aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore6
-rw-r--r--HOWTO/BENCHMARKS.md73
-rw-r--r--HOWTO/BOOTSTRAP.md (renamed from README.bootstrap)24
-rw-r--r--HOWTO/DTRACE.md (renamed from README.dtrace.md)36
-rw-r--r--HOWTO/INSTALL-CROSS.md560
-rw-r--r--HOWTO/INSTALL-WIN32.md (renamed from INSTALL-WIN32.md)3
-rw-r--r--HOWTO/INSTALL.md (renamed from INSTALL.md)31
-rw-r--r--HOWTO/MARKDOWN.md (renamed from README.md.txt)4
-rw-r--r--HOWTO/SYSTEMTAP.md75
-rw-r--r--HOWTO/TESTING.md150
l---------INSTALL-CROSS.md1
-rw-r--r--Makefile.in222
-rw-r--r--README.md10
-rw-r--r--README.systemtap.md72
-rw-r--r--TAR.include8
-rw-r--r--aclocal.m4143
-rw-r--r--bootstrap/bin/start.bootbin5248 -> 5242 bytes
-rw-r--r--bootstrap/bin/start_clean.bootbin5248 -> 5242 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_a.beambin0 -> 2236 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_asm.beambin11576 -> 11576 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_block.beambin14388 -> 14424 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_bool.beambin16156 -> 16480 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_bsm.beambin13724 -> 12488 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_clean.beambin11468 -> 9476 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_dead.beambin10180 -> 10212 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_dict.beambin5364 -> 5360 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_disasm.beambin25032 -> 25028 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_except.beambin3484 -> 3344 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_flatten.beambin3448 -> 2900 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_jump.beambin9516 -> 8524 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_opcodes.beambin6812 -> 6812 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_peep.beambin2456 -> 2428 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_receive.beambin5832 -> 6568 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_split.beambin2048 -> 2048 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_trim.beambin8568 -> 7452 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_type.beambin13852 -> 14032 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_utils.beambin14552 -> 13220 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_validator.beambin34476 -> 34356 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_z.beambin0 -> 2080 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/cerl.beambin30264 -> 30084 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/cerl_inline.beambin37572 -> 37496 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/cerl_trees.beambin18952 -> 18952 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/compile.beambin37112 -> 37548 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/compiler.app5
-rw-r--r--bootstrap/lib/compiler/ebin/core_lib.beambin5468 -> 5460 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/core_lint.beambin11632 -> 11636 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/core_parse.beambin37696 -> 37688 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/core_pp.beambin12124 -> 12064 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/core_scan.beambin6628 -> 6628 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/erl_bifs.beambin2156 -> 2088 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/rec_env.beambin4780 -> 4772 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/sys_core_dsetel.beambin6996 -> 6992 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/sys_core_fold.beambin46776 -> 47060 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/sys_core_inline.beambin4208 -> 4208 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/sys_expand_pmod.beambin8424 -> 0 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/sys_pre_attributes.beambin3356 -> 3348 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/sys_pre_expand.beambin16292 -> 13420 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/v3_codegen.beambin51696 -> 51816 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/v3_core.beambin51076 -> 51060 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/v3_kernel.beambin42764 -> 43196 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/v3_kernel_pp.beambin11828 -> 11780 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/v3_life.beambin19924 -> 19880 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/application.beambin3772 -> 3860 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/application_controller.beambin30996 -> 30848 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/application_master.beambin6352 -> 6336 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/application_starter.beambin1264 -> 1256 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/auth.beambin6480 -> 6472 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/code.beambin6952 -> 6916 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/code_server.beambin26228 -> 26184 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/disk_log.beambin37260 -> 37096 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/disk_log_1.beambin24928 -> 24908 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/disk_log_server.beambin6408 -> 6404 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/dist_ac.beambin26708 -> 26580 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/dist_util.beambin10508 -> 10512 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/erl_boot_server.beambin5696 -> 5684 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/erl_ddll.beambin2868 -> 2868 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/erl_distribution.beambin1836 -> 1832 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/erl_epmd.beambin7064 -> 7064 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/error_handler.beambin1952 -> 1660 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/erts_debug.beambin3916 -> 3916 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/file.beambin14416 -> 14608 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/file_io_server.beambin14248 -> 14548 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/file_server.beambin5328 -> 5312 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/gen_sctp.beambin3528 -> 3524 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/gen_tcp.beambin2432 -> 2416 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/gen_udp.beambin1644 -> 1632 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/global.beambin32236 -> 32324 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/global_group.beambin17728 -> 17688 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/global_search.beambin3088 -> 3084 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/group.beambin11784 -> 13576 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/heart.beambin3912 -> 3952 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/hipe_unified_loader.beambin12732 -> 12716 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet.beambin20160 -> 21148 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet6_tcp.beambin2684 -> 2680 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet6_tcp_dist.beambin6256 -> 6248 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet6_udp.beambin1732 -> 1728 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_config.beambin8192 -> 7468 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_db.beambin26424 -> 26408 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_dns.beambin19728 -> 19716 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_gethost_native.beambin10484 -> 10468 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_parse.beambin13056 -> 12936 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_res.beambin14816 -> 14808 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_tcp.beambin2492 -> 2488 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_tcp_dist.beambin6540 -> 6532 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_udp.beambin1920 -> 1916 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/kernel.app1
-rw-r--r--bootstrap/lib/kernel/ebin/kernel.beambin3792 -> 3792 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/kernel_config.beambin2720 -> 2708 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/net_adm.beambin3064 -> 3060 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/net_kernel.beambin22800 -> 22756 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/os.beambin5480 -> 5276 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/packages.beambin2244 -> 0 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/pg2.beambin7696 -> 7692 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/ram_file.beambin6800 -> 7016 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/rpc.beambin8748 -> 8752 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/seq_trace.beambin1336 -> 1336 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/standard_error.beambin3636 -> 3620 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/user.beambin12332 -> 12384 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/wrap_log_reader.beambin3384 -> 3380 bytes
-rw-r--r--bootstrap/lib/kernel/include/file.hrl59
-rw-r--r--bootstrap/lib/stdlib/ebin/array.beambin12036 -> 12032 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/base64.beambin4532 -> 4528 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/beam_lib.beambin18060 -> 18044 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/binary.beambin3660 -> 3644 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/c.beambin13932 -> 13904 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/dets.beambin53456 -> 53452 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/dets_server.beambin7020 -> 7016 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/dets_utils.beambin28864 -> 28820 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/dets_v8.beambin27516 -> 27496 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/dets_v9.beambin50072 -> 50008 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/dict.beambin9140 -> 9132 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/digraph.beambin8320 -> 8308 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/digraph_utils.beambin6808 -> 6796 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/edlin.beambin7912 -> 9260 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/epp.beambin24188 -> 27164 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_bits.beambin2568 -> 2552 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_compile.beambin4896 -> 4876 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_eval.beambin23752 -> 23116 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_expand_records.beambin21900 -> 21652 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_internal.beambin5016 -> 5016 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_lint.beambin85064 -> 82592 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_parse.beambin71552 -> 69040 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_pp.beambin23344 -> 24544 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_scan.beambin31784 -> 33120 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_tar.beambin15352 -> 15328 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/escript.beambin16876 -> 17264 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/ets.beambin21724 -> 21688 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/eval_bits.beambin6748 -> 6736 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/file_sorter.beambin30592 -> 30552 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/filelib.beambin7308 -> 7816 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/filename.beambin12412 -> 12436 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/gb_sets.beambin8256 -> 8252 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/gb_trees.beambin4992 -> 4992 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/gen.beambin4112 -> 4080 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/gen_event.beambin17604 -> 17560 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/gen_fsm.beambin15348 -> 15360 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/gen_server.beambin17256 -> 17264 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/io.beambin6608 -> 6784 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/io_lib.beambin8896 -> 9200 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/io_lib_format.beambin11960 -> 12100 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/io_lib_fread.beambin7404 -> 7404 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/io_lib_pretty.beambin12364 -> 13212 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/lib.beambin9068 -> 9624 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/lists.beambin29556 -> 29556 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/log_mf_h.beambin2644 -> 2640 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/ms_transform.beambin20180 -> 20068 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/otp_internal.beambin7156 -> 7860 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/pg.beambin2064 -> 2064 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/pool.beambin3848 -> 3848 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/proc_lib.beambin9200 -> 9776 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/qlc.beambin69376 -> 69144 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/qlc_pt.beambin71588 -> 71084 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/queue.beambin5900 -> 6184 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/re.beambin12544 -> 12752 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/sets.beambin7020 -> 7016 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/shell.beambin30628 -> 30320 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/slave.beambin4424 -> 4424 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/sofs.beambin41100 -> 40756 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/supervisor.beambin22940 -> 23004 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/sys.beambin7328 -> 7332 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/unicode.beambin11620 -> 11584 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/win32reg.beambin5652 -> 5652 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/zip.beambin26800 -> 26740 bytes
-rw-r--r--configure.in17
-rw-r--r--erts/Makefile.in15
-rw-r--r--erts/aclocal.m4143
-rw-r--r--erts/autoconf/vxworks/sed.general2
-rw-r--r--erts/configure.in135
-rw-r--r--erts/doc/src/Makefile7
-rw-r--r--erts/doc/src/absform.xml7
-rw-r--r--erts/doc/src/communication.xml89
-rw-r--r--erts/doc/src/driver_entry.xml41
-rw-r--r--erts/doc/src/erl.xml175
-rw-r--r--erts/doc/src/erl_dist_protocol.xml288
-rw-r--r--erts/doc/src/erl_driver.xml256
-rw-r--r--erts/doc/src/erl_ext_dist.xml121
-rw-r--r--erts/doc/src/erl_nif.xml123
-rw-r--r--erts/doc/src/erl_set_memory_block.xml172
-rw-r--r--erts/doc/src/erlang.xml447
-rw-r--r--erts/doc/src/erlc.xml4
-rw-r--r--erts/doc/src/erlsrv.xml15
-rw-r--r--erts/doc/src/erts_alloc.xml6
-rw-r--r--erts/doc/src/notes.xml755
-rw-r--r--erts/doc/src/part.xml1
-rw-r--r--erts/doc/src/ref_man.xml1
-rw-r--r--erts/emulator/Makefile.in153
-rw-r--r--erts/emulator/beam/atom.c169
-rw-r--r--erts/emulator/beam/atom.h62
-rw-r--r--erts/emulator/beam/atom.names15
-rw-r--r--erts/emulator/beam/beam_bif_load.c49
-rw-r--r--erts/emulator/beam/beam_bp.c44
-rw-r--r--erts/emulator/beam/beam_emu.c101
-rw-r--r--erts/emulator/beam/beam_load.c22
-rw-r--r--erts/emulator/beam/benchmark.c2
-rw-r--r--erts/emulator/beam/benchmark.h2
-rw-r--r--erts/emulator/beam/bif.c797
-rw-r--r--erts/emulator/beam/bif.h23
-rw-r--r--erts/emulator/beam/bif.tab301
-rw-r--r--erts/emulator/beam/binary.c6
-rw-r--r--erts/emulator/beam/break.c90
-rw-r--r--erts/emulator/beam/code_ix.c46
-rw-r--r--erts/emulator/beam/code_ix.h6
-rw-r--r--erts/emulator/beam/copy.c2
-rw-r--r--erts/emulator/beam/dist.c335
-rw-r--r--erts/emulator/beam/dist.h17
-rw-r--r--erts/emulator/beam/dtrace-wrapper.h2
-rw-r--r--erts/emulator/beam/erl_afit_alloc.c11
-rw-r--r--erts/emulator/beam/erl_afit_alloc.h5
-rw-r--r--erts/emulator/beam/erl_alloc.c49
-rw-r--r--erts/emulator/beam/erl_alloc.h2
-rw-r--r--erts/emulator/beam/erl_alloc.types47
-rw-r--r--erts/emulator/beam/erl_alloc_util.c1050
-rw-r--r--erts/emulator/beam/erl_alloc_util.h55
-rw-r--r--erts/emulator/beam/erl_ao_firstfit_alloc.c16
-rw-r--r--erts/emulator/beam/erl_async.c25
-rw-r--r--erts/emulator/beam/erl_bestfit_alloc.c45
-rw-r--r--erts/emulator/beam/erl_bif_ddll.c199
-rwxr-xr-xerts/emulator/beam/erl_bif_info.c426
-rw-r--r--erts/emulator/beam/erl_bif_op.c5
-rw-r--r--erts/emulator/beam/erl_bif_os.c110
-rw-r--r--erts/emulator/beam/erl_bif_port.c885
-rw-r--r--erts/emulator/beam/erl_bif_re.c16
-rw-r--r--erts/emulator/beam/erl_bif_timer.c18
-rw-r--r--erts/emulator/beam/erl_bif_trace.c234
-rw-r--r--erts/emulator/beam/erl_bits.c30
-rw-r--r--erts/emulator/beam/erl_cpu_topology.c40
-rw-r--r--erts/emulator/beam/erl_cpu_topology.h2
-rw-r--r--erts/emulator/beam/erl_db.c127
-rw-r--r--erts/emulator/beam/erl_db.h10
-rw-r--r--erts/emulator/beam/erl_db_util.c72
-rw-r--r--erts/emulator/beam/erl_db_util.h6
-rw-r--r--erts/emulator/beam/erl_debug.c30
-rw-r--r--erts/emulator/beam/erl_debug.h2
-rw-r--r--erts/emulator/beam/erl_driver.h55
-rw-r--r--erts/emulator/beam/erl_fun.h2
-rw-r--r--erts/emulator/beam/erl_gc.c69
-rw-r--r--erts/emulator/beam/erl_goodfit_alloc.c13
-rw-r--r--erts/emulator/beam/erl_init.c181
-rw-r--r--erts/emulator/beam/erl_lock_check.c28
-rw-r--r--erts/emulator/beam/erl_lock_check.h5
-rw-r--r--erts/emulator/beam/erl_lock_count.c2
-rw-r--r--erts/emulator/beam/erl_lock_count.h2
-rw-r--r--erts/emulator/beam/erl_message.c63
-rw-r--r--erts/emulator/beam/erl_message.h29
-rw-r--r--erts/emulator/beam/erl_monitors.c13
-rw-r--r--erts/emulator/beam/erl_monitors.h2
-rw-r--r--erts/emulator/beam/erl_mtrace.c2
-rw-r--r--erts/emulator/beam/erl_nif.c1166
-rw-r--r--erts/emulator/beam/erl_nif.h17
-rw-r--r--erts/emulator/beam/erl_nif_api_funcs.h4
-rw-r--r--erts/emulator/beam/erl_node_container_utils.h102
-rw-r--r--erts/emulator/beam/erl_node_tables.c65
-rw-r--r--erts/emulator/beam/erl_node_tables.h9
-rw-r--r--erts/emulator/beam/erl_port.h943
-rw-r--r--erts/emulator/beam/erl_port_task.c2090
-rw-r--r--erts/emulator/beam/erl_port_task.h160
-rw-r--r--erts/emulator/beam/erl_posix_str.c1
-rw-r--r--erts/emulator/beam/erl_process.c2423
-rw-r--r--erts/emulator/beam/erl_process.h322
-rw-r--r--erts/emulator/beam/erl_process_dict.c6
-rw-r--r--erts/emulator/beam/erl_process_dump.c20
-rw-r--r--erts/emulator/beam/erl_process_lock.c317
-rw-r--r--erts/emulator/beam/erl_process_lock.h62
-rw-r--r--erts/emulator/beam/erl_ptab.c1566
-rw-r--r--erts/emulator/beam/erl_ptab.h472
-rw-r--r--erts/emulator/beam/erl_resolv_dns.c23
-rw-r--r--erts/emulator/beam/erl_resolv_nodns.c23
-rw-r--r--erts/emulator/beam/erl_sched_spec_pre_alloc.c113
-rw-r--r--erts/emulator/beam/erl_sched_spec_pre_alloc.h7
-rw-r--r--erts/emulator/beam/erl_smp.h28
-rw-r--r--erts/emulator/beam/erl_sys_driver.h1
-rw-r--r--erts/emulator/beam/erl_term.c6
-rw-r--r--erts/emulator/beam/erl_term.h36
-rw-r--r--erts/emulator/beam/erl_thr_progress.c288
-rw-r--r--erts/emulator/beam/erl_thr_progress.h73
-rw-r--r--erts/emulator/beam/erl_threads.h298
-rw-r--r--erts/emulator/beam/erl_time_sup.c16
-rw-r--r--erts/emulator/beam/erl_trace.c382
-rw-r--r--erts/emulator/beam/erl_trace.h141
-rw-r--r--erts/emulator/beam/erl_unicode.c334
-rw-r--r--erts/emulator/beam/erl_utils.h215
-rw-r--r--erts/emulator/beam/erl_vm.h2
-rw-r--r--erts/emulator/beam/erlang_dtrace.d2916
-rw-r--r--erts/emulator/beam/external.c298
-rw-r--r--erts/emulator/beam/external.h7
-rwxr-xr-xerts/emulator/beam/global.h940
-rw-r--r--erts/emulator/beam/index.c5
-rw-r--r--erts/emulator/beam/io.c4517
-rw-r--r--erts/emulator/beam/module.c2
-rw-r--r--erts/emulator/beam/register.c53
-rw-r--r--erts/emulator/beam/register.h19
-rw-r--r--erts/emulator/beam/sys.h183
-rw-r--r--erts/emulator/beam/utils.c44
-rw-r--r--erts/emulator/drivers/common/efile_drv.c149
-rw-r--r--erts/emulator/drivers/common/erl_efile.h1
-rw-r--r--erts/emulator/drivers/common/gzio.c5
-rw-r--r--erts/emulator/drivers/common/inet_drv.c458
-rw-r--r--erts/emulator/drivers/common/ram_file_drv.c8
-rw-r--r--erts/emulator/drivers/unix/ttsl_drv.c12
-rw-r--r--erts/emulator/drivers/unix/unix_efile.c669
-rw-r--r--erts/emulator/drivers/win32/registry_drv.c2
-rw-r--r--erts/emulator/drivers/win32/win_con.c4
-rw-r--r--erts/emulator/drivers/win32/win_efile.c27
-rw-r--r--erts/emulator/hipe/hipe_amd64_bifs.m46
-rw-r--r--erts/emulator/hipe/hipe_arm.c2
-rw-r--r--erts/emulator/hipe/hipe_arm_bifs.m42
-rw-r--r--erts/emulator/hipe/hipe_bif0.c49
-rw-r--r--erts/emulator/hipe/hipe_bif1.c2
-rw-r--r--erts/emulator/hipe/hipe_bif2.c2
-rw-r--r--erts/emulator/hipe/hipe_bif2.tab4
-rw-r--r--erts/emulator/hipe/hipe_bif_list.m45
-rw-r--r--erts/emulator/hipe/hipe_debug.c11
-rw-r--r--erts/emulator/hipe/hipe_gc.c2
-rw-r--r--erts/emulator/hipe/hipe_mkliterals.c99
-rw-r--r--erts/emulator/hipe/hipe_ppc_bifs.m416
-rw-r--r--erts/emulator/hipe/hipe_stack.h2
-rw-r--r--erts/emulator/hipe/hipe_x86.c2
-rw-r--r--erts/emulator/hipe/hipe_x86_bifs.m412
-rw-r--r--erts/emulator/hipe/hipe_x86_gc.h2
-rw-r--r--erts/emulator/pcre/pcre.mk8
-rw-r--r--erts/emulator/sys/common/erl_check_io.c53
-rw-r--r--erts/emulator/sys/common/erl_mseg.c1134
-rw-r--r--erts/emulator/sys/common/erl_mseg.h31
-rw-r--r--erts/emulator/sys/common/erl_poll.c25
-rw-r--r--erts/emulator/sys/common/erl_sys_common_misc.c151
-rw-r--r--erts/emulator/sys/unix/erl_unix_sys_ddll.c4
-rw-r--r--erts/emulator/sys/unix/sys.c226
-rw-r--r--erts/emulator/sys/unix/sys_float.c13
-rw-r--r--erts/emulator/sys/vxworks/driver_int.h30
-rw-r--r--erts/emulator/sys/vxworks/erl_main.c45
-rw-r--r--erts/emulator/sys/vxworks/erl_vxworks_sys.h184
-rw-r--r--erts/emulator/sys/vxworks/erl_vxworks_sys_ddll.c253
-rw-r--r--erts/emulator/sys/vxworks/sys.c2610
-rw-r--r--erts/emulator/sys/win32/erl_win32_sys_ddll.c4
-rw-r--r--erts/emulator/sys/win32/erl_win_dyn_driver.h8
-rw-r--r--erts/emulator/sys/win32/erl_win_sys.h6
-rwxr-xr-xerts/emulator/sys/win32/sys.c273
-rw-r--r--erts/emulator/sys/win32/sys_env.c570
-rw-r--r--erts/emulator/sys/win32/sys_float.c13
-rw-r--r--erts/emulator/sys/win32/sys_interrupt.c6
-rw-r--r--erts/emulator/test/Makefile10
-rw-r--r--erts/emulator/test/alloc_SUITE_data/allocator_test.h8
-rw-r--r--erts/emulator/test/alloc_SUITE_data/basic.c2
-rw-r--r--erts/emulator/test/alloc_SUITE_data/bucket_mask.c73
-rw-r--r--erts/emulator/test/alloc_SUITE_data/coalesce.c6
-rw-r--r--erts/emulator/test/alloc_SUITE_data/mseg_clear_cache.c4
-rw-r--r--erts/emulator/test/alloc_SUITE_data/testcase_driver.c10
-rw-r--r--erts/emulator/test/beam_SUITE.erl77
-rw-r--r--erts/emulator/test/bif_SUITE.erl4
-rw-r--r--erts/emulator/test/binary_SUITE.erl28
-rw-r--r--erts/emulator/test/bs_bit_binaries_SUITE.erl45
-rw-r--r--erts/emulator/test/bs_construct_SUITE.erl93
-rw-r--r--erts/emulator/test/busy_port_SUITE.erl334
-rw-r--r--erts/emulator/test/busy_port_SUITE_data/Makefile.src3
-rw-r--r--erts/emulator/test/busy_port_SUITE_data/hs_busy_drv.c4
-rw-r--r--erts/emulator/test/busy_port_SUITE_data/scheduling_drv.c190
-rw-r--r--erts/emulator/test/code_SUITE.erl2
-rw-r--r--erts/emulator/test/ddll_SUITE.erl2
-rw-r--r--erts/emulator/test/distribution_SUITE.erl348
-rw-r--r--erts/emulator/test/driver_SUITE.erl67
-rw-r--r--erts/emulator/test/driver_SUITE_data/Makefile.src3
-rw-r--r--erts/emulator/test/driver_SUITE_data/async_blast_drv.c8
-rw-r--r--erts/emulator/test/driver_SUITE_data/caller_drv.c4
-rw-r--r--erts/emulator/test/driver_SUITE_data/chkio_drv.c2
-rw-r--r--erts/emulator/test/driver_SUITE_data/io_ready_exit_drv.c2
-rw-r--r--erts/emulator/test/driver_SUITE_data/ioq_exit_drv.c2
-rw-r--r--erts/emulator/test/driver_SUITE_data/missing_callback_drv.c2
-rw-r--r--erts/emulator/test/driver_SUITE_data/monitor_drv.c2
-rw-r--r--erts/emulator/test/driver_SUITE_data/otp_9302_drv.c4
-rw-r--r--erts/emulator/test/driver_SUITE_data/peek_non_existing_queue_drv.c13
-rw-r--r--erts/emulator/test/driver_SUITE_data/thr_msg_blast_drv.c178
-rw-r--r--erts/emulator/test/driver_SUITE_data/timer_drv.c12
-rw-r--r--erts/emulator/test/emulator.spec.vxworks26
-rw-r--r--erts/emulator/test/emulator_bench.spec1
-rw-r--r--erts/emulator/test/erl_drv_thread_SUITE_data/testcase_driver.c10
-rw-r--r--erts/emulator/test/estone_SUITE.erl16
-rw-r--r--erts/emulator/test/estone_SUITE_data/estone_cat.c4
-rw-r--r--erts/emulator/test/float_SUITE.erl11
-rw-r--r--erts/emulator/test/fun_SUITE.erl6
-rw-r--r--erts/emulator/test/gc_SUITE.erl59
-rw-r--r--erts/emulator/test/hibernate_SUITE.erl2
-rw-r--r--erts/emulator/test/match_spec_SUITE.erl16
-rw-r--r--erts/emulator/test/node_container_SUITE.erl2
-rw-r--r--erts/emulator/test/nofrag_SUITE.erl26
-rw-r--r--erts/emulator/test/num_bif_SUITE.erl45
-rw-r--r--erts/emulator/test/port_SUITE.erl1246
-rw-r--r--erts/emulator/test/port_SUITE_data/dead_port.c24
-rw-r--r--erts/emulator/test/port_SUITE_data/port_test.c35
-rw-r--r--erts/emulator/test/port_SUITE_data/reclaim.h60
-rw-r--r--erts/emulator/test/port_bif_SUITE.erl2
-rw-r--r--erts/emulator/test/port_bif_SUITE_data/port_test.c37
-rw-r--r--erts/emulator/test/port_bif_SUITE_data/reclaim.h60
-rw-r--r--erts/emulator/test/process_SUITE.erl2077
-rw-r--r--erts/emulator/test/save_calls_SUITE.erl27
-rw-r--r--erts/emulator/test/send_term_SUITE_data/send_term_drv.c8
-rw-r--r--erts/emulator/test/smoke_test_SUITE.erl37
-rw-r--r--erts/emulator/test/timer_bif_SUITE.erl67
-rw-r--r--erts/emulator/test/trace_local_SUITE.erl133
-rw-r--r--erts/emulator/test/trace_port_SUITE.erl7
-rw-r--r--erts/emulator/test/tuple_SUITE.erl305
-rwxr-xr-xerts/emulator/utils/beam_makeops21
-rwxr-xr-xerts/emulator/utils/make_preload3
-rw-r--r--erts/emulator/valgrind/suppress.halfword56
-rw-r--r--erts/emulator/valgrind/suppress.patched.3.6.020
-rw-r--r--erts/emulator/valgrind/suppress.standard12
-rw-r--r--erts/emulator/zlib/zlib.mk6
-rw-r--r--erts/epmd/src/Makefile.in6
-rw-r--r--erts/epmd/src/epmd.c2
-rw-r--r--erts/epmd/src/epmd_cli.c13
-rw-r--r--erts/epmd/src/epmd_int.h20
-rw-r--r--erts/epmd/src/epmd_srv.c216
-rw-r--r--erts/epmd/test/Makefile2
-rw-r--r--erts/epmd/test/epmd_SUITE.erl49
-rw-r--r--erts/etc/common/Makefile.in203
-rw-r--r--erts/etc/common/dialyzer.c2
-rw-r--r--erts/etc/common/erlc.c4
-rw-r--r--erts/etc/common/erlexec.c183
-rw-r--r--erts/etc/common/escript.c2
-rw-r--r--erts/etc/common/heart.c325
-rw-r--r--erts/etc/common/inet_gethost.c12
-rw-r--r--erts/etc/unix/Install.src2
-rw-r--r--erts/etc/unix/cerl.src32
-rw-r--r--erts/etc/unix/erl.src.src2
-rw-r--r--erts/etc/unix/etp-commands258
-rw-r--r--erts/etc/unix/run_erl.c2
-rw-r--r--erts/etc/vxworks/README.VxWorks350
-rw-r--r--erts/etc/vxworks/erl.exec.c129
-rw-r--r--erts/etc/vxworks/erl_io.c108
-rw-r--r--erts/etc/vxworks/erl_script.sam.in100
-rw-r--r--erts/etc/vxworks/heart_config.c60
-rw-r--r--erts/etc/vxworks/heart_config.h35
-rw-r--r--erts/etc/vxworks/rdate.c87
-rw-r--r--erts/etc/vxworks/reclaim.c551
-rw-r--r--erts/etc/vxworks/reclaim.h150
-rw-r--r--erts/etc/vxworks/reclaim_private.h44
-rw-r--r--erts/etc/vxworks/resolv.conf6
-rw-r--r--erts/etc/vxworks/vxcall.c145
-rw-r--r--erts/etc/vxworks/wd_example.c141
-rw-r--r--erts/etc/win32/Makefile2
-rw-r--r--erts/etc/win32/erlsrv/erlsrv_service.c5
-rw-r--r--erts/etc/win32/nsis/Makefile14
-rwxr-xr-xerts/etc/win32/nsis/dll_version_helper.sh16
-rw-r--r--erts/etc/win32/nsis/erlang20.nsi13
-rw-r--r--erts/etc/win32/port_entry.c4
-rw-r--r--erts/etc/win32/start_erl.c5
-rw-r--r--erts/include/internal/ethread.h4
-rw-r--r--erts/lib_src/Makefile.in109
-rw-r--r--erts/lib_src/common/erl_misc_utils.c10
-rw-r--r--erts/lib_src/common/erl_printf_format.c15
-rw-r--r--erts/lib_src/common/ethr_aux.c2
-rw-r--r--erts/preloaded/ebin/erl_prim_loader.beambin52904 -> 54136 bytes
-rw-r--r--erts/preloaded/ebin/erlang.beambin88536 -> 92996 bytes
-rw-r--r--erts/preloaded/ebin/erts_internal.beambin0 -> 3616 bytes
-rw-r--r--erts/preloaded/ebin/init.beambin48060 -> 48556 bytes
-rw-r--r--erts/preloaded/ebin/otp_ring0.beambin1444 -> 1448 bytes
-rw-r--r--erts/preloaded/ebin/prim_file.beambin41124 -> 41176 bytes
-rw-r--r--erts/preloaded/ebin/prim_inet.beambin69896 -> 70016 bytes
-rw-r--r--erts/preloaded/ebin/prim_zip.beambin23456 -> 23408 bytes
-rw-r--r--erts/preloaded/ebin/zlib.beambin12804 -> 12784 bytes
-rw-r--r--erts/preloaded/src/Makefile5
-rw-r--r--erts/preloaded/src/erl_prim_loader.erl361
-rw-r--r--erts/preloaded/src/erlang.erl351
-rw-r--r--erts/preloaded/src/erts_internal.erl155
-rw-r--r--erts/preloaded/src/init.erl16
-rw-r--r--erts/preloaded/src/prim_file.erl30
-rw-r--r--erts/preloaded/src/prim_inet.erl15
-rw-r--r--erts/start_scripts/Makefile54
-rw-r--r--erts/test/Makefile4
-rw-r--r--erts/test/erl_print_SUITE_data/Makefile.src2
-rw-r--r--erts/test/erl_print_SUITE_data/erl_print_tests.c2
-rw-r--r--erts/test/erlc_SUITE.erl2
-rw-r--r--erts/test/erlexec_SUITE_data/Makefile.src2
-rw-r--r--erts/test/ethread_SUITE_data/Makefile.src2
-rw-r--r--erts/test/install_SUITE.erl2
-rw-r--r--erts/test/otp_SUITE.erl47
-rw-r--r--erts/test/run_erl_SUITE.erl2
-rw-r--r--erts/test/system.spec.vxworks2
-rw-r--r--erts/test/utils/gccifier.c2
-rwxr-xr-xerts/test/utils/gccifier.sh2
-rw-r--r--erts/vsn.mk2
-rw-r--r--lib/.gitignore1
-rw-r--r--lib/appmon/doc/src/Makefile2
-rw-r--r--lib/appmon/priv/Makefile2
-rw-r--r--lib/appmon/src/Makefile10
-rw-r--r--lib/appmon/src/appmon.app.src4
-rw-r--r--lib/asn1/c_src/Makefile11
-rw-r--r--lib/asn1/doc/src/Makefile2
-rw-r--r--lib/asn1/doc/src/asn1_ug.xml397
-rw-r--r--lib/asn1/doc/src/asn1ct.xml73
-rw-r--r--lib/asn1/doc/src/asn1rt.xml39
-rw-r--r--lib/asn1/doc/src/notes.xml73
-rw-r--r--lib/asn1/src/Makefile45
-rw-r--r--lib/asn1/src/asn1.app.src1
-rw-r--r--lib/asn1/src/asn1ct.erl131
-rw-r--r--lib/asn1/src/asn1ct_check.erl113
-rw-r--r--lib/asn1/src/asn1ct_constructed_ber.erl1596
-rw-r--r--lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl25
-rw-r--r--lib/asn1/src/asn1ct_constructed_per.erl1011
-rw-r--r--lib/asn1/src/asn1ct_gen.erl377
-rw-r--r--lib/asn1/src/asn1ct_gen_ber.erl1749
-rw-r--r--lib/asn1/src/asn1ct_gen_ber_bin_v2.erl66
-rw-r--r--lib/asn1/src/asn1ct_gen_per.erl377
-rw-r--r--lib/asn1/src/asn1ct_gen_per_rt2ct.erl405
-rw-r--r--lib/asn1/src/asn1ct_imm.erl626
-rw-r--r--lib/asn1/src/asn1ct_parser2.erl31
-rw-r--r--lib/asn1/src/asn1ct_value.erl24
-rw-r--r--lib/asn1/src/asn1rt_ber_bin.erl1974
-rw-r--r--lib/asn1/src/asn1rt_ber_bin_v2.erl59
-rw-r--r--lib/asn1/src/asn1rt_check.erl3
-rw-r--r--lib/asn1/src/asn1rt_per_bin.erl2287
-rw-r--r--lib/asn1/src/asn1rt_per_bin_rt2ct.erl375
-rw-r--r--lib/asn1/src/asn1rt_uper_bin.erl299
-rw-r--r--lib/asn1/test/Makefile1
-rw-r--r--lib/asn1/test/asn1_SUITE.erl302
-rw-r--r--lib/asn1/test/asn1_SUITE_data/ConstraintEquivalence.asn142
-rw-r--r--lib/asn1/test/asn1_SUITE_data/Constraints.py5
-rw-r--r--lib/asn1/test/asn1_SUITE_data/EnumN2N.asn125
-rw-r--r--lib/asn1/test/asn1_SUITE_data/Extension-Addition-Group.asn23
-rw-r--r--lib/asn1/test/asn1_SUITE_data/LargeConstraints.py9
-rw-r--r--lib/asn1/test/asn1_SUITE_data/MultipleLevels.asn19
-rw-r--r--lib/asn1/test/asn1_SUITE_data/PartialDecSeq.asn1config2
-rw-r--r--lib/asn1/test/asn1_SUITE_data/PrimStrings.asn126
-rw-r--r--lib/asn1/test/asn1_SUITE_data/SeqExtension.asn128
-rw-r--r--lib/asn1/test/asn1_SUITE_data/extensionAdditionGroup.erl23
-rw-r--r--lib/asn1/test/asn1_SUITE_data/testobj.erl53
-rw-r--r--lib/asn1/test/asn1_app_test.erl3
-rw-r--r--lib/asn1/test/asn1_test_lib.erl10
-rw-r--r--lib/asn1/test/asn1_wrapper.erl35
-rw-r--r--lib/asn1/test/h323test.erl1
-rw-r--r--lib/asn1/test/testChoExtension.erl41
-rw-r--r--lib/asn1/test/testChoExternal.erl75
-rw-r--r--lib/asn1/test/testChoRecursive.erl51
-rw-r--r--lib/asn1/test/testChoiceIndefinite.erl5
-rw-r--r--lib/asn1/test/testCompactBitString.erl302
-rw-r--r--lib/asn1/test/testConstraints.erl175
-rw-r--r--lib/asn1/test/testDeepTConstr.erl26
-rw-r--r--lib/asn1/test/testEnumExt.erl63
-rw-r--r--lib/asn1/test/testINSTANCE_OF.erl17
-rw-r--r--lib/asn1/test/testInfObjectClass.erl15
-rw-r--r--lib/asn1/test/testMergeCompile.erl14
-rw-r--r--lib/asn1/test/testMultipleLevels.erl (renamed from lib/compiler/test/compilation_SUITE_data/bad_functional_value.erl)25
-rw-r--r--lib/asn1/test/testParameterizedInfObj.erl4
-rw-r--r--lib/asn1/test/testPrimStrings.erl353
-rw-r--r--lib/asn1/test/testSSLspecs.erl14
-rw-r--r--lib/asn1/test/testSeqExtension.erl135
-rw-r--r--lib/asn1/test/testSeqIndefinite.erl6
-rw-r--r--lib/asn1/test/testSeqOf.erl11
-rw-r--r--lib/asn1/test/testSetIndefinite.erl5
-rw-r--r--lib/asn1/test/testSetOptional.erl212
-rw-r--r--lib/asn1/test/testTCAP.erl4
-rw-r--r--lib/asn1/test/testTimer.erl53
-rw-r--r--lib/asn1/test/testTypeValueNotation.erl31
-rw-r--r--lib/asn1/test/testX420.erl2
-rw-r--r--lib/asn1/test/test_compile_options.erl3
-rw-r--r--lib/asn1/test/test_inline.erl16
-rw-r--r--lib/asn1/test/test_partial_incomplete_decode.erl2
-rw-r--r--lib/asn1/test/test_selective_decode.erl2
-rw-r--r--lib/asn1/test/test_special_decode_performance.erl8
-rw-r--r--lib/asn1/vsn.mk2
-rw-r--r--lib/common_test/doc/src/Makefile7
-rw-r--r--lib/common_test/doc/src/common_test_app.xml70
-rw-r--r--lib/common_test/doc/src/config_file_chapter.xml23
-rw-r--r--lib/common_test/doc/src/cover_chapter.xml116
-rw-r--r--lib/common_test/doc/src/ct_hooks.xml20
-rw-r--r--lib/common_test/doc/src/ct_hooks_chapter.xml56
-rw-r--r--lib/common_test/doc/src/ct_master_chapter.xml51
-rw-r--r--lib/common_test/doc/src/ct_run.xml32
-rw-r--r--lib/common_test/doc/src/event_handler_chapter.xml12
-rw-r--r--lib/common_test/doc/src/example_chapter.xml5
-rw-r--r--lib/common_test/doc/src/getting_started_chapter.xml57
-rw-r--r--lib/common_test/doc/src/notes.xml492
-rw-r--r--lib/common_test/doc/src/ref_man.xml3
-rw-r--r--lib/common_test/doc/src/run_test_chapter.xml756
-rw-r--r--lib/common_test/doc/src/write_test_chapter.xml134
-rw-r--r--lib/common_test/include/ct.hrl15
-rw-r--r--lib/common_test/priv/Makefile.in16
-rw-r--r--lib/common_test/priv/ct_default.css17
-rw-r--r--lib/common_test/priv/jquery-latest.js154
-rw-r--r--lib/common_test/priv/jquery.tablesorter.min.js4
-rw-r--r--lib/common_test/src/Makefile13
-rw-r--r--lib/common_test/src/common_test.app.src3
-rw-r--r--lib/common_test/src/ct.erl574
-rw-r--r--lib/common_test/src/ct_config.erl261
-rw-r--r--lib/common_test/src/ct_conn_log_h.erl236
-rw-r--r--lib/common_test/src/ct_cover.erl32
-rw-r--r--lib/common_test/src/ct_event.erl18
-rw-r--r--lib/common_test/src/ct_framework.erl492
-rw-r--r--lib/common_test/src/ct_ftp.erl8
-rw-r--r--lib/common_test/src/ct_gen_conn.erl256
-rw-r--r--lib/common_test/src/ct_groups.erl599
-rw-r--r--lib/common_test/src/ct_hooks.erl9
-rw-r--r--lib/common_test/src/ct_logs.erl500
-rw-r--r--lib/common_test/src/ct_master.erl7
-rw-r--r--lib/common_test/src/ct_master_logs.erl145
-rw-r--r--lib/common_test/src/ct_netconfc.erl1858
-rw-r--r--lib/common_test/src/ct_netconfc.hrl58
-rw-r--r--lib/common_test/src/ct_repeat.erl88
-rw-r--r--lib/common_test/src/ct_run.erl949
-rw-r--r--lib/common_test/src/ct_slave.erl67
-rw-r--r--lib/common_test/src/ct_snmp.erl335
-rw-r--r--lib/common_test/src/ct_ssh.erl292
-rw-r--r--lib/common_test/src/ct_telnet.erl4
-rw-r--r--lib/common_test/src/ct_testspec.erl971
-rw-r--r--lib/common_test/src/ct_util.erl105
-rw-r--r--lib/common_test/src/ct_util.hrl15
-rw-r--r--lib/common_test/src/cth_conn_log.erl124
-rw-r--r--lib/common_test/src/cth_log_redirect.erl2
-rw-r--r--lib/common_test/src/cth_surefire.erl235
-rw-r--r--lib/common_test/test/Makefile16
-rw-r--r--lib/common_test/test/common_test.cover10
-rw-r--r--lib/common_test/test/common_test.spec2
-rw-r--r--lib/common_test/test/ct_auto_compile_SUITE.erl187
-rw-r--r--lib/common_test/test/ct_auto_compile_SUITE_data/bad_SUITE.erl (renamed from lib/stdlib/test/erl_eval_helper.erl)21
-rw-r--r--lib/common_test/test/ct_auto_compile_SUITE_data/dummy_SUITE.erl130
-rw-r--r--lib/common_test/test/ct_basic_html_SUITE.erl180
-rw-r--r--lib/common_test/test/ct_basic_html_SUITE_data/babbling_SUITE.erl130
-rw-r--r--lib/common_test/test/ct_config_SUITE.erl116
-rw-r--r--lib/common_test/test/ct_config_SUITE_data/config/config.txt3
-rw-r--r--lib/common_test/test/ct_config_SUITE_data/config/config.xml1
-rw-r--r--lib/common_test/test/ct_config_SUITE_data/config/shadow.txt12
-rw-r--r--lib/common_test/test/ct_config_SUITE_data/config/test/config_static_SUITE.erl100
-rw-r--r--lib/common_test/test/ct_config_info_SUITE.erl14
-rw-r--r--lib/common_test/test/ct_cover_SUITE.erl312
-rw-r--r--lib/common_test/test/ct_cover_SUITE_data/cover_SUITE.erl156
-rw-r--r--lib/common_test/test/ct_cover_SUITE_data/cover_SUITE_data/.gitignore0
-rw-r--r--lib/common_test/test/ct_cover_SUITE_data/cover_test_mod.erl4
-rw-r--r--lib/common_test/test/ct_error_SUITE.erl452
-rw-r--r--lib/common_test/test/ct_error_SUITE_data/error/test/misc_error_1_SUITE.erl12
-rw-r--r--lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_8_SUITE.erl258
-rw-r--r--lib/common_test/test/ct_group_leader_SUITE.erl181
-rw-r--r--lib/common_test/test/ct_group_leader_SUITE_data/group_leader_SUITE.erl252
-rw-r--r--lib/common_test/test/ct_groups_search_SUITE.erl1245
-rw-r--r--lib/common_test/test/ct_groups_search_SUITE_data/groups_search_dummy_1_SUITE.erl83
-rw-r--r--lib/common_test/test/ct_groups_search_SUITE_data/groups_search_dummy_2_SUITE.erl102
-rw-r--r--lib/common_test/test/ct_hooks_SUITE.erl2
-rw-r--r--lib/common_test/test/ct_master_SUITE.erl60
-rw-r--r--lib/common_test/test/ct_master_SUITE_data/master/master_SUITE.erl9
-rw-r--r--lib/common_test/test/ct_misc_1_SUITE.erl4
-rw-r--r--lib/common_test/test/ct_netconfc_SUITE.erl128
-rw-r--r--lib/common_test/test/ct_netconfc_SUITE_data/netconfc1.cfg6
-rw-r--r--lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl1129
-rw-r--r--lib/common_test/test/ct_netconfc_SUITE_data/ns.erl555
-rw-r--r--lib/common_test/test/ct_priv_dir_SUITE_data/priv_dir_SUITE.erl26
-rw-r--r--lib/common_test/test/ct_shell_SUITE.erl133
-rw-r--r--lib/common_test/test/ct_shell_SUITE_data/cfgdata2
-rw-r--r--lib/common_test/test/ct_snmp_SUITE.erl141
-rw-r--r--lib/common_test/test/ct_snmp_SUITE_data/snmp.cfg44
-rw-r--r--lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE.erl395
-rw-r--r--lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/community.conf1
-rw-r--r--lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/context.conf1
-rw-r--r--lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/notify.conf1
-rw-r--r--lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/standard.conf7
-rw-r--r--lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/target_addr.conf2
-rw-r--r--lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/target_params.conf1
-rw-r--r--lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/usm.conf1
-rw-r--r--lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/vacm.conf6
-rw-r--r--lib/common_test/test/ct_surefire_SUITE.erl351
-rw-r--r--lib/common_test/test/ct_surefire_SUITE_data/surefire_SUITE.erl92
-rw-r--r--lib/common_test/test/ct_system_error_SUITE.erl132
-rw-r--r--lib/common_test/test/ct_system_error_SUITE_data/a_SUITE.erl122
-rw-r--r--lib/common_test/test/ct_test_support.erl148
-rw-r--r--lib/common_test/test/ct_testspec_1_SUITE.erl114
-rw-r--r--lib/common_test/test/ct_testspec_2_SUITE.erl759
-rw-r--r--lib/common_test/test/ct_verbosity_SUITE.erl244
-rw-r--r--lib/common_test/test/ct_verbosity_SUITE_data/io_test_SUITE.erl156
-rw-r--r--lib/common_test/vsn.mk2
-rw-r--r--lib/compiler/doc/src/Makefile2
-rw-r--r--lib/compiler/doc/src/compile.xml34
-rw-r--r--lib/compiler/doc/src/notes.xml78
-rw-r--r--lib/compiler/src/Makefile15
-rw-r--r--lib/compiler/src/beam_a.erl97
-rw-r--r--lib/compiler/src/beam_block.erl77
-rw-r--r--lib/compiler/src/beam_bool.erl41
-rw-r--r--lib/compiler/src/beam_bsm.erl157
-rw-r--r--lib/compiler/src/beam_clean.erl45
-rw-r--r--lib/compiler/src/beam_dead.erl16
-rw-r--r--lib/compiler/src/beam_except.erl4
-rw-r--r--lib/compiler/src/beam_flatten.erl53
-rw-r--r--lib/compiler/src/beam_jump.erl127
-rw-r--r--lib/compiler/src/beam_peep.erl8
-rw-r--r--lib/compiler/src/beam_receive.erl54
-rw-r--r--lib/compiler/src/beam_trim.erl68
-rw-r--r--lib/compiler/src/beam_type.erl17
-rw-r--r--lib/compiler/src/beam_utils.erl289
-rw-r--r--lib/compiler/src/beam_validator.erl18
-rw-r--r--lib/compiler/src/beam_z.erl79
-rw-r--r--lib/compiler/src/compile.erl103
-rw-r--r--lib/compiler/src/compiler.app.src3
-rw-r--r--lib/compiler/src/core_lint.erl3
-rw-r--r--lib/compiler/src/core_scan.erl27
-rw-r--r--lib/compiler/src/sys_core_fold.erl149
-rw-r--r--lib/compiler/src/sys_expand_pmod.erl433
-rw-r--r--lib/compiler/src/sys_pre_expand.erl152
-rw-r--r--lib/compiler/src/v3_codegen.erl101
-rw-r--r--lib/compiler/src/v3_core.erl7
-rw-r--r--lib/compiler/src/v3_kernel.erl60
-rw-r--r--lib/compiler/test/Makefile5
-rw-r--r--lib/compiler/test/andor_SUITE.erl7
-rw-r--r--lib/compiler/test/beam_except_SUITE.erl (renamed from lib/compiler/test/beam_expect_SUITE.erl)2
-rw-r--r--lib/compiler/test/beam_validator_SUITE.erl19
-rw-r--r--lib/compiler/test/bs_bit_binaries_SUITE.erl12
-rw-r--r--lib/compiler/test/bs_construct_SUITE.erl40
-rw-r--r--lib/compiler/test/bs_match_SUITE.erl142
-rw-r--r--lib/compiler/test/compilation_SUITE.erl42
-rw-r--r--lib/compiler/test/compile_SUITE.erl71
-rw-r--r--lib/compiler/test/compile_SUITE_data/simple-phony.mk3
-rw-r--r--lib/compiler/test/core_SUITE.erl8
-rw-r--r--lib/compiler/test/core_fold_SUITE.erl8
-rw-r--r--lib/compiler/test/error_SUITE.erl45
-rw-r--r--lib/compiler/test/guard_SUITE.erl19
-rw-r--r--lib/compiler/test/guard_SUITE_data/guard_SUITE_tuple_size.S8
-rw-r--r--lib/compiler/test/inline_SUITE.erl74
-rw-r--r--lib/compiler/test/match_SUITE.erl8
-rw-r--r--lib/compiler/test/misc_SUITE.erl33
-rw-r--r--lib/compiler/test/pmod_SUITE.erl121
-rw-r--r--lib/compiler/test/pmod_SUITE_data/pmod_basic.erl83
-rw-r--r--lib/compiler/test/receive_SUITE.erl6
-rw-r--r--lib/compiler/test/receive_SUITE_data/ref_opt/no_4.erl12
-rw-r--r--lib/compiler/test/receive_SUITE_data/ref_opt/yes_10.erl13
-rw-r--r--lib/compiler/test/receive_SUITE_data/ref_opt/yes_11.erl21
-rw-r--r--lib/compiler/test/receive_SUITE_data/ref_opt/yes_12.erl12
-rw-r--r--lib/compiler/test/receive_SUITE_data/ref_opt/yes_13.erl12
-rw-r--r--lib/compiler/test/record_SUITE.erl10
-rw-r--r--lib/compiler/test/test_lib.erl15
-rw-r--r--lib/compiler/test/trycatch_SUITE.erl12
-rw-r--r--lib/compiler/test/warnings_SUITE.erl14
-rw-r--r--lib/compiler/vsn.mk2
-rw-r--r--lib/cosEvent/doc/src/Makefile2
-rw-r--r--lib/cosEvent/src/Makefile18
-rw-r--r--lib/cosEvent/test/Makefile2
-rw-r--r--lib/cosEventDomain/doc/src/Makefile2
-rw-r--r--lib/cosEventDomain/src/Makefile12
-rw-r--r--lib/cosEventDomain/test/Makefile2
-rw-r--r--lib/cosFileTransfer/doc/src/Makefile2
-rw-r--r--lib/cosFileTransfer/src/Makefile12
-rw-r--r--lib/cosFileTransfer/test/Makefile2
-rw-r--r--lib/cosFileTransfer/test/fileTransfer_SUITE.erl43
-rw-r--r--lib/cosNotification/doc/src/Makefile2
-rw-r--r--lib/cosNotification/src/Makefile26
-rw-r--r--lib/cosNotification/test/Makefile2
-rw-r--r--lib/cosProperty/doc/src/Makefile2
-rw-r--r--lib/cosProperty/src/Makefile12
-rw-r--r--lib/cosProperty/test/Makefile2
-rw-r--r--lib/cosTime/doc/src/Makefile2
-rw-r--r--lib/cosTime/src/Makefile20
-rw-r--r--lib/cosTime/test/Makefile2
-rw-r--r--lib/cosTransactions/doc/src/Makefile2
-rw-r--r--lib/cosTransactions/examples/Makefile2
-rw-r--r--lib/cosTransactions/src/Makefile12
-rw-r--r--lib/cosTransactions/test/Makefile2
-rw-r--r--lib/crypto/c_src/Makefile.in57
-rw-r--r--lib/crypto/c_src/crypto.c1120
-rw-r--r--lib/crypto/c_src/crypto_callback.c165
-rw-r--r--lib/crypto/c_src/crypto_callback.h46
-rwxr-xr-x[-rw-r--r--]lib/crypto/doc/src/crypto.xml196
-rw-r--r--lib/crypto/doc/src/crypto_app.xml2
-rw-r--r--lib/crypto/doc/src/notes.xml49
-rw-r--r--lib/crypto/src/Makefile6
-rw-r--r--lib/crypto/src/crypto.erl306
-rw-r--r--lib/crypto/test/crypto_SUITE.erl739
-rw-r--r--lib/crypto/vsn.mk2
-rw-r--r--lib/debugger/doc/src/Makefile2
-rw-r--r--lib/debugger/doc/src/notes.xml2
-rw-r--r--lib/debugger/priv/Makefile2
-rw-r--r--lib/debugger/src/Makefile6
-rw-r--r--lib/debugger/src/dbg_debugged.erl5
-rw-r--r--lib/debugger/src/dbg_ieval.erl6
-rw-r--r--lib/debugger/src/dbg_ui_trace.erl12
-rw-r--r--lib/debugger/src/dbg_ui_trace_win.erl4
-rw-r--r--lib/debugger/src/dbg_wx_code.erl33
-rw-r--r--lib/debugger/src/dbg_wx_filedialog_win.erl9
-rw-r--r--lib/debugger/src/dbg_wx_settings.erl48
-rw-r--r--lib/debugger/src/dbg_wx_trace.erl14
-rw-r--r--lib/debugger/src/dbg_wx_trace_win.erl19
-rw-r--r--lib/debugger/src/dbg_wx_win.erl10
-rw-r--r--lib/debugger/src/int.erl25
-rw-r--r--lib/debugger/test/Makefile2
-rw-r--r--lib/debugger/test/erl_eval_SUITE.erl34
-rw-r--r--lib/debugger/test/int_eval_SUITE_data/my_int_eval_module.erl6
-rw-r--r--lib/dialyzer/doc/src/dialyzer.xml8
-rw-r--r--lib/dialyzer/doc/src/dialyzer_chapter.xml6
-rw-r--r--lib/dialyzer/doc/src/notes.xml78
-rw-r--r--lib/dialyzer/src/Makefile12
-rw-r--r--lib/dialyzer/src/dialyzer.erl23
-rw-r--r--lib/dialyzer/src/dialyzer.hrl9
-rw-r--r--lib/dialyzer/src/dialyzer_analysis_callgraph.erl30
-rw-r--r--lib/dialyzer/src/dialyzer_behaviours.erl106
-rw-r--r--lib/dialyzer/src/dialyzer_cl.erl5
-rw-r--r--lib/dialyzer/src/dialyzer_cl_parse.erl9
-rw-r--r--lib/dialyzer/src/dialyzer_contracts.erl29
-rw-r--r--lib/dialyzer/src/dialyzer_coordinator.erl7
-rw-r--r--lib/dialyzer/src/dialyzer_dataflow.erl69
-rw-r--r--lib/dialyzer/src/dialyzer_gui_wx.erl324
-rw-r--r--lib/dialyzer/src/dialyzer_options.erl14
-rw-r--r--lib/dialyzer/src/dialyzer_races.erl5
-rw-r--r--lib/dialyzer/src/dialyzer_succ_typings.erl48
-rw-r--r--lib/dialyzer/src/dialyzer_typesig.erl594
-rw-r--r--lib/dialyzer/src/dialyzer_utils.erl2
-rw-r--r--lib/dialyzer/src/dialyzer_worker.erl2
-rw-r--r--lib/dialyzer/test/options1_SUITE_data/src/compiler/core_scan.erl25
-rw-r--r--lib/dialyzer/test/options1_SUITE_data/src/compiler/sys_pre_expand.erl56
-rw-r--r--lib/dialyzer/test/r9c_SUITE_data/results/asn14
-rw-r--r--lib/dialyzer/test/r9c_SUITE_data/results/mnesia3
-rw-r--r--lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_check.erl2
-rw-r--r--lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_gen.erl4
-rw-r--r--lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1rt_ber_bin.erl2
-rw-r--r--lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1rt_ber_bin_v2.erl2
-rw-r--r--lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1rt_per_bin_rt2ct.erl2
-rw-r--r--lib/dialyzer/test/r9c_SUITE_data/src/mnesia/mnesia.erl2
-rw-r--r--lib/dialyzer/test/r9c_SUITE_data/src/mnesia/mnesia_monitor.erl4
-rw-r--r--lib/dialyzer/test/r9c_SUITE_data/src/mnesia/mnesia_sup.erl22
-rw-r--r--lib/dialyzer/test/race_SUITE_data/results/ets_insert_args102
-rw-r--r--lib/dialyzer/test/race_SUITE_data/src/ets_insert_args10.erl19
-rw-r--r--lib/dialyzer/test/small_SUITE_data/results/contracts_with_subtypes2
-rw-r--r--lib/dialyzer/test/small_SUITE_data/results/empty_list_infimum2
-rw-r--r--lib/dialyzer/test/small_SUITE_data/results/fun_ref_match2
-rw-r--r--lib/dialyzer/test/small_SUITE_data/results/port_info_test4
-rw-r--r--lib/dialyzer/test/small_SUITE_data/results/record_creation_diffs3
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/comm_layer/comm_connection.erl5
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/comm_layer/comm_port.erl5
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/empty_list_infimum.erl57
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/record_creation_diffs.erl11
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/remote_tuple_set.erl8
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/unknown_arity_function_spec.erl10
-rw-r--r--lib/dialyzer/vsn.mk2
-rw-r--r--lib/diameter/.gitignore57
-rw-r--r--lib/diameter/Makefile32
-rw-r--r--lib/diameter/Makefile.in88
-rw-r--r--lib/diameter/aclocal.m465
-rwxr-xr-xlib/diameter/autoconf/config.guess1519
-rwxr-xr-xlib/diameter/autoconf/config.sub1630
-rwxr-xr-xlib/diameter/autoconf/configure.vxworks147
-rwxr-xr-xlib/diameter/autoconf/install-sh519
-rw-r--r--lib/diameter/autoconf/vxworks/sed.general124
-rw-r--r--lib/diameter/autoconf/vxworks/sed.vxworks_cpu3245
-rw-r--r--lib/diameter/autoconf/vxworks/sed.vxworks_ppc3252
-rw-r--r--lib/diameter/autoconf/vxworks/sed.vxworks_ppc60351
-rw-r--r--lib/diameter/autoconf/vxworks/sed.vxworks_ppc603_nolongcall51
-rw-r--r--lib/diameter/autoconf/vxworks/sed.vxworks_ppc86050
-rw-r--r--lib/diameter/autoconf/vxworks/sed.vxworks_simlinux59
-rw-r--r--lib/diameter/autoconf/vxworks/sed.vxworks_simso64
-rw-r--r--lib/diameter/autoconf/vxworks/sed.vxworks_sparc38
-rw-r--r--lib/diameter/configure.in137
-rw-r--r--lib/diameter/doc/.gitignore4
-rw-r--r--lib/diameter/doc/src/.gitignore1
-rw-r--r--lib/diameter/doc/src/Makefile23
-rw-r--r--lib/diameter/doc/src/diameter.xml1423
-rw-r--r--lib/diameter/doc/src/diameter_app.xml483
-rw-r--r--lib/diameter/doc/src/diameter_codec.xml389
-rw-r--r--lib/diameter/doc/src/diameter_compile.xml81
-rw-r--r--lib/diameter/doc/src/diameter_dict.xml194
-rw-r--r--lib/diameter/doc/src/diameter_examples.xml3
-rw-r--r--lib/diameter/doc/src/diameter_intro.xml38
-rw-r--r--lib/diameter/doc/src/diameter_make.xml144
-rw-r--r--lib/diameter/doc/src/diameter_sctp.xml81
-rw-r--r--lib/diameter/doc/src/diameter_soc.xml30
-rw-r--r--lib/diameter/doc/src/diameter_tcp.xml84
-rw-r--r--lib/diameter/doc/src/diameter_transport.xml168
-rw-r--r--lib/diameter/doc/src/diameter_using.xml4
-rw-r--r--lib/diameter/doc/src/files.mk4
-rw-r--r--lib/diameter/doc/src/notes.xml314
-rw-r--r--lib/diameter/doc/src/ref_man.xml3
-rw-r--r--lib/diameter/doc/src/seealso.ent132
-rw-r--r--lib/diameter/doc/src/seehere.sed35
-rw-r--r--lib/diameter/doc/standard/draft-ietf-dime-capablities-update-07.txt392
-rw-r--r--lib/diameter/doc/standard/rfc6733.txt (renamed from lib/diameter/doc/standard/draft-ietf-dime-rfc3588bis-26.txt)6264
-rw-r--r--lib/diameter/doc/standard/rfc6737.txt339
-rw-r--r--lib/diameter/examples/code/peer.erl5
-rw-r--r--lib/diameter/include/diameter.hrl5
-rw-r--r--lib/diameter/make/release_targets.mk92
-rw-r--r--lib/diameter/make/rules.mk.in193
-rw-r--r--lib/diameter/make/subdir.mk53
-rw-r--r--lib/diameter/make/target.mk33
-rw-r--r--lib/diameter/src/.gitignore1
-rw-r--r--lib/diameter/src/Makefile51
-rw-r--r--lib/diameter/src/base/diameter.app.src2
-rw-r--r--lib/diameter/src/base/diameter.appup.src24
-rw-r--r--lib/diameter/src/base/diameter.erl28
-rw-r--r--lib/diameter/src/base/diameter_capx.erl8
-rw-r--r--lib/diameter/src/base/diameter_codec.erl22
-rw-r--r--lib/diameter/src/base/diameter_config.erl88
-rw-r--r--lib/diameter/src/base/diameter_peer.erl143
-rw-r--r--lib/diameter/src/base/diameter_peer_fsm.erl508
-rw-r--r--lib/diameter/src/base/diameter_reg.erl255
-rw-r--r--lib/diameter/src/base/diameter_service.erl1328
-rw-r--r--lib/diameter/src/base/diameter_session.erl14
-rw-r--r--lib/diameter/src/base/diameter_stats.erl265
-rw-r--r--lib/diameter/src/base/diameter_watchdog.erl213
-rw-r--r--lib/diameter/src/modules.mk12
-rw-r--r--lib/diameter/src/transport/diameter_etcp.erl22
-rw-r--r--lib/diameter/src/transport/diameter_sctp.erl129
-rw-r--r--lib/diameter/src/transport/diameter_tcp.erl79
-rw-r--r--lib/diameter/src/transport/diameter_transport.erl55
-rw-r--r--lib/diameter/test/Makefile18
-rw-r--r--lib/diameter/test/diameter_capx_SUITE.erl6
-rw-r--r--lib/diameter/test/diameter_compiler_SUITE.erl14
-rw-r--r--lib/diameter/test/diameter_ct.erl13
-rw-r--r--lib/diameter/test/diameter_dict_SUITE.erl4
-rw-r--r--lib/diameter/test/diameter_dpr_SUITE.erl196
-rw-r--r--lib/diameter/test/diameter_event_SUITE.erl182
-rw-r--r--lib/diameter/test/diameter_failover_SUITE.erl145
-rw-r--r--lib/diameter/test/diameter_gen_sctp_SUITE.erl19
-rw-r--r--lib/diameter/test/diameter_reg_SUITE.erl4
-rw-r--r--lib/diameter/test/diameter_relay_SUITE.erl11
-rw-r--r--lib/diameter/test/diameter_stats_SUITE.erl94
-rw-r--r--lib/diameter/test/diameter_sync_SUITE.erl4
-rw-r--r--lib/diameter/test/diameter_tls_SUITE.erl4
-rw-r--r--lib/diameter/test/diameter_traffic_SUITE.erl447
-rw-r--r--lib/diameter/test/diameter_util.erl55
-rw-r--r--lib/diameter/test/diameter_watchdog_SUITE.erl711
-rw-r--r--lib/diameter/test/modules.mk6
-rw-r--r--lib/diameter/vsn.mk4
-rw-r--r--lib/edoc/doc/src/notes.xml21
-rw-r--r--lib/edoc/priv/Makefile7
-rw-r--r--lib/edoc/priv/edoc.dtd4
-rw-r--r--lib/edoc/src/Makefile6
-rw-r--r--lib/edoc/src/edoc.erl9
-rw-r--r--lib/edoc/src/edoc.hrl6
-rw-r--r--lib/edoc/src/edoc_data.erl8
-rw-r--r--lib/edoc/src/edoc_doclet.erl28
-rw-r--r--lib/edoc/src/edoc_extract.erl16
-rw-r--r--lib/edoc/src/edoc_layout.erl15
-rw-r--r--lib/edoc/src/edoc_lib.erl71
-rw-r--r--lib/edoc/src/edoc_macros.erl4
-rw-r--r--lib/edoc/src/edoc_parser.yrl12
-rw-r--r--lib/edoc/src/edoc_refs.erl19
-rw-r--r--lib/edoc/src/edoc_wiki.erl3
-rw-r--r--lib/edoc/vsn.mk2
-rw-r--r--lib/eldap/doc/src/eldap.xml2
-rw-r--r--lib/eldap/src/Makefile6
-rw-r--r--lib/eldap/src/eldap.erl35
-rw-r--r--lib/eldap/vsn.mk2
-rw-r--r--lib/erl_docgen/doc/src/Makefile2
-rw-r--r--lib/erl_docgen/doc/src/doc-build.xml5
-rw-r--r--lib/erl_docgen/doc/src/notes.xml35
-rw-r--r--lib/erl_docgen/priv/bin/Makefile2
-rwxr-xr-xlib/erl_docgen/priv/bin/xml_from_edoc.escript8
-rw-r--r--lib/erl_docgen/priv/css/Makefile2
-rw-r--r--lib/erl_docgen/priv/dtd/Makefile2
-rw-r--r--lib/erl_docgen/priv/dtd/common.refs.dtd7
-rw-r--r--lib/erl_docgen/priv/dtd/erlref.dtd3
-rw-r--r--lib/erl_docgen/priv/dtd_html_entities/Makefile2
-rw-r--r--lib/erl_docgen/priv/dtd_man_entities/Makefile2
-rw-r--r--lib/erl_docgen/priv/fop.xconf45
-rw-r--r--lib/erl_docgen/priv/images/Makefile2
-rw-r--r--lib/erl_docgen/priv/js/flipmenu/Makefile2
-rw-r--r--lib/erl_docgen/priv/xsl/Makefile5
-rw-r--r--lib/erl_docgen/priv/xsl/db_html.xsl57
-rw-r--r--lib/erl_docgen/priv/xsl/db_man.xsl26
-rw-r--r--lib/erl_docgen/priv/xsl/db_pdf.xsl34
-rw-r--r--lib/erl_docgen/priv/xsl/db_pdf_params.xsl38
-rw-r--r--lib/erl_docgen/src/Makefile6
-rw-r--r--lib/erl_docgen/src/docgen_edoc_xml_cb.erl51
-rw-r--r--lib/erl_docgen/src/docgen_xmerl_xml_cb.erl28
-rw-r--r--lib/erl_docgen/vsn.mk3
-rw-r--r--lib/erl_interface/aclocal.m4143
-rw-r--r--lib/erl_interface/configure.in14
-rw-r--r--lib/erl_interface/doc/src/Makefile2
-rw-r--r--lib/erl_interface/doc/src/ei.xml64
-rw-r--r--lib/erl_interface/doc/src/erl_eterm.xml18
-rw-r--r--lib/erl_interface/doc/src/notes.xml37
-rw-r--r--lib/erl_interface/include/ei.h40
-rw-r--r--lib/erl_interface/include/erl_interface.h40
-rw-r--r--lib/erl_interface/src/Makefile5
-rw-r--r--lib/erl_interface/src/Makefile.in147
-rw-r--r--lib/erl_interface/src/connect/ei_connect.c7
-rw-r--r--lib/erl_interface/src/connect/ei_connect_int.h2
-rw-r--r--lib/erl_interface/src/connect/ei_resolve.c8
-rw-r--r--lib/erl_interface/src/connect/eirecv.c12
-rw-r--r--lib/erl_interface/src/decode/decode_atom.c159
-rw-r--r--lib/erl_interface/src/decode/decode_boolean.c36
-rw-r--r--lib/erl_interface/src/decode/decode_fun.c6
-rw-r--r--lib/erl_interface/src/decode/decode_pid.c14
-rw-r--r--lib/erl_interface/src/decode/decode_port.c13
-rw-r--r--lib/erl_interface/src/decode/decode_ref.c28
-rw-r--r--lib/erl_interface/src/encode/encode_atom.c154
-rw-r--r--lib/erl_interface/src/encode/encode_fun.c4
-rw-r--r--lib/erl_interface/src/encode/encode_pid.c24
-rw-r--r--lib/erl_interface/src/encode/encode_port.c23
-rw-r--r--lib/erl_interface/src/encode/encode_ref.c24
-rw-r--r--lib/erl_interface/src/legacy/erl_connect.c18
-rw-r--r--lib/erl_interface/src/legacy/erl_eterm.c158
-rw-r--r--lib/erl_interface/src/legacy/erl_eterm.h4
-rw-r--r--lib/erl_interface/src/legacy/erl_format.c20
-rw-r--r--lib/erl_interface/src/legacy/erl_malloc.c20
-rw-r--r--lib/erl_interface/src/legacy/erl_marshal.c413
-rw-r--r--lib/erl_interface/src/legacy/global_whereis.c11
-rw-r--r--lib/erl_interface/src/misc/ei_decode_term.c40
-rw-r--r--lib/erl_interface/src/misc/ei_format.c4
-rw-r--r--lib/erl_interface/src/misc/ei_printterm.c7
-rw-r--r--lib/erl_interface/src/misc/ei_x_encode.c21
-rw-r--r--lib/erl_interface/src/misc/get_type.c79
-rw-r--r--lib/erl_interface/src/misc/putget.h7
-rw-r--r--lib/erl_interface/src/misc/show_msg.c14
-rw-r--r--lib/erl_interface/src/prog/ei_fake_prog.c6
-rw-r--r--lib/erl_interface/test/Makefile2
-rw-r--r--lib/erl_interface/test/all_SUITE_data/Makefile.src2
-rw-r--r--lib/erl_interface/test/all_SUITE_data/gccifier.c2
-rw-r--r--lib/erl_interface/test/all_SUITE_data/init_tc.erl2
-rw-r--r--lib/erl_interface/test/ei_decode_SUITE.erl9
-rw-r--r--lib/erl_interface/test/ei_decode_encode_SUITE.erl121
-rw-r--r--lib/erl_interface/test/ei_decode_encode_SUITE_data/ei_decode_encode_test.c396
-rw-r--r--lib/erl_interface/test/ei_encode_SUITE.erl13
-rw-r--r--lib/erl_interface/test/ei_print_SUITE.erl4
-rw-r--r--lib/erl_interface/test/ei_tmo_SUITE.erl2
-rw-r--r--lib/erl_interface/test/erl_match_SUITE.erl2
-rw-r--r--lib/erl_interface/vsn.mk2
-rw-r--r--lib/et/doc/src/Makefile2
-rw-r--r--lib/et/doc/src/et_intro.xml2
-rw-r--r--lib/et/doc/src/notes.xml17
-rw-r--r--lib/et/examples/Makefile2
-rw-r--r--lib/et/src/Makefile6
-rw-r--r--lib/et/src/et.erl4
-rw-r--r--lib/et/src/et_wx_contents_viewer.erl23
-rw-r--r--lib/et/test/Makefile2
-rw-r--r--lib/et/vsn.mk2
-rw-r--r--lib/eunit/doc/overview.edoc8
-rw-r--r--lib/eunit/doc/src/notes.xml34
-rw-r--r--lib/eunit/include/eunit.hrl170
-rw-r--r--lib/eunit/src/Makefile4
-rw-r--r--lib/eunit/src/eunit.app.src1
-rw-r--r--lib/eunit/src/eunit.erl12
-rw-r--r--lib/eunit/src/eunit_autoexport.erl7
-rw-r--r--lib/eunit/src/eunit_data.erl39
-rw-r--r--lib/eunit/src/eunit_lib.erl99
-rw-r--r--lib/eunit/src/eunit_surefire.erl37
-rw-r--r--lib/eunit/src/eunit_test.erl26
-rw-r--r--lib/eunit/src/eunit_tty.erl59
-rw-r--r--lib/eunit/test/Makefile2
-rw-r--r--lib/eunit/vsn.mk2
-rw-r--r--lib/gs/contribs/bonk/Makefile10
-rw-r--r--lib/gs/contribs/cols/Makefile14
-rw-r--r--lib/gs/contribs/mandel/Makefile14
-rw-r--r--lib/gs/contribs/mandel/mandel.erl3
-rw-r--r--lib/gs/contribs/othello/Makefile10
-rw-r--r--lib/gs/doc/src/Makefile2
-rw-r--r--lib/gs/doc/src/notes.xml2
-rw-r--r--lib/gs/examples/Makefile2
-rw-r--r--lib/gs/src/Makefile8
-rw-r--r--lib/gs/src/gstk_editor.erl6
-rw-r--r--lib/gs/tcl/Makefile.in2
-rw-r--r--lib/hipe/Makefile10
-rw-r--r--lib/hipe/amd64/hipe_amd64_encode.erl10
-rw-r--r--lib/hipe/cerl/erl_bif_types.erl57
-rw-r--r--lib/hipe/cerl/erl_types.erl30
-rw-r--r--lib/hipe/doc/src/notes.xml62
-rw-r--r--lib/hipe/flow/hipe_dominators.erl6
-rw-r--r--lib/hipe/icode/hipe_beam_to_icode.erl37
-rw-r--r--lib/hipe/icode/hipe_icode.hrl8
-rw-r--r--lib/hipe/icode/hipe_icode_mulret.erl14
-rw-r--r--lib/hipe/icode/hipe_icode_type.erl6
-rw-r--r--lib/hipe/main/Makefile8
-rw-r--r--lib/hipe/misc/Makefile2
-rw-r--r--lib/hipe/regalloc/hipe_coalescing_regalloc.erl8
-rw-r--r--lib/hipe/regalloc/hipe_optimistic_regalloc.erl12
-rw-r--r--lib/hipe/regalloc/hipe_reg_worklists.erl6
-rw-r--r--lib/hipe/rtl/Makefile4
-rw-r--r--lib/hipe/rtl/hipe_rtl_arith.inc6
-rw-r--r--lib/hipe/rtl/hipe_rtl_mk_switch.erl6
-rw-r--r--lib/hipe/rtl/hipe_rtl_primops.erl6
-rw-r--r--lib/hipe/rtl/hipe_rtl_ssa_const_prop.erl6
-rw-r--r--lib/hipe/rtl/hipe_rtl_ssapre.erl10
-rw-r--r--lib/hipe/sparc/hipe_rtl_to_sparc.erl38
-rw-r--r--lib/hipe/ssa/hipe_ssa.inc6
-rw-r--r--lib/hipe/tools/Makefile2
-rw-r--r--lib/hipe/vsn.mk2
-rw-r--r--lib/hipe/x86/hipe_x86_assemble.erl4
-rw-r--r--lib/hipe/x86/hipe_x86_postpass.erl8
-rw-r--r--lib/hipe/x86/hipe_x86_ra_postconditions.erl4
-rw-r--r--lib/ic/c_src/Makefile.in12
-rw-r--r--lib/ic/doc/src/Makefile2
-rw-r--r--lib/ic/doc/src/ic_clib.xml14
-rw-r--r--lib/ic/doc/src/notes.xml26
-rw-r--r--lib/ic/examples/all-against-all/client.c1
-rw-r--r--lib/ic/examples/c-client/client.c1
-rw-r--r--lib/ic/examples/c-server/client.c1
-rw-r--r--lib/ic/examples/pre_post_condition/Makefile6
-rw-r--r--lib/ic/java_src/com/ericsson/otp/ic/Makefile7
-rw-r--r--lib/ic/src/Makefile6
-rw-r--r--lib/ic/src/ic.erl2
-rw-r--r--lib/ic/src/ic_pp.erl2
-rw-r--r--lib/ic/test/Makefile2
-rw-r--r--lib/ic/test/java_client_erl_server_SUITE.erl13
-rw-r--r--lib/ic/test/java_client_erl_server_SUITE_data/Makefile.src17
-rw-r--r--lib/ic/vsn.mk2
-rw-r--r--lib/inets/doc/src/http_uri.xml5
-rw-r--r--lib/inets/doc/src/httpc.xml178
-rw-r--r--lib/inets/doc/src/httpd.xml10
-rw-r--r--lib/inets/doc/src/notes.xml158
-rw-r--r--lib/inets/examples/httpd_load_test/Makefile2
-rw-r--r--lib/inets/examples/server_root/Makefile2
-rw-r--r--lib/inets/priv/Makefile2
-rw-r--r--lib/inets/src/ftp/Makefile2
-rw-r--r--lib/inets/src/http_client/Makefile2
-rw-r--r--lib/inets/src/http_client/httpc.erl38
-rw-r--r--lib/inets/src/http_client/httpc_handler.erl1022
-rw-r--r--lib/inets/src/http_client/httpc_internal.hrl2
-rw-r--r--lib/inets/src/http_client/httpc_manager.erl198
-rw-r--r--lib/inets/src/http_lib/Makefile2
-rw-r--r--lib/inets/src/http_server/httpd_conf.erl19
-rw-r--r--lib/inets/src/http_server/httpd_log.erl12
-rw-r--r--lib/inets/src/http_server/httpd_request_handler.erl62
-rw-r--r--lib/inets/src/inets_app/Makefile6
-rw-r--r--lib/inets/src/inets_app/inets.appup.src96
-rw-r--r--lib/inets/src/inets_app/inets.erl9
-rw-r--r--lib/inets/src/inets_app/inets.mk6
-rw-r--r--lib/inets/src/inets_app/inets_service.erl17
-rw-r--r--lib/inets/src/tftp/Makefile2
-rw-r--r--lib/inets/src/tftp/tftp.erl18
-rw-r--r--lib/inets/test/Makefile6
-rw-r--r--lib/inets/test/erl_make_certs.erl429
-rw-r--r--lib/inets/test/ftp_suite_lib.erl1
-rw-r--r--lib/inets/test/httpc_SUITE.erl698
-rw-r--r--lib/inets/test/httpc_cookie_SUITE.erl3
-rw-r--r--lib/inets/test/httpc_proxy_SUITE.erl575
-rw-r--r--lib/inets/test/httpc_proxy_SUITE_data/apache2/apache2.conf87
-rw-r--r--lib/inets/test/httpc_proxy_SUITE_data/apache2/htdocs/index.html4
-rwxr-xr-xlib/inets/test/httpc_proxy_SUITE_data/server_proxy.sh198
-rw-r--r--lib/inets/test/httpd_SUITE.erl49
-rw-r--r--lib/inets/test/httpd_basic_SUITE.erl16
-rw-r--r--lib/inets/test/httpd_mod.erl237
-rw-r--r--lib/inets/test/inets.spec.vxworks5
-rw-r--r--lib/inets/test/inets_SUITE.erl7
-rw-r--r--lib/inets/test/inets_app_test.erl18
-rw-r--r--lib/inets/test/inets_sup_SUITE.erl6
-rw-r--r--lib/inets/test/inets_test_lib.erl39
-rw-r--r--lib/inets/test/rules.mk7
-rw-r--r--lib/inets/vsn.mk2
-rw-r--r--lib/jinterface/doc/src/Makefile2
-rw-r--r--lib/jinterface/doc/src/notes.xml2
-rw-r--r--lib/jinterface/java_src/Makefile2
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractNode.java4
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/Makefile21
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpEpmd.java2
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangAtom.java2
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpExternal.java6
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpInputStream.java76
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java60
-rw-r--r--lib/jinterface/test/Makefile2
-rw-r--r--lib/jinterface/test/jitu.erl14
-rw-r--r--lib/jinterface/test/nc_SUITE.erl171
-rw-r--r--lib/kernel/doc/src/Makefile3
-rw-r--r--lib/kernel/doc/src/application.xml9
-rw-r--r--lib/kernel/doc/src/code.xml2
-rw-r--r--lib/kernel/doc/src/error_handler.xml38
-rw-r--r--lib/kernel/doc/src/file.xml91
-rw-r--r--lib/kernel/doc/src/gen_sctp.xml2
-rw-r--r--lib/kernel/doc/src/gen_udp.xml2
-rw-r--r--lib/kernel/doc/src/global.xml12
-rw-r--r--lib/kernel/doc/src/heart.xml35
-rw-r--r--lib/kernel/doc/src/inet.xml195
-rw-r--r--lib/kernel/doc/src/notes.xml131
-rw-r--r--lib/kernel/doc/src/os.xml20
-rw-r--r--lib/kernel/doc/src/packages.xml214
-rw-r--r--lib/kernel/doc/src/ref_man.xml1
-rw-r--r--lib/kernel/examples/uds_dist/c_src/uds_drv.c4
-rw-r--r--lib/kernel/include/dist.hrl1
-rw-r--r--lib/kernel/include/file.hrl59
-rw-r--r--lib/kernel/internal_doc/distribution_handshake.txt216
-rw-r--r--lib/kernel/src/Makefile13
-rw-r--r--lib/kernel/src/application.erl21
-rw-r--r--lib/kernel/src/application_controller.erl19
-rw-r--r--lib/kernel/src/code.erl17
-rw-r--r--lib/kernel/src/code_server.erl13
-rw-r--r--lib/kernel/src/disk_log.erl2
-rw-r--r--lib/kernel/src/dist_util.erl6
-rw-r--r--lib/kernel/src/erl_ddll.erl4
-rw-r--r--lib/kernel/src/error_handler.erl53
-rw-r--r--lib/kernel/src/file.erl21
-rw-r--r--lib/kernel/src/file_io_server.erl22
-rw-r--r--lib/kernel/src/gen_sctp.erl2
-rw-r--r--lib/kernel/src/gen_tcp.erl10
-rw-r--r--lib/kernel/src/gen_udp.erl8
-rw-r--r--lib/kernel/src/global.erl12
-rw-r--r--lib/kernel/src/group.erl148
-rw-r--r--lib/kernel/src/heart.erl11
-rw-r--r--lib/kernel/src/inet.erl106
-rw-r--r--lib/kernel/src/inet_config.erl59
-rw-r--r--lib/kernel/src/inet_int.hrl5
-rw-r--r--lib/kernel/src/inet_parse.erl29
-rw-r--r--lib/kernel/src/kernel.app.src1
-rw-r--r--lib/kernel/src/os.erl41
-rw-r--r--lib/kernel/src/packages.erl158
-rw-r--r--lib/kernel/src/pg2.erl4
-rw-r--r--lib/kernel/src/ram_file.erl7
-rw-r--r--lib/kernel/src/rpc.erl6
-rw-r--r--lib/kernel/src/user.erl190
-rw-r--r--lib/kernel/src/wrap_log_reader.erl4
-rw-r--r--lib/kernel/test/Makefile4
-rw-r--r--lib/kernel/test/application_SUITE.erl13
-rw-r--r--lib/kernel/test/bif_SUITE.erl2
-rw-r--r--lib/kernel/test/code_SUITE.erl50
-rw-r--r--lib/kernel/test/disk_log_SUITE.erl43
-rw-r--r--lib/kernel/test/disk_log_SUITE_data/Makefile.src15
-rw-r--r--lib/kernel/test/disk_log_SUITE_data/nfs_check.c46
-rw-r--r--lib/kernel/test/erl_prim_loader_SUITE.erl92
-rw-r--r--lib/kernel/test/error_handler_SUITE.erl68
-rw-r--r--lib/kernel/test/error_logger_warn_SUITE.erl4
-rw-r--r--lib/kernel/test/file_SUITE.erl685
-rw-r--r--lib/kernel/test/file_name_SUITE.erl219
-rw-r--r--lib/kernel/test/gen_sctp_SUITE.erl242
-rw-r--r--lib/kernel/test/gen_tcp_echo_SUITE.erl25
-rw-r--r--lib/kernel/test/gen_tcp_misc_SUITE.erl202
-rw-r--r--lib/kernel/test/gen_udp_SUITE.erl5
-rw-r--r--lib/kernel/test/global_SUITE.erl158
-rw-r--r--lib/kernel/test/heart_SUITE.erl447
-rw-r--r--lib/kernel/test/heart_SUITE_data/simple_echo.c5
-rw-r--r--lib/kernel/test/ignore_cores.erl158
-rw-r--r--lib/kernel/test/inet_SUITE.erl40
-rw-r--r--lib/kernel/test/inet_sockopt_SUITE.erl155
-rw-r--r--lib/kernel/test/inet_sockopt_SUITE_data/sockopt_helper.c11
-rw-r--r--lib/kernel/test/init_SUITE.erl153
-rw-r--r--lib/kernel/test/interactive_shell_SUITE.erl217
-rw-r--r--lib/kernel/test/kernel.cover2
-rw-r--r--lib/kernel/test/kernel.spec.wxworks63
-rw-r--r--lib/kernel/test/os_SUITE.erl2
-rw-r--r--lib/kernel/test/pdict_SUITE.erl1
-rw-r--r--lib/kernel/test/prim_file_SUITE.erl291
-rw-r--r--lib/kernel/test/ram_file_SUITE.erl17
-rw-r--r--lib/kernel/test/wrap_log_reader_SUITE.erl4
-rw-r--r--lib/megaco/Makefile2
-rw-r--r--lib/megaco/aclocal.m41905
-rw-r--r--lib/megaco/configure.in7
-rw-r--r--lib/megaco/doc/src/Makefile2
-rw-r--r--lib/megaco/doc/src/definitions/term.defs1
-rw-r--r--lib/megaco/doc/src/definitions/term.defs.xml7
-rw-r--r--lib/megaco/doc/src/megaco_encode.xml23
-rw-r--r--lib/megaco/doc/src/notes.xml72
-rw-r--r--lib/megaco/examples/meas/Makefile.in10
-rw-r--r--lib/megaco/examples/meas/megaco_codec_meas.erl14
-rw-r--r--lib/megaco/examples/meas/megaco_codec_mstone_lib.erl38
-rw-r--r--lib/megaco/examples/meas/megaco_codec_transform.erl4
-rw-r--r--lib/megaco/examples/simple/Makefile2
-rw-r--r--lib/megaco/src/app/Makefile6
-rw-r--r--lib/megaco/src/app/megaco.app.src26
-rw-r--r--lib/megaco/src/app/megaco.appup.src11
-rw-r--r--lib/megaco/src/binary/Makefile41
-rw-r--r--lib/megaco/src/binary/depend.mk329
-rw-r--r--lib/megaco/src/binary/megaco_ber_bin_drv_media_gateway_control_prev3a.asn1config43
-rw-r--r--lib/megaco/src/binary/megaco_ber_bin_drv_media_gateway_control_prev3a.set.asn1
-rw-r--r--lib/megaco/src/binary/megaco_ber_bin_drv_media_gateway_control_prev3b.asn1config43
-rw-r--r--lib/megaco/src/binary/megaco_ber_bin_drv_media_gateway_control_prev3b.set.asn1
-rw-r--r--lib/megaco/src/binary/megaco_ber_bin_drv_media_gateway_control_prev3c.asn1config43
-rw-r--r--lib/megaco/src/binary/megaco_ber_bin_drv_media_gateway_control_prev3c.set.asn1
-rw-r--r--lib/megaco/src/binary/megaco_ber_bin_drv_media_gateway_control_v1.set.asn1
-rw-r--r--lib/megaco/src/binary/megaco_ber_bin_drv_media_gateway_control_v2.asn1config43
-rw-r--r--lib/megaco/src/binary/megaco_ber_bin_drv_media_gateway_control_v2.set.asn1
-rw-r--r--lib/megaco/src/binary/megaco_ber_bin_drv_media_gateway_control_v3.asn1config43
-rw-r--r--lib/megaco/src/binary/megaco_ber_bin_drv_media_gateway_control_v3.set.asn1
-rw-r--r--lib/megaco/src/binary/megaco_ber_bin_encoder.erl716
-rw-r--r--lib/megaco/src/binary/megaco_ber_bin_media_gateway_control_prev3a.set.asn1
-rw-r--r--lib/megaco/src/binary/megaco_ber_bin_media_gateway_control_prev3b.set.asn1
-rw-r--r--lib/megaco/src/binary/megaco_ber_bin_media_gateway_control_prev3c.asn1config43
-rw-r--r--lib/megaco/src/binary/megaco_ber_bin_media_gateway_control_prev3c.set.asn1
-rw-r--r--lib/megaco/src/binary/megaco_ber_bin_media_gateway_control_v1.asn1config44
-rw-r--r--lib/megaco/src/binary/megaco_ber_bin_media_gateway_control_v1.set.asn1
-rw-r--r--lib/megaco/src/binary/megaco_ber_bin_media_gateway_control_v2.set.asn1
-rw-r--r--lib/megaco/src/binary/megaco_ber_bin_media_gateway_control_v3.set.asn1
-rw-r--r--lib/megaco/src/binary/megaco_ber_media_gateway_control_prev3a.asn1config (renamed from lib/megaco/src/binary/megaco_ber_bin_media_gateway_control_v2.asn1config)4
-rw-r--r--lib/megaco/src/binary/megaco_ber_media_gateway_control_prev3b.asn1config (renamed from lib/megaco/src/binary/megaco_ber_bin_media_gateway_control_v3.asn1config)4
-rw-r--r--lib/megaco/src/binary/megaco_ber_media_gateway_control_prev3c.asn1config (renamed from lib/megaco/src/binary/megaco_ber_bin_media_gateway_control_prev3a.asn1config)4
-rw-r--r--lib/megaco/src/binary/megaco_ber_media_gateway_control_v1.asn1config (renamed from lib/megaco/src/binary/megaco_ber_bin_drv_media_gateway_control_v1.asn1config)6
-rw-r--r--lib/megaco/src/binary/megaco_ber_media_gateway_control_v3.asn1config (renamed from lib/megaco/src/binary/megaco_ber_bin_media_gateway_control_prev3b.asn1config)4
-rw-r--r--lib/megaco/src/binary/megaco_binary_encoder.erl285
-rw-r--r--lib/megaco/src/binary/megaco_binary_encoder_lib.erl15
-rw-r--r--lib/megaco/src/binary/megaco_binary_name_resolver_prev3a.erl6
-rw-r--r--lib/megaco/src/binary/megaco_binary_name_resolver_prev3b.erl6
-rw-r--r--lib/megaco/src/binary/megaco_binary_name_resolver_prev3c.erl6
-rw-r--r--lib/megaco/src/binary/megaco_binary_name_resolver_v3.erl6
-rw-r--r--lib/megaco/src/binary/megaco_per_bin_drv_media_gateway_control_prev3a.set.asn1
-rw-r--r--lib/megaco/src/binary/megaco_per_bin_drv_media_gateway_control_prev3b.set.asn1
-rw-r--r--lib/megaco/src/binary/megaco_per_bin_drv_media_gateway_control_prev3c.set.asn1
-rw-r--r--lib/megaco/src/binary/megaco_per_bin_drv_media_gateway_control_v1.set.asn1
-rw-r--r--lib/megaco/src/binary/megaco_per_bin_drv_media_gateway_control_v2.set.asn1
-rw-r--r--lib/megaco/src/binary/megaco_per_bin_drv_media_gateway_control_v3.set.asn1
-rw-r--r--lib/megaco/src/binary/megaco_per_bin_encoder.erl447
-rw-r--r--lib/megaco/src/binary/megaco_per_bin_media_gateway_control_prev3a.set.asn1
-rw-r--r--lib/megaco/src/binary/megaco_per_bin_media_gateway_control_prev3b.set.asn1
-rw-r--r--lib/megaco/src/binary/megaco_per_bin_media_gateway_control_prev3c.set.asn1
-rw-r--r--lib/megaco/src/binary/megaco_per_bin_media_gateway_control_v1.set.asn1
-rw-r--r--lib/megaco/src/binary/megaco_per_bin_media_gateway_control_v2.set.asn1
-rw-r--r--lib/megaco/src/binary/megaco_per_bin_media_gateway_control_v3.set.asn1
-rw-r--r--lib/megaco/src/binary/modules.mk50
-rw-r--r--lib/megaco/src/engine/Makefile2
-rw-r--r--lib/megaco/src/engine/modules.mk2
-rw-r--r--lib/megaco/src/flex/Makefile.in57
-rw-r--r--lib/megaco/src/flex/megaco_flex_scanner_drv.flex.src10
-rw-r--r--lib/megaco/src/rules.mk4
-rw-r--r--lib/megaco/src/tcp/Makefile2
-rw-r--r--lib/megaco/src/text/Makefile2
-rw-r--r--lib/megaco/src/udp/Makefile2
-rw-r--r--lib/megaco/test/Makefile2
-rw-r--r--lib/megaco/test/megaco.spec.vxworks5
-rw-r--r--lib/megaco/test/megaco_actions_test.erl36
-rw-r--r--lib/megaco/test/megaco_call_flow_test.erl22
-rw-r--r--lib/megaco/test/megaco_codec_prev3a_test.erl35
-rw-r--r--lib/megaco/test/megaco_codec_prev3b_test.erl34
-rw-r--r--lib/megaco/test/megaco_codec_prev3c_test.erl43
-rw-r--r--lib/megaco/test/megaco_codec_v1_test.erl36
-rw-r--r--lib/megaco/test/megaco_codec_v2_test.erl35
-rw-r--r--lib/megaco/test/megaco_codec_v3_test.erl43
-rw-r--r--lib/megaco/test/megaco_mess_test.erl4
-rw-r--r--lib/megaco/test/megaco_mib_test.erl6
-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/vsn.mk2
-rw-r--r--lib/mnesia/doc/src/Makefile2
-rw-r--r--lib/mnesia/doc/src/notes.xml34
-rw-r--r--lib/mnesia/examples/Makefile2
-rw-r--r--lib/mnesia/examples/mnesia_tpcb.erl56
-rw-r--r--lib/mnesia/include/Makefile2
-rw-r--r--lib/mnesia/include/mnemosyne.hrl18
-rw-r--r--lib/mnesia/src/Makefile6
-rw-r--r--lib/mnesia/src/mnesia.appup.src6
-rw-r--r--lib/mnesia/src/mnesia.erl2
-rw-r--r--lib/mnesia/src/mnesia_controller.erl15
-rw-r--r--lib/mnesia/src/mnesia_event.erl2
-rw-r--r--lib/mnesia/src/mnesia_index.erl8
-rw-r--r--lib/mnesia/src/mnesia_locker.erl6
-rw-r--r--lib/mnesia/src/mnesia_monitor.erl4
-rw-r--r--lib/mnesia/src/mnesia_recover.erl66
-rw-r--r--lib/mnesia/src/mnesia_sup.erl13
-rw-r--r--lib/mnesia/src/mnesia_tm.erl3
-rw-r--r--lib/mnesia/test/Makefile5
-rw-r--r--lib/mnesia/test/mnesia.spec3
-rw-r--r--lib/mnesia/test/mnesia.spec.vxworks362
-rw-r--r--lib/mnesia/test/mnesia_SUITE.erl1
-rw-r--r--lib/mnesia/test/mnesia_bench.spec1
-rw-r--r--lib/mnesia/test/mnesia_bench_SUITE.erl69
-rw-r--r--lib/mnesia/test/mnesia_config_test.erl42
-rw-r--r--lib/mnesia/test/mnesia_consistency_test.erl2
-rw-r--r--lib/mnesia/test/mnesia_dirty_access_test.erl5
-rw-r--r--lib/mnesia/test/mnesia_measure_test.erl3
-rw-r--r--lib/mnesia/test/mnesia_recovery_test.erl92
-rw-r--r--lib/mnesia/test/mnesia_test_lib.erl16
-rw-r--r--lib/mnesia/test/mnesia_trans_access_test.erl6
-rw-r--r--lib/mnesia/vsn.mk2
-rw-r--r--lib/observer/doc/src/notes.xml71
-rw-r--r--lib/observer/src/Makefile4
-rw-r--r--lib/observer/src/observer.app.src14
-rw-r--r--lib/observer/src/observer_app_wx.erl17
-rw-r--r--lib/observer/src/observer_lib.erl54
-rw-r--r--lib/observer/src/observer_perf_wx.erl16
-rw-r--r--lib/observer/src/observer_pro_wx.erl5
-rw-r--r--lib/observer/src/observer_procinfo.erl10
-rw-r--r--lib/observer/src/observer_sys_wx.erl4
-rw-r--r--lib/observer/src/observer_trace_wx.erl4
-rw-r--r--lib/observer/src/observer_tv_table.erl66
-rw-r--r--lib/observer/src/observer_wx.erl117
-rw-r--r--lib/observer/test/crashdump_viewer_SUITE.erl66
-rw-r--r--lib/observer/vsn.mk2
-rw-r--r--lib/odbc/aclocal.m4143
-rw-r--r--lib/odbc/c_src/Makefile.in8
-rw-r--r--lib/odbc/c_src/odbcserver.c188
-rw-r--r--lib/odbc/c_src/odbcserver.h6
-rw-r--r--lib/odbc/configure.in5
-rw-r--r--lib/odbc/doc/src/Makefile2
-rw-r--r--lib/odbc/doc/src/notes.xml24
-rw-r--r--lib/odbc/doc/src/odbc.xml37
-rw-r--r--lib/odbc/src/Makefile6
-rw-r--r--lib/odbc/src/odbc.appup.src8
-rw-r--r--lib/odbc/src/odbc.erl38
-rw-r--r--lib/odbc/test/Makefile2
-rw-r--r--lib/odbc/test/odbc_connect_SUITE.erl32
-rw-r--r--lib/odbc/test/odbc_data_type_SUITE.erl7
-rw-r--r--lib/odbc/test/odbc_test_lib.erl3
-rw-r--r--lib/odbc/vsn.mk2
-rw-r--r--lib/orber/COSS/CosNaming/Makefile10
-rw-r--r--lib/orber/c_src/Makefile.in22
-rw-r--r--lib/orber/doc/src/Makefile2
-rw-r--r--lib/orber/doc/src/ch_idl_to_erlang_mapping.xml5
-rw-r--r--lib/orber/doc/src/notes.xml2
-rw-r--r--lib/orber/examples/Stack/Makefile6
-rw-r--r--lib/orber/java_src/Orber/Makefile2
-rw-r--r--lib/orber/priv/Makefile2
-rw-r--r--lib/orber/src/Makefile21
-rw-r--r--lib/orber/src/orber_ifr_exceptiondef.erl20
-rw-r--r--lib/orber/src/orber_iiop_net.erl24
-rw-r--r--lib/orber/test/Makefile2
-rw-r--r--lib/orber/test/csiv2_SUITE.erl104
-rw-r--r--lib/orber/test/orber_test_lib.erl26
-rw-r--r--lib/os_mon/c_src/Makefile.in26
-rw-r--r--lib/os_mon/c_src/cpu_sup.c16
-rw-r--r--lib/os_mon/c_src/memsup.c96
-rw-r--r--lib/os_mon/c_src/win32sysinfo.c32
-rw-r--r--lib/os_mon/doc/src/Makefile2
-rw-r--r--lib/os_mon/doc/src/notes.xml23
-rw-r--r--lib/os_mon/mibs/Makefile10
-rw-r--r--lib/os_mon/src/Makefile6
-rw-r--r--lib/os_mon/src/disksup.erl2
-rw-r--r--lib/os_mon/src/memsup.erl4
-rw-r--r--lib/os_mon/src/os_mon.erl6
-rw-r--r--lib/os_mon/test/Makefile3
-rw-r--r--lib/os_mon/test/os_mon.spec1
-rw-r--r--lib/os_mon/test/os_mon_mib_SUITE.cfg8
-rw-r--r--lib/os_mon/test/os_mon_mib_SUITE.erl147
-rw-r--r--lib/os_mon/vsn.mk2
-rw-r--r--lib/otp_mibs/doc/src/Makefile2
-rw-r--r--lib/otp_mibs/mibs/Makefile4
-rw-r--r--lib/otp_mibs/src/Makefile6
-rw-r--r--lib/parsetools/doc/src/Makefile2
-rw-r--r--lib/parsetools/include/yeccpre.hrl6
-rw-r--r--lib/parsetools/src/Makefile6
-rw-r--r--lib/parsetools/src/esyntax.yrl360
-rw-r--r--lib/parsetools/src/leex.erl99
-rw-r--r--lib/parsetools/src/yecc.erl120
-rw-r--r--lib/parsetools/src/yeccscan.erl6
-rw-r--r--lib/parsetools/test/Makefile2
-rw-r--r--lib/parsetools/test/leex_SUITE.erl116
-rw-r--r--lib/parsetools/test/yecc_SUITE.erl154
-rw-r--r--lib/percept/doc/src/Makefile2
-rw-r--r--lib/percept/doc/src/notes.xml15
-rw-r--r--lib/percept/priv/Makefile2
-rw-r--r--lib/percept/src/Makefile6
-rw-r--r--lib/percept/src/percept.app.src32
-rw-r--r--lib/percept/test/Makefile2
-rw-r--r--lib/percept/vsn.mk2
-rw-r--r--lib/pman/doc/src/Makefile2
-rw-r--r--lib/pman/src/Makefile6
-rw-r--r--lib/public_key/asn1/AuthenticationFramework.asn1367
-rw-r--r--lib/public_key/asn1/InformationFramework.asn1682
-rw-r--r--lib/public_key/asn1/Makefile20
-rw-r--r--lib/public_key/asn1/OTP-PKIX.asn118
-rw-r--r--lib/public_key/asn1/OTP-PUB-KEY.asn1config3
-rw-r--r--lib/public_key/asn1/OTP-PUB-KEY.set.asn2
-rw-r--r--lib/public_key/asn1/PKCS-1.asn13
-rw-r--r--lib/public_key/asn1/PKCS-10.asn170
-rw-r--r--lib/public_key/asn1/PKCS-7.asn1387
-rw-r--r--lib/public_key/asn1/PKIX1Explicit88.asn15
-rw-r--r--lib/public_key/asn1/SelectedAttributeTypes.asn11575
-rw-r--r--lib/public_key/asn1/UsefulDefinitions.asn1234
-rw-r--r--lib/public_key/doc/src/Makefile2
-rw-r--r--lib/public_key/doc/src/cert_records.xml76
-rw-r--r--lib/public_key/doc/src/introduction.xml25
-rw-r--r--lib/public_key/doc/src/notes.xml62
-rw-r--r--lib/public_key/doc/src/part.xml8
-rw-r--r--lib/public_key/doc/src/public_key.xml194
-rw-r--r--lib/public_key/doc/src/public_key_records.xml12
-rw-r--r--lib/public_key/doc/src/using_public_key.xml8
-rw-r--r--lib/public_key/include/public_key.hrl21
-rw-r--r--lib/public_key/src/Makefile9
-rw-r--r--lib/public_key/src/pubkey_cert.erl232
-rw-r--r--lib/public_key/src/pubkey_cert_records.erl17
-rw-r--r--lib/public_key/src/pubkey_crl.erl701
-rw-r--r--lib/public_key/src/pubkey_pem.erl27
-rw-r--r--lib/public_key/src/pubkey_ssh.erl246
-rw-r--r--lib/public_key/src/public_key.app.src1
-rw-r--r--lib/public_key/src/public_key.erl269
-rw-r--r--lib/public_key/test/Makefile2
-rw-r--r--lib/public_key/test/erl_make_certs.erl11
-rw-r--r--lib/public_key/test/pbe_SUITE.erl105
-rw-r--r--lib/public_key/test/pkits_SUITE.erl1351
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/BadCRLIssuerNameCACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/BadCRLSignatureCACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/BadSignedCACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/BadnotAfterDateCACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/BadnotBeforeDateCACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/BasicSelfIssuedCRLSigningKeyCACRL.pem13
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/BasicSelfIssuedCRLSigningKeyCRLCertCRL.pem15
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/BasicSelfIssuedNewKeyCACRL.pem13
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/BasicSelfIssuedOldKeyCACRL.pem13
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/BasicSelfIssuedOldKeySelfIssuedCertCRL.pem15
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/DSACACRL.pem7
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/DSAParametersInheritedCACRL.pem8
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/GeneralizedTimeCRLnextUpdateCACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/GoodCACRL.pem13
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/GoodsubCACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/GoodsubCAPanyPolicyMapping1to2CACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/LongSerialNumberCACRL.pem13
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/Mapping1to2CACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/MappingFromanyPolicyCACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/MappingToanyPolicyCACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/MissingbasicConstraintsCACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/NameOrderCACRL.pem14
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/NegativeSerialNumberCACRL.pem13
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/NoPoliciesCACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/NoissuingDistributionPointCACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/OldCRLnextUpdateCACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/P12Mapping1to3CACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/P12Mapping1to3subCACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/P12Mapping1to3subsubCACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/P1Mapping1to234CACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/P1Mapping1to234subCACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/P1anyPolicyMapping1to2CACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/PanyPolicyMapping1to2CACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/PoliciesP1234CACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/PoliciesP1234subCAP123CRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/PoliciesP1234subsubCAP123P12CRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/PoliciesP123CACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/PoliciesP123subCAP12CRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/PoliciesP123subsubCAP12P1CRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/PoliciesP123subsubCAP2P2CRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/PoliciesP123subsubsubCAP12P2P1CRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/PoliciesP12CACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/PoliciesP12subCAP1CRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/PoliciesP12subsubCAP1P2CRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/PoliciesP2subCA2CRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/PoliciesP2subCACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/PoliciesP3CACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/RFC3280MandatoryAttributeTypesCACRL.pem14
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/RFC3280OptionalAttributeTypesCACRL.pem14
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/RevokedsubCACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/RolloverfromPrintableStringtoUTF8StringCACRL.pem13
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/SeparateCertificateandCRLKeysCA2CRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/SeparateCertificateandCRLKeysCRL.pem13
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/TrustAnchorRootCRL.pem13
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/TwoCRLsCABadCRL.pem13
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/TwoCRLsCAGoodCRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/UIDCACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/UTF8StringCaseInsensitiveMatchCACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/UTF8StringEncodedNamesCACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/UnknownCRLEntryExtensionCACRL.pem13
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/UnknownCRLExtensionCACRL.pem13
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/WrongCRLCACRL.pem13
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/anyPolicyCACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/basicConstraintsCriticalcAFalseCACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/basicConstraintsNotCriticalCACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/basicConstraintsNotCriticalcAFalseCACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/deltaCRLCA1CRL.pem16
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/deltaCRLCA1deltaCRL.pem15
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/deltaCRLCA2CRL.pem15
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/deltaCRLCA2deltaCRL.pem13
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/deltaCRLCA3CRL.pem14
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/deltaCRLCA3deltaCRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/deltaCRLIndicatorNoBaseCACRL.pem13
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/distributionPoint1CACRL.pem16
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/distributionPoint2CACRL.pem14
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/indirectCRLCA1CRL.pem13
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/indirectCRLCA3CRL.pem14
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/indirectCRLCA3cRLIssuerCRL.pem15
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/indirectCRLCA4cRLIssuerCRL.pem15
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/indirectCRLCA5CRL.pem35
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitAnyPolicy0CACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitAnyPolicy1CACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitAnyPolicy1subCA1CRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitAnyPolicy1subCA2CRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitAnyPolicy1subCAIAP5CRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitAnyPolicy1subsubCA2CRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitAnyPolicy5CACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitAnyPolicy5subCACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitAnyPolicy5subsubCACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitPolicyMapping0CACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitPolicyMapping0subCACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitPolicyMapping1P12CACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitPolicyMapping1P12subCACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitPolicyMapping1P12subCAIPM5CRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitPolicyMapping1P12subsubCACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitPolicyMapping1P12subsubCAIPM5CRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitPolicyMapping1P1CACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitPolicyMapping1P1subCACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitPolicyMapping1P1subsubCACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitPolicyMapping5CACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitPolicyMapping5subCACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitPolicyMapping5subsubCACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitPolicyMapping5subsubsubCACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/keyUsageCriticalcRLSignFalseCACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/keyUsageCriticalkeyCertSignFalseCACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/keyUsageNotCriticalCACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/keyUsageNotCriticalcRLSignFalseCACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/keyUsageNotCriticalkeyCertSignFalseCACRL.pem13
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsDN1CACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsDN1subCA1CRL.pem13
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsDN1subCA2CRL.pem13
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsDN1subCA3CRL.pem13
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsDN2CACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsDN3CACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsDN3subCA1CRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsDN3subCA2CRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsDN4CACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsDN5CACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsDNS1CACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsDNS2CACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsRFC822CA1CRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsRFC822CA2CRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsRFC822CA3CRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsURI1CACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsURI2CACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/onlyContainsAttributeCertsCACRL.pem13
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/onlyContainsCACertsCACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/onlyContainsUserCertsCACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/onlySomeReasonsCA1compromiseCRL.pem13
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/onlySomeReasonsCA1otherreasonsCRL.pem13
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/onlySomeReasonsCA2CRL1.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/onlySomeReasonsCA2CRL2.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/onlySomeReasonsCA3compromiseCRL.pem15
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/onlySomeReasonsCA3otherreasonsCRL.pem15
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/onlySomeReasonsCA4compromiseCRL.pem15
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/onlySomeReasonsCA4otherreasonsCRL.pem15
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/pathLenConstraint0CACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/pathLenConstraint0subCA2CRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/pathLenConstraint0subCACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/pathLenConstraint1CACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/pathLenConstraint1subCACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/pathLenConstraint6CACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/pathLenConstraint6subCA0CRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/pathLenConstraint6subCA1CRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/pathLenConstraint6subCA4CRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/pathLenConstraint6subsubCA00CRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/pathLenConstraint6subsubCA11CRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/pathLenConstraint6subsubCA41CRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/pathLenConstraint6subsubsubCA11XCRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/pathLenConstraint6subsubsubCA41XCRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/pre2000CRLnextUpdateCACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy0CACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy0subCACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy0subsubCACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy0subsubsubCACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy10CACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy10subCACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy10subsubCACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy10subsubsubCACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy2CACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy2subCACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy4CACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy4subCACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy4subsubCACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy4subsubsubCACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy5CACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy5subCACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy5subsubCACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy5subsubsubCACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy7CACRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy7subCARE2CRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy7subsubCARE2RE4CRL.pem12
-rw-r--r--lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy7subsubsubCARE2RE4CRL.pem12
-rw-r--r--lib/public_key/test/public_key_SUITE.erl356
-rw-r--r--lib/public_key/test/public_key_SUITE_data/auth_keys4
-rw-r--r--lib/public_key/test/public_key_SUITE_data/known_hosts5
-rw-r--r--lib/public_key/test/public_key_SUITE_data/pkcs7_cert.pem23
-rw-r--r--lib/public_key/test/public_key_SUITE_data/ssh1_auth_keys6
-rw-r--r--lib/public_key/test/public_key_SUITE_data/ssh1_known_hosts1
-rw-r--r--lib/public_key/vsn.mk2
-rw-r--r--lib/reltool/doc/src/Makefile2
-rw-r--r--lib/reltool/doc/src/notes.xml25
-rw-r--r--lib/reltool/doc/src/reltool.xml79
-rw-r--r--lib/reltool/doc/src/reltool_usage.xml11
-rw-r--r--lib/reltool/examples/Makefile2
-rw-r--r--lib/reltool/src/Makefile6
-rw-r--r--lib/reltool/src/reltool.hrl3
-rw-r--r--lib/reltool/src/reltool_server.erl92
-rw-r--r--lib/reltool/src/reltool_sys_win.erl104
-rw-r--r--lib/reltool/src/reltool_target.erl180
-rw-r--r--lib/reltool/test/reltool_server_SUITE.erl288
-rw-r--r--lib/reltool/test/reltool_server_SUITE_data/Makefile.src11
-rw-r--r--lib/reltool/test/reltool_server_SUITE_data/dependencies/y-1.0/ebin/y.app2
-rw-r--r--lib/reltool/test/reltool_server_SUITE_data/dependencies/y-1.0/src/y0.erl5
-rw-r--r--lib/reltool/test/reltool_server_SUITE_data/non_standard_vsn_id/b-first/ebin/b.app6
-rw-r--r--lib/reltool/test/reltool_server_SUITE_data/non_standard_vsn_id/b-first/src/b.erl4
-rw-r--r--lib/reltool/test/reltool_server_SUITE_data/non_standard_vsn_id/b-second/ebin/b.app6
-rw-r--r--lib/reltool/test/reltool_server_SUITE_data/non_standard_vsn_id/b-second/src/b.erl4
-rw-r--r--lib/reltool/test/reltool_server_SUITE_data/slim/a-1.0/ebin/a.app (renamed from lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/ebin/a.app)5
-rw-r--r--lib/reltool/test/reltool_server_SUITE_data/slim/a-1.0/src/a.erl (renamed from lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/src/a.erl)2
-rw-r--r--lib/reltool/test/reltool_server_SUITE_data/slim/a-1.0/src/a_sup.erl (renamed from lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/src/a_sup.erl)0
-rw-r--r--lib/reltool/vsn.mk2
-rw-r--r--lib/runtime_tools/c_src/Makefile.in42
-rw-r--r--lib/runtime_tools/c_src/dtrace_user.d28
-rw-r--r--lib/runtime_tools/c_src/dyntrace.c77
-rw-r--r--lib/runtime_tools/c_src/trace_ip_drv.c31
-rw-r--r--lib/runtime_tools/doc/src/Makefile9
-rw-r--r--lib/runtime_tools/doc/src/book.xml3
-rw-r--r--lib/runtime_tools/doc/src/dbg.xml5
-rw-r--r--lib/runtime_tools/doc/src/dyntrace.xml2
-rw-r--r--lib/runtime_tools/doc/src/notes.xml48
-rw-r--r--lib/runtime_tools/doc/src/part.xml42
-rw-r--r--lib/runtime_tools/examples/user-probe-n.d44
-rw-r--r--lib/runtime_tools/examples/user-probe-n.systemtap53
-rw-r--r--lib/runtime_tools/examples/user-probe.systemtap4
-rw-r--r--lib/runtime_tools/src/Makefile5
-rw-r--r--lib/runtime_tools/src/appmon_info.erl (renamed from lib/appmon/src/appmon_info.erl)0
-rw-r--r--lib/runtime_tools/src/dbg.erl6
-rw-r--r--lib/runtime_tools/src/dyntrace.erl173
-rw-r--r--lib/runtime_tools/src/observer_backend.erl2
-rw-r--r--lib/runtime_tools/src/runtime_tools.app.src7
-rw-r--r--lib/runtime_tools/src/runtime_tools_sup.erl15
-rw-r--r--lib/runtime_tools/test/dbg_SUITE.erl69
-rw-r--r--lib/runtime_tools/test/runtime_tools_SUITE.erl21
-rw-r--r--lib/runtime_tools/vsn.mk2
-rw-r--r--lib/sasl/doc/src/notes.xml2
-rw-r--r--lib/sasl/examples/src/Makefile2
-rw-r--r--lib/sasl/src/Makefile6
-rw-r--r--lib/sasl/src/overload.erl5
-rw-r--r--lib/sasl/src/release_handler.erl8
-rw-r--r--lib/sasl/src/systools_make.erl83
-rw-r--r--lib/sasl/src/systools_rc.erl40
-rw-r--r--lib/sasl/src/systools_relup.erl5
-rw-r--r--lib/sasl/test/Makefile2
-rw-r--r--lib/sasl/test/installer.erl6
-rw-r--r--lib/sasl/test/rb_SUITE.erl27
-rw-r--r--lib/sasl/test/release_handler_SUITE.erl48
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/Makefile.src12
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/c/c.app2
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/ebin/a.app2
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/ebin/a.app2
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/lib/a-1.2/ebin/a.app2
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/lib/b-1.0/ebin/b.app2
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/lib/b-2.0/ebin/b.app2
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/lib/many_mods-1.0/ebin/many_mods.app12
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/lib/many_mods-1.1/ebin/many_mods.app12
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/lib/many_mods-2.0/ebin/many_mods.app2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/ebin/db.app2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/ebin/fe.app2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_bad_appup/lib/fe-2.1/ebin/fe.app2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_bad_appup/lib/fe-3.1/ebin/fe.app2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/ebin/db.app2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/ebin/fe.app2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/ebin/db.app2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/ebin/db.app2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/ebin/fe.app2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-2.1/ebin/fe.app2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-3.1/ebin/fe.app2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-500.18.7/ebin/fe.app2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.0/ebin/db.app2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.1/ebin/db.app2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/ebin/db.app2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/ebin/fe.app2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/ebin/fe.app2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/ebin/fe.app2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_regexp_appup/lib/fe-2.1.1/ebin/fe.app2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_regexp_appup/lib/fe-2.1/ebin/fe.app2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_regexp_appup/lib/fe-3.1/ebin/fe.app2
-rw-r--r--lib/sasl/test/systools_rc_SUITE.erl18
-rw-r--r--lib/snmp/doc/src/Makefile4
-rw-r--r--lib/snmp/doc/src/notes.xml174
-rw-r--r--lib/snmp/doc/src/snmp_config.xml3
-rw-r--r--lib/snmp/doc/src/snmpa_network_interface_filter.xml8
-rw-r--r--lib/snmp/doc/src/snmpm.xml17
-rw-r--r--lib/snmp/doc/src/snmpm_network_interface_filter.xml4
-rw-r--r--lib/snmp/examples/ex1/Makefile2
-rw-r--r--lib/snmp/examples/ex2/Makefile2
-rw-r--r--lib/snmp/mibs/Makefile.in10
-rw-r--r--lib/snmp/priv/conf/agent/Makefile2
-rw-r--r--lib/snmp/priv/conf/manager/Makefile2
-rw-r--r--lib/snmp/src/agent/Makefile2
-rw-r--r--lib/snmp/src/agent/snmp_user_based_sm_mib.erl190
-rw-r--r--lib/snmp/src/agent/snmp_view_based_acm_mib.erl115
-rw-r--r--lib/snmp/src/agent/snmpa_agent.erl6
-rw-r--r--lib/snmp/src/app/Makefile6
-rw-r--r--lib/snmp/src/app/snmp.appup.src525
-rw-r--r--lib/snmp/src/compile/Makefile2
-rw-r--r--lib/snmp/src/compile/depend.mk4
-rw-r--r--lib/snmp/src/manager/Makefile2
-rw-r--r--lib/snmp/src/manager/snmpm.erl624
-rw-r--r--lib/snmp/src/manager/snmpm_conf.erl5
-rw-r--r--lib/snmp/src/manager/snmpm_mpd.erl4
-rw-r--r--lib/snmp/src/manager/snmpm_server.erl37
-rw-r--r--lib/snmp/src/manager/snmpm_user_default.erl6
-rw-r--r--lib/snmp/src/misc/Makefile2
-rw-r--r--lib/snmp/src/misc/snmp_pdus.erl112
-rw-r--r--lib/snmp/test/exp/snmp_agent_bl_test.erl57
-rw-r--r--lib/snmp/test/exp/snmp_agent_ms_test.erl50
-rw-r--r--lib/snmp/test/exp/snmp_agent_mt_test.erl50
-rw-r--r--lib/snmp/test/exp/snmp_agent_v2_test.erl50
-rw-r--r--lib/snmp/test/exp/snmp_agent_v3_test.erl49
-rw-r--r--lib/snmp/test/modules.mk2
-rw-r--r--lib/snmp/test/snmp.spec.vxworks4
-rw-r--r--lib/snmp/test/snmp_agent_test.erl119
-rw-r--r--lib/snmp/test/snmp_agent_test_lib.erl9
-rw-r--r--lib/snmp/test/snmp_log_test.erl12
-rw-r--r--lib/snmp/test/snmp_manager_test.erl217
-rw-r--r--lib/snmp/test/snmp_manager_user.erl318
-rw-r--r--lib/snmp/test/snmp_manager_user_old.erl126
-rw-r--r--lib/snmp/test/snmp_manager_user_test.erl2
-rw-r--r--lib/snmp/test/snmp_pdus_test.erl178
-rw-r--r--lib/snmp/test/snmp_test_lib.erl4
-rw-r--r--lib/snmp/test/snmp_test_manager.erl8
-rw-r--r--lib/snmp/test/snmp_test_mgr.erl1
-rw-r--r--lib/snmp/vsn.mk2
-rw-r--r--lib/ssh/doc/html/SSH_protocols.pngbin0 -> 15381 bytes
-rw-r--r--lib/ssh/doc/man6/.gitignore0
-rw-r--r--lib/ssh/doc/src/Makefile37
-rw-r--r--lib/ssh/doc/src/SSH_protocols.pngbin0 -> 15381 bytes
-rw-r--r--lib/ssh/doc/src/book.xml7
-rw-r--r--lib/ssh/doc/src/fascicules.xml5
-rw-r--r--lib/ssh/doc/src/introduction.xml54
-rw-r--r--lib/ssh/doc/src/notes.xml124
-rw-r--r--lib/ssh/doc/src/ref_man.xml15
-rw-r--r--lib/ssh/doc/src/ssh.xml218
-rw-r--r--lib/ssh/doc/src/ssh_app.xml100
-rw-r--r--lib/ssh/doc/src/ssh_channel.xml349
-rw-r--r--lib/ssh/doc/src/ssh_client_key_api.xml124
-rw-r--r--lib/ssh/doc/src/ssh_connection.xml260
-rw-r--r--lib/ssh/doc/src/ssh_protocol.xml149
-rw-r--r--lib/ssh/doc/src/ssh_server_key_api.xml90
-rw-r--r--lib/ssh/doc/src/ssh_sftp.xml65
-rw-r--r--lib/ssh/doc/src/ssh_sftpd.xml51
-rw-r--r--lib/ssh/doc/src/user_guide.gifbin1581 -> 0 bytes
-rw-r--r--lib/ssh/doc/src/usersguide.xml37
-rw-r--r--lib/ssh/doc/src/using_ssh.xml298
-rw-r--r--lib/ssh/examples/Makefile2
-rw-r--r--lib/ssh/src/Makefile8
-rw-r--r--lib/ssh/src/ssh.app.src6
-rw-r--r--lib/ssh/src/ssh.appup.src16
-rw-r--r--lib/ssh/src/ssh.erl206
-rw-r--r--lib/ssh/src/ssh_auth.erl87
-rw-r--r--lib/ssh/src/ssh_auth.hrl6
-rw-r--r--lib/ssh/src/ssh_channel.erl37
-rw-r--r--lib/ssh/src/ssh_cli.erl3
-rw-r--r--lib/ssh/src/ssh_client_key.erl34
-rw-r--r--lib/ssh/src/ssh_client_key_api.erl35
-rw-r--r--lib/ssh/src/ssh_connection.erl92
-rw-r--r--lib/ssh/src/ssh_connection_handler.erl64
-rw-r--r--lib/ssh/src/ssh_connection_manager.erl174
-rw-r--r--lib/ssh/src/ssh_connection_sup.erl10
-rw-r--r--lib/ssh/src/ssh_file.erl40
-rw-r--r--lib/ssh/src/ssh_io.erl63
-rw-r--r--lib/ssh/src/ssh_key_api.erl45
-rw-r--r--lib/ssh/src/ssh_server_key.erl (renamed from lib/test_server/test/test_server_line_SUITE_data/parse_transform_test.erl)56
-rw-r--r--lib/ssh/src/ssh_server_key_api.erl30
-rw-r--r--lib/ssh/src/ssh_sftpd.erl89
-rw-r--r--lib/ssh/src/ssh_sftpd_file_api.erl62
-rw-r--r--lib/ssh/src/ssh_subsystem.erl47
-rw-r--r--lib/ssh/src/ssh_transport.erl6
-rw-r--r--lib/ssh/src/ssh_xfer.erl5
-rw-r--r--lib/ssh/src/ssh_xfer.hrl12
-rw-r--r--lib/ssh/test/Makefile4
-rw-r--r--lib/ssh/test/ssh.spec.vxworks3
-rw-r--r--lib/ssh/test/ssh_basic_SUITE.erl339
-rw-r--r--lib/ssh/test/ssh_connection_SUITE.erl313
-rw-r--r--lib/ssh/test/ssh_connection_SUITE_data/ssh_host_rsa_key15
-rw-r--r--lib/ssh/test/ssh_echo_server.erl71
-rw-r--r--lib/ssh/test/ssh_sftp_SUITE.erl288
-rw-r--r--lib/ssh/test/ssh_sftpd_SUITE.erl294
-rw-r--r--lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl165
-rw-r--r--lib/ssh/test/ssh_sftpd_erlclient_SUITE_data/ssh_sftpd_file_alt.erl2
-rw-r--r--lib/ssh/test/ssh_test_lib.erl19
-rw-r--r--lib/ssh/test/ssh_to_openssh_SUITE.erl176
-rw-r--r--lib/ssh/vsn.mk2
-rw-r--r--lib/ssl/doc/src/Makefile2
-rw-r--r--lib/ssl/doc/src/notes.xml1396
-rw-r--r--lib/ssl/doc/src/ssl.xml60
-rw-r--r--lib/ssl/doc/src/ssl_app.xml14
-rw-r--r--lib/ssl/doc/src/ssl_protocol.xml7
-rw-r--r--lib/ssl/examples/certs/Makefile2
-rw-r--r--lib/ssl/examples/src/Makefile2
-rw-r--r--lib/ssl/examples/src/client_server.erl24
-rw-r--r--lib/ssl/src/Makefile26
-rw-r--r--lib/ssl/src/ssl.appup.src6
-rw-r--r--lib/ssl/src/ssl.erl251
-rw-r--r--lib/ssl/src/ssl_alert.erl13
-rw-r--r--lib/ssl/src/ssl_alert.hrl4
-rw-r--r--lib/ssl/src/ssl_certificate.erl9
-rw-r--r--lib/ssl/src/ssl_certificate_db.erl14
-rw-r--r--lib/ssl/src/ssl_cipher.erl320
-rw-r--r--lib/ssl/src/ssl_cipher.hrl46
-rw-r--r--lib/ssl/src/ssl_connection.erl868
-rw-r--r--lib/ssl/src/ssl_handshake.erl754
-rw-r--r--lib/ssl/src/ssl_handshake.hrl50
-rw-r--r--lib/ssl/src/ssl_internal.hrl14
-rw-r--r--lib/ssl/src/ssl_manager.erl49
-rw-r--r--lib/ssl/src/ssl_record.erl39
-rw-r--r--lib/ssl/src/ssl_record.hrl13
-rw-r--r--lib/ssl/src/ssl_session.erl18
-rw-r--r--lib/ssl/src/ssl_ssl3.erl54
-rw-r--r--lib/ssl/src/ssl_tls1.erl188
-rw-r--r--lib/ssl/test/Makefile11
-rw-r--r--lib/ssl/test/erl_make_certs.erl4
-rw-r--r--lib/ssl/test/make_certs.erl19
-rw-r--r--lib/ssl/test/ssl_basic_SUITE.erl3118
-rw-r--r--lib/ssl/test/ssl_certificate_verify_SUITE.erl978
-rw-r--r--lib/ssl/test/ssl_cipher_SUITE.erl158
-rw-r--r--lib/ssl/test/ssl_dist_SUITE.erl32
-rw-r--r--lib/ssl/test/ssl_handshake_SUITE.erl11
-rw-r--r--lib/ssl/test/ssl_npn_handshake_SUITE.erl312
-rw-r--r--lib/ssl/test/ssl_npn_hello_SUITE.erl129
-rw-r--r--lib/ssl/test/ssl_packet_SUITE.erl1705
-rw-r--r--lib/ssl/test/ssl_payload_SUITE.erl315
-rw-r--r--lib/ssl/test/ssl_session_cache_SUITE.erl132
-rw-r--r--lib/ssl/test/ssl_test_lib.erl299
-rw-r--r--lib/ssl/test/ssl_to_openssl_SUITE.erl1275
-rw-r--r--lib/ssl/vsn.mk2
-rw-r--r--lib/stdlib/doc/src/Makefile13
-rw-r--r--lib/stdlib/doc/src/epp.xml61
-rw-r--r--lib/stdlib/doc/src/erl_pp.xml8
-rw-r--r--lib/stdlib/doc/src/ets.xml8
-rw-r--r--lib/stdlib/doc/src/filelib.xml9
-rw-r--r--lib/stdlib/doc/src/io.xml233
-rw-r--r--lib/stdlib/doc/src/io_lib.xml69
-rw-r--r--lib/stdlib/doc/src/io_protocol.xml476
-rw-r--r--lib/stdlib/doc/src/notes.xml95
-rw-r--r--lib/stdlib/doc/src/proplists.xml22
-rw-r--r--lib/stdlib/doc/src/re.xml10
-rw-r--r--lib/stdlib/doc/src/supervisor.xml8
-rw-r--r--lib/stdlib/doc/src/unicode.xml71
-rw-r--r--lib/stdlib/doc/src/unicode_usage.xml204
-rw-r--r--lib/stdlib/doc/src/ushell1.gifbin13870 -> 0 bytes
-rw-r--r--lib/stdlib/doc/src/ushell1.ps1196
-rw-r--r--lib/stdlib/doc/src/ushell2.gifbin4384 -> 0 bytes
-rw-r--r--lib/stdlib/doc/src/ushell2.ps404
-rw-r--r--lib/stdlib/doc/src/ushell3.gifbin8239 -> 0 bytes
-rw-r--r--lib/stdlib/doc/src/ushell3.ps662
-rw-r--r--lib/stdlib/doc/src/zip.xml4
-rw-r--r--lib/stdlib/examples/erl_id_trans.erl14
-rw-r--r--lib/stdlib/src/Makefile17
-rw-r--r--lib/stdlib/src/base64.erl4
-rw-r--r--lib/stdlib/src/binary.erl4
-rw-r--r--lib/stdlib/src/c.erl2
-rw-r--r--lib/stdlib/src/dets.erl154
-rw-r--r--lib/stdlib/src/dict.erl5
-rw-r--r--lib/stdlib/src/edlin.erl116
-rw-r--r--lib/stdlib/src/epp.erl215
-rw-r--r--lib/stdlib/src/erl_eval.erl45
-rw-r--r--lib/stdlib/src/erl_expand_records.erl10
-rw-r--r--lib/stdlib/src/erl_internal.erl1
-rw-r--r--lib/stdlib/src/erl_lint.erl366
-rw-r--r--lib/stdlib/src/erl_parse.yrl200
-rw-r--r--lib/stdlib/src/erl_pp.erl757
-rw-r--r--lib/stdlib/src/erl_scan.erl290
-rw-r--r--lib/stdlib/src/escript.erl47
-rw-r--r--lib/stdlib/src/ets.erl4
-rw-r--r--lib/stdlib/src/filelib.erl38
-rw-r--r--lib/stdlib/src/filename.erl26
-rw-r--r--lib/stdlib/src/gb_sets.erl7
-rw-r--r--lib/stdlib/src/gb_trees.erl7
-rw-r--r--lib/stdlib/src/gen_server.erl2
-rw-r--r--lib/stdlib/src/io.erl194
-rw-r--r--lib/stdlib/src/io_lib.erl147
-rw-r--r--lib/stdlib/src/io_lib_format.erl37
-rw-r--r--lib/stdlib/src/io_lib_fread.erl2
-rw-r--r--lib/stdlib/src/io_lib_pretty.erl226
-rw-r--r--lib/stdlib/src/lib.erl173
-rw-r--r--lib/stdlib/src/lists.erl3
-rw-r--r--lib/stdlib/src/log_mf_h.erl4
-rw-r--r--lib/stdlib/src/otp_internal.erl127
-rw-r--r--lib/stdlib/src/proc_lib.erl38
-rw-r--r--lib/stdlib/src/proplists.erl72
-rw-r--r--lib/stdlib/src/qlc.erl4
-rw-r--r--lib/stdlib/src/qlc_pt.erl10
-rw-r--r--lib/stdlib/src/queue.erl14
-rw-r--r--lib/stdlib/src/re.erl6
-rw-r--r--lib/stdlib/src/sets.erl5
-rw-r--r--lib/stdlib/src/shell.erl193
-rw-r--r--lib/stdlib/src/string.erl3
-rw-r--r--lib/stdlib/src/supervisor.erl25
-rw-r--r--lib/stdlib/src/sys.erl99
-rw-r--r--lib/stdlib/src/win32reg.erl4
-rw-r--r--lib/stdlib/src/zip.erl4
-rw-r--r--lib/stdlib/test/Makefile1
-rw-r--r--lib/stdlib/test/base64_SUITE.erl14
-rw-r--r--lib/stdlib/test/binary_module_SUITE.erl9
-rw-r--r--lib/stdlib/test/dets_SUITE.erl72
-rw-r--r--lib/stdlib/test/dict_SUITE.erl20
-rw-r--r--lib/stdlib/test/dict_test_lib.erl55
-rw-r--r--lib/stdlib/test/digraph_SUITE.erl4
-rw-r--r--lib/stdlib/test/epp_SUITE.erl91
-rw-r--r--lib/stdlib/test/erl_eval_SUITE.erl57
-rw-r--r--lib/stdlib/test/erl_expand_records_SUITE.erl49
-rw-r--r--lib/stdlib/test/erl_lint_SUITE.erl78
-rw-r--r--lib/stdlib/test/erl_pp_SUITE.erl133
-rw-r--r--lib/stdlib/test/erl_scan_SUITE.erl216
-rw-r--r--lib/stdlib/test/escript_SUITE.erl189
-rw-r--r--lib/stdlib/test/escript_SUITE_data/archive_script/archive_script_main2.erl19
-rw-r--r--lib/stdlib/test/escript_SUITE_data/archive_script_file_access/archive_script_file_access.erl105
-rw-r--r--lib/stdlib/test/escript_SUITE_data/emulator_flags_no_shebang10
-rwxr-xr-xlib/stdlib/test/escript_SUITE_data/unicode114
-rwxr-xr-xlib/stdlib/test/escript_SUITE_data/unicode214
-rwxr-xr-xlib/stdlib/test/escript_SUITE_data/unicode313
-rw-r--r--lib/stdlib/test/ets_SUITE.erl94
-rw-r--r--lib/stdlib/test/filelib_SUITE.erl57
-rw-r--r--lib/stdlib/test/filename_SUITE.erl263
-rw-r--r--lib/stdlib/test/gen_fsm_SUITE.erl17
-rw-r--r--lib/stdlib/test/gen_server_SUITE.erl35
-rw-r--r--lib/stdlib/test/id_transform_SUITE.erl15
-rw-r--r--lib/stdlib/test/io_SUITE.erl63
-rw-r--r--lib/stdlib/test/io_proto_SUITE.erl165
-rw-r--r--lib/stdlib/test/proc_lib_SUITE.erl31
-rw-r--r--lib/stdlib/test/qlc_SUITE.erl34
-rw-r--r--lib/stdlib/test/re_SUITE.erl27
-rw-r--r--lib/stdlib/test/re_testoutput1_replacement_test.erl55
-rw-r--r--lib/stdlib/test/re_testoutput1_split_test.erl81
-rw-r--r--lib/stdlib/test/sets_SUITE.erl342
-rw-r--r--lib/stdlib/test/sets_test_lib.erl82
-rw-r--r--lib/stdlib/test/shell_SUITE.erl235
-rw-r--r--lib/stdlib/test/stdlib.cover15
-rw-r--r--lib/stdlib/test/stdlib.spec.vxworks8
-rw-r--r--lib/stdlib/test/string_SUITE.erl7
-rw-r--r--lib/stdlib/test/supervisor_SUITE.erl39
-rw-r--r--lib/stdlib/test/supervisor_SUITE_data/Makefile.src15
-rw-r--r--lib/stdlib/test/supervisor_SUITE_data/app_faulty/ebin/app_faulty.app10
-rw-r--r--lib/stdlib/test/supervisor_SUITE_data/app_faulty/src/app_faulty.erl17
-rw-r--r--lib/stdlib/test/supervisor_SUITE_data/app_faulty/src/app_faulty_server.erl32
-rw-r--r--lib/stdlib/test/supervisor_SUITE_data/app_faulty/src/app_faulty_sup.erl17
-rw-r--r--lib/stdlib/test/sys_SUITE.erl88
-rw-r--r--lib/stdlib/test/timer_SUITE.erl8
-rw-r--r--lib/syntax_tools/doc/overview.edoc6
-rw-r--r--lib/syntax_tools/doc/src/Makefile2
-rw-r--r--lib/syntax_tools/doc/src/notes.xml20
-rw-r--r--lib/syntax_tools/src/Makefile6
-rw-r--r--lib/syntax_tools/src/epp_dodger.erl8
-rw-r--r--lib/syntax_tools/src/erl_comment_scan.erl14
-rw-r--r--lib/syntax_tools/src/erl_prettypr.erl49
-rw-r--r--lib/syntax_tools/src/erl_recomment.erl4
-rw-r--r--lib/syntax_tools/src/erl_syntax.erl2122
-rw-r--r--lib/syntax_tools/src/erl_syntax_lib.erl9
-rw-r--r--lib/syntax_tools/src/erl_tidy.erl15
-rw-r--r--lib/syntax_tools/src/igor.erl59
-rw-r--r--lib/syntax_tools/src/prettypr.erl4
-rw-r--r--lib/syntax_tools/vsn.mk2
-rw-r--r--lib/test_server/doc/src/Makefile2
-rw-r--r--lib/test_server/doc/src/notes.xml115
-rw-r--r--lib/test_server/doc/src/test_server.xml14
-rw-r--r--lib/test_server/doc/src/test_server_ctrl.xml101
-rw-r--r--lib/test_server/doc/src/ts.xml35
-rw-r--r--lib/test_server/src/Makefile19
-rw-r--r--lib/test_server/src/configure.in2
-rw-r--r--lib/test_server/src/erl2html2.erl274
-rw-r--r--lib/test_server/src/test_server.app.src1
-rw-r--r--lib/test_server/src/test_server.erl1361
-rw-r--r--lib/test_server/src/test_server_ctrl.erl1449
-rw-r--r--lib/test_server/src/test_server_gl.erl293
-rw-r--r--lib/test_server/src/test_server_h.erl5
-rw-r--r--lib/test_server/src/test_server_internal.hrl4
-rw-r--r--lib/test_server/src/test_server_io.erl319
-rw-r--r--lib/test_server/src/test_server_node.erl305
-rw-r--r--lib/test_server/src/test_server_sup.erl73
-rw-r--r--lib/test_server/src/ts.erl192
-rw-r--r--lib/test_server/src/ts.hrl2
-rw-r--r--lib/test_server/src/ts_autoconf_vxworks.erl191
-rw-r--r--lib/test_server/src/ts_autoconf_win32.erl2
-rw-r--r--lib/test_server/src/ts_benchmark.erl91
-rw-r--r--lib/test_server/src/ts_erl_config.erl14
-rw-r--r--lib/test_server/src/ts_install.erl8
-rw-r--r--lib/test_server/src/ts_install_cth.erl2
-rw-r--r--lib/test_server/src/ts_lib.erl71
-rw-r--r--lib/test_server/src/ts_make.erl2
-rw-r--r--lib/test_server/src/ts_reports.erl545
-rw-r--r--lib/test_server/src/ts_run.erl68
-rw-r--r--lib/test_server/src/ts_selftest.erl120
-rw-r--r--lib/test_server/src/vxworks_client.erl243
-rw-r--r--lib/test_server/test/Makefile7
-rw-r--r--lib/test_server/test/erl2html2_SUITE.erl254
-rw-r--r--lib/test_server/test/erl2html2_SUITE_data/Makefile.src2
-rw-r--r--lib/test_server/test/erl2html2_SUITE_data/header1.hrl4
-rw-r--r--lib/test_server/test/erl2html2_SUITE_data/include/header2.hrl0
-rw-r--r--lib/test_server/test/erl2html2_SUITE_data/m1.erl46
-rw-r--r--lib/test_server/test/test_server.cover21
-rw-r--r--lib/test_server/test/test_server_SUITE.erl141
-rw-r--r--lib/test_server/test/test_server_SUITE_data/Makefile.src5
-rw-r--r--lib/test_server/test/test_server_SUITE_data/test_server_SUITE.erl62
-rw-r--r--lib/test_server/test/test_server_SUITE_data/test_server_break_SUITE.erl148
-rw-r--r--lib/test_server/test/test_server_SUITE_data/test_server_cover_SUITE.erl58
-rw-r--r--lib/test_server/test/test_server_SUITE_data/test_server_cover_SUITE_data/cover_helper.erl4
-rw-r--r--lib/test_server/test/test_server_line_SUITE.erl131
-rw-r--r--lib/test_server/test/test_server_line_SUITE_data/Makefile.src6
-rw-r--r--lib/test_server/test/test_server_test_lib.erl32
-rw-r--r--lib/test_server/vsn.mk2
-rw-r--r--lib/toolbar/doc/src/Makefile2
-rw-r--r--lib/toolbar/doc/src/notes.xml2
-rw-r--r--lib/toolbar/src/Makefile6
-rw-r--r--lib/tools/c_src/Makefile.in10
-rw-r--r--lib/tools/doc/src/Makefile2
-rw-r--r--lib/tools/doc/src/cover.xml20
-rw-r--r--lib/tools/doc/src/eprof.xml2
-rw-r--r--lib/tools/doc/src/notes.xml65
-rw-r--r--lib/tools/doc/src/xref.xml2
-rw-r--r--lib/tools/doc/src/xref_chapter.xml4
-rw-r--r--lib/tools/emacs/Makefile16
-rw-r--r--lib/tools/emacs/erlang-pkg.el3
-rw-r--r--lib/tools/emacs/erlang.el102
-rw-r--r--lib/tools/emacs/test.erl.indented7
-rw-r--r--lib/tools/emacs/test.erl.orig7
-rw-r--r--lib/tools/emacs/vsn.mk3
-rw-r--r--lib/tools/src/Makefile6
-rw-r--r--lib/tools/src/cover.erl268
-rw-r--r--lib/tools/src/lcnt.erl3
-rw-r--r--lib/tools/src/tools.app.src3
-rw-r--r--lib/tools/src/xref_reader.erl9
-rw-r--r--lib/tools/src/xref_utils.erl4
-rw-r--r--lib/tools/test/Makefile2
-rw-r--r--lib/tools/test/cover_SUITE.erl235
-rw-r--r--lib/tools/test/cover_SUITE_data/f.erl11
-rw-r--r--lib/tools/test/xref_SUITE.erl79
-rw-r--r--lib/tv/doc/src/Makefile2
-rw-r--r--lib/tv/doc/src/notes.xml2
-rw-r--r--lib/tv/priv/Makefile2
-rw-r--r--lib/tv/src/Makefile6
-rw-r--r--lib/tv/src/tv_main.hrl1
-rw-r--r--lib/typer/src/Makefile8
-rw-r--r--lib/typer/src/typer.erl4
-rw-r--r--lib/typer/vsn.mk2
-rw-r--r--lib/webtool/src/Makefile6
-rw-r--r--lib/wx/.gitignore5
-rw-r--r--lib/wx/aclocal.m4143
-rw-r--r--lib/wx/api_gen/gen_util.erl12
-rw-r--r--lib/wx/api_gen/gl_gen.erl25
-rw-r--r--lib/wx/api_gen/gl_gen_erl.erl22
-rw-r--r--lib/wx/api_gen/gl_scan_doc.erl14
-rw-r--r--lib/wx/api_gen/wx_extra/wxEvtHandler.erl2
-rw-r--r--lib/wx/api_gen/wx_extra/wxPrintout.erl5
-rw-r--r--lib/wx/api_gen/wx_gen.erl114
-rw-r--r--lib/wx/api_gen/wx_gen.hrl5
-rw-r--r--lib/wx/api_gen/wx_gen_cpp.erl55
-rw-r--r--lib/wx/api_gen/wx_gen_erl.erl128
-rw-r--r--lib/wx/api_gen/wxapi.conf144
-rw-r--r--lib/wx/c_src/Makefile.in5
-rw-r--r--lib/wx/c_src/gen/wxe_derived_dest.h12
-rw-r--r--lib/wx/c_src/gen/wxe_events.cpp425
-rw-r--r--lib/wx/c_src/gen/wxe_funcs.cpp2197
-rw-r--r--lib/wx/c_src/gen/wxe_macros.h299
-rw-r--r--lib/wx/c_src/wxe_impl.cpp36
-rw-r--r--lib/wx/c_src/wxe_ps_init.c5
-rwxr-xr-xlib/wx/configure.in70
-rw-r--r--lib/wx/doc/src/notes.xml2
-rw-r--r--lib/wx/examples/demo/demo.erl53
-rw-r--r--lib/wx/examples/demo/demo_html_tagger.erl1
-rw-r--r--lib/wx/examples/demo/ex_aui.erl22
-rw-r--r--lib/wx/examples/demo/ex_button.erl10
-rw-r--r--lib/wx/examples/demo/ex_canvas.erl5
-rw-r--r--lib/wx/examples/demo/ex_canvas_paint.erl25
-rw-r--r--lib/wx/examples/demo/ex_choices.erl6
-rw-r--r--lib/wx/examples/demo/ex_cursor.erl4
-rw-r--r--lib/wx/examples/demo/ex_dialogs.erl4
-rw-r--r--lib/wx/examples/demo/ex_frame_utils.erl4
-rw-r--r--lib/wx/examples/demo/ex_gauge.erl4
-rw-r--r--lib/wx/examples/demo/ex_gl.erl61
-rw-r--r--lib/wx/examples/demo/ex_graphicsContext.erl42
-rw-r--r--lib/wx/examples/demo/ex_grid.erl4
-rw-r--r--lib/wx/examples/demo/ex_htmlWindow.erl4
-rw-r--r--lib/wx/examples/demo/ex_listCtrl.erl8
-rw-r--r--lib/wx/examples/demo/ex_notebook.erl4
-rw-r--r--lib/wx/examples/demo/ex_pickers.erl4
-rw-r--r--lib/wx/examples/demo/ex_popupMenu.erl4
-rw-r--r--lib/wx/examples/demo/ex_radioBox.erl4
-rw-r--r--lib/wx/examples/demo/ex_sashWindow.erl4
-rw-r--r--lib/wx/examples/demo/ex_sizers.erl4
-rw-r--r--lib/wx/examples/demo/ex_slider.erl4
-rw-r--r--lib/wx/examples/demo/ex_splitterWindow.erl4
-rw-r--r--lib/wx/examples/demo/ex_static.erl4
-rw-r--r--lib/wx/examples/demo/ex_textCtrl.erl24
-rw-r--r--lib/wx/examples/demo/ex_treeCtrl.erl4
-rw-r--r--lib/wx/examples/simple/menu.erl4
-rw-r--r--lib/wx/examples/simple/minimal.erl6
-rw-r--r--lib/wx/examples/sudoku/sudoku_board.erl46
-rw-r--r--lib/wx/examples/sudoku/sudoku_gui.erl25
-rw-r--r--lib/wx/include/wx.hrl215
-rw-r--r--lib/wx/src/Makefile12
-rw-r--r--lib/wx/src/gen/gl.erl285
-rw-r--r--lib/wx/src/gen/glu.erl82
-rw-r--r--lib/wx/src/gen/wxCalendarCtrl.erl2
-rw-r--r--lib/wx/src/gen/wxClientDC.erl2
-rw-r--r--lib/wx/src/gen/wxClipboard.erl3
-rw-r--r--lib/wx/src/gen/wxCursor.erl2
-rw-r--r--lib/wx/src/gen/wxDC.erl17
-rw-r--r--lib/wx/src/gen/wxEvtHandler.erl2
-rw-r--r--lib/wx/src/gen/wxGraphicsContext.erl11
-rw-r--r--lib/wx/src/gen/wxGraphicsPath.erl8
-rw-r--r--lib/wx/src/gen/wxGraphicsRenderer.erl2
-rw-r--r--lib/wx/src/gen/wxGridCellEditor.erl2
-rw-r--r--lib/wx/src/gen/wxIdleEvent.erl2
-rw-r--r--lib/wx/src/gen/wxImage.erl9
-rw-r--r--lib/wx/src/gen/wxMDIClientWindow.erl2
-rw-r--r--lib/wx/src/gen/wxMouseEvent.erl2
-rw-r--r--lib/wx/src/gen/wxPageSetupDialogData.erl9
-rw-r--r--lib/wx/src/gen/wxPaintDC.erl2
-rw-r--r--lib/wx/src/gen/wxPaintEvent.erl2
-rw-r--r--lib/wx/src/gen/wxPen.erl6
-rw-r--r--lib/wx/src/gen/wxPostScriptDC.erl2
-rw-r--r--lib/wx/src/gen/wxPrintData.erl6
-rw-r--r--lib/wx/src/gen/wxPrintout.erl5
-rw-r--r--lib/wx/src/gen/wxStyledTextCtrl.erl47
-rw-r--r--lib/wx/src/gen/wxSystemSettings.erl2
-rw-r--r--lib/wx/src/gen/wxWindowDC.erl2
-rw-r--r--lib/wx/src/gen/wxe_debug.hrl299
-rw-r--r--lib/wx/src/gen/wxe_funcs.hrl299
-rw-r--r--lib/wx/src/wx.erl36
-rw-r--r--lib/wx/src/wxe_master.erl28
-rw-r--r--lib/wx/src/wxe_server.erl14
-rw-r--r--lib/wx/src/wxe_util.erl26
-rw-r--r--lib/wx/test/wx_basic_SUITE.erl32
-rw-r--r--lib/wx/test/wx_class_SUITE.erl17
-rw-r--r--lib/wx/test/wx_event_SUITE.erl79
-rw-r--r--lib/wx/test/wx_opengl_SUITE.erl40
-rw-r--r--lib/wx/test/wx_xtra_SUITE.erl32
-rw-r--r--lib/wx/wxwin-2.8.m4 (renamed from lib/wx/wxwin.m4)0
-rw-r--r--lib/wx/wxwin-2.9.m41060
-rw-r--r--lib/xmerl/doc/src/Makefile2
-rw-r--r--lib/xmerl/doc/src/motorcycles_dtd.txt3
-rw-r--r--lib/xmerl/doc/src/notes.xml2
-rw-r--r--lib/xmerl/src/Makefile20
-rw-r--r--lib/xmerl/src/xmerl_sax_parser_base.erlsrc2
-rw-r--r--lib/xmerl/src/xmerl_scan.erl5
-rw-r--r--lib/xmerl/src/xmerl_xsd.erl3
-rw-r--r--lib/xmerl/test/Makefile2
-rw-r--r--lib/xmerl/test/xmerl_SUITE.erl7
-rw-r--r--make/emd2exml.in23
-rwxr-xr-xmake/fakefop118
-rw-r--r--make/otp.mk.in69
-rw-r--r--make/otp_ded.mk.in2
-rw-r--r--make/otp_release_targets.mk39
-rw-r--r--make/output.mk.in112
-rw-r--r--make/run_make.mk7
-rwxr-xr-xotp_build4
-rw-r--r--system/README7
-rw-r--r--system/doc/design_principles/Makefile2
-rw-r--r--system/doc/efficiency_guide/Makefile2
-rw-r--r--system/doc/efficiency_guide/advanced.xml24
-rw-r--r--system/doc/efficiency_guide/drivers.xml14
-rw-r--r--system/doc/embedded/Makefile2
-rw-r--r--system/doc/embedded/part.xml1
-rw-r--r--system/doc/embedded/vxworks.xml193
-rw-r--r--system/doc/embedded/xmlfiles.mk3
-rw-r--r--system/doc/getting_started/Makefile2
-rw-r--r--system/doc/installation_guide/Makefile10
-rw-r--r--system/doc/installation_guide/part.xml4
-rw-r--r--system/doc/installation_guide/xmlfiles.mk3
-rw-r--r--system/doc/oam/Makefile2
-rw-r--r--system/doc/programming_examples/Makefile2
-rw-r--r--system/doc/reference_manual/Makefile2
-rw-r--r--system/doc/reference_manual/expressions.xml11
-rw-r--r--system/doc/reference_manual/introduction.xml13
-rw-r--r--system/doc/reference_manual/ports.xml13
-rw-r--r--system/doc/system_architecture_intro/Makefile2
-rw-r--r--system/doc/system_principles/Makefile2
-rw-r--r--system/doc/top/Makefile31
-rw-r--r--system/doc/top/templates/index.html.src15
-rw-r--r--system/doc/tutorial/Makefile2
-rw-r--r--system/doc/tutorial/port_driver.c28
-rw-r--r--xcomp/README.md6
-rw-r--r--xcomp/erl-xcomp-TileraMDE2.0-tilepro.conf11
-rw-r--r--xcomp/erl-xcomp-avr32-atmel-linux-gnu.conf9
-rw-r--r--xcomp/erl-xcomp-mips-linux.conf11
-rw-r--r--xcomp/erl-xcomp-powerpc-dso-linux-gnu.conf258
-rw-r--r--xcomp/erl-xcomp-vars.sh4
-rw-r--r--xcomp/erl-xcomp-vxworks_ppc32.conf11
-rw-r--r--xcomp/erl-xcomp-x86_64-saf-linux-gnu.conf11
-rw-r--r--xcomp/erl-xcomp.conf.template11
2245 files changed, 110172 insertions, 83893 deletions
diff --git a/.gitignore b/.gitignore
index 0986bb6e4c..e5e74f9a3d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -86,6 +86,8 @@ lib/os_mon/priv/obj/win32/
lib/runtime_tools/c_src/win32/
lib/runtime_tools/priv/lib/
lib/runtime_tools/priv/obj/
+lib/runtime_tools/doc/src/DTRACE.xml
+lib/runtime_tools/doc/src/SYSTEMTAP.xml
lib/tools/bin/win32/
lib/tools/c_src/win32/
lib/tools/obj/win32/
@@ -127,6 +129,7 @@ JAVADOC-GENERATED
/release
+/make/output.mk
/make/emd2exml
# Created by "out_build update_primary"
@@ -240,6 +243,8 @@ JAVADOC-GENERATED
# hipe
+/lib/hipe/boot_ebin/hipe.app
+/lib/hipe/boot_ebin/hipe.appup
/lib/hipe/main/hipe.hrl
/lib/hipe/rtl/hipe_literals.hrl
@@ -335,6 +340,7 @@ JAVADOC-GENERATED
/system/doc/installation_guide/INSTALL.xml
/system/doc/installation_guide/INSTALL-CROSS.xml
/system/doc/installation_guide/INSTALL-WIN32.xml
+/system/doc/installation_guide/MARKDOWN.xml
# test_server
diff --git a/HOWTO/BENCHMARKS.md b/HOWTO/BENCHMARKS.md
new file mode 100644
index 0000000000..28a590dfd2
--- /dev/null
+++ b/HOWTO/BENCHMARKS.md
@@ -0,0 +1,73 @@
+Benchmarking Erlang/OTP
+=======================
+
+The Erlang/OTP source tree contains a number of benchmarks. The same framework
+is used to run these benchmarks as is used to run tests. Therefore in order to
+run benchmarks you have to [release the tests][] just as you normally would.
+
+Note that many of these benchmarks were developed to test a specific feature
+under a specific setting. We strive to keep the benchmarks up-to-date, but alas
+time is not an endless resource so some benchmarks will be outdated and
+irrelevant.
+
+Running the benchmarks
+----------------------
+
+As with testing, `ts` is used to run the benchmarks. Before running any
+benchmarks you have to [install the tests][]. To get a listing of all
+benchmarks you have available call `ts:benchmarks()`.
+
+To run all benchmarks call `ts:bench()`. This will run all benchmarks using
+the emulator which is in your `$PATH` (Note that this does not have to be the
+same as from which the benchmarks were built from). All the results of the
+benchmarks are put in a folder in `$TESTROOT/test_server/` called
+`YYYY_MO_DDTHH_MI_SS`.
+
+Each benchmark is run multiple times and the data for all runs is collected in
+the files within the benchmark folder. All benchmarks are written so that
+higher values are better.
+
+Writing benchmarks
+------------------
+
+Benchmarks are just normal testcases in Common Test suites. They are marked as
+benchmarks by being included in the `AppName_bench.spec` which is located in
+`lib/AppName/test/` for the applications which have benchmarks. Note that you
+might want to add a skip clause to `AppName.spec` for the benchmarks if you do
+not want them to be run in the nightly tests.
+
+Results of benchmarks are sent using the ct_event mechanism and automatically
+collected and formatted by ts.
+
+ ct_event:notify(
+ #event{name = benchmark_data,
+ data = [{value,TPS}]}).
+
+The application, suite and testcase associated with the value is automatically
+detected. If you want to supply your own you can include `suite` andor `name`
+with the data. i.e.
+
+ ct_event:notify(
+ #event{name = benchmark_data,
+ data = [{suite,"erts_bench"},
+ {name,"ets_transactions_per_sec"},
+ {value,TPS}]}).
+
+The reason for using the internal ct_event and not ct is because the benchmark
+code has to be backwards compatible with at least R14.
+
+The value which is reported should be as raw as possible. i.e. you should not
+do any averaging of the value before reporting. The tools we use to collect the
+benchmark data over time will do averages, means, stddev and more with the data.
+So the more data which is sent using `ct_event` the better.
+
+Viewing benchmarks
+------------------
+
+At the moment of writing this HOWTO the tool for viewing benchmark results is
+not available as opensource. This will hopefully change in the near future.
+
+
+ [release the tests]: TESTING.md#releasing-tests
+ [install the tests]: TESTING.md#configuring-the-test-environment
+
diff --git a/README.bootstrap b/HOWTO/BOOTSTRAP.md
index f42bc7aa47..59e31165cf 100644
--- a/README.bootstrap
+++ b/HOWTO/BOOTSTRAP.md
@@ -1,16 +1,16 @@
Notes about prebuilt beam files under version control
------------------------------------------------------
+=====================================================
This information applies mostly to developers, some parts only
to developers of the main branch i.e. Ericsson and HiPE personel.
There are two types of derived code under version control, namely:
-primary bootstrap - Resides in the $ERL_TOP/bootstrap/{lib,bin} directories.
-preloaded code - Resides in the $ERL_TOP/erts/preloaded directory.
+primary bootstrap - Resides in the `$ERL_TOP/bootstrap/{lib,bin}` directories.
+preloaded code - Resides in the `$ERL_TOP/erts/preloaded` directory.
Primary bootstrap
-.................
+-----------------
The two types of version controlled code are fundamentally
different. The primary bootstrap is code compiled from source files in
@@ -19,7 +19,7 @@ lib/orber/include. They are checked in in the version control system
to make it possible to build directly from the code base tree without
the need for an earlier version of the compiler. When a new version of
OTP is released, these files are updated manually (or rather, by using
-the $ERL_TOP/otp_build script) and checked in. The files can also be
+the `$ERL_TOP/otp_build` script) and checked in. The files can also be
updated due to changes in the compiler during the development
process. The primary bootstrap is always updated as a separate
deliberate process, never during a normal development build.
@@ -27,31 +27,31 @@ deliberate process, never during a normal development build.
If a prebuilt open source version of erlang is used, the directory
bootstrap initially does not contain any beam files, the directory is
instead populated by copying beam files from the
-$ERL_TOP/lib/{kernel,stdlib,compiler}/ebin directories. This
+`$ERL_TOP/lib/{kernel,stdlib,compiler}/ebin` directories. This
construction is to save space in the distribution, but the result
would be the same. Open source developers need not provide patches for
the precompiled beam files in the primary bootstrap, the bootstrap
update is always performed by the main developers.
Preloaded code
-..............
+--------------
-The directory $ERL_TOP/preloaded contains both src and ebin
+The directory `$ERL_TOP/preloaded` contains both src and ebin
subdirectories. The preloaded code is compiled into the virtual
machine and always present. When compiling the virtual machine, those
beam files need to be present and they are considered a part of the
virtual machine rather than a part of the kernel application. When
preloaded files are to be updated, the source code is built using a
-special Makefile in the $ERL_TOP/preloaded/src directory, which
+special Makefile in the `$ERL_TOP/preloaded/src` directory, which
creates beam files in the same directory. When they seem to compile
successfully, they can be used in an emulator build by being copied
-to the ebin directory. otp_build update_preloaded can be used to
+to the ebin directory. `otp_build update_preloaded` can be used to
ease the process (there are also similar targets in the
-$ERL_TOP/preloaded/src/Makefile).
+`$ERL_TOP/preloaded/src/Makefile`).
In prebuilt open source distributions, these beam files are also
present, but to update them one might need to change permission on the
-$ERL_TOP/preloaded/ebin directory, then build and then manually copy
+`$ERL_TOP/preloaded/ebin` directory, then build and then manually copy
the beam files from the source directory to ../ebin. If patches are
created that involve the source files used to build preloaded code,
always note this specially as the preloaded/ebin directory needs
diff --git a/README.dtrace.md b/HOWTO/DTRACE.md
index 5bc042f9fc..8fa2fd9d50 100644
--- a/README.dtrace.md
+++ b/HOWTO/DTRACE.md
@@ -5,7 +5,7 @@ History
-------
The first implementation of DTrace probes for the Erlang virtual
-machine was presented at the [2008 Erlang User Conference] [4]. That
+machine was presented at the [2008 Erlang User Conference] [1]. That
work, based on the Erlang/OTP R12 release, was discontinued due to
what appears to be miscommunication with the original developers.
@@ -16,13 +16,13 @@ e.g. `foo_module:dtrace_probe("message goes here!")`.
Goals
-----
-1. Annotate as much of the Erlang VM as is practical.
+* Annotate as much of the Erlang VM as is practical.
* The initial goal is to trace file I/O operations.
-2. Support all platforms that implement DTrace: OS X, Solaris,
- and (I hope) FreeBSD and NetBSD.
-3. To the extent that it's practical, support SystemTap on Linux
- via DTrace provider compatibility.
-4. Allow Erlang code to supply annotations.
+* Support all platforms that implement DTrace: OS X, Solaris,
+ and (I hope) FreeBSD and NetBSD.
+* To the extent that it's practical, support SystemTap on Linux
+ via DTrace provider compatibility.
+* Allow Erlang code to supply annotations.
Supported platforms
-------------------
@@ -33,8 +33,8 @@ Supported platforms
OpenIndiana release 151a, and both appear to work.
* FreeBSD 9.0, though please see the "FreeBSD 9.0 Release Notes"
section below!
-* Linux via SystemTap compatibility. Please see the file
- `README.systemtap.md` for more details.
+* Linux via SystemTap compatibility. Please see
+ [$ERL_TOP/HOWTO/SYSTEMTAP.md][] for more details.
Just add the `--with-dynamic-trace=dtrace` option to your command when you
run the `configure` script. If you are using systemtap, the configure option
@@ -43,10 +43,10 @@ is `--with-dynamic-trace=systemtap`
Status
------
-As of R15B01, the dynamic trace code is included in the main OTP distribution,
+As of R15B01, the dynamic trace code is included in the OTP source distribution,
although it's considered experimental. The main development of the dtrace code
still happens outside of Ericsson, but there is no need to fetch a patched
-version of OTP to get the basic funtionality.
+version of the OTP source to get the basic funtionality.
Implementation summary
----------------------
@@ -66,12 +66,11 @@ following may be executed in a different Pthread:
* `efile_drv` command execution (C code)
* `efile_drv` status return (C code)
-**TODO: keep this description up-to-date.**
-
-Example output from `lib/dtrace/examples/efile_drv.d` while executing
+Example output from `lib/runtime_tools/examples/efile_drv.d` while executing
`file:rename("old-name", "new-name")`:
- efile_drv enter tag={3,84} user tag some-user-tag | RENAME (12) | args: old-name new-name , 0 0 (port #Port<0.59>)
+ efile_drv enter tag={3,84} user tag some-user-tag | RENAME (12) | args: old-name new-name ,\
+ 0 0 (port #Port<0.59>)
async I/O worker tag={3,83} | RENAME (12) | efile_drv-int_entry
async I/O worker tag={3,83} | RENAME (12) | efile_drv-int_return
efile_drv return tag={3,83} user tag | RENAME (12) | errno 2
@@ -83,7 +82,7 @@ Example output from `lib/dtrace/examples/efile_drv.d` while executing
these two numbers form a unique ID for the I/O operation.
* `12` is the command number for the rename operation. See the
definition for `FILE_RENAME` in the source code file `efile_drv.c`
- or the `BEGIN` section of the D script `lib/dtrace/examples/efile_drv.d`.
+ or the `BEGIN` section of the D script `lib/runtime_tools/examples/efile_drv.d`.
* `old-name` and `new-name` are the two string arguments for the
source and destination of the `rename(2)` system call.
The two integer arguments are unused; the simple formatting code
@@ -101,8 +100,8 @@ So, where does the `some-user-tag` string come from?
At the moment, the user tag comes from code like the following:
- put(dtrace_utag, "some-user-tag"),
- file:rename("old-name", "new-name").
+ dyntrace:put_tag("some-user-tag"),
+ file:rename("old-name", "new-name"),
This method of tagging I/O at the Erlang level is subject to change.
@@ -391,3 +390,4 @@ Guide to efile_drv.c probe arguments
probe arg9 = C driver dt_i4 = advise_type;
[1]: http://www.erlang.org/euc/08/
+ [$ERL_TOP/HOWTO/SYSTEMTAP.md]: SYSTEMTAP.md
diff --git a/HOWTO/INSTALL-CROSS.md b/HOWTO/INSTALL-CROSS.md
new file mode 100644
index 0000000000..8a2b1bc8ad
--- /dev/null
+++ b/HOWTO/INSTALL-CROSS.md
@@ -0,0 +1,560 @@
+Cross Compiling Erlang/OTP
+==========================
+
+Introduction
+------------
+
+This document describes how to cross compile Erlang/OTP-%OTP-REL%. Note that
+the support for cross compiling Erlang/OTP should be considered as
+experimental. As far as we know, the %OTP-REL% release should cross compile
+fine, but since we currently have a very limited set of cross compilation
+environments to test with we cannot be sure. The cross compilation support
+will remain in an experimental state until we get a lot more cross compilation
+environments to test with.
+
+You are advised to read the whole document before attempting to cross
+compile Erlang/OTP. However, before reading this document, you should read
+the [$ERL_TOP/HOWTO/INSTALL.md][] document which describes building and installing
+Erlang/OTP in general. `$ERL_TOP` is the top directory in the source tree.
+
+### otp\_build Versus configure/make ###
+
+Building Erlang/OTP can be done either by using the `$ERL_TOP/otp_build`
+script, or by invoking `$ERL_TOP/configure` and `make` directly. Building using
+`otp_build` is easier since it involves fewer steps, but the `otp_build` build
+procedure is not as flexible as the `configure`/`make` build procedure. Note
+that `otp_build configure` will produce a default configuration that differs
+from what `configure` will produce by default. For example, currently
+`--disable-dynamic-ssl-lib` is added to the `configure` command line arguments
+unless `--enable-dynamic-ssl-lib` has been explicitly passed. The binary
+releases that we deliver are built using `otp_build`. The defaults used by
+`otp_build configure` may change at any time without prior notice.
+
+### Cross Configuration ###
+
+The `$ERL_TOP/xcomp/erl-xcomp.conf.template` file contains all available cross
+configuration variables and can be used as a template when creating a cross
+compilation configuration. All [cross configuration variables][] are also
+listed at the end of this document. For examples of working cross
+configurations see the `$ERL_TOP/xcomp/erl-xcomp-TileraMDE2.0-tilepro.conf`
+file and the `$ERL_TOP/xcomp/erl-xcomp-x86_64-saf-linux-gnu.conf` file. If the
+default behavior of a variable is satisfactory, the variable does not need to
+be set. However, the `configure` script will issue a warning when a default
+value is used. When a variable has been set, no warning will be issued.
+
+A cross configuration file can be passed to `otp_build configure` using the
+`--xcomp-conf` command line argument. Note that `configure` does not accept
+this command line argument. When using the `configure` script directly, pass
+the configuration variables as arguments to `configure` using a
+`<VARIABLE>=<VALUE>` syntax. Variables can also be passed as environment
+variables to `configure`. However, if you pass the configuration in the
+environment, make sure to unset all of these environment variables before
+invoking `make`; otherwise, the environment variables might set make variables
+in some applications, or parts of some applications, and you may end up with
+an erroneously configured build.
+
+### What can be Cross Compiled? ###
+
+All Erlang/OTP applications except the `wx` application can be cross compiled.
+The build of the `wx` driver will currently be automatically disabled when
+cross compiling.
+
+### Compatibility ###
+
+The build system, including cross compilation configuration variables used,
+may be subject to non backward compatible changes without prior notice.
+Current cross build system has been tested when cross compiling some Linux/GNU
+systems, but has only been partly tested for more esoteric platforms. The
+VxWorks example file is highly dependent on our environment and is here more
+or less only for internal use.
+
+### Patches ###
+
+Please submit any patches for cross compiling in a way consistent with this
+system. All input is welcome as we have a very limited set of cross compiling
+environments to test with. If a new configuration variable is needed, add it
+to `$ERL_TOP/xcomp/erl-xcomp.conf.template`, and use it in `configure.in`.
+Other files that might need to be updated are:
+
+- `$ERL_TOP/xcomp/erl-xcomp-vars.sh`
+- `$ERL_TOP/erl-build-tool-vars.sh`
+- `$ERL_TOP/erts/aclocal.m4`
+- `$ERL_TOP/xcomp/README.md`
+- `$ERL_TOP/xcomp/erl-xcomp-*.conf`
+
+Note that this might be an incomplete list of files that need to be updated.
+
+General information on how to submit patches can be found at:
+ <http://wiki.github.com/erlang/otp/submitting-patches>
+
+Build and Install Procedure
+---------------------------
+
+If you are building in Git, you want to read the [Building in Git][] section
+of [$ERL_TOP/HOWTO/INSTALL.md][] before proceeding.
+
+We will first go through the `configure`/`make` build procedure which people
+probably are most familiar with.
+
+### Building With configure/make Directly ###
+
+ (1)
+
+Change directory into the top directory of the Erlang/OTP source tree.
+
+ $ cd $ERL_TOP
+
+In order to compile Erlang code, a small Erlang bootstrap system has to be
+built, or an Erlang/OTP system of the same release as the one being built
+has to be provided in the `$PATH`. The Erlang/OTP for the target system will
+be built using this Erlang system, together with the cross compilation tools
+provided.
+
+If you want to build the documentation out of the same source tree as you are
+cross compiling in, you currently need a full Erlang/OTP system of the same
+release as the one being built for the build machine. If this is the case,
+build and install one for the build machine (or use one already built) and add
+it to the `$PATH` before cross building, and building the documentation. See
+the [How to Build the Documentation][] section in the [$ERL_TOP/HOWTO/INSTALL.md][]
+document for information on how to build the documentation.
+
+If you want to build using a compatible Erlang/OTP system in the `$PATH`,
+jump to (3).
+
+#### Building a Bootstrap System ####
+
+ (2)
+
+ $ ./configure --enable-bootstrap-only
+ $ make
+
+The `--enable-bootstrap-only` argument to `configure` isn't strictly necessary,
+but will speed things up. It will only run `configure` in applications
+necessary for the bootstrap, and will disable a lot of things not needed by
+the bootstrap system. If you run `configure` without `--enable-boostrap-only`
+you also have to run make as `make bootstrap`; otherwise, the whole system will
+be built.
+
+#### Cross Building the System ####
+
+ (3)
+
+ $ ./configure --host=<HOST> --build=<BUILD> [Other Config Args]
+ $ make
+
+`<HOST>` is the host/target system that you build for. It does not have to be
+a full `CPU-VENDOR-OS` triplet, but can be. The full `CPU-VENDOR-OS` triplet
+will be created by executing `$ERL_TOP/erts/autoconf/config.sub <HOST>`. If
+`config.sub` fails, you need to be more specific.
+
+`<BUILD>` should equal the `CPU-VENDOR-OS` triplet of the system that you
+build on. If you execute `$ERL_TOP/erts/autoconf/config.guess`, it will in
+most cases print the triplet you want to use for this.
+
+Pass the cross compilation variables as command line arguments to `configure`
+using a `<VARIABLE>=<VALUE>` syntax.
+
+> *NOTE*: You can *not* pass a configuration file using the `--xcomp-conf`
+> argument when you invoke `configure` directly. The `--xcomp-conf` argument
+> can only be passed to `otp_build configure`.
+
+`make` will verify that the Erlang/OTP system used when building is of the
+same release as the system being built, and will fail if this is not the case.
+It is possible, however not recommended, to force the cross compilation even
+though the wrong Erlang/OTP system is used. This by invoking `make` like this:
+`make ERL_XCOMP_FORCE_DIFFERENT_OTP=yes`.
+
+> *WARNING*: Invoking `make ERL_XCOMP_FORCE_DIFFERENT_OTP=yes` might fail,
+> silently produce suboptimal code, or silently produce erroneous code.
+
+#### Installing ####
+
+You can either install using the installation paths determined by `configure`
+(4), or install manually using (5).
+
+##### Installing Using Paths Determined by configure #####
+
+ (4)
+
+ $ make install DESTDIR=<TEMPORARY_PREFIX>
+
+`make install` will install at a location specified when doing `configure`.
+`configure` arguments specifying where the installation should reside are for
+example: `--prefix`, `--exec-prefix`, `--libdir`, `--bindir`, etc. By default
+it will install under `/usr/local`. You typically do not want to install your
+cross build under `/usr/local` on your build machine. Using [DESTDIR][]
+will cause the installation paths to be prefixed by `$DESTDIR`. This makes it
+possible to install and package the installation on the build machine without
+having to place the installation in the same directory on the build machine as
+it should be executed from on the target machine.
+
+When `make install` has finished, change directory into `$DESTDIR`, package
+the system, move it to the target machine, and unpack it. Note that the
+installation will only be working on the target machine at the location
+determined by `configure`.
+
+##### Installing Manually #####
+
+ (5)
+
+ $ make release RELEASE_ROOT=<RELEASE_DIR>
+
+`make release` will copy what you have built for the target machine to
+`<RELEASE_DIR>`. The `Install` script will not be run. The content of
+`<RELEASE_DIR>` is what by default ends up in `/usr/local/lib/erlang`.
+
+The `Install` script used when installing Erlang/OTP requires common Unix
+tools such as `sed` to be present in your `$PATH`. If your target system
+does not have such tools, you need to run the `Install` script on your
+build machine before packaging Erlang/OTP. The `Install` script should
+currently be invoked as follows in the directory where it resides
+(the top directory):
+
+ $ ./Install [-cross] [-minimal|-sasl] <ERL_ROOT>
+
+where:
+
+* `-minimal` Creates an installation that starts up a minimal amount
+ of applications, i.e., only `kernel` and `stdlib` are started. The
+ minimal system is normally enough, and is what `make install` uses.
+* `-sasl` Creates an installation that also starts up the `sasl`
+ application.
+* `-cross` For cross compilation. Informs the install script that it
+ is run on the build machine.
+* `<ERL_ROOT>` - The absolute path to the Erlang installation to use
+ at run time. This is often the same as the current working directory,
+ but does not have to be. It can follow any other path through the file
+ system to the same directory.
+
+If neither `-minimal`, nor `-sasl` is passed as argument you will be
+prompted.
+
+You can now either do:
+
+ (6)
+
+* Decide where the installation should be located on the target machine,
+ run the `Install` script on the build machine, and package the installed
+ installation. The installation just need to be unpacked at the right
+ location on the target machine:
+
+ $ cd <RELEASE_DIR>
+ $ ./Install -cross [-minimal|-sasl] <ABSOLUTE_INSTALL_DIR_ON_TARGET>
+
+or:
+
+ (7)
+
+* Package the installation in `<RELEASE_DIR>`, place it wherever you want
+ on your target machine, and run the `Install` script on your target
+ machine:
+
+ $ cd <ABSOLUTE_INSTALL_DIR_ON_TARGET>
+ $ ./Install [-minimal|-sasl] <ABSOLUTE_INSTALL_DIR_ON_TARGET>
+
+### Building With the otp\_build Script ###
+
+ (8)
+
+ $ cd $ERL_TOP
+
+ (9)
+
+ $ ./otp_build configure --xcomp-conf=<FILE> [Other Config Args]
+
+alternatively:
+
+ $ ./otp_build configure --host=<HOST> --build=<BUILD> [Other Config Args]
+
+If you have your cross compilation configuration in a file, pass it using the
+`--xcomp-conf=<FILE>` command line argument. If not, pass `--host=<HOST>`,
+`--build=<BUILD>`, and the configuration variables using a `<VARIABLE>=<VALUE>`
+syntax on the command line (same as in (3)). Note that `<HOST>` and `<BUILD>`
+have to be passed one way or the other; either by using `erl_xcomp_host=<HOST>`
+and `erl_xcomp_build=<BUILD>` in the configuration file, or by using the
+`--host=<HOST>`, and `--build=<BUILD>` command line arguments.
+
+`otp_build configure` will configure both for the boostrap system on the
+build machine and the cross host system.
+
+ (10)
+
+ $ ./otp_build boot -a
+
+`otp_build boot -a` will first build a bootstrap system for the build machine
+and then do the cross build of the system.
+
+ (11)
+
+ $ ./otp_build release -a <RELEASE_DIR>
+
+`otp_build release -a` will do the same as (5), and you will after this have
+to do a manual install either by doing (6), or (7).
+
+Testing the cross compiled system
+---------------------------------
+Some of the tests that come with erlang use native code to test. This means
+that when cross compiling erlang you also have to cross compile test suites
+in order to run tests on the target host. To do this you first have to release
+the tests as usual.
+
+ $ make release_tests
+
+or
+
+ $ ./otp_build tests
+
+The tests will be released into `$ERL_TOP/release/tests`. After releasing the
+tests you have to install the tests on the build machine. You supply the same
+xcomp file as to `./otp_build` in (9).
+
+ $ cd $ERL_TOP/release/tests/test_server/
+ $ $ERL_TOP/bootstrap/bin/erl -eval 'ts:install([{xcomp,"<FILE>"}])' -s ts compile_testcases -s init stop
+
+You should get a lot of printouts as the testcases are compiled. Once done you
+should copy the entire `$ERL_TOP/release/tests` folder to the cross host system.
+
+Then go to the cross host system and setup the erlang installed in (4) or (5)
+to be in your `$PATH`. Then go to what previously was
+`$ERL_TOP/release/tests/test_server` and issue the following command.
+
+ $ erl -s ts install -s ts run all_tests -s init stop
+
+The configure should be skipped and all tests should hopefully pass. For more
+details about how to use ts run `erl -s ts help -s init stop`
+
+Currently Used Configuration Variables
+--------------------------------------
+
+Note that you cannot define arbitrary variables in a cross compilation
+configuration file. Only the ones listed below will be guaranteed to be
+visible throughout the whole execution of all `configure` scripts. Other
+variables needs to be defined as arguments to `configure` or exported in
+the environment.
+
+### Variables for otp\_build Only ###
+
+Variables in this section are only used, when configuring Erlang/OTP for
+cross compilation using `$ERL_TOP/otp_build configure`.
+
+> *NOTE*: These variables currently have *no* effect if you configure using
+> the `configure` script directly.
+
+* `erl_xcomp_build` - The build system used. This value will be passed as
+ `--build=$erl_xcomp_build` argument to the `configure` script. It does
+ not have to be a full `CPU-VENDOR-OS` triplet, but can be. The full
+ `CPU-VENDOR-OS` triplet will be created by
+ `$ERL_TOP/erts/autoconf/config.sub $erl_xcomp_build`. If set to `guess`,
+ the build system will be guessed using
+ `$ERL_TOP/erts/autoconf/config.guess`.
+
+* `erl_xcomp_host` - Cross host/target system to build for. This value will
+ be passed as `--host=$erl_xcomp_host` argument to the `configure` script.
+ It does not have to be a full `CPU-VENDOR-OS` triplet, but can be. The
+ full `CPU-VENDOR-OS` triplet will be created by
+ `$ERL_TOP/erts/autoconf/config.sub $erl_xcomp_host`.
+
+* `erl_xcomp_configure_flags` - Extra configure flags to pass to the
+ `configure` script.
+
+### Cross Compiler and Other Tools ###
+
+If the cross compilation tools are prefixed by `<HOST>-` you probably do
+not need to set these variables (where `<HOST>` is what has been passed as
+`--host=<HOST>` argument to `configure`).
+
+All variables in this section can also be used when native compiling.
+
+* `CC` - C compiler.
+
+* `CFLAGS` - C compiler flags.
+
+* `STATIC_CFLAGS` - Static C compiler flags.
+
+* `CFLAG_RUNTIME_LIBRARY_PATH` - This flag should set runtime library
+ search path for the shared libraries. Note that this actually is a
+ linker flag, but it needs to be passed via the compiler.
+
+* `CPP` - C pre-processor.
+
+* `CPPFLAGS` - C pre-processor flags.
+
+* `CXX` - C++ compiler.
+
+* `CXXFLAGS` - C++ compiler flags.
+
+* `LD` - Linker.
+
+* `LDFLAGS` - Linker flags.
+
+* `LIBS` - Libraries.
+
+#### Dynamic Erlang Driver Linking ####
+
+> *NOTE*: Either set all or none of the `DED_LD*` variables.
+
+* `DED_LD` - Linker for Dynamically loaded Erlang Drivers.
+
+* `DED_LDFLAGS` - Linker flags to use with `DED_LD`.
+
+* `DED_LD_FLAG_RUNTIME_LIBRARY_PATH` - This flag should set runtime library
+ search path for shared libraries when linking with `DED_LD`.
+
+#### Large File Support ####
+
+> *NOTE*: Either set all or none of the `LFS_*` variables.
+
+* `LFS_CFLAGS` - Large file support C compiler flags.
+
+* `LFS_LDFLAGS` - Large file support linker flags.
+
+* `LFS_LIBS` - Large file support libraries.
+
+#### Other Tools ####
+
+* `RANLIB` - `ranlib` archive index tool.
+
+* `AR` - `ar` archiving tool.
+
+* `GETCONF` - `getconf` system configuration inspection tool. `getconf` is
+ currently used for finding out large file support flags to use, and
+ on Linux systems for finding out if we have an NPTL thread library or
+ not.
+
+### Cross System Root Locations ###
+
+* `erl_xcomp_sysroot` - The absolute path to the system root of the cross
+ compilation environment. Currently, the `crypto`, `odbc`, `ssh` and
+ `ssl` applications need the system root. These applications will be
+ skipped if the system root has not been set. The system root might be
+ needed for other things too. If this is the case and the system root
+ has not been set, `configure` will fail and request you to set it.
+
+* `erl_xcomp_isysroot` - The absolute path to the system root for includes
+ of the cross compilation environment. If not set, this value defaults
+ to `$erl_xcomp_sysroot`, i.e., only set this value if the include system
+ root path is not the same as the system root path.
+
+### Optional Feature, and Bug Tests ###
+
+These tests cannot (always) be done automatically when cross compiling. You
+usually do not need to set these variables.
+
+> *WARNING*: Setting these variables wrong may cause hard to detect
+> runtime errors. If you need to change these values, *really* make sure
+> that the values are correct.
+
+> *NOTE*: Some of these values will override results of tests performed
+> by `configure`, and some will not be used until `configure` is sure that
+> it cannot figure the result out.
+
+The `configure` script will issue a warning when a default value is used.
+When a variable has been set, no warning will be issued.
+
+* `erl_xcomp_after_morecore_hook` - `yes|no`. Defaults to `no`. If `yes`,
+ the target system must have a working `__after_morecore_hook` that can be
+ used for tracking used `malloc()` implementations core memory usage.
+ This is currently only used by unsupported features.
+
+* `erl_xcomp_bigendian` - `yes|no`. No default. If `yes`, the target system
+ must be big endian. If `no`, little endian. This can often be
+ automatically detected, but not always. If not automatically detected,
+ `configure` will fail unless this variable is set. Since no default
+ value is used, `configure` will try to figure this out automatically.
+
+* `erl_xcomp_double_middle` - `yes|no`. Defaults to `no`.
+ If `yes`, the target system must have doubles in "middle-endian" format. If
+ `no`, it has "regular" endianness.
+
+* `erl_xcomp_clock_gettime_cpu_time` - `yes|no`. Defaults to `no`. If `yes`,
+ the target system must have a working `clock_gettime()` implementation
+ that can be used for retrieving process CPU time.
+
+* `erl_xcomp_getaddrinfo` - `yes|no`. Defaults to `no`. If `yes`, the target
+ system must have a working `getaddrinfo()` implementation that can
+ handle both IPv4 and IPv6.
+
+* `erl_xcomp_gethrvtime_procfs_ioctl` - `yes|no`. Defaults to `no`. If `yes`,
+ the target system must have a working `gethrvtime()` implementation and
+ is used with procfs `ioctl()`.
+
+* `erl_xcomp_dlsym_brk_wrappers` - `yes|no`. Defaults to `no`. If `yes`, the
+ target system must have a working `dlsym(RTLD_NEXT, <S>)` implementation
+ that can be used on `brk` and `sbrk` symbols used by the `malloc()`
+ implementation in use, and by this track the `malloc()` implementations
+ core memory usage. This is currently only used by unsupported features.
+
+* `erl_xcomp_kqueue` - `yes|no`. Defaults to `no`. If `yes`, the target
+ system must have a working `kqueue()` implementation that returns a file
+ descriptor which can be used by `poll()` and/or `select()`. If `no` and
+ the target system has not got `epoll()` or `/dev/poll`, the kernel-poll
+ feature will be disabled.
+
+* `erl_xcomp_linux_clock_gettime_correction` - `yes|no`. Defaults to `yes` on
+ Linux; otherwise, `no`. If `yes`, `clock_gettime(CLOCK_MONOTONIC, _)` on
+ the target system must work. This variable is recommended to be set to
+ `no` on Linux systems with kernel versions less than 2.6.
+
+* `erl_xcomp_linux_nptl` - `yes|no`. Defaults to `yes` on Linux; otherwise,
+ `no`. If `yes`, the target system must have NPTL (Native POSIX Thread
+ Library). Older Linux systems have LinuxThreads instead of NPTL (Linux
+ kernel versions typically less than 2.6).
+
+* `erl_xcomp_linux_usable_sigaltstack` - `yes|no`. Defaults to `yes` on Linux;
+ otherwise, `no`. If `yes`, `sigaltstack()` must be usable on the target
+ system. `sigaltstack()` on Linux kernel versions less than 2.4 are
+ broken.
+
+* `erl_xcomp_linux_usable_sigusrx` - `yes|no`. Defaults to `yes`. If `yes`,
+ the `SIGUSR1` and `SIGUSR2` signals must be usable by the ERTS. Old
+ LinuxThreads thread libraries (Linux kernel versions typically less than
+ 2.2) used these signals and made them unusable by the ERTS.
+
+* `erl_xcomp_poll` - `yes|no`. Defaults to `no` on Darwin/MacOSX; otherwise,
+ `yes`. If `yes`, the target system must have a working `poll()`
+ implementation that also can handle devices. If `no`, `select()` will be
+ used instead of `poll()`.
+
+* `erl_xcomp_putenv_copy` - `yes|no`. Defaults to `no`. If `yes`, the target
+ system must have a `putenv()` implementation that stores a copy of the
+ key/value pair.
+
+* `erl_xcomp_reliable_fpe` - `yes|no`. Defaults to `no`. If `yes`, the target
+ system must have reliable floating point exceptions.
+
+Copyright and License
+---------------------
+
+%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%
+
+Modifying This Document
+-----------------------
+
+Before modifying this document you need to have a look at the
+[$ERL_TOP/HOWTO/MARKDOWN.md][] document.
+
+
+
+ [$ERL_TOP/HOWTO/INSTALL.md]: INSTALL.md
+ [Building in Git]: INSTALL.md#How-to-Build-and-Install-ErlangOTP_Building-in-Git
+ [How to Build the Documentation]: INSTALL.md#The-ErlangOTP-Documentation_How-to-Build-the-Documentation
+ [cross configuration variables]: #Currently-Used-Configuration-Variables
+ [DESTDIR]: http://www.gnu.org/prep/standards/html_node/DESTDIR.html
+ [$ERL_TOP/HOWTO/MARKDOWN.md]: MARKDOWN.md
+
+ [?TOC]: true
diff --git a/INSTALL-WIN32.md b/HOWTO/INSTALL-WIN32.md
index ff253d3dfa..94d3688f23 100644
--- a/INSTALL-WIN32.md
+++ b/HOWTO/INSTALL-WIN32.md
@@ -1037,10 +1037,11 @@ Modifying This Document
-----------------------
Before modifying this document you need to have a look at the
-`$ERL_TOP/README.md.txt` document.
+[$ERL_TOP/HOWTO/MARKDOWN.md][] document.
[1]: http://www.erlang.org/faq.html "mailing lists"
+ [$ERL_TOP/HOWTO/MARKDOWN.md]: MARKDOWN.md
[?TOC]: true
diff --git a/INSTALL.md b/HOWTO/INSTALL.md
index 70d465831d..4f7c317a47 100644
--- a/INSTALL.md
+++ b/HOWTO/INSTALL.md
@@ -18,10 +18,10 @@ Erlang/OTP should be possible to build from source on any Unix system,
including Mac OS X. This document describes how to native compile Erlang/OTP
on Unix. For detailed instructions on how to
-* cross compile Erlang/OTP, see the [$ERL_TOP/INSTALL-CROSS.md][]
+* cross compile Erlang/OTP, see the [$ERL_TOP/HOWTO/INSTALL-CROSS.md][]
document.
-* build Erlang/OTP on Windows, see the [$ERL_TOP/INSTALL-WIN32.md][]
+* build Erlang/OTP on Windows, see the [$ERL_TOP/HOWTO/INSTALL-WIN32.md][]
document.
Binary releases for Windows can be found at
@@ -708,23 +708,25 @@ the best possible performance, do like this:
Install Xcode from the AppStore if it is not already installed.
-For Xcode 4.3 you will also need to download "Command Line Tools"
-via the Downloads preference pane i Xcode.
+If you have Xcode 4.3, or later, you will also need to download
+"Command Line Tools" via the Downloads preference pane in Xcode.
Some tools may still be lacking or out-of-date, we recommend using
[Homebrew](https://github.com/mxcl/homebrew/wiki/installation) or
-Macports update those tools.
+Macports to update those tools.
Install MacPorts (<http://www.macports.org/>). Then:
$ sudo port selfupdate
$ sudo port install gcc45 +universal
-If you want to build the `wx` application, get wxMac-2.8.12
+### Building with wxErlang ###
+
+If you want to build the `wx` application, you will need to get wxMac-2.8.12
(`wxMac-2.8.12.tar.gz` from
-<http://sourceforge.net/projects/wxwindows/files/2.8.12/>) and build:
+<http://sourceforge.net/projects/wxwindows/files/2.8.12/>) and install it.
-Export the path for MacOSX10.6.sdk,
+Export the path for MacOSX10.6.sdk:
$ export SDK=/Developer/SDKs/MacOSX10.6.sdk
@@ -732,18 +734,20 @@ In Xcode 4.3 the path has changed so use the following instead,
$ export SDK=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.6.sdk
-Then configure and build wx,
+Then configure and build wxMac:
$ arch_flags="-arch i386" ./configure CFLAGS="$arch_flags" CXXFLAGS="$arch_flags" CPPFLAGS="$arch_flags" LDFLAGS="$arch_flags" OBJCFLAGS="$arch_flags" OBJCXXFLAGS="$arch_flags" --prefix=/usr/local --with-macosx-sdk="$SDK" --with-macosx-version-min=10.6 --enable-unicode --with-opengl --disable-shared
$ make
$ sudo make install
-To link wx properly we will also need to build and install `wxStyledTextCtrl`
+To link wx properly you will also need to build and install `wxStyledTextCtrl`:
$ cd contrib/src/stc
$ make
$ sudo make install
+### Finish up ###
+
Build Erlang with the MacPorts GCC as the main compiler (using `clang`
for the Objective-C Cocoa code in the `wx` application):
@@ -821,12 +825,12 @@ Modifying This Document
-----------------------
Before modifying this document you need to have a look at the
-`$ERL_TOP/README.md.txt` document.
+[$ERL_TOP/HOWTO/MARKDOWN.md][] document.
- [$ERL_TOP/INSTALL-CROSS.md]: INSTALL-CROSS
- [$ERL_TOP/INSTALL-WIN32.md]: INSTALL-WIN32
+ [$ERL_TOP/HOWTO/INSTALL-CROSS.md]: INSTALL-CROSS.md
+ [$ERL_TOP/HOWTO/INSTALL-WIN32.md]: INSTALL-WIN32.md
[DESTDIR]: http://www.gnu.org/prep/standards/html_node/DESTDIR.html
[Building in Git]: #How-to-Build-and-Install-ErlangOTP_Building-in-Git
[Pre-built Source Release]: #How-to-Build-and-Install-ErlangOTP_Prebuilt-Source-Release
@@ -834,5 +838,6 @@ Before modifying this document you need to have a look at the
[html documentation]: http://www.erlang.org/download/otp_doc_html_%OTP-REL%.tar.gz
[man pages]: http://www.erlang.org/download/otp_doc_man_%OTP-REL%.tar.gz
[the released source tar ball]: http://www.erlang.org/download/otp_src_%OTP-REL%.tar.gz
+ [$ERL_TOP/HOWTO/MARKDOWN.md]: MARKDOWN.md
[?TOC]: true
diff --git a/README.md.txt b/HOWTO/MARKDOWN.md
index a5a1bf4b7d..42045fb10c 100644
--- a/README.md.txt
+++ b/HOWTO/MARKDOWN.md
@@ -5,7 +5,7 @@ Introduction
------------
If you are looking for information on how to build and install Erlang/OTP you
-want to read the [$ERL_TOP/INSTALL.md][] document instead of this document
+want to read the [$ERL_TOP/HOWTO/INSTALL.md][] document instead of this document
(where `$ERL_TOP` is the top source directory in the source tree).
All files with the `.md` suffix (as well as this file) are ordinary text files
@@ -258,7 +258,7 @@ under the License.
- [$ERL_TOP/INSTALL.md]: doc/installation_guide:INSTALL
+ [$ERL_TOP/HOWTO/INSTALL.md]: INSTALL.md
[github]: http://github.com
[our github repository]: http://github.com/erlang/otp
diff --git a/HOWTO/SYSTEMTAP.md b/HOWTO/SYSTEMTAP.md
new file mode 100644
index 0000000000..ce9c0b2f0c
--- /dev/null
+++ b/HOWTO/SYSTEMTAP.md
@@ -0,0 +1,75 @@
+SystemTap and Erlang/OTP
+========================
+
+Introduction
+------------
+
+SystemTap is DTrace for Linux. In fact Erlang's SystemTap support
+is build using SystemTap's DTrace compatibility's layer. For an
+introduction to Erlang DTrace support read [$ERL_TOP/HOWTO/DTRACE.md][].
+
+Requisites
+----------
+
+* Linux Kernel with UTRACE support
+
+ check for UTRACE support in your current kernel:
+
+ # grep CONFIG_UTRACE /boot/config-`uname -r`
+ CONFIG_UTRACE=y
+
+ Fedora 16 is known to contain UTRACE, for most other Linux distributions
+ a custom build kernel will be required.
+ Check Fedora's SystemTap documentation for additional required packages
+ (e.g. Kernel Debug Symbols)
+
+* SystemTap > 1.6
+
+ A the time of writing this, the latest released version of SystemTap is
+ version 1.6. Erlang's DTrace support requires a MACRO that was introduced
+ after that release. So either get a newer release or build SystemTap from
+ git yourself (see: http://sourceware.org/systemtap/getinvolved.html)
+
+Building Erlang
+---------------
+
+Configure and build Erlang with SystemTap support:
+
+ # ./configure --with-dynamic-trace=systemtap + whatever args you need
+ # make
+
+Testing
+-------
+
+SystemTap, unlike DTrace, needs to know what binary it is tracing and has to
+be able to read that binary before it starts tracing. Your probe script
+therefor has to reference the correct beam emulator and stap needs to be able
+to find that binary.
+The examples are written for "beam", but other versions such as "beam.smp" or
+"beam.debug.smp" might exist (depending on your configuration). Make sure you
+either specify the full the path of the binary in the probe or your "beam"
+binary is in the search path.
+
+All available probes can be listed like this:
+
+ # stap -L 'process("beam").mark("*")'
+
+or:
+
+ # PATH=/path/to/beam:$PATH stap -L 'process("beam").mark("*")'
+
+
+Probes in the dtrace.so NIF library like this:
+
+ # PATH=/path/to/dtrace/priv/lib:$PATH stap -L 'process("dtrace.so").mark("*")'
+
+Running SystemTap scripts
+-------------------------
+
+Adjust the process("beam") reference to your beam version and attach the script
+to a running "beam" instance:
+
+ # stap /path/to/probe/script/port1.systemtap -x <pid of beam>
+
+
+ [$ERL_TOP/HOWTO/DTRACE.md]: DTRACE.md
diff --git a/HOWTO/TESTING.md b/HOWTO/TESTING.md
new file mode 100644
index 0000000000..34eaa68df8
--- /dev/null
+++ b/HOWTO/TESTING.md
@@ -0,0 +1,150 @@
+Testing Erlang/OTP
+==================
+
+Before you start testing you need to have the Erlang release which you
+are going to test in your path. See [$ERL_TOP/HOWTO/INSTALL.md][] for
+instructions on how to build an Erlang release.
+
+Short version
+-------------
+Move to the top directory of the Erlang release you want to test, i.e.
+cd /ldisk/work/otp
+
+ export ERL_TOP=`pwd`
+ ./otp_build setup -a
+ export PATH=`pwd`/bin:$PATH
+ ./otp_build tests
+ cd release/tests/test_server
+ erl -s ts install -s ts run all_tests -s init stop
+
+Where are the tests
+-------------------
+
+There are a lot of tests which test Erlang/OTP (as of 2012 about 12000) and
+they are located in different places throughout the source tree. Below is a list
+of the places where you can expect to find tests:
+
+* $ERL_TOP/lib/AppName/test/
+* $ERL_TOP/erts/test/
+* $ERL_TOP/erts/emulator/test/
+* $ERL_TOP/erts/epmd/test/
+
+Writing tests
+-------------
+
+All tests are [common_test][] suites and follow the same pattern as all
+[common_test][] suites. However, a couple of corner cases are
+handled by a test wrapper called `ts`. `ts` allows the test writer to put
+`Makefile.src` and `Makefile.first` files in the [data_dir][] and a special
+directory called `all_SUITE_data`.
+
+`Makefile.first` is run before any other Makefile and is typically used to
+generate .hrl files which are needed by other test suites. At the moment only
+the erl_interface tests use this feature and it should remain that way.
+
+`Makefile.src` is configured to a `Makefile` using the `variables` created when
+[configuring the tests][]. These `Makefile`s are later run by the test suite
+to compile whatever platform specific code the tests need to run.
+
+Releasing tests
+---------------
+
+If you cannot use [ct_run][] in the source tree you have to release the tests
+into a common test directory. The easiest way to do this is to use `otp_build`
+like this:
+
+ export ERL_TOP=`pwd`; ./otp_build tests
+
+This will release all tests in Erlang/OTP to `$ERL_TOP/release/tests/`. If you
+want to change the directory where the tests are released to use the `TESTROOT`
+environmental variable.
+
+In the `$TESTROOT` you should now see *_test folders. These folders contain
+everything needed to test Erlang/OTP and are platform independent; if you are
+testing Erlang on multiple platforms you just have to release on one and copy
+the tests to all other platforms.
+
+### Releasing cross tests
+
+For releasing tests in a cross compilation environment see [$ERL_TOP/HOWTO/INSTALL-CROSS.md][].
+
+Configuring and Running tests
+-----------------------------
+
+Running tests is done by first navigating to the `$TESTROOT/test_server` folder
+created when you released the tests and then start `erl` in that directory. The
+emulator flags specified will be used in the test runs. For example, if you want
+to test using async threads you have to supply `+A 10` to `erl` when you start it.
+
+To configure and run the tests `ts` is used. `ts` is a wrapper module to
+[common_test][] which takes care of configuration and build issues before
+[common_test][] is started.
+
+`ts` has a lot of special options and functions which can be usefull when
+testing Erlang/OTP. For a full listing issue `ts:help()` in the erlang shell.
+
+### Configuring the test environment
+
+Before running released tests you have to install them on the target system.
+Installing the tests is done by calling `ts:install().` in the Erlang shell
+which you intend to test from. `ts:install()` is basically a wrapper to a
+configure script and some Erlang code which figures out what your system looks
+like and what kind of emulator you are testing with. `ts:install()` can also
+take some arguments if necessary, see `ts:help()` for details.
+
+All variables created by `ts:install()` are found in
+`$TESTROOT/test_server/variables`.
+
+### Running the tests
+
+To run all test suites go to `$TESTROOT/test_server` fire up an Erlang shell and type:
+
+ ts:run().
+
+Note that running all tests will require several hours, so you may want to run
+the test cases for a single application
+
+ ts:run(Application, [batch]).
+
+or even part of the test suite for an application, for example
+
+ ts:run(emulator, bs, [batch]).
+
+to run all test suite modules starting with `bs` (i.e. all modules that test
+the bit syntax).
+
+To run a specific test case in a module, the full name of the module and test
+case must be specified:
+
+ ts:run(emulator, bs_bincomp_SUITE, byte_aligned, [batch]).
+
+Run `ts:help().` for more information.
+
+As of R14B02 it is also possibly to start all tests but the erl_interface tests
+by invoking Common Test directly from the released applications test directory,
+i.e.
+
+ cd $TESTROOT/test_server
+ $ERL_TOP/bin/ct_run -suite ../compiler_test/andor_SUITE -case t_orelse
+
+Running [ct_run][] from the command line still requires you to do the
+`ts:install()` step above.
+
+Examining the results
+---------------------
+
+Open the file `release/tests/test_server/index.html` in a web browser. Or open
+`release/tests/test_server/last_test.html` when a test suite is running to
+examine the results so far for the currently executing test suite (in R14B02 and
+later you want to open the `release/tests/test_server/all_runs.html` file to
+get to the currently running test)
+
+ [ct_run]: http://www.erlang.org/doc/man/ct_run.html
+ [ct hook]: http://www.erlang.org/doc/apps/common_test/ct_hooks_chapter.html
+ [$ERL_TOP/HOWTO/INSTALL.md]: INSTALL.md
+ [$ERL_TOP/HOWTO/INSTALL-CROSS.md]: INSTALL-CROSS.md#testing-the-cross-compiled-system
+ [common_test]: http://www.erlang.org/doc/man/ct.html
+ [data_dir]: http://www.erlang.org/doc/apps/common_test/write_test_chapter.html#data_priv_dir
+ [configuring the tests]: #configuring-the-test-environment
+
+ [?TOC]: true
diff --git a/INSTALL-CROSS.md b/INSTALL-CROSS.md
deleted file mode 120000
index 9e7743b9de..0000000000
--- a/INSTALL-CROSS.md
+++ /dev/null
@@ -1 +0,0 @@
-xcomp/README.md \ No newline at end of file
diff --git a/Makefile.in b/Makefile.in
index 544233f097..e5909aa7f0 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -35,6 +35,9 @@ OTP = OTP-@OTP_REL@
# erts (Erlang RunTime System) version
ERTS = erts-@ERTS_VSN@
+# Include verbose output variables
+include $(ERL_TOP)/make/output.mk
+
# ----------------------------------------------------------------------
#
@@ -426,57 +429,58 @@ BOOTSTRAP_COMPILER = $(BOOTSTRAP_TOP)/primary_compiler
.PHONY: emulator libs kernel stdlib compiler hipe typer syntax_tools preloaded
emulator:
- cd erts && ERL_TOP=$(ERL_TOP) $(MAKE) NO_START_SCRIPTS=true $(TYPE) FLAVOR=$(FLAVOR)
+ $(make_verbose)cd erts && ERL_TOP=$(ERL_TOP) $(MAKE) NO_START_SCRIPTS=true $(TYPE) FLAVOR=$(FLAVOR)
libs:
ifeq ($(OTP_SMALL_BUILD),true)
- cd lib && \
+ $(make_verbose)cd lib && \
ERL_TOP=$(ERL_TOP) PATH=$(BOOT_PREFIX)"$${PATH}" \
$(MAKE) opt
else
- cd lib && \
+ $(make_verbose)cd lib && \
ERL_TOP=$(ERL_TOP) PATH=$(BOOT_PREFIX)"$${PATH}" \
$(MAKE) opt BUILD_ALL=true
endif
kernel:
- cd lib/kernel && \
+ $(make_verbose)cd lib/kernel && \
ERL_TOP=$(ERL_TOP) PATH=$(BOOT_PREFIX)"$${PATH}" \
$(MAKE) opt BUILD_ALL=true
stdlib:
- cd lib/stdlib && \
+ $(make_verbose)cd lib/stdlib && \
ERL_TOP=$(ERL_TOP) PATH=$(BOOT_PREFIX)"$${PATH}" \
$(MAKE) opt BUILD_ALL=true
compiler:
- cd lib/compiler && \
+ $(make_verbose)cd lib/compiler && \
ERL_TOP=$(ERL_TOP) PATH=$(BOOT_PREFIX)"$${PATH}" \
$(MAKE) opt BUILD_ALL=true
hipe:
- cd lib/hipe && \
+ $(make_verbose)cd lib/hipe && \
ERL_TOP=$(ERL_TOP) PATH=$(BOOT_PREFIX)"$${PATH}" \
$(MAKE) opt BUILD_ALL=true
typer:
- cd lib/typer && \
+ $(make_verbose)cd lib/typer && \
ERL_TOP=$(ERL_TOP) PATH=$(BOOT_PREFIX)"$${PATH}" \
$(MAKE) opt BUILD_ALL=true
syntax_tools:
- cd lib/syntax_tools && \
+ $(make_verbose)cd lib/syntax_tools && \
ERL_TOP=$(ERL_TOP) PATH=$(BOOT_PREFIX)"$${PATH}" \
$(MAKE) opt BUILD_ALL=true
preloaded:
- cd erts/preloaded/src && \
+ $(make_verbose)cd erts/preloaded/src && \
ERL_TOP=$(ERL_TOP) PATH=$(BOOT_PREFIX)"$${PATH}" \
$(MAKE) opt BUILD_ALL=true
dep depend:
- test X"$$ERTS_SKIP_DEPEND" = X"true" || (cd erts/emulator && ERL_TOP=$(ERL_TOP) $(MAKE) generate)
- test X"$$ERTS_SKIP_DEPEND" = X"true" || (cd erts/emulator && ERL_TOP=$(ERL_TOP) $(MAKE) depend)
- test X"$$ERTS_SKIP_DEPEND" = X"true" || (cd erts/lib_src && ERL_TOP=$(ERL_TOP) $(MAKE) depend)
+ $(make_verbose)
+ $(V_at)test X"$$ERTS_SKIP_DEPEND" = X"true" || (cd erts/emulator && ERL_TOP=$(ERL_TOP) $(MAKE) generate)
+ $(V_at)test X"$$ERTS_SKIP_DEPEND" = X"true" || (cd erts/emulator && ERL_TOP=$(ERL_TOP) $(MAKE) depend)
+ $(V_at)test X"$$ERTS_SKIP_DEPEND" = X"true" || (cd erts/lib_src && ERL_TOP=$(ERL_TOP) $(MAKE) depend)
# Creates "erl" and "erlc" in bootstrap/bin which uses the precompiled
# libraries in the bootstrap directory
@@ -530,14 +534,15 @@ bootstrap_setup_target:
echo $(TARGET) > $(BOOTSTRAP_ROOT)/bootstrap/target
secondary_bootstrap_build:
- cd lib && \
+ $(make_verbose)cd lib && \
ERL_TOP=$(ERL_TOP) PATH=$(BOOT_PREFIX)"$${PATH}" \
$(MAKE) opt SECONDARY_BOOTSTRAP=true
secondary_bootstrap_copy:
- if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/hipe ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/hipe ; fi
- if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/hipe/ebin ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/hipe/ebin ; fi
- for x in lib/hipe/$(HIPE_BOOTSTRAP_EBIN)/*.beam; do \
+ $(make_verbose)
+ $(V_at)if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/hipe ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/hipe ; fi
+ $(V_at)if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/hipe/ebin ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/hipe/ebin ; fi
+ $(V_at)for x in lib/hipe/$(HIPE_BOOTSTRAP_EBIN)/*.beam; do \
BN=`basename $$x`; \
TF=$(BOOTSTRAP_ROOT)/bootstrap/lib/hipe/ebin/$$BN; \
test -f $$TF && \
@@ -547,12 +552,12 @@ secondary_bootstrap_copy:
cp $$x $$TF; \
true; \
done
- if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/parsetools ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/parsetools ; fi
- if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/parsetools/ebin ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/parsetools/ebin ; fi
- if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/parsetools/include ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/parsetools/include ; fi
- if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/orber ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/orber ; fi
- if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/orber/include ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/orber/include ; fi
- for x in lib/parsetools/ebin/*.beam; do \
+ $(V_at)if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/parsetools ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/parsetools ; fi
+ $(V_at)if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/parsetools/ebin ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/parsetools/ebin ; fi
+ $(V_at)if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/parsetools/include ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/parsetools/include ; fi
+ $(V_at)if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/orber ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/orber ; fi
+ $(V_at)if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/orber/include ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/orber/include ; fi
+ $(V_at)for x in lib/parsetools/ebin/*.beam; do \
BN=`basename $$x`; \
TF=$(BOOTSTRAP_ROOT)/bootstrap/lib/parsetools/ebin/$$BN; \
test -f $$TF && \
@@ -562,8 +567,8 @@ secondary_bootstrap_copy:
cp $$x $$TF; \
true; \
done
-# cp lib/parsetools/ebin/*.beam $(BOOTSTRAP_ROOT)/bootstrap/lib/parsetools/ebin
- for x in lib/parsetools/include/*.hrl; do \
+# $(V_at)cp lib/parsetools/ebin/*.beam $(BOOTSTRAP_ROOT)/bootstrap/lib/parsetools/ebin
+ $(V_at)for x in lib/parsetools/include/*.hrl; do \
BN=`basename $$x`; \
TF=$(BOOTSTRAP_ROOT)/bootstrap/lib/parsetools/include/$$BN; \
test -f $$TF && \
@@ -573,11 +578,11 @@ secondary_bootstrap_copy:
cp $$x $$TF; \
true; \
done
-# cp -f lib/parsetools/include/*.hrl $(BOOTSTRAP_ROOT)/bootstrap/lib/parsetools/include
- if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/asn1 ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/asn1 ; fi
- if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/asn1/ebin ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/asn1/ebin ; fi
- if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/asn1/src ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/asn1/src ; fi
- for x in lib/asn1/ebin/*.beam; do \
+# $(V_at)cp -f lib/parsetools/include/*.hrl $(BOOTSTRAP_ROOT)/bootstrap/lib/parsetools/include
+ $(V_at)if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/asn1 ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/asn1 ; fi
+ $(V_at)if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/asn1/ebin ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/asn1/ebin ; fi
+ $(V_at)if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/asn1/src ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/asn1/src ; fi
+ $(V_at)for x in lib/asn1/ebin/*.beam; do \
BN=`basename $$x`; \
TF=$(BOOTSTRAP_ROOT)/bootstrap/lib/asn1/ebin/$$BN; \
test -f $$TF && \
@@ -587,8 +592,8 @@ secondary_bootstrap_copy:
cp $$x $$TF; \
true; \
done
-# cp lib/asn1/ebin/*.beam $(BOOTSTRAP_ROOT)/bootstrap/lib/asn1/ebin
- for x in lib/asn1/src/*.[eh]rl; do \
+# $(V_at)cp lib/asn1/ebin/*.beam $(BOOTSTRAP_ROOT)/bootstrap/lib/asn1/ebin
+ $(V_at)for x in lib/asn1/src/*.[eh]rl; do \
BN=`basename $$x`; \
TF=$(BOOTSTRAP_ROOT)/bootstrap/lib/asn1/src/$$BN; \
test -f $$TF && \
@@ -598,8 +603,8 @@ secondary_bootstrap_copy:
cp $$x $$TF; \
true; \
done
-# cp -f lib/asn1/src/*.erl lib/asn1/src/*.hrl $(BOOTSTRAP_ROOT)/bootstrap/lib/asn1/src
- for x in lib/orber/include/*.hrl; do \
+# $(V_at)cp -f lib/asn1/src/*.erl lib/asn1/src/*.hrl $(BOOTSTRAP_ROOT)/bootstrap/lib/asn1/src
+ $(V_at)for x in lib/orber/include/*.hrl; do \
BN=`basename $$x`; \
TF=$(BOOTSTRAP_ROOT)/bootstrap/lib/orber/include/$$BN; \
test -f $$TF && \
@@ -609,9 +614,9 @@ secondary_bootstrap_copy:
cp $$x $$TF; \
true; \
done
- if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/xmerl ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/xmerl ; fi
- if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/xmerl/include ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/xmerl/include ; fi
- for x in lib/xmerl/include/*.hrl; do \
+ $(V_at)if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/xmerl ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/xmerl ; fi
+ $(V_at)if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/xmerl/include ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/xmerl/include ; fi
+ $(V_at)for x in lib/xmerl/include/*.hrl; do \
BN=`basename $$x`; \
TF=$(BOOTSTRAP_ROOT)/bootstrap/lib/xmerl/include/$$BN; \
test -f $$TF && \
@@ -623,15 +628,16 @@ secondary_bootstrap_copy:
done
tertiary_bootstrap_build:
- cd lib && \
+ $(make_verbose)cd lib && \
ERL_TOP=$(ERL_TOP) PATH=$(BOOT_PREFIX)"$${PATH}" \
$(MAKE) opt TERTIARY_BOOTSTRAP=true
tertiary_bootstrap_copy:
- if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/snmp ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/snmp ; fi
- if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/snmp/ebin ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/snmp/ebin ; fi
- if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/snmp/include ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/snmp/include ; fi
- for x in lib/snmp/ebin/*.beam; do \
+ $(make_verbose)
+ $(V_at)if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/snmp ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/snmp ; fi
+ $(V_at)if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/snmp/ebin ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/snmp/ebin ; fi
+ $(V_at)if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/snmp/include ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/snmp/include ; fi
+ $(V_at)for x in lib/snmp/ebin/*.beam; do \
BN=`basename $$x`; \
TF=$(BOOTSTRAP_ROOT)/bootstrap/lib/snmp/ebin/$$BN; \
test -f $$TF && \
@@ -641,21 +647,21 @@ tertiary_bootstrap_copy:
cp $$x $$TF; \
true; \
done
-# cp lib/snmp/ebin/*.beam $(BOOTSTRAP_ROOT)/bootstrap/lib/snmp/ebin
- if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/sasl ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/sasl ; fi
- if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/sasl/ebin ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/sasl/ebin ; fi
- if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/sasl/include ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/sasl/include ; fi
- if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/ic ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/ic ; fi
- if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/ic/ebin ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/ic/ebin ; fi
- if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/ic/include ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/ic/include ; fi
- if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/wx ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/wx ; fi
- if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/wx/ebin ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/wx/ebin ; fi
- if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/wx/include ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/wx/include ; fi
- if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/test_server ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/test_server ; fi
- if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/test_server/include ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/test_server/include ; fi
- if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/common_test ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/common_test ; fi
- if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/common_test/include ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/common_test/include ; fi
- for x in lib/ic/ebin/*.beam; do \
+# $(V_at)cp lib/snmp/ebin/*.beam $(BOOTSTRAP_ROOT)/bootstrap/lib/snmp/ebin
+ $(V_at)if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/sasl ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/sasl ; fi
+ $(V_at)if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/sasl/ebin ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/sasl/ebin ; fi
+ $(V_at)if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/sasl/include ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/sasl/include ; fi
+ $(V_at)if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/ic ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/ic ; fi
+ $(V_at)if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/ic/ebin ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/ic/ebin ; fi
+ $(V_at)if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/ic/include ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/ic/include ; fi
+ $(V_at)if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/wx ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/wx ; fi
+ $(V_at)if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/wx/ebin ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/wx/ebin ; fi
+ $(V_at)if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/wx/include ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/wx/include ; fi
+ $(V_at)if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/test_server ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/test_server ; fi
+ $(V_at)if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/test_server/include ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/test_server/include ; fi
+ $(V_at)if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/common_test ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/common_test ; fi
+ $(V_at)if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/common_test/include ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/common_test/include ; fi
+ $(V_at)for x in lib/ic/ebin/*.beam; do \
BN=`basename $$x`; \
TF=$(BOOTSTRAP_ROOT)/bootstrap/lib/ic/ebin/$$BN; \
test -f $$TF && \
@@ -665,8 +671,8 @@ tertiary_bootstrap_copy:
cp $$x $$TF; \
true; \
done
-# cp lib/ic/ebin/*.beam $(BOOTSTRAP_ROOT)/bootstrap/lib/ic/ebin
- for x in lib/ic/include/*.idl; do \
+# $(V_at)cp lib/ic/ebin/*.beam $(BOOTSTRAP_ROOT)/bootstrap/lib/ic/ebin
+ $(V_at)for x in lib/ic/include/*.idl; do \
BN=`basename $$x`; \
TF=$(BOOTSTRAP_ROOT)/bootstrap/lib/ic/include/$$BN; \
test -f $$TF && \
@@ -676,7 +682,7 @@ tertiary_bootstrap_copy:
cp $$x $$TF; \
true; \
done
- for x in lib/ic/include/*.h; do \
+ $(V_at)for x in lib/ic/include/*.h; do \
BN=`basename $$x`; \
TF=$(BOOTSTRAP_ROOT)/bootstrap/lib/ic/include/$$BN; \
test -f $$TF && \
@@ -686,8 +692,8 @@ tertiary_bootstrap_copy:
cp $$x $$TF; \
true; \
done
-# cp -f lib/ic/include/*.idl lib/ic/include/*.h $(BOOTSTRAP_ROOT)/bootstrap/lib/ic/include
- for x in lib/sasl/ebin/*.beam; do \
+# $(V_at)cp -f lib/ic/include/*.idl lib/ic/include/*.h $(BOOTSTRAP_ROOT)/bootstrap/lib/ic/include
+ $(V_at)for x in lib/sasl/ebin/*.beam; do \
BN=`basename $$x`; \
TF=$(BOOTSTRAP_ROOT)/bootstrap/lib/sasl/ebin/$$BN; \
test -f $$TF && \
@@ -697,10 +703,10 @@ tertiary_bootstrap_copy:
cp $$x $$TF; \
true; \
done
-# cp lib/sasl/ebin/*.beam $(BOOTSTRAP_ROOT)/bootstrap/lib/sasl/ebin
- if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/syntax_tools ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/syntax_tools ; fi
- if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/syntax_tools/ebin ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/syntax_tools/ebin ; fi
- for x in lib/syntax_tools/ebin/*.beam; do \
+# $(V_at)cp lib/sasl/ebin/*.beam $(BOOTSTRAP_ROOT)/bootstrap/lib/sasl/ebin
+ $(V_at)if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/syntax_tools ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/syntax_tools ; fi
+ $(V_at)if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/syntax_tools/ebin ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/syntax_tools/ebin ; fi
+ $(V_at)for x in lib/syntax_tools/ebin/*.beam; do \
BN=`basename $$x`; \
TF=$(BOOTSTRAP_ROOT)/bootstrap/lib/syntax_tools/ebin/$$BN; \
test -f $$TF && \
@@ -710,7 +716,7 @@ tertiary_bootstrap_copy:
cp $$x $$TF; \
true; \
done
- for x in lib/wx/include/*.hrl; do \
+ $(V_at)for x in lib/wx/include/*.hrl; do \
BN=`basename $$x`; \
TF=$(BOOTSTRAP_ROOT)/bootstrap/lib/wx/include/$$BN; \
test -f $$TF && \
@@ -721,7 +727,7 @@ tertiary_bootstrap_copy:
true; \
done
# copy wx_object to remove undef behaviour warnings
- for x in lib/wx/ebin/wx_object.beam; do \
+ $(V_at)for x in lib/wx/ebin/wx_object.beam; do \
BN=`basename $$x`; \
TF=$(BOOTSTRAP_ROOT)/bootstrap/lib/wx/ebin/$$BN; \
test -f $$TF && \
@@ -733,7 +739,7 @@ tertiary_bootstrap_copy:
done
# copy test includes to be able to compile tests with bootstrap compiler
- for x in lib/test_server/include/*.hrl; do \
+ $(V_at)for x in lib/test_server/include/*.hrl; do \
BN=`basename $$x`; \
TF=$(BOOTSTRAP_ROOT)/bootstrap/lib/test_server/include/$$BN; \
test -f $$TF && \
@@ -744,7 +750,7 @@ tertiary_bootstrap_copy:
true; \
done
- for x in lib/common_test/include/*.hrl; do \
+ $(V_at)for x in lib/common_test/include/*.hrl; do \
BN=`basename $$x`; \
TF=$(BOOTSTRAP_ROOT)/bootstrap/lib/common_test/include/$$BN; \
test -f $$TF && \
@@ -754,7 +760,7 @@ tertiary_bootstrap_copy:
cp $$x $$TF; \
true; \
done
-# cp lib/syntax_tools/ebin/*.beam $(BOOTSTRAP_ROOT)/bootstrap/lib/syntax_tools/ebin
+# $(V_at)cp lib/syntax_tools/ebin/*.beam $(BOOTSTRAP_ROOT)/bootstrap/lib/syntax_tools/ebin
.PHONY: check_recreate_primary_bootstrap recreate_primary_bootstrap
@@ -783,7 +789,7 @@ check_recreate_primary_bootstrap:
# directories of all applications part of the primary bootstrap.
#
recreate_primary_bootstrap:
- $(ERL_TOP)/otp_build save_bootstrap
+ $(V_at)$(ERL_TOP)/otp_build save_bootstrap
# The first bootstrap build is rarely (never) used in open source, it's
# used to build the shipped bootstrap directory. The Open source bootstrap
@@ -802,21 +808,21 @@ recreate_primary_bootstrap:
primary_bootstrap:
@echo "=== Building a bootstrap compiler in $(BOOTSTRAP_ROOT)/bootstrap"
- $(MAKE) BOOTSTRAP_ROOT=$(BOOTSTRAP_ROOT) \
+ $(V_at)$(MAKE) BOOTSTRAP_ROOT=$(BOOTSTRAP_ROOT) \
ERL_TOP=$(ERL_TOP) \
bootstrap_clean
- cd $(ERL_TOP) && \
+ $(V_at)cd $(ERL_TOP) && \
$(MAKE) TESTROOT=$(BOOTSTRAP_TOP) \
BOOTSTRAP_TOP=$(BOOTSTRAP_TOP) \
primary_bootstrap_build
- cd $(ERL_TOP) && \
+ $(V_at)cd $(ERL_TOP) && \
$(MAKE) TESTROOT=$(BOOTSTRAP_TOP) \
BOOTSTRAP_TOP=$(BOOTSTRAP_TOP) \
primary_bootstrap_copy
- cd $(ERL_TOP)/erts/start_scripts && \
+ $(V_at)cd $(ERL_TOP)/erts/start_scripts && \
$(MAKE) TESTROOT=$(BOOTSTRAP_TOP) \
BOOTSTRAP_TOP=$(BOOTSTRAP_TOP) bootstrap_scripts
- test $(BOOTSTRAP_ROOT) = $(ERL_TOP) \
+ $(V_at)test $(BOOTSTRAP_ROOT) = $(ERL_TOP) \
|| $(ERL_TOP)/otp_build \
copy_primary_bootstrap \
$(BOOTSTRAP_TOP) \
@@ -824,50 +830,52 @@ primary_bootstrap:
primary_bootstrap_build: primary_bootstrap_mkdirs primary_bootstrap_compiler \
primary_bootstrap_stdlib
- cd lib && $(MAKE) ERLC_FLAGS='-pa $(BOOTSTRAP_COMPILER)/ebin' \
+ $(make_verbose)cd lib && $(MAKE) ERLC_FLAGS='-pa $(BOOTSTRAP_COMPILER)/ebin' \
BOOTSTRAP_TOP=$(BOOTSTRAP_TOP) \
BOOTSTRAP=1 opt
primary_bootstrap_compiler:
- cd lib/compiler && $(MAKE) \
+ $(make_verbose)cd lib/compiler && $(MAKE) \
BOOTSTRAP_TOP=$(BOOTSTRAP_TOP) \
BOOTSTRAP_COMPILER=$(BOOTSTRAP_COMPILER) \
BOOTSTRAP=1 \
opt
primary_bootstrap_stdlib:
- cd lib/stdlib/src && $(MAKE) \
+ $(make_verbose)cd lib/stdlib/src && $(MAKE) \
BOOTSTRAP_COMPILER=$(BOOTSTRAP_COMPILER) \
primary_bootstrap_compiler
primary_bootstrap_mkdirs:
- test -d $(BOOTSTRAP_COMPILER)/egen \
+ $(make_verbose)
+ $(V_at)test -d $(BOOTSTRAP_COMPILER)/egen \
|| mkdir -p $(BOOTSTRAP_COMPILER)/egen
- test -d $(BOOTSTRAP_COMPILER)/ebin \
+ $(V_at)test -d $(BOOTSTRAP_COMPILER)/ebin \
|| mkdir -p $(BOOTSTRAP_COMPILER)/ebin
- test -d $(BOOTSTRAP_TOP)/lib/kernel/egen \
+ $(V_at)test -d $(BOOTSTRAP_TOP)/lib/kernel/egen \
|| mkdir -p $(BOOTSTRAP_TOP)/lib/kernel/egen
- test -d $(BOOTSTRAP_TOP)/lib/kernel/ebin \
+ $(V_at)test -d $(BOOTSTRAP_TOP)/lib/kernel/ebin \
|| mkdir -p $(BOOTSTRAP_TOP)/lib/kernel/ebin
- test -d $(BOOTSTRAP_TOP)/lib/kernel/include \
+ $(V_at)test -d $(BOOTSTRAP_TOP)/lib/kernel/include \
|| mkdir -p $(BOOTSTRAP_TOP)/lib/kernel/include
- test -d $(BOOTSTRAP_TOP)/lib/stdlib/egen \
+ $(V_at)test -d $(BOOTSTRAP_TOP)/lib/stdlib/egen \
|| mkdir -p $(BOOTSTRAP_TOP)/lib/stdlib/egen
- test -d $(BOOTSTRAP_TOP)/lib/stdlib/ebin \
+ $(V_at)test -d $(BOOTSTRAP_TOP)/lib/stdlib/ebin \
|| mkdir -p $(BOOTSTRAP_TOP)/lib/stdlib/ebin
- test -d $(BOOTSTRAP_TOP)/lib/stdlib/include \
+ $(V_at)test -d $(BOOTSTRAP_TOP)/lib/stdlib/include \
|| mkdir -p $(BOOTSTRAP_TOP)/lib/stdlib/include
- test -d $(BOOTSTRAP_TOP)/lib/compiler/egen \
+ $(V_at)test -d $(BOOTSTRAP_TOP)/lib/compiler/egen \
|| mkdir -p $(BOOTSTRAP_TOP)/lib/compiler/egen
- test -d $(BOOTSTRAP_TOP)/lib/compiler/ebin \
+ $(V_at)test -d $(BOOTSTRAP_TOP)/lib/compiler/ebin \
|| mkdir -p $(BOOTSTRAP_TOP)/lib/compiler/ebin
- test -d $(BOOTSTRAP_TOP)/lib/orber/include \
+ $(V_at)test -d $(BOOTSTRAP_TOP)/lib/orber/include \
|| mkdir -p $(BOOTSTRAP_TOP)/lib/orber/include
primary_bootstrap_copy:
- cp -f lib/kernel/include/*.hrl $(BOOTSTRAP_TOP)/lib/kernel/include
- cp -f lib/stdlib/include/*.hrl $(BOOTSTRAP_TOP)/lib/stdlib/include
- cp -f lib/orber/include/* $(BOOTSTRAP_TOP)/lib/orber/include
+ $(make_verbose)
+ $(V_at)cp -f lib/kernel/include/*.hrl $(BOOTSTRAP_TOP)/lib/kernel/include
+ $(V_at)cp -f lib/stdlib/include/*.hrl $(BOOTSTRAP_TOP)/lib/stdlib/include
+ $(V_at)cp -f lib/orber/include/* $(BOOTSTRAP_TOP)/lib/orber/include
# To remove modules left by the bootstrap building, but leave (restore)
# the modules in kernel which are needed for an emulator build
@@ -1000,9 +1008,10 @@ eclean:
#
bootstrap_root_clean:
- rm -f $(BOOTSTRAP_ROOT)/bootstrap/lib/*/ebin/*.beam
- rm -f $(BOOTSTRAP_ROOT)/bootstrap/lib/*/include/*.hrl
- rm -f $(BOOTSTRAP_ROOT)/bootstrap/bin/*.*
+ $(make_verbose)
+ $(V_at)rm -f $(BOOTSTRAP_ROOT)/bootstrap/lib/*/ebin/*.beam
+ $(V_at)rm -f $(BOOTSTRAP_ROOT)/bootstrap/lib/*/include/*.hrl
+ $(V_at)rm -f $(BOOTSTRAP_ROOT)/bootstrap/bin/*.*
# $(ERL_TOP)/bootstrap *should* equal $(BOOTSTRAP_TOP)
#
@@ -1010,15 +1019,16 @@ bootstrap_root_clean:
# extra safety precaution (we would really make a mess if
# $(BOOTSTRAP_TOP) for some reason should be empty).
bootstrap_clean:
- rm -f $(ERL_TOP)/bootstrap/lib/*/ebin/*.beam
- rm -f $(ERL_TOP)/bootstrap/lib/*/ebin/*.app
- rm -f $(ERL_TOP)/bootstrap/lib/*/egen/*
- rm -f $(ERL_TOP)/bootstrap/lib/*/include/*.hrl
- rm -f $(ERL_TOP)/bootstrap/primary_compiler/ebin/*
- rm -f $(ERL_TOP)/bootstrap/primary_compiler/egen/*
- rm -f $(ERL_TOP)/bootstrap/bin/*.*
- rm -f $(KERNEL_PRELOAD:%=$(ERL_TOP)/lib/kernel/ebin/%.beam)
- test $(BOOTSTRAP_ROOT) = $(ERL_TOP) \
+ $(make_verbose)
+ $(V_at)rm -f $(ERL_TOP)/bootstrap/lib/*/ebin/*.beam
+ $(V_at)rm -f $(ERL_TOP)/bootstrap/lib/*/ebin/*.app
+ $(V_at)rm -f $(ERL_TOP)/bootstrap/lib/*/egen/*
+ $(V_at)rm -f $(ERL_TOP)/bootstrap/lib/*/include/*.hrl
+ $(V_at)rm -f $(ERL_TOP)/bootstrap/primary_compiler/ebin/*
+ $(V_at)rm -f $(ERL_TOP)/bootstrap/primary_compiler/egen/*
+ $(V_at)rm -f $(ERL_TOP)/bootstrap/bin/*.*
+ $(V_at)rm -f $(KERNEL_PRELOAD:%=$(ERL_TOP)/lib/kernel/ebin/%.beam)
+ $(V_at)test $(BOOTSTRAP_ROOT) = $(ERL_TOP) \
|| $(MAKE) BOOTSTRAP_ROOT=$(BOOTSTRAP_ROOT) bootstrap_root_clean
# ----------------------------------------------------------------------
diff --git a/README.md b/README.md
index c38f097b0f..1bcf14e760 100644
--- a/README.md
+++ b/README.md
@@ -18,7 +18,7 @@ Building and Installing
-----------------------
Information on building and installing Erlang/OTP can be found
-in the `INSTALL.md` document.
+in the [$ERL_TOP/HOWTO/INSTALL.md] [5] document.
Contributing to Erlang/OTP
--------------------------
@@ -46,7 +46,7 @@ In short:
may suggest improvements that are needed before the change can be accepted
and merged.
-* Once or twice a week, a status email called "What's cooking in Erlang/OTP"
+* Once or twice a week, a status email called ["What's cooking in Erlang/OTP"] [4]
will be sent to the [`erlang-patches`] [3] mailing list.
Copyright and License
@@ -54,7 +54,7 @@ Copyright and License
> %CopyrightBegin%
>
-> Copyright Ericsson AB 2010. All Rights Reserved.
+> Copyright Ericsson AB 2010-2012. All Rights Reserved.
>
> The contents of this file are subject to the Erlang Public License,
> Version 1.1, (the "License"); you may not use this file except in
@@ -73,4 +73,6 @@ Copyright and License
[1]: http://www.erlang.org
[2]: http://wiki.github.com/erlang/otp/submitting-patches
- [3]: http://www.erlang.org/faq.html
+ [3]: http://www.erlang.org/static/doc/mailinglist.html
+ [4]: http://erlang.github.com/otp/
+ [5]: HOWTO/INSTALL.md
diff --git a/README.systemtap.md b/README.systemtap.md
deleted file mode 100644
index c190bcc893..0000000000
--- a/README.systemtap.md
+++ /dev/null
@@ -1,72 +0,0 @@
-SystemTap and Erlang/OTP
-========================
-
-Introduction
-------------
-
-SystemTap is DTrace for Linux. In fact Erlang's SystemTap support
-is build using SystemTap's DTrace compatibility's layer. For an
-introduction to Erlang DTrace support read README.dtrace.md.
-
-Requisites
-----------
-
-* Linux Kernel with UTRACE support
-
- check for UTRACE support in your current kernel:
-
- # grep CONFIG_UTRACE /boot/config-`uname -r`
- CONFIG_UTRACE=y
-
- Fedora 16 is known to contain UTRACE, for most other Linux distributions
- a custom build kernel will be required.
- Check Fedora's SystemTap documentation for additional required packages
- (e.g. Kernel Debug Symbols)
-
-* SystemTap > 1.6
-
- A the time of writing this, the latest released version of SystemTap is
- version 1.6. Erlang's DTrace support requires a MACRO that was introduced
- after that release. So either get a newer release or build SystemTap from
- git yourself (see: http://sourceware.org/systemtap/getinvolved.html)
-
-Building Erlang
----------------
-
-Configure and build Erlang with SystemTap support:
-
- # ./configure --with-dynamic-trace=systemtap + whatever args you need
- # make
-
-Testing
--------
-
-SystemTap, unlike DTrace, needs to know what binary it is tracing and has to
-be able to read that binary before it starts tracing. Your probe script
-therefor has to reference the correct beam emulator and stap needs to be able
-to find that binary.
-The examples are written for "beam", but other versions such as "beam.smp" or
-"beam.debug.smp" might exist (depending on your configuration). Make sure you
-either specify the full the path of the binary in the probe or your "beam"
-binary is in the search path.
-
-All available probes can be listed like this:
-
- # stap -L 'process("beam").mark("*")'
-
-or:
-
- # PATH=/path/to/beam:$PATH stap -L 'process("beam").mark("*")'
-
-
-Probes in the dtrace.so NIF library like this:
-
- # PATH=/path/to/dtrace/priv/lib:$PATH stap -L 'process("dtrace.so").mark("*")'
-
-Running SystemTap scripts
--------------------------
-
-Adjust the process("beam") reference to your beam version and attach the script
-to a running "beam" instance:
-
- # stap /path/to/probe/script/port1.systemtap -x <pid of beam>
diff --git a/TAR.include b/TAR.include
index 5146458eea..87854d24c7 100644
--- a/TAR.include
+++ b/TAR.include
@@ -2,10 +2,10 @@ AUTHORS
EPLICENCE
Makefile.in
README.md
-README.md.txt
-INSTALL.md
-INSTALL-CROSS.md
-INSTALL-WIN32.md
+HOWTO/MARKDOWN.md
+HOWTO/INSTALL.md
+HOWTO/INSTALL-CROSS.md
+HOWTO/INSTALL-WIN32.md
configure.in
aclocal.m4
otp_build
diff --git a/aclocal.m4 b/aclocal.m4
index 339a15a2bb..918e30a886 100644
--- a/aclocal.m4
+++ b/aclocal.m4
@@ -1,7 +1,7 @@
dnl
dnl %CopyrightBegin%
dnl
-dnl Copyright Ericsson AB 1998-2011. All Rights Reserved.
+dnl Copyright Ericsson AB 1998-2012. All Rights Reserved.
dnl
dnl The contents of this file are subject to the Erlang Public License,
dnl Version 1.1, (the "License"); you may not use this file except in
@@ -59,6 +59,7 @@ AC_ARG_VAR(erl_xcomp_isysroot, [Absolute cross system root include path (only us
dnl Cross compilation variables
AC_ARG_VAR(erl_xcomp_bigendian, [big endian system: yes|no (only used when cross compiling)])
+AC_ARG_VAR(erl_xcomp_double_middle_endian, [double-middle-endian system: yes|no (only used when cross compiling)])
AC_ARG_VAR(erl_xcomp_linux_clock_gettime_correction, [clock_gettime() can be used for time correction: yes|no (only used when cross compiling)])
AC_ARG_VAR(erl_xcomp_linux_nptl, [have Native POSIX Thread Library: yes|no (only used when cross compiling)])
AC_ARG_VAR(erl_xcomp_linux_usable_sigusrx, [SIGUSR1 and SIGUSR2 can be used: yes|no (only used when cross compiling)])
@@ -606,6 +607,103 @@ ifelse([$5], , , [$5
fi
])
+dnl ----------------------------------------------------------------------
+dnl
+dnl AC_DOUBLE_MIDDLE_ENDIAN
+dnl
+dnl Checks whether doubles are represented in "middle-endian" format.
+dnl Sets ac_cv_double_middle_endian={no,yes,unknown} accordingly,
+dnl as well as DOUBLE_MIDDLE_ENDIAN.
+dnl
+dnl
+
+AC_DEFUN([AC_C_DOUBLE_MIDDLE_ENDIAN],
+[AC_CACHE_CHECK(whether double word ordering is middle-endian, ac_cv_c_double_middle_endian,
+[# It does not; compile a test program.
+AC_RUN_IFELSE(
+[AC_LANG_SOURCE([[#include <stdlib.h>
+
+int
+main(void)
+{
+ int i = 0;
+ int zero = 0;
+ int bigendian;
+ int zero_index = 0;
+
+ union
+ {
+ long int l;
+ char c[sizeof (long int)];
+ } u;
+
+ /* we'll use the one with 32-bit words */
+ union
+ {
+ double d;
+ unsigned int c[2];
+ } vint;
+
+ union
+ {
+ double d;
+ unsigned long c[2];
+ } vlong;
+
+ union
+ {
+ double d;
+ unsigned short c[2];
+ } vshort;
+
+
+ /* Are we little or big endian? From Harbison&Steele. */
+ u.l = 1;
+ bigendian = (u.c[sizeof (long int) - 1] == 1);
+
+ zero_index = bigendian ? 1 : 0;
+
+ vint.d = 1.0;
+ vlong.d = 1.0;
+ vshort.d = 1.0;
+
+ if (sizeof(unsigned int) == 4)
+ {
+ if (vint.c[zero_index] != 0)
+ zero = 1;
+ }
+ else if (sizeof(unsigned long) == 4)
+ {
+ if (vlong.c[zero_index] != 0)
+ zero = 1;
+ }
+ else if (sizeof(unsigned short) == 4)
+ {
+ if (vshort.c[zero_index] != 0)
+ zero = 1;
+ }
+
+ exit (zero);
+}
+]])],
+ [ac_cv_c_double_middle_endian=no],
+ [ac_cv_c_double_middle_endian=yes],
+ [ac_cv_c_double_middle=unknown])])
+case $ac_cv_c_double_middle_endian in
+ yes)
+ m4_default([$1],
+ [AC_DEFINE([DOUBLE_MIDDLE_ENDIAN], 1,
+ [Define to 1 if your processor stores the words in a double in
+ middle-endian format (like some ARMs).])]) ;;
+ no)
+ $2 ;;
+ *)
+ m4_default([$3],
+ [AC_MSG_WARN([unknown double endianness
+presetting ac_cv_c_double_middle_endian=no (or yes) will help])]) ;;
+esac
+])# AC_C_DOUBLE_MIDDLE_ENDIAN
+
dnl ----------------------------------------------------------------------
dnl
@@ -642,11 +740,16 @@ dnl Try to find POSIX threads
dnl The usual pthread lib...
AC_CHECK_LIB(pthread, pthread_create, THR_LIBS="-lpthread")
-dnl FreeBSD has pthreads in special c library, c_r...
+dnl Very old versions of FreeBSD have pthreads in special c library, c_r...
if test "x$THR_LIBS" = "x"; then
AC_CHECK_LIB(c_r, pthread_create, THR_LIBS="-lc_r")
fi
+dnl QNX has pthreads in standard C library
+ if test "x$THR_LIBS" = "x"; then
+ AC_CHECK_FUNC(pthread_create, THR_LIBS="none_needed")
+ fi
+
dnl On ofs1 the '-pthread' switch should be used
if test "x$THR_LIBS" = "x"; then
AC_MSG_CHECKING([if the '-pthread' switch can be used])
@@ -667,6 +770,9 @@ dnl On ofs1 the '-pthread' switch should be used
if test "x$THR_LIBS" != "x"; then
THR_DEFS="$THR_DEFS -D_THREAD_SAFE -D_REENTRANT -DPOSIX_THREADS"
THR_LIB_NAME=pthread
+ if test "x$THR_LIBS" = "xnone_needed"; then
+ THR_LIBS=
+ fi
case $host_os in
solaris*)
THR_DEFS="$THR_DEFS -D_POSIX_PTHREAD_SEMANTICS" ;;
@@ -1337,6 +1443,14 @@ if test "$ac_cv_c_bigendian" = "yes"; then
AC_DEFINE(ETHR_BIGENDIAN, 1, [Define if bigendian])
fi
+case X$erl_xcomp_double_middle_endian in
+ X) ;;
+ Xyes|Xno|Xunknown) ac_cv_c_double_middle_endian=$erl_xcomp_double_middle_endian;;
+ *) AC_MSG_ERROR([Bad erl_xcomp_double_middle_endian value: $erl_xcomp_double_middle_endian]);;
+esac
+
+AC_C_DOUBLE_MIDDLE_ENDIAN
+
AC_ARG_ENABLE(native-ethr-impls,
AS_HELP_STRING([--disable-native-ethr-impls],
[disable native ethread implementations]),
@@ -1735,6 +1849,31 @@ case $erl_gethrvtime in
esac
])dnl
+dnl ----------------------------------------------------------------------
+dnl
+dnl LM_TRY_ENABLE_CFLAG
+dnl
+dnl
+dnl Tries a CFLAG and sees if it can be enabled without compiler errors
+dnl $1: textual cflag to add
+dnl $2: variable to store the modified CFLAG in
+dnl Usage example LM_TRY_ENABLE_CFLAG([-Werror=return-type], [CFLAGS])
+dnl
+dnl
+AC_DEFUN([LM_TRY_ENABLE_CFLAG], [
+ AC_MSG_CHECKING([if we can add $1 to $2 (via CFLAGS)])
+ saved_CFLAGS=$CFLAGS;
+ CFLAGS="$1 $$2";
+ AC_TRY_COMPILE([],[return 0;],can_enable_flag=true,can_enable_flag=false)
+ CFLAGS=$saved_CFLAGS;
+ if test "X$can_enable_flag" = "Xtrue"; then
+ AC_MSG_RESULT([yes])
+ AS_VAR_SET($2, "$1 $$2")
+ else
+ AC_MSG_RESULT([no])
+ fi
+])
+
dnl ERL_TRY_LINK_JAVA(CLASSES, FUNCTION-BODY
dnl [ACTION_IF_FOUND [, ACTION-IF-NOT-FOUND]])
dnl Freely inspired by AC_TRY_LINK. (Maybe better to create a
diff --git a/bootstrap/bin/start.boot b/bootstrap/bin/start.boot
index c6ebc852a2..a98b0eda7d 100644
--- a/bootstrap/bin/start.boot
+++ b/bootstrap/bin/start.boot
Binary files differ
diff --git a/bootstrap/bin/start_clean.boot b/bootstrap/bin/start_clean.boot
index c6ebc852a2..a98b0eda7d 100644
--- a/bootstrap/bin/start_clean.boot
+++ b/bootstrap/bin/start_clean.boot
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_a.beam b/bootstrap/lib/compiler/ebin/beam_a.beam
new file mode 100644
index 0000000000..fd53a2e0f9
--- /dev/null
+++ b/bootstrap/lib/compiler/ebin/beam_a.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_asm.beam b/bootstrap/lib/compiler/ebin/beam_asm.beam
index 7b1ee38ef8..fd36250580 100644
--- a/bootstrap/lib/compiler/ebin/beam_asm.beam
+++ b/bootstrap/lib/compiler/ebin/beam_asm.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_block.beam b/bootstrap/lib/compiler/ebin/beam_block.beam
index a7bb91adbb..72cce89b9d 100644
--- a/bootstrap/lib/compiler/ebin/beam_block.beam
+++ b/bootstrap/lib/compiler/ebin/beam_block.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_bool.beam b/bootstrap/lib/compiler/ebin/beam_bool.beam
index 252257b71d..18eb174687 100644
--- a/bootstrap/lib/compiler/ebin/beam_bool.beam
+++ b/bootstrap/lib/compiler/ebin/beam_bool.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_bsm.beam b/bootstrap/lib/compiler/ebin/beam_bsm.beam
index 0c998c8e8c..e0d595bea0 100644
--- a/bootstrap/lib/compiler/ebin/beam_bsm.beam
+++ b/bootstrap/lib/compiler/ebin/beam_bsm.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_clean.beam b/bootstrap/lib/compiler/ebin/beam_clean.beam
index 70523ca134..3fe3225c14 100644
--- a/bootstrap/lib/compiler/ebin/beam_clean.beam
+++ b/bootstrap/lib/compiler/ebin/beam_clean.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_dead.beam b/bootstrap/lib/compiler/ebin/beam_dead.beam
index 17cced3c1a..b664286f0f 100644
--- a/bootstrap/lib/compiler/ebin/beam_dead.beam
+++ b/bootstrap/lib/compiler/ebin/beam_dead.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_dict.beam b/bootstrap/lib/compiler/ebin/beam_dict.beam
index 81e62b0a7d..50771f4e90 100644
--- a/bootstrap/lib/compiler/ebin/beam_dict.beam
+++ b/bootstrap/lib/compiler/ebin/beam_dict.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_disasm.beam b/bootstrap/lib/compiler/ebin/beam_disasm.beam
index ec48b89de7..2ecba86d56 100644
--- a/bootstrap/lib/compiler/ebin/beam_disasm.beam
+++ b/bootstrap/lib/compiler/ebin/beam_disasm.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_except.beam b/bootstrap/lib/compiler/ebin/beam_except.beam
index 5bd28a35d9..e86cdce99e 100644
--- a/bootstrap/lib/compiler/ebin/beam_except.beam
+++ b/bootstrap/lib/compiler/ebin/beam_except.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_flatten.beam b/bootstrap/lib/compiler/ebin/beam_flatten.beam
index 0ac0dc7b92..8f06d7270c 100644
--- a/bootstrap/lib/compiler/ebin/beam_flatten.beam
+++ b/bootstrap/lib/compiler/ebin/beam_flatten.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_jump.beam b/bootstrap/lib/compiler/ebin/beam_jump.beam
index 4811360890..c296bdd18d 100644
--- a/bootstrap/lib/compiler/ebin/beam_jump.beam
+++ b/bootstrap/lib/compiler/ebin/beam_jump.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_opcodes.beam b/bootstrap/lib/compiler/ebin/beam_opcodes.beam
index 2c414f1c7d..8425c2b2b5 100644
--- a/bootstrap/lib/compiler/ebin/beam_opcodes.beam
+++ b/bootstrap/lib/compiler/ebin/beam_opcodes.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_peep.beam b/bootstrap/lib/compiler/ebin/beam_peep.beam
index aaba79046c..a21cc7e1e1 100644
--- a/bootstrap/lib/compiler/ebin/beam_peep.beam
+++ b/bootstrap/lib/compiler/ebin/beam_peep.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_receive.beam b/bootstrap/lib/compiler/ebin/beam_receive.beam
index 43f41f6535..95145224bf 100644
--- a/bootstrap/lib/compiler/ebin/beam_receive.beam
+++ b/bootstrap/lib/compiler/ebin/beam_receive.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_split.beam b/bootstrap/lib/compiler/ebin/beam_split.beam
index 296e6c4671..2271c15ee4 100644
--- a/bootstrap/lib/compiler/ebin/beam_split.beam
+++ b/bootstrap/lib/compiler/ebin/beam_split.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_trim.beam b/bootstrap/lib/compiler/ebin/beam_trim.beam
index 86f5d027f4..a497efa4cf 100644
--- a/bootstrap/lib/compiler/ebin/beam_trim.beam
+++ b/bootstrap/lib/compiler/ebin/beam_trim.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_type.beam b/bootstrap/lib/compiler/ebin/beam_type.beam
index 116da39f89..7b005ba802 100644
--- a/bootstrap/lib/compiler/ebin/beam_type.beam
+++ b/bootstrap/lib/compiler/ebin/beam_type.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_utils.beam b/bootstrap/lib/compiler/ebin/beam_utils.beam
index 9c6ad019f9..1aa160b998 100644
--- a/bootstrap/lib/compiler/ebin/beam_utils.beam
+++ b/bootstrap/lib/compiler/ebin/beam_utils.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_validator.beam b/bootstrap/lib/compiler/ebin/beam_validator.beam
index 52643de79a..d13cbe1944 100644
--- a/bootstrap/lib/compiler/ebin/beam_validator.beam
+++ b/bootstrap/lib/compiler/ebin/beam_validator.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_z.beam b/bootstrap/lib/compiler/ebin/beam_z.beam
new file mode 100644
index 0000000000..4d28344035
--- /dev/null
+++ b/bootstrap/lib/compiler/ebin/beam_z.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/cerl.beam b/bootstrap/lib/compiler/ebin/cerl.beam
index dcebe13bc8..a10f017869 100644
--- a/bootstrap/lib/compiler/ebin/cerl.beam
+++ b/bootstrap/lib/compiler/ebin/cerl.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/cerl_inline.beam b/bootstrap/lib/compiler/ebin/cerl_inline.beam
index eed98ecd2c..b195422469 100644
--- a/bootstrap/lib/compiler/ebin/cerl_inline.beam
+++ b/bootstrap/lib/compiler/ebin/cerl_inline.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/cerl_trees.beam b/bootstrap/lib/compiler/ebin/cerl_trees.beam
index df3f351c8d..49528c1635 100644
--- a/bootstrap/lib/compiler/ebin/cerl_trees.beam
+++ b/bootstrap/lib/compiler/ebin/cerl_trees.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/compile.beam b/bootstrap/lib/compiler/ebin/compile.beam
index e2bd3c4ce0..373c0afc0c 100644
--- a/bootstrap/lib/compiler/ebin/compile.beam
+++ b/bootstrap/lib/compiler/ebin/compile.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/compiler.app b/bootstrap/lib/compiler/ebin/compiler.app
index 5170231c59..733120aa9e 100644
--- a/bootstrap/lib/compiler/ebin/compiler.app
+++ b/bootstrap/lib/compiler/ebin/compiler.app
@@ -18,8 +18,9 @@
{application, compiler,
[{description, "ERTS CXC 138 10"},
- {vsn, "4.8.1"},
+ {vsn, "4.8.2"},
{modules, [
+ beam_a,
beam_asm,
beam_block,
beam_bool,
@@ -40,6 +41,7 @@
beam_type,
beam_utils,
beam_validator,
+ beam_z,
cerl,
cerl_clauses,
cerl_inline,
@@ -55,7 +57,6 @@
sys_core_dsetel,
sys_core_fold,
sys_core_inline,
- sys_expand_pmod,
sys_pre_attributes,
sys_pre_expand,
v3_codegen,
diff --git a/bootstrap/lib/compiler/ebin/core_lib.beam b/bootstrap/lib/compiler/ebin/core_lib.beam
index 1a60454b42..d03dfe646c 100644
--- a/bootstrap/lib/compiler/ebin/core_lib.beam
+++ b/bootstrap/lib/compiler/ebin/core_lib.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/core_lint.beam b/bootstrap/lib/compiler/ebin/core_lint.beam
index 08fef1eb71..bc18af0f7c 100644
--- a/bootstrap/lib/compiler/ebin/core_lint.beam
+++ b/bootstrap/lib/compiler/ebin/core_lint.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/core_parse.beam b/bootstrap/lib/compiler/ebin/core_parse.beam
index 4f555d8036..ac2a161ced 100644
--- a/bootstrap/lib/compiler/ebin/core_parse.beam
+++ b/bootstrap/lib/compiler/ebin/core_parse.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/core_pp.beam b/bootstrap/lib/compiler/ebin/core_pp.beam
index ef3504058a..e96e4bc654 100644
--- a/bootstrap/lib/compiler/ebin/core_pp.beam
+++ b/bootstrap/lib/compiler/ebin/core_pp.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/core_scan.beam b/bootstrap/lib/compiler/ebin/core_scan.beam
index 245e59483d..f14eda5a33 100644
--- a/bootstrap/lib/compiler/ebin/core_scan.beam
+++ b/bootstrap/lib/compiler/ebin/core_scan.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/erl_bifs.beam b/bootstrap/lib/compiler/ebin/erl_bifs.beam
index 9234a54428..815258e0ac 100644
--- a/bootstrap/lib/compiler/ebin/erl_bifs.beam
+++ b/bootstrap/lib/compiler/ebin/erl_bifs.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/rec_env.beam b/bootstrap/lib/compiler/ebin/rec_env.beam
index 91a86d9855..edabfdf444 100644
--- a/bootstrap/lib/compiler/ebin/rec_env.beam
+++ b/bootstrap/lib/compiler/ebin/rec_env.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/sys_core_dsetel.beam b/bootstrap/lib/compiler/ebin/sys_core_dsetel.beam
index 0764734cb1..11c194a5cc 100644
--- a/bootstrap/lib/compiler/ebin/sys_core_dsetel.beam
+++ b/bootstrap/lib/compiler/ebin/sys_core_dsetel.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/sys_core_fold.beam b/bootstrap/lib/compiler/ebin/sys_core_fold.beam
index c878868598..a4529cdd39 100644
--- a/bootstrap/lib/compiler/ebin/sys_core_fold.beam
+++ b/bootstrap/lib/compiler/ebin/sys_core_fold.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/sys_core_inline.beam b/bootstrap/lib/compiler/ebin/sys_core_inline.beam
index 907c9d53a5..b2762781e7 100644
--- a/bootstrap/lib/compiler/ebin/sys_core_inline.beam
+++ b/bootstrap/lib/compiler/ebin/sys_core_inline.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/sys_expand_pmod.beam b/bootstrap/lib/compiler/ebin/sys_expand_pmod.beam
deleted file mode 100644
index 1649b9f2ff..0000000000
--- a/bootstrap/lib/compiler/ebin/sys_expand_pmod.beam
+++ /dev/null
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/sys_pre_attributes.beam b/bootstrap/lib/compiler/ebin/sys_pre_attributes.beam
index a7ca15a033..d3b213e75a 100644
--- a/bootstrap/lib/compiler/ebin/sys_pre_attributes.beam
+++ b/bootstrap/lib/compiler/ebin/sys_pre_attributes.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/sys_pre_expand.beam b/bootstrap/lib/compiler/ebin/sys_pre_expand.beam
index f7e9541e4c..60933730c3 100644
--- a/bootstrap/lib/compiler/ebin/sys_pre_expand.beam
+++ b/bootstrap/lib/compiler/ebin/sys_pre_expand.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/v3_codegen.beam b/bootstrap/lib/compiler/ebin/v3_codegen.beam
index 12026564d9..8071b9b616 100644
--- a/bootstrap/lib/compiler/ebin/v3_codegen.beam
+++ b/bootstrap/lib/compiler/ebin/v3_codegen.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/v3_core.beam b/bootstrap/lib/compiler/ebin/v3_core.beam
index 84c005edc9..a51af4dd43 100644
--- a/bootstrap/lib/compiler/ebin/v3_core.beam
+++ b/bootstrap/lib/compiler/ebin/v3_core.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/v3_kernel.beam b/bootstrap/lib/compiler/ebin/v3_kernel.beam
index cd4bf48540..97a9a79260 100644
--- a/bootstrap/lib/compiler/ebin/v3_kernel.beam
+++ b/bootstrap/lib/compiler/ebin/v3_kernel.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/v3_kernel_pp.beam b/bootstrap/lib/compiler/ebin/v3_kernel_pp.beam
index 485aa63dcb..8f18f5ff25 100644
--- a/bootstrap/lib/compiler/ebin/v3_kernel_pp.beam
+++ b/bootstrap/lib/compiler/ebin/v3_kernel_pp.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/v3_life.beam b/bootstrap/lib/compiler/ebin/v3_life.beam
index aa8b6b227b..f4a45b2a22 100644
--- a/bootstrap/lib/compiler/ebin/v3_life.beam
+++ b/bootstrap/lib/compiler/ebin/v3_life.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/application.beam b/bootstrap/lib/kernel/ebin/application.beam
index 4485b9e1e8..3287c3f132 100644
--- a/bootstrap/lib/kernel/ebin/application.beam
+++ b/bootstrap/lib/kernel/ebin/application.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/application_controller.beam b/bootstrap/lib/kernel/ebin/application_controller.beam
index 9c94dbd5c7..28512de9f1 100644
--- a/bootstrap/lib/kernel/ebin/application_controller.beam
+++ b/bootstrap/lib/kernel/ebin/application_controller.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/application_master.beam b/bootstrap/lib/kernel/ebin/application_master.beam
index 4d1098bcec..e73de8b687 100644
--- a/bootstrap/lib/kernel/ebin/application_master.beam
+++ b/bootstrap/lib/kernel/ebin/application_master.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/application_starter.beam b/bootstrap/lib/kernel/ebin/application_starter.beam
index 14ff4dc088..06d8098af9 100644
--- a/bootstrap/lib/kernel/ebin/application_starter.beam
+++ b/bootstrap/lib/kernel/ebin/application_starter.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/auth.beam b/bootstrap/lib/kernel/ebin/auth.beam
index 33f1ab875b..0f0eb3437e 100644
--- a/bootstrap/lib/kernel/ebin/auth.beam
+++ b/bootstrap/lib/kernel/ebin/auth.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/code.beam b/bootstrap/lib/kernel/ebin/code.beam
index 6ba0d1b663..04150eee56 100644
--- a/bootstrap/lib/kernel/ebin/code.beam
+++ b/bootstrap/lib/kernel/ebin/code.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/code_server.beam b/bootstrap/lib/kernel/ebin/code_server.beam
index adc53aa5f3..cd96c87f99 100644
--- a/bootstrap/lib/kernel/ebin/code_server.beam
+++ b/bootstrap/lib/kernel/ebin/code_server.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/disk_log.beam b/bootstrap/lib/kernel/ebin/disk_log.beam
index fa221e7c9d..6573609386 100644
--- a/bootstrap/lib/kernel/ebin/disk_log.beam
+++ b/bootstrap/lib/kernel/ebin/disk_log.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/disk_log_1.beam b/bootstrap/lib/kernel/ebin/disk_log_1.beam
index 1deb5ad05e..d851460d38 100644
--- a/bootstrap/lib/kernel/ebin/disk_log_1.beam
+++ b/bootstrap/lib/kernel/ebin/disk_log_1.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/disk_log_server.beam b/bootstrap/lib/kernel/ebin/disk_log_server.beam
index d985f64937..81b76167a5 100644
--- a/bootstrap/lib/kernel/ebin/disk_log_server.beam
+++ b/bootstrap/lib/kernel/ebin/disk_log_server.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/dist_ac.beam b/bootstrap/lib/kernel/ebin/dist_ac.beam
index 3eae6b82a7..b8c133313b 100644
--- a/bootstrap/lib/kernel/ebin/dist_ac.beam
+++ b/bootstrap/lib/kernel/ebin/dist_ac.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/dist_util.beam b/bootstrap/lib/kernel/ebin/dist_util.beam
index 7fc3325da4..8e07296a49 100644
--- a/bootstrap/lib/kernel/ebin/dist_util.beam
+++ b/bootstrap/lib/kernel/ebin/dist_util.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/erl_boot_server.beam b/bootstrap/lib/kernel/ebin/erl_boot_server.beam
index 19f6ceeda2..47563a5d81 100644
--- a/bootstrap/lib/kernel/ebin/erl_boot_server.beam
+++ b/bootstrap/lib/kernel/ebin/erl_boot_server.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/erl_ddll.beam b/bootstrap/lib/kernel/ebin/erl_ddll.beam
index 4fdec24b11..776c36e739 100644
--- a/bootstrap/lib/kernel/ebin/erl_ddll.beam
+++ b/bootstrap/lib/kernel/ebin/erl_ddll.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/erl_distribution.beam b/bootstrap/lib/kernel/ebin/erl_distribution.beam
index b084e63a0d..9a6acc0585 100644
--- a/bootstrap/lib/kernel/ebin/erl_distribution.beam
+++ b/bootstrap/lib/kernel/ebin/erl_distribution.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/erl_epmd.beam b/bootstrap/lib/kernel/ebin/erl_epmd.beam
index 8ae4bbaf14..0eaecd949a 100644
--- a/bootstrap/lib/kernel/ebin/erl_epmd.beam
+++ b/bootstrap/lib/kernel/ebin/erl_epmd.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/error_handler.beam b/bootstrap/lib/kernel/ebin/error_handler.beam
index 6d56c98c67..740ba28f72 100644
--- a/bootstrap/lib/kernel/ebin/error_handler.beam
+++ b/bootstrap/lib/kernel/ebin/error_handler.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/erts_debug.beam b/bootstrap/lib/kernel/ebin/erts_debug.beam
index 5c6483e368..4dda8d492c 100644
--- a/bootstrap/lib/kernel/ebin/erts_debug.beam
+++ b/bootstrap/lib/kernel/ebin/erts_debug.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/file.beam b/bootstrap/lib/kernel/ebin/file.beam
index 243be75c43..b66197b778 100644
--- a/bootstrap/lib/kernel/ebin/file.beam
+++ b/bootstrap/lib/kernel/ebin/file.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/file_io_server.beam b/bootstrap/lib/kernel/ebin/file_io_server.beam
index b8e2df2a2d..2c617ae7e4 100644
--- a/bootstrap/lib/kernel/ebin/file_io_server.beam
+++ b/bootstrap/lib/kernel/ebin/file_io_server.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/file_server.beam b/bootstrap/lib/kernel/ebin/file_server.beam
index fd9a9924bb..32c6a94a25 100644
--- a/bootstrap/lib/kernel/ebin/file_server.beam
+++ b/bootstrap/lib/kernel/ebin/file_server.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/gen_sctp.beam b/bootstrap/lib/kernel/ebin/gen_sctp.beam
index 3092353498..1f33d85cb2 100644
--- a/bootstrap/lib/kernel/ebin/gen_sctp.beam
+++ b/bootstrap/lib/kernel/ebin/gen_sctp.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/gen_tcp.beam b/bootstrap/lib/kernel/ebin/gen_tcp.beam
index 02103cc215..0182e11345 100644
--- a/bootstrap/lib/kernel/ebin/gen_tcp.beam
+++ b/bootstrap/lib/kernel/ebin/gen_tcp.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/gen_udp.beam b/bootstrap/lib/kernel/ebin/gen_udp.beam
index 3b9ca85608..e49561399d 100644
--- a/bootstrap/lib/kernel/ebin/gen_udp.beam
+++ b/bootstrap/lib/kernel/ebin/gen_udp.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/global.beam b/bootstrap/lib/kernel/ebin/global.beam
index 96d7365006..e9f463867e 100644
--- a/bootstrap/lib/kernel/ebin/global.beam
+++ b/bootstrap/lib/kernel/ebin/global.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/global_group.beam b/bootstrap/lib/kernel/ebin/global_group.beam
index dd7d0e429e..e989448fae 100644
--- a/bootstrap/lib/kernel/ebin/global_group.beam
+++ b/bootstrap/lib/kernel/ebin/global_group.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/global_search.beam b/bootstrap/lib/kernel/ebin/global_search.beam
index aa70042058..0d6f105e5f 100644
--- a/bootstrap/lib/kernel/ebin/global_search.beam
+++ b/bootstrap/lib/kernel/ebin/global_search.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/group.beam b/bootstrap/lib/kernel/ebin/group.beam
index 8e813f6964..4338bb4687 100644
--- a/bootstrap/lib/kernel/ebin/group.beam
+++ b/bootstrap/lib/kernel/ebin/group.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/heart.beam b/bootstrap/lib/kernel/ebin/heart.beam
index b20f872241..7e81d1610a 100644
--- a/bootstrap/lib/kernel/ebin/heart.beam
+++ b/bootstrap/lib/kernel/ebin/heart.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/hipe_unified_loader.beam b/bootstrap/lib/kernel/ebin/hipe_unified_loader.beam
index 1574686ba6..5b41832dec 100644
--- a/bootstrap/lib/kernel/ebin/hipe_unified_loader.beam
+++ b/bootstrap/lib/kernel/ebin/hipe_unified_loader.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet.beam b/bootstrap/lib/kernel/ebin/inet.beam
index ddb1f2a24b..88a97e0ec2 100644
--- a/bootstrap/lib/kernel/ebin/inet.beam
+++ b/bootstrap/lib/kernel/ebin/inet.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet6_tcp.beam b/bootstrap/lib/kernel/ebin/inet6_tcp.beam
index 4c80d1d71e..dc592e336c 100644
--- a/bootstrap/lib/kernel/ebin/inet6_tcp.beam
+++ b/bootstrap/lib/kernel/ebin/inet6_tcp.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet6_tcp_dist.beam b/bootstrap/lib/kernel/ebin/inet6_tcp_dist.beam
index 1bc785fa37..dd2d62b8a0 100644
--- a/bootstrap/lib/kernel/ebin/inet6_tcp_dist.beam
+++ b/bootstrap/lib/kernel/ebin/inet6_tcp_dist.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet6_udp.beam b/bootstrap/lib/kernel/ebin/inet6_udp.beam
index 736c0157e5..2c94263b99 100644
--- a/bootstrap/lib/kernel/ebin/inet6_udp.beam
+++ b/bootstrap/lib/kernel/ebin/inet6_udp.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet_config.beam b/bootstrap/lib/kernel/ebin/inet_config.beam
index fc1e2c8387..9479212ed0 100644
--- a/bootstrap/lib/kernel/ebin/inet_config.beam
+++ b/bootstrap/lib/kernel/ebin/inet_config.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet_db.beam b/bootstrap/lib/kernel/ebin/inet_db.beam
index 555c8c307e..4fd0b25503 100644
--- a/bootstrap/lib/kernel/ebin/inet_db.beam
+++ b/bootstrap/lib/kernel/ebin/inet_db.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet_dns.beam b/bootstrap/lib/kernel/ebin/inet_dns.beam
index 7f262cf20d..07b4356f7b 100644
--- a/bootstrap/lib/kernel/ebin/inet_dns.beam
+++ b/bootstrap/lib/kernel/ebin/inet_dns.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet_gethost_native.beam b/bootstrap/lib/kernel/ebin/inet_gethost_native.beam
index 4e6ce0fc5e..47aa6e05f0 100644
--- a/bootstrap/lib/kernel/ebin/inet_gethost_native.beam
+++ b/bootstrap/lib/kernel/ebin/inet_gethost_native.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet_parse.beam b/bootstrap/lib/kernel/ebin/inet_parse.beam
index 14ca272933..1b92542df2 100644
--- a/bootstrap/lib/kernel/ebin/inet_parse.beam
+++ b/bootstrap/lib/kernel/ebin/inet_parse.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet_res.beam b/bootstrap/lib/kernel/ebin/inet_res.beam
index 9e8e9fa4de..bef3efd11b 100644
--- a/bootstrap/lib/kernel/ebin/inet_res.beam
+++ b/bootstrap/lib/kernel/ebin/inet_res.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet_tcp.beam b/bootstrap/lib/kernel/ebin/inet_tcp.beam
index d16795f44d..00a8175665 100644
--- a/bootstrap/lib/kernel/ebin/inet_tcp.beam
+++ b/bootstrap/lib/kernel/ebin/inet_tcp.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet_tcp_dist.beam b/bootstrap/lib/kernel/ebin/inet_tcp_dist.beam
index 22f2db4182..39389454f9 100644
--- a/bootstrap/lib/kernel/ebin/inet_tcp_dist.beam
+++ b/bootstrap/lib/kernel/ebin/inet_tcp_dist.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet_udp.beam b/bootstrap/lib/kernel/ebin/inet_udp.beam
index 917e3da3a7..6fb98bd777 100644
--- a/bootstrap/lib/kernel/ebin/inet_udp.beam
+++ b/bootstrap/lib/kernel/ebin/inet_udp.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/kernel.app b/bootstrap/lib/kernel/ebin/kernel.app
index 48f6250828..ca2dc4f7c5 100644
--- a/bootstrap/lib/kernel/ebin/kernel.app
+++ b/bootstrap/lib/kernel/ebin/kernel.app
@@ -28,7 +28,6 @@
application_starter,
auth,
code,
- packages,
code_server,
dist_util,
erl_boot_server,
diff --git a/bootstrap/lib/kernel/ebin/kernel.beam b/bootstrap/lib/kernel/ebin/kernel.beam
index 8321a999c1..7f6d8fcb30 100644
--- a/bootstrap/lib/kernel/ebin/kernel.beam
+++ b/bootstrap/lib/kernel/ebin/kernel.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/kernel_config.beam b/bootstrap/lib/kernel/ebin/kernel_config.beam
index c5a63b6217..848401eacf 100644
--- a/bootstrap/lib/kernel/ebin/kernel_config.beam
+++ b/bootstrap/lib/kernel/ebin/kernel_config.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/net_adm.beam b/bootstrap/lib/kernel/ebin/net_adm.beam
index 182c2cd0fc..dc7e091cf2 100644
--- a/bootstrap/lib/kernel/ebin/net_adm.beam
+++ b/bootstrap/lib/kernel/ebin/net_adm.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/net_kernel.beam b/bootstrap/lib/kernel/ebin/net_kernel.beam
index 9eebd72403..ebb9ed19d5 100644
--- a/bootstrap/lib/kernel/ebin/net_kernel.beam
+++ b/bootstrap/lib/kernel/ebin/net_kernel.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/os.beam b/bootstrap/lib/kernel/ebin/os.beam
index 1474e8f0d6..8e13b88768 100644
--- a/bootstrap/lib/kernel/ebin/os.beam
+++ b/bootstrap/lib/kernel/ebin/os.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/packages.beam b/bootstrap/lib/kernel/ebin/packages.beam
deleted file mode 100644
index 511c6a6e74..0000000000
--- a/bootstrap/lib/kernel/ebin/packages.beam
+++ /dev/null
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/pg2.beam b/bootstrap/lib/kernel/ebin/pg2.beam
index 439bbc24f2..02b9264e22 100644
--- a/bootstrap/lib/kernel/ebin/pg2.beam
+++ b/bootstrap/lib/kernel/ebin/pg2.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/ram_file.beam b/bootstrap/lib/kernel/ebin/ram_file.beam
index 02baece827..74fcc898c3 100644
--- a/bootstrap/lib/kernel/ebin/ram_file.beam
+++ b/bootstrap/lib/kernel/ebin/ram_file.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/rpc.beam b/bootstrap/lib/kernel/ebin/rpc.beam
index 4355f300e9..305779539a 100644
--- a/bootstrap/lib/kernel/ebin/rpc.beam
+++ b/bootstrap/lib/kernel/ebin/rpc.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/seq_trace.beam b/bootstrap/lib/kernel/ebin/seq_trace.beam
index ceb4e14324..13489135a2 100644
--- a/bootstrap/lib/kernel/ebin/seq_trace.beam
+++ b/bootstrap/lib/kernel/ebin/seq_trace.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/standard_error.beam b/bootstrap/lib/kernel/ebin/standard_error.beam
index f34c1a9bd1..b5b22bb71a 100644
--- a/bootstrap/lib/kernel/ebin/standard_error.beam
+++ b/bootstrap/lib/kernel/ebin/standard_error.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/user.beam b/bootstrap/lib/kernel/ebin/user.beam
index f3aae4a980..7b761283a0 100644
--- a/bootstrap/lib/kernel/ebin/user.beam
+++ b/bootstrap/lib/kernel/ebin/user.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/wrap_log_reader.beam b/bootstrap/lib/kernel/ebin/wrap_log_reader.beam
index 1af9de5e72..7a328be682 100644
--- a/bootstrap/lib/kernel/ebin/wrap_log_reader.beam
+++ b/bootstrap/lib/kernel/ebin/wrap_log_reader.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/include/file.hrl b/bootstrap/lib/kernel/include/file.hrl
index bf97173122..69aec1ee36 100644
--- a/bootstrap/lib/kernel/include/file.hrl
+++ b/bootstrap/lib/kernel/include/file.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -22,32 +22,37 @@
%%--------------------------------------------------------------------------
-record(file_info,
- {size :: non_neg_integer(), % Size of file in bytes.
- type :: 'device' | 'directory' | 'other' | 'regular' | 'symlink',
- access :: 'read' | 'write' | 'read_write' | 'none',
- atime :: file:date_time() | integer(), % The local time the file was last read:
- % {{Year, Mon, Day}, {Hour, Min, Sec}}.
- % atime, ctime, mtime may also be unix epochs()
- mtime :: file:date_time() | integer(), % The local time the file was last written.
- ctime :: file:date_time() | integer(), % The interpretation of this time field
- % is dependent on operating system.
- % On Unix it is the last time the file
- % or the inode was changed. On Windows,
- % it is the creation time.
- mode :: integer(), % File permissions. On Windows,
- % the owner permissions will be
- % duplicated for group and user.
- links :: non_neg_integer(), % Number of links to the file (1 if the
- % filesystem doesn't support links).
- major_device :: integer(), % Identifies the file system (Unix),
- % or the drive number (A: = 0, B: = 1)
- % (Windows).
- %% The following are Unix specific.
- %% They are set to zero on other operating systems.
- minor_device :: integer(), % Only valid for devices.
- inode :: integer(), % Inode number for file.
- uid :: integer(), % User id for owner.
- gid :: integer()}). % Group id for owner.
+ {size :: non_neg_integer(), % Size of file in bytes.
+ type :: 'device' | 'directory' | 'other' | 'regular' | 'symlink',
+ access :: 'read' | 'write' | 'read_write' | 'none',
+ atime :: file:date_time() | non_neg_integer(),
+ % The local time the file was last read:
+ % {{Year, Mon, Day}, {Hour, Min, Sec}}.
+ % atime, ctime, mtime may also be unix epochs()
+ mtime :: file:date_time() | non_neg_integer(),
+ % The local time the file was last written.
+ ctime :: file:date_time() | non_neg_integer(),
+ % The interpretation of this time field
+ % is dependent on operating system.
+ % On Unix it is the last time the file
+ % or the inode was changed. On Windows,
+ % it is the creation time.
+ mode :: non_neg_integer(), % File permissions. On Windows,
+ % the owner permissions will be
+ % duplicated for group and user.
+ links :: non_neg_integer(),
+ % Number of links to the file (1 if the
+ % filesystem doesn't support links).
+ major_device :: non_neg_integer(),
+ % Identifies the file system (Unix),
+ % or the drive number (A: = 0, B: = 1)
+ % (Windows).
+ %% The following are Unix specific.
+ %% They are set to zero on other operating systems.
+ minor_device :: non_neg_integer(), % Only valid for devices.
+ inode :: non_neg_integer(), % Inode number for file.
+ uid :: non_neg_integer(), % User id for owner.
+ gid :: non_neg_integer()}). % Group id for owner.
-record(file_descriptor,
diff --git a/bootstrap/lib/stdlib/ebin/array.beam b/bootstrap/lib/stdlib/ebin/array.beam
index 8741e4d5df..28d04dea2c 100644
--- a/bootstrap/lib/stdlib/ebin/array.beam
+++ b/bootstrap/lib/stdlib/ebin/array.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/base64.beam b/bootstrap/lib/stdlib/ebin/base64.beam
index 07877625dd..2ca9a4d0b8 100644
--- a/bootstrap/lib/stdlib/ebin/base64.beam
+++ b/bootstrap/lib/stdlib/ebin/base64.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/beam_lib.beam b/bootstrap/lib/stdlib/ebin/beam_lib.beam
index afe3bcc466..c7ebc3b440 100644
--- a/bootstrap/lib/stdlib/ebin/beam_lib.beam
+++ b/bootstrap/lib/stdlib/ebin/beam_lib.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/binary.beam b/bootstrap/lib/stdlib/ebin/binary.beam
index 255c50aa01..6077d965ed 100644
--- a/bootstrap/lib/stdlib/ebin/binary.beam
+++ b/bootstrap/lib/stdlib/ebin/binary.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/c.beam b/bootstrap/lib/stdlib/ebin/c.beam
index 8c69197cbc..d919fa0a2d 100644
--- a/bootstrap/lib/stdlib/ebin/c.beam
+++ b/bootstrap/lib/stdlib/ebin/c.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/dets.beam b/bootstrap/lib/stdlib/ebin/dets.beam
index 4a33312cc0..48ed694380 100644
--- a/bootstrap/lib/stdlib/ebin/dets.beam
+++ b/bootstrap/lib/stdlib/ebin/dets.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/dets_server.beam b/bootstrap/lib/stdlib/ebin/dets_server.beam
index 26de164bd9..871104a73d 100644
--- a/bootstrap/lib/stdlib/ebin/dets_server.beam
+++ b/bootstrap/lib/stdlib/ebin/dets_server.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/dets_utils.beam b/bootstrap/lib/stdlib/ebin/dets_utils.beam
index e4cd1e8b2e..06ace1f82d 100644
--- a/bootstrap/lib/stdlib/ebin/dets_utils.beam
+++ b/bootstrap/lib/stdlib/ebin/dets_utils.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/dets_v8.beam b/bootstrap/lib/stdlib/ebin/dets_v8.beam
index c8e1549256..e580e2f1ea 100644
--- a/bootstrap/lib/stdlib/ebin/dets_v8.beam
+++ b/bootstrap/lib/stdlib/ebin/dets_v8.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/dets_v9.beam b/bootstrap/lib/stdlib/ebin/dets_v9.beam
index 8cfcb9f8f1..4d383902cd 100644
--- a/bootstrap/lib/stdlib/ebin/dets_v9.beam
+++ b/bootstrap/lib/stdlib/ebin/dets_v9.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/dict.beam b/bootstrap/lib/stdlib/ebin/dict.beam
index 35e67d6c59..83d30876cc 100644
--- a/bootstrap/lib/stdlib/ebin/dict.beam
+++ b/bootstrap/lib/stdlib/ebin/dict.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/digraph.beam b/bootstrap/lib/stdlib/ebin/digraph.beam
index 2c1788668d..82735d07cc 100644
--- a/bootstrap/lib/stdlib/ebin/digraph.beam
+++ b/bootstrap/lib/stdlib/ebin/digraph.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/digraph_utils.beam b/bootstrap/lib/stdlib/ebin/digraph_utils.beam
index 39cb9d90da..9bab801654 100644
--- a/bootstrap/lib/stdlib/ebin/digraph_utils.beam
+++ b/bootstrap/lib/stdlib/ebin/digraph_utils.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/edlin.beam b/bootstrap/lib/stdlib/ebin/edlin.beam
index 874bc4e107..bb586c5f6a 100644
--- a/bootstrap/lib/stdlib/ebin/edlin.beam
+++ b/bootstrap/lib/stdlib/ebin/edlin.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/epp.beam b/bootstrap/lib/stdlib/ebin/epp.beam
index 55f5adc24d..168bd3e878 100644
--- a/bootstrap/lib/stdlib/ebin/epp.beam
+++ b/bootstrap/lib/stdlib/ebin/epp.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_bits.beam b/bootstrap/lib/stdlib/ebin/erl_bits.beam
index 595be6a76a..7149491121 100644
--- a/bootstrap/lib/stdlib/ebin/erl_bits.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_bits.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_compile.beam b/bootstrap/lib/stdlib/ebin/erl_compile.beam
index e449884f0b..c2757faf82 100644
--- a/bootstrap/lib/stdlib/ebin/erl_compile.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_compile.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_eval.beam b/bootstrap/lib/stdlib/ebin/erl_eval.beam
index 6198c4f43a..1445449c6e 100644
--- a/bootstrap/lib/stdlib/ebin/erl_eval.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_eval.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_expand_records.beam b/bootstrap/lib/stdlib/ebin/erl_expand_records.beam
index 5c9663cd54..764f624cab 100644
--- a/bootstrap/lib/stdlib/ebin/erl_expand_records.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_expand_records.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_internal.beam b/bootstrap/lib/stdlib/ebin/erl_internal.beam
index 94a0a75c29..d557c6c5d7 100644
--- a/bootstrap/lib/stdlib/ebin/erl_internal.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_internal.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_lint.beam b/bootstrap/lib/stdlib/ebin/erl_lint.beam
index 798924c19b..b387a2c62c 100644
--- a/bootstrap/lib/stdlib/ebin/erl_lint.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_lint.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_parse.beam b/bootstrap/lib/stdlib/ebin/erl_parse.beam
index 85fa455908..ee3064f9e1 100644
--- a/bootstrap/lib/stdlib/ebin/erl_parse.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_parse.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_pp.beam b/bootstrap/lib/stdlib/ebin/erl_pp.beam
index 2c32b68322..52759b0ea5 100644
--- a/bootstrap/lib/stdlib/ebin/erl_pp.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_pp.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_scan.beam b/bootstrap/lib/stdlib/ebin/erl_scan.beam
index 9e70ed846e..56d710de2a 100644
--- a/bootstrap/lib/stdlib/ebin/erl_scan.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_scan.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_tar.beam b/bootstrap/lib/stdlib/ebin/erl_tar.beam
index 7eacf3272c..89ee92fddb 100644
--- a/bootstrap/lib/stdlib/ebin/erl_tar.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_tar.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/escript.beam b/bootstrap/lib/stdlib/ebin/escript.beam
index 17881e2552..f8f3145066 100644
--- a/bootstrap/lib/stdlib/ebin/escript.beam
+++ b/bootstrap/lib/stdlib/ebin/escript.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/ets.beam b/bootstrap/lib/stdlib/ebin/ets.beam
index cc6521891b..7d0554106e 100644
--- a/bootstrap/lib/stdlib/ebin/ets.beam
+++ b/bootstrap/lib/stdlib/ebin/ets.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/eval_bits.beam b/bootstrap/lib/stdlib/ebin/eval_bits.beam
index 73e20d9a8d..379fc3655f 100644
--- a/bootstrap/lib/stdlib/ebin/eval_bits.beam
+++ b/bootstrap/lib/stdlib/ebin/eval_bits.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/file_sorter.beam b/bootstrap/lib/stdlib/ebin/file_sorter.beam
index 633d8acd10..180054c091 100644
--- a/bootstrap/lib/stdlib/ebin/file_sorter.beam
+++ b/bootstrap/lib/stdlib/ebin/file_sorter.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/filelib.beam b/bootstrap/lib/stdlib/ebin/filelib.beam
index 20f48fe18e..65f45632ac 100644
--- a/bootstrap/lib/stdlib/ebin/filelib.beam
+++ b/bootstrap/lib/stdlib/ebin/filelib.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/filename.beam b/bootstrap/lib/stdlib/ebin/filename.beam
index e371f3f478..de676f2bed 100644
--- a/bootstrap/lib/stdlib/ebin/filename.beam
+++ b/bootstrap/lib/stdlib/ebin/filename.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/gb_sets.beam b/bootstrap/lib/stdlib/ebin/gb_sets.beam
index dc4c752654..10b584677d 100644
--- a/bootstrap/lib/stdlib/ebin/gb_sets.beam
+++ b/bootstrap/lib/stdlib/ebin/gb_sets.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/gb_trees.beam b/bootstrap/lib/stdlib/ebin/gb_trees.beam
index edf1805440..24aab57544 100644
--- a/bootstrap/lib/stdlib/ebin/gb_trees.beam
+++ b/bootstrap/lib/stdlib/ebin/gb_trees.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/gen.beam b/bootstrap/lib/stdlib/ebin/gen.beam
index 0a39dc1c7e..77d0bf806a 100644
--- a/bootstrap/lib/stdlib/ebin/gen.beam
+++ b/bootstrap/lib/stdlib/ebin/gen.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/gen_event.beam b/bootstrap/lib/stdlib/ebin/gen_event.beam
index 23c4f53c30..37b6753300 100644
--- a/bootstrap/lib/stdlib/ebin/gen_event.beam
+++ b/bootstrap/lib/stdlib/ebin/gen_event.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/gen_fsm.beam b/bootstrap/lib/stdlib/ebin/gen_fsm.beam
index c2713544ff..1fcabac0d7 100644
--- a/bootstrap/lib/stdlib/ebin/gen_fsm.beam
+++ b/bootstrap/lib/stdlib/ebin/gen_fsm.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/gen_server.beam b/bootstrap/lib/stdlib/ebin/gen_server.beam
index 3fa7d89db4..f0fa4f03a9 100644
--- a/bootstrap/lib/stdlib/ebin/gen_server.beam
+++ b/bootstrap/lib/stdlib/ebin/gen_server.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/io.beam b/bootstrap/lib/stdlib/ebin/io.beam
index 0a2ae3490c..ddfcca41e8 100644
--- a/bootstrap/lib/stdlib/ebin/io.beam
+++ b/bootstrap/lib/stdlib/ebin/io.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/io_lib.beam b/bootstrap/lib/stdlib/ebin/io_lib.beam
index b240aa157d..9420ea0f53 100644
--- a/bootstrap/lib/stdlib/ebin/io_lib.beam
+++ b/bootstrap/lib/stdlib/ebin/io_lib.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/io_lib_format.beam b/bootstrap/lib/stdlib/ebin/io_lib_format.beam
index 5fb023c5e1..1a7c3393f8 100644
--- a/bootstrap/lib/stdlib/ebin/io_lib_format.beam
+++ b/bootstrap/lib/stdlib/ebin/io_lib_format.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/io_lib_fread.beam b/bootstrap/lib/stdlib/ebin/io_lib_fread.beam
index 5ecd664026..f3f059df26 100644
--- a/bootstrap/lib/stdlib/ebin/io_lib_fread.beam
+++ b/bootstrap/lib/stdlib/ebin/io_lib_fread.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/io_lib_pretty.beam b/bootstrap/lib/stdlib/ebin/io_lib_pretty.beam
index be283307f3..b14eb23e7c 100644
--- a/bootstrap/lib/stdlib/ebin/io_lib_pretty.beam
+++ b/bootstrap/lib/stdlib/ebin/io_lib_pretty.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/lib.beam b/bootstrap/lib/stdlib/ebin/lib.beam
index 3865cfb227..668ea7eb29 100644
--- a/bootstrap/lib/stdlib/ebin/lib.beam
+++ b/bootstrap/lib/stdlib/ebin/lib.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/lists.beam b/bootstrap/lib/stdlib/ebin/lists.beam
index dfd689aa5a..948af8d1be 100644
--- a/bootstrap/lib/stdlib/ebin/lists.beam
+++ b/bootstrap/lib/stdlib/ebin/lists.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/log_mf_h.beam b/bootstrap/lib/stdlib/ebin/log_mf_h.beam
index fdb417b71e..7d5ef0e093 100644
--- a/bootstrap/lib/stdlib/ebin/log_mf_h.beam
+++ b/bootstrap/lib/stdlib/ebin/log_mf_h.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/ms_transform.beam b/bootstrap/lib/stdlib/ebin/ms_transform.beam
index bc16416161..6031052e4c 100644
--- a/bootstrap/lib/stdlib/ebin/ms_transform.beam
+++ b/bootstrap/lib/stdlib/ebin/ms_transform.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/otp_internal.beam b/bootstrap/lib/stdlib/ebin/otp_internal.beam
index 270d35ef33..84feea2c66 100644
--- a/bootstrap/lib/stdlib/ebin/otp_internal.beam
+++ b/bootstrap/lib/stdlib/ebin/otp_internal.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/pg.beam b/bootstrap/lib/stdlib/ebin/pg.beam
index 0b08fb57ca..65195defbf 100644
--- a/bootstrap/lib/stdlib/ebin/pg.beam
+++ b/bootstrap/lib/stdlib/ebin/pg.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/pool.beam b/bootstrap/lib/stdlib/ebin/pool.beam
index 3b1dbc3524..8ad59de20d 100644
--- a/bootstrap/lib/stdlib/ebin/pool.beam
+++ b/bootstrap/lib/stdlib/ebin/pool.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/proc_lib.beam b/bootstrap/lib/stdlib/ebin/proc_lib.beam
index 6b5c081480..e22b3ad758 100644
--- a/bootstrap/lib/stdlib/ebin/proc_lib.beam
+++ b/bootstrap/lib/stdlib/ebin/proc_lib.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/qlc.beam b/bootstrap/lib/stdlib/ebin/qlc.beam
index 716f28b661..302dda040f 100644
--- a/bootstrap/lib/stdlib/ebin/qlc.beam
+++ b/bootstrap/lib/stdlib/ebin/qlc.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/qlc_pt.beam b/bootstrap/lib/stdlib/ebin/qlc_pt.beam
index 8a25d95384..458684586b 100644
--- a/bootstrap/lib/stdlib/ebin/qlc_pt.beam
+++ b/bootstrap/lib/stdlib/ebin/qlc_pt.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/queue.beam b/bootstrap/lib/stdlib/ebin/queue.beam
index 5ebb0f9cc6..8907a03b57 100644
--- a/bootstrap/lib/stdlib/ebin/queue.beam
+++ b/bootstrap/lib/stdlib/ebin/queue.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/re.beam b/bootstrap/lib/stdlib/ebin/re.beam
index e20546d45d..8001c49672 100644
--- a/bootstrap/lib/stdlib/ebin/re.beam
+++ b/bootstrap/lib/stdlib/ebin/re.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/sets.beam b/bootstrap/lib/stdlib/ebin/sets.beam
index 5ac7691a8f..614f807ae1 100644
--- a/bootstrap/lib/stdlib/ebin/sets.beam
+++ b/bootstrap/lib/stdlib/ebin/sets.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/shell.beam b/bootstrap/lib/stdlib/ebin/shell.beam
index 20b9938d06..5614d31ac8 100644
--- a/bootstrap/lib/stdlib/ebin/shell.beam
+++ b/bootstrap/lib/stdlib/ebin/shell.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/slave.beam b/bootstrap/lib/stdlib/ebin/slave.beam
index dfc8cad9d6..4f3dfa3716 100644
--- a/bootstrap/lib/stdlib/ebin/slave.beam
+++ b/bootstrap/lib/stdlib/ebin/slave.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/sofs.beam b/bootstrap/lib/stdlib/ebin/sofs.beam
index a0c8c58c03..d8d65c5521 100644
--- a/bootstrap/lib/stdlib/ebin/sofs.beam
+++ b/bootstrap/lib/stdlib/ebin/sofs.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/supervisor.beam b/bootstrap/lib/stdlib/ebin/supervisor.beam
index 837027e93a..aae89985c8 100644
--- a/bootstrap/lib/stdlib/ebin/supervisor.beam
+++ b/bootstrap/lib/stdlib/ebin/supervisor.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/sys.beam b/bootstrap/lib/stdlib/ebin/sys.beam
index 724dfed4e6..841874f1f5 100644
--- a/bootstrap/lib/stdlib/ebin/sys.beam
+++ b/bootstrap/lib/stdlib/ebin/sys.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/unicode.beam b/bootstrap/lib/stdlib/ebin/unicode.beam
index 074fe0534d..572cb2b6fa 100644
--- a/bootstrap/lib/stdlib/ebin/unicode.beam
+++ b/bootstrap/lib/stdlib/ebin/unicode.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/win32reg.beam b/bootstrap/lib/stdlib/ebin/win32reg.beam
index 9c7893f7ff..6df20f8db7 100644
--- a/bootstrap/lib/stdlib/ebin/win32reg.beam
+++ b/bootstrap/lib/stdlib/ebin/win32reg.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/zip.beam b/bootstrap/lib/stdlib/ebin/zip.beam
index 2907ee286b..e8492c52bc 100644
--- a/bootstrap/lib/stdlib/ebin/zip.beam
+++ b/bootstrap/lib/stdlib/ebin/zip.beam
Binary files differ
diff --git a/configure.in b/configure.in
index e906c17ecc..c0994245e8 100644
--- a/configure.in
+++ b/configure.in
@@ -354,6 +354,21 @@ elif test X"$TMPSYS" '=' X"Darwin-i386"; then
export LDFLAGS
fi
+AC_ARG_ENABLE([silent-rules], [dnl
+AS_HELP_STRING(
+ [--enable-silent-rules],
+ [less verbose build output (undo: "make V=1")])
+AS_HELP_STRING(
+ [--disable-silent-rules],
+ [verbose build output (undo: "make V=0")])dnl
+])
+
+DEFAULT_VERBOSITY=1
+if test X${enable_silent_rules} = Xyes; then
+ DEFAULT_VERBOSITY=0
+fi
+AC_SUBST(DEFAULT_VERBOSITY)
+
if test X${enable_m64_build} = Xyes; then
enable_hipe=no
CFLAGS="-m64 $CFLAGS"
@@ -378,7 +393,7 @@ AC_SUBST(NATIVE_LIBS_ENABLED)
export ERL_TOP
AC_CONFIG_SUBDIRS(lib erts)
-AC_CONFIG_FILES([Makefile])
+AC_CONFIG_FILES([Makefile make/output.mk])
AC_CONFIG_FILES([make/emd2exml], [chmod +x make/emd2exml])
AC_OUTPUT
diff --git a/erts/Makefile.in b/erts/Makefile.in
index 5df6d71ef3..0bcb784972 100644
--- a/erts/Makefile.in
+++ b/erts/Makefile.in
@@ -19,6 +19,7 @@
.NOTPARALLEL:
+include $(ERL_TOP)/make/output.mk
include $(ERL_TOP)/make/target.mk
include vsn.mk
@@ -38,11 +39,11 @@ all: smp opt
.PHONY: docs
docs:
- ( cd doc/src && $(MAKE) $@ )
+ $(V_at)( cd doc/src && $(MAKE) $@ )
.PHONY: debug opt clean
debug opt clean:
- for d in emulator $(ERTSDIRS); do \
+ $(V_at)for d in emulator $(ERTSDIRS); do \
if test -d $$d; then \
( cd $$d && $(MAKE) $@ FLAVOR=$(FLAVOR) ) || exit $$? ; \
fi ; \
@@ -55,7 +56,7 @@ debug opt clean:
.PHONY: $(EXTRA_FLAVORS)
$(EXTRA_FLAVORS):
- ( cd emulator && $(MAKE) opt FLAVOR=$@ )
+ $(V_at)( cd emulator && $(MAKE) opt FLAVOR=$@ )
# Make erl script and erlc in $(ERL_TOP)/bin which runs the compiled version
# Note that erlc is not a script and requires extra handling on cygwin.
@@ -67,7 +68,7 @@ $(EXTRA_FLAVORS):
.PHONY: local_setup
local_setup:
@cd start_scripts && $(MAKE)
- @echo `ls $(ERL_TOP)/bin/`
+ $(V_colon)@echo `ls $(ERL_TOP)/bin/`
@rm -f $(ERL_TOP)/bin/erl $(ERL_TOP)/bin/erlc $(ERL_TOP)/bin/cerl \
$(ERL_TOP)/bin/erl.exe $(ERL_TOP)/bin/erlc.exe \
$(ERL_TOP)/bin/escript $(ERL_TOP)/bin/escript.exe \
@@ -128,10 +129,10 @@ makefiles:
.PHONY: release
release:
- for f in plain $(EXTRA_FLAVORS) ; do \
+ $(V_at)for f in plain $(EXTRA_FLAVORS) ; do \
( cd emulator && $(MAKE) release FLAVOR=$$f ) \
done
- for d in $(ERTSDIRS) $(XINSTDIRS); do \
+ $(V_at)for d in $(ERTSDIRS) $(XINSTDIRS); do \
if test -d $$d; then \
( cd $$d && $(MAKE) $@ ) || exit $$? ; \
fi ; \
@@ -139,4 +140,4 @@ release:
.PHONY: release_docs
release_docs:
- ( cd doc/src && $(MAKE) $@ )
+ $(V_at)( cd doc/src && $(MAKE) $@ )
diff --git a/erts/aclocal.m4 b/erts/aclocal.m4
index 339a15a2bb..918e30a886 100644
--- a/erts/aclocal.m4
+++ b/erts/aclocal.m4
@@ -1,7 +1,7 @@
dnl
dnl %CopyrightBegin%
dnl
-dnl Copyright Ericsson AB 1998-2011. All Rights Reserved.
+dnl Copyright Ericsson AB 1998-2012. All Rights Reserved.
dnl
dnl The contents of this file are subject to the Erlang Public License,
dnl Version 1.1, (the "License"); you may not use this file except in
@@ -59,6 +59,7 @@ AC_ARG_VAR(erl_xcomp_isysroot, [Absolute cross system root include path (only us
dnl Cross compilation variables
AC_ARG_VAR(erl_xcomp_bigendian, [big endian system: yes|no (only used when cross compiling)])
+AC_ARG_VAR(erl_xcomp_double_middle_endian, [double-middle-endian system: yes|no (only used when cross compiling)])
AC_ARG_VAR(erl_xcomp_linux_clock_gettime_correction, [clock_gettime() can be used for time correction: yes|no (only used when cross compiling)])
AC_ARG_VAR(erl_xcomp_linux_nptl, [have Native POSIX Thread Library: yes|no (only used when cross compiling)])
AC_ARG_VAR(erl_xcomp_linux_usable_sigusrx, [SIGUSR1 and SIGUSR2 can be used: yes|no (only used when cross compiling)])
@@ -606,6 +607,103 @@ ifelse([$5], , , [$5
fi
])
+dnl ----------------------------------------------------------------------
+dnl
+dnl AC_DOUBLE_MIDDLE_ENDIAN
+dnl
+dnl Checks whether doubles are represented in "middle-endian" format.
+dnl Sets ac_cv_double_middle_endian={no,yes,unknown} accordingly,
+dnl as well as DOUBLE_MIDDLE_ENDIAN.
+dnl
+dnl
+
+AC_DEFUN([AC_C_DOUBLE_MIDDLE_ENDIAN],
+[AC_CACHE_CHECK(whether double word ordering is middle-endian, ac_cv_c_double_middle_endian,
+[# It does not; compile a test program.
+AC_RUN_IFELSE(
+[AC_LANG_SOURCE([[#include <stdlib.h>
+
+int
+main(void)
+{
+ int i = 0;
+ int zero = 0;
+ int bigendian;
+ int zero_index = 0;
+
+ union
+ {
+ long int l;
+ char c[sizeof (long int)];
+ } u;
+
+ /* we'll use the one with 32-bit words */
+ union
+ {
+ double d;
+ unsigned int c[2];
+ } vint;
+
+ union
+ {
+ double d;
+ unsigned long c[2];
+ } vlong;
+
+ union
+ {
+ double d;
+ unsigned short c[2];
+ } vshort;
+
+
+ /* Are we little or big endian? From Harbison&Steele. */
+ u.l = 1;
+ bigendian = (u.c[sizeof (long int) - 1] == 1);
+
+ zero_index = bigendian ? 1 : 0;
+
+ vint.d = 1.0;
+ vlong.d = 1.0;
+ vshort.d = 1.0;
+
+ if (sizeof(unsigned int) == 4)
+ {
+ if (vint.c[zero_index] != 0)
+ zero = 1;
+ }
+ else if (sizeof(unsigned long) == 4)
+ {
+ if (vlong.c[zero_index] != 0)
+ zero = 1;
+ }
+ else if (sizeof(unsigned short) == 4)
+ {
+ if (vshort.c[zero_index] != 0)
+ zero = 1;
+ }
+
+ exit (zero);
+}
+]])],
+ [ac_cv_c_double_middle_endian=no],
+ [ac_cv_c_double_middle_endian=yes],
+ [ac_cv_c_double_middle=unknown])])
+case $ac_cv_c_double_middle_endian in
+ yes)
+ m4_default([$1],
+ [AC_DEFINE([DOUBLE_MIDDLE_ENDIAN], 1,
+ [Define to 1 if your processor stores the words in a double in
+ middle-endian format (like some ARMs).])]) ;;
+ no)
+ $2 ;;
+ *)
+ m4_default([$3],
+ [AC_MSG_WARN([unknown double endianness
+presetting ac_cv_c_double_middle_endian=no (or yes) will help])]) ;;
+esac
+])# AC_C_DOUBLE_MIDDLE_ENDIAN
+
dnl ----------------------------------------------------------------------
dnl
@@ -642,11 +740,16 @@ dnl Try to find POSIX threads
dnl The usual pthread lib...
AC_CHECK_LIB(pthread, pthread_create, THR_LIBS="-lpthread")
-dnl FreeBSD has pthreads in special c library, c_r...
+dnl Very old versions of FreeBSD have pthreads in special c library, c_r...
if test "x$THR_LIBS" = "x"; then
AC_CHECK_LIB(c_r, pthread_create, THR_LIBS="-lc_r")
fi
+dnl QNX has pthreads in standard C library
+ if test "x$THR_LIBS" = "x"; then
+ AC_CHECK_FUNC(pthread_create, THR_LIBS="none_needed")
+ fi
+
dnl On ofs1 the '-pthread' switch should be used
if test "x$THR_LIBS" = "x"; then
AC_MSG_CHECKING([if the '-pthread' switch can be used])
@@ -667,6 +770,9 @@ dnl On ofs1 the '-pthread' switch should be used
if test "x$THR_LIBS" != "x"; then
THR_DEFS="$THR_DEFS -D_THREAD_SAFE -D_REENTRANT -DPOSIX_THREADS"
THR_LIB_NAME=pthread
+ if test "x$THR_LIBS" = "xnone_needed"; then
+ THR_LIBS=
+ fi
case $host_os in
solaris*)
THR_DEFS="$THR_DEFS -D_POSIX_PTHREAD_SEMANTICS" ;;
@@ -1337,6 +1443,14 @@ if test "$ac_cv_c_bigendian" = "yes"; then
AC_DEFINE(ETHR_BIGENDIAN, 1, [Define if bigendian])
fi
+case X$erl_xcomp_double_middle_endian in
+ X) ;;
+ Xyes|Xno|Xunknown) ac_cv_c_double_middle_endian=$erl_xcomp_double_middle_endian;;
+ *) AC_MSG_ERROR([Bad erl_xcomp_double_middle_endian value: $erl_xcomp_double_middle_endian]);;
+esac
+
+AC_C_DOUBLE_MIDDLE_ENDIAN
+
AC_ARG_ENABLE(native-ethr-impls,
AS_HELP_STRING([--disable-native-ethr-impls],
[disable native ethread implementations]),
@@ -1735,6 +1849,31 @@ case $erl_gethrvtime in
esac
])dnl
+dnl ----------------------------------------------------------------------
+dnl
+dnl LM_TRY_ENABLE_CFLAG
+dnl
+dnl
+dnl Tries a CFLAG and sees if it can be enabled without compiler errors
+dnl $1: textual cflag to add
+dnl $2: variable to store the modified CFLAG in
+dnl Usage example LM_TRY_ENABLE_CFLAG([-Werror=return-type], [CFLAGS])
+dnl
+dnl
+AC_DEFUN([LM_TRY_ENABLE_CFLAG], [
+ AC_MSG_CHECKING([if we can add $1 to $2 (via CFLAGS)])
+ saved_CFLAGS=$CFLAGS;
+ CFLAGS="$1 $$2";
+ AC_TRY_COMPILE([],[return 0;],can_enable_flag=true,can_enable_flag=false)
+ CFLAGS=$saved_CFLAGS;
+ if test "X$can_enable_flag" = "Xtrue"; then
+ AC_MSG_RESULT([yes])
+ AS_VAR_SET($2, "$1 $$2")
+ else
+ AC_MSG_RESULT([no])
+ fi
+])
+
dnl ERL_TRY_LINK_JAVA(CLASSES, FUNCTION-BODY
dnl [ACTION_IF_FOUND [, ACTION-IF-NOT-FOUND]])
dnl Freely inspired by AC_TRY_LINK. (Maybe better to create a
diff --git a/erts/autoconf/vxworks/sed.general b/erts/autoconf/vxworks/sed.general
index ac792b44b5..162fd38c2b 100644
--- a/erts/autoconf/vxworks/sed.general
+++ b/erts/autoconf/vxworks/sed.general
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/erts/configure.in b/erts/configure.in
index b3289bf84c..a0c5cab181 100644
--- a/erts/configure.in
+++ b/erts/configure.in
@@ -537,6 +537,9 @@ else
fi
if test "x$GCC" = xyes; then
+ # Treat certain GCC warnings as errors
+ LM_TRY_ENABLE_CFLAG([-Werror=return-type], [WERRORFLAGS])
+
# until the emulator can handle this, I suggest we turn it off!
#WFLAGS="-Wall -Wshadow -Wcast-qual -Wmissing-declarations"
WFLAGS="-Wall -Wstrict-prototypes"
@@ -559,11 +562,13 @@ if test "x$GCC" = xyes; then
CFLAGS=$saved_CFLAGS
else
WFLAGS=""
+ WERRORFLAGS=""
fi
dnl DEBUG_FLAGS is obsolete (I hope)
AC_SUBST(DEBUG_FLAGS)
AC_SUBST(DEBUG_CFLAGS)
AC_SUBST(WFLAGS)
+AC_SUBST(WERRORFLAGS)
AC_SUBST(CFLAG_RUNTIME_LIBRARY_PATH)
AC_CHECK_SIZEOF(void *) # Needed for ARCH and smp checks below
@@ -735,12 +740,8 @@ esac
AC_MSG_CHECKING(if VM has to be linked with Carbon framework)
case $ARCH-$OPSYS in
- amd64-darwin*)
- LIBCARBON=
- AC_MSG_RESULT([no])
- ;;
*-darwin*)
- LIBCARBON="-framework Carbon "
+ LIBCARBON="-framework Carbon -framework Cocoa"
AC_MSG_RESULT([yes])
;;
*)
@@ -826,6 +827,12 @@ if test -z "$FOP"; then
AC_MSG_WARN([No 'fop' command found: going to generate placeholder PDF files])
fi
+AC_CHECK_PROGS(XMLLINT, xmllint)
+if test -z "$XMLLINT"; then
+ echo "xmllint" >> doc/CONF_INFO
+ AC_MSG_WARN([No 'xmllint' command found: can't run the xmllint target for the documentation])
+fi
+
dnl
dnl We can live with Solaris /usr/ucb/install
dnl
@@ -1075,8 +1082,45 @@ fi
AC_SUBST(ERTS_BUILD_SMP_EMU)
-AC_CHECK_FUNCS([posix_fadvise])
+AC_CHECK_FUNCS([posix_fadvise, fallocate])
+AC_CHECK_HEADERS([linux/falloc.h])
+dnl * Old glibcs have broken posix_fallocate(). Make sure not to use it.
+dnl * It may also be broken in AIX.
+AC_CACHE_CHECK([whether posix_fallocate() works],i_cv_posix_fallocate_works,[
+ AC_TRY_RUN([
+ #if !defined(__sun) && !defined(__sun__)
+ #define _XOPEN_SOURCE 600
+ #endif
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <fcntl.h>
+ #include <unistd.h>
+ #if defined(__GLIBC__) && (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 7))
+ possibly broken posix_fallocate
+ #endif
+ int main() {
+ int fd = creat("conftest.temp", 0600);
+ int ret;
+ if (-1 == fd) {
+ perror("creat()");
+ return 2;
+ }
+ ret = posix_fallocate(fd, 1024, 1024) < 0 ? 1 : 0;
+ unlink("conftest.temp");
+ return ret;
+ }
+ ], [
+ i_cv_posix_fallocate_works=yes
+ ], [
+ i_cv_posix_fallocate_works=no
+ ], [
+ i_cv_posix_fallocate_works=no
+ ])
+])
+if test $i_cv_posix_fallocate_works = yes; then
+ AC_DEFINE(HAVE_POSIX_FALLOCATE,, Define if you have a working posix_fallocate())
+fi
#
# Figure out if the emulator should use threads. The default is set above
@@ -1730,6 +1774,7 @@ case X$erl_xcomp_bigendian in
esac
AC_C_BIGENDIAN
+AC_C_DOUBLE_MIDDLE_ENDIAN
dnl fdatasync syscall (Unix only)
AC_CHECK_FUNCS([fdatasync])
@@ -1881,9 +1926,10 @@ AC_CHECK_FUNCS([ieee_handler fpsetmask finite isnan isinf res_gethostbyname dlop
pread pwrite writev memmove strerror strerror_r strncasecmp \
gethrtime localtime_r gmtime_r inet_pton mmap mremap memcpy mallopt \
sbrk _sbrk __sbrk brk _brk __brk \
- flockfile fstat strlcpy strlcat setsid posix2time setlocale nl_langinfo poll])
+ flockfile fstat strlcpy strlcat setsid posix2time time2posix \
+ setlocale nl_langinfo poll])
-AC_CHECK_DECLS([posix2time],,,[#include <time.h>])
+AC_CHECK_DECLS([posix2time, time2posix],,,[#include <time.h>])
disable_vfork=false
if test "x$EMU_THR_LIB_NAME" != "x"; then
@@ -1943,6 +1989,68 @@ AC_CHECK_FUNCS([openpty])
AC_CHECK_HEADERS(net/if_dl.h ifaddrs.h netpacket/packet.h)
AC_CHECK_FUNCS([getifaddrs])
+dnl Checks for variables in6addr_any and in6addr_loopback,
+dnl
+dnl They normally declared by netinet/in.h, according to POSIX,
+dnl but not on Windows 7 (Windows SDK 7.1). I would have liked
+dnl to just write AC_CHECK_DECL([in6addr_any], ...) but if doing so,
+dnl the configure check fails erroneously on Linux with the error
+dnl "cannot convert to a pointer type", on a line looking like
+dnl "char *p = (char *) in6addr_any;", so work around that
+dnl with some more code.
+AC_CACHE_CHECK(
+ [whether in6addr_any is declared],
+ [erts_cv_have_in6addr_any],
+ [AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[
+ #include <sys/types.h>
+ #include <sys/socket.h>
+ #include <netinet/in.h>
+ ]],
+ [[printf("%d", in6addr_any.s6_addr[16]);]]
+ )],
+ [erts_cv_have_in6addr_any=yes],
+ [erts_cv_have_in6addr_any=no]
+ )]
+)
+
+case "$erts_cv_have_in6addr_any" in
+ yes)
+ AC_DEFINE([HAVE_IN6ADDR_ANY], [1],
+ [Define to 1 if you have the variable in6addr_any declared.])
+esac
+
+AC_CACHE_CHECK(
+ [whether in6addr_loopback is declared],
+ [erts_cv_have_in6addr_loopback],
+ [AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[
+ #include <sys/types.h>
+ #include <sys/socket.h>
+ #include <netinet/in.h>
+ ]],
+ [[printf("%d", in6addr_loopback.s6_addr[16]);]]
+ )],
+ [erts_cv_have_in6addr_loopback=yes],
+ [erts_cv_have_in6addr_loopback=no]
+ )]
+)
+
+case "$erts_cv_have_in6addr_loopback" in
+ yes)
+ AC_DEFINE([HAVE_IN6ADDR_LOOPBACK], [1],
+ [Define to 1 if you have the variable in6addr_loopback declared.])
+esac
+
+AC_CHECK_DECLS([IN6ADDR_ANY_INIT, IN6ADDR_LOOPBACK_INIT, IPV6_V6ONLY], [], [],
+ [
+ #include <sys/types.h>
+ #include <sys/socket.h>
+ #include <netinet/in.h>
+ ])
+
dnl ----------------------------------------------------------------------
dnl Checks for features/quirks in the system that affects Erlang.
dnl ----------------------------------------------------------------------
@@ -3515,6 +3623,11 @@ case $host_os in
DED_LDFLAGS="-m32 $DED_LDFLAGS"
fi
;;
+ openbsd*)
+ DED_LD="$CC"
+ DED_LD_FLAG_RUNTIME_LIBRARY_PATH="$CFLAG_RUNTIME_LIBRARY_PATH"
+ DED_LDFLAGS="-shared"
+ ;;
osf*)
# NOTE! Whitespace after -rpath is important.
DED_LD_FLAG_RUNTIME_LIBRARY_PATH="-rpath "
@@ -4392,7 +4505,7 @@ dnl needs to be rebuilt.
dnl
AC_DEFINE_UNQUOTED(ERTS_EMU_CMDLINE_FLAGS,
-"$STATIC_CFLAGS $CFLAGS $DEBUG_CFLAGS $EMU_THR_DEFS $DEFS $WFLAGS",
+"$STATIC_CFLAGS $CFLAGS $DEBUG_CFLAGS $EMU_THR_DEFS $DEFS $WERRORFLAGS $WFLAGS",
[The only reason ERTS_EMU_CMDLINE_FLAGS exists is to force modification of config.h when the emulator command line flags are modified by configure])
dnl ----------------------------------------------------------------------
@@ -4455,6 +4568,10 @@ AH_BOTTOM([
#endif
])
+if test "x$GCC" = xyes; then
+ CFLAGS="$WERRORFLAGS $CFLAGS"
+fi
+
dnl ----------------------------------------------------------------------
dnl Output the result.
dnl ----------------------------------------------------------------------
diff --git a/erts/doc/src/Makefile b/erts/doc/src/Makefile
index 13cf003aad..89d7c85a86 100644
--- a/erts/doc/src/Makefile
+++ b/erts/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -55,7 +55,6 @@ XML_REF3_EFILES = \
XML_REF3_FILES = \
driver_entry.xml \
erl_nif.xml \
- erl_set_memory_block.xml \
erl_driver.xml \
erl_prim_loader.xml \
erlang.xml \
@@ -78,6 +77,7 @@ XML_CHAPTER_FILES = \
inet_cfg.xml \
erl_ext_dist.xml \
erl_dist_protocol.xml \
+ communication.xml \
notes.xml \
notes_history.xml
@@ -158,9 +158,6 @@ $(SPECDIR)/specs_driver_entry.xml:
$(SPECDIR)/specs_erl_nif.xml:
escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) \
-o$(dir $@) -module erl_nif
-$(SPECDIR)/specs_erl_set_memory_block.xml:
- escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) \
- -o$(dir $@) -module erl_set_memory_block
$(SPECDIR)/specs_erl_driver.xml:
escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) \
-o$(dir $@) -module erl_driver
diff --git a/erts/doc/src/absform.xml b/erts/doc/src/absform.xml
index 4455d0ac92..d036b4c7fb 100644
--- a/erts/doc/src/absform.xml
+++ b/erts/doc/src/absform.xml
@@ -290,13 +290,6 @@
<item>If E is <c><![CDATA[fun Fc_1 ; ... ; Fc_k end]]></c>
where each <c><![CDATA[Fc_i]]></c> is a function clause then Rep(E) =
<c><![CDATA[{'fun',LINE,{clauses,[Rep(Fc_1), ..., Rep(Fc_k)]}}]]></c>.</item>
- <item>If E is <c><![CDATA[query [E_0 || W_1, ..., W_k] end]]></c>,
- where each <c><![CDATA[W_i]]></c> is a generator or a filter, then
- Rep(E) = <c><![CDATA[{'query',LINE,{lc,LINE,Rep(E_0),[Rep(W_1), ..., Rep(W_k)]}}]]></c>.
- For Rep(W), see below.</item>
- <item>If E is <c><![CDATA[E_0.Field]]></c>, a Mnesia record access
- inside a query, then
- Rep(E) = <c><![CDATA[{record_field,LINE,Rep(E_0),Rep(Field)}]]></c>.</item>
<item>If E is <c><![CDATA[( E_0 )]]></c>, then
Rep(E) = <c><![CDATA[Rep(E_0)]]></c>,
i.e., parenthesized expressions cannot be distinguished from their bodies.</item>
diff --git a/erts/doc/src/communication.xml b/erts/doc/src/communication.xml
new file mode 100644
index 0000000000..6049123f6a
--- /dev/null
+++ b/erts/doc/src/communication.xml
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE chapter SYSTEM "chapter.dtd">
+
+<chapter>
+ <header>
+ <copyright>
+ <year>2012</year><year>2012</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>Communication in Erlang</title>
+ <prepared>Rickard Green</prepared>
+ <responsible></responsible>
+ <docno></docno>
+ <approved></approved>
+ <checked></checked>
+ <date>2012-12-03</date>
+ <rev>PA1</rev>
+ <file>communication.xml</file>
+ </header>
+ <p>Communication in Erlang is conceptually performed using
+ asynchronous signaling. All different executing entities
+ such as processes, and ports communicate via asynchronous
+ signals. The most commonly used signal is a message. Other
+ common signals are exit, link, unlink, monitor, demonitor
+ signals.</p>
+ <section>
+ <title>Passing of Signals</title>
+ <p>The amount of time that passes between a signal being sent
+ and the arrival of the signal at the destination is unspecified
+ but positive. If the receiver has terminated, the signal will
+ not arrive, but it is possible that it triggers another signal.
+ For example, a link signal sent to a non-existing process will
+ trigger an exit signal which will be sent back to where the link
+ signal originated from. When communicating over the distribution,
+ signals may be lost if the distribution channel goes down.</p>
+ <p>The only signal ordering guarantee given is the following. If
+ an entity sends multiple signals to the same destination entity,
+ the order will be preserved. That is, if <c>A</c> send
+ a signal <c>S1</c> to <c>B</c>, and later sends
+ the signal <c>S2</c> to <c>B</c>, <c>S1</c> is guaranteed not to
+ arrive after <c>S2</c>.</p>
+ </section>
+ <section>
+ <title>Synchronous Communication</title>
+ <p>Some communication is synchronous. If broken down into pieces,
+ a synchronous communication operation, consists of two asynchronous
+ signals. One request signal and one reply signal. An example of
+ such a synchronous communication is a call to <c>process_info/2</c>
+ when the first argument is not <c>self()</c>. The caller will send
+ an asynchronous signal requesting information, and will then
+ wait for the reply signal containing the requested information. When
+ the request signal reaches its destination the destination process
+ replies with the requested information.</p>
+ </section>
+ <section>
+ <title>Implementation</title>
+ <p>The implementation of different asynchronous signals in the
+ VM may vary over time, but the behavior will always respect this
+ concept of asynchronous signals being passed between entities
+ as described above.</p>
+ <p>By inspecting the implementation you might notice that some
+ specific signal actually gives a stricter guarantee than described
+ above. It is of vital importance that such knowledge about the
+ implementation is <em>not</em> used by Erlang code, since the
+ implementation might change at any time without prior notice.</p>
+ <p>Some example of major implementation changes:</p>
+ <list type="bulleted">
+ <item>As of ERTS version 5.5.2 exit signals to processes are truly
+ asynchronously delivered.</item>
+ <item>As of ERTS version 5.10 all signals from processes to ports
+ are truly asynchronously delivered.</item>
+ </list>
+ </section>
+</chapter>
+
diff --git a/erts/doc/src/driver_entry.xml b/erts/doc/src/driver_entry.xml
index a2efdf3ebc..c37138e7b1 100644
--- a/erts/doc/src/driver_entry.xml
+++ b/erts/doc/src/driver_entry.xml
@@ -4,7 +4,7 @@
<cref>
<header>
<copyright>
- <year>2001</year><year>2011</year>
+ <year>2001</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -34,6 +34,29 @@
<lib>driver_entry</lib>
<libsummary>The driver-entry structure used by erlang drivers.</libsummary>
<description>
+ <marker id="WARNING"/>
+ <warning><p><em>Use this functionality with extreme care!</em></p>
+ <p>A driver callback is executed as a direct extension of the
+ native code of the VM. Execution is not made in a safe environment.
+ The VM can <em>not</em> provide the same services as provided when
+ executing Erlang code, such as preemptive scheduling or memory
+ protection. If the driver callback function doesn't behave well,
+ the whole VM will misbehave.</p>
+ <list>
+ <item><p>A driver callback that crash will crash the whole VM.</p></item>
+ <item><p>An erroneously implemented driver callback might cause
+ a VM internal state inconsistency which may cause a crash of the VM,
+ or miscellaneous misbehaviors of the VM at any point after the call
+ to the driver callback.</p></item>
+ <item><p>A driver callback that do
+ <seealso marker="erl_driver#lengthy_work">lengthy work</seealso>
+ before returning will degrade responsiveness of the VM,
+ and may cause miscellaneous strange behaviors. Such strange behaviors
+ include, but are not limited to, extreme memory usage, and bad load
+ balancing between schedulers. Strange behaviors that might occur due
+ to lengthy work may also vary between OTP releases.</p></item>
+ </list>
+ </warning>
<p>
As of erts version 5.9 (OTP release R15B) the driver interface
has been changed with larger types for the callbacks
@@ -377,11 +400,11 @@ typedef struct erl_drv_entry {
<tag><marker id="driver_flags"/>int driver_flags</tag>
<item>
- <p>This field is used to pass driver capability information to the
- runtime system. If the <c>extended_marker</c> field equals
- <c>ERL_DRV_EXTENDED_MARKER</c>, it should contain <c>0</c> or
- driver flags (<c>ERL_DRV_FLAG_*</c>) ored bitwise. Currently
- the following driver flags exist:
+ <p>This field is used to pass driver capability and other
+ information to the runtime system. If the
+ <c>extended_marker</c> field equals <c>ERL_DRV_EXTENDED_MARKER</c>,
+ it should contain <c>0</c> or driver flags (<c>ERL_DRV_FLAG_*</c>)
+ ored bitwise. Currently the following driver flags exist:
</p>
<taglist>
<tag><c>ERL_DRV_FLAG_USE_PORT_LOCKING</c></tag>
@@ -404,6 +427,12 @@ typedef struct erl_drv_entry {
by the Erlang distribution (the behaviour has always been
required by drivers used by the distribution).
</item>
+ <tag><c>ERL_DRV_FLAG_NO_BUSY_MSGQ</c></tag>
+ <item>Disable busy port message queue functionality. For
+ more information, see the documentation of the
+ <seealso marker="erl_driver#erl_drv_busy_msgq_limits">erl_drv_busy_msgq_limits()</seealso>
+ function.
+ </item>
</taglist>
</item>
<tag>void *handle2</tag>
diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml
index 6221a239fa..37c7f3466b 100644
--- a/erts/doc/src/erl.xml
+++ b/erts/doc/src/erl.xml
@@ -4,7 +4,7 @@
<comref>
<header>
<copyright>
- <year>1996</year><year>2011</year>
+ <year>1996</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -479,7 +479,7 @@
<tag><marker id="async_thread_pool_size"><c><![CDATA[+A size]]></c></marker></tag>
<item>
<p>Sets the number of threads in async thread pool, valid range
- is 0-1024. Default is 0.</p>
+ is 0-1024. If thread support is available, the default is 10.</p>
</item>
<tag><c><![CDATA[+B [c | d | i]]]></c></tag>
<item>
@@ -533,7 +533,7 @@
<p>Force the <c>compressed</c> option on all ETS tables.
Only intended for test and evaluation.</p>
</item>
- <tag><c><![CDATA[+fnl]]></c></tag>
+ <tag><marker id="file_name_encoding"></marker><c><![CDATA[+fnl]]></c></tag>
<item>
<p>The VM works with file names as if they are encoded using the ISO-latin-1 encoding, disallowing Unicode characters with codepoints beyond 255. This is default on operating systems that have transparent file naming, i.e. all Unixes except MacOSX.</p>
</item>
@@ -582,13 +582,71 @@
<seealso marker="erts_alloc">erts_alloc(3)</seealso> for
further information.</p>
</item>
- <tag><marker id="max_processes"><c><![CDATA[+P Number]]></c></marker></tag>
- <item>
- <p>Sets the maximum number of concurrent processes for this
- system. <c><![CDATA[Number]]></c> must be in the range 16..134217727.
- Default is 32768.</p>
- <p><em>NOTE</em>: It is possible to choose any value in the range
- but powers of 2 perform best.</p>
+ <tag><marker id="+n"/><c><![CDATA[+n Behavior]]></c></tag>
+ <item>
+ <p>Control behavior of signals to ports.</p>
+ <p>As of OTP-R16 signals to ports are truly asynchronously
+ delivered. Note that signals always have been documented as
+ asynchronous. The underlying implementation has, however,
+ previously delivered these signals synchronously. Correctly
+ written Erlang programs should be able to handle this without
+ any issues. Bugs in existing Erlang programs that make false
+ assumptions about signals to ports may, however, be tricky to
+ find. This switch has been introduced in order to at least
+ make it easier to compare behaviors during a transition period.
+ Note that <em>this flag is deprecated</em> as of its
+ introduction, and is scheduled for removal in OTP-R17.
+ <c>Behavior</c> should be one of the following characters:</p>
+ <taglist>
+ <tag><c>d</c></tag>
+ <item>The default. Asynchronous signals. A process that sends
+ a signal to a port may continue execution before the signal
+ has been delivered to the port.</item>
+ <tag><c>s</c></tag>
+ <item>Synchronous signals. A processes that sends a signal
+ to a port will not continue execution until the signal has
+ been delivered. Should <em>only</em> be used for testing and
+ debugging.</item>
+ <tag><c>a</c></tag>
+ <item>Asynchronous signals. As the default, but a processes
+ that sends a signal will even more frequently continue
+ execution before the signal has been delivered to the
+ port. Should <em>only</em> be used for testing and
+ debugging.</item>
+ </taglist>
+ </item>
+ <tag><marker id="+P"/><marker id="max_processes"><c><![CDATA[+P Number]]></c></marker></tag>
+ <item>
+ <p>Sets the maximum number of simultaneously existing processes for this
+ system. Valid range for <c>Number</c> is <c>[1024-134217727]</c></p>
+ <p><em>NOTE</em>: The actual maximum chosen may be much larger than
+ the <c>Number</c> passed. Currently the runtime system often,
+ but not always, chooses a value that is a power of 2. This might,
+ however, be changed in the future. The actual value chosen can be
+ checked by calling
+ <seealso marker="erlang#system_info_process_limit">erlang:system_info(process_limit)</seealso>.</p>
+ <p>The default value is <c>262144</c></p>
+ </item>
+ <tag><marker id="+Q"/><marker id="max_ports"><c><![CDATA[+Q Number]]></c></marker></tag>
+ <item>
+ <p>Sets the maximum number of simultaneously existing ports for this
+ system. Valid range for <c>Number</c> is <c>[1024-134217727]</c></p>
+ <p><em>NOTE</em>: The actual maximum chosen may be much larger than
+ the actual <c>Number</c> passed. Currently the runtime system often,
+ but not always, chooses a value that is a power of 2. This might,
+ however, be changed in the future. The actual value chosen can be
+ checked by calling
+ <seealso marker="erlang#system_info_port_limit">erlang:system_info(port_limit)</seealso>.</p>
+ <p>The default value used is normally <c>65536</c>. However, if
+ the runtime system is able to determine maximum amount of file
+ descriptors that it is allowed to open and this value is larger
+ than <c>65536</c>, the chosen value will increased to a value
+ larger or equal to the maximum amount of file descriptors that
+ can be opened.</p>
+ <p>Previously the environment variable <c>ERL_MAX_PORTS</c> was used
+ for setting the maximum number of simultaneously existing ports. This
+ environment variable is deprecated, and scheduled for removal in
+ OTP-R17, but can still be used.</p>
</item>
<tag><marker id="compat_rel"><c><![CDATA[+R ReleaseNumber]]></c></marker></tag>
<item>
@@ -597,21 +655,14 @@
default. This flags sets the emulator in compatibility mode
with an earlier Erlang/OTP release <c><![CDATA[ReleaseNumber]]></c>.
The release number must be in the range
- <c><![CDATA[7..<current release>]]></c>. This limits the emulator,
- making it possible for it to communicate with Erlang nodes
- (as well as C- and Java nodes) running that earlier release.</p>
- <p>For example, an R10 node is not automatically compatible
- with an R9 node, but R10 nodes started with the <c><![CDATA[+R 9]]></c>
- flag can co-exist with R9 nodes in the same distributed
- Erlang system, they are R9-compatible.</p>
+ <c><![CDATA[<current release>-2..<current release>]]></c>. This
+ limits the emulator, making it possible for it to communicate
+ with Erlang nodes (as well as C- and Java nodes) running that
+ earlier release.</p>
<p>Note: Make sure all nodes (Erlang-, C-, and Java nodes) of
a distributed Erlang system is of the same Erlang/OTP release,
or from two different Erlang/OTP releases X and Y, where
<em>all</em> Y nodes have compatibility mode X.</p>
- <p>For example: A distributed Erlang system can consist of
- R10 nodes, or of R9 nodes and R9-compatible R10 nodes, but
- not of R9 nodes, R9-compatible R10 nodes and "regular" R10
- nodes, as R9 and "regular" R10 nodes are not compatible.</p>
</item>
<tag><c><![CDATA[+r]]></c></tag>
<item>
@@ -621,7 +672,7 @@
<item>
<p>Limits the amount of reader groups used by read/write locks
optimized for read operations in the Erlang runtime system. By
- default the reader groups limit equals 8.</p>
+ default the reader groups limit equals 64.</p>
<p>When the amount of schedulers is less than or equal to the reader
groups limit, each scheduler has its own reader group. When the
amount of schedulers is larger than the reader groups limit,
@@ -659,7 +710,24 @@
<taglist>
<tag><marker id="+sbt"><c>+sbt BindType</c></marker></tag>
<item>
- <p>Set scheduler bind type. Currently valid <c>BindType</c>s:
+ <p>Set scheduler bind type.</p>
+ <p>Schedulers can also be bound using the
+ <seealso marker="#+stbt">+stbt</seealso> flag. The only difference
+ between these two flags is how the following errors are handled:</p>
+ <list>
+ <item>Binding of schedulers is not supported on the specific
+ platform.</item>
+ <item>No available CPU topology. That is the runtime system
+ was not able to automatically detected the CPU topology, and
+ no <seealso marker="#+sct">user defined CPU topology</seealso>
+ was set.</item>
+ </list>
+ <p>If any of these errors occur when <c>+sbt</c> has been passed,
+ the runtime system will print an error message, and refuse to
+ start. If any of these errors occur when <c>+stbt</c> has been
+ passed, the runtime system will silently ignore the error, and
+ start up using unbound schedulers.</p>
+ <p>Currently valid <c>BindType</c>s:
</p>
<taglist>
<tag><c>u</c></tag>
@@ -909,6 +977,14 @@
<p>For more information, see
<seealso marker="erlang#system_info_cpu_topology">erlang:system_info(cpu_topology)</seealso>.</p>
</item>
+ <tag><marker id="+stbt"><c>+stbt BindType</c></marker></tag>
+ <item>
+ <p>Try to set scheduler bind type. The same as the
+ <seealso marker="#+sbt">+sbt</seealso> flag with the exception of
+ how some errors are handled. For more information, see the
+ documentation of the <seealso marker="#+sbt">+sbt</seealso> flag.
+ </p>
+ </item>
<tag><marker id="+sws"><c>+sws default|legacy|proposal</c></marker></tag>
<item>
<p>Set scheduler wakeup strategy. Default is <c>legacy</c> (has been
@@ -934,6 +1010,22 @@
without prior notice.
</p>
</item>
+ <tag><marker id="+spp"><c>+spp Bool</c></marker></tag>
+ <item>
+ <p>Set default scheduler hint for port parallelism. If set to
+ <c>true</c>, the VM will schedule port tasks when it by this can
+ improve the parallelism in the system. If set to <c>false</c>,
+ the VM will try to perform port tasks immediately and by this
+ improve latency at the expense of parallelism. If this
+ flag has not been passed, the default scheduler hint for port
+ parallelism is currently <c>false</c>. The default used can be
+ inspected in runtime by calling
+ <seealso marker="erlang#system_info_port_parallelism">erlang:system_info(port_parallelism)</seealso>.
+ The default can be overriden on port creation by passing the
+ <seealso marker="erlang#open_port_parallelism">parallelism</seealso>
+ option to
+ <seealso marker="erlang#open_port/2">open_port/2</seealso></p>.
+ </item>
<tag><marker id="sched_thread_stack_size"><c><![CDATA[+sss size]]></c></marker></tag>
<item>
<p>Suggested stack size, in kilowords, for scheduler threads.
@@ -1042,8 +1134,39 @@
the emulator will be allowed to spend writing a crash dump. When
the given number of seconds have elapsed, the emulator will be
terminated by a SIGALRM signal.</p>
- </item>
- <tag><c><![CDATA[ERL_AFLAGS]]></c></tag>
+
+ <p> If the environment variable is <em>not</em> set or it is set to zero seconds, <c><![CDATA[ERL_CRASH_DUMP_SECONDS=0]]></c>,
+ the runtime system will not even attempt to write the crash dump file. It will just terminate.
+ </p>
+ <p> If the environment variable is set to negative valie, e.g. <c><![CDATA[ERL_CRASH_DUMP_SECONDS=-1]]></c>,
+ the runtime system will wait indefinitely for the crash dump file to be written.
+ </p>
+ <p> This environment variable is used in conjuction with
+ <seealso marker="kernel:heart"><c>heart</c></seealso> if <c>heart</c> is running:
+ </p>
+ <taglist>
+ <tag><c><![CDATA[ERL_CRASH_DUMP_SECONDS=0]]></c></tag>
+ <item><p>
+ Suppresses the writing a crash dump file entirely,
+ thus rebooting the runtime system immediately.
+ This is the same as not setting the environment variable.
+ </p>
+ </item>
+ <tag><c><![CDATA[ERL_CRASH_DUMP_SECONDS=-1]]></c></tag>
+ <item><p>Setting the environment variable to a negative value will cause the
+ termination of the runtime system to wait until the crash dump file
+ has been completly written.
+ </p>
+ </item>
+ <tag><c><![CDATA[ERL_CRASH_DUMP_SECONDS=S]]></c></tag>
+ <item><p>
+ Will wait for <c>S</c> seconds to complete the crash dump file and
+ then terminate the runtime system.
+ </p>
+ </item>
+ </taglist>
+ </item>
+ <tag><marker id="ERL_AFLAGS"><c><![CDATA[ERL_AFLAGS]]></c></marker></tag>
<item>
<p>The content of this environment variable will be added to the
beginning of the command line for <c><![CDATA[erl]]></c>.</p>
@@ -1053,7 +1176,7 @@
the <c><![CDATA[-extra]]></c> section, i.e. the end of the command line
following after an <c><![CDATA[-extra]]></c> flag.</p>
</item>
- <tag><c><![CDATA[ERL_ZFLAGS]]></c> and <c><![CDATA[ERL_FLAGS]]></c></tag>
+ <tag><marker id="ERL_ZFLAGS"><c><![CDATA[ERL_ZFLAGS]]></c></marker> and <marker id="ERL_FLAGS"><c><![CDATA[ERL_FLAGS]]></c></marker></tag>
<item>
<p>The content of these environment variables will be added to the
end of the command line for <c><![CDATA[erl]]></c>.</p>
diff --git a/erts/doc/src/erl_dist_protocol.xml b/erts/doc/src/erl_dist_protocol.xml
index 6c725fc82d..0252187be5 100644
--- a/erts/doc/src/erl_dist_protocol.xml
+++ b/erts/doc/src/erl_dist_protocol.xml
@@ -547,13 +547,289 @@ If Result > 0, the packet only consists of [119, Result].
-->
</section>
-
+ <marker id="distribution_handshake"/>
<section>
- <title>Handshake</title>
- <p>
- The handshake is discussed in detail in the internal documentation for
- the kernel (Erlang) application.
- </p>
+ <title>Distribution Handshake</title>
+ <p>
+ This section describes the distribution handshake protocol
+ introduced in the OTP-R6 release of Erlang/OTP. This
+ description was previously located in
+ <c>$ERL_TOP/lib/kernel/internal_doc/distribution_handshake.txt</c>,
+ and has more or less been copied and "formatted" here. It has been
+ more or less unchanged since the year 1999, but the handshake
+ should not have changed much since then either.
+ </p>
+ <section>
+ <title>General</title>
+ <p>
+ 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.
+ </p>
+ <p>
+ This is not entirely safe, as it is vulnerable against takeover
+ attacks, but it is a tradeoff between fair safety and performance.
+ </p>
+ <p>
+ The cookies are never sent in cleartext and the handshake procedure
+ expects the client (called A) to be the first one to prove that it can
+ generate a sufficient digest. The digest is generated with the
+ MD5 message digest algorithm and the challenges are expected to be very
+ random numbers.
+ </p>
+ </section>
+ <section>
+ <title>Definitions</title>
+ <p>
+ A challenge is a 32 bit integer number in big endian order. Below the function
+ <c>gen_challenge()</c> returns a random 32 bit integer used as a challenge.
+ </p>
+ <p>
+ A digest is a (16 bytes) MD5 hash of the Challenge (as text) concatenated
+ with the cookie (as text). Below, the function <c>gen_digest(Challenge, Cookie)</c>
+ generates a digest as described above.
+ </p>
+ <p>
+ An out_cookie is the cookie used in outgoing communication to a certain node,
+ so that A's out_cookie for B should correspond with B's in_cookie for A and
+ the other way around. A's out_cookie for B and A's in_cookie for B need <em>NOT</em>
+ be the same. Below the function <c>out_cookie(Node)</c> returns the current
+ node's out_cookie for <c>Node</c>.
+ </p>
+ <p>
+ An in_cookie is the cookie expected to be used by another node when
+ communicating with us, so that A's in_cookie for B corresponds with B's
+ out_cookie for A. Below the function <c>in_cookie(Node)</c> returns the current
+ node's <c>in_cookie</c> for <c>Node</c>.
+ </p>
+ <p>
+ The cookies are text strings that can be viewed as passwords.
+ </p>
+ <p>
+ 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 <c>gen_tcp</c> option <c>{packet, 2}</c>. Note that after
+ the handshake, the distribution switches to 4 byte packet headers.
+ </p>
+
+ </section>
+ <section>
+ <title>The Handshake in Detail</title>
+ <p>
+ Imagine two nodes, node A, which initiates the handshake and node B, which
+ accepts the connection.
+ </p>
+ <taglist>
+ <tag>1) connect/accept</tag>
+ <item><p>A connects to B via TCP/IP and B accepts the connection.</p></item>
+ <tag>2) send_name/receive_name</tag>
+ <item><p>A sends an initial identification to B. B receives the message.
+ The message looks like this (every "square" being one byte and the packet
+ header removed):
+ </p>
+<pre>
++---+--------+--------+-----+-----+-----+-----+-----+-----+-...-+-----+
+|'n'|Version0|Version1|Flag0|Flag1|Flag2|Flag3|Name0|Name1| ... |NameN|
++---+--------+--------+-----+-----+-----+-----+-----+-----+-... +-----+
+</pre>
+ <p>
+ The 'n' is just a message tag.
+ Version0 and Version1 is the distribution version selected by node A,
+ based on information from EPMD. (16 bit big endian)
+ Flag0 ... Flag3 are capability flags, the capabilities defined in
+ <c>$ERL_TOP/lib/kernel/include/dist.hrl</c>.
+ (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).
+ </p></item>
+ <tag>3) recv_status/send_status</tag>
+ <item><p>B sends a status message to A, which indicates
+ if the connection is allowed. The following status codes are defined:</p>
+ <taglist>
+ <tag><c>ok</c></tag>
+ <item>The handshake will continue.</item>
+ <tag><c>ok_simultaneous</c></tag>
+ <item>The handshake will continue, but A is informed that B
+ has another ongoing connection attempt that will be
+ shut down (simultaneous connect where A's name is
+ greater than B's name, compared literally).</item>
+ <tag><c>nok</c></tag>
+ <item>The handshake will not continue, as B already has an ongoing handshake
+ which it itself has initiated. (simultaneous connect where B's name is
+ greater than A's).</item>
+ <tag><c>not_allowed</c></tag>
+ <item>The connection is disallowed for some (unspecified) security
+ reason.</item>
+ <tag><c>alive</c></tag>
+ <item>A connection to the node is already active, which either means
+ that node A is confused or that the TCP connection breakdown
+ of a previous node with this name has not yet reached node B.
+ See 3B below.</item>
+ </taglist>
+ <p>This is the format of the status message:</p>
+<pre>
++---+-------+-------+-...-+-------+
+|'s'|Status0|Status1| ... |StatusN|
++---+-------+-------+-...-+-------+
+</pre>
+ <p>
+ 's' is the message tag Status0 ... StatusN is the status as a string (not terminated)
+ </p>
+ </item>
+ <tag>3B) send_status/recv_status</tag>
+ <item><p>If status was 'alive', node A will answer with
+ another status message containing either 'true' which means that the
+ connection should continue (The old connection from this node is broken), or
+ <c>'false'</c>, which simply means that the connection should be closed, the
+ connection attempt was a mistake.</p></item>
+ <tag>4) recv_challenge/send_challenge</tag>
+ <item><p>If the status was <c>ok</c> or <c>ok_simultaneous</c>,
+ The handshake continues with B sending A another message, the challenge.
+ The challenge contains the same type of information as the "name" message
+ initially sent from A to B, with the addition of a 32 bit challenge:</p>
+<pre>
++---+--------+--------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-...-+-----+
+|'n'|Version0|Version1|Flag0|Flag1|Flag2|Flag3|Chal0|Chal1|Chal2|Chal3|Name0|Name1| ... |NameN|
++---+--------+--------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-... +-----+
+</pre>
+ <p>
+ 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.
+ </p></item>
+ <tag>5) send_challenge_reply/recv_challenge_reply</tag>
+ <item><p>Now A has generated a digest and its own challenge. Those are
+ sent together in a package to B:</p>
+<pre>
++---+-----+-----+-----+-----+-----+-----+-----+-----+-...-+------+
+|'r'|Chal0|Chal1|Chal2|Chal3|Dige0|Dige1|Dige2|Dige3| ... |Dige15|
++---+-----+-----+-----+-----+-----+-----+-----+-----+-...-+------+
+</pre>
+ <p>
+ Where 'r' is the tag, Chal0 ... Chal3 is A's challenge for B to handle and
+ Dige0 ... Dige15 is the digest that A constructed from the challenge B sent
+ in the previous step.
+ </p></item>
+ <tag>6) recv_challenge_ack/send_challenge_ack</tag>
+ <item><p>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:</p>
+<pre>
++---+-----+-----+-----+-----+-...-+------+
+|'a'|Dige0|Dige1|Dige2|Dige3| ... |Dige15|
++---+-----+-----+-----+-----+-...-+------+
+</pre>
+ <p>
+ Where 'a' is the tag and Dige0 ... Dige15 is the digest calculated by B
+ for A's challenge.</p></item>
+ <tag>7)</tag>
+ <item><p>A checks the digest from B and the connection is up.</p></item>
+ </taglist>
+ </section>
+ <section>
+ <title>Semigraphic View</title>
+<pre>
+A (initiator) B (acceptor)
+
+TCP connect -----------------------------------------&gt;
+ TCP accept
+
+send_name -----------------------------------------&gt;
+ recv_name
+
+ &lt;---------------------------------------- send_status
+recv_status
+(if status was 'alive'
+ send_status - - - - - - - - - - - - - - - - - - - -&gt;
+ recv_status)
+ ChB = gen_challenge()
+ (ChB)
+ &lt;---------------------------------------- send_challenge
+recv_challenge
+
+ChA = gen_challenge(),
+OCA = out_cookie(B),
+DiA = gen_digest(ChB,OCA)
+ (ChA, DiA)
+send_challenge_reply --------------------------------&gt;
+ recv_challenge_reply
+ ICB = in_cookie(A),
+ check:
+ DiA == gen_digest
+ (ChB, ICB) ?
+ - if OK:
+ OCB = out_cookie(A),
+ DiB = gen_digest
+ (DiB) (ChA, OCB)
+ &lt;----------------------------------------- send_challenge_ack
+recv_challenge_ack DONE
+ICA = in_cookie(B), - else
+check: CLOSE
+DiB == gen_digest(ChA,ICA) ?
+- if OK
+ DONE
+- else
+ CLOSE
+</pre>
+ </section>
+ <marker id="dflags"/>
+ <section>
+ <title>The Currently Defined Distribution Flags</title>
+ <p>
+ Currently (OTP-R16) the following capability flags are defined:
+ </p>
+<pre>
+%% The node should be published and part of the global namespace
+-define(DFLAG_PUBLISHED,1).
+
+%% The node implements an atom cache (obsolete)
+-define(DFLAG_ATOM_CACHE,2).
+
+%% The node implements extended (3 * 32 bits) references. This is
+%% required today. If not present connection will be refused.
+-define(DFLAG_EXTENDED_REFERENCES,4).
+
+%% The node implements distributed process monitoring.
+-define(DFLAG_DIST_MONITOR,8).
+
+%% The node uses separate tag for fun's (lambdas) in the distribution protocol.
+-define(DFLAG_FUN_TAGS,16#10).
+
+%% The node implements distributed named process monitoring.
+-define(DFLAG_DIST_MONITOR_NAME,16#20).
+
+%% The (hidden) node implements atom cache (obsolete)
+-define(DFLAG_HIDDEN_ATOM_CACHE,16#40).
+
+%% The node understand new fun-tags
+-define(DFLAG_NEW_FUN_TAGS,16#80).
+
+%% The node is capable of handling extended pids and ports. This is
+%% required today. If not present connection will be refused.
+-define(DFLAG_EXTENDED_PIDS_PORTS,16#100).
+
+%%
+-define(DFLAG_EXPORT_PTR_TAG,16#200).
+
+%%
+-define(DFLAG_BIT_BINARIES,16#400).
+
+%% The node understands new float format
+-define(DFLAG_NEW_FLOATS,16#800).
+
+%%
+-define(DFLAG_UNICODE_IO,16#1000).
+
+%% The node implements atom cache in distribution header.
+-define(DFLAG_DIST_HDR_ATOM_CACHE,16#2000).
+
+%% The node understand the SMALL_ATOM_EXT tag
+-define(DFLAG_SMALL_ATOM_TAGS, 16#4000).
+
+%% The node understand UTF-8 encoded atoms
+-define(DFLAG_UTF8_ATOMS, 16#10000).
+
+</pre>
+ </section>
</section>
<section>
diff --git a/erts/doc/src/erl_driver.xml b/erts/doc/src/erl_driver.xml
index 4fd74b783e..1212c34586 100644
--- a/erts/doc/src/erl_driver.xml
+++ b/erts/doc/src/erl_driver.xml
@@ -4,7 +4,7 @@
<cref>
<header>
<copyright>
- <year>2001</year><year>2011</year>
+ <year>2001</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -34,6 +34,32 @@
<lib>erl_driver</lib>
<libsummary>API functions for an Erlang driver</libsummary>
<description>
+ <p>An Erlang driver is a library containing a set of native driver
+ callback functions that the Erlang VM calls when certain
+ events occur. There may be multiple instances of a driver, each
+ instance is associated with an Erlang port.</p>
+ <marker id="WARNING"/>
+ <warning><p><em>Use this functionality with extreme care!</em></p>
+ <p>A driver callback is executed as a direct extension of the
+ native code of the VM. Execution is not made in a safe environment.
+ The VM can <em>not</em> provide the same services as provided when
+ executing Erlang code, such as preemptive scheduling or memory
+ protection. If the driver callback function doesn't behave well,
+ the whole VM will misbehave.</p>
+ <list>
+ <item><p>A driver callback that crash will crash the whole VM.</p></item>
+ <item><p>An erroneously implemented driver callback might cause
+ a VM internal state inconsistency which may cause a crash of the VM,
+ or miscellaneous misbehaviors of the VM at any point after the call
+ to the driver callback.</p></item>
+ <item><p>A driver callback that do <seealso marker="#lengthy_work">lengthy
+ work</seealso> before returning will degrade responsiveness of the VM,
+ and may cause miscellaneous strange behaviors. Such strange behaviors
+ include, but are not limited to, extreme memory usage, and bad load
+ balancing between schedulers. Strange behaviors that might occur due
+ to lengthy work may also vary between OTP releases.</p></item>
+ </list>
+ </warning>
<p>As of erts version 5.5.3 the driver interface has been extended
(see <seealso marker="driver_entry#extended_marker">extended marker</seealso>).
The extended interface introduce
@@ -53,16 +79,12 @@
<p>The driver calls back to the emulator, using the API
functions declared in <c>erl_driver.h</c>. They are used for
outputting data from the driver, using timers, etc.</p>
- <p>A driver is a library with a set of function that the emulator
- calls, in response to Erlang functions and message
- sending. There may be multiple instances of a driver, each
- instance is connected to an Erlang port. Every port has a port
- owner process. Communication with the port is normally done
- through the port owner process.</p>
- <p>Most of the functions take the <c>port</c> handle as an
- argument. This identifies the driver instance. Note that this
- port handle must be stored by the driver, it is not given when
- the driver is called from the emulator (see
+ <p>Each driver instance is associated with a port. Every port
+ has a port owner process. Communication with the port is normally
+ done through the port owner process. Most of the functions take
+ the <c>port</c> handle as an argument. This identifies the driver
+ instance. Note that this port handle must be stored by the driver,
+ it is not given when the driver is called from the emulator (see
<seealso marker="driver_entry#emulator">driver_entry</seealso>).</p>
<p>Some of the functions take a parameter of type
<c>ErlDrvBinary</c>, a driver binary. It should be both
@@ -123,12 +145,35 @@
different threads. This, however, is not a problem for any
function in this API, since the emulator has control over
these threads.</p>
- <note>
- <p>Functions not explicitly documented as thread-safe are
- <em>not</em> thread-safe. Also note that some functions
+ <warning>
+ <p>Functions not explicitly documented as thread safe are
+ <em>not</em> thread safe. Also note that some functions
are <em>only</em> thread safe when used in a runtime
system with SMP support.</p>
- </note>
+ <p>A function not explicitly documented as thread safe may at
+ some point in time have a thread safe implementation in the
+ runtime system. Such an implementation may however change to
+ a thread <em>unsafe</em> implementation at any time <em>without
+ any notice</em> at all.
+ </p>
+ <p><em>Only use functions explicitly documented as thread safe
+ from arbitrary threads.</em></p>
+ </warning>
+ <p><marker id="lengthy_work"/>
+ As mentioned in the <seealso marker="#WARNING">warning</seealso> text at
+ the beginning of this document it is of vital importance that a driver callback
+ does return relatively fast. It is hard to give an exact maximum amount
+ of time that a driver callback is allowed to work, but as a rule of thumb
+ a well behaving driver callback should return before a millisecond has
+ passed. This can be achieved using different approaches.
+ If you have full control over the code that are to execute in the driver
+ callback, the best approach is to divide the work into multiple chunks of
+ work and trigger multiple calls to the
+ <seealso marker="driver_entry#timeout">timeout callback</seealso> using
+ zero timeouts. This might, however, not always be possible, e.g. when
+ calling third party libraries. In this case you typically want to dispatch
+ the work to another thread. Information about thread primitives can be
+ found below.</p>
</description>
<section>
@@ -272,9 +317,9 @@
minor version used by the driver is greater than the one used
by the runtime system.</p>
<p>The emulator will refuse to load a driver that does not use
- the extended driver interface since,
+ the extended driver interface,
to allow for 64-bit capable drivers,
- incompatible type changes for the callbacks
+ since incompatible type changes for the callbacks
<seealso marker="driver_entry#output">output</seealso>,
<seealso marker="driver_entry#control">control</seealso> and
<seealso marker="driver_entry#call">call</seealso>
@@ -1067,7 +1112,7 @@ typedef struct ErlIOVec {
</desc>
</func>
<func>
- <name><ret>ErlDrvBinary*</ret><nametext>driver_alloc_binary(ErlDrvSizeT size)</nametext></name>
+ <name><ret>ErlDrvBinary *</ret><nametext>driver_alloc_binary(ErlDrvSizeT size)</nametext></name>
<fsummary>Allocate a driver binary</fsummary>
<desc>
<marker id="driver_alloc_binary"></marker>
@@ -1087,7 +1132,7 @@ typedef struct ErlIOVec {
</desc>
</func>
<func>
- <name><ret>ErlDrvBinary*</ret><nametext>driver_realloc_binary(ErlDrvBinary *bin, ErlDrvSizeT size)</nametext></name>
+ <name><ret>ErlDrvBinary *</ret><nametext>driver_realloc_binary(ErlDrvBinary *bin, ErlDrvSizeT size)</nametext></name>
<fsummary>Resize a driver binary</fsummary>
<desc>
<marker id="driver_realloc_binary"></marker>
@@ -1277,7 +1322,7 @@ typedef struct ErlIOVec {
</desc>
</func>
<func>
- <name><ret>SysIOVec*</ret><nametext>driver_peekq(ErlDrvPort port, int *vlen)</nametext></name>
+ <name><ret>SysIOVec *</ret><nametext>driver_peekq(ErlDrvPort port, int *vlen)</nametext></name>
<fsummary>Get the driver queue as a vector</fsummary>
<desc>
<marker id="driver_peekq"></marker>
@@ -1481,7 +1526,7 @@ typedef struct ErlIOVec {
</desc>
</func>
<func>
- <name><ret>char*</ret><nametext>erl_errno_id(int error)</nametext></name>
+ <name><ret>char *</ret><nametext>erl_errno_id(int error)</nametext></name>
<fsummary>Get erlang error atom name from error number</fsummary>
<desc>
<marker id="erl_errno_id"></marker>
@@ -1492,16 +1537,81 @@ typedef struct ErlIOVec {
</desc>
</func>
<func>
+ <name><ret>void</ret><nametext>erl_drv_busy_msgq_limits(ErlDrvPort port, ErlDrvSizeT *low, ErlDrvSizeT *high)</nametext></name>
+ <fsummary>Set and get limits for busy port message queue</fsummary>
+ <desc>
+ <marker id="erl_drv_busy_msgq_limits"></marker>
+ <p>Sets and gets limits that will be used for controling the
+ busy state of the port message queue.</p>
+ <p>The port message queue will be set into a busy
+ state when the amount of command data queued on the
+ message queue reaches the <c>high</c> limit. The port
+ message queue will be set into a not busy state when the
+ amount of command data queued on the message queue falls
+ below the <c>low</c> limit. Command data is in this
+ context data passed to the port using either
+ <c>Port ! {Owner, {command, Data}}</c>, or
+ <c>port_command/[2,3]</c>. Note that these limits
+ only concerns command data that have not yet reached the
+ port. The <seealso marker="#set_busy_port">busy port</seealso>
+ feature can be used for data that has reached the port.</p>
+
+ <p>Valid limits are values in the range
+ <c>[ERL_DRV_BUSY_MSGQ_LIM_MIN, ERL_DRV_BUSY_MSGQ_LIM_MAX]</c>.
+ Limits will be automatically adjusted to be sane. That is,
+ the system will adjust values so that the low limit used is
+ lower than or equal to the high limit used. By default the high
+ limit will be 8 kB and the low limit will be 4 kB.</p>
+
+ <p>By passing a pointer to an integer variable containing
+ the value <c>ERL_DRV_BUSY_MSGQ_READ_ONLY</c>, currently used
+ limit will be read and written back to the integer variable.
+ A new limit can be set by passing a pointer to an integer
+ variable containing a valid limit. The passed value will be
+ written to the internal limit. The internal limit will then
+ be adjusted. After this the adjusted limit will be written
+ back to the integer variable from which the new value was
+ read. Values are in bytes.</p>
+
+ <p>The busy message queue feature can be disabled either
+ by setting the <c>ERL_DRV_FLAG_NO_BUSY_MSGQ</c>
+ <seealso marker="driver_entry#driver_flags">driver flag</seealso>
+ in the <seealso marker="driver_entry">driver_entry</seealso>
+ used by the driver, or by calling this function with
+ <c>ERL_DRV_BUSY_MSGQ_DISABLED</c> as a limit (either low or
+ high). When this feature has been disabled it cannot be
+ enabled again. When reading the limits both of them
+ will be <c>ERL_DRV_BUSY_MSGQ_DISABLED</c>, if this
+ feature has been disabled.</p>
+
+ <p>Processes sending command data to the port will be suspended
+ if either the port is busy or if the port message queue is
+ busy. Suspended processes will be resumed when neither the
+ port is busy, nor the port message queue is busy.</p>
+
+ <p>For information about busy port functionality
+ see the documentation of the
+ <seealso marker="#set_busy_port">set_busy_port()</seealso>
+ function.</p>
+ </desc>
+ </func>
+ <func>
<name><ret>void</ret><nametext>set_busy_port(ErlDrvPort port, int on)</nametext></name>
<fsummary>Signal or unsignal port as busy</fsummary>
<desc>
<marker id="set_busy_port"></marker>
- <p>This function set and resets the busy status of the port. If
- <c>on</c> is 1, the port is set to busy, if it's 0 the port
- is set to not busy.</p>
- <p>When the port is busy, sending to it with <c>Port ! Data</c>
- or <c>port_command/2</c>, will block the port owner process,
- until the port is signaled as not busy.</p>
+ <p>This function set and unset the busy state of the port. If
+ <c>on</c> is non-zero, the port is set to busy, if it's zero the port
+ is set to not busy. You typically want to combine
+ this feature with the <seealso marker="#erl_drv_busy_msgq_limits">busy
+ port message queue</seealso> functionality.</p>
+ <p>Processes sending command data to the port will be suspended
+ if either the port is busy or if the port message queue
+ is busy. Suspended processes will be resumed when neither the
+ port is busy, nor the port message queue is busy. Command data
+ is in this context data passed to the port using either
+ <c>Port ! {Owner, {command, Data}}</c>, or
+ <c>port_command/[2,3]</c>.</p>
<p>If the
<seealso marker="driver_entry#driver_flags"><![CDATA[ERL_DRV_FLAG_SOFT_BUSY]]></seealso>
has been set in the
@@ -1510,6 +1620,10 @@ typedef struct ErlIOVec {
<seealso marker="erlang#port_command/3">port_command(Port, Data, [force])</seealso>
even though the driver has signaled that it is busy.
</p>
+ <p>For information about busy port message queue functionality
+ see the documentation of the
+ <seealso marker="#erl_drv_busy_msgq_limits">erl_drv_busy_msgq_limits()</seealso>
+ function.</p>
</desc>
</func>
<func>
@@ -1570,6 +1684,8 @@ typedef struct ErlIOVec {
<desc>
<marker id="driver_connected"></marker>
<p>This function returns the port owner process.</p>
+ <p>Note that this function is <em>not</em> thread-safe, not
+ even when the emulator with SMP support is used.</p>
</desc>
</func>
<func>
@@ -1597,22 +1713,32 @@ typedef struct ErlIOVec {
<tag><seealso marker="driver_entry#call">call</seealso></tag>
<item>Called from <c>erlang:port_call/3</c></item>
</taglist>
+ <p>Note that this function is <em>not</em> thread-safe, not
+ even when the emulator with SMP support is used.</p>
</desc>
</func>
<func>
- <name><ret>int</ret><nametext>driver_output_term(ErlDrvPort port, ErlDrvTermData* term, int n)</nametext></name>
+ <name><ret>int</ret><nametext>erl_drv_output_term(ErlDrvTermData port, ErlDrvTermData* term, int n)</nametext></name>
<fsummary>Send term data from driver to port owner</fsummary>
<desc>
- <marker id="driver_output_term"></marker>
+ <marker id="erl_drv_output_term"></marker>
<p>This functions sends data in the special driver term
- format. This is a fast way to deliver term data from a
- driver. It also needs no binary conversion, so the port
- owner process receives data as normal Erlang terms.</p>
+ format to the port owner process. This is a fast way to
+ deliver term data from a driver. It also needs no binary
+ conversion, so the port owner process receives data as
+ normal Erlang terms. The
+ <seealso marker="#erl_drv_send_term">erl_drv_send_term()</seealso>
+ functions can be used for sending to any arbitrary process
+ on the local node.</p>
+ <note><p>Note that the <c>port</c> parameter is <em>not</em>
+ an ordinary port handle, but a port handle converted using
+ <c>driver_mk_port()</c>.</p></note>
<p>The <c>term</c> parameter points to an array of
<c>ErlDrvTermData</c>, with <c>n</c> elements. This array
contains terms described in the driver term format. Every
term consists of one to four elements in the array. The
- term first has a term type, and then arguments.</p>
+ term first has a term type, and then arguments. The
+ <c>port</c> parameter specifies the sending port.</p>
<p>Tuple and lists (with the exception of strings, see below),
are built in reverse polish notation, so that to build a
tuple, the elements are given first, and then the tuple
@@ -1664,17 +1790,17 @@ ERL_DRV_EXT2TERM char *buf, ErlDrvUInt len
ErlDrvPort port = ...
ErlDrvTermData spec[] = {
ERL_DRV_ATOM, driver_mk_atom("tcp"),
- ERL_DRV_PORT, driver_mk_port(port),
+ ERL_DRV_PORT, driver_mk_port(drvport),
ERL_DRV_INT, 100,
ERL_DRV_BINARY, bin, 50, 0,
ERL_DRV_LIST, 2,
ERL_DRV_TUPLE, 3,
};
- driver_output_term(port, spec, sizeof(spec) / sizeof(spec[0]));
+ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0]));
]]>
</code>
<p>Where <c>bin</c> is a driver binary of length at least 50
- and <c>port</c> is a port handle. Note that the <c>ERL_DRV_LIST</c>
+ and <c>drvport</c> is a port handle. Note that the <c>ERL_DRV_LIST</c>
comes after the elements of the list, likewise the
<c>ERL_DRV_TUPLE</c>.</p>
<p>The term <c>ERL_DRV_STRING_CONS</c> is a way to construct
@@ -1695,7 +1821,7 @@ ERL_DRV_EXT2TERM char *buf, ErlDrvUInt len
ERL_DRV_NIL,
ERL_DRV_LIST, 4
};
- driver_output_term(port, spec, sizeof(spec) / sizeof(spec[0]));
+ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0]));
]]></code>
<p></p>
<code type="none"><![CDATA[
@@ -1705,7 +1831,7 @@ ERL_DRV_EXT2TERM char *buf, ErlDrvUInt len
ERL_DRV_STRING_CONS, (ErlDrvTermData)"123", 3,
ERL_DRV_STRING_CONS, (ErlDrvTermData)"abc", 3,
};
- driver_output_term(port, spec, sizeof(spec) / sizeof(spec[0]));
+ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0]));
]]></code>
<p>The <c>ERL_DRV_EXT2TERM</c> term type is used for passing a
term encoded with the
@@ -1725,7 +1851,7 @@ ERL_DRV_EXT2TERM char *buf, ErlDrvUInt len
ERL_DRV_EXT2TERM, (ErlDrvTermData) binp->orig_bytes, binp->orig_size
ERL_DRV_TUPLE, 2,
};
- driver_output_term(port, spec, sizeof(spec) / sizeof(spec[0]));
+ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0]));
]]></code>
<p>If you want to pass a binary and don't already have the content
of the binary in an <c>ErlDrvBinary</c>, you can benefit from using
@@ -1741,6 +1867,22 @@ ERL_DRV_EXT2TERM char *buf, ErlDrvUInt len
<c>ERL_DRV_EXT2TERM</c> term types were introduced in the 5.6
version of erts.
</p>
+ <p>This function is only thread-safe when the emulator with SMP
+ support is used.</p>
+ </desc>
+ </func>
+ <func>
+ <name><ret>int</ret><nametext>driver_output_term(ErlDrvPort port, ErlDrvTermData* term, int n)</nametext></name>
+ <fsummary>Send term data from driver to port owner</fsummary>
+ <desc>
+ <marker id="driver_output_term"></marker>
+ <warning><p><c>driver_output_term()</c> is deferred and will
+ be removed in the OTP-R17 release. Use
+ <seealso marker="#erl_drv_send_term">erl_drv_output_term()</seealso>
+ instead.</p>
+ </warning>
+ <p>The parameters <c>term</c> and <c>n</c> do the same thing
+ as in <seealso marker="#erl_drv_output_term">erl_drv_output_term()</seealso>.</p>
<p>Note that this function is <em>not</em> thread-safe, not
even when the emulator with SMP support is used.</p>
</desc>
@@ -1754,6 +1896,8 @@ ERL_DRV_EXT2TERM char *buf, ErlDrvUInt len
<c>string</c>. The atom is created and won't change, so the
return value may be saved and reused, which is faster than
looking up the atom several times.</p>
+ <p>Note that this function is <em>not</em> thread-safe, not
+ even when the emulator with SMP support is used.</p>
</desc>
</func>
<func>
@@ -1762,20 +1906,46 @@ ERL_DRV_EXT2TERM char *buf, ErlDrvUInt len
<desc>
<marker id="driver_mk_port"></marker>
<p>This function converts a port handle to the erlang term
- format, usable in the <c>driver_output_send</c> function.</p>
+ format, usable in the <seealso marker="#erl_drv_output_term">erl_drv_output_term()</seealso>, and <seealso marker="#erl_drv_send_term">erl_drv_send_term()</seealso> functions.</p>
+ <p>Note that this function is <em>not</em> thread-safe, not
+ even when the emulator with SMP support is used.</p>
</desc>
</func>
<func>
- <name><ret>int</ret><nametext>driver_send_term(ErlDrvPort port, ErlDrvTermData receiver, ErlDrvTermData* term, int n)</nametext></name>
+ <name><ret>int</ret><nametext>erl_drv_send_term(ErlDrvTermData port, ErlDrvTermData receiver, ErlDrvTermData* term, int n)</nametext></name>
<fsummary>Send term data to other process than port owner process</fsummary>
<desc>
- <marker id="driver_send_term"></marker>
+ <marker id="erl_drv_send_term"></marker>
<p>This function is the only way for a driver to send data to
<em>other</em> processes than the port owner process. The
<c>receiver</c> parameter specifies the process to receive
the data.</p>
+ <note><p>Note that the <c>port</c> parameter is <em>not</em>
+ an ordinary port handle, but a port handle converted using
+ <c>driver_mk_port()</c>.</p></note>
+ <p>The parameters <c>port</c>, <c>term</c> and <c>n</c> do the same thing
+ as in <seealso marker="#erl_drv_output_term">erl_drv_output_term()</seealso>.</p>
+ <p>This function is only thread-safe when the emulator with SMP
+ support is used.</p>
+ </desc>
+ </func>
+ <func>
+ <name><ret>int</ret><nametext>driver_send_term(ErlDrvPort port, ErlDrvTermData receiver, ErlDrvTermData* term, int n)</nametext></name>
+ <fsummary>Send term data to other process than port owner process</fsummary>
+ <desc>
+ <marker id="driver_send_term"></marker>
+ <warning><p><c>driver_send_term()</c> is deferred and will
+ be removed in the OTP-R17 release. Use
+ <seealso marker="#erl_drv_send_term">erl_drv_send_term()</seealso>
+ instead.</p>
+ <p>Also note that parameters of <c>driver_send_term()</c>
+ cannot be properly checked by the runtime system when
+ executed by arbitrary threads. This may cause the
+ <c>driver_send_term()</c> function not to fail when
+ it should.</p>
+ </warning>
<p>The parameters <c>term</c> and <c>n</c> do the same thing
- as in <seealso marker="#driver_output_term">driver_output_term</seealso>.</p>
+ as in <seealso marker="#erl_drv_output_term">erl_drv_output_term()</seealso>.</p>
<p>This function is only thread-safe when the emulator with SMP
support is used.</p>
</desc>
diff --git a/erts/doc/src/erl_ext_dist.xml b/erts/doc/src/erl_ext_dist.xml
index fd2da2cfe3..28afea8b29 100644
--- a/erts/doc/src/erl_ext_dist.xml
+++ b/erts/doc/src/erl_ext_dist.xml
@@ -119,10 +119,39 @@
<cell align="center">Data</cell>
</row>
<tcaption></tcaption></table>
+ <marker id="utf8_atoms"/>
+ <note>
+ <p>As of ERTS version 5.10 (OTP-R16) support
+ for UTF-8 encoded atoms has been introduced in the external format.
+ However, only characters that can be encoded using Latin1 (ISO-8859-1)
+ are currently supported in atoms. The support for UTF-8 encoded atoms
+ in the external format has been implemented in order to be able to support
+ all Unicode characters in atoms in <em>some future release</em>. Full
+ support for Unicode atoms will not happen before OTP-R18, and might
+ be introduced even later than that. Until full Unicode support for
+ atoms has been introduced, it is an <em>error</em> to pass atoms containing
+ characters that cannot be encoded in Latin1, and <em>the behavior is
+ undefined</em>.</p>
+ <p>When the
+ <seealso marker="erl_dist_protocol#dflags"><c>DFLAG_UTF8_ATOMS</c></seealso>
+ distribution flag has been exchanged between both nodes in the
+ <seealso marker="erl_dist_protocol#distribution_handshake">distribution handshake</seealso>,
+ all atoms in the distribution header will be encoded in UTF-8; otherwise,
+ all atoms in the distribution header will be encoded in Latin1. The two
+ new tags <seealso marker="#ATOM_UTF8_EXT">ATOM_UTF8_EXT</seealso>, and
+ <seealso marker="#SMALL_ATOM_UTF8_EXT">SMALL_ATOM_UTF8_EXT</seealso>
+ will only be used if the <c>DFLAG_UTF8_ATOMS</c> distribution flag has
+ been exchanged between nodes, or if an atom containing characters
+ that cannot be encoded in Latin1 is encountered.
+ </p>
+ <p>The maximum number of allowed characters in an atom is 255. In the
+ UTF-8 case each character may need 4 bytes to be encoded.
+ </p>
+ </note>
</section>
- <section>
- <marker id="distribution_header"/>
+ <marker id="distribution_header"/>
+ <section>
<title>Distribution header</title>
<p>
As of erts version 5.7.2 the old atom cache protocol was
@@ -219,8 +248,7 @@
<p>
The least significant bit in that half byte is the <c>LongAtoms</c>
flag. If it is set, 2 bytes are used for atom lengths instead of
- 1 byte in the distribution header. However, the current emulator
- cannot handle long atoms, so it will currently always be 0.
+ 1 byte in the distribution header.
</p>
<p>
After the <c>Flags</c> field follow the <c>AtomCacheRefs</c>. The
@@ -247,16 +275,26 @@
<p>
<c>InternalSegmentIndex</c> together with the <c>SegmentIndex</c>
completely identify the location of an atom cache entry in the
- atom cache. <c>Length</c> is number of one byte characters that
- the atom text consists of. Length is a two byte big endian integer
+ atom cache. <c>Length</c> is number of bytes that <c>AtomText</c>
+ consists of. Length is a two byte big endian integer
if the <c>LongAtoms</c> flag has been set, otherwise a one byte
- integer. Subsequent <c>CachedAtomRef</c>s with the same
+ integer. When the
+ <seealso marker="erl_dist_protocol#dflags"><c>DFLAG_UTF8_ATOMS</c></seealso>
+ distribution flag has been exchanged between both nodes in the
+ <seealso marker="erl_dist_protocol#distribution_handshake">distribution handshake</seealso>,
+ characters in <c>AtomText</c> is encoded in UTF-8; otherwise,
+ encoded in Latin1. Subsequent <c>CachedAtomRef</c>s with the same
<c>SegmentIndex</c> and <c>InternalSegmentIndex</c> as this
<c>NewAtomCacheRef</c> will refer to this atom until a new
<c>NewAtomCacheRef</c> with the same <c>SegmentIndex</c>
and <c>InternalSegmentIndex</c> appear.
</p>
<p>
+ For more information on encoding of atoms, see
+ <seealso marker="#utf8_atoms">note on UTF-8 encoded atoms</seealso>
+ in the beginning of this document.
+ </p>
+ <p>
If the <c>NewCacheEntryFlag</c> for the next <c>AtomCacheRef</c>
has not been set, a <c>CachedAtomRef</c> on the following format
will follow:
@@ -383,9 +421,9 @@
<tcaption></tcaption></table>
<p>
An atom is stored with a 2 byte unsigned length in big-endian order,
- followed by <c>Len</c> numbers of 8 bit characters that forms the
- <c>AtomName</c>.
- Note: The maximum allowed value for <c>Len</c> is 255.
+ followed by <c>Len</c> numbers of 8 bit Latin1 characters that forms
+ the <c>AtomName</c>.
+ <em>Note</em>: The maximum allowed value for <c>Len</c> is 255.
</p>
</section>
@@ -754,12 +792,14 @@
<tcaption></tcaption></table>
<p>
An atom is stored with a 1 byte unsigned length,
- followed by <c>Len</c> numbers of 8 bit characters that
+ followed by <c>Len</c> numbers of 8 bit Latin1 characters that
forms the <c>AtomName</c>. Longer atoms can be represented
by <seealso marker="#ATOM_EXT">ATOM_EXT</seealso>. <em>Note</em>
the <c>SMALL_ATOM_EXT</c> was introduced in erts version 5.7.2 and
- require a small atom distribution flag exchanged in the distribution
- handshake.
+ require an exchange of the
+ <seealso marker="erl_dist_protocol#dflags"><c>DFLAG_SMALL_ATOM_TAGS</c></seealso>
+ distribution flag in the
+ <seealso marker="erl_dist_protocol#distribution_handshake">distribution handshake</seealso>.
</p>
</section>
@@ -1007,7 +1047,62 @@
This term is used in minor version 1 of the external format.
</p>
</section>
+ <section>
+ <marker id="ATOM_UTF8_EXT"/>
+ <title>ATOM_UTF8_EXT</title>
+
+ <table align="left">
+ <row>
+ <cell align="center">1</cell>
+ <cell align="center">2</cell>
+ <cell align="center">Len</cell>
+ </row>
+ <row>
+ <cell align="center"><c>118</c></cell>
+ <cell align="center"><c>Len</c></cell>
+ <cell align="center"><c>AtomName</c></cell>
+ </row>
+ <tcaption></tcaption></table>
+ <p>
+ An atom is stored with a 2 byte unsigned length in big-endian order,
+ followed by <c>Len</c> bytes containing the <c>AtomName</c> encoded
+ in UTF-8.
+ </p>
+ <p>
+ For more information on encoding of atoms, see
+ <seealso marker="#utf8_atoms">note on UTF-8 encoded atoms</seealso>
+ in the beginning of this document.
+ </p>
+ </section>
+ <section>
+ <marker id="SMALL_ATOM_UTF8_EXT"/>
+ <title>SMALL_ATOM_UTF8_EXT</title>
+
+ <table align="left">
+ <row>
+ <cell align="center">1</cell>
+ <cell align="center">1</cell>
+ <cell align="center">Len</cell>
+ </row>
+ <row>
+ <cell align="center"><c>119</c></cell>
+ <cell align="center"><c>Len</c></cell>
+ <cell align="center"><c>AtomName</c></cell>
+ </row>
+ <tcaption></tcaption></table>
+ <p>
+ An atom is stored with a 1 byte unsigned length,
+ followed by <c>Len</c> bytes containing the <c>AtomName</c> encoded
+ in UTF-8. Longer atoms encoded in UTF-8 can be represented using
+ <seealso marker="#ATOM_UTF8_EXT">ATOM_UTF8_EXT</seealso>.
+ </p>
+ <p>
+ For more information on encoding of atoms, see
+ <seealso marker="#utf8_atoms">note on UTF-8 encoded atoms</seealso>
+ in the beginning of this document.
+ </p>
+ </section>
</chapter>
diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml
index 5fc6508aad..f00f7b9f46 100644
--- a/erts/doc/src/erl_nif.xml
+++ b/erts/doc/src/erl_nif.xml
@@ -4,7 +4,7 @@
<cref>
<header>
<copyright>
- <year>2001</year><year>2011</year>
+ <year>2001</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -34,30 +34,6 @@
<lib>erl_nif</lib>
<libsummary>API functions for an Erlang NIF library</libsummary>
<description>
- <note><p>The NIF concept is officially supported from R14B. NIF source code
- written for earlier experimental versions might need adaption to run on R14B.</p>
- <p>No incompatible changes between <em>R14B</em> and R14A.</p>
- <p>Incompatible changes between <em>R14A</em> and R13B04:</p>
- <list>
- <item>Environment argument removed for <c>enif_alloc</c>,
- <c>enif_realloc</c>, <c>enif_free</c>, <c>enif_alloc_binary</c>,
- <c>enif_realloc_binary</c>, <c>enif_release_binary</c>,
- <c>enif_alloc_resource</c>, <c>enif_release_resource</c>,
- <c>enif_is_identical</c> and <c>enif_compare</c>.</item>
- <item>Character encoding argument added to <c>enif_get_atom</c>
- and <c>enif_make_existing_atom</c>.</item>
- <item>Module argument added to <c>enif_open_resource_type</c>
- while changing name spaces of resource types from global to module local.</item>
- </list>
- <p>Incompatible changes between <em>R13B04</em> and R13B03:</p>
- <list>
- <item>The function prototypes of the NIFs have changed to expect <c>argc</c> and <c>argv</c>
- arguments. The arity of a NIF is by that no longer limited to 3.</item>
- <item><c>enif_get_data</c> renamed as <c>enif_priv_data</c>.</item>
- <item><c>enif_make_string</c> got a third argument for character encoding.</item>
- </list>
- </note>
-
<p>A NIF library contains native implementation of some functions
of an Erlang module. The native implemented functions (NIFs) are
called like any other functions without any difference to the
@@ -67,6 +43,57 @@
is to throw an exception. But it can also be used as a fallback
implementation if the NIF library is not implemented for some
architecture.</p>
+ <marker id="WARNING"/>
+ <warning><p><em>Use this functionality with extreme care!</em></p>
+ <p>A native function is executed as a direct extension of the
+ native code of the VM. Execution is not made in a safe environment.
+ The VM can <em>not</em> provide the same services as provided when
+ executing Erlang code, such as preemptive scheduling or memory
+ protection. If the native function doesn't behave well, the whole
+ VM will misbehave.</p>
+ <list>
+ <item><p>A native function that crash will crash the whole VM.</p></item>
+ <item><p>An erroneously implemented native function might cause
+ a VM internal state inconsistency which may cause a crash of the VM,
+ or miscellaneous misbehaviors of the VM at any point after the call
+ to the native function.</p></item>
+ <item><p>A native function that do <seealso marker="#lengthy_work">lengthy
+ work</seealso> before returning will degrade responsiveness of the VM,
+ and may cause miscellaneous strange behaviors. Such strange behaviors
+ include, but are not limited to, extreme memory usage, and bad load
+ balancing between schedulers. Strange behaviors that might occur due
+ to lengthy work may also vary between OTP releases.</p></item>
+ </list>
+ </warning>
+
+ <p>The NIF concept is officially supported from R14B. NIF source code
+ written for earlier experimental versions might need adaption to run on R14B
+ or later versions:</p>
+ <list>
+ <item>No incompatible changes between <em>R14B</em> and R14A.</item>
+ <item>Incompatible changes between <em>R14A</em> and R13B04:
+ <list>
+ <item>Environment argument removed for <c>enif_alloc</c>,
+ <c>enif_realloc</c>, <c>enif_free</c>, <c>enif_alloc_binary</c>,
+ <c>enif_realloc_binary</c>, <c>enif_release_binary</c>,
+ <c>enif_alloc_resource</c>, <c>enif_release_resource</c>,
+ <c>enif_is_identical</c> and <c>enif_compare</c>.</item>
+ <item>Character encoding argument added to <c>enif_get_atom</c>
+ and <c>enif_make_existing_atom</c>.</item>
+ <item>Module argument added to <c>enif_open_resource_type</c>
+ while changing name spaces of resource types from global to module local.</item>
+ </list>
+ </item>
+ <item>Incompatible changes between <em>R13B04</em> and R13B03:
+ <list>
+ <item>The function prototypes of the NIFs have changed to expect <c>argc</c> and <c>argv</c>
+ arguments. The arity of a NIF is by that no longer limited to 3.</item>
+ <item><c>enif_get_data</c> renamed as <c>enif_priv_data</c>.</item>
+ <item><c>enif_make_string</c> got a third argument for character encoding.</item>
+ </list>
+ </item>
+ </list>
+
<p>A minimal example of a NIF library can look like this:</p>
<p/>
<code type="none">
@@ -136,7 +163,23 @@ ok
then retrieved by calling <seealso marker="#enif_priv_data">enif_priv_data</seealso>.</p>
<p>There is no way to explicitly unload a NIF library. A library will be
automatically unloaded when the module code that it belongs to is purged
- by the code server.</p>
+ by the code server.</p>
+
+ <p><marker id="lengthy_work"/>
+ As mentioned in the <seealso marker="#WARNING">warning</seealso> text at
+ the beginning of this document it is of vital importance that a native function
+ does return relatively fast. It is hard to give an exact maximum amount
+ of time that a native function is allowed to work, but as a rule of thumb
+ a well behaving native function should return to its caller before a
+ millisecond has passed. This can be achieved using different approaches.
+ If you have full control over the code that are to execute in the native
+ function, the best approach is to divide the work into multiple chunks of
+ work and call the native function multiple times. This might, however,
+ not always be possible, e.g. when calling third party libraries. In this
+ case you typically want to dispatch the work to another thread, return
+ from the native function, and wait for the result. The thread can send
+ the result back to the calling thread using message passing. Information
+ about thread primitives can be found below.</p>
</description>
<section>
<title>FUNCTIONALITY</title>
@@ -266,10 +309,6 @@ ok
mutable.</p>
<p>The library initialization callbacks <c>load</c>, <c>reload</c> and
<c>upgrade</c> are all thread-safe even for shared state data.</p>
- <p>Avoid doing lengthy work in NIF calls as that may degrade the
- responsiveness of the VM. NIFs are called directly by the same scheduler
- thread that executed the calling Erlang code. The calling scheduler will thus
- be blocked from doing any other work until the NIF returns.</p>
</item>
</taglist>
</section>
@@ -472,7 +511,7 @@ typedef enum {
</section>
<funcs>
- <func><name><ret>void*</ret><nametext>enif_alloc(size_t size)</nametext></name>
+ <func><name><ret>void *</ret><nametext>enif_alloc(size_t size)</nametext></name>
<fsummary>Allocate dynamic memory.</fsummary>
<desc><p>Allocate memory of <c>size</c> bytes. Return NULL if allocation failed.</p></desc>
</func>
@@ -489,7 +528,7 @@ typedef enum {
<p>Return true on success or false if allocation failed.</p>
</desc>
</func>
- <func><name><ret>ErlNifEnv*</ret><nametext>enif_alloc_env()</nametext></name>
+ <func><name><ret>ErlNifEnv *</ret><nametext>enif_alloc_env()</nametext></name>
<fsummary>Create a new environment</fsummary>
<desc><p>Allocate a new process independent environment. The environment can
be used to hold terms that is not bound to any process. Such terms can
@@ -499,7 +538,7 @@ typedef enum {
<p>Return pointer to the new environment.</p>
</desc>
</func>
- <func><name><ret>void*</ret><nametext>enif_alloc_resource(ErlNifResourceType* type, unsigned size)</nametext></name>
+ <func><name><ret>void *</ret><nametext>enif_alloc_resource(ErlNifResourceType* type, unsigned size)</nametext></name>
<fsummary>Allocate a memory managed resource object</fsummary>
<desc><p>Allocate a memory managed resource object of type <c>type</c> and size <c>size</c> bytes.</p></desc>
</func>
@@ -522,7 +561,7 @@ typedef enum {
<desc><p>Same as <seealso marker="erl_driver#erl_drv_cond_broadcast">erl_drv_cond_broadcast</seealso>.
</p></desc>
</func>
- <func><name><ret>ErlNifCond*</ret><nametext>enif_cond_create(char *name)</nametext></name>
+ <func><name><ret>ErlNifCond *</ret><nametext>enif_cond_create(char *name)</nametext></name>
<fsummary></fsummary>
<desc><p>Same as <seealso marker="erl_driver#erl_drv_cond_create">erl_drv_cond_create</seealso>.
</p></desc>
@@ -840,7 +879,7 @@ typedef enum {
<fsummary>Create an integer term from a long int</fsummary>
<desc><p>Create an integer term from a <c>long int</c>.</p></desc>
</func>
- <func><name><ret>unsigned char*</ret><nametext>enif_make_new_binary(ErlNifEnv* env, size_t size, ERL_NIF_TERM* termp)</nametext></name>
+ <func><name><ret>unsigned char *</ret><nametext>enif_make_new_binary(ErlNifEnv* env, size_t size, ERL_NIF_TERM* termp)</nametext></name>
<fsummary>Allocate and create a new binary term</fsummary>
<desc><p>Allocate a binary of size <c>size</c> bytes and create an owning
term. The binary data is mutable until the calling NIF returns. This is a
@@ -951,7 +990,7 @@ typedef enum {
<fsummary>Create an integer term from an unsigned long int</fsummary>
<desc><p>Create an integer term from an <c>unsigned long int</c>.</p></desc>
</func>
- <func><name><ret>ErlNifMutex*</ret><nametext>enif_mutex_create(char *name)</nametext></name>
+ <func><name><ret>ErlNifMutex *</ret><nametext>enif_mutex_create(char *name)</nametext></name>
<fsummary></fsummary>
<desc><p>Same as <seealso marker="erl_driver#erl_drv_mutex_create">erl_drv_mutex_create</seealso>.
</p></desc>
@@ -976,7 +1015,7 @@ typedef enum {
<desc><p>Same as <seealso marker="erl_driver#erl_drv_mutex_unlock">erl_drv_mutex_unlock</seealso>.
</p></desc>
</func>
- <func><name><ret>ErlNifResourceType*</ret><nametext>enif_open_resource_type(ErlNifEnv* env,
+ <func><name><ret>ErlNifResourceType *</ret><nametext>enif_open_resource_type(ErlNifEnv* env,
const char* module_str, const char* name,
ErlNifResourceDtor* dtor, ErlNifResourceFlags flags, ErlNifResourceFlags* tried)</nametext></name>
<fsummary>Create or takeover a resource type</fsummary>
@@ -1005,7 +1044,7 @@ typedef enum {
and <seealso marker="#upgrade">upgrade</seealso>.</p>
</desc>
</func>
- <func><name><ret>void*</ret><nametext>enif_priv_data(ErlNifEnv* env)</nametext></name>
+ <func><name><ret>void *</ret><nametext>enif_priv_data(ErlNifEnv* env)</nametext></name>
<fsummary>Get the private data of a NIF library</fsummary>
<desc><p>Return the pointer to the private data that was set by <c>load</c>,
<c>reload</c> or <c>upgrade</c>.</p>
@@ -1033,7 +1072,7 @@ typedef enum {
References made by <seealso marker="#enif_make_resource">enif_make_resource</seealso>
can only be removed by the garbage collector.</p></desc>
</func>
- <func><name><ret>ErlNifRWLock*</ret><nametext>enif_rwlock_create(char *name)</nametext></name>
+ <func><name><ret>ErlNifRWLock *</ret><nametext>enif_rwlock_create(char *name)</nametext></name>
<fsummary></fsummary>
<desc><p>Same as <seealso marker="erl_driver#erl_drv_rwlock_create">erl_drv_rwlock_create</seealso>.
</p></desc>
@@ -1073,7 +1112,7 @@ typedef enum {
<desc><p>Same as <seealso marker="erl_driver#erl_drv_rwlock_tryrwlock">erl_drv_rwlock_tryrwlock</seealso>.
</p></desc>
</func>
- <func><name><ret>ErlNifPid*</ret><nametext>enif_self(ErlNifEnv* caller_env, ErlNifPid* pid)</nametext></name>
+ <func><name><ret>ErlNifPid *</ret><nametext>enif_self(ErlNifEnv* caller_env, ErlNifPid* pid)</nametext></name>
<fsummary>Get the pid of the calling process.</fsummary>
<desc><p>Initialize the pid variable <c>*pid</c> to represent the
calling process. Return <c>pid</c>.</p></desc>
@@ -1129,7 +1168,7 @@ typedef enum {
<desc><p>Same as <seealso marker="erl_driver#erl_drv_thread_join">erl_drv_thread_join </seealso>.
</p></desc>
</func>
- <func><name><ret>ErlNifThreadOpts*</ret><nametext>enif_thread_opts_create(char *name)</nametext></name>
+ <func><name><ret>ErlNifThreadOpts *</ret><nametext>enif_thread_opts_create(char *name)</nametext></name>
<fsummary></fsummary>
<desc><p>Same as <seealso marker="erl_driver#erl_drv_thread_opts_create">erl_drv_thread_opts_create</seealso>.
</p></desc>
@@ -1154,7 +1193,7 @@ typedef enum {
<desc><p>Same as <seealso marker="erl_driver#erl_drv_tsd_key_destroy">erl_drv_tsd_key_destroy</seealso>.
</p></desc>
</func>
- <func><name><ret>void*</ret><nametext>enif_tsd_get(ErlNifTSDKey key)</nametext></name>
+ <func><name><ret>void *</ret><nametext>enif_tsd_get(ErlNifTSDKey key)</nametext></name>
<fsummary></fsummary>
<desc><p>Same as <seealso marker="erl_driver#erl_drv_tsd_get">erl_drv_tsd_get</seealso>.
</p></desc>
diff --git a/erts/doc/src/erl_set_memory_block.xml b/erts/doc/src/erl_set_memory_block.xml
deleted file mode 100644
index d77da56d95..0000000000
--- a/erts/doc/src/erl_set_memory_block.xml
+++ /dev/null
@@ -1,172 +0,0 @@
-<?xml version="1.0" encoding="latin1" ?>
-<!DOCTYPE cref SYSTEM "cref.dtd">
-
-<cref>
- <header>
- <copyright>
- <year>1998</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>erl_set_memory_block</title>
- <prepared>Patrik Nyblom</prepared>
- <responsible></responsible>
- <docno></docno>
- <approved></approved>
- <checked></checked>
- <date>98-08-05</date>
- <rev>A</rev>
- <file>erl_set_memory_block.xml</file>
- </header>
- <lib>erl_set_memory_block</lib>
- <libsummary>Custom memory allocation for Erlang on VxWorks&reg;</libsummary>
- <description>
- <p>This documentation is specific to VxWorks.</p>
- <p>The <c><![CDATA[erl_set_memory_block]]></c> function/command initiates custom
- memory allocation for the Erlang emulator. It has to be called
- before the Erlang emulator is started and makes Erlang use one
- single large memory block for all memory allocation.</p>
- <p>The memory within the block can be utilized by other tasks than
- Erlang. This is accomplished by calling the functions
- <c><![CDATA[sys_alloc]]></c>, <c><![CDATA[sys_realloc]]></c> and <c><![CDATA[sys_free]]></c> instead
- of <c><![CDATA[malloc]]></c>, <c><![CDATA[realloc]]></c> and <c><![CDATA[free]]></c> respectively.</p>
- <p>The purpose of this is to avoid problems inherent in the
- VxWorks systems <c><![CDATA[malloc]]></c> library. The memory allocation within the
- large memory block avoids fragmentation by using an "address
- order first fit" algorithm. Another advantage of using a
- separate memory block is that resource reclamation can be made
- more easily when Erlang is stopped.</p>
- <p>The <c><![CDATA[erl_set_memory_block]]></c> function is callable from any C
- program as an ordinary 10 argument function as well as
- from the commandline.</p>
- </description>
- <funcs>
- <func>
- <name><ret>int</ret><nametext>erl_set_memory_block(size_t size, void *ptr, int warn_mixed_malloc, int realloc_always_moves, int use_reclaim, ...)</nametext></name>
- <fsummary>Specify parameters for Erlang internal memory allocation.</fsummary>
- <desc>
- <p>The function is called before Erlang is
- started to specify a large memory block where Erlang can
- maintain memory internally.</p>
- <p>Parameters:</p>
- <taglist>
- <tag>size_t size</tag>
- <item>The size in bytes of Erlang's internal memory block. Has to
- be specified. Note that the VxWorks system uses dynamic
- memory allocation heavily, so leave some memory to the system.</item>
- <tag>void *ptr</tag>
- <item>
- <p>A pointer to the actual memory block of size
- <c><![CDATA[size]]></c>. If this is specified as 0 (NULL), Erlang will
- allocate the memory when starting and will reclaim the
- memory block (as a whole) when stopped.</p>
- <p>If a memory block is allocated and provided here, the
- <c><![CDATA[sys_alloc]]></c> etc routines can still be used after
- the Erlang emulator is stopped. The Erlang emulator can
- also be restarted while other tasks using the memory
- block are running without destroying the memory. If
- Erlang is to be restarted, also set the
- <c><![CDATA[use_reclaim]]></c> flag.</p>
- <p>If 0 is specified here, the Erlang system should not
- be stopped while some other task uses the memory block
- (has called <c><![CDATA[sys_alloc]]></c>).</p>
- </item>
- <tag>int warn_mixed_malloc</tag>
- <item>
- <p>If this flag is set to true (anything else than 0), the
- system will write a warning message on the console if a
- program is mixing normal <c><![CDATA[malloc]]></c> with
- <c><![CDATA[sys_realloc]]></c> or <c><![CDATA[sys_free]]></c>.</p>
- </item>
- <tag>int realloc_always_moves</tag>
- <item>
- <p>If this flag is set to true (anything else than 0), all
- calls to <c><![CDATA[sys_realloc]]></c> result in a moved memory
- block. This can in certain conditions give less
- fragmentation. This flag may be removed in future releases.</p>
- </item>
- <tag>int use_reclaim</tag>
- <item>
- <p>If this flag is set to true (anything else than 0), all
- memory allocated with <c><![CDATA[sys_alloc]]></c> is automatically
- reclaimed as soon as a task exits. This is very useful
- to make writing port programs (and other programs as
- well) easier. Combine this with using the routines
- <c><![CDATA[save_open]]></c> etc. specified in the reclaim.h
- file delivered in the Erlang distribution.</p>
- </item>
- </taglist>
- <p>Return Value:</p>
- <p>Returns 0 (OK) on success, otherwise a value &lt;&gt; 0.</p>
- </desc>
- </func>
- <func>
- <name><ret>int</ret><nametext>erl_memory_show(...)</nametext></name>
- <fsummary>A utility similar to VxWorks <c><![CDATA[memShow]]></c>, but for the Erlang memory area.</fsummary>
- <desc>
- <p>Return Value:</p>
- <p>Returns 0 (OK) on success, otherwise a value &lt;&gt; 0.</p>
- </desc>
- </func>
- <func>
- <name><ret>int</ret><nametext>erl_mem_info_get(MEM_PART_STATS *stats)</nametext></name>
- <fsummary>A utility similar to VxWorks <c><![CDATA[memPartInfoGet]]></c>, but for the Erlang memory area.</fsummary>
- <desc>
- <p>Parameter:</p>
- <taglist>
- <tag>MEM_PART_STATS *stats</tag>
- <item>A pointer to a MEM_PART_STATS structure as defined in
- <c><![CDATA[<memLib.h>]]></c>. A successful call will fill in all
- fields of the structure, on error all fields are left untouched. </item>
- </taglist>
- <p>Return Value:</p>
- <p>Returns 0 (OK) on success, otherwise a value &lt;&gt; 0</p>
- </desc>
- </func>
- </funcs>
-
- <section>
- <title>NOTES</title>
- <p>The memory block used by Erlang actually does not need to be
- inside the area known to ordinary <c><![CDATA[malloc]]></c>. It is possible
- to set the <c><![CDATA[USER_RESERVED_MEM]]></c> preprocessor symbol when compiling
- the wind kernel and then use user reserved memory for
- Erlang. Erlang can therefor utilize memory above the 32 Mb limit
- of VxWorks on the PowerPC architecture.</p>
- <p>Example:</p>
- <p>In config.h for the wind kernel:</p>
- <code type="none"><![CDATA[
- #undef LOCAL_MEM_AUTOSIZE
- #undef LOCAL_MEM_SIZE
- #undef USER_RESERVED_MEM
-
- #define LOCAL_MEM_SIZE 0x05000000
- #define USER_RESERVED_MEM 0x03000000
- ]]></code>
- <p>In the start-up script/code for the VxWorks node:</p>
- <code type="none"><![CDATA[
-erl_set_memory_block(sysPhysMemTop()-sysMemTop(),sysMemTop(),0,0,1);
- ]]></code>
- <p>Setting the <c><![CDATA[use_reclaim]]></c> flag decreases performance of the
- system, but makes programming much easier. Other similar
- facilities are present in the Erlang system even without using a
- separate memory block. The routines called <c><![CDATA[save_malloc]]></c>,
- <c><![CDATA[save_realloc]]></c> and <c><![CDATA[save_free]]></c> provide the same
- facilities by using VxWorks own <c><![CDATA[malloc]]></c>. Similar routines
- exist for files, see the file <c><![CDATA[reclaim.h]]></c> in the distribution.</p>
- </section>
-</cref>
-
diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml
index b5ddf0e17c..315dc323ba 100644
--- a/erts/doc/src/erlang.xml
+++ b/erts/doc/src/erlang.xml
@@ -277,7 +277,9 @@
the binary contains Unicode characters greater than 16#FF.
In a future release, such Unicode characters might be allowed
and <c>binary_to_atom(<anno>Binary</anno>, utf8)</c>
- will not fail in that case.</p></note>
+ will not fail in that case. For more information on Unicode support in atoms
+ see <seealso marker="erl_ext_dist#utf8_atoms">note on UTF-8 encoded atoms</seealso>
+ in the chapter about the external term format in the ERTS User's Guide.</p></note>
<pre>
> <input>binary_to_atom(&lt;&lt;"Erlang"&gt;&gt;, latin1).</input>
@@ -626,6 +628,22 @@ false</pre>
{more,6}</pre>
</desc>
</func>
+
+ <func>
+ <name name="delete_element" arity="2"/>
+ <fsummary>Delete element at index in a tuple</fsummary>
+ <type_desc variable="Index">1..tuple_size(<anno>Tuple1</anno>)</type_desc>
+ <desc>
+ <p>
+ Returns a new tuple with element at <c><anno>Index</anno></c> removed from
+ tuple <c><anno>Tuple1</anno></c>.
+ </p>
+ <pre>
+> <input>erlang:delete_element(2, {one, two, three}).</input>
+{one,three}</pre>
+ </desc>
+ </func>
+
<func>
<name name="delete_module" arity="1"/>
<fsummary>Make the current code for a module old</fsummary>
@@ -854,10 +872,10 @@ b</pre>
</func>
<func>
<name name="exit" arity="2"/>
- <fsummary>Send an exit signal to a process</fsummary>
+ <fsummary>Send an exit signal to a process or a port</fsummary>
<desc>
<p>Sends an exit signal with exit reason <c><anno>Reason</anno></c> to
- the process <c><anno>Pid</anno></c>.</p>
+ the process or port identified by <c><anno>Pid</anno></c>.</p>
<p>The following behavior apply if <c><anno>Reason</anno></c> is any term
except <c>normal</c> or <c>kill</c>:</p>
<p>If <c><anno>Pid</anno></c> is not trapping exits, <c><anno>Pid</anno></c> itself will
@@ -952,6 +970,37 @@ true
</desc>
</func>
<func>
+ <name>float_to_list(Float, Options) -> string()</name>
+ <fsummary>Text representation of a float formatted using given options</fsummary>
+ <type>
+ <v>Float = float()</v>
+ <v>Options = [Option]</v>
+ <v>Option = {decimals, Decimals::0..249} |
+ {scientific, Decimals::0..249} |
+ compact</v>
+ </type>
+ <desc>
+ <p>Returns a string which corresponds to the text
+ representation of <c>Float</c> using fixed decimal point formatting.
+ When <c>decimals</c> option is specified
+ the returned value will contain at most <c>Decimals</c> number of
+ digits past the decimal point. If the number doesn't fit in the
+ internal static buffer of 256 bytes, the function throws <c>badarg</c>.
+ When <c>compact</c> option is provided
+ the trailing zeros at the end of the list are truncated (this option is
+ only meaningful together with the <c>decimals</c> option). When
+ <c>scientific</c> option is provided, the float will be formatted using
+ scientific notation with <c>Decimals</c> digits of precision. If
+ <c>Options</c> is <c>[]</c> the function behaves like <c>float_to_list/1</c>.
+ </p>
+ <pre>
+> <input>float_to_list(7.12, [{decimals, 4}]).</input>
+"7.1200"
+> <input>float_to_list(7.12, [{decimals, 4}, compact]).</input>
+"7.12"</pre>
+ </desc>
+ </func>
+ <func>
<name name="fun_info" arity="1"/>
<fsummary>Information about a fun</fsummary>
<desc>
@@ -1361,6 +1410,24 @@ os_prompt% </pre>
when the process wakes up.</p>
</desc>
</func>
+
+ <func>
+ <name name="insert_element" arity="3"/>
+ <fsummary>Insert an element at index in a tuple</fsummary>
+ <type_desc variable="Index">1..tuple_size(<anno>Tuple1</anno>) + 1</type_desc>
+ <desc>
+ <p>
+ Returns a new tuple with element <c><anno>Term</anno></c> insert at position
+ <c><anno>Index</anno></c> in tuple <c><anno>Tuple1</anno></c>.
+ All elements from position <c><anno>Index</anno></c> and upwards are subsequently
+ pushed one step higher in the new tuple <c><anno>Tuple2</anno></c>.
+ </p>
+ <pre>
+> <input>erlang:insert_element(2, {one, two, three}, new).</input>
+{one,new,two,three}</pre>
+ </desc>
+ </func>
+
<func>
<name name="integer_to_list" arity="1"/>
<fsummary>Text representation of an integer</fsummary>
@@ -1497,13 +1564,6 @@ os_prompt% </pre>
applied with <c><anno>Arity</anno></c> number of arguments; otherwise
returns <c>false</c>.</p>
<p>Allowed in guard tests.</p>
- <warning>
- <p>Currently, <c>is_function/2</c> will also return
- <c>true</c> if the first argument is a tuple fun (a tuple
- containing two atoms). In a future release, tuple funs will
- no longer be supported and <c>is_function/2</c> will return
- <c>false</c> if given a tuple fun.</p>
- </warning>
</desc>
</func>
<func>
@@ -1654,9 +1714,11 @@ os_prompt% </pre>
<desc>
<p>Returns the atom whose text representation is <c><anno>String</anno></c>.</p>
<p><c><anno>String</anno></c> may only contain ISO-latin-1
- characterns (i.e. numbers below 256) as the current
+ characters (i.e. numbers below 256) as the current
implementation does not allow unicode characters >= 256 in
- atoms.</p>
+ atoms. For more information on Unicode support in atoms
+ see <seealso marker="erl_ext_dist#utf8_atoms">note on UTF-8 encoded atoms</seealso>
+ in the chapter about the external term format in the ERTS User's Guide.</p>
<pre>
> <input>list_to_atom("Erlang").</input>
'Erlang'</pre>
@@ -2465,6 +2527,10 @@ os_prompt% </pre>
<p>It can only be used to check the local time of day if
the time-zone info of the underlying operating system is
properly configured.</p>
+ <p>If you do not need the return value to be unique and
+ monotonically increasing, use
+ <seealso marker="kernel:os#timestamp/0">os:timestamp/0</seealso>
+ instead to avoid some overhead.</p>
</desc>
</func>
<func>
@@ -2606,8 +2672,8 @@ os_prompt% </pre>
<p>This is only valid for <c>{spawn, <anno>Command</anno>}</c> and
<c>{spawn_executable, <anno>FileName</anno>}</c>.
The external program starts using <c><anno>Dir</anno></c> as its
- working directory. <c><anno>Dir</anno></c> must be a string. Not
- available on VxWorks.</p>
+ working directory. <c><anno>Dir</anno></c> must be a string.
+ </p>
</item>
<tag><c>{env, <anno>Env</anno>}</c></tag>
<item>
@@ -2621,8 +2687,12 @@ os_prompt% </pre>
port process. Both <c><anno>Name</anno></c> and <c><anno>Val</anno></c> must be
strings. The one exception is <c><anno>Val</anno></c> being the atom
<c>false</c> (in analogy with <c>os:getenv/1</c>), which
- removes the environment variable. Not available on
- VxWorks.</p>
+ removes the environment variable.
+ </p>
+ <p>If Unicode filename encoding is in effect (see the <seealso
+ marker="erts:erl#file_name_encoding">erl manual
+ page</seealso>), the strings (both <c>Name</c> and
+ <c>Value</c>) may contain characters with codepoints > 255.</p>
</item>
<tag><c>{args, [ string() | binary() ]}</c></tag>
<item>
@@ -2759,6 +2829,18 @@ os_prompt% </pre>
console window when spawning the port program.
(This option has no effect on other platforms.)</p>
</item>
+ <tag><marker id="open_port_parallelism"><c>{parallelism, Boolean}</c></marker></tag>
+ <item>
+ <p>Set scheduler hint for port parallelism. If set to <c>true</c>,
+ the VM will schedule port tasks when it by this can improve the
+ parallelism in the system. If set to <c>false</c>, the VM will
+ try to perform port tasks immediately and by this improving the
+ latency at the expense of parallelism. The default can be set on
+ system startup by passing the
+ <seealso marker="erl#+spp">+spp</seealso> command line argument
+ to <seealso marker="erl">erl(1)</seealso>.
+ </p>
+ </item>
</taglist>
<p>The default is <c>stream</c> for all types of port and
<c>use_stdio</c> for spawned ports.</p>
@@ -2811,10 +2893,11 @@ os_prompt% </pre>
the owning process using signals of the form
<c>{'EXIT', Port, PosixCode}</c>. See <c>file(3)</c> for
possible values of <c>PosixCode</c>.</p>
- <p><marker id="ERL_MAX_PORTS"></marker>
- The maximum number of ports that can be open at the same
- time is 1024 by default, but can be configured by
- the environment variable <c>ERL_MAX_PORTS</c>.</p>
+ <p>The maximum number of ports that can be open at the same
+ time can be configured by passing the
+ <seealso marker="erl#max_ports"><c>+Q</c></seealso>
+ command line flag to
+ <seealso marker="erl"><c>erl(1)</c></seealso>.</p>
</desc>
</func>
<func>
@@ -2872,10 +2955,10 @@ os_prompt% </pre>
<desc>
<p>Closes an open port. Roughly the same as
<c><anno>Port</anno> ! {self(), close}</c> except for the error behaviour
- (see below), and that the port does <em>not</em> reply with
- <c>{Port, closed}</c>. Any process may close a port with
- <c>port_close/1</c>, not only the port owner (the connected
- process).</p>
+ (see below), being synchronous, and that the port does
+ <em>not</em> reply with <c>{Port, closed}</c>. Any process may
+ close a port with <c>port_close/1</c>, not only the port owner
+ (the connected process).</p>
<p>For comparison: <c><anno>Port</anno> ! {self(), close}</c> fails with
<c>badarg</c> if <c><anno>Port</anno></c> cannot be sent to (i.e.,
<c><anno>Port</anno></c> refers neither to a port nor to a process). If
@@ -2887,8 +2970,12 @@ os_prompt% </pre>
<p>Note that any process can close a port using
<c><anno>Port</anno> ! {PortOwner, close}</c> just as if it itself was
the port owner, but the reply always goes to the port owner.</p>
- <p>In short: <c>port_close(Port)</c> has a cleaner and more
- logical behaviour than <c><anno>Port</anno> ! {self(), close}</c>.</p>
+ <p>As of OTP-R16 <c><anno>Port</anno> ! {PortOwner, close}</c> is truly
+ asynchronous. Note that this operation has always been
+ documented as an asynchronous operation, while the underlying
+ implementation has been synchronous. <c>port_close/1</c> is
+ however still fully synchronous. This due to its error
+ behavior.</p>
<p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not an open port or
the registered name of an open port.</p>
</desc>
@@ -2898,11 +2985,11 @@ os_prompt% </pre>
<fsummary>Send data to a port</fsummary>
<desc>
<p>Sends data to a port. Same as
- <c><anno>Port</anno> ! {self(), {command, Data}}</c> except for the error
- behaviour (see below). Any process may send data to a port
- with <c>port_command/2</c>, not only the port owner
- (the connected process).</p>
- <p>For comparison: <c><anno>Port</anno> ! {self(), {command, Data}}</c>
+ <c><anno>Port</anno> ! {PortOwner, {command, Data}}</c> except for the error
+ behaviour and being synchronous (see below). Any process may
+ send data to a port with <c>port_command/2</c>, not only the
+ port owner (the connected process).</p>
+ <p>For comparison: <c><anno>Port</anno> ! {PortOwner, {command, Data}}</c>
fails with <c>badarg</c> if <c><anno>Port</anno></c> cannot be sent to
(i.e., <c><anno>Port</anno></c> refers neither to a port nor to a process).
If <c><anno>Port</anno></c> is a closed port the data message disappears
@@ -2913,11 +3000,14 @@ os_prompt% </pre>
<p>Note that any process can send to a port using
<c><anno>Port</anno> ! {PortOwner, {command, <anno>Data</anno>}}</c> just as if it
itself was the port owner.</p>
- <p>In short: <c>port_command(<anno>Port</anno>, <anno>Data</anno>)</c> has a cleaner and
- more logical behaviour than
- <c><anno>Port</anno> ! {self(), {command, Data}}</c>.</p>
<p>If the port is busy, the calling process will be suspended
until the port is not busy anymore.</p>
+ <p>As of OTP-R16 <c><anno>Port</anno> ! {PortOwner, {command, Data}}</c> is
+ truly asynchronous. Note that this operation has always been
+ documented as an asynchronous operation, while the underlying
+ implementation has been synchronous. <c>port_command/2</c> is
+ however still fully synchronous. This due to its error
+ behavior.</p>
<p>Failures:</p>
<taglist>
<tag><c>badarg</c></tag>
@@ -2991,7 +3081,7 @@ os_prompt% </pre>
<fsummary>Set the owner of a port</fsummary>
<desc>
<p>Sets the port owner (the connected port) to <c><anno>Pid</anno></c>.
- Roughly the same as <c><anno>Port</anno> ! {self(), {connect, <anno>Pid</anno>}}</c>
+ Roughly the same as <c><anno>Port</anno> ! {Owner, {connect, <anno>Pid</anno>}}</c>
except for the following:</p>
<list type="bulleted">
<item>
@@ -3002,6 +3092,9 @@ os_prompt% </pre>
<c>{Port,connected}</c>.</p>
</item>
<item>
+ <p><c>port_connect/1</c> is synchronous, see below.</p>
+ </item>
+ <item>
<p>The new port owner gets linked to the port.</p>
</item>
</list>
@@ -3025,11 +3118,14 @@ os_prompt% </pre>
<c><anno>Port</anno> ! {PortOwner, {connect, <anno>Pid</anno>}}</c> just as if it
itself was the port owner, but the reply always goes to
the port owner.</p>
- <p>In short: <c>port_connect(<anno>Port</anno>, <anno>Pid</anno>)</c> has a cleaner and
- more logical behaviour than
- <c><anno>Port</anno> ! {self(),{connect,<anno>Pid</anno>}}</c>.</p>
+ <p>As of OTP-R16 <c><anno>Port</anno> ! {PortOwner, {connect, <anno>Pid</anno>}}</c> is
+ truly asynchronous. Note that this operation has always been
+ documented as an asynchronous operation, while the underlying
+ implementation has been synchronous. <c>port_connect/2</c> is
+ however still fully synchronous. This due to its error
+ behavior.</p>
<p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not an open port
- or the registered name of an open port, or if <c><anno>Pid</anno></c> is
+ or the registered name of an open port, or if <c>Pid</c> is
not an existing local pid.</p>
</desc>
</func>
@@ -3076,71 +3172,187 @@ os_prompt% </pre>
</func>
<func>
<name name="port_info" arity="1"/>
- <type name="port_info_result_item"/>
<fsummary>Information about a port</fsummary>
<desc>
<p>Returns a list containing tuples with information about
the <c><anno>Port</anno></c>, or <c>undefined</c> if the port is not open.
The order of the tuples is not defined, nor are all the
tuples mandatory.</p>
- <taglist>
- <tag><c>{registered_name, <anno>RegName</anno>}</c></tag>
- <item>
- <p><c><anno>RegName</anno></c> (an atom) is the registered name of
- the port. If the port has no registered name, this tuple
- is not present in the list.</p>
- </item>
- <tag><c>{id, <anno>Index</anno>}</c></tag>
- <item>
- <p><c><anno>Index</anno></c> (an integer) is the internal index of the
- port. This index may be used to separate ports.</p>
- </item>
- <tag><c>{connected, <anno>Pid</anno>}</c></tag>
- <item>
- <p><c><anno>Pid</anno></c> is the process connected to the port.</p>
- </item>
- <tag><c>{links, <anno>Pids</anno>}</c></tag>
- <item>
- <p><c><anno>Pids</anno></c> is a list of pids to which processes the
- port is linked.</p>
- </item>
- <tag><c>{name, <anno>String</anno>}</c></tag>
- <item>
- <p><c><anno>String</anno></c> is the command name set by
- <c>open_port</c>.</p>
- </item>
- <tag><c>{input, <anno>Bytes</anno>}</c></tag>
- <item>
- <p><c><anno>Bytes</anno></c> is the total number of bytes read from
- the port.</p>
- </item>
- <tag><c>{output, <anno>Bytes</anno>}</c></tag>
- <item>
- <p><c><anno>Bytes</anno></c> is the total number of bytes written to
- the port.</p>
- </item>
- <tag><c>{os_pid, <anno>OsPid</anno> | undefined}</c></tag>
- <item>
- <p><c> <anno>OsPid</anno></c> is the process identifier (or equivalent) of an OS process created with <c>open_port({spawn | spawn_executable, Command}, Options)</c>. If the port is not the result of spawning an OS process, the value is <c>undefined</c>.</p>
- </item>
- </taglist>
- <p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not a local port.</p>
+ <p>Currently the result will containt information about the
+ following <c>Item</c>s: <c>registered_name</c> (if the port has
+ a registered name), <c>id</c>, <c>connected</c>, <c>links</c>,
+ <c>name</c>, <c>input</c>, and <c>output</c>. For more information
+ about the different <c>Item</c>s, see
+ <seealso marker="#port_info/2">port_info/2</seealso>.</p>
+ <p>Failure: <c>badarg</c> if <c>Port</c> is not a local port
+ identifier, or an atom.</p>
</desc>
</func>
<func>
- <name name="port_info" arity="2"/>
- <type name="port_info_item"/>
- <type name="port_info_result_item"/>
- <fsummary>Information about a port</fsummary>
+ <name name="port_info" arity="2" clause_i="1"/>
+ <fsummary>Information about the connected process of a port</fsummary>
<desc>
- <p>Returns information about <c><anno>Port</anno></c> as specified
- by <c><anno>Item</anno></c>, or <c>undefined</c> if the port is not open.
- Also, if <c>Item =:= registered_name</c> and the port has no
- registered name, <c>[]</c> is returned.</p>
- <p>For valid values of <c><anno>Item</anno></c>, and corresponding
- values of <c><anno>Result</anno></c>, see
- <seealso marker="#port_info/1">erlang:port_info/1</seealso>.</p>
- <p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not a local port.</p>
+ <p><c><anno>Pid</anno></c> is the process identifier of the process
+ connected to the port.</p>
+ <p>If the port identified by <c><anno>Port</anno></c> is not open,
+ <c>undefined</c> is returned.</p>
+ <p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not a local
+ port identifier, or an atom.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="port_info" arity="2" clause_i="2"/>
+ <fsummary>Information about the internal index of a port</fsummary>
+ <desc>
+ <p><c><anno>Index</anno></c> is the internal index of the port. This
+ index may be used to separate ports.</p>
+ <p>If the port identified by <c><anno>Port</anno></c> is not open,
+ <c>undefined</c> is returned.</p>
+ <p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not a local
+ port identifier, or an atom.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="port_info" arity="2" clause_i="3"/>
+ <fsummary>Information about the input of a port</fsummary>
+ <desc>
+ <p><c><anno>Bytes</anno></c> is the total number of bytes
+ read from the port.</p>
+ <p>If the port identified by <c><anno>Port</anno></c> is not open,
+ <c>undefined</c> is returned.</p>
+ <p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not a local
+ port identifier, or an atom.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="port_info" arity="2" clause_i="4"/>
+ <fsummary>Information about the links of a port</fsummary>
+ <desc>
+ <p><c><anno>Pids</anno></c> is a list of the process identifiers
+ of the processes that the port is linked to.</p>
+ <p>If the port identified by <c><anno>Port</anno></c> is not open,
+ <c>undefined</c> is returned.</p>
+ <p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not a local
+ port identifier, or an atom.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="port_info" arity="2" clause_i="5"/>
+ <fsummary>Information about the locking of a port</fsummary>
+ <desc>
+ <p><c><anno>Locking</anno></c> is currently either <c>false</c>
+ (emulator without SMP support), <c>port_level</c> (port specific
+ locking), or <c>driver_level</c> (driver specific locking). Note
+ that these results are highly implementation specific and might
+ change in the future.</p>
+ <p>If the port identified by <c><anno>Port</anno></c> is not open,
+ <c>undefined</c> is returned.</p>
+ <p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not a local
+ port identifier, or an atom.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="port_info" arity="2" clause_i="6"/>
+ <fsummary>Information about the memory size of a port</fsummary>
+ <desc>
+ <p><c><anno>Bytes</anno></c> is the total amount of memory,
+ in bytes, allocated for this port by the runtime system. Note
+ that the port itself might have allocated memory which is not
+ included in <c><anno>Bytes</anno></c>.</p>
+ <p>If the port identified by <c><anno>Port</anno></c> is not open,
+ <c>undefined</c> is returned.</p>
+ <p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not a local
+ port identifier, or an atom.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="port_info" arity="2" clause_i="7"/>
+ <fsummary>Information about the monitors of a port</fsummary>
+ <desc>
+ <p><c><anno>Monitors</anno></c> represent processes that this port
+ is monitoring.</p>
+ <p>If the port identified by <c><anno>Port</anno></c> is not open,
+ <c>undefined</c> is returned.</p>
+ <p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not a local
+ port identifier, or an atom.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="port_info" arity="2" clause_i="8"/>
+ <fsummary>Information about the name of a port</fsummary>
+ <desc>
+ <p><c><anno>Name</anno></c> is the command name set by
+ <seealso marker="#open_port/2">open_port/2</seealso>.</p>
+ <p>If the port identified by <c><anno>Port</anno></c> is not open,
+ <c>undefined</c> is returned.</p>
+ <p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not a local
+ port identifier, or an atom.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="port_info" arity="2" clause_i="9"/>
+ <fsummary>Information about the OS pid of a port</fsummary>
+ <desc>
+ <p><c><anno>OsPid</anno></c> is the process identifier (or equivalent)
+ of an OS process created with
+ <seealso marker="#open_port/2">open_port({spawn | spawn_executable,
+ Command}, Options)</seealso>. If the port is not the result of spawning
+ an OS process, the value is <c>undefined</c>.</p>
+ <p>If the port identified by <c><anno>Port</anno></c> is not open,
+ <c>undefined</c> is returned.</p>
+ <p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not a local
+ port identifier, or an atom.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="port_info" arity="2" clause_i="10"/>
+ <fsummary>Information about the output of a port</fsummary>
+ <desc>
+ <p><c><anno>Bytes</anno></c> is the total number of bytes written
+ to the port from Erlang processes using either
+ <seealso marker="#port_command/2">port_command/2</seealso>,
+ <seealso marker="#port_command/3">port_command/3</seealso>,
+ or <c><anno>Port</anno> ! {Owner, {command, Data}</c>.
+ </p>
+ <p>If the port identified by <c><anno>Port</anno></c> is not open,
+ <c>undefined</c> is returned.</p>
+ <p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not a local
+ port identifier, or an atom.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="port_info" arity="2" clause_i="11"/>
+ <fsummary>Information about the parallelism hint of a port</fsummary>
+ <desc>
+ <p><c><anno>Boolean</anno></c> corresponds to the port parallelism
+ hint being used by this port. For more information see
+ the <seealso marker="#open_port_parallelism">parallelism</seealso>
+ option of <seealso marker="#open_port/2">open_port/2</seealso>.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="port_info" arity="2" clause_i="12"/>
+ <fsummary>Information about the queue size of a port</fsummary>
+ <desc>
+ <p><c><anno>Bytes</anno></c> is the total amount of data,
+ in bytes, queued by the port using the ERTS driver queue
+ implementation.</p>
+ <p>If the port identified by <c><anno>Port</anno></c> is not open,
+ <c>undefined</c> is returned.</p>
+ <p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not a local
+ port identifier, or an atom.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="port_info" arity="2" clause_i="13"/>
+ <fsummary>Information about the registered name of a port</fsummary>
+ <desc>
+ <p><c><anno>RegisteredName</anno></c> is the registered name of
+ the port. If the port has no registered name, <c>[]</c> is returned.</p>
+ <p>If the port identified by <c><anno>Port</anno></c> is not open,
+ <c>undefined</c> is returned.</p>
+ <p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not a local
+ port identifier, or an atom.</p>
</desc>
</func>
<func>
@@ -3160,7 +3372,10 @@ os_prompt% </pre>
<name name="ports" arity="0"/>
<fsummary>All open ports</fsummary>
<desc>
- <p>Returns a list of all ports on the local node.</p>
+ <p>Returns a list of port identifiers corresponding to all the
+ ports currently existing on the local node.</p>
+
+ <p>Note that a port that is exiting, exists but is not open.</p>
</desc>
</func>
<func>
@@ -3526,10 +3741,11 @@ os_prompt% </pre>
the initial function call with which the process was
spawned.</p>
</item>
- <tag><c>{links, <anno>Pids</anno>}</c></tag>
+ <tag><c>{links, <anno>PidsAndPorts</anno>}</c></tag>
<item>
- <p><c><anno>Pids</anno></c> is a list of pids, with processes to
- which the process has a link.</p>
+ <p><c><anno>PidsAndPorts</anno></c> is a list of pids and
+ port identifiers, with processes or ports to which the process
+ has a link.</p>
</item>
<tag><c>{last_calls, false|Calls}</c></tag>
<item>
@@ -5493,19 +5709,40 @@ ok
<item>
<p>Returns a string containing the OTP release number.</p>
</item>
- <tag><c>process_count</c></tag>
+ <tag><marker id="system_info_port_parallelism"><c>port_parallelism</c></marker></tag>
+ <item><p>Returns the default port parallelism scheduling hint used.
+ For more information see the
+ <seealso marker="erl#+spp">+spp</seealso> command line argument
+ of <seealso marker="erl">erl(1)</seealso>.</p></item>
+ <tag><marker id="system_info_port_count"/><c>port_count</c></tag>
+ <item>
+ <p>Returns the number of ports currently existing at
+ the local node as an integer. The same value as
+ <c>length(erlang:ports())</c> returns, but more efficient.</p>
+ </item>
+ <tag><marker id="system_info_port_limit"><c>port_limit</c></marker></tag>
+ <item>
+ <p>Returns the maximum number of simultaneously existing
+ ports at the local node as an integer. This limit
+ can be configured at startup by using the
+ <seealso marker="erl#+Q">+Q</seealso>
+ command line flag of
+ <seealso marker="erl">erl(1)</seealso>.</p>
+ </item>
+ <tag><marker id="system_info_process_count"/><c>process_count</c></tag>
<item>
<p>Returns the number of processes currently existing at
the local node as an integer. The same value as
- <c>length(processes())</c> returns.</p>
+ <c>length(processes())</c> returns, but more efficient.</p>
</item>
- <tag><c>process_limit</c></tag>
+ <tag><marker id="system_info_process_limit"><c>process_limit</c></marker></tag>
<item>
- <p>Returns the maximum number of concurrently existing
+ <p>Returns the maximum number of simultaneously existing
processes at the local node as an integer. This limit
- can be configured at startup by using the command line
- flag <c>+P</c>, see
- <seealso marker="erts:erl#max_processes">erl(1)</seealso>.</p>
+ can be configured at startup by using the
+ <seealso marker="erl#+P">+P</seealso>
+ command line flag of
+ <seealso marker="erl">erl(1)</seealso>.</p>
</item>
<tag><c>procs</c></tag>
<item>
diff --git a/erts/doc/src/erlc.xml b/erts/doc/src/erlc.xml
index 3358b8f115..81dffe45cf 100644
--- a/erts/doc/src/erlc.xml
+++ b/erts/doc/src/erlc.xml
@@ -4,7 +4,7 @@
<comref>
<header>
<copyright>
- <year>1997</year><year>2011</year>
+ <year>1997</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -67,7 +67,7 @@
<item>
<p>Instructs the compiler to search for include files in
the specified directory. When encountering an
- <c><![CDATA[-include]]></c> or <c><![CDATA[-include_dir]]></c> directive, the
+ <c><![CDATA[-include]]></c> or <c><![CDATA[-include_lib]]></c> directive, the
compiler searches for header files in the following
directories:</p>
<list type="ordered">
diff --git a/erts/doc/src/erlsrv.xml b/erts/doc/src/erlsrv.xml
index c1ecbc7b77..365ae21d39 100644
--- a/erts/doc/src/erlsrv.xml
+++ b/erts/doc/src/erlsrv.xml
@@ -4,7 +4,7 @@
<comref>
<header>
<copyright>
- <year>1998</year><year>2011</year>
+ <year>1998</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -357,11 +357,12 @@ The environment of an Erlang machine started
the console subsystem and programs running as window
applications. An application which runs in the console subsystem
(normal for port programs) uses the win32 function
- <c><![CDATA[SetConsoleCtrlHandler]]></c> to a control handler that returns
- TRUE in answer to the <c><![CDATA[CTRL_LOGOFF_EVENT]]></c>. Other
- applications just forward <c><![CDATA[WM_ENDSESSION]]></c> and
- <c><![CDATA[WM_QUERYENDSESSION]]></c> to the default window procedure. Here
- is a brief example in C of how to set the console control
+ <c><![CDATA[SetConsoleCtrlHandler]]></c> to register a control handler
+ that returns TRUE in answer to the <c><![CDATA[CTRL_LOGOFF_EVENT]]></c>
+ and <c><![CDATA[CTRL_SHUTDOWN_EVENT]]></c> events. Other applications
+ just forward <c><![CDATA[WM_ENDSESSION]]></c> and
+ <c><![CDATA[WM_QUERYENDSESSION]]></c> to the default window procedure.
+ Here is a brief example in C of how to set the console control
handler:</p>
<code type="none"><![CDATA[
#include <windows.h>
@@ -372,6 +373,8 @@ The environment of an Erlang machine started
BOOL WINAPI service_aware_handler(DWORD ctrl){
if(ctrl == CTRL_LOGOFF_EVENT)
return TRUE;
+ if(ctrl == CTRL_SHUTDOWN_EVENT)
+ return TRUE;
return FALSE;
}
diff --git a/erts/doc/src/erts_alloc.xml b/erts/doc/src/erts_alloc.xml
index ec5e7d9b74..87d6682328 100644
--- a/erts/doc/src/erts_alloc.xml
+++ b/erts/doc/src/erts_alloc.xml
@@ -341,7 +341,8 @@
Largest (<c>mseg_alloc</c>) multiblock carrier size (in
kilobytes). See <seealso marker="#mseg_mbc_sizes">the description
on how sizes for mseg_alloc multiblock carriers are decided</seealso>
- in "the <c>alloc_util</c> framework" section.</item>
+ in "the <c>alloc_util</c> framework" section. On 32-bit Unix style OS
+ this limit can not be set higher than 128 megabyte.</item>
<tag><marker id="M_mbcgs"><c><![CDATA[+M<S>mbcgs <ratio>]]></c></marker></tag>
<item>
(<c>mseg_alloc</c>) multiblock carrier growth stages. See
@@ -413,7 +414,8 @@
Singleblock carrier threshold. Blocks larger than this
threshold will be placed in singleblock carriers. Blocks
smaller than this threshold will be placed in multiblock
- carriers.</item>
+ carriers. On 32-bit Unix style OS this threshold can not be set higher
+ than 8 megabytes.</item>
<tag><marker id="M_sbmbcs"><c><![CDATA[+M<S>sbmbcs <size>]]></c></marker></tag>
<item>
Small block multiblock carrier size (in bytes). Memory blocks smaller
diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml
index 6b6a3374d1..de6696671b 100644
--- a/erts/doc/src/notes.xml
+++ b/erts/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2004</year><year>2011</year>
+ <year>2004</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -30,6 +30,759 @@
</header>
<p>This document describes the changes made to the ERTS application.</p>
+<section><title>Erts 5.10</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Set new peeled off SCTP socket to nonblocking socket
+ (Thanks to Jonas Falkevik)</p>
+ <p>
+ Own Id: OTP-10491</p>
+ </item>
+ <item>
+ <p>
+ Fix various typos (thanks to Tuncer Ayaz)</p>
+ <p>
+ Own Id: OTP-10611</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ A boolean socket option 'ipv6_v6only' for IPv6 sockets
+ has been added. The default value of the option is OS
+ dependent, so applications aiming to be portable should
+ consider using <c>{ipv6_v6only,true}</c> when creating an
+ <c>inet6</c> listening/destination socket, and if
+ neccesary also create an <c>inet</c> socket on the same
+ port for IPv4 traffic. See the documentation.</p>
+ <p>
+ Own Id: OTP-8928 Aux Id: kunagi-193 [104] </p>
+ </item>
+ <item>
+ <p>It is now allowed to define stubs for BIFs, to allow
+ type specs to be written for BIFs. For example, if there
+ is BIF called <c>lists:member/2</c>, a dummy definition
+ of <c>lists:member/2</c> is now allowed.</p>
+ <p>
+ Own Id: OTP-9861</p>
+ </item>
+ <item>
+ <p>
+ Code loading and upgrade are now done without blocking
+ the emulator in single threaded mode. This will improve
+ realtime characteristics when code is loaded/upgraded on
+ a running SMP system.</p>
+ <p>
+ Own Id: OTP-9974</p>
+ </item>
+ <item>
+ <p>In the SMP emulator, turning on and off tracing will
+ no longer take down the system to single-scheduling. </p>
+ <p>
+ Own Id: OTP-10122</p>
+ </item>
+ <item>
+ <p>
+ Tuple funs (deprecated in R15B) are no longer supported.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-10170</p>
+ </item>
+ <item>
+ <p>Major port improvements. The most notable:</p> <list>
+ <item>New internal port table implementation allowing for
+ both parallel reads as well as writes. Especially read
+ operations have become really cheap.</item> <item>Dynamic
+ allocation of port structures. This allow for a much
+ larger maximum amount of ports allowed as a default. The
+ previous default of 1024 has been raised to 65536.
+ Maximum amount of ports can be set using the <seealso
+ marker="erts:erl#+Q">+Q</seealso> command line flag of
+ <seealso marker="erts:erl">erl(1)</seealso>. The
+ previously used environment variable <c>ERL_MAX_PORTS</c>
+ has been deprecated and scheduled for removal in
+ OTP-R17.</item> <item>Major rewrite of scheduling of port
+ tasks. Major benefits of the rewrite are reduced
+ contention on run queue locks, and reduced amount of
+ memory allocation operations needed. The rewrite was also
+ necessary in order to make it possible to schedule
+ signals from processes to ports.</item> <item>Improved
+ internal thread progress functionality for easy
+ management of unmanaged threads. This improvement was
+ necessary for the rewrite of the port task
+ scheduling.</item> <item>Rewrite of all process to port
+ signal implementations in order to make it possible to
+ schedule those operations. All port operations can now be
+ scheduled which allows for reduced lock contention on the
+ port lock as well as truly asynchronous communication
+ with ports.</item> <item>Optimized lookup of port handles
+ from drivers.</item> <item>Optimized driver lookup when
+ creating ports.</item> <item>Preemptable <seealso
+ marker="erts:erlang#ports-0">erlang:ports/0</seealso>
+ BIF.</item> </list>
+ <p>These changes imply changes of the characteristics of
+ the system. The most notable:</p> <taglist> <tag>Order of
+ signal delivery.</tag> <item>The previous implementation
+ of the VM has delivered signals from processes to ports
+ in a synchronous stricter fashion than required by the
+ language. As of ERTS version 5.10, signals are truly
+ asynchronously delivered. The order of signal delivery
+ still adheres to the requirements of the language, but
+ only to the requirements. That is, some signal sequences
+ that previously always were delivered in one specific
+ order may now from time to time be delivered in different
+ orders. This may cause Erlang programs that have made
+ <em>false assumptions</em> about signal delivery order to
+ fail even though they previously succeeded. For more
+ information about signal ordering guarantees, see the
+ chapter on <seealso
+ marker="erts:communication">communication</seealso> in
+ the ERTS user's guide. The <seealso
+ marker="erts:erl#+n">+n</seealso> command line flag of
+ <seealso marker="erts:erl">erl(1)</seealso> can be
+ helpful when trying to find signaling order bugs in
+ Erlang code that have been exposed by these
+ changes.</item> <tag>Latency of signals sent from
+ processes to ports.</tag> <item>Signals from processes to
+ ports where previously always delivered immediately. This
+ kept latency for such communication to a minimum, but it
+ could cause lock contention which was very expensive for
+ the system as a whole. In order to keep this latency low
+ also in the future, most signals from processes to ports
+ are by default still delivered immediately as long as no
+ conflicts occur. Such conflicts include not being able to
+ acquire the port lock, but also include other conflicts.
+ When a conflict occur, the signal will be scheduled for
+ delivery at a later time. A scheduled signal delivery may
+ cause a higher latency for this specific communication,
+ but improves the overall performance of the system since
+ it reduce lock contention between schedulers. The default
+ behavior of only scheduling delivery of these signals on
+ conflict can be changed by passing the <seealso
+ marker="erts:erl#+spp">+spp</seealso> command line flag
+ to <seealso marker="erts:erl">erl(1)</seealso>. The
+ behavior can also be changed on port basis using the
+ <seealso
+ marker="erts:erlang#open_port_parallelism">parallelism</seealso>
+ option of the <seealso
+ marker="erts:erlang#open_port-2">open_port/2</seealso>
+ BIF.</item> <tag>Execution time of the
+ <c>erlang:ports/0</c> BIF.</tag> <item>Since <seealso
+ marker="erts:erlang#ports-0">erlang:ports/0</seealso> now
+ can be preempted, the responsiveness of the system as a
+ whole has been improved. A call to <c>erlang:ports/0</c>
+ may, however, take a much longer time to complete than
+ before. How much longer time heavily depends on the
+ system load.</item> </taglist>
+ <p><em>Potential incompatibilities</em>:</p> <list>
+ <item><c>driver_send_term()</c> has been deprecated and
+ has been scheduled for removal in OTP-R17. Replace usage
+ of <c>driver_send_term()</c> with usage of <seealso
+ marker="erts:erl_driver#erl_drv_send_term">erl_drv_send_term()</seealso>.</item>
+ <item><c>driver_output_term()</c> has been deprecated and
+ has been scheduled for removal in OTP-R17. Replace usage
+ of <c>driver_output_term()</c> with usage of <seealso
+ marker="erts:erl_driver#erl_drv_output_term">erl_drv_output_term()</seealso>.</item>
+ <item>The new function <seealso
+ marker="erts:erl_driver#erl_drv_busy_msgq_limits">erl_drv_busy_msgq_limits()</seealso>
+ has been added in order to able to control management of
+ port queues.</item> </list>
+ <p>The <seealso
+ marker="erts:erl_driver#version_management">driver API
+ version</seealso> has been bumped to 2.1 from 2.0 due to
+ the above changes in the driver API.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-10336 Aux Id: kunagi-138
+ [b5b97f67-fe34-46dc-93e6-a2931576db12] </p>
+ </item>
+ <item>
+ <p>
+ Erlang specification 4.7.3 defines max tuple size to
+ 65535 elements It is now enforced to no more than
+ 16777215 elements (arity 24 bits)</p>
+ <p>
+ Previous edge cases (28 bits) were not validated and
+ could cause undefined behaviour.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-10633</p>
+ </item>
+ <item>
+ <p>
+ The previous default of a maximum of 32768 simultaneous
+ processes has been raised to 262144. This value can be
+ changed using the the <seealso
+ marker="erl#+P">+P</seealso> command line flag of
+ <seealso marker="erl">erl(1)</seealso>. Note that the
+ value passed now is considered as a hint, and that actual
+ value chosen in most cases will be a power of two.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-10647 Aux Id: OTP-10336 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 5.9.3.1</title>
+
+ <section><title>Known Bugs and Problems</title>
+ <list>
+ <item>
+ <p>
+ Create an erl_crash.dump if no heart exists and no
+ ERL_CRASH_DUMP_SECONDS is set (behaviour changed).</p>
+ <p>
+ Don't create an erl_crash.dump if heart do exists and no
+ ERL_CRASH_DUMP_SECONDS is set (behaviour not changed).</p>
+ <p>
+ This changes the behaviour back to the R15B02 default
+ considering if a beam was running with no heart.</p>
+ <p>
+ Own Id: OTP-10602</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 5.9.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix linking in OpenBSD. (Thanks to Matthew Dempsky)</p>
+ <p>
+ Own Id: OTP-10395</p>
+ </item>
+ <item>
+ <p>
+ Fix bug causing fallback atomics to be used even though
+ healthy gcc atomics or libatomic_ops was detected.</p>
+ <p>
+ Own Id: OTP-10418</p>
+ </item>
+ <item>
+ <p>
+ Ensure 'erl_crash.dump' when asked for it. This will
+ change erl_crash.dump behaviour.</p>
+ <p>
+ * Not setting ERL_CRASH_DUMP_SECONDS will now terminate
+ beam immediately on a crash without writing a crash dump
+ file.</p>
+ <p>
+ * Setting ERL_CRASH_DUMP_SECONDS to 0 will also terminate
+ beam immediately on a crash without writing a crash dump
+ file, i.e. same as not setting ERL_CRASH_DUMP_SECONDS
+ environment variable.</p>
+ <p>
+ * Setting ERL_CRASH_DUMP_SECONDS to a negative value will
+ let the beam wait indefinitely on the crash dump file
+ being written.</p>
+ <p>
+ * Setting ERL_CRASH_DUMP_SECONDS to a positive value will
+ let the beam wait that many seconds on the crash dump
+ file being written.</p>
+ <p>
+ A positive value will set an alarm/timeout for restart
+ both in beam and in heart if heart is running.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-10422 Aux Id: kunagi-250 [161] </p>
+ </item>
+ <item>
+ <p>
+ Fix bug where MSVRT100.dll was not included in the
+ windows installer.</p>
+ <p>
+ Own Id: OTP-10481</p>
+ </item>
+ <item>
+ <p>In the expression
+ <c>&lt;&lt;Bin/binary,...&gt;&gt;</c>, if <c>Bin</c> was
+ a bitstring with a size not a multiple of 8, either no
+ exception was generated or an incorrect exception was
+ generated. (Thanks to Adam Rutkowski for reporting this
+ bug.)</p>
+ <p>
+ Own Id: OTP-10524</p>
+ </item>
+ <item>
+ <p>
+ The runtime system could crash while scheduling a port
+ task. The port task was scheduled either due to an
+ external I/O event being triggered, a driver timeout
+ being triggered, or data being sent over a distribution
+ channel.</p>
+ <p>
+ Own Id: OTP-10556</p>
+ </item>
+ <item>
+ <p>
+ <c>erlang:memory(ets)</c> erroneously included the size
+ of each ETS-table main structure twice.</p>
+ <p>
+ Own Id: OTP-10558</p>
+ </item>
+ <item>
+ <p>
+ Fix compile error in generated file hipe_amd64_bifs.S for
+ Solaris.</p>
+ <p>
+ Own Id: OTP-10577</p>
+ </item>
+ <item>
+ <p>
+ A faulty spec for process_info/2 could cause false
+ dialyzer warnings. The spec is corrected.</p>
+ <p>
+ Own Id: OTP-10584</p>
+ </item>
+ <item>
+ <p>
+ In very rare cases, the VM could crash if a garbage
+ collector was called while executing an appending bit
+ syntax instruction. The symptom was a core when
+ reallocating memory in the function erts_bs_append. The
+ garbage collector bug is now corrected.</p>
+ <p>
+ Own Id: OTP-10590</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Improve support for building and testing in embedded ppc
+ environments.</p>
+ <p>
+ Own Id: OTP-10265 Aux Id: kunagi-159
+ [daf97f67-5724-4812-a5b6-7e86990133d2-1] </p>
+ </item>
+ <item>
+ <p>
+ Due to a race condition on Windows, sometimes when
+ printing to standard output and then immediately
+ terminating erlang all data would not be printed. The
+ emulator now waits for all data to be printed before
+ exiting.</p>
+ <p>
+ Own Id: OTP-10325 Aux Id: kunagi-166
+ [dd72d0e2-3e76-4a51-8b56-7564e24eecae] </p>
+ </item>
+ <item>
+ <p>
+ The frequency with which sleeping schedulers are woken
+ due to outstanding memory deallocation jobs has been
+ reduced.</p>
+ <p>
+ Own Id: OTP-10476 Aux Id: OTP-10162 </p>
+ </item>
+ <item>
+ <p>
+ Clearer warnings about the dangers of misuse of <seealso
+ marker="erl_nif#WARNING">native functions</seealso> and
+ <seealso marker="erl_driver#WARNING">drivers</seealso>
+ have been added to the documentation.</p>
+ <p>
+ Own Id: OTP-10557</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 5.9.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix erl_prim_loader errors in handling of primary
+ archive. The following errors have been corrected:</p>
+ <p>
+ <list> <item> If primary archive was named "xxx", then a
+ file in the same directory named "xxxyyy" would be
+ interpreted as a file named "yyy" inside the archive.
+ </item> <item> erl_prim_loader did not correctly create
+ and normalize absolute paths for primary archive and
+ files inside it, so unless given with exact same path
+ files inside the archive would not be found. E.g. if
+ escript was started as /full/path/to/xxx then
+ "./xxx/file" would not be found since erl_prim_loader
+ would try to match /full/path/to/xxx with
+ /full/path/to/./xxx. Same problem with ../. </item>
+ <item> Depending on how the primary archive was built,
+ erl_prim_loader:list_dir/1 would sometimes return an
+ empty string inside the file list. This was a virtual
+ element representing the top directory of the archive.
+ This has been removed. </item> </list></p>
+ <p>
+ Thanks to Tuncer Ayaz and Shunichi Shinohara for
+ reporting and co-authoring corrections.</p>
+ <p>
+ Own Id: OTP-10071</p>
+ </item>
+ <item>
+ <p>Fix: Add port-I/O statistics for active once and true
+ and not only active false.</p>
+ <p>
+ Own Id: OTP-10073</p>
+ </item>
+ <item>
+ <p>
+ The 64-bit windows installer did not look in the right
+ directories for 64-bit version of Microsoft Visual C++
+ 2010 Redistibutable Package and hence took the wrong
+ decision about having to install the redistributable
+ package if the 32-bit version was installed but not the
+ 64-bit and vice versa. This bug has now been fixed
+ Furthermore the sub-installer for the redistributable
+ package is now run in silent mode if the erlang installer
+ is.</p>
+ <p>
+ Own Id: OTP-10096</p>
+ </item>
+ <item>
+ <p>
+ epmd would fail to start automatically when starting a
+ distributed erlang node installed in a location with a
+ whitespace in the path.</p>
+ <p>
+ Own Id: OTP-10106</p>
+ </item>
+ <item>
+ <p>
+ A more or less harmless bug that sometimes caused memory
+ deallocations to be delayed longer than intended has been
+ fixed.</p>
+ <p>
+ Own Id: OTP-10116</p>
+ </item>
+ <item>
+ <p>
+ Fix bug causing emulator crash when running HiPE on ARM.
+ Bug has existed since R15B.</p>
+ <p>
+ Own Id: OTP-10137</p>
+ </item>
+ <item>
+ <p> A bug regarding spaces in C function prototypes has
+ been fixed. (Thanks to Richard O'Keefe.) </p>
+ <p>
+ Own Id: OTP-10138</p>
+ </item>
+ <item>
+ <p>
+ Corrected dtrace pid length in message related probes.
+ (Thanks to Zheng Siyao)</p>
+ <p>
+ Own Id: OTP-10142</p>
+ </item>
+ <item>
+ <p>
+ Correct formating in exit error messages</p>
+ <p>
+ Ensure displayed sizes are not negative. (Thanks to
+ Michael Santos)</p>
+ <p>
+ Own Id: OTP-10148</p>
+ </item>
+ <item>
+ <p>
+ fix escript/primary archive reloading</p>
+ <p>
+ If the mtime of an escript/primary archive file changes
+ after being added to the code path, correctly reload the
+ archive and update the cache. (Thanks to Tuncer Ayaz)</p>
+ <p>
+ Own Id: OTP-10151</p>
+ </item>
+ <item>
+ <p>
+ Doc fix: link from erlang:now/0 to os:timestamp/0</p>
+ <p>
+ Sometimes os:timestamp/0 is more appropriate than
+ erlang:now/0. The documentation for the former has a link
+ to the latter; this patch adds a link in the other
+ direction to make os:timestamp/0 more visible. Thanks to
+ Magnus Henoch</p>
+ <p>
+ Own Id: OTP-10180</p>
+ </item>
+ <item>
+ <p>
+ The caret in the werl window (on Windows) could appear at
+ the wrong place after regaining focus. This is now
+ corrected.</p>
+ <p>
+ Own Id: OTP-10181</p>
+ </item>
+ <item>
+ <p>
+ Fix bug that in some cases could cause corrupted binaries
+ in ETS tables with <c>compressed</c> option.</p>
+ <p>
+ Own Id: OTP-10182</p>
+ </item>
+ <item>
+ <p>
+ Fix use of "clever" mktime</p>
+ <p>
+ Commit 1eef765 introduced regression (conditional
+ _always_ evaluates to true) in which
+ erlang:localtime_to_universaltime/2 stopped working on
+ systems configured with timezone without DST (i.e. UTC)
+ on *BSD platforms: 1>
+ erlang:localtime_to_universaltime({{2012,1,1},{0,0,0}},
+ true). ** exception error: bad argument Thanks to Piotr
+ Sikora</p>
+ <p>
+ Own Id: OTP-10187</p>
+ </item>
+ <item>
+ <p>
+ Relocate bodies of DTrace probes to the statically-linked
+ VM.</p>
+ <p>
+ Due to various operating systems (in both the DTrace and
+ SystemTap worlds) not fully supporting DTrace probes (or
+ SystemTap-compatibility mode probes) in shared libraries,
+ we relocate those probes to the statically-linked virtual
+ machine. This could be seen as pollution of the pristine
+ VM by a (yet) experimental feature. However:</p>
+ <p>
+ 1. This code can be eliminated completely by the C
+ preprocessor. 2. Leaving the probes in the dyntrace NIF
+ shared library simply does not work correctly on too many
+ platforms. *Many* thanks to Macneil Shonle at Basho for
+ assisting when my RSI-injured fingers gave out. (note:
+ Solaris 10 and FreeBSD 9.0-RELEASE can take a long time
+ to compile)</p>
+ <p>
+ Own Id: OTP-10189</p>
+ </item>
+ <item>
+ <p>
+ Fix bug in <c>ets:test_ms/2</c> that could cause emulator
+ crash when using <c>'$_'</c> in match spec.</p>
+ <p>
+ Own Id: OTP-10190</p>
+ </item>
+ <item>
+ <p>
+ Supplying a filename longer than the operating system
+ MAX_PATH to file:read_link/1 would cause a crash
+ (Segemntation fault/Critical Error) on all platforms.
+ This is now corrected.</p>
+ <p>
+ Own Id: OTP-10200</p>
+ </item>
+ <item>
+ <p>
+ If Perl was configured to interpret files as being
+ encoded in UTF-8, the build would crash in
+ <c>make_preload</c>. (Thanks to Aaron Harnly for noticing
+ this issue.)</p>
+ <p>
+ Own Id: OTP-10201</p>
+ </item>
+ <item>
+ <p>
+ Fix the erlc -MP flag</p>
+ <p>
+ Because of a copy-and-paste error in erlc.c, the -MP flag
+ had the same effect as -MG. As a workaround, you had to
+ pass +makedep_phony to enable the MP option. This patch
+ makes -MP work as intended.</p>
+ <p>
+ Own Id: OTP-10211</p>
+ </item>
+ <item>
+ <p>
+ Allow mixed IPv4 and IPv6 addresses to sctp_bindx</p>
+ <p>
+ Also allow mixed address families to bind, since the
+ first address on a multihomed sctp socket must be bound
+ with bind, while the rest are to be bound using
+ sctp_bindx. At least Linux supports adding address of
+ mixing families. Make inet_set_faddress function
+ available also when HAVE_SCTP is not defined, since we
+ use it to find an address for bind to be able to mix ipv4
+ and ipv6 addresses. Thanks to Tomas Abrahamsson</p>
+ <p>
+ Own Id: OTP-10217</p>
+ </item>
+ <item>
+ <p>
+ Fix support for leap seconds-aware timezones</p>
+ <p>
+ erlang:universaltime_to_localtime is leap seconds-aware
+ (since 2008), however erlang:localtime_to_universaltime
+ is not, which gives surprising results on systems
+ configured with leap seconds-aware timezones: 1>
+ erlang:universaltime_to_localtime({{2012,1,1},{0,0,0}}).
+ {{2012,1,1},{0,0,0}} 2>
+ erlang:localtime_to_universaltime({{2012,1,1},{0,0,0}}).
+ {{2012,1,1},{0,0,24}} and completely breaks
+ calendar:local_time_to_universal_time_dst: 3>
+ calendar:local_time_to_universal_time_dst({{2011,1,1},{0,0,0}}).
+ [] Thanks to Piotr Sikora</p>
+ <p>
+ Own Id: OTP-10227</p>
+ </item>
+ <item>
+ <p>
+ erlsrv: gracefully stop emulator on Windows shutdown</p>
+ <p>
+ Windows will send the SERVICE_CONTROL_SHUTDOWN event to
+ the service control handler when shutting down the
+ system. Instead of ignoring the event, erlsrv will now
+ invoke the stop action. Likewise, the Erlang emulator
+ (and it's po drivers) must not quit upon reception of the
+ CTRL_SHUTDOWN_EVENT event in th console control handler.
+ Thanks to Jan Kloetzke</p>
+ <p>
+ Own Id: OTP-10228</p>
+ </item>
+ <item>
+ <p>
+ Fix dtrace bug in file rename operation.</p>
+ <p>
+ Own Id: OTP-10234</p>
+ </item>
+ <item>
+ <p>
+ Fix bug in memory management of driver port data locks
+ (PDL). In some cases PDLs could be deallocated before
+ <c>ready_async</c> or <c>async_free</c> callback was
+ called.</p>
+ <p>
+ Own Id: OTP-10249</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>Add port and suspend options to lock-counter
+ profiling. (Thanks to Rick Reed)</p>
+ <p>
+ Own Id: OTP-10051</p>
+ </item>
+ <item>
+ <p>
+ Latency when using the active_once option in gen_tcp
+ communication is reduced.</p>
+ <p>
+ Own Id: OTP-10055 Aux Id: sto139 </p>
+ </item>
+ <item>
+ <p>Remove bit8 option support from inet</p>
+ <p>
+ Own Id: OTP-10056</p>
+ </item>
+ <item>
+ <p>
+ The OS Pid of a port program is now available by calling
+ erlang:port_info(Port,os_pid), Thanks to Matthias Lang
+ for the original patch.</p>
+ <p>
+ Own Id: OTP-10057</p>
+ </item>
+ <item>
+ <p>
+ Fix openpty usage in run_erl. </p>
+ <p>
+ Reopening a slave file descriptor which was closed
+ earlier could lead to a misbehaving connection. This has
+ now been remedied.</p>
+ <p>
+ Own Id: OTP-10076</p>
+ </item>
+ <item>
+ <p>
+ Remove all code, documentation, options and diagnostic
+ functions which were related to the experimental hybrid
+ heap implementation.</p>
+ <p>
+ Own Id: OTP-10105</p>
+ </item>
+ <item>
+ <p>
+ Optimizations of memory deallocations.</p>
+ <p>
+ Own Id: OTP-10162 Aux Id: OTP-7775 </p>
+ </item>
+ <item>
+ <p>
+ Optimization of process locking.</p>
+ <p>
+ Own Id: OTP-10163</p>
+ </item>
+ <item>
+ <p>
+ Added a xcomp example file for powerpc-dso-linux-gnu</p>
+ <p>
+ Own Id: OTP-10198</p>
+ </item>
+ <item>
+ <p>
+ Detect when middle endian doubles are used by a platform
+ and account for it when decoding floats. (Thanks to Mike
+ Sperber)</p>
+ <p>
+ Own Id: OTP-10209</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 5.9.1.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ On Linux systems using heart (erl -heart) and a
+ HEAR_BEAT_TIMEOUT less than default, heart could fire
+ even though Erlang was running fine after approx 298 to
+ 497 days (depending on kernel config). This was due to
+ the behaviour of the times(2) system call. Usage of
+ times(2) is now replaced with clock_gettime(2) and the
+ CLOCK_MONOTONIC clock, resulting in a more stable
+ solution. The Erlang VM itself has used clock_gettime(2)
+ on linux since before R12B, so this only affects the
+ heart program.</p>
+ <p>
+ Own Id: OTP-10111 Aux Id: seq12075 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Erts 5.9.1.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/erts/doc/src/part.xml b/erts/doc/src/part.xml
index e27b722721..2c178556d4 100644
--- a/erts/doc/src/part.xml
+++ b/erts/doc/src/part.xml
@@ -31,6 +31,7 @@
<description>
<p>The Erlang Runtime System Application <em>ERTS</em>.</p>
</description>
+ <xi:include href="communication.xml"/>
<xi:include href="match_spec.xml"/>
<xi:include href="crash_dump.xml"/>
<xi:include href="alt_dist.xml"/>
diff --git a/erts/doc/src/ref_man.xml b/erts/doc/src/ref_man.xml
index 2042cf28bd..5c9938075c 100644
--- a/erts/doc/src/ref_man.xml
+++ b/erts/doc/src/ref_man.xml
@@ -49,7 +49,6 @@
<xi:include href="escript.xml"/>
<xi:include href="erlsrv.xml"/>
<xi:include href="start_erl.xml"/>
- <xi:include href="erl_set_memory_block.xml"/>
<xi:include href="run_erl.xml"/>
<xi:include href="start.xml"/>
<xi:include href="erl_driver.xml"/>
diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in
index e20e38d61b..3e44bbb8db 100644
--- a/erts/emulator/Makefile.in
+++ b/erts/emulator/Makefile.in
@@ -61,7 +61,7 @@ else
ifeq ($(TYPE),purify)
PURIFY = purify $(PURIFY_BUILD_OPTIONS)
TYPEMARKER = .purify
-TYPE_FLAGS = $(DEBUG_CFLAGS) -DPURIFY -DNO_JUMP_TABLE -DERTS_MSEG_FAKE_SEGMENTS
+TYPE_FLAGS = $(DEBUG_CFLAGS) -DPURIFY -DNO_JUMP_TABLE
ENABLE_ALLOC_TYPE_VARS += purify
else
@@ -92,7 +92,7 @@ else
ifeq ($(TYPE),valgrind)
PURIFY =
TYPEMARKER = .valgrind
-TYPE_FLAGS = $(DEBUG_CFLAGS) -DVALGRIND -DNO_JUMP_TABLE -DERTS_MSEG_FAKE_SEGMENTS
+TYPE_FLAGS = $(DEBUG_CFLAGS) -DVALGRIND -DNO_JUMP_TABLE
ENABLE_ALLOC_TYPE_VARS += valgrind
else
@@ -332,15 +332,11 @@ LIBS += $(THR_LIBS)
ifneq ($(findstring erts_internal_r, $(THR_LIBS)),erts_internal_r)
-ifeq ($(findstring vxworks,$(TARGET)),vxworks)
-ERTS_INTERNAL_LIB=erts_internal
-else
ifneq ($(strip $(THR_LIB_NAME)),)
ERTS_INTERNAL_LIB=erts_internal_r
else
ERTS_INTERNAL_LIB=erts_internal
endif
-endif
LIBS += -l$(ERTS_INTERNAL_LIB)$(TYPEMARKER)
@@ -403,7 +399,7 @@ include zlib/zlib.mk
include pcre/pcre.mk
$(ERTS_LIB):
- cd $(ERTS_LIB_DIR) && $(MAKE) $(TYPE)
+ $(V_at)cd $(ERTS_LIB_DIR) && $(MAKE) $(TYPE)
.PHONY: clean
clean:
@@ -463,8 +459,6 @@ _create_dirs := $(shell mkdir -p $(CREATE_DIRS))
GENERATE =
HIPE_ASM =
-ifeq ($(findstring vxworks,$(TARGET)),vxworks)
-else
ifdef HIPE_ENABLED
HIPE_ASM += $(TTF_DIR)/hipe_x86_asm.h \
$(TTF_DIR)/hipe_amd64_asm.h \
@@ -476,7 +470,6 @@ GENERATE += $(HIPE_ASM) \
$(TTF_DIR)/hipe_literals.h \
$(BINDIR)/hipe_mkliterals$(TF_MARKER)
endif
-endif
ifdef DTRACE_ENABLED
# global.h causes problems by including dtrace-wrapper.h which includes
@@ -498,7 +491,7 @@ $(TTF_DIR)/beam_pred_funcs.h \
$(TTF_DIR)/beam_tr_funcs.h \
: $(TTF_DIR)/OPCODES-GENERATED
$(TTF_DIR)/OPCODES-GENERATED: $(OPCODE_TABLES) utils/beam_makeops
- LANG=C $(PERL) utils/beam_makeops \
+ $(gen_verbose)LANG=C $(PERL) utils/beam_makeops \
-wordsize @EXTERNAL_WORD_SIZE@ \
-outdir $(TTF_DIR) \
-DUSE_VM_PROBES=$(if $(USE_VM_PROBES),1,0) \
@@ -532,22 +525,22 @@ $(TARGET)/erl_atom_table.h \
$(TARGET)/erl_pbifs.c \
: $(TARGET)/TABLES-GENERATED
$(TARGET)/TABLES-GENERATED: $(ATOMS) $(BIFS) utils/make_tables
- LANG=C $(PERL) utils/make_tables -src $(TARGET) -include $(TARGET)\
+ $(gen_verbose)LANG=C $(PERL) utils/make_tables -src $(TARGET) -include $(TARGET)\
$(ATOMS) $(BIFS) && echo $? >$(TARGET)/TABLES-GENERATED
GENERATE += $(TARGET)/TABLES-GENERATED
$(TTF_DIR)/erl_alloc_types.h: beam/erl_alloc.types utils/make_alloc_types
- LANG=C $(PERL) utils/make_alloc_types -src $< -dst $@ $(ENABLE_ALLOC_TYPE_VARS)
+ $(gen_verbose)LANG=C $(PERL) utils/make_alloc_types -src $< -dst $@ $(ENABLE_ALLOC_TYPE_VARS)
GENERATE += $(TTF_DIR)/erl_alloc_types.h
# version include file
$(TARGET)/erl_version.h: ../vsn.mk
- LANG=C $(PERL) utils/make_version -o $@ $(SYSTEM_VSN) $(VSN)$(SERIALNO) $(TARGET)
+ $(gen_verbose)LANG=C $(PERL) utils/make_version -o $@ $(SYSTEM_VSN) $(VSN)$(SERIALNO) $(TARGET)
GENERATE += $(TARGET)/erl_version.h
# driver table
$(TTF_DIR)/driver_tab.c: Makefile.in
- LANG=C $(PERL) utils/make_driver_tab -o $@ $(DRV_OBJS)
+ $(gen_verbose)LANG=C $(PERL) utils/make_driver_tab -o $@ $(DRV_OBJS)
GENERATE += $(TTF_DIR)/driver_tab.c
@@ -567,8 +560,9 @@ $(PRELOAD_SRC): $(ERL_TOP)/erts/preloaded/ebin/otp_ring0.beam \
$(ERL_TOP)/erts/preloaded/ebin/zlib.beam \
$(ERL_TOP)/erts/preloaded/ebin/prim_zip.beam \
$(ERL_TOP)/erts/preloaded/ebin/erl_prim_loader.beam \
- $(ERL_TOP)/erts/preloaded/ebin/erlang.beam
- LANG=C $(PERL) utils/make_preload $(MAKE_PRELOAD_EXTRA) -rc $^ > $@
+ $(ERL_TOP)/erts/preloaded/ebin/erlang.beam \
+ $(ERL_TOP)/erts/preloaded/ebin/erts_internal.beam
+ $(gen_verbose)LANG=C $(PERL) utils/make_preload $(MAKE_PRELOAD_EXTRA) -rc $^ > $@
else
PRELOAD_OBJ = $(OBJDIR)/preload.o
PRELOAD_SRC = $(TARGET)/preload.c
@@ -579,8 +573,9 @@ $(PRELOAD_SRC): $(ERL_TOP)/erts/preloaded/ebin/otp_ring0.beam \
$(ERL_TOP)/erts/preloaded/ebin/zlib.beam \
$(ERL_TOP)/erts/preloaded/ebin/prim_zip.beam \
$(ERL_TOP)/erts/preloaded/ebin/erl_prim_loader.beam \
- $(ERL_TOP)/erts/preloaded/ebin/erlang.beam
- LANG=C $(PERL) utils/make_preload -old $^ > $@
+ $(ERL_TOP)/erts/preloaded/ebin/erlang.beam \
+ $(ERL_TOP)/erts/preloaded/ebin/erts_internal.beam
+ $(gen_verbose)LANG=C $(PERL) utils/make_preload -old $^ > $@
endif
.PHONY : generate
@@ -591,13 +586,13 @@ else
generate: $(TTF_DIR)/GENERATED $(PRELOAD_SRC)
$(TTF_DIR)/GENERATED: $(GENERATE)
- echo $? >$(TTF_DIR)/GENERATED
+ $(gen_verbose)echo $? >$(TTF_DIR)/GENERATED
endif
$(TARGET)/erlang_dtrace.h: beam/erlang_dtrace.d
- dtrace -h -C -Ibeam -s $< -o ./erlang_dtrace.tmp
- sed -e '/^#define[ ]*ERLANG_[A-Z0-9_]*(.*)/y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/' ./erlang_dtrace.tmp > $@
- rm ./erlang_dtrace.tmp
+ $(dtrace_verbose)dtrace -h -C -Ibeam -s $< -o ./erlang_dtrace.tmp
+ $(V_at)sed -e '/^#define[ ]*ERLANG_[A-Z0-9_]*(.*)/y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/' ./erlang_dtrace.tmp > $@
+ $(V_at)rm ./erlang_dtrace.tmp
# ----------------------------------------------------------------------
# Pattern rules
@@ -616,58 +611,47 @@ ifdef PERFCTR_PATH
INCLUDES += -I$(PERFCTR_PATH)/usr.lib -I$(PERFCTR_PATH)/linux/include
endif
-# Need to include etc dir on VxWorks
-ifeq ($(findstring vxworks,$(TARGET)),vxworks)
-INCLUDES += -I$(ERL_TOP)/erts/etc/vxworks
-endif
-
ifeq ($(TARGET),win32)
$(OBJDIR)/dll_sys.o: sys/$(ERLANG_OSTYPE)/sys.c
- $(CC) $(CFLAGS) -DERL_RUN_SHARED_LIB=1 $(INCLUDES) -c $< -o $@
+ $(V_CC) $(CFLAGS) -DERL_RUN_SHARED_LIB=1 $(INCLUDES) -c $< -o $@
$(OBJDIR)/beams.$(RES_EXT): $(TARGET)/beams.rc
- $(RC) -o $@ -I$(ERL_TOP)/erts/etc/win32 $(TARGET)/beams.rc
+ $(V_RC) -o $@ -I$(ERL_TOP)/erts/etc/win32 $(TARGET)/beams.rc
endif
ifneq ($(filter tile-%,$(TARGET)),)
$(OBJDIR)/beam_emu.o: beam/beam_emu.c
- $(CC) $(subst -O2, $(GEN_OPT_FLGS), $(CFLAGS)) \
+ $(V_CC) $(subst -O2, $(GEN_OPT_FLGS), $(CFLAGS)) \
-OPT:Olimit=0 -WOPT:lpre=off:spre=off:epre=off \
$(INCLUDES) -c $< -o $@
else
# Usually the same as the default rule, but certain platforms (e.g. win32) mix
# different compilers
$(OBJDIR)/beam_emu.o: beam/beam_emu.c
- $(EMU_CC) $(subst -O2, $(GEN_OPT_FLGS), $(CFLAGS)) $(INCLUDES) -c $< -o $@
+ $(V_EMU_CC) $(subst -O2, $(GEN_OPT_FLGS), $(CFLAGS)) $(INCLUDES) -c $< -o $@
endif
$(OBJDIR)/%.o: beam/%.c
- $(CC) $(subst -O2, $(GEN_OPT_FLGS), $(CFLAGS)) $(INCLUDES) -c $< -o $@
+ $(V_CC) $(subst -O2, $(GEN_OPT_FLGS), $(CFLAGS)) $(INCLUDES) -c $< -o $@
$(OBJDIR)/%.o: $(TARGET)/%.c
- $(CC) $(CFLAGS) $(INCLUDES) -Idrivers/common -c $< -o $@
+ $(V_CC) $(CFLAGS) $(INCLUDES) -Idrivers/common -c $< -o $@
$(OBJDIR)/%.o: $(TTF_DIR)/%.c
- $(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@
+ $(V_CC) $(CFLAGS) $(INCLUDES) -c $< -o $@
$(OBJDIR)/%.o: sys/$(ERLANG_OSTYPE)/%.c
- $(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@
+ $(V_CC) $(CFLAGS) $(INCLUDES) -c $< -o $@
$(OBJDIR)/%.o: sys/common/%.c
- $(CC) $(subst -O2, $(GEN_OPT_FLGS), $(CFLAGS)) $(INCLUDES) -c $< -o $@
+ $(V_CC) $(subst -O2, $(GEN_OPT_FLGS), $(CFLAGS)) $(INCLUDES) -c $< -o $@
$(OBJDIR)/%.o: drivers/common/%.c
- $(CC) $(CFLAGS) -DLIBSCTP=$(LIBSCTP) $(INCLUDES) -Idrivers/common -Idrivers/$(ERLANG_OSTYPE) -c $< -o $@
+ $(V_CC) $(CFLAGS) -DLIBSCTP=$(LIBSCTP) $(INCLUDES) -Idrivers/common -Idrivers/$(ERLANG_OSTYPE) -c $< -o $@
$(OBJDIR)/%.o: drivers/$(ERLANG_OSTYPE)/%.c
- $(CC) $(CFLAGS) $(INCLUDES) -Idrivers/common -Idrivers/$(ERLANG_OSTYPE) -I../etc/$(ERLANG_OSTYPE) -c $< -o $@
-
-# VxWorks uses unix drivers too...
-ifeq ($(findstring vxworks,$(TARGET)),vxworks)
-$(OBJDIR)/%.o: drivers/unix/%.c
- $(CC) $(CFLAGS) $(INCLUDES) -Idrivers/common -c $< -o $@
-endif
+ $(V_CC) $(CFLAGS) $(INCLUDES) -Idrivers/common -Idrivers/$(ERLANG_OSTYPE) -I../etc/$(ERLANG_OSTYPE) -c $< -o $@
# ----------------------------------------------------------------------
# Specials
@@ -675,19 +659,19 @@ endif
CS_SRC = sys/$(ERLANG_OSTYPE)/erl_child_setup.c
$(BINDIR)/$(CS_EXECUTABLE): $(TTF_DIR)/GENERATED $(PRELOAD_SRC) $(CS_SRC) $(ERTS_LIB)
- $(CS_PURIFY) $(CC) $(CS_LDFLAGS) -o $(BINDIR)/$(CS_EXECUTABLE) \
+ $(ld_verbose)$(CS_PURIFY) $(CC) $(CS_LDFLAGS) -o $(BINDIR)/$(CS_EXECUTABLE) \
$(CS_CFLAGS) $(COMMON_INCLUDES) $(CS_SRC) $(CS_LIBS)
$(OBJDIR)/%.kp.o: sys/common/%.c
- $(CC) -DERTS_KERNEL_POLL_VERSION $(subst -O2, $(GEN_OPT_FLGS), $(CFLAGS)) $(INCLUDES) -c $< -o $@
+ $(V_CC) -DERTS_KERNEL_POLL_VERSION $(subst -O2, $(GEN_OPT_FLGS), $(CFLAGS)) $(INCLUDES) -c $< -o $@
$(OBJDIR)/%.nkp.o: sys/common/%.c
- $(CC) -DERTS_NO_KERNEL_POLL_VERSION $(subst -O2, $(GEN_OPT_FLGS), $(CFLAGS)) $(INCLUDES) -c $< -o $@
+ $(V_CC) -DERTS_NO_KERNEL_POLL_VERSION $(subst -O2, $(GEN_OPT_FLGS), $(CFLAGS)) $(INCLUDES) -c $< -o $@
ifeq ($(GCC),yes)
$(OBJDIR)/erl_goodfit_alloc.o: beam/erl_goodfit_alloc.c
- $(CC) $(subst -O2, $(GEN_OPT_FLGS) $(UNROLL_FLG), $(CFLAGS)) $(INCLUDES) -c $< -o $@
+ $(V_CC) $(subst -O2, $(GEN_OPT_FLGS) $(UNROLL_FLG), $(CFLAGS)) $(INCLUDES) -c $< -o $@
endif
# ----------------------------------------------------------------------
@@ -753,7 +737,8 @@ RUN_OBJS = \
$(OBJDIR)/packet_parser.o $(OBJDIR)/safe_hash.o \
$(OBJDIR)/erl_zlib.o $(OBJDIR)/erl_nif.o \
$(OBJDIR)/erl_bif_binary.o $(OBJDIR)/erl_ao_firstfit_alloc.o \
- $(OBJDIR)/erl_thr_queue.o $(OBJDIR)/erl_sched_spec_pre_alloc.o
+ $(OBJDIR)/erl_thr_queue.o $(OBJDIR)/erl_sched_spec_pre_alloc.o \
+ $(OBJDIR)/erl_ptab.o
ifeq ($(TARGET),win32)
DRV_OBJS = \
@@ -781,12 +766,8 @@ OS_OBJS = \
$(OBJDIR)/gzio.o \
$(OBJDIR)/elib_memmove.o
-ifeq ($(findstring vxworks,$(TARGET)),vxworks)
- OS_OBJS += $(OBJDIR)/int64.o
-else
OS_OBJS += $(OBJDIR)/sys_float.o \
$(OBJDIR)/sys_time.o
-endif
DRV_OBJS = \
$(OBJDIR)/efile_drv.o \
$(OBJDIR)/inet_drv.o \
@@ -794,9 +775,7 @@ DRV_OBJS = \
$(OBJDIR)/ram_file_drv.o
endif
-ifneq ($(findstring vxworks,$(TARGET)),vxworks)
DRV_OBJS += $(OBJDIR)/ttsl_drv.o
-endif
ifeq ($(ERTS_ENABLE_KERNEL_POLL),yes)
OS_OBJS += $(OBJDIR)/erl_poll.kp.o \
@@ -865,28 +844,28 @@ $(OBJS): $(TTF_DIR)/GENERATED
M4FLAGS += -DTARGET=$(TARGET) -DOPSYS=$(OPSYS) -DARCH=$(ARCH)
$(TTF_DIR)/%.S: hipe/%.m4
- m4 $(M4FLAGS) $< > $@
+ $(m4_verbose)m4 $(M4FLAGS) $< > $@
$(TTF_DIR)/%.h: hipe/%.m4
- m4 $(M4FLAGS) $< > $@
+ $(m4_verbose)m4 $(M4FLAGS) $< > $@
$(OBJDIR)/%.o: $(TTF_DIR)/%.S
- $(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@
+ $(V_CC) $(CFLAGS) $(INCLUDES) -c $< -o $@
$(OBJDIR)/%.o: hipe/%.S
- $(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@
+ $(V_CC) $(CFLAGS) $(INCLUDES) -c $< -o $@
$(OBJDIR)/%.o: hipe/%.c
- $(CC) $(subst O2,O3, $(CFLAGS)) $(INCLUDES) -c $< -o $@
+ $(V_CC) $(subst O2,O3, $(CFLAGS)) $(INCLUDES) -c $< -o $@
$(BINDIR)/hipe_mkliterals$(TF_MARKER): $(OBJDIR)/hipe_mkliterals.o
- $(CC) $(CFLAGS) $(INCLUDES) -o $@ $<
+ $(ld_verbose)$(CC) $(CFLAGS) $(INCLUDES) -o $@ $<
$(OBJDIR)/hipe_mkliterals.o: $(HIPE_ASM) $(TTF_DIR)/erl_alloc_types.h \
$(TTF_DIR)/OPCODES-GENERATED $(TARGET)/TABLES-GENERATED
$(TTF_DIR)/hipe_literals.h: $(BINDIR)/hipe_mkliterals$(TF_MARKER)
- $(BINDIR)/hipe_mkliterals$(TF_MARKER) -c > $@
+ $(gen_verbose)$(BINDIR)/hipe_mkliterals$(TF_MARKER) -c > $@
$(OBJDIR)/hipe_x86_glue.o: hipe/hipe_x86_glue.S \
$(TTF_DIR)/hipe_x86_asm.h $(TTF_DIR)/hipe_literals.h \
@@ -929,49 +908,24 @@ $(OBJDIR)/hipe_arm_bifs.o: $(TTF_DIR)/hipe_arm_bifs.S \
# Use -fomit-frame-pointer to work around gcc (v4.5.2) bug causing
# "error: r7 cannot be used in asm here" for DEBUG build.
$(OBJDIR)/hipe_arm.o: hipe/hipe_arm.c
- $(CC) $(subst O2,O3, $(CFLAGS)) -fomit-frame-pointer $(INCLUDES) -c $< -o $@
+ $(V_CC) $(subst O2,O3, $(CFLAGS)) -fomit-frame-pointer $(INCLUDES) -c $< -o $@
# end of HiPE section
########################################
-ifeq ($(findstring vxworks,$(TARGET)),vxworks)
-########################################
-# Extract what we need from libgcc.a
-########################################
-GCCLIBFLAGS=@GCCLIBFLAGS@
-STRIP=@STRIP@
-SYMPREFIX=@SYMPREFIX@
-
-NEEDFUNCTIONS=__divdi3 __moddi3 __udivdi3
-KEEPSYMS=$(NEEDFUNCTIONS:%=-K $(SYMPREFIX)%)
-
-$(OBJDIR)/int64.o: $(TARGET)/int64.c
- $(CC) -o $(OBJDIR)/int64tmp.o -c $(TARGET)/int64.c
- $(LD) -o $(OBJDIR)/int64.o $(OBJDIR)/int64tmp.o $(LDFLAGS) $(GCCLIBFLAGS)
- $(STRIP) $(KEEPSYMS) $(OBJDIR)/int64.o
-
-$(TARGET)/int64.c:
- echo 'void dummy(void); void dummy(void) {' > $(TARGET)/int64.c
- for x in $(NEEDFUNCTIONS); do echo 'extern void '$$x'();' \
- >> $(TARGET)/int64.c; done
- for x in $(NEEDFUNCTIONS); do echo $$x'();' >> $(TARGET)/int64.c; done
- echo '}' >> $(TARGET)/int64.c
-
-endif
-
# ----------------------------------------------------------------------
# The emulator itself
ifeq ($(TARGET), win32)
# Only the basic erlang to begin with eh?
$(BINDIR)/$(EMULATOR_EXECUTABLE): $(INIT_OBJS) $(OBJS) $(DEPLIBS)
- $(PURIFY) $(LD) -dll -def:sys/$(ERLANG_OSTYPE)/erl.def -implib:$(BINDIR)/erl_dll.lib -o $(BINDIR)/$(EMULATOR_EXECUTABLE) \
+ $(ld_verbose)$(PURIFY) $(LD) -dll -def:sys/$(ERLANG_OSTYPE)/erl.def -implib:$(BINDIR)/erl_dll.lib -o $(BINDIR)/$(EMULATOR_EXECUTABLE) \
$(LDFLAGS) $(DEXPORT) $(INIT_OBJS) $(OBJS) $(LIBS)
else
$(BINDIR)/$(EMULATOR_EXECUTABLE): $(INIT_OBJS) $(OBJS) $(DEPLIBS)
- $(PURIFY) $(LD) -o $(BINDIR)/$(EMULATOR_EXECUTABLE) \
+ $(ld_verbose)$(PURIFY) $(LD) -o $(BINDIR)/$(EMULATOR_EXECUTABLE) \
$(HIPEBEAMLDFLAGS) $(LDFLAGS) $(DEXPORT) $(INIT_OBJS) $(OBJS) $(LIBS)
endif
@@ -1059,23 +1013,24 @@ depend:
else
depend: $(TTF_DIR)/depend.mk
$(TTF_DIR)/depend.mk: $(TTF_DIR)/GENERATED $(PRELOAD_SRC)
- $(DEP_CC) $(DEP_FLAGS) $(BEAM_SRC) \
+ $(gen_verbose)
+ $(V_at)$(DEP_CC) $(DEP_FLAGS) $(BEAM_SRC) \
| $(SED_DEPEND) > $(TTF_DIR)/depend.mk
- $(DEP_CC) $(DEP_FLAGS) -DLIBSCTP=$(LIBSCTP) $(DRV_COMMON_SRC) \
+ $(V_at)$(DEP_CC) $(DEP_FLAGS) -DLIBSCTP=$(LIBSCTP) $(DRV_COMMON_SRC) \
| $(SED_DEPEND) >> $(TTF_DIR)/depend.mk
- $(DEP_CC) $(DEP_FLAGS) -I../etc/$(ERLANG_OSTYPE) $(DRV_OSTYPE_SRC) \
+ $(V_at)$(DEP_CC) $(DEP_FLAGS) -I../etc/$(ERLANG_OSTYPE) $(DRV_OSTYPE_SRC) \
| $(SED_DEPEND) >> $(TTF_DIR)/depend.mk
- $(DEP_CC) $(DEP_FLAGS) $(SYS_SRC) \
+ $(V_at)$(DEP_CC) $(DEP_FLAGS) $(SYS_SRC) \
| $(SED_DEPEND) >> $(TTF_DIR)/depend.mk
- $(DEP_CC) $(DEP_FLAGS) $(TARGET_SRC) \
+ $(V_at)$(DEP_CC) $(DEP_FLAGS) $(TARGET_SRC) \
| $(SED_DEPEND) >> $(TTF_DIR)/depend.mk
- $(DEP_CC) $(DEP_FLAGS) $(ZLIB_SRC) \
+ $(V_at)$(DEP_CC) $(DEP_FLAGS) $(ZLIB_SRC) \
| $(SED_DEPEND_ZLIB) >> $(TTF_DIR)/depend.mk
ifdef HIPE_ENABLED
- $(DEP_CC) $(DEP_FLAGS) $(HIPE_SRC) \
+ $(V_at)$(DEP_CC) $(DEP_FLAGS) $(HIPE_SRC) \
| $(SED_DEPEND) >> $(TTF_DIR)/depend.mk
endif
- cd $(ERTS_LIB_DIR) && $(MAKE) depend
+ $(V_at)cd $(ERTS_LIB_DIR) && $(MAKE) depend
endif
ifneq ($(MAKECMDGOALS),clean)
diff --git a/erts/emulator/beam/atom.c b/erts/emulator/beam/atom.c
index d7c7f117cf..82dd320ea9 100644
--- a/erts/emulator/beam/atom.c
+++ b/erts/emulator/beam/atom.c
@@ -111,7 +111,7 @@ atom_text_alloc(int bytes)
{
byte *res;
- ASSERT(bytes <= MAX_ATOM_LENGTH);
+ ASSERT(bytes <= MAX_ATOM_SZ_LIMIT);
if (atom_text_pos + bytes >= atom_text_end) {
more_atom_space();
}
@@ -162,6 +162,7 @@ atom_alloc(Atom* tmpl)
obj->name = atom_text_alloc(tmpl->len);
sys_memcpy(obj->name, tmpl->name, tmpl->len);
obj->len = tmpl->len;
+ obj->latin1_chars = tmpl->latin1_chars;
obj->slot.index = -1;
/*
@@ -192,44 +193,146 @@ atom_free(Atom* obj)
erts_free(ERTS_ALC_T_ATOM, (void*) obj);
}
+static void latin1_to_utf8(byte* conv_buf, const byte** srcp, int* lenp)
+{
+ byte* dst;
+ const byte* src = *srcp;
+ int i, len = *lenp;
+
+ for (i=0 ; i < len; ++i) {
+ if (src[i] & 0x80) {
+ goto need_convertion;
+ }
+ }
+ return;
+
+need_convertion:
+ sys_memcpy(conv_buf, src, i);
+ dst = conv_buf + i;
+ for ( ; i < len; ++i) {
+ unsigned char chr = src[i];
+ if (!(chr & 0x80)) {
+ *dst++ = chr;
+ }
+ else {
+ *dst++ = 0xC0 | (chr >> 6);
+ *dst++ = 0x80 | (chr & 0x3F);
+ }
+ }
+ *srcp = conv_buf;
+ *lenp = dst - conv_buf;
+}
+
+/*
+ * erts_atom_put() may fail. If it fails THE_NON_VALUE is returned!
+ */
Eterm
-am_atom_put(const char* name, int len)
+erts_atom_put(const byte *name, int len, ErtsAtomEncoding enc, int trunc)
{
+ byte utf8_copy[MAX_ATOM_SZ_FROM_LATIN1];
+ const byte *text = name;
+ int tlen = len;
+ Sint no_latin1_chars;
Atom a;
- Eterm ret;
int aix;
- /*
- * Silently truncate the atom if it is too long. Overlong atoms
- * could occur in situations where we have no good way to return
- * an error, such as in the I/O system. (Unfortunately, many
- * drivers don't check for errors.)
- *
- * If an error should be produced for overlong atoms (such in
- * list_to_atom/1), the caller should check the length before
- * calling this function.
- */
- if (len > MAX_ATOM_LENGTH) {
- len = MAX_ATOM_LENGTH;
- }
#ifdef ERTS_ATOM_PUT_OPS_STAT
erts_smp_atomic_inc_nob(&atom_put_ops);
#endif
- a.len = len;
- a.name = (byte*)name;
+
+ if (tlen < 0) {
+ if (trunc)
+ tlen = 0;
+ else
+ return THE_NON_VALUE;
+ }
+
+ switch (enc) {
+ case ERTS_ATOM_ENC_7BIT_ASCII:
+ if (tlen > MAX_ATOM_CHARACTERS) {
+ if (trunc)
+ tlen = MAX_ATOM_CHARACTERS;
+ else
+ return THE_NON_VALUE;
+ }
+#ifdef DEBUG
+ for (aix = 0; aix < len; aix++) {
+ ASSERT((name[aix] & 0x80) == 0);
+ }
+#endif
+ no_latin1_chars = tlen;
+ break;
+ case ERTS_ATOM_ENC_LATIN1:
+ if (tlen > MAX_ATOM_CHARACTERS) {
+ if (trunc)
+ tlen = MAX_ATOM_CHARACTERS;
+ else
+ return THE_NON_VALUE;
+ }
+ no_latin1_chars = tlen;
+ latin1_to_utf8(utf8_copy, &text, &tlen);
+ break;
+ case ERTS_ATOM_ENC_UTF8:
+ /* First sanity check; need to verify later */
+ if (tlen > MAX_ATOM_SZ_LIMIT && !trunc)
+ return THE_NON_VALUE;
+ break;
+ }
+
+ a.len = tlen;
+ a.name = (byte *) text;
atom_read_lock();
aix = index_get(&erts_atom_table, (void*) &a);
atom_read_unlock();
- if (aix >= 0)
- ret = make_atom(aix);
- else {
- atom_write_lock();
- ret = make_atom(index_put(&erts_atom_table, (void*) &a));
- atom_write_unlock();
+ if (aix >= 0) {
+ /* Already in table no need to verify it */
+ return make_atom(aix);
}
- return ret;
+
+ if (enc == ERTS_ATOM_ENC_UTF8) {
+ /* Need to verify encoding and length */
+ byte *err_pos;
+ Uint no_chars;
+ switch (erts_analyze_utf8_x((byte *) text,
+ (Uint) tlen,
+ &err_pos,
+ &no_chars, NULL,
+ &no_latin1_chars,
+ MAX_ATOM_CHARACTERS)) {
+ case ERTS_UTF8_OK:
+ ASSERT(no_chars <= MAX_ATOM_CHARACTERS);
+ break;
+ case ERTS_UTF8_OK_MAX_CHARS:
+ /* Truncated... */
+ if (!trunc)
+ return THE_NON_VALUE;
+ ASSERT(no_chars == MAX_ATOM_CHARACTERS);
+ tlen = err_pos - text;
+ break;
+ default:
+ /* Bad utf8... */
+ return THE_NON_VALUE;
+ }
+ }
+
+ ASSERT(tlen <= MAX_ATOM_SZ_LIMIT);
+ ASSERT(-1 <= no_latin1_chars && no_latin1_chars <= MAX_ATOM_CHARACTERS);
+
+ a.len = tlen;
+ a.latin1_chars = (Sint16) no_latin1_chars;
+ a.name = (byte *) text;
+ atom_write_lock();
+ aix = index_put(&erts_atom_table, (void*) &a);
+ atom_write_unlock();
+ return make_atom(aix);
}
+Eterm
+am_atom_put(const char* name, int len)
+{
+ /* Assumes 7-bit ascii; use erts_atom_put() for other encodings... */
+ return erts_atom_put((byte *) name, len, ERTS_ATOM_ENC_7BIT_ASCII, 1);
+}
int atom_table_size(void)
{
@@ -264,14 +367,19 @@ int atom_table_sz(void)
}
int
-erts_atom_get(const char *name, int len, Eterm* ap)
+erts_atom_get(const char *name, int len, Eterm* ap, int is_latin1)
{
+ byte utf8_copy[MAX_ATOM_SZ_FROM_LATIN1];
Atom a;
int i;
int res;
- a.len = len;
+ a.len = (Sint16) len;
a.name = (byte *)name;
+ if (is_latin1) {
+ latin1_to_utf8(utf8_copy, (const byte**)&a.name, &len);
+ a.len = (Sint16) len;
+ }
atom_read_lock();
i = index_get(&erts_atom_table, (void*) &a);
res = i < 0 ? 0 : (*ap = make_atom(i), 1);
@@ -333,8 +441,15 @@ init_atom_table(void)
for (i = 0; erl_atom_names[i] != 0; i++) {
int ix;
a.len = strlen(erl_atom_names[i]);
+ a.latin1_chars = a.len;
a.name = (byte*)erl_atom_names[i];
a.slot.index = i;
+#ifdef DEBUG
+ /* Verify 7-bit ascii */
+ for (ix = 0; ix < a.len; ix++) {
+ ASSERT((a.name[ix] & 0x80) == 0);
+ }
+#endif
ix = index_put(&erts_atom_table, (void*) &a);
atom_text_pos -= a.len;
atom_space -= a.len;
diff --git a/erts/emulator/beam/atom.h b/erts/emulator/beam/atom.h
index fd9c04d3d0..f721999a4c 100644
--- a/erts/emulator/beam/atom.h
+++ b/erts/emulator/beam/atom.h
@@ -26,7 +26,9 @@
#include "erl_atom_table.h"
-#define MAX_ATOM_LENGTH 255
+#define MAX_ATOM_CHARACTERS 255
+#define MAX_ATOM_SZ_FROM_LATIN1 (2*MAX_ATOM_CHARACTERS)
+#define MAX_ATOM_SZ_LIMIT (4*MAX_ATOM_CHARACTERS) /* theoretical byte limit */
#define ATOM_LIMIT (1024*1024)
#define MIN_ATOM_TABLE_SIZE 8192
@@ -45,7 +47,8 @@
*/
typedef struct atom {
IndexSlot slot; /* MUST BE LOCATED AT TOP OF STRUCT!!! */
- int len; /* length of atom name */
+ Sint16 len; /* length of atom name (UTF-8 encoded) */
+ Sint16 latin1_chars; /* 0-255 if atom can be encoded in latin1; otherwise, -1 */
int ord0; /* ordinal value of first 3 bytes + 7 bits */
byte* name; /* name of atom */
} Atom;
@@ -53,8 +56,8 @@ typedef struct atom {
extern IndexTable erts_atom_table;
ERTS_GLB_INLINE Atom* atom_tab(Uint i);
-ERTS_GLB_INLINE int erts_is_atom_bytes(byte *text, size_t len, Eterm term);
-ERTS_GLB_INLINE int erts_is_atom_str(char *str, Eterm term);
+ERTS_GLB_INLINE int erts_is_atom_utf8_bytes(byte *text, size_t len, Eterm term);
+ERTS_GLB_INLINE int erts_is_atom_str(const char *str, Eterm term, int is_latin1);
#if ERTS_GLB_INLINE_INCL_FUNC_DEF
ERTS_GLB_INLINE Atom*
@@ -63,7 +66,7 @@ atom_tab(Uint i)
return (Atom *) erts_index_lookup(&erts_atom_table, i);
}
-ERTS_GLB_INLINE int erts_is_atom_bytes(byte *text, size_t len, Eterm term)
+ERTS_GLB_INLINE int erts_is_atom_utf8_bytes(byte *text, size_t len, Eterm term)
{
Atom *a;
if (!is_atom(term))
@@ -73,43 +76,70 @@ ERTS_GLB_INLINE int erts_is_atom_bytes(byte *text, size_t len, Eterm term)
&& sys_memcmp((void *) a->name, (void *) text, len) == 0);
}
-ERTS_GLB_INLINE int erts_is_atom_str(char *str, Eterm term)
+ERTS_GLB_INLINE int erts_is_atom_str(const char *str, Eterm term, int is_latin1)
{
Atom *a;
int i, len;
- char *aname;
+ const byte* aname;
+ const byte* s = (const byte*) str;
+
if (!is_atom(term))
return 0;
a = atom_tab(atom_val(term));
len = a->len;
- aname = (char *) a->name;
- for (i = 0; i < len; i++)
- if (aname[i] != str[i] || str[i] == '\0')
- return 0;
- return str[len] == '\0';
+ aname = a->name;
+ if (is_latin1) {
+ for (i = 0; i < len; s++) {
+ if (aname[i] < 0x80) {
+ if (aname[i] != *s || *s == '\0')
+ return 0;
+ i++;
+ }
+ else {
+ if (aname[i] != (0xC0 | (*s >> 6)) ||
+ aname[i+1] != (0x80 | (*s & 0x3F))) {
+ return 0;
+ }
+ i += 2;
+ }
+ }
+ }
+ else {
+ for (i = 0; i < len; i++, s++)
+ if (aname[i] != *s || *s == '\0')
+ return 0;
+ }
+ return *s == '\0';
}
#endif
+typedef enum {
+ ERTS_ATOM_ENC_7BIT_ASCII,
+ ERTS_ATOM_ENC_LATIN1,
+ ERTS_ATOM_ENC_UTF8
+} ErtsAtomEncoding;
+
/*
* Note, ERTS_IS_ATOM_STR() expects the first argument to be a
- * string literal.
+ * 7-bit ASCII string literal.
*/
#define ERTS_IS_ATOM_STR(LSTR, TERM) \
- (erts_is_atom_bytes((byte *) LSTR, sizeof(LSTR) - 1, (TERM)))
+ (erts_is_atom_utf8_bytes((byte *) LSTR, sizeof(LSTR) - 1, (TERM)))
#define ERTS_DECL_AM(S) Eterm AM_ ## S = am_atom_put(#S, sizeof(#S) - 1)
#define ERTS_INIT_AM(S) AM_ ## S = am_atom_put(#S, sizeof(#S) - 1)
int atom_table_size(void); /* number of elements */
int atom_table_sz(void); /* table size in bytes, excluding stored objects */
-Eterm am_atom_put(const char*, int); /* most callers pass plain char*'s */
+Eterm am_atom_put(const char*, int); /* ONLY 7-bit ascii! */
+Eterm erts_atom_put(const byte *name, int len, ErtsAtomEncoding enc, int trunc);
int atom_erase(byte*, int);
int atom_static_put(byte*, int);
void init_atom_table(void);
void atom_info(int, void *);
void dump_atoms(int, void *);
-int erts_atom_get(const char* name, int len, Eterm* ap);
+int erts_atom_get(const char* name, int len, Eterm* ap, int is_latin1);
void erts_atom_get_text_space_sizes(Uint *reserved, Uint *used);
#endif
diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names
index 106fad030b..590b2fc960 100644
--- a/erts/emulator/beam/atom.names
+++ b/erts/emulator/beam/atom.names
@@ -18,6 +18,8 @@
#
#
+# IMPORTANT! All atoms defined here *need* to be in 7-bit ascii!
+#
# File format:
#
# Lines starting with '#' are ignored.
@@ -94,6 +96,7 @@ atom asynchronous
atom atom
atom atom_used
atom attributes
+atom await_port_send_result
atom await_proc_exit
atom await_sched_wall_time_modifications
atom awaiting_load
@@ -143,6 +146,7 @@ atom close
atom closed
atom code
atom command
+atom compact
atom compat_rel
atom compile
atom compressed
@@ -152,6 +156,7 @@ atom connection_closed
atom cons
atom const
atom context_switches
+atom control
atom copy
atom cpu
atom cpu_timestamp
@@ -163,6 +168,7 @@ atom current_location
atom current_stacktrace
atom data
atom debug_flags
+atom decimals
atom delay_trap
atom dexit
atom depth
@@ -204,6 +210,7 @@ atom erlang
atom ERROR='ERROR'
atom error_handler
atom error_logger
+atom erts_internal
atom ets
atom ETS_TRANSFER='ETS-TRANSFER'
atom event
@@ -237,6 +244,7 @@ atom gc_end
atom gc_start
atom Ge='>='
atom generational
+atom get_data
atom get_seq_token
atom get_tcw
atom getenv
@@ -252,6 +260,7 @@ atom heap_block_size
atom heap_size
atom heap_sizes
atom heap_type
+atom heart_port
atom heir
atom hidden
atom hide
@@ -407,6 +416,7 @@ atom overlapped_io
atom owner
atom packet
atom packet_size
+atom parallelism
atom Plus='+'
atom pause
atom pending
@@ -418,12 +428,12 @@ atom pid
atom port
atom ports
atom port_count
+atom port_limit
atom print
atom priority
atom private
atom process
atom processes
-atom processes_trap
atom processes_used
atom process_count
atom process_display
@@ -433,6 +443,7 @@ atom procs
atom profile
atom protected
atom protection
+atom ptab_list_continue
atom public
atom purify
atom quantify
@@ -473,6 +484,7 @@ atom scheduler
atom scheduler_id
atom schedulers_online
atom scheme
+atom scientific
atom scope
atom sensitive
atom sequential_tracer
@@ -480,6 +492,7 @@ atom sequential_trace_token
atom serial
atom set
atom set_cpu_topology
+atom set_data
atom set_on_first_link
atom set_on_first_spawn
atom set_on_link
diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c
index 94f8edf165..e0a4f86d2d 100644
--- a/erts/emulator/beam/beam_bif_load.c
+++ b/erts/emulator/beam/beam_bif_load.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1999-2011. All Rights Reserved.
+ * Copyright Ericsson AB 1999-2012. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -148,6 +148,15 @@ struct m {
};
static Eterm staging_epilogue(Process* c_p, int, Eterm res, int, struct m*, int);
+#ifdef ERTS_SMP
+static void smp_code_ix_commiter(void*);
+
+static struct /* Protected by code_write_permission */
+{
+ Process* stager;
+ ErtsThrPrgrLaterOp lop;
+}commiter_state;
+#endif
static Eterm
exception_list(Process* p, Eterm tag, struct m* mp, Sint exceptions)
@@ -347,7 +356,6 @@ staging_epilogue(Process* c_p, int commit, Eterm res, int is_blocking,
}
#ifdef ERTS_SMP
else {
- ErtsThrPrgrVal later;
ASSERT(is_value(res));
if (loaded) {
@@ -356,17 +364,17 @@ staging_epilogue(Process* c_p, int commit, Eterm res, int is_blocking,
erts_end_staging_code_ix();
/*
* Now we must wait for all schedulers to do a memory barrier before
- * we can activate and let them access the new staged code. This allows
+ * we can commit and let them access the new staged code. This allows
* schedulers to read active code_ix in a safe way while executing
* without any memory barriers at all.
*/
-
- later = erts_thr_progress_later(c_p->scheduler_data);
- erts_thr_progress_wakeup(c_p->scheduler_data, later);
- erts_notify_code_ix_activation(c_p, later);
+ ASSERT(commiter_state.stager == NULL);
+ commiter_state.stager = c_p;
+ erts_schedule_thr_prgr_later_op(smp_code_ix_commiter, NULL, &commiter_state.lop);
+ erts_smp_proc_inc_refc(c_p);
erts_suspend(c_p, ERTS_PROC_LOCK_MAIN, NULL);
/*
- * handle_code_ix_activation() will do the rest "later"
+ * smp_code_ix_commiter() will do the rest "later"
* and resume this process to return 'res'.
*/
ERTS_BIF_YIELD_RETURN(c_p, res);
@@ -374,6 +382,28 @@ staging_epilogue(Process* c_p, int commit, Eterm res, int is_blocking,
#endif
}
+
+#ifdef ERTS_SMP
+static void smp_code_ix_commiter(void* null)
+{
+ Process* p = commiter_state.stager;
+
+ erts_commit_staging_code_ix();
+ erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS);
+ if (!ERTS_PROC_IS_EXITING(p)) {
+ erts_resume(p, ERTS_PROC_LOCK_STATUS);
+ }
+ erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
+ erts_smp_proc_dec_refc(p);
+#ifdef DEBUG
+ commiter_state.stager = NULL;
+#endif
+ erts_release_code_write_permission();
+}
+#endif /* ERTS_SMP */
+
+
+
BIF_RETTYPE
check_old_code_1(BIF_ALIST_1)
{
@@ -408,8 +438,7 @@ check_process_code_2(BIF_ALIST_2)
if (is_internal_pid(BIF_ARG_1)) {
Eterm res;
ErtsCodeIndex code_ix;
- if (internal_pid_index(BIF_ARG_1) >= erts_max_processes)
- goto error;
+
code_ix = erts_active_code_ix();
modp = erts_get_module(BIF_ARG_2, code_ix);
if (modp == NULL) { /* Doesn't exist. */
diff --git a/erts/emulator/beam/beam_bp.c b/erts/emulator/beam/beam_bp.c
index 50d18b0347..c1e11f6448 100644
--- a/erts/emulator/beam/beam_bp.c
+++ b/erts/emulator/beam/beam_bp.c
@@ -65,10 +65,10 @@
#define ERTS_BPF_ALL 0xFF
-extern Eterm beam_return_to_trace[1]; /* OpCode(i_return_to_trace) */
-extern Eterm beam_return_trace[1]; /* OpCode(i_return_trace) */
-extern Eterm beam_exception_trace[1]; /* OpCode(i_exception_trace) */
-extern Eterm beam_return_time_trace[1]; /* OpCode(i_return_time_trace) */
+extern BeamInstr beam_return_to_trace[1]; /* OpCode(i_return_to_trace) */
+extern BeamInstr beam_return_trace[1]; /* OpCode(i_return_trace) */
+extern BeamInstr beam_exception_trace[1]; /* OpCode(i_exception_trace) */
+extern BeamInstr beam_return_time_trace[1]; /* OpCode(i_return_time_trace) */
erts_smp_atomic32_t erts_active_bp_index;
erts_smp_atomic32_t erts_staging_bp_index;
@@ -161,7 +161,7 @@ erts_bp_match_functions(BpFunctions* f, Eterm mfa[3], int specified)
for (current = 0; current < num_modules; current++) {
BeamInstr** code_base = (BeamInstr **) module[current]->curr.code;
BeamInstr* code;
- Uint num_functions = (Uint) code_base[MI_NUM_FUNCTIONS];
+ Uint num_functions = (Uint)(UWord) code_base[MI_NUM_FUNCTIONS];
Uint fi;
if (specified > 0) {
@@ -172,7 +172,7 @@ erts_bp_match_functions(BpFunctions* f, Eterm mfa[3], int specified)
}
for (fi = 0; fi < num_functions; fi++) {
- Eterm* pc;
+ BeamInstr* pc;
int wi;
code = code_base[MI_FUNCTIONS+fi];
@@ -254,7 +254,7 @@ erts_consolidate_bp_data(BpFunctions* f, int local)
Uint i;
Uint n = f->matched;
- ERTS_SMP_LC_ASSERT(erts_is_code_ix_locked());
+ ERTS_SMP_LC_ASSERT(erts_has_code_write_permission());
for (i = 0; i < n; i++) {
consolidate_bp_data(fs[i].mod, fs[i].pc, local);
@@ -266,7 +266,7 @@ erts_consolidate_bif_bp_data(void)
{
int i;
- ERTS_SMP_LC_ASSERT(erts_is_code_ix_locked());
+ ERTS_SMP_LC_ASSERT(erts_has_code_write_permission());
for (i = 0; i < BIF_SIZE; i++) {
Export *ep = bif_export[i];
consolidate_bp_data(0, ep->code+3, 0);
@@ -555,7 +555,7 @@ erts_clear_module_break(Module *modp) {
if (code_base == NULL) {
return 0;
}
- n = (Uint) code_base[MI_NUM_FUNCTIONS];
+ n = (Uint)(UWord) code_base[MI_NUM_FUNCTIONS];
for (i = 0; i < n; ++i) {
BeamInstr* pc;
@@ -692,7 +692,7 @@ erts_bif_trace(int bif_index, Process* p, Eterm* args, BeamInstr* I)
* export entry */
BeamInstr *cp = p->cp;
GenericBp* g;
- GenericBpData* bp;
+ GenericBpData* bp = NULL;
Uint bp_flags = 0;
ERTS_SMP_CHK_HAVE_ONLY_MAIN_PROC_LOCK(p);
@@ -714,7 +714,7 @@ erts_bif_trace(int bif_index, Process* p, Eterm* args, BeamInstr* I)
IS_TRACED_FL(p, F_TRACE_CALLS)) {
int local = !!(bp_flags & ERTS_BPF_LOCAL_TRACE);
flags = erts_call_trace(p, ep->code, bp->local_ms, args,
- local, &p->tracer_proc);
+ local, &ERTS_TRACER_PROC(p));
}
if (bp_flags & ERTS_BPF_META_TRACE) {
Eterm tpid1, tpid2;
@@ -800,7 +800,7 @@ erts_bif_trace(int bif_index, Process* p, Eterm* args, BeamInstr* I)
}
if (flags & MATCH_SET_EXCEPTION_TRACE) {
erts_trace_exception(p, ep->code, class, value,
- &p->tracer_proc);
+ &ERTS_TRACER_PROC(p));
}
if ((flags & MATCH_SET_RETURN_TO_TRACE) && p->catches > 0) {
/* can only happen if(local)*/
@@ -825,7 +825,7 @@ erts_bif_trace(int bif_index, Process* p, Eterm* args, BeamInstr* I)
UnUseTmpHeapNoproc(3);
if ((flags_meta|flags) & MATCH_SET_EXCEPTION_TRACE) {
erts_smp_proc_lock(p, ERTS_PROC_LOCKS_ALL_MINOR);
- p->trace_flags |= F_EXCEPTION_TRACE;
+ ERTS_TRACE_FLAGS(p) |= F_EXCEPTION_TRACE;
erts_smp_proc_unlock(p, ERTS_PROC_LOCKS_ALL_MINOR);
}
}
@@ -835,7 +835,7 @@ erts_bif_trace(int bif_index, Process* p, Eterm* args, BeamInstr* I)
}
/* MATCH_SET_RETURN_TO_TRACE cannot occur if(meta) */
if (flags & MATCH_SET_RX_TRACE) {
- erts_trace_return(p, ep->code, result, &p->tracer_proc);
+ erts_trace_return(p, ep->code, result, &ERTS_TRACER_PROC(p));
}
if (flags & MATCH_SET_RETURN_TO_TRACE) {
/* can only happen if(local)*/
@@ -919,7 +919,7 @@ do_call_trace(Process* c_p, BeamInstr* I, Eterm* reg,
E -= 1;
ASSERT(c_p->htop <= E && E <= c_p->hend);
E[0] = make_cp(c_p->cp);
- c_p->cp = (BeamInstr *) beam_return_to_trace;
+ c_p->cp = beam_return_to_trace;
}
if (flags & MATCH_SET_RX_TRACE) {
E -= 3;
@@ -935,7 +935,7 @@ do_call_trace(Process* c_p, BeamInstr* I, Eterm* reg,
c_p->cp = (flags & MATCH_SET_EXCEPTION_TRACE) ?
beam_exception_trace : beam_return_trace;
erts_smp_proc_lock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
- c_p->trace_flags |= F_EXCEPTION_TRACE;
+ ERTS_TRACE_FLAGS(c_p) |= F_EXCEPTION_TRACE;
erts_smp_proc_unlock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
}
c_p->stop = E;
@@ -974,7 +974,7 @@ erts_trace_time_call(Process* c_p, BeamInstr* I, BpDataTime* bdt)
ASSERT(pbt->pc);
/* add time to previous code */
bp_time_diff(&sitem, pbt, ms, s, us);
- sitem.pid = c_p->id;
+ sitem.pid = c_p->common.id;
sitem.count = 0;
/* previous breakpoint */
@@ -997,7 +997,7 @@ erts_trace_time_call(Process* c_p, BeamInstr* I, BpDataTime* bdt)
}
/* Add count to this code */
- sitem.pid = c_p->id;
+ sitem.pid = c_p->common.id;
sitem.count = 1;
sitem.s_time = 0;
sitem.us_time = 0;
@@ -1055,7 +1055,7 @@ erts_trace_time_return(Process *p, BeamInstr *pc)
ASSERT(pbt->pc);
bp_time_diff(&sitem, pbt, ms, s, us);
- sitem.pid = p->id;
+ sitem.pid = p->common.id;
sitem.count = 0;
/* previous breakpoint */
@@ -1386,7 +1386,7 @@ void erts_schedule_time_break(Process *p, Uint schedule) {
if (pbdt) {
get_sys_now(&ms,&s,&us);
bp_time_diff(&sitem, pbt, ms, s, us);
- sitem.pid = p->id;
+ sitem.pid = p->common.id;
sitem.count = 0;
h = &(pbdt->hash[bp_sched2ix_proc(p)]);
@@ -1449,7 +1449,7 @@ set_function_break(BeamInstr *pc, Binary *match_spec, Uint break_flags,
Uint common;
ErtsBpIndex ix = erts_staging_bp_ix();
- ERTS_SMP_LC_ASSERT(erts_is_code_ix_locked());
+ ERTS_SMP_LC_ASSERT(erts_has_code_write_permission());
g = (GenericBp *) pc[-4];
if (g == 0) {
int i;
@@ -1565,7 +1565,7 @@ clear_function_break(BeamInstr *pc, Uint break_flags)
Uint common;
ErtsBpIndex ix = erts_staging_bp_ix();
- ERTS_SMP_LC_ASSERT(erts_is_code_ix_locked());
+ ERTS_SMP_LC_ASSERT(erts_has_code_write_permission());
if ((g = (GenericBp *) pc[-4]) == 0) {
return 1;
diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c
index efce070061..0e9d140908 100644
--- a/erts/emulator/beam/beam_emu.c
+++ b/erts/emulator/beam/beam_emu.c
@@ -231,11 +231,6 @@ BeamInstr beam_return_time_trace[1]; /* OpCode(i_return_time_trace) */
/*
- * We should warn only once for tuple funs.
- */
-static erts_smp_atomic_t warned_for_tuple_funs;
-
-/*
* All Beam instructions in numerical order.
*/
@@ -521,7 +516,7 @@ extern int count_instructions;
# define Dispatchfun() DispatchMacroFun()
#endif
-#define Self(R) R = c_p->id
+#define Self(R) R = c_p->common.id
#define Node(R) R = erts_this_node->sysname
#define Arg(N) I[(N)+1]
@@ -960,17 +955,9 @@ static void save_stacktrace(Process* c_p, BeamInstr* pc, Eterm* reg,
static struct StackTrace * get_trace_from_exc(Eterm exc);
static Eterm make_arglist(Process* c_p, Eterm* reg, int a);
-#if defined(VXWORKS)
-static int init_done;
-#endif
-
void
init_emulator(void)
{
-#if defined(VXWORKS)
- init_done = 0;
-#endif
- erts_smp_atomic_init_nob(&warned_for_tuple_funs, (erts_aint_t) 0);
process_main();
}
@@ -1087,11 +1074,11 @@ init_emulator(void)
void
dtrace_drvport_str(ErlDrvPort drvport, char *port_buf)
{
- Port *port = erts_drvport2port(drvport);
+ Port *port = erts_drvport2port(drvport, NULL);
erts_snprintf(port_buf, DTRACE_TERM_BUF_SIZE, "#Port<%lu.%lu>",
- port_channel_no(port->id),
- port_number(port->id));
+ port_channel_no(port->common.id),
+ port_number(port->common.id));
}
#endif
/*
@@ -1102,9 +1089,7 @@ dtrace_drvport_str(ErlDrvPort drvport, char *port_buf)
*/
void process_main(void)
{
-#if !defined(VXWORKS)
static int init_done = 0;
-#endif
Process* c_p = NULL;
int reds_used;
#ifdef DEBUG
@@ -1210,7 +1195,7 @@ void process_main(void)
c_p = schedule(c_p, reds_used);
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
#ifdef DEBUG
- pid = c_p->id; /* Save for debugging purpouses */
+ pid = c_p->common.id; /* Save for debugging purpouses */
#endif
ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p);
PROCESS_MAIN_CHK_LOCKS(c_p);
@@ -1242,7 +1227,7 @@ void process_main(void)
reds = c_p->fcalls;
if (ERTS_PROC_GET_SAVED_CALLS_BUF(c_p)
- && (c_p->trace_flags & F_SENSITIVE) == 0) {
+ && (ERTS_TRACE_FLAGS(c_p) & F_SENSITIVE) == 0) {
neg_o_reds = -reds;
FCALLS = REDS_IN(c_p) = 0;
} else {
@@ -1606,6 +1591,7 @@ void process_main(void)
reg[0] = r(0);
result = erl_send(c_p, r(0), x(1));
PreFetch(0, next);
+ ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p);
PROCESS_MAIN_CHK_LOCKS(c_p);
if (c_p->mbuf || MSO(c_p).overhead >= BIN_VHEAP_SZ(c_p)) {
@@ -1881,14 +1867,14 @@ void process_main(void)
erts_fprintf(stderr,
"Dtrace -> (%T) stop spreading "
"tag %T with message %T\r\n",
- c_p->id,DT_UTAG(c_p),ERL_MESSAGE_TERM(msgp));
+ c_p->common.id,DT_UTAG(c_p),ERL_MESSAGE_TERM(msgp));
#endif
} else {
#ifdef DTRACE_TAG_HARDDEBUG
erts_fprintf(stderr,
"Dtrace -> (%T) kill tag %T with "
"message %T\r\n",
- c_p->id,DT_UTAG(c_p),ERL_MESSAGE_TERM(msgp));
+ c_p->common.id,DT_UTAG(c_p),ERL_MESSAGE_TERM(msgp));
#endif
DT_UTAG(c_p) = NIL;
SEQ_TRACE_TOKEN(c_p) = NIL;
@@ -1913,7 +1899,7 @@ void process_main(void)
erts_fprintf(stderr,
"Dtrace -> (%T) receive tag (%T) "
"with message %T\r\n",
- c_p->id, DT_UTAG(c_p), ERL_MESSAGE_TERM(msgp));
+ c_p->common.id, DT_UTAG(c_p), ERL_MESSAGE_TERM(msgp));
#endif
} else {
#endif
@@ -1929,7 +1915,7 @@ void process_main(void)
}
msg = ERL_MESSAGE_TERM(msgp);
seq_trace_output(SEQ_TRACE_TOKEN(c_p), msg, SEQ_TRACE_RECEIVE,
- c_p->id, c_p);
+ c_p->common.id, c_p);
#ifdef USE_VM_PROBES
}
#endif
@@ -2582,6 +2568,7 @@ void process_main(void)
reg[0] = r(0);
result = (*bf)(c_p, reg, I);
ASSERT(!ERTS_PROC_IS_EXITING(c_p) || is_non_value(result));
+ ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
ERTS_HOLE_CHECK(c_p);
ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p);
PROCESS_MAIN_CHK_LOCKS(c_p);
@@ -3316,7 +3303,6 @@ void process_main(void)
PROCESS_MAIN_CHK_LOCKS(c_p);
bif_nif_arity = I[-1];
ERTS_SMP_UNREQ_PROC_MAIN_LOCK(c_p);
- ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
ASSERT(!ERTS_PROC_IS_EXITING(c_p));
{
@@ -3361,7 +3347,6 @@ void process_main(void)
bif_nif_arity = I[-1];
ASSERT(bif_nif_arity <= 3);
ERTS_SMP_UNREQ_PROC_MAIN_LOCK(c_p);
- ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
reg[0] = r(0);
{
Eterm (*bf)(Process*, Eterm*, BeamInstr*) = vbf;
@@ -5272,7 +5257,7 @@ terminate_proc(Process* c_p, Eterm Value)
/* EXF_LOG is a primary exception flag */
if (c_p->freason & EXF_LOG) {
erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf();
- erts_dsprintf(dsbufp, "Error in process %T ", c_p->id);
+ erts_dsprintf(dsbufp, "Error in process %T ", c_p->common.id);
if (erts_is_alive)
erts_dsprintf(dsbufp, "on node %T ", erts_this_node->sysname);
erts_dsprintf(dsbufp,"with exit value: %0.*T\n", display_items, Value);
@@ -5999,7 +5984,6 @@ call_fun(Process* p, /* Current process. */
Eterm fun = reg[arity];
Eterm hdr;
int i;
- Eterm function;
Eterm* hp;
if (!is_boxed(fun)) {
@@ -6137,63 +6121,6 @@ call_fun(Process* p, /* Current process. */
p->fvalue = TUPLE2(hp, fun, args);
return NULL;
}
- } else if (hdr == make_arityval(2)) {
- Eterm* tp;
- Export* ep;
- Eterm module;
-
- tp = tuple_val(fun);
- module = tp[1];
- function = tp[2];
- if (!is_atom(module) || !is_atom(function)) {
- goto badfun;
- }
-
- /*
- * If this is the first time a tuple fun is used,
- * send a warning to the logger.
- */
- if (erts_smp_atomic_xchg_nob(&warned_for_tuple_funs,
- (erts_aint_t) 1) == 0) {
- erts_dsprintf_buf_t* dsbufp;
-
- dsbufp = erts_create_logger_dsbuf();
- erts_dsprintf(dsbufp, "Call to tuple fun {%T,%T}.\n\n"
- "Tuple funs are deprecated and will be removed "
- "in R16. Use \"fun M:F/A\" instead, for example "
- "\"fun %T:%T/%d\".\n\n"
- "(This warning will only be shown the first time "
- "a tuple fun is called.)\n",
- module, function, module, function, arity);
- erts_send_warning_to_logger(p->group_leader, dsbufp);
- }
-
- if ((ep = erts_active_export_entry(module, function, arity)) == NULL) {
- ep = erts_active_export_entry(erts_proc_get_error_handler(p),
- am_undefined_function, 3);
- if (ep == NULL) {
- p->freason = EXC_UNDEF;
- return 0;
- }
- if (is_non_value(args)) {
- Uint sz = 2 * arity;
- if (HeapWordsLeft(p) < sz) {
- erts_garbage_collect(p, sz, reg, arity);
- }
- hp = HEAP_TOP(p);
- HEAP_TOP(p) += sz;
- args = NIL;
- while (arity-- > 0) {
- args = CONS(hp, reg[arity], args);
- hp += 2;
- }
- }
- reg[0] = module;
- reg[1] = function;
- reg[2] = args;
- }
- DTRACE_GLOBAL_CALL(p, module, function, arity);
- return ep->addressv[erts_active_code_ix()];
} else {
badfun:
p->current = NULL;
@@ -6259,7 +6186,7 @@ new_fun(Process* p, Eterm* reg, ErlFunEntry* fe, int num_free)
MSO(p).first = (struct erl_off_heap_header*) funp;
funp->fe = fe;
funp->num_free = num_free;
- funp->creator = p->id;
+ funp->creator = p->common.id;
#ifdef HIPE
funp->native_address = fe->native_address;
#endif
diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c
index d3f55a2ba4..8b4135e21d 100644
--- a/erts/emulator/beam/beam_load.c
+++ b/erts/emulator/beam/beam_load.c
@@ -756,7 +756,7 @@ erts_finish_loading(Binary* magic, Process* c_p,
* table which is not protected by any locks.
*/
- ERTS_SMP_LC_ASSERT(erts_initialized == 0 || erts_is_code_ix_locked() ||
+ ERTS_SMP_LC_ASSERT(erts_initialized == 0 || erts_has_code_write_permission() ||
erts_smp_thr_progress_is_blocking());
/*
@@ -1230,7 +1230,7 @@ load_atom_table(LoaderState* stp)
GetByte(stp, n);
GetString(stp, atom, n);
- stp->atom[i] = am_atom_put((char*)atom, n);
+ stp->atom[i] = erts_atom_put(atom, n, ERTS_ATOM_ENC_LATIN1, 1);
}
/*
@@ -1240,7 +1240,7 @@ load_atom_table(LoaderState* stp)
if (is_nil(stp->module)) {
stp->module = stp->atom[1];
} else if (stp->atom[1] != stp->module) {
- char sbuf[256];
+ char sbuf[MAX_ATOM_SZ_FROM_LATIN1];
Atom* ap;
ap = atom_tab(atom_val(stp->atom[1]));
@@ -1620,7 +1620,7 @@ read_line_table(LoaderState* stp)
GetInt(stp, 2, n);
GetString(stp, fname, n);
- stp->fname[i] = am_atom_put((char*)fname, n);
+ stp->fname[i] = erts_atom_put(fname, n, ERTS_ATOM_ENC_LATIN1, 1);
}
}
@@ -5527,7 +5527,8 @@ stub_copy_info(LoaderState* stp,
int chunk, /* Chunk: ATTR_CHUNK or COMPILE_CHUNK */
byte* info, /* Where to store info. */
BeamInstr* ptr_word, /* Where to store pointer into info. */
- BeamInstr* size_word) /* Where to store size of info. */
+ BeamInstr* size_word, /* Where to store size into info. */
+ BeamInstr* size_on_heap_word) /* Where to store size on heap. */
{
Sint decoded_size;
Uint size = stp->chunks[chunk].size;
@@ -5538,7 +5539,8 @@ stub_copy_info(LoaderState* stp,
if (decoded_size < 0) {
return 0;
}
- *size_word = decoded_size;
+ *size_word = (BeamInstr) size;
+ *size_on_heap_word = decoded_size;
}
return info + size;
}
@@ -5960,12 +5962,16 @@ erts_make_stub_module(Process* p, Eterm Mod, Eterm Beam, Eterm Info)
info = (byte *) fp;
info = stub_copy_info(stp, ATTR_CHUNK, info,
- code+MI_ATTR_PTR, code+MI_ATTR_SIZE_ON_HEAP);
+ code+MI_ATTR_PTR,
+ code+MI_ATTR_SIZE,
+ code+MI_ATTR_SIZE_ON_HEAP);
if (info == NULL) {
goto error;
}
info = stub_copy_info(stp, COMPILE_CHUNK, info,
- code+MI_COMPILE_PTR, code+MI_COMPILE_SIZE_ON_HEAP);
+ code+MI_COMPILE_PTR,
+ code+MI_COMPILE_SIZE,
+ code+MI_COMPILE_SIZE_ON_HEAP);
if (info == NULL) {
goto error;
}
diff --git a/erts/emulator/beam/benchmark.c b/erts/emulator/beam/benchmark.c
index 7ac14b8e8b..8613131176 100644
--- a/erts/emulator/beam/benchmark.c
+++ b/erts/emulator/beam/benchmark.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2002-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2002-2012. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
diff --git a/erts/emulator/beam/benchmark.h b/erts/emulator/beam/benchmark.h
index 003e821bce..766edaac42 100644
--- a/erts/emulator/beam/benchmark.h
+++ b/erts/emulator/beam/benchmark.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2002-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2002-2012. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c
index 6943c8852c..bde70911a2 100644
--- a/erts/emulator/beam/bif.c
+++ b/erts/emulator/beam/bif.c
@@ -37,10 +37,13 @@
#include "erl_db_util.h"
#include "register.h"
#include "erl_thr_progress.h"
+#define ERTS_PTAB_WANT_BIF_IMPL__
+#include "erl_ptab.h"
static Export* flush_monitor_message_trap = NULL;
static Export* set_cpu_topology_trap = NULL;
static Export* await_proc_exit_trap = NULL;
+static Export* await_port_send_result_trap = NULL;
Export* erts_format_cpu_topology_trap = NULL;
static Export *await_sched_wall_time_mod_trap;
@@ -83,8 +86,10 @@ static int insert_internal_link(Process* p, Eterm rpid)
ASSERT(is_internal_pid(rpid));
#ifdef ERTS_SMP
- if (IS_TRACED(p) && (p->trace_flags & (F_TRACE_SOL|F_TRACE_SOL1)))
+ if (IS_TRACED(p)
+ && (ERTS_TRACE_FLAGS(p) & (F_TRACE_SOL|F_TRACE_SOL1))) {
rp_locks = ERTS_PROC_LOCKS_ALL;
+ }
erts_smp_proc_lock(p, ERTS_PROC_LOCK_LINK);
#endif
@@ -100,27 +105,27 @@ static int insert_internal_link(Process* p, Eterm rpid)
}
if (p != rp) {
- erts_add_link(&(p->nlinks), LINK_PID, rp->id);
- erts_add_link(&(rp->nlinks), LINK_PID, p->id);
+ erts_add_link(&ERTS_P_LINKS(p), LINK_PID, rp->common.id);
+ erts_add_link(&ERTS_P_LINKS(rp), LINK_PID, p->common.id);
- ASSERT(is_nil(p->tracer_proc)
- || is_internal_pid(p->tracer_proc)
- || is_internal_port(p->tracer_proc));
+ ASSERT(is_nil(ERTS_TRACER_PROC(p))
+ || is_internal_pid(ERTS_TRACER_PROC(p))
+ || is_internal_port(ERTS_TRACER_PROC(p)));
if (IS_TRACED(p)) {
- if (p->trace_flags & (F_TRACE_SOL|F_TRACE_SOL1)) {
- rp->trace_flags |= (p->trace_flags & TRACEE_FLAGS);
- rp->tracer_proc = p->tracer_proc; /* maybe steal */
+ if (ERTS_TRACE_FLAGS(p) & (F_TRACE_SOL|F_TRACE_SOL1)) {
+ ERTS_TRACE_FLAGS(rp) |= (ERTS_TRACE_FLAGS(p) & TRACEE_FLAGS);
+ ERTS_TRACER_PROC(rp) = ERTS_TRACER_PROC(p); /* maybe steal */
- if (p->trace_flags & F_TRACE_SOL1) { /* maybe override */
- rp->trace_flags &= ~(F_TRACE_SOL1 | F_TRACE_SOL);
- p->trace_flags &= ~(F_TRACE_SOL1 | F_TRACE_SOL);
+ if (ERTS_TRACE_FLAGS(p) & F_TRACE_SOL1) { /* maybe override */
+ ERTS_TRACE_FLAGS(rp) &= ~(F_TRACE_SOL1 | F_TRACE_SOL);
+ ERTS_TRACE_FLAGS(p) &= ~(F_TRACE_SOL1 | F_TRACE_SOL);
}
}
}
}
if (IS_TRACED_FL(rp, F_TRACE_PROCS))
- trace_proc(p, rp, am_getting_linked, p->id);
+ trace_proc(p, rp, am_getting_linked, p->common.id);
if (p == rp)
erts_smp_proc_unlock(p, rp_locks & ~ERTS_PROC_LOCK_MAIN);
@@ -144,10 +149,6 @@ BIF_RETTYPE link_1(BIF_ALIST_1)
/* check that the pid or port which is our argument is OK */
if (is_internal_pid(BIF_ARG_1)) {
- if (internal_pid_index(BIF_ARG_1) >= erts_max_processes) {
- BIF_ERROR(BIF_P, BADARG);
- }
-
if (insert_internal_link(BIF_P, BIF_ARG_1)) {
BIF_RET(am_true);
}
@@ -157,19 +158,37 @@ BIF_RETTYPE link_1(BIF_ALIST_1)
}
if (is_internal_port(BIF_ARG_1)) {
- Port *pt = erts_id2port(BIF_ARG_1, BIF_P, ERTS_PROC_LOCK_MAIN);
- if (!pt) {
+ int send_link_signal = 0;
+ Port *prt = erts_port_lookup(BIF_ARG_1, ERTS_PORT_SFLGS_INVALID_LOOKUP);
+ if (!prt) {
goto res_no_proc;
}
erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_LINK);
- if (erts_add_link(&(BIF_P->nlinks), LINK_PID, BIF_ARG_1) >= 0)
- erts_add_link(&(pt->nlinks), LINK_PID, BIF_P->id);
+ if (erts_add_link(&ERTS_P_LINKS(BIF_P), LINK_PID, BIF_ARG_1) >= 0)
+ send_link_signal = 1;
/* else: already linked */
erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_LINK);
- erts_smp_port_unlock(pt);
+
+ if (send_link_signal) {
+ Eterm ref;
+ Eterm *refp = erts_port_synchronous_ops ? &ref : NULL;
+
+ switch (erts_port_link(BIF_P, prt, BIF_P->common.id, refp)) {
+ case ERTS_PORT_OP_DROPPED:
+ case ERTS_PORT_OP_BADARG:
+ goto res_no_proc;
+ case ERTS_PORT_OP_SCHEDULED:
+ if (refp) {
+ ASSERT(is_internal_ref(ref));
+ BIF_TRAP3(await_port_send_result_trap, BIF_P, ref, am_true, am_true);
+ }
+ default:
+ break;
+ }
+ }
BIF_RET(am_true);
}
else if (is_external_port(BIF_ARG_1)
@@ -182,7 +201,7 @@ BIF_RETTYPE link_1(BIF_ALIST_1)
erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_LINK);
/* We may earn time by checking first that we're not linked already */
- if (erts_lookup_link(BIF_P->nlinks, BIF_ARG_1) != NULL) {
+ if (erts_lookup_link(ERTS_P_LINKS(BIF_P), BIF_ARG_1) != NULL) {
erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_LINK);
BIF_RET(am_true);
}
@@ -209,10 +228,10 @@ BIF_RETTYPE link_1(BIF_ALIST_1)
erts_smp_de_links_lock(dep);
- erts_add_link(&(BIF_P->nlinks), LINK_PID, BIF_ARG_1);
+ erts_add_link(&ERTS_P_LINKS(BIF_P), LINK_PID, BIF_ARG_1);
lnk = erts_add_or_lookup_link(&(dep->nlinks),
LINK_PID,
- BIF_P->id);
+ BIF_P->common.id);
ASSERT(lnk != NULL);
erts_add_link(&ERTS_LINK_ROOT(lnk), LINK_PID, BIF_ARG_1);
@@ -220,7 +239,7 @@ BIF_RETTYPE link_1(BIF_ALIST_1)
erts_smp_de_runlock(dep);
erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_LINK);
- code = erts_dsig_send_link(&dsd, BIF_P->id, BIF_ARG_1);
+ code = erts_dsig_send_link(&dsd, BIF_P->common.id, BIF_ARG_1);
if (code == ERTS_DSIG_SEND_YIELD)
ERTS_BIF_YIELD_RETURN(BIF_P, am_true);
BIF_RET(am_true);
@@ -289,7 +308,7 @@ remote_demonitor(Process *c_p, DistEntry *dep, Eterm ref, Eterm to)
if (dmon)
erts_destroy_monitor(dmon);
}
- mon = erts_remove_monitor(&c_p->monitors, ref);
+ mon = erts_remove_monitor(&ERTS_P_MONITORS(c_p), ref);
erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_LINK);
res = ERTS_DEMONITOR_TRUE;
@@ -298,7 +317,7 @@ remote_demonitor(Process *c_p, DistEntry *dep, Eterm ref, Eterm to)
case ERTS_DSIG_PREP_CONNECTED:
erts_smp_de_links_lock(dep);
- mon = erts_remove_monitor(&c_p->monitors, ref);
+ mon = erts_remove_monitor(&ERTS_P_MONITORS(c_p), ref);
dmon = erts_remove_monitor(&dep->monitors, ref);
erts_smp_de_links_unlock(dep);
erts_smp_de_runlock(dep);
@@ -325,7 +344,7 @@ remote_demonitor(Process *c_p, DistEntry *dep, Eterm ref, Eterm to)
* the atom is stored there. Yield if necessary.
*/
code = erts_dsig_send_demonitor(&dsd,
- c_p->id,
+ c_p->common.id,
(mon->name != NIL
? mon->name
: mon->pid),
@@ -387,7 +406,7 @@ static int demonitor(Process *c_p, Eterm ref)
goto done; /* Cannot be this monitor's ref */
}
- mon = erts_lookup_monitor(c_p->monitors, ref);
+ mon = erts_lookup_monitor(ERTS_P_MONITORS(c_p), ref);
if (!mon) {
res = ERTS_DEMONITOR_FALSE;
goto done;
@@ -426,7 +445,7 @@ static int demonitor(Process *c_p, Eterm ref)
to,
ERTS_PROC_LOCK_LINK,
ERTS_P2P_FLG_ALLOW_OTHER_X);
- mon = erts_remove_monitor(&c_p->monitors, ref);
+ mon = erts_remove_monitor(&ERTS_P_MONITORS(c_p), ref);
#ifndef ERTS_SMP
ASSERT(mon);
#else
@@ -440,7 +459,7 @@ static int demonitor(Process *c_p, Eterm ref)
}
if (rp) {
ErtsMonitor *rmon;
- rmon = erts_remove_monitor(&(rp->monitors), ref);
+ rmon = erts_remove_monitor(&ERTS_P_MONITORS(rp), ref);
if (rp != c_p)
erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
if (rmon != NULL)
@@ -582,7 +601,7 @@ local_pid_monitor(Process *p, Eterm target)
mon_ref = erts_make_ref(p);
ERTS_BIF_PREP_RET(ret, mon_ref);
- if (target == p->id) {
+ if (target == p->common.id) {
return ret;
}
@@ -599,8 +618,8 @@ local_pid_monitor(Process *p, Eterm target)
else {
ASSERT(rp != p);
- erts_add_monitor(&(p->monitors), MON_ORIGIN, mon_ref, target, NIL);
- erts_add_monitor(&(rp->monitors), MON_TARGET, mon_ref, p->id, NIL);
+ erts_add_monitor(&ERTS_P_MONITORS(p), MON_ORIGIN, mon_ref, target, NIL);
+ erts_add_monitor(&ERTS_P_MONITORS(rp), MON_TARGET, mon_ref, p->common.id, NIL);
erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
}
@@ -635,9 +654,9 @@ local_name_monitor(Process *p, Eterm target_name)
UnUseTmpHeap(3,p);
}
else if (rp != p) {
- erts_add_monitor(&(p->monitors), MON_ORIGIN, mon_ref, rp->id,
+ erts_add_monitor(&ERTS_P_MONITORS(p), MON_ORIGIN, mon_ref, rp->common.id,
target_name);
- erts_add_monitor(&(rp->monitors), MON_TARGET, mon_ref, p->id,
+ erts_add_monitor(&ERTS_P_MONITORS(rp), MON_TARGET, mon_ref, p->common.id,
target_name);
erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
}
@@ -689,16 +708,16 @@ remote_monitor(Process *p, Eterm bifarg1, Eterm bifarg2,
erts_smp_de_links_lock(dep);
- erts_add_monitor(&(p->monitors), MON_ORIGIN, mon_ref, p_trgt,
+ erts_add_monitor(&ERTS_P_MONITORS(p), MON_ORIGIN, mon_ref, p_trgt,
p_name);
- erts_add_monitor(&(dep->monitors), MON_TARGET, mon_ref, p->id,
+ erts_add_monitor(&(dep->monitors), MON_TARGET, mon_ref, p->common.id,
d_name);
erts_smp_de_links_unlock(dep);
erts_smp_de_runlock(dep);
erts_smp_proc_unlock(p, ERTS_PROC_LOCK_LINK);
- code = erts_dsig_send_monitor(&dsd, p->id, target, mon_ref);
+ code = erts_dsig_send_monitor(&dsd, p->common.id, target, mon_ref);
if (code == ERTS_DSIG_SEND_YIELD)
ERTS_BIF_PREP_YIELD_RETURN(ret, p, mon_ref);
else
@@ -941,36 +960,39 @@ BIF_RETTYPE unlink_1(BIF_ALIST_1)
}
if (is_internal_port(BIF_ARG_1)) {
- Port *pt = erts_id2port_sflgs(BIF_ARG_1,
- BIF_P,
- ERTS_PROC_LOCK_MAIN,
- ERTS_PORT_SFLGS_DEAD);
-
erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCK_STATUS);
#ifdef ERTS_SMP
- if (ERTS_PROC_PENDING_EXIT(BIF_P)) {
- if (pt)
- erts_smp_port_unlock(pt);
+ if (ERTS_PROC_PENDING_EXIT(BIF_P))
goto handle_pending_exit;
- }
#endif
- l = erts_remove_link(&BIF_P->nlinks, BIF_ARG_1);
-
- ASSERT(pt || !l);
-
- if (pt) {
- rl = erts_remove_link(&pt->nlinks, BIF_P->id);
- erts_smp_port_unlock(pt);
- if (rl)
- erts_destroy_link(rl);
- }
+ l = erts_remove_link(&ERTS_P_LINKS(BIF_P), BIF_ARG_1);
erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCK_STATUS);
- if (l)
+ if (l) {
+ Port *prt;
+
erts_destroy_link(l);
+ /* Send unlink signal */
+ prt = erts_port_lookup(BIF_ARG_1, ERTS_PORT_SFLGS_DEAD);
+ if (prt) {
+ ErtsPortOpResult res;
+ Eterm ref;
+ Eterm *refp = erts_port_synchronous_ops ? &ref : NULL;
+#ifdef DEBUG
+ ref = NIL;
+#endif
+ res = erts_port_unlink(BIF_P, prt, BIF_P->common.id, refp);
+
+ if (refp && res == ERTS_PORT_OP_SCHEDULED) {
+ ASSERT(is_internal_ref(ref));
+ BIF_TRAP3(await_port_send_result_trap, BIF_P, ref, am_true, am_true);
+ }
+ }
+ }
+
BIF_RET(am_true);
}
else if (is_external_port(BIF_ARG_1)
@@ -993,7 +1015,7 @@ BIF_RETTYPE unlink_1(BIF_ALIST_1)
if (ERTS_PROC_PENDING_EXIT(BIF_P))
goto handle_pending_exit;
#endif
- l = erts_remove_link(&BIF_P->nlinks,BIF_ARG_1);
+ l = erts_remove_link(&ERTS_P_LINKS(BIF_P), BIF_ARG_1);
erts_smp_proc_unlock(BIF_P,
ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCK_STATUS);
@@ -1022,8 +1044,8 @@ BIF_RETTYPE unlink_1(BIF_ALIST_1)
#endif
case ERTS_DSIG_PREP_CONNECTED:
- erts_remove_dist_link(&dld, BIF_P->id, BIF_ARG_1, dep);
- code = erts_dsig_send_unlink(&dsd, BIF_P->id, BIF_ARG_1);
+ erts_remove_dist_link(&dld, BIF_P->common.id, BIF_ARG_1, dep);
+ code = erts_dsig_send_unlink(&dsd, BIF_P->common.id, BIF_ARG_1);
erts_destroy_dist_link(&dld);
if (code == ERTS_DSIG_SEND_YIELD)
ERTS_BIF_YIELD_RETURN(BIF_P, am_true);
@@ -1037,10 +1059,6 @@ BIF_RETTYPE unlink_1(BIF_ALIST_1)
/* Internal pid... */
- /* process ok ? */
- if (internal_pid_index(BIF_ARG_1) >= erts_max_processes)
- BIF_ERROR(BIF_P, BADARG);
-
erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCK_STATUS);
/* get process struct */
@@ -1059,7 +1077,7 @@ BIF_RETTYPE unlink_1(BIF_ALIST_1)
#endif
/* unlink and ignore errors */
- l = erts_remove_link(&BIF_P->nlinks,BIF_ARG_1);
+ l = erts_remove_link(&ERTS_P_LINKS(BIF_P), BIF_ARG_1);
if (l != NULL)
erts_destroy_link(l);
@@ -1067,12 +1085,12 @@ BIF_RETTYPE unlink_1(BIF_ALIST_1)
ERTS_SMP_ASSERT_IS_NOT_EXITING(BIF_P);
}
else {
- rl = erts_remove_link(&(rp->nlinks),BIF_P->id);
+ rl = erts_remove_link(&ERTS_P_LINKS(rp), BIF_P->common.id);
if (rl != NULL)
erts_destroy_link(rl);
if (IS_TRACED_FL(rp, F_TRACE_PROCS) && rl != NULL) {
- trace_proc(BIF_P, rp, am_getting_unlinked, BIF_P->id);
+ trace_proc(BIF_P, rp, am_getting_unlinked, BIF_P->common.id);
}
if (rp != BIF_P)
@@ -1345,15 +1363,28 @@ BIF_RETTYPE exit_2(BIF_ALIST_2)
*/
if (is_internal_port(BIF_ARG_1)) {
- Port *prt;
- erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
- prt = erts_id2port(BIF_ARG_1, NULL, 0);
+ Port *prt = erts_port_lookup(BIF_ARG_1, ERTS_PORT_SFLGS_INVALID_LOOKUP);
+
if (prt) {
- erts_do_exit_port(prt, BIF_P->id, BIF_ARG_2);
- erts_port_release(prt);
+ Eterm ref;
+ Eterm *refp = erts_port_synchronous_ops ? &ref : NULL;
+ ErtsPortOpResult res;
+
+#ifdef DEBUG
+ ref = NIL;
+#endif
+
+ res = erts_port_exit(BIF_P, 0, prt, BIF_P->common.id, BIF_ARG_2, refp);
+
+ ERTS_BIF_CHK_EXITED(BIF_P);
+
+ if (refp && res == ERTS_PORT_OP_SCHEDULED) {
+ ASSERT(is_internal_ref(ref));
+ BIF_TRAP3(await_port_send_result_trap, BIF_P, ref, am_true, am_true);
+ }
+
}
- erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
- ERTS_BIF_CHK_EXITED(BIF_P);
+
BIF_RET(am_true);
}
else if(is_external_port(BIF_ARG_1)
@@ -1379,7 +1410,7 @@ BIF_RETTYPE exit_2(BIF_ALIST_2)
case ERTS_DSIG_PREP_NOT_CONNECTED:
BIF_TRAP2(dexit_trap, BIF_P, BIF_ARG_1, BIF_ARG_2);
case ERTS_DSIG_PREP_CONNECTED:
- code = erts_dsig_send_exit2(&dsd, BIF_P->id, BIF_ARG_1, BIF_ARG_2);
+ code = erts_dsig_send_exit2(&dsd, BIF_P->common.id, BIF_ARG_1, BIF_ARG_2);
if (code == ERTS_DSIG_SEND_YIELD)
ERTS_BIF_YIELD_RETURN(BIF_P, am_true);
BIF_RET(am_true);
@@ -1397,9 +1428,7 @@ BIF_RETTYPE exit_2(BIF_ALIST_2)
*/
ErtsProcLocks rp_locks;
- if (internal_pid_index(BIF_ARG_1) >= erts_max_processes)
- BIF_ERROR(BIF_P, BADARG);
- if (BIF_ARG_1 == BIF_P->id) {
+ if (BIF_ARG_1 == BIF_P->common.id) {
rp_locks = ERTS_PROC_LOCKS_ALL;
rp = BIF_P;
erts_smp_proc_lock(rp, ERTS_PROC_LOCKS_ALL_MINOR);
@@ -1417,7 +1446,7 @@ BIF_RETTYPE exit_2(BIF_ALIST_2)
* Send an exit signal.
*/
erts_send_exit_signal(BIF_P,
- BIF_P->id,
+ BIF_P->common.id,
rp,
&rp_locks,
BIF_ARG_2,
@@ -1519,22 +1548,24 @@ BIF_RETTYPE process_flag_2(BIF_ALIST_2)
}
/*
* NOTE: It is important that we check for pending exit signals
- * and handle them before flag trap_exit is set to true.
- * For more info, see implementation of erts_send_exit_signal().
+ * and handle them before returning if trap_exit is set to
+ * true. For more info, see implementation of
+ * erts_send_exit_signal().
*/
- erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_STATUS);
- ERTS_SMP_LC_ASSERT((ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_STATUS)
- & erts_proc_lc_my_proc_locks(BIF_P));
- ERTS_SMP_BIF_CHK_PENDING_EXIT(BIF_P,
- ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_STATUS);
if (trap_exit)
- state = erts_smp_atomic32_read_bor_nob(&BIF_P->state,
- ERTS_PSFLG_TRAP_EXIT);
+ state = erts_smp_atomic32_read_bor_mb(&BIF_P->state,
+ ERTS_PSFLG_TRAP_EXIT);
else
- state = erts_smp_atomic32_read_band_nob(&BIF_P->state,
- ~ERTS_PSFLG_TRAP_EXIT);
+ state = erts_smp_atomic32_read_band_mb(&BIF_P->state,
+ ~ERTS_PSFLG_TRAP_EXIT);
+#ifdef ERTS_SMP
+ if (ERTS_PROC_PENDING_EXIT(BIF_P)) {
+ erts_handle_pending_exit(BIF_P, ERTS_PROC_LOCK_MAIN);
+ ERTS_BIF_EXITED(BIF_P);
+ }
+#endif
+
old_value = (state & ERTS_PSFLG_TRAP_EXIT) ? am_true : am_false;
- erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_STATUS);
BIF_RET(old_value);
}
else if (BIF_ARG_1 == am_scheduler) {
@@ -1617,11 +1648,13 @@ BIF_RETTYPE process_flag_2(BIF_ALIST_2)
goto error;
}
erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCKS_ALL_MINOR);
- old_value = BIF_P->trace_flags & F_SENSITIVE ? am_true : am_false;
+ old_value = (ERTS_TRACE_FLAGS(BIF_P) & F_SENSITIVE
+ ? am_true
+ : am_false);
if (is_sensitive) {
- BIF_P->trace_flags |= F_SENSITIVE;
+ ERTS_TRACE_FLAGS(BIF_P) |= F_SENSITIVE;
} else {
- BIF_P->trace_flags &= ~F_SENSITIVE;
+ ERTS_TRACE_FLAGS(BIF_P) &= ~F_SENSITIVE;
}
erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCKS_ALL_MINOR);
BIF_RET(old_value);
@@ -1747,8 +1780,9 @@ ebif_bang_2(BIF_ALIST_2)
#define SEND_BADARG (-4)
#define SEND_USER_ERROR (-5)
#define SEND_INTERNAL_ERROR (-6)
+#define SEND_AWAIT_RESULT (-7)
-Sint do_send(Process *p, Eterm to, Eterm msg, int suspend);
+Sint do_send(Process *p, Eterm to, Eterm msg, int suspend, Eterm *refp);
static Sint remote_send(Process *p, DistEntry *dep,
Eterm to, Eterm full_to, Eterm msg, int suspend)
@@ -1802,7 +1836,7 @@ static Sint remote_send(Process *p, DistEntry *dep,
}
Sint
-do_send(Process *p, Eterm to, Eterm msg, int suspend) {
+do_send(Process *p, Eterm to, Eterm msg, int suspend, Eterm *refp) {
Eterm portid;
Port *pt;
Process* rp;
@@ -1814,16 +1848,10 @@ do_send(Process *p, Eterm to, Eterm msg, int suspend) {
trace_send(p, to, msg);
if (ERTS_PROC_GET_SAVED_CALLS_BUF(p))
save_calls(p, &exp_send);
-
- if (internal_pid_index(to) >= erts_max_processes)
- return SEND_BADARG;
- rp = erts_proc_lookup_raw(to);
-
- if (!rp) {
- ERTS_SMP_ASSERT_IS_NOT_EXITING(p);
+ rp = erts_proc_lookup_raw(to);
+ if (!rp)
return 0;
- }
} else if (is_external_pid(to)) {
dep = external_pid_dist_entry(to);
if(dep == erts_this_dist_entry) {
@@ -1832,7 +1860,7 @@ do_send(Process *p, Eterm to, Eterm msg, int suspend) {
"Discarding message %T from %T to %T in an old "
"incarnation (%d) of this node (%d)\n",
msg,
- p->id,
+ p->common.id,
to,
external_pid_creation(to),
erts_this_node->creation);
@@ -1841,45 +1869,24 @@ do_send(Process *p, Eterm to, Eterm msg, int suspend) {
}
return remote_send(p, dep, to, to, msg, suspend);
} else if (is_atom(to)) {
-
- /* Need to virtual schedule out sending process
- * because of lock wait. This is only necessary
- * for internal port calling but the lock is bundled
- * with name lookup.
- */
-
- if (IS_TRACED_FL(p, F_TRACE_SCHED_PROCS)) {
- trace_virtual_sched(p, am_out);
- }
- if (erts_system_profile_flags.runnable_procs && erts_system_profile_flags.exclusive) {
- profile_runnable_proc(p, am_inactive);
- }
- erts_whereis_name(p, ERTS_PROC_LOCK_MAIN,
- to,
- &rp, 0, 0,
- &pt);
+ Eterm id = erts_whereis_name_to_id(p, to);
+
+ rp = erts_proc_lookup(id);
+ if (rp)
+ goto send_message;
+ pt = erts_port_lookup(id, ERTS_PORT_SFLGS_INVALID_LOOKUP);
if (pt) {
- portid = pt->id;
+ portid = id;
goto port_common;
}
-
- /* Not a port virtually schedule the process back in */
- if (IS_TRACED_FL(p, F_TRACE_SCHED_PROCS)) {
- trace_virtual_sched(p, am_in);
- }
- if (erts_system_profile_flags.runnable_procs && erts_system_profile_flags.exclusive) {
- profile_runnable_proc(p, am_active);
- }
if (IS_TRACED(p))
trace_send(p, to, msg);
if (ERTS_PROC_GET_SAVED_CALLS_BUF(p))
save_calls(p, &exp_send);
- if (!rp) {
- return SEND_BADARG;
- }
+ return SEND_BADARG;
} else if (is_external_port(to)
&& (external_port_dist_entry(to)
== erts_this_dist_entry)) {
@@ -1888,50 +1895,56 @@ do_send(Process *p, Eterm to, Eterm msg, int suspend) {
"Discarding message %T from %T to %T in an old "
"incarnation (%d) of this node (%d)\n",
msg,
- p->id,
+ p->common.id,
to,
external_port_creation(to),
erts_this_node->creation);
erts_send_error_to_logger(p->group_leader, dsbufp);
return 0;
} else if (is_internal_port(to)) {
+ int ret_val;
portid = to;
- /* schedule out calling process, waiting for lock*/
- if (IS_TRACED_FL(p, F_TRACE_SCHED_PROCS)) {
- trace_virtual_sched(p, am_out);
- }
- if (erts_system_profile_flags.runnable_procs && erts_system_profile_flags.exclusive) {
- profile_runnable_proc(p, am_inactive);
- }
- pt = erts_id2port(to, p, ERTS_PROC_LOCK_MAIN);
+
+ pt = erts_port_lookup(portid, ERTS_PORT_SFLGS_INVALID_LOOKUP);
+
port_common:
- ERTS_SMP_LC_ASSERT(!pt || erts_lc_is_port_locked(pt));
+ ret_val = 0;
- /* We have waited for locks, trace schedule ports */
- if (pt && IS_TRACED_FL(pt, F_TRACE_SCHED_PORTS)) {
- trace_sched_ports_where(pt, am_in, am_command);
- }
- if (pt && erts_system_profile_flags.runnable_ports && !erts_port_is_scheduled(pt)) {
- profile_runnable_port(pt, am_active);
- }
-
- /* XXX let port_command handle the busy stuff !!! */
- if (pt && (pt->status & ERTS_PORT_SFLG_PORT_BUSY)) {
- if (suspend) {
- erts_suspend(p, ERTS_PROC_LOCK_MAIN, pt);
- if (erts_system_monitor_flags.busy_port) {
- monitor_generic(p, am_busy_port, portid);
+ if (pt) {
+ int ps_flags = suspend ? 0 : ERTS_PORT_SIG_FLG_NOSUSPEND;
+ *refp = NIL;
+
+ switch (erts_port_command(p, ps_flags, pt, msg, refp)) {
+ case ERTS_PORT_OP_CALLER_EXIT:
+ /* We are exiting... */
+ return SEND_USER_ERROR;
+ case ERTS_PORT_OP_BUSY:
+ /* Nothing has been sent */
+ if (suspend)
+ erts_suspend(p, ERTS_PROC_LOCK_MAIN, pt);
+ return SEND_YIELD;
+ case ERTS_PORT_OP_BUSY_SCHEDULED:
+ /* Message was sent */
+ if (suspend) {
+ erts_suspend(p, ERTS_PROC_LOCK_MAIN, pt);
+ ret_val = SEND_YIELD_RETURN;
+ break;
}
+ /* Fall through */
+ case ERTS_PORT_OP_SCHEDULED:
+ if (is_not_nil(*refp)) {
+ ASSERT(is_internal_ref(*refp));
+ ret_val = SEND_AWAIT_RESULT;
+ }
+ break;
+ case ERTS_PORT_OP_DROPPED:
+ case ERTS_PORT_OP_BADARG:
+ case ERTS_PORT_OP_DONE:
+ break;
+ default:
+ ERTS_INTERNAL_ERROR("Unexpected erts_port_command() result");
+ break;
}
- /* Virtually schedule out the port before releasing */
- if (IS_TRACED_FL(pt, F_TRACE_SCHED_PORTS)) {
- trace_sched_ports_where(pt, am_out, am_command);
- }
- if (erts_system_profile_flags.runnable_ports && !erts_port_is_scheduled(pt)) {
- profile_runnable_port(pt, am_inactive);
- }
- erts_port_release(pt);
- return SEND_YIELD;
}
if (IS_TRACED(p)) /* trace once only !! */
@@ -1949,30 +1962,11 @@ do_send(Process *p, Eterm to, Eterm msg, int suspend) {
SEQ_TRACE_SEND, portid, p);
}
- /* XXX NO GC in port command */
- erts_port_command(p, p->id, pt, msg);
- if (pt) {
- /* Virtually schedule out the port before releasing */
- if (IS_TRACED_FL(pt, F_TRACE_SCHED_PORTS)) {
- trace_sched_ports_where(pt, am_out, am_command);
- }
- if (erts_system_profile_flags.runnable_ports && !erts_port_is_scheduled(pt)) {
- profile_runnable_port(pt, am_inactive);
- }
- erts_port_release(pt);
- }
- /* Virtually schedule in process */
- if (IS_TRACED_FL(p, F_TRACE_SCHED_PROCS)) {
- trace_virtual_sched(p, am_in);
- }
- if (erts_system_profile_flags.runnable_procs && erts_system_profile_flags.exclusive) {
- profile_runnable_proc(p, am_active);
- }
if (ERTS_PROC_IS_EXITING(p)) {
KILL_CATCHES(p); /* Must exit */
return SEND_USER_ERROR;
}
- return 0;
+ return ret_val;
} else if (is_tuple(to)) { /* Remote send */
int ret;
tp = tuple_val(to);
@@ -1988,47 +1982,24 @@ do_send(Process *p, Eterm to, Eterm msg, int suspend) {
dep = erts_sysname_to_connected_dist_entry(tp[2]);
if (dep == erts_this_dist_entry) {
+ Eterm id;
erts_deref_dist_entry(dep);
if (IS_TRACED(p))
trace_send(p, to, msg);
if (ERTS_PROC_GET_SAVED_CALLS_BUF(p))
save_calls(p, &exp_send);
-
- /* Need to virtual schedule out sending process
- * because of lock wait. This is only necessary
- * for internal port calling but the lock is bundled.
- */
-
- if (IS_TRACED_FL(p, F_TRACE_SCHED_PROCS)) {
- trace_virtual_sched(p, am_out);
- }
- if (erts_system_profile_flags.runnable_procs && erts_system_profile_flags.exclusive) {
- profile_runnable_proc(p, am_inactive);
- }
- erts_whereis_name(p, ERTS_PROC_LOCK_MAIN,
- tp[1],
- &rp, 0, 0,
- &pt);
+ id = erts_whereis_name_to_id(p, tp[1]);
+
+ rp = erts_proc_lookup_raw(id);
+ if (rp)
+ goto send_message;
+ pt = erts_port_lookup(id, ERTS_PORT_SFLGS_INVALID_LOOKUP);
if (pt) {
- portid = pt->id;
+ portid = id;
goto port_common;
}
- /* Port lookup failed, virtually schedule the process
- * back in.
- */
-
- if (IS_TRACED_FL(p, F_TRACE_SCHED_PROCS)) {
- trace_virtual_sched(p, am_in);
- }
- if (erts_system_profile_flags.runnable_procs && erts_system_profile_flags.exclusive) {
- profile_runnable_proc(p, am_active);
- }
-
- if (!rp) {
- return 0;
- }
- goto send_message;
+ return 0;
}
ret = remote_send(p, dep, tp[1], to, msg, suspend);
@@ -2051,18 +2022,11 @@ do_send(Process *p, Eterm to, Eterm msg, int suspend) {
rp_locks |= ERTS_PROC_LOCK_MAIN;
#endif
/* send to local process */
- erts_send_message(p, rp, &rp_locks, msg, 0);
- if (!erts_use_sender_punish)
+ res = erts_send_message(p, rp, &rp_locks, msg, 0);
+ if (erts_use_sender_punish)
+ res *= 4;
+ else
res = 0;
- else {
-#ifdef ERTS_SMP
- res = rp->msg_inq.len*4;
- if (ERTS_PROC_LOCK_MAIN & rp_locks)
- res += rp->msg.len*4;
-#else
- res = rp->msg.len*4;
-#endif
- }
erts_smp_proc_unlock(rp,
p == rp
? (rp_locks & ~ERTS_PROC_LOCK_MAIN)
@@ -2074,6 +2038,7 @@ do_send(Process *p, Eterm to, Eterm msg, int suspend) {
BIF_RETTYPE send_3(BIF_ALIST_3)
{
+ Eterm ref;
Process *p = BIF_P;
Eterm to = BIF_ARG_1;
Eterm msg = BIF_ARG_2;
@@ -2097,12 +2062,18 @@ BIF_RETTYPE send_3(BIF_ALIST_3)
if(!is_nil(l)) {
BIF_ERROR(p, BADARG);
}
-
- result = do_send(p, to, msg, suspend);
+
+#ifdef DEBUG
+ ref = NIL;
+#endif
+
+ result = do_send(p, to, msg, suspend, &ref);
if (result > 0) {
ERTS_VBUMP_REDS(p, result);
BIF_RET(am_ok);
- } else switch (result) {
+ }
+
+ switch (result) {
case 0:
BIF_RET(am_ok);
break;
@@ -2125,6 +2096,9 @@ BIF_RETTYPE send_3(BIF_ALIST_3)
ERTS_BIF_YIELD_RETURN(p, am_ok);
else
BIF_RET(am_nosuspend);
+ case SEND_AWAIT_RESULT:
+ ASSERT(is_internal_ref(ref));
+ BIF_TRAP3(await_port_send_result_trap, p, ref, am_nosuspend, am_ok);
case SEND_BADARG:
BIF_ERROR(p, BADARG);
break;
@@ -2149,12 +2123,21 @@ BIF_RETTYPE send_2(BIF_ALIST_2)
Eterm erl_send(Process *p, Eterm to, Eterm msg)
{
- Sint result = do_send(p, to, msg, !0);
+ Eterm ref;
+ Sint result;
+
+#ifdef DEBUG
+ ref = NIL;
+#endif
+
+ result = do_send(p, to, msg, !0, &ref);
if (result > 0) {
ERTS_VBUMP_REDS(p, result);
BIF_RET(msg);
- } else switch (result) {
+ }
+
+ switch (result) {
case 0:
BIF_RET(msg);
break;
@@ -2166,6 +2149,9 @@ Eterm erl_send(Process *p, Eterm to, Eterm msg)
break;
case SEND_YIELD_RETURN:
ERTS_BIF_YIELD_RETURN(p, msg);
+ case SEND_AWAIT_RESULT:
+ ASSERT(is_internal_ref(ref));
+ BIF_TRAP3(await_port_send_result_trap, p, ref, msg, msg);
case SEND_BADARG:
BIF_ERROR(p, BADARG);
break;
@@ -2435,9 +2421,7 @@ BIF_RETTYPE setelement_3(BIF_ALIST_3)
/* copy the tuple */
resp = hp;
- while (size--) { /* XXX use memcpy? */
- *hp++ = *ptr++;
- }
+ sys_memcpy(hp, ptr, sizeof(Eterm)*size);
resp[ix] = BIF_ARG_3;
BIF_RET(make_tuple(resp));
}
@@ -2450,7 +2434,7 @@ BIF_RETTYPE make_tuple_2(BIF_ALIST_2)
Eterm* hp;
Eterm res;
- if (is_not_small(BIF_ARG_1) || (n = signed_val(BIF_ARG_1)) < 0) {
+ if (is_not_small(BIF_ARG_1) || (n = signed_val(BIF_ARG_1)) < 0 || n > ERTS_MAX_TUPLE_SIZE) {
BIF_ERROR(BIF_P, BADARG);
}
hp = HAlloc(BIF_P, n+1);
@@ -2471,7 +2455,7 @@ BIF_RETTYPE make_tuple_3(BIF_ALIST_3)
Eterm list = BIF_ARG_3;
Eterm* tup;
- if (is_not_small(BIF_ARG_1) || (n = signed_val(BIF_ARG_1)) < 0) {
+ if (is_not_small(BIF_ARG_1) || (n = signed_val(BIF_ARG_1)) < 0 || n > ERTS_MAX_TUPLE_SIZE) {
error:
BIF_ERROR(BIF_P, BADARG);
}
@@ -2523,11 +2507,16 @@ BIF_RETTYPE append_element_2(BIF_ALIST_2)
Eterm res;
if (is_not_tuple(BIF_ARG_1)) {
+ error:
BIF_ERROR(BIF_P, BADARG);
}
- ptr = tuple_val(BIF_ARG_1);
+ ptr = tuple_val(BIF_ARG_1);
arity = arityval(*ptr);
- hp = HAlloc(BIF_P, arity + 2);
+
+ if (arity + 1 > ERTS_MAX_TUPLE_SIZE)
+ goto error;
+
+ hp = HAlloc(BIF_P, arity + 2);
res = make_tuple(hp);
*hp = make_arityval(arity+1);
while (arity--) {
@@ -2537,15 +2526,91 @@ BIF_RETTYPE append_element_2(BIF_ALIST_2)
BIF_RET(res);
}
+BIF_RETTYPE insert_element_3(BIF_ALIST_3)
+{
+ Eterm* ptr;
+ Eterm* hp;
+ Uint arity;
+ Eterm res;
+ Sint ix;
+
+ if (is_not_tuple(BIF_ARG_2) || is_not_small(BIF_ARG_1)) {
+ BIF_ERROR(BIF_P, BADARG);
+ }
+
+ ptr = tuple_val(BIF_ARG_2);
+ arity = arityval(*ptr);
+ ix = signed_val(BIF_ARG_1);
+
+ if ((ix < 1) || (ix > (arity + 1))) {
+ BIF_ERROR(BIF_P, BADARG);
+ }
+
+ hp = HAlloc(BIF_P, arity + 1 + 1);
+ res = make_tuple(hp);
+ *hp = make_arityval(arity + 1);
+
+ ix--;
+ arity -= ix;
+
+ while (ix--) { *++hp = *++ptr; }
+
+ *++hp = BIF_ARG_3;
+
+ while(arity--) { *++hp = *++ptr; }
+
+ BIF_RET(res);
+}
+
+BIF_RETTYPE delete_element_2(BIF_ALIST_3)
+{
+ Eterm* ptr;
+ Eterm* hp;
+ Uint arity;
+ Eterm res;
+ Sint ix;
+
+ if (is_not_tuple(BIF_ARG_2) || is_not_small(BIF_ARG_1)) {
+ BIF_ERROR(BIF_P, BADARG);
+ }
+
+ ptr = tuple_val(BIF_ARG_2);
+ arity = arityval(*ptr);
+ ix = signed_val(BIF_ARG_1);
+
+ if ((ix < 1) || (ix > arity) || (arity == 0)) {
+ BIF_ERROR(BIF_P, BADARG);
+ }
+
+ hp = HAlloc(BIF_P, arity + 1 - 1);
+ res = make_tuple(hp);
+ *hp = make_arityval(arity - 1);
+
+ ix--;
+ arity -= ix;
+
+ while (ix--) { *++hp = *++ptr; }
+
+ ++ptr;
+
+ while(arity--) { *++hp = *++ptr; }
+
+ BIF_RET(res);
+}
+
/**********************************************************************/
/* convert an atom to a list of ascii integer */
BIF_RETTYPE atom_to_list_1(BIF_ALIST_1)
{
- Uint need;
- Eterm* hp;
Atom* ap;
+ Uint num_chars, num_built, num_eaten;
+ byte* err_pos;
+ Eterm res;
+#ifdef DEBUG
+ int ares;
+#endif
if (is_not_atom(BIF_ARG_1))
BIF_ERROR(BIF_P, BADARG);
@@ -2554,9 +2619,18 @@ BIF_RETTYPE atom_to_list_1(BIF_ALIST_1)
ap = atom_tab(atom_val(BIF_ARG_1));
if (ap->len == 0)
BIF_RET(NIL); /* the empty atom */
- need = ap->len*2;
- hp = HAlloc(BIF_P, need);
- BIF_RET(buf_to_intlist(&hp,(char*)ap->name,ap->len, NIL));
+
+#ifdef DEBUG
+ ares =
+#endif
+ erts_analyze_utf8(ap->name, ap->len, &err_pos, &num_chars, NULL);
+ ASSERT(ares == ERTS_UTF8_OK);
+
+ res = erts_utf8_to_list(BIF_P, num_chars, ap->name, ap->len, ap->len,
+ &num_built, &num_eaten, NIL);
+ ASSERT(num_built == num_chars);
+ ASSERT(num_eaten == ap->len);
+ BIF_RET(res);
}
/**********************************************************************/
@@ -2566,18 +2640,19 @@ BIF_RETTYPE atom_to_list_1(BIF_ALIST_1)
BIF_RETTYPE list_to_atom_1(BIF_ALIST_1)
{
Eterm res;
- char *buf = (char *) erts_alloc(ERTS_ALC_T_TMP, MAX_ATOM_LENGTH);
- int i = intlist_to_buf(BIF_ARG_1, buf, MAX_ATOM_LENGTH);
+ char *buf = (char *) erts_alloc(ERTS_ALC_T_TMP, MAX_ATOM_CHARACTERS);
+ int i = intlist_to_buf(BIF_ARG_1, buf, MAX_ATOM_CHARACTERS);
if (i < 0) {
erts_free(ERTS_ALC_T_TMP, (void *) buf);
i = list_length(BIF_ARG_1);
- if (i > MAX_ATOM_LENGTH) {
+ if (i > MAX_ATOM_CHARACTERS) {
BIF_ERROR(BIF_P, SYSTEM_LIMIT);
}
BIF_ERROR(BIF_P, BADARG);
}
- res = am_atom_put(buf, i);
+ res = erts_atom_put((byte *) buf, i, ERTS_ATOM_ENC_LATIN1, 1);
+ ASSERT(is_atom(res));
erts_free(ERTS_ALC_T_TMP, (void *) buf);
BIF_RET(res);
}
@@ -2587,16 +2662,16 @@ BIF_RETTYPE list_to_atom_1(BIF_ALIST_1)
BIF_RETTYPE list_to_existing_atom_1(BIF_ALIST_1)
{
int i;
- char *buf = (char *) erts_alloc(ERTS_ALC_T_TMP, MAX_ATOM_LENGTH);
+ char *buf = (char *) erts_alloc(ERTS_ALC_T_TMP, MAX_ATOM_CHARACTERS);
- if ((i = intlist_to_buf(BIF_ARG_1, buf, MAX_ATOM_LENGTH)) < 0) {
+ if ((i = intlist_to_buf(BIF_ARG_1, buf, MAX_ATOM_CHARACTERS)) < 0) {
error:
erts_free(ERTS_ALC_T_TMP, (void *) buf);
BIF_ERROR(BIF_P, BADARG);
} else {
Eterm a;
- if (erts_atom_get(buf, i, &a)) {
+ if (erts_atom_get(buf, i, &a, 1)) {
erts_free(ERTS_ALC_T_TMP, (void *) buf);
BIF_RET(a);
} else {
@@ -2851,12 +2926,78 @@ BIF_RETTYPE float_to_list_1(BIF_ALIST_1)
if (is_not_float(BIF_ARG_1))
BIF_ERROR(BIF_P, BADARG);
GET_DOUBLE(BIF_ARG_1, f);
- if ((i = sys_double_to_chars(f.fd, fbuf)) <= 0)
+ if ((i = sys_double_to_chars(f.fd, fbuf, sizeof(fbuf))) <= 0)
BIF_ERROR(BIF_P, EXC_INTERNAL_ERROR);
need = i*2;
hp = HAlloc(BIF_P, need);
BIF_RET(buf_to_intlist(&hp, fbuf, i, NIL));
- }
+}
+
+BIF_RETTYPE float_to_list_2(BIF_ALIST_2)
+{
+ const static int arity_two = make_arityval(2);
+ int decimals = SYS_DEFAULT_FLOAT_DECIMALS;
+ int compact = 0;
+ enum fmt_type_ {
+ FMT_LEGACY,
+ FMT_FIXED,
+ FMT_SCIENTIFIC
+ } fmt_type = FMT_LEGACY;
+ Eterm list = BIF_ARG_2;
+ Eterm arg;
+ int i;
+ Uint need;
+ Eterm* hp;
+ FloatDef f;
+ char fbuf[256];
+
+ /* check the arguments */
+ if (is_not_float(BIF_ARG_1))
+ goto badarg;
+
+ for(; is_list(list); list = CDR(list_val(list))) {
+ arg = CAR(list_val(list));
+ if (arg == am_compact) {
+ compact = 1;
+ continue;
+ } else if (is_tuple(arg)) {
+ Eterm* tp = tuple_val(arg);
+ if (*tp == arity_two && is_small(tp[2])) {
+ decimals = signed_val(tp[2]);
+ if (decimals > 0 && decimals < sizeof(fbuf) - 6 /* "X." ++ "e+YY" */)
+ switch (tp[1]) {
+ case am_decimals:
+ fmt_type = FMT_FIXED;
+ continue;
+ case am_scientific:
+ fmt_type = FMT_SCIENTIFIC;
+ continue;
+ }
+ }
+ }
+ goto badarg;
+ }
+ if (is_not_nil(list)) {
+ goto badarg;
+ }
+
+ GET_DOUBLE(BIF_ARG_1, f);
+
+ if (fmt_type == FMT_FIXED) {
+ if ((i = sys_double_to_chars_fast(f.fd, fbuf, sizeof(fbuf),
+ decimals, compact)) <= 0)
+ goto badarg;
+ } else {
+ if ((i = sys_double_to_chars_ext(f.fd, fbuf, sizeof(fbuf), decimals)) <= 0)
+ goto badarg;
+ }
+
+ need = i*2;
+ hp = HAlloc(BIF_P, need);
+ BIF_RET(buf_to_intlist(&hp, fbuf, i, NIL));
+badarg:
+ BIF_ERROR(BIF_P, BADARG);
+}
/**********************************************************************/
@@ -3111,7 +3252,7 @@ BIF_RETTYPE list_to_tuple_1(BIF_ALIST_1)
Eterm* hp;
int len;
- if ((len = list_length(list)) < 0) {
+ if ((len = list_length(list)) < 0 || len > ERTS_MAX_TUPLE_SIZE) {
BIF_ERROR(BIF_P, BADARG);
}
@@ -3133,7 +3274,7 @@ BIF_RETTYPE list_to_tuple_1(BIF_ALIST_1)
BIF_RETTYPE self_0(BIF_ALIST_0)
{
- BIF_RET(BIF_P->id);
+ BIF_RET(BIF_P->common.id);
}
/**********************************************************************/
@@ -3170,11 +3311,9 @@ static erts_smp_spinlock_t make_ref_lock;
static erts_smp_mtx_t ports_snapshot_mtx;
erts_smp_atomic_t erts_dead_ports_ptr; /* To store dying ports during snapshot */
-Eterm erts_make_ref_in_buffer(Eterm buffer[REF_THING_SIZE])
+void
+erts_make_ref_in_array(Uint32 ref[ERTS_MAX_REF_NUMBERS])
{
- Eterm* hp = buffer;
- Uint32 ref0, ref1, ref2;
-
erts_smp_spin_lock(&make_ref_lock);
reference0++;
@@ -3186,24 +3325,36 @@ Eterm erts_make_ref_in_buffer(Eterm buffer[REF_THING_SIZE])
}
}
- ref0 = reference0;
- ref1 = reference1;
- ref2 = reference2;
+ ref[0] = reference0;
+ ref[1] = reference1;
+ ref[2] = reference2;
erts_smp_spin_unlock(&make_ref_lock);
+}
+
+Eterm erts_make_ref_in_buffer(Eterm buffer[REF_THING_SIZE])
+{
+ Eterm* hp = buffer;
+ Uint32 ref[ERTS_MAX_REF_NUMBERS];
- write_ref_thing(hp, ref0, ref1, ref2);
+ erts_make_ref_in_array(ref);
+ write_ref_thing(hp, ref[0], ref[1], ref[2]);
return make_internal_ref(hp);
}
Eterm erts_make_ref(Process *p)
{
Eterm* hp;
+ Uint32 ref[ERTS_MAX_REF_NUMBERS];
ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_MAIN & erts_proc_lc_my_proc_locks(p));
hp = HAlloc(p, REF_THING_SIZE);
- return erts_make_ref_in_buffer(hp);
+
+ erts_make_ref_in_array(ref);
+ write_ref_thing(hp, ref[0], ref[1], ref[2]);
+
+ return make_internal_ref(hp);
}
BIF_RETTYPE make_ref_0(BIF_ALIST_0)
@@ -3467,7 +3618,7 @@ BIF_RETTYPE garbage_collect_1(BIF_ALIST_1)
BIF_ERROR(BIF_P, BADARG);
}
- if (BIF_P->id == BIF_ARG_1)
+ if (BIF_P->common.id == BIF_ARG_1)
rp = BIF_P;
else {
#ifdef ERTS_SMP
@@ -3507,71 +3658,23 @@ BIF_RETTYPE garbage_collect_0(BIF_ALIST_0)
}
/**********************************************************************/
-/* Return a list of active ports */
+/*
+ * The erlang:processes/0 BIF.
+ */
-BIF_RETTYPE ports_0(BIF_ALIST_0)
+BIF_RETTYPE processes_0(BIF_ALIST_0)
{
- Eterm res = NIL;
- Eterm* port_buf = erts_alloc(ERTS_ALC_T_TMP,
- sizeof(Eterm)*erts_max_ports);
- Eterm* pp = port_buf;
- Eterm* dead_ports;
- int alive, dead;
- Uint32 next_ss;
- int i;
-
- /* To get a consistent snapshot...
- * We add alive ports from start of the buffer
- * while dying ports are added from the other end by the killing threads.
- */
-
- erts_smp_mtx_lock(&ports_snapshot_mtx); /* One snapshot at a time */
-
- erts_smp_atomic_set_nob(&erts_dead_ports_ptr,
- (erts_aint_t) (port_buf + erts_max_ports));
-
- next_ss = erts_smp_atomic32_inc_read_relb(&erts_ports_snapshot);
-
- for (i = erts_max_ports-1; i >= 0; i--) {
- Port* prt = &erts_port[i];
- erts_smp_port_state_lock(prt);
- if (!(prt->status & ERTS_PORT_SFLGS_DEAD)
- && prt->snapshot != next_ss) {
- ASSERT(prt->snapshot == next_ss - 1);
- *pp++ = prt->id;
- prt->snapshot = next_ss; /* Consumed by this snapshot */
- }
- erts_smp_port_state_unlock(prt);
- }
-
- dead_ports = (Eterm*)erts_smp_atomic_xchg_nob(&erts_dead_ports_ptr,
- (erts_aint_t) NULL);
- erts_smp_mtx_unlock(&ports_snapshot_mtx);
-
- ASSERT(pp <= dead_ports);
-
- alive = pp - port_buf;
- dead = port_buf + erts_max_ports - dead_ports;
-
- ASSERT((alive+dead) <= erts_max_ports);
-
- if (alive+dead > 0) {
- erts_aint_t i;
- Eterm *hp = HAlloc(BIF_P, (alive+dead)*2);
-
- for (i = 0; i < alive; i++) {
- res = CONS(hp, port_buf[i], res);
- hp += 2;
- }
- for (i = 0; i < dead; i++) {
- res = CONS(hp, dead_ports[i], res);
- hp += 2;
- }
- }
+ return erts_ptab_list(BIF_P, &erts_proc);
+}
- erts_free(ERTS_ALC_T_TMP, port_buf);
+/**********************************************************************/
+/*
+ * The erlang:ports/0 BIF.
+ */
- BIF_RET(res);
+BIF_RETTYPE ports_0(BIF_ALIST_0)
+{
+ return erts_ptab_list(BIF_P, &erts_port);
}
/**********************************************************************/
@@ -4201,12 +4304,13 @@ BIF_RETTYPE system_flag_2(BIF_ALIST_2)
BIF_RET(old_value);
}
} else if (BIF_ARG_1 == make_small(1)) {
- Uint i;
+ int i, max;
ErlMessage* mp;
erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
erts_smp_thr_progress_block();
- for (i = 0; i < erts_max_processes; i++) {
+ max = erts_ptab_max(&erts_proc);
+ for (i = 0; i < max; i++) {
Process *p = erts_pix2proc(i);
if (p) {
#ifdef USE_VM_PROBES
@@ -4553,6 +4657,8 @@ void erts_init_bif(void)
am_format_cpu_topology,
1);
await_proc_exit_trap = erts_export_put(am_erlang,am_await_proc_exit,3);
+ await_port_send_result_trap
+ = erts_export_put(am_erts_internal, am_await_port_send_result, 3);
await_sched_wall_time_mod_trap
= erts_export_put(am_erlang, am_await_sched_wall_time_modifications, 2);
erts_smp_atomic32_init_nob(&sched_wall_time, 0);
@@ -4568,19 +4674,18 @@ bif erlang:send_to_logger/2
BIF_RETTYPE send_to_logger_2(BIF_ALIST_2)
{
byte *buf;
- int len;
+ ErlDrvSizeT len;
if (!is_atom(BIF_ARG_1) || !(is_list(BIF_ARG_2) ||
is_nil(BIF_ARG_1))) {
BIF_ERROR(BIF_P,BADARG);
}
- len = io_list_len(BIF_ARG_2);
- if (len < 0)
+ if (erts_iolist_size(BIF_ARG_2, &len) != 0)
BIF_ERROR(BIF_P,BADARG);
else if (len == 0)
buf = "";
else {
#ifdef DEBUG
- int len2;
+ ErlDrvSizeT len2;
#endif
buf = (byte *) erts_alloc(ERTS_ALC_T_TMP, len+1);
#ifdef DEBUG
@@ -4588,7 +4693,7 @@ BIF_RETTYPE send_to_logger_2(BIF_ALIST_2)
#else
(void)
#endif
- io_list_to_buf(BIF_ARG_2, buf, len);
+ erts_iolist_to_buf(BIF_ARG_2, buf, len);
ASSERT(len2 == len);
buf[len] = '\0';
switch (BIF_ARG_1) {
@@ -4682,7 +4787,6 @@ BIF_RETTYPE dt_prepend_vm_tag_data_1(BIF_ALIST_1)
#ifdef USE_VM_PROBES
Eterm b;
Eterm *hp;
- hp = HAlloc(BIF_P,2);
if (is_binary((DT_UTAG(BIF_P)))) {
Uint sz = binary_size(DT_UTAG(BIF_P));
int i;
@@ -4699,6 +4803,7 @@ BIF_RETTYPE dt_prepend_vm_tag_data_1(BIF_ALIST_1)
} else {
b = new_binary(BIF_P,(byte *)"\0",1);
}
+ hp = HAlloc(BIF_P,2);
BIF_RET(CONS(hp,b,BIF_ARG_1));
#else
BIF_RET(BIF_ARG_1);
@@ -4709,7 +4814,6 @@ BIF_RETTYPE dt_append_vm_tag_data_1(BIF_ALIST_1)
#ifdef USE_VM_PROBES
Eterm b;
Eterm *hp;
- hp = HAlloc(BIF_P,2);
if (is_binary((DT_UTAG(BIF_P)))) {
Uint sz = binary_size(DT_UTAG(BIF_P));
int i;
@@ -4726,6 +4830,7 @@ BIF_RETTYPE dt_append_vm_tag_data_1(BIF_ALIST_1)
} else {
b = new_binary(BIF_P,(byte *)"\0",1);
}
+ hp = HAlloc(BIF_P,2);
BIF_RET(CONS(hp,BIF_ARG_1,b));
#else
BIF_RET(BIF_ARG_1);
@@ -4749,14 +4854,14 @@ BIF_RETTYPE dt_spread_tag_1(BIF_ALIST_1)
#ifdef DTRACE_TAG_HARDDEBUG
erts_fprintf(stderr,
"Dtrace -> (%T) start spreading tag %T\r\n",
- BIF_P->id,DT_UTAG(BIF_P));
+ BIF_P->common.id,DT_UTAG(BIF_P));
#endif
} else {
DT_UTAG_FLAGS(BIF_P) &= ~DT_UTAG_SPREADING;
#ifdef DTRACE_TAG_HARDDEBUG
erts_fprintf(stderr,
"Dtrace -> (%T) stop spreading tag %T\r\n",
- BIF_P->id,DT_UTAG(BIF_P));
+ BIF_P->common.id,DT_UTAG(BIF_P));
#endif
}
}
@@ -4782,7 +4887,7 @@ BIF_RETTYPE dt_restore_tag_1(BIF_ALIST_1)
#ifdef DTRACE_TAG_HARDDEBUG
erts_fprintf(stderr,
"Dtrace -> (%T) restore Killing tag!\r\n",
- BIF_P->id);
+ BIF_P->common.id);
#endif
}
DT_UTAG(BIF_P) = NIL;
@@ -4799,12 +4904,12 @@ BIF_RETTYPE dt_restore_tag_1(BIF_ALIST_1)
erts_fprintf(stderr,
"Dtrace -> (%T) restore stop spreading "
"tag %T\r\n",
- BIF_P->id, tpl[2]);
+ BIF_P->common.id, tpl[2]);
} else if ((x & DT_UTAG_SPREADING) &&
!(DT_UTAG_FLAGS(BIF_P) & DT_UTAG_SPREADING)) {
erts_fprintf(stderr,
"Dtrace -> (%T) restore start spreading "
- "tag %T\r\n",BIF_P->id,tpl[2]);
+ "tag %T\r\n",BIF_P->common.id,tpl[2]);
}
#endif
DT_UTAG_FLAGS(BIF_P) = x;
diff --git a/erts/emulator/beam/bif.h b/erts/emulator/beam/bif.h
index 7cb2c78815..4e456988a3 100644
--- a/erts/emulator/beam/bif.h
+++ b/erts/emulator/beam/bif.h
@@ -59,6 +59,8 @@ do { \
} while(0)
#define BUMP_REDS(p, gc) do { \
+ ASSERT(p); \
+ ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_MAIN & erts_proc_lc_my_proc_locks(p));\
(p)->fcalls -= (gc); \
if ((p)->fcalls < 0) { \
if (!ERTS_PROC_GET_SAVED_CALLS_BUF((p))) \
@@ -322,27 +324,6 @@ do { \
ERTS_BIF_EXITED((PROC)); \
} while (0)
-#ifdef ERTS_SMP
-#define ERTS_SMP_BIF_CHK_PENDING_EXIT(P, L) \
-do { \
- ERTS_SMP_LC_ASSERT((L) == erts_proc_lc_my_proc_locks((P))); \
- ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_MAIN & (L)); \
- if (!((L) & ERTS_PROC_LOCK_STATUS)) \
- erts_smp_proc_lock((P), ERTS_PROC_LOCK_STATUS); \
- if (ERTS_PROC_PENDING_EXIT((P))) { \
- erts_handle_pending_exit((P), (L)|ERTS_PROC_LOCK_STATUS); \
- erts_smp_proc_unlock((P), \
- (((L)|ERTS_PROC_LOCK_STATUS) \
- & ~ERTS_PROC_LOCK_MAIN)); \
- ERTS_BIF_EXITED((P)); \
- } \
- if (!((L) & ERTS_PROC_LOCK_STATUS)) \
- erts_smp_proc_unlock((P), ERTS_PROC_LOCK_STATUS); \
-} while (0)
-#else
-#define ERTS_SMP_BIF_CHK_PENDING_EXIT(P, L)
-#endif
-
/*
* The ERTS_BIF_*_AWAIT_X_*_TRAP makros either exits the caller, or
* sets up a trap to erlang:await_proc_exit/3.
diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab
index f7dad2767f..a79feb6da3 100644
--- a/erts/emulator/beam/bif.tab
+++ b/erts/emulator/beam/bif.tab
@@ -31,432 +31,234 @@
#
# Important: Use "ubif" for guard BIFs and operators; use "bif" for ordinary BIFs.
#
-# Add new BIFs to the end of the file. Do not bother adding a "packaged BIF name"
-# (such as 'erl.lang.number'); if/when packages will be supported we will add
-# all those names.
+# Add new BIFs to the end of the file.
#
# Note: Guards BIFs require special support in the compiler (to be able to actually
# call them from within a guard).
#
ubif erlang:abs/1
-ubif 'erl.lang.number':abs/1 ebif_abs_1
bif erlang:adler32/1
-bif 'erl.util.crypt.adler32':sum/1 ebif_adler32_1
bif erlang:adler32/2
-bif 'erl.util.crypt.adler32':sum/2 ebif_adler32_2
bif erlang:adler32_combine/3
-bif 'erl.util.crypt.adler32':combine/3 ebif_adler32_combine_3
bif erlang:apply/3
-bif 'erl.lang':apply/3 ebif_apply_3
bif erlang:atom_to_list/1
-bif 'erl.lang.atom':to_string/1 ebif_atom_to_string_1 atom_to_list_1
bif erlang:binary_to_list/1
-bif 'erl.lang.binary':to_list/1 ebif_binary_to_list_1
bif erlang:binary_to_list/3
-bif 'erl.lang.binary':to_list/3 ebif_binary_to_list_3
bif erlang:binary_to_term/1
-bif 'erl.lang.binary':to_term/1 ebif_binary_to_term_1
bif erlang:check_process_code/2
-bif 'erl.system.code':check_process/2 ebif_check_process_code_2
bif erlang:crc32/1
-bif 'erl.util.crypt.crc32':sum/1 ebif_crc32_1
bif erlang:crc32/2
-bif 'erl.util.crypt.crc32':sum/2 ebif_crc32_2
bif erlang:crc32_combine/3
-bif 'erl.util.crypt.crc32':combine/3 ebif_crc32_combine_3
bif erlang:date/0
-bif 'erl.util.date':today/0 ebif_date_0
bif erlang:delete_module/1
-bif 'erl.system.code':delete/1 ebif_delete_module_1
bif erlang:display/1
-bif 'erl.system.debug':display/1 ebif_display_1
bif erlang:display_string/1
-bif 'erl.system.debug':display_string/1 ebif_display_string_1
bif erlang:display_nl/0
-bif 'erl.system.debug':display_nl/0 ebif_display_nl_0
ubif erlang:element/2
-ubif 'erl.lang.tuple':element/2 ebif_element_2
bif erlang:erase/0
-bif 'erl.lang.proc.pdict':erase/0 ebif_erase_0
bif erlang:erase/1
-bif 'erl.lang.proc.pdict':erase/1 ebif_erase_1
bif erlang:exit/1
-bif 'erl.lang':exit/1 ebif_exit_1
bif erlang:exit/2
-bif 'erl.lang.proc':signal/2 ebif_signal_2 exit_2
bif erlang:external_size/1
-bif 'erl.lang.term':external_size/1 ebif_external_size_1
bif erlang:external_size/2
-bif 'erl.lang.term':external_size/2 ebif_external_size_2
ubif erlang:float/1
-ubif 'erl.lang.number':to_float/1 ebif_to_float_1 float_1
bif erlang:float_to_list/1
-bif 'erl.lang.float':to_string/1 ebif_float_to_string_1 float_to_list_1
+bif erlang:float_to_list/2
bif erlang:fun_info/2
-bif 'erl.lang.function':info/2 ebif_fun_info_2
bif erlang:garbage_collect/0
-bif 'erl.system':garbage_collect/0 ebif_garbage_collect_0
bif erlang:garbage_collect/1
-bif 'erl.system':garbage_collect/1 ebif_garbage_collect_1
bif erlang:get/0
-bif 'erl.lang.proc.pdict':get/0 ebif_get_0
bif erlang:get/1
-bif 'erl.lang.proc.pdict':get/1 ebif_get_1
bif erlang:get_keys/1
-bif 'erl.lang.proc.pdict':get_keys/1 ebif_get_keys_1
bif erlang:group_leader/0
-bif 'erl.lang.proc':group_leader/0 ebif_group_leader_0
bif erlang:group_leader/2
-bif 'erl.lang.proc':set_group_leader/2 ebif_group_leader_2
bif erlang:halt/0
-bif 'erl.lang.system':halt/0 ebif_halt_0
bif erlang:halt/1
-bif 'erl.lang.system':halt/1 ebif_halt_1
bif erlang:halt/2
-bif 'erl.lang.system':halt/2 ebif_halt_2
bif erlang:phash/2
bif erlang:phash2/1
bif erlang:phash2/2
-bif 'erl.lang.term':hash/1 ebif_phash2_1
-bif 'erl.lang.term':hash/2 ebif_phash2_2
ubif erlang:hd/1
-ubif 'erl.lang.list':hd/1 ebif_hd_1
bif erlang:integer_to_list/1
-bif 'erl.lang.integer':to_string/1 ebif_integer_to_string_1 integer_to_list_1
bif erlang:is_alive/0
-bif 'erl.lang.node':is_alive/0 ebif_is_alive_0
ubif erlang:length/1
-ubif 'erl.lang.list':length/1 ebif_length_1
bif erlang:link/1
-bif 'erl.lang.proc':link/1 ebif_link_1
bif erlang:list_to_atom/1
-bif 'erl.lang.atom':from_string/1 ebif_string_to_atom_1 list_to_atom_1
bif erlang:list_to_binary/1
-bif 'erl.lang.binary':from_list/1 ebif_list_to_binary_1
bif erlang:list_to_float/1
-bif 'erl.lang.float':from_string/1 ebif_string_to_float_1 list_to_float_1
bif erlang:list_to_integer/1
-bif 'erl.lang.integer':from_string/1 ebif_string_to_integer_1 list_to_integer_1
bif erlang:list_to_pid/1
-bif 'erl.lang.proc':string_to_pid/1 ebif_string_to_pid_1 list_to_pid_1
bif erlang:list_to_tuple/1
-bif 'erl.lang.tuple':from_list/1 ebif_list_to_tuple_1
bif erlang:loaded/0
-bif 'erl.system.code':loaded/0 ebif_loaded_0
bif erlang:localtime/0
-bif 'erl.util.date':local/0 ebif_localtime_0
bif erlang:localtime_to_universaltime/2
-bif 'erl.util.date':local_to_utc/2 ebif_localtime_to_universaltime_2
bif erlang:make_ref/0
-bif 'erl.lang.ref':new/0 ebif_make_ref_0
bif erlang:md5/1
-bif 'erl.util.crypt.md5':digest/1 ebif_md5_1
bif erlang:md5_init/0
-bif 'erl.util.crypt.md5':init/0 ebif_md5_init_0
bif erlang:md5_update/2
-bif 'erl.util.crypt.md5':update/2 ebif_md5_update_2
bif erlang:md5_final/1
-bif 'erl.util.crypt.md5':final/1 ebif_md5_final_1
bif erlang:module_loaded/1
-bif 'erl.system.code':is_loaded/1 ebif_is_loaded_1 module_loaded_1
bif erlang:function_exported/3
-bif 'erl.system.code':is_loaded/3 ebif_is_loaded_3 function_exported_3
bif erlang:monitor_node/2
-bif 'erl.lang.node':monitor/2 ebif_monitor_node_2
bif erlang:monitor_node/3
-bif 'erl.lang.node':monitor/3 ebif_monitor_node_3
ubif erlang:node/1
-ubif 'erl.lang.node':node/1 ebif_node_1
ubif erlang:node/0
-ubif 'erl.lang.node':node/0 ebif_node_0
bif erlang:nodes/1
-bif 'erl.lang.node':nodes/1 ebif_nodes_1
bif erlang:now/0
-bif 'erl.system':now/0 ebif_now_0
bif erlang:open_port/2
-bif 'erl.lang.port':open/2 ebif_open_port_2 open_port_2
bif erlang:pid_to_list/1
-bif 'erl.lang.proc':pid_to_string/1 ebif_pid_to_string_1 pid_to_list_1
-bif erlang:port_info/1
-bif 'erl.lang.port':info/1 ebif_port_info_1
-bif erlang:port_info/2
-bif 'erl.lang.port':info/2 ebif_port_info_2
bif erlang:ports/0
-bif 'erl.lang.node':ports/0 ebif_ports_0
bif erlang:pre_loaded/0
-bif 'erl.system.code':preloaded/0 ebif_pre_loaded_0
bif erlang:process_flag/2
-bif 'erl.lang.proc':set_flag/2 ebif_process_flag_2
bif erlang:process_flag/3
-bif 'erl.lang.proc':set_flag/3 ebif_process_flag_3
bif erlang:process_info/1
-bif 'erl.lang.proc':info/1 ebif_process_info_1
bif erlang:process_info/2
-bif 'erl.lang.proc':info/2 ebif_process_info_2
bif erlang:processes/0
-bif 'erl.lang.node':processes/0 ebif_processes_0
bif erlang:purge_module/1
-bif 'erl.system.code':purge/1 ebif_purge_module_1
bif erlang:put/2
-bif 'erl.lang.proc.pdict':put/2 ebif_put_2
bif erlang:register/2
-bif 'erl.lang.node':register/2 ebif_register_2
bif erlang:registered/0
-bif 'erl.lang.node':registered/0 ebif_registered_0
ubif erlang:round/1
-ubif 'erl.lang.number':round/1 ebif_round_1
ubif erlang:self/0
-ubif 'erl.lang.proc':self/0 ebif_self_0
bif erlang:setelement/3
-bif 'erl.lang.tuple':setelement/3 ebif_setelement_3
ubif erlang:size/1
-ubif 'erl.lang.term':size/1 ebif_size_1
bif erlang:spawn/3
-bif 'erl.lang.proc':spawn/3 ebif_spawn_3
bif erlang:spawn_link/3
-bif 'erl.lang.proc':spawn_link/3 ebif_spawn_link_3
bif erlang:split_binary/2
-bif 'erl.lang.binary':split/2 ebif_split_binary_2
bif erlang:statistics/1
-bif 'erl.system':statistics/1 ebif_statistics_1
bif erlang:term_to_binary/1
-bif 'erl.lang.binary':from_term/1 ebif_term_to_binary_1
bif erlang:term_to_binary/2
-bif 'erl.lang.binary':from_term/2 ebif_term_to_binary_2
bif erlang:throw/1
-bif 'erl.lang':throw/1 ebif_throw_1
bif erlang:time/0
-bif 'erl.util.date':time_of_day/0 ebif_time_0
ubif erlang:tl/1
-ubif 'erl.lang.list':tl/1 ebif_tl_1
ubif erlang:trunc/1
-ubif 'erl.lang.number':trunc/1 ebif_trunc_1
bif erlang:tuple_to_list/1
-bif 'erl.lang.tuple':to_list/1 ebif_tuple_to_list_1
bif erlang:universaltime/0
-bif 'erl.util.date':utc/0 ebif_universaltime_0
bif erlang:universaltime_to_localtime/1
-bif 'erl.util.date':utc_to_local/1 ebif_universaltime_to_localtime_1
bif erlang:unlink/1
-bif 'erl.lang.proc':unlink/1 ebif_unlink_1
bif erlang:unregister/1
-bif 'erl.lang.node':unregister/1 ebif_unregister_1
bif erlang:whereis/1
-bif 'erl.lang.node':whereis/1 ebif_whereis_1
bif erlang:spawn_opt/1
-bif 'erl.lang.proc':spawn_opt/1 ebif_spawn_opt_1
bif erlang:setnode/2
bif erlang:setnode/3
bif erlang:dist_exit/3
-bif erlang:port_call/2
-bif 'erl.lang.port':call/2 ebif_port_call_2
-bif erlang:port_call/3
-bif 'erl.lang.port':call/3 ebif_port_call_3
-bif erlang:port_command/2
-bif 'erl.lang.port':command/2 ebif_port_command_2
-bif erlang:port_command/3
-bif 'erl.lang.port':command/3 ebif_port_command_3
-bif erlang:port_control/3
-bif 'erl.lang.port':control/3 ebif_port_control_3
-bif erlang:port_close/1
-bif 'erl.lang.port':close/1 ebif_port_close_1
-bif erlang:port_connect/2
-bif 'erl.lang.port':connect/2 ebif_port_connect_2
-bif erlang:port_set_data/2
-bif 'erl.lang.port':set_data/2 ebif_port_set_data_2
-bif erlang:port_get_data/1
-bif 'erl.lang.port':get_data/1 ebif_port_get_data_1
+# Static native functions in erts_internal
+bif erts_internal:port_info/1
+bif erts_internal:port_info/2
+bif erts_internal:port_call/3
+bif erts_internal:port_command/3
+bif erts_internal:port_control/3
+bif erts_internal:port_close/1
+bif erts_internal:port_connect/2
+bif erts_internal:port_set_data/2
+bif erts_internal:port_get_data/1
# Tracing & debugging.
bif erlang:trace_pattern/2
-bif 'erl.system.debug':trace_pattern/2 ebif_trace_pattern_2
bif erlang:trace_pattern/3
-bif 'erl.system.debug':trace_pattern/3 ebif_trace_pattern_3
bif erlang:trace/3
-bif 'erl.system.debug':trace/3 ebif_trace_3
bif erlang:trace_info/2
-bif 'erl.system.debug':trace_info/2 ebif_trace_info_2
bif erlang:trace_delivered/1
-bif 'erl.system.debug':trace_delivered/1 ebif_trace_delivered_1
bif erlang:seq_trace/2
-bif 'erl.system.debug':seq_trace/2 ebif_seq_trace_2
bif erlang:seq_trace_info/1
-bif 'erl.system.debug':seq_trace_info/1 ebif_seq_trace_info_1
bif erlang:seq_trace_print/1
-bif 'erl.system.debug':seq_trace_print/1 ebif_seq_trace_print_1
bif erlang:seq_trace_print/2
-bif 'erl.system.debug':seq_trace_print/2 ebif_seq_trace_print_2
bif erlang:suspend_process/2
-bif 'erl.system.debug':suspend_process/2 ebif_suspend_process_2
bif erlang:resume_process/1
-bif 'erl.system.debug':resume_process/1 ebif_resume_process_1
bif erlang:process_display/2
-bif 'erl.system.debug':process_display/2 ebif_process_display_2
bif erlang:bump_reductions/1
-bif 'erl.lang.proc':bump_reductions/1 ebif_bump_reductions_1
bif math:cos/1
-bif 'erl.lang.math':cos/1 ebif_math_cos_1
bif math:cosh/1
-bif 'erl.lang.math':cosh/1 ebif_math_cosh_1
bif math:sin/1
-bif 'erl.lang.math':sin/1 ebif_math_sin_1
bif math:sinh/1
-bif 'erl.lang.math':sinh/1 ebif_math_sinh_1
bif math:tan/1
-bif 'erl.lang.math':tan/1 ebif_math_tan_1
bif math:tanh/1
-bif 'erl.lang.math':tanh/1 ebif_math_tanh_1
bif math:acos/1
-bif 'erl.lang.math':acos/1 ebif_math_acos_1
bif math:acosh/1
-bif 'erl.lang.math':acosh/1 ebif_math_acosh_1
bif math:asin/1
-bif 'erl.lang.math':asin/1 ebif_math_asin_1
bif math:asinh/1
-bif 'erl.lang.math':asinh/1 ebif_math_asinh_1
bif math:atan/1
-bif 'erl.lang.math':atan/1 ebif_math_atan_1
bif math:atanh/1
-bif 'erl.lang.math':atanh/1 ebif_math_atanh_1
bif math:erf/1
-bif 'erl.lang.math':erf/1 ebif_math_erf_1
bif math:erfc/1
-bif 'erl.lang.math':erfc/1 ebif_math_erfc_1
bif math:exp/1
-bif 'erl.lang.math':exp/1 ebif_math_exp_1
bif math:log/1
-bif 'erl.lang.math':log/1 ebif_math_log_1
bif math:log10/1
-bif 'erl.lang.math':log10/1 ebif_math_log10_1
bif math:sqrt/1
-bif 'erl.lang.math':sqrt/1 ebif_math_sqrt_1
bif math:atan2/2
-bif 'erl.lang.math':atan2/2 ebif_math_atan2_2
bif math:pow/2
-bif 'erl.lang.math':pow/2 ebif_math_pow_2
bif erlang:start_timer/3
-bif 'erl.lang.timer':start/3 ebif_start_timer_3
bif erlang:send_after/3
-bif 'erl.lang.timer':send_after/3 ebif_send_after_3
bif erlang:cancel_timer/1
-bif 'erl.lang.timer':cancel/1 ebif_cancel_timer_1
bif erlang:read_timer/1
-bif 'erl.lang.timer':read/1 ebif_read_timer_1
bif erlang:make_tuple/2
-bif 'erl.lang.tuple':make/2 ebif_make_tuple_2
bif erlang:append_element/2
-bif 'erl.lang.tuple':append_element/2 ebif_append_element_2
bif erlang:make_tuple/3
bif erlang:system_flag/2
-bif 'erl.system':set_flag/2 ebif_system_flag_2
bif erlang:system_info/1
-bif 'erl.system':info/1 ebif_system_info_1
# New in R9C
bif erlang:system_monitor/0
-bif 'erl.system':monitor/0 ebif_system_monitor_0
bif erlang:system_monitor/1
-bif 'erl.system':monitor/1 ebif_system_monitor_1
bif erlang:system_monitor/2
-bif 'erl.system':monitor/2 ebif_system_monitor_2
# Added 2006-11-07
bif erlang:system_profile/2
-bif 'erl.system':profile/2 ebif_system_profile_2
# End Added 2006-11-07
# Added 2007-01-17
bif erlang:system_profile/0
-bif 'erl.system':profile/0 ebif_system_profile_0
# End Added 2007-01-17
bif erlang:ref_to_list/1
-bif 'erl.lang.ref':to_string/1 ebif_ref_to_string_1 ref_to_list_1
bif erlang:port_to_list/1
-bif 'erl.lang.port':to_string/1 ebif_port_to_string_1 port_to_list_1
bif erlang:fun_to_list/1
-bif 'erl.lang.function':to_string/1 ebif_fun_to_string_1 fun_to_list_1
bif erlang:monitor/2
-bif 'erl.lang.proc':monitor/2 ebif_monitor_2
bif erlang:demonitor/1
-bif 'erl.lang.proc':demonitor/1 ebif_demonitor_1
bif erlang:demonitor/2
-bif 'erl.lang.proc':demonitor/2 ebif_demonitor_2
bif erlang:is_process_alive/1
-bif 'erl.lang.proc':is_alive/1 ebif_proc_is_alive_1 is_process_alive_1
bif erlang:error/1 error_1
-bif 'erl.lang':error/1 ebif_error_1 error_1
bif erlang:error/2 error_2
-bif 'erl.lang':error/2 ebif_error_2 error_2
bif erlang:raise/3 raise_3
-bif 'erl.lang':raise/3 ebif_raise_3 raise_3
bif erlang:get_stacktrace/0
-bif 'erl.lang.proc':get_stacktrace/0 ebif_get_stacktrace_0
bif erlang:is_builtin/3
-bif 'erl.system.code':is_builtin/3 ebif_is_builtin_3
ubif erlang:'and'/2
-ubif 'erl.lang.bool':'and'/2 ebif_and_2
ubif erlang:'or'/2
-ubif 'erl.lang.bool':'or'/2 ebif_or_2
ubif erlang:'xor'/2
-ubif 'erl.lang.bool':'xor'/2 ebif_xor_2
ubif erlang:'not'/1
-ubif 'erl.lang.bool':'not'/1 ebif_not_1
ubif erlang:'>'/2 sgt_2
-ubif 'erl.lang.term':greater/2 ebif_gt_2 sgt_2
ubif erlang:'>='/2 sge_2
-ubif 'erl.lang.term':greater_or_equal/2 ebif_ge_2 sge_2
ubif erlang:'<'/2 slt_2
-ubif 'erl.lang.term':less/2 ebif_lt_2 slt_2
ubif erlang:'=<'/2 sle_2
-ubif 'erl.lang.term':less_or_equal/2 ebif_le_2 sle_2
ubif erlang:'=:='/2 seq_2
-ubif 'erl.lang.term':equal/2 ebif_eq_2 seq_2
ubif erlang:'=='/2 seqeq_2
-ubif 'erl.lang.term':arith_equal/2 ebif_areq_2 seqeq_2
ubif erlang:'=/='/2 sneq_2
-ubif 'erl.lang.term':not_equal/2 ebif_neq_2 sneq_2
ubif erlang:'/='/2 sneqeq_2
-ubif 'erl.lang.term':not_arith_equal/2 ebif_nareq_2 sneqeq_2
ubif erlang:'+'/2 splus_2
-ubif 'erl.lang.number':plus/2 ebif_plus_2 splus_2
ubif erlang:'-'/2 sminus_2
-ubif 'erl.lang.number':minus/2 ebif_minus_2 sminus_2
ubif erlang:'*'/2 stimes_2
-ubif 'erl.lang.number':multiply/2 ebif_multiply_2 stimes_2
ubif erlang:'/'/2 div_2
-ubif 'erl.lang.number':divide/2 ebif_divide_2 div_2
ubif erlang:'div'/2 intdiv_2
-ubif 'erl.lang.integer':'div'/2 ebif_intdiv_2
ubif erlang:'rem'/2
-ubif 'erl.lang.integer':'rem'/2 ebif_rem_2
ubif erlang:'bor'/2
-ubif 'erl.lang.integer':'bor'/2 ebif_bor_2
ubif erlang:'band'/2
-ubif 'erl.lang.integer':'band'/2 ebif_band_2
ubif erlang:'bxor'/2
-ubif 'erl.lang.integer':'bxor'/2 ebif_bxor_2
ubif erlang:'bsl'/2
-ubif 'erl.lang.integer':'bsl'/2 ebif_bsl_2
ubif erlang:'bsr'/2
-ubif 'erl.lang.integer':'bsr'/2 ebif_bsr_2
ubif erlang:'bnot'/1
-ubif 'erl.lang.integer':'bnot'/1 ebif_bnot_1
ubif erlang:'-'/1 sminus_1
-ubif 'erl.lang.number':minus/1 ebif_minus_1 sminus_1
ubif erlang:'+'/1 splus_1
-ubif 'erl.lang.number':plus/1 ebif_plus_1 splus_1
# New operators in R8. These were the only operators missing.
# erlang:send/2, erlang:append/2 and erlang:subtract/2 are now also
@@ -464,45 +266,27 @@ ubif 'erl.lang.number':plus/1 ebif_plus_1 splus_1
# internal references have been updated to the new ebif_... entries.
bif erlang:'!'/2 ebif_bang_2
-bif 'erl.lang.proc':send/2 ebif_send_2 send_2
bif erlang:send/2
-bif 'erl.lang':send/3 ebif_send_3 send_3
bif erlang:send/3
bif erlang:'++'/2 ebif_plusplus_2
-bif 'erl.lang.list':append/2 ebif_append_2 ebif_plusplus_2
bif erlang:append/2
bif erlang:'--'/2 ebif_minusminus_2
-bif 'erl.lang.list':subtract/2 ebif_list_subtract_2 ebif_minusminus_2
bif erlang:subtract/2
ubif erlang:is_atom/1
-ubif 'erl.lang.term':is_atom/1 ebif_is_atom_1
ubif erlang:is_list/1
-ubif 'erl.lang.term':is_list/1 ebif_is_list_1
ubif erlang:is_tuple/1
-ubif 'erl.lang.term':is_tuple/1 ebif_is_tuple_1
ubif erlang:is_float/1
-ubif 'erl.lang.term':is_float/1 ebif_is_float_1
ubif erlang:is_integer/1
-ubif 'erl.lang.term':is_integer/1 ebif_is_integer_1
ubif erlang:is_number/1
-ubif 'erl.lang.term':is_number/1 ebif_is_number_1
ubif erlang:is_pid/1
-ubif 'erl.lang.term':is_pid/1 ebif_is_pid_1
ubif erlang:is_port/1
-ubif 'erl.lang.term':is_port/1 ebif_is_port_1
ubif erlang:is_reference/1
-ubif 'erl.lang.term':is_reference/1 ebif_is_reference_1
ubif erlang:is_binary/1
-ubif 'erl.lang.term':is_binary/1 ebif_is_binary_1
ubif erlang:is_function/1
-ubif 'erl.lang.term':is_function/1 ebif_is_function_1
ubif erlang:is_function/2
-ubif 'erl.lang.term':is_function/2 ebif_is_function_2
ubif erlang:is_record/2
-ubif 'erl.lang.term':is_record/2 ebif_is_record_2
ubif erlang:is_record/3
-ubif 'erl.lang.term':is_record/3 ebif_is_record_3
bif erlang:match_spec_test/3
@@ -511,96 +295,53 @@ bif erlang:match_spec_test/3
#
bif ets:all/0
-bif 'erl.lang.ets':all/0 ebif_ets_all_0
bif ets:new/2
-bif 'erl.lang.ets':new/2 ebif_ets_new_2
bif ets:delete/1
-bif 'erl.lang.ets':delete/1 ebif_ets_delete_1
bif ets:delete/2
-bif 'erl.lang.ets':delete/2 ebif_ets_delete_2
bif ets:delete_all_objects/1
-bif 'erl.lang.ets':delete_all_objects/1 ebif_ets_delete_all_objects_1
bif ets:delete_object/2
-bif 'erl.lang.ets':delete_object/2 ebif_ets_delete_object_2
bif ets:first/1
-bif 'erl.lang.ets':first/1 ebif_ets_first_1
bif ets:is_compiled_ms/1
-bif 'erl.lang.ets':is_compiled_ms/1 ebif_ets_is_compiled_ms_1
bif ets:lookup/2
-bif 'erl.lang.ets':lookup/2 ebif_ets_lookup_2
bif ets:lookup_element/3
-bif 'erl.lang.ets':lookup_element/3 ebif_ets_lookup_element_3
bif ets:info/1
-bif 'erl.lang.ets':info/1 ebif_ets_info_1
bif ets:info/2
-bif 'erl.lang.ets':info/2 ebif_ets_info_2
bif ets:last/1
-bif 'erl.lang.ets':last/1 ebif_ets_last_1
bif ets:match/1
-bif 'erl.lang.ets':match/1 ebif_ets_match_1
bif ets:match/2
-bif 'erl.lang.ets':match/2 ebif_ets_match_2
bif ets:match/3
-bif 'erl.lang.ets':match/3 ebif_ets_match_3
bif ets:match_object/1
-bif 'erl.lang.ets':match_object/1 ebif_ets_match_object_1
bif ets:match_object/2
-bif 'erl.lang.ets':match_object/2 ebif_ets_match_object_2
bif ets:match_object/3
-bif 'erl.lang.ets':match_object/3 ebif_ets_match_object_3
bif ets:member/2
-bif 'erl.lang.ets':is_key/2 ebif_ets_member_2
bif ets:next/2
-bif 'erl.lang.ets':next/2 ebif_ets_next_2
bif ets:prev/2
-bif 'erl.lang.ets':prev/2 ebif_ets_prev_2
bif ets:insert/2
-bif 'erl.lang.ets':insert/2 ebif_ets_insert_2
bif ets:insert_new/2
-bif 'erl.lang.ets':insert_new/2 ebif_ets_insert_new_2
bif ets:rename/2
-bif 'erl.lang.ets':rename/2 ebif_ets_rename_2
bif ets:safe_fixtable/2
-bif 'erl.lang.ets':fixtable/2 ebif_ets_safe_fixtable_2
bif ets:slot/2
-bif 'erl.lang.ets':slot/2 ebif_ets_slot_2
bif ets:update_counter/3
-bif 'erl.lang.ets':update_counter/3 ebif_ets_update_counter_3
bif ets:select/1
-bif 'erl.lang.ets':select/1 ebif_ets_select_1
bif ets:select/2
-bif 'erl.lang.ets':select/2 ebif_ets_select_2
bif ets:select/3
-bif 'erl.lang.ets':select/3 ebif_ets_select_3
bif ets:select_count/2
-bif 'erl.lang.ets':select/2 ebif_ets_select_count_2
bif ets:select_reverse/1
-bif 'erl.lang.ets':select_reverse/1 ebif_ets_select_reverse_1
bif ets:select_reverse/2
-bif 'erl.lang.ets':select_reverse/2 ebif_ets_select_reverse_2
bif ets:select_reverse/3
-bif 'erl.lang.ets':select_reverse/3 ebif_ets_select_reverse_3
bif ets:select_delete/2
-bif 'erl.lang.ets':select_delete/2 ebif_ets_select_delete_2
bif ets:match_spec_compile/1
-bif 'erl.lang.ets':match_spec_compile/1 ebif_ets_match_spec_compile_1
bif ets:match_spec_run_r/3
-bif 'erl.lang.ets':match_spec_run_r/3 ebif_ets_match_spec_run_r_3
#
# Bifs in os module.
#
bif os:putenv/2
-bif 'erl.system.os':setenv/2 ebif_os_setenv_2 os_putenv_2
bif os:getenv/0
-bif 'erl.system.os':getenv/0 ebif_os_getenv_0
bif os:getenv/1
-bif 'erl.system.os':getenv/1 ebif_os_getenv_1
bif os:getpid/0
-bif 'erl.system.os':pid/0 ebif_os_pid_0 os_getpid_0
bif os:timestamp/0
-bif 'erl.system.os':timestamp/0 ebif_os_timestamp_0 os_timestamp_0
#
# Bifs in the erl_ddll module (the module actually does not exist)
@@ -627,13 +368,9 @@ bif re:run/3
#
bif lists:member/2
-bif 'erl.lang.list':is_element/2 ebif_list_is_element_2 lists_member_2
bif lists:reverse/2
-bif 'erl.lang.list':reverse/2 ebif_list_reverse_2 lists_reverse_2
bif lists:keymember/3
-bif 'erl.lang.list.keylist':is_element/3 ebif_keylist_is_element_3 lists_keymember_3
bif lists:keysearch/3
-bif 'erl.lang.list.keylist':search/3 ebif_keylist_search_3 lists_keysearch_3
bif lists:keyfind/3
#
@@ -641,21 +378,13 @@ bif lists:keyfind/3
#
bif erts_debug:disassemble/1
-bif 'erl.system.debug':disassemble/1 ebif_erts_debug_disassemble_1
bif erts_debug:breakpoint/2
-bif 'erl.system.debug':breakpoint/2 ebif_erts_debug_breakpoint_2
bif erts_debug:same/2
-bif 'erl.system.debug':same/2 ebif_erts_debug_same_2
bif erts_debug:flat_size/1
-bif 'erl.system.debug':flat_size/1 ebif_erts_debug_flat_size_1
bif erts_debug:get_internal_state/1
-bif 'erl.system.debug':get_internal_state/1 ebif_erts_debug_get_internal_state_1
bif erts_debug:set_internal_state/2
-bif 'erl.system.debug':set_internal_state/2 ebif_erts_debug_set_internal_state_2
bif erts_debug:display/1
-bif 'erl.system.debug':display/1 ebif_erts_debug_display_1
bif erts_debug:dist_ext_to_term/2
-bif 'erl.system.debug':dist_ext_to_term/2 ebif_erts_debug_dist_ext_to_term_2
bif erts_debug:instructions/0
#
@@ -675,13 +404,9 @@ bif erts_debug:lock_counters/1
#
bif code:get_chunk/2
-bif 'erl.system.code':get_chunk/2 ebif_code_get_chunk_2
bif code:module_md5/1
-bif 'erl.system.code':module_md5/1 ebif_code_module_md5_1
bif code:make_stub_module/3
-bif 'erl.system.code':make_stub_module/3 ebif_code_make_stub_module_3
bif code:is_module_native/1
-bif 'erl.system.code':is_native/1 ebif_code_is_native_1 code_is_module_native_1
#
# New Bifs in R9C.
@@ -833,6 +558,8 @@ bif erlang:dt_append_vm_tag_data/1
#
bif erlang:prepare_loading/2
bif erlang:finish_loading/1
+bif erlang:insert_element/3
+bif erlang:delete_element/2
#
# Obsolete
diff --git a/erts/emulator/beam/binary.c b/erts/emulator/beam/binary.c
index 3d2725e239..4441dab181 100644
--- a/erts/emulator/beam/binary.c
+++ b/erts/emulator/beam/binary.c
@@ -355,10 +355,10 @@ BIF_RETTYPE bitstring_to_list_1(BIF_ALIST_1)
BIF_RETTYPE erts_list_to_binary_bif(Process *p, Eterm arg)
{
Eterm bin;
- Uint size;
+ ErlDrvSizeT size;
byte* bytes;
#ifdef DEBUG
- int offset;
+ ErlDrvSizeT offset;
#endif
if (is_nil(arg)) {
@@ -377,7 +377,7 @@ BIF_RETTYPE erts_list_to_binary_bif(Process *p, Eterm arg)
#ifdef DEBUG
offset =
#endif
- io_list_to_buf(arg, (char*) bytes, size);
+ erts_iolist_to_buf(arg, (char*) bytes, size);
ASSERT(offset == 0);
BIF_RET(bin);
diff --git a/erts/emulator/beam/break.c b/erts/emulator/beam/break.c
index 0a51e00016..9aa1e5f30d 100644
--- a/erts/emulator/beam/break.c
+++ b/erts/emulator/beam/break.c
@@ -61,16 +61,19 @@ extern char* erts_system_version[];
static void
port_info(int to, void *to_arg)
{
- int i;
- for (i = 0; i < erts_max_ports; i++)
- print_port_info(to, to_arg, i);
+ int i, max = erts_ptab_max(&erts_port);
+ for (i = 0; i < max; i++) {
+ Port *p = erts_pix2port(i);
+ if (p)
+ print_port_info(p, to, to_arg);
+ }
}
void
process_info(int to, void *to_arg)
{
- int i;
- for (i = 0; i < erts_max_processes; i++) {
+ int i, max = erts_ptab_max(&erts_proc);
+ for (i = 0; i < max; i++) {
Process *p = erts_pix2proc(i);
if (p && p->i != ENULL) {
if (!ERTS_PROC_IS_EXITING(p))
@@ -84,12 +87,12 @@ process_info(int to, void *to_arg)
static void
process_killer(void)
{
- int i, j;
+ int i, j, max = erts_ptab_max(&erts_proc);
Process* rp;
erts_printf("\n\nProcess Information\n\n");
erts_printf("--------------------------------------------------\n");
- for (i = erts_max_processes-1; i >= 0; i--) {
+ for (i = max-1; i >= 0; i--) {
rp = erts_pix2proc(i);
if (rp && rp->i != ENULL) {
int br;
@@ -196,7 +199,7 @@ print_process_info(int to, void *to_arg, Process *p)
erts_aint32_t state;
/* display the PID */
- erts_print(to, to_arg, "=proc:%T\n", p->id);
+ erts_print(to, to_arg, "=proc:%T\n", p->common.id);
/* Display the state */
erts_print(to, to_arg, "State: ");
@@ -226,8 +229,8 @@ print_process_info(int to, void *to_arg, Process *p)
* If the process is registered as a global process, display the
* registered name
*/
- if (p->reg != NULL)
- erts_print(to, to_arg, "Name: %T\n", p->reg->name);
+ if (p->common.u.alive.reg)
+ erts_print(to, to_arg, "Name: %T\n", p->common.u.alive.reg->name);
/*
* Display the initial function name
@@ -301,11 +304,11 @@ print_process_info(int to, void *to_arg, Process *p)
}
/* display the links only if there are any*/
- if (p->nlinks != NULL || p->monitors != NULL) {
+ if (ERTS_P_LINKS(p) || ERTS_P_MONITORS(p)) {
PrintMonitorContext context = {1,to};
erts_print(to, to_arg,"Link list: [");
- erts_doforall_links(p->nlinks, &doit_print_link, &context);
- erts_doforall_monitors(p->monitors, &doit_print_monitor, &context);
+ erts_doforall_links(ERTS_P_LINKS(p), &doit_print_link, &context);
+ erts_doforall_monitors(ERTS_P_MONITORS(p), &doit_print_monitor, &context);
erts_print(to, to_arg,"]\n");
}
@@ -625,9 +628,9 @@ bin_check(void)
{
Process *rp;
struct erl_off_heap_header* hdr;
- int i, printed = 0;
+ int i, printed = 0, max = erts_ptab_max(&erts_proc);
- for (i=0; i < erts_max_processes; i++) {
+ for (i=0; i < max; i++) {
rp = erts_pix2proc(i);
if (!rp)
continue;
@@ -635,7 +638,7 @@ bin_check(void)
if (hdr->thing_word == HEADER_PROC_BIN) {
ProcBin *bp = (ProcBin*) hdr;
if (!printed) {
- erts_printf("Process %T holding binary data \n", rp->id);
+ erts_printf("Process %T holding binary data \n", rp->common.id);
printed = 1;
}
erts_printf("%p orig_size: %bpd, norefs = %bpd\n",
@@ -663,10 +666,14 @@ erl_crash_dump_v(char *file, int line, char* fmt, va_list args)
ErtsThrPrgrData tpd_buf; /* in case we aren't a managed thread... */
#endif
int fd;
+ size_t envsz;
time_t now;
+ char env[21]; /* enough to hold any 64-bit integer */
size_t dumpnamebufsize = MAXPATHLEN;
char dumpnamebuf[MAXPATHLEN];
char* dumpname;
+ int secs;
+ int env_erl_crash_dump_seconds_set = 1;
if (ERTS_SOMEONE_IS_CRASH_DUMPING)
return;
@@ -689,9 +696,54 @@ erl_crash_dump_v(char *file, int line, char* fmt, va_list args)
erts_writing_erl_crash_dump = 1;
#endif
- erts_sys_prepare_crash_dump();
+ envsz = sizeof(env);
+ /* ERL_CRASH_DUMP_SECONDS not set
+ * if we have a heart port, break immediately
+ * otherwise dump crash indefinitely (until crash is complete)
+ * same as ERL_CRASH_DUMP_SECONDS = 0
+ * - do not write dump
+ * - do not set an alarm
+ * - break immediately
+ *
+ * ERL_CRASH_DUMP_SECONDS = 0
+ * - do not write dump
+ * - do not set an alarm
+ * - break immediately
+ *
+ * ERL_CRASH_DUMP_SECONDS < 0
+ * - do not set alarm
+ * - write dump until done
+ *
+ * ERL_CRASH_DUMP_SECONDS = S (and S positive)
+ * - Don't dump file forever
+ * - set alarm (set in sys)
+ * - write dump until alarm or file is written completely
+ */
+
+ if (erts_sys_getenv__("ERL_CRASH_DUMP_SECONDS", env, &envsz) != 0) {
+ env_erl_crash_dump_seconds_set = 0;
+ secs = -1;
+ } else {
+ env_erl_crash_dump_seconds_set = 1;
+ secs = atoi(env);
+ }
+
+ if (secs == 0) {
+ return;
+ }
+
+ /* erts_sys_prepare_crash_dump returns 1 if heart port is found, otherwise 0
+ * If we don't find heart (0) and we don't have ERL_CRASH_DUMP_SECONDS set
+ * we should continue writing a dump
+ *
+ * beware: secs -1 means no alarm
+ */
+
+ if (erts_sys_prepare_crash_dump(secs) && !env_erl_crash_dump_seconds_set ) {
+ return;
+ }
- if (erts_sys_getenv("ERL_CRASH_DUMP",&dumpnamebuf[0],&dumpnamebufsize) != 0)
+ if (erts_sys_getenv__("ERL_CRASH_DUMP",&dumpnamebuf[0],&dumpnamebufsize) != 0)
dumpname = "erl_crash.dump";
else
dumpname = &dumpnamebuf[0];
@@ -717,7 +769,7 @@ erl_crash_dump_v(char *file, int line, char* fmt, va_list args)
erts_print_nif_taints(fd, NULL);
erts_fdprintf(fd, "Atoms: %d\n", atom_table_size());
info(fd, NULL); /* General system info */
- if (erts_proc.tab)
+ if (erts_ptab_initialized(&erts_proc))
process_info(fd, NULL); /* Info about each process and port */
db_info(fd, NULL, 0);
erts_print_bif_timer_info(fd, NULL);
diff --git a/erts/emulator/beam/code_ix.c b/erts/emulator/beam/code_ix.c
index 8025058ee0..c66d5a2f05 100644
--- a/erts/emulator/beam/code_ix.c
+++ b/erts/emulator/beam/code_ix.c
@@ -36,13 +36,13 @@
erts_smp_atomic32_t the_active_code_index;
erts_smp_atomic32_t the_staging_code_index;
-static int the_code_ix_lock = 0;
-struct code_ix_queue_item {
+static Process* code_writing_process = NULL;
+struct code_write_queue_item {
Process *p;
- struct code_ix_queue_item* next;
+ struct code_write_queue_item* next;
};
-static struct code_ix_queue_item* the_code_ix_queue = NULL;
-static erts_smp_mtx_t the_code_ix_queue_lock;
+static struct code_write_queue_item* code_write_queue = NULL;
+static erts_smp_mtx_t code_write_permission_mtx;
#ifdef ERTS_ENABLE_LOCK_CHECK
static erts_tsd_key_t has_code_write_permission;
@@ -56,7 +56,7 @@ void erts_code_ix_init(void)
*/
erts_smp_atomic32_init_nob(&the_active_code_index, 0);
erts_smp_atomic32_init_nob(&the_staging_code_index, 0);
- erts_smp_mtx_init(&the_code_ix_queue_lock, "code_ix_queue");
+ erts_smp_mtx_init(&code_write_permission_mtx, "code_write_permission");
#ifdef ERTS_ENABLE_LOCK_CHECK
erts_tsd_key_create(&has_code_write_permission);
#endif
@@ -114,53 +114,55 @@ int erts_try_seize_code_write_permission(Process* c_p)
#ifdef ERTS_SMP
ASSERT(!erts_smp_thr_progress_is_blocking()); /* to avoid deadlock */
#endif
+ ASSERT(c_p != NULL);
- erts_smp_mtx_lock(&the_code_ix_queue_lock);
- success = !the_code_ix_lock;
+ erts_smp_mtx_lock(&code_write_permission_mtx);
+ success = (code_writing_process == NULL);
if (success) {
- the_code_ix_lock = 1;
+ code_writing_process = c_p;
#ifdef ERTS_ENABLE_LOCK_CHECK
erts_tsd_set(has_code_write_permission, (void *) 1);
#endif
}
else { /* Already locked */
- struct code_ix_queue_item* qitem;
+ struct code_write_queue_item* qitem;
+ ASSERT(code_writing_process != c_p);
qitem = erts_alloc(ERTS_ALC_T_CODE_IX_LOCK_Q, sizeof(*qitem));
qitem->p = c_p;
erts_smp_proc_inc_refc(c_p);
- qitem->next = the_code_ix_queue;
- the_code_ix_queue = qitem;
+ qitem->next = code_write_queue;
+ code_write_queue = qitem;
erts_suspend(c_p, ERTS_PROC_LOCK_MAIN, NULL);
}
- erts_smp_mtx_unlock(&the_code_ix_queue_lock);
+ erts_smp_mtx_unlock(&code_write_permission_mtx);
return success;
}
void erts_release_code_write_permission(void)
{
- ERTS_SMP_LC_ASSERT(erts_is_code_ix_locked());
- erts_smp_mtx_lock(&the_code_ix_queue_lock);
- while (the_code_ix_queue != NULL) { /* unleash the entire herd */
- struct code_ix_queue_item* qitem = the_code_ix_queue;
+ erts_smp_mtx_lock(&code_write_permission_mtx);
+ ERTS_SMP_LC_ASSERT(erts_has_code_write_permission());
+ while (code_write_queue != NULL) { /* unleash the entire herd */
+ struct code_write_queue_item* qitem = code_write_queue;
erts_smp_proc_lock(qitem->p, ERTS_PROC_LOCK_STATUS);
if (!ERTS_PROC_IS_EXITING(qitem->p)) {
erts_resume(qitem->p, ERTS_PROC_LOCK_STATUS);
}
erts_smp_proc_unlock(qitem->p, ERTS_PROC_LOCK_STATUS);
- the_code_ix_queue = qitem->next;
+ code_write_queue = qitem->next;
erts_smp_proc_dec_refc(qitem->p);
erts_free(ERTS_ALC_T_CODE_IX_LOCK_Q, qitem);
}
- the_code_ix_lock = 0;
+ code_writing_process = NULL;
#ifdef ERTS_ENABLE_LOCK_CHECK
erts_tsd_set(has_code_write_permission, (void *) 0);
#endif
- erts_smp_mtx_unlock(&the_code_ix_queue_lock);
+ erts_smp_mtx_unlock(&code_write_permission_mtx);
}
#ifdef ERTS_ENABLE_LOCK_CHECK
-int erts_is_code_ix_locked(void)
+int erts_has_code_write_permission(void)
{
- return the_code_ix_lock && erts_tsd_get(has_code_write_permission);
+ return (code_writing_process != NULL) && erts_tsd_get(has_code_write_permission);
}
#endif
diff --git a/erts/emulator/beam/code_ix.h b/erts/emulator/beam/code_ix.h
index 6b2680044e..e2bd0da112 100644
--- a/erts/emulator/beam/code_ix.h
+++ b/erts/emulator/beam/code_ix.h
@@ -18,7 +18,7 @@
*/
/* Description:
- * This is the interface that facilitate changing the beam code
+ * This is the interface that facilitates changing the beam code
* (load,upgrade,delete) while allowing executing Erlang processes to
* access the code without any locks or other expensive memory barriers.
*
@@ -35,7 +35,7 @@
* The third code index is not explicitly used. It can be thought of as
* the "previous active" or the "next staging" index. It is needed to make
* sure that we do not reuse a code index for staging until we are sure
- * that no executing BIFs are still referring it.
+ * that no executing BIFs are still referencing it.
* We could get by with only two (0 and 1), but that would require that we
* must wait for all schedulers to re-schedule before each code change
* operation can start staging.
@@ -116,7 +116,7 @@ void erts_commit_staging_code_ix(void);
void erts_abort_staging_code_ix(void);
#ifdef ERTS_ENABLE_LOCK_CHECK
-int erts_is_code_ix_locked(void);
+int erts_has_code_write_permission(void);
#endif
diff --git a/erts/emulator/beam/copy.c b/erts/emulator/beam/copy.c
index 36eda04de2..23c0fca6aa 100644
--- a/erts/emulator/beam/copy.c
+++ b/erts/emulator/beam/copy.c
@@ -47,7 +47,7 @@ copy_object(Eterm obj, Process* to)
if (DTRACE_ENABLED(copy_object)) {
DTRACE_CHARBUF(proc_name, 64);
- erts_snprintf(proc_name, sizeof(proc_name), "%T", to->id);
+ erts_snprintf(proc_name, sizeof(proc_name), "%T", to->common.id);
DTRACE2(copy_object, proc_name, size);
}
#endif
diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c
index 5db68f6d45..145e6861f6 100644
--- a/erts/emulator/beam/dist.c
+++ b/erts/emulator/beam/dist.c
@@ -67,7 +67,7 @@ dist_msg_dbg(ErtsDistExternal *edep, char *what, byte *buf, int sz)
{
byte *extp = edep->extp;
Eterm msg;
- Sint size = erts_decode_dist_ext_size(edep, 0);
+ Sint size = erts_decode_dist_ext_size(edep);
if (size < 0) {
erts_fprintf(stderr,
"DIST MSG DEBUG: erts_decode_dist_ext_size(%s) failed:\n",
@@ -124,6 +124,13 @@ static void send_nodes_mon_msgs(Process *, Eterm, Eterm, Eterm, Eterm);
static void init_nodes_monitors(void);
static erts_smp_atomic_t no_caches;
+static erts_smp_atomic_t no_nodes;
+
+struct {
+ Eterm reason;
+ ErlHeapFragment *bp;
+} nodedown;
+
static void
delete_cache(ErtsAtomCache *cache)
@@ -144,7 +151,7 @@ create_cache(DistEntry *dep)
ERTS_SMP_LC_ASSERT(
is_internal_port(dep->cid)
- && erts_lc_is_port_locked(&erts_port[internal_port_index(dep->cid)]));
+ && erts_lc_is_port_locked(erts_port_lookup_raw(dep->cid)));
ASSERT(!dep->cache);
dep->cache = cp = (ErtsAtomCache*) erts_alloc(ERTS_ALC_T_DCACHE,
@@ -171,11 +178,10 @@ get_suspended_on_de(DistEntry *dep, Uint32 unset_qflgs)
return NULL;
}
else {
- ErtsProcList *plp;
- plp = dep->suspended.first;
- dep->suspended.first = NULL;
- dep->suspended.last = NULL;
- return plp;
+ ErtsProcList *suspended = dep->suspended;
+ dep->suspended = NULL;
+ erts_proclist_fetch(&suspended, NULL);
+ return suspended;
}
}
@@ -252,7 +258,7 @@ static void doit_monitor_net_exits(ErtsMonitor *mon, void *vnecp)
if (mon->type == MON_ORIGIN) {
/* local pid is beeing monitored */
- rmon = erts_remove_monitor(&(rp->monitors),mon->ref);
+ rmon = erts_remove_monitor(&ERTS_P_MONITORS(rp), mon->ref);
/* ASSERT(rmon != NULL); nope, can happen during process exit */
if (rmon != NULL) {
erts_destroy_monitor(rmon);
@@ -262,7 +268,7 @@ static void doit_monitor_net_exits(ErtsMonitor *mon, void *vnecp)
Eterm watched;
UseTmpHeapNoproc(3);
ASSERT(mon->type == MON_TARGET);
- rmon = erts_remove_monitor(&(rp->monitors),mon->ref);
+ rmon = erts_remove_monitor(&ERTS_P_MONITORS(rp), mon->ref);
/* ASSERT(rmon != NULL); can happen during process exit */
if (rmon != NULL) {
ASSERT(is_atom(rmon->name) || is_nil(rmon->name));
@@ -311,7 +317,7 @@ static void doit_link_net_exits_sub(ErtsLink *sublnk, void *vlnecp)
goto done;
}
- rlnk = erts_remove_link(&(rp->nlinks), sublnk->pid);
+ rlnk = erts_remove_link(&ERTS_P_LINKS(rp), sublnk->pid);
xres = erts_send_exit_signal(NULL,
sublnk->pid,
rp,
@@ -370,7 +376,7 @@ static void doit_node_link_net_exits(ErtsLink *lnk, void *vnecp)
if (!rp) {
goto done;
}
- rlnk = erts_remove_link(&(rp->nlinks), name);
+ rlnk = erts_remove_link(&ERTS_P_LINKS(rp), name);
if (rlnk != NULL) {
ASSERT(is_atom(rlnk->pid) && (rlnk->type == LINK_NODE));
erts_destroy_link(rlnk);
@@ -394,6 +400,47 @@ static void doit_node_link_net_exits(ErtsLink *lnk, void *vnecp)
erts_destroy_link(lnk);
}
+static void
+set_node_not_alive(void *unused)
+{
+ ErlHeapFragment *bp;
+ Eterm nodename = erts_this_dist_entry->sysname;
+
+ ASSERT(erts_smp_atomic_read_nob(&no_nodes) == 0);
+
+ erts_smp_thr_progress_block();
+ erts_set_this_node(am_Noname, 0);
+ erts_is_alive = 0;
+ send_nodes_mon_msgs(NULL, am_nodedown, nodename, am_visible, nodedown.reason);
+ nodedown.reason = NIL;
+ bp = nodedown.bp;
+ nodedown.bp = NULL;
+ erts_smp_thr_progress_unblock();
+ if (bp)
+ free_message_buffer(bp);
+}
+
+static ERTS_INLINE void
+dec_no_nodes(void)
+{
+ erts_aint_t no = erts_smp_atomic_dec_read_mb(&no_nodes);
+ ASSERT(no >= 0);
+ ASSERT(erts_get_scheduler_id()); /* Need to be a scheduler */
+ if (no == 0)
+ erts_schedule_misc_aux_work(erts_get_scheduler_id(),
+ set_node_not_alive,
+ NULL);
+}
+
+static ERTS_INLINE void
+inc_no_nodes(void)
+{
+#ifdef DEBUG
+ erts_aint_t no = erts_smp_atomic_read_nob(&no_nodes);
+ ASSERT(erts_is_alive ? no > 0 : no == 0);
+#endif
+ erts_smp_atomic_inc_mb(&no_nodes);
+}
/*
* proc is currently running or exiting process.
@@ -403,47 +450,76 @@ int erts_do_net_exits(DistEntry *dep, Eterm reason)
Eterm nodename;
if (dep == erts_this_dist_entry) { /* Net kernel has died (clean up!!) */
+ DistEntry *tdep;
+ int no_dist_port = 0;
Eterm nd_reason = (reason == am_no_network
? am_no_network
: am_net_kernel_terminated);
erts_smp_rwmtx_rwlock(&erts_dist_table_rwmtx);
+ for (tdep = erts_hidden_dist_entries; tdep; tdep = tdep->next)
+ no_dist_port++;
+ for (tdep = erts_visible_dist_entries; tdep; tdep = tdep->next)
+ no_dist_port++;
+
/* KILL all port controllers */
- while(erts_visible_dist_entries || erts_hidden_dist_entries) {
- DistEntry *tdep;
- Eterm prt_id;
- Port *prt;
- if(erts_hidden_dist_entries)
- tdep = erts_hidden_dist_entries;
- else
- tdep = erts_visible_dist_entries;
- prt_id = tdep->cid;
- ASSERT(is_internal_port(prt_id));
+ if (no_dist_port == 0)
erts_smp_rwmtx_rwunlock(&erts_dist_table_rwmtx);
+ else {
+ Eterm def_buf[128];
+ int i = 0;
+ Eterm *dist_port;
- prt = erts_id2port(prt_id, NULL, 0);
- if (prt) {
- ASSERT(prt->status & ERTS_PORT_SFLG_DISTRIBUTION);
- ASSERT(prt->dist_entry);
- /* will call do_net_exists !!! */
- erts_do_exit_port(prt, prt_id, nd_reason);
- erts_port_release(prt);
+ if (no_dist_port <= sizeof(def_buf)/sizeof(def_buf[0]))
+ dist_port = &def_buf[0];
+ else
+ dist_port = erts_alloc(ERTS_ALC_T_TMP,
+ sizeof(Eterm)*no_dist_port);
+ for (tdep = erts_hidden_dist_entries; tdep; tdep = tdep->next) {
+ ASSERT(is_internal_port(tdep->cid));
+ dist_port[i++] = tdep->cid;
+ }
+ for (tdep = erts_visible_dist_entries; tdep; tdep = tdep->next) {
+ ASSERT(is_internal_port(tdep->cid));
+ dist_port[i++] = tdep->cid;
}
+ erts_smp_rwmtx_rwunlock(&erts_dist_table_rwmtx);
- erts_smp_rwmtx_rwlock(&erts_dist_table_rwmtx);
- }
+ for (i = 0; i < no_dist_port; i++) {
+ Port *prt = erts_port_lookup(dist_port[i],
+ ERTS_PORT_SFLGS_INVALID_LOOKUP);
+ if (!prt)
+ continue;
+ ASSERT(erts_atomic32_read_nob(&prt->state)
+ & ERTS_PORT_SFLG_DISTRIBUTION);
- erts_smp_rwmtx_rwunlock(&erts_dist_table_rwmtx);
+ erts_port_exit(NULL, ERTS_PORT_SIG_FLG_FORCE_SCHED,
+ prt, dist_port[i], nd_reason, NULL);
+ }
- nodename = erts_this_dist_entry->sysname;
- erts_smp_thr_progress_block();
- erts_set_this_node(am_Noname, 0);
- erts_is_alive = 0;
- send_nodes_mon_msgs(NULL, am_nodedown, nodename, am_visible, nd_reason);
- erts_smp_thr_progress_unblock();
+ if (dist_port != &def_buf[0])
+ erts_free(ERTS_ALC_T_TMP, dist_port);
+ }
+ /*
+ * When last dist port exits, node will be taken
+ * from alive to not alive.
+ */
+ ASSERT(is_nil(nodedown.reason) && !nodedown.bp);
+ if (is_immed(nd_reason))
+ nodedown.reason = nd_reason;
+ else {
+ Eterm *hp;
+ Uint sz = size_object(nd_reason);
+ nodedown.bp = new_message_buffer(sz);
+ hp = nodedown.bp->mem;
+ nodedown.reason = copy_struct(nd_reason,
+ sz,
+ &hp,
+ &nodedown.bp->off_heap);
+ }
}
- else { /* recursive call via erts_do_exit_port() will end up here */
+ else { /* Call from distribution port */
NetExitsContext nec = {dep};
ErtsLink *nlinks;
ErtsLink *node_links;
@@ -454,10 +530,10 @@ int erts_do_net_exits(DistEntry *dep, Eterm reason)
erts_smp_de_rwlock(dep);
ERTS_SMP_LC_ASSERT(is_internal_port(dep->cid)
- && erts_lc_is_port_locked(&erts_port[internal_port_index(dep->cid)]));
+ && erts_lc_is_port_locked(erts_port_lookup_raw(dep->cid)));
if (erts_port_task_is_scheduled(&dep->dist_cmd))
- erts_port_task_abort(dep->cid, &dep->dist_cmd);
+ erts_port_task_abort(&dep->dist_cmd);
if (dep->status & ERTS_DE_SFLG_EXITING) {
#ifdef DEBUG
@@ -503,6 +579,9 @@ int erts_do_net_exits(DistEntry *dep, Eterm reason)
clear_dist_entry(dep);
}
+
+ dec_no_nodes();
+
return 1;
}
@@ -516,6 +595,10 @@ void init_dist(void)
{
init_nodes_monitors();
+ nodedown.reason = NIL;
+ nodedown.bp = NULL;
+
+ erts_smp_atomic_init_nob(&no_nodes, 0);
erts_smp_atomic_init_nob(&no_caches, 0);
/* Lookup/Install all references to trap functions */
@@ -769,7 +852,7 @@ erts_dsig_send_msg(ErtsDSigData *dsdp, Eterm remote, Eterm message)
*node_name = *sender_name = *receiver_name = '\0';
if (DTRACE_ENABLED(message_send) || DTRACE_ENABLED(message_send_remote)) {
erts_snprintf(node_name, sizeof(node_name), "%T", dsdp->dep->sysname);
- erts_snprintf(sender_name, sizeof(sender_name), "%T", sender->id);
+ erts_snprintf(sender_name, sizeof(sender_name), "%T", sender->common.id);
erts_snprintf(receiver_name, sizeof(receiver_name), "%T", remote);
msize = size_object(message);
if (token != NIL && token != am_have_dt_utag) {
@@ -826,7 +909,7 @@ erts_dsig_send_reg_msg(ErtsDSigData *dsdp, Eterm remote_name, Eterm message)
*node_name = *sender_name = *receiver_name = '\0';
if (DTRACE_ENABLED(message_send) || DTRACE_ENABLED(message_send_remote)) {
erts_snprintf(node_name, sizeof(node_name), "%T", dsdp->dep->sysname);
- erts_snprintf(sender_name, sizeof(sender_name), "%T", sender->id);
+ erts_snprintf(sender_name, sizeof(sender_name), "%T", sender->common.id);
erts_snprintf(receiver_name, sizeof(receiver_name),
"{%T,%s}", remote_name, node_name);
msize = size_object(message);
@@ -840,10 +923,10 @@ erts_dsig_send_reg_msg(ErtsDSigData *dsdp, Eterm remote_name, Eterm message)
if (token != NIL)
ctl = TUPLE5(&ctl_heap[0], make_small(DOP_REG_SEND_TT),
- sender->id, am_Cookie, remote_name, token);
+ sender->common.id, am_Cookie, remote_name, token);
else
ctl = TUPLE4(&ctl_heap[0], make_small(DOP_REG_SEND),
- sender->id, am_Cookie, remote_name);
+ sender->common.id, am_Cookie, remote_name);
DTRACE6(message_send, sender_name, receiver_name,
msize, tok_label, tok_lastcnt, tok_serial);
DTRACE7(message_send_remote, sender_name, node_name, receiver_name,
@@ -889,7 +972,7 @@ erts_dsig_send_exit_tt(ErtsDSigData *dsdp, Eterm local, Eterm remote,
*node_name = *sender_name = *remote_name = '\0';
if (DTRACE_ENABLED(process_exit_signal_remote)) {
erts_snprintf(node_name, sizeof(node_name), "%T", dsdp->dep->sysname);
- erts_snprintf(sender_name, sizeof(sender_name), "%T", sender->id);
+ erts_snprintf(sender_name, sizeof(sender_name), "%T", sender->common.id);
erts_snprintf(remote_name, sizeof(remote_name),
"{%T,%s}", remote, node_name);
erts_snprintf(reason_str, sizeof(reason), "%T", reason);
@@ -968,16 +1051,16 @@ erts_dsig_send_group_leader(ErtsDSigData *dsdp, Eterm leader, Eterm remote)
#define VALGRIND_PRINTF_XML VALGRIND_PRINTF
#endif
-# define PURIFY_MSG(msg) \
- do { \
- char buf__[1]; size_t bufsz__ = sizeof(buf__); \
- if (erts_sys_getenv("VALGRIND_LOG_XML", buf__, &bufsz__) >= 0) { \
- VALGRIND_PRINTF_XML("<erlang_error_log>" \
- "%s, line %d: %s</erlang_error_log>\n", \
- __FILE__, __LINE__, msg); \
- } else { \
- VALGRIND_PRINTF("%s, line %d: %s", __FILE__, __LINE__, msg); \
- } \
+# define PURIFY_MSG(msg) \
+ do { \
+ char buf__[1]; size_t bufsz__ = sizeof(buf__); \
+ if (erts_sys_getenv_raw("VALGRIND_LOG_XML", buf__, &bufsz__) >= 0) { \
+ VALGRIND_PRINTF_XML("<erlang_error_log>" \
+ "%s, line %d: %s</erlang_error_log>\n", \
+ __FILE__, __LINE__, msg); \
+ } else { \
+ VALGRIND_PRINTF("%s, line %d: %s", __FILE__, __LINE__, msg); \
+ } \
} while (0)
#else
# define PURIFY_MSG(msg)
@@ -1141,7 +1224,7 @@ int erts_net_message(Port *prt,
}
erts_smp_de_links_lock(dep);
- res = erts_add_link(&(rp->nlinks), LINK_PID, from);
+ res = erts_add_link(&ERTS_P_LINKS(rp), LINK_PID, from);
if (res < 0) {
/* It was already there! Lets skip the rest... */
@@ -1149,7 +1232,7 @@ int erts_net_message(Port *prt,
erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
break;
}
- lnk = erts_add_or_lookup_link(&(dep->nlinks), LINK_PID, rp->id);
+ lnk = erts_add_or_lookup_link(&(dep->nlinks), LINK_PID, rp->common.id);
erts_add_link(&(ERTS_LINK_ROOT(lnk)), LINK_PID, from);
erts_smp_de_links_unlock(dep);
@@ -1176,7 +1259,7 @@ int erts_net_message(Port *prt,
if (!rp)
break;
- lnk = erts_remove_link(&(rp->nlinks), from);
+ lnk = erts_remove_link(&ERTS_P_LINKS(rp), from);
if (IS_TRACED_FL(rp, F_TRACE_PROCS) && lnk != NULL) {
trace_proc(NULL, rp, am_getting_unlinked, from);
@@ -1233,10 +1316,10 @@ int erts_net_message(Port *prt,
}
else {
if (is_atom(watched))
- watched = rp->id;
+ watched = rp->common.id;
erts_smp_de_links_lock(dep);
erts_add_monitor(&(dep->monitors), MON_ORIGIN, ref, watched, name);
- erts_add_monitor(&(rp->monitors), MON_TARGET, ref, watcher, name);
+ erts_add_monitor(&ERTS_P_MONITORS(rp), MON_TARGET, ref, watcher, name);
erts_smp_de_links_unlock(dep);
erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
}
@@ -1275,7 +1358,7 @@ int erts_net_message(Port *prt,
if (!rp) {
break;
}
- mon = erts_remove_monitor(&(rp->monitors),ref);
+ mon = erts_remove_monitor(&ERTS_P_MONITORS(rp), ref);
erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
ASSERT(mon != NULL);
if (mon == NULL) {
@@ -1432,7 +1515,7 @@ int erts_net_message(Port *prt,
erts_destroy_monitor(mon);
- mon = erts_remove_monitor(&(rp->monitors),ref);
+ mon = erts_remove_monitor(&ERTS_P_MONITORS(rp), ref);
if (mon == NULL) {
erts_smp_proc_unlock(rp, rp_locks);
@@ -1483,7 +1566,7 @@ int erts_net_message(Port *prt,
if (!rp)
lnk = NULL;
else {
- lnk = erts_remove_link(&(rp->nlinks), from);
+ lnk = erts_remove_link(&ERTS_P_LINKS(rp), from);
/* If lnk == NULL, we have unlinked on this side, i.e.
* ignore exit.
@@ -1597,7 +1680,7 @@ int erts_net_message(Port *prt,
erts_free(ERTS_ALC_T_DCTRL_BUF, (void *) ctl);
}
UnUseTmpHeapNoproc(DIST_CTL_DEFAULT_SIZE);
- erts_do_exit_port(prt, dep->cid, am_killed);
+ erts_deliver_port_exit(prt, dep->cid, am_killed, 0);
ERTS_SMP_CHK_NO_PROC_LOCKS;
return -1;
}
@@ -1646,7 +1729,7 @@ dsig_send(ErtsDSigData *dsdp, Eterm ctl, Eterm msg, int force_busy)
data_size += erts_encode_dist_ext_size(ctl, flags, acmp);
if (is_value(msg))
data_size += erts_encode_dist_ext_size(msg, flags, acmp);
- erts_finalize_atom_cache_map(acmp);
+ erts_finalize_atom_cache_map(acmp, flags);
dhdr_ext_size = erts_encode_ext_dist_header_size(acmp);
data_size += dhdr_ext_size;
@@ -1693,7 +1776,6 @@ dsig_send(ErtsDSigData *dsdp, Eterm ctl, Eterm msg, int force_busy)
erts_smp_mtx_unlock(&dep->qlock);
plp = erts_proclist_create(c_p);
- plp->next = NULL;
erts_suspend(c_p, ERTS_PROC_LOCK_MAIN, NULL);
suspended = 1;
erts_smp_mtx_lock(&dep->qlock);
@@ -1726,11 +1808,7 @@ dsig_send(ErtsDSigData *dsdp, Eterm ctl, Eterm msg, int force_busy)
else {
/* Enqueue suspended process on dist entry */
ASSERT(plp);
- if (dep->suspended.last)
- dep->suspended.last->next = plp;
- else
- dep->suspended.first = plp;
- dep->suspended.last = plp;
+ erts_proclist_store_last(&dep->suspended, plp);
}
}
@@ -1779,7 +1857,7 @@ dsig_send(ErtsDSigData *dsdp, Eterm ctl, Eterm msg, int force_busy)
erts_snprintf(port_str, sizeof(port_str), "%T", cid);
erts_snprintf(remote_str, sizeof(remote_str), "%T", dep->sysname);
- erts_snprintf(pid_str, sizeof(pid_str), "%T", c_p->id);
+ erts_snprintf(pid_str, sizeof(pid_str), "%T", c_p->common.id);
DTRACE4(dist_port_busy, erts_this_node_sysname,
port_str, remote_str, pid_str);
}
@@ -1812,7 +1890,7 @@ dist_port_command(Port *prt, ErtsDistOutputBuf *obuf)
DTRACE_CHARBUF(port_str, 64);
DTRACE_CHARBUF(remote_str, 64);
- erts_snprintf(port_str, sizeof(port_str), "%T", prt->id);
+ erts_snprintf(port_str, sizeof(port_str), "%T", prt->common.id);
erts_snprintf(remote_str, sizeof(remote_str),
"%T", prt->dist_entry->sysname);
DTRACE4(dist_output, erts_this_node_sysname, port_str,
@@ -1866,7 +1944,7 @@ dist_port_commandv(Port *prt, ErtsDistOutputBuf *obuf)
DTRACE_CHARBUF(port_str, 64);
DTRACE_CHARBUF(remote_str, 64);
- erts_snprintf(port_str, sizeof(port_str), "%T", prt->id);
+ erts_snprintf(port_str, sizeof(port_str), "%T", prt->common.id);
erts_snprintf(remote_str, sizeof(remote_str),
"%T", prt->dist_entry->sysname);
DTRACE4(dist_outputv, erts_this_node_sysname, port_str,
@@ -1903,13 +1981,13 @@ int
erts_dist_command(Port *prt, int reds_limit)
{
Sint reds = ERTS_PORT_REDS_DIST_CMD_START;
- int prt_busy;
Uint32 status;
Uint32 flags;
Sint obufsize = 0;
ErtsDistOutputQueue oq, foq;
DistEntry *dep = prt->dist_entry;
Uint (*send)(Port *prt, ErtsDistOutputBuf *obuf);
+ erts_aint32_t sched_flags;
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
@@ -1925,7 +2003,7 @@ erts_dist_command(Port *prt, int reds_limit)
erts_smp_de_runlock(dep);
if (status & ERTS_DE_SFLG_EXITING) {
- erts_do_exit_port(prt, prt->id, am_killed);
+ erts_deliver_port_exit(prt, prt->common.id, am_killed, 0);
erts_deref_dist_entry(dep);
return reds + ERTS_PORT_REDS_DIST_CMD_EXIT;
}
@@ -1952,12 +2030,12 @@ erts_dist_command(Port *prt, int reds_limit)
dep->finalized_out_queue.first = NULL;
dep->finalized_out_queue.last = NULL;
+ sched_flags = erts_smp_atomic32_read_nob(&prt->sched.flags);
+
if (reds > reds_limit)
goto preempted;
- prt_busy = (int) (prt->status & ERTS_PORT_SFLG_PORT_BUSY);
-
- if (!prt_busy && foq.first) {
+ if (!(sched_flags & ERTS_PTS_FLG_BUSY_PORT) && foq.first) {
int preempt = 0;
do {
Uint size;
@@ -1974,11 +2052,10 @@ erts_dist_command(Port *prt, int reds_limit)
obufsize += size_obuf(fob);
foq.first = foq.first->next;
free_dist_obuf(fob);
- preempt = reds > reds_limit || (prt->status & ERTS_PORT_SFLGS_DEAD);
- if (prt->status & ERTS_PORT_SFLG_PORT_BUSY) {
- prt_busy = 1;
+ sched_flags = erts_smp_atomic32_read_nob(&prt->sched.flags);
+ preempt = reds > reds_limit || (sched_flags & ERTS_PTS_FLG_EXIT);
+ if (sched_flags & ERTS_PTS_FLG_BUSY_PORT)
break;
- }
} while (foq.first && !preempt);
if (!foq.first)
foq.last = NULL;
@@ -1986,7 +2063,7 @@ erts_dist_command(Port *prt, int reds_limit)
goto preempted;
}
- if (prt_busy) {
+ if (sched_flags & ERTS_PTS_FLG_BUSY_PORT) {
if (oq.first) {
ErtsDistOutputBuf *ob;
int preempt;
@@ -1996,7 +2073,8 @@ erts_dist_command(Port *prt, int reds_limit)
ASSERT(ob);
do {
ob->extp = erts_encode_ext_dist_header_finalize(ob->extp,
- dep->cache);
+ dep->cache,
+ flags);
if (!(flags & DFLAG_DIST_HDR_ATOM_CACHE))
*--ob->extp = PASS_THROUGH; /* Old node; 'pass through'
needed */
@@ -2040,7 +2118,8 @@ erts_dist_command(Port *prt, int reds_limit)
Uint size;
oq.first->extp
= erts_encode_ext_dist_header_finalize(oq.first->extp,
- dep->cache);
+ dep->cache,
+ flags);
reds += ERTS_PORT_REDS_DIST_CMD_FINALIZE;
if (!(flags & DFLAG_DIST_HDR_ATOM_CACHE))
*--oq.first->extp = PASS_THROUGH; /* Old node; 'pass through'
@@ -2058,12 +2137,10 @@ erts_dist_command(Port *prt, int reds_limit)
obufsize += size_obuf(fob);
oq.first = oq.first->next;
free_dist_obuf(fob);
- preempt = reds > reds_limit || (prt->status & ERTS_PORT_SFLGS_DEAD);
- if (prt->status & ERTS_PORT_SFLG_PORT_BUSY) {
- prt_busy = 1;
- if (oq.first && !preempt)
- goto finalize_only;
- }
+ sched_flags = erts_smp_atomic32_read_nob(&prt->sched.flags);
+ preempt = reds > reds_limit || (sched_flags & ERTS_PTS_FLG_EXIT);
+ if ((sched_flags & ERTS_PTS_FLG_BUSY_PORT) && oq.first && !preempt)
+ goto finalize_only;
}
ASSERT(!oq.first || preempt);
@@ -2091,7 +2168,7 @@ erts_dist_command(Port *prt, int reds_limit)
ASSERT(dep->qsize >= obufsize);
dep->qsize -= obufsize;
obufsize = 0;
- if (!prt_busy
+ if (!(sched_flags & ERTS_PTS_FLG_BUSY_PORT)
&& (dep->qflgs & ERTS_DE_QFLG_BUSY)
&& dep->qsize < erts_dist_buf_busy_limit) {
ErtsProcList *suspendees;
@@ -2137,11 +2214,15 @@ erts_dist_command(Port *prt, int reds_limit)
return reds;
preempted:
+ /*
+ * Here we assume that state has been read
+ * since last call to driver.
+ */
ASSERT(oq.first || !oq.last);
ASSERT(!oq.first || oq.last);
- if (prt->status & ERTS_PORT_SFLGS_DEAD) {
+ if (sched_flags & ERTS_PTS_FLG_EXIT) {
/*
* Port died during port command; clean up 'oq'
* and 'foq'. Things buffered in dist entry after
@@ -2199,7 +2280,7 @@ erts_dist_port_not_busy(Port *prt)
DTRACE_CHARBUF(port_str, 64);
DTRACE_CHARBUF(remote_str, 64);
- erts_snprintf(port_str, sizeof(port_str), "%T", prt->id);
+ erts_snprintf(port_str, sizeof(port_str), "%T", prt->common.id);
erts_snprintf(remote_str, sizeof(remote_str),
"%T", prt->dist_entry->sysname);
DTRACE3(dist_port_not_busy, erts_this_node_sysname,
@@ -2241,7 +2322,7 @@ static void doit_print_monitor_info(ErtsMonitor *mon, void *vptdp)
Process *rp;
ErtsMonitor *rmon;
rp = erts_proc_lookup(mon->pid);
- if (!rp || (rmon = erts_lookup_monitor(rp->monitors, mon->ref)) == NULL) {
+ if (!rp || (rmon = erts_lookup_monitor(ERTS_P_MONITORS(rp), mon->ref)) == NULL) {
erts_print(to, arg, "Warning, stray monitor for: %T\n", mon->pid);
} else if (mon->type == MON_ORIGIN) {
/* Local pid is being monitored */
@@ -2486,6 +2567,7 @@ BIF_RETTYPE setnode_2(BIF_ALIST_2)
erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
erts_smp_thr_progress_block();
+ inc_no_nodes();
erts_set_this_node(BIF_ARG_1, (Uint32) creation);
erts_is_alive = 1;
send_nodes_mon_msgs(NULL, am_nodeup, BIF_ARG_1, am_visible, NIL);
@@ -2554,9 +2636,9 @@ BIF_RETTYPE setnode_3(BIF_ALIST_3)
/* DFLAG_EXTENDED_REFERENCES is compulsory from R9 and forward */
if (!(DFLAG_EXTENDED_REFERENCES & flags)) {
erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf();
- erts_dsprintf(dsbufp, "%T", BIF_P->id);
- if (BIF_P->reg)
- erts_dsprintf(dsbufp, " (%T)", BIF_P->reg->name);
+ erts_dsprintf(dsbufp, "%T", BIF_P->common.id);
+ if (BIF_P->common.u.alive.reg)
+ erts_dsprintf(dsbufp, " (%T)", BIF_P->common.u.alive.reg->name);
erts_dsprintf(dsbufp,
" attempted to enable connection to node %T "
"which is not able to handle extended references.\n",
@@ -2576,10 +2658,14 @@ BIF_RETTYPE setnode_3(BIF_ALIST_3)
else if (!dep)
goto system_limit; /* Should never happen!!! */
- pp = erts_id2port(BIF_ARG_2, BIF_P, ERTS_PROC_LOCK_MAIN);
+ pp = erts_id2port_sflgs(BIF_ARG_2,
+ BIF_P,
+ ERTS_PROC_LOCK_MAIN,
+ ERTS_PORT_SFLGS_INVALID_LOOKUP);
erts_smp_de_rwlock(dep);
- if (!pp || (pp->status & ERTS_PORT_SFLG_EXITING))
+ if (!pp || (erts_atomic32_read_nob(&pp->state)
+ & ERTS_PORT_SFLG_EXITING))
goto badarg;
if ((pp->drv_ptr->flags & ERL_DRV_FLAG_SOFT_BUSY) == 0)
@@ -2594,11 +2680,7 @@ BIF_RETTYPE setnode_3(BIF_ALIST_3)
plp->next = NULL;
erts_suspend(BIF_P, ERTS_PROC_LOCK_MAIN, NULL);
erts_smp_mtx_lock(&dep->qlock);
- if (dep->suspended.last)
- dep->suspended.last->next = plp;
- else
- dep->suspended.first = plp;
- dep->suspended.last = plp;
+ erts_proclist_store_last(&dep->suspended, plp);
erts_smp_mtx_unlock(&dep->qlock);
goto yield;
}
@@ -2608,7 +2690,16 @@ BIF_RETTYPE setnode_3(BIF_ALIST_3)
if (pp->dist_entry || is_not_nil(dep->cid))
goto badarg;
- erts_port_status_bor_set(pp, ERTS_PORT_SFLG_DISTRIBUTION);
+ erts_atomic32_read_bor_nob(&pp->state, ERTS_PORT_SFLG_DISTRIBUTION);
+
+ /*
+ * Dist-ports do not use the "busy port message queue" functionality, but
+ * instead use "busy dist entry" functionality.
+ */
+ {
+ ErlDrvSizeT disable = ERL_DRV_BUSY_MSGQ_DISABLED;
+ erl_drv_busy_msgq_limits((ErlDrvPort) pp, &disable, NULL);
+ }
pp->dist_entry = dep;
@@ -2640,6 +2731,8 @@ BIF_RETTYPE setnode_3(BIF_ALIST_3)
erts_smp_de_rwunlock(dep);
dep = NULL; /* inc of refc transferred to port (dist_entry field) */
+ inc_no_nodes();
+
send_nodes_mon_msgs(BIF_P,
am_nodeup,
BIF_ARG_1,
@@ -2653,7 +2746,7 @@ BIF_RETTYPE setnode_3(BIF_ALIST_3)
}
if (pp)
- erts_smp_port_unlock(pp);
+ erts_port_release(pp);
return ret;
@@ -2697,7 +2790,7 @@ BIF_RETTYPE dist_exit_3(BIF_ALIST_3)
if (is_internal_pid(local)) {
Process *lp;
ErtsProcLocks lp_locks;
- if (BIF_P->id == local) {
+ if (BIF_P->common.id == local) {
lp_locks = ERTS_PROC_LOCKS_ALL;
lp = BIF_P;
erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCKS_ALL_MINOR);
@@ -2725,11 +2818,17 @@ BIF_RETTYPE dist_exit_3(BIF_ALIST_3)
#endif
erts_smp_proc_unlock(lp, lp_locks);
if (lp == BIF_P) {
+ erts_aint32_t state = erts_smp_atomic32_read_acqb(&BIF_P->state);
/*
* We may have exited current process and may have to take action.
*/
- ERTS_BIF_CHK_EXITED(BIF_P);
- ERTS_SMP_BIF_CHK_PENDING_EXIT(BIF_P, ERTS_PROC_LOCK_MAIN);
+ if (state & (ERTS_PSFLG_EXITING|ERTS_PSFLG_PENDING_EXIT)) {
+#ifdef ERTS_SMP
+ if (state & ERTS_PSFLG_PENDING_EXIT)
+ erts_handle_pending_exit(BIF_P, ERTS_PROC_LOCK_MAIN);
+#endif
+ ERTS_BIF_EXITED(BIF_P);
+ }
}
}
else if (is_external_pid(local)
@@ -2927,23 +3026,23 @@ monitor_node(Process* p, Eterm Node, Eterm Bool, Eterm Options)
if (Bool == am_true) {
ASSERT(dep->cid != NIL);
lnk = erts_add_or_lookup_link(&(dep->node_links), LINK_NODE,
- p->id);
+ p->common.id);
++ERTS_LINK_REFC(lnk);
- lnk = erts_add_or_lookup_link(&(p->nlinks), LINK_NODE, Node);
+ lnk = erts_add_or_lookup_link(&ERTS_P_LINKS(p), LINK_NODE, Node);
++ERTS_LINK_REFC(lnk);
}
else {
- lnk = erts_lookup_link(dep->node_links, p->id);
+ lnk = erts_lookup_link(dep->node_links, p->common.id);
if (lnk != NULL) {
if ((--ERTS_LINK_REFC(lnk)) == 0) {
erts_destroy_link(erts_remove_link(&(dep->node_links),
- p->id));
+ p->common.id));
}
}
- lnk = erts_lookup_link(p->nlinks, Node);
+ lnk = erts_lookup_link(ERTS_P_LINKS(p), Node);
if (lnk != NULL) {
if ((--ERTS_LINK_REFC(lnk)) == 0) {
- erts_destroy_link(erts_remove_link(&(p->nlinks),
+ erts_destroy_link(erts_remove_link(&ERTS_P_LINKS(p),
Node));
}
}
@@ -3505,7 +3604,7 @@ erts_processes_monitoring_nodes(Process *c_p)
olist = erts_bld_cons(hpp, szp, am_nodedown_reason, olist);
res = erts_bld_cons(hpp, szp,
erts_bld_tuple(hpp, szp, 2,
- nmp->proc->id,
+ nmp->proc->common.id,
olist),
res);
}
diff --git a/erts/emulator/beam/dist.h b/erts/emulator/beam/dist.h
index 845151c895..310d09768d 100644
--- a/erts/emulator/beam/dist.h
+++ b/erts/emulator/beam/dist.h
@@ -38,7 +38,8 @@
#define DFLAG_UNICODE_IO 0x1000
#define DFLAG_DIST_HDR_ATOM_CACHE 0x2000
#define DFLAG_SMALL_ATOM_TAGS 0x4000
-#define DFLAGS_INTERNAL_TAGS 0x8000
+#define DFLAG_INTERNAL_TAGS 0x8000
+#define DFLAG_UTF8_ATOMS 0x10000
/* All flags that should be enabled when term_to_binary/1 is used. */
#define TERM_TO_BINARY_DFLAGS (DFLAG_EXTENDED_REFERENCES \
@@ -187,11 +188,12 @@ void erts_schedule_dist_command(Port *prt, DistEntry *dist_entry)
if (prt) {
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
- ASSERT((erts_port_status_get(prt) & ERTS_PORT_SFLGS_DEAD) == 0);
+ ASSERT((erts_atomic32_read_nob(&prt->state)
+ & ERTS_PORT_SFLGS_DEAD) == 0);
ASSERT(prt->dist_entry);
dep = prt->dist_entry;
- id = prt->id;
+ id = prt->common.id;
}
else {
ASSERT(dist_entry);
@@ -203,13 +205,8 @@ void erts_schedule_dist_command(Port *prt, DistEntry *dist_entry)
id = dep->cid;
}
- if (!erts_smp_atomic_xchg_mb(&dep->dist_cmd_scheduled, 1)) {
- (void) erts_port_task_schedule(id,
- &dep->dist_cmd,
- ERTS_PORT_TASK_DIST_CMD,
- (ErlDrvEvent) -1,
- NULL);
- }
+ if (!erts_smp_atomic_xchg_mb(&dep->dist_cmd_scheduled, 1))
+ erts_port_task_schedule(id, &dep->dist_cmd, ERTS_PORT_TASK_DIST_CMD);
}
#endif
diff --git a/erts/emulator/beam/dtrace-wrapper.h b/erts/emulator/beam/dtrace-wrapper.h
index 1aeb7f9221..6ec0c91e21 100644
--- a/erts/emulator/beam/dtrace-wrapper.h
+++ b/erts/emulator/beam/dtrace-wrapper.h
@@ -42,6 +42,8 @@
#define DTRACE_CHARBUF(name, size) \
char name##_BUFFER[size], *name = name##_BUFFER
+#define DTRACE_CHARBUF_NAME(name) name##_BUFFER
+
#if defined(USE_DYNAMIC_TRACE) && defined(USE_VM_PROBES)
#include "erlang_dtrace.h"
diff --git a/erts/emulator/beam/erl_afit_alloc.c b/erts/emulator/beam/erl_afit_alloc.c
index 570cc59be2..306b32691c 100644
--- a/erts/emulator/beam/erl_afit_alloc.c
+++ b/erts/emulator/beam/erl_afit_alloc.c
@@ -37,6 +37,12 @@
#define GET_ERL_AF_ALLOC_IMPL
#include "erl_afit_alloc.h"
+struct AFFreeBlock_t_ {
+ Block_t block_head;
+ AFFreeBlock_t *prev;
+ AFFreeBlock_t *next;
+};
+#define AF_BLK_SZ(B) MBC_FBLK_SZ(&(B)->block_head)
#define MIN_MBC_SZ (16*1024)
#define MIN_MBC_FIRST_FREE_SZ (4*1024)
@@ -80,7 +86,6 @@ erts_afalc_start(AFAllctr_t *afallctr,
init->sbmbct = 0; /* Small mbc not supported by afit */
- allctr->mbc_header_size = sizeof(Carrier_t);
allctr->min_mbc_size = MIN_MBC_SZ;
allctr->min_mbc_first_free_size = MIN_MBC_FIRST_FREE_SZ;
allctr->min_block_size = sizeof(AFFreeBlock_t);
@@ -118,7 +123,7 @@ get_free_block(Allctr_t *allctr, Uint size, Block_t *cand_blk, Uint cand_size,
ASSERT(!cand_blk || cand_size >= size);
- if (afallctr->free_list && BLK_SZ(afallctr->free_list) >= size) {
+ if (afallctr->free_list && AF_BLK_SZ(afallctr->free_list) >= size) {
AFFreeBlock_t *res = afallctr->free_list;
afallctr->free_list = res->next;
if (res->next)
@@ -135,7 +140,7 @@ link_free_block(Allctr_t *allctr, Block_t *block, Uint32 flags)
AFFreeBlock_t *blk = (AFFreeBlock_t *) block;
AFAllctr_t *afallctr = (AFAllctr_t *) allctr;
- if (afallctr->free_list && BLK_SZ(afallctr->free_list) > BLK_SZ(blk)) {
+ if (afallctr->free_list && AF_BLK_SZ(afallctr->free_list) > AF_BLK_SZ(blk)) {
blk->next = afallctr->free_list->next;
blk->prev = afallctr->free_list;
afallctr->free_list->next = blk;
diff --git a/erts/emulator/beam/erl_afit_alloc.h b/erts/emulator/beam/erl_afit_alloc.h
index ea408a7194..87caccac20 100644
--- a/erts/emulator/beam/erl_afit_alloc.h
+++ b/erts/emulator/beam/erl_afit_alloc.h
@@ -49,11 +49,6 @@ Allctr_t *erts_afalc_start(AFAllctr_t *, AFAllctrInit_t *, AllctrInit_t *);
#include "erl_alloc_util.h"
typedef struct AFFreeBlock_t_ AFFreeBlock_t;
-struct AFFreeBlock_t_ {
- Block_t block_head;
- AFFreeBlock_t *prev;
- AFFreeBlock_t *next;
-};
struct AFAllctr_t_ {
Allctr_t allctr; /* Has to be first! */
diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c
index 918a800555..007cdbdfa6 100644
--- a/erts/emulator/beam/erl_alloc.c
+++ b/erts/emulator/beam/erl_alloc.c
@@ -310,9 +310,9 @@ set_default_ll_alloc_opts(struct au_init *ip)
ip->init.util.name_prefix = "ll_";
ip->init.util.alloc_no = ERTS_ALC_A_LONG_LIVED;
#ifndef SMALL_MEMORY
- ip->init.util.mmbcs = 2*1024*1024; /* Main carrier size */
+ ip->init.util.mmbcs = 2*1024*1024 - 40; /* Main carrier size */
#else
- ip->init.util.mmbcs = 1*1024*1024; /* Main carrier size */
+ ip->init.util.mmbcs = 1*1024*1024 - 40; /* Main carrier size */
#endif
ip->init.util.ts = ERTS_ALC_MTA_LONG_LIVED;
ip->init.util.asbcst = 0;
@@ -1173,6 +1173,11 @@ handle_au_arg(struct au_init *auip,
break;
case 'e':
auip->enable = get_bool_value(sub_param+1, argv, ip);
+#if !HAVE_ERTS_SBMBC
+ if (auip->init.util.alloc_no == ERTS_ALC_A_SBMBC) {
+ auip->enable = 0;
+ }
+#endif
break;
case 'l':
if (has_prefix("lmbcs", sub_param)) {
@@ -1233,10 +1238,16 @@ handle_au_arg(struct au_init *auip,
auip->init.util.sbct = get_kb_value(sub_param + 4, argv, ip);
}
else if (has_prefix("sbmbcs", sub_param)) {
- auip->init.util.sbmbcs = get_byte_value(sub_param + 6, argv, ip);
+#if HAVE_ERTS_SBMBC
+ auip->init.util.sbmbcs =
+#endif
+ get_byte_value(sub_param + 6, argv, ip);
}
else if (has_prefix("sbmbct", sub_param)) {
- auip->init.util.sbmbct = get_byte_value(sub_param + 6, argv, ip);
+#if HAVE_ERTS_SBMBC
+ auip->init.util.sbmbct =
+#endif
+ get_byte_value(sub_param + 6, argv, ip);
}
else if (has_prefix("smbcs", sub_param)) {
auip->default_.smbcs = 0;
@@ -1403,6 +1414,9 @@ handle_args(int *argc, char **argv, erts_alc_hndl_args_init_t *init)
else if (strcmp("max", arg) == 0) {
for (a = 0; a < aui_sz; a++)
aui[a]->enable = 1;
+#if !HAVE_ERTS_SBMBC
+ init->sbmbc_alloc.enable = 0;
+#endif
}
else if (strcmp("config", arg) == 0) {
init->erts_alloc_config = 1;
@@ -1675,7 +1689,7 @@ erts_alc_fatal_error(int error, int func, ErtsAlcType_t n, ...)
t_str = type_no_str(n);
if (!t_str) {
- sprintf(buf, "%d", (int) n);
+ erts_snprintf(buf, sizeof(buf), "%d", (int) n);
t_str = buf;
}
@@ -2128,6 +2142,7 @@ erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg)
if (want_tot_or_sys || want.processes || want.processes_used) {
+ int max_processes = erts_ptab_max(&erts_proc);
UWord tmp;
if (ERTS_MEM_NEED_ALL_ALCU)
@@ -2137,7 +2152,7 @@ erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg)
fi, ERTS_ALC_NO_FIXED_SIZES);
tmp = alcu_size(ERTS_ALC_A_EHEAP, NULL, 0);
}
- tmp += erts_max_processes*sizeof(Process*);
+ tmp += max_processes*sizeof(erts_smp_atomic_t);
tmp += erts_bif_timer_memory_size();
tmp += erts_tot_link_lh_size();
@@ -2268,6 +2283,8 @@ erts_allocated_areas(int *print_to_p, void *print_to_arg, void *proc)
Eterm res = THE_NON_VALUE;
int i, length;
Uint reserved_atom_space, atom_space;
+ int max_processes = erts_ptab_max(&erts_proc);
+ int max_ports = erts_ptab_max(&erts_port);
if (proc) {
ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_MAIN
@@ -2299,12 +2316,8 @@ erts_allocated_areas(int *print_to_p, void *print_to_arg, void *proc)
values[i].arity = 2;
values[i].name = "static";
values[i].ui[0] =
- erts_max_ports*sizeof(Port) /* Port table */
- + erts_timer_wheel_memory_size() /* Timer wheel */
-#ifdef SYS_TMP_BUF_SIZE
- + SYS_TMP_BUF_SIZE /* tmp_buf in sys on vxworks & ose */
-#endif
- ;
+ max_ports*sizeof(erts_smp_atomic_t) /* Port table */
+ + erts_timer_wheel_memory_size(); /* Timer wheel */
i++;
erts_atom_get_text_space_sizes(&reserved_atom_space, &atom_space);
@@ -2382,7 +2395,7 @@ erts_allocated_areas(int *print_to_p, void *print_to_arg, void *proc)
values[i].arity = 2;
values[i].name = "process_table";
- values[i].ui[0] = erts_max_processes*sizeof(Process*);
+ values[i].ui[0] = max_processes*sizeof(Process*);
i++;
values[i].arity = 2;
@@ -3049,13 +3062,13 @@ erts_request_alloc_info(struct process *c_p,
Eterm alloc = CAR(consp);
for (ai = ERTS_ALC_A_MIN; ai <= ERTS_ALC_A_MAX; ai++)
- if (erts_is_atom_str((char *) erts_alc_a2ad[ai], alloc))
+ if (erts_is_atom_str(erts_alc_a2ad[ai], alloc, 0))
goto save_alloc;
- if (erts_is_atom_str("mseg_alloc", alloc)) {
+ if (erts_is_atom_str("mseg_alloc", alloc, 0)) {
ai = ERTS_ALC_INFO_A_MSEG_ALLOC;
goto save_alloc;
}
- if (erts_is_atom_str("alloc_util", alloc)) {
+ if (erts_is_atom_str("alloc_util", alloc, 0)) {
ai = ERTS_ALC_INFO_A_ALLOC_UTIL;
save_alloc:
if (req_ai[ai])
@@ -3576,12 +3589,12 @@ check_memory_fence(void *ptr, Uint *size, ErtsAlcType_t n, int func)
ftype = type_no_str(found_type);
if (!ftype) {
- sprintf(fbuf, "%d", (int) found_type);
+ erts_snprintf(fbuf, sizeof(fbuf), "%d", (int) found_type);
ftype = fbuf;
}
otype = type_no_str(n);
if (!otype) {
- sprintf(obuf, "%d", (int) n);
+ erts_snprintf(obuf, sizeof(obuf), "%d", (int) n);
otype = obuf;
}
diff --git a/erts/emulator/beam/erl_alloc.h b/erts/emulator/beam/erl_alloc.h
index e475f9d8a2..ba5ec9c367 100644
--- a/erts/emulator/beam/erl_alloc.h
+++ b/erts/emulator/beam/erl_alloc.h
@@ -267,6 +267,8 @@ typedef void (*erts_alloc_verify_func_t)(Allctr_t *);
erts_alloc_verify_func_t
erts_alloc_get_verify_unused_temp_alloc(Allctr_t **allctr);
+#define ERTS_ALC_DATA_ALIGN_SIZE(SZ) \
+ (((((SZ) - 1) / 8) + 1) * 8)
#define ERTS_ALC_CACHE_LINE_ALIGN_SIZE(SZ) \
(((((SZ) - 1) / ERTS_CACHE_LINE_SIZE) + 1) * ERTS_CACHE_LINE_SIZE)
diff --git a/erts/emulator/beam/erl_alloc.types b/erts/emulator/beam/erl_alloc.types
index 134de6dfa6..3d437652ce 100644
--- a/erts/emulator/beam/erl_alloc.types
+++ b/erts/emulator/beam/erl_alloc.types
@@ -49,6 +49,8 @@
# true after a "+enable X" statement or if it has been passed as a
# command line argument to make_alloc_types. The variable X is false
# after a "+disable X" statement or if it has never been mentioned.
+#
+# IMPORTANT! Only use 7-bit ascii text in this file!
+if smp
+disable threads_no_smp
@@ -146,6 +148,7 @@ class SYSTEM system_data
type SBMBC SBMBC SYSTEM small_block_mbc
type PROC FIXED_SIZE PROCESSES proc
+type PORT DRIVER SYSTEM port
type ATOM LONG_LIVED ATOM atom_entry
type MODULE LONG_LIVED CODE module_entry
type REG_PROC STANDARD PROCESSES reg_proc
@@ -189,7 +192,10 @@ type PORT_TABLE LONG_LIVED SYSTEM port_tab
type TIMER_WHEEL LONG_LIVED SYSTEM timer_wheel
type DRV DRIVER SYSTEM drv_internal
type DRV_BINARY BINARY BINARIES drv_binary
-type DRIVER STANDARD SYSTEM driver
+type DRIVER DRIVER SYSTEM driver
+type DRV_CMD_DATA DRIVER SYSTEM driver_command_data
+type DRV_CTRL_DATA DRIVER SYSTEM driver_control_data
+type DRV_CALL_DATA DRIVER SYSTEM driver_call_data
type NIF DRIVER SYSTEM nif_internal
type BINARY BINARY BINARIES binary
type NBIF_TABLE SYSTEM SYSTEM nbif_tab
@@ -197,14 +203,12 @@ type ARG_REG STANDARD PROCESSES arg_reg
type PROC_DICT STANDARD PROCESSES proc_dict
type CALLS_BUF STANDARD PROCESSES calls_buf
type BPD STANDARD SYSTEM bpd
-type PORT_NAME STANDARD SYSTEM port_name
type LINEBUF STANDARD SYSTEM line_buf
type IOQ STANDARD SYSTEM io_queue
type BITS_BUF STANDARD SYSTEM bits_buf
type TMP_DIST_BUF TEMPORARY SYSTEM tmp_dist_buf
type ASYNC_DATA LONG_LIVED SYSTEM internal_async_data
type ESTACK TEMPORARY SYSTEM estack
-type PORT_CALL_BUF TEMPORARY SYSTEM port_call_buf
type DB_TABLE ETS ETS db_tab
type DB_FIXATION SHORT_LIVED ETS db_fixation
type DB_FIX_DEL SHORT_LIVED ETS fixed_del
@@ -234,14 +238,14 @@ type DDLL_HANDLE STANDARD SYSTEM ddll_handle
type DDLL_ERRCODES LONG_LIVED SYSTEM ddll_errcodes
type DDLL_TMP_BUF TEMPORARY SYSTEM ddll_tmp_buf
type PORT_TASK SHORT_LIVED SYSTEM port_task
-type PORT_TASKQ SHORT_LIVED SYSTEM port_task_queue
+type PT_HNDL_LIST SHORT_LIVED SYSTEM port_task_handle_list
type MISC_OP_LIST SHORT_LIVED SYSTEM misc_op_list
type PORT_NAMES SHORT_LIVED SYSTEM port_names
-type PORT_DATA_LOCK STANDARD SYSTEM port_data_lock
+type PORT_DATA_LOCK DRIVER SYSTEM port_data_lock
type NODES_MON STANDARD PROCESSES nodes_monitor
-type PROCS_TPROC_EL SHORT_LIVED PROCESSES processes_term_proc_el
-type PROCS_CNKINF SHORT_LIVED PROCESSES processes_chunk_info
-type PROCS_PIDS SHORT_LIVED PROCESSES processes_pids
+type PTAB_LIST_DEL SHORT_LIVED PROCESSES ptab_list_deleted_el
+type PTAB_LIST_CNKI SHORT_LIVED PROCESSES ptab_list_chunk_info
+type PTAB_LIST_PIDS SHORT_LIVED PROCESSES ptab_list_pids
type RE_TMP_BUF TEMPORARY SYSTEM re_tmp_buf
type RE_SUBJECT SHORT_LIVED SYSTEM re_subject
type RE_HEAP STANDARD SYSTEM re_heap
@@ -266,6 +270,8 @@ type AUX_WORK_TMO LONG_LIVED SYSTEM aux_work_timeouts
type MISC_AUX_WORK_Q LONG_LIVED SYSTEM misc_aux_work_q
type CODE_IX_LOCK_Q SHORT_LIVED SYSTEM code_ix_lock_q
type PROC_INTERVAL LONG_LIVED SYSTEM process_interval
+type BUSY_CALLER_TAB SHORT_LIVED SYSTEM busy_caller_table
+type BUSY_CALLER SHORT_LIVED SYSTEM busy_caller
+if threads_no_smp
# Need thread safe allocs, but std_alloc and fix_alloc are not;
@@ -294,7 +300,6 @@ type PORT_LOCK STANDARD SYSTEM port_lock
type DRIVER_LOCK STANDARD SYSTEM driver_lock
type XPORTS_LIST SHORT_LIVED SYSTEM extra_port_list
type PROC_LCK_WTR LONG_LIVED SYSTEM proc_lock_waiter
-type PROC_LCK_QS LONG_LIVED SYSTEM proc_lock_queues
type RUNQ_BLNS LONG_LIVED SYSTEM run_queue_balancing
type THR_PRGR_IDATA LONG_LIVED SYSTEM thr_prgr_internal_data
type THR_PRGR_DATA LONG_LIVED SYSTEM thr_prgr_data
@@ -420,28 +425,4 @@ type CON_VPRINTF_BUF TEMPORARY SYSTEM con_vprintf_buf
+endif
-+if vxworks
-
-type SYS_TMP_BUF LONG_LIVED SYSTEM sys_tmp_buf
-type PEND_DATA SYSTEM SYSTEM pending_data
-type FD_TAB LONG_LIVED SYSTEM fd_tab
-type FD_ENTRY_BUF SYSTEM SYSTEM fd_entry_buf
-
-+endif
-
-+if ose
-
-type SYS_TMP_BUF LONG_LIVED SYSTEM sys_tmp_buf
-type PUTENV_STR SYSTEM SYSTEM putenv_string
-type GETENV_STR SYSTEM SYSTEM getenv_string
-type GETENV_STATE SYSTEM SYSTEM getenv_state
-type SIG_ENTRY SYSTEM SYSTEM sig_entry
-type DRIVER_DATA SYSTEM SYSTEM driver_data
-type PGM_TAB SYSTEM SYSTEM pgm_tab
-type PGM_ENTRY SYSTEM SYSTEM pgm_entry
-type PRT_TAB SYSTEM SYSTEM prt_tab
-type PRT_ENTRY SYSTEM SYSTEM prt_entry
-
-+endif
-
# ----------------------------------------------------------------------------
diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c
index 9a011e2adc..6de0099636 100644
--- a/erts/emulator/beam/erl_alloc_util.c
+++ b/erts/emulator/beam/erl_alloc_util.c
@@ -78,10 +78,12 @@ int erts_have_sbmbc_alloc;
#if HAVE_ERTS_MSEG
-#define INV_MSEG_UNIT_MASK ((UWord) (mseg_unit_size - 1))
-#define MSEG_UNIT_MASK (~INV_MSEG_UNIT_MASK)
+#define MSEG_UNIT_SHIFT MSEG_ALIGN_BITS
+#define MSEG_UNIT_SZ (1 << MSEG_UNIT_SHIFT)
+#define MSEG_UNIT_MASK ((~(UWord)0) << MSEG_UNIT_SHIFT)
+
#define MSEG_UNIT_FLOOR(X) ((X) & MSEG_UNIT_MASK)
-#define MSEG_UNIT_CEILING(X) MSEG_UNIT_FLOOR((X) + INV_MSEG_UNIT_MASK)
+#define MSEG_UNIT_CEILING(X) MSEG_UNIT_FLOOR((X) + ~MSEG_UNIT_MASK)
#endif
@@ -104,7 +106,6 @@ int erts_have_sbmbc_alloc;
static Uint sys_alloc_carrier_size;
#if HAVE_ERTS_MSEG
static Uint max_mseg_carriers;
-static Uint mseg_unit_size;
#endif
#define ONE_GIGA (1000000000)
@@ -117,16 +118,47 @@ static Uint mseg_unit_size;
? ((CC).giga_no--, (CC).no = ONE_GIGA - 1) \
: (CC).no--)
-/* ... */
+/* Multi block carrier (MBC) memory layout in R16:
+
+Empty MBC:
+[Carrier_t|pad|Block_t L0T|fhdr| free... ]
+
+MBC after allocating first block:
+[Carrier_t|pad|Block_t 000| udata |pad|Block_t L0T|fhdr| free... ]
+
+MBC after allocating second block:
+[Carrier_t|pad|Block_t 000| udata |pad|Block_t 000| udata |pad|Block_t L0T|fhdr| free... ]
+
+MBC after deallocating first block:
+[Carrier_t|pad|Block_t 00T|fhdr| free |FreeBlkFtr_t|Block_t 0P0| udata |pad|Block_t L0T|fhdr| free... ]
+
+
+ udata = Allocated user data
+ pad = Padding to ensure correct alignment for user data
+ fhdr = Allocator specific header to keep track of free block
+ free = Unused free memory
+ T = This block is free (THIS_FREE_BLK_HDR_FLG)
+ P = Previous block is free (PREV_FREE_BLK_HDR_FLG)
+ L = Last block in carrier (LAST_BLK_HDR_FLG)
+*/
+
+/* Single block carrier (SBC):
+[Carrier_t|pad|Block_t 111| udata... ]
+*/
+
/* Blocks ... */
-#define SBC_BLK_FTR_FLG (((UWord) 1) << 0)
+#define UNUSED0_BLK_FTR_FLG (((UWord) 1) << 0)
#define UNUSED1_BLK_FTR_FLG (((UWord) 1) << 1)
#define UNUSED2_BLK_FTR_FLG (((UWord) 1) << 2)
-#define ABLK_HDR_SZ (sizeof(Block_t))
-#define FBLK_FTR_SZ (sizeof(UWord))
+#if MBC_ABLK_OFFSET_BITS
+# define ABLK_HDR_SZ (offsetof(Block_t,u))
+#else
+# define ABLK_HDR_SZ (sizeof(Block_t))
+#endif
+#define FBLK_FTR_SZ (sizeof(FreeBlkFtr_t))
#define UMEMSZ2BLKSZ(AP, SZ) \
(ABLK_HDR_SZ + (SZ) <= (AP)->min_block_size \
@@ -136,88 +168,181 @@ static Uint mseg_unit_size;
#define UMEM2BLK(P) ((Block_t *) (((char *) (P)) - ABLK_HDR_SZ))
#define BLK2UMEM(P) ((void *) (((char *) (P)) + ABLK_HDR_SZ))
-#define PREV_BLK_SZ(B) \
- ((UWord) (*(((UWord *) (B)) - 1) & SZ_MASK))
+#define PREV_BLK_SZ(B) ((UWord) (((FreeBlkFtr_t *)(B))[-1]))
#define SET_BLK_SZ_FTR(B, SZ) \
- (*((UWord *) (((char *) (B)) + (SZ) - sizeof(UWord))) = (SZ))
+ (((FreeBlkFtr_t *) (((char *) (B)) + (SZ)))[-1] = (SZ))
#define THIS_FREE_BLK_HDR_FLG (((UWord) 1) << 0)
#define PREV_FREE_BLK_HDR_FLG (((UWord) 1) << 1)
#define LAST_BLK_HDR_FLG (((UWord) 1) << 2)
-#define SET_BLK_SZ(B, SZ) \
+/* Special flag combo for (allocated) SBC blocks
+*/
+#define SBC_BLK_HDR_FLG (THIS_FREE_BLK_HDR_FLG | PREV_FREE_BLK_HDR_FLG | LAST_BLK_HDR_FLG)
+
+#define SET_MBC_ABLK_SZ(B, SZ) \
+ (ASSERT(((SZ) & FLG_MASK) == 0), \
+ (B)->bhdr = (((B)->bhdr) & ~MBC_ABLK_SZ_MASK) | (SZ))
+#define SET_MBC_FBLK_SZ(B, SZ) \
+ (ASSERT(((SZ) & FLG_MASK) == 0), \
+ (B)->bhdr = (((B)->bhdr) & ~MBC_FBLK_SZ_MASK) | (SZ))
+#define SET_SBC_BLK_SZ(B, SZ) \
(ASSERT(((SZ) & FLG_MASK) == 0), \
- (*((Block_t *) (B)) = ((*((Block_t *) (B)) & FLG_MASK) | (SZ))))
-#define SET_BLK_FREE(B) \
- (*((Block_t *) (B)) |= THIS_FREE_BLK_HDR_FLG)
-#define SET_BLK_ALLOCED(B) \
- (*((Block_t *) (B)) &= ~THIS_FREE_BLK_HDR_FLG)
-#define SET_PREV_BLK_FREE(B) \
- (*((Block_t *) (B)) |= PREV_FREE_BLK_HDR_FLG)
+ (B)->bhdr = (((B)->bhdr) & ~SBC_BLK_SZ_MASK) | (SZ))
+#define SET_PREV_BLK_FREE(AP,B) \
+ (ASSERT(!IS_MBC_FIRST_BLK(AP,B)), \
+ ASSERT(!IS_FREE_BLK(B)), \
+ (B)->bhdr |= PREV_FREE_BLK_HDR_FLG)
#define SET_PREV_BLK_ALLOCED(B) \
- (*((Block_t *) (B)) &= ~PREV_FREE_BLK_HDR_FLG)
+ ((B)->bhdr &= ~PREV_FREE_BLK_HDR_FLG)
#define SET_LAST_BLK(B) \
- (*((Block_t *) (B)) |= LAST_BLK_HDR_FLG)
+ ((B)->bhdr |= LAST_BLK_HDR_FLG)
#define SET_NOT_LAST_BLK(B) \
- (*((Block_t *) (B)) &= ~LAST_BLK_HDR_FLG)
+ ((B)->bhdr &= ~LAST_BLK_HDR_FLG)
#define SBH_THIS_FREE THIS_FREE_BLK_HDR_FLG
-#define SBH_THIS_ALLOCED ((UWord) 0)
#define SBH_PREV_FREE PREV_FREE_BLK_HDR_FLG
-#define SBH_PREV_ALLOCED ((UWord) 0)
#define SBH_LAST_BLK LAST_BLK_HDR_FLG
-#define SBH_NOT_LAST_BLK ((UWord) 0)
-#define SET_BLK_HDR(B, Sz, F) \
- (ASSERT(((Sz) & FLG_MASK) == 0), *((Block_t *) (B)) = ((Sz) | (F)))
+
+#if MBC_ABLK_OFFSET_BITS
+
+# define MBC_SZ_MAX_LIMIT ((((UWord)1 << MBC_ABLK_OFFSET_BITS) - 1) << MSEG_ALIGN_BITS)
+
+# define BLK_CARRIER_OFFSET(B, C) (((char*)(B) - (char*)(C)) >> MSEG_UNIT_SHIFT)
+
+# define SET_MBC_ABLK_HDR(B, Sz, F, C) \
+ (ASSERT(((Sz) & ~MBC_ABLK_SZ_MASK) == 0), \
+ ASSERT(!((UWord)(F) & (~FLG_MASK|THIS_FREE_BLK_HDR_FLG))), \
+ (B)->bhdr = ((Sz) | (F) | (BLK_CARRIER_OFFSET(B,C) << MBC_ABLK_OFFSET_SHIFT)))
+
+# define SET_MBC_FBLK_HDR(B, Sz, F, C) \
+ (ASSERT(((Sz) & ~MBC_FBLK_SZ_MASK) == 0), \
+ ASSERT(((UWord)(F) & (~FLG_MASK|THIS_FREE_BLK_HDR_FLG|PREV_FREE_BLK_HDR_FLG)) == THIS_FREE_BLK_HDR_FLG), \
+ (B)->bhdr = ((Sz) | (F)), \
+ (B)->u.carrier = (C))
+
+# define ABLK_TO_MBC(B) \
+ (ASSERT(IS_MBC_BLK(B) && IS_ALLOCED_BLK(B)), \
+ (Carrier_t*)((MSEG_UNIT_FLOOR((UWord)(B)) - \
+ (((B)->bhdr >> MBC_ABLK_OFFSET_SHIFT) << MSEG_UNIT_SHIFT))))
+
+# define FBLK_TO_MBC(B) \
+ (ASSERT(IS_MBC_BLK(B) && IS_FREE_BLK(B)), \
+ (B)->u.carrier)
+
+# define BLK_TO_MBC(B) (IS_FREE_BLK(B) ? FBLK_TO_MBC(B) : ABLK_TO_MBC(B))
+
+# define IS_MBC_FIRST_ABLK(AP,B) \
+ ((((UWord)(B) & ~MSEG_UNIT_MASK) == MBC_HEADER_SIZE(AP)) \
+ && ((B)->bhdr & MBC_ABLK_OFFSET_MASK) == 0)
+
+# define IS_MBC_FIRST_FBLK(AP,B) \
+ ((char*)(B) == (char*)((B)->u.carrier) + MBC_HEADER_SIZE(AP))
+
+# define IS_MBC_FIRST_BLK(AP,B) \
+ (IS_FREE_BLK(B) ? IS_MBC_FIRST_FBLK(AP,B) : IS_MBC_FIRST_ABLK(AP,B))
+
+# define SET_BLK_FREE(B) \
+ (ASSERT(!IS_PREV_BLK_FREE(B)), \
+ (B)->u.carrier = ABLK_TO_MBC(B), \
+ (B)->bhdr |= THIS_FREE_BLK_HDR_FLG, \
+ (B)->bhdr &= (MBC_ABLK_SZ_MASK|FLG_MASK))
+
+# define SET_BLK_ALLOCED(B) \
+ (ASSERT(((B)->bhdr & (MBC_ABLK_OFFSET_MASK|THIS_FREE_BLK_HDR_FLG)) == THIS_FREE_BLK_HDR_FLG), \
+ (B)->bhdr &= ~THIS_FREE_BLK_HDR_FLG, \
+ (B)->bhdr |= (BLK_CARRIER_OFFSET(B,(B)->u.carrier) << MBC_ABLK_OFFSET_SHIFT))
+
+#else /* !MBC_ABLK_OFFSET_BITS */
+
+# define MBC_SZ_MAX_LIMIT ((UWord)~0)
+
+# define SET_MBC_ABLK_HDR(B, Sz, F, C) \
+ (ASSERT(((Sz) & FLG_MASK) == 0), \
+ ASSERT(!((UWord)(F) & (~FLG_MASK|THIS_FREE_BLK_HDR_FLG))), \
+ ASSERT((UWord)(F) < SBC_BLK_HDR_FLG), \
+ (B)->bhdr = ((Sz) | (F)), \
+ (B)->carrier = (C))
+
+# define SET_MBC_FBLK_HDR(B, Sz, F, C) \
+ (ASSERT(((Sz) & FLG_MASK) == 0), \
+ ASSERT(((UWord)(F) & (~FLG_MASK|THIS_FREE_BLK_HDR_FLG|PREV_FREE_BLK_HDR_FLG)) == THIS_FREE_BLK_HDR_FLG), \
+ (B)->bhdr = ((Sz) | (F)), \
+ (B)->carrier = (C))
+
+# define BLK_TO_MBC(B) ((B)->carrier)
+# define ABLK_TO_MBC(B) BLK_TO_MBC(B)
+# define FBLK_TO_MBC(B) BLK_TO_MBC(B)
+
+# define IS_MBC_FIRST_BLK(AP,B) \
+ ((char*)(B) == (char*)((B)->carrier) + MBC_HEADER_SIZE(AP))
+# define IS_MBC_FIRST_ABLK(AP,B) IS_MBC_FIRST_BLK(AP,B)
+# define IS_MBC_FIRST_FBLK(AP,B) IS_MBC_FIRST_BLK(AP,B)
+
+# define SET_BLK_FREE(B) \
+ (ASSERT(!IS_PREV_BLK_FREE(B)), \
+ (B)->bhdr |= THIS_FREE_BLK_HDR_FLG)
+
+# define SET_BLK_ALLOCED(B) \
+ ((B)->bhdr &= ~THIS_FREE_BLK_HDR_FLG)
+
+#endif /* !MBC_ABLK_OFFSET_BITS */
+
+#define SET_SBC_BLK_HDR(B, Sz) \
+ (ASSERT(((Sz) & FLG_MASK) == 0), (B)->bhdr = ((Sz) | (SBC_BLK_HDR_FLG)))
+
#define BLK_UMEM_SZ(B) \
(BLK_SZ(B) - (ABLK_HDR_SZ))
#define IS_PREV_BLK_FREE(B) \
- (*((Block_t *) (B)) & PREV_FREE_BLK_HDR_FLG)
+ ((B)->bhdr & PREV_FREE_BLK_HDR_FLG)
#define IS_PREV_BLK_ALLOCED(B) \
(!IS_PREV_BLK_FREE((B)))
#define IS_FREE_BLK(B) \
- (*((Block_t *) (B)) & THIS_FREE_BLK_HDR_FLG)
+ (ASSERT(!IS_SBC_BLK(B)), (B)->bhdr & THIS_FREE_BLK_HDR_FLG)
#define IS_ALLOCED_BLK(B) \
(!IS_FREE_BLK((B)))
#define IS_LAST_BLK(B) \
- (*((Block_t *) (B)) & LAST_BLK_HDR_FLG)
+ ((B)->bhdr & LAST_BLK_HDR_FLG)
#define IS_NOT_LAST_BLK(B) \
(!IS_LAST_BLK((B)))
#define GET_LAST_BLK_HDR_FLG(B) \
- (*((Block_t*) (B)) & LAST_BLK_HDR_FLG)
+ ((B)->bhdr & LAST_BLK_HDR_FLG)
#define GET_THIS_FREE_BLK_HDR_FLG(B) \
- (*((Block_t*) (B)) & THIS_FREE_BLK_HDR_FLG)
+ ((B)->bhdr & THIS_FREE_BLK_HDR_FLG)
#define GET_PREV_FREE_BLK_HDR_FLG(B) \
- (*((Block_t*) (B)) & PREV_FREE_BLK_HDR_FLG)
+ ((B)->bhdr & PREV_FREE_BLK_HDR_FLG)
#define GET_BLK_HDR_FLGS(B) \
- (*((Block_t*) (B)) & FLG_MASK)
-
-#define IS_FIRST_BLK(B) \
- (IS_PREV_BLK_FREE((B)) && (PREV_BLK_SZ((B)) == 0))
-#define IS_NOT_FIRST_BLK(B) \
- (!IS_FIRST_BLK((B)))
-
-#define SET_SBC_BLK_FTR(FTR) \
- ((FTR) = (0 | SBC_BLK_FTR_FLG))
-#define SET_MBC_BLK_FTR(FTR) \
- ((FTR) = 0)
+ ((B)->bhdr & FLG_MASK)
#define IS_SBC_BLK(B) \
- (IS_PREV_BLK_FREE((B)) && (((UWord *) (B))[-1] & SBC_BLK_FTR_FLG))
+ (((B)->bhdr & FLG_MASK) == SBC_BLK_HDR_FLG)
#define IS_MBC_BLK(B) \
(!IS_SBC_BLK((B)))
+#define MBC_BLK_SZ(B) (IS_FREE_BLK(B) ? MBC_FBLK_SZ(B) : MBC_ABLK_SZ(B))
+
#define NXT_BLK(B) \
- ((Block_t *) (((char *) (B)) + BLK_SZ((B))))
+ (ASSERT(IS_MBC_BLK(B)), \
+ (Block_t *) (((char *) (B)) + MBC_BLK_SZ((B))))
#define PREV_BLK(B) \
((Block_t *) (((char *) (B)) - PREV_BLK_SZ((B))))
+#define BLK_AFTER(B,Sz) \
+ ((Block_t *) (((char *) (B)) + (Sz)))
+
+#define BLK_SZ(B) ((B)->bhdr & (((B)->bhdr & THIS_FREE_BLK_HDR_FLG) ? MBC_FBLK_SZ_MASK : MBC_ABLK_SZ_MASK))
+
/* Carriers ... */
+#define SBC_HEADER_SIZE (UNIT_CEILING(sizeof(Carrier_t) + ABLK_HDR_SZ) \
+ - ABLK_HDR_SZ)
+#define MBC_HEADER_SIZE(AP) SBC_HEADER_SIZE
+
+
#define MSEG_CARRIER_HDR_FLAG (((UWord) 1) << 0)
#define SBC_CARRIER_HDR_FLAG (((UWord) 1) << 1)
@@ -226,20 +351,20 @@ static Uint mseg_unit_size;
#define SCH_MBC 0
#define SCH_SBC SBC_CARRIER_HDR_FLAG
-#define SET_CARRIER_HDR(C, Sz, F) \
- (ASSERT(((Sz) & FLG_MASK) == 0), (C)->chdr = ((Sz) | (F)))
+#define SET_CARRIER_HDR(C, Sz, F, AP) \
+ (ASSERT(((Sz) & FLG_MASK) == 0), (C)->chdr = ((Sz) | (F)), (C)->allctr = (AP))
-#define BLK2SBC(AP, B) \
- ((Carrier_t *) (((char *) (B)) - (AP)->sbc_header_size))
-#define FBLK2MBC(AP, B) \
- ((Carrier_t *) (((char *) (B)) - (AP)->mbc_header_size))
+#define BLK_TO_SBC(B) \
+ ((Carrier_t *) (((char *) (B)) - SBC_HEADER_SIZE))
+#define FIRST_BLK_TO_MBC(AP, B) \
+ ((Carrier_t *) (((char *) (B)) - MBC_HEADER_SIZE(AP)))
-#define MBC2FBLK(AP, P) \
- ((Block_t *) (((char *) (P)) + (AP)->mbc_header_size))
+#define MBC_TO_FIRST_BLK(AP, P) \
+ ((Block_t *) (((char *) (P)) + MBC_HEADER_SIZE(AP)))
#define SBC2BLK(AP, P) \
- ((Block_t *) (((char *) (P)) + (AP)->sbc_header_size))
+ ((Block_t *) (((char *) (P)) + SBC_HEADER_SIZE))
#define SBC2UMEM(AP, P) \
- ((void *) (((char *) (P)) + ((AP)->sbc_header_size + ABLK_HDR_SZ)))
+ ((void *) (((char *) (P)) + (SBC_HEADER_SIZE + ABLK_HDR_SZ)))
#define IS_MSEG_CARRIER(C) \
((C)->chdr & MSEG_CARRIER_HDR_FLAG)
@@ -250,15 +375,6 @@ static Uint mseg_unit_size;
#define IS_MB_CARRIER(C) \
(!IS_SB_CARRIER((C)))
-#define SET_MSEG_CARRIER(C) \
- ((C)->chdr |= MSEG_CARRIER_HDR_FLAG)
-#define SET_SYS_ALLOC_CARRIER(C) \
- ((C)->chdr &= ~MSEG_CARRIER_HDR_FLAG)
-#define SET_SB_CARRIER(C) \
- ((C)->chdr |= SBC_CARRIER_HDR_FLAG)
-#define SET_MB_CARRIER(C) \
- ((C)->chdr &= ~SBC_CARRIER_HDR_FLAG)
-
#define SET_CARRIER_SZ(C, SZ) \
(ASSERT(((SZ) & FLG_MASK) == 0), \
((C)->chdr = ((C)->chdr & FLG_MASK) | (SZ)))
@@ -506,11 +622,11 @@ static void mbc_free(Allctr_t *allctr, void *p);
#if HAVE_ERTS_MSEG
static ERTS_INLINE void *
-alcu_mseg_alloc(Allctr_t *allctr, Uint *size_p)
+alcu_mseg_alloc(Allctr_t *allctr, Uint *size_p, Uint flags)
{
void *res;
- res = erts_mseg_alloc_opt(allctr->alloc_no, size_p, &allctr->mseg_opt);
+ res = erts_mseg_alloc_opt(allctr->alloc_no, size_p, flags, &allctr->mseg_opt);
INC_CC(allctr->calls.mseg_alloc);
return res;
}
@@ -521,7 +637,7 @@ alcu_mseg_realloc(Allctr_t *allctr, void *seg, Uint old_size, Uint *new_size_p)
void *res;
res = erts_mseg_realloc_opt(allctr->alloc_no, seg, old_size, new_size_p,
- &allctr->mseg_opt);
+ ERTS_MSEG_FLG_NONE, &allctr->mseg_opt);
INC_CC(allctr->calls.mseg_realloc);
return res;
}
@@ -765,13 +881,9 @@ erts_alcu_fix_alloc_shrink(Allctr_t *allctr, erts_aint32_t flgs)
#define ERTS_ALCU_DD_FIX_TYPE_OFFS \
((sizeof(ErtsAllctrDDBlock_t)-1)/sizeof(UWord) + 1)
-#define ERTS_AU_PREF_ALLOC_IX_MASK \
- ((((UWord) 1) << ERTS_AU_PREF_ALLOC_BITS) - 1)
-#define ERTS_AU_PREF_ALLOC_SIZE_MASK \
- ((((UWord) 1) << (sizeof(UWord)*8 - ERTS_AU_PREF_ALLOC_BITS)) - 1)
-static ERTS_INLINE int
-get_pref_allctr(void *extra, Allctr_t **allctr)
+static ERTS_INLINE Allctr_t*
+get_pref_allctr(void *extra)
{
ErtsAllocatorThrSpec_t *tspec = (ErtsAllocatorThrSpec_t *) extra;
int pref_ix;
@@ -781,34 +893,33 @@ get_pref_allctr(void *extra, Allctr_t **allctr)
ASSERT(sizeof(UWord) == sizeof(Allctr_t *));
ASSERT(0 <= pref_ix && pref_ix < tspec->size);
- *allctr = tspec->allctr[pref_ix];
- return pref_ix;
+ return tspec->allctr[pref_ix];
}
-static ERTS_INLINE void *
-get_used_allctr(void *extra, void *p, Allctr_t **allctr, UWord *sizep)
+/* SMP note:
+ * get_used_allctr() must be safe WITHOUT locking the allocator while
+ * concurrent threads may be updating adjacent blocks.
+ * We rely on getting a consistent result (without atomic op) when reading
+ * the block header word even if a concurrent thread is updating
+ * the "PREV_FREE" flag bit.
+ */
+static ERTS_INLINE Allctr_t*
+get_used_allctr(void *extra, void *p, UWord *sizep)
{
- ErtsAllocatorThrSpec_t *tspec = (ErtsAllocatorThrSpec_t *) extra;
- void *ptr = (void *) (((char *) p) - sizeof(UWord));
- UWord ainfo = *((UWord *) ptr);
- int aix = (int) (ainfo & ERTS_AU_PREF_ALLOC_IX_MASK);
- *allctr = tspec->allctr[aix];
- if (sizep)
- *sizep = ((ainfo >> ERTS_AU_PREF_ALLOC_BITS)
- & ERTS_AU_PREF_ALLOC_SIZE_MASK);
- return ptr;
-}
+ Block_t* blk = UMEM2BLK(p);
+ Carrier_t* crr;
-static ERTS_INLINE void *
-put_used_allctr(void *p, int ix, UWord size)
-{
- UWord ainfo = (size >= ERTS_AU_PREF_ALLOC_SIZE_MASK
- ? ERTS_AU_PREF_ALLOC_SIZE_MASK
- : size);
- ainfo <<= ERTS_AU_PREF_ALLOC_BITS;
- ainfo |= (UWord) ix;
- *((UWord *) p) = ainfo;
- return (void *) (((char *) p) + sizeof(UWord));
+ if (IS_SBC_BLK(blk)) {
+ crr = BLK_TO_SBC(blk);
+ if (sizep)
+ *sizep = SBC_BLK_SZ(blk) - ABLK_HDR_SZ;
+ }
+ else {
+ crr = ABLK_TO_MBC(blk);
+ if (sizep)
+ *sizep = MBC_ABLK_SZ(blk) - ABLK_HDR_SZ;
+ }
+ return crr->allctr;
}
static void
@@ -829,49 +940,83 @@ init_dd_queue(ErtsAllctrDDQueue_t *ddq)
ddq->head.used_marker = 1;
}
-static ERTS_INLINE erts_aint_t
-ddq_managed_thread_enqueue(ErtsAllctrDDQueue_t *ddq, void *ptr)
+static ERTS_INLINE int
+ddq_managed_thread_enqueue(ErtsAllctrDDQueue_t *ddq, void *ptr, int cinit)
{
- erts_aint_t first_ilast, ilast, itmp;
- ErtsAllctrDDBlock_t *this = ptr;
+ erts_aint_t itmp;
+ ErtsAllctrDDBlock_t *enq, *this = ptr;
erts_atomic_init_nob(&this->atmc_next, ERTS_AINT_NULL);
-
/* Enqueue at end of list... */
- first_ilast = ilast = erts_atomic_read_nob(&ddq->tail.data.last);
- while (1) {
- ErtsAllctrDDBlock_t *last = (ErtsAllctrDDBlock_t *) ilast;
- itmp = erts_atomic_cmpxchg_mb(&last->atmc_next,
- (erts_aint_t) this,
- ERTS_AINT_NULL);
- if (itmp == ERTS_AINT_NULL)
- break;
- ilast = itmp;
+ enq = (ErtsAllctrDDBlock_t *) erts_atomic_read_nob(&ddq->tail.data.last);
+ itmp = erts_atomic_cmpxchg_relb(&enq->atmc_next,
+ (erts_aint_t) this,
+ ERTS_AINT_NULL);
+ if (itmp == ERTS_AINT_NULL) {
+ /* We are required to move last pointer */
+#ifdef DEBUG
+ ASSERT(ERTS_AINT_NULL == erts_atomic_read_nob(&this->atmc_next));
+ ASSERT(((erts_aint_t) enq)
+ == erts_atomic_xchg_relb(&ddq->tail.data.last,
+ (erts_aint_t) this));
+#else
+ erts_atomic_set_relb(&ddq->tail.data.last, (erts_aint_t) this);
+#endif
+ return 1;
}
+ else {
+ /*
+ * We *need* to insert element somewhere in between the
+ * last element we read earlier and the actual last element.
+ */
+ int i = cinit;
- /* Move last pointer forward... */
- while (1) {
- if (erts_atomic_read_rb(&this->atmc_next) != ERTS_AINT_NULL) {
- ilast = erts_atomic_read_rb(&ddq->tail.data.last);
- if (first_ilast != ilast) {
- /* Someone else will move it forward */
- return ilast;
+ while (1) {
+ erts_aint_t itmp2;
+ erts_atomic_set_nob(&this->atmc_next, itmp);
+ itmp2 = erts_atomic_cmpxchg_relb(&enq->atmc_next,
+ (erts_aint_t) this,
+ itmp);
+ if (itmp == itmp2)
+ return 0; /* inserted this */
+ if ((i & 1) == 0)
+ itmp = itmp2;
+ else {
+ enq = (ErtsAllctrDDBlock_t *) itmp2;
+ itmp = erts_atomic_read_acqb(&enq->atmc_next);
+ ASSERT(itmp != ERTS_AINT_NULL);
}
+ i++;
}
- itmp = erts_atomic_cmpxchg_mb(&ddq->tail.data.last,
- (erts_aint_t) this,
- ilast);
- if (ilast == itmp)
- return (erts_aint_t) this;
- ilast = itmp;
}
}
+static ERTS_INLINE erts_aint_t
+check_insert_marker(ErtsAllctrDDQueue_t *ddq, erts_aint_t ilast)
+{
+ if (!ddq->head.used_marker
+ && ddq->head.unref_end == (ErtsAllctrDDBlock_t *) ilast) {
+ erts_aint_t itmp;
+ ErtsAllctrDDBlock_t *last = (ErtsAllctrDDBlock_t *) ilast;
+
+ erts_atomic_init_nob(&ddq->tail.data.marker.atmc_next, ERTS_AINT_NULL);
+ itmp = erts_atomic_cmpxchg_relb(&last->atmc_next,
+ (erts_aint_t) &ddq->tail.data.marker,
+ ERTS_AINT_NULL);
+ if (itmp == ERTS_AINT_NULL) {
+ ilast = (erts_aint_t) &ddq->tail.data.marker;
+ ddq->head.used_marker = !0;
+ erts_atomic_set_relb(&ddq->tail.data.last, ilast);
+ }
+ }
+ return ilast;
+}
+
static ERTS_INLINE int
-ddq_enqueue(ErtsAlcType_t type, ErtsAllctrDDQueue_t *ddq, void *ptr)
+ddq_enqueue(ErtsAlcType_t type, ErtsAllctrDDQueue_t *ddq, void *ptr, int cinit)
{
- erts_aint_t ilast;
+ int last_elem;
int um_refc_ix = 0;
int managed_thread = erts_thr_progress_is_managed_thread();
if (!managed_thread) {
@@ -887,11 +1032,11 @@ ddq_enqueue(ErtsAlcType_t type, ErtsAllctrDDQueue_t *ddq, void *ptr)
}
}
- ilast = ddq_managed_thread_enqueue(ddq, ptr);
+ last_elem = ddq_managed_thread_enqueue(ddq, ptr, cinit);
if (!managed_thread)
erts_atomic_dec_relb(&ddq->tail.data.um_refc[um_refc_ix]);
- return ilast == (erts_aint_t) ptr;
+ return last_elem;
}
static ERTS_INLINE void *
@@ -937,20 +1082,16 @@ ddq_check_incoming(ErtsAllctrDDQueue_t *ddq)
int um_refc_ix;
ddq->head.next.thr_progress_reached = 1;
um_refc_ix = ddq->head.next.um_refc_ix;
- if (erts_atomic_read_acqb(&ddq->tail.data.um_refc[um_refc_ix]) == 0) {
+ if (erts_atomic_read_nob(&ddq->tail.data.um_refc[um_refc_ix]) == 0) {
/* Move unreferenced end pointer forward... */
+ ETHR_MEMBAR(ETHR_LoadLoad|ETHR_LoadStore);
+
ddq->head.unref_end = ddq->head.next.unref_end;
- if (!ddq->head.used_marker
- && ddq->head.unref_end == (ErtsAllctrDDBlock_t *) ilast) {
- ddq->head.used_marker = 1;
- ilast = ddq_managed_thread_enqueue(ddq, &ddq->tail.data.marker);
- }
+ ilast = check_insert_marker(ddq, ilast);
- if (ddq->head.unref_end == (ErtsAllctrDDBlock_t *) ilast)
- ERTS_THR_MEMORY_BARRIER;
- else {
+ if (ddq->head.unref_end != (ErtsAllctrDDBlock_t *) ilast) {
ddq->head.next.unref_end = (ErtsAllctrDDBlock_t *) ilast;
ddq->head.next.thr_progress = erts_thr_progress_later(NULL);
erts_atomic32_set_relb(&ddq->tail.data.um_refc_ix,
@@ -1095,12 +1236,15 @@ handle_delayed_dealloc(Allctr_t *allctr,
}
static ERTS_INLINE void
-enqueue_dealloc_other_instance(ErtsAlcType_t type, Allctr_t *allctr, void *ptr)
+enqueue_dealloc_other_instance(ErtsAlcType_t type,
+ Allctr_t *allctr,
+ void *ptr,
+ int cinit)
{
if (allctr->fix)
((UWord *) ptr)[ERTS_ALCU_DD_FIX_TYPE_OFFS] = (UWord) type;
- if (ddq_enqueue(type, &allctr->dd.q, ptr))
+ if (ddq_enqueue(type, &allctr->dd.q, ptr, cinit))
erts_alloc_notify_delayed_dealloc(allctr->ix);
}
@@ -1176,10 +1320,8 @@ mbc_alloc_block(Allctr_t *allctr, Uint size, Uint *blk_szp, Uint32 *alcu_flgsp)
if ((*alcu_flgsp) & ERTS_ALCU_FLG_SBMBC)
blk = create_sbmbc(allctr, get_blk_sz);
else {
-#if HALFWORD_HEAP
- blk = create_carrier(allctr, get_blk_sz, CFLG_MBC|CFLG_FORCE_MSEG);
-#else
blk = create_carrier(allctr, get_blk_sz, CFLG_MBC);
+#if !HALFWORD_HEAP && !HAVE_SUPER_ALIGNED_MB_CARRIERS
if (!blk) {
/* Emergency! We couldn't create the carrier as we wanted.
Try to place it in a sys_alloced sbc. */
@@ -1209,6 +1351,7 @@ mbc_alloc_finalize(Allctr_t *allctr,
Block_t *blk,
Uint org_blk_sz,
UWord flags,
+ Carrier_t *crr,
Uint want_blk_sz,
int valid_blk_info,
Uint32 alcu_flgs)
@@ -1229,22 +1372,18 @@ mbc_alloc_finalize(Allctr_t *allctr,
/* Shrink block... */
blk_sz = want_blk_sz;
nxt_blk_sz = org_blk_sz - blk_sz;
- SET_BLK_HDR(blk,
- blk_sz,
- SBH_THIS_ALLOCED|SBH_NOT_LAST_BLK|prev_free_flg);
+ SET_MBC_ABLK_HDR(blk, blk_sz, prev_free_flg, crr);
- nxt_blk = NXT_BLK(blk);
- SET_BLK_HDR(nxt_blk,
- nxt_blk_sz,
- (SBH_THIS_FREE
- | SBH_PREV_ALLOCED
- | (flags & LAST_BLK_HDR_FLG)));
+ nxt_blk = BLK_AFTER(blk, blk_sz);
+ SET_MBC_FBLK_HDR(nxt_blk, nxt_blk_sz,
+ SBH_THIS_FREE|(flags & LAST_BLK_HDR_FLG),
+ crr);
if (!(flags & LAST_BLK_HDR_FLG)) {
SET_BLK_SZ_FTR(nxt_blk, nxt_blk_sz);
if (!valid_blk_info) {
- Block_t *nxt_nxt_blk = NXT_BLK(nxt_blk);
- SET_PREV_BLK_FREE(nxt_nxt_blk);
+ Block_t *nxt_nxt_blk = BLK_AFTER(nxt_blk, nxt_blk_sz);
+ SET_PREV_BLK_FREE(allctr, nxt_nxt_blk);
}
}
(*allctr->link_free_block)(allctr, nxt_blk, alcu_flgs);
@@ -1258,40 +1397,40 @@ mbc_alloc_finalize(Allctr_t *allctr,
|| nxt_blk == PREV_BLK(NXT_BLK(nxt_blk)));
ASSERT((flags & LAST_BLK_HDR_FLG)
|| IS_PREV_BLK_FREE(NXT_BLK(nxt_blk)));
- ASSERT(nxt_blk_sz == BLK_SZ(nxt_blk));
+ ASSERT(nxt_blk_sz == MBC_BLK_SZ(nxt_blk));
ASSERT(nxt_blk_sz % sizeof(Unit_t) == 0);
ASSERT(nxt_blk_sz >= allctr->min_block_size);
+ ASSERT(ABLK_TO_MBC(blk) == crr);
+ ASSERT(FBLK_TO_MBC(nxt_blk) == crr);
}
else {
+ ASSERT(org_blk_sz <= MBC_ABLK_SZ_MASK);
blk_sz = org_blk_sz;
if (flags & LAST_BLK_HDR_FLG) {
if (valid_blk_info)
SET_BLK_ALLOCED(blk);
else
- SET_BLK_HDR(blk,
- blk_sz,
- SBH_THIS_ALLOCED|SBH_LAST_BLK|prev_free_flg);
+ SET_MBC_ABLK_HDR(blk, blk_sz, SBH_LAST_BLK|prev_free_flg, crr);
}
else {
if (valid_blk_info)
SET_BLK_ALLOCED(blk);
else
- SET_BLK_HDR(blk,
- blk_sz,
- SBH_THIS_ALLOCED|SBH_NOT_LAST_BLK|prev_free_flg);
- nxt_blk = NXT_BLK(blk);
+ SET_MBC_ABLK_HDR(blk, blk_sz, prev_free_flg, crr);
+ nxt_blk = BLK_AFTER(blk, blk_sz);
SET_PREV_BLK_ALLOCED(nxt_blk);
}
ASSERT((flags & LAST_BLK_HDR_FLG)
? IS_LAST_BLK(blk)
: IS_NOT_LAST_BLK(blk));
+ ASSERT(ABLK_TO_MBC(blk) == crr);
}
STAT_MBC_BLK_ALLOC(allctr, blk_sz, alcu_flgs);
ASSERT(IS_ALLOCED_BLK(blk));
- ASSERT(blk_sz == BLK_SZ(blk));
+ ASSERT(blk_sz == MBC_BLK_SZ(blk));
ASSERT(blk_sz % sizeof(Unit_t) == 0);
ASSERT(blk_sz >= allctr->min_block_size);
ASSERT(blk_sz >= want_blk_sz);
@@ -1315,8 +1454,9 @@ mbc_alloc(Allctr_t *allctr, Uint size)
if (IS_MBC_BLK(blk))
mbc_alloc_finalize(allctr,
blk,
- BLK_SZ(blk),
+ MBC_FBLK_SZ(blk),
GET_BLK_HDR_FLGS(blk),
+ FBLK_TO_MBC(blk),
blk_sz,
1,
alcu_flgs);
@@ -1337,7 +1477,7 @@ mbc_free(Allctr_t *allctr, void *p)
ASSERT(p);
blk = UMEM2BLK(p);
- blk_sz = BLK_SZ(blk);
+ blk_sz = MBC_ABLK_SZ(blk);
if (blk_sz < allctr->sbmbc_threshold)
alcu_flgs |= ERTS_ALCU_FLG_SBMBC;
@@ -1348,17 +1488,18 @@ mbc_free(Allctr_t *allctr, void *p)
STAT_MBC_BLK_FREE(allctr, blk_sz, alcu_flgs);
- is_first_blk = IS_FIRST_BLK(blk);
+ is_first_blk = IS_MBC_FIRST_ABLK(allctr, blk);
is_last_blk = IS_LAST_BLK(blk);
- if (!is_first_blk && IS_PREV_BLK_FREE(blk)) {
+ if (IS_PREV_BLK_FREE(blk)) {
+ ASSERT(!is_first_blk);
/* Coalesce with previous block... */
blk = PREV_BLK(blk);
(*allctr->unlink_free_block)(allctr, blk, alcu_flgs);
- blk_sz += BLK_SZ(blk);
- is_first_blk = IS_FIRST_BLK(blk);
- SET_BLK_SZ(blk, blk_sz);
+ blk_sz += MBC_FBLK_SZ(blk);
+ is_first_blk = IS_MBC_FIRST_FBLK(allctr, blk);
+ SET_MBC_FBLK_SZ(blk, blk_sz);
}
else {
SET_BLK_FREE(blk);
@@ -1367,12 +1508,12 @@ mbc_free(Allctr_t *allctr, void *p)
if (is_last_blk)
SET_LAST_BLK(blk);
else {
- nxt_blk = NXT_BLK(blk);
+ nxt_blk = BLK_AFTER(blk, blk_sz);
if (IS_FREE_BLK(nxt_blk)) {
/* Coalesce with next block... */
(*allctr->unlink_free_block)(allctr, nxt_blk, alcu_flgs);
- blk_sz += BLK_SZ(nxt_blk);
- SET_BLK_SZ(blk, blk_sz);
+ blk_sz += MBC_FBLK_SZ(nxt_blk);
+ SET_MBC_FBLK_SZ(blk, blk_sz);
is_last_blk = IS_LAST_BLK(nxt_blk);
if (is_last_blk)
@@ -1383,26 +1524,26 @@ mbc_free(Allctr_t *allctr, void *p)
}
}
else {
- SET_PREV_BLK_FREE(nxt_blk);
+ SET_PREV_BLK_FREE(allctr, nxt_blk);
SET_NOT_LAST_BLK(blk);
SET_BLK_SZ_FTR(blk, blk_sz);
}
}
- ASSERT(is_last_blk ? IS_LAST_BLK(blk) : IS_NOT_LAST_BLK(blk));
- ASSERT(is_first_blk ? IS_FIRST_BLK(blk) : IS_NOT_FIRST_BLK(blk));
ASSERT(IS_FREE_BLK(blk));
+ ASSERT(!is_last_blk == !IS_LAST_BLK(blk));
+ ASSERT(!is_first_blk == !IS_MBC_FIRST_FBLK(allctr, blk));
ASSERT(is_first_blk || IS_PREV_BLK_ALLOCED(blk));
ASSERT(is_last_blk || IS_PREV_BLK_FREE(NXT_BLK(blk)));
- ASSERT(blk_sz == BLK_SZ(blk));
+ ASSERT(blk_sz == MBC_BLK_SZ(blk));
ASSERT(is_last_blk || blk == PREV_BLK(NXT_BLK(blk)));
ASSERT(blk_sz % sizeof(Unit_t) == 0);
ASSERT(IS_MBC_BLK(blk));
if (is_first_blk
&& is_last_blk
- && allctr->main_carrier != FBLK2MBC(allctr, blk)) {
+ && allctr->main_carrier != FIRST_BLK_TO_MBC(allctr, blk)) {
if (alcu_flgs & ERTS_ALCU_FLG_SBMBC)
destroy_sbmbc(allctr, blk);
else
@@ -1439,7 +1580,7 @@ mbc_realloc(Allctr_t *allctr, void *p, Uint size, Uint32 alcu_flgs)
ASSERT(size < allctr->sbc_threshold);
blk = (Block_t *) UMEM2BLK(p);
- old_blk_sz = BLK_SZ(blk);
+ old_blk_sz = MBC_ABLK_SZ(blk);
ASSERT(old_blk_sz >= allctr->min_block_size);
@@ -1464,6 +1605,7 @@ mbc_realloc(Allctr_t *allctr, void *p, Uint size, Uint32 alcu_flgs)
return p;
else if (blk_sz < old_blk_sz) {
/* Shrink block... */
+ Carrier_t* crr;
Block_t *nxt_nxt_blk;
Uint diff_sz_val = old_blk_sz - blk_sz;
Uint old_blk_sz_val = old_blk_sz;
@@ -1483,16 +1625,18 @@ mbc_realloc(Allctr_t *allctr, void *p, Uint size, Uint32 alcu_flgs)
return NULL;
cand_blk_sz = old_blk_sz;
- if (!IS_PREV_BLK_FREE(blk) || IS_FIRST_BLK(blk))
+ if (!IS_PREV_BLK_FREE(blk)) {
cand_blk = blk;
+ }
else {
+ ASSERT(!IS_MBC_FIRST_ABLK(allctr, blk));
cand_blk = PREV_BLK(blk);
cand_blk_sz += PREV_BLK_SZ(blk);
}
if (!is_last_blk) {
- nxt_blk = NXT_BLK(blk);
+ nxt_blk = BLK_AFTER(blk, old_blk_sz);
if (IS_FREE_BLK(nxt_blk))
- cand_blk_sz += BLK_SZ(nxt_blk);
+ cand_blk_sz += MBC_FBLK_SZ(nxt_blk);
}
new_blk = (*allctr->get_free_block)(allctr,
@@ -1508,52 +1652,48 @@ mbc_realloc(Allctr_t *allctr, void *p, Uint size, Uint32 alcu_flgs)
nxt_blk_sz = old_blk_sz - blk_sz;
- if ((is_last_blk || IS_ALLOCED_BLK(NXT_BLK(blk)))
+ if ((is_last_blk || IS_ALLOCED_BLK(BLK_AFTER(blk,old_blk_sz)))
&& (nxt_blk_sz < allctr->min_block_size))
return p;
HARD_CHECK_BLK_CARRIER(allctr, blk);
- SET_BLK_SZ(blk, blk_sz);
+ nxt_nxt_blk = BLK_AFTER(blk, old_blk_sz);
+
+ SET_MBC_ABLK_SZ(blk, blk_sz);
SET_NOT_LAST_BLK(blk);
- nxt_blk = NXT_BLK(blk);
- SET_BLK_HDR(nxt_blk,
- nxt_blk_sz,
- SBH_THIS_FREE|SBH_PREV_ALLOCED|SBH_NOT_LAST_BLK);
+ nxt_blk = BLK_AFTER(blk, blk_sz);
STAT_MBC_BLK_FREE(allctr, old_blk_sz, alcu_flgs);
STAT_MBC_BLK_ALLOC(allctr, blk_sz, alcu_flgs);
- ASSERT(BLK_SZ(blk) >= allctr->min_block_size);
+ ASSERT(MBC_BLK_SZ(blk) >= allctr->min_block_size);
- if (is_last_blk)
- SET_LAST_BLK(nxt_blk);
- else {
- nxt_nxt_blk = NXT_BLK(nxt_blk);
+ if (!is_last_blk) {
if (IS_FREE_BLK(nxt_nxt_blk)) {
/* Coalesce with next free block... */
- nxt_blk_sz += BLK_SZ(nxt_nxt_blk);
+ nxt_blk_sz += MBC_FBLK_SZ(nxt_nxt_blk);
(*allctr->unlink_free_block)(allctr, nxt_nxt_blk, alcu_flgs);
- SET_BLK_SZ(nxt_blk, nxt_blk_sz);
- is_last_blk = IS_LAST_BLK(nxt_nxt_blk);
- if (is_last_blk)
- SET_LAST_BLK(nxt_blk);
- else
- SET_BLK_SZ_FTR(nxt_blk, nxt_blk_sz);
+ is_last_blk = GET_LAST_BLK_HDR_FLG(nxt_nxt_blk);
}
else {
- SET_BLK_SZ_FTR(nxt_blk, nxt_blk_sz);
- SET_PREV_BLK_FREE(nxt_nxt_blk);
+ SET_PREV_BLK_FREE(allctr, nxt_nxt_blk);
}
+ SET_BLK_SZ_FTR(nxt_blk, nxt_blk_sz);
}
+ crr = ABLK_TO_MBC(blk);
+ SET_MBC_FBLK_HDR(nxt_blk, nxt_blk_sz,
+ SBH_THIS_FREE | (is_last_blk ? SBH_LAST_BLK : 0),
+ crr);
+
(*allctr->link_free_block)(allctr, nxt_blk, alcu_flgs);
ASSERT(IS_ALLOCED_BLK(blk));
- ASSERT(blk_sz == BLK_SZ(blk));
+ ASSERT(blk_sz == MBC_BLK_SZ(blk));
ASSERT(blk_sz % sizeof(Unit_t) == 0);
ASSERT(blk_sz >= allctr->min_block_size);
ASSERT(blk_sz >= size + ABLK_HDR_SZ);
@@ -1561,14 +1701,15 @@ mbc_realloc(Allctr_t *allctr, void *p, Uint size, Uint32 alcu_flgs)
ASSERT(IS_FREE_BLK(nxt_blk));
ASSERT(IS_PREV_BLK_ALLOCED(nxt_blk));
- ASSERT(nxt_blk_sz == BLK_SZ(nxt_blk));
+ ASSERT(nxt_blk_sz == MBC_BLK_SZ(nxt_blk));
ASSERT(nxt_blk_sz % sizeof(Unit_t) == 0);
ASSERT(nxt_blk_sz >= allctr->min_block_size);
ASSERT(IS_MBC_BLK(nxt_blk));
ASSERT(is_last_blk ? IS_LAST_BLK(nxt_blk) : IS_NOT_LAST_BLK(nxt_blk));
ASSERT(is_last_blk || nxt_blk == PREV_BLK(NXT_BLK(nxt_blk)));
ASSERT(is_last_blk || IS_PREV_BLK_FREE(NXT_BLK(nxt_blk)));
-
+ ASSERT(FBLK_TO_MBC(nxt_blk) == crr);
+
HARD_CHECK_BLK_CARRIER(allctr, blk);
return p;
@@ -1577,8 +1718,8 @@ mbc_realloc(Allctr_t *allctr, void *p, Uint size, Uint32 alcu_flgs)
/* Need larger block... */
if (!is_last_blk) {
- nxt_blk = NXT_BLK(blk);
- nxt_blk_sz = BLK_SZ(nxt_blk);
+ nxt_blk = BLK_AFTER(blk, old_blk_sz);
+ nxt_blk_sz = MBC_BLK_SZ(nxt_blk);
if (IS_FREE_BLK(nxt_blk) && get_blk_sz <= old_blk_sz + nxt_blk_sz) {
/* Grow into next block... */
@@ -1591,7 +1732,7 @@ mbc_realloc(Allctr_t *allctr, void *p, Uint size, Uint32 alcu_flgs)
if (nxt_blk_sz < allctr->min_block_size) {
blk_sz += nxt_blk_sz;
- SET_BLK_SZ(blk, blk_sz);
+ SET_MBC_ABLK_SZ(blk, blk_sz);
if (is_last_blk) {
SET_LAST_BLK(blk);
@@ -1600,21 +1741,20 @@ mbc_realloc(Allctr_t *allctr, void *p, Uint size, Uint32 alcu_flgs)
#endif
}
else {
- nxt_blk = NXT_BLK(blk);
+ nxt_blk = BLK_AFTER(blk, blk_sz);
SET_PREV_BLK_ALLOCED(nxt_blk);
#ifdef DEBUG
is_last_blk = IS_LAST_BLK(nxt_blk);
- nxt_blk_sz = BLK_SZ(nxt_blk);
+ nxt_blk_sz = MBC_BLK_SZ(nxt_blk);
#endif
}
}
else {
- SET_BLK_SZ(blk, blk_sz);
+ Carrier_t* crr = ABLK_TO_MBC(blk);
+ SET_MBC_ABLK_SZ(blk, blk_sz);
- nxt_blk = NXT_BLK(blk);
- SET_BLK_HDR(nxt_blk,
- nxt_blk_sz,
- SBH_THIS_FREE|SBH_PREV_ALLOCED|SBH_NOT_LAST_BLK);
+ nxt_blk = BLK_AFTER(blk, blk_sz);
+ SET_MBC_FBLK_HDR(nxt_blk, nxt_blk_sz, SBH_THIS_FREE, crr);
if (is_last_blk)
SET_LAST_BLK(nxt_blk);
@@ -1624,6 +1764,7 @@ mbc_realloc(Allctr_t *allctr, void *p, Uint size, Uint32 alcu_flgs)
(*allctr->link_free_block)(allctr, nxt_blk, alcu_flgs);
ASSERT(IS_FREE_BLK(nxt_blk));
+ ASSERT(FBLK_TO_MBC(nxt_blk) == crr);
}
STAT_MBC_BLK_FREE(allctr, old_blk_sz, alcu_flgs);
@@ -1631,14 +1772,14 @@ mbc_realloc(Allctr_t *allctr, void *p, Uint size, Uint32 alcu_flgs)
ASSERT(IS_ALLOCED_BLK(blk));
- ASSERT(blk_sz == BLK_SZ(blk));
+ ASSERT(blk_sz == MBC_BLK_SZ(blk));
ASSERT(blk_sz % sizeof(Unit_t) == 0);
ASSERT(blk_sz >= allctr->min_block_size);
ASSERT(blk_sz >= size + ABLK_HDR_SZ);
ASSERT(IS_MBC_BLK(blk));
ASSERT(!nxt_blk || IS_PREV_BLK_ALLOCED(nxt_blk));
- ASSERT(!nxt_blk || nxt_blk_sz == BLK_SZ(nxt_blk));
+ ASSERT(!nxt_blk || nxt_blk_sz == MBC_BLK_SZ(nxt_blk));
ASSERT(!nxt_blk || nxt_blk_sz % sizeof(Unit_t) == 0);
ASSERT(!nxt_blk || nxt_blk_sz >= allctr->min_block_size);
ASSERT(!nxt_blk || IS_MBC_BLK(nxt_blk));
@@ -1663,18 +1804,19 @@ mbc_realloc(Allctr_t *allctr, void *p, Uint size, Uint32 alcu_flgs)
/* Need to grow in another block */
- if (!IS_PREV_BLK_FREE(blk) || IS_FIRST_BLK(blk)) {
+ if (!IS_PREV_BLK_FREE(blk)) {
cand_blk = NULL;
cand_blk_sz = 0;
}
else {
+ ASSERT(!IS_MBC_FIRST_ABLK(allctr, blk));
cand_blk = PREV_BLK(blk);
cand_blk_sz = old_blk_sz + PREV_BLK_SZ(blk);
if (!is_last_blk) {
- nxt_blk = NXT_BLK(blk);
+ nxt_blk = BLK_AFTER(blk, old_blk_sz);
if (IS_FREE_BLK(nxt_blk))
- cand_blk_sz += BLK_SZ(nxt_blk);
+ cand_blk_sz += MBC_FBLK_SZ(nxt_blk);
}
}
@@ -1710,8 +1852,9 @@ mbc_realloc(Allctr_t *allctr, void *p, Uint size, Uint32 alcu_flgs)
if (new_blk) {
mbc_alloc_finalize(allctr,
new_blk,
- BLK_SZ(new_blk),
+ MBC_FBLK_SZ(new_blk),
GET_BLK_HDR_FLGS(new_blk),
+ FBLK_TO_MBC(new_blk),
blk_sz,
1,
alcu_flgs);
@@ -1721,6 +1864,7 @@ mbc_realloc(Allctr_t *allctr, void *p, Uint size, Uint32 alcu_flgs)
return new_p;
}
else {
+ Carrier_t* crr;
Uint new_blk_sz;
UWord new_blk_flgs;
Uint prev_blk_sz;
@@ -1741,10 +1885,10 @@ mbc_realloc(Allctr_t *allctr, void *p, Uint size, Uint32 alcu_flgs)
if (is_last_blk)
new_blk_flgs |= LAST_BLK_HDR_FLG;
else {
- nxt_blk = NXT_BLK(blk);
+ nxt_blk = BLK_AFTER(blk, old_blk_sz);
if (IS_FREE_BLK(nxt_blk)) {
new_blk_flgs |= GET_LAST_BLK_HDR_FLG(nxt_blk);
- new_blk_sz += BLK_SZ(nxt_blk);
+ new_blk_sz += MBC_FBLK_SZ(nxt_blk);
(*allctr->unlink_free_block)(allctr, nxt_blk, alcu_flgs);
}
}
@@ -1757,6 +1901,7 @@ mbc_realloc(Allctr_t *allctr, void *p, Uint size, Uint32 alcu_flgs)
new_p = BLK2UMEM(new_blk);
blk_cpy_sz = MIN(blk_sz, old_blk_sz);
+ crr = FBLK_TO_MBC(new_blk);
if (prev_blk_sz >= blk_cpy_sz)
sys_memcpy(new_p, p, blk_cpy_sz - ABLK_HDR_SZ);
@@ -1767,6 +1912,7 @@ mbc_realloc(Allctr_t *allctr, void *p, Uint size, Uint32 alcu_flgs)
new_blk,
new_blk_sz,
new_blk_flgs,
+ crr,
blk_sz,
0,
alcu_flgs);
@@ -1782,35 +1928,40 @@ mbc_realloc(Allctr_t *allctr, void *p, Uint size, Uint32 alcu_flgs)
#ifdef DEBUG
#if HAVE_ERTS_MSEG
-#define ASSERT_MSEG_UNIT_SIZE_MULTIPLE(CSZ) ASSERT((CSZ) % mseg_unit_size == 0)
+#define ASSERT_MSEG_UNIT_SIZE_MULTIPLE(CSZ) ASSERT((CSZ) % MSEG_UNIT_SZ == 0)
#else
#define ASSERT_MSEG_UNIT_SIZE_MULTIPLE(CSZ)
#endif
-#define CHECK_1BLK_CARRIER(A, SBC, MSEGED, C, CSZ, B, BSZ) \
-do { \
- ASSERT(IS_FIRST_BLK((B))); \
- ASSERT(IS_LAST_BLK((B))); \
- ASSERT((CSZ) == CARRIER_SZ((C))); \
- ASSERT((BSZ) == BLK_SZ((B))); \
- ASSERT((BSZ) % sizeof(Unit_t) == 0); \
- if ((SBC)) { \
- ASSERT(IS_SBC_BLK((B))); \
- ASSERT(IS_SB_CARRIER((C))); \
- } \
- else { \
- ASSERT(IS_MBC_BLK((B))); \
- ASSERT(IS_MB_CARRIER((C))); \
- } \
- if ((MSEGED)) { \
- ASSERT(IS_MSEG_CARRIER((C))); \
- ASSERT_MSEG_UNIT_SIZE_MULTIPLE((CSZ)); \
- } \
- else { \
- ASSERT(IS_SYS_ALLOC_CARRIER((C))); \
- ASSERT((CSZ) % sizeof(Unit_t) == 0); \
- } \
-} while (0)
+static void CHECK_1BLK_CARRIER(Allctr_t* A, int SBC, int MSEGED, Carrier_t* C,
+ UWord CSZ, Block_t* B, UWord BSZ)
+{
+ ASSERT(IS_LAST_BLK((B)));
+ ASSERT((CSZ) == CARRIER_SZ((C)));
+ ASSERT((BSZ) % sizeof(Unit_t) == 0);
+ if ((SBC)) {
+ ASSERT((BSZ) == SBC_BLK_SZ((B)));
+ ASSERT((char*)B == (char*)C + SBC_HEADER_SIZE);
+ ASSERT(IS_SBC_BLK((B)));
+ ASSERT(IS_SB_CARRIER((C)));
+ }
+ else {
+ ASSERT(IS_FREE_BLK(B));
+ ASSERT((BSZ) == MBC_FBLK_SZ((B)));
+ ASSERT(IS_MBC_FIRST_FBLK(A, (B)));
+ ASSERT(IS_MBC_BLK((B)));
+ ASSERT(IS_MB_CARRIER((C)));
+ ASSERT(FBLK_TO_MBC(B) == (C));
+ }
+ if ((MSEGED)) {
+ ASSERT(IS_MSEG_CARRIER((C)));
+ ASSERT_MSEG_UNIT_SIZE_MULTIPLE((CSZ));
+ }
+ else {
+ ASSERT(IS_SYS_ALLOC_CARRIER((C)));
+ ASSERT((CSZ) % sizeof(Unit_t) == 0);
+ }
+}
#else
#define CHECK_1BLK_CARRIER(A, SBC, MSEGED, C, CSZ, B, BSZ)
@@ -1832,37 +1983,18 @@ create_sbmbc(Allctr_t *allctr, Uint umem_sz)
crr = erts_alloc(ERTS_ALC_T_SBMBC, crr_sz);
INC_CC(allctr->calls.sbmbc_alloc);
- SET_CARRIER_HDR(crr, crr_sz, SCH_SYS_ALLOC|SCH_MBC);
-
- blk = MBC2FBLK(allctr, crr);
-
-#ifdef ERTS_ALLOC_UTIL_HARD_DEBUG
- if (allctr->mbc_header_size % sizeof(Unit_t) == 0)
- crr_sz -= sizeof(UWord);
-#endif
+ SET_CARRIER_HDR(crr, crr_sz, SCH_SYS_ALLOC|SCH_MBC, allctr);
- blk_sz = UNIT_FLOOR(crr_sz - allctr->mbc_header_size);
+ blk = MBC_TO_FIRST_BLK(allctr, crr);
- SET_MBC_BLK_FTR(((UWord *) blk)[-1]);
- SET_BLK_HDR(blk, blk_sz, SBH_THIS_FREE|SBH_PREV_FREE|SBH_LAST_BLK);
+ blk_sz = UNIT_FLOOR(crr_sz - MBC_HEADER_SIZE(allctr));
-#ifdef ERTS_ALLOC_UTIL_HARD_DEBUG
- *((Carrier_t **) NXT_BLK(blk)) = crr;
-#endif
+ SET_MBC_FBLK_HDR(blk, blk_sz, SBH_THIS_FREE|SBH_LAST_BLK, crr);
link_carrier(&allctr->sbmbc_list, crr);
-#ifdef ERTS_ALLOC_UTIL_HARD_DEBUG
- if (allctr->mbc_header_size % sizeof(Unit_t) == 0)
- crr_sz += sizeof(UWord);
-#endif
-
STAT_SBMBC_ALLOC(allctr, crr_sz);
CHECK_1BLK_CARRIER(allctr, 0, 0, crr, crr_sz, blk, blk_sz);
-#ifdef ERTS_ALLOC_UTIL_HARD_DEBUG
- if (allctr->mbc_header_size % sizeof(Unit_t) == 0)
- crr_sz -= sizeof(UWord);
-#endif
if (allctr->creating_mbc)
(*allctr->creating_mbc)(allctr, crr, ERTS_ALCU_FLG_SBMBC);
@@ -1876,11 +2008,10 @@ destroy_sbmbc(Allctr_t *allctr, Block_t *blk)
Uint crr_sz;
Carrier_t *crr;
- ASSERT(IS_FIRST_BLK(blk));
-
ASSERT(IS_MBC_BLK(blk));
+ ASSERT(IS_MBC_FIRST_FBLK(allctr, blk));
- crr = FBLK2MBC(allctr, blk);
+ crr = FIRST_BLK_TO_MBC(allctr, blk);
crr_sz = CARRIER_SZ(crr);
#ifdef DEBUG
@@ -1919,14 +2050,25 @@ create_carrier(Allctr_t *allctr, Uint umem_sz, UWord flags)
Uint blk_sz, bcrr_sz, crr_sz;
#if HAVE_ERTS_MSEG
int have_tried_sys_alloc = 0, have_tried_mseg = 0;
+ Uint mseg_flags;
#endif
#ifdef DEBUG
int is_mseg = 0;
#endif
+#if HALFWORD_HEAP
+ flags |= CFLG_FORCE_MSEG;
+#elif HAVE_SUPER_ALIGNED_MB_CARRIERS
+ if (flags & CFLG_MBC) {
+ flags |= CFLG_FORCE_MSEG;
+ }
+#endif
+
ASSERT((flags & CFLG_SBC && !(flags & CFLG_MBC))
|| (flags & CFLG_MBC && !(flags & CFLG_SBC)));
+ ASSERT(!(flags & CFLG_FORCE_MSEG && flags & CFLG_FORCE_SYS_ALLOC));
+
blk_sz = UMEMSZ2BLKSZ(allctr, umem_sz);
#if HAVE_ERTS_MSEG
@@ -1941,29 +2083,27 @@ create_carrier(Allctr_t *allctr, Uint umem_sz, UWord flags)
if (allctr->sbcs.curr.norm.mseg.no >= allctr->max_mseg_sbcs)
goto try_sys_alloc;
}
+#if !HAVE_SUPER_ALIGNED_MB_CARRIERS
else {
if (allctr->mbcs.curr.norm.mseg.no >= allctr->max_mseg_mbcs)
goto try_sys_alloc;
}
+#endif
try_mseg:
if (flags & CFLG_SBC) {
- crr_sz = blk_sz + allctr->sbc_header_size;
+ crr_sz = blk_sz + SBC_HEADER_SIZE;
+ mseg_flags = ERTS_MSEG_FLG_NONE;
}
else {
crr_sz = (*allctr->get_next_mbc_size)(allctr);
- if (crr_sz < allctr->mbc_header_size + blk_sz)
- crr_sz = allctr->mbc_header_size + blk_sz;
-#ifdef ERTS_ALLOC_UTIL_HARD_DEBUG
- if (allctr->mbc_header_size % sizeof(Unit_t) == 0)
- crr_sz += sizeof(UWord);
-#endif
+ if (crr_sz < MBC_HEADER_SIZE(allctr) + blk_sz)
+ crr_sz = MBC_HEADER_SIZE(allctr) + blk_sz;
+ mseg_flags = ERTS_MSEG_FLG_2POW;
}
- crr_sz = MSEG_UNIT_CEILING(crr_sz);
- ASSERT(crr_sz % mseg_unit_size == 0);
- crr = (Carrier_t *) alcu_mseg_alloc(allctr, &crr_sz);
+ crr = (Carrier_t *) alcu_mseg_alloc(allctr, &crr_sz, mseg_flags);
if (!crr) {
have_tried_mseg = 1;
if (!(have_tried_sys_alloc || flags & CFLG_FORCE_MSEG))
@@ -1975,32 +2115,31 @@ create_carrier(Allctr_t *allctr, Uint umem_sz, UWord flags)
is_mseg = 1;
#endif
if (flags & CFLG_SBC) {
- SET_CARRIER_HDR(crr, crr_sz, SCH_MSEG|SCH_SBC);
+ SET_CARRIER_HDR(crr, crr_sz, SCH_MSEG|SCH_SBC, allctr);
STAT_MSEG_SBC_ALLOC(allctr, crr_sz, blk_sz);
goto sbc_final_touch;
}
else {
- SET_CARRIER_HDR(crr, crr_sz, SCH_MSEG|SCH_MBC);
+#ifndef ARCH_64
+ ASSERT(crr_sz <= MBC_SZ_MAX_LIMIT);
+#endif
+ SET_CARRIER_HDR(crr, crr_sz, SCH_MSEG|SCH_MBC, allctr);
STAT_MSEG_MBC_ALLOC(allctr, crr_sz);
goto mbc_final_touch;
}
try_sys_alloc:
+
#endif /* #if HAVE_ERTS_MSEG */
if (flags & CFLG_SBC) {
- bcrr_sz = blk_sz + allctr->sbc_header_size;
+ bcrr_sz = blk_sz + SBC_HEADER_SIZE;
}
else {
- bcrr_sz = allctr->mbc_header_size + blk_sz;
+ bcrr_sz = MBC_HEADER_SIZE(allctr) + blk_sz;
if (!(flags & CFLG_MAIN_CARRIER)
&& bcrr_sz < allctr->smallest_mbc_size)
bcrr_sz = allctr->smallest_mbc_size;
-#ifdef ERTS_ALLOC_UTIL_HARD_DEBUG
- if (allctr->mbc_header_size % sizeof(Unit_t) == 0)
- bcrr_sz += sizeof(UWord);
-#endif
-
}
crr_sz = (flags & CFLG_FORCE_SIZE
@@ -2024,7 +2163,7 @@ create_carrier(Allctr_t *allctr, Uint umem_sz, UWord flags)
}
}
if (flags & CFLG_SBC) {
- SET_CARRIER_HDR(crr, crr_sz, SCH_SYS_ALLOC|SCH_SBC);
+ SET_CARRIER_HDR(crr, crr_sz, SCH_SYS_ALLOC|SCH_SBC, allctr);
STAT_SYS_ALLOC_SBC_ALLOC(allctr, crr_sz, blk_sz);
#if HAVE_ERTS_MSEG
@@ -2033,8 +2172,7 @@ create_carrier(Allctr_t *allctr, Uint umem_sz, UWord flags)
blk = SBC2BLK(allctr, crr);
- SET_SBC_BLK_FTR(((UWord *) blk)[-1]);
- SET_BLK_HDR(blk, blk_sz, SBH_THIS_ALLOCED|SBH_PREV_FREE|SBH_LAST_BLK);
+ SET_SBC_BLK_HDR(blk, blk_sz);
link_carrier(&allctr->sbc_list, crr);
@@ -2042,28 +2180,18 @@ create_carrier(Allctr_t *allctr, Uint umem_sz, UWord flags)
}
else {
- SET_CARRIER_HDR(crr, crr_sz, SCH_SYS_ALLOC|SCH_MBC);
+ SET_CARRIER_HDR(crr, crr_sz, SCH_SYS_ALLOC|SCH_MBC, allctr);
STAT_SYS_ALLOC_MBC_ALLOC(allctr, crr_sz);
#if HAVE_ERTS_MSEG
mbc_final_touch:
#endif
- blk = MBC2FBLK(allctr, crr);
+ blk = MBC_TO_FIRST_BLK(allctr, crr);
-#ifdef ERTS_ALLOC_UTIL_HARD_DEBUG
- if (allctr->mbc_header_size % sizeof(Unit_t) == 0)
- crr_sz -= sizeof(UWord);
-#endif
+ blk_sz = UNIT_FLOOR(crr_sz - MBC_HEADER_SIZE(allctr));
- blk_sz = UNIT_FLOOR(crr_sz - allctr->mbc_header_size);
-
- SET_MBC_BLK_FTR(((UWord *) blk)[-1]);
- SET_BLK_HDR(blk, blk_sz, SBH_THIS_FREE|SBH_PREV_FREE|SBH_LAST_BLK);
-
-#ifdef ERTS_ALLOC_UTIL_HARD_DEBUG
- *((Carrier_t **) NXT_BLK(blk)) = crr;
-#endif
+ SET_MBC_FBLK_HDR(blk, blk_sz, SBH_THIS_FREE|SBH_LAST_BLK, crr);
if (flags & CFLG_MAIN_CARRIER) {
ASSERT(!allctr->main_carrier);
@@ -2072,15 +2200,7 @@ create_carrier(Allctr_t *allctr, Uint umem_sz, UWord flags)
link_carrier(&allctr->mbc_list, crr);
-#ifdef ERTS_ALLOC_UTIL_HARD_DEBUG
- if (allctr->mbc_header_size % sizeof(Unit_t) == 0)
- crr_sz += sizeof(UWord);
-#endif
CHECK_1BLK_CARRIER(allctr, 0, is_mseg, crr, crr_sz, blk, blk_sz);
-#ifdef ERTS_ALLOC_UTIL_HARD_DEBUG
- if (allctr->mbc_header_size % sizeof(Unit_t) == 0)
- crr_sz -= sizeof(UWord);
-#endif
if (allctr->creating_mbc)
(*allctr->creating_mbc)(allctr, crr, 0);
@@ -2109,8 +2229,8 @@ resize_carrier(Allctr_t *allctr, Block_t *old_blk, Uint umem_sz, UWord flags)
HARD_CHECK_BLK_CARRIER(allctr, old_blk);
- old_blk_sz = BLK_SZ(old_blk);
- old_crr = BLK2SBC(allctr, old_blk);
+ old_blk_sz = SBC_BLK_SZ(old_blk);
+ old_crr = BLK_TO_SBC(old_blk);
old_crr_sz = CARRIER_SZ(old_crr);
ASSERT(IS_SB_CARRIER(old_crr));
ASSERT(IS_SBC_BLK(old_blk));
@@ -2124,7 +2244,7 @@ resize_carrier(Allctr_t *allctr, Block_t *old_blk, Uint umem_sz, UWord flags)
if (!(flags & CFLG_FORCE_SYS_ALLOC)) {
- new_crr_sz = new_blk_sz + allctr->sbc_header_size;
+ new_crr_sz = new_blk_sz + SBC_HEADER_SIZE;
new_crr_sz = MSEG_UNIT_CEILING(new_crr_sz);
new_crr = (Carrier_t *) alcu_mseg_realloc(allctr,
old_crr,
@@ -2133,7 +2253,7 @@ resize_carrier(Allctr_t *allctr, Block_t *old_blk, Uint umem_sz, UWord flags)
if (new_crr) {
SET_CARRIER_SZ(new_crr, new_crr_sz);
new_blk = SBC2BLK(allctr, new_crr);
- SET_BLK_SZ(new_blk, new_blk_sz);
+ SET_SBC_BLK_SZ(new_blk, new_blk_sz);
STAT_MSEG_SBC_ALLOC(allctr, new_crr_sz, new_blk_sz);
relink_carrier(&allctr->sbc_list, new_crr);
CHECK_1BLK_CARRIER(allctr, 1, 1, new_crr, new_crr_sz,
@@ -2141,6 +2261,11 @@ resize_carrier(Allctr_t *allctr, Block_t *old_blk, Uint umem_sz, UWord flags)
DEBUG_SAVE_ALIGNMENT(new_crr);
return new_blk;
}
+#if HALFWORD_HEAP
+ /* Old carrier unchanged; restore stat */
+ STAT_MSEG_SBC_ALLOC(allctr, old_crr_sz, old_blk_sz);
+ return NULL;
+#endif
create_flags |= CFLG_FORCE_SYS_ALLOC; /* since mseg_realloc()
failed */
}
@@ -2163,7 +2288,7 @@ resize_carrier(Allctr_t *allctr, Block_t *old_blk, Uint umem_sz, UWord flags)
else {
if (!(flags & CFLG_FORCE_MSEG)) {
#endif /* #if HAVE_ERTS_MSEG */
- new_bcrr_sz = new_blk_sz + allctr->sbc_header_size;
+ new_bcrr_sz = new_blk_sz + SBC_HEADER_SIZE;
new_crr_sz = (flags & CFLG_FORCE_SIZE
? UNIT_CEILING(new_bcrr_sz)
: SYS_ALLOC_CARRIER_CEILING(new_bcrr_sz));
@@ -2175,7 +2300,7 @@ resize_carrier(Allctr_t *allctr, Block_t *old_blk, Uint umem_sz, UWord flags)
sys_realloc_success:
SET_CARRIER_SZ(new_crr, new_crr_sz);
new_blk = SBC2BLK(allctr, new_crr);
- SET_BLK_SZ(new_blk, new_blk_sz);
+ SET_SBC_BLK_SZ(new_blk, new_blk_sz);
STAT_SYS_ALLOC_SBC_FREE(allctr, old_crr_sz, old_blk_sz);
STAT_SYS_ALLOC_SBC_ALLOC(allctr, new_crr_sz, new_blk_sz);
relink_carrier(&allctr->sbc_list, new_crr);
@@ -2185,7 +2310,7 @@ resize_carrier(Allctr_t *allctr, Block_t *old_blk, Uint umem_sz, UWord flags)
return new_blk;
}
else if (new_crr_sz > UNIT_CEILING(new_bcrr_sz)) {
- new_crr_sz = new_blk_sz + allctr->sbc_header_size;
+ new_crr_sz = new_blk_sz + SBC_HEADER_SIZE;
new_crr_sz = UNIT_CEILING(new_crr_sz);
new_crr = (Carrier_t *) alcu_sys_realloc(allctr,
(void *) old_crr,
@@ -2229,11 +2354,9 @@ destroy_carrier(Allctr_t *allctr, Block_t *blk)
Uint is_mseg = 0;
#endif
- ASSERT(IS_FIRST_BLK(blk));
-
if (IS_SBC_BLK(blk)) {
- Uint blk_sz = BLK_SZ(blk);
- crr = BLK2SBC(allctr, blk);
+ Uint blk_sz = SBC_BLK_SZ(blk);
+ crr = BLK_TO_SBC(blk);
crr_sz = CARRIER_SZ(crr);
ASSERT(IS_LAST_BLK(blk));
@@ -2243,7 +2366,7 @@ destroy_carrier(Allctr_t *allctr, Block_t *blk)
#if HAVE_ERTS_MSEG
if (IS_MSEG_CARRIER(crr)) {
is_mseg++;
- ASSERT(crr_sz % mseg_unit_size == 0);
+ ASSERT(crr_sz % MSEG_UNIT_SZ == 0);
STAT_MSEG_SBC_FREE(allctr, crr_sz, blk_sz);
}
else
@@ -2254,7 +2377,8 @@ destroy_carrier(Allctr_t *allctr, Block_t *blk)
}
else {
- crr = FBLK2MBC(allctr, blk);
+ ASSERT(IS_MBC_FIRST_FBLK(allctr, blk));
+ crr = FIRST_BLK_TO_MBC(allctr, blk);
crr_sz = CARRIER_SZ(crr);
#ifdef DEBUG
@@ -2272,7 +2396,7 @@ destroy_carrier(Allctr_t *allctr, Block_t *blk)
#if HAVE_ERTS_MSEG
if (IS_MSEG_CARRIER(crr)) {
is_mseg++;
- ASSERT(crr_sz % mseg_unit_size == 0);
+ ASSERT(crr_sz % MSEG_UNIT_SZ == 0);
STAT_MSEG_MBC_FREE(allctr, crr_sz);
}
else
@@ -2782,10 +2906,10 @@ make_name_atoms(Allctr_t *allctr)
char alloc[] = "alloc";
char realloc[] = "realloc";
char free[] = "free";
- char buf[MAX_ATOM_LENGTH];
+ char buf[MAX_ATOM_CHARACTERS];
size_t prefix_len = strlen(allctr->name_prefix);
- if (prefix_len > MAX_ATOM_LENGTH + sizeof(realloc) - 1)
+ if (prefix_len > MAX_ATOM_CHARACTERS + sizeof(realloc) - 1)
erl_exit(1,"Too long allocator name: %salloc\n",allctr->name_prefix);
memcpy((void *) buf, (void *) allctr->name_prefix, prefix_len);
@@ -3397,12 +3521,7 @@ do_erts_alcu_alloc(ErtsAlcType_t type, void *extra, Uint size)
if (allctr->dd.use)
ERTS_ALCU_HANDLE_DD_IN_OP(allctr, 1);
#endif
-#if HALFWORD_HEAP
- blk = create_carrier(allctr, size,
- CFLG_SBC | CFLG_FORCE_MSEG);
-#else
blk = create_carrier(allctr, size, CFLG_SBC);
-#endif
res = blk ? BLK2UMEM(blk) : NULL;
}
else
@@ -3473,24 +3592,20 @@ erts_alcu_alloc_thr_spec(ErtsAlcType_t type, void *extra, Uint size)
void *
erts_alcu_alloc_thr_pref(ErtsAlcType_t type, void *extra, Uint size)
{
- int pref_ix;
Allctr_t *pref_allctr;
void *res;
- pref_ix = get_pref_allctr(extra, &pref_allctr);
+ pref_allctr = get_pref_allctr(extra);
if (pref_allctr->thread_safe)
erts_mtx_lock(&pref_allctr->mutex);
ERTS_ALCU_DBG_CHK_THR_ACCESS(pref_allctr);
- res = do_erts_alcu_alloc(type, pref_allctr, size + sizeof(UWord));
+ res = do_erts_alcu_alloc(type, pref_allctr, size);
if (pref_allctr->thread_safe)
erts_mtx_unlock(&pref_allctr->mutex);
- if (res)
- res = put_used_allctr(res, pref_ix, size);
-
DEBUG_CHECK_ALIGNMENT(res);
@@ -3611,17 +3726,20 @@ erts_alcu_free_thr_pref(ErtsAlcType_t type, void *extra, void *p)
{
if (p) {
Allctr_t *pref_allctr, *used_allctr;
- void *ptr;
- get_pref_allctr(extra, &pref_allctr);
- ptr = get_used_allctr(extra, p, &used_allctr, NULL);
+ pref_allctr = get_pref_allctr(extra);
+ used_allctr = get_used_allctr(extra, p, NULL);
if (pref_allctr != used_allctr)
- enqueue_dealloc_other_instance(type, used_allctr, ptr);
+ enqueue_dealloc_other_instance(type,
+ used_allctr,
+ p,
+ (used_allctr->dd.ix
+ - pref_allctr->dd.ix));
else {
if (used_allctr->thread_safe)
erts_mtx_lock(&used_allctr->mutex);
ERTS_ALCU_DBG_CHK_THR_ACCESS(used_allctr);
- do_erts_alcu_free(type, used_allctr, ptr);
+ do_erts_alcu_free(type, used_allctr, p);
if (used_allctr->thread_safe)
erts_mtx_unlock(&used_allctr->mutex);
}
@@ -3702,13 +3820,13 @@ do_erts_alcu_realloc(ErtsAlcType_t type,
if (IS_MBC_BLK(blk))
res = mbc_realloc(allctr, p, size, alcu_flgs);
else {
- Uint used_sz = allctr->sbc_header_size + ABLK_HDR_SZ + size;
+ Uint used_sz = SBC_HEADER_SIZE + ABLK_HDR_SZ + size;
Uint crr_sz;
Uint diff_sz_val;
Uint crr_sz_val;
#if HAVE_ERTS_MSEG
- if (IS_SYS_ALLOC_CARRIER(BLK2SBC(allctr, blk)))
+ if (IS_SYS_ALLOC_CARRIER(BLK_TO_SBC(blk)))
#endif
crr_sz = SYS_ALLOC_CARRIER_CEILING(used_sz);
#if HAVE_ERTS_MSEG
@@ -3738,7 +3856,7 @@ do_erts_alcu_realloc(ErtsAlcType_t type,
if (res) {
sys_memcpy((void*) res,
(void*) p,
- MIN(BLK_SZ(blk) - ABLK_HDR_SZ, size));
+ MIN(SBC_BLK_SZ(blk) - ABLK_HDR_SZ, size));
destroy_carrier(allctr, blk);
}
}
@@ -3761,16 +3879,12 @@ do_erts_alcu_realloc(ErtsAlcType_t type,
else if (alcu_flgs & ERTS_ALCU_FLG_FAIL_REALLOC_MOVE)
return NULL;
else {
-#if HALFWORD_HEAP
- new_blk = create_carrier(allctr, size, CFLG_SBC | CFLG_FORCE_MSEG);
-#else
new_blk = create_carrier(allctr, size, CFLG_SBC);
-#endif
if (new_blk) {
res = BLK2UMEM(new_blk);
sys_memcpy((void *) res,
(void *) p,
- MIN(BLK_SZ(blk) - ABLK_HDR_SZ, size));
+ MIN(MBC_ABLK_SZ(blk) - ABLK_HDR_SZ, size));
mbc_free(allctr, p);
}
else
@@ -3929,16 +4043,15 @@ static ERTS_INLINE void *
realloc_thr_pref(ErtsAlcType_t type, void *extra, void *p, Uint size,
int force_move)
{
- int pref_ix;
- void *ptr, *res;
+ void *res;
Allctr_t *pref_allctr, *used_allctr;
UWord old_user_size;
if (!p)
return erts_alcu_alloc_thr_pref(type, extra, size);
- pref_ix = get_pref_allctr(extra, &pref_allctr);
- ptr = get_used_allctr(extra, p, &used_allctr, &old_user_size);
+ pref_allctr = get_pref_allctr(extra);
+ used_allctr = get_used_allctr(extra, p, &old_user_size);
ASSERT(used_allctr && pref_allctr);
@@ -3948,52 +4061,33 @@ realloc_thr_pref(ErtsAlcType_t type, void *extra, void *p, Uint size,
ERTS_ALCU_DBG_CHK_THR_ACCESS(used_allctr);
res = do_erts_alcu_realloc(type,
used_allctr,
- ptr,
- size + sizeof(UWord),
+ p,
+ size,
0);
if (used_allctr->thread_safe)
erts_mtx_unlock(&used_allctr->mutex);
- if (res)
- res = put_used_allctr(res, pref_ix, size);
}
else {
if (pref_allctr->thread_safe)
erts_mtx_lock(&pref_allctr->mutex);
- res = do_erts_alcu_alloc(type, pref_allctr, size + sizeof(UWord));
- if (pref_allctr->thread_safe && (!force_move
- || used_allctr != pref_allctr))
+ res = do_erts_alcu_alloc(type, pref_allctr, size);
+ if (pref_allctr->thread_safe && used_allctr != pref_allctr) {
erts_mtx_unlock(&pref_allctr->mutex);
+ }
if (res) {
- Block_t *blk;
- size_t cpy_size;
-
- res = put_used_allctr(res, pref_ix, size);
-
DEBUG_CHECK_ALIGNMENT(res);
- blk = UMEM2BLK(ptr);
- if (old_user_size != ERTS_AU_PREF_ALLOC_SIZE_MASK)
- cpy_size = old_user_size;
- else {
- if (used_allctr->thread_safe && (!force_move
- || used_allctr != pref_allctr))
- erts_mtx_lock(&used_allctr->mutex);
- ERTS_SMP_LC_ASSERT(!used_allctr->thread_safe ||
- erts_lc_mtx_is_locked(&used_allctr->mutex));
- cpy_size = BLK_SZ(blk);
- if (used_allctr->thread_safe && (!force_move
- || used_allctr != pref_allctr))
- erts_mtx_unlock(&used_allctr->mutex);
- cpy_size -= ABLK_HDR_SZ + sizeof(UWord);
- }
- if (cpy_size > size)
- cpy_size = size;
- sys_memcpy(res, p, cpy_size);
+ sys_memcpy(res, p, MIN(size,old_user_size));
- if (!force_move || used_allctr != pref_allctr)
- enqueue_dealloc_other_instance(type, used_allctr, ptr);
+ if (used_allctr != pref_allctr) {
+ enqueue_dealloc_other_instance(type,
+ used_allctr,
+ p,
+ (used_allctr->dd.ix
+ - pref_allctr->dd.ix));
+ }
else {
- do_erts_alcu_free(type, used_allctr, ptr);
+ do_erts_alcu_free(type, used_allctr, p);
ASSERT(pref_allctr == used_allctr);
if (pref_allctr->thread_safe)
erts_mtx_unlock(&pref_allctr->mutex);
@@ -4070,7 +4164,7 @@ erts_alcu_start(Allctr_t *allctr, AllctrInit_t *init)
allctr->ramv = init->ramv;
allctr->main_carrier_size = init->mmbcs;
- allctr->sbc_threshold = init->sbct;
+
#if HAVE_ERTS_MSEG
allctr->mseg_opt.abs_shrink_th = init->asbcst;
allctr->mseg_opt.rel_shrink_th = init->rsbcst;
@@ -4079,20 +4173,29 @@ erts_alcu_start(Allctr_t *allctr, AllctrInit_t *init)
allctr->mbc_move_threshold = init->rmbcmt;
#if HAVE_ERTS_MSEG
allctr->max_mseg_sbcs = init->mmsbc;
+# if HAVE_SUPER_ALIGNED_MB_CARRIERS
+ allctr->max_mseg_mbcs = ~(Uint)0;
+# else
allctr->max_mseg_mbcs = init->mmmbc;
+# endif
#endif
allctr->largest_mbc_size = MAX(init->lmbcs, init->smbcs);
+#ifndef ARCH_64
+ if (allctr->largest_mbc_size > MBC_SZ_MAX_LIMIT) {
+ allctr->largest_mbc_size = MBC_SZ_MAX_LIMIT;
+ }
+#endif
allctr->smallest_mbc_size = init->smbcs;
allctr->mbc_growth_stages = MAX(1, init->mbcgs);
if (allctr->min_block_size < ABLK_HDR_SZ)
goto error;
allctr->min_block_size = UNIT_CEILING(allctr->min_block_size
- + sizeof(UWord));
+ + sizeof(FreeBlkFtr_t));
#if ERTS_SMP
if (init->tpref) {
- Uint sz = sizeof(Block_t);
+ Uint sz = ABLK_HDR_SZ;
sz += ERTS_ALCU_DD_FIX_TYPE_OFFS*sizeof(UWord);
if (init->fix)
sz += sizeof(UWord);
@@ -4102,6 +4205,23 @@ erts_alcu_start(Allctr_t *allctr, AllctrInit_t *init)
}
#endif
+ allctr->sbc_threshold = init->sbct;
+#ifndef ARCH_64
+ if (allctr->sbc_threshold > 0) {
+ Uint max_mbc_block_sz = UNIT_CEILING(allctr->sbc_threshold - 1 + ABLK_HDR_SZ);
+ if (max_mbc_block_sz + UNIT_FLOOR(allctr->min_block_size - 1) > MBC_ABLK_SZ_MASK
+ || max_mbc_block_sz < allctr->sbc_threshold) { /* wrap around */
+ /*
+ * By limiting sbc_threshold to (hard limit - min_block_size)
+ * we avoid having to split off free "residue blocks"
+ * smaller than min_block_size.
+ */
+ max_mbc_block_sz = MBC_ABLK_SZ_MASK - UNIT_FLOOR(allctr->min_block_size - 1);
+ allctr->sbc_threshold = max_mbc_block_sz - ABLK_HDR_SZ + 1;
+ }
+ }
+#endif
+
allctr->sbmbc_threshold = init->sbmbct;
@@ -4117,7 +4237,7 @@ erts_alcu_start(Allctr_t *allctr, AllctrInit_t *init)
allctr->sbmbc_size = init->sbmbcs;
min_size = allctr->sbmbc_threshold;
min_size += allctr->min_block_size;
- min_size += allctr->mbc_header_size;
+ min_size += MBC_HEADER_SIZE(allctr);
if (allctr->sbmbc_size < min_size)
allctr->sbmbc_size = min_size;
}
@@ -4162,58 +4282,26 @@ erts_alcu_start(Allctr_t *allctr, AllctrInit_t *init)
if (!allctr->get_next_mbc_size)
allctr->get_next_mbc_size = get_next_mbc_size;
- if (allctr->mbc_header_size < sizeof(Carrier_t))
- goto error;
#ifdef ERTS_SMP
allctr->dd.use = 0;
if (init->tpref) {
- allctr->mbc_header_size = (UNIT_CEILING(allctr->mbc_header_size
- + FBLK_FTR_SZ
- + ABLK_HDR_SZ
- + sizeof(UWord))
- - ABLK_HDR_SZ
- - sizeof(UWord));
- allctr->sbc_header_size = (UNIT_CEILING(sizeof(Carrier_t)
- + FBLK_FTR_SZ
- + ABLK_HDR_SZ
- + sizeof(UWord))
- - ABLK_HDR_SZ
- - sizeof(UWord));
-
allctr->dd.use = 1;
init_dd_queue(&allctr->dd.q);
+ allctr->dd.ix = init->ix;
}
- else
#endif
- {
- allctr->mbc_header_size = (UNIT_CEILING(allctr->mbc_header_size
- + FBLK_FTR_SZ
- + ABLK_HDR_SZ)
- - ABLK_HDR_SZ);
- allctr->sbc_header_size = (UNIT_CEILING(sizeof(Carrier_t)
- + FBLK_FTR_SZ
- + ABLK_HDR_SZ)
- - ABLK_HDR_SZ);
- }
if (allctr->main_carrier_size) {
Block_t *blk;
-#if HALFWORD_HEAP
- blk = create_carrier(allctr,
- allctr->main_carrier_size,
- CFLG_MBC
- | CFLG_FORCE_SIZE
- | CFLG_FORCE_MSEG
- | CFLG_MAIN_CARRIER);
-#else
blk = create_carrier(allctr,
allctr->main_carrier_size,
CFLG_MBC
| CFLG_FORCE_SIZE
+#if !HALFWORD_HEAP && !HAVE_SUPER_ALIGNED_MB_CARRIERS
| CFLG_FORCE_SYS_ALLOC
- | CFLG_MAIN_CARRIER);
#endif
+ | CFLG_MAIN_CARRIER);
if (!blk)
goto error;
@@ -4261,9 +4349,9 @@ erts_alcu_stop(Allctr_t *allctr)
while (allctr->sbc_list.first)
destroy_carrier(allctr, SBC2BLK(allctr, allctr->sbc_list.first));
while (allctr->mbc_list.first)
- destroy_carrier(allctr, MBC2FBLK(allctr, allctr->mbc_list.first));
+ destroy_carrier(allctr, MBC_TO_FIRST_BLK(allctr, allctr->mbc_list.first));
while (allctr->sbmbc_list.first)
- destroy_sbmbc(allctr, MBC2FBLK(allctr, allctr->sbmbc_list.first));
+ destroy_sbmbc(allctr, MBC_TO_FIRST_BLK(allctr, allctr->sbmbc_list.first));
#ifdef USE_THREADS
if (allctr->thread_safe)
@@ -4277,17 +4365,9 @@ erts_alcu_stop(Allctr_t *allctr)
void
erts_alcu_init(AlcUInit_t *init)
{
-
+ ASSERT(SBC_BLK_SZ_MASK == MBC_FBLK_SZ_MASK); /* see BLK_SZ */
#if HAVE_ERTS_MSEG
- mseg_unit_size = erts_mseg_unit_size();
-
- if (mseg_unit_size % sizeof(Unit_t)) /* A little paranoid... */
- erl_exit(-1,
- "Mseg unit size (%d) not evenly divideble by "
- "internal unit size of alloc_util (%d)\n",
- mseg_unit_size,
- sizeof(Unit_t));
-
+ ASSERT(erts_mseg_unit_size() == MSEG_UNIT_SZ);
max_mseg_carriers = init->mmc;
sys_alloc_carrier_size = MSEG_UNIT_CEILING(init->ycs);
#else /* #if HAVE_ERTS_MSEG */
@@ -4330,11 +4410,10 @@ erts_alcu_test(unsigned long op, unsigned long a1, unsigned long a2)
case 0x00b: return (unsigned long) CARRIER_SZ((Carrier_t *) a1);
case 0x00c: return (unsigned long) SBC2BLK((Allctr_t *) a1,
(Carrier_t *) a2);
- case 0x00d: return (unsigned long) BLK2SBC((Allctr_t *) a1,
- (Block_t *) a2);
- case 0x00e: return (unsigned long) MBC2FBLK((Allctr_t *) a1,
+ case 0x00d: return (unsigned long) BLK_TO_SBC((Block_t *) a2);
+ case 0x00e: return (unsigned long) MBC_TO_FIRST_BLK((Allctr_t *) a1,
(Carrier_t *) a2);
- case 0x00f: return (unsigned long) FBLK2MBC((Allctr_t *) a1,
+ case 0x00f: return (unsigned long) FIRST_BLK_TO_MBC((Allctr_t *) a1,
(Block_t *) a2);
case 0x010: return (unsigned long) ((Allctr_t *) a1)->mbc_list.first;
case 0x011: return (unsigned long) ((Allctr_t *) a1)->mbc_list.last;
@@ -4346,7 +4425,7 @@ erts_alcu_test(unsigned long op, unsigned long a1, unsigned long a2)
case 0x017: return (unsigned long) ((Allctr_t *) a1)->min_block_size;
case 0x018: return (unsigned long) NXT_BLK((Block_t *) a1);
case 0x019: return (unsigned long) PREV_BLK((Block_t *) a1);
- case 0x01a: return (unsigned long) IS_FIRST_BLK((Block_t *) a1);
+ case 0x01a: return (unsigned long) IS_MBC_FIRST_BLK((Allctr_t*)a1, (Block_t *) a2);
case 0x01b: return (unsigned long) sizeof(Unit_t);
default: ASSERT(0); return ~((unsigned long) 0);
}
@@ -4390,6 +4469,13 @@ erts_alcu_verify_unused_ts(Allctr_t *allctr)
#endif
}
+#ifdef DEBUG
+int is_sbc_blk(Block_t* blk)
+{
+ return IS_SBC_BLK(blk);
+}
+#endif
+
#ifdef ERTS_ALLOC_UTIL_HARD_DEBUG
static void
@@ -4399,34 +4485,37 @@ check_blk_carrier(Allctr_t *allctr, Block_t *iblk)
CarrierList_t *cl;
if (IS_SBC_BLK(iblk)) {
- Carrier_t *sbc = BLK2SBC(allctr, iblk);
+ Carrier_t *sbc = BLK_TO_SBC(iblk);
ASSERT(SBC2BLK(allctr, sbc) == iblk);
- ASSERT(IS_ALLOCED_BLK(iblk));
- ASSERT(IS_FIRST_BLK(iblk));
- ASSERT(IS_LAST_BLK(iblk));
- ASSERT(CARRIER_SZ(sbc) - allctr->sbc_header_size >= BLK_SZ(iblk));
+ ASSERT(CARRIER_SZ(sbc) - SBC_HEADER_SIZE >= SBC_BLK_SZ(iblk));
#if HAVE_ERTS_MSEG
if (IS_MSEG_CARRIER(sbc)) {
- ASSERT(CARRIER_SZ(sbc) % mseg_unit_size == 0);
+ ASSERT(CARRIER_SZ(sbc) % MSEG_UNIT_SZ == 0);
}
#endif
crr = sbc;
cl = &allctr->sbc_list;
}
else {
- Carrier_t *mbc = NULL;
Block_t *prev_blk = NULL;
Block_t *blk;
char *carrier_end;
Uint is_free_blk;
Uint tot_blk_sz;
Uint blk_sz;
+ int has_wrapped_around = 0;
blk = iblk;
tot_blk_sz = 0;
+ crr = BLK_TO_MBC(blk);
+ ASSERT(IS_MB_CARRIER(crr));
+ /* Step around the carrier one whole lap starting at 'iblk'
+ */
while (1) {
+ ASSERT(IS_MBC_BLK(blk));
+ ASSERT(BLK_TO_MBC(blk) == crr);
if (prev_blk) {
ASSERT(NXT_BLK(prev_blk) == blk);
@@ -4439,18 +4528,16 @@ check_blk_carrier(Allctr_t *allctr, Block_t *iblk)
}
}
- if (mbc) {
+ if (has_wrapped_around) {
+ ASSERT(((Block_t *) crr) < blk);
if (blk == iblk)
break;
- ASSERT(((Block_t *) mbc) < blk && blk < iblk);
+ ASSERT(blk < iblk);
}
else
ASSERT(blk >= iblk);
-
- ASSERT(IS_MBC_BLK(blk));
-
- blk_sz = BLK_SZ(blk);
+ blk_sz = MBC_BLK_SZ(blk);
ASSERT(blk_sz % sizeof(Unit_t) == 0);
ASSERT(blk_sz >= allctr->min_block_size);
@@ -4458,44 +4545,40 @@ check_blk_carrier(Allctr_t *allctr, Block_t *iblk)
tot_blk_sz += blk_sz;
is_free_blk = (int) IS_FREE_BLK(blk);
- if(is_free_blk) {
- if (IS_NOT_LAST_BLK(blk))
- ASSERT(*((UWord *) (((char *) blk)+blk_sz-sizeof(UWord)))
- == blk_sz);
- }
+ ASSERT(!is_free_blk
+ || IS_LAST_BLK(blk)
+ || PREV_BLK_SZ(((char *) blk)+blk_sz) == blk_sz);
if (allctr->check_block)
(*allctr->check_block)(allctr, blk, (int) is_free_blk);
if (IS_LAST_BLK(blk)) {
carrier_end = ((char *) NXT_BLK(blk));
- mbc = *((Carrier_t **) NXT_BLK(blk));
+ has_wrapped_around = 1;
prev_blk = NULL;
- blk = MBC2FBLK(allctr, mbc);
- ASSERT(IS_FIRST_BLK(blk));
+ blk = MBC_TO_FIRST_BLK(allctr, crr);
+ ASSERT(IS_MBC_FIRST_BLK(allctr,blk));
}
else {
prev_blk = blk;
blk = NXT_BLK(blk);
}
}
-
- ASSERT(IS_MB_CARRIER(mbc));
- ASSERT((((char *) mbc)
- + allctr->mbc_header_size
+
+ ASSERT((((char *) crr)
+ + MBC_HEADER_SIZE(allctr)
+ tot_blk_sz) == carrier_end);
- ASSERT(((char *) mbc) + CARRIER_SZ(mbc) - sizeof(Unit_t) <= carrier_end
- && carrier_end <= ((char *) mbc) + CARRIER_SZ(mbc));
+ ASSERT(((char *) crr) + CARRIER_SZ(crr) - sizeof(Unit_t) <= carrier_end
+ && carrier_end <= ((char *) crr) + CARRIER_SZ(crr));
if (allctr->check_mbc)
- (*allctr->check_mbc)(allctr, mbc);
+ (*allctr->check_mbc)(allctr, crr);
#if HAVE_ERTS_MSEG
- if (IS_MSEG_CARRIER(mbc)) {
- ASSERT(CARRIER_SZ(mbc) % mseg_unit_size == 0);
+ if (IS_MSEG_CARRIER(crr)) {
+ ASSERT(CARRIER_SZ(crr) % MSEG_UNIT_SZ == 0);
}
#endif
- crr = mbc;
cl = &allctr->mbc_list;
}
@@ -4517,4 +4600,5 @@ check_blk_carrier(Allctr_t *allctr, Block_t *iblk)
#endif
}
-#endif
+#endif /* ERTS_ALLOC_UTIL_HARD_DEBUG */
+
diff --git a/erts/emulator/beam/erl_alloc_util.h b/erts/emulator/beam/erl_alloc_util.h
index cedf4ccf85..e0754e7f69 100644
--- a/erts/emulator/beam/erl_alloc_util.h
+++ b/erts/emulator/beam/erl_alloc_util.h
@@ -216,16 +216,40 @@ erts_aint32_t erts_alcu_fix_alloc_shrink(Allctr_t *, erts_aint32_t);
#define UNIT_FLOOR(X) ((X) & UNIT_MASK)
#define UNIT_CEILING(X) UNIT_FLOOR((X) + INV_UNIT_MASK)
+#define FLG_MASK INV_UNIT_MASK
+#define SBC_BLK_SZ_MASK UNIT_MASK
+#define MBC_FBLK_SZ_MASK UNIT_MASK
+#define CARRIER_SZ_MASK UNIT_MASK
-#define SZ_MASK (~((UWord) 0) << 3)
-#define FLG_MASK (~(SZ_MASK))
+#if HAVE_ERTS_MSEG
+# ifdef ARCH_64
+# define MBC_ABLK_OFFSET_BITS 24
+# elif HAVE_SUPER_ALIGNED_MB_CARRIERS
+# define MBC_ABLK_OFFSET_BITS 9
+ /* Affects hard limits for sbct and lmbcs documented in erts_alloc.xml */
+# endif
+#endif
+#ifndef MBC_ABLK_OFFSET_BITS
+# define MBC_ABLK_OFFSET_BITS 0 /* no carrier offset in block header */
+#endif
+
+#if MBC_ABLK_OFFSET_BITS
+# define MBC_ABLK_OFFSET_SHIFT (sizeof(UWord)*8 - MBC_ABLK_OFFSET_BITS)
+# define MBC_ABLK_OFFSET_MASK (~((UWord)0) << MBC_ABLK_OFFSET_SHIFT)
+# define MBC_ABLK_SZ_MASK (~MBC_ABLK_OFFSET_MASK & ~FLG_MASK)
+# define HAVE_ERTS_SBMBC 0
+#else
+# define MBC_ABLK_SZ_MASK (~FLG_MASK)
+# define HAVE_ERTS_SBMBC 1
+#endif
-#define BLK_SZ(B) \
- (*((Block_t *) (B)) & SZ_MASK)
+#define MBC_ABLK_SZ(B) (ASSERT_EXPR(!is_sbc_blk(B)), (B)->bhdr & MBC_ABLK_SZ_MASK)
+#define MBC_FBLK_SZ(B) (ASSERT_EXPR(!is_sbc_blk(B)), (B)->bhdr & MBC_FBLK_SZ_MASK)
+#define SBC_BLK_SZ(B) (ASSERT_EXPR(is_sbc_blk(B)), (B)->bhdr & SBC_BLK_SZ_MASK)
#define CARRIER_SZ(C) \
- ((C)->chdr & SZ_MASK)
+ ((C)->chdr & CARRIER_SZ_MASK)
extern int erts_have_sbmbc_alloc;
@@ -236,6 +260,7 @@ struct Carrier_t_ {
UWord chdr;
Carrier_t *next;
Carrier_t *prev;
+ Allctr_t *allctr;
};
typedef struct {
@@ -243,8 +268,19 @@ typedef struct {
Carrier_t *last;
} CarrierList_t;
-typedef UWord Block_t;
-typedef UWord FreeBlkFtr_t;
+typedef struct {
+ UWord bhdr;
+#if !MBC_ABLK_OFFSET_BITS
+ Carrier_t *carrier;
+#else
+ union {
+ Carrier_t *carrier; /* if free */
+ char udata__[1]; /* if allocated */
+ }u;
+#endif
+} Block_t;
+
+typedef UWord FreeBlkFtr_t; /* Footer of a free block */
typedef struct {
UWord giga_no;
@@ -381,8 +417,6 @@ struct Allctr_t_ {
#endif
/* */
- Uint mbc_header_size;
- Uint sbc_header_size;
Uint min_mbc_size;
Uint min_mbc_first_free_size;
Uint min_block_size;
@@ -469,6 +503,9 @@ void erts_alcu_verify_unused_ts(Allctr_t *allctr);
unsigned long erts_alcu_test(unsigned long, unsigned long, unsigned long);
+#ifdef DEBUG
+int is_sbc_blk(Block_t*);
+#endif
#endif /* #if defined(GET_ERL_ALLOC_UTIL_IMPL)
diff --git a/erts/emulator/beam/erl_ao_firstfit_alloc.c b/erts/emulator/beam/erl_ao_firstfit_alloc.c
index 5bdb752d3a..86b4696d8f 100644
--- a/erts/emulator/beam/erl_ao_firstfit_alloc.c
+++ b/erts/emulator/beam/erl_ao_firstfit_alloc.c
@@ -91,6 +91,7 @@ struct AOFF_RBTree_t_ {
AOFF_RBTree_t *right;
Uint max_sz; /* of all blocks in this sub-tree */
};
+#define AOFF_BLK_SZ(B) MBC_FBLK_SZ(&(B)->hdr)
#ifdef HARD_DEBUG
static AOFF_RBTree_t * check_tree(AOFF_RBTree_t* root, Uint);
@@ -102,7 +103,7 @@ static AOFF_RBTree_t * check_tree(AOFF_RBTree_t* root, Uint);
*/
static ERTS_INLINE Uint node_max_size(AOFF_RBTree_t *x)
{
- Uint sz = BLK_SZ(x);
+ Uint sz = AOFF_BLK_SZ(x);
if (x->left && x->left->max_sz > sz) {
sz = x->left->max_sz;
}
@@ -183,7 +184,6 @@ erts_aoffalc_start(AOFFAllctr_t *alc,
sys_memcpy((void *) alc, (void *) &zero.allctr, sizeof(AOFFAllctr_t));
- allctr->mbc_header_size = sizeof(Carrier_t);
allctr->min_mbc_size = MIN_MBC_SZ;
allctr->min_mbc_first_free_size = MIN_MBC_FIRST_FREE_SZ;
allctr->min_block_size = sizeof(AOFF_RBTree_t);
@@ -587,7 +587,7 @@ aoff_link_free_block(Allctr_t *allctr, Block_t *block, Uint32 flags)
AOFF_RBTree_t *blk = (AOFF_RBTree_t *) block;
AOFF_RBTree_t **root = ((flags & ERTS_ALCU_FLG_SBMBC)
? &alc->sbmbc_root : &alc->mbc_root);
- Uint blk_sz = BLK_SZ(blk);
+ Uint blk_sz = AOFF_BLK_SZ(blk);
#ifdef HARD_DEBUG
check_tree(*root, 0);
@@ -659,7 +659,7 @@ aoff_get_free_block(Allctr_t *allctr, Uint size,
if (x->left && x->left->max_sz >= size) {
x = x->left;
}
- else if (BLK_SZ(x) >= size) {
+ else if (AOFF_BLK_SZ(x) >= size) {
blk = x;
break;
}
@@ -910,12 +910,12 @@ check_tree(AOFF_RBTree_t* root, Uint size)
ASSERT(x->right > x);
ASSERT(x->right->max_sz <= x->max_sz);
}
- ASSERT(x->max_sz >= BLK_SZ(x));
- ASSERT(x->max_sz == BLK_SZ(x)
+ ASSERT(x->max_sz >= AOFF_BLK_SZ(x));
+ ASSERT(x->max_sz == AOFF_BLK_SZ(x)
|| x->max_sz == (x->left ? x->left->max_sz : 0)
|| x->max_sz == (x->right ? x->right->max_sz : 0));
- if (size && BLK_SZ(x) >= size) {
+ if (size && AOFF_BLK_SZ(x) >= size) {
if (!res || x < res) {
res = x;
}
@@ -956,7 +956,7 @@ print_tree_aux(AOFF_RBTree_t *x, int indent)
}
fprintf(stderr, "%s: sz=%lu addr=0x%lx max_size=%lu\r\n",
IS_BLACK(x) ? "BLACK" : "RED",
- BLK_SZ(x), (Uint)x, x->max_sz);
+ AOFF_BLK_SZ(x), (Uint)x, x->max_sz);
print_tree_aux(x->left, indent + INDENT_STEP);
}
}
diff --git a/erts/emulator/beam/erl_async.c b/erts/emulator/beam/erl_async.c
index cb975d64b0..f2ca193ace 100644
--- a/erts/emulator/beam/erl_async.c
+++ b/erts/emulator/beam/erl_async.c
@@ -122,6 +122,8 @@ typedef struct {
#endif
} ErtsAsyncData;
+#if defined(USE_THREADS) && defined(USE_VM_PROBES)
+
/*
* Some compilers, e.g. GCC 4.2.1 and -O3, will optimize away DTrace
* calls if they're the last thing in the function. :-(
@@ -129,6 +131,7 @@ typedef struct {
* https://github.com/memcached/memcached/commit/6298b3978687530bc9d219b6ac707a1b681b2a46
*/
static unsigned gcc_optimizer_hack = 0;
+#endif
int erts_async_max_threads; /* Initialized by erl_init.c */
int erts_async_thread_suggested_stack_size; /* Initialized by erl_init.c */
@@ -281,8 +284,8 @@ static ERTS_INLINE void async_add(ErtsAsync *a, ErtsAsyncQ* q)
len = -1;
DTRACE2(aio_pool_add, port_str, len);
}
-#endif
gcc_optimizer_hack++;
+#endif
}
static ERTS_INLINE ErtsAsync *async_get(ErtsThrQ_t *q,
@@ -379,10 +382,15 @@ static ERTS_INLINE ErtsAsync *async_get(ErtsThrQ_t *q,
static ERTS_INLINE void call_async_ready(ErtsAsync *a)
{
+#if ERTS_USE_ASYNC_READY_Q
Port *p = erts_id2port_sflgs(a->port,
NULL,
0,
ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP);
+#else
+ Port *p = erts_thr_id2port_sflgs(a->port,
+ ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP);
+#endif
if (!p) {
if (a->async_free)
a->async_free(a->async_data);
@@ -392,8 +400,14 @@ static ERTS_INLINE void call_async_ready(ErtsAsync *a)
if (a->async_free)
a->async_free(a->async_data);
}
+#if ERTS_USE_ASYNC_READY_Q
erts_port_release(p);
+#else
+ erts_thr_port_release(p);
+#endif
}
+ if (a->pdl)
+ driver_pdl_dec_refc(a->pdl);
if (a->hndl)
erts_ddll_dereference_driver(a->hndl);
}
@@ -403,9 +417,6 @@ static ERTS_INLINE void async_reply(ErtsAsync *a, ErtsThrQPrepEnQ_t *prep_enq)
#if ERTS_USE_ASYNC_READY_Q
ErtsAsyncReadyQ *arq;
- if (a->pdl)
- driver_pdl_dec_refc(a->pdl);
-
#if ERTS_ASYNC_PRINT_JOB
erts_fprintf(stderr, "=>> %ld\n", a->async_id);
#endif
@@ -425,8 +436,6 @@ static ERTS_INLINE void async_reply(ErtsAsync *a, ErtsThrQPrepEnQ_t *prep_enq)
#else /* ERTS_USE_ASYNC_READY_Q */
call_async_ready(a);
- if (a->pdl)
- driver_pdl_dec_refc(a->pdl);
erts_free(ERTS_ALC_T_ASYNC, (void *) a);
#endif /* ERTS_USE_ASYNC_READY_Q */
@@ -603,7 +612,7 @@ long driver_async(ErlDrvPort ix, unsigned int* key,
sched_id = 1;
#endif
- prt = erts_drvport2port(ix);
+ prt = erts_drvport2port(ix, NULL);
if (!prt)
return -1;
@@ -615,7 +624,7 @@ long driver_async(ErlDrvPort ix, unsigned int* key,
a->sched_id = sched_id;
#endif
a->hndl = (DE_Handle*)prt->drv_ptr->handle;
- a->port = prt->id;
+ a->port = prt->common.id;
a->pdl = NULL;
a->async_data = async_data;
a->async_invoke = async_invoke;
diff --git a/erts/emulator/beam/erl_bestfit_alloc.c b/erts/emulator/beam/erl_bestfit_alloc.c
index c50fdeb4e8..743cbd93c9 100644
--- a/erts/emulator/beam/erl_bestfit_alloc.c
+++ b/erts/emulator/beam/erl_bestfit_alloc.c
@@ -73,6 +73,8 @@
#define SET_RED(N) (((RBTree_t *) (N))->flags |= RED_FLG)
#define SET_BLACK(N) (((RBTree_t *) (N))->flags &= ~RED_FLG)
+#define BF_BLK_SZ(B) MBC_FBLK_SZ(&(B)->hdr)
+
#undef ASSERT
#define ASSERT ASSERT_EXPR
@@ -177,7 +179,6 @@ erts_bfalc_start(BFAllctr_t *bfallctr,
bfallctr->address_order = bfinit->ao;
- allctr->mbc_header_size = sizeof(Carrier_t);
allctr->min_mbc_size = MIN_MBC_SZ;
allctr->min_mbc_first_free_size = MIN_MBC_FIRST_FREE_SZ;
allctr->min_block_size = (bfinit->ao
@@ -592,7 +593,7 @@ aobf_link_free_block(Allctr_t *allctr, Block_t *block, Uint32 flags)
? &bfallctr->sbmbc_root
: &bfallctr->mbc_root);
RBTree_t *blk = (RBTree_t *) block;
- Uint blk_sz = BLK_SZ(blk);
+ Uint blk_sz = BF_BLK_SZ(blk);
@@ -610,7 +611,7 @@ aobf_link_free_block(Allctr_t *allctr, Block_t *block, Uint32 flags)
while (1) {
Uint size;
- size = BLK_SZ(x);
+ size = BF_BLK_SZ(x);
if (blk_sz < size || (blk_sz == size && blk < x)) {
if (!x->left) {
@@ -668,7 +669,7 @@ aobf_get_free_block(Allctr_t *allctr, Uint size,
ASSERT(!cand_blk || cand_size >= size);
while (x) {
- blk_sz = BLK_SZ(x);
+ blk_sz = BF_BLK_SZ(x);
if (blk_sz < size) {
x = x->right;
}
@@ -686,7 +687,7 @@ aobf_get_free_block(Allctr_t *allctr, Uint size,
#endif
if (cand_blk) {
- blk_sz = BLK_SZ(blk);
+ blk_sz = BF_BLK_SZ(blk);
if (cand_size < blk_sz)
return NULL; /* cand_blk was better */
if (cand_size == blk_sz && ((void *) cand_blk) < ((void *) blk))
@@ -711,7 +712,7 @@ bf_link_free_block(Allctr_t *allctr, Block_t *block, Uint32 flags)
? &bfallctr->sbmbc_root
: &bfallctr->mbc_root);
RBTree_t *blk = (RBTree_t *) block;
- Uint blk_sz = BLK_SZ(blk);
+ Uint blk_sz = BF_BLK_SZ(blk);
SET_TREE_NODE(blk);
@@ -730,7 +731,7 @@ bf_link_free_block(Allctr_t *allctr, Block_t *block, Uint32 flags)
while (1) {
Uint size;
- size = BLK_SZ(x);
+ size = BF_BLK_SZ(x);
if (blk_sz == size) {
@@ -796,7 +797,7 @@ bf_unlink_free_block(Allctr_t *allctr, Block_t *block, Uint32 flags)
else if (LIST_NEXT(x)) {
/* Replace tree node by next element in list... */
- ASSERT(BLK_SZ(LIST_NEXT(x)) == BLK_SZ(x));
+ ASSERT(BF_BLK_SZ(LIST_NEXT(x)) == BF_BLK_SZ(x));
ASSERT(IS_TREE_NODE(x));
ASSERT(IS_LIST_ELEM(LIST_NEXT(x)));
@@ -834,7 +835,7 @@ bf_get_free_block(Allctr_t *allctr, Uint size,
ASSERT(!cand_blk || cand_size >= size);
while (x) {
- blk_sz = BLK_SZ(x);
+ blk_sz = BF_BLK_SZ(x);
if (blk_sz < size) {
x = x->right;
}
@@ -855,11 +856,11 @@ bf_get_free_block(Allctr_t *allctr, Uint size,
#ifdef HARD_DEBUG
{
RBTree_t *ct_blk = check_tree(root, 0, size);
- ASSERT(BLK_SZ(ct_blk) == BLK_SZ(blk));
+ ASSERT(BF_BLK_SZ(ct_blk) == BF_BLK_SZ(blk));
}
#endif
- if (cand_blk && cand_size <= BLK_SZ(blk))
+ if (cand_blk && cand_size <= BF_BLK_SZ(blk))
return NULL; /* cand_blk was better */
/* Use next block if it exist in order to avoid replacing
@@ -1093,36 +1094,36 @@ check_tree(RBTree_t *root, int ao, Uint size)
if (x->left) {
ASSERT(x->left->parent == x);
if (ao) {
- ASSERT(BLK_SZ(x->left) < BLK_SZ(x)
- || (BLK_SZ(x->left) == BLK_SZ(x) && x->left < x));
+ ASSERT(BF_BLK_SZ(x->left) < BF_BLK_SZ(x)
+ || (BF_BLK_SZ(x->left) == BF_BLK_SZ(x) && x->left < x));
}
else {
ASSERT(IS_TREE_NODE(x->left));
- ASSERT(BLK_SZ(x->left) < BLK_SZ(x));
+ ASSERT(BF_BLK_SZ(x->left) < BF_BLK_SZ(x));
}
}
if (x->right) {
ASSERT(x->right->parent == x);
if (ao) {
- ASSERT(BLK_SZ(x->right) > BLK_SZ(x)
- || (BLK_SZ(x->right) == BLK_SZ(x) && x->right > x));
+ ASSERT(BF_BLK_SZ(x->right) > BF_BLK_SZ(x)
+ || (BF_BLK_SZ(x->right) == BF_BLK_SZ(x) && x->right > x));
}
else {
ASSERT(IS_TREE_NODE(x->right));
- ASSERT(BLK_SZ(x->right) > BLK_SZ(x));
+ ASSERT(BF_BLK_SZ(x->right) > BF_BLK_SZ(x));
}
}
- if (size && BLK_SZ(x) >= size) {
+ if (size && BF_BLK_SZ(x) >= size) {
if (ao) {
if (!res
- || BLK_SZ(x) < BLK_SZ(res)
- || (BLK_SZ(x) == BLK_SZ(res) && x < res))
+ || BF_BLK_SZ(x) < BF_BLK_SZ(res)
+ || (BF_BLK_SZ(x) == BF_BLK_SZ(res) && x < res))
res = x;
}
else {
- if (!res || BLK_SZ(x) < BLK_SZ(res))
+ if (!res || BF_BLK_SZ(x) < BF_BLK_SZ(res))
res = x;
}
}
@@ -1168,7 +1169,7 @@ print_tree_aux(RBTree_t *x, int indent)
}
fprintf(stderr, "%s: sz=%lu addr=0x%lx\r\n",
IS_BLACK(x) ? "BLACK" : "RED",
- BLK_SZ(x),
+ BF_BLK_SZ(x),
(Uint) x);
print_tree_aux(x->left, indent + INDENT_STEP);
}
diff --git a/erts/emulator/beam/erl_bif_ddll.c b/erts/emulator/beam/erl_bif_ddll.c
index 7f7c975e78..889fefacfc 100644
--- a/erts/emulator/beam/erl_bif_ddll.c
+++ b/erts/emulator/beam/erl_bif_ddll.c
@@ -104,16 +104,49 @@ static void dereference_all_processes(DE_Handle *dh);
static void restore_process_references(DE_Handle *dh);
static void ddll_no_more_references(void *vdh);
-#define lock_drv_list() erts_smp_mtx_lock(&erts_driver_list_lock)
-#define unlock_drv_list() erts_smp_mtx_unlock(&erts_driver_list_lock)
+#define lock_drv_list() erts_smp_rwmtx_rwlock(&erts_driver_list_lock)
+#define unlock_drv_list() erts_smp_rwmtx_rwunlock(&erts_driver_list_lock)
#define assert_drv_list_locked() \
- ERTS_SMP_LC_ASSERT(erts_smp_lc_mtx_is_locked(&erts_driver_list_lock))
+ ERTS_SMP_LC_ASSERT(erts_smp_lc_rwmtx_is_rwlocked(&erts_driver_list_lock) \
+ || erts_smp_lc_rwmtx_is_rlocked(&erts_driver_list_lock))
+#define assert_drv_list_rwlocked() \
+ ERTS_SMP_LC_ASSERT(erts_smp_lc_rwmtx_is_rwlocked(&erts_driver_list_lock))
+#define assert_drv_list_rlocked() \
+ ERTS_SMP_LC_ASSERT(erts_smp_lc_rwmtx_is_rlocked(&erts_driver_list_lock))
#define assert_drv_list_not_locked() \
- ERTS_SMP_LC_ASSERT(!erts_smp_lc_mtx_is_locked(&erts_driver_list_lock))
+ ERTS_SMP_LC_ASSERT(!erts_smp_lc_rwmtx_is_rwlocked(&erts_driver_list_lock) \
+ && !erts_smp_lc_rwmtx_is_rlocked(&erts_driver_list_lock))
#define FREE_PORT_FLAGS (ERTS_PORT_SFLGS_DEAD & (~ERTS_PORT_SFLG_INITIALIZING))
+static void
+kill_ports_driver_unloaded(DE_Handle *dh)
+{
+ int ix, max = erts_ptab_max(&erts_port);
+
+ for (ix = 0; ix < max; ix++) {
+ erts_aint32_t state;
+ Port* prt = erts_pix2port(ix);
+ if (!prt)
+ continue;
+
+ ERTS_SMP_DATA_DEPENDENCY_READ_MEMORY_BARRIER;
+
+ state = erts_atomic32_read_nob(&prt->state);
+ if (state & FREE_PORT_FLAGS)
+ continue;
+
+ erts_smp_port_lock(prt);
+
+ state = erts_atomic32_read_nob(&prt->state);
+ if (!(state & ERTS_PORT_SFLGS_DEAD) && prt->drv_ptr->handle == dh)
+ driver_failure_atom((ErlDrvPort) prt, "driver_unloaded");
+
+ erts_port_release(prt);
+ }
+}
+
/*
* try_load(Path, Name, OptionList) -> {ok,Status} |
* {ok, PendingStatus, Ref} |
@@ -149,7 +182,7 @@ BIF_RETTYPE erl_ddll_try_load_3(BIF_ALIST_3)
Eterm name_term = BIF_ARG_2;
Eterm options = BIF_ARG_3;
char *path = NULL;
- Uint path_len;
+ ErlDrvSizeT path_len;
char *name = NULL;
DE_Handle *dh;
erts_driver_t *drv;
@@ -228,7 +261,7 @@ BIF_RETTYPE erl_ddll_try_load_3(BIF_ALIST_3)
goto error;
}
path = erts_alloc(ERTS_ALC_T_DDLL_TMP_BUF, path_len + 1 /* might need path separator */ + sys_strlen(name) + 1);
- if (io_list_to_buf(path_term, path, path_len) != 0) {
+ if (erts_iolist_to_buf(path_term, path, path_len) != 0) {
goto error;
}
while (path_len > 0 && (path[path_len-1] == '\\' || path[path_len-1] == '/')) {
@@ -356,40 +389,16 @@ BIF_RETTYPE erl_ddll_try_load_3(BIF_ALIST_3)
ok_term = mkatom("loaded");
}
}
- assert_drv_list_locked();
+ assert_drv_list_rwlocked();
if (kill_ports) {
- int j;
- /* Avoid closing the driver by referencing it */
+ /* Avoid closing the driver by referencing it */
erts_ddll_reference_driver(dh);
ASSERT(dh->status == ERL_DE_RELOAD);
dh->status = ERL_DE_FORCE_RELOAD;
#if DDLL_SMP
unlock_drv_list();
#endif
- for (j = 0; j < erts_max_ports; j++) {
- Port* prt = &erts_port[j];
- erts_smp_port_state_lock(prt);
- if (!(prt->status & FREE_PORT_FLAGS) &&
- prt->drv_ptr->handle == dh) {
- erts_smp_atomic_inc_nob(&prt->refc);
-#if DDLL_SMP
- /* Extremely rare spinlock */
- while(prt->status & ERTS_PORT_SFLG_INITIALIZING) {
- erts_smp_port_state_unlock(prt);
- erts_smp_port_state_lock(prt);
- }
- erts_smp_port_state_unlock(prt);
- erts_smp_mtx_lock(prt->lock);
- if (!(prt->status & ERTS_PORT_SFLGS_DEAD)) {
- driver_failure_atom(j, "driver_unloaded");
- }
-#else
- driver_failure_atom(j, "driver_unloaded");
-#endif
- erts_port_release(prt);
- }
- else erts_smp_port_state_unlock(prt);
- }
+ kill_ports_driver_unloaded(dh);
/* Dereference, eventually causing driver destruction */
#if DDLL_SMP
lock_drv_list();
@@ -579,45 +588,21 @@ Eterm erl_ddll_try_unload_2(BIF_ALIST_2)
dh->reload_full_path = dh->reload_driver_name = NULL;
dh->reload_flags = 0;
}
- if (dh->port_count > 0) {
+ if (erts_smp_atomic32_read_nob(&dh->port_count) > 0) {
++kill_ports;
}
dh->status = ERL_DE_UNLOAD;
ok_term = am_pending_driver;
done:
- assert_drv_list_locked();
+ assert_drv_list_rwlocked();
if (kill_ports > 1) {
- int j;
/* Avoid closing the driver by referencing it */
erts_ddll_reference_driver(dh);
dh->status = ERL_DE_FORCE_UNLOAD;
#if DDLL_SMP
unlock_drv_list();
#endif
- for (j = 0; j < erts_max_ports; j++) {
- Port* prt = &erts_port[j];
- erts_smp_port_state_lock(prt);
- if (!(prt->status & FREE_PORT_FLAGS)
- && prt->drv_ptr->handle == dh) {
- erts_smp_atomic_inc_nob(&prt->refc);
-#if DDLL_SMP
- /* Extremely rare spinlock */
- while(prt->status & ERTS_PORT_SFLG_INITIALIZING) {
- erts_smp_port_state_unlock(prt);
- erts_smp_port_state_lock(prt);
- }
- erts_smp_port_state_unlock(prt);
- erts_smp_mtx_lock(prt->lock);
- if (!(prt->status & ERTS_PORT_SFLGS_DEAD)) {
- driver_failure_atom(j, "driver_unloaded");
- }
-#else
- driver_failure_atom(j, "driver_unloaded");
-#endif
- erts_port_release(prt);
- }
- else erts_smp_port_state_unlock(prt);
- }
+ kill_ports_driver_unloaded(dh);
#if DDLL_SMP
lock_drv_list();
#endif
@@ -787,7 +772,7 @@ BIF_RETTYPE erl_ddll_info_2(BIF_ALIST_2)
} else if (drv->handle->status == ERL_DE_PERMANENT) {
res = am_permanent;
} else {
- res = make_small(drv->handle->port_count);
+ res = make_small(erts_smp_atomic32_read_nob(&drv->handle->port_count));
}
goto done;
case am_linked_in_driver:
@@ -1045,38 +1030,16 @@ void erts_ddll_proc_dead(Process *p, ErtsProcLocks plocks)
}
dh->status = ERL_DE_UNLOAD;
}
- if (!left && drv->handle->port_count > 0) {
+ if (!left
+ && erts_smp_atomic32_read_nob(&drv->handle->port_count) > 0) {
if (kill_ports) {
- int j;
DE_Handle *dh = drv->handle;
erts_ddll_reference_driver(dh);
dh->status = ERL_DE_FORCE_UNLOAD;
#if DDLL_SMP
unlock_drv_list();
#endif
- for (j = 0; j < erts_max_ports; j++) {
- Port* prt = &erts_port[j];
- erts_smp_port_state_lock(prt);
- if (!(prt->status & FREE_PORT_FLAGS) &&
- prt->drv_ptr->handle == dh) {
- erts_smp_atomic_inc_nob(&prt->refc);
-#if DDLL_SMP
- while(prt->status & ERTS_PORT_SFLG_INITIALIZING) {
- erts_smp_port_state_unlock(prt);
- erts_smp_port_state_lock(prt);
- }
- erts_smp_port_state_unlock(prt);
- erts_smp_mtx_lock(prt->lock);
- if (!(prt->status & ERTS_PORT_SFLGS_DEAD)) {
- driver_failure_atom(j, "driver_unloaded");
- }
-#else
- driver_failure_atom(j, "driver_unloaded");
-#endif
- erts_port_release(prt);
- }
- else erts_smp_port_state_unlock(prt);
- }
+ kill_ports_driver_unloaded(dh);
#if DDLL_SMP
lock_drv_list(); /* Needed for future list operations */
#endif
@@ -1098,7 +1061,7 @@ void erts_ddll_proc_dead(Process *p, ErtsProcLocks plocks)
void erts_ddll_lock_driver(DE_Handle *dh, char *name)
{
DE_ProcEntry *p,*q;
- assert_drv_list_locked();
+ assert_drv_list_rwlocked();
notify_all(dh, name,
ERL_DE_PROC_AWAIT_LOAD, am_UP, am_permanent);
notify_all(dh, name,
@@ -1121,19 +1084,22 @@ void erts_ddll_lock_driver(DE_Handle *dh, char *name)
void erts_ddll_increment_port_count(DE_Handle *dh)
{
assert_drv_list_locked();
- dh->port_count++;
+ erts_smp_atomic32_inc_nob(&dh->port_count);
}
void erts_ddll_decrement_port_count(DE_Handle *dh)
{
assert_drv_list_locked();
- ASSERT(dh->port_count > 0);
- dh->port_count--;
+#if DEBUG
+ ASSERT(erts_smp_atomic32_dec_read_nob(&dh->port_count) >= 0);
+#else
+ erts_smp_atomic32_dec_nob(&dh->port_count);
+#endif
}
static void first_ddll_reference(DE_Handle *dh)
{
- assert_drv_list_locked();
+ assert_drv_list_rwlocked();
erts_refc_init(&(dh->refc),1);
}
@@ -1161,7 +1127,7 @@ void erts_ddll_dereference_driver(DE_Handle *dh)
static void dereference_all_processes(DE_Handle *dh)
{
DE_ProcEntry *p;
- assert_drv_list_locked();
+ assert_drv_list_rwlocked();
for(p = dh->procs;p != NULL; p = p->next) {
if (p->awaiting_status == ERL_DE_PROC_LOADED) {
ASSERT(!(p->flags & ERL_DE_FL_DEREFERENCED));
@@ -1174,7 +1140,7 @@ static void dereference_all_processes(DE_Handle *dh)
static void restore_process_references(DE_Handle *dh)
{
DE_ProcEntry *p;
- assert_drv_list_locked();
+ assert_drv_list_rwlocked();
ASSERT(erts_refc_read(&(dh->refc),0) == 0);
for(p = dh->procs;p != NULL; p = p->next) {
if (p->awaiting_status == ERL_DE_PROC_LOADED) {
@@ -1402,7 +1368,7 @@ static int is_last_user(DE_Handle *dh, Process *proc) {
DE_ProcEntry *p = dh->procs;
int found = 0;
- assert_drv_list_locked();
+ assert_drv_list_rwlocked();
while (p != NULL) {
if (p->proc == proc && p->awaiting_status == ERL_DE_PROC_LOADED) {
@@ -1423,7 +1389,7 @@ static DE_ProcEntry *find_proc_entry(DE_Handle *dh, Process *proc, Uint status)
{
DE_ProcEntry *p = dh->procs;
- assert_drv_list_locked();
+ assert_drv_list_rwlocked();
while (p != NULL) {
if (p->proc == proc && p->awaiting_status == status) {
@@ -1450,7 +1416,7 @@ static int num_procs(DE_Handle *dh, Uint status) {
DE_ProcEntry *p = dh->procs;
int i = 0;
- assert_drv_list_locked();
+ assert_drv_list_rwlocked();
while (p != NULL) {
if (p->awaiting_status == status) {
@@ -1465,7 +1431,7 @@ static int num_entries(DE_Handle *dh, Process *proc, Uint status) {
DE_ProcEntry *p = dh->procs;
int i = 0;
- assert_drv_list_locked();
+ assert_drv_list_rwlocked();
while (p != NULL) {
if (p->awaiting_status == status && p->proc == proc) {
++i;
@@ -1478,7 +1444,7 @@ static int num_entries(DE_Handle *dh, Process *proc, Uint status) {
static void add_proc_loaded(DE_Handle *dh, Process *proc)
{
DE_ProcEntry *p;
- assert_drv_list_locked();
+ assert_drv_list_rwlocked();
p = erts_alloc(ERTS_ALC_T_DDLL_PROCESS, sizeof(DE_ProcEntry));
p->proc = proc;
p->flags = 0;
@@ -1490,7 +1456,7 @@ static void add_proc_loaded(DE_Handle *dh, Process *proc)
static void add_proc_loaded_deref(DE_Handle *dh, Process *proc)
{
DE_ProcEntry *p;
- assert_drv_list_locked();
+ assert_drv_list_rwlocked();
p = erts_alloc(ERTS_ALC_T_DDLL_PROCESS, sizeof(DE_ProcEntry));
p->proc = proc;
p->awaiting_status = ERL_DE_PROC_LOADED;
@@ -1510,7 +1476,7 @@ static void add_proc_waiting(DE_Handle *dh, Process *proc,
Uint status, Eterm ref)
{
DE_ProcEntry *p;
- assert_drv_list_locked();
+ assert_drv_list_rwlocked();
p = erts_alloc(ERTS_ALC_T_DDLL_PROCESS, sizeof(DE_ProcEntry));
p->proc = proc;
p->flags = 0;
@@ -1524,7 +1490,7 @@ static Eterm add_monitor(Process *p, DE_Handle *dh, Uint status)
{
Eterm r;
- assert_drv_list_locked();
+ assert_drv_list_rwlocked();
r = erts_make_ref(p);
add_proc_waiting(dh, p, status, r);
return r;
@@ -1535,7 +1501,7 @@ static void set_driver_reloading(DE_Handle *dh, Process *proc, char *path, char
{
DE_ProcEntry *p;
- assert_drv_list_locked();
+ assert_drv_list_rwlocked();
p = erts_alloc(ERTS_ALC_T_DDLL_PROCESS, sizeof(DE_ProcEntry));
p->proc = proc;
p->awaiting_status = ERL_DE_OK;
@@ -1556,7 +1522,7 @@ static int do_load_driver_entry(DE_Handle *dh, char *path, char *name)
int res;
ErlDrvEntry *dp;
- assert_drv_list_locked();
+ assert_drv_list_rwlocked();
if ((res = erts_sys_ddll_open(path, &(dh->handle))) != ERL_DE_NO_ERROR) {
return res;
@@ -1594,7 +1560,7 @@ static int do_load_driver_entry(DE_Handle *dh, char *path, char *name)
goto error;
}
erts_smp_atomic_init_nob(&(dh->refc), (erts_aint_t) 0);
- dh->port_count = 0;
+ erts_smp_atomic32_init_nob(&dh->port_count, 0);
dh->full_path = erts_alloc(ERTS_ALC_T_DDLL_HANDLE, sys_strlen(path) + 1);
sys_strcpy(dh->full_path, path);
dh->flags = 0;
@@ -1620,7 +1586,7 @@ static int do_unload_driver_entry(DE_Handle *dh, Eterm *save_name)
{
erts_driver_t *q, *p = driver_list;
- assert_drv_list_locked();
+ assert_drv_list_rwlocked();
while (p != NULL) {
if (p->handle == dh) {
@@ -1660,11 +1626,11 @@ static int load_driver_entry(DE_Handle **dhp, char *path, char *name)
int res;
DE_Handle *dh = erts_alloc(ERTS_ALC_T_DDLL_HANDLE, sizeof(DE_Handle));
- assert_drv_list_locked();
+ assert_drv_list_rwlocked();
dh->handle = NULL;
dh->procs = NULL;
- dh->port_count = 0;
+ erts_smp_atomic32_init_nob(&dh->port_count, 0);
erts_refc_init(&(dh->refc), (erts_aint_t) 0);
dh->status = -1;
dh->reload_full_path = NULL;
@@ -1698,7 +1664,7 @@ static int reload_driver_entry(DE_Handle *dh)
int loadres;
Uint flags = dh->reload_flags;
- assert_drv_list_locked();
+ assert_drv_list_rwlocked();
dh->reload_full_path = NULL;
dh->reload_driver_name = NULL;
@@ -1736,7 +1702,7 @@ static void notify_proc(Process *proc, Eterm ref, Eterm driver_name, Eterm type,
ErtsProcLocks rp_locks = 0;
ERTS_SMP_CHK_NO_PROC_LOCKS;
- assert_drv_list_locked();
+ assert_drv_list_rwlocked();
if (errcode != 0) {
int need = load_error_need(errcode);
Eterm e;
@@ -1769,7 +1735,7 @@ static void notify_all(DE_Handle *dh, char *name, Uint awaiting, Eterm type, Ete
{
DE_ProcEntry **p;
- assert_drv_list_locked();
+ assert_drv_list_rwlocked();
p = &(dh->procs);
while (*p != NULL) {
@@ -1869,13 +1835,16 @@ static Eterm build_load_error_hp(Eterm *hp, int code)
static Eterm mkatom(char *str)
{
- return am_atom_put(str, sys_strlen(str));
+ return erts_atom_put((byte *) str,
+ sys_strlen(str),
+ ERTS_ATOM_ENC_LATIN1,
+ 1);
}
static char *pick_list_or_atom(Eterm name_term)
{
char *name = NULL;
- Uint name_len;
+ ErlDrvSizeT name_len;
if (is_atom(name_term)) {
Atom *ap = atom_tab(atom_val(name_term));
if (ap->len == 0) {
@@ -1891,7 +1860,7 @@ static char *pick_list_or_atom(Eterm name_term)
goto error;
}
name = erts_alloc(ERTS_ALC_T_DDLL_TMP_BUF, name_len + 1);
- if (io_list_to_buf(name_term, name, name_len) != 0) {
+ if (erts_iolist_to_buf(name_term, name, name_len) != 0) {
goto error;
}
name[name_len] = '\0';
@@ -1912,10 +1881,10 @@ static int build_proc_info(DE_Handle *dh, ProcEntryInfo **out_pei, Uint filter)
int i;
DE_ProcEntry *pe;
- assert_drv_list_locked();
+ assert_drv_list_rwlocked();
for (pe = dh->procs; pe != NULL; pe = pe->next) {
- Eterm id = pe->proc->id;
+ Eterm id = pe->proc->common.id;
Uint stat = pe->awaiting_status;
if (stat == ERL_DE_PROC_AWAIT_UNLOAD_ONLY) {
stat = ERL_DE_PROC_AWAIT_UNLOAD;
diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c
index e2f7c8673f..b8026063e6 100755
--- a/erts/emulator/beam/erl_bif_info.c
+++ b/erts/emulator/beam/erl_bif_info.c
@@ -40,6 +40,8 @@
#include "erl_cpu_topology.h"
#include "erl_async.h"
#include "erl_thr_progress.h"
+#define ERTS_PTAB_WANT_DEBUG_FUNCS__
+#include "erl_ptab.h"
#ifdef HIPE
#include "hipe_arch.h"
#endif
@@ -128,8 +130,6 @@ static char erts_system_version[] = ("Erlang " ERLANG_OTP_RELEASE
static Eterm os_type_tuple;
static Eterm os_version_tuple;
-static BIF_RETTYPE port_info(Process* p, Eterm portid, Eterm item);
-
static Eterm
current_function(Process* p, Process* rp, Eterm** hpp, int full_info);
static Eterm current_stacktrace(Process* p, Process* rp, Eterm** hpp);
@@ -873,8 +873,7 @@ BIF_RETTYPE process_info_1(BIF_ALIST_1)
&& external_pid_dist_entry(BIF_ARG_1) == erts_this_dist_entry)
BIF_RET(am_undefined);
- if (is_not_internal_pid(BIF_ARG_1)
- || internal_pid_index(BIF_ARG_1) >= erts_max_processes) {
+ if (is_not_internal_pid(BIF_ARG_1)) {
BIF_ERROR(BIF_P, BADARG);
}
@@ -909,8 +908,7 @@ BIF_RETTYPE process_info_2(BIF_ALIST_2)
&& external_pid_dist_entry(pid) == erts_this_dist_entry)
BIF_RET(am_undefined);
- if (is_not_internal_pid(pid)
- || internal_pid_index(BIF_ARG_1) >= erts_max_processes) {
+ if (is_not_internal_pid(pid)) {
BIF_ERROR(BIF_P, BADARG);
}
@@ -1002,9 +1000,9 @@ process_info_aux(Process *BIF_P,
switch (item) {
case am_registered_name:
- if (rp->reg != NULL) {
+ if (rp->common.u.alive.reg) {
hp = HAlloc(BIF_P, 3);
- res = rp->reg->name;
+ res = rp->common.u.alive.reg->name;
} else {
if (always_wrap) {
hp = HAlloc(BIF_P, 3);
@@ -1050,7 +1048,7 @@ process_info_aux(Process *BIF_P,
ERTS_SMP_MSGQ_MV_INQ2PRIVQ(rp);
n = rp->msg.len;
- if (n == 0 || rp->trace_flags & F_SENSITIVE) {
+ if (n == 0 || ERTS_TRACE_FLAGS(rp) & F_SENSITIVE) {
hp = HAlloc(BIF_P, 3);
} else {
int remove_bad_messages = 0;
@@ -1209,7 +1207,7 @@ process_info_aux(Process *BIF_P,
INIT_MONITOR_INFOS(mic);
- erts_doforall_links(rp->nlinks,&collect_one_link,&mic);
+ erts_doforall_links(ERTS_P_LINKS(rp),&collect_one_link,&mic);
hp = HAlloc(BIF_P, 3 + mic.sz);
res = NIL;
@@ -1227,7 +1225,7 @@ process_info_aux(Process *BIF_P,
int i;
INIT_MONITOR_INFOS(mic);
- erts_doforall_monitors(rp->monitors,&collect_one_origin_monitor,&mic);
+ erts_doforall_monitors(ERTS_P_MONITORS(rp),&collect_one_origin_monitor,&mic);
hp = HAlloc(BIF_P, 3 + mic.sz);
res = NIL;
for (i = 0; i < mic.mi_i; i++) {
@@ -1264,7 +1262,7 @@ process_info_aux(Process *BIF_P,
Eterm item;
INIT_MONITOR_INFOS(mic);
- erts_doforall_monitors(rp->monitors,&collect_one_target_monitor,&mic);
+ erts_doforall_monitors(ERTS_P_MONITORS(rp),&collect_one_target_monitor,&mic);
hp = HAlloc(BIF_P, 3 + mic.sz);
res = NIL;
@@ -1330,7 +1328,7 @@ process_info_aux(Process *BIF_P,
}
case am_dictionary:
- if (rp->trace_flags & F_SENSITIVE) {
+ if (ERTS_TRACE_FLAGS(rp) & F_SENSITIVE) {
res = NIL;
} else {
res = erts_dictionary_copy(BIF_P, rp->dictionary);
@@ -1426,8 +1424,8 @@ process_info_aux(Process *BIF_P,
ERTS_SMP_MSGQ_MV_INQ2PRIVQ(rp);
- erts_doforall_links(rp->nlinks, &one_link_size, &size);
- erts_doforall_monitors(rp->monitors, &one_mon_size, &size);
+ erts_doforall_links(ERTS_P_LINKS(rp), &one_link_size, &size);
+ erts_doforall_monitors(ERTS_P_MONITORS(rp), &one_mon_size, &size);
size += (rp->heap_sz + rp->mbuf_sz) * sizeof(Eterm);
if (rp->old_hend && rp->old_heap)
size += (rp->old_hend - rp->old_heap) * sizeof(Eterm);
@@ -1500,7 +1498,7 @@ process_info_aux(Process *BIF_P,
case am_trace:
hp = HAlloc(BIF_P, 3);
- res = make_small(rp->trace_flags & TRACEE_FLAGS);
+ res = make_small(ERTS_TRACE_FLAGS(rp) & TRACEE_FLAGS);
break;
case am_binary: {
@@ -1605,7 +1603,7 @@ current_function(Process* BIF_P, Process* rp, Eterm** hpp, int full_info)
}
}
- if (BIF_P->id == rp->id) {
+ if (BIF_P == rp) {
FunctionInfo fi2;
/*
@@ -1699,7 +1697,7 @@ static int check_if_xml(void)
{
char buf[1];
size_t bufsz = sizeof(buf);
- return erts_sys_getenv("VALGRIND_LOG_XML", buf, &bufsz) >= 0;
+ return erts_sys_getenv_raw("VALGRIND_LOG_XML", buf, &bufsz) >= 0;
}
#else
#define check_if_xml() 0
@@ -1837,17 +1835,17 @@ info_1_tuple(Process* BIF_P, /* Pointer to current process. */
# define ERTS_ERROR_CHECKER_PRINTF_XML VALGRIND_PRINTF_XML
# endif
#endif
- Uint buf_size = 8*1024; /* Try with 8KB first */
+ ErlDrvSizeT buf_size = 8*1024; /* Try with 8KB first */
char *buf = erts_alloc(ERTS_ALC_T_TMP, buf_size);
- int r = io_list_to_buf(*tp, (char*) buf, buf_size - 1);
- if (r < 0) {
+ ErlDrvSizeT r = erts_iolist_to_buf(*tp, (char*) buf, buf_size - 1);
+ if (ERTS_IOLIST_TO_BUF_FAILED(r)) {
erts_free(ERTS_ALC_T_TMP, (void *) buf);
if (erts_iolist_size(*tp, &buf_size)) {
goto badarg;
}
buf_size++;
buf = erts_alloc(ERTS_ALC_T_TMP, buf_size);
- r = io_list_to_buf(*tp, (char*) buf, buf_size - 1);
+ r = erts_iolist_to_buf(*tp, (char*) buf, buf_size - 1);
ASSERT(r == buf_size - 1);
}
buf[buf_size - 1 - r] = '\0';
@@ -2159,9 +2157,13 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1)
res = TUPLE2(hp, am_min_bin_vheap_size,make_small(BIN_VH_MIN_SIZE));
BIF_RET(res);
} else if (BIF_ARG_1 == am_process_count) {
- BIF_RET(make_small(erts_process_count()));
+ BIF_RET(make_small(erts_ptab_count(&erts_proc)));
} else if (BIF_ARG_1 == am_process_limit) {
- BIF_RET(make_small(erts_max_processes));
+ BIF_RET(make_small(erts_ptab_max(&erts_proc)));
+ } else if (BIF_ARG_1 == am_port_count) {
+ BIF_RET(make_small(erts_ptab_count(&erts_port)));
+ } else if (BIF_ARG_1 == am_port_limit) {
+ BIF_RET(make_small(erts_ptab_max(&erts_port)));
} else if (BIF_ARG_1 == am_info
|| BIF_ARG_1 == am_procs
|| BIF_ARG_1 == am_loaded
@@ -2296,8 +2298,10 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1)
for (i = num_instructions-1; i >= 0; i--) {
res = erts_bld_cons(hpp, hszp,
erts_bld_tuple(hpp, hszp, 2,
- am_atom_put(opc[i].name,
- strlen(opc[i].name)),
+ erts_atom_put(opc[i].name,
+ strlen(opc[i].name),
+ ERTS_ATOM_ENC_LATIN1,
+ 1),
erts_bld_uint(hpp, hszp,
opc[i].count)),
res);
@@ -2534,6 +2538,9 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1)
} else if (ERTS_IS_ATOM_STR("run_queues", BIF_ARG_1)) {
res = make_small(erts_no_run_queues);
BIF_RET(res);
+ } else if (ERTS_IS_ATOM_STR("port_parallelism", BIF_ARG_1)) {
+ res = erts_port_parallelism ? am_true : am_false;
+ BIF_RET(res);
} else if (ERTS_IS_ATOM_STR("c_compiler_used", BIF_ARG_1)) {
Eterm *hp = NULL;
Uint sz = 0;
@@ -2701,66 +2708,6 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1)
BIF_ERROR(BIF_P, BADARG);
}
-BIF_RETTYPE
-port_info_1(BIF_ALIST_1)
-{
- Process* p = BIF_P;
- Eterm pid = BIF_ARG_1;
- static Eterm keys[] = {
- am_name,
- am_links,
- am_id,
- am_connected,
- am_input,
- am_output,
- am_os_pid
- };
- Eterm items[ASIZE(keys)];
- Eterm result = NIL;
- Eterm reg_name;
- Eterm* hp;
- Uint need;
- int i;
-
- /*
- * Collect all information about the port.
- */
-
- for (i = 0; i < ASIZE(keys); i++) {
- Eterm item;
-
- item = port_info(p, pid, keys[i]);
- if (is_non_value(item)) {
- return THE_NON_VALUE;
- }
- if (item == am_undefined) {
- return am_undefined;
- }
- items[i] = item;
- }
- reg_name = port_info(p, pid, am_registered_name);
-
- /*
- * Build the resulting list.
- */
-
- need = 2*ASIZE(keys);
- if (is_tuple(reg_name)) {
- need += 2;
- }
- hp = HAlloc(p, need);
- for (i = ASIZE(keys) - 1; i >= 0; i--) {
- result = CONS(hp, items[i], result);
- hp += 2;
- }
- if (is_tuple(reg_name)) {
- result = CONS(hp, reg_name, result);
- }
-
- return result;
-}
-
-
/**********************************************************************/
/* Return information on ports */
/* Info:
@@ -2773,38 +2720,20 @@ port_info_1(BIF_ALIST_1)
** os_pid The child's process ID
*/
-BIF_RETTYPE port_info_2(BIF_ALIST_2)
-{
- return port_info(BIF_P, BIF_ARG_1, BIF_ARG_2);
-}
-
-static BIF_RETTYPE port_info(Process* p, Eterm portid, Eterm item)
+Eterm
+erts_bld_port_info(Eterm **hpp, ErlOffHeap *ohp, Uint *szp, Port *prt, Eterm item)
{
- BIF_RETTYPE ret;
- Port *prt;
- Eterm res;
- Eterm* hp;
- int count;
-
- if (is_internal_port(portid))
- prt = erts_id2port(portid, p, ERTS_PROC_LOCK_MAIN);
- else if (is_atom(portid))
- erts_whereis_name(p, ERTS_PROC_LOCK_MAIN,
- portid, NULL, 0, 0, &prt);
- else if (is_external_port(portid)
- && external_port_dist_entry(portid) == erts_this_dist_entry)
- BIF_RET(am_undefined);
- else {
- BIF_ERROR(p, BADARG);
- }
+ Eterm res = THE_NON_VALUE;
- if (!prt) {
- BIF_RET(am_undefined);
- }
+ ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
if (item == am_id) {
- hp = HAlloc(p, 3);
- res = make_small(internal_port_number(portid));
+ if (hpp)
+ res = make_small(internal_port_index(prt->common.id));
+ if (szp) {
+ res = am_true;
+ goto done;
+ }
}
else if (item == am_links) {
MonitorInfoCollection mic;
@@ -2813,17 +2742,26 @@ static BIF_RETTYPE port_info(Process* p, Eterm portid, Eterm item)
INIT_MONITOR_INFOS(mic);
- erts_doforall_links(prt->nlinks, &collect_one_link, &mic);
+ erts_doforall_links(ERTS_P_LINKS(prt), &collect_one_link, &mic);
- hp = HAlloc(p, 3 + mic.sz);
- res = NIL;
- for (i = 0; i < mic.mi_i; i++) {
- item = STORE_NC(&hp, &MSO(p), mic.mi[i].entity);
- res = CONS(hp, item, res);
- hp += 2;
+ if (szp)
+ *szp += mic.sz;
+
+ if (hpp) {
+ res = NIL;
+ for (i = 0; i < mic.mi_i; i++) {
+ item = STORE_NC(hpp, ohp, mic.mi[i].entity);
+ res = CONS(*hpp, item, res);
+ *hpp += 2;
+ }
}
+
DESTROY_MONITOR_INFOS(mic);
+ if (szp) {
+ res = am_true;
+ goto done;
+ }
}
else if (item == am_monitors) {
MonitorInfoCollection mic;
@@ -2832,79 +2770,96 @@ static BIF_RETTYPE port_info(Process* p, Eterm portid, Eterm item)
INIT_MONITOR_INFOS(mic);
- erts_doforall_monitors(prt->monitors, &collect_one_origin_monitor, &mic);
+ erts_doforall_monitors(ERTS_P_MONITORS(prt), &collect_one_origin_monitor, &mic);
- hp = HAlloc(p, 3 + mic.sz);
- res = NIL;
- for (i = 0; i < mic.mi_i; i++) {
- Eterm t;
- item = STORE_NC(&hp, &MSO(p), mic.mi[i].entity);
- t = TUPLE2(hp, am_process, item);
- hp += 3;
- res = CONS(hp, t, res);
- hp += 2;
+ if (szp)
+ *szp += mic.sz;
+
+ if (hpp) {
+ res = NIL;
+ for (i = 0; i < mic.mi_i; i++) {
+ Eterm t;
+ item = STORE_NC(hpp, ohp, mic.mi[i].entity);
+ t = TUPLE2(*hpp, am_process, item);
+ *hpp += 3;
+ res = CONS(*hpp, t, res);
+ *hpp += 2;
+ }
}
+
DESTROY_MONITOR_INFOS(mic);
+ if (szp) {
+ res = am_true;
+ goto done;
+ }
}
else if (item == am_name) {
- count = sys_strlen(prt->name);
+ int count = sys_strlen(prt->name);
+
+ if (hpp)
+ res = buf_to_intlist(hpp, prt->name, count, NIL);
- hp = HAlloc(p, 3 + 2*count);
- res = buf_to_intlist(&hp, prt->name, count, NIL);
+ if (szp) {
+ *szp += 2*count;
+ res = am_true;
+ goto done;
+ }
}
else if (item == am_connected) {
- hp = HAlloc(p, 3);
- res = prt->connected; /* internal pid */
+ if (hpp)
+ res = ERTS_PORT_GET_CONNECTED(prt); /* internal pid */
+ if (szp) {
+ res = am_true;
+ goto done;
+ }
}
else if (item == am_input) {
- Uint hsz = 3;
- Uint n = prt->bytes_in;
- (void) erts_bld_uint(NULL, &hsz, n);
- hp = HAlloc(p, hsz);
- res = erts_bld_uint(&hp, NULL, n);
+ res = erts_bld_uint(hpp, szp, prt->bytes_in);
+ if (szp) {
+ res = am_true;
+ goto done;
+ }
}
else if (item == am_output) {
- Uint hsz = 3;
- Uint n = prt->bytes_out;
- (void) erts_bld_uint(NULL, &hsz, n);
- hp = HAlloc(p, hsz);
- res = erts_bld_uint(&hp, NULL, n);
+ res = erts_bld_uint(hpp, szp, prt->bytes_out);
+ if (szp) {
+ res = am_true;
+ goto done;
+ }
}
else if (item == am_os_pid) {
- if (prt->os_pid >= 0) {
- Uint hsz = 3;
- UWord n = prt->os_pid;
- (void) erts_bld_uword(NULL, &hsz, n);
- hp = HAlloc(p, hsz);
- res = erts_bld_uword(&hp, NULL, n);
- } else {
- hp = HAlloc(p, 3);
- res = am_undefined;
- }
+ res = (prt->os_pid < 0
+ ? am_undefined
+ : erts_bld_uword(hpp, szp, (UWord) prt->os_pid));
+ if (szp) {
+ res = am_true;
+ goto done;
+ }
}
else if (item == am_registered_name) {
- RegProc *reg;
- reg = prt->reg;
- if (reg == NULL) {
- ERTS_BIF_PREP_RET(ret, NIL);
- goto done;
- } else {
- hp = HAlloc(p, 3);
+ RegProc *reg = prt->common.u.alive.reg;
+ if (reg) {
res = reg->name;
+ if (szp) {
+ res = am_true;
+ goto done;
+ }
+ }
+ else {
+ if (szp)
+ return am_undefined;
+ return NIL;
}
}
else if (item == am_memory) {
/* All memory consumed in bytes (the Port struct should not be
included though).
*/
- Uint hsz = 3;
Uint size = 0;
ErlHeapFragment* bp;
- hp = HAlloc(p, 3);
-
- erts_doforall_links(prt->nlinks, &one_link_size, &size);
+ erts_doforall_links(ERTS_P_LINKS(prt), &one_link_size, &size);
for (bp = prt->bp; bp; bp = bp->next)
size += sizeof(ErlHeapFragment) + (bp->alloc_size - 1)*sizeof(Eterm);
@@ -2918,51 +2873,72 @@ static BIF_RETTYPE port_info(Process* p, Eterm portid, Eterm item)
/* All memory allocated by the driver should be included, but it is
hard to retrieve... */
- (void) erts_bld_uint(NULL, &hsz, size);
- hp = HAlloc(p, hsz);
- res = erts_bld_uint(&hp, NULL, size);
+ res = erts_bld_uint(hpp, szp, size);
+ if (szp) {
+ res = am_true;
+ goto done;
+ }
}
else if (item == am_queue_size) {
Uint ioq_size = erts_port_ioq_size(prt);
- Uint hsz = 3;
- (void) erts_bld_uint(NULL, &hsz, ioq_size);
- hp = HAlloc(p, hsz);
- res = erts_bld_uint(&hp, NULL, ioq_size);
+ res = erts_bld_uint(hpp, szp, ioq_size);
+ if (szp) {
+ res = am_true;
+ goto done;
+ }
}
else if (ERTS_IS_ATOM_STR("locking", item)) {
- hp = HAlloc(p, 3);
+ if (hpp) {
#ifndef ERTS_SMP
- res = am_false;
+ res = am_false;
#else
- if (prt->status & ERTS_PORT_SFLG_PORT_SPECIFIC_LOCK) {
- DECL_AM(port_level);
- ASSERT(prt->drv_ptr->flags
- & ERL_DRV_FLAG_USE_PORT_LOCKING);
- res = AM_port_level;
+ if (erts_atomic32_read_nob(&prt->state)
+ & ERTS_PORT_SFLG_PORT_SPECIFIC_LOCK) {
+ DECL_AM(port_level);
+ ASSERT(prt->drv_ptr->flags
+ & ERL_DRV_FLAG_USE_PORT_LOCKING);
+ res = AM_port_level;
+ }
+ else {
+ DECL_AM(driver_level);
+ ASSERT(!(prt->drv_ptr->flags
+ & ERL_DRV_FLAG_USE_PORT_LOCKING));
+ res = AM_driver_level;
+ }
+#endif
}
- else {
- DECL_AM(driver_level);
- ASSERT(!(prt->drv_ptr->flags
- & ERL_DRV_FLAG_USE_PORT_LOCKING));
- res = AM_driver_level;
+ if (szp) {
+ res = am_true;
+ goto done;
}
-#endif
+ }
+ else if (item == am_parallelism) {
+ if (szp) {
+ res = am_true;
+ goto done;
+ }
+ res = ((ERTS_PTS_FLG_PARALLELISM &
+ erts_smp_atomic32_read_nob(&prt->sched.flags))
+ ? am_true
+ : am_false);
}
else {
- ERTS_BIF_PREP_ERROR(ret, p, BADARG);
- goto done;
+ if (szp)
+ return am_false;
+ return THE_NON_VALUE;
}
- ERTS_BIF_PREP_RET(ret, TUPLE2(hp, item, res));
-
- done:
-
- erts_smp_port_unlock(prt);
+done:
+ if (szp)
+ *szp += 3;
+ if (hpp) {
+ res = TUPLE2(*hpp, item, res);
+ *hpp += 3;
+ }
- return ret;
+ return res;
}
-
BIF_RETTYPE
fun_info_2(BIF_ALIST_2)
{
@@ -3094,12 +3070,9 @@ BIF_RETTYPE is_process_alive_1(BIF_ALIST_1)
if(is_internal_pid(BIF_ARG_1)) {
Process *rp;
- if (BIF_ARG_1 == BIF_P->id)
+ if (BIF_ARG_1 == BIF_P->common.id)
BIF_RET(am_true);
- if(internal_pid_index(BIF_ARG_1) >= erts_max_processes)
- BIF_ERROR(BIF_P, BADARG);
-
rp = erts_proc_lookup(BIF_ARG_1);
if (!rp) {
BIF_RET(am_false);
@@ -3317,10 +3290,9 @@ BIF_RETTYPE erts_debug_get_internal_state_1(BIF_ALIST_1)
/* Used by node_container_SUITE (emulator) */
Eterm res;
if (ERTS_IS_ATOM_STR("next_pid", BIF_ARG_1))
- res = erts_test_next_pid(0, 0);
- else {
- res = erts_test_next_port(0, 0);
- }
+ res = erts_ptab_test_next_id(&erts_proc, 0, 0);
+ else
+ res = erts_ptab_test_next_id(&erts_port, 0, 0);
if (res < 0)
BIF_RET(am_false);
BIF_RET(erts_make_integer(res, BIF_P));
@@ -3356,11 +3328,11 @@ BIF_RETTYPE erts_debug_get_internal_state_1(BIF_ALIST_1)
}
else if (ERTS_IS_ATOM_STR("processes", BIF_ARG_1)) {
/* Used by process_SUITE (emulator) */
- BIF_RET(erts_debug_processes(BIF_P));
+ BIF_RET(erts_debug_ptab_list(BIF_P, &erts_proc));
}
else if (ERTS_IS_ATOM_STR("processes_bif_info", BIF_ARG_1)) {
/* Used by process_SUITE (emulator) */
- BIF_RET(erts_debug_processes_bif_info(BIF_P));
+ BIF_RET(erts_debug_ptab_list_bif_info(BIF_P, &erts_proc));
}
else if (ERTS_IS_ATOM_STR("max_atom_out_cache_index", BIF_ARG_1)) {
/* Used by distribution_SUITE (emulator) */
@@ -3421,17 +3393,20 @@ BIF_RETTYPE erts_debug_get_internal_state_1(BIF_ALIST_1)
ERTS_SMP_ASSERT_IS_NOT_EXITING(BIF_P);
BIF_RET(am_undefined);
}
- res = make_link_list(BIF_P, p->nlinks, NIL);
+ res = make_link_list(BIF_P, ERTS_P_LINKS(p), NIL);
erts_smp_proc_unlock(p, ERTS_PROC_LOCK_LINK);
BIF_RET(res);
}
else if(is_internal_port(tp[2])) {
Eterm res;
- Port *p = erts_id2port(tp[2], BIF_P, ERTS_PROC_LOCK_MAIN);
+ Port *p = erts_id2port_sflgs(tp[2],
+ BIF_P,
+ ERTS_PROC_LOCK_MAIN,
+ ERTS_PORT_SFLGS_INVALID_LOOKUP);
if(!p)
BIF_RET(am_undefined);
- res = make_link_list(BIF_P, p->nlinks, NIL);
- erts_smp_port_unlock(p);
+ res = make_link_list(BIF_P, ERTS_P_LINKS(p), NIL);
+ erts_port_release(p);
BIF_RET(res);
}
else if(is_node_name_atom(tp[2])) {
@@ -3463,7 +3438,7 @@ BIF_RETTYPE erts_debug_get_internal_state_1(BIF_ALIST_1)
ERTS_SMP_ASSERT_IS_NOT_EXITING(BIF_P);
BIF_RET(am_undefined);
}
- res = make_monitor_list(BIF_P, p->monitors);
+ res = make_monitor_list(BIF_P, ERTS_P_MONITORS(p));
erts_smp_proc_unlock(p, ERTS_PROC_LOCK_LINK);
BIF_RET(res);
} else if(is_node_name_atom(tp[2])) {
@@ -3606,7 +3581,7 @@ BIF_RETTYPE erts_debug_set_internal_state_2(BIF_ALIST_2)
erts_aint_t prev_on = erts_smp_atomic_xchg_nob(&available_internal_state, on);
if (on) {
erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf();
- erts_dsprintf(dsbufp, "Process %T ", BIF_P->id);
+ erts_dsprintf(dsbufp, "Process %T ", BIF_P->common.id);
if (erts_is_alive)
erts_dsprintf(dsbufp, "on node %T ", erts_this_node->sysname);
erts_dsprintf(dsbufp,
@@ -3674,10 +3649,9 @@ BIF_RETTYPE erts_debug_set_internal_state_2(BIF_ALIST_2)
Eterm res;
if (ERTS_IS_ATOM_STR("next_pid", BIF_ARG_1))
- res = erts_test_next_pid(1, next);
- else {
- res = erts_test_next_port(1, next);
- }
+ res = erts_ptab_test_next_id(&erts_proc, 1, next);
+ else
+ res = erts_ptab_test_next_id(&erts_port, 1, next);
if (res < 0)
BIF_RET(am_false);
BIF_RET(erts_make_integer(res, BIF_P));
@@ -3901,7 +3875,7 @@ static Eterm lcnt_build_lock_stats_term(Eterm **hpp, Uint *szp, erts_lcnt_lock_s
timer_ns = stats->timer.ns;
timer_n = stats->timer_n;
- af = am_atom_put(stats->file, strlen(stats->file));
+ af = erts_atom_put(stats->file, strlen(stats->file), ERTS_ATOM_ENC_LATIN1, 1);
uil = erts_bld_uint( hpp, szp, line);
tloc = erts_bld_tuple(hpp, szp, 2, af, uil);
@@ -3938,18 +3912,18 @@ static Eterm lcnt_build_lock_term(Eterm **hpp, Uint *szp, erts_lcnt_lock_t *lock
ASSERT(ltype);
- type = am_atom_put(ltype, strlen(ltype));
- name = am_atom_put(lock->name, strlen(lock->name));
+ type = erts_atom_put(ltype, strlen(ltype), ERTS_ATOM_ENC_LATIN1, 1);
+ name = erts_atom_put(lock->name, strlen(lock->name), ERTS_ATOM_ENC_LATIN1, 1);
if (lock->flag & ERTS_LCNT_LT_ALLOC) {
/* use allocator types names as id's for allocator locks */
ltype = (char *) ERTS_ALC_A2AD(signed_val(lock->id));
- id = am_atom_put(ltype, strlen(ltype));
+ id = erts_atom_put(ltype, strlen(ltype), ERTS_ATOM_ENC_LATIN1, 1);
} else if (lock->flag & ERTS_LCNT_LT_PROCLOCK) {
/* use registered names as id's for process locks if available */
proc = erts_proc_lookup(lock->id);
- if (proc && proc->reg) {
- id = proc->reg->name;
+ if (proc && proc->common.u.alive.reg) {
+ id = proc->common.u.alive.reg->name;
} else {
/* otherwise use process id */
id = lock->id;
@@ -3984,12 +3958,12 @@ static Eterm lcnt_build_result_term(Eterm **hpp, Uint *szp, erts_lcnt_data_t *da
dtns = erts_bld_uint( hpp, szp, data->duration.ns);
tdt = erts_bld_tuple(hpp, szp, 2, dts, dtns);
- adur = am_atom_put(str_duration, strlen(str_duration));
+ adur = erts_atom_put(str_duration, strlen(str_duration), ERTS_ATOM_ENC_LATIN1, 1);
tdur = erts_bld_tuple(hpp, szp, 2, adur, tdt);
/* lock tuple */
- aloc = am_atom_put(str_locks, strlen(str_locks));
+ aloc = erts_atom_put(str_locks, strlen(str_locks), ERTS_ATOM_ENC_LATIN1, 1);
for (lock = data->current_locks->head; lock != NULL ; lock = lock->next ) {
lloc = lcnt_build_lock_term(hpp, szp, lock, lloc);
@@ -4125,14 +4099,14 @@ BIF_RETTYPE erts_debug_lock_counters_1(BIF_ALIST_1)
static void os_info_init(void)
{
- Eterm type = am_atom_put(os_type, strlen(os_type));
+ Eterm type = erts_atom_put((byte *) os_type, strlen(os_type), ERTS_ATOM_ENC_LATIN1, 1);
Eterm flav;
int major, minor, build;
char* buf = erts_alloc(ERTS_ALC_T_TMP, 1024); /* More than enough */
Eterm* hp;
os_flavor(buf, 1024);
- flav = am_atom_put(buf, strlen(buf));
+ flav = erts_atom_put((byte *) buf, strlen(buf), ERTS_ATOM_ENC_LATIN1, 1);
erts_free(ERTS_ALC_T_TMP, (void *) buf);
hp = erts_alloc(ERTS_ALC_T_LL_TEMP_TERM, (3+4)*sizeof(Eterm));
os_type_tuple = TUPLE2(hp, type, flav);
diff --git a/erts/emulator/beam/erl_bif_op.c b/erts/emulator/beam/erl_bif_op.c
index 13f8b1f63c..d4cd9c89b3 100644
--- a/erts/emulator/beam/erl_bif_op.c
+++ b/erts/emulator/beam/erl_bif_op.c
@@ -261,11 +261,6 @@ Eterm erl_is_function(Process* p, Eterm arg1, Eterm arg2)
if (exp->code[2] == (Uint) arity) {
BIF_RET(am_true);
}
- } else if (is_tuple(arg1)) {
- Eterm* tp = tuple_val(arg1);
- if (tp[0] == make_arityval(2) && is_atom(tp[1]) && is_atom(tp[2])) {
- BIF_RET(am_true);
- }
}
BIF_RET(am_false);
}
diff --git a/erts/emulator/beam/erl_bif_os.c b/erts/emulator/beam/erl_bif_os.c
index 58d48199fa..1062d4379b 100644
--- a/erts/emulator/beam/erl_bif_os.c
+++ b/erts/emulator/beam/erl_bif_os.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1999-2010. All Rights Reserved.
+ * Copyright Ericsson AB 1999-2012. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -58,7 +58,7 @@ BIF_RETTYPE os_getpid_0(BIF_ALIST_0)
char pid_string[21]; /* enough for a 64 bit number */
int n;
Eterm* hp;
- sys_get_pid(pid_string); /* In sys.c */
+ sys_get_pid(pid_string, sizeof(pid_string)); /* In sys.c */
n = sys_strlen(pid_string);
hp = HAlloc(BIF_P, n*2);
BIF_RET(buf_to_intlist(&hp, pid_string, n, NIL));
@@ -71,15 +71,13 @@ BIF_RETTYPE os_getenv_0(BIF_ALIST_0)
Eterm* hp;
Eterm ret;
Eterm str;
- int len;
init_getenv_state(&state);
ret = NIL;
while ((cp = getenv_string(&state)) != NULL) {
- len = strlen(cp);
- hp = HAlloc(BIF_P, len*2+2);
- str = buf_to_intlist(&hp, cp, len, NIL);
+ str = erts_convert_native_to_filename(BIF_P,(byte *)cp);
+ hp = HAlloc(BIF_P, 2);
ret = CONS(hp, str, ret);
}
@@ -88,32 +86,30 @@ BIF_RETTYPE os_getenv_0(BIF_ALIST_0)
return ret;
}
-
+#define STATIC_BUF_SIZE 1024
BIF_RETTYPE os_getenv_1(BIF_ALIST_1)
{
Process* p = BIF_P;
- Eterm key = BIF_ARG_1;
Eterm str;
- int len, res;
+ Sint len;
+ int res;
char *key_str, *val;
- char buf[1024];
+ char buf[STATIC_BUF_SIZE];
size_t val_size = sizeof(buf);
- len = is_string(key);
- if (!len) {
+ key_str = erts_convert_filename_to_native(BIF_ARG_1,buf,STATIC_BUF_SIZE,
+ ERTS_ALC_T_TMP,1,0,&len);
+
+ if (!key_str) {
BIF_ERROR(p, BADARG);
}
- /* Leave at least one byte in buf for value */
- key_str = len < sizeof(buf)-2 ? &buf[0] : erts_alloc(ERTS_ALC_T_TMP, len+1);
- if (intlist_to_buf(key, key_str, len) != len)
- erl_exit(1, "%s:%d: Internal error\n", __FILE__, __LINE__);
- key_str[len] = '\0';
if (key_str != &buf[0])
val = &buf[0];
else {
- val_size -= len + 1;
- val = &buf[len + 1];
+ /* len includes zero byte */
+ val_size -= len;
+ val = &buf[len];
}
res = erts_sys_getenv(key_str, val, &val_size);
@@ -121,7 +117,6 @@ BIF_RETTYPE os_getenv_1(BIF_ALIST_1)
no_var:
str = am_false;
} else {
- Eterm* hp;
if (res > 0) {
val = erts_alloc(ERTS_ALC_T_TMP, val_size);
while (1) {
@@ -134,9 +129,7 @@ BIF_RETTYPE os_getenv_1(BIF_ALIST_1)
val = erts_realloc(ERTS_ALC_T_TMP, val, val_size);
}
}
- if (val_size)
- hp = HAlloc(p, val_size*2);
- str = buf_to_intlist(&hp, val, val_size, NIL);
+ str = erts_convert_native_to_filename(p,(byte *)val);
}
if (key_str != &buf[0])
erts_free(ERTS_ALC_T_TMP, key_str);
@@ -147,46 +140,43 @@ BIF_RETTYPE os_getenv_1(BIF_ALIST_1)
BIF_RETTYPE os_putenv_2(BIF_ALIST_2)
{
- Process* p = BIF_P;
- Eterm key = BIF_ARG_1;
- Eterm value = BIF_ARG_2;
- char def_buf[1024];
- char *buf = NULL;
- int sep_ix, i, key_len, value_len, tot_len;
- key_len = is_string(key);
- if (!key_len) {
- error:
- if (buf)
- erts_free(ERTS_ALC_T_TMP, (void *) buf);
- BIF_ERROR(p, BADARG);
+ char def_buf_key[STATIC_BUF_SIZE];
+ char def_buf_value[STATIC_BUF_SIZE];
+ char *key_buf, *value_buf;
+
+ key_buf = erts_convert_filename_to_native(BIF_ARG_1,def_buf_key,
+ STATIC_BUF_SIZE,
+ ERTS_ALC_T_TMP,0,0,NULL);
+ if (!key_buf) {
+ BIF_ERROR(BIF_P, BADARG);
}
- if (is_nil(value))
- value_len = 0;
- else {
- value_len = is_string(value);
- if (!value_len)
- goto error;
+ value_buf = erts_convert_filename_to_native(BIF_ARG_2,def_buf_value,
+ STATIC_BUF_SIZE,
+ ERTS_ALC_T_TMP,1,0,
+ NULL);
+ if (!value_buf) {
+ if (key_buf != def_buf_key) {
+ erts_free(ERTS_ALC_T_TMP, key_buf);
+ }
+ BIF_ERROR(BIF_P, BADARG);
+ }
+
+
+ if (erts_sys_putenv(key_buf, value_buf)) {
+ if (key_buf != def_buf_key) {
+ erts_free(ERTS_ALC_T_TMP, key_buf);
+ }
+ if (value_buf != def_buf_value) {
+ erts_free(ERTS_ALC_T_TMP, value_buf);
+ }
+ BIF_ERROR(BIF_P, BADARG);
+ }
+ if (key_buf != def_buf_key) {
+ erts_free(ERTS_ALC_T_TMP, key_buf);
}
- tot_len = key_len + 1 + value_len + 1;
- if (tot_len <= sizeof(def_buf))
- buf = &def_buf[0];
- else
- buf = erts_alloc(ERTS_ALC_T_TMP, tot_len);
- i = intlist_to_buf(key, buf, key_len);
- if (i != key_len)
- erl_exit(1, "%s:%d: Internal error\n", __FILE__, __LINE__);
- sep_ix = i;
- buf[i++] = '=';
- if (is_not_nil(value))
- i += intlist_to_buf(value, &buf[i], value_len);
- if (i != key_len + 1 + value_len)
- erl_exit(1, "%s:%d: Internal error\n", __FILE__, __LINE__);
- buf[i] = '\0';
- if (erts_sys_putenv(buf, sep_ix)) {
- goto error;
+ if (value_buf != def_buf_value) {
+ erts_free(ERTS_ALC_T_TMP, value_buf);
}
- if (buf != &def_buf[0])
- erts_free(ERTS_ALC_T_TMP, (void *) buf);
BIF_RET(am_true);
}
diff --git a/erts/emulator/beam/erl_bif_port.c b/erts/emulator/beam/erl_bif_port.c
index 2a1b01b107..a4b837541b 100644
--- a/erts/emulator/beam/erl_bif_port.c
+++ b/erts/emulator/beam/erl_bif_port.c
@@ -42,578 +42,438 @@
#include "erl_bits.h"
#include "dtrace-wrapper.h"
-static int open_port(Process* p, Eterm name, Eterm settings, int *err_nump);
+static Port *open_port(Process* p, Eterm name, Eterm settings, int *err_typep, int *err_nump);
static byte* convert_environment(Process* p, Eterm env);
static char **convert_args(Eterm);
static void free_args(char **);
char *erts_default_arg0 = "default";
-static BIF_RETTYPE
-port_call(Process* p, Eterm arg1, Eterm arg2, Eterm arg3);
-
BIF_RETTYPE open_port_2(BIF_ALIST_2)
{
- int port_num;
- Eterm port_val;
+ Port *port;
+ Eterm port_id;
char *str;
- int err_num;
+ int err_type, err_num;
- if ((port_num = open_port(BIF_P, BIF_ARG_1, BIF_ARG_2, &err_num)) < 0) {
- if (port_num == -3) {
+ port = open_port(BIF_P, BIF_ARG_1, BIF_ARG_2, &err_type, &err_num);
+ if (!port) {
+ if (err_type == -3) {
ASSERT(err_num == BADARG || err_num == SYSTEM_LIMIT);
BIF_ERROR(BIF_P, err_num);
- } else if (port_num == -2) {
+ } else if (err_type == -2) {
str = erl_errno_id(err_num);
} else {
str = "einval";
}
- BIF_P->fvalue = am_atom_put(str, strlen(str));
+ BIF_P->fvalue = erts_atom_put((byte *) str, strlen(str), ERTS_ATOM_ENC_LATIN1, 1);
BIF_ERROR(BIF_P, EXC_ERROR);
}
erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_LINK);
- port_val = erts_port[port_num].id;
- erts_add_link(&(erts_port[port_num].nlinks), LINK_PID, BIF_P->id);
- erts_add_link(&(BIF_P->nlinks), LINK_PID, port_val);
+ port_id = port->common.id;
+ erts_add_link(&ERTS_P_LINKS(port), LINK_PID, BIF_P->common.id);
+ erts_add_link(&ERTS_P_LINKS(BIF_P), LINK_PID, port_id);
erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_LINK);
- erts_port_release(&erts_port[port_num]);
+ erts_port_release(port);
- BIF_RET(port_val);
+ BIF_RET(port_id);
}
-/****************************************************************************
-
- PORT BIFS:
-
- port_command/2 -- replace Port ! {..., {command, Data}}
- port_command(Port, Data) -> true
- when port(Port), io-list(Data)
-
- port_control/3 -- new port_control(Port, Ctl, Data) -> Reply
- port_control(Port, Ctl, Data) -> Reply
- where integer(Ctl), io-list(Data), io-list(Reply)
-
- port_close/1 -- replace Port ! {..., close}
- port_close(Port) -> true
- when port(Port)
-
- port_connect/2 -- replace Port ! {..., {connect, Pid}}
- port_connect(Port, Pid)
- when port(Port), pid(Pid)
-
- ***************************************************************************/
-
-static Port*
-id_or_name2port(Process *c_p, Eterm id)
+static ERTS_INLINE Port *
+lookup_port(Process *c_p, Eterm id_or_name)
{
- Port *port;
- if (is_not_atom(id))
- port = erts_id2port(id, c_p, ERTS_PROC_LOCK_MAIN);
+ /* TODO: Implement nicer lookup in register... */
+ Eterm id;
+ if (is_atom(id_or_name))
+ id = erts_whereis_name_to_id(c_p, id_or_name);
else
- erts_whereis_name(c_p, ERTS_PROC_LOCK_MAIN, id, NULL, 0, 0, &port);
- return port;
+ id = id_or_name;
+ return erts_port_lookup(id, ERTS_PORT_SFLGS_INVALID_LOOKUP);
}
-#define ERTS_PORT_COMMAND_FLAG_FORCE (((Uint32) 1) << 0)
-#define ERTS_PORT_COMMAND_FLAG_NOSUSPEND (((Uint32) 1) << 1)
+/*
+ * erts_internal:port_command/3 is used by the
+ * erlang:port_command/2 and erlang:port_command/3
+ * BIFs.
+ */
-static BIF_RETTYPE
-do_port_command(Process *BIF_P, Eterm arg1, Eterm arg2, Eterm arg3,
- Uint32 flags)
+BIF_RETTYPE erts_internal_port_command_3(BIF_ALIST_3)
{
BIF_RETTYPE res;
- Port *p;
-
- /* Trace sched out before lock check wait */
- if (IS_TRACED_FL(BIF_P, F_TRACE_SCHED_PROCS)) {
- trace_virtual_sched(BIF_P, am_out);
- }
-
- if (erts_system_profile_flags.runnable_procs && erts_system_profile_flags.exclusive) {
- profile_runnable_proc(BIF_P, am_inactive);
- }
-
- p = id_or_name2port(BIF_P, arg1);
- if (!p) {
- if (IS_TRACED_FL(BIF_P, F_TRACE_SCHED_PROCS)) {
- trace_virtual_sched(BIF_P, am_in);
+ Port *prt;
+ int flags = 0;
+ Eterm ref;
+
+ if (is_not_nil(BIF_ARG_3)) {
+ Eterm l = BIF_ARG_3;
+ while (is_list(l)) {
+ Eterm* cons = list_val(l);
+ Eterm car = CAR(cons);
+ if (car == am_force)
+ flags |= ERTS_PORT_SIG_FLG_FORCE;
+ else if (car == am_nosuspend)
+ flags |= ERTS_PORT_SIG_FLG_NOSUSPEND;
+ else
+ BIF_RET(am_badarg);
+ l = CDR(cons);
}
- if (erts_system_profile_flags.runnable_procs && erts_system_profile_flags.exclusive) {
- profile_runnable_proc(BIF_P, am_active);
- }
- BIF_ERROR(BIF_P, BADARG);
+ if (!is_nil(l))
+ BIF_RET(am_badarg);
}
-
- /* Trace port in, id_or_name2port causes wait */
- if (IS_TRACED_FL(p, F_TRACE_SCHED_PORTS)) {
- trace_sched_ports_where(p, am_in, am_command);
- }
- if (erts_system_profile_flags.runnable_ports && !erts_port_is_scheduled(p)) {
- profile_runnable_port(p, am_active);
+ prt = lookup_port(BIF_P, BIF_ARG_1);
+ if (!prt)
+ BIF_RET(am_badarg);
+
+ if (flags & ERTS_PORT_SIG_FLG_FORCE) {
+ if (!(prt->drv_ptr->flags & ERL_DRV_FLAG_SOFT_BUSY))
+ BIF_RET(am_notsup);
}
- ERTS_BIF_PREP_RET(res, am_true);
+#ifdef DEBUG
+ ref = NIL;
+#endif
- if ((flags & ERTS_PORT_COMMAND_FLAG_FORCE)
- && !(p->drv_ptr->flags & ERL_DRV_FLAG_SOFT_BUSY)) {
- ERTS_BIF_PREP_ERROR(res, BIF_P, EXC_NOTSUP);
- }
- else if (!(flags & ERTS_PORT_COMMAND_FLAG_FORCE)
- && p->status & ERTS_PORT_SFLG_PORT_BUSY) {
- if (flags & ERTS_PORT_COMMAND_FLAG_NOSUSPEND) {
+ switch (erts_port_output(BIF_P, flags, prt, prt->common.id, BIF_ARG_2, &ref)) {
+ case ERTS_PORT_OP_CALLER_EXIT:
+ case ERTS_PORT_OP_BADARG:
+ case ERTS_PORT_OP_DROPPED:
+ ERTS_BIF_PREP_RET(res, am_badarg);
+ break;
+ case ERTS_PORT_OP_BUSY:
+ ASSERT(!(flags & ERTS_PORT_SIG_FLG_FORCE));
+ if (flags & ERTS_PORT_SIG_FLG_NOSUSPEND)
ERTS_BIF_PREP_RET(res, am_false);
- }
else {
- erts_suspend(BIF_P, ERTS_PROC_LOCK_MAIN, p);
- if (erts_system_monitor_flags.busy_port) {
- monitor_generic(BIF_P, am_busy_port, p->id);
- }
- ERTS_BIF_PREP_YIELD3(res, bif_export[BIF_port_command_3], BIF_P,
- arg1, arg2, arg3);
- }
- } else {
- int wres;
- erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
- ERTS_SMP_CHK_NO_PROC_LOCKS;
- wres = erts_write_to_port(BIF_P->id, p, arg2);
- erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
- if (wres != 0) {
- ERTS_BIF_PREP_ERROR(res, BIF_P, BADARG);
+ erts_suspend(BIF_P, ERTS_PROC_LOCK_MAIN, prt);
+ ERTS_BIF_PREP_YIELD3(res, bif_export[BIF_erts_internal_port_command_3],
+ BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3);
}
- }
-
- if (IS_TRACED_FL(p, F_TRACE_SCHED_PORTS)) {
- trace_sched_ports_where(p, am_out, am_command);
- }
- if (erts_system_profile_flags.runnable_ports && !erts_port_is_scheduled(p)) {
- profile_runnable_port(p, am_inactive);
+ break;
+ case ERTS_PORT_OP_BUSY_SCHEDULED:
+ ASSERT(!(flags & ERTS_PORT_SIG_FLG_FORCE));
+ /* Fall through... */
+ case ERTS_PORT_OP_SCHEDULED:
+ ASSERT(is_internal_ref(ref));
+ ERTS_BIF_PREP_RET(res, ref);
+ break;
+ case ERTS_PORT_OP_DONE:
+ ERTS_BIF_PREP_RET(res, am_true);
+ break;
+ default:
+ ERTS_INTERNAL_ERROR("Unexpected erts_port_output() result");
+ break;
}
- erts_port_release(p);
- /* Trace sched in after port release */
- if (IS_TRACED_FL(BIF_P, F_TRACE_SCHED_PROCS)) {
- trace_virtual_sched(BIF_P, am_in);
- }
- if (erts_system_profile_flags.runnable_procs && erts_system_profile_flags.exclusive) {
- profile_runnable_proc(BIF_P, am_active);
- }
-
if (ERTS_PROC_IS_EXITING(BIF_P)) {
KILL_CATCHES(BIF_P); /* Must exit */
ERTS_BIF_PREP_ERROR(res, BIF_P, EXC_ERROR);
}
- return res;
-}
-BIF_RETTYPE port_command_2(BIF_ALIST_2)
-{
- return do_port_command(BIF_P, BIF_ARG_1, BIF_ARG_2, NIL, 0);
+ return res;
}
-BIF_RETTYPE port_command_3(BIF_ALIST_3)
+BIF_RETTYPE erts_internal_port_call_3(BIF_ALIST_3)
{
- Eterm l = BIF_ARG_3;
- Uint32 flags = 0;
- while (is_list(l)) {
- Eterm* cons = list_val(l);
- Eterm car = CAR(cons);
- if (car == am_force) {
- flags |= ERTS_PORT_COMMAND_FLAG_FORCE;
- } else if (car == am_nosuspend) {
- flags |= ERTS_PORT_COMMAND_FLAG_NOSUSPEND;
- } else {
- BIF_ERROR(BIF_P, BADARG);
- }
- l = CDR(cons);
- }
- if(!is_nil(l)) {
- BIF_ERROR(BIF_P, BADARG);
+ Port* prt;
+ Eterm retval;
+ Uint uint_op;
+ unsigned int op;
+ erts_aint32_t state;
+
+ prt = lookup_port(BIF_P, BIF_ARG_1);
+ if (!prt)
+ BIF_RET(am_badarg);
+
+ if (!term_to_Uint(BIF_ARG_2, &uint_op))
+ BIF_RET(am_badarg);
+
+ if (uint_op > (Uint) UINT_MAX)
+ BIF_RET(am_badarg);
+
+ op = (unsigned int) uint_op;
+
+ switch (erts_port_call(BIF_P, prt, op, BIF_ARG_3, &retval)) {
+ case ERTS_PORT_OP_CALLER_EXIT:
+ case ERTS_PORT_OP_DROPPED:
+ case ERTS_PORT_OP_BADARG:
+ retval = am_badarg;
+ break;
+ case ERTS_PORT_OP_SCHEDULED:
+ ASSERT(is_internal_ref(retval));
+ break;
+ case ERTS_PORT_OP_DONE:
+ ASSERT(is_not_internal_ref(retval));
+ break;
+ default:
+ ERTS_INTERNAL_ERROR("Unexpected erts_port_call() result");
+ retval = am_internal_error;
+ break;
}
- return do_port_command(BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3, flags);
-}
-BIF_RETTYPE port_call_2(BIF_ALIST_2)
-{
- return port_call(BIF_P,BIF_ARG_1, make_small(0), BIF_ARG_2);
-}
+ state = erts_smp_atomic32_read_acqb(&BIF_P->state);
+ if (state & (ERTS_PSFLG_EXITING|ERTS_PSFLG_PENDING_EXIT)) {
+#ifdef ERTS_SMP
+ if (state & ERTS_PSFLG_PENDING_EXIT)
+ erts_handle_pending_exit(BIF_P, ERTS_PROC_LOCK_MAIN);
+#endif
+ ERTS_BIF_EXITED(BIF_P);
+ }
-BIF_RETTYPE port_call_3(BIF_ALIST_3)
-{
- return port_call(BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3);
+ BIF_RET(retval);
}
-static BIF_RETTYPE
-port_call(Process* c_p, Eterm arg1, Eterm arg2, Eterm arg3)
+BIF_RETTYPE erts_internal_port_control_3(BIF_ALIST_3)
{
- Uint op;
- Port *p;
- Uint size;
- byte *bytes;
- byte *endp;
- ErlDrvSizeT real_size;
- erts_driver_t *drv;
- byte port_input[256]; /* Default input buffer to encode in */
- byte port_result[256]; /* Buffer for result from port. */
- byte* port_resp; /* Pointer to result buffer. */
- char *prc;
- ErlDrvSSizeT ret;
- Eterm res;
- Sint result_size;
- Eterm *hp;
- Eterm *hp_end;
- unsigned ret_flags = 0U;
- int fpe_was_unmasked;
-
- bytes = &port_input[0];
- port_resp = port_result;
- /* trace of port scheduling with virtual process descheduling
- * lock wait
- */
- if (IS_TRACED_FL(c_p, F_TRACE_SCHED_PROCS)) {
- trace_virtual_sched(c_p, am_out);
- }
-
- if (erts_system_profile_flags.runnable_procs && erts_system_profile_flags.exclusive) {
- profile_runnable_proc(c_p, am_inactive);
+ Port* prt;
+ Eterm retval;
+ Uint uint_op;
+ unsigned int op;
+ erts_aint32_t state;
+
+ prt = lookup_port(BIF_P, BIF_ARG_1);
+ if (!prt)
+ BIF_RET(am_badarg);
+
+ if (!term_to_Uint(BIF_ARG_2, &uint_op))
+ BIF_RET(am_badarg);
+
+ if (uint_op > (Uint) UINT_MAX)
+ BIF_RET(am_badarg);
+
+ op = (unsigned int) uint_op;
+
+ switch (erts_port_control(BIF_P, prt, op, BIF_ARG_3, &retval)) {
+ case ERTS_PORT_OP_CALLER_EXIT:
+ case ERTS_PORT_OP_BADARG:
+ case ERTS_PORT_OP_DROPPED:
+ retval = am_badarg;
+ break;
+ case ERTS_PORT_OP_SCHEDULED:
+ ASSERT(is_internal_ref(retval));
+ break;
+ case ERTS_PORT_OP_DONE:
+ ASSERT(is_not_internal_ref(retval));
+ break;
+ default:
+ ERTS_INTERNAL_ERROR("Unexpected erts_port_control() result");
+ retval = am_internal_error;
+ break;
}
- p = id_or_name2port(c_p, arg1);
- if (!p) {
- error:
- if (port_resp != port_result &&
- !(ret_flags & DRIVER_CALL_KEEP_BUFFER)) {
- driver_free(port_resp);
- }
- if (bytes != &port_input[0])
- erts_free(ERTS_ALC_T_PORT_CALL_BUF, bytes);
- /* Need to virtual schedule in the process if there
- * was an error.
- */
- if (IS_TRACED_FL(c_p, F_TRACE_SCHED_PROCS)) {
- trace_virtual_sched(c_p, am_in);
- }
-
- if (erts_system_profile_flags.runnable_procs && erts_system_profile_flags.exclusive) {
- profile_runnable_proc(c_p, am_active);
- }
-
- if (p)
- erts_port_release(p);
+ state = erts_smp_atomic32_read_acqb(&BIF_P->state);
+ if (state & (ERTS_PSFLG_EXITING|ERTS_PSFLG_PENDING_EXIT)) {
#ifdef ERTS_SMP
- ERTS_SMP_BIF_CHK_PENDING_EXIT(c_p, ERTS_PROC_LOCK_MAIN);
-#else
- ERTS_BIF_CHK_EXITED(c_p);
+ if (state & ERTS_PSFLG_PENDING_EXIT)
+ erts_handle_pending_exit(BIF_P, ERTS_PROC_LOCK_MAIN);
#endif
- BIF_ERROR(c_p, BADARG);
- }
-
- if ((drv = p->drv_ptr) == NULL) {
- goto error;
- }
- if (drv->call == NULL) {
- goto error;
- }
- if (!term_to_Uint(arg2, &op)) {
- goto error;
- }
- p->caller = c_p->id;
-
- /* Lock taken, virtual schedule of port */
- if (IS_TRACED_FL(p, F_TRACE_SCHED_PORTS)) {
- trace_sched_ports_where(p, am_in, am_call);
- }
-
- if (erts_system_profile_flags.runnable_ports && !erts_port_is_scheduled(p)) {
- profile_runnable_port(p, am_active);
+ ERTS_BIF_EXITED(BIF_P);
}
- size = erts_encode_ext_size(arg3);
- if (size > sizeof(port_input))
- bytes = erts_alloc(ERTS_ALC_T_PORT_CALL_BUF, size);
- endp = bytes;
- erts_encode_ext(arg3, &endp);
+ BIF_RET(retval);
+}
- real_size = endp - bytes;
- if (real_size > size) {
- erl_exit(1, "%s, line %d: buffer overflow: %d word(s)\n",
- __FILE__, __LINE__, endp - (bytes + size));
- }
- erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_MAIN);
-#ifdef USE_VM_PROBES
- if (DTRACE_ENABLED(driver_call)) {
- DTRACE_CHARBUF(process_str, DTRACE_TERM_BUF_SIZE);
- DTRACE_CHARBUF(port_str, DTRACE_TERM_BUF_SIZE);
+/*
+ * erts_internal:port_close/1 is used by the
+ * erlang:port_close/1 BIF.
+ */
+BIF_RETTYPE erts_internal_port_close_1(BIF_ALIST_1)
+{
+ Eterm ref;
+ Port *prt;
- dtrace_pid_str(p->connected, process_str);
- dtrace_port_str(p, port_str);
- DTRACE5(driver_call, process_str, port_str, p->name, op, real_size);
- }
-#endif
- prc = (char *) port_resp;
- fpe_was_unmasked = erts_block_fpe();
- ret = drv->call((ErlDrvData)p->drv_data,
- (unsigned) op,
- (char *) bytes,
- (int) real_size,
- &prc,
- (int) sizeof(port_result),
- &ret_flags);
- erts_unblock_fpe(fpe_was_unmasked);
- if (IS_TRACED_FL(p, F_TRACE_SCHED_PORTS)) {
- trace_sched_ports_where(p, am_out, am_call);
- }
-
- if (erts_system_profile_flags.runnable_ports && !erts_port_is_scheduled(p)) {
- profile_runnable_port(p, am_inactive);
- }
-
- port_resp = (byte *) prc;
- p->caller = NIL;
- erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_MAIN);
-#ifdef HARDDEBUG
- {
- ErlDrvSizeT z;
- printf("real_size = %ld,%d, ret = %ld,%d\r\n", (unsigned long) real_size,
- (int) real_size, (unsigned long)ret, (int) ret);
- printf("[");
- for(z = 0; z < real_size; ++z) {
- printf("%d, ",(int) bytes[z]);
- }
- printf("]\r\n");
- printf("[");
- for(z = 0; z < ret; ++z) {
- printf("%d, ",(int) port_resp[z]);
- }
- printf("]\r\n");
- }
-#endif
- if (ret <= 0 || port_resp[0] != VERSION_MAGIC) {
- /* Error or a binary without magic/ with wrong magic */
- goto error;
- }
- result_size = erts_decode_ext_size(port_resp, ret);
- if (result_size < 0) {
- goto error;
- }
- hp = HAlloc(c_p, result_size);
- hp_end = hp + result_size;
- endp = port_resp;
- res = erts_decode_ext(&hp, &MSO(c_p), &endp);
- if (res == THE_NON_VALUE) {
- goto error;
- }
- HRelease(c_p, hp_end, hp);
- if (port_resp != port_result && !(ret_flags & DRIVER_CALL_KEEP_BUFFER)) {
- driver_free(port_resp);
- }
- if (bytes != &port_input[0])
- erts_free(ERTS_ALC_T_PORT_CALL_BUF, bytes);
- if (p)
- erts_port_release(p);
-#ifdef ERTS_SMP
- ERTS_SMP_BIF_CHK_PENDING_EXIT(c_p, ERTS_PROC_LOCK_MAIN);
-#else
- ERTS_BIF_CHK_EXITED(c_p);
+#ifdef DEBUG
+ ref = NIL;
#endif
- if (IS_TRACED_FL(c_p, F_TRACE_SCHED_PROCS)) {
- trace_virtual_sched(c_p, am_in);
- }
- if (erts_system_profile_flags.runnable_procs && erts_system_profile_flags.exclusive) {
- profile_runnable_proc(c_p, am_active);
+ prt = lookup_port(BIF_P, BIF_ARG_1);
+ if (!prt)
+ BIF_RET(am_badarg);
+
+
+ switch (erts_port_exit(BIF_P, 0, prt, prt->common.id, am_normal, &ref)) {
+ case ERTS_PORT_OP_CALLER_EXIT:
+ case ERTS_PORT_OP_BADARG:
+ case ERTS_PORT_OP_DROPPED:
+ BIF_RET(am_badarg);
+ case ERTS_PORT_OP_SCHEDULED:
+ ASSERT(is_internal_ref(ref));
+ BIF_RET(ref);
+ case ERTS_PORT_OP_DONE:
+ BIF_RET(am_true);
+ default:
+ ERTS_INTERNAL_ERROR("Unexpected erts_port_exit() result");
+ BIF_RET(am_internal_error);
}
-
- return res;
}
-
-BIF_RETTYPE port_control_3(BIF_ALIST_3)
+
+/*
+ * erts_internal:port_connect/2 is used by the
+ * erlang:port_connect/2 BIF.
+ */
+BIF_RETTYPE erts_internal_port_connect_2(BIF_ALIST_2)
{
- Port* p;
- Uint op;
- Eterm res = THE_NON_VALUE;
-
- /* Virtual schedule out calling process before lock wait */
- if (IS_TRACED_FL(BIF_P, F_TRACE_SCHED_PROCS)) {
- trace_virtual_sched(BIF_P, am_out);
- }
+ Eterm ref;
+ Port* prt;
- if (erts_system_profile_flags.runnable_procs && erts_system_profile_flags.exclusive) {
- profile_runnable_proc(BIF_P, am_inactive);
- }
+ prt = lookup_port(BIF_P, BIF_ARG_1);
+ if (!prt)
+ BIF_RET(am_badarg);
- p = id_or_name2port(BIF_P, BIF_ARG_1);
- if (!p) {
- /* Schedule the process before exiting */
- if (IS_TRACED_FL(BIF_P, F_TRACE_SCHED_PROCS)) {
- trace_virtual_sched(BIF_P, am_in);
- }
-
- if (erts_system_profile_flags.runnable_procs && erts_system_profile_flags.exclusive) {
- profile_runnable_proc(BIF_P, am_active);
- }
-
- BIF_ERROR(BIF_P, BADARG);
- }
+#ifdef DEBUG
+ ref = NIL;
+#endif
- /* Trace the port for scheduling in */
- if (IS_TRACED_FL(p, F_TRACE_SCHED_PORTS)) {
- trace_sched_ports_where(p, am_in, am_control);
- }
-
- if (erts_system_profile_flags.runnable_ports && !erts_port_is_scheduled(p)) {
- profile_runnable_port(p, am_active);
+ switch (erts_port_connect(BIF_P, 0, prt, prt->common.id, BIF_ARG_2, &ref)) {
+ case ERTS_PORT_OP_CALLER_EXIT:
+ case ERTS_PORT_OP_BADARG:
+ case ERTS_PORT_OP_DROPPED:
+ BIF_RET(am_badarg);
+ case ERTS_PORT_OP_SCHEDULED:
+ ASSERT(is_internal_ref(ref));
+ BIF_RET(ref);
+ break;
+ case ERTS_PORT_OP_DONE:
+ BIF_RET(am_true);
+ break;
+ default:
+ ERTS_INTERNAL_ERROR("Unexpected erts_port_connect() result");
+ BIF_RET(am_internal_error);
}
+}
- if (term_to_Uint(BIF_ARG_2, &op))
- res = erts_port_control(BIF_P, p, op, BIF_ARG_3);
-
- /* Trace the port for scheduling out */
- if (IS_TRACED_FL(p, F_TRACE_SCHED_PORTS)) {
- trace_sched_ports_where(p, am_out, am_control);
- }
+BIF_RETTYPE erts_internal_port_info_1(BIF_ALIST_1)
+{
+ Eterm retval;
+ Port* prt;
- if (erts_system_profile_flags.runnable_ports && !erts_port_is_scheduled(p)) {
- profile_runnable_port(p, am_inactive);
+ if (is_internal_port(BIF_ARG_1) || is_atom(BIF_ARG_1)) {
+ prt = lookup_port(BIF_P, BIF_ARG_1);
+ if (!prt)
+ BIF_RET(am_undefined);
}
-
- erts_port_release(p);
-#ifdef ERTS_SMP
- ERTS_SMP_BIF_CHK_PENDING_EXIT(BIF_P, ERTS_PROC_LOCK_MAIN);
-#else
- ERTS_BIF_CHK_EXITED(BIF_P);
-#endif
-
- if (IS_TRACED_FL(BIF_P, F_TRACE_SCHED_PROCS)) {
- trace_virtual_sched(BIF_P, am_in);
- }
-
- if (erts_system_profile_flags.runnable_procs && erts_system_profile_flags.exclusive) {
- profile_runnable_proc(BIF_P, am_active);
+ else if (is_external_port(BIF_ARG_1)) {
+ if (external_port_dist_entry(BIF_ARG_1) == erts_this_dist_entry)
+ BIF_RET(am_undefined);
+ else
+ BIF_RET(am_badarg);
}
-
- if (is_non_value(res)) {
- BIF_ERROR(BIF_P, BADARG);
+ else {
+ BIF_RET(am_badarg);
+ }
+
+ switch (erts_port_info(BIF_P, prt, THE_NON_VALUE, &retval)) {
+ case ERTS_PORT_OP_CALLER_EXIT:
+ case ERTS_PORT_OP_BADARG:
+ BIF_RET(am_badarg);
+ case ERTS_PORT_OP_DROPPED:
+ BIF_RET(am_undefined);
+ case ERTS_PORT_OP_SCHEDULED:
+ ASSERT(is_internal_ref(retval));
+ BIF_RET(retval);
+ case ERTS_PORT_OP_DONE:
+ ASSERT(is_not_internal_ref(retval));
+ BIF_RET(retval);
+ default:
+ ERTS_INTERNAL_ERROR("Unexpected erts_port_info() result");
+ BIF_RET(am_internal_error);
}
- BIF_RET(res);
}
-BIF_RETTYPE port_close_1(BIF_ALIST_1)
-{
- Port* p;
- erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
- p = id_or_name2port(NULL, BIF_ARG_1);
- if (!p) {
- erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
- BIF_ERROR(BIF_P, BADARG);
- }
- erts_do_exit_port(p, p->connected, am_normal);
- /* if !ERTS_SMP: since we terminate port with reason normal
- we SHOULD never get an exit signal ourselves
- */
- erts_port_release(p);
- erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
- BIF_RET(am_true);
-}
-BIF_RETTYPE port_connect_2(BIF_ALIST_2)
+BIF_RETTYPE erts_internal_port_info_2(BIF_ALIST_2)
{
+ Eterm retval;
Port* prt;
- Process* rp;
- Eterm pid = BIF_ARG_2;
- if (is_not_internal_pid(pid)) {
- error:
- BIF_ERROR(BIF_P, BADARG);
+ if (is_internal_port(BIF_ARG_1) || is_atom(BIF_ARG_1)) {
+ prt = lookup_port(BIF_P, BIF_ARG_1);
+ if (!prt)
+ BIF_RET(am_undefined);
}
- prt = id_or_name2port(BIF_P, BIF_ARG_1);
- if (!prt) {
- goto error;
+ else if (is_external_port(BIF_ARG_1)) {
+ if (external_port_dist_entry(BIF_ARG_1) == erts_this_dist_entry)
+ BIF_RET(am_undefined);
+ else
+ BIF_RET(am_badarg);
}
-
- rp = erts_pid2proc(BIF_P, ERTS_PROC_LOCK_MAIN,
- pid, ERTS_PROC_LOCK_LINK);
- if (!rp) {
- erts_smp_port_unlock(prt);
- ERTS_SMP_ASSERT_IS_NOT_EXITING(BIF_P);
- goto error;
- }
-
- erts_add_link(&(rp->nlinks), LINK_PID, prt->id);
- erts_add_link(&(prt->nlinks), LINK_PID, pid);
-
- erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
-
- prt->connected = pid; /* internal pid */
- erts_smp_port_unlock(prt);
-#ifdef USE_VM_PROBES
- if (DTRACE_ENABLED(port_connect)) {
- DTRACE_CHARBUF(process_str, DTRACE_TERM_BUF_SIZE);
- DTRACE_CHARBUF(port_str, DTRACE_TERM_BUF_SIZE);
- DTRACE_CHARBUF(newprocess_str, DTRACE_TERM_BUF_SIZE);
-
- dtrace_pid_str(prt->connected, process_str);
- erts_snprintf(port_str, sizeof(port_str), "%T", prt->id);
- dtrace_proc_str(rp, newprocess_str);
- DTRACE4(port_connect, process_str, port_str, prt->name, newprocess_str);
+ else {
+ BIF_RET(am_badarg);
+ }
+
+ switch (erts_port_info(BIF_P, prt, BIF_ARG_2, &retval)) {
+ case ERTS_PORT_OP_CALLER_EXIT:
+ case ERTS_PORT_OP_BADARG:
+ BIF_RET(am_badarg);
+ case ERTS_PORT_OP_DROPPED:
+ BIF_RET(am_undefined);
+ case ERTS_PORT_OP_SCHEDULED:
+ ASSERT(is_internal_ref(retval));
+ BIF_RET(retval);
+ case ERTS_PORT_OP_DONE:
+ ASSERT(is_not_internal_ref(retval));
+ BIF_RET(retval);
+ default:
+ ERTS_INTERNAL_ERROR("Unexpected erts_port_info() result");
+ BIF_RET(am_internal_error);
}
-#endif
- BIF_RET(am_true);
}
-BIF_RETTYPE port_set_data_2(BIF_ALIST_2)
+
+BIF_RETTYPE erts_internal_port_set_data_2(BIF_ALIST_2)
{
+ Eterm ref;
Port* prt;
- Eterm portid = BIF_ARG_1;
- Eterm data = BIF_ARG_2;
- prt = id_or_name2port(BIF_P, portid);
- if (!prt) {
- BIF_ERROR(BIF_P, BADARG);
- }
- if (prt->bp != NULL) {
- free_message_buffer(prt->bp);
- prt->bp = NULL;
- }
- if (IS_CONST(data)) {
- prt->data = data;
- } else {
- Uint size;
- ErlHeapFragment* bp;
- Eterm* hp;
-
- size = size_object(data);
- prt->bp = bp = new_message_buffer(size);
- hp = bp->mem;
- prt->data = copy_struct(data, size, &hp, &bp->off_heap);
+ prt = lookup_port(BIF_P, BIF_ARG_1);
+ if (!prt)
+ BIF_RET(am_badarg);
+
+ switch (erts_port_set_data(BIF_P, prt, BIF_ARG_2, &ref)) {
+ case ERTS_PORT_OP_CALLER_EXIT:
+ case ERTS_PORT_OP_BADARG:
+ case ERTS_PORT_OP_DROPPED:
+ BIF_RET(am_badarg);
+ case ERTS_PORT_OP_SCHEDULED:
+ ASSERT(is_internal_ref(ref));
+ BIF_RET(ref);
+ case ERTS_PORT_OP_DONE:
+ BIF_RET(am_true);
+ default:
+ ERTS_INTERNAL_ERROR("Unexpected erts_port_set_data() result");
+ BIF_RET(am_internal_error);
}
- erts_smp_port_unlock(prt);
- BIF_RET(am_true);
}
-BIF_RETTYPE port_get_data_1(BIF_ALIST_1)
+BIF_RETTYPE erts_internal_port_get_data_1(BIF_ALIST_1)
{
- BIF_RETTYPE res;
+ Eterm retval;
Port* prt;
- Eterm portid = BIF_ARG_1;
- prt = id_or_name2port(BIF_P, portid);
- if (!prt) {
- BIF_ERROR(BIF_P, BADARG);
- }
- if (prt->bp == NULL) { /* MUST be CONST! */
- res = prt->data;
- } else {
- Eterm* hp = HAlloc(BIF_P, prt->bp->used_size);
- res = copy_struct(prt->data, prt->bp->used_size, &hp, &MSO(BIF_P));
+ prt = lookup_port(BIF_P, BIF_ARG_1);
+ if (!prt)
+ BIF_RET(am_badarg);
+
+ switch (erts_port_get_data(BIF_P, prt, &retval)) {
+ case ERTS_PORT_OP_CALLER_EXIT:
+ case ERTS_PORT_OP_BADARG:
+ case ERTS_PORT_OP_DROPPED:
+ BIF_RET(am_badarg);
+ case ERTS_PORT_OP_SCHEDULED:
+ ASSERT(is_internal_ref(retval));
+ BIF_RET(retval);
+ case ERTS_PORT_OP_DONE:
+ ASSERT(is_not_internal_ref(retval));
+ BIF_RET(retval);
+ default:
+ ERTS_INTERNAL_ERROR("Unexpected erts_port_get_data() result");
+ BIF_RET(am_internal_error);
}
- erts_smp_port_unlock(prt);
- BIF_RET(res);
}
/*
@@ -625,11 +485,10 @@ BIF_RETTYPE port_get_data_1(BIF_ALIST_1)
* either BADARG or SYSTEM_LIMIT).
*/
-static int
-open_port(Process* p, Eterm name, Eterm settings, int *err_nump)
+static Port *
+open_port(Process* p, Eterm name, Eterm settings, int *err_typep, int *err_nump)
{
-#define OPEN_PORT_ERROR(VAL) do { port_num = (VAL); goto do_return; } while (0)
- int i, port_num;
+ int i;
Eterm option;
Uint arity;
Eterm* tp;
@@ -637,11 +496,11 @@ open_port(Process* p, Eterm name, Eterm settings, int *err_nump)
erts_driver_t* driver;
char* name_buf = NULL;
SysDriverOpts opts;
- int binary_io;
- int soft_eof;
Sint linebuf;
Eterm edir = NIL;
byte dir[MAXPATHLEN];
+ erts_aint32_t sflgs = 0;
+ Port *port;
/* These are the defaults */
opts.packet_bytes = 0;
@@ -655,8 +514,7 @@ open_port(Process* p, Eterm name, Eterm settings, int *err_nump)
opts.overlapped_io = 0;
opts.spawn_type = ERTS_SPAWN_ANY;
opts.argv = NULL;
- binary_io = 0;
- soft_eof = 0;
+ opts.parallelism = erts_port_parallelism;
linebuf = 0;
*err_nump = 0;
@@ -718,7 +576,7 @@ open_port(Process* p, Eterm name, Eterm settings, int *err_nump)
} else if (option == am_arg0) {
char *a0;
- if ((a0 = erts_convert_filename_to_native(*tp, ERTS_ALC_T_TMP, 1)) == NULL) {
+ if ((a0 = erts_convert_filename_to_native(*tp, NULL, 0, ERTS_ALC_T_TMP, 1, 1, NULL)) == NULL) {
goto badarg;
}
if (opts.argv == NULL) {
@@ -734,6 +592,13 @@ open_port(Process* p, Eterm name, Eterm settings, int *err_nump)
}
} else if (option == am_cd) {
edir = *tp;
+ } else if (option == am_parallelism) {
+ if (*tp == am_true)
+ opts.parallelism = 1;
+ else if (*tp == am_false)
+ opts.parallelism = 0;
+ else
+ goto badarg;
} else {
goto badarg;
}
@@ -748,13 +613,13 @@ open_port(Process* p, Eterm name, Eterm settings, int *err_nump)
} else if (*nargs == am_nouse_stdio) {
opts.use_stdio = 0;
} else if (*nargs == am_binary) {
- binary_io = 1;
+ sflgs |= ERTS_PORT_SFLG_BINARY_IO;
} else if (*nargs == am_in) {
opts.read_write |= DO_READ;
} else if (*nargs == am_out) {
opts.read_write |= DO_WRITE;
} else if (*nargs == am_eof) {
- soft_eof = 1;
+ sflgs |= ERTS_PORT_SFLG_SOFT_EOF;
} else if (*nargs == am_hide) {
opts.hide_window = 1;
} else if (*nargs == am_exit_status) {
@@ -845,7 +710,7 @@ open_port(Process* p, Eterm name, Eterm settings, int *err_nump)
goto badarg;
}
name = tp[1];
- if ((name_buf = erts_convert_filename_to_native(name,ERTS_ALC_T_TMP,0)) == NULL) {
+ if ((name_buf = erts_convert_filename_to_native(name, NULL, 0, ERTS_ALC_T_TMP,0,1, NULL)) == NULL) {
goto badarg;
}
opts.spawn_type = ERTS_SPAWN_EXECUTABLE;
@@ -902,14 +767,14 @@ open_port(Process* p, Eterm name, Eterm settings, int *err_nump)
heap[2] = make_small(0);
heap[3] = NIL;
iolist = make_list(heap);
- r = io_list_to_buf(iolist, (char*) dir, MAXPATHLEN);
+ r = erts_iolist_to_buf(iolist, (char*) dir, MAXPATHLEN);
UnUseTmpHeap(4,p);
- if (r < 0) {
+ if (ERTS_IOLIST_TO_BUF_FAILED(r)) {
goto badarg;
}
opts.wd = (char *) dir;
} else {
- if ((opts.wd = erts_convert_filename_to_native(edir,ERTS_ALC_T_TMP,0)) == NULL) {
+ if ((opts.wd = erts_convert_filename_to_native(edir, NULL, 0, ERTS_ALC_T_TMP,0,1,NULL)) == NULL) {
goto badarg;
}
}
@@ -926,44 +791,40 @@ open_port(Process* p, Eterm name, Eterm settings, int *err_nump)
erts_smp_proc_unlock(p, ERTS_PROC_LOCK_MAIN);
- port_num = erts_open_driver(driver, p->id, name_buf, &opts, err_nump);
+ port = erts_open_driver(driver, p->common.id, name_buf, &opts, err_typep, err_nump);
#ifdef USE_VM_PROBES
- if (port_num >= 0 && DTRACE_ENABLED(port_open)) {
+ if (port && DTRACE_ENABLED(port_open)) {
DTRACE_CHARBUF(process_str, DTRACE_TERM_BUF_SIZE);
DTRACE_CHARBUF(port_str, DTRACE_TERM_BUF_SIZE);
dtrace_proc_str(p, process_str);
- erts_snprintf(port_str, sizeof(port_str), "%T", erts_port[port_num].id);
+ erts_snprintf(port_str, sizeof(port_str), "%T", port->common.id);
DTRACE3(port_open, process_str, name_buf, port_str);
}
#endif
erts_smp_proc_lock(p, ERTS_PROC_LOCK_MAIN);
- if (port_num < 0) {
- DEBUGF(("open_driver returned %d(%d)\n", port_num, *err_nump));
+ if (!port) {
+ DEBUGF(("open_driver returned (%d:%d)\n",
+ err_typep ? *err_typep : 4711,
+ err_nump ? *err_nump : 4711));
if (IS_TRACED_FL(p, F_TRACE_SCHED_PROCS)) {
trace_virtual_sched(p, am_in);
}
- OPEN_PORT_ERROR(port_num);
+ goto do_return;
}
if (IS_TRACED_FL(p, F_TRACE_SCHED_PROCS)) {
trace_virtual_sched(p, am_in);
}
- if (binary_io) {
- erts_port_status_bor_set(&erts_port[port_num],
- ERTS_PORT_SFLG_BINARY_IO);
- }
- if (soft_eof) {
- erts_port_status_bor_set(&erts_port[port_num],
- ERTS_PORT_SFLG_SOFT_EOF);
- }
- if (linebuf && erts_port[port_num].linebuf == NULL){
- erts_port[port_num].linebuf = allocate_linebuf(linebuf);
- erts_port_status_bor_set(&erts_port[port_num],
- ERTS_PORT_SFLG_LINEBUF_IO);
+ if (linebuf && port->linebuf == NULL){
+ port->linebuf = allocate_linebuf(linebuf);
+ sflgs |= ERTS_PORT_SFLG_LINEBUF_IO;
}
+
+ if (sflgs)
+ erts_atomic32_read_bor_relb(&port->state, sflgs);
do_return:
if (name_buf)
@@ -974,13 +835,15 @@ open_port(Process* p, Eterm name, Eterm settings, int *err_nump)
if (opts.wd && opts.wd != ((char *)dir)) {
erts_free(ERTS_ALC_T_TMP, (void *) opts.wd);
}
- return port_num;
+ return port;
badarg:
- *err_nump = BADARG;
- OPEN_PORT_ERROR(-3);
+ if (err_typep)
+ *err_typep = -3;
+ if (err_nump)
+ *err_nump = BADARG;
+ port = NULL;
goto do_return;
-#undef OPEN_PORT_ERROR
}
/* Arguments can be given i unicode and as raw binaries, convert filename is used to convert */
@@ -1000,7 +863,7 @@ static char **convert_args(Eterm l)
pp[i++] = erts_default_arg0;
while (is_list(l)) {
str = CAR(list_val(l));
- if ((b = erts_convert_filename_to_native(str,ERTS_ALC_T_TMP,1)) == NULL) {
+ if ((b = erts_convert_filename_to_native(str,NULL,0,ERTS_ALC_T_TMP,1,1,NULL)) == NULL) {
int j;
for (j = 1; j < i; ++j)
erts_free(ERTS_ALC_T_TMP, pp[j]);
@@ -1035,8 +898,9 @@ static byte* convert_environment(Process* p, Eterm env)
Eterm* hp;
Uint heap_size;
int n;
- Uint size;
+ Sint size;
byte* bytes;
+ int encoding = erts_get_native_filename_encoding();
if ((n = list_length(env)) < 0) {
return NULL;
@@ -1079,7 +943,8 @@ static byte* convert_environment(Process* p, Eterm env)
if (is_not_nil(env)) {
goto done;
}
- if (erts_iolist_size(all, &size)) {
+
+ if ((size = erts_native_filename_need(all,encoding)) < 0) {
goto done;
}
@@ -1087,7 +952,7 @@ static byte* convert_environment(Process* p, Eterm env)
* Put the result in a binary (no risk for a memory leak that way).
*/
(void) erts_new_heap_binary(p, NULL, size, &bytes);
- io_list_to_buf(all, (char*)bytes, size);
+ erts_native_filename_put(all,encoding,bytes);
done:
erts_free(ERTS_ALC_T_TMP, temp_heap);
diff --git a/erts/emulator/beam/erl_bif_re.c b/erts/emulator/beam/erl_bif_re.c
index b036c5ef5c..88f980d19f 100644
--- a/erts/emulator/beam/erl_bif_re.c
+++ b/erts/emulator/beam/erl_bif_re.c
@@ -413,7 +413,7 @@ build_compile_result(Process *p, Eterm error_tag, pcre *result, int errcode, con
static BIF_RETTYPE
re_compile(Process* p, Eterm arg1, Eterm arg2)
{
- Uint slen;
+ ErlDrvSizeT slen;
char *expr;
pcre *result;
int errcode = 0;
@@ -444,7 +444,7 @@ re_compile(Process* p, Eterm arg1, Eterm arg2)
BIF_ERROR(p,BADARG);
}
expr = erts_alloc(ERTS_ALC_T_RE_TMP_BUF, slen + 1);
- if (io_list_to_buf(arg1, expr, slen) != 0) {
+ if (erts_iolist_to_buf(arg1, expr, slen) != 0) {
erts_free(ERTS_ALC_T_RE_TMP_BUF, expr);
BIF_ERROR(p,BADARG);
}
@@ -797,7 +797,7 @@ build_capture(Eterm capture_spec[CAPSPEC_SIZE], const pcre *code)
memcpy(tmpb,ap->name,ap->len);
tmpb[ap->len] = '\0';
} else {
- Uint slen;
+ ErlDrvSizeT slen;
if (erts_iolist_size(val, &slen)) {
goto error;
}
@@ -809,7 +809,7 @@ build_capture(Eterm capture_spec[CAPSPEC_SIZE], const pcre *code)
(tmpbsiz = slen + 1));
}
}
- if (io_list_to_buf(val, tmpb, slen) != 0) {
+ if (erts_iolist_to_buf(val, tmpb, slen) != 0) {
goto error;
}
tmpb[slen] = '\0';
@@ -853,7 +853,7 @@ re_run(Process *p, Eterm arg1, Eterm arg2, Eterm arg3)
const pcre *code_tmp;
RestartContext restart;
byte *temp_alloc = NULL;
- Uint slength;
+ ErlDrvSizeT slength;
int startoffset = 0;
int options = 0, comp_options = 0;
int ovsize;
@@ -877,7 +877,7 @@ re_run(Process *p, Eterm arg1, Eterm arg2, Eterm arg3)
if (is_not_tuple(arg2) || (arityval(*tuple_val(arg2)) != 4)) {
if (is_binary(arg2) || is_list(arg2) || is_nil(arg2)) {
/* Compile from textual RE */
- Uint slen;
+ ErlDrvSizeT slen;
char *expr;
pcre *result;
int errcode = 0;
@@ -896,7 +896,7 @@ re_run(Process *p, Eterm arg1, Eterm arg2, Eterm arg3)
}
expr = erts_alloc(ERTS_ALC_T_RE_TMP_BUF, slen + 1);
- if (io_list_to_buf(arg2, expr, slen) != 0) {
+ if (erts_iolist_to_buf(arg2, expr, slen) != 0) {
erts_free(ERTS_ALC_T_RE_TMP_BUF, expr);
BIF_ERROR(p,BADARG);
}
@@ -1039,7 +1039,7 @@ handle_iolist:
}
restart.subject = erts_alloc(ERTS_ALC_T_RE_SUBJECT, slength);
- if (io_list_to_buf(arg1, restart.subject, slength) != 0) {
+ if (erts_iolist_to_buf(arg1, restart.subject, slength) != 0) {
erts_free(ERTS_ALC_T_RE_SUBJECT, restart.ovector);
erts_free(ERTS_ALC_T_RE_SUBJECT, restart.code);
erts_free(ERTS_ALC_T_RE_SUBJECT, restart.subject);
diff --git a/erts/emulator/beam/erl_bif_timer.c b/erts/emulator/beam/erl_bif_timer.c
index 525b11f61c..d67695e533 100644
--- a/erts/emulator/beam/erl_bif_timer.c
+++ b/erts/emulator/beam/erl_bif_timer.c
@@ -265,10 +265,10 @@ link_proc(Process *p, ErtsBifTimer* btm)
{
btm->receiver.proc.ess = p;
btm->receiver.proc.prev = NULL;
- btm->receiver.proc.next = p->bif_timers;
- if (p->bif_timers)
- p->bif_timers->receiver.proc.prev = btm;
- p->bif_timers = btm;
+ btm->receiver.proc.next = p->u.bif_timers;
+ if (p->u.bif_timers)
+ p->u.bif_timers->receiver.proc.prev = btm;
+ p->u.bif_timers = btm;
}
static ERTS_INLINE void
@@ -277,7 +277,7 @@ unlink_proc(ErtsBifTimer* btm)
if (btm->receiver.proc.prev)
btm->receiver.proc.prev->receiver.proc.next = btm->receiver.proc.next;
else
- btm->receiver.proc.ess->bif_timers = btm->receiver.proc.next;
+ btm->receiver.proc.ess->u.bif_timers = btm->receiver.proc.next;
if (btm->receiver.proc.next)
btm->receiver.proc.next->receiver.proc.prev = btm->receiver.proc.prev;
}
@@ -613,7 +613,7 @@ erts_print_bif_timer_info(int to, void *to_arg)
for (btm = bif_timer_tab[i]; btm; btm = btm->tab.next) {
Eterm receiver = (btm->flags & BTM_FLG_BYNAME
? btm->receiver.name
- : btm->receiver.proc.ess->id);
+ : btm->receiver.proc.ess->common.id);
erts_print(to, to_arg, "=timer:%T\n", receiver);
erts_print(to, to_arg, "Message: %T\n", btm->message);
erts_print(to, to_arg, "Time left: %u ms\n",
@@ -637,7 +637,7 @@ erts_cancel_bif_timers(Process *p, ErtsProcLocks plocks)
erts_smp_proc_lock(p, plocks);
}
- btm = p->bif_timers;
+ btm = p->u.bif_timers;
while (btm) {
ErtsBifTimer *tmp_btm;
ASSERT(!(btm->flags & BTM_FLG_CANCELED));
@@ -647,7 +647,7 @@ erts_cancel_bif_timers(Process *p, ErtsProcLocks plocks)
erts_cancel_timer(&tmp_btm->tm);
}
- p->bif_timers = NULL;
+ p->u.bif_timers = NULL;
erts_smp_btm_rwunlock();
}
@@ -696,7 +696,7 @@ erts_bif_timer_foreach(void (*func)(Eterm, Eterm, ErlHeapFragment *, void *),
for (btm = bif_timer_tab[i]; btm; btm = btm->tab.next) {
(*func)((btm->flags & BTM_FLG_BYNAME
? btm->receiver.name
- : btm->receiver.proc.ess->id),
+ : btm->receiver.proc.ess->common.id),
btm->message,
btm->bp,
arg);
diff --git a/erts/emulator/beam/erl_bif_trace.c b/erts/emulator/beam/erl_bif_trace.c
index e88fb8c9f4..99a4394666 100644
--- a/erts/emulator/beam/erl_bif_trace.c
+++ b/erts/emulator/beam/erl_bif_trace.c
@@ -58,10 +58,17 @@ static struct { /* Protected by code write permission */
int local;
BpFunctions f; /* Local functions */
BpFunctions e; /* Export entries */
+#ifdef ERTS_SMP
+ Process* stager;
+ ErtsThrPrgrLaterOp lop;
+#endif
} finish_bp;
static Eterm
trace_pattern(Process* p, Eterm MFA, Eterm Pattern, Eterm flaglist);
+#ifdef ERTS_SMP
+static void smp_bp_finisher(void* arg);
+#endif
static BIF_RETTYPE
system_monitor(Process *p, Eterm monitor_pid, Eterm list);
@@ -117,8 +124,7 @@ trace_pattern(Process* p, Eterm MFA, Eterm Pattern, Eterm flaglist)
struct trace_pattern_flags flags = erts_trace_pattern_flags_off;
int is_global;
Process *meta_tracer_proc = p;
- Eterm meta_tracer_pid = p->id;
- int is_blocking = 0;
+ Eterm meta_tracer_pid = p->common.id;
if (!erts_try_seize_code_write_permission(p)) {
ERTS_BIF_YIELD3(bif_export[BIF_trace_pattern_3], p, MFA, Pattern, flaglist);
@@ -165,14 +171,12 @@ trace_pattern(Process* p, Eterm MFA, Eterm Pattern, Eterm flaglist)
}
} else if (is_internal_port(meta_tracer_pid)) {
Port *meta_tracer_port;
- meta_tracer_proc = NULL;
- if (internal_port_index(meta_tracer_pid) >= erts_max_ports)
- goto error;
- meta_tracer_port =
- &erts_port[internal_port_index(meta_tracer_pid)];
- if (INVALID_TRACER_PORT(meta_tracer_port, meta_tracer_pid)) {
+ meta_tracer_proc = NULL;
+ meta_tracer_port = (erts_port_lookup(
+ meta_tracer_pid,
+ ERTS_PORT_SFLGS_INVALID_TRACER_LOOKUP));
+ if (!meta_tracer_port)
goto error;
- }
} else {
goto error;
}
@@ -248,7 +252,7 @@ trace_pattern(Process* p, Eterm MFA, Eterm Pattern, Eterm flaglist)
MatchSetRef(erts_default_meta_match_spec);
erts_default_meta_tracer_pid = meta_tracer_pid;
if (meta_tracer_proc) {
- meta_tracer_proc->trace_flags |= F_TRACER;
+ ERTS_TRACE_FLAGS(meta_tracer_proc) |= F_TRACER;
}
} else if (! flags.breakpoint) {
MatchSetUnref(erts_default_meta_match_spec);
@@ -336,7 +340,7 @@ trace_pattern(Process* p, Eterm MFA, Eterm Pattern, Eterm flaglist)
}
if (meta_tracer_proc) {
- meta_tracer_proc->trace_flags |= F_TRACER;
+ ERTS_TRACE_FLAGS(meta_tracer_proc) |= F_TRACER;
}
matches = erts_set_trace_pattern(p, mfa, specified,
@@ -351,7 +355,10 @@ trace_pattern(Process* p, Eterm MFA, Eterm Pattern, Eterm flaglist)
#ifdef ERTS_SMP
if (finish_bp.current >= 0) {
ASSERT(matches >= 0);
- erts_notify_finish_breakpointing(p);
+ ASSERT(finish_bp.stager == NULL);
+ finish_bp.stager = p;
+ erts_schedule_thr_prgr_later_op(smp_bp_finisher, NULL, &finish_bp.lop);
+ erts_smp_proc_inc_refc(p);
erts_suspend(p, ERTS_PROC_LOCK_MAIN, NULL);
ERTS_BIF_YIELD_RETURN(p, make_small(matches));
}
@@ -367,6 +374,29 @@ trace_pattern(Process* p, Eterm MFA, Eterm Pattern, Eterm flaglist)
}
}
+#ifdef ERTS_SMP
+static void smp_bp_finisher(void* null)
+{
+ if (erts_finish_breakpointing()) { /* Not done */
+ /* Arrange for being called again */
+ erts_schedule_thr_prgr_later_op(smp_bp_finisher, NULL, &finish_bp.lop);
+ }
+ else { /* Done */
+ Process* p = finish_bp.stager;
+#ifdef DEBUG
+ finish_bp.stager = NULL;
+#endif
+ erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS);
+ if (!ERTS_PROC_IS_EXITING(p)) {
+ erts_resume(p, ERTS_PROC_LOCK_STATUS);
+ }
+ erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
+ erts_smp_proc_dec_refc(p);
+ erts_release_code_write_permission();
+ }
+}
+#endif /* ERTS_SMP */
+
void
erts_get_default_trace_pattern(int *trace_pattern_is_on,
Binary **match_spec,
@@ -374,7 +404,7 @@ erts_get_default_trace_pattern(int *trace_pattern_is_on,
struct trace_pattern_flags *trace_pattern_flags,
Eterm *meta_tracer_pid)
{
- ERTS_SMP_LC_ASSERT(erts_is_code_ix_locked() ||
+ ERTS_SMP_LC_ASSERT(erts_has_code_write_permission() ||
erts_smp_thr_progress_is_blocking());
if (trace_pattern_is_on)
*trace_pattern_is_on = erts_default_trace_pattern_is_on;
@@ -390,7 +420,7 @@ erts_get_default_trace_pattern(int *trace_pattern_is_on,
int erts_is_default_trace_enabled(void)
{
- ERTS_SMP_LC_ASSERT(erts_is_code_ix_locked() ||
+ ERTS_SMP_LC_ASSERT(erts_has_code_write_permission() ||
erts_smp_thr_progress_is_blocking());
return erts_default_trace_pattern_is_on;
}
@@ -494,24 +524,24 @@ Eterm trace_3(BIF_ALIST_3)
if (is_nil(tracer) || is_internal_pid(tracer)) {
Process *tracer_proc = erts_pid2proc(p,
ERTS_PROC_LOCK_MAIN,
- is_nil(tracer) ? p->id : tracer,
+ is_nil(tracer) ? p->common.id : tracer,
ERTS_PROC_LOCKS_ALL);
if (!tracer_proc)
goto error;
- tracer_proc->trace_flags |= F_TRACER;
+ ERTS_TRACE_FLAGS(tracer_proc) |= F_TRACER;
erts_smp_proc_unlock(tracer_proc,
(tracer_proc == p
? ERTS_PROC_LOCKS_ALL_MINOR
: ERTS_PROC_LOCKS_ALL));
} else if (is_internal_port(tracer)) {
- Port *tracer_port = erts_id2port(tracer, p, ERTS_PROC_LOCK_MAIN);
- if (!erts_is_valid_tracer_port(tracer)) {
- if (tracer_port)
- erts_smp_port_unlock(tracer_port);
+ Port *tracer_port = erts_id2port_sflgs(tracer,
+ p,
+ ERTS_PROC_LOCK_MAIN,
+ ERTS_PORT_SFLGS_INVALID_TRACER_LOOKUP);
+ if (!tracer_port)
goto error;
- }
- tracer_port->trace_flags |= F_TRACER;
- erts_smp_port_unlock(tracer_port);
+ ERTS_TRACE_FLAGS(tracer_port) |= F_TRACER;
+ erts_port_release(tracer_port);
} else
goto error;
@@ -522,7 +552,7 @@ Eterm trace_3(BIF_ALIST_3)
case am_true:
on = 1;
if (is_nil(tracer))
- tracer = p->id;
+ tracer = p->common.id;
break;
default:
goto error;
@@ -544,26 +574,29 @@ Eterm trace_3(BIF_ALIST_3)
if (pid_spec == tracer)
goto error;
- tracee_port = erts_id2port(pid_spec, p, ERTS_PROC_LOCK_MAIN);
+ tracee_port = erts_id2port_sflgs(pid_spec,
+ p,
+ ERTS_PROC_LOCK_MAIN,
+ ERTS_PORT_SFLGS_INVALID_LOOKUP);
if (!tracee_port)
goto error;
if (tracer != NIL && port_already_traced(p, tracee_port, tracer)) {
- erts_smp_port_unlock(tracee_port);
+ erts_port_release(tracee_port);
goto already_traced;
}
if (on)
- tracee_port->trace_flags |= mask;
+ ERTS_TRACE_FLAGS(tracee_port) |= mask;
else
- tracee_port->trace_flags &= ~mask;
+ ERTS_TRACE_FLAGS(tracee_port) &= ~mask;
- if (!tracee_port->trace_flags)
- tracee_port->tracer_proc = NIL;
+ if (!ERTS_TRACE_FLAGS(tracee_port))
+ ERTS_TRACER_PROC(tracee_port) = NIL;
else if (tracer != NIL)
- tracee_port->tracer_proc = tracer;
+ ERTS_TRACER_PROC(tracee_port) = tracer;
- erts_smp_port_unlock(tracee_port);
+ erts_port_release(tracee_port);
matches = 1;
} else if (is_pid(pid_spec)) {
@@ -595,14 +628,14 @@ Eterm trace_3(BIF_ALIST_3)
}
if (on)
- tracee_p->trace_flags |= mask;
+ ERTS_TRACE_FLAGS(tracee_p) |= mask;
else
- tracee_p->trace_flags &= ~mask;
+ ERTS_TRACE_FLAGS(tracee_p) &= ~mask;
- if ((tracee_p->trace_flags & TRACEE_FLAGS) == 0)
- tracee_p->tracer_proc = NIL;
+ if ((ERTS_TRACE_FLAGS(tracee_p) & TRACEE_FLAGS) == 0)
+ ERTS_TRACER_PROC(tracee_p) = NIL;
else if (tracer != NIL)
- tracee_p->tracer_proc = tracer;
+ ERTS_TRACER_PROC(tracee_p) = tracer;
erts_smp_proc_unlock(tracee_p,
(tracee_p == p
@@ -676,47 +709,56 @@ Eterm trace_3(BIF_ALIST_3)
ok = 1;
if (procs || mods) {
+ int max = erts_ptab_max(&erts_proc);
/* tracing of processes */
- for (i = 0; i < erts_max_processes; i++) {
+ for (i = 0; i < max; i++) {
Process* tracee_p = erts_pix2proc(i);
if (! tracee_p)
continue;
if (tracer != NIL) {
- if (tracee_p->id == tracer)
+ if (tracee_p->common.id == tracer)
continue;
if (already_traced(NULL, tracee_p, tracer))
continue;
}
if (on) {
- tracee_p->trace_flags |= mask;
+ ERTS_TRACE_FLAGS(tracee_p) |= mask;
} else {
- tracee_p->trace_flags &= ~mask;
+ ERTS_TRACE_FLAGS(tracee_p) &= ~mask;
}
- if(!(tracee_p->trace_flags & TRACEE_FLAGS)) {
- tracee_p->tracer_proc = NIL;
+ if(!(ERTS_TRACE_FLAGS(tracee_p) & TRACEE_FLAGS)) {
+ ERTS_TRACER_PROC(tracee_p) = NIL;
} else if (tracer != NIL) {
- tracee_p->tracer_proc = tracer;
+ ERTS_TRACER_PROC(tracee_p) = tracer;
}
matches++;
}
}
if (ports || mods) {
+ int max = erts_ptab_max(&erts_port);
/* tracing of ports */
- for (i = 0; i < erts_max_ports; i++) {
- Port *tracee_port = &erts_port[i];
- if (tracee_port->status & ERTS_PORT_SFLGS_DEAD) continue;
+ for (i = 0; i < max; i++) {
+ erts_aint32_t state;
+ Port *tracee_port = erts_pix2port(i);
+ if (!tracee_port)
+ continue;
+ state = erts_atomic32_read_nob(&tracee_port->state);
+ if (state & ERTS_PORT_SFLGS_DEAD)
+ continue;
if (tracer != NIL) {
- if (tracee_port->id == tracer) continue;
- if (port_already_traced(NULL, tracee_port, tracer)) continue;
+ if (tracee_port->common.id == tracer)
+ continue;
+ if (port_already_traced(NULL, tracee_port, tracer))
+ continue;
}
- if (on) tracee_port->trace_flags |= mask;
- else tracee_port->trace_flags &= ~mask;
+ if (on) ERTS_TRACE_FLAGS(tracee_port) |= mask;
+ else ERTS_TRACE_FLAGS(tracee_port) &= ~mask;
- if (!(tracee_port->trace_flags & TRACEE_FLAGS)) {
- tracee_port->tracer_proc = NIL;
+ if (!(ERTS_TRACE_FLAGS(tracee_port) & TRACEE_FLAGS)) {
+ ERTS_TRACER_PROC(tracee_port) = NIL;
} else if (tracer != NIL) {
- tracee_port->tracer_proc = tracer;
+ ERTS_TRACER_PROC(tracee_port) = tracer;
}
/* matches are not counted for ports since it would violate compatibility */
/* This could be a reason to modify this function or make a new one. */
@@ -785,20 +827,20 @@ static int port_already_traced(Process *c_p, Port *tracee_port, Eterm tracer)
* * main lock is held on c_p
* * all locks are held on port tracee_p
*/
- if ((tracee_port->trace_flags & TRACEE_FLAGS)
- && tracee_port->tracer_proc != tracer) {
+ if ((ERTS_TRACE_FLAGS(tracee_port) & TRACEE_FLAGS)
+ && ERTS_TRACER_PROC(tracee_port) != tracer) {
/* This tracee is already being traced, and not by the
* tracer to be */
- if (is_internal_port(tracee_port->tracer_proc)) {
- if (!erts_is_valid_tracer_port(tracee_port->tracer_proc)) {
+ if (is_internal_port(ERTS_TRACER_PROC(tracee_port))) {
+ if (!erts_is_valid_tracer_port(ERTS_TRACER_PROC(tracee_port))) {
/* Current trace port now invalid
* - discard it and approve the new. */
goto remove_tracer;
} else
return 1;
}
- else if(is_internal_pid(tracee_port->tracer_proc)) {
- Process *tracer_p = erts_proc_lookup(tracee_port->tracer_proc);
+ else if(is_internal_pid(ERTS_TRACER_PROC(tracee_port))) {
+ Process *tracer_p = erts_proc_lookup(ERTS_TRACER_PROC(tracee_port));
if (!tracer_p) {
/* Current trace process now invalid
* - discard it and approve the new. */
@@ -808,8 +850,8 @@ static int port_already_traced(Process *c_p, Port *tracee_port, Eterm tracer)
}
else {
remove_tracer:
- tracee_port->trace_flags &= ~TRACEE_FLAGS;
- tracee_port->tracer_proc = NIL;
+ ERTS_TRACE_FLAGS(tracee_port) &= ~TRACEE_FLAGS;
+ ERTS_TRACER_PROC(tracee_port) = NIL;
}
}
return 0;
@@ -825,20 +867,22 @@ static int already_traced(Process *c_p, Process *tracee_p, Eterm tracer)
* * main lock is held on c_p
* * all locks multiple are held on tracee_p
*/
- if ((tracee_p->trace_flags & TRACEE_FLAGS)
- && tracee_p->tracer_proc != tracer) {
+ if ((ERTS_TRACE_FLAGS(tracee_p) & TRACEE_FLAGS)
+ && ERTS_TRACER_PROC(tracee_p) != tracer) {
/* This tracee is already being traced, and not by the
* tracer to be */
- if (is_internal_port(tracee_p->tracer_proc)) {
- if (!erts_is_valid_tracer_port(tracee_p->tracer_proc)) {
+ if (is_internal_port(ERTS_TRACER_PROC(tracee_p))) {
+ if (!erts_is_valid_tracer_port(ERTS_TRACER_PROC(tracee_p))) {
/* Current trace port now invalid
* - discard it and approve the new. */
goto remove_tracer;
} else
return 1;
}
- else if(is_internal_pid(tracee_p->tracer_proc)) {
- Process *tracer_p = erts_proc_lookup(tracee_p->tracer_proc);
+ else if(is_internal_pid(ERTS_TRACER_PROC(tracee_p))) {
+ Process *tracer_p;
+
+ tracer_p = erts_proc_lookup(ERTS_TRACER_PROC(tracee_p));
if (!tracer_p) {
/* Current trace process now invalid
* - discard it and approve the new. */
@@ -848,8 +892,8 @@ static int already_traced(Process *c_p, Process *tracee_p, Eterm tracer)
}
else {
remove_tracer:
- tracee_p->trace_flags &= ~TRACEE_FLAGS;
- tracee_p->tracer_proc = NIL;
+ ERTS_TRACE_FLAGS(tracee_p) &= ~TRACEE_FLAGS;
+ ERTS_TRACER_PROC(tracee_p) = NIL;
}
}
return 0;
@@ -893,8 +937,7 @@ trace_info_pid(Process* p, Eterm pid_spec, Eterm key)
if (pid_spec == am_new) {
erts_get_default_tracing(&trace_flags, &tracer);
- } else if (is_internal_pid(pid_spec)
- && internal_pid_index(pid_spec) < erts_max_processes) {
+ } else if (is_internal_pid(pid_spec)) {
Process *tracee;
tracee = erts_pid2proc(p, ERTS_PROC_LOCK_MAIN,
pid_spec, ERTS_PROC_LOCKS_ALL);
@@ -902,16 +945,16 @@ trace_info_pid(Process* p, Eterm pid_spec, Eterm key)
if (!tracee) {
return am_undefined;
} else {
- tracer = tracee->tracer_proc;
- trace_flags = tracee->trace_flags;
+ tracer = ERTS_TRACER_PROC(tracee);
+ trace_flags = ERTS_TRACE_FLAGS(tracee);
}
if (is_internal_pid(tracer)) {
if (!erts_proc_lookup(tracer)) {
reset_tracer:
- tracee->trace_flags &= ~TRACEE_FLAGS;
- trace_flags = tracee->trace_flags;
- tracer = tracee->tracer_proc = NIL;
+ ERTS_TRACE_FLAGS(tracee) &= ~TRACEE_FLAGS;
+ trace_flags = ERTS_TRACE_FLAGS(tracee);
+ tracer = ERTS_TRACER_PROC(tracee) = NIL;
}
}
else if (is_internal_port(tracer)) {
@@ -1370,12 +1413,8 @@ erts_set_trace_pattern(Process*p, Eterm* mfa, int specified,
BeamInstr* pc = fp[i].pc;
Export* ep = (Export *)(((char *)(pc-3)) - offsetof(Export, code));
- if (!on || flags.breakpoint) {
- erts_clear_call_trace_bif(pc, 0);
- if (pc[0] == (BeamInstr) BeamOp(op_i_generic_breakpoint)) {
- pc[0] = (BeamInstr) BeamOp(op_jump_f);
- }
- } else {
+ if (on && !flags.breakpoint) {
+ /* Turn on global call tracing */
if (ep->addressv[code_ix] != pc) {
fp[i].mod->curr.num_traced_exports++;
#ifdef DEBUG
@@ -1388,6 +1427,17 @@ erts_set_trace_pattern(Process*p, Eterm* mfa, int specified,
if (ep->addressv[code_ix] != pc) {
pc[0] = (BeamInstr) BeamOp(op_i_generic_breakpoint);
}
+ } else if (!on && flags.breakpoint) {
+ /* Turn off breakpoint tracing -- nothing to do here. */
+ } else {
+ /*
+ * Turn off global tracing, either explicitly or implicitly
+ * before turning on breakpoint tracing.
+ */
+ erts_clear_call_trace_bif(pc, 0);
+ if (pc[0] == (BeamInstr) BeamOp(op_i_generic_breakpoint)) {
+ pc[0] = (BeamInstr) BeamOp(op_jump_f);
+ }
}
}
@@ -1524,7 +1574,7 @@ erts_set_trace_pattern(Process*p, Eterm* mfa, int specified,
int
erts_finish_breakpointing(void)
{
- ERTS_SMP_LC_ASSERT(erts_is_code_ix_locked());
+ ERTS_SMP_LC_ASSERT(erts_has_code_write_permission());
/*
* Memory barriers will be issued for all processes *before*
@@ -1532,7 +1582,6 @@ erts_finish_breakpointing(void)
* are blocked, in which case memory barriers will be issued
* when they are awaken.)
*/
-
switch (finish_bp.current++) {
case 0:
/*
@@ -1839,7 +1888,7 @@ new_seq_trace_token(Process* p)
SEQ_TRACE_TOKEN(p) = TUPLE5(hp, make_small(0), /* Flags */
make_small(0), /* Label */
make_small(0), /* Serial */
- p->id, /* Internal pid */ /* From */
+ p->common.id, /* Internal pid */ /* From */
make_small(p->seq_trace_lastcnt));
}
}
@@ -2209,9 +2258,11 @@ BIF_RETTYPE system_profile_2(BIF_ALIST_2)
if (!profiler_p)
goto error;
} else if (is_internal_port(profiler)) {
- if (internal_port_index(profiler) >= erts_max_ports) goto error;
- profiler_port = &erts_port[internal_port_index(profiler)];
- if (INVALID_TRACER_PORT(profiler_port, profiler)) goto error;
+ profiler_port = (erts_port_lookup(
+ profiler,
+ ERTS_PORT_SFLGS_INVALID_TRACER_LOOKUP));
+ if (!profiler_port)
+ goto error;
} else {
goto error;
}
@@ -2275,8 +2326,7 @@ trace_delivered_1(BIF_ALIST_1)
p = NULL;
} else if (! (p = erts_pid2proc(BIF_P, ERTS_PROC_LOCK_MAIN,
BIF_ARG_1, ERTS_PROC_LOCKS_ALL))) {
- if (is_not_internal_pid(BIF_ARG_1)
- || internal_pid_index(BIF_ARG_1) >= erts_max_processes) {
+ if (is_not_internal_pid(BIF_ARG_1)) {
BIF_ERROR(BIF_P, BADARG);
}
}
@@ -2295,7 +2345,7 @@ trace_delivered_1(BIF_ALIST_1)
msg = TUPLE3(hp, AM_trace_delivered, BIF_ARG_1, msg_ref);
#ifdef ERTS_SMP
- erts_send_sys_msg_proc(BIF_P->id, BIF_P->id, msg, bp);
+ erts_send_sys_msg_proc(BIF_P->common.id, BIF_P->common.id, msg, bp);
if (p)
erts_smp_proc_unlock(p,
(BIF_P == p
diff --git a/erts/emulator/beam/erl_bits.c b/erts/emulator/beam/erl_bits.c
index 6f7309f493..3753b618e1 100644
--- a/erts/emulator/beam/erl_bits.c
+++ b/erts/emulator/beam/erl_bits.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1999-2011. All Rights Reserved.
+ * Copyright Ericsson AB 1999-2012. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -1005,8 +1005,13 @@ erts_new_bs_put_float(Process *c_p, Eterm arg, Uint num_bits, int flags)
if (is_float(arg)) {
FloatDef *fdp = (FloatDef*)(float_val(arg) + 1);
+#ifdef DOUBLE_MIDDLE_ENDIAN
+ a = fdp->fw[1];
+ b = fdp->fw[0];
+#else
a = fdp->fw[0];
b = fdp->fw[1];
+#endif
} else if (is_small(arg)) {
u.f64 = (double) signed_val(arg);
a = u.i32[0];
@@ -1015,8 +1020,13 @@ erts_new_bs_put_float(Process *c_p, Eterm arg, Uint num_bits, int flags)
if (big_to_double(arg, &u.f64) < 0) {
return 0;
}
+#ifdef DOUBLE_MIDDLE_ENDIAN
+ a = u.i32[1];
+ b = u.i32[0];
+#else
a = u.i32[0];
b = u.i32[1];
+#endif
} else {
return 0;
}
@@ -1237,6 +1247,12 @@ erts_bs_append(Process* c_p, Eterm* reg, Uint live, Eterm build_size_term,
*/
erts_bin_offset = 8*sb->size + sb->bitsize;
+ if (unit > 1) {
+ if ((unit == 8 && (erts_bin_offset & 7) != 0) ||
+ (erts_bin_offset % unit) != 0) {
+ goto badarg;
+ }
+ }
used_size_in_bits = erts_bin_offset + build_size_in_bits;
sb->is_writable = 0; /* Make sure that no one else can write. */
pb->size = NBYTES(used_size_in_bits);
@@ -1306,6 +1322,12 @@ erts_bs_append(Process* c_p, Eterm* reg, Uint live, Eterm build_size_term,
*/
ERTS_GET_BINARY_BYTES(bin, src_bytes, bitoffs, bitsize);
erts_bin_offset = 8*binary_size(bin) + bitsize;
+ if (unit > 1) {
+ if ((unit == 8 && (erts_bin_offset & 7) != 0) ||
+ (erts_bin_offset % unit) != 0) {
+ goto badarg;
+ }
+ }
used_size_in_bits = erts_bin_offset + build_size_in_bits;
used_size_in_bytes = NBYTES(used_size_in_bits);
bin_size = 2*used_size_in_bytes;
@@ -1353,12 +1375,6 @@ erts_bs_append(Process* c_p, Eterm* reg, Uint live, Eterm build_size_term,
/*
* Now copy the data into the binary.
*/
- if (unit > 1) {
- if ((unit == 8 && (erts_bin_offset & 7) != 0) ||
- (erts_bin_offset % unit) != 0) {
- return THE_NON_VALUE;
- }
- }
copy_binary_to_buffer(erts_current_bin, 0, src_bytes, bitoffs, erts_bin_offset);
return make_binary(sb);
diff --git a/erts/emulator/beam/erl_cpu_topology.c b/erts/emulator/beam/erl_cpu_topology.c
index 41e71881a0..88456a85f3 100644
--- a/erts/emulator/beam/erl_cpu_topology.c
+++ b/erts/emulator/beam/erl_cpu_topology.c
@@ -34,7 +34,7 @@
#include "bif.h"
#include "erl_cpu_topology.h"
-#define ERTS_MAX_READER_GROUPS 8
+#define ERTS_MAX_READER_GROUPS 64
/*
* Cpu topology hierarchy.
@@ -486,7 +486,7 @@ erts_sched_check_cpu_bind_post_suspend(ErtsSchedulerData *esdp)
erts_thr_set_main_status(1, (int) esdp->no);
/* Make sure we check if we should bind to a cpu or not... */
- ERTS_RUNQ_FLGS_SET(esdp->run_queue, ERTS_RUNQ_FLG_CHK_CPU_BIND);
+ (void) ERTS_RUNQ_FLGS_SET(esdp->run_queue, ERTS_RUNQ_FLG_CHK_CPU_BIND);
}
#endif
@@ -620,30 +620,38 @@ write_schedulers_bind_change(erts_cpu_topology_t *cpudata, int size)
int
erts_init_scheduler_bind_type_string(char *how)
{
+ ErtsCpuBindOrder order;
+
if (sys_strcmp(how, "u") == 0)
- cpu_bind_order = ERTS_CPU_BIND_NONE;
- else if (erts_bind_to_cpu(cpuinfo, -1) == -ENOTSUP)
- return ERTS_INIT_SCHED_BIND_TYPE_NOT_SUPPORTED;
- else if (!system_cpudata && !user_cpudata)
- return ERTS_INIT_SCHED_BIND_TYPE_ERROR_NO_CPU_TOPOLOGY;
+ order = ERTS_CPU_BIND_NONE;
else if (sys_strcmp(how, "db") == 0)
- cpu_bind_order = ERTS_CPU_BIND_DEFAULT_BIND;
+ order = ERTS_CPU_BIND_DEFAULT_BIND;
else if (sys_strcmp(how, "s") == 0)
- cpu_bind_order = ERTS_CPU_BIND_SPREAD;
+ order = ERTS_CPU_BIND_SPREAD;
else if (sys_strcmp(how, "ps") == 0)
- cpu_bind_order = ERTS_CPU_BIND_PROCESSOR_SPREAD;
+ order = ERTS_CPU_BIND_PROCESSOR_SPREAD;
else if (sys_strcmp(how, "ts") == 0)
- cpu_bind_order = ERTS_CPU_BIND_THREAD_SPREAD;
+ order = ERTS_CPU_BIND_THREAD_SPREAD;
else if (sys_strcmp(how, "tnnps") == 0)
- cpu_bind_order = ERTS_CPU_BIND_THREAD_NO_NODE_PROCESSOR_SPREAD;
+ order = ERTS_CPU_BIND_THREAD_NO_NODE_PROCESSOR_SPREAD;
else if (sys_strcmp(how, "nnps") == 0)
- cpu_bind_order = ERTS_CPU_BIND_NO_NODE_PROCESSOR_SPREAD;
+ order = ERTS_CPU_BIND_NO_NODE_PROCESSOR_SPREAD;
else if (sys_strcmp(how, "nnts") == 0)
- cpu_bind_order = ERTS_CPU_BIND_NO_NODE_THREAD_SPREAD;
+ order = ERTS_CPU_BIND_NO_NODE_THREAD_SPREAD;
else if (sys_strcmp(how, "ns") == 0)
- cpu_bind_order = ERTS_CPU_BIND_NO_SPREAD;
+ order = ERTS_CPU_BIND_NO_SPREAD;
else
- return ERTS_INIT_SCHED_BIND_TYPE_ERROR_NO_BAD_TYPE;
+ return ERTS_INIT_SCHED_BIND_TYPE_ERROR_BAD_TYPE;
+
+ if (order != ERTS_CPU_BIND_NONE) {
+ if (erts_bind_to_cpu(cpuinfo, -1) == -ENOTSUP)
+ return ERTS_INIT_SCHED_BIND_TYPE_NOT_SUPPORTED;
+ else if (!system_cpudata && !user_cpudata)
+ return ERTS_INIT_SCHED_BIND_TYPE_ERROR_NO_CPU_TOPOLOGY;
+ }
+
+ cpu_bind_order = order;
+
return ERTS_INIT_SCHED_BIND_TYPE_SUCCESS;
}
diff --git a/erts/emulator/beam/erl_cpu_topology.h b/erts/emulator/beam/erl_cpu_topology.h
index c5a9520b61..11915e1ea8 100644
--- a/erts/emulator/beam/erl_cpu_topology.h
+++ b/erts/emulator/beam/erl_cpu_topology.h
@@ -40,7 +40,7 @@ void erts_init_cpu_topology(void);
#define ERTS_INIT_SCHED_BIND_TYPE_SUCCESS 0
#define ERTS_INIT_SCHED_BIND_TYPE_NOT_SUPPORTED 1
#define ERTS_INIT_SCHED_BIND_TYPE_ERROR_NO_CPU_TOPOLOGY 2
-#define ERTS_INIT_SCHED_BIND_TYPE_ERROR_NO_BAD_TYPE 3
+#define ERTS_INIT_SCHED_BIND_TYPE_ERROR_BAD_TYPE 3
int erts_init_scheduler_bind_type_string(char *how);
diff --git a/erts/emulator/beam/erl_db.c b/erts/emulator/beam/erl_db.c
index 5bd8163968..7932966539 100644
--- a/erts/emulator/beam/erl_db.c
+++ b/erts/emulator/beam/erl_db.c
@@ -224,8 +224,9 @@ Export ets_select_continue_exp;
static Export ets_delete_continue_exp;
static void
-free_dbtable(DbTable* tb)
+free_dbtable(void *vtb)
{
+ DbTable *tb = (DbTable *) vtb;
#ifdef HARDDEBUG
if (erts_smp_atomic_read_nob(&tb->common.memory_size) != sizeof(DbTable)) {
erts_fprintf(stderr, "ets: free_dbtable memory remain=%ld fix=%x\n",
@@ -250,21 +251,8 @@ free_dbtable(DbTable* tb)
#endif
ASSERT(is_immed(tb->common.heir_data));
erts_db_free(ERTS_ALC_T_DB_TABLE, tb, (void *) tb, sizeof(DbTable));
- ERTS_ETS_MISC_MEM_ADD(-sizeof(DbTable));
- ERTS_SMP_MEMORY_BARRIER;
}
-#ifdef ERTS_SMP
-static void
-chk_free_dbtable(void *vtb)
-{
- DbTable * tb = (DbTable *) vtb;
- ERTS_THR_MEMORY_BARRIER;
- if (erts_refc_dectest(&tb->common.ref, 0) == 0)
- free_dbtable(tb);
-}
-#endif
-
static void schedule_free_dbtable(DbTable* tb)
{
/*
@@ -275,15 +263,10 @@ static void schedule_free_dbtable(DbTable* tb)
* need to unlock the table lock after this
* function has returned).
*/
-#ifdef ERTS_SMP
- int scheds = erts_get_max_no_executing_schedulers();
- ASSERT(scheds >= 1);
ASSERT(erts_refc_read(&tb->common.ref, 0) == 0);
- erts_refc_init(&tb->common.ref, scheds);
- erts_schedule_multi_misc_aux_work(0, scheds, chk_free_dbtable, tb);
-#else
- free_dbtable(tb);
-#endif
+ erts_schedule_thr_prgr_later_op(free_dbtable,
+ (void *) tb,
+ &tb->release.data);
}
static ERTS_INLINE void db_init_lock(DbTable* tb, int use_frequent_read_lock,
@@ -442,7 +425,8 @@ DbTable* db_get_table_aux(Process *p,
if (tb) {
db_lock(tb, kind);
if (tb->common.id != id
- || ((tb->common.status & what) == 0 && p->id != tb->common.owner)) {
+ || ((tb->common.status & what) == 0
+ && p->common.id != tb->common.owner)) {
db_unlock(tb, kind);
tb = NULL;
}
@@ -542,10 +526,6 @@ static int remove_named_tab(DbTable *tb, int have_lock)
&rwlock);
#ifdef ERTS_SMP
if (!have_lock && erts_smp_rwmtx_tryrwlock(rwlock) == EBUSY) {
- /*
- * We keep our increased refc over this op in order to
- * prevent the table from disapearing.
- */
db_unlock(tb, LCK_WRITE);
erts_smp_rwmtx_rwlock(rwlock);
db_lock(tb, LCK_WRITE);
@@ -636,7 +616,7 @@ BIF_RETTYPE ets_safe_fixtable_2(BIF_ALIST_2)
#ifdef HARDDEBUG
erts_fprintf(stderr,
"ets:safe_fixtable(%T,%T); Process: %T, initial: %T:%T/%bpu\n",
- BIF_ARG_1, BIF_ARG_2, BIF_P->id,
+ BIF_ARG_1, BIF_ARG_2, BIF_P->common.id,
BIF_P->initial[0], BIF_P->initial[1], BIF_P->initial[2]);
#endif
kind = (BIF_ARG_2 == am_true) ? LCK_READ : LCK_WRITE_REC;
@@ -1215,7 +1195,7 @@ BIF_RETTYPE ets_rename_2(BIF_ALIST_2)
#ifdef HARDDEBUG
erts_fprintf(stderr,
"ets:rename(%T,%T); Process: %T, initial: %T:%T/%bpu\n",
- BIF_ARG_1, BIF_ARG_2, BIF_P->id,
+ BIF_ARG_1, BIF_ARG_2, BIF_P->common.id,
BIF_P->initial[0], BIF_P->initial[1], BIF_P->initial[2]);
#endif
@@ -1443,7 +1423,6 @@ BIF_RETTYPE ets_new_2(BIF_ALIST_2)
erts_smp_atomic_init_nob(&init_tb.common.memory_size, 0);
tb = (DbTable*) erts_db_alloc(ERTS_ALC_T_DB_TABLE,
&init_tb, sizeof(DbTable));
- ERTS_ETS_MISC_MEM_ADD(sizeof(DbTable));
erts_smp_atomic_init_nob(&tb->common.memory_size,
erts_smp_atomic_read_nob(&init_tb.common.memory_size));
}
@@ -1459,7 +1438,7 @@ BIF_RETTYPE ets_new_2(BIF_ALIST_2)
db_init_lock(tb, status & (DB_FINE_LOCKED|DB_FREQ_READ),
"db_tab", "db_tab_fix");
tb->common.keypos = keypos;
- tb->common.owner = BIF_P->id;
+ tb->common.owner = BIF_P->common.id;
set_heir(BIF_P, tb, heir, heir_data);
erts_smp_atomic_init_nob(&tb->common.nitems, 0);
@@ -1481,7 +1460,7 @@ BIF_RETTYPE ets_new_2(BIF_ALIST_2)
"** Too many db tables **\n");
free_heir_data(tb);
tb->common.meth->db_free_table(tb);
- free_dbtable(tb);
+ free_dbtable((void *) tb);
BIF_ERROR(BIF_P, SYSTEM_LIMIT);
}
@@ -1528,7 +1507,7 @@ BIF_RETTYPE ets_new_2(BIF_ALIST_2)
#ifdef HARDDEBUG
erts_fprintf(stderr,
"ets:new(%T,%T)=%T; Process: %T, initial: %T:%T/%bpu\n",
- BIF_ARG_1, BIF_ARG_2, ret, BIF_P->id,
+ BIF_ARG_1, BIF_ARG_2, ret, BIF_P->common.id,
BIF_P->initial[0], BIF_P->initial[1], BIF_P->initial[2]);
erts_fprintf(stderr, "ets: new: meta_pid_to_tab common.memory_size = %ld\n",
erts_smp_atomic_read_nob(&meta_pid_to_tab->common.memory_size));
@@ -1540,7 +1519,9 @@ BIF_RETTYPE ets_new_2(BIF_ALIST_2)
db_meta_lock(meta_pid_to_tab, LCK_WRITE_REC);
if (db_put_hash(meta_pid_to_tab,
- TUPLE2(meta_tuple, BIF_P->id, make_small(slot)),
+ TUPLE2(meta_tuple,
+ BIF_P->common.id,
+ make_small(slot)),
0) != DB_ERROR_NONE) {
erl_exit(1,"Could not update ets metadata.");
}
@@ -1659,7 +1640,7 @@ BIF_RETTYPE ets_delete_1(BIF_ALIST_1)
#ifdef HARDDEBUG
erts_fprintf(stderr,
"ets:delete(%T); Process: %T, initial: %T:%T/%bpu\n",
- BIF_ARG_1, BIF_P->id,
+ BIF_ARG_1, BIF_P->common.id,
BIF_P->initial[0], BIF_P->initial[1], BIF_P->initial[2]);
#endif
@@ -1676,7 +1657,7 @@ BIF_RETTYPE ets_delete_1(BIF_ALIST_1)
tb->common.status &= ~(DB_PROTECTED|DB_PUBLIC|DB_PRIVATE);
tb->common.status |= DB_DELETE;
- if (tb->common.owner != BIF_P->id) {
+ if (tb->common.owner != BIF_P->common.id) {
DeclareTmpHeap(meta_tuple,3,BIF_P);
/*
@@ -1691,10 +1672,12 @@ BIF_RETTYPE ets_delete_1(BIF_ALIST_1)
make_small(tb->common.slot));
BIF_P->flags |= F_USING_DB;
- tb->common.owner = BIF_P->id;
+ tb->common.owner = BIF_P->common.id;
db_put_hash(meta_pid_to_tab,
- TUPLE2(meta_tuple,BIF_P->id,make_small(tb->common.slot)),
+ TUPLE2(meta_tuple,
+ BIF_P->common.id,
+ make_small(tb->common.slot)),
0);
db_meta_unlock(meta_pid_to_tab, LCK_WRITE_REC);
UnUseTmpHeap(3,BIF_P);
@@ -1770,7 +1753,7 @@ BIF_RETTYPE ets_give_away_3(BIF_ALIST_3)
}
if ((tb = db_get_table(BIF_P, BIF_ARG_1, DB_WRITE, LCK_WRITE)) == NULL
- || tb->common.owner != BIF_P->id) {
+ || tb->common.owner != BIF_P->common.id) {
goto badarg;
}
from_pid = tb->common.owner;
@@ -1793,7 +1776,10 @@ BIF_RETTYPE ets_give_away_3(BIF_ALIST_3)
db_unlock(tb,LCK_WRITE);
erts_send_message(BIF_P, to_proc, &to_locks,
- TUPLE4(buf, am_ETS_TRANSFER, tb->common.id, from_pid, BIF_ARG_3),
+ TUPLE4(buf, am_ETS_TRANSFER,
+ tb->common.id,
+ from_pid,
+ BIF_ARG_3),
0);
erts_smp_proc_unlock(to_proc, to_locks);
UnUseTmpHeap(5,BIF_P);
@@ -1855,7 +1841,7 @@ BIF_RETTYPE ets_setopts_2(BIF_ALIST_2)
if (tail != NIL
|| (tb = db_get_table(BIF_P, BIF_ARG_1, DB_WRITE, LCK_WRITE)) == NULL
- || tb->common.owner != BIF_P->id) {
+ || tb->common.owner != BIF_P->common.id) {
goto badarg;
}
@@ -2669,7 +2655,7 @@ BIF_RETTYPE ets_info_1(BIF_ALIST_1)
*/
/* If/when we implement lockless private tables:
- if ((tb->common.status & DB_PRIVATE) && owner != BIF_P->id) {
+ if ((tb->common.status & DB_PRIVATE) && owner != BIF_P->common.id) {
db_unlock(tb, LCK_READ);
rp = erts_pid2proc_not_running(BIF_P, ERTS_PROC_LOCK_MAIN,
owner, ERTS_PROC_LOCK_MAIN);
@@ -2887,7 +2873,6 @@ void init_db(void)
meta_pid_to_tab = (DbTable*) erts_db_alloc(ERTS_ALC_T_DB_TABLE,
&init_tb,
sizeof(DbTable));
- ERTS_ETS_MISC_MEM_ADD(sizeof(DbTable));
erts_smp_atomic_init_nob(&meta_pid_to_tab->common.memory_size,
erts_smp_atomic_read_nob(&init_tb.common.memory_size));
@@ -2919,7 +2904,6 @@ void init_db(void)
meta_pid_to_fixed_tab = (DbTable*) erts_db_alloc(ERTS_ALC_T_DB_TABLE,
&init_tb,
sizeof(DbTable));
- ERTS_ETS_MISC_MEM_ADD(sizeof(DbTable));
erts_smp_atomic_init_nob(&meta_pid_to_fixed_tab->common.memory_size,
erts_smp_atomic_read_nob(&init_tb.common.memory_size));
@@ -3063,9 +3047,9 @@ static int give_away_to_heir(Process* p, DbTable* tb)
Eterm to_pid;
UWord heir_data;
- ASSERT(tb->common.owner == p->id);
+ ASSERT(tb->common.owner == p->common.id);
ASSERT(is_internal_pid(tb->common.heir));
- ASSERT(tb->common.heir != p->id);
+ ASSERT(tb->common.heir != p->common.id);
retry:
to_pid = tb->common.heir;
to_proc = erts_pid2proc_opt(p, ERTS_PROC_LOCK_MAIN,
@@ -3078,7 +3062,7 @@ retry:
db_lock(tb,LCK_WRITE);
ASSERT(tb != NULL);
- if (tb->common.owner != p->id) {
+ if (tb->common.owner != p->common.id) {
if (to_proc != NULL ) {
erts_smp_proc_unlock(to_proc, to_locks);
}
@@ -3089,7 +3073,7 @@ retry:
if (to_proc != NULL ) {
erts_smp_proc_unlock(to_proc, to_locks);
}
- if (to_pid == p->id || to_pid == am_none) {
+ if (to_pid == p->common.id || to_pid == am_none) {
return 0; /* no real heir, table still mine */
}
goto retry;
@@ -3098,7 +3082,8 @@ retry:
if (to_proc == NULL) {
return 0; /* heir not alive, table still mine */
}
- if (to_proc->started_interval != tb->common.heir_started_interval) {
+ if (to_proc->common.u.alive.started_interval
+ != tb->common.heir_started_interval) {
erts_smp_proc_unlock(to_proc, to_locks);
return 0; /* heir dead and pid reused, table still mine */
}
@@ -3123,7 +3108,11 @@ retry:
heir_data = tpv[1];
}
erts_send_message(p, to_proc, &to_locks,
- TUPLE4(buf, am_ETS_TRANSFER, tb->common.id, p->id, heir_data),
+ TUPLE4(buf,
+ am_ETS_TRANSFER,
+ tb->common.id,
+ p->common.id,
+ heir_data),
0);
erts_smp_proc_unlock(to_proc, to_locks);
return !0;
@@ -3132,7 +3121,7 @@ retry:
/*
* erts_db_process_exiting() is called when a process terminates.
* It returns 0 when completely done, and !0 when it wants to
- * yield. c_p->u.exit_data can hold a pointer to a state while
+ * yield. c_p->u.terminate can hold a pointer to a state while
* yielding.
*/
#define ERTS_DB_INTERNAL_ERROR(LSTR) \
@@ -3142,8 +3131,8 @@ retry:
int
erts_db_process_exiting(Process *c_p, ErtsProcLocks c_p_locks)
{
- ErtsDbProcCleanupState *state = (ErtsDbProcCleanupState *) c_p->u.exit_data;
- Eterm pid = c_p->id;
+ ErtsDbProcCleanupState *state = (ErtsDbProcCleanupState *) c_p->u.terminate;
+ Eterm pid = c_p->common.id;
ErtsDbProcCleanupState default_state;
int ret;
@@ -3324,7 +3313,7 @@ erts_db_process_exiting(Process *c_p, ErtsProcLocks c_p_locks)
if (state != &default_state)
erts_free(ERTS_ALC_T_DB_PROC_CLEANUP, state);
- c_p->u.exit_data = NULL;
+ c_p->u.terminate = NULL;
return 0;
default:
@@ -3345,13 +3334,13 @@ erts_db_process_exiting(Process *c_p, ErtsProcLocks c_p_locks)
break;
}
- ASSERT(c_p->u.exit_data == (void *) state
+ ASSERT(c_p->u.terminate == (void *) state
|| state == &default_state);
if (state == &default_state) {
- c_p->u.exit_data = erts_alloc(ERTS_ALC_T_DB_PROC_CLEANUP,
+ c_p->u.terminate = erts_alloc(ERTS_ALC_T_DB_PROC_CLEANUP,
sizeof(ErtsDbProcCleanupState));
- sys_memcpy(c_p->u.exit_data,
+ sys_memcpy(c_p->u.terminate,
(void*) state,
sizeof(ErtsDbProcCleanupState));
}
@@ -3377,7 +3366,7 @@ static void fix_table_locked(Process* p, DbTable* tb)
}
else {
for (; fix != NULL; fix = fix->next) {
- if (fix->pid == p->id) {
+ if (fix->pid == p->common.id) {
++(fix->counter);
#ifdef ERTS_SMP
erts_smp_mtx_unlock(&tb->common.fixlock);
@@ -3389,7 +3378,7 @@ static void fix_table_locked(Process* p, DbTable* tb)
fix = (DbFixation *) erts_db_alloc(ERTS_ALC_T_DB_FIXATION,
tb, sizeof(DbFixation));
ERTS_ETS_MISC_MEM_ADD(sizeof(DbFixation));
- fix->pid = p->id;
+ fix->pid = p->common.id;
fix->counter = 1;
fix->next = tb->common.fixations;
tb->common.fixations = fix;
@@ -3400,7 +3389,9 @@ static void fix_table_locked(Process* p, DbTable* tb)
UseTmpHeap(3,p);
db_meta_lock(meta_pid_to_fixed_tab, LCK_WRITE_REC);
if (db_put_hash(meta_pid_to_fixed_tab,
- TUPLE2(meta_tuple, p->id, make_small(tb->common.slot)),
+ TUPLE2(meta_tuple,
+ p->common.id,
+ make_small(tb->common.slot)),
0) != DB_ERROR_NONE) {
UnUseTmpHeap(3,p);
erl_exit(1,"Could not insert ets metadata in safe_fixtable.");
@@ -3420,7 +3411,7 @@ static void unfix_table_locked(Process* p, DbTable* tb,
erts_smp_mtx_lock(&tb->common.fixlock);
#endif
for (pp = &tb->common.fixations; *pp != NULL; pp = &(*pp)->next) {
- if ((*pp)->pid == p->id) {
+ if ((*pp)->pid == p->common.id) {
DbFixation* fix = *pp;
erts_refc_dec(&tb->common.ref,0);
--(fix->counter);
@@ -3434,7 +3425,7 @@ static void unfix_table_locked(Process* p, DbTable* tb,
#endif
db_meta_lock(meta_pid_to_fixed_tab, LCK_WRITE_REC);
db_erase_bag_exact2(meta_pid_to_fixed_tab,
- p->id, make_small(tb->common.slot));
+ p->common.id, make_small(tb->common.slot));
db_meta_unlock(meta_pid_to_fixed_tab, LCK_WRITE_REC);
erts_db_free(ERTS_ALC_T_DB_FIXATION,
tb, (void *) fix, sizeof(DbFixation));
@@ -3493,15 +3484,15 @@ static void set_heir(Process* me, DbTable* tb, Eterm heir, UWord heir_data)
if (heir == am_none) {
return;
}
- if (heir == me->id) {
- erts_ensure_later_proc_interval(me->started_interval);
- tb->common.heir_started_interval = me->started_interval;
+ if (heir == me->common.id) {
+ erts_ensure_later_proc_interval(me->common.u.alive.started_interval);
+ tb->common.heir_started_interval = me->common.u.alive.started_interval;
}
else {
Process* heir_proc= erts_proc_lookup(heir);
if (heir_proc != NULL) {
- erts_ensure_later_proc_interval(heir_proc->started_interval);
- tb->common.heir_started_interval = heir_proc->started_interval;
+ erts_ensure_later_proc_interval(heir_proc->common.u.alive.started_interval);
+ tb->common.heir_started_interval = heir_proc->common.u.alive.started_interval;
} else {
tb->common.heir = am_none;
}
@@ -3839,7 +3830,7 @@ erts_ets_colliding_names(Process* p, Eterm name, Uint cnt)
while (index >= atom_table_size()) {
char tmp[20];
erts_snprintf(tmp, sizeof(tmp), "am%x", atom_table_size());
- am_atom_put(tmp,strlen(tmp));
+ erts_atom_put((byte *) tmp, strlen(tmp), ERTS_ATOM_ENC_LATIN1, 1);
}
list = CONS(hp, make_atom(index), list);
hp += 2;
diff --git a/erts/emulator/beam/erl_db.h b/erts/emulator/beam/erl_db.h
index 2e5deaf338..e9a661efbc 100644
--- a/erts/emulator/beam/erl_db.h
+++ b/erts/emulator/beam/erl_db.h
@@ -27,6 +27,10 @@
#define __DB_H__
#include "sys.h"
+#undef ERL_THR_PROGRESS_TSD_TYPE_ONLY
+#define ERL_THR_PROGRESS_TSD_TYPE_ONLY
+#include "erl_thr_progress.h"
+#undef ERL_THR_PROGRESS_TSD_TYPE_ONLY
#include "bif.h"
#include "erl_db_util.h" /* Flags */
@@ -36,6 +40,11 @@
Uint erts_get_ets_misc_mem_size(void);
+typedef struct {
+ DbTableCommon common;
+ ErtsThrPrgrLaterOp data;
+} DbTableRelease;
+
/*
* So, the structure for a database table, NB this is only
* interesting in db.c.
@@ -44,6 +53,7 @@ union db_table {
DbTableCommon common; /* Any type of db table */
DbTableHash hash; /* Linear hash array specific data */
DbTableTree tree; /* AVL tree specific data */
+ DbTableRelease release;
/*TT*/
};
diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c
index c2f6cfa933..58c4d75c31 100644
--- a/erts/emulator/beam/erl_db_util.c
+++ b/erts/emulator/beam/erl_db_util.c
@@ -138,21 +138,23 @@ set_tracee_flags(Process *tracee_p, Eterm tracer, Uint d_flags, Uint e_flags) {
Uint flags;
if (tracer == NIL) {
- flags = tracee_p->trace_flags & ~TRACEE_FLAGS;
+ flags = ERTS_TRACE_FLAGS(tracee_p) & ~TRACEE_FLAGS;
} else {
- flags = ((tracee_p->trace_flags & ~d_flags) | e_flags);
+ flags = ((ERTS_TRACE_FLAGS(tracee_p) & ~d_flags) | e_flags);
if (! flags) tracer = NIL;
}
- ret = tracee_p->tracer_proc != tracer || tracee_p->trace_flags != flags
- ? am_true : am_false;
- tracee_p->tracer_proc = tracer;
- tracee_p->trace_flags = flags;
+ ret = ((ERTS_TRACER_PROC(tracee_p) != tracer
+ || ERTS_TRACE_FLAGS(tracee_p) != flags)
+ ? am_true
+ : am_false);
+ ERTS_TRACER_PROC(tracee_p) = tracer;
+ ERTS_TRACE_FLAGS(tracee_p) = flags;
return ret;
}
/*
** Assuming all locks on tracee_p on entry
**
-** Changes tracee_p->trace_flags and tracee_p->tracer_proc
+** Changes ERTS_TRACE_FLAGS(tracee_p) and ERTS_TRACER_PROC(tracee_p)
** according to input disable/enable flags and tracer.
**
** Returns am_true|am_false on success, am_true if value changed,
@@ -173,17 +175,20 @@ set_match_trace(Process *tracee_p, Eterm fail_term, Eterm tracer,
tracer, ERTS_PROC_LOCKS_ALL))) {
if (tracee_p != tracer_p) {
ret = set_tracee_flags(tracee_p, tracer, d_flags, e_flags);
- tracer_p->trace_flags |= tracee_p->trace_flags ? F_TRACER : 0;
+ ERTS_TRACE_FLAGS(tracer_p) |= (ERTS_TRACE_FLAGS(tracee_p)
+ ? F_TRACER
+ : 0);
erts_smp_proc_unlock(tracer_p, ERTS_PROC_LOCKS_ALL);
}
} else if (is_internal_port(tracer)) {
Port *tracer_port =
- erts_id2port(tracer, tracee_p, ERTS_PROC_LOCKS_ALL);
+ erts_id2port_sflgs(tracer,
+ tracee_p,
+ ERTS_PROC_LOCKS_ALL,
+ ERTS_PORT_SFLGS_INVALID_TRACER_LOOKUP);
if (tracer_port) {
- if (! INVALID_TRACER_PORT(tracer_port, tracer)) {
- ret = set_tracee_flags(tracee_p, tracer, d_flags, e_flags);
- }
- erts_smp_port_unlock(tracer_port);
+ ret = set_tracee_flags(tracee_p, tracer, d_flags, e_flags);
+ erts_port_release(tracer_port);
}
} else {
ASSERT(is_nil(tracer));
@@ -1233,7 +1238,7 @@ static Eterm erts_match_set_run_ets(Process *p, Binary *mpsp,
Eterm ret;
ret = db_prog_match(p, mpsp, args, NULL, NULL, num_args,
- ERTS_PAM_CONTIGUOUS_TUPLE | ERTS_PAM_COPY_RESULT,
+ ERTS_PAM_COPY_RESULT,
return_flags);
#if defined(HARDDEBUG)
if (is_non_value(ret)) {
@@ -2174,7 +2179,7 @@ restart:
pc += n;
break;
case matchSelf:
- *esp++ = c_p->id;
+ *esp++ = c_p->common.id;
break;
case matchWaste:
--esp;
@@ -2261,7 +2266,7 @@ restart:
case matchEnableTrace:
if ( (n = erts_trace_flag2bit(esp[-1]))) {
BEGIN_ATOMIC_TRACE(c_p);
- set_tracee_flags(c_p, c_p->tracer_proc, 0, n);
+ set_tracee_flags(c_p, ERTS_TRACER_PROC(c_p), 0, n);
esp[-1] = am_true;
} else {
esp[-1] = FAIL_TERM;
@@ -2274,7 +2279,7 @@ restart:
BEGIN_ATOMIC_TRACE(c_p);
if ( (tmpp = get_proc(c_p, 0, esp[0], 0))) {
/* Always take over the tracer of the current process */
- set_tracee_flags(tmpp, c_p->tracer_proc, 0, n);
+ set_tracee_flags(tmpp, ERTS_TRACER_PROC(c_p), 0, n);
esp[-1] = am_true;
}
}
@@ -2282,7 +2287,7 @@ restart:
case matchDisableTrace:
if ( (n = erts_trace_flag2bit(esp[-1]))) {
BEGIN_ATOMIC_TRACE(c_p);
- set_tracee_flags(c_p, c_p->tracer_proc, n, 0);
+ set_tracee_flags(c_p, ERTS_TRACER_PROC(c_p), n, 0);
esp[-1] = am_true;
} else {
esp[-1] = FAIL_TERM;
@@ -2295,7 +2300,7 @@ restart:
BEGIN_ATOMIC_TRACE(c_p);
if ( (tmpp = get_proc(c_p, 0, esp[0], 0))) {
/* Always take over the tracer of the current process */
- set_tracee_flags(tmpp, c_p->tracer_proc, n, 0);
+ set_tracee_flags(tmpp, ERTS_TRACER_PROC(c_p), n, 0);
esp[-1] = am_true;
}
}
@@ -2316,12 +2321,12 @@ restart:
--esp;
if (*esp == am_true) {
erts_smp_proc_lock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
- c_p->trace_flags |= F_TRACE_SILENT;
+ ERTS_TRACE_FLAGS(c_p) |= F_TRACE_SILENT;
erts_smp_proc_unlock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
}
else if (*esp == am_false) {
erts_smp_proc_lock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
- c_p->trace_flags &= ~F_TRACE_SILENT;
+ ERTS_TRACE_FLAGS(c_p) &= ~F_TRACE_SILENT;
erts_smp_proc_unlock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
}
break;
@@ -2329,11 +2334,11 @@ restart:
{
/* disable enable */
Uint d_flags = 0, e_flags = 0; /* process trace flags */
- Eterm tracer = c_p->tracer_proc;
+ Eterm tracer = ERTS_TRACER_PROC(c_p);
/* XXX Atomicity note: Not fully atomic. Default tracer
* is sampled from current process but applied to
* tracee and tracer later after releasing main
- * locks on current process, so c_p->tracer_proc
+ * locks on current process, so ERTS_TRACER_PROC(c_p)
* may actually have changed when tracee and tracer
* gets updated. I do not think nobody will notice.
* It is just the default value that is not fully atomic.
@@ -2358,7 +2363,7 @@ restart:
{
/* disable enable */
Uint d_flags = 0, e_flags = 0; /* process trace flags */
- Eterm tracer = c_p->tracer_proc;
+ Eterm tracer = ERTS_TRACER_PROC(c_p);
/* XXX Atomicity note. Not fully atomic. See above.
* Above it could possibly be solved, but not here.
*/
@@ -2480,7 +2485,7 @@ Eterm db_format_dmc_err_info(Process *p, DMCErrInfo *ei)
vnum = tmp->variable;
}
if (vnum >= 0)
- sprintf(buff,tmp->error_string, vnum);
+ erts_snprintf(buff,sizeof(buff)+20,tmp->error_string, vnum);
else
strcpy(buff,tmp->error_string);
sl = strlen(buff);
@@ -2995,7 +3000,7 @@ Eterm db_copy_from_comp(DbTableCommon* tb, DbTerm* bp, Eterm** hpp,
}
ASSERT((*hpp - hp) <= bp->size);
#ifdef DEBUG_CLONE
- ASSERT(eq_rel(make_tuple(hp),make_tuple(bp->debug_clone),bp->debug_clone));
+ ASSERT(eq_rel(make_tuple(hp),NULL,make_tuple(bp->debug_clone),bp->debug_clone));
#endif
return make_tuple(hp);
}
@@ -3018,7 +3023,7 @@ Eterm db_copy_element_from_ets(DbTableCommon* tb, Process* p,
hp += extra;
HRelease(p, endp, hp);
#ifdef DEBUG_CLONE
- ASSERT(eq_rel(copy, obj->debug_clone[pos], obj->debug_clone));
+ ASSERT(eq_rel(copy, NULL, obj->debug_clone[pos], obj->debug_clone));
#endif
return copy;
}
@@ -4485,7 +4490,9 @@ static DMCRet dmc_fun(DMCContext *context,
if (context->err_info != NULL) {
/* Ugly, should define a better RETURN_TERM_ERROR interface... */
char buff[100];
- sprintf(buff, "Function %%T/%d does_not_exist.", (int)a - 1);
+ erts_snprintf(buff, sizeof(buff),
+ "Function %%T/%d does_not_exist.",
+ (int)a - 1);
RETURN_TERM_ERROR(buff, p[1], context, *constant);
} else {
return retFail;
@@ -4500,7 +4507,7 @@ static DMCRet dmc_fun(DMCContext *context,
if (context->err_info != NULL) {
/* Ugly, should define a better RETURN_TERM_ERROR interface... */
char buff[100];
- sprintf(buff,
+ erts_snprintf(buff, sizeof(buff),
"Function %%T/%d cannot be called in this context.",
(int)a - 1);
RETURN_TERM_ERROR(buff, p[1], context, *constant);
@@ -4764,9 +4771,10 @@ static int match_compact(ErlHeapFragment *expr, DMCErrInfo *err_info)
for (j = 0; j < x && DMC_PEEK(heap,j) != n; ++j)
;
ASSERT(j < x);
- sprintf(buff+1,"%u", (unsigned) j);
+ erts_snprintf(buff+1, sizeof(buff) - 1, "%u", (unsigned) j);
/* Yes, writing directly into terms, they ARE off heap */
- *p = am_atom_put(buff, strlen(buff));
+ *p = erts_atom_put((byte *) buff, strlen(buff),
+ ERTS_ATOM_ENC_LATIN1, 1);
}
++p;
}
@@ -5002,7 +5010,7 @@ static Eterm match_spec_test(Process *p, Eterm against, Eterm spec, int trace)
static Eterm seq_trace_fake(Process *p, Eterm arg1)
{
Eterm result = erl_seq_trace_info(p, arg1);
- if (is_tuple(result) && *tuple_val(result) == 2) {
+ if (!is_non_value(result) && is_tuple(result) && *tuple_val(result) == 2) {
return (tuple_val(result))[2];
}
return result;
diff --git a/erts/emulator/beam/erl_db_util.h b/erts/emulator/beam/erl_db_util.h
index ff5982640d..d8f6e40d2e 100644
--- a/erts/emulator/beam/erl_db_util.h
+++ b/erts/emulator/beam/erl_db_util.h
@@ -209,7 +209,7 @@ typedef struct db_fixation {
*/
typedef struct db_table_common {
- erts_refc_t ref; /* fixation counter and delete counter */
+ erts_refc_t ref; /* fixation counter */
#ifdef ERTS_SMP
erts_smp_rwmtx_t rwlock; /* rw lock on table */
erts_smp_mtx_t fixlock; /* Protects fixations,megasec,sec,microsec */
@@ -320,10 +320,10 @@ ERTS_GLB_INLINE int db_eq(DbTableCommon* tb, Eterm a, DbTerm* b)
#define DB_INFO (DB_PROTECTED|DB_PUBLIC|DB_PRIVATE)
#define ONLY_WRITER(P,T) (((T)->common.status & (DB_PRIVATE|DB_PROTECTED)) \
- && (T)->common.owner == (P)->id)
+ && (T)->common.owner == (P)->common.id)
#define ONLY_READER(P,T) (((T)->common.status & DB_PRIVATE) && \
-(T)->common.owner == (P)->id)
+(T)->common.owner == (P)->common.id)
/* Function prototypes */
BIF_RETTYPE db_get_trace_control_word(Process* p);
diff --git a/erts/emulator/beam/erl_debug.c b/erts/emulator/beam/erl_debug.c
index 2121f72fd2..b90d00f236 100644
--- a/erts/emulator/beam/erl_debug.c
+++ b/erts/emulator/beam/erl_debug.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2010. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2012. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -252,16 +252,16 @@ void erts_check_stack(Process *p)
if (p->stop > stack_start)
erl_exit(1,
"<%lu.%lu.%lu>: Stack underflow\n",
- internal_pid_channel_no(p->id),
- internal_pid_number(p->id),
- internal_pid_serial(p->id));
+ internal_pid_channel_no(p->common.id),
+ internal_pid_number(p->common.id),
+ internal_pid_serial(p->common.id));
if (p->stop < stack_end)
erl_exit(1,
"<%lu.%lu.%lu>: Stack overflow\n",
- internal_pid_channel_no(p->id),
- internal_pid_number(p->id),
- internal_pid_serial(p->id));
+ internal_pid_channel_no(p->common.id),
+ internal_pid_number(p->common.id),
+ internal_pid_serial(p->common.id));
for (elemp = p->stop; elemp < stack_start; elemp++) {
int in_mbuf = 0;
@@ -284,9 +284,9 @@ void erts_check_stack(Process *p)
erl_exit(1,
"<%lu.%lu.%lu>: Wild stack pointer\n",
- internal_pid_channel_no(p->id),
- internal_pid_number(p->id),
- internal_pid_serial(p->id));
+ internal_pid_channel_no(p->common.id),
+ internal_pid_number(p->common.id),
+ internal_pid_serial(p->common.id));
}
}
@@ -387,16 +387,16 @@ void verify_process(Process *p)
#define VERIFY_AREA(name,ptr,sz) { \
int n = (sz); \
while (n--) if(!verify_eterm(p,*(ptr+n))) \
- erl_exit(1,"Wild pointer found in " name " of %T!\n",p->id); }
+ erl_exit(1,"Wild pointer found in " name " of %T!\n",p->common.id); }
#define VERIFY_ETERM(name,eterm) { \
if(!verify_eterm(p,eterm)) \
- erl_exit(1,"Wild pointer found in " name " of %T!\n",p->id); }
+ erl_exit(1,"Wild pointer found in " name " of %T!\n",p->common.id); }
ErlMessage* mp = p->msg.first;
- VERBOSE(DEBUG_MEMORY,("Verify process: %T...\n",p->id));
+ VERBOSE(DEBUG_MEMORY,("Verify process: %T...\n",p->common.id));
while (mp != NULL) {
VERIFY_ETERM("message term",ERL_MESSAGE_TERM(mp));
@@ -516,7 +516,7 @@ static void print_process_memory(Process *p)
ErlHeapFragment* bp = MBUF(p);
erts_printf("==============================\n");
- erts_printf("|| Memory info for %T ||\n",p->id);
+ erts_printf("|| Memory info for %T ||\n",p->common.id);
erts_printf("==============================\n");
erts_printf("-- %-*s ---%s-%s-%s-%s--\n",
@@ -601,7 +601,7 @@ void print_memory_info(Process *p)
{
if (p != NULL) {
erts_printf("======================================\n");
- erts_printf("|| Memory info for %-12T ||\n",p->id);
+ erts_printf("|| Memory info for %-12T ||\n",p->common.id);
erts_printf("======================================\n");
erts_printf("+- local heap ----%s-%s-%s-%s-+\n",
dashes,dashes,dashes,dashes);
diff --git a/erts/emulator/beam/erl_debug.h b/erts/emulator/beam/erl_debug.h
index a028a95fef..af51212281 100644
--- a/erts/emulator/beam/erl_debug.h
+++ b/erts/emulator/beam/erl_debug.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2004-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2004-2012. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
diff --git a/erts/emulator/beam/erl_driver.h b/erts/emulator/beam/erl_driver.h
index 1ae9a211d7..d50ba364d0 100644
--- a/erts/emulator/beam/erl_driver.h
+++ b/erts/emulator/beam/erl_driver.h
@@ -85,12 +85,9 @@
#include "erl_drv_nif.h"
#include <stdlib.h>
-#include <string.h> /* ssize_t on Mac OS X */
+#include <sys/types.h> /* ssize_t */
-#if defined(VXWORKS)
-# include <ioLib.h>
-typedef struct iovec SysIOVec;
-#elif defined(__WIN32__) || defined(_WIN32) || defined(_WIN32_)
+#if defined(__WIN32__) || defined(_WIN32) || defined(_WIN32_)
#ifndef STATIC_ERLANG_DRIVER
/* Windows dynamic drivers, everything is different... */
#define ERL_DRIVER_TYPES_ONLY
@@ -136,7 +133,7 @@ typedef struct {
#define ERL_DRV_EXTENDED_MARKER (0xfeeeeeed)
#define ERL_DRV_EXTENDED_MAJOR_VERSION 2
-#define ERL_DRV_EXTENDED_MINOR_VERSION 0
+#define ERL_DRV_EXTENDED_MINOR_VERSION 1
/*
* The emulator will refuse to load a driver with different major
@@ -157,6 +154,7 @@ typedef struct {
#define ERL_DRV_FLAG_USE_PORT_LOCKING (1 << 0)
#define ERL_DRV_FLAG_SOFT_BUSY (1 << 1)
+#define ERL_DRV_FLAG_NO_BUSY_MSGQ (1 << 2)
/*
* Integer types
@@ -210,8 +208,8 @@ typedef struct erl_drv_binary {
typedef struct _erl_drv_data* ErlDrvData; /* Data to be used by the driver itself. */
#ifndef ERL_SYS_DRV
typedef struct _erl_drv_event* ErlDrvEvent; /* An event to be selected on. */
-typedef struct _erl_drv_port* ErlDrvPort; /* A port descriptor. */
#endif
+typedef struct _erl_drv_port* ErlDrvPort; /* A port descriptor. */
typedef struct _erl_drv_port* ErlDrvThreadData; /* Thread data. */
#if !defined(__WIN32__) && !defined(_WIN32) && !defined(_WIN32_) && !defined(USE_SELECT)
@@ -370,11 +368,7 @@ typedef struct erl_drv_entry {
/* For windows dynamic drivers */
#ifndef ERL_DRIVER_TYPES_ONLY
-#if defined(VXWORKS)
-# define DRIVER_INIT(DRIVER_NAME) \
- ErlDrvEntry* DRIVER_NAME ## _init(void); \
- ErlDrvEntry* DRIVER_NAME ## _init(void)
-#elif defined(__WIN32__)
+#if defined(__WIN32__)
# define DRIVER_INIT(DRIVER_NAME) \
__declspec(dllexport) ErlDrvEntry* driver_init(void); \
__declspec(dllexport) ErlDrvEntry* driver_init(void)
@@ -384,9 +378,18 @@ typedef struct erl_drv_entry {
ErlDrvEntry* driver_init(void)
#endif
+#define ERL_DRV_BUSY_MSGQ_DISABLED (~((ErlDrvSizeT) 0))
+#define ERL_DRV_BUSY_MSGQ_READ_ONLY ((ErlDrvSizeT) 0)
+#define ERL_DRV_BUSY_MSGQ_LIM_MAX (ERL_DRV_BUSY_MSGQ_DISABLED - 1)
+#define ERL_DRV_BUSY_MSGQ_LIM_MIN ((ErlDrvSizeT) 1)
+
/*
* These are the functions available for driver writers.
*/
+EXTERN void erl_drv_busy_msgq_limits(ErlDrvPort port,
+ ErlDrvSizeT *low,
+ ErlDrvSizeT *high);
+
EXTERN int driver_select(ErlDrvPort port, ErlDrvEvent event, int mode, int on);
EXTERN int driver_event(ErlDrvPort port, ErlDrvEvent event,
ErlDrvEventData event_data);
@@ -601,11 +604,33 @@ EXTERN ErlDrvPort driver_create_port(ErlDrvPort creator_port,
ErlDrvData drv_data);
+/*
+ * driver_output_term() is deprecated, and scheduled for removal in
+ * OTP-R17. Use erl_drv_output_term() instead. For more information
+ * see the erl_driver(3) documentation.
+ */
+EXTERN int driver_output_term(ErlDrvPort ix,
+ ErlDrvTermData* data,
+ int len) ERL_DRV_DEPRECATED_FUNC;
+/*
+ * driver_send_term() is deprecated, and scheduled for removal in
+ * OTP-R17. Use erl_drv_send_term() instead. For more information
+ * see the erl_driver(3) documentation.
+ */
+EXTERN int driver_send_term(ErlDrvPort ix,
+ ErlDrvTermData to,
+ ErlDrvTermData* data,
+ int len) ERL_DRV_DEPRECATED_FUNC;
+
/* output term data to the port owner */
-EXTERN int driver_output_term(ErlDrvPort ix, ErlDrvTermData* data, int len);
+EXTERN int erl_drv_output_term(ErlDrvTermData port,
+ ErlDrvTermData* data,
+ int len);
/* output term data to a specific process */
-EXTERN int driver_send_term(ErlDrvPort ix, ErlDrvTermData to,
- ErlDrvTermData* data, int len);
+EXTERN int erl_drv_send_term(ErlDrvTermData port,
+ ErlDrvTermData to,
+ ErlDrvTermData* data,
+ int len);
/* Async IO functions */
EXTERN long driver_async(ErlDrvPort ix,
diff --git a/erts/emulator/beam/erl_fun.h b/erts/emulator/beam/erl_fun.h
index 217066ab11..b673ef6b3c 100644
--- a/erts/emulator/beam/erl_fun.h
+++ b/erts/emulator/beam/erl_fun.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2000-2010. All Rights Reserved.
+ * Copyright Ericsson AB 2000-2012. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c
index 6075a527c3..a33085315a 100644
--- a/erts/emulator/beam/erl_gc.c
+++ b/erts/emulator/beam/erl_gc.c
@@ -59,7 +59,7 @@ static Uint reclaimed; /* no of words reclaimed in GCs */
erts_fprintf(stderr, "htop=%p\n", (p)->htop); \
erts_fprintf(stderr, "heap=%p\n", (p)->heap); \
erl_exit(ERTS_ABORT_EXIT, "%s, line %d: %T: Overrun stack and heap\n", \
- __FILE__,__LINE__,(P)->id); \
+ __FILE__,__LINE__,(P)->common.id); \
}
#ifdef DEBUG
@@ -129,7 +129,7 @@ static void disallow_heap_frag_ref(Process* p, Eterm* n_htop, Eterm* objv, int n
#if defined(ARCH_64) && !HALFWORD_HEAP
# define MAX_HEAP_SIZES 154
#else
-# define MAX_HEAP_SIZES 55
+# define MAX_HEAP_SIZES 59
#endif
static Sint heap_sizes[MAX_HEAP_SIZES]; /* Suitable heap sizes. */
@@ -144,6 +144,7 @@ void
erts_init_gc(void)
{
int i = 0;
+ Sint max_heap_size = 0;
ASSERT(offsetof(ProcBin,thing_word) == offsetof(struct erl_off_heap_header,thing_word));
ASSERT(offsetof(ProcBin,thing_word) == offsetof(ErlFunThing,thing_word));
@@ -168,16 +169,30 @@ erts_init_gc(void)
* we really don't want that growth when the heaps are that big.
*/
- heap_sizes[0] = 34;
- heap_sizes[1] = 55;
- for (i = 2; i < 23; i++) {
- heap_sizes[i] = heap_sizes[i-1] + heap_sizes[i-2];
+ /* Growth stage 1 - Fibonacci + 1*/
+ /* 12,38 will hit size 233, the old default */
+
+ heap_sizes[0] = 12;
+ heap_sizes[1] = 38;
+
+ for(i = 2; i < 23; i++) {
+ /* one extra word for block header */
+ heap_sizes[i] = heap_sizes[i-1] + heap_sizes[i-2] + 1;
}
+
+ /* for 32 bit we want max_heap_size to be MAX(32bit) / 4 [words] (and halfword)
+ * for 64 bit we want max_heap_size to be MAX(52bit) / 8 [words]
+ */
+
+ max_heap_size = sizeof(Eterm) < 8 ? (Sint)((~(Uint)0)/(sizeof(Eterm))) :
+ (Sint)(((Uint64)1 << 53)/sizeof(Eterm));
+
+ /* Growth stage 2 - 20% growth */
/* At 1.3 mega words heap, we start to slow down. */
for (i = 23; i < ALENGTH(heap_sizes); i++) {
- heap_sizes[i] = 5*(heap_sizes[i-1]/4);
- if (heap_sizes[i] < 0) {
+ heap_sizes[i] = heap_sizes[i-1] + heap_sizes[i-1]/5;
+ if ((heap_sizes[i] < 0) || heap_sizes[i] > max_heap_size) {
/* Size turned negative. Discard this last size. */
i--;
break;
@@ -336,6 +351,19 @@ erts_gc_after_bif_call(Process* p, Eterm result, Eterm* regs, Uint arity)
return result;
}
+static ERTS_INLINE void reset_active_writer(Process *p)
+{
+ struct erl_off_heap_header* ptr;
+ ptr = MSO(p).first;
+ while (ptr) {
+ if (ptr->thing_word == HEADER_PROC_BIN) {
+ ProcBin *pbp = (ProcBin*) ptr;
+ pbp->flags &= ~PB_ACTIVE_WRITER;
+ }
+ ptr = ptr->next;
+ }
+}
+
/*
* Garbage collect a process.
*
@@ -391,6 +419,7 @@ erts_garbage_collect(Process* p, int need, Eterm* objv, int nobj)
DTRACE2(gc_minor_end, pidbuf, reclaimed_now);
}
}
+ reset_active_writer(p);
/*
* Finish.
@@ -854,14 +883,12 @@ minor_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl)
}
}
- if (wanted < MIN_HEAP_SIZE(p)) {
- wanted = MIN_HEAP_SIZE(p);
- } else {
- wanted = next_heap_size(p, wanted, 0);
- }
+ wanted = wanted < MIN_HEAP_SIZE(p) ? MIN_HEAP_SIZE(p)
+ : next_heap_size(p, wanted, 0);
if (wanted < HEAP_SIZE(p)) {
shrink_new_heap(p, wanted, objv, nobj);
}
+
ASSERT(HEAP_SIZE(p) == next_heap_size(p, HEAP_SIZE(p), 0));
return 1; /* We are done. */
}
@@ -1420,11 +1447,10 @@ adjust_after_fullsweep(Process *p, Uint size_before, int need, Eterm *objv, int
I think this is better as fullsweep is used mainly on
small memory systems, but I could be wrong... */
wanted = 2 * need_after;
- if (wanted < p->min_heap_size) {
- sz = p->min_heap_size;
- } else {
- sz = next_heap_size(p, wanted, 0);
- }
+
+ sz = wanted < p->min_heap_size ? p->min_heap_size
+ : next_heap_size(p, wanted, 0);
+
if (sz < HEAP_SIZE(p)) {
shrink_new_heap(p, sz, objv, nobj);
}
@@ -1932,9 +1958,9 @@ setup_rootset(Process *p, Eterm *objv, int nobj, Rootset *rootset)
n++;
}
#endif
- ASSERT(is_nil(p->tracer_proc) ||
- is_internal_pid(p->tracer_proc) ||
- is_internal_port(p->tracer_proc));
+ ASSERT(is_nil(ERTS_TRACER_PROC(p)) ||
+ is_internal_pid(ERTS_TRACER_PROC(p)) ||
+ is_internal_port(ERTS_TRACER_PROC(p)));
ASSERT(is_pid(follow_moved(p->group_leader)));
if (is_not_immed(p->group_leader)) {
@@ -2166,7 +2192,6 @@ link_live_proc_bin(struct shrink_cand_data *shrink,
if (pbp->flags & PB_ACTIVE_WRITER) {
- pbp->flags &= ~PB_ACTIVE_WRITER;
shrink->no_of_active++;
}
else { /* inactive */
diff --git a/erts/emulator/beam/erl_goodfit_alloc.c b/erts/emulator/beam/erl_goodfit_alloc.c
index e7d4ac2b67..f98d377fd6 100644
--- a/erts/emulator/beam/erl_goodfit_alloc.c
+++ b/erts/emulator/beam/erl_goodfit_alloc.c
@@ -205,7 +205,6 @@ erts_gfalc_start(GFAllctr_t *gfallctr,
init->sbmbct = 0; /* Small mbc not yet supported by goodfit */
- allctr->mbc_header_size = sizeof(Carrier_t);
allctr->min_mbc_size = MIN_MBC_SZ;
allctr->min_mbc_first_free_size = MIN_MBC_FIRST_FREE_SZ;
allctr->min_block_size = sizeof(GFFreeBlock_t);
@@ -363,7 +362,7 @@ search_bucket(Allctr_t *allctr, int ix, Uint size)
blk && i < max_blk_search;
blk = blk->next, i++) {
- blk_sz = BLK_SZ(blk);
+ blk_sz = MBC_FBLK_SZ(&blk->block_head);
blk_on_lambc = (((char *) blk) < gfallctr->last_aux_mbc_end
&& gfallctr->last_aux_mbc_start <= ((char *) blk));
@@ -402,7 +401,7 @@ get_free_block(Allctr_t *allctr, Uint size,
if (min_bi == unsafe_bi) {
blk = search_bucket(allctr, min_bi, size);
if (blk) {
- if (cand_blk && cand_size <= BLK_SZ(blk))
+ if (cand_blk && cand_size <= MBC_FBLK_SZ(blk))
return NULL; /* cand_blk was better */
unlink_free_block(allctr, blk, flags);
return blk;
@@ -422,7 +421,7 @@ get_free_block(Allctr_t *allctr, Uint size,
/* We are guaranteed to find a block that fits in this bucket */
blk = search_bucket(allctr, min_bi, size);
ASSERT(blk);
- if (cand_blk && cand_size <= BLK_SZ(blk))
+ if (cand_blk && cand_size <= MBC_FBLK_SZ(blk))
return NULL; /* cand_blk was better */
unlink_free_block(allctr, blk, flags);
return blk;
@@ -435,7 +434,7 @@ link_free_block(Allctr_t *allctr, Block_t *block, Uint32 flags)
{
GFAllctr_t *gfallctr = (GFAllctr_t *) allctr;
GFFreeBlock_t *blk = (GFFreeBlock_t *) block;
- Uint sz = BLK_SZ(blk);
+ Uint sz = MBC_FBLK_SZ(&blk->block_head);
int i = BKT_IX(gfallctr, sz);
ASSERT(sz >= MIN_BLK_SZ);
@@ -456,7 +455,7 @@ unlink_free_block(Allctr_t *allctr, Block_t *block, Uint32 flags)
{
GFAllctr_t *gfallctr = (GFAllctr_t *) allctr;
GFFreeBlock_t *blk = (GFFreeBlock_t *) block;
- Uint sz = BLK_SZ(blk);
+ Uint sz = MBC_FBLK_SZ(&blk->block_head);
int i = BKT_IX(gfallctr, sz);
if (!blk->prev) {
@@ -618,7 +617,7 @@ check_block(Allctr_t *allctr, Block_t * blk, int free_block)
GFFreeBlock_t *fblk;
if(free_block) {
- Uint blk_sz = BLK_SZ(blk);
+ Uint blk_sz = is_sbc_blk(blk) ? SBC_BLK_SZ(blk) : MBC_BLK_SZ(blk);
bi = BKT_IX(gfallctr, blk_sz);
ASSERT(gfallctr->bucket_mask.main & (((UWord) 1) << IX2SMIX(bi)));
diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c
index d382e421e6..61b3c09d16 100644
--- a/erts/emulator/beam/erl_init.c
+++ b/erts/emulator/beam/erl_init.c
@@ -44,6 +44,7 @@
#include "erl_thr_progress.h"
#include "erl_thr_queue.h"
#include "erl_async.h"
+#include "erl_ptab.h"
#ifdef HIPE
#include "hipe_mode_switch.h" /* for hipe_mode_switch_init() */
@@ -54,6 +55,8 @@
# include <sys/resource.h>
#endif
+#define ERTS_DEFAULT_NO_ASYNC_THREADS 10
+
/*
* The variables below (prefixed with etp_) are for erts/etc/unix/etp-commands
* only. Do not remove even though they aren't used elsewhere in the emulator!
@@ -109,6 +112,11 @@ const int etp_lock_check = 1;
#else
const int etp_lock_check = 0;
#endif
+#ifdef WORDS_BIGENDIAN
+const int etp_big_endian = 1;
+#else
+const int etp_big_endian = 0;
+#endif
/*
* Note about VxWorks: All variables must be initialized by executable code,
* not by an initializer. Otherwise a new instance of the emulator will
@@ -121,9 +129,10 @@ extern void ConNormalExit(void);
extern void ConWaitForExit(void);
#endif
-static void erl_init(int ncpu);
-
-#define ERTS_MIN_COMPAT_REL 7
+static void erl_init(int ncpu,
+ int proc_tab_sz,
+ int port_tab_sz,
+ int port_tab_sz_ignore_files);
static erts_atomic_t exiting;
@@ -206,8 +215,6 @@ ErtsModifiedTimings erts_modified_timings[] = {
Export *erts_delay_trap = NULL;
-int erts_use_r9_pids_ports;
-
int ignore_break;
int replace_intr;
@@ -271,12 +278,18 @@ void
erts_short_init(void)
{
int ncpu = early_init(NULL, NULL);
- erl_init(ncpu);
+ erl_init(ncpu,
+ ERTS_DEFAULT_MAX_PROCESSES,
+ ERTS_DEFAULT_MAX_PORTS,
+ 0);
erts_initialized = 1;
}
static void
-erl_init(int ncpu)
+erl_init(int ncpu,
+ int proc_tab_sz,
+ int port_tab_sz,
+ int port_tab_sz_ignore_files)
{
init_benchmarking();
@@ -284,7 +297,7 @@ erl_init(int ncpu)
erts_init_gc();
erts_init_time();
erts_init_sys_common_misc();
- erts_init_process(ncpu);
+ erts_init_process(ncpu, proc_tab_sz);
erts_init_scheduling(no_schedulers,
no_schedulers_online);
erts_init_cpu_topology(); /* Must be after init_scheduling */
@@ -306,6 +319,7 @@ erl_init(int ncpu)
erts_bif_info_init();
erts_ddll_init();
init_emulator();
+ erts_ptab_init(); /* Must be after init_emulator() */
erts_bp_init();
init_db(); /* Must be after init_emulator */
erts_bif_timer_init();
@@ -313,7 +327,7 @@ erl_init(int ncpu)
init_dist();
erl_drv_thr_init();
erts_init_async();
- init_io();
+ erts_init_io(port_tab_sz, port_tab_sz_ignore_files);
init_load();
erts_init_bif();
erts_init_bif_chksum();
@@ -344,7 +358,7 @@ erl_first_process_otp(char* modname, void* code, unsigned size, int argc, char**
ErlSpawnOpts so;
Eterm env;
- start_mod = am_atom_put(modname, sys_strlen(modname));
+ start_mod = erts_atom_put((byte *) modname, sys_strlen(modname), ERTS_ATOM_ENC_LATIN1, 1);
if (erts_find_function(start_mod, am_start, 2,
erts_active_code_ix()) == NULL) {
erl_exit(5, "No function %s:start/2\n", modname);
@@ -441,7 +455,7 @@ load_preloaded(void)
i = 0;
while ((name = preload_p[i].name) != NULL) {
length = preload_p[i].size;
- module_name = am_atom_put(name, sys_strlen(name));
+ module_name = erts_atom_put((byte *) name, sys_strlen(name), ERTS_ATOM_ENC_LATIN1, 1);
if ((code = sys_preload_begin(&preload_p[i])) == 0)
erl_exit(1, "Failed to find preloaded code for module %s\n",
name);
@@ -457,6 +471,7 @@ load_preloaded(void)
/* be helpful (or maybe downright rude:-) */
void erts_usage(void)
{
+ int this_rel = this_rel_num();
erts_fprintf(stderr, "Usage: %s [flags] [ -- [init_args] ]\n", progname(program));
erts_fprintf(stderr, "The flags are:\n\n");
@@ -490,21 +505,25 @@ void erts_usage(void)
/* erts_fprintf(stderr, "-i module set the boot module (default init)\n"); */
erts_fprintf(stderr, "-K boolean enable or disable kernel poll\n");
-
+ erts_fprintf(stderr, "-n[s|a|d] Control behavior of signals to ports\n");
+ erts_fprintf(stderr, " Note that this flag is deprecated!\n");
erts_fprintf(stderr, "-M<X> <Y> memory allocator switches,\n");
erts_fprintf(stderr, " see the erts_alloc(3) documentation for more info.\n");
erts_fprintf(stderr, "-P number set maximum number of processes on this node,\n");
erts_fprintf(stderr, " valid range is [%d-%d]\n",
- ERTS_MIN_PROCESSES, ERTS_MAX_PROCESSES);
+ ERTS_MIN_PROCESSES, ERTS_MAX_PROCESSES);
+ erts_fprintf(stderr, "-Q number set maximum number of ports on this node,\n");
+ erts_fprintf(stderr, " valid range is [%d-%d]\n",
+ ERTS_MIN_PORTS, ERTS_MAX_PORTS);
erts_fprintf(stderr, "-R number set compatibility release number,\n");
erts_fprintf(stderr, " valid range [%d-%d]\n",
- ERTS_MIN_COMPAT_REL, this_rel_num());
+ this_rel-2, this_rel);
erts_fprintf(stderr, "-r force ets memory block to be moved on realloc\n");
erts_fprintf(stderr, "-rg amount set reader groups limit\n");
erts_fprintf(stderr, "-sbt type set scheduler bind type, valid types are:\n");
- erts_fprintf(stderr, " u|ns|ts|ps|s|nnts|nnps|tnnps|db\n");
+ erts_fprintf(stderr, "-stbt type u|ns|ts|ps|s|nnts|nnps|tnnps|db\n");
erts_fprintf(stderr, "-sbwt val set scheduler busy wait threshold, valid values are:\n");
erts_fprintf(stderr, " none|very_short|short|medium|long|very_long.\n");
erts_fprintf(stderr, "-scl bool enable/disable compaction of scheduler load,\n");
@@ -512,13 +531,14 @@ void erts_usage(void)
erts_fprintf(stderr, "-sct cput set cpu topology,\n");
erts_fprintf(stderr, " see the erl(1) documentation for more info.\n");
erts_fprintf(stderr, "-sws val set scheduler wakeup strategy, valid values are:\n");
- erts_fprintf(stderr, " default|legacy|proposal.\n");
+ erts_fprintf(stderr, " default|legacy.\n");
erts_fprintf(stderr, "-swt val set scheduler wakeup threshold, valid values are:\n");
erts_fprintf(stderr, " very_low|low|medium|high|very_high.\n");
erts_fprintf(stderr, "-sss size suggested stack size in kilo words for scheduler threads,\n");
erts_fprintf(stderr, " valid range is [%d-%d]\n",
ERTS_SCHED_THREAD_MIN_STACK_SIZE,
ERTS_SCHED_THREAD_MAX_STACK_SIZE);
+ erts_fprintf(stderr, "-spp Bool set port parallelism scheduling hint\n");
erts_fprintf(stderr, "-S n1:n2 set number of schedulers (n1), and number of\n");
erts_fprintf(stderr, " schedulers online (n2), valid range for both\n");
erts_fprintf(stderr, " numbers are [1-%d]\n",
@@ -612,9 +632,8 @@ early_init(int *argc, char **argv) /*
erts_printf_eterm_func = erts_printf_term;
erts_disable_tolerant_timeofday = 0;
display_items = 200;
- erts_proc.max = ERTS_DEFAULT_MAX_PROCESSES;
erts_backtrace_depth = DEFAULT_BACKTRACE_SIZE;
- erts_async_max_threads = 0;
+ erts_async_max_threads = ERTS_DEFAULT_NO_ASYNC_THREADS;
erts_async_thread_suggested_stack_size = ERTS_ASYNC_THREAD_MIN_STACK_SIZE;
H_MIN_SIZE = H_DEFAULT_SIZE;
BIN_VH_MIN_SIZE = VH_DEFAULT_SIZE;
@@ -641,8 +660,6 @@ early_init(int *argc, char **argv) /*
erts_compat_rel = this_rel_num();
- erts_use_r9_pids_ports = 0;
-
erts_sys_pre_init();
erts_atomic_init_nob(&exiting, 0);
#ifdef ERTS_SMP
@@ -681,11 +698,11 @@ early_init(int *argc, char **argv) /*
envbufsz = sizeof(envbuf);
- /* erts_sys_getenv() not initialized yet; need erts_sys_getenv__() */
+ /* erts_sys_getenv(_raw)() not initialized yet; need erts_sys_getenv__() */
if (erts_sys_getenv__("ERL_THREAD_POOL_SIZE", envbuf, &envbufsz) == 0)
erts_async_max_threads = atoi(envbuf);
else
- erts_async_max_threads = 0;
+ erts_async_max_threads = ERTS_DEFAULT_NO_ASYNC_THREADS;
if (erts_async_max_threads > ERTS_MAX_NO_OF_ASYNC_THREADS)
erts_async_max_threads = ERTS_MAX_NO_OF_ASYNC_THREADS;
@@ -897,20 +914,22 @@ erl_start(int argc, char **argv)
{
int i = 1;
char* arg=NULL;
- char* Parg = NULL;
int have_break_handler = 1;
char envbuf[21]; /* enough for any 64-bit integer */
size_t envbufsz;
int ncpu = early_init(&argc, argv);
+ int proc_tab_sz = ERTS_DEFAULT_MAX_PROCESSES;
+ int port_tab_sz = ERTS_DEFAULT_MAX_PORTS;
+ int port_tab_sz_ignore_files = 0;
envbufsz = sizeof(envbuf);
- if (erts_sys_getenv(ERL_MAX_ETS_TABLES_ENV, envbuf, &envbufsz) == 0)
+ if (erts_sys_getenv_raw(ERL_MAX_ETS_TABLES_ENV, envbuf, &envbufsz) == 0)
user_requested_db_max_tabs = atoi(envbuf);
else
user_requested_db_max_tabs = 0;
envbufsz = sizeof(envbuf);
- if (erts_sys_getenv("ERL_FULLSWEEP_AFTER", envbuf, &envbufsz) == 0) {
+ if (erts_sys_getenv_raw("ERL_FULLSWEEP_AFTER", envbuf, &envbufsz) == 0) {
Uint16 max_gen_gcs = atoi(envbuf);
erts_smp_atomic32_set_nob(&erts_max_gen_gcs,
(erts_aint32_t) max_gen_gcs);
@@ -972,6 +991,7 @@ erl_start(int argc, char **argv)
break;
case 'a':
erts_set_user_requested_filename_encoding(ERL_FILENAME_UNKNOWN);
+ break;
default:
erts_fprintf(stderr, "bad filename encoding %s, can be (l,u or a)\n", arg);
erts_usage();
@@ -1152,12 +1172,53 @@ erl_start(int argc, char **argv)
arg);
break;
- case 'P':
- /* set maximum number of processes */
- Parg = get_arg(argv[i]+2, argv[i+1], &i);
- erts_proc.max = atoi(Parg);
- /* Check of result is delayed until later. This is because +R
- may be given after +P. */
+ case 'n':
+ arg = get_arg(argv[i]+2, argv[i+1], &i);
+ switch (arg[0]) {
+ case 's': /* synchronous */
+ erts_port_synchronous_ops = 1;
+ erts_port_schedule_all_ops = 0;
+ break;
+ case 'a': /* asynchronous */
+ erts_port_synchronous_ops = 0;
+ erts_port_schedule_all_ops = 1;
+ break;
+ case 'd': /* Default - schedule on conflict (asynchronous) */
+ erts_port_synchronous_ops = 0;
+ erts_port_schedule_all_ops = 0;
+ break;
+ default:
+ bad_n_option:
+ erts_fprintf(stderr, "bad -n option %s\n", arg);
+ erts_usage();
+ }
+ if (arg[1] != '\0')
+ goto bad_n_option;
+ break;
+
+ case 'P': /* set maximum number of processes */
+ arg = get_arg(argv[i]+2, argv[i+1], &i);
+ errno = 0;
+ proc_tab_sz = strtol(arg, NULL, 10);
+ if (errno != 0
+ || proc_tab_sz < ERTS_MIN_PROCESSES
+ || ERTS_MAX_PROCESSES < proc_tab_sz) {
+ erts_fprintf(stderr, "bad number of processes %s\n", arg);
+ erts_usage();
+ }
+ break;
+
+ case 'Q': /* set maximum number of ports */
+ arg = get_arg(argv[i]+2, argv[i+1], &i);
+ errno = 0;
+ port_tab_sz = strtol(arg, NULL, 10);
+ if (errno != 0
+ || port_tab_sz < ERTS_MIN_PROCESSES
+ || ERTS_MAX_PROCESSES < port_tab_sz) {
+ erts_fprintf(stderr, "bad number of ports %s\n", arg);
+ erts_usage();
+ }
+ port_tab_sz_ignore_files = 1;
break;
case 'S' : /* Was handled in early_init() just read past it */
@@ -1179,7 +1240,7 @@ erl_start(int argc, char **argv)
case ERTS_INIT_SCHED_BIND_TYPE_ERROR_NO_CPU_TOPOLOGY:
estr = "no cpu topology available";
break;
- case ERTS_INIT_SCHED_BIND_TYPE_ERROR_NO_BAD_TYPE:
+ case ERTS_INIT_SCHED_BIND_TYPE_ERROR_BAD_TYPE:
estr = "invalid type";
break;
default:
@@ -1259,8 +1320,31 @@ erl_start(int argc, char **argv)
erts_usage();
}
}
+ else if (has_prefix("pp", sub_param)) {
+ arg = get_arg(sub_param+2, argv[i+1], &i);
+ if (sys_strcmp(arg, "true") == 0)
+ erts_port_parallelism = 1;
+ else if (sys_strcmp(arg, "false") == 0)
+ erts_port_parallelism = 0;
+ else {
+ erts_fprintf(stderr,
+ "bad port parallelism scheduling hint %s\n",
+ arg);
+ erts_usage();
+ }
+ }
else if (sys_strcmp("nsp", sub_param) == 0)
erts_use_sender_punish = 0;
+ else if (has_prefix("tbt", sub_param)) {
+ arg = get_arg(sub_param+3, argv[i+1], &i);
+ res = erts_init_scheduler_bind_type_string(arg);
+ if (res == ERTS_INIT_SCHED_BIND_TYPE_ERROR_BAD_TYPE) {
+ erts_fprintf(stderr,
+ "setting scheduler bind type '%s' failed: invalid type\n",
+ arg);
+ erts_usage();
+ }
+ }
else if (sys_strcmp("wt", sub_param) == 0) {
arg = get_arg(sub_param+2, argv[i+1], &i);
if (erts_sched_set_wakeup_other_thresold(arg) != 0) {
@@ -1340,22 +1424,19 @@ erl_start(int argc, char **argv)
case 'R': {
/* set compatibility release */
+ int this_rel;
arg = get_arg(argv[i]+2, argv[i+1], &i);
erts_compat_rel = atoi(arg);
- if (erts_compat_rel < ERTS_MIN_COMPAT_REL
- || erts_compat_rel > this_rel_num()) {
+ this_rel = this_rel_num();
+ if (erts_compat_rel < this_rel - 2 || this_rel < erts_compat_rel) {
erts_fprintf(stderr, "bad compatibility release number %s\n", arg);
erts_usage();
}
- ASSERT(ERTS_MIN_COMPAT_REL >= 7);
switch (erts_compat_rel) {
- case 7:
- case 8:
- case 9:
- erts_use_r9_pids_ports = 1;
+ /* Currently no compat features... */
default:
break;
}
@@ -1397,8 +1478,6 @@ erl_start(int argc, char **argv)
}
break;
}
- case 'n': /* XXX obsolete */
- break;
case 'c':
if (argv[i][2] == 0) { /* -c: documented option */
erts_disable_tolerant_timeofday = 1;
@@ -1453,14 +1532,13 @@ erl_start(int argc, char **argv)
i++;
}
- /* Delayed check of +P flag */
- if (erts_proc.max < ERTS_MIN_PROCESSES
- || erts_proc.max > ERTS_MAX_PROCESSES
- || (erts_use_r9_pids_ports
- && erts_proc.max > ERTS_MAX_R9_PROCESSES)) {
- erts_fprintf(stderr, "bad number of processes %s\n", Parg);
- erts_usage();
- }
+/* Output format on windows for sprintf defaults to three exponents.
+ * We use two-exponent to mimic normal sprintf behaviour.
+ */
+
+#if defined(__WIN32__) && defined(_TWO_DIGIT_EXPONENT)
+ _set_output_format(_TWO_DIGIT_EXPONENT);
+#endif
/* Restart will not reinstall the break handler */
#ifdef __WIN32__
@@ -1482,7 +1560,10 @@ erl_start(int argc, char **argv)
boot_argc = argc - i; /* Number of arguments to init */
boot_argv = &argv[i];
- erl_init(ncpu);
+ erl_init(ncpu,
+ proc_tab_sz,
+ port_tab_sz,
+ port_tab_sz_ignore_files);
load_preloaded();
erts_end_staging_code_ix();
diff --git a/erts/emulator/beam/erl_lock_check.c b/erts/emulator/beam/erl_lock_check.c
index 95efd9efc3..69bb4be717 100644
--- a/erts/emulator/beam/erl_lock_check.c
+++ b/erts/emulator/beam/erl_lock_check.c
@@ -93,9 +93,8 @@ static erts_lc_lock_order_t erts_lock_order[] = {
{ "proc_msgq", "pid" },
{ "dist_entry", "address" },
{ "dist_entry_links", "address" },
- { "code_ix_queue", NULL },
+ { "code_write_permission", NULL },
{ "proc_status", "pid" },
- { "proc_tab", NULL },
{ "ports_snapshot", NULL },
{ "meta_name_tab", "address" },
{ "meta_main_tab_slot", "address" },
@@ -115,9 +114,6 @@ static erts_lc_lock_order_t erts_lock_order[] = {
#if defined(ENABLE_CHILD_WAITER_THREAD) || defined(ERTS_SMP)
{ "child_status", NULL },
#endif
-#ifdef __WIN32__
- { "sys_driver_data_lock", NULL },
-#endif
{ "drv_ev_state_grow", NULL, },
{ "drv_ev_state", "address" },
{ "safe_hash", "address" },
@@ -127,6 +123,7 @@ static erts_lc_lock_order_t erts_lock_order[] = {
{ "schdlr_sspnd", NULL },
{ "migration_info_update", NULL },
{ "run_queue", "address" },
+ { "process_table", NULL },
{ "cpu_info", NULL },
{ "pollset", "address" },
#ifdef __WIN32__
@@ -157,12 +154,10 @@ static erts_lc_lock_order_t erts_lock_order[] = {
{ "pmmap", NULL },
#endif
#ifdef ERTS_SMP
+ { "port_sched_lock", "port_id" },
{ "port_task_pre_alloc_lock", "address" },
- { "port_taskq_pre_alloc_lock", "address" },
{ "proclist_pre_alloc_lock", "address" },
- { "port_tasks_lock", NULL },
- { "get_free_port", NULL },
- { "port_state", "address" },
+ { "port_table", NULL },
{ "xports_list_pre_alloc_lock", "address" },
{ "inet_buffer_stack_lock", NULL },
{ "gc_info", NULL },
@@ -177,9 +172,6 @@ static erts_lc_lock_order_t erts_lock_order[] = {
{ "sched_stat", NULL },
#endif
{ "async_init_mtx", NULL },
-#ifdef ERTS_SMP
- { "proc_lck_qs_alloc", NULL },
-#endif
#ifdef __WIN32__
#ifdef DEBUG
{ "save_ops_lock", NULL },
@@ -250,6 +242,7 @@ typedef struct {
typedef struct erts_lc_locked_locks_t_ erts_lc_locked_locks_t;
struct erts_lc_locked_locks_t_ {
char *thread_name;
+ int emu_thread;
erts_tid_t tid;
erts_lc_locked_locks_t *next;
erts_lc_locked_locks_t *prev;
@@ -367,6 +360,7 @@ create_locked_locks(char *thread_name)
if (!l_lcks->thread_name)
lc_abort();
+ l_lcks->emu_thread = 0;
l_lcks->tid = erts_thr_self();
l_lcks->required.first = NULL;
l_lcks->required.last = NULL;
@@ -674,7 +668,7 @@ erts_lc_set_thread_name(char *thread_name)
{
erts_lc_locked_locks_t *l_lcks = get_my_locked_locks();
if (!l_lcks)
- (void) create_locked_locks(thread_name);
+ l_lcks = create_locked_locks(thread_name);
else {
ASSERT(l_lcks->thread_name);
free((void *) l_lcks->thread_name);
@@ -682,6 +676,14 @@ erts_lc_set_thread_name(char *thread_name)
if (!l_lcks->thread_name)
lc_abort();
}
+ l_lcks->emu_thread = 1;
+}
+
+int
+erts_lc_is_emu_thr(void)
+{
+ erts_lc_locked_locks_t *l_lcks = get_my_locked_locks();
+ return l_lcks->emu_thread;
}
int
diff --git a/erts/emulator/beam/erl_lock_check.h b/erts/emulator/beam/erl_lock_check.h
index b67f36fa06..068340abe7 100644
--- a/erts/emulator/beam/erl_lock_check.h
+++ b/erts/emulator/beam/erl_lock_check.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2005-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2005-2012. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -102,9 +102,10 @@ void erts_lc_unrequire_lock_flg(erts_lc_lock_t *lck, Uint16 op_flags);
void erts_lc_require_lock(erts_lc_lock_t *lck);
void erts_lc_unrequire_lock(erts_lc_lock_t *lck);
+int erts_lc_is_emu_thr(void);
#define ERTS_LC_ASSERT(A) \
- ((void) ((A) ? 1 : erts_lc_assert_failed(__FILE__, __LINE__, #A)))
+ ((void) (((A) || ERTS_SOMEONE_IS_CRASH_DUMPING) ? 1 : erts_lc_assert_failed(__FILE__, __LINE__, #A)))
#ifdef ERTS_SMP
#define ERTS_SMP_LC_ASSERT(A) ERTS_LC_ASSERT(A)
#else
diff --git a/erts/emulator/beam/erl_lock_count.c b/erts/emulator/beam/erl_lock_count.c
index 741c0cb08e..5f75b0ac0b 100644
--- a/erts/emulator/beam/erl_lock_count.c
+++ b/erts/emulator/beam/erl_lock_count.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2008-2010. All Rights Reserved.
+ * Copyright Ericsson AB 2008-2012. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
diff --git a/erts/emulator/beam/erl_lock_count.h b/erts/emulator/beam/erl_lock_count.h
index 690551c71f..a4fc91b510 100644
--- a/erts/emulator/beam/erl_lock_count.h
+++ b/erts/emulator/beam/erl_lock_count.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2008-2010. All Rights Reserved.
+ * Copyright Ericsson AB 2008-2012. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c
index 26bb015070..325d77e911 100644
--- a/erts/emulator/beam/erl_message.c
+++ b/erts/emulator/beam/erl_message.c
@@ -418,7 +418,7 @@ erts_queue_dist_message(Process *rcvr,
}
/* Add a message last in message queue */
-static int
+static Sint
queue_message(Process *c_p,
Process* receiver,
ErtsProcLocks *receiver_locks,
@@ -431,6 +431,7 @@ queue_message(Process *c_p,
#endif
)
{
+ Sint res;
ErlMessage* mp;
int locked_msgq = 0;
erts_aint_t state;
@@ -491,7 +492,10 @@ queue_message(Process *c_p,
mp->next = NULL;
mp->data.heap_frag = bp;
-#ifdef ERTS_SMP
+#ifndef ERTS_SMP
+ res = receiver->msg.len;
+#else
+ res = receiver->msg_inq.len;
if (*receiver_locks & ERTS_PROC_LOCK_MAIN) {
/*
* We move 'in queue' to 'private queue' and place
@@ -501,6 +505,7 @@ queue_message(Process *c_p,
* we don't need to include the 'in queue' in
* the root set when garbage collecting.
*/
+ res += receiver->msg.len;
ERTS_SMP_MSGQ_MV_INQ2PRIVQ(receiver);
LINK_MESSAGE_PRIVQ(receiver, mp);
}
@@ -540,7 +545,7 @@ queue_message(Process *c_p,
#ifndef ERTS_SMP
ERTS_HOLE_CHECK(receiver);
#endif
- return 0;
+ return res;
}
void
@@ -864,7 +869,7 @@ erts_move_msg_attached_data_to_heap(Eterm **hpp, ErlOffHeap *ohp, ErlMessage *ms
* Send a local message when sender & receiver processes are known.
*/
-void
+Sint
erts_send_message(Process* sender,
Process* receiver,
ErtsProcLocks *receiver_locks,
@@ -874,6 +879,7 @@ erts_send_message(Process* sender,
Uint msize;
ErlHeapFragment* bp = NULL;
Eterm token = NIL;
+ Sint res = 0;
#ifdef USE_VM_PROBES
DTRACE_CHARBUF(sender_name, 64);
DTRACE_CHARBUF(receiver_name, 64);
@@ -888,8 +894,8 @@ erts_send_message(Process* sender,
#ifdef USE_VM_PROBES
*sender_name = *receiver_name = '\0';
if (DTRACE_ENABLED(message_send)) {
- erts_snprintf(sender_name, sizeof(sender_name), "%T", sender->id);
- erts_snprintf(receiver_name, sizeof(receiver_name), "%T", receiver->id);
+ erts_snprintf(sender_name, sizeof(DTRACE_CHARBUF_NAME(sender_name)), "%T", sender->common.id);
+ erts_snprintf(receiver_name, sizeof(DTRACE_CHARBUF_NAME(receiver_name)), "%T", receiver->common.id);
}
#endif
if (SEQ_TRACE_TOKEN(sender) != NIL && !(flags & ERTS_SND_FLG_NO_SEQ_TRACE)) {
@@ -911,7 +917,7 @@ erts_send_message(Process* sender,
seq_trace_update_send(sender);
seq_trace_output(stoken, message, SEQ_TRACE_SEND,
- receiver->id, sender);
+ receiver->common.id, sender);
seq_trace_size = 6; /* TUPLE5 */
#ifdef USE_VM_PROBES
}
@@ -942,7 +948,7 @@ erts_send_message(Process* sender,
#ifdef DTRACE_TAG_HARDDEBUG
erts_fprintf(stderr,
"Dtrace -> (%T) Spreading tag (%T) with "
- "message %T!\r\n",sender->id, utag, message);
+ "message %T!\r\n",sender->common.id, utag, message);
#endif
}
#endif
@@ -960,15 +966,17 @@ erts_send_message(Process* sender,
msize, tok_label, tok_lastcnt, tok_serial);
}
#endif
- erts_queue_message(receiver,
- receiver_locks,
- bp,
- message,
- token
+ res = queue_message(NULL,
+ receiver,
+ receiver_locks,
+ NULL,
+ bp,
+ message,
+ token
#ifdef USE_VM_PROBES
- , utag
+ , utag
#endif
- );
+ );
BM_SWAP_TIMER(send,system);
} else if (sender == receiver) {
/* Drop message if receiver has a pending exit ... */
@@ -1012,12 +1020,13 @@ erts_send_message(Process* sender,
ERTS_SMP_MSGQ_MV_INQ2PRIVQ(receiver);
LINK_MESSAGE_PRIVQ(receiver, mp);
+ res = receiver->msg.len;
+
if (IS_TRACED_FL(receiver, F_TRACE_RECEIVE)) {
trace_receive(receiver, message);
}
}
BM_SWAP_TIMER(send,system);
- return;
} else {
#ifdef ERTS_SMP
ErlOffHeap *ohp;
@@ -1039,15 +1048,15 @@ erts_send_message(Process* sender,
BM_SWAP_TIMER(copy,send);
DTRACE6(message_send, sender_name, receiver_name,
msize, tok_label, tok_lastcnt, tok_serial);
- queue_message(sender,
- receiver,
- receiver_locks,
- &state,
- bp,
- message,
- token
+ res = queue_message(sender,
+ receiver,
+ receiver_locks,
+ &state,
+ bp,
+ message,
+ token
#ifdef USE_VM_PROBES
- , NIL
+ , NIL
#endif
);
BM_SWAP_TIMER(send,system);
@@ -1079,7 +1088,7 @@ erts_send_message(Process* sender,
mp->next = NULL;
mp->data.attached = NULL;
LINK_MESSAGE(receiver, mp);
-
+ res = receiver->msg.len;
erts_proc_notify_new_message(receiver);
if (IS_TRACED_FL(receiver, F_TRACE_RECEIVE)) {
@@ -1087,8 +1096,8 @@ erts_send_message(Process* sender,
}
BM_SWAP_TIMER(send,system);
#endif /* #ifndef ERTS_SMP */
- return;
}
+ return res;
}
/*
@@ -1127,7 +1136,7 @@ erts_deliver_exit_message(Eterm from, Process *to, ErtsProcLocks *to_locksp,
save = TUPLE3(hp, am_EXIT, from_copy, mess);
hp += 4;
/* the trace token must in this case be updated by the caller */
- seq_trace_output(token, save, SEQ_TRACE_SEND, to->id, NULL);
+ seq_trace_output(token, save, SEQ_TRACE_SEND, to->common.id, NULL);
temptoken = copy_struct(token, sz_token, &hp, &bp->off_heap);
erts_queue_message(to, to_locksp, bp, save, temptoken
#ifdef USE_VM_PROBES
diff --git a/erts/emulator/beam/erl_message.h b/erts/emulator/beam/erl_message.h
index 3e9a24ee81..771eba431f 100644
--- a/erts/emulator/beam/erl_message.h
+++ b/erts/emulator/beam/erl_message.h
@@ -90,7 +90,7 @@ typedef struct {
ErlMessage* first;
ErlMessage** last; /* point to the last next pointer */
ErlMessage** save;
- int len; /* queue length */
+ Sint len; /* queue length */
/*
* The following two fields are used by the recv_mark/1 and
@@ -105,7 +105,7 @@ typedef struct {
typedef struct {
ErlMessage* first;
ErlMessage** last; /* point to the last next pointer */
- int len; /* queue length */
+ Sint len; /* queue length */
} ErlMessageInQueue;
#endif
@@ -125,16 +125,16 @@ typedef struct {
#ifdef ERTS_SMP
/* Move in message queue to end of private message queue */
-#define ERTS_SMP_MSGQ_MV_INQ2PRIVQ(P) \
-do { \
- if ((P)->msg_inq.first) { \
- *(P)->msg.last = (P)->msg_inq.first; \
- (P)->msg.last = (P)->msg_inq.last; \
- (P)->msg.len += (P)->msg_inq.len; \
- (P)->msg_inq.first = NULL; \
- (P)->msg_inq.last = &(P)->msg_inq.first; \
- (P)->msg_inq.len = 0; \
- } \
+#define ERTS_SMP_MSGQ_MV_INQ2PRIVQ(P) \
+do { \
+ if ((P)->msg_inq.first) { \
+ *(P)->msg.last = (P)->msg_inq.first; \
+ (P)->msg.last = (P)->msg_inq.last; \
+ (P)->msg.len += (P)->msg_inq.len; \
+ (P)->msg_inq.first = NULL; \
+ (P)->msg_inq.last = &(P)->msg_inq.first; \
+ (P)->msg_inq.len = 0; \
+ } \
} while (0)
/* Add message last in message queue */
@@ -234,7 +234,7 @@ void erts_queue_message(Process*, ErtsProcLocks*, ErlHeapFragment*, Eterm, Eterm
#endif
);
void erts_deliver_exit_message(Eterm, Process*, ErtsProcLocks *, Eterm, Eterm);
-void erts_send_message(Process*, Process*, ErtsProcLocks*, Eterm, unsigned);
+Sint erts_send_message(Process*, Process*, ErtsProcLocks*, Eterm, unsigned);
void erts_link_mbuf_to_proc(Process *proc, ErlHeapFragment *bp);
void erts_move_msg_mbuf_to_heap(Eterm**, ErlOffHeap*, ErlMessage *);
@@ -245,6 +245,9 @@ void erts_move_msg_attached_data_to_heap(Eterm **, ErlOffHeap *, ErlMessage *);
Eterm erts_msg_distext2heap(Process *, ErtsProcLocks *, ErlHeapFragment **,
Eterm *, ErtsDistExternal *);
+void erts_cleanup_offheap(ErlOffHeap *offheap);
+
+
ERTS_GLB_INLINE Uint erts_msg_used_frag_sz(const ErlMessage *msg);
ERTS_GLB_INLINE Uint erts_msg_attached_data_size(ErlMessage *msg);
diff --git a/erts/emulator/beam/erl_monitors.c b/erts/emulator/beam/erl_monitors.c
index 1a84950120..63175c44d6 100644
--- a/erts/emulator/beam/erl_monitors.c
+++ b/erts/emulator/beam/erl_monitors.c
@@ -971,7 +971,7 @@ Eterm erts_debug_dump_monitors_1(BIF_ALIST_1)
}
} else {
erts_printf("Dumping pid monitors--------------------\n");
- erts_dump_monitors(rp->monitors,0);
+ erts_dump_monitors(ERTS_P_MONITORS(rp),0);
erts_printf("Monitors dumped-------------------------\n");
erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
BIF_RET(am_true);
@@ -985,12 +985,15 @@ Eterm erts_debug_dump_links_1(BIF_ALIST_1)
Process *rp;
DistEntry *dep;
if (is_internal_port(pid)) {
- Port *rport = erts_id2port(pid, p, ERTS_PROC_LOCK_MAIN);
+ Port *rport = erts_id2port_sflgs(pid,
+ p,
+ ERTS_PROC_LOCK_MAIN,
+ ERTS_PORT_SFLGS_INVALID_LOOKUP);
if (rport) {
erts_printf("Dumping port links----------------------\n");
- erts_dump_links(rport->nlinks,0);
+ erts_dump_links(ERTS_P_LINKS(rport), 0);
erts_printf("Links dumped----------------------------\n");
- erts_smp_port_unlock(rport);
+ erts_port_release(rport);
BIF_RET(am_true);
} else {
BIF_ERROR(p,BADARG);
@@ -1014,7 +1017,7 @@ Eterm erts_debug_dump_links_1(BIF_ALIST_1)
} else {
erts_printf("Dumping pid links-----------------------\n");
- erts_dump_links(rp->nlinks,0);
+ erts_dump_links(ERTS_P_LINKS(rp), 0);
erts_printf("Links dumped----------------------------\n");
erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
BIF_RET(am_true);
diff --git a/erts/emulator/beam/erl_monitors.h b/erts/emulator/beam/erl_monitors.h
index d3f6d410dd..a7fa4e0145 100644
--- a/erts/emulator/beam/erl_monitors.h
+++ b/erts/emulator/beam/erl_monitors.h
@@ -137,8 +137,6 @@ typedef struct erts_suspend_monitor {
#define ERTS_LINK_ROOT(Linkp) ((Linkp)->shared.root)
#define ERTS_LINK_REFC(Linkp) ((Linkp)->shared.refc)
-#define ERTS_LINK_ROOT_AS_UINT(Linkp) (*((Uint *) &((Linkp)->root)))
-
Uint erts_tot_link_lh_size(void);
diff --git a/erts/emulator/beam/erl_mtrace.c b/erts/emulator/beam/erl_mtrace.c
index 358c67bf20..5a6fb8589f 100644
--- a/erts/emulator/beam/erl_mtrace.c
+++ b/erts/emulator/beam/erl_mtrace.c
@@ -611,7 +611,7 @@ void erts_mtrace_init(char *receiver, char *nodename)
if (erts_sock_gethostname(hostname, MAXHOSTNAMELEN) != 0)
hostname[0] = '\0';
hostname[MAXHOSTNAMELEN-1] = '\0';
- sys_get_pid(pid);
+ sys_get_pid(pid, sizeof(pid));
write_trace_header(nodename ? nodename : "", pid, hostname);
erts_mtrace_update_heap_size();
}
diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c
index 594c51a5db..fb295c9a8a 100644
--- a/erts/emulator/beam/erl_nif.c
+++ b/erts/emulator/beam/erl_nif.c
@@ -33,6 +33,10 @@
#include "big.h"
#include "beam_bp.h"
#include "erl_thr_progress.h"
+#include "dtrace-wrapper.h"
+#if defined(USE_DYNAMIC_TRACE) && (defined(USE_DTRACE) || defined(USE_SYSTEMTAP))
+#define HAVE_USE_DTRACE 1
+#endif
#include <limits.h>
#include <stddef.h> /* offsetof */
@@ -259,7 +263,7 @@ ErlNifEnv* enif_alloc_env(void)
HEAP_LIMIT(&msg_env->phony_proc) = phony_heap;
HEAP_END(&msg_env->phony_proc) = phony_heap;
MBUF(&msg_env->phony_proc) = NULL;
- msg_env->phony_proc.id = ERTS_INVALID_PID;
+ msg_env->phony_proc.common.id = ERTS_INVALID_PID;
#ifdef FORCE_HEAP_FRAGS
msg_env->phony_proc.space_verified = 0;
msg_env->phony_proc.space_verified_from = NULL;
@@ -283,7 +287,7 @@ void enif_clear_env(ErlNifEnv* env)
struct enif_msg_environment_t* menv = (struct enif_msg_environment_t*)env;
Process* p = &menv->phony_proc;
ASSERT(p == menv->env.proc);
- ASSERT(p->id == ERTS_INVALID_PID);
+ ASSERT(p->common.id == ERTS_INVALID_PID);
ASSERT(MBUF(p) == menv->env.heap_frag);
if (MBUF(p) != NULL) {
erts_cleanup_offheap(&MSO(p));
@@ -315,7 +319,7 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid,
if (env != NULL) {
c_p = env->proc;
- if (receiver == c_p->id) {
+ if (receiver == c_p->common.id) {
rp_locks = ERTS_PROC_LOCK_MAIN;
flush_me = 1;
}
@@ -337,7 +341,7 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid,
: erts_pid2proc_opt(c_p, ERTS_PROC_LOCK_MAIN,
receiver, rp_locks, ERTS_P2P_FLG_SMP_INC_REFC));
if (rp == NULL) {
- ASSERT(env == NULL || receiver != c_p->id);
+ ASSERT(env == NULL || receiver != c_p->common.id);
return 0;
}
flush_env(msg_env);
@@ -393,7 +397,7 @@ static int is_offheap(const ErlOffHeap* oh)
ErlNifPid* enif_self(ErlNifEnv* caller_env, ErlNifPid* pid)
{
- pid->pid = caller_env->proc->id;
+ pid->pid = caller_env->proc->common.id;
return pid;
}
int enif_get_local_pid(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifPid* pid)
@@ -501,7 +505,7 @@ int enif_inspect_iolist_as_binary(ErlNifEnv* env, Eterm term, ErlNifBinary* bin)
{
struct enif_tmp_obj_t* tobj;
ErtsAlcType_t allocator;
- Uint sz;
+ ErlDrvSizeT sz;
if (is_binary(term)) {
return enif_inspect_binary(env,term,bin);
}
@@ -527,7 +531,7 @@ int enif_inspect_iolist_as_binary(ErlNifEnv* env, Eterm term, ErlNifBinary* bin)
bin->size = sz;
bin->bin_term = THE_NON_VALUE;
bin->ref_bin = NULL;
- io_list_to_buf(term, (char*) bin->data, sz);
+ erts_iolist_to_buf(term, (char*) bin->data, sz);
ADD_READONLY_CHECK(env, bin->data, bin->size);
return 1;
}
@@ -739,16 +743,23 @@ int enif_get_atom(ErlNifEnv* env, Eterm atom, char* buf, unsigned len,
{
Atom* ap;
ASSERT(encoding == ERL_NIF_LATIN1);
- if (is_not_atom(atom)) {
+ if (is_not_atom(atom) || len==0) {
return 0;
}
ap = atom_tab(atom_val(atom));
- if (ap->len+1 > len) {
+
+ if (ap->latin1_chars < 0 || ap->latin1_chars >= len) {
return 0;
}
- sys_memcpy(buf, ap->name, ap->len);
- buf[ap->len] = '\0';
- return ap->len + 1;
+ if (ap->latin1_chars == ap->len) {
+ sys_memcpy(buf, ap->name, ap->len);
+ }
+ else {
+ int dlen = erts_utf8_to_latin1((byte*)buf, ap->name, ap->len);
+ ASSERT(dlen == ap->latin1_chars); (void)dlen;
+ }
+ buf[ap->latin1_chars] = '\0';
+ return ap->latin1_chars + 1;
}
int enif_get_int(ErlNifEnv* env, Eterm term, int* ip)
@@ -850,7 +861,10 @@ int enif_get_atom_length(ErlNifEnv* env, Eterm atom, unsigned* len,
ASSERT(enc == ERL_NIF_LATIN1);
if (is_not_atom(atom)) return 0;
ap = atom_tab(atom_val(atom));
- *len = ap->len;
+ if (ap->latin1_chars < 0) {
+ return 0;
+ }
+ *len = ap->latin1_chars;
return 1;
}
@@ -957,7 +971,7 @@ ERL_NIF_TERM enif_make_atom(ErlNifEnv* env, const char* name)
ERL_NIF_TERM enif_make_atom_len(ErlNifEnv* env, const char* name, size_t len)
{
- return am_atom_put(name, len);
+ return erts_atom_put((byte*)name, len, ERTS_ATOM_ENC_LATIN1, 1);
}
int enif_make_existing_atom(ErlNifEnv* env, const char* name, ERL_NIF_TERM* atom,
@@ -970,7 +984,7 @@ int enif_make_existing_atom_len(ErlNifEnv* env, const char* name, size_t len,
ERL_NIF_TERM* atom, ErlNifCharEncoding encoding)
{
ASSERT(encoding == ERL_NIF_LATIN1);
- return erts_atom_get(name, len, atom);
+ return erts_atom_get(name, len, atom, 1);
}
ERL_NIF_TERM enif_make_tuple(ErlNifEnv* env, unsigned cnt, ...)
@@ -1392,6 +1406,44 @@ size_t enif_sizeof_resource(void* obj)
return ERTS_MAGIC_BIN_DATA_SIZE(bin) - offsetof(ErlNifResource,data);
}
+
+void* enif_dlopen(const char* lib,
+ void (*err_handler)(void*,const char*), void* err_arg)
+{
+ ErtsSysDdllError errdesc = ERTS_SYS_DDLL_ERROR_INIT;
+ void* handle;
+ void* init_func;
+ if (erts_sys_ddll_open2(lib, &handle, &errdesc) == ERL_DE_NO_ERROR) {
+ if (erts_sys_ddll_load_nif_init(handle, &init_func, &errdesc) == ERL_DE_NO_ERROR) {
+ erts_sys_ddll_call_nif_init(init_func);
+ }
+ }
+ else {
+ if (err_handler != NULL) {
+ (*err_handler)(err_arg, errdesc.str);
+ }
+ handle = NULL;
+ }
+ erts_sys_ddll_free_error(&errdesc);
+ return handle;
+}
+
+void* enif_dlsym(void* handle, const char* symbol,
+ void (*err_handler)(void*,const char*), void* err_arg)
+{
+ ErtsSysDdllError errdesc = ERTS_SYS_DDLL_ERROR_INIT;
+ void* ret;
+ if (erts_sys_ddll_sym2(handle, symbol, &ret, &errdesc) != ERL_DE_NO_ERROR) {
+ if (err_handler != NULL) {
+ (*err_handler)(err_arg, errdesc.str);
+ }
+ erts_sys_ddll_free_error(&errdesc);
+ return NULL;
+ }
+ return ret;
+}
+
+
/***************************************************************************
** load_nif/2 **
***************************************************************************/
@@ -1591,7 +1643,7 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
"this vm variant (%s).",
entry->vm_variant, ERL_NIF_VM_VARIANT);
}
- else if (!erts_is_atom_str((char*)entry->name, mod_atom)) {
+ else if (!erts_is_atom_str((char*)entry->name, mod_atom, 1)) {
ret = load_nif_error(BIF_P, bad_lib, "Library module name '%s' does not"
" match calling module '%T'", entry->name, mod_atom);
}
@@ -1601,7 +1653,7 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
for (i=0; i < entry->num_of_funcs && ret==am_ok; i++) {
BeamInstr** code_pp;
ErlNifFunc* f = &entry->funcs[i];
- if (!erts_atom_get(f->name, sys_strlen(f->name), &f_atom)
+ if (!erts_atom_get(f->name, sys_strlen(f->name), &f_atom, 1)
|| (code_pp = get_func_pp(mod->curr.code, f_atom, f->arity))==NULL) {
ret = load_nif_error(BIF_P,bad_lib,"Function not found %T:%s/%u",
mod_atom, f->name, f->arity);
@@ -1704,7 +1756,7 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
for (i=0; i < entry->num_of_funcs; i++)
{
BeamInstr* code_ptr;
- erts_atom_get(entry->funcs[i].name, sys_strlen(entry->funcs[i].name), &f_atom);
+ erts_atom_get(entry->funcs[i].name, sys_strlen(entry->funcs[i].name), &f_atom, 1);
code_ptr = *get_func_pp(mod->curr.code, f_atom, entry->funcs[i].arity);
if (code_ptr[1] == 0) {
@@ -1802,7 +1854,7 @@ void erl_nif_init()
#ifdef USE_VM_PROBES
void dtrace_nifenv_str(ErlNifEnv *env, char *process_buf)
{
- dtrace_pid_str(env->proc->id, process_buf);
+ dtrace_pid_str(env->proc->common.id, process_buf);
}
#endif
@@ -1854,3 +1906,1079 @@ static unsigned calc_checksum(unsigned char* ptr, unsigned size)
#endif /* READONLY_CHECK */
+#ifdef HAVE_USE_DTRACE
+
+#define MESSAGE_BUFSIZ 1024
+
+static void get_string_maybe(ErlNifEnv *env, const ERL_NIF_TERM term,
+ char **ptr, char *buf, int bufsiz)
+{
+ ErlNifBinary str_bin;
+
+ if (!enif_inspect_iolist_as_binary(env, term, &str_bin) ||
+ str_bin.size > bufsiz) {
+ *ptr = NULL;
+ } else {
+ memcpy(buf, (char *) str_bin.data, str_bin.size);
+ buf[str_bin.size] = '\0';
+ *ptr = buf;
+ }
+}
+
+ERL_NIF_TERM erl_nif_user_trace_s1(ErlNifEnv* env, int argc,
+ const ERL_NIF_TERM argv[])
+{
+ ErlNifBinary message_bin;
+ DTRACE_CHARBUF(messagebuf, MESSAGE_BUFSIZ + 1);
+
+ if (DTRACE_ENABLED(user_trace_s1)) {
+ if (!enif_inspect_iolist_as_binary(env, argv[0], &message_bin) ||
+ message_bin.size > MESSAGE_BUFSIZ) {
+ return am_badarg;
+ }
+ memcpy(messagebuf, (char *) message_bin.data, message_bin.size);
+ messagebuf[message_bin.size] = '\0';
+ DTRACE1(user_trace_s1, messagebuf);
+ return am_true;
+ } else {
+ return am_false;
+ }
+}
+
+ERL_NIF_TERM erl_nif_user_trace_i4s4(ErlNifEnv* env, int argc,
+ const ERL_NIF_TERM argv[])
+{
+ DTRACE_CHARBUF(procbuf, 32 + 1);
+ DTRACE_CHARBUF(user_tagbuf, MESSAGE_BUFSIZ + 1);
+ char *utbuf = NULL;
+ ErlNifSInt64 i1, i2, i3, i4;
+ DTRACE_CHARBUF(messagebuf1, MESSAGE_BUFSIZ + 1);
+ DTRACE_CHARBUF(messagebuf2, MESSAGE_BUFSIZ + 1);
+ DTRACE_CHARBUF(messagebuf3, MESSAGE_BUFSIZ + 1);
+ DTRACE_CHARBUF(messagebuf4, MESSAGE_BUFSIZ + 1);
+ char *mbuf1 = NULL, *mbuf2 = NULL, *mbuf3 = NULL, *mbuf4 = NULL;
+
+ if (DTRACE_ENABLED(user_trace_i4s4)) {
+ dtrace_nifenv_str(env, procbuf);
+ get_string_maybe(env, argv[0], &utbuf, user_tagbuf, MESSAGE_BUFSIZ);
+ if (! enif_get_int64(env, argv[1], &i1))
+ i1 = 0;
+ if (! enif_get_int64(env, argv[2], &i2))
+ i2 = 0;
+ if (! enif_get_int64(env, argv[3], &i3))
+ i3 = 0;
+ if (! enif_get_int64(env, argv[4], &i4))
+ i4 = 0;
+ get_string_maybe(env, argv[5], &mbuf1, messagebuf1, MESSAGE_BUFSIZ);
+ get_string_maybe(env, argv[6], &mbuf2, messagebuf2, MESSAGE_BUFSIZ);
+ get_string_maybe(env, argv[7], &mbuf3, messagebuf3, MESSAGE_BUFSIZ);
+ get_string_maybe(env, argv[8], &mbuf4, messagebuf4, MESSAGE_BUFSIZ);
+ DTRACE10(user_trace_i4s4, procbuf, utbuf,
+ i1, i2, i3, i4, mbuf1, mbuf2, mbuf3, mbuf4);
+ return am_true;
+ } else {
+ return am_false;
+ }
+}
+
+#define DTRACE10_LABEL(name, label, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9) \
+ erlang_##name##label((a0), (a1), (a2), (a3), (a4), (a5), (a6), (a7), (a8), (a9))
+#define N_STATEMENT(the_label) \
+ case the_label: \
+ if (DTRACE_ENABLED(user_trace_n##the_label)) { \
+ dtrace_nifenv_str(env, procbuf); \
+ get_string_maybe(env, argv[1], &utbuf, user_tagbuf, MESSAGE_BUFSIZ); \
+ if (! enif_get_int64(env, argv[2], &i1)) \
+ i1 = 0; \
+ if (! enif_get_int64(env, argv[3], &i2)) \
+ i2 = 0; \
+ if (! enif_get_int64(env, argv[4], &i3)) \
+ i3 = 0; \
+ if (! enif_get_int64(env, argv[5], &i4)) \
+ i4 = 0; \
+ get_string_maybe(env, argv[6], &mbuf1, messagebuf1, MESSAGE_BUFSIZ); \
+ get_string_maybe(env, argv[7], &mbuf2, messagebuf2, MESSAGE_BUFSIZ); \
+ get_string_maybe(env, argv[8], &mbuf3, messagebuf3, MESSAGE_BUFSIZ); \
+ get_string_maybe(env, argv[9], &mbuf4, messagebuf4, MESSAGE_BUFSIZ); \
+ DTRACE10_LABEL(user_trace_n, the_label, procbuf, utbuf, \
+ i1, i2, i3, i4, mbuf1, mbuf2, mbuf3, mbuf4); \
+ return am_true; \
+ } else { \
+ return am_false; \
+ } \
+ break
+
+ERL_NIF_TERM erl_nif_user_trace_n(ErlNifEnv* env, int argc,
+ const ERL_NIF_TERM argv[])
+{
+ DTRACE_CHARBUF(procbuf, 32 + 1);
+ DTRACE_CHARBUF(user_tagbuf, MESSAGE_BUFSIZ + 1);
+ char *utbuf = NULL;
+ ErlNifSInt64 i1, i2, i3, i4;
+ DTRACE_CHARBUF(messagebuf1, MESSAGE_BUFSIZ + 1);
+ DTRACE_CHARBUF(messagebuf2, MESSAGE_BUFSIZ + 1);
+ DTRACE_CHARBUF(messagebuf3, MESSAGE_BUFSIZ + 1);
+ DTRACE_CHARBUF(messagebuf4, MESSAGE_BUFSIZ + 1);
+ char *mbuf1 = NULL, *mbuf2 = NULL, *mbuf3 = NULL, *mbuf4 = NULL;
+ ErlNifSInt64 label = 0;
+
+ if (! enif_get_int64(env, argv[0], &label) || label < 0 || label > 1023) {
+ return am_badarg;
+ }
+ switch (label) {
+ N_STATEMENT(0);
+ N_STATEMENT(1);
+ N_STATEMENT(2);
+ N_STATEMENT(3);
+ N_STATEMENT(4);
+ N_STATEMENT(5);
+ N_STATEMENT(6);
+ N_STATEMENT(7);
+ N_STATEMENT(8);
+ N_STATEMENT(9);
+ N_STATEMENT(10);
+ N_STATEMENT(11);
+ N_STATEMENT(12);
+ N_STATEMENT(13);
+ N_STATEMENT(14);
+ N_STATEMENT(15);
+ N_STATEMENT(16);
+ N_STATEMENT(17);
+ N_STATEMENT(18);
+ N_STATEMENT(19);
+ N_STATEMENT(20);
+ N_STATEMENT(21);
+ N_STATEMENT(22);
+ N_STATEMENT(23);
+ N_STATEMENT(24);
+ N_STATEMENT(25);
+ N_STATEMENT(26);
+ N_STATEMENT(27);
+ N_STATEMENT(28);
+ N_STATEMENT(29);
+ N_STATEMENT(30);
+ N_STATEMENT(31);
+ N_STATEMENT(32);
+ N_STATEMENT(33);
+ N_STATEMENT(34);
+ N_STATEMENT(35);
+ N_STATEMENT(36);
+ N_STATEMENT(37);
+ N_STATEMENT(38);
+ N_STATEMENT(39);
+ N_STATEMENT(40);
+ N_STATEMENT(41);
+ N_STATEMENT(42);
+ N_STATEMENT(43);
+ N_STATEMENT(44);
+ N_STATEMENT(45);
+ N_STATEMENT(46);
+ N_STATEMENT(47);
+ N_STATEMENT(48);
+ N_STATEMENT(49);
+ N_STATEMENT(50);
+ N_STATEMENT(51);
+ N_STATEMENT(52);
+ N_STATEMENT(53);
+ N_STATEMENT(54);
+ N_STATEMENT(55);
+ N_STATEMENT(56);
+ N_STATEMENT(57);
+ N_STATEMENT(58);
+ N_STATEMENT(59);
+ N_STATEMENT(60);
+ N_STATEMENT(61);
+ N_STATEMENT(62);
+ N_STATEMENT(63);
+ N_STATEMENT(64);
+ N_STATEMENT(65);
+ N_STATEMENT(66);
+ N_STATEMENT(67);
+ N_STATEMENT(68);
+ N_STATEMENT(69);
+ N_STATEMENT(70);
+ N_STATEMENT(71);
+ N_STATEMENT(72);
+ N_STATEMENT(73);
+ N_STATEMENT(74);
+ N_STATEMENT(75);
+ N_STATEMENT(76);
+ N_STATEMENT(77);
+ N_STATEMENT(78);
+ N_STATEMENT(79);
+ N_STATEMENT(80);
+ N_STATEMENT(81);
+ N_STATEMENT(82);
+ N_STATEMENT(83);
+ N_STATEMENT(84);
+ N_STATEMENT(85);
+ N_STATEMENT(86);
+ N_STATEMENT(87);
+ N_STATEMENT(88);
+ N_STATEMENT(89);
+ N_STATEMENT(90);
+ N_STATEMENT(91);
+ N_STATEMENT(92);
+ N_STATEMENT(93);
+ N_STATEMENT(94);
+ N_STATEMENT(95);
+ N_STATEMENT(96);
+ N_STATEMENT(97);
+ N_STATEMENT(98);
+ N_STATEMENT(99);
+ N_STATEMENT(100);
+ N_STATEMENT(101);
+ N_STATEMENT(102);
+ N_STATEMENT(103);
+ N_STATEMENT(104);
+ N_STATEMENT(105);
+ N_STATEMENT(106);
+ N_STATEMENT(107);
+ N_STATEMENT(108);
+ N_STATEMENT(109);
+ N_STATEMENT(110);
+ N_STATEMENT(111);
+ N_STATEMENT(112);
+ N_STATEMENT(113);
+ N_STATEMENT(114);
+ N_STATEMENT(115);
+ N_STATEMENT(116);
+ N_STATEMENT(117);
+ N_STATEMENT(118);
+ N_STATEMENT(119);
+ N_STATEMENT(120);
+ N_STATEMENT(121);
+ N_STATEMENT(122);
+ N_STATEMENT(123);
+ N_STATEMENT(124);
+ N_STATEMENT(125);
+ N_STATEMENT(126);
+ N_STATEMENT(127);
+ N_STATEMENT(128);
+ N_STATEMENT(129);
+ N_STATEMENT(130);
+ N_STATEMENT(131);
+ N_STATEMENT(132);
+ N_STATEMENT(133);
+ N_STATEMENT(134);
+ N_STATEMENT(135);
+ N_STATEMENT(136);
+ N_STATEMENT(137);
+ N_STATEMENT(138);
+ N_STATEMENT(139);
+ N_STATEMENT(140);
+ N_STATEMENT(141);
+ N_STATEMENT(142);
+ N_STATEMENT(143);
+ N_STATEMENT(144);
+ N_STATEMENT(145);
+ N_STATEMENT(146);
+ N_STATEMENT(147);
+ N_STATEMENT(148);
+ N_STATEMENT(149);
+ N_STATEMENT(150);
+ N_STATEMENT(151);
+ N_STATEMENT(152);
+ N_STATEMENT(153);
+ N_STATEMENT(154);
+ N_STATEMENT(155);
+ N_STATEMENT(156);
+ N_STATEMENT(157);
+ N_STATEMENT(158);
+ N_STATEMENT(159);
+ N_STATEMENT(160);
+ N_STATEMENT(161);
+ N_STATEMENT(162);
+ N_STATEMENT(163);
+ N_STATEMENT(164);
+ N_STATEMENT(165);
+ N_STATEMENT(166);
+ N_STATEMENT(167);
+ N_STATEMENT(168);
+ N_STATEMENT(169);
+ N_STATEMENT(170);
+ N_STATEMENT(171);
+ N_STATEMENT(172);
+ N_STATEMENT(173);
+ N_STATEMENT(174);
+ N_STATEMENT(175);
+ N_STATEMENT(176);
+ N_STATEMENT(177);
+ N_STATEMENT(178);
+ N_STATEMENT(179);
+ N_STATEMENT(180);
+ N_STATEMENT(181);
+ N_STATEMENT(182);
+ N_STATEMENT(183);
+ N_STATEMENT(184);
+ N_STATEMENT(185);
+ N_STATEMENT(186);
+ N_STATEMENT(187);
+ N_STATEMENT(188);
+ N_STATEMENT(189);
+ N_STATEMENT(190);
+ N_STATEMENT(191);
+ N_STATEMENT(192);
+ N_STATEMENT(193);
+ N_STATEMENT(194);
+ N_STATEMENT(195);
+ N_STATEMENT(196);
+ N_STATEMENT(197);
+ N_STATEMENT(198);
+ N_STATEMENT(199);
+ N_STATEMENT(200);
+ N_STATEMENT(201);
+ N_STATEMENT(202);
+ N_STATEMENT(203);
+ N_STATEMENT(204);
+ N_STATEMENT(205);
+ N_STATEMENT(206);
+ N_STATEMENT(207);
+ N_STATEMENT(208);
+ N_STATEMENT(209);
+ N_STATEMENT(210);
+ N_STATEMENT(211);
+ N_STATEMENT(212);
+ N_STATEMENT(213);
+ N_STATEMENT(214);
+ N_STATEMENT(215);
+ N_STATEMENT(216);
+ N_STATEMENT(217);
+ N_STATEMENT(218);
+ N_STATEMENT(219);
+ N_STATEMENT(220);
+ N_STATEMENT(221);
+ N_STATEMENT(222);
+ N_STATEMENT(223);
+ N_STATEMENT(224);
+ N_STATEMENT(225);
+ N_STATEMENT(226);
+ N_STATEMENT(227);
+ N_STATEMENT(228);
+ N_STATEMENT(229);
+ N_STATEMENT(230);
+ N_STATEMENT(231);
+ N_STATEMENT(232);
+ N_STATEMENT(233);
+ N_STATEMENT(234);
+ N_STATEMENT(235);
+ N_STATEMENT(236);
+ N_STATEMENT(237);
+ N_STATEMENT(238);
+ N_STATEMENT(239);
+ N_STATEMENT(240);
+ N_STATEMENT(241);
+ N_STATEMENT(242);
+ N_STATEMENT(243);
+ N_STATEMENT(244);
+ N_STATEMENT(245);
+ N_STATEMENT(246);
+ N_STATEMENT(247);
+ N_STATEMENT(248);
+ N_STATEMENT(249);
+ N_STATEMENT(250);
+ N_STATEMENT(251);
+ N_STATEMENT(252);
+ N_STATEMENT(253);
+ N_STATEMENT(254);
+ N_STATEMENT(255);
+ N_STATEMENT(256);
+ N_STATEMENT(257);
+ N_STATEMENT(258);
+ N_STATEMENT(259);
+ N_STATEMENT(260);
+ N_STATEMENT(261);
+ N_STATEMENT(262);
+ N_STATEMENT(263);
+ N_STATEMENT(264);
+ N_STATEMENT(265);
+ N_STATEMENT(266);
+ N_STATEMENT(267);
+ N_STATEMENT(268);
+ N_STATEMENT(269);
+ N_STATEMENT(270);
+ N_STATEMENT(271);
+ N_STATEMENT(272);
+ N_STATEMENT(273);
+ N_STATEMENT(274);
+ N_STATEMENT(275);
+ N_STATEMENT(276);
+ N_STATEMENT(277);
+ N_STATEMENT(278);
+ N_STATEMENT(279);
+ N_STATEMENT(280);
+ N_STATEMENT(281);
+ N_STATEMENT(282);
+ N_STATEMENT(283);
+ N_STATEMENT(284);
+ N_STATEMENT(285);
+ N_STATEMENT(286);
+ N_STATEMENT(287);
+ N_STATEMENT(288);
+ N_STATEMENT(289);
+ N_STATEMENT(290);
+ N_STATEMENT(291);
+ N_STATEMENT(292);
+ N_STATEMENT(293);
+ N_STATEMENT(294);
+ N_STATEMENT(295);
+ N_STATEMENT(296);
+ N_STATEMENT(297);
+ N_STATEMENT(298);
+ N_STATEMENT(299);
+ N_STATEMENT(300);
+ N_STATEMENT(301);
+ N_STATEMENT(302);
+ N_STATEMENT(303);
+ N_STATEMENT(304);
+ N_STATEMENT(305);
+ N_STATEMENT(306);
+ N_STATEMENT(307);
+ N_STATEMENT(308);
+ N_STATEMENT(309);
+ N_STATEMENT(310);
+ N_STATEMENT(311);
+ N_STATEMENT(312);
+ N_STATEMENT(313);
+ N_STATEMENT(314);
+ N_STATEMENT(315);
+ N_STATEMENT(316);
+ N_STATEMENT(317);
+ N_STATEMENT(318);
+ N_STATEMENT(319);
+ N_STATEMENT(320);
+ N_STATEMENT(321);
+ N_STATEMENT(322);
+ N_STATEMENT(323);
+ N_STATEMENT(324);
+ N_STATEMENT(325);
+ N_STATEMENT(326);
+ N_STATEMENT(327);
+ N_STATEMENT(328);
+ N_STATEMENT(329);
+ N_STATEMENT(330);
+ N_STATEMENT(331);
+ N_STATEMENT(332);
+ N_STATEMENT(333);
+ N_STATEMENT(334);
+ N_STATEMENT(335);
+ N_STATEMENT(336);
+ N_STATEMENT(337);
+ N_STATEMENT(338);
+ N_STATEMENT(339);
+ N_STATEMENT(340);
+ N_STATEMENT(341);
+ N_STATEMENT(342);
+ N_STATEMENT(343);
+ N_STATEMENT(344);
+ N_STATEMENT(345);
+ N_STATEMENT(346);
+ N_STATEMENT(347);
+ N_STATEMENT(348);
+ N_STATEMENT(349);
+ N_STATEMENT(350);
+ N_STATEMENT(351);
+ N_STATEMENT(352);
+ N_STATEMENT(353);
+ N_STATEMENT(354);
+ N_STATEMENT(355);
+ N_STATEMENT(356);
+ N_STATEMENT(357);
+ N_STATEMENT(358);
+ N_STATEMENT(359);
+ N_STATEMENT(360);
+ N_STATEMENT(361);
+ N_STATEMENT(362);
+ N_STATEMENT(363);
+ N_STATEMENT(364);
+ N_STATEMENT(365);
+ N_STATEMENT(366);
+ N_STATEMENT(367);
+ N_STATEMENT(368);
+ N_STATEMENT(369);
+ N_STATEMENT(370);
+ N_STATEMENT(371);
+ N_STATEMENT(372);
+ N_STATEMENT(373);
+ N_STATEMENT(374);
+ N_STATEMENT(375);
+ N_STATEMENT(376);
+ N_STATEMENT(377);
+ N_STATEMENT(378);
+ N_STATEMENT(379);
+ N_STATEMENT(380);
+ N_STATEMENT(381);
+ N_STATEMENT(382);
+ N_STATEMENT(383);
+ N_STATEMENT(384);
+ N_STATEMENT(385);
+ N_STATEMENT(386);
+ N_STATEMENT(387);
+ N_STATEMENT(388);
+ N_STATEMENT(389);
+ N_STATEMENT(390);
+ N_STATEMENT(391);
+ N_STATEMENT(392);
+ N_STATEMENT(393);
+ N_STATEMENT(394);
+ N_STATEMENT(395);
+ N_STATEMENT(396);
+ N_STATEMENT(397);
+ N_STATEMENT(398);
+ N_STATEMENT(399);
+ N_STATEMENT(400);
+ N_STATEMENT(401);
+ N_STATEMENT(402);
+ N_STATEMENT(403);
+ N_STATEMENT(404);
+ N_STATEMENT(405);
+ N_STATEMENT(406);
+ N_STATEMENT(407);
+ N_STATEMENT(408);
+ N_STATEMENT(409);
+ N_STATEMENT(410);
+ N_STATEMENT(411);
+ N_STATEMENT(412);
+ N_STATEMENT(413);
+ N_STATEMENT(414);
+ N_STATEMENT(415);
+ N_STATEMENT(416);
+ N_STATEMENT(417);
+ N_STATEMENT(418);
+ N_STATEMENT(419);
+ N_STATEMENT(420);
+ N_STATEMENT(421);
+ N_STATEMENT(422);
+ N_STATEMENT(423);
+ N_STATEMENT(424);
+ N_STATEMENT(425);
+ N_STATEMENT(426);
+ N_STATEMENT(427);
+ N_STATEMENT(428);
+ N_STATEMENT(429);
+ N_STATEMENT(430);
+ N_STATEMENT(431);
+ N_STATEMENT(432);
+ N_STATEMENT(433);
+ N_STATEMENT(434);
+ N_STATEMENT(435);
+ N_STATEMENT(436);
+ N_STATEMENT(437);
+ N_STATEMENT(438);
+ N_STATEMENT(439);
+ N_STATEMENT(440);
+ N_STATEMENT(441);
+ N_STATEMENT(442);
+ N_STATEMENT(443);
+ N_STATEMENT(444);
+ N_STATEMENT(445);
+ N_STATEMENT(446);
+ N_STATEMENT(447);
+ N_STATEMENT(448);
+ N_STATEMENT(449);
+ N_STATEMENT(450);
+ N_STATEMENT(451);
+ N_STATEMENT(452);
+ N_STATEMENT(453);
+ N_STATEMENT(454);
+ N_STATEMENT(455);
+ N_STATEMENT(456);
+ N_STATEMENT(457);
+ N_STATEMENT(458);
+ N_STATEMENT(459);
+ N_STATEMENT(460);
+ N_STATEMENT(461);
+ N_STATEMENT(462);
+ N_STATEMENT(463);
+ N_STATEMENT(464);
+ N_STATEMENT(465);
+ N_STATEMENT(466);
+ N_STATEMENT(467);
+ N_STATEMENT(468);
+ N_STATEMENT(469);
+ N_STATEMENT(470);
+ N_STATEMENT(471);
+ N_STATEMENT(472);
+ N_STATEMENT(473);
+ N_STATEMENT(474);
+ N_STATEMENT(475);
+ N_STATEMENT(476);
+ N_STATEMENT(477);
+ N_STATEMENT(478);
+ N_STATEMENT(479);
+ N_STATEMENT(480);
+ N_STATEMENT(481);
+ N_STATEMENT(482);
+ N_STATEMENT(483);
+ N_STATEMENT(484);
+ N_STATEMENT(485);
+ N_STATEMENT(486);
+ N_STATEMENT(487);
+ N_STATEMENT(488);
+ N_STATEMENT(489);
+ N_STATEMENT(490);
+ N_STATEMENT(491);
+ N_STATEMENT(492);
+ N_STATEMENT(493);
+ N_STATEMENT(494);
+ N_STATEMENT(495);
+ N_STATEMENT(496);
+ N_STATEMENT(497);
+ N_STATEMENT(498);
+ N_STATEMENT(499);
+ N_STATEMENT(500);
+ N_STATEMENT(501);
+ N_STATEMENT(502);
+ N_STATEMENT(503);
+ N_STATEMENT(504);
+ N_STATEMENT(505);
+ N_STATEMENT(506);
+ N_STATEMENT(507);
+ N_STATEMENT(508);
+ N_STATEMENT(509);
+ N_STATEMENT(510);
+ N_STATEMENT(511);
+ N_STATEMENT(512);
+ N_STATEMENT(513);
+ N_STATEMENT(514);
+ N_STATEMENT(515);
+ N_STATEMENT(516);
+ N_STATEMENT(517);
+ N_STATEMENT(518);
+ N_STATEMENT(519);
+ N_STATEMENT(520);
+ N_STATEMENT(521);
+ N_STATEMENT(522);
+ N_STATEMENT(523);
+ N_STATEMENT(524);
+ N_STATEMENT(525);
+ N_STATEMENT(526);
+ N_STATEMENT(527);
+ N_STATEMENT(528);
+ N_STATEMENT(529);
+ N_STATEMENT(530);
+ N_STATEMENT(531);
+ N_STATEMENT(532);
+ N_STATEMENT(533);
+ N_STATEMENT(534);
+ N_STATEMENT(535);
+ N_STATEMENT(536);
+ N_STATEMENT(537);
+ N_STATEMENT(538);
+ N_STATEMENT(539);
+ N_STATEMENT(540);
+ N_STATEMENT(541);
+ N_STATEMENT(542);
+ N_STATEMENT(543);
+ N_STATEMENT(544);
+ N_STATEMENT(545);
+ N_STATEMENT(546);
+ N_STATEMENT(547);
+ N_STATEMENT(548);
+ N_STATEMENT(549);
+ N_STATEMENT(550);
+ N_STATEMENT(551);
+ N_STATEMENT(552);
+ N_STATEMENT(553);
+ N_STATEMENT(554);
+ N_STATEMENT(555);
+ N_STATEMENT(556);
+ N_STATEMENT(557);
+ N_STATEMENT(558);
+ N_STATEMENT(559);
+ N_STATEMENT(560);
+ N_STATEMENT(561);
+ N_STATEMENT(562);
+ N_STATEMENT(563);
+ N_STATEMENT(564);
+ N_STATEMENT(565);
+ N_STATEMENT(566);
+ N_STATEMENT(567);
+ N_STATEMENT(568);
+ N_STATEMENT(569);
+ N_STATEMENT(570);
+ N_STATEMENT(571);
+ N_STATEMENT(572);
+ N_STATEMENT(573);
+ N_STATEMENT(574);
+ N_STATEMENT(575);
+ N_STATEMENT(576);
+ N_STATEMENT(577);
+ N_STATEMENT(578);
+ N_STATEMENT(579);
+ N_STATEMENT(580);
+ N_STATEMENT(581);
+ N_STATEMENT(582);
+ N_STATEMENT(583);
+ N_STATEMENT(584);
+ N_STATEMENT(585);
+ N_STATEMENT(586);
+ N_STATEMENT(587);
+ N_STATEMENT(588);
+ N_STATEMENT(589);
+ N_STATEMENT(590);
+ N_STATEMENT(591);
+ N_STATEMENT(592);
+ N_STATEMENT(593);
+ N_STATEMENT(594);
+ N_STATEMENT(595);
+ N_STATEMENT(596);
+ N_STATEMENT(597);
+ N_STATEMENT(598);
+ N_STATEMENT(599);
+ N_STATEMENT(600);
+ N_STATEMENT(601);
+ N_STATEMENT(602);
+ N_STATEMENT(603);
+ N_STATEMENT(604);
+ N_STATEMENT(605);
+ N_STATEMENT(606);
+ N_STATEMENT(607);
+ N_STATEMENT(608);
+ N_STATEMENT(609);
+ N_STATEMENT(610);
+ N_STATEMENT(611);
+ N_STATEMENT(612);
+ N_STATEMENT(613);
+ N_STATEMENT(614);
+ N_STATEMENT(615);
+ N_STATEMENT(616);
+ N_STATEMENT(617);
+ N_STATEMENT(618);
+ N_STATEMENT(619);
+ N_STATEMENT(620);
+ N_STATEMENT(621);
+ N_STATEMENT(622);
+ N_STATEMENT(623);
+ N_STATEMENT(624);
+ N_STATEMENT(625);
+ N_STATEMENT(626);
+ N_STATEMENT(627);
+ N_STATEMENT(628);
+ N_STATEMENT(629);
+ N_STATEMENT(630);
+ N_STATEMENT(631);
+ N_STATEMENT(632);
+ N_STATEMENT(633);
+ N_STATEMENT(634);
+ N_STATEMENT(635);
+ N_STATEMENT(636);
+ N_STATEMENT(637);
+ N_STATEMENT(638);
+ N_STATEMENT(639);
+ N_STATEMENT(640);
+ N_STATEMENT(641);
+ N_STATEMENT(642);
+ N_STATEMENT(643);
+ N_STATEMENT(644);
+ N_STATEMENT(645);
+ N_STATEMENT(646);
+ N_STATEMENT(647);
+ N_STATEMENT(648);
+ N_STATEMENT(649);
+ N_STATEMENT(650);
+ N_STATEMENT(651);
+ N_STATEMENT(652);
+ N_STATEMENT(653);
+ N_STATEMENT(654);
+ N_STATEMENT(655);
+ N_STATEMENT(656);
+ N_STATEMENT(657);
+ N_STATEMENT(658);
+ N_STATEMENT(659);
+ N_STATEMENT(660);
+ N_STATEMENT(661);
+ N_STATEMENT(662);
+ N_STATEMENT(663);
+ N_STATEMENT(664);
+ N_STATEMENT(665);
+ N_STATEMENT(666);
+ N_STATEMENT(667);
+ N_STATEMENT(668);
+ N_STATEMENT(669);
+ N_STATEMENT(670);
+ N_STATEMENT(671);
+ N_STATEMENT(672);
+ N_STATEMENT(673);
+ N_STATEMENT(674);
+ N_STATEMENT(675);
+ N_STATEMENT(676);
+ N_STATEMENT(677);
+ N_STATEMENT(678);
+ N_STATEMENT(679);
+ N_STATEMENT(680);
+ N_STATEMENT(681);
+ N_STATEMENT(682);
+ N_STATEMENT(683);
+ N_STATEMENT(684);
+ N_STATEMENT(685);
+ N_STATEMENT(686);
+ N_STATEMENT(687);
+ N_STATEMENT(688);
+ N_STATEMENT(689);
+ N_STATEMENT(690);
+ N_STATEMENT(691);
+ N_STATEMENT(692);
+ N_STATEMENT(693);
+ N_STATEMENT(694);
+ N_STATEMENT(695);
+ N_STATEMENT(696);
+ N_STATEMENT(697);
+ N_STATEMENT(698);
+ N_STATEMENT(699);
+ N_STATEMENT(700);
+ N_STATEMENT(701);
+ N_STATEMENT(702);
+ N_STATEMENT(703);
+ N_STATEMENT(704);
+ N_STATEMENT(705);
+ N_STATEMENT(706);
+ N_STATEMENT(707);
+ N_STATEMENT(708);
+ N_STATEMENT(709);
+ N_STATEMENT(710);
+ N_STATEMENT(711);
+ N_STATEMENT(712);
+ N_STATEMENT(713);
+ N_STATEMENT(714);
+ N_STATEMENT(715);
+ N_STATEMENT(716);
+ N_STATEMENT(717);
+ N_STATEMENT(718);
+ N_STATEMENT(719);
+ N_STATEMENT(720);
+ N_STATEMENT(721);
+ N_STATEMENT(722);
+ N_STATEMENT(723);
+ N_STATEMENT(724);
+ N_STATEMENT(725);
+ N_STATEMENT(726);
+ N_STATEMENT(727);
+ N_STATEMENT(728);
+ N_STATEMENT(729);
+ N_STATEMENT(730);
+ N_STATEMENT(731);
+ N_STATEMENT(732);
+ N_STATEMENT(733);
+ N_STATEMENT(734);
+ N_STATEMENT(735);
+ N_STATEMENT(736);
+ N_STATEMENT(737);
+ N_STATEMENT(738);
+ N_STATEMENT(739);
+ N_STATEMENT(740);
+ N_STATEMENT(741);
+ N_STATEMENT(742);
+ N_STATEMENT(743);
+ N_STATEMENT(744);
+ N_STATEMENT(745);
+ N_STATEMENT(746);
+ N_STATEMENT(747);
+ N_STATEMENT(748);
+ N_STATEMENT(749);
+ N_STATEMENT(750);
+ N_STATEMENT(751);
+ N_STATEMENT(752);
+ N_STATEMENT(753);
+ N_STATEMENT(754);
+ N_STATEMENT(755);
+ N_STATEMENT(756);
+ N_STATEMENT(757);
+ N_STATEMENT(758);
+ N_STATEMENT(759);
+ N_STATEMENT(760);
+ N_STATEMENT(761);
+ N_STATEMENT(762);
+ N_STATEMENT(763);
+ N_STATEMENT(764);
+ N_STATEMENT(765);
+ N_STATEMENT(766);
+ N_STATEMENT(767);
+ N_STATEMENT(768);
+ N_STATEMENT(769);
+ N_STATEMENT(770);
+ N_STATEMENT(771);
+ N_STATEMENT(772);
+ N_STATEMENT(773);
+ N_STATEMENT(774);
+ N_STATEMENT(775);
+ N_STATEMENT(776);
+ N_STATEMENT(777);
+ N_STATEMENT(778);
+ N_STATEMENT(779);
+ N_STATEMENT(780);
+ N_STATEMENT(781);
+ N_STATEMENT(782);
+ N_STATEMENT(783);
+ N_STATEMENT(784);
+ N_STATEMENT(785);
+ N_STATEMENT(786);
+ N_STATEMENT(787);
+ N_STATEMENT(788);
+ N_STATEMENT(789);
+ N_STATEMENT(790);
+ N_STATEMENT(791);
+ N_STATEMENT(792);
+ N_STATEMENT(793);
+ N_STATEMENT(794);
+ N_STATEMENT(795);
+ N_STATEMENT(796);
+ N_STATEMENT(797);
+ N_STATEMENT(798);
+ N_STATEMENT(799);
+ N_STATEMENT(800);
+ N_STATEMENT(801);
+ N_STATEMENT(802);
+ N_STATEMENT(803);
+ N_STATEMENT(804);
+ N_STATEMENT(805);
+ N_STATEMENT(806);
+ N_STATEMENT(807);
+ N_STATEMENT(808);
+ N_STATEMENT(809);
+ N_STATEMENT(810);
+ N_STATEMENT(811);
+ N_STATEMENT(812);
+ N_STATEMENT(813);
+ N_STATEMENT(814);
+ N_STATEMENT(815);
+ N_STATEMENT(816);
+ N_STATEMENT(817);
+ N_STATEMENT(818);
+ N_STATEMENT(819);
+ N_STATEMENT(820);
+ N_STATEMENT(821);
+ N_STATEMENT(822);
+ N_STATEMENT(823);
+ N_STATEMENT(824);
+ N_STATEMENT(825);
+ N_STATEMENT(826);
+ N_STATEMENT(827);
+ N_STATEMENT(828);
+ N_STATEMENT(829);
+ N_STATEMENT(830);
+ N_STATEMENT(831);
+ N_STATEMENT(832);
+ N_STATEMENT(833);
+ N_STATEMENT(834);
+ N_STATEMENT(835);
+ N_STATEMENT(836);
+ N_STATEMENT(837);
+ N_STATEMENT(838);
+ N_STATEMENT(839);
+ N_STATEMENT(840);
+ N_STATEMENT(841);
+ N_STATEMENT(842);
+ N_STATEMENT(843);
+ N_STATEMENT(844);
+ N_STATEMENT(845);
+ N_STATEMENT(846);
+ N_STATEMENT(847);
+ N_STATEMENT(848);
+ N_STATEMENT(849);
+ N_STATEMENT(850);
+ N_STATEMENT(851);
+ N_STATEMENT(852);
+ N_STATEMENT(853);
+ N_STATEMENT(854);
+ N_STATEMENT(855);
+ N_STATEMENT(856);
+ N_STATEMENT(857);
+ N_STATEMENT(858);
+ N_STATEMENT(859);
+ N_STATEMENT(860);
+ N_STATEMENT(861);
+ N_STATEMENT(862);
+ N_STATEMENT(863);
+ N_STATEMENT(864);
+ N_STATEMENT(865);
+ N_STATEMENT(866);
+ N_STATEMENT(867);
+ N_STATEMENT(868);
+ N_STATEMENT(869);
+ N_STATEMENT(870);
+ N_STATEMENT(871);
+ N_STATEMENT(872);
+ N_STATEMENT(873);
+ N_STATEMENT(874);
+ N_STATEMENT(875);
+ N_STATEMENT(876);
+ N_STATEMENT(877);
+ N_STATEMENT(878);
+ N_STATEMENT(879);
+ N_STATEMENT(880);
+ N_STATEMENT(881);
+ N_STATEMENT(882);
+ N_STATEMENT(883);
+ N_STATEMENT(884);
+ N_STATEMENT(885);
+ N_STATEMENT(886);
+ N_STATEMENT(887);
+ N_STATEMENT(888);
+ N_STATEMENT(889);
+ N_STATEMENT(890);
+ N_STATEMENT(891);
+ N_STATEMENT(892);
+ N_STATEMENT(893);
+ N_STATEMENT(894);
+ N_STATEMENT(895);
+ N_STATEMENT(896);
+ N_STATEMENT(897);
+ N_STATEMENT(898);
+ N_STATEMENT(899);
+ N_STATEMENT(900);
+ N_STATEMENT(901);
+ N_STATEMENT(902);
+ N_STATEMENT(903);
+ N_STATEMENT(904);
+ N_STATEMENT(905);
+ N_STATEMENT(906);
+ N_STATEMENT(907);
+ N_STATEMENT(908);
+ N_STATEMENT(909);
+ N_STATEMENT(910);
+ N_STATEMENT(911);
+ N_STATEMENT(912);
+ N_STATEMENT(913);
+ N_STATEMENT(914);
+ N_STATEMENT(915);
+ N_STATEMENT(916);
+ N_STATEMENT(917);
+ N_STATEMENT(918);
+ N_STATEMENT(919);
+ N_STATEMENT(920);
+ N_STATEMENT(921);
+ N_STATEMENT(922);
+ N_STATEMENT(923);
+ N_STATEMENT(924);
+ N_STATEMENT(925);
+ N_STATEMENT(926);
+ N_STATEMENT(927);
+ N_STATEMENT(928);
+ N_STATEMENT(929);
+ N_STATEMENT(930);
+ N_STATEMENT(931);
+ N_STATEMENT(932);
+ N_STATEMENT(933);
+ N_STATEMENT(934);
+ N_STATEMENT(935);
+ N_STATEMENT(936);
+ N_STATEMENT(937);
+ N_STATEMENT(938);
+ N_STATEMENT(939);
+ N_STATEMENT(940);
+ N_STATEMENT(941);
+ N_STATEMENT(942);
+ N_STATEMENT(943);
+ N_STATEMENT(944);
+ N_STATEMENT(945);
+ N_STATEMENT(946);
+ N_STATEMENT(947);
+ N_STATEMENT(948);
+ N_STATEMENT(949);
+ N_STATEMENT(950);
+ }
+ return am_error; /* NOTREACHED, shut up the compiler */
+}
+
+#endif /* HAVE_USE_DTRACE */
diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h
index e5d99dc4f1..93e56332e1 100644
--- a/erts/emulator/beam/erl_nif.h
+++ b/erts/emulator/beam/erl_nif.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2009-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2009-2012. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -187,11 +187,7 @@ extern TWinDynNifCallbacks WinDynNifCallbacks;
#else
# define ERL_NIF_INIT_GLOB
# define ERL_NIF_INIT_BODY
-# if defined(VXWORKS)
-# define ERL_NIF_INIT_DECL(MODNAME) ErlNifEntry* MODNAME ## _init(void)
-# else
-# define ERL_NIF_INIT_DECL(MODNAME) ErlNifEntry* nif_init(void)
-# endif
+# define ERL_NIF_INIT_DECL(MODNAME) ErlNifEntry* nif_init(void)
#endif
@@ -226,6 +222,15 @@ ERL_NIF_INIT_DECL(NAME) \
} \
ERL_NIF_INIT_EPILOGUE
+#if defined(USE_DYNAMIC_TRACE) && (defined(USE_DTRACE) || defined(USE_SYSTEMTAP))
+#define HAVE_USE_DTRACE 1
+#endif
+
+#ifdef HAVE_USE_DTRACE
+ERL_NIF_TERM erl_nif_user_trace_s1(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+ERL_NIF_TERM erl_nif_user_trace_i4s4(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+ERL_NIF_TERM erl_nif_user_trace_n(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+#endif
#endif /* __ERL_NIF_H__ */
diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h
index 6396af09d0..51ff1eaa48 100644
--- a/erts/emulator/beam/erl_nif_api_funcs.h
+++ b/erts/emulator/beam/erl_nif_api_funcs.h
@@ -138,6 +138,8 @@ ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM,enif_make_uint64,(ErlNifEnv*, ErlNifUInt64));
ERL_NIF_API_FUNC_DECL(int,enif_is_exception,(ErlNifEnv*, ERL_NIF_TERM term));
ERL_NIF_API_FUNC_DECL(int,enif_make_reverse_list,(ErlNifEnv*, ERL_NIF_TERM term, ERL_NIF_TERM *list));
ERL_NIF_API_FUNC_DECL(int,enif_is_number,(ErlNifEnv*, ERL_NIF_TERM term));
+ERL_NIF_API_FUNC_DECL(void*,enif_dlopen,(const char* lib, void (*err_handler)(void*,const char*), void* err_arg));
+ERL_NIF_API_FUNC_DECL(void*,enif_dlsym,(void* handle, const char* symbol, void (*err_handler)(void*,const char*), void* err_arg));
/*
** Add new entries here to keep compatibility on Windows!!!
@@ -260,6 +262,8 @@ ERL_NIF_API_FUNC_DECL(int,enif_is_number,(ErlNifEnv*, ERL_NIF_TERM term));
# define enif_is_exception ERL_NIF_API_FUNC_MACRO(enif_is_exception)
# define enif_make_reverse_list ERL_NIF_API_FUNC_MACRO(enif_make_reverse_list)
# define enif_is_number ERL_NIF_API_FUNC_MACRO(enif_is_number)
+# define enif_dlopen ERL_NIF_API_FUNC_MACRO(enif_dlopen)
+# define enif_dlsym ERL_NIF_API_FUNC_MACRO(enif_dlsym)
/*
** Add new entries here
diff --git a/erts/emulator/beam/erl_node_container_utils.h b/erts/emulator/beam/erl_node_container_utils.h
index 7b4cb7b042..667bda255b 100644
--- a/erts/emulator/beam/erl_node_container_utils.h
+++ b/erts/emulator/beam/erl_node_container_utils.h
@@ -20,7 +20,7 @@
#ifndef ERL_NODE_CONTAINER_UTILS_H__
#define ERL_NODE_CONTAINER_UTILS_H__
-#include "erl_term.h"
+#include "erl_ptab.h"
/*
* Note regarding node containers:
@@ -29,9 +29,6 @@
* the emulator) for the Erlang data types that contain a reference
* to a node, i.e. pids, ports, and references.
*
- * Observe! The layouts of the node container data types have been
- * changed in R9.
- *
* Node containers are divided into internal and external node containers.
* An internal node container refer to the current incarnation of the
* node which it reside on. An external node container refer to
@@ -52,13 +49,6 @@
* reference is a boxed data type. An internal node container have an
* implicit reference to the 'erts_this_node' element in the node table.
*
- * Due to the R9 changes in layouts of node containers there are room to
- * store more data than previously. Today (R9) this extra space is unused,
- * but it is planned to be used in the future. For example only 18 bits
- * are used for data in a pid but there is room for 28 bits of data (on a
- * 32-bit machine). Some preparations have been made in the emulator for
- * usage of this extra space.
- *
* OBSERVE! Pids doesn't use fixed size 'serial' and 'number' fields any
* more. Previously the 15 bit 'number' field of a pid was used as index
* into the process table, and the 3 bit 'serial' field was used as a
@@ -104,8 +94,6 @@
#define internal_dist_entry(x) (erts_this_node->dist_entry)
#define external_dist_entry(x) (external_node((x))->dist_entry)
-extern int erts_use_r9_pids_ports;
-
/*
* For this node (and previous incarnations of this node), 0 is used as
* channel no. For other nodes, the atom index of the atom corresponding
@@ -128,47 +116,20 @@ extern int erts_use_r9_pids_ports;
* Pids *
\* */
-#define erts_max_processes erts_proc.max
-
-typedef struct {
- erts_smp_atomic_t *tab;
- int max;
- int tab_cache_lines;
- int pix_per_cache_line;
- int pix_cl_mask;
- int pix_cl_shift;
- int pix_cli_mask;
- int pix_cli_shift;
-} ErtsProcTab;
-
-extern ErtsProcTab erts_proc;
-
-ERTS_GLB_INLINE int erts_pid_data2ix(Eterm pid_data);
-
-#if ERTS_GLB_INLINE_INCL_FUNC_DEF
-
-ERTS_GLB_INLINE int erts_pid_data2ix(Eterm pid_data)
-{
- int n, pix;
-
- n = (int) pid_data;
- if (erts_proc.pix_cl_mask) {
- pix = ((n & erts_proc.pix_cl_mask) << erts_proc.pix_cl_shift);
- pix += ((n >> erts_proc.pix_cli_shift) & erts_proc.pix_cli_mask);
- }
- else {
- n %= erts_proc.max;
- pix = n % erts_proc.tab_cache_lines;
- pix *= erts_proc.pix_per_cache_line;
- pix += n / erts_proc.tab_cache_lines;
- }
- ASSERT(0 <= pix && pix < erts_proc.max);
- return pix;
-}
+extern ErtsPTab erts_proc;
-#endif
+#define make_internal_pid(D) erts_ptab_make_id(&erts_proc, \
+ (D), \
+ _TAG_IMMED1_PID)
-#define internal_pid_index(x) erts_pid_data2ix(internal_pid_data((x)))
+#define internal_pid_index(PID) (ASSERT_EXPR(is_internal_pid((PID))), \
+ erts_ptab_id2pix(&erts_proc, (PID)))
+
+#define internal_pid_data(PID) (ASSERT_EXPR(is_internal_pid((PID))), \
+ erts_ptab_id2data(&erts_proc, (PID)))
+
+#define internal_pid_number(x) _GET_PID_NUM(internal_pid_data((x)))
+#define internal_pid_serial(x) _GET_PID_SER(internal_pid_data((x)))
#define internal_pid_node_name(x) (internal_pid_node((x))->sysname)
#define external_pid_node_name(x) (external_pid_node((x))->sysname)
@@ -208,34 +169,37 @@ ERTS_GLB_INLINE int erts_pid_data2ix(Eterm pid_data)
|| is_external_pid((x)))
#define is_not_pid(x) (!is_pid(x))
-#define ERTS_MAX_R9_PROCESSES (1 << ERTS_R9_PROC_BITS)
-
/*
* Maximum number of processes. We want the number to fit in a SMALL on
* 32-bit CPU.
*/
-#define ERTS_MAX_PROCESSES ((SWORD_CONSTANT(1) << 27)-1)
-#if (ERTS_MAX_PROCESSES > MAX_SMALL)
-# error "The maximum number of processes must fit in a SMALL."
-#endif
-
+#define ERTS_MAX_PROCESSES (ERTS_PTAB_MAX_SIZE-1)
#define ERTS_MAX_PID_DATA ((1 << _PID_DATA_SIZE) - 1)
#define ERTS_MAX_PID_NUMBER ((1 << _PID_NUM_SIZE) - 1)
#define ERTS_MAX_PID_SERIAL ((1 << _PID_SER_SIZE) - 1)
-#define ERTS_MAX_PID_R9_SERIAL ((1 << _PID_R9_SER_SIZE) - 1)
-#define ERTS_R9_PROC_BITS (_PID_R9_SER_SIZE + _PID_NUM_SIZE)
#define ERTS_PROC_BITS (_PID_SER_SIZE + _PID_NUM_SIZE)
-#define ERTS_INVALID_PID make_internal_pid(ERTS_MAX_PID_DATA)
+#define ERTS_INVALID_PID ERTS_PTAB_INVALID_ID(_TAG_IMMED1_PID)
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
* Ports *
\* */
-#define internal_port_index(x) (internal_port_data((x)) \
- & erts_port_tab_index_mask)
+extern ErtsPTab erts_port;
+
+#define make_internal_port(D) erts_ptab_make_id(&erts_port, \
+ (D), \
+ _TAG_IMMED1_PORT)
+
+#define internal_port_index(PRT) (ASSERT_EXPR(is_internal_port((PRT))), \
+ erts_ptab_id2pix(&erts_port, (PRT)))
+
+#define internal_port_data(PRT) (ASSERT_EXPR(is_internal_port((PRT))), \
+ erts_ptab_id2data(&erts_port, (PRT)))
+
+#define internal_port_number(x) _GET_PORT_NUM(internal_port_data((x)))
#define internal_port_node_name(x) (internal_port_node((x))->sysname)
#define external_port_node_name(x) (external_port_node((x))->sysname)
@@ -274,18 +238,18 @@ ERTS_GLB_INLINE int erts_pid_data2ix(Eterm pid_data)
#define is_not_port(x) (!is_port(x))
/* Highest port-ID part in a term of type Port
- Not necessarily the same as the variable erts_max_ports
+ Not necessarily the same as current maximum port table size
which defines the maximum number of simultaneous Ports
in the Erlang node. ERTS_MAX_PORTS is a hard upper limit.
*/
-#define ERTS_MAX_R9_PORTS (1 << ERTS_R9_PORTS_BITS)
-#define ERTS_MAX_PORTS (1 << ERTS_PORTS_BITS)
-
+#define ERTS_MAX_PORTS (ERTS_PTAB_MAX_SIZE-1)
#define ERTS_MAX_PORT_DATA ((1 << _PORT_DATA_SIZE) - 1)
#define ERTS_MAX_PORT_NUMBER ((1 << _PORT_NUM_SIZE) - 1)
-#define ERTS_R9_PORTS_BITS (_PORT_R9_NUM_SIZE)
#define ERTS_PORTS_BITS (_PORT_NUM_SIZE)
+
+#define ERTS_INVALID_PORT ERTS_PTAB_INVALID_ID(_TAG_IMMED1_PORT)
+
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
* Refs *
\* */
diff --git a/erts/emulator/beam/erl_node_tables.c b/erts/emulator/beam/erl_node_tables.c
index 649be3f454..ebfba065d1 100644
--- a/erts/emulator/beam/erl_node_tables.c
+++ b/erts/emulator/beam/erl_node_tables.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2012. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -116,8 +116,7 @@ dist_table_alloc(void *dep_tmpl)
dep->qsize = 0;
dep->out_queue.first = NULL;
dep->out_queue.last = NULL;
- dep->suspended.first = NULL;
- dep->suspended.last = NULL;
+ dep->suspended = NULL;
dep->finalized_out_queue.first = NULL;
dep->finalized_out_queue.last = NULL;
@@ -769,8 +768,7 @@ void erts_init_node_tables(void)
erts_this_dist_entry->qsize = 0;
erts_this_dist_entry->out_queue.first = NULL;
erts_this_dist_entry->out_queue.last = NULL;
- erts_this_dist_entry->suspended.first = NULL;
- erts_this_dist_entry->suspended.last = NULL;
+ erts_this_dist_entry->suspended = NULL;
erts_this_dist_entry->finalized_out_queue.first = NULL;
erts_this_dist_entry->finalized_out_queue.last = NULL;
@@ -1268,7 +1266,7 @@ setup_reference_table(void)
ErlHeapFragment *hfp;
DistEntry *dep;
HashInfo hi;
- int i;
+ int i, max;
DeclareTmpHeapNoproc(heap,3);
inserted_bins = NULL;
@@ -1297,8 +1295,9 @@ setup_reference_table(void)
UnUseTmpHeapNoproc(3);
+ max = erts_ptab_max(&erts_proc);
/* Insert all processes */
- for (i = 0; i < erts_max_processes; i++) {
+ for (i = 0; i < max; i++) {
Process *proc = erts_pix2proc(i);
if (proc) {
ErlMessage *msg;
@@ -1306,12 +1305,12 @@ setup_reference_table(void)
/* Insert Heap */
insert_offheap(&(proc->off_heap),
HEAP_REF,
- proc->id);
+ proc->common.id);
/* Insert message buffers */
for(hfp = proc->mbuf; hfp; hfp = hfp->next)
insert_offheap(&(hfp->off_heap),
HEAP_REF,
- proc->id);
+ proc->common.id);
/* Insert msg msg buffers */
for (msg = proc->msg.first; msg; msg = msg->next) {
ErlHeapFragment *heap_frag = NULL;
@@ -1321,7 +1320,7 @@ setup_reference_table(void)
else {
if (msg->data.dist_ext->dep)
insert_dist_entry(msg->data.dist_ext->dep,
- HEAP_REF, proc->id, 0);
+ HEAP_REF, proc->common.id, 0);
if (is_not_nil(ERL_MESSAGE_TOKEN(msg)))
heap_frag = erts_dist_ext_trailer(msg->data.dist_ext);
}
@@ -1329,7 +1328,7 @@ setup_reference_table(void)
if (heap_frag)
insert_offheap(&(heap_frag->off_heap),
HEAP_REF,
- proc->id);
+ proc->common.id);
}
#ifdef ERTS_SMP
for (msg = proc->msg_inq.first; msg; msg = msg->next) {
@@ -1340,7 +1339,7 @@ setup_reference_table(void)
else {
if (msg->data.dist_ext->dep)
insert_dist_entry(msg->data.dist_ext->dep,
- HEAP_REF, proc->id, 0);
+ HEAP_REF, proc->common.id, 0);
if (is_not_nil(ERL_MESSAGE_TOKEN(msg)))
heap_frag = erts_dist_ext_trailer(msg->data.dist_ext);
}
@@ -1348,19 +1347,19 @@ setup_reference_table(void)
if (heap_frag)
insert_offheap(&(heap_frag->off_heap),
HEAP_REF,
- proc->id);
+ proc->common.id);
}
#endif
/* Insert links */
- if(proc->nlinks)
- insert_links(proc->nlinks, proc->id);
- if(proc->monitors)
- insert_monitors(proc->monitors, proc->id);
+ if (ERTS_P_LINKS(proc))
+ insert_links(ERTS_P_LINKS(proc), proc->common.id);
+ if (ERTS_P_MONITORS(proc))
+ insert_monitors(ERTS_P_MONITORS(proc), proc->common.id);
/* Insert controller */
{
DistEntry *dep = ERTS_PROC_GET_DIST_ENTRY(proc);
if (dep)
- insert_dist_entry(dep, CTRL_REF, proc->id, 0);
+ insert_dist_entry(dep, CTRL_REF, proc->common.id, 0);
}
}
}
@@ -1370,21 +1369,33 @@ setup_reference_table(void)
#endif
/* Insert all ports */
- for (i = 0; i < erts_max_ports; i++) {
- if (erts_port[i].status & ERTS_PORT_SFLGS_DEAD)
+ max = erts_ptab_max(&erts_port);
+ for (i = 0; i < max; i++) {
+ erts_aint32_t state;
+ Port *prt;
+
+ prt = erts_pix2port(i);
+ if (!prt)
+ continue;
+
+ state = erts_atomic32_read_nob(&prt->state);
+ if (state & ERTS_PORT_SFLGS_DEAD)
continue;
/* Insert links */
- if(erts_port[i].nlinks)
- insert_links(erts_port[i].nlinks, erts_port[i].id);
+ if (ERTS_P_LINKS(prt))
+ insert_links(ERTS_P_LINKS(prt), prt->common.id);
+ /* Insert monitors */
+ if (ERTS_P_MONITORS(prt))
+ insert_monitors(ERTS_P_MONITORS(prt), prt->common.id);
/* Insert port data */
- for(hfp = erts_port[i].bp; hfp; hfp = hfp->next)
- insert_offheap(&(hfp->off_heap), HEAP_REF, erts_port[i].id);
+ for(hfp = prt->bp; hfp; hfp = hfp->next)
+ insert_offheap(&(hfp->off_heap), HEAP_REF, prt->common.id);
/* Insert controller */
- if (erts_port[i].dist_entry)
- insert_dist_entry(erts_port[i].dist_entry,
+ if (prt->dist_entry)
+ insert_dist_entry(prt->dist_entry,
CTRL_REF,
- erts_port[i].id,
+ prt->common.id,
0);
}
diff --git a/erts/emulator/beam/erl_node_tables.h b/erts/emulator/beam/erl_node_tables.h
index 4a015bdef9..af60071ea5 100644
--- a/erts/emulator/beam/erl_node_tables.h
+++ b/erts/emulator/beam/erl_node_tables.h
@@ -84,10 +84,6 @@ typedef struct {
} ErtsDistOutputQueue;
struct ErtsProcList_;
-typedef struct {
- struct ErtsProcList_ *first;
- struct ErtsProcList_ *last;
-} ErtsDistSuspended;
/*
* Lock order:
@@ -100,7 +96,6 @@ typedef struct {
*/
struct erl_link;
-struct port;
typedef struct dist_entry_ {
HashBucket hash_bucket; /* Hash bucket */
@@ -135,13 +130,13 @@ typedef struct dist_entry_ {
Uint32 qflgs;
Sint qsize;
ErtsDistOutputQueue out_queue;
- ErtsDistSuspended suspended;
+ struct ErtsProcList_ *suspended;
ErtsDistOutputQueue finalized_out_queue;
erts_smp_atomic_t dist_cmd_scheduled;
ErtsPortTaskHandle dist_cmd;
- Uint (*send)(struct port *prt, ErtsDistOutputBuf *obuf);
+ Uint (*send)(Port *prt, ErtsDistOutputBuf *obuf);
struct cache* cache; /* The atom cache */
} DistEntry;
diff --git a/erts/emulator/beam/erl_port.h b/erts/emulator/beam/erl_port.h
new file mode 100644
index 0000000000..65b4cd0bfe
--- /dev/null
+++ b/erts/emulator/beam/erl_port.h
@@ -0,0 +1,943 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2012. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, 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 ERL_PORT_TYPE__
+#define ERL_PORT_TYPE__
+typedef struct _erl_drv_port Port;
+typedef struct ErtsProc2PortSigData_ ErtsProc2PortSigData;
+#endif
+
+#if !defined(ERL_PORT_H__) && !defined(ERL_PORT_GET_PORT_TYPE_ONLY__)
+#define ERL_PORT_H__
+
+#include "erl_port_task.h"
+#include "erl_ptab.h"
+#include "erl_thr_progress.h"
+#include "erl_trace.h"
+
+#define ERTS_DEFAULT_MAX_PORTS (1 << 16)
+#define ERTS_MIN_PORTS 1024
+
+extern int erts_port_synchronous_ops;
+extern int erts_port_schedule_all_ops;
+extern int erts_port_parallelism;
+
+typedef struct erts_driver_t_ erts_driver_t;
+
+#define ERTS_INVALID_ERL_DRV_PORT ((ErlDrvPort) (SWord) -1)
+#define SMALL_IO_QUEUE 5 /* Number of fixed elements */
+
+typedef struct {
+ ErlDrvSizeT size; /* total size in bytes */
+
+ SysIOVec* v_start;
+ SysIOVec* v_end;
+ SysIOVec* v_head;
+ SysIOVec* v_tail;
+ SysIOVec v_small[SMALL_IO_QUEUE];
+
+ ErlDrvBinary** b_start;
+ ErlDrvBinary** b_end;
+ ErlDrvBinary** b_head;
+ ErlDrvBinary** b_tail;
+ ErlDrvBinary* b_small[SMALL_IO_QUEUE];
+} ErlIOQueue;
+
+typedef struct line_buf { /* Buffer used in line oriented I/O */
+ ErlDrvSizeT bufsiz; /* Size of character buffer */
+ ErlDrvSizeT ovlen; /* Length of overflow data */
+ ErlDrvSizeT ovsiz; /* Actual size of overflow buffer */
+ char data[1]; /* Starting point of buffer data,
+ data[0] is a flag indicating an unprocess CR,
+ The rest is the overflow buffer. */
+} LineBuf;
+
+/*
+ * Items part of erlang:port_info/1 result. Note am_registered_name
+ * *need* to be first.
+ */
+
+#define ERTS_PORT_INFO_1_ITEMS \
+ { am_registered_name, /* Needs to be first */ \
+ am_name, \
+ am_links, \
+ am_id, \
+ am_connected, \
+ am_input, \
+ am_output, \
+ am_os_pid }
+
+/*
+ * Port Specific Data.
+ *
+ * Only use PrtSD for very rarely used data.
+ */
+
+#define ERTS_PRTSD_SCHED_ID 0
+
+#define ERTS_PRTSD_SIZE 1
+
+typedef struct {
+ void *data[ERTS_PRTSD_SIZE];
+} ErtsPrtSD;
+
+#ifdef ERTS_SMP
+typedef struct ErtsXPortsList_ ErtsXPortsList;
+#endif
+
+/*
+ * Port locking:
+ *
+ * Locking is done either driver specific or port specific. When
+ * driver specific locking is used, all instances of the driver,
+ * i.e. ports running the driver, share the same lock. When port
+ * specific locking is used each instance have its own lock.
+ *
+ * Most fields in the Port structure are protected by the lock
+ * referred to by the 'lock' field. This lock is shared between
+ * all ports running the same driver when driver specific locking
+ * is used.
+ *
+ * The 'sched' field is protected by the run queue lock that the
+ * port currently is assigned to.
+ *
+ */
+
+struct _erl_drv_port {
+ ErtsPTabElementCommon common; /* *Need* to be first in struct */
+
+ ErtsPortTaskSched sched;
+ ErtsPortTaskHandle timeout_task;
+#ifdef ERTS_SMP
+ erts_mtx_t *lock;
+ ErtsXPortsList *xports;
+ erts_smp_atomic_t run_queue;
+#else
+ erts_atomic32_t refc;
+ int cleanup;
+#endif
+ erts_atomic_t connected; /* A connected process */
+ Eterm caller; /* Current caller. */
+ Eterm data; /* Data associated with port. */
+ ErlHeapFragment* bp; /* Heap fragment holding data (NULL if imm data). */
+ Uint bytes_in; /* Number of bytes read */
+ Uint bytes_out; /* Number of bytes written */
+
+ ErlIOQueue ioq; /* driver accessible i/o queue */
+ DistEntry *dist_entry; /* Dist entry used in DISTRIBUTION */
+ char *name; /* String used in the open */
+ erts_driver_t* drv_ptr;
+ UWord drv_data;
+ SWord os_pid; /* Child process ID */
+ ErtsProcList *suspended; /* List of suspended processes. */
+ LineBuf *linebuf; /* Buffer to hold data not ready for
+ process to get (line oriented I/O)*/
+ erts_atomic32_t state; /* Status and type flags */
+ int control_flags; /* Flags for port_control() */
+ ErlDrvPDL port_data_lock;
+
+ ErtsPrtSD *psd; /* Port specific data */
+};
+
+#define ERTS_PORT_GET_CONNECTED(PRT) \
+ ((Eterm) erts_atomic_read_nob(&(PRT)->connected))
+#define ERTS_PORT_SET_CONNECTED(PRT, PID) \
+ erts_atomic_set_relb(&(PRT)->connected, (erts_aint_t) (PID))
+#define ERTS_PORT_INIT_CONNECTED(PRT, PID) \
+ erts_atomic_init_nob(&(PRT)->connected, (erts_aint_t) (PID))
+
+
+struct erl_drv_port_data_lock {
+ erts_mtx_t mtx;
+ erts_atomic_t refc;
+ Port *prt;
+};
+
+ERTS_GLB_INLINE ErtsRunQueue *erts_port_runq(Port *prt);
+
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+
+ERTS_GLB_INLINE ErtsRunQueue *
+erts_port_runq(Port *prt)
+{
+#ifdef ERTS_SMP
+ ErtsRunQueue *rq1, *rq2;
+ rq1 = (ErtsRunQueue *) erts_smp_atomic_read_nob(&prt->run_queue);
+ if (!rq1)
+ return NULL;
+ while (1) {
+ erts_smp_runq_lock(rq1);
+ rq2 = (ErtsRunQueue *) erts_smp_atomic_read_nob(&prt->run_queue);
+ if (rq1 == rq2)
+ return rq1;
+ erts_smp_runq_unlock(rq1);
+ rq1 = rq2;
+ if (!rq1)
+ return NULL;
+ }
+#else
+ return ERTS_RUNQ_IX(0);
+#endif
+}
+
+#endif
+
+
+ERTS_GLB_INLINE void *erts_prtsd_get(Port *p, int ix);
+ERTS_GLB_INLINE void *erts_prtsd_set(Port *p, int ix, void *new);
+
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+
+ERTS_GLB_INLINE void *
+erts_prtsd_get(Port *prt, int ix)
+{
+ return prt->psd ? prt->psd->data[ix] : NULL;
+}
+
+ERTS_GLB_INLINE void *
+erts_prtsd_set(Port *prt, int ix, void *data)
+{
+ if (prt->psd) {
+ void *old = prt->psd->data[ix];
+ prt->psd->data[ix] = data;
+ return old;
+ }
+ else {
+ prt->psd = erts_alloc(ERTS_ALC_T_PRTSD, sizeof(ErtsPrtSD));
+ prt->psd->data[ix] = data;
+ return NULL;
+ }
+}
+
+#endif
+
+extern erts_smp_atomic_t erts_bytes_out; /* no bytes written out */
+extern erts_smp_atomic_t erts_bytes_in; /* no bytes sent into the system */
+
+
+/* port status flags */
+
+#define ERTS_PORT_SFLG_CONNECTED ((Uint32) (1 << 0))
+/* Port have begun exiting */
+#define ERTS_PORT_SFLG_EXITING ((Uint32) (1 << 1))
+/* Distribution port */
+#define ERTS_PORT_SFLG_DISTRIBUTION ((Uint32) (1 << 2))
+#define ERTS_PORT_SFLG_BINARY_IO ((Uint32) (1 << 3))
+#define ERTS_PORT_SFLG_SOFT_EOF ((Uint32) (1 << 4))
+/* Flow control */
+/* Port is closing (no i/o accepted) */
+#define ERTS_PORT_SFLG_CLOSING ((Uint32) (1 << 5))
+/* Send a closed message when terminating */
+#define ERTS_PORT_SFLG_SEND_CLOSED ((Uint32) (1 << 6))
+/* Line orinted io on port */
+#define ERTS_PORT_SFLG_LINEBUF_IO ((Uint32) (1 << 7))
+/* Immortal port (only certain system ports) */
+#define ERTS_PORT_SFLG_FREE ((Uint32) (1 << 8))
+#define ERTS_PORT_SFLG_INITIALIZING ((Uint32) (1 << 9))
+/* Port uses port specific locking (opposed to driver specific locking) */
+#define ERTS_PORT_SFLG_PORT_SPECIFIC_LOCK ((Uint32) (1 << 10))
+#define ERTS_PORT_SFLG_INVALID ((Uint32) (1 << 11))
+/* Last port to terminate halts the emulator */
+#define ERTS_PORT_SFLG_HALT ((Uint32) (1 << 12))
+#ifdef DEBUG
+/* Only debug: make sure all flags aren't cleared unintentionally */
+#define ERTS_PORT_SFLG_PORT_DEBUG ((Uint32) (1 << 31))
+#endif
+
+/* Combinations of port status flags */
+#define ERTS_PORT_SFLGS_DEAD \
+ (ERTS_PORT_SFLG_FREE | ERTS_PORT_SFLG_INITIALIZING)
+#define ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP \
+ (ERTS_PORT_SFLGS_DEAD | ERTS_PORT_SFLG_INVALID)
+#define ERTS_PORT_SFLGS_INVALID_LOOKUP \
+ (ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP \
+ | ERTS_PORT_SFLG_EXITING \
+ | ERTS_PORT_SFLG_CLOSING)
+#define ERTS_PORT_SFLGS_INVALID_TRACER_LOOKUP \
+ (ERTS_PORT_SFLGS_INVALID_LOOKUP \
+ | ERTS_PORT_SFLG_DISTRIBUTION)
+
+/*
+ * Costs in reductions for some port operations.
+ */
+#define ERTS_PORT_REDS_EXECUTE (CONTEXT_REDS/4)
+#define ERTS_PORT_REDS_FREE (CONTEXT_REDS/400)
+#define ERTS_PORT_REDS_TIMEOUT (CONTEXT_REDS/100)
+#define ERTS_PORT_REDS_INPUT (CONTEXT_REDS/100)
+#define ERTS_PORT_REDS_OUTPUT (CONTEXT_REDS/100)
+#define ERTS_PORT_REDS_EVENT (CONTEXT_REDS/100)
+#define ERTS_PORT_REDS_CMD_OUTPUTV (CONTEXT_REDS/100)
+#define ERTS_PORT_REDS_CMD_OUTPUT (CONTEXT_REDS/100)
+#define ERTS_PORT_REDS_EXIT (CONTEXT_REDS/100)
+#define ERTS_PORT_REDS_CONNECT (CONTEXT_REDS/200)
+#define ERTS_PORT_REDS_UNLINK (CONTEXT_REDS/200)
+#define ERTS_PORT_REDS_LINK (CONTEXT_REDS/200)
+#define ERTS_PORT_REDS_BADSIG (CONTEXT_REDS/200)
+#define ERTS_PORT_REDS_CONTROL (CONTEXT_REDS/100)
+#define ERTS_PORT_REDS_CALL (CONTEXT_REDS/50)
+#define ERTS_PORT_REDS_INFO (CONTEXT_REDS/100)
+#define ERTS_PORT_REDS_SET_DATA (CONTEXT_REDS/100)
+#define ERTS_PORT_REDS_GET_DATA (CONTEXT_REDS/100)
+#define ERTS_PORT_REDS_TERMINATE (CONTEXT_REDS/50)
+
+void print_port_info(Port *, int, void *);
+void erts_port_free(Port *);
+#ifndef ERTS_SMP
+void erts_port_cleanup(Port *);
+#endif
+void erts_fire_port_monitor(Port *prt, Eterm ref);
+#ifdef ERTS_SMP
+int erts_port_handle_xports(Port *);
+#endif
+
+#if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK)
+int erts_lc_is_port_locked(Port *);
+#endif
+
+ERTS_GLB_INLINE void erts_port_inc_refc(Port *prt);
+ERTS_GLB_INLINE void erts_port_dec_refc(Port *prt);
+ERTS_GLB_INLINE void erts_port_add_refc(Port *prt, Sint32 add_refc);
+
+ERTS_GLB_INLINE int erts_smp_port_trylock(Port *prt);
+ERTS_GLB_INLINE void erts_smp_port_lock(Port *prt);
+ERTS_GLB_INLINE void erts_smp_port_unlock(Port *prt);
+
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+
+ERTS_GLB_INLINE void erts_port_inc_refc(Port *prt)
+{
+#ifdef ERTS_SMP
+ erts_ptab_inc_refc(&prt->common);
+#else
+ erts_atomic32_inc_nob(&prt->refc);
+#endif
+}
+
+ERTS_GLB_INLINE void erts_port_dec_refc(Port *prt)
+{
+#ifdef ERTS_SMP
+ int referred = erts_ptab_dec_test_refc(&prt->common);
+ if (!referred)
+ erts_port_free(prt);
+#else
+ int refc = erts_atomic32_dec_read_nob(&prt->refc);
+ if (refc == 0)
+ erts_port_free(prt);
+#endif
+}
+
+ERTS_GLB_INLINE void erts_port_add_refc(Port *prt, Sint32 add_refc)
+{
+#ifdef ERTS_SMP
+ int referred = erts_ptab_add_test_refc(&prt->common, add_refc);
+ if (!referred)
+ erts_port_free(prt);
+#else
+ int refc = erts_atomic32_add_read_nob(&prt->refc, add_refc);
+ if (refc == 0)
+ erts_port_free(prt);
+#endif
+}
+
+ERTS_GLB_INLINE int
+erts_smp_port_trylock(Port *prt)
+{
+#ifdef ERTS_SMP
+ /* *Need* to be a managed thread */
+ ERTS_SMP_LC_ASSERT(erts_thr_progress_is_managed_thread());
+ return erts_mtx_trylock(prt->lock);
+#else
+ return 0;
+#endif
+}
+
+ERTS_GLB_INLINE void
+erts_smp_port_lock(Port *prt)
+{
+#ifdef ERTS_SMP
+ /* *Need* to be a managed thread */
+ ERTS_SMP_LC_ASSERT(erts_thr_progress_is_managed_thread());
+ erts_mtx_lock(prt->lock);
+#endif
+}
+
+ERTS_GLB_INLINE void
+erts_smp_port_unlock(Port *prt)
+{
+#ifdef ERTS_SMP
+ /* *Need* to be a managed thread */
+ ERTS_SMP_LC_ASSERT(erts_thr_progress_is_managed_thread());
+ erts_mtx_unlock(prt->lock);
+#endif
+}
+
+#endif /* #if ERTS_GLB_INLINE_INCL_FUNC_DEF */
+
+
+#define ERTS_INVALID_PORT_OPT(PP, ID, FLGS) \
+ (!(PP) \
+ || (erts_atomic32_read_nob(&(PP)->state) & (FLGS)) \
+ || (PP)->common.id != (ID))
+
+/* port lookup */
+
+#define INVALID_PORT(PP, ID) \
+ ERTS_INVALID_PORT_OPT((PP), (ID), ERTS_PORT_SFLGS_INVALID_LOOKUP)
+
+/* Invalidate trace port if anything suspicious, for instance
+ * that the port is a distribution port or it is busy.
+ */
+#define INVALID_TRACER_PORT(PP, ID) \
+ ERTS_INVALID_PORT_OPT((PP), (ID), ERTS_PORT_SFLGS_INVALID_TRACER_LOOKUP)
+
+#define ERTS_PORT_SCHED_ID(P, ID) \
+ ((Uint) (UWord) erts_prtsd_set((P), ERTS_PSD_SCHED_ID, (void *) (UWord) (ID)))
+
+extern const Port erts_invalid_port;
+#define ERTS_PORT_LOCK_BUSY ((Port *) &erts_invalid_port)
+
+int erts_is_port_ioq_empty(Port *);
+void erts_terminate_port(Port *);
+
+#ifdef ERTS_SMP
+Port *erts_de2port(DistEntry *, Process *, ErtsProcLocks);
+#endif
+
+ERTS_GLB_INLINE Port *erts_pix2port(int);
+ERTS_GLB_INLINE Port *erts_port_lookup_raw(Eterm);
+ERTS_GLB_INLINE Port *erts_port_lookup(Eterm, Uint32);
+ERTS_GLB_INLINE Port*erts_id2port(Eterm id);
+ERTS_GLB_INLINE Port *erts_id2port_sflgs(Eterm, Process *, ErtsProcLocks, Uint32);
+ERTS_GLB_INLINE void erts_port_release(Port *);
+#ifdef ERTS_SMP
+ERTS_GLB_INLINE Port *erts_thr_id2port_sflgs(Eterm id, Uint32 invalid_sflgs);
+ERTS_GLB_INLINE void erts_thr_port_release(Port *prt);
+#endif
+ERTS_GLB_INLINE Port *erts_thr_drvport2port_raw(ErlDrvPort, int);
+ERTS_GLB_INLINE Port *erts_drvport2port_raw(ErlDrvPort drvport);
+ERTS_GLB_INLINE Port *erts_drvport2port(ErlDrvPort, erts_aint32_t *);
+ERTS_GLB_INLINE Port *erts_drvportid2port(Eterm);
+ERTS_GLB_INLINE Eterm erts_drvport2id(ErlDrvPort);
+ERTS_GLB_INLINE Uint32 erts_portid2status(Eterm);
+ERTS_GLB_INLINE int erts_is_port_alive(Eterm);
+ERTS_GLB_INLINE int erts_is_valid_tracer_port(Eterm);
+ERTS_GLB_INLINE int erts_port_driver_callback_epilogue(Port *, erts_aint32_t *);
+
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+
+ERTS_GLB_INLINE Port *erts_pix2port(int ix)
+{
+ Port *prt;
+ ASSERT(0 <= ix && ix < erts_ptab_max(&erts_port));
+ prt = (Port *) erts_ptab_pix2intptr_nob(&erts_port, ix);
+ return prt == ERTS_PORT_LOCK_BUSY ? NULL : prt;
+}
+
+ERTS_GLB_INLINE Port *
+erts_port_lookup_raw(Eterm id)
+{
+ Port *prt;
+
+ ERTS_SMP_LC_ASSERT(erts_thr_progress_lc_is_delaying());
+
+ if (is_not_internal_port(id))
+ return NULL;
+
+ prt = (Port *) erts_ptab_pix2intptr_ddrb(&erts_port,
+ internal_port_index(id));
+ return prt && prt->common.id == id ? prt : NULL;
+}
+
+ERTS_GLB_INLINE Port *
+erts_port_lookup(Eterm id, Uint32 invalid_sflgs)
+{
+ Port *prt = erts_port_lookup_raw(id);
+ return (!prt
+ ? NULL
+ : ((invalid_sflgs & erts_atomic32_read_nob(&prt->state))
+ ? NULL
+ : prt));
+}
+
+
+ERTS_GLB_INLINE Port*
+erts_id2port(Eterm id)
+{
+ erts_aint32_t state;
+ Port *prt;
+
+ /* Only allowed to be called from managed threads */
+ ERTS_SMP_LC_ASSERT(erts_thr_progress_is_managed_thread());
+
+ if (is_not_internal_port(id))
+ return NULL;
+
+ prt = (Port *) erts_ptab_pix2intptr_ddrb(&erts_port,
+ internal_port_index(id));
+
+ if (!prt || prt->common.id != id)
+ return NULL;
+
+ erts_smp_port_lock(prt);
+ state = erts_atomic32_read_nob(&prt->state);
+ if (state & ERTS_PORT_SFLGS_INVALID_LOOKUP) {
+ erts_smp_port_unlock(prt);
+ return NULL;
+ }
+
+ return prt;
+}
+
+
+ERTS_GLB_INLINE Port*
+erts_id2port_sflgs(Eterm id,
+ Process *c_p, ErtsProcLocks c_p_locks,
+ Uint32 invalid_sflgs)
+{
+#ifdef ERTS_SMP
+ int no_proc_locks = !c_p || !c_p_locks;
+#endif
+ erts_aint32_t state;
+ Port *prt;
+
+ /* Only allowed to be called from managed threads */
+ ERTS_SMP_LC_ASSERT(erts_thr_progress_is_managed_thread());
+
+ if (is_not_internal_port(id))
+ return NULL;
+
+ prt = (Port *) erts_ptab_pix2intptr_ddrb(&erts_port,
+ internal_port_index(id));
+
+ if (!prt || prt->common.id != id)
+ return NULL;
+
+#ifdef ERTS_SMP
+ if (no_proc_locks)
+ erts_smp_port_lock(prt);
+ else if (erts_smp_port_trylock(prt) == EBUSY) {
+ /* Unlock process locks, and acquire locks in lock order... */
+ erts_smp_proc_unlock(c_p, c_p_locks);
+ erts_smp_port_lock(prt);
+ erts_smp_proc_lock(c_p, c_p_locks);
+ }
+#endif
+ state = erts_atomic32_read_nob(&prt->state);
+ if (state & invalid_sflgs) {
+#ifdef ERTS_SMP
+ erts_smp_port_unlock(prt);
+#endif
+ return NULL;
+ }
+
+ return prt;
+}
+
+ERTS_GLB_INLINE void
+erts_port_release(Port *prt)
+{
+ /* Only allowed to be called from managed threads */
+ ERTS_SMP_LC_ASSERT(erts_thr_progress_is_managed_thread());
+#ifdef ERTS_SMP
+ erts_smp_port_unlock(prt);
+#else
+ if (prt->cleanup) {
+ prt->cleanup = 0;
+ erts_port_cleanup(prt);
+ }
+#endif
+}
+
+#ifdef ERTS_SMP
+
+/*
+ * erts_thr_id2port_sflgs() and erts_thr_port_release() can
+ * be used by unmanaged threads in the SMP case.
+ */
+ERTS_GLB_INLINE Port *
+erts_thr_id2port_sflgs(Eterm id, Uint32 invalid_sflgs)
+{
+ Port *prt;
+ ErtsThrPrgrDelayHandle dhndl;
+
+ if (is_not_internal_port(id))
+ return NULL;
+
+ dhndl = erts_thr_progress_unmanaged_delay();
+
+ prt = (Port *) erts_ptab_pix2intptr_ddrb(&erts_port,
+ internal_port_index(id));
+
+ if (!prt || prt->common.id != id) {
+ erts_thr_progress_unmanaged_continue(dhndl);
+ prt = NULL;
+ }
+ else {
+ erts_aint32_t state;
+ if (dhndl != ERTS_THR_PRGR_DHANDLE_MANAGED) {
+ erts_port_inc_refc(prt);
+ erts_thr_progress_unmanaged_continue(dhndl);
+ }
+
+ erts_mtx_lock(prt->lock);
+ state = erts_atomic32_read_nob(&prt->state);
+ if (state & invalid_sflgs) {
+ erts_mtx_unlock(prt->lock);
+ if (dhndl != ERTS_THR_PRGR_DHANDLE_MANAGED)
+ erts_port_dec_refc(prt);
+ prt = NULL;
+ }
+ }
+
+ return prt;
+}
+
+ERTS_GLB_INLINE void
+erts_thr_port_release(Port *prt)
+{
+ erts_mtx_unlock(prt->lock);
+#ifdef ERTS_SMP
+ if (!erts_thr_progress_is_managed_thread())
+ erts_port_dec_refc(prt);
+#endif
+}
+
+#endif
+
+ERTS_GLB_INLINE Port*
+erts_thr_drvport2port_raw(ErlDrvPort drvport, int lock_pdl)
+{
+#if ERTS_ENABLE_LOCK_CHECK
+ int emu_thread = erts_lc_is_emu_thr();
+#endif
+ if (drvport == ERTS_INVALID_ERL_DRV_PORT)
+ return NULL;
+ else {
+ Port *prt = (Port *) drvport;
+ if (lock_pdl && prt->port_data_lock)
+ driver_pdl_lock(prt->port_data_lock);
+#if ERTS_ENABLE_LOCK_CHECK
+ if (!ERTS_IS_CRASH_DUMPING) {
+ if (emu_thread) {
+ ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
+ ERTS_LC_ASSERT(!prt->port_data_lock
+ || erts_lc_mtx_is_locked(&prt->port_data_lock->mtx));
+ }
+ else {
+ ERTS_LC_ASSERT(prt->port_data_lock);
+ ERTS_LC_ASSERT(erts_lc_mtx_is_locked(&prt->port_data_lock->mtx));
+ }
+ }
+#endif
+ return prt;
+ }
+}
+
+ERTS_GLB_INLINE Port*
+erts_drvport2port_raw(ErlDrvPort drvport)
+{
+ ERTS_LC_ASSERT(erts_lc_is_emu_thr());
+ if (drvport == ERTS_INVALID_ERL_DRV_PORT)
+ return NULL;
+ else {
+ Port *prt = (Port *) drvport;
+ ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt)
+ || ERTS_IS_CRASH_DUMPING);
+ return prt;
+ }
+}
+
+ERTS_GLB_INLINE Port*
+erts_drvport2port(ErlDrvPort drvport, erts_aint32_t *statep)
+{
+ Port *prt = erts_drvport2port_raw(drvport);
+ erts_aint32_t state;
+ if (!prt)
+ return NULL;
+ state = erts_atomic32_read_nob(&prt->state);
+ if (state & ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP)
+ return NULL;
+ if (statep)
+ *statep = state;
+ return prt;
+}
+
+ERTS_GLB_INLINE Port*
+erts_drvportid2port(Eterm id)
+{
+ Port *prt;
+ erts_aint32_t state;
+ if (is_not_internal_port(id))
+ return NULL;
+ prt = (Port *) erts_ptab_pix2intptr_nob(&erts_port,
+ internal_port_index(id));
+ if (!prt)
+ return NULL;
+ ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt)
+ || ERTS_IS_CRASH_DUMPING);
+ if (prt->common.id != id)
+ return NULL;
+ state = erts_atomic32_read_nob(&prt->state);
+ if (state & ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP)
+ return NULL;
+ return prt;
+}
+
+ERTS_GLB_INLINE Eterm
+erts_drvport2id(ErlDrvPort drvport)
+{
+ Port *prt = erts_drvport2port_raw(drvport);
+ if (!prt)
+ return am_undefined;
+ else
+ return prt->common.id;
+}
+
+ERTS_GLB_INLINE Uint32
+erts_portid2status(Eterm id)
+{
+ Port *prt = erts_port_lookup_raw(id);
+ if (prt)
+ return (Uint32) erts_atomic32_read_acqb(&prt->state);
+ else
+ return ERTS_PORT_SFLG_INVALID;
+}
+
+ERTS_GLB_INLINE int
+erts_is_port_alive(Eterm id)
+{
+ return !(erts_portid2status(id) & (ERTS_PORT_SFLG_INVALID
+ | ERTS_PORT_SFLGS_DEAD));
+}
+
+ERTS_GLB_INLINE int
+erts_is_valid_tracer_port(Eterm id)
+{
+ return !(erts_portid2status(id) & ERTS_PORT_SFLGS_INVALID_TRACER_LOOKUP);
+}
+
+ERTS_GLB_INLINE int
+erts_port_driver_callback_epilogue(Port *prt, erts_aint32_t *statep)
+{
+ int reds = 0;
+ erts_aint32_t state;
+
+ ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
+
+ state = erts_atomic32_read_nob(&prt->state);
+ if ((state & ERTS_PORT_SFLG_CLOSING) && erts_is_port_ioq_empty(prt)) {
+ reds += ERTS_PORT_REDS_TERMINATE;
+ erts_terminate_port(prt);
+ state = erts_atomic32_read_nob(&prt->state);
+ ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
+ }
+
+#ifdef ERTS_SMP
+ if (prt->xports) {
+ reds += erts_port_handle_xports(prt);
+ ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
+ ASSERT(!prt->xports);
+ }
+#endif
+
+ if (statep)
+ *statep = state;
+
+ return reds;
+}
+
+#endif /* #if ERTS_GLB_INLINE_INCL_FUNC_DEF */
+
+void erts_port_resume_procs(Port *);
+
+struct binary;
+
+#define ERTS_P2P_SIG_TYPE_BAD 0
+#define ERTS_P2P_SIG_TYPE_OUTPUT 1
+#define ERTS_P2P_SIG_TYPE_OUTPUTV 2
+#define ERTS_P2P_SIG_TYPE_CONNECT 3
+#define ERTS_P2P_SIG_TYPE_EXIT 4
+#define ERTS_P2P_SIG_TYPE_CONTROL 5
+#define ERTS_P2P_SIG_TYPE_CALL 6
+#define ERTS_P2P_SIG_TYPE_INFO 7
+#define ERTS_P2P_SIG_TYPE_LINK 8
+#define ERTS_P2P_SIG_TYPE_UNLINK 9
+#define ERTS_P2P_SIG_TYPE_SET_DATA 10
+#define ERTS_P2P_SIG_TYPE_GET_DATA 11
+
+#define ERTS_P2P_SIG_TYPE_BITS 4
+#define ERTS_P2P_SIG_TYPE_MASK \
+ ((1 << ERTS_P2P_SIG_TYPE_BITS) - 1)
+
+#define ERTS_P2P_SIG_DATA_FLG(N) \
+ (1 << (ERTS_P2P_SIG_TYPE_BITS + (N)))
+#define ERTS_P2P_SIG_DATA_FLG_BANG_OP ERTS_P2P_SIG_DATA_FLG(0)
+#define ERTS_P2P_SIG_DATA_FLG_REPLY ERTS_P2P_SIG_DATA_FLG(1)
+#define ERTS_P2P_SIG_DATA_FLG_NOSUSPEND ERTS_P2P_SIG_DATA_FLG(2)
+#define ERTS_P2P_SIG_DATA_FLG_FORCE ERTS_P2P_SIG_DATA_FLG(3)
+#define ERTS_P2P_SIG_DATA_FLG_BAD_OUTPUT ERTS_P2P_SIG_DATA_FLG(4)
+#define ERTS_P2P_SIG_DATA_FLG_BROKEN_LINK ERTS_P2P_SIG_DATA_FLG(5)
+#define ERTS_P2P_SIG_DATA_FLG_SCHED ERTS_P2P_SIG_DATA_FLG(6)
+
+struct ErtsProc2PortSigData_ {
+ int flags;
+ Eterm caller;
+ Uint32 ref[ERTS_MAX_REF_NUMBERS];
+ union {
+ struct {
+ Eterm from;
+ ErlIOVec *evp;
+ ErlDrvBinary *cbinp;
+ } outputv;
+ struct {
+ Eterm from;
+ char *bufp;
+ ErlDrvSizeT size;
+ } output;
+ struct {
+ Eterm from;
+ Eterm connected;
+ } connect;
+ struct {
+ Eterm from;
+ Eterm reason;
+ ErlHeapFragment *bp;
+ } exit;
+ struct {
+ struct binary *binp;
+ unsigned int command;
+ char *bufp;
+ ErlDrvSizeT size;
+ } control;
+ struct {
+ unsigned int command;
+ char *bufp;
+ ErlDrvSizeT size;
+ } call;
+ struct {
+ Eterm item;
+ } info;
+ struct {
+ Eterm port;
+ Eterm to;
+ } link;
+ struct {
+ Eterm from;
+ } unlink;
+ struct {
+ ErlHeapFragment *bp;
+ Eterm data;
+ } set_data;
+ } u;
+} ;
+
+ERTS_GLB_INLINE int
+erts_proc2port_sig_is_command_op(ErtsProc2PortSigData *sigdp);
+ERTS_GLB_INLINE ErlDrvSizeT
+erts_proc2port_sig_command_data_size(ErtsProc2PortSigData *sigdp);
+
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+
+ERTS_GLB_INLINE int
+erts_proc2port_sig_is_command_op(ErtsProc2PortSigData *sigdp)
+{
+ switch (sigdp->flags & ERTS_P2P_SIG_TYPE_MASK) {
+ case ERTS_P2P_SIG_TYPE_OUTPUT: return !0;
+ case ERTS_P2P_SIG_TYPE_OUTPUTV: return !0;
+ default: return 0;
+ }
+}
+
+ERTS_GLB_INLINE ErlDrvSizeT
+erts_proc2port_sig_command_data_size(ErtsProc2PortSigData *sigdp)
+{
+ switch (sigdp->flags & ERTS_P2P_SIG_TYPE_MASK) {
+ case ERTS_P2P_SIG_TYPE_OUTPUT: return sigdp->u.output.size;
+ case ERTS_P2P_SIG_TYPE_OUTPUTV: return sigdp->u.outputv.evp->size;
+ default: return (ErlDrvSizeT) 0;
+ }
+}
+
+#endif
+
+#define ERTS_PROC2PORT_SIG_EXEC 0
+#define ERTS_PROC2PORT_SIG_ABORT 1
+#define ERTS_PROC2PORT_SIG_ABORT_NOSUSPEND 2
+#define ERTS_PROC2PORT_SIG_ABORT_CLOSED 3
+
+typedef int (*ErtsProc2PortSigCallback)(Port *,
+ erts_aint32_t,
+ int,
+ ErtsProc2PortSigData *);
+
+typedef enum {
+ ERTS_PORT_OP_BADARG,
+ ERTS_PORT_OP_CALLER_EXIT,
+ ERTS_PORT_OP_BUSY,
+ ERTS_PORT_OP_BUSY_SCHEDULED,
+ ERTS_PORT_OP_SCHEDULED,
+ ERTS_PORT_OP_DROPPED,
+ ERTS_PORT_OP_DONE
+} ErtsPortOpResult;
+
+ErtsPortOpResult
+erts_schedule_proc2port_signal(Process *,
+ Port *,
+ Eterm,
+ Eterm *,
+ ErtsProc2PortSigData *,
+ int,
+ ErtsProc2PortSigCallback);
+
+int erts_deliver_port_exit(Port *, Eterm, Eterm, int);
+
+/*
+ * Port signal flags
+ */
+#define ERTS_PORT_SIG_FLG_BANG_OP ERTS_P2P_SIG_DATA_FLG_BANG_OP
+#define ERTS_PORT_SIG_FLG_NOSUSPEND ERTS_P2P_SIG_DATA_FLG_NOSUSPEND
+#define ERTS_PORT_SIG_FLG_FORCE ERTS_P2P_SIG_DATA_FLG_FORCE
+#define ERTS_PORT_SIG_FLG_BROKEN_LINK ERTS_P2P_SIG_DATA_FLG_BROKEN_LINK
+#define ERTS_PORT_SIG_FLG_BAD_OUTPUT ERTS_P2P_SIG_DATA_FLG_BAD_OUTPUT
+#define ERTS_PORT_SIG_FLG_FORCE_SCHED ERTS_P2P_SIG_DATA_FLG_SCHED
+/* ERTS_PORT_SIG_FLG_FORCE_IMM_CALL only when crash dumping... */
+#define ERTS_PORT_SIG_FLG_FORCE_IMM_CALL ERTS_P2P_SIG_DATA_FLG_BAD_OUTPUT
+
+/*
+ * Port ! {Owner, {command, Data}}
+ * Port ! {Owner, {connect, NewOwner}}
+ * Port ! {Owner, close}
+ */
+ErtsPortOpResult erts_port_command(Process *, int, Port *, Eterm, Eterm *);
+
+/*
+ * Signals from processes to ports.
+ */
+ErtsPortOpResult erts_port_output(Process *, int, Port *, Eterm, Eterm, Eterm *);
+ErtsPortOpResult erts_port_exit(Process *, int, Port *, Eterm, Eterm, Eterm *);
+ErtsPortOpResult erts_port_connect(Process *, int, Port *, Eterm, Eterm, Eterm *);
+ErtsPortOpResult erts_port_link(Process *, Port *, Eterm, Eterm *);
+ErtsPortOpResult erts_port_unlink(Process *, Port *, Eterm, Eterm *);
+ErtsPortOpResult erts_port_control(Process *, Port *, unsigned int, Eterm, Eterm *);
+ErtsPortOpResult erts_port_call(Process *, Port *, unsigned int, Eterm, Eterm *);
+ErtsPortOpResult erts_port_info(Process *, Port *, Eterm, Eterm *);
+ErtsPortOpResult erts_port_set_data(Process *, Port *, Eterm, Eterm *);
+ErtsPortOpResult erts_port_get_data(Process *, Port *, Eterm *);
+
+#endif
diff --git a/erts/emulator/beam/erl_port_task.c b/erts/emulator/beam/erl_port_task.c
index 86454fe1fa..09c8e760f4 100644
--- a/erts/emulator/beam/erl_port_task.c
+++ b/erts/emulator/beam/erl_port_task.c
@@ -33,36 +33,34 @@
#include "erl_port_task.h"
#include "dist.h"
#include "dtrace-wrapper.h"
-
-#if defined(DEBUG) && 0
-#define HARD_DEBUG
-#endif
+#include <stdarg.h>
/*
- * Costs in reductions for some port operations.
+ * ERTS_PORT_CALLBACK_VREDS: Limit the amount of callback calls we do...
*/
-#define ERTS_PORT_REDS_EXECUTE 0
-#define ERTS_PORT_REDS_FREE 50
-#define ERTS_PORT_REDS_TIMEOUT 200
-#define ERTS_PORT_REDS_INPUT 200
-#define ERTS_PORT_REDS_OUTPUT 200
-#define ERTS_PORT_REDS_EVENT 200
-#define ERTS_PORT_REDS_TERMINATE 100
+#define ERTS_PORT_CALLBACK_VREDS (CONTEXT_REDS/5)
+#if defined(DEBUG) && 0
+#define ERTS_HARD_DEBUG_TASK_QUEUES
+#else
+#undef ERTS_HARD_DEBUG_TASK_QUEUES
+#endif
-#define ERTS_PORT_TASK_INVALID_PORT(P, ID) \
- ((erts_port_status_get((P)) & ERTS_PORT_SFLGS_DEAD) || (P)->id != (ID))
-
-#define ERTS_PORT_IS_IN_RUNQ(RQ, P) \
- ((P)->sched.next || (P)->sched.prev || (RQ)->ports.start == (P))
+#ifdef ERTS_HARD_DEBUG_TASK_QUEUES
+static void chk_task_queues(Port *pp, ErtsPortTask *execq, int processing_busy_queue);
+#define ERTS_PT_DBG_CHK_TASK_QS(PP, EQ, PBQ) \
+ chk_task_queues((PP), (EQ), (PBQ))
+#else
+#define ERTS_PT_DBG_CHK_TASK_QS(PP, EQ, PBQ)
+#endif
#ifdef USE_VM_PROBES
#define DTRACE_DRIVER(PROBE_NAME, PP) \
- if (DTRACE_ENABLED(driver_ready_input)) { \
+ if (DTRACE_ENABLED(PROBE_NAME)) { \
DTRACE_CHARBUF(process_str, DTRACE_TERM_BUF_SIZE); \
DTRACE_CHARBUF(port_str, DTRACE_TERM_BUF_SIZE); \
\
- dtrace_pid_str(PP->connected, process_str); \
+ dtrace_pid_str(ERTS_PORT_GET_CONNECTED(PP), process_str); \
dtrace_port_str(PP, port_str); \
DTRACE3(PROBE_NAME, process_str, port_str, PP->name); \
}
@@ -72,83 +70,768 @@
erts_smp_atomic_t erts_port_task_outstanding_io_tasks;
-struct ErtsPortTaskQueue_ {
- ErtsPortTask *first;
- ErtsPortTask *last;
- Port *port;
-};
+#define ERTS_PT_STATE_SCHEDULED 0
+#define ERTS_PT_STATE_ABORTED 1
+#define ERTS_PT_STATE_EXECUTING 2
+
+typedef union {
+ struct { /* I/O tasks */
+ ErlDrvEvent event;
+ ErlDrvEventData event_data;
+ } io;
+ struct {
+ ErtsProc2PortSigCallback callback;
+ ErtsProc2PortSigData data;
+ } psig;
+} ErtsPortTaskTypeData;
struct ErtsPortTask_ {
- ErtsPortTask *prev;
- ErtsPortTask *next;
- ErtsPortTaskQueue *queue;
- ErtsPortTaskHandle *handle;
+ erts_smp_atomic32_t state;
ErtsPortTaskType type;
- ErlDrvEvent event;
- ErlDrvEventData event_data;
+ union {
+ struct {
+ ErtsPortTask *next;
+ ErtsPortTaskHandle *handle;
+ int flags;
+ Uint32 ref[ERTS_MAX_REF_NUMBERS];
+ ErtsPortTaskTypeData td;
+ } alive;
+ ErtsThrPrgrLaterOp release;
+ } u;
};
-#ifdef HARD_DEBUG
-#define ERTS_PT_CHK_PORTQ(RQ) check_port_queue((RQ), NULL, 0)
-#define ERTS_PT_CHK_PRES_PORTQ(RQ, PP) check_port_queue((RQ), (PP), -1)
-#define ERTS_PT_CHK_IN_PORTQ(RQ, PP) check_port_queue((RQ), (PP), 1)
-#define ERTS_PT_CHK_NOT_IN_PORTQ(RQ, PP) check_port_queue((RQ), (PP), 0)
-#define ERTS_PT_CHK_TASKQ(Q) check_task_queue((Q), NULL, 0)
-#define ERTS_PT_CHK_IN_TASKQ(Q, T) check_task_queue((Q), (T), 1)
-#define ERTS_PT_CHK_NOT_IN_TASKQ(Q, T) check_task_queue((Q), (T), 0)
-static void
-check_port_queue(Port *chk_pp, int inq);
-static void
-check_task_queue(ErtsPortTaskQueue *ptqp,
- ErtsPortTask *chk_ptp,
- int inq);
-#else
-#define ERTS_PT_CHK_PORTQ(RQ)
-#define ERTS_PT_CHK_PRES_PORTQ(RQ, PP)
-#define ERTS_PT_CHK_IN_PORTQ(RQ, PP)
-#define ERTS_PT_CHK_NOT_IN_PORTQ(RQ, PP)
-#define ERTS_PT_CHK_TASKQ(Q)
-#define ERTS_PT_CHK_IN_TASKQ(Q, T)
-#define ERTS_PT_CHK_NOT_IN_TASKQ(Q, T)
+struct ErtsPortTaskHandleList_ {
+ ErtsPortTaskHandle handle;
+ union {
+ ErtsPortTaskHandleList *next;
+#ifdef ERTS_SMP
+ ErtsThrPrgrLaterOp release;
#endif
+ } u;
+};
+
+typedef struct ErtsPortTaskBusyCaller_ ErtsPortTaskBusyCaller;
+struct ErtsPortTaskBusyCaller_ {
+ ErtsPortTaskBusyCaller *next;
+ Eterm caller;
+ SWord count;
+ ErtsPortTask *last;
+};
+
+#define ERTS_PORT_TASK_BUSY_CALLER_TABLE_BUCKETS 17
+struct ErtsPortTaskBusyCallerTable_ {
+ ErtsPortTaskBusyCaller *bucket[ERTS_PORT_TASK_BUSY_CALLER_TABLE_BUCKETS];
+ ErtsPortTaskBusyCaller pre_alloc_busy_caller;
+};
-static void handle_remaining_tasks(ErtsRunQueue *runq, Port *pp);
+
+static void begin_port_cleanup(Port *pp,
+ ErtsPortTask **execq,
+ int *processing_busy_q_p);
ERTS_SCHED_PREF_QUICK_ALLOC_IMPL(port_task,
ErtsPortTask,
- 200,
+ 1000,
ERTS_ALC_T_PORT_TASK)
-ERTS_SCHED_PREF_QUICK_ALLOC_IMPL(port_taskq,
- ErtsPortTaskQueue,
+
+ERTS_SCHED_PREF_QUICK_ALLOC_IMPL(busy_caller_table,
+ ErtsPortTaskBusyCallerTable,
50,
- ERTS_ALC_T_PORT_TASKQ)
+ ERTS_ALC_T_BUSY_CALLER_TAB)
+
+#ifdef ERTS_SMP
+static void
+call_port_task_free(void *vptp)
+{
+ port_task_free((ErtsPortTask *) vptp);
+}
+#endif
+
+static ERTS_INLINE void
+schedule_port_task_free(ErtsPortTask *ptp)
+{
+#ifdef ERTS_SMP
+ erts_schedule_thr_prgr_later_op(call_port_task_free,
+ (void *) ptp,
+ &ptp->u.release);
+#else
+ port_task_free(ptp);
+#endif
+}
+
+static ERTS_INLINE ErtsPortTask *
+p2p_sig_data_to_task(ErtsProc2PortSigData *sigdp)
+{
+ ErtsPortTask *ptp;
+ char *ptr = (char *) sigdp;
+ ptr -= offsetof(ErtsPortTask, u.alive.td.psig.data);
+ ptp = (ErtsPortTask *) ptr;
+ ASSERT(ptp->type == ERTS_PORT_TASK_PROC_SIG);
+ return ptp;
+}
+
+ErtsProc2PortSigData *
+erts_port_task_alloc_p2p_sig_data(void)
+{
+ ErtsPortTask *ptp = port_task_alloc();
+
+ ptp->type = ERTS_PORT_TASK_PROC_SIG;
+ ptp->u.alive.flags = ERTS_PT_FLG_SIG_DEP;
+ erts_smp_atomic32_init_nob(&ptp->state, ERTS_PT_STATE_SCHEDULED);
+
+ ASSERT(ptp == p2p_sig_data_to_task(&ptp->u.alive.td.psig.data));
+
+ return &ptp->u.alive.td.psig.data;
+}
+
+static ERTS_INLINE Eterm
+task_caller(ErtsPortTask *ptp)
+{
+ Eterm caller;
+
+ ASSERT(ptp->type == ERTS_PORT_TASK_PROC_SIG);
+
+ caller = ptp->u.alive.td.psig.data.caller;
+
+ ASSERT(is_internal_pid(caller) || is_internal_port(caller));
+
+ return caller;
+}
+
+/*
+ * Busy queue management
+ */
+
+static ERTS_INLINE int
+caller2bix(Eterm caller)
+{
+ ASSERT(is_internal_pid(caller) || is_internal_port(caller));
+ return (int) (_GET_PID_DATA(caller) % ERTS_PORT_TASK_BUSY_CALLER_TABLE_BUCKETS);
+}
+
+
+static void
+popped_from_busy_queue(Port *pp, ErtsPortTask *ptp, int last)
+{
+ ErtsPortTaskBusyCaller **prev_bcpp = NULL, *bcp;
+ ErtsPortTaskBusyCallerTable *tabp = pp->sched.taskq.local.busy.table;
+ Eterm caller = task_caller(ptp);
+ int bix = caller2bix(caller);
+
+ ASSERT(is_internal_pid(caller));
+
+ ASSERT(tabp);
+ bcp = tabp->bucket[bix];
+ prev_bcpp = &tabp->bucket[bix];
+ ASSERT(bcp);
+ while (bcp->caller != caller) {
+ prev_bcpp = &bcp->next;
+ bcp = bcp->next;
+ ASSERT(bcp);
+ }
+ ASSERT(bcp->count > 0);
+ if (--bcp->count != 0) {
+ ASSERT(!last);
+ }
+ else {
+ *prev_bcpp = bcp->next;
+ if (bcp == &tabp->pre_alloc_busy_caller)
+ bcp->caller = am_undefined;
+ else
+ erts_free(ERTS_ALC_T_BUSY_CALLER, bcp);
+ if (last) {
+#ifdef DEBUG
+ erts_aint32_t flags =
+#endif
+ erts_smp_atomic32_read_band_nob(
+ &pp->sched.flags,
+ ~ERTS_PTS_FLG_HAVE_BUSY_TASKS);
+ ASSERT(flags & ERTS_PTS_FLG_HAVE_BUSY_TASKS);
+#ifdef DEBUG
+ for (bix = 0; bix < ERTS_PORT_TASK_BUSY_CALLER_TABLE_BUCKETS; bix++) {
+ ASSERT(!tabp->bucket[bix]);
+ }
+#endif
+ busy_caller_table_free(tabp);
+ pp->sched.taskq.local.busy.first = NULL;
+ pp->sched.taskq.local.busy.last = NULL;
+ pp->sched.taskq.local.busy.table = NULL;
+ }
+ }
+}
+
+static void
+busy_wait_move_to_busy_queue(Port *pp, ErtsPortTask *ptp)
+{
+ ErtsPortTaskBusyCallerTable *tabp = pp->sched.taskq.local.busy.table;
+ Eterm caller = task_caller(ptp);
+ ErtsPortTaskBusyCaller *bcp;
+ int bix;
+
+ ASSERT(is_internal_pid(caller));
+ /*
+ * Port is busy and this task type needs to wait until not busy.
+ */
+
+ ASSERT(ptp->u.alive.flags & ERTS_PT_FLG_WAIT_BUSY);
+
+ ptp->u.alive.next = NULL;
+ if (pp->sched.taskq.local.busy.last) {
+ ASSERT(pp->sched.taskq.local.busy.first);
+ pp->sched.taskq.local.busy.last->u.alive.next = ptp;
+ }
+ else {
+ int i;
+ erts_aint32_t flags;
+
+ pp->sched.taskq.local.busy.first = ptp;
+ flags = erts_smp_atomic32_read_bor_nob(&pp->sched.flags,
+ ERTS_PTS_FLG_HAVE_BUSY_TASKS);
+ ASSERT(!(flags & ERTS_PTS_FLG_HAVE_BUSY_TASKS));
+
+ ASSERT(!tabp);
+
+ tabp = busy_caller_table_alloc();
+ pp->sched.taskq.local.busy.table = tabp;
+ for (i = 0; i < ERTS_PORT_TASK_BUSY_CALLER_TABLE_BUCKETS; i++)
+ tabp->bucket[i] = NULL;
+ tabp->pre_alloc_busy_caller.caller = am_undefined;
+ }
+ pp->sched.taskq.local.busy.last = ptp;
+
+ bix = caller2bix(caller);
+ ASSERT(tabp);
+ bcp = tabp->bucket[bix];
+
+ while (bcp && bcp->caller != caller)
+ bcp = bcp->next;
+
+ if (bcp)
+ bcp->count++;
+ else {
+ if (tabp->pre_alloc_busy_caller.caller == am_undefined)
+ bcp = &tabp->pre_alloc_busy_caller;
+ else
+ bcp = erts_alloc(ERTS_ALC_T_BUSY_CALLER,
+ sizeof(ErtsPortTaskBusyCaller));
+ bcp->caller = caller;
+ bcp->count = 1;
+ bcp->next = tabp->bucket[bix];
+ tabp->bucket[bix] = bcp;
+ }
+
+ bcp->last = ptp;
+}
+
+static ERTS_INLINE int
+check_sig_dep_move_to_busy_queue(Port *pp, ErtsPortTask *ptp)
+{
+ ErtsPortTaskBusyCallerTable *tabp = pp->sched.taskq.local.busy.table;
+ ErtsPortTask *last_ptp;
+ ErtsPortTaskBusyCaller *bcp;
+ int bix;
+ Eterm caller;
+
+ ASSERT(ptp->u.alive.flags & ERTS_PT_FLG_SIG_DEP);
+ ASSERT(pp->sched.taskq.local.busy.last);
+ ASSERT(tabp);
+
+
+ /*
+ * We are either not busy, or the task does not imply wait on busy port.
+ * However, due to the signaling order requirements the task might depend
+ * on other tasks in the busy queue.
+ */
+
+ caller = task_caller(ptp);
+ bix = caller2bix(caller);
+ bcp = tabp->bucket[bix];
+ while (bcp && bcp->caller != caller)
+ bcp = bcp->next;
+
+ if (!bcp)
+ return 0;
+
+ /*
+ * There are other tasks that we depend on in the busy queue;
+ * move into busy queue.
+ */
+
+ bcp->count++;
+ last_ptp = bcp->last;
+ ptp->u.alive.next = last_ptp->u.alive.next;
+ if (!ptp->u.alive.next) {
+ ASSERT(pp->sched.taskq.local.busy.last == last_ptp);
+ pp->sched.taskq.local.busy.last = ptp;
+ }
+ last_ptp->u.alive.next = ptp;
+ bcp->last = ptp;
+
+ return 1;
+}
+
+static void
+no_sig_dep_move_from_busyq(Port *pp)
+{
+ ErtsPortTaskBusyCallerTable *tabp = pp->sched.taskq.local.busy.table;
+ ErtsPortTask *first_ptp, *last_ptp, *ptp;
+ ErtsPortTaskBusyCaller **prev_bcpp = NULL, *bcp = NULL;
+
+ /*
+ * Move tasks at the head of the busy queue that no longer
+ * have any dependencies to busy wait tasks into the ordinary
+ * queue.
+ */
+
+ first_ptp = ptp = pp->sched.taskq.local.busy.first;
+
+ ASSERT(ptp && !(ptp->u.alive.flags & ERTS_PT_FLG_WAIT_BUSY));
+ ASSERT(tabp);
+
+ do {
+ Eterm caller = task_caller(ptp);
+
+ if (!bcp || bcp->caller != caller) {
+ int bix = caller2bix(caller);
+
+ prev_bcpp = &tabp->bucket[bix];
+ bcp = tabp->bucket[bix];
+ ASSERT(bcp);
+ while (bcp->caller != caller) {
+ ASSERT(bcp);
+ prev_bcpp = &bcp->next;
+ bcp = bcp->next;
+ }
+ }
+
+ ASSERT(bcp->caller == caller);
+ ASSERT(bcp->count > 0);
+
+ if (--bcp->count == 0) {
+ *prev_bcpp = bcp->next;
+ if (bcp == &tabp->pre_alloc_busy_caller)
+ bcp->caller = am_undefined;
+ else
+ erts_free(ERTS_ALC_T_BUSY_CALLER, bcp);
+ }
+
+ last_ptp = ptp;
+ ptp = ptp->u.alive.next;
+ } while (ptp && !(ptp->u.alive.flags & ERTS_PT_FLG_WAIT_BUSY));
+
+ pp->sched.taskq.local.busy.first = last_ptp->u.alive.next;
+ if (!pp->sched.taskq.local.busy.first) {
+#ifdef DEBUG
+ int bix;
+ erts_aint32_t flags =
+#endif
+ erts_smp_atomic32_read_band_nob(
+ &pp->sched.flags,
+ ~ERTS_PTS_FLG_HAVE_BUSY_TASKS);
+ ASSERT(flags & ERTS_PTS_FLG_HAVE_BUSY_TASKS);
+#ifdef DEBUG
+ for (bix = 0; bix < ERTS_PORT_TASK_BUSY_CALLER_TABLE_BUCKETS; bix++) {
+ ASSERT(!tabp->bucket[bix]);
+ }
+#endif
+ busy_caller_table_free(tabp);
+ pp->sched.taskq.local.busy.last = NULL;
+ pp->sched.taskq.local.busy.table = NULL;
+ }
+ last_ptp->u.alive.next = pp->sched.taskq.local.first;
+ pp->sched.taskq.local.first = first_ptp;
+}
+
+#ifdef ERTS_HARD_DEBUG_TASK_QUEUES
+
+static void
+chk_task_queues(Port *pp, ErtsPortTask *execq, int processing_busy_queue)
+{
+ Sint tot_count, tot_table_count;
+ int bix;
+ ErtsPortTask *ptp, *last;
+ ErtsPortTask *first = processing_busy_queue ? execq : pp->sched.taskq.local.busy.first;
+ ErtsPortTask *nb_task_queue = processing_busy_queue ? pp->sched.taskq.local.first : execq;
+ ErtsPortTaskBusyCallerTable *tabp = pp->sched.taskq.local.busy.table;
+ ErtsPortTaskBusyCaller *bcp;
+
+ if (!first) {
+ ASSERT(!tabp);
+ ASSERT(!pp->sched.taskq.local.busy.last);
+ ASSERT(!(erts_smp_atomic32_read_nob(&pp->sched.flags) & ERTS_PTS_FLG_HAVE_BUSY_TASKS));
+ return;
+ }
+
+ ASSERT(erts_smp_atomic32_read_nob(&pp->sched.flags) & ERTS_PTS_FLG_HAVE_BUSY_TASKS);
+ ASSERT(tabp);
+
+ tot_count = 0;
+ ptp = first;
+ while (ptp) {
+ Sint count = 0;
+ Eterm caller = task_caller(ptp);
+ int bix = caller2bix(caller);
+ for (bcp = tabp->bucket[bix]; bcp; bcp = bcp->next)
+ if (bcp->caller == caller)
+ break;
+ ASSERT(bcp && bcp->caller == caller);
+
+ ASSERT(bcp->last);
+ while (1) {
+ ErtsPortTask *ptp2;
+
+ ASSERT(caller == task_caller(ptp));
+ count++;
+ tot_count++;
+ last = ptp;
+
+ for (ptp2 = nb_task_queue; ptp2; ptp2 = ptp2->u.alive.next) {
+ ASSERT(ptp != ptp2);
+ }
+
+ if (ptp == bcp->last)
+ break;
+ ptp = ptp->u.alive.next;
+ }
+
+ ASSERT(count == bcp->count);
+ ptp = ptp->u.alive.next;
+ }
+
+ tot_table_count = 0;
+ for (bix = 0; bix < ERTS_PORT_TASK_BUSY_CALLER_TABLE_BUCKETS; bix++) {
+ for (bcp = tabp->bucket[bix]; bcp; bcp = bcp->next)
+ tot_table_count += bcp->count;
+ }
+
+ ASSERT(tot_count == tot_table_count);
+
+ ASSERT(last == pp->sched.taskq.local.busy.last);
+}
+
+#endif /* ERTS_HARD_DEBUG_TASK_QUEUES */
/*
* Task handle manipulation.
*/
+static ERTS_INLINE void
+reset_port_task_handle(ErtsPortTaskHandle *pthp)
+{
+ erts_smp_atomic_set_relb(pthp, (erts_aint_t) NULL);
+}
+
static ERTS_INLINE ErtsPortTask *
handle2task(ErtsPortTaskHandle *pthp)
{
- return (ErtsPortTask *) erts_smp_atomic_read_nob(pthp);
+ return (ErtsPortTask *) erts_smp_atomic_read_acqb(pthp);
}
static ERTS_INLINE void
reset_handle(ErtsPortTask *ptp)
{
- if (ptp->handle) {
- ASSERT(ptp == handle2task(ptp->handle));
- erts_smp_atomic_set_nob(ptp->handle, (erts_aint_t) NULL);
+ if (ptp->u.alive.handle) {
+ ASSERT(ptp == handle2task(ptp->u.alive.handle));
+ reset_port_task_handle(ptp->u.alive.handle);
}
}
static ERTS_INLINE void
set_handle(ErtsPortTask *ptp, ErtsPortTaskHandle *pthp)
{
- ptp->handle = pthp;
+ ptp->u.alive.handle = pthp;
if (pthp) {
- erts_smp_atomic_set_nob(pthp, (erts_aint_t) ptp);
- ASSERT(ptp == handle2task(ptp->handle));
+ erts_smp_atomic_set_relb(pthp, (erts_aint_t) ptp);
+ ASSERT(ptp == handle2task(ptp->u.alive.handle));
+ }
+}
+
+
+/*
+ * Busy port queue management
+ */
+
+static erts_aint32_t
+check_unset_busy_port_q(Port *pp,
+ erts_aint32_t flags,
+ ErtsPortTaskBusyPortQ *bpq)
+{
+ ErlDrvSizeT qsize, low;
+ int resume_procs = 0;
+
+ ASSERT(bpq);
+ ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(pp));
+
+ erts_port_task_sched_lock(&pp->sched);
+ qsize = (ErlDrvSizeT) erts_smp_atomic_read_nob(&bpq->size);
+ low = (ErlDrvSizeT) erts_smp_atomic_read_nob(&bpq->low);
+ if (qsize < low) {
+ erts_aint32_t mask = ~(ERTS_PTS_FLG_CHK_UNSET_BUSY_PORT_Q
+ | ERTS_PTS_FLG_BUSY_PORT_Q);
+ flags = erts_smp_atomic32_read_band_relb(&pp->sched.flags, mask);
+ if ((flags & ERTS_PTS_FLGS_BUSY) == ERTS_PTS_FLG_BUSY_PORT_Q)
+ resume_procs = 1;
+ }
+ else if (flags & ERTS_PTS_FLG_CHK_UNSET_BUSY_PORT_Q) {
+ flags = erts_smp_atomic32_read_band_relb(&pp->sched.flags,
+ ~ERTS_PTS_FLG_CHK_UNSET_BUSY_PORT_Q);
+ flags &= ~ERTS_PTS_FLG_CHK_UNSET_BUSY_PORT_Q;
+ }
+ erts_port_task_sched_unlock(&pp->sched);
+ if (resume_procs)
+ erts_port_resume_procs(pp);
+
+ return flags;
+}
+
+static ERTS_INLINE void
+aborted_proc2port_data(Port *pp, ErlDrvSizeT size)
+{
+ ErtsPortTaskBusyPortQ *bpq;
+ erts_aint32_t flags;
+ ErlDrvSizeT qsz;
+
+ ASSERT(pp->sched.taskq.bpq);
+
+ if (size == 0)
+ return;
+
+ bpq = pp->sched.taskq.bpq;
+
+ qsz = (ErlDrvSizeT) erts_smp_atomic_add_read_acqb(&bpq->size,
+ (erts_aint_t) -size);
+ ASSERT(qsz + size > qsz);
+ flags = erts_smp_atomic32_read_nob(&pp->sched.flags);
+ ASSERT(pp->sched.taskq.bpq);
+ if ((flags & (ERTS_PTS_FLG_CHK_UNSET_BUSY_PORT_Q
+ | ERTS_PTS_FLG_BUSY_PORT_Q)) != ERTS_PTS_FLG_BUSY_PORT_Q)
+ return;
+ if (qsz < (ErlDrvSizeT) erts_smp_atomic_read_nob(&bpq->low))
+ erts_smp_atomic32_read_bor_nob(&pp->sched.flags,
+ ERTS_PTS_FLG_CHK_UNSET_BUSY_PORT_Q);
+}
+
+static ERTS_INLINE void
+dequeued_proc2port_data(Port *pp, ErlDrvSizeT size)
+{
+ ErtsPortTaskBusyPortQ *bpq;
+ erts_aint32_t flags;
+ ErlDrvSizeT qsz;
+
+ ASSERT(pp->sched.taskq.bpq);
+
+ if (size == 0)
+ return;
+
+ bpq = pp->sched.taskq.bpq;
+
+ qsz = (ErlDrvSizeT) erts_smp_atomic_add_read_acqb(&bpq->size,
+ (erts_aint_t) -size);
+ ASSERT(qsz + size > qsz);
+ flags = erts_smp_atomic32_read_nob(&pp->sched.flags);
+ if (!(flags & ERTS_PTS_FLG_BUSY_PORT_Q))
+ return;
+ if (qsz < (ErlDrvSizeT) erts_smp_atomic_read_acqb(&bpq->low))
+ check_unset_busy_port_q(pp, flags, bpq);
+}
+
+static ERTS_INLINE erts_aint32_t
+enqueue_proc2port_data(Port *pp,
+ ErtsProc2PortSigData *sigdp,
+ erts_aint32_t flags)
+{
+ ErtsPortTaskBusyPortQ *bpq = pp->sched.taskq.bpq;
+ if (sigdp && bpq) {
+ ErlDrvSizeT size = erts_proc2port_sig_command_data_size(sigdp);
+ if (size) {
+ erts_aint_t asize = erts_smp_atomic_add_read_acqb(&bpq->size,
+ (erts_aint_t) size);
+ ErlDrvSizeT qsz = (ErlDrvSizeT) asize;
+
+ ASSERT(qsz - size < qsz);
+
+ if (!(flags & ERTS_PTS_FLG_BUSY_PORT_Q) && qsz > bpq->high) {
+ flags = erts_smp_atomic32_read_bor_acqb(&pp->sched.flags,
+ ERTS_PTS_FLG_BUSY_PORT_Q);
+ flags |= ERTS_PTS_FLG_BUSY_PORT_Q;
+ qsz = (ErlDrvSizeT) erts_smp_atomic_read_acqb(&bpq->size);
+ if (qsz < (ErlDrvSizeT) erts_smp_atomic_read_nob(&bpq->low)) {
+ flags = (erts_smp_atomic32_read_bor_relb(
+ &pp->sched.flags,
+ ERTS_PTS_FLG_CHK_UNSET_BUSY_PORT_Q));
+ flags |= ERTS_PTS_FLG_CHK_UNSET_BUSY_PORT_Q;
+ }
+ }
+ ASSERT(!(flags & ERTS_PTS_FLG_EXIT));
+ }
+ }
+ return flags;
+}
+
+/*
+ * erl_drv_busy_msgq_limits() is called by drivers either reading or
+ * writing the limits.
+ *
+ * A limit of zero is interpreted as a read only request (using a
+ * limit of zero would not be useful). Other values are interpreted
+ * as a write-read request.
+ */
+
+void
+erl_drv_busy_msgq_limits(ErlDrvPort dport, ErlDrvSizeT *lowp, ErlDrvSizeT *highp)
+{
+ Port *pp = erts_drvport2port(dport, NULL);
+ ErtsPortTaskBusyPortQ *bpq = pp->sched.taskq.bpq;
+ int written = 0, resume_procs = 0;
+ ErlDrvSizeT low, high;
+
+ if (!pp || !bpq) {
+ if (lowp)
+ *lowp = ERL_DRV_BUSY_MSGQ_DISABLED;
+ if (highp)
+ *highp = ERL_DRV_BUSY_MSGQ_DISABLED;
+ return;
+ }
+
+ low = lowp ? *lowp : 0;
+ high = highp ? *highp : 0;
+
+ erts_port_task_sched_lock(&pp->sched);
+
+ if (low == ERL_DRV_BUSY_MSGQ_DISABLED
+ || high == ERL_DRV_BUSY_MSGQ_DISABLED) {
+ /* Disable busy msgq feature */
+ erts_aint32_t flags;
+ pp->sched.taskq.bpq = NULL;
+ flags = ~(ERTS_PTS_FLG_BUSY_PORT_Q|ERTS_PTS_FLG_CHK_UNSET_BUSY_PORT_Q);
+ flags = erts_smp_atomic32_read_band_acqb(&pp->sched.flags, flags);
+ if ((flags & ERTS_PTS_FLGS_BUSY) == ERTS_PTS_FLG_BUSY_PORT_Q)
+ resume_procs = 1;
+ }
+ else {
+
+ if (!low)
+ low = (ErlDrvSizeT) erts_smp_atomic_read_nob(&bpq->low);
+ else {
+ if (bpq->high < low)
+ bpq->high = low;
+ erts_smp_atomic_set_relb(&bpq->low, (erts_aint_t) low);
+ written = 1;
+ }
+
+ if (!high)
+ high = bpq->high;
+ else {
+ if (low > high) {
+ low = high;
+ erts_smp_atomic_set_relb(&bpq->low, (erts_aint_t) low);
+ }
+ bpq->high = high;
+ written = 1;
+ }
+
+ if (written) {
+ ErlDrvSizeT size = (ErlDrvSizeT) erts_smp_atomic_read_nob(&bpq->size);
+ if (size > high)
+ erts_smp_atomic32_read_bor_relb(&pp->sched.flags,
+ ERTS_PTS_FLG_BUSY_PORT_Q);
+ else if (size < low)
+ erts_smp_atomic32_read_bor_relb(&pp->sched.flags,
+ ERTS_PTS_FLG_CHK_UNSET_BUSY_PORT_Q);
+ }
+ }
+
+ erts_port_task_sched_unlock(&pp->sched);
+
+ if (resume_procs)
+ erts_port_resume_procs(pp);
+ if (lowp)
+ *lowp = low;
+ if (highp)
+ *highp = high;
+}
+
+/*
+ * No-suspend handles.
+ */
+
+#ifdef ERTS_SMP
+static void
+free_port_task_handle_list(void *vpthlp)
+{
+ erts_free(ERTS_ALC_T_PT_HNDL_LIST, vpthlp);
+}
+#endif
+
+static void
+schedule_port_task_handle_list_free(ErtsPortTaskHandleList *pthlp)
+{
+#ifdef ERTS_SMP
+ erts_schedule_thr_prgr_later_op(free_port_task_handle_list,
+ (void *) pthlp,
+ &pthlp->u.release);
+#else
+ erts_free(ERTS_ALC_T_PT_HNDL_LIST, pthlp);
+#endif
+}
+
+static ERTS_INLINE void
+abort_nosuspend_task(Port *pp,
+ ErtsPortTaskType type,
+ ErtsPortTaskTypeData *tdp)
+{
+
+ ASSERT(type == ERTS_PORT_TASK_PROC_SIG);
+
+ if (!pp->sched.taskq.bpq)
+ tdp->psig.callback(NULL,
+ ERTS_PORT_SFLG_INVALID,
+ ERTS_PROC2PORT_SIG_ABORT_NOSUSPEND,
+ &tdp->psig.data);
+ else {
+ ErlDrvSizeT size = erts_proc2port_sig_command_data_size(&tdp->psig.data);
+ tdp->psig.callback(NULL,
+ ERTS_PORT_SFLG_INVALID,
+ ERTS_PROC2PORT_SIG_ABORT_NOSUSPEND,
+ &tdp->psig.data);
+ aborted_proc2port_data(pp, size);
+ }
+}
+
+static ErtsPortTaskHandleList *
+get_free_nosuspend_handles(Port *pp)
+{
+ ErtsPortTaskHandleList *nshp, *last_nshp = NULL;
+
+ ERTS_SMP_LC_ASSERT(erts_port_task_sched_lock_is_locked(&pp->sched));
+
+ nshp = pp->sched.taskq.local.busy.nosuspend;
+
+ while (nshp && !erts_port_task_is_scheduled(&nshp->handle)) {
+ last_nshp = nshp;
+ nshp = nshp->u.next;
+ }
+
+ if (!last_nshp)
+ nshp = NULL;
+ else {
+ nshp = pp->sched.taskq.local.busy.nosuspend;
+ pp->sched.taskq.local.busy.nosuspend = last_nshp->u.next;
+ last_nshp->u.next = NULL;
+ if (!pp->sched.taskq.local.busy.nosuspend)
+ erts_smp_atomic32_read_band_nob(&pp->sched.flags,
+ ~ERTS_PTS_FLG_HAVE_NS_TASKS);
+ }
+ return nshp;
+}
+
+static void
+free_nosuspend_handles(ErtsPortTaskHandleList *free_nshp)
+{
+ while (free_nshp) {
+ ErtsPortTaskHandleList *nshp = free_nshp;
+ free_nshp = free_nshp->u.next;
+ schedule_port_task_handle_list_free(nshp);
}
}
@@ -161,7 +844,6 @@ enqueue_port(ErtsRunQueue *runq, Port *pp)
{
ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(runq));
pp->sched.next = NULL;
- pp->sched.in_runq = 1;
if (runq->ports.end) {
ASSERT(runq->ports.start);
runq->ports.end->sched.next = pp;
@@ -199,285 +881,423 @@ pop_port(ErtsRunQueue *runq)
return pp;
}
+/*
+ * Task queue operations
+ */
-#ifdef HARD_DEBUG
+static ERTS_INLINE int
+enqueue_task(Port *pp,
+ ErtsPortTask *ptp,
+ ErtsProc2PortSigData *sigdp,
+ ErtsPortTaskHandleList *ns_pthlp,
+ erts_aint32_t *flagsp)
-static void
-check_port_queue(ErtsRunQueue *runq, Port *chk_pp, int inq)
{
- Port *pp;
- Port *last_pp;
- Port *first_pp = runq->ports.start;
- int no_forward = 0, no_backward = 0;
- int found_forward = 0, found_backward = 0;
- if (!first_pp) {
- ASSERT(!runq->ports.end);
- }
+ int res;
+ erts_aint32_t fail_flags = ERTS_PTS_FLG_EXIT;
+ erts_aint32_t flags;
+ ptp->u.alive.next = NULL;
+ if (ns_pthlp)
+ fail_flags |= ERTS_PTS_FLG_BUSY_PORT;
+ erts_port_task_sched_lock(&pp->sched);
+ flags = erts_smp_atomic32_read_nob(&pp->sched.flags);
+ if (flags & fail_flags)
+ res = 0;
else {
- ASSERT(!first_pp->sched.prev);
- for (pp = first_pp; pp; pp = pp->sched.next) {
- ASSERT(pp->sched.taskq);
- if (pp->sched.taskq->first)
- no_forward++;
- if (chk_pp == pp)
- found_forward = 1;
- if (!pp->sched.prev) {
- ASSERT(first_pp == pp);
- }
- if (!pp->sched.next) {
- ASSERT(runq->ports.end == pp);
- last_pp = pp;
- }
- }
- for (pp = last_pp; pp; pp = pp->sched.prev) {
- ASSERT(pp->sched.taskq);
- if (pp->sched.taskq->last)
- no_backward++;
- if (chk_pp == pp)
- found_backward = 1;
- if (!pp->sched.prev) {
- ASSERT(first_pp == pp);
- }
- if (!pp->sched.next) {
- ASSERT(runq->ports.end == pp);
- }
- check_task_queue(pp->sched.taskq, NULL, 0);
- }
- ASSERT(no_forward == no_backward);
- }
- ASSERT(no_forward == RUNQ_READ_LEN(&runq->ports.info.len));
- if (chk_pp) {
- if (chk_pp->sched.taskq || chk_pp->sched.exe_taskq) {
- ASSERT(chk_pp->sched.taskq != chk_pp->sched.exe_taskq);
+ if (ns_pthlp) {
+ ns_pthlp->u.next = pp->sched.taskq.local.busy.nosuspend;
+ pp->sched.taskq.local.busy.nosuspend = ns_pthlp;
}
- ASSERT(!chk_pp->sched.taskq || chk_pp->sched.taskq->first);
- if (inq < 0)
- inq = chk_pp->sched.taskq && !chk_pp->sched.exe_taskq;
- if (inq) {
- ASSERT(found_forward && found_backward);
+ if (pp->sched.taskq.in.last) {
+ ASSERT(pp->sched.taskq.in.first);
+ ASSERT(!pp->sched.taskq.in.last->u.alive.next);
+
+ pp->sched.taskq.in.last->u.alive.next = ptp;
}
else {
- ASSERT(!found_forward && !found_backward);
- }
- }
-}
-
-#endif
-
-/*
- * Task queue operations
- */
+ ASSERT(!pp->sched.taskq.in.first);
-static ERTS_INLINE ErtsPortTaskQueue *
-port_taskq_init(ErtsPortTaskQueue *ptqp, Port *pp)
-{
- if (ptqp) {
- ptqp->first = NULL;
- ptqp->last = NULL;
- ptqp->port = pp;
+ pp->sched.taskq.in.first = ptp;
+ }
+ pp->sched.taskq.in.last = ptp;
+ flags = enqueue_proc2port_data(pp, sigdp, flags);
+ res = 1;
}
- return ptqp;
+ erts_port_task_sched_unlock(&pp->sched);
+ *flagsp = flags;
+ return res;
}
static ERTS_INLINE void
-enqueue_task(ErtsPortTaskQueue *ptqp, ErtsPortTask *ptp)
+prepare_exec(Port *pp, ErtsPortTask **execqp, int *processing_busy_q_p)
{
- ERTS_PT_CHK_NOT_IN_TASKQ(ptqp, ptp);
- ptp->next = NULL;
- ptp->prev = ptqp->last;
- ptp->queue = ptqp;
- if (ptqp->last) {
- ASSERT(ptqp->first);
- ptqp->last->next = ptp;
+ erts_aint32_t act = erts_smp_atomic32_read_nob(&pp->sched.flags);
+
+ if (!pp->sched.taskq.local.busy.first || (act & ERTS_PTS_FLG_BUSY_PORT)) {
+ *execqp = pp->sched.taskq.local.first;
+ *processing_busy_q_p = 0;
}
else {
- ASSERT(!ptqp->first);
- ptqp->first = ptp;
+ *execqp = pp->sched.taskq.local.busy.first;
+ *processing_busy_q_p = 1;
+ }
+
+ ERTS_PT_DBG_CHK_TASK_QS(pp, *execqp, *processing_busy_q_p);
+
+ while (1) {
+ erts_aint32_t new, exp;
+
+ new = exp = act;
+
+ new &= ~ERTS_PTS_FLG_IN_RUNQ;
+ new |= ERTS_PTS_FLG_EXEC;
+
+ act = erts_smp_atomic32_cmpxchg_nob(&pp->sched.flags, new, exp);
+
+ ASSERT(act & ERTS_PTS_FLG_IN_RUNQ);
+
+ if (exp == act)
+ break;
}
- ptqp->last = ptp;
- ERTS_PT_CHK_IN_TASKQ(ptqp, ptp);
}
-static ERTS_INLINE void
-push_task(ErtsPortTaskQueue *ptqp, ErtsPortTask *ptp)
+/* finalize_exec() return value != 0 if port should remain active */
+static ERTS_INLINE int
+finalize_exec(Port *pp, ErtsPortTask **execq, int processing_busy_q)
{
- ERTS_PT_CHK_NOT_IN_TASKQ(ptqp, ptp);
- ptp->next = ptqp->first;
- ptp->prev = NULL;
- ptp->queue = ptqp;
- if (ptqp->first) {
- ASSERT(ptqp->last);
- ptqp->first->prev = ptp;
- }
+ erts_aint32_t act;
+
+ if (!processing_busy_q)
+ pp->sched.taskq.local.first = *execq;
else {
- ASSERT(!ptqp->last);
- ptqp->last = ptp;
+ pp->sched.taskq.local.busy.first = *execq;
+ ASSERT(*execq);
}
- ptqp->first = ptp;
- ERTS_PT_CHK_IN_TASKQ(ptqp, ptp);
+
+ ERTS_PT_DBG_CHK_TASK_QS(pp, *execq, processing_busy_q);
+
+ *execq = NULL;
+
+ act = erts_smp_atomic32_read_nob(&pp->sched.flags);
+ if (act & ERTS_PTS_FLG_CHK_UNSET_BUSY_PORT_Q)
+ act = check_unset_busy_port_q(pp, act, pp->sched.taskq.bpq);
+
+ while (1) {
+ erts_aint32_t new, exp;
+
+ new = exp = act;
+
+ new &= ~ERTS_PTS_FLG_EXEC;
+ if (act & ERTS_PTS_FLG_HAVE_TASKS)
+ new |= ERTS_PTS_FLG_IN_RUNQ;
+
+ act = erts_smp_atomic32_cmpxchg_relb(&pp->sched.flags, new, exp);
+
+ ASSERT(!(act & ERTS_PTS_FLG_IN_RUNQ));
+
+ if (exp == act)
+ break;
+ }
+
+ return (act & ERTS_PTS_FLG_HAVE_TASKS) != 0;
}
-static ERTS_INLINE void
-dequeue_task(ErtsPortTask *ptp)
+static ERTS_INLINE erts_aint32_t
+select_queue_for_exec(Port *pp, ErtsPortTask **execqp, int *processing_busy_q_p)
{
- ASSERT(ptp);
- ASSERT(ptp->queue);
- ERTS_PT_CHK_IN_TASKQ(ptp->queue, ptp);
- if (ptp->next)
- ptp->next->prev = ptp->prev;
- else {
- ASSERT(ptp->queue->last == ptp);
- ptp->queue->last = ptp->prev;
+ erts_aint32_t flags = erts_smp_atomic32_read_nob(&pp->sched.flags);
+
+ if (flags & ERTS_PTS_FLG_CHK_UNSET_BUSY_PORT_Q)
+ flags = check_unset_busy_port_q(pp, flags, pp->sched.taskq.bpq);
+
+ ERTS_PT_DBG_CHK_TASK_QS(pp, *execqp, *processing_busy_q_p);
+
+ if (flags & ERTS_PTS_FLG_BUSY_PORT) {
+ if (*processing_busy_q_p) {
+ ErtsPortTask *ptp;
+
+ ptp = pp->sched.taskq.local.busy.first = *execqp;
+ if (!ptp)
+ pp->sched.taskq.local.busy.last = NULL;
+ else if (!(ptp->u.alive.flags & ERTS_PT_FLG_WAIT_BUSY))
+ no_sig_dep_move_from_busyq(pp);
+
+ *execqp = pp->sched.taskq.local.first;
+ *processing_busy_q_p = 0;
+
+ ERTS_PT_DBG_CHK_TASK_QS(pp, *execqp, *processing_busy_q_p);
+ }
+
+ return flags;
}
- if (ptp->prev)
- ptp->prev->next = ptp->next;
- else {
- ASSERT(ptp->queue->first == ptp);
- ptp->queue->first = ptp->next;
+
+ /* Not busy */
+
+ if (!*processing_busy_q_p && pp->sched.taskq.local.busy.first) {
+ pp->sched.taskq.local.first = *execqp;
+ *execqp = pp->sched.taskq.local.busy.first;
+ *processing_busy_q_p = 1;
+
+ ERTS_PT_DBG_CHK_TASK_QS(pp, *execqp, *processing_busy_q_p);
}
- ASSERT(ptp->queue->first || !ptp->queue->last);
- ASSERT(ptp->queue->last || !ptp->queue->first);
- ERTS_PT_CHK_NOT_IN_TASKQ(ptp->queue, ptp);
+ return flags;
}
-static ERTS_INLINE ErtsPortTask *
-pop_task(ErtsPortTaskQueue *ptqp)
+/*
+ * check_task_for_exec() returns a value !0 if the task
+ * is ok to execute; otherwise 0.
+ */
+static ERTS_INLINE int
+check_task_for_exec(Port *pp,
+ erts_aint32_t flags,
+ ErtsPortTask **execqp,
+ int *processing_busy_q_p,
+ ErtsPortTask *ptp)
{
- ErtsPortTask *ptp = ptqp->first;
- if (!ptp) {
- ASSERT(!ptqp->last);
+
+ if (!*processing_busy_q_p) {
+ /* Processing normal queue */
+
+ ERTS_PT_DBG_CHK_TASK_QS(pp, ptp, *processing_busy_q_p);
+
+ if ((flags & ERTS_PTS_FLG_BUSY_PORT)
+ && (ptp->u.alive.flags & ERTS_PT_FLG_WAIT_BUSY)) {
+
+ busy_wait_move_to_busy_queue(pp, ptp);
+ ERTS_PT_DBG_CHK_TASK_QS(pp, *execqp, *processing_busy_q_p);
+
+ return 0;
+ }
+
+ if (pp->sched.taskq.local.busy.last
+ && (ptp->u.alive.flags & ERTS_PT_FLG_SIG_DEP)) {
+
+ int res = !check_sig_dep_move_to_busy_queue(pp, ptp);
+ ERTS_PT_DBG_CHK_TASK_QS(pp, *execqp, *processing_busy_q_p);
+
+ return res;
+ }
+
}
else {
- ERTS_PT_CHK_IN_TASKQ(ptqp, ptp);
- ASSERT(!ptp->prev);
- ptqp->first = ptp->next;
- if (ptqp->first)
- ptqp->first->prev = NULL;
- else {
- ASSERT(ptqp->last == ptp);
- ptqp->last = NULL;
+ /* Processing busy queue */
+
+ ASSERT(!(flags & ERTS_PTS_FLG_BUSY_PORT));
+
+ ERTS_PT_DBG_CHK_TASK_QS(pp, ptp, *processing_busy_q_p);
+
+ popped_from_busy_queue(pp, ptp, !*execqp);
+
+ if (!*execqp) {
+ *execqp = pp->sched.taskq.local.first;
+ *processing_busy_q_p = 0;
}
- ASSERT(ptp->queue->first || !ptp->queue->last);
- ASSERT(ptp->queue->last || !ptp->queue->first);
+
+ ERTS_PT_DBG_CHK_TASK_QS(pp, *execqp, *processing_busy_q_p);
+
}
- ERTS_PT_CHK_NOT_IN_TASKQ(ptqp, ptp);
- return ptp;
+
+ return 1;
}
-#ifdef HARD_DEBUG
+static ErtsPortTask *
+fetch_in_queue(Port *pp, ErtsPortTask **execqp)
+{
+ ErtsPortTask *ptp;
+ ErtsPortTaskHandleList *free_nshp = NULL;
+
+ erts_port_task_sched_lock(&pp->sched);
-static void
-check_task_queue(ErtsPortTaskQueue *ptqp,
- ErtsPortTask *chk_ptp,
- int inq)
+ ptp = pp->sched.taskq.in.first;
+ pp->sched.taskq.in.first = NULL;
+ pp->sched.taskq.in.last = NULL;
+ if (ptp)
+ *execqp = ptp->u.alive.next;
+ else
+ erts_smp_atomic32_read_band_nob(&pp->sched.flags,
+ ~ERTS_PTS_FLG_HAVE_TASKS);
+
+
+ if (pp->sched.taskq.local.busy.nosuspend)
+ free_nshp = get_free_nosuspend_handles(pp);
+
+ erts_port_task_sched_unlock(&pp->sched);
+
+ if (free_nshp)
+ free_nosuspend_handles(free_nshp);
+
+ return ptp;
+}
+
+static ERTS_INLINE ErtsPortTask *
+select_task_for_exec(Port *pp,
+ ErtsPortTask **execqp,
+ int *processing_busy_q_p)
{
ErtsPortTask *ptp;
- ErtsPortTask *last_ptp;
- ErtsPortTask *first_ptp = ptqp->first;
- int found_forward = 0, found_backward = 0;
- if (!first_ptp) {
- ASSERT(!ptqp->last);
- }
- else {
- ASSERT(!first_ptp->prev);
- for (ptp = first_ptp; ptp; ptp = ptp->next) {
- ASSERT(ptp->queue == ptqp);
- if (chk_ptp == ptp)
- found_forward = 1;
- if (!ptp->prev) {
- ASSERT(first_ptp == ptp);
- }
- if (!ptp->next) {
- ASSERT(ptqp->last == ptp);
- last_ptp = ptp;
- }
- }
- for (ptp = last_ptp; ptp; ptp = ptp->prev) {
- ASSERT(ptp->queue == ptqp);
- if (chk_ptp == ptp)
- found_backward = 1;
- if (!ptp->prev) {
- ASSERT(first_ptp == ptp);
- }
- if (!ptp->next) {
- ASSERT(ptqp->last == ptp);
- }
- }
- }
- if (chk_ptp) {
- if (inq) {
- ASSERT(found_forward && found_backward);
- }
+ erts_aint32_t flags;
+
+ flags = select_queue_for_exec(pp, execqp, processing_busy_q_p);
+
+ while (1) {
+ ptp = *execqp;
+ if (ptp)
+ *execqp = ptp->u.alive.next;
else {
- ASSERT(!found_forward && !found_backward);
+ ptp = fetch_in_queue(pp, execqp);
+ if (!ptp)
+ return NULL;
}
+ if (check_task_for_exec(pp, flags, execqp, processing_busy_q_p, ptp))
+ return ptp;
}
}
-#endif
/*
* Abort a scheduled task.
*/
int
-erts_port_task_abort(Eterm id, ErtsPortTaskHandle *pthp)
+erts_port_task_abort(ErtsPortTaskHandle *pthp)
{
- ErtsRunQueue *runq;
- ErtsPortTaskQueue *ptqp;
+ int res;
ErtsPortTask *ptp;
- Port *pp;
-
- pp = &erts_port[internal_port_index(id)];
- runq = erts_port_runq(pp);
- if (!runq)
- return 1;
+#ifdef ERTS_SMP
+ ErtsThrPrgrDelayHandle dhndl = erts_thr_progress_unmanaged_delay();
+#endif
ptp = handle2task(pthp);
+ if (!ptp)
+ res = -1;
+ else {
+ erts_aint32_t old_state;
+
+#ifdef DEBUG
+ ErtsPortTaskHandle *saved_pthp = ptp->u.alive.handle;
+ ERTS_SMP_READ_MEMORY_BARRIER;
+ old_state = erts_smp_atomic32_read_nob(&ptp->state);
+ if (old_state == ERTS_PT_STATE_SCHEDULED) {
+ ASSERT(saved_pthp == pthp);
+ }
+#endif
- if (!ptp) {
- erts_smp_runq_unlock(runq);
- return 1;
+ old_state = erts_smp_atomic32_cmpxchg_nob(&ptp->state,
+ ERTS_PT_STATE_ABORTED,
+ ERTS_PT_STATE_SCHEDULED);
+ if (old_state != ERTS_PT_STATE_SCHEDULED)
+ res = - 1; /* Task already aborted, executing, or executed */
+ else {
+
+ reset_port_task_handle(pthp);
+
+ switch (ptp->type) {
+ case ERTS_PORT_TASK_INPUT:
+ case ERTS_PORT_TASK_OUTPUT:
+ case ERTS_PORT_TASK_EVENT:
+ ASSERT(erts_smp_atomic_read_nob(
+ &erts_port_task_outstanding_io_tasks) > 0);
+ erts_smp_atomic_dec_relb(&erts_port_task_outstanding_io_tasks);
+ break;
+ case ERTS_PORT_TASK_PROC_SIG:
+ ERTS_INTERNAL_ERROR("Aborted process to port signal");
+ break;
+ default:
+ break;
+ }
+
+ res = 0;
+ }
}
- ASSERT(ptp->handle == pthp);
- ptqp = ptp->queue;
- ASSERT(pp == ptqp->port);
+#ifdef ERTS_SMP
+ erts_thr_progress_unmanaged_continue(dhndl);
+#endif
- ERTS_PT_CHK_PRES_PORTQ(runq, pp);
- ASSERT(ptqp);
- ASSERT(ptqp->first);
+ return res;
+}
- dequeue_task(ptp);
- reset_handle(ptp);
+void
+erts_port_task_abort_nosuspend_tasks(Port *pp)
+{
+ erts_aint32_t flags;
+ ErtsPortTaskHandleList *abort_list;
+#ifdef ERTS_SMP
+ ErtsThrPrgrDelayHandle dhndl = ERTS_THR_PRGR_DHANDLE_INVALID;
+#endif
- switch (ptp->type) {
- case ERTS_PORT_TASK_INPUT:
- case ERTS_PORT_TASK_OUTPUT:
- case ERTS_PORT_TASK_EVENT:
- ASSERT(erts_smp_atomic_read_nob(&erts_port_task_outstanding_io_tasks) > 0);
- erts_smp_atomic_dec_relb(&erts_port_task_outstanding_io_tasks);
- break;
- default:
- break;
- }
+ erts_port_task_sched_lock(&pp->sched);
+ flags = erts_smp_atomic32_read_band_nob(&pp->sched.flags,
+ ~ERTS_PTS_FLG_HAVE_NS_TASKS);
+ abort_list = pp->sched.taskq.local.busy.nosuspend;
+ pp->sched.taskq.local.busy.nosuspend = NULL;
+ erts_port_task_sched_unlock(&pp->sched);
- ASSERT(ptqp == pp->sched.taskq || ptqp == pp->sched.exe_taskq);
+ while (abort_list) {
+#ifdef DEBUG
+ ErtsPortTaskHandle *saved_pthp;
+#endif
+ ErtsPortTaskType type;
+ ErtsPortTaskTypeData td;
+ ErtsPortTaskHandle *pthp;
+ ErtsPortTask *ptp;
+ ErtsPortTaskHandleList *pthlp;
+ erts_aint32_t old_state;
- if (ptqp->first || pp->sched.taskq != ptqp)
- ptqp = NULL;
- else
- pp->sched.taskq = NULL;
+ pthlp = abort_list;
+ abort_list = pthlp->u.next;
- ERTS_PT_CHK_PRES_PORTQ(runq, pp);
+#ifdef ERTS_SMP
+ if (dhndl != ERTS_THR_PRGR_DHANDLE_MANAGED)
+ dhndl = erts_thr_progress_unmanaged_delay();
+#endif
- erts_smp_runq_unlock(runq);
+ pthp = &pthlp->handle;
+ ptp = handle2task(pthp);
+ if (!ptp) {
+#ifdef ERTS_SMP
+ if (dhndl != ERTS_THR_PRGR_DHANDLE_MANAGED)
+ erts_thr_progress_unmanaged_continue(dhndl);
+#endif
+ schedule_port_task_handle_list_free(pthlp);
+ continue;
+ }
- port_task_free(ptp);
- if (ptqp)
- port_taskq_free(ptqp);
+#ifdef DEBUG
+ saved_pthp = ptp->u.alive.handle;
+ ERTS_SMP_READ_MEMORY_BARRIER;
+ old_state = erts_smp_atomic32_read_nob(&ptp->state);
+ if (old_state == ERTS_PT_STATE_SCHEDULED) {
+ ASSERT(saved_pthp == pthp);
+ }
+#endif
- return 0;
+ old_state = erts_smp_atomic32_cmpxchg_nob(&ptp->state,
+ ERTS_PT_STATE_ABORTED,
+ ERTS_PT_STATE_SCHEDULED);
+ if (old_state != ERTS_PT_STATE_SCHEDULED) {
+ /* Task already aborted, executing, or executed */
+#ifdef ERTS_SMP
+ if (dhndl != ERTS_THR_PRGR_DHANDLE_MANAGED)
+ erts_thr_progress_unmanaged_continue(dhndl);
+#endif
+ schedule_port_task_handle_list_free(pthlp);
+ continue;
+ }
+
+ reset_port_task_handle(pthp);
+
+ type = ptp->type;
+ td = ptp->u.alive.td;
+
+#ifdef ERTS_SMP
+ if (dhndl != ERTS_THR_PRGR_DHANDLE_MANAGED)
+ erts_thr_progress_unmanaged_continue(dhndl);
+#endif
+ schedule_port_task_handle_list_free(pthlp);
+
+ abort_nosuspend_task(pp, type, &td);
+ }
}
/*
@@ -488,240 +1308,265 @@ int
erts_port_task_schedule(Eterm id,
ErtsPortTaskHandle *pthp,
ErtsPortTaskType type,
- ErlDrvEvent event,
- ErlDrvEventData event_data)
+ ...)
{
+ ErtsProc2PortSigData *sigdp = NULL;
+ ErtsPortTaskHandleList *ns_pthlp = NULL;
+#ifdef ERTS_SMP
+ ErtsRunQueue *xrunq;
+ ErtsThrPrgrDelayHandle dhndl;
+#endif
ErtsRunQueue *runq;
Port *pp;
- ErtsPortTask *ptp;
- int enq_port = 0;
-
- /*
- * NOTE: We might not have the port lock here. We are only
- * allowed to access the 'sched', 'tab_status',
- * and 'id' fields of the port struct while
- * tasks_lock is held.
- */
+ ErtsPortTask *ptp = NULL;
+ erts_aint32_t act, add_flags;
if (pthp && erts_port_task_is_scheduled(pthp)) {
ASSERT(0);
- erts_port_task_abort(id, pthp);
+ erts_port_task_abort(pthp);
}
- ptp = port_task_alloc();
-
ASSERT(is_internal_port(id));
- pp = &erts_port[internal_port_index(id)];
- runq = erts_port_runq(pp);
-
- if (!runq || ERTS_PORT_TASK_INVALID_PORT(pp, id)) {
- if (runq)
- erts_smp_runq_unlock(runq);
- return -1;
- }
-
- ASSERT(!erts_port_task_is_scheduled(pthp));
- ERTS_PT_CHK_PRES_PORTQ(runq, pp);
+#ifdef ERTS_SMP
+ dhndl = erts_thr_progress_unmanaged_delay();
+#endif
- if (!pp->sched.taskq) {
- pp->sched.taskq = port_taskq_init(port_taskq_alloc(), pp);
- enq_port = !pp->sched.in_runq && !pp->sched.exe_taskq;
- }
+ pp = erts_port_lookup_raw(id);
#ifdef ERTS_SMP
- if (enq_port) {
- ErtsRunQueue *xrunq = erts_check_emigration_need(runq, ERTS_PORT_PRIO_LEVEL);
- if (xrunq) {
- /* Port emigrated ... */
- erts_smp_atomic_set_nob(&pp->run_queue, (erts_aint_t) xrunq);
- erts_smp_runq_unlock(runq);
- runq = erts_port_runq(pp);
- if (!runq)
- return -1;
- }
+ if (dhndl != ERTS_THR_PRGR_DHANDLE_MANAGED) {
+ if (pp)
+ erts_port_inc_refc(pp);
+ erts_thr_progress_unmanaged_continue(dhndl);
}
#endif
- ASSERT(pp->sched.taskq);
- ASSERT(ptp);
+ if (!pp)
+ goto fail;
+
+ if (type != ERTS_PORT_TASK_PROC_SIG) {
+ ptp = port_task_alloc();
- ptp->type = type;
- ptp->event = event;
- ptp->event_data = event_data;
+ ptp->type = type;
+ ptp->u.alive.flags = 0;
- set_handle(ptp, pthp);
+ erts_smp_atomic32_init_nob(&ptp->state, ERTS_PT_STATE_SCHEDULED);
+
+ set_handle(ptp, pthp);
+ }
switch (type) {
- case ERTS_PORT_TASK_FREE:
- erl_exit(ERTS_ABORT_EXIT,
- "erts_port_task_schedule(): Cannot schedule free task\n");
- break;
case ERTS_PORT_TASK_INPUT:
- case ERTS_PORT_TASK_OUTPUT:
- case ERTS_PORT_TASK_EVENT:
+ case ERTS_PORT_TASK_OUTPUT: {
+ va_list argp;
+ va_start(argp, type);
+ ptp->u.alive.td.io.event = va_arg(argp, ErlDrvEvent);
+ va_end(argp);
+ erts_smp_atomic_inc_relb(&erts_port_task_outstanding_io_tasks);
+ break;
+ }
+ case ERTS_PORT_TASK_EVENT: {
+ va_list argp;
+ va_start(argp, type);
+ ptp->u.alive.td.io.event = va_arg(argp, ErlDrvEvent);
+ ptp->u.alive.td.io.event_data = va_arg(argp, ErlDrvEventData);
+ va_end(argp);
erts_smp_atomic_inc_relb(&erts_port_task_outstanding_io_tasks);
- /* Fall through... */
+ break;
+ }
+ case ERTS_PORT_TASK_PROC_SIG: {
+ va_list argp;
+ ASSERT(!pthp);
+ va_start(argp, type);
+ sigdp = va_arg(argp, ErtsProc2PortSigData *);
+ ptp = p2p_sig_data_to_task(sigdp);
+ ptp->u.alive.td.psig.callback = va_arg(argp, ErtsProc2PortSigCallback);
+ ptp->u.alive.flags |= va_arg(argp, int);
+ va_end(argp);
+ if (!(ptp->u.alive.flags & ERTS_PT_FLG_NOSUSPEND))
+ set_handle(ptp, pthp);
+ else {
+ ns_pthlp = erts_alloc(ERTS_ALC_T_PT_HNDL_LIST,
+ sizeof(ErtsPortTaskHandleList));
+ set_handle(ptp, &ns_pthlp->handle);
+ }
+ break;
+ }
default:
- enqueue_task(pp->sched.taskq, ptp);
break;
}
-#ifndef ERTS_SMP
- /*
- * When (!enq_port && !pp->sched.exe_taskq) is true in the smp case,
- * the port might not be in the run queue. If this is the case, another
- * thread is in the process of enqueueing the port. This very seldom
- * occur, but do occur and is a valid scenario. Debug info showing this
- * enqueue in progress must be introduced before we can enable (modified
- * versions of these) assertions in the smp case again.
- */
-#if defined(HARD_DEBUG)
- if (pp->sched.exe_taskq || enq_port)
- ERTS_PT_CHK_NOT_IN_PORTQ(runq, pp);
- else
- ERTS_PT_CHK_IN_PORTQ(runq, pp);
-#elif defined(DEBUG)
- if (!enq_port && !pp->sched.exe_taskq) {
- /* We should be in port run q */
- ASSERT(pp->sched.in_runq);
+ if (!enqueue_task(pp, ptp, sigdp, ns_pthlp, &act)) {
+ reset_handle(ptp);
+ if (ns_pthlp && !(act & ERTS_PTS_FLG_EXIT))
+ goto abort_nosuspend;
+ else
+ goto fail;
}
-#endif
-#endif
- if (!enq_port) {
- ERTS_PT_CHK_PRES_PORTQ(runq, pp);
- erts_smp_runq_unlock(runq);
- }
- else {
- enqueue_port(runq, pp);
- ERTS_PT_CHK_PRES_PORTQ(runq, pp);
-
- if (erts_system_profile_flags.runnable_ports) {
- profile_runnable_port(pp, am_active);
+ add_flags = ERTS_PTS_FLG_HAVE_TASKS;
+ if (ns_pthlp)
+ add_flags |= ERTS_PTS_FLG_HAVE_NS_TASKS;
+
+ while (1) {
+ erts_aint32_t new, exp;
+
+ if ((act & add_flags) == add_flags
+ && (act & (ERTS_PTS_FLG_IN_RUNQ|ERTS_PTS_FLG_EXEC)))
+ goto done; /* Done */
+
+ new = exp = act;
+ new |= add_flags;
+ if (!(act & (ERTS_PTS_FLG_IN_RUNQ|ERTS_PTS_FLG_EXEC)))
+ new |= ERTS_PTS_FLG_IN_RUNQ;
+
+ act = erts_smp_atomic32_cmpxchg_relb(&pp->sched.flags, new, exp);
+
+ if (exp == act) {
+ if (!(act & (ERTS_PTS_FLG_IN_RUNQ|ERTS_PTS_FLG_EXEC)))
+ break; /* Need to enqueue port */
+ goto done; /* Done */
}
+ if (act & ERTS_PTS_FLG_EXIT)
+ goto done; /* Died after our task insert... */
+ }
+
+ /* Enqueue port on run-queue */
+
+ runq = erts_port_runq(pp);
+ if (!runq)
+ ERTS_INTERNAL_ERROR("Missing run-queue");
+
+#ifdef ERTS_SMP
+ xrunq = erts_check_emigration_need(runq, ERTS_PORT_PRIO_LEVEL);
+ if (xrunq) {
+ /* Port emigrated ... */
+ erts_smp_atomic_set_nob(&pp->run_queue, (erts_aint_t) xrunq);
erts_smp_runq_unlock(runq);
+ runq = erts_port_runq(pp);
+ if (!runq)
+ ERTS_INTERNAL_ERROR("Missing run-queue");
+ }
+#endif
- erts_smp_notify_inc_runq(runq);
+ enqueue_port(runq, pp);
+
+ if (erts_system_profile_flags.runnable_ports) {
+ profile_runnable_port(pp, am_active);
}
+
+ erts_smp_runq_unlock(runq);
+
+ erts_smp_notify_inc_runq(runq);
+
+done:
+
+#ifdef ERTS_SMP
+ if (dhndl != ERTS_THR_PRGR_DHANDLE_MANAGED)
+ erts_port_dec_refc(pp);
+#endif
+
return 0;
+
+abort_nosuspend:
+
+#ifdef ERTS_SMP
+ if (dhndl != ERTS_THR_PRGR_DHANDLE_MANAGED)
+ erts_port_dec_refc(pp);
+#endif
+
+ abort_nosuspend_task(pp, ptp->type, &ptp->u.alive.td);
+
+ ASSERT(ns_pthlp);
+ erts_free(ERTS_ALC_T_PT_HNDL_LIST, ns_pthlp);
+ if (ptp)
+ port_task_free(ptp);
+
+ return 0;
+
+fail:
+
+#ifdef ERTS_SMP
+ if (dhndl != ERTS_THR_PRGR_DHANDLE_MANAGED)
+ erts_port_dec_refc(pp);
+#endif
+
+ if (ns_pthlp)
+ erts_free(ERTS_ALC_T_PT_HNDL_LIST, ns_pthlp);
+
+ if (ptp)
+ port_task_free(ptp);
+
+ return -1;
}
void
erts_port_task_free_port(Port *pp)
{
+ ErtsProcList *suspended;
+ erts_aint32_t flags;
ErtsRunQueue *runq;
- ErtsPortTaskQueue *ptqp;
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(pp));
- ASSERT(!(pp->status & ERTS_PORT_SFLGS_DEAD));
+ ASSERT(!(erts_atomic32_read_nob(&pp->state) & ERTS_PORT_SFLGS_DEAD));
+
runq = erts_port_runq(pp);
- ASSERT(runq);
- ERTS_PT_CHK_PRES_PORTQ(runq, pp);
- ptqp = pp->sched.exe_taskq;
- if (ptqp) {
- /* I (this thread) am currently executing this port, free it
- when scheduled out... */
- ErtsPortTask *ptp;
- enqueue_free:
- ptp = port_task_alloc();
- erts_smp_port_state_lock(pp);
- pp->status &= ~ERTS_PORT_SFLG_CLOSING;
- pp->status |= ERTS_PORT_SFLG_FREE_SCHEDULED;
- erts_may_save_closed_port(pp);
- erts_smp_port_state_unlock(pp);
- ERTS_LC_ASSERT(erts_smp_atomic_read_nob(&pp->refc) > 1);
- ptp->type = ERTS_PORT_TASK_FREE;
- ptp->event = (ErlDrvEvent) -1;
- ptp->event_data = NULL;
- set_handle(ptp, NULL);
- push_task(ptqp, ptp);
- ERTS_PT_CHK_PRES_PORTQ(runq, pp);
- erts_smp_runq_unlock(runq);
- }
- else {
- if (pp->sched.in_runq) {
- ptqp = pp->sched.taskq;
- if (!ptqp)
- pp->sched.taskq = ptqp = port_taskq_init(port_taskq_alloc(), pp);
- goto enqueue_free;
- }
- ASSERT(!pp->sched.taskq);
- erts_smp_port_state_lock(pp);
- pp->status &= ~ERTS_PORT_SFLG_CLOSING;
- pp->status |= ERTS_PORT_SFLG_FREE_SCHEDULED;
- erts_may_save_closed_port(pp);
- erts_smp_port_state_unlock(pp);
- erts_smp_atomic_dec_nob(&pp->refc); /* Not alive */
- ERTS_LC_ASSERT(erts_smp_atomic_read_nob(&pp->refc) > 0); /* Lock */
- handle_remaining_tasks(runq, pp); /* May release runq lock */
- ASSERT(!pp->sched.exe_taskq && (!ptqp || !ptqp->first));
- pp->sched.taskq = NULL;
- ERTS_PT_CHK_PRES_PORTQ(runq, pp);
- erts_smp_runq_unlock(runq);
- }
-}
+ if (!runq)
+ ERTS_INTERNAL_ERROR("Missing run-queue");
+ erts_port_task_sched_lock(&pp->sched);
+ flags = erts_smp_atomic32_read_bor_relb(&pp->sched.flags,
+ ERTS_PTS_FLG_EXIT);
+ suspended = pp->suspended;
+ pp->suspended = NULL;
+ erts_port_task_sched_unlock(&pp->sched);
+ erts_atomic32_read_bset_relb(&pp->state,
+ (ERTS_PORT_SFLG_CLOSING
+ | ERTS_PORT_SFLG_FREE),
+ ERTS_PORT_SFLG_FREE);
-typedef struct {
- ErtsRunQueue *runq;
- int *resp;
-} ErtsPortTaskExeBlockData;
+ erts_smp_runq_unlock(runq);
+
+ if (erts_proclist_fetch(&suspended, NULL))
+ erts_resume_processes(suspended);
+
+ if (!(flags & (ERTS_PTS_FLG_IN_RUNQ|ERTS_PTS_FLG_EXEC)))
+ begin_port_cleanup(pp, NULL, NULL);
+}
/*
- * Run all scheduled tasks for the first port in run queue. If
- * new tasks appear while running reschedule port (free task is
- * an exception; it is always handled instantly).
+ * Execute scheduled tasks of a port.
*
* erts_port_task_execute() is called by scheduler threads between
- * scheduleing of processes. Sched lock should be held by caller.
+ * scheduling of processes. Run-queue lock should be held by caller.
*/
int
erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp)
{
Port *pp;
- ErtsPortTaskQueue *ptqp;
- ErtsPortTask *ptp;
+ ErtsPortTask *execq;
+ int processing_busy_q;
int res = 0;
+ int vreds = 0;
int reds = ERTS_PORT_REDS_EXECUTE;
erts_aint_t io_tasks_executed = 0;
int fpe_was_unmasked;
+ erts_aint32_t state;
+ int active;
ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(runq));
- ERTS_PT_CHK_PORTQ(runq);
-
pp = pop_port(runq);
if (!pp) {
res = 0;
goto done;
}
- ASSERT(pp->sched.in_runq);
- pp->sched.in_runq = 0;
- if (!pp->sched.taskq) {
- if (erts_system_profile_flags.runnable_ports)
- profile_runnable_port(pp, am_inactive);
- res = (erts_smp_atomic_read_nob(&erts_port_task_outstanding_io_tasks)
- != (erts_aint_t) 0);
- goto done;
- }
+ erts_smp_runq_unlock(runq);
*curr_port_pp = pp;
-
- ASSERT(pp->sched.taskq->first);
- ptqp = pp->sched.taskq;
- pp->sched.taskq = NULL;
-
- ASSERT(!pp->sched.exe_taskq);
- pp->sched.exe_taskq = ptqp;
-
- if (erts_smp_port_trylock(pp) == EBUSY) {
- erts_smp_runq_unlock(runq);
- erts_smp_port_lock(pp);
- erts_smp_runq_lock(runq);
- }
if (erts_sched_stat.enabled) {
ErtsSchedulerData *esdp = erts_get_scheduler_data();
@@ -738,77 +1583,94 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp)
erts_smp_spin_unlock(&erts_sched_stat.lock);
}
+ prepare_exec(pp, &execq, &processing_busy_q);
+
+ erts_smp_port_lock(pp);
+
/* trace port scheduling, in */
if (IS_TRACED_FL(pp, F_TRACE_SCHED_PORTS)) {
trace_sched_ports(pp, am_in);
}
- ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(pp));
+ fpe_was_unmasked = erts_block_fpe();
- ERTS_PT_CHK_PRES_PORTQ(runq, pp);
- ptp = pop_task(ptqp);
+ state = erts_atomic32_read_nob(&pp->state);
+ goto begin_handle_tasks;
- fpe_was_unmasked = erts_block_fpe();
+ while (1) {
+ erts_aint32_t task_state;
+ ErtsPortTask *ptp;
- while (ptp) {
- ASSERT(pp->sched.taskq != pp->sched.exe_taskq);
+ ptp = select_task_for_exec(pp, &execq, &processing_busy_q);
+ if (!ptp)
+ break;
+
+ task_state = erts_smp_atomic32_cmpxchg_nob(&ptp->state,
+ ERTS_PT_STATE_EXECUTING,
+ ERTS_PT_STATE_SCHEDULED);
+ if (task_state != ERTS_PT_STATE_SCHEDULED) {
+ ASSERT(task_state == ERTS_PT_STATE_ABORTED);
+ goto aborted_port_task;
+ }
reset_handle(ptp);
- erts_smp_runq_unlock(runq);
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(pp));
ERTS_SMP_CHK_NO_PROC_LOCKS;
ASSERT(pp->drv_ptr);
switch (ptp->type) {
- case ERTS_PORT_TASK_FREE: /* May be pushed in q at any time */
- reds += ERTS_PORT_REDS_FREE;
- erts_smp_runq_lock(runq);
-
- erts_unblock_fpe(fpe_was_unmasked);
- ASSERT(pp->status & ERTS_PORT_SFLG_FREE_SCHEDULED);
- if (ptqp->first || (pp->sched.taskq && pp->sched.taskq->first))
- handle_remaining_tasks(runq, pp);
- ASSERT(!ptqp->first
- && (!pp->sched.taskq || !pp->sched.taskq->first));
- erts_smp_atomic_dec_nob(&pp->refc); /* Not alive */
- ERTS_LC_ASSERT(erts_smp_atomic_read_nob(&pp->refc) > 0); /* Lock */
-
- port_task_free(ptp);
- if (pp->sched.taskq)
- port_taskq_free(pp->sched.taskq);
- pp->sched.taskq = NULL;
-
- goto tasks_done;
case ERTS_PORT_TASK_TIMEOUT:
reds += ERTS_PORT_REDS_TIMEOUT;
- if (!(pp->status & ERTS_PORT_SFLGS_DEAD)) {
+ if (!(state & ERTS_PORT_SFLGS_DEAD)) {
DTRACE_DRIVER(driver_timeout, pp);
(*pp->drv_ptr->timeout)((ErlDrvData) pp->drv_data);
}
break;
case ERTS_PORT_TASK_INPUT:
reds += ERTS_PORT_REDS_INPUT;
- ASSERT((pp->status & ERTS_PORT_SFLGS_DEAD) == 0);
+ ASSERT((state & ERTS_PORT_SFLGS_DEAD) == 0);
DTRACE_DRIVER(driver_ready_input, pp);
/* NOTE some windows drivers use ->ready_input for input and output */
- (*pp->drv_ptr->ready_input)((ErlDrvData) pp->drv_data, ptp->event);
+ (*pp->drv_ptr->ready_input)((ErlDrvData) pp->drv_data,
+ ptp->u.alive.td.io.event);
io_tasks_executed++;
break;
case ERTS_PORT_TASK_OUTPUT:
reds += ERTS_PORT_REDS_OUTPUT;
- ASSERT((pp->status & ERTS_PORT_SFLGS_DEAD) == 0);
+ ASSERT((state & ERTS_PORT_SFLGS_DEAD) == 0);
DTRACE_DRIVER(driver_ready_output, pp);
- (*pp->drv_ptr->ready_output)((ErlDrvData) pp->drv_data, ptp->event);
+ (*pp->drv_ptr->ready_output)((ErlDrvData) pp->drv_data,
+ ptp->u.alive.td.io.event);
io_tasks_executed++;
break;
case ERTS_PORT_TASK_EVENT:
reds += ERTS_PORT_REDS_EVENT;
- ASSERT((pp->status & ERTS_PORT_SFLGS_DEAD) == 0);
+ ASSERT((state & ERTS_PORT_SFLGS_DEAD) == 0);
DTRACE_DRIVER(driver_event, pp);
- (*pp->drv_ptr->event)((ErlDrvData) pp->drv_data, ptp->event, ptp->event_data);
+ (*pp->drv_ptr->event)((ErlDrvData) pp->drv_data,
+ ptp->u.alive.td.io.event,
+ ptp->u.alive.td.io.event_data);
io_tasks_executed++;
break;
+ case ERTS_PORT_TASK_PROC_SIG: {
+ ErtsProc2PortSigData *sigdp = &ptp->u.alive.td.psig.data;
+ ASSERT((state & ERTS_PORT_SFLGS_DEAD) == 0);
+ if (!pp->sched.taskq.bpq)
+ reds += ptp->u.alive.td.psig.callback(pp,
+ state,
+ ERTS_PROC2PORT_SIG_EXEC,
+ sigdp);
+ else {
+ ErlDrvSizeT size = erts_proc2port_sig_command_data_size(sigdp);
+ reds += ptp->u.alive.td.psig.callback(pp,
+ state,
+ ERTS_PROC2PORT_SIG_EXEC,
+ sigdp);
+ dequeued_proc2port_data(pp, size);
+ }
+ break;
+ }
case ERTS_PORT_TASK_DIST_CMD:
reds += erts_dist_command(pp, CONTEXT_REDS-reds);
break;
@@ -819,33 +1681,33 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp)
break;
}
- if ((pp->status & ERTS_PORT_SFLG_CLOSING)
- && erts_is_port_ioq_empty(pp)) {
- reds += ERTS_PORT_REDS_TERMINATE;
- erts_terminate_port(pp);
- }
-
- ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(pp));
+ reds += erts_port_driver_callback_epilogue(pp, &state);
-#ifdef ERTS_SMP
- if (pp->xports)
- erts_smp_xports_unlock(pp);
- ASSERT(!pp->xports);
-#endif
+ aborted_port_task:
+ schedule_port_task_free(ptp);
- ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(pp));
+ begin_handle_tasks:
+ if (state & ERTS_PORT_SFLG_FREE) {
+ reds += ERTS_PORT_REDS_FREE;
+ begin_port_cleanup(pp, &execq, &processing_busy_q);
- port_task_free(ptp);
+ break;
+ }
- erts_smp_runq_lock(runq);
+ vreds += ERTS_PORT_CALLBACK_VREDS;
+ reds += ERTS_PORT_CALLBACK_VREDS;
- ptp = pop_task(ptqp);
+ if (reds >= CONTEXT_REDS)
+ break;
}
- tasks_done:
-
erts_unblock_fpe(fpe_was_unmasked);
+ /* trace port scheduling, out */
+ if (IS_TRACED_FL(pp, F_TRACE_SCHED_PORTS)) {
+ trace_sched_ports(pp, am_out);
+ }
+
if (io_tasks_executed) {
ASSERT(erts_smp_atomic_read_nob(&erts_port_task_outstanding_io_tasks)
>= io_tasks_executed);
@@ -853,15 +1715,19 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp)
-1*io_tasks_executed);
}
- *curr_port_pp = NULL;
-
#ifdef ERTS_SMP
ASSERT(runq == (ErtsRunQueue *) erts_smp_atomic_read_nob(&pp->run_queue));
#endif
- if (!pp->sched.taskq) {
- ASSERT(pp->sched.exe_taskq);
- pp->sched.exe_taskq = NULL;
+ active = finalize_exec(pp, &execq, processing_busy_q);
+
+ erts_port_release(pp);
+
+ *curr_port_pp = NULL;
+
+ erts_smp_runq_lock(runq);
+
+ if (!active) {
if (erts_system_profile_flags.runnable_ports)
profile_runnable_port(pp, am_inactive);
}
@@ -870,16 +1736,13 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp)
ErtsRunQueue *xrunq;
#endif
- ASSERT(!(pp->status & ERTS_PORT_SFLGS_DEAD));
- ASSERT(pp->sched.taskq->first);
+ ASSERT(!(erts_atomic32_read_nob(&pp->state) & ERTS_PORT_SFLGS_DEAD));
#ifdef ERTS_SMP
xrunq = erts_check_emigration_need(runq, ERTS_PORT_PRIO_LEVEL);
if (!xrunq) {
#endif
enqueue_port(runq, pp);
- ASSERT(pp->sched.exe_taskq);
- pp->sched.exe_taskq = NULL;
/* No need to notify ourselves about inc in runq. */
#ifdef ERTS_SMP
}
@@ -889,128 +1752,214 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp)
erts_smp_runq_unlock(runq);
xrunq = erts_port_runq(pp);
- if (xrunq) {
- enqueue_port(xrunq, pp);
- ASSERT(pp->sched.exe_taskq);
- pp->sched.exe_taskq = NULL;
- erts_smp_runq_unlock(xrunq);
- erts_smp_notify_inc_runq(xrunq);
- }
+ ASSERT(xrunq);
+ enqueue_port(xrunq, pp);
+ erts_smp_runq_unlock(xrunq);
+ erts_smp_notify_inc_runq(xrunq);
erts_smp_runq_lock(runq);
}
#endif
}
+ done:
res = (erts_smp_atomic_read_nob(&erts_port_task_outstanding_io_tasks)
!= (erts_aint_t) 0);
- ERTS_PT_CHK_PRES_PORTQ(runq, pp);
+ reds -= vreds;
+ runq->scheduler->reductions += reds;
- port_taskq_free(ptqp);
-
- /* trace port scheduling, out */
- if (IS_TRACED_FL(pp, F_TRACE_SCHED_PORTS)) {
- trace_sched_ports(pp, am_out);
- }
-#ifndef ERTS_SMP
- erts_port_release(pp);
-#else
- {
- erts_aint_t refc;
- erts_smp_mtx_unlock(pp->lock);
- refc = erts_smp_atomic_dec_read_nob(&pp->refc);
- ASSERT(refc >= 0);
- if (refc == 0) {
- erts_smp_runq_unlock(runq);
- erts_port_cleanup(pp); /* Might aquire runq lock */
- erts_smp_runq_lock(runq);
- res = (erts_smp_atomic_read_nob(&erts_port_task_outstanding_io_tasks)
- != (erts_aint_t) 0);
- }
- }
-#endif
-
- done:
ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(runq));
-
ERTS_PORT_REDUCTIONS_EXECUTED(runq, reds);
return res;
}
-/*
- * Handle remaining tasks after a free task.
- */
+#ifdef ERTS_SMP
+static void
+release_port(void *vport)
+{
+ erts_port_dec_refc((Port *) vport);
+}
+#endif
static void
-handle_remaining_tasks(ErtsRunQueue *runq, Port *pp)
+begin_port_cleanup(Port *pp, ErtsPortTask **execqp, int *processing_busy_q_p)
{
- int i;
- ErtsPortTask *ptp;
- ErtsPortTaskQueue *ptqps[] = {pp->sched.exe_taskq, pp->sched.taskq};
+ int i, max;
+ ErtsPortTaskBusyCallerTable *tabp;
+ ErtsPortTask *qs[3];
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(pp));
- for (i = 0; i < sizeof(ptqps)/sizeof(ErtsPortTaskQueue *); i++) {
- if (!ptqps[i])
- continue;
- ptp = pop_task(ptqps[i]);
- while (ptp) {
+ /*
+ * Abort remaining tasks...
+ *
+ * We want to process queues in the following order in order
+ * to preserve signal ordering guarantees:
+ * 1. Local busy queue
+ * 2. Local queue
+ * 3. In queue
+ */
+
+ max = 0;
+ if (!execqp) {
+ if (pp->sched.taskq.local.busy.first)
+ qs[max++] = pp->sched.taskq.local.busy.first;
+ if (pp->sched.taskq.local.first)
+ qs[max++] = pp->sched.taskq.local.first;
+ }
+ else {
+ if (*processing_busy_q_p) {
+ if (*execqp)
+ qs[max++] = *execqp;
+ if (pp->sched.taskq.local.first)
+ qs[max++] = pp->sched.taskq.local.first;
+ }
+ else {
+ if (pp->sched.taskq.local.busy.first)
+ qs[max++] = pp->sched.taskq.local.busy.first;
+ if (*execqp)
+ qs[max++] = *execqp;
+ }
+ *execqp = NULL;
+ *processing_busy_q_p = 0;
+ }
+ pp->sched.taskq.local.busy.first = NULL;
+ pp->sched.taskq.local.busy.last = NULL;
+ pp->sched.taskq.local.first = NULL;
+ tabp = pp->sched.taskq.local.busy.table;
+ if (tabp) {
+ int bix;
+ for (bix = 0; bix < ERTS_PORT_TASK_BUSY_CALLER_TABLE_BUCKETS; bix++) {
+ ErtsPortTaskBusyCaller *bcp = tabp->bucket[bix];
+ while (bcp) {
+ ErtsPortTaskBusyCaller *free_bcp = bcp;
+ bcp = bcp->next;
+ if (free_bcp != &tabp->pre_alloc_busy_caller)
+ erts_free(ERTS_ALC_T_BUSY_CALLER, free_bcp);
+ }
+ }
+
+ busy_caller_table_free(tabp);
+ pp->sched.taskq.local.busy.table = NULL;
+ }
+
+ erts_port_task_sched_lock(&pp->sched);
+ qs[max] = pp->sched.taskq.in.first;
+ pp->sched.taskq.in.first = NULL;
+ pp->sched.taskq.in.last = NULL;
+ erts_port_task_sched_unlock(&pp->sched);
+ if (qs[max])
+ max++;
+
+ for (i = 0; i < max; i++) {
+ while (1) {
+ erts_aint32_t state;
+ ErtsPortTask *ptp = qs[i];
+ if (!ptp)
+ break;
+
+ qs[i] = ptp->u.alive.next;
+
+ /* Normal case here is aborted tasks... */
+ state = erts_smp_atomic32_read_nob(&ptp->state);
+ if (state == ERTS_PT_STATE_ABORTED)
+ goto aborted_port_task;
+
+ state = erts_smp_atomic32_cmpxchg_nob(&ptp->state,
+ ERTS_PT_STATE_EXECUTING,
+ ERTS_PT_STATE_SCHEDULED);
+ if (state != ERTS_PT_STATE_SCHEDULED) {
+ ASSERT(state == ERTS_PT_STATE_ABORTED);
+ goto aborted_port_task;
+ }
+
reset_handle(ptp);
- erts_smp_runq_unlock(runq);
switch (ptp->type) {
- case ERTS_PORT_TASK_FREE:
case ERTS_PORT_TASK_TIMEOUT:
break;
case ERTS_PORT_TASK_INPUT:
- erts_stale_drv_select(pp->id, ptp->event, DO_READ, 1);
+ erts_stale_drv_select(pp->common.id,
+ ptp->u.alive.td.io.event,
+ DO_READ,
+ 1);
break;
case ERTS_PORT_TASK_OUTPUT:
- erts_stale_drv_select(pp->id, ptp->event, DO_WRITE, 1);
+ erts_stale_drv_select(pp->common.id,
+ ptp->u.alive.td.io.event,
+ DO_WRITE,
+ 1);
break;
case ERTS_PORT_TASK_EVENT:
- erts_stale_drv_select(pp->id, ptp->event, 0, 1);
+ erts_stale_drv_select(pp->common.id,
+ ptp->u.alive.td.io.event,
+ 0,
+ 1);
break;
case ERTS_PORT_TASK_DIST_CMD:
break;
+ case ERTS_PORT_TASK_PROC_SIG: {
+ ErtsProc2PortSigData *sigdp = &ptp->u.alive.td.psig.data;
+ if (!pp->sched.taskq.bpq)
+ ptp->u.alive.td.psig.callback(NULL,
+ ERTS_PORT_SFLG_INVALID,
+ ERTS_PROC2PORT_SIG_ABORT_CLOSED,
+ sigdp);
+ else {
+ ErlDrvSizeT size = erts_proc2port_sig_command_data_size(sigdp);
+ ptp->u.alive.td.psig.callback(NULL,
+ ERTS_PORT_SFLG_INVALID,
+ ERTS_PROC2PORT_SIG_ABORT_CLOSED,
+ sigdp);
+ aborted_proc2port_data(pp, size);
+ }
+ break;
+ }
default:
erl_exit(ERTS_ABORT_EXIT,
"Invalid port task type: %d\n",
(int) ptp->type);
}
- port_task_free(ptp);
-
- erts_smp_runq_lock(runq);
- ptp = pop_task(ptqps[i]);
+ aborted_port_task:
+ schedule_port_task_free(ptp);
}
}
- ASSERT(!pp->sched.taskq || !pp->sched.taskq->first);
+ erts_smp_atomic32_read_band_nob(&pp->sched.flags,
+ ~(ERTS_PTS_FLG_HAVE_BUSY_TASKS
+ |ERTS_PTS_FLG_HAVE_TASKS));
+
+ /*
+ * Schedule cleanup of port structure...
+ */
+#ifdef ERTS_SMP
+ erts_schedule_thr_prgr_later_op(release_port,
+ (void *) pp,
+ &pp->common.u.release);
+#else
+ pp->cleanup = 1;
+#endif
}
int
erts_port_is_scheduled(Port *pp)
{
- int res;
- ErtsRunQueue *runq = erts_port_runq(pp);
- if (!runq)
- return 0;
- res = pp->sched.taskq || pp->sched.exe_taskq;
- erts_smp_runq_unlock(runq);
- return res;
+ erts_aint32_t flags = erts_smp_atomic32_read_acqb(&pp->sched.flags);
+ return (flags & (ERTS_PTS_FLG_IN_RUNQ|ERTS_PTS_FLG_EXEC)) != 0;
}
#ifdef ERTS_SMP
+
void
erts_enqueue_port(ErtsRunQueue *rq, Port *pp)
{
ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(rq));
ASSERT(rq == (ErtsRunQueue *) erts_smp_atomic_read_nob(&pp->run_queue));
- ASSERT(pp->sched.in_runq);
+ ASSERT(erts_smp_atomic32_read_nob(&pp->sched.flags) & ERTS_PTS_FLG_IN_RUNQ);
enqueue_port(rq, pp);
}
@@ -1022,7 +1971,8 @@ erts_dequeue_port(ErtsRunQueue *rq)
pp = pop_port(rq);
ASSERT(!pp
|| rq == (ErtsRunQueue *) erts_smp_atomic_read_nob(&pp->run_queue));
- ASSERT(!pp || pp->sched.in_runq);
+ ASSERT(!pp || (erts_smp_atomic32_read_nob(&pp->sched.flags)
+ & ERTS_PTS_FLG_IN_RUNQ));
return pp;
}
@@ -1037,5 +1987,5 @@ erts_port_task_init(void)
erts_smp_atomic_init_nob(&erts_port_task_outstanding_io_tasks,
(erts_aint_t) 0);
init_port_task_alloc();
- init_port_taskq_alloc();
+ init_busy_caller_table_alloc();
}
diff --git a/erts/emulator/beam/erl_port_task.h b/erts/emulator/beam/erl_port_task.h
index fd88b1c1ff..ae6cd69ae2 100644
--- a/erts/emulator/beam/erl_port_task.h
+++ b/erts/emulator/beam/erl_port_task.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2006-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2006-2012. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -27,6 +27,9 @@
#define ERTS_PORT_TASK_H_BASIC_TYPES__
#include "erl_sys_driver.h"
#include "erl_smp.h"
+#define ERL_PORT_GET_PORT_TYPE_ONLY__
+#include "erl_port.h"
+#undef ERL_PORT_GET_PORT_TYPE_ONLY__
typedef erts_smp_atomic_t ErtsPortTaskHandle;
#endif
@@ -43,13 +46,19 @@ typedef erts_smp_atomic_t ErtsPortTaskHandle;
#define ERTS_INCLUDE_SCHEDULER_INTERNALS
#endif
+#define ERTS_PT_FLG_WAIT_BUSY (1 << 0)
+#define ERTS_PT_FLG_SIG_DEP (1 << 1)
+#define ERTS_PT_FLG_NOSUSPEND (1 << 2)
+#define ERTS_PT_FLG_REF (1 << 3)
+#define ERTS_PT_FLG_BAD_OUTPUT (1 << 4)
+
typedef enum {
- ERTS_PORT_TASK_FREE,
ERTS_PORT_TASK_INPUT,
ERTS_PORT_TASK_OUTPUT,
ERTS_PORT_TASK_EVENT,
ERTS_PORT_TASK_TIMEOUT,
- ERTS_PORT_TASK_DIST_CMD
+ ERTS_PORT_TASK_DIST_CMD,
+ ERTS_PORT_TASK_PROC_SIG
} ErtsPortTaskType;
#ifdef ERTS_INCLUDE_SCHEDULER_INTERNALS
@@ -57,19 +66,76 @@ typedef enum {
extern erts_smp_atomic_t erts_port_task_outstanding_io_tasks;
#endif
+#define ERTS_PTS_FLG_IN_RUNQ (((erts_aint32_t) 1) << 0)
+#define ERTS_PTS_FLG_EXEC (((erts_aint32_t) 1) << 1)
+#define ERTS_PTS_FLG_HAVE_TASKS (((erts_aint32_t) 1) << 2)
+#define ERTS_PTS_FLG_EXIT (((erts_aint32_t) 1) << 3)
+#define ERTS_PTS_FLG_BUSY_PORT (((erts_aint32_t) 1) << 4)
+#define ERTS_PTS_FLG_BUSY_PORT_Q (((erts_aint32_t) 1) << 5)
+#define ERTS_PTS_FLG_CHK_UNSET_BUSY_PORT_Q (((erts_aint32_t) 1) << 6)
+#define ERTS_PTS_FLG_HAVE_BUSY_TASKS (((erts_aint32_t) 1) << 7)
+#define ERTS_PTS_FLG_HAVE_NS_TASKS (((erts_aint32_t) 1) << 8)
+#define ERTS_PTS_FLG_PARALLELISM (((erts_aint32_t) 1) << 9)
+#define ERTS_PTS_FLG_FORCE_SCHED (((erts_aint32_t) 1) << 10)
+
+#define ERTS_PTS_FLGS_BUSY \
+ (ERTS_PTS_FLG_BUSY_PORT | ERTS_PTS_FLG_BUSY_PORT_Q)
+
+#define ERTS_PTS_FLGS_FORCE_SCHEDULE_OP \
+ (ERTS_PTS_FLG_EXIT \
+ | ERTS_PTS_FLG_HAVE_BUSY_TASKS \
+ | ERTS_PTS_FLG_HAVE_TASKS \
+ | ERTS_PTS_FLG_EXEC \
+ | ERTS_PTS_FLG_FORCE_SCHED)
+
+#define ERTS_PORT_TASK_DEFAULT_BUSY_PORT_Q_HIGH 8192
+#define ERTS_PORT_TASK_DEFAULT_BUSY_PORT_Q_LOW 4096
+
+typedef struct {
+ ErlDrvSizeT high;
+ erts_smp_atomic_t low;
+ erts_smp_atomic_t size;
+} ErtsPortTaskBusyPortQ;
+
typedef struct ErtsPortTask_ ErtsPortTask;
-typedef struct ErtsPortTaskQueue_ ErtsPortTaskQueue;
+typedef struct ErtsPortTaskBusyCallerTable_ ErtsPortTaskBusyCallerTable;
+typedef struct ErtsPortTaskHandleList_ ErtsPortTaskHandleList;
typedef struct {
Port *next;
- int in_runq;
- ErtsPortTaskQueue *taskq;
- ErtsPortTaskQueue *exe_taskq;
+ struct {
+ struct {
+ struct {
+ ErtsPortTask *first;
+ ErtsPortTask *last;
+ ErtsPortTaskBusyCallerTable *table;
+ ErtsPortTaskHandleList *nosuspend;
+ } busy;
+ ErtsPortTask *first;
+ } local;
+ struct {
+ ErtsPortTask *first;
+ ErtsPortTask *last;
+ } in;
+ ErtsPortTaskBusyPortQ *bpq;
+ } taskq;
+ erts_smp_atomic32_t flags;
+#ifdef ERTS_SMP
+ erts_mtx_t mtx;
+#endif
} ErtsPortTaskSched;
ERTS_GLB_INLINE void erts_port_task_handle_init(ErtsPortTaskHandle *pthp);
ERTS_GLB_INLINE int erts_port_task_is_scheduled(ErtsPortTaskHandle *pthp);
-ERTS_GLB_INLINE void erts_port_task_init_sched(ErtsPortTaskSched *ptsp);
+ERTS_GLB_INLINE void erts_port_task_pre_init_sched(ErtsPortTaskSched *ptsp,
+ ErtsPortTaskBusyPortQ *bpq);
+ERTS_GLB_INLINE void erts_port_task_init_sched(ErtsPortTaskSched *ptsp,
+ Eterm id);
+ERTS_GLB_INLINE void erts_port_task_fini_sched(ErtsPortTaskSched *ptsp);
+ERTS_GLB_INLINE void erts_port_task_sched_lock(ErtsPortTaskSched *ptsp);
+ERTS_GLB_INLINE void erts_port_task_sched_unlock(ErtsPortTaskSched *ptsp);
+ERTS_GLB_INLINE int erts_port_task_sched_lock_is_locked(ErtsPortTaskSched *ptsp);
+
#ifdef ERTS_INCLUDE_SCHEDULER_INTERNALS
ERTS_GLB_INLINE int erts_port_task_have_outstanding_io_tasks(void);
#endif
@@ -88,13 +154,75 @@ erts_port_task_is_scheduled(ErtsPortTaskHandle *pthp)
return ((void *) erts_smp_atomic_read_nob(pthp)) != NULL;
}
+ERTS_GLB_INLINE void erts_port_task_pre_init_sched(ErtsPortTaskSched *ptsp,
+ ErtsPortTaskBusyPortQ *bpq)
+{
+ if (bpq) {
+ erts_aint_t low = (erts_aint_t) ERTS_PORT_TASK_DEFAULT_BUSY_PORT_Q_LOW;
+ erts_smp_atomic_init_nob(&bpq->low, low);
+ bpq->high = (ErlDrvSizeT) ERTS_PORT_TASK_DEFAULT_BUSY_PORT_Q_HIGH;
+ erts_smp_atomic_init_nob(&bpq->size, (erts_aint_t) 0);
+ }
+ ptsp->taskq.bpq = bpq;
+}
+
ERTS_GLB_INLINE void
-erts_port_task_init_sched(ErtsPortTaskSched *ptsp)
+erts_port_task_init_sched(ErtsPortTaskSched *ptsp, Eterm instr_id)
{
+#ifdef ERTS_SMP
+ char *lock_str = "port_sched_lock";
+#endif
ptsp->next = NULL;
- ptsp->in_runq = 0;
- ptsp->taskq = NULL;
- ptsp->exe_taskq = NULL;
+ ptsp->taskq.local.busy.first = NULL;
+ ptsp->taskq.local.busy.last = NULL;
+ ptsp->taskq.local.busy.table = NULL;
+ ptsp->taskq.local.busy.nosuspend = NULL;
+ ptsp->taskq.local.first = NULL;
+ ptsp->taskq.in.first = NULL;
+ ptsp->taskq.in.last = NULL;
+ erts_smp_atomic32_init_nob(&ptsp->flags, 0);
+#ifdef ERTS_SMP
+#ifdef ERTS_ENABLE_LOCK_COUNT
+ if (!(erts_lcnt_rt_options & ERTS_LCNT_OPT_PORTLOCK))
+ lock_str = NULL;
+#endif
+ erts_mtx_init_x(&ptsp->mtx, lock_str, instr_id);
+#endif
+}
+
+ERTS_GLB_INLINE void
+erts_port_task_sched_lock(ErtsPortTaskSched *ptsp)
+{
+#ifdef ERTS_SMP
+ erts_mtx_lock(&ptsp->mtx);
+#endif
+}
+
+ERTS_GLB_INLINE void
+erts_port_task_sched_unlock(ErtsPortTaskSched *ptsp)
+{
+#ifdef ERTS_SMP
+ erts_mtx_unlock(&ptsp->mtx);
+#endif
+}
+
+ERTS_GLB_INLINE int
+erts_port_task_sched_lock_is_locked(ErtsPortTaskSched *ptsp)
+{
+#if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK)
+ return erts_lc_mtx_is_locked(&ptsp->mtx);
+#else
+ return 0;
+#endif
+}
+
+
+ERTS_GLB_INLINE void
+erts_port_task_fini_sched(ErtsPortTaskSched *ptsp)
+{
+#ifdef ERTS_SMP
+ erts_mtx_destroy(&ptsp->mtx);
+#endif
}
#ifdef ERTS_INCLUDE_SCHEDULER_INTERNALS
@@ -115,14 +243,16 @@ int erts_port_task_execute(ErtsRunQueue *, Port **);
void erts_port_task_init(void);
#endif
-int erts_port_task_abort(Eterm id, ErtsPortTaskHandle *);
+int erts_port_task_abort(ErtsPortTaskHandle *);
+void erts_port_task_abort_nosuspend_tasks(Port *);
+
int erts_port_task_schedule(Eterm,
ErtsPortTaskHandle *,
ErtsPortTaskType,
- ErlDrvEvent,
- ErlDrvEventData);
+ ...);
void erts_port_task_free_port(Port *);
int erts_port_is_scheduled(Port *);
+ErtsProc2PortSigData *erts_port_task_alloc_p2p_sig_data(void);
#ifdef ERTS_SMP
void erts_enqueue_port(ErtsRunQueue *rq, Port *pp);
diff --git a/erts/emulator/beam/erl_posix_str.c b/erts/emulator/beam/erl_posix_str.c
index 02db10905b..deb7e3e173 100644
--- a/erts/emulator/beam/erl_posix_str.c
+++ b/erts/emulator/beam/erl_posix_str.c
@@ -619,6 +619,7 @@ erl_errno_id(error)
case WSAEINVALIDPROVIDER: return "einvalidprovider";
#endif
#ifdef WSAEPROVIDERFAILEDINIT
+ /* You could get this if SYSTEMROOT env variable is set incorrectly */
case WSAEPROVIDERFAILEDINIT: return "eproviderfailedinit";
#endif
#ifdef WSASYSCALLFAILURE
diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c
index e07c9ae2b0..6e9bf7ca12 100644
--- a/erts/emulator/beam/erl_process.c
+++ b/erts/emulator/beam/erl_process.c
@@ -42,6 +42,10 @@
#include "erl_thr_queue.h"
#include "erl_async.h"
#include "dtrace-wrapper.h"
+#include "erl_ptab.h"
+
+#define ERTS_DELAYED_WAKEUP_INFINITY (~(Uint64) 0)
+#define ERTS_DELAYED_WAKEUP_REDUCTIONS ((Uint64) CONTEXT_REDS/2)
#define ERTS_RUNQ_CHECK_BALANCE_REDS_PER_SCHED (2000*CONTEXT_REDS)
#define ERTS_RUNQ_CALL_CHECK_BALANCE_REDS \
@@ -133,155 +137,16 @@ do { \
#define ERTS_EMPTY_RUNQ_PORTS(RQ) \
(RUNQ_READ_LEN(&(RQ)->ports.info.len) == 0 && (RQ)->misc.start == NULL)
+const Process erts_invalid_process = {{ERTS_INVALID_PID}};
+
extern BeamInstr beam_apply[];
extern BeamInstr beam_exit[];
extern BeamInstr beam_continue_exit[];
-#ifdef ARCH_32
-
-union {
- erts_smp_dw_atomic_t pid_data;
- char align[ERTS_CACHE_LINE_SIZE];
-} last erts_align_attribute(ERTS_CACHE_LINE_SIZE);
-
-
-static ERTS_INLINE Uint64
-dw_aint_to_uint64(erts_dw_aint_t *dw)
-{
-#ifdef ETHR_SU_DW_NAINT_T__
- return (Uint64) dw->dw_sint;
-#else
- Uint64 res;
- res = (Uint64) ((Uint32) dw->sint[ERTS_DW_AINT_HIGH_WORD]);
- res <<= 32;
- res |= (Uint64) ((Uint32) dw->sint[ERTS_DW_AINT_LOW_WORD]);
- return res;
-#endif
-}
-
-static void
-unint64_to_dw_aint(erts_dw_aint_t *dw, Uint64 val)
-{
-#ifdef ETHR_SU_DW_NAINT_T__
- dw->dw_sint = (ETHR_SU_DW_NAINT_T__) val;
-#else
- dw->sint[ERTS_DW_AINT_LOW_WORD] = (erts_aint_t) (val & 0xffffffff);
- dw->sint[ERTS_DW_AINT_HIGH_WORD] = (erts_aint_t) ((val >> 32) & 0xffffffff);
-#endif
-}
-
-static ERTS_INLINE void
-last_pid_data_init_nob(Uint64 val)
-{
- erts_dw_aint_t dw;
- unint64_to_dw_aint(&dw, val);
- erts_smp_dw_atomic_init_nob(&last.pid_data, &dw);
-}
-
-static ERTS_INLINE void
-last_pid_data_set_relb(Uint64 val)
-{
- erts_dw_aint_t dw;
- unint64_to_dw_aint(&dw, val);
- erts_smp_dw_atomic_set_relb(&last.pid_data, &dw);
-}
-
-static ERTS_INLINE Uint64
-last_pid_data_read_nob(void)
-{
- erts_dw_aint_t dw;
- erts_smp_dw_atomic_read_nob(&last.pid_data, &dw);
- return dw_aint_to_uint64(&dw);
-}
-
-static ERTS_INLINE Uint64
-last_pid_data_read_acqb(void)
-{
- erts_dw_aint_t dw;
- erts_smp_dw_atomic_read_acqb(&last.pid_data, &dw);
- return dw_aint_to_uint64(&dw);
-}
-
-static ERTS_INLINE Uint64
-last_pid_data_cmpxchg_relb(Uint64 new, Uint64 exp)
-{
- erts_dw_aint_t dw_new, dw_xchg;
-
- unint64_to_dw_aint(&dw_new, new);
- unint64_to_dw_aint(&dw_xchg, exp);
-
- if (erts_smp_dw_atomic_cmpxchg_relb(&last.pid_data, &dw_new, &dw_xchg))
- return exp;
- else
- return dw_aint_to_uint64(&dw_xchg);
-}
-
-#elif defined(ARCH_64)
-
-union {
- erts_smp_atomic_t pid_data;
- char align[ERTS_CACHE_LINE_SIZE];
-} last erts_align_attribute(ERTS_CACHE_LINE_SIZE);
-
-static ERTS_INLINE void
-last_pid_data_init_nob(Uint64 val)
-{
- erts_smp_atomic_init_nob(&last.pid_data, (erts_aint_t) val);
-}
-
-static ERTS_INLINE void
-last_pid_data_set_relb(Uint64 val)
-{
- erts_smp_atomic_set_relb(&last.pid_data, (erts_aint_t) val);
-}
-
-static ERTS_INLINE Uint64
-last_pid_data_read_nob(void)
-{
- return (Uint64) erts_smp_atomic_read_nob(&last.pid_data);
-}
-
-static ERTS_INLINE Uint64
-last_pid_data_read_acqb(void)
-{
- return (Uint64) erts_smp_atomic_read_acqb(&last.pid_data);
-}
-
-static ERTS_INLINE Uint64
-last_pid_data_cmpxchg_relb(Uint64 new, Uint64 exp)
-{
- return (Uint64) erts_smp_atomic_cmpxchg_relb(&last.pid_data,
- (erts_aint_t) new,
- (erts_aint_t) exp);
-}
-
-#else
-# error "Not 64-bit, nor 32-bit architecture..."
-#endif
-
-static ERTS_INLINE int
-last_pid_data_cmp(Uint64 lpd1, Uint64 lpd2)
-{
- Uint64 lpd1_wrap;
-
- if (lpd1 == lpd2)
- return 0;
-
- lpd1_wrap = lpd1 + (((Uint64) 1) << 63);
-
- if (lpd1 < lpd1_wrap)
- return (lpd1 < lpd2 && lpd2 < lpd1_wrap) ? -1 : 1;
- else
- return (lpd1_wrap <= lpd2 && lpd2 < lpd1) ? 1 : -1;
-}
-
-
-#define ERTS_PID_DATA_MASK__ ((1 << _PID_DATA_SIZE) - 1)
-
int erts_sched_compact_load;
Uint erts_no_schedulers;
-ErtsProcTab erts_proc erts_align_attribute(ERTS_CACHE_LINE_SIZE);
+ErtsPTab erts_proc erts_align_attribute(ERTS_CACHE_LINE_SIZE);
int erts_sched_thread_suggested_stack_size = -1;
@@ -368,8 +233,6 @@ erts_sched_stat_t erts_sched_stat;
static erts_tsd_key_t sched_data_key;
#endif
-erts_smp_rwmtx_t erts_proc_tab_rwmtx;
-
static erts_smp_atomic32_t function_calls;
#ifdef ERTS_SMP
@@ -406,29 +269,6 @@ struct erts_system_profile_flags_t erts_system_profile_flags;
#if ERTS_MAX_PROCESSES > 0x7fffffff
#error "Need to store process_count in another type"
#endif
-static erts_smp_atomic32_t process_count;
-
-typedef struct ErtsTermProcElement_ ErtsTermProcElement;
-struct ErtsTermProcElement_ {
- ErtsTermProcElement *next;
- ErtsTermProcElement *prev;
- int ix;
- union {
- struct {
- Eterm pid;
- Uint64 spawned;
- Uint64 exited;
- } process;
- struct {
- Uint64 interval;
- } bif_invocation;
- } u;
-};
-
-static struct {
- ErtsTermProcElement *start;
- ErtsTermProcElement *end;
-} saved_term_procs;
ERTS_SCHED_PREF_QUICK_ALLOC_IMPL(misc_op_list,
ErtsMiscOpList,
@@ -490,8 +330,6 @@ do { \
* Local functions.
*/
-static void init_processes_bif(void);
-static void save_terminating_process(Process *p);
static void exec_misc_ops(ErtsRunQueue *);
static void print_function_from_pc(int to, void *to_arg, BeamInstr* x);
static int stack_element_dump(int to, void *to_arg, Process* p, Eterm* sp,
@@ -518,9 +356,11 @@ dbg_chk_aux_work_val(erts_aint32_t value)
valid |= ERTS_SSI_AUX_WORK_ASYNC_READY_CLEAN;
#endif
#ifdef ERTS_SMP
+ valid |= ERTS_SSI_AUX_WORK_DELAYED_AW_WAKEUP;
valid |= ERTS_SSI_AUX_WORK_MISC_THR_PRGR;
valid |= ERTS_SSI_AUX_WORK_DD;
valid |= ERTS_SSI_AUX_WORK_DD_THR_PRGR;
+ valid |= ERTS_SSI_AUX_WORK_THR_PRGR_LATER_OP;
#endif
#if HAVE_ERTS_MSEG
valid |= ERTS_SSI_AUX_WORK_MSEG_CACHE_CHECK;
@@ -528,10 +368,6 @@ dbg_chk_aux_work_val(erts_aint32_t value)
#ifdef ERTS_SMP_SCHEDULERS_NEED_TO_CHECK_CHILDREN
valid |= ERTS_SSI_AUX_WORK_CHECK_CHILDREN;
#endif
-#ifdef ERTS_SMP
- valid |= ERTS_SSI_AUX_WORK_CODE_IX_ACTIVATION;
- valid |= ERTS_SSI_AUX_WORK_FINISH_BP;
-#endif
#ifdef ERTS_SSI_AUX_WORK_REAP_PORTS
valid |= ERTS_SSI_AUX_WORK_REAP_PORTS;
#endif
@@ -561,39 +397,17 @@ erts_smp_lc_runq_is_locked(ErtsRunQueue *runq)
}
#endif
-static erts_interval_t *proc_interval;
-
-static void
-proc_interval_init(void)
-{
- proc_interval = erts_alloc_permanent_cache_aligned(
- ERTS_ALC_T_PROC_INTERVAL,
- sizeof(erts_interval_t));
- erts_smp_interval_init(proc_interval);
-}
-
-static ERTS_INLINE Uint64
-get_proc_interval(void)
-{
- return erts_smp_current_interval_nob(proc_interval);
-}
static ERTS_INLINE Uint64
ensure_later_proc_interval(Uint64 interval)
{
- return erts_smp_ensure_later_interval_nob(proc_interval, interval);
-}
-
-static ERTS_INLINE Uint64
-step_proc_interval(void)
-{
- return erts_smp_step_interval_nob(proc_interval);
+ return erts_smp_ensure_later_interval_nob(erts_ptab_interval(&erts_proc), interval);
}
Uint64
erts_get_proc_interval(void)
{
- return get_proc_interval();
+ return erts_smp_current_interval_nob(erts_ptab_interval(&erts_proc));
}
Uint64
@@ -605,7 +419,7 @@ erts_ensure_later_proc_interval(Uint64 interval)
Uint64
erts_step_proc_interval(void)
{
- return step_proc_interval();
+ return erts_smp_step_interval_nob(erts_ptab_interval(&erts_proc));
}
void
@@ -653,20 +467,18 @@ erts_pre_init_process(void)
#endif
}
+#ifdef ERTS_SMP
+static void
+release_process(void *vproc)
+{
+ erts_smp_proc_dec_refc((Process *) vproc);
+}
+#endif
+
/* initialize the scheduler */
void
-erts_init_process(int ncpu)
+erts_init_process(int ncpu, int proc_tab_size)
{
- int proc_tab_sz;
- int max_proc_bits;
- int proc_bits = ERTS_PROC_BITS;
- erts_smp_atomic_t *proc_entry;
- char *proc_tab_end;
- erts_smp_rwmtx_opt_t proc_tab_rwmtx_opts = ERTS_SMP_RWMTX_OPT_DEFAULT_INITER;
- proc_tab_rwmtx_opts.type = ERTS_SMP_RWMTX_TYPE_EXTREMELY_FREQUENT_READ;
- proc_tab_rwmtx_opts.lived = ERTS_SMP_RWMTX_LONG_LIVED;
-
- proc_interval_init();
#ifdef ERTS_SMP
erts_disable_proc_not_running_opt = 0;
@@ -675,52 +487,16 @@ erts_init_process(int ncpu)
init_proclist_alloc();
- erts_smp_atomic32_init_nob(&process_count, 0);
-
- if (erts_use_r9_pids_ports)
- proc_bits = ERTS_R9_PROC_BITS;
-
- if (erts_proc.max > (1 << proc_bits))
- erts_proc.max = 1 << proc_bits;
-
- proc_tab_sz = ERTS_ALC_CACHE_LINE_ALIGN_SIZE(erts_proc.max
- * sizeof(erts_smp_atomic_t));
- erts_proc.tab = erts_alloc(ERTS_ALC_T_PROC_TABLE, proc_tab_sz);
- proc_tab_end = ((char *) erts_proc.tab) + proc_tab_sz;
- proc_entry = erts_proc.tab;
- while (proc_tab_end > ((char *) proc_entry)) {
- erts_smp_atomic_init_nob(proc_entry, ERTS_AINT_NULL);
- proc_entry++;
- }
-
- erts_smp_rwmtx_init_opt(&erts_proc_tab_rwmtx,
- &proc_tab_rwmtx_opts,
- "proc_tab");
- last_pid_data_init_nob(~((Uint64) 0));
-
- max_proc_bits = erts_fit_in_bits_int32((Sint32) erts_proc.max - 1);
-
- erts_proc.tab_cache_lines = proc_tab_sz/ERTS_CACHE_LINE_SIZE;
- erts_proc.pix_per_cache_line = ERTS_CACHE_LINE_SIZE/sizeof(erts_smp_atomic_t);
- if ((erts_proc.max & (erts_proc.max - 1))
- | (erts_proc.pix_per_cache_line & (erts_proc.pix_per_cache_line - 1))) {
- /*
- * erts_proc.max or erts_proc.pix_per_cache_line
- * not a power of 2 :(
- */
- erts_proc.pix_cl_mask = 0;
- erts_proc.pix_cl_shift = 0;
- erts_proc.pix_cli_mask = 0;
- erts_proc.pix_cli_shift = 0;
- }
- else {
- ASSERT((erts_proc.tab_cache_lines
- & (erts_proc.tab_cache_lines - 1)) == 0);
- erts_proc.pix_cl_mask = erts_proc.tab_cache_lines-1;
- erts_proc.pix_cl_shift = erts_fit_in_bits_int32(erts_proc.pix_per_cache_line-1);
- erts_proc.pix_cli_shift = erts_fit_in_bits_int32(erts_proc.pix_cl_mask);
- erts_proc.pix_cli_mask = (1 << (max_proc_bits - erts_proc.pix_cli_shift)) - 1;
- }
+ erts_ptab_init_table(&erts_proc,
+ ERTS_ALC_T_PROC_TABLE,
+#ifdef ERTS_SMP
+ release_process,
+#else
+ NULL,
+#endif
+ (ErtsPTabElementCommon *) &erts_invalid_process.common,
+ proc_tab_size,
+ "process_table");
last_reductions = 0;
last_exact_reductions = 0;
@@ -731,7 +507,6 @@ void
erts_late_init_process(void)
{
int ix;
- init_processes_bif();
erts_smp_spinlock_init(&erts_sched_stat.lock, "sched_stat");
for (ix = 0; ix < ERTS_NO_PRIO_LEVELS; ix++) {
@@ -979,9 +754,9 @@ static ERTS_INLINE ErtsProcList *
proclist_create(Process *p)
{
ErtsProcList *plp = proclist_alloc();
- ensure_later_proc_interval(p->started_interval);
- plp->pid = p->id;
- plp->started_interval = p->started_interval;
+ ensure_later_proc_interval(p->common.u.alive.started_interval);
+ plp->pid = p->common.id;
+ plp->started_interval = p->common.u.alive.started_interval;
return plp;
}
@@ -991,12 +766,6 @@ proclist_destroy(ErtsProcList *plp)
proclist_free(plp);
}
-static ERTS_INLINE int
-proclist_same(ErtsProcList *plp, Process *p)
-{
- return plp->pid == p->id && plp->started_interval == p->started_interval;
-}
-
ErtsProcList *
erts_proclist_create(Process *p)
{
@@ -1009,12 +778,6 @@ erts_proclist_destroy(ErtsProcList *plp)
proclist_destroy(plp);
}
-int
-erts_proclist_same(ErtsProcList *plp, Process *p)
-{
- return proclist_same(plp, p);
-}
-
void *
erts_psd_set_init(Process *p, ErtsProcLocks plocks, int ix, void *data)
{
@@ -1177,6 +940,67 @@ haw_thr_prgr_current_check_progress(ErtsAuxWorkData *awdp)
}
}
+static ERTS_INLINE erts_aint32_t
+handle_delayed_aux_work_wakeup(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, int waiting)
+{
+ int jix, max_jix;
+
+ ASSERT(awdp->delayed_wakeup.next != ERTS_DELAYED_WAKEUP_INFINITY);
+
+ if (!waiting && awdp->delayed_wakeup.next > awdp->esdp->reductions)
+ return aux_work;
+
+ unset_aux_work_flags(awdp->ssi, ERTS_SSI_AUX_WORK_DELAYED_AW_WAKEUP);
+
+ ERTS_THR_MEMORY_BARRIER;
+
+ max_jix = awdp->delayed_wakeup.jix;
+ awdp->delayed_wakeup.jix = -1;
+ for (jix = 0; jix <= max_jix; jix++) {
+ int sched = awdp->delayed_wakeup.job[jix].sched;
+ erts_aint32_t aux_work = awdp->delayed_wakeup.job[jix].aux_work;
+
+ ASSERT(awdp->delayed_wakeup.sched2jix[sched] == jix);
+ awdp->delayed_wakeup.sched2jix[sched] = -1;
+ set_aux_work_flags_wakeup_nob(ERTS_SCHED_SLEEP_INFO_IX(sched-1),
+ aux_work);
+ }
+ awdp->delayed_wakeup.next = ERTS_DELAYED_WAKEUP_INFINITY;
+ return aux_work & ~ERTS_SSI_AUX_WORK_DELAYED_AW_WAKEUP;
+}
+
+static ERTS_INLINE void
+schedule_aux_work_wakeup(ErtsAuxWorkData *awdp,
+ int sched,
+ erts_aint32_t aux_work)
+{
+ int jix = awdp->delayed_wakeup.sched2jix[sched];
+ if (jix >= 0) {
+ ASSERT(awdp->delayed_wakeup.job[jix].sched == sched);
+ awdp->delayed_wakeup.job[jix].aux_work |= aux_work;
+ }
+ else {
+ jix = ++awdp->delayed_wakeup.jix;
+ awdp->delayed_wakeup.sched2jix[sched] = jix;
+ awdp->delayed_wakeup.job[jix].sched = sched;
+ awdp->delayed_wakeup.job[jix].aux_work = aux_work;
+ }
+
+ if (awdp->delayed_wakeup.next != ERTS_DELAYED_WAKEUP_INFINITY) {
+ ASSERT(erts_atomic32_read_nob(&awdp->ssi->aux_work)
+ & ERTS_SSI_AUX_WORK_DELAYED_AW_WAKEUP);
+ }
+ else {
+ awdp->delayed_wakeup.next = (awdp->esdp->reductions
+ + ERTS_DELAYED_WAKEUP_REDUCTIONS);
+
+ ASSERT(!(erts_atomic32_read_nob(&awdp->ssi->aux_work)
+ & ERTS_SSI_AUX_WORK_DELAYED_AW_WAKEUP));
+ set_aux_work_flags_wakeup_nob(awdp->ssi,
+ ERTS_SSI_AUX_WORK_DELAYED_AW_WAKEUP);
+ }
+}
+
#endif
typedef struct erts_misc_aux_work_t_ erts_misc_aux_work_t;
@@ -1253,7 +1077,8 @@ misc_aux_work_clean(ErtsThrQ_t *q,
static ERTS_INLINE erts_aint32_t
handle_misc_aux_work(ErtsAuxWorkData *awdp,
- erts_aint32_t aux_work)
+ erts_aint32_t aux_work,
+ int waiting)
{
ErtsThrQ_t *q = &misc_aux_work_queues[awdp->sched_id].q;
@@ -1273,7 +1098,8 @@ handle_misc_aux_work(ErtsAuxWorkData *awdp,
static ERTS_INLINE erts_aint32_t
handle_misc_aux_work_thr_prgr(ErtsAuxWorkData *awdp,
- erts_aint32_t aux_work)
+ erts_aint32_t aux_work,
+ int waiting)
{
if (!erts_thr_progress_has_reached_this(haw_thr_prgr_current(awdp),
awdp->misc.thr_prgr))
@@ -1352,7 +1178,8 @@ erts_notify_check_async_ready_queue(void *vno)
static ERTS_INLINE erts_aint32_t
handle_async_ready(ErtsAuxWorkData *awdp,
- erts_aint32_t aux_work)
+ erts_aint32_t aux_work,
+ int waiting)
{
ErtsSchedulerSleepInfo *ssi = awdp->ssi;
unset_aux_work_flags(ssi, ERTS_SSI_AUX_WORK_ASYNC_READY);
@@ -1374,7 +1201,8 @@ handle_async_ready(ErtsAuxWorkData *awdp,
static ERTS_INLINE erts_aint32_t
handle_async_ready_clean(ErtsAuxWorkData *awdp,
- erts_aint32_t aux_work)
+ erts_aint32_t aux_work,
+ int waiting)
{
void *thr_prgr_p;
@@ -1409,95 +1237,9 @@ handle_async_ready_clean(ErtsAuxWorkData *awdp,
#endif /* ERTS_USE_ASYNC_READY_Q */
-#ifdef ERTS_SMP
-void
-erts_notify_code_ix_activation(Process* p, ErtsThrPrgrVal later)
-{
- ErtsAuxWorkData* awdp = &p->scheduler_data->aux_work_data;
- ASSERT(awdp->code_ix_activation.code_stager == NULL);
- awdp->code_ix_activation.code_stager = p;
- awdp->code_ix_activation.thr_prgr = later;
- erts_smp_proc_inc_refc(p);
- set_aux_work_flags_wakeup_relb(p->scheduler_data->ssi,
- ERTS_SSI_AUX_WORK_CODE_IX_ACTIVATION);
-}
-
-static erts_aint32_t
-handle_code_ix_activation(ErtsAuxWorkData *awdp, erts_aint32_t aux_work)
-{
- Process* p;
- if (!erts_thr_progress_has_reached(awdp->code_ix_activation.thr_prgr)) {
- return aux_work & ~ERTS_SSI_AUX_WORK_CODE_IX_ACTIVATION;
- }
- unset_aux_work_flags(awdp->ssi, ERTS_SSI_AUX_WORK_CODE_IX_ACTIVATION);
- p = awdp->code_ix_activation.code_stager;
- ASSERT(p);
-#ifdef DEBUG
- awdp->code_ix_activation.code_stager = NULL;
-#endif
- erts_commit_staging_code_ix();
- erts_release_code_write_permission();
- erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS);
- if (!ERTS_PROC_IS_EXITING(p)) {
- erts_resume(p, ERTS_PROC_LOCK_STATUS);
- }
- erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
- erts_smp_proc_dec_refc(p);
- return aux_work & ~ERTS_SSI_AUX_WORK_CODE_IX_ACTIVATION;
-}
-#endif /* ERTS_SMP */
-
-#ifdef ERTS_SMP
-void
-erts_notify_finish_breakpointing(Process* p)
-{
- ErtsAuxWorkData* awdp = &p->scheduler_data->aux_work_data;
-
- ASSERT(awdp->bp_ix_activation.stager == NULL);
- awdp->bp_ix_activation.stager = p;
- awdp->bp_ix_activation.thr_prgr = erts_thr_progress_later(awdp->esdp);
- erts_thr_progress_wakeup(awdp->esdp, awdp->bp_ix_activation.thr_prgr);
- erts_smp_proc_inc_refc(p);
- set_aux_work_flags_wakeup_relb(p->scheduler_data->ssi,
- ERTS_SSI_AUX_WORK_FINISH_BP);
-}
-
-static erts_aint32_t
-handle_finish_bp(ErtsAuxWorkData* awdp, erts_aint32_t aux_work)
-{
- ErtsThrPrgrVal current = haw_thr_prgr_current(awdp);
-
- if (!erts_thr_progress_has_reached_this(current,
- awdp->bp_ix_activation.thr_prgr)) {
- return aux_work & ~ERTS_SSI_AUX_WORK_FINISH_BP;
- }
- if (erts_finish_breakpointing()) { /* Not done */
- /* Arrange for being called again */
- awdp->bp_ix_activation.thr_prgr =
- erts_thr_progress_later(awdp->esdp);
- erts_thr_progress_wakeup(awdp->esdp, awdp->bp_ix_activation.thr_prgr);
- } else { /* Done */
- Process* p;
-
- unset_aux_work_flags(awdp->ssi, ERTS_SSI_AUX_WORK_FINISH_BP);
- p = awdp->bp_ix_activation.stager;
-#ifdef DEBUG
- awdp->bp_ix_activation.stager = NULL;
-#endif
- erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS);
- if (!ERTS_PROC_IS_EXITING(p)) {
- erts_resume(p, ERTS_PROC_LOCK_STATUS);
- }
- erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
- erts_smp_proc_dec_refc(p);
- erts_release_code_write_permission();
- }
- return aux_work & ~ERTS_SSI_AUX_WORK_FINISH_BP;
-}
-#endif /* ERTS_SMP */
static ERTS_INLINE erts_aint32_t
-handle_fix_alloc(ErtsAuxWorkData *awdp, erts_aint32_t aux_work)
+handle_fix_alloc(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, int waiting)
{
ErtsSchedulerSleepInfo *ssi = awdp->ssi;
erts_aint32_t res;
@@ -1520,12 +1262,18 @@ handle_fix_alloc(ErtsAuxWorkData *awdp, erts_aint32_t aux_work)
void
erts_alloc_notify_delayed_dealloc(int ix)
{
- set_aux_work_flags_wakeup_nob(ERTS_SCHED_SLEEP_INFO_IX(ix-1),
- ERTS_SSI_AUX_WORK_DD);
+ ErtsSchedulerData *esdp = erts_get_scheduler_data();
+ if (esdp)
+ schedule_aux_work_wakeup(&esdp->aux_work_data,
+ ix,
+ ERTS_SSI_AUX_WORK_DD);
+ else
+ set_aux_work_flags_wakeup_relb(ERTS_SCHED_SLEEP_INFO_IX(ix-1),
+ ERTS_SSI_AUX_WORK_DD);
}
static ERTS_INLINE erts_aint32_t
-handle_delayed_dealloc(ErtsAuxWorkData *awdp, erts_aint32_t aux_work)
+handle_delayed_dealloc(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, int waiting)
{
ErtsSchedulerSleepInfo *ssi = awdp->ssi;
int need_thr_progress = 0;
@@ -1563,7 +1311,7 @@ handle_delayed_dealloc(ErtsAuxWorkData *awdp, erts_aint32_t aux_work)
}
static ERTS_INLINE erts_aint32_t
-handle_delayed_dealloc_thr_prgr(ErtsAuxWorkData *awdp, erts_aint32_t aux_work)
+handle_delayed_dealloc_thr_prgr(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, int waiting)
{
ErtsSchedulerSleepInfo *ssi;
int need_thr_progress;
@@ -1607,6 +1355,77 @@ handle_delayed_dealloc_thr_prgr(ErtsAuxWorkData *awdp, erts_aint32_t aux_work)
return aux_work & ~ERTS_SSI_AUX_WORK_DD_THR_PRGR;
}
+/*
+ * Handle scheduled thread progress later operations.
+ */
+#define ERTS_MAX_THR_PRGR_LATER_OPS 50
+
+static ERTS_INLINE erts_aint32_t
+handle_thr_prgr_later_op(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, int waiting)
+{
+ int lops;
+ ErtsThrPrgrVal current = haw_thr_prgr_current(awdp);
+
+ for (lops = 0; lops < ERTS_MAX_THR_PRGR_LATER_OPS; lops++) {
+ ErtsThrPrgrLaterOp *lop = awdp->later_op.first;
+ if (!erts_thr_progress_has_reached_this(current, lop->later))
+ return aux_work & ~ERTS_SSI_AUX_WORK_THR_PRGR_LATER_OP;
+ awdp->later_op.first = lop->next;
+ if (!awdp->later_op.first) {
+ awdp->later_op.last = NULL;
+ }
+ lop->func(lop->data);
+ if (!awdp->later_op.first) {
+ awdp->later_op.last = NULL;
+ unset_aux_work_flags(awdp->ssi,
+ ERTS_SSI_AUX_WORK_THR_PRGR_LATER_OP);
+ return aux_work & ~ERTS_SSI_AUX_WORK_THR_PRGR_LATER_OP;
+ }
+ }
+
+ return aux_work;
+}
+
+#endif /* ERTS_SMP */
+
+void
+erts_schedule_thr_prgr_later_op(void (*later_func)(void *),
+ void *later_data,
+ ErtsThrPrgrLaterOp *lop)
+{
+#ifndef ERTS_SMP
+ later_func(later_data);
+#else
+ ErtsSchedulerData *esdp;
+ ErtsAuxWorkData *awdp;
+ int request_wakeup = 1;
+
+ esdp = erts_get_scheduler_data();
+ ASSERT(esdp);
+ awdp = &esdp->aux_work_data;
+
+ lop->func = later_func;
+ lop->data = later_data;
+ lop->later = erts_thr_progress_later(esdp);
+ lop->next = NULL;
+ if (!awdp->later_op.last)
+ awdp->later_op.first = lop;
+ else {
+ ErtsThrPrgrLaterOp *last = awdp->later_op.last;
+ last->next = lop;
+ if (erts_thr_progress_equal(last->later, lop->later))
+ request_wakeup = 0;
+ }
+ awdp->later_op.last = lop;
+ set_aux_work_flags_wakeup_nob(awdp->ssi,
+ ERTS_SSI_AUX_WORK_THR_PRGR_LATER_OP);
+ if (request_wakeup)
+ erts_thr_progress_wakeup(esdp, lop->later);
+#endif
+}
+
+#ifdef ERTS_SMP
+
static erts_atomic32_t completed_dealloc_count;
static void
@@ -1691,7 +1510,7 @@ erts_smp_notify_check_children_needed(void)
}
static ERTS_INLINE erts_aint32_t
-handle_check_children(ErtsAuxWorkData *awdp, erts_aint32_t aux_work)
+handle_check_children(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, int waiting)
{
unset_aux_work_flags(awdp->ssi, ERTS_SSI_AUX_WORK_CHECK_CHILDREN);
erts_check_children();
@@ -1714,42 +1533,37 @@ erts_smp_atomic32_t erts_halt_progress;
int erts_halt_code;
static ERTS_INLINE erts_aint32_t
-handle_reap_ports(ErtsAuxWorkData *awdp, erts_aint32_t aux_work)
+handle_reap_ports(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, int waiting)
{
unset_aux_work_flags(awdp->ssi, ERTS_SSI_AUX_WORK_REAP_PORTS);
awdp->esdp->run_queue->halt_in_progress = 1;
if (erts_smp_atomic32_dec_read_acqb(&erts_halt_progress) == 0) {
- int i;
+ int i, max = erts_ptab_max(&erts_port);
erts_smp_atomic32_set_nob(&erts_halt_progress, 1);
- for (i = 0; i < erts_max_ports; i++) {
- Port *prt = &erts_port[i];
- erts_smp_port_state_lock(prt);
- if ((prt->status & (ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP
- | ERTS_PORT_SFLG_HALT))) {
- erts_smp_port_state_unlock(prt);
- continue;
- }
- /* We need to set the halt flag - get the port lock */
-#ifdef ERTS_SMP
- erts_smp_atomic_inc_nob(&prt->refc);
-#endif
- erts_smp_port_state_unlock(prt);
-#ifdef ERTS_SMP
- erts_smp_mtx_lock(prt->lock);
-#endif
- if ((prt->status & (ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP
- | ERTS_PORT_SFLG_HALT))) {
- erts_port_release(prt);
+ for (i = 0; i < max; i++) {
+ erts_aint32_t state;
+ Port *prt = erts_pix2port(i);
+ if (!prt)
continue;
- }
- erts_port_status_bor_set(prt, ERTS_PORT_SFLG_HALT);
- erts_smp_atomic32_inc_nob(&erts_halt_progress);
- if (prt->status & (ERTS_PORT_SFLG_EXITING
- | ERTS_PORT_SFLG_CLOSING)) {
- erts_port_release(prt);
+ state = erts_atomic32_read_acqb(&prt->state);
+ if (state & (ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP
+ | ERTS_PORT_SFLG_HALT))
continue;
+
+ /* We need to set the halt flag - get the port lock */
+
+ erts_smp_port_lock(prt);
+
+ state = erts_atomic32_read_nob(&prt->state);
+ if (!(state & (ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP
+ | ERTS_PORT_SFLG_HALT))) {
+ state = erts_atomic32_read_bor_relb(&prt->state,
+ ERTS_PORT_SFLG_HALT);
+ erts_smp_atomic32_inc_nob(&erts_halt_progress);
+ if (!(state & (ERTS_PORT_SFLG_EXITING|ERTS_PORT_SFLG_CLOSING)))
+ erts_deliver_port_exit(prt, prt->common.id, am_killed, 0);
}
- erts_do_exit_port(prt, prt->id, am_killed);
+
erts_port_release(prt);
}
if (erts_smp_atomic32_dec_read_nob(&erts_halt_progress) == 0) {
@@ -1762,7 +1576,7 @@ handle_reap_ports(ErtsAuxWorkData *awdp, erts_aint32_t aux_work)
#if HAVE_ERTS_MSEG
static ERTS_INLINE erts_aint32_t
-handle_mseg_cache_check(ErtsAuxWorkData *awdp, erts_aint32_t aux_work)
+handle_mseg_cache_check(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, int waiting)
{
unset_aux_work_flags(awdp->ssi, ERTS_SSI_AUX_WORK_MSEG_CACHE_CHECK);
erts_mseg_cache_check();
@@ -1772,7 +1586,7 @@ handle_mseg_cache_check(ErtsAuxWorkData *awdp, erts_aint32_t aux_work)
#endif
static ERTS_INLINE erts_aint32_t
-handle_setup_aux_work_timer(ErtsAuxWorkData *awdp, erts_aint32_t aux_work)
+handle_setup_aux_work_timer(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, int waiting)
{
unset_aux_work_flags(awdp->ssi, ERTS_SSI_AUX_WORK_SET_TMO);
setup_aux_work_timer();
@@ -1786,7 +1600,7 @@ handle_aux_work(ErtsAuxWorkData *awdp, erts_aint32_t orig_aux_work, int waiting)
#define HANDLE_AUX_WORK(FLG, HNDLR) \
ignore |= FLG; \
if (aux_work & FLG) { \
- aux_work = HNDLR(awdp, aux_work); \
+ aux_work = HNDLR(awdp, aux_work, waiting); \
ERTS_DBG_CHK_AUX_WORK_VAL(aux_work); \
if (!(aux_work & ~ignore)) { \
ERTS_DBG_CHK_AUX_WORK_VAL(aux_work); \
@@ -1819,6 +1633,8 @@ handle_aux_work(ErtsAuxWorkData *awdp, erts_aint32_t orig_aux_work, int waiting)
* eachother. Most frequent first.
*/
#ifdef ERTS_SMP
+ HANDLE_AUX_WORK(ERTS_SSI_AUX_WORK_DELAYED_AW_WAKEUP,
+ handle_delayed_aux_work_wakeup);
HANDLE_AUX_WORK(ERTS_SSI_AUX_WORK_DD,
handle_delayed_dealloc);
/* DD must be before DD_THR_PRGR */
@@ -1830,6 +1646,11 @@ handle_aux_work(ErtsAuxWorkData *awdp, erts_aint32_t orig_aux_work, int waiting)
| ERTS_SSI_AUX_WORK_FIX_ALLOC_DEALLOC),
handle_fix_alloc);
+#ifdef ERTS_SMP
+ HANDLE_AUX_WORK(ERTS_SSI_AUX_WORK_THR_PRGR_LATER_OP,
+ handle_thr_prgr_later_op);
+#endif
+
#if ERTS_USE_ASYNC_READY_Q
HANDLE_AUX_WORK(ERTS_SSI_AUX_WORK_ASYNC_READY,
handle_async_ready);
@@ -1859,19 +1680,9 @@ handle_aux_work(ErtsAuxWorkData *awdp, erts_aint32_t orig_aux_work, int waiting)
handle_mseg_cache_check);
#endif
-#ifdef ERTS_SMP
- HANDLE_AUX_WORK(ERTS_SSI_AUX_WORK_CODE_IX_ACTIVATION,
- handle_code_ix_activation);
-#endif
-
HANDLE_AUX_WORK(ERTS_SSI_AUX_WORK_REAP_PORTS,
handle_reap_ports);
-#ifdef ERTS_SMP
- HANDLE_AUX_WORK(ERTS_SSI_AUX_WORK_FINISH_BP,
- handle_finish_bp);
-#endif
-
ERTS_DBG_CHK_AUX_WORK_VAL(aux_work);
#ifdef ERTS_SMP
@@ -2030,8 +1841,8 @@ sched_waiting_sys(Uint no, ErtsRunQueue *rq)
{
ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(rq));
ASSERT(rq->waiting >= 0);
- ERTS_RUNQ_FLGS_SET(rq, (ERTS_RUNQ_FLG_OUT_OF_WORK
- | ERTS_RUNQ_FLG_HALFTIME_OUT_OF_WORK));
+ (void) ERTS_RUNQ_FLGS_SET(rq, (ERTS_RUNQ_FLG_OUT_OF_WORK
+ | ERTS_RUNQ_FLG_HALFTIME_OUT_OF_WORK));
rq->waiting++;
rq->waiting *= -1;
rq->woken = 0;
@@ -2107,8 +1918,8 @@ static ERTS_INLINE void
sched_waiting(Uint no, ErtsRunQueue *rq)
{
ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(rq));
- ERTS_RUNQ_FLGS_SET(rq, (ERTS_RUNQ_FLG_OUT_OF_WORK
- | ERTS_RUNQ_FLG_HALFTIME_OUT_OF_WORK));
+ (void) ERTS_RUNQ_FLGS_SET(rq, (ERTS_RUNQ_FLG_OUT_OF_WORK
+ | ERTS_RUNQ_FLG_HALFTIME_OUT_OF_WORK));
if (rq->waiting < 0)
rq->waiting--;
else
@@ -2305,7 +2116,7 @@ thr_prgr_fin_wait(void *vssi)
| ERTS_SSI_FLG_TSE_SLEEPING));
}
-static void init_aux_work_data(ErtsAuxWorkData *awdp, ErtsSchedulerData *esdp);
+static void init_aux_work_data(ErtsAuxWorkData *awdp, ErtsSchedulerData *esdp, char *dawwp);
static void *
aux_thread(void *unused)
@@ -2325,7 +2136,7 @@ aux_thread(void *unused)
callbacks.finalize_wait = thr_prgr_fin_wait;
erts_thr_progress_register_managed_thread(NULL, &callbacks, 1);
- init_aux_work_data(awdp, NULL);
+ init_aux_work_data(awdp, NULL, NULL);
awdp->ssi = ssi;
sched_prep_spin_wait(ssi);
@@ -2782,7 +2593,7 @@ chk_wake_sched(ErtsRunQueue *crq, int ix, int activate)
if (!(flags & (ERTS_RUNQ_FLG_SUSPENDED|ERTS_RUNQ_FLG_NONEMPTY))) {
if (activate) {
if (try_inc_no_active_runqs(ix+1))
- ERTS_RUNQ_FLGS_UNSET(wrq, ERTS_RUNQ_FLG_INACTIVE);
+ (void) ERTS_RUNQ_FLGS_UNSET(wrq, ERTS_RUNQ_FLG_INACTIVE);
}
wake_scheduler(wrq, 0);
return 1;
@@ -2849,7 +2660,7 @@ erts_sched_notify_check_cpu_bind(void)
int ix;
for (ix = 0; ix < erts_no_run_queues; ix++) {
ErtsRunQueue *rq = ERTS_RUNQ_IX(ix);
- ERTS_RUNQ_FLGS_SET(rq, ERTS_RUNQ_FLG_CHK_CPU_BIND);
+ (void) ERTS_RUNQ_FLGS_SET(rq, ERTS_RUNQ_FLG_CHK_CPU_BIND);
wake_scheduler(rq, 0);
}
#else
@@ -3115,7 +2926,7 @@ suspend_run_queue(ErtsRunQueue *rq)
{
erts_smp_atomic32_read_bor_nob(&rq->scheduler->ssi->flags,
ERTS_SSI_FLG_SUSPENDED);
- ERTS_RUNQ_FLGS_SET(rq, ERTS_RUNQ_FLG_SUSPENDED);
+ (void) ERTS_RUNQ_FLGS_SET(rq, ERTS_RUNQ_FLG_SUSPENDED);
wake_scheduler(rq, 0);
}
@@ -3129,12 +2940,12 @@ resume_run_queue(ErtsRunQueue *rq)
erts_smp_runq_lock(rq);
- (void) ERTS_RUNQ_FLGS_MASK_SET(rq,
- (ERTS_RUNQ_FLG_OUT_OF_WORK
- | ERTS_RUNQ_FLG_HALFTIME_OUT_OF_WORK
- | ERTS_RUNQ_FLG_SUSPENDED),
- (ERTS_RUNQ_FLG_OUT_OF_WORK
- | ERTS_RUNQ_FLG_HALFTIME_OUT_OF_WORK));
+ (void) ERTS_RUNQ_FLGS_READ_BSET(rq,
+ (ERTS_RUNQ_FLG_OUT_OF_WORK
+ | ERTS_RUNQ_FLG_HALFTIME_OUT_OF_WORK
+ | ERTS_RUNQ_FLG_SUSPENDED),
+ (ERTS_RUNQ_FLG_OUT_OF_WORK
+ | ERTS_RUNQ_FLG_HALFTIME_OUT_OF_WORK));
rq->check_balance_reds = ERTS_RUNQ_CALL_CHECK_BALANCE_REDS;
for (pix = 0; pix < ERTS_NO_PROC_PRIO_LEVELS; pix++) {
@@ -3182,7 +2993,7 @@ evacuate_run_queue(ErtsRunQueue *rq,
ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(rq));
- ERTS_RUNQ_FLGS_UNSET(rq, ERTS_RUNQ_FLG_PROTECTED);
+ (void) ERTS_RUNQ_FLGS_UNSET(rq, ERTS_RUNQ_FLG_PROTECTED);
mps = erts_get_migration_paths_managed();
mp = &mps->mpath[rq->ix];
@@ -3687,9 +3498,9 @@ check_balance(ErtsRunQueue *c_rq)
ERTS_FOREACH_RUNQ(rq,
{
if (rq->waiting)
- ERTS_RUNQ_FLGS_SET(rq, ERTS_RUNQ_FLG_HALFTIME_OUT_OF_WORK);
+ (void) ERTS_RUNQ_FLGS_SET(rq, ERTS_RUNQ_FLG_HALFTIME_OUT_OF_WORK);
else
- ERTS_RUNQ_FLGS_UNSET(rq, ERTS_RUNQ_FLG_HALFTIME_OUT_OF_WORK);
+ (void) ERTS_RUNQ_FLGS_UNSET(rq, ERTS_RUNQ_FLG_HALFTIME_OUT_OF_WORK);
rq->check_balance_reds = ERTS_RUNQ_CALL_CHECK_BALANCE_REDS;
});
@@ -4156,7 +3967,7 @@ erts_fprintf(stderr, "--------------------------------\n");
ERTS_DBG_CHK_FULL_REDS_HISTORY(rq);
rq->out_of_work_count = 0;
- (void) ERTS_RUNQ_FLGS_MASK_SET(rq, ERTS_RUNQ_FLGS_MIGRATION_INFO, flags);
+ (void) ERTS_RUNQ_FLGS_READ_BSET(rq, ERTS_RUNQ_FLGS_MIGRATION_INFO, flags);
rq->max_len = rq->len;
for (pix = 0; pix < ERTS_NO_PRIO_LEVELS; pix++) {
@@ -4269,11 +4080,11 @@ typedef enum {
} ErtsSchedWakeupOtherThreshold;
typedef enum {
- ERTS_SCHED_WAKEUP_OTHER_TYPE_PROPOSAL,
+ ERTS_SCHED_WAKEUP_OTHER_TYPE_DEFAULT,
ERTS_SCHED_WAKEUP_OTHER_TYPE_LEGACY
} ErtsSchedWakeupOtherType;
-/* First proposal */
+/* Default */
#define ERTS_WAKEUP_OTHER_LIMIT_VERY_HIGH (200*CONTEXT_REDS)
#define ERTS_WAKEUP_OTHER_LIMIT_HIGH (50*CONTEXT_REDS)
@@ -4290,7 +4101,7 @@ typedef enum {
#define ERTS_WAKEUP_OTHER_DEC_SHIFT 2
#define ERTS_WAKEUP_OTHER_FIXED_INC (CONTEXT_REDS/10)
-/* To be legacy */
+/* Legacy */
#define ERTS_WAKEUP_OTHER_LIMIT_VERY_HIGH_LEGACY (200*CONTEXT_REDS)
#define ERTS_WAKEUP_OTHER_LIMIT_HIGH_LEGACY (50*CONTEXT_REDS)
@@ -4332,7 +4143,7 @@ wakeup_other_check(ErtsRunQueue *rq, Uint32 flags)
int empty_rqs =
erts_smp_atomic32_read_acqb(&no_empty_run_queues);
if (flags & ERTS_RUNQ_FLG_PROTECTED)
- ERTS_RUNQ_FLGS_UNSET(rq, ERTS_RUNQ_FLG_PROTECTED);
+ (void) ERTS_RUNQ_FLGS_UNSET(rq, ERTS_RUNQ_FLG_PROTECTED);
if (empty_rqs != 0)
wake_scheduler_on_empty_runq(rq);
rq->wakeup_other = 0;
@@ -4391,7 +4202,7 @@ wakeup_other_check_legacy(ErtsRunQueue *rq, Uint32 flags)
rq->wakeup_other += len*wo_reds + ERTS_WAKEUP_OTHER_FIXED_INC_LEGACY;
else {
if (flags & ERTS_RUNQ_FLG_PROTECTED)
- ERTS_RUNQ_FLGS_UNSET(rq, ERTS_RUNQ_FLG_PROTECTED);
+ (void) ERTS_RUNQ_FLGS_UNSET(rq, ERTS_RUNQ_FLG_PROTECTED);
if (erts_smp_atomic32_read_acqb(&no_empty_run_queues) != 0) {
wake_scheduler_on_empty_runq(rq);
rq->wakeup_other = 0;
@@ -4428,7 +4239,7 @@ static void
set_wakeup_other_data(void)
{
switch (wakeup_other.type) {
- case ERTS_SCHED_WAKEUP_OTHER_TYPE_PROPOSAL:
+ case ERTS_SCHED_WAKEUP_OTHER_TYPE_DEFAULT:
wakeup_other.check = wakeup_other_check;
wakeup_other_set_limit();
break;
@@ -4447,7 +4258,7 @@ erts_early_init_scheduling(int no_schedulers)
aux_work_timeout_early_init(no_schedulers);
#ifdef ERTS_SMP
wakeup_other.threshold = ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_MEDIUM;
- wakeup_other.type = ERTS_SCHED_WAKEUP_OTHER_TYPE_LEGACY;
+ wakeup_other.type = ERTS_SCHED_WAKEUP_OTHER_TYPE_DEFAULT;
#endif
sched_busy_wait.sys_schedule = ERTS_SCHED_SYS_SLEEP_SPINCOUNT_MEDIUM;
sched_busy_wait.tse = (ERTS_SCHED_SYS_SLEEP_SPINCOUNT_MEDIUM
@@ -4483,10 +4294,8 @@ int
erts_sched_set_wakeup_other_type(char *str)
{
ErtsSchedWakeupOtherType type;
- if (sys_strcmp(str, "proposal") == 0)
- type = ERTS_SCHED_WAKEUP_OTHER_TYPE_PROPOSAL;
- else if (sys_strcmp(str, "default") == 0)
- type = ERTS_SCHED_WAKEUP_OTHER_TYPE_LEGACY;
+ if (sys_strcmp(str, "default") == 0)
+ type = ERTS_SCHED_WAKEUP_OTHER_TYPE_DEFAULT;
else if (sys_strcmp(str, "legacy") == 0)
type = ERTS_SCHED_WAKEUP_OTHER_TYPE_LEGACY;
else
@@ -4538,7 +4347,7 @@ erts_sched_set_busy_wait_threshold(char *str)
return 0;
}
static void
-init_aux_work_data(ErtsAuxWorkData *awdp, ErtsSchedulerData *esdp)
+init_aux_work_data(ErtsAuxWorkData *awdp, ErtsSchedulerData *esdp, char *dawwp)
{
awdp->sched_id = esdp ? (int) esdp->no : 0;
awdp->esdp = esdp;
@@ -4548,6 +4357,8 @@ init_aux_work_data(ErtsAuxWorkData *awdp, ErtsSchedulerData *esdp)
awdp->dd.thr_prgr = ERTS_THR_PRGR_VAL_WAITING;
awdp->dd.completed_callback = NULL;
awdp->dd.completed_arg = NULL;
+ awdp->later_op.first = NULL;
+ awdp->later_op.last = NULL;
#endif
#ifdef ERTS_USE_ASYNC_READY_Q
#ifdef ERTS_SMP
@@ -4556,12 +4367,33 @@ init_aux_work_data(ErtsAuxWorkData *awdp, ErtsSchedulerData *esdp)
#endif
awdp->async_ready.queue = NULL;
#endif
+#ifdef ERTS_SMP
+ awdp->delayed_wakeup.next = ERTS_DELAYED_WAKEUP_INFINITY;
+ if (!dawwp) {
+ awdp->delayed_wakeup.job = NULL;
+ awdp->delayed_wakeup.sched2jix = NULL;
+ awdp->delayed_wakeup.jix = -1;
+ }
+ else {
+ int i;
+ awdp->delayed_wakeup.job = (ErtsDelayedAuxWorkWakeupJob *) dawwp;
+ dawwp += sizeof(ErtsDelayedAuxWorkWakeupJob)*(erts_no_schedulers+1);
+ awdp->delayed_wakeup.sched2jix = (int *) dawwp;
+ awdp->delayed_wakeup.jix = -1;
+ for (i = 0; i <= erts_no_schedulers; i++)
+ awdp->delayed_wakeup.sched2jix[i] = -1;
+ }
+#endif
}
void
erts_init_scheduling(int no_schedulers, int no_schedulers_online)
{
int ix, n, no_ssi;
+ char *daww_ptr;
+#ifdef ERTS_SMP
+ size_t daww_sz;
+#endif
init_misc_op_list_alloc();
@@ -4684,6 +4516,15 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online)
/* Create and initialize scheduler specific data */
+#ifdef ERTS_SMP
+ daww_sz = ERTS_ALC_CACHE_LINE_ALIGN_SIZE((sizeof(ErtsDelayedAuxWorkWakeupJob)
+ + sizeof(int))*(n+1));
+ daww_ptr = erts_alloc_permanent_cache_aligned(ERTS_ALC_T_SCHDLR_DATA,
+ daww_sz*n);
+#else
+ daww_ptr = NULL;
+#endif
+
erts_aligned_scheduler_data =
erts_alloc_permanent_cache_aligned(ERTS_ALC_T_SCHDLR_DATA,
n*sizeof(ErtsAlignedSchedulerData));
@@ -4718,7 +4559,13 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online)
esdp->run_queue = ERTS_RUNQ_IX(ix);
esdp->run_queue->scheduler = esdp;
- init_aux_work_data(&esdp->aux_work_data, esdp);
+ init_aux_work_data(&esdp->aux_work_data, esdp, daww_ptr);
+#ifdef ERTS_SMP
+ daww_ptr += daww_sz;
+#endif
+
+ esdp->reductions = 0;
+
init_sched_wall_time(&esdp->sched_wall_time);
}
@@ -4804,6 +4651,11 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online)
erts_smp_atomic32_init_relb(&erts_halt_progress, -1);
erts_halt_code = 0;
+
+#if !defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK)
+ erts_lc_set_thread_name("scheduler 1");
+#endif
+
}
ErtsRunQueue *
@@ -4921,7 +4773,9 @@ schedule_process(Process *p, erts_aint32_t state, int active_enq)
while (1) {
erts_aint32_t e;
n = e = a;
- ASSERT(!(a & ERTS_PSFLG_FREE));
+
+ if (a & ERTS_PSFLG_FREE)
+ return; /* We don't want to schedule free processes... */
n |= ERTS_PSFLG_ACTIVE;
if (!(a & (ERTS_PSFLG_SUSPENDED|ERTS_PSFLG_RUNNING)))
n |= ERTS_PSFLG_IN_RUNQ;
@@ -4932,23 +4786,13 @@ schedule_process(Process *p, erts_aint32_t state, int active_enq)
return; /* Someone else activated process ... */
}
-#ifdef RRR_DEBUG
- if (active_enq)
- erts_fprintf(stderr, "! state=0x%x\n", n);
-#endif
-
if (erts_system_profile_flags.runnable_procs
&& !(a & (ERTS_PSFLG_ACTIVE|ERTS_PSFLG_SUSPENDED))) {
profile_runnable_proc(p, am_active);
}
- if ((n & ERTS_PSFLG_IN_RUNQ) && !(a & ERTS_PSFLG_IN_RUNQ)) {
-#ifdef RRR_DEBUG
- if (active_enq)
- erts_fprintf(stderr, "-->\n");
-#endif
+ if ((n & ERTS_PSFLG_IN_RUNQ) && !(a & ERTS_PSFLG_IN_RUNQ))
add2runq(p, n);
- }
}
void
@@ -5018,9 +4862,6 @@ resume_process(Process *p)
state = erts_smp_atomic32_read_band_mb(&p->state, ~ERTS_PSFLG_SUSPENDED);
state &= ~ERTS_PSFLG_SUSPENDED;
-#ifdef RRR_DEBUG
- erts_fprintf(stderr, "%T - state=0x%x\n", p->id, state);
-#endif
if ((state & (ERTS_PSFLG_EXITING
| ERTS_PSFLG_ACTIVE
| ERTS_PSFLG_IN_RUNQ
@@ -5492,8 +5333,7 @@ erts_block_multi_scheduling(Process *p, ErtsProcLocks plocks, int on, int all)
else if (on) { /* ------ BLOCK ------ */
if (schdlr_sspnd.msb.procs) {
plp = proclist_create(p);
- plp->next = schdlr_sspnd.msb.procs;
- schdlr_sspnd.msb.procs = plp;
+ erts_proclist_store_last(&schdlr_sspnd.msb.procs, plp);
p->flags |= F_HAVE_BLCKD_MSCHED;
ASSERT(erts_smp_atomic32_read_nob(&schdlr_sspnd.active) == 1);
ASSERT(p->scheduler_data->no == 1);
@@ -5578,8 +5418,7 @@ erts_block_multi_scheduling(Process *p, ErtsProcLocks plocks, int on, int all)
~ERTS_SCHDLR_SSPND_CHNG_WAITER);
}
plp = proclist_create(p);
- plp->next = schdlr_sspnd.msb.procs;
- schdlr_sspnd.msb.procs = plp;
+ erts_proclist_store_last(&schdlr_sspnd.msb.procs, plp);
ASSERT(p->scheduler_data);
}
}
@@ -5590,20 +5429,16 @@ erts_block_multi_scheduling(Process *p, ErtsProcLocks plocks, int on, int all)
}
else { /* ------ UNBLOCK ------ */
if (p->flags & F_HAVE_BLCKD_MSCHED) {
- ErtsProcList **plpp = &schdlr_sspnd.msb.procs;
- plp = schdlr_sspnd.msb.procs;
+ ErtsProcList *plp = erts_proclist_peek_first(schdlr_sspnd.msb.procs);
while (plp) {
- if (!proclist_same(plp, p)){
- plpp = &plp->next;
- plp = plp->next;
- }
- else {
- *plpp = plp->next;
- proclist_destroy(plp);
+ ErtsProcList *tmp_plp = plp;
+ plp = erts_proclist_peek_next(schdlr_sspnd.msb.procs, plp);
+ if (erts_proclist_same(tmp_plp, p)) {
+ erts_proclist_remove(&schdlr_sspnd.msb.procs, tmp_plp);
+ proclist_destroy(tmp_plp);
if (!all)
break;
- plp = *plpp;
}
}
}
@@ -5672,23 +5507,25 @@ erts_multi_scheduling_blockers(Process *p)
Eterm res = NIL;
erts_smp_mtx_lock(&schdlr_sspnd.mtx);
- if (schdlr_sspnd.msb.procs) {
+ if (!erts_proclist_is_empty(schdlr_sspnd.msb.procs)) {
Eterm *hp, *hp_end;
ErtsProcList *plp1, *plp2;
- Uint max_size;
- ASSERT(schdlr_sspnd.msb.procs);
- for (max_size = 0, plp1 = schdlr_sspnd.msb.procs;
+ Uint max_size = 0;
+
+ for (plp1 = erts_proclist_peek_first(schdlr_sspnd.msb.procs);
plp1;
- plp1 = plp1->next) {
+ plp1 = erts_proclist_peek_next(schdlr_sspnd.msb.procs, plp1)) {
max_size += 2;
}
ASSERT(max_size);
hp = HAlloc(p, max_size);
hp_end = hp + max_size;
- for (plp1 = schdlr_sspnd.msb.procs; plp1; plp1 = plp1->next) {
- for (plp2 = schdlr_sspnd.msb.procs;
+ for (plp1 = erts_proclist_peek_first(schdlr_sspnd.msb.procs);
+ plp1;
+ plp1 = erts_proclist_peek_next(schdlr_sspnd.msb.procs, plp1)) {
+ for (plp2 = erts_proclist_peek_first(schdlr_sspnd.msb.procs);
plp2->pid != plp1->pid;
- plp2 = plp2->next);
+ plp2 = erts_proclist_peek_next(schdlr_sspnd.msb.procs, plp2));
if (plp2 == plp1) {
res = CONS(hp, plp1->pid, res);
hp += 2;
@@ -5940,7 +5777,7 @@ handle_pend_sync_suspend(Process *suspendee,
ASSERT(is_nil(suspender->suspendee));
if (suspendee_alive) {
erts_suspend(suspendee, suspendee_locks, NULL);
- suspender->suspendee = suspendee->id;
+ suspender->suspendee = suspendee->common.id;
}
/* suspender is suspended waiting for suspendee to suspend;
resume suspender */
@@ -5961,7 +5798,7 @@ pid2proc_not_running(Process *c_p, ErtsProcLocks c_p_locks,
ERTS_SMP_LC_ASSERT(c_p_locks & ERTS_PROC_LOCK_MAIN);
ERTS_SMP_LC_ASSERT(pid_locks & (ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_STATUS));
- if (c_p->id == pid)
+ if (c_p->common.id == pid)
return erts_pid2proc(c_p, c_p_locks, pid, pid_locks);
if (c_p_locks & ERTS_PROC_LOCK_STATUS)
@@ -6013,7 +5850,7 @@ pid2proc_not_running(Process *c_p, ErtsProcLocks c_p_locks,
*/
if (!c_p->pending_suspenders) {
/* Mark rp pending for suspend by c_p */
- add_pend_suspend(rp, c_p->id, handle_pend_sync_suspend);
+ add_pend_suspend(rp, c_p->common.id, handle_pend_sync_suspend);
ASSERT(is_nil(c_p->suspendee));
/* Suspend c_p; when rp is suspended c_p will be resumed. */
@@ -6119,20 +5956,20 @@ handle_pend_bif_sync_suspend(Process *suspendee,
ASSERT(is_nil(suspender->suspendee));
if (!suspendee_alive)
erts_delete_suspend_monitor(&suspender->suspend_monitors,
- suspendee->id);
+ suspendee->common.id);
else {
#ifdef DEBUG
int res;
#endif
ErtsSuspendMonitor *smon;
smon = erts_lookup_suspend_monitor(suspender->suspend_monitors,
- suspendee->id);
+ suspendee->common.id);
#ifdef DEBUG
res =
#endif
do_bif_suspend_process(suspendee, smon, suspendee);
ASSERT(!smon || res != 0);
- suspender->suspendee = suspendee->id;
+ suspender->suspendee = suspendee->common.id;
}
/* suspender is suspended waiting for suspendee to suspend;
resume suspender */
@@ -6161,14 +5998,14 @@ handle_pend_bif_async_suspend(Process *suspendee,
ASSERT(is_nil(suspender->suspendee));
if (!suspendee_alive)
erts_delete_suspend_monitor(&suspender->suspend_monitors,
- suspendee->id);
+ suspendee->common.id);
else {
#ifdef DEBUG
int res;
#endif
ErtsSuspendMonitor *smon;
smon = erts_lookup_suspend_monitor(suspender->suspend_monitors,
- suspendee->id);
+ suspendee->common.id);
#ifdef DEBUG
res =
#endif
@@ -6213,7 +6050,7 @@ suspend_process_2(BIF_ALIST_2)
int unless_suspending = 0;
- if (BIF_P->id == BIF_ARG_1)
+ if (BIF_P->common.id == BIF_ARG_1)
goto badarg; /* We are not allowed to suspend ourselves */
if (is_not_nil(BIF_ARG_2)) {
@@ -6309,7 +6146,7 @@ suspend_process_2(BIF_ALIST_2)
if (!do_bif_suspend_process(BIF_P, smon, suspendee))
add_pend_suspend(suspendee,
- BIF_P->id,
+ BIF_P->common.id,
handle_pend_bif_async_suspend);
res = am_true;
@@ -6372,7 +6209,7 @@ suspend_process_2(BIF_ALIST_2)
else {
/* Mark suspendee pending for suspend by BIF_P */
add_pend_suspend(suspendee,
- BIF_P->id,
+ BIF_P->common.id,
handle_pend_bif_sync_suspend);
ASSERT(is_nil(BIF_P->suspendee));
@@ -6446,7 +6283,7 @@ resume_process_1(BIF_ALIST_1)
Process *suspendee;
int is_active;
- if (BIF_P->id == BIF_ARG_1)
+ if (BIF_P->common.id == BIF_ARG_1)
BIF_ERROR(BIF_P, BADARG);
erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_LINK);
@@ -6571,7 +6408,8 @@ erts_process_status(Process *c_p, ErtsProcLocks c_p_locks,
for (i = 0; i < erts_no_schedulers; i++) {
esdp = ERTS_SCHEDULER_IX(i);
erts_smp_runq_lock(esdp->run_queue);
- if (esdp->free_process && esdp->free_process->id == rpid) {
+ if (esdp->free_process
+ && esdp->free_process->common.id == rpid) {
res = am_free;
erts_smp_runq_unlock(esdp->run_queue);
break;
@@ -6592,27 +6430,31 @@ erts_process_status(Process *c_p, ErtsProcLocks c_p_locks,
void
erts_suspend(Process* c_p, ErtsProcLocks c_p_locks, Port *busy_port)
{
-#ifdef DEBUG
- int res;
-#endif
+ int suspend;
+
ASSERT(c_p == erts_get_current_process());
ERTS_SMP_LC_ASSERT(c_p_locks == erts_proc_lc_my_proc_locks(c_p));
if (!(c_p_locks & ERTS_PROC_LOCK_STATUS))
erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_STATUS);
+ if (busy_port)
+ suspend = erts_save_suspend_process_on_port(busy_port, c_p);
+ else
+ suspend = 1;
+
+ if (suspend) {
#ifdef DEBUG
- res =
+ int res =
#endif
- suspend_process(c_p, c_p);
-
- ASSERT(res);
-
- if (busy_port)
- erts_wake_process_later(busy_port, c_p);
+ suspend_process(c_p, c_p);
+ ASSERT(res);
+ }
if (!(c_p_locks & ERTS_PROC_LOCK_STATUS))
erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_STATUS);
+ if (suspend && busy_port && erts_system_monitor_flags.busy_port)
+ monitor_generic(c_p, am_busy_port, busy_port->common.id);
}
void
@@ -6627,16 +6469,19 @@ erts_resume(Process* process, ErtsProcLocks process_locks)
}
int
-erts_resume_processes(ErtsProcList *plp)
+erts_resume_processes(ErtsProcList *list)
{
+ /* 'list' is expected to have been fetched (i.e. not a ring anymore) */
int nresumed = 0;
+ ErtsProcList *plp = list;
+
while (plp) {
Process *proc;
ErtsProcList *fplp;
ASSERT(is_internal_pid(plp->pid));
proc = erts_pid2proc(NULL, 0, plp->pid, ERTS_PROC_LOCK_STATUS);
if (proc) {
- if (proclist_same(plp, proc)) {
+ if (erts_proclist_same(plp, proc)) {
resume_process(proc);
nresumed++;
}
@@ -6789,9 +6634,8 @@ Process *schedule(Process *p, int calls)
state = erts_smp_atomic32_read_acqb(&p->state);
if (IS_TRACED(p)) {
- if (IS_TRACED_FL(p, F_TRACE_CALLS) && !(state & ERTS_PSFLG_FREE)) {
+ if (IS_TRACED_FL(p, F_TRACE_CALLS) && !(state & ERTS_PSFLG_FREE))
erts_schedule_time_break(p, ERTS_BP_CALL_TIME_SCHEDULE_OUT);
- }
if (state & (ERTS_PSFLG_FREE|ERTS_PSFLG_EXITING)) {
if (ARE_TRACE_FLAGS_ON(p, F_TRACE_SCHED_EXIT))
trace_sched(p, ((state & ERTS_PSFLG_FREE)
@@ -6804,7 +6648,7 @@ Process *schedule(Process *p, int calls)
else if (ARE_TRACE_FLAGS_ON(p, F_TRACE_SCHED_PROCS))
trace_virtual_sched(p, am_out);
}
- }
+ }
#ifdef ERTS_SMP
if (state & ERTS_PSFLG_PENDING_EXIT)
@@ -6815,6 +6659,8 @@ Process *schedule(Process *p, int calls)
| ERTS_PROC_LOCK_STATUS));
#endif
+ esdp->reductions += reds;
+
schedule_out_process(rq, state, p); /* Returns with rq locked! */
ERTS_PROC_REDUCTIONS_EXECUTED(rq,
@@ -6866,9 +6712,8 @@ Process *schedule(Process *p, int calls)
#ifdef ERTS_SMP
{
ErtsProcList *pnd_xtrs = rq->procs.pending_exiters;
- rq->procs.pending_exiters = NULL;
-
- if (pnd_xtrs) {
+ if (erts_proclist_fetch(&pnd_xtrs, NULL)) {
+ rq->procs.pending_exiters = NULL;
erts_smp_runq_unlock(rq);
handle_pending_exiters(pnd_xtrs);
erts_smp_runq_lock(rq);
@@ -6957,7 +6802,7 @@ Process *schedule(Process *p, int calls)
goto continue_check_activities_to_run;
}
- ERTS_RUNQ_FLGS_UNSET(rq, ERTS_RUNQ_FLG_PROTECTED);
+ (void) ERTS_RUNQ_FLGS_UNSET(rq, ERTS_RUNQ_FLG_PROTECTED);
/*
* Check for ERTS_RUNQ_FLG_SUSPENDED has to be done
@@ -7106,7 +6951,7 @@ Process *schedule(Process *p, int calls)
erts_smp_runq_unlock(rq);
if (flags & ERTS_RUNQ_FLG_PROTECTED)
- ERTS_RUNQ_FLGS_UNSET(rq, ERTS_RUNQ_FLG_PROTECTED);
+ (void) ERTS_RUNQ_FLGS_UNSET(rq, ERTS_RUNQ_FLG_PROTECTED);
ERTS_SMP_CHK_NO_PROC_LOCKS;
@@ -7164,7 +7009,7 @@ Process *schedule(Process *p, int calls)
erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
#ifdef ERTS_SMP
- if (is_not_nil(p->tracer_proc))
+ if (is_not_nil(ERTS_TRACER_PROC(p)))
erts_check_my_tracer_proc(p);
#endif
@@ -7367,70 +7212,6 @@ erts_get_exact_total_reductions(Process *c_p, Uint *redsp, Uint *diffp)
erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_MAIN);
}
-/*
- * erts_test_next_pid() is only used for testing.
- */
-Sint
-erts_test_next_pid(int set, Uint next)
-{
- Uint64 lpd;
- Sint res;
- Eterm pid_data;
- int first_pix = -1;
-
- erts_smp_rwmtx_rwlock(&erts_proc_tab_rwmtx);
-
- if (!set)
- lpd = last_pid_data_read_nob();
- else {
-
- lpd = (Uint64) next;
- pid_data = (Eterm) (lpd & ERTS_PID_DATA_MASK__);
- if (ERTS_INVALID_PID == make_internal_pid(pid_data)) {
- lpd += erts_proc.max;
- ASSERT(erts_pid_data2ix(pid_data)
- == erts_pid_data2ix(lpd & ERTS_PID_DATA_MASK__));
- }
- last_pid_data_set_relb(lpd);
- }
-
- while (1) {
- int pix;
- lpd++;
- pix = (int) (lpd % erts_proc.max);
- if (first_pix < 0)
- first_pix = pix;
- else if (pix == first_pix) {
- res = -1;
- break;
- }
- if (ERTS_AINT_NULL == erts_smp_atomic_read_nob(&erts_proc.tab[pix])) {
- pid_data = (Eterm) (lpd & ERTS_PID_DATA_MASK__);
- if (ERTS_INVALID_PID == make_internal_pid(pid_data)) {
- lpd += erts_proc.max;
- ASSERT(erts_pid_data2ix(pid_data)
- == erts_pid_data2ix(lpd & ERTS_PID_DATA_MASK__));
- }
- res = lpd & ERTS_PID_DATA_MASK__;
- break;
- }
- }
-
- erts_smp_rwmtx_rwunlock(&erts_proc_tab_rwmtx);
-
- return res;
-
-}
-
-Uint erts_process_count(void)
-{
- erts_aint32_t res = erts_smp_atomic32_read_nob(&process_count);
- if (res > erts_proc.max)
- return erts_proc.max;
- ASSERT(res >= 0);
- return (Uint) res;
-}
-
void
erts_free_proc(Process *p)
{
@@ -7440,132 +7221,66 @@ erts_free_proc(Process *p)
erts_free(ERTS_ALC_T_PROC, (void *) p);
}
+typedef struct {
+ Process *proc;
+ erts_aint32_t state;
+ ErtsRunQueue *run_queue;
+} ErtsEarlyProcInit;
+
+static void early_init_process_struct(void *varg, Eterm data)
+{
+ ErtsEarlyProcInit *arg = (ErtsEarlyProcInit *) varg;
+ Process *proc = arg->proc;
+
+ proc->common.id = make_internal_pid(data);
+ erts_smp_atomic32_init_relb(&proc->state, arg->state);
+
+#ifdef ERTS_SMP
+ RUNQ_SET_RQ(&proc->run_queue, arg->run_queue);
+
+ erts_proc_lock_init(proc); /* All locks locked */
+#endif
+
+}
+
/*
** Allocate process and find out where to place next process.
*/
static Process*
alloc_process(ErtsRunQueue *rq, erts_aint32_t state)
{
- int pix;
- Process* p;
- Uint64 lpd, exp_lpd;
- Eterm pid_data;
- erts_aint32_t proc_count;
-#ifdef DEBUG
- Eterm pid;
-#endif
-
- erts_smp_rwmtx_rlock(&erts_proc_tab_rwmtx);
-
- proc_count = erts_smp_atomic32_inc_read_acqb(&process_count);
- if (proc_count > erts_proc.max) {
- while (1) {
- erts_aint32_t act_proc_count;
-
- act_proc_count = erts_smp_atomic32_cmpxchg_relb(&process_count,
- proc_count-1,
- proc_count);
- if (act_proc_count == proc_count)
- goto system_limit;
- proc_count = act_proc_count;
- if (proc_count <= erts_proc.max)
- break;
- }
- }
+ ErtsEarlyProcInit init_arg;
+ Process *p;
- p = (Process*) erts_alloc_fnf(ERTS_ALC_T_PROC, sizeof(Process));
+ p = erts_alloc_fnf(ERTS_ALC_T_PROC, sizeof(Process));
if (!p)
- goto enomem;
-
- p->approx_started = erts_get_approx_time();
- p->started_interval = get_proc_interval();
-
- lpd = last_pid_data_read_acqb();
-
- /* Reserve slot */
- while (1) {
- lpd++;
- pix = erts_pid_data2ix((Eterm) (lpd & ERTS_PID_DATA_MASK__));
- if (erts_smp_atomic_read_nob(&erts_proc.tab[pix]) == ERTS_AINT_NULL) {
- erts_aint_t val;
- val = erts_smp_atomic_cmpxchg_relb(&erts_proc.tab[pix],
- ((erts_aint_t)
- ERTS_PROC_LOCK_BUSY),
- ERTS_AINT_NULL);
-
- if (ERTS_AINT_NULL == val)
- break;
- }
- }
-
- pid_data = (Eterm) lpd & ERTS_PID_DATA_MASK__;
+ return NULL;
- p->id = make_internal_pid(pid_data);
- if (p->id == ERTS_INVALID_PID) {
- /* Do not use the invalid pid; change serial */
- lpd += erts_proc.max;
- ASSERT(pix == erts_pid_data2ix((Eterm) (lpd & ERTS_PID_DATA_MASK__)));
- pid_data = (Eterm) lpd & ERTS_PID_DATA_MASK__;
- p->id = make_internal_pid(pid_data);
- ASSERT(p->id != ERTS_INVALID_PID);
- }
+ init_arg.proc = (Process *) p;
+ init_arg.run_queue = rq;
+ init_arg.state = state;
- exp_lpd = last_pid_data_read_nob();
+ ASSERT(((char *) p) == ((char *) &p->common));
- /* Move last pid data forward */
- while (1) {
- Uint64 act_lpd;
- if (last_pid_data_cmp(lpd, exp_lpd) < 0)
- break;
- act_lpd = last_pid_data_cmpxchg_relb(lpd, exp_lpd);
- if (act_lpd == exp_lpd)
- break;
- exp_lpd = act_lpd;
+ if (!erts_ptab_new_element(&erts_proc,
+ &p->common,
+ (void *) &init_arg,
+ early_init_process_struct)) {
+ erts_free(ERTS_ALC_T_PROC, p);
+ return NULL;
}
-#ifdef ERTS_SMP
- RUNQ_SET_RQ(&p->run_queue, rq);
-#endif
-
- erts_smp_atomic32_init_relb(&p->state, state);
-
-#ifdef DEBUG
- pid = p->id;
-#endif
-
-#ifdef ERTS_SMP
- erts_proc_lock_init(p); /* All locks locked */
-#endif
-
- /* Move into slot reserved */
-#ifdef DEBUG
- ASSERT(ERTS_PROC_LOCK_BUSY
- == (Process *) erts_smp_atomic_xchg_relb(&erts_proc.tab[pix],
- (erts_aint_t) p));
-#else
- erts_smp_atomic_set_relb(&erts_proc.tab[pix], (erts_aint_t) p);
-#endif
-
- ASSERT(internal_pid_serial(p->id) <= (erts_use_r9_pids_ports
- ? ERTS_MAX_PID_R9_SERIAL
- : ERTS_MAX_PID_SERIAL));
-
- erts_smp_rwmtx_runlock(&erts_proc_tab_rwmtx);
-
+ ASSERT(internal_pid_serial(p->common.id) <= ERTS_MAX_PID_SERIAL);
+
+ p->approx_started = erts_get_approx_time();
p->rcount = 0;
- ASSERT(p == (Process *)
- erts_smp_atomic_read_nob(
- &erts_proc.tab[internal_pid_index(pid)]));
- return p;
-
-enomem:
-system_limit:
-
- erts_smp_rwmtx_runlock(&erts_proc_tab_rwmtx);
- return NULL;
+ ASSERT(p == (Process *) (erts_ptab_pix2intptr_nob(
+ &erts_proc,
+ internal_pid_index(p->common.id))));
+ return p;
}
Eterm
@@ -7712,21 +7427,21 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
p->reds = 0;
#ifdef ERTS_SMP
- p->u.ptimer = NULL;
+ p->common.u.alive.ptimer = NULL;
#else
- sys_memset(&p->u.tm, 0, sizeof(ErlTimer));
+ sys_memset(&p->common.u.alive.tm, 0, sizeof(ErlTimer));
#endif
- p->reg = NULL;
- p->nlinks = NULL;
- p->monitors = NULL;
+ p->common.u.alive.reg = NULL;
+ ERTS_P_LINKS(p) = NULL;
+ ERTS_P_MONITORS(p) = NULL;
p->nodes_monitors = NULL;
p->suspend_monitors = NULL;
ASSERT(is_pid(parent->group_leader));
if (parent->group_leader == ERTS_INVALID_PID)
- p->group_leader = p->id;
+ p->group_leader = p->common.id;
else {
/* Needs to be done after the heap has been set up */
p->group_leader =
@@ -7735,7 +7450,7 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
: STORE_NC(&p->htop, &p->off_heap, parent->group_leader);
}
- erts_get_default_tracing(&p->trace_flags, &p->tracer_proc);
+ erts_get_default_tracing(&ERTS_TRACE_FLAGS(p), &ERTS_TRACER_PROC(p));
p->msg.first = NULL;
p->msg.last = &p->msg.first;
@@ -7746,7 +7461,7 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
p->msg_inq.last = &p->msg_inq.first;
p->msg_inq.len = 0;
#endif
- p->bif_timers = NULL;
+ p->u.bif_timers = NULL;
p->mbuf = NULL;
p->mbuf_sz = 0;
p->psd = NULL;
@@ -7758,7 +7473,9 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
DT_UTAG(p) = NIL;
DT_UTAG_FLAGS(p) = 0;
#endif
- p->parent = parent->id == ERTS_INVALID_PID ? NIL : parent->id;
+ p->parent = (parent->common.id == ERTS_INVALID_PID
+ ? NIL
+ : parent->common.id);
INIT_HOLE_CHECK(p);
#ifdef DEBUG
@@ -7766,18 +7483,19 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
#endif
if (IS_TRACED(parent)) {
- if (parent->trace_flags & F_TRACE_SOS) {
- p->trace_flags |= (parent->trace_flags & TRACEE_FLAGS);
- p->tracer_proc = parent->tracer_proc;
+ if (ERTS_TRACE_FLAGS(parent) & F_TRACE_SOS) {
+ ERTS_TRACE_FLAGS(p) |= (ERTS_TRACE_FLAGS(parent) & TRACEE_FLAGS);
+ ERTS_TRACER_PROC(p) = ERTS_TRACER_PROC(parent);
}
if (ARE_TRACE_FLAGS_ON(parent, F_TRACE_PROCS)) {
- trace_proc_spawn(parent, p->id, mod, func, args);
+ trace_proc_spawn(parent, p->common.id, mod, func, args);
}
- if (parent->trace_flags & F_TRACE_SOS1) { /* Overrides TRACE_CHILDREN */
- p->trace_flags |= (parent->trace_flags & TRACEE_FLAGS);
- p->tracer_proc = parent->tracer_proc;
- p->trace_flags &= ~(F_TRACE_SOS1 | F_TRACE_SOS);
- parent->trace_flags &= ~(F_TRACE_SOS1 | F_TRACE_SOS);
+ if (ERTS_TRACE_FLAGS(parent) & F_TRACE_SOS1) {
+ /* Overrides TRACE_CHILDREN */
+ ERTS_TRACE_FLAGS(p) |= (ERTS_TRACE_FLAGS(parent) & TRACEE_FLAGS);
+ ERTS_TRACER_PROC(p) = ERTS_TRACER_PROC(parent);
+ ERTS_TRACE_FLAGS(p) &= ~(F_TRACE_SOS1 | F_TRACE_SOS);
+ ERTS_TRACE_FLAGS(parent) &= ~(F_TRACE_SOS1 | F_TRACE_SOS);
}
}
@@ -7790,27 +7508,27 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
int ret;
#endif
if (IS_TRACED_FL(parent, F_TRACE_PROCS)) {
- trace_proc(parent, parent, am_link, p->id);
+ trace_proc(parent, parent, am_link, p->common.id);
}
#ifdef DEBUG
- ret = erts_add_link(&(parent->nlinks), LINK_PID, p->id);
+ ret = erts_add_link(&ERTS_P_LINKS(parent), LINK_PID, p->common.id);
ASSERT(ret == 0);
- ret = erts_add_link(&(p->nlinks), LINK_PID, parent->id);
+ ret = erts_add_link(&ERTS_P_LINKS(p), LINK_PID, parent->common.id);
ASSERT(ret == 0);
#else
- erts_add_link(&(parent->nlinks), LINK_PID, p->id);
- erts_add_link(&(p->nlinks), LINK_PID, parent->id);
+ erts_add_link(&ERTS_P_LINKS(parent), LINK_PID, p->common.id);
+ erts_add_link(&ERTS_P_LINKS(p), LINK_PID, parent->common.id);
#endif
if (IS_TRACED(parent)) {
- if (parent->trace_flags & (F_TRACE_SOL|F_TRACE_SOL1)) {
- p->trace_flags |= (parent->trace_flags & TRACEE_FLAGS);
- p->tracer_proc = parent->tracer_proc; /* maybe steal */
+ if (ERTS_TRACE_FLAGS(parent) & (F_TRACE_SOL|F_TRACE_SOL1)) {
+ ERTS_TRACE_FLAGS(p) |= (ERTS_TRACE_FLAGS(parent)&TRACEE_FLAGS);
+ ERTS_TRACER_PROC(p) = ERTS_TRACER_PROC(parent); /*maybe steal*/
- if (parent->trace_flags & F_TRACE_SOL1) { /* maybe override */
- p ->trace_flags &= ~(F_TRACE_SOL1 | F_TRACE_SOL);
- parent->trace_flags &= ~(F_TRACE_SOL1 | F_TRACE_SOL);
+ if (ERTS_TRACE_FLAGS(parent) & F_TRACE_SOL1) {/*maybe override*/
+ ERTS_TRACE_FLAGS(p) &= ~(F_TRACE_SOL1 | F_TRACE_SOL);
+ ERTS_TRACE_FLAGS(parent) &= ~(F_TRACE_SOL1 | F_TRACE_SOL);
}
}
}
@@ -7823,8 +7541,8 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
Eterm mref;
mref = erts_make_ref(parent);
- erts_add_monitor(&(parent->monitors), MON_ORIGIN, mref, p->id, NIL);
- erts_add_monitor(&(p->monitors), MON_TARGET, mref, parent->id, NIL);
+ erts_add_monitor(&ERTS_P_MONITORS(parent), MON_ORIGIN, mref, p->common.id, NIL);
+ erts_add_monitor(&ERTS_P_MONITORS(p), MON_TARGET, mref, parent->common.id, NIL);
so->mref = mref;
}
@@ -7842,7 +7560,7 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
erts_smp_proc_unlock(p, ERTS_PROC_LOCKS_ALL);
- res = p->id;
+ res = p->common.id;
/*
* Schedule process for execution.
@@ -7850,7 +7568,7 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
schedule_process(p, state, 0);
- VERBOSE(DEBUG_PROCESSES, ("Created a new process: %T\n",p->id));
+ VERBOSE(DEBUG_PROCESSES, ("Created a new process: %T\n",p->common.id));
#ifdef USE_VM_PROBES
if (DTRACE_ENABLED(process_spawn)) {
@@ -7885,10 +7603,10 @@ void erts_init_empty_process(Process *p)
p->min_heap_size = 0;
p->min_vheap_size = 0;
p->rcount = 0;
- p->id = ERTS_INVALID_PID;
+ p->common.id = ERTS_INVALID_PID;
p->reds = 0;
- p->tracer_proc = NIL;
- p->trace_flags = F_INITIAL_TRACE_FLAGS;
+ ERTS_TRACER_PROC(p) = NIL;
+ ERTS_TRACE_FLAGS(p) = F_INITIAL_TRACE_FLAGS;
p->group_leader = ERTS_INVALID_PID;
p->flags = 0;
p->fvalue = NIL;
@@ -7901,14 +7619,14 @@ void erts_init_empty_process(Process *p)
p->bin_old_vheap = 0;
p->bin_vheap_mature = 0;
#ifdef ERTS_SMP
- p->u.ptimer = NULL;
+ p->common.u.alive.ptimer = NULL;
#else
- memset(&(p->u.tm), 0, sizeof(ErlTimer));
+ memset(&(p->common.u.alive.tm), 0, sizeof(ErlTimer));
#endif
p->next = NULL;
p->off_heap.first = NULL;
p->off_heap.overhead = 0;
- p->reg = NULL;
+ p->common.u.alive.reg = NULL;
p->heap_sz = 0;
p->high_water = NULL;
p->old_hend = NULL;
@@ -7917,15 +7635,15 @@ void erts_init_empty_process(Process *p)
p->mbuf = NULL;
p->mbuf_sz = 0;
p->psd = NULL;
- p->monitors = NULL;
- p->nlinks = NULL; /* List of links */
+ ERTS_P_MONITORS(p) = NULL;
+ ERTS_P_LINKS(p) = NULL; /* List of links */
p->nodes_monitors = NULL;
p->suspend_monitors = NULL;
p->msg.first = NULL;
p->msg.last = &p->msg.first;
p->msg.save = &p->msg.first;
p->msg.len = 0;
- p->bif_timers = NULL;
+ p->u.bif_timers = NULL;
p->dictionary = NULL;
p->seq_trace_clock = 0;
p->seq_trace_lastcnt = 0;
@@ -7953,7 +7671,7 @@ void erts_init_empty_process(Process *p)
p->parent = NIL;
p->approx_started = 0;
- p->started_interval = 0;
+ p->common.u.alive.started_interval = 0;
#ifdef HIPE
hipe_init_process(&p->hipe);
@@ -7999,25 +7717,25 @@ erts_debug_verify_clean_empty_process(Process* p)
ASSERT(p->stop == NULL);
ASSERT(p->hend == NULL);
ASSERT(p->heap == NULL);
- ASSERT(p->id == ERTS_INVALID_PID);
- ASSERT(p->tracer_proc == NIL);
- ASSERT(p->trace_flags == F_INITIAL_TRACE_FLAGS);
+ ASSERT(p->common.id == ERTS_INVALID_PID);
+ ASSERT(ERTS_TRACER_PROC(p) == NIL);
+ ASSERT(ERTS_TRACE_FLAGS(p) == F_INITIAL_TRACE_FLAGS);
ASSERT(p->group_leader == ERTS_INVALID_PID);
ASSERT(p->next == NULL);
- ASSERT(p->reg == NULL);
+ ASSERT(p->common.u.alive.reg == NULL);
ASSERT(p->heap_sz == 0);
ASSERT(p->high_water == NULL);
ASSERT(p->old_hend == NULL);
ASSERT(p->old_htop == NULL);
ASSERT(p->old_heap == NULL);
- ASSERT(p->monitors == NULL);
- ASSERT(p->nlinks == NULL);
+ ASSERT(ERTS_P_MONITORS(p) == NULL);
+ ASSERT(ERTS_P_LINKS(p) == NULL);
ASSERT(p->nodes_monitors == NULL);
ASSERT(p->suspend_monitors == NULL);
ASSERT(p->msg.first == NULL);
ASSERT(p->msg.len == 0);
- ASSERT(p->bif_timers == NULL);
+ ASSERT(p->u.bif_timers == NULL);
ASSERT(p->dictionary == NULL);
ASSERT(p->catches == 0);
ASSERT(p->cp == NULL);
@@ -8074,7 +7792,7 @@ delete_process(Process* p)
{
ErlMessage* mp;
- VERBOSE(DEBUG_PROCESSES, ("Removing process: %T\n",p->id));
+ VERBOSE(DEBUG_PROCESSES, ("Removing process: %T\n",p->common.id));
/* Cleanup psd */
@@ -8148,8 +7866,6 @@ delete_process(Process* p)
mp = next_mp;
}
- ASSERT(!p->monitors);
- ASSERT(!p->nlinks);
ASSERT(!p->nodes_monitors);
ASSERT(!p->suspend_monitors);
@@ -8244,12 +7960,14 @@ erts_handle_pending_exit(Process *c_p, ErtsProcLocks locks)
static void
handle_pending_exiters(ErtsProcList *pnd_xtrs)
{
+ /* 'list' is expected to have been fetched (i.e. not a ring anymore) */
ErtsProcList *plp = pnd_xtrs;
- ErtsProcList *free_plp;
+
while (plp) {
+ ErtsProcList *free_plp;
Process *p = erts_pid2proc(NULL, 0, plp->pid, ERTS_PROC_LOCKS_ALL);
if (p) {
- if (proclist_same(plp, p)) {
+ if (erts_proclist_same(plp, p)) {
erts_aint32_t state = erts_smp_atomic32_read_acqb(&p->state);
if (!(state & ERTS_PSFLG_RUNNING)) {
ASSERT(state & ERTS_PSFLG_PENDING_EXIT);
@@ -8278,8 +7996,7 @@ save_pending_exiter(Process *p)
erts_smp_runq_lock(rq);
- plp->next = rq->procs.pending_exiters;
- rq->procs.pending_exiters = plp;
+ erts_proclist_store_last(&rq->procs.pending_exiters, plp);
erts_smp_runq_unlock(rq);
wake_scheduler(rq, 1);
@@ -8326,7 +8043,7 @@ send_exit_message(Process *to, ErtsProcLocks *to_locksp,
hp = bp->mem;
mess = copy_struct(exit_term, term_size, &hp, &bp->off_heap);
/* the trace token must in this case be updated by the caller */
- seq_trace_output(token, mess, SEQ_TRACE_SEND, to->id, NULL);
+ seq_trace_output(token, mess, SEQ_TRACE_SEND, to->common.id, NULL);
temp_token = copy_struct(token, sz_token, &hp, &bp->off_heap);
erts_queue_message(to, to_locksp, bp, mess, temp_token
#ifdef USE_VM_PROBES
@@ -8617,7 +8334,7 @@ static void doit_exit_monitor(ErtsMonitor *mon, void *vpcontext)
if (!rp) {
goto done;
}
- rmon = erts_remove_monitor(&(rp->monitors),mon->ref);
+ rmon = erts_remove_monitor(&ERTS_P_MONITORS(rp), mon->ref);
erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
if (rmon == NULL) {
goto done;
@@ -8652,7 +8369,7 @@ static void doit_exit_monitor(ErtsMonitor *mon, void *vpcontext)
ASSERT(mon->type == MON_TARGET);
ASSERT(is_pid(mon->pid) || is_internal_port(mon->pid));
if (is_internal_port(mon->pid)) {
- Port *prt = erts_id2port(mon->pid, NULL, 0);
+ Port *prt = erts_id2port(mon->pid);
if (prt == NULL) {
goto done;
}
@@ -8668,13 +8385,13 @@ static void doit_exit_monitor(ErtsMonitor *mon, void *vpcontext)
goto done;
}
UseTmpHeapNoproc(3);
- rmon = erts_remove_monitor(&(rp->monitors),mon->ref);
+ rmon = erts_remove_monitor(&ERTS_P_MONITORS(rp), mon->ref);
if (rmon) {
erts_destroy_monitor(rmon);
watched = (is_atom(mon->name)
? TUPLE2(lhp, mon->name,
erts_this_dist_entry->sysname)
- : pcontext->p->id);
+ : pcontext->p->common.id);
erts_queue_monitor_message(rp, &rp_locks, mon->ref, am_process,
watched, pcontext->reason);
}
@@ -8739,21 +8456,22 @@ static void doit_exit_link(ErtsLink *lnk, void *vpcontext)
switch(lnk->type) {
case LINK_PID:
if(is_internal_port(item)) {
- Port *prt = erts_id2port(item, NULL, 0);
- if (prt) {
- rlnk = erts_remove_link(&prt->nlinks, p->id);
- if (rlnk)
- erts_destroy_link(rlnk);
- erts_do_exit_port(prt, p->id, reason);
- erts_port_release(prt);
- }
+ Port *prt = erts_port_lookup(item, ERTS_PORT_SFLGS_INVALID_LOOKUP);
+ if (prt)
+ erts_port_exit(NULL,
+ (ERTS_PORT_SIG_FLG_FORCE_SCHED
+ | ERTS_PORT_SIG_FLG_BROKEN_LINK),
+ prt,
+ p->common.id,
+ reason,
+ NULL);
}
else if(is_external_port(item)) {
erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf();
erts_dsprintf(dsbufp,
"Erroneous link between %T and external port %T "
"found\n",
- p->id,
+ p->common.id,
item);
erts_send_error_to_logger_nogl(dsbufp);
ASSERT(0); /* It isn't possible to setup such a link... */
@@ -8763,14 +8481,14 @@ static void doit_exit_link(ErtsLink *lnk, void *vpcontext)
| ERTS_PROC_LOCKS_XSIG_SEND);
rp = erts_pid2proc(NULL, 0, item, rp_locks);
if (rp) {
- rlnk = erts_remove_link(&(rp->nlinks), p->id);
+ rlnk = erts_remove_link(&ERTS_P_LINKS(rp), p->common.id);
/* If rlnk == NULL, we got unlinked while exiting,
i.e., do nothing... */
if (rlnk) {
int xres;
erts_destroy_link(rlnk);
xres = send_exit_signal(NULL,
- p->id,
+ p->common.id,
rp,
&rp_locks,
reason,
@@ -8782,7 +8500,7 @@ static void doit_exit_link(ErtsLink *lnk, void *vpcontext)
if (xres >= 0 && IS_TRACED_FL(rp, F_TRACE_PROCS)) {
/* We didn't exit the process and it is traced */
if (IS_TRACED_FL(rp, F_TRACE_PROCS)) {
- trace_proc(p, rp, am_getting_unlinked, p->id);
+ trace_proc(p, rp, am_getting_unlinked, p->common.id);
}
}
}
@@ -8796,12 +8514,12 @@ static void doit_exit_link(ErtsLink *lnk, void *vpcontext)
ErtsDSigData dsd;
int code;
ErtsDistLinkData dld;
- erts_remove_dist_link(&dld, p->id, item, dep);
+ erts_remove_dist_link(&dld, p->common.id, item, dep);
erts_smp_proc_lock(p, ERTS_PROC_LOCK_MAIN);
code = erts_dsig_prepare(&dsd, dep, p, ERTS_DSP_NO_LOCK, 0);
if (code == ERTS_DSIG_PREP_CONNECTED) {
- code = erts_dsig_send_exit_tt(&dsd, p->id, item, reason,
- SEQ_TRACE_TOKEN(p));
+ code = erts_dsig_send_exit_tt(&dsd, p->common.id, item,
+ reason, SEQ_TRACE_TOKEN(p));
ASSERT(code == ERTS_DSIG_SEND_OK);
}
erts_smp_proc_unlock(p, ERTS_PROC_LOCK_MAIN);
@@ -8816,7 +8534,7 @@ static void doit_exit_link(ErtsLink *lnk, void *vpcontext)
/* dist entries have node links in a separate structure to
avoid confusion */
erts_smp_de_links_lock(dep);
- rlnk = erts_remove_link(&(dep->node_links), p->id);
+ rlnk = erts_remove_link(&(dep->node_links), p->common.id);
erts_smp_de_links_unlock(dep);
if (rlnk)
erts_destroy_link(rlnk);
@@ -8844,15 +8562,6 @@ resume_suspend_monitor(ErtsSuspendMonitor *smon, void *vc_p)
erts_destroy_suspend_monitor(smon);
}
-#ifdef ERTS_SMP
-static void
-proc_dec_refc(void *vproc)
-{
- erts_smp_proc_dec_refc((Process *) vproc);
-}
-#endif
-
-
/* this function fishishes a process and propagates exit messages - called
by process_main when a process dies */
void
@@ -8910,23 +8619,25 @@ erts_do_exit_process(Process* p, Eterm reason)
trace_proc(p, p, am_exit, reason);
}
- erts_trace_check_exiting(p->id);
+ erts_trace_check_exiting(p->common.id);
- ASSERT((p->trace_flags & F_INITIAL_TRACE_FLAGS) == F_INITIAL_TRACE_FLAGS);
+ ASSERT((ERTS_TRACE_FLAGS(p) & F_INITIAL_TRACE_FLAGS)
+ == F_INITIAL_TRACE_FLAGS);
cancel_timer(p); /* Always cancel timer just in case */
- /*
- * The timer of this process can *not* be used anymore. The field used
- * for the timer is now used for misc exiting data.
- */
- p->u.exit_data = NULL;
-
- if (p->bif_timers)
+ if (p->u.bif_timers)
erts_cancel_bif_timers(p, ERTS_PROC_LOCKS_ALL);
erts_smp_proc_unlock(p, ERTS_PROC_LOCKS_ALL_MINOR);
+ /*
+ * The p->u.bif_timers of this process can *not* be used anymore;
+ * will be overwritten by misc termination data.
+ */
+ p->u.terminate = NULL;
+
+
erts_continue_exit_process(p);
}
@@ -8998,9 +8709,9 @@ erts_continue_exit_process(Process *p)
* The registered name *should* be the last "erlang resource" to
* cleanup.
*/
- if (p->reg) {
+ if (p->common.u.alive.reg) {
(void) erts_unregister_name(p, ERTS_PROC_LOCK_MAIN, NULL, THE_NON_VALUE);
- ASSERT(!p->reg);
+ ASSERT(!p->common.u.alive.reg);
}
erts_smp_proc_lock(p, ERTS_PROC_LOCKS_ALL_MINOR);
@@ -9014,22 +8725,18 @@ erts_continue_exit_process(Process *p)
yield_allowed = 0;
#endif
+ /*
+ * Note! The monitor and link fields will be overwritten
+ * by erts_ptab_delete_element() below.
+ */
+ mon = ERTS_P_MONITORS(p);
+ lnk = ERTS_P_LINKS(p);
+
{
- int maybe_save;
- int pix;
/* Do *not* use erts_get_runq_proc() */
ErtsRunQueue *rq;
rq = erts_get_runq_current(ERTS_GET_SCHEDULER_DATA_FROM_PROC(p));
- pix = internal_pid_index(p->id);
-
- erts_smp_rwmtx_rlock(&erts_proc_tab_rwmtx);
- maybe_save = saved_term_procs.end != NULL;
- if (maybe_save) {
- erts_smp_rwmtx_runlock(&erts_proc_tab_rwmtx);
- erts_smp_rwmtx_rwlock(&erts_proc_tab_rwmtx);
- }
-
erts_smp_runq_lock(rq);
#ifdef ERTS_SMP
@@ -9040,22 +8747,11 @@ erts_continue_exit_process(Process *p)
p->scheduler_data->current_process = NULL;
p->scheduler_data->free_process = p;
#endif
- /* Time of death! */
- erts_smp_atomic_set_relb(&erts_proc.tab[pix], ERTS_AINT_NULL);
- ASSERT(erts_smp_atomic32_read_nob(&process_count) > 0);
- erts_smp_atomic32_dec_relb(&process_count);
+ /* Time of death! */
+ erts_ptab_delete_element(&erts_proc, &p->common);
erts_smp_runq_unlock(rq);
-
- if (!maybe_save)
- erts_smp_rwmtx_runlock(&erts_proc_tab_rwmtx);
- else {
- if (saved_term_procs.end)
- save_terminating_process(p);
- erts_smp_rwmtx_rwunlock(&erts_proc_tab_rwmtx);
- }
-
}
/*
@@ -9065,12 +8761,6 @@ erts_continue_exit_process(Process *p)
* when the monitors and/or links hit.
*/
- mon = p->monitors;
- p->monitors = NULL; /* to avoid recursive deletion during traversal */
-
- lnk = p->nlinks;
- p->nlinks = NULL;
-
{
/* Inactivate and notify free */
erts_aint32_t n, e, a = erts_smp_atomic32_read_nob(&p->state);
@@ -9114,7 +8804,7 @@ erts_continue_exit_process(Process *p)
UseTmpHeap(4,p);
hp = &tmp_heap[0];
- exit_tuple = TUPLE3(hp, am_EXIT, p->id, reason);
+ exit_tuple = TUPLE3(hp, am_EXIT, p->common.id, reason);
exit_tuple_sz = size_object(exit_tuple);
@@ -9140,21 +8830,6 @@ erts_continue_exit_process(Process *p)
delete_process(p);
#ifdef ERTS_SMP
- /*
- * Each scheduler will decrease refc by one via misc aux work;
- * we have one refc for reference from process table which we
- * now want to remove, i.e. we increase refc with schedulers-1.
- *
- * Process struct wont be deallocated until (earliest) when
- * all schedulers have decreased refc via misc aux work...
- */
- if (erts_no_schedulers != 1)
- erts_smp_proc_add_refc(p, (Sint32) erts_no_schedulers-1);
- erts_schedule_multi_misc_aux_work(0,
- erts_no_schedulers,
- proc_dec_refc,
- (void *) p);
-
erts_smp_proc_lock(p, ERTS_PROC_LOCK_MAIN);
ERTS_SMP_CHK_HAVE_ONLY_MAIN_PROC_LOCK(p);
#endif
@@ -9206,9 +8881,9 @@ cancel_timer(Process* p)
ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_MAIN & erts_proc_lc_my_proc_locks(p));
p->flags &= ~(F_INSLPQUEUE|F_TIMO);
#ifdef ERTS_SMP
- erts_cancel_smp_ptimer(p->u.ptimer);
+ erts_cancel_smp_ptimer(p->common.u.alive.ptimer);
#else
- erts_cancel_timer(&p->u.tm);
+ erts_cancel_timer(&p->common.u.alive.tm);
#endif
}
@@ -9229,12 +8904,12 @@ set_timer(Process* p, Uint timeout)
p->flags &= ~F_TIMO;
#ifdef ERTS_SMP
- erts_create_smp_ptimer(&p->u.ptimer,
- p->id,
+ erts_create_smp_ptimer(&p->common.u.alive.ptimer,
+ p->common.id,
(ErlTimeoutProc) timeout_proc,
timeout);
#else
- erts_set_timer(&p->u.tm,
+ erts_set_timer(&p->common.u.alive.tm,
(ErlTimeoutProc) timeout_proc,
NULL,
(void*) p,
@@ -9252,7 +8927,7 @@ erts_stack_dump(int to, void *to_arg, Process *p)
Eterm* sp;
int yreg = -1;
- if (p->trace_flags & F_SENSITIVE) {
+ if (ERTS_TRACE_FLAGS(p) & F_SENSITIVE) {
return;
}
erts_program_counter_info(to, to_arg, p);
@@ -9320,7 +8995,7 @@ stack_element_dump(int to, void *to_arg, Process* p, Eterm* sp, int yreg)
erts_print(to, to_arg, "\n%p ", sp);
} else {
char sbuf[16];
- sprintf(sbuf, "y(%d)", yreg);
+ erts_snprintf(sbuf, sizeof(sbuf), "y(%d)", yreg);
erts_print(to, to_arg, "%-8s ", sbuf);
yreg++;
}
@@ -9340,1068 +9015,6 @@ stack_element_dump(int to, void *to_arg, Process* p, Eterm* sp, int yreg)
return yreg;
}
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
- * The processes/0 BIF implementation. *
-\* */
-
-
-#define ERTS_PROCESSES_BIF_TAB_INSPECT_INDICES_PER_RED 25
-#define ERTS_PROCESSES_BIF_TAB_CHUNK_SIZE 1000
-#define ERTS_PROCESSES_BIF_MIN_START_REDS \
- (ERTS_PROCESSES_BIF_TAB_CHUNK_SIZE \
- / ERTS_PROCESSES_BIF_TAB_INSPECT_INDICES_PER_RED)
-
-#define ERTS_PROCESSES_BIF_TAB_FREE_TERM_PROC_REDS 1
-
-#define ERTS_PROCESSES_BIF_INSPECT_TERM_PROC_PER_RED 10
-
-#define ERTS_PROCESSES_INSPECT_TERM_PROC_MAX_REDS \
- (ERTS_PROCESSES_BIF_TAB_CHUNK_SIZE \
- / ERTS_PROCESSES_BIF_TAB_INSPECT_INDICES_PER_RED)
-
-
-#define ERTS_PROCESSES_BIF_BUILD_RESULT_CONSES_PER_RED 75
-
-#define ERTS_PROCS_DBG_DO_TRACE 0
-
-#ifdef DEBUG
-# define ERTS_PROCESSES_BIF_DEBUGLEVEL 100
-#else
-# define ERTS_PROCESSES_BIF_DEBUGLEVEL 0
-#endif
-
-#define ERTS_PROCS_DBGLVL_CHK_HALLOC 1
-#define ERTS_PROCS_DBGLVL_CHK_FOUND_PIDS 5
-#define ERTS_PROCS_DBGLVL_CHK_PIDS 10
-#define ERTS_PROCS_DBGLVL_CHK_TERM_PROC_LIST 20
-#define ERTS_PROCS_DBGLVL_CHK_RESLIST 20
-
-#if ERTS_PROCESSES_BIF_DEBUGLEVEL == 0
-# define ERTS_PROCS_ASSERT(EXP)
-#else
-# define ERTS_PROCS_ASSERT(EXP) \
- ((void) ((EXP) \
- ? 1 \
- : (debug_processes_assert_error(#EXP, __FILE__, __LINE__), 0)))
-#endif
-
-
-#if ERTS_PROCESSES_BIF_DEBUGLEVEL >= ERTS_PROCS_DBGLVL_CHK_HALLOC
-# define ERTS_PROCS_DBG_SAVE_HEAP_ALLOC(PBDP, HP, SZ) \
-do { \
- ERTS_PROCS_ASSERT(!(PBDP)->debug.heap); \
- ERTS_PROCS_ASSERT(!(PBDP)->debug.heap_size); \
- (PBDP)->debug.heap = (HP); \
- (PBDP)->debug.heap_size = (SZ); \
-} while (0)
-# define ERTS_PROCS_DBG_VERIFY_HEAP_ALLOC_USED(PBDP, HP) \
-do { \
- ERTS_PROCS_ASSERT((PBDP)->debug.heap); \
- ERTS_PROCS_ASSERT((PBDP)->debug.heap_size); \
- ERTS_PROCS_ASSERT((PBDP)->debug.heap + (PBDP)->debug.heap_size == (HP));\
- (PBDP)->debug.heap = NULL; \
- (PBDP)->debug.heap_size = 0; \
-} while (0)
-# define ERTS_PROCS_DBG_HEAP_ALLOC_INIT(PBDP) \
-do { \
- (PBDP)->debug.heap = NULL; \
- (PBDP)->debug.heap_size = 0; \
-} while (0)
-#else
-# define ERTS_PROCS_DBG_SAVE_HEAP_ALLOC(PBDP, HP, SZ)
-# define ERTS_PROCS_DBG_VERIFY_HEAP_ALLOC_USED(PBDP, HP)
-# define ERTS_PROCS_DBG_HEAP_ALLOC_INIT(PBDP)
-#endif
-
-#if ERTS_PROCESSES_BIF_DEBUGLEVEL >= ERTS_PROCS_DBGLVL_CHK_RESLIST
-# define ERTS_PROCS_DBG_CHK_RESLIST(R) debug_processes_check_res_list((R))
-#else
-# define ERTS_PROCS_DBG_CHK_RESLIST(R)
-#endif
-
-#if ERTS_PROCESSES_BIF_DEBUGLEVEL >= ERTS_PROCS_DBGLVL_CHK_PIDS
-# define ERTS_PROCS_DBG_SAVE_PIDS(PBDP) debug_processes_save_all_pids((PBDP))
-# define ERTS_PROCS_DBG_VERIFY_PIDS(PBDP) \
-do { \
- if (!(PBDP)->debug.correct_pids_verified) \
- debug_processes_verify_all_pids((PBDP)); \
-} while (0)
-# define ERTS_PROCS_DBG_CLEANUP_CHK_PIDS(PBDP) \
-do { \
- if ((PBDP)->debug.correct_pids) { \
- erts_free(ERTS_ALC_T_PROCS_PIDS, \
- (PBDP)->debug.correct_pids); \
- (PBDP)->debug.correct_pids = NULL; \
- } \
-} while(0)
-# define ERTS_PROCS_DBG_CHK_PIDS_INIT(PBDP) \
-do { \
- (PBDP)->debug.correct_pids_verified = 0; \
- (PBDP)->debug.correct_pids = NULL; \
-} while (0)
-#else
-# define ERTS_PROCS_DBG_SAVE_PIDS(PBDP)
-# define ERTS_PROCS_DBG_VERIFY_PIDS(PBDP)
-# define ERTS_PROCS_DBG_CLEANUP_CHK_PIDS(PBDP)
-# define ERTS_PROCS_DBG_CHK_PIDS_INIT(PBDP)
-#endif
-
-#if ERTS_PROCESSES_BIF_DEBUGLEVEL >= ERTS_PROCS_DBGLVL_CHK_FOUND_PIDS
-# define ERTS_PROCS_DBG_CHK_PID_FOUND(PBDP, PID, IC) \
- debug_processes_check_found_pid((PBDP), (PID), (IC), 1)
-# define ERTS_PROCS_DBG_CHK_PID_NOT_FOUND(PBDP, PID, IC) \
- debug_processes_check_found_pid((PBDP), (PID), (IC), 0)
-#else
-# define ERTS_PROCS_DBG_CHK_PID_FOUND(PBDP, PID, IC)
-# define ERTS_PROCS_DBG_CHK_PID_NOT_FOUND(PBDP, PID, IC)
-#endif
-
-#if ERTS_PROCESSES_BIF_DEBUGLEVEL >= ERTS_PROCS_DBGLVL_CHK_TERM_PROC_LIST
-# define ERTS_PROCS_DBG_CHK_TPLIST() \
- debug_processes_check_term_proc_list()
-# define ERTS_PROCS_DBG_CHK_FREELIST(FL) \
- debug_processes_check_term_proc_free_list(FL)
-#else
-# define ERTS_PROCS_DBG_CHK_TPLIST()
-# define ERTS_PROCS_DBG_CHK_FREELIST(FL)
-#endif
-
-#if ERTS_PROCESSES_BIF_DEBUGLEVEL == 0
-#if ERTS_PROCS_DBG_DO_TRACE
-# define ERTS_PROCS_DBG_INIT(P, PBDP) (PBDP)->debug.caller = (P)->id
-# else
-# define ERTS_PROCS_DBG_INIT(P, PBDP)
-# endif
-# define ERTS_PROCS_DBG_CLEANUP(PBDP)
-#else
-# define ERTS_PROCS_DBG_INIT(P, PBDP) \
-do { \
- (PBDP)->debug.caller = (P)->id; \
- ERTS_PROCS_DBG_HEAP_ALLOC_INIT((PBDP)); \
- ERTS_PROCS_DBG_CHK_PIDS_INIT((PBDP)); \
-} while (0)
-# define ERTS_PROCS_DBG_CLEANUP(PBDP) \
-do { \
- ERTS_PROCS_DBG_CLEANUP_CHK_PIDS((PBDP)); \
-} while (0)
-#endif
-
-#if ERTS_PROCS_DBG_DO_TRACE
-# define ERTS_PROCS_DBG_TRACE(PID, FUNC, WHAT) \
- erts_fprintf(stderr, "%T %s:%d:%s(): %s\n", \
- (PID), __FILE__, __LINE__, #FUNC, #WHAT)
-#else
-# define ERTS_PROCS_DBG_TRACE(PID, FUNC, WHAT)
-#endif
-
-static Uint processes_bif_tab_chunks;
-static Export processes_trap_export;
-
-typedef struct {
- Uint64 interval;
-} ErtsProcessesBifChunkInfo;
-
-typedef enum {
- INITIALIZING,
- INSPECTING_TABLE,
- INSPECTING_TERMINATED_PROCESSES,
- BUILDING_RESULT,
- RETURN_RESULT
-} ErtsProcessesBifState;
-
-typedef struct {
- ErtsProcessesBifState state;
- Eterm caller;
- ErtsProcessesBifChunkInfo *chunk;
- int tix;
- int pid_ix;
- int pid_sz;
- Eterm *pid;
- ErtsTermProcElement *bif_invocation; /* Only used when > 1 chunk */
-
-#if ERTS_PROCESSES_BIF_DEBUGLEVEL != 0 || ERTS_PROCS_DBG_DO_TRACE
- struct {
- Eterm caller;
-#if ERTS_PROCESSES_BIF_DEBUGLEVEL >= ERTS_PROCS_DBGLVL_CHK_FOUND_PIDS
- Uint64 *pid_started;
-#endif
-#if ERTS_PROCESSES_BIF_DEBUGLEVEL >= ERTS_PROCS_DBGLVL_CHK_HALLOC
- Eterm *heap;
- Uint heap_size;
-#endif
-#if ERTS_PROCESSES_BIF_DEBUGLEVEL >= ERTS_PROCS_DBGLVL_CHK_PIDS
- int correct_pids_verified;
- Eterm *correct_pids;
-#endif
- } debug;
-#endif
-
-} ErtsProcessesBifData;
-
-
-#if ERTS_PROCESSES_BIF_DEBUGLEVEL != 0
-static void debug_processes_assert_error(char* expr, char* file, int line);
-#endif
-#if ERTS_PROCESSES_BIF_DEBUGLEVEL >= ERTS_PROCS_DBGLVL_CHK_RESLIST
-static void debug_processes_check_res_list(Eterm list);
-#endif
-#if ERTS_PROCESSES_BIF_DEBUGLEVEL >= ERTS_PROCS_DBGLVL_CHK_PIDS
-static void debug_processes_save_all_pids(ErtsProcessesBifData *pbdp);
-static void debug_processes_verify_all_pids(ErtsProcessesBifData *pbdp);
-#endif
-#if ERTS_PROCESSES_BIF_DEBUGLEVEL >= ERTS_PROCS_DBGLVL_CHK_FOUND_PIDS
-static void debug_processes_check_found_pid(ErtsProcessesBifData *pbdp,
- Eterm pid,
- Uint64 ic,
- int pid_should_be_found);
-#endif
-#if ERTS_PROCESSES_BIF_DEBUGLEVEL >= ERTS_PROCS_DBGLVL_CHK_TERM_PROC_LIST
-static void debug_processes_check_term_proc_list(void);
-static void debug_processes_check_term_proc_free_list(ErtsTermProcElement *tpep);
-#endif
-
-static void
-save_terminating_process(Process *p)
-{
- ErtsTermProcElement *tpep = erts_alloc(ERTS_ALC_T_PROCS_TPROC_EL,
- sizeof(ErtsTermProcElement));
- ERTS_PROCS_ASSERT(saved_term_procs.start && saved_term_procs.end);
- ERTS_SMP_LC_ASSERT(erts_lc_rwmtx_is_rwlocked(&erts_proc_tab_rwmtx));
-
- ERTS_PROCS_DBG_CHK_TPLIST();
-
- tpep->prev = saved_term_procs.end;
- tpep->next = NULL;
- tpep->ix = internal_pid_index(p->id);
- tpep->u.process.pid = p->id;
- tpep->u.process.spawned = p->started_interval;
- tpep->u.process.exited = get_proc_interval();
-
- saved_term_procs.end->next = tpep;
- saved_term_procs.end = tpep;
-
- ERTS_PROCS_DBG_CHK_TPLIST();
-
- ERTS_PROCS_ASSERT(tpep->prev->ix >= 0
- ? (tpep->u.process.exited
- >= tpep->prev->u.process.exited)
- : (tpep->u.process.exited
- >= tpep->prev->u.bif_invocation.interval));
-}
-
-static void
-cleanup_processes_bif_data(Binary *bp)
-{
- ErtsProcessesBifData *pbdp = ERTS_MAGIC_BIN_DATA(bp);
-
- ERTS_PROCS_DBG_TRACE(pbdp->debug.caller, cleanup_processes_bif_data, call);
-
- if (pbdp->state != INITIALIZING) {
-
- if (pbdp->chunk) {
- erts_free(ERTS_ALC_T_PROCS_CNKINF, pbdp->chunk);
- pbdp->chunk = NULL;
- }
- if (pbdp->pid) {
- erts_free(ERTS_ALC_T_PROCS_PIDS, pbdp->pid);
- pbdp->pid = NULL;
- }
-
-#if ERTS_PROCESSES_BIF_DEBUGLEVEL >= ERTS_PROCS_DBGLVL_CHK_FOUND_PIDS
- if (pbdp->debug.pid_started) {
- erts_free(ERTS_ALC_T_PROCS_PIDS, pbdp->debug.pid_started);
- pbdp->debug.pid_started = NULL;
- }
-#endif
-
- if (pbdp->bif_invocation) {
- ErtsTermProcElement *tpep;
-
- erts_smp_rwmtx_rwlock(&erts_proc_tab_rwmtx);
-
- ERTS_PROCS_DBG_TRACE(pbdp->debug.caller,
- cleanup_processes_bif_data,
- term_proc_cleanup);
-
- tpep = pbdp->bif_invocation;
- pbdp->bif_invocation = NULL;
-
- ERTS_PROCS_DBG_CHK_TPLIST();
-
- if (tpep->prev) {
- /*
- * Only remove this bif invokation when we
- * have preceding invokations.
- */
- tpep->prev->next = tpep->next;
- if (tpep->next)
- tpep->next->prev = tpep->prev;
- else {
- /*
- * At the time of writing this branch cannot be
- * reached. I don't want to remove this code though
- * since it may be possible to reach this line
- * in the future if the cleanup order in
- * erts_do_exit_process() is changed. The ASSERT(0)
- * is only here to make us aware that the reorder
- * has happened. /rickard
- */
- ASSERT(0);
- saved_term_procs.end = tpep->prev;
- }
- erts_free(ERTS_ALC_T_PROCS_TPROC_EL, tpep);
- }
- else {
- /*
- * Free all elements until next bif invokation
- * is found.
- */
- ERTS_PROCS_ASSERT(saved_term_procs.start == tpep);
- do {
- ErtsTermProcElement *ftpep = tpep;
- tpep = tpep->next;
- erts_free(ERTS_ALC_T_PROCS_TPROC_EL, ftpep);
- } while (tpep && tpep->ix >= 0);
- saved_term_procs.start = tpep;
- if (tpep)
- tpep->prev = NULL;
- else
- saved_term_procs.end = NULL;
- }
-
- ERTS_PROCS_DBG_CHK_TPLIST();
-
- erts_smp_rwmtx_rwunlock(&erts_proc_tab_rwmtx);
-
- }
- }
-
- ERTS_PROCS_DBG_TRACE(pbdp->debug.caller,
- cleanup_processes_bif_data,
- return);
- ERTS_PROCS_DBG_CLEANUP(pbdp);
-}
-
-static int
-processes_bif_engine(Process *p, Eterm *res_accp, Binary *mbp)
-{
- ErtsProcessesBifData *pbdp = ERTS_MAGIC_BIN_DATA(mbp);
- int have_reds;
- int reds;
- int locked = 0;
-
- do {
- switch (pbdp->state) {
- case INITIALIZING:
- pbdp->chunk = erts_alloc(ERTS_ALC_T_PROCS_CNKINF,
- (sizeof(ErtsProcessesBifChunkInfo)
- * processes_bif_tab_chunks));
- pbdp->tix = 0;
- pbdp->pid_ix = 0;
-
- erts_smp_rwmtx_rwlock(&erts_proc_tab_rwmtx);
- locked = 1;
-
- ERTS_PROCS_DBG_TRACE(p->id, processes_bif_engine, init);
-
- pbdp->pid_sz = erts_process_count();
- pbdp->pid = erts_alloc(ERTS_ALC_T_PROCS_PIDS,
- sizeof(Eterm)*pbdp->pid_sz);
-
-#if ERTS_PROCESSES_BIF_DEBUGLEVEL >= ERTS_PROCS_DBGLVL_CHK_FOUND_PIDS
- pbdp->debug.pid_started = erts_alloc(ERTS_ALC_T_PROCS_PIDS,
- sizeof(Uint64)*pbdp->pid_sz);
-#endif
-
- ERTS_PROCS_DBG_SAVE_PIDS(pbdp);
-
- if (processes_bif_tab_chunks == 1)
- pbdp->bif_invocation = NULL;
- else {
- /*
- * We will have to access the table multiple times
- * releasing the table lock in between chunks.
- */
- pbdp->bif_invocation = erts_alloc(ERTS_ALC_T_PROCS_TPROC_EL,
- sizeof(ErtsTermProcElement));
- pbdp->bif_invocation->ix = -1;
- pbdp->bif_invocation->u.bif_invocation.interval
- = step_proc_interval();
- ERTS_PROCS_DBG_CHK_TPLIST();
-
- pbdp->bif_invocation->next = NULL;
- if (saved_term_procs.end) {
- pbdp->bif_invocation->prev = saved_term_procs.end;
- saved_term_procs.end->next = pbdp->bif_invocation;
- ERTS_PROCS_ASSERT(saved_term_procs.start);
- }
- else {
- pbdp->bif_invocation->prev = NULL;
- saved_term_procs.start = pbdp->bif_invocation;
- }
- saved_term_procs.end = pbdp->bif_invocation;
-
- ERTS_PROCS_DBG_CHK_TPLIST();
-
- }
-
- pbdp->state = INSPECTING_TABLE;
- /* Fall through */
-
- case INSPECTING_TABLE: {
- int ix = pbdp->tix;
- int indices = ERTS_PROCESSES_BIF_TAB_CHUNK_SIZE;
- int cix = ix / ERTS_PROCESSES_BIF_TAB_CHUNK_SIZE;
- int end_ix = ix + indices;
- Uint64 *invocation_interval_p;
-
- invocation_interval_p
- = (pbdp->bif_invocation
- ? &pbdp->bif_invocation->u.bif_invocation.interval
- : NULL);
-
- ERTS_PROCS_ASSERT(is_nil(*res_accp));
- if (!locked) {
- erts_smp_rwmtx_rwlock(&erts_proc_tab_rwmtx);
- locked = 1;
- }
-
- ERTS_SMP_LC_ASSERT(erts_lc_rwmtx_is_rwlocked(&erts_proc_tab_rwmtx));
- ERTS_PROCS_DBG_TRACE(p->id, processes_bif_engine, insp_table);
-
- if (cix != 0)
- pbdp->chunk[cix].interval = step_proc_interval();
- else if (pbdp->bif_invocation)
- pbdp->chunk[0].interval = *invocation_interval_p;
- /* else: interval is irrelevant */
-
- if (end_ix >= erts_proc.max) {
- ERTS_PROCS_ASSERT(cix+1 == processes_bif_tab_chunks);
- end_ix = erts_proc.max;
- indices = end_ix - ix;
- /* What to do when done with this chunk */
- pbdp->state = (processes_bif_tab_chunks == 1
- ? BUILDING_RESULT
- : INSPECTING_TERMINATED_PROCESSES);
- }
-
- for (; ix < end_ix; ix++) {
- Process *rp = erts_pix2proc(ix);
- if (rp
- && (!invocation_interval_p
- || rp->started_interval < *invocation_interval_p)) {
- ERTS_PROCS_ASSERT(is_internal_pid(rp->id));
- pbdp->pid[pbdp->pid_ix] = rp->id;
-
-#if ERTS_PROCESSES_BIF_DEBUGLEVEL >= ERTS_PROCS_DBGLVL_CHK_FOUND_PIDS
- pbdp->debug.pid_started[pbdp->pid_ix] = rp->started_interval;
-#endif
-
- pbdp->pid_ix++;
- ERTS_PROCS_ASSERT(pbdp->pid_ix <= pbdp->pid_sz);
- }
- }
-
- pbdp->tix = end_ix;
-
- erts_smp_rwmtx_rwunlock(&erts_proc_tab_rwmtx);
- locked = 0;
-
- reds = indices/ERTS_PROCESSES_BIF_TAB_INSPECT_INDICES_PER_RED;
- BUMP_REDS(p, reds);
-
- have_reds = ERTS_BIF_REDS_LEFT(p);
-
- if (have_reds && pbdp->state == INSPECTING_TABLE) {
- ix = pbdp->tix;
- indices = ERTS_PROCESSES_BIF_TAB_CHUNK_SIZE;
- end_ix = ix + indices;
- if (end_ix > erts_proc.max) {
- end_ix = erts_proc.max;
- indices = end_ix - ix;
- }
-
- reds = indices/ERTS_PROCESSES_BIF_TAB_INSPECT_INDICES_PER_RED;
-
- /* Pretend we have no reds left if we haven't got enough
- reductions to complete next chunk */
- if (reds > have_reds)
- have_reds = 0;
- }
-
- break;
- }
-
- case INSPECTING_TERMINATED_PROCESSES: {
- int i;
- int max_reds;
- int free_term_procs = 0;
- Uint64 invocation_interval;
- ErtsTermProcElement *tpep;
- ErtsTermProcElement *free_list = NULL;
-
- tpep = pbdp->bif_invocation;
- ERTS_PROCS_ASSERT(tpep);
- invocation_interval = tpep->u.bif_invocation.interval;
-
- max_reds = have_reds = ERTS_BIF_REDS_LEFT(p);
- if (max_reds > ERTS_PROCESSES_INSPECT_TERM_PROC_MAX_REDS)
- max_reds = ERTS_PROCESSES_INSPECT_TERM_PROC_MAX_REDS;
-
- reds = 0;
- erts_smp_rwmtx_rwlock(&erts_proc_tab_rwmtx);
- ERTS_PROCS_DBG_TRACE(p->id, processes_bif_engine, insp_term_procs);
-
- ERTS_PROCS_DBG_CHK_TPLIST();
-
- if (tpep->prev)
- tpep->prev->next = tpep->next;
- else {
- ERTS_PROCS_ASSERT(saved_term_procs.start == tpep);
- saved_term_procs.start = tpep->next;
-
- if (saved_term_procs.start && saved_term_procs.start->ix >= 0) {
- free_list = saved_term_procs.start;
- free_term_procs = 1;
- }
- }
-
- if (tpep->next)
- tpep->next->prev = tpep->prev;
- else
- saved_term_procs.end = tpep->prev;
-
- tpep = tpep->next;
-
- i = 0;
- while (reds < max_reds && tpep) {
- if (tpep->ix < 0) {
- if (free_term_procs) {
- ERTS_PROCS_ASSERT(free_list);
- ERTS_PROCS_ASSERT(tpep->prev);
-
- tpep->prev->next = NULL; /* end of free_list */
- saved_term_procs.start = tpep;
- tpep->prev = NULL;
- free_term_procs = 0;
- }
- }
- else {
- int cix = tpep->ix/ERTS_PROCESSES_BIF_TAB_CHUNK_SIZE;
- Uint64 chunk_interval = pbdp->chunk[cix].interval;
- Eterm pid = tpep->u.process.pid;
- ERTS_PROCS_ASSERT(is_internal_pid(pid));
-
- if (tpep->u.process.spawned < invocation_interval) {
- if (tpep->u.process.exited < chunk_interval) {
- ERTS_PROCS_DBG_CHK_PID_NOT_FOUND(pbdp,
- pid,
- tpep->u.process.spawned);
- pbdp->pid[pbdp->pid_ix] = pid;
-#if ERTS_PROCESSES_BIF_DEBUGLEVEL >= ERTS_PROCS_DBGLVL_CHK_FOUND_PIDS
- pbdp->debug.pid_started[pbdp->pid_ix]
- = tpep->u.process.spawned;
-#endif
- pbdp->pid_ix++;
- ERTS_PROCS_ASSERT(pbdp->pid_ix <= pbdp->pid_sz);
- }
- else {
- ERTS_PROCS_DBG_CHK_PID_FOUND(pbdp,
- pid,
- tpep->u.process.spawned);
- }
- }
- else {
- ERTS_PROCS_DBG_CHK_PID_NOT_FOUND(pbdp,
- pid,
- tpep->u.process.spawned);
- }
-
- i++;
- if (i == ERTS_PROCESSES_BIF_INSPECT_TERM_PROC_PER_RED) {
- reds++;
- i = 0;
- }
- if (free_term_procs)
- reds += ERTS_PROCESSES_BIF_TAB_FREE_TERM_PROC_REDS;
- }
- tpep = tpep->next;
- }
-
- if (free_term_procs) {
- ERTS_PROCS_ASSERT(free_list);
- saved_term_procs.start = tpep;
- if (!tpep)
- saved_term_procs.end = NULL;
- else {
- ERTS_PROCS_ASSERT(tpep->prev);
- tpep->prev->next = NULL; /* end of free_list */
- tpep->prev = NULL;
- }
- }
-
- if (!tpep) {
- /* Done */
- ERTS_PROCS_ASSERT(pbdp->pid_ix == pbdp->pid_sz);
- pbdp->state = BUILDING_RESULT;
- pbdp->bif_invocation->next = free_list;
- free_list = pbdp->bif_invocation;
- pbdp->bif_invocation = NULL;
- }
- else {
- /* Link in bif_invocation again where we left off */
- pbdp->bif_invocation->prev = tpep->prev;
- pbdp->bif_invocation->next = tpep;
- tpep->prev = pbdp->bif_invocation;
- if (pbdp->bif_invocation->prev)
- pbdp->bif_invocation->prev->next = pbdp->bif_invocation;
- else {
- ERTS_PROCS_ASSERT(saved_term_procs.start == tpep);
- saved_term_procs.start = pbdp->bif_invocation;
- }
- }
-
- ERTS_PROCS_DBG_CHK_TPLIST();
- ERTS_PROCS_DBG_CHK_FREELIST(free_list);
- erts_smp_rwmtx_rwunlock(&erts_proc_tab_rwmtx);
-
- /*
- * We do the actual free of term proc structures now when we
- * have released the table lock instead of when we encountered
- * them. This since free() isn't for free and we don't want to
- * unnecessarily block other schedulers.
- */
- while (free_list) {
- tpep = free_list;
- free_list = tpep->next;
- erts_free(ERTS_ALC_T_PROCS_TPROC_EL, tpep);
- }
-
- have_reds -= reds;
- if (have_reds < 0)
- have_reds = 0;
- BUMP_REDS(p, reds);
- break;
- }
-
- case BUILDING_RESULT: {
- int conses, ix, min_ix;
- Eterm *hp;
- Eterm res = *res_accp;
-
- ERTS_PROCS_DBG_VERIFY_PIDS(pbdp);
- ERTS_PROCS_DBG_CHK_RESLIST(res);
-
- ERTS_PROCS_DBG_TRACE(p->id, processes_bif_engine, begin_build_res);
-
- have_reds = ERTS_BIF_REDS_LEFT(p);
- conses = ERTS_PROCESSES_BIF_BUILD_RESULT_CONSES_PER_RED*have_reds;
- min_ix = pbdp->pid_ix - conses;
- if (min_ix < 0) {
- min_ix = 0;
- conses = pbdp->pid_ix;
- }
-
- hp = HAlloc(p, conses*2);
- ERTS_PROCS_DBG_SAVE_HEAP_ALLOC(pbdp, hp, conses*2);
-
- for (ix = pbdp->pid_ix - 1; ix >= min_ix; ix--) {
- ERTS_PROCS_ASSERT(is_internal_pid(pbdp->pid[ix]));
- res = CONS(hp, pbdp->pid[ix], res);
- hp += 2;
- }
-
- ERTS_PROCS_DBG_VERIFY_HEAP_ALLOC_USED(pbdp, hp);
-
- pbdp->pid_ix = min_ix;
- if (min_ix == 0)
- pbdp->state = RETURN_RESULT;
- else {
- pbdp->pid_sz = min_ix;
- pbdp->pid = erts_realloc(ERTS_ALC_T_PROCS_PIDS,
- pbdp->pid,
- sizeof(Eterm)*pbdp->pid_sz);
-#if ERTS_PROCESSES_BIF_DEBUGLEVEL >= ERTS_PROCS_DBGLVL_CHK_FOUND_PIDS
- pbdp->debug.pid_started = erts_realloc(ERTS_ALC_T_PROCS_PIDS,
- pbdp->debug.pid_started,
- (sizeof(Uint64)
- * pbdp->pid_sz));
-#endif
- }
- reds = conses/ERTS_PROCESSES_BIF_BUILD_RESULT_CONSES_PER_RED;
- BUMP_REDS(p, reds);
- have_reds -= reds;
-
- ERTS_PROCS_DBG_CHK_RESLIST(res);
- ERTS_PROCS_DBG_TRACE(p->id, processes_bif_engine, end_build_res);
- *res_accp = res;
- break;
- }
- case RETURN_RESULT:
- cleanup_processes_bif_data(mbp);
- return 1;
-
- default:
- erl_exit(ERTS_ABORT_EXIT,
- "erlang:processes/0: Invalid state: %d\n",
- (int) pbdp->state);
- }
-
-
- } while (have_reds || pbdp->state == RETURN_RESULT);
-
- return 0;
-}
-
-/*
- * processes_trap/2 is a hidden BIF that processes/0 traps to.
- */
-
-static BIF_RETTYPE processes_trap(BIF_ALIST_2)
-{
- Eterm res_acc;
- Binary *mbp;
-
- /*
- * This bif cannot be called from erlang code. It can only be
- * trapped to from processes/0; therefore, a bad argument
- * is a processes/0 internal error.
- */
-
- ERTS_PROCS_DBG_TRACE(BIF_P->id, processes_trap, call);
- ERTS_PROCS_ASSERT(is_nil(BIF_ARG_1) || is_list(BIF_ARG_1));
-
- res_acc = BIF_ARG_1;
-
- ERTS_PROCS_ASSERT(ERTS_TERM_IS_MAGIC_BINARY(BIF_ARG_2));
-
- mbp = ((ProcBin *) binary_val(BIF_ARG_2))->val;
-
- ERTS_PROCS_ASSERT(ERTS_MAGIC_BIN_DESTRUCTOR(mbp)
- == cleanup_processes_bif_data);
- ERTS_PROCS_ASSERT(
- ((ErtsProcessesBifData *) ERTS_MAGIC_BIN_DATA(mbp))->debug.caller
- == BIF_P->id);
-
- if (processes_bif_engine(BIF_P, &res_acc, mbp)) {
- ERTS_PROCS_DBG_TRACE(BIF_P->id, processes_trap, return);
- BIF_RET(res_acc);
- }
- else {
- ERTS_PROCS_DBG_TRACE(BIF_P->id, processes_trap, trap);
- ERTS_BIF_YIELD2(&processes_trap_export, BIF_P, res_acc, BIF_ARG_2);
- }
-}
-
-
-
-/*
- * The actual processes/0 BIF.
- */
-
-BIF_RETTYPE processes_0(BIF_ALIST_0)
-{
- /*
- * A requirement: The list of pids returned should be a consistent
- * snapshot of all processes existing at some point
- * in time during the execution of processes/0. Since
- * processes might terminate while processes/0 is
- * executing, we have to keep track of terminated
- * processes and add them to the result. We also
- * ignore processes created after processes/0 has
- * begun executing.
- */
- Eterm res_acc = NIL;
- Binary *mbp = erts_create_magic_binary(sizeof(ErtsProcessesBifData),
- cleanup_processes_bif_data);
- ErtsProcessesBifData *pbdp = ERTS_MAGIC_BIN_DATA(mbp);
-
- ERTS_PROCS_DBG_TRACE(BIF_P->id, processes_0, call);
- pbdp->state = INITIALIZING;
- ERTS_PROCS_DBG_INIT(BIF_P, pbdp);
-
- if (ERTS_BIF_REDS_LEFT(BIF_P) >= ERTS_PROCESSES_BIF_MIN_START_REDS
- && processes_bif_engine(BIF_P, &res_acc, mbp)) {
- erts_bin_free(mbp);
- ERTS_PROCS_DBG_CHK_RESLIST(res_acc);
- ERTS_PROCS_DBG_TRACE(BIF_P->id, processes_0, return);
- BIF_RET(res_acc);
- }
- else {
- Eterm *hp;
- Eterm magic_bin;
- ERTS_PROCS_DBG_CHK_RESLIST(res_acc);
- hp = HAlloc(BIF_P, PROC_BIN_SIZE);
- ERTS_PROCS_DBG_SAVE_HEAP_ALLOC(pbdp, hp, PROC_BIN_SIZE);
- magic_bin = erts_mk_magic_binary_term(&hp, &MSO(BIF_P), mbp);
- ERTS_PROCS_DBG_VERIFY_HEAP_ALLOC_USED(pbdp, hp);
- ERTS_PROCS_DBG_TRACE(BIF_P->id, processes_0, trap);
- ERTS_BIF_YIELD2(&processes_trap_export, BIF_P, res_acc, magic_bin);
- }
-}
-
-static void
-init_processes_bif(void)
-{
- saved_term_procs.start = NULL;
- saved_term_procs.end = NULL;
- processes_bif_tab_chunks = (((erts_proc.max - 1)
- / ERTS_PROCESSES_BIF_TAB_CHUNK_SIZE)
- + 1);
-
- /* processes_trap/2 is a hidden BIF that the processes/0 BIF traps to. */
- erts_init_trap_export(&processes_trap_export, am_erlang, am_processes_trap, 2,
- &processes_trap);
-
-}
-
-/*
- * Debug stuff
- */
-
-#if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK)
-int
-erts_dbg_check_halloc_lock(Process *p)
-{
- if (ERTS_PROC_LOCK_MAIN & erts_proc_lc_my_proc_locks(p))
- return 1;
- if (p->id == ERTS_INVALID_PID)
- return 1;
- if (p->scheduler_data && p == p->scheduler_data->match_pseudo_process)
- return 1;
- if (erts_thr_progress_is_blocking())
- return 1;
- return 0;
-}
-#endif
-
-Eterm
-erts_debug_processes(Process *c_p)
-{
- /* This is the old processes/0 BIF. */
- int i;
- Uint need;
- Eterm res;
- Eterm* hp;
- Process *p;
- Eterm *hp_end;
-
- erts_smp_rwmtx_rwlock(&erts_proc_tab_rwmtx);
-
- res = NIL;
- need = erts_process_count() * 2;
- hp = HAlloc(c_p, need); /* we need two heap words for each pid */
- hp_end = hp + need;
-
- /* make the list by scanning bakward */
-
-
- for (i = erts_proc.max-1; i >= 0; i--) {
- p = erts_pix2proc(i);
- if (p) {
- res = CONS(hp, p->id, res);
- hp += 2;
- }
- }
-
- erts_smp_rwmtx_rwunlock(&erts_proc_tab_rwmtx);
-
- HRelease(c_p, hp_end, hp);
-
- return res;
-}
-
-Eterm
-erts_debug_processes_bif_info(Process *c_p)
-{
- ERTS_DECL_AM(processes_bif_info);
- Eterm elements[] = {
- AM_processes_bif_info,
- make_small((Uint) ERTS_PROCESSES_BIF_MIN_START_REDS),
- make_small((Uint) processes_bif_tab_chunks),
- make_small((Uint) ERTS_PROCESSES_BIF_TAB_CHUNK_SIZE),
- make_small((Uint) ERTS_PROCESSES_BIF_TAB_INSPECT_INDICES_PER_RED),
- make_small((Uint) ERTS_PROCESSES_BIF_TAB_FREE_TERM_PROC_REDS),
- make_small((Uint) ERTS_PROCESSES_BIF_INSPECT_TERM_PROC_PER_RED),
- make_small((Uint) ERTS_PROCESSES_INSPECT_TERM_PROC_MAX_REDS),
- make_small((Uint) ERTS_PROCESSES_BIF_BUILD_RESULT_CONSES_PER_RED),
- make_small((Uint) ERTS_PROCESSES_BIF_DEBUGLEVEL)
- };
- Uint sz = 0;
- Eterm *hp;
- (void) erts_bld_tuplev(NULL, &sz, sizeof(elements)/sizeof(Eterm), elements);
- hp = HAlloc(c_p, sz);
- return erts_bld_tuplev(&hp, NULL, sizeof(elements)/sizeof(Eterm), elements);
-}
-
-#if ERTS_PROCESSES_BIF_DEBUGLEVEL >= ERTS_PROCS_DBGLVL_CHK_FOUND_PIDS
-static void
-debug_processes_check_found_pid(ErtsProcessesBifData *pbdp,
- Eterm pid,
- Uint64 ic,
- int pid_should_be_found)
-{
- int i;
- for (i = 0; i < pbdp->pid_ix; i++) {
- if (pbdp->pid[i] == pid && pbdp->debug.pid_started[i] == ic) {
- ERTS_PROCS_ASSERT(pid_should_be_found);
- return;
- }
- }
- ERTS_PROCS_ASSERT(!pid_should_be_found);
-}
-#endif
-
-#if ERTS_PROCESSES_BIF_DEBUGLEVEL >= ERTS_PROCS_DBGLVL_CHK_RESLIST
-static void
-debug_processes_check_res_list(Eterm list)
-{
- while (is_list(list)) {
- Eterm* consp = list_val(list);
- Eterm hd = CAR(consp);
- ERTS_PROCS_ASSERT(is_internal_pid(hd));
- list = CDR(consp);
- }
-
- ERTS_PROCS_ASSERT(is_nil(list));
-}
-#endif
-
-#if ERTS_PROCESSES_BIF_DEBUGLEVEL >= ERTS_PROCS_DBGLVL_CHK_PIDS
-
-static void
-debug_processes_save_all_pids(ErtsProcessesBifData *pbdp)
-{
- int ix, tix, cpix;
- pbdp->debug.correct_pids_verified = 0;
- pbdp->debug.correct_pids = erts_alloc(ERTS_ALC_T_PROCS_PIDS,
- sizeof(Eterm)*pbdp->pid_sz);
-
- for (tix = 0, cpix = 0; tix < erts_proc.max; tix++) {
- Process *rp = erts_pix2proc(tix);
- if (rp) {
- ERTS_PROCS_ASSERT(is_internal_pid(rp->id));
- pbdp->debug.correct_pids[cpix++] = rp->id;
- ERTS_PROCS_ASSERT(cpix <= pbdp->pid_sz);
- }
- }
- ERTS_PROCS_ASSERT(cpix == pbdp->pid_sz);
-
- for (ix = 0; ix < pbdp->pid_sz; ix++)
- pbdp->pid[ix] = make_small(ix);
-}
-
-static void
-debug_processes_verify_all_pids(ErtsProcessesBifData *pbdp)
-{
- int ix, cpix;
-
- ERTS_PROCS_ASSERT(pbdp->pid_ix == pbdp->pid_sz);
-
- for (ix = 0; ix < pbdp->pid_sz; ix++) {
- int found = 0;
- Eterm pid = pbdp->pid[ix];
- ERTS_PROCS_ASSERT(is_internal_pid(pid));
- for (cpix = ix; cpix < pbdp->pid_sz; cpix++) {
- if (pbdp->debug.correct_pids[cpix] == pid) {
- pbdp->debug.correct_pids[cpix] = NIL;
- found = 1;
- break;
- }
- }
- if (!found) {
- for (cpix = 0; cpix < ix; cpix++) {
- if (pbdp->debug.correct_pids[cpix] == pid) {
- pbdp->debug.correct_pids[cpix] = NIL;
- found = 1;
- break;
- }
- }
- }
- ERTS_PROCS_ASSERT(found);
- }
- pbdp->debug.correct_pids_verified = 1;
-
- erts_free(ERTS_ALC_T_PROCS_PIDS, pbdp->debug.correct_pids);
- pbdp->debug.correct_pids = NULL;
-}
-#endif /* ERTS_PROCESSES_BIF_DEBUGLEVEL >= ERTS_PROCS_DBGLVL_CHK_PIDS */
-
-#if ERTS_PROCESSES_BIF_DEBUGLEVEL >= ERTS_PROCS_DBGLVL_CHK_TERM_PROC_LIST
-static void
-debug_processes_check_term_proc_list(void)
-{
- ERTS_SMP_LC_ASSERT(erts_lc_rwmtx_is_rwlocked(&erts_proc_tab_rwmtx));
- if (!saved_term_procs.start)
- ERTS_PROCS_ASSERT(!saved_term_procs.end);
- else {
- Uint64 curr_interval = get_proc_interval();
- Uint64 *prev_x_interval_p = NULL;
- ErtsTermProcElement *tpep;
-
- for (tpep = saved_term_procs.start; tpep; tpep = tpep->next) {
- if (!tpep->prev)
- ERTS_PROCS_ASSERT(saved_term_procs.start == tpep);
- else
- ERTS_PROCS_ASSERT(tpep->prev->next == tpep);
- if (!tpep->next)
- ERTS_PROCS_ASSERT(saved_term_procs.end == tpep);
- else
- ERTS_PROCS_ASSERT(tpep->next->prev == tpep);
- if (tpep->ix < 0) {
- Uint64 interval = tpep->u.bif_invocation.interval;
- ERTS_PROCS_ASSERT(interval <= curr_interval);
- }
- else {
- Uint64 s_interval = tpep->u.process.spawned;
- Uint64 x_interval = tpep->u.process.exited;
-
- ERTS_PROCS_ASSERT(s_interval <= x_interval);
- if (prev_x_interval_p)
- ERTS_PROCS_ASSERT(*prev_x_interval_p <= x_interval);
- prev_x_interval_p = &tpep->u.process.exited;
- ERTS_PROCS_ASSERT(is_internal_pid(tpep->u.process.pid));
- ERTS_PROCS_ASSERT(tpep->ix
- == internal_pid_index(tpep->u.process.pid));
- }
- }
-
- }
-}
-
-static void
-debug_processes_check_term_proc_free_list(ErtsTermProcElement *free_list)
-{
- if (saved_term_procs.start) {
- ErtsTermProcElement *ftpep;
- ErtsTermProcElement *tpep;
-
- for (ftpep = free_list; ftpep; ftpep = ftpep->next) {
- for (tpep = saved_term_procs.start; tpep; tpep = tpep->next)
- ERTS_PROCS_ASSERT(ftpep != tpep);
- }
- }
-}
-
-#endif
-
-#if ERTS_PROCESSES_BIF_DEBUGLEVEL != 0
-
-static void
-debug_processes_assert_error(char* expr, char* file, int line)
-{
- fflush(stdout);
- erts_fprintf(stderr, "%s:%d: Assertion failed: %s\n", file, line, expr);
- fflush(stderr);
- abort();
-}
-
-#endif
-
-/* *\
- * End of the processes/0 BIF implementation. *
-\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
/*
* A nice system halt closing all open port goes as follows:
* 1) This function schedules the aux work ERTS_SSI_AUX_WORK_REAP_PORTS
@@ -10428,3 +9041,19 @@ void erl_halt(int code)
notify_reap_ports_relb();
}
}
+
+#if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK)
+int
+erts_dbg_check_halloc_lock(Process *p)
+{
+ if (ERTS_PROC_LOCK_MAIN & erts_proc_lc_my_proc_locks(p))
+ return 1;
+ if (p->common.id == ERTS_INVALID_PID)
+ return 1;
+ if (p->scheduler_data && p == p->scheduler_data->match_pseudo_process)
+ return 1;
+ if (erts_thr_progress_is_blocking())
+ return 1;
+ return 0;
+}
+#endif
diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h
index 93e71681da..6d1032c292 100644
--- a/erts/emulator/beam/erl_process.h
+++ b/erts/emulator/beam/erl_process.h
@@ -42,6 +42,9 @@ typedef struct process Process;
#include "erl_process_lock.h" /* Only pull out important types... */
#undef ERTS_PROCESS_LOCK_ONLY_PROC_LOCK_TYPE__
+#define ERL_PORT_GET_PORT_TYPE_ONLY__
+#include "erl_port.h"
+#undef ERL_PORT_GET_PORT_TYPE_ONLY__
#include "erl_vm.h"
#include "erl_smp.h"
#include "erl_message.h"
@@ -66,11 +69,10 @@ typedef struct process Process;
#undef ERL_THR_PROGRESS_TSD_TYPE_ONLY
struct ErtsNodesMonitor_;
-struct port;
#define ERTS_MAX_NO_OF_SCHEDULERS 1024
-#define ERTS_DEFAULT_MAX_PROCESSES (1 << 15)
+#define ERTS_DEFAULT_MAX_PROCESSES (1 << 18)
#define ERTS_HEAP_ALLOC(Type, Size) \
erts_alloc((Type), (Size))
@@ -205,32 +207,10 @@ extern int erts_sched_thread_suggested_stack_size;
((Uint32) erts_smp_atomic32_read_nob(&(RQ)->flags))
#define ERTS_RUNQ_FLGS_GET_MB(RQ) \
((Uint32) erts_smp_atomic32_read_mb(&(RQ)->flags))
-#define ERTS_RUNQ_FLGS_MASK_SET(RQ, MSK, FLGS) \
- ((Uint32) erts_smp_atomic32_mask_set_relb(&(RQ)->flags, \
- (erts_aint32_t) (MSK), \
- (erts_aint32_t) (FLGS)))
-
-ERTS_GLB_INLINE erts_aint32_t
-erts_smp_atomic32_mask_set_relb(erts_smp_atomic32_t *a32p,
- erts_aint32_t mask,
- erts_aint32_t set);
-#if ERTS_GLB_INLINE_INCL_FUNC_DEF
-ERTS_GLB_INLINE erts_aint32_t
-erts_smp_atomic32_mask_set_relb(erts_smp_atomic32_t *a32p,
- erts_aint32_t mask,
- erts_aint32_t set)
-{
- erts_aint32_t act = erts_smp_atomic32_read_nob(a32p);
- while (1) {
- erts_aint32_t exp = act;
- erts_aint32_t new = exp & ~mask;
- new |= (mask & set);
- act = erts_smp_atomic32_cmpxchg_relb(a32p, new, exp);
- if (act == exp)
- return act;
- }
-}
-#endif
+#define ERTS_RUNQ_FLGS_READ_BSET(RQ, MSK, FLGS) \
+ ((Uint32) erts_smp_atomic32_read_bset_relb(&(RQ)->flags, \
+ (erts_aint32_t) (MSK), \
+ (erts_aint32_t) (FLGS)))
typedef enum {
ERTS_SCHDLR_SSPND_DONE_MSCHED_BLOCKED,
@@ -270,20 +250,20 @@ typedef enum {
* eachother. Most frequent - lowest bit number.
*/
-#define ERTS_SSI_AUX_WORK_DD (((erts_aint32_t) 1) << 0)
-#define ERTS_SSI_AUX_WORK_DD_THR_PRGR (((erts_aint32_t) 1) << 1)
-#define ERTS_SSI_AUX_WORK_FIX_ALLOC_DEALLOC (((erts_aint32_t) 1) << 2)
-#define ERTS_SSI_AUX_WORK_FIX_ALLOC_LOWER_LIM (((erts_aint32_t) 1) << 3)
-#define ERTS_SSI_AUX_WORK_ASYNC_READY (((erts_aint32_t) 1) << 4)
-#define ERTS_SSI_AUX_WORK_ASYNC_READY_CLEAN (((erts_aint32_t) 1) << 5)
-#define ERTS_SSI_AUX_WORK_MISC_THR_PRGR (((erts_aint32_t) 1) << 6)
-#define ERTS_SSI_AUX_WORK_MISC (((erts_aint32_t) 1) << 7)
-#define ERTS_SSI_AUX_WORK_CHECK_CHILDREN (((erts_aint32_t) 1) << 8)
-#define ERTS_SSI_AUX_WORK_SET_TMO (((erts_aint32_t) 1) << 9)
-#define ERTS_SSI_AUX_WORK_MSEG_CACHE_CHECK (((erts_aint32_t) 1) << 10)
-#define ERTS_SSI_AUX_WORK_CODE_IX_ACTIVATION (((erts_aint32_t) 1) << 11)
-#define ERTS_SSI_AUX_WORK_REAP_PORTS (((erts_aint32_t) 1) << 12)
-#define ERTS_SSI_AUX_WORK_FINISH_BP (((erts_aint32_t) 1) << 13)
+#define ERTS_SSI_AUX_WORK_DELAYED_AW_WAKEUP (((erts_aint32_t) 1) << 0)
+#define ERTS_SSI_AUX_WORK_DD (((erts_aint32_t) 1) << 1)
+#define ERTS_SSI_AUX_WORK_DD_THR_PRGR (((erts_aint32_t) 1) << 2)
+#define ERTS_SSI_AUX_WORK_FIX_ALLOC_DEALLOC (((erts_aint32_t) 1) << 3)
+#define ERTS_SSI_AUX_WORK_FIX_ALLOC_LOWER_LIM (((erts_aint32_t) 1) << 4)
+#define ERTS_SSI_AUX_WORK_THR_PRGR_LATER_OP (((erts_aint32_t) 1) << 5)
+#define ERTS_SSI_AUX_WORK_ASYNC_READY (((erts_aint32_t) 1) << 6)
+#define ERTS_SSI_AUX_WORK_ASYNC_READY_CLEAN (((erts_aint32_t) 1) << 7)
+#define ERTS_SSI_AUX_WORK_MISC_THR_PRGR (((erts_aint32_t) 1) << 8)
+#define ERTS_SSI_AUX_WORK_MISC (((erts_aint32_t) 1) << 9)
+#define ERTS_SSI_AUX_WORK_CHECK_CHILDREN (((erts_aint32_t) 1) << 10)
+#define ERTS_SSI_AUX_WORK_SET_TMO (((erts_aint32_t) 1) << 11)
+#define ERTS_SSI_AUX_WORK_MSEG_CACHE_CHECK (((erts_aint32_t) 1) << 12)
+#define ERTS_SSI_AUX_WORK_REAP_PORTS (((erts_aint32_t) 1) << 13)
typedef struct ErtsSchedulerSleepInfo_ ErtsSchedulerSleepInfo;
@@ -311,6 +291,7 @@ struct ErtsProcList_ {
Eterm pid;
Uint64 started_interval;
ErtsProcList* next;
+ ErtsProcList* prev;
};
typedef struct ErtsMiscOpList_ ErtsMiscOpList;
@@ -401,8 +382,8 @@ struct ErtsRunQueue_ {
struct {
ErtsRunQueueInfo info;
- struct port *start;
- struct port *end;
+ Port *start;
+ Port *end;
} ports;
};
@@ -439,6 +420,11 @@ typedef struct {
} ErtsSchedWallTime;
typedef struct {
+ int sched;
+ erts_aint32_t aux_work;
+} ErtsDelayedAuxWorkWakeupJob;
+
+typedef struct {
int sched_id;
ErtsSchedulerData *esdp;
ErtsSchedulerSleepInfo *ssi;
@@ -457,6 +443,10 @@ typedef struct {
void (*completed_callback)(void *);
void (*completed_arg)(void *);
} dd;
+ struct {
+ ErtsThrPrgrLaterOp *first;
+ ErtsThrPrgrLaterOp *last;
+ } later_op;
#endif
#ifdef ERTS_USE_ASYNC_READY_Q
struct {
@@ -469,15 +459,11 @@ typedef struct {
#endif
#ifdef ERTS_SMP
struct {
- Process* code_stager;
- ErtsThrPrgrVal thr_prgr;
- } code_ix_activation;
-#endif
-#ifdef ERTS_SMP
- struct {
- Process* stager;
- ErtsThrPrgrVal thr_prgr;
- } bp_ix_activation;
+ Uint64 next;
+ int *sched2jix;
+ int jix;
+ ErtsDelayedAuxWorkWakeupJob *job;
+ } delayed_wakeup;
#endif
} ErtsAuxWorkData;
@@ -507,16 +493,16 @@ struct ErtsSchedulerData_ {
ErtsSchedulerSleepInfo *ssi;
Process *current_process;
Uint no; /* Scheduler number */
- struct port *current_port;
+ Port *current_port;
ErtsRunQueue *run_queue;
int virtual_reds;
int cpu_id; /* >= 0 when bound */
ErtsAuxWorkData aux_work_data;
-
ErtsAtomCacheMap atom_cache_map;
ErtsSchedAllocData alloc_data;
+ Uint64 reductions;
ErtsSchedWallTime sched_wall_time;
#ifdef ERTS_DO_VERIFY_UNUSED_TEMP_ALLOC
@@ -732,8 +718,8 @@ struct ErtsPendingSuspend_ {
# define BIN_OLD_VHEAP(p) (p)->bin_old_vheap
struct process {
- Eterm id; /* The pid of this process
- (need to be first in struct) */
+ ErtsPTabElementCommon common; /* *Need* to be first in struct */
+
/* All fields in the PCB that differs between different heap
* architectures, have been moved to the end of this struct to
* make sure that as few offsets as possible differ. Different
@@ -777,12 +763,8 @@ struct process {
* Only valid for the current process.
*/
Uint32 rcount; /* suspend count */
- int prio; /* Priority of process */
int schedule_count; /* Times left to reschedule a low prio process */
Uint reds; /* No of reductions for this process */
- Eterm tracer_proc; /* If proc is traced, this is the tracer
- (can NOT be boxed) */
- Uint trace_flags; /* Trace flags (used to be in flags) */
Eterm group_leader; /* Pid in charge
(can be boxed) */
Uint flags; /* Trap exit, etc (no trace flags anymore) */
@@ -792,10 +774,6 @@ struct process {
Process *next; /* Pointer to next process in run queue */
- struct reg_proc *reg; /* NULL iff not registered */
- ErtsLink *nlinks;
- ErtsMonitor *monitors; /* The process monitors, both ends */
-
struct ErtsNodesMonitor_ *nodes_monitors;
ErtsSuspendMonitor *suspend_monitors; /* Processes suspended by
@@ -804,7 +782,10 @@ struct process {
ErlMessageQueue msg; /* Message queue */
- ErtsBifTimer *bif_timers; /* Bif timers aiming at this process */
+ union {
+ ErtsBifTimer *bif_timers; /* Bif timers aiming at this process */
+ void *terminate;
+ } u;
ProcDict *dictionary; /* Process dictionary, may be NULL */
@@ -829,7 +810,6 @@ struct process {
*/
Eterm parent; /* Pid of process that created this process. */
erts_approx_time_t approx_started; /* Time when started. */
- Uint64 started_interval;
/* This is the place, where all fields that differs between memory
* architectures, have gone to.
@@ -851,24 +831,15 @@ struct process {
Uint64 bin_old_vheap_sz; /* Virtual old heap block size for binaries */
Uint64 bin_old_vheap; /* Virtual old heap size for binaries */
- union {
-#ifdef ERTS_SMP
- ErtsSmpPTimer *ptimer;
-#else
- ErlTimer tm; /* Timer entry */
-#endif
- void *exit_data; /* Misc data referred during termination */
- } u;
-
erts_smp_atomic32_t state; /* Process state flags (see ERTS_PSFLG_*) */
#ifdef ERTS_SMP
+ ErlMessageInQueue msg_inq;
+ ErtsPendExit pending_exit;
erts_proc_lock_t lock;
ErtsSchedulerData *scheduler_data;
- ErlMessageInQueue msg_inq;
Eterm suspendee;
ErtsPendingSuspend *pending_suspenders;
- ErtsPendExit pending_exit;
erts_smp_atomic_t run_queue;
#ifdef HIPE
struct hipe_process_state_smp hipe_smp;
@@ -894,6 +865,8 @@ struct process {
#endif
};
+extern const Process erts_invalid_process;
+
#ifdef CHECK_FOR_HOLES
# define INIT_HOLE_CHECK(p) \
do { \
@@ -1018,8 +991,6 @@ Eterm* erts_heap_alloc(Process* p, Uint need, Uint xtra);
Eterm* erts_set_hole_marker(Eterm* ptr, Uint sz);
#endif
-extern erts_smp_rwmtx_t erts_proc_tab_rwmtx;
-extern erts_smp_atomic_t *erts_proc_tab;
extern Uint erts_default_process_flags;
extern erts_smp_rwmtx_t erts_cpu_bind_rwmtx;
/* If any of the erts_system_monitor_* variables are set (enabled),
@@ -1047,10 +1018,6 @@ struct erts_system_profile_flags_t {
unsigned int exclusive : 1;
};
extern struct erts_system_profile_flags_t erts_system_profile_flags;
-
-#define IS_TRACED(p) ( (p)->tracer_proc != NIL )
-#define ARE_TRACE_FLAGS_ON(p,tf) ( ((p)->trace_flags & (tf|F_SENSITIVE)) == (tf) )
-#define IS_TRACED_FL(p,tf) ( IS_TRACED(p) && ARE_TRACE_FLAGS_ON(p,tf) )
/* process flags */
#define F_HIBERNATE_SCHED (1 << 0) /* Schedule out after hibernate op */
@@ -1161,12 +1128,181 @@ Uint64 erts_step_proc_interval(void);
ErtsProcList *erts_proclist_create(Process *);
void erts_proclist_destroy(ErtsProcList *);
-int erts_proclist_same(ErtsProcList *, Process *);
+
+ERTS_GLB_INLINE int erts_proclist_same(ErtsProcList *, Process *);
+ERTS_GLB_INLINE void erts_proclist_store_first(ErtsProcList **, ErtsProcList *);
+ERTS_GLB_INLINE void erts_proclist_store_last(ErtsProcList **, ErtsProcList *);
+ERTS_GLB_INLINE ErtsProcList *erts_proclist_peek_first(ErtsProcList *);
+ERTS_GLB_INLINE ErtsProcList *erts_proclist_peek_last(ErtsProcList *);
+ERTS_GLB_INLINE ErtsProcList *erts_proclist_peek_next(ErtsProcList *, ErtsProcList *);
+ERTS_GLB_INLINE ErtsProcList *erts_proclist_peek_prev(ErtsProcList *, ErtsProcList *);
+ERTS_GLB_INLINE ErtsProcList *erts_proclist_fetch_first(ErtsProcList **);
+ERTS_GLB_INLINE ErtsProcList *erts_proclist_fetch_last(ErtsProcList **);
+ERTS_GLB_INLINE int erts_proclist_fetch(ErtsProcList **, ErtsProcList **);
+ERTS_GLB_INLINE void erts_proclist_remove(ErtsProcList **, ErtsProcList *);
+ERTS_GLB_INLINE int erts_proclist_is_empty(ErtsProcList *);
+ERTS_GLB_INLINE int erts_proclist_is_first(ErtsProcList *, ErtsProcList *);
+ERTS_GLB_INLINE int erts_proclist_is_last(ErtsProcList *, ErtsProcList *);
+
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+
+ERTS_GLB_INLINE int
+erts_proclist_same(ErtsProcList *plp, Process *p)
+{
+ return (plp->pid == p->common.id
+ && (plp->started_interval
+ == p->common.u.alive.started_interval));
+}
+
+ERTS_GLB_INLINE void erts_proclist_store_first(ErtsProcList **list,
+ ErtsProcList *element)
+{
+ if (!*list)
+ element->next = element->prev = element;
+ else {
+ element->prev = (*list)->prev;
+ element->next = *list;
+ element->prev->next = element;
+ element->next->prev = element;
+ }
+ *list = element;
+}
+
+ERTS_GLB_INLINE void erts_proclist_store_last(ErtsProcList **list,
+ ErtsProcList *element)
+{
+ if (!*list) {
+ element->next = element->prev = element;
+ *list = element;
+ }
+ else {
+ element->prev = (*list)->prev;
+ element->next = *list;
+ element->prev->next = element;
+ element->next->prev = element;
+ }
+}
+
+ERTS_GLB_INLINE ErtsProcList *erts_proclist_peek_first(ErtsProcList *list)
+{
+ return list;
+}
+
+ERTS_GLB_INLINE ErtsProcList *erts_proclist_peek_last(ErtsProcList *list)
+{
+ if (!list)
+ return NULL;
+ else
+ return list->prev;
+}
+
+ERTS_GLB_INLINE ErtsProcList *erts_proclist_peek_next(ErtsProcList *list,
+ ErtsProcList *element)
+{
+ ErtsProcList *next;
+ ASSERT(list && element);
+ next = element->next;
+ return list == next ? NULL : next;
+}
+
+ERTS_GLB_INLINE ErtsProcList *erts_proclist_peek_prev(ErtsProcList *list,
+ ErtsProcList *element)
+{
+ ErtsProcList *prev;
+ ASSERT(list && element);
+ prev = element->prev;
+ return list == element ? NULL : prev;
+}
+
+ERTS_GLB_INLINE ErtsProcList *erts_proclist_fetch_first(ErtsProcList **list)
+{
+ if (!*list)
+ return NULL;
+ else {
+ ErtsProcList *res = *list;
+ if (res == *list)
+ *list = NULL;
+ else
+ *list = res->next;
+ res->next->prev = res->prev;
+ res->prev->next = res->next;
+ return res;
+ }
+}
+
+ERTS_GLB_INLINE ErtsProcList *erts_proclist_fetch_last(ErtsProcList **list)
+{
+ if (!*list)
+ return NULL;
+ else {
+ ErtsProcList *res = (*list)->prev;
+ if (res == *list)
+ *list = NULL;
+ res->next->prev = res->prev;
+ res->prev->next = res->next;
+ return res;
+ }
+}
+
+ERTS_GLB_INLINE int erts_proclist_fetch(ErtsProcList **list_first,
+ ErtsProcList **list_last)
+{
+ if (!*list_first) {
+ if (list_last)
+ *list_last = NULL;
+ return 0;
+ }
+ else {
+ if (list_last)
+ *list_last = (*list_first)->prev;
+ (*list_first)->prev->next = NULL;
+ (*list_first)->prev = NULL;
+ return !0;
+ }
+}
+
+ERTS_GLB_INLINE void erts_proclist_remove(ErtsProcList **list,
+ ErtsProcList *element)
+{
+ ASSERT(list && *list);
+ if (*list == element) {
+ *list = element->next;
+ if (*list == element)
+ *list = NULL;
+ }
+ element->next->prev = element->prev;
+ element->prev->next = element->next;
+}
+
+ERTS_GLB_INLINE int erts_proclist_is_empty(ErtsProcList *list)
+{
+ return list == NULL;
+}
+
+ERTS_GLB_INLINE int erts_proclist_is_first(ErtsProcList *list,
+ ErtsProcList *element)
+{
+ ASSERT(list && element);
+ return list == element;
+}
+
+ERTS_GLB_INLINE int erts_proclist_is_last(ErtsProcList *list,
+ ErtsProcList *element)
+{
+ ASSERT(list && element);
+ return list->prev == element;
+}
+
+#endif
int erts_sched_set_wakeup_other_thresold(char *str);
int erts_sched_set_wakeup_other_type(char *str);
int erts_sched_set_busy_wait_threshold(char *str);
+void erts_schedule_thr_prgr_later_op(void (*)(void *),
+ void *,
+ ErtsThrPrgrLaterOp *);
+
#if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK)
int erts_dbg_check_halloc_lock(Process *p);
#endif
@@ -1207,7 +1343,7 @@ void erts_schedule_multi_misc_aux_work(int ignore_self,
erts_aint32_t erts_set_aux_work_timeout(int, erts_aint32_t, int);
void erts_sched_notify_check_cpu_bind(void);
Uint erts_active_schedulers(void);
-void erts_init_process(int);
+void erts_init_process(int, int);
Eterm erts_process_status(Process *, ErtsProcLocks, Process *, Eterm);
Uint erts_run_queues_len(Uint *);
void erts_add_to_runq(Process *);
@@ -1226,7 +1362,6 @@ void set_timer(Process*, Uint);
void cancel_timer(Process*);
/* Begin System profile */
Uint erts_runnable_process_count(void);
-Uint erts_process_count(void);
/* End System profile */
void erts_init_empty_process(Process *p);
void erts_cleanup_empty_process(Process* p);
@@ -1250,7 +1385,7 @@ Eterm erts_sched_stat_term(Process *p, int total);
void erts_free_proc(Process *);
-void erts_suspend(Process*, ErtsProcLocks, struct port*);
+void erts_suspend(Process*, ErtsProcLocks, Port*);
void erts_resume(Process*, ErtsProcLocks);
int erts_resume_processes(ErtsProcList *);
@@ -1275,9 +1410,6 @@ void erts_deep_process_dump(int, void *);
Eterm erts_get_reader_groups_map(Process *c_p);
Eterm erts_debug_reader_groups_map(Process *c_p, int groups);
-Sint erts_test_next_pid(int, Uint);
-Eterm erts_debug_processes(Process *c_p);
-Eterm erts_debug_processes_bif_info(Process *c_p);
Uint erts_debug_nbalance(void);
int erts_debug_wait_deallocations(Process *c_p);
@@ -1589,7 +1721,7 @@ ERTS_GLB_INLINE
Eterm erts_get_current_pid(void)
{
Process *proc = erts_get_current_process();
- return proc ? proc->id : THE_NON_VALUE;
+ return proc ? proc->common.id : THE_NON_VALUE;
}
ERTS_GLB_INLINE
@@ -1798,10 +1930,10 @@ extern int erts_disable_proc_not_running_opt;
/* Minimum NUMBER of processes for a small system to start */
-#ifdef ERTS_SMP
+#define ERTS_MIN_PROCESSES 1024
+#if defined(ERTS_SMP) && ERTS_MIN_PROCESSES < ERTS_NO_OF_PIX_LOCKS
+#undef ERTS_MIN_PROCESSES
#define ERTS_MIN_PROCESSES ERTS_NO_OF_PIX_LOCKS
-#else
-#define ERTS_MIN_PROCESSES 16
#endif
void erts_smp_notify_inc_runq(ErtsRunQueue *runq);
diff --git a/erts/emulator/beam/erl_process_dict.c b/erts/emulator/beam/erl_process_dict.c
index 93466da3aa..bf384c66e1 100644
--- a/erts/emulator/beam/erl_process_dict.c
+++ b/erts/emulator/beam/erl_process_dict.c
@@ -360,7 +360,7 @@ static void pd_hash_erase(Process *p, Eterm id, Eterm *ret)
erts_fprintf(stderr,
"Process dictionary for process %T is broken, trying to "
"display term found in line %d:\n"
- "%T\n", p->id, __LINE__, old);
+ "%T\n", p->common.id, __LINE__, old);
#endif
erl_exit(1, "Damaged process dictionary found during erase/1.");
}
@@ -405,7 +405,7 @@ Eterm erts_pd_hash_get(Process *p, Eterm id)
erts_fprintf(stderr,
"Process dictionary for process %T is broken, trying to "
"display term found in line %d:\n"
- "%T\n", p->id, __LINE__, tmp);
+ "%T\n", p->common.id, __LINE__, tmp);
#endif
erl_exit(1, "Damaged process dictionary found during get/1.");
}
@@ -614,7 +614,7 @@ static Eterm pd_hash_put(Process *p, Eterm id, Eterm value)
erts_fprintf(stderr,
"Process dictionary for process %T is broken, trying to "
"display term found in line %d:\n"
- "%T\n", p->id, __LINE__, old);
+ "%T\n", p->common.id, __LINE__, old);
#endif
erl_exit(1, "Damaged process dictionary found during put/2.");
diff --git a/erts/emulator/beam/erl_process_dump.c b/erts/emulator/beam/erl_process_dump.c
index 964dc1ae3e..ba74dfd6a1 100644
--- a/erts/emulator/beam/erl_process_dump.c
+++ b/erts/emulator/beam/erl_process_dump.c
@@ -60,11 +60,11 @@ extern BeamInstr beam_continue_exit[];
void
erts_deep_process_dump(int to, void *to_arg)
{
- int i;
+ int i, max = erts_ptab_max(&erts_proc);
all_binaries = NULL;
- for (i = 0; i < erts_max_processes; i++) {
+ for (i = 0; i < max; i++) {
Process *p = erts_pix2proc(i);
if (p && p->i != ENULL) {
erts_aint32_t state = erts_smp_atomic32_read_acqb(&p->state);
@@ -85,8 +85,8 @@ dump_process_info(int to, void *to_arg, Process *p)
ERTS_SMP_MSGQ_MV_INQ2PRIVQ(p);
- if ((p->trace_flags & F_SENSITIVE) == 0 && p->msg.first) {
- erts_print(to, to_arg, "=proc_messages:%T\n", p->id);
+ if ((ERTS_TRACE_FLAGS(p) & F_SENSITIVE) == 0 && p->msg.first) {
+ erts_print(to, to_arg, "=proc_messages:%T\n", p->common.id);
for (mp = p->msg.first; mp != NULL; mp = mp->next) {
Eterm mesg = ERL_MESSAGE_TERM(mp);
if (is_value(mesg))
@@ -100,21 +100,21 @@ dump_process_info(int to, void *to_arg, Process *p)
}
}
- if ((p->trace_flags & F_SENSITIVE) == 0) {
+ if ((ERTS_TRACE_FLAGS(p) & F_SENSITIVE) == 0) {
if (p->dictionary) {
- erts_print(to, to_arg, "=proc_dictionary:%T\n", p->id);
+ erts_print(to, to_arg, "=proc_dictionary:%T\n", p->common.id);
erts_deep_dictionary_dump(to, to_arg,
p->dictionary, dump_element_nl);
}
}
- if ((p->trace_flags & F_SENSITIVE) == 0) {
- erts_print(to, to_arg, "=proc_stack:%T\n", p->id);
+ if ((ERTS_TRACE_FLAGS(p) & F_SENSITIVE) == 0) {
+ erts_print(to, to_arg, "=proc_stack:%T\n", p->common.id);
for (sp = p->stop; sp < STACK_START(p); sp++) {
yreg = stack_element_dump(to, to_arg, p, sp, yreg);
}
- erts_print(to, to_arg, "=proc_heap:%T\n", p->id);
+ erts_print(to, to_arg, "=proc_heap:%T\n", p->common.id);
for (sp = p->stop; sp < STACK_START(p); sp++) {
Eterm term = *sp;
@@ -323,7 +323,7 @@ heap_dump(int to, void *to_arg, Eterm x)
int i;
GET_DOUBLE_DATA((ptr+1), f);
- i = sys_double_to_chars(f.fd, (char*) sbuf);
+ i = sys_double_to_chars(f.fd, (char*) sbuf, sizeof(sbuf));
sys_memset(sbuf+i, 0, 31-i);
erts_print(to, to_arg, "F%X:%s\n", i, sbuf);
*ptr = OUR_NIL;
diff --git a/erts/emulator/beam/erl_process_lock.c b/erts/emulator/beam/erl_process_lock.c
index bae2b383a4..2db5df06b4 100644
--- a/erts/emulator/beam/erl_process_lock.c
+++ b/erts/emulator/beam/erl_process_lock.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2007-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2007-2012. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -66,8 +66,7 @@
#endif
#include "erl_process.h"
-
-const Process erts_proc_lock_busy = {ERTS_INVALID_PID};
+#include "erl_thr_progress.h"
#ifdef ERTS_SMP
@@ -92,16 +91,6 @@ static void check_queue(erts_proc_lock_t *lck);
#error "The size of the 'uflgs' field of the erts_tse_t type is too small"
#endif
-struct erts_proc_lock_queues_t_ {
- erts_proc_lock_queues_t *next;
- erts_tse_t *queue[ERTS_PROC_LOCK_MAX_BIT+1];
-};
-
-static erts_proc_lock_queues_t zeroqs = {0};
-
-static erts_smp_spinlock_t qs_lock;
-static erts_proc_lock_queues_t *queue_free_list;
-
static int proc_lock_spin_count;
static int aux_thr_proc_lock_spin_count;
@@ -134,8 +123,6 @@ erts_init_proc_lock(int cpus)
#endif
}
#if ERTS_PROC_LOCK_OWN_IMPL
- erts_smp_spinlock_init(&qs_lock, "proc_lck_qs_alloc");
- queue_free_list = NULL;
erts_thr_install_exit_handler(cleanup_tse);
if (cpus > 1) {
proc_lock_spin_count = ERTS_PROC_LOCK_SPIN_COUNT_BASE;
@@ -165,16 +152,7 @@ erts_init_proc_lock(int cpus)
#if ERTS_PROC_LOCK_OWN_IMPL
#ifdef ERTS_ENABLE_LOCK_CHECK
-static void
-check_unused_tse(erts_tse_t *wtr)
-{
- int i;
- erts_proc_lock_queues_t *queues = wtr->udata;
- ERTS_LC_ASSERT(wtr->uflgs == 0);
- for (i = 0; i <= ERTS_PROC_LOCK_MAX_BIT; i++)
- ERTS_LC_ASSERT(!queues->queue[i]);
-}
-#define CHECK_UNUSED_TSE(W) check_unused_tse((W))
+#define CHECK_UNUSED_TSE(W) ERTS_LC_ASSERT((W)->uflgs == 0)
#else
#define CHECK_UNUSED_TSE(W)
#endif
@@ -183,49 +161,14 @@ static ERTS_INLINE erts_tse_t *
tse_fetch(erts_pix_lock_t *pix_lock)
{
erts_tse_t *tse = erts_tse_fetch();
- if (!tse->udata) {
- erts_proc_lock_queues_t *qs;
-#if ERTS_PROC_LOCK_SPINLOCK_IMPL && !ERTS_PROC_LOCK_ATOMIC_IMPL
- if (pix_lock)
- erts_pix_unlock(pix_lock);
-#endif
- erts_smp_spin_lock(&qs_lock);
- qs = queue_free_list;
- if (qs) {
- queue_free_list = queue_free_list->next;
- erts_smp_spin_unlock(&qs_lock);
- }
- else {
- erts_smp_spin_unlock(&qs_lock);
- qs = erts_alloc(ERTS_ALC_T_PROC_LCK_QS,
- sizeof(erts_proc_lock_queues_t));
- sys_memcpy((void *) qs,
- (void *) &zeroqs,
- sizeof(erts_proc_lock_queues_t));
- }
- tse->udata = qs;
-#if ERTS_PROC_LOCK_SPINLOCK_IMPL && !ERTS_PROC_LOCK_ATOMIC_IMPL
- if (pix_lock)
- erts_pix_lock(pix_lock);
-#endif
- }
tse->uflgs = 0;
return tse;
}
static ERTS_INLINE void
-tse_return(erts_tse_t *tse, int force_free_q)
+tse_return(erts_tse_t *tse)
{
CHECK_UNUSED_TSE(tse);
- if (force_free_q || erts_tse_is_tmp(tse)) {
- erts_proc_lock_queues_t *qs = tse->udata;
- ASSERT(qs);
- erts_smp_spin_lock(&qs_lock);
- qs->next = queue_free_list;
- queue_free_list = qs;
- erts_smp_spin_unlock(&qs_lock);
- tse->udata = NULL;
- }
erts_tse_return(tse);
}
@@ -233,55 +176,49 @@ static void
cleanup_tse(void)
{
erts_tse_t *tse = erts_tse_fetch();
- if (tse) {
- if (tse->udata)
- tse_return(tse, 1);
- else
- erts_tse_return(tse);
- }
+ if (tse)
+ erts_tse_return(tse);
}
/*
* Waiters are queued in a circular double linked list;
- * where qs->queue[lock_ix] is the first waiter in queue, and
- * qs->queue[lock_ix]->prev is the last waiter in queue.
+ * where lck->queue[lock_ix] is the first waiter in queue, and
+ * lck->queue[lock_ix]->prev is the last waiter in queue.
*/
static ERTS_INLINE void
-enqueue_waiter(erts_proc_lock_queues_t *qs,
- int ix,
- erts_tse_t *wtr)
+enqueue_waiter(erts_proc_lock_t *lck, int ix, erts_tse_t *wtr)
{
- if (!qs->queue[ix]) {
- qs->queue[ix] = wtr;
+ if (!lck->queue[ix]) {
+ lck->queue[ix] = wtr;
wtr->next = wtr;
wtr->prev = wtr;
}
else {
- ERTS_LC_ASSERT(qs->queue[ix]->next && qs->queue[ix]->prev);
- wtr->next = qs->queue[ix];
- wtr->prev = qs->queue[ix]->prev;
+ ERTS_LC_ASSERT(lck->queue[ix]->next && lck->queue[ix]->prev);
+ wtr->next = lck->queue[ix];
+ wtr->prev = lck->queue[ix]->prev;
wtr->prev->next = wtr;
- qs->queue[ix]->prev = wtr;
+ lck->queue[ix]->prev = wtr;
}
}
static erts_tse_t *
-dequeue_waiter(erts_proc_lock_queues_t *qs, int ix)
+dequeue_waiter(erts_proc_lock_t *lck, int ix)
{
- erts_tse_t *wtr = qs->queue[ix];
- ERTS_LC_ASSERT(qs->queue[ix]);
+ erts_tse_t *wtr = lck->queue[ix];
+ ERTS_LC_ASSERT(lck->queue[ix]);
if (wtr->next == wtr) {
- ERTS_LC_ASSERT(qs->queue[ix]->prev == wtr);
- qs->queue[ix] = NULL;
+ ERTS_LC_ASSERT(lck->queue[ix]->prev == wtr);
+ lck->queue[ix] = NULL;
}
else {
ERTS_LC_ASSERT(wtr->next != wtr);
ERTS_LC_ASSERT(wtr->prev != wtr);
wtr->next->prev = wtr->prev;
wtr->prev->next = wtr->next;
- qs->queue[ix] = wtr->next;
+ lck->queue[ix] = wtr->next;
}
return wtr;
}
@@ -302,19 +239,18 @@ try_aquire(erts_proc_lock_t *lck, erts_tse_t *wtr)
ErtsProcLocks locks = wtr->uflgs;
int lock_no;
- ERTS_LC_ASSERT(lck->queues);
ERTS_LC_ASSERT(got_locks != locks);
for (lock_no = 0; lock_no <= ERTS_PROC_LOCK_MAX_BIT; lock_no++) {
ErtsProcLocks lock = ((ErtsProcLocks) 1) << lock_no;
if (locks & lock) {
ErtsProcLocks wflg, old_lflgs;
- if (lck->queues->queue[lock_no]) {
+ if (lck->queue[lock_no]) {
/* Others already waiting */
enqueue:
ERTS_LC_ASSERT(ERTS_PROC_LOCK_FLGS_READ_(lck)
& (lock << ERTS_PROC_LOCK_WAITER_SHIFT));
- enqueue_waiter(lck->queues, lock_no, wtr);
+ enqueue_waiter(lck, lock_no, wtr);
break;
}
wflg = lock << ERTS_PROC_LOCK_WAITER_SHIFT;
@@ -366,7 +302,6 @@ transfer_locks(Process *p,
for (lock_no = 0; tlocks && lock_no <= ERTS_PROC_LOCK_MAX_BIT; lock_no++) {
ErtsProcLocks lock = ((ErtsProcLocks) 1) << lock_no;
if (tlocks & lock) {
- erts_proc_lock_queues_t *qs = p->lock.queues;
/* Transfer lock */
#ifdef ERTS_ENABLE_LOCK_CHECK
tlocks &= ~lock;
@@ -374,9 +309,9 @@ transfer_locks(Process *p,
ERTS_LC_ASSERT(ERTS_PROC_LOCK_FLGS_READ_(&p->lock)
& (lock << ERTS_PROC_LOCK_WAITER_SHIFT));
transferred++;
- wtr = dequeue_waiter(qs, lock_no);
+ wtr = dequeue_waiter(&p->lock, lock_no);
ERTS_LC_ASSERT(wtr);
- if (!qs->queue[lock_no])
+ if (!p->lock.queue[lock_no])
unset_waiter |= lock;
ERTS_LC_ASSERT(wtr->uflgs & lock);
wtr->uflgs &= ~lock;
@@ -463,9 +398,8 @@ wait_for_locks(Process *p,
ErtsProcLocks need_locks,
ErtsProcLocks olflgs)
{
- erts_pix_lock_t *pix_lock = pixlck ? pixlck : ERTS_PID2PIXLOCK(p->id);
+ erts_pix_lock_t *pix_lock = pixlck ? pixlck : ERTS_PID2PIXLOCK(p->common.id);
erts_tse_t *wtr;
- erts_proc_lock_queues_t *qs;
/* Acquire a waiter object on which this thread can wait. */
wtr = tse_fetch(pix_lock);
@@ -481,18 +415,6 @@ wait_for_locks(Process *p,
ERTS_LC_ASSERT(erts_lc_pix_lock_is_locked(pix_lock));
- qs = wtr->udata;
- ASSERT(qs);
- /* Provide the process with waiter queues, if it doesn't have one. */
- if (!p->lock.queues) {
- qs->next = NULL;
- p->lock.queues = qs;
- }
- else {
- qs->next = p->lock.queues->next;
- p->lock.queues->next = qs;
- }
-
#ifdef ERTS_PROC_LOCK_HARD_DEBUG
check_queue(&p->lock);
#endif
@@ -506,7 +428,9 @@ wait_for_locks(Process *p,
check_queue(&p->lock);
#endif
- if (wtr->uflgs) {
+ if (wtr->uflgs == 0)
+ erts_pix_unlock(pix_lock);
+ else {
/* We didn't get them all; need to wait... */
ASSERT((wtr->uflgs & ~ERTS_PROC_LOCKS_ALL) == 0);
@@ -531,28 +455,12 @@ wait_for_locks(Process *p,
} while (res != 0);
}
- erts_pix_lock(pix_lock);
-
ASSERT(wtr->uflgs == 0);
}
- /* Recover some queues to store in the waiter. */
- ERTS_LC_ASSERT(p->lock.queues);
- if (p->lock.queues->next) {
- qs = p->lock.queues->next;
- p->lock.queues->next = qs->next;
- }
- else {
- qs = p->lock.queues;
- p->lock.queues = NULL;
- }
- wtr->udata = qs;
-
- erts_pix_unlock(pix_lock);
-
ERTS_LC_ASSERT(locks == (ERTS_PROC_LOCK_FLGS_READ_(&p->lock) & locks));
- tse_return(wtr, 0);
+ tse_return(wtr);
}
/*
@@ -644,7 +552,7 @@ erts_proc_unlock_failed(Process *p,
erts_pix_lock_t *pixlck,
ErtsProcLocks wait_locks)
{
- erts_pix_lock_t *pix_lock = pixlck ? pixlck : ERTS_PID2PIXLOCK(p->id);
+ erts_pix_lock_t *pix_lock = pixlck ? pixlck : ERTS_PID2PIXLOCK(p->common.id);
#if ERTS_PROC_LOCK_ATOMIC_IMPL
erts_pix_lock(pix_lock);
@@ -659,7 +567,7 @@ void
erts_proc_lock_prepare_proc_lock_waiter(void)
{
#if ERTS_PROC_LOCK_OWN_IMPL
- tse_return(tse_fetch(NULL), 0);
+ tse_return(tse_fetch(NULL));
#endif
}
@@ -671,7 +579,7 @@ erts_proc_lock_prepare_proc_lock_waiter(void)
*/
static void
-proc_safelock(int is_sched,
+proc_safelock(int is_managed,
Process *a_proc,
ErtsProcLocks a_have_locks,
ErtsProcLocks a_need_locks,
@@ -694,40 +602,40 @@ proc_safelock(int is_sched,
* Locks with the same lock order should be locked on p1 before p2.
*/
if (a_proc) {
- if (a_proc->id < b_proc->id) {
+ if (a_proc->common.id < b_proc->common.id) {
p1 = a_proc;
#ifdef ERTS_ENABLE_LOCK_CHECK
- pid1 = a_proc->id;
+ pid1 = a_proc->common.id;
#endif
need_locks1 = a_need_locks;
have_locks1 = a_have_locks;
p2 = b_proc;
#ifdef ERTS_ENABLE_LOCK_CHECK
- pid2 = b_proc->id;
+ pid2 = b_proc->common.id;
#endif
need_locks2 = b_need_locks;
have_locks2 = b_have_locks;
}
- else if (a_proc->id > b_proc->id) {
+ else if (a_proc->common.id > b_proc->common.id) {
p1 = b_proc;
#ifdef ERTS_ENABLE_LOCK_CHECK
- pid1 = b_proc->id;
+ pid1 = b_proc->common.id;
#endif
need_locks1 = b_need_locks;
have_locks1 = b_have_locks;
p2 = a_proc;
#ifdef ERTS_ENABLE_LOCK_CHECK
- pid2 = a_proc->id;
+ pid2 = a_proc->common.id;
#endif
need_locks2 = a_need_locks;
have_locks2 = a_have_locks;
}
else {
ERTS_LC_ASSERT(a_proc == b_proc);
- ERTS_LC_ASSERT(a_proc->id == b_proc->id);
+ ERTS_LC_ASSERT(a_proc->common.id == b_proc->common.id);
p1 = a_proc;
#ifdef ERTS_ENABLE_LOCK_CHECK
- pid1 = a_proc->id;
+ pid1 = a_proc->common.id;
#endif
need_locks1 = a_need_locks | b_need_locks;
have_locks1 = a_have_locks | b_have_locks;
@@ -742,7 +650,7 @@ proc_safelock(int is_sched,
else {
p1 = b_proc;
#ifdef ERTS_ENABLE_LOCK_CHECK
- pid1 = b_proc->id;
+ pid1 = b_proc->common.id;
#endif
need_locks1 = b_need_locks;
have_locks1 = b_have_locks;
@@ -797,7 +705,7 @@ proc_safelock(int is_sched,
if (unlock_locks) {
have_locks1 &= ~unlock_locks;
need_locks1 |= unlock_locks;
- if (!is_sched && !have_locks1) {
+ if (!is_managed && !have_locks1) {
refc1 = 1;
erts_smp_proc_inc_refc(p1);
}
@@ -807,7 +715,7 @@ proc_safelock(int is_sched,
if (unlock_locks) {
have_locks2 &= ~unlock_locks;
need_locks2 |= unlock_locks;
- if (!is_sched && !have_locks2) {
+ if (!is_managed && !have_locks2) {
refc2 = 1;
erts_smp_proc_inc_refc(p2);
}
@@ -888,7 +796,7 @@ proc_safelock(int is_sched,
}
#endif
- if (!is_sched) {
+ if (!is_managed) {
if (refc1)
erts_smp_proc_dec_refc(p1);
if (refc2)
@@ -921,7 +829,7 @@ erts_pid2proc_opt(Process *c_p,
int flags)
{
Process *dec_refc_proc = NULL;
- int need_ptl;
+ ErtsThrPrgrDelayHandle dhndl;
ErtsProcLocks need_locks;
Uint pix;
Process *proc;
@@ -944,8 +852,8 @@ erts_pid2proc_opt(Process *c_p,
ERTS_LC_ASSERT((pid_need_locks & ERTS_PROC_LOCKS_ALL) == pid_need_locks);
need_locks = pid_need_locks;
- if (c_p && c_p->id == pid) {
- ASSERT(c_p->id != ERTS_INVALID_PID);
+ if (c_p && c_p->common.id == pid) {
+ ASSERT(c_p->common.id != ERTS_INVALID_PID);
ASSERT(c_p == erts_pix2proc(pix));
if (!(flags & ERTS_P2P_FLG_ALLOW_OTHER_X)
@@ -959,15 +867,12 @@ erts_pid2proc_opt(Process *c_p,
}
}
- need_ptl = !erts_get_scheduler_id();
-
- if (need_ptl)
- erts_smp_rwmtx_rwlock(&erts_proc_tab_rwmtx);
+ dhndl = erts_thr_progress_unmanaged_delay();
- proc = (Process *) erts_smp_atomic_read_ddrb(&erts_proc.tab[pix]);
+ proc = (Process *) erts_ptab_pix2intptr_ddrb(&erts_proc, pix);
if (proc) {
- if (proc->id != pid)
+ if (proc->common.id != pid)
proc = NULL;
else if (!need_locks) {
if (flags & ERTS_P2P_FLG_SMP_INC_REFC)
@@ -1026,6 +931,7 @@ erts_pid2proc_opt(Process *c_p,
if (flags & ERTS_P2P_FLG_TRY_LOCK)
proc = ERTS_PROC_LOCK_BUSY;
else {
+ int managed;
if (flags & ERTS_P2P_FLG_SMP_INC_REFC)
erts_smp_proc_inc_refc(proc);
@@ -1033,14 +939,21 @@ erts_pid2proc_opt(Process *c_p,
erts_lcnt_proc_lock_unaquire(&proc->lock, lcnt_locks);
#endif
- if (need_ptl) {
+ managed = dhndl == ERTS_THR_PRGR_DHANDLE_MANAGED;
+ if (!managed) {
erts_smp_proc_inc_refc(proc);
+ erts_thr_progress_unmanaged_continue(dhndl);
dec_refc_proc = proc;
- erts_smp_rwmtx_rwunlock(&erts_proc_tab_rwmtx);
- need_ptl = 0;
+
+ /*
+ * We don't want to call
+ * erts_thr_progress_unmanaged_continue()
+ * again.
+ */
+ dhndl = ERTS_THR_PRGR_DHANDLE_MANAGED;
}
- proc_safelock(!need_ptl,
+ proc_safelock(managed,
c_p,
c_p_have_locks,
c_p_have_locks,
@@ -1052,8 +965,8 @@ erts_pid2proc_opt(Process *c_p,
}
}
- if (need_ptl)
- erts_smp_rwmtx_rwunlock(&erts_proc_tab_rwmtx);
+ if (dhndl != ERTS_THR_PRGR_DHANDLE_MANAGED)
+ erts_thr_progress_unmanaged_continue(dhndl);
if (need_locks
&& proc
@@ -1061,7 +974,7 @@ erts_pid2proc_opt(Process *c_p,
&& (!(flags & ERTS_P2P_FLG_ALLOW_OTHER_X)
? ERTS_PROC_IS_EXITING(proc)
: (proc
- != (Process *) erts_smp_atomic_read_nob(&erts_proc.tab[pix])))) {
+ != (Process *) erts_ptab_pix2intptr_nob(&erts_proc, pix)))) {
erts_smp_proc_unlock(proc, need_locks);
@@ -1089,6 +1002,7 @@ void
erts_proc_lock_init(Process *p)
{
#if ERTS_PROC_LOCK_OWN_IMPL
+ int i;
/* We always start with all locks locked */
#if ERTS_PROC_LOCK_ATOMIC_IMPL
erts_smp_atomic32_init_nob(&p->lock.flags,
@@ -1096,27 +1010,28 @@ erts_proc_lock_init(Process *p)
#else
p->lock.flags = ERTS_PROC_LOCKS_ALL;
#endif
- p->lock.queues = NULL;
+ for (i = 0; i <= ERTS_PROC_LOCK_MAX_BIT; i++)
+ p->lock.queue[i] = NULL;
#ifdef ERTS_ENABLE_LOCK_CHECK
erts_proc_lc_trylock(p, ERTS_PROC_LOCKS_ALL, 1);
#endif
#elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL
- erts_mtx_init_x(&p->lock.main, "proc_main", p->id);
+ erts_mtx_init_x(&p->lock.main, "proc_main", p->common.id);
ethr_mutex_lock(&p->lock.main.mtx);
#ifdef ERTS_ENABLE_LOCK_CHECK
erts_lc_trylock(1, &p->lock.main.lc);
#endif
- erts_mtx_init_x(&p->lock.link, "proc_link", p->id);
+ erts_mtx_init_x(&p->lock.link, "proc_link", p->common.id);
ethr_mutex_lock(&p->lock.link.mtx);
#ifdef ERTS_ENABLE_LOCK_CHECK
erts_lc_trylock(1, &p->lock.link.lc);
#endif
- erts_mtx_init_x(&p->lock.msgq, "proc_msgq", p->id);
+ erts_mtx_init_x(&p->lock.msgq, "proc_msgq", p->common.id);
ethr_mutex_lock(&p->lock.msgq.mtx);
#ifdef ERTS_ENABLE_LOCK_CHECK
erts_lc_trylock(1, &p->lock.msgq.lc);
#endif
- erts_mtx_init_x(&p->lock.status, "proc_status", p->id);
+ erts_mtx_init_x(&p->lock.status, "proc_status", p->common.id);
ethr_mutex_lock(&p->lock.status.mtx);
#ifdef ERTS_ENABLE_LOCK_CHECK
erts_lc_trylock(1, &p->lock.status.lc);
@@ -1124,11 +1039,8 @@ erts_proc_lock_init(Process *p)
#endif
erts_atomic32_init_nob(&p->lock.refc, 1);
#ifdef ERTS_PROC_LOCK_DEBUG
- {
- int i;
- for (i = 0; i <= ERTS_PROC_LOCK_MAX_BIT; i++)
- erts_smp_atomic32_init_nob(&p->lock.locked[i], (erts_aint32_t) 1);
- }
+ for (i = 0; i <= ERTS_PROC_LOCK_MAX_BIT; i++)
+ erts_smp_atomic32_init_nob(&p->lock.locked[i], (erts_aint32_t) 1);
#endif
#ifdef ERTS_ENABLE_LOCK_COUNT
erts_lcnt_proc_lock_init(p);
@@ -1156,11 +1068,11 @@ erts_proc_lock_fin(Process *p)
#if ERTS_PROC_LOCK_OWN_IMPL && defined(ERTS_ENABLE_LOCK_COUNT)
void erts_lcnt_proc_lock_init(Process *p) {
if (erts_lcnt_rt_options & ERTS_LCNT_OPT_PROCLOCK) {
- if (p->id != ERTS_INVALID_PID) {
- erts_lcnt_init_lock_x(&(p->lock.lcnt_main), "proc_main", ERTS_LCNT_LT_PROCLOCK, p->id);
- erts_lcnt_init_lock_x(&(p->lock.lcnt_msgq), "proc_msgq", ERTS_LCNT_LT_PROCLOCK, p->id);
- erts_lcnt_init_lock_x(&(p->lock.lcnt_link), "proc_link", ERTS_LCNT_LT_PROCLOCK, p->id);
- erts_lcnt_init_lock_x(&(p->lock.lcnt_status), "proc_status", ERTS_LCNT_LT_PROCLOCK, p->id);
+ if (p->common.id != ERTS_INVALID_PID) {
+ erts_lcnt_init_lock_x(&(p->lock.lcnt_main), "proc_main", ERTS_LCNT_LT_PROCLOCK, p->common.id);
+ erts_lcnt_init_lock_x(&(p->lock.lcnt_msgq), "proc_msgq", ERTS_LCNT_LT_PROCLOCK, p->common.id);
+ erts_lcnt_init_lock_x(&(p->lock.lcnt_link), "proc_link", ERTS_LCNT_LT_PROCLOCK, p->common.id);
+ erts_lcnt_init_lock_x(&(p->lock.lcnt_status), "proc_status", ERTS_LCNT_LT_PROCLOCK, p->common.id);
} else {
erts_lcnt_init_lock(&(p->lock.lcnt_main), "proc_main", ERTS_LCNT_LT_PROCLOCK);
erts_lcnt_init_lock(&(p->lock.lcnt_msgq), "proc_msgq", ERTS_LCNT_LT_PROCLOCK);
@@ -1268,11 +1180,12 @@ void erts_lcnt_proc_trylock(erts_proc_lock_t *lock, ErtsProcLocks locks, int res
}
-void erts_lcnt_enable_proc_lock_count(int enable) {
- int i;
+void erts_lcnt_enable_proc_lock_count(int enable)
+{
+ int i, max = erts_ptab_max(&erts_proc);
- for (i = 0; i < erts_max_processes; ++i) {
- Process* p = process_tab[i];
+ for (i = 0; i < max; ++i) {
+ Process* p = erts_pix2proc(i);
if (p) {
if (enable) {
if (!ERTS_LCNT_LOCK_TYPE(&(p->lock.lcnt_main))) {
@@ -1300,7 +1213,7 @@ void
erts_proc_lc_lock(Process *p, ErtsProcLocks locks)
{
erts_lc_lock_t lck = ERTS_LC_LOCK_INIT(-1,
- p->id,
+ p->common.id,
ERTS_LC_FLG_LT_PROCLOCK);
if (locks & ERTS_PROC_LOCK_MAIN) {
lck.id = lc_id.proc_lock_main;
@@ -1324,7 +1237,7 @@ void
erts_proc_lc_trylock(Process *p, ErtsProcLocks locks, int locked)
{
erts_lc_lock_t lck = ERTS_LC_LOCK_INIT(-1,
- p->id,
+ p->common.id,
ERTS_LC_FLG_LT_PROCLOCK);
if (locks & ERTS_PROC_LOCK_MAIN) {
lck.id = lc_id.proc_lock_main;
@@ -1348,7 +1261,7 @@ void
erts_proc_lc_unlock(Process *p, ErtsProcLocks locks)
{
erts_lc_lock_t lck = ERTS_LC_LOCK_INIT(-1,
- p->id,
+ p->common.id,
ERTS_LC_FLG_LT_PROCLOCK);
if (locks & ERTS_PROC_LOCK_STATUS) {
lck.id = lc_id.proc_lock_status;
@@ -1375,7 +1288,7 @@ erts_proc_lc_might_unlock(Process *p, ErtsProcLocks locks)
{
#if ERTS_PROC_LOCK_OWN_IMPL
erts_lc_lock_t lck = ERTS_LC_LOCK_INIT(-1,
- p->id,
+ p->common.id,
ERTS_LC_FLG_LT_PROCLOCK);
if (locks & ERTS_PROC_LOCK_STATUS) {
lck.id = lc_id.proc_lock_status;
@@ -1410,7 +1323,7 @@ erts_proc_lc_require_lock(Process *p, ErtsProcLocks locks)
{
#if ERTS_PROC_LOCK_OWN_IMPL
erts_lc_lock_t lck = ERTS_LC_LOCK_INIT(-1,
- p->id,
+ p->common.id,
ERTS_LC_FLG_LT_PROCLOCK);
if (locks & ERTS_PROC_LOCK_MAIN) {
lck.id = lc_id.proc_lock_main;
@@ -1445,7 +1358,7 @@ erts_proc_lc_unrequire_lock(Process *p, ErtsProcLocks locks)
{
#if ERTS_PROC_LOCK_OWN_IMPL
erts_lc_lock_t lck = ERTS_LC_LOCK_INIT(-1,
- p->id,
+ p->common.id,
ERTS_LC_FLG_LT_PROCLOCK);
if (locks & ERTS_PROC_LOCK_STATUS) {
lck.id = lc_id.proc_lock_status;
@@ -1482,7 +1395,7 @@ erts_proc_lc_trylock_force_busy(Process *p, ErtsProcLocks locks)
{
if (locks & ERTS_PROC_LOCKS_ALL) {
erts_lc_lock_t lck = ERTS_LC_LOCK_INIT(-1,
- p->id,
+ p->common.id,
ERTS_LC_FLG_LT_PROCLOCK);
if (locks & ERTS_PROC_LOCK_MAIN)
@@ -1507,7 +1420,7 @@ void erts_proc_lc_chk_only_proc_main(Process *p)
{
#if ERTS_PROC_LOCK_OWN_IMPL
erts_lc_lock_t proc_main = ERTS_LC_LOCK_INIT(lc_id.proc_lock_main,
- p->id,
+ p->common.id,
ERTS_LC_FLG_LT_PROCLOCK);
erts_lc_check_exact(&proc_main, 1);
#elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL
@@ -1531,19 +1444,19 @@ erts_proc_lc_chk_have_proc_locks(Process *p, ErtsProcLocks locks)
ERTS_PROC_LC_EMPTY_LOCK_INIT};
if (locks & ERTS_PROC_LOCK_MAIN) {
have_locks[have_locks_len].id = lc_id.proc_lock_main;
- have_locks[have_locks_len++].extra = p->id;
+ have_locks[have_locks_len++].extra = p->common.id;
}
if (locks & ERTS_PROC_LOCK_LINK) {
have_locks[have_locks_len].id = lc_id.proc_lock_link;
- have_locks[have_locks_len++].extra = p->id;
+ have_locks[have_locks_len++].extra = p->common.id;
}
if (locks & ERTS_PROC_LOCK_MSGQ) {
have_locks[have_locks_len].id = lc_id.proc_lock_msgq;
- have_locks[have_locks_len++].extra = p->id;
+ have_locks[have_locks_len++].extra = p->common.id;
}
if (locks & ERTS_PROC_LOCK_STATUS) {
have_locks[have_locks_len].id = lc_id.proc_lock_status;
- have_locks[have_locks_len++].extra = p->id;
+ have_locks[have_locks_len++].extra = p->common.id;
}
#elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL
erts_lc_lock_t have_locks[4];
@@ -1576,35 +1489,35 @@ erts_proc_lc_chk_proc_locks(Process *p, ErtsProcLocks locks)
if (locks & ERTS_PROC_LOCK_MAIN) {
have_locks[have_locks_len].id = lc_id.proc_lock_main;
- have_locks[have_locks_len++].extra = p->id;
+ have_locks[have_locks_len++].extra = p->common.id;
}
else {
have_not_locks[have_not_locks_len].id = lc_id.proc_lock_main;
- have_not_locks[have_not_locks_len++].extra = p->id;
+ have_not_locks[have_not_locks_len++].extra = p->common.id;
}
if (locks & ERTS_PROC_LOCK_LINK) {
have_locks[have_locks_len].id = lc_id.proc_lock_link;
- have_locks[have_locks_len++].extra = p->id;
+ have_locks[have_locks_len++].extra = p->common.id;
}
else {
have_not_locks[have_not_locks_len].id = lc_id.proc_lock_link;
- have_not_locks[have_not_locks_len++].extra = p->id;
+ have_not_locks[have_not_locks_len++].extra = p->common.id;
}
if (locks & ERTS_PROC_LOCK_MSGQ) {
have_locks[have_locks_len].id = lc_id.proc_lock_msgq;
- have_locks[have_locks_len++].extra = p->id;
+ have_locks[have_locks_len++].extra = p->common.id;
}
else {
have_not_locks[have_not_locks_len].id = lc_id.proc_lock_msgq;
- have_not_locks[have_not_locks_len++].extra = p->id;
+ have_not_locks[have_not_locks_len++].extra = p->common.id;
}
if (locks & ERTS_PROC_LOCK_STATUS) {
have_locks[have_locks_len].id = lc_id.proc_lock_status;
- have_locks[have_locks_len++].extra = p->id;
+ have_locks[have_locks_len++].extra = p->common.id;
}
else {
have_not_locks[have_not_locks_len].id = lc_id.proc_lock_status;
- have_not_locks[have_not_locks_len++].extra = p->id;
+ have_not_locks[have_not_locks_len++].extra = p->common.id;
}
#elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL
erts_lc_lock_t have_locks[4];
@@ -1639,16 +1552,16 @@ erts_proc_lc_my_proc_locks(Process *p)
ErtsProcLocks res = 0;
#if ERTS_PROC_LOCK_OWN_IMPL
erts_lc_lock_t locks[4] = {ERTS_LC_LOCK_INIT(lc_id.proc_lock_main,
- p->id,
+ p->common.id,
ERTS_LC_FLG_LT_PROCLOCK),
ERTS_LC_LOCK_INIT(lc_id.proc_lock_link,
- p->id,
+ p->common.id,
ERTS_LC_FLG_LT_PROCLOCK),
ERTS_LC_LOCK_INIT(lc_id.proc_lock_msgq,
- p->id,
+ p->common.id,
ERTS_LC_FLG_LT_PROCLOCK),
ERTS_LC_LOCK_INIT(lc_id.proc_lock_status,
- p->id,
+ p->common.id,
ERTS_LC_FLG_LT_PROCLOCK)};
#elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL
erts_lc_lock_t locks[4] = {p->lock.main.lc,
@@ -1679,7 +1592,7 @@ erts_proc_lc_chk_no_proc_locks(char *file, int line)
lc_id.proc_lock_msgq,
lc_id.proc_lock_status};
erts_lc_have_lock_ids(resv, ids, 4);
- if (resv[0] || resv[1] || resv[2] || resv[3]) {
+ if (!ERTS_IS_CRASH_DUMPING && (resv[0] || resv[1] || resv[2] || resv[3])) {
erts_lc_fail("%s:%d: Thread has process locks locked when expected "
"not to have any process locks locked",
file, line);
@@ -1701,21 +1614,21 @@ check_queue(erts_proc_lock_t *lck)
if (lflgs & wtr) {
int n;
erts_tse_t *wtr;
- ERTS_LC_ASSERT(lck->queues && lck->queues->queue[lock_no]);
- wtr = lck->queues->queue[lock_no];
+ ERTS_LC_ASSERT(lck->queue[lock_no]);
+ wtr = lck->queue[lock_no];
n = 0;
do {
wtr = wtr->next;
n++;
- } while (wtr != lck->queues->queue[lock_no]);
+ } while (wtr != lck->queue[lock_no]);
do {
wtr = wtr->prev;
n--;
- } while (wtr != lck->queues->queue[lock_no]);
+ } while (wtr != lck->queue[lock_no]);
ERTS_LC_ASSERT(n == 0);
}
else {
- ERTS_LC_ASSERT(!lck->queues || !lck->queues->queue[lock_no]);
+ ERTS_LC_ASSERT(!lck->queue[lock_no]);
}
}
}
diff --git a/erts/emulator/beam/erl_process_lock.h b/erts/emulator/beam/erl_process_lock.h
index 7367e03b44..9dd503f3cb 100644
--- a/erts/emulator/beam/erl_process_lock.h
+++ b/erts/emulator/beam/erl_process_lock.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2007-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2007-2012. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -69,8 +69,6 @@
typedef erts_aint32_t ErtsProcLocks;
-typedef struct erts_proc_lock_queues_t_ erts_proc_lock_queues_t;
-
typedef struct erts_proc_lock_t_ {
#if ERTS_PROC_LOCK_OWN_IMPL
#if ERTS_PROC_LOCK_ATOMIC_IMPL
@@ -78,7 +76,7 @@ typedef struct erts_proc_lock_t_ {
#else
ErtsProcLocks flags;
#endif
- erts_proc_lock_queues_t *queues;
+ erts_tse_t *queue[ERTS_PROC_LOCK_MAX_BIT+1];
#ifdef ERTS_ENABLE_LOCK_COUNT
erts_lcnt_lock_t lcnt_main;
erts_lcnt_lock_t lcnt_link;
@@ -178,8 +176,8 @@ typedef struct erts_proc_lock_t_ {
* on multiple processes, locks on processes with low process ids
* have to be locked before locks on processes with high process
* ids. E.g., if the main and the message queue locks are to be
- * locked on processes p1 and p2 and p1->id < p2->id, then locks
- * should be locked in the following order:
+ * locked on processes p1 and p2 and p1->common.id < p2->common.id,
+ * then locks should be locked in the following order:
* 1. main lock on p1
* 2. main lock on p2
* 3. message queue lock on p1
@@ -205,7 +203,7 @@ typedef struct erts_proc_lock_t_ {
& ~ERTS_PROC_LOCK_MAIN)
-#define ERTS_PIX_LOCKS_BITS 8
+#define ERTS_PIX_LOCKS_BITS 10
#define ERTS_NO_OF_PIX_LOCKS (1 << ERTS_PIX_LOCKS_BITS)
@@ -769,7 +767,7 @@ erts_smp_proc_lock(Process *p, ErtsProcLocks locks)
#if ERTS_PROC_LOCK_ATOMIC_IMPL
NULL,
#else
- ERTS_PID2PIXLOCK(p->id),
+ ERTS_PID2PIXLOCK(p->common.id),
#endif /*ERTS_PROC_LOCK_ATOMIC_IMPL*/
locks, file, line);
#elif defined(ERTS_SMP)
@@ -777,7 +775,7 @@ erts_smp_proc_lock(Process *p, ErtsProcLocks locks)
#if ERTS_PROC_LOCK_ATOMIC_IMPL
NULL,
#else
- ERTS_PID2PIXLOCK(p->id),
+ ERTS_PID2PIXLOCK(p->common.id),
#endif /*ERTS_PROC_LOCK_ATOMIC_IMPL*/
locks);
#endif /*ERTS_SMP*/
@@ -791,7 +789,7 @@ erts_smp_proc_unlock(Process *p, ErtsProcLocks locks)
#if ERTS_PROC_LOCK_ATOMIC_IMPL
NULL,
#else
- ERTS_PID2PIXLOCK(p->id),
+ ERTS_PID2PIXLOCK(p->common.id),
#endif
locks);
#endif
@@ -807,7 +805,7 @@ erts_smp_proc_trylock(Process *p, ErtsProcLocks locks)
#if ERTS_PROC_LOCK_ATOMIC_IMPL
NULL,
#else
- ERTS_PID2PIXLOCK(p->id),
+ ERTS_PID2PIXLOCK(p->common.id),
#endif
locks);
#endif
@@ -816,21 +814,15 @@ erts_smp_proc_trylock(Process *p, ErtsProcLocks locks)
ERTS_GLB_INLINE void erts_smp_proc_inc_refc(Process *p)
{
#ifdef ERTS_SMP
-#ifdef ERTS_ENABLE_LOCK_CHECK
- erts_aint32_t refc = erts_atomic32_inc_read_nob(&p->lock.refc);
- ERTS_SMP_LC_ASSERT(refc > 1);
-#else
- erts_atomic32_inc_nob(&p->lock.refc);
-#endif
+ erts_ptab_inc_refc(&p->common);
#endif
}
ERTS_GLB_INLINE void erts_smp_proc_dec_refc(Process *p)
{
#ifdef ERTS_SMP
- erts_aint32_t refc = erts_atomic32_dec_read_nob(&p->lock.refc);
- ERTS_SMP_LC_ASSERT(refc >= 0);
- if (refc == 0)
+ int referred = erts_ptab_dec_test_refc(&p->common);
+ if (!referred)
erts_free_proc(p);
#endif
}
@@ -838,10 +830,8 @@ ERTS_GLB_INLINE void erts_smp_proc_dec_refc(Process *p)
ERTS_GLB_INLINE void erts_smp_proc_add_refc(Process *p, Sint32 add_refc)
{
#ifdef ERTS_SMP
- erts_aint32_t refc = erts_atomic32_add_read_nob(&p->lock.refc,
- (erts_aint32_t) add_refc);
- ERTS_SMP_LC_ASSERT(refc >= 0);
- if (refc == 0)
+ int referred = erts_ptab_add_test_refc(&p->common, add_refc);
+ if (!referred)
erts_free_proc(p);
#endif
}
@@ -877,8 +867,7 @@ void erts_proc_safelock(Process *a_proc,
#define ERTS_P2P_FLG_TRY_LOCK (1 << 1)
#define ERTS_P2P_FLG_SMP_INC_REFC (1 << 2)
-#define ERTS_PROC_LOCK_BUSY ((Process *) &erts_proc_lock_busy)
-extern const Process erts_proc_lock_busy;
+#define ERTS_PROC_LOCK_BUSY ((Process *) &erts_invalid_process)
#define erts_pid2proc(PROC, HL, PID, NL) \
erts_pid2proc_opt((PROC), (HL), (PID), (NL), 0)
@@ -898,33 +887,24 @@ Process *erts_pid2proc_opt(Process *, ErtsProcLocks, Eterm, ErtsProcLocks, int);
ERTS_GLB_INLINE Process *erts_pix2proc(int ix)
{
Process *proc;
- ASSERT(0 <= ix && ix < erts_proc.max);
- proc = (Process *) erts_smp_atomic_read_nob(&erts_proc.tab[ix]);
+ ASSERT(0 <= ix && ix < erts_ptab_max(&erts_proc));
+ proc = (Process *) erts_ptab_pix2intptr_nob(&erts_proc, ix);
return proc == ERTS_PROC_LOCK_BUSY ? NULL : proc;
}
ERTS_GLB_INLINE Process *erts_proc_lookup_raw(Eterm pid)
{
Process *proc;
- int pix;
- /*
- * In SMP case: Only scheduler threads are allowed
- * to use this function. Other threads need to
- * atomicaly increment refc at lookup, i.e., use
- * erts_pid2proc_opt() with ERTS_P2P_FLG_SMP_INC_REFC.
- */
- ERTS_SMP_LC_ASSERT(erts_get_scheduler_id());
+ ERTS_SMP_LC_ASSERT(erts_thr_progress_lc_is_delaying());
if (is_not_internal_pid(pid))
return NULL;
- pix = internal_pid_index(pid);
-
- proc = (Process *) erts_smp_atomic_read_ddrb(&erts_proc.tab[pix]);
- if (proc && proc->id != pid)
+ proc = (Process *) erts_ptab_pix2intptr_ddrb(&erts_proc,
+ internal_pid_index(pid));
+ if (proc && proc->common.id != pid)
return NULL;
-
return proc;
}
diff --git a/erts/emulator/beam/erl_ptab.c b/erts/emulator/beam/erl_ptab.c
new file mode 100644
index 0000000000..87beeafa1a
--- /dev/null
+++ b/erts/emulator/beam/erl_ptab.c
@@ -0,0 +1,1566 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2012. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, 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: Process/Port table implementation.
+ *
+ * Author: Rickard Green
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#define ERTS_PTAB_WANT_BIF_IMPL__
+#define ERTS_PTAB_WANT_DEBUG_FUNCS__
+#include "erl_ptab.h"
+#include "global.h"
+#include "erl_binary.h"
+
+typedef struct ErtsPTabListBifData_ ErtsPTabListBifData;
+
+#define ERTS_PTAB_LIST_BIF_TAB_INSPECT_INDICES_PER_RED 25
+#define ERTS_PTAB_LIST_BIF_TAB_CHUNK_SIZE 1000
+#define ERTS_PTAB_LIST_BIF_MIN_START_REDS \
+ (ERTS_PTAB_LIST_BIF_TAB_CHUNK_SIZE \
+ / ERTS_PTAB_LIST_BIF_TAB_INSPECT_INDICES_PER_RED)
+
+#define ERTS_PTAB_LIST_BIF_TAB_FREE_DELETED_REDS 1
+
+#define ERTS_PTAB_LIST_BIF_INSPECT_DELETED_PER_RED 10
+
+#define ERTS_PTAB_LIST_INSPECT_DELETED_MAX_REDS \
+ (ERTS_PTAB_LIST_BIF_TAB_CHUNK_SIZE \
+ / ERTS_PTAB_LIST_BIF_TAB_INSPECT_INDICES_PER_RED)
+
+
+#define ERTS_PTAB_LIST_BIF_BUILD_RESULT_CONSES_PER_RED 75
+
+#define ERTS_PTAB_LIST_DBG_DO_TRACE 0
+
+#ifdef DEBUG
+# define ERTS_PTAB_LIST_BIF_DEBUGLEVEL 100
+#else
+# define ERTS_PTAB_LIST_BIF_DEBUGLEVEL 0
+#endif
+
+#define ERTS_PTAB_LIST_DBGLVL_CHK_HALLOC 1
+#define ERTS_PTAB_LIST_DBGLVL_CHK_FOUND_PIDS 5
+#define ERTS_PTAB_LIST_DBGLVL_CHK_PIDS 10
+#define ERTS_PTAB_LIST_DBGLVL_CHK_DEL_LIST 20
+#define ERTS_PTAB_LIST_DBGLVL_CHK_RESLIST 20
+
+#if ERTS_PTAB_LIST_BIF_DEBUGLEVEL == 0
+# define ERTS_PTAB_LIST_ASSERT(EXP)
+#else
+# define ERTS_PTAB_LIST_ASSERT(EXP) \
+ ((void) ((EXP) \
+ ? 1 \
+ : (debug_ptab_list_assert_error(#EXP, \
+ __FILE__, \
+ __LINE__, \
+ __func__), \
+ 0)))
+#endif
+
+
+#if ERTS_PTAB_LIST_BIF_DEBUGLEVEL >= ERTS_PTAB_LIST_DBGLVL_CHK_HALLOC
+# define ERTS_PTAB_LIST_DBG_SAVE_HEAP_ALLOC(PTLBDP, HP, SZ) \
+do { \
+ ERTS_PTAB_LIST_ASSERT(!(PTLBDP)->debug.heap); \
+ ERTS_PTAB_LIST_ASSERT(!(PTLBDP)->debug.heap_size); \
+ (PTLBDP)->debug.heap = (HP); \
+ (PTLBDP)->debug.heap_size = (SZ); \
+} while (0)
+# define ERTS_PTAB_LIST_DBG_VERIFY_HEAP_ALLOC_USED(PTLBDP, HP) \
+do { \
+ ERTS_PTAB_LIST_ASSERT((PTLBDP)->debug.heap); \
+ ERTS_PTAB_LIST_ASSERT((PTLBDP)->debug.heap_size); \
+ ERTS_PTAB_LIST_ASSERT(((PTLBDP)->debug.heap \
+ + (PTLBDP)->debug.heap_size) \
+ == (HP)); \
+ (PTLBDP)->debug.heap = NULL; \
+ (PTLBDP)->debug.heap_size = 0; \
+} while (0)
+# define ERTS_PTAB_LIST_DBG_HEAP_ALLOC_INIT(PTLBDP) \
+do { \
+ (PTLBDP)->debug.heap = NULL; \
+ (PTLBDP)->debug.heap_size = 0; \
+} while (0)
+#else
+# define ERTS_PTAB_LIST_DBG_SAVE_HEAP_ALLOC(PTLBDP, HP, SZ)
+# define ERTS_PTAB_LIST_DBG_VERIFY_HEAP_ALLOC_USED(PTLBDP, HP)
+# define ERTS_PTAB_LIST_DBG_HEAP_ALLOC_INIT(PTLBDP)
+#endif
+
+#if ERTS_PTAB_LIST_BIF_DEBUGLEVEL >= ERTS_PTAB_LIST_DBGLVL_CHK_RESLIST
+# define ERTS_PTAB_LIST_DBG_CHK_RESLIST(R) \
+ debug_ptab_list_check_res_list((R))
+#else
+# define ERTS_PTAB_LIST_DBG_CHK_RESLIST(R)
+#endif
+
+#if ERTS_PTAB_LIST_BIF_DEBUGLEVEL >= ERTS_PTAB_LIST_DBGLVL_CHK_PIDS
+# define ERTS_PTAB_LIST_DBG_SAVE_PIDS(PTLBDP) \
+ debug_ptab_list_save_all_pids((PTLBDP))
+# define ERTS_PTAB_LIST_DBG_VERIFY_PIDS(PTLBDP) \
+do { \
+ if (!(PTLBDP)->debug.correct_pids_verified) \
+ debug_ptab_list_verify_all_pids((PTLBDP)); \
+} while (0)
+# define ERTS_PTAB_LIST_DBG_CLEANUP_CHK_PIDS(PTLBDP) \
+do { \
+ if ((PTLBDP)->debug.correct_pids) { \
+ erts_free(ERTS_ALC_T_PTAB_LIST_PIDS, \
+ (PTLBDP)->debug.correct_pids); \
+ (PTLBDP)->debug.correct_pids = NULL; \
+ } \
+} while(0)
+# define ERTS_PTAB_LIST_DBG_CHK_PIDS_INIT(PTLBDP) \
+do { \
+ (PTLBDP)->debug.correct_pids_verified = 0; \
+ (PTLBDP)->debug.correct_pids = NULL; \
+} while (0)
+#else
+# define ERTS_PTAB_LIST_DBG_SAVE_PIDS(PTLBDP)
+# define ERTS_PTAB_LIST_DBG_VERIFY_PIDS(PTLBDP)
+# define ERTS_PTAB_LIST_DBG_CLEANUP_CHK_PIDS(PTLBDP)
+# define ERTS_PTAB_LIST_DBG_CHK_PIDS_INIT(PTLBDP)
+#endif
+
+#if ERTS_PTAB_LIST_BIF_DEBUGLEVEL >= ERTS_PTAB_LIST_DBGLVL_CHK_FOUND_PIDS
+# define ERTS_PTAB_LIST_DBG_CHK_PID_FOUND(PTLBDP, PID, IC) \
+ debug_ptab_list_check_found_pid((PTLBDP), (PID), (IC), 1)
+# define ERTS_PTAB_LIST_DBG_CHK_PID_NOT_FOUND(PTLBDP, PID, IC) \
+ debug_ptab_list_check_found_pid((PTLBDP), (PID), (IC), 0)
+#else
+# define ERTS_PTAB_LIST_DBG_CHK_PID_FOUND(PTLBDP, PID, IC)
+# define ERTS_PTAB_LIST_DBG_CHK_PID_NOT_FOUND(PTLBDP, PID, IC)
+#endif
+
+#if ERTS_PTAB_LIST_BIF_DEBUGLEVEL >= ERTS_PTAB_LIST_DBGLVL_CHK_DEL_LIST
+# define ERTS_PTAB_LIST_DBG_CHK_DEL_LIST(PTab) \
+ debug_ptab_list_check_del_list((PTab))
+# define ERTS_PTAB_LIST_DBG_CHK_FREELIST(PTab, FL) \
+ debug_ptab_list_check_del_free_list((PTab), (FL))
+#else
+# define ERTS_PTAB_LIST_DBG_CHK_DEL_LIST(PTab)
+# define ERTS_PTAB_LIST_DBG_CHK_FREELIST(PTab, FL)
+#endif
+
+#if ERTS_PTAB_LIST_BIF_DEBUGLEVEL == 0
+#if ERTS_PTAB_LIST_DBG_DO_TRACE
+# define ERTS_PTAB_LIST_DBG_INIT(P, PTLBDP) \
+ (PTLBDP)->debug.caller = (P)->common.id
+# else
+# define ERTS_PTAB_LIST_DBG_INIT(P, PTLBDP)
+# endif
+# define ERTS_PTAB_LIST_DBG_CLEANUP(PTLBDP)
+#else
+# define ERTS_PTAB_LIST_DBG_INIT(P, PTLBDP) \
+do { \
+ (PTLBDP)->debug.caller = (P)->common.id; \
+ ERTS_PTAB_LIST_DBG_HEAP_ALLOC_INIT((PTLBDP)); \
+ ERTS_PTAB_LIST_DBG_CHK_PIDS_INIT((PTLBDP)); \
+} while (0)
+# define ERTS_PTAB_LIST_DBG_CLEANUP(PTLBDP) \
+do { \
+ ERTS_PTAB_LIST_DBG_CLEANUP_CHK_PIDS((PTLBDP)); \
+} while (0)
+#endif
+
+#if ERTS_PTAB_LIST_DBG_DO_TRACE
+# define ERTS_PTAB_LIST_DBG_TRACE(PID, WHAT) \
+ erts_fprintf(stderr, "%T %s:%d:%s(): %s\n", \
+ (PID), __FILE__, __LINE__, __func__, #WHAT)
+#else
+# define ERTS_PTAB_LIST_DBG_TRACE(PID, WHAT)
+#endif
+
+
+#if ERTS_PTAB_LIST_BIF_DEBUGLEVEL != 0
+static void debug_ptab_list_assert_error(char* expr,
+ const char* file,
+ int line,
+ const char *func);
+#endif
+#if ERTS_PTAB_LIST_BIF_DEBUGLEVEL >= ERTS_PTAB_LIST_DBGLVL_CHK_RESLIST
+static void debug_ptab_list_check_res_list(Eterm list);
+#endif
+#if ERTS_PTAB_LIST_BIF_DEBUGLEVEL >= ERTS_PTAB_LIST_DBGLVL_CHK_PIDS
+static void debug_ptab_list_save_all_pids(ErtsPTabListBifData *ptlbdp);
+static void debug_ptab_list_verify_all_pids(ErtsPTabListBifData *ptlbdp);
+#endif
+#if ERTS_PTAB_LIST_BIF_DEBUGLEVEL >= ERTS_PTAB_LIST_DBGLVL_CHK_FOUND_PIDS
+static void debug_ptab_list_check_found_pid(ErtsPTabListBifData *ptlbdp,
+ Eterm pid,
+ Uint64 ic,
+ int pid_should_be_found);
+#endif
+#if ERTS_PTAB_LIST_BIF_DEBUGLEVEL >= ERTS_PTAB_LIST_DBGLVL_CHK_DEL_LIST
+static void debug_ptab_list_check_del_list(ErtsPTab *ptab);
+static void debug_ptab_list_check_del_free_list(ErtsPTab *ptab,
+ ErtsPTabDeletedElement *ptdep);
+#endif
+
+struct ErtsPTabDeletedElement_ {
+ ErtsPTabDeletedElement *next;
+ ErtsPTabDeletedElement *prev;
+ int ix;
+ union {
+ struct {
+ Eterm id;
+ Uint64 inserted;
+ Uint64 deleted;
+ } element;
+ struct {
+ Uint64 interval;
+ } bif_invocation;
+ } u;
+};
+
+static Export ptab_list_continue_export;
+
+typedef struct {
+ Uint64 interval;
+} ErtsPTabListBifChunkInfo;
+
+typedef enum {
+ INITIALIZING,
+ INSPECTING_TABLE,
+ INSPECTING_DELETED,
+ BUILDING_RESULT,
+ RETURN_RESULT
+} ErtsPTabListBifState;
+
+struct ErtsPTabListBifData_ {
+ ErtsPTab *ptab;
+ ErtsPTabListBifState state;
+ Eterm caller;
+ ErtsPTabListBifChunkInfo *chunk;
+ int tix;
+ int pid_ix;
+ int pid_sz;
+ Eterm *pid;
+ ErtsPTabDeletedElement *bif_invocation; /* Only used when > 1 chunk */
+
+#if ERTS_PTAB_LIST_BIF_DEBUGLEVEL != 0 || ERTS_PTAB_LIST_DBG_DO_TRACE
+ struct {
+ Eterm caller;
+#if ERTS_PTAB_LIST_BIF_DEBUGLEVEL >= ERTS_PTAB_LIST_DBGLVL_CHK_FOUND_PIDS
+ Uint64 *pid_started;
+#endif
+#if ERTS_PTAB_LIST_BIF_DEBUGLEVEL >= ERTS_PTAB_LIST_DBGLVL_CHK_HALLOC
+ Eterm *heap;
+ Uint heap_size;
+#endif
+#if ERTS_PTAB_LIST_BIF_DEBUGLEVEL >= ERTS_PTAB_LIST_DBGLVL_CHK_PIDS
+ int correct_pids_verified;
+ Eterm *correct_pids;
+#endif
+ } debug;
+#endif
+
+};
+
+#ifdef ARCH_32
+
+static ERTS_INLINE Uint64
+dw_aint_to_uint64(erts_dw_aint_t *dw)
+{
+#ifdef ETHR_SU_DW_NAINT_T__
+ return (Uint64) dw->dw_sint;
+#else
+ Uint64 res;
+ res = (Uint64) ((Uint32) dw->sint[ERTS_DW_AINT_HIGH_WORD]);
+ res <<= 32;
+ res |= (Uint64) ((Uint32) dw->sint[ERTS_DW_AINT_LOW_WORD]);
+ return res;
+#endif
+}
+
+static void
+unint64_to_dw_aint(erts_dw_aint_t *dw, Uint64 val)
+{
+#ifdef ETHR_SU_DW_NAINT_T__
+ dw->dw_sint = (ETHR_SU_DW_NAINT_T__) val;
+#else
+ dw->sint[ERTS_DW_AINT_LOW_WORD] = (erts_aint_t) (val & 0xffffffff);
+ dw->sint[ERTS_DW_AINT_HIGH_WORD] = (erts_aint_t) ((val >> 32) & 0xffffffff);
+#endif
+}
+
+static ERTS_INLINE void
+last_data_init_nob(ErtsPTab *ptab, Uint64 val)
+{
+ erts_dw_aint_t dw;
+ unint64_to_dw_aint(&dw, val);
+ erts_smp_dw_atomic_init_nob(&ptab->vola.tile.last_data, &dw);
+}
+
+static ERTS_INLINE void
+last_data_set_relb(ErtsPTab *ptab, Uint64 val)
+{
+ erts_dw_aint_t dw;
+ unint64_to_dw_aint(&dw, val);
+ erts_smp_dw_atomic_set_relb(&ptab->vola.tile.last_data, &dw);
+}
+
+static ERTS_INLINE Uint64
+last_data_read_nob(ErtsPTab *ptab)
+{
+ erts_dw_aint_t dw;
+ erts_smp_dw_atomic_read_nob(&ptab->vola.tile.last_data, &dw);
+ return dw_aint_to_uint64(&dw);
+}
+
+static ERTS_INLINE Uint64
+last_data_read_acqb(ErtsPTab *ptab)
+{
+ erts_dw_aint_t dw;
+ erts_smp_dw_atomic_read_acqb(&ptab->vola.tile.last_data, &dw);
+ return dw_aint_to_uint64(&dw);
+}
+
+static ERTS_INLINE Uint64
+last_data_cmpxchg_relb(ErtsPTab *ptab, Uint64 new, Uint64 exp)
+{
+ erts_dw_aint_t dw_new, dw_xchg;
+
+ unint64_to_dw_aint(&dw_new, new);
+ unint64_to_dw_aint(&dw_xchg, exp);
+
+ if (erts_smp_dw_atomic_cmpxchg_relb(&ptab->vola.tile.last_data,
+ &dw_new,
+ &dw_xchg))
+ return exp;
+ else
+ return dw_aint_to_uint64(&dw_xchg);
+}
+
+#elif defined(ARCH_64)
+
+union {
+ erts_smp_atomic_t pid_data;
+ char align[ERTS_CACHE_LINE_SIZE];
+} last erts_align_attribute(ERTS_CACHE_LINE_SIZE);
+
+static ERTS_INLINE void
+last_data_init_nob(ErtsPTab *ptab, Uint64 val)
+{
+ erts_smp_atomic_init_nob(&ptab->vola.tile.last_data, (erts_aint_t) val);
+}
+
+static ERTS_INLINE void
+last_data_set_relb(ErtsPTab *ptab, Uint64 val)
+{
+ erts_smp_atomic_set_relb(&ptab->vola.tile.last_data, (erts_aint_t) val);
+}
+
+static ERTS_INLINE Uint64
+last_data_read_nob(ErtsPTab *ptab)
+{
+ return (Uint64) erts_smp_atomic_read_nob(&ptab->vola.tile.last_data);
+}
+
+static ERTS_INLINE Uint64
+last_data_read_acqb(ErtsPTab *ptab)
+{
+ return (Uint64) erts_smp_atomic_read_acqb(&ptab->vola.tile.last_data);
+}
+
+static ERTS_INLINE Uint64
+last_data_cmpxchg_relb(ErtsPTab *ptab, Uint64 new, Uint64 exp)
+{
+ return (Uint64) erts_smp_atomic_cmpxchg_relb(&ptab->vola.tile.last_data,
+ (erts_aint_t) new,
+ (erts_aint_t) exp);
+}
+
+#else
+# error "Not 64-bit, nor 32-bit architecture..."
+#endif
+
+static ERTS_INLINE int
+last_data_cmp(Uint64 ld1, Uint64 ld2)
+{
+ Uint64 ld1_wrap;
+
+ if (ld1 == ld2)
+ return 0;
+
+ ld1_wrap = ld1 + (((Uint64) 1) << 63);
+
+ if (ld1 < ld1_wrap)
+ return (ld1 < ld2 && ld2 < ld1_wrap) ? -1 : 1;
+ else
+ return (ld1_wrap <= ld2 && ld2 < ld1) ? 1 : -1;
+}
+
+#define ERTS_PTAB_LastData2EtermData(LD) \
+ ((Eterm) ((LD) & ~(~((Uint64) 0) << ERTS_PTAB_ID_DATA_SIZE)))
+
+void
+erts_ptab_init_table(ErtsPTab *ptab,
+ ErtsAlcType_t atype,
+ void (*release_element)(void *),
+ ErtsPTabElementCommon *invalid_element,
+ int size,
+ char *name)
+{
+ size_t tab_sz;
+ int bits;
+ char *tab_end;
+ erts_smp_atomic_t *tab_entry;
+ erts_smp_rwmtx_opt_t rwmtx_opts = ERTS_SMP_RWMTX_OPT_DEFAULT_INITER;
+ rwmtx_opts.type = ERTS_SMP_RWMTX_TYPE_EXTREMELY_FREQUENT_READ;
+ rwmtx_opts.lived = ERTS_SMP_RWMTX_LONG_LIVED;
+
+ erts_smp_rwmtx_init_opt(&ptab->list.data.rwmtx, &rwmtx_opts, name);
+ erts_smp_atomic32_init_nob(&ptab->vola.tile.count, 0);
+ last_data_init_nob(ptab, ~((Uint64) 0));
+
+ /* A size that is a power of 2 is to prefer performance wise */
+ bits = erts_fit_in_bits_int32(size-1);
+ size = 1 << bits;
+ if (size > ERTS_PTAB_MAX_SIZE) {
+ size = ERTS_PTAB_MAX_SIZE;
+ bits = erts_fit_in_bits_int32((Sint32) size - 1);
+ }
+
+ ptab->r.o.max = size;
+
+ tab_sz = ERTS_ALC_CACHE_LINE_ALIGN_SIZE(size*sizeof(erts_smp_atomic_t));
+ ptab->r.o.tab = erts_alloc_permanent_cache_aligned(atype, tab_sz);
+ tab_end = ((char *) ptab->r.o.tab) + tab_sz;
+ tab_entry = ptab->r.o.tab;
+ while (tab_end > ((char *) tab_entry)) {
+ erts_smp_atomic_init_nob(tab_entry, ERTS_AINT_NULL);
+ tab_entry++;
+ }
+
+ ptab->r.o.tab_cache_lines = tab_sz/ERTS_CACHE_LINE_SIZE;
+ ptab->r.o.pix_per_cache_line = (ERTS_CACHE_LINE_SIZE
+ / sizeof(erts_smp_atomic_t));
+ ASSERT((ptab->r.o.max & (ptab->r.o.max - 1)) == 0); /* power of 2 */
+ ASSERT((ptab->r.o.pix_per_cache_line
+ & (ptab->r.o.pix_per_cache_line - 1)) == 0); /* power of 2 */
+ ASSERT((ptab->r.o.tab_cache_lines
+ & (ptab->r.o.tab_cache_lines - 1)) == 0); /* power of 2 */
+
+ ptab->r.o.pix_mask
+ = (1 << bits) - 1;
+ ptab->r.o.pix_cl_mask
+ = ptab->r.o.tab_cache_lines-1;
+ ptab->r.o.pix_cl_shift
+ = erts_fit_in_bits_int32(ptab->r.o.pix_per_cache_line-1);
+ ptab->r.o.pix_cli_shift
+ = erts_fit_in_bits_int32(ptab->r.o.pix_cl_mask);
+ ptab->r.o.pix_cli_mask
+ = (1 << (bits - ptab->r.o.pix_cli_shift)) - 1;
+
+ ASSERT(ptab->r.o.pix_cl_shift + ptab->r.o.pix_cli_shift == bits);
+
+ ptab->r.o.invalid_element = invalid_element;
+ ptab->r.o.invalid_data = erts_ptab_id2data(ptab, invalid_element->id);
+ ptab->r.o.release_element = release_element;
+
+ erts_smp_interval_init(&ptab->list.data.interval);
+ ptab->list.data.deleted.start = NULL;
+ ptab->list.data.deleted.end = NULL;
+ ptab->list.data.chunks = (((ptab->r.o.max - 1)
+ / ERTS_PTAB_LIST_BIF_TAB_CHUNK_SIZE)
+ + 1);
+
+ if (size == ERTS_PTAB_MAX_SIZE) {
+ int pix;
+ /*
+ * We want a table size of a power of 2 which ERTS_PTAB_MAX_SIZE
+ * is. We only have ERTS_PTAB_MAX_SIZE-1 unique identifiers and
+ * we don't want to shrink the size to ERTS_PTAB_MAX_SIZE/2.
+ *
+ * In order to fix this, we insert a pointer from the table
+ * to the invalid_element, wich will be interpreted as a
+ * slot currently being modified. This way we will be able to
+ * have ERTS_PTAB_MAX_SIZE-1 valid elements in the table while
+ * still having a table size of the power of 2.
+ */
+ erts_smp_atomic32_inc_nob(&ptab->vola.tile.count);
+ pix = erts_ptab_data2pix(ptab, ptab->r.o.invalid_data);
+ erts_smp_atomic_set_relb(&ptab->r.o.tab[pix],
+ (erts_aint_t) ptab->r.o.invalid_element);
+ }
+
+}
+
+int
+erts_ptab_initialized(ErtsPTab *ptab)
+{
+ return ptab->r.o.tab != NULL;
+}
+
+int
+erts_ptab_new_element(ErtsPTab *ptab,
+ ErtsPTabElementCommon *ptab_el,
+ void *init_arg,
+ void (*init_ptab_el)(void *, Eterm))
+{
+ int pix;
+ Uint64 ld, exp_ld;
+ Eterm data;
+ erts_aint32_t count;
+ erts_aint_t invalid = (erts_aint_t) ptab->r.o.invalid_element;
+
+ erts_ptab_rlock(ptab);
+
+ count = erts_smp_atomic32_inc_read_acqb(&ptab->vola.tile.count);
+ if (count > ptab->r.o.max) {
+ while (1) {
+ erts_aint32_t act_count;
+
+ act_count = erts_smp_atomic32_cmpxchg_relb(&ptab->vola.tile.count,
+ count-1,
+ count);
+ if (act_count == count) {
+ erts_ptab_runlock(ptab);
+ return 0;
+ }
+ count = act_count;
+ if (count <= ptab->r.o.max)
+ break;
+ }
+ }
+
+ ptab_el->u.alive.started_interval
+ = erts_smp_current_interval_nob(erts_ptab_interval(ptab));
+
+ ld = last_data_read_acqb(ptab);
+
+ /* Reserve slot */
+ while (1) {
+ ld++;
+ pix = erts_ptab_data2pix(ptab, ERTS_PTAB_LastData2EtermData(ld));
+ if (erts_smp_atomic_read_nob(&ptab->r.o.tab[pix]) == ERTS_AINT_NULL) {
+ erts_aint_t val;
+ val = erts_smp_atomic_cmpxchg_relb(&ptab->r.o.tab[pix],
+ invalid,
+ ERTS_AINT_NULL);
+
+ if (ERTS_AINT_NULL == val)
+ break;
+ }
+ }
+
+ data = ERTS_PTAB_LastData2EtermData(ld);
+
+ if (data == ptab->r.o.invalid_data) {
+ /* Do not use invalid data; fix it... */
+ ld += ptab->r.o.max;
+ ASSERT(pix == erts_ptab_data2pix(ptab,
+ ERTS_PTAB_LastData2EtermData(ld)));
+ data = ERTS_PTAB_LastData2EtermData(ld);
+ ASSERT(data != ptab->r.o.invalid_data);
+ }
+
+ exp_ld = last_data_read_nob(ptab);
+
+ /* Move last data forward */
+ while (1) {
+ Uint64 act_ld;
+ if (last_data_cmp(ld, exp_ld) < 0)
+ break;
+ act_ld = last_data_cmpxchg_relb(ptab, ld, exp_ld);
+ if (act_ld == exp_ld)
+ break;
+ exp_ld = act_ld;
+ }
+
+ init_ptab_el(init_arg, data);
+
+#ifdef ERTS_SMP
+ erts_smp_atomic32_init_nob(&ptab_el->refc, 1);
+#endif
+
+ /* Move into slot reserved */
+#ifdef DEBUG
+ ASSERT(invalid == erts_smp_atomic_xchg_relb(&ptab->r.o.tab[pix],
+ (erts_aint_t) ptab_el));
+#else
+ erts_smp_atomic_set_relb(&ptab->r.o.tab[pix], (erts_aint_t) ptab_el);
+#endif
+
+ erts_ptab_runlock(ptab);
+
+ return 1;
+}
+
+static void
+save_deleted_element(ErtsPTab *ptab, ErtsPTabElementCommon *ptab_el)
+{
+ ErtsPTabDeletedElement *ptdep = erts_alloc(ERTS_ALC_T_PTAB_LIST_DEL,
+ sizeof(ErtsPTabDeletedElement));
+ ERTS_PTAB_LIST_ASSERT(ptab->list.data.deleted.start
+ && ptab->list.data.deleted.end);
+ ERTS_SMP_LC_ASSERT(erts_smp_lc_ptab_is_rwlocked(ptab));
+
+ ERTS_PTAB_LIST_DBG_CHK_DEL_LIST(ptab);
+
+ ptdep->prev = ptab->list.data.deleted.end;
+ ptdep->next = NULL;
+ ptdep->ix = erts_ptab_id2pix(ptab, ptab_el->id);
+ ptdep->u.element.id = ptab_el->id;
+ ptdep->u.element.inserted = ptab_el->u.alive.started_interval;
+ ptdep->u.element.deleted =
+ erts_smp_current_interval_nob(erts_ptab_interval(ptab));
+
+ ptab->list.data.deleted.end->next = ptdep;
+ ptab->list.data.deleted.end = ptdep;
+
+ ERTS_PTAB_LIST_DBG_CHK_DEL_LIST(ptab);
+
+ ERTS_PTAB_LIST_ASSERT(ptdep->prev->ix >= 0
+ ? (ptdep->u.element.deleted
+ >= ptdep->prev->u.element.deleted)
+ : (ptdep->u.element.deleted
+ >= ptdep->prev->u.bif_invocation.interval));
+}
+
+void
+erts_ptab_delete_element(ErtsPTab *ptab,
+ ErtsPTabElementCommon *ptab_el)
+{
+ int maybe_save;
+ int pix = erts_ptab_id2pix(ptab, ptab_el->id);
+
+ ASSERT(erts_get_scheduler_id()); /* *Need* to be a scheduler */
+
+ erts_ptab_rlock(ptab);
+ maybe_save = ptab->list.data.deleted.end != NULL;
+ if (maybe_save) {
+ erts_ptab_runlock(ptab);
+ erts_ptab_rwlock(ptab);
+ }
+
+ erts_smp_atomic_set_relb(&ptab->r.o.tab[pix], ERTS_AINT_NULL);
+
+ ASSERT(erts_smp_atomic32_read_nob(&ptab->vola.tile.count) > 0);
+ erts_smp_atomic32_dec_relb(&ptab->vola.tile.count);
+
+ if (!maybe_save)
+ erts_ptab_runlock(ptab);
+ else {
+ if (ptab->list.data.deleted.end)
+ save_deleted_element(ptab, ptab_el);
+ erts_ptab_rwunlock(ptab);
+ }
+
+ if (ptab->r.o.release_element)
+ erts_schedule_thr_prgr_later_op(ptab->r.o.release_element,
+ (void *) ptab_el,
+ &ptab_el->u.release);
+}
+
+/*
+ * erts_ptab_list() implements BIFs listing the content of the table,
+ * e.g. erlang:processes/0.
+ */
+static void cleanup_ptab_list_bif_data(Binary *bp);
+static int ptab_list_bif_engine(Process *c_p, Eterm *res_accp, Binary *mbp);
+
+
+BIF_RETTYPE
+erts_ptab_list(Process *c_p, ErtsPTab *ptab)
+{
+ /*
+ * A requirement: The list of identifiers returned should be a
+ * consistent snapshot of all elements existing
+ * in the table at some point in time during the
+ * execution of the BIF calling this function.
+ * Since elements might be deleted while the BIF
+ * is executing, we have to keep track of all
+ * deleted elements and add them to the result.
+ * We also ignore elements created after the BIF
+ * has begun executing.
+ */
+ BIF_RETTYPE ret_val;
+ Eterm res_acc = NIL;
+ Binary *mbp = erts_create_magic_binary(sizeof(ErtsPTabListBifData),
+ cleanup_ptab_list_bif_data);
+ ErtsPTabListBifData *ptlbdp = ERTS_MAGIC_BIN_DATA(mbp);
+
+ ERTS_PTAB_LIST_DBG_TRACE(c_p->common.id, call);
+ ptlbdp->ptab = ptab;
+ ptlbdp->state = INITIALIZING;
+ ERTS_PTAB_LIST_DBG_INIT(c_p, ptlbdp);
+
+ if (ERTS_BIF_REDS_LEFT(c_p) >= ERTS_PTAB_LIST_BIF_MIN_START_REDS
+ && ptab_list_bif_engine(c_p, &res_acc, mbp)) {
+ erts_bin_free(mbp);
+ ERTS_PTAB_LIST_DBG_CHK_RESLIST(res_acc);
+ ERTS_PTAB_LIST_DBG_TRACE(c_p->common.id, return);
+ ERTS_BIF_PREP_RET(ret_val, res_acc);
+ }
+ else {
+ Eterm *hp;
+ Eterm magic_bin;
+ ERTS_PTAB_LIST_DBG_CHK_RESLIST(res_acc);
+ hp = HAlloc(c_p, PROC_BIN_SIZE);
+ ERTS_PTAB_LIST_DBG_SAVE_HEAP_ALLOC(ptlbdp, hp, PROC_BIN_SIZE);
+ magic_bin = erts_mk_magic_binary_term(&hp, &MSO(c_p), mbp);
+ ERTS_PTAB_LIST_DBG_VERIFY_HEAP_ALLOC_USED(ptlbdp, hp);
+ ERTS_PTAB_LIST_DBG_TRACE(c_p->common.id, trap);
+ ERTS_BIF_PREP_YIELD2(ret_val,
+ &ptab_list_continue_export,
+ c_p,
+ res_acc,
+ magic_bin);
+ }
+ return ret_val;
+}
+
+static void
+cleanup_ptab_list_bif_data(Binary *bp)
+{
+ ErtsPTabListBifData *ptlbdp = ERTS_MAGIC_BIN_DATA(bp);
+ ErtsPTab *ptab = ptlbdp->ptab;
+
+ ERTS_PTAB_LIST_DBG_TRACE(ptlbdp->debug.caller, call);
+
+ if (ptlbdp->state != INITIALIZING) {
+
+ if (ptlbdp->chunk) {
+ erts_free(ERTS_ALC_T_PTAB_LIST_CNKI, ptlbdp->chunk);
+ ptlbdp->chunk = NULL;
+ }
+ if (ptlbdp->pid) {
+ erts_free(ERTS_ALC_T_PTAB_LIST_PIDS, ptlbdp->pid);
+ ptlbdp->pid = NULL;
+ }
+
+#if ERTS_PTAB_LIST_BIF_DEBUGLEVEL >= ERTS_PTAB_LIST_DBGLVL_CHK_FOUND_PIDS
+ if (ptlbdp->debug.pid_started) {
+ erts_free(ERTS_ALC_T_PTAB_LIST_PIDS, ptlbdp->debug.pid_started);
+ ptlbdp->debug.pid_started = NULL;
+ }
+#endif
+
+ if (ptlbdp->bif_invocation) {
+ ErtsPTabDeletedElement *ptdep;
+
+ erts_ptab_rwlock(ptab);
+
+ ERTS_PTAB_LIST_DBG_TRACE(ptlbdp->debug.caller, deleted_cleanup);
+
+ ptdep = ptlbdp->bif_invocation;
+ ptlbdp->bif_invocation = NULL;
+
+ ERTS_PTAB_LIST_DBG_CHK_DEL_LIST(ptab);
+
+ if (ptdep->prev) {
+ /*
+ * Only remove this bif invokation when we
+ * have preceding invokations.
+ */
+ ptdep->prev->next = ptdep->next;
+ if (ptdep->next)
+ ptdep->next->prev = ptdep->prev;
+ else {
+ /*
+ * At the time of writing this branch cannot be
+ * reached. I don't want to remove this code though
+ * since it may be possible to reach this line
+ * in the future if the cleanup order in
+ * erts_do_exit_process() is changed. The ASSERT(0)
+ * is only here to make us aware that the reorder
+ * has happened. /rickard
+ */
+ ASSERT(0);
+ ptab->list.data.deleted.end = ptdep->prev;
+ }
+ erts_free(ERTS_ALC_T_PTAB_LIST_DEL, ptdep);
+ }
+ else {
+ /*
+ * Free all elements until next bif invokation
+ * is found.
+ */
+ ERTS_PTAB_LIST_ASSERT(ptab->list.data.deleted.start == ptdep);
+ do {
+ ErtsPTabDeletedElement *fptdep = ptdep;
+ ptdep = ptdep->next;
+ erts_free(ERTS_ALC_T_PTAB_LIST_DEL, fptdep);
+ } while (ptdep && ptdep->ix >= 0);
+ ptab->list.data.deleted.start = ptdep;
+ if (ptdep)
+ ptdep->prev = NULL;
+ else
+ ptab->list.data.deleted.end = NULL;
+ }
+
+ ERTS_PTAB_LIST_DBG_CHK_DEL_LIST(ptab);
+
+ erts_ptab_rwunlock(ptab);
+
+ }
+ }
+
+ ERTS_PTAB_LIST_DBG_TRACE(ptlbdp->debug.caller, return);
+ ERTS_PTAB_LIST_DBG_CLEANUP(ptlbdp);
+}
+
+static int
+ptab_list_bif_engine(Process *c_p, Eterm *res_accp, Binary *mbp)
+{
+ ErtsPTabListBifData *ptlbdp = ERTS_MAGIC_BIN_DATA(mbp);
+ ErtsPTab *ptab = ptlbdp->ptab;
+ int have_reds;
+ int reds;
+ int locked = 0;
+
+ do {
+ switch (ptlbdp->state) {
+ case INITIALIZING:
+ ptlbdp->chunk = erts_alloc(ERTS_ALC_T_PTAB_LIST_CNKI,
+ (sizeof(ErtsPTabListBifChunkInfo)
+ * ptab->list.data.chunks));
+ ptlbdp->tix = 0;
+ ptlbdp->pid_ix = 0;
+
+ erts_ptab_rwlock(ptab);
+ locked = 1;
+
+ ERTS_PTAB_LIST_DBG_TRACE(c_p->common.id, init);
+
+ ptlbdp->pid_sz = erts_ptab_count(ptab);
+ ptlbdp->pid = erts_alloc(ERTS_ALC_T_PTAB_LIST_PIDS,
+ sizeof(Eterm)*ptlbdp->pid_sz);
+
+#if ERTS_PTAB_LIST_BIF_DEBUGLEVEL >= ERTS_PTAB_LIST_DBGLVL_CHK_FOUND_PIDS
+ ptlbdp->debug.pid_started
+ = erts_alloc(ERTS_ALC_T_PTAB_LIST_PIDS,
+ sizeof(Uint64)*ptlbdp->pid_sz);
+#endif
+
+ ERTS_PTAB_LIST_DBG_SAVE_PIDS(ptlbdp);
+
+ if (ptab->list.data.chunks == 1)
+ ptlbdp->bif_invocation = NULL;
+ else {
+ /*
+ * We will have to access the table multiple times
+ * releasing the table lock in between chunks.
+ */
+ ptlbdp->bif_invocation
+ = erts_alloc(ERTS_ALC_T_PTAB_LIST_DEL,
+ sizeof(ErtsPTabDeletedElement));
+ ptlbdp->bif_invocation->ix = -1;
+ ptlbdp->bif_invocation->u.bif_invocation.interval
+ = erts_smp_step_interval_nob(erts_ptab_interval(ptab));
+ ERTS_PTAB_LIST_DBG_CHK_DEL_LIST(ptab);
+
+ ptlbdp->bif_invocation->next = NULL;
+ if (ptab->list.data.deleted.end) {
+ ptlbdp->bif_invocation->prev = ptab->list.data.deleted.end;
+ ptab->list.data.deleted.end->next = ptlbdp->bif_invocation;
+ ERTS_PTAB_LIST_ASSERT(ptab->list.data.deleted.start);
+ }
+ else {
+ ptlbdp->bif_invocation->prev = NULL;
+ ptab->list.data.deleted.start = ptlbdp->bif_invocation;
+ }
+ ptab->list.data.deleted.end = ptlbdp->bif_invocation;
+
+ ERTS_PTAB_LIST_DBG_CHK_DEL_LIST(ptab);
+
+ }
+
+ ptlbdp->state = INSPECTING_TABLE;
+ /* Fall through */
+
+ case INSPECTING_TABLE: {
+ int ix = ptlbdp->tix;
+ int indices = ERTS_PTAB_LIST_BIF_TAB_CHUNK_SIZE;
+ int cix = ix / ERTS_PTAB_LIST_BIF_TAB_CHUNK_SIZE;
+ int end_ix = ix + indices;
+ Uint64 *invocation_interval_p;
+ ErtsPTabElementCommon *invalid_element;
+
+ invocation_interval_p
+ = (ptlbdp->bif_invocation
+ ? &ptlbdp->bif_invocation->u.bif_invocation.interval
+ : NULL);
+
+ ERTS_PTAB_LIST_ASSERT(is_nil(*res_accp));
+ if (!locked) {
+ erts_ptab_rwlock(ptab);
+ locked = 1;
+ }
+
+ ERTS_SMP_LC_ASSERT(erts_smp_lc_ptab_is_rwlocked(ptab));
+ ERTS_PTAB_LIST_DBG_TRACE(p->common.id, insp_table);
+
+ if (cix != 0)
+ ptlbdp->chunk[cix].interval
+ = erts_smp_step_interval_nob(erts_ptab_interval(ptab));
+ else if (ptlbdp->bif_invocation)
+ ptlbdp->chunk[0].interval = *invocation_interval_p;
+ /* else: interval is irrelevant */
+
+ if (end_ix >= ptab->r.o.max) {
+ ERTS_PTAB_LIST_ASSERT(cix+1 == ptab->list.data.chunks);
+ end_ix = ptab->r.o.max;
+ indices = end_ix - ix;
+ /* What to do when done with this chunk */
+ ptlbdp->state = (ptab->list.data.chunks == 1
+ ? BUILDING_RESULT
+ : INSPECTING_DELETED);
+ }
+
+ invalid_element = ptab->r.o.invalid_element;
+ for (; ix < end_ix; ix++) {
+ ErtsPTabElementCommon *el;
+ el = (ErtsPTabElementCommon *) erts_ptab_pix2intptr_nob(ptab,
+ ix);
+ if (el
+ && el != invalid_element
+ && (!invocation_interval_p
+ || el->u.alive.started_interval < *invocation_interval_p)) {
+ ERTS_PTAB_LIST_ASSERT(erts_ptab_is_valid_id(el->id));
+ ptlbdp->pid[ptlbdp->pid_ix] = el->id;
+
+#if ERTS_PTAB_LIST_BIF_DEBUGLEVEL >= ERTS_PTAB_LIST_DBGLVL_CHK_FOUND_PIDS
+ ptlbdp->debug.pid_started[ptlbdp->pid_ix]
+ = el->u.alive.started_interval;
+#endif
+
+ ptlbdp->pid_ix++;
+ ERTS_PTAB_LIST_ASSERT(ptlbdp->pid_ix <= ptlbdp->pid_sz);
+ }
+ }
+
+ ptlbdp->tix = end_ix;
+
+ erts_ptab_rwunlock(ptab);
+ locked = 0;
+
+ reds = indices/ERTS_PTAB_LIST_BIF_TAB_INSPECT_INDICES_PER_RED;
+ BUMP_REDS(c_p, reds);
+
+ have_reds = ERTS_BIF_REDS_LEFT(c_p);
+
+ if (have_reds && ptlbdp->state == INSPECTING_TABLE) {
+ ix = ptlbdp->tix;
+ indices = ERTS_PTAB_LIST_BIF_TAB_CHUNK_SIZE;
+ end_ix = ix + indices;
+ if (end_ix > ptab->r.o.max) {
+ end_ix = ptab->r.o.max;
+ indices = end_ix - ix;
+ }
+
+ reds = indices/ERTS_PTAB_LIST_BIF_TAB_INSPECT_INDICES_PER_RED;
+
+ /* Pretend we have no reds left if we haven't got enough
+ reductions to complete next chunk */
+ if (reds > have_reds)
+ have_reds = 0;
+ }
+
+ break;
+ }
+
+ case INSPECTING_DELETED: {
+ int i;
+ int max_reds;
+ int free_deleted = 0;
+ Uint64 invocation_interval;
+ ErtsPTabDeletedElement *ptdep;
+ ErtsPTabDeletedElement *free_list = NULL;
+
+ ptdep = ptlbdp->bif_invocation;
+ ERTS_PTAB_LIST_ASSERT(ptdep);
+ invocation_interval = ptdep->u.bif_invocation.interval;
+
+ max_reds = have_reds = ERTS_BIF_REDS_LEFT(c_p);
+ if (max_reds > ERTS_PTAB_LIST_INSPECT_DELETED_MAX_REDS)
+ max_reds = ERTS_PTAB_LIST_INSPECT_DELETED_MAX_REDS;
+
+ reds = 0;
+ erts_ptab_rwlock(ptab);
+ ERTS_PTAB_LIST_DBG_TRACE(p->common.id, insp_term_procs);
+
+ ERTS_PTAB_LIST_DBG_CHK_DEL_LIST(ptab);
+
+ if (ptdep->prev)
+ ptdep->prev->next = ptdep->next;
+ else {
+ ERTS_PTAB_LIST_ASSERT(ptab->list.data.deleted.start == ptdep);
+ ptab->list.data.deleted.start = ptdep->next;
+
+ if (ptab->list.data.deleted.start
+ && ptab->list.data.deleted.start->ix >= 0) {
+ free_list = ptab->list.data.deleted.start;
+ free_deleted = 1;
+ }
+ }
+
+ if (ptdep->next)
+ ptdep->next->prev = ptdep->prev;
+ else
+ ptab->list.data.deleted.end = ptdep->prev;
+
+ ptdep = ptdep->next;
+
+ i = 0;
+ while (reds < max_reds && ptdep) {
+ if (ptdep->ix < 0) {
+ if (free_deleted) {
+ ERTS_PTAB_LIST_ASSERT(free_list);
+ ERTS_PTAB_LIST_ASSERT(ptdep->prev);
+
+ ptdep->prev->next = NULL; /* end of free_list */
+ ptab->list.data.deleted.start = ptdep;
+ ptdep->prev = NULL;
+ free_deleted = 0;
+ }
+ }
+ else {
+ int cix = ptdep->ix/ERTS_PTAB_LIST_BIF_TAB_CHUNK_SIZE;
+ Uint64 chunk_interval = ptlbdp->chunk[cix].interval;
+ Eterm pid = ptdep->u.element.id;
+ ERTS_PTAB_LIST_ASSERT(erts_ptab_is_valid_id(pid));
+
+ if (ptdep->u.element.inserted < invocation_interval) {
+ if (ptdep->u.element.deleted < chunk_interval) {
+ ERTS_PTAB_LIST_DBG_CHK_PID_NOT_FOUND(
+ ptlbdp,
+ pid,
+ ptdep->u.element.inserted);
+ ptlbdp->pid[ptlbdp->pid_ix] = pid;
+#if ERTS_PTAB_LIST_BIF_DEBUGLEVEL >= ERTS_PTAB_LIST_DBGLVL_CHK_FOUND_PIDS
+ ptlbdp->debug.pid_started[ptlbdp->pid_ix]
+ = ptdep->u.element.inserted;
+#endif
+ ptlbdp->pid_ix++;
+ ERTS_PTAB_LIST_ASSERT(ptlbdp->pid_ix
+ <= ptlbdp->pid_sz);
+ }
+ else {
+ ERTS_PTAB_LIST_DBG_CHK_PID_FOUND(
+ ptlbdp,
+ pid,
+ ptdep->u.element.inserted);
+ }
+ }
+ else {
+ ERTS_PTAB_LIST_DBG_CHK_PID_NOT_FOUND(
+ ptlbdp,
+ pid,
+ ptdep->u.element.inserted);
+ }
+
+ i++;
+ if (i == ERTS_PTAB_LIST_BIF_INSPECT_DELETED_PER_RED) {
+ reds++;
+ i = 0;
+ }
+ if (free_deleted)
+ reds += ERTS_PTAB_LIST_BIF_TAB_FREE_DELETED_REDS;
+ }
+ ptdep = ptdep->next;
+ }
+
+ if (free_deleted) {
+ ERTS_PTAB_LIST_ASSERT(free_list);
+ ptab->list.data.deleted.start = ptdep;
+ if (!ptdep)
+ ptab->list.data.deleted.end = NULL;
+ else {
+ ERTS_PTAB_LIST_ASSERT(ptdep->prev);
+ ptdep->prev->next = NULL; /* end of free_list */
+ ptdep->prev = NULL;
+ }
+ }
+
+ if (!ptdep) {
+ /* Done */
+ ERTS_PTAB_LIST_ASSERT(ptlbdp->pid_ix == ptlbdp->pid_sz);
+ ptlbdp->state = BUILDING_RESULT;
+ ptlbdp->bif_invocation->next = free_list;
+ free_list = ptlbdp->bif_invocation;
+ ptlbdp->bif_invocation = NULL;
+ }
+ else {
+ /* Link in bif_invocation again where we left off */
+ ptlbdp->bif_invocation->prev = ptdep->prev;
+ ptlbdp->bif_invocation->next = ptdep;
+ ptdep->prev = ptlbdp->bif_invocation;
+ if (ptlbdp->bif_invocation->prev)
+ ptlbdp->bif_invocation->prev->next = ptlbdp->bif_invocation;
+ else {
+ ERTS_PTAB_LIST_ASSERT(ptab->list.data.deleted.start
+ == ptdep);
+ ptab->list.data.deleted.start = ptlbdp->bif_invocation;
+ }
+ }
+
+ ERTS_PTAB_LIST_DBG_CHK_DEL_LIST(ptab);
+ ERTS_PTAB_LIST_DBG_CHK_FREELIST(ptab, free_list);
+ erts_ptab_rwunlock(ptab);
+
+ /*
+ * We do the actual free of deleted structures now when we
+ * have released the table lock instead of when we encountered
+ * them. This since free() isn't for free and we don't want to
+ * unnecessarily block other schedulers.
+ */
+ while (free_list) {
+ ptdep = free_list;
+ free_list = ptdep->next;
+ erts_free(ERTS_ALC_T_PTAB_LIST_DEL, ptdep);
+ }
+
+ have_reds -= reds;
+ if (have_reds < 0)
+ have_reds = 0;
+ BUMP_REDS(c_p, reds);
+ break;
+ }
+
+ case BUILDING_RESULT: {
+ int conses, ix, min_ix;
+ Eterm *hp;
+ Eterm res = *res_accp;
+
+ ERTS_PTAB_LIST_DBG_VERIFY_PIDS(ptlbdp);
+ ERTS_PTAB_LIST_DBG_CHK_RESLIST(res);
+
+ ERTS_PTAB_LIST_DBG_TRACE(p->common.id, begin_build_res);
+
+ have_reds = ERTS_BIF_REDS_LEFT(c_p);
+ conses = ERTS_PTAB_LIST_BIF_BUILD_RESULT_CONSES_PER_RED*have_reds;
+ min_ix = ptlbdp->pid_ix - conses;
+ if (min_ix < 0) {
+ min_ix = 0;
+ conses = ptlbdp->pid_ix;
+ }
+
+ if (conses) {
+ hp = HAlloc(c_p, conses*2);
+ ERTS_PTAB_LIST_DBG_SAVE_HEAP_ALLOC(ptlbdp, hp, conses*2);
+
+ for (ix = ptlbdp->pid_ix - 1; ix >= min_ix; ix--) {
+ ERTS_PTAB_LIST_ASSERT(erts_ptab_is_valid_id(ptlbdp->pid[ix]));
+ res = CONS(hp, ptlbdp->pid[ix], res);
+ hp += 2;
+ }
+
+ ERTS_PTAB_LIST_DBG_VERIFY_HEAP_ALLOC_USED(ptlbdp, hp);
+ }
+
+ ptlbdp->pid_ix = min_ix;
+ if (min_ix == 0)
+ ptlbdp->state = RETURN_RESULT;
+ else {
+ ptlbdp->pid_sz = min_ix;
+ ptlbdp->pid = erts_realloc(ERTS_ALC_T_PTAB_LIST_PIDS,
+ ptlbdp->pid,
+ sizeof(Eterm)*ptlbdp->pid_sz);
+#if ERTS_PTAB_LIST_BIF_DEBUGLEVEL >= ERTS_PTAB_LIST_DBGLVL_CHK_FOUND_PIDS
+ ptlbdp->debug.pid_started
+ = erts_realloc(ERTS_ALC_T_PTAB_LIST_PIDS,
+ ptlbdp->debug.pid_started,
+ sizeof(Uint64) * ptlbdp->pid_sz);
+#endif
+ }
+ reds = conses/ERTS_PTAB_LIST_BIF_BUILD_RESULT_CONSES_PER_RED;
+ BUMP_REDS(c_p, reds);
+ have_reds -= reds;
+
+ ERTS_PTAB_LIST_DBG_CHK_RESLIST(res);
+ ERTS_PTAB_LIST_DBG_TRACE(c_p->common.id, end_build_res);
+ *res_accp = res;
+ break;
+ }
+ case RETURN_RESULT:
+ cleanup_ptab_list_bif_data(mbp);
+ return 1;
+
+ default:
+ erl_exit(ERTS_ABORT_EXIT,
+ "%s:%d:ptab_list_bif_engine(): Invalid state: %d\n",
+ __FILE__, __LINE__, (int) ptlbdp->state);
+ }
+
+
+ } while (have_reds || ptlbdp->state == RETURN_RESULT);
+
+ return 0;
+}
+
+/*
+ * ptab_list_continue/2 is a hidden BIF that the original BIF traps to
+ * if there are too much work to do in one go.
+ */
+
+static BIF_RETTYPE ptab_list_continue(BIF_ALIST_2)
+{
+ Eterm res_acc;
+ Binary *mbp;
+
+ /*
+ * This bif cannot be called from erlang code. It can only be
+ * trapped to from other BIFs; therefore, a bad argument
+ * is an internal error and should never occur...
+ */
+
+ ERTS_PTAB_LIST_DBG_TRACE(BIF_P->common.id, call);
+ ERTS_PTAB_LIST_ASSERT(is_nil(BIF_ARG_1) || is_list(BIF_ARG_1));
+
+ res_acc = BIF_ARG_1;
+
+ ERTS_PTAB_LIST_ASSERT(ERTS_TERM_IS_MAGIC_BINARY(BIF_ARG_2));
+
+ mbp = ((ProcBin *) binary_val(BIF_ARG_2))->val;
+
+ ERTS_PTAB_LIST_ASSERT(ERTS_MAGIC_BIN_DESTRUCTOR(mbp)
+ == cleanup_ptab_list_bif_data);
+ ERTS_PTAB_LIST_ASSERT(
+ ((ErtsPTabListBifData *) ERTS_MAGIC_BIN_DATA(mbp))->debug.caller
+ == BIF_P->common.id);
+
+ if (ptab_list_bif_engine(BIF_P, &res_acc, mbp)) {
+ ERTS_PTAB_LIST_DBG_TRACE(BIF_P->common.id, return);
+ BIF_RET(res_acc);
+ }
+ else {
+ ERTS_PTAB_LIST_DBG_TRACE(BIF_P->common.id, trap);
+ ERTS_BIF_YIELD2(&ptab_list_continue_export, BIF_P, res_acc, BIF_ARG_2);
+ }
+}
+
+void
+erts_ptab_init(void)
+{
+ /* ptab_list_continue/2 is a hidden BIF that the original BIF traps to. */
+ erts_init_trap_export(&ptab_list_continue_export,
+ am_erlang, am_ptab_list_continue, 2,
+ &ptab_list_continue);
+
+}
+
+/*
+ * Debug stuff
+ */
+
+Sint
+erts_ptab_test_next_id(ErtsPTab *ptab, int set, Uint next)
+{
+ Uint64 ld;
+ Sint res;
+ Eterm data;
+ int first_pix = -1;
+
+ erts_ptab_rwlock(ptab);
+
+ if (!set)
+ ld = last_data_read_nob(ptab);
+ else {
+
+ ld = (Uint64) next;
+ data = ERTS_PTAB_LastData2EtermData(ld);
+ if (ptab->r.o.invalid_data == data) {
+ ld += ptab->r.o.max;
+ ASSERT(erts_ptab_data2pix(ptab, data)
+ == erts_ptab_data2pix(ptab,
+ ERTS_PTAB_LastData2EtermData(ld)));
+ }
+ last_data_set_relb(ptab, ld);
+ }
+
+ while (1) {
+ int pix;
+ ld++;
+ pix = (int) (ld % ptab->r.o.max);
+ if (first_pix < 0)
+ first_pix = pix;
+ else if (pix == first_pix) {
+ res = -1;
+ break;
+ }
+ if (ERTS_AINT_NULL == erts_ptab_pix2intptr_nob(ptab, pix)) {
+ data = ERTS_PTAB_LastData2EtermData(ld);
+ if (ptab->r.o.invalid_data == data) {
+ ld += ptab->r.o.max;
+ ASSERT(erts_ptab_data2pix(ptab, data)
+ == erts_ptab_data2pix(ptab,
+ ERTS_PTAB_LastData2EtermData(ld)));
+ data = ERTS_PTAB_LastData2EtermData(ld);
+ }
+ res = data;
+ break;
+ }
+ }
+
+ erts_ptab_rwunlock(ptab);
+
+ return res;
+}
+
+static ERTS_INLINE ErtsPTabElementCommon *
+ptab_pix2el(ErtsPTab *ptab, int ix)
+{
+ ErtsPTabElementCommon *ptab_el;
+ ASSERT(0 <= ix && ix < ptab->r.o.max);
+ ptab_el = (ErtsPTabElementCommon *) erts_ptab_pix2intptr_nob(ptab, ix);
+ if (ptab_el == ptab->r.o.invalid_element)
+ return NULL;
+ else
+ return ptab_el;
+}
+
+Eterm
+erts_debug_ptab_list(Process *c_p, ErtsPTab *ptab)
+{
+ int i;
+ Uint need;
+ Eterm res;
+ Eterm* hp;
+ Eterm *hp_end;
+
+ erts_ptab_rwlock(ptab);
+
+ res = NIL;
+ need = erts_ptab_count(ptab) * 2;
+ hp = HAlloc(c_p, need); /* we need two heap words for each id */
+ hp_end = hp + need;
+
+ /* make the list by scanning bakward */
+
+
+ for (i = ptab->r.o.max-1; i >= 0; i--) {
+ ErtsPTabElementCommon *el = ptab_pix2el(ptab, i);
+ if (el) {
+ res = CONS(hp, el->id, res);
+ hp += 2;
+ }
+ }
+
+ erts_ptab_rwunlock(ptab);
+
+ HRelease(c_p, hp_end, hp);
+
+ return res;
+}
+
+Eterm
+erts_debug_ptab_list_bif_info(Process *c_p, ErtsPTab *ptab)
+{
+ ERTS_DECL_AM(ptab_list_bif_info);
+ Eterm elements[] = {
+ AM_ptab_list_bif_info,
+ make_small((Uint) ERTS_PTAB_LIST_BIF_MIN_START_REDS),
+ make_small((Uint) ptab->list.data.chunks),
+ make_small((Uint) ERTS_PTAB_LIST_BIF_TAB_CHUNK_SIZE),
+ make_small((Uint) ERTS_PTAB_LIST_BIF_TAB_INSPECT_INDICES_PER_RED),
+ make_small((Uint) ERTS_PTAB_LIST_BIF_TAB_FREE_DELETED_REDS),
+ make_small((Uint) ERTS_PTAB_LIST_BIF_INSPECT_DELETED_PER_RED),
+ make_small((Uint) ERTS_PTAB_LIST_INSPECT_DELETED_MAX_REDS),
+ make_small((Uint) ERTS_PTAB_LIST_BIF_BUILD_RESULT_CONSES_PER_RED),
+ make_small((Uint) ERTS_PTAB_LIST_BIF_DEBUGLEVEL)
+ };
+ Uint sz = 0;
+ Eterm *hp;
+ (void) erts_bld_tuplev(NULL, &sz, sizeof(elements)/sizeof(Eterm), elements);
+ hp = HAlloc(c_p, sz);
+ return erts_bld_tuplev(&hp, NULL, sizeof(elements)/sizeof(Eterm), elements);
+}
+
+#if ERTS_PTAB_LIST_BIF_DEBUGLEVEL >= ERTS_PTAB_LIST_DBGLVL_CHK_FOUND_PIDS
+static void
+debug_ptab_list_check_found_pid(ErtsPTabListBifData *ptlbdp,
+ Eterm pid,
+ Uint64 ic,
+ int pid_should_be_found)
+{
+ int i;
+ for (i = 0; i < ptlbdp->pid_ix; i++) {
+ if (ptlbdp->pid[i] == pid && ptlbdp->debug.pid_started[i] == ic) {
+ ERTS_PTAB_LIST_ASSERT(pid_should_be_found);
+ return;
+ }
+ }
+ ERTS_PTAB_LIST_ASSERT(!pid_should_be_found);
+}
+#endif
+
+#if ERTS_PTAB_LIST_BIF_DEBUGLEVEL >= ERTS_PTAB_LIST_DBGLVL_CHK_RESLIST
+static void
+debug_ptab_list_check_res_list(Eterm list)
+{
+ while (is_list(list)) {
+ Eterm* consp = list_val(list);
+ Eterm hd = CAR(consp);
+ ERTS_PTAB_LIST_ASSERT(erts_ptab_is_valid_id(hd));
+ list = CDR(consp);
+ }
+
+ ERTS_PTAB_LIST_ASSERT(is_nil(list));
+}
+#endif
+
+#if ERTS_PTAB_LIST_BIF_DEBUGLEVEL >= ERTS_PTAB_LIST_DBGLVL_CHK_PIDS
+
+static void
+debug_ptab_list_save_all_pids(ErtsPTabListBifData *ptlbdp)
+{
+ int ix, tix, cpix;
+ ErtsPTab *ptab = ptlbdp->ptab;
+ ptlbdp->debug.correct_pids_verified = 0;
+ ptlbdp->debug.correct_pids = erts_alloc(ERTS_ALC_T_PTAB_LIST_PIDS,
+ sizeof(Eterm)*ptlbdp->pid_sz);
+
+ for (tix = 0, cpix = 0; tix < ptab->r.o.max; tix++) {
+ ErtsPTabElementCommon *el = ptab_pix2el(ptab, tix);
+ if (el) {
+ ERTS_PTAB_LIST_ASSERT(erts_ptab_is_valid_id(el->id));
+ ptlbdp->debug.correct_pids[cpix++] = el->id;
+ ERTS_PTAB_LIST_ASSERT(cpix <= ptlbdp->pid_sz);
+ }
+ }
+ ERTS_PTAB_LIST_ASSERT(cpix == ptlbdp->pid_sz);
+
+ for (ix = 0; ix < ptlbdp->pid_sz; ix++)
+ ptlbdp->pid[ix] = make_small(ix);
+}
+
+static void
+debug_ptab_list_verify_all_pids(ErtsPTabListBifData *ptlbdp)
+{
+ int ix, cpix;
+
+ ERTS_PTAB_LIST_ASSERT(ptlbdp->pid_ix == ptlbdp->pid_sz);
+
+ for (ix = 0; ix < ptlbdp->pid_sz; ix++) {
+ int found = 0;
+ Eterm pid = ptlbdp->pid[ix];
+ ERTS_PTAB_LIST_ASSERT(erts_ptab_is_valid_id(pid));
+ for (cpix = ix; cpix < ptlbdp->pid_sz; cpix++) {
+ if (ptlbdp->debug.correct_pids[cpix] == pid) {
+ ptlbdp->debug.correct_pids[cpix] = NIL;
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ for (cpix = 0; cpix < ix; cpix++) {
+ if (ptlbdp->debug.correct_pids[cpix] == pid) {
+ ptlbdp->debug.correct_pids[cpix] = NIL;
+ found = 1;
+ break;
+ }
+ }
+ }
+ ERTS_PTAB_LIST_ASSERT(found);
+ }
+ ptlbdp->debug.correct_pids_verified = 1;
+
+ erts_free(ERTS_ALC_T_PTAB_LIST_PIDS, ptlbdp->debug.correct_pids);
+ ptlbdp->debug.correct_pids = NULL;
+}
+#endif /* ERTS_PTAB_LIST_BIF_DEBUGLEVEL >= ERTS_PTAB_LIST_DBGLVL_CHK_PIDS */
+
+#if ERTS_PTAB_LIST_BIF_DEBUGLEVEL >= ERTS_PTAB_LIST_DBGLVL_CHK_DEL_LIST
+static void
+debug_ptab_list_check_del_list(ErtsPTab *ptab)
+{
+ ERTS_SMP_LC_ASSERT(erts_smp_lc_ptab_is_rwlocked(ptab));
+ if (!ptab->list.data.deleted.start)
+ ERTS_PTAB_LIST_ASSERT(!ptab->list.data.deleted.end);
+ else {
+ Uint64 curr_interval = erts_smp_current_interval_nob(erts_ptab_interval(ptab));
+ Uint64 *prev_x_interval_p = NULL;
+ ErtsPTabDeletedElement *ptdep;
+
+ for (ptdep = ptab->list.data.deleted.start;
+ ptdep;
+ ptdep = ptdep->next) {
+ if (!ptdep->prev)
+ ERTS_PTAB_LIST_ASSERT(ptab->list.data.deleted.start == ptdep);
+ else
+ ERTS_PTAB_LIST_ASSERT(ptdep->prev->next == ptdep);
+ if (!ptdep->next)
+ ERTS_PTAB_LIST_ASSERT(ptab->list.data.deleted.end == ptdep);
+ else
+ ERTS_PTAB_LIST_ASSERT(ptdep->next->prev == ptdep);
+ if (ptdep->ix < 0) {
+ Uint64 interval = ptdep->u.bif_invocation.interval;
+ ERTS_PTAB_LIST_ASSERT(interval <= curr_interval);
+ }
+ else {
+ Uint64 s_interval = ptdep->u.element.inserted;
+ Uint64 x_interval = ptdep->u.element.deleted;
+
+ ERTS_PTAB_LIST_ASSERT(s_interval <= x_interval);
+ if (prev_x_interval_p)
+ ERTS_PTAB_LIST_ASSERT(*prev_x_interval_p <= x_interval);
+ prev_x_interval_p = &ptdep->u.element.deleted;
+ ERTS_PTAB_LIST_ASSERT(
+ erts_ptab_is_valid_id(ptdep->u.element.id));
+ ERTS_PTAB_LIST_ASSERT(erts_ptab_id2pix(ptab,
+ ptdep->u.element.id)
+ == ptdep->ix);
+
+ }
+ }
+
+ }
+}
+
+static void
+debug_ptab_list_check_del_free_list(ErtsPTab *ptab,
+ ErtsPTabDeletedElement *free_list)
+{
+ if (ptab->list.data.deleted.start) {
+ ErtsPTabDeletedElement *fptdep;
+ ErtsPTabDeletedElement *ptdep;
+
+ for (fptdep = free_list; fptdep; fptdep = fptdep->next) {
+ for (ptdep = ptab->list.data.deleted.start;
+ ptdep;
+ ptdep = ptdep->next) {
+ ERTS_PTAB_LIST_ASSERT(fptdep != ptdep);
+ }
+ }
+ }
+}
+
+#endif
+
+#if ERTS_PTAB_LIST_BIF_DEBUGLEVEL != 0
+
+static void
+debug_ptab_list_assert_error(char* expr, const char* file, int line, const char *func)
+{
+ fflush(stdout);
+ erts_fprintf(stderr, "%s:%d:%s(): Assertion failed: %s\n",
+ (char *) file, line, (char *) func, expr);
+ fflush(stderr);
+ abort();
+}
+
+#endif
diff --git a/erts/emulator/beam/erl_ptab.h b/erts/emulator/beam/erl_ptab.h
new file mode 100644
index 0000000000..8a130f42a3
--- /dev/null
+++ b/erts/emulator/beam/erl_ptab.h
@@ -0,0 +1,472 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2012. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, 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: Process/Port table implementation.
+ *
+ * Author: Rickard Green
+ */
+
+#ifndef ERL_PTAB_H__
+#define ERL_PTAB_H__
+
+#include "sys.h"
+#include "erl_term.h"
+#include "erl_time.h"
+#include "erl_utils.h"
+#define ERL_THR_PROGRESS_TSD_TYPE_ONLY
+#include "erl_thr_progress.h"
+#undef ERL_THR_PROGRESS_TSD_TYPE_ONLY
+#include "erl_alloc.h"
+#include "erl_monitors.h"
+
+#define ERTS_TRACER_PROC(P) ((P)->common.tracer_proc)
+#define ERTS_TRACE_FLAGS(P) ((P)->common.trace_flags)
+
+#define ERTS_P_LINKS(P) ((P)->common.u.alive.links)
+#define ERTS_P_MONITORS(P) ((P)->common.u.alive.monitors)
+
+#define IS_TRACED(p) \
+ (ERTS_TRACER_PROC((p)) != NIL)
+#define ARE_TRACE_FLAGS_ON(p,tf) \
+ ((ERTS_TRACE_FLAGS((p)) & (tf|F_SENSITIVE)) == (tf))
+#define IS_TRACED_FL(p,tf) \
+ ( IS_TRACED(p) && ARE_TRACE_FLAGS_ON(p,tf) )
+
+typedef struct {
+ Eterm id;
+#ifdef ERTS_SMP
+ erts_atomic32_t refc;
+#endif
+ Eterm tracer_proc;
+ Uint trace_flags;
+ union {
+ /* --- While being alive --- */
+ struct {
+ Uint64 started_interval;
+ struct reg_proc *reg;
+ ErtsLink *links;
+ ErtsMonitor *monitors;
+#ifdef ERTS_SMP
+ ErtsSmpPTimer *ptimer;
+#else
+ ErlTimer tm;
+#endif
+ } alive;
+
+ /* --- While being released --- */
+ ErtsThrPrgrLaterOp release;
+ } u;
+} ErtsPTabElementCommon;
+
+typedef struct ErtsPTabDeletedElement_ ErtsPTabDeletedElement;
+
+typedef struct {
+ erts_smp_rwmtx_t rwmtx;
+ erts_interval_t interval;
+ struct {
+ ErtsPTabDeletedElement *start;
+ ErtsPTabDeletedElement *end;
+ } deleted;
+ int chunks;
+} ErtsPTabListData;
+
+typedef struct {
+#ifdef ARCH_32
+ erts_smp_dw_atomic_t last_data;
+#else
+ erts_smp_atomic_t last_data;
+#endif
+ erts_smp_atomic32_t count;
+} ErtsPTabVolatileData;
+
+typedef struct {
+ erts_smp_atomic_t *tab;
+ Uint32 max;
+ Uint32 tab_cache_lines;
+ Uint32 pix_per_cache_line;
+ Uint32 pix_mask;
+ Uint32 pix_cl_mask;
+ Uint32 pix_cl_shift;
+ Uint32 pix_cli_mask;
+ Uint32 pix_cli_shift;
+ ErtsPTabElementCommon *invalid_element;
+ Eterm invalid_data;
+ void (*release_element)(void *);
+} ErtsPTabReadOnlyData;
+
+typedef struct {
+ /*
+ * Data mainly modified when someone is listing
+ * the content of the table.
+ */
+ union {
+ ErtsPTabListData data;
+ char algn[ERTS_ALC_CACHE_LINE_ALIGN_SIZE(sizeof(ErtsPTabListData))];
+ } list;
+
+ /*
+ * Frequently modified data.
+ */
+ union {
+ ErtsPTabVolatileData tile;
+ char algn[ERTS_ALC_CACHE_LINE_ALIGN_SIZE(sizeof(ErtsPTabVolatileData))];
+ } vola;
+
+ /*
+ * Read only data.
+ */
+ union {
+ ErtsPTabReadOnlyData o;
+ char algn[ERTS_ALC_CACHE_LINE_ALIGN_SIZE(sizeof(ErtsPTabReadOnlyData))];
+ } r;
+} ErtsPTab;
+
+#define ERTS_PTAB_ID_DATA_SIZE 28
+#define ERTS_PTAB_ID_DATA_SHIFT (_TAG_IMMED1_SIZE)
+/* ERTS_PTAB_MAX_SIZE must be a power of 2 */
+#define ERTS_PTAB_MAX_SIZE (SWORD_CONSTANT(1) << 27)
+#if (ERTS_PTAB_MAX_SIZE-1) > MAX_SMALL
+# error "The maximum number of processes/ports must fit in a SMALL."
+#endif
+
+
+/*
+ * Currently pids and ports are allowed.
+ */
+#if _PID_DATA_SIZE != ERTS_PTAB_ID_DATA_SIZE
+# error "Unexpected pid data size"
+#endif
+#if _PID_DATA_SHIFT != ERTS_PTAB_ID_DATA_SHIFT
+# error "Unexpected pid tag size"
+#endif
+#if _PORT_DATA_SIZE != ERTS_PTAB_ID_DATA_SIZE
+# error "Unexpected port data size"
+#endif
+#if _PORT_DATA_SHIFT != ERTS_PTAB_ID_DATA_SHIFT
+# error "Unexpected port tag size"
+#endif
+
+#define ERTS_PTAB_INVALID_ID(TAG) \
+ ((Eterm) \
+ ((((1 << ERTS_PTAB_ID_DATA_SIZE) - 1) << ERTS_PTAB_ID_DATA_SHIFT) \
+ | (TAG)))
+
+#define erts_ptab_is_valid_id(ID) \
+ (is_internal_pid((ID)) || is_internal_port((ID)))
+
+void erts_ptab_init(void);
+void erts_ptab_init_table(ErtsPTab *ptab,
+ ErtsAlcType_t atype,
+ void (*release_element)(void *),
+ ErtsPTabElementCommon *invalid_element,
+ int size,
+ char *name);
+int erts_ptab_new_element(ErtsPTab *ptab,
+ ErtsPTabElementCommon *ptab_el,
+ void *init_arg,
+ void (*init_ptab_el)(void *, Eterm));
+void erts_ptab_delete_element(ErtsPTab *ptab,
+ ErtsPTabElementCommon *ptab_el);
+int erts_ptab_initialized(ErtsPTab *ptab);
+
+ERTS_GLB_INLINE erts_interval_t *erts_ptab_interval(ErtsPTab *ptab);
+ERTS_GLB_INLINE int erts_ptab_max(ErtsPTab *ptab);
+ERTS_GLB_INLINE int erts_ptab_count(ErtsPTab *ptab);
+ERTS_GLB_INLINE Uint erts_ptab_pixdata2data(ErtsPTab *ptab, Eterm pixdata);
+ERTS_GLB_INLINE Uint32 erts_ptab_pixdata2pix(ErtsPTab *ptab, Eterm pixdata);
+ERTS_GLB_INLINE Uint32 erts_ptab_data2pix(ErtsPTab *ptab, Eterm data);
+ERTS_GLB_INLINE Uint erts_ptab_data2pixdata(ErtsPTab *ptab, Eterm data);
+ERTS_GLB_INLINE Eterm erts_ptab_make_id(ErtsPTab *ptab, Eterm data, Eterm tag);
+ERTS_GLB_INLINE int erts_ptab_id2pix(ErtsPTab *ptab, Eterm id);
+ERTS_GLB_INLINE Uint erts_ptab_id2data(ErtsPTab *ptab, Eterm id);
+ERTS_GLB_INLINE erts_aint_t erts_ptab_pix2intptr_nob(ErtsPTab *ptab, int ix);
+ERTS_GLB_INLINE erts_aint_t erts_ptab_pix2intptr_ddrb(ErtsPTab *ptab, int ix);
+ERTS_GLB_INLINE erts_aint_t erts_ptab_pix2intptr_rb(ErtsPTab *ptab, int ix);
+ERTS_GLB_INLINE erts_aint_t erts_ptab_pix2intptr_acqb(ErtsPTab *ptab, int ix);
+ERTS_GLB_INLINE void erts_ptab_inc_refc(ErtsPTabElementCommon *ptab_el);
+ERTS_GLB_INLINE int erts_ptab_dec_test_refc(ErtsPTabElementCommon *ptab_el);
+ERTS_GLB_INLINE int erts_ptab_add_test_refc(ErtsPTabElementCommon *ptab_el,
+ Sint32 add_refc);
+ERTS_GLB_INLINE void erts_ptab_rlock(ErtsPTab *ptab);
+ERTS_GLB_INLINE int erts_ptab_tryrlock(ErtsPTab *ptab);
+ERTS_GLB_INLINE void erts_ptab_runlock(ErtsPTab *ptab);
+ERTS_GLB_INLINE void erts_ptab_rwlock(ErtsPTab *ptab);
+ERTS_GLB_INLINE int erts_ptab_tryrwlock(ErtsPTab *ptab);
+ERTS_GLB_INLINE void erts_ptab_rwunlock(ErtsPTab *ptab);
+ERTS_GLB_INLINE int erts_smp_lc_ptab_is_rlocked(ErtsPTab *ptab);
+ERTS_GLB_INLINE int erts_smp_lc_ptab_is_rwlocked(ErtsPTab *ptab);
+
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+
+ERTS_GLB_INLINE erts_interval_t *
+erts_ptab_interval(ErtsPTab *ptab)
+{
+ return &ptab->list.data.interval;
+}
+
+ERTS_GLB_INLINE int
+erts_ptab_max(ErtsPTab *ptab)
+{
+ int max = ptab->r.o.max;
+ return max == ERTS_PTAB_MAX_SIZE ? max - 1 : max;
+}
+
+ERTS_GLB_INLINE int
+erts_ptab_count(ErtsPTab *ptab)
+{
+ int max = ptab->r.o.max;
+ erts_aint32_t res = erts_smp_atomic32_read_nob(&ptab->vola.tile.count);
+ if (max == ERTS_PTAB_MAX_SIZE) {
+ max--;
+ res--;
+ }
+ if (res > max)
+ return max;
+ ASSERT(res >= 0);
+ return (int) res;
+
+}
+
+ERTS_GLB_INLINE Uint erts_ptab_pixdata2data(ErtsPTab *ptab, Eterm pixdata)
+{
+ Uint32 data = ((Uint32) pixdata) & ~ptab->r.o.pix_mask;
+ data |= (pixdata >> ptab->r.o.pix_cl_shift) & ptab->r.o.pix_cl_mask;
+ data |= (pixdata & ptab->r.o.pix_cli_mask) << ptab->r.o.pix_cli_shift;
+ return data;
+}
+
+ERTS_GLB_INLINE Uint32 erts_ptab_pixdata2pix(ErtsPTab *ptab, Eterm pixdata)
+{
+ return ((Uint32) pixdata) & ptab->r.o.pix_mask;
+}
+
+ERTS_GLB_INLINE Uint32 erts_ptab_data2pix(ErtsPTab *ptab, Eterm data)
+{
+ Uint32 n, pix;
+ n = (Uint32) data;
+ pix = ((n & ptab->r.o.pix_cl_mask) << ptab->r.o.pix_cl_shift);
+ pix += ((n >> ptab->r.o.pix_cli_shift) & ptab->r.o.pix_cli_mask);
+ ASSERT(0 <= pix && pix < ptab->r.o.max);
+ return pix;
+}
+
+ERTS_GLB_INLINE Uint erts_ptab_data2pixdata(ErtsPTab *ptab, Eterm data)
+{
+ Uint pixdata = data & ~((Uint) ptab->r.o.pix_mask);
+ pixdata |= (Uint) erts_ptab_data2pix(ptab, data);
+ ASSERT(data == erts_ptab_pixdata2data(ptab, pixdata));
+ return pixdata;
+}
+
+#if ERTS_SIZEOF_TERM == 8
+
+ERTS_GLB_INLINE Eterm
+erts_ptab_make_id(ErtsPTab *ptab, Eterm data, Eterm tag)
+{
+ HUint huint;
+ Uint32 low_data = (Uint32) data;
+ low_data &= (1 << ERTS_PTAB_ID_DATA_SIZE) - 1;
+ low_data <<= ERTS_PTAB_ID_DATA_SHIFT;
+ huint.hval[ERTS_HUINT_HVAL_HIGH] = erts_ptab_data2pix(ptab, data);
+ huint.hval[ERTS_HUINT_HVAL_LOW] = low_data | ((Uint32) tag);
+ return (Eterm) huint.val;
+}
+
+ERTS_GLB_INLINE int
+erts_ptab_id2pix(ErtsPTab *ptab, Eterm id)
+{
+ HUint huint;
+ huint.val = id;
+ return (int) huint.hval[ERTS_HUINT_HVAL_HIGH];
+}
+
+ERTS_GLB_INLINE Uint
+erts_ptab_id2data(ErtsPTab *ptab, Eterm id)
+{
+ HUint huint;
+ huint.val = id;
+ return (Uint) (huint.hval[ERTS_HUINT_HVAL_LOW] >> ERTS_PTAB_ID_DATA_SHIFT);
+}
+
+#elif ERTS_SIZEOF_TERM == 4
+
+ERTS_GLB_INLINE Eterm
+erts_ptab_make_id(ErtsPTab *ptab, Eterm data, Eterm tag)
+{
+ Eterm id;
+ data &= ((1 << ERTS_PTAB_ID_DATA_SIZE) - 1);
+ id = (Eterm) erts_ptab_data2pixdata(ptab, data);
+ return (id << ERTS_PTAB_ID_DATA_SHIFT) | tag;
+}
+
+ERTS_GLB_INLINE int
+erts_ptab_id2pix(ErtsPTab *ptab, Eterm id)
+{
+ Uint pixdata = (Uint) id;
+ pixdata >>= ERTS_PTAB_ID_DATA_SHIFT;
+ return (int) erts_ptab_pixdata2pix(ptab, pixdata);
+}
+
+ERTS_GLB_INLINE Uint
+erts_ptab_id2data(ErtsPTab *ptab, Eterm id)
+{
+ Uint pixdata = (Uint) id;
+ pixdata >>= ERTS_PTAB_ID_DATA_SHIFT;
+ return erts_ptab_pixdata2data(ptab, pixdata);
+}
+
+#else
+#error "Unsupported size of term"
+#endif
+
+ERTS_GLB_INLINE erts_aint_t erts_ptab_pix2intptr_nob(ErtsPTab *ptab, int ix)
+{
+ ASSERT(0 <= ix && ix < ptab->r.o.max);
+ return erts_smp_atomic_read_nob(&ptab->r.o.tab[ix]);
+}
+
+ERTS_GLB_INLINE erts_aint_t erts_ptab_pix2intptr_ddrb(ErtsPTab *ptab, int ix)
+{
+ ASSERT(0 <= ix && ix < ptab->r.o.max);
+ return erts_smp_atomic_read_ddrb(&ptab->r.o.tab[ix]);
+}
+
+ERTS_GLB_INLINE erts_aint_t erts_ptab_pix2intptr_rb(ErtsPTab *ptab, int ix)
+{
+ ASSERT(0 <= ix && ix < ptab->r.o.max);
+ return erts_smp_atomic_read_rb(&ptab->r.o.tab[ix]);
+}
+
+ERTS_GLB_INLINE erts_aint_t erts_ptab_pix2intptr_acqb(ErtsPTab *ptab, int ix)
+{
+ ASSERT(0 <= ix && ix < ptab->r.o.max);
+ return erts_smp_atomic_read_acqb(&ptab->r.o.tab[ix]);
+}
+
+ERTS_GLB_INLINE void erts_ptab_inc_refc(ErtsPTabElementCommon *ptab_el)
+{
+#ifdef ERTS_SMP
+#ifdef ERTS_ENABLE_LOCK_CHECK
+ erts_aint32_t refc = erts_atomic32_inc_read_nob(&ptab_el->refc);
+ ERTS_SMP_LC_ASSERT(refc > 1);
+#else
+ erts_atomic32_inc_nob(&ptab_el->refc);
+#endif
+#endif
+}
+
+ERTS_GLB_INLINE int erts_ptab_dec_test_refc(ErtsPTabElementCommon *ptab_el)
+{
+#ifdef ERTS_SMP
+ erts_aint32_t refc = erts_atomic32_dec_read_nob(&ptab_el->refc);
+ ERTS_SMP_LC_ASSERT(refc >= 0);
+ return (int) refc;
+#else
+ return 0;
+#endif
+}
+
+ERTS_GLB_INLINE int erts_ptab_add_test_refc(ErtsPTabElementCommon *ptab_el,
+ Sint32 add_refc)
+{
+#ifdef ERTS_SMP
+ erts_aint32_t refc;
+
+#ifndef ERTS_ENABLE_LOCK_CHECK
+ if (add_refc >= 0) {
+ erts_atomic32_add_nob(&ptab_el->refc,
+ (erts_aint32_t) add_refc);
+ return 1;
+ }
+#endif
+
+ refc = erts_atomic32_add_read_nob(&ptab_el->refc,
+ (erts_aint32_t) add_refc);
+ ERTS_SMP_LC_ASSERT(refc >= 0);
+ return (int) refc;
+#else
+ return 0;
+#endif
+}
+
+ERTS_GLB_INLINE void erts_ptab_rlock(ErtsPTab *ptab)
+{
+ erts_smp_rwmtx_rlock(&ptab->list.data.rwmtx);
+}
+
+ERTS_GLB_INLINE int erts_ptab_tryrlock(ErtsPTab *ptab)
+{
+ return erts_smp_rwmtx_tryrlock(&ptab->list.data.rwmtx);
+}
+
+ERTS_GLB_INLINE void erts_ptab_runlock(ErtsPTab *ptab)
+{
+ erts_smp_rwmtx_runlock(&ptab->list.data.rwmtx);
+}
+
+ERTS_GLB_INLINE void erts_ptab_rwlock(ErtsPTab *ptab)
+{
+ erts_smp_rwmtx_rwlock(&ptab->list.data.rwmtx);
+}
+
+ERTS_GLB_INLINE int erts_ptab_tryrwlock(ErtsPTab *ptab)
+{
+ return erts_smp_rwmtx_tryrwlock(&ptab->list.data.rwmtx);
+}
+
+ERTS_GLB_INLINE void erts_ptab_rwunlock(ErtsPTab *ptab)
+{
+ erts_smp_rwmtx_rwunlock(&ptab->list.data.rwmtx);
+}
+
+ERTS_GLB_INLINE int erts_smp_lc_ptab_is_rlocked(ErtsPTab *ptab)
+{
+ return erts_smp_lc_rwmtx_is_rlocked(&ptab->list.data.rwmtx);
+}
+
+ERTS_GLB_INLINE int erts_smp_lc_ptab_is_rwlocked(ErtsPTab *ptab)
+{
+ return erts_smp_lc_rwmtx_is_rwlocked(&ptab->list.data.rwmtx);
+}
+
+#endif
+
+#endif
+
+#if defined(ERTS_PTAB_WANT_BIF_IMPL__) && !defined(ERTS_PTAB_LIST__)
+#define ERTS_PTAB_LIST__
+
+#include "erl_process.h"
+#include "bif.h"
+
+BIF_RETTYPE erts_ptab_list(struct process *c_p, ErtsPTab *ptab);
+
+#endif
+
+#if defined(ERTS_PTAB_WANT_DEBUG_FUNCS__) && !defined(ERTS_PTAB_DEBUG_FUNCS__)
+#define ERTS_PTAB_DEBUG_FUNCS__
+#include "erl_process.h"
+
+/* Debug functions */
+Sint erts_ptab_test_next_id(ErtsPTab *ptab, int set, Uint next);
+Eterm erts_debug_ptab_list(Process *c_p, ErtsPTab *ptab);
+Eterm erts_debug_ptab_list_bif_info(Process *c_p, ErtsPTab *ptab);
+
+#endif
diff --git a/erts/emulator/beam/erl_resolv_dns.c b/erts/emulator/beam/erl_resolv_dns.c
deleted file mode 100644
index 9d76fa89f8..0000000000
--- a/erts/emulator/beam/erl_resolv_dns.c
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * %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%
- */
-
-/*
- * Set this to non-zero value if DNS should be used.
- */
-int erl_use_resolver = 1;
diff --git a/erts/emulator/beam/erl_resolv_nodns.c b/erts/emulator/beam/erl_resolv_nodns.c
deleted file mode 100644
index f14ab68e27..0000000000
--- a/erts/emulator/beam/erl_resolv_nodns.c
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * %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%
- */
-
-/*
- * Set this to non-zero value if DNS should be used.
- */
-int erl_use_resolver = 0;
diff --git a/erts/emulator/beam/erl_sched_spec_pre_alloc.c b/erts/emulator/beam/erl_sched_spec_pre_alloc.c
index 37b186abd9..a490aec734 100644
--- a/erts/emulator/beam/erl_sched_spec_pre_alloc.c
+++ b/erts/emulator/beam/erl_sched_spec_pre_alloc.c
@@ -116,54 +116,84 @@ erts_sspa_create(size_t blk_sz, int pa_size)
return data;
}
-static ERTS_INLINE erts_aint_t
+static ERTS_INLINE void
enqueue_remote_managed_thread(erts_sspa_chunk_header_t *chdr,
erts_sspa_blk_t *this,
- int want_last)
+ int cinit)
{
- erts_aint_t ilast, itmp;
+ erts_aint_t itmp;
+ erts_sspa_blk_t *enq;
erts_atomic_init_nob(&this->next_atmc, ERTS_AINT_NULL);
-
/* Enqueue at end of list... */
- ilast = erts_atomic_read_nob(&chdr->tail.data.last);
- while (1) {
- erts_sspa_blk_t *last = (erts_sspa_blk_t *) ilast;
- itmp = erts_atomic_cmpxchg_mb(&last->next_atmc,
- (erts_aint_t) this,
- ERTS_AINT_NULL);
- if (itmp == ERTS_AINT_NULL)
- break;
- ilast = itmp;
+ enq = (erts_sspa_blk_t *) erts_atomic_read_nob(&chdr->tail.data.last);
+ itmp = erts_atomic_cmpxchg_relb(&enq->next_atmc,
+ (erts_aint_t) this,
+ ERTS_AINT_NULL);
+ if (itmp == ERTS_AINT_NULL) {
+ /* We are required to move last pointer */
+#ifdef DEBUG
+ ASSERT(ERTS_AINT_NULL == erts_atomic_read_nob(&this->next_atmc));
+ ASSERT(((erts_aint_t) enq)
+ == erts_atomic_xchg_relb(&chdr->tail.data.last,
+ (erts_aint_t) this));
+#else
+ erts_atomic_set_relb(&chdr->tail.data.last, (erts_aint_t) this);
+#endif
}
+ else {
+ /*
+ * We *need* to insert element somewhere in between the
+ * last element we read earlier and the actual last element.
+ */
+ int i = cinit;
- /* Move last pointer forward... */
- while (1) {
- erts_aint_t itmp;
- if (want_last) {
- if (erts_atomic_read_rb(&this->next_atmc) != ERTS_AINT_NULL) {
- /* Someone else will move it forward */
- return erts_atomic_read_nob(&chdr->tail.data.last);
+ while (1) {
+ erts_aint_t itmp2;
+ erts_atomic_set_nob(&this->next_atmc, itmp);
+ itmp2 = erts_atomic_cmpxchg_relb(&enq->next_atmc,
+ (erts_aint_t) this,
+ itmp);
+ if (itmp == itmp2)
+ break; /* inserted this */
+ if ((i & 1) == 0)
+ itmp = itmp2;
+ else {
+ enq = (erts_sspa_blk_t *) itmp;
+ itmp = erts_atomic_read_acqb(&enq->next_atmc);
+ ASSERT(itmp != ERTS_AINT_NULL);
}
+ i++;
}
- else {
- if (erts_atomic_read_nob(&this->next_atmc) != ERTS_AINT_NULL) {
- /* Someone else will move it forward */
- return ERTS_AINT_NULL;
- }
+ }
+}
+
+static ERTS_INLINE erts_aint_t
+check_insert_marker(erts_sspa_chunk_header_t *chdr, erts_aint_t ilast)
+{
+ if (!chdr->head.used_marker
+ && chdr->head.unref_end == (erts_sspa_blk_t *) ilast) {
+ erts_aint_t itmp;
+ erts_sspa_blk_t *last = (erts_sspa_blk_t *) ilast;
+
+ erts_atomic_init_nob(&chdr->tail.data.marker.next_atmc, ERTS_AINT_NULL);
+ itmp = erts_atomic_cmpxchg_relb(&last->next_atmc,
+ (erts_aint_t) &chdr->tail.data.marker,
+ ERTS_AINT_NULL);
+ if (itmp == ERTS_AINT_NULL) {
+ ilast = (erts_aint_t) &chdr->tail.data.marker;
+ chdr->head.used_marker = !0;
+ erts_atomic_set_relb(&chdr->tail.data.last, ilast);
}
- itmp = erts_atomic_cmpxchg_mb(&chdr->tail.data.last,
- (erts_aint_t) this,
- ilast);
- if (ilast == itmp)
- return want_last ? (erts_aint_t) this : ERTS_AINT_NULL;
- ilast = itmp;
}
+ return ilast;
}
void
-erts_sspa_remote_free(erts_sspa_chunk_header_t *chdr, erts_sspa_blk_t *blk)
+erts_sspa_remote_free(erts_sspa_chunk_header_t *chdr,
+ erts_sspa_blk_t *blk,
+ int cinit)
{
int um_refc_ix = 0;
int managed_thread = erts_thr_progress_is_managed_thread();
@@ -180,7 +210,7 @@ erts_sspa_remote_free(erts_sspa_chunk_header_t *chdr, erts_sspa_blk_t *blk)
}
}
- (void) enqueue_remote_managed_thread(chdr, blk, 0);
+ enqueue_remote_managed_thread(chdr, blk, cinit);
if (!managed_thread)
erts_atomic_dec_relb(&chdr->tail.data.um_refc[um_refc_ix]);
@@ -208,24 +238,17 @@ fetch_remote(erts_sspa_chunk_header_t *chdr, int max)
int um_refc_ix;
chdr->head.next.thr_progress_reached = 1;
um_refc_ix = chdr->head.next.um_refc_ix;
- if (erts_atomic_read_acqb(&chdr->tail.data.um_refc[um_refc_ix]) == 0) {
+ if (erts_atomic_read_nob(&chdr->tail.data.um_refc[um_refc_ix]) == 0) {
+
+ ETHR_MEMBAR(ETHR_LoadLoad|ETHR_LoadStore);
/* Move unreferenced end pointer forward... */
chdr->head.unref_end = chdr->head.next.unref_end;
- if (!chdr->head.used_marker
- && chdr->head.unref_end == (erts_sspa_blk_t *) ilast) {
- /* Need to equeue marker */
- chdr->head.used_marker = 1;
- ilast = enqueue_remote_managed_thread(chdr,
- &chdr->tail.data.marker,
- 1);
- }
+ ilast = check_insert_marker(chdr, ilast);
- if (chdr->head.unref_end == (erts_sspa_blk_t *) ilast)
- ERTS_THR_MEMORY_BARRIER;
- else {
+ if (chdr->head.unref_end != (erts_sspa_blk_t *) ilast) {
chdr->head.next.unref_end = (erts_sspa_blk_t *) ilast;
chdr->head.next.thr_progress = erts_thr_progress_later(NULL);
erts_atomic32_set_relb(&chdr->tail.data.um_refc_ix,
diff --git a/erts/emulator/beam/erl_sched_spec_pre_alloc.h b/erts/emulator/beam/erl_sched_spec_pre_alloc.h
index d36066c399..9144c73acd 100644
--- a/erts/emulator/beam/erl_sched_spec_pre_alloc.h
+++ b/erts/emulator/beam/erl_sched_spec_pre_alloc.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2011. All Rights Reserved.
+ * Copyright Ericsson AB 2011-2012. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -142,7 +142,8 @@ check_local_list(erts_sspa_chunk_header_t *chdr)
erts_sspa_data_t *erts_sspa_create(size_t blk_sz,
int pa_size);
void erts_sspa_remote_free(erts_sspa_chunk_header_t *chdr,
- erts_sspa_blk_t *blk);
+ erts_sspa_blk_t *blk,
+ int cinit);
erts_sspa_blk_t *erts_sspa_process_remote_frees(erts_sspa_chunk_header_t *chdr,
erts_sspa_blk_t *old_res);
@@ -216,7 +217,7 @@ erts_sspa_free(erts_sspa_data_t *data, int cix, char *cblk)
chdr = &chnk->aligned.header;
if (chnk_cix != cix) {
/* Remote chunk */
- erts_sspa_remote_free(chdr, blk);
+ erts_sspa_remote_free(chdr, blk, chnk_cix - cix);
}
else {
/* Local chunk */
diff --git a/erts/emulator/beam/erl_smp.h b/erts/emulator/beam/erl_smp.h
index a32e9d9d7c..34c90c0bda 100644
--- a/erts/emulator/beam/erl_smp.h
+++ b/erts/emulator/beam/erl_smp.h
@@ -274,6 +274,7 @@ ERTS_GLB_INLINE void erts_smp_thr_sigwait(const sigset_t *set, int *sig);
#define erts_smp_atomic_read_band_nob erts_atomic_read_band_nob
#define erts_smp_atomic_xchg_nob erts_atomic_xchg_nob
#define erts_smp_atomic_cmpxchg_nob erts_atomic_cmpxchg_nob
+#define erts_smp_atomic_read_bset_nob erts_atomic_read_bset_nob
#define erts_smp_atomic_init_mb erts_atomic_init_mb
#define erts_smp_atomic_set_mb erts_atomic_set_mb
@@ -288,6 +289,7 @@ ERTS_GLB_INLINE void erts_smp_thr_sigwait(const sigset_t *set, int *sig);
#define erts_smp_atomic_read_band_mb erts_atomic_read_band_mb
#define erts_smp_atomic_xchg_mb erts_atomic_xchg_mb
#define erts_smp_atomic_cmpxchg_mb erts_atomic_cmpxchg_mb
+#define erts_smp_atomic_read_bset_mb erts_atomic_read_bset_mb
#define erts_smp_atomic_init_acqb erts_atomic_init_acqb
#define erts_smp_atomic_set_acqb erts_atomic_set_acqb
@@ -302,6 +304,7 @@ ERTS_GLB_INLINE void erts_smp_thr_sigwait(const sigset_t *set, int *sig);
#define erts_smp_atomic_read_band_acqb erts_atomic_read_band_acqb
#define erts_smp_atomic_xchg_acqb erts_atomic_xchg_acqb
#define erts_smp_atomic_cmpxchg_acqb erts_atomic_cmpxchg_acqb
+#define erts_smp_atomic_read_bset_acqb erts_atomic_read_bset_acqb
#define erts_smp_atomic_init_relb erts_atomic_init_relb
#define erts_smp_atomic_set_relb erts_atomic_set_relb
@@ -316,6 +319,7 @@ ERTS_GLB_INLINE void erts_smp_thr_sigwait(const sigset_t *set, int *sig);
#define erts_smp_atomic_read_band_relb erts_atomic_read_band_relb
#define erts_smp_atomic_xchg_relb erts_atomic_xchg_relb
#define erts_smp_atomic_cmpxchg_relb erts_atomic_cmpxchg_relb
+#define erts_smp_atomic_read_bset_relb erts_atomic_read_bset_relb
#define erts_smp_atomic_init_ddrb erts_atomic_init_ddrb
#define erts_smp_atomic_set_ddrb erts_atomic_set_ddrb
@@ -330,6 +334,7 @@ ERTS_GLB_INLINE void erts_smp_thr_sigwait(const sigset_t *set, int *sig);
#define erts_smp_atomic_read_band_ddrb erts_atomic_read_band_ddrb
#define erts_smp_atomic_xchg_ddrb erts_atomic_xchg_ddrb
#define erts_smp_atomic_cmpxchg_ddrb erts_atomic_cmpxchg_ddrb
+#define erts_smp_atomic_read_bset_ddrb erts_atomic_read_bset_ddrb
#define erts_smp_atomic_init_rb erts_atomic_init_rb
#define erts_smp_atomic_set_rb erts_atomic_set_rb
@@ -344,6 +349,7 @@ ERTS_GLB_INLINE void erts_smp_thr_sigwait(const sigset_t *set, int *sig);
#define erts_smp_atomic_read_band_rb erts_atomic_read_band_rb
#define erts_smp_atomic_xchg_rb erts_atomic_xchg_rb
#define erts_smp_atomic_cmpxchg_rb erts_atomic_cmpxchg_rb
+#define erts_smp_atomic_read_bset_rb erts_atomic_read_bset_rb
#define erts_smp_atomic_init_wb erts_atomic_init_wb
#define erts_smp_atomic_set_wb erts_atomic_set_wb
@@ -358,6 +364,7 @@ ERTS_GLB_INLINE void erts_smp_thr_sigwait(const sigset_t *set, int *sig);
#define erts_smp_atomic_read_band_wb erts_atomic_read_band_wb
#define erts_smp_atomic_xchg_wb erts_atomic_xchg_wb
#define erts_smp_atomic_cmpxchg_wb erts_atomic_cmpxchg_wb
+#define erts_smp_atomic_read_bset_wb erts_atomic_read_bset_wb
/* 32-bit atomics */
@@ -374,6 +381,7 @@ ERTS_GLB_INLINE void erts_smp_thr_sigwait(const sigset_t *set, int *sig);
#define erts_smp_atomic32_read_band_nob erts_atomic32_read_band_nob
#define erts_smp_atomic32_xchg_nob erts_atomic32_xchg_nob
#define erts_smp_atomic32_cmpxchg_nob erts_atomic32_cmpxchg_nob
+#define erts_smp_atomic32_read_bset_nob erts_atomic32_read_bset_nob
#define erts_smp_atomic32_init_mb erts_atomic32_init_mb
#define erts_smp_atomic32_set_mb erts_atomic32_set_mb
@@ -388,6 +396,7 @@ ERTS_GLB_INLINE void erts_smp_thr_sigwait(const sigset_t *set, int *sig);
#define erts_smp_atomic32_read_band_mb erts_atomic32_read_band_mb
#define erts_smp_atomic32_xchg_mb erts_atomic32_xchg_mb
#define erts_smp_atomic32_cmpxchg_mb erts_atomic32_cmpxchg_mb
+#define erts_smp_atomic32_read_bset_mb erts_atomic32_read_bset_mb
#define erts_smp_atomic32_init_acqb erts_atomic32_init_acqb
#define erts_smp_atomic32_set_acqb erts_atomic32_set_acqb
@@ -402,6 +411,7 @@ ERTS_GLB_INLINE void erts_smp_thr_sigwait(const sigset_t *set, int *sig);
#define erts_smp_atomic32_read_band_acqb erts_atomic32_read_band_acqb
#define erts_smp_atomic32_xchg_acqb erts_atomic32_xchg_acqb
#define erts_smp_atomic32_cmpxchg_acqb erts_atomic32_cmpxchg_acqb
+#define erts_smp_atomic32_read_bset_acqb erts_atomic32_read_bset_acqb
#define erts_smp_atomic32_init_relb erts_atomic32_init_relb
#define erts_smp_atomic32_set_relb erts_atomic32_set_relb
@@ -416,6 +426,7 @@ ERTS_GLB_INLINE void erts_smp_thr_sigwait(const sigset_t *set, int *sig);
#define erts_smp_atomic32_read_band_relb erts_atomic32_read_band_relb
#define erts_smp_atomic32_xchg_relb erts_atomic32_xchg_relb
#define erts_smp_atomic32_cmpxchg_relb erts_atomic32_cmpxchg_relb
+#define erts_smp_atomic32_read_bset_relb erts_atomic32_read_bset_relb
#define erts_smp_atomic32_init_ddrb erts_atomic32_init_ddrb
#define erts_smp_atomic32_set_ddrb erts_atomic32_set_ddrb
@@ -430,6 +441,7 @@ ERTS_GLB_INLINE void erts_smp_thr_sigwait(const sigset_t *set, int *sig);
#define erts_smp_atomic32_read_band_ddrb erts_atomic32_read_band_ddrb
#define erts_smp_atomic32_xchg_ddrb erts_atomic32_xchg_ddrb
#define erts_smp_atomic32_cmpxchg_ddrb erts_atomic32_cmpxchg_ddrb
+#define erts_smp_atomic32_read_bset_ddrb erts_atomic32_read_bset_ddrb
#define erts_smp_atomic32_init_rb erts_atomic32_init_rb
#define erts_smp_atomic32_set_rb erts_atomic32_set_rb
@@ -444,6 +456,7 @@ ERTS_GLB_INLINE void erts_smp_thr_sigwait(const sigset_t *set, int *sig);
#define erts_smp_atomic32_read_band_rb erts_atomic32_read_band_rb
#define erts_smp_atomic32_xchg_rb erts_atomic32_xchg_rb
#define erts_smp_atomic32_cmpxchg_rb erts_atomic32_cmpxchg_rb
+#define erts_smp_atomic32_read_bset_rb erts_atomic32_read_bset_rb
#define erts_smp_atomic32_init_wb erts_atomic32_init_wb
#define erts_smp_atomic32_set_wb erts_atomic32_set_wb
@@ -458,6 +471,7 @@ ERTS_GLB_INLINE void erts_smp_thr_sigwait(const sigset_t *set, int *sig);
#define erts_smp_atomic32_read_band_wb erts_atomic32_read_band_wb
#define erts_smp_atomic32_xchg_wb erts_atomic32_xchg_wb
#define erts_smp_atomic32_cmpxchg_wb erts_atomic32_cmpxchg_wb
+#define erts_smp_atomic32_read_bset_wb erts_atomic32_read_bset_wb
#else /* !ERTS_SMP */
@@ -513,6 +527,7 @@ ERTS_GLB_INLINE void erts_smp_thr_sigwait(const sigset_t *set, int *sig);
#define erts_smp_atomic_read_band_nob erts_no_atomic_read_band
#define erts_smp_atomic_xchg_nob erts_no_atomic_xchg
#define erts_smp_atomic_cmpxchg_nob erts_no_atomic_cmpxchg
+#define erts_smp_atomic_read_bset_nob erts_no_atomic_read_bset
#define erts_smp_atomic_init_mb erts_no_atomic_set
#define erts_smp_atomic_set_mb erts_no_atomic_set
@@ -527,6 +542,7 @@ ERTS_GLB_INLINE void erts_smp_thr_sigwait(const sigset_t *set, int *sig);
#define erts_smp_atomic_read_band_mb erts_no_atomic_read_band
#define erts_smp_atomic_xchg_mb erts_no_atomic_xchg
#define erts_smp_atomic_cmpxchg_mb erts_no_atomic_cmpxchg
+#define erts_smp_atomic_read_bset_mb erts_no_atomic_read_bset
#define erts_smp_atomic_init_acqb erts_no_atomic_set
#define erts_smp_atomic_set_acqb erts_no_atomic_set
@@ -541,6 +557,7 @@ ERTS_GLB_INLINE void erts_smp_thr_sigwait(const sigset_t *set, int *sig);
#define erts_smp_atomic_read_band_acqb erts_no_atomic_read_band
#define erts_smp_atomic_xchg_acqb erts_no_atomic_xchg
#define erts_smp_atomic_cmpxchg_acqb erts_no_atomic_cmpxchg
+#define erts_smp_atomic_read_bset_acqb erts_no_atomic_read_bset
#define erts_smp_atomic_init_relb erts_no_atomic_set
#define erts_smp_atomic_set_relb erts_no_atomic_set
@@ -555,6 +572,7 @@ ERTS_GLB_INLINE void erts_smp_thr_sigwait(const sigset_t *set, int *sig);
#define erts_smp_atomic_read_band_relb erts_no_atomic_read_band
#define erts_smp_atomic_xchg_relb erts_no_atomic_xchg
#define erts_smp_atomic_cmpxchg_relb erts_no_atomic_cmpxchg
+#define erts_smp_atomic_read_bset_relb erts_no_atomic_read_bset
#define erts_smp_atomic_init_ddrb erts_no_atomic_set
#define erts_smp_atomic_set_ddrb erts_no_atomic_set
@@ -569,6 +587,7 @@ ERTS_GLB_INLINE void erts_smp_thr_sigwait(const sigset_t *set, int *sig);
#define erts_smp_atomic_read_band_ddrb erts_no_atomic_read_band
#define erts_smp_atomic_xchg_ddrb erts_no_atomic_xchg
#define erts_smp_atomic_cmpxchg_ddrb erts_no_atomic_cmpxchg
+#define erts_smp_atomic_read_bset_ddrb erts_no_atomic_read_bset
#define erts_smp_atomic_init_rb erts_no_atomic_set
#define erts_smp_atomic_set_rb erts_no_atomic_set
@@ -583,6 +602,7 @@ ERTS_GLB_INLINE void erts_smp_thr_sigwait(const sigset_t *set, int *sig);
#define erts_smp_atomic_read_band_rb erts_no_atomic_read_band
#define erts_smp_atomic_xchg_rb erts_no_atomic_xchg
#define erts_smp_atomic_cmpxchg_rb erts_no_atomic_cmpxchg
+#define erts_smp_atomic_read_bset_rb erts_no_atomic_read_bset
#define erts_smp_atomic_init_wb erts_no_atomic_set
#define erts_smp_atomic_set_wb erts_no_atomic_set
@@ -597,6 +617,7 @@ ERTS_GLB_INLINE void erts_smp_thr_sigwait(const sigset_t *set, int *sig);
#define erts_smp_atomic_read_band_wb erts_no_atomic_read_band
#define erts_smp_atomic_xchg_wb erts_no_atomic_xchg
#define erts_smp_atomic_cmpxchg_wb erts_no_atomic_cmpxchg
+#define erts_smp_atomic_read_bset_wb erts_no_atomic_read_bset
/* 32-bit atomics */
@@ -613,6 +634,7 @@ ERTS_GLB_INLINE void erts_smp_thr_sigwait(const sigset_t *set, int *sig);
#define erts_smp_atomic32_read_band_nob erts_no_atomic32_read_band
#define erts_smp_atomic32_xchg_nob erts_no_atomic32_xchg
#define erts_smp_atomic32_cmpxchg_nob erts_no_atomic32_cmpxchg
+#define erts_smp_atomic32_read_bset_nob erts_no_atomic32_read_bset
#define erts_smp_atomic32_init_mb erts_no_atomic32_set
#define erts_smp_atomic32_set_mb erts_no_atomic32_set
@@ -627,6 +649,7 @@ ERTS_GLB_INLINE void erts_smp_thr_sigwait(const sigset_t *set, int *sig);
#define erts_smp_atomic32_read_band_mb erts_no_atomic32_read_band
#define erts_smp_atomic32_xchg_mb erts_no_atomic32_xchg
#define erts_smp_atomic32_cmpxchg_mb erts_no_atomic32_cmpxchg
+#define erts_smp_atomic32_read_bset_mb erts_no_atomic32_read_bset
#define erts_smp_atomic32_init_acqb erts_no_atomic32_set
#define erts_smp_atomic32_set_acqb erts_no_atomic32_set
@@ -641,6 +664,7 @@ ERTS_GLB_INLINE void erts_smp_thr_sigwait(const sigset_t *set, int *sig);
#define erts_smp_atomic32_read_band_acqb erts_no_atomic32_read_band
#define erts_smp_atomic32_xchg_acqb erts_no_atomic32_xchg
#define erts_smp_atomic32_cmpxchg_acqb erts_no_atomic32_cmpxchg
+#define erts_smp_atomic32_read_bset_acqb erts_no_atomic32_read_bset
#define erts_smp_atomic32_init_relb erts_no_atomic32_set
#define erts_smp_atomic32_set_relb erts_no_atomic32_set
@@ -655,6 +679,7 @@ ERTS_GLB_INLINE void erts_smp_thr_sigwait(const sigset_t *set, int *sig);
#define erts_smp_atomic32_read_band_relb erts_no_atomic32_read_band
#define erts_smp_atomic32_xchg_relb erts_no_atomic32_xchg
#define erts_smp_atomic32_cmpxchg_relb erts_no_atomic32_cmpxchg
+#define erts_smp_atomic32_read_bset_relb erts_no_atomic32_read_bset
#define erts_smp_atomic32_init_ddrb erts_no_atomic32_set
#define erts_smp_atomic32_set_ddrb erts_no_atomic32_set
@@ -669,6 +694,7 @@ ERTS_GLB_INLINE void erts_smp_thr_sigwait(const sigset_t *set, int *sig);
#define erts_smp_atomic32_read_band_ddrb erts_no_atomic32_read_band
#define erts_smp_atomic32_xchg_ddrb erts_no_atomic32_xchg
#define erts_smp_atomic32_cmpxchg_ddrb erts_no_atomic32_cmpxchg
+#define erts_smp_atomic32_read_bset_ddrb erts_no_atomic32_read_bset
#define erts_smp_atomic32_init_rb erts_no_atomic32_set
#define erts_smp_atomic32_set_rb erts_no_atomic32_set
@@ -683,6 +709,7 @@ ERTS_GLB_INLINE void erts_smp_thr_sigwait(const sigset_t *set, int *sig);
#define erts_smp_atomic32_read_band_rb erts_no_atomic32_read_band
#define erts_smp_atomic32_xchg_rb erts_no_atomic32_xchg
#define erts_smp_atomic32_cmpxchg_rb erts_no_atomic32_cmpxchg
+#define erts_smp_atomic32_read_bset_rb erts_no_atomic32_read_bset
#define erts_smp_atomic32_init_wb erts_no_atomic32_set
#define erts_smp_atomic32_set_wb erts_no_atomic32_set
@@ -697,6 +724,7 @@ ERTS_GLB_INLINE void erts_smp_thr_sigwait(const sigset_t *set, int *sig);
#define erts_smp_atomic32_read_band_wb erts_no_atomic32_read_band
#define erts_smp_atomic32_xchg_wb erts_no_atomic32_xchg
#define erts_smp_atomic32_cmpxchg_wb erts_no_atomic32_cmpxchg
+#define erts_smp_atomic32_read_bset_wb erts_no_atomic32_read_bset
#endif /* !ERTS_SMP */
diff --git a/erts/emulator/beam/erl_sys_driver.h b/erts/emulator/beam/erl_sys_driver.h
index d429d0ce96..b991a2840c 100644
--- a/erts/emulator/beam/erl_sys_driver.h
+++ b/erts/emulator/beam/erl_sys_driver.h
@@ -31,7 +31,6 @@
#define ERL_SYS_DRV
typedef long ErlDrvEvent; /* An event to be selected on. */
-typedef long ErlDrvPort; /* A port descriptor. */
/* typedef struct _SysDriverOpts SysDriverOpts; defined in sys.h */
diff --git a/erts/emulator/beam/erl_term.c b/erts/emulator/beam/erl_term.c
index f77e8b798f..4587cd84d1 100644
--- a/erts/emulator/beam/erl_term.c
+++ b/erts/emulator/beam/erl_term.c
@@ -105,7 +105,7 @@ unsigned tag_val_def(Wterm x)
break;
}
}
- sprintf(msg, "tag_val_def: %#lx", (unsigned long) x);
+ erts_snprintf(msg, sizeof(msg), "tag_val_def: %#lx", (unsigned long) x);
et_abort(msg, file, line);
#undef file
#undef line
@@ -133,7 +133,7 @@ ET_DEFINE_CHECKED(Uint,unsigned_val,Eterm,is_small);
ET_DEFINE_CHECKED(Sint,signed_val,Eterm,is_small);
ET_DEFINE_CHECKED(Uint,atom_val,Eterm,is_atom);
ET_DEFINE_CHECKED(Uint,header_arity,Eterm,is_header);
-ET_DEFINE_CHECKED(Uint,arityval,Eterm,is_arity_value);
+ET_DEFINE_CHECKED(Uint,arityval,Eterm,is_sane_arity_value);
ET_DEFINE_CHECKED(Uint,thing_arityval,Eterm,is_thing);
ET_DEFINE_CHECKED(Uint,thing_subtag,Eterm,is_thing);
ET_DEFINE_CHECKED(Eterm*,binary_val,Wterm,is_binary);
@@ -144,9 +144,7 @@ ET_DEFINE_CHECKED(Uint,bignum_header_arity,Eterm,_is_bignum_header);
ET_DEFINE_CHECKED(Eterm*,big_val,Wterm,is_big);
ET_DEFINE_CHECKED(Eterm*,float_val,Wterm,is_float);
ET_DEFINE_CHECKED(Eterm*,tuple_val,Wterm,is_tuple);
-ET_DEFINE_CHECKED(Uint,internal_pid_data,Eterm,is_internal_pid);
ET_DEFINE_CHECKED(struct erl_node_*,internal_pid_node,Eterm,is_internal_pid);
-ET_DEFINE_CHECKED(Uint,internal_port_data,Eterm,is_internal_port);
ET_DEFINE_CHECKED(struct erl_node_*,internal_port_node,Eterm,is_internal_port);
ET_DEFINE_CHECKED(Eterm*,internal_ref_val,Wterm,is_internal_ref);
ET_DEFINE_CHECKED(Uint,internal_ref_data_words,Wterm,is_internal_ref);
diff --git a/erts/emulator/beam/erl_term.h b/erts/emulator/beam/erl_term.h
index c270d13365..fb3ef9cd6c 100644
--- a/erts/emulator/beam/erl_term.h
+++ b/erts/emulator/beam/erl_term.h
@@ -300,8 +300,17 @@ _ET_DECLARE_CHECKED(Uint,header_arity,Eterm)
#define header_arity(x) _ET_APPLY(header_arity,(x))
/* arityval access methods */
+/* Erlang Spec. 4.7.3 defines max arity to 65535
+ * we will however enforce max arity of 16777215 (24 bits)
+ * (checked in bifs and asserted in debug)
+ */
+#define MAX_ARITYVAL ((((Uint)1) << 24) - 1)
+#define ERTS_MAX_TUPLE_SIZE MAX_ARITYVAL
+
#define make_arityval(sz) _make_header((sz),_TAG_HEADER_ARITYVAL)
#define is_arity_value(x) (((x) & _TAG_HEADER_MASK) == _TAG_HEADER_ARITYVAL)
+#define is_sane_arity_value(x) ((((x) & _TAG_HEADER_MASK) == _TAG_HEADER_ARITYVAL) && \
+ (((x) >> _HEADER_ARITY_OFFS) <= MAX_ARITYVAL))
#define is_not_arity_value(x) (!is_arity_value((x)))
#define _unchecked_arityval(x) _unchecked_header_arity((x))
_ET_DECLARE_CHECKED(Uint,arityval,Eterm)
@@ -542,12 +551,6 @@ _ET_DECLARE_CHECKED(Eterm*,tuple_val,Wterm)
#define _GETBITS(X,Pos,Size) (((X) >> (Pos)) & ~(~((Uint) 0) << (Size)))
/*
- * Observe! New layout for pids, ports and references in R9 (see also note
- * in erl_node_container_utils.h).
- */
-
-
-/*
* Creation in node specific data (pids, ports, refs)
*/
@@ -584,7 +587,6 @@ _ET_DECLARE_CHECKED(Eterm*,tuple_val,Wterm)
*
*/
-#define _PID_R9_SER_SIZE 3
#define _PID_SER_SIZE (_PID_DATA_SIZE - _PID_NUM_SIZE)
#define _PID_NUM_SIZE 15
@@ -598,23 +600,13 @@ _ET_DECLARE_CHECKED(Eterm*,tuple_val,Wterm)
#define make_pid_data(Ser, Num) \
((Uint) ((Ser) << _PID_NUM_SIZE | (Num)))
-#define make_internal_pid(X) \
- ((Eterm) (((X) << _PID_DATA_SHIFT) | _TAG_IMMED1_PID))
-
#define is_internal_pid(x) (((x) & _TAG_IMMED1_MASK) == _TAG_IMMED1_PID)
#define is_not_internal_pid(x) (!is_internal_pid((x)))
-#define _unchecked_internal_pid_data(x) _GET_PID_DATA((x))
-_ET_DECLARE_CHECKED(Uint,internal_pid_data,Eterm)
-#define internal_pid_data(x) _ET_APPLY(internal_pid_data,(x))
-
#define _unchecked_internal_pid_node(x) erts_this_node
_ET_DECLARE_CHECKED(struct erl_node_*,internal_pid_node,Eterm)
#define internal_pid_node(x) _ET_APPLY(internal_pid_node,(x))
-#define internal_pid_number(x) _GET_PID_NUM(internal_pid_data((x)))
-#define internal_pid_serial(x) _GET_PID_SER(internal_pid_data((x)))
-
#define internal_pid_data_words(x) (1)
/*
@@ -644,7 +636,6 @@ _ET_DECLARE_CHECKED(struct erl_node_*,internal_pid_node,Eterm)
* N : node number
*
*/
-#define _PORT_R9_NUM_SIZE 18
#define _PORT_NUM_SIZE _PORT_DATA_SIZE
#define _PORT_DATA_SIZE 28
@@ -654,18 +645,9 @@ _ET_DECLARE_CHECKED(struct erl_node_*,internal_pid_node,Eterm)
#define _GET_PORT_NUM(X) _GETBITS((X), 0, _PORT_NUM_SIZE)
-#define make_internal_port(X) \
- ((Eterm) (((X) << _PORT_DATA_SHIFT) | _TAG_IMMED1_PORT))
-
#define is_internal_port(x) (((x) & _TAG_IMMED1_MASK) == _TAG_IMMED1_PORT)
#define is_not_internal_port(x) (!is_internal_port(x))
-#define _unchecked_internal_port_data(x) _GET_PORT_DATA((x))
-_ET_DECLARE_CHECKED(Uint,internal_port_data,Eterm)
-#define internal_port_data(x) _ET_APPLY(internal_port_data,(x))
-
-#define internal_port_number(x) _GET_PORT_NUM(internal_port_data((x)))
-
#define _unchecked_internal_port_node(x) erts_this_node
_ET_DECLARE_CHECKED(struct erl_node_*,internal_port_node,Eterm)
#define internal_port_node(x) _ET_APPLY(internal_port_node,(x))
diff --git a/erts/emulator/beam/erl_thr_progress.c b/erts/emulator/beam/erl_thr_progress.c
index 88524bdd4c..9678d7e08b 100644
--- a/erts/emulator/beam/erl_thr_progress.c
+++ b/erts/emulator/beam/erl_thr_progress.c
@@ -96,17 +96,14 @@
#define ERTS_THR_PRGR_LFLG_BLOCK (((erts_aint32_t) 1) << 31)
#define ERTS_THR_PRGR_LFLG_NO_LEADER (((erts_aint32_t) 1) << 30)
-#define ERTS_THR_PRGR_LFLG_ACTIVE_MASK (~(ERTS_THR_PRGR_LFLG_NO_LEADER \
- | ERTS_THR_PRGR_LFLG_BLOCK))
+#define ERTS_THR_PRGR_LFLG_WAITING_UM (((erts_aint32_t) 1) << 29)
+#define ERTS_THR_PRGR_LFLG_ACTIVE_MASK (~(ERTS_THR_PRGR_LFLG_NO_LEADER \
+ | ERTS_THR_PRGR_LFLG_BLOCK \
+ | ERTS_THR_PRGR_LFLG_WAITING_UM))
-#define ERTS_THR_PRGR_LFLGS_ACTIVE(LFLGS) \
+#define ERTS_THR_PRGR_LFLGS_ACTIVE(LFLGS) \
((LFLGS) & ERTS_THR_PRGR_LFLG_ACTIVE_MASK)
-#define ERTS_THR_PRGR_LFLGS_ALL_WAITING(LFLGS) \
- (((LFLGS) & (ERTS_THR_PRGR_LFLG_NO_LEADER \
- |ERTS_THR_PRGR_LFLG_ACTIVE_MASK)) \
- == ERTS_THR_PRGR_LFLG_NO_LEADER)
-
/*
* We use a 64-bit value for thread progress. By this wrapping of
* the thread progress will more or less never occur.
@@ -262,6 +259,11 @@ typedef struct {
erts_atomic32_t managed_count;
erts_atomic32_t managed_id;
erts_atomic32_t unmanaged_id;
+ int chk_next_ix;
+ struct {
+ int waiting;
+ erts_atomic32_t current;
+ } umrefc_ix;
} ErtsThrPrgrMiscData;
typedef struct {
@@ -276,12 +278,18 @@ typedef union {
char align__[ERTS_ALC_CACHE_LINE_ALIGN_SIZE(sizeof(ErtsThrPrgrElement))];
} ErtsThrPrgrArray;
+typedef union {
+ erts_atomic_t refc;
+ char align__[ERTS_ALC_CACHE_LINE_ALIGN_SIZE(sizeof(erts_atomic_t))];
+} ErtsThrPrgrUnmanagedRefc;
+
typedef struct {
union {
ErtsThrPrgrMiscData data;
char align__[ERTS_ALC_CACHE_LINE_ALIGN_SIZE(
sizeof(ErtsThrPrgrMiscData))];
} misc;
+ ErtsThrPrgrUnmanagedRefc umrefc[2];
ErtsThrPrgrArray *thr;
struct {
int no;
@@ -346,7 +354,9 @@ init_tmp_thr_prgr_data(ErtsThrPrgrData *tpd)
tpd->is_managed = 0;
tpd->is_blocking = 0;
tpd->is_temporary = 1;
-
+#ifdef ERTS_ENABLE_LOCK_CHECK
+ tpd->is_delaying = 0;
+#endif
erts_tsd_set(erts_thr_prgr_data_key__, (void *) tpd);
}
@@ -461,6 +471,12 @@ erts_thr_progress_init(int no_schedulers, int managed, int unmanaged)
erts_atomic32_init_nob(&intrnl->misc.data.managed_count, 0);
erts_atomic32_init_nob(&intrnl->misc.data.managed_id, no_schedulers);
erts_atomic32_init_nob(&intrnl->misc.data.unmanaged_id, -1);
+ intrnl->misc.data.chk_next_ix = 0;
+ intrnl->misc.data.umrefc_ix.waiting = -1;
+ erts_atomic32_init_nob(&intrnl->misc.data.umrefc_ix.current, 0);
+
+ erts_atomic_init_nob(&intrnl->umrefc[0].refc, (erts_aint_t) 0);
+ erts_atomic_init_nob(&intrnl->umrefc[1].refc, (erts_aint_t) 0);
intrnl->thr = (ErtsThrPrgrArray *) ptr;
ptr += thr_arr_sz;
@@ -547,6 +563,9 @@ erts_thr_progress_register_unmanaged_thread(ErtsThrPrgrCallbacks *callbacks)
tpd->is_managed = 0;
tpd->is_blocking = is_blocking;
tpd->is_temporary = 0;
+#ifdef ERTS_ENABLE_LOCK_CHECK
+ tpd->is_delaying = 0;
+#endif
ASSERT(tpd->id >= 0);
if (tpd->id >= intrnl->unmanaged.no)
erl_exit(ERTS_ABORT_EXIT,
@@ -600,6 +619,9 @@ erts_thr_progress_register_managed_thread(ErtsSchedulerData *esdp,
tpd->is_managed = 1;
tpd->is_blocking = is_blocking;
tpd->is_temporary = 0;
+#ifdef ERTS_ENABLE_LOCK_CHECK
+ tpd->is_delaying = 1;
+#endif
init_wakeup_request_array(&tpd->wakeup_request[0]);
@@ -607,8 +629,8 @@ erts_thr_progress_register_managed_thread(ErtsSchedulerData *esdp,
tpd->leader = 0;
tpd->active = 1;
- tpd->previous.local = 0;
- tpd->previous.current = ERTS_THR_PRGR_VAL_WAITING;
+ tpd->confirmed = 0;
+ tpd->leader_state.current = ERTS_THR_PRGR_VAL_WAITING;
erts_tsd_set(erts_thr_prgr_data_key__, (void *) tpd);
erts_atomic32_inc_nob(&intrnl->misc.data.lflgs);
@@ -651,60 +673,113 @@ leader_update(ErtsThrPrgrData *tpd)
block_thread(tpd);
}
else {
+ ErtsThrPrgrVal current;
+ int ix, chk_next_ix, umrefc_ix, my_ix, no_managed, waiting_unmanaged;
erts_aint32_t lflgs;
ErtsThrPrgrVal next;
- int ix, sz, make_progress;
+ erts_aint_t refc;
- if (tpd->previous.current == ERTS_THR_PRGR_VAL_WAITING) {
- /* Took over as leader from another thread */
- tpd->previous.current = read_acqb(&erts_thr_prgr__.current);
- tpd->previous.next = tpd->previous.current;
- tpd->previous.next++;
- if (tpd->previous.next == ERTS_THR_PRGR_VAL_WAITING)
- tpd->previous.next = 0;
- }
+ my_ix = tpd->id;
- if (tpd->previous.local == tpd->previous.current) {
- ErtsThrPrgrVal val = tpd->previous.current + 1;
- if (val == ERTS_THR_PRGR_VAL_WAITING)
- val = 0;
- tpd->previous.local = val;
- set_mb(&intrnl->thr[tpd->id].data.current, val);
+ if (tpd->leader_state.current == ERTS_THR_PRGR_VAL_WAITING) {
+ /* Took over as leader from another thread */
+ tpd->leader_state.current = read_nob(&erts_thr_prgr__.current);
+ tpd->leader_state.next = tpd->leader_state.current;
+ tpd->leader_state.next++;
+ if (tpd->leader_state.next == ERTS_THR_PRGR_VAL_WAITING)
+ tpd->leader_state.next = 0;
+ tpd->leader_state.chk_next_ix = intrnl->misc.data.chk_next_ix;
+ tpd->leader_state.umrefc_ix.waiting = intrnl->misc.data.umrefc_ix.waiting;
+ tpd->leader_state.umrefc_ix.current =
+ (int) erts_atomic32_read_nob(&intrnl->misc.data.umrefc_ix.current);
+
+ if (tpd->confirmed == tpd->leader_state.current) {
+ ErtsThrPrgrVal val = tpd->leader_state.current + 1;
+ if (val == ERTS_THR_PRGR_VAL_WAITING)
+ val = 0;
+ tpd->confirmed = val;
+ set_mb(&intrnl->thr[my_ix].data.current, val);
+ }
}
- next = tpd->previous.next;
- make_progress = 1;
- sz = intrnl->managed.no;
- for (ix = 0; ix < sz; ix++) {
- ErtsThrPrgrVal tmp;
- tmp = read_nob(&intrnl->thr[ix].data.current);
- if (tmp != next && tmp != ERTS_THR_PRGR_VAL_WAITING) {
- make_progress = 0;
- ASSERT(erts_thr_progress_has_passed__(next, tmp));
- break;
+ next = tpd->leader_state.next;
+
+ waiting_unmanaged = 0;
+ umrefc_ix = -1; /* Shut up annoying warning */
+
+ chk_next_ix = tpd->leader_state.chk_next_ix;
+ no_managed = intrnl->managed.no;
+ ASSERT(0 <= chk_next_ix && chk_next_ix <= no_managed);
+ /* Check manged threads */
+ if (chk_next_ix < no_managed) {
+ for (ix = chk_next_ix; ix < no_managed; ix++) {
+ ErtsThrPrgrVal tmp;
+ if (ix == my_ix)
+ continue;
+ tmp = read_nob(&intrnl->thr[ix].data.current);
+ if (tmp != next && tmp != ERTS_THR_PRGR_VAL_WAITING) {
+ tpd->leader_state.chk_next_ix = ix;
+ ASSERT(erts_thr_progress_has_passed__(next, tmp));
+ goto done;
+ }
}
}
- if (make_progress) {
- ErtsThrPrgrVal current = next;
+ /* Check unmanged threads */
+ waiting_unmanaged = tpd->leader_state.umrefc_ix.waiting != -1;
+ umrefc_ix = (waiting_unmanaged
+ ? tpd->leader_state.umrefc_ix.waiting
+ : tpd->leader_state.umrefc_ix.current);
+ refc = erts_atomic_read_nob(&intrnl->umrefc[umrefc_ix].refc);
+ ASSERT(refc >= 0);
+ if (refc != 0) {
+ int new_umrefc_ix;
+
+ if (waiting_unmanaged)
+ goto done;
+
+ new_umrefc_ix = (umrefc_ix + 1) & 0x1;
+ tpd->leader_state.umrefc_ix.waiting = umrefc_ix;
+ tpd->leader_state.chk_next_ix = no_managed;
+ erts_atomic32_set_nob(&intrnl->misc.data.umrefc_ix.current,
+ (erts_aint32_t) new_umrefc_ix);
+ ETHR_MEMBAR(ETHR_StoreLoad);
+ refc = erts_atomic_read_nob(&intrnl->umrefc[umrefc_ix].refc);
+ ASSERT(refc >= 0);
+ waiting_unmanaged = 1;
+ if (refc != 0)
+ goto done;
+ }
- next++;
- if (next == ERTS_THR_PRGR_VAL_WAITING)
- next = 0;
+ /* Make progress */
+ current = next;
- set_nob(&intrnl->thr[tpd->id].data.current, next);
- set_mb(&erts_thr_prgr__.current, current);
- tpd->previous.local = next;
- tpd->previous.next = next;
- tpd->previous.current = current;
+ next++;
+ if (next == ERTS_THR_PRGR_VAL_WAITING)
+ next = 0;
+
+ set_nob(&intrnl->thr[my_ix].data.current, next);
+ set_mb(&erts_thr_prgr__.current, current);
+ tpd->confirmed = next;
+ tpd->leader_state.next = next;
+ tpd->leader_state.current = current;
#if ERTS_THR_PRGR_PRINT_VAL
- if (current % 1000 == 0)
- erts_fprintf(stderr, "%b64u\n", current);
+ if (current % 1000 == 0)
+ erts_fprintf(stderr, "%b64u\n", current);
#endif
- handle_wakeup_requests(current);
+ handle_wakeup_requests(current);
+
+ if (waiting_unmanaged) {
+ waiting_unmanaged = 0;
+ tpd->leader_state.umrefc_ix.waiting = -1;
+ erts_atomic32_read_band_nob(&intrnl->misc.data.lflgs,
+ ~ERTS_THR_PRGR_LFLG_WAITING_UM);
}
+ tpd->leader_state.chk_next_ix = 0;
+
+ done:
if (tpd->active) {
lflgs = erts_atomic32_read_nob(&intrnl->misc.data.lflgs);
@@ -712,20 +787,44 @@ leader_update(ErtsThrPrgrData *tpd)
(void) block_thread(tpd);
}
else {
+ int force_wakeup_check = 0;
+ erts_aint32_t set_flags = ERTS_THR_PRGR_LFLG_NO_LEADER;
tpd->leader = 0;
- tpd->previous.current = ERTS_THR_PRGR_VAL_WAITING;
+ tpd->leader_state.current = ERTS_THR_PRGR_VAL_WAITING;
#if ERTS_THR_PRGR_PRINT_LEADER
erts_fprintf(stderr, "L <- %d\n", tpd->id);
#endif
ERTS_THR_PROGRESS_STATE_DEBUG_SET_LEADER(tpd->id, 0);
+ if (waiting_unmanaged)
+ set_flags |= ERTS_THR_PRGR_LFLG_WAITING_UM;
+
lflgs = erts_atomic32_read_bor_relb(&intrnl->misc.data.lflgs,
- ERTS_THR_PRGR_LFLG_NO_LEADER);
+ set_flags);
+ lflgs |= set_flags;
if (lflgs & ERTS_THR_PRGR_LFLG_BLOCK)
lflgs = block_thread(tpd);
- if (ERTS_THR_PRGR_LFLGS_ACTIVE(lflgs) == 0 && got_sched_wakeups())
+
+ if (waiting_unmanaged) {
+ /* Need to check umrefc again */
+ ETHR_MEMBAR(ETHR_StoreLoad);
+ refc = erts_atomic_read_nob(&intrnl->umrefc[umrefc_ix].refc);
+ if (refc == 0) {
+ /* Need to force wakeup check */
+ force_wakeup_check = 1;
+ }
+ }
+
+ if ((force_wakeup_check
+ || ((lflgs & (ERTS_THR_PRGR_LFLG_NO_LEADER
+ | ERTS_THR_PRGR_LFLG_WAITING_UM
+ | ERTS_THR_PRGR_LFLG_ACTIVE_MASK))
+ == ERTS_THR_PRGR_LFLG_NO_LEADER))
+ && got_sched_wakeups()) {
+ /* Someone need to make progress */
wakeup_managed(0);
+ }
}
}
@@ -744,11 +843,11 @@ update(ErtsThrPrgrData *tpd)
erts_aint32_t lflgs;
res = 0;
val = read_acqb(&erts_thr_prgr__.current);
- if (tpd->previous.local == val) {
+ if (tpd->confirmed == val) {
val++;
if (val == ERTS_THR_PRGR_VAL_WAITING)
val = 0;
- tpd->previous.local = val;
+ tpd->confirmed = val;
set_mb(&intrnl->thr[tpd->id].data.current, val);
}
@@ -801,12 +900,19 @@ erts_thr_progress_prepare_wait(ErtsSchedulerData *esdp)
block_count_dec();
- tpd->previous.local = ERTS_THR_PRGR_VAL_WAITING;
+ tpd->confirmed = ERTS_THR_PRGR_VAL_WAITING;
set_mb(&intrnl->thr[tpd->id].data.current, ERTS_THR_PRGR_VAL_WAITING);
lflgs = erts_atomic32_read_nob(&intrnl->misc.data.lflgs);
- if (ERTS_THR_PRGR_LFLGS_ALL_WAITING(lflgs) && got_sched_wakeups())
- wakeup_managed(0); /* Someone need to make progress */
+
+ if ((lflgs & (ERTS_THR_PRGR_LFLG_NO_LEADER
+ | ERTS_THR_PRGR_LFLG_WAITING_UM
+ | ERTS_THR_PRGR_LFLG_ACTIVE_MASK))
+ == ERTS_THR_PRGR_LFLG_NO_LEADER
+ && got_sched_wakeups()) {
+ /* Someone need to make progress */
+ wakeup_managed(0);
+ }
}
void
@@ -828,7 +934,7 @@ erts_thr_progress_finalize_wait(ErtsSchedulerData *esdp)
val++;
if (val == ERTS_THR_PRGR_VAL_WAITING)
val = 0;
- tpd->previous.local = val;
+ tpd->confirmed = val;
set_mb(&intrnl->thr[tpd->id].data.current, val);
val = read_acqb(&erts_thr_prgr__.current);
if (current == val)
@@ -875,6 +981,68 @@ erts_thr_progress_active(ErtsSchedulerData *esdp, int on)
}
+static ERTS_INLINE void
+unmanaged_continue(ErtsThrPrgrDelayHandle handle)
+{
+ int umrefc_ix = (int) handle;
+ erts_aint_t refc;
+
+ ASSERT(umrefc_ix == 0 || umrefc_ix == 1);
+ refc = erts_atomic_dec_read_relb(&intrnl->umrefc[umrefc_ix].refc);
+ ASSERT(refc >= 0);
+ if (refc == 0) {
+ erts_aint_t lflgs;
+ ERTS_THR_READ_MEMORY_BARRIER;
+ lflgs = erts_atomic32_read_nob(&intrnl->misc.data.lflgs);
+ if ((lflgs & (ERTS_THR_PRGR_LFLG_NO_LEADER
+ | ERTS_THR_PRGR_LFLG_WAITING_UM
+ | ERTS_THR_PRGR_LFLG_ACTIVE_MASK))
+ == (ERTS_THR_PRGR_LFLG_NO_LEADER|ERTS_THR_PRGR_LFLG_WAITING_UM)
+ && got_sched_wakeups()) {
+ /* Others waiting for us... */
+ wakeup_managed(0);
+ }
+ }
+}
+
+void
+erts_thr_progress_unmanaged_continue__(ErtsThrPrgrDelayHandle handle)
+{
+#ifdef ERTS_ENABLE_LOCK_CHECK
+ ErtsThrPrgrData *tpd = perhaps_thr_prgr_data(NULL);
+ ERTS_LC_ASSERT(tpd && tpd->is_delaying);
+ tpd->is_delaying = 0;
+ return_tmp_thr_prgr_data(tpd);
+#endif
+ ASSERT(!erts_thr_progress_is_managed_thread());
+
+ unmanaged_continue(handle);
+}
+
+ErtsThrPrgrDelayHandle
+erts_thr_progress_unmanaged_delay__(void)
+{
+ int umrefc_ix;
+ ASSERT(!erts_thr_progress_is_managed_thread());
+ umrefc_ix = (int) erts_atomic32_read_acqb(&intrnl->misc.data.umrefc_ix.current);
+ while (1) {
+ int tmp_ix;
+ erts_atomic_inc_acqb(&intrnl->umrefc[umrefc_ix].refc);
+ tmp_ix = (int) erts_atomic32_read_acqb(&intrnl->misc.data.umrefc_ix.current);
+ if (tmp_ix == umrefc_ix)
+ break;
+ unmanaged_continue(umrefc_ix);
+ umrefc_ix = tmp_ix;
+ }
+#ifdef ERTS_ENABLE_LOCK_CHECK
+ {
+ ErtsThrPrgrData *tpd = tmp_thr_prgr_data(NULL);
+ tpd->is_delaying = 1;
+ }
+#endif
+ return (ErtsThrPrgrDelayHandle) umrefc_ix;
+}
+
static ERTS_INLINE int
has_reached_wakeup(ErtsThrPrgrVal wakeup)
{
@@ -931,7 +1099,7 @@ request_wakeup_managed(ErtsThrPrgrData *tpd, ErtsThrPrgrVal value)
*/
ASSERT(tpd->is_managed);
- ASSERT(tpd->previous.local != ERTS_THR_PRGR_VAL_WAITING);
+ ASSERT(tpd->confirmed != ERTS_THR_PRGR_VAL_WAITING);
if (has_reached_wakeup(value)) {
wakeup_managed(tpd->id);
@@ -946,7 +1114,7 @@ request_wakeup_managed(ErtsThrPrgrData *tpd, ErtsThrPrgrVal value)
tpd->wakeup_request[wix]));
- if (tpd->previous.local == value) {
+ if (tpd->confirmed == value) {
/*
* We have already confirmed this value. We need to request
* wakeup for a value later than our latest confirmed value in
diff --git a/erts/emulator/beam/erl_thr_progress.h b/erts/emulator/beam/erl_thr_progress.h
index 89486b065b..1416aa6166 100644
--- a/erts/emulator/beam/erl_thr_progress.h
+++ b/erts/emulator/beam/erl_thr_progress.h
@@ -53,9 +53,22 @@ typedef Uint64 ErtsThrPrgrVal;
#define ERTS_THR_PRGR_WAKEUP_DATA_SIZE 4 /* Need to be an even power of 2. */
typedef struct {
+ ErtsThrPrgrVal next;
+ ErtsThrPrgrVal current;
+ int chk_next_ix;
+ struct {
+ int current;
+ int waiting;
+ } umrefc_ix;
+} ErtsThrPrgrLeaderState;
+
+typedef struct {
int id;
int is_managed;
int is_blocking;
+#ifdef ERTS_ENABLE_LOCK_CHECK
+ int is_delaying; /* managed is always delaying */
+#endif
int is_temporary;
/* --- Part below only for registered threads --- */
@@ -66,11 +79,8 @@ typedef struct {
int leader; /* Needs to be first in the managed threads part */
int active;
- struct {
- ErtsThrPrgrVal local;
- ErtsThrPrgrVal next;
- ErtsThrPrgrVal current;
- } previous;
+ ErtsThrPrgrVal confirmed;
+ ErtsThrPrgrLeaderState leader_state;
} ErtsThrPrgrData;
void erts_thr_progress_fatal_error_block(SWord timeout,
@@ -78,6 +88,16 @@ void erts_thr_progress_fatal_error_block(SWord timeout,
#endif /* ERTS_SMP */
+typedef struct ErtsThrPrgrLaterOp_ ErtsThrPrgrLaterOp;
+struct ErtsThrPrgrLaterOp_ {
+#ifdef ERTS_SMP
+ ErtsThrPrgrVal later;
+#endif
+ void (*func)(void *);
+ void *data;
+ ErtsThrPrgrLaterOp *next;
+};
+
#endif
#if !defined(ERL_THR_PROGRESS_H__) && !defined(ERL_THR_PROGRESS_TSD_TYPE_ONLY)
@@ -111,6 +131,11 @@ typedef struct {
ERTS_THR_PRGR_ATOMIC current;
} ErtsThrPrgr;
+typedef int ErtsThrPrgrDelayHandle;
+#define ERTS_THR_PRGR_DHANDLE_MANAGED ((ErtsThrPrgrDelayHandle) -1)
+/* ERTS_THR_PRGR_DHANDLE_MANAGED implies managed thread */
+#define ERTS_THR_PRGR_DHANDLE_INVALID ((ErtsThrPrgrDelayHandle) -2)
+
extern ErtsThrPrgr erts_thr_prgr__;
void erts_thr_progress_pre_init(void);
@@ -126,6 +151,8 @@ int erts_thr_progress_update(ErtsSchedulerData *esdp);
int erts_thr_progress_leader_update(ErtsSchedulerData *esdp);
void erts_thr_progress_prepare_wait(ErtsSchedulerData *esdp);
void erts_thr_progress_finalize_wait(ErtsSchedulerData *esdp);
+ErtsThrPrgrDelayHandle erts_thr_progress_unmanaged_delay__(void);
+void erts_thr_progress_unmanaged_continue__(int umrefc_ix);
void erts_thr_progress_dbg_print_state(void);
@@ -138,6 +165,11 @@ ERTS_GLB_INLINE ErtsThrPrgrVal erts_thr_prgr_read_acqb__(ERTS_THR_PRGR_ATOMIC *a
ERTS_GLB_INLINE ErtsThrPrgrVal erts_thr_prgr_read_mb__(ERTS_THR_PRGR_ATOMIC *atmc);
ERTS_GLB_INLINE int erts_thr_progress_is_managed_thread(void);
+ERTS_GLB_INLINE ErtsThrPrgrDelayHandle erts_thr_progress_unmanaged_delay(void);
+ERTS_GLB_INLINE void erts_thr_progress_unmanaged_continue(ErtsThrPrgrDelayHandle handle);
+#ifdef ERTS_ENABLE_LOCK_CHECK
+ERTS_GLB_INLINE int erts_thr_progress_lc_is_delaying(void);
+#endif
ERTS_GLB_INLINE ErtsThrPrgrVal erts_thr_progress_current_to_later__(ErtsThrPrgrVal val);
ERTS_GLB_INLINE ErtsThrPrgrVal erts_thr_progress_later(ErtsSchedulerData *);
ERTS_GLB_INLINE ErtsThrPrgrVal erts_thr_progress_current(void);
@@ -219,6 +251,35 @@ erts_thr_progress_is_managed_thread(void)
return tpd && tpd->is_managed;
}
+ERTS_GLB_INLINE ErtsThrPrgrDelayHandle
+erts_thr_progress_unmanaged_delay(void)
+{
+ if (erts_thr_progress_is_managed_thread())
+ return ERTS_THR_PRGR_DHANDLE_MANAGED; /* Nothing to do */
+ else
+ return erts_thr_progress_unmanaged_delay__();
+}
+
+ERTS_GLB_INLINE void
+erts_thr_progress_unmanaged_continue(ErtsThrPrgrDelayHandle handle)
+{
+ ASSERT(handle != ERTS_THR_PRGR_DHANDLE_MANAGED
+ || erts_thr_progress_is_managed_thread());
+ if (handle != ERTS_THR_PRGR_DHANDLE_MANAGED)
+ erts_thr_progress_unmanaged_continue__(handle);
+}
+
+#ifdef ERTS_ENABLE_LOCK_CHECK
+
+ERTS_GLB_INLINE int
+erts_thr_progress_lc_is_delaying(void)
+{
+ ErtsThrPrgrData *tpd = erts_tsd_get(erts_thr_prgr_data_key__);
+ return tpd && tpd->is_delaying;
+}
+
+#endif
+
ERTS_GLB_INLINE ErtsThrPrgrVal
erts_thr_progress_current_to_later__(ErtsThrPrgrVal val)
{
@@ -238,7 +299,7 @@ erts_thr_progress_later(ErtsSchedulerData *esdp)
if (esdp) {
tpd = &esdp->thr_progress_data;
managed_thread:
- val = tpd->previous.local;
+ val = tpd->confirmed;
ERTS_THR_MEMORY_BARRIER;
}
else {
diff --git a/erts/emulator/beam/erl_threads.h b/erts/emulator/beam/erl_threads.h
index 17628286bc..1dc3ffeb3c 100644
--- a/erts/emulator/beam/erl_threads.h
+++ b/erts/emulator/beam/erl_threads.h
@@ -533,6 +533,9 @@ ERTS_GLB_INLINE erts_aint_t erts_no_atomic_xchg(erts_no_atomic_t *xchgp,
ERTS_GLB_INLINE erts_aint_t erts_no_atomic_cmpxchg(erts_no_atomic_t *xchgp,
erts_aint_t new,
erts_aint_t expected);
+ERTS_GLB_INLINE erts_aint_t erts_no_atomic_read_bset(erts_no_atomic_t *var,
+ erts_aint_t mask,
+ erts_aint_t set);
ERTS_GLB_INLINE void erts_no_atomic32_set(erts_no_atomic32_t *var,
erts_aint32_t i);
ERTS_GLB_INLINE erts_aint32_t erts_no_atomic32_read(erts_no_atomic32_t *var);
@@ -553,6 +556,9 @@ ERTS_GLB_INLINE erts_aint32_t erts_no_atomic32_xchg(erts_no_atomic32_t *xchgp,
ERTS_GLB_INLINE erts_aint32_t erts_no_atomic32_cmpxchg(erts_no_atomic32_t *xchgp,
erts_aint32_t new,
erts_aint32_t expected);
+ERTS_GLB_INLINE erts_aint32_t erts_no_atomic32_read_bset(erts_no_atomic32_t *var,
+ erts_aint32_t mask,
+ erts_aint32_t set);
ERTS_GLB_INLINE void erts_spinlock_init_x_opt(erts_spinlock_t *lock,
char *name,
@@ -612,6 +618,78 @@ ERTS_GLB_INLINE void erts_thr_sigwait(const sigset_t *set, int *sig);
#ifdef USE_THREADS
+ERTS_GLB_INLINE erts_aint_t
+erts_atomic_read_bset_nob(erts_atomic_t *var,
+ erts_aint_t mask,
+ erts_aint_t set);
+ERTS_GLB_INLINE erts_aint_t
+erts_atomic_read_bset_ddrb(erts_atomic_t *var,
+ erts_aint_t mask,
+ erts_aint_t set);
+ERTS_GLB_INLINE erts_aint_t
+erts_atomic_read_bset_rb(erts_atomic_t *var,
+ erts_aint_t mask,
+ erts_aint_t set);
+ERTS_GLB_INLINE erts_aint_t
+erts_atomic_read_bset_wb(erts_atomic_t *var,
+ erts_aint_t mask,
+ erts_aint_t set);
+ERTS_GLB_INLINE erts_aint_t
+erts_atomic_read_bset_acqb(erts_atomic_t *var,
+ erts_aint_t mask,
+ erts_aint_t set);
+ERTS_GLB_INLINE erts_aint_t
+erts_atomic_read_bset_relb(erts_atomic_t *var,
+ erts_aint_t mask,
+ erts_aint_t set);
+ERTS_GLB_INLINE erts_aint_t
+erts_atomic_read_bset_mb(erts_atomic_t *var,
+ erts_aint_t mask,
+ erts_aint_t set);
+ERTS_GLB_INLINE erts_aint32_t
+erts_atomic32_read_bset_nob(erts_atomic32_t *var,
+ erts_aint32_t mask,
+ erts_aint32_t set);
+ERTS_GLB_INLINE erts_aint32_t
+erts_atomic32_read_bset_ddrb(erts_atomic32_t *var,
+ erts_aint32_t mask,
+ erts_aint32_t set);
+ERTS_GLB_INLINE erts_aint32_t
+erts_atomic32_read_bset_rb(erts_atomic32_t *var,
+ erts_aint32_t mask,
+ erts_aint32_t set);
+ERTS_GLB_INLINE erts_aint32_t
+erts_atomic32_read_bset_wb(erts_atomic32_t *var,
+ erts_aint32_t mask,
+ erts_aint32_t set);
+ERTS_GLB_INLINE erts_aint32_t
+erts_atomic32_read_bset_acqb(erts_atomic32_t *var,
+ erts_aint32_t mask,
+ erts_aint32_t set);
+ERTS_GLB_INLINE erts_aint32_t
+erts_atomic32_read_bset_relb(erts_atomic32_t *var,
+ erts_aint32_t mask,
+ erts_aint32_t set);
+ERTS_GLB_INLINE erts_aint32_t
+erts_atomic32_read_bset_mb(erts_atomic32_t *var,
+ erts_aint32_t mask,
+ erts_aint32_t set);
+
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+#define ERTS_ATOMIC_BSET_IMPL__(Type, ReadOp, CmpxchgOp, VarP, Mask, Set) \
+do { \
+ Type act = ReadOp((VarP)); \
+ while (1) { \
+ Type exp = act; \
+ Type new = exp & ~(Mask); \
+ new |= ((Mask) & (Set)); \
+ act = CmpxchgOp((VarP), new, exp); \
+ if (act == exp) \
+ return act; \
+ } \
+} while (0)
+#endif
+
/*
* See "Documentation of atomics and memory barriers" at the top
* of this file for info on atomics.
@@ -670,6 +748,19 @@ ERTS_GLB_INLINE void erts_thr_sigwait(const sigset_t *set, int *sig);
#define erts_atomic_xchg_nob ethr_atomic_xchg
#define erts_atomic_cmpxchg_nob ethr_atomic_cmpxchg
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+ERTS_GLB_INLINE erts_aint_t
+erts_atomic_read_bset_nob(erts_atomic_t *var,
+ erts_aint_t mask,
+ erts_aint_t set)
+{
+ ERTS_ATOMIC_BSET_IMPL__(erts_aint_t,
+ ethr_atomic_read,
+ ethr_atomic_cmpxchg,
+ var, mask, set);
+}
+#endif
+
#define erts_atomic_init_mb ethr_atomic_init_mb
#define erts_atomic_set_mb ethr_atomic_set_mb
#define erts_atomic_read_mb ethr_atomic_read_mb
@@ -684,6 +775,19 @@ ERTS_GLB_INLINE void erts_thr_sigwait(const sigset_t *set, int *sig);
#define erts_atomic_xchg_mb ethr_atomic_xchg_mb
#define erts_atomic_cmpxchg_mb ethr_atomic_cmpxchg_mb
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+ERTS_GLB_INLINE erts_aint_t
+erts_atomic_read_bset_mb(erts_atomic_t *var,
+ erts_aint_t mask,
+ erts_aint_t set)
+{
+ ERTS_ATOMIC_BSET_IMPL__(erts_aint_t,
+ ethr_atomic_read,
+ ethr_atomic_cmpxchg_mb,
+ var, mask, set);
+}
+#endif
+
#define erts_atomic_init_acqb ethr_atomic_init_acqb
#define erts_atomic_set_acqb ethr_atomic_set_acqb
#define erts_atomic_read_acqb ethr_atomic_read_acqb
@@ -698,6 +802,19 @@ ERTS_GLB_INLINE void erts_thr_sigwait(const sigset_t *set, int *sig);
#define erts_atomic_xchg_acqb ethr_atomic_xchg_acqb
#define erts_atomic_cmpxchg_acqb ethr_atomic_cmpxchg_acqb
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+ERTS_GLB_INLINE erts_aint_t
+erts_atomic_read_bset_acqb(erts_atomic_t *var,
+ erts_aint_t mask,
+ erts_aint_t set)
+{
+ ERTS_ATOMIC_BSET_IMPL__(erts_aint_t,
+ ethr_atomic_read,
+ ethr_atomic_cmpxchg_acqb,
+ var, mask, set);
+}
+#endif
+
#define erts_atomic_init_relb ethr_atomic_init_relb
#define erts_atomic_set_relb ethr_atomic_set_relb
#define erts_atomic_read_relb ethr_atomic_read_relb
@@ -712,6 +829,19 @@ ERTS_GLB_INLINE void erts_thr_sigwait(const sigset_t *set, int *sig);
#define erts_atomic_xchg_relb ethr_atomic_xchg_relb
#define erts_atomic_cmpxchg_relb ethr_atomic_cmpxchg_relb
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+ERTS_GLB_INLINE erts_aint_t
+erts_atomic_read_bset_relb(erts_atomic_t *var,
+ erts_aint_t mask,
+ erts_aint_t set)
+{
+ ERTS_ATOMIC_BSET_IMPL__(erts_aint_t,
+ ethr_atomic_read,
+ ethr_atomic_cmpxchg_relb,
+ var, mask, set);
+}
+#endif
+
#define erts_atomic_init_ddrb ethr_atomic_init_ddrb
#define erts_atomic_set_ddrb ethr_atomic_set_ddrb
#define erts_atomic_read_ddrb ethr_atomic_read_ddrb
@@ -726,6 +856,19 @@ ERTS_GLB_INLINE void erts_thr_sigwait(const sigset_t *set, int *sig);
#define erts_atomic_xchg_ddrb ethr_atomic_xchg_ddrb
#define erts_atomic_cmpxchg_ddrb ethr_atomic_cmpxchg_ddrb
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+ERTS_GLB_INLINE erts_aint_t
+erts_atomic_read_bset_ddrb(erts_atomic_t *var,
+ erts_aint_t mask,
+ erts_aint_t set)
+{
+ ERTS_ATOMIC_BSET_IMPL__(erts_aint_t,
+ ethr_atomic_read,
+ ethr_atomic_cmpxchg_ddrb,
+ var, mask, set);
+}
+#endif
+
#define erts_atomic_init_rb ethr_atomic_init_rb
#define erts_atomic_set_rb ethr_atomic_set_rb
#define erts_atomic_read_rb ethr_atomic_read_rb
@@ -740,6 +883,19 @@ ERTS_GLB_INLINE void erts_thr_sigwait(const sigset_t *set, int *sig);
#define erts_atomic_xchg_rb ethr_atomic_xchg_rb
#define erts_atomic_cmpxchg_rb ethr_atomic_cmpxchg_rb
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+ERTS_GLB_INLINE erts_aint_t
+erts_atomic_read_bset_rb(erts_atomic_t *var,
+ erts_aint_t mask,
+ erts_aint_t set)
+{
+ ERTS_ATOMIC_BSET_IMPL__(erts_aint_t,
+ ethr_atomic_read,
+ ethr_atomic_cmpxchg_rb,
+ var, mask, set);
+}
+#endif
+
#define erts_atomic_init_wb ethr_atomic_init_wb
#define erts_atomic_set_wb ethr_atomic_set_wb
#define erts_atomic_read_wb ethr_atomic_read_wb
@@ -754,6 +910,19 @@ ERTS_GLB_INLINE void erts_thr_sigwait(const sigset_t *set, int *sig);
#define erts_atomic_xchg_wb ethr_atomic_xchg_wb
#define erts_atomic_cmpxchg_wb ethr_atomic_cmpxchg_wb
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+ERTS_GLB_INLINE erts_aint_t
+erts_atomic_read_bset_wb(erts_atomic_t *var,
+ erts_aint_t mask,
+ erts_aint_t set)
+{
+ ERTS_ATOMIC_BSET_IMPL__(erts_aint_t,
+ ethr_atomic_read,
+ ethr_atomic_cmpxchg_wb,
+ var, mask, set);
+}
+#endif
+
/* 32-bit atomics */
#define erts_atomic32_init_nob ethr_atomic32_init
@@ -770,6 +939,19 @@ ERTS_GLB_INLINE void erts_thr_sigwait(const sigset_t *set, int *sig);
#define erts_atomic32_xchg_nob ethr_atomic32_xchg
#define erts_atomic32_cmpxchg_nob ethr_atomic32_cmpxchg
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+ERTS_GLB_INLINE erts_aint32_t
+erts_atomic32_read_bset_nob(erts_atomic32_t *var,
+ erts_aint32_t mask,
+ erts_aint32_t set)
+{
+ ERTS_ATOMIC_BSET_IMPL__(erts_aint32_t,
+ ethr_atomic32_read,
+ ethr_atomic32_cmpxchg,
+ var, mask, set);
+}
+#endif
+
#define erts_atomic32_init_mb ethr_atomic32_init_mb
#define erts_atomic32_set_mb ethr_atomic32_set_mb
#define erts_atomic32_read_mb ethr_atomic32_read_mb
@@ -784,6 +966,19 @@ ERTS_GLB_INLINE void erts_thr_sigwait(const sigset_t *set, int *sig);
#define erts_atomic32_xchg_mb ethr_atomic32_xchg_mb
#define erts_atomic32_cmpxchg_mb ethr_atomic32_cmpxchg_mb
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+ERTS_GLB_INLINE erts_aint32_t
+erts_atomic32_read_bset_mb(erts_atomic32_t *var,
+ erts_aint32_t mask,
+ erts_aint32_t set)
+{
+ ERTS_ATOMIC_BSET_IMPL__(erts_aint32_t,
+ ethr_atomic32_read,
+ ethr_atomic32_cmpxchg_mb,
+ var, mask, set);
+}
+#endif
+
#define erts_atomic32_init_acqb ethr_atomic32_init_acqb
#define erts_atomic32_set_acqb ethr_atomic32_set_acqb
#define erts_atomic32_read_acqb ethr_atomic32_read_acqb
@@ -798,6 +993,19 @@ ERTS_GLB_INLINE void erts_thr_sigwait(const sigset_t *set, int *sig);
#define erts_atomic32_xchg_acqb ethr_atomic32_xchg_acqb
#define erts_atomic32_cmpxchg_acqb ethr_atomic32_cmpxchg_acqb
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+ERTS_GLB_INLINE erts_aint32_t
+erts_atomic32_read_bset_acqb(erts_atomic32_t *var,
+ erts_aint32_t mask,
+ erts_aint32_t set)
+{
+ ERTS_ATOMIC_BSET_IMPL__(erts_aint32_t,
+ ethr_atomic32_read,
+ ethr_atomic32_cmpxchg_acqb,
+ var, mask, set);
+}
+#endif
+
#define erts_atomic32_init_relb ethr_atomic32_init_relb
#define erts_atomic32_set_relb ethr_atomic32_set_relb
#define erts_atomic32_read_relb ethr_atomic32_read_relb
@@ -812,6 +1020,19 @@ ERTS_GLB_INLINE void erts_thr_sigwait(const sigset_t *set, int *sig);
#define erts_atomic32_xchg_relb ethr_atomic32_xchg_relb
#define erts_atomic32_cmpxchg_relb ethr_atomic32_cmpxchg_relb
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+ERTS_GLB_INLINE erts_aint32_t
+erts_atomic32_read_bset_relb(erts_atomic32_t *var,
+ erts_aint32_t mask,
+ erts_aint32_t set)
+{
+ ERTS_ATOMIC_BSET_IMPL__(erts_aint32_t,
+ ethr_atomic32_read,
+ ethr_atomic32_cmpxchg_relb,
+ var, mask, set);
+}
+#endif
+
#define erts_atomic32_init_ddrb ethr_atomic32_init_ddrb
#define erts_atomic32_set_ddrb ethr_atomic32_set_ddrb
#define erts_atomic32_read_ddrb ethr_atomic32_read_ddrb
@@ -826,6 +1047,19 @@ ERTS_GLB_INLINE void erts_thr_sigwait(const sigset_t *set, int *sig);
#define erts_atomic32_xchg_ddrb ethr_atomic32_xchg_ddrb
#define erts_atomic32_cmpxchg_ddrb ethr_atomic32_cmpxchg_ddrb
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+ERTS_GLB_INLINE erts_aint32_t
+erts_atomic32_read_bset_ddrb(erts_atomic32_t *var,
+ erts_aint32_t mask,
+ erts_aint32_t set)
+{
+ ERTS_ATOMIC_BSET_IMPL__(erts_aint32_t,
+ ethr_atomic32_read,
+ ethr_atomic32_cmpxchg_ddrb,
+ var, mask, set);
+}
+#endif
+
#define erts_atomic32_init_rb ethr_atomic32_init_rb
#define erts_atomic32_set_rb ethr_atomic32_set_rb
#define erts_atomic32_read_rb ethr_atomic32_read_rb
@@ -840,6 +1074,19 @@ ERTS_GLB_INLINE void erts_thr_sigwait(const sigset_t *set, int *sig);
#define erts_atomic32_xchg_rb ethr_atomic32_xchg_rb
#define erts_atomic32_cmpxchg_rb ethr_atomic32_cmpxchg_rb
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+ERTS_GLB_INLINE erts_aint32_t
+erts_atomic32_read_bset_rb(erts_atomic32_t *var,
+ erts_aint32_t mask,
+ erts_aint32_t set)
+{
+ ERTS_ATOMIC_BSET_IMPL__(erts_aint32_t,
+ ethr_atomic32_read,
+ ethr_atomic32_cmpxchg_rb,
+ var, mask, set);
+}
+#endif
+
#define erts_atomic32_init_wb ethr_atomic32_init_wb
#define erts_atomic32_set_wb ethr_atomic32_set_wb
#define erts_atomic32_read_wb ethr_atomic32_read_wb
@@ -854,6 +1101,21 @@ ERTS_GLB_INLINE void erts_thr_sigwait(const sigset_t *set, int *sig);
#define erts_atomic32_xchg_wb ethr_atomic32_xchg_wb
#define erts_atomic32_cmpxchg_wb ethr_atomic32_cmpxchg_wb
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+ERTS_GLB_INLINE erts_aint32_t
+erts_atomic32_read_bset_wb(erts_atomic32_t *var,
+ erts_aint32_t mask,
+ erts_aint32_t set)
+{
+ ERTS_ATOMIC_BSET_IMPL__(erts_aint32_t,
+ ethr_atomic32_read,
+ ethr_atomic32_cmpxchg_wb,
+ var, mask, set);
+}
+#endif
+
+#undef ERTS_ATOMIC_BSET_IMPL__
+
#else /* !USE_THREADS */
/* Double word size atomics */
@@ -908,6 +1170,7 @@ ERTS_GLB_INLINE void erts_thr_sigwait(const sigset_t *set, int *sig);
#define erts_atomic_read_band_nob erts_no_atomic_read_band
#define erts_atomic_xchg_nob erts_no_atomic_xchg
#define erts_atomic_cmpxchg_nob erts_no_atomic_cmpxchg
+#define erts_atomic_read_bset_nob erts_no_atomic_read_bset
#define erts_atomic_init_mb erts_no_atomic_set
#define erts_atomic_set_mb erts_no_atomic_set
@@ -922,6 +1185,7 @@ ERTS_GLB_INLINE void erts_thr_sigwait(const sigset_t *set, int *sig);
#define erts_atomic_read_band_mb erts_no_atomic_read_band
#define erts_atomic_xchg_mb erts_no_atomic_xchg
#define erts_atomic_cmpxchg_mb erts_no_atomic_cmpxchg
+#define erts_atomic_read_bset_mb erts_no_atomic_read_bset
#define erts_atomic_init_acqb erts_no_atomic_set
#define erts_atomic_set_acqb erts_no_atomic_set
@@ -936,6 +1200,7 @@ ERTS_GLB_INLINE void erts_thr_sigwait(const sigset_t *set, int *sig);
#define erts_atomic_read_band_acqb erts_no_atomic_read_band
#define erts_atomic_xchg_acqb erts_no_atomic_xchg
#define erts_atomic_cmpxchg_acqb erts_no_atomic_cmpxchg
+#define erts_atomic_read_bset_acqb erts_no_atomic_read_bset
#define erts_atomic_init_relb erts_no_atomic_set
#define erts_atomic_set_relb erts_no_atomic_set
@@ -950,6 +1215,7 @@ ERTS_GLB_INLINE void erts_thr_sigwait(const sigset_t *set, int *sig);
#define erts_atomic_read_band_relb erts_no_atomic_read_band
#define erts_atomic_xchg_relb erts_no_atomic_xchg
#define erts_atomic_cmpxchg_relb erts_no_atomic_cmpxchg
+#define erts_atomic_read_bset_relb erts_no_atomic_read_bset
#define erts_atomic_init_ddrb erts_no_atomic_set
#define erts_atomic_set_ddrb erts_no_atomic_set
@@ -964,6 +1230,7 @@ ERTS_GLB_INLINE void erts_thr_sigwait(const sigset_t *set, int *sig);
#define erts_atomic_read_band_ddrb erts_no_atomic_read_band
#define erts_atomic_xchg_ddrb erts_no_atomic_xchg
#define erts_atomic_cmpxchg_ddrb erts_no_atomic_cmpxchg
+#define erts_atomic_read_bset_ddrb erts_no_atomic_read_bset
#define erts_atomic_init_rb erts_no_atomic_set
#define erts_atomic_set_rb erts_no_atomic_set
@@ -978,6 +1245,7 @@ ERTS_GLB_INLINE void erts_thr_sigwait(const sigset_t *set, int *sig);
#define erts_atomic_read_band_rb erts_no_atomic_read_band
#define erts_atomic_xchg_rb erts_no_atomic_xchg
#define erts_atomic_cmpxchg_rb erts_no_atomic_cmpxchg
+#define erts_atomic_read_bset_rb erts_no_atomic_read_bset
#define erts_atomic_init_wb erts_no_atomic_set
#define erts_atomic_set_wb erts_no_atomic_set
@@ -992,6 +1260,7 @@ ERTS_GLB_INLINE void erts_thr_sigwait(const sigset_t *set, int *sig);
#define erts_atomic_read_band_wb erts_no_atomic_read_band
#define erts_atomic_xchg_wb erts_no_atomic_xchg
#define erts_atomic_cmpxchg_wb erts_no_atomic_cmpxchg
+#define erts_atomic_read_bset_wb erts_no_atomic_read_bset
/* 32-bit atomics */
@@ -1008,6 +1277,7 @@ ERTS_GLB_INLINE void erts_thr_sigwait(const sigset_t *set, int *sig);
#define erts_atomic32_read_band_nob erts_no_atomic32_read_band
#define erts_atomic32_xchg_nob erts_no_atomic32_xchg
#define erts_atomic32_cmpxchg_nob erts_no_atomic32_cmpxchg
+#define erts_atomic32_read_bset_nob erts_no_atomic32_read_bset
#define erts_atomic32_init_mb erts_no_atomic32_set
#define erts_atomic32_set_mb erts_no_atomic32_set
@@ -1022,6 +1292,7 @@ ERTS_GLB_INLINE void erts_thr_sigwait(const sigset_t *set, int *sig);
#define erts_atomic32_read_band_mb erts_no_atomic32_read_band
#define erts_atomic32_xchg_mb erts_no_atomic32_xchg
#define erts_atomic32_cmpxchg_mb erts_no_atomic32_cmpxchg
+#define erts_atomic32_read_bset_mb erts_no_atomic32_read_bset
#define erts_atomic32_init_acqb erts_no_atomic32_set
#define erts_atomic32_set_acqb erts_no_atomic32_set
@@ -1036,6 +1307,7 @@ ERTS_GLB_INLINE void erts_thr_sigwait(const sigset_t *set, int *sig);
#define erts_atomic32_read_band_acqb erts_no_atomic32_read_band
#define erts_atomic32_xchg_acqb erts_no_atomic32_xchg
#define erts_atomic32_cmpxchg_acqb erts_no_atomic32_cmpxchg
+#define erts_atomic32_read_bset_acqb erts_no_atomic32_read_bset
#define erts_atomic32_init_relb erts_no_atomic32_set
#define erts_atomic32_set_relb erts_no_atomic32_set
@@ -1050,6 +1322,7 @@ ERTS_GLB_INLINE void erts_thr_sigwait(const sigset_t *set, int *sig);
#define erts_atomic32_read_band_relb erts_no_atomic32_read_band
#define erts_atomic32_xchg_relb erts_no_atomic32_xchg
#define erts_atomic32_cmpxchg_relb erts_no_atomic32_cmpxchg
+#define erts_atomic32_read_bset_relb erts_no_atomic32_read_bset
#define erts_atomic32_init_ddrb erts_no_atomic32_set
#define erts_atomic32_set_ddrb erts_no_atomic32_set
@@ -1064,6 +1337,7 @@ ERTS_GLB_INLINE void erts_thr_sigwait(const sigset_t *set, int *sig);
#define erts_atomic32_read_band_ddrb erts_no_atomic32_read_band
#define erts_atomic32_xchg_ddrb erts_no_atomic32_xchg
#define erts_atomic32_cmpxchg_ddrb erts_no_atomic32_cmpxchg
+#define erts_atomic32_read_bset_ddrb erts_no_atomic32_read_bset
#define erts_atomic32_init_rb erts_no_atomic32_set
#define erts_atomic32_set_rb erts_no_atomic32_set
@@ -1078,6 +1352,7 @@ ERTS_GLB_INLINE void erts_thr_sigwait(const sigset_t *set, int *sig);
#define erts_atomic32_read_band_rb erts_no_atomic32_read_band
#define erts_atomic32_xchg_rb erts_no_atomic32_xchg
#define erts_atomic32_cmpxchg_rb erts_no_atomic32_cmpxchg
+#define erts_atomic32_read_bset_rb erts_no_atomic32_read_bset
#define erts_atomic32_init_wb erts_no_atomic32_set
#define erts_atomic32_set_wb erts_no_atomic32_set
@@ -1092,6 +1367,7 @@ ERTS_GLB_INLINE void erts_thr_sigwait(const sigset_t *set, int *sig);
#define erts_atomic32_read_band_wb erts_no_atomic32_read_band
#define erts_atomic32_xchg_wb erts_no_atomic32_xchg
#define erts_atomic32_cmpxchg_wb erts_no_atomic32_cmpxchg
+#define erts_atomic32_read_bset_wb erts_no_atomic32_read_bset
#endif /* !USE_THREADS */
@@ -1856,6 +2132,17 @@ erts_no_atomic_cmpxchg(erts_no_atomic_t *xchgp,
return old;
}
+ERTS_GLB_INLINE erts_aint_t
+erts_no_atomic_read_bset(erts_no_atomic_t *var,
+ erts_aint_t mask,
+ erts_aint_t set)
+{
+ erts_aint_t old = *var;
+ *var &= ~mask;
+ *var |= (mask & set);
+ return old;
+}
+
/* atomic32 */
ERTS_GLB_INLINE void
@@ -1943,6 +2230,17 @@ erts_no_atomic32_cmpxchg(erts_no_atomic32_t *xchgp,
return old;
}
+ERTS_GLB_INLINE erts_aint32_t
+erts_no_atomic32_read_bset(erts_no_atomic32_t *var,
+ erts_aint32_t mask,
+ erts_aint32_t set)
+{
+ erts_aint32_t old = *var;
+ *var &= ~mask;
+ *var |= (mask & set);
+ return old;
+}
+
/* spinlock */
ERTS_GLB_INLINE void
diff --git a/erts/emulator/beam/erl_time_sup.c b/erts/emulator/beam/erl_time_sup.c
index c1b4408c06..3272a5326d 100644
--- a/erts/emulator/beam/erl_time_sup.c
+++ b/erts/emulator/beam/erl_time_sup.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1999-2011. All Rights Reserved.
+ * Copyright Ericsson AB 1999-2012. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -386,7 +386,7 @@ static int clock_resolution;
/*
** The clock resolution should really be the resolution of the
** time function in use, which on most platforms
-** is 1. On VxWorks the resolution shold be
+** is 1. On VxWorks the resolution should be
** the number of ticks per second (or 1, which would work nicely to).
**
** Setting lower resolutions is mostly interesting when timers are used
@@ -754,6 +754,11 @@ int univ_to_seconds(Sint year, Sint month, Sint day, Sint hour, Sint minute, Sin
return 1;
}
+#if defined(HAVE_TIME2POSIX) && defined(HAVE_DECL_TIME2POSIX) && \
+ !HAVE_DECL_TIME2POSIX
+extern time_t time2posix(time_t);
+#endif
+
int
local_to_univ(Sint *year, Sint *month, Sint *day,
Sint *hour, Sint *minute, Sint *second, int isdst)
@@ -794,7 +799,7 @@ local_to_univ(Sint *year, Sint *month, Sint *day,
refuses to give us a DST time, we simulate the Linux/Solaris
behaviour of giving the same data as if is_dst was not set. */
t.tm_isdst = 0;
- if (erl_mktime(&the_clock, &t)) {
+ if (erl_mktime(&the_clock, &t) < 0) {
/* Failed anyway, something else is bad - will be a badarg */
return 0;
}
@@ -803,6 +808,11 @@ local_to_univ(Sint *year, Sint *month, Sint *day,
return 0;
}
}
+
+#ifdef HAVE_TIME2POSIX
+ the_clock = time2posix(the_clock);
+#endif
+
#ifdef HAVE_GMTIME_R
tm = gmtime_r(&the_clock, &tmbuf);
#else
diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c
index d04a91f18c..848877d43e 100644
--- a/erts/emulator/beam/erl_trace.c
+++ b/erts/emulator/beam/erl_trace.c
@@ -44,9 +44,9 @@
#undef DEBUG_PRINTOUTS
#endif
-extern Eterm beam_return_to_trace[1]; /* OpCode(i_return_to_trace) */
-extern Eterm beam_return_trace[1]; /* OpCode(i_return_trace) */
-extern Eterm beam_return_time_trace[1]; /* OpCode(i_return_time_trace) */
+extern BeamInstr beam_return_to_trace[1]; /* OpCode(i_return_to_trace) */
+extern BeamInstr beam_return_trace[1]; /* OpCode(i_return_trace) */
+extern BeamInstr beam_return_time_trace[1]; /* OpCode(i_return_time_trace) */
/* Pseudo export entries. Never filled in with data, only used to
yield unique pointers of the correct type. */
@@ -156,8 +156,8 @@ do { (RES) = (TPID); } while(0)
#define ERTS_TRACER_REF_TYPE Process *
#define ERTS_GET_TRACER_REF(RES, TPID, TRACEE_FLGS) \
do { \
- (RES) = erts_proc_lookup((TPID)); \
- if (!(RES) || !((RES)->trace_flags & F_TRACER)) { \
+ (RES) = erts_proc_lookup((TPID)); \
+ if (!(RES) || !(ERTS_TRACE_FLAGS((RES)) & F_TRACER)) { \
(TPID) = NIL; \
(TRACEE_FLGS) &= ~TRACEE_FLAGS; \
return; \
@@ -409,7 +409,7 @@ WRITE_SYS_MSG_TO_PORT(Eterm unused_to,
}
#ifndef ERTS_SMP
- if (!INVALID_TRACER_PORT(trace_port, trace_port->id))
+ if (!INVALID_TRACER_PORT(trace_port, trace_port->common.id))
#endif
erts_raw_port_command(trace_port, buffer, ptr-buffer);
@@ -441,7 +441,7 @@ do_send_schedfix_to_port(Port *trace_port, Eterm pid, Eterm timestamp) {
message = TUPLE5(hp, am_trace_ts, pid, am_out, mfarity, timestamp);
/* Note, hp is deliberately NOT incremented since it will be reused */
- do_send_to_port(trace_port->id,
+ do_send_to_port(trace_port->common.id,
trace_port,
pid,
SYS_MSG_TYPE_UNDEFINED,
@@ -451,7 +451,7 @@ do_send_schedfix_to_port(Port *trace_port, Eterm pid, Eterm timestamp) {
hp += 5;
hp = patch_ts(message, hp);
- do_send_to_port(trace_port->id,
+ do_send_to_port(trace_port->common.id,
trace_port,
pid,
SYS_MSG_TYPE_UNDEFINED,
@@ -508,7 +508,7 @@ send_to_port(Process *c_p, Eterm message,
#endif
do_send_to_port(*tracer_pid,
trace_port,
- c_p ? c_p->id : NIL,
+ c_p ? c_p->common.id : NIL,
SYS_MSG_TYPE_TRACE,
message);
#ifndef ERTS_SMP
@@ -543,7 +543,7 @@ send_to_port(Process *c_p, Eterm message,
trace_port->control_flags &= ~PORT_CONTROL_FLAG_HEAVY;
do_send_to_port(*tracer_pid,
trace_port,
- c_p ? c_p->id : NIL,
+ c_p ? c_p->common.id : NIL,
SYS_MSG_TYPE_TRACE,
message);
@@ -557,7 +557,7 @@ send_to_port(Process *c_p, Eterm message,
* just after writning the real trace message, and now gets scheduled
* in again.
*/
- do_send_schedfix_to_port(trace_port, c_p->id, ts);
+ do_send_schedfix_to_port(trace_port, c_p->common.id, ts);
}
erts_port_release(trace_port);
@@ -599,15 +599,14 @@ profile_send(Eterm from, Eterm message) {
if (profiler_port) {
do_send_to_port(profiler,
profiler_port,
- NIL, /* or current process->id */
+ NIL, /* or current process->common.id */
SYS_MSG_TYPE_SYSPROF,
message);
erts_port_release(profiler_port);
}
} else {
- ASSERT(is_internal_pid(profiler)
- && internal_pid_index(profiler) < erts_max_processes);
+ ASSERT(is_internal_pid(profiler));
profile_p = erts_proc_lookup(profiler);
@@ -673,7 +672,7 @@ seq_trace_send_to_port(Process *c_p,
#endif
do_send_to_port(seq_tracer,
trace_port,
- c_p ? c_p->id : NIL,
+ c_p ? c_p->common.id : NIL,
SYS_MSG_TYPE_SEQTRACE,
message);
@@ -704,7 +703,7 @@ seq_trace_send_to_port(Process *c_p,
trace_port->control_flags &= ~PORT_CONTROL_FLAG_HEAVY;
do_send_to_port(seq_tracer,
trace_port,
- c_p ? c_p->id : NIL,
+ c_p ? c_p->common.id : NIL,
SYS_MSG_TYPE_SEQTRACE,
message);
@@ -718,7 +717,7 @@ seq_trace_send_to_port(Process *c_p,
* just after writing the real trace message, and now gets scheduled
* in again.
*/
- do_send_schedfix_to_port(trace_port, c_p->id, ts);
+ do_send_schedfix_to_port(trace_port, c_p->common.id, ts);
}
erts_port_release(trace_port);
@@ -729,7 +728,9 @@ seq_trace_send_to_port(Process *c_p,
}
#define TS_HEAP_WORDS 5
-#define TS_SIZE(p) (((p)->trace_flags & F_TIMESTAMP) ? TS_HEAP_WORDS : 0)
+#define TS_SIZE(p) ((ERTS_TRACE_FLAGS((p)) & F_TIMESTAMP) \
+ ? TS_HEAP_WORDS \
+ : 0)
/*
* Patch a timestamp into a tuple. The tuple must be the last thing
@@ -764,17 +765,17 @@ send_to_tracer(Process *tracee,
erts_smp_mtx_lock(&smq_mtx);
- if (tracee->trace_flags & F_TIMESTAMP)
+ if (ERTS_TRACE_FLAGS(tracee) & F_TIMESTAMP)
*hpp = patch_ts(msg, *hpp);
- if (is_internal_pid(tracee->tracer_proc))
- ERTS_ENQ_TRACE_MSG(tracee->id, tracer_ref, msg, bp);
+ if (is_internal_pid(ERTS_TRACER_PROC(tracee)))
+ ERTS_ENQ_TRACE_MSG(tracee->common.id, tracer_ref, msg, bp);
else {
- ASSERT(is_internal_port(tracee->tracer_proc));
+ ASSERT(is_internal_port(ERTS_TRACER_PROC(tracee)));
send_to_port(no_fake_sched ? NULL : tracee,
msg,
- &tracee->tracer_proc,
- &tracee->trace_flags);
+ &ERTS_TRACER_PROC(tracee),
+ &ERTS_TRACE_FLAGS(tracee));
}
erts_smp_mtx_unlock(&smq_mtx);
@@ -792,7 +793,7 @@ trace_sched_aux(Process *p, Eterm what, int never_fake_sched)
ERTS_TRACER_REF_TYPE tracer_ref = ERTS_NULL_TRACER_REF;
int sched_no, curr_func, to_port, no_fake_sched;
- if (is_nil(p->tracer_proc))
+ if (is_nil(ERTS_TRACER_PROC(p)))
return;
no_fake_sched = never_fake_sched;
@@ -812,13 +813,14 @@ trace_sched_aux(Process *p, Eterm what, int never_fake_sched)
}
sched_no = IS_TRACED_FL(p, F_TRACE_SCHED_NO);
- to_port = is_internal_port(p->tracer_proc);
+ to_port = is_internal_port(ERTS_TRACER_PROC(p));
if (!to_port) {
- ASSERT(is_internal_pid(p->tracer_proc)
- && internal_pid_index(p->tracer_proc) < erts_max_processes);
+ ASSERT(is_internal_pid(ERTS_TRACER_PROC(p)));
- ERTS_GET_TRACER_REF(tracer_ref, p->tracer_proc, p->trace_flags);
+ ERTS_GET_TRACER_REF(tracer_ref,
+ ERTS_TRACER_PROC(p),
+ ERTS_TRACE_FLAGS(p));
}
if (ERTS_PROC_IS_EXITING(p))
@@ -851,7 +853,7 @@ trace_sched_aux(Process *p, Eterm what, int never_fake_sched)
}
if (!sched_no) {
- mess = TUPLE4(hp, am_trace, p->id, what, tmp);
+ mess = TUPLE4(hp, am_trace, p->common.id, what, tmp);
hp += 5;
}
else {
@@ -860,7 +862,7 @@ trace_sched_aux(Process *p, Eterm what, int never_fake_sched)
#else
Eterm sched_id = make_small(1);
#endif
- mess = TUPLE5(hp, am_trace, p->id, what, sched_id, tmp);
+ mess = TUPLE5(hp, am_trace, p->common.id, what, sched_id, tmp);
hp += 6;
}
@@ -912,19 +914,19 @@ trace_send(Process *p, Eterm to, Eterm msg)
operation = am_atom_put(s, sys_strlen(s));
}
- if (is_internal_port(p->tracer_proc)) {
+ if (is_internal_port(ERTS_TRACER_PROC(p))) {
#define LOCAL_HEAP_SIZE (11)
DeclareTmpHeapNoproc(local_heap,LOCAL_HEAP_SIZE);
UseTmpHeapNoproc(LOCAL_HEAP_SIZE);
hp = local_heap;
- mess = TUPLE5(hp, am_trace, p->id, operation, msg, to);
+ mess = TUPLE5(hp, am_trace, p->common.id, operation, msg, to);
hp += 6;
erts_smp_mtx_lock(&smq_mtx);
- if (p->trace_flags & F_TIMESTAMP) {
+ if (ERTS_TRACE_FLAGS(p) & F_TIMESTAMP) {
hp = patch_ts(mess, hp);
}
- send_to_port(p, mess, &p->tracer_proc, &p->trace_flags);
+ send_to_port(p, mess, &ERTS_TRACER_PROC(p), &ERTS_TRACE_FLAGS(p));
UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE);
#undef LOCAL_HEAP_SIZE
erts_smp_mtx_unlock(&smq_mtx);
@@ -934,10 +936,11 @@ trace_send(Process *p, Eterm to, Eterm msg)
ErlOffHeap *off_heap;
ERTS_TRACER_REF_TYPE tracer_ref;
- ASSERT(is_internal_pid(p->tracer_proc)
- && internal_pid_index(p->tracer_proc) < erts_max_processes);
+ ASSERT(is_internal_pid(ERTS_TRACER_PROC(p)));
- ERTS_GET_TRACER_REF(tracer_ref, p->tracer_proc, p->trace_flags);
+ ERTS_GET_TRACER_REF(tracer_ref,
+ ERTS_TRACER_PROC(p),
+ ERTS_TRACE_FLAGS(p));
sz_msg = size_object(msg);
sz_to = size_object(to);
@@ -953,16 +956,16 @@ trace_send(Process *p, Eterm to, Eterm msg)
sz_msg,
&hp,
off_heap);
- mess = TUPLE5(hp, am_trace, p->id/* Local pid */, operation, msg, to);
+ mess = TUPLE5(hp, am_trace, p->common.id, operation, msg, to);
hp += 6;
erts_smp_mtx_lock(&smq_mtx);
- if (p->trace_flags & F_TIMESTAMP) {
+ if (ERTS_TRACE_FLAGS(p) & F_TIMESTAMP) {
patch_ts(mess, hp);
}
- ERTS_ENQ_TRACE_MSG(p->id, tracer_ref, mess, bp);
+ ERTS_ENQ_TRACE_MSG(p->common.id, tracer_ref, mess, bp);
erts_smp_mtx_unlock(&smq_mtx);
}
}
@@ -977,19 +980,19 @@ trace_receive(Process *rp, Eterm msg)
size_t sz_msg;
Eterm* hp;
- if (is_internal_port(rp->tracer_proc)) {
+ if (is_internal_port(ERTS_TRACER_PROC(rp))) {
#define LOCAL_HEAP_SIZE (10)
DeclareTmpHeapNoproc(local_heap,LOCAL_HEAP_SIZE);
UseTmpHeapNoproc(LOCAL_HEAP_SIZE);
hp = local_heap;
- mess = TUPLE4(hp, am_trace, rp->id, am_receive, msg);
+ mess = TUPLE4(hp, am_trace, rp->common.id, am_receive, msg);
hp += 5;
erts_smp_mtx_lock(&smq_mtx);
- if (rp->trace_flags & F_TIMESTAMP) {
+ if (ERTS_TRACE_FLAGS(rp) & F_TIMESTAMP) {
hp = patch_ts(mess, hp);
}
- send_to_port(rp, mess, &rp->tracer_proc, &rp->trace_flags);
+ send_to_port(rp, mess, &ERTS_TRACER_PROC(rp), &ERTS_TRACE_FLAGS(rp));
UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE);
#undef LOCAL_HEAP_SIZE
erts_smp_mtx_unlock(&smq_mtx);
@@ -999,10 +1002,11 @@ trace_receive(Process *rp, Eterm msg)
ErlOffHeap *off_heap;
ERTS_TRACER_REF_TYPE tracer_ref;
- ASSERT(is_internal_pid(rp->tracer_proc)
- && internal_pid_index(rp->tracer_proc) < erts_max_processes);
+ ASSERT(is_internal_pid(ERTS_TRACER_PROC(rp)));
- ERTS_GET_TRACER_REF(tracer_ref, rp->tracer_proc, rp->trace_flags);
+ ERTS_GET_TRACER_REF(tracer_ref,
+ ERTS_TRACER_PROC(rp),
+ ERTS_TRACE_FLAGS(rp));
sz_msg = size_object(msg);
@@ -1011,16 +1015,16 @@ trace_receive(Process *rp, Eterm msg)
hp = ERTS_ALLOC_SYSMSG_HEAP(hsz, &bp, &off_heap, tracer_ref);
msg = copy_struct(msg, sz_msg, &hp, off_heap);
- mess = TUPLE4(hp, am_trace, rp->id/* Local pid */, am_receive, msg);
+ mess = TUPLE4(hp, am_trace, rp->common.id, am_receive, msg);
hp += 5;
erts_smp_mtx_lock(&smq_mtx);
- if (rp->trace_flags & F_TIMESTAMP) {
+ if (ERTS_TRACE_FLAGS(rp) & F_TIMESTAMP) {
patch_ts(mess, hp);
}
- ERTS_ENQ_TRACE_MSG(rp->id, tracer_ref, mess, bp);
+ ERTS_ENQ_TRACE_MSG(rp->common.id, tracer_ref, mess, bp);
erts_smp_mtx_unlock(&smq_mtx);
}
}
@@ -1030,14 +1034,14 @@ seq_trace_update_send(Process *p)
{
Eterm seq_tracer = erts_get_system_seq_tracer();
ASSERT((is_tuple(SEQ_TRACE_TOKEN(p)) || is_nil(SEQ_TRACE_TOKEN(p))));
- if ( (p->id == seq_tracer) || (SEQ_TRACE_TOKEN(p) == NIL)
+ if ( (p->common.id == seq_tracer) || (SEQ_TRACE_TOKEN(p) == NIL)
#ifdef USE_VM_PROBES
|| (SEQ_TRACE_TOKEN(p) == am_have_dt_utag)
#endif
) {
return 0;
}
- SEQ_TRACE_TOKEN_SENDER(p) = p->id; /* Internal pid */
+ SEQ_TRACE_TOKEN_SENDER(p) = p->common.id;
SEQ_TRACE_TOKEN_SERIAL(p) =
make_small(++(p -> seq_trace_clock));
SEQ_TRACE_TOKEN_LASTCNT(p) =
@@ -1074,7 +1078,7 @@ seq_trace_output_generic(Eterm token, Eterm msg, Uint type,
ASSERT(is_tuple(token) || is_nil(token));
if (SEQ_TRACE_T_SENDER(token) == seq_tracer || token == NIL ||
- (process && process->trace_flags & F_SENSITIVE)) {
+ (process && ERTS_TRACE_FLAGS(process) & F_SENSITIVE)) {
return;
}
@@ -1138,8 +1142,7 @@ seq_trace_output_generic(Eterm token, Eterm msg, Uint type,
Uint sz_label, sz_lastcnt_serial, sz_msg, sz_ts, sz_sender,
sz_exitfrom, sz_receiver;
- ASSERT(is_internal_pid(seq_tracer)
- && internal_pid_index(seq_tracer) < erts_max_processes);
+ ASSERT(is_internal_pid(seq_tracer));
#ifndef ERTS_SMP
@@ -1253,17 +1256,17 @@ erts_trace_return_to(Process *p, BeamInstr *pc)
hp += 4;
}
- mess = TUPLE4(hp, am_trace, p->id, am_return_to, mfa);
+ mess = TUPLE4(hp, am_trace, p->common.id, am_return_to, mfa);
hp += 5;
erts_smp_mtx_lock(&smq_mtx);
- if (p->trace_flags & F_TIMESTAMP) {
+ if (ERTS_TRACE_FLAGS(p) & F_TIMESTAMP) {
hp = patch_ts(mess, hp);
}
- if (is_internal_port(p->tracer_proc)) {
- send_to_port(p, mess, &p->tracer_proc, &p->trace_flags);
+ if (is_internal_port(ERTS_TRACER_PROC(p))) {
+ send_to_port(p, mess, &ERTS_TRACER_PROC(p), &ERTS_TRACE_FLAGS(p));
} else {
ErlHeapFragment *bp;
ErlOffHeap *off_heap;
@@ -1273,10 +1276,11 @@ erts_trace_return_to(Process *p, BeamInstr *pc)
/*
* Find the tracer.
*/
- ASSERT(is_internal_pid(p->tracer_proc)
- && internal_pid_index(p->tracer_proc) < erts_max_processes);
+ ASSERT(is_internal_pid(ERTS_TRACER_PROC(p)));
- ERTS_GET_TRACER_REF(tracer_ref, p->tracer_proc, p->trace_flags);
+ ERTS_GET_TRACER_REF(tracer_ref,
+ ERTS_TRACER_PROC(p),
+ ERTS_TRACE_FLAGS(p));
size = size_object(mess);
@@ -1286,7 +1290,7 @@ erts_trace_return_to(Process *p, BeamInstr *pc)
* Copy the trace message into the buffer and enqueue it.
*/
mess = copy_struct(mess, size, &hp, off_heap);
- ERTS_ENQ_TRACE_MSG(p->id, tracer_ref, mess, bp);
+ ERTS_ENQ_TRACE_MSG(p->common.id, tracer_ref, mess, bp);
}
UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE);
#undef LOCAL_HEAP_SIZE
@@ -1315,25 +1319,25 @@ erts_trace_return(Process* p, BeamInstr* fi, Eterm retval, Eterm *tracer_pid)
/* Breakpoint trace enabled without specifying tracer =>
* use process tracer and flags
*/
- tracer_pid = &p->tracer_proc;
+ tracer_pid = &ERTS_TRACER_PROC(p);
}
if (is_nil(*tracer_pid)) {
/* Trace disabled */
return;
}
ASSERT(is_internal_pid(*tracer_pid) || is_internal_port(*tracer_pid));
- if (*tracer_pid == p->id) {
+ if (*tracer_pid == p->common.id) {
/* Do not generate trace messages to oneself */
return;
}
- if (tracer_pid == &p->tracer_proc) {
+ if (tracer_pid == &ERTS_TRACER_PROC(p)) {
/* Tracer specified in process structure =>
* non-breakpoint trace =>
* use process flags
*/
- tracee_flags = &p->trace_flags;
+ tracee_flags = &ERTS_TRACE_FLAGS(p);
#ifdef ERTS_SMP
- tracee = p->id;
+ tracee = p->common.id;
#endif
} else {
/* Tracer not specified in process structure =>
@@ -1362,7 +1366,7 @@ erts_trace_return(Process* p, BeamInstr* fi, Eterm retval, Eterm *tracer_pid)
hp = local_heap;
mfa = TUPLE3(hp, mod, name, make_small(arity));
hp += 4;
- mess = TUPLE5(hp, am_trace, p->id, am_return_from, mfa, retval);
+ mess = TUPLE5(hp, am_trace, p->common.id, am_return_from, mfa, retval);
hp += 6;
erts_smp_mtx_lock(&smq_mtx);
if (*tracee_flags & F_TIMESTAMP) {
@@ -1382,8 +1386,7 @@ erts_trace_return(Process* p, BeamInstr* fi, Eterm retval, Eterm *tracer_pid)
Eterm* limit;
#endif
- ASSERT(is_internal_pid(*tracer_pid)
- && internal_pid_index(*tracer_pid) < erts_max_processes);
+ ASSERT(is_internal_pid(*tracer_pid));
ERTS_GET_TRACER_REF(tracer_ref, *tracer_pid, *tracee_flags);
@@ -1405,7 +1408,7 @@ erts_trace_return(Process* p, BeamInstr* fi, Eterm retval, Eterm *tracer_pid)
mfa = TUPLE3(hp, mod, name, make_small(arity));
hp += 4;
retval = copy_struct(retval, retval_size, &hp, off_heap);
- mess = TUPLE5(hp, am_trace, p->id/* Local pid */, am_return_from, mfa, retval);
+ mess = TUPLE5(hp, am_trace, p->common.id, am_return_from, mfa, retval);
hp += 6;
erts_smp_mtx_lock(&smq_mtx);
@@ -1446,25 +1449,25 @@ erts_trace_exception(Process* p, BeamInstr mfa[3], Eterm class, Eterm value,
/* Breakpoint trace enabled without specifying tracer =>
* use process tracer and flags
*/
- tracer_pid = &p->tracer_proc;
+ tracer_pid = &ERTS_TRACER_PROC(p);
}
if (is_nil(*tracer_pid)) {
/* Trace disabled */
return;
}
ASSERT(is_internal_pid(*tracer_pid) || is_internal_port(*tracer_pid));
- if (*tracer_pid == p->id) {
+ if (*tracer_pid == p->common.id) {
/* Do not generate trace messages to oneself */
return;
}
- if (tracer_pid == &p->tracer_proc) {
+ if (tracer_pid == &ERTS_TRACER_PROC(p)) {
/* Tracer specified in process structure =>
* non-breakpoint trace =>
* use process flags
*/
- tracee_flags = &p->trace_flags;
+ tracee_flags = &ERTS_TRACE_FLAGS(p);
#ifdef ERTS_SMP
- tracee = p->id;
+ tracee = p->common.id;
#endif
if (! (*tracee_flags & F_TRACE_CALLS)) {
return;
@@ -1492,7 +1495,7 @@ erts_trace_exception(Process* p, BeamInstr mfa[3], Eterm class, Eterm value,
hp += 4;
cv = TUPLE2(hp, class, value);
hp += 3;
- mess = TUPLE5(hp, am_trace, p->id, am_exception_from, mfa_tuple, cv);
+ mess = TUPLE5(hp, am_trace, p->common.id, am_exception_from, mfa_tuple, cv);
hp += 6;
ASSERT((hp - local_heap) <= LOCAL_HEAP_SIZE);
erts_smp_mtx_lock(&smq_mtx);
@@ -1514,8 +1517,7 @@ erts_trace_exception(Process* p, BeamInstr mfa[3], Eterm class, Eterm value,
Eterm* limit;
#endif
- ASSERT(is_internal_pid(*tracer_pid)
- && internal_pid_index(*tracer_pid) < erts_max_processes);
+ ASSERT(is_internal_pid(*tracer_pid));
ERTS_GET_TRACER_REF(tracer_ref, *tracer_pid, *tracee_flags);
@@ -1539,7 +1541,7 @@ erts_trace_exception(Process* p, BeamInstr mfa[3], Eterm class, Eterm value,
value = copy_struct(value, value_size, &hp, off_heap);
cv = TUPLE2(hp, class, value);
hp += 3;
- mess = TUPLE5(hp, am_trace, p->id/* Local pid */,
+ mess = TUPLE5(hp, am_trace, p->common.id,
am_exception_from, mfa_tuple, cv);
hp += 6;
@@ -1593,25 +1595,25 @@ erts_call_trace(Process* p, BeamInstr mfa[3], Binary *match_spec,
/* Breakpoint trace enabled without specifying tracer =>
* use process tracer and flags
*/
- tracer_pid = &p->tracer_proc;
+ tracer_pid = &ERTS_TRACER_PROC(p);
}
if (is_nil(*tracer_pid)) {
/* Trace disabled */
return 0;
}
ASSERT(is_internal_pid(*tracer_pid) || is_internal_port(*tracer_pid));
- if (*tracer_pid == p->id) {
+ if (*tracer_pid == p->common.id) {
/* Do not generate trace messages to oneself */
return 0;
}
- if (tracer_pid == &p->tracer_proc) {
+ if (tracer_pid == &ERTS_TRACER_PROC(p)) {
/* Tracer specified in process structure =>
* non-breakpoint trace =>
* use process flags
*/
- tracee_flags = &p->trace_flags;
+ tracee_flags = &ERTS_TRACE_FLAGS(p);
#ifdef ERTS_SMP
- tracee = p->id;
+ tracee = p->common.id;
#endif
} else {
/* Tracer not specified in process structure =>
@@ -1619,7 +1621,7 @@ erts_call_trace(Process* p, BeamInstr mfa[3], Binary *match_spec,
* meta trace =>
* use fixed flag set instead of process flags
*/
- if (p->trace_flags & F_SENSITIVE) {
+ if (ERTS_TRACE_FLAGS(p) & F_SENSITIVE) {
/* No trace messages for sensitive processes. */
return 0;
}
@@ -1677,7 +1679,7 @@ erts_call_trace(Process* p, BeamInstr mfa[3], Binary *match_spec,
if (!erts_is_valid_tracer_port(*tracer_pid)) {
#ifdef ERTS_SMP
- ASSERT(is_nil(tracee) || tracer_pid == &p->tracer_proc);
+ ASSERT(is_nil(tracee) || tracer_pid == &ERTS_TRACER_PROC(p));
if (is_not_nil(tracee))
erts_smp_proc_lock(p, ERTS_PROC_LOCKS_ALL_MINOR);
#endif
@@ -1779,7 +1781,7 @@ erts_call_trace(Process* p, BeamInstr mfa[3], Binary *match_spec,
* Build the trace tuple and send it to the port.
*/
- mess = TUPLE4(hp, am_trace, p->id, am_call, mfa_tuple);
+ mess = TUPLE4(hp, am_trace, p->common.id, am_call, mfa_tuple);
hp += 5;
if (pam_result != am_true) {
hp[-5] = make_arityval(5);
@@ -1814,21 +1816,21 @@ erts_call_trace(Process* p, BeamInstr mfa[3], Binary *match_spec,
Eterm* limit;
#endif
- ASSERT(is_internal_pid(*tracer_pid)
- && internal_pid_index(*tracer_pid) < erts_max_processes);
+ ASSERT(is_internal_pid(*tracer_pid));
tracer = erts_pid2proc(p, ERTS_PROC_LOCK_MAIN,
*tracer_pid, ERTS_PROC_LOCK_STATUS);
if (!tracer)
invalid_tracer = 1;
else {
- invalid_tracer = (tracer->trace_flags & F_TRACER) == 0;
+ invalid_tracer = !(ERTS_TRACE_FLAGS(tracer) & F_TRACER);
erts_smp_proc_unlock(tracer, ERTS_PROC_LOCK_STATUS);
}
if (invalid_tracer) {
#ifdef ERTS_SMP
- ASSERT(is_nil(tracee) || tracer_pid == &p->tracer_proc);
+ ASSERT(is_nil(tracee)
+ || tracer_pid == &ERTS_TRACER_PROC(p));
if (is_not_nil(tracee))
erts_smp_proc_lock(p, ERTS_PROC_LOCKS_ALL_MINOR);
#endif
@@ -1952,7 +1954,7 @@ erts_call_trace(Process* p, BeamInstr mfa[3], Binary *match_spec,
* Build the trace tuple and enqueue it.
*/
- mess = TUPLE4(hp, am_trace, p->id/* Local pid */, am_call, mfa_tuple);
+ mess = TUPLE4(hp, am_trace, p->common.id, am_call, mfa_tuple);
hp += 5;
if (pam_result != am_true) {
hp[-5] = make_arityval(5);
@@ -1990,17 +1992,17 @@ trace_proc(Process *c_p, Process *t_p, Eterm what, Eterm data)
ERTS_SMP_LC_ASSERT((erts_proc_lc_my_proc_locks(t_p) != 0)
|| erts_thr_progress_is_blocking());
- if (is_internal_port(t_p->tracer_proc)) {
+ if (is_internal_port(ERTS_TRACER_PROC(t_p))) {
#define LOCAL_HEAP_SIZE (5+5)
DeclareTmpHeapNoproc(local_heap,LOCAL_HEAP_SIZE);
UseTmpHeapNoproc(LOCAL_HEAP_SIZE);
hp = local_heap;
- mess = TUPLE4(hp, am_trace, t_p->id, what, data);
+ mess = TUPLE4(hp, am_trace, t_p->common.id, what, data);
hp += 5;
erts_smp_mtx_lock(&smq_mtx);
- if (t_p->trace_flags & F_TIMESTAMP) {
+ if (ERTS_TRACE_FLAGS(t_p) & F_TIMESTAMP) {
hp = patch_ts(mess, hp);
}
send_to_port(
@@ -2011,7 +2013,9 @@ trace_proc(Process *c_p, Process *t_p, Eterm what, Eterm data)
/* Fake schedule out and in are never sent when smp enabled */
c_p,
#endif
- mess, &t_p->tracer_proc, &t_p->trace_flags);
+ mess,
+ &ERTS_TRACER_PROC(t_p),
+ &ERTS_TRACE_FLAGS(t_p));
UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE);
#undef LOCAL_HEAP_SIZE
erts_smp_mtx_unlock(&smq_mtx);
@@ -2022,10 +2026,11 @@ trace_proc(Process *c_p, Process *t_p, Eterm what, Eterm data)
ERTS_TRACER_REF_TYPE tracer_ref;
size_t sz_data;
- ASSERT(is_internal_pid(t_p->tracer_proc)
- && internal_pid_index(t_p->tracer_proc) < erts_max_processes);
+ ASSERT(is_internal_pid(ERTS_TRACER_PROC(t_p)));
- ERTS_GET_TRACER_REF(tracer_ref, t_p->tracer_proc, t_p->trace_flags);
+ ERTS_GET_TRACER_REF(tracer_ref,
+ ERTS_TRACER_PROC(t_p),
+ ERTS_TRACE_FLAGS(t_p));
sz_data = size_object(data);
@@ -2034,16 +2039,16 @@ trace_proc(Process *c_p, Process *t_p, Eterm what, Eterm data)
hp = ERTS_ALLOC_SYSMSG_HEAP(need, &bp, &off_heap, tracer_ref);
tmp = copy_struct(data, sz_data, &hp, off_heap);
- mess = TUPLE4(hp, am_trace, t_p->id/* Local pid */, what, tmp);
+ mess = TUPLE4(hp, am_trace, t_p->common.id, what, tmp);
hp += 5;
erts_smp_mtx_lock(&smq_mtx);
- if (t_p->trace_flags & F_TIMESTAMP) {
+ if (ERTS_TRACE_FLAGS(t_p) & F_TIMESTAMP) {
hp = patch_ts(mess, hp);
}
- ERTS_ENQ_TRACE_MSG(t_p->id, tracer_ref, mess, bp);
+ ERTS_ENQ_TRACE_MSG(t_p->common.id, tracer_ref, mess, bp);
erts_smp_mtx_unlock(&smq_mtx);
}
}
@@ -2064,7 +2069,7 @@ trace_proc_spawn(Process *p, Eterm pid,
Eterm mess;
Eterm* hp;
- if (is_internal_port(p->tracer_proc)) {
+ if (is_internal_port(ERTS_TRACER_PROC(p))) {
#define LOCAL_HEAP_SIZE (4+6+5)
DeclareTmpHeapNoproc(local_heap,LOCAL_HEAP_SIZE);
UseTmpHeapNoproc(LOCAL_HEAP_SIZE);
@@ -2072,13 +2077,13 @@ trace_proc_spawn(Process *p, Eterm pid,
hp = local_heap;
mfa = TUPLE3(hp, mod, func, args);
hp += 4;
- mess = TUPLE5(hp, am_trace, p->id, am_spawn, pid, mfa);
+ mess = TUPLE5(hp, am_trace, p->common.id, am_spawn, pid, mfa);
hp += 6;
erts_smp_mtx_lock(&smq_mtx);
- if (p->trace_flags & F_TIMESTAMP) {
+ if (ERTS_TRACE_FLAGS(p) & F_TIMESTAMP) {
hp = patch_ts(mess, hp);
}
- send_to_port(p, mess, &p->tracer_proc, &p->trace_flags);
+ send_to_port(p, mess, &ERTS_TRACER_PROC(p), &ERTS_TRACE_FLAGS(p));
UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE);
#undef LOCAL_HEAP_SIZE
erts_smp_mtx_unlock(&smq_mtx);
@@ -2090,10 +2095,11 @@ trace_proc_spawn(Process *p, Eterm pid,
size_t sz_args, sz_pid;
Uint need;
- ASSERT(is_internal_pid(p->tracer_proc)
- && internal_pid_index(p->tracer_proc) < erts_max_processes);
+ ASSERT(is_internal_pid(ERTS_TRACER_PROC(p)));
- ERTS_GET_TRACER_REF(tracer_ref, p->tracer_proc, p->trace_flags);
+ ERTS_GET_TRACER_REF(tracer_ref,
+ ERTS_TRACER_PROC(p),
+ ERTS_TRACE_FLAGS(p));
sz_args = size_object(args);
sz_pid = size_object(pid);
@@ -2105,16 +2111,16 @@ trace_proc_spawn(Process *p, Eterm pid,
mfa = TUPLE3(hp, mod, func, tmp);
hp += 4;
tmp = copy_struct(pid, sz_pid, &hp, off_heap);
- mess = TUPLE5(hp, am_trace, p->id, am_spawn, tmp, mfa);
+ mess = TUPLE5(hp, am_trace, p->common.id, am_spawn, tmp, mfa);
hp += 6;
erts_smp_mtx_lock(&smq_mtx);
- if (p->trace_flags & F_TIMESTAMP) {
+ if (ERTS_TRACE_FLAGS(p) & F_TIMESTAMP) {
hp = patch_ts(mess, hp);
}
- ERTS_ENQ_TRACE_MSG(p->id, tracer_ref, mess, bp);
+ ERTS_ENQ_TRACE_MSG(p->common.id, tracer_ref, mess, bp);
erts_smp_mtx_unlock(&smq_mtx);
}
}
@@ -2204,7 +2210,7 @@ trace_gc(Process *p, Eterm what)
UseTmpHeap(LOCAL_HEAP_SIZE,p);
- if (is_internal_port(p->tracer_proc)) {
+ if (is_internal_port(ERTS_TRACER_PROC(p))) {
hp = local_heap;
#ifdef DEBUG
size = 0;
@@ -2216,10 +2222,11 @@ trace_gc(Process *p, Eterm what)
size += 5/*4-tuple*/ + TS_SIZE(p);
#endif
} else {
- ASSERT(is_internal_pid(p->tracer_proc)
- && internal_pid_index(p->tracer_proc) < erts_max_processes);
+ ASSERT(is_internal_pid(ERTS_TRACER_PROC(p)));
- ERTS_GET_TRACER_REF(tracer_ref, p->tracer_proc, p->trace_flags);
+ ERTS_GET_TRACER_REF(tracer_ref,
+ ERTS_TRACER_PROC(p),
+ ERTS_TRACE_FLAGS(p));
size = 0;
(void) erts_bld_atom_uint_2tup_list(NULL,
@@ -2243,19 +2250,19 @@ trace_gc(Process *p, Eterm what)
tags,
values);
- msg = TUPLE4(hp, am_trace, p->id/* Local pid */, what, msg);
+ msg = TUPLE4(hp, am_trace, p->common.id, what, msg);
hp += 5;
erts_smp_mtx_lock(&smq_mtx);
- if (p->trace_flags & F_TIMESTAMP) {
+ if (ERTS_TRACE_FLAGS(p) & F_TIMESTAMP) {
hp = patch_ts(msg, hp);
}
ASSERT(hp == limit);
- if (is_internal_port(p->tracer_proc))
- send_to_port(p, msg, &p->tracer_proc, &p->trace_flags);
+ if (is_internal_port(ERTS_TRACER_PROC(p)))
+ send_to_port(p, msg, &ERTS_TRACER_PROC(p), &ERTS_TRACE_FLAGS(p));
else
- ERTS_ENQ_TRACE_MSG(p->id, tracer_ref, msg, bp);
+ ERTS_ENQ_TRACE_MSG(p->common.id, tracer_ref, msg, bp);
erts_smp_mtx_unlock(&smq_mtx);
UnUseTmpHeap(LOCAL_HEAP_SIZE,p);
#undef LOCAL_HEAP_SIZE
@@ -2295,8 +2302,7 @@ monitor_long_gc(Process *p, Uint time) {
#endif
#ifndef ERTS_SMP
- ASSERT(is_internal_pid(system_monitor)
- && internal_pid_index(system_monitor) < erts_max_processes);
+ ASSERT(is_internal_pid(system_monitor));
monitor_p = erts_proc_lookup(system_monitor);
if (!monitor_p || p == monitor_p)
return;
@@ -2321,7 +2327,7 @@ monitor_long_gc(Process *p, Uint time) {
sizeof(values)/sizeof(Uint),
tags,
values);
- msg = TUPLE4(hp, am_monitor, p->id/* Local pid */, am_long_gc, list);
+ msg = TUPLE4(hp, am_monitor, p->common.id, am_long_gc, list);
#ifdef DEBUG
hp += 5 /* 4-tuple */;
@@ -2329,7 +2335,7 @@ monitor_long_gc(Process *p, Uint time) {
#endif
#ifdef ERTS_SMP
- enqueue_sys_msg(SYS_MSG_TYPE_SYSMON, p->id, NIL, msg, bp);
+ enqueue_sys_msg(SYS_MSG_TYPE_SYSMON, p->common.id, NIL, msg, bp);
#else
erts_queue_message(monitor_p, NULL, bp, msg, NIL
#ifdef USE_VM_PROBES
@@ -2370,8 +2376,7 @@ monitor_large_heap(Process *p) {
#ifndef ERTS_SMP
- ASSERT(is_internal_pid(system_monitor)
- && internal_pid_index(system_monitor) < erts_max_processes);
+ ASSERT(is_internal_pid(system_monitor));
monitor_p = erts_proc_lookup(system_monitor);
if (monitor_p || p == monitor_p) {
return;
@@ -2397,7 +2402,7 @@ monitor_large_heap(Process *p) {
sizeof(values)/sizeof(Uint),
tags,
values);
- msg = TUPLE4(hp, am_monitor, p->id/* Local pid */, am_large_heap, list);
+ msg = TUPLE4(hp, am_monitor, p->common.id, am_large_heap, list);
#ifdef DEBUG
hp += 5 /* 4-tuple */;
@@ -2405,7 +2410,7 @@ monitor_large_heap(Process *p) {
#endif
#ifdef ERTS_SMP
- enqueue_sys_msg(SYS_MSG_TYPE_SYSMON, p->id, NIL, msg, bp);
+ enqueue_sys_msg(SYS_MSG_TYPE_SYSMON, p->common.id, NIL, msg, bp);
#else
erts_queue_message(monitor_p, NULL, bp, msg, NIL
#ifdef USE_VM_PROBES
@@ -2425,8 +2430,7 @@ monitor_generic(Process *p, Eterm type, Eterm spec) {
Eterm *hp, msg;
#ifndef ERTS_SMP
- ASSERT(is_internal_pid(system_monitor)
- && internal_pid_index(system_monitor) < erts_max_processes);
+ ASSERT(is_internal_pid(system_monitor));
monitor_p = erts_proc_lookup(system_monitor);
if (!monitor_p || p == monitor_p)
return;
@@ -2434,11 +2438,11 @@ monitor_generic(Process *p, Eterm type, Eterm spec) {
hp = ERTS_ALLOC_SYSMSG_HEAP(5, &bp, &off_heap, monitor_p);
- msg = TUPLE4(hp, am_monitor, p->id/* Local pid */, type, spec);
+ msg = TUPLE4(hp, am_monitor, p->common.id, type, spec);
hp += 5;
#ifdef ERTS_SMP
- enqueue_sys_msg(SYS_MSG_TYPE_SYSMON, p->id, NIL, msg, bp);
+ enqueue_sys_msg(SYS_MSG_TYPE_SYSMON, p->common.id, NIL, msg, bp);
#else
erts_queue_message(monitor_p, NULL, bp, msg, NIL
#ifdef USE_VM_PROBES
@@ -2560,21 +2564,21 @@ trace_port_open(Port *p, Eterm calling_pid, Eterm drv_name) {
Eterm mess;
Eterm* hp;
- if (is_internal_port(p->tracer_proc)) {
+ if (is_internal_port(ERTS_TRACER_PROC(p))) {
#define LOCAL_HEAP_SIZE (5+6)
DeclareTmpHeapNoproc(local_heap,LOCAL_HEAP_SIZE);
UseTmpHeapNoproc(LOCAL_HEAP_SIZE);
hp = local_heap;
- mess = TUPLE5(hp, am_trace, calling_pid, am_open, p->id, drv_name);
+ mess = TUPLE5(hp, am_trace, calling_pid, am_open, p->common.id, drv_name);
hp += 6;
erts_smp_mtx_lock(&smq_mtx);
- if (p->trace_flags & F_TIMESTAMP) {
+ if (ERTS_TRACE_FLAGS(p) & F_TIMESTAMP) {
hp = patch_ts(mess, hp);
}
/* No fake schedule */
- send_to_port(NULL, mess, &p->tracer_proc, &p->trace_flags);
+ send_to_port(NULL, mess, &ERTS_TRACER_PROC(p), &ERTS_TRACE_FLAGS(p));
UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE);
#undef LOCAL_HEAP_SIZE
erts_smp_mtx_unlock(&smq_mtx);
@@ -2584,25 +2588,26 @@ trace_port_open(Port *p, Eterm calling_pid, Eterm drv_name) {
size_t sz_data;
ERTS_TRACER_REF_TYPE tracer_ref;
- ASSERT(is_internal_pid(p->tracer_proc)
- && internal_pid_index(p->tracer_proc) < erts_max_processes);
+ ASSERT(is_internal_pid(ERTS_TRACER_PROC(p)));
sz_data = 6 + TS_SIZE(p);
- ERTS_GET_TRACER_REF(tracer_ref, p->tracer_proc, p->trace_flags);
+ ERTS_GET_TRACER_REF(tracer_ref,
+ ERTS_TRACER_PROC(p),
+ ERTS_TRACE_FLAGS(p));
hp = ERTS_ALLOC_SYSMSG_HEAP(sz_data, &bp, &off_heap, tracer_ref);
- mess = TUPLE5(hp, am_trace, calling_pid, am_open, p->id, drv_name);
+ mess = TUPLE5(hp, am_trace, calling_pid, am_open, p->common.id, drv_name);
hp += 6;
erts_smp_mtx_lock(&smq_mtx);
- if (p->trace_flags & F_TIMESTAMP) {
+ if (ERTS_TRACE_FLAGS(p) & F_TIMESTAMP) {
hp = patch_ts(mess, hp);
}
- ERTS_ENQ_TRACE_MSG(p->id, tracer_ref, mess, bp);
+ ERTS_ENQ_TRACE_MSG(p->common.id, tracer_ref, mess, bp);
erts_smp_mtx_unlock(&smq_mtx);
}
@@ -2623,20 +2628,20 @@ trace_port(Port *t_p, Eterm what, Eterm data) {
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(t_p)
|| erts_thr_progress_is_blocking());
- if (is_internal_port(t_p->tracer_proc)) {
+ if (is_internal_port(ERTS_TRACER_PROC(t_p))) {
#define LOCAL_HEAP_SIZE (5+5)
DeclareTmpHeapNoproc(local_heap,LOCAL_HEAP_SIZE);
UseTmpHeapNoproc(LOCAL_HEAP_SIZE);
hp = local_heap;
- mess = TUPLE4(hp, am_trace, t_p->id, what, data);
+ mess = TUPLE4(hp, am_trace, t_p->common.id, what, data);
hp += 5;
erts_smp_mtx_lock(&smq_mtx);
- if (t_p->trace_flags & F_TIMESTAMP) {
+ if (ERTS_TRACE_FLAGS(t_p) & F_TIMESTAMP) {
hp = patch_ts(mess, hp);
}
/* No fake schedule */
- send_to_port(NULL, mess, &t_p->tracer_proc, &t_p->trace_flags);
+ send_to_port(NULL,mess,&ERTS_TRACER_PROC(t_p),&ERTS_TRACE_FLAGS(t_p));
UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE);
#undef LOCAL_HEAP_SIZE
erts_smp_mtx_unlock(&smq_mtx);
@@ -2646,25 +2651,26 @@ trace_port(Port *t_p, Eterm what, Eterm data) {
size_t sz_data;
ERTS_TRACER_REF_TYPE tracer_ref;
- ASSERT(is_internal_pid(t_p->tracer_proc)
- && internal_pid_index(t_p->tracer_proc) < erts_max_processes);
+ ASSERT(is_internal_pid(ERTS_TRACER_PROC(t_p)));
sz_data = 5 + TS_SIZE(t_p);
- ERTS_GET_TRACER_REF(tracer_ref, t_p->tracer_proc, t_p->trace_flags);
+ ERTS_GET_TRACER_REF(tracer_ref,
+ ERTS_TRACER_PROC(t_p),
+ ERTS_TRACE_FLAGS(t_p));
hp = ERTS_ALLOC_SYSMSG_HEAP(sz_data, &bp, &off_heap, tracer_ref);
- mess = TUPLE4(hp, am_trace, t_p->id, what, data);
+ mess = TUPLE4(hp, am_trace, t_p->common.id, what, data);
hp += 5;
erts_smp_mtx_lock(&smq_mtx);
- if (t_p->trace_flags & F_TIMESTAMP) {
+ if (ERTS_TRACE_FLAGS(t_p) & F_TIMESTAMP) {
hp = patch_ts(mess, hp);
}
- ERTS_ENQ_TRACE_MSG(t_p->id, tracer_ref, mess, bp);
+ ERTS_ENQ_TRACE_MSG(t_p->common.id, tracer_ref, mess, bp);
erts_smp_mtx_unlock(&smq_mtx);
}
}
@@ -2689,7 +2695,7 @@ trace_sched_ports_where(Port *p, Eterm what, Eterm where) {
int ws = 5;
Eterm sched_id = am_undefined;
- if (is_internal_port(p->tracer_proc)) {
+ if (is_internal_port(ERTS_TRACER_PROC(p))) {
#define LOCAL_HEAP_SIZE (5+6)
DeclareTmpHeapNoproc(local_heap,LOCAL_HEAP_SIZE);
UseTmpHeapNoproc(LOCAL_HEAP_SIZE);
@@ -2704,21 +2710,21 @@ trace_sched_ports_where(Port *p, Eterm what, Eterm where) {
#else
sched_id = make_small(1);
#endif
- mess = TUPLE5(hp, am_trace, p->id, what, sched_id, where);
+ mess = TUPLE5(hp, am_trace, p->common.id, what, sched_id, where);
ws = 6;
} else {
- mess = TUPLE4(hp, am_trace, p->id, what, where);
+ mess = TUPLE4(hp, am_trace, p->common.id, what, where);
ws = 5;
}
hp += ws;
erts_smp_mtx_lock(&smq_mtx);
- if (p->trace_flags & F_TIMESTAMP) {
+ if (ERTS_TRACE_FLAGS(p) & F_TIMESTAMP) {
hp = patch_ts(mess, hp);
}
/* No fake scheduling */
- send_to_port(NULL, mess, &p->tracer_proc, &p->trace_flags);
+ send_to_port(NULL, mess, &ERTS_TRACER_PROC(p), &ERTS_TRACE_FLAGS(p));
UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE);
#undef LOCAL_HEAP_SIZE
erts_smp_mtx_unlock(&smq_mtx);
@@ -2727,12 +2733,13 @@ trace_sched_ports_where(Port *p, Eterm what, Eterm where) {
ErlOffHeap *off_heap;
ERTS_TRACER_REF_TYPE tracer_ref;
- ASSERT(is_internal_pid(p->tracer_proc)
- && internal_pid_index(p->tracer_proc) < erts_max_processes);
+ ASSERT(is_internal_pid(ERTS_TRACER_PROC(p)));
if (IS_TRACED_FL(p, F_TRACE_SCHED_NO)) ws = 6; /* Make place for scheduler id */
- ERTS_GET_TRACER_REF(tracer_ref, p->tracer_proc, p->trace_flags);
+ ERTS_GET_TRACER_REF(tracer_ref,
+ ERTS_TRACER_PROC(p),
+ ERTS_TRACE_FLAGS(p));
hp = ERTS_ALLOC_SYSMSG_HEAP(ws+TS_SIZE(p), &bp, &off_heap, tracer_ref);
@@ -2744,19 +2751,19 @@ trace_sched_ports_where(Port *p, Eterm what, Eterm where) {
#else
sched_id = make_small(1);
#endif
- mess = TUPLE5(hp, am_trace, p->id, what, sched_id, where);
+ mess = TUPLE5(hp, am_trace, p->common.id, what, sched_id, where);
} else {
- mess = TUPLE4(hp, am_trace, p->id, what, where);
+ mess = TUPLE4(hp, am_trace, p->common.id, what, where);
}
hp += ws;
erts_smp_mtx_lock(&smq_mtx);
- if (p->trace_flags & F_TIMESTAMP) {
+ if (ERTS_TRACE_FLAGS(p) & F_TIMESTAMP) {
hp = patch_ts(mess, hp);
}
- ERTS_ENQ_TRACE_MSG(p->id, tracer_ref, mess, bp);
+ ERTS_ENQ_TRACE_MSG(p->common.id, tracer_ref, mess, bp);
erts_smp_mtx_unlock(&smq_mtx);
}
}
@@ -2792,14 +2799,14 @@ profile_runnable_port(Port *p, Eterm status) {
GET_NOW(&Ms, &s, &us);
timestamp = TUPLE3(hp, make_small(Ms), make_small(s), make_small(us)); hp += 4;
- msg = TUPLE5(hp, am_profile, p->id, status, count, timestamp); hp += 6;
+ msg = TUPLE5(hp, am_profile, p->common.id, status, count, timestamp); hp += 6;
#ifndef ERTS_SMP
- profile_send(p->id, msg);
+ profile_send(p->common.id, msg);
UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE);
#undef LOCAL_HEAP_SIZE
#else
- enqueue_sys_msg_unlocked(SYS_MSG_TYPE_SYSPROF, p->id, NIL, msg, bp);
+ enqueue_sys_msg_unlocked(SYS_MSG_TYPE_SYSPROF, p->common.id, NIL, msg, bp);
#endif
erts_smp_mtx_unlock(&smq_mtx);
}
@@ -2846,13 +2853,13 @@ profile_runnable_proc(Process *p, Eterm status){
GET_NOW(&Ms, &s, &us);
timestamp = TUPLE3(hp, make_small(Ms), make_small(s), make_small(us)); hp += 4;
- msg = TUPLE5(hp, am_profile, p->id, status, where, timestamp); hp += 6;
+ msg = TUPLE5(hp, am_profile, p->common.id, status, where, timestamp); hp += 6;
#ifndef ERTS_SMP
- profile_send(p->id, msg);
+ profile_send(p->common.id, msg);
UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE);
#undef LOCAL_HEAP_SIZE
#else
- enqueue_sys_msg_unlocked(SYS_MSG_TYPE_SYSPROF, p->id, NIL, msg, bp);
+ enqueue_sys_msg_unlocked(SYS_MSG_TYPE_SYSPROF, p->common.id, NIL, msg, bp);
#endif
erts_smp_mtx_unlock(&smq_mtx);
}
@@ -2865,16 +2872,19 @@ profile_runnable_proc(Process *p, Eterm status){
void
erts_check_my_tracer_proc(Process *p)
{
- if (is_internal_pid(p->tracer_proc)) {
- Process *tracer = erts_pid2proc(p, ERTS_PROC_LOCK_MAIN,
- p->tracer_proc, ERTS_PROC_LOCK_STATUS);
- int invalid_tracer = !tracer || !(tracer->trace_flags & F_TRACER);
+ if (is_internal_pid(ERTS_TRACER_PROC(p))) {
+ Process *tracer = erts_pid2proc(p,
+ ERTS_PROC_LOCK_MAIN,
+ ERTS_TRACER_PROC(p),
+ ERTS_PROC_LOCK_STATUS);
+ int invalid_tracer = (!tracer
+ || !(ERTS_TRACE_FLAGS(tracer) & F_TRACER));
if (tracer)
erts_smp_proc_unlock(tracer, ERTS_PROC_LOCK_STATUS);
if (invalid_tracer) {
erts_smp_proc_lock(p, ERTS_PROC_LOCKS_ALL_MINOR);
- p->trace_flags &= ~TRACEE_FLAGS;
- p->tracer_proc = NIL;
+ ERTS_TRACE_FLAGS(p) &= ~TRACEE_FLAGS;
+ ERTS_TRACER_PROC(p) = NIL;
erts_smp_proc_unlock(p, ERTS_PROC_LOCKS_ALL_MINOR);
}
}
@@ -3218,7 +3228,7 @@ sys_msg_dispatcher_func(void *unused)
proc = erts_pid2proc(NULL, 0, receiver, proc_locks);
if (!proc
|| (smqp->type == SYS_MSG_TYPE_TRACE
- && !(proc->trace_flags & F_TRACER))) {
+ && !(ERTS_TRACE_FLAGS(proc) & F_TRACER))) {
/* Bad tracer */
#ifdef DEBUG_PRINTOUTS
if (smqp->type == SYS_MSG_TYPE_TRACE && proc)
@@ -3245,16 +3255,14 @@ sys_msg_dispatcher_func(void *unused)
proc = erts_whereis_process(NULL,0,receiver,proc_locks,0);
if (!proc)
goto failure;
- else if (smqp->from == proc->id)
+ else if (smqp->from == proc->common.id)
goto drop_sys_msg;
else
goto queue_proc_msg;
}
else if (is_internal_port(receiver)) {
- port = erts_id2port_sflgs(receiver,
- NULL,
- 0,
- ERTS_PORT_SFLGS_INVALID_TRACER_LOOKUP);
+ port = erts_thr_id2port_sflgs(receiver,
+ ERTS_PORT_SFLGS_INVALID_TRACER_LOOKUP);
if (!port)
goto failure;
else {
@@ -3268,7 +3276,7 @@ sys_msg_dispatcher_func(void *unused)
#ifdef DEBUG_PRINTOUTS
erts_fprintf(stderr, "delivered\n");
#endif
- erts_port_release(port);
+ erts_thr_port_release(port);
if (smqp->bp)
free_message_buffer(smqp->bp);
}
diff --git a/erts/emulator/beam/erl_trace.h b/erts/emulator/beam/erl_trace.h
new file mode 100644
index 0000000000..50fb27aab0
--- /dev/null
+++ b/erts/emulator/beam/erl_trace.h
@@ -0,0 +1,141 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2012. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, 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 ERL_TRACE_H__
+#define ERL_TRACE_H__
+
+struct binary;
+
+/* erl_bif_trace.c */
+Eterm erl_seq_trace_info(Process *p, Eterm arg1);
+void erts_system_monitor_clear(Process *c_p);
+void erts_system_profile_clear(Process *c_p);
+
+/* erl_trace.c */
+void erts_init_trace(void);
+void erts_trace_check_exiting(Eterm exiting);
+Eterm erts_set_system_seq_tracer(Process *c_p,
+ ErtsProcLocks c_p_locks,
+ Eterm new);
+Eterm erts_get_system_seq_tracer(void);
+void erts_change_default_tracing(int setflags, Uint *flagsp, Eterm *tracerp);
+void erts_get_default_tracing(Uint *flagsp, Eterm *tracerp);
+void erts_set_system_monitor(Eterm monitor);
+Eterm erts_get_system_monitor(void);
+
+#ifdef ERTS_SMP
+void erts_check_my_tracer_proc(Process *);
+void erts_block_sys_msg_dispatcher(void);
+void erts_release_sys_msg_dispatcher(void);
+void erts_foreach_sys_msg_in_q(void (*func)(Eterm,
+ Eterm,
+ Eterm,
+ ErlHeapFragment *));
+void erts_queue_error_logger_message(Eterm, Eterm, ErlHeapFragment *);
+#endif
+
+void erts_send_sys_msg_proc(Eterm, Eterm, Eterm, ErlHeapFragment *);
+void trace_send(Process*, Eterm, Eterm);
+void trace_receive(Process*, Eterm);
+Uint32 erts_call_trace(Process *p, BeamInstr mfa[], struct binary *match_spec, Eterm* args,
+ int local, Eterm *tracer_pid);
+void erts_trace_return(Process* p, BeamInstr* fi, Eterm retval, Eterm *tracer_pid);
+void erts_trace_exception(Process* p, BeamInstr mfa[], Eterm class, Eterm value,
+ Eterm *tracer);
+void erts_trace_return_to(Process *p, BeamInstr *pc);
+void trace_sched(Process*, Eterm);
+void trace_proc(Process*, Process*, Eterm, Eterm);
+void trace_proc_spawn(Process*, Eterm pid, Eterm mod, Eterm func, Eterm args);
+void save_calls(Process *p, Export *);
+void trace_gc(Process *p, Eterm what);
+/* port tracing */
+void trace_virtual_sched(Process*, Eterm);
+void trace_sched_ports(Port *pp, Eterm);
+void trace_sched_ports_where(Port *pp, Eterm, Eterm);
+void trace_port(Port *, Eterm what, Eterm data);
+void trace_port_open(Port *, Eterm calling_pid, Eterm drv_name);
+
+/* system_profile */
+void erts_set_system_profile(Eterm profile);
+Eterm erts_get_system_profile(void);
+void profile_scheduler(Eterm scheduler_id, Eterm);
+void profile_scheduler_q(Eterm scheduler_id, Eterm state, Eterm no_schedulers, Uint Ms, Uint s, Uint us);
+void profile_runnable_proc(Process* p, Eterm status);
+void profile_runnable_port(Port* p, Eterm status);
+void erts_system_profile_setup_active_schedulers(void);
+
+/* system_monitor */
+void monitor_long_gc(Process *p, Uint time);
+void monitor_large_heap(Process *p);
+void monitor_generic(Process *p, Eterm type, Eterm spec);
+Uint erts_trace_flag2bit(Eterm flag);
+int erts_trace_flags(Eterm List,
+ Uint *pMask, Eterm *pTracer, int *pCpuTimestamp);
+Eterm erts_bif_trace(int bif_index, Process* p, Eterm* args, BeamInstr *I);
+
+#ifdef ERTS_SMP
+void erts_send_pending_trace_msgs(ErtsSchedulerData *esdp);
+#define ERTS_SMP_CHK_PEND_TRACE_MSGS(ESDP) \
+do { \
+ if ((ESDP)->pending_trace_msgs) \
+ erts_send_pending_trace_msgs((ESDP)); \
+} while (0)
+#else
+#define ERTS_SMP_CHK_PEND_TRACE_MSGS(ESDP)
+#endif
+
+#define seq_trace_output(token, msg, type, receiver, process) \
+seq_trace_output_generic((token), (msg), (type), (receiver), (process), NIL)
+#define seq_trace_output_exit(token, msg, type, receiver, exitfrom) \
+seq_trace_output_generic((token), (msg), (type), (receiver), NULL, (exitfrom))
+void seq_trace_output_generic(Eterm token, Eterm msg, Uint type,
+ Eterm receiver, Process *process, Eterm exitfrom);
+
+int seq_trace_update_send(Process *process);
+
+Eterm erts_seq_trace(Process *process,
+ Eterm atom_type, Eterm atom_true_or_false,
+ int build_result);
+
+struct trace_pattern_flags {
+ unsigned int breakpoint : 1; /* Set if any other is set */
+ unsigned int local : 1; /* Local call trace breakpoint */
+ unsigned int meta : 1; /* Metadata trace breakpoint */
+ unsigned int call_count : 1; /* Fast call count breakpoint */
+ unsigned int call_time : 1; /* Fast call time breakpoint */
+};
+extern const struct trace_pattern_flags erts_trace_pattern_flags_off;
+extern int erts_call_time_breakpoint_tracing;
+int erts_set_trace_pattern(Process*p, Eterm* mfa, int specified,
+ struct binary* match_prog_set,
+ struct binary *meta_match_prog_set,
+ int on, struct trace_pattern_flags,
+ Eterm meta_tracer_pid, int is_blocking);
+void
+erts_get_default_trace_pattern(int *trace_pattern_is_on,
+ struct binary **match_spec,
+ struct binary **meta_match_spec,
+ struct trace_pattern_flags *trace_pattern_flags,
+ Eterm *meta_tracer_pid);
+int erts_is_default_trace_enabled(void);
+void erts_bif_trace_init(void);
+int erts_finish_breakpointing(void);
+
+#endif /* ERL_TRACE_H__ */
diff --git a/erts/emulator/beam/erl_unicode.c b/erts/emulator/beam/erl_unicode.c
index 049226ab7d..883405d066 100644
--- a/erts/emulator/beam/erl_unicode.c
+++ b/erts/emulator/beam/erl_unicode.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2008-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2008-2012. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -1154,15 +1154,24 @@ BIF_RETTYPE unicode_characters_to_list_2(BIF_ALIST_2)
* When input to characters_to_list is a plain binary and the format is 'unicode', we do
* a faster analyze and size count with this function.
*/
-int erts_analyze_utf8(byte *source, Uint size,
- byte **err_pos, Uint *num_chars, int *left)
+static ERTS_INLINE int
+analyze_utf8(byte *source, Uint size, byte **err_pos, Uint *num_chars, int *left,
+ Sint *num_latin1_chars, Uint max_chars)
{
+ Uint latin1_count;
+ int is_latin1;
*err_pos = source;
+ if (num_latin1_chars) {
+ is_latin1 = 1;
+ latin1_count = 0;
+ }
*num_chars = 0;
while (size) {
if (((*source) & ((byte) 0x80)) == 0) {
source++;
- --size;
+ --size;
+ if (num_latin1_chars)
+ latin1_count++;
} else if (((*source) & ((byte) 0xE0)) == 0xC0) {
if (size < 2) {
return ERTS_UTF8_INCOMPLETE;
@@ -1171,6 +1180,11 @@ int erts_analyze_utf8(byte *source, Uint size,
((*source) < 0xC2) /* overlong */) {
return ERTS_UTF8_ERROR;
}
+ if (num_latin1_chars) {
+ latin1_count++;
+ if ((source[0] & ((byte) 0xFC)) != ((byte) 0xC0))
+ is_latin1 = 0;
+ }
source += 2;
size -= 2;
} else if (((*source) & ((byte) 0xF0)) == 0xE0) {
@@ -1188,6 +1202,8 @@ int erts_analyze_utf8(byte *source, Uint size,
}
source += 3;
size -= 3;
+ if (num_latin1_chars)
+ is_latin1 = 0;
} else if (((*source) & ((byte) 0xF8)) == 0xF0) {
if (size < 4) {
return ERTS_UTF8_INCOMPLETE;
@@ -1205,21 +1221,40 @@ int erts_analyze_utf8(byte *source, Uint size,
}
source += 4;
size -= 4;
+ if (num_latin1_chars)
+ is_latin1 = 0;
} else {
return ERTS_UTF8_ERROR;
}
++(*num_chars);
*err_pos = source;
- if (left && --(*left) <= 0) {
+ if (max_chars && size > 0 && *num_chars == max_chars)
+ return ERTS_UTF8_OK_MAX_CHARS;
+ if (left && --(*left) <= 0 && size) {
return ERTS_UTF8_ANALYZE_MORE;
}
}
+ if (num_latin1_chars)
+ *num_latin1_chars = is_latin1 ? latin1_count : -1;
return ERTS_UTF8_OK;
}
+int erts_analyze_utf8(byte *source, Uint size,
+ byte **err_pos, Uint *num_chars, int *left)
+{
+ return analyze_utf8(source, size, err_pos, num_chars, left, NULL, 0);
+}
+
+int erts_analyze_utf8_x(byte *source, Uint size,
+ byte **err_pos, Uint *num_chars, int *left,
+ Sint *num_latin1_chars, Uint max_chars)
+{
+ return analyze_utf8(source, size, err_pos, num_chars, left, num_latin1_chars, max_chars);
+}
+
/*
* No errors should be able to occur - no overlongs, no malformed, no nothing
- */
+ */
static Eterm do_utf8_to_list(Process *p, Uint num, byte *bytes, Uint sz,
Uint left,
Uint *num_built, Uint *num_eaten, Eterm tail)
@@ -1275,6 +1310,12 @@ static Eterm do_utf8_to_list(Process *p, Uint num, byte *bytes, Uint sz,
return ret;
}
+Eterm erts_utf8_to_list(Process *p, Uint num, byte *bytes, Uint sz, Uint left,
+ Uint *num_built, Uint *num_eaten, Eterm tail)
+{
+ return do_utf8_to_list(p, num, bytes, sz, left, num_built, num_eaten, tail);
+}
+
static int is_candidate(Uint cp)
{
int index,pos;
@@ -1812,31 +1853,25 @@ BIF_RETTYPE atom_to_binary_2(BIF_ALIST_2)
ap = atom_tab(atom_val(BIF_ARG_1));
if (BIF_ARG_2 == am_latin1) {
- BIF_RET(new_binary(BIF_P, ap->name, ap->len));
- } else if (BIF_ARG_2 == am_utf8 || BIF_ARG_2 == am_unicode) {
- int bin_size = 0;
- int i;
Eterm bin_term;
- byte* bin_p;
- for (i = 0; i < ap->len; i++) {
- bin_size += (ap->name[i] >= 0x80) ? 2 : 1;
+ if (ap->latin1_chars < 0) {
+ goto error;
}
- if (bin_size == ap->len) {
- BIF_RET(new_binary(BIF_P, ap->name, ap->len));
+ if (ap->latin1_chars == ap->len) {
+ bin_term = new_binary(BIF_P, ap->name, ap->len);
}
- bin_term = new_binary(BIF_P, 0, bin_size);
- bin_p = binary_bytes(bin_term);
- for (i = 0; i < ap->len; i++) {
- byte b = ap->name[i];
- if (b < 0x80) {
- *bin_p++ = b;
- } else {
- *bin_p++ = 0xC0 | (b >> 6);
- *bin_p++ = 0x80 | (b & 0x3F);
- }
+ else {
+ byte* bin_p;
+ int dbg_sz;
+ bin_term = new_binary(BIF_P, 0, ap->latin1_chars);
+ bin_p = binary_bytes(bin_term);
+ dbg_sz = erts_utf8_to_latin1(bin_p, ap->name, ap->len);
+ ASSERT(dbg_sz == ap->latin1_chars); (void)dbg_sz;
}
BIF_RET(bin_term);
+ } else if (BIF_ARG_2 == am_utf8 || BIF_ARG_2 == am_unicode) {
+ BIF_RET(new_binary(BIF_P, ap->name, ap->len));
} else {
error:
BIF_ERROR(BIF_P, BADARG);
@@ -1844,118 +1879,78 @@ BIF_RETTYPE atom_to_binary_2(BIF_ALIST_2)
}
static BIF_RETTYPE
-binary_to_atom(Process* p, Eterm bin, Eterm enc, int must_exist)
+binary_to_atom(Process* proc, Eterm bin, Eterm enc, int must_exist)
{
byte* bytes;
byte *temp_alloc = NULL;
Uint bin_size;
if ((bytes = erts_get_aligned_binary_bytes(bin, &temp_alloc)) == 0) {
- BIF_ERROR(p, BADARG);
+ BIF_ERROR(proc, BADARG);
}
bin_size = binary_size(bin);
if (enc == am_latin1) {
Eterm a;
- if (bin_size > MAX_ATOM_LENGTH) {
+ if (bin_size > MAX_ATOM_CHARACTERS) {
system_limit:
erts_free_aligned_binary_bytes(temp_alloc);
- BIF_ERROR(p, SYSTEM_LIMIT);
+ BIF_ERROR(proc, SYSTEM_LIMIT);
}
if (!must_exist) {
- a = am_atom_put((char *)bytes, bin_size);
- erts_free_aligned_binary_bytes(temp_alloc);
+ a = erts_atom_put((byte *) bytes,
+ bin_size,
+ ERTS_ATOM_ENC_LATIN1,
+ 0);
+ erts_free_aligned_binary_bytes(temp_alloc);
+ if (is_non_value(a))
+ goto badarg;
BIF_RET(a);
- } else if (erts_atom_get((char *)bytes, bin_size, &a)) {
+ } else if (erts_atom_get((char *)bytes, bin_size, &a, 1)) {
erts_free_aligned_binary_bytes(temp_alloc);
BIF_RET(a);
} else {
goto badarg;
}
} else if (enc == am_utf8 || enc == am_unicode) {
- char *buf;
- char *dst;
- int i;
- int num_chars;
Eterm res;
+ Uint num_chars = 0;
+ const byte* p = bytes;
+ Uint left = bin_size;
- if (bin_size > 2*MAX_ATOM_LENGTH) {
- byte* err_pos;
- Uint n;
- int reds_left = bin_size+1; /* Number of reductions left. */
-
- if (erts_analyze_utf8(bytes, bin_size, &err_pos,
- &n, &reds_left) == ERTS_UTF8_OK) {
- /*
- * Correct UTF-8 encoding, but too many characters to
- * fit in an atom.
- */
+ while (left) {
+ if (++num_chars > MAX_ATOM_CHARACTERS) {
goto system_limit;
- } else {
- /*
- * Something wrong in the UTF-8 encoding or Unicode code
- * points > 255.
- */
- goto badarg;
}
- }
-
- /*
- * Allocate a temporary buffer the same size as the binary,
- * so that we don't need an extra overflow test.
- */
- buf = (char *) erts_alloc(ERTS_ALC_T_TMP, bin_size);
- dst = buf;
- for (i = 0; i < bin_size; i++) {
- int c = bytes[i];
- if (c < 0x80) {
- *dst++ = c;
- } else if (i < bin_size-1) {
- int c2;
- if ((c & 0xE0) != 0xC0) {
- goto free_badarg;
- }
- i++;
- c = (c & 0x3F) << 6;
- c2 = bytes[i];
- if ((c2 & 0xC0) != 0x80) {
- goto free_badarg;
- }
- c = c | (c2 & 0x3F);
- if (0x80 <= c && c < 256) {
- *dst++ = c;
- } else {
- goto free_badarg;
- }
- } else {
- free_badarg:
- erts_free(ERTS_ALC_T_TMP, (void *) buf);
- goto badarg;
+ if ((p[0] & 0x80) == 0) {
+ ++p;
+ --left;
}
+ else if (left >= 2
+ && (p[0] & 0xFE) == 0xC2 /* only allow latin1 subset */
+ && (p[1] & 0xC0) == 0x80) {
+ p += 2;
+ left -= 2;
+ }
+ else goto badarg;
}
- num_chars = dst - buf;
- if (num_chars > MAX_ATOM_LENGTH) {
- erts_free(ERTS_ALC_T_TMP, (void *) buf);
- goto system_limit;
- }
+
if (!must_exist) {
- res = am_atom_put(buf, num_chars);
- erts_free(ERTS_ALC_T_TMP, (void *) buf);
- erts_free_aligned_binary_bytes(temp_alloc);
- BIF_RET(res);
- } else {
- int exists = erts_atom_get(buf, num_chars, &res);
- erts_free(ERTS_ALC_T_TMP, (void *) buf);
- if (exists) {
- erts_free_aligned_binary_bytes(temp_alloc);
- BIF_RET(res);
- } else {
- goto badarg;
- }
+ res = erts_atom_put((byte *) bytes,
+ bin_size,
+ ERTS_ATOM_ENC_UTF8,
+ 0);
+ }
+ else if (!erts_atom_get((char*)bytes, bin_size, &res, 0)) {
+ goto badarg;
}
+ erts_free_aligned_binary_bytes(temp_alloc);
+ if (is_non_value(res))
+ goto badarg;
+ BIF_RET(res);
} else {
badarg:
erts_free_aligned_binary_bytes(temp_alloc);
- BIF_ERROR(p, BADARG);
+ BIF_ERROR(proc, BADARG);
}
}
@@ -1986,12 +1981,14 @@ BIF_RETTYPE binary_to_existing_atom_2(BIF_ALIST_2)
* string routines, that will certainly fail on some OS.
*/
-char *erts_convert_filename_to_native(Eterm name, ErtsAlcType_t alloc_type, int allow_empty)
+char *erts_convert_filename_to_native(Eterm name, char *statbuf, size_t statbuf_size, ErtsAlcType_t alloc_type, int allow_empty, int allow_atom, Sint *used)
{
int encoding = erts_get_native_filename_encoding();
char* name_buf = NULL;
- if (is_atom(name) || is_list(name) || (allow_empty && is_nil(name))) {
+ if ((allow_atom && is_atom(name)) ||
+ is_list(name) ||
+ (allow_empty && is_nil(name))) {
Sint need;
if ((need = erts_native_filename_need(name,encoding)) < 0) {
return NULL;
@@ -2001,7 +1998,13 @@ char *erts_convert_filename_to_native(Eterm name, ErtsAlcType_t alloc_type, int
} else {
++need;
}
- name_buf = (char *) erts_alloc(alloc_type, need);
+ if (used)
+ *used = (Sint) need;
+ if (need > statbuf_size) {
+ name_buf = (char *) erts_alloc(alloc_type, need);
+ } else {
+ name_buf = statbuf;
+ }
erts_native_filename_put(name,encoding,(byte *)name_buf);
name_buf[need-1] = 0;
if (encoding == ERL_FILENAME_WIN_WCHAR) {
@@ -2017,14 +2020,26 @@ char *erts_convert_filename_to_native(Eterm name, ErtsAlcType_t alloc_type, int
bytes = erts_get_aligned_binary_bytes(name, &temp_alloc);
if (encoding != ERL_FILENAME_WIN_WCHAR) {
/*Add 0 termination only*/
- name_buf = (char *) erts_alloc(alloc_type, size+1);
+ if (used)
+ *used = (Sint) size+1;
+ if (size+1 > statbuf_size) {
+ name_buf = (char *) erts_alloc(alloc_type, size+1);
+ } else {
+ name_buf = statbuf;
+ }
memcpy(name_buf,bytes,size);
name_buf[size]=0;
} else if (erts_analyze_utf8(bytes,size,&err_pos,&num_chars,NULL) != ERTS_UTF8_OK ||
erts_get_user_requested_filename_encoding() == ERL_FILENAME_LATIN1) {
byte *p;
/* What to do now? Maybe latin1, so just take byte for byte instead */
- name_buf = (char *) erts_alloc(alloc_type, (size+1)*2);
+ if (used)
+ *used = (Sint) (size+1)*2;
+ if ((size+1)*2 > statbuf_size) {
+ name_buf = (char *) erts_alloc(alloc_type, (size+1)*2);
+ } else {
+ name_buf = statbuf;
+ }
p = (byte *) name_buf;
while (size--) {
*p++ = *bytes++;
@@ -2033,7 +2048,13 @@ char *erts_convert_filename_to_native(Eterm name, ErtsAlcType_t alloc_type, int
*p++ = 0;
*p++ = 0;
} else { /* WIN_WCHAR and valid UTF8 */
- name_buf = (char *) erts_alloc(alloc_type, (num_chars+1)*2);
+ if (used)
+ *used = (Sint) (num_chars+1)*2;
+ if ((num_chars+1)*2 > statbuf_size) {
+ name_buf = (char *) erts_alloc(alloc_type, (num_chars+1)*2);
+ } else {
+ name_buf = statbuf;
+ }
erts_copy_utf8_to_utf16_little((byte *) name_buf, bytes, num_chars);
name_buf[num_chars*2] = 0;
name_buf[num_chars*2+1] = 0;
@@ -2045,6 +2066,71 @@ char *erts_convert_filename_to_native(Eterm name, ErtsAlcType_t alloc_type, int
return name_buf;
}
+static int filename_len_16bit(byte *str)
+{
+ byte *p = str;
+ while(*p != '\0' || p[1] != '\0') {
+ p += 2;
+ }
+ return (p - str);
+}
+Eterm erts_convert_native_to_filename(Process *p, byte *bytes)
+{
+ Uint size,num_chars;
+ Eterm *hp;
+ byte *err_pos;
+ Uint num_built; /* characters */
+ Uint num_eaten; /* bytes */
+ Eterm ret;
+ int mac = 0;
+
+ switch (erts_get_native_filename_encoding()) {
+ case ERL_FILENAME_LATIN1:
+ goto noconvert;
+ case ERL_FILENAME_UTF8_MAC:
+ mac = 1;
+ case ERL_FILENAME_UTF8:
+ size = strlen((char *) bytes);
+ if (erts_analyze_utf8(bytes,size,&err_pos,&num_chars,NULL) != ERTS_UTF8_OK) {
+ goto noconvert;
+ }
+ num_built = 0;
+ num_eaten = 0;
+ if (mac) {
+ ret = do_utf8_to_list_normalize(p, num_chars, bytes, size);
+ } else {
+ ret = do_utf8_to_list(p, num_chars, bytes, size, num_chars, &num_built, &num_eaten, NIL);
+ }
+ return ret;
+ case ERL_FILENAME_WIN_WCHAR:
+ size=filename_len_16bit(bytes);
+ if ((size % 2) != 0) { /* Panic fixup to avoid crashing the emulator */
+ size--;
+ hp = HAlloc(p, size+2);
+ ret = CONS(hp,make_small((Uint) bytes[size]),NIL);
+ hp += 2;
+ } else {
+ hp = HAlloc(p, size);
+ ret = NIL;
+ }
+ bytes += size-1;
+ while (size > 0) {
+ Uint x = ((Uint) *bytes--) << 8;
+ x |= ((Uint) *bytes--);
+ size -= 2;
+ ret = CONS(hp,make_small(x),ret);
+ hp += 2;
+ }
+ return ret;
+ default:
+ goto noconvert;
+ }
+ noconvert:
+ size = strlen((char *) bytes);
+ hp = HAlloc(p, 2 * size);
+ return erts_bin_bytes_to_list(NIL, hp, bytes, size, 0);
+}
+
Sint erts_native_filename_need(Eterm ioterm, int encoding)
{
@@ -2578,3 +2664,29 @@ BIF_RETTYPE file_native_name_encoding_0(BIF_ALIST_0)
BIF_RET(am_undefined);
}
}
+
+int erts_utf8_to_latin1(byte* dest, const byte* source, int slen)
+{
+ /*
+ * Assumes source contains valid utf8 that can be encoded as latin1,
+ * and that dest has enough room.
+ */
+ byte* dp = dest;
+
+ while (slen > 0) {
+ if ((source[0] & 0x80) == 0) {
+ *dp++ = *source++;
+ --slen;
+ }
+ else {
+ ASSERT(slen > 1);
+ ASSERT((source[0] & 0xFE) == 0xC2);
+ ASSERT((source[1] & 0xC0) == 0x80);
+ *dp++ = (char) ((source[0] << 6) | (source[1] & 0x3F));
+ source += 2;
+ slen -= 2;
+ }
+ }
+ return dp - dest;
+}
+
diff --git a/erts/emulator/beam/erl_utils.h b/erts/emulator/beam/erl_utils.h
new file mode 100644
index 0000000000..a2064bd8a3
--- /dev/null
+++ b/erts/emulator/beam/erl_utils.h
@@ -0,0 +1,215 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2012. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, 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 ERL_UTILS_H__
+#define ERL_UTILS_H__
+
+#include "sys.h"
+#include "erl_smp.h"
+#include "erl_printf.h"
+
+typedef struct {
+#ifdef DEBUG
+ int smp_api;
+#endif
+ union {
+ Uint64 not_atomic;
+#ifdef ARCH_64
+ erts_atomic_t atomic;
+#else
+ erts_dw_atomic_t atomic;
+#endif
+ } counter;
+} erts_interval_t;
+
+void erts_interval_init(erts_interval_t *);
+void erts_smp_interval_init(erts_interval_t *);
+Uint64 erts_step_interval_nob(erts_interval_t *);
+Uint64 erts_step_interval_relb(erts_interval_t *);
+Uint64 erts_smp_step_interval_nob(erts_interval_t *);
+Uint64 erts_smp_step_interval_relb(erts_interval_t *);
+Uint64 erts_ensure_later_interval_nob(erts_interval_t *, Uint64);
+Uint64 erts_ensure_later_interval_acqb(erts_interval_t *, Uint64);
+Uint64 erts_smp_ensure_later_interval_nob(erts_interval_t *, Uint64);
+Uint64 erts_smp_ensure_later_interval_acqb(erts_interval_t *, Uint64);
+#ifdef ARCH_32
+ERTS_GLB_INLINE Uint64 erts_interval_dw_aint_to_val__(erts_dw_aint_t *);
+#endif
+ERTS_GLB_INLINE Uint64 erts_current_interval_nob__(erts_interval_t *);
+ERTS_GLB_INLINE Uint64 erts_current_interval_acqb__(erts_interval_t *);
+ERTS_GLB_INLINE Uint64 erts_current_interval_nob(erts_interval_t *);
+ERTS_GLB_INLINE Uint64 erts_current_interval_acqb(erts_interval_t *);
+ERTS_GLB_INLINE Uint64 erts_smp_current_interval_nob(erts_interval_t *);
+ERTS_GLB_INLINE Uint64 erts_smp_current_interval_acqb(erts_interval_t *);
+
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+
+#ifdef ARCH_32
+
+ERTS_GLB_INLINE Uint64
+erts_interval_dw_aint_to_val__(erts_dw_aint_t *dw)
+{
+#ifdef ETHR_SU_DW_NAINT_T__
+ return (Uint64) dw->dw_sint;
+#else
+ Uint64 res;
+ res = (Uint64) ((Uint32) dw->sint[ERTS_DW_AINT_HIGH_WORD]);
+ res <<= 32;
+ res |= (Uint64) ((Uint32) dw->sint[ERTS_DW_AINT_LOW_WORD]);
+ return res;
+#endif
+}
+
+#endif
+
+ERTS_GLB_INLINE Uint64
+erts_current_interval_nob__(erts_interval_t *icp)
+{
+#ifdef ARCH_64
+ return (Uint64) erts_atomic_read_nob(&icp->counter.atomic);
+#else
+ erts_dw_aint_t dw;
+ erts_dw_atomic_read_nob(&icp->counter.atomic, &dw);
+ return erts_interval_dw_aint_to_val__(&dw);
+#endif
+}
+
+ERTS_GLB_INLINE Uint64
+erts_current_interval_acqb__(erts_interval_t *icp)
+{
+#ifdef ARCH_64
+ return (Uint64) erts_atomic_read_acqb(&icp->counter.atomic);
+#else
+ erts_dw_aint_t dw;
+ erts_dw_atomic_read_acqb(&icp->counter.atomic, &dw);
+ return erts_interval_dw_aint_to_val__(&dw);
+#endif
+}
+
+ERTS_GLB_INLINE Uint64
+erts_current_interval_nob(erts_interval_t *icp)
+{
+ ASSERT(!icp->smp_api);
+ return erts_current_interval_nob__(icp);
+}
+
+ERTS_GLB_INLINE Uint64
+erts_current_interval_acqb(erts_interval_t *icp)
+{
+ ASSERT(!icp->smp_api);
+ return erts_current_interval_acqb__(icp);
+}
+
+ERTS_GLB_INLINE Uint64
+erts_smp_current_interval_nob(erts_interval_t *icp)
+{
+ ASSERT(icp->smp_api);
+#ifdef ERTS_SMP
+ return erts_current_interval_nob__(icp);
+#else
+ return icp->counter.not_atomic;
+#endif
+}
+
+ERTS_GLB_INLINE Uint64
+erts_smp_current_interval_acqb(erts_interval_t *icp)
+{
+ ASSERT(icp->smp_api);
+#ifdef ERTS_SMP
+ return erts_current_interval_acqb__(icp);
+#else
+ return icp->counter.not_atomic;
+#endif
+}
+
+#endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */
+
+/*
+ * To be used to silence unused result warnings, but do not abuse it.
+ */
+void erts_silence_warn_unused_result(long unused);
+
+
+int erts_fit_in_bits_int64(Sint64);
+int erts_fit_in_bits_int32(Sint32);
+int list_length(Eterm);
+int erts_is_builtin(Eterm, Eterm, int);
+Uint32 make_broken_hash(Eterm);
+Uint32 block_hash(byte *, unsigned, Uint32);
+Uint32 make_hash2(Eterm);
+Uint32 make_hash(Eterm);
+
+
+Eterm erts_bld_atom(Uint **hpp, Uint *szp, char *str);
+Eterm erts_bld_uint(Uint **hpp, Uint *szp, Uint ui);
+Eterm erts_bld_uword(Uint **hpp, Uint *szp, UWord uw);
+Eterm erts_bld_uint64(Uint **hpp, Uint *szp, Uint64 ui64);
+Eterm erts_bld_sint64(Uint **hpp, Uint *szp, Sint64 si64);
+Eterm erts_bld_cons(Uint **hpp, Uint *szp, Eterm car, Eterm cdr);
+Eterm erts_bld_tuple(Uint **hpp, Uint *szp, Uint arity, ...);
+Eterm erts_bld_tuplev(Uint **hpp, Uint *szp, Uint arity, Eterm terms[]);
+Eterm erts_bld_string_n(Uint **hpp, Uint *szp, const char *str, Sint len);
+#define erts_bld_string(hpp,szp,str) erts_bld_string_n(hpp,szp,str,strlen(str))
+Eterm erts_bld_list(Uint **hpp, Uint *szp, Sint length, Eterm terms[]);
+Eterm erts_bld_2tup_list(Uint **hpp, Uint *szp,
+ Sint length, Eterm terms1[], Uint terms2[]);
+Eterm
+erts_bld_atom_uint_2tup_list(Uint **hpp, Uint *szp,
+ Sint length, Eterm atoms[], Uint uints[]);
+Eterm
+erts_bld_atom_2uint_3tup_list(Uint **hpp, Uint *szp, Sint length,
+ Eterm atoms[], Uint uints1[], Uint uints2[]);
+
+void erts_init_utils(void);
+void erts_init_utils_mem(void);
+
+erts_dsprintf_buf_t *erts_create_tmp_dsbuf(Uint);
+void erts_destroy_tmp_dsbuf(erts_dsprintf_buf_t *);
+
+#if HALFWORD_HEAP
+int eq_rel(Eterm a, Eterm* a_base, Eterm b, Eterm* b_base);
+# define eq(A,B) eq_rel(A,NULL,B,NULL)
+#else
+int eq(Eterm, Eterm);
+# define eq_rel(A,A_BASE,B,B_BASE) eq(A,B)
+#endif
+
+#define EQ(x,y) (((x) == (y)) || (is_not_both_immed((x),(y)) && eq((x),(y))))
+
+#if HALFWORD_HEAP
+Sint cmp_rel(Eterm, Eterm*, Eterm, Eterm*);
+#define CMP(A,B) cmp_rel(A,NULL,B,NULL)
+#else
+Sint cmp(Eterm, Eterm);
+#define cmp_rel(A,A_BASE,B,B_BASE) cmp(A,B)
+#define CMP(A,B) cmp(A,B)
+#endif
+#define cmp_lt(a,b) (CMP((a),(b)) < 0)
+#define cmp_le(a,b) (CMP((a),(b)) <= 0)
+#define cmp_eq(a,b) (CMP((a),(b)) == 0)
+#define cmp_ne(a,b) (CMP((a),(b)) != 0)
+#define cmp_ge(a,b) (CMP((a),(b)) >= 0)
+#define cmp_gt(a,b) (CMP((a),(b)) > 0)
+
+#define CMP_LT(a,b) ((a) != (b) && cmp_lt((a),(b)))
+#define CMP_GE(a,b) ((a) == (b) || cmp_ge((a),(b)))
+#define CMP_EQ(a,b) ((a) == (b) || cmp_eq((a),(b)))
+#define CMP_NE(a,b) ((a) != (b) && cmp_ne((a),(b)))
+
+#endif
diff --git a/erts/emulator/beam/erl_vm.h b/erts/emulator/beam/erl_vm.h
index a0e12e57f2..c962955de9 100644
--- a/erts/emulator/beam/erl_vm.h
+++ b/erts/emulator/beam/erl_vm.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2011. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2012. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
diff --git a/erts/emulator/beam/erlang_dtrace.d b/erts/emulator/beam/erlang_dtrace.d
index c1024dafc4..e3ebbb84f4 100644
--- a/erts/emulator/beam/erlang_dtrace.d
+++ b/erts/emulator/beam/erlang_dtrace.d
@@ -639,9 +639,9 @@ provider erlang {
* Entry into the efile_drv.c file I/O driver
*
* For a list of command numbers used by this driver, see the section
- * "Guide to probe arguments" in ../../../README.md. That section
- * also contains explanation of the various integer and string
- * arguments that may be present when any particular probe fires.
+ * "Guide to efile_drv.c probe arguments" in ../../../HOWTO/DTRACE.md.
+ * That section also contains explanation of the various integer and
+ * string arguments that may be present when any particular probe fires.
*
* NOTE: Not all Linux platforms (using SystemTap) can support
* arguments beyond arg9.
@@ -717,6 +717,2916 @@ provider erlang {
* inside the same I/O worker pool thread.
* For R14B03's source, see erl_async.c lines 302-317.
*/
+
+/*
+ * The set of probes for use by Erlang code ... moved to here from
+ * lib/runtime_tools/c_src/dtrace_user.d until a more portable solution
+ * is found. This move pollutes the Erlang VM with functions that are
+ * only used by the NIF shared library code in
+ * lib/runtime_tools/c_src/dyntrace.c. The reason this is necessary is
+ * in order to work around an issue on several platforms, including
+ * SystemTap 1.3 and Solaris. The Solaris issue is discussed in the
+ * `dtrace-discuss` mailing list thread on 01 Dec 2011 17:58:15.
+ */
+ /**
+ * If you use only a single probe, but you also embed that probe
+ * in many different places in your code, if that probe fires 100K
+ * or more times per second, then it *will* hurt when you have to
+ * enable that probe.
+ *
+ * However, if you have any different probes, then you can ensure
+ * that any probe on a hot code path will use separate probe(s)
+ * than everyone else ... and you can then enable many non-hot
+ * probes in production without worry about creating too much
+ * measurement overhead.
+ *
+ * In an ideal world, we would use the libusdt library to be able
+ * to create arbitrary DTrace probes with more convenient and
+ * meaningful names than "user_trace-n7". But libusdt doesn't
+ * (yet) support all of the platforms that DTrace does, and there
+ * is no known (yet) equivalent for SystemTap.
+ */
+ probe user_trace__n0(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n1(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n2(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n3(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n4(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n5(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n6(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n7(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n8(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n9(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n10(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n11(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n12(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n13(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n14(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n15(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n16(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n17(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n18(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n19(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n20(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n21(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n22(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n23(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n24(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n25(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n26(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n27(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n28(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n29(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n30(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n31(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n32(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n33(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n34(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n35(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n36(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n37(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n38(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n39(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n40(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n41(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n42(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n43(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n44(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n45(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n46(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n47(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n48(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n49(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n50(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n51(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n52(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n53(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n54(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n55(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n56(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n57(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n58(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n59(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n60(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n61(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n62(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n63(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n64(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n65(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n66(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n67(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n68(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n69(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n70(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n71(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n72(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n73(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n74(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n75(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n76(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n77(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n78(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n79(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n80(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n81(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n82(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n83(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n84(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n85(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n86(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n87(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n88(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n89(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n90(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n91(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n92(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n93(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n94(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n95(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n96(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n97(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n98(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n99(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n100(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n101(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n102(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n103(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n104(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n105(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n106(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n107(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n108(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n109(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n110(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n111(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n112(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n113(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n114(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n115(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n116(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n117(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n118(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n119(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n120(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n121(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n122(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n123(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n124(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n125(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n126(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n127(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n128(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n129(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n130(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n131(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n132(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n133(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n134(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n135(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n136(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n137(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n138(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n139(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n140(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n141(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n142(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n143(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n144(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n145(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n146(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n147(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n148(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n149(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n150(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n151(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n152(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n153(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n154(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n155(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n156(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n157(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n158(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n159(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n160(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n161(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n162(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n163(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n164(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n165(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n166(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n167(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n168(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n169(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n170(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n171(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n172(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n173(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n174(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n175(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n176(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n177(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n178(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n179(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n180(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n181(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n182(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n183(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n184(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n185(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n186(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n187(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n188(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n189(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n190(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n191(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n192(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n193(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n194(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n195(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n196(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n197(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n198(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n199(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n200(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n201(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n202(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n203(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n204(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n205(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n206(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n207(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n208(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n209(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n210(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n211(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n212(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n213(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n214(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n215(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n216(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n217(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n218(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n219(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n220(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n221(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n222(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n223(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n224(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n225(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n226(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n227(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n228(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n229(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n230(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n231(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n232(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n233(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n234(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n235(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n236(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n237(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n238(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n239(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n240(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n241(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n242(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n243(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n244(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n245(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n246(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n247(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n248(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n249(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n250(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n251(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n252(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n253(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n254(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n255(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n256(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n257(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n258(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n259(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n260(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n261(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n262(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n263(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n264(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n265(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n266(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n267(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n268(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n269(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n270(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n271(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n272(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n273(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n274(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n275(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n276(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n277(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n278(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n279(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n280(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n281(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n282(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n283(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n284(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n285(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n286(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n287(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n288(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n289(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n290(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n291(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n292(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n293(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n294(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n295(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n296(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n297(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n298(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n299(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n300(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n301(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n302(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n303(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n304(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n305(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n306(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n307(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n308(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n309(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n310(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n311(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n312(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n313(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n314(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n315(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n316(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n317(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n318(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n319(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n320(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n321(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n322(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n323(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n324(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n325(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n326(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n327(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n328(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n329(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n330(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n331(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n332(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n333(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n334(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n335(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n336(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n337(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n338(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n339(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n340(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n341(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n342(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n343(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n344(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n345(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n346(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n347(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n348(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n349(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n350(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n351(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n352(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n353(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n354(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n355(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n356(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n357(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n358(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n359(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n360(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n361(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n362(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n363(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n364(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n365(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n366(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n367(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n368(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n369(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n370(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n371(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n372(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n373(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n374(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n375(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n376(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n377(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n378(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n379(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n380(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n381(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n382(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n383(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n384(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n385(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n386(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n387(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n388(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n389(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n390(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n391(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n392(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n393(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n394(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n395(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n396(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n397(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n398(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n399(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n400(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n401(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n402(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n403(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n404(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n405(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n406(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n407(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n408(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n409(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n410(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n411(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n412(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n413(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n414(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n415(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n416(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n417(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n418(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n419(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n420(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n421(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n422(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n423(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n424(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n425(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n426(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n427(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n428(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n429(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n430(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n431(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n432(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n433(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n434(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n435(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n436(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n437(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n438(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n439(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n440(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n441(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n442(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n443(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n444(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n445(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n446(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n447(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n448(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n449(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n450(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n451(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n452(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n453(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n454(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n455(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n456(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n457(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n458(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n459(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n460(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n461(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n462(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n463(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n464(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n465(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n466(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n467(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n468(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n469(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n470(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n471(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n472(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n473(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n474(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n475(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n476(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n477(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n478(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n479(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n480(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n481(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n482(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n483(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n484(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n485(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n486(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n487(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n488(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n489(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n490(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n491(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n492(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n493(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n494(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n495(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n496(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n497(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n498(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n499(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n500(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n501(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n502(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n503(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n504(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n505(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n506(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n507(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n508(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n509(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n510(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n511(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n512(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n513(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n514(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n515(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n516(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n517(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n518(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n519(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n520(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n521(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n522(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n523(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n524(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n525(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n526(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n527(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n528(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n529(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n530(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n531(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n532(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n533(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n534(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n535(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n536(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n537(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n538(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n539(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n540(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n541(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n542(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n543(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n544(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n545(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n546(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n547(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n548(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n549(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n550(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n551(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n552(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n553(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n554(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n555(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n556(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n557(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n558(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n559(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n560(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n561(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n562(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n563(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n564(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n565(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n566(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n567(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n568(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n569(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n570(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n571(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n572(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n573(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n574(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n575(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n576(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n577(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n578(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n579(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n580(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n581(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n582(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n583(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n584(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n585(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n586(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n587(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n588(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n589(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n590(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n591(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n592(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n593(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n594(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n595(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n596(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n597(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n598(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n599(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n600(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n601(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n602(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n603(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n604(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n605(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n606(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n607(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n608(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n609(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n610(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n611(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n612(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n613(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n614(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n615(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n616(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n617(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n618(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n619(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n620(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n621(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n622(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n623(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n624(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n625(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n626(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n627(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n628(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n629(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n630(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n631(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n632(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n633(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n634(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n635(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n636(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n637(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n638(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n639(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n640(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n641(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n642(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n643(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n644(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n645(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n646(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n647(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n648(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n649(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n650(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n651(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n652(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n653(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n654(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n655(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n656(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n657(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n658(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n659(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n660(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n661(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n662(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n663(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n664(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n665(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n666(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n667(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n668(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n669(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n670(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n671(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n672(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n673(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n674(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n675(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n676(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n677(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n678(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n679(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n680(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n681(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n682(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n683(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n684(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n685(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n686(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n687(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n688(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n689(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n690(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n691(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n692(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n693(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n694(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n695(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n696(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n697(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n698(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n699(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n700(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n701(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n702(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n703(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n704(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n705(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n706(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n707(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n708(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n709(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n710(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n711(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n712(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n713(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n714(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n715(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n716(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n717(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n718(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n719(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n720(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n721(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n722(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n723(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n724(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n725(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n726(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n727(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n728(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n729(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n730(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n731(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n732(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n733(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n734(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n735(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n736(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n737(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n738(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n739(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n740(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n741(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n742(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n743(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n744(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n745(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n746(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n747(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n748(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n749(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n750(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n751(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n752(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n753(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n754(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n755(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n756(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n757(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n758(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n759(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n760(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n761(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n762(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n763(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n764(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n765(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n766(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n767(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n768(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n769(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n770(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n771(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n772(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n773(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n774(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n775(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n776(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n777(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n778(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n779(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n780(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n781(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n782(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n783(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n784(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n785(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n786(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n787(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n788(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n789(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n790(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n791(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n792(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n793(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n794(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n795(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n796(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n797(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n798(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n799(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n800(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n801(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n802(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n803(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n804(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n805(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n806(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n807(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n808(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n809(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n810(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n811(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n812(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n813(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n814(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n815(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n816(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n817(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n818(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n819(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n820(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n821(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n822(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n823(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n824(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n825(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n826(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n827(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n828(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n829(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n830(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n831(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n832(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n833(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n834(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n835(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n836(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n837(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n838(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n839(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n840(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n841(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n842(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n843(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n844(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n845(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n846(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n847(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n848(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n849(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n850(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n851(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n852(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n853(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n854(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n855(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n856(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n857(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n858(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n859(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n860(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n861(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n862(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n863(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n864(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n865(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n866(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n867(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n868(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n869(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n870(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n871(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n872(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n873(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n874(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n875(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n876(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n877(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n878(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n879(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n880(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n881(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n882(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n883(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n884(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n885(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n886(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n887(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n888(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n889(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n890(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n891(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n892(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n893(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n894(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n895(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n896(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n897(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n898(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n899(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n900(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n901(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n902(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n903(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n904(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n905(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n906(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n907(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n908(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n909(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n910(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n911(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n912(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n913(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n914(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n915(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n916(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n917(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n918(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n919(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n920(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n921(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n922(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n923(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n924(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n925(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n926(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n927(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n928(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n929(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n930(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n931(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n932(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n933(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n934(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n935(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n936(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n937(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n938(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n939(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n940(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n941(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n942(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n943(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n944(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n945(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n946(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n947(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n948(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n949(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n950(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ /**
+ * Send a single string to a probe.
+ * This probe is deprecated.
+ *
+ * @param NUL-terminated string
+ */
+ probe user_trace__s1(char* message);
+
+ /**
+ * Multi-purpose probe: up to 4 NUL-terminated strings and 4
+ * 64-bit integer arguments.
+ * This probe is deprecated.
+ *
+ * @param proc, the PID (string form) of the sending process
+ * @param user_tag, the user tag of the sender
+ * @param i1, integer
+ * @param i2, integer
+ * @param i3, integer
+ * @param i4, integer
+ * @param s1, string/iolist. D's arg6 is NULL if not given by Erlang
+ * @param s2, string/iolist. D's arg7 is NULL if not given by Erlang
+ * @param s3, string/iolist. D's arg8 is NULL if not given by Erlang
+ * @param s4, string/iolist. D's arg9 is NULL if not given by Erlang
+ */
+ probe user_trace__i4s4(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+
};
#pragma D attributes Evolving/Evolving/Common provider erlang provider
diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c
index feb1740649..8c4d9108d4 100644
--- a/erts/emulator/beam/external.c
+++ b/erts/emulator/beam/external.c
@@ -142,6 +142,7 @@ erts_init_atom_cache_map(ErtsAtomCacheMap *acmp)
{
if (acmp) {
int ix;
+ acmp->long_atoms = 0;
for (ix = 0; ix < ERTS_ATOM_CACHE_SIZE; ix++)
acmp->cache[ix].iix = -1;
acmp->sz = 0;
@@ -154,6 +155,7 @@ erts_reset_atom_cache_map(ErtsAtomCacheMap *acmp)
{
if (acmp) {
int i;
+ acmp->long_atoms = 0;
for (i = 0; i < acmp->sz; i++) {
ASSERT(0 <= acmp->cix[i] && acmp->cix[i] < ERTS_ATOM_CACHE_SIZE);
acmp->cache[acmp->cix[i]].iix = -1;
@@ -175,9 +177,23 @@ erts_destroy_atom_cache_map(ErtsAtomCacheMap *acmp)
}
static ERTS_INLINE void
-insert_acache_map(ErtsAtomCacheMap *acmp, Eterm atom)
+insert_acache_map(ErtsAtomCacheMap *acmp, Eterm atom, Uint32 dflags)
{
- if (acmp && acmp->sz < ERTS_MAX_INTERNAL_ATOM_CACHE_ENTRIES) {
+ /*
+ * If the receiver do not understand utf8 atoms
+ * and this atom cannot be represented in latin1,
+ * we are not allowed to cache it.
+ *
+ * In this case all atoms are assumed to have
+ * latin1 encoding in the cache. By refusing it
+ * in the cache we will instead encode it using
+ * ATOM_UTF8_EXT/SMALL_ATOM_UTF8_EXT which the
+ * receiver do not recognize and tear down the
+ * connection.
+ */
+ if (acmp && acmp->sz < ERTS_MAX_INTERNAL_ATOM_CACHE_ENTRIES
+ && ((dflags & DFLAG_UTF8_ATOMS)
+ || atom_tab(atom_val(atom))->latin1_chars >= 0)) {
int ix;
ASSERT(acmp->hdr_sz < 0);
ix = atom2cix(atom);
@@ -190,7 +206,7 @@ insert_acache_map(ErtsAtomCacheMap *acmp, Eterm atom)
}
static ERTS_INLINE int
-get_iix_acache_map(ErtsAtomCacheMap *acmp, Eterm atom)
+get_iix_acache_map(ErtsAtomCacheMap *acmp, Eterm atom, Uint32 dflags)
{
if (!acmp)
return -1;
@@ -199,7 +215,9 @@ get_iix_acache_map(ErtsAtomCacheMap *acmp, Eterm atom)
ASSERT(is_atom(atom));
ix = atom2cix(atom);
if (acmp->cache[ix].iix < 0) {
- ASSERT(acmp->sz == ERTS_MAX_INTERNAL_ATOM_CACHE_ENTRIES);
+ ASSERT(acmp->sz == ERTS_MAX_INTERNAL_ATOM_CACHE_ENTRIES
+ || (!(dflags & DFLAG_UTF8_ATOMS)
+ && atom_tab(atom_val(atom))->latin1_chars < 0));
return -1;
}
else {
@@ -210,18 +228,17 @@ get_iix_acache_map(ErtsAtomCacheMap *acmp, Eterm atom)
}
void
-erts_finalize_atom_cache_map(ErtsAtomCacheMap *acmp)
+erts_finalize_atom_cache_map(ErtsAtomCacheMap *acmp, Uint32 dflags)
{
if (acmp) {
-#if MAX_ATOM_LENGTH > 255
-#error "This code is not complete; long_atoms info need to be passed to the following stages."
- int long_atoms = 0; /* !0 if one or more atoms are long than 255. */
-#endif
+ int utf8_atoms = (int) (dflags & DFLAG_UTF8_ATOMS);
+ int long_atoms = 0; /* !0 if one or more atoms are longer than 255. */
int i;
int sz;
int fix_sz
= 1 /* VERSION_MAGIC */
+ 1 /* DIST_HEADER */
+ + 1 /* dist header flags */
+ 1 /* number of internal cache entries */
;
int min_sz;
@@ -230,22 +247,23 @@ erts_finalize_atom_cache_map(ErtsAtomCacheMap *acmp)
min_sz = fix_sz+(2+4)*acmp->sz;
sz = fix_sz;
for (i = 0; i < acmp->sz; i++) {
+ Atom *a;
Eterm atom;
int len;
atom = acmp->cache[acmp->cix[i]].atom;
ASSERT(is_atom(atom));
- len = atom_tab(atom_val(atom))->len;
-#if MAX_ATOM_LENGTH > 255
+ a = atom_tab(atom_val(atom));
+ len = (int) (utf8_atoms ? a->len : a->latin1_chars);
+ ASSERT(len >= 0);
if (!long_atoms && len > 255)
long_atoms = 1;
-#endif
/* Enough for a new atom cache value */
sz += 1 /* cix */ + 1 /* length */ + len /* text */;
}
-#if MAX_ATOM_LENGTH > 255
- if (long_atoms)
+ if (long_atoms) {
+ acmp->long_atoms = 1;
sz += acmp->sz; /* we need 2 bytes per atom for length */
-#endif
+ }
/* Dynamically sized flag field */
sz += ERTS_DIST_HDR_ATOM_CACHE_FLAG_BYTES(acmp->sz);
if (sz < min_sz)
@@ -274,6 +292,7 @@ byte *erts_encode_ext_dist_header_setup(byte *ctl_ext, ErtsAtomCacheMap *acmp)
else {
int i;
byte *ep = ctl_ext;
+ byte dist_hdr_flags = acmp->long_atoms ? ERTS_DIST_HDR_LONG_ATOMS_FLG : 0;
ASSERT(acmp->hdr_sz >= 0);
/*
* Write cache update instructions. Note that this is a purely
@@ -296,28 +315,36 @@ byte *erts_encode_ext_dist_header_setup(byte *ctl_ext, ErtsAtomCacheMap *acmp)
}
--ep;
put_int8(acmp->sz, ep);
+ --ep;
+ put_int8(dist_hdr_flags, ep);
*--ep = DIST_HEADER;
*--ep = VERSION_MAGIC;
return ep;
}
}
-byte *erts_encode_ext_dist_header_finalize(byte *ext, ErtsAtomCache *cache)
+byte *erts_encode_ext_dist_header_finalize(byte *ext, ErtsAtomCache *cache, Uint32 dflags)
{
byte *ip;
byte instr_buf[(2+4)*ERTS_ATOM_CACHE_SIZE];
int ci, sz;
+ byte dist_hdr_flags;
+ int long_atoms;
+ int utf8_atoms = (int) (dflags & DFLAG_UTF8_ATOMS);
register byte *ep = ext;
ASSERT(ep[0] == VERSION_MAGIC);
if (ep[1] != DIST_HEADER)
return ext;
+ dist_hdr_flags = ep[2];
+ long_atoms = ERTS_DIST_HDR_LONG_ATOMS_FLG & ((int) dist_hdr_flags);
+
/*
* Update output atom cache and write the external version of
* the dist header. We write the header backwards just
* before the actual term(s).
*/
- ep += 2;
+ ep += 3;
ci = (int) get_int8(ep);
ASSERT(0 <= ci && ci < ERTS_ATOM_CACHE_SIZE);
ep += 1;
@@ -342,12 +369,7 @@ byte *erts_encode_ext_dist_header_finalize(byte *ext, ErtsAtomCache *cache)
flgs_bytes = ERTS_DIST_HDR_ATOM_CACHE_FLAG_BYTES(ci);
ASSERT(flgs_bytes <= sizeof(flgs_buf));
-#if MAX_ATOM_LENGTH > 255
- /* long_atoms info needs to be passed from previous stages */
- if (long_atoms)
- flgs |= ERTS_DIST_HDR_LONG_ATOMS_FLG;
-#endif
- flgs = 0;
+ flgs = (Uint32) dist_hdr_flags;
flgs_buf_ix = 0;
if ((ci & 1) == 0)
used_half_bytes = 2;
@@ -382,17 +404,22 @@ byte *erts_encode_ext_dist_header_finalize(byte *ext, ErtsAtomCache *cache)
Atom *a;
cache->out_arr[cix] = atom;
a = atom_tab(atom_val(atom));
- sz = a->len;
- ep -= sz;
- sys_memcpy((void *) ep, (void *) a->name, sz);
-#if MAX_ATOM_LENGTH > 255
+ if (utf8_atoms) {
+ sz = a->len;
+ ep -= sz;
+ sys_memcpy((void *) ep, (void *) a->name, sz);
+ }
+ else {
+ ASSERT(0 <= a->latin1_chars && a->latin1_chars <= MAX_ATOM_CHARACTERS);
+ ep -= a->latin1_chars;
+ sz = erts_utf8_to_latin1(ep, a->name, a->len);
+ ASSERT(a->latin1_chars == sz);
+ }
if (long_atoms) {
ep -= 2;
put_int16(sz, ep);
}
- else
-#endif
- {
+ else {
ASSERT(0 <= sz && sz <= 255);
--ep;
put_int8(sz, ep);
@@ -467,7 +494,7 @@ Uint erts_encode_ext_size_2(Eterm term, unsigned dflags)
Uint erts_encode_ext_size_ets(Eterm term)
{
- return encode_size_struct2(NULL, term, TERM_TO_BINARY_DFLAGS|DFLAGS_INTERNAL_TAGS);
+ return encode_size_struct2(NULL, term, TERM_TO_BINARY_DFLAGS|DFLAG_INTERNAL_TAGS);
}
@@ -500,7 +527,7 @@ void erts_encode_ext(Eterm term, byte **ext)
byte* erts_encode_ext_ets(Eterm term, byte *ep, struct erl_off_heap_header** off_heap)
{
- return enc_term(NULL, term, ep, TERM_TO_BINARY_DFLAGS|DFLAGS_INTERNAL_TAGS,
+ return enc_term(NULL, term, ep, TERM_TO_BINARY_DFLAGS|DFLAG_INTERNAL_TAGS,
off_heap);
}
@@ -553,6 +580,7 @@ erts_prepare_dist_ext(ErtsDistExternal *edep,
#endif
register byte *ep = ext;
+ int utf8_atoms = (int) (dep->flags & DFLAG_UTF8_ATOMS);
edep->heap_size = -1;
edep->ext_endp = ext+size;
@@ -611,9 +639,7 @@ erts_prepare_dist_ext(ErtsDistExternal *edep,
ERTS_EXT_HDR_FAIL;
ep++;
if (no_atoms) {
-#if MAX_ATOM_LENGTH > 255
int long_atoms = 0;
-#endif
#ifdef DEBUG
byte *flgs_buf = ep;
#endif
@@ -632,14 +658,8 @@ erts_prepare_dist_ext(ErtsDistExternal *edep,
*/
byte_ix = ERTS_DIST_HDR_ATOM_CACHE_FLAG_BYTE_IX(no_atoms);
bit_ix = ERTS_DIST_HDR_ATOM_CACHE_FLAG_BIT_IX(no_atoms);
- if (flgsp[byte_ix] & (((byte) ERTS_DIST_HDR_LONG_ATOMS_FLG)
- << bit_ix)) {
-#if MAX_ATOM_LENGTH > 255
+ if (flgsp[byte_ix] & (((byte) ERTS_DIST_HDR_LONG_ATOMS_FLG) << bit_ix))
long_atoms = 1;
-#else
- ERTS_EXT_HDR_FAIL; /* Long atoms not supported yet */
-#endif
- }
#ifdef DEBUG
byte_ix = 0;
@@ -707,23 +727,25 @@ erts_prepare_dist_ext(ErtsDistExternal *edep,
if (cix >= ERTS_ATOM_CACHE_SIZE)
ERTS_EXT_HDR_FAIL;
ep++;
-#if MAX_ATOM_LENGTH > 255
if (long_atoms) {
CHKSIZE(2);
len = get_int16(ep);
ep += 2;
}
- else
-#endif
- {
+ else {
CHKSIZE(1);
len = get_int8(ep);
ep++;
}
- if (len > MAX_ATOM_LENGTH)
- ERTS_EXT_HDR_FAIL; /* Too long atom */
CHKSIZE(len);
- atom = am_atom_put((char *) ep, len);
+ atom = erts_atom_put((byte *) ep,
+ len,
+ (utf8_atoms
+ ? ERTS_ATOM_ENC_UTF8
+ : ERTS_ATOM_ENC_LATIN1),
+ 0);
+ if (is_non_value(atom))
+ ERTS_EXT_HDR_FAIL;
ep += len;
cache->in_arr[cix] = atom;
edep->attab.atom[tix] = atom;
@@ -1404,11 +1426,12 @@ static byte*
enc_atom(ErtsAtomCacheMap *acmp, Eterm atom, byte *ep, Uint32 dflags)
{
int iix;
- int i, j;
+ int len;
+ int utf8_atoms = (int) (dflags & DFLAG_UTF8_ATOMS);
ASSERT(is_atom(atom));
- if (dflags & DFLAGS_INTERNAL_TAGS) {
+ if (dflags & DFLAG_INTERNAL_TAGS) {
Uint aval = atom_val(atom);
ASSERT(aval < (1<<24));
if (aval >= (1 << 16)) {
@@ -1423,27 +1446,46 @@ enc_atom(ErtsAtomCacheMap *acmp, Eterm atom, byte *ep, Uint32 dflags)
}
return ep;
}
+
/*
* term_to_binary/1,2 and the initial distribution message
* don't use the cache.
*/
- iix = get_iix_acache_map(acmp, atom);
- if (iix < 0) {
- i = atom_val(atom);
- j = atom_tab(i)->len;
- if ((MAX_ATOM_LENGTH <= 255 || j <= 255)
- && (dflags & DFLAG_SMALL_ATOM_TAGS)) {
- *ep++ = SMALL_ATOM_EXT;
- put_int8(j, ep);
- ep++;
+
+ iix = get_iix_acache_map(acmp, atom, dflags);
+ if (iix < 0) {
+ Atom *a = atom_tab(atom_val(atom));
+ if (utf8_atoms || a->latin1_chars < 0) {
+ len = a->len;
+ if (len > 255) {
+ *ep++ = ATOM_UTF8_EXT;
+ put_int16(len, ep);
+ ep += 2;
+ }
+ else {
+ *ep++ = SMALL_ATOM_UTF8_EXT;
+ put_int8(len, ep);
+ ep += 1;
+ }
+ sys_memcpy((char *) ep, (char *) a->name, len);
}
else {
- *ep++ = ATOM_EXT;
- put_int16(j, ep);
- ep += 2;
+ if (a->latin1_chars <= 255 && (dflags & DFLAG_SMALL_ATOM_TAGS)) {
+ *ep++ = SMALL_ATOM_EXT;
+ len = erts_utf8_to_latin1(ep+1, a->name, a->len);
+ ASSERT(len == a->latin1_chars);
+ put_int8(len, ep);
+ ep++;
+ }
+ else {
+ *ep++ = ATOM_EXT;
+ len = erts_utf8_to_latin1(ep+2, a->name, a->len);
+ ASSERT(len == a->latin1_chars);
+ put_int16(len, ep);
+ ep += 2;
+ }
}
- sys_memcpy((char *) ep, (char*)atom_tab(i)->name, (int) j);
- ep += j;
+ ep += len;
return ep;
}
@@ -1472,7 +1514,7 @@ enc_pid(ErtsAtomCacheMap *acmp, Eterm pid, byte* ep, Uint32 dflags)
ep += 4;
put_int32(os, ep);
ep += 4;
- *ep++ = (is_internal_pid(pid) && (dflags & DFLAGS_INTERNAL_TAGS)) ?
+ *ep++ = (is_internal_pid(pid) && (dflags & DFLAG_INTERNAL_TAGS)) ?
INTERNAL_CREATION : pid_creation(pid);
return ep;
}
@@ -1482,7 +1524,7 @@ static byte*
dec_atom(ErtsDistExternal *edep, byte* ep, Eterm* objp)
{
Uint len;
- int n;
+ int n, is_latin1;
switch (*ep++) {
case ATOM_CACHE_REF:
@@ -1498,17 +1540,37 @@ dec_atom(ErtsDistExternal *edep, byte* ep, Eterm* objp)
case ATOM_EXT:
len = get_int16(ep),
ep += 2;
+ is_latin1 = 1;
goto dec_atom_common;
case SMALL_ATOM_EXT:
len = get_int8(ep);
ep++;
+ is_latin1 = 1;
+ goto dec_atom_common;
+ case ATOM_UTF8_EXT:
+ len = get_int16(ep),
+ ep += 2;
+ is_latin1 = 0;
+ goto dec_atom_common;
+ case SMALL_ATOM_UTF8_EXT:
+ len = get_int8(ep),
+ ep++;
+ is_latin1 = 0;
dec_atom_common:
if (edep && (edep->flags & ERTS_DIST_EXT_BTT_SAFE)) {
- if (!erts_atom_get((char*)ep, len, objp)) {
+ if (!erts_atom_get((char*)ep, len, objp, is_latin1)) {
goto error;
}
} else {
- *objp = am_atom_put((char*)ep, len);
+ Eterm atom = erts_atom_put(ep,
+ len,
+ (is_latin1
+ ? ERTS_ATOM_ENC_LATIN1
+ : ERTS_ATOM_ENC_UTF8),
+ 0);
+ if (is_non_value(atom))
+ goto error;
+ *objp = atom;
}
ep += len;
break;
@@ -1770,7 +1832,7 @@ enc_term(ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, Uint32 dflags,
put_int16(i, ep);
ep += 2;
ep = enc_atom(acmp,ref_node_name(obj),ep,dflags);
- *ep++ = ((dflags & DFLAGS_INTERNAL_TAGS) && is_internal_ref(obj)) ?
+ *ep++ = ((dflags & DFLAG_INTERNAL_TAGS) && is_internal_ref(obj)) ?
INTERNAL_CREATION : ref_creation(obj);
ref_num = ref_numbers(obj);
for (j = 0; j < i; j++) {
@@ -1787,7 +1849,7 @@ enc_term(ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, Uint32 dflags,
j = port_number(obj);
put_int32(j, ep);
ep += 4;
- *ep++ = ((dflags & DFLAGS_INTERNAL_TAGS) && is_internal_port(obj)) ?
+ *ep++ = ((dflags & DFLAG_INTERNAL_TAGS) && is_internal_port(obj)) ?
INTERNAL_CREATION : port_creation(obj);
break;
@@ -1850,8 +1912,8 @@ enc_term(ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, Uint32 dflags,
} else {
*ep++ = FLOAT_EXT;
- /* now the sprintf which does the work */
- i = sys_double_to_chars(f.fd, (char*) ep);
+ /* now the erts_snprintf which does the work */
+ i = sys_double_to_chars(f.fd, (char*) ep, (size_t)31);
/* Don't leave garbage after the float! (Bad practice in general,
* and Purify complains.)
@@ -1868,7 +1930,7 @@ enc_term(ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, Uint32 dflags,
byte* bytes;
ERTS_GET_BINARY_BYTES(obj, bytes, bitoffs, bitsize);
- if (dflags & DFLAGS_INTERNAL_TAGS) {
+ if (dflags & DFLAG_INTERNAL_TAGS) {
ProcBin* pb = (ProcBin*) binary_val(obj);
Uint bytesize = pb->size;
if (pb->thing_word == HEADER_SUB_BIN) {
@@ -1889,7 +1951,9 @@ enc_term(ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, Uint32 dflags,
*ep++ = BINARY_INTERNAL_REF;
}
if (pb->flags) {
+ char* before_realloc = pb->val->orig_bytes;
erts_emasculate_writable_binary(pb);
+ bytes += (pb->val->orig_bytes - before_realloc);
}
erts_refc_inc(&pb->val->refc, 2);
@@ -2111,7 +2175,7 @@ static byte*
dec_term(ErtsDistExternal *edep, Eterm** hpp, byte* ep, ErlOffHeap* off_heap, Eterm* objp)
{
Eterm* hp_saved = *hpp;
- int n;
+ int n, is_latin1;
register Eterm* hp = *hpp; /* Please don't take the address of hp */
Eterm* next = objp;
@@ -2197,17 +2261,37 @@ dec_term(ErtsDistExternal *edep, Eterm** hpp, byte* ep, ErlOffHeap* off_heap, Et
case ATOM_EXT:
n = get_int16(ep);
ep += 2;
- goto dec_term_atom_common;
+ is_latin1 = 1;
+ goto dec_term_atom_common;
case SMALL_ATOM_EXT:
n = get_int8(ep);
ep++;
+ is_latin1 = 1;
+ goto dec_term_atom_common;
+ case ATOM_UTF8_EXT:
+ n = get_int16(ep);
+ ep += 2;
+ is_latin1 = 0;
+ goto dec_term_atom_common;
+ case SMALL_ATOM_UTF8_EXT:
+ n = get_int8(ep);
+ ep++;
+ is_latin1 = 0;
dec_term_atom_common:
if (edep && (edep->flags & ERTS_DIST_EXT_BTT_SAFE)) {
- if (!erts_atom_get((char*)ep, n, objp)) {
+ if (!erts_atom_get((char*)ep, n, objp, is_latin1)) {
goto error;
}
} else {
- *objp = am_atom_put((char*)ep, n);
+ Eterm atom = erts_atom_put(ep,
+ n,
+ (is_latin1
+ ? ERTS_ATOM_ENC_LATIN1
+ : ERTS_ATOM_ENC_UTF8),
+ 0);
+ if (is_non_value(atom))
+ goto error;
+ *objp = atom;
}
ep += n;
break;
@@ -2867,7 +2951,7 @@ encode_size_struct2(ErtsAtomCacheMap *acmp, Eterm obj, unsigned dflags)
result++;
break;
case ATOM_DEF:
- if (dflags & DFLAGS_INTERNAL_TAGS) {
+ if (dflags & DFLAG_INTERNAL_TAGS) {
if (atom_val(obj) >= (1<<16)) {
result += 1 + 3;
}
@@ -2876,17 +2960,22 @@ encode_size_struct2(ErtsAtomCacheMap *acmp, Eterm obj, unsigned dflags)
}
}
else {
- int alen = atom_tab(atom_val(obj))->len;
- if ((MAX_ATOM_LENGTH <= 255 || alen <= 255)
- && (dflags & DFLAG_SMALL_ATOM_TAGS)) {
- /* Make sure a SMALL_ATOM_EXT fits: SMALL_ATOM_EXT l t1 t2... */
- result += 1 + 1 + alen;
+ Atom *a = atom_tab(atom_val(obj));
+ int alen;
+ if ((dflags & DFLAG_UTF8_ATOMS) || a->latin1_chars < 0) {
+ alen = a->len;
+ result += 1 + 1 + alen;
+ if (alen > 255) {
+ result++; /* ATOM_UTF8_EXT (not small) */
+ }
}
else {
- /* Make sure an ATOM_EXT fits: ATOM_EXT l1 l0 t1 t2... */
- result += 1 + 2 + alen;
+ alen = a->latin1_chars;
+ result += 1 + 1 + alen;
+ if (alen > 255 || !(dflags & DFLAG_SMALL_ATOM_TAGS))
+ result++; /* ATOM_EXT (not small) */
}
- insert_acache_map(acmp, obj);
+ insert_acache_map(acmp, obj, dflags);
}
break;
case SMALL_DEF:
@@ -2967,7 +3056,7 @@ encode_size_struct2(ErtsAtomCacheMap *acmp, Eterm obj, unsigned dflags)
}
break;
case BINARY_DEF:
- if (dflags & DFLAGS_INTERNAL_TAGS) {
+ if (dflags & DFLAG_INTERNAL_TAGS) {
ProcBin* pb = (ProcBin*) binary_val(obj);
Uint sub_extra = 0;
Uint tot_bytes = pb->size;
@@ -3056,6 +3145,17 @@ encode_size_struct2(ErtsAtomCacheMap *acmp, Eterm obj, unsigned dflags)
return result;
}
+static int is_valid_utf8_atom(byte* bytes, Uint nbytes)
+{
+ byte* err_pos;
+ Uint num_chars;
+
+ /*SVERK Do we really need to validate correct utf8? */
+ return nbytes <= MAX_ATOM_SZ_LIMIT
+ && erts_analyze_utf8(bytes, nbytes, &err_pos, &num_chars, NULL) == ERTS_UTF8_OK
+ && num_chars <= MAX_ATOM_CHARACTERS;
+}
+
static Sint
decoded_size(byte *ep, byte* endp, int internal_tags)
{
@@ -3123,21 +3223,41 @@ decoded_size(byte *ep, byte* endp, int internal_tags)
case ATOM_EXT:
CHKSIZE(2);
n = get_int16(ep);
- if (n > MAX_ATOM_LENGTH) {
+ if (n > MAX_ATOM_CHARACTERS) {
return -1;
}
SKIP(n+2+atom_extra_skip);
atom_extra_skip = 0;
break;
+ case ATOM_UTF8_EXT:
+ CHKSIZE(2);
+ n = get_int16(ep);
+ ep += 2;
+ if (!is_valid_utf8_atom(ep, n)) {
+ return -1;
+ }
+ SKIP(n+atom_extra_skip);
+ atom_extra_skip = 0;
+ break;
case SMALL_ATOM_EXT:
CHKSIZE(1);
n = get_int8(ep);
- if (n > MAX_ATOM_LENGTH) {
+ if (n > MAX_ATOM_CHARACTERS) {
return -1;
}
SKIP(n+1+atom_extra_skip);
atom_extra_skip = 0;
break;
+ case SMALL_ATOM_UTF8_EXT:
+ CHKSIZE(1);
+ n = get_int8(ep);
+ ep++;
+ if (!is_valid_utf8_atom(ep, n)) {
+ return -1;
+ }
+ SKIP(n+atom_extra_skip);
+ atom_extra_skip = 0;
+ break;
case ATOM_CACHE_REF:
SKIP(1+atom_extra_skip);
atom_extra_skip = 0;
diff --git a/erts/emulator/beam/external.h b/erts/emulator/beam/external.h
index eddd4571dd..ad430117c8 100644
--- a/erts/emulator/beam/external.h
+++ b/erts/emulator/beam/external.h
@@ -51,6 +51,8 @@
#define NEW_FUN_EXT 'p'
#define EXPORT_EXT 'q'
#define FUN_EXT 'u'
+#define ATOM_UTF8_EXT 'v'
+#define SMALL_ATOM_UTF8_EXT 'w'
#define DIST_HEADER 'D'
#define ATOM_CACHE_REF 'R'
@@ -90,6 +92,7 @@ typedef struct cache {
typedef struct {
int hdr_sz;
int sz;
+ int long_atoms;
int cix[ERTS_ATOM_CACHE_SIZE];
struct {
Eterm atom;
@@ -150,12 +153,12 @@ typedef struct {
void erts_init_atom_cache_map(ErtsAtomCacheMap *);
void erts_reset_atom_cache_map(ErtsAtomCacheMap *);
void erts_destroy_atom_cache_map(ErtsAtomCacheMap *);
-void erts_finalize_atom_cache_map(ErtsAtomCacheMap *);
+void erts_finalize_atom_cache_map(ErtsAtomCacheMap *, Uint32);
Uint erts_encode_ext_dist_header_size(ErtsAtomCacheMap *);
Uint erts_encode_ext_dist_header_size(ErtsAtomCacheMap *);
byte *erts_encode_ext_dist_header_setup(byte *, ErtsAtomCacheMap *);
-byte *erts_encode_ext_dist_header_finalize(byte *, ErtsAtomCache *);
+byte *erts_encode_ext_dist_header_finalize(byte *, ErtsAtomCache *, Uint32);
Uint erts_encode_dist_ext_size(Eterm, Uint32, ErtsAtomCacheMap *);
void erts_encode_dist_ext(Eterm, byte **, Uint32, ErtsAtomCacheMap *);
diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h
index c9be20322d..41c2a0f2b9 100755
--- a/erts/emulator/beam/global.h
+++ b/erts/emulator/beam/global.h
@@ -39,38 +39,8 @@
#include "erl_sys_driver.h"
#include "erl_debug.h"
#include "error.h"
-
-typedef struct port Port;
-#include "erl_port_task.h"
-
-typedef struct erts_driver_t_ erts_driver_t;
-
-#define SMALL_IO_QUEUE 5 /* Number of fixed elements */
-
-typedef struct {
- ErlDrvSizeT size; /* total size in bytes */
-
- SysIOVec* v_start;
- SysIOVec* v_end;
- SysIOVec* v_head;
- SysIOVec* v_tail;
- SysIOVec v_small[SMALL_IO_QUEUE];
-
- ErlDrvBinary** b_start;
- ErlDrvBinary** b_end;
- ErlDrvBinary** b_head;
- ErlDrvBinary** b_tail;
- ErlDrvBinary* b_small[SMALL_IO_QUEUE];
-} ErlIOQueue;
-
-typedef struct line_buf { /* Buffer used in line oriented I/O */
- ErlDrvSizeT bufsiz; /* Size of character buffer */
- ErlDrvSizeT ovlen; /* Length of overflow data */
- ErlDrvSizeT ovsiz; /* Actual size of overflow buffer */
- char data[1]; /* Starting point of buffer data,
- data[0] is a flag indicating an unprocess CR,
- The rest is the overflow buffer. */
-} LineBuf;
+#include "erl_utils.h"
+#include "erl_port.h"
struct enif_environment_t /* ErlNifEnv */
{
@@ -90,162 +60,6 @@ extern void erts_print_nif_taints(int to, void* to_arg);
void erts_unload_nif(struct erl_module_nif* nif);
extern void erl_nif_init(void);
-/*
- * Port Specific Data.
- *
- * Only use PrtSD for very rarely used data.
- */
-
-#define ERTS_PRTSD_SCHED_ID 0
-
-#define ERTS_PRTSD_SIZE 1
-
-typedef struct {
- void *data[ERTS_PRTSD_SIZE];
-} ErtsPrtSD;
-
-#ifdef ERTS_SMP
-typedef struct ErtsXPortsList_ ErtsXPortsList;
-#endif
-
-/*
- * Port locking:
- *
- * Locking is done either driver specific or port specific. When
- * driver specific locking is used, all instances of the driver,
- * i.e. ports running the driver, share the same lock. When port
- * specific locking is used each instance have its own lock.
- *
- * Most fields in the Port structure are protected by the lock
- * referred to by the lock field. I'v called it the port lock.
- * This lock is shared between all ports running the same driver
- * when driver specific locking is used.
- *
- * The 'sched' field is protected by the port tasks lock
- * (see erl_port_tasks.c)
- *
- * The 'status' field is protected by a combination of the port lock,
- * the port tasks lock, and the state_lck. It may be read if
- * the state_lck, or the port lock is held. It may only be
- * modified if both the port lock and the state_lck is held
- * (with one exception; see below). When changeing status from alive
- * to dead or vice versa, also the port task lock has to be held.
- * This in order to guarantee that tasks are scheduled only for
- * ports that are alive.
- *
- * The status field may be modified with only the state_lck
- * held when status is changed from dead to alive. This since no
- * threads can have any references to the port other than via the
- * port table.
- *
- * /rickard
- */
-
-struct port {
- ErtsPortTaskSched sched;
- ErtsPortTaskHandle timeout_task;
- erts_smp_atomic_t refc;
-#ifdef ERTS_SMP
- erts_smp_mtx_t *lock;
- ErtsXPortsList *xports;
- erts_smp_atomic_t run_queue;
- erts_smp_spinlock_t state_lck; /* protects: id, status, snapshot */
-#endif
- Eterm id; /* The Port id of this port */
- Eterm connected; /* A connected process */
- Eterm caller; /* Current caller. */
- Eterm data; /* Data associated with port. */
- ErlHeapFragment* bp; /* Heap fragment holding data (NULL if imm data). */
- ErtsLink *nlinks;
- ErtsMonitor *monitors; /* Only MON_ORIGIN monitors of pid's */
- Uint bytes_in; /* Number of bytes read */
- Uint bytes_out; /* Number of bytes written */
-#ifdef ERTS_SMP
- ErtsSmpPTimer *ptimer;
-#else
- ErlTimer tm; /* Timer entry */
-#endif
-
- Eterm tracer_proc; /* If the port is traced, this is the tracer */
- Uint trace_flags; /* Trace flags */
-
- ErlIOQueue ioq; /* driver accessible i/o queue */
- DistEntry *dist_entry; /* Dist entry used in DISTRIBUTION */
- char *name; /* String used in the open */
- erts_driver_t* drv_ptr;
- UWord drv_data;
- SWord os_pid; /* Child process ID */
- ErtsProcList *suspended; /* List of suspended processes. */
- LineBuf *linebuf; /* Buffer to hold data not ready for
- process to get (line oriented I/O)*/
- Uint32 status; /* Status and type flags */
- int control_flags; /* Flags for port_control() */
- erts_aint32_t snapshot; /* Next snapshot that port should be part of */
- struct reg_proc *reg;
- ErlDrvPDL port_data_lock;
-
- ErtsPrtSD *psd; /* Port specific data */
-};
-
-
-ERTS_GLB_INLINE ErtsRunQueue *erts_port_runq(Port *prt);
-
-#if ERTS_GLB_INLINE_INCL_FUNC_DEF
-
-ERTS_GLB_INLINE ErtsRunQueue *
-erts_port_runq(Port *prt)
-{
-#ifdef ERTS_SMP
- ErtsRunQueue *rq1, *rq2;
- rq1 = (ErtsRunQueue *) erts_smp_atomic_read_nob(&prt->run_queue);
- if (!rq1)
- return NULL;
- while (1) {
- erts_smp_runq_lock(rq1);
- rq2 = (ErtsRunQueue *) erts_smp_atomic_read_nob(&prt->run_queue);
- if (rq1 == rq2)
- return rq1;
- erts_smp_runq_unlock(rq1);
- rq1 = rq2;
- if (!rq1)
- return NULL;
- }
-#else
- return ERTS_RUNQ_IX(0);
-#endif
-}
-
-#endif
-
-
-ERTS_GLB_INLINE void *erts_prtsd_get(Port *p, int ix);
-ERTS_GLB_INLINE void *erts_prtsd_set(Port *p, int ix, void *new);
-
-#if ERTS_GLB_INLINE_INCL_FUNC_DEF
-
-ERTS_GLB_INLINE void *
-erts_prtsd_get(Port *prt, int ix)
-{
- return prt->psd ? prt->psd->data[ix] : NULL;
-}
-
-ERTS_GLB_INLINE void *
-erts_prtsd_set(Port *prt, int ix, void *data)
-{
- if (prt->psd) {
- void *old = prt->psd->data[ix];
- prt->psd->data[ix] = data;
- return old;
- }
- else {
- prt->psd = erts_alloc(ERTS_ALC_T_PRTSD, sizeof(ErtsPrtSD));
- prt->psd->data[ix] = data;
- return NULL;
- }
-}
-
-#endif
-
/* Driver handle (wrapper for old plain handle) */
#define ERL_DE_OK 0
#define ERL_DE_UNLOAD 1
@@ -297,7 +111,7 @@ typedef struct {
or that wait for it to change state */
erts_refc_t refc; /* Number of ports/processes having
references to the driver */
- Uint port_count; /* Number of ports using the driver */
+ erts_smp_atomic32_t port_count; /* Number of ports using the driver */
Uint flags; /* ERL_DE_FL_KILL_PORTS */
int status; /* ERL_DE_xxx */
char *full_path; /* Full path of the driver */
@@ -349,7 +163,7 @@ struct erts_driver_t_ {
};
extern erts_driver_t *driver_list;
-extern erts_smp_mtx_t erts_driver_list_lock;
+extern erts_smp_rwmtx_t erts_driver_list_lock;
extern void erts_ddll_init(void);
extern void erts_ddll_lock_driver(DE_Handle *dh, char *name);
@@ -529,40 +343,9 @@ union erl_off_heap_ptr {
void* voidp;
};
-/* arrays that get malloced at startup */
-extern Port* erts_port;
-
-extern Uint erts_max_ports;
-extern Uint erts_port_tab_index_mask;
-extern erts_smp_atomic32_t erts_ports_snapshot;
-extern erts_smp_atomic_t erts_dead_ports_ptr;
-
-ERTS_GLB_INLINE void erts_may_save_closed_port(Port *prt);
-
-#if ERTS_GLB_INLINE_INCL_FUNC_DEF
-
-ERTS_GLB_INLINE void erts_may_save_closed_port(Port *prt)
-{
- ERTS_SMP_LC_ASSERT(erts_smp_lc_spinlock_is_locked(&prt->state_lck));
- if (prt->snapshot != erts_smp_atomic32_read_acqb(&erts_ports_snapshot)) {
- /* Dead ports are added from the end of the snapshot buffer */
- Eterm* tombstone;
- tombstone = (Eterm*) erts_smp_atomic_add_read_nob(&erts_dead_ports_ptr,
- -(erts_aint_t)sizeof(Eterm));
- ASSERT(tombstone+1 != NULL);
- ASSERT(prt->snapshot == erts_smp_atomic32_read_nob(&erts_ports_snapshot) - 1);
- *tombstone = prt->id;
- }
- /*else no ongoing snapshot or port was already included or created after snapshot */
-}
-
-#endif
-
/* controls warning mapping in error_logger */
extern Eterm node_cookie;
-extern erts_smp_atomic_t erts_bytes_out; /* no bytes written out */
-extern erts_smp_atomic_t erts_bytes_in; /* no bytes sent into the system */
extern Uint display_items; /* no of items to display in traces etc */
extern int erts_backtrace_depth;
@@ -700,54 +483,6 @@ do { \
#define WSTACK_ISEMPTY(s) (WSTK_CONCAT(s,_sp) == WSTK_CONCAT(s,_start))
#define WSTACK_POP(s) (*(--WSTK_CONCAT(s,_sp)))
-
-/* port status flags */
-
-#define ERTS_PORT_SFLG_CONNECTED ((Uint32) (1 << 0))
-/* Port have begun exiting */
-#define ERTS_PORT_SFLG_EXITING ((Uint32) (1 << 1))
-/* Distribution port */
-#define ERTS_PORT_SFLG_DISTRIBUTION ((Uint32) (1 << 2))
-#define ERTS_PORT_SFLG_BINARY_IO ((Uint32) (1 << 3))
-#define ERTS_PORT_SFLG_SOFT_EOF ((Uint32) (1 << 4))
-/* Flow control */
-#define ERTS_PORT_SFLG_PORT_BUSY ((Uint32) (1 << 5))
-/* Port is closing (no i/o accepted) */
-#define ERTS_PORT_SFLG_CLOSING ((Uint32) (1 << 6))
-/* Send a closed message when terminating */
-#define ERTS_PORT_SFLG_SEND_CLOSED ((Uint32) (1 << 7))
-/* Line orinted io on port */
-#define ERTS_PORT_SFLG_LINEBUF_IO ((Uint32) (1 << 8))
-/* Immortal port (only certain system ports) */
-#define ERTS_PORT_SFLG_IMMORTAL ((Uint32) (1 << 9))
-#define ERTS_PORT_SFLG_FREE ((Uint32) (1 << 10))
-#define ERTS_PORT_SFLG_FREE_SCHEDULED ((Uint32) (1 << 11))
-#define ERTS_PORT_SFLG_INITIALIZING ((Uint32) (1 << 12))
-/* Port uses port specific locking (opposed to driver specific locking) */
-#define ERTS_PORT_SFLG_PORT_SPECIFIC_LOCK ((Uint32) (1 << 13))
-#define ERTS_PORT_SFLG_INVALID ((Uint32) (1 << 14))
-/* Last port to terminate halts the emulator */
-#define ERTS_PORT_SFLG_HALT ((Uint32) (1 << 15))
-#ifdef DEBUG
-/* Only debug: make sure all flags aren't cleared unintentionally */
-#define ERTS_PORT_SFLG_PORT_DEBUG ((Uint32) (1 << 31))
-#endif
-
-/* Combinations of port status flags */
-#define ERTS_PORT_SFLGS_DEAD \
- (ERTS_PORT_SFLG_FREE \
- | ERTS_PORT_SFLG_FREE_SCHEDULED \
- | ERTS_PORT_SFLG_INITIALIZING)
-#define ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP \
- (ERTS_PORT_SFLGS_DEAD | ERTS_PORT_SFLG_INVALID)
-#define ERTS_PORT_SFLGS_INVALID_LOOKUP \
- (ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP \
- | ERTS_PORT_SFLG_CLOSING)
-#define ERTS_PORT_SFLGS_INVALID_TRACER_LOOKUP \
- (ERTS_PORT_SFLGS_INVALID_LOOKUP \
- | ERTS_PORT_SFLG_PORT_BUSY \
- | ERTS_PORT_SFLG_DISTRIBUTION)
-
/* binary.c */
void erts_emasculate_writable_binary(ProcBin* pb);
@@ -758,11 +493,35 @@ Eterm erts_realloc_binary(Eterm bin, size_t size);
/* erl_bif_info.c */
+Eterm
+erts_bld_port_info(Eterm **hpp,
+ ErlOffHeap *ohp,
+ Uint *szp,
+ Port *prt,
+ Eterm item);
+
void erts_bif_info_init(void);
/* bif.c */
Eterm erts_make_ref(Process *);
Eterm erts_make_ref_in_buffer(Eterm buffer[REF_THING_SIZE]);
+void erts_make_ref_in_array(Uint32 ref[ERTS_MAX_REF_NUMBERS]);
+
+ERTS_GLB_INLINE Eterm
+erts_proc_store_ref(Process *c_p, Uint32 ref[ERTS_MAX_REF_NUMBERS]);
+
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+
+ERTS_GLB_INLINE Eterm
+erts_proc_store_ref(Process *c_p, Uint32 ref[ERTS_MAX_REF_NUMBERS])
+{
+ Eterm *hp = HAlloc(c_p, REF_THING_SIZE);
+ write_ref_thing(hp, ref[0], ref[1], ref[2]);
+ return make_internal_ref(hp);
+}
+
+#endif
+
void erts_queue_monitor_message(Process *,
ErtsProcLocks*,
Eterm,
@@ -778,13 +537,6 @@ Eterm erl_send(Process *p, Eterm to, Eterm msg);
Eterm erl_is_function(Process* p, Eterm arg1, Eterm arg2);
-/* erl_bif_port.c */
-
-/* erl_bif_trace.c */
-Eterm erl_seq_trace_info(Process *p, Eterm arg1);
-void erts_system_monitor_clear(Process *c_p);
-void erts_system_profile_clear(Process *c_p);
-
/* beam_load.c */
typedef struct {
BeamInstr* current; /* Pointer to: Mod, Name, Arity */
@@ -960,11 +712,6 @@ void erts_free_heap_frags(Process* p);
/* io.c */
-struct erl_drv_port_data_lock {
- erts_mtx_t mtx;
- erts_atomic_t refc;
-};
-
typedef struct {
char *name;
char *driver_name;
@@ -973,474 +720,33 @@ typedef struct {
#define ERTS_SPAWN_DRIVER 1
#define ERTS_SPAWN_EXECUTABLE 2
#define ERTS_SPAWN_ANY (ERTS_SPAWN_DRIVER | ERTS_SPAWN_EXECUTABLE)
-
int erts_add_driver_entry(ErlDrvEntry *drv, DE_Handle *handle, int driver_list_locked);
void erts_destroy_driver(erts_driver_t *drv);
-void erts_wake_process_later(Port*, Process*);
-int erts_open_driver(erts_driver_t*, Eterm, char*, SysDriverOpts*, int *);
-int erts_is_port_ioq_empty(Port *);
-void erts_terminate_port(Port *);
-void close_port(Eterm);
-void init_io(void);
-void cleanup_io(void);
-void erts_do_exit_port(Port *, Eterm, Eterm);
-void erts_port_command(Process *, Eterm, Port *, Eterm);
-Eterm erts_port_control(Process*, Port*, Uint, Eterm);
-int erts_write_to_port(Eterm caller_id, Port *p, Eterm list);
-void print_port_info(int, void *, int);
+int erts_save_suspend_process_on_port(Port*, Process*);
+Port *erts_open_driver(erts_driver_t*, Eterm, char*, SysDriverOpts*, int *, int *);
+void erts_init_io(int, int);
void erts_raw_port_command(Port*, byte*, Uint);
-void driver_report_exit(int, int);
+void driver_report_exit(ErlDrvPort, int);
LineBuf* allocate_linebuf(int);
int async_ready(Port *, void*);
-Sint erts_test_next_port(int, Uint);
ErtsPortNames *erts_get_port_names(Eterm);
void erts_free_port_names(ErtsPortNames *);
Uint erts_port_ioq_size(Port *pp);
void erts_stale_drv_select(Eterm, ErlDrvEvent, int, int);
-void erts_port_cleanup(Port *);
-void erts_fire_port_monitor(Port *prt, Eterm ref);
-#ifdef ERTS_SMP
-void erts_smp_xports_unlock(Port *);
-#endif
+
+Port *erts_get_heart_port(void);
#if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_COUNT)
void erts_lcnt_enable_io_lock_count(int enable);
#endif
-#if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK)
-int erts_lc_is_port_locked(Port *);
-#endif
-
-ERTS_GLB_INLINE void erts_smp_port_state_lock(Port*);
-ERTS_GLB_INLINE void erts_smp_port_state_unlock(Port*);
-
-ERTS_GLB_INLINE int erts_smp_port_trylock(Port *prt);
-ERTS_GLB_INLINE void erts_smp_port_lock(Port *prt);
-ERTS_GLB_INLINE void erts_smp_port_unlock(Port *prt);
-
-#if ERTS_GLB_INLINE_INCL_FUNC_DEF
-
-ERTS_GLB_INLINE void
-erts_smp_port_state_lock(Port* prt)
-{
-#ifdef ERTS_SMP
- erts_smp_spin_lock(&prt->state_lck);
-#endif
-}
-
-ERTS_GLB_INLINE void
-erts_smp_port_state_unlock(Port *prt)
-{
-#ifdef ERTS_SMP
- erts_smp_spin_unlock(&prt->state_lck);
-#endif
-}
-
-
-ERTS_GLB_INLINE int
-erts_smp_port_trylock(Port *prt)
-{
- int res;
-
- ASSERT(erts_smp_atomic_read_nob(&prt->refc) > 0);
- erts_smp_atomic_inc_nob(&prt->refc);
-
-#ifdef ERTS_SMP
- res = erts_smp_mtx_trylock(prt->lock);
- if (res == EBUSY) {
- erts_smp_atomic_dec_nob(&prt->refc);
- }
-#else
- res = 0;
-#endif
-
- return res;
-}
-
-ERTS_GLB_INLINE void
-erts_smp_port_lock(Port *prt)
-{
- ASSERT(erts_smp_atomic_read_nob(&prt->refc) > 0);
- erts_smp_atomic_inc_nob(&prt->refc);
-#ifdef ERTS_SMP
- erts_smp_mtx_lock(prt->lock);
-#endif
-}
-
-ERTS_GLB_INLINE void
-erts_smp_port_unlock(Port *prt)
-{
- erts_aint_t refc;
-#ifdef ERTS_SMP
- erts_smp_mtx_unlock(prt->lock);
-#endif
- refc = erts_smp_atomic_dec_read_nob(&prt->refc);
- ASSERT(refc >= 0);
- if (refc == 0)
- erts_port_cleanup(prt);
-}
-
-#endif /* #if ERTS_GLB_INLINE_INCL_FUNC_DEF */
-
-
-#define ERTS_INVALID_PORT_OPT(PP, ID, FLGS) \
- (!(PP) || ((PP)->status & (FLGS)) || (PP)->id != (ID))
-
-/* port lookup */
-
-#define INVALID_PORT(PP, ID) \
- ERTS_INVALID_PORT_OPT((PP), (ID), ERTS_PORT_SFLGS_INVALID_LOOKUP)
-
-/* Invalidate trace port if anything suspicious, for instance
- * that the port is a distribution port or it is busy.
- */
-#define INVALID_TRACER_PORT(PP, ID) \
- ERTS_INVALID_PORT_OPT((PP), (ID), ERTS_PORT_SFLGS_INVALID_TRACER_LOOKUP)
-
-#define ERTS_PORT_SCHED_ID(P, ID) \
- ((Uint) (UWord) erts_prtsd_set((P), ERTS_PSD_SCHED_ID, (void *) (UWord) (ID)))
-
-#ifdef ERTS_SMP
-Port *erts_de2port(DistEntry *, Process *, ErtsProcLocks);
-#endif
-
-#define erts_id2port(ID, P, PL) \
- erts_id2port_sflgs((ID), (P), (PL), ERTS_PORT_SFLGS_INVALID_LOOKUP)
-
-ERTS_GLB_INLINE Port*erts_id2port_sflgs(Eterm, Process *, ErtsProcLocks, Uint32);
-ERTS_GLB_INLINE void erts_port_release(Port *);
-ERTS_GLB_INLINE Port*erts_drvport2port(ErlDrvPort);
-ERTS_GLB_INLINE Port*erts_drvportid2port(Eterm);
-ERTS_GLB_INLINE Uint32 erts_portid2status(Eterm id);
-ERTS_GLB_INLINE int erts_is_port_alive(Eterm id);
-ERTS_GLB_INLINE int erts_is_valid_tracer_port(Eterm id);
-ERTS_GLB_INLINE void erts_port_status_bandor_set(Port *, Uint32, Uint32);
-ERTS_GLB_INLINE void erts_port_status_band_set(Port *, Uint32);
-ERTS_GLB_INLINE void erts_port_status_bor_set(Port *, Uint32);
-ERTS_GLB_INLINE void erts_port_status_set(Port *, Uint32);
-ERTS_GLB_INLINE Uint32 erts_port_status_get(Port *);
-
-#if ERTS_GLB_INLINE_INCL_FUNC_DEF
-
-ERTS_GLB_INLINE Port*
-erts_id2port_sflgs(Eterm id, Process *c_p, ErtsProcLocks c_p_locks, Uint32 sflgs)
-{
-#ifdef ERTS_SMP
- int no_proc_locks = !c_p || !c_p_locks;
-#endif
- Port *prt;
-
- if (is_not_internal_port(id))
- return NULL;
-
- prt = &erts_port[internal_port_index(id)];
-
- erts_smp_port_state_lock(prt);
- if (ERTS_INVALID_PORT_OPT(prt, id, sflgs)) {
- erts_smp_port_state_unlock(prt);
- prt = NULL;
- }
- else {
- erts_smp_atomic_inc_nob(&prt->refc);
- erts_smp_port_state_unlock(prt);
-
-#ifdef ERTS_SMP
- if (no_proc_locks)
- erts_smp_mtx_lock(prt->lock);
- else if (erts_smp_mtx_trylock(prt->lock) == EBUSY) {
- /* Unlock process locks, and acquire locks in lock order... */
- erts_smp_proc_unlock(c_p, c_p_locks);
- erts_smp_mtx_lock(prt->lock);
- erts_smp_proc_lock(c_p, c_p_locks);
- }
-
- /* The id may not have changed... */
- ERTS_SMP_LC_ASSERT(prt->id == id);
- /* ... but status may have... */
- if (prt->status & sflgs) {
- erts_smp_port_unlock(prt); /* Also decrements refc... */
- prt = NULL;
- }
-#endif
-
- }
-
- return prt;
-}
-
-ERTS_GLB_INLINE void
-erts_port_release(Port *prt)
-{
- erts_smp_port_unlock(prt);
-}
-
-ERTS_GLB_INLINE Port*
-erts_drvport2port(ErlDrvPort drvport)
-{
- int ix = (int) drvport;
- if (ix < 0 || erts_max_ports <= ix)
- return NULL;
- if (erts_port[ix].status & ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP)
- return NULL;
- ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(&erts_port[ix]));
- return &erts_port[ix];
-}
-
-ERTS_GLB_INLINE Port*
-erts_drvportid2port(Eterm id)
-{
- int ix;
- if (is_not_internal_port(id))
- return NULL;
- ix = (int) internal_port_index(id);
- if (erts_max_ports <= ix)
- return NULL;
- if (erts_port[ix].status & ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP)
- return NULL;
- if (erts_port[ix].id != id)
- return NULL;
- ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(&erts_port[ix]));
- return &erts_port[ix];
-}
-
-ERTS_GLB_INLINE Uint32
-erts_portid2status(Eterm id)
-{
- if (is_not_internal_port(id))
- return ERTS_PORT_SFLG_INVALID;
- else {
- Uint32 status;
- int ix = internal_port_index(id);
- if (erts_max_ports <= ix)
- return ERTS_PORT_SFLG_INVALID;
- erts_smp_port_state_lock(&erts_port[ix]);
- if (erts_port[ix].id == id)
- status = erts_port[ix].status;
- else
- status = ERTS_PORT_SFLG_INVALID;
- erts_smp_port_state_unlock(&erts_port[ix]);
- return status;
- }
-}
-
-ERTS_GLB_INLINE int
-erts_is_port_alive(Eterm id)
-{
- return !(erts_portid2status(id) & (ERTS_PORT_SFLG_INVALID
- | ERTS_PORT_SFLGS_DEAD));
-}
-
-ERTS_GLB_INLINE int
-erts_is_valid_tracer_port(Eterm id)
-{
- return !(erts_portid2status(id) & ERTS_PORT_SFLGS_INVALID_TRACER_LOOKUP);
-}
-
-ERTS_GLB_INLINE void erts_port_status_bandor_set(Port *prt,
- Uint32 band_status,
- Uint32 bor_status)
-{
- ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
- erts_smp_port_state_lock(prt);
- prt->status &= band_status;
- prt->status |= bor_status;
- erts_smp_port_state_unlock(prt);
-}
-
-ERTS_GLB_INLINE void erts_port_status_band_set(Port *prt, Uint32 status)
-{
- ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
- erts_smp_port_state_lock(prt);
- prt->status &= status;
- erts_smp_port_state_unlock(prt);
-}
-
-ERTS_GLB_INLINE void erts_port_status_bor_set(Port *prt, Uint32 status)
-{
- ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
- erts_smp_port_state_lock(prt);
- prt->status |= status;
- erts_smp_port_state_unlock(prt);
-}
-
-ERTS_GLB_INLINE void erts_port_status_set(Port *prt, Uint32 status)
-{
- ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
- erts_smp_port_state_lock(prt);
- prt->status = status;
- erts_smp_port_state_unlock(prt);
-}
-
-ERTS_GLB_INLINE Uint32 erts_port_status_get(Port *prt)
-{
- Uint32 res;
- erts_smp_port_state_lock(prt);
- res = prt->status;
- erts_smp_port_state_unlock(prt);
- return res;
-}
-#endif /* #if ERTS_GLB_INLINE_INCL_FUNC_DEF */
-
/* erl_drv_thread.c */
void erl_drv_thr_init(void);
-/* time.c */
-
/* utils.c */
-
-typedef struct {
-#ifdef DEBUG
- int smp_api;
-#endif
- union {
- Uint64 not_atomic;
-#ifdef ARCH_64
- erts_atomic_t atomic;
-#else
- erts_dw_atomic_t atomic;
-#endif
- } counter;
-} erts_interval_t;
-
-void erts_interval_init(erts_interval_t *);
-void erts_smp_interval_init(erts_interval_t *);
-Uint64 erts_step_interval_nob(erts_interval_t *);
-Uint64 erts_step_interval_relb(erts_interval_t *);
-Uint64 erts_smp_step_interval_nob(erts_interval_t *);
-Uint64 erts_smp_step_interval_relb(erts_interval_t *);
-Uint64 erts_ensure_later_interval_nob(erts_interval_t *, Uint64);
-Uint64 erts_ensure_later_interval_acqb(erts_interval_t *, Uint64);
-Uint64 erts_smp_ensure_later_interval_nob(erts_interval_t *, Uint64);
-Uint64 erts_smp_ensure_later_interval_acqb(erts_interval_t *, Uint64);
-#ifdef ARCH_32
-ERTS_GLB_INLINE Uint64 erts_interval_dw_aint_to_val__(erts_dw_aint_t *);
-#endif
-ERTS_GLB_INLINE Uint64 erts_current_interval_nob__(erts_interval_t *);
-ERTS_GLB_INLINE Uint64 erts_current_interval_acqb__(erts_interval_t *);
-ERTS_GLB_INLINE Uint64 erts_current_interval_nob(erts_interval_t *);
-ERTS_GLB_INLINE Uint64 erts_current_interval_acqb(erts_interval_t *);
-ERTS_GLB_INLINE Uint64 erts_smp_current_interval_nob(erts_interval_t *);
-ERTS_GLB_INLINE Uint64 erts_smp_current_interval_acqb(erts_interval_t *);
-
-#if ERTS_GLB_INLINE_INCL_FUNC_DEF
-
-#ifdef ARCH_32
-
-ERTS_GLB_INLINE Uint64
-erts_interval_dw_aint_to_val__(erts_dw_aint_t *dw)
-{
-#ifdef ETHR_SU_DW_NAINT_T__
- return (Uint64) dw->dw_sint;
-#else
- Uint64 res;
- res = (Uint64) ((Uint32) dw->sint[ERTS_DW_AINT_HIGH_WORD]);
- res <<= 32;
- res |= (Uint64) ((Uint32) dw->sint[ERTS_DW_AINT_LOW_WORD]);
- return res;
-#endif
-}
-
-#endif
-
-ERTS_GLB_INLINE Uint64
-erts_current_interval_nob__(erts_interval_t *icp)
-{
-#ifdef ARCH_64
- return (Uint64) erts_atomic_read_nob(&icp->counter.atomic);
-#else
- erts_dw_aint_t dw;
- erts_dw_atomic_read_nob(&icp->counter.atomic, &dw);
- return erts_interval_dw_aint_to_val__(&dw);
-#endif
-}
-
-ERTS_GLB_INLINE Uint64
-erts_current_interval_acqb__(erts_interval_t *icp)
-{
-#ifdef ARCH_64
- return (Uint64) erts_atomic_read_acqb(&icp->counter.atomic);
-#else
- erts_dw_aint_t dw;
- erts_dw_atomic_read_acqb(&icp->counter.atomic, &dw);
- return erts_interval_dw_aint_to_val__(&dw);
-#endif
-}
-
-ERTS_GLB_INLINE Uint64
-erts_current_interval_nob(erts_interval_t *icp)
-{
- ASSERT(!icp->smp_api);
- return erts_current_interval_nob__(icp);
-}
-
-ERTS_GLB_INLINE Uint64
-erts_current_interval_acqb(erts_interval_t *icp)
-{
- ASSERT(!icp->smp_api);
- return erts_current_interval_acqb__(icp);
-}
-
-ERTS_GLB_INLINE Uint64
-erts_smp_current_interval_nob(erts_interval_t *icp)
-{
- ASSERT(icp->smp_api);
-#ifdef ERTS_SMP
- return erts_current_interval_nob__(icp);
-#else
- return icp->counter.not_atomic;
-#endif
-}
-
-ERTS_GLB_INLINE Uint64
-erts_smp_current_interval_acqb(erts_interval_t *icp)
-{
- ASSERT(icp->smp_api);
-#ifdef ERTS_SMP
- return erts_current_interval_acqb__(icp);
-#else
- return icp->counter.not_atomic;
-#endif
-}
-
-#endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */
-
-/*
- * To be used to silence unused result warnings, but do not abuse it.
- */
-void erts_silence_warn_unused_result(long unused);
-
void erts_cleanup_offheap(ErlOffHeap *offheap);
-int erts_fit_in_bits_int64(Sint64);
-int erts_fit_in_bits_int32(Sint32);
-int list_length(Eterm);
Export* erts_find_function(Eterm, Eterm, unsigned int, ErtsCodeIndex);
-int erts_is_builtin(Eterm, Eterm, int);
-Uint32 make_broken_hash(Eterm);
-Uint32 block_hash(byte *, unsigned, Uint32);
-Uint32 make_hash2(Eterm);
-Uint32 make_hash(Eterm);
-
-
-Eterm erts_bld_atom(Uint **hpp, Uint *szp, char *str);
-Eterm erts_bld_uint(Uint **hpp, Uint *szp, Uint ui);
-Eterm erts_bld_uword(Uint **hpp, Uint *szp, UWord uw);
-Eterm erts_bld_uint64(Uint **hpp, Uint *szp, Uint64 ui64);
-Eterm erts_bld_sint64(Uint **hpp, Uint *szp, Sint64 si64);
-Eterm erts_bld_cons(Uint **hpp, Uint *szp, Eterm car, Eterm cdr);
-Eterm erts_bld_tuple(Uint **hpp, Uint *szp, Uint arity, ...);
-Eterm erts_bld_tuplev(Uint **hpp, Uint *szp, Uint arity, Eterm terms[]);
-Eterm erts_bld_string_n(Uint **hpp, Uint *szp, const char *str, Sint len);
-#define erts_bld_string(hpp,szp,str) erts_bld_string_n(hpp,szp,str,strlen(str))
-Eterm erts_bld_list(Uint **hpp, Uint *szp, Sint length, Eterm terms[]);
-Eterm erts_bld_2tup_list(Uint **hpp, Uint *szp,
- Sint length, Eterm terms1[], Uint terms2[]);
-Eterm
-erts_bld_atom_uint_2tup_list(Uint **hpp, Uint *szp,
- Sint length, Eterm atoms[], Uint uints[]);
-Eterm
-erts_bld_atom_2uint_3tup_list(Uint **hpp, Uint *szp, Sint length,
- Eterm atoms[], Uint uints1[], Uint uints2[]);
Eterm store_external_or_ref_in_proc_(Process *, Eterm);
Eterm store_external_or_ref_(Uint **, ErlOffHeap*, Eterm);
@@ -1455,42 +761,6 @@ Eterm store_external_or_ref_(Uint **, ErlOffHeap*, Eterm);
(ASSERT_EXPR(is_node_container((NC))), \
IS_CONST((NC)) ? (NC) : store_external_or_ref_in_proc_((Pp), (NC)))
-void erts_init_utils(void);
-void erts_init_utils_mem(void);
-
-erts_dsprintf_buf_t *erts_create_tmp_dsbuf(Uint);
-void erts_destroy_tmp_dsbuf(erts_dsprintf_buf_t *);
-
-#if HALFWORD_HEAP
-int eq_rel(Eterm a, Eterm* a_base, Eterm b, Eterm* b_base);
-# define eq(A,B) eq_rel(A,NULL,B,NULL)
-#else
-int eq(Eterm, Eterm);
-# define eq_rel(A,A_BASE,B,B_BASE) eq(A,B)
-#endif
-
-#define EQ(x,y) (((x) == (y)) || (is_not_both_immed((x),(y)) && eq((x),(y))))
-
-#if HALFWORD_HEAP
-Sint cmp_rel(Eterm, Eterm*, Eterm, Eterm*);
-#define CMP(A,B) cmp_rel(A,NULL,B,NULL)
-#else
-Sint cmp(Eterm, Eterm);
-#define cmp_rel(A,A_BASE,B,B_BASE) cmp(A,B)
-#define CMP(A,B) cmp(A,B)
-#endif
-#define cmp_lt(a,b) (CMP((a),(b)) < 0)
-#define cmp_le(a,b) (CMP((a),(b)) <= 0)
-#define cmp_eq(a,b) (CMP((a),(b)) == 0)
-#define cmp_ne(a,b) (CMP((a),(b)) != 0)
-#define cmp_ge(a,b) (CMP((a),(b)) >= 0)
-#define cmp_gt(a,b) (CMP((a),(b)) > 0)
-
-#define CMP_LT(a,b) ((a) != (b) && cmp_lt((a),(b)))
-#define CMP_GE(a,b) ((a) == (b) || cmp_ge((a),(b)))
-#define CMP_EQ(a,b) ((a) == (b) || cmp_eq((a),(b)))
-#define CMP_NE(a,b) ((a) != (b) && cmp_ne((a),(b)))
-
/* duplicates from big.h */
int term_to_Uint(Eterm term, Uint *up);
int term_to_UWord(Eterm, UWord*);
@@ -1516,85 +786,23 @@ Sint erts_native_filename_need(Eterm ioterm, int encoding);
void erts_copy_utf8_to_utf16_little(byte *target, byte *bytes, int num_chars);
int erts_analyze_utf8(byte *source, Uint size,
byte **err_pos, Uint *num_chars, int *left);
-char *erts_convert_filename_to_native(Eterm name, ErtsAlcType_t alloc_type, int allow_empty);
-
+int erts_analyze_utf8_x(byte *source, Uint size,
+ byte **err_pos, Uint *num_chars, int *left,
+ Sint *num_latin1_chars, Uint max_chars);
+char *erts_convert_filename_to_native(Eterm name, char *statbuf,
+ size_t statbuf_size,
+ ErtsAlcType_t alloc_type,
+ int allow_empty, int allow_atom,
+ Sint *used /* out */);
+Eterm erts_convert_native_to_filename(Process *p, byte *bytes);
+Eterm erts_utf8_to_list(Process *p, Uint num, byte *bytes, Uint sz, Uint left,
+ Uint *num_built, Uint *num_eaten, Eterm tail);
+int erts_utf8_to_latin1(byte* dest, const byte* source, int slen);
#define ERTS_UTF8_OK 0
#define ERTS_UTF8_INCOMPLETE 1
#define ERTS_UTF8_ERROR 2
#define ERTS_UTF8_ANALYZE_MORE 3
-
-/* erl_trace.c */
-void erts_init_trace(void);
-void erts_trace_check_exiting(Eterm exiting);
-Eterm erts_set_system_seq_tracer(Process *c_p,
- ErtsProcLocks c_p_locks,
- Eterm new);
-Eterm erts_get_system_seq_tracer(void);
-void erts_change_default_tracing(int setflags, Uint *flagsp, Eterm *tracerp);
-void erts_get_default_tracing(Uint *flagsp, Eterm *tracerp);
-void erts_set_system_monitor(Eterm monitor);
-Eterm erts_get_system_monitor(void);
-
-#ifdef ERTS_SMP
-void erts_check_my_tracer_proc(Process *);
-void erts_block_sys_msg_dispatcher(void);
-void erts_release_sys_msg_dispatcher(void);
-void erts_foreach_sys_msg_in_q(void (*func)(Eterm,
- Eterm,
- Eterm,
- ErlHeapFragment *));
-void erts_queue_error_logger_message(Eterm, Eterm, ErlHeapFragment *);
-#endif
-
-void erts_send_sys_msg_proc(Eterm, Eterm, Eterm, ErlHeapFragment *);
-void trace_send(Process*, Eterm, Eterm);
-void trace_receive(Process*, Eterm);
-Uint32 erts_call_trace(Process *p, BeamInstr mfa[], Binary *match_spec, Eterm* args,
- int local, Eterm *tracer_pid);
-void erts_trace_return(Process* p, BeamInstr* fi, Eterm retval, Eterm *tracer_pid);
-void erts_trace_exception(Process* p, BeamInstr mfa[], Eterm class, Eterm value,
- Eterm *tracer);
-void erts_trace_return_to(Process *p, BeamInstr *pc);
-void trace_sched(Process*, Eterm);
-void trace_proc(Process*, Process*, Eterm, Eterm);
-void trace_proc_spawn(Process*, Eterm pid, Eterm mod, Eterm func, Eterm args);
-void save_calls(Process *p, Export *);
-void trace_gc(Process *p, Eterm what);
-/* port tracing */
-void trace_virtual_sched(Process*, Eterm);
-void trace_sched_ports(Port *pp, Eterm);
-void trace_sched_ports_where(Port *pp, Eterm, Eterm);
-void trace_port(Port *, Eterm what, Eterm data);
-void trace_port_open(Port *, Eterm calling_pid, Eterm drv_name);
-
-/* system_profile */
-void erts_set_system_profile(Eterm profile);
-Eterm erts_get_system_profile(void);
-void profile_scheduler(Eterm scheduler_id, Eterm);
-void profile_scheduler_q(Eterm scheduler_id, Eterm state, Eterm no_schedulers, Uint Ms, Uint s, Uint us);
-void profile_runnable_proc(Process* p, Eterm status);
-void profile_runnable_port(Port* p, Eterm status);
-void erts_system_profile_setup_active_schedulers(void);
-
-/* system_monitor */
-void monitor_long_gc(Process *p, Uint time);
-void monitor_large_heap(Process *p);
-void monitor_generic(Process *p, Eterm type, Eterm spec);
-Uint erts_trace_flag2bit(Eterm flag);
-int erts_trace_flags(Eterm List,
- Uint *pMask, Eterm *pTracer, int *pCpuTimestamp);
-Eterm erts_bif_trace(int bif_index, Process* p, Eterm* args, BeamInstr *I);
-
-#ifdef ERTS_SMP
-void erts_send_pending_trace_msgs(ErtsSchedulerData *esdp);
-#define ERTS_SMP_CHK_PEND_TRACE_MSGS(ESDP) \
-do { \
- if ((ESDP)->pending_trace_msgs) \
- erts_send_pending_trace_msgs((ESDP)); \
-} while (0)
-#else
-#define ERTS_SMP_CHK_PEND_TRACE_MSGS(ESDP)
-#endif
+#define ERTS_UTF8_OK_MAX_CHARS 4
void bin_write(int, void*, byte*, size_t);
int intlist_to_buf(Eterm, char*, int); /* most callers pass plain char*'s */
@@ -1613,9 +821,16 @@ char* Sint_to_buf(Sint, struct Sint_buf*);
#define ERTS_IOLIST_TYPE 2
Eterm buf_to_intlist(Eterm**, char*, size_t, Eterm); /* most callers pass plain char*'s */
-int io_list_to_buf(Eterm, char*, int);
-int io_list_to_buf2(Eterm, char*, int);
-int erts_iolist_size(Eterm, Uint *);
+
+#define ERTS_IOLIST_TO_BUF_OVERFLOW (~((ErlDrvSizeT) 0))
+#define ERTS_IOLIST_TO_BUF_TYPE_ERROR (~((ErlDrvSizeT) 1))
+#define ERTS_IOLIST_TO_BUF_FAILED(R) \
+ (((R) & (~((ErlDrvSizeT) 1))) == (~((ErlDrvSizeT) 1)))
+#define ERTS_IOLIST_TO_BUF_SUCCEEDED(R) \
+ (!ERTS_IOLIST_TO_BUF_FAILED((R)))
+
+ErlDrvSizeT erts_iolist_to_buf(Eterm, char*, ErlDrvSizeT);
+int erts_iolist_size(Eterm, ErlDrvSizeT *);
int is_string(Eterm);
void erl_at_exit(void (*) (void*), void*);
Eterm collect_memory(Process *);
@@ -1660,41 +875,6 @@ Uint erts_current_reductions(Process* current, Process *p);
int erts_print_system_version(int to, void *arg, Process *c_p);
int erts_hibernate(Process* c_p, Eterm module, Eterm function, Eterm args, Eterm* reg);
-#define seq_trace_output(token, msg, type, receiver, process) \
-seq_trace_output_generic((token), (msg), (type), (receiver), (process), NIL)
-#define seq_trace_output_exit(token, msg, type, receiver, exitfrom) \
-seq_trace_output_generic((token), (msg), (type), (receiver), NULL, (exitfrom))
-void seq_trace_output_generic(Eterm token, Eterm msg, Uint type,
- Eterm receiver, Process *process, Eterm exitfrom);
-
-int seq_trace_update_send(Process *process);
-
-Eterm erts_seq_trace(Process *process,
- Eterm atom_type, Eterm atom_true_or_false,
- int build_result);
-
-struct trace_pattern_flags {
- unsigned int breakpoint : 1; /* Set if any other is set */
- unsigned int local : 1; /* Local call trace breakpoint */
- unsigned int meta : 1; /* Metadata trace breakpoint */
- unsigned int call_count : 1; /* Fast call count breakpoint */
- unsigned int call_time : 1; /* Fast call time breakpoint */
-};
-extern const struct trace_pattern_flags erts_trace_pattern_flags_off;
-extern int erts_call_time_breakpoint_tracing;
-int erts_set_trace_pattern(Process*p, Eterm* mfa, int specified,
- Binary* match_prog_set, Binary *meta_match_prog_set,
- int on, struct trace_pattern_flags,
- Eterm meta_tracer_pid, int is_blocking);
-void
-erts_get_default_trace_pattern(int *trace_pattern_is_on,
- Binary **match_spec,
- Binary **meta_match_spec,
- struct trace_pattern_flags *trace_pattern_flags,
- Eterm *meta_tracer_pid);
-int erts_is_default_trace_enabled(void);
-void erts_bif_trace_init(void);
-int erts_finish_breakpointing(void);
/*
** Call_trace uses this API for the parameter matching functions
@@ -1936,15 +1116,15 @@ dtrace_pid_str(Eterm pid, char *process_buf)
ERTS_GLB_INLINE void
dtrace_proc_str(Process *process, char *process_buf)
{
- dtrace_pid_str(process->id, process_buf);
+ dtrace_pid_str(process->common.id, process_buf);
}
ERTS_GLB_INLINE void
dtrace_port_str(Port *port, char *port_buf)
{
erts_snprintf(port_buf, DTRACE_TERM_BUF_SIZE, "#Port<%lu.%lu>",
- port_channel_no(port->id),
- port_number(port->id));
+ port_channel_no(port->common.id),
+ port_number(port->common.id));
}
ERTS_GLB_INLINE void
diff --git a/erts/emulator/beam/index.c b/erts/emulator/beam/index.c
index 25d5cce0f3..79c3ecf1b3 100644
--- a/erts/emulator/beam/index.c
+++ b/erts/emulator/beam/index.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2009. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2012. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -82,7 +82,8 @@ index_put_entry(IndexTable* t, void* tmpl)
if (ix >= t->size) {
Uint sz;
if (ix >= t->limit) {
- erl_exit(1, "no more index entries in %s (max=%d)\n",
+ /* A core dump is unnecessary */
+ erl_exit(ERTS_DUMP_EXIT, "no more index entries in %s (max=%d)\n",
t->htable.name, t->limit);
}
sz = INDEX_PAGE_SIZE*sizeof(IndexSlot*);
diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c
index 845593d0b2..536a3cc819 100644
--- a/erts/emulator/beam/io.c
+++ b/erts/emulator/beam/io.c
@@ -43,6 +43,8 @@
#include "erl_version.h"
#include "error.h"
#include "erl_async.h"
+#define ERTS_WANT_EXTERNAL_TAGS
+#include "external.h"
#include "dtrace-wrapper.h"
extern ErlDrvEntry fd_driver_entry;
@@ -51,34 +53,40 @@ extern ErlDrvEntry spawn_driver_entry;
extern ErlDrvEntry *driver_tab[]; /* table of static drivers, only used during initialization */
erts_driver_t *driver_list; /* List of all drivers, static and dynamic. */
-erts_smp_mtx_t erts_driver_list_lock; /* Mutex for driver list */
+erts_smp_rwmtx_t erts_driver_list_lock; /* Mutex for driver list */
static erts_smp_tsd_key_t driver_list_lock_status_key; /*stop recursive locks when calling
driver init */
static erts_smp_tsd_key_t driver_list_last_error_key; /* Save last DDLL error on a
per thread basis (for BC interfaces) */
-Port* erts_port; /* The port table */
+ErtsPTab erts_port erts_align_attribute(ERTS_CACHE_LINE_SIZE); /* The port table */
erts_smp_atomic_t erts_bytes_out; /* No bytes sent out of the system */
erts_smp_atomic_t erts_bytes_in; /* No bytes gotten into the system */
-Uint erts_max_ports;
-Uint erts_port_tab_index_mask;
-
const ErlDrvTermData driver_term_nil = (ErlDrvTermData)NIL;
+const Port erts_invalid_port = {{ERTS_INVALID_PORT}};
+
erts_driver_t vanilla_driver;
erts_driver_t spawn_driver;
erts_driver_t fd_driver;
+int erts_port_synchronous_ops = 0;
+int erts_port_schedule_all_ops = 0;
+int erts_port_parallelism = 0;
+
+static void deliver_result(Eterm sender, Eterm pid, Eterm res);
static int init_driver(erts_driver_t *, ErlDrvEntry *, DE_Handle *);
static void terminate_port(Port *p);
static void pdl_init(void);
#ifdef ERTS_SMP
static void driver_monitor_lock_pdl(Port *p);
static void driver_monitor_unlock_pdl(Port *p);
+#define DRV_MONITOR_LOOKUP_PORT_LOCK_PDL(Port) erts_thr_drvport2port_raw((Port), 1)
#define DRV_MONITOR_LOCK_PDL(Port) driver_monitor_lock_pdl(Port)
#define DRV_MONITOR_UNLOCK_PDL(Port) driver_monitor_unlock_pdl(Port)
#else
+#define DRV_MONITOR_LOOKUP_PORT_LOCK_PDL(Port) erts_thr_drvport2port_raw((Port), 0)
#define DRV_MONITOR_LOCK_PDL(Port) /* nothing */
#define DRV_MONITOR_UNLOCK_PDL(Port) /* nothing */
#endif
@@ -89,36 +97,12 @@ static void driver_monitor_unlock_pdl(Port *p);
static ERTS_INLINE ErlIOQueue*
drvport2ioq(ErlDrvPort drvport)
{
- int ix = (int) drvport;
- Uint32 status;
-
- if (ix < 0 || erts_max_ports <= ix)
+ Port *prt = erts_thr_drvport2port_raw(drvport, 0);
+ erts_aint32_t state = erts_atomic32_read_nob(&prt->state);
+ if (state & ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP)
return NULL;
-
- if (erts_get_scheduler_data()) {
- ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(&erts_port[ix]));
- ERTS_LC_ASSERT(!erts_port[ix].port_data_lock
- || erts_lc_mtx_is_locked(
- &erts_port[ix].port_data_lock->mtx));
-
- status = erts_port[ix].status;
- }
- else {
- erts_smp_port_state_lock(&erts_port[ix]);
- status = erts_port[ix].status;
- erts_smp_port_state_unlock(&erts_port[ix]);
-
- ERTS_LC_ASSERT((status & ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP)
- || erts_port[ix].port_data_lock);
- ERTS_LC_ASSERT(!erts_port[ix].port_data_lock
- || erts_lc_mtx_is_locked(
- &erts_port[ix].port_data_lock->mtx));
-
- }
-
- return ((status & ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP)
- ? NULL
- : &erts_port[ix].ioq);
+ else
+ return &prt->ioq;
}
static ERTS_INLINE int
@@ -196,27 +180,13 @@ typedef struct line_buf_context {
dtrace_port_str((PORT), port_str);
#endif
-/* The 'number' field in a port now has two parts: the lowest bits
- contain the index in the port table, and the higher bits are a counter
- which is incremented each time we look for a free port and start from
- the beginning of the table. erts_max_ports is the number of file descriptors,
- rounded up to a power of 2.
- To get the index from a port, use the macro 'internal_port_index';
- 'port_number' returns the whole number field.
-*/
-
-static erts_smp_spinlock_t get_free_port_lck;
-static Uint last_port_num;
-static Uint port_num_mask;
-erts_smp_atomic32_t erts_ports_snapshot; /* Identifies the _next_ snapshot (not the ongoing) */
-
-
static ERTS_INLINE void
kill_port(Port *pp)
{
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(pp));
+ erts_ptab_delete_element(&erts_port, &pp->common); /* Time of death */
erts_port_task_free_port(pp);
- ASSERT(pp->status & ERTS_PORT_SFLGS_DEAD);
+ /* In non-smp case the port structure may have been deallocated now */
}
#ifdef ERTS_SMP
@@ -227,146 +197,280 @@ erts_lc_is_port_locked(Port *prt)
{
if (!prt)
return 0;
+ ERTS_SMP_LC_ASSERT(prt->lock);
return erts_smp_lc_mtx_is_locked(prt->lock);
}
#endif
#endif /* #ifdef ERTS_SMP */
-static int
-get_free_port(void)
-{
- Uint num;
- Uint tries = erts_max_ports;
- Port* port;
+static void initq(Port* prt);
- erts_smp_spin_lock(&get_free_port_lck);
- num = last_port_num + 1;
- for (;; ++num) {
- port = &erts_port[num & erts_port_tab_index_mask];
+#if defined(ERTS_ENABLE_LOCK_CHECK) || defined(ERTS_ENABLE_LOCK_COUNT)
+#define ERTS_PORT_INIT_INSTR_NEED_ID 1
+#else
+#define ERTS_PORT_INIT_INSTR_NEED_ID 0
+#endif
- erts_smp_port_state_lock(port);
- if (port->status & ERTS_PORT_SFLG_FREE) {
- last_port_num = num;
- erts_smp_spin_unlock(&get_free_port_lck);
- break;
- }
- erts_smp_port_state_unlock(port);
+static ERTS_INLINE void port_init_instr(Port *prt
+#if ERTS_PORT_INIT_INSTR_NEED_ID
+ , Eterm id
+#endif
+ )
+{
+#if !ERTS_PORT_INIT_INSTR_NEED_ID
+ Eterm id = NIL; /* Not used */
+#endif
- if (--tries == 0) {
- erts_smp_spin_unlock(&get_free_port_lck);
- return -1;
- }
+ /*
+ * Stuff that need to be initialized with the port id
+ * in the instrumented case, but not in the normal case.
+ */
+#ifdef ERTS_SMP
+ ASSERT(prt->drv_ptr && prt->lock);
+ if (!prt->drv_ptr->lock) {
+ char *lock_str = "port_lock";
+#ifdef ERTS_ENABLE_LOCK_COUNT
+ if (!(erts_lcnt_rt_options & ERTS_LCNT_OPT_PORTLOCK))
+ lock_str = NULL;
+#endif
+ erts_mtx_init_locked_x(prt->lock, lock_str, id);
}
- port->status = ERTS_PORT_SFLG_INITIALIZING;
- ERTS_LC_ASSERT(erts_smp_atomic_read_nob(&port->refc) == 0);
- erts_smp_atomic_set_nob(&port->refc, 2); /* Port alive + lock */
- erts_smp_port_state_unlock(port);
- return num & port_num_mask;
+#endif
+ erts_port_task_init_sched(&prt->sched, id);
}
-/*
- * erts_test_next_port() is only used for testing.
- */
-Sint
-erts_test_next_port(int set, Uint next)
+#if !ERTS_PORT_INIT_INSTR_NEED_ID
+static ERTS_INLINE void port_init_instr_abort(Port *prt)
{
- Uint i, num;
- Sint res = -1;
-
- erts_smp_spin_lock(&get_free_port_lck);
- if (set) {
- last_port_num = (next - 1) & port_num_mask;
+#ifdef ERTS_SMP
+ ASSERT(prt->drv_ptr && prt->lock);
+ if (!prt->drv_ptr->lock) {
+ erts_mtx_unlock(prt->lock);
+ erts_mtx_destroy(prt->lock);
}
- num = last_port_num + 1;
+#endif
+ erts_port_task_fini_sched(&prt->sched);
+}
+#endif
- for (i=0; i < erts_max_ports && res<0; ++i, ++num) {
-
- Port* port = &erts_port[num & erts_port_tab_index_mask];
+static void insert_port_struct(void *vprt, Eterm data)
+{
+ Port *prt = (Port *) vprt;
+ Eterm id = make_internal_port(data);
+#if ERTS_PORT_INIT_INSTR_NEED_ID
+ /*
+ * This cannot be done earlier in the instrumented
+ * case since we don't now 'id' until now.
+ */
+ port_init_instr(prt, id);
+#endif
+ prt->common.id = id;
+ erts_atomic32_init_relb(&prt->state, ERTS_PORT_SFLG_INITIALIZING);
+}
- erts_smp_port_state_lock(port);
+#define ERTS_CREATE_PORT_FLAG_PARALLELISM (1 << 0)
- if (port->status & ERTS_PORT_SFLG_FREE) {
- last_port_num = num - 1;
- res = num & port_num_mask;
- }
- erts_smp_port_state_unlock(port);
+static Port *create_port(char *name,
+ erts_driver_t *driver,
+ erts_mtx_t *driver_lock,
+ int create_flags,
+ Eterm pid,
+ int *enop)
+{
+ ErtsPortTaskBusyPortQ *busy_port_queue;
+ Port *prt;
+ char *p;
+ size_t port_size, busy_port_queue_size, size;
+ erts_aint32_t state = ERTS_PORT_SFLG_CONNECTED;
+ erts_aint32_t x_pts_flgs = 0;
+#ifdef DEBUG
+ /* Make sure the debug flags survives until port is freed */
+ state |= ERTS_PORT_SFLG_PORT_DEBUG;
+#endif
+
+#ifdef ERTS_SMP
+ if (!driver_lock) {
+ /* Align size for mutex following port struct */
+ port_size = size = ERTS_ALC_DATA_ALIGN_SIZE(sizeof(Port));
+ size += sizeof(erts_mtx_t);
}
- erts_smp_spin_unlock(&get_free_port_lck);
- return res;
-}
+ else
+#endif
+ port_size = size = ERTS_ALC_DATA_ALIGN_SIZE(sizeof(Port));
+ busy_port_queue_size
+ = ((driver->flags & ERL_DRV_FLAG_NO_BUSY_MSGQ)
+ ? 0
+ : ERTS_ALC_DATA_ALIGN_SIZE(sizeof(ErtsPortTaskBusyPortQ)));
+ size += busy_port_queue_size;
-static void port_cleanup(Port *prt);
+ size += sys_strlen(name) + 1;
-#ifdef ERTS_SMP
+ p = erts_alloc_fnf(ERTS_ALC_T_PORT, size);
+ if (!p) {
+ if (enop)
+ *enop = ENOMEM;
+ return NULL;
+ }
-static void
-sched_port_cleanup(void *vprt)
-{
- Port *prt = (Port *) vprt;
- erts_smp_mtx_lock(prt->lock);
- port_cleanup(prt);
-}
+ prt = (Port *) p;
+ p += port_size;
-#endif
+ if (!busy_port_queue_size)
+ busy_port_queue = NULL;
+ else {
+ busy_port_queue = (ErtsPortTaskBusyPortQ *) p;
+ p += busy_port_queue_size;
+ }
-void
-erts_port_cleanup(Port *prt)
-{
#ifdef ERTS_SMP
- if (erts_smp_mtx_trylock(prt->lock) == EBUSY)
- erts_schedule_misc_op(sched_port_cleanup, (void *) prt);
- else
+ if (driver_lock) {
+ prt->lock = driver_lock;
+ erts_mtx_lock(driver_lock);
+ }
+ else {
+ prt->lock = (erts_mtx_t *) p;
+ p += sizeof(erts_mtx_t);
+ state |= ERTS_PORT_SFLG_PORT_SPECIFIC_LOCK;
+ }
+ erts_smp_atomic_set_nob(&prt->run_queue,
+ (erts_aint_t) erts_get_runq_current(NULL));
+ prt->xports = NULL;
+#else
+ erts_atomic32_init_nob(&prt->refc, 1);
+ prt->cleanup = 0;
#endif
- port_cleanup(prt);
-}
+
+ erts_port_task_pre_init_sched(&prt->sched, busy_port_queue);
-void
-port_cleanup(Port *prt)
-{
+ prt->name = p;
+ sys_strcpy(p, name);
+ prt->drv_ptr = driver;
+ ERTS_P_LINKS(prt) = NULL;
+ ERTS_P_MONITORS(prt) = NULL;
+ prt->linebuf = NULL;
+ prt->bp = NULL;
+ prt->suspended = NULL;
+ prt->data = am_undefined;
+ prt->port_data_lock = NULL;
+ prt->control_flags = 0;
+ prt->bytes_in = 0;
+ prt->bytes_out = 0;
+ prt->dist_entry = NULL;
+ ERTS_PORT_INIT_CONNECTED(prt, pid);
+ prt->common.u.alive.reg = NULL;
#ifdef ERTS_SMP
- Uint32 port_specific;
- erts_smp_mtx_t *mtx;
+ prt->common.u.alive.ptimer = NULL;
+#else
+ sys_memset(&prt->common.u.alive.tm, 0, sizeof(ErlTimer));
#endif
- erts_driver_t *driver;
+ erts_port_task_handle_init(&prt->timeout_task);
+ prt->psd = NULL;
+ prt->drv_data = (SWord) 0;
+ prt->os_pid = -1;
- erts_smp_port_state_lock(prt);
+ /* Set default tracing */
+ erts_get_default_tracing(&ERTS_TRACE_FLAGS(prt), &ERTS_TRACER_PROC(prt));
- ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
- driver = prt->drv_ptr;
- prt->drv_ptr = NULL;
- ASSERT(driver);
+ ASSERT(((char *) prt) == ((char *) &prt->common));
- ASSERT(prt->status & ERTS_PORT_SFLG_FREE_SCHEDULED);
- ERTS_LC_ASSERT(erts_smp_atomic_read_nob(&prt->refc) == 0);
+#if !ERTS_PORT_INIT_INSTR_NEED_ID
+ /*
+ * When 'id' isn't needed (the normal case), it is better to
+ * do the initialization here avoiding unnecessary contention
+ * on table...
+ */
+ port_init_instr(prt);
+#endif
- ASSERT(prt->status & ERTS_PORT_SFLG_PORT_DEBUG);
- ASSERT(!(prt->status & ERTS_PORT_SFLG_FREE));
- prt->status = ERTS_PORT_SFLG_FREE;
+ if (!erts_ptab_new_element(&erts_port,
+ &prt->common,
+ (void *) prt,
+ insert_port_struct)) {
+#if !ERTS_PORT_INIT_INSTR_NEED_ID
+ port_init_instr_abort(prt);
+#endif
#ifdef ERTS_SMP
+ if (driver_lock)
+ erts_mtx_unlock(driver_lock);
+#endif
+ if (enop)
+ *enop = 0;
+ return NULL;
+ }
+
+ ASSERT(prt == (Port *) (erts_ptab_pix2intptr_nob(
+ &erts_port,
+ internal_port_index(prt->common.id))));
+
+ initq(prt);
- port_specific = (prt->status & ERTS_PORT_SFLG_PORT_SPECIFIC_LOCK);
+ ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
- mtx = prt->lock;
- ASSERT(mtx);
+ if (erts_port_schedule_all_ops)
+ x_pts_flgs |= ERTS_PTS_FLG_FORCE_SCHED;
- prt->lock = NULL;
+ if (create_flags & ERTS_CREATE_PORT_FLAG_PARALLELISM)
+ x_pts_flgs |= ERTS_PTS_FLG_PARALLELISM;
- erts_smp_port_state_unlock(prt);
- erts_smp_mtx_unlock(mtx);
+ if (x_pts_flgs)
+ erts_smp_atomic32_read_bor_nob(&prt->sched.flags, x_pts_flgs);
- if (port_specific) {
- erts_smp_mtx_destroy(mtx);
- erts_free(ERTS_ALC_T_PORT_LOCK, mtx);
- }
-#endif
+ erts_atomic32_set_relb(&prt->state, state);
+ return prt;
+}
- if (driver->handle)
- erts_ddll_dereference_driver(driver->handle);
+#ifndef ERTS_SMP
+void
+erts_port_cleanup(Port *prt)
+{
+ if (prt->drv_ptr && prt->drv_ptr->handle)
+ erts_ddll_dereference_driver(prt->drv_ptr->handle);
+ prt->drv_ptr = NULL;
+ erts_port_dec_refc(prt);
}
+#endif
+
+void
+erts_port_free(Port *prt)
+{
+#if defined(ERTS_SMP) || defined(DEBUG) || defined(ERTS_ENABLE_LOCK_CHECK)
+ erts_aint32_t state = erts_atomic32_read_nob(&prt->state);
+#endif
+ ERTS_LC_ASSERT(state & (ERTS_PORT_SFLG_INITIALIZING
+ | ERTS_PORT_SFLG_FREE));
+ ASSERT(state & ERTS_PORT_SFLG_PORT_DEBUG);
+
+#ifdef ERTS_SMP
+ ERTS_LC_ASSERT(erts_atomic32_read_nob(&prt->common.refc) == 0);
+#else
+ ERTS_LC_ASSERT(erts_atomic32_read_nob(&prt->refc) == 0);
+#endif
+ erts_port_task_fini_sched(&prt->sched);
+
+#ifdef ERTS_SMP
+ ASSERT(prt->lock);
+ if (state & ERTS_PORT_SFLG_PORT_SPECIFIC_LOCK)
+ erts_mtx_destroy(prt->lock);
+
+ /*
+ * We cannot dereference a driver using driver
+ * locking until here in smp case. Otherwise,
+ * the driver lock may still be in use by others.
+ *
+ * In the non-smp case we cannot do it here since
+ * this function may be called by non-scheduler
+ * threads. This is done in erts_port_cleanup()
+ * in the non-smp case.
+ */
+ if (prt->drv_ptr->handle)
+ erts_ddll_dereference_driver(prt->drv_ptr->handle);
+#endif
+ erts_free(ERTS_ALC_T_PORT, prt);
+}
/*
** Initialize v_start to point to the small fixed vector.
@@ -414,94 +518,21 @@ static void stopq(Port* prt)
if (prt->port_data_lock) {
driver_pdl_unlock(prt->port_data_lock);
driver_pdl_dec_refc(prt->port_data_lock);
- prt->port_data_lock = NULL;
}
}
-
-
-static void
-setup_port(Port* prt, Eterm pid, erts_driver_t *driver,
- ErlDrvData drv_data, char *name, Uint32 xstatus)
-{
- ErtsRunQueue *runq = erts_get_runq_current(NULL);
- char *new_name, *old_name;
-#ifdef DEBUG
- /* Make sure the debug flags survives until port is freed */
- xstatus |= ERTS_PORT_SFLG_PORT_DEBUG;
-#endif
- ASSERT(runq);
- ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
-
-
- new_name = (char*) erts_alloc(ERTS_ALC_T_PORT_NAME, sys_strlen(name)+1);
- sys_strcpy(new_name, name);
- erts_smp_runq_lock(runq);
- erts_smp_port_state_lock(prt);
- prt->os_pid = -1;
- prt->status = ERTS_PORT_SFLG_CONNECTED | xstatus;
- prt->snapshot = erts_smp_atomic32_read_nob(&erts_ports_snapshot);
- old_name = prt->name;
- prt->name = new_name;
-#ifdef ERTS_SMP
- erts_smp_atomic_set_nob(&prt->run_queue, (erts_aint_t) runq);
-#endif
- ASSERT(!prt->drv_ptr);
- prt->drv_ptr = driver;
- erts_smp_port_state_unlock(prt);
- erts_smp_runq_unlock(runq);
-#ifdef ERTS_SMP
- ASSERT(!prt->xports);
-#endif
- if (old_name) {
- erts_free(ERTS_ALC_T_PORT_NAME, (void *) old_name);
- }
-
- prt->control_flags = 0;
- prt->connected = pid;
- prt->drv_data = (SWord) drv_data;
- prt->bytes_in = 0;
- prt->bytes_out = 0;
- prt->dist_entry = NULL;
- prt->reg = NULL;
-#ifdef ERTS_SMP
- prt->ptimer = NULL;
-#else
- sys_memset(&prt->tm, 0, sizeof(ErlTimer));
-#endif
- erts_port_task_handle_init(&prt->timeout_task);
- prt->suspended = NULL;
- sys_strcpy(prt->name, name);
- prt->nlinks = NULL;
- prt->monitors = NULL;
- prt->linebuf = NULL;
- prt->bp = NULL;
- prt->data = am_undefined;
- /* Set default tracing */
- erts_get_default_tracing(&(prt->trace_flags), &(prt->tracer_proc));
-
- prt->psd = NULL;
-
- initq(prt);
-}
-
-void
-erts_wake_process_later(Port *prt, Process *process)
+int
+erts_save_suspend_process_on_port(Port *prt, Process *process)
{
- ErtsProcList** p;
- ErtsProcList* new_p;
-
- ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
-
- if (prt->status & ERTS_PORT_SFLGS_DEAD)
- return;
-
- for (p = &(prt->suspended); *p != NULL; p = &((*p)->next))
- /* Empty loop body */;
-
- new_p = erts_proclist_create(process);
- new_p->next = NULL;
- *p = new_p;
+ int saved;
+ erts_aint32_t flags;
+ erts_port_task_sched_lock(&prt->sched);
+ flags = erts_smp_atomic32_read_nob(&prt->sched.flags);
+ saved = (flags & ERTS_PTS_FLGS_BUSY) && !(flags & ERTS_PTS_FLG_EXIT);
+ if (saved)
+ erts_proclist_store_last(&prt->suspended, erts_proclist_create(process));
+ erts_port_task_sched_unlock(&prt->sched);
+ return saved;
}
/*
@@ -513,47 +544,44 @@ erts_wake_process_later(Port *prt, Process *process)
(*error_number_ptr must contain either BADARG or SYSTEM_LIMIT).
The driver start function must obey the same conventions.
*/
-int
+Port *
erts_open_driver(erts_driver_t* driver, /* Pointer to driver. */
Eterm pid, /* Current process. */
char* name, /* Driver name. */
SysDriverOpts* opts, /* Options. */
- int *error_number_ptr) /* errno in case -2 is returned */
+ int *error_type_ptr, /* error type */
+ int *error_number_ptr) /* errno in case of error type -2 */
{
- int port_num;
- int port_ix;
+
+#undef ERTS_OPEN_DRIVER_RET
+#define ERTS_OPEN_DRIVER_RET(Prt, EType, ENo) \
+ do { \
+ if (error_type_ptr) \
+ *error_type_ptr = (EType); \
+ if (error_number_ptr) \
+ *error_number_ptr = (ENo); \
+ return (Prt); \
+ } while (0)
+
ErlDrvData drv_data = 0;
- Uint32 xstatus = 0;
Port *port;
int fpe_was_unmasked;
-
- if (error_number_ptr)
- *error_number_ptr = 0;
+ int error_type, error_number;
+ int port_errno = 0;
+ erts_mtx_t *driver_lock = NULL;
+ int cprt_flgs = 0;
ERTS_SMP_CHK_NO_PROC_LOCKS;
- if ((port_num = get_free_port()) < 0) {
- if (error_number_ptr) {
- *error_number_ptr = SYSTEM_LIMIT;
- }
- return -3;
- }
-
- port_ix = port_num & erts_port_tab_index_mask;
- port = &erts_port[port_ix];
- port->id = make_internal_port(port_num);
-
- erts_smp_mtx_lock(&erts_driver_list_lock);
+ erts_smp_rwmtx_rlock(&erts_driver_list_lock);
if (!driver) {
for (driver = driver_list; driver; driver = driver->next) {
if (sys_strcmp(driver->name, name) == 0)
break;
}
if (!driver) {
- erts_smp_mtx_unlock(&erts_driver_list_lock);
- if (error_number_ptr)
- *error_number_ptr = BADARG;
- return -3;
+ erts_smp_rwmtx_runlock(&erts_driver_list_lock);
+ ERTS_OPEN_DRIVER_RET(NULL, -3, BADARG);
}
}
if (driver == &spawn_driver) {
@@ -597,59 +625,52 @@ erts_open_driver(erts_driver_t* driver, /* Pointer to driver. */
}
if (driver == NULL || (driver != &spawn_driver && opts->exit_status)) {
- erts_smp_mtx_unlock(&erts_driver_list_lock);
- if (error_number_ptr) {
- *error_number_ptr = BADARG;
- }
- /* Need to mark the port as free again */
- erts_smp_port_state_lock(port);
- port->status = ERTS_PORT_SFLG_FREE;
- ERTS_LC_ASSERT(erts_smp_atomic_read_nob(&port->refc) == 2);
- erts_smp_atomic_set_nob(&port->refc, 0);
- erts_smp_port_state_unlock(port);
- return -3;
+ erts_smp_rwmtx_runlock(&erts_driver_list_lock);
+ ERTS_OPEN_DRIVER_RET(NULL, -3, BADARG);
}
- /*
- * We'll set up the port before calling the start function,
- * to allow message sending and setting timers in the start function.
- */
-
#ifdef ERTS_SMP
- ASSERT(!port->lock);
- port->lock = driver->lock;
- if (!port->lock) {
- port->lock = erts_alloc(ERTS_ALC_T_PORT_LOCK,
- sizeof(erts_smp_mtx_t));
- erts_smp_mtx_init_x(port->lock,
-#ifdef ERTS_ENABLE_LOCK_COUNT
- (erts_lcnt_rt_options & ERTS_LCNT_OPT_PORTLOCK) ? "port_lock" : NULL,
-#else
- "port_lock",
-#endif
- port->id);
- xstatus |= ERTS_PORT_SFLG_PORT_SPECIFIC_LOCK;
- }
+ driver_lock = driver->lock;
#endif
if (driver->handle != NULL) {
erts_ddll_increment_port_count(driver->handle);
erts_ddll_reference_driver(driver->handle);
}
- erts_smp_mtx_unlock(&erts_driver_list_lock);
+ erts_smp_rwmtx_runlock(&erts_driver_list_lock);
-#ifdef ERTS_SMP
- erts_smp_mtx_lock(port->lock);
-#endif
+ /*
+ * We'll set up the port before calling the start function,
+ * to allow message sending and setting timers in the start function.
+ */
+
+ if (opts->parallelism)
+ cprt_flgs |= ERTS_CREATE_PORT_FLAG_PARALLELISM;
- setup_port(port, pid, driver, drv_data, name, xstatus);
+ port = create_port(name, driver, driver_lock, cprt_flgs, pid, &port_errno);
+ if (!port) {
+ if (driver->handle) {
+ erts_smp_rwmtx_rlock(&erts_driver_list_lock);
+ erts_ddll_decrement_port_count(driver->handle);
+ erts_smp_rwmtx_runlock(&erts_driver_list_lock);
+ erts_ddll_dereference_driver(driver->handle);
+ }
+ if (port_errno)
+ ERTS_OPEN_DRIVER_RET(NULL, -2, port_errno);
+ else
+ ERTS_OPEN_DRIVER_RET(NULL, -3, SYSTEM_LIMIT);
+ }
if (IS_TRACED_FL(port, F_TRACE_PORTS)) {
trace_port_open(port,
- pid,
- am_atom_put(port->name, strlen(port->name)));
+ pid,
+ erts_atom_put((byte *) port->name,
+ strlen(port->name),
+ ERTS_ATOM_ENC_LATIN1,
+ 1));
}
-
+
+ error_number = error_type = 0;
if (driver->start) {
if (IS_TRACED_FL(port, F_TRACE_SCHED_PORTS)) {
trace_sched_ports_where(port, am_in, am_start);
@@ -662,56 +683,63 @@ erts_open_driver(erts_driver_t* driver, /* Pointer to driver. */
}
#endif
fpe_was_unmasked = erts_block_fpe();
- drv_data = (*driver->start)((ErlDrvPort)(port_ix),
- name, opts);
+ drv_data = (*driver->start)((ErlDrvPort) port, name, opts);
+ if (((SWord) drv_data) == -1)
+ error_type = -1;
+ else if (((SWord) drv_data) == -2) {
+ /*
+ * We need to save errno quickly after the
+ * call to the 'start' callback before
+ * something else modify it.
+ */
+ error_type = -2;
+ error_number = errno;
+ }
+ else if (((SWord) drv_data) == -3) {
+ error_type = -3;
+ error_number = BADARG;
+ }
+
erts_unblock_fpe(fpe_was_unmasked);
port->caller = NIL;
if (IS_TRACED_FL(port, F_TRACE_SCHED_PORTS)) {
trace_sched_ports_where(port, am_out, am_start);
}
- if (error_number_ptr && ((SWord) drv_data) == (SWord) -2)
- *error_number_ptr = errno;
#ifdef ERTS_SMP
if (port->xports)
- erts_smp_xports_unlock(port);
+ erts_port_handle_xports(port);
ASSERT(!port->xports);
#endif
}
- if (((SWord)drv_data) == -1 ||
- ((SWord)drv_data) == -2 ||
- ((SWord)drv_data) == -3) {
- int res = (int) ((SWord) drv_data);
-
- if (res == -3 && error_number_ptr) {
- *error_number_ptr = BADARG;
- }
-
+ if (error_type) {
/*
* Must clean up the port.
*/
#ifdef ERTS_SMP
- erts_cancel_smp_ptimer(port->ptimer);
+ erts_cancel_smp_ptimer(port->common.u.alive.ptimer);
#else
- erts_cancel_timer(&(port->tm));
+ erts_cancel_timer(&(port->common.u.alive.tm));
#endif
stopq(port);
- kill_port(port);
if (port->linebuf != NULL) {
erts_free(ERTS_ALC_T_LINEBUF,
(void *) port->linebuf);
port->linebuf = NULL;
}
if (driver->handle != NULL) {
- erts_smp_mtx_lock(&erts_driver_list_lock);
+ erts_smp_rwmtx_rlock(&erts_driver_list_lock);
erts_ddll_decrement_port_count(driver->handle);
- erts_smp_mtx_unlock(&erts_driver_list_lock);
+ erts_smp_rwmtx_runlock(&erts_driver_list_lock);
}
+ kill_port(port);
erts_port_release(port);
- return res;
+ ERTS_OPEN_DRIVER_RET(NULL, error_type, error_number);
}
- port->drv_data = (SWord) drv_data;
- return port_ix;
+ port->drv_data = (UWord) drv_data;
+ ERTS_OPEN_DRIVER_RET(port, 0, 0);
+
+#undef ERTS_OPEN_DRIVER_RET
}
#ifdef ERTS_SMP
@@ -736,102 +764,122 @@ driver_create_port(ErlDrvPort creator_port_ix, /* Creating port */
char* name, /* Driver name */
ErlDrvData drv_data) /* Driver data */
{
+ int cprt_flgs = 0;
Port *creator_port;
Port* port;
erts_driver_t *driver;
Process *rp;
- int port_num;
- Eterm port_id;
- Uint32 xstatus = 0;
+ erts_mtx_t *driver_lock = NULL;
ERTS_SMP_CHK_NO_PROC_LOCKS;
- creator_port = erts_drvport2port(creator_port_ix);
+ /* Need to be called from a scheduler thread */
+ if (!erts_get_scheduler_id())
+ return ERTS_INVALID_ERL_DRV_PORT;
+
+ creator_port = erts_drvport2port(creator_port_ix, NULL);
if (!creator_port)
- return (ErlDrvTermData) -1;
+ return ERTS_INVALID_ERL_DRV_PORT;
+
+ rp = erts_proc_lookup(pid);
+ if (!rp)
+ return ERTS_INVALID_ERL_DRV_PORT;
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(creator_port));
driver = creator_port->drv_ptr;
- erts_smp_mtx_lock(&erts_driver_list_lock);
+ erts_smp_rwmtx_rlock(&erts_driver_list_lock);
if (!erts_ddll_driver_ok(driver->handle)) {
- erts_smp_mtx_unlock(&erts_driver_list_lock);
- return (ErlDrvTermData) -1;
+ erts_smp_rwmtx_runlock(&erts_driver_list_lock);
+ return ERTS_INVALID_ERL_DRV_PORT;
}
- rp = erts_pid2proc(NULL, 0, pid, ERTS_PROC_LOCK_LINK);
- if (!rp) {
- erts_smp_mtx_unlock(&erts_driver_list_lock);
- return (ErlDrvTermData) -1; /* pid does not exist */
+ if (driver->handle != NULL) {
+ erts_ddll_increment_port_count(driver->handle);
+ erts_ddll_reference_referenced_driver(driver->handle);
+ }
+
+#ifdef ERTS_SMP
+ driver_lock = driver->lock;
+#endif
+
+ erts_smp_rwmtx_runlock(&erts_driver_list_lock);
+
+ /* Inherit parallelism flag from parent */
+ if (ERTS_PTS_FLG_PARALLELISM &
+ erts_smp_atomic32_read_nob(&creator_port->sched.flags))
+ cprt_flgs |= ERTS_CREATE_PORT_FLAG_PARALLELISM;
+ port = create_port(name, driver, driver_lock, cprt_flgs, pid, NULL);
+ if (!port) {
+ if (driver->handle) {
+ erts_smp_rwmtx_rlock(&erts_driver_list_lock);
+ erts_ddll_decrement_port_count(driver->handle);
+ erts_smp_rwmtx_runlock(&erts_driver_list_lock);
+ erts_ddll_dereference_driver(driver->handle);
+ }
+ return ERTS_INVALID_ERL_DRV_PORT;
}
- if ((port_num = get_free_port()) < 0) {
- errno = SYSTEM_LIMIT;
+ ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(port));
+
+ erts_smp_proc_lock(rp, ERTS_PROC_LOCK_LINK);
+ if (ERTS_PROC_IS_EXITING(rp)) {
erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
- erts_smp_mtx_unlock(&erts_driver_list_lock);
- return (ErlDrvTermData) -1;
+ if (driver->handle) {
+ erts_smp_rwmtx_rlock(&erts_driver_list_lock);
+ erts_ddll_decrement_port_count(driver->handle);
+ erts_smp_rwmtx_runlock(&erts_driver_list_lock);
+ }
+ kill_port(port);
+ erts_port_release(port);
+ return ERTS_INVALID_ERL_DRV_PORT;
}
- port_id = make_internal_port(port_num);
- port = &erts_port[port_num & erts_port_tab_index_mask];
+ erts_add_link(&ERTS_P_LINKS(port), LINK_PID, pid);
+ erts_add_link(&ERTS_P_LINKS(rp), LINK_PID, port->common.id);
+ erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
#ifdef ERTS_SMP
- ASSERT(!port->lock);
- port->lock = driver->lock;
- if (!port->lock) {
+ if (!driver_lock) {
ErtsXPortsList *xplp = xports_list_alloc();
xplp->port = port;
xplp->next = creator_port->xports;
creator_port->xports = xplp;
- port->lock = erts_alloc(ERTS_ALC_T_PORT_LOCK,
- sizeof(erts_smp_mtx_t));
- erts_smp_mtx_init_locked_x(port->lock,
-#ifdef ERTS_ENABLE_LOCK_COUNT
- (erts_lcnt_rt_options & ERTS_LCNT_OPT_PORTLOCK) ? "port_lock" : NULL,
-#else
- "port_lock",
-#endif
- port_id);
- xstatus |= ERTS_PORT_SFLG_PORT_SPECIFIC_LOCK;
}
-
#endif
- if (driver->handle != NULL) {
- erts_ddll_increment_port_count(driver->handle);
- erts_ddll_reference_referenced_driver(driver->handle);
- }
- erts_smp_mtx_unlock(&erts_driver_list_lock);
-
- ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(port));
-
- setup_port(port, pid, driver, drv_data, name, xstatus);
- port->id = port_id;
+ port->drv_data = (UWord) drv_data;
- erts_add_link(&(port->nlinks), LINK_PID, pid);
- erts_add_link(&(rp->nlinks), LINK_PID, port_id);
- erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
- return port_num & erts_port_tab_index_mask;
+ return (ErlDrvPort) port;
}
#ifdef ERTS_SMP
-void
-erts_smp_xports_unlock(Port *prt)
+int erts_port_handle_xports(Port *prt)
{
+ int reds = 0;
ErtsXPortsList *xplp;
ASSERT(prt);
xplp = prt->xports;
ASSERT(xplp);
while (xplp) {
+ Port *rprt = xplp->port;
ErtsXPortsList *free_xplp;
- if (xplp->port->xports)
- erts_smp_xports_unlock(xplp->port);
- erts_port_release(xplp->port);
+ erts_aint32_t state;
+ if (rprt->xports)
+ reds += erts_port_handle_xports(rprt);
+ state = erts_atomic32_read_nob(&rprt->state);
+ if ((state & ERTS_PORT_SFLG_CLOSING) && erts_is_port_ioq_empty(rprt)) {
+ terminate_port(rprt);
+ reds += ERTS_PORT_REDS_TERMINATE;
+ }
+ erts_port_release(rprt);
free_xplp = xplp;
xplp = xplp->next;
xports_list_free(free_xplp);
+ reds++;
}
prt->xports = NULL;
+ return reds;
}
#endif
@@ -866,8 +914,8 @@ io_list_to_vec(Eterm obj, /* io-list */
DECLARE_ESTACK(s);
Eterm* objp;
char *buf = cbin->orig_bytes;
- ErlDrvSizeT len = cbin->orig_size;
- ErlDrvSizeT csize = 0;
+ Uint len = cbin->orig_size;
+ Uint csize = 0;
int vlen = 0;
char* cptr = buf;
@@ -982,7 +1030,7 @@ io_list_to_vec(Eterm obj, /* io-list */
#define IO_LIST_VEC_COUNT(obj) \
do { \
- ErlDrvSizeT _size = binary_size(obj); \
+ Uint _size = binary_size(obj); \
Eterm _real; \
ERTS_DECLARE_DUMMY(Uint _offset); \
int _bitoffs; \
@@ -1033,8 +1081,9 @@ do { \
*/
static int
-io_list_vec_len(Eterm obj, Uint* vsize, Uint* csize,
- Uint* pvsize, Uint* pcsize, Uint* total_size)
+io_list_vec_len(Eterm obj, int* vsize, Uint* csize,
+ Uint* pvsize, Uint* pcsize,
+ ErlDrvSizeT* total_size)
{
DECLARE_ESTACK(s);
Eterm* objp;
@@ -1045,7 +1094,7 @@ io_list_vec_len(Eterm obj, Uint* vsize, Uint* csize,
Uint p_v_size = 0;
Uint p_c_size = 0;
Uint p_in_clist = 0;
- Uint total;
+ Uint total; /* Uint due to halfword emulator */
goto L_jump_start; /* avoid a push */
@@ -1105,7 +1154,7 @@ io_list_vec_len(Eterm obj, Uint* vsize, Uint* csize,
if (total < c_size) {
goto L_overflow_error;
}
- *total_size = total;
+ *total_size = (ErlDrvSizeT) total;
DESTROY_ESTACK(s);
*vsize = v_size;
@@ -1120,56 +1169,726 @@ io_list_vec_len(Eterm obj, Uint* vsize, Uint* csize,
return 1;
}
-/* write data to a port */
-int erts_write_to_port(Eterm caller_id, Port *p, Eterm list)
-{
- char *buf;
- erts_driver_t *drv = p->drv_ptr;
- Uint size;
+typedef enum {
+ ERTS_TRY_IMM_DRV_CALL_OK,
+ ERTS_TRY_IMM_DRV_CALL_BUSY_LOCK,
+ ERTS_TRY_IMM_DRV_CALL_INVALID_PORT,
+ ERTS_TRY_IMM_DRV_CALL_INVALID_SCHED_FLAGS
+} ErtsTryImmDrvCallResult;
+
+typedef struct {
+ Process *c_p; /* Currently executing process (unlocked) */
+ Port *port; /* Port to operate on */
+ Eterm port_op; /* port operation as an atom */
+ erts_aint32_t state; /* in: invalid state; out: read state (if read) */
+ erts_aint32_t sched_flags; /* in: invalid flags; out: read flags (if read) */
+ int async; /* Asynchronous operation */
+ int pre_chk_sched_flags; /* Check sched flags before lock? */
int fpe_was_unmasked;
-
- ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(p));
+} ErtsTryImmDrvCallState;
+
+#define ERTS_INIT_TRY_IMM_DRV_CALL_STATE(C_P, PRT, SFLGS, PTS_FLGS, A, PRT_OP) \
+ {(C_P), (PRT), (PRT_OP), (SFLGS), (PTS_FLGS), (A), 1, 0}
+
+/*
+ * Try doing an immediate driver callback call from a process. If
+ * this fail, the operation should be scheduled in the normal case...
+ *
+ */
+static ERTS_INLINE ErtsTryImmDrvCallResult
+try_imm_drv_call(ErtsTryImmDrvCallState *sp)
+{
+ ErtsTryImmDrvCallResult res;
+ erts_aint32_t invalid_state, invalid_sched_flags;
+ Port *prt = sp->port;
+ Process *c_p = sp->c_p;
+
+ ASSERT(is_atom(sp->port_op));
+
+ invalid_sched_flags = ERTS_PTS_FLGS_FORCE_SCHEDULE_OP;
+ invalid_sched_flags |= sp->sched_flags;
+ if (sp->async)
+ invalid_sched_flags |= ERTS_PTS_FLG_PARALLELISM;
+
+ if (sp->pre_chk_sched_flags) {
+ sp->sched_flags = erts_smp_atomic32_read_nob(&prt->sched.flags);
+ if (sp->sched_flags & invalid_sched_flags)
+ return ERTS_TRY_IMM_DRV_CALL_INVALID_SCHED_FLAGS;
+ }
+
+ if (erts_smp_port_trylock(prt) == EBUSY)
+ return ERTS_TRY_IMM_DRV_CALL_BUSY_LOCK;
+
+ invalid_state = sp->state;
+ sp->state = erts_atomic32_read_nob(&prt->state);
+ if (sp->state & invalid_state) {
+ res = ERTS_TRY_IMM_DRV_CALL_INVALID_PORT;
+ goto locked_fail;
+ }
+
+ sp->sched_flags = erts_smp_atomic32_read_nob(&prt->sched.flags);
+ if (sp->sched_flags & invalid_sched_flags) {
+ res = ERTS_TRY_IMM_DRV_CALL_INVALID_SCHED_FLAGS;
+ goto locked_fail;
+ }
+
+ if (c_p) {
+ if (IS_TRACED_FL(c_p, F_TRACE_SCHED_PROCS))
+ trace_virtual_sched(c_p, am_out);
+ if (erts_system_profile_flags.runnable_procs
+ && erts_system_profile_flags.exclusive)
+ profile_runnable_proc(c_p, am_inactive);
+
+ erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_MAIN);
+ }
+
ERTS_SMP_CHK_NO_PROC_LOCKS;
- p->caller = caller_id;
- if (drv->outputv != NULL) {
- Uint vsize;
- Uint csize;
- Uint pvsize;
- Uint pcsize;
- ErlDrvSizeT blimit;
+ if (IS_TRACED_FL(prt, F_TRACE_SCHED_PORTS))
+ trace_sched_ports_where(prt, am_in, sp->port_op);
+ if (erts_system_profile_flags.runnable_ports
+ && !erts_port_is_scheduled(prt))
+ profile_runnable_port(prt, am_active);
+
+ sp->fpe_was_unmasked = erts_block_fpe();
+
+ return ERTS_TRY_IMM_DRV_CALL_OK;
+
+locked_fail:
+ erts_port_release(prt);
+ return res;
+}
+
+static ERTS_INLINE void
+finalize_imm_drv_call(ErtsTryImmDrvCallState *sp)
+{
+ Port *prt = sp->port;
+ Process *c_p = sp->c_p;
+
+ erts_port_driver_callback_epilogue(prt, NULL);
+
+ erts_unblock_fpe(sp->fpe_was_unmasked);
+
+ if (IS_TRACED_FL(prt, F_TRACE_SCHED_PORTS))
+ trace_sched_ports_where(prt, am_out, sp->port_op);
+ if (erts_system_profile_flags.runnable_ports
+ && !erts_port_is_scheduled(prt))
+ profile_runnable_port(prt, am_inactive);
+
+ erts_port_release(prt);
+
+ if (c_p) {
+ erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_MAIN);
+
+ if (IS_TRACED_FL(c_p, F_TRACE_SCHED_PROCS))
+ trace_virtual_sched(c_p, am_in);
+ if (erts_system_profile_flags.runnable_procs
+ && erts_system_profile_flags.exclusive)
+ profile_runnable_proc(c_p, am_active);
+ }
+}
+
+/*
+ * force_imm_drv_call()/finalize_force_imm_drv_call() should *only*
+ * be used while crash dumping...
+ */
+static ErtsTryImmDrvCallResult
+force_imm_drv_call(ErtsTryImmDrvCallState *sp)
+{
+ erts_aint32_t invalid_state;
+ Port *prt = sp->port;
+
+ ASSERT(ERTS_IS_CRASH_DUMPING)
+ ASSERT(is_atom(sp->port_op));
+
+ invalid_state = sp->state;
+ sp->state = erts_atomic32_read_nob(&prt->state);
+ if (sp->state & invalid_state)
+ return ERTS_TRY_IMM_DRV_CALL_INVALID_PORT;
+
+ sp->fpe_was_unmasked = erts_block_fpe();
+
+ return ERTS_TRY_IMM_DRV_CALL_OK;
+}
+
+static void
+finalize_force_imm_drv_call(ErtsTryImmDrvCallState *sp)
+{
+ erts_unblock_fpe(sp->fpe_was_unmasked);
+}
+
+#define ERTS_QUEUE_PORT_SCHED_OP_REPLY_SIZE (REF_THING_SIZE + 3)
+
+static ERTS_INLINE void
+queue_port_sched_op_reply(Process *rp,
+ ErtsProcLocks *rp_locksp,
+ Eterm *hp_start,
+ Eterm *hp,
+ Uint h_size,
+ ErlHeapFragment* bp,
+ Uint32 *ref_num,
+ Eterm msg)
+{
+ Eterm ref = make_internal_ref(hp);
+ write_ref_thing(hp, ref_num[0], ref_num[1], ref_num[2]);
+ hp += REF_THING_SIZE;
+
+ msg = TUPLE2(hp, ref, msg);
+ hp += 3;
+
+ if (!bp) {
+ HRelease(rp, hp_start + h_size, hp);
+ }
+ else {
+ Uint used_h_size = hp - hp_start;
+ ASSERT(h_size >= used_h_size);
+ if (h_size > used_h_size)
+ bp = erts_resize_message_buffer(bp, used_h_size, &msg, 1);
+ }
+
+ erts_queue_message(rp,
+ rp_locksp,
+ bp,
+ msg,
+ NIL
+#ifdef USE_VM_PROBES
+ , NIL
+#endif
+ );
+}
+
+static void
+port_sched_op_reply(Eterm to, Uint32 *ref_num, Eterm msg)
+{
+ Process *rp = erts_proc_lookup_raw(to);
+ if (rp) {
+ ErlOffHeap *ohp;
+ ErlHeapFragment* bp;
+ Eterm msg_copy;
+ Uint hsz, msg_sz;
+ Eterm *hp, *hp_start;
+ ErtsProcLocks rp_locks = 0;
+
+ hsz = ERTS_QUEUE_PORT_SCHED_OP_REPLY_SIZE;
+ if (is_immed(msg))
+ msg_sz = 0;
+ else {
+ msg_sz = size_object(msg);
+ hsz += msg_sz;
+ }
+
+ hp_start = hp = erts_alloc_message_heap(hsz,
+ &bp,
+ &ohp,
+ rp,
+ &rp_locks);
+ if (is_immed(msg))
+ msg_copy = msg;
+ else
+ msg_copy = copy_struct(msg, msg_sz, &hp, ohp);
+
+ queue_port_sched_op_reply(rp,
+ &rp_locks,
+ hp_start,
+ hp,
+ hsz,
+ bp,
+ ref_num,
+ msg_copy);
+
+ if (rp_locks)
+ erts_smp_proc_unlock(rp, rp_locks);
+ }
+}
+
+
+ErtsPortOpResult
+erts_schedule_proc2port_signal(Process *c_p,
+ Port *prt,
+ Eterm caller,
+ Eterm *refp,
+ ErtsProc2PortSigData *sigdp,
+ int task_flags,
+ ErtsProc2PortSigCallback callback)
+{
+ int sched_res;
+ if (!refp) {
+ if (c_p)
+ erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_MAIN);
+ }
+ else {
+ ASSERT(c_p);
+ sigdp->flags |= ERTS_P2P_SIG_DATA_FLG_REPLY;
+ erts_make_ref_in_array(sigdp->ref);
+ *refp = erts_proc_store_ref(c_p, sigdp->ref);
+
+ /*
+ * Caller needs to wait for a message containing
+ * the ref that we just created. No such message
+ * can exist in callers message queue at this time.
+ * We therefore move the save pointer of the
+ * callers message queue to the end of the queue.
+ *
+ * NOTE: It is of vital importance that the caller
+ * immediately do a receive unconditionaly
+ * waiting for the message with the reference;
+ * otherwise, next receive will *not* work
+ * as expected!
+ */
+ erts_smp_proc_lock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE);
+
+ if (ERTS_PROC_PENDING_EXIT(c_p)) {
+ /* need to exit caller instead */
+ erts_smp_proc_unlock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE);
+ KILL_CATCHES(c_p);
+ c_p->freason = EXC_EXIT;
+ return ERTS_PORT_OP_CALLER_EXIT;
+ }
+
+ ERTS_SMP_MSGQ_MV_INQ2PRIVQ(c_p);
+ c_p->msg.save = c_p->msg.last;
+
+ erts_smp_proc_unlock(c_p,
+ (ERTS_PROC_LOCK_MAIN
+ | ERTS_PROC_LOCKS_MSG_RECEIVE));
+ }
+
+
+ sigdp->caller = caller;
+
+ /* Schedule port close call for later execution... */
+ sched_res = erts_port_task_schedule(prt->common.id,
+ NULL,
+ ERTS_PORT_TASK_PROC_SIG,
+ sigdp,
+ callback,
+ task_flags);
+
+ if (c_p)
+ erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_MAIN);
+
+ if (sched_res != 0) {
+ if (refp)
+ *refp = NIL;
+ return ERTS_PORT_OP_DROPPED;
+ }
+ return ERTS_PORT_OP_SCHEDULED;
+}
+
+static ERTS_INLINE void
+send_badsig(Port *prt)
+{
+ ErtsProcLocks rp_locks = ERTS_PROC_LOCKS_XSIG_SEND;
+ Process* rp;
+ Eterm connected = ERTS_PORT_GET_CONNECTED(prt);
+
+ ERTS_SMP_CHK_NO_PROC_LOCKS;
+ ERTS_LC_ASSERT(erts_get_scheduler_id());
+
+ ASSERT(is_internal_pid(connected));
+
+ rp = erts_proc_lookup_raw(connected);
+ if (rp) {
+ erts_smp_proc_lock(rp, rp_locks);
+ if (!ERTS_PROC_IS_EXITING(rp))
+ (void) erts_send_exit_signal(NULL,
+ prt->common.id,
+ rp,
+ &rp_locks,
+ am_badsig,
+ NIL,
+ NULL,
+ 0);
+ if (rp_locks)
+ erts_smp_proc_unlock(rp, rp_locks);
+ }
+}
+
+static void
+badsig_received(int bang_op,
+ Port *prt,
+ erts_aint32_t state,
+ int bad_output_value)
+{
+ /*
+ * if (bang_op)
+ * we are part of a "Prt ! Something" operation
+ * else
+ * we are part of a call to a port BIF
+ * behave accordingly...
+ */
+ if (!(state & ERTS_PORT_SFLGS_INVALID_LOOKUP)) {
+ if (bad_output_value) {
+ erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf();
+ erts_dsprintf(dsbufp, "Bad value on output port '%s'\n", prt->name);
+ erts_send_error_to_logger_nogl(dsbufp);
+ }
+ if (bang_op)
+ send_badsig(prt);
+ }
+}
+
+static int
+port_badsig(Port *prt, erts_aint32_t state, int op, ErtsProc2PortSigData *sigdp)
+{
+ if (op == ERTS_PROC2PORT_SIG_EXEC)
+ badsig_received(sigdp->flags & ERTS_P2P_SIG_DATA_FLG_BANG_OP,
+ prt,
+ state,
+ sigdp->flags & ERTS_P2P_SIG_DATA_FLG_BAD_OUTPUT);
+ if (sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY)
+ port_sched_op_reply(sigdp->caller, sigdp->ref, am_badarg);
+ return ERTS_PORT_REDS_BADSIG;
+}
+
+
+/*
+ * bad_port_signal() will
+ * - preserve signal order of signals.
+ * - send a 'badsig' exit signal to connected process if 'from' is an
+ * internal pid and the port is alive when the bad signal reaches
+ * it.
+ */
+static ErtsPortOpResult
+bad_port_signal(Process *c_p,
+ int flags,
+ Port *prt,
+ Eterm from,
+ Eterm *refp,
+ Eterm port_op)
+{
+ ErtsProc2PortSigData *sigdp;
+ ErtsTryImmDrvCallResult try_call_res;
+ ErtsTryImmDrvCallState try_call_state
+ = ERTS_INIT_TRY_IMM_DRV_CALL_STATE(
+ c_p,
+ prt,
+ ERTS_PORT_SFLGS_INVALID_LOOKUP,
+ 0,
+ !refp,
+ port_op);
+
+ try_call_res = try_imm_drv_call(&try_call_state);
+ switch (try_call_res) {
+ case ERTS_TRY_IMM_DRV_CALL_OK:
+ badsig_received(flags & ERTS_PORT_SIG_FLG_BANG_OP,
+ prt,
+ try_call_state.state,
+ flags & ERTS_PORT_SIG_FLG_BAD_OUTPUT);
+ finalize_imm_drv_call(&try_call_state);
+ if (c_p)
+ BUMP_REDS(c_p, ERTS_PORT_REDS_BADSIG);
+ return ERTS_PORT_OP_BADARG;
+ case ERTS_TRY_IMM_DRV_CALL_INVALID_PORT:
+ return ERTS_PORT_OP_DROPPED;
+ case ERTS_TRY_IMM_DRV_CALL_INVALID_SCHED_FLAGS:
+ case ERTS_TRY_IMM_DRV_CALL_BUSY_LOCK:
+ /* Schedule badsig() call instead... */
+ break;
+ }
+
+ sigdp = erts_port_task_alloc_p2p_sig_data();
+ sigdp->flags = (flags & ~ERTS_P2P_SIG_TYPE_MASK) | ERTS_P2P_SIG_TYPE_BAD;
+
+ return erts_schedule_proc2port_signal(c_p,
+ prt,
+ c_p->common.id,
+ refp,
+ sigdp,
+ 0,
+ port_badsig);
+}
+
+
+/*
+ * Driver outputv() callback
+ */
+
+static ERTS_INLINE void
+call_driver_outputv(int bang_op,
+ Eterm caller,
+ Eterm from,
+ Port *prt,
+ erts_driver_t *drv,
+ ErlIOVec *evp)
+{
+ /*
+ * if (bang_op)
+ * we are part of a "Prt ! {From, {command, Data}}" operation
+ * else
+ * we are part of a call to port_command/[2,3]
+ * behave accordingly...
+ */
+ if (bang_op && from != ERTS_PORT_GET_CONNECTED(prt))
+ send_badsig(prt);
+ else {
+ ErlDrvSizeT size = evp->size;
+
+ ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt)
+ || ERTS_IS_CRASH_DUMPING);
+
+#ifdef USE_VM_PROBES
+ if (DTRACE_ENABLED(driver_outputv)) {
+ DTRACE_FORMAT_COMMON_PID_AND_PORT(caller, prt);
+ DTRACE4(driver_outputv, process_str, port_str, prt->name, size);
+ }
+#endif
+
+ prt->caller = caller;
+ (*drv->outputv)((ErlDrvData) prt->drv_data, evp);
+ prt->caller = NIL;
+
+ prt->bytes_out += size;
+ erts_smp_atomic_add_nob(&erts_bytes_out, size);
+ }
+}
+
+static ERTS_INLINE void
+cleanup_scheduled_outputv(ErlIOVec *ev, ErlDrvBinary *cbinp)
+{
+ int i;
+ /* Need to free all binaries */
+ for (i = 1; i < ev->vsize; i++)
+ if (ev->binv[i])
+ driver_free_binary(ev->binv[i]);
+ if (cbinp)
+ driver_free_binary(cbinp);
+ erts_free(ERTS_ALC_T_DRV_CMD_DATA, ev);
+}
+
+static int
+port_sig_outputv(Port *prt, erts_aint32_t state, int op, ErtsProc2PortSigData *sigdp)
+{
+ Eterm reply;
+
+ switch (op) {
+ case ERTS_PROC2PORT_SIG_EXEC:
+ /* Execution of a scheduled outputv() call */
+
+ ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
+
+ if (state & ERTS_PORT_SFLGS_INVALID_LOOKUP)
+ reply = am_badarg;
+ else {
+ call_driver_outputv(sigdp->flags & ERTS_P2P_SIG_DATA_FLG_BANG_OP,
+ sigdp->caller,
+ sigdp->u.outputv.from,
+ prt,
+ prt->drv_ptr,
+ sigdp->u.outputv.evp);
+ reply = am_true;
+ }
+ break;
+ case ERTS_PROC2PORT_SIG_ABORT_NOSUSPEND:
+ reply = am_false;
+ break;
+ default:
+ reply = am_badarg;
+ break;
+ }
+
+ if (sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY)
+ port_sched_op_reply(sigdp->caller, sigdp->ref, reply);
+
+ cleanup_scheduled_outputv(sigdp->u.outputv.evp,
+ sigdp->u.outputv.cbinp);
+
+ return ERTS_PORT_REDS_CMD_OUTPUTV;
+}
+
+/*
+ * Driver output() callback
+ */
+
+static ERTS_INLINE void
+call_driver_output(int bang_op,
+ Eterm caller,
+ Eterm from,
+ Port *prt,
+ erts_driver_t *drv,
+ char *bufp,
+ ErlDrvSizeT size)
+{
+ /*
+ * if (bang_op)
+ * we are part of a "Prt ! {From, {command, Data}}" operation
+ * else
+ * we are part of a call to port_command/[2,3]
+ * behave accordingly...
+ */
+ if (bang_op && from != ERTS_PORT_GET_CONNECTED(prt))
+ send_badsig(prt);
+ else {
+
+ ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt)
+ || ERTS_IS_CRASH_DUMPING);
+
+#ifdef USE_VM_PROBES
+ if (DTRACE_ENABLED(driver_output)) {
+ DTRACE_FORMAT_COMMON_PID_AND_PORT(caller, prt);
+ DTRACE4(driver_output, process_str, port_str, prt->name, size);
+ }
+#endif
+
+ prt->caller = caller;
+ (*drv->output)((ErlDrvData) prt->drv_data, bufp, size);
+ prt->caller = NIL;
+
+ prt->bytes_out += size;
+ erts_smp_atomic_add_nob(&erts_bytes_out, size);
+ }
+}
+
+static ERTS_INLINE void
+cleanup_scheduled_output(char *bufp)
+{
+ erts_free(ERTS_ALC_T_DRV_CMD_DATA, bufp);
+}
+
+static int
+port_sig_output(Port *prt, erts_aint32_t state, int op, ErtsProc2PortSigData *sigdp)
+{
+ Eterm reply;
+
+ switch (op) {
+ case ERTS_PROC2PORT_SIG_EXEC:
+ /* Execution of a scheduled output() call */
+
+ ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
+
+ if (state & ERTS_PORT_SFLGS_INVALID_LOOKUP)
+ reply = am_badarg;
+ else {
+ call_driver_output(sigdp->flags & ERTS_P2P_SIG_DATA_FLG_BANG_OP,
+ sigdp->caller,
+ sigdp->u.output.from,
+ prt,
+ prt->drv_ptr,
+ sigdp->u.output.bufp,
+ sigdp->u.output.size);
+ reply = am_true;
+ }
+ break;
+ case ERTS_PROC2PORT_SIG_ABORT_NOSUSPEND:
+ reply = am_false;
+ break;
+ default:
+ reply = am_badarg;
+ break;
+ }
+
+ if (sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY)
+ port_sched_op_reply(sigdp->caller, sigdp->ref, reply);
+
+ cleanup_scheduled_output(sigdp->u.output.bufp);
+
+ return ERTS_PORT_REDS_CMD_OUTPUT;
+}
+
+ErtsPortOpResult
+erts_port_output(Process *c_p,
+ int flags,
+ Port *prt,
+ Eterm from,
+ Eterm list,
+ Eterm *refp)
+{
+ ErtsPortOpResult res;
+ ErtsProc2PortSigData *sigdp;
+ erts_driver_t *drv = prt->drv_ptr;
+ size_t size;
+ int try_call;
+ erts_aint32_t sched_flags, busy_flgs, invalid_flags;
+ int task_flags;
+ ErtsProc2PortSigCallback port_sig_callback;
+ ErlDrvBinary *cbin = NULL;
+ ErlIOVec *evp = NULL;
+ char *buf = NULL;
+ int force_immediate_call = (flags & ERTS_PORT_SIG_FLG_FORCE_IMM_CALL);
+
+ ASSERT((flags & ~(ERTS_PORT_SIG_FLG_BANG_OP
+ | ERTS_PORT_SIG_FLG_NOSUSPEND
+ | ERTS_PORT_SIG_FLG_FORCE
+ | ERTS_PORT_SIG_FLG_FORCE_IMM_CALL)) == 0);
+
+ busy_flgs = ((flags & ERTS_PORT_SIG_FLG_FORCE)
+ ? ((erts_aint32_t) 0)
+ : ERTS_PTS_FLGS_BUSY);
+ invalid_flags = busy_flgs;
+ if (!refp)
+ invalid_flags |= ERTS_PTS_FLG_PARALLELISM;
+
+ /*
+ * Assumes caller have checked that port is valid...
+ */
+
+ sched_flags = erts_smp_atomic32_read_nob(&prt->sched.flags);
+ if (sched_flags & (busy_flgs|ERTS_PTS_FLG_EXIT))
+ return ((sched_flags & ERTS_PTS_FLG_EXIT)
+ ? ERTS_PORT_OP_DROPPED
+ : ERTS_PORT_OP_BUSY);
+
+ try_call = (force_immediate_call /* crash dumping */
+ || !(sched_flags & (invalid_flags
+ | ERTS_PTS_FLGS_FORCE_SCHEDULE_OP)));
+
+#ifdef USE_VM_PROBES
+ if(DTRACE_ENABLED(port_command)) {
+ DTRACE_FORMAT_COMMON_PID_AND_PORT(c_p ? c_p->common.id : ERTS_INVALID_PID, prt);
+ DTRACE4(port_command, process_str, port_str, prt->name, "command");
+ }
+#endif
+
+ if (drv->outputv) {
+ ErlIOVec ev;
SysIOVec iv[SMALL_WRITE_VEC];
ErlDrvBinary* bv[SMALL_WRITE_VEC];
SysIOVec* ivp;
ErlDrvBinary** bvp;
- ErlDrvBinary* cbin;
- ErlIOVec ev;
+ int vsize;
+ Uint csize;
+ Uint pvsize;
+ Uint pcsize;
+ Uint blimit;
+ size_t iov_offset, binv_offset, alloc_size;
- if (io_list_vec_len(list, &vsize, &csize,
- &pvsize, &pcsize, &size)) {
+ if (io_list_vec_len(list, &vsize, &csize, &pvsize, &pcsize, &size))
goto bad_value;
+
+ iov_offset = ERTS_ALC_DATA_ALIGN_SIZE(sizeof(ErlIOVec));
+ binv_offset = iov_offset;
+ binv_offset += ERTS_ALC_DATA_ALIGN_SIZE((vsize+1)*sizeof(SysIOVec));
+ alloc_size = binv_offset;
+ alloc_size += (vsize+1)*sizeof(ErlDrvBinary *);
+
+ if (try_call && vsize < SMALL_WRITE_VEC) {
+ ivp = ev.iov = iv;
+ bvp = ev.binv = bv;
+ evp = &ev;
+ }
+ else {
+ char *ptr = erts_alloc((try_call
+ ? ERTS_ALC_T_TMP
+ : ERTS_ALC_T_DRV_CMD_DATA), alloc_size);
+
+ evp = (ErlIOVec *) ptr;
+ ivp = evp->iov = (SysIOVec *) (ptr + iov_offset);
+ bvp = evp->binv = (ErlDrvBinary **) (ptr + binv_offset);
}
+
/* To pack or not to pack (small binaries) ...? */
- vsize++;
- if (vsize <= SMALL_WRITE_VEC) {
+ if (vsize < SMALL_WRITE_VEC) {
/* Do NOT pack */
blimit = 0;
- } else {
+ }
+ else {
/* Do pack */
vsize = pvsize + 1;
csize = pcsize;
blimit = ERL_SMALL_IO_BIN_LIMIT;
}
/* Use vsize and csize from now on */
- if (vsize <= SMALL_WRITE_VEC) {
- ivp = iv;
- bvp = bv;
- } else {
- ivp = (SysIOVec *) erts_alloc(ERTS_ALC_T_TMP,
- vsize * sizeof(SysIOVec));
- bvp = (ErlDrvBinary**) erts_alloc(ERTS_ALC_T_TMP,
- vsize * sizeof(ErlDrvBinary*));
- }
+
cbin = driver_alloc_binary(csize);
if (!cbin)
erts_alloc_enomem(ERTS_ALC_T_DRV_BINARY, ERTS_SIZEOF_Binary(csize));
@@ -1178,210 +1897,769 @@ int erts_write_to_port(Eterm caller_id, Port *p, Eterm list)
ivp[0].iov_base = NULL;
ivp[0].iov_len = 0;
bvp[0] = NULL;
- ev.vsize = io_list_to_vec(list, ivp+1, bvp+1, cbin, blimit);
- if (ev.vsize < 0) {
- if (ivp != iv) {
- erts_free(ERTS_ALC_T_TMP, (void *) ivp);
- }
- if (bvp != bv) {
- erts_free(ERTS_ALC_T_TMP, (void *) bvp);
- }
+ evp->vsize = io_list_to_vec(list, ivp+1, bvp+1, cbin, blimit);
+ if (evp->vsize < 0) {
+ if (evp != &ev)
+ erts_free(try_call ? ERTS_ALC_T_TMP : ERTS_ALC_T_DRV_CMD_DATA,
+ evp);
driver_free_binary(cbin);
goto bad_value;
}
- ev.vsize++;
#if 0
/* This assertion may say something useful, but it can
be falsified during the emulator test suites. */
- ASSERT(ev.vsize == vsize);
-#endif
- ev.size = size; /* total size */
- ev.iov = ivp;
- ev.binv = bvp;
-#ifdef USE_VM_PROBES
- if (DTRACE_ENABLED(driver_outputv)) {
- DTRACE_FORMAT_COMMON_PID_AND_PORT(caller_id, p)
- DTRACE4(driver_outputv, process_str, port_str, p->name, size);
- }
+ ASSERT(evp->vsize == vsize);
#endif
- fpe_was_unmasked = erts_block_fpe();
- (*drv->outputv)((ErlDrvData)p->drv_data, &ev);
- erts_unblock_fpe(fpe_was_unmasked);
- if (ivp != iv) {
- erts_free(ERTS_ALC_T_TMP, (void *) ivp);
+ evp->vsize++;
+ evp->size = size; /* total size */
+
+ if (!try_call) {
+ int i;
+ /* Need to increase refc on all binaries */
+ for (i = 1; i < evp->vsize; i++)
+ if (bvp[i])
+ driver_binary_inc_refc(bvp[i]);
}
- if (bvp != bv) {
- erts_free(ERTS_ALC_T_TMP, (void *) bvp);
- }
- driver_free_binary(cbin);
- } else {
- int r;
-
- /* Try with an 8KB buffer first (will often be enough I guess). */
- size = 8*1024;
- /* See below why the extra byte is added. */
- buf = erts_alloc(ERTS_ALC_T_TMP, size+1);
- r = io_list_to_buf(list, buf, size);
+ else {
+ int i;
+ ErlIOVec *new_evp;
+ ErtsTryImmDrvCallResult try_call_res;
+ ErtsTryImmDrvCallState try_call_state
+ = ERTS_INIT_TRY_IMM_DRV_CALL_STATE(
+ c_p,
+ prt,
+ ERTS_PORT_SFLGS_INVALID_LOOKUP,
+ invalid_flags,
+ !refp,
+ am_command);
+
+ try_call_state.pre_chk_sched_flags = 0; /* already checked */
+ if (force_immediate_call)
+ try_call_res = force_imm_drv_call(&try_call_state);
+ else
+ try_call_res = try_imm_drv_call(&try_call_state);
+ switch (try_call_res) {
+ case ERTS_TRY_IMM_DRV_CALL_OK:
+ call_driver_outputv(flags & ERTS_PORT_SIG_FLG_BANG_OP,
+ c_p ? c_p->common.id : ERTS_INVALID_PID,
+ from,
+ prt,
+ drv,
+ evp);
+ if (force_immediate_call)
+ finalize_force_imm_drv_call(&try_call_state);
+ else
+ finalize_imm_drv_call(&try_call_state);
+ /* Fall through... */
+ case ERTS_TRY_IMM_DRV_CALL_INVALID_PORT:
+ driver_free_binary(cbin);
+ if (evp != &ev)
+ erts_free(ERTS_ALC_T_TMP, evp);
+ if (try_call_res != ERTS_TRY_IMM_DRV_CALL_OK)
+ return ERTS_PORT_OP_DROPPED;
+ if (c_p)
+ BUMP_REDS(c_p, ERTS_PORT_REDS_CMD_OUTPUTV);
+ return ERTS_PORT_OP_DONE;
+ case ERTS_TRY_IMM_DRV_CALL_INVALID_SCHED_FLAGS:
+ sched_flags = try_call_state.sched_flags;
+ case ERTS_TRY_IMM_DRV_CALL_BUSY_LOCK:
+ /* Schedule outputv() call instead... */
+ break;
+ }
-#ifdef USE_VM_PROBES
- if(DTRACE_ENABLED(port_command)) {
- DTRACE_FORMAT_COMMON_PID_AND_PORT(caller_id, p)
- DTRACE4(port_command, process_str, port_str, p->name, "command");
- }
+ /* Need to increase refc on all binaries */
+ for (i = 1; i < evp->vsize; i++)
+ if (bvp[i])
+ driver_binary_inc_refc(bvp[i]);
+
+ new_evp = erts_alloc(ERTS_ALC_T_DRV_CMD_DATA, alloc_size);
+
+ if (evp != &ev) {
+ sys_memcpy((void *) new_evp, (void *) evp, alloc_size);
+ new_evp->iov = (SysIOVec *) (((char *) new_evp)
+ + iov_offset);
+ bvp = new_evp->binv = (ErlDrvBinary **) (((char *) new_evp)
+ + binv_offset);
+
+#ifdef DEBUG
+ ASSERT(new_evp->vsize == evp->vsize);
+ ASSERT(new_evp->size == evp->size);
+ for (i = 0; i < evp->vsize; i++) {
+ ASSERT(new_evp->iov[i].iov_len == evp->iov[i].iov_len);
+ ASSERT(new_evp->iov[i].iov_base == evp->iov[i].iov_base);
+ ASSERT(new_evp->binv[i] == evp->binv[i]);
+ }
#endif
- if (r >= 0) {
- size -= r;
-#ifdef USE_VM_PROBES
- if (DTRACE_ENABLED(driver_output)) {
- DTRACE_FORMAT_COMMON_PID_AND_PORT(caller_id, p)
- DTRACE4(driver_output, process_str, port_str, p->name, size);
- }
+ erts_free(ERTS_ALC_T_TMP, evp);
+ }
+ else { /* from stack allocated structure; offsets may differ */
+
+ sys_memcpy((void *) new_evp, (void *) evp, sizeof(ErlIOVec));
+ new_evp->iov = (SysIOVec *) (((char *) new_evp)
+ + iov_offset);
+ sys_memcpy((void *) new_evp->iov,
+ (void *) evp->iov,
+ evp->vsize * sizeof(SysIOVec));
+ new_evp->binv = (ErlDrvBinary **) (((char *) new_evp)
+ + binv_offset);
+ sys_memcpy((void *) new_evp->binv,
+ (void *) evp->binv,
+ evp->vsize * sizeof(ErlDrvBinary *));
+
+#ifdef DEBUG
+ ASSERT(new_evp->vsize == evp->vsize);
+ ASSERT(new_evp->size == evp->size);
+ for (i = 0; i < evp->vsize; i++) {
+ ASSERT(new_evp->iov[i].iov_len == evp->iov[i].iov_len);
+ ASSERT(new_evp->iov[i].iov_base == evp->iov[i].iov_base);
+ ASSERT(new_evp->binv[i] == evp->binv[i]);
+ }
#endif
- fpe_was_unmasked = erts_block_fpe();
- (*drv->output)((ErlDrvData)p->drv_data, buf, size);
- erts_unblock_fpe(fpe_was_unmasked);
- erts_free(ERTS_ALC_T_TMP, buf);
+
+ }
+
+ evp = new_evp;
}
- else if (r == -2) {
- erts_free(ERTS_ALC_T_TMP, buf);
- goto bad_value;
+
+ sigdp = erts_port_task_alloc_p2p_sig_data();
+ sigdp->flags = ERTS_P2P_SIG_TYPE_OUTPUTV;
+ sigdp->u.outputv.from = from;
+ sigdp->u.outputv.evp = evp;
+ sigdp->u.outputv.cbinp = cbin;
+ port_sig_callback = port_sig_outputv;
+ }
+ else {
+ ErlDrvSizeT r;
+
+ /*
+ * Apperently there exist code that write 1 byte to
+ * much in buffer. Where it resides I don't know, but
+ * we can live with one byte extra allocated...
+ */
+
+ if (!try_call) {
+ if (erts_iolist_size(list, &size))
+ goto bad_value;
+
+ buf = erts_alloc(ERTS_ALC_T_DRV_CMD_DATA, size + 1);
+
+ r = erts_iolist_to_buf(list, buf, size);
+ ASSERT(ERTS_IOLIST_TO_BUF_SUCCEEDED(r));
}
else {
- ASSERT(r == -1); /* Overflow */
- erts_free(ERTS_ALC_T_TMP, buf);
- if (erts_iolist_size(list, &size)) {
- goto bad_value;
+ char *new_buf;
+ ErtsTryImmDrvCallResult try_call_res;
+ ErtsTryImmDrvCallState try_call_state
+ = ERTS_INIT_TRY_IMM_DRV_CALL_STATE(
+ c_p,
+ prt,
+ ERTS_PORT_SFLGS_INVALID_LOOKUP,
+ invalid_flags,
+ !refp,
+ am_command);
+
+ /* Try with an 8KB buffer first (will often be enough I guess). */
+ size = 8*1024;
+
+ buf = erts_alloc(ERTS_ALC_T_TMP, size + 1);
+ r = erts_iolist_to_buf(list, buf, size);
+
+ if (ERTS_IOLIST_TO_BUF_SUCCEEDED(r)) {
+ ASSERT(r <= size);
+ size -= r;
+ }
+ else {
+ erts_free(ERTS_ALC_T_TMP, buf);
+ if (r == ERTS_IOLIST_TO_BUF_TYPE_ERROR)
+ goto bad_value;
+ ASSERT(r == ERTS_IOLIST_TO_BUF_OVERFLOW);
+ if (erts_iolist_size(list, &size))
+ goto bad_value;
+ buf = erts_alloc(ERTS_ALC_T_TMP, size + 1);
+ r = erts_iolist_to_buf(list, buf, size);
+ ASSERT(ERTS_IOLIST_TO_BUF_SUCCEEDED(r));
}
- /*
- * I know drivers that pad space with '\0' this is clearly
- * incorrect but I don't feel like fixing them now, insted
- * add ONE extra byte.
- */
- buf = erts_alloc(ERTS_ALC_T_TMP, size+1);
- r = io_list_to_buf(list, buf, size);
-#ifdef USE_VM_PROBES
- if (DTRACE_ENABLED(driver_output)) {
- DTRACE_FORMAT_COMMON_PID_AND_PORT(caller_id, p)
- DTRACE4(driver_output, process_str, port_str, p->name, size);
- }
-#endif
- fpe_was_unmasked = erts_block_fpe();
- (*drv->output)((ErlDrvData)p->drv_data, buf, size);
- erts_unblock_fpe(fpe_was_unmasked);
+ try_call_state.pre_chk_sched_flags = 0; /* already checked */
+ if (force_immediate_call)
+ try_call_res = force_imm_drv_call(&try_call_state);
+ else
+ try_call_res = try_imm_drv_call(&try_call_state);
+ switch (try_call_res) {
+ case ERTS_TRY_IMM_DRV_CALL_OK:
+ call_driver_output(flags & ERTS_PORT_SIG_FLG_BANG_OP,
+ c_p ? c_p->common.id : ERTS_INVALID_PID,
+ from,
+ prt,
+ drv,
+ buf,
+ size);
+ if (force_immediate_call)
+ finalize_force_imm_drv_call(&try_call_state);
+ else
+ finalize_imm_drv_call(&try_call_state);
+ /* Fall through... */
+ case ERTS_TRY_IMM_DRV_CALL_INVALID_PORT:
+ erts_free(ERTS_ALC_T_TMP, buf);
+ if (try_call_res != ERTS_TRY_IMM_DRV_CALL_OK)
+ return ERTS_PORT_OP_DROPPED;
+ if (c_p)
+ BUMP_REDS(c_p, ERTS_PORT_REDS_CMD_OUTPUT);
+ return ERTS_PORT_OP_DONE;
+ case ERTS_TRY_IMM_DRV_CALL_INVALID_SCHED_FLAGS:
+ sched_flags = try_call_state.sched_flags;
+ case ERTS_TRY_IMM_DRV_CALL_BUSY_LOCK:
+ /* Schedule outputv() call instead... */
+ break;
+ }
+
+ new_buf = erts_alloc(ERTS_ALC_T_DRV_CMD_DATA, size + 1);
+ sys_memcpy(new_buf, buf, size);
erts_free(ERTS_ALC_T_TMP, buf);
+ buf = new_buf;
}
+
+ sigdp = erts_port_task_alloc_p2p_sig_data();
+ sigdp->flags = ERTS_P2P_SIG_TYPE_OUTPUT;
+ sigdp->u.output.from = from;
+ sigdp->u.output.bufp = buf;
+ sigdp->u.output.size = size;
+ port_sig_callback = port_sig_output;
+ }
+
+ task_flags = ERTS_PT_FLG_WAIT_BUSY;
+ sigdp->flags |= flags;
+ if (flags & (ERTS_P2P_SIG_DATA_FLG_FORCE|ERTS_P2P_SIG_DATA_FLG_NOSUSPEND)) {
+ task_flags = 0;
+ if (flags & ERTS_P2P_SIG_DATA_FLG_FORCE)
+ sigdp->flags &= ~ERTS_P2P_SIG_DATA_FLG_NOSUSPEND;
+ else if (flags & ERTS_P2P_SIG_DATA_FLG_NOSUSPEND)
+ task_flags = ERTS_PT_FLG_NOSUSPEND;
+ }
+
+ res = erts_schedule_proc2port_signal(c_p,
+ prt,
+ c_p ? c_p->common.id : ERTS_INVALID_PID,
+ refp,
+ sigdp,
+ task_flags,
+ port_sig_callback);
+
+ if (res != ERTS_PORT_OP_SCHEDULED) {
+ if (drv->outputv)
+ cleanup_scheduled_outputv(evp, cbin);
+ else
+ cleanup_scheduled_output(buf);
+ return res;
}
- p->bytes_out += size;
- erts_smp_atomic_add_nob(&erts_bytes_out, size);
-#ifdef ERTS_SMP
- if (p->xports)
- erts_smp_xports_unlock(p);
- ASSERT(!p->xports);
+ if (!(sched_flags & ERTS_PTS_FLG_EXIT) && (sched_flags & busy_flgs))
+ return ERTS_PORT_OP_BUSY_SCHEDULED;
+
+ return res;
+
+bad_value:
+
+ flags |= ERTS_PORT_SIG_FLG_BAD_OUTPUT;
+ return bad_port_signal(c_p, flags, prt, from, refp, am_command);
+}
+
+static ERTS_INLINE ErtsPortOpResult
+call_deliver_port_exit(int bang_op,
+ Eterm from,
+ Port *prt,
+ erts_aint32_t state,
+ Eterm reason,
+ int broken_link)
+{
+ /*
+ * if (bang_op)
+ * we are part of a "Prt ! {From, close}" operation
+ * else
+ * we are part of a call to port_close(Port)
+ * behave accordingly...
+ */
+
+ if (state & ERTS_PORT_SFLGS_INVALID_LOOKUP)
+ return ERTS_PORT_OP_DROPPED;
+
+ if (bang_op && from != ERTS_PORT_GET_CONNECTED(prt)) {
+ send_badsig(prt);
+ return ERTS_PORT_OP_DROPPED;
+ }
+
+ if (broken_link) {
+ ErtsLink *lnk = erts_remove_link(&ERTS_P_LINKS(prt), from);
+ if (lnk)
+ erts_destroy_link(lnk);
+ else
+ return ERTS_PORT_OP_DROPPED;
+ }
+
+ if (!erts_deliver_port_exit(prt, from, reason, bang_op))
+ return ERTS_PORT_OP_DROPPED;
+
+#ifdef USE_VM_PROBES
+ if(DTRACE_ENABLED(port_command) && bang_op) {
+ DTRACE_FORMAT_COMMON_PID_AND_PORT(from, prt);
+ DTRACE4(port_command, process_str, port_str, prt->name, "close");
+ }
#endif
- p->caller = NIL;
- return 0;
- bad_value:
- p->caller = NIL;
- {
- erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf();
- erts_dsprintf(dsbufp, "Bad value on output port '%s'\n", p->name);
- erts_send_error_to_logger_nogl(dsbufp);
- return 1;
+ return ERTS_PORT_OP_DONE;
+}
+
+static int
+port_sig_exit(Port *prt,
+ erts_aint32_t state,
+ int op,
+ ErtsProc2PortSigData *sigdp)
+{
+ Eterm msg = am_badarg;
+ if (op == ERTS_PROC2PORT_SIG_EXEC) {
+ ErtsPortOpResult res;
+ int bang_op = sigdp->flags & ERTS_P2P_SIG_DATA_FLG_BANG_OP;
+ int broken_link = sigdp->flags & ERTS_P2P_SIG_DATA_FLG_BROKEN_LINK;
+ res = call_deliver_port_exit(bang_op,
+ sigdp->u.exit.from,
+ prt,
+ state,
+ sigdp->u.exit.reason,
+ broken_link);
+
+ if (res == ERTS_PORT_OP_DONE)
+ msg = am_true;
+ }
+ if (sigdp->u.exit.bp)
+ free_message_buffer(sigdp->u.exit.bp);
+ if (sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY)
+ port_sched_op_reply(sigdp->caller, sigdp->ref, msg);
+
+ return ERTS_PORT_REDS_EXIT;
+}
+
+ErtsPortOpResult
+erts_port_exit(Process *c_p,
+ int flags,
+ Port *prt,
+ Eterm from,
+ Eterm reason,
+ Eterm *refp)
+{
+ ErtsPortOpResult res;
+ ErtsProc2PortSigData *sigdp;
+ ErlHeapFragment *bp = NULL;
+
+ ASSERT((flags & ~(ERTS_PORT_SIG_FLG_BANG_OP
+ | ERTS_PORT_SIG_FLG_BROKEN_LINK
+ | ERTS_PORT_SIG_FLG_FORCE_SCHED)) == 0);
+
+ if (!(flags & ERTS_PORT_SIG_FLG_FORCE_SCHED)) {
+ ErtsTryImmDrvCallState try_call_state
+ = ERTS_INIT_TRY_IMM_DRV_CALL_STATE(c_p,
+ prt,
+ ERTS_PORT_SFLGS_INVALID_LOOKUP,
+ 0,
+ !refp,
+ am_exit);
+
+
+ switch (try_imm_drv_call(&try_call_state)) {
+ case ERTS_TRY_IMM_DRV_CALL_OK: {
+ res = call_deliver_port_exit(flags & ERTS_PORT_SIG_FLG_BANG_OP,
+ from,
+ prt,
+ try_call_state.state,
+ reason,
+ flags & ERTS_PORT_SIG_FLG_BROKEN_LINK);
+ finalize_imm_drv_call(&try_call_state);
+ if (res == ERTS_PORT_OP_DONE && c_p)
+ BUMP_REDS(c_p, ERTS_PORT_REDS_EXIT);
+ return res;
+ }
+ case ERTS_TRY_IMM_DRV_CALL_INVALID_PORT:
+ return ERTS_PORT_OP_DROPPED;
+ default:
+ /* Schedule call instead... */
+ break;
+ }
}
+
+ sigdp = erts_port_task_alloc_p2p_sig_data();
+ sigdp->flags = ERTS_P2P_SIG_TYPE_EXIT | flags;
+ sigdp->u.exit.from = from;
+
+ if (is_immed(reason)) {
+ sigdp->u.exit.reason = reason;
+ sigdp->u.exit.bp = NULL;
+ }
+ else {
+ Eterm *hp;
+ Uint hsz = size_object(reason);
+ bp = new_message_buffer(hsz);
+ sigdp->u.exit.bp = bp;
+ hp = bp->mem;
+ sigdp->u.exit.reason = copy_struct(reason,
+ hsz,
+ &hp,
+ &bp->off_heap);
+ }
+
+ res = erts_schedule_proc2port_signal(c_p,
+ prt,
+ c_p ? c_p->common.id : from,
+ refp,
+ sigdp,
+ 0,
+ port_sig_exit);
+
+ if (res == ERTS_PORT_OP_DROPPED) {
+ if (bp)
+ free_message_buffer(bp);
+ }
+
+ return res;
}
-/* initialize the port array */
-void init_io(void)
+static ErtsPortOpResult
+set_port_connected(int bang_op,
+ Eterm from,
+ Port *prt,
+ erts_aint32_t state,
+ Eterm connect)
{
- int i;
- ErlDrvEntry** dp;
- char maxports[21]; /* enough for any 64-bit integer */
- size_t maxportssize = sizeof(maxports);
- Uint ports_bits = ERTS_PORTS_BITS;
- Sint port_extra_shift;
+ /*
+ * if (bang_op)
+ * we are part of a "Prt ! {From, {connect, Connect}}" operation
+ * else
+ * we are part of a call to port_connect(Port, Connect)
+ * behave accordingly...
+ */
-#ifdef ERTS_SMP
- init_xports_list_alloc();
+ if (state & ERTS_PORT_SFLGS_INVALID_LOOKUP)
+ return ERTS_PORT_OP_DROPPED;
+
+ if (bang_op) { /* Bang operation */
+ if (is_not_internal_pid(connect) || ERTS_PORT_GET_CONNECTED(prt) != from) {
+ send_badsig(prt);
+ return ERTS_PORT_OP_DROPPED;
+ }
+
+ ERTS_PORT_SET_CONNECTED(prt, connect);
+ deliver_result(prt->common.id, from, am_connected);
+
+#ifdef USE_VM_PROBES
+ if(DTRACE_ENABLED(port_command)) {
+ DTRACE_FORMAT_COMMON_PID_AND_PORT(from, prt);
+ DTRACE4(port_command, process_str, port_str, prt->name, "connect");
+ }
#endif
+ }
+ else { /* Port BIF operation */
+ Process *rp = erts_proc_lookup_raw(connect);
+ if (!rp)
+ return ERTS_PORT_OP_DROPPED;
+ erts_smp_proc_lock(rp, ERTS_PROC_LOCK_LINK);
+ if (ERTS_PROC_IS_EXITING(rp)) {
+ erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
+ return ERTS_PORT_OP_DROPPED;
+ }
- pdl_init();
+ erts_add_link(&ERTS_P_LINKS(rp), LINK_PID, prt->common.id);
+ erts_add_link(&ERTS_P_LINKS(prt), LINK_PID, connect);
+
+ ERTS_PORT_SET_CONNECTED(prt, connect);
+
+ erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
+
+#ifdef USE_VM_PROBES
+ if (DTRACE_ENABLED(port_connect)) {
+ DTRACE_CHARBUF(process_str, DTRACE_TERM_BUF_SIZE);
+ DTRACE_CHARBUF(port_str, DTRACE_TERM_BUF_SIZE);
+ DTRACE_CHARBUF(newprocess_str, DTRACE_TERM_BUF_SIZE);
+
+ dtrace_pid_str(connect, process_str);
+ erts_snprintf(port_str, sizeof(port_str), "%T", prt->common.id);
+ dtrace_proc_str(rp, newprocess_str);
+ DTRACE4(port_connect, process_str, port_str, prt->name, newprocess_str);
+ }
+#endif
+ }
- if (erts_sys_getenv("ERL_MAX_PORTS", maxports, &maxportssize) == 0)
- erts_max_ports = atoi(maxports);
+ return ERTS_PORT_OP_DONE;
+}
+
+static int
+port_sig_connect(Port *prt, erts_aint32_t state, int op, ErtsProc2PortSigData *sigdp)
+{
+ Eterm msg = am_badarg;
+ if (op == ERTS_PROC2PORT_SIG_EXEC) {
+ ErtsPortOpResult res;
+ res = set_port_connected(sigdp->flags & ERTS_P2P_SIG_DATA_FLG_BANG_OP,
+ sigdp->u.connect.from,
+ prt,
+ state,
+ sigdp->u.connect.connected);
+ if (res == ERTS_PORT_OP_DONE)
+ msg = am_true;
+ }
+ if (sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY)
+ port_sched_op_reply(sigdp->caller, sigdp->ref, msg);
+ return ERTS_PORT_REDS_CONNECT;
+}
+
+ErtsPortOpResult
+erts_port_connect(Process *c_p,
+ int flags,
+ Port *prt,
+ Eterm from,
+ Eterm connect,
+ Eterm *refp)
+{
+ ErtsProc2PortSigData *sigdp;
+ Eterm connect_id;
+ ErtsTryImmDrvCallState try_call_state
+ = ERTS_INIT_TRY_IMM_DRV_CALL_STATE(c_p,
+ prt,
+ ERTS_PORT_SFLGS_INVALID_LOOKUP,
+ 0,
+ !refp,
+ am_connect);
+
+ ASSERT((flags & ~ERTS_PORT_SIG_FLG_BANG_OP) == 0);
+
+ if (is_not_internal_pid(connect))
+ connect_id = NIL; /* Fail in op (for signal order) */
else
- erts_max_ports = sys_max_files();
+ connect_id = connect;
+
+ switch (try_imm_drv_call(&try_call_state)) {
+ case ERTS_TRY_IMM_DRV_CALL_OK: {
+ ErtsPortOpResult res;
+ res = set_port_connected(flags & ERTS_PORT_SIG_FLG_BANG_OP,
+ from,
+ prt,
+ try_call_state.state,
+ connect_id);
+ finalize_imm_drv_call(&try_call_state);
+ if (res == ERTS_PORT_OP_DONE)
+ BUMP_REDS(c_p, ERTS_PORT_REDS_CONNECT);
+ return res;
+ }
+ case ERTS_TRY_IMM_DRV_CALL_INVALID_PORT:
+ return ERTS_PORT_OP_DROPPED;
+ default:
+ /* Schedule call instead... */
+ break;
+ }
+
+ sigdp = erts_port_task_alloc_p2p_sig_data();
+ sigdp->flags = ERTS_P2P_SIG_TYPE_CONNECT | flags;
+
+ sigdp->u.connect.from = from;
+ sigdp->u.connect.connected = connect_id;
+
+ return erts_schedule_proc2port_signal(c_p,
+ prt,
+ c_p->common.id,
+ refp,
+ sigdp,
+ 0,
+ port_sig_connect);
+}
- if (erts_max_ports > ERTS_MAX_PORTS)
- erts_max_ports = ERTS_MAX_PORTS;
- if (erts_max_ports < 1024)
- erts_max_ports = 1024;
+static void
+port_unlink(Port *prt, Eterm from)
+{
+ ErtsLink *lnk = erts_remove_link(&ERTS_P_LINKS(prt), from);
+ if (lnk)
+ erts_destroy_link(lnk);
+}
- if (erts_use_r9_pids_ports) {
- ports_bits = ERTS_R9_PORTS_BITS;
- if (erts_max_ports > ERTS_MAX_R9_PORTS)
- erts_max_ports = ERTS_MAX_R9_PORTS;
+static int
+port_sig_unlink(Port *prt, erts_aint32_t state, int op, ErtsProc2PortSigData *sigdp)
+{
+ if (op == ERTS_PROC2PORT_SIG_EXEC)
+ port_unlink(prt, sigdp->u.unlink.from);
+ if (sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY)
+ port_sched_op_reply(sigdp->caller, sigdp->ref, am_true);
+ return ERTS_PORT_REDS_UNLINK;
+}
+
+ErtsPortOpResult
+erts_port_unlink(Process *c_p, Port *prt, Eterm from, Eterm *refp)
+{
+ ErtsProc2PortSigData *sigdp;
+ ErtsTryImmDrvCallState try_call_state
+ = ERTS_INIT_TRY_IMM_DRV_CALL_STATE(c_p,
+ prt,
+ ERTS_PORT_SFLGS_DEAD,
+ 0,
+ !refp,
+ am_unlink);
+
+ switch (try_imm_drv_call(&try_call_state)) {
+ case ERTS_TRY_IMM_DRV_CALL_OK:
+ port_unlink(prt, from);
+ finalize_imm_drv_call(&try_call_state);
+ BUMP_REDS(c_p, ERTS_PORT_REDS_UNLINK);
+ return ERTS_PORT_OP_DONE;
+ case ERTS_TRY_IMM_DRV_CALL_INVALID_PORT:
+ return ERTS_PORT_OP_DROPPED;
+ default:
+ /* Schedule call instead... */
+ break;
}
- port_extra_shift = erts_fit_in_bits_int32(erts_max_ports - 1);
- port_num_mask = (1 << ports_bits) - 1;
+ sigdp = erts_port_task_alloc_p2p_sig_data();
+ sigdp->flags = ERTS_P2P_SIG_TYPE_UNLINK;
+ sigdp->u.unlink.from = from;
+
+ return erts_schedule_proc2port_signal(c_p,
+ prt,
+ c_p ? c_p->common.id : from,
+ refp,
+ sigdp,
+ 0,
+ port_sig_unlink);
+}
- erts_port_tab_index_mask = ~(~((Uint) 0) << port_extra_shift);
- erts_max_ports = 1 << port_extra_shift;
+static void
+port_link_failure(Eterm port_id, Eterm linker)
+{
+ Process *rp;
+ ErtsProcLocks rp_locks = ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCKS_XSIG_SEND;
+ ASSERT(is_internal_pid(linker));
+ rp = erts_pid2proc(NULL, 0, linker, rp_locks);
+ if (rp) {
+ ErtsLink *rlnk = erts_remove_link(&ERTS_P_LINKS(rp), port_id);
+ if (rlnk) {
+ int xres = erts_send_exit_signal(NULL,
+ port_id,
+ rp,
+ &rp_locks,
+ am_noproc,
+ NIL,
+ NULL,
+ 0);
+ if (xres >= 0 && IS_TRACED_FL(rp, F_TRACE_PROCS)) {
+ /* We didn't exit the process and it is traced */
+ if (IS_TRACED_FL(rp, F_TRACE_PROCS))
+ trace_proc(NULL, rp, am_getting_unlinked, port_id);
+ }
+ if (rp_locks)
+ erts_smp_proc_unlock(rp, rp_locks);
+ }
+ }
+}
- erts_smp_mtx_init(&erts_driver_list_lock,"driver_list");
- driver_list = NULL;
- erts_smp_tsd_key_create(&driver_list_lock_status_key);
- erts_smp_tsd_key_create(&driver_list_last_error_key);
+static void
+port_link(Port *prt, erts_aint32_t state, Eterm to)
+{
+ if (!(state & ERTS_PORT_SFLGS_INVALID_LOOKUP))
+ erts_add_link(&ERTS_P_LINKS(prt), LINK_PID, to);
+ else
+ port_link_failure(prt->common.id, to);
+}
- if (erts_max_ports * sizeof(Port) <= erts_max_ports) {
- /* More memory needed than the whole address space. */
- erts_alloc_enomem(ERTS_ALC_T_PORT_TABLE, ~((Uint) 0));
+static int
+port_sig_link(Port *prt, erts_aint32_t state, int op, ErtsProc2PortSigData *sigdp)
+{
+ if (op == ERTS_PROC2PORT_SIG_EXEC)
+ port_link(prt, state, sigdp->u.link.to);
+ else
+ port_link_failure(sigdp->u.link.port, sigdp->u.link.to);
+ if (sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY)
+ port_sched_op_reply(sigdp->caller, sigdp->ref, am_true);
+ return ERTS_PORT_REDS_LINK;
+}
+
+ErtsPortOpResult
+erts_port_link(Process *c_p, Port *prt, Eterm to, Eterm *refp)
+{
+ ErtsProc2PortSigData *sigdp;
+ ErtsTryImmDrvCallState try_call_state
+ = ERTS_INIT_TRY_IMM_DRV_CALL_STATE(c_p,
+ prt,
+ ERTS_PORT_SFLGS_INVALID_LOOKUP,
+ 0,
+ !refp,
+ am_link);
+
+ switch (try_imm_drv_call(&try_call_state)) {
+ case ERTS_TRY_IMM_DRV_CALL_OK:
+ port_link(prt, try_call_state.state, to);
+ finalize_imm_drv_call(&try_call_state);
+ BUMP_REDS(c_p, ERTS_PORT_REDS_LINK);
+ return ERTS_PORT_OP_DONE;
+ case ERTS_TRY_IMM_DRV_CALL_INVALID_PORT:
+ return ERTS_PORT_OP_BADARG;
+ default:
+ /* Schedule call instead... */
+ break;
}
- erts_port = (Port *) erts_alloc(ERTS_ALC_T_PORT_TABLE,
- erts_max_ports * sizeof(Port));
+ sigdp = erts_port_task_alloc_p2p_sig_data();
+ sigdp->flags = ERTS_P2P_SIG_TYPE_LINK;
+ sigdp->u.link.port = prt->common.id;
+ sigdp->u.link.to = to;
+
+ return erts_schedule_proc2port_signal(c_p,
+ prt,
+ c_p ? c_p->common.id : to,
+ refp,
+ sigdp,
+ 0,
+ port_sig_link);
+}
- erts_smp_atomic_init_nob(&erts_bytes_out, 0);
- erts_smp_atomic_init_nob(&erts_bytes_in, 0);
+void erts_init_io(int port_tab_size,
+ int port_tab_size_ignore_files)
+{
+ ErlDrvEntry** dp;
+ erts_smp_rwmtx_opt_t drv_list_rwmtx_opts = ERTS_SMP_RWMTX_OPT_DEFAULT_INITER;
+ drv_list_rwmtx_opts.type = ERTS_SMP_RWMTX_TYPE_EXTREMELY_FREQUENT_READ;
+ drv_list_rwmtx_opts.lived = ERTS_SMP_RWMTX_LONG_LIVED;
- for (i = 0; i < erts_max_ports; i++) {
- erts_port_task_init_sched(&erts_port[i].sched);
- erts_smp_atomic_init_nob(&erts_port[i].refc, 0);
#ifdef ERTS_SMP
- erts_port[i].lock = NULL;
- erts_port[i].xports = NULL;
- erts_smp_spinlock_init_x(&erts_port[i].state_lck,
-#ifdef ERTS_ENABLE_LOCK_COUNT
- (erts_lcnt_rt_options & ERTS_LCNT_OPT_PORTLOCK) ? "port_state" : NULL,
-#else
- "port_state",
-#endif
- make_small(0));
+ init_xports_list_alloc();
#endif
- erts_port[i].tracer_proc = NIL;
- erts_port[i].trace_flags = 0;
- erts_port[i].drv_ptr = NULL;
- erts_port[i].status = ERTS_PORT_SFLG_FREE;
- erts_port[i].name = NULL;
- erts_port[i].nlinks = NULL;
- erts_port[i].monitors = NULL;
- erts_port[i].linebuf = NULL;
- erts_port[i].port_data_lock = NULL;
+ pdl_init();
+
+ if (!port_tab_size_ignore_files) {
+ int max_files = sys_max_files();
+ if (port_tab_size < max_files)
+ port_tab_size = max_files;
}
- erts_smp_atomic32_init_nob(&erts_ports_snapshot, (erts_aint32_t) 0);
- last_port_num = 0;
- erts_smp_spinlock_init(&get_free_port_lck, "get_free_port");
+ if (port_tab_size > ERTS_MAX_PORTS)
+ port_tab_size = ERTS_MAX_PORTS;
+ else if (port_tab_size < ERTS_MIN_PORTS)
+ port_tab_size = ERTS_MIN_PORTS;
+
+ erts_smp_rwmtx_init_opt(&erts_driver_list_lock,
+ &drv_list_rwmtx_opts,
+ "driver_list");
+ driver_list = NULL;
+ erts_smp_tsd_key_create(&driver_list_lock_status_key);
+ erts_smp_tsd_key_create(&driver_list_last_error_key);
+
+ erts_ptab_init_table(&erts_port,
+ ERTS_ALC_T_PORT_TABLE,
+ NULL,
+ (ErtsPTabElementCommon *) &erts_invalid_port.common,
+ port_tab_size,
+ "port_table");
+
+ erts_smp_atomic_init_nob(&erts_bytes_out, 0);
+ erts_smp_atomic_init_nob(&erts_bytes_in, 0);
sys_init_io();
erts_smp_tsd_set(driver_list_lock_status_key, (void *) 1);
- erts_smp_mtx_lock(&erts_driver_list_lock);
+ erts_smp_rwmtx_rwlock(&erts_driver_list_lock);
init_driver(&fd_driver, &fd_driver_entry, NULL);
init_driver(&vanilla_driver, &vanilla_driver_entry, NULL);
@@ -1390,28 +2668,67 @@ void init_io(void)
erts_add_driver_entry(*dp, NULL, 1);
erts_smp_tsd_set(driver_list_lock_status_key, NULL);
- erts_smp_mtx_unlock(&erts_driver_list_lock);
+ erts_smp_rwmtx_rwunlock(&erts_driver_list_lock);
}
#if defined(ERTS_ENABLE_LOCK_COUNT) && defined(ERTS_SMP)
-void erts_lcnt_enable_io_lock_count(int enable) {
- int i;
- for (i = 0; i < erts_max_ports; i++) {
- Port* p = &erts_port[i];
- if (enable) {
- erts_lcnt_init_lock_x(&p->state_lck.lcnt, "port_state", ERTS_LCNT_LT_SPINLOCK, make_small(i));
- if (p->lock) {
- erts_lcnt_init_lock_x(&p->lock->lcnt, "port_lock", ERTS_LCNT_LT_MUTEX, make_small(i));
- }
- } else {
- erts_lcnt_destroy_lock(&p->state_lck.lcnt);
- if (p->lock) {
- erts_lcnt_destroy_lock(&p->lock->lcnt);
- }
- }
+static ERTS_INLINE void lcnt_enable_drv_lock_count(erts_driver_t *dp, int enable)
+{
+ if (dp->lock) {
+ if (enable)
+ erts_lcnt_init_lock_x(&dp->lock->lcnt,
+ "driver_lock",
+ ERTS_LCNT_LT_MUTEX,
+ erts_atom_put((byte*)dp->name,
+ sys_strlen(dp->name),
+ ERTS_ATOM_ENC_LATIN1,
+ 1));
+
+ else
+ erts_lcnt_destroy_lock(&dp->lock->lcnt);
+
}
}
+
+static ERTS_INLINE void lcnt_enable_port_lock_count(Port *prt, int enable)
+{
+ erts_aint32_t state = erts_atomic32_read_nob(&prt->state);
+ if (!enable) {
+ erts_lcnt_destroy_lock(&prt->sched.mtx.lcnt);
+ if (state & ERTS_PORT_SFLG_PORT_SPECIFIC_LOCK)
+ erts_lcnt_destroy_lock(&prt->lock->lcnt);
+ }
+ else {
+ erts_lcnt_init_lock_x(&prt->sched.mtx.lcnt,
+ "port_sched_lock",
+ ERTS_LCNT_LT_MUTEX,
+ prt->common.id);
+ if (state & ERTS_PORT_SFLG_PORT_SPECIFIC_LOCK)
+ erts_lcnt_init_lock_x(&prt->lock->lcnt,
+ "port_lock",
+ ERTS_LCNT_LT_MUTEX,
+ prt->common.id);
+ }
+}
+
+void erts_lcnt_enable_io_lock_count(int enable)
+{
+ erts_driver_t *dp;
+ int i, max = erts_ptab_max(&erts_port);
+
+ for (i = 0; i < max; i++) {
+ Port *prt = erts_pix2port(i);
+ if (prt)
+ lcnt_enable_port_lock_count(prt, enable);
+ }
+
+ lcnt_enable_drv_lock_count(&vanilla_driver, enable);
+ lcnt_enable_drv_lock_count(&spawn_driver, enable);
+ lcnt_enable_drv_lock_count(&fd_driver, enable);
+ for (dp = driver_list; dp; dp = dp->next)
+ lcnt_enable_drv_lock_count(dp, enable);
+}
#endif
/*
@@ -1594,9 +2911,7 @@ deliver_result(Eterm sender, Eterm pid, Eterm res)
ERTS_SMP_CHK_NO_PROC_LOCKS;
- ASSERT(is_internal_port(sender)
- && is_internal_pid(pid)
- && internal_pid_index(pid) < erts_max_processes);
+ ASSERT(is_internal_port(sender) && is_internal_pid(pid));
rp = (scheduler
? erts_proc_lookup(pid)
@@ -1608,16 +2923,19 @@ deliver_result(Eterm sender, Eterm pid, Eterm res)
ErlOffHeap *ohp;
Eterm* hp;
Uint sz_res;
- sz_res = size_object(res);
- hp = erts_alloc_message_heap(sz_res + 3, &bp, &ohp, rp, &rp_locks);
- res = copy_struct(res, sz_res, &hp, ohp);
- tuple = TUPLE2(hp, sender, res);
+
+ sz_res = size_object(res);
+ hp = erts_alloc_message_heap(sz_res + 3, &bp, &ohp, rp, &rp_locks);
+ res = copy_struct(res, sz_res, &hp, ohp);
+ tuple = TUPLE2(hp, sender, res);
erts_queue_message(rp, &rp_locks, bp, tuple, NIL
#ifdef USE_VM_PROBES
, NIL
#endif
);
- erts_smp_proc_unlock(rp, rp_locks);
+
+ if (rp_locks)
+ erts_smp_proc_unlock(rp, rp_locks);
if (!scheduler)
erts_smp_proc_dec_refc(rp);
@@ -1633,7 +2951,7 @@ deliver_result(Eterm sender, Eterm pid, Eterm res)
* len -- length of data
*/
-static void deliver_read_message(Port* prt, Eterm to,
+static void deliver_read_message(Port* prt, erts_aint32_t state, Eterm to,
char *hbuf, ErlDrvSizeT hlen,
char *buf, ErlDrvSizeT len, int eol)
{
@@ -1651,10 +2969,11 @@ static void deliver_read_message(Port* prt, Eterm to,
ERTS_SMP_CHK_NO_PROC_LOCKS;
need = 3 + 3 + 2*hlen;
- if (prt->status & ERTS_PORT_SFLG_LINEBUF_IO) {
+
+ if (state & ERTS_PORT_SFLG_LINEBUF_IO) {
need += 3;
}
- if (prt->status & ERTS_PORT_SFLG_BINARY_IO && buf != NULL) {
+ if ((state & ERTS_PORT_SFLG_BINARY_IO) && buf != NULL) {
need += PROC_BIN_SIZE;
} else {
need += 2*len;
@@ -1670,7 +2989,7 @@ static void deliver_read_message(Port* prt, Eterm to,
hp = erts_alloc_message_heap(need, &bp, &ohp, rp, &rp_locks);
listp = NIL;
- if ((prt->status & ERTS_PORT_SFLG_BINARY_IO) == 0) {
+ if ((state & ERTS_PORT_SFLG_BINARY_IO) == 0) {
listp = buf_to_intlist(&hp, buf, len, listp);
} else if (buf != NULL) {
ProcBin* pb;
@@ -1701,14 +3020,14 @@ static void deliver_read_message(Port* prt, Eterm to,
listp = buf_to_intlist(&hp, hbuf, hlen, listp);
}
- if (prt->status & ERTS_PORT_SFLG_LINEBUF_IO){
+ if (state & ERTS_PORT_SFLG_LINEBUF_IO){
listp = TUPLE2(hp, (eol) ? am_eol : am_noeol, listp);
hp += 3;
}
tuple = TUPLE2(hp, am_data, listp);
hp += 3;
- tuple = TUPLE2(hp, prt->id, tuple);
+ tuple = TUPLE2(hp, prt->common.id, tuple);
hp += 3;
erts_queue_message(rp, &rp_locks, bp, tuple, am_undefined
@@ -1726,7 +3045,8 @@ static void deliver_read_message(Port* prt, Eterm to,
* Deliver all lines in a line buffer, repeats calls to
* deliver_read_message, and takes the same parameters.
*/
-static void deliver_linebuf_message(Port* prt, Eterm to,
+static void deliver_linebuf_message(Port* prt, erts_aint_t state,
+ Eterm to,
char* hbuf, ErlDrvSizeT hlen,
char *buf, ErlDrvSizeT len)
{
@@ -1735,7 +3055,7 @@ static void deliver_linebuf_message(Port* prt, Eterm to,
if(init_linebuf_context(&lc,&(prt->linebuf), buf, len) < 0)
return;
while((ret = read_linebuf(&lc)) > LINEBUF_EMPTY)
- deliver_read_message(prt, to, hbuf, hlen, LINEBUF_DATA(lc),
+ deliver_read_message(prt, state, to, hbuf, hlen, LINEBUF_DATA(lc),
LINEBUF_DATALEN(lc), (ret == LINEBUF_EOL));
}
@@ -1746,20 +3066,25 @@ static void deliver_linebuf_message(Port* prt, Eterm to,
* Parameters:
* prt - Pointer to a Port structure for this port.
*/
-static void flush_linebuf_messages(Port *prt)
+static void flush_linebuf_messages(Port *prt, erts_aint32_t state)
{
LineBufContext lc;
int ret;
ERTS_SMP_LC_ASSERT(!prt || erts_lc_is_port_locked(prt));
- if(prt == NULL || !(prt->status & ERTS_PORT_SFLG_LINEBUF_IO))
+
+ if (!prt)
+ return;
+
+ if (!(state & ERTS_PORT_SFLG_LINEBUF_IO))
return;
if(init_linebuf_context(&lc,&(prt->linebuf), NULL, 0) < 0)
return;
while((ret = flush_linebuf(&lc)) > LINEBUF_EMPTY)
deliver_read_message(prt,
- prt->connected,
+ state,
+ ERTS_PORT_GET_CONNECTED(prt),
NULL,
0,
LINEBUF_DATA(lc),
@@ -1787,6 +3112,7 @@ deliver_vec_message(Port* prt, /* Port */
ErlOffHeap *ohp;
ErtsProcLocks rp_locks = 0;
int scheduler = erts_get_scheduler_id() != 0;
+ erts_aint32_t state;
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
ERTS_SMP_CHK_NO_PROC_LOCKS;
@@ -1802,12 +3128,13 @@ deliver_vec_message(Port* prt, /* Port */
if (!rp)
return;
+ state = erts_atomic32_read_nob(&prt->state);
/*
* Calculate the exact number of heap words needed.
*/
need = 3 + 3; /* Heap space for two tuples */
- if (prt->status & ERTS_PORT_SFLG_BINARY_IO) {
+ if (state & ERTS_PORT_SFLG_BINARY_IO) {
need += (2+PROC_BIN_SIZE)*vsize - 2 + hlen*2;
} else {
need += (hlen+csize)*2;
@@ -1818,7 +3145,7 @@ deliver_vec_message(Port* prt, /* Port */
listp = NIL;
iov += vsize;
- if ((prt->status & ERTS_PORT_SFLG_BINARY_IO) == 0) {
+ if ((state & ERTS_PORT_SFLG_BINARY_IO) == 0) {
Eterm* thp = hp;
while (vsize--) {
iov--;
@@ -1871,7 +3198,7 @@ deliver_vec_message(Port* prt, /* Port */
tuple = TUPLE2(hp, am_data, listp);
hp += 3;
- tuple = TUPLE2(hp, prt->id, tuple);
+ tuple = TUPLE2(hp, prt->common.id, tuple);
hp += 3;
erts_queue_message(rp, &rp_locks, bp, tuple, am_undefined
@@ -1904,7 +3231,7 @@ static void deliver_bin_message(Port* prt, /* port */
/*
* Note.
*
- * The test for (p->status & ERTS_PORT_SFLGS_DEAD) == 0 is important since the
+ * The test for ERTS_PORT_SFLGS_DEAD is important since the
* driver's flush function might call driver_async, which when using no
* threads and being short circuited will notice that the io queue is empty
* (after calling the driver's async_ready) and recursively call
@@ -1920,7 +3247,7 @@ static void flush_port(Port *p)
if (p->drv_ptr->flush != NULL) {
#ifdef USE_VM_PROBES
if (DTRACE_ENABLED(driver_flush)) {
- DTRACE_FORMAT_COMMON_PID_AND_PORT(p->connected, p)
+ DTRACE_FORMAT_COMMON_PID_AND_PORT(ERTS_PORT_GET_CONNECTED(p), p)
DTRACE3(driver_flush, process_str, port_str, p->name);
}
#endif
@@ -1935,11 +3262,12 @@ static void flush_port(Port *p)
}
#ifdef ERTS_SMP
if (p->xports)
- erts_smp_xports_unlock(p);
+ erts_port_handle_xports(p);
ASSERT(!p->xports);
#endif
}
- if ((p->status & ERTS_PORT_SFLGS_DEAD) == 0 && is_port_ioq_empty(p)) {
+ if ((erts_atomic32_read_nob(&p->state) & ERTS_PORT_SFLGS_DEAD) == 0
+ && is_port_ioq_empty(p)) {
terminate_port(p);
}
}
@@ -1951,29 +3279,29 @@ terminate_port(Port *prt)
Eterm send_closed_port_id;
Eterm connected_id = NIL /* Initialize to silence compiler */;
erts_driver_t *drv;
- int halt;
+ erts_aint32_t state;
ERTS_SMP_CHK_NO_PROC_LOCKS;
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
- ASSERT(!prt->nlinks);
- ASSERT(!prt->monitors);
+ ASSERT(!ERTS_P_LINKS(prt));
+ ASSERT(!ERTS_P_MONITORS(prt));
- /* prt->status may be altered by kill_port()below */
- halt = (prt->status & ERTS_PORT_SFLG_HALT) != 0;
- if (prt->status & ERTS_PORT_SFLG_SEND_CLOSED) {
- erts_port_status_band_set(prt, ~ERTS_PORT_SFLG_SEND_CLOSED);
- send_closed_port_id = prt->id;
- connected_id = prt->connected;
+ /* state may be altered by kill_port() below */
+ state = erts_atomic32_read_band_nob(&prt->state,
+ ~ERTS_PORT_SFLG_SEND_CLOSED);
+ if (state & ERTS_PORT_SFLG_SEND_CLOSED) {
+ send_closed_port_id = prt->common.id;
+ connected_id = ERTS_PORT_GET_CONNECTED(prt);
}
else {
send_closed_port_id = NIL;
}
#ifdef ERTS_SMP
- erts_cancel_smp_ptimer(prt->ptimer);
+ erts_cancel_smp_ptimer(prt->common.u.alive.ptimer);
#else
- erts_cancel_timer(&prt->tm);
+ erts_cancel_timer(&prt->common.u.alive.tm);
#endif
drv = prt->drv_ptr;
@@ -1981,7 +3309,7 @@ terminate_port(Port *prt)
int fpe_was_unmasked = erts_block_fpe();
#ifdef USE_VM_PROBES
if (DTRACE_ENABLED(driver_stop)) {
- DTRACE_FORMAT_COMMON_PID_AND_PORT(prt->connected, prt)
+ DTRACE_FORMAT_COMMON_PID_AND_PORT(connected_id, prt)
DTRACE3(driver_stop, process_str, drv->name, port_str);
}
#endif
@@ -1989,14 +3317,14 @@ terminate_port(Port *prt)
erts_unblock_fpe(fpe_was_unmasked);
#ifdef ERTS_SMP
if (prt->xports)
- erts_smp_xports_unlock(prt);
+ erts_port_handle_xports(prt);
ASSERT(!prt->xports);
#endif
}
if(drv->handle != NULL) {
- erts_smp_mtx_lock(&erts_driver_list_lock);
+ erts_smp_rwmtx_rlock(&erts_driver_list_lock);
erts_ddll_decrement_port_count(drv->handle);
- erts_smp_mtx_unlock(&erts_driver_list_lock);
+ erts_smp_rwmtx_runlock(&erts_driver_list_lock);
}
stopq(prt); /* clear queue memory */
if(prt->linebuf != NULL){
@@ -2012,20 +3340,21 @@ terminate_port(Port *prt)
if (prt->psd)
erts_free(ERTS_ALC_T_PRTSD, prt->psd);
+ ASSERT(prt->dist_entry == NULL);
+
kill_port(prt);
/*
* We don't want to send the closed message until after the
* port has been removed from the port table (in kill_port()).
*/
- if (halt && (erts_smp_atomic32_dec_read_nob(&erts_halt_progress) == 0)) {
- erts_smp_port_unlock(prt); /* We will exit and never return */
+ if ((state & ERTS_PORT_SFLG_HALT)
+ && (erts_smp_atomic32_dec_read_nob(&erts_halt_progress) == 0)) {
+ erts_port_release(prt); /* We will exit and never return */
erl_exit_flush_async(erts_halt_code, "");
}
if (is_internal_port(send_closed_port_id))
deliver_result(send_closed_port_id, connected_id, am_closed);
-
- ASSERT(prt->dist_entry == NULL);
}
void
@@ -2045,7 +3374,7 @@ static void sweep_one_monitor(ErtsMonitor *mon, void *vpsc)
if (!rp) {
goto done;
}
- rmon = erts_remove_monitor(&(rp->monitors),mon->ref);
+ rmon = erts_remove_monitor(&ERTS_P_MONITORS(rp), mon->ref);
erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
if (rmon == NULL) {
goto done;
@@ -2099,7 +3428,7 @@ static void sweep_one_link(ErtsLink *lnk, void *vpsc)
ASSERT(is_internal_pid(lnk->pid));
rp = erts_pid2proc(NULL, 0, lnk->pid, rp_locks);
if (rp) {
- ErtsLink *rlnk = erts_remove_link(&(rp->nlinks), psc->port);
+ ErtsLink *rlnk = erts_remove_link(&ERTS_P_LINKS(rp), psc->port);
if (rlnk) {
int xres = erts_send_exit_signal(NULL,
@@ -2135,11 +3464,13 @@ static void sweep_one_link(ErtsLink *lnk, void *vpsc)
* that is to kill a port till reason kill. Then the port is stopped.
*
*/
-void
-erts_do_exit_port(Port *p, Eterm from, Eterm reason)
+
+int
+erts_deliver_port_exit(Port *p, Eterm from, Eterm reason, int send_closed)
{
ErtsLink *lnk;
Eterm rreason;
+ erts_aint32_t state;
ERTS_SMP_CHK_NO_PROC_LOCKS;
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(p));
@@ -2159,66 +3490,76 @@ erts_do_exit_port(Port *p, Eterm from, Eterm reason)
}
#endif
- if ((p->status & (ERTS_PORT_SFLGS_DEAD
- | ERTS_PORT_SFLG_EXITING
- | ERTS_PORT_SFLG_IMMORTAL))
- || ((reason == am_normal) &&
- ((from != p->connected) && (from != p->id)))) {
- return;
- }
+ state = erts_atomic32_read_nob(&p->state);
+ if (state & (ERTS_PORT_SFLGS_DEAD
+ | ERTS_PORT_SFLG_EXITING
+ | ERTS_PORT_SFLG_CLOSING))
+ return 0;
+
+ if (reason == am_normal && from != ERTS_PORT_GET_CONNECTED(p) && from != p->common.id)
+ return 0;
+
+ if (send_closed)
+ erts_atomic32_read_bor_relb(&p->state,
+ ERTS_PORT_SFLG_SEND_CLOSED);
if (IS_TRACED_FL(p, F_TRACE_PORTS)) {
trace_port(p, am_closed, reason);
}
- erts_trace_check_exiting(p->id);
+ erts_trace_check_exiting(p->common.id);
/*
* Setting the port to not busy here, frees the list of pending
* processes and makes them runnable.
*/
- set_busy_port((ErlDrvPort)internal_port_index(p->id), 0);
+ set_busy_port((ErlDrvPort) p, 0);
- if (p->reg != NULL)
- (void) erts_unregister_name(NULL, 0, p, p->reg->name);
+ if (p->common.u.alive.reg != NULL)
+ (void) erts_unregister_name(NULL, 0, p, p->common.u.alive.reg->name);
- erts_port_status_bor_set(p, ERTS_PORT_SFLG_EXITING);
+ state = erts_atomic32_read_bor_relb(&p->state, ERTS_PORT_SFLG_EXITING);
{
- SweepContext sc = {p->id, rreason};
- lnk = p->nlinks;
- p->nlinks = NULL;
+ SweepContext sc = {p->common.id, rreason};
+ lnk = ERTS_P_LINKS(p);
+ ERTS_P_LINKS(p) = NULL;
erts_sweep_links(lnk, &sweep_one_link, &sc);
}
DRV_MONITOR_LOCK_PDL(p);
{
- ErtsMonitor *moni = p->monitors;
- p->monitors = NULL;
+ ErtsMonitor *moni = ERTS_P_MONITORS(p);
+ ERTS_P_MONITORS(p) = NULL;
erts_sweep_monitors(moni, &sweep_one_monitor, NULL);
}
DRV_MONITOR_UNLOCK_PDL(p);
- if ((p->status & ERTS_PORT_SFLG_DISTRIBUTION) && p->dist_entry) {
+ if ((state & ERTS_PORT_SFLG_DISTRIBUTION) && p->dist_entry) {
erts_do_net_exits(p->dist_entry, rreason);
erts_deref_dist_entry(p->dist_entry);
- p->dist_entry = NULL;
- erts_port_status_band_set(p, ~ERTS_PORT_SFLG_DISTRIBUTION);
+ p->dist_entry = NULL;
+ erts_atomic32_read_band_relb(&p->state,
+ ~ERTS_PORT_SFLG_DISTRIBUTION);
}
if ((reason != am_kill) && !is_port_ioq_empty(p)) {
- erts_port_status_bandor_set(p,
- ~ERTS_PORT_SFLG_EXITING, /* must turn it off */
- ERTS_PORT_SFLG_CLOSING);
+ /* must turn exiting flag off */
+ erts_atomic32_read_bset_relb(&p->state,
+ (ERTS_PORT_SFLG_EXITING
+ | ERTS_PORT_SFLG_CLOSING),
+ ERTS_PORT_SFLG_CLOSING);
flush_port(p);
}
else {
terminate_port(p);
}
+
+ return 1;
}
/* About the states ERTS_PORT_SFLG_EXITING and ERTS_PORT_SFLG_CLOSING used above.
**
-** ERTS_PORT_SFLG_EXITING is a recursion protection for erts_do_exit_port().
+** ERTS_PORT_SFLG_EXITING is a recursion protection for erts_deliver_port_exit().
** It is unclear whether this state is necessary or not, it might be possible
** to merge it with ERTS_PORT_SFLG_CLOSING. ERTS_PORT_SFLG_EXITING only persists
** over a section of sequential (but highly recursive) code.
@@ -2234,232 +3575,1113 @@ erts_do_exit_port(Port *p, Eterm from, Eterm reason)
** {PID, close}
** {PID, {command, io-list}}
** {PID, {connect, New_PID}}
-**
-**
*/
-void erts_port_command(Process *proc,
- Eterm caller_id,
- Port *port,
- Eterm command)
+ErtsPortOpResult
+erts_port_command(Process *c_p,
+ int flags,
+ Port *port,
+ Eterm command,
+ Eterm *refp)
{
Eterm *tp;
- Eterm pid;
- if (!port)
- return;
+ ASSERT(port);
- erts_smp_proc_unlock(proc, ERTS_PROC_LOCK_MAIN);
- ERTS_SMP_CHK_NO_PROC_LOCKS;
- ASSERT(!INVALID_PORT(port, port->id));
+ flags |= ERTS_PORT_SIG_FLG_BANG_OP;
if (is_tuple_arity(command, 2)) {
+ Eterm cntd;
tp = tuple_val(command);
- if ((pid = port->connected) == tp[1]) {
- /* PID must be connected */
+ cntd = tp[1];
+ if (is_internal_pid(cntd)) {
if (tp[2] == am_close) {
- erts_port_status_bor_set(port, ERTS_PORT_SFLG_SEND_CLOSED);
- erts_do_exit_port(port, pid, am_normal);
-
-#ifdef USE_VM_PROBES
- if(DTRACE_ENABLED(port_command)) {
- DTRACE_FORMAT_COMMON_PROC_AND_PORT(proc, port)
- DTRACE4(port_command, process_str, port_str, port->name, "close");
- }
-#endif
- goto done;
+ if (!erts_port_synchronous_ops)
+ refp = NULL;
+ flags &= ~ERTS_PORT_SIG_FLG_NOSUSPEND;
+ return erts_port_exit(c_p, flags, port, cntd, am_normal, refp);
} else if (is_tuple_arity(tp[2], 2)) {
tp = tuple_val(tp[2]);
if (tp[1] == am_command) {
- if (erts_write_to_port(caller_id, port, tp[2]) == 0)
- goto done;
- } else if ((tp[1] == am_connect) && is_internal_pid(tp[2])) {
-#ifdef USE_VM_PROBES
- if(DTRACE_ENABLED(port_command)) {
- DTRACE_FORMAT_COMMON_PROC_AND_PORT(proc, port)
- DTRACE4(port_command, process_str, port_str, port->name, "connect");
- }
-#endif
- port->connected = tp[2];
- deliver_result(port->id, pid, am_connected);
- goto done;
+ if (!(flags & ERTS_PORT_SIG_FLG_NOSUSPEND)
+ && !erts_port_synchronous_ops)
+ refp = NULL;
+ return erts_port_output(c_p, flags, port, cntd, tp[2], refp);
+ }
+ else if (tp[1] == am_connect) {
+ if (!erts_port_synchronous_ops)
+ refp = NULL;
+ flags &= ~ERTS_PORT_SIG_FLG_NOSUSPEND;
+ return erts_port_connect(c_p, flags, port, cntd, tp[2], refp);
}
}
}
}
- {
- ErtsProcLocks rp_locks = ERTS_PROC_LOCKS_XSIG_SEND;
- Process* rp = erts_pid2proc(NULL, 0,
- port->connected, rp_locks);
- if (rp) {
- (void) erts_send_exit_signal(NULL,
- port->id,
- rp,
- &rp_locks,
- am_badsig,
- NIL,
- NULL,
- 0);
- erts_smp_proc_unlock(rp, rp_locks);
- }
+ /* badsig */
+ if (!erts_port_synchronous_ops)
+ refp = NULL;
+ flags &= ~ERTS_PORT_SIG_FLG_NOSUSPEND;
+ return bad_port_signal(c_p, flags, port, c_p->common.id, refp, am_command);
+}
+
+static ERTS_INLINE ErtsPortOpResult
+call_driver_control(Eterm caller,
+ Port *prt,
+ unsigned int command,
+ char *bufp,
+ ErlDrvSizeT size,
+ char **resp_bufp,
+ ErlDrvSizeT *from_size)
+{
+ ErlDrvSSizeT cres;
+
+ if (!prt->drv_ptr->control)
+ return ERTS_PORT_OP_BADARG;
+
+#ifdef USE_VM_PROBES
+ if (DTRACE_ENABLED(port_control) || DTRACE_ENABLED(driver_control)) {
+ DTRACE_FORMAT_COMMON_PID_AND_PORT(caller, prt);
+ DTRACE4(port_control, process_str, port_str, prt->name, command);
+ DTRACE5(driver_control, process_str, port_str, prt->name,
+ command, size);
}
- done:
- erts_smp_proc_lock(proc, ERTS_PROC_LOCK_MAIN);
+#endif
+
+ prt->caller = caller;
+ cres = prt->drv_ptr->control((ErlDrvData) prt->drv_data,
+ command,
+ bufp,
+ size,
+ resp_bufp,
+ *from_size);
+ prt->caller = NIL;
+
+ if (cres < 0)
+ return ERTS_PORT_OP_BADARG;
+
+ *from_size = (ErlDrvSizeT) cres;
+
+ return ERTS_PORT_OP_DONE;
}
-/*
- * Control a port synchronously.
- * Returns either a list or a binary.
- */
-Eterm
-erts_port_control(Process* p, Port* prt, Uint command, Eterm iolist)
-{
- byte* to_port = NULL; /* Buffer to write to port. */
- /* Initialization is for shutting up
- warning about use before set. */
- Uint to_len = 0; /* Length of buffer. */
- int must_free = 0; /* True if the buffer should be freed. */
- char port_result[ERL_ONHEAP_BIN_LIMIT]; /* Default buffer for result from port. */
- char* port_resp; /* Pointer to result buffer. */
- ErlDrvSSizeT n;
- ErlDrvSSizeT (*control)
- (ErlDrvData, unsigned, char*, ErlDrvSizeT, char**, ErlDrvSizeT);
- int fpe_was_unmasked;
+static void
+cleanup_scheduled_control(Binary *binp, char *bufp)
+{
+ if (binp) {
+ if (erts_refc_dectest(&binp->refc, 0) == 0)
+ erts_bin_free(binp);
+ }
+ else {
+ if (bufp)
+ erts_free(ERTS_ALC_T_DRV_CTRL_DATA, bufp);
+ }
+}
- ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
- if ((control = prt->drv_ptr->control) == NULL) {
- return THE_NON_VALUE;
+static ERTS_INLINE Uint
+port_control_result_size(int control_flags,
+ char *resp_bufp,
+ ErlDrvSizeT *resp_size,
+ char *pre_alloc_buf)
+{
+ if (!resp_bufp)
+ return (Uint) 0;
+
+ if (control_flags & PORT_CONTROL_FLAG_BINARY) {
+ if (resp_bufp != pre_alloc_buf) {
+ ErlDrvBinary *dbin = (ErlDrvBinary *) resp_bufp;
+ *resp_size = dbin->orig_size;
+ if (*resp_size > ERL_ONHEAP_BIN_LIMIT)
+ return PROC_BIN_SIZE;
+ }
+ ASSERT(*resp_size <= ERL_ONHEAP_BIN_LIMIT);
+ return (Uint) heap_bin_size((*resp_size));
}
- /*
- * Convert the iolist to a buffer, pointed to by to_port,
- * and with its length in to_len.
- */
- if (is_binary(iolist) && binary_bitoffset(iolist) == 0) {
+ return (Uint) 2*(*resp_size);
+}
+
+static ERTS_INLINE Eterm
+write_port_control_result(int control_flags,
+ char *resp_bufp,
+ ErlDrvSizeT resp_size,
+ char *pre_alloc_buf,
+ Eterm **hpp,
+ ErlHeapFragment *bp,
+ ErlOffHeap *ohp)
+{
+ Eterm res;
+ if (!resp_bufp)
+ return NIL;
+ if (control_flags & PORT_CONTROL_FLAG_BINARY) {
+ /* Binary result */
+ ErlDrvBinary *dbin;
+ ErlHeapBin *hbin;
+
+ if (resp_bufp == pre_alloc_buf)
+ dbin = NULL;
+ else {
+ dbin = (ErlDrvBinary *) resp_bufp;
+ if (dbin->orig_size > ERL_ONHEAP_BIN_LIMIT) {
+ ProcBin* pb = (ProcBin *) *hpp;
+ *hpp += PROC_BIN_SIZE;
+ pb->thing_word = HEADER_PROC_BIN;
+ pb->size = dbin->orig_size;
+ pb->next = ohp->first;
+ ohp->first = (struct erl_off_heap_header *) pb;
+ pb->val = ErlDrvBinary2Binary(dbin);
+ pb->bytes = (byte*) dbin->orig_bytes;
+ pb->flags = 0;
+ OH_OVERHEAD(ohp, dbin->orig_size / sizeof(Eterm));
+ return make_binary(pb);
+ }
+ resp_bufp = dbin->orig_bytes;
+ resp_size = dbin->orig_size;
+ }
+
+ hbin = (ErlHeapBin *) *hpp;
+ *hpp += heap_bin_size(resp_size);
+ ASSERT(resp_size <= ERL_ONHEAP_BIN_LIMIT);
+ hbin->thing_word = header_heap_bin(resp_size);
+ hbin->size = resp_size;
+ sys_memcpy(hbin->data, resp_bufp, resp_size);
+ if (dbin)
+ driver_free_binary(dbin);
+ return make_binary(hbin);
+ }
+
+ /* List result */
+ res = buf_to_intlist(hpp, resp_bufp, resp_size, NIL);
+ if (resp_bufp != pre_alloc_buf)
+ driver_free(resp_bufp);
+ return res;
+}
+
+static int
+port_sig_control(Port *prt,
+ erts_aint32_t state,
+ int op,
+ ErtsProc2PortSigData *sigdp)
+{
+ ASSERT(sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY);
+
+ if (op == ERTS_PROC2PORT_SIG_EXEC) {
+ char resp_buf[ERL_ONHEAP_BIN_LIMIT];
+ ErlDrvSizeT resp_size = sizeof(resp_buf);
+ char *resp_bufp = &resp_buf[0];
+ ErtsPortOpResult res;
+
+ res = call_driver_control(sigdp->caller,
+ prt,
+ sigdp->u.control.command,
+ sigdp->u.control.bufp,
+ sigdp->u.control.size,
+ &resp_bufp,
+ &resp_size);
+
+ if (res == ERTS_PORT_OP_DONE) {
+ Eterm msg;
+ Eterm *hp, *hp_start;
+ ErlHeapFragment *bp;
+ ErlOffHeap *ohp;
+ Process *rp;
+ ErtsProcLocks rp_locks = 0;
+ Uint hsz;
+ int control_flags;
+
+ rp = erts_proc_lookup_raw(sigdp->caller);
+ if (!rp)
+ goto done;
+
+ control_flags = prt->control_flags;
+
+ hsz = ERTS_QUEUE_PORT_SCHED_OP_REPLY_SIZE;
+ hsz += port_control_result_size(control_flags,
+ resp_bufp,
+ &resp_size,
+ &resp_buf[0]);
+
+ hp_start = hp = erts_alloc_message_heap(hsz,
+ &bp,
+ &ohp,
+ rp,
+ &rp_locks);
+
+ msg = write_port_control_result(control_flags,
+ resp_bufp,
+ resp_size,
+ &resp_buf[0],
+ &hp,
+ bp,
+ ohp);
+
+ queue_port_sched_op_reply(rp,
+ &rp_locks,
+ hp_start,
+ hp,
+ hsz,
+ bp,
+ sigdp->ref,
+ msg);
+
+ if (rp_locks)
+ erts_smp_proc_unlock(rp, rp_locks);
+ goto done;
+ }
+ }
+
+ /* failure */
+
+ port_sched_op_reply(sigdp->caller, sigdp->ref, am_badarg);
+
+done:
+
+ cleanup_scheduled_control(sigdp->u.control.binp,
+ sigdp->u.control.bufp);
+
+ return ERTS_PORT_REDS_CONTROL;
+}
+
+
+ErtsPortOpResult
+erts_port_control(Process* c_p,
+ Port *prt,
+ unsigned int command,
+ Eterm data,
+ Eterm *retvalp)
+{
+ ErtsPortOpResult res;
+ char *bufp = NULL;
+ ErlDrvSizeT size = 0;
+ int try_call;
+ int tmp_alloced = 0;
+ erts_aint32_t sched_flags;
+ Binary *binp;
+ int copy;
+ ErtsProc2PortSigData *sigdp;
+
+ sched_flags = erts_smp_atomic32_read_nob(&prt->sched.flags);
+ if (sched_flags & ERTS_PTS_FLG_EXIT)
+ return ERTS_PORT_OP_BADARG;
+
+ try_call = !(sched_flags & ERTS_PTS_FLGS_FORCE_SCHEDULE_OP);
+
+ if (is_binary(data) && binary_bitoffset(data) == 0) {
+ byte *bytep;
ERTS_DECLARE_DUMMY(Uint bitoffs);
ERTS_DECLARE_DUMMY(Uint bitsize);
- ERTS_GET_BINARY_BYTES(iolist, to_port, bitoffs, bitsize);
- to_len = binary_size(iolist);
+ ERTS_GET_BINARY_BYTES(data, bytep, bitoffs, bitsize);
+ bufp = (char *) bytep;
+ size = binary_size(data);
} else {
int r;
- /* Try with an 8KB buffer first (will often be enough I guess). */
- to_len = 8*1024;
- to_port = erts_alloc(ERTS_ALC_T_TMP, to_len);
- must_free = 1;
+ if (!try_call) {
+ if (erts_iolist_size(data, &size))
+ return ERTS_PORT_OP_BADARG;
+ bufp = erts_alloc(ERTS_ALC_T_DRV_CTRL_DATA, size);
+ r = erts_iolist_to_buf(data, bufp, size);
+ ASSERT(r == 0);
+ }
+ else {
+ /* Try with an 8KB buffer first (will often be enough I guess). */
+ size = 8*1024;
+ bufp = erts_alloc(ERTS_ALC_T_TMP, size);
+ tmp_alloced = 1;
+
+ r = erts_iolist_to_buf(data, bufp, size);
+ if (ERTS_IOLIST_TO_BUF_SUCCEEDED(r)) {
+ size -= r;
+ } else {
+ if (r == ERTS_IOLIST_TO_BUF_TYPE_ERROR) { /* Type error */
+ erts_free(ERTS_ALC_T_TMP, bufp);
+ return ERTS_PORT_OP_BADARG;
+ }
+ else {
+ ASSERT(r == ERTS_IOLIST_TO_BUF_OVERFLOW); /* Overflow */
+ erts_free(ERTS_ALC_T_TMP, bufp);
+ if (erts_iolist_size(data, &size))
+ return ERTS_PORT_OP_BADARG; /* Type error */
+ }
+ bufp = erts_alloc(ERTS_ALC_T_TMP, size);
+ r = erts_iolist_to_buf(data, bufp, size);
+ ASSERT(r == 0);
+ }
+ }
+ }
- /*
- * In versions before R10B, we used to reserve random
- * amounts of extra memory. From R10B, we allocate the
- * exact amount.
- */
- r = io_list_to_buf(iolist, (char*) to_port, to_len);
- if (r >= 0) {
- to_len -= r;
- } else if (r == -2) { /* Type error */
- erts_free(ERTS_ALC_T_TMP, (void *) to_port);
- return THE_NON_VALUE;
- } else {
- ASSERT(r == -1); /* Overflow */
- erts_free(ERTS_ALC_T_TMP, (void *) to_port);
- if (erts_iolist_size(iolist, &to_len)) { /* Type error */
- return THE_NON_VALUE;
+ if (try_call) {
+ char resp_buf[ERL_ONHEAP_BIN_LIMIT];
+ char* resp_bufp = &resp_buf[0];
+ ErlDrvSizeT resp_size = sizeof(resp_buf);
+ ErtsTryImmDrvCallResult try_call_res;
+ ErtsTryImmDrvCallState try_call_state
+ = ERTS_INIT_TRY_IMM_DRV_CALL_STATE(
+ c_p,
+ prt,
+ ERTS_PORT_SFLGS_INVALID_LOOKUP,
+ 0,
+ 0,
+ am_control);
+
+ try_call_res = try_imm_drv_call(&try_call_state);
+ switch (try_call_res) {
+ case ERTS_TRY_IMM_DRV_CALL_OK: {
+ Eterm *hp;
+ Uint hsz;
+ int control_flags;
+
+ res = call_driver_control(c_p->common.id,
+ prt,
+ command,
+ bufp,
+ size,
+ &resp_bufp,
+ &resp_size);
+ finalize_imm_drv_call(&try_call_state);
+ if (tmp_alloced)
+ erts_free(ERTS_ALC_T_TMP, bufp);
+ if (res == ERTS_PORT_OP_BADARG) {
+ return ERTS_PORT_OP_BADARG;
}
- must_free = 1;
- to_port = erts_alloc(ERTS_ALC_T_TMP, to_len);
- r = io_list_to_buf(iolist, (char*) to_port, to_len);
- ASSERT(r == 0);
+
+ control_flags = prt->control_flags;
+
+ hsz = port_control_result_size(control_flags,
+ resp_bufp,
+ &resp_size,
+ &resp_buf[0]);
+ hp = HAlloc(c_p, hsz);
+ *retvalp = write_port_control_result(control_flags,
+ resp_bufp,
+ resp_size,
+ &resp_buf[0],
+ &hp,
+ NULL,
+ &c_p->off_heap);
+ BUMP_REDS(c_p, ERTS_PORT_REDS_CONTROL);
+ return ERTS_PORT_OP_DONE;
+ }
+ case ERTS_TRY_IMM_DRV_CALL_INVALID_PORT:
+ if (tmp_alloced)
+ erts_free(ERTS_ALC_T_TMP, bufp);
+ return ERTS_PORT_OP_BADARG;
+ default:
+ /* Schedule control() call instead... */
+ break;
}
}
- prt->caller = p->id; /* Internal pid */
+ /* Convert data into something that can be scheduled */
- erts_smp_proc_unlock(p, ERTS_PROC_LOCK_MAIN);
- ERTS_SMP_CHK_NO_PROC_LOCKS;
+ copy = tmp_alloced;
+
+ binp = NULL;
+
+ if (is_binary(data) && binary_bitoffset(data) == 0) {
+ Eterm *ebinp = binary_val_rel(data, NULL);
+ ASSERT(!tmp_alloced);
+ if (*ebinp == HEADER_SUB_BIN)
+ ebinp = binary_val_rel(((ErlSubBin *) ebinp)->orig, NULL);
+ if (*ebinp != HEADER_PROC_BIN)
+ copy = 1;
+ else {
+ binp = ((ProcBin *) ebinp)->val;
+ ASSERT(bufp < bufp + size);
+ ASSERT(binp->orig_bytes <= bufp
+ && bufp + size <= binp->orig_bytes + binp->orig_size);
+ erts_refc_inc(&binp->refc, 1);
+ }
+ }
+
+ if (copy) {
+ char *old_bufp = bufp;
+ bufp = erts_alloc(ERTS_ALC_T_DRV_CTRL_DATA, size);
+ sys_memcpy(bufp, old_bufp, size);
+ if (tmp_alloced)
+ erts_free(ERTS_ALC_T_TMP, old_bufp);
+ }
+
+ sigdp = erts_port_task_alloc_p2p_sig_data();
+ sigdp->flags = ERTS_P2P_SIG_TYPE_CONTROL;
+ sigdp->u.control.binp = binp;
+ sigdp->u.control.command = command;
+ sigdp->u.control.bufp = bufp;
+ sigdp->u.control.size = size;
+
+ res = erts_schedule_proc2port_signal(c_p,
+ prt,
+ c_p->common.id,
+ retvalp,
+ sigdp,
+ 0,
+ port_sig_control);
+ if (res != ERTS_PORT_OP_SCHEDULED) {
+ cleanup_scheduled_control(binp, bufp);
+ return ERTS_PORT_OP_BADARG;
+ }
+ return res;
+}
+
+static ERTS_INLINE ErtsPortOpResult
+call_driver_call(Eterm caller,
+ Port *prt,
+ unsigned int command,
+ char *bufp,
+ ErlDrvSizeT size,
+ char **resp_bufp,
+ ErlDrvSizeT *from_size,
+ unsigned *ret_flagsp)
+{
+ ErlDrvSSizeT cres;
+
+ if (!prt->drv_ptr->call)
+ return ERTS_PORT_OP_BADARG;
#ifdef USE_VM_PROBES
- if (DTRACE_ENABLED(port_control) || DTRACE_ENABLED(driver_control)) {
- DTRACE_FORMAT_COMMON_PROC_AND_PORT(p, prt);
- DTRACE4(port_control, process_str, port_str, prt->name, command);
- DTRACE5(driver_control, process_str, port_str, prt->name,
- command, to_len);
+ if (DTRACE_ENABLED(driver_call)) {
+ DTRACE_CHARBUF(process_str, DTRACE_TERM_BUF_SIZE);
+ DTRACE_CHARBUF(port_str, DTRACE_TERM_BUF_SIZE);
+
+ dtrace_pid_str(caller, process_str);
+ dtrace_port_str(prt, port_str);
+ DTRACE5(driver_call, process_str, port_str, prt->name, command, size);
}
#endif
- /*
- * Call the port's control routine.
- */
+ prt->caller = caller;
+ cres = prt->drv_ptr->call((ErlDrvData) prt->drv_data,
+ command,
+ bufp,
+ size,
+ resp_bufp,
+ *from_size,
+ ret_flagsp);
+ prt->caller = NIL;
- port_resp = port_result;
- fpe_was_unmasked = erts_block_fpe();
- n = control((ErlDrvData)prt->drv_data, command, (char*)to_port, to_len,
- &port_resp, sizeof(port_result));
- erts_unblock_fpe(fpe_was_unmasked);
- if (must_free) {
- erts_free(ERTS_ALC_T_TMP, (void *) to_port);
+ if (cres <= 0
+ || ((byte) (*resp_bufp)[0]) != VERSION_MAGIC)
+ return ERTS_PORT_OP_BADARG;
+
+ *from_size = (ErlDrvSizeT) cres;
+
+ return ERTS_PORT_OP_DONE;
+}
+
+
+static
+void cleanup_scheduled_call(char *bufp)
+{
+ if (bufp)
+ erts_free(ERTS_ALC_T_DRV_CALL_DATA, bufp);
+}
+
+static int
+port_sig_call(Port *prt,
+ erts_aint32_t state,
+ int op,
+ ErtsProc2PortSigData *sigdp)
+{
+ char resp_buf[256];
+ ErlDrvSizeT resp_size = sizeof(resp_buf);
+ char *resp_bufp = &resp_buf[0];
+ unsigned ret_flags = 0U;
+
+
+ ASSERT(sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY);
+
+ if (op == ERTS_PROC2PORT_SIG_EXEC) {
+ ErtsPortOpResult res;
+
+ res = call_driver_call(sigdp->caller,
+ prt,
+ sigdp->u.call.command,
+ sigdp->u.call.bufp,
+ sigdp->u.call.size,
+ &resp_bufp,
+ &resp_size,
+ &ret_flags);
+
+ if (res == ERTS_PORT_OP_DONE) {
+ Eterm msg;
+ Eterm *hp;
+ ErlHeapFragment *bp;
+ ErlOffHeap *ohp;
+ Process *rp;
+ ErtsProcLocks rp_locks = 0;
+ Uint hsz;
+
+ rp = erts_proc_lookup_raw(sigdp->caller);
+ if (!rp)
+ goto done;
+
+ hsz = erts_decode_ext_size((byte *) resp_bufp, resp_size);
+ if (hsz >= 0) {
+ Eterm *hp_start;
+ byte *endp;
+
+ hsz += 3; /* ok tuple */
+ hsz += ERTS_QUEUE_PORT_SCHED_OP_REPLY_SIZE;
+
+ hp_start = hp = erts_alloc_message_heap(hsz,
+ &bp,
+ &ohp,
+ rp,
+ &rp_locks);
+ endp = (byte *) resp_bufp;
+ msg = erts_decode_ext(&hp, ohp, &endp);
+ if (is_value(msg)) {
+ msg = TUPLE2(hp, am_ok, msg);
+ hp += 3;
+
+ queue_port_sched_op_reply(rp,
+ &rp_locks,
+ hp_start,
+ hp,
+ hsz,
+ bp,
+ sigdp->ref,
+ msg);
+
+ if (rp_locks)
+ erts_smp_proc_unlock(rp, rp_locks);
+ goto done;
+ }
+ if (bp)
+ free_message_buffer(bp);
+ if (rp_locks)
+ erts_smp_proc_unlock(rp, rp_locks);
+ }
+ }
}
- prt->caller = NIL;
-#ifdef ERTS_SMP
- if (prt->xports)
- erts_smp_xports_unlock(prt);
- ASSERT(!prt->xports);
-#endif
- erts_smp_proc_lock(p, ERTS_PROC_LOCK_MAIN);
- /*
- * Handle the result.
- */
+ port_sched_op_reply(sigdp->caller, sigdp->ref, am_badarg);
+
+done:
+
+ if (resp_bufp != &resp_buf[0] && !(ret_flags & DRIVER_CALL_KEEP_BUFFER))
+ driver_free(resp_bufp);
+
+ cleanup_scheduled_call(sigdp->u.call.bufp);
+
+ return ERTS_PORT_REDS_CALL;
+}
+
+
+ErtsPortOpResult
+erts_port_call(Process* c_p,
+ Port *prt,
+ unsigned int command,
+ Eterm data,
+ Eterm *retvalp)
+{
+ ErtsPortOpResult res;
+ char input_buf[256];
+ char *bufp;
+ byte *endp;
+ ErlDrvSizeT size;
+ int try_call;
+ erts_aint32_t sched_flags;
+ ErtsProc2PortSigData *sigdp;
- if (n < 0) {
- return THE_NON_VALUE;
+ sched_flags = erts_smp_atomic32_read_nob(&prt->sched.flags);
+ if (sched_flags & ERTS_PTS_FLG_EXIT) {
+ return ERTS_PORT_OP_BADARG;
}
- if ((prt->control_flags & PORT_CONTROL_FLAG_BINARY) == 0) { /* List result */
- Eterm ret;
- Eterm* hp = HAlloc(p, 2*n);
- ret = buf_to_intlist(&hp, port_resp, n, NIL);
- if (port_resp != port_result) {
- driver_free(port_resp);
+ try_call = !(sched_flags & ERTS_PTS_FLGS_FORCE_SCHEDULE_OP);
+
+ size = erts_encode_ext_size(data);
+
+ if (!try_call)
+ bufp = erts_alloc(ERTS_ALC_T_DRV_CALL_DATA, size);
+ else if (size <= sizeof(input_buf))
+ bufp = &input_buf[0];
+ else
+ bufp = erts_alloc(ERTS_ALC_T_TMP, size);
+
+ endp = (byte *) bufp;
+ erts_encode_ext(data, &endp);
+
+ if (endp - (byte *) bufp > size)
+ ERTS_INTERNAL_ERROR("erts_internal:port_call() - Buffer overflow");
+
+ size = endp - (byte *) bufp;
+
+ if (try_call) {
+ char resp_buf[255];
+ char* resp_bufp = &resp_buf[0];
+ ErlDrvSizeT resp_size = sizeof(resp_buf);
+ ErtsTryImmDrvCallResult try_call_res;
+ ErtsTryImmDrvCallState try_call_state
+ = ERTS_INIT_TRY_IMM_DRV_CALL_STATE(
+ c_p,
+ prt,
+ ERTS_PORT_SFLGS_INVALID_LOOKUP,
+ 0,
+ 0,
+ am_call);
+
+ try_call_res = try_imm_drv_call(&try_call_state);
+ switch (try_call_res) {
+ case ERTS_TRY_IMM_DRV_CALL_OK: {
+ Eterm *hp, *hp_end;
+ Uint hsz;
+ unsigned ret_flags = 0U;
+ Eterm term;
+
+ res = call_driver_call(c_p->common.id,
+ prt,
+ command,
+ bufp,
+ size,
+ &resp_bufp,
+ &resp_size,
+ &ret_flags);
+
+ finalize_imm_drv_call(&try_call_state);
+ if (bufp != &input_buf[0])
+ erts_free(ERTS_ALC_T_TMP, bufp);
+ if (res == ERTS_PORT_OP_BADARG)
+ return ERTS_PORT_OP_BADARG;
+ hsz = erts_decode_ext_size((byte *) resp_bufp, resp_size);
+ if (hsz < 0)
+ return ERTS_PORT_OP_BADARG;
+ hsz += 3;
+ hp = HAlloc(c_p, hsz);
+ hp_end = hp + hsz;
+ endp = (byte *) resp_bufp;
+ term = erts_decode_ext(&hp, &MSO(c_p), &endp);
+ if (term == THE_NON_VALUE)
+ return ERTS_PORT_OP_BADARG;
+ *retvalp = TUPLE2(hp, am_ok, term);
+ hp += 3;
+ HRelease(c_p, hp_end, hp);
+ if (resp_buf != &resp_buf[0]
+ && !(ret_flags & DRIVER_CALL_KEEP_BUFFER))
+ driver_free(resp_buf);
+ BUMP_REDS(c_p, ERTS_PORT_REDS_CALL);
+ return ERTS_PORT_OP_DONE;
}
- return ret;
- }
- else if (port_resp == NULL) {
- return NIL;
- }
- else { /* Binary result */
- ErlDrvBinary *dbin;
- ErlHeapBin *hbin;
- if (port_resp != port_result) {
- dbin = (ErlDrvBinary *) port_resp;
- if (dbin->orig_size > ERL_ONHEAP_BIN_LIMIT) {
- ProcBin* pb = (ProcBin *) HAlloc(p, PROC_BIN_SIZE);
- pb->thing_word = HEADER_PROC_BIN;
- pb->size = dbin->orig_size;
- pb->next = MSO(p).first;
- MSO(p).first = (struct erl_off_heap_header*)pb;
- pb->val = ErlDrvBinary2Binary(dbin);
- pb->bytes = (byte*) dbin->orig_bytes;
- pb->flags = 0;
- OH_OVERHEAD(&(MSO(p)), dbin->orig_size / sizeof(Eterm));
- return make_binary(pb);
- }
- port_resp = dbin->orig_bytes;
- n = dbin->orig_size;
- } else {
- dbin = NULL;
+ case ERTS_TRY_IMM_DRV_CALL_INVALID_PORT:
+ if (bufp != &input_buf[0])
+ erts_free(ERTS_ALC_T_TMP, bufp);
+ return ERTS_PORT_OP_BADARG;
+ default:
+ /* Schedule call() call instead... */
+ break;
}
- hbin = (ErlHeapBin*) HAlloc(p, heap_bin_size(n));
- ASSERT(n <= ERL_ONHEAP_BIN_LIMIT);
- hbin->thing_word = header_heap_bin(n);
- hbin->size = n;
- sys_memcpy(hbin->data, port_resp, n);
- if (dbin != NULL) {
- driver_free_binary(dbin);
+ }
+
+ /* Convert data into something that can be scheduled */
+
+ if (bufp == &input_buf[0] || try_call) {
+ char *new_bufp = erts_alloc(ERTS_ALC_T_DRV_CALL_DATA, size);
+ sys_memcpy(new_bufp, bufp, size);
+ if (bufp != &input_buf[0])
+ erts_free(ERTS_ALC_T_TMP, bufp);
+ bufp = new_bufp;
+ }
+
+ sigdp = erts_port_task_alloc_p2p_sig_data();
+ sigdp->flags = ERTS_P2P_SIG_TYPE_CALL;
+ sigdp->u.call.command = command;
+ sigdp->u.call.bufp = bufp;
+ sigdp->u.call.size = size;
+
+ res = erts_schedule_proc2port_signal(c_p,
+ prt,
+ c_p->common.id,
+ retvalp,
+ sigdp,
+ 0,
+ port_sig_call);
+ if (res != ERTS_PORT_OP_SCHEDULED) {
+ cleanup_scheduled_call(bufp);
+ return ERTS_PORT_OP_BADARG;
+ }
+ return res;
+}
+
+static Eterm
+make_port_info_term(Eterm **hpp_start,
+ Eterm **hpp,
+ Uint *hszp,
+ ErlHeapFragment **bpp,
+ Port *prt,
+ Eterm item)
+{
+ ErlOffHeap *ohp;
+
+ if (is_value(item)) {
+ if (erts_bld_port_info(NULL, NULL, hszp, prt, item) == am_false)
+ return THE_NON_VALUE;
+ if (*hszp) {
+ *bpp = new_message_buffer(*hszp);
+ *hpp_start = *hpp = (*bpp)->mem;
+ ohp = &(*bpp)->off_heap;
}
- return make_binary(hbin);
+ else {
+ *bpp = NULL;
+ *hpp_start = *hpp = NULL;
+ ohp = NULL;
+ }
+ return erts_bld_port_info(hpp, ohp, NULL, prt, item);
+ }
+ else {
+ int i;
+ int len;
+ int start;
+ static Eterm item[] = ERTS_PORT_INFO_1_ITEMS;
+ static Eterm value[sizeof(item)/sizeof(item[0])];
+
+ start = 0;
+ len = sizeof(item)/sizeof(item[0]);
+
+ for (i = start; i < sizeof(item)/sizeof(item[0]); i++) {
+ ASSERT(is_atom(item[i]));
+ value[i] = erts_bld_port_info(NULL, NULL, hszp, prt, item[i]);
+ }
+
+ if (value[0] == am_undefined) {
+ start++;
+ len--;
+ }
+
+ erts_bld_list(NULL, hszp, len, &value[start]);
+
+ *bpp = new_message_buffer(*hszp);
+ *hpp_start = *hpp = (*bpp)->mem;
+ ohp = &(*bpp)->off_heap;
+
+ for (i = start; i < sizeof(item)/sizeof(item[0]); i++)
+ value[i] = erts_bld_port_info(hpp, ohp, NULL, prt, item[i]);
+
+ return erts_bld_list(hpp, NULL, len, &value[start]);
+ }
+}
+
+static int
+port_sig_info(Port *prt,
+ erts_aint32_t state,
+ int op,
+ ErtsProc2PortSigData *sigdp)
+{
+ ASSERT(sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY);
+ if (op != ERTS_PROC2PORT_SIG_EXEC)
+ port_sched_op_reply(sigdp->caller, sigdp->ref, am_undefined);
+ else {
+ Eterm *hp, *hp_start;
+ Uint hsz;
+ ErlHeapFragment *bp;
+ Eterm value;
+ Process *rp;
+ ErtsProcLocks rp_locks = 0;
+
+ rp = erts_proc_lookup_raw(sigdp->caller);
+ if (!rp)
+ return ERTS_PORT_REDS_INFO;
+
+ hsz = ERTS_QUEUE_PORT_SCHED_OP_REPLY_SIZE;
+ value = make_port_info_term(&hp_start,
+ &hp,
+ &hsz,
+ &bp,
+ prt,
+ sigdp->u.info.item);
+ if (is_value(value)) {
+ queue_port_sched_op_reply(rp,
+ &rp_locks,
+ hp_start,
+ hp,
+ hsz,
+ bp,
+ sigdp->ref,
+ value);
+ }
+ if (rp_locks)
+ erts_smp_proc_unlock(rp, rp_locks);
+ }
+ return ERTS_PORT_REDS_INFO;
+}
+
+ErtsPortOpResult
+erts_port_info(Process* c_p,
+ Port *prt,
+ Eterm item,
+ Eterm *retvalp)
+{
+ ErtsProc2PortSigData *sigdp;
+ ErtsTryImmDrvCallResult try_call_res;
+ ErtsTryImmDrvCallState try_call_state
+ = ERTS_INIT_TRY_IMM_DRV_CALL_STATE(
+ c_p,
+ prt,
+ ERTS_PORT_SFLGS_INVALID_LOOKUP,
+ 0,
+ 0,
+ am_info);
+
+ try_call_res = try_imm_drv_call(&try_call_state);
+ switch (try_call_res) {
+ case ERTS_TRY_IMM_DRV_CALL_OK: {
+ Eterm *hp, *hp_start;
+ ErlHeapFragment *bp;
+ Uint hsz = 0;
+ Eterm value = make_port_info_term(&hp_start, &hp, &hsz, &bp, prt, item);
+ finalize_imm_drv_call(&try_call_state);
+ if (is_non_value(value))
+ return ERTS_PORT_OP_BADARG;
+ else if (is_immed(value))
+ *retvalp = value;
+ else {
+ Uint used_h_size = hp - hp_start;
+ hp = HAlloc(c_p, used_h_size);
+ *retvalp = copy_struct(value, used_h_size, &hp, &MSO(c_p));
+ free_message_buffer(bp);
+ }
+ BUMP_REDS(c_p, ERTS_PORT_REDS_INFO);
+ return ERTS_PORT_OP_DONE;
+ }
+ case ERTS_TRY_IMM_DRV_CALL_INVALID_PORT:
+ return ERTS_PORT_OP_DROPPED;
+ case ERTS_TRY_IMM_DRV_CALL_INVALID_SCHED_FLAGS:
+ case ERTS_TRY_IMM_DRV_CALL_BUSY_LOCK:
+ /* Schedule call instead... */
+ break;
+ }
+
+ sigdp = erts_port_task_alloc_p2p_sig_data();
+ sigdp->flags = ERTS_P2P_SIG_TYPE_INFO;
+ sigdp->u.info.item = item;
+
+ return erts_schedule_proc2port_signal(c_p,
+ prt,
+ c_p->common.id,
+ retvalp,
+ sigdp,
+ 0,
+ port_sig_info);
+}
+
+static int
+port_sig_set_data(Port *prt,
+ erts_aint32_t state,
+ int op,
+ ErtsProc2PortSigData *sigdp)
+{
+ ASSERT(sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY);
+
+ if (op == ERTS_PROC2PORT_SIG_EXEC) {
+ if (prt->bp)
+ free_message_buffer(prt->bp);
+ prt->bp = sigdp->u.set_data.bp;
+ prt->data = sigdp->u.set_data.data;
+ port_sched_op_reply(sigdp->caller, sigdp->ref, am_true);
+ }
+ else {
+ if (sigdp->u.set_data.bp)
+ free_message_buffer(sigdp->u.set_data.bp);
+ port_sched_op_reply(sigdp->caller, sigdp->ref, am_badarg);
+ }
+ return ERTS_PORT_REDS_SET_DATA;
+}
+
+ErtsPortOpResult
+erts_port_set_data(Process* c_p,
+ Port *prt,
+ Eterm data,
+ Eterm *refp)
+{
+ ErtsPortOpResult res;
+ Eterm set_data;
+ ErlHeapFragment *bp;
+ ErtsProc2PortSigData *sigdp;
+ ErtsTryImmDrvCallResult try_call_res;
+ ErtsTryImmDrvCallState try_call_state
+ = ERTS_INIT_TRY_IMM_DRV_CALL_STATE(
+ c_p,
+ prt,
+ ERTS_PORT_SFLGS_INVALID_LOOKUP,
+ 0,
+ !refp,
+ am_set_data);
+
+ if (is_immed(data)) {
+ set_data = data;
+ bp = NULL;
+ }
+ else {
+ Eterm *hp;
+ Uint sz = size_object(data);
+ bp = new_message_buffer(sz);
+ hp = bp->mem;
+ set_data = copy_struct(data, sz, &hp, &bp->off_heap);
+ }
+
+ try_call_res = try_imm_drv_call(&try_call_state);
+ switch (try_call_res) {
+ case ERTS_TRY_IMM_DRV_CALL_OK:
+ if (prt->bp)
+ free_message_buffer(prt->bp);
+ prt->bp = bp;
+ prt->data = set_data;
+ finalize_imm_drv_call(&try_call_state);
+ BUMP_REDS(c_p, ERTS_PORT_REDS_SET_DATA);
+ return ERTS_PORT_OP_DONE;
+ case ERTS_TRY_IMM_DRV_CALL_INVALID_PORT:
+ return ERTS_PORT_OP_DROPPED;
+ case ERTS_TRY_IMM_DRV_CALL_INVALID_SCHED_FLAGS:
+ case ERTS_TRY_IMM_DRV_CALL_BUSY_LOCK:
+ /* Schedule call instead... */
+ break;
+ }
+
+ sigdp = erts_port_task_alloc_p2p_sig_data();
+ sigdp->flags = ERTS_P2P_SIG_TYPE_SET_DATA;
+ sigdp->u.set_data.data = set_data;
+ sigdp->u.set_data.bp = bp;
+
+ res = erts_schedule_proc2port_signal(c_p,
+ prt,
+ c_p->common.id,
+ refp,
+ sigdp,
+ 0,
+ port_sig_set_data);
+ if (res != ERTS_PORT_OP_SCHEDULED && bp)
+ free_message_buffer(bp);
+ return res;
+}
+
+static int
+port_sig_get_data(Port *prt,
+ erts_aint32_t state,
+ int op,
+ ErtsProc2PortSigData *sigdp)
+{
+ ASSERT(sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY);
+ if (op != ERTS_PROC2PORT_SIG_EXEC)
+ port_sched_op_reply(sigdp->caller, sigdp->ref, am_badarg);
+ else {
+ Process *rp;
+ ErtsProcLocks rp_locks = 0;
+
+ rp = erts_proc_lookup_raw(sigdp->caller);
+ if (rp) {
+ Uint hsz;
+ Eterm *hp, *hp_start;
+ Eterm data, msg;
+ ErlHeapFragment *bp;
+ ErlOffHeap *ohp;
+
+ hsz = ERTS_QUEUE_PORT_SCHED_OP_REPLY_SIZE;
+ hsz += 3;
+ if (prt->bp)
+ hsz += prt->bp->used_size;
+
+ hp_start = hp = erts_alloc_message_heap(hsz,
+ &bp,
+ &ohp,
+ rp,
+ &rp_locks);
+
+ if (is_immed(prt->data))
+ data = prt->data;
+ else
+ data = copy_struct(prt->data,
+ prt->bp->used_size,
+ &hp,
+ &bp->off_heap);
+
+
+
+ msg = TUPLE2(hp, am_ok, data);
+ hp += 3;
+
+ queue_port_sched_op_reply(rp,
+ &rp_locks,
+ hp_start,
+ hp,
+ hsz,
+ bp,
+ sigdp->ref,
+ msg);
+ if (rp_locks)
+ erts_smp_proc_unlock(rp, rp_locks);
+ }
+ }
+ return ERTS_PORT_REDS_GET_DATA;
+}
+
+ErtsPortOpResult
+erts_port_get_data(Process* c_p,
+ Port *prt,
+ Eterm *retvalp)
+{
+ ErtsProc2PortSigData *sigdp;
+ ErtsTryImmDrvCallResult try_call_res;
+ ErtsTryImmDrvCallState try_call_state
+ = ERTS_INIT_TRY_IMM_DRV_CALL_STATE(
+ c_p,
+ prt,
+ ERTS_PORT_SFLGS_INVALID_LOOKUP,
+ 0,
+ 0,
+ am_get_data);
+
+ try_call_res = try_imm_drv_call(&try_call_state);
+ switch (try_call_res) {
+ case ERTS_TRY_IMM_DRV_CALL_OK: {
+ Eterm *hp;
+ Eterm data;
+ ErlHeapFragment *bp;
+ Uint sz;
+ if (is_immed(prt->data)) {
+ bp = NULL;
+ data = prt->data;
+ }
+ else {
+ bp = new_message_buffer(prt->bp->used_size);
+ data = copy_struct(prt->data,
+ prt->bp->used_size,
+ &hp,
+ &bp->off_heap);
+ }
+ finalize_imm_drv_call(&try_call_state);
+ if (is_immed(data))
+ sz = 0;
+ else
+ sz = bp->used_size;
+
+ hp = HAlloc(c_p, sz + 3);
+ if (is_not_immed(data)) {
+ data = copy_struct(data, bp->used_size, &hp, &MSO(c_p));
+ free_message_buffer(bp);
+ }
+ *retvalp = TUPLE2(hp, am_ok, data);
+ BUMP_REDS(c_p, ERTS_PORT_REDS_GET_DATA);
+ return ERTS_PORT_OP_DONE;
+ }
+ case ERTS_TRY_IMM_DRV_CALL_INVALID_PORT:
+ return ERTS_PORT_OP_DROPPED;
+ case ERTS_TRY_IMM_DRV_CALL_INVALID_SCHED_FLAGS:
+ case ERTS_TRY_IMM_DRV_CALL_BUSY_LOCK:
+ /* Schedule call instead... */
+ break;
}
+
+ sigdp = erts_port_task_alloc_p2p_sig_data();
+ sigdp->flags = ERTS_P2P_SIG_TYPE_GET_DATA;
+
+ return erts_schedule_proc2port_signal(c_p,
+ prt,
+ c_p->common.id,
+ retvalp,
+ sigdp,
+ 0,
+ port_sig_get_data);
}
typedef struct {
@@ -2480,39 +4702,39 @@ static void prt_one_lnk(ErtsLink *lnk, void *vprtd)
}
void
-print_port_info(int to, void *arg, int i)
+print_port_info(Port *p, int to, void *arg)
{
- Port* p = &erts_port[i];
+ erts_aint32_t state = erts_atomic32_read_nob(&p->state);
- if (p->status & ERTS_PORT_SFLGS_DEAD)
+ if (state & ERTS_PORT_SFLGS_DEAD)
return;
- erts_print(to, arg, "=port:%T\n", p->id);
- erts_print(to, arg, "Slot: %d\n", i);
- if (p->status & ERTS_PORT_SFLG_CONNECTED) {
- erts_print(to, arg, "Connected: %T", p->connected);
+ erts_print(to, arg, "=port:%T\n", p->common.id);
+ erts_print(to, arg, "Slot: %d\n", internal_port_index(p->common.id));
+ if (state & ERTS_PORT_SFLG_CONNECTED) {
+ erts_print(to, arg, "Connected: %T", ERTS_PORT_GET_CONNECTED(p));
erts_print(to, arg, "\n");
}
- if (p->nlinks != NULL) {
+ if (ERTS_P_LINKS(p)) {
prt_one_lnk_data prtd;
prtd.to = to;
prtd.arg = arg;
erts_print(to, arg, "Links: ");
- erts_doforall_links(p->nlinks, &prt_one_lnk, &prtd);
+ erts_doforall_links(ERTS_P_LINKS(p), &prt_one_lnk, &prtd);
erts_print(to, arg, "\n");
}
- if (p->monitors != NULL) {
+ if (ERTS_P_MONITORS(p)) {
prt_one_lnk_data prtd;
prtd.to = to;
prtd.arg = arg;
erts_print(to, arg, "Monitors: ");
- erts_doforall_monitors(p->monitors, &prt_one_monitor, &prtd);
+ erts_doforall_monitors(ERTS_P_MONITORS(p), &prt_one_monitor, &prtd);
erts_print(to, arg, "\n");
}
- if (p->reg != NULL)
- erts_print(to, arg, "Registered as: %T\n", p->reg->name);
+ if (p->common.u.alive.reg != NULL)
+ erts_print(to, arg, "Registered as: %T\n", p->common.u.alive.reg->name);
if (p->drv_ptr == &fd_driver) {
erts_print(to, arg, "Port is UNIX fd not opened by emulator: %s\n", p->name);
@@ -2526,109 +4748,143 @@ print_port_info(int to, void *arg, int i)
}
void
-set_busy_port(ErlDrvPort port_num, int on)
+set_busy_port(ErlDrvPort dprt, int on)
{
+ Port *prt;
+ erts_aint32_t flags;
+
#ifdef USE_VM_PROBES
DTRACE_CHARBUF(port_str, 16);
#endif
ERTS_SMP_CHK_NO_PROC_LOCKS;
- ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(&erts_port[port_num]));
+ prt = erts_drvport2port_raw(dprt);
+ if (!prt)
+ return;
if (on) {
- erts_port_status_bor_set(&erts_port[port_num],
- ERTS_PORT_SFLG_PORT_BUSY);
+ flags = erts_smp_atomic32_read_bor_acqb(&prt->sched.flags,
+ ERTS_PTS_FLG_BUSY_PORT);
+ if (flags & ERTS_PTS_FLG_BUSY_PORT)
+ return; /* Already busy */
+
+ if (flags & ERTS_PTS_FLG_HAVE_NS_TASKS)
+ erts_port_task_abort_nosuspend_tasks(prt);
+
#ifdef USE_VM_PROBES
if (DTRACE_ENABLED(port_busy)) {
erts_snprintf(port_str, sizeof(port_str),
- "%T", erts_port[port_num].id);
+ "%T", prt->common.id);
DTRACE1(port_busy, port_str);
}
#endif
} else {
- ErtsProcList* plp = erts_port[port_num].suspended;
- erts_port_status_band_set(&erts_port[port_num],
- ~ERTS_PORT_SFLG_PORT_BUSY);
- erts_port[port_num].suspended = NULL;
+ flags = erts_smp_atomic32_read_band_acqb(&prt->sched.flags,
+ ~ERTS_PTS_FLG_BUSY_PORT);
+ if (!(flags & ERTS_PTS_FLG_BUSY_PORT))
+ return; /* Already non-busy */
#ifdef USE_VM_PROBES
if (DTRACE_ENABLED(port_not_busy)) {
erts_snprintf(port_str, sizeof(port_str),
- "%T", erts_port[port_num].id);
+ "%T", prt->common.id);
DTRACE1(port_not_busy, port_str);
}
#endif
- if (erts_port[port_num].dist_entry) {
+ if (prt->dist_entry) {
/*
* Processes suspended on distribution ports are
* normally queued on the dist entry.
*/
- erts_dist_port_not_busy(&erts_port[port_num]);
+ erts_dist_port_not_busy(prt);
}
- /*
- * Resume, in a round-robin fashion, all processes waiting on the port.
- *
- * This version submitted by Tony Rogvall. The earlier version used
- * to resume the processes in order, which caused starvation of all but
- * the first process.
- */
+ if (!(flags & ERTS_PTS_FLG_BUSY_PORT_Q))
+ erts_port_resume_procs(prt);
+ }
+}
+
+void
+erts_port_resume_procs(Port *prt)
+{
+ /*
+ * Resume, in a round-robin fashion, all processes waiting on the port.
+ *
+ * This version submitted by Tony Rogvall. The earlier version used
+ * to resume the processes in order, which caused starvation of all but
+ * the first process.
+ */
+ ErtsProcList *plp;
+
+ erts_port_task_sched_lock(&prt->sched);
+ plp = prt->suspended;
+ prt->suspended = NULL;
+ erts_port_task_sched_unlock(&prt->sched);
+
+ if (erts_proclist_fetch(&plp, NULL)) {
- if (plp) {
#ifdef USE_VM_PROBES
- /*
- * Hrm, for blocked dist ports, plp always seems to be NULL.
- * That's not so fun.
- * Well, another way to get the same info is using a D
- * script to correlate an earlier process-port_blocked+pid
- * event with a later process-scheduled event. That's
- * subject to the multi-CPU races with how events are
- * handled, but hey, that way works most of the time.
- */
- if (DTRACE_ENABLED(process_port_unblocked)) {
- DTRACE_CHARBUF(pid_str, 16);
- ErtsProcList* plp2 = plp;
-
- erts_snprintf(port_str, sizeof(port_str),
- "%T", erts_port[port_num]);
- while (plp2 != NULL) {
- erts_snprintf(pid_str, sizeof(pid_str), "%T", plp2->pid);
- DTRACE2(process_port_unblocked, pid_str, port_str);
- }
- }
-#endif
- /* First proc should be resumed last */
- if (plp->next) {
- erts_resume_processes(plp->next);
- plp->next = NULL;
+ /*
+ * Hrm, for blocked dist ports, plp always seems to be NULL.
+ * That's not so fun.
+ * Well, another way to get the same info is using a D
+ * script to correlate an earlier process-port_blocked+pid
+ * event with a later process-scheduled event. That's
+ * subject to the multi-CPU races with how events are
+ * handled, but hey, that way works most of the time.
+ */
+ if (DTRACE_ENABLED(process_port_unblocked)) {
+ DTRACE_CHARBUF(port_str, 16);
+ DTRACE_CHARBUF(pid_str, 16);
+ ErtsProcList* plp2 = plp;
+
+ erts_snprintf(port_str, sizeof(port_str), "%T", prt->common.id);
+ while (plp2 != NULL) {
+ erts_snprintf(pid_str, sizeof(pid_str), "%T", plp2->pid);
+ DTRACE2(process_port_unblocked, pid_str, port_str);
}
- erts_resume_processes(plp);
- }
+ }
+#endif
+
+ /* First proc should be resumed last */
+ if (plp->next) {
+ plp->next->prev = NULL;
+ erts_resume_processes(plp->next);
+ plp->next = NULL;
+ }
+ erts_resume_processes(plp);
}
}
void set_port_control_flags(ErlDrvPort port_num, int flags)
{
-
- ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(&erts_port[port_num]));
-
- erts_port[port_num].control_flags = flags;
+ Port *prt = erts_drvport2port_raw(port_num);
+ if (prt)
+ prt->control_flags = flags;
}
-int get_port_flags(ErlDrvPort ix) {
- Port* prt = erts_drvport2port(ix);
+int get_port_flags(ErlDrvPort ix)
+{
+ int flags;
+ Port *prt;
+ erts_aint32_t state;
+
+ prt = erts_drvport2port(ix, &state);
+ if (!prt)
+ return 0;
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
- if (prt == NULL)
- return 0;
+ flags = 0;
+ if (state & ERTS_PORT_SFLG_BINARY_IO)
+ flags |= PORT_FLAG_BINARY;
+ if (state & ERTS_PORT_SFLG_LINEBUF_IO)
+ flags |= PORT_FLAG_LINE;
- return (prt->status & ERTS_PORT_SFLG_BINARY_IO ? PORT_FLAG_BINARY : 0)
- | (prt->status & ERTS_PORT_SFLG_LINEBUF_IO ? PORT_FLAG_LINE : 0);
+ return flags;
}
-
void erts_raw_port_command(Port* p, byte* buf, Uint len)
{
int fpe_was_unmasked;
@@ -2665,25 +4921,18 @@ int async_ready(Port *p, void* data)
if (p) {
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(p));
- ASSERT(!(p->status & ERTS_PORT_SFLGS_DEAD));
if (p->drv_ptr->ready_async != NULL) {
#ifdef USE_VM_PROBES
if (DTRACE_ENABLED(driver_ready_async)) {
- DTRACE_FORMAT_COMMON_PID_AND_PORT(p->connected, p)
+ DTRACE_FORMAT_COMMON_PID_AND_PORT(ERTS_PORT_GET_CONNECTED(p), p)
DTRACE3(driver_ready_async, process_str, port_str, p->name);
}
#endif
(*p->drv_ptr->ready_async)((ErlDrvData)p->drv_data, data);
need_free = 0;
-#ifdef ERTS_SMP
- if (p->xports)
- erts_smp_xports_unlock(p);
- ASSERT(!p->xports);
-#endif
- }
- if ((p->status & ERTS_PORT_SFLG_CLOSING) && is_port_ioq_empty(p)) {
- terminate_port(p);
+
}
+ erts_port_driver_callback_epilogue(p, NULL);
}
return need_free;
}
@@ -2691,12 +4940,12 @@ int async_ready(Port *p, void* data)
static void
report_missing_drv_callback(Port *p, char *drv_type, char *callback)
{
- ErtsPortNames *pnp = erts_get_port_names(p->id);
+ ErtsPortNames *pnp = erts_get_port_names(p->common.id);
char *unknown = "<unknown>";
char *drv_name = pnp->driver_name ? pnp->driver_name : unknown;
char *prt_name = pnp->name ? pnp->name : unknown;
erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf();
- erts_dsprintf(dsbufp, "%T: %s driver '%s' ", p->id, drv_type, drv_name);
+ erts_dsprintf(dsbufp, "%T: %s driver '%s' ", p->common.id, drv_type, drv_name);
if (sys_strcmp(drv_name, prt_name) != 0)
erts_dsprintf(dsbufp, "(%s) ", prt_name);
erts_dsprintf(dsbufp, "does not implement the %s callback!\n", callback);
@@ -2711,7 +4960,7 @@ erts_stale_drv_select(Eterm port,
int deselect)
{
char *type;
- ErlDrvPort drv_port = internal_port_index(port);
+ ErlDrvPort drv_port = (ErlDrvPort) erts_port_lookup_raw(port);
ErtsPortNames *pnp = erts_get_port_names(port);
erts_dsprintf_buf_t *dsbufp;
@@ -2751,16 +5000,16 @@ erts_stale_drv_select(Eterm port,
ErtsPortNames *
erts_get_port_names(Eterm id)
{
+ Port *prt = erts_port_lookup_raw(id);
ErtsPortNames *pnp;
ASSERT(is_nil(id) || is_internal_port(id));
-
- if (is_not_internal_port(id)) {
+
+ if (!prt) {
pnp = erts_alloc(ERTS_ALC_T_PORT_NAMES, sizeof(ErtsPortNames));
pnp->name = NULL;
pnp->driver_name = NULL;
}
else {
- Port* prt = &erts_port[internal_port_index(id)];
int do_realloc = 1;
int len = -1;
size_t pnp_len = sizeof(ErtsPortNames);
@@ -2776,17 +5025,10 @@ erts_get_port_names(Eterm id)
pnp_len = sizeof(ErtsPortNames) + len;
pnp = erts_alloc(ERTS_ALC_T_PORT_NAMES, pnp_len);
}
- erts_smp_port_state_lock(prt);
- if (id != prt->id) {
- len = nlen = 0;
- name = driver_name = NULL;
- }
- else {
- name = prt->name;
- len = nlen = name ? sys_strlen(name) + 1 : 0;
- driver_name = (prt->drv_ptr ? prt->drv_ptr->name : NULL);
- len += driver_name ? sys_strlen(driver_name) + 1 : 0;
- }
+ name = prt->name;
+ len = nlen = name ? sys_strlen(name) + 1 : 0;
+ driver_name = (prt->drv_ptr ? prt->drv_ptr->name : NULL);
+ len += driver_name ? sys_strlen(driver_name) + 1 : 0;
if (len <= pnp_len - sizeof(ErtsPortNames)) {
if (!name)
pnp->name = NULL;
@@ -2804,7 +5046,6 @@ erts_get_port_names(Eterm id)
}
do_realloc = 0;
}
- erts_smp_port_state_unlock(prt);
} while (do_realloc);
}
return pnp;
@@ -2829,11 +5070,9 @@ static void schedule_port_timeout(Port *p)
* /Rickard
*/
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(p));
- (void) erts_port_task_schedule(p->id,
- &p->timeout_task,
- ERTS_PORT_TASK_TIMEOUT,
- (ErlDrvEvent) -1,
- NULL);
+ erts_port_task_schedule(p->common.id,
+ &p->timeout_task,
+ ERTS_PORT_TASK_TIMEOUT);
}
ErlDrvTermData driver_mk_term_nil(void)
@@ -2841,9 +5080,9 @@ ErlDrvTermData driver_mk_term_nil(void)
return driver_term_nil;
}
-void driver_report_exit(int ix, int status)
+void driver_report_exit(ErlDrvPort ix, int status)
{
- Port* prt = erts_drvport2port(ix);
+ Port* prt = erts_drvport2port(ix, NULL);
Eterm* hp;
Eterm tuple;
Process *rp;
@@ -2856,7 +5095,7 @@ void driver_report_exit(int ix, int status)
ERTS_SMP_CHK_NO_PROC_LOCKS;
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
- pid = prt->connected;
+ pid = ERTS_PORT_GET_CONNECTED(prt);
ASSERT(is_internal_pid(pid));
rp = (scheduler
@@ -2869,7 +5108,7 @@ void driver_report_exit(int ix, int status)
tuple = TUPLE2(hp, am_exit_status, make_small(status));
hp += 3;
- tuple = TUPLE2(hp, prt->id, tuple);
+ tuple = TUPLE2(hp, prt->common.id, tuple);
erts_queue_message(rp, &rp_locks, bp, tuple, am_undefined
#ifdef USE_VM_PROBES
@@ -2882,28 +5121,6 @@ void driver_report_exit(int ix, int status)
erts_smp_proc_dec_refc(rp);
}
-
-static ERTS_INLINE int
-deliver_term_check_port(ErlDrvPort drvport)
-{
- int res;
- int ix = (int) drvport;
- if (ix < 0 || erts_max_ports <= ix)
- res = -1; /* invalid */
- else {
- Port* prt = &erts_port[ix];
- erts_smp_port_state_lock(prt);
- if (!(prt->status & ERTS_PORT_SFLGS_INVALID_LOOKUP))
- res = 1; /* ok */
- else if (prt->status & ERTS_PORT_SFLG_CLOSING)
- res = 0; /* closing */
- else
- res = -1; /* invalid (dead) */
- erts_smp_port_state_unlock(prt);
- }
- return res;
-}
-
#define ERTS_B2T_STATES_DEF_STATES_SZ 5
#define ERTS_B2T_STATES_DEF_STATES_INC 100
@@ -2991,10 +5208,7 @@ cleanup_b2t_states(struct b2t_states__ *b2tsp)
*/
static int
-driver_deliver_term(ErlDrvPort port,
- Eterm to,
- ErlDrvTermData* data,
- int len)
+driver_deliver_term(Eterm to, ErlDrvTermData* data, int len)
{
#define ERTS_DDT_FAIL do { res = -1; goto done; } while (0)
Uint need = 0;
@@ -3196,11 +5410,8 @@ driver_deliver_term(ErlDrvPort port,
b2t.ix = 0;
/*
- * The term is OK. Go ahead and validate the port and process.
+ * The term is OK. Go ahead and validate the process.
*/
- res = deliver_term_check_port(port);
- if (res <= 0)
- goto done;
/*
* Increase refc on proc if done from a non-scheduler thread.
@@ -3470,25 +5681,115 @@ driver_deliver_term(ErlDrvPort port,
#undef ERTS_DDT_FAIL
}
+static ERTS_INLINE int
+deliver_term_check_port(ErlDrvTermData port_id, Eterm *connected_p)
+{
+#ifdef ERTS_SMP
+ ErtsThrPrgrDelayHandle dhndl = erts_thr_progress_unmanaged_delay();
+#endif
+ Port *prt = erts_port_lookup_raw((Eterm) port_id);
+ erts_aint32_t state = erts_atomic32_read_nob(&prt->state);
+ if (connected_p) {
+#ifdef ERTS_SMP
+ if (dhndl != ERTS_THR_PRGR_DHANDLE_MANAGED)
+ ETHR_MEMBAR(ETHR_LoadLoad);
+#endif
+ *connected_p = ERTS_PORT_GET_CONNECTED(prt);
+ }
+#ifdef ERTS_SMP
+ if (dhndl != ERTS_THR_PRGR_DHANDLE_MANAGED) {
+ erts_thr_progress_unmanaged_continue(dhndl);
+ ETHR_MEMBAR(ETHR_LoadLoad|ETHR_LoadStore);
+ }
+#endif
+ ERTS_SMP_LC_ASSERT(dhndl == ERTS_THR_PRGR_DHANDLE_MANAGED
+ ? erts_lc_is_port_locked(prt)
+ : !erts_lc_is_port_locked(prt));
+ return ((state & ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP)
+ ? -1
+ : ((state & ERTS_PORT_SFLG_CLOSING) ? 0 : 1));
+}
+
+int erl_drv_output_term(ErlDrvTermData port_id, ErlDrvTermData* data, int len)
+{
+ /* May be called from arbitrary thread */
+ Eterm connected;
+ int res = deliver_term_check_port(port_id, &connected);
+ if (res <= 0)
+ return res;
+ return driver_deliver_term(connected, data, len);
+}
+/*
+ * driver_output_term() is deprecated, and has been scheduled for
+ * removal in OTP-R17. It is replaced by erl_drv_output_term()
+ * above.
+ */
int
-driver_output_term(ErlDrvPort ix, ErlDrvTermData* data, int len)
+driver_output_term(ErlDrvPort drvport, ErlDrvTermData* data, int len)
{
- Port* prt = erts_drvport2port(ix);
+ erts_aint32_t state;
+ Port* prt;
ERTS_SMP_CHK_NO_PROC_LOCKS;
+ /* NOTE! It *not* safe to access 'drvport' from unmanaged threads. */
+ prt = erts_drvport2port(drvport, &state);
+ if (!prt)
+ return -1; /* invalid (dead) */
+ ERTS_SMP_CHK_NO_PROC_LOCKS;
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
-
- if (prt == NULL)
+ if (state & ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP)
return -1;
- return driver_deliver_term(ix, prt->connected, data, len);
+ else if (state & ERTS_PORT_SFLG_CLOSING)
+ return 0;
+
+ return driver_deliver_term(ERTS_PORT_GET_CONNECTED(prt), data, len);
}
+int erl_drv_send_term(ErlDrvTermData port_id,
+ ErlDrvTermData to,
+ ErlDrvTermData* data,
+ int len)
+{
+ /* May be called from arbitrary thread */
+ int res = deliver_term_check_port(port_id, NULL);
+ if (res <= 0)
+ return res;
+ return driver_deliver_term(to, data, len);
+}
+/*
+ * driver_send_term() is deprecated, and has been scheduled for
+ * removal in OTP-R17. It is replaced by erl_drv_send_term() above.
+ */
int
-driver_send_term(ErlDrvPort ix, ErlDrvTermData to, ErlDrvTermData* data, int len)
+driver_send_term(ErlDrvPort drvport,
+ ErlDrvTermData to,
+ ErlDrvTermData* data,
+ int len)
{
- return driver_deliver_term(ix, to, data, len);
+ /*
+ * NOTE! It is *not* safe to access the 'drvport' parameter
+ * from unmanaged threads. Also note that it is impossible
+ * to make this access safe without using a less efficient
+ * internal data representation for ErlDrvPort.
+ */
+ ERTS_SMP_CHK_NO_PROC_LOCKS;
+#ifdef ERTS_SMP
+ if (erts_thr_progress_is_managed_thread())
+#endif
+ {
+ erts_aint32_t state;
+ Port* prt = erts_drvport2port(drvport, &state);
+ if (!prt)
+ return -1; /* invalid (dead) */
+ ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
+ if (state & ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP)
+ return -1;
+ else if (state & ERTS_PORT_SFLG_CLOSING)
+ return 0;
+ }
+ return driver_deliver_term(to, data, len);
}
@@ -3500,26 +5801,27 @@ driver_send_term(ErlDrvPort ix, ErlDrvTermData to, ErlDrvTermData* data, int len
int driver_output_binary(ErlDrvPort ix, char* hbuf, ErlDrvSizeT hlen,
ErlDrvBinary* bin, ErlDrvSizeT offs, ErlDrvSizeT len)
{
- Port* prt = erts_drvport2port(ix);
+ erts_aint32_t state;
+ Port* prt = erts_drvport2port(ix, &state);
ERTS_SMP_CHK_NO_PROC_LOCKS;
if (prt == NULL)
return -1;
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
- if (prt->status & ERTS_PORT_SFLG_CLOSING)
+ if (state & ERTS_PORT_SFLG_CLOSING)
return 0;
prt->bytes_in += (hlen + len);
erts_smp_atomic_add_nob(&erts_bytes_in, (erts_aint_t) (hlen + len));
- if (prt->status & ERTS_PORT_SFLG_DISTRIBUTION) {
+ if (state & ERTS_PORT_SFLG_DISTRIBUTION) {
return erts_net_message(prt,
prt->dist_entry,
(byte*) hbuf, hlen,
(byte*) (bin->orig_bytes+offs), len);
}
else
- deliver_bin_message(prt, prt->connected,
+ deliver_bin_message(prt, ERTS_PORT_GET_CONNECTED(prt),
hbuf, hlen, bin, offs, len);
return 0;
}
@@ -3534,7 +5836,8 @@ int driver_output_binary(ErlDrvPort ix, char* hbuf, ErlDrvSizeT hlen,
int driver_output2(ErlDrvPort ix, char* hbuf, ErlDrvSizeT hlen,
char* buf, ErlDrvSizeT len)
{
- Port* prt = erts_drvport2port(ix);
+ erts_aint32_t state;
+ Port* prt = erts_drvport2port(ix, &state);
ERTS_SMP_CHK_NO_PROC_LOCKS;
@@ -3543,12 +5846,12 @@ int driver_output2(ErlDrvPort ix, char* hbuf, ErlDrvSizeT hlen,
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
- if (prt->status & ERTS_PORT_SFLG_CLOSING)
+ if (state & ERTS_PORT_SFLG_CLOSING)
return 0;
prt->bytes_in += (hlen + len);
erts_smp_atomic_add_nob(&erts_bytes_in, (erts_aint_t) (hlen + len));
- if (prt->status & ERTS_PORT_SFLG_DISTRIBUTION) {
+ if (state & ERTS_PORT_SFLG_DISTRIBUTION) {
if (len == 0)
return erts_net_message(prt,
prt->dist_entry,
@@ -3560,10 +5863,12 @@ int driver_output2(ErlDrvPort ix, char* hbuf, ErlDrvSizeT hlen,
(byte*) hbuf, hlen,
(byte*) buf, len);
}
- else if(prt->status & ERTS_PORT_SFLG_LINEBUF_IO)
- deliver_linebuf_message(prt, prt->connected, hbuf, hlen, buf, len);
+ else if (state & ERTS_PORT_SFLG_LINEBUF_IO)
+ deliver_linebuf_message(prt, state, ERTS_PORT_GET_CONNECTED(prt),
+ hbuf, hlen, buf, len);
else
- deliver_read_message(prt, prt->connected, hbuf, hlen, buf, len, 0);
+ deliver_read_message(prt, state, ERTS_PORT_GET_CONNECTED(prt),
+ hbuf, hlen, buf, len, 0);
return 0;
}
@@ -3584,6 +5889,7 @@ int driver_outputv(ErlDrvPort ix, char* hbuf, ErlDrvSizeT hlen,
SysIOVec* iov;
ErlDrvBinary** binv;
Port* prt;
+ erts_aint32_t state;
ERTS_SMP_CHK_NO_PROC_LOCKS;
@@ -3596,13 +5902,13 @@ int driver_outputv(ErlDrvPort ix, char* hbuf, ErlDrvSizeT hlen,
if (hlen < 0)
hlen = 0;
- prt = erts_drvport2port(ix);
+ prt = erts_drvport2port(ix, &state);
if (prt == NULL)
return -1;
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
- if (prt->status & ERTS_PORT_SFLG_CLOSING)
+ if (state & ERTS_PORT_SFLG_CLOSING)
return 0;
/* size > 0 ! */
@@ -3627,7 +5933,8 @@ int driver_outputv(ErlDrvPort ix, char* hbuf, ErlDrvSizeT hlen,
/* XXX handle distribution !!! */
prt->bytes_in += (hlen + size);
erts_smp_atomic_add_nob(&erts_bytes_in, (erts_aint_t) (hlen + size));
- deliver_vec_message(prt, prt->connected, hbuf, hlen, binv, iov, n, size);
+ deliver_vec_message(prt, ERTS_PORT_GET_CONNECTED(prt), hbuf, hlen,
+ binv, iov, n, size);
return 0;
}
@@ -3738,8 +6045,7 @@ ErlDrvBinary* driver_realloc_binary(ErlDrvBinary* bin, ErlDrvSizeT size)
}
-void driver_free_binary(dbin)
-ErlDrvBinary* dbin;
+void driver_free_binary(ErlDrvBinary* dbin)
{
Binary *bin;
if (!dbin) {
@@ -3835,6 +6141,7 @@ static ERTS_INLINE void pdl_destroy(ErlDrvPDL pdl)
{
ERTS_LC_ASSERT(driver_pdl_get_refc(pdl) == 0);
erts_mtx_destroy(&pdl->mtx);
+ erts_port_dec_refc(pdl->prt);
erts_free(ERTS_ALC_T_PORT_DATA_LOCK, pdl);
}
@@ -3872,16 +6179,18 @@ ErlDrvPDL
driver_pdl_create(ErlDrvPort dp)
{
ErlDrvPDL pdl;
- Port *pp = erts_drvport2port(dp);
+ Port *pp = erts_drvport2port(dp, NULL);
if (!pp || pp->port_data_lock)
return NULL;
pdl = erts_alloc(ERTS_ALC_T_PORT_DATA_LOCK,
sizeof(struct erl_drv_port_data_lock));
erts_mtx_init(&pdl->mtx, "port_data_lock");
pdl_init_refc(pdl);
+ erts_port_inc_refc(pp);
+ pdl->prt = pp;
pp->port_data_lock = pdl;
#ifdef HARDDEBUG
- erts_fprintf(stderr, "driver_pdl_create(%T) -> 0x%08X\r\n",pp->id,(unsigned) pdl);
+ erts_fprintf(stderr, "driver_pdl_create(%T) -> 0x%08X\r\n",pp->common.id,(unsigned) pdl);
#endif
return pdl;
}
@@ -4319,33 +6628,33 @@ static ERTS_INLINE void
drv_cancel_timer(Port *prt)
{
#ifdef ERTS_SMP
- erts_cancel_smp_ptimer(prt->ptimer);
+ erts_cancel_smp_ptimer(prt->common.u.alive.ptimer);
#else
- erts_cancel_timer(&prt->tm);
+ erts_cancel_timer(&prt->common.u.alive.tm);
#endif
if (erts_port_task_is_scheduled(&prt->timeout_task))
- erts_port_task_abort(prt->id, &prt->timeout_task);
+ erts_port_task_abort(&prt->timeout_task);
}
int driver_set_timer(ErlDrvPort ix, unsigned long t)
{
- Port* prt = erts_drvport2port(ix);
+ Port* prt = erts_drvport2port(ix, NULL);
ERTS_SMP_CHK_NO_PROC_LOCKS;
if (prt == NULL)
return -1;
- ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
+
if (prt->drv_ptr->timeout == NULL)
return -1;
drv_cancel_timer(prt);
#ifdef ERTS_SMP
- erts_create_smp_ptimer(&prt->ptimer,
- prt->id,
+ erts_create_smp_ptimer(&prt->common.u.alive.ptimer,
+ prt->common.id,
(ErlTimeoutProc) schedule_port_timeout,
t);
#else
- erts_set_timer(&prt->tm,
+ erts_set_timer(&prt->common.u.alive.tm,
(ErlTimeoutProc) schedule_port_timeout,
NULL,
prt,
@@ -4356,7 +6665,7 @@ int driver_set_timer(ErlDrvPort ix, unsigned long t)
int driver_cancel_timer(ErlDrvPort ix)
{
- Port* prt = erts_drvport2port(ix);
+ Port* prt = erts_drvport2port(ix, NULL);
if (prt == NULL)
return -1;
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
@@ -4368,7 +6677,7 @@ int driver_cancel_timer(ErlDrvPort ix)
int
driver_read_timer(ErlDrvPort ix, unsigned long* t)
{
- Port* prt = erts_drvport2port(ix);
+ Port* prt = erts_drvport2port(ix, NULL);
ERTS_SMP_CHK_NO_PROC_LOCKS;
@@ -4376,9 +6685,11 @@ driver_read_timer(ErlDrvPort ix, unsigned long* t)
return -1;
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
#ifdef ERTS_SMP
- *t = prt->ptimer ? erts_time_left(&prt->ptimer->timer.tm) : 0;
+ *t = (prt->common.u.alive.ptimer
+ ? erts_time_left(&prt->common.u.alive.ptimer->timer.tm)
+ : 0);
#else
- *t = erts_time_left(&prt->tm);
+ *t = erts_time_left(&prt->common.u.alive.tm);
#endif
return 0;
}
@@ -4429,8 +6740,8 @@ static int do_driver_monitor_process(Port *prt,
}
ref = erts_make_ref_in_buffer(buf);
- erts_add_monitor(&(prt->monitors), MON_ORIGIN, ref, rp->id, NIL);
- erts_add_monitor(&(rp->monitors), MON_TARGET, ref, prt->id, NIL);
+ erts_add_monitor(&ERTS_P_MONITORS(prt), MON_ORIGIN, ref, rp->common.id, NIL);
+ erts_add_monitor(&ERTS_P_MONITORS(rp), MON_TARGET, ref, prt->common.id, NIL);
erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
ref_to_driver_monitor(ref,monitor);
@@ -4440,31 +6751,22 @@ static int do_driver_monitor_process(Port *prt,
/*
* This can be called from a non scheduler thread iff a port_data_lock exists
*/
-int driver_monitor_process(ErlDrvPort port,
+int driver_monitor_process(ErlDrvPort drvport,
ErlDrvTermData process,
ErlDrvMonitor *monitor)
{
Port *prt;
int ret;
- Uint32 status;
+ erts_aint32_t state;
+#if !HEAP_ON_C_STACK || (defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK))
ErtsSchedulerData *sched = erts_get_scheduler_data();
- int ix = (int) port;
- if (ix < 0 || erts_max_ports <= ix) {
- return -1;
- }
- prt = &erts_port[ix];
+#endif
- DRV_MONITOR_LOCK_PDL(prt);
+ prt = DRV_MONITOR_LOOKUP_PORT_LOCK_PDL(drvport);
- if (sched) {
- status = erts_port[ix].status;
- } else {
- erts_smp_port_state_lock(prt);
- status = erts_port[ix].status;
- erts_smp_port_state_unlock(prt);
- }
+ state = erts_atomic32_read_nob(&prt->state);
- if (status & ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP) {
+ if (state & ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP) {
DRV_MONITOR_UNLOCK_PDL(prt);
return -1;
}
@@ -4502,7 +6804,7 @@ static int do_driver_demonitor_process(Port *prt, Eterm *buf,
memcpy(buf,monitor,sizeof(Eterm)*REF_THING_SIZE);
ref = make_internal_ref(buf);
- mon = erts_lookup_monitor(prt->monitors, ref);
+ mon = erts_lookup_monitor(ERTS_P_MONITORS(prt), ref);
if (mon == NULL) {
return 1;
}
@@ -4514,13 +6816,13 @@ static int do_driver_demonitor_process(Port *prt, Eterm *buf,
to,
ERTS_PROC_LOCK_LINK,
ERTS_P2P_FLG_ALLOW_OTHER_X);
- mon = erts_remove_monitor(&(prt->monitors), ref);
+ mon = erts_remove_monitor(&ERTS_P_MONITORS(prt), ref);
if (mon) {
erts_destroy_monitor(mon);
}
if (rp) {
ErtsMonitor *rmon;
- rmon = erts_remove_monitor(&(rp->monitors), ref);
+ rmon = erts_remove_monitor(&ERTS_P_MONITORS(rp), ref);
erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
if (rmon != NULL) {
erts_destroy_monitor(rmon);
@@ -4529,30 +6831,21 @@ static int do_driver_demonitor_process(Port *prt, Eterm *buf,
return 0;
}
-int driver_demonitor_process(ErlDrvPort port,
+int driver_demonitor_process(ErlDrvPort drvport,
const ErlDrvMonitor *monitor)
{
Port *prt;
int ret;
- Uint32 status;
+ erts_aint32_t state;
+#if !HEAP_ON_C_STACK || (defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK))
ErtsSchedulerData *sched = erts_get_scheduler_data();
- int ix = (int) port;
- if (ix < 0 || erts_max_ports <= ix) {
- return -1;
- }
- prt = &erts_port[ix];
+#endif
- DRV_MONITOR_LOCK_PDL(prt);
+ prt = DRV_MONITOR_LOOKUP_PORT_LOCK_PDL(drvport);
- if (sched) {
- status = erts_port[ix].status;
- } else {
- erts_smp_port_state_lock(prt);
- status = erts_port[ix].status;
- erts_smp_port_state_unlock(prt);
- }
+ state = erts_atomic32_read_nob(&prt->state);
- if (status & ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP) {
+ if (state & ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP) {
DRV_MONITOR_UNLOCK_PDL(prt);
return -1;
}
@@ -4588,7 +6881,7 @@ static ErlDrvTermData do_driver_get_monitored_process(Port *prt, Eterm *buf,
memcpy(buf,monitor,sizeof(Eterm)*REF_THING_SIZE);
ref = make_internal_ref(buf);
- mon = erts_lookup_monitor(prt->monitors, ref);
+ mon = erts_lookup_monitor(ERTS_P_MONITORS(prt), ref);
if (mon == NULL) {
return driver_term_nil;
}
@@ -4599,30 +6892,20 @@ static ErlDrvTermData do_driver_get_monitored_process(Port *prt, Eterm *buf,
}
-ErlDrvTermData driver_get_monitored_process(ErlDrvPort port,
+ErlDrvTermData driver_get_monitored_process(ErlDrvPort drvport,
const ErlDrvMonitor *monitor)
{
Port *prt;
ErlDrvTermData ret;
- Uint32 status;
+ erts_aint32_t state;
+#if !HEAP_ON_C_STACK || (defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK))
ErtsSchedulerData *sched = erts_get_scheduler_data();
- int ix = (int) port;
- if (ix < 0 || erts_max_ports <= ix) {
- return driver_term_nil;
- }
- prt = &erts_port[ix];
-
- DRV_MONITOR_LOCK_PDL(prt);
+#endif
- if (sched) {
- status = erts_port[ix].status;
- } else {
- erts_smp_port_state_lock(prt);
- status = erts_port[ix].status;
- erts_smp_port_state_unlock(prt);
- }
+ prt = DRV_MONITOR_LOOKUP_PORT_LOCK_PDL(drvport);
- if (status & ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP) {
+ state = erts_atomic32_read_nob(&prt->state);
+ if (state & ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP) {
DRV_MONITOR_UNLOCK_PDL(prt);
return driver_term_nil;
}
@@ -4667,7 +6950,7 @@ void erts_fire_port_monitor(Port *prt, Eterm ref)
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
ASSERT(prt->drv_ptr != NULL);
DRV_MONITOR_LOCK_PDL(prt);
- if (erts_lookup_monitor(prt->monitors,ref) == NULL) {
+ if (erts_lookup_monitor(ERTS_P_MONITORS(prt), ref) == NULL) {
DRV_MONITOR_UNLOCK_PDL(prt);
return;
}
@@ -4677,7 +6960,7 @@ void erts_fire_port_monitor(Port *prt, Eterm ref)
DRV_MONITOR_UNLOCK_PDL(prt);
#ifdef USE_VM_PROBES
if (DTRACE_ENABLED(driver_process_exit)) {
- DTRACE_FORMAT_COMMON_PID_AND_PORT(prt->connected, prt)
+ DTRACE_FORMAT_COMMON_PID_AND_PORT(ERTS_PORT_GET_CONNECTED(prt), prt)
DTRACE3(driver_process_exit, process_str, port_str, prt->name);
}
#endif
@@ -4686,7 +6969,7 @@ void erts_fire_port_monitor(Port *prt, Eterm ref)
erts_unblock_fpe(fpe_was_unmasked);
DRV_MONITOR_LOCK_PDL(prt);
/* remove monitor *after* callback */
- rmon = erts_remove_monitor(&(prt->monitors),ref);
+ rmon = erts_remove_monitor(&ERTS_P_MONITORS(prt), ref);
DRV_MONITOR_UNLOCK_PDL(prt);
if (rmon) {
erts_destroy_monitor(rmon);
@@ -4697,7 +6980,8 @@ void erts_fire_port_monitor(Port *prt, Eterm ref)
static int
driver_failure_term(ErlDrvPort ix, Eterm term, int eof)
{
- Port* prt = erts_drvport2port(ix);
+ erts_aint32_t state;
+ Port* prt = erts_drvport2port(ix, &state);
ERTS_SMP_CHK_NO_PROC_LOCKS;
@@ -4705,19 +6989,19 @@ driver_failure_term(ErlDrvPort ix, Eterm term, int eof)
return -1;
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
if (eof)
- flush_linebuf_messages(prt);
- if (prt->status & ERTS_PORT_SFLG_CLOSING) {
+ flush_linebuf_messages(prt, state);
+ if (state & ERTS_PORT_SFLG_CLOSING) {
terminate_port(prt);
- } else if (eof && (prt->status & ERTS_PORT_SFLG_SOFT_EOF)) {
- deliver_result(prt->id, prt->connected, am_eof);
+ } else if (eof && (state & ERTS_PORT_SFLG_SOFT_EOF)) {
+ deliver_result(prt->common.id, ERTS_PORT_GET_CONNECTED(prt), am_eof);
} else {
- /* XXX UGLY WORK AROUND, Let do_exit_port terminate the port */
+ /* XXX UGLY WORK AROUND, Let erts_deliver_port_exit() terminate the port */
if (prt->port_data_lock)
driver_pdl_lock(prt->port_data_lock);
prt->ioq.size = 0;
if (prt->port_data_lock)
driver_pdl_unlock(prt->port_data_lock);
- erts_do_exit_port(prt, prt->id, eof ? am_normal : term);
+ erts_deliver_port_exit(prt, prt->common.id, eof ? am_normal : term, 0);
}
return 0;
}
@@ -4730,23 +7014,23 @@ driver_failure_term(ErlDrvPort ix, Eterm term, int eof)
*/
int driver_exit(ErlDrvPort ix, int err)
{
- Port* prt = erts_drvport2port(ix);
+ Port* prt = erts_drvport2port(ix, NULL);
Process* rp;
ErtsLink *lnk, *rlnk = NULL;
+ Eterm connected;
ERTS_SMP_CHK_NO_PROC_LOCKS;
if (prt == NULL)
return -1;
- ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
-
- rp = erts_pid2proc(NULL, 0, prt->connected, ERTS_PROC_LOCK_LINK);
+ connected = ERTS_PORT_GET_CONNECTED(prt);
+ rp = erts_pid2proc(NULL, 0, connected, ERTS_PROC_LOCK_LINK);
if (rp) {
- rlnk = erts_remove_link(&(rp->nlinks),prt->id);
+ rlnk = erts_remove_link(&ERTS_P_LINKS(rp),prt->common.id);
}
- lnk = erts_remove_link(&(prt->nlinks),prt->connected);
+ lnk = erts_remove_link(&ERTS_P_LINKS(prt), connected);
#ifdef ERTS_SMP
if (rp)
@@ -4765,7 +7049,8 @@ int driver_exit(ErlDrvPort ix, int err)
return driver_failure_term(ix, am_normal, 0);
else {
char* err_str = erl_errno_id(err);
- Eterm am_err = am_atom_put(err_str, sys_strlen(err_str));
+ Eterm am_err = erts_atom_put((byte *) err_str, sys_strlen(err_str),
+ ERTS_ATOM_ENC_LATIN1, 1);
return driver_failure_term(ix, am_err, 0);
}
}
@@ -4778,8 +7063,12 @@ int driver_failure(ErlDrvPort ix, int code)
int driver_failure_atom(ErlDrvPort ix, char* string)
{
- Eterm am = am_atom_put(string, strlen(string));
- return driver_failure_term(ix, am, 0);
+ return driver_failure_term(ix,
+ erts_atom_put((byte *) string,
+ strlen(string),
+ ERTS_ATOM_ENC_LATIN1,
+ 1),
+ 0);
}
int driver_failure_posix(ErlDrvPort ix, int err)
@@ -4796,31 +7085,34 @@ int driver_failure_eof(ErlDrvPort ix)
ErlDrvTermData driver_mk_atom(char* string)
{
- Eterm am = am_atom_put(string, sys_strlen(string));
+ Eterm am = erts_atom_put((byte *) string,
+ sys_strlen(string),
+ ERTS_ATOM_ENC_LATIN1,
+ 1);
ERTS_SMP_CHK_NO_PROC_LOCKS;
return (ErlDrvTermData) am;
}
ErlDrvTermData driver_mk_port(ErlDrvPort ix)
{
- Port* prt = erts_drvport2port(ix);
+ Port* prt = erts_drvport2port(ix, NULL);
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
- return (ErlDrvTermData) prt->id;
+ return (ErlDrvTermData) prt->common.id;
}
ErlDrvTermData driver_connected(ErlDrvPort ix)
{
- Port* prt = erts_drvport2port(ix);
+ Port* prt = erts_drvport2port(ix, NULL);
ERTS_SMP_CHK_NO_PROC_LOCKS;
if (prt == NULL)
return NIL;
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
- return prt->connected;
+ return ERTS_PORT_GET_CONNECTED(prt);
}
ErlDrvTermData driver_caller(ErlDrvPort ix)
{
- Port* prt = erts_drvport2port(ix);
+ Port* prt = erts_drvport2port(ix, NULL);
ERTS_SMP_CHK_NO_PROC_LOCKS;
if (prt == NULL)
return NIL;
@@ -4830,25 +7122,25 @@ ErlDrvTermData driver_caller(ErlDrvPort ix)
int driver_lock_driver(ErlDrvPort ix)
{
- Port* prt = erts_drvport2port(ix);
+ Port* prt = erts_drvport2port(ix, NULL);
DE_Handle* dh;
ERTS_SMP_CHK_NO_PROC_LOCKS;
- erts_smp_mtx_lock(&erts_driver_list_lock);
+ erts_smp_rwmtx_rwlock(&erts_driver_list_lock);
if (prt == NULL) {
- erts_smp_mtx_unlock(&erts_driver_list_lock);
+ erts_smp_rwmtx_rwunlock(&erts_driver_list_lock);
return -1;
}
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
if ((dh = (DE_Handle*)prt->drv_ptr->handle ) == NULL) {
- erts_smp_mtx_unlock(&erts_driver_list_lock);
+ erts_smp_rwmtx_rwunlock(&erts_driver_list_lock);
return -1;
}
erts_ddll_lock_driver(dh, prt->drv_ptr->name);
- erts_smp_mtx_unlock(&erts_driver_list_lock);
+ erts_smp_rwmtx_rwunlock(&erts_driver_list_lock);
return 0;
}
@@ -4858,7 +7150,7 @@ static int maybe_lock_driver_list(void)
void *rec_lock;
rec_lock = erts_smp_tsd_get(driver_list_lock_status_key);
if (rec_lock == 0) {
- erts_smp_mtx_lock(&erts_driver_list_lock);
+ erts_smp_rwmtx_rwlock(&erts_driver_list_lock);
return 1;
}
return 0;
@@ -4866,7 +7158,7 @@ static int maybe_lock_driver_list(void)
static void maybe_unlock_driver_list(int doit)
{
if (doit) {
- erts_smp_mtx_unlock(&erts_driver_list_lock);
+ erts_smp_rwmtx_rwunlock(&erts_driver_list_lock);
}
}
/*
@@ -5035,7 +7327,7 @@ no_event_callback(ErlDrvData drv_data, ErlDrvEvent event, ErlDrvEventData event_
{
Port *prt = get_current_port();
report_missing_drv_callback(prt, "Event", "event()");
- driver_event((ErlDrvPort) internal_port_index(prt->id), event, NULL);
+ driver_event((ErlDrvPort) prt, event, NULL);
}
static void
@@ -5043,7 +7335,7 @@ no_ready_input_callback(ErlDrvData drv_data, ErlDrvEvent event)
{
Port *prt = get_current_port();
report_missing_drv_callback(prt, "Input", "ready_input()");
- driver_select((ErlDrvPort) internal_port_index(prt->id), event,
+ driver_select((ErlDrvPort) prt, event,
(ERL_DRV_READ | ERL_DRV_USE_NO_CALLBACK), 0);
}
@@ -5052,7 +7344,7 @@ no_ready_output_callback(ErlDrvData drv_data, ErlDrvEvent event)
{
Port *prt = get_current_port();
report_missing_drv_callback(prt, "Output", "ready_output()");
- driver_select((ErlDrvPort) internal_port_index(prt->id), event,
+ driver_select((ErlDrvPort) prt, event,
(ERL_DRV_WRITE | ERL_DRV_USE_NO_CALLBACK), 0);
}
@@ -5087,13 +7379,16 @@ init_driver(erts_driver_t *drv, ErlDrvEntry *de, DE_Handle *handle)
drv->lock = NULL;
else {
drv->lock = erts_alloc(ERTS_ALC_T_DRIVER_LOCK,
- sizeof(erts_smp_mtx_t));
- erts_smp_mtx_init_x(drv->lock,
- "driver_lock",
+ sizeof(erts_mtx_t));
+ erts_mtx_init_x(drv->lock,
+ "driver_lock",
#if defined(ERTS_ENABLE_LOCK_CHECK) || defined(ERTS_ENABLE_LOCK_COUNT)
- am_atom_put(drv->name, sys_strlen(drv->name))
+ erts_atom_put((byte *) drv->name,
+ sys_strlen(drv->name),
+ ERTS_ATOM_ENC_LATIN1,
+ 1)
#else
- NIL
+ NIL
#endif
);
}
@@ -5165,7 +7460,7 @@ int erts_add_driver_entry(ErlDrvEntry *de, DE_Handle *handle, int driver_list_lo
int res;
if (!driver_list_locked) {
- erts_smp_mtx_lock(&erts_driver_list_lock);
+ erts_smp_rwmtx_rwlock(&erts_driver_list_lock);
}
dp->next = driver_list;
@@ -5194,7 +7489,7 @@ int erts_add_driver_entry(ErlDrvEntry *de, DE_Handle *handle, int driver_list_lo
if (!driver_list_locked) {
erts_smp_tsd_set(driver_list_lock_status_key, NULL);
- erts_smp_mtx_unlock(&erts_driver_list_lock);
+ erts_smp_rwmtx_rwunlock(&erts_driver_list_lock);
}
return res;
}
@@ -5207,7 +7502,7 @@ int remove_driver_entry(ErlDrvEntry *drv)
rec_lock = erts_smp_tsd_get(driver_list_lock_status_key);
if (rec_lock == NULL) {
- erts_smp_mtx_lock(&erts_driver_list_lock);
+ erts_smp_rwmtx_rwlock(&erts_driver_list_lock);
}
dp = driver_list;
while (dp && dp->entry != drv)
@@ -5215,7 +7510,7 @@ int remove_driver_entry(ErlDrvEntry *drv)
if (dp) {
if (dp->handle) {
if (rec_lock == NULL) {
- erts_smp_mtx_unlock(&erts_driver_list_lock);
+ erts_smp_rwmtx_rwunlock(&erts_driver_list_lock);
}
return -1;
}
@@ -5229,12 +7524,12 @@ int remove_driver_entry(ErlDrvEntry *drv)
}
erts_destroy_driver(dp);
if (rec_lock == NULL) {
- erts_smp_mtx_unlock(&erts_driver_list_lock);
+ erts_smp_rwmtx_rwunlock(&erts_driver_list_lock);
}
return 1;
}
if (rec_lock == NULL) {
- erts_smp_mtx_unlock(&erts_driver_list_lock);
+ erts_smp_rwmtx_rwunlock(&erts_driver_list_lock);
}
return 0;
}
@@ -5250,11 +7545,39 @@ int null_func(void)
int
erl_drv_putenv(char *key, char *value)
{
- return erts_write_env(key, value);
+ return erts_sys_putenv_raw(key, value);
}
int
erl_drv_getenv(char *key, char *value, size_t *value_size)
{
- return erts_sys_getenv(key, value, value_size);
+ return erts_sys_getenv_raw(key, value, value_size);
+}
+
+/* get heart_port
+ * used by erl_crash_dump
+ * - uses the fact that heart_port is registered when starting heart
+ */
+
+Port *erts_get_heart_port(void)
+{
+ int ix, max = erts_ptab_max(&erts_port);
+
+ for (ix = 0; ix < max; ix++) {
+ struct reg_proc *reg;
+ Port *port = erts_pix2port(ix);
+
+ if (!port)
+ continue;
+ /* only examine undead or alive ports */
+ if (erts_atomic32_read_nob(&port->state) & ERTS_PORT_SFLGS_DEAD)
+ continue;
+ /* immediate atom compare */
+ reg = port->common.u.alive.reg;
+ if (reg && reg->name == am_heart_port) {
+ return port;
+ }
+ }
+
+ return NULL;
}
diff --git a/erts/emulator/beam/module.c b/erts/emulator/beam/module.c
index 1ef71cda79..01856007ee 100644
--- a/erts/emulator/beam/module.c
+++ b/erts/emulator/beam/module.c
@@ -144,7 +144,7 @@ erts_put_module(Eterm mod)
ASSERT(is_atom(mod));
ERTS_SMP_LC_ASSERT(erts_initialized == 0
- || erts_is_code_ix_locked());
+ || erts_has_code_write_permission());
mod_tab = &module_tables[erts_staging_code_ix()];
e.module = atom_val(mod);
diff --git a/erts/emulator/beam/register.c b/erts/emulator/beam/register.c
index c02872ef80..757e2800e6 100644
--- a/erts/emulator/beam/register.c
+++ b/erts/emulator/beam/register.c
@@ -175,14 +175,14 @@ int erts_register_name(Process *c_p, Eterm name, Eterm id)
if (is_not_atom(name) || name == am_undefined)
return res;
- if (c_p->id == id) /* A very common case I think... */
+ if (c_p->common.id == id) /* A very common case I think... */
proc = c_p;
else {
if (is_not_internal_pid(id) && is_not_internal_port(id))
return res;
erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_MAIN);
if (is_internal_port(id)) {
- port = erts_id2port(id, NULL, 0);
+ port = erts_id2port(id);
if (!port)
goto done;
}
@@ -204,7 +204,7 @@ int erts_register_name(Process *c_p, Eterm name, Eterm id)
r.p = proc;
if (!proc)
goto done;
- if (proc->reg)
+ if (proc->common.u.alive.reg)
goto done;
r.pt = NULL;
}
@@ -212,7 +212,7 @@ int erts_register_name(Process *c_p, Eterm name, Eterm id)
ASSERT(!INVALID_PORT(port, id));
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(port));
r.pt = port;
- if (r.pt->reg)
+ if (r.pt->common.u.alive.reg)
goto done;
r.p = NULL;
}
@@ -224,23 +224,24 @@ int erts_register_name(Process *c_p, Eterm name, Eterm id)
if (IS_TRACED_FL(proc, F_TRACE_PROCS)) {
trace_proc(c_p, proc, am_register, name);
}
- proc->reg = rp;
+ proc->common.u.alive.reg = rp;
}
else if (port && rp->pt == port) {
if (IS_TRACED_FL(port, F_TRACE_PORTS)) {
trace_port(port, am_register, name);
}
- port->reg = rp;
+ port->common.u.alive.reg = rp;
}
- if ((rp->p && rp->p->id == id) || (rp->pt && rp->pt->id == id)) {
+ if ((rp->p && rp->p->common.id == id)
+ || (rp->pt && rp->pt->common.id == id)) {
res = 1;
}
done:
reg_write_unlock();
if (port)
- erts_smp_port_unlock(port);
+ erts_port_release(port);
if (c_p != proc) {
if (proc)
erts_smp_proc_unlock(proc, ERTS_PROC_LOCK_MAIN);
@@ -291,9 +292,9 @@ erts_whereis_name_to_id(Process *c_p, Eterm name)
* is read only.
*/
if (rp->p)
- res = rp->p->id;
+ res = rp->p->common.id;
else if (rp->pt)
- res = rp->pt->id;
+ res = rp->pt->common.id;
break;
}
b = b->next;
@@ -396,28 +397,26 @@ erts_whereis_name(Process *c_p,
if (!rp || !rp->pt)
*port = NULL;
else {
-#ifndef ERTS_SMP
- erts_smp_atomic_inc_nob(&rp->pt->refc);
-#else
+#ifdef ERTS_SMP
if (pending_port == rp->pt)
pending_port = NULL;
else {
if (pending_port) {
/* Ahh! Registered port changed while reg lock
was unlocked... */
- erts_smp_port_unlock(pending_port);
+ erts_port_release(pending_port);
pending_port = NULL;
}
if (erts_smp_port_trylock(rp->pt) == EBUSY) {
- Eterm id = rp->pt->id; /* id read only... */
+ Eterm id = rp->pt->common.id; /* id read only... */
/* Unlock all locks, acquire port lock, and restart... */
if (current_c_p_locks) {
erts_smp_proc_unlock(c_p, current_c_p_locks);
current_c_p_locks = 0;
}
reg_read_unlock();
- pending_port = erts_id2port(id, NULL, 0);
+ pending_port = erts_id2port(id);
goto restart;
}
}
@@ -431,7 +430,7 @@ erts_whereis_name(Process *c_p,
if (c_p && !current_c_p_locks)
erts_smp_proc_lock(c_p, c_p_locks);
if (pending_port)
- erts_smp_port_unlock(pending_port);
+ erts_port_release(pending_port);
#endif
reg_read_unlock();
@@ -493,8 +492,8 @@ int erts_unregister_name(Process *c_p,
current_c_p_locks = c_p_locks;
}
#endif
- if (c_p->reg) {
- r.name = c_p->reg->name;
+ if (c_p->common.u.alive.reg) {
+ r.name = c_p->common.u.alive.reg->name;
} else {
/* Name got unregistered while main lock was released */
res = 0;
@@ -505,24 +504,22 @@ int erts_unregister_name(Process *c_p,
if ((rp = (RegProc*) hash_get(&process_reg, (void*) &r)) != NULL) {
if (rp->pt) {
if (port != rp->pt) {
-#ifndef ERTS_SMP
- erts_smp_atomic_inc_nob(&rp->pt->refc);
-#else
+#ifdef ERTS_SMP
if (port) {
ASSERT(port != c_prt);
- erts_smp_port_unlock(port);
+ erts_port_release(port);
port = NULL;
}
if (erts_smp_port_trylock(rp->pt) == EBUSY) {
- Eterm id = rp->pt->id; /* id read only... */
+ Eterm id = rp->pt->common.id; /* id read only... */
/* Unlock all locks, acquire port lock, and restart... */
if (current_c_p_locks) {
erts_smp_proc_unlock(c_p, current_c_p_locks);
current_c_p_locks = 0;
}
reg_write_unlock();
- port = erts_id2port(id, NULL, 0);
+ port = erts_id2port(id);
goto restart;
}
#endif
@@ -532,7 +529,7 @@ int erts_unregister_name(Process *c_p,
ASSERT(rp->pt == port);
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(port));
- rp->pt->reg = NULL;
+ rp->pt->common.u.alive.reg = NULL;
if (IS_TRACED_FL(port, F_TRACE_PORTS)) {
trace_port(port, am_unregister, r.name);
@@ -549,7 +546,7 @@ int erts_unregister_name(Process *c_p,
ERTS_PROC_LOCK_MAIN);
current_c_p_locks = c_p_locks;
#endif
- rp->p->reg = NULL;
+ rp->p->common.u.alive.reg = NULL;
if (IS_TRACED_FL(rp->p, F_TRACE_PROCS)) {
trace_proc(c_p, rp->p, am_unregister, r.name);
}
@@ -568,7 +565,7 @@ int erts_unregister_name(Process *c_p,
reg_write_unlock();
if (c_prt != port) {
if (port) {
- erts_smp_port_unlock(port);
+ erts_port_release(port);
}
if (c_prt) {
erts_smp_port_lock(c_prt);
diff --git a/erts/emulator/beam/register.h b/erts/emulator/beam/register.h
index 38e8cfbf28..7170463375 100644
--- a/erts/emulator/beam/register.h
+++ b/erts/emulator/beam/register.h
@@ -24,26 +24,19 @@
#ifndef __REGPROC_H__
#define __REGPROC_H__
-#ifndef __SYS_H__
#include "sys.h"
-#endif
-
-#ifndef __HASH_H__
#include "hash.h"
-#endif
-
-#ifndef __PROCESS_H__
#include "erl_process.h"
-#endif
-
-struct port;
+#define ERL_PORT_GET_PORT_TYPE_ONLY__
+#include "erl_port.h"
+#undef ERL_PORT_GET_PORT_TYPE_ONLY__
typedef struct reg_proc
{
HashBucket bucket; /* MUST BE LOCATED AT TOP OF STRUCT!!! */
Process *p; /* The process registered (only one of this and
'pt' is non-NULL */
- struct port *pt; /* The port registered */
+ Port *pt; /* The port registered */
Eterm name; /* Atom name */
} RegProc;
@@ -55,12 +48,12 @@ int erts_register_name(Process *, Eterm, Eterm);
Eterm erts_whereis_name_to_id(Process *, Eterm);
void erts_whereis_name(Process *, ErtsProcLocks,
Eterm, Process**, ErtsProcLocks, int,
- struct port**);
+ Port**);
Process *erts_whereis_process(Process *,
ErtsProcLocks,
Eterm,
ErtsProcLocks,
int);
-int erts_unregister_name(Process *, ErtsProcLocks, struct port *, Eterm);
+int erts_unregister_name(Process *, ErtsProcLocks, Port *, Eterm);
#endif
diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h
index 0d3b910278..cecaff54a4 100644
--- a/erts/emulator/beam/sys.h
+++ b/erts/emulator/beam/sys.h
@@ -25,11 +25,6 @@
# define NO_FPE_SIGNALS
#endif
-/* xxxP __VXWORKS__ */
-#ifdef VXWORKS
-#include <vxWorks.h>
-#endif
-
#ifdef DISABLE_CHILD_WAITER_THREAD
#undef ENABLE_CHILD_WAITER_THREAD
#endif
@@ -43,8 +38,6 @@
#if defined (__WIN32__)
# include "erl_win_sys.h"
-#elif defined (VXWORKS)
-# include "erl_vxworks_sys.h"
#else
# include "erl_unix_sys.h"
#ifndef UNIX
@@ -123,6 +116,16 @@ typedef ERTS_SYS_FD_TYPE ErtsSysFdType;
# define ERTS_DECLARE_DUMMY(X) X
#endif
+#if !defined(__func__)
+# if !defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901L
+# if !defined(__GNUC__) || __GNUC__ < 2
+# define __func__ "[unknown_function]"
+# else
+# define __func__ __FUNCTION__
+# endif
+# endif
+#endif
+
#if defined(DEBUG) || defined(ERTS_ENABLE_LOCK_CHECK)
# undef ERTS_CAN_INLINE
# define ERTS_CAN_INLINE 0
@@ -182,12 +185,6 @@ void erl_assert_error(char* expr, char* file, int line);
# define const
#endif
-#ifdef VXWORKS
-/* Replace VxWorks' printf with a real one that does fprintf(stdout, ...) */
-int real_printf(const char *fmt, ...);
-# define printf real_printf
-#endif
-
#undef __deprecated
#if ERTS_AT_LEAST_GCC_VSN__(3, 0, 0)
# define __deprecated __attribute__((deprecated))
@@ -244,9 +241,11 @@ int real_printf(const char *fmt, ...);
#if SIZEOF_VOID_P == 8
#undef ARCH_32
#define ARCH_64
+#define ERTS_SIZEOF_TERM 8
#elif SIZEOF_VOID_P == 4
#define ARCH_32
#undef ARCH_64
+#define ERTS_SIZEOF_TERM 4
#else
#error Neither 32 nor 64 bit architecture
#endif
@@ -254,6 +253,8 @@ int real_printf(const char *fmt, ...);
# define HALFWORD_HEAP 1
# define HALFWORD_ASSERT 0
# define ASSERT_HALFWORD(COND) ASSERT(COND)
+# undef ERTS_SIZEOF_TERM
+# define ERTS_SIZEOF_TERM 4
#else
# define HALFWORD_HEAP 0
# define HALFWORD_ASSERT 0
@@ -380,6 +381,27 @@ typedef unsigned char byte;
#error 64-bit architecture, but no appropriate type to use for Uint64 and Sint64 found
#endif
+#ifdef WORDS_BIGENDIAN
+# define ERTS_HUINT_HVAL_HIGH 0
+# define ERTS_HUINT_HVAL_LOW 1
+#else
+# define ERTS_HUINT_HVAL_HIGH 1
+# define ERTS_HUINT_HVAL_LOW 0
+#endif
+#if ERTS_SIZEOF_TERM == 8
+typedef union {
+ Uint val;
+ Uint32 hval[2];
+} HUint;
+#elif ERTS_SIZEOF_TERM == 4
+typedef union {
+ Uint val;
+ Uint16 hval[2];
+} HUint;
+#else
+#error "Unsupported size of term"
+#endif
+
# define ERTS_EXTRA_DATA_ALIGN_SZ(X) \
(((size_t) 8) - (((size_t) (X)) & ((size_t) 7)))
@@ -486,38 +508,28 @@ static unsigned long zero_value = 0, one_value = 1;
# define SET_NONBLOCKING(fd) ioctlsocket((fd), FIONBIO, &one_value)
# else
-# ifdef VXWORKS
-# include <fcntl.h> /* xxxP added for O_WRONLY etc ... macro:s ... */
-# include <ioLib.h>
-static const int zero_value = 0, one_value = 1;
-# define SET_BLOCKING(fd) ioctl((fd), FIONBIO, (int)&zero_value)
-# define SET_NONBLOCKING(fd) ioctl((fd), FIONBIO, (int)&one_value)
-# define ERRNO_BLOCK EWOULDBLOCK
-
-# else
-# ifdef NB_FIONBIO /* Old BSD */
-# include <sys/ioctl.h>
+# ifdef NB_FIONBIO /* Old BSD */
+# include <sys/ioctl.h>
static const int zero_value = 0, one_value = 1;
-# define SET_BLOCKING(fd) ioctl((fd), FIONBIO, &zero_value)
-# define SET_NONBLOCKING(fd) ioctl((fd), FIONBIO, &one_value)
-# define ERRNO_BLOCK EWOULDBLOCK
-# else /* !NB_FIONBIO */
-# include <fcntl.h>
-# ifdef NB_O_NDELAY /* Nothing needs this? */
-# define NB_FLAG O_NDELAY
-# ifndef ERRNO_BLOCK /* allow override (e.g. EAGAIN) via Makefile */
-# define ERRNO_BLOCK EWOULDBLOCK
-# endif
-# else /* !NB_O_NDELAY */ /* The True Way - POSIX!:-) */
-# define NB_FLAG O_NONBLOCK
-# define ERRNO_BLOCK EAGAIN
-# endif /* !NB_O_NDELAY */
-# define SET_BLOCKING(fd) fcntl((fd), F_SETFL, \
- fcntl((fd), F_GETFL, 0) & ~NB_FLAG)
-# define SET_NONBLOCKING(fd) fcntl((fd), F_SETFL, \
- fcntl((fd), F_GETFL, 0) | NB_FLAG)
-# endif /* !NB_FIONBIO */
-# endif /* _WXWORKS_ */
+# define SET_BLOCKING(fd) ioctl((fd), FIONBIO, &zero_value)
+# define SET_NONBLOCKING(fd) ioctl((fd), FIONBIO, &one_value)
+# define ERRNO_BLOCK EWOULDBLOCK
+# else /* !NB_FIONBIO */
+# include <fcntl.h>
+# ifdef NB_O_NDELAY /* Nothing needs this? */
+# define NB_FLAG O_NDELAY
+# ifndef ERRNO_BLOCK /* allow override (e.g. EAGAIN) via Makefile */
+# define ERRNO_BLOCK EWOULDBLOCK
+# endif
+# else /* !NB_O_NDELAY */ /* The True Way - POSIX!:-) */
+# define NB_FLAG O_NONBLOCK
+# define ERRNO_BLOCK EAGAIN
+# endif /* !NB_O_NDELAY */
+# define SET_BLOCKING(fd) fcntl((fd), F_SETFL, \
+ fcntl((fd), F_GETFL, 0) & ~NB_FLAG)
+# define SET_NONBLOCKING(fd) fcntl((fd), F_SETFL, \
+ fcntl((fd), F_GETFL, 0) | NB_FLAG)
+# endif /* !NB_FIONBIO */
# endif /* !__WIN32__ */
#endif /* WANT_NONBLOCKING */
@@ -528,6 +540,10 @@ __decl_noreturn void __noreturn erl_exit(int n, char*, ...);
#define ERTS_ABORT_EXIT (INT_MIN + 1) /* no crash dump; only abort() */
#define ERTS_DUMP_EXIT (INT_MIN + 2) /* crash dump; then exit() */
+#define ERTS_INTERNAL_ERROR(What) \
+ erl_exit(ERTS_ABORT_EXIT, "%s:%d:%s(): Internal error: %s\n", \
+ __FILE__, __LINE__, __func__, What)
+
Eterm erts_check_io_info(void *p);
/* Size of misc memory allocated from system dependent code */
@@ -602,6 +618,7 @@ typedef struct _SysDriverOpts {
char *wd; /* Working directory. */
unsigned spawn_type; /* Bitfield of ERTS_SPAWN_DRIVER |
ERTS_SPAWN_EXTERNAL | both*/
+ int parallelism; /* Optimize for parallelism */
} SysDriverOpts;
extern char *erts_default_arg0;
@@ -641,7 +658,7 @@ typedef struct {
#define ERTS_SYS_DDLL_ERROR_INIT {NULL}
extern void erts_sys_ddll_free_error(ErtsSysDdllError*);
extern void erl_sys_ddll_init(void); /* to initialize mutexes etc */
-extern int erts_sys_ddll_open2(char *path, void **handle, ErtsSysDdllError*);
+extern int erts_sys_ddll_open2(const char *path, void **handle, ErtsSysDdllError*);
#define erts_sys_ddll_open(P,H) erts_sys_ddll_open2(P,H,NULL)
extern int erts_sys_ddll_open_noext(char *path, void **handle, ErtsSysDdllError*);
extern int erts_sys_ddll_load_driver_init(void *handle, void **function);
@@ -650,7 +667,7 @@ extern int erts_sys_ddll_close2(void *handle, ErtsSysDdllError*);
#define erts_sys_ddll_close(H) erts_sys_ddll_close2(H,NULL)
extern void *erts_sys_ddll_call_init(void *function);
extern void *erts_sys_ddll_call_nif_init(void *function);
-extern int erts_sys_ddll_sym2(void *handle, char *name, void **function, ErtsSysDdllError*);
+extern int erts_sys_ddll_sym2(void *handle, const char *name, void **function, ErtsSysDdllError*);
#define erts_sys_ddll_sym(H,N,F) erts_sys_ddll_sym2(H,N,F,NULL)
extern char *erts_sys_ddll_error(int code);
@@ -667,7 +684,7 @@ void erts_sys_schedule_interrupt_timed(int set, erts_short_time_t msec);
void erts_sys_main_thread(void);
#endif
-extern void erts_sys_prepare_crash_dump(void);
+extern int erts_sys_prepare_crash_dump(int secs);
extern void erts_sys_pre_init(void);
extern void erl_sys_init(void);
extern void erl_sys_args(int *argc, char **argv);
@@ -712,24 +729,30 @@ char * getenv_string(GETENV_STATE *);
void fini_getenv_state(GETENV_STATE *);
/* xxxP */
+#define SYS_DEFAULT_FLOAT_DECIMALS 20
void init_sys_float(void);
int sys_chars_to_double(char*, double*);
-int sys_double_to_chars(double, char*);
-void sys_get_pid(char *);
+int sys_double_to_chars(double, char*, size_t);
+int sys_double_to_chars_ext(double, char*, size_t, size_t);
+int sys_double_to_chars_fast(double, char*, int, int, int);
+void sys_get_pid(char *, size_t);
/* erts_sys_putenv() returns, 0 on success and a value != 0 on failure. */
-int erts_sys_putenv(char *key_value, int sep_ix);
+int erts_sys_putenv(char *key, char *value);
+/* Simple variant used from drivers, raw eightbit interface */
+int erts_sys_putenv_raw(char *key, char *value);
/* erts_sys_getenv() returns 0 on success (length of value string in
*size), a value > 0 if value buffer is too small (*size is set to needed
size), and a value < 0 on failure. */
int erts_sys_getenv(char *key, char *value, size_t *size);
+/* Simple variant used from drivers, raw eightbit interface */
+int erts_sys_getenv_raw(char *key, char *value, size_t *size);
/* erts_sys_getenv__() is only allowed to be used in early init phase */
int erts_sys_getenv__(char *key, char *value, size_t *size);
/* Easier to use, but not as efficient, environment functions */
char *erts_read_env(char *key);
void erts_free_read_env(void *value);
-int erts_write_env(char *key, char *value);
/* utils.c */
@@ -870,13 +893,6 @@ erts_refc_read(erts_refc_t *refcp, erts_aint_t min_val)
extern int erts_use_kernel_poll;
#endif
-#if defined(VXWORKS)
-/* NOTE! sys_calloc2 does not exist on other
- platforms than VxWorks and OSE */
-void* sys_calloc2(Uint, Uint);
-#endif /* VXWORKS || OSE */
-
-
#define sys_memcpy(s1,s2,n) memcpy(s1,s2,n)
#define sys_memmove(s1,s2,n) memmove(s1,s2,n)
#define sys_memcmp(s1,s2,n) memcmp(s1,s2,n)
@@ -983,43 +999,6 @@ void erl_bin_write(unsigned char *, int, int);
# define DEBUGF(x)
#endif
-
-#ifdef VXWORKS
-/* This includes redefines of malloc etc
- this should be done after sys_alloc, etc, above */
-# include "reclaim.h"
-/*********************Malloc and friends************************
- * There is a problem with the naming of malloc and friends,
- * malloc is used throughout sys.c and the resolver to mean save_alloc,
- * but it should actually mean either sys_alloc or sys_alloc2,
- * so the definitions from reclaim_master.h are not any
- * good, i redefine the malloc family here, although it's quite
- * ugly, actually it would be preferrable to use the
- * names sys_alloc and so on throughout the offending code, but
- * that will be saved as an later exercise...
- * I also add an own calloc, to make the BSD resolver source happy.
- ***************************************************************/
-/* Undefine malloc and friends */
-# ifdef malloc
-# undef malloc
-# endif
-# ifdef calloc
-# undef calloc
-# endif
-# ifdef realloc
-# undef realloc
-# endif
-# ifdef free
-# undef free
-# endif
-/* Redefine malloc and friends */
-# define malloc sys_alloc
-# define calloc sys_calloc
-# define realloc sys_realloc
-# define free sys_free
-
-#endif
-
#ifdef __WIN32__
#ifdef ARCH_64
#define ERTS_ALLOC_ALIGN_BYTES 16
@@ -1035,23 +1014,20 @@ void erl_bin_write(unsigned char *, int, int);
#ifdef __WIN32__
-
void call_break_handler(void);
char* last_error(void);
char* win32_errorstr(int);
-
-
#endif
/************************************************************************
* Find out the native filename encoding of the process (look at locale of
* Unix processes and just do UTF16 on windows
************************************************************************/
-#define ERL_FILENAME_UNKNOWN 0
-#define ERL_FILENAME_LATIN1 1
-#define ERL_FILENAME_UTF8 2
-#define ERL_FILENAME_UTF8_MAC 3
-#define ERL_FILENAME_WIN_WCHAR 4
+#define ERL_FILENAME_UNKNOWN (0)
+#define ERL_FILENAME_LATIN1 (1)
+#define ERL_FILENAME_UTF8 (2)
+#define ERL_FILENAME_UTF8_MAC (3)
+#define ERL_FILENAME_WIN_WCHAR (4)
int erts_get_native_filename_encoding(void);
/* The set function is only to be used by erl_init! */
@@ -1061,4 +1037,3 @@ int erts_get_user_requested_filename_encoding(void);
void erts_init_sys_common_misc(void);
#endif
-
diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c
index db6597dc7c..2a6a8efd4d 100644
--- a/erts/emulator/beam/utils.c
+++ b/erts/emulator/beam/utils.c
@@ -46,6 +46,7 @@
#include "erl_thr_queue.h"
#include "erl_sched_spec_pre_alloc.h"
#include "beam_bp.h"
+#include "erl_ptab.h"
#undef M_TRIM_THRESHOLD
#undef M_TOP_PAD
@@ -370,7 +371,7 @@ Eterm
erts_bld_atom(Uint **hpp, Uint *szp, char *str)
{
if (hpp)
- return am_atom_put(str, sys_strlen(str));
+ return erts_atom_put((byte *) str, sys_strlen(str), ERTS_ATOM_ENC_LATIN1, 1);
else
return THE_NON_VALUE;
}
@@ -3016,12 +3017,13 @@ buf_to_intlist(Eterm** hpp, char *buf, size_t len, Eterm tail)
** ;
**
** Return remaining bytes in buffer on success
-** -1 on overflow
-** -2 on type error (including that result would not be a whole number of bytes)
+** ERTS_IOLIST_TO_BUF_OVERFLOW on overflow
+** ERTS_IOLIST_TO_BUF_TYPE_ERROR on type error (including that result would not be a whole number of bytes)
*/
-int io_list_to_buf(Eterm obj, char* buf, int len)
+ErlDrvSizeT erts_iolist_to_buf(Eterm obj, char* buf, ErlDrvSizeT alloced_len)
{
+ ErlDrvSizeT len = (ErlDrvSizeT) alloced_len;
Eterm* objp;
DECLARE_ESTACK(s);
goto L_again;
@@ -3114,20 +3116,20 @@ int io_list_to_buf(Eterm obj, char* buf, int len)
L_type_error:
DESTROY_ESTACK(s);
- return -2;
+ return ERTS_IOLIST_TO_BUF_TYPE_ERROR;
L_overflow:
DESTROY_ESTACK(s);
- return -1;
+ return ERTS_IOLIST_TO_BUF_OVERFLOW;
}
/*
* Return 0 if successful, and non-zero if unsuccessful.
*/
-int erts_iolist_size(Eterm obj, Uint* sizep)
+int erts_iolist_size(Eterm obj, ErlDrvSizeT* sizep)
{
Eterm* objp;
- Uint size = 0;
+ Uint size = 0; /* Intentionally Uint due to halfword heap */
DECLARE_ESTACK(s);
goto L_again;
@@ -3179,7 +3181,7 @@ int erts_iolist_size(Eterm obj, Uint* sizep)
#undef SAFE_ADD
DESTROY_ESTACK(s);
- *sizep = size;
+ *sizep = (ErlDrvSizeT) size;
return ERTS_IOLIST_OK;
L_overflow_error:
@@ -3443,7 +3445,7 @@ erts_read_env(char *key)
char *value = erts_alloc(ERTS_ALC_T_TMP, value_len);
int res;
while (1) {
- res = erts_sys_getenv(key, value, &value_len);
+ res = erts_sys_getenv_raw(key, value, &value_len);
if (res <= 0)
break;
value = erts_realloc(ERTS_ALC_T_TMP, value, value_len);
@@ -3462,28 +3464,6 @@ erts_free_read_env(void *value)
erts_free(ERTS_ALC_T_TMP, value);
}
-int
-erts_write_env(char *key, char *value)
-{
- int ix, res;
- size_t key_len = sys_strlen(key), value_len = sys_strlen(value);
- char *key_value = erts_alloc_fnf(ERTS_ALC_T_TMP,
- key_len + 1 + value_len + 1);
- if (!key_value) {
- errno = ENOMEM;
- return -1;
- }
- sys_memcpy((void *) key_value, (void *) key, key_len);
- ix = key_len;
- key_value[ix++] = '=';
- sys_memcpy((void *) key_value, (void *) value, value_len);
- ix += value_len;
- key_value[ix] = '\0';
- res = erts_sys_putenv(key_value, key_len);
- erts_free(ERTS_ALC_T_TMP, key_value);
- return res;
-}
-
/*
* To be used to silence unused result warnings, but do not abuse it.
*/
diff --git a/erts/emulator/drivers/common/efile_drv.c b/erts/emulator/drivers/common/efile_drv.c
index 603d1d47b6..186af03eff 100644
--- a/erts/emulator/drivers/common/efile_drv.c
+++ b/erts/emulator/drivers/common/efile_drv.c
@@ -56,7 +56,8 @@
#define FILE_FDATASYNC 30
#define FILE_FADVISE 31
#define FILE_SENDFILE 32
-
+#define FILE_FALLOCATE 33
+#define FILE_CLOSE_ON_PORT_EXIT 34
/* Return codes */
#define FILE_RESP_OK 0
@@ -177,6 +178,7 @@ dt_private *get_dt_private(int);
#define MUTEX_LOCK(m) do { IF_THRDS { TRACE_DRIVER; driver_pdl_lock(m); } } while (0)
#define MUTEX_UNLOCK(m) do { IF_THRDS { TRACE_DRIVER; driver_pdl_unlock(m); } } while (0)
#else
+#define IF_THRDS if (0)
#define MUTEX_INIT(m, p)
#define MUTEX_LOCK(m)
#define MUTEX_UNLOCK(m)
@@ -206,6 +208,9 @@ dt_private *get_dt_private(int);
# define KEY(desc) (&(desc)->key)
#endif
+#ifndef MAX
+# define MAX(x, y) (((x) > (y)) ? (x) : (y))
+#endif
#ifdef FILENAMES_16BIT
#ifdef USE_VM_PROBES
@@ -425,6 +430,7 @@ struct t_data
int level;
void (*invoke)(void *);
void (*free)(void *);
+ void *data_to_free; /* used by FILE_CLOSE_ON_PORT_EXIT only */
int again;
int reply;
#ifdef USE_VM_PROBES
@@ -436,6 +442,7 @@ struct t_data
Efile_error errInfo;
int flags;
SWord fd;
+ int is_fd_unused;
/**/
Efile_info info;
EFILE_DIR_HANDLE dir_handle; /* Handle to open directory. */
@@ -500,6 +507,10 @@ struct t_data
Uint64 written;
} sendfile;
#endif /* HAVE_SENDFILE */
+ struct {
+ Sint64 offset;
+ Sint64 length;
+ } fallocate;
} c;
char b[1];
};
@@ -522,7 +533,7 @@ static void *ef_safe_alloc(Uint s)
static void *ef_safe_realloc(void *op, Uint s)
{
void *p = EF_REALLOC(op, s);
- if (!p) erl_exit(1, "efile drv: Can't reallocate %d bytes of memory\n", s);
+ if (!p) erl_exit(1, "efile drv: Can't reallocate %lu bytes of memory\n", (unsigned long)s);
return p;
}
@@ -778,11 +789,6 @@ file_start(ErlDrvPort port, char* command)
return (ErlDrvData) desc;
}
-static void free_data(void *data)
-{
- EF_FREE(data);
-}
-
static void do_close(int flags, SWord fd) {
if (flags & EFILE_COMPRESSED) {
erts_gzclose((gzFile)(fd));
@@ -800,25 +806,27 @@ static void invoke_close(void *data)
DTRACE_INVOKE_RETURN(FILE_CLOSE);
}
-/*********************************************************************
- * Driver entry point -> stop
- */
-static void
-file_stop(ErlDrvData e)
+static void free_data(void *data)
{
- file_descriptor* desc = (file_descriptor*)e;
-
- TRACE_C('p');
+ struct t_data *d = (struct t_data *) data;
- if (desc->fd != FILE_FD_INVALID) {
- do_close(desc->flags, desc->fd);
- desc->fd = FILE_FD_INVALID;
- desc->flags = 0;
- }
- if (desc->read_binp) {
- driver_free_binary(desc->read_binp);
+ switch (d->command) {
+ case FILE_OPEN:
+ if (d->is_fd_unused && d->fd != FILE_FD_INVALID) {
+ /* This is OK to do in scheduler thread because there can be no async op
+ ongoing for this fd here, as we exited during async open.
+ Ideally, this close should happen in an async thread too, but that would
+ require a substantial rewrite, as we are here because of a dead port and
+ cannot schedule async jobs for that port any more... */
+ do_close(d->flags, d->fd);
+ }
+ break;
+ case FILE_CLOSE_ON_PORT_EXIT:
+ EF_FREE(d->data_to_free);
+ break;
}
- EF_FREE(desc);
+
+ EF_FREE(data);
}
@@ -1859,6 +1867,9 @@ static void invoke_open(void *data)
}
d->result_ok = status;
+ if (!status) {
+ d->fd = FILE_FD_INVALID;
+ }
DTRACE_INVOKE_RETURN(FILE_OPEN);
}
@@ -1950,6 +1961,17 @@ static int flush_sendfile(file_descriptor *desc,void *_) {
#endif /* HAVE_SENDFILE */
+static void invoke_fallocate(void *data)
+{
+ struct t_data *d = (struct t_data *) data;
+ int fd = (int) d->fd;
+ Sint64 offset = d->c.fallocate.offset;
+ Sint64 length = d->c.fallocate.length;
+
+ d->again = 0;
+ d->result_ok = efile_fallocate(&d->errInfo, fd, offset, length);
+}
+
static void free_readdir(void *data)
{
struct t_data *d = (struct t_data *) data;
@@ -2213,6 +2235,47 @@ static int lseek_flush_read(file_descriptor *desc, int *errp
}
+/*********************************************************************
+ * Driver entry point -> stop
+ * The close has to be scheduled on async thread, so that currently active
+ * async operation does not suddenly have the ground disappearing under their feet...
+ */
+static void
+file_stop(ErlDrvData e)
+{
+ file_descriptor* desc = (file_descriptor*)e;
+
+ TRACE_C('p');
+
+ IF_THRDS {
+ flush_read(desc);
+ if (desc->fd != FILE_FD_INVALID) {
+ struct t_data *d = EF_SAFE_ALLOC(sizeof(struct t_data));
+ d->command = FILE_CLOSE_ON_PORT_EXIT;
+ d->reply = !0;
+ d->fd = desc->fd;
+ d->flags = desc->flags;
+ d->invoke = invoke_close;
+ d->free = free_data;
+ d->level = 2;
+ d->data_to_free = (void *) desc;
+ cq_enq(desc, d);
+ desc->fd = FILE_FD_INVALID;
+ desc->flags = 0;
+ cq_execute(desc);
+ }
+ } else {
+ if (desc->fd != FILE_FD_INVALID) {
+ do_close(desc->flags, desc->fd);
+ desc->fd = FILE_FD_INVALID;
+ desc->flags = 0;
+ }
+ if (desc->read_binp) {
+ driver_free_binary(desc->read_binp);
+ }
+ EF_FREE(desc);
+ }
+}
/*********************************************************************
* Driver entry point -> ready_async
@@ -2345,6 +2408,7 @@ file_async_ready(ErlDrvData e, ErlDrvThreadData data)
case FILE_RENAME:
case FILE_WRITE_INFO:
case FILE_FADVISE:
+ case FILE_FALLOCATE:
reply(desc, d->result_ok, &d->errInfo);
free_data(data);
break;
@@ -2370,8 +2434,10 @@ file_async_ready(ErlDrvData e, ErlDrvThreadData data)
if (!d->result_ok) {
reply_error(desc, &d->errInfo);
} else {
+ ASSERT(d->is_fd_unused);
desc->fd = d->fd;
desc->flags = d->flags;
+ d->is_fd_unused = 0;
reply_Uint(desc, d->fd);
}
free_data(data);
@@ -2433,7 +2499,6 @@ file_async_ready(ErlDrvData e, ErlDrvThreadData data)
}
free_readdir(data);
break;
- /* See file_stop */
case FILE_CLOSE:
if (d->reply) {
TRACE_C('K');
@@ -2493,6 +2558,15 @@ file_async_ready(ErlDrvData e, ErlDrvThreadData data)
}
break;
#endif
+ case FILE_CLOSE_ON_PORT_EXIT:
+ /* See file_stop. However this is never invoked after the port is killed. */
+ free_data(data);
+ EF_FREE(desc);
+ desc = NULL;
+ /* This is it for this port, so just send dtrace and return, avoid doing anything to the freed data */
+ DTRACE6(efile_drv_return, sched_i1, sched_i2, sched_utag,
+ command, result_ok, posix_errno);
+ return;
default:
abort();
}
@@ -2503,6 +2577,7 @@ file_async_ready(ErlDrvData e, ErlDrvThreadData data)
driver_set_timer(desc->port, desc->write_delay);
}
cq_execute(desc);
+
}
@@ -2597,7 +2672,7 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count)
#ifdef USE_VM_PROBES
dt_s1 = d->b;
dt_s2 = d->b + namelen;
- dt_utag = buf + namelen + FILENAME_BYTELEN(name) + FILENAME_CHARSIZE;
+ dt_utag = buf + namelen + FILENAME_BYTELEN(new_name) + FILENAME_CHARSIZE;
#endif
d->flags = desc->flags;
d->fd = fd;
@@ -2742,6 +2817,7 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count)
d->invoke = invoke_open;
d->free = free_data;
d->level = 2;
+ d->is_fd_unused = 1;
goto done;
}
@@ -2848,8 +2924,9 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count)
case FILE_READLINK:
{
- d = EF_SAFE_ALLOC(sizeof(struct t_data) - 1 + RESBUFSIZE + 1);
-
+ d = EF_SAFE_ALLOC(sizeof(struct t_data) - 1 +
+ MAX(RESBUFSIZE, (FILENAME_BYTELEN(name) +
+ FILENAME_CHARSIZE)) + 1);
FILENAME_COPY(d->b, name);
#ifdef USE_VM_PROBES
dt_s1 = d->b;
@@ -2864,7 +2941,9 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count)
case FILE_ALTNAME:
{
- d = EF_SAFE_ALLOC(sizeof(struct t_data) - 1 + RESBUFSIZE + 1);
+ d = EF_SAFE_ALLOC(sizeof(struct t_data) - 1 +
+ MAX(RESBUFSIZE, (FILENAME_BYTELEN(name) +
+ FILENAME_CHARSIZE)) + 1);
FILENAME_COPY(d->b, name);
#ifdef USE_VM_PROBES
dt_s1 = d->b;
@@ -2952,6 +3031,20 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count)
goto done;
}
+ case FILE_FALLOCATE:
+ {
+ d = EF_SAFE_ALLOC(sizeof(struct t_data));
+
+ d->fd = fd;
+ d->command = command;
+ d->invoke = invoke_fallocate;
+ d->free = free_data;
+ d->level = 2;
+ d->c.fallocate.offset = get_int64((uchar*) buf);
+ d->c.fallocate.length = get_int64(((uchar*) buf) + sizeof(Sint64));
+ goto done;
+ }
+
}
/*
diff --git a/erts/emulator/drivers/common/erl_efile.h b/erts/emulator/drivers/common/erl_efile.h
index 69ad02633c..b29b4f971c 100644
--- a/erts/emulator/drivers/common/erl_efile.h
+++ b/erts/emulator/drivers/common/erl_efile.h
@@ -185,3 +185,4 @@ int efile_fadvise(Efile_error* errInfo, int fd, Sint64 offset, Sint64 length,
int efile_sendfile(Efile_error* errInfo, int in_fd, int out_fd,
off_t *offset, Uint64 *nbytes, struct t_sendfile_hdtl *hdtl);
#endif /* HAVE_SENDFILE */
+int efile_fallocate(Efile_error* errInfo, int fd, Sint64 offset, Sint64 length);
diff --git a/erts/emulator/drivers/common/gzio.c b/erts/emulator/drivers/common/gzio.c
index a9303d55bc..ca6d25adb4 100644
--- a/erts/emulator/drivers/common/gzio.c
+++ b/erts/emulator/drivers/common/gzio.c
@@ -21,11 +21,6 @@
#include "erl_driver.h"
#include "sys.h"
-#ifdef VXWORKS
-/* pull in FOPEN from zutil.h instead */
-#undef F_OPEN
-#endif
-
#ifdef __WIN32__
#ifndef HAVE_CONFLICTING_FREAD_DECLARATION
#define HAVE_CONFLICTING_FREAD_DECLARATION
diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c
index cfa158ffb1..f0c22e9ebe 100644
--- a/erts/emulator/drivers/common/inet_drv.c
+++ b/erts/emulator/drivers/common/inet_drv.c
@@ -284,27 +284,15 @@ static unsigned long one_value = 1;
#else
-#ifdef VXWORKS
-#include <sockLib.h>
-#include <sys/times.h>
-#include <iosLib.h>
-#include <taskLib.h>
-#include <selectLib.h>
-#include <ioLib.h>
-#else
#include <sys/time.h>
#ifdef NETDB_H_NEEDS_IN_H
#include <netinet/in.h>
#endif
#include <netdb.h>
-#endif
#include <sys/socket.h>
#include <netinet/in.h>
-#ifdef VXWORKS
-#include <rpc/rpctypes.h>
-#endif
#ifdef DEF_INADDR_LOOPBACK_IN_RPC_TYPES_H
#include <rpc/types.h>
#endif
@@ -312,12 +300,10 @@ static unsigned long one_value = 1;
#include <netinet/tcp.h>
#include <arpa/inet.h>
-#if (!defined(VXWORKS))
#include <sys/param.h>
#ifdef HAVE_ARPA_NAMESER_H
#include <arpa/nameser.h>
#endif
-#endif
#ifdef HAVE_SYS_SOCKIO_H
#include <sys/sockio.h>
@@ -331,7 +317,7 @@ static unsigned long one_value = 1;
/* SCTP support -- currently for UNIX platforms only: */
#undef HAVE_SCTP
-#if (!defined(VXWORKS) && !defined(__WIN32__) && defined(HAVE_SCTP_H))
+#if (!defined(__WIN32__) && defined(HAVE_SCTP_H))
#include <netinet/sctp.h>
@@ -478,15 +464,8 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n)
#define sock_connect(s, addr, len) connect((s), (addr), (len))
#define sock_listen(s, b) listen((s), (b))
#define sock_bind(s, addr, len) bind((s), (addr), (len))
-#ifdef VXWORKS
-#define sock_getopt(s,t,n,v,l) wrap_sockopt(&getsockopt,\
- s,t,n,v,(unsigned int)(l))
-#define sock_setopt(s,t,n,v,l) wrap_sockopt(&setsockopt,\
- s,t,n,v,(unsigned int)(l))
-#else
#define sock_getopt(s,t,n,v,l) getsockopt((s),(t),(n),(v),(l))
#define sock_setopt(s,t,n,v,l) setsockopt((s),(t),(n),(v),(l))
-#endif
#define sock_name(s, addr, len) getsockname((s), (addr), (len))
#define sock_peer(s, addr, len) getpeername((s), (addr), (len))
#define sock_ntohs(x) ntohs((x))
@@ -681,6 +660,7 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n)
#define UDP_OPT_MULTICAST_LOOP 13 /* set/get IP multicast loopback */
#define UDP_OPT_ADD_MEMBERSHIP 14 /* add an IP group membership */
#define UDP_OPT_DROP_MEMBERSHIP 15 /* drop an IP group membership */
+#define INET_OPT_IPV6_V6ONLY 16 /* IPv6 only socket, no mapped v4 addrs */
/* LOPT is local options */
#define INET_LOPT_BUFFER 20 /* min buffer size hint */
#define INET_LOPT_HEADER 21 /* list header size */
@@ -698,6 +678,8 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n)
#define INET_LOPT_UDP_READ_PACKETS 33 /* Number of packets to read */
#define INET_OPT_RAW 34 /* Raw socket options */
#define INET_LOPT_TCP_SEND_TIMEOUT_CLOSE 35 /* auto-close on send timeout or not */
+#define INET_LOPT_TCP_MSGQ_HIWTRMRK 36 /* set local high watermark */
+#define INET_LOPT_TCP_MSGQ_LOWTRMRK 37 /* set local low watermark */
/* SCTP options: a separate range, from 100: */
#define SCTP_OPT_RTOINFO 100
#define SCTP_OPT_ASSOCINFO 101
@@ -808,6 +790,8 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n)
#define INET_HIGH_WATERMARK (1024*8) /* 8k pending high => busy */
#define INET_LOW_WATERMARK (1024*4) /* 4k pending => allow more */
+#define INET_HIGH_MSGQ_WATERMARK (1024*8) /* 8k pending high => busy */
+#define INET_LOW_MSGQ_WATERMARK (1024*4) /* 4k pending => allow more */
#define INET_INFINITY 0xffffffff /* infinity value */
@@ -899,7 +883,7 @@ typedef struct subs_list_ {
#define NO_PROCESS 0
#define NO_SUBSCRIBERS(SLP) ((SLP)->subscriber == NO_PROCESS)
-static void send_to_subscribers(ErlDrvPort, subs_list *, int,
+static void send_to_subscribers(ErlDrvTermData, subs_list *, int,
ErlDrvTermData [], int);
static void free_subscribers(subs_list*);
static int save_subscriber(subs_list *, ErlDrvTermData);
@@ -949,12 +933,20 @@ typedef struct {
int bufsz; /* minimum buffer constraint */
unsigned int hsz; /* the list header size, -1 is large !!! */
/* statistics */
- unsigned long recv_oct[2]; /* number of received octets >= 64 bits */
+#ifdef ARCH_64
+ Uint64 recv_oct; /* number of received octets, 64 bits */
+#else
+ Uint32 recv_oct[2]; /* number of received octets, 64 bits */
+#endif
unsigned long recv_cnt; /* number of packets received */
unsigned long recv_max; /* maximum packet size received */
double recv_avg; /* average packet size received */
double recv_dvi; /* avarage deviation from avg_size */
- unsigned long send_oct[2]; /* number of octets sent >= 64 bits */
+#ifdef ARCH_64
+ Uint64 send_oct; /* number of octets sent, 64 bits */
+#else
+ Uint32 send_oct[2]; /* number of octets sent, 64 bits */
+#endif
unsigned long send_cnt; /* number of packets sent */
unsigned long send_max; /* maximum packet send */
double send_avg; /* average packet size sent */
@@ -1188,6 +1180,7 @@ static ErlDrvTermData am_reuseaddr;
static ErlDrvTermData am_dontroute;
static ErlDrvTermData am_priority;
static ErlDrvTermData am_tos;
+static ErlDrvTermData am_ipv6_v6only;
#endif
/* speical errors for bad ports and sequences */
@@ -1225,6 +1218,27 @@ struct erl_drv_entry inet_driver_entry =
NULL,
};
+#if HAVE_IN6
+# if ! defined(HAVE_IN6ADDR_ANY) || ! HAVE_IN6ADDR_ANY
+# if HAVE_DECL_IN6ADDR_ANY_INIT
+static const struct in6_addr in6addr_any = { { IN6ADDR_ANY_INIT } };
+# else
+static const struct in6_addr in6addr_any =
+ { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } } };
+# endif /* HAVE_IN6ADDR_ANY_INIT */
+# endif /* ! HAVE_DECL_IN6ADDR_ANY */
+
+# if ! defined(HAVE_IN6ADDR_LOOPBACK) || ! HAVE_IN6ADDR_LOOPBACK
+# if HAVE_DECL_IN6ADDR_LOOPBACK_INIT
+static const struct in6_addr in6addr_loopback =
+ { { IN6ADDR_LOOPBACK_INIT } };
+# else
+static const struct in6_addr in6addr_loopback =
+ { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } } };
+# endif /* HAVE_IN6ADDR_LOOPBACk_INIT */
+# endif /* ! HAVE_DECL_IN6ADDR_LOOPBACK */
+#endif /* HAVE_IN6 */
+
/* XXX: is this a driver interface function ??? */
void erl_exit(int n, char*, ...);
@@ -1871,8 +1885,7 @@ static int deq_async(inet_descriptor* desc, int* ap, ErlDrvTermData* cp, int* rp
** {inet_async, Port, Ref, ok}
*/
static int
-send_async_ok(ErlDrvPort port, ErlDrvTermData Port, int Ref,
- ErlDrvTermData recipient)
+send_async_ok(ErlDrvTermData Port, int Ref,ErlDrvTermData recipient)
{
ErlDrvTermData spec[2*LOAD_ATOM_CNT + LOAD_PORT_CNT +
LOAD_INT_CNT + LOAD_TUPLE_CNT];
@@ -1886,14 +1899,14 @@ send_async_ok(ErlDrvPort port, ErlDrvTermData Port, int Ref,
ASSERT(i == sizeof(spec)/sizeof(*spec));
- return driver_send_term(port, recipient, spec, i);
+ return erl_drv_send_term(Port, recipient, spec, i);
}
/* send message:
** {inet_async, Port, Ref, {ok,Port2}}
*/
static int
-send_async_ok_port(ErlDrvPort port, ErlDrvTermData Port, int Ref,
+send_async_ok_port(ErlDrvTermData Port, int Ref,
ErlDrvTermData recipient, ErlDrvTermData Port2)
{
ErlDrvTermData spec[2*LOAD_ATOM_CNT + 2*LOAD_PORT_CNT +
@@ -1912,14 +1925,14 @@ send_async_ok_port(ErlDrvPort port, ErlDrvTermData Port, int Ref,
ASSERT(i == sizeof(spec)/sizeof(*spec));
- return driver_send_term(port, recipient, spec, i);
+ return erl_drv_send_term(Port, recipient, spec, i);
}
/* send message:
** {inet_async, Port, Ref, {error,Reason}}
*/
static int
-send_async_error(ErlDrvPort port, ErlDrvTermData Port, int Ref,
+send_async_error(ErlDrvTermData Port, int Ref,
ErlDrvTermData recipient, ErlDrvTermData Reason)
{
ErlDrvTermData spec[3*LOAD_ATOM_CNT + LOAD_PORT_CNT +
@@ -1937,7 +1950,7 @@ send_async_error(ErlDrvPort port, ErlDrvTermData Port, int Ref,
i = LOAD_TUPLE(spec, i, 4);
ASSERT(i == sizeof(spec)/sizeof(*spec));
DEBUGF(("send_async_error %ld %ld\r\n", recipient, Reason));
- return driver_send_term(port, recipient, spec, i);
+ return erl_drv_send_term(Port, recipient, spec, i);
}
@@ -1949,7 +1962,7 @@ static int async_ok(inet_descriptor* desc)
if (deq_async(desc, &aid, &caller, &req) < 0)
return -1;
- return send_async_ok(desc->port, desc->dport, aid, caller);
+ return send_async_ok(desc->dport, aid, caller);
}
static int async_ok_port(inet_descriptor* desc, ErlDrvTermData Port2)
@@ -1960,7 +1973,7 @@ static int async_ok_port(inet_descriptor* desc, ErlDrvTermData Port2)
if (deq_async(desc, &aid, &caller, &req) < 0)
return -1;
- return send_async_ok_port(desc->port, desc->dport, aid, caller, Port2);
+ return send_async_ok_port(desc->dport, aid, caller, Port2);
}
static int async_error_am(inet_descriptor* desc, ErlDrvTermData reason)
@@ -1971,8 +1984,7 @@ static int async_error_am(inet_descriptor* desc, ErlDrvTermData reason)
if (deq_async(desc, &aid, &caller, &req) < 0)
return -1;
- return send_async_error(desc->port, desc->dport, aid, caller,
- reason);
+ return send_async_error(desc->dport, aid, caller, reason);
}
/* dequeue all operations */
@@ -1983,8 +1995,7 @@ static int async_error_am_all(inet_descriptor* desc, ErlDrvTermData reason)
ErlDrvTermData caller;
while (deq_async(desc, &aid, &caller, &req) == 0) {
- send_async_error(desc->port, desc->dport, aid, caller,
- reason);
+ send_async_error(desc->dport, aid, caller, reason);
}
return 0;
}
@@ -2012,7 +2023,7 @@ static int inet_reply_ok(inet_descriptor* desc)
ASSERT(i == sizeof(spec)/sizeof(*spec));
desc->caller = 0;
- return driver_send_term(desc->port, caller, spec, i);
+ return erl_drv_send_term(desc->dport, caller, spec, i);
}
#ifdef HAVE_SCTP
@@ -2031,7 +2042,7 @@ static int inet_reply_ok_port(inet_descriptor* desc, ErlDrvTermData dport)
ASSERT(i == sizeof(spec)/sizeof(*spec));
desc->caller = 0;
- return driver_send_term(desc->port, caller, spec, i);
+ return erl_drv_send_term(desc->dport, caller, spec, i);
}
#endif
@@ -2054,7 +2065,7 @@ static int inet_reply_error_am(inet_descriptor* desc, ErlDrvTermData reason)
desc->caller = 0;
DEBUGF(("inet_reply_error_am %ld %ld\r\n", caller, reason));
- return driver_send_term(desc->port, caller, spec, i);
+ return erl_drv_send_term(desc->dport, caller, spec, i);
}
/* send:
@@ -2163,12 +2174,12 @@ static int http_response_inetdrv(void *arg, int major, int minor,
i = LOAD_TUPLE(spec, i, 2);
i = LOAD_TUPLE(spec, i, 4);
ASSERT(i<=27);
- return driver_send_term(desc->inet.port, caller, spec, i);
+ return erl_drv_send_term(desc->inet.dport, caller, spec, i);
}
else {
i = LOAD_TUPLE(spec, i, 3);
ASSERT(i<=27);
- return driver_output_term(desc->inet.port, spec, i);
+ return erl_drv_output_term(desc->inet.dport, spec, i);
}
}
@@ -2260,12 +2271,12 @@ http_request_inetdrv(void* arg, const http_atom_t* meth, const char* meth_ptr,
i = LOAD_TUPLE(spec, i, 2);
i = LOAD_TUPLE(spec, i, 4);
ASSERT(i <= 43);
- return driver_send_term(desc->inet.port, caller, spec, i);
+ return erl_drv_send_term(desc->inet.dport, caller, spec, i);
}
else {
i = LOAD_TUPLE(spec, i, 3);
ASSERT(i <= 43);
- return driver_output_term(desc->inet.port, spec, i);
+ return erl_drv_output_term(desc->inet.dport, spec, i);
}
}
@@ -2314,12 +2325,12 @@ http_header_inetdrv(void* arg, const http_atom_t* name, const char* name_ptr,
i = LOAD_TUPLE(spec, i, 2);
i = LOAD_TUPLE(spec, i, 4);
ASSERT(i <= 26);
- return driver_send_term(desc->inet.port, caller, spec, i);
+ return erl_drv_send_term(desc->inet.dport, caller, spec, i);
}
else {
i = LOAD_TUPLE(spec, i, 3);
ASSERT(i <= 26);
- return driver_output_term(desc->inet.port, spec, i);
+ return erl_drv_output_term(desc->inet.dport, spec, i);
}
}
@@ -2345,7 +2356,7 @@ static int http_eoh_inetdrv(void* arg)
i = LOAD_TUPLE(spec, i, 2);
i = LOAD_TUPLE(spec, i, 4);
ASSERT(i <= 14);
- return driver_send_term(desc->inet.port, caller, spec, i);
+ return erl_drv_send_term(desc->inet.dport, caller, spec, i);
}
else {
/* {http, S, http_eoh} */
@@ -2354,7 +2365,7 @@ static int http_eoh_inetdrv(void* arg)
i = LOAD_ATOM(spec, i, am_http_eoh);
i = LOAD_TUPLE(spec, i, 3);
ASSERT(i <= 14);
- return driver_output_term(desc->inet.port, spec, i);
+ return erl_drv_output_term(desc->inet.dport, spec, i);
}
}
@@ -2382,7 +2393,7 @@ static int http_error_inetdrv(void* arg, const char* buf, int len)
i = LOAD_TUPLE(spec, i, 2);
i = LOAD_TUPLE(spec, i, 4);
ASSERT(i <= 19);
- return driver_send_term(desc->inet.port, caller, spec, i);
+ return erl_drv_send_term(desc->inet.dport, caller, spec, i);
}
else {
/* {http, S, {http_error,Line} */
@@ -2393,7 +2404,7 @@ static int http_error_inetdrv(void* arg, const char* buf, int len)
i = LOAD_TUPLE(spec, i, 2);
i = LOAD_TUPLE(spec, i, 3);
ASSERT(i <= 19);
- return driver_output_term(desc->inet.port, spec, i);
+ return erl_drv_output_term(desc->inet.dport, spec, i);
}
}
@@ -2446,11 +2457,11 @@ int ssl_tls_inetdrv(void* arg, unsigned type, unsigned major, unsigned minor,
i = LOAD_TUPLE(spec, i, 2);
i = LOAD_TUPLE(spec, i, 4);
ASSERT(i <= 28);
- ret = driver_send_term(desc->inet.port, caller, spec, i);
+ ret = erl_drv_send_term(desc->inet.dport, caller, spec, i);
}
else {
ASSERT(i <= 28);
- ret = driver_output_term(desc->inet.port, spec, i);
+ ret = erl_drv_output_term(desc->inet.dport, spec, i);
}
done:
driver_free_binary(bin);
@@ -2500,7 +2511,7 @@ static int inet_async_data(inet_descriptor* desc, const char* buf, int len)
i = LOAD_TUPLE(spec, i, 4);
ASSERT(i == 15);
desc->caller = 0;
- return driver_send_term(desc->port, caller, spec, i);
+ return erl_drv_send_term(desc->dport, caller, spec, i);
}
else {
/* INET_MODE_BINARY => [H1,H2,...HSz | Binary] */
@@ -2514,7 +2525,7 @@ static int inet_async_data(inet_descriptor* desc, const char* buf, int len)
i = LOAD_TUPLE(spec, i, 4);
ASSERT(i <= 20);
desc->caller = 0;
- code = driver_send_term(desc->port, caller, spec, i);
+ code = erl_drv_send_term(desc->dport, caller, spec, i);
return code;
}
}
@@ -3107,7 +3118,7 @@ inet_async_binary_data
ASSERT(i <= PACKET_ERL_DRV_TERM_DATA_LEN);
desc->caller = 0;
- return driver_send_term(desc->port, caller, spec, i);
+ return erl_drv_send_term(desc->dport, caller, spec, i);
}
/*
@@ -3130,7 +3141,7 @@ static int tcp_message(inet_descriptor* desc, const char* buf, int len)
i = LOAD_STRING(spec, i, buf, len); /* => [H1,H2,...Hn] */
i = LOAD_TUPLE(spec, i, 3);
ASSERT(i <= 20);
- return driver_output_term(desc->port, spec, i);
+ return erl_drv_output_term(desc->dport, spec, i);
}
else {
/* INET_MODE_BINARY => [H1,H2,...HSz | Binary] */
@@ -3142,7 +3153,7 @@ static int tcp_message(inet_descriptor* desc, const char* buf, int len)
i = LOAD_STRING_CONS(spec, i, buf, hsz);
i = LOAD_TUPLE(spec, i, 3);
ASSERT(i <= 20);
- code = driver_output_term(desc->port, spec, i);
+ code = erl_drv_output_term(desc->dport, spec, i);
return code;
}
}
@@ -3177,7 +3188,7 @@ tcp_binary_message(inet_descriptor* desc, ErlDrvBinary* bin, int offs, int len)
}
i = LOAD_TUPLE(spec, i, 3);
ASSERT(i <= 20);
- return driver_output_term(desc->port, spec, i);
+ return erl_drv_output_term(desc->dport, spec, i);
}
/*
@@ -3196,7 +3207,7 @@ static int tcp_closed_message(tcp_descriptor* desc)
i = LOAD_PORT(spec, i, desc->inet.dport);
i = LOAD_TUPLE(spec, i, 2);
ASSERT(i <= 6);
- return driver_output_term(desc->inet.port, spec, i);
+ return erl_drv_output_term(desc->inet.dport, spec, i);
}
return 0;
}
@@ -3217,7 +3228,7 @@ static int tcp_error_message(tcp_descriptor* desc, int err)
i = LOAD_ATOM(spec, i, am_err);
i = LOAD_TUPLE(spec, i, 3);
ASSERT(i <= 8);
- return driver_output_term(desc->inet.port, spec, i);
+ return erl_drv_output_term(desc->inet.dport, spec, i);
}
/*
@@ -3308,7 +3319,7 @@ static int packet_binary_message
/* Close up the outer 5-tuple: */
i = LOAD_TUPLE(spec, i, 5);
ASSERT(i <= PACKET_ERL_DRV_TERM_DATA_LEN);
- return driver_output_term(desc->port, spec, i);
+ return erl_drv_output_term(desc->dport, spec, i);
}
/*
@@ -3335,7 +3346,7 @@ static int packet_error_message(udp_descriptor* udesc, int err)
i = LOAD_ATOM(spec, i, am_err);
i = LOAD_TUPLE(spec, i, 3);
ASSERT(i == sizeof(spec)/sizeof(*spec));
- return driver_output_term(desc->port, spec, i);
+ return erl_drv_output_term(desc->dport, spec, i);
}
@@ -3486,6 +3497,7 @@ static void inet_init_sctp(void) {
INIT_ATOM(dontroute);
INIT_ATOM(priority);
INIT_ATOM(tos);
+ INIT_ATOM(ipv6_v6only);
/* Option names */
INIT_ATOM(sctp_rtoinfo);
@@ -3686,6 +3698,9 @@ static char* inet_set_address(int family, inet_address* dst,
if ((family == AF_INET) && (*len >= 2+4)) {
sys_memzero((char*)dst, sizeof(struct sockaddr_in));
port = get_int16(src);
+#ifndef NO_SA_LEN
+ dst->sai.sin_len = sizeof(struct sockaddr_in);
+#endif
dst->sai.sin_family = family;
dst->sai.sin_port = sock_htons(port);
sys_memcpy(&dst->sai.sin_addr, src+2, 4);
@@ -3696,6 +3711,9 @@ static char* inet_set_address(int family, inet_address* dst,
else if ((family == AF_INET6) && (*len >= 2+16)) {
sys_memzero((char*)dst, sizeof(struct sockaddr_in6));
port = get_int16(src);
+#ifndef NO_SA_LEN
+ dst->sai6.sin6_len = sizeof(struct sockaddr_in6);
+#endif
dst->sai6.sin6_family = family;
dst->sai6.sin6_port = sock_htons(port);
dst->sai6.sin6_flowinfo = 0; /* XXX this may be set as well ?? */
@@ -3706,7 +3724,7 @@ static char* inet_set_address(int family, inet_address* dst,
#endif
return NULL;
}
-#ifdef HAVE_SCTP
+
/*
** Set an inaddr structure, address family comes from source data,
** or from argument if source data specifies constant address.
@@ -3750,6 +3768,9 @@ static char *inet_set_faddress(int family, inet_address* dst,
return NULL;
}
sys_memzero((char*)dst, sizeof(struct sockaddr_in));
+#ifndef NO_SA_LEN
+ dst->sai.sin_len = sizeof(struct sockaddr_in6);
+#endif
dst->sai.sin_family = family;
dst->sai.sin_port = sock_htons(port);
dst->sai.sin_addr.s_addr = addr.s_addr;
@@ -3769,6 +3790,9 @@ static char *inet_set_faddress(int family, inet_address* dst,
return NULL;
}
sys_memzero((char*)dst, sizeof(struct sockaddr_in6));
+#ifndef NO_SA_LEN
+ dst->sai6.sin6_len = sizeof(struct sockaddr_in6);
+#endif
dst->sai6.sin6_family = family;
dst->sai6.sin6_port = sock_htons(port);
dst->sai6.sin6_flowinfo = 0; /* XXX this may be set as well ?? */
@@ -3786,7 +3810,7 @@ static char *inet_set_faddress(int family, inet_address* dst,
}
return inet_set_address(family, dst, src, len);
}
-#endif /* HAVE_SCTP */
+
/* Get a inaddr structure
** src = inaddr structure
@@ -5260,50 +5284,6 @@ static ErlDrvSSizeT inet_ctl_getifaddrs(inet_descriptor* desc_p,
#endif
-
-
-#ifdef VXWORKS
-/*
-** THIS is a terrible creature, a bug in the TCP part
-** of the old VxWorks stack (non SENS) created a race.
-** If (and only if?) a socket got closed from the other
-** end and we tried a set/getsockopt on the TCP level,
-** the task would generate a bus error...
-*/
-static STATUS wrap_sockopt(STATUS (*function)() /* Yep, no parameter
- check */,
- int s, int level, int optname,
- char *optval, unsigned int optlen
- /* optlen is a pointer if function
- is getsockopt... */)
-{
- fd_set rs;
- struct timeval timeout;
- int to_read;
- int ret;
-
- FD_ZERO(&rs);
- FD_SET(s,&rs);
- memset(&timeout,0,sizeof(timeout));
- if (level == IPPROTO_TCP) {
- taskLock();
- if (select(s+1,&rs,NULL,NULL,&timeout)) {
- if (ioctl(s,FIONREAD,(int)&to_read) == ERROR ||
- to_read == 0) { /* End of file, other end closed? */
- sock_errno() = EBADF;
- taskUnlock();
- return ERROR;
- }
- }
- ret = (*function)(s,level,optname,optval,optlen);
- taskUnlock();
- } else {
- ret = (*function)(s,level,optname,optval,optlen);
- }
- return ret;
-}
-#endif
-
/* Per H @ Tail-f: The original code here had problems that possibly
only occur if you abuse it for non-INET sockets, but anyway:
a) If the getsockopt for SO_PRIORITY or IP_TOS failed, the actual
@@ -5494,6 +5474,28 @@ static int inet_set_opts(inet_descriptor* desc, char* ptr, int len)
}
continue;
+ case INET_LOPT_TCP_MSGQ_HIWTRMRK:
+ if (desc->stype == SOCK_STREAM) {
+ ErlDrvSizeT high;
+ if (ival < ERL_DRV_BUSY_MSGQ_LIM_MIN
+ || ERL_DRV_BUSY_MSGQ_LIM_MAX < ival)
+ return -1;
+ high = (ErlDrvSizeT) ival;
+ erl_drv_busy_msgq_limits(desc->port, NULL, &high);
+ }
+ continue;
+
+ case INET_LOPT_TCP_MSGQ_LOWTRMRK:
+ if (desc->stype == SOCK_STREAM) {
+ ErlDrvSizeT low;
+ if (ival < ERL_DRV_BUSY_MSGQ_LIM_MIN
+ || ERL_DRV_BUSY_MSGQ_LIM_MAX < ival)
+ return -1;
+ low = (ErlDrvSizeT) ival;
+ erl_drv_busy_msgq_limits(desc->port, &low, NULL);
+ }
+ continue;
+
case INET_LOPT_TCP_SEND_TIMEOUT:
if (desc->stype == SOCK_STREAM) {
tcp_descriptor* tdesc = (tcp_descriptor*) desc;
@@ -5555,23 +5557,11 @@ static int inet_set_opts(inet_descriptor* desc, char* ptr, int len)
case INET_OPT_SNDBUF: type = SO_SNDBUF;
DEBUGF(("inet_set_opts(%ld): s=%d, SO_SNDBUF=%d\r\n",
(long)desc->port, desc->s, ival));
- /*
- * Setting buffer sizes in VxWorks gives unexpected results
- * our workaround is to leave it at default.
- */
-#ifdef VXWORKS
- goto skip_os_setopt;
-#else
break;
-#endif
case INET_OPT_RCVBUF: type = SO_RCVBUF;
DEBUGF(("inet_set_opts(%ld): s=%d, SO_RCVBUF=%d\r\n",
(long)desc->port, desc->s, ival));
-#ifdef VXWORKS
- goto skip_os_setopt;
-#else
break;
-#endif
case INET_OPT_LINGER: type = SO_LINGER;
if (len < 4)
return -1;
@@ -5662,6 +5652,23 @@ static int inet_set_opts(inet_descriptor* desc, char* ptr, int len)
#endif /* HAVE_MULTICAST_SUPPORT */
+ case INET_OPT_IPV6_V6ONLY:
+#if HAVE_DECL_IPV6_V6ONLY
+ proto = IPPROTO_IPV6;
+ type = IPV6_V6ONLY;
+ propagate = 1;
+ DEBUGF(("inet_set_opts(%ld): s=%d, IPV6_V6ONLY=%d\r\n",
+ (long)desc->port, desc->s, ival));
+ break;
+#elif defined(__WIN32__) && defined(HAVE_IN6) && defined(AF_INET6)
+ /* Fake a'la OpenBSD; set to 'true' is fine but 'false' invalid. */
+ if (ival != 0) continue;
+ else return -1;
+ break;
+#else
+ continue;
+#endif
+
case INET_OPT_RAW:
if (len < 8) {
return -1;
@@ -5693,9 +5700,6 @@ static int inet_set_opts(inet_descriptor* desc, char* ptr, int len)
}
DEBUGF(("inet_set_opts(%ld): s=%d returned %d\r\n",
(long)desc->port, desc->s, res));
-#ifdef VXWORKS
-skip_os_setopt:
-#endif
if (type == SO_RCVBUF) {
/* make sure we have desc->bufsz >= SO_RCVBUF */
if (ival > desc->bufsz)
@@ -5994,6 +5998,22 @@ static int sctp_set_opts(inet_descriptor* desc, char* ptr, int len)
continue; /* Option not supported -- ignore it */
# endif
+ case INET_OPT_IPV6_V6ONLY:
+# if HAVE_DECL_IPV6_V6ONLY
+ {
+ arg.ival= get_int32 (curr); curr += 4;
+ proto = IPPROTO_IPV6;
+ type = IPV6_V6ONLY;
+ arg_ptr = (char*) (&arg.ival);
+ arg_sz = sizeof ( arg.ival);
+ break;
+ }
+# elif defined(__WIN32__) && defined(HAVE_IN6) && defined(AF_INET6)
+# error Here is a fix for Win IPv6 SCTP missing
+# else
+ continue; /* Option not supported -- ignore it */
+# endif
+
case SCTP_OPT_AUTOCLOSE:
{
arg.ival= get_int32 (curr); curr += 4;
@@ -6376,6 +6396,32 @@ static ErlDrvSSizeT inet_fill_opts(inet_descriptor* desc,
}
continue;
+ case INET_LOPT_TCP_MSGQ_HIWTRMRK:
+ if (desc->stype == SOCK_STREAM) {
+ ErlDrvSizeT high = ERL_DRV_BUSY_MSGQ_READ_ONLY;
+ *ptr++ = opt;
+ erl_drv_busy_msgq_limits(desc->port, NULL, &high);
+ ival = high > INT_MAX ? INT_MAX : (int) high;
+ put_int32(ival, ptr);
+ }
+ else {
+ TRUNCATE_TO(0,ptr);
+ }
+ continue;
+
+ case INET_LOPT_TCP_MSGQ_LOWTRMRK:
+ if (desc->stype == SOCK_STREAM) {
+ ErlDrvSizeT low = ERL_DRV_BUSY_MSGQ_READ_ONLY;
+ *ptr++ = opt;
+ erl_drv_busy_msgq_limits(desc->port, &low, NULL);
+ ival = low > INT_MAX ? INT_MAX : (int) low;
+ put_int32(ival, ptr);
+ }
+ else {
+ TRUNCATE_TO(0,ptr);
+ }
+ continue;
+
case INET_LOPT_TCP_SEND_TIMEOUT:
if (desc->stype == SOCK_STREAM) {
*ptr++ = opt;
@@ -6482,6 +6528,22 @@ static ErlDrvSSizeT inet_fill_opts(inet_descriptor* desc,
break;
#endif /* HAVE_MULTICAST_SUPPORT */
+ case INET_OPT_IPV6_V6ONLY:
+#if HAVE_DECL_IPV6_V6ONLY
+ proto = IPPROTO_IPV6;
+ type = IPV6_V6ONLY;
+ break;
+#elif defined(__WIN32__) && defined(HAVE_IN6) && defined(AF_INET6)
+ /* Fake reading 'true' */
+ *ptr++ = opt;
+ put_int32(1, ptr);
+ ptr += 4;
+ continue;
+#else
+ TRUNCATE_TO(0,ptr);
+ continue; /* skip - no result */
+#endif
+
case INET_OPT_RAW:
{
int data_provided;
@@ -6786,6 +6848,7 @@ static ErlDrvSSizeT sctp_fill_opts(inet_descriptor* desc,
case INET_OPT_DONTROUTE:
case INET_OPT_PRIORITY :
case INET_OPT_TOS :
+ case INET_OPT_IPV6_V6ONLY:
case SCTP_OPT_AUTOCLOSE:
case SCTP_OPT_MAXSEG :
/* The following options return true or false: */
@@ -6858,6 +6921,20 @@ static ErlDrvSSizeT sctp_fill_opts(inet_descriptor* desc,
continue;
# endif
}
+ case INET_OPT_IPV6_V6ONLY:
+# if HAVE_DECL_IPV6_V6ONLY
+ {
+ proto = IPPROTO_IPV6;
+ type = IPV6_V6ONLY;
+ tag = am_ipv6_v6only;
+ break;
+ }
+# elif defined(__WIN32__) && defined(HAVE_IN6) && defined(AF_INET6)
+# error Here is a fix for Win IPv6 SCTP needed
+# else
+ /* Not supported -- ignore */
+ continue;
+# endif
case SCTP_OPT_AUTOCLOSE:
{
proto = IPPROTO_SCTP;
@@ -7258,7 +7335,7 @@ static ErlDrvSSizeT sctp_fill_opts(inet_descriptor* desc,
i = LOAD_TUPLE(spec, i, 3);
/* Now, convert "spec" into the returnable term: */
- driver_send_term(desc->port, driver_caller(desc->port), spec, i);
+ erl_drv_send_term(desc->dport, driver_caller(desc->port), spec, i);
FREE(spec);
(*dest)[0] = INET_REP;
@@ -7308,13 +7385,21 @@ static ErlDrvSSizeT inet_fill_stat(inet_descriptor* desc,
val = (unsigned long) driver_sizeq(desc->port);
break;
case INET_STAT_RECV_OCT:
+#ifdef ARCH_64
+ put_int64(desc->recv_oct, dst); /* write it all */
+#else
put_int32(desc->recv_oct[1], dst); /* write high 32bit */
put_int32(desc->recv_oct[0], dst+4); /* write low 32bit */
+#endif
dst += 8;
continue;
case INET_STAT_SEND_OCT:
+#ifdef ARCH_64
+ put_int64(desc->send_oct, dst); /* write it all */
+#else
put_int32(desc->send_oct[1], dst); /* write high 32bit */
put_int32(desc->send_oct[0], dst+4); /* write low 32bit */
+#endif
dst += 8;
continue;
default: return -1; /* invalid argument */
@@ -7340,7 +7425,7 @@ send_empty_out_q_msgs(inet_descriptor* desc)
ASSERT(msg_len == sizeof(msg)/sizeof(*msg));
- send_to_subscribers(desc->port,
+ send_to_subscribers(desc->dport,
&desc->empty_out_q_subs,
1,
msg,
@@ -7422,12 +7507,20 @@ static ErlDrvData inet_start(ErlDrvPort port, int size, int protocol)
desc->peer_ptr = NULL;
desc->name_ptr = NULL;
+#ifdef ARCH_64
+ desc->recv_oct = 0;
+#else
desc->recv_oct[0] = desc->recv_oct[1] = 0;
+#endif
desc->recv_cnt = 0;
desc->recv_max = 0;
desc->recv_avg = 0.0;
desc->recv_dvi = 0.0;
+#ifdef ARCH_64
+ desc->send_oct = 0;
+#else
desc->send_oct[0] = desc->send_oct[1] = 0;
+#endif
desc->send_cnt = 0;
desc->send_max = 0;
desc->send_avg = 0.0;
@@ -7712,7 +7805,7 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf,
if (desc->state != INET_STATE_OPEN)
return ctl_xerror(EXBADPORT, rbuf, rsize);
- if (inet_set_address(desc->sfamily, &local, buf, &len) == NULL)
+ if (inet_set_faddress(desc->sfamily, &local, buf, &len) == NULL)
return ctl_error(EINVAL, rbuf, rsize);
if (IS_SOCKET_ERROR(sock_bind(desc->s,(struct sockaddr*) &local, len)))
@@ -7757,8 +7850,6 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf,
return ctl_reply(INET_REP_OK, NULL, 0, rbuf, rsize);
}
-#ifndef VXWORKS
-
case INET_REQ_GETSERVBYNAME: { /* L1 Name-String L2 Proto-String */
char namebuf[256];
char protobuf[256];
@@ -7809,8 +7900,6 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf,
return ctl_reply(INET_REP_OK, srv->s_name, len, rbuf, rsize);
}
-#endif /* !VXWORKS */
-
default:
return ctl_xerror(EXBADPORT, rbuf, rsize);
}
@@ -7820,14 +7909,19 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf,
static void inet_output_count(inet_descriptor* desc, ErlDrvSizeT len)
{
unsigned long n = desc->send_cnt + 1;
- unsigned long t = desc->send_oct[0] + len;
+#ifndef ARCH_64
+ Uint32 t = desc->send_oct[0] + len;
int c = (t < desc->send_oct[0]);
+#endif
double avg = desc->send_avg;
- /* at least 64 bit octet count */
+#ifdef ARCH_64
+ desc->send_oct += len;
+#else
+ /* 64 bit octet count in 32 bit words */
desc->send_oct[0] = t;
desc->send_oct[1] += c;
-
+#endif
if (n == 0) /* WRAP, use old avg as input to a new sequence */
n = 1;
desc->send_avg += (len - avg) / n;
@@ -7840,14 +7934,20 @@ static void inet_output_count(inet_descriptor* desc, ErlDrvSizeT len)
static void inet_input_count(inet_descriptor* desc, ErlDrvSizeT len)
{
unsigned long n = desc->recv_cnt + 1;
- unsigned long t = desc->recv_oct[0] + len;
+#ifndef ARCH_64
+ Uint32 t = (desc->recv_oct[0] + len);
int c = (t < desc->recv_oct[0]);
+#endif
double avg = desc->recv_avg;
double dvi;
- /* at least 64 bit octet count */
+#ifdef ARCH_64
+ desc->recv_oct += len;
+#else
+ /* 64 bit octet count in 32 bit words */
desc->recv_oct[0] = t;
desc->recv_oct[1] += c;
+#endif
if (n == 0) /* WRAP */
n = 1;
@@ -7991,6 +8091,7 @@ static int tcp_inet_init(void)
static ErlDrvData tcp_inet_start(ErlDrvPort port, char* args)
{
+ ErlDrvSizeT q_low, q_high;
tcp_descriptor* desc;
DEBUGF(("tcp_inet_start(%ld) {\r\n", (long)port));
@@ -8000,6 +8101,17 @@ static ErlDrvData tcp_inet_start(ErlDrvPort port, char* args)
return ERL_DRV_ERROR_ERRNO;
desc->high = INET_HIGH_WATERMARK;
desc->low = INET_LOW_WATERMARK;
+ q_high = INET_HIGH_MSGQ_WATERMARK;
+ q_low = INET_LOW_MSGQ_WATERMARK;
+ if (q_low < ERL_DRV_BUSY_MSGQ_LIM_MIN)
+ q_low = ERL_DRV_BUSY_MSGQ_LIM_MIN;
+ else if (q_low > ERL_DRV_BUSY_MSGQ_LIM_MAX)
+ q_low = ERL_DRV_BUSY_MSGQ_LIM_MAX;
+ if (q_high < ERL_DRV_BUSY_MSGQ_LIM_MIN)
+ q_high = ERL_DRV_BUSY_MSGQ_LIM_MIN;
+ else if (q_high > ERL_DRV_BUSY_MSGQ_LIM_MAX)
+ q_high = ERL_DRV_BUSY_MSGQ_LIM_MAX;
+ erl_drv_busy_msgq_limits(port, &q_low, &q_high);
desc->send_timeout = INET_INFINITY;
desc->send_timeout_close = 0;
desc->busy_on_send = 0;
@@ -8023,6 +8135,7 @@ static ErlDrvData tcp_inet_start(ErlDrvPort port, char* args)
static tcp_descriptor* tcp_inet_copy(tcp_descriptor* desc,SOCKET s,
ErlDrvTermData owner, int* err)
{
+ ErlDrvSizeT q_low, q_high;
ErlDrvPort port = desc->inet.port;
tcp_descriptor* copy_desc;
@@ -8060,6 +8173,13 @@ static tcp_descriptor* tcp_inet_copy(tcp_descriptor* desc,SOCKET s,
FREE(copy_desc);
return NULL;
}
+
+ /* Read busy msgq limits of parent */
+ q_low = q_high = ERL_DRV_BUSY_MSGQ_READ_ONLY;
+ erl_drv_busy_msgq_limits(desc->inet.port, &q_low, &q_high);
+ /* Write same busy msgq limits to child */
+ erl_drv_busy_msgq_limits(port, &q_low, &q_high);
+
copy_desc->inet.port = port;
copy_desc->inet.dport = driver_mk_port(port);
*err = 0;
@@ -8092,7 +8212,7 @@ static void tcp_close_check(tcp_descriptor* desc)
desc->inet.state = INET_STATE_LISTENING;
while (deq_multi_op(desc,&id,&req,&caller,NULL,&monitor) == 0) {
driver_demonitor_process(desc->inet.port, &monitor);
- send_async_error(desc->inet.port, desc->inet.dport, id, caller, am_closed);
+ send_async_error(desc->inet.dport, id, caller, am_closed);
}
clean_multi_timers(&(desc->mtd), desc->inet.port);
}
@@ -8516,7 +8636,7 @@ static void tcp_inet_multi_timeout(ErlDrvData e, ErlDrvTermData caller)
sock_select(INETP(desc),FD_ACCEPT,0);
desc->inet.state = INET_STATE_LISTENING; /* restore state */
}
- send_async_error(desc->inet.port, desc->inet.dport, id, caller, am_timeout);
+ send_async_error(desc->inet.dport, id, caller, am_timeout);
}
@@ -9257,7 +9377,7 @@ static int tcp_inet_input(tcp_descriptor* desc, HANDLE event)
if (s == INVALID_SOCKET) { /* Not ERRNO_BLOCK, that's handled right away */
- ret = send_async_error(desc->inet.port, desc->inet.dport,
+ ret = send_async_error(desc->inet.dport,
id, caller, error_atom(sock_errno()));
goto done;
}
@@ -9267,7 +9387,7 @@ static int tcp_inet_input(tcp_descriptor* desc, HANDLE event)
if ((accept_desc = tcp_inet_copy(desc,s,caller,&err)) == NULL) {
sock_close(s);
- ret = send_async_error(desc->inet.port, desc->inet.dport,
+ ret = send_async_error(desc->inet.dport,
id, caller, error_atom(err));
goto done;
}
@@ -9278,7 +9398,7 @@ static int tcp_inet_input(tcp_descriptor* desc, HANDLE event)
ERL_DRV_READ, 1);
#endif
accept_desc->inet.state = INET_STATE_CONNECTED;
- ret = send_async_ok_port(desc->inet.port, desc->inet.dport,
+ ret = send_async_ok_port(desc->inet.dport,
id, caller, accept_desc->inet.dport);
}
}
@@ -9638,6 +9758,7 @@ static int tcp_inet_output(tcp_descriptor* desc, HANDLE event)
DEBUGF(("tcp_inet_output(%ld): s=%d, About to send %d items\r\n",
(long)desc->inet.port, desc->inet.s, vsize));
if (IS_SOCKET_ERROR(sock_sendv(desc->inet.s, iov, vsize, &n, 0))) {
+ write_error:
if ((sock_errno() != ERRNO_BLOCK) && (sock_errno() != EINTR)) {
DEBUGF(("tcp_inet_output(%ld): sock_sendv(%d) errno = %d\r\n",
(long)desc->inet.port, vsize, sock_errno()));
@@ -9648,6 +9769,22 @@ static int tcp_inet_output(tcp_descriptor* desc, HANDLE event)
desc->inet.send_would_block = 1;
#endif
goto done;
+ } else if (n == 0) { /* Workaround for redhat/CentOS 6.3 returning
+ 0 when sending packets with
+ sizes > (max 32 bit signed int) */
+ size_t howmuch = 0x7FFFFFFF; /* max signed 32 bit */
+ int x;
+ for(x = 0; x < vsize && iov[x].iov_len == 0; ++x)
+ ;
+ if (x < vsize) {
+ if (howmuch > iov[x].iov_len) {
+ howmuch = iov[x].iov_len;
+ }
+ n = sock_send(desc->inet.s, iov[x].iov_base,howmuch,0);
+ if (IS_SOCKET_ERROR(n)) {
+ goto write_error;
+ }
+ }
}
if (driver_deq(ix, n) <= desc->low) {
if (IS_BUSY(INETP(desc))) {
@@ -10075,12 +10212,13 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf,
case SCTP_REQ_BINDX:
{ /* Multi-homing bind for SCTP: */
- /* Construct the list of addresses we bind to. The curr limit is
- 256 addrs. Buff structure: Flags(1), ListItem,...:
+ /* Add additional addresses by calling sctp_bindx with one address
+ at a time, since this is what some OSes promise will work.
+ Buff structure: Flags(1), ListItem,...:
*/
- struct sockaddr addrs[256];
+ inet_address addr;
char* curr;
- int add_flag, n, rflag;
+ int add_flag, rflag;
if (!IS_SCTP(desc))
return ctl_xerror(EXBADPORT, rbuf, rsize);
@@ -10089,27 +10227,22 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf,
add_flag = get_int8(curr);
curr++;
- for(n=0; n < 256 && curr < buf+len; n++)
+ /* Make the real flags: */
+ rflag = add_flag ? SCTP_BINDX_ADD_ADDR : SCTP_BINDX_REM_ADDR;
+
+ while (curr < buf+len)
{
- /* List item format: Port(2), IP(4|16) -- compatible with
- "inet_set_address": */
- inet_address tmp;
+ /* List item format: see "inet_set_faddress": */
ErlDrvSizeT alen = buf + len - curr;
- curr = inet_set_address(desc->sfamily, &tmp, curr, &alen);
+ curr = inet_set_faddress(desc->sfamily, &addr, curr, &alen);
if (curr == NULL)
return ctl_error(EINVAL, rbuf, rsize);
- /* Now: we need to squeeze "tmp" into the size of "sockaddr",
- which is smaller than "tmp" for IPv6 (extra IN6 info will
- be cut off): */
- memcpy(addrs + n, &tmp, sizeof(struct sockaddr));
+ /* Invoke the call: */
+ if (p_sctp_bindx(desc->s, (struct sockaddr *)&addr, 1,
+ rflag) < 0)
+ return ctl_error(sock_errno(), rbuf, rsize);
}
- /* Make the real flags: */
- rflag = add_flag ? SCTP_BINDX_ADD_ADDR : SCTP_BINDX_REM_ADDR;
-
- /* Invoke the call: */
- if (p_sctp_bindx(desc->s, addrs, n, rflag) < 0)
- return ctl_error(sock_errno(), rbuf, rsize);
desc->state = INET_STATE_BOUND;
@@ -10150,6 +10283,7 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf,
}
new_udesc->inet.state = INET_STATE_CONNECTED;
new_udesc->inet.stype = SOCK_STREAM;
+ SET_NONBLOCKING(new_udesc->inet.s);
inet_reply_ok_port(desc, new_udesc->inet.dport);
(*rbuf)[0] = INET_REP;
@@ -10923,7 +11057,7 @@ subs_list *subs;
static void send_to_subscribers
(
- ErlDrvPort port,
+ ErlDrvTermData port,
subs_list *subs,
int free_subs,
ErlDrvTermData msg[],
@@ -10940,7 +11074,7 @@ static void send_to_subscribers
this = subs;
while(this) {
- (void) driver_send_term(port, this->subscriber, msg, msg_len);
+ (void) erl_drv_send_term(port, this->subscriber, msg, msg_len);
if(free_subs && !first) {
next = this->next;
diff --git a/erts/emulator/drivers/common/ram_file_drv.c b/erts/emulator/drivers/common/ram_file_drv.c
index a109e40333..7f7cd7cd91 100644
--- a/erts/emulator/drivers/common/ram_file_drv.c
+++ b/erts/emulator/drivers/common/ram_file_drv.c
@@ -48,6 +48,7 @@
#define RAM_FILE_SIZE 37 /* get file size */
#define RAM_FILE_ADVISE 38 /* predeclare the access
* pattern for file data */
+#define RAM_FILE_ALLOCATE 39 /* allocate space for a file */
/* possible new operations include:
DES_ENCRYPT
DES_DECRYPT
@@ -720,6 +721,13 @@ static void rfile_command(ErlDrvData e, char* buf, ErlDrvSizeT count)
else
reply(f, 1, 0);
break;
+
+ case RAM_FILE_ALLOCATE:
+ if (f->flags == 0)
+ error_reply(f, EBADF);
+ else
+ reply(f, 1, 0);
+ break;
}
/*
* Ignore anything else -- let the caller hang.
diff --git a/erts/emulator/drivers/unix/ttsl_drv.c b/erts/emulator/drivers/unix/ttsl_drv.c
index b29f80a8ba..ab2abb88d1 100644
--- a/erts/emulator/drivers/unix/ttsl_drv.c
+++ b/erts/emulator/drivers/unix/ttsl_drv.c
@@ -912,11 +912,15 @@ static int insert_buf(byte *s, int n)
lbuf[lpos++] = (CONTROL_TAG | ((Uint32) ch));
ch = 0;
} while (lpos % 8);
- } else if (ch == '\n' || ch == '\r') {
+ } else if (ch == '\e' || ch == '\n' || ch == '\r') {
write_buf(lbuf + buffpos, lpos - buffpos);
- outc('\r');
- if (ch == '\n')
- outc('\n');
+ if (ch == '\e') {
+ outc('\e');
+ } else {
+ outc('\r');
+ if (ch == '\n')
+ outc('\n');
+ }
if (llen > lpos) {
memcpy(lbuf, lbuf + lpos, llen - lpos);
}
diff --git a/erts/emulator/drivers/unix/unix_efile.c b/erts/emulator/drivers/unix/unix_efile.c
index ad112f7590..558651fff9 100644
--- a/erts/emulator/drivers/unix/unix_efile.c
+++ b/erts/emulator/drivers/unix/unix_efile.c
@@ -22,6 +22,12 @@
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
+#if defined(HAVE_POSIX_FALLOCATE) && !defined(__sun) && !defined(__sun__)
+#define _XOPEN_SOURCE 600
+#endif
+#if !defined(_GNU_SOURCE) && defined(HAVE_LINUX_FALLOC_H)
+#define _GNU_SOURCE
+#endif
#include "sys.h"
#include "erl_driver.h"
#include "erl_efile.h"
@@ -41,25 +47,12 @@
#define DARWIN 1
#endif
-#ifdef DARWIN
+#if defined(DARWIN) || defined(HAVE_LINUX_FALLOC_H) || defined(HAVE_POSIX_FALLOCATE)
#include <fcntl.h>
-#endif /* DARWIN */
+#endif
-#ifdef VXWORKS
-#include <ioLib.h>
-#include <dosFsLib.h>
-#include <nfsLib.h>
-#include <sys/stat.h>
-/*
-** Not nice to include usrLib.h as MANY normal variable names get reported
-** as shadowing globals, like 'i' for example.
-** Instead we declare the only function we use here
-*/
-/*
- * #include <usrLib.h>
- */
-extern STATUS copy(char *, char *);
-#include <errno.h>
+#ifdef HAVE_LINUX_FALLOC_H
+#include <linux/falloc.h>
#endif
#ifdef SUNOS4
@@ -93,276 +86,27 @@ extern STATUS copy(char *, char *);
#define DIR_MODE 0777
#endif
-#ifdef VXWORKS /* Currently only used on vxworks */
-
-#define EF_ALLOC(S) driver_alloc((S))
-#define EF_REALLOC(P, S) driver_realloc((P), (S))
-#define EF_SAFE_ALLOC(S) ef_safe_alloc((S))
-#define EF_SAFE_REALLOC(P, S) ef_safe_realloc((P), (S))
-#define EF_FREE(P) do { if((P)) driver_free((P)); } while(0)
-
-void erl_exit(int n, char *fmt, ...);
-
-static void *ef_safe_alloc(Uint s)
-{
- void *p = EF_ALLOC(s);
- if (!p) erl_exit(1,
- "unix efile drv: Can't allocate %d bytes of memory\n",
- s);
- return p;
-}
-
-#if 0 /* Currently not used */
-
-static void *ef_safe_realloc(void *op, Uint s)
-{
- void *p = EF_REALLOC(op, s);
- if (!p) erl_exit(1,
- "unix efile drv: Can't reallocate %d bytes of memory\n",
- s);
- return p;
-}
-
-#endif /* #if 0 */
-#endif /* #ifdef VXWORKS */
-
#define IS_DOT_OR_DOTDOT(s) \
(s[0] == '.' && (s[1] == '\0' || (s[1] == '.' && s[2] == '\0')))
-#ifdef VXWORKS
-static int vxworks_to_posix(int vx_errno);
-#endif
-
-/*
-** VxWorks (not) strikes again. Too long RESULTING paths
-** may give the infamous bus error. Have to check ALL
-** filenames and pathnames. No wonder the emulator is slow on
-** these cards...
-*/
-#ifdef VXWORKS
-#define CHECK_PATHLEN(Name, ErrInfo) \
- if (path_size(Name) > PATH_MAX) { \
- errno = ENAMETOOLONG; \
- return check_error(-1, ErrInfo); \
- }
-#else
-#define CHECK_PATHLEN(X,Y) /* Nothing */
-#endif
-
static int check_error(int result, Efile_error* errInfo);
static int
check_error(int result, Efile_error *errInfo)
{
if (result < 0) {
-#ifdef VXWORKS
- errInfo->posix_errno = errInfo->os_errno = vxworks_to_posix(errno);
-#else
errInfo->posix_errno = errInfo->os_errno = errno;
-#endif
return 0;
}
return 1;
}
-#ifdef VXWORKS
-
-/*
- * VxWorks has different error codes for different file systems.
- * We map those to POSIX ones.
- */
-static int
-vxworks_to_posix(int vx_errno)
-{
- DEBUGF(("[vxworks_to_posix] vx_errno: %08x\n", vx_errno));
- switch (vx_errno) {
- /* dosFsLib mapping */
-#ifdef S_dosFsLib_FILE_ALREADY_EXISTS
- case S_dosFsLib_FILE_ALREADY_EXISTS: return EEXIST;
-#else
- case S_dosFsLib_FILE_EXISTS: return EEXIST;
-#endif
-#ifdef S_dosFsLib_BAD_DISK
- case S_dosFsLib_BAD_DISK: return EIO;
-#endif
-#ifdef S_dosFsLib_CANT_CHANGE_ROOT
- case S_dosFsLib_CANT_CHANGE_ROOT: return EINVAL;
-#endif
-#ifdef S_dosFsLib_NO_BLOCK_DEVICE
- case S_dosFsLib_NO_BLOCK_DEVICE: return ENOTBLK;
-#endif
-#ifdef S_dosFsLib_BAD_SEEK
- case S_dosFsLib_BAD_SEEK: return ESPIPE;
-#endif
- case S_dosFsLib_VOLUME_NOT_AVAILABLE: return ENXIO;
- case S_dosFsLib_DISK_FULL: return ENOSPC;
- case S_dosFsLib_FILE_NOT_FOUND: return ENOENT;
- case S_dosFsLib_NO_FREE_FILE_DESCRIPTORS: return ENFILE;
- case S_dosFsLib_INVALID_NUMBER_OF_BYTES: return EINVAL;
- case S_dosFsLib_ILLEGAL_NAME: return EINVAL;
- case S_dosFsLib_CANT_DEL_ROOT: return EACCES;
- case S_dosFsLib_NOT_FILE: return EISDIR;
- case S_dosFsLib_NOT_DIRECTORY: return ENOTDIR;
- case S_dosFsLib_NOT_SAME_VOLUME: return EXDEV;
- case S_dosFsLib_READ_ONLY: return EACCES;
- case S_dosFsLib_ROOT_DIR_FULL: return ENOSPC;
- case S_dosFsLib_DIR_NOT_EMPTY: return EEXIST;
- case S_dosFsLib_NO_LABEL: return ENXIO;
- case S_dosFsLib_INVALID_PARAMETER: return EINVAL;
- case S_dosFsLib_NO_CONTIG_SPACE: return ENOSPC;
- case S_dosFsLib_FD_OBSOLETE: return EBADF;
- case S_dosFsLib_DELETED: return EINVAL;
- case S_dosFsLib_INTERNAL_ERROR: return EIO;
- case S_dosFsLib_WRITE_ONLY: return EACCES;
- /* nfsLib mapping - is needed since Windriver has used */
- /* inconsistent error codes (errno.h/nfsLib.h). */
- case S_nfsLib_NFS_OK: return 0;
- case S_nfsLib_NFSERR_PERM: return EPERM;
- case S_nfsLib_NFSERR_NOENT: return ENOENT;
- case S_nfsLib_NFSERR_IO: return EIO;
- case S_nfsLib_NFSERR_NXIO: return ENXIO;
-#ifdef S_nfsLib_NFSERR_ACCES
- case S_nfsLib_NFSERR_ACCES: return EACCES;
-#else
- case S_nfsLib_NFSERR_ACCESS: return EACCES;
-#endif
- case S_nfsLib_NFSERR_EXIST: return EEXIST;
- case S_nfsLib_NFSERR_NODEV: return ENODEV;
- case S_nfsLib_NFSERR_NOTDIR: return ENOTDIR;
- case S_nfsLib_NFSERR_ISDIR: return EISDIR;
- case S_nfsLib_NFSERR_FBIG: return EFBIG;
- case S_nfsLib_NFSERR_NOSPC: return ENOSPC;
- case S_nfsLib_NFSERR_ROFS: return EROFS;
- case S_nfsLib_NFSERR_NAMETOOLONG: return ENAMETOOLONG;
- case S_nfsLib_NFSERR_NOTEMPTY: return EEXIST;
- case S_nfsLib_NFSERR_DQUOT: return ENOSPC;
- case S_nfsLib_NFSERR_STALE: return EINVAL;
- case S_nfsLib_NFSERR_WFLUSH: return ENXIO;
- /* And sometimes (...) the error codes are from ioLib (as in the */
- /* case of the (for nfsLib) unimplemented rename function) */
- case S_ioLib_DISK_NOT_PRESENT: return EIO;
-#if S_ioLib_DISK_NOT_PRESENT != S_ioLib_NO_DRIVER
- case S_ioLib_NO_DRIVER: return ENXIO;
-#endif
- case S_ioLib_UNKNOWN_REQUEST: return ENOSYS;
- case S_ioLib_DEVICE_TIMEOUT: return EIO;
-#ifdef S_ioLib_UNFORMATED
- /* Added (VxWorks 5.2 -> 5.3.1) */
- #if S_ioLib_UNFORMATED != S_ioLib_DEVICE_TIMEOUT
- case S_ioLib_UNFORMATED: return EIO;
- #endif
-#endif
-#if S_ioLib_DEVICE_TIMEOUT != S_ioLib_DEVICE_ERROR
- case S_ioLib_DEVICE_ERROR: return ENXIO;
-#endif
- case S_ioLib_WRITE_PROTECTED: return EACCES;
- case S_ioLib_NO_FILENAME: return EINVAL;
- case S_ioLib_CANCELLED: return EINTR;
- case S_ioLib_NO_DEVICE_NAME_IN_PATH: return EINVAL;
- case S_ioLib_NAME_TOO_LONG: return ENAMETOOLONG;
-#ifdef S_objLib_OBJ_UNAVAILABLE
- case S_objLib_OBJ_UNAVAILABLE: return ENOENT;
-#endif
-
- /* Temporary workaround for a weird error in passFs
- (VxWorks Simsparc only). File operation fails because of
- ENOENT, but errno is not set. */
-#ifdef SIMSPARCSOLARIS
- case 0: return ENOENT;
-#endif
-
- }
- /* If the error code matches none of the above, assume */
- /* it is a POSIX one already. The upper bits (>=16) are */
- /* cleared since VxWorks uses those bits to indicate in */
- /* what module the error occured. */
- return vx_errno & 0xffff;
-}
-
-static int
-vxworks_enotsup(Efile_error *errInfo)
-{
- errInfo->posix_errno = errInfo->os_errno = ENOTSUP;
- return 0;
-}
-
-static int
-count_path_length(char *pathname, char *pathname2)
-{
- static int stack[PATH_MAX / 2 + 1];
- int sp = 0;
- char *tmp;
- char *cpy = NULL;
- int i;
- int sum;
- for(i = 0;i < 2;++i) {
- if (!i) {
- cpy = EF_SAFE_ALLOC(strlen(pathname)+1);
- strcpy(cpy, pathname);
- } else if (pathname2 != NULL) {
- EF_FREE(cpy);
- cpy = EF_SAFE_ALLOC(strlen(pathname2)+1);
- strcpy(cpy, pathname2);
- } else
- break;
-
- for (tmp = strtok(cpy,"/"); tmp != NULL; tmp = strtok(NULL,"/")) {
- if (!strcmp(tmp,"..") && sp > 0)
- --sp;
- else if (strcmp(tmp,"."))
- stack[sp++] = strlen(tmp);
- }
- }
- if (cpy != NULL)
- EF_FREE(cpy);
- sum = 0;
- for(i = 0;i < sp; ++i)
- sum += stack[i]+1;
- return (sum) ? sum : 1;
-}
-
-static int
-path_size(char *pathname)
-{
- static char currdir[PATH_MAX+2];
- if (*pathname == '/')
- return count_path_length(pathname,NULL);
- ioDefPathGet(currdir);
- strcat(currdir,"/");
- return count_path_length(currdir,pathname);
-}
-
-#endif /* VXWORKS */
-
int
efile_mkdir(Efile_error* errInfo, /* Where to return error codes. */
char* name) /* Name of directory to create. */
{
- CHECK_PATHLEN(name,errInfo);
#ifdef NO_MKDIR_MODE
-#ifdef VXWORKS
- /* This is a VxWorks/nfs workaround for erl_tar to create
- * non-existant directories. (of some reason (...) VxWorks
- * returns, the *non-module-prefixed*, 0xd code when
- * trying to create a directory in a directory that doesn't exist).
- * (see efile_openfile)
- */
- if (mkdir(name) < 0) {
- struct stat sb;
- if (name[0] == '\0') {
- /* Return the correct error code enoent */
- errno = S_nfsLib_NFSERR_NOENT;
- } else if (stat(name, &sb) == OK) {
- errno = S_nfsLib_NFSERR_EXIST;
- } else if((strchr(name, '/') != NULL) && (errno == 0xd)) {
- /* Return the correct error code enoent */
- errno = S_nfsLib_NFSERR_NOENT;
- }
- return check_error(-1, errInfo);
- } else return 1;
-#else
return check_error(mkdir(name), errInfo);
-#endif
#else
return check_error(mkdir(name, DIR_MODE), errInfo);
#endif
@@ -372,16 +116,9 @@ int
efile_rmdir(Efile_error* errInfo, /* Where to return error codes. */
char* name) /* Name of directory to delete. */
{
- CHECK_PATHLEN(name, errInfo);
if (rmdir(name) == 0) {
return 1;
}
-#ifdef VXWORKS
- if (name[0] == '\0') {
- /* Return the correct error code enoent */
- errno = S_nfsLib_NFSERR_NOENT;
- }
-#else
if (errno == ENOTEMPTY) {
errno = EEXIST;
}
@@ -401,7 +138,6 @@ efile_rmdir(Efile_error* errInfo, /* Where to return error codes. */
}
errno = saved_errno;
}
-#endif
return check_error(-1, errInfo);
}
@@ -409,7 +145,6 @@ int
efile_delete_file(Efile_error* errInfo, /* Where to return error codes. */
char* name) /* Name of file to delete. */
{
- CHECK_PATHLEN(name,errInfo);
if (unlink(name) == 0) {
return 1;
}
@@ -457,32 +192,13 @@ efile_rename(Efile_error* errInfo, /* Where to return error codes. */
char* src, /* Original name. */
char* dst) /* New name. */
{
- CHECK_PATHLEN(src,errInfo);
- CHECK_PATHLEN(dst,errInfo);
-#ifdef VXWORKS
-
- /* First check if src == dst, if so, just return. */
- /* VxWorks dos file system destroys the file otherwise, */
- /* VxWorks nfs file system rename doesn't work at all. */
- if(strcmp(src, dst) == 0)
- return 1;
-#endif
if (rename(src, dst) == 0) {
return 1;
}
-#ifdef VXWORKS
- /* nfs for VxWorks doesn't support rename. We try to emulate it */
- /* (by first copying src to dst and then deleting src). */
- if(errno == S_ioLib_UNKNOWN_REQUEST && /* error code returned
- by ioLib (!) */
- copy(src, dst) == OK &&
- unlink(src) == OK)
- return 1;
-#endif
if (errno == ENOTEMPTY) {
errno = EEXIST;
}
-#if defined (sparc) && !defined(VXWORKS)
+#if defined (sparc)
/*
* SunOS 4.1.4 reports overwriting a non-empty directory with a
* directory as EINVAL instead of EEXIST (first rule out the correct
@@ -543,7 +259,6 @@ int
efile_chdir(Efile_error* errInfo, /* Where to return error codes. */
char* name) /* Name of directory to make current. */
{
- CHECK_PATHLEN(name, errInfo);
return check_error(chdir(name), errInfo);
}
@@ -600,8 +315,6 @@ efile_readdir(Efile_error* errInfo, /* Where to return error codes. */
* If this is the first call, we must open the directory.
*/
- CHECK_PATHLEN(name, errInfo);
-
if (*p_dir_handle == NULL) {
dp = opendir(name);
if (dp == NULL)
@@ -641,26 +354,8 @@ efile_openfile(Efile_error* errInfo, /* Where to return error codes. */
struct stat statbuf;
int fd;
int mode; /* Open mode. */
-#ifdef VXWORKS
- char pathbuff[PATH_MAX+2];
- char sbuff[PATH_MAX*2];
- char *totbuff = sbuff;
- int nameneed;
-#endif
-
-
- CHECK_PATHLEN(name, errInfo);
-
-#ifdef VXWORKS
- /* Have to check that it's not a directory. */
- if (stat(name,&statbuf) != ERROR && ISDIR(statbuf)) {
- errno = EISDIR;
- return check_error(-1, errInfo);
- }
-#endif
if (stat(name, &statbuf) >= 0 && !ISREG(statbuf)) {
-#if !defined(VXWORKS) && !defined(OSE)
/*
* For UNIX only, here is some ugly code to allow
* /dev/null to be opened as a file.
@@ -677,12 +372,9 @@ efile_openfile(Efile_error* errInfo, /* Where to return error codes. */
}
}
if (!(dev_null_ino && statbuf.st_ino == dev_null_ino)) {
-#endif
errno = EISDIR;
return check_error(-1, errInfo);
-#if !defined(VXWORKS) && !defined(OSE)
}
-#endif
}
switch (flags & (EFILE_MODE_READ|EFILE_MODE_WRITE)) {
@@ -706,49 +398,14 @@ efile_openfile(Efile_error* errInfo, /* Where to return error codes. */
if (flags & EFILE_MODE_APPEND) {
mode &= ~O_TRUNC;
-#ifndef VXWORKS
- mode |= O_APPEND; /* Dont make VxWorks think things it shouldn't */
-#endif
+ mode |= O_APPEND;
}
if (flags & EFILE_MODE_EXCL) {
mode |= O_EXCL;
}
-#ifdef VXWORKS
- if (*name != '/') {
- /* Make sure it is an absolute pathname, because ftruncate needs it */
- ioDefPathGet(pathbuff);
- strcat(pathbuff,"/");
- nameneed = strlen(pathbuff) + strlen(name) + 1;
- if (nameneed > PATH_MAX*2)
- totbuff = EF_SAFE_ALLOC(nameneed);
- strcpy(totbuff,pathbuff);
- strcat(totbuff,name);
- fd = open(totbuff, mode, FILE_MODE);
- if (totbuff != sbuff)
- EF_FREE(totbuff);
- } else {
- fd = open(name, mode, FILE_MODE);
- }
-#else
fd = open(name, mode, FILE_MODE);
-#endif
-
-#ifdef VXWORKS
-
- /* This is a VxWorks/nfs workaround for erl_tar to create
- * non-existant directories. (of some reason (...) VxWorks
- * returns, the *non-module-prefixed*, 0xd code when
- * trying to write a file in a directory that doesn't exist).
- * (see efile_mkdir)
- */
- if ((fd < 0) && (strchr(name, '/') != NULL) && (errno == 0xd)) {
- /* Return the correct error code enoent */
- errno = S_nfsLib_NFSERR_NOENT;
- return check_error(-1, errInfo);
- }
-#endif
if (!check_error(fd, errInfo))
return 0;
@@ -797,11 +454,7 @@ efile_fsync(Efile_error *errInfo, /* Where to return error codes. */
int fd) /* File descriptor for file to sync. */
{
#ifdef NO_FSYNC
-#ifdef VXWORKS
- return check_error(ioctl(fd, FIOSYNC, 0), errInfo);
-#else
- undefined fsync
-#endif /* VXWORKS */
+ undefined fsync /* XXX: Really? */
#else
#if defined(DARWIN) && defined(F_FULLFSYNC)
return check_error(fcntl(fd, F_FULLFSYNC), errInfo);
@@ -818,21 +471,8 @@ efile_fileinfo(Efile_error* errInfo, Efile_info* pInfo,
struct stat statbuf; /* Information about the file */
int result;
-#ifdef VXWORKS
- if (*name == '\0') {
- errInfo->posix_errno = errInfo->os_errno = ENOENT;
- return 0;
- }
-#endif
-
- CHECK_PATHLEN(name, errInfo);
-
if (info_for_link) {
-#if (defined(VXWORKS))
- result = stat(name, &statbuf);
-#else
result = lstat(name, &statbuf);
-#endif
} else {
result = stat(name, &statbuf);
}
@@ -849,19 +489,9 @@ efile_fileinfo(Efile_error* errInfo, Efile_info* pInfo,
#ifdef NO_ACCESS
/* Just look at read/write access for owner. */
-#ifdef VXWORKS
-
- pInfo->access = FA_NONE;
- if(statbuf.st_mode & S_IRUSR)
- pInfo->access |= FA_READ;
- if(statbuf.st_mode & S_IWUSR)
- pInfo->access |= FA_WRITE;
-
-#else
pInfo->access = ((statbuf.st_mode >> 6) & 07) >> 1;
-#endif /* VXWORKS */
#else
pInfo->access = FA_NONE;
if (access(name, R_OK) == 0)
@@ -902,35 +532,6 @@ efile_write_info(Efile_error *errInfo, Efile_info *pInfo, char *name)
{
struct utimbuf tval;
- CHECK_PATHLEN(name, errInfo);
-
-#ifdef VXWORKS
-
- if (pInfo->mode != -1) {
- int fd;
- struct stat statbuf;
-
- fd = open(name, O_RDONLY, 0);
- if (!check_error(fd, errInfo))
- return 0;
- if (fstat(fd, &statbuf) < 0) {
- close(fd);
- return check_error(-1, errInfo);
- }
- if (pInfo->mode & S_IWUSR) {
- /* clear read only bit */
- statbuf.st_attrib &= ~DOS_ATTR_RDONLY;
- } else {
- /* set read only bit */
- statbuf.st_attrib |= DOS_ATTR_RDONLY;
- }
- /* This should work for dos files but not for nfs ditos, so don't
- * report errors (to avoid problems when running e.g. erl_tar)
- */
- ioctl(fd, FIOATTRIBSET, statbuf.st_attrib);
- close(fd);
- }
-#else
/*
* On some systems chown will always fail for a non-root user unless
* POSIX_CHOWN_RESTRICTED is not set. Others will succeed as long as
@@ -952,20 +553,10 @@ efile_write_info(Efile_error *errInfo, Efile_info *pInfo, char *name)
}
}
-#endif /* !VXWORKS */
-
tval.actime = pInfo->accessTime;
tval.modtime = pInfo->modifyTime;
-#ifdef VXWORKS
- /* VxWorks' utime doesn't work when the file is a nfs mounted
- * one, don't report error if utime fails.
- */
- utime(name, &tval);
- return 1;
-#else
return check_error(utime(name, &tval), errInfo);
-#endif
}
@@ -979,11 +570,6 @@ efile_write(Efile_error* errInfo, /* Where to return error codes. */
{
ssize_t written; /* Bytes written in last operation. */
-#ifdef VXWORKS
- if (flags & EFILE_MODE_APPEND) {
- lseek(fd, 0, SEEK_END); /* Naive append emulation on VXWORKS */
- }
-#endif
while (count > 0) {
if ((written = write(fd, buf, count)) < 0) {
if (errno != EINTR)
@@ -1012,12 +598,6 @@ efile_writev(Efile_error* errInfo, /* Where to return error codes */
ASSERT(iovcnt >= 0);
-#ifdef VXWORKS
- if (flags & EFILE_MODE_APPEND) {
- lseek(fd, 0, SEEK_END); /* Naive append emulation on VXWORKS */
- }
-#endif
-
while (cnt < iovcnt) {
if ((! iov[cnt].iov_base) || (iov[cnt].iov_len <= 0)) {
/* Empty buffer - skip */
@@ -1226,118 +806,6 @@ efile_seek(Efile_error* errInfo, /* Where to return error codes. */
int
efile_truncate_file(Efile_error* errInfo, int *fd, int flags)
{
-#ifdef VXWORKS
- off_t offset;
- char namebuf[PATH_MAX+1];
- char namebuf2[PATH_MAX+10];
- int new;
- int dummy;
- int i;
- int left;
- static char buff[1024];
- struct stat st;
- Efile_error tmperr;
-
- if ((offset = lseek(*fd, 0, 1)) < 0) {
- return check_error((int) offset,errInfo);
- }
- if (ftruncate(*fd, offset) < 0) {
- if (vxworks_to_posix(errno) != EINVAL) {
- return check_error(-1, errInfo);
- }
- /*
- ** Kludge
- */
- if(ioctl(*fd,FIOGETNAME,(int) namebuf) < 0) {
- return check_error(-1, errInfo);
- }
- for(i=0;i<1000;++i) {
- sprintf(namebuf2,"%s%d",namebuf,i);
- CHECK_PATHLEN(namebuf2,errInfo);
- if (stat(namebuf2,&st) < 0) {
- break;
- }
- }
- if (i > 1000) {
- errno = EINVAL;
- return check_error(-1, errInfo);
- }
- if (close(*fd) < 0) {
- return check_error(-1, errInfo);
- }
- if (efile_rename(&tmperr,namebuf,namebuf2) < 0) {
- i = check_error(-1,&tmperr);
- if (!efile_openfile(errInfo,namebuf,flags | EFILE_NO_TRUNCATE,
- fd,&dummy)) {
- *fd = -1;
- } else {
- *errInfo = tmperr;
- }
- return i;
- }
- if ((*fd = open(namebuf2, O_RDONLY, 0)) < 0) {
- i = check_error(-1,errInfo);
- efile_rename(&tmperr,namebuf2,namebuf); /* at least try */
- if (!efile_openfile(errInfo,namebuf,flags | EFILE_NO_TRUNCATE,
- fd,&dummy)) {
- *fd = -1;
- } else {
- lseek(*fd,offset,SEEK_SET);
- }
- return i;
- }
- /* Point of no return... */
-
- if ((new = open(namebuf,O_RDWR | O_CREAT, FILE_MODE)) < 0) {
- close(*fd);
- *fd = -1;
- return 0;
- }
- left = offset;
-
- while (left) {
- if ((i = read(*fd,buff,(left > 1024) ? 1024 : left)) < 0) {
- i = check_error(-1,errInfo);
- close(new);
- close(*fd);
- unlink(namebuf);
- efile_rename(&tmperr,namebuf2,namebuf); /* at least try */
- if (!efile_openfile(errInfo,namebuf,flags | EFILE_NO_TRUNCATE,
- fd,&dummy)) {
- *fd = -1;
- } else {
- lseek(*fd,offset,SEEK_SET);
- }
- return i;
- }
- left -= i;
- if (write(new,buff,i) < 0) {
- i = check_error(-1,errInfo);
- close(new);
- close(*fd);
- unlink(namebuf);
- rename(namebuf2,namebuf); /* at least try */
- if (!efile_openfile(errInfo,namebuf,flags | EFILE_NO_TRUNCATE,
- fd,&dummy)) {
- *fd = -1;
- } else {
- lseek(*fd,offset,SEEK_SET);
- }
- return i;
- }
- }
- close(*fd);
- unlink(namebuf2);
- close(new);
- i = efile_openfile(errInfo,namebuf,flags | EFILE_NO_TRUNCATE,fd,
- &dummy);
- if (i) {
- lseek(*fd,offset,SEEK_SET);
- }
- return i;
- }
- return 1;
-#else
#ifndef NO_FTRUNCATE
off_t offset;
@@ -1347,15 +815,11 @@ efile_truncate_file(Efile_error* errInfo, int *fd, int flags)
#else
return 1;
#endif
-#endif
}
int
efile_readlink(Efile_error* errInfo, char* name, char* buffer, size_t size)
{
-#ifdef VXWORKS
- return vxworks_enotsup(errInfo);
-#else
int len;
ASSERT(size > 0);
len = readlink(name, buffer, size-1);
@@ -1364,7 +828,6 @@ efile_readlink(Efile_error* errInfo, char* name, char* buffer, size_t size)
}
buffer[len] = '\0';
return 1;
-#endif
}
int
@@ -1377,21 +840,13 @@ efile_altname(Efile_error* errInfo, char* name, char* buffer, size_t size)
int
efile_link(Efile_error* errInfo, char* old, char* new)
{
-#ifdef VXWORKS
- return vxworks_enotsup(errInfo);
-#else
return check_error(link(old, new), errInfo);
-#endif
}
int
efile_symlink(Efile_error* errInfo, char* old, char* new)
{
-#ifdef VXWORKS
- return vxworks_enotsup(errInfo);
-#else
return check_error(symlink(old, new), errInfo);
-#endif
}
int
@@ -1406,8 +861,8 @@ efile_fadvise(Efile_error* errInfo, int fd, Sint64 offset,
}
#ifdef HAVE_SENDFILE
-// For some reason the maximum size_t cannot be used as the max size
-// 3GB seems to work on all platforms
+/* For some reason the maximum size_t cannot be used as the max size
+ 3GB seems to work on all platforms */
#define SENDFILE_CHUNK_SIZE ((1UL << 30) -1)
/*
@@ -1444,7 +899,7 @@ efile_sendfile(Efile_error* errInfo, int in_fd, int out_fd,
#if defined(__linux__)
ssize_t retval;
do {
- // check if *nbytes is 0 or greater than chunk size
+ /* check if *nbytes is 0 or greater than chunk size */
if (*nbytes == 0 || *nbytes > SENDFILE_CHUNK_SIZE)
retval = sendfile(out_fd, in_fd, offset, SENDFILE_CHUNK_SIZE);
else
@@ -1455,7 +910,7 @@ efile_sendfile(Efile_error* errInfo, int in_fd, int out_fd,
}
} while (retval == SENDFILE_CHUNK_SIZE);
if (written != 0) {
- // -1 is not returned by the linux API so we have to simulate it
+ /* -1 is not returned by the linux API so we have to simulate it */
retval = -1;
errno = EAGAIN;
}
@@ -1468,23 +923,29 @@ efile_sendfile(Efile_error* errInfo, int in_fd, int out_fd,
do {
fdrec.sfv_off = *offset;
len = 0;
- // check if *nbytes is 0 or greater than chunk size
+ /* check if *nbytes is 0 or greater than chunk size */
if (*nbytes == 0 || *nbytes > SENDFILE_CHUNK_SIZE)
fdrec.sfv_len = SENDFILE_CHUNK_SIZE;
else
fdrec.sfv_len = *nbytes;
retval = sendfilev(out_fd, &fdrec, 1, &len);
- if (retval != -1 || errno == EAGAIN || errno == EINTR) {
+
+ /* Sometimes sendfilev can return -1 and still send data.
+ When that happens we just pretend that no error happend. */
+ if (retval != -1 || errno == EAGAIN || errno == EINTR ||
+ len != 0) {
*offset += len;
*nbytes -= len;
written += len;
+ if (errno != EAGAIN && errno != EINTR && len != 0)
+ retval = len;
}
} while (len == SENDFILE_CHUNK_SIZE);
#elif defined(DARWIN)
int retval;
off_t len;
do {
- // check if *nbytes is 0 or greater than chunk size
+ /* check if *nbytes is 0 or greater than chunk size */
if(*nbytes > SENDFILE_CHUNK_SIZE)
len = SENDFILE_CHUNK_SIZE;
else
@@ -1516,3 +977,81 @@ efile_sendfile(Efile_error* errInfo, int in_fd, int out_fd,
return check_error(retval, errInfo);
}
#endif /* HAVE_SENDFILE */
+
+#ifdef HAVE_POSIX_FALLOCATE
+static int
+call_posix_fallocate(int fd, Sint64 offset, Sint64 length)
+{
+ int ret;
+
+ /*
+ * On Linux and Solaris for example, posix_fallocate() returns
+ * a positive error number on error and it does not set errno.
+ * On FreeBSD however (9.0 at least), it returns -1 on error
+ * and it sets errno.
+ */
+ do {
+ ret = posix_fallocate(fd, (off_t) offset, (off_t) length);
+ if (ret > 0) {
+ errno = ret;
+ ret = -1;
+ }
+ } while (ret != 0 && errno == EINTR);
+
+ return ret;
+}
+#endif /* HAVE_POSIX_FALLOCATE */
+
+int
+efile_fallocate(Efile_error* errInfo, int fd, Sint64 offset, Sint64 length)
+{
+#if defined HAVE_FALLOCATE
+ /* Linux specific, more efficient than posix_fallocate. */
+ int ret;
+
+ do {
+ ret = fallocate(fd, FALLOC_FL_KEEP_SIZE, (off_t) offset, (off_t) length);
+ } while (ret != 0 && errno == EINTR);
+
+#if defined HAVE_POSIX_FALLOCATE
+ /* Fallback to posix_fallocate if available. */
+ if (ret != 0) {
+ ret = call_posix_fallocate(fd, offset, length);
+ }
+#endif
+
+ return check_error(ret, errInfo);
+#elif defined F_PREALLOCATE
+ /* Mac OS X specific, equivalent to posix_fallocate. */
+ int ret;
+ fstore_t fs;
+
+ memset(&fs, 0, sizeof(fs));
+ fs.fst_flags = F_ALLOCATECONTIG;
+ fs.fst_posmode = F_VOLPOSMODE;
+ fs.fst_offset = (off_t) offset;
+ fs.fst_length = (off_t) length;
+
+ ret = fcntl(fd, F_PREALLOCATE, &fs);
+
+ if (-1 == ret) {
+ fs.fst_flags = F_ALLOCATEALL;
+ ret = fcntl(fd, F_PREALLOCATE, &fs);
+
+#if defined HAVE_POSIX_FALLOCATE
+ /* Fallback to posix_fallocate if available. */
+ if (-1 == ret) {
+ ret = call_posix_fallocate(fd, offset, length);
+ }
+#endif
+ }
+
+ return check_error(ret, errInfo);
+#elif defined HAVE_POSIX_FALLOCATE
+ /* Other Unixes, use posix_fallocate if available. */
+ return check_error(call_posix_fallocate(fd, offset, length), errInfo);
+#else
+ errno = ENOTSUP;
+ return check_error(-1, errInfo);
+#endif
+}
diff --git a/erts/emulator/drivers/win32/registry_drv.c b/erts/emulator/drivers/win32/registry_drv.c
index 1fad34e380..5b200ebd32 100644
--- a/erts/emulator/drivers/win32/registry_drv.c
+++ b/erts/emulator/drivers/win32/registry_drv.c
@@ -344,7 +344,7 @@ fix_value_result(RegPort* rp, LONG result, DWORD type,
#ifdef DEBUG
if (ok != ERROR_SUCCESS) {
char buff[256];
- sprintf(buff,"Failure in registry_drv line %d, error = %d",
+ erts_snprintf(buff, sizeof(buff), "Failure in registry_drv line %d, error = %d",
__LINE__, GetLastError());
MessageBox(NULL, buff, "Internal error", MB_OK);
ASSERT(ok == ERROR_SUCCESS);
diff --git a/erts/emulator/drivers/win32/win_con.c b/erts/emulator/drivers/win32/win_con.c
index 6b45b92cbe..7b9cadd32b 100644
--- a/erts/emulator/drivers/win32/win_con.c
+++ b/erts/emulator/drivers/win32/win_con.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1997-2011. All Rights Reserved.
+ * Copyright Ericsson AB 1997-2012. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -569,7 +569,7 @@ FrameWndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
return 1;
case WM_SETFOCUS :
CreateCaret(hClientWnd, NULL, cxChar, cyChar);
- SetCaretPos(GetXFromCurrentY(GetDC(hwnd),0,cur_x), (cur_y-iVscrollPos)*cyChar);
+ SetCaretPos(GetXFromCurrentY(GetDC(hClientWnd),iHscrollPos,cur_x), (cur_y-iVscrollPos)*cyChar);
ShowCaret(hClientWnd);
return 0;
case WM_KILLFOCUS:
diff --git a/erts/emulator/drivers/win32/win_efile.c b/erts/emulator/drivers/win32/win_efile.c
index 606fa1d7de..f2b0c8a843 100644
--- a/erts/emulator/drivers/win32/win_efile.c
+++ b/erts/emulator/drivers/win32/win_efile.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1997-2011. All Rights Reserved.
+ * Copyright Ericsson AB 1997-2012. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -41,6 +41,8 @@
#define IS_DOT_OR_DOTDOT(s) \
((s)[0] == L'.' && ((s)[1] == L'\0' || ((s)[1] == L'.' && (s)[2] == L'\0')))
+#define FILE_SHARE_FLAGS (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE)
+
#ifndef INVALID_FILE_ATTRIBUTES
#define INVALID_FILE_ATTRIBUTES ((DWORD) 0xFFFFFFFF)
#endif
@@ -724,7 +726,7 @@ efile_openfile(Efile_error* errInfo, /* Where to return error codes. */
crFlags = CREATE_NEW;
}
fd = CreateFileW(wname, access,
- FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ FILE_SHARE_FLAGS,
NULL, crFlags, FILE_ATTRIBUTE_NORMAL, NULL);
/*
@@ -897,7 +899,8 @@ efile_fileinfo(Efile_error* errInfo, Efile_info* pInfo,
we should be able to find its target */
WCHAR target_name[_MAX_PATH];
if (efile_readlink(errInfo, (char *) name,
- (char *) target_name,256) == 1) {
+ (char *) target_name,
+ _MAX_PATH * sizeof(WCHAR)) == 1) {
FindClose(findhandle);
return efile_fileinfo(errInfo, pInfo,
(char *) target_name, info_for_link);
@@ -908,7 +911,7 @@ efile_fileinfo(Efile_error* errInfo, Efile_info* pInfo,
{
HANDLE handle; /* Handle returned by CreateFile() */
BY_HANDLE_FILE_INFORMATION fileInfo; /* from CreateFile() */
- if (handle = CreateFileW(name, GENERIC_READ, 0,NULL,
+ if (handle = CreateFileW(name, GENERIC_READ, FILE_SHARE_FLAGS, NULL,
OPEN_EXISTING, 0, NULL)) {
GetFileInformationByHandle(handle, &fileInfo);
pInfo->links = fileInfo.nNumberOfLinks;
@@ -1020,7 +1023,7 @@ efile_write_info(Efile_error* errInfo,
}
fd = CreateFileW(wname, GENERIC_READ|GENERIC_WRITE,
- FILE_SHARE_READ | FILE_SHARE_WRITE,
+ FILE_SHARE_FLAGS,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (fd != INVALID_HANDLE_VALUE) {
BOOL result = SetFileTime(fd, &CreationFileTime, &AccessFileTime, &ModifyFileTime);
@@ -1383,10 +1386,10 @@ efile_readlink(Efile_error* errInfo, char* name, char* buffer, size_t size)
DWORD fileAttributes = GetFileAttributesW(wname);
if ((fileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
BOOLEAN success = 0;
- HANDLE h = CreateFileW(wname, GENERIC_READ, 0,NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+ HANDLE h = CreateFileW(wname, GENERIC_READ, FILE_SHARE_FLAGS, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
int len;
if(h != INVALID_HANDLE_VALUE) {
- success = pGetFinalPathNameByHandle(h, wbuffer, size,0);
+ success = pGetFinalPathNameByHandle(h, wbuffer, size / sizeof(WCHAR),0);
/* GetFinalPathNameByHandle prepends path with "\\?\": */
len = wcslen(wbuffer);
wmemmove(wbuffer,wbuffer+4,len-3);
@@ -1557,3 +1560,13 @@ efile_fadvise(Efile_error* errInfo, int fd, Sint64 offset,
errno = ERROR_SUCCESS;
return check_error(0, errInfo);
}
+
+int
+efile_fallocate(Efile_error* errInfo, int fd, Sint64 offset, Sint64 length)
+{
+ /* No file preallocation method available in Windows. */
+ errno = errno_map(ERROR_NOT_SUPPORTED);
+ SetLastError(ERROR_NOT_SUPPORTED);
+
+ return check_error(-1, errInfo);
+}
diff --git a/erts/emulator/hipe/hipe_amd64_bifs.m4 b/erts/emulator/hipe/hipe_amd64_bifs.m4
index ec25c0b9b7..0de69a617f 100644
--- a/erts/emulator/hipe/hipe_amd64_bifs.m4
+++ b/erts/emulator/hipe/hipe_amd64_bifs.m4
@@ -2,7 +2,7 @@ changecom(`/*', `*/')dnl
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2004-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2004-2012. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -30,12 +30,12 @@ include(`hipe/hipe_amd64_asm.m4')
#define TEST_GOT_EXN cmpq $THE_NON_VALUE, %rax
#endif'
-define(TEST_GOT_MBUF,`movq P_MBUF(P), %rdx # `TEST_GOT_MBUF'
+define(TEST_GOT_MBUF,`movq P_MBUF(P), %rdx /* `TEST_GOT_MBUF' */
testq %rdx, %rdx
jnz 3f
2:')
define(HANDLE_GOT_MBUF,`
-3: call nbif_$1_gc_after_bif # `HANDLE_GOT_MBUF'
+3: call nbif_$1_gc_after_bif /* `HANDLE_GOT_MBUF' */
jmp 2b')
`#if defined(ERTS_ENABLE_LOCK_CHECK) && defined(ERTS_SMP)
diff --git a/erts/emulator/hipe/hipe_arm.c b/erts/emulator/hipe/hipe_arm.c
index 651d0e3a75..3db3ffe9b1 100644
--- a/erts/emulator/hipe/hipe_arm.c
+++ b/erts/emulator/hipe/hipe_arm.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2005-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2005-2012. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
diff --git a/erts/emulator/hipe/hipe_arm_bifs.m4 b/erts/emulator/hipe/hipe_arm_bifs.m4
index 17c013f1fb..bd8bc5ab6b 100644
--- a/erts/emulator/hipe/hipe_arm_bifs.m4
+++ b/erts/emulator/hipe/hipe_arm_bifs.m4
@@ -2,7 +2,7 @@ changecom(`/*', `*/')dnl
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2005-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2005-2012. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
diff --git a/erts/emulator/hipe/hipe_bif0.c b/erts/emulator/hipe/hipe_bif0.c
index 74868b868c..1562748f2d 100644
--- a/erts/emulator/hipe/hipe_bif0.c
+++ b/erts/emulator/hipe/hipe_bif0.c
@@ -1583,14 +1583,6 @@ BIF_RETTYPE hipe_nonclosure_address(BIF_ALIST_2)
goto badfun;
m = ep->code[0];
f = ep->code[1];
- } else if (hdr == make_arityval(2)) {
- Eterm *tp = tuple_val(BIF_ARG_1);
- m = tp[1];
- f = tp[2];
- if (is_not_atom(m) || is_not_atom(f))
- goto badfun;
- if (!erts_active_export_entry(m, f, BIF_ARG_2))
- goto badfun;
} else
goto badfun;
address = hipe_get_na_nofail(m, f, BIF_ARG_2, 1);
@@ -1755,6 +1747,41 @@ BIF_RETTYPE hipe_bifs_mark_referred_from_1(BIF_ALIST_1) /* get_refs_from */
BIF_RET(NIL);
}
+/* Called by init:restart after unloading all hipe compiled modules
+ * to work around bug causing execution of deallocated beam code.
+ * Can be removed when delete/purge of native modules works better.
+ * Test: Do init:restart in debug compiled vm with hipe compiled kernel.
+ */
+static void hipe_purge_all_refs(void)
+{
+ struct hipe_mfa_info **bucket;
+ unsigned int i, nrbuckets;
+
+ hipe_mfa_info_table_lock();
+
+ bucket = hipe_mfa_info_table.bucket;
+ nrbuckets = 1 << hipe_mfa_info_table.log2size;
+ for (i = 0; i < nrbuckets; ++i) {
+ while (bucket[i] != NULL) {
+ struct hipe_mfa_info* mfa = bucket[i];
+ bucket[i] = mfa->bucket.next;
+
+ while (mfa->refers_to) {
+ struct hipe_mfa_info_list *to = mfa->refers_to;
+ mfa->refers_to = to->next;
+ erts_free(ERTS_ALC_T_HIPE, to);
+ }
+ while (mfa->referred_from) {
+ struct ref* from = mfa->referred_from;
+ mfa->referred_from = from->next;
+ erts_free(ERTS_ALC_T_HIPE, from);
+ }
+ erts_free(ERTS_ALC_T_HIPE, mfa);
+ }
+ }
+ hipe_mfa_info_table_unlock();
+}
+
BIF_RETTYPE hipe_bifs_remove_refs_from_1(BIF_ALIST_1)
{
struct mfa mfa;
@@ -1762,6 +1789,11 @@ BIF_RETTYPE hipe_bifs_remove_refs_from_1(BIF_ALIST_1)
struct hipe_mfa_info_list *refers_to, *tmp_refers_to;
struct ref **prev, *ref;
+ if (BIF_ARG_1 == am_all) {
+ hipe_purge_all_refs();
+ BIF_RET(NIL);
+ }
+
if (!term_to_mfa(BIF_ARG_1, &mfa))
BIF_ERROR(BIF_P, BADARG);
hipe_mfa_info_table_lock();
@@ -1799,6 +1831,7 @@ BIF_RETTYPE hipe_bifs_remove_refs_from_1(BIF_ALIST_1)
BIF_RET(NIL);
}
+
/* redirect_referred_from(CalleeMFA)
* Redirect all pending-redirect refs in CalleeMFA's referred_from.
* Then remove any pending-redirect && pending-remove refs from CalleeMFA's referred_from.
diff --git a/erts/emulator/hipe/hipe_bif1.c b/erts/emulator/hipe/hipe_bif1.c
index 64de754e18..56767ef04b 100644
--- a/erts/emulator/hipe/hipe_bif1.c
+++ b/erts/emulator/hipe/hipe_bif1.c
@@ -2,7 +2,7 @@
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2012. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
diff --git a/erts/emulator/hipe/hipe_bif2.c b/erts/emulator/hipe/hipe_bif2.c
index ac0c2e2ffa..e09988e2c5 100644
--- a/erts/emulator/hipe/hipe_bif2.c
+++ b/erts/emulator/hipe/hipe_bif2.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2012. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
diff --git a/erts/emulator/hipe/hipe_bif2.tab b/erts/emulator/hipe/hipe_bif2.tab
index ddbd31f155..45a395bf57 100644
--- a/erts/emulator/hipe/hipe_bif2.tab
+++ b/erts/emulator/hipe/hipe_bif2.tab
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2001-2011. All Rights Reserved.
+# Copyright Ericsson AB 2001-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -29,4 +29,4 @@ bif hipe_bifs:show_term/1
bif hipe_bifs:in_native/0
bif hipe_bifs:modeswitch_debug_on/0
bif hipe_bifs:modeswitch_debug_off/0
-bif hipe_bifs:debug_native_called/2 \ No newline at end of file
+bif hipe_bifs:debug_native_called/2
diff --git a/erts/emulator/hipe/hipe_bif_list.m4 b/erts/emulator/hipe/hipe_bif_list.m4
index d9aa09b79e..764b8d180c 100644
--- a/erts/emulator/hipe/hipe_bif_list.m4
+++ b/erts/emulator/hipe/hipe_bif_list.m4
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2004-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2004-2012. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -145,6 +145,7 @@
* Zero-arity BIFs that can fail.
*/
standard_bif_interface_0(nbif_processes_0, processes_0)
+standard_bif_interface_0(nbif_ports_0, ports_0)
/*
* BIFs and primops that may do a GC (change heap limit and walk the native stack).
@@ -246,7 +247,7 @@ noproc_primop_interface_5(nbif_bs_put_big_integer, hipe_bs_put_big_integer)
gc_bif_interface_0(nbif_check_get_msg, hipe_check_get_msg)
-#ifdef NO_FPE_SIGNALS
+#`ifdef' NO_FPE_SIGNALS
nocons_nofail_primop_interface_0(nbif_emulate_fpe, hipe_emulate_fpe)
#endif
diff --git a/erts/emulator/hipe/hipe_debug.c b/erts/emulator/hipe/hipe_debug.c
index 37615bf718..f2e9d03607 100644
--- a/erts/emulator/hipe/hipe_debug.c
+++ b/erts/emulator/hipe/hipe_debug.c
@@ -189,11 +189,10 @@ void hipe_print_pcb(Process *p)
U("old_head ", old_heap);
U("min_heap_..", min_heap_size);
U("rcount ", rcount);
- U("id ", id);
- U("prio ", prio);
+ U("id ", common.id);
U("reds ", reds);
- U("tracer_pr..", tracer_proc);
- U("trace_fla..", trace_flags);
+ U("tracer_pr..", common.tracer_proc);
+ U("trace_fla..", common.trace_flags);
U("group_lea..", group_leader);
U("flags ", flags);
U("fvalue ", fvalue);
@@ -202,8 +201,8 @@ void hipe_print_pcb(Process *p)
/*XXX: ErlTimer tm; */
U("next ", next);
/*XXX: ErlOffHeap off_heap; */
- U("reg ", reg);
- U("nlinks ", nlinks);
+ U("reg ", common.u.alive.reg);
+ U("nlinks ", common.u.alive.links);
/*XXX: ErlMessageQueue msg; */
U("mbuf ", mbuf);
U("mbuf_sz ", mbuf_sz);
diff --git a/erts/emulator/hipe/hipe_gc.c b/erts/emulator/hipe/hipe_gc.c
index 07e4b8a4d6..86c4068072 100644
--- a/erts/emulator/hipe/hipe_gc.c
+++ b/erts/emulator/hipe/hipe_gc.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2004-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2004-2012. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
diff --git a/erts/emulator/hipe/hipe_mkliterals.c b/erts/emulator/hipe/hipe_mkliterals.c
index 6e9041c84a..0e287908b1 100644
--- a/erts/emulator/hipe/hipe_mkliterals.c
+++ b/erts/emulator/hipe/hipe_mkliterals.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2012. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -262,47 +262,6 @@ static const struct literal {
const char *name;
int value;
} literals[] = {
- /* Field offsets in a process struct */
- { "P_HP", offsetof(struct process, htop) },
- { "P_HP_LIMIT", offsetof(struct process, stop) },
- { "P_OFF_HEAP_FIRST", offsetof(struct process, off_heap.first) },
- { "P_MBUF", offsetof(struct process, mbuf) },
- { "P_ID", offsetof(struct process, id) },
- { "P_FLAGS", offsetof(struct process, flags) },
- { "P_FVALUE", offsetof(struct process, fvalue) },
- { "P_FREASON", offsetof(struct process, freason) },
- { "P_FTRACE", offsetof(struct process, ftrace) },
- { "P_FCALLS", offsetof(struct process, fcalls) },
- { "P_BEAM_IP", offsetof(struct process, i) },
- { "P_ARITY", offsetof(struct process, arity) },
- { "P_ARG0", offsetof(struct process, def_arg_reg[0]) },
- { "P_ARG1", offsetof(struct process, def_arg_reg[1]) },
- { "P_ARG2", offsetof(struct process, def_arg_reg[2]) },
- { "P_ARG3", offsetof(struct process, def_arg_reg[3]) },
- { "P_ARG4", offsetof(struct process, def_arg_reg[4]) },
- { "P_ARG5", offsetof(struct process, def_arg_reg[5]) },
-#ifdef HIPE
- { "P_NSP", offsetof(struct process, hipe.nsp) },
- { "P_NCALLEE", offsetof(struct process, hipe.ncallee) },
- { "P_CLOSURE", offsetof(struct process, hipe.closure) },
-#if defined(__i386__) || defined(__x86_64__)
- { "P_NSP_LIMIT", offsetof(struct process, hipe.nstack) },
- { "P_CSP", offsetof(struct process, hipe.ncsp) },
-#elif defined(__sparc__) || defined(__powerpc__) || defined(__ppc__) || defined(__powerpc64__) || defined(__arm__)
- { "P_NSP_LIMIT", offsetof(struct process, hipe.nstack) },
- { "P_NRA", offsetof(struct process, hipe.nra) },
-#endif
- { "P_NARITY", offsetof(struct process, hipe.narity) },
- { "P_FLOAT_RESULT",
-# ifdef NO_FPE_SIGNALS
- offsetof(struct process, hipe.float_result)
-# endif
- },
-# if defined(ERTS_ENABLE_LOCK_CHECK) && defined(ERTS_SMP)
- { "P_BIF_CALLEE", offsetof(struct process, hipe.bif_callee) },
-# endif
-#endif /* HIPE */
-
/* process flags bits */
{ "F_TIMO", F_TIMO },
@@ -380,8 +339,6 @@ static const struct literal {
{ "MS_SAVEOFFSET_SIZE", field_sizeof(struct erl_bin_match_struct, save_offset)},
/* messages */
- { "P_MSG_FIRST", offsetof(struct process, msg.first) },
- { "P_MSG_SAVE", offsetof(struct process, msg.save) },
{ "MSG_NEXT", offsetof(struct erl_mesg, next) },
/* ARM */
@@ -460,12 +417,14 @@ static const struct atom_literal {
* These depend on configuration options such as heap architecture.
* The compiler accesses these through hipe_bifs:get_rts_param/1.
*/
-static const struct rts_param {
+struct rts_param {
unsigned int nr;
const char *name;
unsigned int is_defined;
int value;
-} rts_params[] = {
+};
+
+static const struct rts_param rts_params[] = {
{ 1, "P_OFF_HEAP_FUNS",
1, offsetof(struct process, off_heap.first)
},
@@ -518,7 +477,53 @@ static const struct rts_param {
{ 19, "MSG_MESSAGE",
1, offsetof(struct erl_mesg, m[0])
},
- /* highest entry ever used == 21 */
+
+ /* Field offsets in a process struct */
+ { 22, "P_HP", 1, offsetof(struct process, htop) },
+ { 23, "P_HP_LIMIT", 1, offsetof(struct process, stop) },
+ { 24, "P_OFF_HEAP_FIRST", 1, offsetof(struct process, off_heap.first) },
+ { 25, "P_MBUF", 1, offsetof(struct process, mbuf) },
+ { 26, "P_ID", 1, offsetof(struct process, common.id) },
+ { 27, "P_FLAGS", 1, offsetof(struct process, flags) },
+ { 28, "P_FVALUE", 1, offsetof(struct process, fvalue) },
+ { 29, "P_FREASON", 1, offsetof(struct process, freason) },
+ { 30, "P_FTRACE", 1, offsetof(struct process, ftrace) },
+ { 31, "P_FCALLS", 1, offsetof(struct process, fcalls) },
+ { 32, "P_BEAM_IP", 1, offsetof(struct process, i) },
+ { 33, "P_ARITY", 1, offsetof(struct process, arity) },
+ { 34, "P_ARG0", 1, offsetof(struct process, def_arg_reg[0]) },
+ { 35, "P_ARG1", 1, offsetof(struct process, def_arg_reg[1]) },
+ { 36, "P_ARG2", 1, offsetof(struct process, def_arg_reg[2]) },
+ { 37, "P_ARG3", 1, offsetof(struct process, def_arg_reg[3]) },
+ { 38, "P_ARG4", 1, offsetof(struct process, def_arg_reg[4]) },
+ { 39, "P_ARG5", 1, offsetof(struct process, def_arg_reg[5]) },
+ { 40, "P_NSP", 1, offsetof(struct process, hipe.nsp) },
+ { 41, "P_NCALLEE", 1, offsetof(struct process, hipe.ncallee) },
+ { 42, "P_CLOSURE", 1, offsetof(struct process, hipe.closure) },
+ { 43, "P_NSP_LIMIT", 1, offsetof(struct process, hipe.nstack) },
+ { 44, "P_CSP",
+#if defined(__i386__) || defined(__x86_64__)
+ 1, offsetof(struct process, hipe.ncsp)
+#endif
+ },
+ { 45, "P_NRA",
+#if defined(__sparc__) || defined(__powerpc__) || defined(__ppc__) || defined(__powerpc64__) || defined(__arm__)
+ 1, offsetof(struct process, hipe.nra)
+#endif
+ },
+ { 46, "P_NARITY", 1, offsetof(struct process, hipe.narity) },
+ { 47, "P_FLOAT_RESULT",
+#ifdef NO_FPE_SIGNALS
+ 1, offsetof(struct process, hipe.float_result)
+#endif
+ },
+ { 48, "P_BIF_CALLEE",
+#if defined(ERTS_ENABLE_LOCK_CHECK) && defined(ERTS_SMP)
+ 1, offsetof(struct process, hipe.bif_callee)
+#endif
+ },
+ { 49, "P_MSG_FIRST", 1, offsetof(struct process, msg.first) },
+ { 50, "P_MSG_SAVE", 1, offsetof(struct process, msg.save) },
};
#define NR_PARAMS ARRAY_SIZE(rts_params)
diff --git a/erts/emulator/hipe/hipe_ppc_bifs.m4 b/erts/emulator/hipe/hipe_ppc_bifs.m4
index d09551d10d..7cc2b5c7b6 100644
--- a/erts/emulator/hipe/hipe_ppc_bifs.m4
+++ b/erts/emulator/hipe/hipe_ppc_bifs.m4
@@ -2,7 +2,7 @@ changecom(`/*', `*/')dnl
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2004-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2004-2012. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -32,12 +32,12 @@ include(`hipe/hipe_ppc_asm.m4')
.text
.p2align 2
-define(TEST_GOT_MBUF,`LOAD r4, P_MBUF(P) # `TEST_GOT_MBUF'
+define(TEST_GOT_MBUF,`LOAD r4, P_MBUF(P) /* `TEST_GOT_MBUF' */
CMPI r4, 0
bne- 3f
2:')
define(HANDLE_GOT_MBUF,`
-3: bl CSYM(nbif_$1_gc_after_bif) # `HANDLE_GOT_MBUF'
+3: bl CSYM(nbif_$1_gc_after_bif) /* `HANDLE_GOT_MBUF' */
b 2b')
@@ -62,7 +62,7 @@ ASYM($1):
/* Save caller-save registers and call the C function. */
SAVE_CONTEXT_BIF
- STORE r4, P_ARG0(r3) # Store BIF__ARGS in def_arg_reg[]
+ STORE r4, P_ARG0(r3) /* Store BIF__ARGS in def_arg_reg[] */
addi r4, r3, P_ARG0
CALL_BIF($2)
TEST_GOT_MBUF
@@ -92,7 +92,7 @@ ASYM($1):
/* Save caller-save registers and call the C function. */
SAVE_CONTEXT_BIF
- STORE r4, P_ARG0(r3) # Store BIF__ARGS in def_arg_reg[]
+ STORE r4, P_ARG0(r3) /* Store BIF__ARGS in def_arg_reg[] */
STORE r5, P_ARG1(r3)
addi r4, r3, P_ARG0
CALL_BIF($2)
@@ -124,7 +124,7 @@ ASYM($1):
/* Save caller-save registers and call the C function. */
SAVE_CONTEXT_BIF
- STORE r4, P_ARG0(r3) # Store BIF__ARGS in def_arg_reg[]
+ STORE r4, P_ARG0(r3) /* Store BIF__ARGS in def_arg_reg[] */
STORE r5, P_ARG1(r3)
STORE r6, P_ARG2(r3)
addi r4, r3, P_ARG0
@@ -214,7 +214,7 @@ ASYM($1):
/* Save caller-save registers and call the C function. */
SAVE_CONTEXT_GC
- STORE r4, P_ARG0(r3) # Store BIF__ARGS in def_arg_reg[]
+ STORE r4, P_ARG0(r3) /* Store BIF__ARGS in def_arg_reg[] */
addi r4, r3, P_ARG0
CALL_BIF($2)
TEST_GOT_MBUF
@@ -244,7 +244,7 @@ ASYM($1):
/* Save caller-save registers and call the C function. */
SAVE_CONTEXT_GC
- STORE r4, P_ARG0(r3) # Store BIF__ARGS in def_arg_reg[]
+ STORE r4, P_ARG0(r3) /* Store BIF__ARGS in def_arg_reg[] */
STORE r5, P_ARG1(r3)
addi r4, r3, P_ARG0
CALL_BIF($2)
diff --git a/erts/emulator/hipe/hipe_stack.h b/erts/emulator/hipe/hipe_stack.h
index da706ae25e..66f9f04c73 100644
--- a/erts/emulator/hipe/hipe_stack.h
+++ b/erts/emulator/hipe/hipe_stack.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2012. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
diff --git a/erts/emulator/hipe/hipe_x86.c b/erts/emulator/hipe/hipe_x86.c
index 24d232c968..4281730ae2 100644
--- a/erts/emulator/hipe/hipe_x86.c
+++ b/erts/emulator/hipe/hipe_x86.c
@@ -265,7 +265,7 @@ void *hipe_make_native_stub(void *beamAddress, unsigned int beamArity)
void hipe_arch_print_pcb(struct hipe_process_state *p)
{
#define U(n,x) \
- printf(" % 4d | %s | 0x%08x | |\r\n", offsetof(struct hipe_process_state,x), n, (unsigned)p->x)
+ printf(" % 4d | %s | 0x%08x | |\r\n", (int)offsetof(struct hipe_process_state,x), n, (unsigned)p->x)
U("ncsp ", ncsp);
U("narity ", narity);
#undef U
diff --git a/erts/emulator/hipe/hipe_x86_bifs.m4 b/erts/emulator/hipe/hipe_x86_bifs.m4
index 3cb7d67be0..dd6980f555 100644
--- a/erts/emulator/hipe/hipe_x86_bifs.m4
+++ b/erts/emulator/hipe/hipe_x86_bifs.m4
@@ -2,7 +2,7 @@ changecom(`/*', `*/')dnl
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2012. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -35,12 +35,12 @@ include(`hipe/hipe_x86_asm.m4')
# define CALL_BIF(F) call CSYM(F)
#endif'
-define(TEST_GOT_MBUF,`movl P_MBUF(P), %edx # `TEST_GOT_MBUF'
+define(TEST_GOT_MBUF,`movl P_MBUF(P), %edx /* `TEST_GOT_MBUF' */
testl %edx, %edx
jnz 3f
2:')
define(HANDLE_GOT_MBUF,`
-3: call nbif_$1_gc_after_bif # `HANDLE_GOT_MBUF'
+3: call nbif_$1_gc_after_bif /* `HANDLE_GOT_MBUF' */
jmp 2b')
/*
@@ -70,7 +70,7 @@ ASYM($1):
NBIF_ARG_REG(0,P)
NBIF_ARG(2,1,0)
lea 8(%esp), %eax
- NBIF_ARG_REG(1,%eax) # BIF__ARGS
+ NBIF_ARG_REG(1,%eax) /* BIF__ARGS */
CALL_BIF($2)
TEST_GOT_MBUF
@@ -105,7 +105,7 @@ ASYM($1):
NBIF_ARG(2,2,0)
NBIF_ARG(3,2,1)
lea 8(%esp), %eax
- NBIF_ARG_REG(1,%eax) # BIF__ARGS
+ NBIF_ARG_REG(1,%eax) /* BIF__ARGS */
CALL_BIF($2)
TEST_GOT_MBUF
@@ -141,7 +141,7 @@ ASYM($1):
NBIF_ARG(3,3,1)
NBIF_ARG(4,3,2)
lea 8(%esp), %eax
- NBIF_ARG_REG(1,%eax) # BIF__ARGS
+ NBIF_ARG_REG(1,%eax) /* BIF__ARGS */
CALL_BIF($2)
TEST_GOT_MBUF
diff --git a/erts/emulator/hipe/hipe_x86_gc.h b/erts/emulator/hipe/hipe_x86_gc.h
index aa4abb6f59..4bea9276c0 100644
--- a/erts/emulator/hipe/hipe_x86_gc.h
+++ b/erts/emulator/hipe/hipe_x86_gc.h
@@ -71,7 +71,7 @@ nstack_walk_init_sdesc(const Process *p, struct nstack_walk_state *state)
state->sdesc0[0].livebits[0] = 0;
# ifdef DEBUG
state->sdesc0[0].dbg_M = 0;
- state->sdesc0[0].dbg_F = am_init;
+ state->sdesc0[0].dbg_F = am_undefined;
state->sdesc0[0].dbg_A = 0;
# endif
/* XXX: this appears to prevent a gcc-4.1.1 bug on x86 */
diff --git a/erts/emulator/pcre/pcre.mk b/erts/emulator/pcre/pcre.mk
index 352137b341..57bf5de2fb 100644
--- a/erts/emulator/pcre/pcre.mk
+++ b/erts/emulator/pcre/pcre.mk
@@ -49,18 +49,18 @@ PCRE_CFLAGS = $(filter-out -DDEBUG,$(CFLAGS)) -DERLANG_INTEGRATION
ifeq ($(TARGET), win32)
$(EPCRE_LIB): $(PCRE_OBJS)
- $(AR) -out:$@ $(PCRE_OBJS)
+ $(V_AR) -out:$@ $(PCRE_OBJS)
else
$(EPCRE_LIB): $(PCRE_OBJS)
- $(AR) $(ARFLAGS) $@ $(PCRE_OBJS)
+ $(V_AR) $(ARFLAGS) $@ $(PCRE_OBJS)
-@ ($(RANLIB) $@ || true) 2>/dev/null
endif
$(PCRE_OBJDIR)/%.o: pcre/%.c
- $(CC) -c $(PCRE_CFLAGS) -o $@ $<
+ $(V_CC) -c $(PCRE_CFLAGS) -o $@ $<
$(PCRE_GENINC): pcre/pcre_exec.c
- for x in `grep -n COST_CHK pcre/pcre_exec.c | grep -v 'COST_CHK(N)' | awk -F: '{print $$1}'`; \
+ $(gen_verbose)for x in `grep -n COST_CHK pcre/pcre_exec.c | grep -v 'COST_CHK(N)' | awk -F: '{print $$1}'`; \
do \
N=`expr $$x + 100`; \
echo "case $$N: goto L_LOOP_COUNT_$${x};"; \
diff --git a/erts/emulator/sys/common/erl_check_io.c b/erts/emulator/sys/common/erl_check_io.c
index ce014c19c2..474408ae7c 100644
--- a/erts/emulator/sys/common/erl_check_io.c
+++ b/erts/emulator/sys/common/erl_check_io.c
@@ -200,17 +200,6 @@ static void event_large_fd_error(ErlDrvPort, ErtsSysFdType, ErlDrvEventData);
#endif
static void steal_pending_stop_select(erts_dsprintf_buf_t*, ErlDrvPort,
ErtsDrvEventState*, int mode, int on);
-static ERTS_INLINE Eterm
-drvport2id(ErlDrvPort dp)
-{
- Port *pp = erts_drvport2port(dp);
- if (pp)
- return pp->id;
- else {
- ASSERT(0);
- return am_undefined;
- }
-}
#ifdef ERTS_SMP
ERTS_SCHED_PREF_QUICK_ALLOC_IMPL(removed_fd, struct removed_fd, 64, ERTS_ALC_T_FD_LIST)
@@ -378,7 +367,7 @@ abort_task(Eterm id, ErtsPortTaskHandle *pthp, EventStateType type)
|| !erts_port_task_is_scheduled(pthp));
}
else if (erts_port_task_is_scheduled(pthp)) {
- erts_port_task_abort(id, pthp);
+ erts_port_task_abort(pthp);
ASSERT(erts_is_port_alive(id));
}
}
@@ -492,7 +481,7 @@ ERTS_CIO_EXPORT(driver_select)(ErlDrvPort ix,
int on)
{
void (*stop_select_fn)(ErlDrvEvent, void*) = NULL;
- Eterm id = drvport2id(ix);
+ Eterm id = erts_drvport2id(ix);
ErtsSysFdType fd = (ErtsSysFdType) e;
ErtsPollEvents ctl_events = (ErtsPollEvents) 0;
ErtsPollEvents new_events, old_events;
@@ -503,8 +492,8 @@ ERTS_CIO_EXPORT(driver_select)(ErlDrvPort ix,
DTRACE_CHARBUF(name, 64);
#endif
- ERTS_SMP_LC_ASSERT(erts_drvport2port(ix)
- && erts_lc_is_port_locked(erts_drvport2port(ix)));
+ ERTS_SMP_LC_ASSERT(erts_drvport2port(ix, NULL)
+ && erts_lc_is_port_locked(erts_drvport2port(ix, NULL)));
#ifdef ERTS_SYS_CONTINOUS_FD_NUMBERS
if ((unsigned)fd >= (unsigned)erts_smp_atomic_read_nob(&drv_ev_state_len)) {
@@ -530,9 +519,9 @@ ERTS_CIO_EXPORT(driver_select)(ErlDrvPort ix,
if (!on && (mode&ERL_DRV_USE_NO_CALLBACK) == ERL_DRV_USE) {
if (IS_FD_UNKNOWN(state)) {
/* fast track to stop_select callback */
- stop_select_fn = erts_drvport2port(ix)->drv_ptr->stop_select;
+ stop_select_fn = erts_drvport2port(ix, NULL)->drv_ptr->stop_select;
#ifdef USE_VM_PROBES
- strncpy(name, erts_drvport2port(ix)->drv_ptr->name, sizeof(name)-1);
+ strncpy(name, erts_drvport2port(ix, NULL)->drv_ptr->name, sizeof(name)-1);
name[sizeof(name)-1] = '\0';
#endif
ret = 0;
@@ -665,14 +654,14 @@ ERTS_CIO_EXPORT(driver_select)(ErlDrvPort ix,
}
}
if ((mode & ERL_DRV_USE_NO_CALLBACK) == ERL_DRV_USE) {
- erts_driver_t* drv_ptr = erts_drvport2port(ix)->drv_ptr;
+ erts_driver_t* drv_ptr = erts_drvport2port(ix, NULL)->drv_ptr;
ASSERT(new_events==0);
if (state->remove_cnt == 0 || !wake_poller) {
/* Safe to close fd now as it is not in pollset
or there was no need to eject fd (kernel poll) */
stop_select_fn = drv_ptr->stop_select;
#ifdef USE_VM_PROBES
- strncpy(name, erts_drvport2port(ix)->drv_ptr->name, sizeof(name)-1);
+ strncpy(name, erts_drvport2port(ix, NULL)->drv_ptr->name, sizeof(name)-1);
name[sizeof(name)-1] = '\0';
#endif
}
@@ -719,13 +708,13 @@ ERTS_CIO_EXPORT(driver_event)(ErlDrvPort ix,
ErtsPollEvents events;
ErtsPollEvents add_events;
ErtsPollEvents remove_events;
- Eterm id = drvport2id(ix);
+ Eterm id = erts_drvport2id(ix);
ErtsDrvEventState *state;
int do_wake = 0;
int ret;
- ERTS_SMP_LC_ASSERT(erts_drvport2port(ix)
- && erts_lc_is_port_locked(erts_drvport2port(ix)));
+ ERTS_SMP_LC_ASSERT(erts_drvport2port(ix, NULL)
+ && erts_lc_is_port_locked(erts_drvport2port(ix, NULL)));
#ifdef ERTS_SYS_CONTINOUS_FD_NUMBERS
if ((unsigned)fd >= (unsigned)erts_smp_atomic_read_nob(&drv_ev_state_len)) {
@@ -960,7 +949,7 @@ static void
print_select_op(erts_dsprintf_buf_t *dsbufp,
ErlDrvPort ix, ErtsSysFdType fd, int mode, int on)
{
- Port *pp = erts_drvport2port(ix);
+ Port *pp = erts_drvport2port(ix, NULL);
erts_dsprintf(dsbufp,
"driver_select(%p, %d,%s%s%s%s, %d) "
"by ",
@@ -971,8 +960,8 @@ print_select_op(erts_dsprintf_buf_t *dsbufp,
mode & ERL_DRV_USE ? " ERL_DRV_USE" : "",
mode & (ERL_DRV_USE_NO_CALLBACK & ~ERL_DRV_USE) ? "_NO_CALLBACK" : "",
on);
- print_driver_name(dsbufp, pp->id);
- erts_dsprintf(dsbufp, "driver %T ", pp ? pp->id : NIL);
+ print_driver_name(dsbufp, pp->common.id);
+ erts_dsprintf(dsbufp, "driver %T ", pp ? pp->common.id : NIL);
}
static void
@@ -1031,7 +1020,7 @@ steal_pending_stop_select(erts_dsprintf_buf_t *dsbufp, ErlDrvPort ix,
state->driver.drv_ptr = NULL;
}
else if ((mode & ERL_DRV_USE_NO_CALLBACK) == ERL_DRV_USE) {
- erts_driver_t* drv_ptr = erts_drvport2port(ix)->drv_ptr;
+ erts_driver_t* drv_ptr = erts_drvport2port(ix, NULL)->drv_ptr;
if (drv_ptr != state->driver.drv_ptr) {
/* Some other driver wants the stop_select callback */
if (state->driver.drv_ptr->handle) {
@@ -1053,7 +1042,7 @@ static void
print_event_op(erts_dsprintf_buf_t *dsbufp,
ErlDrvPort ix, ErtsSysFdType fd, ErlDrvEventData event_data)
{
- Port *pp = erts_drvport2port(ix);
+ Port *pp = erts_drvport2port(ix, NULL);
erts_dsprintf(dsbufp, "driver_event(%p, %d, ", ix, (int) fd);
if (!event_data)
erts_dsprintf(dsbufp, "NULL");
@@ -1062,8 +1051,8 @@ print_event_op(erts_dsprintf_buf_t *dsbufp,
(unsigned int) event_data->events,
(unsigned int) event_data->revents);
erts_dsprintf(dsbufp, ") by ");
- print_driver_name(dsbufp, pp->id);
- erts_dsprintf(dsbufp, "driver %T ", pp ? pp->id : NIL);
+ print_driver_name(dsbufp, pp->common.id);
+ erts_dsprintf(dsbufp, "driver %T ", pp ? pp->common.id : NIL);
}
static void
@@ -1100,8 +1089,7 @@ iready(Eterm id, ErtsDrvEventState *state)
if (erts_port_task_schedule(id,
&state->driver.select->intask,
ERTS_PORT_TASK_INPUT,
- (ErlDrvEvent) state->fd,
- NULL) != 0) {
+ (ErlDrvEvent) state->fd) != 0) {
stale_drv_select(id, state, ERL_DRV_READ);
}
}
@@ -1112,8 +1100,7 @@ oready(Eterm id, ErtsDrvEventState *state)
if (erts_port_task_schedule(id,
&state->driver.select->outtask,
ERTS_PORT_TASK_OUTPUT,
- (ErlDrvEvent) state->fd,
- NULL) != 0) {
+ (ErlDrvEvent) state->fd) != 0) {
stale_drv_select(id, state, ERL_DRV_WRITE);
}
}
diff --git a/erts/emulator/sys/common/erl_mseg.c b/erts/emulator/sys/common/erl_mseg.c
index db2854fa40..94f9f76a20 100644
--- a/erts/emulator/sys/common/erl_mseg.c
+++ b/erts/emulator/sys/common/erl_mseg.c
@@ -57,26 +57,48 @@
/* Implement some other way to get the real page size if needed! */
#endif
-#define MAX_CACHE_SIZE 30
-
#undef MIN
#define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
#undef MAX
#define MAX(X, Y) ((X) > (Y) ? (X) : (Y))
-#undef PAGE_MASK
-#define INV_PAGE_MASK ((Uint) (page_size - 1))
-#define PAGE_MASK (~INV_PAGE_MASK)
-#define PAGE_FLOOR(X) ((X) & PAGE_MASK)
-#define PAGE_CEILING(X) PAGE_FLOOR((X) + INV_PAGE_MASK)
-#define PAGES(X) ((X) >> page_shift)
+#define INV_ALIGNED_MASK ((UWord) ((MSEG_ALIGNED_SIZE) - 1))
+#define ALIGNED_MASK (~INV_ALIGNED_MASK)
+#define ALIGNED_FLOOR(X) (((UWord)(X)) & ALIGNED_MASK)
+#define ALIGNED_CEILING(X) ALIGNED_FLOOR((X) + INV_ALIGNED_MASK)
+#define MAP_IS_ALIGNED(X) (((UWord)(X) & (MSEG_ALIGNED_SIZE - 1)) == 0)
+
+#define IS_2POW(X) ((X) && !((X) & ((X) - 1)))
+static ERTS_INLINE Uint ceil_2pow(Uint x) {
+ int i = 1 << (4 + (sizeof(Uint) != 4 ? 1 : 0));
+ x--;
+ do { x |= x >> i; } while(i >>= 1);
+ return x + 1;
+}
+static const int debruijn[32] = {
+ 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8,
+ 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9
+};
+
+#define LOG2(X) (debruijn[((Uint32)(((X) & -(X)) * 0x077CB531U)) >> 27])
+
+#define CACHE_AREAS (32 - MSEG_ALIGN_BITS)
+
+#define SIZE_TO_CACHE_AREA_IDX(S) (LOG2((S)) - MSEG_ALIGN_BITS)
+#define MAX_CACHE_SIZE (30)
+
+#define MSEG_FLG_IS_2POW(X) ((X) & ERTS_MSEG_FLG_2POW)
+
+#ifdef DEBUG
+#define DBG(F,...) fprintf(stderr, (F), __VA_ARGS__ )
+#else
+#define DBG(F,...) do{}while(0)
+#endif
static int atoms_initialized;
typedef struct mem_kind_t MemKind;
-static void mseg_clear_cache(MemKind*);
-
#if HALFWORD_HEAP
static int initialize_pmmap(void);
static void *pmmap(size_t size);
@@ -116,15 +138,6 @@ static int mmap_fd;
#error "Not supported"
#endif /* #if HAVE_MMAP */
-#if defined(ERTS_MSEG_FAKE_SEGMENTS) && HALFWORD_HEAP
-# warning "ERTS_MSEG_FAKE_SEGMENTS will only be used for high memory segments"
-#endif
-
-#if defined(ERTS_MSEG_FAKE_SEGMENTS)
-#undef CAN_PARTLY_DESTROY
-#define CAN_PARTLY_DESTROY 0
-#endif
-
const ErtsMsegOpt_t erts_mseg_default_opt = {
1, /* Use cache */
1, /* Preserv data */
@@ -137,26 +150,17 @@ const ErtsMsegOpt_t erts_mseg_default_opt = {
};
-typedef struct cache_desc_t_ {
- void *seg;
- Uint size;
- struct cache_desc_t_ *next;
- struct cache_desc_t_ *prev;
-} cache_desc_t;
-
typedef struct {
Uint32 giga_no;
Uint32 no;
} CallCounter;
-static Uint page_size;
-static Uint page_shift;
-
typedef struct {
CallCounter alloc;
CallCounter dealloc;
CallCounter realloc;
CallCounter create;
+ CallCounter create_resize;
CallCounter destroy;
#if HAVE_MSEG_RECREATE
CallCounter recreate;
@@ -165,17 +169,25 @@ typedef struct {
CallCounter check_cache;
} ErtsMsegCalls;
+typedef struct cache_t_ cache_t;
+
+struct cache_t_ {
+ Uint size;
+ void *seg;
+ cache_t *next;
+};
+
+
typedef struct ErtsMsegAllctr_t_ ErtsMsegAllctr_t;
struct mem_kind_t {
- cache_desc_t cache_descs[MAX_CACHE_SIZE];
- cache_desc_t *free_cache_descs;
- cache_desc_t *cache;
- cache_desc_t *cache_end;
-
- Uint cache_size;
- Uint min_cached_seg_size;
- Uint max_cached_seg_size;
+
+ cache_t cache[MAX_CACHE_SIZE];
+ cache_t *cache_unpowered;
+ cache_t *cache_area[CACHE_AREAS];
+ cache_t *cache_free;
+
+ Sint cache_size;
Uint cache_hits;
struct {
@@ -320,8 +332,7 @@ static erts_mtx_t init_atoms_mutex; /* Also needed when !USE_THREADS */
static ERTS_INLINE void
-schedule_cache_check(ErtsMsegAllctr_t *ma)
-{
+schedule_cache_check(ErtsMsegAllctr_t *ma) {
if (!ma->is_cache_check_scheduled && ma->is_init_done) {
erts_set_aux_work_timeout(ma->ix,
@@ -331,12 +342,45 @@ schedule_cache_check(ErtsMsegAllctr_t *ma)
}
}
+/* remove ErtsMsegAllctr_t from arguments?
+ * only used for statistics
+ */
+static ERTS_INLINE void *
+mmap_align(ErtsMsegAllctr_t *ma, void *addr, size_t length, int prot, int flags, int fd, off_t offset) {
+
+ void *p, *q;
+ UWord d;
+
+ p = mmap(addr, length, prot, flags, fd, offset);
+
+ if (MAP_IS_ALIGNED(p) || p == MAP_FAILED)
+ return p;
+
+ if (ma)
+ INC_CC(ma, create_resize);
+
+ munmap(p, length);
+
+ if ((p = mmap(addr, length + MSEG_ALIGNED_SIZE, prot, flags, fd, offset)) == MAP_FAILED)
+ return MAP_FAILED;
+
+ q = (void *)ALIGNED_CEILING(p);
+ d = q - p;
+
+ if (d > 0)
+ munmap(p, d);
+
+ if (MSEG_ALIGNED_SIZE - d > 0)
+ munmap((void *) (q + length), MSEG_ALIGNED_SIZE - d);
+
+ return q;
+}
+
static ERTS_INLINE void *
mseg_create(ErtsMsegAllctr_t *ma, MemKind* mk, Uint size)
{
void *seg;
-
- ASSERT(size % page_size == 0);
+ ASSERT(size % MSEG_ALIGNED_SIZE == 0);
#if HALFWORD_HEAP
if (mk == &ma->low_mem) {
@@ -345,18 +389,17 @@ mseg_create(ErtsMsegAllctr_t *ma, MemKind* mk, Uint size)
erts_fprintf(stderr,"Pointer mask failure (0x%08lx)\n",(unsigned long) seg);
return NULL;
}
- }
- else
+ } else
#endif
{
-#if defined(ERTS_MSEG_FAKE_SEGMENTS)
- seg = erts_sys_alloc(ERTS_ALC_N_INVALID, NULL, size);
-#elif HAVE_MMAP
+#if HAVE_MMAP
{
- seg = (void *) mmap((void *) 0, (size_t) size,
+ seg = (void *) mmap_align(ma, (void *) 0, (size_t) size,
MMAP_PROT, MMAP_FLAGS, MMAP_FD, 0);
if (seg == (void *) MAP_FAILED)
seg = NULL;
+
+ ASSERT(MAP_IS_ALIGNED(seg) || !seg);
}
#else
# error "Missing mseg_create() implementation"
@@ -369,38 +412,24 @@ mseg_create(ErtsMsegAllctr_t *ma, MemKind* mk, Uint size)
}
static ERTS_INLINE void
-mseg_destroy(ErtsMsegAllctr_t *ma, MemKind* mk, void *seg, Uint size)
-{
-#ifdef DEBUG
- int res;
-#endif
+mseg_destroy(ErtsMsegAllctr_t *ma, MemKind* mk, void *seg, Uint size) {
+ ERTS_DECLARE_DUMMY(int res);
#if HALFWORD_HEAP
if (mk == &ma->low_mem) {
-#ifdef DEBUG
- res =
-#endif
- pmunmap((void *) seg, size);
+ res = pmunmap((void *) seg, size);
}
else
#endif
{
-#ifdef ERTS_MSEG_FAKE_SEGMENTS
- erts_sys_free(ERTS_ALC_N_INVALID, NULL, seg);
-#ifdef DEBUG
- res = 0;
-#endif
-#elif HAVE_MMAP
-#ifdef DEBUG
- res =
-#endif
- munmap((void *) seg, size);
+#ifdef HAVE_MMAP
+ res = munmap((void *) seg, size);
#else
# error "Missing mseg_destroy() implementation"
#endif
}
- ASSERT(size % page_size == 0);
+ ASSERT(size % MSEG_ALIGNED_SIZE == 0);
ASSERT(res == 0);
INC_CC(ma, destroy);
@@ -408,14 +437,36 @@ mseg_destroy(ErtsMsegAllctr_t *ma, MemKind* mk, void *seg, Uint size)
}
#if HAVE_MSEG_RECREATE
+#if defined(__NetBsd__)
+#define MREMAP_FLAGS (0)
+#else
+#define MREMAP_FLAGS (MREMAP_MAYMOVE)
+#endif
+
+
+/* mseg_recreate
+ * May return *unaligned* segments as in address not aligned to MSEG_ALIGNMENT
+ * it is still page aligned
+ *
+ * This is fine for single block carriers as long as we don't cache misaligned
+ * segments (since multiblock carriers may use them)
+ *
+ * For multiblock carriers we *need* MSEG_ALIGNMENT but mbc's will never be
+ * reallocated.
+ *
+ * This should probably be fixed the following way:
+ * 1) Use an option to segment allocation - NEED_ALIGNMENT
+ * 2) Add mremap_align which takes care of aligning a new a mremaped area
+ * 3) Fix the cache to handle of aligned and unaligned segments
+ */
static ERTS_INLINE void *
mseg_recreate(ErtsMsegAllctr_t *ma, MemKind* mk, void *old_seg, Uint old_size, Uint new_size)
{
void *new_seg;
- ASSERT(old_size % page_size == 0);
- ASSERT(new_size % page_size == 0);
+ ASSERT(old_size % MSEG_ALIGNED_SIZE == 0);
+ ASSERT(new_size % MSEG_ALIGNED_SIZE == 0);
#if HALFWORD_HEAP
if (mk == &ma->low_mem) {
@@ -426,22 +477,12 @@ mseg_recreate(ErtsMsegAllctr_t *ma, MemKind* mk, void *old_seg, Uint old_size, U
else
#endif
{
-#if defined(ERTS_MSEG_FAKE_SEGMENTS)
- new_seg = erts_sys_realloc(ERTS_ALC_N_INVALID, NULL, old_seg, new_size);
-#elif HAVE_MREMAP
-
- #if defined(__NetBSD__)
- new_seg = (void *) mremap((void *) old_seg,
- (size_t) old_size,
- NULL,
- (size_t) new_size,
- 0);
- #else
- new_seg = (void *) mremap((void *) old_seg,
- (size_t) old_size,
- (size_t) new_size,
- MREMAP_MAYMOVE);
- #endif
+#if HAVE_MREMAP
+#if defined(__NetBSD__)
+ new_seg = mremap(old_seg, (size_t)old_size, NULL, new_size, MREMAP_FLAGS);
+#else
+ new_seg = mremap(old_seg, (size_t)old_size, (size_t)new_size, MREMAP_FLAGS);
+#endif
if (new_seg == (void *) MAP_FAILED)
new_seg = NULL;
#else
@@ -475,151 +516,265 @@ do { \
#define ERTS_DBG_MK_CHK_THR_ACCESS(MK)
#endif
-static ERTS_INLINE cache_desc_t *
-alloc_cd(MemKind* mk)
-{
- cache_desc_t *cd = mk->free_cache_descs;
+/* NEW CACHE interface */
+
+static ERTS_INLINE cache_t *mseg_cache_alloc_descriptor(MemKind *mk) {
+ cache_t *c = mk->cache_free;
+
ERTS_DBG_MK_CHK_THR_ACCESS(mk);
- if (cd)
- mk->free_cache_descs = cd->next;
- return cd;
+ if (c)
+ mk->cache_free = c->next;
+
+ return c;
}
-static ERTS_INLINE void
-free_cd(MemKind* mk, cache_desc_t *cd)
-{
+static ERTS_INLINE void mseg_cache_free_descriptor(MemKind *mk, cache_t *c) {
ERTS_DBG_MK_CHK_THR_ACCESS(mk);
- cd->next = mk->free_cache_descs;
- mk->free_cache_descs = cd;
+ ASSERT(c);
+
+ c->seg = NULL;
+ c->size = 0;
+ c->next = mk->cache_free;
+ mk->cache_free = c;
}
+static ERTS_INLINE int cache_bless_segment(MemKind *mk, void *seg, Uint size) {
-static ERTS_INLINE void
-link_cd(MemKind* mk, cache_desc_t *cd)
-{
+ cache_t *c;
ERTS_DBG_MK_CHK_THR_ACCESS(mk);
- if (mk->cache)
- mk->cache->prev = cd;
- cd->next = mk->cache;
- cd->prev = NULL;
- mk->cache = cd;
-
- if (!mk->cache_end) {
- ASSERT(!cd->next);
- mk->cache_end = cd;
+
+ if (mk->cache_free && MAP_IS_ALIGNED(seg)) {
+ if (IS_2POW(size)) {
+ int ix = SIZE_TO_CACHE_AREA_IDX(size);
+
+ ASSERT(ix < CACHE_AREAS);
+ ASSERT((1 << (ix + MSEG_ALIGN_BITS)) == size);
+
+ /* unlink from free cache list */
+ c = mseg_cache_alloc_descriptor(mk);
+
+ /* link to cache area */
+ c->seg = seg;
+ c->size = size;
+ c->next = mk->cache_area[ix];
+
+ mk->cache_area[ix] = c;
+ mk->cache_size++;
+
+ ASSERT(mk->cache_size <= mk->ma->max_cache_size);
+
+ return 1;
+ } else {
+ /* unlink from free cache list */
+ c = mseg_cache_alloc_descriptor(mk);
+
+ /* link to cache area */
+ c->seg = seg;
+ c->size = size;
+ c->next = mk->cache_unpowered;
+
+ mk->cache_unpowered = c;
+ mk->cache_size++;
+
+ ASSERT(mk->cache_size <= mk->ma->max_cache_size);
+
+ return 1;
+ }
}
- mk->cache_size++;
+ return 0;
}
-#if CAN_PARTLY_DESTROY
-static ERTS_INLINE void
-end_link_cd(MemKind* mk, cache_desc_t *cd)
-{
+static ERTS_INLINE void *cache_get_segment(MemKind *mk, Uint *size_p) {
+
+ Uint size = *size_p;
+
ERTS_DBG_MK_CHK_THR_ACCESS(mk);
- if (mk->cache_end)
- mk->cache_end->next = cd;
- cd->next = NULL;
- cd->prev = mk->cache_end;
- mk->cache_end = cd;
-
- if (!mk->cache) {
- ASSERT(!cd->prev);
- mk->cache = cd;
- }
+ if (IS_2POW(size)) {
+
+ int i, ix = SIZE_TO_CACHE_AREA_IDX(size);
+ void *seg;
+ cache_t *c;
+ Uint csize;
+
+ for( i = ix; i < CACHE_AREAS; i++) {
+
+ if ((c = mk->cache_area[i]) == NULL)
+ continue;
+
+ ASSERT(IS_2POW(c->size));
+
+ /* unlink from cache area */
+ csize = c->size;
+ seg = c->seg;
+ mk->cache_area[i] = c->next;
+ c->next = NULL;
+ mk->cache_size--;
+ mk->cache_hits++;
+
+ /* link to free cache list */
+ mseg_cache_free_descriptor(mk, c);
+
+ ASSERT(!(mk->cache_size < 0));
+
+ /* divvy up the cache - if needed */
+ while( i > ix) {
+ csize = csize >> 1;
+ /* try to cache half of it */
+ if (!cache_bless_segment(mk, (char *)seg + csize, csize)) {
+ /* wouldn't cache .. destroy it instead */
+ mseg_destroy(mk->ma, mk, (char *)seg + csize, csize);
+ }
+ i--;
+ }
+ ASSERT(csize == size);
+ return seg;
+ }
+ }
+ else if (mk->cache_unpowered) {
+ void *seg;
+ cache_t *c, *pc;
+ Uint csize;
+ Uint bad_max_abs = mk->ma->abs_max_cache_bad_fit;
+ Uint bad_max_rel = mk->ma->rel_max_cache_bad_fit;
+
+ c = mk->cache_unpowered;
+ pc = c;
+
+ while (c) {
+ csize = c->size;
+ if (csize >= size &&
+ ((csize - size)*100 < bad_max_rel*size) &&
+ (csize - size) < bad_max_abs ) {
+
+ /* unlink from cache area */
+ seg = c->seg;
+
+ if (pc == c) {
+ mk->cache_unpowered = c->next;
+ } else {
+ pc->next = c->next;
+ }
+
+ c->next = NULL;
+ mk->cache_size--;
+ mk->cache_hits++;
+
+ /* link to free cache list */
+ mseg_cache_free_descriptor(mk, c);
+ *size_p = csize;
+
+ return seg;
+ }
- mk->cache_size++;
+ pc = c;
+ c = c->next;
+ }
+ }
+ return NULL;
}
-#endif
-static ERTS_INLINE void
-unlink_cd(MemKind* mk, cache_desc_t *cd)
-{
- ERTS_DBG_MK_CHK_THR_ACCESS(mk);
- if (cd->next)
- cd->next->prev = cd->prev;
- else
- mk->cache_end = cd->prev;
-
- if (cd->prev)
- cd->prev->next = cd->next;
- else
- mk->cache = cd->next;
- ASSERT(mk->cache_size > 0);
+/* *_mseg_check_*_cache
+ * Slowly remove segments cached in the allocator by
+ * using callbacks from aux-work in the scheduler.
+ */
+
+static ERTS_INLINE Uint mseg_drop_one_memkind_cache_size(MemKind *mk, cache_t **head) {
+ cache_t *c = NULL;
+
+ c = *head;
+
+ ASSERT( c != NULL );
+
+ *head = c->next;
+
+ if (erts_mtrace_enabled)
+ erts_mtrace_crr_free(SEGTYPE, SEGTYPE, c->seg);
+
+ mseg_destroy(mk->ma, mk, c->seg, c->size);
+ mseg_cache_free_descriptor(mk, c);
+
+ mk->segments.current.watermark--;
mk->cache_size--;
-}
-static ERTS_INLINE void
-check_cache_limits(MemKind* mk)
-{
- cache_desc_t *cd;
- ERTS_DBG_MK_CHK_THR_ACCESS(mk);
- mk->max_cached_seg_size = 0;
- mk->min_cached_seg_size = ~((Uint) 0);
- for (cd = mk->cache; cd; cd = cd->next) {
- if (cd->size < mk->min_cached_seg_size)
- mk->min_cached_seg_size = cd->size;
- if (cd->size > mk->max_cached_seg_size)
- mk->max_cached_seg_size = cd->size;
- }
+ ASSERT( mk->cache_size >= 0 );
+
+ return mk->cache_size;
}
-static ERTS_INLINE void
-adjust_cache_size(MemKind* mk, int force_check_limits)
-{
- cache_desc_t *cd;
- int check_limits = force_check_limits;
- Sint max_cached = ((Sint) mk->segments.current.watermark
- - (Sint) mk->segments.current.no);
- ERTS_DBG_MK_CHK_THR_ACCESS(mk);
- while (((Sint) mk->cache_size) > max_cached && ((Sint) mk->cache_size) > 0) {
- ASSERT(mk->cache_end);
- cd = mk->cache_end;
- if (!check_limits &&
- !(mk->min_cached_seg_size < cd->size
- && cd->size < mk->max_cached_seg_size)) {
- check_limits = 1;
- }
+static ERTS_INLINE Uint mseg_drop_memkind_cache_size(MemKind *mk, cache_t **head) {
+ cache_t *c = NULL, *next = NULL;
+
+ c = *head;
+ ASSERT( c != NULL );
+
+ while (c) {
+
+ next = c->next;
+
if (erts_mtrace_enabled)
- erts_mtrace_crr_free(SEGTYPE, SEGTYPE, cd->seg);
- mseg_destroy(mk->ma, mk, cd->seg, cd->size);
- unlink_cd(mk,cd);
- free_cd(mk,cd);
- }
+ erts_mtrace_crr_free(SEGTYPE, SEGTYPE, c->seg);
- if (check_limits)
- check_cache_limits(mk);
-}
+ mseg_destroy(mk->ma, mk, c->seg, c->size);
+ mseg_cache_free_descriptor(mk, c);
-static Uint
-check_one_cache(MemKind* mk)
-{
- if (mk->segments.current.watermark > mk->segments.current.no)
mk->segments.current.watermark--;
- adjust_cache_size(mk, 0);
+ mk->cache_size--;
+
+ c = next;
+ }
+
+ *head = NULL;
+
+ ASSERT( mk->cache_size >= 0 );
- if (mk->cache_size)
- schedule_cache_check(mk->ma);
return mk->cache_size;
}
-static void do_cache_check(ErtsMsegAllctr_t *ma)
-{
- int empty_cache = 1;
+/* mseg_check_memkind_cache
+ * - Check if we can empty some cached segments in this
+ * MemKind.
+ */
+
+
+static Uint mseg_check_memkind_cache(MemKind *mk) {
+ int i;
+
+ ERTS_DBG_MK_CHK_THR_ACCESS(mk);
+
+ for (i = 0; i < CACHE_AREAS; i++) {
+ if (mk->cache_area[i] != NULL)
+ return mseg_drop_one_memkind_cache_size(mk, &(mk->cache_area[i]));
+ }
+
+ if (mk->cache_unpowered)
+ return mseg_drop_one_memkind_cache_size(mk, &(mk->cache_unpowered));
+
+ return 0;
+}
+
+/* mseg_cache_check
+ * - Check if we have some cache we can purge
+ * in any of the memkinds.
+ */
+
+static void mseg_cache_check(ErtsMsegAllctr_t *ma) {
MemKind* mk;
+ Uint empty_cache = 1;
ERTS_MSEG_LOCK(ma);
- for (mk=ma->mk_list; mk; mk=mk->next) {
- if (check_one_cache(mk))
+ for (mk = ma->mk_list; mk; mk = mk->next) {
+ if (mseg_check_memkind_cache(mk))
empty_cache = 0;
}
+ /* If all MemKinds caches are empty,
+ * remove aux-work callback
+ */
if (empty_cache) {
ma->is_cache_check_scheduled = 0;
- erts_set_aux_work_timeout(ma->ix,
- ERTS_SSI_AUX_WORK_MSEG_CACHE_CHECK,
- 0);
+ erts_set_aux_work_timeout(ma->ix, ERTS_SSI_AUX_WORK_MSEG_CACHE_CHECK, 0);
}
INC_CC(ma, check_cache);
@@ -627,27 +782,65 @@ static void do_cache_check(ErtsMsegAllctr_t *ma)
ERTS_MSEG_UNLOCK(ma);
}
-void erts_mseg_cache_check(void)
-{
- do_cache_check(ERTS_MSEG_ALLCTR_SS());
+/* erts_mseg_cache_check
+ * - This is a callback that is scheduled as aux-work from
+ * schedulers and is called at some interval if we have a cache
+ * on this mseg-allocator and memkind.
+ * - Purpose: Empty cache slowly so we don't collect mapped areas
+ * and bloat memory.
+ */
+
+void erts_mseg_cache_check(void) {
+ mseg_cache_check(ERTS_MSEG_ALLCTR_SS());
}
-static void
-mseg_clear_cache(MemKind* mk)
-{
- mk->segments.current.watermark = 0;
- adjust_cache_size(mk, 1);
+/* *_mseg_clear_*_cache
+ * Remove cached segments from the allocator completely
+ */
+
+static void mseg_clear_memkind_cache(MemKind *mk) {
+ int i;
+
+ /* drop pow2 caches */
+ for (i = 0; i < CACHE_AREAS; i++) {
+ if (mk->cache_area[i] == NULL)
+ continue;
+
+ mseg_drop_memkind_cache_size(mk, &(mk->cache_area[i]));
+ ASSERT(mk->cache_area[i] == NULL);
+ }
+ /* drop varied caches */
+ if(mk->cache_unpowered)
+ mseg_drop_memkind_cache_size(mk, &(mk->cache_unpowered));
+
+ ASSERT(mk->cache_unpowered == NULL);
+ ASSERT(mk->cache_size == 0);
+}
+
+static void mseg_clear_cache(ErtsMsegAllctr_t *ma) {
+ MemKind* mk;
+
+ ERTS_MSEG_LOCK(ma);
+ ERTS_DBG_MA_CHK_THR_ACCESS(ma);
+
- ASSERT(!mk->cache);
- ASSERT(!mk->cache_end);
- ASSERT(!mk->cache_size);
+ for (mk = ma->mk_list; mk; mk = mk->next) {
+ mseg_clear_memkind_cache(mk);
+ }
- mk->segments.current.watermark = mk->segments.current.no;
+ INC_CC(ma, clear_cache);
- INC_CC(mk->ma, clear_cache);
+ ERTS_MSEG_UNLOCK(ma);
}
+void erts_mseg_clear_cache(void) {
+ mseg_clear_cache(ERTS_MSEG_ALLCTR_SS());
+ mseg_clear_cache(ERTS_MSEG_ALLCTR_IX(0));
+}
+
+
+
static ERTS_INLINE MemKind* memkind(ErtsMsegAllctr_t *ma,
const ErtsMsegOpt_t *opt)
{
@@ -660,116 +853,40 @@ static ERTS_INLINE MemKind* memkind(ErtsMsegAllctr_t *ma,
static void *
mseg_alloc(ErtsMsegAllctr_t *ma, ErtsAlcType_t atype, Uint *size_p,
- const ErtsMsegOpt_t *opt)
+ Uint flags, const ErtsMsegOpt_t *opt)
{
- Uint max, min, diff_size, size;
- cache_desc_t *cd, *cand_cd;
+ Uint size;
void *seg;
MemKind* mk = memkind(ma, opt);
INC_CC(ma, alloc);
- size = PAGE_CEILING(*size_p);
+ /* Carrier align */
+ size = ALIGNED_CEILING(*size_p);
+
+ /* Cache optim (if applicable) */
+ if (MSEG_FLG_IS_2POW(flags) && !IS_2POW(size))
+ size = ceil_2pow(size);
#if CAN_PARTLY_DESTROY
if (size < ma->min_seg_size)
ma->min_seg_size = size;
#endif
+
+ if (opt->cache && mk->cache_size > 0 && (seg = cache_get_segment(mk, &size)) != NULL)
+ goto done;
- if (!opt->cache) {
- create_seg:
- adjust_cache_size(mk,0);
- seg = mseg_create(ma, mk, size);
- if (!seg) {
- mseg_clear_cache(mk);
- seg = mseg_create(ma, mk, size);
- if (!seg)
- size = 0;
- }
-
- *size_p = size;
- if (seg) {
- if (erts_mtrace_enabled)
- erts_mtrace_crr_alloc(seg, atype, ERTS_MTRACE_SEGMENT_ID, size);
- ERTS_MSEG_ALLOC_STAT(mk,size);
- }
- return seg;
- }
-
- if (size > mk->max_cached_seg_size)
- goto create_seg;
-
- if (size < mk->min_cached_seg_size) {
-
- diff_size = mk->min_cached_seg_size - size;
-
- if (diff_size > ma->abs_max_cache_bad_fit)
- goto create_seg;
-
- if (100*PAGES(diff_size) > ma->rel_max_cache_bad_fit*PAGES(size))
- goto create_seg;
-
- }
-
- max = 0;
- min = ~((Uint) 0);
- cand_cd = NULL;
-
- for (cd = mk->cache; cd; cd = cd->next) {
- if (cd->size >= size) {
- if (!cand_cd) {
- cand_cd = cd;
- continue;
- }
- else if (cd->size < cand_cd->size) {
- if (max < cand_cd->size)
- max = cand_cd->size;
- if (min > cand_cd->size)
- min = cand_cd->size;
- cand_cd = cd;
- continue;
- }
- }
- if (max < cd->size)
- max = cd->size;
- if (min > cd->size)
- min = cd->size;
- }
-
- mk->min_cached_seg_size = min;
- mk->max_cached_seg_size = max;
-
- if (!cand_cd)
- goto create_seg;
-
- diff_size = cand_cd->size - size;
-
- if (diff_size > ma->abs_max_cache_bad_fit
- || 100*PAGES(diff_size) > ma->rel_max_cache_bad_fit*PAGES(size)) {
- if (mk->max_cached_seg_size < cand_cd->size)
- mk->max_cached_seg_size = cand_cd->size;
- if (mk->min_cached_seg_size > cand_cd->size)
- mk->min_cached_seg_size = cand_cd->size;
- goto create_seg;
- }
-
- mk->cache_hits++;
-
- size = cand_cd->size;
- seg = cand_cd->seg;
-
- unlink_cd(mk,cand_cd);
- free_cd(mk,cand_cd);
+ if ((seg = mseg_create(ma, mk, size)) == NULL)
+ size = 0;
+done:
*size_p = size;
+ if (seg) {
+ if (erts_mtrace_enabled)
+ erts_mtrace_crr_alloc(seg, atype, ERTS_MTRACE_SEGMENT_ID, size);
- if (erts_mtrace_enabled) {
- erts_mtrace_crr_free(SEGTYPE, SEGTYPE, seg);
- erts_mtrace_crr_alloc(seg, atype, SEGTYPE, size);
- }
-
- if (seg)
ERTS_MSEG_ALLOC_STAT(mk,size);
+ }
return seg;
}
@@ -780,73 +897,42 @@ mseg_dealloc(ErtsMsegAllctr_t *ma, ErtsAlcType_t atype, void *seg, Uint size,
const ErtsMsegOpt_t *opt)
{
MemKind* mk = memkind(ma, opt);
- cache_desc_t *cd;
+
ERTS_MSEG_DEALLOC_STAT(mk,size);
- if (!opt->cache || ma->max_cache_size == 0) {
- if (erts_mtrace_enabled)
- erts_mtrace_crr_free(atype, SEGTYPE, seg);
- mseg_destroy(ma, mk, seg, size);
+ if (opt->cache && cache_bless_segment(mk, seg, size)) {
+ schedule_cache_check(ma);
+ goto done;
}
- else {
- int check_limits = 0;
-
- if (size < mk->min_cached_seg_size)
- mk->min_cached_seg_size = size;
- if (size > mk->max_cached_seg_size)
- mk->max_cached_seg_size = size;
-
- if (!mk->free_cache_descs) {
- cd = mk->cache_end;
- if (!(mk->min_cached_seg_size < cd->size
- && cd->size < mk->max_cached_seg_size)) {
- check_limits = 1;
- }
- if (erts_mtrace_enabled)
- erts_mtrace_crr_free(SEGTYPE, SEGTYPE, cd->seg);
- mseg_destroy(ma, mk, cd->seg, cd->size);
- unlink_cd(mk,cd);
- free_cd(mk,cd);
- }
- cd = alloc_cd(mk);
- ASSERT(cd);
- cd->seg = seg;
- cd->size = size;
- link_cd(mk,cd);
+ if (erts_mtrace_enabled)
+ erts_mtrace_crr_free(atype, SEGTYPE, seg);
- if (erts_mtrace_enabled) {
- erts_mtrace_crr_free(atype, SEGTYPE, seg);
- erts_mtrace_crr_alloc(seg, SEGTYPE, SEGTYPE, size);
- }
-
- /* ASSERT(segments.current.watermark >= segments.current.no + cache_size); */
-
- if (check_limits)
- check_cache_limits(mk);
+ mseg_destroy(ma, mk, seg, size);
- schedule_cache_check(ma);
-
- }
+done:
INC_CC(ma, dealloc);
}
static void *
mseg_realloc(ErtsMsegAllctr_t *ma, ErtsAlcType_t atype, void *seg,
- Uint old_size, Uint *new_size_p, const ErtsMsegOpt_t *opt)
+ Uint old_size, Uint *new_size_p, Uint flags, const ErtsMsegOpt_t *opt)
{
MemKind* mk;
void *new_seg;
Uint new_size;
+ /* Just allocate a new segment if we didn't have one before */
if (!seg || !old_size) {
- new_seg = mseg_alloc(ma, atype, new_size_p, opt);
+ new_seg = mseg_alloc(ma, atype, new_size_p, flags, opt);
DEC_CC(ma, alloc);
return new_seg;
}
+
+ /* Dealloc old segment if new segment is of size 0 */
if (!(*new_size_p)) {
mseg_dealloc(ma, atype, seg, old_size, opt);
DEC_CC(ma, dealloc);
@@ -855,7 +941,13 @@ mseg_realloc(ErtsMsegAllctr_t *ma, ErtsAlcType_t atype, void *seg,
mk = memkind(ma, opt);
new_seg = seg;
- new_size = PAGE_CEILING(*new_size_p);
+
+ /* Carrier align */
+ new_size = ALIGNED_CEILING(*new_size_p);
+
+ /* Cache optim (if applicable) */
+ if (MSEG_FLG_IS_2POW(flags) && !IS_2POW(new_size))
+ new_size = ceil_2pow(new_size);
if (new_size == old_size)
;
@@ -866,53 +958,27 @@ mseg_realloc(ErtsMsegAllctr_t *ma, ErtsAlcType_t atype, void *seg,
if (new_size < ma->min_seg_size)
ma->min_seg_size = new_size;
#endif
-
+ /* +M<S>rsbcst <ratio> */
if (shrink_sz < opt->abs_shrink_th
- && 100*PAGES(shrink_sz) < opt->rel_shrink_th*PAGES(old_size)) {
+ && 100*shrink_sz < opt->rel_shrink_th*old_size) {
new_size = old_size;
}
else {
#if CAN_PARTLY_DESTROY
- if (shrink_sz > ma->min_seg_size
- && mk->free_cache_descs
- && opt->cache) {
- cache_desc_t *cd;
-
- cd = alloc_cd(mk);
- ASSERT(cd);
- cd->seg = ((char *) seg) + new_size;
- cd->size = shrink_sz;
- end_link_cd(mk,cd);
-
- if (erts_mtrace_enabled) {
- erts_mtrace_crr_realloc(new_seg,
- atype,
- SEGTYPE,
- seg,
- new_size);
- erts_mtrace_crr_alloc(cd->seg, SEGTYPE, SEGTYPE, cd->size);
- }
- schedule_cache_check(ma);
- }
- else {
- if (erts_mtrace_enabled)
- erts_mtrace_crr_realloc(new_seg,
- atype,
- SEGTYPE,
- seg,
- new_size);
- mseg_destroy(ma, mk, ((char *) seg) + new_size, shrink_sz);
- }
+ if (erts_mtrace_enabled)
+ erts_mtrace_crr_realloc(new_seg, atype, SEGTYPE, seg, new_size);
-#elif HAVE_MSEG_RECREATE
+ mseg_destroy(ma, mk, ((char *) seg) + new_size, shrink_sz);
+#elif HAVE_MSEG_RECREATE
goto do_recreate;
-
#else
+ new_seg = mseg_alloc(ma, atype, &new_size, flags, opt);
+
+ ASSERT(MAP_IS_ALIGNED(new_seg) || !new_seg);
- new_seg = mseg_alloc(ma, atype, &new_size, opt);
if (!new_seg)
new_size = old_size;
else {
@@ -921,16 +987,15 @@ mseg_realloc(ErtsMsegAllctr_t *ma, ErtsAlcType_t atype, void *seg,
MIN(new_size, old_size));
mseg_dealloc(ma, atype, seg, old_size, opt);
}
-
#endif
-
}
}
else {
if (!opt->preserv) {
mseg_dealloc(ma, atype, seg, old_size, opt);
- new_seg = mseg_alloc(ma, atype, &new_size, opt);
+ new_seg = mseg_alloc(ma, atype, &new_size, flags, opt);
+ ASSERT(MAP_IS_ALIGNED(new_seg) || !new_seg);
}
else {
#if HAVE_MSEG_RECREATE
@@ -938,18 +1003,23 @@ mseg_realloc(ErtsMsegAllctr_t *ma, ErtsAlcType_t atype, void *seg,
do_recreate:
#endif
new_seg = mseg_recreate(ma, mk, (void *) seg, old_size, new_size);
+ /* ASSERT(MAP_IS_ALIGNED(new_seg) || !new_seg);
+ * will not always be aligned and it ok for now
+ */
+
if (erts_mtrace_enabled)
erts_mtrace_crr_realloc(new_seg, atype, SEGTYPE, seg, new_size);
if (!new_seg)
new_size = old_size;
#else
- new_seg = mseg_alloc(ma, atype, &new_size, opt);
+ new_seg = mseg_alloc(ma, atype, &new_size, flags, opt);
+
+ ASSERT(MAP_IS_ALIGNED(new_seg) || !new_seg);
+
if (!new_seg)
new_size = old_size;
else {
- sys_memcpy(((char *) new_seg),
- ((char *) seg),
- MIN(new_size, old_size));
+ sys_memcpy(((char *) new_seg), ((char *) seg), MIN(new_size, old_size));
mseg_dealloc(ma, atype, seg, old_size, opt);
}
#endif
@@ -958,6 +1028,7 @@ mseg_realloc(ErtsMsegAllctr_t *ma, ErtsAlcType_t atype, void *seg,
INC_CC(ma, realloc);
+ ASSERT(!MSEG_FLG_IS_2POW(flags) || IS_2POW(new_size));
*new_size_p = new_size;
ERTS_MSEG_REALLOC_STAT(mk, old_size, new_size);
@@ -990,6 +1061,7 @@ static struct {
Eterm mseg_dealloc;
Eterm mseg_realloc;
Eterm mseg_create;
+ Eterm mseg_create_resize;
Eterm mseg_destroy;
#if HAVE_MSEG_RECREATE
Eterm mseg_recreate;
@@ -1046,6 +1118,7 @@ init_atoms(ErtsMsegAllctr_t *ma)
AM_INIT(mseg_dealloc);
AM_INIT(mseg_realloc);
AM_INIT(mseg_create);
+ AM_INIT(mseg_create_resize);
AM_INIT(mseg_destroy);
#if HAVE_MSEG_RECREATE
AM_INIT(mseg_recreate);
@@ -1065,14 +1138,12 @@ init_atoms(ErtsMsegAllctr_t *ma)
erts_mtx_unlock(&init_atoms_mutex);
}
-
#define bld_uint erts_bld_uint
#define bld_cons erts_bld_cons
#define bld_tuple erts_bld_tuple
#define bld_string erts_bld_string
#define bld_2tup_list erts_bld_2tup_list
-
/*
* bld_unstable_uint() (instead of bld_uint()) is used when values may
* change between size check and actual build. This because a value
@@ -1116,6 +1187,7 @@ add_4tup(Uint **hpp, Uint *szp, Eterm *lp,
*lp = bld_cons(hpp, szp, bld_tuple(hpp, szp, 4, el1, el2, el3, el4), *lp);
}
+
static Eterm
info_options(ErtsMsegAllctr_t *ma,
char *prefix,
@@ -1176,6 +1248,7 @@ info_calls(ErtsMsegAllctr_t *ma, int *print_to_p, void *print_to_arg, Uint **hpp
PRINT_CC(to, arg, dealloc);
PRINT_CC(to, arg, realloc);
PRINT_CC(to, arg, create);
+ PRINT_CC(to, arg, create_resize);
PRINT_CC(to, arg, destroy);
#if HAVE_MSEG_RECREATE
PRINT_CC(to, arg, recreate);
@@ -1215,6 +1288,10 @@ info_calls(ErtsMsegAllctr_t *ma, int *print_to_p, void *print_to_arg, Uint **hpp
bld_unstable_uint(hpp, szp, ma->calls.create.giga_no),
bld_unstable_uint(hpp, szp, ma->calls.create.no));
+ add_3tup(hpp, szp, &res,
+ am.mseg_create_resize,
+ bld_unstable_uint(hpp, szp, ma->calls.create_resize.giga_no),
+ bld_unstable_uint(hpp, szp, ma->calls.create_resize.no));
add_3tup(hpp, szp, &res,
am.mseg_realloc,
@@ -1401,21 +1478,21 @@ erts_mseg_info(int ix,
}
void *
-erts_mseg_alloc_opt(ErtsAlcType_t atype, Uint *size_p, const ErtsMsegOpt_t *opt)
+erts_mseg_alloc_opt(ErtsAlcType_t atype, Uint *size_p, Uint flags, const ErtsMsegOpt_t *opt)
{
ErtsMsegAllctr_t *ma = ERTS_MSEG_ALLCTR_OPT(opt);
void *seg;
ERTS_MSEG_LOCK(ma);
ERTS_DBG_MA_CHK_THR_ACCESS(ma);
- seg = mseg_alloc(ma, atype, size_p, opt);
+ seg = mseg_alloc(ma, atype, size_p, flags, opt);
ERTS_MSEG_UNLOCK(ma);
return seg;
}
void *
-erts_mseg_alloc(ErtsAlcType_t atype, Uint *size_p)
+erts_mseg_alloc(ErtsAlcType_t atype, Uint *size_p, Uint flags)
{
- return erts_mseg_alloc_opt(atype, size_p, &erts_mseg_default_opt);
+ return erts_mseg_alloc_opt(atype, size_p, flags, &erts_mseg_default_opt);
}
void
@@ -1438,44 +1515,24 @@ erts_mseg_dealloc(ErtsAlcType_t atype, void *seg, Uint size)
void *
erts_mseg_realloc_opt(ErtsAlcType_t atype, void *seg,
Uint old_size, Uint *new_size_p,
+ Uint flags,
const ErtsMsegOpt_t *opt)
{
ErtsMsegAllctr_t *ma = ERTS_MSEG_ALLCTR_OPT(opt);
void *new_seg;
ERTS_MSEG_LOCK(ma);
ERTS_DBG_MA_CHK_THR_ACCESS(ma);
- new_seg = mseg_realloc(ma, atype, seg, old_size, new_size_p, opt);
+ new_seg = mseg_realloc(ma, atype, seg, old_size, new_size_p, flags, opt);
ERTS_MSEG_UNLOCK(ma);
return new_seg;
}
void *
erts_mseg_realloc(ErtsAlcType_t atype, void *seg,
- Uint old_size, Uint *new_size_p)
+ Uint old_size, Uint *new_size_p, Uint flags)
{
return erts_mseg_realloc_opt(atype, seg, old_size, new_size_p,
- &erts_mseg_default_opt);
-}
-
-void
-erts_mseg_clear_cache(void)
-{
- ErtsMsegAllctr_t *ma = ERTS_MSEG_ALLCTR_SS();
- MemKind* mk;
-
-start:
-
- ERTS_MSEG_LOCK(ma);
- ERTS_DBG_MA_CHK_THR_ACCESS(ma);
- for (mk=ma->mk_list; mk; mk=mk->next) {
- mseg_clear_cache(mk);
- }
- ERTS_MSEG_UNLOCK(ma);
-
- if (ma->ix != 0) {
- ma = ERTS_MSEG_ALLCTR_IX(0);
- goto start;
- }
+ flags, &erts_mseg_default_opt);
}
Uint
@@ -1496,28 +1553,32 @@ erts_mseg_no(const ErtsMsegOpt_t *opt)
Uint
erts_mseg_unit_size(void)
{
- return page_size;
+ return MSEG_ALIGNED_SIZE;
}
static void mem_kind_init(ErtsMsegAllctr_t *ma, MemKind* mk, const char* name)
{
- unsigned i;
+ int i;
- mk->cache = NULL;
- mk->cache_end = NULL;
- mk->max_cached_seg_size = 0;
- mk->min_cached_seg_size = ~((Uint) 0);
- mk->cache_size = 0;
- mk->cache_hits = 0;
+ for (i = 0; i < CACHE_AREAS; i++) {
+ mk->cache_area[i] = NULL;
+ }
+
+ mk->cache_free = NULL;
- if (ma->max_cache_size > 0) {
- for (i = 0; i < ma->max_cache_size - 1; i++)
- mk->cache_descs[i].next = &mk->cache_descs[i + 1];
- mk->cache_descs[ma->max_cache_size - 1].next = NULL;
- mk->free_cache_descs = &mk->cache_descs[0];
+ ASSERT(ma->max_cache_size <= MAX_CACHE_SIZE);
+
+ for (i = 0; i < ma->max_cache_size; i++) {
+ mk->cache[i].seg = NULL;
+ mk->cache[i].size = 0;
+ mk->cache[i].next = mk->cache_free;
+ mk->cache_free = &(mk->cache[i]);
}
- else
- mk->free_cache_descs = NULL;
+
+ mk->cache_unpowered = NULL;
+
+ mk->cache_size = 0;
+ mk->cache_hits = 0;
mk->segments.current.watermark = 0;
mk->segments.current.no = 0;
@@ -1570,15 +1631,10 @@ erts_mseg_init(ErtsMsegInit_t *init)
initialize_pmmap();
#endif
- page_size = GET_PAGE_SIZE;
+ if (!IS_2POW(GET_PAGE_SIZE))
+ erl_exit(ERTS_ABORT_EXIT, "erts_mseg: Unexpected page_size %beu\n", GET_PAGE_SIZE);
- page_shift = 1;
- while ((page_size >> page_shift) != 1) {
- if ((page_size & (1 << (page_shift - 1))) != 0)
- erl_exit(ERTS_ABORT_EXIT,
- "erts_mseg: Unexpected page_size %beu\n", page_size);
- page_shift++;
- }
+ ASSERT((MSEG_ALIGNED_SIZE % GET_PAGE_SIZE) == 0);
for (i = 0; i < no_mseg_allocators; i++) {
ErtsMsegAllctr_t *ma = ERTS_MSEG_ALLCTR_IX(i);
@@ -1663,7 +1719,7 @@ erts_mseg_test(unsigned long op,
case 0x400: /* Have erts_mseg */
return (unsigned long) 1;
case 0x401:
- return (unsigned long) erts_mseg_alloc(ERTS_ALC_A_INVALID, (Uint *) a1);
+ return (unsigned long) erts_mseg_alloc(ERTS_ALC_A_INVALID, (Uint *) a1, (Uint) 0);
case 0x402:
erts_mseg_dealloc(ERTS_ALC_A_INVALID, (void *) a1, (Uint) a2);
return (unsigned long) 0;
@@ -1671,7 +1727,8 @@ erts_mseg_test(unsigned long op,
return (unsigned long) erts_mseg_realloc(ERTS_ALC_A_INVALID,
(void *) a1,
(Uint) a2,
- (Uint *) a3);
+ (Uint *) a3,
+ (Uint) 0);
case 0x404:
erts_mseg_clear_cache();
return (unsigned long) 0;
@@ -1707,7 +1764,40 @@ erts_mseg_test(unsigned long op,
* mapping tricks.
*/
-/*#define HARDDEBUG 1*/
+/* #define HARDDEBUG 1 */
+
+#ifdef HARDDEBUG
+static void dump_freelist(void)
+{
+ FreeBlock *p = first;
+
+ while (p) {
+ fprintf(stderr, "p = %p\r\np->num = %ld\r\np->next = %p\r\n\r\n",
+ (void *) p, (unsigned long) p->num, (void *) p->next);
+ p = p->next;
+ }
+}
+
+#define HARDDEBUG_HW_INCOMPLETE_ALIGNMENT(PTR, SZ) \
+ fprintf(stderr,"Mapping of address %p with size %ld " \
+ "does not map complete pages (%s:%d)\r\n", \
+ (void *) (PTR), (unsigned long) (SZ),__FILE__, __LINE__)
+
+#define HARDDEBUG_HW_UNALIGNED_ALIGNMENT(PTR, SZ) \
+ fprintf(stderr,"Mapping of address %p with size %ld " \
+ "is not page aligned (%s:%d)\r\n", \
+ (void *) (PTR), (unsigned long) (SZ),__FILE__, __LINE__)
+
+#define HARDDEBUG_MAP_FAILED(PTR, SZ) \
+ fprintf(stderr, "Could not actually map memory " \
+ "at address %p with size %ld (%s:%d) ..\r\n", \
+ (void *) (PTR), (unsigned long) (SZ),__FILE__, __LINE__)
+#else
+#define HARDDEBUG_HW_INCOMPLETE_ALIGNMENT(PTR, SZ) do{}while(0)
+#define HARDDEBUG_HW_UNALIGNED_ALIGNMENT(PTR, SZ) do{}while(0)
+#define HARDDEBUG_MAP_FAILED(PTR, SZ) do{}while(0)
+#endif
+
#ifdef __APPLE__
#define MAP_ANONYMOUS MAP_ANON
@@ -1726,49 +1816,20 @@ typedef struct _free_block {
struct _free_block *next;
} FreeBlock;
-/* Assigned once and for all */
-static size_t pagsz;
-
/* Protect with lock */
static FreeBlock *first;
-static size_t round_up_to_pagesize(size_t size)
-{
- size_t x = size / pagsz;
-
- if ((size % pagsz)) {
- ++x;
- }
-
- return pagsz * x;
-}
-
-static size_t round_down_to_pagesize(size_t size)
-{
- size_t x = size / pagsz;
-
- return pagsz * x;
-}
-
static void *do_map(void *ptr, size_t sz)
{
void *res;
- if (round_up_to_pagesize(sz) != sz) {
-#ifdef HARDDEBUG
- fprintf(stderr,"Mapping of address %p with size %ld "
- "does not map complete pages\r\n",
- (void *) ptr, (unsigned long) sz);
-#endif
+ if (ALIGNED_CEILING(sz) != sz) {
+ HARDDEBUG_HW_INCOMPLETE_ALIGNMENT(ptr, sz);
return NULL;
}
- if (((unsigned long) ptr) % pagsz) {
-#ifdef HARDDEBUG
- fprintf(stderr,"Mapping of address %p with size %ld "
- "is not page aligned\r\n",
- (void *) ptr, (unsigned long) sz);
-#endif
+ if (((unsigned long) ptr) % MSEG_ALIGNED_SIZE) {
+ HARDDEBUG_HW_UNALIGNED_ALIGNMENT(ptr, sz);
return NULL;
}
@@ -1782,10 +1843,7 @@ static void *do_map(void *ptr, size_t sz)
#endif
if (res == MAP_FAILED) {
-#ifdef HARDDEBUG
- fprintf(stderr,"Mapping of address %p with size %ld failed!\r\n",
- (void *) ptr, (unsigned long) sz);
-#endif
+ HARDDEBUG_MAP_FAILED(ptr, sz);
return NULL;
}
@@ -1796,35 +1854,22 @@ static int do_unmap(void *ptr, size_t sz)
{
void *res;
- if (round_up_to_pagesize(sz) != sz) {
-#ifdef HARDDEBUG
- fprintf(stderr,"Mapping of address %p with size %ld "
- "does not map complete pages\r\n",
- (void *) ptr, (unsigned long) sz);
-#endif
+ if (ALIGNED_CEILING(sz) != sz) {
+ HARDDEBUG_HW_INCOMPLETE_ALIGNMENT(ptr, sz);
return 1;
}
- if (((unsigned long) ptr) % pagsz) {
-#ifdef HARDDEBUG
- fprintf(stderr,"Mapping of address %p with size %ld "
- "is not page aligned\r\n",
- (void *) ptr, (unsigned long) sz);
-#endif
+ if (((unsigned long) ptr) % MSEG_ALIGNED_SIZE) {
+ HARDDEBUG_HW_UNALIGNED_ALIGNMENT(ptr, sz);
return 1;
}
-
res = mmap(ptr, sz,
- PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE
- | MAP_FIXED,
+ PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_FIXED,
-1 , 0);
if (res == MAP_FAILED) {
-#ifdef HARDDEBUG
- fprintf(stderr,"Mapping of address %p with size %ld failed!\r\n",
- (void *) ptr, (unsigned long) sz);
-#endif
+ HARDDEBUG_MAP_FAILED(ptr, sz);
return 1;
}
@@ -1862,8 +1907,6 @@ static int initialize_pmmap(void)
size_t rsz;
FreeBlock *initial;
-
- pagsz = getpagesize();
SET_RANGE_MIN();
if (sizeof(void *) != 8) {
erl_exit(1,"Halfword emulator cannot be run in 32bit mode");
@@ -1872,15 +1915,15 @@ static int initialize_pmmap(void)
p = (char *) RANGE_MIN;
q = (char *) RANGE_MAX;
- rsz = round_down_to_pagesize(q - p);
+ rsz = ALIGNED_FLOOR(q - p);
- rptr = mmap((void *) p, rsz,
+ rptr = mmap_align(NULL, (void *) p, rsz,
PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS |
MAP_NORESERVE | EXTRA_MAP_FLAGS,
-1 , 0);
#ifdef HARDDEBUG
printf("p=%p, rsz = %ld, pages = %ld, got range = %p -> %p\r\n",
- p, (unsigned long) rsz, (unsigned long) (rsz / pagsz),
+ p, (unsigned long) rsz, (unsigned long) (rsz / MSEG_ALIGNED_SIZE),
(void *) rptr, (void*)(rptr + rsz));
#endif
if ((UWord)(rptr + rsz) > RANGE_MAX) {
@@ -1892,39 +1935,27 @@ static int initialize_pmmap(void)
munmap((void*)RANGE_MAX, rsz - rsz_trunc);
rsz = rsz_trunc;
}
- if (!do_map(rptr,pagsz)) {
+ if (!do_map(rptr, MSEG_ALIGNED_SIZE)) {
erl_exit(1,"Could not actually mmap first page for halfword emulator...\n");
}
initial = (FreeBlock *) rptr;
- initial->num = (rsz / pagsz);
+ initial->num = (rsz / MSEG_ALIGNED_SIZE);
initial->next = NULL;
first = initial;
INIT_LOCK();
return 0;
}
-#ifdef HARDDEBUG
-static void dump_freelist(void)
-{
- FreeBlock *p = first;
-
- while (p) {
- printf("p = %p\r\np->num = %ld\r\np->next = %p\r\n\r\n",
- (void *) p, (unsigned long) p->num, (void *) p->next);
- p = p->next;
- }
-}
-#endif
-
-
static void *pmmap(size_t size)
{
- size_t real_size = round_up_to_pagesize(size);
- size_t num_pages = real_size / pagsz;
+ size_t real_size = ALIGNED_CEILING(size);
+ size_t num_pages = real_size / MSEG_ALIGNED_SIZE;
FreeBlock **block;
FreeBlock *tail;
FreeBlock *res;
+
TAKE_LOCK();
+
for (block = &first;
*block != NULL && (*block)->num < num_pages;
block = &((*block)->next))
@@ -1935,29 +1966,25 @@ static void *pmmap(size_t size)
}
if ((*block)->num == num_pages) {
/* nice, perfect fit */
- res = *block;
+ res = *block;
*block = (*block)->next;
} else {
tail = (FreeBlock *) (((char *) ((void *) (*block))) + real_size);
- if (!do_map(tail,pagsz)) {
-#ifdef HARDDEBUG
- fprintf(stderr, "Could not actually allocate page at %p...\r\n",
- (void *) tail);
-#endif
+ if (!do_map(tail, MSEG_ALIGNED_SIZE)) {
+ HARDDEBUG_MAP_FAILED(tail, MSEG_ALIGNED_SIZE);
RELEASE_LOCK();
return NULL;
}
- tail->num = (*block)->num - num_pages;
+ tail->num = (*block)->num - num_pages;
tail->next = (*block)->next;
res = *block;
*block = tail;
}
+
RELEASE_LOCK();
- if (!do_map(res,real_size)) {
-#ifdef HARDDEBUG
- fprintf(stderr, "Could not actually allocate %ld at %p...\r\n",
- (unsigned long) real_size, (void *) res);
-#endif
+
+ if (!do_map(res, real_size)) {
+ HARDDEBUG_MAP_FAILED(res, real_size);
return NULL;
}
@@ -1966,15 +1993,17 @@ static void *pmmap(size_t size)
static int pmunmap(void *p, size_t size)
{
- size_t real_size = round_up_to_pagesize(size);
- size_t num_pages = real_size / pagsz;
+ size_t real_size = ALIGNED_CEILING(size);
+ size_t num_pages = real_size / MSEG_ALIGNED_SIZE;
+
FreeBlock *block;
FreeBlock *last;
FreeBlock *nb = (FreeBlock *) p;
ASSERT(((unsigned long)p & CHECK_POINTER_MASK)==0);
- if (real_size > pagsz) {
- if (do_unmap(((char *) p) + pagsz,real_size - pagsz)) {
+
+ if (real_size > MSEG_ALIGNED_SIZE) {
+ if (do_unmap(((char *) p) + MSEG_ALIGNED_SIZE, real_size - MSEG_ALIGNED_SIZE)) {
return 1;
}
}
@@ -1993,7 +2022,7 @@ static int pmunmap(void *p, size_t size)
/* Merge new free block with following */
nb->num = block->num + num_pages;
nb->next = block->next;
- if (do_unmap(block,pagsz)) {
+ if (do_unmap(block, MSEG_ALIGNED_SIZE)) {
RELEASE_LOCK();
return 1;
}
@@ -2003,11 +2032,11 @@ static int pmunmap(void *p, size_t size)
nb->next = block;
}
if (last != NULL) {
- if (p == ((void *) (((char *) last) + (last->num * pagsz)))) {
+ if (p == ((void *) (((char *) last) + (last->num * MSEG_ALIGNED_SIZE)))) {
/* Merge with previous */
last->num += nb->num;
last->next = nb->next;
- if (do_unmap(nb,pagsz)) {
+ if (do_unmap(nb, MSEG_ALIGNED_SIZE)) {
RELEASE_LOCK();
return 1;
}
@@ -2024,10 +2053,10 @@ static int pmunmap(void *p, size_t size)
static void *pmremap(void *old_address, size_t old_size,
size_t new_size)
{
- size_t new_real_size = round_up_to_pagesize(new_size);
- size_t new_num_pages = new_real_size / pagsz;
- size_t old_real_size = round_up_to_pagesize(old_size);
- size_t old_num_pages = old_real_size / pagsz;
+ size_t new_real_size = ALIGNED_CEILING(new_size);
+ size_t new_num_pages = new_real_size / MSEG_ALIGNED_SIZE;
+ size_t old_real_size = ALIGNED_CEILING(old_size);
+ size_t old_num_pages = old_real_size / MSEG_ALIGNED_SIZE;
if (new_num_pages == old_num_pages) {
return old_address;
} else if (new_num_pages < old_num_pages) { /* Shrink */
@@ -2045,8 +2074,8 @@ static void *pmremap(void *old_address, size_t old_size,
(*block) > ((FreeBlock *)(((char *) vnfb) + nfb_real_size))) {
/* Normal link in */
if (nfb_pages > 1) {
- if (do_unmap((void *)(((char *) vnfb) + pagsz),
- (nfb_pages - 1)*pagsz)) {
+ if (do_unmap((void *)(((char *) vnfb) + MSEG_ALIGNED_SIZE),
+ (nfb_pages - 1)*MSEG_ALIGNED_SIZE)) {
return NULL;
}
}
@@ -2058,8 +2087,8 @@ static void *pmremap(void *old_address, size_t old_size,
nfb->num = nfb_pages + (*block)->num;
/* unmap also the first page of the next freeblock */
(*block) = nfb;
- if (do_unmap((void *)(((char *) vnfb) + pagsz),
- nfb_pages*pagsz)) {
+ if (do_unmap((void *)(((char *) vnfb) + MSEG_ALIGNED_SIZE),
+ nfb_pages*MSEG_ALIGNED_SIZE)) {
return NULL;
}
}
@@ -2094,9 +2123,9 @@ static void *pmremap(void *old_address, size_t old_size,
size_t remaining_pages = (*block)->num -
(new_num_pages - old_num_pages);
if (!remaining_pages) {
- void *p = (void *) (((char *) (*block)) + pagsz);
+ void *p = (void *) (((char *) (*block)) + MSEG_ALIGNED_SIZE);
void *n = (*block)->next;
- size_t x = ((*block)->num - 1) * pagsz;
+ size_t x = ((*block)->num - 1) * MSEG_ALIGNED_SIZE;
if (x > 0) {
if (do_map(p,x) == NULL) {
RELEASE_LOCK();
@@ -2108,7 +2137,7 @@ static void *pmremap(void *old_address, size_t old_size,
FreeBlock *nfb = (FreeBlock *) ((void *)
(((char *) old_address) +
new_real_size));
- void *p = (void *) (((char *) (*block)) + pagsz);
+ void *p = (void *) (((char *) (*block)) + MSEG_ALIGNED_SIZE);
if (do_map(p,new_real_size - old_real_size) == NULL) {
RELEASE_LOCK();
return NULL;
@@ -2122,5 +2151,4 @@ static void *pmremap(void *old_address, size_t old_size,
}
}
}
-
#endif /* HALFWORD_HEAP */
diff --git a/erts/emulator/sys/common/erl_mseg.h b/erts/emulator/sys/common/erl_mseg.h
index 741080fb78..6f373f13f9 100644
--- a/erts/emulator/sys/common/erl_mseg.h
+++ b/erts/emulator/sys/common/erl_mseg.h
@@ -32,12 +32,35 @@
#if HAVE_MMAP
# define HAVE_ERTS_MSEG 1
+# define HAVE_SUPER_ALIGNED_MB_CARRIERS 1
#else
# define HAVE_ERTS_MSEG 0
+# define HAVE_SUPER_ALIGNED_MB_CARRIERS 0
+#endif
+
+#if HAVE_SUPER_ALIGNED_MB_CARRIERS
+# define MSEG_ALIGN_BITS (18)
+ /* Affects hard limits for sbct and lmbcs documented in erts_alloc.xml */
+#else
+/* If we don't use super aligned multiblock carriers
+ * we will mmap with page size alignment (and thus use corresponding
+ * align bits).
+ *
+ * Current implementation needs this to be a constant and
+ * only uses this for user dev testing so setting page size
+ * to 4096 (12 bits) is fine.
+ */
+# define MSEG_ALIGN_BITS (12)
#endif
#if HAVE_ERTS_MSEG
+#define MSEG_ALIGNED_SIZE (1 << MSEG_ALIGN_BITS)
+
+#define ERTS_MSEG_FLG_NONE ((Uint)(0))
+#define ERTS_MSEG_FLG_2POW ((Uint)(1 << 0))
+
+
#define ERTS_MSEG_VSN_STR "0.9"
typedef struct {
@@ -68,13 +91,13 @@ typedef struct {
extern const ErtsMsegOpt_t erts_mseg_default_opt;
-void *erts_mseg_alloc(ErtsAlcType_t, Uint *);
-void *erts_mseg_alloc_opt(ErtsAlcType_t, Uint *, const ErtsMsegOpt_t *);
+void *erts_mseg_alloc(ErtsAlcType_t, Uint *, Uint);
+void *erts_mseg_alloc_opt(ErtsAlcType_t, Uint *, Uint, const ErtsMsegOpt_t *);
void erts_mseg_dealloc(ErtsAlcType_t, void *, Uint);
void erts_mseg_dealloc_opt(ErtsAlcType_t, void *, Uint, const ErtsMsegOpt_t *);
-void *erts_mseg_realloc(ErtsAlcType_t, void *, Uint, Uint *);
+void *erts_mseg_realloc(ErtsAlcType_t, void *, Uint, Uint *, Uint);
void *erts_mseg_realloc_opt(ErtsAlcType_t, void *, Uint, Uint *,
- const ErtsMsegOpt_t *);
+ Uint, const ErtsMsegOpt_t *);
void erts_mseg_clear_cache(void);
void erts_mseg_cache_check(void);
Uint erts_mseg_no( const ErtsMsegOpt_t *);
diff --git a/erts/emulator/sys/common/erl_poll.c b/erts/emulator/sys/common/erl_poll.c
index 5a8588351d..a523d67158 100644
--- a/erts/emulator/sys/common/erl_poll.c
+++ b/erts/emulator/sys/common/erl_poll.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2006-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2006-2012. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -55,17 +55,12 @@
# ifdef SYS_SELECT_H
# include <sys/select.h>
# endif
-# ifdef VXWORKS
-# include <selectLib.h>
-# endif
#endif
-#ifndef VXWORKS
-# ifdef NO_SYSCONF
-# if ERTS_POLL_USE_SELECT
-# include <sys/param.h>
-# else
-# include <limits.h>
-# endif
+#ifdef NO_SYSCONF
+# if ERTS_POLL_USE_SELECT
+# include <sys/param.h>
+# else
+# include <limits.h>
# endif
#endif
#include "erl_thr_progress.h"
@@ -2222,10 +2217,6 @@ ERTS_POLL_EXPORT(erts_poll_max_fds)(void)
* --- Initialization --------------------------------------------------------
*/
-#ifdef VXWORKS
-extern int erts_vxworks_max_files;
-#endif
-
void
ERTS_POLL_EXPORT(erts_poll_init)(void)
{
@@ -2234,9 +2225,7 @@ ERTS_POLL_EXPORT(erts_poll_init)(void)
errno = 0;
-#if defined(VXWORKS)
- max_fds = erts_vxworks_max_files;
-#elif !defined(NO_SYSCONF)
+#if !defined(NO_SYSCONF)
max_fds = sysconf(_SC_OPEN_MAX);
#elif ERTS_POLL_USE_SELECT
max_fds = NOFILE;
diff --git a/erts/emulator/sys/common/erl_sys_common_misc.c b/erts/emulator/sys/common/erl_sys_common_misc.c
index 461e763f03..d22914acea 100644
--- a/erts/emulator/sys/common/erl_sys_common_misc.c
+++ b/erts/emulator/sys/common/erl_sys_common_misc.c
@@ -105,3 +105,154 @@ int erts_get_native_filename_encoding(void)
{
return filename_encoding;
}
+
+/* For internal use by sys_double_to_chars_fast() */
+static char* float_first_trailing_zero(char* p)
+{
+ for (--p; *p == '0' && *(p-1) == '0'; --p);
+ if (*(p-1) == '.') ++p;
+ return p;
+}
+
+int
+sys_double_to_chars(double fp, char *buffer, size_t buffer_size)
+{
+ return sys_double_to_chars_ext(fp, buffer, buffer_size, SYS_DEFAULT_FLOAT_DECIMALS);
+}
+
+int
+sys_double_to_chars_fast(double f, char *outbuf, int maxlen, int decimals, int compact)
+{
+ enum {
+ FRAC_SIZE = 52
+ , EXP_SIZE = 11
+ , EXP_MASK = (1ll << EXP_SIZE) - 1
+ , FRAC_MASK = (1ll << FRAC_SIZE) - 1
+ , FRAC_MASK2 = (1ll << (FRAC_SIZE + 1)) - 1
+ , MAX_FLOAT = 1ll << (FRAC_SIZE+1)
+ };
+
+ long long mantissa, int_part, int_part2, frac_part;
+ short exp;
+ int sign, i, n, m, max;
+ double absf;
+ union { long long L; double F; } x;
+ char c, *p = outbuf;
+ int digit, roundup;
+
+ x.F = f;
+
+ exp = (x.L >> FRAC_SIZE) & EXP_MASK;
+ mantissa = x.L & FRAC_MASK;
+ sign = x.L >= 0 ? 1 : -1;
+ if (exp == EXP_MASK) {
+ if (mantissa == 0) {
+ if (sign == -1)
+ *p++ = '-';
+ *p++ = 'i';
+ *p++ = 'n';
+ *p++ = 'f';
+ } else {
+ *p++ = 'n';
+ *p++ = 'a';
+ *p++ = 'n';
+ }
+ *p = '\0';
+ return p - outbuf;
+ }
+
+ exp -= EXP_MASK >> 1;
+ mantissa |= (1ll << FRAC_SIZE);
+ frac_part = 0;
+ int_part = 0;
+ absf = f * sign;
+
+ /* Don't bother with optimizing too large numbers and decimals */
+ if (absf > MAX_FLOAT || decimals > maxlen-17) {
+ int len = erts_snprintf(outbuf, maxlen, "%.*f", decimals, f);
+ if (len >= maxlen)
+ return -1;
+ p = outbuf + len;
+ /* Delete trailing zeroes */
+ if (compact)
+ p = float_first_trailing_zero(outbuf + len);
+ *p = '\0';
+ return p - outbuf;
+ }
+
+ if (exp >= FRAC_SIZE)
+ int_part = mantissa << (exp - FRAC_SIZE);
+ else if (exp >= 0) {
+ int_part = mantissa >> (FRAC_SIZE - exp);
+ frac_part = (mantissa << (exp + 1)) & FRAC_MASK2;
+ }
+ else /* if (exp < 0) */
+ frac_part = (mantissa & FRAC_MASK2) >> -(exp + 1);
+
+ if (int_part == 0) {
+ if (sign == -1)
+ *p++ = '-';
+ *p++ = '0';
+ } else {
+ int ret;
+ while (int_part != 0) {
+ int_part2 = int_part / 10;
+ *p++ = (char)(int_part - ((int_part2 << 3) + (int_part2 << 1)) + '0');
+ int_part = int_part2;
+ }
+ if (sign == -1)
+ *p++ = '-';
+ /* Reverse string */
+ ret = p - outbuf;
+ for (i = 0, n = ret/2; i < n; i++) {
+ int j = ret - i - 1;
+ c = outbuf[i];
+ outbuf[i] = outbuf[j];
+ outbuf[j] = c;
+ }
+ }
+ if (decimals != 0)
+ *p++ = '.';
+
+ max = maxlen - (p - outbuf) - 1 /* leave room for trailing '\0' */;
+ if (max > decimals)
+ max = decimals;
+ for (m = 0; m < max; m++) {
+ /* frac_part *= 10; */
+ frac_part = (frac_part << 3) + (frac_part << 1);
+
+ *p++ = (char)((frac_part >> (FRAC_SIZE + 1)) + '0');
+ frac_part &= FRAC_MASK2;
+ }
+
+ roundup = 0;
+ /* Rounding - look at the next digit */
+ frac_part = (frac_part << 3) + (frac_part << 1);
+ digit = (frac_part >> (FRAC_SIZE + 1));
+ if (digit > 5)
+ roundup = 1;
+ else if (digit == 5) {
+ frac_part &= FRAC_MASK2;
+ if (frac_part != 0) roundup = 1;
+ }
+ if (roundup) {
+ char d;
+ int pos = p - outbuf - 1;
+ do {
+ d = outbuf[pos];
+ if (d == '-') break;
+ if (d == '.') continue;
+ if (++d != ':') {
+ outbuf[pos] = d;
+ break;
+ }
+ outbuf[pos] = '0';
+ } while (--pos);
+ }
+
+ /* Delete trailing zeroes */
+ if (compact && *(p - 1) == '0')
+ p = float_first_trailing_zero(--p);
+ *p = '\0';
+ return p - outbuf;
+}
diff --git a/erts/emulator/sys/unix/erl_unix_sys_ddll.c b/erts/emulator/sys/unix/erl_unix_sys_ddll.c
index 336d9586c4..a35aec560a 100644
--- a/erts/emulator/sys/unix/erl_unix_sys_ddll.c
+++ b/erts/emulator/sys/unix/erl_unix_sys_ddll.c
@@ -101,7 +101,7 @@ void erl_sys_ddll_init(void) {
/*
* Open a shared object
*/
-int erts_sys_ddll_open2(char *full_name, void **handle, ErtsSysDdllError* err)
+int erts_sys_ddll_open2(const char *full_name, void **handle, ErtsSysDdllError* err)
{
#if defined(HAVE_DLOPEN)
char* dlname;
@@ -153,7 +153,7 @@ int erts_sys_ddll_open_noext(char *dlname, void **handle, ErtsSysDdllError* err)
/*
* Find a symbol in the shared object
*/
-int erts_sys_ddll_sym2(void *handle, char *func_name, void **function,
+int erts_sys_ddll_sym2(void *handle, const char *func_name, void **function,
ErtsSysDdllError* err)
{
#if defined(HAVE_DLOPEN)
diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c
index bf69f3bf90..0b96eded76 100644
--- a/erts/emulator/sys/unix/sys.c
+++ b/erts/emulator/sys/unix/sys.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2011. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2012. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -58,7 +58,6 @@
#define __DARWIN__ 1
#endif
-
#ifdef USE_THREADS
#include "erl_threads.h"
#endif
@@ -71,7 +70,6 @@ static erts_smp_rwmtx_t environ_rwmtx;
#define MAX_VSIZE 16 /* Max number of entries allowed in an I/O
* vector sock_sendv().
*/
-
/*
* Don't need global.h, but bif_table.h (included by bif.h),
* won't compile otherwise
@@ -123,6 +121,16 @@ struct ErtsSysReportExit_ {
#endif
};
+/* This data is shared by these drivers - initialized by spawn_init() */
+static struct driver_data {
+ ErlDrvPort port_num;
+ int ofd, packet_bytes;
+ ErtsSysReportExit *report_exit;
+ int pid;
+ int alive;
+ int status;
+} *driver_data; /* indexed by fd */
+
static ErtsSysReportExit *report_exit_list;
#if CHLDWTHR && !defined(ERTS_SMP)
static ErtsSysReportExit *report_exit_transit_list;
@@ -551,7 +559,7 @@ erl_sys_init(void)
size_t bindirsz = sizeof(bindir);
Uint csp_path_sz;
- res = erts_sys_getenv("BINDIR", bindir, &bindirsz);
+ res = erts_sys_getenv_raw("BINDIR", bindir, &bindirsz);
if (res != 0) {
if (res < 0)
erl_exit(-1,
@@ -570,7 +578,7 @@ erl_sys_init(void)
+ 1);
child_setup_prog = erts_alloc(ERTS_ALC_T_CS_PROG_PATH, csp_path_sz);
erts_smp_atomic_add_nob(&sys_misc_mem_sz, csp_path_sz);
- sprintf(child_setup_prog,
+ erts_snprintf(child_setup_prog, csp_path_sz,
"%s%c%s",
bindir,
DIR_SEPARATOR_CHAR,
@@ -679,18 +687,58 @@ static RETSIGTYPE break_handler(int sig)
}
#endif /* 0 */
-static ERTS_INLINE void
-prepare_crash_dump(void)
+static ERTS_INLINE int
+prepare_crash_dump(int secs)
{
+#define NUFBUF (3)
int i, max;
char env[21]; /* enough to hold any 64-bit integer */
size_t envsz;
+ DeclareTmpHeapNoproc(heap,NUFBUF);
+ Port *heart_port;
+ Eterm *hp = heap;
+ Eterm list = NIL;
+ int heart_fd[2] = {-1,-1};
+ int has_heart = 0;
+
+ UseTmpHeapNoproc(NUFBUF);
if (ERTS_PREPARED_CRASH_DUMP)
- return; /* We have already been called */
+ return 0; /* We have already been called */
+
+ heart_port = erts_get_heart_port();
+
+ /* Positive secs means an alarm must be set
+ * 0 or negative means no alarm
+ *
+ * Set alarm before we try to write to a port
+ * we don't want to hang on a port write with
+ * no alarm.
+ *
+ */
+
+ if (secs >= 0) {
+ alarm((unsigned int)secs);
+ }
+
+ if (heart_port) {
+ /* hearts input fd
+ * We "know" drv_data is the in_fd since the port is started with read|write
+ */
+ heart_fd[0] = (int)heart_port->drv_data;
+ heart_fd[1] = (int)driver_data[heart_fd[0]].ofd;
+ has_heart = 1;
+
+ list = CONS(hp, make_small(8), list); hp += 2;
+
+ /* send to heart port, CMD = 8, i.e. prepare crash dump =o */
+ erts_port_output(NULL, ERTS_PORT_SIG_FLG_FORCE_IMM_CALL, heart_port,
+ heart_port->common.id, list, NULL);
+ }
/* Make sure we unregister at epmd (unknown fd) and get at least
one free filedescriptor (for erl_crash.dump) */
+
max = max_files;
if (max < 1024)
max = 1024;
@@ -704,11 +752,15 @@ prepare_crash_dump(void)
if (i == async_fd[0] || i == async_fd[1])
continue;
#endif
+ /* We don't want to close our heart yet ... */
+ if (i == heart_fd[0] || i == heart_fd[1])
+ continue;
+
close(i);
}
envsz = sizeof(env);
- i = erts_sys_getenv("ERL_CRASH_DUMP_NICE", env, &envsz);
+ i = erts_sys_getenv__("ERL_CRASH_DUMP_NICE", env, &envsz);
if (i >= 0) {
int nice_val;
nice_val = i != 0 ? 0 : atoi(env);
@@ -717,21 +769,15 @@ prepare_crash_dump(void)
}
erts_silence_warn_unused_result(nice(nice_val));
}
-
- envsz = sizeof(env);
- i = erts_sys_getenv("ERL_CRASH_DUMP_SECONDS", env, &envsz);
- if (i >= 0) {
- unsigned sec;
- sec = (unsigned) i != 0 ? 0 : atoi(env);
- alarm(sec);
- }
+ UnUseTmpHeapNoproc(NUFBUF);
+#undef NUFBUF
+ return has_heart;
}
-void
-erts_sys_prepare_crash_dump(void)
+int erts_sys_prepare_crash_dump(int secs)
{
- prepare_crash_dump();
+ return prepare_crash_dump(secs);
}
static ERTS_INLINE void
@@ -768,12 +814,22 @@ static RETSIGTYPE request_break(int signum)
static ERTS_INLINE void
sigusr1_exit(void)
{
- /* We do this at interrupt level, since the main reason for
- wanting to generate a crash dump in this way is that the emulator
- is hung somewhere, so it won't be able to poll any flag we set here.
- */
+ char env[21]; /* enough to hold any 64-bit integer */
+ size_t envsz;
+ int i, secs = -1;
+
+ /* We do this at interrupt level, since the main reason for
+ * wanting to generate a crash dump in this way is that the emulator
+ * is hung somewhere, so it won't be able to poll any flag we set here.
+ */
ERTS_SET_GOT_SIGUSR1;
- prepare_crash_dump();
+
+ envsz = sizeof(env);
+ if ((i = erts_sys_getenv_raw("ERL_CRASH_DUMP_SECONDS", env, &envsz)) >= 0) {
+ secs = i != 0 ? 0 : atoi(env);
+ }
+
+ prepare_crash_dump(secs);
erl_exit(1, "Received SIGUSR1\n");
}
@@ -1021,15 +1077,6 @@ void fini_getenv_state(GETENV_STATE *state)
#define ERTS_SYS_READ_BUF_SZ (64*1024)
-/* This data is shared by these drivers - initialized by spawn_init() */
-static struct driver_data {
- int port_num, ofd, packet_bytes;
- ErtsSysReportExit *report_exit;
- int pid;
- int alive;
- int status;
-} *driver_data; /* indexed by fd */
-
/* Driver interfaces */
static ErlDrvData spawn_start(ErlDrvPort, char*, SysDriverOpts*);
static ErlDrvData fd_start(ErlDrvPort, char*, SysDriverOpts*);
@@ -1137,7 +1184,7 @@ static RETSIGTYPE onchld(int signum)
#endif
}
-static int set_driver_data(int port_num,
+static int set_driver_data(ErlDrvPort port_num,
int ifd,
int ofd,
int packet_bytes,
@@ -1145,6 +1192,7 @@ static int set_driver_data(int port_num,
int exit_status,
int pid)
{
+ Port *prt;
ErtsSysReportExit *report_exit;
if (!exit_status)
@@ -1153,7 +1201,7 @@ static int set_driver_data(int port_num,
report_exit = erts_alloc(ERTS_ALC_T_PRT_REP_EXIT,
sizeof(ErtsSysReportExit));
report_exit->next = report_exit_list;
- report_exit->port = erts_port[port_num].id;
+ report_exit->port = erts_drvport2id(port_num);
report_exit->pid = pid;
report_exit->ifd = read_write & DO_READ ? ifd : -1;
report_exit->ofd = read_write & DO_WRITE ? ofd : -1;
@@ -1163,7 +1211,9 @@ static int set_driver_data(int port_num,
report_exit_list = report_exit;
}
- erts_port[port_num].os_pid = pid;
+ prt = erts_drvport2port(port_num, NULL);
+ if (prt)
+ prt->os_pid = pid;
if (read_write & DO_READ) {
driver_data[ifd].packet_bytes = packet_bytes;
@@ -1236,7 +1286,7 @@ static void close_pipes(int ifd[2], int ofd[2], int read_write)
}
}
-static void init_fd_data(int fd, int prt)
+static void init_fd_data(int fd, ErlDrvPort port_num)
{
fd_data[fd].buf = NULL;
fd_data[fd].cpos = NULL;
@@ -1356,9 +1406,9 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name, SysDriverOpts* op
int no_vfork;
size_t no_vfork_sz = sizeof(no_vfork);
- no_vfork = (erts_sys_getenv("ERL_NO_VFORK",
- (char *) &no_vfork,
- &no_vfork_sz) >= 0);
+ no_vfork = (erts_sys_getenv_raw("ERL_NO_VFORK",
+ (char *) &no_vfork,
+ &no_vfork_sz) >= 0);
#endif
switch (opts->read_write) {
@@ -1532,12 +1582,13 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name, SysDriverOpts* op
}
#if !DISABLE_VFORK
}
+#define ENOUGH_BYTES (44)
else { /* Use vfork() */
char **cs_argv= erts_alloc(ERTS_ALC_T_TMP,(CS_ARGV_NO_OF_ARGS + 1)*
sizeof(char *));
- char fd_close_range[44]; /* 44 bytes are enough to */
- char dup2_op[CS_ARGV_NO_OF_DUP2_OPS][44]; /* hold any "%d:%d" string */
- /* on a 64-bit machine. */
+ char fd_close_range[ENOUGH_BYTES]; /* 44 bytes are enough to */
+ char dup2_op[CS_ARGV_NO_OF_DUP2_OPS][ENOUGH_BYTES]; /* hold any "%d:%d" string */
+ /* on a 64-bit machine. */
/* Setup argv[] for the child setup program (implemented in
erl_child_setup.c) */
@@ -1545,23 +1596,23 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name, SysDriverOpts* op
if (opts->use_stdio) {
if (opts->read_write & DO_READ){
/* stdout for process */
- sprintf(&dup2_op[i++][0], "%d:%d", ifd[1], 1);
+ erts_snprintf(&dup2_op[i++][0], ENOUGH_BYTES, "%d:%d", ifd[1], 1);
if(opts->redir_stderr)
/* stderr for process */
- sprintf(&dup2_op[i++][0], "%d:%d", ifd[1], 2);
+ erts_snprintf(&dup2_op[i++][0], ENOUGH_BYTES, "%d:%d", ifd[1], 2);
}
if (opts->read_write & DO_WRITE)
/* stdin for process */
- sprintf(&dup2_op[i++][0], "%d:%d", ofd[0], 0);
+ erts_snprintf(&dup2_op[i++][0], ENOUGH_BYTES, "%d:%d", ofd[0], 0);
} else { /* XXX will fail if ofd[0] == 4 (unlikely..) */
if (opts->read_write & DO_READ)
- sprintf(&dup2_op[i++][0], "%d:%d", ifd[1], 4);
+ erts_snprintf(&dup2_op[i++][0], ENOUGH_BYTES, "%d:%d", ifd[1], 4);
if (opts->read_write & DO_WRITE)
- sprintf(&dup2_op[i++][0], "%d:%d", ofd[0], 3);
+ erts_snprintf(&dup2_op[i++][0], ENOUGH_BYTES, "%d:%d", ofd[0], 3);
}
for (; i < CS_ARGV_NO_OF_DUP2_OPS; i++)
strcpy(&dup2_op[i][0], "-");
- sprintf(fd_close_range, "%d:%d", opts->use_stdio ? 3 : 5, max_files-1);
+ erts_snprintf(fd_close_range, ENOUGH_BYTES, "%d:%d", opts->use_stdio ? 3 : 5, max_files-1);
cs_argv[CS_ARGV_PROGNAME_IX] = child_setup_prog;
cs_argv[CS_ARGV_WD_IX] = opts->wd ? opts->wd : ".";
@@ -1612,6 +1663,7 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name, SysDriverOpts* op
}
erts_free(ERTS_ALC_T_TMP,cs_argv);
}
+#undef ENOUGH_BYTES
#endif
erts_sched_bind_atfork_parent(unbind);
@@ -1924,7 +1976,7 @@ static void clear_fd_data(int fd)
fd_data[fd].psz = 0;
}
-static void nbio_stop_fd(int prt, int fd)
+static void nbio_stop_fd(ErlDrvPort prt, int fd)
{
driver_select(prt,fd,DO_READ|DO_WRITE,0);
clear_fd_data(fd);
@@ -1972,7 +2024,8 @@ static ErlDrvData vanilla_start(ErlDrvPort port_num, char* name,
static void stop(ErlDrvData fd)
{
- int prt, ofd;
+ ErlDrvPort prt;
+ int ofd;
prt = driver_data[(int)(long)fd].port_num;
nbio_stop_fd(prt, (int)(long)fd);
@@ -1985,7 +2038,7 @@ static void stop(ErlDrvData fd)
CHLD_STAT_LOCK;
- /* Mark as unused. Maybe resetting the 'port_num' slot is better? */
+ /* Mark as unused. */
driver_data[(int)(long)fd].pid = -1;
CHLD_STAT_UNLOCK;
@@ -2001,7 +2054,7 @@ static void stop(ErlDrvData fd)
static void outputv(ErlDrvData e, ErlIOVec* ev)
{
int fd = (int)(long)e;
- int ix = driver_data[fd].port_num;
+ ErlDrvPort ix = driver_data[fd].port_num;
int pb = driver_data[fd].packet_bytes;
int ofd = driver_data[fd].ofd;
ssize_t n;
@@ -2051,7 +2104,7 @@ static void outputv(ErlDrvData e, ErlIOVec* ev)
static void output(ErlDrvData e, char* buf, ErlDrvSizeT len)
{
int fd = (int)(long)e;
- int ix = driver_data[fd].port_num;
+ ErlDrvPort ix = driver_data[fd].port_num;
int pb = driver_data[fd].packet_bytes;
int ofd = driver_data[fd].ofd;
ssize_t n;
@@ -2102,7 +2155,7 @@ static void output(ErlDrvData e, char* buf, ErlDrvSizeT len)
return; /* 0; */
}
-static int port_inp_failure(int port_num, int ready_fd, int res)
+static int port_inp_failure(ErlDrvPort port_num, int ready_fd, int res)
/* Result: 0 (eof) or -1 (error) */
{
int err = errno;
@@ -2152,7 +2205,7 @@ static int port_inp_failure(int port_num, int ready_fd, int res)
static void ready_input(ErlDrvData e, ErlDrvEvent ready_fd)
{
int fd = (int)(long)e;
- int port_num;
+ ErlDrvPort port_num;
int packet_bytes;
int res;
Uint h;
@@ -2275,7 +2328,7 @@ static void ready_input(ErlDrvData e, ErlDrvEvent ready_fd)
static void ready_output(ErlDrvData e, ErlDrvEvent ready_fd)
{
int fd = (int)(long)e;
- int ix = driver_data[fd].port_num;
+ ErlDrvPort ix = driver_data[fd].port_num;
int n;
struct iovec* iv;
int vsize;
@@ -2355,28 +2408,38 @@ void erts_do_break_handling(void)
** no interpretatione of this should be done by the rest of the
** emulator. The buffer should be at least 21 bytes long.
*/
-void sys_get_pid(char *buffer){
+void sys_get_pid(char *buffer, size_t buffer_size){
pid_t p = getpid();
/* Assume the pid is scalar and can rest in an unsigned long... */
- sprintf(buffer,"%lu",(unsigned long) p);
+ erts_snprintf(buffer, buffer_size, "%lu",(unsigned long) p);
}
int
-erts_sys_putenv(char *buffer, int sep_ix)
+erts_sys_putenv_raw(char *key, char *value) {
+ return erts_sys_putenv(key, value);
+}
+int
+erts_sys_putenv(char *key, char *value)
{
int res;
char *env;
+ Uint need = strlen(key) + strlen(value) + 2;
+
#ifdef HAVE_COPYING_PUTENV
- env = buffer;
+ env = erts_alloc(ERTS_ALC_T_TMP, need);
#else
- Uint sz = strlen(buffer)+1;
- env = erts_alloc(ERTS_ALC_T_PUTENV_STR, sz);
- erts_smp_atomic_add_nob(&sys_misc_mem_sz, sz);
- strcpy(env,buffer);
+ env = erts_alloc(ERTS_ALC_T_PUTENV_STR, need);
+ erts_smp_atomic_add_nob(&sys_misc_mem_sz, need);
#endif
+ strcpy(env,key);
+ strcat(env,"=");
+ strcat(env,value);
erts_smp_rwmtx_rwlock(&environ_rwmtx);
res = putenv(env);
erts_smp_rwmtx_rwunlock(&environ_rwmtx);
+#ifdef HAVE_COPYING_PUTENV
+ erts_free(ERTS_ALC_T_TMP, env);
+#endif
return res;
}
@@ -2403,6 +2466,20 @@ erts_sys_getenv__(char *key, char *value, size_t *size)
}
int
+erts_sys_getenv_raw(char *key, char *value, size_t *size) {
+ return erts_sys_getenv(key, value, size);
+}
+
+/*
+ * erts_sys_getenv
+ * returns:
+ * -1, if environment key is not set with a value
+ * 0, if environment key is set and value fits into buffer size
+ * 1, if environment key is set but does not fit into buffer size
+ * size is set with the needed buffer size value
+ */
+
+int
erts_sys_getenv(char *key, char *value, size_t *size)
{
int res;
@@ -2560,19 +2637,20 @@ report_exit_status(ErtsSysReportExit *rep, int status)
Port *pp;
#ifdef ERTS_SMP
CHLD_STAT_UNLOCK;
-#endif
+ pp = erts_thr_id2port_sflgs(rep->port,
+ ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP);
+ CHLD_STAT_LOCK;
+#else
pp = erts_id2port_sflgs(rep->port,
NULL,
0,
ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP);
-#ifdef ERTS_SMP
- CHLD_STAT_LOCK;
#endif
if (pp) {
if (rep->ifd >= 0) {
driver_data[rep->ifd].alive = 0;
driver_data[rep->ifd].status = status;
- (void) driver_select((ErlDrvPort) internal_port_index(pp->id),
+ (void) driver_select((ErlDrvPort) pp,
rep->ifd,
(ERL_DRV_READ|ERL_DRV_USE),
1);
@@ -2580,12 +2658,16 @@ report_exit_status(ErtsSysReportExit *rep, int status)
if (rep->ofd >= 0) {
driver_data[rep->ofd].alive = 0;
driver_data[rep->ofd].status = status;
- (void) driver_select((ErlDrvPort) internal_port_index(pp->id),
+ (void) driver_select((ErlDrvPort) pp,
rep->ofd,
(ERL_DRV_WRITE|ERL_DRV_USE),
1);
}
+#ifdef ERTS_SMP
+ erts_thr_port_release(pp);
+#else
erts_port_release(pp);
+#endif
}
erts_free(ERTS_ALC_T_PRT_REP_EXIT, rep);
}
@@ -3001,7 +3083,7 @@ erl_sys_args(int* argc, char** argv)
if (erts_use_kernel_poll) {
char no_kp[10];
size_t no_kp_sz = sizeof(no_kp);
- int res = erts_sys_getenv("ERL_NO_KERNEL_POLL", no_kp, &no_kp_sz);
+ int res = erts_sys_getenv_raw("ERL_NO_KERNEL_POLL", no_kp, &no_kp_sz);
if (res > 0
|| (res == 0
&& sys_strcmp("false", no_kp) != 0
diff --git a/erts/emulator/sys/unix/sys_float.c b/erts/emulator/sys/unix/sys_float.c
index 8ec7b31ce0..6875c17a75 100644
--- a/erts/emulator/sys/unix/sys_float.c
+++ b/erts/emulator/sys/unix/sys_float.c
@@ -735,7 +735,7 @@ void erts_sys_unblock_fpe(int unmasked)
/*
** Convert a double to ascii format 0.dddde[+|-]ddd
- ** return number of characters converted
+ ** return number of characters converted or -1 if error.
**
** These two functions should maybe use localeconv() to pick up
** the current radix character, but since it is uncertain how
@@ -745,18 +745,19 @@ void erts_sys_unblock_fpe(int unmasked)
*/
int
-sys_double_to_chars(double fp, char *buf)
+sys_double_to_chars_ext(double fp, char *buffer, size_t buffer_size, size_t decimals)
{
- char *s = buf;
-
- (void) sprintf(buf, "%.20e", fp);
+ char *s = buffer;
+
+ if (erts_snprintf(buffer, buffer_size, "%.*e", decimals, fp) >= buffer_size)
+ return -1;
/* Search upto decimal point */
if (*s == '+' || *s == '-') s++;
while (ISDIGIT(*s)) s++;
if (*s == ',') *s++ = '.'; /* Replace ',' with '.' */
/* Scan to end of string */
while (*s) s++;
- return s-buf; /* i.e strlen(buf) */
+ return s-buffer; /* i.e strlen(buffer) */
}
/* Float conversion */
diff --git a/erts/emulator/sys/vxworks/driver_int.h b/erts/emulator/sys/vxworks/driver_int.h
deleted file mode 100644
index f6bc71a799..0000000000
--- a/erts/emulator/sys/vxworks/driver_int.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * %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%
- */
-/*----------------------------------------------------------------------
-** Purpose : System dependant driver declarations
-**---------------------------------------------------------------------- */
-
-#ifndef __DRIVER_INT_H__
-#define __DRIVER_INT_H__
-
-#include <ioLib.h>
-
-typedef struct iovec SysIOVec;
-
-#endif
diff --git a/erts/emulator/sys/vxworks/erl_main.c b/erts/emulator/sys/vxworks/erl_main.c
deleted file mode 100644
index c9b44a635a..0000000000
--- a/erts/emulator/sys/vxworks/erl_main.c
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 2000-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%
- */
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-#include "sys.h"
-#include "erl_vm.h"
-
-#if defined(__GNUC__)
-/*
- * The generated assembler does the usual trick (relative
- * branch-and-link to next instruction) to get a copy of the
- * instruction ptr. Instead of branching to an explicit zero offset,
- * it branches to the symbol `__eabi' --- which is expected to be
- * undefined and thus zero (if it is defined as non-zero, things will
- * be interesting --- as in the Chinese curse). To shut up the VxWorks
- * linker, we define `__eabi' as zero.
- *
- * This is just a work around. It's really Wind River's GCC's code
- * generator that should be fixed.
- */
-__asm__(".equ __eabi, 0");
-#endif
-
-void
-erl_main(int argc, char **argv)
-{
- erl_start(argc, argv);
-}
diff --git a/erts/emulator/sys/vxworks/erl_vxworks_sys.h b/erts/emulator/sys/vxworks/erl_vxworks_sys.h
deleted file mode 100644
index 3d53238ea6..0000000000
--- a/erts/emulator/sys/vxworks/erl_vxworks_sys.h
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- * %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%
- */
-#ifndef __ERL_VXWORKS_SYS_H__
-#define __ERL_VXWORKS_SYS_H__
-
-/* stdarg.h don't work without this one... */
-#include <vxWorks.h>
-
-#include <stdio.h>
-#include <math.h>
-#include <limits.h>
-#include <stdlib.h>
-#define index StringIndexFunctionThatIDontWantDeclared
-#include <string.h>
-#undef index
-
-
-
-#include <sys/times.h>
-#include <time.h>/* xxxP */
-
-#include <dirent.h>
-#include <sys/stat.h>
-
-/* xxxP from unix_sys.h begin */
-
-/*
- * Make sure that MAXPATHLEN is defined.
- */
-
-#ifndef MAXPATHLEN
-# ifdef PATH_MAX
-# define MAXPATHLEN PATH_MAX
-# else
-# define MAXPATHLEN 2048
-# endif
-#endif
-
-/* xxxP end */
-
-
-/* Unimplemented math functions */
-#define NO_ASINH
-#define NO_ACOSH
-#define NO_ATANH
-#define NO_ERF
-#define NO_ERFC
-
-/* Stuff that is useful for port programs, drivers, etc */
-#ifndef VXWORKS
-#define VXWORKS
-#endif
-
-#define DONT_USE_MAIN
-#define NO_FSYNC
-#define NO_MKDIR_MODE
-#define NO_UMASK
-#define NO_SYMBOLIC_LINKS
-#define NO_DEVICE_FILES
-#define NO_UID
-#define NO_ACCESS
-#define NO_FCNTL
-#define NO_SYSLOG
-#define NO_SYSCONF
-#define NO_PWD /* XXX Means what? */
-#define NO_DAEMON
-/* This chooses ~250 reductions instead of 500 in config.h */
-#if (CPU == CPU32)
-#define SLOW_PROCESSOR
-#endif
-
-/*
- * Even though we does not always have small memories on VxWorks
- * we certainly does not have virtual memory.
- */
-#if !defined(LARGE_MEMORY)
-#define SMALL_MEMORY
-#endif
-
-/*************** Floating point exception handling ***************/
-
-/* There are no known ways to customize the handling of invalid floating
- point operations, such as matherr() or ieee_handler(), in VxWorks 5.1. */
-
-#if (CPU == MC68040 || CPU == CPU32 || CPU == PPC860 || CPU == PPC32 || \
- CPU == PPC603 || CPU == PPC604 || CPU == SIMSPARCSOLARIS)
-
-/* VxWorks 5.1 on Motorola 68040 never generates SIGFPE, but sets the
- result of invalid floating point ops to Inf and NaN - unfortunately
- the way to test for those values is undocumented and hidden in a
- "private" include file... */
-/* Haven't found any better way, as of yet, for ppc860 xxxP*/
-
-#include <private/mathP.h>
-#define NO_FPE_SIGNALS
-#define erts_get_current_fp_exception() NULL
-#define __ERTS_FP_CHECK_INIT(fpexnp) do {} while (0)
-#define __ERTS_FP_ERROR(fpexnp, f, Action) if (isInf(f) || isNan(f)) { Action; } else {}
-#define __ERTS_FP_ERROR_THOROUGH(fpexnp, f, Action) __ERTS_FP_ERROR(fpexnp, f, Action)
-#define __ERTS_SAVE_FP_EXCEPTION(fpexnp)
-#define __ERTS_RESTORE_FP_EXCEPTION(fpexnp)
-
-#define ERTS_FP_CHECK_INIT(p) __ERTS_FP_CHECK_INIT(&(p)->fp_exception)
-#define ERTS_FP_ERROR(p, f, A) __ERTS_FP_ERROR(&(p)->fp_exception, f, A)
-#define ERTS_SAVE_FP_EXCEPTION(p) __ERTS_SAVE_FP_EXCEPTION(&(p)->fp_exception)
-#define ERTS_RESTORE_FP_EXCEPTION(p) __ERTS_RESTORE_FP_EXCEPTION(&(p)->fp_exception)
-#define ERTS_FP_ERROR_THOROUGH(p, f, A) __ERTS_FP_ERROR_THOROUGH(&(p)->fp_exception, f, A)
-
-#define erts_sys_block_fpe() 0
-#define erts_sys_unblock_fpe(x) do{}while(0)
-
-#if (CPU == PPC603)
-/* Need fppLib to change the Floating point registers
- (fix_registers in sys.c)*/
-
-#include <fppLib.h>
-
-#endif /* PPC603 */
-
-#else
-
-Unsupported CPU value !
-
-#endif
-
-typedef void *GETENV_STATE;
-
-#define HAVE_GETHRTIME
-
-extern int erts_clock_rate;
-
-#define SYS_CLK_TCK (erts_clock_rate)
-
-#define SYS_CLOCK_RESOLUTION 1
-
-typedef struct _vxworks_tms {
- clock_t tms_utime;
- clock_t tms_stime;
- clock_t tms_cutime;
- clock_t tms_cstime;
-} SysTimes;
-
-typedef long long SysHrTime;
-
-typedef time_t erts_time_t;
-typedef struct timeval SysTimeval;
-
-extern int sys_init_hrtime(void);
-extern SysHrTime sys_gethrtime(void);
-extern void sys_gettimeofday(SysTimeval *tvp);
-extern clock_t sys_times(SysTimes *t);
-
-#define SIZEOF_SHORT 2
-#define SIZEOF_INT 4
-#define SIZEOF_LONG 4
-#define SIZEOF_VOID_P 4
-#define SIZEOF_SIZE_T 4
-#define SIZEOF_OFF_T 4
-
-/*
- * Temporary buffer *only* used in sys code.
- */
-#define SYS_TMP_BUF_SIZE 65536
-
-/* Need to be able to interrupt erts_poll_wait() from signal handler */
-#define ERTS_POLL_NEED_ASYNC_INTERRUPT_SUPPORT
-
-#endif /* __ERL_VXWORKS_SYS_H__ */
diff --git a/erts/emulator/sys/vxworks/erl_vxworks_sys_ddll.c b/erts/emulator/sys/vxworks/erl_vxworks_sys_ddll.c
deleted file mode 100644
index c56c633b2f..0000000000
--- a/erts/emulator/sys/vxworks/erl_vxworks_sys_ddll.c
+++ /dev/null
@@ -1,253 +0,0 @@
-/*
- * %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%
- */
-
-/*
- * Interface functions to the dynamic linker using dl* functions.
- * (As far as I know it works on SunOS 4, 5, Linux and FreeBSD. /Seb)
- */
-
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-#include <vxWorks.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdarg.h>
-#include <a_out.h>
-#include <symLib.h>
-#include <loadLib.h>
-#include <unldLib.h>
-#include <moduleLib.h>
-#include <sysSymTbl.h>
-#include "sys.h"
-#include "global.h"
-#include "erl_alloc.h"
-#include "erl_driver.h"
-
-#define EXT_LEN 4
-#define FILE_EXT ".eld"
-#define ALT_FILE_EXT ".o"
-/* ALT_FILE_EXT must not be longer than FILE_EXT */
-#define DRIVER_INIT_SUFFIX "_init"
-
-static MODULE_ID get_mid(char *);
-static FUNCPTR lookup(char *);
-
-typedef enum {
- NoError,
- ModuleNotFound,
- ModuleNotUnloadable,
- UnknownError
-} FakeSytemError;
-
-static char *errcode_tab[] = {
- "No error",
- "Module/file not found",
- "Module cannot be unloaded",
- "Unknown error"
-};
-
-void erl_sys_ddll_init(void) {
- return;
-}
-/*
- * Open a shared object
- */
-int erts_sys_ddll_open2(char *full_name, void **handle, ErtsSysDdllError* err)
-{
- int len;
-
- if (erts_sys_ddll_open_noext(full_name, handle, err) == ERL_DE_NO_ERROR) {
- return ERL_DE_NO_ERROR;
- }
- if ((len = sys_strlen(full_name)) > PATH_MAX-EXT_LEN) {
- return ERL_DE_LOAD_ERROR_NAME_TO_LONG;
- } else {
- static char dlname[PATH_MAX + 1];
-
- sys_strcpy(dlname, full_name);
- sys_strcpy(dlname+len, FILE_EXT);
- if (erts_sys_ddll_open_noext(dlname, handle, err) == ERL_DE_NO_ERROR) {
- return ERL_DE_NO_ERROR;
- }
- sys_strcpy(dlname+len, ALT_FILE_EXT);
- return erts_sys_ddll_open_noext(dlname, handle, err);
- }
-}
-int erts_sys_ddll_open_noext(char *dlname, void **handle, ErtsSysDdllError* err)
-{
- MODULE_ID mid;
-
- if((mid = get_mid(dlname)) == NULL) {
- return ERL_DE_DYNAMIC_ERROR_OFFSET - ((int) ModuleNotFound);
- }
- *handle = (void *) mid;
- return ERL_DE_NO_ERROR;
-}
-
-/*
- * Find a symbol in the shared object
- */
-#define PREALLOC_BUFFER_SIZE 256
-int erts_sys_ddll_sym2(void *handle, char *func_name, void **function, ErtsSysDdllError* err)
-{
- FUNCPTR proc;
- static char statbuf[PREALLOC_BUFFER_SIZE];
- char *buf = statbuf;
- int need;
-
- if ((proc = lookup(func_name)) == NULL) {
- if ((need = strlen(func_name)+2) > PREALLOC_BUFFER_SIZE) {
- buf = erts_alloc(ERTS_ALC_T_DDLL_TMP_BUF,need);
- }
- buf[0] = '_';
- sys_strcpy(buf+1,func_name);
- proc = lookup(buf);
- if (buf != statbuf) {
- erts_free(ERTS_ALC_T_DDLL_TMP_BUF, buf);
- }
- if (proc == NULL) {
- return ERL_DE_LOOKUP_ERROR_NOT_FOUND;
- }
- }
- *function = (void *) proc;
- return ERL_DE_NO_ERROR;
-}
-
-/* XXX:PaN These two will be changed with new driver interface! */
-
-/*
- * Load the driver init function, might appear under different names depending on object arch...
- */
-
-int erts_sys_ddll_load_driver_init(void *handle, void **function)
-{
- MODULE_ID mid = (MODULE_ID) handle;
- char *modname;
- char *cp;
- static char statbuf[PREALLOC_BUFFER_SIZE];
- char *fname = statbuf;
- int len;
- int res;
- void *func;
- int need;
-
- if((modname = moduleNameGet(mid)) == NULL) {
- return ERL_DE_DYNAMIC_ERROR_OFFSET - ((int) ModuleNotFound);
- }
-
- if((cp = strrchr(modname, '.')) == NULL) {
- len = strlen(modname);
- } else {
- len = cp - modname;
- }
-
- need = len + strlen(DRIVER_INIT_SUFFIX) + 1;
- if (need > PREALLOC_BUFFER_SIZE) {
- fname = erts_alloc(ERTS_ALC_T_DDLL_TMP_BUF, need); /* erts_alloc exits on failure */
- }
- sys_strncpy(fname, modname, len);
- fname[len] = '\0';
- sys_strcat(fname, DRIVER_INIT_SUFFIX);
- res = erts_sys_ddll_sym(handle, fname, &func);
- if (fname != statbuf) {
- erts_free(ERTS_ALC_T_DDLL_TMP_BUF, fname);
- }
- if ( res != ERL_DE_NO_ERROR) {
- return res;
- }
- *function = func;
- return ERL_DE_NO_ERROR;
-}
-
-int erts_sys_ddll_load_nif_init(void *handle, void **function, ErtsSysDdllError* err)
-{
- /* NIFs not implemented for vxworks */
- return ERL_DE_ERROR_NO_DDLL_FUNCTIONALITY;
-}
-
-/*
- * Call the driver_init function, whatever it's really called, simple on unix...
-*/
-void *erts_sys_ddll_call_init(void *function) {
- void *(*initfn)(void) = function;
- return (*initfn)();
-}
-void *erts_sys_ddll_call_nif_init(void *function) {
- return erts_sys_ddll_call_init(function);
-}
-
-
-/*
- * Close a chared object
- */
-int erts_sys_ddll_close2(void *handle, ErtsSysDdllError* err)
-{
- MODULE_ID mid = (MODULE_ID) handle;
- if (unld(mid, 0) < 0) {
- return ERL_DE_DYNAMIC_ERROR_OFFSET - ((int) ModuleNotUnloadable);
- }
- return ERL_DE_NO_ERROR;
-}
-
-/*
- * Return string that describes the (current) error
- */
-char *erts_sys_ddll_error(int code)
-{
- int actual_code;
- if (code > ERL_DE_DYNAMIC_ERROR_OFFSET) {
- return "Unspecified error";
- }
- actual_code = -1*(code - ERL_DE_DYNAMIC_ERROR_OFFSET);
- if (actual_code > ((int) UnknownError)) {
- actual_code = UnknownError;
- }
- return errcode_tab[actual_code];
-}
-
-static FUNCPTR lookup(char *sym)
-{
- FUNCPTR entry;
- SYM_TYPE type;
-
- if (symFindByNameAndType(sysSymTbl, sym, (char **)&entry,
- &type, N_EXT | N_TEXT, N_EXT | N_TEXT) != OK) {
- return NULL ;
- }
- return entry;
-}
-
-static MODULE_ID get_mid(char* name)
-{
- int fd;
- MODULE_ID mid = NULL;
-
- if((fd = open(name, O_RDONLY, 0664)) >= 0) {
- mid = loadModule(fd, GLOBAL_SYMBOLS);
- close(fd);
- }
- return mid;
-}
-
-void erts_sys_ddll_free_error(ErtsSysDdllError* err)
-{
- /* NYI */
-}
-
diff --git a/erts/emulator/sys/vxworks/sys.c b/erts/emulator/sys/vxworks/sys.c
deleted file mode 100644
index 739b026fb1..0000000000
--- a/erts/emulator/sys/vxworks/sys.c
+++ /dev/null
@@ -1,2610 +0,0 @@
-/*
- * %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%
- */
-/*
- * system-dependent functions
- *
- */
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-#include <vxWorks.h>
-#include <version.h>
-#include <string.h>
-#include <types.h>
-#include <sigLib.h>
-#include <ioLib.h>
-#include <iosLib.h>
-#include <envLib.h>
-#include <fioLib.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <errno.h>
-#include <symLib.h>
-#include <sysLib.h>
-#include <sysSymTbl.h>
-#include <loadLib.h>
-#include <taskLib.h>
-#include <taskVarLib.h>
-#include <taskHookLib.h>
-#include <tickLib.h>
-#include <time.h>
-#include <rngLib.h>
-#include <semLib.h>
-#include <selectLib.h>
-#include <sockLib.h>
-#include <a_out.h>
-#include <wdLib.h>
-#include <timers.h>
-#include <ctype.h>
-#include <sys/stat.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <netinet/tcp.h>
-#include <stdarg.h>
-
-
-#ifndef WANT_NONBLOCKING
-#define WANT_NONBLOCKING
-#endif
-
-#include "sys.h"
-#include "erl_alloc.h"
-
-/* don't need global.h, but bif_table.h (included by bif.h) won't compile otherwise */
-#include "global.h"
-#include "bif.h"
-
-#include "erl_sys_driver.h"
-
-#include "elib_stat.h"
-
-#include "reclaim_private.h" /* Some more or less private reclaim facilities */
-
-#ifndef RETSIGTYPE
-#define RETSIGTYPE void
-#endif
-
-EXTERN_FUNCTION(void, erl_start, (int, char**));
-EXTERN_FUNCTION(void, erl_exit, (int n, char*, _DOTS_));
-EXTERN_FUNCTION(void, erl_error, (char*, va_list));
-EXTERN_FUNCTION(int, driver_interrupt, (int, int));
-EXTERN_FUNCTION(void, increment_time, (int));
-EXTERN_FUNCTION(int, erts_next_time, (_VOID_));
-EXTERN_FUNCTION(void, set_reclaim_free_function, (FreeFunction));
-EXTERN_FUNCTION(int, erl_mem_info_get, (MEM_PART_STATS *));
-EXTERN_FUNCTION(void, erl_crash_dump, (char* file, int line, char* fmt, ...));
-
-#define ISREG(st) (((st).st_mode&S_IFMT) == S_IFREG)
-
-/* these are defined in usrLib.c */
-extern int spTaskPriority, spTaskOptions;
-
-/* forward declarations */
-static FUNCTION(FUNCPTR, lookup, (char*));
-static FUNCTION(int, read_fill, (int, char*, int));
-#if (CPU == SPARC)
-static FUNCTION(RETSIGTYPE, fpe_sig_handler, (int)); /*where is this fun? */
-#elif (CPU == PPC603)
-static FUNCTION(void, fix_registers, (void));
-#endif
-static FUNCTION(void, close_pipes, (int*, int*, int));
-static FUNCTION(void, delete_hook, (void));
-static FUNCTION(void, initialize_allocation, (void));
-
-FUNCTION(STATUS, uxPipeDrv, (void));
-FUNCTION(STATUS, pipe, (int*));
-FUNCTION(void, uxPipeShow, (int));
-
-void erl_main(int argc, char **argv);
-void argcall(char *args);
-
-/* Malloc-realted functions called from the VxWorks shell */
-EXTERN_FUNCTION(int, erl_set_memory_block,
- (int, int, int, int, int, int, int, int, int, int));
-EXTERN_FUNCTION(int, erl_memory_show,
- (int, int, int, int, int, int, int, int, int, int));
-
-#define DEFAULT_PORT_STACK_SIZE 100000
-static int port_stack_size;
-
-static int erlang_id = 0; /* Inited at loading, set/reset at each run */
-
-/* interval time reported to emulator */
-static int sys_itime;
-
-/* XXX - This is defined in .../config/all/configAll.h (NUM_FILES),
- and not easily accessible at compile or run time - however,
- in VxWorks 5.1 it is stored in the (undocumented?) maxFiles variable;
- probably shouldn't depend on it, but we try to pick it up... */
-static int max_files = 50; /* default configAll.h */
-
-int erts_vxworks_max_files;
-
-/*
- * used by the break handler (set by signal handler on ctl-c)
- */
-volatile int erts_break_requested;
-
-/********************* General functions ****************************/
-
-/*
- * Reset the terminal to the original settings on exit
- * (nothing to do for WxWorks).
- */
-void sys_tty_reset(int exit_code)
-{
-}
-
-Uint
-erts_sys_misc_mem_sz(void)
-{
- Uint res = erts_check_io_size();
- /* res += FIXME */
- return res;
-}
-
-/*
- * XXX This declaration should not be here.
- */
-void erl_sys_schedule_loop(void);
-
-#ifdef SOFTDEBUG
-static void do_trace(int line, char *file, char *format, ...)
-{
- va_list va;
- int tid = taskIdSelf();
- char buff[512];
-
- va_start(va, format);
- sprintf(buff,"Trace: Task: 0x%08x, %s:%d - ",
- tid, file, line);
- vsprintf(buff + strlen(buff), format, va);
- va_end(va);
- strcat(buff,"\r\n");
- write(2,buff,strlen(buff));
-}
-
-#define TRACE() do_trace(__LINE__, __FILE__,"")
-#define TRACEF(Args...) do_trace(__LINE__,__FILE__, ## Args)
-#endif
-
-void
-erts_sys_pre_init(void)
-{
- if (erlang_id != 0) {
- /* NOTE: This particular case must *not* call erl_exit() */
- erts_fprintf(stderr, "Sorry, erlang is already running (as task %d)\n",
- erlang_id);
- exit(1);
- }
-
- /* This must be done as early as possible... */
- if(!reclaim_init())
- fprintf(stderr, "Warning : reclaim facility should be initiated before "
- "erlang is started!\n");
- erts_vxworks_max_files = max_files = reclaim_max_files();
-
- /* Floating point exceptions */
-#if (CPU == SPARC)
- sys_sigset(SIGFPE, fpe_sig_handler);
-#elif (CPU == PPC603)
- fix_registers();
-#endif
-
- /* register the private delete hook in reclaim */
- save_delete_hook((FUNCPTR)delete_hook, (caddr_t)0);
- erlang_id = taskIdSelf();
-#ifdef DEBUG
- printf("emulator task id = 0x%x\n", erlang_id);
-#endif
-}
-
-void erts_sys_alloc_init(void)
-{
- initialize_allocation();
-}
-
-void
-erl_sys_init(void)
-{
- setvbuf(stdout, (char *)NULL, _IOLBF, BUFSIZ);
- /* XXX Bug in VxWorks stdio loses fputch()'ed output after the
- setvbuf() but before a *printf(), and possibly worse (malloc
- errors, crash?) - so let's give it a *printf().... */
- fprintf(stdout, "%s","");
-}
-
-void
-erl_sys_args(int* argc, char** argv)
-{
- erts_init_check_io();
- max_files = erts_check_io_max_files();
- ASSERT(max_files <= erts_vxworks_max_files);
-}
-
-void
-erts_sys_schedule_interrupt(int set)
-{
- erts_check_io_interrupt(set);
-}
-
-/*
- * Called from schedule() when it runs out of runnable processes,
- * or when Erlang code has performed INPUT_REDUCTIONS reduction
- * steps. runnable == 0 iff there are no runnable Erlang processes.
- */
-void
-erl_sys_schedule(int runnable)
-{
- erts_check_io(!runnable);
-}
-
-void erts_do_break_handling(void)
-{
- SET_BLOCKING(0);
- /* call the break handling function, reset the flag */
- do_break();
- erts_break_requested = 0;
- SET_NONBLOCKING(0);
-}
-
-/* signal handling */
-RETSIGTYPE (*sys_sigset(sig, func))()
- int sig;
- RETSIGTYPE (*func)();
-{
- struct sigaction act, oact;
-
- sigemptyset(&act.sa_mask);
- act.sa_flags = 0;
- act.sa_handler = func;
- sigaction(sig, &act, &oact);
- return(oact.sa_handler);
-}
-
-void sys_sigblock(int sig)
-{
- sigset_t mask;
-
- sigemptyset(&mask);
- sigaddset(&mask, sig);
- sigprocmask(SIG_BLOCK, &mask, (sigset_t *)NULL);
-}
-
-void sys_sigrelease(int sig)
-{
- sigset_t mask;
-
- sigemptyset(&mask);
- sigaddset(&mask, sig);
- sigprocmask(SIG_UNBLOCK, &mask, (sigset_t *)NULL);
-}
-
-void
-erts_sys_prepare_crash_dump(void)
-{
-
-}
-
-/* register signal handlers XXX - they don't work, need to find out why... */
-/* set up signal handlers for break and quit */
-static void request_break(void)
-{
- /* just set a flag - checked for and handled
- * in main thread (not signal handler).
- * see check_io()
- */
-#ifdef DEBUG
- fprintf(stderr,"break!\n");
-#endif
- erts_break_requested = 1;
- erts_check_io_async_sig_interrupt(1); /* Make sure we don't sleep in erts_poll_wait */
-}
-
-static void do_quit(void)
-{
- halt_0(0);
-}
-
-void erts_set_ignore_break(void) {
-}
-
-void init_break_handler(void)
-{
- sys_sigset(SIGINT, request_break);
- sys_sigset(SIGQUIT, do_quit);
-}
-
-void erts_replace_intr(void) {
-}
-
-int sys_max_files(void)
-{
- return(max_files);
-}
-
-/******************* Routines for time measurement *********************/
-
-int sys_init_time(void)
-{
- erts_clock_rate = sysClkRateGet();
- /*
- ** One could imagine that it would be better returning
- ** a resolution more near the clock rate, like in:
- ** return 1000 / erts_clock_rate;
- ** but tests show that such isn't the case (rounding errors?)
- ** Well, we go for the Unix variant of returning 1
- ** as a constant virtual clock rate.
- */
- return SYS_CLOCK_RESOLUTION;
-}
-
-int erts_clock_rate;
-static volatile int ticks_inuse;
-static volatile unsigned long ticks_collected; /* will wrap */
-static WDOG_ID watchdog_id;
-static ULONG user_time;
-static int this_task_id, sys_itime;
-static SysHrTime hrtime_wrap;
-static unsigned long last_tick_count;
-
-static void tolerant_time_clockint(int count)
-{
- if (watchdog_id != NULL) {
- if (taskIsReady(this_task_id))
- user_time += 1;
- ++count;
- if (!ticks_inuse) {
- ticks_collected += count;
- count = 0;
- }
- wdStart(watchdog_id, 1, (FUNCPTR)tolerant_time_clockint, count);
- }
-}
-
-int sys_init_hrtime(void)
-{
- this_task_id = taskIdSelf(); /* OK, this only works for one single task
- in the system... */
- user_time = 0;
-
- ticks_inuse = 0;
- ticks_collected = 0;
- hrtime_wrap = 0;
- last_tick_count = 0;
-
- sys_itime = 1000 / erts_clock_rate;
- watchdog_id = wdCreate();
- wdStart(watchdog_id, 1, (FUNCPTR) tolerant_time_clockint, 0);
- return 0;
-}
-
-SysHrTime sys_gethrtime(void)
-{
- SysHrTime ticks;
-
- ++ticks_inuse;
- ticks = (SysHrTime) (ticks_collected & 0x7FFFFFFF);
- ticks_inuse = 0;
- if (ticks < (SysHrTime) last_tick_count) {
- hrtime_wrap += 1UL << 31;
- }
- last_tick_count = ticks;
- return (ticks + hrtime_wrap) * ((SysHrTime) (1000000000UL /
- erts_clock_rate));
-}
-
-void sys_gettimeofday(SysTimeval *tvp)
-{
- struct timespec now;
-
- clock_gettime(CLOCK_REALTIME, &now);
- tvp->tv_sec = now.tv_sec;
- tvp->tv_usec = now.tv_nsec / 1000;
-}
-
-clock_t sys_times(SysTimes *t)
-{
- t->tms_stime = t->tms_cutime = t->tms_cstime = 0;
- ++ticks_inuse;
- t->tms_utime = user_time;
- ticks_inuse = 0;
- return tickGet(); /* The best we can do... */
-}
-
-/* This is called when *this task* is deleted */
-static void delete_hook(void)
-{
- if (watchdog_id != NULL) {
- wdDelete(watchdog_id);
- watchdog_id = NULL;
- }
- erlang_id = 0;
- this_task_id = 0;
-}
-
-/************************** OS info *******************************/
-
-/* Used by erlang:info/1. */
-/* (This code was formerly in drv.XXX/XXX_os_drv.c) */
-
-#define MAX_VER_STR 9 /* Number of characters to
- consider in version string */
-
-static FUNCTION(int, get_number, (char** str_ptr));
-
-char os_type[] = "vxworks";
-
-static int
-get_number(char **str_ptr)
-{
- char* s = *str_ptr; /* Pointer to beginning of string. */
- char* dot; /* Pointer to dot in string or NULL. */
-
- if (!isdigit(*s))
- return 0;
- if ((dot = strchr(s, '.')) == NULL) {
- *str_ptr = s+strlen(s);
- return atoi(s);
- } else {
- *dot = '\0';
- *str_ptr = dot+1;
- return atoi(s);
- }
-}
-
-/* namebuf; Where to return the name. */
-/* size; Size of name buffer. */
-void
-os_flavor(char *namebuf, unsigned size)
-{
- strcpy(namebuf, "-");
-}
-
-/* int* pMajor; Pointer to major version. */
-/* int* pMinor; Pointer to minor version. */
-/* int* pBuild; Pointer to build number. */
-void
-os_version(int *pMajor, int *pMinor, int *pBuild)
-{
- char os_ver[MAX_VER_STR+2];
- char* release; /* Pointer to the release string:
- * X.Y or X.Y.Z.
- */
- strncpy(os_ver, vxWorksVersion, MAX_VER_STR);
- release = os_ver;
- *pMajor = get_number(&release);
- *pMinor = get_number(&release);
- *pBuild = get_number(&release);
-}
-
-void init_getenv_state(GETENV_STATE *state)
-{
- *state = NULL;
-}
-
-char *getenv_string(GETENV_STATE *state0)
-{
- return NULL;
-}
-
-void fini_getenv_state(GETENV_STATE *state)
-{
- *state = NULL;
-}
-
-/************************** Port I/O *******************************/
-
-
-/* I. Common stuff */
-
-#define TMP_BUF_MAX (tmp_buf_size - 1024)
-static byte *tmp_buf;
-static Uint tmp_buf_size;
-
-/* II. The spawn/fd/vanilla drivers */
-
-/* This data is shared by these drivers - initialized by spawn_init() */
-static struct driver_data {
- int port_num, ofd, packet_bytes, report_exit;
- int exitcode, exit_reported; /* For returning of exit codes. */
-} *driver_data; /* indexed by fd */
-
-/*
- * Locking only for exitcodes and exit_reported, one global sem for all
- * spawn ports as this is rare.
- */
-static SEM_ID driver_data_sem = NULL;
-/*
- * Also locking when looking up entries in the load table
- */
-static SEM_ID entry_data_sem = NULL;
-
-/* We maintain a linked fifo queue of these structs in order */
-/* to manage unfinnished reads/and writes on differenet fd's */
-
-typedef struct pend {
- char *cpos;
- int fd;
- int remain;
- struct pend *next;
- char buf[1]; /* this is a trick to be able to malloc one chunk */
-} Pend;
-
-static struct fd_data {
- int inport, outport;
- char *buf, *cpos;
- int sz, remain; /* for input on fd */
- Pend* pending; /* pending outputs */
-
-} *fd_data; /* indexed by fd */
-
-
-/* Driver interfaces */
-static ErlDrvData spawn_start(ErlDrvPort port_num, char *name, SysDriverOpts* opts);
-static ErlDrvData fd_start(ErlDrvPort port_num, char *name, SysDriverOpts* opts);
-static ErlDrvData vanilla_start(ErlDrvPort port_num, char *name, SysDriverOpts* opts);
-static int spawn_init(void);
-static void fd_stop(ErlDrvData);
-static void stop(ErlDrvData);
-static void ready_input(ErlDrvData fd, ErlDrvEvent ready_fd);
-static void ready_output(ErlDrvData fd, ErlDrvEvent ready_fd);
-static void output(ErlDrvData fd, char *buf, ErlDrvSizeT len);
-static void stop_select(ErlDrvEvent, void*);
-
-struct erl_drv_entry spawn_driver_entry = {
- spawn_init,
- spawn_start,
- stop,
- output,
- ready_input,
- ready_output,
- "spawn",
- NULL, /* finish */
- NULL, /* handle */
- NULL, /* control */
- NULL, /* timeout */
- NULL, /* outputv */
- NULL, /* ready_async */
- NULL, /* flush */
- NULL, /* call */
- NULL, /* event */
- ERL_DRV_EXTENDED_MARKER,
- ERL_DRV_EXTENDED_MAJOR_VERSION,
- ERL_DRV_EXTENDED_MINOR_VERSION,
- 0, /* ERL_DRV_FLAGs */
- NULL, /* handle2 */
- NULL, /* process_exit */
- stop_select
-
-};
-struct erl_drv_entry fd_driver_entry = {
- NULL,
- fd_start,
- fd_stop,
- output,
- ready_input,
- ready_output,
- "fd",
- NULL, /* finish */
- NULL, /* handle */
- NULL, /* control */
- NULL, /* timeout */
- NULL, /* outputv */
- NULL, /* ready_async */
- NULL, /* flush */
- NULL, /* call */
- NULL, /* event */
- ERL_DRV_EXTENDED_MARKER,
- ERL_DRV_EXTENDED_MAJOR_VERSION,
- ERL_DRV_EXTENDED_MINOR_VERSION,
- 0, /* ERL_DRV_FLAGs */
- NULL, /* handle2 */
- NULL, /* process_exit */
- stop_select
-};
-struct erl_drv_entry vanilla_driver_entry = {
- NULL,
- vanilla_start,
- stop,
- output,
- ready_input,
- ready_output,
- "vanilla",
- NULL, /* finish */
- NULL, /* handle */
- NULL, /* control */
- NULL, /* timeout */
- NULL, /* outputv */
- NULL, /* ready_async */
- NULL, /* flush */
- NULL, /* call */
- NULL, /* event */
- ERL_DRV_EXTENDED_MARKER,
- ERL_DRV_EXTENDED_MAJOR_VERSION,
- ERL_DRV_EXTENDED_MINOR_VERSION,
- 0, /* ERL_DRV_FLAGs */
- NULL, /* handle2 */
- NULL, /* process_exit */
- stop_select
-};
-
-/*
-** Set up enough of the driver_data structure to be able to report exit status.
-** Some things may be initiated again, but that is no real problem.
-*/
-static int pre_set_driver_data(int ifd, int ofd,
- int read_write, int report_exit) {
- if (read_write & DO_READ) {
- driver_data[ifd].report_exit = report_exit;
- driver_data[ifd].exitcode = 0;
- driver_data[ifd].exit_reported = 0;
- if (read_write & DO_WRITE) {
- driver_data[ifd].ofd = ofd;
- if (ifd != ofd) {
- driver_data[ofd] = driver_data[ifd];
- driver_data[ofd].report_exit = 0;
- }
- } else { /* DO_READ only */
- driver_data[ifd].ofd = -1;
- }
- return(ifd);
- } else { /* DO_WRITE only */
- driver_data[ofd].report_exit = 0;
- driver_data[ofd].exitcode = 0;
- driver_data[ofd].exit_reported = 0;
- driver_data[ofd].ofd = ofd;
- return(ofd);
- }
-}
-
-/*
-** Set up the driver_data structure, it may have been initiated
-** partly by the function above, but we dont care.
-*/
-static int set_driver_data(int port_num, int ifd, int ofd,
- int packet_bytes, int read_write,
- int report_exit)
-{
- if (read_write & DO_READ) {
- driver_data[ifd].packet_bytes = packet_bytes;
- driver_data[ifd].port_num = port_num;
- driver_data[ifd].report_exit = report_exit;
- if (read_write & DO_WRITE) {
- driver_data[ifd].ofd = ofd;
- if (ifd != ofd) {
- driver_data[ofd] = driver_data[ifd];
- driver_data[ofd].report_exit = 0;
- }
- } else { /* DO_READ only */
- driver_data[ifd].ofd = -1;
- }
- (void) driver_select(port_num, ifd, ERL_DRV_READ|ERL_DRV_USE, 1);
- return(ifd);
- } else { /* DO_WRITE only */
- driver_data[ofd].packet_bytes = packet_bytes;
- driver_data[ofd].port_num = port_num;
- driver_data[ofd].report_exit = 0;
- driver_data[ofd].ofd = ofd;
- return(ofd);
- }
-}
-
-static int need_new_sems = 1;
-
-static int spawn_init(void)
-{
- char *stackenv;
- int size;
- driver_data = (struct driver_data *)
- erts_alloc(ERTS_ALC_T_DRV_TAB, max_files * sizeof(struct driver_data));
- if (need_new_sems) {
- driver_data_sem = semMCreate
- (SEM_Q_PRIORITY | SEM_DELETE_SAFE | SEM_INVERSION_SAFE);
- entry_data_sem = semMCreate
- (SEM_Q_PRIORITY | SEM_DELETE_SAFE | SEM_INVERSION_SAFE);
- }
- if (driver_data_sem == NULL || entry_data_sem == NULL) {
- erl_exit(1,"Could not allocate driver locking semaphore.");
- }
- need_new_sems = 0;
-
- (void)uxPipeDrv(); /* Install pipe driver */
-
- if ((stackenv = getenv("ERLPORTSTACKSIZE")) != NULL &&
- (size = atoi(stackenv)) > 0)
- port_stack_size = size;
- else
- port_stack_size = DEFAULT_PORT_STACK_SIZE;
- return 0;
-}
-
-/* Argv has to be built vith the save_xxx routines, not with whathever
- sys_xxx2 has in mind... */
-#define argv_alloc save_malloc
-#define argv_realloc save_realloc
-#define argv_free save_free
-/* Build argv, return argc or -1 on failure */
-static int build_argv(char *name, char ***argvp)
-{
- int argvsize = 10, argc = 0;
- char *args, *arglast = NULL, *argp;
- char **argv;
-
-#ifdef DEBUG
- fdprintf(2, "Building argv, %s =>\n", name);
-#endif
- if ((argv = (char **)argv_alloc(argvsize * sizeof(char *))) == NULL)
- return(-1);
- if ((args = argv_alloc(strlen(name) + 1)) == NULL)
- return(-1);
- strcpy(args, name);
- argp = strtok_r(args, " \t", &arglast);
- while (argp != NULL) {
- if (argc + 1 >= argvsize) {
- argvsize += 10;
- argv = (char **)argv_realloc((char *)argv, argvsize*sizeof(char *));
- if (argv == NULL) {
- argv_free(args);
- return(-1);
- }
- }
-#ifdef DEBUG
- fdprintf(2, "%s\n", argp);
-#endif
- argv[argc++] = argp;
- argp = strtok_r((char *)NULL, " \t", &arglast);
- }
- argv[argc] = NULL;
- *argvp = argv;
- return(argc);
-}
-#undef argv_alloc
-#undef argv_realloc
-#undef argv_free
-
-
-/* Lookup and return global text symbol or NULL on failure
- Symbol name is null-terminated and without the leading '_' */
-static FUNCPTR
-lookup(char *sym)
-{
- char buf[256];
- char *symname = buf;
- int len;
- FUNCPTR entry;
- SYM_TYPE type;
-
- len = strlen(sym);
- if (len > 254 && (symname = malloc(len+2)) == NULL)
- return(NULL);
-#if defined _ARCH_PPC || defined SIMSPARCSOLARIS
- /* GCC for PPC and SIMSPARC doesn't add a leading _ to symbols */
- strcpy(symname, sym);
-#else
- sprintf(symname, "_%s", sym);
-#endif
- if (symFindByNameAndType(sysSymTbl, symname, (char **)&entry,
- &type, N_EXT | N_TEXT, N_EXT | N_TEXT) != OK)
- entry = NULL;
- if (symname != buf)
- free(symname);
- return(entry);
-}
-
-/* This function is spawned to build argc, argv, lookup the symbol to call,
- connect and set up file descriptors, and make the actual call.
- N.B. 'name' was allocated by the Erlang task (through plain_malloc) and
- is freed by this port program task.
- Note: 'name' may be a path containing '/'. */
-
-static void call_proc(char *name, int ifd, int ofd, int read_write,
- int redir_stderr, int driver_index,
- int p6, int p7, int p8, int p9)
-{
- int argc;
- char **argv, *bname;
- FUNCPTR entry;
- int ret = -1;
-
- /* Must consume 'name' */
- argc = build_argv(name, &argv);
- plain_free(name);
- /* Find basename of path */
- if ((bname = strrchr(argv[0], '/')) != NULL) {
- bname++;
- } else {
- bname = argv[0];
- }
-#ifdef DEBUG
- fdprintf(2, "Port program name: %s\n", bname);
-#endif
- semTake(entry_data_sem, WAIT_FOREVER);
-
- if (argc > 0) {
- if ((entry = lookup(bname)) == NULL) {
- int fd;
- char *fn;
- /* NOTE: We don't check the return value of loadModule,
- since that was incompatibly changed from 5.0.2b to 5.1,
- but rather do a repeated lookup(). */
- if ((fd = open(argv[0], O_RDONLY)) > 0) {
- (void) loadModule(fd, GLOBAL_SYMBOLS);
- close(fd);
- entry = lookup(bname);
- }
- if (entry == NULL) {
- /* filename == func failed, try func.o */
- if ((fn = malloc(strlen(argv[0]) + 3)) != NULL) { /* ".o\0" */
- strcpy(fn, argv[0]);
- strcat(fn, ".o");
- if ((fd = open(fn, O_RDONLY)) > 0) {
- (void) loadModule(fd, GLOBAL_SYMBOLS);
- close(fd);
- entry = lookup(bname);
- }
- free(fn);
- }
- }
- }
- } else {
- entry = NULL;
- }
- semGive(entry_data_sem);
-
- if (read_write & DO_READ) { /* emulator read */
- save_fd(ofd);
- ioTaskStdSet(0, 1, ofd); /* stdout for process */
- if(redir_stderr)
- ioTaskStdSet(0, 2, ofd);/* stderr for process */
- }
- if (read_write & DO_WRITE) { /* emulator write */
- save_fd(ifd);
- ioTaskStdSet(0, 0, ifd); /* stdin for process */
- }
- if (entry != NULL) {
- ret = (*entry)(argc, argv, (char **)NULL); /* NULL for envp */
- } else {
- fdprintf(2, "Could not exec \"%s\"\n", argv[0]);
- ret = -1;
- }
- if (driver_data[driver_index].report_exit) {
- semTake(driver_data_sem, WAIT_FOREVER);
- driver_data[driver_index].exitcode = ret;
- driver_data[driver_index].exit_reported = 1;
- semGive(driver_data_sem);
- }
- /* We *don't* want to close the pipes here, but let the delete
- hook take care of it - it might want to flush stdout and there'd
- better be an open descriptor to flush to... */
- exit(ret);
-}
-
-static void close_pipes(int ifd[2], int ofd[2], int read_write)
-{
- if (read_write & DO_READ) {
- (void) close(ifd[0]);
- (void) close(ifd[1]);
- }
- if (read_write & DO_WRITE) {
- (void) close(ofd[0]);
- (void) close(ofd[1]);
- }
-}
-
-static void init_fd_data(int fd, int port_unused_argument)
-{
- SET_NONBLOCKING(fd);
- fd_data[fd].pending = NULL;
- fd_data[fd].buf = fd_data[fd].cpos = NULL;
- fd_data[fd].remain = fd_data[fd].sz = 0;
-}
-
-static ErlDrvData spawn_start(ErlDrvPort port_num, char *name,SysDriverOpts* opts)
-{
- int ifd[2], ofd[2], len, nl, id;
- char taskname[11], *progname, *bname;
- char *space_in_command;
- int packet_bytes = opts->packet_bytes;
- int read_write = opts->read_write;
- int use_stdio = opts->use_stdio;
- int redir_stderr = opts->redir_stderr;
- int driver_index;
-
- if (!use_stdio){
- return (ErlDrvData) -3;
- }
-
- /* Create pipes and set the Erlang task as owner of its
- * read and write ends (through save_fd()).
- */
- switch (read_write) {
- case DO_READ:
- if (pipe(ifd) < 0){
- return (ErlDrvData) -2;
- }
- if (ifd[0] >= max_files) {
- close_pipes(ifd, ofd, read_write);
- errno = ENFILE;
- return (ErlDrvData) -2;
- }
- save_fd(ifd[0]);
- break;
- case DO_WRITE:
- if (pipe(ofd) < 0) {
- return (ErlDrvData) -2;
- }
- if (ofd[1] >= max_files) {
- close_pipes(ifd, ofd, read_write);
- errno = ENFILE;
- return (ErlDrvData) -2;
- }
- save_fd(ofd[1]);
- break;
- case DO_READ|DO_WRITE:
- if (pipe(ifd) < 0){
- return (ErlDrvData) -2;
- }
- if (ifd[0] >= max_files || pipe(ofd) < 0) {
- close_pipes(ifd, ofd, DO_READ);
- errno = ENFILE;
- return (ErlDrvData) -2;
- }
- if (ofd[1] >= max_files) {
- close_pipes(ifd, ofd, read_write);
- errno = ENFILE;
- return (ErlDrvData) -2;
- }
- save_fd(ifd[0]);
- save_fd(ofd[1]);
- break;
- default:
- return (ErlDrvData) -1;
- }
-
- /* Allocate space for program name to be freed by the
- * spawned task. We use plain_malloc so that the allocated
- * space is not owned by the Erlang task.
- */
-
- if ((progname = plain_malloc(strlen(name) + 1)) == NULL) {
- close_pipes(ifd, ofd, read_write);
- errno = ENOMEM;
- return (ErlDrvData) -2;
- }
- strcpy(progname, name);
-
- /* Check if name contains a space
- * (e.g "port_test -o/home/gandalf/tornado/wind/target/erlang")
- */
- if ((space_in_command = strrchr(progname, ' ')) != NULL) {
- *space_in_command = '\0';
- }
-
- /* resulting in "port_test" */
- if ((bname = strrchr(progname, '/')) != NULL)
- bname++;
- else
- bname = progname;
-
- /* resulting in "port_test" */
- len = strlen(bname);
- nl = len > 10 ? 10 : len;
- strncpy(taskname, bname, nl);
- taskname[nl] = '\0';
- if (space_in_command != NULL)
- *space_in_command = ' ';
- driver_index = pre_set_driver_data(ifd[0], ofd[1],
- read_write, opts->exit_status);
-
- /* resetting to "port_test -o/home/gandalf/tornado/wind/target/erlang" */
- if ((id = taskSpawn(taskname, spTaskPriority, spTaskOptions,
- port_stack_size, (FUNCPTR)call_proc, (int)progname,
- ofd[0], ifd[1], read_write, redir_stderr, driver_index,
- 0,0,0,0))
- == ERROR) {
- close_pipes(ifd, ofd, read_write);
- plain_free(progname); /* only when spawn fails */
- errno = ENOMEM;
- return (ErlDrvData) -2;
- }
-#ifdef DEBUG
- fdprintf(2, "Spawned %s as %s[0x%x]\n", name, taskname, id);
-#endif
- if (read_write & DO_READ)
- init_fd_data(ifd[0], port_num);
- if (read_write & DO_WRITE)
- init_fd_data(ofd[1], port_num);
- return (ErlDrvData) (set_driver_data(port_num, ifd[0], ofd[1],
- packet_bytes,read_write,
- opts->exit_status));
-}
-
-static ErlDrvData fd_start(ErlDrvPort port_num, char *name, SysDriverOpts* opts)
-{
- if (((opts->read_write & DO_READ) && opts->ifd >= max_files) ||
- ((opts->read_write & DO_WRITE) && opts->ofd >= max_files)) {
- return (ErlDrvData) -1;
- }
-
- if (opts->read_write & DO_READ)
- init_fd_data(opts->ifd, port_num);
- if (opts->read_write & DO_WRITE)
- init_fd_data(opts->ofd, port_num);
- return (ErlDrvData) (set_driver_data(port_num, opts->ifd, opts->ofd,
- opts->packet_bytes, opts->read_write, 0));
-}
-
-static void clear_fd_data(int fd)
-{
-
- if (fd_data[fd].sz > 0)
- erts_free(ERTS_ALC_T_FD_ENTRY_BUF, (void *) fd_data[fd].buf);
- fd_data[fd].buf = NULL;
- fd_data[fd].sz = 0;
- fd_data[fd].remain = 0;
- fd_data[fd].cpos = NULL;
-}
-
-static void nbio_stop_fd(int port_num, int fd)
-{
- Pend *p, *p1;
-
- driver_select(port_num, fd, ERL_DRV_READ|ERL_DRV_WRITE, 0);
- clear_fd_data(fd);
- p = fd_data[fd].pending;
- SET_BLOCKING(fd);
- while (p) {
- p1 = p->next;
- free(p);
- p = p1;
- }
- fd_data[fd].pending = NULL;
-}
-
-static void fd_stop(ErlDrvData drv_data)
-{
- int ofd;
- int fd = (int) drv_data;
-
- nbio_stop_fd(driver_data[fd].port_num, (int)fd);
- ofd = driver_data[fd].ofd;
- if (ofd != fd && ofd != -1)
- nbio_stop_fd(driver_data[fd].port_num, (int)ofd); /* XXX fd = ofd? */
-}
-
-static ErlDrvData
-vanilla_start(ErlDrvPort port_num, char *name, SysDriverOpts* opts)
-{
- int flags, fd;
- struct stat statbuf;
-
- DEBUGF(("vanilla_start, name: %s [r=%1i w=%1i]\n", name,
- opts->read_write & DO_READ,
- opts->read_write & DO_WRITE));
-
- flags = (opts->read_write == DO_READ ? O_RDONLY :
- opts->read_write == DO_WRITE ? O_WRONLY|O_CREAT|O_TRUNC :
- O_RDWR|O_CREAT);
- if ((fd = open(name, flags, 0666)) < 0){
- errno = ENFILE;
- return (ErlDrvData) -2;
- }
- if (fd >= max_files) {
- close(fd);
- errno = ENFILE;
- return (ErlDrvData) -2;
- }
- if (fstat(fd, &statbuf) < 0) {
- close(fd);
- errno = ENFILE;
- return (ErlDrvData) -2;
- }
-
- /* Return error for reading regular files (doesn't work) */
- if (ISREG(statbuf) && ((opts->read_write) & DO_READ)) {
- close(fd);
- return (ErlDrvData) -3;
- }
- init_fd_data(fd, port_num);
- return (ErlDrvData) (set_driver_data(port_num, fd, fd,
- opts->packet_bytes, opts->read_write, 0));
-}
-
-/* Note that driver_data[fd].ifd == fd if the port was opened for reading, */
-/* otherwise (i.e. write only) driver_data[fd].ofd = fd. */
-
-static void stop(ErlDrvData drv_data)
-{
- int port_num, ofd;
- int fd = (int) drv_data;
-
- port_num = driver_data[fd].port_num;
- nbio_stop_fd(port_num, fd);
- driver_select(port_num, fd, ERL_DRV_USE, 0); /* close(fd) */
-
- ofd = driver_data[fd].ofd;
- if (ofd != fd && ofd != -1) {
- nbio_stop_fd(port_num, ofd);
- driver_select(port_num, ofd, ERL_DRV_USE, 0); /* close(fd) */
- }
-}
-
-static int sched_write(int port_num,int fd, char *buf, int len, int pb)
-{
- Pend *p, *p2, *p3;
- int p_bytes = len;
-
- p = (Pend*) erts_alloc_fnf(ERTS_ALC_T_PEND_DATA, pb + len + sizeof(Pend));
- if (!p) {
- driver_failure(port_num, -1);
- return(-1);
- }
-
- switch(pb) {
- case 4: put_int32(len, p->buf); break;
- case 2: put_int16(len, p->buf); break;
- case 1: put_int8(len, p->buf); break;
- case 0: break; /* Handles this case too */
- }
- sys_memcpy(p->buf + pb, buf, len);
- driver_select(port_num, fd, ERL_DRV_WRITE|ERL_DRV_USE, 1);
- p->cpos = p->buf;
- p->fd = fd;
- p->next = NULL;
- p->remain = len + pb;
- p2 = fd_data[fd].pending;
- if (p2 == NULL)
- fd_data[fd].pending = p;
- else {
- p3 = p2->next;
- while(p3) {
- p_bytes += p2->remain;
- p2 = p2->next;
- p3 = p3->next;
- }
- p2->next = p;
- }
- if (p_bytes > (1 << 13)) /* More than 8 k pending */
- set_busy_port(port_num, 1);
- return(0);
-}
-
-/* Fd is the value returned as drv_data by the start func */
-static void output(ErlDrvData drv_data, char *buf, ErlDrvSizeT len)
-{
- int buf_done, port_num, wval, pb, ofd;
- byte lb[4];
- struct iovec iv[2];
- int fd = (int) drv_data;
-
- pb = driver_data[fd].packet_bytes;
- port_num = driver_data[fd].port_num;
-
- if ((ofd = driver_data[fd].ofd) == -1) {
- return;
- }
-
- if (fd_data[ofd].pending) {
- sched_write(port_num, ofd, buf, len, pb);
- return;
- }
-
- if ((pb == 2 && len > 65535) || (pb == 1 && len > 255)) {
- driver_failure_posix(port_num, EINVAL);
- return;
- }
- if (pb == 0) {
- wval = write(ofd, buf, len);
- } else {
- lb[0] = (len >> 24) & 255; /* MSB */
- lb[1] = (len >> 16) & 255;
- lb[2] = (len >> 8) & 255;
- lb[3] = len & 255; /* LSB */
- iv[0].iov_base = (char*) lb + (4 - pb);
- iv[0].iov_len = pb;
- iv[1].iov_base = buf;
- iv[1].iov_len = len;
- wval = writev(ofd, iv, 2);
- }
- if (wval == pb + len ) {
- return;
- }
- if (wval < 0) {
- if ((errno == EINTR) || (errno == ERRNO_BLOCK)) {
- if (pb) {
- sched_write(port_num, ofd, buf ,len, pb);
- } else if (pb == 0) {
- sched_write(port_num, ofd, buf ,len, 0);
- }
- return;
- }
- driver_failure_posix(driver_data[fd].port_num, EINVAL);
- return;
- }
- if (wval < pb) {
- sched_write(port_num, ofd, (lb +4 -pb) + wval, pb-wval, 0);
- sched_write(port_num, ofd, buf ,len, 0);
- return;
- }
-
- /* we now know that wval < (pb + len) */
- buf_done = wval - pb;
- sched_write(port_num, ofd, buf + buf_done, len - buf_done,0);
-}
-
-static void stop_select(ErlDrvEvent fd, void* _)
-{
- close((int)fd);
-}
-
-static int ensure_header(int fd,char *buf,int packet_size, int sofar)
-{
- int res = 0;
- int remaining = packet_size - sofar;
-
- SET_BLOCKING(fd);
- if (read_fill(fd, buf+sofar, remaining) != remaining)
- return -1;
- switch (packet_size) {
- case 1: res = get_int8(buf); break;
- case 2: res = get_int16(buf); break;
- case 4: res = get_int32(buf); break;
- }
- SET_NONBLOCKING(fd);
- return(res);
-}
-
-static int port_inp_failure(int port_num, int ready_fd, int res)
-{
- (void) driver_select(port_num, ready_fd, ERL_DRV_READ|ERL_DRV_WRITE, 0);
- clear_fd_data(ready_fd);
- if (res == 0) {
- if (driver_data[ready_fd].report_exit) {
- int tmpexit = 0;
- int reported;
- /* Lock the driver_data structure */
- semTake(driver_data_sem, WAIT_FOREVER);
- if ((reported = driver_data[ready_fd].exit_reported))
- tmpexit = driver_data[ready_fd].exitcode;
- semGive(driver_data_sem);
- if (reported) {
- erts_fprintf(stderr,"Exitcode %d reported\r\n", tmpexit);
- driver_report_exit(port_num, tmpexit);
- }
- }
- driver_failure_eof(port_num);
- } else {
- driver_failure(port_num, res);
- }
- return 0;
-}
-
-/* fd is the drv_data that is returned from the */
-/* initial start routine */
-/* ready_fd is the descriptor that is ready to read */
-
-static void ready_input(ErlDrvData drv_data, ErlDrvEvent drv_event)
-{
- int port_num, packet_bytes, res;
- Uint h = 0;
- char *buf;
- int fd = (int) drv_data;
- int ready_fd = (int) drv_event;
-
- port_num = driver_data[fd].port_num;
- packet_bytes = driver_data[fd].packet_bytes;
-
- if (packet_bytes == 0) {
- if ((res = read(ready_fd, tmp_buf, tmp_buf_size)) > 0) {
- driver_output(port_num, (char*)tmp_buf, res);
- return;
- }
- port_inp_failure(port_num, ready_fd, res);
- return;
- }
-
- if (fd_data[ready_fd].remain > 0) { /* We try to read the remainder */
- /* space is allocated in buf */
- res = read(ready_fd, fd_data[ready_fd].cpos,
- fd_data[ready_fd].remain);
- if (res < 0) {
- if ((errno == EINTR) || (errno == ERRNO_BLOCK)) {
- ;
- } else {
- port_inp_failure(port_num, ready_fd, res);
- }
- } else if (res == 0) {
- port_inp_failure(port_num, ready_fd, res);
- } else if (res == fd_data[ready_fd].remain) { /* we're done */
- driver_output(port_num, fd_data[ready_fd].buf,
- fd_data[ready_fd].sz);
- clear_fd_data(ready_fd);
- } else { /* if (res < fd_data[ready_fd].remain) */
- fd_data[ready_fd].cpos += res;
- fd_data[ready_fd].remain -= res;
- }
- return;
- }
-
-
- if (fd_data[ready_fd].remain == 0) { /* clean fd */
- /* We make one read attempt and see what happens */
- res = read(ready_fd, tmp_buf, tmp_buf_size);
- if (res < 0) {
- if ((errno == EINTR) || (errno == ERRNO_BLOCK))
- return;
- port_inp_failure(port_num, ready_fd, res);
- return;
- }
- else if (res == 0) { /* eof */
- port_inp_failure(port_num, ready_fd, res);
- return;
- }
- else if (res < packet_bytes) { /* Ugly case... get at least */
- if ((h = ensure_header(ready_fd, tmp_buf, packet_bytes, res))==-1) {
- port_inp_failure(port_num, ready_fd, -1);
- return;
- }
- buf = erts_alloc_fnf(ERTS_ALC_T_FD_ENTRY_BUF, h);
- if (!buf) {
- port_inp_failure(port_num, ready_fd, -1);
- return;
- }
- fd_data[ready_fd].buf = buf;
- fd_data[ready_fd].sz = h;
- fd_data[ready_fd].remain = h;
- fd_data[ready_fd].cpos = buf;
- return;
- }
- else { /* if (res >= packet_bytes) */
- unsigned char* cpos = tmp_buf;
- int bytes_left = res;
- while (1) { /* driver_output as many as possible */
- if (bytes_left == 0) {
- clear_fd_data(ready_fd);
- return;
- }
- if (bytes_left < packet_bytes) { /* Yet an ugly case */
- if((h=ensure_header(ready_fd, cpos,
- packet_bytes, bytes_left))==-1) {
- port_inp_failure(port_num, ready_fd, -1);
- return;
- }
- buf = erts_alloc_fnf(ERTS_ALC_T_FD_ENTRY_BUF, h);
- if (!buf)
- port_inp_failure(port_num, ready_fd, -1);
- fd_data[ready_fd].buf = buf;
- fd_data[ready_fd].sz = h;
- fd_data[ready_fd].remain = h;
- fd_data[ready_fd].cpos = buf;
- return;
- }
- switch (packet_bytes) {
- case 1: h = get_int8(cpos); cpos += 1; break;
- case 2: h = get_int16(cpos); cpos += 2; break;
- case 4: h = get_int32(cpos); cpos += 4; break;
- }
- bytes_left -= packet_bytes;
- /* we've got the header, now check if we've got the data */
- if (h <= (bytes_left)) {
- driver_output(port_num, (char*) cpos, h);
- cpos += h;
- bytes_left -= h;
- continue;
- }
- else { /* The last message we got was split */
- buf = erts_alloc_fnf(ERTS_ALC_T_FD_ENTRY_BUF, h);
- if (!buf) {
- port_inp_failure(port_num, ready_fd, -1);
- }
- sys_memcpy(buf, cpos, bytes_left);
- fd_data[ready_fd].buf = buf;
- fd_data[ready_fd].sz = h;
- fd_data[ready_fd].remain = h - bytes_left;
- fd_data[ready_fd].cpos = buf + bytes_left;
- return;
- }
- }
- return;
- }
- }
- fprintf(stderr, "remain %d \n", fd_data[ready_fd].remain);
- port_inp_failure(port_num, ready_fd, -1);
-}
-
-
-/* fd is the drv_data that is returned from the */
-/* initial start routine */
-/* ready_fd is the descriptor that is ready to read */
-
-static void ready_output(ErlDrvData drv_data, ErlDrvEvent drv_event)
-{
- Pend *p;
- int wval;
-
- int fd = (int) drv_data;
- int ready_fd = (int) drv_event;
-
- while(1) {
- if ((p = fd_data[ready_fd].pending) == NULL) {
- driver_select(driver_data[fd].port_num, ready_fd,
- ERL_DRV_WRITE, 0);
- return;
- }
- wval = write(p->fd, p->cpos, p->remain);
- if (wval == p->remain) {
- fd_data[ready_fd].pending = p->next;
- erts_free(ERTS_ALC_T_PEND_DATA, p);
- if (fd_data[ready_fd].pending == NULL) {
- driver_select(driver_data[fd].port_num, ready_fd,
- ERL_DRV_WRITE, 0);
- set_busy_port(driver_data[fd].port_num, 0);
- return;
- }
- else
- continue;
- }
- else if (wval < 0) {
- if (errno == ERRNO_BLOCK || errno == EINTR)
- return;
- else {
- driver_select(driver_data[fd].port_num, ready_fd,
- ERL_DRV_WRITE, 0);
- driver_failure(driver_data[fd].port_num, -1);
- return;
- }
- }
- else if (wval < p->remain) {
- p->cpos += wval;
- p->remain -= wval;
- return;
- }
- }
-}
-
-/* Fills in the systems representation of the jam/beam process identifier.
-** The Pid is put in STRING representation in the supplied buffer,
-** no interpretatione of this should be done by the rest of the
-** emulator. The buffer should be at least 21 bytes long.
-*/
-void sys_get_pid(char *buffer){
- int p = taskIdSelf(); /* Hmm, may be negative??? requires some GB of
- memory to make the TCB address convert to a
- negative value. */
- sprintf(buffer,"%d", p);
-}
-
-int
-erts_sys_putenv(char *buffer, int sep_ix)
-{
- return putenv(buffer);
-}
-
-int
-erts_sys_getenv(char *key, char *value, size_t *size)
-{
- char *orig_value;
- int res;
- orig_value = getenv(key);
- if (!orig_value)
- res = -1;
- else {
- size_t len = sys_strlen(orig_value);
- if (len >= *size) {
- *size = len + 1;
- res = 1;
- }
- else {
- *size = len;
- sys_memcpy((void *) value, (void *) orig_value, len+1);
- res = 0;
- }
- }
- return res;
-}
-
-int
-erts_sys_getenv__(char *key, char *value, size_t *size)
-{
- return erts_sys_getenv(key, value, size);
-}
-
-void
-sys_init_io(void)
-{
- tmp_buf = (byte *) erts_alloc(ERTS_ALC_T_SYS_TMP_BUF, SYS_TMP_BUF_SIZE);
- tmp_buf_size = SYS_TMP_BUF_SIZE;
- fd_data = (struct fd_data *)
- erts_alloc(ERTS_ALC_T_FD_TAB, max_files * sizeof(struct fd_data));
-}
-
-
-/* Fill buffer, return buffer length, 0 for EOF, < 0 for error. */
-
-static int read_fill(int fd, char *buf, int len)
-{
- int i, got = 0;
- do {
- if ((i = read(fd, buf+got, len-got)) <= 0) {
- return i;
- }
- got += i;
- } while (got < len);
- return (len);
-}
-
-
-/************************** Misc... *******************************/
-
-extern const char pre_loaded_code[];
-extern char* const pre_loaded[];
-
-
-/* Float conversion */
-
-int sys_chars_to_double(char *buf, double *fp)
-{
- char *s = buf;
-
- /* The following check is incorporated from the Vee machine */
-
-#define ISDIGIT(d) ((d) >= '0' && (d) <= '9')
-
- /* Robert says that something like this is what he really wanted:
- *
- * 7 == sscanf(Tbuf, "%[+-]%[0-9].%[0-9]%[eE]%[+-]%[0-9]%s", ....);
- * if (*s2 == 0 || *s3 == 0 || *s4 == 0 || *s6 == 0 || *s7)
- * break;
- */
-
- /* Scan string to check syntax. */
- if (*s == '+' || *s == '-')
- s++;
-
- if (!ISDIGIT(*s)) /* Leading digits. */
- return -1;
- while (ISDIGIT(*s)) s++;
- if (*s++ != '.') /* Decimal part. */
- return -1;
- if (!ISDIGIT(*s))
- return -1;
- while (ISDIGIT(*s)) s++;
- if (*s == 'e' || *s == 'E') {
- /* There is an exponent. */
- s++;
- if (*s == '+' || *s == '-')
- s++;
- if (!ISDIGIT(*s))
- return -1;
- while (ISDIGIT(*s)) s++;
- }
- if (*s) /* That should be it */
- return -1;
-
- if (sscanf(buf, "%lf", fp) != 1)
- return -1;
- return 0;
-}
-
-/*
- ** Convert a double to ascii format 0.dddde[+|-]ddd
- ** return number of characters converted
- */
-
-int sys_double_to_chars(double fp, char *buf)
-{
- (void) sprintf(buf, "%.20e", fp);
- return strlen(buf);
-}
-
-
-/* Floating point exceptions */
-
-#if (CPU == SPARC)
-jmp_buf fpe_jmp;
-
-RETSIGTYPE fpe_sig_handler(int sig)
-{
- longjmp(fpe_jmp, 1);
-}
-
-#elif (CPU == PPC603)
-static void fix_registers(void){
- FP_CONTEXT fpcontext;
- fppSave(&fpcontext);
- fpcontext.fpcsr &= ~(_PPC_FPSCR_INIT);
- fppRestore(&fpcontext);
-}
-#endif
-
-
-/* Return a pointer to a vector of names of preloaded modules */
-
-Preload* sys_preloaded(void)
-{
- return (Preload *) pre_loaded;
-}
-
-/* Return a pointer to preloaded code for module "module" */
-unsigned char* sys_preload_begin(Preload *pp)
-{
- return pp->code;
-}
-
-/* Clean up if allocated */
-void sys_preload_end(Preload *pp)
-{
- /* Nothing */
-}
-
-/* Read a key from console (?) */
-
-int sys_get_key(int fd)
-{
- int c;
- unsigned char rbuf[64];
-
- fflush(stdout); /* Flush query ??? */
-
- if ((c = read(fd,rbuf,64)) <= 0)
- return c;
- return rbuf[0];
-}
-
-
-/* A real printf that does the equivalent of fprintf(stdout, ...) */
-
-/* ARGSUSED */
-static STATUS
-stdio_write(char *buf, int nchars, int fp)
-{
- if (fwrite(buf, sizeof(char), nchars, (FILE *)fp) == 0)
- return(ERROR);
- return(OK);
-}
-
-int real_printf(const char *fmt, ...)
-{
- va_list ap;
- int err;
-
- va_start(ap, fmt);
- err = fioFormatV(fmt, ap, stdio_write, (int)stdout);
- va_end(ap);
- return(err);
-}
-
-
-/*
- * Little function to do argc, argv calls from (e.g.) VxWorks shell
- * The arguments should be in the form of a single ""-enclosed string
- * NOTE: This isn't really part of the emulator, just included here
- * so we can use the handy functions and memory reclamation.
- */
-void argcall(char *args)
-{
- int argc;
- char **argv;
- FUNCPTR entry;
-
- if (args != NULL) {
- if ((argc = build_argv(args, &argv)) > 0) {
- if ((entry = lookup(argv[0])) != NULL)
- (*entry)(argc, argv, (char **)NULL); /* NULL for envp */
- else
- fprintf(stderr, "Couldn't find %s\n", argv[0]);
- } else
- fprintf(stderr, "Failed to build argv!\n");
- } else
- fprintf(stderr, "No argument list!\n");
-}
-
-
-/* That concludes the Erlang stuff - now we just need to implement an OS...
- - Just kidding, but resource reclamation isn't the strength of VxWorks */
-#undef calloc
-#undef free
-#undef cfree
-#undef malloc
-#undef realloc
-#undef open
-#undef creat
-#undef socket
-#undef accept
-#undef close
-#undef fopen
-#undef fdopen
-#undef freopen
-#undef fclose
-
-/********************* Using elib_malloc ****************************/
-/* This gives us yet another level of malloc wrappers. The purpouse */
-/* is to be able to select between different varieties of memory */
-/* allocation without recompiling. */
-/* Maybe the performance is somewhat degraded by this, but */
-/* on the other hand, performance may be much better if the most */
-/* suiting malloc is used (not to mention the much lower */
-/* fragmentation). */
-/* /Patrik N */
-/********************************************************************/
-
-/*
- * I don't want to include the whole elib header, especially
- * as it uses char * for generic pointers. Let's fool ANSI C instead.
- */
-extern void *elib_malloc(size_t);
-extern void *elib_realloc(void *, size_t);
-extern void elib_free(void *);
-extern void elib_init(void *, int);
-extern void elib_force_init(void *, int);
-extern size_t elib_sizeof(void *);
-
-/* Flags */
-#define USING_ELIB_MALLOC 1 /* We are using the elib_malloc */
-#define WARN_MALLOC_MIX 2 /* Warn if plain malloc or save_malloc
- is mixed with sys_free2 or
- sys_realloc2 */
-#define REALLOC_MOVES 4 /* Always move on realloc
- (less fragmentation) */
-#define USER_POOL 8 /* The user supplied the memory
- pool, it was not save_alloced. */
-#define RECLAIM_USER_POOL 16 /* Use the reclaim mechanism in the
- user pool. */
-#define NEW_USER_POOL 32 /* The user pool is newly suppllied,
- any old pool should be discarded */
-
-
-#define ELIB_LOCK \
-if(alloc_flags & USING_ELIB_MALLOC) \
- semTake(elib_malloc_sem, WAIT_FOREVER)
-
-#define ELIB_UNLOCK \
-if(alloc_flags & USING_ELIB_MALLOC) \
- semGive(elib_malloc_sem)
-
-#define USER_RECLAIM() ((alloc_flags & USING_ELIB_MALLOC) && \
- (alloc_flags & USER_POOL) && \
- (alloc_flags & RECLAIM_USER_POOL))
-
-/*
- * Global state
- * The use of function pointers for the malloc/realloc/free functions
- * is actually only useful in the malloc case, we must know what kind of
- * realloc/free we are going to use, so we could call elib_xxx directly.
- * However, as the overhead is small and this construction makes it
- * fairly easy to add another malloc algorithm, the function pointers
- * are used in realloc/free to.
- */
-static MallocFunction actual_alloc = &save_malloc;
-static ReallocFunction actual_realloc = &save_realloc;
-static FreeFunction actual_free = &save_free;
-static int alloc_flags = 0;
-static int alloc_pool_size = 0;
-static void *alloc_pool_ptr = NULL;
-static SEM_ID elib_malloc_sem = NULL;
-
-/*
- * Descide if we should use the save_free instead of elib_free or,
- * in the case of the free used in a delete hook, if we should
- * use plain free instead of elib_free.
- */
-static int use_save_free(void *ptr){
- register int diff = ((char *) ptr) - ((char *) alloc_pool_ptr);
- /*
- * Hmmm... should it be save_free even if diff is exactly 0?
- * The answer is Yes if the whole area is save_alloced and No if not,
- * so reclaim_free_hook is NOT run in the case of one save_alloced area.
- */
- return (!(alloc_flags & USING_ELIB_MALLOC) ||
- (diff < 0 || diff >= alloc_pool_size));
-}
-
-/*
- * A free function used by the task deletion hook for the save_xxx functions.
- * Set with the set_reclaim_free_function function.
- */
-static void reclaim_free_hook(void *ptr){
- if(use_save_free(ptr)){
- free(ptr);
- } else {
- ELIB_LOCK;
- (*actual_free)(ptr);
- ELIB_UNLOCK;
- }
-}
-
-
-/*
- * Initialize, sets the values of pointers based on
- * either nothing (the default) or what's set previously by the
- * erl_set_memory_block function.
- */
-static void initialize_allocation(void){
- set_reclaim_free_function(NULL);
- if(alloc_pool_size == 0){
- actual_alloc = (void *(*)(size_t))&save_malloc;
- actual_realloc = (void *(*)(void *, size_t))&save_realloc;
- actual_free = &save_free;
- alloc_flags &= ~(USING_ELIB_MALLOC | USER_POOL | RECLAIM_USER_POOL);
- } else {
- if(elib_malloc_sem == NULL)
- elib_malloc_sem = semMCreate
- (SEM_Q_PRIORITY | SEM_DELETE_SAFE | SEM_INVERSION_SAFE);
- if(elib_malloc_sem == NULL)
- erl_exit(1,"Could not create mutex semaphore for elib_malloc");
- if(!(alloc_flags & USER_POOL)){
- if((alloc_pool_ptr = save_malloc(alloc_pool_size)) == NULL)
- erl_exit(1,"Erlang set to allocate a %d byte block initially;"
- " not enough memory available.", alloc_pool_size);
- elib_force_init(alloc_pool_ptr, alloc_pool_size);
- } else if(alloc_flags & NEW_USER_POOL){
- elib_force_init(alloc_pool_ptr, alloc_pool_size);
- }
- actual_alloc=&elib_malloc;
- actual_realloc=&elib_realloc;
- actual_free=&elib_free;
- alloc_flags |= USING_ELIB_MALLOC;
- /* We MUST see to that the right free function is used
- otherwise we'll get a very nasty crash! */
- if(USER_RECLAIM())
- set_reclaim_free_function(&reclaim_free_hook);
- }
- alloc_flags &= ~(NEW_USER_POOL); /* It's never new after initialization*/
-}
-
-/* This does not exist on other platforms, we just use it in sys.c
- and the BSD resolver */
-void *sys_calloc2(Uint nelem, Uint elsize){
- void *ptr = erts_alloc_fnf(ERTS_ALC_T_UNDEF, nelem*elsize);
- if(ptr != NULL)
- memset(ptr,0,nelem*elsize);
- return ptr;
-}
-
-/*
- * The malloc wrapper
- */
-void *
-erts_sys_alloc(ErtsAlcType_t type, void *extra, Uint size)
-{
- register void *ret;
- ELIB_LOCK;
- if(USER_RECLAIM())
- ret = save_malloc2((size_t)size,actual_alloc);
- else
- ret = (*actual_alloc)((size_t)size);
- ELIB_UNLOCK;
- return ret;
-}
-
-/*
- * The realloc wrapper, may respond to the "realloc-always-moves" flag
- * if the area is initially allocated with elib_malloc.
- */
-void *
-erts_sys_realloc(ErtsAlcType_t type, void *extra, void *ptr, Uint size)
-{
- register void *ret;
- if(use_save_free(ptr)){
- if((alloc_flags & WARN_MALLOC_MIX) &&
- (alloc_flags & USING_ELIB_MALLOC))
- erts_fprintf(stderr,"Warning, save_malloced data realloced "
- "by sys_realloc2\n");
- return save_realloc(ptr, (size_t) size);
- } else {
- ELIB_LOCK;
- if((alloc_flags & REALLOC_MOVES) &&
- (alloc_flags & USING_ELIB_MALLOC)){
- size_t osz = elib_sizeof(ptr);
- if(USER_RECLAIM())
- ret = save_malloc2((size_t) size, actual_alloc);
- else
- ret = (*actual_alloc)((size_t) size);
- if(ret != NULL){
- memcpy(ret,ptr,(((size_t)size) < osz) ? ((size_t)size) : osz);
- if(USER_RECLAIM())
- save_free2(ptr,actual_free);
- else
- (*actual_free)(ptr);
- }
- } else {
- if(USER_RECLAIM())
- ret = save_realloc2(ptr,(size_t)size,actual_realloc);
- else
- ret = (*actual_realloc)(ptr,(size_t)size);
- }
- ELIB_UNLOCK;
- return ret;
- }
-}
-
-/*
- * Wrapped free().
- */
-void
-erts_sys_free(ErtsAlcType_t type, void *extra, void *ptr)
-{
- if(use_save_free(ptr)){
- /*
- * This might happen when linked in drivers use save_malloc etc
- * directly.
- */
- if((alloc_flags & WARN_MALLOC_MIX) &&
- (alloc_flags & USING_ELIB_MALLOC))
- erts_fprintf(stderr,"Warning, save_malloced data freed by "
- "sys_free2\n");
- save_free(ptr);
- } else {
- ELIB_LOCK;
- if(USER_RECLAIM())
- save_free2(ptr,actual_free);
- else
- (*actual_free)(ptr);
- ELIB_UNLOCK;
- }
-}
-
-/*
- * External interface to be called before erlang is started
- * Parameters:
- * isize: The size of the memory block where erlang should malloc().
- * iptr: (optional) A pointer to a user supplied memory block of
- * size isize.
- * warn_save: Instructs sys_free2 and sys_realloc2 to warn if
- * memory allocation/reallocation/freeing is mixed between
- * pure malloc/save_malloc/sys_alloc2 routines (only
- * warns if elib is actually used in the sys_alloc2 routines).
- * realloc_moves: Always allocate a fresh memory block on reallocation
- * (less fragmentation).
- * reclaim_in_supplied: Use memory reclaim mechanisms inside the user
- * supplied area, this makes one area reusable between
- * starts of erlang and might be nice for drivers etc.
- */
-
-int erl_set_memory_block(int isize, int iptr, int warn_save,
- int realloc_moves, int reclaim_in_supplied, int p5,
- int p6, int p7, int p8, int p9){
- if(erlang_id != 0){
- erts_fprintf(stderr,"Error, cannot set erlang memory block while an "
- "erlang task is running!\n");
- return 1;
- }
- if(isize < 8 * 1024 *1024)
- erts_fprintf(stderr,
- "Warning, the memory pool of %dMb may be to small to "
- "run erlang in!\n", isize / (1024 * 1024));
- alloc_pool_size = (size_t) isize;
- alloc_pool_ptr = (void *) iptr;
- alloc_flags = 0;
- /* USING_ELIB_MALLOC gets set by the initialization routine */
- if((void *)iptr != NULL)
- alloc_flags |= (USER_POOL | NEW_USER_POOL);
- if(realloc_moves)
- alloc_flags |= REALLOC_MOVES;
- if(warn_save)
- alloc_flags |= WARN_MALLOC_MIX;
- if((void *)iptr != NULL && reclaim_in_supplied)
- alloc_flags |= RECLAIM_USER_POOL;
- return 0;
-}
-
-/* External statistics interface */
-int erl_memory_show(int p0, int p1, int p2, int p3, int p4, int p5,
- int p6, int p7, int p8, int p9){
- struct elib_stat statistics;
- if(!(alloc_flags & USING_ELIB_MALLOC) && erlang_id != 0){
- erts_printf("Using plain save_alloc, use memShow instead.\n");
- return 1;
- }
- if(erlang_id == 0 && !((alloc_flags & USER_POOL) &&
- !(alloc_flags & NEW_USER_POOL))){
- erts_printf("Sorry, no allocation statistics until erlang "
- "is started.\n");
- return 1;
- }
- erts_printf("Allocation settings:\n");
- erts_printf("Using elib_malloc with memory pool size of %lu bytes.\n",
- (unsigned long) alloc_pool_size);
- erts_printf("Realloc-always-moves is %s\n",
- (alloc_flags & REALLOC_MOVES) ? "on" : "off");
- erts_printf("Warnings about mixed malloc/free's are %s\n",
- (alloc_flags & WARN_MALLOC_MIX) ? "on" : "off");
- if(alloc_flags & USER_POOL){
- erts_printf("The memory block used by elib is user supplied "
- "at 0x%08x.\n", (unsigned int) alloc_pool_ptr);
- if(alloc_flags & RECLAIM_USER_POOL)
- erts_printf("Allocated memory within the user supplied pool\n"
- " will be automatically reclaimed at task exit.\n");
- } else {
- erts_printf("The memory block used by elib is save_malloc'ed "
- "at 0x%08x.\n", (unsigned int) alloc_pool_ptr);
- }
- erts_printf("Statistics from elib_malloc:\n");
- ELIB_LOCK;
-
- elib_stat(&statistics);
- ELIB_UNLOCK;
- erts_printf("Type Size (bytes) Number of blocks\n");
- erts_printf("============= ============ ================\n");
- erts_printf("Total: %12lu %16lu\n",
- (unsigned long) statistics.mem_total*4,
- (unsigned long) statistics.mem_blocks);
- erts_printf("Allocated: %12lu %16lu\n",
- (unsigned long) statistics.mem_alloc*4,
- (unsigned long) statistics.mem_blocks-statistics.free_blocks);
- erts_printf("Free: %12lu %16lu\n",
- (unsigned long) statistics.mem_free*4,
- (unsigned long) statistics.free_blocks);
- erts_printf("Largest free: %12lu -\n\n",
- (unsigned long) statistics.max_free*4);
- return 0;
-}
-
-
-/*
-** More programmer friendly (as opposed to user friendly ;-) interface
-** to the memory statistics. Resembles the VxWorks memPartInfoGet but
-** does not take a partition id as parameter...
-*/
-int erl_mem_info_get(MEM_PART_STATS *stats){
- struct elib_stat statistics;
- if(!(alloc_flags & USING_ELIB_MALLOC))
- return -1;
- ELIB_LOCK;
- elib_stat(&statistics);
- ELIB_UNLOCK;
- stats->numBytesFree = statistics.mem_free*4;
- stats->numBlocksFree = statistics.free_blocks;
- stats->maxBlockSizeFree = statistics.max_free*4;
- stats->numBytesAlloc = statistics.mem_alloc*4;
- stats->numBlocksAlloc = statistics.mem_blocks-statistics.free_blocks;
- return 0;
-}
-
-/********************* Pipe driver **********************************/
-/*
- * Purpose: Pipe driver with Unix (unnamed) pipe semantics.
- * Author: Peter Hogfeldt ([email protected]) from an outline
- * by Per Hedeland ([email protected]).
- *
- * Note: This driver must *not* use the reclaim facilities, hence it
- * is placed here. (after the #undef's of open,malloc etc)
- *
- * This driver supports select() and non-blocking I/O via
- * ioctl(fd, FIONBIO, val).
- *
- * 1997-03-21 Peter Hogfeldt
- * Added non-blocking I/O.
- *
- */
-
-/*
- * SEMAPHORES
- *
- * Each end of a pipe has two semaphores: semExcl for serialising access to
- * the pipe end, and semBlock for blocking I/O.
- *
- * reader->semBlock is available (full) if and only if the pipe is
- * not empty, or the write end is closed. Otherwise
- * it is unavailable (empty). It is initially
- * unavailable.
- *
- * writer->semBlock is available (full) if and only if the pipe is
- * not full, or if the reader end is closed.
- * Otherwise it is unavailable. It is initially
- * available.
- */
-
-#define UXPIPE_SIZE 4096
-
-/* Forward declaration */
-typedef struct uxPipeDev UXPIPE_DEV;
-
-/*
- * Pipe descriptor (one for each open pipe).
- */
-typedef struct {
- int drvNum;
- UXPIPE_DEV *reader, *writer;
- RING_ID ringId;
-} UXPIPE;
-
-/*
- * Device descriptor (one for each of the read and write
- * ends of an open pipe).
- */
-struct uxPipeDev {
- UXPIPE *pipe;
- int blocking;
- SEL_WAKEUP_LIST wakeupList;
- SEM_ID semExcl;
- SEM_ID semBlock;
-};
-
-int uxPipeDrvNum = 0; /* driver number of pipe driver */
-
-#define PIPE_NAME "/uxpipe" /* only used internally */
-#define PIPE_READ "/r" /* ditto */
-#define PIPE_WRITE "/w" /* ditto */
-
-LOCAL char pipeRead[64], pipeWrite[64];
-LOCAL DEV_HDR devHdr;
-LOCAL UXPIPE *newPipe; /* communicate btwn open()s in pipe() */
-LOCAL SEM_ID pipeSem; /* mutual exclusion in pipe() */
-
-/* forward declarations */
-LOCAL int uxPipeOpen(DEV_HDR *pDv, char *name, int mode);
-LOCAL int uxPipeClose(UXPIPE_DEV *pDev);
-LOCAL int uxPipeRead(UXPIPE_DEV *pDev, char *buffer, int maxbytes);
-LOCAL int uxPipeWrite(UXPIPE_DEV *pDev, char *buffer, int nbytes);
-LOCAL STATUS uxPipeIoctl(FAST UXPIPE_DEV *pDev, FAST int function, int arg);
-
-
-/***************************************************************************
- *
- * uxPipeDrv - install Unix pipe driver
- *
- * This routine initializes the Unix pipe driver. It must be called
- * before any other routine in this driver.
- *
- * RETURNS:
- * OK, or ERROR if I/O system is unable to install driver.
- */
-
-STATUS
-uxPipeDrv(void)
-{
- if (uxPipeDrvNum > 0)
- return (OK); /* driver already installed */
- if ((uxPipeDrvNum = iosDrvInstall((FUNCPTR) NULL, (FUNCPTR) NULL,
- uxPipeOpen, uxPipeClose, uxPipeRead,
- uxPipeWrite, uxPipeIoctl)) == ERROR)
- return (ERROR);
- if (iosDevAdd(&devHdr, PIPE_NAME, uxPipeDrvNum) == ERROR)
- return (ERROR);
- strcpy(pipeRead, PIPE_NAME);
- strcat(pipeRead, PIPE_READ);
- strcpy(pipeWrite, PIPE_NAME);
- strcat(pipeWrite, PIPE_WRITE);
- if ((pipeSem = semMCreate(SEM_Q_PRIORITY | SEM_DELETE_SAFE)) == NULL)
- return (ERROR);
- return (OK);
-}
-
-/***************************************************************************
- *
- * uxPipeOpen - open a pipe
- *
- * RETURNS: Pointer to device descriptor, or ERROR if memory cannot be
- * allocated (errno = ENOMEM), or invalid argument (errno = EINVAL).
- */
-
-/*
- * DEV_HDR *pDv; pointer to device header (dummy)
- * char *name; name of pipe to open ("/r" or "/w")
- * int mode; access mode (O_RDONLY or O_WRONLY)
- */
-LOCAL int
-uxPipeOpen(DEV_HDR *pDv, char *name, int mode)
-{
- UXPIPE_DEV *reader, *writer;
-
- if (mode == O_RDONLY && strcmp(name, PIPE_READ) == 0) {
- /* reader open */
- if ((newPipe = (UXPIPE *) malloc(sizeof(UXPIPE))) != NULL) {
- if ((newPipe->ringId = rngCreate(UXPIPE_SIZE)) != NULL) {
- if ((reader = (UXPIPE_DEV *) malloc(sizeof(UXPIPE_DEV))) != NULL) {
- if ((reader->semExcl = semBCreate(SEM_Q_FIFO, SEM_FULL)) != NULL) {
- if ((reader->semBlock = semBCreate(SEM_Q_FIFO, SEM_EMPTY)) != NULL) {
- reader->pipe = newPipe;
- reader->blocking = 1;
- selWakeupListInit(&reader->wakeupList);
- newPipe->reader = reader;
- newPipe->writer = NULL;
- newPipe->drvNum = uxPipeDrvNum;
- return ((int) reader);
- }
- semDelete(reader->semExcl);
- }
- free(reader);
- }
- rngDelete(newPipe->ringId);
- }
- free(newPipe);
- newPipe = NULL;
- errno = ENOMEM;
- }
- } else if (mode == O_WRONLY && strcmp(name, PIPE_WRITE) == 0) {
- /* writer open */
- if (newPipe != NULL &&
- (writer = (UXPIPE_DEV *) malloc(sizeof(UXPIPE_DEV))) != NULL) {
- if ((writer->semExcl = semBCreate(SEM_Q_FIFO, SEM_FULL)) != NULL) {
- if ((writer->semBlock = semBCreate(SEM_Q_FIFO, SEM_FULL)) != NULL) {
- writer->blocking = 1;
- writer->pipe = newPipe;
- selWakeupListInit(&writer->wakeupList);
- newPipe->writer = writer;
- newPipe = NULL;
- return ((int) writer);
- }
- semDelete(writer->semExcl);
- }
- free(writer);
- }
- if (newPipe != NULL)
- free(newPipe);
- newPipe = NULL;
- errno = ENOMEM;
- } else {
- errno = EINVAL;
- }
- return (ERROR);
-}
-
-/***************************************************************************
- *
- * uxPipeClose - close read or write end of a pipe.
- *
- * RETURNS:
- * OK, or ERROR if device descriptor does not refer to an open read or
- write end of a pipe (errno = EBADF).
- */
-
-LOCAL int
-uxPipeClose(UXPIPE_DEV *pDev)
-{
- UXPIPE *pajp = pDev->pipe;
-
- taskLock();
- if (pDev == pajp->reader) {
- /* Close this end */
- semDelete(pDev->semExcl);
- semDelete(pDev->semBlock);
- free(pDev);
- pajp->reader = NULL;
- /* Inform the other end */
- if (pajp->writer != NULL) {
- selWakeupAll(&pajp->writer->wakeupList, SELWRITE);
- semGive(pajp->writer->semBlock);
- }
- } else if (pDev == pajp->writer) {
- /* Close this end */
- semDelete(pDev->semExcl);
- semDelete(pDev->semBlock);
- free(pDev);
- pajp->writer = NULL;
- /* Inform the other end */
- if (pajp->reader != NULL) {
- selWakeupAll(&pajp->reader->wakeupList, SELREAD);
- semGive(pajp->reader->semBlock);
- }
- } else {
- errno = EBADF;
- taskUnlock();
- return (ERROR);
- }
- if (pajp->reader == NULL && pajp->writer == NULL) {
- rngDelete(pajp->ringId);
- pajp->drvNum = 0;
- free(pajp);
- }
- taskUnlock();
- return (OK);
-}
-/***************************************************************************
- *
- * uxPipeRead - read from a pipe.
- *
- * Reads at most maxbytes bytes from the pipe. Blocks if blocking mode is
- * set and the pipe is empty.
- *
- * RETURNS:
- * number of bytes read, 0 on EOF, or ERROR if device descriptor does
- * not refer to an open read end of a pipe (errno = EBADF), or if
- * non-blocking mode is set and the pipe is empty (errno = EWOULDBLOCK).
- */
-
-LOCAL int
-uxPipeRead(UXPIPE_DEV *pDev, char *buffer, int maxbytes)
-{
- UXPIPE *pajp = pDev->pipe;
- int nbytes = 0;
-
- if (pDev != pajp->reader) {
- errno = EBADF;
- return (ERROR);
- }
- if (maxbytes == 0)
- return (0);
- semTake(pDev->semExcl, WAIT_FOREVER);
- /*
- * Note that semBlock may be full, although there is nothing to read.
- * This happens e.g. after the following sequence of operations: a
- * reader task blocks, a writer task writes two times (the first
- * write unblocks the reader task, the second write makes semBlock
- * full).
- */
- while (nbytes == 0) {
- if (pDev->blocking)
- semTake(pDev->semBlock, WAIT_FOREVER);
- /*
- * Reading and updating of the write end must not be interleaved
- * with a write from another task - hence we lock this task.
- */
- taskLock();
- nbytes = rngBufGet(pajp->ringId, buffer, maxbytes);
- if (nbytes > 0) {
- /* Give own semaphore if bytes remain or if write end is closed */
- if ((!rngIsEmpty(pajp->ringId) || pajp->writer == NULL) &&
- pDev->blocking)
- semGive(pDev->semBlock);
- /* Inform write end */
- if (pajp->writer != NULL) {
- if (pajp->writer->blocking)
- semGive(pajp->writer->semBlock);
- selWakeupAll(&pajp->writer->wakeupList, SELWRITE);
- }
- } else if (pajp->writer == NULL) {
- nbytes = 0; /* EOF */
- /* Give semaphore when write end is closed */
- if (pDev->blocking)
- semGive(pDev->semBlock);
- taskUnlock();
- semGive(pDev->semExcl);
- return (nbytes);
- } else if (!pDev->blocking) {
- taskUnlock();
- semGive(pDev->semExcl);
- errno = EWOULDBLOCK;
- return (ERROR);
- }
- taskUnlock();
- }
- semGive(pDev->semExcl);
- return (nbytes);
-}
-
-/***************************************************************************
- *
- * uxPipeWrite - write to a pipe.
- *
- * Writes nbytes bytes to the pipe. Blocks if blocking mode is set, and if
- * the pipe is full.
- *
- * RETURNS:
- * number of bytes written, or ERROR if the device descriptor does not
- * refer to an open write end of a pipe (errno = EBADF); or if the read end
- * of the pipe is closed (errno = EPIPE); or if non-blocking mode is set
- * and the pipe is full (errno = EWOULDBLOCK).
- *
- */
-
-LOCAL int
-uxPipeWrite(UXPIPE_DEV *pDev, char *buffer, int nbytes)
-{
-
- UXPIPE *pajp = pDev->pipe;
- int sofar = 0, written;
-
- if (pDev != pajp->writer) {
- errno = EBADF;
- return (ERROR);
- }
- if (pajp->reader == NULL) {
- errno = EPIPE;
- return (ERROR);
- }
- if (nbytes == 0)
- return (0);
- semTake(pDev->semExcl, WAIT_FOREVER);
- while (sofar < nbytes) {
- if (pDev->blocking)
- semTake(pDev->semBlock, WAIT_FOREVER);
- if (pajp->reader == NULL) {
- errno = EPIPE;
- semGive(pDev->semBlock);
- semGive(pDev->semExcl);
- return (ERROR);
- }
- /* Writing and updating of the read end must not be interleaved
- * with a read from another task - hence we lock this task.
- */
- taskLock();
- written = rngBufPut(pajp->ringId, buffer + sofar, nbytes - sofar);
- sofar += written;
- /* Inform the read end if we really wrote something */
- if (written > 0 && pajp->reader != NULL) {
- selWakeupAll(&pajp->reader->wakeupList, SELREAD);
- if (pajp->reader->blocking)
- semGive(pajp->reader->semBlock);
- }
- taskUnlock();
- if (!pDev->blocking) {
- if (sofar == 0) {
- errno = EWOULDBLOCK;
- sofar = ERROR;
- }
- break;
- }
- }
- /* Give own semaphore if space remains */
- if (!rngIsFull(pajp->ringId) && pDev->blocking)
- semGive(pDev->semBlock);
- semGive(pDev->semExcl);
- return (sofar);
-}
-
-/***************************************************************************
- *
- * uxPipeIoctl - do device specific I/O control
- *
- * RETURNS:
- * OK or ERROR.
- */
-
-LOCAL STATUS
-uxPipeIoctl(FAST UXPIPE_DEV *pDev, FAST int function, int arg)
-
-{
- UXPIPE *pajp = pDev->pipe;
- int status = OK;
-
- switch (function) {
- case FIONBIO:
- pDev->blocking = (*(int *)arg) ? 0 : 1;
- break;
- case FIOSELECT:
- taskLock();
- selNodeAdd(&pDev->wakeupList, (SEL_WAKEUP_NODE *) arg);
- if (selWakeupType((SEL_WAKEUP_NODE *) arg) == SELREAD &&
- pDev == pajp->reader &&
- (!rngIsEmpty(pajp->ringId) || pajp->writer == NULL))
- selWakeup((SEL_WAKEUP_NODE *) arg);
- if (selWakeupType((SEL_WAKEUP_NODE *) arg) == SELWRITE &&
- pDev == pajp->writer &&
- (!rngIsFull(pajp->ringId) || pajp->reader == NULL))
- selWakeup((SEL_WAKEUP_NODE *) arg);
- taskUnlock();
- break;
- case FIOUNSELECT:
- selNodeDelete(&pDev->wakeupList, (SEL_WAKEUP_NODE *) arg);
- break;
- default:
- status = ERROR;
- break;
- }
- return (status);
-}
-
-/***************************************************************************
- *
- * pipe - create an intertask channel
- *
- * Creates a pipe. fd[0] (fd[1]) is the read (write) file descriptor.
- *
- * RETURNS:
- * OK or ERROR, if the pipe could not be created.
- */
-
-STATUS
-pipe(int fd[2])
-{
- semTake(pipeSem, WAIT_FOREVER);
- if ((fd[0] = open(pipeRead, O_RDONLY, 0)) != ERROR) {
- if ((fd[1] = open(pipeWrite, O_WRONLY, 0)) != ERROR) {
- semGive(pipeSem);
- return (OK);
- }
- (void) close(fd[0]);
- }
- errno &= 0xFFFF;
- if((errno & 0xFFFF) == EINTR) /* Why on earth EINTR??? */
- errno = ENFILE; /* It means we are out of file descriptors...*/
- semGive(pipeSem);
- return (ERROR);
-}
-
-/***************************************************************************
- *
- * uxPipeShow - display pipe information
- *
- * RETURNS:
- * N/A.
- */
-
-void
-uxPipeShow(int fd)
-{
- UXPIPE_DEV *pDev;
- UXPIPE *pajp;
- int drvValue;
-
- if ((drvValue = iosFdValue(fd)) == ERROR) {
- erts_fprintf(stderr, "Error: file descriptor invalid\n");
- return;
- }
- pDev = (UXPIPE_DEV *)drvValue;
- pajp = pDev->pipe;
- if (pajp->drvNum != uxPipeDrvNum) {
- erts_fprintf(stderr, "Error: Not a ux pipe device\n");
- return;
- }
- erts_fprintf(stderr, "Device : 0x%x\n", (int) pDev);
- erts_fprintf(stderr, "Buffer size : %d\n", UXPIPE_SIZE);
- erts_fprintf(stderr, "Bytes in buffer : %d\n\n", rngNBytes(pajp->ringId));
- erts_fprintf(stderr, "READ END\n\n");
- if (pajp->reader != NULL) {
- erts_fprintf(stderr, "Mode : ");
- erts_fprintf(stderr, "%s\n",
- (pajp->reader->blocking) ? "blocking" : "non-blocking");
- }
- erts_fprintf(stderr, "Status : ");
- if (pajp->reader != NULL) {
- erts_fprintf(stderr, "OPEN\n");
- erts_fprintf(stderr, "Wake-up list : %d\n\n",
- selWakeupListLen(&pajp->reader->wakeupList));
- erts_fprintf(stderr, "Exclusion Semaphore\n");
- semShow(pajp->reader->semExcl, 1);
- erts_fprintf(stderr, "Blocking Semaphore\n");
- semShow(pajp->reader->semBlock, 1);
- } else
- erts_fprintf(stderr, "CLOSED\n\n");
- erts_fprintf(stderr, "WRITE END\n\n");
- if (pajp->writer != NULL) {
- erts_fprintf(stderr, "Mode : ");
- erts_fprintf(stderr, "%s\n",
- (pajp->writer->blocking) ? "blocking" : "non-blocking");
- }
- erts_fprintf(stderr, "Status : ");
- if (pajp->writer != NULL) {
- erts_fprintf(stderr, "OPEN\n");
- erts_fprintf(stderr, "Wake-up list : %d\n\n",
- selWakeupListLen(&pajp->writer->wakeupList));
- erts_fprintf(stderr, "Exclusion Semaphore\n");
- semShow(pajp->writer->semExcl, 1);
- erts_fprintf(stderr, "Blocking Semaphore\n");
- semShow(pajp->writer->semBlock, 1);
- } else
- erts_fprintf(stderr, "CLOSED\n\n");
-}
-
-#ifdef DEBUG
-void
-erl_assert_error(char* expr, char* file, int line)
-{
- fflush(stdout);
- fprintf(stderr, "Assertion failed: %s in %s, line %d\n",
- expr, file, line);
- fflush(stderr);
- erl_crash_dump(file, line, "Assertion failed: %s\n", expr);
- abort();
-}
-void
-erl_debug(char* fmt, ...)
-{
- char sbuf[1024]; /* Temporary buffer. */
- va_list va;
-
- va_start(va, fmt);
- vsprintf(sbuf, fmt, va);
- va_end(va);
- fprintf(stderr, "%s\n", sbuf);
-}
-#endif
diff --git a/erts/emulator/sys/win32/erl_win32_sys_ddll.c b/erts/emulator/sys/win32/erl_win32_sys_ddll.c
index d00eed932b..54991a610c 100644
--- a/erts/emulator/sys/win32/erl_win32_sys_ddll.c
+++ b/erts/emulator/sys/win32/erl_win32_sys_ddll.c
@@ -58,7 +58,7 @@ void erl_sys_ddll_init(void) {
/*
* Open a shared object
*/
-int erts_sys_ddll_open2(char *full_name, void **handle, ErtsSysDdllError* err)
+int erts_sys_ddll_open2(const char *full_name, void **handle, ErtsSysDdllError* err)
{
int len;
char dlname[MAXPATHLEN + 1];
@@ -92,7 +92,7 @@ int erts_sys_ddll_open_noext(char *dlname, void **handle, ErtsSysDdllError* err)
/*
* Find a symbol in the shared object
*/
-int erts_sys_ddll_sym2(void *handle, char *func_name, void **function,
+int erts_sys_ddll_sym2(void *handle, const char *func_name, void **function,
ErtsSysDdllError* err)
{
FARPROC proc;
diff --git a/erts/emulator/sys/win32/erl_win_dyn_driver.h b/erts/emulator/sys/win32/erl_win_dyn_driver.h
index ec5141838a..8b6be2b2f1 100644
--- a/erts/emulator/sys/win32/erl_win_dyn_driver.h
+++ b/erts/emulator/sys/win32/erl_win_dyn_driver.h
@@ -74,7 +74,9 @@ WDD_TYPEDEF(ErlDrvTermData, driver_mk_port,(ErlDrvPort));
WDD_TYPEDEF(ErlDrvTermData, driver_connected,(ErlDrvPort));
WDD_TYPEDEF(ErlDrvTermData, driver_caller,(ErlDrvPort));
WDD_TYPEDEF(ErlDrvTermData, driver_mk_term_nil,(void));
+WDD_TYPEDEF(int, erl_drv_output_term, (ErlDrvTermData, ErlDrvTermData*, int));
WDD_TYPEDEF(int, driver_output_term, (ErlDrvPort, ErlDrvTermData*, int));
+WDD_TYPEDEF(int, erl_drv_send_term, (ErlDrvTermData, ErlDrvTermData, ErlDrvTermData*, int));
WDD_TYPEDEF(int, driver_send_term, (ErlDrvPort, ErlDrvTermData, ErlDrvTermData*, int));
WDD_TYPEDEF(long, driver_async, (ErlDrvPort,unsigned int*,void (*)(void*),void*,void (*)(void*)));
WDD_TYPEDEF(int, driver_async_cancel, (unsigned int));
@@ -187,7 +189,9 @@ typedef struct {
WDD_FTYPE(driver_connected) *driver_connected;
WDD_FTYPE(driver_caller) *driver_caller;
WDD_FTYPE(driver_mk_term_nil) *driver_mk_term_nil;
+ WDD_FTYPE(erl_drv_output_term) *erl_drv_output_term;
WDD_FTYPE(driver_output_term) *driver_output_term;
+ WDD_FTYPE(erl_drv_send_term) *erl_drv_send_term;
WDD_FTYPE(driver_send_term) *driver_send_term;
WDD_FTYPE(driver_async) *driver_async;
WDD_FTYPE(driver_async_cancel) *driver_async_cancel;
@@ -294,7 +298,9 @@ extern TWinDynDriverCallbacks WinDynDriverCallbacks;
#define driver_connected (WinDynDriverCallbacks.driver_connected)
#define driver_caller (WinDynDriverCallbacks.driver_caller)
#define driver_mk_term_nil (WinDynDriverCallbacks.driver_mk_term_nil)
+#define erl_drv_output_term (WinDynDriverCallbacks.erl_drv_output_term)
#define driver_output_term (WinDynDriverCallbacks.driver_output_term)
+#define erl_drv_send_term (WinDynDriverCallbacks.erl_drv_send_term)
#define driver_send_term (WinDynDriverCallbacks.driver_send_term)
#define driver_async (WinDynDriverCallbacks.driver_async)
#define driver_async_cancel (WinDynDriverCallbacks.driver_async_cancel)
@@ -425,7 +431,9 @@ do { \
((W).driver_connected) = driver_connected; \
((W).driver_caller) = driver_caller; \
((W).driver_mk_term_nil) = driver_mk_term_nil; \
+((W).erl_drv_output_term) = erl_drv_output_term; \
((W).driver_output_term) = driver_output_term; \
+((W).erl_drv_send_term) = erl_drv_send_term; \
((W).driver_send_term) = driver_send_term; \
((W).driver_async) = driver_async; \
((W).driver_async_cancel) = driver_async_cancel; \
diff --git a/erts/emulator/sys/win32/erl_win_sys.h b/erts/emulator/sys/win32/erl_win_sys.h
index 21c52ca26d..5ce1a61303 100644
--- a/erts/emulator/sys/win32/erl_win_sys.h
+++ b/erts/emulator/sys/win32/erl_win_sys.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1997-2011. All Rights Reserved.
+ * Copyright Ericsson AB 1997-2012. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -176,8 +176,8 @@ extern clock_t sys_times(SysTimes *buffer);
extern char *win_build_environment(char *);
typedef struct {
- char *environment_strings;
- char *next_string;
+ WCHAR *environment_strings;
+ WCHAR *next_string;
} GETENV_STATE;
void erts_sys_env_init(void);
diff --git a/erts/emulator/sys/win32/sys.c b/erts/emulator/sys/win32/sys.c
index acbbfc2ce9..1cd9072cea 100755
--- a/erts/emulator/sys/win32/sys.c
+++ b/erts/emulator/sys/win32/sys.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2011. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2012. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -87,9 +87,6 @@ static erts_smp_tsd_key_t win32_errstr_key;
static erts_smp_atomic_t pipe_creation_counter;
-static erts_smp_mtx_t sys_driver_data_lock;
-
-
/* Results from application_type(_w) is one of */
#define APPL_NONE 0
#define APPL_DOS 1
@@ -97,7 +94,6 @@ static erts_smp_mtx_t sys_driver_data_lock;
#define APPL_WIN32 3
static int driver_write(long, HANDLE, byte*, int);
-static void common_stop(int);
static int create_file_thread(struct async_io* aio, int mode);
#ifdef ERTS_SMP
static void close_active_handle(ErlDrvPort, HANDLE handle);
@@ -115,9 +111,6 @@ BOOL WINAPI ctrl_handler(DWORD dwCtrlType);
#define PORT_BUFSIZ 4096
-#define PORT_FREE (-1)
-#define PORT_EXITING (-2)
-
#define DRV_BUF_ALLOC(SZ) \
erts_alloc_fnf(ERTS_ALC_T_DRV_DATA_BUF, (SZ))
#define DRV_BUF_REALLOC(P, SZ) \
@@ -255,11 +248,30 @@ void erl_sys_args(int* argc, char** argv)
#endif
}
-void
-erts_sys_prepare_crash_dump(void)
+int erts_sys_prepare_crash_dump(int secs)
{
+ Port *heart_port;
+ Eterm heap[3];
+ Eterm *hp = heap;
+ Eterm list = NIL;
+
+ heart_port = erts_get_heart_port();
+
+ if (heart_port) {
+
+ list = CONS(hp, make_small(8), list); hp += 2;
+
+ /* send to heart port, CMD = 8, i.e. prepare crash dump =o */
+ erts_port_output(NULL, ERTS_PORT_SIG_FLG_FORCE_IMM_CALL, heart_port,
+ heart_port->common.id, list, NULL);
+
+ return 1;
+ }
+
/* Windows - free file descriptors are hopefully available */
- return;
+ /* Alarm not used on windows */
+
+ return 0;
}
static void
@@ -420,6 +432,8 @@ typedef struct async_io {
HANDLE ioAllowed; /* The thread will wait for this event
* before starting a new read or write.
*/
+ HANDLE flushEvent; /* Used to signal that a flush should be done. */
+ HANDLE flushReplyEvent; /* Used to signal that a flush has been done. */
DWORD pendingError; /* Used to delay presentating an error to Erlang
* until the check_io function is entered.
*/
@@ -454,7 +468,7 @@ typedef struct driver_data {
byte *inbuf; /* Buffer to use for overlapped read. */
int outBufSize; /* Size of output buffer. */
byte *outbuf; /* Buffer to use for overlapped write. */
- ErlDrvPort port_num; /* The port number. */
+ ErlDrvPort port_num; /* The port handle. */
int packet_bytes; /* 0: continous stream, 1, 2, or 4: the number
* of bytes in the packet header.
*/
@@ -464,8 +478,6 @@ typedef struct driver_data {
int report_exit; /* Do report exit status for the port */
} DriverData;
-static DriverData* driver_data; /* Pointer to array of driver data. */
-
/* Driver interfaces */
static ErlDrvData spawn_start(ErlDrvPort, char*, SysDriverOpts*);
static ErlDrvData fd_start(ErlDrvPort, char*, SysDriverOpts*);
@@ -577,67 +589,53 @@ struct erl_drv_entry vanilla_driver_entry = {
*/
static DriverData*
-new_driver_data(int port_num, int packet_bytes, int wait_objs_required, int use_threads)
+new_driver_data(ErlDrvPort port_num, int packet_bytes, int wait_objs_required, int use_threads)
{
DriverData* dp;
-
- erts_smp_mtx_lock(&sys_driver_data_lock);
- DEBUGF(("new_driver_data(port_num %d, pb %d)\n",
- port_num, packet_bytes));
+ DEBUGF(("new_driver_data(%p, pb %d)\n", port_num, packet_bytes));
+ dp = driver_alloc(sizeof(DriverData));
+ if (!dp)
+ return NULL;
/*
* We used to test first at all that there is enough room in the
* array used by WaitForMultipleObjects(), but that is not necessary
* any more, since driver_select() can't fail.
*/
- /*
- * Search for a free slot.
- */
+ dp->bytesInBuffer = 0;
+ dp->totalNeeded = packet_bytes;
+ dp->inBufSize = PORT_BUFSIZ;
+ dp->inbuf = DRV_BUF_ALLOC(dp->inBufSize);
+ if (dp->inbuf == NULL)
+ goto buf_alloc_error;
+ erts_smp_atomic_add_nob(&sys_misc_mem_sz, dp->inBufSize);
+ dp->outBufSize = 0;
+ dp->outbuf = NULL;
+ dp->port_num = port_num;
+ dp->packet_bytes = packet_bytes;
+ dp->port_pid = INVALID_HANDLE_VALUE;
+ if (init_async_io(&dp->in, use_threads) == -1)
+ goto async_io_error1;
+ if (init_async_io(&dp->out, use_threads) == -1)
+ goto async_io_error2;
- for (dp = driver_data; dp < driver_data+max_files; dp++) {
- if (dp->port_num == PORT_FREE) {
- dp->bytesInBuffer = 0;
- dp->totalNeeded = packet_bytes;
- dp->inBufSize = PORT_BUFSIZ;
- dp->inbuf = DRV_BUF_ALLOC(dp->inBufSize);
- if (dp->inbuf == NULL) {
- erts_smp_mtx_unlock(&sys_driver_data_lock);
- return NULL;
- }
- erts_smp_atomic_add_nob(&sys_misc_mem_sz, dp->inBufSize);
- dp->outBufSize = 0;
- dp->outbuf = NULL;
- dp->port_num = port_num;
- dp->packet_bytes = packet_bytes;
- dp->port_pid = INVALID_HANDLE_VALUE;
- if (init_async_io(&dp->in, use_threads) == -1)
- break;
- if (init_async_io(&dp->out, use_threads) == -1)
- break;
- erts_smp_mtx_unlock(&sys_driver_data_lock);
- return dp;
- }
- }
+ return dp;
- /*
- * Error or no free driver data.
- */
+async_io_error2:
+ release_async_io(&dp->in, dp->port_num);
+async_io_error1:
+ release_async_io(&dp->out, dp->port_num);
- if (dp < driver_data+max_files) {
- release_async_io(&dp->in, dp->port_num);
- release_async_io(&dp->out, dp->port_num);
- }
- erts_smp_mtx_unlock(&sys_driver_data_lock);
+buf_alloc_error:
+ driver_free(dp);
return NULL;
}
static void
release_driver_data(DriverData* dp)
{
- erts_smp_mtx_lock(&sys_driver_data_lock);
-
#ifdef ERTS_SMP
#ifdef USE_CANCELIOEX
if (fpCancelIoEx != NULL) {
@@ -721,8 +719,7 @@ release_driver_data(DriverData* dp)
* the exit thread.
*/
- dp->port_num = PORT_FREE;
- erts_smp_mtx_unlock(&sys_driver_data_lock);
+ driver_free(dp);
}
#ifdef ERTS_SMP
@@ -817,7 +814,6 @@ threaded_handle_closer(LPVOID param)
static ErlDrvData
set_driver_data(DriverData* dp, HANDLE ifd, HANDLE ofd, int read_write, int report_exit)
{
- int index = dp - driver_data;
int result;
dp->in.fd = ifd;
@@ -836,13 +832,12 @@ set_driver_data(DriverData* dp, HANDLE ifd, HANDLE ofd, int read_write, int repo
ERL_DRV_WRITE|ERL_DRV_USE, 1);
ASSERT(result != -1);
}
- return (ErlDrvData)index;
+ return (ErlDrvData) dp;
}
static ErlDrvData
reuse_driver_data(DriverData *dp, HANDLE ifd, HANDLE ofd, int read_write, ErlDrvPort port_num)
{
- int index = dp - driver_data;
int result;
dp->port_num = port_num;
@@ -861,7 +856,7 @@ reuse_driver_data(DriverData *dp, HANDLE ifd, HANDLE ofd, int read_write, ErlDrv
ERL_DRV_WRITE|ERL_DRV_USE, 1);
ASSERT(result != -1);
}
- return (ErlDrvData)index;
+ return (ErlDrvData) dp;
}
/*
@@ -878,6 +873,8 @@ init_async_io(AsyncIo* aio, int use_threads)
aio->ov.Offset = 0L;
aio->ov.OffsetHigh = 0L;
aio->ioAllowed = NULL;
+ aio->flushEvent = NULL;
+ aio->flushReplyEvent = NULL;
aio->pendingError = 0;
aio->bytesTransferred = 0;
#ifdef ERTS_SMP
@@ -890,6 +887,12 @@ init_async_io(AsyncIo* aio, int use_threads)
aio->ioAllowed = CreateAutoEvent(FALSE);
if (aio->ioAllowed == NULL)
return -1;
+ aio->flushEvent = CreateAutoEvent(FALSE);
+ if (aio->flushEvent == NULL)
+ return -1;
+ aio->flushReplyEvent = CreateAutoEvent(FALSE);
+ if (aio->flushReplyEvent == NULL)
+ return -1;
}
return 0;
}
@@ -923,6 +926,14 @@ release_async_io(AsyncIo* aio, ErlDrvPort port_num)
if (aio->ioAllowed != NULL)
CloseHandle(aio->ioAllowed);
aio->ioAllowed = NULL;
+
+ if (aio->flushEvent != NULL)
+ CloseHandle(aio->flushEvent);
+ aio->flushEvent = NULL;
+
+ if (aio->flushReplyEvent != NULL)
+ CloseHandle(aio->flushReplyEvent);
+ aio->flushReplyEvent = NULL;
}
/* ----------------------------------------------------------------------
@@ -1118,12 +1129,6 @@ spawn_init(void)
((module != NULL) ? GetProcAddress(module,"CancelIoEx") : NULL);
DEBUGF(("fpCancelIoEx = %p\r\n", fpCancelIoEx));
#endif
- driver_data = (struct driver_data *)
- erts_alloc(ERTS_ALC_T_DRV_TAB, max_files * sizeof(struct driver_data));
- erts_smp_atomic_add_nob(&sys_misc_mem_sz,
- max_files*sizeof(struct driver_data));
- for (i = 0; i < max_files; i++)
- driver_data[i].port_num = PORT_FREE;
return 0;
}
@@ -1204,9 +1209,7 @@ spawn_start(ErlDrvPort port_num, char* name, SysDriverOpts* opts)
*/
DEBUGF(("Spawning \"%s\"\n", name));
- envir = win_build_environment(envir); /* Still an ansi environment, could be
- converted to unicode for spawn_executable, but
- that is not done (yet) */
+ envir = win_build_environment(envir); /* Always a unicode environment */
ok = create_child_process(name,
hChildStdin,
hChildStdout,
@@ -1256,9 +1259,12 @@ spawn_start(ErlDrvPort port_num, char* name, SysDriverOpts* opts)
#endif
retval = set_driver_data(dp, hFromChild, hToChild, opts->read_write,
opts->exit_status);
- if (retval != ERL_DRV_ERROR_GENERAL && retval != ERL_DRV_ERROR_ERRNO)
- /* We assume that this cannot generate a negative number */
- erts_port[port_num].os_pid = (SWord) pid;
+ if (retval != ERL_DRV_ERROR_GENERAL && retval != ERL_DRV_ERROR_ERRNO) {
+ Port *prt = erts_drvport2port_raw(port_num);
+ /* We assume that this cannot generate a negative number */
+ ASSERT(prt);
+ prt->os_pid = (SWord) pid;
+ }
}
if (retval != ERL_DRV_ERROR_GENERAL && retval != ERL_DRV_ERROR_ERRNO)
@@ -1485,7 +1491,8 @@ create_child_process
NULL,
NULL,
TRUE,
- createFlags | staticCreateFlags,
+ createFlags | staticCreateFlags |
+ CREATE_UNICODE_ENVIRONMENT,
env,
wd,
&siStartInfo,
@@ -1613,7 +1620,8 @@ create_child_process
NULL,
NULL,
TRUE,
- createFlags | staticCreateFlags,
+ createFlags | staticCreateFlags |
+ CREATE_UNICODE_ENVIRONMENT,
env,
(WCHAR *) wd,
&siStartInfo,
@@ -2083,16 +2091,26 @@ threaded_writer(LPVOID param)
AsyncIo* aio = (AsyncIo *) param;
HANDLE thread = GetCurrentThread();
char* buf;
- DWORD numToWrite;
+ DWORD numToWrite, handle;
int ok;
+ HANDLE handles[2];
+ handles[0] = aio->ioAllowed;
+ handles[1] = aio->flushEvent;
for (;;) {
- WaitForSingleObject(aio->ioAllowed, INFINITE);
+ handle = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
if (aio->flags & DF_EXIT_THREAD)
break;
+
buf = OV_BUFFER_PTR(aio);
numToWrite = OV_NUM_TO_READ(aio);
aio->pendingError = 0;
+
+ if (handle == (WAIT_OBJECT_0 + 1) && numToWrite == 0) {
+ SetEvent(aio->flushReplyEvent);
+ continue;
+ }
+
ok = WriteFile(aio->fd, buf, numToWrite, &aio->bytesTransferred, NULL);
if (!ok) {
aio->pendingError = GetLastError();
@@ -2127,7 +2145,11 @@ threaded_writer(LPVOID param)
}
}
}
- SetEvent(aio->ov.hEvent);
+ OV_NUM_TO_READ(aio) = 0;
+ if (handle == (WAIT_OBJECT_0 + 1))
+ SetEvent(aio->flushReplyEvent);
+ else
+ SetEvent(aio->ov.hEvent);
if (aio->pendingError != NO_ERROR || aio->bytesTransferred == 0)
break;
if (aio->flags & DF_EXIT_THREAD)
@@ -2193,13 +2215,48 @@ fd_start(ErlDrvPort port_num, char* name, SysDriverOpts* opts)
if ((dp = new_driver_data(port_num, opts->packet_bytes, 2, TRUE)) == NULL)
return ERL_DRV_ERROR_GENERAL;
+ /**
+ * Here is a brief description about how the fd driver works on windows.
+ *
+ * fd_init:
+ * For each in/out fd pair a threaded_reader and threaded_writer thread is
+ * created. Within the DriverData struct each of the threads have an AsyncIO
+ * sctruct associated with it. Within AsyncIO there are two important HANDLEs,
+ * ioAllowed and ov.hEvent. ioAllowed is used to signal the threaded_* threads
+ * should read/write some data, and ov.hEvent is driver_select'ed to be used to
+ * signal that the thread is done reading/writing.
+ *
+ * The reason for the driver being threaded like this is because once the FD is open
+ * on windows, it is not possible to set the it in overlapped mode. So we have to
+ * simulate this using threads.
+ *
+ * output:
+ * When an output occurs the data to be outputted is copied to AsyncIO.ov. Then
+ * the ioAllowed HANDLE is set, ov.hEvent is cleared and the port is marked as busy.
+ * The threaded_writer thread is lying in WaitForMultipleObjects on ioAllowed, and
+ * when signalled it writes all data in AsyncIO.ov and then sets ov.hEvent so that
+ * ready_output gets triggered and (potentially) sends the reply to the port and
+ * marks the port an non-busy.
+ *
+ * input:
+ * The threaded_reader is lying waiting in ReadFile on the in fd and when a new
+ * line is written it sets ov.hEvent that new data is available and then goes
+ * and waits for ioAllowed to be set. ready_input is run when ov.hEvent is set and
+ * delivers the data to the port. Then ioAllowed is signalled again and threaded_reader
+ * goes back to ReadFile.
+ *
+ * shutdown:
+ * In order to guarantee that all io is outputted before the driver is stopped,
+ * fd_stop uses flushEvent and flushReplyEvent to make sure that there is no data
+ * in ov which needs writing before returning from fd_stop.
+ *
+ **/
+
if (!create_file_thread(&dp->in, DO_READ)) {
- dp->port_num = PORT_FREE;
return ERL_DRV_ERROR_GENERAL;
}
if (!create_file_thread(&dp->out, DO_WRITE)) {
- dp->port_num = PORT_FREE;
return ERL_DRV_ERROR_GENERAL;
}
@@ -2219,10 +2276,9 @@ fd_start(ErlDrvPort port_num, char* name, SysDriverOpts* opts)
}
}
-static void fd_stop(ErlDrvData d)
+static void fd_stop(ErlDrvData data)
{
- int fd = (int)d;
- DriverData* dp = driver_data+fd;
+ DriverData * dp = (DriverData *) data;
/*
* There's no way we can terminate an fd port in a consistent way.
* Instead we let it live until it's opened again (which it is,
@@ -2241,6 +2297,8 @@ static void fd_stop(ErlDrvData d)
(void) driver_select(dp->port_num,
(ErlDrvEvent)dp->out.ov.hEvent,
ERL_DRV_WRITE, 0);
+ SetEvent(dp->out.flushEvent);
+ WaitForSingleObject(dp->out.flushReplyEvent, INFINITE);
}
}
@@ -2283,16 +2341,10 @@ vanilla_start(ErlDrvPort port_num, char* name, SysDriverOpts* opts)
}
static void
-stop(ErlDrvData index)
-{
- common_stop((int)index);
-}
-
-static void common_stop(int index)
+stop(ErlDrvData data)
{
- DriverData* dp = driver_data+index;
-
- DEBUGF(("common_stop(%d)\n", index));
+ DriverData *dp = (DriverData *) data;
+ DEBUGF(("stop(%p)\n", dp));
if (dp->in.ov.hEvent != NULL) {
(void) driver_select(dp->port_num,
@@ -2314,7 +2366,6 @@ static void common_stop(int index)
*/
HANDLE thread;
DWORD tid;
- dp->port_num = PORT_EXITING;
thread = (HANDLE *) _beginthreadex(NULL, 0, threaded_exiter, dp, 0, &tid);
CloseHandle(thread);
}
@@ -2439,22 +2490,17 @@ threaded_exiter(LPVOID param)
static void
output(ErlDrvData drv_data, char* buf, ErlDrvSizeT len)
-/* long drv_data; /* The slot to use in the driver data table.
+/* ErlDrvData drv_data; /* The slot to use in the driver data table.
* For Windows NT, this is *NOT* a file handle.
* The handle is found in the driver data.
*/
/* char *buf; /* Pointer to data to write to the port program. */
/* ErlDrvSizeT len; /* Number of bytes to write. */
{
- DriverData* dp;
+ DriverData* dp = (DriverData *) drv_data;
int pb; /* The header size for this port. */
- int port_num; /* The actual port number (for diagnostics). */
char* current;
- dp = driver_data + (int)drv_data;
- if ((port_num = dp->port_num) == -1)
- return ; /*-1;*/
-
pb = dp->packet_bytes;
if ((pb+len) == 0)
@@ -2465,7 +2511,7 @@ output(ErlDrvData drv_data, char* buf, ErlDrvSizeT len)
*/
if ((pb == 2 && len > 65535) || (pb == 1 && len > 255)) {
- driver_failure_posix(port_num, EINVAL);
+ driver_failure_posix(dp->port_num, EINVAL);
return ; /* -1; */
}
@@ -2479,7 +2525,7 @@ output(ErlDrvData drv_data, char* buf, ErlDrvSizeT len)
ASSERT(!dp->outbuf);
dp->outbuf = DRV_BUF_ALLOC(pb+len);
if (!dp->outbuf) {
- driver_failure_posix(port_num, ENOMEM);
+ driver_failure_posix(dp->port_num, ENOMEM);
return ; /* -1; */
}
@@ -2509,7 +2555,7 @@ output(ErlDrvData drv_data, char* buf, ErlDrvSizeT len)
memcpy(current, buf, len);
if (!async_write_file(&dp->out, dp->outbuf, pb+len)) {
- set_busy_port(port_num, 1);
+ set_busy_port(dp->port_num, 1);
} else {
dp->out.ov.Offset += pb+len; /* For vanilla driver. */
/* XXX OffsetHigh should be changed too. */
@@ -2544,10 +2590,9 @@ ready_input(ErlDrvData drv_data, ErlDrvEvent ready_event)
{
int error = 0; /* The error code (assume initially no errors). */
DWORD bytesRead; /* Number of bytes read. */
- DriverData* dp;
+ DriverData* dp = (DriverData *) drv_data;
int pb;
- dp = driver_data+(int)drv_data;
pb = dp->packet_bytes;
#ifdef ERTS_SMP
if(dp->in.thread == (HANDLE) -1) {
@@ -2715,7 +2760,7 @@ static void
ready_output(ErlDrvData drv_data, ErlDrvEvent ready_event)
{
DWORD bytesWritten;
- DriverData* dp = driver_data + (int)drv_data;
+ DriverData *dp = (DriverData *) drv_data;
int error;
#ifdef ERTS_SMP
@@ -2723,7 +2768,7 @@ ready_output(ErlDrvData drv_data, ErlDrvEvent ready_event)
dp->out.async_io_active = 0;
}
#endif
- DEBUGF(("ready_output(%d, 0x%x)\n", drv_data, ready_event));
+ DEBUGF(("ready_output(%p, 0x%x)\n", drv_data, ready_event));
set_busy_port(dp->port_num, 0);
if (!(dp->outbuf)) {
/* Happens because event sometimes get signalled during a successful
@@ -2764,10 +2809,10 @@ static void stop_select(ErlDrvEvent e, void* _)
** no interpretation of this should be done by the rest of the
** emulator. The buffer should be at least 21 bytes long.
*/
-void sys_get_pid(char *buffer){
+void sys_get_pid(char *buffer, size_t buffer_size){
DWORD p = GetCurrentProcessId();
/* The pid is scalar and is an unsigned long. */
- sprintf(buffer,"%lu",(unsigned long) p);
+ erts_snprintf(buffer, buffer_size, "%lu",(unsigned long) p);
}
void
@@ -2778,7 +2823,7 @@ sys_init_io(void)
can change our view of the number of open files possible.
We estimate the number to twice the amount of ports.
We really dont know on windows, do we? */
- max_files = 2*erts_max_ports;
+ max_files = 2*erts_ptab_max(&erts_port);
}
#ifdef ERTS_SMP
@@ -3107,7 +3152,8 @@ erl_assert_error(char* expr, char* file, int line)
{
char message[1024];
- sprintf(message, "File %hs, line %d: %hs", file, line, expr);
+ erts_snprintf(message, sizeof(message),
+ "File %hs, line %d: %hs", file, line, expr);
MessageBox(GetActiveWindow(), message, "Assertion failed",
MB_OK | MB_ICONERROR);
#if 0
@@ -3232,9 +3278,6 @@ void erl_sys_init(void)
noinherit_std_handle(STD_INPUT_HANDLE);
noinherit_std_handle(STD_ERROR_HANDLE);
-
- erts_smp_mtx_init(&sys_driver_data_lock, "sys_driver_data_lock");
-
#ifdef ERTS_SMP
erts_smp_tsd_key_create(&win32_errstr_key);
InitializeCriticalSection(&htbc_lock);
diff --git a/erts/emulator/sys/win32/sys_env.c b/erts/emulator/sys/win32/sys_env.c
index 064745d418..754f4c6e4c 100644
--- a/erts/emulator/sys/win32/sys_env.c
+++ b/erts/emulator/sys/win32/sys_env.c
@@ -1,271 +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%
- */
-
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-
-#include "sys.h"
-#include "erl_sys_driver.h"
-#include "erl_alloc.h"
-
-static char* merge_environment(char *current, char *add);
-static char* arg_to_env(char **arg);
-static char** env_to_arg(char *env);
-static char** find_arg(char **arg, char *str);
-static int compare(const void *a, const void *b);
-
-static erts_smp_rwmtx_t environ_rwmtx;
-
-void
-erts_sys_env_init(void)
-{
- erts_smp_rwmtx_init(&environ_rwmtx, "environ");
-}
-
-int
-erts_sys_putenv(char *key_value, int sep_ix)
-{
- int res;
- char sep = key_value[sep_ix];
- ASSERT(sep == '=');
- key_value[sep_ix] = '\0';
- erts_smp_rwmtx_rwlock(&environ_rwmtx);
- res = (SetEnvironmentVariable((LPCTSTR) key_value,
- (LPCTSTR) &key_value[sep_ix+1]) ? 0 : 1);
- erts_smp_rwmtx_rwunlock(&environ_rwmtx);
- key_value[sep_ix] = sep;
- return res;
-}
-
-int
-erts_sys_getenv__(char *key, char *value, size_t *size)
-{
- size_t req_size = 0;
- int res = 0;
- DWORD new_size;
-
- SetLastError(0);
- new_size = GetEnvironmentVariable((LPCTSTR) key,
- (LPTSTR) value,
- (DWORD) *size);
- res = !new_size && GetLastError() == ERROR_ENVVAR_NOT_FOUND ? -1 : 0;
- if (res < 0)
- return res;
- res = new_size > *size ? 1 : 0;
- *size = new_size;
- return res;
-}
-
-int
-erts_sys_getenv(char *key, char *value, size_t *size)
-{
- int res;
- erts_smp_rwmtx_rlock(&environ_rwmtx);
- res = erts_sys_getenv__(key, value, size);
- erts_smp_rwmtx_runlock(&environ_rwmtx);
- return res;
-}
-
-struct win32_getenv_state {
- char *env;
- char *next;
-};
-
-
-void init_getenv_state(GETENV_STATE *state)
-{
- erts_smp_rwmtx_rlock(&environ_rwmtx);
- state->environment_strings = (char *) GetEnvironmentStrings();
- state->next_string = state->environment_strings;
-}
-
-char *getenv_string(GETENV_STATE *state)
-{
- ERTS_SMP_LC_ASSERT(erts_smp_lc_rwmtx_is_rlocked(&environ_rwmtx));
- if (state->next_string[0] == '\0')
- return NULL;
- else {
- char *res = state->next_string;
- state->next_string += sys_strlen(res) + 1;
- return res;
- }
-}
-
-void fini_getenv_state(GETENV_STATE *state)
-{
- FreeEnvironmentStrings(state->environment_strings);
- state->environment_strings = state->next_string = NULL;
- erts_smp_rwmtx_runlock(&environ_rwmtx);
-}
-
-char*
-win_build_environment(char* new_env)
-{
- if (new_env == NULL) {
- return NULL;
- } else {
- char *tmp, *merged;
-
- erts_smp_rwmtx_rlock(&environ_rwmtx);
- tmp = GetEnvironmentStrings();
- merged = merge_environment(tmp, new_env);
-
- FreeEnvironmentStrings(tmp);
- erts_smp_rwmtx_runlock(&environ_rwmtx);
- return merged;
- }
-}
-
-static char*
-merge_environment(char *old, char *add)
-{
- char **a_arg = env_to_arg(add);
- char **c_arg = env_to_arg(old);
- char *ret;
- int i, j;
-
- for(i = 0; c_arg[i] != NULL; ++i)
- ;
-
- for(j = 0; a_arg[j] != NULL; ++j)
- ;
-
- c_arg = erts_realloc(ERTS_ALC_T_TMP,
- c_arg, (i+j+1) * sizeof(char *));
-
- for(j = 0; a_arg[j] != NULL; ++j){
- char **tmp;
- char *current = a_arg[j];
- char *eq_p = strchr(current,'=');
- int unset = (eq_p!=NULL && eq_p[1]=='\0');
-
- if ((tmp = find_arg(c_arg, current)) != NULL) {
- if (!unset) {
- *tmp = current;
- } else {
- *tmp = c_arg[--i];
- c_arg[i] = NULL;
- }
- } else if (!unset) {
- c_arg[i++] = current;
- c_arg[i] = NULL;
- }
- }
- ret = arg_to_env(c_arg);
- erts_free(ERTS_ALC_T_TMP, c_arg);
- erts_free(ERTS_ALC_T_TMP, a_arg);
- return ret;
-}
-
-static char**
-find_arg(char **arg, char *str)
-{
- char *tmp;
- int len;
-
- if ((tmp = strchr(str, '=')) != NULL) {
- tmp++;
- len = tmp - str;
- while (*arg != NULL){
- if (_strnicmp(*arg, str, len) == 0){
- return arg;
- }
- ++arg;
- }
- }
- return NULL;
-}
-
-static int
-compare(const void *a, const void *b)
-{
- char *s1 = *((char **) a);
- char *s2 = *((char **) b);
- char *e1 = strchr(s1,'=');
- char *e2 = strchr(s2,'=');
- int ret;
- int len;
-
- if(!e1)
- e1 = s1 + strlen(s1);
- if(!e2)
- e2 = s2 + strlen(s2);
-
- if((e1 - s1) > (e2 - s2))
- len = (e2 - s2);
- else
- len = (e1 - s1);
-
- ret = _strnicmp(s1,s2,len);
- if (ret == 0)
- return ((e1 - s1) - (e2 - s2));
- else
- return ret;
-}
-
-static char**
-env_to_arg(char *env)
-{
- char **ret;
- char *tmp;
- int i;
- int num_strings = 0;
-
- for(tmp = env; *tmp != '\0'; tmp += strlen(tmp)+1) {
- ++num_strings;
- }
- ret = erts_alloc(ERTS_ALC_T_TMP, sizeof(char *) * (num_strings + 1));
- i = 0;
- for(tmp = env; *tmp != '\0'; tmp += strlen(tmp)+1){
- ret[i++] = tmp;
- }
- ret[i] = NULL;
- return ret;
-}
-
-static char*
-arg_to_env(char **arg)
-{
- char *block;
- char *ptr;
- int i;
- int totlen = 1; /* extra '\0' */
-
- for(i = 0; arg[i] != NULL; ++i) {
- totlen += strlen(arg[i])+1;
- }
-
- /* sort the environment vector */
- qsort(arg, i, sizeof(char *), &compare);
-
- if (totlen == 1){
- block = erts_alloc(ERTS_ALC_T_ENVIRONMENT, 2);
- block[0] = block[1] = '\0';
- } else {
- block = erts_alloc(ERTS_ALC_T_ENVIRONMENT, totlen);
- ptr = block;
- for(i=0; arg[i] != NULL; ++i){
- strcpy(ptr, arg[i]);
- ptr += strlen(ptr)+1;
- }
- *ptr = '\0';
- }
- return block;
-}
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2002-2012. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "sys.h"
+#include "erl_sys_driver.h"
+#include "erl_alloc.h"
+
+static WCHAR *merge_environment(WCHAR *current, WCHAR *add);
+static WCHAR *arg_to_env(WCHAR **arg);
+static WCHAR **env_to_arg(WCHAR *env);
+static WCHAR **find_arg(WCHAR **arg, WCHAR *str);
+static int compare(const void *a, const void *b);
+
+static erts_smp_rwmtx_t environ_rwmtx;
+
+void
+erts_sys_env_init(void)
+{
+ erts_smp_rwmtx_init(&environ_rwmtx, "environ");
+}
+
+int
+erts_sys_putenv_raw(char *key, char *value)
+{
+ int res;
+ erts_smp_rwmtx_rwlock(&environ_rwmtx);
+ res = (SetEnvironmentVariable((LPCTSTR) key,
+ (LPCTSTR) value) ? 0 : 1);
+ erts_smp_rwmtx_rwunlock(&environ_rwmtx);
+ return res;
+}
+
+int
+erts_sys_putenv(char *key, char *value)
+{
+ int res;
+ WCHAR *wkey = (WCHAR *) key;
+ WCHAR *wvalue = (WCHAR *) value;
+ erts_smp_rwmtx_rwlock(&environ_rwmtx);
+ res = (SetEnvironmentVariableW(wkey,
+ wvalue) ? 0 : 1);
+ erts_smp_rwmtx_rwunlock(&environ_rwmtx);
+ return res;
+}
+
+int
+erts_sys_getenv(char *key, char *value, size_t *size)
+{
+ size_t req_size = 0;
+ int res = 0;
+ DWORD new_size;
+ WCHAR *wkey = (WCHAR *) key;
+ WCHAR *wvalue = (WCHAR *) value;
+ DWORD wsize = *size / (sizeof(WCHAR) / sizeof(char));
+
+ SetLastError(0);
+ erts_smp_rwmtx_rlock(&environ_rwmtx);
+ new_size = GetEnvironmentVariableW(wkey,
+ wvalue,
+ (DWORD) wsize);
+ res = !new_size && GetLastError() == ERROR_ENVVAR_NOT_FOUND ? -1 : 0;
+ erts_smp_rwmtx_runlock(&environ_rwmtx);
+ if (res < 0)
+ return res;
+ res = new_size > wsize ? 1 : 0;
+ *size = new_size * (sizeof(WCHAR) / sizeof(char));
+ return res;
+}
+int
+erts_sys_getenv__(char *key, char *value, size_t *size)
+{
+ size_t req_size = 0;
+ int res = 0;
+ DWORD new_size;
+
+ SetLastError(0);
+ new_size = GetEnvironmentVariable((LPCTSTR) key,
+ (LPTSTR) value,
+ (DWORD) *size);
+ res = !new_size && GetLastError() == ERROR_ENVVAR_NOT_FOUND ? -1 : 0;
+ if (res < 0)
+ return res;
+ res = new_size > *size ? 1 : 0;
+ *size = new_size;
+ return res;
+}
+
+int
+erts_sys_getenv_raw(char *key, char *value, size_t *size)
+{
+ int res;
+ erts_smp_rwmtx_rlock(&environ_rwmtx);
+ res = erts_sys_getenv__(key, value, size);
+ erts_smp_rwmtx_runlock(&environ_rwmtx);
+ return res;
+}
+
+void init_getenv_state(GETENV_STATE *state)
+{
+ erts_smp_rwmtx_rlock(&environ_rwmtx);
+ state->environment_strings = GetEnvironmentStringsW();
+ state->next_string = state->environment_strings;
+}
+
+char *getenv_string(GETENV_STATE *state)
+{
+ ERTS_SMP_LC_ASSERT(erts_smp_lc_rwmtx_is_rlocked(&environ_rwmtx));
+ if (state->next_string[0] == L'\0') {
+ return NULL;
+ } else {
+ WCHAR *res = state->next_string;
+ state->next_string += wcslen(res) + 1;
+ return (char *) res;
+ }
+}
+
+void fini_getenv_state(GETENV_STATE *state)
+{
+ FreeEnvironmentStringsW(state->environment_strings);
+ state->environment_strings = state->next_string = NULL;
+ erts_smp_rwmtx_runlock(&environ_rwmtx);
+}
+
+char*
+win_build_environment(char* new_env)
+{
+ if (new_env == NULL) {
+ return NULL;
+ } else {
+ WCHAR *tmp, *merged, *tmp_new;
+
+ tmp_new = (WCHAR *) new_env;
+
+ erts_smp_rwmtx_rlock(&environ_rwmtx);
+ tmp = GetEnvironmentStringsW();
+ merged = merge_environment(tmp, tmp_new);
+
+ FreeEnvironmentStringsW(tmp);
+ erts_smp_rwmtx_runlock(&environ_rwmtx);
+ return (char *) merged;
+ }
+}
+
+static WCHAR *
+merge_environment(WCHAR *old, WCHAR *add)
+{
+ WCHAR **a_arg = env_to_arg(add);
+ WCHAR **c_arg = env_to_arg(old);
+ WCHAR *ret;
+ int i, j;
+
+ for(i = 0; c_arg[i] != NULL; ++i)
+ ;
+
+ for(j = 0; a_arg[j] != NULL; ++j)
+ ;
+
+ c_arg = erts_realloc(ERTS_ALC_T_TMP,
+ c_arg, (i+j+1) * sizeof(WCHAR *));
+
+ for(j = 0; a_arg[j] != NULL; ++j){
+ WCHAR **tmp;
+ WCHAR *current = a_arg[j];
+ WCHAR *eq_p = wcschr(current,L'=');
+ int unset = (eq_p!=NULL && eq_p[1]==L'\0');
+
+ if ((tmp = find_arg(c_arg, current)) != NULL) {
+ if (!unset) {
+ *tmp = current;
+ } else {
+ *tmp = c_arg[--i];
+ c_arg[i] = NULL;
+ }
+ } else if (!unset) {
+ c_arg[i++] = current;
+ c_arg[i] = NULL;
+ }
+ }
+ ret = arg_to_env(c_arg);
+ erts_free(ERTS_ALC_T_TMP, c_arg);
+ erts_free(ERTS_ALC_T_TMP, a_arg);
+ return ret;
+}
+
+static WCHAR**
+find_arg(WCHAR **arg, WCHAR *str)
+{
+ WCHAR *tmp;
+ int len;
+
+ if ((tmp = wcschr(str, L'=')) != NULL) {
+ tmp++;
+ len = tmp - str;
+ while (*arg != NULL){
+ if (_wcsnicmp(*arg, str, len) == 0){
+ return arg;
+ }
+ ++arg;
+ }
+ }
+ return NULL;
+}
+
+static int
+compare(const void *a, const void *b)
+{
+ WCHAR *s1 = *((WCHAR **) a);
+ WCHAR *s2 = *((WCHAR **) b);
+ WCHAR *e1 = wcschr(s1,L'=');
+ WCHAR *e2 = wcschr(s2,L'=');
+ int ret;
+ int len;
+
+ if(!e1)
+ e1 = s1 + wcslen(s1);
+ if(!e2)
+ e2 = s2 + wcslen(s2);
+
+ if((e1 - s1) > (e2 - s2))
+ len = (e2 - s2);
+ else
+ len = (e1 - s1);
+
+ ret = _wcsnicmp(s1,s2,len);
+ if (ret == 0)
+ return ((e1 - s1) - (e2 - s2));
+ else
+ return ret;
+}
+
+static WCHAR**
+env_to_arg(WCHAR *env)
+{
+ WCHAR **ret;
+ WCHAR *tmp;
+ int i;
+ int num_strings = 0;
+
+ for(tmp = env; *tmp != '\0'; tmp += wcslen(tmp)+1) {
+ ++num_strings;
+ }
+ ret = erts_alloc(ERTS_ALC_T_TMP, sizeof(WCHAR *) * (num_strings + 1));
+ i = 0;
+ for(tmp = env; *tmp != '\0'; tmp += wcslen(tmp)+1){
+ ret[i++] = tmp;
+ }
+ ret[i] = NULL;
+ return ret;
+}
+
+static WCHAR *
+arg_to_env(WCHAR **arg)
+{
+ WCHAR *block;
+ WCHAR *ptr;
+ int i;
+ int totlen = 1; /* extra '\0' */
+
+ for(i = 0; arg[i] != NULL; ++i) {
+ totlen += wcslen(arg[i])+1;
+ }
+
+ /* sort the environment vector */
+ qsort(arg, i, sizeof(WCHAR *), &compare);
+
+ if (totlen == 1){
+ block = erts_alloc(ERTS_ALC_T_ENVIRONMENT, 2 * sizeof(WCHAR));
+ block[0] = block[1] = '\0';
+ } else {
+ block = erts_alloc(ERTS_ALC_T_ENVIRONMENT, totlen * sizeof(WCHAR));
+ ptr = block;
+ for(i=0; arg[i] != NULL; ++i){
+ wcscpy(ptr, arg[i]);
+ ptr += wcslen(ptr)+1;
+ }
+ *ptr = '\0';
+ }
+ return block;
+}
diff --git a/erts/emulator/sys/win32/sys_float.c b/erts/emulator/sys/win32/sys_float.c
index 6558ad2d99..960edaa7a5 100644
--- a/erts/emulator/sys/win32/sys_float.c
+++ b/erts/emulator/sys/win32/sys_float.c
@@ -114,22 +114,23 @@ sys_chars_to_double(char *buf, double *fp)
/*
** Convert a double to ascii format 0.dddde[+|-]ddd
-** return number of characters converted
+** return number of characters converted or -1 if error.
*/
int
-sys_double_to_chars(double fp, char *buf)
+sys_double_to_chars_ext(double fp, char *buffer, size_t buffer_size, size_t decimals)
{
- char *s = buf;
-
- (void) sprintf(buf, "%.20e", fp);
+ char *s = buffer;
+
+ if (erts_snprintf(buffer, buffer_size, "%.*e", decimals, fp) >= buffer_size)
+ return -1;
/* Search upto decimal point */
if (*s == '+' || *s == '-') s++;
while (isdigit(*s)) s++;
if (*s == ',') *s++ = '.'; /* Replace ',' with '.' */
/* Scan to end of string */
while (*s) s++;
- return s-buf; /* i.e strlen(buf) */
+ return s-buffer; /* i.e strlen(buffer) */
}
int
diff --git a/erts/emulator/sys/win32/sys_interrupt.c b/erts/emulator/sys/win32/sys_interrupt.c
index 347c31053b..36c43e93da 100644
--- a/erts/emulator/sys/win32/sys_interrupt.c
+++ b/erts/emulator/sys/win32/sys_interrupt.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1997-2011. All Rights Reserved.
+ * Copyright Ericsson AB 1997-2012. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -75,11 +75,11 @@ BOOL WINAPI ctrl_handler_ignore_break(DWORD dwCtrlType)
return TRUE;
break;
case CTRL_LOGOFF_EVENT:
+ case CTRL_SHUTDOWN_EVENT:
if (nohup)
return TRUE;
/* else pour through... */
case CTRL_CLOSE_EVENT:
- case CTRL_SHUTDOWN_EVENT:
erl_exit(0, "");
break;
}
@@ -127,11 +127,11 @@ BOOL WINAPI ctrl_handler(DWORD dwCtrlType)
SetEvent(erts_sys_break_event);
break;
case CTRL_LOGOFF_EVENT:
+ case CTRL_SHUTDOWN_EVENT:
if (nohup)
return TRUE;
/* else pour through... */
case CTRL_CLOSE_EVENT:
- case CTRL_SHUTDOWN_EVENT:
erl_exit(0, "");
break;
}
diff --git a/erts/emulator/test/Makefile b/erts/emulator/test/Makefile
index 627aa01bbf..9594ab48b1 100644
--- a/erts/emulator/test/Makefile
+++ b/erts/emulator/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -138,10 +138,10 @@ TARGET_FILES = $(MODULES:%=$(EBIN)/%.$(EMULATOR))
EMAKEFILE=Emakefile
-TEST_SPEC_FILES = emulator.spec \
- emulator.spec.win \
- emulator.spec.vxworks \
- emulator.spec.ose
+TEST_SPEC_FILES= emulator.spec \
+ emulator.spec.win \
+ emulator_bench.spec
+
# ----------------------------------------------------
# Release directory specification
# ----------------------------------------------------
diff --git a/erts/emulator/test/alloc_SUITE_data/allocator_test.h b/erts/emulator/test/alloc_SUITE_data/allocator_test.h
index cd4a91d34a..c0396ddb61 100644
--- a/erts/emulator/test/alloc_SUITE_data/allocator_test.h
+++ b/erts/emulator/test/alloc_SUITE_data/allocator_test.h
@@ -60,9 +60,9 @@ typedef void* erts_cond;
#define IS_MMAP_C(C) ((Ulong) ALC_TEST1(0x00a, (C)))
#define C_SZ(C) ((Ulong) ALC_TEST1(0x00b, (C)))
#define SBC2BLK(A, C) ((Block_t *) ALC_TEST2(0x00c, (A), (C)))
-#define BLK2SBC(A, B) ((Carrier_t *) ALC_TEST2(0x00d, (A), (B)))
-#define MBC2FBLK(A, C) ((Block_t *) ALC_TEST2(0x00e, (A), (C)))
-#define FBLK2MBC(A, B) ((Carrier_t *) ALC_TEST2(0x00f, (A), (B)))
+#define BLK_TO_SBC(A, B) ((Carrier_t *) ALC_TEST2(0x00d, (A), (B)))
+#define MBC_TO_FIRST_BLK(A, C) ((Block_t *) ALC_TEST2(0x00e, (A), (C)))
+#define FIRST_BLK_TO_MBC(A, B) ((Carrier_t *) ALC_TEST2(0x00f, (A), (B)))
#define FIRST_MBC(A) ((Carrier_t *) ALC_TEST1(0x010, (A)))
#define LAST_MBC(A) ((Carrier_t *) ALC_TEST1(0x011, (A)))
#define FIRST_SBC(A) ((Carrier_t *) ALC_TEST1(0x012, (A)))
@@ -73,7 +73,7 @@ typedef void* erts_cond;
#define MIN_BLK_SZ(A) ((Ulong) ALC_TEST1(0x017, (A)))
#define NXT_BLK(B) ((Block_t *) ALC_TEST1(0x018, (B)))
#define PREV_BLK(B) ((Block_t *) ALC_TEST1(0x019, (B)))
-#define IS_FIRST_BLK(B) ((Ulong) ALC_TEST1(0x01a, (B)))
+#define IS_MBC_FIRST_BLK(A,B) ((Ulong) ALC_TEST2(0x01a, (A), (B)))
#define UNIT_SZ ((Ulong) ALC_TEST0(0x01b))
/* From erl_goodfit_alloc.c */
diff --git a/erts/emulator/test/alloc_SUITE_data/basic.c b/erts/emulator/test/alloc_SUITE_data/basic.c
index 4a5e888161..0c27665712 100644
--- a/erts/emulator/test/alloc_SUITE_data/basic.c
+++ b/erts/emulator/test/alloc_SUITE_data/basic.c
@@ -44,7 +44,7 @@ testcase_run(TestCaseState_t *tcs)
c = FIRST_MBC(a);
ASSERT(tcs, !NEXT_C(c));
- blk = MBC2FBLK(a, c);
+ blk = MBC_TO_FIRST_BLK(a, c);
ASSERT(tcs, IS_LAST_BLK(blk));
ASSERT(tcs, IS_FREE_BLK(blk));
diff --git a/erts/emulator/test/alloc_SUITE_data/bucket_mask.c b/erts/emulator/test/alloc_SUITE_data/bucket_mask.c
index 13af7d861a..34979cacf1 100644
--- a/erts/emulator/test/alloc_SUITE_data/bucket_mask.c
+++ b/erts/emulator/test/alloc_SUITE_data/bucket_mask.c
@@ -16,11 +16,19 @@
* $Id$
*/
+#include "erl_int_sizes_config.h"
+
#include "testcase_driver.h"
#include "allocator_test.h"
#include <stdio.h>
-#define SBCT (512*1024)
+#if defined(__WIN32__) && SIZEOF_VOID_P == 8
+/* Use larger threashold for win64 as block alignment
+ is 16 bytes and not 8 */
+#define SBCT ((1024*1024))
+#else
+#define SBCT ((512*1024))
+#endif
char *
testcase_name(void)
@@ -40,10 +48,16 @@ testcase_cleanup(TestCaseState_t *tcs)
void
testcase_run(TestCaseState_t *tcs)
{
- void *tmp;
- void **fence;
+ typedef struct linked_block {
+ struct linked_block* next;
+ }Linked;
+ Linked* link;
+ Linked* fence_list;
+ Linked* pad_list;
+ void* tmp;
void **blk;
Ulong sz;
+ Ulong residue;
Ulong smbcs;
int i;
int bi;
@@ -65,7 +79,7 @@ testcase_run(TestCaseState_t *tcs)
ASSERT(tcs, a);
min_blk_sz = MIN_BLK_SZ(a);
- smbcs = 2*(no_bkts*sizeof(void *) + min_blk_sz) + min_blk_sz;
+ smbcs = (no_bkts*sizeof(void *) + min_blk_sz) + min_blk_sz;
for (i = 0; i < no_bkts; i++) {
sz = BKT_MIN_SZ(a, i);
if (sz >= sbct)
@@ -90,26 +104,42 @@ testcase_run(TestCaseState_t *tcs)
tcs->extra = (void *) a;
ASSERT(tcs, a);
+
blk = (void **) ALLOC(a, no_bkts*sizeof(void *));
- fence = (void **) ALLOC(a, no_bkts*sizeof(void *));
- ASSERT(tcs, blk && fence);
+ ASSERT(tcs, blk);
+ fence_list = NULL;
testcase_printf(tcs, "Allocating blocks and fences\n");
for (i = 0; i < bi_tests; i++) {
sz = BKT_MIN_SZ(a, i);
blk[i] = ALLOC(a, sz - ablk_hdr_sz);
- fence[i] = ALLOC(a, 1);
- ASSERT(tcs, blk[i] && fence[i]);
+ link = (Linked*) ALLOC(a, sizeof(Linked));
+ ASSERT(tcs, blk[i] && link);
+ link->next = fence_list;
+ fence_list = link;
}
- tmp = (void *) UMEM2BLK(fence[bi_tests - 1]);
- tmp = (void *) NXT_BLK((Block_t *) tmp);
- ASSERT(tcs, IS_LAST_BLK(tmp));
- sz = BLK_SZ((Block_t *) tmp);
- testcase_printf(tcs, "Allocating leftover size = %lu\n", sz);
- tmp = ALLOC(a, sz - ablk_hdr_sz);
- ASSERT(tcs, tmp);
+ pad_list = 0;
+ do {
+ tmp = (void *) UMEM2BLK(link); /* last allocated */
+ tmp = (void *) NXT_BLK((Block_t *) tmp);
+ ASSERT(tcs, IS_LAST_BLK(tmp));
+ sz = BLK_SZ((Block_t *) tmp);
+ if (sz >= sbct) {
+ residue = sz;
+ sz = sbct - min_blk_sz;
+ residue -= sz;
+ }
+ else {
+ residue = 0;
+ }
+ testcase_printf(tcs, "Allocating leftover size = %lu, residue = %lu\n", sz, residue);
+ link = (Linked*) ALLOC(a, sz - ablk_hdr_sz);
+ ASSERT(tcs, link);
+ link->next = pad_list;
+ pad_list = link;
+ } while (residue);
bi = FIND_BKT(a, 0);
ASSERT(tcs, bi < 0);
@@ -127,16 +157,23 @@ testcase_run(TestCaseState_t *tcs)
for (i = 0; i < bi_tests; i++) {
FREE(a, blk[i]);
- FREE(a, fence[i]);
+ }
+ while (fence_list) {
+ link = fence_list;
+ fence_list = link->next;
+ FREE(a, link);
}
FREE(a, (void *) blk);
- FREE(a, (void *) fence);
bi = FIND_BKT(a, 0);
ASSERT(tcs, bi == no_bkts - 1);
- FREE(a, tmp);
+ while (pad_list) {
+ link = pad_list;
+ pad_list = link->next;
+ FREE(a, link);
+ }
bi = FIND_BKT(a, 0);
ASSERT(tcs, bi < 0);
diff --git a/erts/emulator/test/alloc_SUITE_data/coalesce.c b/erts/emulator/test/alloc_SUITE_data/coalesce.c
index 6f35d3279b..981fa6d43e 100644
--- a/erts/emulator/test/alloc_SUITE_data/coalesce.c
+++ b/erts/emulator/test/alloc_SUITE_data/coalesce.c
@@ -54,7 +54,7 @@ setup_sequence(TestCaseState_t *tcs, Allctr_t *a, Ulong bsz, int no,
no, bsz);
c = FIRST_MBC(a);
ASSERT(tcs, !NEXT_C(c));
- blk = MBC2FBLK(a, c);
+ blk = MBC_TO_FIRST_BLK(a, c);
ASSERT(tcs, IS_LAST_BLK(blk));
for (i = 0; i < no; i++)
@@ -266,7 +266,7 @@ testcase_name(void)
void
testcase_run(TestCaseState_t *tcs)
{
- char *argv_org[] = {"-tmmbcs1024", "-tsbct2048", "-trmbcmt100", "-tas", NULL, NULL};
+ char *argv_org[] = {"-tsmbcs511","-tmmbcs511", "-tsbct512", "-trmbcmt100", "-tas", NULL, NULL};
char *alg[] = {"af", "gf", "bf", "aobf", "aoff", NULL};
int i;
@@ -276,7 +276,7 @@ testcase_run(TestCaseState_t *tcs)
char *argv[sizeof(argv_org)/sizeof(argv_org[0])];
memcpy((void *) argv, (void *) argv_org, sizeof(argv_org));
- argv[4] = alg[i];
+ argv[5] = alg[i];
testcase_printf(tcs, " *** Starting \"%s\" allocator *** \n", alg[i]);
a = START_ALC("coalesce_", 0, argv);
ASSERT(tcs, a);
diff --git a/erts/emulator/test/alloc_SUITE_data/mseg_clear_cache.c b/erts/emulator/test/alloc_SUITE_data/mseg_clear_cache.c
index 0277616bd0..7d5608f890 100644
--- a/erts/emulator/test/alloc_SUITE_data/mseg_clear_cache.c
+++ b/erts/emulator/test/alloc_SUITE_data/mseg_clear_cache.c
@@ -52,10 +52,10 @@ testcase_run(TestCaseState_t *tcs)
tcs->extra = &seg[0];
for (i = 0; i < MAX_SEGS; i++) {
- seg[i].size = 1000;
+ seg[i].size = 1 << 18;
seg[i].ptr = MSEG_ALLOC(&seg[i].size);
ASSERT(tcs, seg[i].ptr);
- ASSERT(tcs, seg[i].size >= 1000);
+ ASSERT(tcs, seg[i].size >= (1 << 18));
}
n = MSEG_NO();
diff --git a/erts/emulator/test/alloc_SUITE_data/testcase_driver.c b/erts/emulator/test/alloc_SUITE_data/testcase_driver.c
index 66971654a2..5c4b11454f 100644
--- a/erts/emulator/test/alloc_SUITE_data/testcase_driver.c
+++ b/erts/emulator/test/alloc_SUITE_data/testcase_driver.c
@@ -42,6 +42,7 @@
typedef struct {
TestCaseState_t visible;
ErlDrvPort port;
+ ErlDrvTermData port_id;
int result;
jmp_buf done_jmp_buf;
char *comment;
@@ -97,6 +98,7 @@ testcase_drv_start(ErlDrvPort port, char *command)
itcs->visible.testcase_name = testcase_name();
itcs->visible.extra = NULL;
itcs->port = port;
+ itcs->port_id = driver_mk_port(port);
itcs->result = TESTCASE_FAILED;
itcs->comment = "";
@@ -142,7 +144,7 @@ testcase_drv_run(ErlDrvData drv_data, char *buf, ErlDrvSizeT len)
msg[1] = (ErlDrvTermData) result_atom;
msg[2] = ERL_DRV_PORT;
- msg[3] = driver_mk_port(itcs->port);
+ msg[3] = itcs->port_id;
msg[4] = ERL_DRV_ATOM;
msg[5] = driver_mk_atom(itcs->visible.testcase_name);
@@ -154,7 +156,7 @@ testcase_drv_run(ErlDrvData drv_data, char *buf, ErlDrvSizeT len)
msg[9] = ERL_DRV_TUPLE;
msg[10] = (ErlDrvTermData) 4;
- driver_output_term(itcs->port, msg, 11);
+ erl_drv_output_term(itcs->port_id, msg, 11);
}
int
@@ -184,7 +186,7 @@ testcase_printf(TestCaseState_t *tcs, char *frmt, ...)
msg[1] = (ErlDrvTermData) driver_mk_atom("print");
msg[2] = ERL_DRV_PORT;
- msg[3] = driver_mk_port(itcs->port);
+ msg[3] = itcs->port_id;
msg[4] = ERL_DRV_ATOM;
msg[5] = driver_mk_atom(itcs->visible.testcase_name);
@@ -196,7 +198,7 @@ testcase_printf(TestCaseState_t *tcs, char *frmt, ...)
msg[9] = ERL_DRV_TUPLE;
msg[10] = (ErlDrvTermData) 4;
- driver_output_term(itcs->port, msg, 11);
+ erl_drv_output_term(itcs->port_id, msg, 11);
}
diff --git a/erts/emulator/test/beam_SUITE.erl b/erts/emulator/test/beam_SUITE.erl
index 02c6e19686..3197a4c137 100644
--- a/erts/emulator/test/beam_SUITE.erl
+++ b/erts/emulator/test/beam_SUITE.erl
@@ -54,7 +54,7 @@ end_per_group(_GroupName, Config) ->
%% Verify that apply(M, F, A) is really tail recursive.
apply_last(Config) when is_list(Config) ->
- Pid=spawn(?MODULE, applied, [self(), 10000]),
+ Pid = spawn(?MODULE, applied, [self(), 10000]),
Size =
receive
{Pid, finished} ->
@@ -94,32 +94,32 @@ apply_last_bif(Config) when is_list(Config) ->
%% Test three high register numbers in a put_list instruction
%% (to test whether packing works properly).
packed_registers(Config) when is_list(Config) ->
- ?line PrivDir = ?config(priv_dir, Config),
- ?line Mod = packed_regs,
- ?line Name = filename:join(PrivDir, atom_to_list(Mod) ++ ".erl"),
+ PrivDir = ?config(priv_dir, Config),
+ Mod = packed_regs,
+ Name = filename:join(PrivDir, atom_to_list(Mod) ++ ".erl"),
%% Generate a module which generates a list of tuples.
%% put_list(A) -> [{A, 600}, {A, 999}, ... {A, 0}].
- ?line Code = gen_packed_regs(600, ["-module("++atom_to_list(Mod)++").\n",
+ Code = gen_packed_regs(600, ["-module("++atom_to_list(Mod)++").\n",
"-export([put_list/1]).\n",
"put_list(A) ->\n["]),
- ?line ok = file:write_file(Name, Code),
+ ok = file:write_file(Name, Code),
%% Compile the module.
- ?line io:format("Compiling: ~s\n", [Name]),
- ?line CompRc = compile:file(Name, [{outdir, PrivDir}, report]),
- ?line io:format("Result: ~p\n",[CompRc]),
- ?line {ok, Mod} = CompRc,
+ io:format("Compiling: ~s\n", [Name]),
+ CompRc = compile:file(Name, [{outdir, PrivDir}, report]),
+ io:format("Result: ~p\n",[CompRc]),
+ {ok, Mod} = CompRc,
%% Load it.
- ?line io:format("Loading...\n",[]),
- ?line LoadRc = code:load_abs(filename:join(PrivDir, atom_to_list(Mod))),
- ?line {module,_Module} = LoadRc,
+ io:format("Loading...\n",[]),
+ LoadRc = code:load_abs(filename:join(PrivDir, atom_to_list(Mod))),
+ {module,_Module} = LoadRc,
%% Call it and verify result.
- ?line Term = {a, b},
- ?line L = Mod:put_list(Term),
- ?line verify_packed_regs(L, Term, 600),
+ Term = {a, b},
+ L = Mod:put_list(Term),
+ verify_packed_regs(L, Term, 600),
ok.
gen_packed_regs(0, Acc) ->
@@ -131,11 +131,11 @@ verify_packed_regs([], _, -1) -> ok;
verify_packed_regs([{Term, N}| T], Term, N) ->
verify_packed_regs(T, Term, N-1);
verify_packed_regs(L, Term, N) ->
- ?line ok = io:format("Expected [{~p, ~p}|T]; got\n~p\n", [Term, N, L]),
- ?line test_server:fail().
+ ok = io:format("Expected [{~p, ~p}|T]; got\n~p\n", [Term, N, L]),
+ test_server:fail().
buildo_mucho(Config) when is_list(Config) ->
- ?line buildo_mucho_1(),
+ buildo_mucho_1(),
ok.
buildo_mucho_1() ->
@@ -206,20 +206,27 @@ buildo_mucho_1() ->
{<<>>,1},{<<>>,1},{<<>>,1},{<<>>,1},{<<>>,1},{<<>>,1},{<<>>,1},{<<>>,1}].
heap_sizes(Config) when is_list(Config) ->
- ?line Sizes = erlang:system_info(heap_sizes),
- ?line io:format("~p heap sizes\n", [length(Sizes)]),
- ?line io:format("~p\n", [Sizes]),
+ Sizes = erlang:system_info(heap_sizes),
+ io:format("~p heap sizes\n", [length(Sizes)]),
+ io:format("~p\n", [Sizes]),
%% Verify that heap sizes increase monotonically.
- ?line Largest = lists:foldl(fun(E, P) when is_integer(P), E > P -> E;
+ Largest = lists:foldl(fun(E, P) when is_integer(P), E > P -> E;
(E, []) -> E
end, [], Sizes),
- %% Verify that the largest heap size consists of 31 or 63 bits.
- ?line
- case Largest bsr (erlang:system_info(wordsize)*8-2) of
- R when R > 0 -> ok
- end,
+ %% Verify that the largest heap size consists of
+ %% - 31 bits of bytes on 32 bits arch
+ %% - atleast 52 bits of bytes (48 is the maximum virtual address)
+ %% and at the most 63 bits on 64 bit archs
+ %% heap sizes are in words
+ case erlang:system_info(wordsize) of
+ 8 ->
+ 0 = (Largest*8) bsr 63,
+ true = (Largest*8) > (1 bsl 52);
+ 4 ->
+ 1 = (Largest*4) bsr 31
+ end,
ok.
%% Thanks to Igor Goryachev.
@@ -302,10 +309,10 @@ b() ->
end.
fconv(Config) when is_list(Config) ->
- ?line do_fconv(atom),
- ?line do_fconv(nil),
- ?line do_fconv(tuple_literal),
- ?line 3.0 = do_fconv(1.0, 2.0),
+ do_fconv(atom),
+ do_fconv(nil),
+ do_fconv(tuple_literal),
+ 3.0 = do_fconv(1.0, 2.0),
ok.
do_fconv(Type) ->
@@ -325,9 +332,9 @@ do_fconv(tuple_literal, Float) when is_float(Float) ->
Float + {a,b}.
select_val(Config) when is_list(Config) ->
- ?line zero = do_select_val(0),
- ?line big = do_select_val(1 bsl 64),
- ?line integer = do_select_val(42),
+ zero = do_select_val(0),
+ big = do_select_val(1 bsl 64),
+ integer = do_select_val(42),
ok.
do_select_val(X) ->
diff --git a/erts/emulator/test/bif_SUITE.erl b/erts/emulator/test/bif_SUITE.erl
index e2442861c7..02c6de8cb1 100644
--- a/erts/emulator/test/bif_SUITE.erl
+++ b/erts/emulator/test/bif_SUITE.erl
@@ -481,8 +481,6 @@ binary_to_atom(Config) when is_list(Config) ->
%% Bad UTF8 sequences.
?line ?BADARG(binary_to_atom(id(<<255>>), utf8)),
?line ?BADARG(binary_to_atom(id(<<255,0>>), utf8)),
- ?line ?BADARG(binary_to_atom(id(<<0:512/unit:8,255>>), utf8)),
- ?line ?BADARG(binary_to_atom(id(<<0:512/unit:8,255,0>>), utf8)),
?line ?BADARG(binary_to_atom(id(<<16#C0,16#80>>), utf8)), %Overlong 0.
?line [?BADARG(binary_to_atom(<<C/utf8>>, utf8)) ||
C <- lists:seq(256, 16#D7FF)],
@@ -494,6 +492,8 @@ binary_to_atom(Config) when is_list(Config) ->
C <- lists:seq(16#90000, 16#10FFFF)],
%% system_limit failures.
+ ?line ?SYS_LIMIT(binary_to_atom(id(<<0:512/unit:8,255>>), utf8)),
+ ?line ?SYS_LIMIT(binary_to_atom(id(<<0:512/unit:8,255,0>>), utf8)),
?line ?SYS_LIMIT(binary_to_atom(<<0:256/unit:8>>, latin1)),
?line ?SYS_LIMIT(binary_to_atom(<<0:257/unit:8>>, latin1)),
?line ?SYS_LIMIT(binary_to_atom(<<0:512/unit:8>>, latin1)),
diff --git a/erts/emulator/test/binary_SUITE.erl b/erts/emulator/test/binary_SUITE.erl
index c9f43bb00b..babdb3363f 100644
--- a/erts/emulator/test/binary_SUITE.erl
+++ b/erts/emulator/test/binary_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -626,8 +626,32 @@ safe_binary_to_term2(Config) when is_list(Config) ->
bad_terms(suite) -> [];
bad_terms(Config) when is_list(Config) ->
?line test_terms(fun corrupter/1).
-
+
+corrupter(Term) when is_function(Term);
+ is_function(hd(Term));
+ is_function(element(2,element(2,element(2,Term)))) ->
+ %% Check if lists is native compiled. If it is, we do not try to
+ %% corrupt funs as this can create some very strange behaviour.
+ %% To show the error print `Byte` in the foreach fun in corrupter/2.
+ case erlang:system_info(hipe_architecture) of
+ undefined ->
+ corrupter0(Term);
+ Architecture ->
+ {lists, ListsBinary, _ListsFilename} = code:get_object_code(lists),
+ ChunkName = hipe_unified_loader:chunk_name(Architecture),
+ NativeChunk = beam_lib:chunks(ListsBinary, [ChunkName]),
+ case NativeChunk of
+ {ok,{_,[{_,Bin}]}} when is_binary(Bin) ->
+ S = io_lib:format("Skipping corruption of: ~P", [Term,12]),
+ io:put_chars(S);
+ {error, beam_lib, _} ->
+ corrupter0(Term)
+ end
+ end;
corrupter(Term) ->
+ corrupter0(Term).
+
+corrupter0(Term) ->
?line try
S = io_lib:format("About to corrupt: ~P", [Term,12]),
io:put_chars(S)
diff --git a/erts/emulator/test/bs_bit_binaries_SUITE.erl b/erts/emulator/test/bs_bit_binaries_SUITE.erl
index ff1088118d..7f2cd1e881 100644
--- a/erts/emulator/test/bs_bit_binaries_SUITE.erl
+++ b/erts/emulator/test/bs_bit_binaries_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -177,14 +177,55 @@ append(Config) when is_list(Config) ->
cs_init(),
?line <<(-1):256/signed-unit:8>> = cs(do_append(id(<<>>), 256*8)),
?line <<(-1):256/signed-unit:8>> = cs(do_append2(id(<<>>), 256*4)),
+ <<(-1):256/signed-unit:8>> = cs(do_append3(id(<<>>), 256*8)),
cs_end().
do_append(Bin, N) when N > 0 -> do_append(<<Bin/bits,1:1>>, N-1);
do_append(Bin, 0) -> Bin.
-do_append2(Bin, N) when N > 0 -> do_append2(<<Bin/bits,3:2>>, N-1);
+do_append2(Bin, N) when N > 0 -> do_append2(<<Bin/binary-unit:2,3:2>>, N-1);
do_append2(Bin, 0) -> Bin.
+do_append3(Bin, N) when N > 0 ->
+ Bits = bit_size(Bin),
+ if
+ Bits rem 2 =:= 0 ->
+ do_append3(<<Bin/binary-unit:2,1:1>>, N-1);
+ Bits rem 3 =:= 0 ->
+ do_append3(<<Bin/binary-unit:3,1:1>>, N-1);
+ Bits rem 4 =:= 0 ->
+ do_append3(<<Bin/binary-unit:4,1:1>>, N-1);
+ Bits rem 5 =:= 0 ->
+ do_append3(<<Bin/binary-unit:5,1:1>>, N-1);
+ Bits rem 6 =:= 0 ->
+ do_append3(<<Bin/binary-unit:6,1:1>>, N-1);
+ Bits rem 7 =:= 0 ->
+ do_append3(<<Bin/binary-unit:7,1:1>>, N-1);
+ Bits rem 8 =:= 0 ->
+ do_append3(<<Bin/binary-unit:8,1:1>>, N-1);
+ Bits rem 9 =:= 0 ->
+ do_append3(<<Bin/binary-unit:9,1:1>>, N-1);
+ Bits rem 10 =:= 0 ->
+ do_append3(<<Bin/binary-unit:10,1:1>>, N-1);
+ Bits rem 11 =:= 0 ->
+ do_append3(<<Bin/binary-unit:11,1:1>>, N-1);
+ Bits rem 12 =:= 0 ->
+ do_append3(<<Bin/binary-unit:12,1:1>>, N-1);
+ Bits rem 13 =:= 0 ->
+ do_append3(<<Bin/binary-unit:13,1:1>>, N-1);
+ Bits rem 14 =:= 0 ->
+ do_append3(<<Bin/binary-unit:14,1:1>>, N-1);
+ Bits rem 15 =:= 0 ->
+ do_append3(<<Bin/binary-unit:15,1:1>>, N-1);
+ Bits rem 16 =:= 0 ->
+ do_append3(<<Bin/binary-unit:16,1:1>>, N-1);
+ Bits rem 17 =:= 0 ->
+ do_append3(<<Bin/binary-unit:17,1:1>>, N-1);
+ true ->
+ do_append3(<<Bin/binary-unit:1,1:1>>, N-1)
+ end;
+do_append3(Bin, 0) -> Bin.
+
cs_init() ->
erts_debug:set_internal_state(available_internal_state, true),
ok.
diff --git a/erts/emulator/test/bs_construct_SUITE.erl b/erts/emulator/test/bs_construct_SUITE.erl
index dea02b1f4b..123952d01d 100644
--- a/erts/emulator/test/bs_construct_SUITE.erl
+++ b/erts/emulator/test/bs_construct_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -28,7 +28,7 @@
mem_leak/1, coerce_to_float/1, bjorn/1,
huge_float_field/1, huge_binary/1, system_limit/1, badarg/1,
copy_writable_binary/1, kostis/1, dynamic/1, bs_add/1,
- otp_7422/1, zero_width/1]).
+ otp_7422/1, zero_width/1, bad_append/1]).
-include_lib("test_server/include/test_server.hrl").
@@ -38,7 +38,8 @@ all() ->
[test1, test2, test3, test4, test5, testf, not_used,
in_guard, mem_leak, coerce_to_float, bjorn,
huge_float_field, huge_binary, system_limit, badarg,
- copy_writable_binary, kostis, dynamic, bs_add, otp_7422, zero_width].
+ copy_writable_binary, kostis, dynamic, bs_add, otp_7422, zero_width,
+ bad_append].
groups() ->
[].
@@ -546,11 +547,47 @@ huge_float_check({'EXIT',{badarg,_}}) -> ok.
huge_binary(Config) when is_list(Config) ->
?line 16777216 = size(<<0:(id(1 bsl 26)),(-1):(id(1 bsl 26))>>),
?line garbage_collect(),
- ?line id(<<0:((1 bsl 32)-1)>>),
+ {Shift,Return} = case free_mem() of
+ undefined -> {32,ok};
+ Mb when Mb > 600 -> {32,ok};
+ Mb when Mb > 300 -> {31,"Limit huge binaries to 256 Mb"};
+ _ -> {30,"Limit huge binary to 128 Mb"}
+ end,
?line garbage_collect(),
- ?line id(<<0:(id((1 bsl 32)-1))>>),
+ ?line id(<<0:((1 bsl Shift)-1)>>),
?line garbage_collect(),
- ok.
+ ?line id(<<0:(id((1 bsl Shift)-1))>>),
+ ?line garbage_collect(),
+ case Return of
+ ok -> ok;
+ Comment -> {comment, Comment}
+ end.
+
+free_mem() ->
+ Cmd = "uname; free",
+ Output = string:tokens(os:cmd(Cmd), "\n"),
+ io:format("Output from command ~p\n~p\n",[Cmd,Output]),
+ case Output of
+ [OS, ColumnNames, Values | _] ->
+ case string:str(OS,"Linux") of
+ 0 ->
+ io:format("Unknown OS\n",[]),
+ undefined;
+ _ ->
+ case {string:tokens(ColumnNames, " \t"),
+ string:tokens(Values, " \t")} of
+ {[_,_,"free"|_],["Mem:",_,_,FreeKb|_]} ->
+ list_to_integer(FreeKb) div 1024;
+ _ ->
+ io:format("Failed to parse output from 'free':\n",[]),
+ undefined
+ end
+ end;
+ _ ->
+ io:format("Too few lines in output\n",[]),
+ undefined
+ end.
+
system_limit(Config) when is_list(Config) ->
WordSize = erlang:system_info(wordsize),
@@ -827,5 +864,49 @@ zero_width(Config) when is_list(Config) ->
?line {'EXIT',{badarg,_}} = (catch <<(id(not_a_number)):0>>),
ok.
+
+bad_append(_) ->
+ do_bad_append(<<127:1>>, fun append_unit_3/1),
+ do_bad_append(<<127:2>>, fun append_unit_3/1),
+ do_bad_append(<<127:17>>, fun append_unit_3/1),
+
+ do_bad_append(<<127:3>>, fun append_unit_4/1),
+ do_bad_append(<<127:5>>, fun append_unit_4/1),
+ do_bad_append(<<127:7>>, fun append_unit_4/1),
+ do_bad_append(<<127:199>>, fun append_unit_4/1),
+
+ do_bad_append(<<127:7>>, fun append_unit_8/1),
+ do_bad_append(<<127:9>>, fun append_unit_8/1),
+
+ do_bad_append(<<0:8>>, fun append_unit_16/1),
+ do_bad_append(<<0:15>>, fun append_unit_16/1),
+ do_bad_append(<<0:17>>, fun append_unit_16/1),
+ ok.
+
+do_bad_append(Bin0, Appender) ->
+ {'EXIT',{badarg,_}} = (catch Appender(Bin0)),
+
+ Bin1 = id(<<0:3,Bin0/bitstring>>),
+ <<_:3,Bin2/bitstring>> = Bin1,
+ {'EXIT',{badarg,_}} = (catch Appender(Bin2)),
+
+ %% Create a writable binary.
+ Empty = id(<<>>),
+ Bin3 = <<Empty/bitstring,Bin0/bitstring>>,
+ {'EXIT',{badarg,_}} = (catch Appender(Bin3)),
+ ok.
+
+append_unit_3(Bin) ->
+ <<Bin/binary-unit:3,0:1>>.
+
+append_unit_4(Bin) ->
+ <<Bin/binary-unit:4,0:1>>.
+
+append_unit_8(Bin) ->
+ <<Bin/binary,0:1>>.
+
+append_unit_16(Bin) ->
+ <<Bin/binary-unit:16,0:1>>.
+
id(I) -> I.
diff --git a/erts/emulator/test/busy_port_SUITE.erl b/erts/emulator/test/busy_port_SUITE.erl
index 3a29fd4d68..a92afef003 100644
--- a/erts/emulator/test/busy_port_SUITE.erl
+++ b/erts/emulator/test/busy_port_SUITE.erl
@@ -26,6 +26,8 @@
no_trap_exit_unlinked/1, trap_exit/1, multiple_writers/1,
hard_busy_driver/1, soft_busy_driver/1]).
+-compile(export_all).
+
-include_lib("test_server/include/test_server.hrl").
%% Internal exports.
@@ -36,7 +38,9 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[io_to_busy, message_order, send_3, system_monitor,
no_trap_exit, no_trap_exit_unlinked, trap_exit,
- multiple_writers, hard_busy_driver, soft_busy_driver].
+ multiple_writers, hard_busy_driver, soft_busy_driver,
+ scheduling_delay_busy,scheduling_delay_busy_nosuspend,
+ scheduling_busy_link].
groups() ->
[].
@@ -148,9 +152,9 @@ message_order(Config) when is_list(Config) ->
send_to_busy_1(Parent) ->
{Owner, Slave} = get_slave(),
- Slave ! {Owner, {command, "set_me_busy"}},
- Slave ! {Owner, {command, "hello"}},
- Slave ! {Owner, {command, "hello again"}},
+ (catch port_command(Slave, "set_me_busy")),
+ (catch port_command(Slave, "hello")),
+ (catch port_command(Slave, "hello again")),
receive
Message ->
Parent ! {self(), Message}
@@ -193,10 +197,10 @@ system_monitor(Config) when is_list(Config) ->
?line Busy =
spawn_link(
fun() ->
- Slave ! {Owner,{command,"set busy"}},
+ (catch port_command(Slave, "set busy")),
receive {Parent,alpha} -> ok end,
- Slave ! {Owner,{command,"busy"}},
- Slave ! {Owner,{command,"free"}},
+ (catch port_command(Slave, "busy")),
+ (catch port_command(Slave, "free")),
Parent ! {self(),alpha},
command(lock),
receive {Parent,beta} -> ok end,
@@ -212,7 +216,7 @@ system_monitor(Config) when is_list(Config) ->
?line Void = rec(Void),
?line Busy ! {self(), beta},
?line {monitor,Owner,busy_port,Slave} = rec(Void),
- ?line Master ! {Owner, {command, "u"}},
+ ?line port_command(Master, "u"),
?line {Busy,beta} = rec(Void),
?line Void = rec(Void),
?line _NewMonitor = erlang:system_monitor(OldMonitor),
@@ -296,9 +300,9 @@ no_trap_exit_process(ResultTo, Link, Config) ->
linked -> ok;
unlink -> unlink(Slave)
end,
- ?line Slave ! {self(), {command, "lock port"}},
+ ?line (catch port_command(Slave, "lock port")),
?line ResultTo ! {self(), port_created, Slave},
- ?line Slave ! {self(), {command, "suspend me"}},
+ ?line (catch port_command(Slave, "suspend me")),
ok.
%% Assuming the following scenario,
@@ -339,9 +343,9 @@ busy_port_exit_process(ResultTo, Config) ->
?line load_busy_driver(Config),
?line _Master = open_port({spawn, "busy_drv master"}, [eof]),
?line Slave = open_port({spawn, "busy_drv slave"}, [eof]),
- ?line Slave ! {self(), {command, "lock port"}},
+ ?line (catch port_command(Slave, "lock port")),
?line ResultTo ! {self(), port_created, Slave},
- ?line Slave ! {self(), {command, "suspend me"}},
+ ?line (catch port_command(Slave, "suspend me")),
receive
{'EXIT', Slave, die} ->
ResultTo ! {self(), ok};
@@ -383,8 +387,8 @@ multiple_writers(Config) when is_list(Config) ->
quick_writer() ->
{Owner, Port} = get_slave(),
- Port ! {Owner, {command, "port to busy"}},
- Port ! {Owner, {command, "lock me"}},
+ (catch port_command(Port, "port to busy")),
+ (catch port_command(Port, "lock me")),
ok.
hard_busy_driver(Config) when is_list(Config) ->
@@ -528,6 +532,304 @@ hs_busy_pcmd(Prt, Opts, StartFun, EndFun) ->
EndFun(P, Res, Time)
end.
+scheduling_delay_busy(Config) ->
+
+ Scenario =
+ [{1,{spawn,[{var,drvname},undefined]}},
+ {2,{call,[{var,1},open_port]}},
+ {3,{spawn,[{var,2},{var,1}]}},
+ {0,{ack,[{var,1},{busy,1,250}]}},
+ {0,{cast,[{var,3},{command,2}]}},
+ [{0,{cast,[{var,3},{command,I}]}}
+ || I <- lists:seq(3,50)],
+ {0,{cast,[{var,3},take_control]}},
+ {0,{cast,[{var,1},{new_owner,{var,3}}]}},
+ {0,{cast,[{var,3},close]}},
+ {0,{timer,sleep,[300]}},
+ {0,{erlang,port_command,[{var,2},<<$N>>,[force]]}},
+ [{0,{cast,[{var,1},{command,I}]}}
+ || I <- lists:seq(101,127)]
+ ,{10,{call,[{var,3},get_data]}}
+ ],
+
+ Validation = [{seq,10,lists:seq(1,50)}],
+
+ port_scheduling(Scenario,Validation,?config(data_dir,Config)).
+
+scheduling_delay_busy_nosuspend(Config) ->
+
+ Scenario =
+ [{1,{spawn,[{var,drvname},undefined]}},
+ {2,{call,[{var,1},open_port]}},
+ {0,{cast,[{var,1},{command,1,100}]}},
+ {0,{cast,[{var,1},{busy,2}]}},
+ {10,{call,[{var,1},{command,3,[nosuspend]}]}},
+ {0,{timer,sleep,[200]}},
+ {0,{erlang,port_command,[{var,2},<<$N>>,[force]]}},
+ {0,{cast,[{var,1},close]}},
+ {20,{call,[{var,1},get_data]}}
+ ],
+
+ Validation = [{eq,10,nosuspend},{seq,20,[1,2]}],
+
+ port_scheduling(Scenario,Validation,?config(data_dir,Config)).
+
+scheduling_busy_link(Config) ->
+
+ Scenario =
+ [{1,{spawn,[{var,drvname},undefined]}},
+ {2,{call,[{var,1},open_port]}},
+ {3,{spawn,[{var,2},{var,1}]}},
+ {0,{cast,[{var,1},unlink]}},
+ {0,{cast,[{var,1},{busy,1}]}},
+ {0,{cast,[{var,1},{command,2}]}},
+ {0,{cast,[{var,1},link]}},
+ {0,{timer,sleep,[1000]}},
+ {0,{ack,[{var,3},take_control]}},
+ {0,{cast,[{var,1},{new_owner,{var,3}}]}},
+ {0,{cast,[{var,3},close]}},
+ {10,{call,[{var,3},get_data]}},
+ {20,{call,[{var,1},get_exit]}}
+ ],
+
+ Validation = [{seq,10,[1]},
+ {seq,20,[{'EXIT',noproc}]}],
+
+ port_scheduling(Scenario,Validation,?config(data_dir,Config)).
+
+process_init(DrvName,Owner) ->
+ process_flag(trap_exit,true),
+ process_loop(DrvName,Owner, {[],[]}).
+
+process_loop(DrvName,undefined,Data) when is_list(DrvName) ->
+ process_loop(DrvName,[binary],Data);
+process_loop(DrvName,PortOpts,Data) when is_list(DrvName) ->
+ receive
+ {call,open_port,P} ->
+ Port = open_port({spawn, DrvName}, PortOpts),
+ send(P,Port),
+ process_loop(Port,self(),Data)
+ end;
+process_loop(Port,undefined,Data) ->
+ receive
+ {cast,{new_owner,Pid}} ->
+ pal("NewOwner: ~p",[Pid]),
+ process_loop(Port,Pid,Data)
+ end;
+process_loop(Port,Owner,{Data,Exit} = DE) ->
+ receive
+ {Port,connected} ->
+ pal("Connected",[]),
+ process_loop(Port,undefined,DE);
+ {Port,{data,NewData}} ->
+ pal("Got: ~p",[NewData]),
+ receive
+ {Port,closed} ->
+ process_loop(Port,Owner,{Data ++ [NewData],Exit})
+ after 2000 ->
+ exit(did_not_get_port_close)
+ end;
+ {'EXIT',Port,Reason} = Exit ->
+ pal("Exit: ~p",[Exit]),
+ process_loop(Port,Owner,{Data, Exit ++ [[{'EXIT',Reason}]]});
+ {'EXIT',_Port,_Reason} = Exit ->
+ pal("Exit: ~p",[Exit]);
+ {call,Msg,P} ->
+ case handle_msg(Msg,Port,Owner,DE) of
+ {Reply,NewOwner,NewData} ->
+ send(P,Reply),
+ process_loop(Port,NewOwner,NewData);
+ Reply ->
+ send(P,Reply),
+ process_loop(Port,Owner,DE)
+ end;
+ {ack,Msg,P} ->
+ send(P,ok),
+ case handle_msg(Msg,Port,Owner,DE) of
+ {_Reply,NewOwner,NewData} ->
+ process_loop(Port,NewOwner,NewData);
+ _Reply ->
+ process_loop(Port,Owner,DE)
+ end;
+ {cast,Msg} when is_atom(Msg) orelse element(1,Msg) /= new_owner ->
+ case handle_msg(Msg,Port,Owner,DE) of
+ {_Reply,NewOwner,NewData} ->
+ process_loop(Port,NewOwner,NewData);
+ _ ->
+ process_loop(Port,Owner,DE)
+ end
+ end.
+
+handle_msg({busy,Value,Delay},Port,Owner,_Data) ->
+ pal("Long busy: ~p",[Value]),
+ send(Port,{Owner,{command,<<$L,Value:32,(round(Delay/100))>>}});
+handle_msg({busy,Value},Port,Owner,_Data) ->
+ pal("Busy: ~p",[Value]),
+ send(Port,{Owner,{command,<<$B,Value:32>>}});
+handle_msg({command,Value},Port,Owner,_Data) ->
+ pal("Short: ~p",[Value]),
+ send(Port,{Owner,{command,<<$C,Value:32>>}});
+handle_msg({command,Value,Delay},Port,Owner,_Data) when is_integer(Delay) ->
+ pal("Long: ~p",[Value]),
+ send(Port,{Owner,{command,<<$D,Value:32,(round(Delay/100))>>}});
+handle_msg({command,Value,Opts},Port,Owner,_Data) ->
+ pal("Short Opt: ~p",[Value]),
+ send(Port,{Owner,{command,<<$C,Value:32>>}},Opts);
+handle_msg({command,Value,Opts,Delay},Port,Owner,_Data) ->
+ pal("Long Opt: ~p",[Value]),
+ send(Port,{Owner,{command,<<$D,Value:32,(round(Delay/100))>>}},Opts);
+handle_msg(take_control,Port,Owner,Data) ->
+ pal("Connect: ~p",[self()]),
+ send(Port,{Owner, {connect, self()}}),
+ {undefined,self(),Data};
+handle_msg(unlink,Port,_Owner,_Data) ->
+ pal("Unlink:",[]),
+ erlang:unlink(Port);
+handle_msg(link,Port,_Owner,_Data) ->
+ pal("Link:",[]),
+ erlang:link(Port);
+handle_msg(close,Port,Owner,_Data) ->
+ pal("Close",[]),
+ send(Port,{Owner,close});
+handle_msg(get_data,Port,_Owner,{[],_Exit}) ->
+ %% Wait for data if it has not arrived yet
+ receive
+ {Port,{data,Data}} ->
+ Data
+ after 2000 ->
+ pal("~p",[erlang:process_info(self())]),
+ exit(did_not_get_port_data)
+ end;
+handle_msg(get_data,_Port,Owner,{Data,Exit}) ->
+ pal("GetData",[]),
+ {hd(Data),Owner,{tl(Data),Exit}};
+handle_msg(get_exit,Port,_Owner,{_Data,[]}) ->
+ %% Wait for exit if it has not arrived yet
+ receive
+ {'EXIT',Port,Reason} ->
+ [{'EXIT',Reason}]
+ after 2000 ->
+ pal("~p",[erlang:process_info(self())]),
+ exit(did_not_get_port_exit)
+ end;
+handle_msg(get_exit,_Port,Owner,{Data,Exit}) ->
+ {hd(Exit),Owner,{Data,tl(Exit)}}.
+
+
+
+call(Pid,Msg) ->
+ pal("call(~p,~p)",[Pid,Msg]),
+ send(Pid,{call,Msg,self()}),
+ receive
+ Ret ->
+ Ret
+ end.
+ack(Pid,Msg) ->
+ pal("ack(~p,~p)",[Pid,Msg]),
+ send(Pid,{ack,Msg,self()}),
+ receive
+ Ret ->
+ Ret
+ end.
+
+cast(Pid,Msg) ->
+ pal("cast(~p,~p)",[Pid,Msg]),
+ send(Pid,{cast,Msg}).
+
+send(Pid,Msg) ->
+ erlang:send(Pid,Msg).
+send(Prt,Msg,Opts) ->
+ erlang:send(Prt,Msg,Opts).
+
+
+port_scheduling(Scenario,Validation,Path) ->
+ DrvName = "scheduling_drv",
+ erl_ddll:start(),
+ case erl_ddll:load_driver(Path, DrvName) of
+ ok -> ok;
+ {error, Error} ->
+ io:format("~s\n", [erl_ddll:format_error(Error)]),
+ ?line ?t:fail()
+ end,
+
+ Data = run_scenario(lists:flatten(Scenario),[{drvname,DrvName}]),
+ ok = validate_scenario(Data,Validation).
+
+
+run_scenario([{V,{Module,Cmd,Args}}|T],Vars) ->
+ Res = run_command(Module,Cmd,
+ replace_args(Args,Vars)),
+ run_scenario(T,[{V,Res}|Vars]);
+run_scenario([{V,{Cmd,Args}}|T],Vars) ->
+ run_scenario([{V,{?MODULE,Cmd,Args}}|T],Vars);
+run_scenario([],Vars) ->
+ Vars.
+
+run_command(_M,spawn,{Args,Opts}) ->
+ Pid = spawn_opt(fun() -> apply(?MODULE,process_init,Args) end,[link|Opts]),
+ pal("spawn(~p): ~p",[Args,Pid]),
+ Pid;
+run_command(M,spawn,Args) ->
+ run_command(M,spawn,{Args,[]});
+run_command(Mod,Func,Args) ->
+ erlang:display({{Mod,Func,Args},now()}),
+ apply(Mod,Func,Args).
+
+validate_scenario(Data,[{print,Var}|T]) ->
+ pal("Val: ~p",[proplists:get_value(Var,Data)]),
+ validate_scenario(Data,T);
+validate_scenario(Data,[{eq,Var,Value}|T]) ->
+ case proplists:get_value(Var,Data) of
+ Value ->
+ validate_scenario(Data,T);
+ Else ->
+ exit({eq_return,Value,Else})
+ end;
+validate_scenario(Data,[{neq,Var,Value}|T]) ->
+ case proplists:get_value(Var,Data) of
+ Value ->
+ exit({neq_return,Value});
+ _Else ->
+ validate_scenario(Data,T)
+ end;
+validate_scenario(Data,[{seq,Var,Seq}|T]) ->
+ try
+ validate_sequence(proplists:get_value(Var,Data),Seq)
+ catch _:{validate_sequence,NotFound} ->
+ exit({validate_sequence,NotFound,Data})
+ end,
+ validate_scenario(Data,T);
+validate_scenario(_,[]) ->
+ ok.
+
+validate_sequence(Data,Validation) when is_binary(Data) ->
+ validate_sequence(binary_to_list(Data),Validation);
+validate_sequence([H|R],[H|T]) ->
+ validate_sequence(R,T);
+validate_sequence([_|R],Seq) ->
+ validate_sequence(R,Seq);
+validate_sequence(_,[]) ->
+ ok;
+validate_sequence([],NotFound) ->
+ exit({validate_sequence,NotFound}).
+
+replace_args({var,Var},Vars) ->
+ proplists:get_value(Var,Vars);
+replace_args([H|T],Vars) ->
+ [replace_args(H,Vars)|replace_args(T,Vars)];
+replace_args([],_Vars) ->
+ [];
+replace_args(Tuple,Vars) when is_tuple(Tuple) ->
+ list_to_tuple(replace_args(tuple_to_list(Tuple),Vars));
+replace_args(Else,_Vars) ->
+ Else.
+
+pal(_F,_A) -> ok.
+%pal(Format,Args) ->
+% ct:pal("~p "++Format,[self()|Args]).
+% erlang:display(lists:flatten(io_lib:format("~p "++Format,[self()|Args]))).
+
+
%%% Utilities.
chk_range(Min, Val, Max) when Min =< Val, Val =< Max ->
@@ -644,11 +946,11 @@ loop(Master, Slave) ->
Pid ! {busy_drv_reply, {self(), Slave}},
loop(Master, Slave);
{Pid, unlock} ->
- Master ! {self(), {command, "u"}},
+ port_command(Master, "u"),
Pid ! {busy_drv_reply, ok},
loop(Master, Slave);
{Pid, lock} ->
- Master ! {self(), {command, "l"}},
+ port_command(Master, "l"),
Pid ! {busy_drv_reply, ok},
loop(Master, Slave);
{Pid, {port_command,Data}} ->
diff --git a/erts/emulator/test/busy_port_SUITE_data/Makefile.src b/erts/emulator/test/busy_port_SUITE_data/Makefile.src
index 664909db71..b5fcf25176 100644
--- a/erts/emulator/test/busy_port_SUITE_data/Makefile.src
+++ b/erts/emulator/test/busy_port_SUITE_data/Makefile.src
@@ -17,9 +17,10 @@
# %CopyrightEnd%
#
-all: busy_drv@dll@ hard_busy_drv@dll@ soft_busy_drv@dll@
+all: busy_drv@dll@ hard_busy_drv@dll@ soft_busy_drv@dll@ scheduling_drv@dll@
@SHLIB_RULES@
hard_busy_drv@obj@: hard_busy_drv.c hs_busy_drv.c
soft_busy_drv@obj@: soft_busy_drv.c hs_busy_drv.c
+scheduling_drv@obj@: scheduling_drv.c
diff --git a/erts/emulator/test/busy_port_SUITE_data/hs_busy_drv.c b/erts/emulator/test/busy_port_SUITE_data/hs_busy_drv.c
index 9f6bd310c6..dcbaf500b8 100644
--- a/erts/emulator/test/busy_port_SUITE_data/hs_busy_drv.c
+++ b/erts/emulator/test/busy_port_SUITE_data/hs_busy_drv.c
@@ -71,9 +71,9 @@ void output(ErlDrvData drv_data, char *buf, ErlDrvSizeT len)
ERL_DRV_PID, driver_caller(port),
ERL_DRV_TUPLE, (ErlDrvTermData) 3
};
- res = driver_output_term(port, msg, sizeof(msg)/sizeof(ErlDrvTermData));
+ res = erl_drv_output_term(driver_mk_port(port), msg, sizeof(msg)/sizeof(ErlDrvTermData));
if (res <= 0)
- driver_failure_atom(port, "driver_output_term failed");
+ driver_failure_atom(port, "erl_drv_output_term failed");
}
ErlDrvSSizeT control(ErlDrvData drv_data, unsigned int command, char *buf,
diff --git a/erts/emulator/test/busy_port_SUITE_data/scheduling_drv.c b/erts/emulator/test/busy_port_SUITE_data/scheduling_drv.c
new file mode 100644
index 0000000000..57be9b6392
--- /dev/null
+++ b/erts/emulator/test/busy_port_SUITE_data/scheduling_drv.c
@@ -0,0 +1,190 @@
+/*
+ * %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%
+ */
+
+#ifdef __WIN32__
+#include <windows.h>
+#else
+#include <sys/select.h>
+#endif
+#include <errno.h>
+#include <stdio.h>
+#include "erl_driver.h"
+
+#define get_int32(s) ((((unsigned char*) (s))[0] << 24) | \
+ (((unsigned char*) (s))[1] << 16) | \
+ (((unsigned char*) (s))[2] << 8) | \
+ (((unsigned char*) (s))[3]))
+
+#define ERTS_TEST_SCHEDULING_DRV_NAME "scheduling_drv"
+#define ERTS_TEST_SCHEDULING_DRV_FLAGS \
+ ERL_DRV_FLAG_USE_PORT_LOCKING | ERL_DRV_FLAG_SOFT_BUSY
+
+ErlDrvData start(ErlDrvPort port, char *command);
+void output(ErlDrvData drv_data, char *buf, ErlDrvSizeT len);
+ErlDrvSSizeT control(ErlDrvData drv_data, unsigned int command, char *buf,
+ ErlDrvSizeT len, char **rbuf, ErlDrvSizeT rlen);
+void stop(ErlDrvData drv_data);
+void timeout(ErlDrvData drv_data);
+
+static void delay(unsigned ms);
+
+static ErlDrvEntry busy_drv_entry = {
+ NULL /* init */,
+ start,
+ stop,
+ output,
+ NULL /* ready_input */,
+ NULL /* ready_output */,
+ ERTS_TEST_SCHEDULING_DRV_NAME,
+ NULL /* finish */,
+ NULL /* handle */,
+ control,
+ timeout,
+ NULL /* outputv */,
+ NULL /* ready_async */,
+ NULL /* flush */,
+ NULL /* call */,
+ NULL /* event */,
+ ERL_DRV_EXTENDED_MARKER,
+ ERL_DRV_EXTENDED_MAJOR_VERSION,
+ ERL_DRV_EXTENDED_MINOR_VERSION,
+ ERTS_TEST_SCHEDULING_DRV_FLAGS,
+ NULL /* handle2 */,
+ NULL /* handle_monitor */,
+ NULL /* stop_select */
+};
+
+#define DBG(data,FMT)
+/* #define DBG(data,FMT) printf("0x%.8lx: %s",driver_caller(data->port),FMT); */
+
+typedef struct SchedDrvData {
+ ErlDrvPort port;
+ char data[255];
+ int curr;
+ int use_auto_busy;
+} SchedDrvData;
+
+DRIVER_INIT(busy_drv)
+{
+ return &busy_drv_entry;
+}
+
+ErlDrvData start(ErlDrvPort port, char *command)
+{
+ SchedDrvData *d = driver_alloc(sizeof(SchedDrvData));
+ d->port = port;
+ d->curr = 0;
+ d->use_auto_busy = 0;
+ DBG(d,"start\r\n");
+ return (ErlDrvData) d;
+}
+
+void stop(ErlDrvData drv_data) {
+ SchedDrvData *d = (SchedDrvData*)drv_data;
+ driver_output(d->port,d->data,d->curr);
+ DBG(d,"close\r\n");
+ driver_free(d);
+ return;
+}
+
+void timeout(ErlDrvData drv_data) {
+ SchedDrvData *d = (SchedDrvData*)drv_data;
+ set_busy_port(d->port, 0);
+ DBG(d,"timeout\r\n");
+}
+
+void output(ErlDrvData drv_data, char *buf, ErlDrvSizeT len)
+{
+ int res;
+ unsigned int command = *buf;
+ SchedDrvData *d = (SchedDrvData*)drv_data;
+
+ switch (command) {
+ case 'B': /* busy */
+ DBG(d,"busy: ");
+ set_busy_port(d->port, 1);
+ break;
+ case 'L': /* busy long call */
+ DBG(d,"long: ");
+ delay(buf[5]*100);
+ set_busy_port(d->port, 1);
+ break;
+ case 'D': /* delay call */
+ DBG(d,"delay: ");
+ delay(buf[5]*100);
+ break;
+ case 'N': /* not busy */
+ DBG(d,"not");
+ set_busy_port(d->port, 0);
+ goto done;
+ case 'C': /* change state */
+ DBG(d,"chang: ");
+ break;
+ case 'G': /* get state */
+ DBG(d,"get : ");
+ driver_output(d->port,d->data,d->curr);
+ return;
+ default:
+ driver_failure_posix((ErlDrvPort) drv_data, EINVAL);
+ break;
+ }
+ if (len > 1) {
+ unsigned int val = get_int32(buf+1);
+ fprintf(stderr,"%u",val);
+ d->data[d->curr++] = val;
+ }
+ done:
+ fprintf(stderr,"\r\n");
+}
+
+ErlDrvSSizeT control(ErlDrvData drv_data, unsigned int command, char *buf,
+ ErlDrvSizeT len, char **rbuf, ErlDrvSizeT rlen)
+{
+ switch (command) {
+ case 'B': /* busy */
+ set_busy_port((ErlDrvPort) drv_data, 1);
+ break;
+ case 'N': /* not busy */
+ set_busy_port((ErlDrvPort) drv_data, 0);
+ break;
+ default:
+ driver_failure_posix((ErlDrvPort) drv_data, EINVAL);
+ break;
+ }
+ return 0;
+}
+
+
+/*
+ * Delays (sleeps) the given number of milli-seconds.
+ */
+
+static void delay(unsigned ms)
+{
+ fprintf(stderr,"delay(%u)",ms);
+#ifdef __WIN32__
+ Sleep(ms);
+#else
+ struct timeval t;
+ t.tv_sec = ms/1000;
+ t.tv_usec = (ms % 1000) * 1000;
+
+ select(0, NULL, NULL, NULL, &t);
+#endif
+}
diff --git a/erts/emulator/test/code_SUITE.erl b/erts/emulator/test/code_SUITE.erl
index 74ce5e397a..b0408cabe1 100644
--- a/erts/emulator/test/code_SUITE.erl
+++ b/erts/emulator/test/code_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/erts/emulator/test/ddll_SUITE.erl b/erts/emulator/test/ddll_SUITE.erl
index 6e15c228cd..4675cab15c 100644
--- a/erts/emulator/test/ddll_SUITE.erl
+++ b/erts/emulator/test/ddll_SUITE.erl
@@ -136,8 +136,8 @@ delayed_unload_with_ports(Config) when is_list(Config) ->
?line {ok,pending_driver,Ref} = erl_ddll:try_unload(echo_drv,[{monitor, pending_driver}]),
?line ok = receive _ -> false after 0 -> ok end,
?line Port ! {self(), close},
- ?line 1 = erl_ddll:info(echo_drv, port_count),
?line ok = receive {Port,closed} -> ok after 1000 -> false end,
+ ?line 1 = erl_ddll:info(echo_drv, port_count),
?line Port2 ! {self(), close},
?line ok = receive {Port2,closed} -> ok after 1000 -> false end,
?line ok = receive {'DOWN', Ref, driver, echo_drv, unloaded} -> ok after 1000 -> false end,
diff --git a/erts/emulator/test/distribution_SUITE.erl b/erts/emulator/test/distribution_SUITE.erl
index 2ddb4d76d9..101007c288 100644
--- a/erts/emulator/test/distribution_SUITE.erl
+++ b/erts/emulator/test/distribution_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,7 +18,17 @@
%%
-module(distribution_SUITE).
--compile(r13).
+-compile(r15).
+
+-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).
+-define(ATOM_UTF8_EXT, 118).
+-define(SMALL_ATOM_UTF8_EXT, 119).
%% Tests distribution and the tcp driver.
@@ -37,8 +47,10 @@
dist_auto_connect_never/1, dist_auto_connect_once/1,
dist_parallel_send/1,
atom_roundtrip/1,
- atom_roundtrip_r13b/1,
+ unicode_atom_roundtrip/1,
+ atom_roundtrip_r15b/1,
contended_atom_cache_entry/1,
+ contended_unicode_atom_cache_entry/1,
bad_dist_structure/1,
bad_dist_ext_receive/1,
bad_dist_ext_process_info/1,
@@ -62,8 +74,9 @@ all() ->
link_to_dead_new_node, applied_monitor_node,
ref_port_roundtrip, nil_roundtrip, stop_dist,
{group, trap_bif}, {group, dist_auto_connect},
- dist_parallel_send, atom_roundtrip, atom_roundtrip_r13b,
- contended_atom_cache_entry, bad_dist_structure, {group, bad_dist_ext}].
+ dist_parallel_send, atom_roundtrip, unicode_atom_roundtrip, atom_roundtrip_r15b,
+ contended_atom_cache_entry, contended_unicode_atom_cache_entry,
+ bad_dist_structure, {group, bad_dist_ext}].
groups() ->
[{bulk_send, [], [bulk_send_small, bulk_send_big, bulk_send_bigbig]},
@@ -98,19 +111,6 @@ end_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
Dog=?config(watchdog, Config),
?t:timetrap_cancel(Dog).
-%%% Don't be too hard on vxworks, the cross server gets nodedown
-%%% cause the card is too busy if we don't sleep a little between pings.
-sleep() ->
- case os:type() of
- vxworks ->
- receive
- after 10 ->
- ok
- end;
- _ ->
- ok
- end.
-
ping(doc) ->
["Tests pinging a node in different ways."];
ping(Config) when is_list(Config) ->
@@ -122,23 +122,21 @@ ping(Config) when is_list(Config) ->
?line Host = hostname(),
?line BadName = list_to_atom("__pucko__@" ++ Host),
?line io:format("Pinging ~s (assumed to not exist)", [BadName]),
- ?line test_server:do_times(Times,
- fun() -> pang = net_adm:ping(BadName),
- sleep()
+ ?line test_server:do_times(Times, fun() -> pang = net_adm:ping(BadName)
end),
%% Pings another node.
?line {ok, OtherNode} = start_node(distribution_SUITE_other),
?line io:format("Pinging ~s (assumed to exist)", [OtherNode]),
- ?line test_server:do_times(Times, fun() -> pong = net_adm:ping(OtherNode),sleep() end),
+ ?line test_server:do_times(Times, fun() -> pong = net_adm:ping(OtherNode) end),
?line stop_node(OtherNode),
%% Pings our own node many times.
?line Node = node(),
?line io:format("Pinging ~s (the same node)", [Node]),
- ?line test_server:do_times(Times, fun() -> pong = net_adm:ping(Node),sleep() end),
+ ?line test_server:do_times(Times, fun() -> pong = net_adm:ping(Node) end),
ok.
@@ -1100,19 +1098,27 @@ atom_roundtrip(Config) when is_list(Config) ->
?line stop_node(Node),
?line ok.
-atom_roundtrip_r13b(Config) when is_list(Config) ->
- case ?t:is_release_available("r13b") of
+atom_roundtrip_r15b(Config) when is_list(Config) ->
+ case ?t:is_release_available("r15b") of
true ->
?line AtomData = atom_data(),
?line verify_atom_data(AtomData),
- ?line {ok, Node} = start_node(Config, [], "r13b"),
+ ?line {ok, Node} = start_node(Config, [], "r15b"),
?line do_atom_roundtrip(Node, AtomData),
?line stop_node(Node),
?line ok;
false ->
- ?line {skip,"No OTP R13B available"}
+ ?line {skip,"No OTP R15B available"}
end.
+unicode_atom_roundtrip(Config) when is_list(Config) ->
+ ?line AtomData = unicode_atom_data(),
+ ?line verify_atom_data(AtomData),
+ ?line {ok, Node} = start_node(Config),
+ ?line do_atom_roundtrip(Node, AtomData),
+ ?line stop_node(Node),
+ ?line ok.
+
do_atom_roundtrip(Node, AtomData) ->
?line Parent = self(),
?line Proc = spawn_link(Node, fun () -> verify_atom_data_loop(Parent) end),
@@ -1143,12 +1149,76 @@ atom_data() ->
lists:seq(1, 2000)).
verify_atom_data(AtomData) ->
- lists:foreach(fun ({Atom, AtomTxt}) ->
- AtomTxt = atom_to_list(Atom)
+ lists:foreach(fun ({Atom, AtomTxt}) when is_atom(Atom) ->
+ AtomTxt = atom_to_list(Atom);
+ ({PPR, AtomTxt}) ->
+ % Pid, Port, or Ref
+ AtomTxt = atom_to_list(node(PPR))
end,
AtomData).
+uc_atom_tup(ATxt) ->
+ Atom = string_to_atom(ATxt),
+ ATxt = atom_to_list(Atom),
+ {Atom, ATxt}.
+
+uc_pid_tup(ATxt) ->
+ ATxtExt = string_to_atom_ext(ATxt),
+ Pid = mk_pid({ATxtExt, 1}, 4711,17),
+ true = is_pid(Pid),
+ Atom = node(Pid),
+ true = is_atom(Atom),
+ ATxt = atom_to_list(Atom),
+ {Pid, ATxt}.
+
+uc_port_tup(ATxt) ->
+ ATxtExt = string_to_atom_ext(ATxt),
+ Port = mk_port({ATxtExt, 2}, 4711),
+ true = is_port(Port),
+ Atom = node(Port),
+ true = is_atom(Atom),
+ ATxt = atom_to_list(Atom),
+ {Port, ATxt}.
+
+uc_ref_tup(ATxt) ->
+ ATxtExt = string_to_atom_ext(ATxt),
+ Ref = mk_ref({ATxtExt, 3}, [4711,17, 4711]),
+ true = is_reference(Ref),
+ Atom = node(Ref),
+ true = is_atom(Atom),
+ ATxt = atom_to_list(Atom),
+ {Ref, ATxt}.
+
+
+unicode_atom_data() ->
+ [uc_pid_tup(lists:seq(16#1f600, 16#1f600+249) ++ "@host"),
+ uc_pid_tup(lists:seq(16#1f600, 16#1f600+30) ++ "@host"),
+ uc_port_tup(lists:seq(16#1f600, 16#1f600+249) ++ "@host"),
+ uc_port_tup(lists:seq(16#1f600, 16#1f600+30) ++ "@host"),
+ uc_ref_tup(lists:seq(16#1f600, 16#1f600+249) ++ "@host"),
+ uc_ref_tup(lists:seq(16#1f600, 16#1f600+30) ++ "@host"),
+ uc_atom_tup(lists:seq(16#1f600, 16#1f600+254)),
+ uc_atom_tup(lists:seq(16#1f600, 16#1f600+63)),
+ uc_atom_tup(lists:seq(0, 254)),
+ uc_atom_tup(lists:seq(100, 163)),
+ uc_atom_tup(lists:seq(200, 354)),
+ uc_atom_tup(lists:seq(200, 263)),
+ uc_atom_tup(lists:seq(2000, 2254)),
+ uc_atom_tup(lists:seq(2000, 2063)),
+ uc_atom_tup(lists:seq(65500, 65754)),
+ uc_atom_tup(lists:seq(65500, 65563))
+ | lists:map(fun (N) ->
+ uc_atom_tup(lists:seq(64000+N, 64254+N))
+ end,
+ lists:seq(1, 2000))].
+
contended_atom_cache_entry(Config) when is_list(Config) ->
+ contended_atom_cache_entry_test(Config, latin1).
+
+contended_unicode_atom_cache_entry(Config) when is_list(Config) ->
+ contended_atom_cache_entry_test(Config, unicode).
+
+contended_atom_cache_entry_test(Config, Type) ->
?line TestServer = self(),
?line ProcessPairs = 10,
?line Msgs = 100000,
@@ -1162,9 +1232,16 @@ contended_atom_cache_entry(Config) when is_list(Config) ->
true),
Master = self(),
CIX = get_cix(),
- TestAtoms = get_conflicting_atoms(CIX, ProcessPairs),
+ TestAtoms = case Type of
+ latin1 ->
+ get_conflicting_atoms(CIX,
+ ProcessPairs);
+ unicode ->
+ get_conflicting_unicode_atoms(CIX,
+ ProcessPairs)
+ end,
io:format("Testing with the following atoms all using "
- "cache index ~p:~n ~p~n",
+ "cache index ~p:~n ~w~n",
[CIX, TestAtoms]),
Ps = lists:map(
fun (A) ->
@@ -1174,8 +1251,12 @@ contended_atom_cache_entry(Config) when is_list(Config) ->
fun () ->
Atom = receive
{Ref, txt, ATxt} ->
- list_to_atom(
- ATxt)
+ case Type of
+ latin1 ->
+ list_to_atom(ATxt);
+ unicode ->
+ string_to_atom(ATxt)
+ end
end,
receive_ref_atom(Ref,
Atom,
@@ -1267,6 +1348,20 @@ get_conflicting_atoms(CIX, N) ->
get_conflicting_atoms(CIX, N)
end.
+get_conflicting_unicode_atoms(_CIX, 0) ->
+ [];
+get_conflicting_unicode_atoms(CIX, N) ->
+ {A, B, C} = now(),
+ Atom = string_to_atom([16#1f608] ++ "atom" ++ integer_to_list(A*1000000000000
+ + B*1000000
+ + C)),
+ case erts_debug:get_internal_state({atom_out_cache_index, Atom}) of
+ CIX ->
+ [Atom|get_conflicting_unicode_atoms(CIX, N-1)];
+ _ ->
+ get_conflicting_unicode_atoms(CIX, N)
+ end.
+
-define(COOKIE, '').
-define(DOP_LINK, 1).
-define(DOP_SEND, 2).
@@ -2146,3 +2241,190 @@ repeat(_Fun, 0) ->
repeat(Fun, N) ->
Fun(),
repeat(Fun, N-1).
+
+string_to_atom_ext(String) ->
+ Utf8List = string_to_utf8_list(String),
+ Len = length(Utf8List),
+ case Len < 256 of
+ true ->
+ [?SMALL_ATOM_UTF8_EXT, Len | Utf8List];
+ false ->
+ [?ATOM_UTF8_EXT, Len bsr 8, Len band 16#ff | Utf8List]
+ end.
+
+string_to_atom(String) ->
+ binary_to_term(list_to_binary([?VERSION_MAGIC
+ | string_to_atom_ext(String)])).
+
+string_to_utf8_list([]) ->
+ [];
+string_to_utf8_list([CP|CPs]) when is_integer(CP),
+ 0 =< CP,
+ CP =< 16#7F ->
+ [CP | string_to_utf8_list(CPs)];
+string_to_utf8_list([CP|CPs]) when is_integer(CP),
+ 16#80 =< CP,
+ CP =< 16#7FF ->
+ [16#C0 bor (CP bsr 6),
+ 16#80 bor (16#3F band CP)
+ | string_to_utf8_list(CPs)];
+string_to_utf8_list([CP|CPs]) when is_integer(CP),
+ 16#800 =< CP,
+ CP =< 16#FFFF ->
+ [16#E0 bor (CP bsr 12),
+ 16#80 bor (16#3F band (CP bsr 6)),
+ 16#80 bor (16#3F band CP)
+ | string_to_utf8_list(CPs)];
+string_to_utf8_list([CP|CPs]) when is_integer(CP),
+ 16#10000 =< CP,
+ CP =< 16#10FFFF ->
+ [16#F0 bor (CP bsr 18),
+ 16#80 bor (16#3F band (CP bsr 12)),
+ 16#80 bor (16#3F band (CP bsr 6)),
+ 16#80 bor (16#3F band CP)
+ | string_to_utf8_list(CPs)].
+
+utf8_list_to_string([]) ->
+ [];
+utf8_list_to_string([B|Bs]) when is_integer(B),
+ 0 =< B,
+ B =< 16#7F ->
+ [B | utf8_list_to_string(Bs)];
+utf8_list_to_string([B0, B1 | Bs]) when is_integer(B0),
+ 16#C0 =< B0,
+ B0 =< 16#DF,
+ is_integer(B1),
+ 16#80 =< B1,
+ B1 =< 16#BF ->
+ [(((B0 band 16#1F) bsl 6)
+ bor (B1 band 16#3F))
+ | utf8_list_to_string(Bs)];
+utf8_list_to_string([B0, B1, B2 | Bs]) when is_integer(B0),
+ 16#E0 =< B0,
+ B0 =< 16#EF,
+ is_integer(B1),
+ 16#80 =< B1,
+ B1 =< 16#BF,
+ is_integer(B2),
+ 16#80 =< B2,
+ B2 =< 16#BF ->
+ [(((B0 band 16#F) bsl 12)
+ bor ((B1 band 16#3F) bsl 6)
+ bor (B2 band 16#3F))
+ | utf8_list_to_string(Bs)];
+utf8_list_to_string([B0, B1, B2, B3 | Bs]) when is_integer(B0),
+ 16#F0 =< B0,
+ B0 =< 16#F7,
+ is_integer(B1),
+ 16#80 =< B1,
+ B1 =< 16#BF,
+ is_integer(B2),
+ 16#80 =< B2,
+ B2 =< 16#BF,
+ is_integer(B3),
+ 16#80 =< B3,
+ B3 =< 16#BF ->
+ [(((B0 band 16#7) bsl 18)
+ bor ((B1 band 16#3F) bsl 12)
+ bor ((B2 band 16#3F) bsl 6)
+ bor (B3 band 16#3F))
+ | utf8_list_to_string(Bs)].
+
+mk_pid({NodeName, Creation}, Number, Serial) when is_atom(NodeName) ->
+ <<?VERSION_MAGIC, NodeNameExt/binary>> = term_to_binary(NodeName),
+ mk_pid({NodeNameExt, Creation}, Number, Serial);
+mk_pid({NodeNameExt, Creation}, Number, Serial) ->
+ case catch binary_to_term(list_to_binary([?VERSION_MAGIC,
+ ?PID_EXT,
+ NodeNameExt,
+ uint32_be(Number),
+ uint32_be(Serial),
+ uint8(Creation)])) of
+ Pid when is_pid(Pid) ->
+ Pid;
+ {'EXIT', {badarg, _}} ->
+ exit({badarg, mk_pid, [{NodeNameExt, Creation}, Number, Serial]});
+ Other ->
+ exit({unexpected_binary_to_term_result, Other})
+ end.
+
+mk_port({NodeName, Creation}, Number) when is_atom(NodeName) ->
+ <<?VERSION_MAGIC, NodeNameExt/binary>> = term_to_binary(NodeName),
+ mk_port({NodeNameExt, Creation}, Number);
+mk_port({NodeNameExt, Creation}, Number) ->
+ case catch binary_to_term(list_to_binary([?VERSION_MAGIC,
+ ?PORT_EXT,
+ NodeNameExt,
+ uint32_be(Number),
+ uint8(Creation)])) of
+ Port when is_port(Port) ->
+ Port;
+ {'EXIT', {badarg, _}} ->
+ exit({badarg, mk_port, [{NodeNameExt, Creation}, Number]});
+ Other ->
+ exit({unexpected_binary_to_term_result, Other})
+ end.
+
+mk_ref({NodeName, Creation}, [Number] = NL) when is_atom(NodeName),
+ is_integer(Creation),
+ is_integer(Number) ->
+ <<?VERSION_MAGIC, NodeNameExt/binary>> = term_to_binary(NodeName),
+ mk_ref({NodeNameExt, Creation}, NL);
+mk_ref({NodeNameExt, Creation}, [Number]) when is_integer(Creation),
+ is_integer(Number) ->
+ case catch binary_to_term(list_to_binary([?VERSION_MAGIC,
+ ?REFERENCE_EXT,
+ NodeNameExt,
+ uint32_be(Number),
+ uint8(Creation)])) of
+ Ref when is_reference(Ref) ->
+ Ref;
+ {'EXIT', {badarg, _}} ->
+ exit({badarg, mk_ref, [{NodeNameExt, Creation}, [Number]]});
+ Other ->
+ exit({unexpected_binary_to_term_result, Other})
+ end;
+mk_ref({NodeName, Creation}, Numbers) when is_atom(NodeName),
+ is_integer(Creation),
+ is_list(Numbers) ->
+ <<?VERSION_MAGIC, NodeNameExt/binary>> = term_to_binary(NodeName),
+ mk_ref({NodeNameExt, Creation}, Numbers);
+mk_ref({NodeNameExt, Creation}, Numbers) when is_integer(Creation),
+ is_list(Numbers) ->
+ case catch binary_to_term(list_to_binary([?VERSION_MAGIC,
+ ?NEW_REFERENCE_EXT,
+ uint16_be(length(Numbers)),
+ NodeNameExt,
+ uint8(Creation),
+ lists:map(fun (N) ->
+ uint32_be(N)
+ end,
+ Numbers)])) of
+ Ref when is_reference(Ref) ->
+ Ref;
+ {'EXIT', {badarg, _}} ->
+ exit({badarg, mk_ref, [{NodeNameExt, Creation}, Numbers]});
+ Other ->
+ exit({unexpected_binary_to_term_result, Other})
+ end.
+
+
+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]}).
diff --git a/erts/emulator/test/driver_SUITE.erl b/erts/emulator/test/driver_SUITE.erl
index 643357263c..13f18b4563 100644
--- a/erts/emulator/test/driver_SUITE.erl
+++ b/erts/emulator/test/driver_SUITE.erl
@@ -77,7 +77,8 @@
thread_mseg_alloc_cache_clean/1,
otp_9302/1,
thr_free_drv/1,
- async_blast/1]).
+ async_blast/1,
+ thr_msg_blast/1]).
-export([bin_prefix/2]).
@@ -147,7 +148,8 @@ all() ->
thread_mseg_alloc_cache_clean,
otp_9302,
thr_free_drv,
- async_blast].
+ async_blast,
+ thr_msg_blast].
groups() ->
[{timer, [],
@@ -1136,7 +1138,9 @@ check_driver_system_info_result(Result) ->
{{1, 1}, _} ->
?line ExpNs = lists:sort(?EXPECTED_SYSTEM_INFO_NAMES
-- ?EXPECTED_SYSTEM_INFO_NAMES2),
- ?line ExpNs = lists:sort(Ns)
+ ?line ExpNs = lists:sort(Ns);
+ {{2, 0}, _} ->
+ ?line [] = Ns
end.
chk_sis(SIs, Ns) ->
@@ -2010,7 +2014,64 @@ async_blast(Config) when is_list(Config) ->
?line erlang:display({async_blast_time, AsyncBlastTime}),
?line ok.
+thr_msg_blast_receiver(_Port, N, N) ->
+ ok;
+thr_msg_blast_receiver(Port, N, Max) ->
+ receive
+ {Port, hi} ->
+ thr_msg_blast_receiver(Port, N+1, Max)
+ end.
+
+thr_msg_blast_receiver_proc(Port, Max, Parent, Done) ->
+ case port_control(Port, 0, "") of
+ "receiver" ->
+ spawn(fun () ->
+ thr_msg_blast_receiver_proc(Port, Max+1, Parent, Done)
+ end),
+ thr_msg_blast_receiver(Port, 0, Max);
+ "done" ->
+ Parent ! Done
+ end.
+thr_msg_blast(Config) when is_list(Config) ->
+ case erlang:system_info(smp_support) of
+ false ->
+ {skipped, "Non-SMP emulator; nothing to test..."};
+ true ->
+ Path = ?config(data_dir, Config),
+ erl_ddll:start(),
+ ok = load_driver(Path, thr_msg_blast_drv),
+ MemBefore = driver_alloc_size(),
+ Start = os:timestamp(),
+ Port = open_port({spawn, thr_msg_blast_drv}, []),
+ true = is_port(Port),
+ Done = make_ref(),
+ Me = self(),
+ spawn(fun () ->
+ thr_msg_blast_receiver_proc(Port, 1, Me, Done)
+ end),
+ receive
+ Done -> ok
+ end,
+ ok = thr_msg_blast_receiver(Port, 0, 32*10000),
+ port_close(Port),
+ End = os:timestamp(),
+ receive
+ Garbage ->
+ ?t:fail({received_garbage, Port, Garbage})
+ after 2000 ->
+ ok
+ end,
+ MemAfter = driver_alloc_size(),
+ io:format("MemBefore=~p, MemAfter=~p~n",
+ [MemBefore, MemAfter]),
+ ThrMsgBlastTime = timer:now_diff(End,Start)/1000000,
+ io:format("ThrMsgBlastTime=~p~n", [ThrMsgBlastTime]),
+ MemBefore = MemAfter,
+ Res = {thr_msg_blast_time, ThrMsgBlastTime},
+ erlang:display(Res),
+ Res
+ end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Utilities
diff --git a/erts/emulator/test/driver_SUITE_data/Makefile.src b/erts/emulator/test/driver_SUITE_data/Makefile.src
index 9cc107cc66..b667dff6b6 100644
--- a/erts/emulator/test/driver_SUITE_data/Makefile.src
+++ b/erts/emulator/test/driver_SUITE_data/Makefile.src
@@ -14,7 +14,8 @@ MISC_DRVS = outputv_drv@dll@ \
thr_alloc_drv@dll@ \
otp_9302_drv@dll@ \
thr_free_drv@dll@ \
- async_blast_drv@dll@
+ async_blast_drv@dll@ \
+ thr_msg_blast_drv@dll@
SYS_INFO_DRVS = sys_info_base_drv@dll@ \
sys_info_prev_drv@dll@ \
diff --git a/erts/emulator/test/driver_SUITE_data/async_blast_drv.c b/erts/emulator/test/driver_SUITE_data/async_blast_drv.c
index c2086c5860..d72b20d143 100644
--- a/erts/emulator/test/driver_SUITE_data/async_blast_drv.c
+++ b/erts/emulator/test/driver_SUITE_data/async_blast_drv.c
@@ -56,6 +56,7 @@ static ErlDrvEntry async_blast_drv_entry = {
typedef struct {
ErlDrvPort port;
+ ErlDrvTermData port_id;
ErlDrvTermData caller;
int counter;
} async_blast_data_t;
@@ -81,6 +82,7 @@ static ErlDrvData start(ErlDrvPort port,
return ERL_DRV_ERROR_GENERAL;
abd->port = port;
+ abd->port_id = driver_mk_port(port);
abd->counter = 0;
return (ErlDrvData) abd;
}
@@ -97,12 +99,12 @@ static void ready_async(ErlDrvData drv_data,
async_blast_data_t *abd = (async_blast_data_t *) drv_data;
if (--abd->counter == 0) {
ErlDrvTermData spec[] = {
- ERL_DRV_PORT, driver_mk_port(abd->port),
+ ERL_DRV_PORT, abd->port_id,
ERL_DRV_ATOM, driver_mk_atom("done"),
ERL_DRV_TUPLE, 2
};
- driver_send_term(abd->port, abd->caller,
- spec, sizeof(spec)/sizeof(spec[0]));
+ erl_drv_send_term(abd->port_id, abd->caller,
+ spec, sizeof(spec)/sizeof(spec[0]));
}
}
diff --git a/erts/emulator/test/driver_SUITE_data/caller_drv.c b/erts/emulator/test/driver_SUITE_data/caller_drv.c
index 1ed20b0638..2731f9b317 100644
--- a/erts/emulator/test/driver_SUITE_data/caller_drv.c
+++ b/erts/emulator/test/driver_SUITE_data/caller_drv.c
@@ -85,9 +85,9 @@ send_caller(ErlDrvData drv_data, char *func)
ERL_DRV_PID, driver_caller(port),
ERL_DRV_TUPLE, (ErlDrvTermData) 4
};
- res = driver_output_term(port, msg, sizeof(msg)/sizeof(ErlDrvTermData));
+ res = erl_drv_output_term(driver_mk_port(port), msg, sizeof(msg)/sizeof(ErlDrvTermData));
if (res <= 0)
- driver_failure_atom(port, "driver_output_term failed");
+ driver_failure_atom(port, "erl_drv_output_term failed");
}
static ErlDrvData
diff --git a/erts/emulator/test/driver_SUITE_data/chkio_drv.c b/erts/emulator/test/driver_SUITE_data/chkio_drv.c
index 40f1ad4fea..faf1040276 100644
--- a/erts/emulator/test/driver_SUITE_data/chkio_drv.c
+++ b/erts/emulator/test/driver_SUITE_data/chkio_drv.c
@@ -17,7 +17,7 @@
*/
#ifndef UNIX
-#if !defined(__WIN32__) && !defined(VXWORKS)
+#if !defined(__WIN32__)
#define UNIX 1
#endif
#endif
diff --git a/erts/emulator/test/driver_SUITE_data/io_ready_exit_drv.c b/erts/emulator/test/driver_SUITE_data/io_ready_exit_drv.c
index e6a3edcd74..1e107309df 100644
--- a/erts/emulator/test/driver_SUITE_data/io_ready_exit_drv.c
+++ b/erts/emulator/test/driver_SUITE_data/io_ready_exit_drv.c
@@ -17,7 +17,7 @@
*/
#ifndef UNIX
-#if !defined(__WIN32__) && !defined(VXWORKS)
+#if !defined(__WIN32__)
#define UNIX 1
#endif
#endif
diff --git a/erts/emulator/test/driver_SUITE_data/ioq_exit_drv.c b/erts/emulator/test/driver_SUITE_data/ioq_exit_drv.c
index b2cc1e785a..d174771629 100644
--- a/erts/emulator/test/driver_SUITE_data/ioq_exit_drv.c
+++ b/erts/emulator/test/driver_SUITE_data/ioq_exit_drv.c
@@ -29,7 +29,7 @@
*/
#ifndef UNIX
-#if !defined(__WIN32__) && !defined(VXWORKS)
+#if !defined(__WIN32__)
#define UNIX 1
#endif
#endif
diff --git a/erts/emulator/test/driver_SUITE_data/missing_callback_drv.c b/erts/emulator/test/driver_SUITE_data/missing_callback_drv.c
index e7d9a294fa..851f2c745b 100644
--- a/erts/emulator/test/driver_SUITE_data/missing_callback_drv.c
+++ b/erts/emulator/test/driver_SUITE_data/missing_callback_drv.c
@@ -17,7 +17,7 @@
*/
#ifndef UNIX
-#if !defined(__WIN32__) && !defined(VXWORKS)
+#if !defined(__WIN32__)
#define UNIX 1
#endif
#endif
diff --git a/erts/emulator/test/driver_SUITE_data/monitor_drv.c b/erts/emulator/test/driver_SUITE_data/monitor_drv.c
index 3da067fd09..81dfb65191 100644
--- a/erts/emulator/test/driver_SUITE_data/monitor_drv.c
+++ b/erts/emulator/test/driver_SUITE_data/monitor_drv.c
@@ -117,7 +117,7 @@ static void handle_monitor(ErlDrvData drv_data, ErlDrvMonitor *monitor)
o->next = p->next;
}
driver_free(p);
- driver_send_term(data->port, data->ipid, spec, sizeof(spec)/sizeof(ErlDrvTermData));
+ erl_drv_send_term(driver_mk_port(data->port), data->ipid, spec, sizeof(spec)/sizeof(ErlDrvTermData));
}
return;
diff --git a/erts/emulator/test/driver_SUITE_data/otp_9302_drv.c b/erts/emulator/test/driver_SUITE_data/otp_9302_drv.c
index 221fd0ce51..93ef767d75 100644
--- a/erts/emulator/test/driver_SUITE_data/otp_9302_drv.c
+++ b/erts/emulator/test/driver_SUITE_data/otp_9302_drv.c
@@ -134,8 +134,8 @@ static void send_reply(Otp9302AsyncData *adata)
ERL_DRV_ATOM, adata->term_data.msg,
ERL_DRV_TUPLE, 2
};
- driver_send_term(adata->port, adata->term_data.receiver,
- spec, sizeof(spec)/sizeof(spec[0]));
+ erl_drv_send_term(adata->term_data.port, adata->term_data.receiver,
+ spec, sizeof(spec)/sizeof(spec[0]));
}
static void enqueue_reply(Otp9302AsyncData *adata)
diff --git a/erts/emulator/test/driver_SUITE_data/peek_non_existing_queue_drv.c b/erts/emulator/test/driver_SUITE_data/peek_non_existing_queue_drv.c
index 8e203f74ec..cbee1c3dce 100644
--- a/erts/emulator/test/driver_SUITE_data/peek_non_existing_queue_drv.c
+++ b/erts/emulator/test/driver_SUITE_data/peek_non_existing_queue_drv.c
@@ -28,7 +28,7 @@
*/
#ifndef UNIX
-#if !defined(__WIN32__) && !defined(VXWORKS)
+#if !defined(__WIN32__)
#define UNIX 1
#endif
#endif
@@ -177,15 +177,16 @@ static void ready_async(ErlDrvData drv_data, ErlDrvThreadData thread_data)
{
PeekNonXQDrvData *dp = (PeekNonXQDrvData *) drv_data;
if (dp->cmd == PEEK_NONXQ_WAIT) {
+ ErlDrvTermData port_id = driver_mk_port(dp->port);
ErlDrvTermData spec[] = {
- ERL_DRV_PORT, driver_mk_port(dp->port),
+ ERL_DRV_PORT, port_id,
ERL_DRV_ATOM, driver_mk_atom("test_successful"),
ERL_DRV_TUPLE, 2
};
- driver_send_term(dp->port,
- dp->caller,
- spec,
- sizeof(spec) / sizeof(spec[0]));
+ erl_drv_send_term(port_id,
+ dp->caller,
+ spec,
+ sizeof(spec) / sizeof(spec[0]));
}
if (thread_data)
driver_free(thread_data);
diff --git a/erts/emulator/test/driver_SUITE_data/thr_msg_blast_drv.c b/erts/emulator/test/driver_SUITE_data/thr_msg_blast_drv.c
new file mode 100644
index 0000000000..5a9112afa3
--- /dev/null
+++ b/erts/emulator/test/driver_SUITE_data/thr_msg_blast_drv.c
@@ -0,0 +1,178 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2012. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, 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"
+
+#define THR_MSG_BLAST_NO_PROCS 10
+#define THR_MSG_BLAST_NO_SENDS_PER_PROC 10000
+
+#define THR_MSG_BLAST_THREADS 32
+
+static void stop(ErlDrvData drv_data);
+static ErlDrvData start(ErlDrvPort port,
+ char *command);
+static ErlDrvSSizeT control(ErlDrvData drv_data,
+ unsigned int command,
+ char *buf, ErlDrvSizeT len,
+ char **rbuf, ErlDrvSizeT rlen);
+
+static ErlDrvEntry thr_msg_blast_drv_entry = {
+ NULL /* init */,
+ start,
+ stop,
+ NULL /* output */,
+ NULL /* ready_input */,
+ NULL /* ready_output */,
+ "thr_msg_blast_drv",
+ NULL /* finish */,
+ NULL /* handle */,
+ control,
+ NULL /* timeout */,
+ NULL /* outputv */,
+ NULL /* ready_async */,
+ NULL /* flush */,
+ NULL /* call */,
+ NULL /* event */,
+ ERL_DRV_EXTENDED_MARKER,
+ ERL_DRV_EXTENDED_MAJOR_VERSION,
+ ERL_DRV_EXTENDED_MINOR_VERSION,
+ ERL_DRV_FLAG_USE_PORT_LOCKING,
+ NULL /* handle2 */,
+ NULL /* handle_monitor */
+};
+
+typedef struct {
+ ErlDrvPort port;
+ ErlDrvTermData td_port;
+ ErlDrvTermData hi;
+ ErlDrvTid tid[THR_MSG_BLAST_THREADS];
+ int no_thrs;
+ ErlDrvTermData proc[THR_MSG_BLAST_NO_PROCS];
+ int no_procs;
+} thr_msg_blast_data_t;
+
+
+DRIVER_INIT(thr_msg_blast_drv)
+{
+ return &thr_msg_blast_drv_entry;
+}
+
+static void stop(ErlDrvData drv_data)
+{
+ int i;
+ thr_msg_blast_data_t *tmbd = (thr_msg_blast_data_t *) drv_data;
+ for (i = 0; i < tmbd->no_thrs; i++)
+ erl_drv_thread_join(tmbd->tid[i], NULL);
+ driver_free((void *) tmbd);
+}
+
+static ErlDrvData start(ErlDrvPort port,
+ char *command)
+{
+ thr_msg_blast_data_t *tmbd;
+
+ tmbd = driver_alloc(sizeof(thr_msg_blast_data_t));
+ if (!tmbd)
+ return ERL_DRV_ERROR_GENERAL;
+
+ tmbd->port = port;
+ tmbd->td_port = driver_mk_port(port);
+ tmbd->hi = driver_mk_atom("hi");
+ tmbd->no_thrs = 0;
+ tmbd->no_procs = 1;
+ tmbd->proc[0] = driver_caller(port);
+
+ return (ErlDrvData) tmbd;
+}
+
+static void *thread(void *);
+
+static ErlDrvSSizeT control(ErlDrvData drv_data,
+ unsigned int command,
+ char *buf, ErlDrvSizeT len,
+ char **rbuf, ErlDrvSizeT rlen)
+{
+ thr_msg_blast_data_t *tmbd = (thr_msg_blast_data_t *) drv_data;
+ char *res_str = "error";
+
+ if (tmbd->no_procs >= THR_MSG_BLAST_NO_PROCS) {
+ int i;
+ for (i = 0; i < tmbd->no_thrs; i++)
+ erl_drv_thread_join(tmbd->tid[i], NULL);
+ tmbd->no_thrs = 0;
+ res_str = "done";
+ }
+ else {
+
+ tmbd->proc[tmbd->no_procs++] = driver_caller(tmbd->port);
+
+ if (tmbd->no_procs == THR_MSG_BLAST_NO_PROCS) {
+ for (tmbd->no_thrs = 0;
+ tmbd->no_thrs < THR_MSG_BLAST_THREADS;
+ tmbd->no_thrs++) {
+ int res = erl_drv_thread_create("test",
+ &tmbd->tid[tmbd->no_thrs],
+ thread,
+ tmbd,
+ NULL);
+ if (res != 0) {
+ driver_failure_posix(tmbd->port, res);
+ goto done;
+ }
+ }
+ }
+
+ res_str = "receiver";
+ }
+
+ done: {
+ ErlDrvSSizeT res_len = strlen(res_str);
+ if (res_len > rlen) {
+ char *abuf = driver_alloc(sizeof(char)*res_len);
+ if (!abuf)
+ return 0;
+ *rbuf = abuf;
+ }
+
+ memcpy((void *) *rbuf, (void *) res_str, res_len);
+
+ return res_len;
+ }
+}
+
+static void *thread(void *varg)
+{
+ int s, p;
+ thr_msg_blast_data_t *tmbd = (thr_msg_blast_data_t *) varg;
+ ErlDrvTermData spec[] = {
+ ERL_DRV_PORT, tmbd->td_port,
+ ERL_DRV_ATOM, tmbd->hi,
+ ERL_DRV_TUPLE, 2
+ };
+
+ for (s = 0; s < THR_MSG_BLAST_NO_SENDS_PER_PROC; s++) {
+ for (p = 0; p < THR_MSG_BLAST_NO_PROCS; p++) {
+ int res = erl_drv_send_term(tmbd->td_port, tmbd->proc[p],
+ spec, sizeof(spec)/sizeof(spec[0]));
+ if (p == 0 && res <= 0)
+ abort(); /* Could not send to creator */
+ }
+ }
+ return NULL;
+}
diff --git a/erts/emulator/test/driver_SUITE_data/timer_drv.c b/erts/emulator/test/driver_SUITE_data/timer_drv.c
index 8c3f203a64..57538e0d57 100644
--- a/erts/emulator/test/driver_SUITE_data/timer_drv.c
+++ b/erts/emulator/test/driver_SUITE_data/timer_drv.c
@@ -1,11 +1,3 @@
-#ifdef VXWORKS
-#include <vxWorks.h>
-#include <taskVarLib.h>
-#include <taskLib.h>
-#include <sysLib.h>
-#include <string.h>
-#include <ioLib.h>
-#endif
#include <stdio.h>
#include "erl_driver.h"
@@ -84,12 +76,8 @@ static void timer_read(ErlDrvData p, char *buf, ErlDrvSizeT len)
driver_output(port, reply, 1);
} else if (buf[0] == DELAY_START_TIMER) {
#ifndef __WIN32__
-#ifdef VXWORKS
- taskDelay(sysClkRateGet());
-#else
sleep(1);
#endif
-#endif
driver_set_timer(port, get_int32(buf + 1));
}
}
diff --git a/erts/emulator/test/emulator.spec.vxworks b/erts/emulator/test/emulator.spec.vxworks
deleted file mode 100644
index 55675bdc29..0000000000
--- a/erts/emulator/test/emulator.spec.vxworks
+++ /dev/null
@@ -1,26 +0,0 @@
-{topcase, {dir, "../emulator_test"}}.
-
-% Added since R11
-{skip,{distribution_SUITE,link_to_dead_new_node,"Does not work in distributed test environments"}}.
-{skip,{binary_SUITE,terms_float,"Floats, VxWorks, PPC = Floating points never equal..."}}.
-{skip,{system_info_SUITE,process_count,"Fix-allocs starving VxWorks cards"}}.
-{skip,{monitor_SUITE,mixer,"Fix-allocs starving VxWorks cards"}}.
-
-{skip,{node_container_SUITE,"Too memory consuming..."}}.
-
-{skip,{trace_SUITE,system_monitor_long_gc_1,"Too memory consuming..."}}.
-{skip,{trace_SUITE,system_monitor_long_gc_2,"Too memory consuming..."}}.
-{skip,{trace_SUITE,system_monitor_large_heap_1,"Too memory consuming..."}}.
-{skip,{trace_SUITE,system_monitor_large_heap_2,"Too memory consuming..."}}.
-% End added since R11
-
-{skip, {distribution_SUITE,stop_dist,"Not written to work on VxWorks."}}.
-{skip, {distribution_SUITE,dist_auto_connect_never,
- "Not written to work on VxWorks."}}.
-{skip, {distribution_SUITE,dist_auto_connect_once,
- "Not written to work on VxWorks."}}.
-{skip, {trace_SUITE,system_monitor_long_gc,
- "Too memory consuming for VxWorks cards."}}.
-{skip, {trace_meta_SUITE,stack_grow,
- "Too memory consuming for VxWorks cards."}}.
-{skip, {obsolete_SUITE, "Not on vxworks"}}.
diff --git a/erts/emulator/test/emulator_bench.spec b/erts/emulator/test/emulator_bench.spec
new file mode 100644
index 0000000000..f709d913b7
--- /dev/null
+++ b/erts/emulator/test/emulator_bench.spec
@@ -0,0 +1 @@
+{groups,"../emulator_test",estone_SUITE,[estone_bench]}.
diff --git a/erts/emulator/test/erl_drv_thread_SUITE_data/testcase_driver.c b/erts/emulator/test/erl_drv_thread_SUITE_data/testcase_driver.c
index b4542f3e36..2cd3209231 100644
--- a/erts/emulator/test/erl_drv_thread_SUITE_data/testcase_driver.c
+++ b/erts/emulator/test/erl_drv_thread_SUITE_data/testcase_driver.c
@@ -42,6 +42,7 @@
typedef struct {
TestCaseState_t visible;
ErlDrvPort port;
+ ErlDrvTermData port_id;
int result;
jmp_buf done_jmp_buf;
char *comment;
@@ -98,6 +99,7 @@ testcase_drv_start(ErlDrvPort port, char *command)
itcs->visible.testcase_name = testcase_name();
itcs->visible.extra = NULL;
itcs->port = port;
+ itcs->port_id = driver_mk_port(port);
itcs->result = TESTCASE_FAILED;
itcs->comment = "";
@@ -143,7 +145,7 @@ testcase_drv_run(ErlDrvData drv_data, char *buf, ErlDrvSizeT len)
msg[1] = (ErlDrvTermData) result_atom;
msg[2] = ERL_DRV_PORT;
- msg[3] = driver_mk_port(itcs->port);
+ msg[3] = itcs->port_id;
msg[4] = ERL_DRV_ATOM;
msg[5] = driver_mk_atom(itcs->visible.testcase_name);
@@ -155,7 +157,7 @@ testcase_drv_run(ErlDrvData drv_data, char *buf, ErlDrvSizeT len)
msg[9] = ERL_DRV_TUPLE;
msg[10] = (ErlDrvTermData) 4;
- driver_output_term(itcs->port, msg, 11);
+ erl_drv_output_term(itcs->port_id, msg, 11);
}
int
@@ -185,7 +187,7 @@ testcase_printf(TestCaseState_t *tcs, char *frmt, ...)
msg[1] = (ErlDrvTermData) driver_mk_atom("print");
msg[2] = ERL_DRV_PORT;
- msg[3] = driver_mk_port(itcs->port);
+ msg[3] = itcs->port_id;
msg[4] = ERL_DRV_ATOM;
msg[5] = driver_mk_atom(itcs->visible.testcase_name);
@@ -197,7 +199,7 @@ testcase_printf(TestCaseState_t *tcs, char *frmt, ...)
msg[9] = ERL_DRV_TUPLE;
msg[10] = (ErlDrvTermData) 4;
- driver_output_term(itcs->port, msg, 11);
+ erl_drv_output_term(itcs->port_id, msg, 11);
}
diff --git a/erts/emulator/test/estone_SUITE.erl b/erts/emulator/test/estone_SUITE.erl
index 2417d4bcfe..21834bfa62 100644
--- a/erts/emulator/test/estone_SUITE.erl
+++ b/erts/emulator/test/estone_SUITE.erl
@@ -19,7 +19,7 @@
-module(estone_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,estone/1]).
+ init_per_group/2,end_per_group/2,estone/1,estone_bench/1]).
-export([init_per_testcase/2, end_per_testcase/2]).
%% Internal exports for EStone tests
@@ -46,6 +46,7 @@
-include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct_event.hrl").
%% Test suite defines
-define(default_timeout, ?t:minutes(10)).
@@ -80,7 +81,7 @@ all() ->
[estone].
groups() ->
- [].
+ [{estone_bench, [{repeat,50}],[estone_bench]}].
init_per_suite(Config) ->
Config.
@@ -108,6 +109,17 @@ estone(Config) when is_list(Config) ->
?line {comment,Mhz ++ " MHz, " ++
integer_to_list(Stones) ++ " ESTONES"}.
+estone_bench(Config) ->
+ DataDir = ?config(data_dir,Config),
+ L = ?MODULE:macro(?MODULE:micros(),DataDir),
+ [ct_event:notify(
+ #event{name = benchmark_data,
+ data = [{name,proplists:get_value(title,Mark)},
+ {value,proplists:get_value(estones,Mark)}]})
+ || Mark <- L],
+ L.
+
+
%%
%% Calculate CPU speed
%%
diff --git a/erts/emulator/test/estone_SUITE_data/estone_cat.c b/erts/emulator/test/estone_SUITE_data/estone_cat.c
index 8ed9f8375b..a34bda4384 100644
--- a/erts/emulator/test/estone_SUITE_data/estone_cat.c
+++ b/erts/emulator/test/estone_SUITE_data/estone_cat.c
@@ -12,11 +12,7 @@
#include <fcntl.h>
#include <errno.h>
-#ifdef VXWORKS
-estone_cat(argc, argv)
-#else
main(argc, argv)
-#endif
int argc;
char *argv[];
{
diff --git a/erts/emulator/test/float_SUITE.erl b/erts/emulator/test/float_SUITE.erl
index 8e6923ce9f..4a45afa9e9 100644
--- a/erts/emulator/test/float_SUITE.erl
+++ b/erts/emulator/test/float_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -25,7 +25,7 @@
init_per_group/2,end_per_group/2,
init_per_testcase/2,end_per_testcase/2,
fpe/1,fp_drv/1,fp_drv_thread/1,denormalized/1,match/1,
- bad_float_unpack/1,cmp_zero/1, cmp_integer/1, cmp_bignum/1]).
+ bad_float_unpack/1, write/1, cmp_zero/1, cmp_integer/1, cmp_bignum/1]).
-export([otp_7178/1]).
-export([hidden_inf/1]).
@@ -42,7 +42,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[fpe, fp_drv, fp_drv_thread, otp_7178, denormalized,
- match, bad_float_unpack, {group, comparison}
+ match, bad_float_unpack, write, {group, comparison}
,hidden_inf
].
@@ -190,6 +190,11 @@ bad_float_unpack(Config) when is_list(Config) ->
bad_float_unpack_match(<<F:64/float>>) -> F;
bad_float_unpack_match(<<I:64/integer-signed>>) -> I.
+%% Exposes endianness issues.
+
+write(Config) when is_list(Config) ->
+ "1.0" = io_lib:write(1.0).
+
cmp_zero(_Config) ->
cmp(0.5e-323,0).
diff --git a/erts/emulator/test/fun_SUITE.erl b/erts/emulator/test/fun_SUITE.erl
index 839ad6a4f4..36ba4e0f48 100644
--- a/erts/emulator/test/fun_SUITE.erl
+++ b/erts/emulator/test/fun_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -726,8 +726,8 @@ t_arity(Config) when is_list(Config) ->
ok.
t_is_function2(Config) when is_list(Config) ->
- ?line true = is_function({a,b}, 0),
- ?line true = is_function({a,b}, 234343434333433433),
+ false = is_function(id({a,b}), 0),
+ false = is_function(id({a,b}), 234343434333433433),
?line true = is_function(fun() -> ok end, 0),
?line true = is_function(fun(_) -> ok end, 1),
?line false = is_function(fun(_) -> ok end, 0),
diff --git a/erts/emulator/test/gc_SUITE.erl b/erts/emulator/test/gc_SUITE.erl
index 771d2c9a7a..19fa433a53 100644
--- a/erts/emulator/test/gc_SUITE.erl
+++ b/erts/emulator/test/gc_SUITE.erl
@@ -54,17 +54,12 @@ grow_heap(doc) -> ["Produce a growing list of elements, ",
"for X calls, then drop one item per call",
"until the list is empty."];
grow_heap(Config) when is_list(Config) ->
- ?line Dog=test_server:timetrap(test_server:minutes(40)),
- ?line ok=grow_heap1(256),
- case os:type() of
- vxworks ->
- stop_here;
- _ ->
- ?line ok=grow_heap1(512),
- ?line ok=grow_heap1(1024),
- ?line ok=grow_heap1(2048)
- end,
- ?line test_server:timetrap_cancel(Dog),
+ Dog = test_server:timetrap(test_server:minutes(40)),
+ ok = grow_heap1(256),
+ ok = grow_heap1(512),
+ ok = grow_heap1(1024),
+ ok = grow_heap1(2048),
+ test_server:timetrap_cancel(Dog),
ok.
grow_heap1(Len) ->
@@ -82,10 +77,10 @@ grow_heap1(List, MaxLen, CurLen, up) ->
grow_heap1([], _MaxLen, _, down) ->
ok;
grow_heap1([_|List], MaxLen, CurLen, down) ->
- ?line {_,_,C}=erlang:now(),
- ?line Num=C rem (length(List))+1,
- ?line Elem=lists:nth(Num, List),
- ?line NewList=lists:delete(Elem, List),
+ {_,_,C} = erlang:now(),
+ Num = C rem (length(List))+1,
+ Elem = lists:nth(Num, List),
+ NewList = lists:delete(Elem, List),
grow_heap1(NewList, MaxLen, CurLen-1, down).
@@ -93,16 +88,11 @@ grow_heap1([_|List], MaxLen, CurLen, down) ->
grow_stack(doc) -> ["Increase and decrease stack size, and ",
"drop off some garbage from time to time."];
grow_stack(Config) when is_list(Config) ->
- ?line Dog=test_server:timetrap(test_server:minutes(80)),
+ Dog = test_server:timetrap(test_server:minutes(80)),
show_heap("before:"),
- case os:type() of
- vxworks ->
- ?line grow_stack1(25, 0);
- _ ->
- ?line grow_stack1(200, 0)
- end,
+ grow_stack1(200, 0),
show_heap("after:"),
- ?line test_server:timetrap_cancel(Dog),
+ test_server:timetrap_cancel(Dog),
ok.
grow_stack1(0, _) ->
@@ -123,16 +113,11 @@ grow_stack_heap(doc) -> ["While growing the heap, bounces the size ",
"of the stack, and while reducing the heap",
"bounces the stack usage."];
grow_stack_heap(Config) when is_list(Config) ->
- case os:type() of
- vxworks ->
- {comment, "Takes too long to run on VxWorks/cpu32"};
- _ ->
- ?line Dog=test_server:timetrap(test_server:minutes(40)),
- ?line grow_stack_heap1(16),
- ?line grow_stack_heap1(32),
- ?line test_server:timetrap_cancel(Dog),
- ok
- end.
+ Dog = test_server:timetrap(test_server:minutes(40)),
+ grow_stack_heap1(16),
+ grow_stack_heap1(32),
+ test_server:timetrap_cancel(Dog),
+ ok.
grow_stack_heap1(MaxLen) ->
io:format("~ngrow_stack_heap with ~p items.",[MaxLen]),
@@ -151,10 +136,10 @@ grow_stack_heap1(List, MaxLen, CurLen, up) ->
grow_stack_heap1([], _MaxLen, _, down) -> ok;
grow_stack_heap1([_|List], MaxLen, CurLen, down) ->
grow_stack1(CurLen*2,0),
- ?line {_,_,C}=erlang:now(),
- ?line Num=C rem (length(List))+1,
- ?line Elem=lists:nth(Num, List),
- ?line NewList=lists:delete(Elem, List),
+ {_,_,C}=erlang:now(),
+ Num=C rem (length(List))+1,
+ Elem=lists:nth(Num, List),
+ NewList=lists:delete(Elem, List),
grow_stack_heap1(NewList, MaxLen, CurLen-1, down),
ok.
diff --git a/erts/emulator/test/hibernate_SUITE.erl b/erts/emulator/test/hibernate_SUITE.erl
index 68bc3434d4..31938e6c65 100644
--- a/erts/emulator/test/hibernate_SUITE.erl
+++ b/erts/emulator/test/hibernate_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/erts/emulator/test/match_spec_SUITE.erl b/erts/emulator/test/match_spec_SUITE.erl
index 461773114e..d5cb4ee1b7 100644
--- a/erts/emulator/test/match_spec_SUITE.erl
+++ b/erts/emulator/test/match_spec_SUITE.erl
@@ -28,7 +28,7 @@
unary_plus/1, unary_minus/1, moving_labels/1]).
-export([fpe/1]).
-export([otp_9422/1]).
-
+-export([faulty_seq_trace/1, do_faulty_seq_trace/0]).
-export([runner/2, loop_runner/3]).
-export([f1/1, f2/2, f3/2, fn/1, fn/2, fn/3]).
-export([do_boxed_and_small/0]).
@@ -59,6 +59,7 @@ all() ->
ms_trace3, boxed_and_small, destructive_in_test_bif,
guard_exceptions, unary_plus, unary_minus, fpe,
moving_labels,
+ faulty_seq_trace,
otp_9422];
true -> [not_run]
end.
@@ -726,6 +727,19 @@ do_boxed_and_small() ->
{ok, false, _, _} = erlang:match_spec_test({0,3},[{{make_ref(),'_'},[],['$_']}],table),
ok.
+faulty_seq_trace(doc) ->
+ ["Test that faulty seq_trace_call does not crash emulator"];
+faulty_seq_trace(suite) -> [];
+faulty_seq_trace(Config) when is_list(Config) ->
+ ?line {ok, Node} = start_node(match_spec_suite_other),
+ ?line ok = rpc:call(Node,?MODULE,do_faulty_seq_trace,[]),
+ ?line stop_node(Node),
+ ok.
+
+do_faulty_seq_trace() ->
+ {ok,'EXIT',_,_} = erlang:match_spec_test([],[{'_',[],[{message,{set_seq_token,yxa,true}}]}],trace),
+ ok.
+
errchk(Pat) ->
case catch erlang:trace_pattern({?MODULE, f2, 2}, Pat) of
{'EXIT', {badarg, _}} ->
diff --git a/erts/emulator/test/node_container_SUITE.erl b/erts/emulator/test/node_container_SUITE.erl
index 0bf2c03233..3f9b339ed2 100644
--- a/erts/emulator/test/node_container_SUITE.erl
+++ b/erts/emulator/test/node_container_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/erts/emulator/test/nofrag_SUITE.erl b/erts/emulator/test/nofrag_SUITE.erl
index 6b6ac28e2e..71567ed0cb 100644
--- a/erts/emulator/test/nofrag_SUITE.erl
+++ b/erts/emulator/test/nofrag_SUITE.erl
@@ -26,7 +26,6 @@
init_per_testcase/2,end_per_testcase/2,
error_handler/1,error_handler_apply/1,
error_handler_fixed_apply/1,error_handler_fun/1,
- error_handler_tuple_fun/1,
debug_breakpoint/1]).
%% Exported functions for an error_handler module.
@@ -37,7 +36,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[error_handler, error_handler_apply,
error_handler_fixed_apply, error_handler_fun,
- error_handler_tuple_fun, debug_breakpoint].
+ debug_breakpoint].
groups() ->
[].
@@ -178,29 +177,6 @@ collect_fun(N, Fun) ->
undefined_lambda(foobarblurf, Fun, Args) when is_function(Fun) ->
Args.
-error_handler_tuple_fun(Config) when is_list(Config) ->
- ?line process_flag(error_handler, ?MODULE),
- ?line Term = collect_tuple_fun(1024, {?MODULE,very_undefined_function}),
- ?line Term = binary_to_term(term_to_binary(Term)),
- ?line 1024 = length(Term),
- ?line [[{foo,bar},42.0,[e,f,g]]] = lists:usort(Term),
- ok.
-
-collect_tuple_fun(0, _) ->
- [];
-collect_tuple_fun(N, Fun) ->
- %% The next line calls the error handle function, which is
- %% ?MODULE:undefined_function/3 (it simply returns the list
- %% of args).
- C = Fun({foo,id(bar)}, 42.0, [e,f,id(g)]),
-
- %% The variable C will be saved onto the stack frame; if C
- %% points into a heap fragment the garbage collector will reach
- %% it and the emulator will crash sooner or later (sooner if
- %% the emulator is debug-compiled).
- Res = collect_tuple_fun(N-1, Fun),
- [C|Res].
-
debug_breakpoint(Config) when is_list(Config) ->
?line process_flag(error_handler, ?MODULE),
?line erts_debug:breakpoint({?MODULE,foobar,5}, true),
diff --git a/erts/emulator/test/num_bif_SUITE.erl b/erts/emulator/test/num_bif_SUITE.erl
index 4459732257..7a045484cf 100644
--- a/erts/emulator/test/num_bif_SUITE.erl
+++ b/erts/emulator/test/num_bif_SUITE.erl
@@ -25,6 +25,7 @@
%% abs/1
%% float/1
%% float_to_list/1
+%% float_to_list/2
%% integer_to_list/1
%% list_to_float/1
%% list_to_integer/1
@@ -114,14 +115,46 @@ t_float(Config) when is_list(Config) ->
ok.
-%% Tests float_to_list/1.
+%% Tests float_to_list/1, float_to_list/2.
t_float_to_list(Config) when is_list(Config) ->
- ?line test_ftl("0.0e+0", 0.0),
- ?line test_ftl("2.5e+1", 25.0),
- ?line test_ftl("2.5e+0", 2.5),
- ?line test_ftl("2.5e-1", 0.25),
- ?line test_ftl("-3.5e+17", -350.0e15),
+ test_ftl("0.0e+0", 0.0),
+ test_ftl("2.5e+1", 25.0),
+ test_ftl("2.5e+0", 2.5),
+ test_ftl("2.5e-1", 0.25),
+ test_ftl("-3.5e+17", -350.0e15),
+ "1.00000000000000000000e+00" = float_to_list(1.0),
+ "1.00000000000000000000e+00" = float_to_list(1.0, []),
+ "-1.00000000000000000000e+00" = float_to_list(-1.0, []),
+ "-1.00000000000000000000" = float_to_list(-1.0, [{decimals, 20}]),
+ {'EXIT', {badarg, _}} = (catch float_to_list(1.0, [{decimals, -1}])),
+ {'EXIT', {badarg, _}} = (catch float_to_list(1.0, [{decimals, 250}])),
+ {'EXIT', {badarg, _}} = (catch float_to_list(1.0e+300, [{decimals, 1}])),
+ "1.0e+300" = float_to_list(1.0e+300, [{scientific, 1}]),
+ "1.0" = float_to_list(1.0, [{decimals, 249}, compact]),
+ Expected = "1." ++ string:copies("0", 249) ++ "e+00",
+ Expected = float_to_list(1.0, [{scientific, 249}, compact]),
+
+ X1 = float_to_list(1.0),
+ X2 = float_to_list(1.0, [{scientific, 20}]),
+ X1 = X2,
+ "1.000e+00" = float_to_list(1.0, [{scientific, 3}]),
+ "1.000" = float_to_list(1.0, [{decimals, 3}]),
+ "1.0" = float_to_list(1.0, [{decimals, 3}, compact]),
+ "1.12" = float_to_list(1.123, [{decimals, 2}]),
+ "1.123" = float_to_list(1.123, [{decimals, 3}]),
+ "1.123" = float_to_list(1.123, [{decimals, 3}, compact]),
+ "1.1230" = float_to_list(1.123, [{decimals, 4}]),
+ "1.12300" = float_to_list(1.123, [{decimals, 5}]),
+ "1.123" = float_to_list(1.123, [{decimals, 5}, compact]),
+ "1.1234" = float_to_list(1.1234,[{decimals, 6}, compact]),
+ "2.333333" = erlang:float_to_list(7/3, [{decimals, 6}, compact]),
+ "2.333333" = erlang:float_to_list(7/3, [{decimals, 6}]),
+ "0.00000000000000000000e+00" = float_to_list(0.0, [compact]),
+ "0.0" = float_to_list(0.0, [{decimals, 10}, compact]),
+ "123000000000000000000.0" = float_to_list(1.23e20, [{decimals, 10}, compact]),
+ "1.2300000000e+20" = float_to_list(1.23e20, [{scientific, 10}, compact]),
+ "1.23000000000000000000e+20" = float_to_list(1.23e20, []),
ok.
test_ftl(Expect, Float) ->
diff --git a/erts/emulator/test/port_SUITE.erl b/erts/emulator/test/port_SUITE.erl
index 0b99b3438a..13aa0f4c00 100644
--- a/erts/emulator/test/port_SUITE.erl
+++ b/erts/emulator/test/port_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -90,7 +90,7 @@
mix_up_ports/1, otp_5112/1, otp_5119/1, otp_6224/1,
exit_status_multi_scheduling_block/1, ports/1,
spawn_driver/1, spawn_executable/1, close_deaf_port/1,
- unregister_name/1]).
+ unregister_name/1, parallelism_option/1]).
-export([]).
@@ -114,7 +114,8 @@ all() ->
stderr_to_stdout, otp_3906, otp_4389, win_massive,
mix_up_ports, otp_5112, otp_5119,
exit_status_multi_scheduling_block, ports, spawn_driver,
- spawn_executable, close_deaf_port, unregister_name].
+ spawn_executable, close_deaf_port, unregister_name,
+ parallelism_option].
groups() ->
[{stream, [], [stream_small, stream_big]},
@@ -157,16 +158,16 @@ win_massive(Config) when is_list(Config) ->
end.
do_win_massive() ->
- ?line Dog = test_server:timetrap(test_server:seconds(360)),
- ?line SuiteDir = filename:dirname(code:which(?MODULE)),
- ?line Env = " -env ERL_MAX_PORTS 8192",
- ?line {ok, Node} =
+ Dog = test_server:timetrap(test_server:seconds(360)),
+ SuiteDir = filename:dirname(code:which(?MODULE)),
+ Ports = " +Q 8192",
+ {ok, Node} =
test_server:start_node(win_massive,
slave,
- [{args, " -pa " ++ SuiteDir ++ Env}]),
- ?line ok = rpc:call(Node,?MODULE,win_massive_client,[3000]),
- ?line test_server:stop_node(Node),
- ?line test_server:timetrap_cancel(Dog),
+ [{args, " -pa " ++ SuiteDir ++ Ports}]),
+ ok = rpc:call(Node,?MODULE,win_massive_client,[3000]),
+ test_server:stop_node(Node),
+ test_server:timetrap_cancel(Dog),
ok.
win_massive_client(N) ->
@@ -208,11 +209,11 @@ win_massive_loop(P,N) ->
%% We will send only a small amount of data, to avoid deadlock.
stream_small(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(10)),
- ?line stream_ping(Config, 512, "", []),
- ?line stream_ping(Config, 1777, "", []),
- ?line stream_ping(Config, 1777, "-s512", []),
- ?line test_server:timetrap_cancel(Dog),
+ Dog = test_server:timetrap(test_server:seconds(10)),
+ stream_ping(Config, 512, "", []),
+ stream_ping(Config, 1777, "", []),
+ stream_ping(Config, 1777, "-s512", []),
+ test_server:timetrap_cancel(Dog),
ok.
%% Send big amounts of data (much bigger than the buffer size in port test).
@@ -220,30 +221,22 @@ stream_small(Config) when is_list(Config) ->
%% non-blocking reads and writes.
stream_big(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(180)),
- case os:type() of
- vxworks ->
- %% Don't stress VxWorks too much
- ?line stream_ping(Config, 43755, "", []),
- ?line stream_ping(Config, 51255, "", []),
- ?line stream_ping(Config, 52345, " -s40000", []);
- _ ->
- ?line stream_ping(Config, 43755, "", []),
- ?line stream_ping(Config, 100000, "", []),
- ?line stream_ping(Config, 77777, " -s40000", [])
- end,
- ?line test_server:timetrap_cancel(Dog),
+ Dog = test_server:timetrap(test_server:seconds(180)),
+ stream_ping(Config, 43755, "", []),
+ stream_ping(Config, 100000, "", []),
+ stream_ping(Config, 77777, " -s40000", []),
+ test_server:timetrap_cancel(Dog),
ok.
%% Sends packet with header size of 1, 2, and 4, with packets of various
%% sizes.
basic_ping(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(120)),
- ?line ping(Config, sizes(1), 1, "", []),
- ?line ping(Config, sizes(2), 2, "", []),
- ?line ping(Config, sizes(4), 4, "", []),
- ?line test_server:timetrap_cancel(Dog),
+ Dog = test_server:timetrap(test_server:seconds(120)),
+ ping(Config, sizes(1), 1, "", []),
+ ping(Config, sizes(2), 2, "", []),
+ ping(Config, sizes(4), 4, "", []),
+ test_server:timetrap_cancel(Dog),
ok.
%% Let the port program insert delays between characters sent back to
@@ -251,30 +244,29 @@ basic_ping(Config) when is_list(Config) ->
%% small chunks rather than all at once.
slow_writes(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(20)),
- ?line ping(Config, [8], 4, "-s1", []),
- ?line ping(Config, [10], 2, "-s2", []),
- ?line test_server:timetrap_cancel(Dog),
+ Dog = test_server:timetrap(test_server:seconds(20)),
+ ping(Config, [8], 4, "-s1", []),
+ ping(Config, [10], 2, "-s2", []),
+ test_server:timetrap_cancel(Dog),
ok.
bad_packet(doc) ->
["Test that we get {'EXIT', Port, einval} if we try to send a bigger "
"packet than the packet header allows."];
bad_packet(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(10)),
- ?line PortTest = port_test(Config),
- ?line process_flag(trap_exit, true),
+ Dog = test_server:timetrap(test_server:seconds(10)),
+ PortTest = port_test(Config),
+ process_flag(trap_exit, true),
- ?line bad_packet(PortTest, 1, 256),
- ?line bad_packet(PortTest, 1, 257),
- ?line bad_packet(PortTest, 2, 65536),
- ?line bad_packet(PortTest, 2, 65537),
+ bad_packet(PortTest, 1, 256),
+ bad_packet(PortTest, 1, 257),
+ bad_packet(PortTest, 2, 65536),
+ bad_packet(PortTest, 2, 65537),
- ?line test_server:timetrap_cancel(Dog),
+ test_server:timetrap_cancel(Dog),
ok.
bad_packet(PortTest, HeaderSize, PacketSize) ->
- %% Intentionally no ?line macros.
P = open_port({spawn, PortTest}, [{packet, HeaderSize}]),
P ! {self(), {command, make_zero_packet(PacketSize)}},
receive
@@ -292,16 +284,16 @@ make_zero_packet(N) ->
%% Test sending bad messages to a port.
bad_port_messages(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(10)),
- ?line PortTest = port_test(Config),
- ?line process_flag(trap_exit, true),
+ Dog = test_server:timetrap(test_server:seconds(10)),
+ PortTest = port_test(Config),
+ process_flag(trap_exit, true),
- ?line bad_message(PortTest, {a,b}),
- ?line bad_message(PortTest, {a}),
- ?line bad_message(PortTest, {self(),{command,bad_command}}),
- ?line bad_message(PortTest, {self(),{connect,no_pid}}),
+ bad_message(PortTest, {a,b}),
+ bad_message(PortTest, {a}),
+ bad_message(PortTest, {self(),{command,bad_command}}),
+ bad_message(PortTest, {self(),{connect,no_pid}}),
- ?line test_server:timetrap_cancel(Dog),
+ test_server:timetrap_cancel(Dog),
ok.
bad_message(PortTest, Message) ->
@@ -319,108 +311,97 @@ bad_message(PortTest, Message) ->
%% Tests the 'binary' option for a port.
t_binary(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(300)),
+ Dog = test_server:timetrap(test_server:seconds(300)),
%% Packet mode.
- ?line ping(Config, sizes(1), 1, "", [binary]),
- ?line ping(Config, sizes(2), 2, "", [binary]),
- ?line ping(Config, sizes(4), 4, "", [binary]),
+ ping(Config, sizes(1), 1, "", [binary]),
+ ping(Config, sizes(2), 2, "", [binary]),
+ ping(Config, sizes(4), 4, "", [binary]),
%% Stream mode.
- case os:type() of
- vxworks ->
- %% don't stress VxWorks too much
- ?line stream_ping(Config, 435, "", [binary]),
- ?line stream_ping(Config, 43755, "", [binary]),
- ?line stream_ping(Config, 50000, "", [binary]);
- _ ->
- ?line stream_ping(Config, 435, "", [binary]),
- ?line stream_ping(Config, 43755, "", [binary]),
- ?line stream_ping(Config, 100000, "", [binary])
- end,
+ stream_ping(Config, 435, "", [binary]),
+ stream_ping(Config, 43755, "", [binary]),
+ stream_ping(Config, 100000, "", [binary]),
- ?line test_server:timetrap_cancel(Dog),
+ test_server:timetrap_cancel(Dog),
ok.
name1(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(100)),
- ?line PortTest = port_test(Config),
- ?line Command = lists:concat([PortTest, " "]),
- ?line P = open_port({spawn, Command}, []),
- ?line register(myport, P),
- ?line P = whereis(myport),
+ Dog = test_server:timetrap(test_server:seconds(100)),
+ PortTest = port_test(Config),
+ Command = lists:concat([PortTest, " "]),
+ P = open_port({spawn, Command}, []),
+ register(myport, P),
+ P = whereis(myport),
Text = "hej",
- ?line myport ! {self(), {command, Text}},
- ?line receive
- {P, {data, Text}} ->
- ok
- end,
- ?line myport ! {self(), close},
- ?line receive
- {P, closed} -> ok
- end,
- ?line undefined = whereis(myport),
- ?line test_server:timetrap_cancel(Dog),
+ myport ! {self(), {command, Text}},
+ receive
+ {P, {data, Text}} ->
+ ok
+ end,
+ myport ! {self(), close},
+ receive
+ {P, closed} -> ok
+ end,
+ undefined = whereis(myport),
+ test_server:timetrap_cancel(Dog),
ok.
%% Test that the 'eof' option works.
eof(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(100)),
- ?line PortTest = port_test(Config),
- ?line Command = lists:concat([PortTest, " -h0 -q"]),
- ?line P = open_port({spawn, Command}, [eof]),
- ?line receive
- {P, eof} ->
- ok
- end,
- ?line P ! {self(), close},
- ?line receive
- {P, closed} -> ok
- end,
- ?line test_server:timetrap_cancel(Dog),
+ Dog = test_server:timetrap(test_server:seconds(100)),
+ PortTest = port_test(Config),
+ Command = lists:concat([PortTest, " -h0 -q"]),
+ P = open_port({spawn, Command}, [eof]),
+ receive
+ {P, eof} ->
+ ok
+ end,
+ P ! {self(), close},
+ receive
+ {P, closed} -> ok
+ end,
+ test_server:timetrap_cancel(Dog),
ok.
%% Tests that the 'in' option for a port works.
input_only(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(300)),
- ?line expect_input(Config, [0, 1, 10, 13, 127, 128, 255], 1, "", [in]),
- ?line expect_input(Config, [0, 1, 255, 2048], 2, "", [in]),
- ?line expect_input(Config, [0, 1, 255, 2048], 4, "", [in]),
- ?line expect_input(Config, [0, 1, 10, 13, 127, 128, 255],
- 1, "", [in, binary]),
- ?line test_server:timetrap_cancel(Dog),
+ Dog = test_server:timetrap(test_server:seconds(300)),
+ expect_input(Config, [0, 1, 10, 13, 127, 128, 255], 1, "", [in]),
+ expect_input(Config, [0, 1, 255, 2048], 2, "", [in]),
+ expect_input(Config, [0, 1, 255, 2048], 4, "", [in]),
+ expect_input(Config, [0, 1, 10, 13, 127, 128, 255],
+ 1, "", [in, binary]),
+ test_server:timetrap_cancel(Dog),
ok.
%% Tests that the 'out' option for a port works.
output_only(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(100)),
- ?line Dir = ?config(priv_dir, Config),
- ?line Filename = filename:join(Dir, "output_only_stream"),
- ?line output_and_verify(Config, Filename, "-h0",
- random_packet(35777, "echo")),
- ?line test_server:timetrap_cancel(Dog),
+ Dog = test_server:timetrap(test_server:seconds(100)),
+ Dir = ?config(priv_dir, Config),
+ Filename = filename:join(Dir, "output_only_stream"),
+ output_and_verify(Config, Filename, "-h0",
+ random_packet(35777, "echo")),
+ test_server:timetrap_cancel(Dog),
ok.
output_and_verify(Config, Filename, Options, Data) ->
- ?line PortTest = port_test(Config),
- ?line Command = lists:concat([PortTest, " ",
- Options, " -o", Filename]),
- ?line Port = open_port({spawn, Command}, [out]),
- ?line Port ! {self(), {command, Data}},
- ?line Port ! {self(), close},
- ?line receive
- {Port, closed} -> ok
- end,
- Wait_time = case os:type() of
- vxworks -> 5000;
- _ -> 500
- end,
- ?line test_server:sleep(Wait_time),
- ?line {ok, Written} = file:read_file(Filename),
- ?line Data = binary_to_list(Written),
+ PortTest = port_test(Config),
+ Command = lists:concat([PortTest, " ",
+ Options, " -o", Filename]),
+ Port = open_port({spawn, Command}, [out]),
+ Port ! {self(), {command, Data}},
+ Port ! {self(), close},
+ receive
+ {Port, closed} -> ok
+ end,
+ Wait_time = 500,
+ test_server:sleep(Wait_time),
+ {ok, Written} = file:read_file(Filename),
+ Data = binary_to_list(Written),
ok.
%% Test that receiving several packages written in the same
@@ -430,19 +411,11 @@ output_and_verify(Config, Filename, Options, Data) ->
%% Basic test of receiving multiple packages, written in
%% one operation by the other end.
mul_basic(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(600)),
- case os:type() of
- vxworks ->
- %% don't stress vxworks too much
- ?line expect_input(Config, [0, 1, 255, 10, 13], 1, "", []),
- ?line expect_input(Config, [0, 10, 13, 1600, 8191, 16383], 2, "", []),
- ?line expect_input(Config, [10, 35000], 4, "", []);
- _ ->
- ?line expect_input(Config, [0, 1, 255, 10, 13], 1, "", []),
- ?line expect_input(Config, [0, 10, 13, 1600, 32767, 65535], 2, "", []),
- ?line expect_input(Config, [10, 70000], 4, "", [])
- end,
- ?line test_server:timetrap_cancel(Dog),
+ Dog = test_server:timetrap(test_server:seconds(600)),
+ expect_input(Config, [0, 1, 255, 10, 13], 1, "", []),
+ expect_input(Config, [0, 10, 13, 1600, 32767, 65535], 2, "", []),
+ expect_input(Config, [10, 70000], 4, "", []),
+ test_server:timetrap_cancel(Dog),
ok.
%% Test reading a buffer consisting of several packets, some
@@ -451,9 +424,9 @@ mul_basic(Config) when is_list(Config) ->
%% delays in between.)
mul_slow_writes(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(250)),
- ?line expect_input(Config, [0, 20, 255, 10, 1], 1, "-s64", []),
- ?line test_server:timetrap_cancel(Dog),
+ Dog = test_server:timetrap(test_server:seconds(250)),
+ expect_input(Config, [0, 20, 255, 10, 1], 1, "-s64", []),
+ test_server:timetrap_cancel(Dog),
ok.
%% Runs several port tests in parallell. Each individual test
@@ -461,27 +434,27 @@ mul_slow_writes(Config) when is_list(Config) ->
%% should also finish in about 5 seconds.
parallell(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(300)),
- ?line Testers =
- [fun() -> stream_ping(Config, 1007, "-s100", []) end,
- fun() -> stream_ping(Config, 10007, "-s1000", []) end,
- fun() -> stream_ping(Config, 10007, "-s1000", []) end,
-
- fun() -> expect_input(Config, [21, 22, 23, 24, 25], 1,
- "-s10", [in]) end,
-
- fun() -> ping(Config, [10], 1, "-d", []) end,
- fun() -> ping(Config, [20000], 2, "-d", []) end,
- fun() -> ping(Config, [101], 1, "-s10", []) end,
- fun() -> ping(Config, [1001], 2, "-s100", []) end,
- fun() -> ping(Config, [10001], 4, "-s1000", []) end,
-
- fun() -> ping(Config, [501, 501], 2, "-s100", []) end,
- fun() -> ping(Config, [11, 12, 13, 14, 11], 1, "-s5", []) end],
- ?line process_flag(trap_exit, true),
- ?line Pids = lists:map(fun fun_spawn/1, Testers),
- ?line wait_for(Pids),
- ?line test_server:timetrap_cancel(Dog),
+ Dog = test_server:timetrap(test_server:seconds(300)),
+ Testers = [
+ fun() -> stream_ping(Config, 1007, "-s100", []) end,
+ fun() -> stream_ping(Config, 10007, "-s1000", []) end,
+ fun() -> stream_ping(Config, 10007, "-s1000", []) end,
+
+ fun() -> expect_input(Config, [21, 22, 23, 24, 25], 1,
+ "-s10", [in]) end,
+
+ fun() -> ping(Config, [10], 1, "-d", []) end,
+ fun() -> ping(Config, [20000], 2, "-d", []) end,
+ fun() -> ping(Config, [101], 1, "-s10", []) end,
+ fun() -> ping(Config, [1001], 2, "-s100", []) end,
+ fun() -> ping(Config, [10001], 4, "-s1000", []) end,
+
+ fun() -> ping(Config, [501, 501], 2, "-s100", []) end,
+ fun() -> ping(Config, [11, 12, 13, 14, 11], 1, "-s5", []) end],
+ process_flag(trap_exit, true),
+ Pids = lists:map(fun fun_spawn/1, Testers),
+ wait_for(Pids),
+ test_server:timetrap_cancel(Dog),
ok.
wait_for([]) ->
@@ -500,29 +473,29 @@ wait_for(Pids) ->
dying_port(suite) -> [];
dying_port(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(150)),
- ?line process_flag(trap_exit, true),
+ Dog = test_server:timetrap(test_server:seconds(150)),
+ process_flag(trap_exit, true),
- ?line P1 = make_dying_port(Config),
- ?line P2 = make_dying_port(Config),
- ?line P3 = make_dying_port(Config),
- ?line P4 = make_dying_port(Config),
- ?line P5 = make_dying_port(Config),
+ P1 = make_dying_port(Config),
+ P2 = make_dying_port(Config),
+ P3 = make_dying_port(Config),
+ P4 = make_dying_port(Config),
+ P5 = make_dying_port(Config),
%% This should be big enough to be sure to block in the write.
- ?line Garbage = random_packet(16384),
+ Garbage = random_packet(16384),
- ?line P1 ! {self(), {command, Garbage}},
- ?line P3 ! {self(), {command, Garbage}},
- ?line P5 ! {self(), {command, Garbage}},
+ P1 ! {self(), {command, Garbage}},
+ P3 ! {self(), {command, Garbage}},
+ P5 ! {self(), {command, Garbage}},
- ?line wait_for_port_exit(P1),
- ?line wait_for_port_exit(P2),
- ?line wait_for_port_exit(P3),
- ?line wait_for_port_exit(P4),
- ?line wait_for_port_exit(P5),
+ wait_for_port_exit(P1),
+ wait_for_port_exit(P2),
+ wait_for_port_exit(P3),
+ wait_for_port_exit(P4),
+ wait_for_port_exit(P5),
- ?line test_server:timetrap_cancel(Dog),
+ test_server:timetrap_cancel(Dog),
ok.
wait_for_port_exit(Port) ->
@@ -549,9 +522,9 @@ make_dying_port(Config) when is_list(Config) ->
port_program_with_path(suite) -> [];
port_program_with_path(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(100)),
- ?line DataDir = ?config(data_dir, Config),
- ?line PrivDir = ?config(priv_dir, Config),
+ Dog = test_server:timetrap(test_server:seconds(100)),
+ DataDir = ?config(data_dir, Config),
+ PrivDir = ?config(priv_dir, Config),
%% Create a copy of the port test program in a directory not
%% included in PATH (i.e. in priv_dir), with the name 'my_port_test.exe'.
@@ -560,36 +533,31 @@ port_program_with_path(Config) when is_list(Config) ->
%% (On Unix, there will be a single file created, which will be
%% a copy of the port program.)
- ?line PortTest = os:find_executable("port_test", DataDir),
+ PortTest = os:find_executable("port_test", DataDir),
io:format("os:find_executable(~p, ~p) returned ~p",
["port_test", DataDir, PortTest]),
- ?line {ok, PortTestPgm} = file:read_file(PortTest),
- ?line NewName = filename:join(PrivDir, filename:basename(PortTest)),
- ?line RedHerring = filename:rootname(NewName),
- ?line ok = file:write_file(RedHerring, "I'm just here to confuse.\n"),
- ?line ok = file:write_file(NewName, PortTestPgm),
- ?line ok = file:write_file_info(NewName, #file_info{mode=8#111}),
- ?line PgmWithPathAndNoExt = filename:rootname(NewName),
+ {ok, PortTestPgm} = file:read_file(PortTest),
+ NewName = filename:join(PrivDir, filename:basename(PortTest)),
+ RedHerring = filename:rootname(NewName),
+ ok = file:write_file(RedHerring, "I'm just here to confuse.\n"),
+ ok = file:write_file(NewName, PortTestPgm),
+ ok = file:write_file_info(NewName, #file_info{mode=8#111}),
+ PgmWithPathAndNoExt = filename:rootname(NewName),
%% Open the port using the path to the copied port test program,
%% but without the .exe extension, and verified that it was started.
%%
%% If the bug is present the open_port call will fail with badarg.
- ?line Command = lists:concat([PgmWithPathAndNoExt, " -h2"]),
- %% allow VxWorks time to write file
- case os:type() of
- vxworks -> test_server:sleep(2500);
- _ -> time
+ Command = lists:concat([PgmWithPathAndNoExt, " -h2"]),
+ P = open_port({spawn, Command}, [{packet, 2}]),
+ Message = "echo back to me",
+ P ! {self(), {command, Message}},
+ receive
+ {P, {data, Message}} ->
+ ok
end,
- ?line P = open_port({spawn, Command}, [{packet, 2}]),
- ?line Message = "echo back to me",
- ?line P ! {self(), {command, Message}},
- ?line receive
- {P, {data, Message}} ->
- ok
- end,
- ?line test_server:timetrap_cancel(Dog),
+ test_server:timetrap_cancel(Dog),
ok.
@@ -597,56 +565,46 @@ port_program_with_path(Config) when is_list(Config) ->
%% This used to fail on Windows.
open_input_file_port(suite) -> [];
open_input_file_port(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(10)),
- ?line PrivDir = ?config(priv_dir, Config),
+ Dog = test_server:timetrap(test_server:seconds(10)),
+ PrivDir = ?config(priv_dir, Config),
%% Create a file with the file driver and read it back using
%% open_port/2.
- ?line MyFile1 = filename:join(PrivDir, "my_input_file"),
- ?line FileData1 = "An input file",
- ?line ok = file:write_file(MyFile1, FileData1),
- case os:type() of
- vxworks ->
- %% Can't open input file with vanilla driver on VxWorks
- ?line process_flag(trap_exit, true),
- ?line case catch open_port(MyFile1, [in]) of
- {'EXIT', {badarg, _}} ->
- ok
- end;
- _ ->
- ?line case open_port(MyFile1, [in]) of
- InputPort when is_port(InputPort) ->
- ?line receive
- {InputPort, {data, FileData1}} ->
- ok
- end
- end
+ MyFile1 = filename:join(PrivDir, "my_input_file"),
+ FileData1 = "An input file",
+ ok = file:write_file(MyFile1, FileData1),
+ case open_port(MyFile1, [in]) of
+ InputPort when is_port(InputPort) ->
+ receive
+ {InputPort, {data, FileData1}} ->
+ ok
+ end
end,
- ?line test_server:timetrap_cancel(Dog),
+ test_server:timetrap_cancel(Dog),
ok.
%% Tests that files can be written using open_port(Filename, [out]).
open_output_file_port(suite) -> [];
open_output_file_port(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(100)),
- ?line PrivDir = ?config(priv_dir, Config),
+ Dog = test_server:timetrap(test_server:seconds(100)),
+ PrivDir = ?config(priv_dir, Config),
%% Create a file with open_port/2 and read it back with
%% the file driver.
- ?line MyFile2 = filename:join(PrivDir, "my_output_file"),
- ?line FileData2_0 = "A file created ",
- ?line FileData2_1 = "with open_port/2.\n",
- ?line FileData2 = FileData2_0 ++ FileData2_1,
- ?line OutputPort = open_port(MyFile2, [out]),
- ?line OutputPort ! {self(), {command, FileData2_0}},
- ?line OutputPort ! {self(), {command, FileData2_1}},
- ?line OutputPort ! {self(), close},
- ?line {ok, Bin} = file:read_file(MyFile2),
- ?line FileData2 = binary_to_list(Bin),
-
- ?line test_server:timetrap_cancel(Dog),
+ MyFile2 = filename:join(PrivDir, "my_output_file"),
+ FileData2_0 = "A file created ",
+ FileData2_1 = "with open_port/2.\n",
+ FileData2 = FileData2_0 ++ FileData2_1,
+ OutputPort = open_port(MyFile2, [out]),
+ OutputPort ! {self(), {command, FileData2_0}},
+ OutputPort ! {self(), {command, FileData2_1}},
+ OutputPort ! {self(), close},
+ {ok, Bin} = file:read_file(MyFile2),
+ FileData2 = binary_to_list(Bin),
+
+ test_server:timetrap_cancel(Dog),
ok.
%%
@@ -670,17 +628,17 @@ iter_max_ports(Config) when is_list(Config) ->
iter_max_ports_test(Config) ->
- ?line Dog = test_server:timetrap(test_server:minutes(20)),
- ?line PortTest = port_test(Config),
- ?line Command = lists:concat([PortTest, " -h0 -q"]),
- ?line Iters = case os:type() of
+ Dog = test_server:timetrap(test_server:minutes(20)),
+ PortTest = port_test(Config),
+ Command = lists:concat([PortTest, " -h0 -q"]),
+ Iters = case os:type() of
{win32,_} -> 4;
_ -> 10
end,
- ?line L = do_iter_max_ports(Iters, Command),
+ L = do_iter_max_ports(Iters, Command),
io:format("Result: ~p",[L]),
- ?line all_equal(L),
- ?line test_server:timetrap_cancel(Dog),
+ all_equal(L),
+ test_server:timetrap_cancel(Dog),
{comment, "Max ports: " ++ integer_to_list(hd(L))}.
do_iter_max_ports(N, Command) when N > 0 ->
@@ -695,9 +653,9 @@ all_equal([]) -> ok.
max_ports(Command) ->
test_server:sleep(500),
- ?line Ps = open_ports({spawn, Command}, [eof]),
- ?line N = length(Ps),
- ?line close_ports(Ps),
+ Ps = open_ports({spawn, Command}, [eof]),
+ N = length(Ps),
+ close_ports(Ps),
io:format("Got ~p ports\n",[N]),
N.
@@ -727,105 +685,105 @@ open_ports(Name, Settings) ->
enomem ->
[];
Other ->
- ?line test_server:fail({open_ports, Other})
+ test_server:fail({open_ports, Other})
end;
Other ->
- ?line test_server:fail({open_ports, Other})
+ test_server:fail({open_ports, Other})
end.
%% Tests that exit(Port, Term) works (has been known to crash the emulator).
t_exit(suite) -> [];
t_exit(Config) when is_list(Config) ->
- ?line process_flag(trap_exit, true),
- ?line Pid = fun_spawn(fun suicide_port/1, [Config]),
- ?line receive
- {'EXIT', Pid, die} ->
- ok;
- Other ->
- test_server:fail({bad_message, Other})
- end.
+ process_flag(trap_exit, true),
+ Pid = fun_spawn(fun suicide_port/1, [Config]),
+ receive
+ {'EXIT', Pid, die} ->
+ ok;
+ Other ->
+ test_server:fail({bad_message, Other})
+ end.
suicide_port(Config) when is_list(Config) ->
- ?line Port = port_expect(Config, [], 0, "", []),
- ?line exit(Port, die),
- ?line receive after infinity -> ok end.
+ Port = port_expect(Config, [], 0, "", []),
+ exit(Port, die),
+ receive after infinity -> ok end.
tps_16_bytes(doc) -> "";
tps_16_bytes(suite) -> [];
tps_16_bytes(Config) when is_list(Config) ->
- ?line tps(16, Config).
+ tps(16, Config).
tps_1K(doc) -> "";
tps_1K(suite) -> [];
tps_1K(Config) when is_list(Config) ->
- ?line tps(1024, Config).
+ tps(1024, Config).
tps(Size, Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(300)),
- ?line PortTest = port_test(Config),
- ?line Packet = list_to_binary(random_packet(Size, "e")),
- ?line Port = open_port({spawn, PortTest}, [binary, {packet, 2}]),
- ?line Transactions = 10000,
- ?line {Elapsed, ok} = test_server:timecall(?MODULE, tps,
+ Dog = test_server:timetrap(test_server:seconds(300)),
+ PortTest = port_test(Config),
+ Packet = list_to_binary(random_packet(Size, "e")),
+ Port = open_port({spawn, PortTest}, [binary, {packet, 2}]),
+ Transactions = 10000,
+ {Elapsed, ok} = test_server:timecall(?MODULE, tps,
[Port, Packet, Transactions]),
- ?line test_server:timetrap_cancel(Dog),
+ test_server:timetrap_cancel(Dog),
{comment, integer_to_list(trunc(Transactions/Elapsed+0.5)) ++ " transactions/s"}.
tps(_Port, _Packet, 0) -> ok;
tps(Port, Packet, N) ->
- ?line port_command(Port, Packet),
- ?line receive
- {Port, {data, Packet}} ->
- ?line tps(Port, Packet, N-1);
- Other ->
- ?line test_server:fail({bad_message, Other})
- end.
+ port_command(Port, Packet),
+ receive
+ {Port, {data, Packet}} ->
+ tps(Port, Packet, N-1);
+ Other ->
+ test_server:fail({bad_message, Other})
+ end.
%% Line I/O test
line(Config) when is_list(Config) ->
- ?line Siz = 110,
- ?line Dog = test_server:timetrap(test_server:seconds(300)),
- ?line Packet1 = random_packet(Siz),
- ?line Packet2 = random_packet(Siz div 2),
+ Siz = 110,
+ Dog = test_server:timetrap(test_server:seconds(300)),
+ Packet1 = random_packet(Siz),
+ Packet2 = random_packet(Siz div 2),
%% Test that packets are split into lines
- ?line port_expect(Config,[{lists:append([Packet1, io_lib:nl(), Packet2,
+ port_expect(Config,[{lists:append([Packet1, io_lib:nl(), Packet2,
io_lib:nl()]),
[{eol, Packet1}, {eol, Packet2}]}],
0, "", [{line,Siz}]),
%% Test the same for binaries
- ?line port_expect(Config,[{lists:append([Packet1, io_lib:nl(), Packet2,
+ port_expect(Config,[{lists:append([Packet1, io_lib:nl(), Packet2,
io_lib:nl()]),
[{eol, Packet1}, {eol, Packet2}]}],
0, "", [{line,Siz},binary]),
%% Test that too long lines get split
- ?line port_expect(Config,[{lists:append([Packet1, io_lib:nl(), Packet1,
+ port_expect(Config,[{lists:append([Packet1, io_lib:nl(), Packet1,
Packet2, io_lib:nl()]),
[{eol, Packet1}, {noeol, Packet1},
{eol, Packet2}]}], 0, "", [{line,Siz}]),
%% Test that last output from closing port program gets received.
- ?line L1 = lists:append([Packet1, io_lib:nl(), Packet2]),
- ?line S1 = lists:flatten(io_lib:format("-l~w", [length(L1)])),
+ L1 = lists:append([Packet1, io_lib:nl(), Packet2]),
+ S1 = lists:flatten(io_lib:format("-l~w", [length(L1)])),
io:format("S1 = ~w, L1 = ~w~n", [S1,L1]),
- ?line port_expect(Config,[{L1,
+ port_expect(Config,[{L1,
[{eol, Packet1}, {noeol, Packet2}, eof]}], 0,
S1, [{line,Siz},eof]),
%% Test that lonely <CR> Don't get treated as newlines
- ?line port_expect(Config,[{lists:append([Packet1, [13], Packet2,
+ port_expect(Config,[{lists:append([Packet1, [13], Packet2,
io_lib:nl()]),
[{noeol, Packet1}, {eol, [13 |Packet2]}]}],
0, "", [{line,Siz}]),
%% Test that packets get built up to lines (delayed output from
%% port program)
- ?line port_expect(Config,[{Packet2,[]},
+ port_expect(Config,[{Packet2,[]},
{lists:append([Packet2, io_lib:nl(),
Packet1, io_lib:nl()]),
[{eol, lists:append(Packet2, Packet2)},
{eol, Packet1}]}], 0, "-d", [{line,Siz}]),
%% Test that we get badarg if trying both packet and line
- ?line bad_argument(Config, [{packet, 5}, {line, 5}]),
- ?line test_server:timetrap_cancel(Dog),
+ bad_argument(Config, [{packet, 5}, {line, 5}]),
+ test_server:timetrap_cancel(Dog),
ok.
%%% Redirection of stderr test
@@ -834,15 +792,15 @@ stderr_to_stdout(suite) ->
stderr_to_stdout(doc) ->
"Test that redirection of standard error to standard output works.";
stderr_to_stdout(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(60)),
+ Dog = test_server:timetrap(test_server:seconds(60)),
%% See that it works
- ?line Packet = random_packet(10),
- ?line port_expect(Config,[{Packet,[Packet]}], 0, "-e -l10",
+ Packet = random_packet(10),
+ port_expect(Config,[{Packet,[Packet]}], 0, "-e -l10",
[stderr_to_stdout]),
- %% ?line stream_ping(Config, 10, "-e", [stderr_to_stdout]),
+ %% stream_ping(Config, 10, "-e", [stderr_to_stdout]),
%% See that it doesn't always happen (will generate garbage on stderr)
- ?line port_expect(Config,[{Packet,[eof]}], 0, "-e -l10", [line,eof]),
- ?line test_server:timetrap_cancel(Dog),
+ port_expect(Config,[{Packet,[eof]}], 0, "-e -l10", [line,eof]),
+ test_server:timetrap_cancel(Dog),
ok.
@@ -862,47 +820,39 @@ env(suite) ->
env(doc) ->
["Test that the 'env' option works"];
env(Config) when is_list(Config) ->
- case os:type() of
- vxworks ->
- {skipped,"Environments not implemented on VxWorks (could be...)"};
- _ ->
- env2(Config)
- end.
-
-env2(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(60)),
- ?line Priv = ?config(priv_dir, Config),
- ?line Temp = filename:join(Priv, "env_fun.bin"),
+ Dog = test_server:timetrap(test_server:seconds(60)),
+ Priv = ?config(priv_dir, Config),
+ Temp = filename:join(Priv, "env_fun.bin"),
PluppVal = "dirty monkey",
- ?line env_slave(Temp, [{"plupp",PluppVal}]),
+ env_slave(Temp, [{"plupp",PluppVal}]),
Long = "LongAndBoringEnvName",
- ?line os:putenv(Long, "nisse"),
-
- ?line env_slave(Temp, [{"plupp",PluppVal},
- {"DIR_PLUPP","###glurfrik"}],
- fun() ->
- PluppVal = os:getenv("plupp"),
- "###glurfrik" = os:getenv("DIR_PLUPP"),
- "nisse" = os:getenv(Long)
- end),
+ os:putenv(Long, "nisse"),
-
- ?line env_slave(Temp, [{"must_define_something","some_value"},
- {"certainly_not_existing",false},
- {"ends_with_equal", "value="},
- {Long,false},
- {"glurf","a glorfy string"}]),
+ env_slave(Temp, [{"plupp",PluppVal},
+ {"DIR_PLUPP","###glurfrik"}],
+ fun() ->
+ PluppVal = os:getenv("plupp"),
+ "###glurfrik" = os:getenv("DIR_PLUPP"),
+ "nisse" = os:getenv(Long)
+ end),
+
+
+ env_slave(Temp, [{"must_define_something","some_value"},
+ {"certainly_not_existing",false},
+ {"ends_with_equal", "value="},
+ {Long,false},
+ {"glurf","a glorfy string"}]),
%% A lot of non existing variables (mingled with existing)
NotExistingList = [{lists:flatten(io_lib:format("V~p_not_existing",[X])),false}
|| X <- lists:seq(1,150)],
ExistingList = [{lists:flatten(io_lib:format("V~p_existing",[X])),"a_value"}
|| X <- lists:seq(1,150)],
- ?line env_slave(Temp, lists:sort(ExistingList ++ NotExistingList)),
+ env_slave(Temp, lists:sort(ExistingList ++ NotExistingList)),
- ?line test_server:timetrap_cancel(Dog),
+ test_server:timetrap_cancel(Dog),
ok.
env_slave(File, Env) ->
@@ -946,23 +896,15 @@ env_slave_main([File]) ->
%% 'env' option
%% Test bad environments.
bad_env(Config) when is_list(Config) ->
- case os:type() of
- vxworks ->
- {skipped,"Environments not implemented on VxWorks"};
- _ ->
- bad_env_1()
- end.
-
-bad_env_1() ->
- ?line try_bad_env([abbb]),
- ?line try_bad_env([{"key","value"}|{"another","value"}]),
- ?line try_bad_env([{"key","value","value2"}]),
- ?line try_bad_env([{"key",[a,b,c]}]),
- ?line try_bad_env([{"key",value}]),
- ?line try_bad_env({a,tuple}),
- ?line try_bad_env(42),
- ?line try_bad_env([a|b]),
- ?line try_bad_env(self()),
+ try_bad_env([abbb]),
+ try_bad_env([{"key","value"}|{"another","value"}]),
+ try_bad_env([{"key","value","value2"}]),
+ try_bad_env([{"key",[a,b,c]}]),
+ try_bad_env([{"key",value}]),
+ try_bad_env({a,tuple}),
+ try_bad_env(42),
+ try_bad_env([a|b]),
+ try_bad_env(self()),
ok.
try_bad_env(Env) ->
@@ -979,36 +921,29 @@ cd(suite) ->
cd(doc) ->
["Test that the 'cd' option works"];
cd(Config) when is_list(Config) ->
- case os:type() of
- vxworks ->
- {skipped,"Task specific directories does not exist on VxWorks"};
- _ ->
- cd2(Config)
- end.
-cd2(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(60)),
-
- ?line Program = atom_to_list(lib:progname()),
- ?line DataDir = ?config(data_dir, Config),
- ?line TestDir = filename:join(DataDir, "dir"),
- ?line Cmd = Program ++ " -pz " ++ DataDir ++
- " -noshell -s port_test pwd -s erlang halt",
- ?line _ = open_port({spawn, Cmd},
- [{cd, TestDir},
- {line, 256}]),
- ?line receive
- {_, {data, {eol, String}}} ->
- case filename_equal(String, TestDir) of
- true ->
- ok;
- false ->
- ?line test_server:fail({cd, String})
- end;
- Other2 ->
- ?line test_server:fail({env, Other2})
- end,
+ Dog = test_server:timetrap(test_server:seconds(60)),
- ?line test_server:timetrap_cancel(Dog),
+ Program = atom_to_list(lib:progname()),
+ DataDir = ?config(data_dir, Config),
+ TestDir = filename:join(DataDir, "dir"),
+ Cmd = Program ++ " -pz " ++ DataDir ++
+ " -noshell -s port_test pwd -s erlang halt",
+ _ = open_port({spawn, Cmd},
+ [{cd, TestDir},
+ {line, 256}]),
+ receive
+ {_, {data, {eol, String}}} ->
+ case filename_equal(String, TestDir) of
+ true ->
+ ok;
+ false ->
+ test_server:fail({cd, String})
+ end;
+ Other2 ->
+ test_server:fail({env, Other2})
+ end,
+
+ test_server:timetrap_cancel(Dog),
ok.
filename_equal(A, B) ->
@@ -1059,8 +994,8 @@ otp_3906(Config) when is_list(Config) ->
-define(OTP_3906_MAX_CONC_OSP, 50).
otp_3906(Config, OSName) ->
- ?line DataDir = filename:dirname(proplists:get_value(data_dir,Config)),
- ?line {ok, Variables} = file:consult(
+ DataDir = filename:dirname(proplists:get_value(data_dir,Config)),
+ {ok, Variables} = file:consult(
filename:join([DataDir,"..","..",
"test_server","variables"])),
case lists:keysearch('CC', 1, Variables) of
@@ -1105,7 +1040,7 @@ otp_3906(Config, OSName) ->
succeded ->
ok;
_ ->
- ?line test_server:fail(Result)
+ test_server:fail(Result)
end;
_ ->
{skipped, "No C compiler found"}
@@ -1246,15 +1181,15 @@ otp_4389(doc) -> [];
otp_4389(Config) when is_list(Config) ->
case os:type() of
{unix, _} ->
- ?line Dog = test_server:timetrap(test_server:seconds(240)),
- ?line TCR = self(),
+ Dog = test_server:timetrap(test_server:seconds(240)),
+ TCR = self(),
case get_true_cmd() of
True when is_list(True) ->
- ?line lists:foreach(
+ lists:foreach(
fun (P) ->
- ?line receive
- {P, ok} -> ?line ok;
- {P, Err} -> ?line ?t:fail(Err)
+ receive
+ {P, ok} -> ok;
+ {P, Err} -> ?t:fail(Err)
end
end,
lists:map(
@@ -1283,14 +1218,14 @@ otp_4389(Config) when is_list(Config) ->
end)
end,
lists:duplicate(1000,[]))),
- ?line test_server:timetrap_cancel(Dog),
+ test_server:timetrap_cancel(Dog),
{comment,
"This test case doesn't always fail when the bug that "
"it tests for is present (it is most likely to fail on"
" a multi processor machine). If the test case fails it"
" will fail by deadlocking the emulator."};
_ ->
- ?line {skipped, "\"true\" command not found"}
+ {skipped, "\"true\" command not found"}
end;
_ ->
{skip,"Only run on Unix"}
@@ -1320,11 +1255,11 @@ exit_status(suite) ->
exit_status(doc) ->
["Test that the 'exit_status' option works"];
exit_status(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(60)),
- ?line port_expect(Config,[{"x",
+ Dog = test_server:timetrap(test_server:seconds(60)),
+ port_expect(Config,[{"x",
[{exit_status, 5}]}],
1, "", [exit_status]),
- ?line test_server:timetrap_cancel(Dog),
+ test_server:timetrap_cancel(Dog),
ok.
spawn_driver(suite) ->
@@ -1332,10 +1267,49 @@ spawn_driver(suite) ->
spawn_driver(doc) ->
["Test spawning a driver specifically"];
spawn_driver(Config) when is_list(Config) ->
+ Dog = test_server:timetrap(test_server:seconds(10)),
+ Path = ?config(data_dir, Config),
+ ok = load_driver(Path, "echo_drv"),
+ Port = erlang:open_port({spawn_driver, "echo_drv"}, []),
+ Port ! {self(), {command, "Hello port!"}},
+ receive
+ {Port, {data, "Hello port!"}} = Msg1 ->
+ io:format("~p~n", [Msg1]),
+ ok;
+ Other ->
+ test_server:fail({unexpected, Other})
+ end,
+ Port ! {self(), close},
+ receive {Port, closed} -> ok end,
+
+ Port2 = erlang:open_port({spawn_driver, "echo_drv -Hello port?"},
+ []),
+ receive
+ {Port2, {data, "Hello port?"}} = Msg2 ->
+ io:format("~p~n", [Msg2]),
+ ok;
+ Other2 ->
+ test_server:fail({unexpected2, Other2})
+ end,
+ Port2 ! {self(), close},
+ receive {Port2, closed} -> ok end,
+ {'EXIT',{badarg,_}} = (catch erlang:open_port({spawn_driver, "ls"}, [])),
+ {'EXIT',{badarg,_}} = (catch erlang:open_port({spawn_driver, "cmd"}, [])),
+ {'EXIT',{badarg,_}} = (catch erlang:open_port({spawn_driver, os:find_executable("erl")}, [])),
+ test_server:timetrap_cancel(Dog),
+ ok.
+
+parallelism_option(suite) ->
+ [];
+parallelism_option(doc) ->
+ ["Test parallelism option of open_port"];
+parallelism_option(Config) when is_list(Config) ->
?line Dog = test_server:timetrap(test_server:seconds(10)),
?line Path = ?config(data_dir, Config),
?line ok = load_driver(Path, "echo_drv"),
- ?line Port = erlang:open_port({spawn_driver, "echo_drv"}, []),
+ ?line Port = erlang:open_port({spawn_driver, "echo_drv"},
+ [{parallelism, true}]),
+ ?line {parallelism, true} = erlang:port_info(Port, parallelism),
?line Port ! {self(), {command, "Hello port!"}},
?line receive
{Port, {data, "Hello port!"}} = Msg1 ->
@@ -1348,7 +1322,8 @@ spawn_driver(Config) when is_list(Config) ->
?line receive {Port, closed} -> ok end,
?line Port2 = erlang:open_port({spawn_driver, "echo_drv -Hello port?"},
- []),
+ [{parallelism, false}]),
+ ?line {parallelism, false} = erlang:port_info(Port2, parallelism),
?line receive
{Port2, {data, "Hello port?"}} = Msg2 ->
io:format("~p~n", [Msg2]),
@@ -1358,9 +1333,6 @@ spawn_driver(Config) when is_list(Config) ->
end,
?line Port2 ! {self(), close},
?line receive {Port2, closed} -> ok end,
- ?line {'EXIT',{badarg,_}} = (catch erlang:open_port({spawn_driver, "ls"}, [])),
- ?line {'EXIT',{badarg,_}} = (catch erlang:open_port({spawn_driver, "cmd"}, [])),
- ?line {'EXIT',{badarg,_}} = (catch erlang:open_port({spawn_driver, os:find_executable("erl")}, [])),
?line test_server:timetrap_cancel(Dog),
ok.
@@ -1369,48 +1341,48 @@ spawn_executable(suite) ->
spawn_executable(doc) ->
["Test spawning an executable specifically"];
spawn_executable(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(10)),
- ?line DataDir = ?config(data_dir, Config),
- ?line EchoArgs1 = filename:join([DataDir,"echo_args"]),
- ?line ExactFile1 = filename:nativename(os:find_executable(EchoArgs1)),
- ?line [ExactFile1] = run_echo_args(DataDir,[]),
- ?line ["echo_args"] = run_echo_args(DataDir,["echo_args"]),
- ?line ["echo_arguments"] = run_echo_args(DataDir,["echo_arguments"]),
- ?line [ExactFile1,"hello world","dlrow olleh"] =
+ Dog = test_server:timetrap(test_server:seconds(10)),
+ DataDir = ?config(data_dir, Config),
+ EchoArgs1 = filename:join([DataDir,"echo_args"]),
+ ExactFile1 = filename:nativename(os:find_executable(EchoArgs1)),
+ [ExactFile1] = run_echo_args(DataDir,[]),
+ ["echo_args"] = run_echo_args(DataDir,["echo_args"]),
+ ["echo_arguments"] = run_echo_args(DataDir,["echo_arguments"]),
+ [ExactFile1,"hello world","dlrow olleh"] =
run_echo_args(DataDir,[ExactFile1,"hello world","dlrow olleh"]),
- ?line [ExactFile1] = run_echo_args(DataDir,[default]),
- ?line [ExactFile1,"hello world","dlrow olleh"] =
+ [ExactFile1] = run_echo_args(DataDir,[default]),
+ [ExactFile1,"hello world","dlrow olleh"] =
run_echo_args(DataDir,[switch_order,ExactFile1,"hello world",
"dlrow olleh"]),
- ?line [ExactFile1,"hello world","dlrow olleh"] =
+ [ExactFile1,"hello world","dlrow olleh"] =
run_echo_args(DataDir,[default,"hello world","dlrow olleh"]),
- ?line [ExactFile1,"hello world","dlrow olleh"] =
+ [ExactFile1,"hello world","dlrow olleh"] =
run_echo_args_2("\""++ExactFile1++"\" "++"\"hello world\" \"dlrow olleh\""),
- ?line PrivDir = ?config(priv_dir, Config),
- ?line SpaceDir =filename:join([PrivDir,"With Spaces"]),
- ?line file:make_dir(SpaceDir),
- ?line Executable = filename:basename(ExactFile1),
- ?line file:copy(ExactFile1,filename:join([SpaceDir,Executable])),
- ?line ExactFile2 = filename:nativename(filename:join([SpaceDir,Executable])),
- ?line chmodplusx(ExactFile2),
+ PrivDir = ?config(priv_dir, Config),
+ SpaceDir =filename:join([PrivDir,"With Spaces"]),
+ file:make_dir(SpaceDir),
+ Executable = filename:basename(ExactFile1),
+ file:copy(ExactFile1,filename:join([SpaceDir,Executable])),
+ ExactFile2 = filename:nativename(filename:join([SpaceDir,Executable])),
+ chmodplusx(ExactFile2),
io:format("|~s|~n",[ExactFile2]),
- ?line [ExactFile2] = run_echo_args(SpaceDir,[]),
- ?line ["echo_args"] = run_echo_args(SpaceDir,["echo_args"]),
- ?line ["echo_arguments"] = run_echo_args(SpaceDir,["echo_arguments"]),
- ?line [ExactFile2,"hello world","dlrow olleh"] =
+ [ExactFile2] = run_echo_args(SpaceDir,[]),
+ ["echo_args"] = run_echo_args(SpaceDir,["echo_args"]),
+ ["echo_arguments"] = run_echo_args(SpaceDir,["echo_arguments"]),
+ [ExactFile2,"hello world","dlrow olleh"] =
run_echo_args(SpaceDir,[ExactFile2,"hello world","dlrow olleh"]),
- ?line [ExactFile2] = run_echo_args(SpaceDir,[default]),
- ?line [ExactFile2,"hello world","dlrow olleh"] =
+ [ExactFile2] = run_echo_args(SpaceDir,[default]),
+ [ExactFile2,"hello world","dlrow olleh"] =
run_echo_args(SpaceDir,[switch_order,ExactFile2,"hello world",
"dlrow olleh"]),
- ?line [ExactFile2,"hello world","dlrow olleh"] =
+ [ExactFile2,"hello world","dlrow olleh"] =
run_echo_args(SpaceDir,[default,"hello world","dlrow olleh"]),
- ?line [ExactFile2,"hello world","dlrow olleh"] =
+ [ExactFile2,"hello world","dlrow olleh"] =
run_echo_args_2("\""++ExactFile2++"\" "++"\"hello world\" \"dlrow olleh\""),
- ?line ExeExt =
+ ExeExt =
case string:to_lower(lists:last(string:tokens(ExactFile2,"."))) of
"exe" ->
".exe";
@@ -1418,47 +1390,47 @@ spawn_executable(Config) when is_list(Config) ->
""
end,
Executable2 = "spoky name"++ExeExt,
- ?line file:copy(ExactFile1,filename:join([SpaceDir,Executable2])),
- ?line ExactFile3 = filename:nativename(filename:join([SpaceDir,Executable2])),
- ?line chmodplusx(ExactFile3),
- ?line [ExactFile3] = run_echo_args(SpaceDir,Executable2,[]),
- ?line ["echo_args"] = run_echo_args(SpaceDir,Executable2,["echo_args"]),
- ?line ["echo_arguments"] = run_echo_args(SpaceDir,Executable2,["echo_arguments"]),
- ?line [ExactFile3,"hello world","dlrow olleh"] =
+ file:copy(ExactFile1,filename:join([SpaceDir,Executable2])),
+ ExactFile3 = filename:nativename(filename:join([SpaceDir,Executable2])),
+ chmodplusx(ExactFile3),
+ [ExactFile3] = run_echo_args(SpaceDir,Executable2,[]),
+ ["echo_args"] = run_echo_args(SpaceDir,Executable2,["echo_args"]),
+ ["echo_arguments"] = run_echo_args(SpaceDir,Executable2,["echo_arguments"]),
+ [ExactFile3,"hello world","dlrow olleh"] =
run_echo_args(SpaceDir,Executable2,[ExactFile3,"hello world","dlrow olleh"]),
- ?line [ExactFile3] = run_echo_args(SpaceDir,Executable2,[default]),
- ?line [ExactFile3,"hello world","dlrow olleh"] =
+ [ExactFile3] = run_echo_args(SpaceDir,Executable2,[default]),
+ [ExactFile3,"hello world","dlrow olleh"] =
run_echo_args(SpaceDir,Executable2,
[switch_order,ExactFile3,"hello world",
"dlrow olleh"]),
- ?line [ExactFile3,"hello world","dlrow olleh"] =
+ [ExactFile3,"hello world","dlrow olleh"] =
run_echo_args(SpaceDir,Executable2,
[default,"hello world","dlrow olleh"]),
- ?line [ExactFile3,"hello world","dlrow olleh"] =
+ [ExactFile3,"hello world","dlrow olleh"] =
run_echo_args_2("\""++ExactFile3++"\" "++"\"hello world\" \"dlrow olleh\""),
- ?line {'EXIT',{enoent,_}} = (catch run_echo_args(SpaceDir,"fnurflmonfi",
+ {'EXIT',{enoent,_}} = (catch run_echo_args(SpaceDir,"fnurflmonfi",
[default,"hello world",
"dlrow olleh"])),
NonExec = "kronxfrt"++ExeExt,
- ?line file:write_file(filename:join([SpaceDir,NonExec]),
+ file:write_file(filename:join([SpaceDir,NonExec]),
<<"Not an executable">>),
- ?line {'EXIT',{eacces,_}} = (catch run_echo_args(SpaceDir,NonExec,
+ {'EXIT',{eacces,_}} = (catch run_echo_args(SpaceDir,NonExec,
[default,"hello world",
"dlrow olleh"])),
- ?line {'EXIT',{enoent,_}} = (catch open_port({spawn_executable,"cmd"},[])),
- ?line {'EXIT',{enoent,_}} = (catch open_port({spawn_executable,"sh"},[])),
+ {'EXIT',{enoent,_}} = (catch open_port({spawn_executable,"cmd"},[])),
+ {'EXIT',{enoent,_}} = (catch open_port({spawn_executable,"sh"},[])),
case os:type() of
{win32,_} ->
test_bat_file(SpaceDir);
{unix,_} ->
test_sh_file(SpaceDir)
end,
- ?line test_server:timetrap_cancel(Dog),
+ test_server:timetrap_cancel(Dog),
ok.
unregister_name(Config) when is_list(Config) ->
- ?line true = register(crash, open_port({spawn, "sleep 100"}, [])),
- ?line true = unregister(crash).
+ true = register(crash, open_port({spawn, "sleep 100"}, [])),
+ true = unregister(crash).
test_bat_file(Dir) ->
FN = "tf.bat",
@@ -1478,16 +1450,16 @@ test_bat_file(Dir) ->
<<"\r\n">>,
<<":done\r\n">>,
<<"\r\n">>],
- ?line file:write_file(Full,list_to_binary(D)),
- ?line EF = filename:basename(FN),
- ?line [DN,"hello","world"] =
+ file:write_file(Full,list_to_binary(D)),
+ EF = filename:basename(FN),
+ [DN,"hello","world"] =
run_echo_args(Dir,FN,
[default,"hello","world"]),
%% The arg0 argumant should be ignored when running batch files
- ?line [DN,"hello","world"] =
+ [DN,"hello","world"] =
run_echo_args(Dir,FN,
["knaskurt","hello","world"]),
- ?line EF = filename:basename(DN),
+ EF = filename:basename(DN),
ok.
test_sh_file(Dir) ->
@@ -1501,20 +1473,20 @@ test_sh_file(Dir) ->
<<" shift\n">>,
<<" i=`expr $i + 1`\n">>,
<<"done\n">>],
- ?line file:write_file(Full,list_to_binary(D)),
- ?line chmodplusx(Full),
- ?line [Full,"hello","world"] =
+ file:write_file(Full,list_to_binary(D)),
+ chmodplusx(Full),
+ [Full,"hello","world"] =
run_echo_args(Dir,FN,
[default,"hello","world"]),
- ?line [Full,"hello","world of spaces"] =
+ [Full,"hello","world of spaces"] =
run_echo_args(Dir,FN,
[default,"hello","world of spaces"]),
- ?line file:write_file(filename:join([Dir,"testfile1"]),<<"testdata1">>),
- ?line file:write_file(filename:join([Dir,"testfile2"]),<<"testdata2">>),
- ?line Pattern = filename:join([Dir,"testfile*"]),
- ?line L = filelib:wildcard(Pattern),
- ?line 2 = length(L),
- ?line [Full,"hello",Pattern] =
+ file:write_file(filename:join([Dir,"testfile1"]),<<"testdata1">>),
+ file:write_file(filename:join([Dir,"testfile2"]),<<"testdata2">>),
+ Pattern = filename:join([Dir,"testfile*"]),
+ L = filelib:wildcard(Pattern),
+ 2 = length(L),
+ [Full,"hello",Pattern] =
run_echo_args(Dir,FN,
[default,"hello",Pattern]),
ok.
@@ -1574,25 +1546,25 @@ mix_up_ports(suite) ->
mix_up_ports(doc) ->
["Test that the emulator does not mix up ports when the port table wraps"];
mix_up_ports(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(10)),
- ?line Path = ?config(data_dir, Config),
- ?line ok = load_driver(Path, "echo_drv"),
- ?line Port = erlang:open_port({spawn, "echo_drv"}, []),
- ?line Port ! {self(), {command, "Hello port!"}},
- ?line receive
+ Dog = test_server:timetrap(test_server:seconds(10)),
+ Path = ?config(data_dir, Config),
+ ok = load_driver(Path, "echo_drv"),
+ Port = erlang:open_port({spawn, "echo_drv"}, []),
+ Port ! {self(), {command, "Hello port!"}},
+ receive
{Port, {data, "Hello port!"}} = Msg1 ->
io:format("~p~n", [Msg1]),
ok;
Other ->
test_server:fail({unexpected, Other})
end,
- ?line Port ! {self(), close},
- ?line receive {Port, closed} -> ok end,
- ?line loop(start, done,
+ Port ! {self(), close},
+ receive {Port, closed} -> ok end,
+ loop(start, done,
fun(P) ->
- ?line Q =
+ Q =
(catch erlang:open_port({spawn, "echo_drv"}, [])),
-%% ?line io:format("~p ", [Q]),
+%% io:format("~p ", [Q]),
if is_port(Q) ->
Q;
true ->
@@ -1600,14 +1572,14 @@ mix_up_ports(Config) when is_list(Config) ->
done
end
end),
- ?line Port ! {self(), {command, "Hello again port!"}},
- ?line receive
+ Port ! {self(), {command, "Hello again port!"}},
+ receive
Msg2 ->
test_server:fail({unexpected, Msg2})
after 1000 ->
ok
end,
- ?line test_server:timetrap_cancel(Dog),
+ test_server:timetrap_cancel(Dog),
ok.
loop(Stop, Stop, Fun) when is_function(Fun) ->
@@ -1622,45 +1594,46 @@ otp_5112(doc) ->
["Test that link to connected process is taken away when port calls",
"driver_exit() also when the port index has wrapped"];
otp_5112(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(10)),
- ?line Path = ?config(data_dir, Config),
- ?line ok = load_driver(Path, "exit_drv"),
- ?line Port = otp_5112_get_wrapped_port(),
- ?line ?t:format("Max ports: ~p~n",[max_ports()]),
- ?line ?t:format("Port: ~p~n",[Port]),
- ?line {links, Links1} = process_info(self(),links),
- ?line ?t:format("Links1: ~p~n",[Links1]),
- ?line true = lists:member(Port, Links1),
- ?line Port ! {self(), {command, ""}},
- ?line {links, Links2} = process_info(self(),links),
- ?line ?t:format("Links2: ~p~n",[Links2]),
- ?line false = lists:member(Port, Links2), %% This used to fail
- ?line test_server:timetrap_cancel(Dog),
+ Dog = test_server:timetrap(test_server:seconds(10)),
+ Path = ?config(data_dir, Config),
+ ok = load_driver(Path, "exit_drv"),
+ Port = otp_5112_get_wrapped_port(),
+ ?t:format("Max ports: ~p~n",[max_ports()]),
+ ?t:format("Port: ~p~n",[Port]),
+ {links, Links1} = process_info(self(),links),
+ ?t:format("Links1: ~p~n",[Links1]),
+ true = lists:member(Port, Links1),
+ Port ! {self(), {command, ""}},
+ ?line wait_until(fun () -> lists:member(Port, erlang:ports()) == false end),
+ {links, Links2} = process_info(self(),links),
+ ?t:format("Links2: ~p~n",[Links2]),
+ false = lists:member(Port, Links2), %% This used to fail
+ test_server:timetrap_cancel(Dog),
ok.
otp_5112_get_wrapped_port() ->
- ?line P1 = erlang:open_port({spawn, "exit_drv"}, []),
- ?line case port_ix(P1) < max_ports() of
+ P1 = erlang:open_port({spawn, "exit_drv"}, []),
+ case port_ix(P1) < max_ports() of
true ->
- ?line ?t:format("Need to wrap port index (~p)~n", [P1]),
- ?line otp_5112_wrap_port_ix([P1]),
- ?line P2 = erlang:open_port({spawn, "exit_drv"}, []),
- ?line false = port_ix(P2) < max_ports(),
- ?line P2;
+ ?t:format("Need to wrap port index (~p)~n", [P1]),
+ otp_5112_wrap_port_ix([P1]),
+ P2 = erlang:open_port({spawn, "exit_drv"}, []),
+ false = port_ix(P2) < max_ports(),
+ P2;
false ->
- ?line ?t:format("Port index already wrapped (~p)~n", [P1]),
- ?line P1
+ ?t:format("Port index already wrapped (~p)~n", [P1]),
+ P1
end.
otp_5112_wrap_port_ix(Ports) ->
- ?line case (catch erlang:open_port({spawn, "exit_drv"}, [])) of
+ case (catch erlang:open_port({spawn, "exit_drv"}, [])) of
Port when is_port(Port) ->
- ?line otp_5112_wrap_port_ix([Port|Ports]);
+ otp_5112_wrap_port_ix([Port|Ports]);
_ ->
%% Port table now full; empty port table
- ?line lists:foreach(fun (P) -> P ! {self(), close} end,
+ lists:foreach(fun (P) -> P ! {self(), close} end,
Ports),
- ?line ok
+ ok
end.
@@ -1669,106 +1642,75 @@ otp_5119(suite) ->
otp_5119(doc) ->
["Test that port index is not unnecessarily wrapped"];
otp_5119(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(10)),
- ?line Path = ?config(data_dir, Config),
- ?line ok = load_driver(Path, "exit_drv"),
- ?line PI1 = port_ix(otp_5119_fill_empty_port_tab([])),
- ?line PI2 = port_ix(erlang:open_port({spawn, "exit_drv"}, [])),
- ?line {PortIx1, PortIx2}
- = case PI2 > PI1 of
- true ->
- ?line {PI1, PI2};
- false ->
- ?line {port_ix(otp_5119_fill_empty_port_tab([PI2])),
- port_ix(erlang:open_port({spawn, "exit_drv"}, []))}
- end,
- ?line MaxPorts = max_ports(),
- ?line ?t:format("PortIx1 = ~p ~p~n", [PI1, PortIx1]),
- ?line ?t:format("PortIx2 = ~p ~p~n", [PI2, PortIx2]),
- ?line ?t:format("MaxPorts = ~p~n", [MaxPorts]),
- ?line true = PortIx2 > PortIx1,
- ?line true = PortIx2 =< PortIx1 + MaxPorts,
- ?line test_server:timetrap_cancel(Dog),
- ?line ok.
+ Dog = test_server:timetrap(test_server:seconds(10)),
+ Path = ?config(data_dir, Config),
+ ok = load_driver(Path, "exit_drv"),
+ PI1 = port_ix(otp_5119_fill_empty_port_tab([])),
+ PI2 = port_ix(erlang:open_port({spawn, "exit_drv"}, [])),
+ {PortIx1, PortIx2} = case PI2 > PI1 of
+ true ->
+ {PI1, PI2};
+ false ->
+ {port_ix(otp_5119_fill_empty_port_tab([PI2])),
+ port_ix(erlang:open_port({spawn, "exit_drv"}, []))}
+ end,
+ MaxPorts = max_ports(),
+ ?t:format("PortIx1 = ~p ~p~n", [PI1, PortIx1]),
+ ?t:format("PortIx2 = ~p ~p~n", [PI2, PortIx2]),
+ ?t:format("MaxPorts = ~p~n", [MaxPorts]),
+ true = PortIx2 > PortIx1,
+ true = PortIx2 =< PortIx1 + MaxPorts,
+ test_server:timetrap_cancel(Dog),
+ ok.
otp_5119_fill_empty_port_tab(Ports) ->
- ?line case (catch erlang:open_port({spawn, "exit_drv"}, [])) of
+ case (catch erlang:open_port({spawn, "exit_drv"}, [])) of
Port when is_port(Port) ->
- ?line otp_5119_fill_empty_port_tab([Port|Ports]);
+ otp_5119_fill_empty_port_tab([Port|Ports]);
_ ->
%% Port table now full; empty port table
- ?line lists:foreach(fun (P) -> P ! {self(), close} end,
+ lists:foreach(fun (P) -> P ! {self(), close} end,
Ports),
- ?line [LastPort|_] = Ports,
- ?line LastPort
- end.
-
--define(DEF_MAX_PORTS, 1024).
-
-max_ports_env() ->
- ?line case os:getenv("ERL_MAX_PORTS") of
- EMP when is_list(EMP) ->
- case catch list_to_integer(EMP) of
- Int when is_integer(Int) -> ?line Int;
- _ -> ?line false
- end;
- _ -> ?line false
+ [LastPort|_] = Ports,
+ LastPort
end.
max_ports() ->
- ?line PreMaxPorts
- = case max_ports_env() of
- Env when is_integer(Env) -> ?line Env;
- _ ->
- ?line case os:type() of
- {unix, _} ->
- ?line UlimStr = string:strip(os:cmd("ulimit -n")
- -- "\n"),
- ?line case catch list_to_integer(UlimStr) of
- Ulim when is_integer(Ulim) -> ?line Ulim;
- _ -> ?line ?DEF_MAX_PORTS
- end;
- _ -> ?line ?DEF_MAX_PORTS
- end
- end,
- ?line case PreMaxPorts > ?DEF_MAX_PORTS of
- true -> ?line PreMaxPorts;
- false -> ?line ?DEF_MAX_PORTS
- end.
+ erlang:system_info(port_limit).
port_ix(Port) when is_port(Port) ->
- ?line ["#Port",_,PortIxStr] = string:tokens(erlang:port_to_list(Port),
+ ["#Port",_,PortIxStr] = string:tokens(erlang:port_to_list(Port),
"<.>"),
- ?line list_to_integer(PortIxStr).
+ list_to_integer(PortIxStr).
otp_6224(doc) -> ["Check that port command failure doesn't crash the emulator"];
otp_6224(suite) -> [];
otp_6224(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(10)),
- ?line Path = ?config(data_dir, Config),
- ?line ok = load_driver(Path, "failure_drv"),
- ?line Go = make_ref(),
- ?line Failer = spawn(fun () ->
+ Dog = test_server:timetrap(test_server:seconds(10)),
+ Path = ?config(data_dir, Config),
+ ok = load_driver(Path, "failure_drv"),
+ Go = make_ref(),
+ Failer = spawn(fun () ->
receive Go -> ok end,
- ?line Port = open_port({spawn, "failure_drv"},
+ Port = open_port({spawn, "failure_drv"},
[]),
Port ! {self(), {command, "Fail, please!"}},
otp_6224_loop()
end),
- ?line Mon = erlang:monitor(process, Failer),
- ?line Failer ! Go,
- ?line receive
+ Mon = erlang:monitor(process, Failer),
+ Failer ! Go,
+ receive
{'DOWN', Mon, process, Failer, Reason} ->
- ?line case Reason of
- {driver_failed, _} -> ?line ok;
- driver_failed -> ?line ok;
- _ -> ?line ?t:fail({unexpected_exit_reason,
+ case Reason of
+ {driver_failed, _} -> ok;
+ driver_failed -> ok;
+ _ -> ?t:fail({unexpected_exit_reason,
Reason})
end
end,
- ?line test_server:timetrap_cancel(Dog),
- ?line ok.
+ test_server:timetrap_cancel(Dog),
+ ok.
otp_6224_loop() ->
receive _ -> ok after 0 -> ok end,
@@ -1781,11 +1723,11 @@ otp_6224_loop() ->
exit_status_multi_scheduling_block(doc) -> [];
exit_status_multi_scheduling_block(suite) -> [];
exit_status_multi_scheduling_block(Config) when is_list(Config) ->
- ?line Repeat = 3,
- ?line case ?t:os_type() of
+ Repeat = 3,
+ case ?t:os_type() of
{unix, _} ->
- ?line Dog = ?t:timetrap(test_server:minutes(2*Repeat)),
- ?line SleepSecs = 6,
+ Dog = ?t:timetrap(test_server:minutes(2*Repeat)),
+ SleepSecs = 6,
try
lists:foreach(fun (_) ->
exit_status_msb_test(Config,
@@ -1799,7 +1741,7 @@ exit_status_multi_scheduling_block(Config) when is_list(Config) ->
?t:timetrap_cancel(Dog),
receive after SleepSecs+500 -> ok end
end;
- _ -> ?line {skip, "Not implemented for this OS"}
+ _ -> {skip, "Not implemented for this OS"}
end.
exit_status_msb_test(Config, SleepSecs) when is_list(Config) ->
@@ -1808,22 +1750,22 @@ exit_status_msb_test(Config, SleepSecs) when is_list(Config) ->
%% and we want these port programs to terminate while multi-scheduling
%% is blocked.
%%
- ?line NoSchedsOnln = erlang:system_info(schedulers_online),
- ?line Parent = self(),
- ?line ?t:format("SleepSecs = ~p~n", [SleepSecs]),
- ?line PortProg = "sleep " ++ integer_to_list(SleepSecs),
- ?line Start = now(),
- ?line NoProcs = case NoSchedsOnln of
+ NoSchedsOnln = erlang:system_info(schedulers_online),
+ Parent = self(),
+ ?t:format("SleepSecs = ~p~n", [SleepSecs]),
+ PortProg = "sleep " ++ integer_to_list(SleepSecs),
+ Start = now(),
+ NoProcs = case NoSchedsOnln of
NProcs when NProcs < ?EXIT_STATUS_MSB_MAX_PROCS ->
NProcs;
_ ->
?EXIT_STATUS_MSB_MAX_PROCS
end,
- ?line NoPortsPerProc = case 20*NoProcs of
+ NoPortsPerProc = case 20*NoProcs of
TNPorts when TNPorts < ?EXIT_STATUS_MSB_MAX_PORTS -> 20;
_ -> ?EXIT_STATUS_MSB_MAX_PORTS div NoProcs
end,
- ?line ?t:format("NoProcs = ~p~nNoPortsPerProc = ~p~n",
+ ?t:format("NoProcs = ~p~nNoPortsPerProc = ~p~n",
[NoProcs, NoPortsPerProc]),
ProcFun
= fun () ->
@@ -1873,32 +1815,32 @@ exit_status_msb_test(Config, SleepSecs) when is_list(Config) ->
PrtSIds),
Parent ! {self(), done}
end,
- ?line Procs = lists:map(fun (N) ->
+ Procs = lists:map(fun (N) ->
spawn_opt(ProcFun,
[link,
{scheduler,
(N rem NoSchedsOnln)+1}])
end,
lists:seq(1, NoProcs)),
- ?line SIds = lists:map(fun (P) ->
+ SIds = lists:map(fun (P) ->
receive {P, started, SIds} -> SIds end
end,
Procs),
- ?line StartedTime = timer:now_diff(now(), Start)/1000000,
- ?line ?t:format("StartedTime = ~p~n", [StartedTime]),
- ?line true = StartedTime < SleepSecs,
- ?line erlang:system_flag(multi_scheduling, block),
- ?line lists:foreach(fun (P) -> receive {P, done} -> ok end end, Procs),
- ?line DoneTime = timer:now_diff(now(), Start)/1000000,
- ?line ?t:format("DoneTime = ~p~n", [DoneTime]),
- ?line true = DoneTime > SleepSecs,
- ?line ok = verify_multi_scheduling_blocked(),
- ?line erlang:system_flag(multi_scheduling, unblock),
- ?line case {length(lists:usort(lists:flatten(SIds))), NoSchedsOnln} of
+ StartedTime = timer:now_diff(now(), Start)/1000000,
+ ?t:format("StartedTime = ~p~n", [StartedTime]),
+ true = StartedTime < SleepSecs,
+ erlang:system_flag(multi_scheduling, block),
+ lists:foreach(fun (P) -> receive {P, done} -> ok end end, Procs),
+ DoneTime = timer:now_diff(now(), Start)/1000000,
+ ?t:format("DoneTime = ~p~n", [DoneTime]),
+ true = DoneTime > SleepSecs,
+ ok = verify_multi_scheduling_blocked(),
+ erlang:system_flag(multi_scheduling, unblock),
+ case {length(lists:usort(lists:flatten(SIds))), NoSchedsOnln} of
{N, N} ->
- ?line ok;
+ ok;
{N, M} ->
- ?line ?t:fail("Failed to create ports on all"
+ ?t:fail("Failed to create ports on all"
++ integer_to_list(M) ++ " available"
"schedulers. Only created ports on "
++ integer_to_list(N) ++ " schedulers.")
@@ -1921,18 +1863,18 @@ sid_proc(SIds) ->
end.
verify_multi_scheduling_blocked() ->
- ?line Procs = lists:map(fun (_) ->
+ Procs = lists:map(fun (_) ->
spawn_link(fun () -> sid_proc([]) end)
end,
lists:seq(1, 3*erlang:system_info(schedulers_online))),
- ?line receive after 1000 -> ok end,
- ?line SIds = lists:map(fun (P) ->
+ receive after 1000 -> ok end,
+ SIds = lists:map(fun (P) ->
P ! {self(), want_sids},
receive {P, sids, PSIds} -> PSIds end
end,
Procs),
- ?line 1 = length(lists:usort(lists:flatten(SIds))),
- ?line ok.
+ 1 = length(lists:usort(lists:flatten(SIds))),
+ ok.
%%% Pinging functions.
@@ -1993,30 +1935,30 @@ build_cmd_line(FixedCmdLine, [], Result) ->
port_expect(Config, Actions, HSize, CmdLine, Options0) ->
% io:format("port_expect(~p, ~p, ~p, ~p)",
% [Actions, HSize, CmdLine, Options0]),
- ?line PortTest = port_test(Config),
- ?line Cmd = lists:concat([PortTest, " -h", HSize, " ", CmdLine]),
- ?line PortType =
+ PortTest = port_test(Config),
+ Cmd = lists:concat([PortTest, " -h", HSize, " ", CmdLine]),
+ PortType =
case HSize of
0 -> stream;
_ -> {packet, HSize}
end,
- ?line Options = [PortType|Options0],
- ?line io:format("open_port({spawn, ~p}, ~p)", [Cmd, Options]),
- ?line Port = open_port({spawn, Cmd}, Options),
- ?line port_expect(Port, Actions, Options),
+ Options = [PortType|Options0],
+ io:format("open_port({spawn, ~p}, ~p)", [Cmd, Options]),
+ Port = open_port({spawn, Cmd}, Options),
+ port_expect(Port, Actions, Options),
Port.
port_expect(Port, [{Send, Expects}|Rest], Options) when is_list(Expects) ->
- ?line port_send(Port, Send),
- ?line IsBinaryPort = lists:member(binary, Options),
- ?line Receiver =
+ port_send(Port, Send),
+ IsBinaryPort = lists:member(binary, Options),
+ Receiver =
case {lists:member(stream, Options), line_option(Options)} of
{false, _} -> fun receive_all/2;
{true,false} -> fun stream_receive_all/2;
{_, true} -> fun receive_all/2
end,
- ?line Receiver(Port, maybe_to_binary(Expects, IsBinaryPort)),
- ?line port_expect(Port, Rest, Options);
+ Receiver(Port, maybe_to_binary(Expects, IsBinaryPort)),
+ port_expect(Port, Rest, Options);
port_expect(_, [], _) ->
ok.
@@ -2068,7 +2010,7 @@ receive_all(Port, [Expect|Rest]) ->
_ ->
%%% io:format("Unexpected message: ~s", [format(Other)]),
io:format("Unexpected message: ~w", [Other]),
- ?line test_server:fail(unexpected_message)
+ test_server:fail(unexpected_message)
end
end,
receive_all(Port, Rest);
@@ -2157,15 +2099,8 @@ build_packet(Left, Result, NextChar0) ->
build_packet(Left-1, [NextChar0|Result], NextChar).
sizes() ->
- case os:type() of
- vxworks ->
- % don't stress VxWorks too much
- [10, 13, 64, 127, 128, 255, 256, 1023, 1024,
- 8191, 8192, 16383, 16384];
- _ ->
- [10, 13, 64, 127, 128, 255, 256, 1023, 1024,
- 32767, 32768, 65535, 65536]
- end.
+ [10, 13, 64, 127, 128, 255, 256, 1023, 1024,
+ 32767, 32768, 65535, 65536].
sizes(Header_Size) ->
sizes(Header_Size, sizes(), []).
@@ -2203,18 +2138,18 @@ fun_spawn(Fun, Args) ->
spawn_link(erlang, apply, [Fun, Args]).
port_test(Config) when is_list(Config) ->
- ?line filename:join(?config(data_dir, Config), "port_test").
+ filename:join(?config(data_dir, Config), "port_test").
ports(doc) -> "Test that erlang:ports/0 returns a consistent snapshot of ports";
ports(suite) -> [];
ports(Config) when is_list(Config) ->
- ?line Path = ?config(data_dir, Config),
- ?line ok = load_driver(Path, "exit_drv"),
+ Path = ?config(data_dir, Config),
+ ok = load_driver(Path, "exit_drv"),
receive after 1000 -> ok end, % Wait for other ports to stabilize
- ?line OtherPorts = erlang:ports(),
+ OtherPorts = erlang:ports(),
io:format("Other ports: ~p\n",[OtherPorts]),
MaxPorts = 1024 - length(OtherPorts),
@@ -2222,7 +2157,7 @@ ports(Config) when is_list(Config) ->
ports_snapshots(100, TrafficPid, OtherPorts),
TrafficPid ! {self(),die},
- ?line receive {TrafficPid, dead} -> ok end,
+ receive {TrafficPid, dead} -> ok end,
ok.
ports_snapshots(0, _, _) ->
@@ -2230,12 +2165,12 @@ ports_snapshots(0, _, _) ->
ports_snapshots(Iter, TrafficPid, OtherPorts) ->
TrafficPid ! start,
- ?line receive after 1 -> ok end,
+ receive after 1 -> ok end,
Snapshot = erlang:ports(),
TrafficPid ! {self(), stop},
- ?line receive {TrafficPid, EventList, TrafficPorts} -> ok end,
+ receive {TrafficPid, EventList, TrafficPorts} -> ok end,
%%io:format("Snapshot=~p\n", [Snapshot]),
ports_verify(Snapshot, OtherPorts ++ TrafficPorts, EventList),
@@ -2252,7 +2187,7 @@ ports_traffic_stopped(MaxPorts, {PortList, PortCnt}) ->
%%io:format("Traffic started in ~p\n",[self()]),
ports_traffic_started(MaxPorts, {PortList, PortCnt}, []);
{Pid,die} ->
- ?line lists:foreach(fun(Port)-> erlang:port_close(Port) end,
+ lists:foreach(fun(Port)-> erlang:port_close(Port) end,
PortList),
Pid ! {self(),dead}
end.
@@ -2272,15 +2207,15 @@ ports_traffic_do(MaxPorts, {PortList, PortCnt}, EventList) ->
N = uniform(MaxPorts),
case N > PortCnt of
true -> % Open port
- ?line P = open_port({spawn, "exit_drv"}, []),
+ P = open_port({spawn, "exit_drv"}, []),
%%io:format("Created port ~p\n",[P]),
ports_traffic_started(MaxPorts, {[P|PortList], PortCnt+1},
[{open,P}|EventList]);
false -> % Close port
- ?line P = lists:nth(N, PortList),
+ P = lists:nth(N, PortList),
%%io:format("Close port ~p\n",[P]),
- ?line true = erlang:port_close(P),
+ true = erlang:port_close(P),
ports_traffic_started(MaxPorts, {lists:delete(P,PortList), PortCnt-1},
[{close,P}|EventList])
end.
@@ -2301,7 +2236,7 @@ ports_verify(Ports, PortsAfter, EventList) ->
ports_verify(Ports, [P | PortsAfter], Tail);
[] ->
- ?line test_server:fail("Inconsistent snapshot from erlang:ports()")
+ test_server:fail("Inconsistent snapshot from erlang:ports()")
end
end.
@@ -2318,31 +2253,38 @@ close_deaf_port(doc) -> ["Send data to port program that does not read it, then
"Primary targeting Windows to test threaded_handle_closer in sys.c"];
close_deaf_port(suite) -> [];
close_deaf_port(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(100)),
- ?line DataDir = ?config(data_dir, Config),
- ?line DeadPort = os:find_executable("dead_port", DataDir),
- ?line Port = open_port({spawn,DeadPort++" 60"},[]),
- ?line erlang:port_command(Port,"Hello, can you hear me!?!?"),
- ?line port_close(Port),
+ Dog = test_server:timetrap(test_server:seconds(100)),
+ DataDir = ?config(data_dir, Config),
+ DeadPort = os:find_executable("dead_port", DataDir),
+ Port = open_port({spawn,DeadPort++" 60"},[]),
+ erlang:port_command(Port,"Hello, can you hear me!?!?"),
+ port_close(Port),
Res = close_deaf_port_1(0, DeadPort),
io:format("Waiting for OS procs to terminate...\n"),
receive after 5*1000 -> ok end,
- ?line test_server:timetrap_cancel(Dog),
+ test_server:timetrap_cancel(Dog),
Res.
close_deaf_port_1(1000, _) ->
ok;
close_deaf_port_1(N, Cmd) ->
Timeout = integer_to_list(random:uniform(5*1000)),
- ?line try open_port({spawn_executable,Cmd},[{args,[Timeout]}]) of
+ try open_port({spawn_executable,Cmd},[{args,[Timeout]}]) of
Port ->
- ?line erlang:port_command(Port,"Hello, can you hear me!?!?"),
- ?line port_close(Port),
+ erlang:port_command(Port,"Hello, can you hear me!?!?"),
+ port_close(Port),
close_deaf_port_1(N+1, Cmd)
catch
_:eagain ->
{comment, "Could not spawn more than " ++ integer_to_list(N) ++ " OS processes."}
end.
-
+wait_until(Fun) ->
+ case catch Fun() of
+ true ->
+ ok;
+ _ ->
+ receive after 100 -> ok end,
+ wait_until(Fun)
+ end.
diff --git a/erts/emulator/test/port_SUITE_data/dead_port.c b/erts/emulator/test/port_SUITE_data/dead_port.c
index 68e96fbf14..4dd9ee4cc2 100644
--- a/erts/emulator/test/port_SUITE_data/dead_port.c
+++ b/erts/emulator/test/port_SUITE_data/dead_port.c
@@ -17,15 +17,6 @@
* %CopyrightEnd%
*/
-#ifdef VXWORKS
-#include <vxWorks.h>
-#include <taskVarLib.h>
-#include <taskLib.h>
-#include <sysLib.h>
-#include <string.h>
-#include <ioLib.h>
-#endif
-
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -37,12 +28,7 @@
#ifndef __WIN32__
#include <unistd.h>
-#ifdef VXWORKS
-#include "reclaim.h"
-#include <sys/times.h>
-#else
#include <sys/time.h>
-#endif
#define O_BINARY 0
#define _setmode(fd, mode)
@@ -53,13 +39,7 @@
#include "winbase.h"
#endif
-
-#ifdef VXWORKS
-#define MAIN(argc, argv) port_test(argc, argv)
-#else
#define MAIN(argc, argv) main(argc, argv)
-#endif
-
extern int errno;
@@ -86,9 +66,6 @@ char *argv[];
static void
delay(unsigned ms)
{
-#ifdef VXWORKS
- taskDelay((sysClkRateGet() * ms) / 1000);
-#else
#ifdef __WIN32__
Sleep(ms);
#else
@@ -98,5 +75,4 @@ delay(unsigned ms)
select(0, NULL, NULL, NULL, &t);
#endif
-#endif
}
diff --git a/erts/emulator/test/port_SUITE_data/port_test.c b/erts/emulator/test/port_SUITE_data/port_test.c
index 7b4e386d87..7abefab2e3 100644
--- a/erts/emulator/test/port_SUITE_data/port_test.c
+++ b/erts/emulator/test/port_SUITE_data/port_test.c
@@ -3,15 +3,6 @@
* Purpose: A port program to be used for testing the open_port bif.
*/
-#ifdef VXWORKS
-#include <vxWorks.h>
-#include <taskVarLib.h>
-#include <taskLib.h>
-#include <sysLib.h>
-#include <string.h>
-#include <ioLib.h>
-#endif
-
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -23,12 +14,7 @@
#ifndef __WIN32__
#include <unistd.h>
-#ifdef VXWORKS
-#include "reclaim.h"
-#include <sys/times.h>
-#else
#include <sys/time.h>
-#endif
#define O_BINARY 0
#define _setmode(fd, mode)
@@ -40,22 +26,13 @@
#endif
-#ifdef VXWORKS
-#define REDIR_STDOUT(fd) ioTaskStdSet(0, 1, fd);
-#else
#define REDIR_STDOUT(fd) if (dup2(fd, 1) == -1) { \
fprintf(stderr, "%s: failed to duplicate handle %d to 1: %d\n", \
port_data->progname, fd, errno); \
exit(1); \
}
-#endif
-#ifdef VXWORKS
-#define MAIN(argc, argv) port_test(argc, argv)
-#else
#define MAIN(argc, argv) main(argc, argv)
-#endif
-
extern int errno;
@@ -101,7 +78,6 @@ static void dump(unsigned char* buf, int sz, int max);
static void replace_stdout(char* filename);
static void generate_reply(char* spec);
-#ifndef VXWORKS
#ifndef HAVE_STRERROR
extern int sys_nerr;
#ifndef sys_errlist /* sys_errlist is sometimes defined to
@@ -125,7 +101,6 @@ int err;
return msgstr;
}
#endif
-#endif
MAIN(argc, argv)
@@ -133,12 +108,6 @@ int argc;
char *argv[];
{
int ret;
-#ifdef VXWORKS
- if(taskVarAdd(0, (int *)&port_data) != OK) {
- fprintf(stderr, "Can't do taskVarAdd in port_test\n");
- exit(1);
- }
-#endif
if((port_data = (PORT_TEST_DATA *) malloc(sizeof(PORT_TEST_DATA))) == NULL) {
fprintf(stderr, "Couldn't malloc for port_data");
exit(1);
@@ -511,9 +480,6 @@ dump(buf, sz, max)
static void
delay(unsigned ms)
{
-#ifdef VXWORKS
- taskDelay((sysClkRateGet() * ms) / 1000);
-#else
#ifdef __WIN32__
Sleep(ms);
#else
@@ -523,7 +489,6 @@ delay(unsigned ms)
select(0, NULL, NULL, NULL, &t);
#endif
-#endif
}
/*
diff --git a/erts/emulator/test/port_SUITE_data/reclaim.h b/erts/emulator/test/port_SUITE_data/reclaim.h
deleted file mode 100644
index 1d57dc5b8a..0000000000
--- a/erts/emulator/test/port_SUITE_data/reclaim.h
+++ /dev/null
@@ -1,60 +0,0 @@
-#ifndef __RECLAIM_H__
-#define __RECLAIM_H__
-
-
-/* The Erlang release for VxWorks includes a simple mechanism for
- "resource reclamation" at task exit - it allows replacement of the
- functions that open/close "files" and malloc/free memory with versions
- that keep track, to be able to "reclaim" file descriptors and memory
- when a task exits (regardless of *how* it exits).
-
- The interface to this mechanism is made available via this file,
- with the following caveats:
-
- - The interface may change (or perhaps even be removed, though that
- isn't likely until VxWorks itself provides similar functionality)
- in future releases - i.e. you must always use the version of this
- file that comes with the Erlang release you are using.
-
- - Disaster is guaranteed if you use the mechanism incorrectly (see
- below for the correct way), e.g. allocate memory with the "tracking"
- version of malloc() and free it with the "standard" version of free().
-
- - The mechanism (of course) incurs some performance penalty - thus
- for a simple program you may be better off with careful programming,
- making sure that you do whatever close()/free()/etc calls that are
- appropriate at all exit points (though if you need to guard against
- taskDelete() etc, things get messy...).
-
- To use the mechanism, simply program your application normally, i.e.
- use open()/close()/malloc()/free() etc as usual, but #include this
- file before any usage of the relevant functions. NOTE: To avoid the
- "disaster" mentioned above, you *must* #include it in *all* (or none)
- of the files that manipulate a particular file descriptor, allocated
- memory area, etc. Finally, note that you can obviously not load your
- application before the Erlang system when using this interface.
-*/
-
-/* Sorry, no ANSI prototypes yet... */
-extern int save_open(),save_creat(),save_socket(),save_accept(),save_close();
-#define open save_open
-#define creat save_creat
-#define socket save_socket
-#define accept save_accept
-#define close save_close
-extern FILE *save_fopen(), *save_fdopen(), *save_freopen();
-extern int save_fclose();
-#define fopen save_fopen
-#define fdopen save_fdopen
-#define freopen save_freopen
-#define fclose save_fclose
-/* XXX Should do opendir/closedir too... */
-extern char *save_malloc(), *save_calloc(), *save_realloc();
-extern void save_free(), save_cfree();
-#define malloc save_malloc
-#define calloc save_calloc
-#define realloc save_realloc
-#define free save_free
-#define cfree save_cfree
-
-#endif
diff --git a/erts/emulator/test/port_bif_SUITE.erl b/erts/emulator/test/port_bif_SUITE.erl
index 8feea87d7e..f439867e9c 100644
--- a/erts/emulator/test/port_bif_SUITE.erl
+++ b/erts/emulator/test/port_bif_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/erts/emulator/test/port_bif_SUITE_data/port_test.c b/erts/emulator/test/port_bif_SUITE_data/port_test.c
index c6b128df66..28324a56a6 100644
--- a/erts/emulator/test/port_bif_SUITE_data/port_test.c
+++ b/erts/emulator/test/port_bif_SUITE_data/port_test.c
@@ -3,15 +3,6 @@
* Purpose: A port program to be used for testing the open_port bif.
*/
-#ifdef VXWORKS
-#include <vxWorks.h>
-#include <taskVarLib.h>
-#include <taskLib.h>
-#include <sysLib.h>
-#include <string.h>
-#include <ioLib.h>
-#endif
-
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -23,12 +14,7 @@
#ifndef __WIN32__
#include <unistd.h>
-#ifdef VXWORKS
-#include "reclaim.h"
-#include <sys/times.h>
-#else
#include <sys/time.h>
-#endif
#define O_BINARY 0
#define _setmode(fd, mode)
@@ -40,22 +26,13 @@
#endif
-#ifdef VXWORKS
-#define REDIR_STDOUT(fd) ioTaskStdSet(0, 1, fd);
-#else
#define REDIR_STDOUT(fd) if (dup2(fd, 1) == -1) { \
fprintf(stderr, "%s: failed to duplicate handle %d to 1: %d\n", \
port_data->progname, fd, errno); \
exit(1); \
}
-#endif
-#ifdef VXWORKS
-#define MAIN(argc, argv) port_test(argc, argv)
-#else
#define MAIN(argc, argv) main(argc, argv)
-#endif
-
extern int errno;
@@ -101,7 +78,6 @@ static void dump(unsigned char* buf, int sz, int max);
static void replace_stdout(char* filename);
static void generate_reply(char* spec);
-#ifndef VXWORKS
#ifndef HAVE_STRERROR
extern int sys_nerr;
#ifndef sys_errlist /* sys_errlist is sometimes defined to
@@ -125,20 +101,13 @@ int err;
return msgstr;
}
#endif
-#endif
-
MAIN(argc, argv)
int argc;
char *argv[];
{
int ret;
-#ifdef VXWORKS
- if(taskVarAdd(0, (int *)&port_data) != OK) {
- fprintf(stderr, "Can't do taskVarAdd in port_test\n");
- exit(1);
- }
-#endif
+
if((port_data = (PORT_TEST_DATA *) malloc(sizeof(PORT_TEST_DATA))) == NULL) {
fprintf(stderr, "Couldn't malloc for port_data");
exit(1);
@@ -508,9 +477,6 @@ dump(buf, sz, max)
static void
delay(unsigned ms)
{
-#ifdef VXWORKS
- taskDelay((sysClkRateGet() * ms) / 1000);
-#else
#ifdef __WIN32__
Sleep(ms);
#else
@@ -520,7 +486,6 @@ delay(unsigned ms)
select(0, NULL, NULL, NULL, &t);
#endif
-#endif
}
/*
diff --git a/erts/emulator/test/port_bif_SUITE_data/reclaim.h b/erts/emulator/test/port_bif_SUITE_data/reclaim.h
deleted file mode 100644
index 1d57dc5b8a..0000000000
--- a/erts/emulator/test/port_bif_SUITE_data/reclaim.h
+++ /dev/null
@@ -1,60 +0,0 @@
-#ifndef __RECLAIM_H__
-#define __RECLAIM_H__
-
-
-/* The Erlang release for VxWorks includes a simple mechanism for
- "resource reclamation" at task exit - it allows replacement of the
- functions that open/close "files" and malloc/free memory with versions
- that keep track, to be able to "reclaim" file descriptors and memory
- when a task exits (regardless of *how* it exits).
-
- The interface to this mechanism is made available via this file,
- with the following caveats:
-
- - The interface may change (or perhaps even be removed, though that
- isn't likely until VxWorks itself provides similar functionality)
- in future releases - i.e. you must always use the version of this
- file that comes with the Erlang release you are using.
-
- - Disaster is guaranteed if you use the mechanism incorrectly (see
- below for the correct way), e.g. allocate memory with the "tracking"
- version of malloc() and free it with the "standard" version of free().
-
- - The mechanism (of course) incurs some performance penalty - thus
- for a simple program you may be better off with careful programming,
- making sure that you do whatever close()/free()/etc calls that are
- appropriate at all exit points (though if you need to guard against
- taskDelete() etc, things get messy...).
-
- To use the mechanism, simply program your application normally, i.e.
- use open()/close()/malloc()/free() etc as usual, but #include this
- file before any usage of the relevant functions. NOTE: To avoid the
- "disaster" mentioned above, you *must* #include it in *all* (or none)
- of the files that manipulate a particular file descriptor, allocated
- memory area, etc. Finally, note that you can obviously not load your
- application before the Erlang system when using this interface.
-*/
-
-/* Sorry, no ANSI prototypes yet... */
-extern int save_open(),save_creat(),save_socket(),save_accept(),save_close();
-#define open save_open
-#define creat save_creat
-#define socket save_socket
-#define accept save_accept
-#define close save_close
-extern FILE *save_fopen(), *save_fdopen(), *save_freopen();
-extern int save_fclose();
-#define fopen save_fopen
-#define fdopen save_fdopen
-#define freopen save_freopen
-#define fclose save_fclose
-/* XXX Should do opendir/closedir too... */
-extern char *save_malloc(), *save_calloc(), *save_realloc();
-extern void save_free(), save_cfree();
-#define malloc save_malloc
-#define calloc save_calloc
-#define realloc save_realloc
-#define free save_free
-#define cfree save_cfree
-
-#endif
diff --git a/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl
index fdc55a4cc5..11dd88413f 100644
--- a/erts/emulator/test/process_SUITE.erl
+++ b/erts/emulator/test/process_SUITE.erl
@@ -118,15 +118,15 @@ fun_spawn(Fun) ->
%% (unclear if this test case will actually prove anything on
%% a modern computer with lots of memory).
spawn_with_binaries(Config) when is_list(Config) ->
- ?line L = lists:duplicate(2048, 42),
- ?line TwoMeg = lists:duplicate(1024, L),
- ?line Fun = fun() -> spawn(?MODULE, binary_owner, [list_to_binary(TwoMeg)]),
+ L = lists:duplicate(2048, 42),
+ TwoMeg = lists:duplicate(1024, L),
+ Fun = fun() -> spawn(?MODULE, binary_owner, [list_to_binary(TwoMeg)]),
receive after 1 -> ok end end,
- ?line Iter = case test_server:purify_is_running() of
+ Iter = case test_server:purify_is_running() of
true -> 10;
false -> 150
end,
- ?line test_server:do_times(Iter, Fun),
+ test_server:do_times(Iter, Fun),
ok.
binary_owner(Bin) when is_binary(Bin) ->
@@ -134,87 +134,87 @@ binary_owner(Bin) when is_binary(Bin) ->
%% Tests exit/1 with a big message.
t_exit_1(Config) when is_list(Config) ->
- ?line start_spawner(),
- ?line Dog = test_server:timetrap(test_server:seconds(20)),
- ?line process_flag(trap_exit, true),
- ?line test_server:do_times(10, fun t_exit_1/0),
- ?line test_server:timetrap_cancel(Dog),
- ?line stop_spawner(),
+ start_spawner(),
+ Dog = test_server:timetrap(test_server:seconds(20)),
+ process_flag(trap_exit, true),
+ test_server:do_times(10, fun t_exit_1/0),
+ test_server:timetrap_cancel(Dog),
+ stop_spawner(),
ok.
t_exit_1() ->
- ?line Pid = fun_spawn(fun() -> exit(kb_128()) end),
- ?line Garbage = kb_128(),
- ?line receive
+ Pid = fun_spawn(fun() -> exit(kb_128()) end),
+ Garbage = kb_128(),
+ receive
{'EXIT', Pid, Garbage} -> ok
end.
%% Tests exit/2 with a lot of data in the exit message.
t_exit_2_other(Config) when is_list(Config) ->
- ?line start_spawner(),
- ?line Dog = test_server:timetrap(test_server:seconds(20)),
- ?line process_flag(trap_exit, true),
- ?line test_server:do_times(10, fun t_exit_2_other/0),
- ?line test_server:timetrap_cancel(Dog),
- ?line stop_spawner(),
+ start_spawner(),
+ Dog = test_server:timetrap(test_server:seconds(20)),
+ process_flag(trap_exit, true),
+ test_server:do_times(10, fun t_exit_2_other/0),
+ test_server:timetrap_cancel(Dog),
+ stop_spawner(),
ok.
t_exit_2_other() ->
- ?line Pid = fun_spawn(fun() -> receive x -> ok end end),
- ?line Garbage = kb_128(),
- ?line exit(Pid, Garbage),
- ?line receive
+ Pid = fun_spawn(fun() -> receive x -> ok end end),
+ Garbage = kb_128(),
+ exit(Pid, Garbage),
+ receive
{'EXIT', Pid, Garbage} -> ok
end.
%% Tests that exit(Pid, normal) does not kill another process.;
t_exit_2_other_normal(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(20)),
- ?line process_flag(trap_exit, true),
- ?line Pid = fun_spawn(fun() -> receive x -> ok end end),
- ?line exit(Pid, normal),
- ?line receive
+ Dog = test_server:timetrap(test_server:seconds(20)),
+ process_flag(trap_exit, true),
+ Pid = fun_spawn(fun() -> receive x -> ok end end),
+ exit(Pid, normal),
+ receive
{'EXIT', Pid, Reason} ->
- ?line test_server:fail({process_died, Reason})
+ test_server:fail({process_died, Reason})
after 1000 ->
ok
end,
- ?line case process_info(Pid) of
+ case process_info(Pid) of
undefined ->
test_server:fail(process_died_on_normal);
List when is_list(List) ->
ok
end,
exit(Pid, kill),
- ?line test_server:timetrap_cancel(Dog),
+ test_server:timetrap_cancel(Dog),
ok.
%% Tests that we can trap an exit message sent with exit/2 from
%% the same process.
self_exit(Config) when is_list(Config) ->
- ?line start_spawner(),
- ?line Dog = test_server:timetrap(test_server:seconds(10)),
- ?line process_flag(trap_exit, true),
- ?line test_server:do_times(200, fun self_exit/0),
- ?line test_server:timetrap_cancel(Dog),
- ?line stop_spawner(),
+ start_spawner(),
+ Dog = test_server:timetrap(test_server:seconds(10)),
+ process_flag(trap_exit, true),
+ test_server:do_times(200, fun self_exit/0),
+ test_server:timetrap_cancel(Dog),
+ stop_spawner(),
ok.
self_exit() ->
- ?line Garbage = eight_kb(),
- ?line P = self(),
- ?line true = exit(P, Garbage),
- ?line receive
+ Garbage = eight_kb(),
+ P = self(),
+ true = exit(P, Garbage),
+ receive
{'EXIT', P, Garbage} -> ok
end.
%% Tests exit(self(), normal) is equivalent to exit(normal) for a process
%% that doesn't trap exits.
normal_suicide_exit(Config) when is_list(Config) ->
- ?line process_flag(trap_exit, true),
- ?line Pid = fun_spawn(fun() -> exit(self(), normal) end),
- ?line receive
+ process_flag(trap_exit, true),
+ Pid = fun_spawn(fun() -> exit(self(), normal) end),
+ receive
{'EXIT', Pid, normal} -> ok;
Other -> test_server:fail({bad_message, Other})
end.
@@ -222,19 +222,19 @@ normal_suicide_exit(Config) when is_list(Config) ->
%% Tests exit(self(), Term) is equivalent to exit(Term) for a process
%% that doesn't trap exits.";
abnormal_suicide_exit(Config) when is_list(Config) ->
- ?line Garbage = eight_kb(),
- ?line process_flag(trap_exit, true),
- ?line Pid = fun_spawn(fun() -> exit(self(), Garbage) end),
- ?line receive
+ Garbage = eight_kb(),
+ process_flag(trap_exit, true),
+ Pid = fun_spawn(fun() -> exit(self(), Garbage) end),
+ receive
{'EXIT', Pid, Garbage} -> ok;
Other -> test_server:fail({bad_message, Other})
end.
%% Tests that exit(self(), die) cannot be catched.
t_exit_2_catch(Config) when is_list(Config) ->
- ?line process_flag(trap_exit, true),
- ?line Pid = fun_spawn(fun() -> catch exit(self(), die) end),
- ?line receive
+ process_flag(trap_exit, true),
+ Pid = fun_spawn(fun() -> catch exit(self(), die) end),
+ receive
{'EXIT', Pid, normal} ->
test_server:fail(catch_worked);
{'EXIT', Pid, die} ->
@@ -246,29 +246,29 @@ t_exit_2_catch(Config) when is_list(Config) ->
%% Tests trapping of an 'EXIT' message generated by a bad argument to
%% the abs/1 bif. The 'EXIT' message will intentionally be very big.
trap_exit_badarg(Config) when is_list(Config) ->
- ?line start_spawner(),
- ?line Dog = test_server:timetrap(test_server:seconds(10)),
- ?line process_flag(trap_exit, true),
- ?line test_server:do_times(10, fun trap_exit_badarg/0),
- ?line test_server:timetrap_cancel(Dog),
- ?line stop_spawner(),
+ start_spawner(),
+ Dog = test_server:timetrap(test_server:seconds(10)),
+ process_flag(trap_exit, true),
+ test_server:do_times(10, fun trap_exit_badarg/0),
+ test_server:timetrap_cancel(Dog),
+ stop_spawner(),
ok.
trap_exit_badarg() ->
- ?line Pid = fun_spawn(fun() -> bad_guy(kb_128()) end),
- ?line Garbage = kb_128(),
- ?line receive
+ Pid = fun_spawn(fun() -> bad_guy(kb_128()) end),
+ Garbage = kb_128(),
+ receive
{'EXIT',Pid,{badarg,[{erlang,abs,[Garbage],Loc1},
{?MODULE,bad_guy,1,Loc2}|_]}}
when is_list(Loc1), is_list(Loc2) ->
ok;
Other ->
- ?line ok = io:format("Bad EXIT message: ~P", [Other, 30]),
- ?line test_server:fail(bad_exit_message)
+ ok = io:format("Bad EXIT message: ~P", [Other, 30]),
+ test_server:fail(bad_exit_message)
end.
bad_guy(Arg) ->
- ?line abs(Arg).
+ abs(Arg).
kb_128() ->
@@ -280,20 +280,12 @@ kb_128() ->
big_binary()}.
eight_kb() ->
- %%% This is really much more than eight kb, so vxworks platforms
- %%% gets away with 1/8 of the other platforms (due to limited
- %%% memory resources).
- B64 = case os:type() of
- vxworks ->
- ?line lists:seq(1, 8);
- _ ->
- ?line lists:seq(1, 64)
- end,
- ?line B512 = {<<1>>,B64,<<2,3>>,B64,make_unaligned_sub_binary(<<4,5,6,7,8,9>>),
+ B64 = lists:seq(1, 64),
+ B512 = {<<1>>,B64,<<2,3>>,B64,make_unaligned_sub_binary(<<4,5,6,7,8,9>>),
B64,make_sub_binary([1,2,3,4,5,6]),
B64,make_sub_binary(lists:seq(1, ?heap_binary_size+1)),
B64,B64,B64,B64,big_binary()},
- ?line lists:duplicate(8, {B512,B512}).
+ lists:duplicate(8, {B512,B512}).
big_binary() ->
big_binary(10, [42]).
@@ -304,19 +296,19 @@ big_binary(N, Acc) ->
%% Test receiving an EXIT message when spawning a BIF with bad arguments.
trap_exit_badarg_in_bif(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(10)),
- ?line process_flag(trap_exit, true),
- ?line test_server:do_times(10, fun trap_exit_badarg_bif/0),
- ?line test_server:timetrap_cancel(Dog),
+ Dog = test_server:timetrap(test_server:seconds(10)),
+ process_flag(trap_exit, true),
+ test_server:do_times(10, fun trap_exit_badarg_bif/0),
+ test_server:timetrap_cancel(Dog),
ok.
trap_exit_badarg_bif() ->
- ?line Pid = spawn_link(erlang, node, [1]),
- ?line receive
+ Pid = spawn_link(erlang, node, [1]),
+ receive
{'EXIT', Pid, {badarg, _}} ->
ok;
Other ->
- ?line test_server:fail({unexpected, Other})
+ test_server:fail({unexpected, Other})
end.
%% The following sequences of events have crasched Beam.
@@ -329,27 +321,27 @@ trap_exit_badarg_bif() ->
%% 3) The process will crash the next time it executes 'receive'.
exit_and_timeout(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(20)),
+ Dog = test_server:timetrap(test_server:seconds(20)),
- ?line process_flag(trap_exit, true),
- ?line Parent = self(),
- ?line Low = fun_spawn(fun() -> eat_low(Parent) end),
- ?line High = fun_spawn(fun() -> eat_high(Low) end),
- ?line eat_wait_for(Low, High),
+ process_flag(trap_exit, true),
+ Parent = self(),
+ Low = fun_spawn(fun() -> eat_low(Parent) end),
+ High = fun_spawn(fun() -> eat_high(Low) end),
+ eat_wait_for(Low, High),
- ?line test_server:timetrap_cancel(Dog),
+ test_server:timetrap_cancel(Dog),
ok.
eat_wait_for(Low, High) ->
- ?line receive
- {'EXIT', Low, {you, are, dead}} ->
- ok;
- {'EXIT', High, normal} ->
- eat_wait_for(Low, High);
- Other ->
- test_server:fail({bad_message, Other})
- end.
+ receive
+ {'EXIT', Low, {you, are, dead}} ->
+ ok;
+ {'EXIT', High, normal} ->
+ eat_wait_for(Low, High);
+ Other ->
+ test_server:fail({bad_message, Other})
+ end.
eat_low(_Parent) ->
receive
@@ -382,27 +374,27 @@ loop(_, _) ->
%% Tries to send two different exit messages to a process.
%% (The second one should be ignored.)
exit_twice(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(20)),
+ Dog = test_server:timetrap(test_server:seconds(20)),
- ?line process_flag(trap_exit, true),
- ?line Low = fun_spawn(fun etwice_low/0),
- ?line High = fun_spawn(fun() -> etwice_high(Low) end),
- ?line etwice_wait_for(Low, High),
+ process_flag(trap_exit, true),
+ Low = fun_spawn(fun etwice_low/0),
+ High = fun_spawn(fun() -> etwice_high(Low) end),
+ etwice_wait_for(Low, High),
- ?line test_server:timetrap_cancel(Dog),
+ test_server:timetrap_cancel(Dog),
ok.
etwice_wait_for(Low, High) ->
- ?line receive
- {'EXIT', Low, first} ->
- ok;
- {'EXIT', Low, Other} ->
- test_server:fail({wrong_exit_reason, Other});
- {'EXIT', High, normal} ->
- etwice_wait_for(Low, High);
- Other ->
- test_server:fail({bad_message, Other})
- end.
+ receive
+ {'EXIT', Low, first} ->
+ ok;
+ {'EXIT', Low, Other} ->
+ test_server:fail({wrong_exit_reason, Other});
+ {'EXIT', High, normal} ->
+ etwice_wait_for(Low, High);
+ Other ->
+ test_server:fail({bad_message, Other})
+ end.
etwice_low() ->
etwice_low().
@@ -414,15 +406,15 @@ etwice_high(Low) ->
%% Tests the process_info/2 BIF.
t_process_info(Config) when is_list(Config) ->
- ?line [] = process_info(self(), registered_name),
- ?line register(my_name, self()),
- ?line {registered_name, my_name} = process_info(self(), registered_name),
- ?line {status, running} = process_info(self(), status),
- ?line {min_heap_size, 233} = process_info(self(), min_heap_size),
- ?line {min_bin_vheap_size, 46368} = process_info(self(), min_bin_vheap_size),
- ?line {current_function,{?MODULE,t_process_info,1}} =
+ [] = process_info(self(), registered_name),
+ register(my_name, self()),
+ {registered_name, my_name} = process_info(self(), registered_name),
+ {status, running} = process_info(self(), status),
+ {min_heap_size, 233} = process_info(self(), min_heap_size),
+ {min_bin_vheap_size,46422} = process_info(self(), min_bin_vheap_size),
+ {current_function,{?MODULE,t_process_info,1}} =
process_info(self(), current_function),
- ?line {current_function,{?MODULE,t_process_info,1}} =
+ {current_function,{?MODULE,t_process_info,1}} =
apply(erlang, process_info, [self(),current_function]),
%% current_location and current_stacktrace
@@ -433,9 +425,9 @@ t_process_info(Config) when is_list(Config) ->
verify_loc(Line2, Res2),
pi_stacktrace([{?MODULE,t_process_info,1,?LINE}]),
- ?line Gleader = group_leader(),
- ?line {group_leader, Gleader} = process_info(self(), group_leader),
- ?line {'EXIT',{badarg,_Info}} = (catch process_info('not_a_pid')),
+ Gleader = group_leader(),
+ {group_leader, Gleader} = process_info(self(), group_leader),
+ {'EXIT',{badarg,_Info}} = (catch process_info('not_a_pid')),
ok.
pi_stacktrace(Expected0) ->
@@ -517,50 +509,50 @@ process_info_looper(Parent) ->
%% Tests the process_info/1 BIF on another process with messages.
process_info_other_msg(Config) when is_list(Config) ->
Self = self(),
- ?line Pid = spawn_link(fun() -> other_process(Self) end),
+ Pid = spawn_link(fun() -> other_process(Self) end),
receive
{go_ahead,Pid} -> ok
end,
- ?line Own = {my,own,message},
+ Own = {my,own,message},
- ?line {messages,[Own]} = process_info(Pid, messages),
+ {messages,[Own]} = process_info(Pid, messages),
- ?line Garbage = kb_128(),
- ?line MsgA = {a,Garbage},
- ?line MsgB = {b,Garbage},
- ?line MsgC = {c,Garbage},
- ?line MsgD = {d,Garbage},
- ?line MsgE = {e,Garbage},
-
- ?line Pid ! MsgA,
- ?line {messages,[Own,MsgA]} = process_info(Pid, messages),
- ?line Pid ! MsgB,
- ?line {messages,[Own,MsgA,MsgB]} = process_info(Pid, messages),
- ?line Pid ! MsgC,
- ?line {messages,[Own,MsgA,MsgB,MsgC]} = process_info(Pid, messages),
- ?line Pid ! MsgD,
- ?line {messages,[Own,MsgA,MsgB,MsgC,MsgD]} = process_info(Pid, messages),
- ?line Pid ! MsgE,
- ?line {messages,[Own,MsgA,MsgB,MsgC,MsgD,MsgE]=All} = process_info(Pid, messages),
- ?line {memory,BytesOther} = process_info(Pid, memory),
- ?line {memory,BytesSelf} = process_info(self(), memory),
+ Garbage = kb_128(),
+ MsgA = {a,Garbage},
+ MsgB = {b,Garbage},
+ MsgC = {c,Garbage},
+ MsgD = {d,Garbage},
+ MsgE = {e,Garbage},
+
+ Pid ! MsgA,
+ {messages,[Own,MsgA]} = process_info(Pid, messages),
+ Pid ! MsgB,
+ {messages,[Own,MsgA,MsgB]} = process_info(Pid, messages),
+ Pid ! MsgC,
+ {messages,[Own,MsgA,MsgB,MsgC]} = process_info(Pid, messages),
+ Pid ! MsgD,
+ {messages,[Own,MsgA,MsgB,MsgC,MsgD]} = process_info(Pid, messages),
+ Pid ! MsgE,
+ {messages,[Own,MsgA,MsgB,MsgC,MsgD,MsgE]=All} = process_info(Pid, messages),
+ {memory,BytesOther} = process_info(Pid, memory),
+ {memory,BytesSelf} = process_info(self(), memory),
io:format("Memory ~p: ~p\n", [Pid,BytesOther]),
io:format("Memory ~p (self): ~p\n", [self(),BytesSelf]),
[Own,MsgA,MsgB,MsgC,MsgD,MsgE] = All,
- ?line Pid ! {self(),empty},
- ?line receive
+ Pid ! {self(),empty},
+ receive
empty -> ok
end,
- ?line {messages,[]} = process_info(Pid, messages),
+ {messages,[]} = process_info(Pid, messages),
- ?line {min_heap_size, 233} = process_info(Pid, min_heap_size),
- ?line {min_bin_vheap_size, 46368} = process_info(Pid, min_bin_vheap_size),
+ {min_heap_size, 233} = process_info(Pid, min_heap_size),
+ {min_bin_vheap_size, 46422} = process_info(Pid, min_bin_vheap_size),
- ?line Pid ! stop,
+ Pid ! stop,
ok.
process_info_other_dist_msg(Config) when is_list(Config) ->
@@ -568,52 +560,51 @@ process_info_other_dist_msg(Config) when is_list(Config) ->
%% Check that process_info can handle messages that have not been
%% decoded yet.
%%
- ?line {ok, Node} = start_node(Config),
- ?line Self = self(),
- ?line Pid = spawn_link(fun() -> other_process(Self) end),
- ?line receive {go_ahead,Pid} -> ok end,
+ {ok, Node} = start_node(Config),
+ Self = self(),
+ Pid = spawn_link(fun() -> other_process(Self) end),
+ receive {go_ahead,Pid} -> ok end,
- ?line Own = {my,own,message},
+ Own = {my,own,message},
- ?line {messages,[Own]} = process_info(Pid, messages),
- ?line Garbage = kb_128(),
- ?line MsgA = {a,self(),Garbage},
- ?line MsgB = {b,self(),Garbage},
- ?line MsgC = {c,self(),Garbage},
- ?line MsgD = {d,self(),Garbage},
- ?line MsgE = {e,self(),Garbage},
+ {messages,[Own]} = process_info(Pid, messages),
+ Garbage = kb_128(),
+ MsgA = {a,self(),Garbage},
+ MsgB = {b,self(),Garbage},
+ MsgC = {c,self(),Garbage},
+ MsgD = {d,self(),Garbage},
+ MsgE = {e,self(),Garbage},
%% We don't want the other process to decode messages itself
%% therefore we suspend it.
- ?line true = erlang:suspend_process(Pid),
- ?line spawn_link(Node, fun () ->
- Pid ! MsgA,
- Pid ! MsgB,
- Pid ! MsgC,
- Self ! check_abc
- end),
- ?line receive check_abc -> ok end,
- ?line [{status,suspended},
- {messages,[Own,MsgA,MsgB,MsgC]},
- {status,suspended}]= process_info(Pid, [status,messages,status]),
- ?line spawn_link(Node, fun () ->
- Pid ! MsgD,
- Pid ! MsgE,
- Self ! check_de
- end),
- ?line receive check_de -> ok end,
- ?line {messages,[Own,MsgA,MsgB,MsgC,MsgD,MsgE]=All}
- = process_info(Pid, messages),
- ?line true = erlang:resume_process(Pid),
- ?line Pid ! {self(), get_all_messages},
- ?line receive
+ true = erlang:suspend_process(Pid),
+ spawn_link(Node, fun () ->
+ Pid ! MsgA,
+ Pid ! MsgB,
+ Pid ! MsgC,
+ Self ! check_abc
+ end),
+ receive check_abc -> ok end,
+ [{status,suspended},
+ {messages,[Own,MsgA,MsgB,MsgC]},
+ {status,suspended}]= process_info(Pid, [status,messages,status]),
+ spawn_link(Node, fun () ->
+ Pid ! MsgD,
+ Pid ! MsgE,
+ Self ! check_de
+ end),
+ receive check_de -> ok end,
+ {messages,[Own,MsgA,MsgB,MsgC,MsgD,MsgE]=All} = process_info(Pid, messages),
+ true = erlang:resume_process(Pid),
+ Pid ! {self(), get_all_messages},
+ receive
{all_messages, AllMsgs} ->
- ?line All = AllMsgs
+ All = AllMsgs
end,
- ?line {messages,[]} = process_info(Pid, messages),
- ?line Pid ! stop,
- ?line stop_node(Node),
- ?line ok.
+ {messages,[]} = process_info(Pid, messages),
+ Pid ! stop,
+ stop_node(Node),
+ ok.
other_process(Parent) ->
@@ -660,38 +651,36 @@ process_info_2_list(doc) ->
process_info_2_list(suite) ->
[];
process_info_2_list(Config) when is_list(Config) ->
- ?line Proc = spawn(fun () ->
- receive after infinity -> ok end end),
+ Proc = spawn(fun () -> receive after infinity -> ok end end),
register(process_SUITE_process_info_2_list1, self()),
register(process_SUITE_process_info_2_list2, Proc),
- ?line erts_debug:set_internal_state(available_internal_state,true),
- ?line AllArgs = erts_debug:get_internal_state(process_info_args),
- ?line A1 = lists:sort(AllArgs) ++ [status] ++ lists:reverse(AllArgs),
+ erts_debug:set_internal_state(available_internal_state,true),
+ AllArgs = erts_debug:get_internal_state(process_info_args),
+ A1 = lists:sort(AllArgs) ++ [status] ++ lists:reverse(AllArgs),
%% Verify that argument is accepted as single atom
- ?line lists:foreach(fun (A) ->
- ?line {A, _} = process_info(Proc, A),
- ?line {A, _} = process_info(self(), A)
- end,
- A1),
+ lists:foreach(fun (A) ->
+ {A, _} = process_info(Proc, A),
+ {A, _} = process_info(self(), A)
+ end, A1),
%% Verify that order is preserved
- ?line ok = chk_pi_order(process_info(self(), A1), A1),
- ?line ok = chk_pi_order(process_info(Proc, A1), A1),
+ ok = chk_pi_order(process_info(self(), A1), A1),
+ ok = chk_pi_order(process_info(Proc, A1), A1),
%% Small arg list
- ?line A2 = [status, stack_size, trap_exit, priority],
- ?line [{status, _}, {stack_size, _}, {trap_exit, _}, {priority, _}]
+ A2 = [status, stack_size, trap_exit, priority],
+ [{status, _}, {stack_size, _}, {trap_exit, _}, {priority, _}]
= process_info(Proc, A2),
- ?line [{status, _}, {stack_size, _}, {trap_exit, _}, {priority, _}]
+ [{status, _}, {stack_size, _}, {trap_exit, _}, {priority, _}]
= process_info(self(), A2),
%% Huge arg list (note values are shared)
- ?line A3 = lists:duplicate(5000,backtrace),
- ?line V3 = process_info(Proc, A3),
- ?line 5000 = length(V3),
- ?line lists:foreach(fun ({backtrace, _}) -> ok end, V3),
- ?line ok.
+ A3 = lists:duplicate(5000,backtrace),
+ V3 = process_info(Proc, A3),
+ 5000 = length(V3),
+ lists:foreach(fun ({backtrace, _}) -> ok end, V3),
+ ok.
process_info_lock_reschedule(doc) ->
[];
@@ -700,43 +689,37 @@ process_info_lock_reschedule(suite) ->
process_info_lock_reschedule(Config) when is_list(Config) ->
%% We need a process that is running and an item that requires
%% process_info to take the main process lock.
- ?line Target1 = spawn_link(fun tok_loop/0),
- ?line Name1 = process_info_lock_reschedule_running,
- ?line register(Name1, Target1),
- ?line Target2 = spawn_link(fun () -> receive after infinity -> ok end end),
- ?line Name2 = process_info_lock_reschedule_waiting,
- ?line register(Name2, Target2),
- ?line PI = fun(_) ->
- ?line erlang:yield(),
- ?line [{registered_name, Name1}]
- = process_info(Target1, [registered_name]),
- ?line [{registered_name, Name2}]
- = process_info(Target2, [registered_name]),
- ?line erlang:yield(),
- ?line {registered_name, Name1}
- = process_info(Target1, registered_name),
- ?line {registered_name, Name2}
- = process_info(Target2, registered_name),
- ?line erlang:yield(),
- ?line [{registered_name, Name1}| _]
- = process_info(Target1),
- ?line [{registered_name, Name2}| _]
- = process_info(Target2)
- end,
- ?line lists:foreach(PI, lists:seq(1,1000)),
+ Target1 = spawn_link(fun tok_loop/0),
+ Name1 = process_info_lock_reschedule_running,
+ register(Name1, Target1),
+ Target2 = spawn_link(fun () -> receive after infinity -> ok end end),
+ Name2 = process_info_lock_reschedule_waiting,
+ register(Name2, Target2),
+ PI = fun(_) ->
+ erlang:yield(),
+ [{registered_name, Name1}] = process_info(Target1, [registered_name]),
+ [{registered_name, Name2}] = process_info(Target2, [registered_name]),
+ erlang:yield(),
+ {registered_name, Name1} = process_info(Target1, registered_name),
+ {registered_name, Name2} = process_info(Target2, registered_name),
+ erlang:yield(),
+ [{registered_name, Name1}| _] = process_info(Target1),
+ [{registered_name, Name2}| _] = process_info(Target2)
+ end,
+ lists:foreach(PI, lists:seq(1,1000)),
%% Make sure Target1 still is willing to "tok loop"
- ?line case process_info(Target1, status) of
- {status, OkStatus} when OkStatus == runnable;
- OkStatus == running;
- OkStatus == garbage_collecting ->
- ?line unlink(Target1),
- ?line unlink(Target2),
- ?line exit(Target1, bang),
- ?line exit(Target2, bang),
- ?line OkStatus;
- {status, BadStatus} ->
- ?line ?t:fail(BadStatus)
- end.
+ case process_info(Target1, status) of
+ {status, OkStatus} when OkStatus == runnable;
+ OkStatus == running;
+ OkStatus == garbage_collecting ->
+ unlink(Target1),
+ unlink(Target2),
+ exit(Target1, bang),
+ exit(Target2, bang),
+ OkStatus;
+ {status, BadStatus} ->
+ ?t:fail(BadStatus)
+ end.
pi_loop(_Name, _Pid, 0) ->
ok;
@@ -749,50 +732,50 @@ process_info_lock_reschedule2(doc) ->
process_info_lock_reschedule2(suite) ->
[];
process_info_lock_reschedule2(Config) when is_list(Config) ->
- ?line Parent = self(),
- ?line Fun = fun () ->
- receive {go, Name, Pid} -> ok end,
- pi_loop(Name, Pid, 10000),
- Parent ! {done, self()},
- receive after infinity -> ok end
- end,
- ?line P1 = spawn_link(Fun),
- ?line N1 = process_info_lock_reschedule2_1,
- ?line true = register(N1, P1),
- ?line P2 = spawn_link(Fun),
- ?line N2 = process_info_lock_reschedule2_2,
- ?line true = register(N2, P2),
- ?line P3 = spawn_link(Fun),
- ?line N3 = process_info_lock_reschedule2_3,
- ?line true = register(N3, P3),
- ?line P4 = spawn_link(Fun),
- ?line N4 = process_info_lock_reschedule2_4,
- ?line true = register(N4, P4),
- ?line P5 = spawn_link(Fun),
- ?line N5 = process_info_lock_reschedule2_5,
- ?line true = register(N5, P5),
- ?line P6 = spawn_link(Fun),
- ?line N6 = process_info_lock_reschedule2_6,
- ?line true = register(N6, P6),
- ?line P1 ! {go, N2, P2},
- ?line P2 ! {go, N1, P1},
- ?line P3 ! {go, N1, P1},
- ?line P4 ! {go, N1, P1},
- ?line P5 ! {go, N6, P6},
- ?line P6 ! {go, N5, P5},
- ?line receive {done, P1} -> ok end,
- ?line receive {done, P2} -> ok end,
- ?line receive {done, P3} -> ok end,
- ?line receive {done, P4} -> ok end,
- ?line receive {done, P5} -> ok end,
- ?line receive {done, P6} -> ok end,
- ?line unlink(P1), exit(P1, bang),
- ?line unlink(P2), exit(P2, bang),
- ?line unlink(P3), exit(P3, bang),
- ?line unlink(P4), exit(P4, bang),
- ?line unlink(P5), exit(P5, bang),
- ?line unlink(P6), exit(P6, bang),
- ?line ok.
+ Parent = self(),
+ Fun = fun () ->
+ receive {go, Name, Pid} -> ok end,
+ pi_loop(Name, Pid, 10000),
+ Parent ! {done, self()},
+ receive after infinity -> ok end
+ end,
+ P1 = spawn_link(Fun),
+ N1 = process_info_lock_reschedule2_1,
+ true = register(N1, P1),
+ P2 = spawn_link(Fun),
+ N2 = process_info_lock_reschedule2_2,
+ true = register(N2, P2),
+ P3 = spawn_link(Fun),
+ N3 = process_info_lock_reschedule2_3,
+ true = register(N3, P3),
+ P4 = spawn_link(Fun),
+ N4 = process_info_lock_reschedule2_4,
+ true = register(N4, P4),
+ P5 = spawn_link(Fun),
+ N5 = process_info_lock_reschedule2_5,
+ true = register(N5, P5),
+ P6 = spawn_link(Fun),
+ N6 = process_info_lock_reschedule2_6,
+ true = register(N6, P6),
+ P1 ! {go, N2, P2},
+ P2 ! {go, N1, P1},
+ P3 ! {go, N1, P1},
+ P4 ! {go, N1, P1},
+ P5 ! {go, N6, P6},
+ P6 ! {go, N5, P5},
+ receive {done, P1} -> ok end,
+ receive {done, P2} -> ok end,
+ receive {done, P3} -> ok end,
+ receive {done, P4} -> ok end,
+ receive {done, P5} -> ok end,
+ receive {done, P6} -> ok end,
+ unlink(P1), exit(P1, bang),
+ unlink(P2), exit(P2, bang),
+ unlink(P3), exit(P3, bang),
+ unlink(P4), exit(P4, bang),
+ unlink(P5), exit(P5, bang),
+ unlink(P6), exit(P6, bang),
+ ok.
many_args(0,_B,_C,_D,_E,_F,_G,_H,_I,_J) ->
ok;
@@ -810,120 +793,115 @@ process_info_lock_reschedule3(suite) ->
process_info_lock_reschedule3(Config) when is_list(Config) ->
%% We need a process that is running and an item that requires
%% process_info to take the main process lock.
- ?line Target1 = spawn_link(fun tok_loop/0),
- ?line Name1 = process_info_lock_reschedule_running,
- ?line register(Name1, Target1),
- ?line Target2 = spawn_link(fun () -> receive after infinity -> ok end end),
- ?line Name2 = process_info_lock_reschedule_waiting,
- ?line register(Name2, Target2),
- ?line PI = fun(N) ->
- case N rem 10 of
- 0 -> erlang:yield();
- _ -> ok
- end,
- ?line do_pi_msg_len({proc, Target1},
- {arg, message_queue_len})
- end,
- ?line many_args(100000,1,2,3,4,5,6,7,8,9),
- ?line lists:foreach(PI, lists:seq(1,1000000)),
+ Target1 = spawn_link(fun tok_loop/0),
+ Name1 = process_info_lock_reschedule_running,
+ register(Name1, Target1),
+ Target2 = spawn_link(fun () -> receive after infinity -> ok end end),
+ Name2 = process_info_lock_reschedule_waiting,
+ register(Name2, Target2),
+ PI = fun(N) ->
+ case N rem 10 of
+ 0 -> erlang:yield();
+ _ -> ok
+ end,
+ do_pi_msg_len({proc, Target1},
+ {arg, message_queue_len})
+ end,
+ many_args(100000,1,2,3,4,5,6,7,8,9),
+ lists:foreach(PI, lists:seq(1,1000000)),
%% Make sure Target1 still is willing to "tok loop"
- ?line case process_info(Target1, status) of
+ case process_info(Target1, status) of
{status, OkStatus} when OkStatus == runnable;
OkStatus == running;
OkStatus == garbage_collecting ->
- ?line unlink(Target1),
- ?line unlink(Target2),
- ?line exit(Target1, bang),
- ?line exit(Target2, bang),
- ?line OkStatus;
+ unlink(Target1),
+ unlink(Target2),
+ exit(Target1, bang),
+ exit(Target2, bang),
+ OkStatus;
{status, BadStatus} ->
- ?line ?t:fail(BadStatus)
+ ?t:fail(BadStatus)
end.
process_status_exiting(Config) when is_list(Config) ->
%% Make sure that erts_debug:get_internal_state({process_status,P})
%% returns exiting if it is in status P_EXITING.
- ?line erts_debug:set_internal_state(available_internal_state,true),
- ?line Prio = process_flag(priority, max),
- ?line P = spawn_opt(fun () -> receive after infinity -> ok end end,
+ erts_debug:set_internal_state(available_internal_state,true),
+ Prio = process_flag(priority, max),
+ P = spawn_opt(fun () -> receive after infinity -> ok end end,
[{priority, normal}]),
- ?line erlang:yield(),
+ erlang:yield(),
%% The tok_loop processes are here to make it hard for the exiting
%% process to be scheduled in for exit...
- ?line TokLoops = lists:map(fun (_) ->
- spawn_opt(fun tok_loop/0,
- [link,{priority, high}])
- end,
- lists:seq(1, erlang:system_info(schedulers_online))),
- ?line exit(P, boom),
- ?line wait_until(
- fun () ->
- exiting =:= erts_debug:get_internal_state({process_status,P})
- end),
- ?line lists:foreach(fun (Tok) -> unlink(Tok), exit(Tok,bang) end, TokLoops),
- ?line process_flag(priority, Prio),
- ?line ok.
+ TokLoops = lists:map(fun (_) ->
+ spawn_opt(fun tok_loop/0,
+ [link,{priority, high}])
+ end, lists:seq(1, erlang:system_info(schedulers_online))),
+ exit(P, boom),
+ wait_until(fun() ->
+ exiting =:= erts_debug:get_internal_state({process_status,P})
+ end),
+ lists:foreach(fun (Tok) -> unlink(Tok), exit(Tok,bang) end, TokLoops),
+ process_flag(priority, Prio),
+ ok.
otp_4725(Config) when is_list(Config) ->
- ?line Tester = self(),
- ?line Ref1 = make_ref(),
- ?line Pid1 = spawn_opt(fun () ->
- Tester ! {Ref1, process_info(self())},
- receive
- Ref1 -> bye
- end
- end,
- [link,
- {priority, max},
- {fullsweep_after, 600}]),
- ?line receive
- {Ref1, ProcInfo1A} ->
- ?line ProcInfo1B = process_info(Pid1),
- ?line Pid1 ! Ref1,
- ?line check_proc_infos(ProcInfo1A, ProcInfo1B)
- end,
- ?line Ref2 = make_ref(),
- ?line Pid2 = spawn_opt(fun () ->
- Tester ! {Ref2, process_info(self())},
- receive
- Ref2 -> bye
- end
- end,
- []),
- ?line receive
- {Ref2, ProcInfo2A} ->
- ?line ProcInfo2B = process_info(Pid2),
- ?line Pid2 ! Ref2,
- ?line check_proc_infos(ProcInfo2A, ProcInfo2B)
- end,
- ?line ok.
+ Tester = self(),
+ Ref1 = make_ref(),
+ Pid1 = spawn_opt(fun () ->
+ Tester ! {Ref1, process_info(self())},
+ receive
+ Ref1 -> bye
+ end
+ end, [link, {priority, max}, {fullsweep_after, 600}]),
+ receive
+ {Ref1, ProcInfo1A} ->
+ ProcInfo1B = process_info(Pid1),
+ Pid1 ! Ref1,
+ check_proc_infos(ProcInfo1A, ProcInfo1B)
+ end,
+ Ref2 = make_ref(),
+ Pid2 = spawn_opt(fun () ->
+ Tester ! {Ref2, process_info(self())},
+ receive
+ Ref2 -> bye
+ end
+ end,
+ []),
+ receive
+ {Ref2, ProcInfo2A} ->
+ ProcInfo2B = process_info(Pid2),
+ Pid2 ! Ref2,
+ check_proc_infos(ProcInfo2A, ProcInfo2B)
+ end,
+ ok.
check_proc_infos(A, B) ->
- ?line IC = lists:keysearch(initial_call, 1, A),
- ?line IC = lists:keysearch(initial_call, 1, B),
+ IC = lists:keysearch(initial_call, 1, A),
+ IC = lists:keysearch(initial_call, 1, B),
- ?line L = lists:keysearch(links, 1, A),
- ?line L = lists:keysearch(links, 1, B),
+ L = lists:keysearch(links, 1, A),
+ L = lists:keysearch(links, 1, B),
- ?line D = lists:keysearch(dictionary, 1, A),
- ?line D = lists:keysearch(dictionary, 1, B),
+ D = lists:keysearch(dictionary, 1, A),
+ D = lists:keysearch(dictionary, 1, B),
- ?line TE = lists:keysearch(trap_exit, 1, A),
- ?line TE = lists:keysearch(trap_exit, 1, B),
+ TE = lists:keysearch(trap_exit, 1, A),
+ TE = lists:keysearch(trap_exit, 1, B),
- ?line EH = lists:keysearch(error_handler, 1, A),
- ?line EH = lists:keysearch(error_handler, 1, B),
+ EH = lists:keysearch(error_handler, 1, A),
+ EH = lists:keysearch(error_handler, 1, B),
- ?line P = lists:keysearch(priority, 1, A),
- ?line P = lists:keysearch(priority, 1, B),
+ P = lists:keysearch(priority, 1, A),
+ P = lists:keysearch(priority, 1, B),
- ?line GL = lists:keysearch(group_leader, 1, A),
- ?line GL = lists:keysearch(group_leader, 1, B),
+ GL = lists:keysearch(group_leader, 1, A),
+ GL = lists:keysearch(group_leader, 1, B),
- ?line GC = lists:keysearch(garbage_collection, 1, A),
- ?line GC = lists:keysearch(garbage_collection, 1, B),
+ GC = lists:keysearch(garbage_collection, 1, A),
+ GC = lists:keysearch(garbage_collection, 1, B),
- ?line ok.
+ ok.
%% Dummies.
@@ -936,18 +914,18 @@ stop_spawner() ->
%% Tests erlang:bump_reductions/1.
bump_reductions(Config) when is_list(Config) ->
- ?line erlang:garbage_collect(),
- ?line receive after 1 -> ok end, % Clear reductions.
- ?line {reductions,R1} = process_info(self(), reductions),
- ?line true = erlang:bump_reductions(100),
- ?line {reductions,R2} = process_info(self(), reductions),
- ?line case R2-R1 of
+ erlang:garbage_collect(),
+ receive after 1 -> ok end, % Clear reductions.
+ {reductions,R1} = process_info(self(), reductions),
+ true = erlang:bump_reductions(100),
+ {reductions,R2} = process_info(self(), reductions),
+ case R2-R1 of
Diff when Diff < 100 ->
- ?line ok = io:format("R1 = ~w, R2 = ~w", [R1, R2]),
- ?line test_server:fail({small_diff, Diff});
+ ok = io:format("R1 = ~w, R2 = ~w", [R1, R2]),
+ test_server:fail({small_diff, Diff});
Diff when Diff > 110 ->
- ?line ok = io:format("R1 = ~w, R2 = ~w", [R1, R2]),
- ?line test_server:fail({big_diff, Diff});
+ ok = io:format("R1 = ~w, R2 = ~w", [R1, R2]),
+ test_server:fail({big_diff, Diff});
Diff ->
io:format("~p\n", [Diff]),
ok
@@ -957,11 +935,11 @@ bump_reductions(Config) when is_list(Config) ->
bump_big(R2, 16#08000000).
bump_big(Prev, Limit) ->
- ?line true = erlang:bump_reductions(100000), %Limited to CONTEXT_REDUCTIONS.
- ?line case process_info(self(), reductions) of
+ true = erlang:bump_reductions(100000), %Limited to CONTEXT_REDUCTIONS.
+ case process_info(self(), reductions) of
{reductions,Big} when is_integer(Big), Big > Limit ->
- ?line erlang:garbage_collect(),
- ?line io:format("~p\n", [Big]);
+ erlang:garbage_collect(),
+ io:format("~p\n", [Big]);
{reductions,R} when is_integer(R), R > Prev ->
bump_big(R, Limit)
end,
@@ -972,34 +950,34 @@ bump_big(Prev, Limit) ->
low_prio(Config) when is_list(Config) ->
case erlang:system_info(schedulers_online) of
1 ->
- ?line ok = low_prio_test(Config);
+ ok = low_prio_test(Config);
_ ->
- ?line erlang:system_flag(multi_scheduling, block),
- ?line ok = low_prio_test(Config),
- ?line erlang:system_flag(multi_scheduling, unblock),
- ?line {comment,
+ erlang:system_flag(multi_scheduling, block),
+ ok = low_prio_test(Config),
+ erlang:system_flag(multi_scheduling, unblock),
+ {comment,
"Test not written for SMP runtime system. "
"Multi scheduling blocked during test."}
end.
low_prio_test(Config) when is_list(Config) ->
- ?line process_flag(trap_exit, true),
- ?line S = spawn_link(?MODULE, prio_server, [0, 0]),
- ?line PCs = spawn_prio_clients(S, erlang:system_info(schedulers_online)),
- ?line timer:sleep(2000),
- ?line lists:foreach(fun (P) -> exit(P, kill) end, PCs),
- ?line S ! exit,
- ?line receive {'EXIT', S, {A, B}} -> check_prio(A, B) end,
+ process_flag(trap_exit, true),
+ S = spawn_link(?MODULE, prio_server, [0, 0]),
+ PCs = spawn_prio_clients(S, erlang:system_info(schedulers_online)),
+ timer:sleep(2000),
+ lists:foreach(fun (P) -> exit(P, kill) end, PCs),
+ S ! exit,
+ receive {'EXIT', S, {A, B}} -> check_prio(A, B) end,
ok.
check_prio(A, B) ->
- ?line Prop = A/B,
- ?line ok = io:format("Low=~p, High=~p, Prop=~p\n", [A, B, Prop]),
+ Prop = A/B,
+ ok = io:format("Low=~p, High=~p, Prop=~p\n", [A, B, Prop]),
%% It isn't 1/8, it's more like 0.3, but let's check that
%% the low-prio processes get some little chance to run at all.
- ?line true = (Prop < 1.0),
- ?line true = (Prop > 1/32).
+ true = (Prop < 1.0),
+ true = (Prop > 1/32).
prio_server(A, B) ->
receive
@@ -1059,25 +1037,25 @@ yield(Config) when is_list(Config) ->
end.
yield_test() ->
- ?line erlang:garbage_collect(),
- ?line receive after 1 -> ok end, % Clear reductions.
- ?line SC = schedcnt(start),
- ?line {reductions, R1} = process_info(self(), reductions),
- ?line {ok, true} = call_yield(middle),
- ?line true = call_yield(final),
- ?line true = call_yield(),
- ?line true = apply(erlang, yield, []),
- ?line {reductions, R2} = process_info(self(), reductions),
- ?line Schedcnt = schedcnt(stop, SC),
- ?line case {R2-R1, Schedcnt} of
- {Diff, 4} when Diff < 30 ->
- ?line ok = io:format("R1 = ~w, R2 = ~w, Schedcnt = ~w",
- [R1, R2, Schedcnt]);
- {Diff, _} ->
- ?line ok = io:format("R1 = ~w, R2 = ~w, Schedcnt = ~w",
- [R1, R2, Schedcnt]),
- ?line test_server:fail({measurement_error, Diff, Schedcnt})
- end.
+ erlang:garbage_collect(),
+ receive after 1 -> ok end, % Clear reductions.
+ SC = schedcnt(start),
+ {reductions, R1} = process_info(self(), reductions),
+ {ok, true} = call_yield(middle),
+ true = call_yield(final),
+ true = call_yield(),
+ true = apply(erlang, yield, []),
+ {reductions, R2} = process_info(self(), reductions),
+ Schedcnt = schedcnt(stop, SC),
+ case {R2-R1, Schedcnt} of
+ {Diff, 4} when Diff < 30 ->
+ ok = io:format("R1 = ~w, R2 = ~w, Schedcnt = ~w",
+ [R1, R2, Schedcnt]);
+ {Diff, _} ->
+ ok = io:format("R1 = ~w, R2 = ~w, Schedcnt = ~w",
+ [R1, R2, Schedcnt]),
+ test_server:fail({measurement_error, Diff, Schedcnt})
+ end.
call_yield() ->
erlang:yield().
@@ -1116,61 +1094,61 @@ schedcnt(stop, {Ref, Pid}) when is_reference(Ref), is_pid(Pid) ->
yield2(doc) -> [];
yield2(suite) -> [];
yield2(Config) when is_list(Config) ->
- ?line Me = self(),
- ?line Go = make_ref(),
- ?line RedDiff = make_ref(),
- ?line Done = make_ref(),
- ?line P = spawn(fun () ->
- receive Go -> ok end,
- {reductions, R1} = process_info(self(), reductions),
- {ok, true} = call_yield(middle),
- true = call_yield(final),
- true = call_yield(),
- true = apply(erlang, yield, []),
- {reductions, R2} = process_info(self(), reductions),
- Me ! {RedDiff, R2 - R1},
- exit(Done)
- end),
- ?line erlang:yield(),
-
- ?line 1 = erlang:trace(P, true, [running, procs, {tracer, self()}]),
-
- ?line P ! Go,
+ Me = self(),
+ Go = make_ref(),
+ RedDiff = make_ref(),
+ Done = make_ref(),
+ P = spawn(fun () ->
+ receive Go -> ok end,
+ {reductions, R1} = process_info(self(), reductions),
+ {ok, true} = call_yield(middle),
+ true = call_yield(final),
+ true = call_yield(),
+ true = apply(erlang, yield, []),
+ {reductions, R2} = process_info(self(), reductions),
+ Me ! {RedDiff, R2 - R1},
+ exit(Done)
+ end),
+ erlang:yield(),
+
+ 1 = erlang:trace(P, true, [running, procs, {tracer, self()}]),
+
+ P ! Go,
%% receive Go -> ok end,
- ?line {trace, P, in, _} = next_tmsg(P),
+ {trace, P, in, _} = next_tmsg(P),
%% {ok, true} = call_yield(middle),
- ?line {trace, P, out, _} = next_tmsg(P),
- ?line {trace, P, in, _} = next_tmsg(P),
+ {trace, P, out, _} = next_tmsg(P),
+ {trace, P, in, _} = next_tmsg(P),
%% true = call_yield(final),
- ?line {trace, P, out, _} = next_tmsg(P),
- ?line {trace, P, in, _} = next_tmsg(P),
+ {trace, P, out, _} = next_tmsg(P),
+ {trace, P, in, _} = next_tmsg(P),
%% true = call_yield(),
- ?line {trace, P, out, _} = next_tmsg(P),
- ?line {trace, P, in, _} = next_tmsg(P),
+ {trace, P, out, _} = next_tmsg(P),
+ {trace, P, in, _} = next_tmsg(P),
%% true = apply(erlang, yield, []),
- ?line {trace, P, out, _} = next_tmsg(P),
- ?line {trace, P, in, _} = next_tmsg(P),
+ {trace, P, out, _} = next_tmsg(P),
+ {trace, P, in, _} = next_tmsg(P),
%% exit(Done)
- ?line {trace, P, exit, Done} = next_tmsg(P),
+ {trace, P, exit, Done} = next_tmsg(P),
- ?line receive
+ receive
{RedDiff, Reductions} when Reductions < 30, Reductions > 0 ->
io:format("Reductions = ~p~n", [Reductions]),
- ?line ok;
+ ok;
{RedDiff, Reductions} ->
- ?line ?t:fail({unexpected_reduction_count, Reductions})
+ ?t:fail({unexpected_reduction_count, Reductions})
end,
- ?line none = next_tmsg(P),
+ none = next_tmsg(P),
- ?line ok.
+ ok.
next_tmsg(Pid) ->
receive
@@ -1186,19 +1164,19 @@ next_tmsg(Pid) ->
bad_register(Config) when is_list(Config) ->
Name = a_long_and_unused_name,
- ?line {'EXIT',{badarg,_}} = (catch register({bad,name}, self())),
- ?line fail_register(undefined, self()),
- ?line fail_register([bad,name], self()),
+ {'EXIT',{badarg,_}} = (catch register({bad,name}, self())),
+ fail_register(undefined, self()),
+ fail_register([bad,name], self()),
- ?line {Dead,Mref} = spawn_monitor(fun() -> true end),
+ {Dead,Mref} = spawn_monitor(fun() -> true end),
receive
{'DOWN',Mref,process,Dead,_} -> ok
end,
- ?line fail_register(Name, Dead),
- ?line fail_register(Name, make_ref()),
- ?line fail_register(Name, []),
- ?line fail_register(Name, {bad,process}),
- ?line fail_register(Name, <<>>),
+ fail_register(Name, Dead),
+ fail_register(Name, make_ref()),
+ fail_register(Name, []),
+ fail_register(Name, {bad,process}),
+ fail_register(Name, <<>>),
ok.
fail_register(Name, Process) ->
@@ -1209,50 +1187,50 @@ fail_register(Name, Process) ->
garbage_collect(doc) -> [];
garbage_collect(suite) -> [];
garbage_collect(Config) when is_list(Config) ->
- ?line Prio = process_flag(priority, high),
- ?line true = erlang:garbage_collect(),
- ?line TokLoopers = lists:map(fun (_) ->
- spawn_opt(fun tok_loop/0,
- [{priority, low}, link])
- end,
- lists:seq(1, 10)),
- ?line lists:foreach(fun (Pid) ->
- ?line Mon = erlang:monitor(process, Pid),
- ?line DownBefore = receive
- {'DOWN', Mon, _, _, _} ->
- ?line true
- after 0 ->
- ?line false
- end,
- ?line GC = erlang:garbage_collect(Pid),
- ?line DownAfter = receive
- {'DOWN', Mon, _, _, _} ->
- ?line true
- after 0 ->
- ?line false
- end,
- ?line true = erlang:demonitor(Mon),
- ?line case {DownBefore, DownAfter} of
- {true, _} -> ?line false = GC;
- {false, false} -> ?line true = GC;
- _ -> ?line GC
- end
- end,
- processes()),
- ?line lists:foreach(fun (Pid) ->
- unlink(Pid),
- exit(Pid, bang)
- end, TokLoopers),
- ?line process_flag(priority, Prio),
- ?line ok.
+ Prio = process_flag(priority, high),
+ true = erlang:garbage_collect(),
+
+ TokLoopers = lists:map(fun (_) ->
+ spawn_opt(fun tok_loop/0, [{priority, low}, link])
+ end, lists:seq(1, 10)),
+
+ lists:foreach(fun (Pid) ->
+ Mon = erlang:monitor(process, Pid),
+ DownBefore = receive
+ {'DOWN', Mon, _, _, _} ->
+ true
+ after 0 ->
+ false
+ end,
+ GC = erlang:garbage_collect(Pid),
+ DownAfter = receive
+ {'DOWN', Mon, _, _, _} ->
+ true
+ after 0 ->
+ false
+ end,
+ true = erlang:demonitor(Mon),
+ case {DownBefore, DownAfter} of
+ {true, _} -> false = GC;
+ {false, false} -> true = GC;
+ _ -> GC
+ end
+ end, processes()),
+
+ lists:foreach(fun (Pid) ->
+ unlink(Pid),
+ exit(Pid, bang)
+ end, TokLoopers),
+ process_flag(priority, Prio),
+ ok.
process_info_messages(doc) ->
["This used to cause the nofrag emulator to dump core"];
process_info_messages(suite) ->
[];
process_info_messages(Config) when is_list(Config) ->
- ?line process_info_messages_test(),
- ?line ok.
+ process_info_messages_test(),
+ ok.
process_info_messages_loop(0) -> ok;
process_info_messages_loop(N) -> process_info_messages_loop(N-1).
@@ -1267,43 +1245,42 @@ process_info_messages_send_my_msgs_to(Rcvr) ->
end.
process_info_messages_test() ->
- ?line Go = make_ref(),
- ?line Done = make_ref(),
- ?line Rcvr = self(),
- ?line Rcvr2 = spawn_link(fun () ->
- receive {Go, Rcvr} -> ok end,
- garbage_collect(),
- Rcvr ! {Done, self()}
- end),
- ?line Sndrs = lists:map(
- fun (_) ->
- spawn_link(fun () ->
- Rcvr ! {Go, self()},
- receive {Go, Rcvr} -> ok end,
- BigData = lists:seq(1, 1000),
- Rcvr ! BigData,
- Rcvr ! BigData,
- Rcvr ! BigData,
- Rcvr ! {Done, self()}
- end)
- end,
- lists:seq(1, 10)),
- ?line lists:foreach(fun (Sndr) -> receive {Go, Sndr} -> ok end end,
+ Go = make_ref(),
+ Done = make_ref(),
+ Rcvr = self(),
+ Rcvr2 = spawn_link(fun () ->
+ receive {Go, Rcvr} -> ok end,
+ garbage_collect(),
+ Rcvr ! {Done, self()}
+ end),
+ Sndrs = lists:map(
+ fun (_) ->
+ spawn_link(fun () ->
+ Rcvr ! {Go, self()},
+ receive {Go, Rcvr} -> ok end,
+ BigData = lists:seq(1, 1000),
+ Rcvr ! BigData,
+ Rcvr ! BigData,
+ Rcvr ! BigData,
+ Rcvr ! {Done, self()}
+ end)
+ end, lists:seq(1, 10)),
+ lists:foreach(fun (Sndr) -> receive {Go, Sndr} -> ok end end,
Sndrs),
- ?line garbage_collect(),
- ?line erlang:yield(),
- ?line lists:foreach(fun (Sndr) -> Sndr ! {Go, self()} end, Sndrs),
- ?line process_info_messages_loop(100000000),
- ?line Msgs = process_info(self(), messages),
- ?line lists:foreach(fun (Sndr) -> receive {Done, Sndr} -> ok end end,
+ garbage_collect(),
+ erlang:yield(),
+ lists:foreach(fun (Sndr) -> Sndr ! {Go, self()} end, Sndrs),
+ process_info_messages_loop(100000000),
+ Msgs = process_info(self(), messages),
+ lists:foreach(fun (Sndr) -> receive {Done, Sndr} -> ok end end,
Sndrs),
- ?line garbage_collect(),
- ?line Rcvr2 ! Msgs,
- ?line process_info_messages_send_my_msgs_to(Rcvr2),
- ?line Rcvr2 ! {Go, self()},
- ?line garbage_collect(),
- ?line receive {Done, Rcvr2} -> ok end,
- ?line Msgs.
+ garbage_collect(),
+ Rcvr2 ! Msgs,
+ process_info_messages_send_my_msgs_to(Rcvr2),
+ Rcvr2 ! {Go, self()},
+ garbage_collect(),
+ receive {Done, Rcvr2} -> ok end,
+ Msgs.
chk_badarg(Fun) ->
try Fun(), exit(no_badarg) catch error:badarg -> ok end.
@@ -1313,76 +1290,72 @@ process_flag_badarg(doc) ->
process_flag_badarg(suite) ->
[];
process_flag_badarg(Config) when is_list(Config) ->
- ?line chk_badarg(fun () -> process_flag(gurka, banan) end),
- ?line chk_badarg(fun () -> process_flag(trap_exit, gurka) end),
- ?line chk_badarg(fun () -> process_flag(error_handler, 1) end),
- ?line chk_badarg(fun () -> process_flag(min_heap_size, gurka) end),
- ?line chk_badarg(fun () -> process_flag(min_bin_vheap_size, gurka) end),
- ?line chk_badarg(fun () -> process_flag(min_bin_vheap_size, -1) end),
- ?line chk_badarg(fun () -> process_flag(priority, 4711) end),
- ?line chk_badarg(fun () -> process_flag(save_calls, hmmm) end),
- ?line P= spawn_link(fun () -> receive die -> ok end end),
- ?line chk_badarg(fun () -> process_flag(P, save_calls, hmmm) end),
- ?line chk_badarg(fun () -> process_flag(gurka, save_calls, hmmm) end),
- ?line P ! die,
- ?line ok.
+ chk_badarg(fun () -> process_flag(gurka, banan) end),
+ chk_badarg(fun () -> process_flag(trap_exit, gurka) end),
+ chk_badarg(fun () -> process_flag(error_handler, 1) end),
+ chk_badarg(fun () -> process_flag(min_heap_size, gurka) end),
+ chk_badarg(fun () -> process_flag(min_bin_vheap_size, gurka) end),
+ chk_badarg(fun () -> process_flag(min_bin_vheap_size, -1) end),
+ chk_badarg(fun () -> process_flag(priority, 4711) end),
+ chk_badarg(fun () -> process_flag(save_calls, hmmm) end),
+ P= spawn_link(fun () -> receive die -> ok end end),
+ chk_badarg(fun () -> process_flag(P, save_calls, hmmm) end),
+ chk_badarg(fun () -> process_flag(gurka, save_calls, hmmm) end),
+ P ! die,
+ ok.
-include_lib("stdlib/include/ms_transform.hrl").
otp_6237(doc) -> [];
otp_6237(suite) -> [];
otp_6237(Config) when is_list(Config) ->
- ?line Slctrs = lists:map(fun (_) ->
- spawn_link(fun () ->
- otp_6237_select_loop()
- end)
- end,
- lists:seq(1,5)),
- ?line lists:foreach(fun (_) -> otp_6237_test() end, lists:seq(1, 100)),
- ?line lists:foreach(fun (S) -> unlink(S),exit(S, kill) end, Slctrs),
- ?line ok.
+ Slctrs = lists:map(fun (_) ->
+ spawn_link(fun () ->
+ otp_6237_select_loop()
+ end)
+ end,
+ lists:seq(1,5)),
+ lists:foreach(fun (_) -> otp_6237_test() end, lists:seq(1, 100)),
+ lists:foreach(fun (S) -> unlink(S),exit(S, kill) end, Slctrs),
+ ok.
otp_6237_test() ->
- ?line Parent = self(),
- ?line Inited = make_ref(),
- ?line Die = make_ref(),
- ?line Pid = spawn_link(fun () ->
- register(otp_6237,self()),
- otp_6237 = ets:new(otp_6237,
- [named_table,
- ordered_set]),
- ets:insert(otp_6237,
- [{I,I}
- || I <- lists:seq(1, 100)]),
- %% Inserting a lot of bif timers
- %% increase the possibility that
- %% the test will fail when the
- %% original cleanup order is used
- lists:foreach(
- fun (_) ->
- erlang:send_after(1000000,
- self(),
- {a,b,c})
- end,
- lists:seq(1,1000)),
- Parent ! Inited,
- receive Die -> bye end
- end),
- ?line receive
- Inited -> ?line ok
- end,
- ?line Pid ! Die,
+ Parent = self(),
+ Inited = make_ref(),
+ Die = make_ref(),
+ Pid = spawn_link(fun () ->
+ register(otp_6237,self()),
+ otp_6237 = ets:new(otp_6237,
+ [named_table,
+ ordered_set]),
+ ets:insert(otp_6237,
+ [{I,I}
+ || I <- lists:seq(1, 100)]),
+ %% Inserting a lot of bif timers
+ %% increase the possibility that
+ %% the test will fail when the
+ %% original cleanup order is used
+ lists:foreach( fun (_) ->
+ erlang:send_after(1000000, self(), {a,b,c})
+ end, lists:seq(1,1000)),
+ Parent ! Inited,
+ receive Die -> bye end
+ end),
+ receive
+ Inited -> ok
+ end,
+ Pid ! Die,
otp_6237_whereis_loop().
otp_6237_whereis_loop() ->
- ?line case whereis(otp_6237) of
+ case whereis(otp_6237) of
undefined ->
- ?line otp_6237 = ets:new(otp_6237,
+ otp_6237 = ets:new(otp_6237,
[named_table,ordered_set]),
- ?line ets:delete(otp_6237),
- ?line ok;
+ ets:delete(otp_6237),
+ ok;
_ ->
- ?line otp_6237_whereis_loop()
+ otp_6237_whereis_loop()
end.
otp_6237_select_loop() ->
@@ -1390,9 +1363,8 @@ otp_6237_select_loop() ->
otp_6237_select_loop().
-
-define(NoTestProcs, 10000).
--record(processes_bif_info, {min_start_reds,
+-record(ptab_list_bif_info, {min_start_reds,
tab_chunks,
tab_chunks_size,
tab_indices_per_red,
@@ -1407,89 +1379,86 @@ processes_large_tab(doc) ->
processes_large_tab(suite) ->
[];
processes_large_tab(Config) when is_list(Config) ->
- ?line enable_internal_state(),
- ?line MaxDbgLvl = 20,
- ?line MinProcTabSize = 2*(1 bsl 15),
- ?line ProcTabSize0 = 1000000,
- ?line ProcTabSize1 = case {erlang:system_info(schedulers_online),
- erlang:system_info(logical_processors)} of
- {Schdlrs, Cpus} when is_integer(Cpus),
- Schdlrs =< Cpus ->
- ProcTabSize0;
- _ ->
- ProcTabSize0 div 4
- end,
- ?line ProcTabSize2 = case erlang:system_info(debug_compiled) of
- true -> ProcTabSize1 - 500000;
- false -> ProcTabSize1
- end,
+ enable_internal_state(),
+ MaxDbgLvl = 20,
+ MinProcTabSize = 2*(1 bsl 15),
+ ProcTabSize0 = 1000000,
+ ProcTabSize1 = case {erlang:system_info(schedulers_online),
+ erlang:system_info(logical_processors)} of
+ {Schdlrs, Cpus} when is_integer(Cpus),
+ Schdlrs =< Cpus ->
+ ProcTabSize0;
+ _ ->
+ ProcTabSize0 div 4
+ end,
+ ProcTabSize2 = case erlang:system_info(debug_compiled) of
+ true -> ProcTabSize1 - 500000;
+ false -> ProcTabSize1
+ end,
%% With high debug levels this test takes so long time that
%% the connection times out; therefore, shrink the test on
%% high debug levels.
- ?line DbgLvl = case erts_debug:get_internal_state(processes_bif_info) of
- #processes_bif_info{debug_level = Lvl} when Lvl > MaxDbgLvl ->
+ DbgLvl = case erts_debug:get_internal_state(processes_bif_info) of
+ #ptab_list_bif_info{debug_level = Lvl} when Lvl > MaxDbgLvl ->
20;
- #processes_bif_info{debug_level = Lvl} when Lvl < 0 ->
- ?line ?t:fail({debug_level, Lvl});
- #processes_bif_info{debug_level = Lvl} ->
+ #ptab_list_bif_info{debug_level = Lvl} when Lvl < 0 ->
+ ?t:fail({debug_level, Lvl});
+ #ptab_list_bif_info{debug_level = Lvl} ->
Lvl
end,
- ?line ProcTabSize3 = ProcTabSize2 - (1300000 * DbgLvl div MaxDbgLvl),
- ?line ProcTabSize = case ProcTabSize3 < MinProcTabSize of
+ ProcTabSize3 = ProcTabSize2 - (1300000 * DbgLvl div MaxDbgLvl),
+ ProcTabSize = case ProcTabSize3 < MinProcTabSize of
true -> MinProcTabSize;
false -> ProcTabSize3
end,
- ?line {ok, LargeNode} = start_node(Config,
+ {ok, LargeNode} = start_node(Config,
"+P " ++ integer_to_list(ProcTabSize)),
- ?line Res = rpc:call(LargeNode, ?MODULE, processes_bif_test, []),
- ?line case rpc:call(LargeNode,
+ Res = rpc:call(LargeNode, ?MODULE, processes_bif_test, []),
+ case rpc:call(LargeNode,
erts_debug,
get_internal_state,
[processes_bif_info]) of
- #processes_bif_info{tab_chunks = Chunks} when is_integer(Chunks),
+ #ptab_list_bif_info{tab_chunks = Chunks} when is_integer(Chunks),
Chunks > 1 -> ok;
PBInfo -> ?t:fail(PBInfo)
end,
- ?line stop_node(LargeNode),
- ?line chk_processes_bif_test_res(Res).
+ stop_node(LargeNode),
+ chk_processes_bif_test_res(Res).
processes_default_tab(doc) ->
[];
processes_default_tab(suite) ->
[];
processes_default_tab(Config) when is_list(Config) ->
- ?line {ok, DefaultNode} = start_node(Config, ""),
- ?line Res = rpc:call(DefaultNode, ?MODULE, processes_bif_test, []),
- ?line stop_node(DefaultNode),
- ?line chk_processes_bif_test_res(Res).
+ {ok, DefaultNode} = start_node(Config, ""),
+ Res = rpc:call(DefaultNode, ?MODULE, processes_bif_test, []),
+ stop_node(DefaultNode),
+ chk_processes_bif_test_res(Res).
processes_small_tab(doc) ->
[];
processes_small_tab(suite) ->
[];
processes_small_tab(Config) when is_list(Config) ->
- ?line {ok, SmallNode} = start_node(Config, "+P 500"),
- ?line Res = rpc:call(SmallNode, ?MODULE, processes_bif_test, []),
- ?line PBInfo = rpc:call(SmallNode,
- erts_debug,
- get_internal_state,
- [processes_bif_info]),
- ?line stop_node(SmallNode),
- ?line 1 = PBInfo#processes_bif_info.tab_chunks,
- ?line chk_processes_bif_test_res(Res).
+ {ok, SmallNode} = start_node(Config, "+P 1024"),
+ Res = rpc:call(SmallNode, ?MODULE, processes_bif_test, []),
+ PBInfo = rpc:call(SmallNode, erts_debug, get_internal_state, [processes_bif_info]),
+ stop_node(SmallNode),
+ true = PBInfo#ptab_list_bif_info.tab_chunks < 10,
+ chk_processes_bif_test_res(Res).
processes_this_tab(doc) ->
[];
processes_this_tab(suite) ->
[];
processes_this_tab(Config) when is_list(Config) ->
- ?line chk_processes_bif_test_res(processes_bif_test()).
+ chk_processes_bif_test_res(processes_bif_test()).
chk_processes_bif_test_res(ok) -> ok;
chk_processes_bif_test_res({comment, _} = Comment) -> Comment;
chk_processes_bif_test_res(Failure) -> ?t:fail(Failure).
-print_processes_bif_info(#processes_bif_info{min_start_reds = MinStartReds,
+print_processes_bif_info(#ptab_list_bif_info{min_start_reds = MinStartReds,
tab_chunks = TabChunks,
tab_chunks_size = TabChunksSize,
tab_indices_per_red = TabIndPerRed,
@@ -1583,26 +1552,26 @@ do_processes(WantReds) ->
processes().
processes_bif_test() ->
- ?line Tester = self(),
- ?line enable_internal_state(),
- ?line PBInfo = erts_debug:get_internal_state(processes_bif_info),
- ?line print_processes_bif_info(PBInfo),
- ?line WantReds = PBInfo#processes_bif_info.min_start_reds + 10,
- ?line WillTrap = case PBInfo of
- #processes_bif_info{tab_chunks = 1} ->
- false;
- #processes_bif_info{tab_chunks = Chunks,
- tab_chunks_size = ChunksSize,
- tab_indices_per_red = IndiciesPerRed
- } ->
- Chunks*ChunksSize >= IndiciesPerRed*WantReds
- end,
- ?line Processes = fun () ->
- erts_debug:set_internal_state(reds_left,WantReds),
- processes()
- end,
+ Tester = self(),
+ enable_internal_state(),
+ PBInfo = erts_debug:get_internal_state(processes_bif_info),
+ print_processes_bif_info(PBInfo),
+ WantReds = PBInfo#ptab_list_bif_info.min_start_reds + 10,
+ WillTrap = case PBInfo of
+ #ptab_list_bif_info{tab_chunks = Chunks} when Chunks < 10 ->
+ false; %% Skip for small tables
+ #ptab_list_bif_info{tab_chunks = Chunks,
+ tab_chunks_size = ChunksSize,
+ tab_indices_per_red = IndiciesPerRed
+ } ->
+ Chunks*ChunksSize >= IndiciesPerRed*WantReds
+ end,
+ Processes = fun () ->
+ erts_debug:set_internal_state(reds_left,WantReds),
+ processes()
+ end,
- ?line ok = do_processes_bif_test(WantReds, WillTrap, Processes),
+ ok = do_processes_bif_test(WantReds, WillTrap, Processes),
case WillTrap of
false ->
@@ -1610,8 +1579,8 @@ processes_bif_test() ->
true ->
%% Do it again with a process suspended while
%% in the processes/0 bif.
- ?line erlang:system_flag(multi_scheduling, block),
- ?line Suspendee = spawn_link(fun () ->
+ erlang:system_flag(multi_scheduling, block),
+ Suspendee = spawn_link(fun () ->
Tester ! {suspend_me, self()},
Tester ! {self(),
done,
@@ -1621,179 +1590,160 @@ processes_bif_test() ->
ok
end
end),
- ?line receive {suspend_me, Suspendee} -> ok end,
- ?line erlang:suspend_process(Suspendee),
- ?line erlang:system_flag(multi_scheduling, unblock),
+ receive {suspend_me, Suspendee} -> ok end,
+ erlang:suspend_process(Suspendee),
+ erlang:system_flag(multi_scheduling, unblock),
- ?line [{status,suspended},
- {current_function,{erlang,processes_trap,2}}]
- = process_info(Suspendee, [status, current_function]),
+ [{status,suspended},{current_function,{erlang,ptab_list_continue,2}}] =
+ process_info(Suspendee, [status, current_function]),
- ?line ok = do_processes_bif_test(WantReds, WillTrap, Processes),
+ ok = do_processes_bif_test(WantReds, WillTrap, Processes),
- ?line erlang:resume_process(Suspendee),
- ?line receive {Suspendee, done, _} -> ok end,
- ?line unlink(Suspendee),
- ?line exit(Suspendee, bang)
+ erlang:resume_process(Suspendee),
+ receive {Suspendee, done, _} -> ok end,
+ unlink(Suspendee),
+ exit(Suspendee, bang)
end,
case get(processes_bif_testcase_comment) of
- undefined -> ?line ok;
- Comment -> ?line {comment, Comment}
+ undefined -> ok;
+ Comment -> {comment, Comment}
end.
do_processes_bif_test(WantReds, DieTest, Processes) ->
- ?line Tester = self(),
- ?line SpawnProcesses = fun (Prio) ->
- spawn_opt(?MODULE,
- do_processes,
- [WantReds],
- [link, {priority, Prio}])
- end,
- ?line Cleaner = spawn_link(fun () ->
- process_flag(trap_exit, true),
- Tester ! {cleaner_alive, self()},
- processes_bif_cleaner()
- end),
- ?line receive {cleaner_alive, Cleaner} -> ok end,
+ Tester = self(),
+ SpawnProcesses = fun (Prio) ->
+ spawn_opt(?MODULE, do_processes, [WantReds], [link, {priority, Prio}])
+ end,
+ Cleaner = spawn_link(fun () ->
+ process_flag(trap_exit, true),
+ Tester ! {cleaner_alive, self()},
+ processes_bif_cleaner()
+ end),
+ receive {cleaner_alive, Cleaner} -> ok end,
try
- ?line DoIt = make_ref(),
- ?line GetGoing = make_ref(),
- ?line {NoTestProcs, TestProcs} = spawn_initial_hangarounds(Cleaner),
- ?line ?t:format("Testing with ~p processes~n", [NoTestProcs]),
- ?line SpawnHangAround = fun () ->
- spawn(?MODULE,
- hangaround,
- [Cleaner, new_hangaround])
- end,
- ?line Killer = spawn_opt(fun () ->
- Splt = NoTestProcs div 10,
- {TP1, TP23} = lists:split(Splt,
- TestProcs),
- {TP2, TP3} = lists:split(Splt, TP23),
- erlang:system_flag(multi_scheduling,
- block),
- Tester ! DoIt,
- receive GetGoing -> ok end,
- erlang:system_flag(multi_scheduling,
- unblock),
- SpawnProcesses(high),
- lists:foreach(
- fun (P) ->
- SpawnHangAround(),
- exit(P, bang)
- end,
- TP1),
- SpawnProcesses(high),
- erlang:yield(),
- lists:foreach(
- fun (P) ->
- SpawnHangAround(),
- exit(P, bang)
- end,
- TP2),
- SpawnProcesses(high),
- lists:foreach(
- fun (P) ->
- SpawnHangAround(),
- exit(P, bang)
- end,
- TP3)
- end,
- [{priority, high}, link]),
- ?line receive DoIt -> ok end,
- ?line process_flag(priority, low),
- ?line SpawnProcesses(low),
- ?line erlang:yield(),
- ?line process_flag(priority, normal),
- ?line CorrectProcs0 = erts_debug:get_internal_state(processes),
- ?line Killer ! GetGoing,
- ?line erts_debug:set_internal_state(reds_left, WantReds),
- ?line Procs0 = processes(),
- ?line Procs = lists:sort(Procs0),
- ?line CorrectProcs = lists:sort(CorrectProcs0),
- ?line LengthCorrectProcs = length(CorrectProcs),
- ?line ?t:format("~p = length(CorrectProcs)~n", [LengthCorrectProcs]),
- ?line true = LengthCorrectProcs > NoTestProcs,
- ?line case CorrectProcs =:= Procs of
- true ->
- ?line ok;
- false ->
- ?line processes_unexpected_result(CorrectProcs, Procs)
- end,
- ?line unlink(Killer),
- ?line exit(Killer, bang)
+ DoIt = make_ref(),
+ GetGoing = make_ref(),
+ {NoTestProcs, TestProcs} = spawn_initial_hangarounds(Cleaner),
+ ?t:format("Testing with ~p processes~n", [NoTestProcs]),
+ SpawnHangAround = fun () ->
+ spawn(?MODULE, hangaround, [Cleaner, new_hangaround])
+ end,
+ Killer = spawn_opt(fun () ->
+ Splt = NoTestProcs div 10,
+ {TP1, TP23} = lists:split(Splt, TestProcs),
+ {TP2, TP3} = lists:split(Splt, TP23),
+ erlang:system_flag(multi_scheduling, block),
+ Tester ! DoIt,
+ receive GetGoing -> ok end,
+ erlang:system_flag(multi_scheduling, unblock),
+ SpawnProcesses(high),
+ lists:foreach( fun (P) ->
+ SpawnHangAround(),
+ exit(P, bang)
+ end, TP1),
+ SpawnProcesses(high),
+ erlang:yield(),
+ lists:foreach( fun (P) ->
+ SpawnHangAround(),
+ exit(P, bang)
+ end, TP2),
+ SpawnProcesses(high),
+ lists:foreach(
+ fun (P) ->
+ SpawnHangAround(),
+ exit(P, bang)
+ end, TP3)
+ end, [{priority, high}, link]),
+ receive DoIt -> ok end,
+ process_flag(priority, low),
+ SpawnProcesses(low),
+ erlang:yield(),
+ process_flag(priority, normal),
+ CorrectProcs0 = erts_debug:get_internal_state(processes),
+ Killer ! GetGoing,
+ erts_debug:set_internal_state(reds_left, WantReds),
+ Procs0 = processes(),
+ Procs = lists:sort(Procs0),
+ CorrectProcs = lists:sort(CorrectProcs0),
+ LengthCorrectProcs = length(CorrectProcs),
+ ?t:format("~p = length(CorrectProcs)~n", [LengthCorrectProcs]),
+ true = LengthCorrectProcs > NoTestProcs,
+ case CorrectProcs =:= Procs of
+ true ->
+ ok;
+ false ->
+ processes_unexpected_result(CorrectProcs, Procs)
+ end,
+ unlink(Killer),
+ exit(Killer, bang)
after
unlink(Cleaner),
exit(Cleaner, kill),
%% Wait for the system to recover to a normal state...
wait_until_system_recover()
end,
- ?line do_processes_bif_die_test(DieTest, Processes),
- ?line ok.
+ do_processes_bif_die_test(DieTest, Processes),
+ ok.
do_processes_bif_die_test(false, _Processes) ->
- ?line ?t:format("Skipping test killing process executing processes/0~n",[]),
- ?line ok;
+ ?t:format("Skipping test killing process executing processes/0~n",[]),
+ ok;
do_processes_bif_die_test(true, Processes) ->
- ?line do_processes_bif_die_test(5, Processes);
+ do_processes_bif_die_test(5, Processes);
do_processes_bif_die_test(N, Processes) ->
- ?line ?t:format("Doing test killing process executing processes/0~n",[]),
+ ?t:format("Doing test killing process executing processes/0~n",[]),
try
- ?line Tester = self(),
- ?line Oooh_Nooooooo = make_ref(),
- ?line {_, DieWhileDoingMon} = erlang:spawn_monitor(
- fun () ->
- Victim = self(),
- spawn_opt(
- fun () ->
- exit(Victim, got_him)
- end,
- [link,
- {priority, max}]),
- Tester ! {Oooh_Nooooooo,
- hd(Processes())},
- exit(ohhhh_nooooo)
- end),
- ?line receive
- {'DOWN', DieWhileDoingMon, _, _, Reason} ->
- case Reason of
- got_him -> ok;
- _ -> throw({kill_in_trap, Reason})
- end
- end,
- ?line receive
- {Oooh_Nooooooo, _} ->
- ?line throw({kill_in_trap, 'Oooh_Nooooooo'})
- after 0 ->
- ?line ok
- end,
- ?line PrcsCllrsSeqLen = 2*erlang:system_info(schedulers_online),
- ?line PrcsCllrsSeq = lists:seq(1, PrcsCllrsSeqLen),
- ?line ProcsCallers = lists:map(
- fun (_) ->
- spawn_link(
- fun () ->
- Tester ! hd(Processes())
- end)
- end,
- PrcsCllrsSeq),
- ?line erlang:yield(),
+ Tester = self(),
+ Oooh_Nooooooo = make_ref(),
+ {_, DieWhileDoingMon} = erlang:spawn_monitor( fun () ->
+ Victim = self(),
+ spawn_opt(
+ fun () ->
+ exit(Victim, got_him)
+ end,
+ [link, {priority, max}]),
+ Tester ! {Oooh_Nooooooo,
+ hd(Processes())},
+ exit(ohhhh_nooooo)
+ end),
+ receive
+ {'DOWN', DieWhileDoingMon, _, _, Reason} ->
+ case Reason of
+ got_him -> ok;
+ _ -> throw({kill_in_trap, Reason})
+ end
+ end,
+ receive
+ {Oooh_Nooooooo, _} ->
+ throw({kill_in_trap, 'Oooh_Nooooooo'})
+ after 0 ->
+ ok
+ end,
+ PrcsCllrsSeqLen = 2*erlang:system_info(schedulers_online),
+ PrcsCllrsSeq = lists:seq(1, PrcsCllrsSeqLen),
+ ProcsCallers = lists:map( fun (_) ->
+ spawn_link(
+ fun () ->
+ Tester ! hd(Processes())
+ end)
+ end, PrcsCllrsSeq),
+ erlang:yield(),
{ProcsCallers1, ProcsCallers2} = lists:split(PrcsCllrsSeqLen div 2,
ProcsCallers),
- ?line process_flag(priority, high),
- ?line lists:foreach(
+ process_flag(priority, high),
+ lists:foreach(
fun (P) ->
unlink(P),
exit(P, bang)
end,
lists:reverse(ProcsCallers2) ++ ProcsCallers1),
- ?line process_flag(priority, normal),
- ?line ok
+ process_flag(priority, normal),
+ ok
catch
throw:{kill_in_trap, R} when N > 0 ->
?t:format("Failed to kill in trap: ~p~n", [R]),
- ?t:format("Trying again~p~n", []),
+ ?t:format("Trying again~n", []),
do_processes_bif_die_test(N-1, Processes)
end.
@@ -1852,23 +1802,23 @@ processes_last_call_trap(doc) ->
processes_last_call_trap(suite) ->
[];
processes_last_call_trap(Config) when is_list(Config) ->
- ?line enable_internal_state(),
- ?line Processes = fun () -> processes() end,
- ?line PBInfo = erts_debug:get_internal_state(processes_bif_info),
- ?line print_processes_bif_info(PBInfo),
- ?line WantReds = case PBInfo#processes_bif_info.min_start_reds of
- R when R > 10 -> R - 1;
- _R -> 9
- end,
- ?line lists:foreach(fun (_) ->
- ?line erts_debug:set_internal_state(reds_left,
- WantReds),
- Processes(),
- ?line erts_debug:set_internal_state(reds_left,
- WantReds),
- my_processes()
- end,
- lists:seq(1,100)).
+ enable_internal_state(),
+ Processes = fun () -> processes() end,
+ PBInfo = erts_debug:get_internal_state(processes_bif_info),
+ print_processes_bif_info(PBInfo),
+ WantReds = case PBInfo#ptab_list_bif_info.min_start_reds of
+ R when R > 10 -> R - 1;
+ _R -> 9
+ end,
+ lists:foreach(fun (_) ->
+ erts_debug:set_internal_state(reds_left,
+ WantReds),
+ Processes(),
+ erts_debug:set_internal_state(reds_left,
+ WantReds),
+ my_processes()
+ end,
+ lists:seq(1,100)).
my_processes() ->
processes().
@@ -1878,108 +1828,106 @@ processes_apply_trap(doc) ->
processes_apply_trap(suite) ->
[];
processes_apply_trap(Config) when is_list(Config) ->
- ?line enable_internal_state(),
- ?line PBInfo = erts_debug:get_internal_state(processes_bif_info),
- ?line print_processes_bif_info(PBInfo),
- ?line WantReds = case PBInfo#processes_bif_info.min_start_reds of
- R when R > 10 -> R - 1;
- _R -> 9
- end,
- ?line lists:foreach(fun (_) ->
- ?line erts_debug:set_internal_state(reds_left,
- WantReds),
- ?line apply(erlang, processes, [])
- end,
- lists:seq(1,100)).
+ enable_internal_state(),
+ PBInfo = erts_debug:get_internal_state(processes_bif_info),
+ print_processes_bif_info(PBInfo),
+ WantReds = case PBInfo#ptab_list_bif_info.min_start_reds of
+ R when R > 10 -> R - 1;
+ _R -> 9
+ end,
+ lists:foreach(fun (_) ->
+ erts_debug:set_internal_state(reds_left,
+ WantReds),
+ apply(erlang, processes, [])
+ end, lists:seq(1,100)).
processes_gc_trap(doc) ->
[];
processes_gc_trap(suite) ->
[];
processes_gc_trap(Config) when is_list(Config) ->
- ?line Tester = self(),
- ?line enable_internal_state(),
- ?line PBInfo = erts_debug:get_internal_state(processes_bif_info),
- ?line print_processes_bif_info(PBInfo),
- ?line WantReds = PBInfo#processes_bif_info.min_start_reds + 10,
- ?line Processes = fun () ->
- erts_debug:set_internal_state(reds_left,WantReds),
- processes()
- end,
+ Tester = self(),
+ enable_internal_state(),
+ PBInfo = erts_debug:get_internal_state(processes_bif_info),
+ print_processes_bif_info(PBInfo),
+ WantReds = PBInfo#ptab_list_bif_info.min_start_reds + 10,
+ Processes = fun () ->
+ erts_debug:set_internal_state(reds_left,WantReds),
+ processes()
+ end,
- ?line erlang:system_flag(multi_scheduling, block),
- ?line Suspendee = spawn_link(fun () ->
+ erlang:system_flag(multi_scheduling, block),
+ Suspendee = spawn_link(fun () ->
Tester ! {suspend_me, self()},
Tester ! {self(),
done,
hd(Processes())},
receive after infinity -> ok end
end),
- ?line receive {suspend_me, Suspendee} -> ok end,
- ?line erlang:suspend_process(Suspendee),
- ?line erlang:system_flag(multi_scheduling, unblock),
+ receive {suspend_me, Suspendee} -> ok end,
+ erlang:suspend_process(Suspendee),
+ erlang:system_flag(multi_scheduling, unblock),
- ?line [{status,suspended}, {current_function,{erlang,processes_trap,2}}]
+ [{status,suspended}, {current_function,{erlang,ptab_list_continue,2}}]
= process_info(Suspendee, [status, current_function]),
- ?line erlang:garbage_collect(Suspendee),
- ?line erlang:garbage_collect(Suspendee),
+ erlang:garbage_collect(Suspendee),
+ erlang:garbage_collect(Suspendee),
- ?line erlang:resume_process(Suspendee),
- ?line receive {Suspendee, done, _} -> ok end,
- ?line erlang:garbage_collect(Suspendee),
- ?line erlang:garbage_collect(Suspendee),
+ erlang:resume_process(Suspendee),
+ receive {Suspendee, done, _} -> ok end,
+ erlang:garbage_collect(Suspendee),
+ erlang:garbage_collect(Suspendee),
- ?line unlink(Suspendee),
- ?line exit(Suspendee, bang),
- ?line ok.
+ unlink(Suspendee),
+ exit(Suspendee, bang),
+ ok.
process_flag_heap_size(doc) ->
[];
process_flag_heap_size(suite) ->
[];
process_flag_heap_size(Config) when is_list(Config) ->
- HSize = 2584, % must be gc fib number
- VHSize = 317811, % must be gc fib number
- ?line OldHmin = erlang:process_flag(min_heap_size, HSize),
- ?line {min_heap_size, HSize} = erlang:process_info(self(), min_heap_size),
- ?line OldVHmin = erlang:process_flag(min_bin_vheap_size, VHSize),
- ?line {min_bin_vheap_size, VHSize} = erlang:process_info(self(), min_bin_vheap_size),
- ?line HSize = erlang:process_flag(min_heap_size, OldHmin),
- ?line VHSize = erlang:process_flag(min_bin_vheap_size, OldVHmin),
- ?line ok.
+ HSize = 2586, % must be gc fib+ number
+ VHSize = 318187, % must be gc fib+ number
+ OldHmin = erlang:process_flag(min_heap_size, HSize),
+ {min_heap_size, HSize} = erlang:process_info(self(), min_heap_size),
+ OldVHmin = erlang:process_flag(min_bin_vheap_size, VHSize),
+ {min_bin_vheap_size, VHSize} = erlang:process_info(self(), min_bin_vheap_size),
+ HSize = erlang:process_flag(min_heap_size, OldHmin),
+ VHSize = erlang:process_flag(min_bin_vheap_size, OldVHmin),
+ ok.
spawn_opt_heap_size(doc) ->
[];
spawn_opt_heap_size(suite) ->
[];
spawn_opt_heap_size(Config) when is_list(Config) ->
- HSize = 987, % must be gc fib number
- VHSize = 46368, % must be gc fib number
- ?line Pid = spawn_opt(fun () -> receive stop -> ok end end,
+ HSize = 987, % must be gc fib+ number
+ VHSize = 46422, % must be gc fib+ number
+ Pid = spawn_opt(fun () -> receive stop -> ok end end,
[{min_heap_size, HSize},{ min_bin_vheap_size, VHSize}]),
- ?line {min_heap_size, HSize} = process_info(Pid, min_heap_size),
- ?line {min_bin_vheap_size, VHSize} = process_info(Pid, min_bin_vheap_size),
- ?line Pid ! stop,
- ?line ok.
+ {min_heap_size, HSize} = process_info(Pid, min_heap_size),
+ {min_bin_vheap_size, VHSize} = process_info(Pid, min_bin_vheap_size),
+ Pid ! stop,
+ ok.
processes_term_proc_list(doc) ->
[];
processes_term_proc_list(suite) ->
[];
processes_term_proc_list(Config) when is_list(Config) ->
- ?line Tester = self(),
- ?line as_expected = processes_term_proc_list_test(false),
- ?line {ok, Node} = start_node(Config, "+Mis true"),
- ?line RT = spawn_link(Node,
- fun () ->
- receive after 1000 -> ok end,
- processes_term_proc_list_test(false),
- Tester ! {it_worked, self()}
- end),
- ?line receive {it_worked, RT} -> ok end,
- ?line stop_node(Node),
- ?line ok.
+ Tester = self(),
+ as_expected = processes_term_proc_list_test(false),
+ {ok, Node} = start_node(Config, "+Mis true"),
+ RT = spawn_link(Node, fun () ->
+ receive after 1000 -> ok end,
+ processes_term_proc_list_test(false),
+ Tester ! {it_worked, self()}
+ end),
+ receive {it_worked, RT} -> ok end,
+ stop_node(Node),
+ ok.
-define(CHK_TERM_PROC_LIST(MC, XB),
chk_term_proc_list(?LINE, MC, XB)).
@@ -1990,8 +1938,8 @@ chk_term_proc_list(Line, MustChk, ExpectBlks) ->
not_enabled;
{_, MS} ->
{value,
- {processes_term_proc_el,
- DL}} = lists:keysearch(processes_term_proc_el, 1, MS),
+ {ptab_list_deleted_el,
+ DL}} = lists:keysearch(ptab_list_deleted_el, 1, MS),
case lists:keysearch(blocks, 1, DL) of
{value, {blocks, ExpectBlks, _, _}} ->
ok;
@@ -2005,35 +1953,34 @@ chk_term_proc_list(Line, MustChk, ExpectBlks) ->
ok.
processes_term_proc_list_test(MustChk) ->
- ?line Tester = self(),
- ?line enable_internal_state(),
- ?line PBInfo = erts_debug:get_internal_state(processes_bif_info),
- ?line print_processes_bif_info(PBInfo),
- ?line WantReds = PBInfo#processes_bif_info.min_start_reds + 10,
- ?line #processes_bif_info{tab_chunks = Chunks,
- tab_chunks_size = ChunksSize,
- tab_indices_per_red = IndiciesPerRed
- } = PBInfo,
- ?line true = Chunks > 1,
- ?line true = Chunks*ChunksSize >= IndiciesPerRed*WantReds,
- ?line Processes = fun () ->
- erts_debug:set_internal_state(reds_left,
- WantReds),
- processes()
- end,
- ?line Exit = fun (P) ->
- unlink(P),
- exit(P, bang),
- wait_until(
- fun () ->
- not lists:member(
- P,
- erts_debug:get_internal_state(
- processes))
- end)
- end,
- ?line SpawnSuspendProcessesProc
- = fun () ->
+ Tester = self(),
+ enable_internal_state(),
+ PBInfo = erts_debug:get_internal_state(processes_bif_info),
+ print_processes_bif_info(PBInfo),
+ WantReds = PBInfo#ptab_list_bif_info.min_start_reds + 10,
+ #ptab_list_bif_info{tab_chunks = Chunks,
+ tab_chunks_size = ChunksSize,
+ tab_indices_per_red = IndiciesPerRed
+ } = PBInfo,
+ true = Chunks > 1,
+ true = Chunks*ChunksSize >= IndiciesPerRed*WantReds,
+ Processes = fun () ->
+ erts_debug:set_internal_state(reds_left,
+ WantReds),
+ processes()
+ end,
+ Exit = fun (P) ->
+ unlink(P),
+ exit(P, bang),
+ wait_until(
+ fun () ->
+ not lists:member(
+ P,
+ erts_debug:get_internal_state(
+ processes))
+ end)
+ end,
+ SpawnSuspendProcessesProc = fun () ->
erlang:system_flag(multi_scheduling, block),
P = spawn_link(fun () ->
Tester ! {suspend_me, self()},
@@ -2046,76 +1993,76 @@ processes_term_proc_list_test(MustChk) ->
erlang:suspend_process(P),
erlang:system_flag(multi_scheduling, unblock),
[{status,suspended},
- {current_function,{erlang,processes_trap,2}}]
+ {current_function,{erlang,ptab_list_continue,2}}]
= process_info(P, [status, current_function]),
P
end,
- ?line ResumeProcessesProc = fun (P) ->
+ ResumeProcessesProc = fun (P) ->
erlang:resume_process(P),
receive {P, done, _} -> ok end
end,
- ?line ?CHK_TERM_PROC_LIST(MustChk, 0),
- ?line HangAround = fun () -> receive after infinity -> ok end end,
- ?line HA1 = spawn_link(HangAround),
- ?line HA2 = spawn_link(HangAround),
- ?line HA3 = spawn_link(HangAround),
- ?line S1 = SpawnSuspendProcessesProc(),
- ?line ?CHK_TERM_PROC_LIST(MustChk, 1),
- ?line Exit(HA1),
- ?line ?CHK_TERM_PROC_LIST(MustChk, 2),
- ?line S2 = SpawnSuspendProcessesProc(),
- ?line ?CHK_TERM_PROC_LIST(MustChk, 3),
- ?line S3 = SpawnSuspendProcessesProc(),
- ?line ?CHK_TERM_PROC_LIST(MustChk, 4),
- ?line Exit(HA2),
- ?line ?CHK_TERM_PROC_LIST(MustChk, 5),
- ?line S4 = SpawnSuspendProcessesProc(),
- ?line ?CHK_TERM_PROC_LIST(MustChk, 6),
- ?line Exit(HA3),
- ?line ?CHK_TERM_PROC_LIST(MustChk, 7),
- ?line ResumeProcessesProc(S1),
- ?line ?CHK_TERM_PROC_LIST(MustChk, 5),
- ?line ResumeProcessesProc(S3),
- ?line ?CHK_TERM_PROC_LIST(MustChk, 4),
- ?line ResumeProcessesProc(S4),
- ?line ?CHK_TERM_PROC_LIST(MustChk, 3),
- ?line ResumeProcessesProc(S2),
- ?line ?CHK_TERM_PROC_LIST(MustChk, 0),
- ?line Exit(S1),
- ?line Exit(S2),
- ?line Exit(S3),
- ?line Exit(S4),
-
-
- ?line HA4 = spawn_link(HangAround),
- ?line HA5 = spawn_link(HangAround),
- ?line HA6 = spawn_link(HangAround),
- ?line S5 = SpawnSuspendProcessesProc(),
- ?line ?CHK_TERM_PROC_LIST(MustChk, 1),
- ?line Exit(HA4),
- ?line ?CHK_TERM_PROC_LIST(MustChk, 2),
- ?line S6 = SpawnSuspendProcessesProc(),
- ?line ?CHK_TERM_PROC_LIST(MustChk, 3),
- ?line Exit(HA5),
- ?line ?CHK_TERM_PROC_LIST(MustChk, 4),
- ?line S7 = SpawnSuspendProcessesProc(),
- ?line ?CHK_TERM_PROC_LIST(MustChk, 5),
- ?line Exit(HA6),
- ?line ?CHK_TERM_PROC_LIST(MustChk, 6),
- ?line S8 = SpawnSuspendProcessesProc(),
- ?line ?CHK_TERM_PROC_LIST(MustChk, 7),
-
- ?line erlang:system_flag(multi_scheduling, block),
- ?line Exit(S8),
- ?line ?CHK_TERM_PROC_LIST(MustChk, 7),
- ?line Exit(S5),
- ?line ?CHK_TERM_PROC_LIST(MustChk, 6),
- ?line Exit(S7),
- ?line ?CHK_TERM_PROC_LIST(MustChk, 6),
- ?line Exit(S6),
- ?line ?CHK_TERM_PROC_LIST(MustChk, 0),
- ?line erlang:system_flag(multi_scheduling, unblock),
- ?line as_expected.
+ ?CHK_TERM_PROC_LIST(MustChk, 0),
+ HangAround = fun () -> receive after infinity -> ok end end,
+ HA1 = spawn_link(HangAround),
+ HA2 = spawn_link(HangAround),
+ HA3 = spawn_link(HangAround),
+ S1 = SpawnSuspendProcessesProc(),
+ ?CHK_TERM_PROC_LIST(MustChk, 1),
+ Exit(HA1),
+ ?CHK_TERM_PROC_LIST(MustChk, 2),
+ S2 = SpawnSuspendProcessesProc(),
+ ?CHK_TERM_PROC_LIST(MustChk, 3),
+ S3 = SpawnSuspendProcessesProc(),
+ ?CHK_TERM_PROC_LIST(MustChk, 4),
+ Exit(HA2),
+ ?CHK_TERM_PROC_LIST(MustChk, 5),
+ S4 = SpawnSuspendProcessesProc(),
+ ?CHK_TERM_PROC_LIST(MustChk, 6),
+ Exit(HA3),
+ ?CHK_TERM_PROC_LIST(MustChk, 7),
+ ResumeProcessesProc(S1),
+ ?CHK_TERM_PROC_LIST(MustChk, 5),
+ ResumeProcessesProc(S3),
+ ?CHK_TERM_PROC_LIST(MustChk, 4),
+ ResumeProcessesProc(S4),
+ ?CHK_TERM_PROC_LIST(MustChk, 3),
+ ResumeProcessesProc(S2),
+ ?CHK_TERM_PROC_LIST(MustChk, 0),
+ Exit(S1),
+ Exit(S2),
+ Exit(S3),
+ Exit(S4),
+
+
+ HA4 = spawn_link(HangAround),
+ HA5 = spawn_link(HangAround),
+ HA6 = spawn_link(HangAround),
+ S5 = SpawnSuspendProcessesProc(),
+ ?CHK_TERM_PROC_LIST(MustChk, 1),
+ Exit(HA4),
+ ?CHK_TERM_PROC_LIST(MustChk, 2),
+ S6 = SpawnSuspendProcessesProc(),
+ ?CHK_TERM_PROC_LIST(MustChk, 3),
+ Exit(HA5),
+ ?CHK_TERM_PROC_LIST(MustChk, 4),
+ S7 = SpawnSuspendProcessesProc(),
+ ?CHK_TERM_PROC_LIST(MustChk, 5),
+ Exit(HA6),
+ ?CHK_TERM_PROC_LIST(MustChk, 6),
+ S8 = SpawnSuspendProcessesProc(),
+ ?CHK_TERM_PROC_LIST(MustChk, 7),
+
+ erlang:system_flag(multi_scheduling, block),
+ Exit(S8),
+ ?CHK_TERM_PROC_LIST(MustChk, 7),
+ Exit(S5),
+ ?CHK_TERM_PROC_LIST(MustChk, 6),
+ Exit(S7),
+ ?CHK_TERM_PROC_LIST(MustChk, 6),
+ Exit(S6),
+ ?CHK_TERM_PROC_LIST(MustChk, 0),
+ erlang:system_flag(multi_scheduling, unblock),
+ as_expected.
otp_7738_waiting(doc) ->
@@ -2123,88 +2070,88 @@ otp_7738_waiting(doc) ->
otp_7738_waiting(suite) ->
[];
otp_7738_waiting(Config) when is_list(Config) ->
- ?line otp_7738_test(waiting).
+ otp_7738_test(waiting).
otp_7738_suspended(doc) ->
[];
otp_7738_suspended(suite) ->
[];
otp_7738_suspended(Config) when is_list(Config) ->
- ?line otp_7738_test(suspended).
+ otp_7738_test(suspended).
otp_7738_resume(doc) ->
[];
otp_7738_resume(suite) ->
[];
otp_7738_resume(Config) when is_list(Config) ->
- ?line otp_7738_test(resume).
+ otp_7738_test(resume).
otp_7738_test(Type) ->
- ?line T = self(),
- ?line S = spawn_link(fun () ->
- receive
- {suspend, Suspendee} ->
- erlang:suspend_process(Suspendee),
- T ! {suspended, Suspendee},
- receive
- after 10 ->
- erlang:resume_process(Suspendee),
- Suspendee ! wake_up
- end;
- {send, To, Msg} ->
- receive after 10 -> ok end,
- To ! Msg
- end
- end),
- ?line R = spawn_link(fun () ->
- X = lists:seq(1, 20000000),
- T ! {initialized, self()},
- ?line case Type of
- _ when Type == suspended;
- Type == waiting ->
- receive _ -> ok end;
- _ when Type == resume ->
- Receive = fun (F) ->
- receive
- _ ->
- ok
- after 0 ->
- F(F)
- end
- end,
- Receive(Receive)
- end,
- T ! {woke_up, self()},
- id(X)
- end),
- ?line receive {initialized, R} -> ok end,
- ?line receive after 10 -> ok end,
- ?line case Type of
+ T = self(),
+ S = spawn_link(fun () ->
+ receive
+ {suspend, Suspendee} ->
+ erlang:suspend_process(Suspendee),
+ T ! {suspended, Suspendee},
+ receive
+ after 10 ->
+ erlang:resume_process(Suspendee),
+ Suspendee ! wake_up
+ end;
+ {send, To, Msg} ->
+ receive after 10 -> ok end,
+ To ! Msg
+ end
+ end),
+ R = spawn_link(fun () ->
+ X = lists:seq(1, 20000000),
+ T ! {initialized, self()},
+ case Type of
+ _ when Type == suspended;
+ Type == waiting ->
+ receive _ -> ok end;
+ _ when Type == resume ->
+ Receive = fun (F) ->
+ receive
+ _ ->
+ ok
+ after 0 ->
+ F(F)
+ end
+ end,
+ Receive(Receive)
+ end,
+ T ! {woke_up, self()},
+ id(X)
+ end),
+ receive {initialized, R} -> ok end,
+ receive after 10 -> ok end,
+ case Type of
suspended ->
- ?line erlang:suspend_process(R),
- ?line S ! {send, R, wake_up};
+ erlang:suspend_process(R),
+ S ! {send, R, wake_up};
waiting ->
- ?line S ! {send, R, wake_up};
+ S ! {send, R, wake_up};
resume ->
- ?line S ! {suspend, R},
- ?line receive {suspended, R} -> ok end
+ S ! {suspend, R},
+ receive {suspended, R} -> ok end
end,
- ?line erlang:garbage_collect(R),
- ?line case Type of
+ erlang:garbage_collect(R),
+ case Type of
suspended ->
- ?line erlang:resume_process(R);
+ erlang:resume_process(R);
_ ->
- ?line ok
+ ok
end,
- ?line receive
+ receive
{woke_up, R} ->
- ?line ok
+ ok
after 2000 ->
- ?line I = process_info(R, [status, message_queue_len]),
- ?line ?t:format("~p~n", [I]),
- ?line ?t:fail(no_progress)
+ I = process_info(R, [status, message_queue_len]),
+ ?t:format("~p~n", [I]),
+ ?t:fail(no_progress)
end,
- ?line ok.
+ ok.
gor(Reds, Stop) ->
receive
@@ -2218,28 +2165,28 @@ gor(Reds, Stop) ->
end.
garb_other_running(Config) when is_list(Config) ->
- ?line Stop = make_ref(),
- ?line {Pid, Mon} = spawn_monitor(fun () -> gor(0, Stop) end),
- ?line Reds = lists:foldl(fun (_, OldReds) ->
- ?line erlang:garbage_collect(Pid),
- ?line receive after 1 -> ok end,
- ?line Pid ! {self(), reds},
- ?line receive
+ Stop = make_ref(),
+ {Pid, Mon} = spawn_monitor(fun () -> gor(0, Stop) end),
+ Reds = lists:foldl(fun (_, OldReds) ->
+ erlang:garbage_collect(Pid),
+ receive after 1 -> ok end,
+ Pid ! {self(), reds},
+ receive
{reds, NewReds, Pid} ->
- ?line true = (NewReds > OldReds),
- ?line NewReds
+ true = (NewReds > OldReds),
+ NewReds
end
end,
0,
lists:seq(1, 10000)),
- ?line receive after 1 -> ok end,
- ?line Pid ! {self(), Stop},
- ?line receive
+ receive after 1 -> ok end,
+ Pid ! {self(), Stop},
+ receive
{stopped, Stop, StopReds, Pid} ->
- ?line true = (StopReds > Reds)
+ true = (StopReds > Reds)
end,
- ?line receive {'DOWN', Mon, process, Pid, normal} -> ok end,
- ?line ok.
+ receive {'DOWN', Mon, process, Pid, normal} -> ok end,
+ ok.
%% Internal functions
@@ -2263,9 +2210,9 @@ start_node(Config) ->
start_node(Config, "").
start_node(Config, Args) when is_list(Config) ->
- ?line Pa = filename:dirname(code:which(?MODULE)),
- ?line {A, B, C} = now(),
- ?line Name = list_to_atom(atom_to_list(?MODULE)
+ Pa = filename:dirname(code:which(?MODULE)),
+ {A, B, C} = now(),
+ Name = list_to_atom(atom_to_list(?MODULE)
++ "-"
++ atom_to_list(?config(testcase, Config))
++ "-"
@@ -2274,7 +2221,7 @@ start_node(Config, Args) when is_list(Config) ->
++ integer_to_list(B)
++ "-"
++ integer_to_list(C)),
- ?line ?t:start_node(Name, slave, [{args, "-pa "++Pa++" "++Args}]).
+ ?t:start_node(Name, slave, [{args, "-pa "++Pa++" "++Args}]).
stop_node(Node) ->
?t:stop_node(Node).
diff --git a/erts/emulator/test/save_calls_SUITE.erl b/erts/emulator/test/save_calls_SUITE.erl
index 390b49b604..26ac4f2f7f 100644
--- a/erts/emulator/test/save_calls_SUITE.erl
+++ b/erts/emulator/test/save_calls_SUITE.erl
@@ -21,8 +21,10 @@
-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([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([save_calls_1/1,dont_break_reductions/1]).
@@ -48,6 +50,27 @@ init_per_group(_GroupName, Config) ->
end_per_group(_GroupName, Config) ->
Config.
+init_per_testcase(dont_break_reductions,Config) ->
+ %% Skip on --enable-native-libs as hipe rescedules after each
+ %% function call.
+ case erlang:system_info(hipe_architecture) of
+ undefined ->
+ Config;
+ Architecture ->
+ {lists, ListsBinary, _ListsFilename} = code:get_object_code(lists),
+ ChunkName = hipe_unified_loader:chunk_name(Architecture),
+ NativeChunk = beam_lib:chunks(ListsBinary, [ChunkName]),
+ case NativeChunk of
+ {ok,{_,[{_,Bin}]}} when is_binary(Bin) ->
+ {skip,"Does not work for --enable-native-libs"};
+ {error, beam_lib, _} -> Config
+ end
+ end;
+init_per_testcase(_,Config) ->
+ Config.
+
+end_per_testcase(_,_Config) ->
+ ok.
dont_break_reductions(suite) ->
[];
diff --git a/erts/emulator/test/send_term_SUITE_data/send_term_drv.c b/erts/emulator/test/send_term_SUITE_data/send_term_drv.c
index b3feca79f0..f8613487b0 100644
--- a/erts/emulator/test/send_term_SUITE_data/send_term_drv.c
+++ b/erts/emulator/test/send_term_SUITE_data/send_term_drv.c
@@ -664,7 +664,7 @@ static void send_term_drv_run(ErlDrvData port, char *buf, ErlDrvSizeT count)
/* Signal end of test case */
msg[0] = ERL_DRV_NIL;
- driver_output_term(erlang_port, msg, 1);
+ erl_drv_output_term(driver_mk_port(erlang_port), msg, 1);
return;
}
break;
@@ -687,14 +687,14 @@ static void send_term_drv_run(ErlDrvData port, char *buf, ErlDrvSizeT count)
static void output_term(ErlDrvTermData* msg, int len)
{
- if (driver_output_term(erlang_port, msg, len) <= 0) {
- driver_failure_atom(erlang_port, "driver_output_term_failed");
+ if (erl_drv_output_term(driver_mk_port(erlang_port), msg, len) <= 0) {
+ driver_failure_atom(erlang_port, "erl_drv_output_term_failed");
}
}
static void fail_term(ErlDrvTermData* msg, int len, int line)
{
- int status = driver_output_term(erlang_port, msg, len);
+ int status = erl_drv_output_term(driver_mk_port(erlang_port), msg, len);
if (status == 1) {
char buf[1024];
diff --git a/erts/emulator/test/smoke_test_SUITE.erl b/erts/emulator/test/smoke_test_SUITE.erl
index 98f1cf1ad5..6f5c2080c0 100644
--- a/erts/emulator/test/smoke_test_SUITE.erl
+++ b/erts/emulator/test/smoke_test_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011. All Rights Reserved.
+%% Copyright Ericsson AB 2011-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -51,7 +51,17 @@ end_per_group(_GroupName, Config) ->
Config.
+init_per_testcase(boot_combo = Case, Config) when is_list(Config) ->
+ case erlang:system_info(build_type) of
+ opt ->
+ init_per_tc(Case, Config);
+ _ ->
+ {skip,"Cannot test boot_combo in special builds since beam.* may not exist"}
+ end;
init_per_testcase(Case, Config) when is_list(Config) ->
+ init_per_tc(Case, Config).
+
+init_per_tc(Case, Config) ->
Dog = ?t:timetrap(?DEFAULT_TIMEOUT),
[{testcase, Case},{watchdog, Dog}|Config].
@@ -121,18 +131,19 @@ start_node(Config) ->
start_node(Config, "").
start_node(Config, Args) when is_list(Config) ->
- ?line Pa = filename:dirname(code:which(?MODULE)),
- ?line {A, B, C} = now(),
- ?line Name = 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)),
- ?line ?t:start_node(Name, slave, [{args, "-pa "++Pa++" "++Args}]).
+ Pa = filename:dirname(code:which(?MODULE)),
+ {A, B, C} = now(),
+ Name = 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)),
+ Opts = [{args, "-pa "++Pa++" "++Args}],
+ ?t:start_node(Name, slave, Opts).
stop_node(Node) ->
?t:stop_node(Node).
diff --git a/erts/emulator/test/timer_bif_SUITE.erl b/erts/emulator/test/timer_bif_SUITE.erl
index 7ff7449ff5..c9533d0748 100644
--- a/erts/emulator/test/timer_bif_SUITE.erl
+++ b/erts/emulator/test/timer_bif_SUITE.erl
@@ -71,38 +71,23 @@ end_per_group(_GroupName, Config) ->
start_timer_1(doc) -> ["Basic start_timer/3 functionality"];
start_timer_1(Config) when is_list(Config) ->
- ?line Ref1 = erlang:start_timer(1000, self(), plopp),
- ?line ok = get(1100, {timeout, Ref1, plopp}),
-
- ?line false = erlang:read_timer(Ref1),
- ?line false = erlang:cancel_timer(Ref1),
- ?line false = erlang:read_timer(Ref1),
-
- ?line Ref2 = erlang:start_timer(1000, self(), plapp),
- ?line Left2 = erlang:cancel_timer(Ref2),
- UpperLimit = case os:type() of
- vxworks ->
- %% The ticks of vxworks have a far lesser granularity
- %% than what is expected in this testcase, in
- %% fact the Left2 variable can get a little more than 1000...
- 1100;
- _ ->
- 1000
- end,
- ?line RetVal = case os:type() of
- vxworks ->
- {comment, "VxWorks behaves slightly unexpected, should be fixed,"};
- _ ->
- ok
- end,
- ?line true = (Left2 > 900) and (Left2 =< UpperLimit),
- ?line empty = get_msg(),
- ?line false = erlang:cancel_timer(Ref2),
-
- ?line Ref3 = erlang:start_timer(1000, self(), plopp),
- ?line no_message = get(900, {timeout, Ref3, plopp}),
-
- RetVal.
+ Ref1 = erlang:start_timer(1000, self(), plopp),
+ ok = get(1100, {timeout, Ref1, plopp}),
+
+ false = erlang:read_timer(Ref1),
+ false = erlang:cancel_timer(Ref1),
+ false = erlang:read_timer(Ref1),
+
+ Ref2 = erlang:start_timer(1000, self(), plapp),
+ Left2 = erlang:cancel_timer(Ref2),
+ UpperLimit = 1000,
+ true = (Left2 > 900) and (Left2 =< UpperLimit),
+ empty = get_msg(),
+ false = erlang:cancel_timer(Ref2),
+
+ Ref3 = erlang:start_timer(1000, self(), plopp),
+ no_message = get(900, {timeout, Ref3, plopp}),
+ ok.
send_after_1(doc) -> ["Basic send_after/3 functionality"];
send_after_1(Config) when is_list(Config) ->
@@ -153,19 +138,11 @@ send_after_2(Config) when is_list(Config) ->
send_after_3(doc) -> ["send_after/3: messages in the right order, worse than send_after_2"];
send_after_3(Config) when is_list(Config) ->
- case os:type() of
- vxworks ->
- {skipped, "VxWorks timer granularity and order is not working good, this is subject to change!"};
- _ ->
- do_send_after_3()
- end.
-
-do_send_after_3() ->
- ?line _ = erlang:send_after(100, self(), b1),
- ?line _ = erlang:send_after(101, self(), b2),
- ?line _ = erlang:send_after(102, self(), b3),
- ?line _ = erlang:send_after(103, self(), last),
- ?line [b1, b2, b3, last] = collect(last),
+ _ = erlang:send_after(100, self(), b1),
+ _ = erlang:send_after(101, self(), b2),
+ _ = erlang:send_after(102, self(), b3),
+ _ = erlang:send_after(103, self(), last),
+ [b1, b2, b3, last] = collect(last),
% This behaviour is not guaranteed:
% ?line _ = erlang:send_after(100, self(), c1),
diff --git a/erts/emulator/test/trace_local_SUITE.erl b/erts/emulator/test/trace_local_SUITE.erl
index b238d5c630..1e0705fabe 100644
--- a/erts/emulator/test/trace_local_SUITE.erl
+++ b/erts/emulator/test/trace_local_SUITE.erl
@@ -70,7 +70,8 @@ config(priv_dir,_) ->
-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, bit_syntax/1,
- return/1, on_and_off/1, stack_grow/1,info/1, delete/1,
+ return/1, on_and_off/1, systematic_on_off/1,
+ stack_grow/1,info/1, delete/1,
exception/1, exception_apply/1,
exception_function/1, exception_apply_function/1,
exception_nocatch/1, exception_nocatch_apply/1,
@@ -105,7 +106,8 @@ all() ->
case test_server:is_native(trace_local_SUITE) of
true -> [not_run];
false ->
- [basic, bit_syntax, return, on_and_off, stack_grow,
+ [basic, bit_syntax, return, on_and_off, systematic_on_off,
+ stack_grow,
info, delete, exception, exception_apply,
exception_function, exception_apply_function,
exception_nocatch, exception_nocatch_apply,
@@ -583,7 +585,118 @@ on_and_off_test() ->
end,
?line ?NM,
ok.
-
+
+systematic_on_off(Config) when is_list(Config) ->
+ setup([call]),
+ Local = combinations([local,meta,call_count,call_time]),
+ [systematic_on_off_1(Flags) || Flags <- Local],
+
+ %% Make sure that we don't get any trace messages when trace
+ %% is supposed to be off.
+ receive_no_next(500).
+
+systematic_on_off_1(Local) ->
+ io:format("~p\n", [Local]),
+
+ %% Global off.
+ verify_trace_info(false, []),
+ 1 = erlang:trace_pattern({?MODULE,exported_wrap,1}, true, Local),
+ verify_trace_info(false, Local),
+ 1 = erlang:trace_pattern({?MODULE,exported_wrap,1}, false, [global]),
+ verify_trace_info(false, Local),
+ 1 = erlang:trace_pattern({?MODULE,exported_wrap,1}, false, Local),
+ verify_trace_info(false, []),
+
+ %% Global on.
+ 1 = erlang:trace_pattern({?MODULE,exported_wrap,1}, true, [global]),
+ verify_trace_info(true, []),
+ 1 = erlang:trace_pattern({?MODULE,exported_wrap,1}, false, Local),
+ verify_trace_info(true, []),
+ 1 = erlang:trace_pattern({?MODULE,exported_wrap,1}, false, [global]),
+ verify_trace_info(false, []),
+
+ %% Implicitly turn off global call trace.
+ 1 = erlang:trace_pattern({?MODULE,exported_wrap,1}, true, [global]),
+ verify_trace_info(true, []),
+ 1 = erlang:trace_pattern({?MODULE,exported_wrap,1}, true, Local),
+ verify_trace_info(false, Local),
+
+ %% Implicitly turn off local call trace.
+ 1 = erlang:trace_pattern({?MODULE,exported_wrap,1}, true, [global]),
+ verify_trace_info(true, []),
+
+ %% Turn off global call trace. Everything should be off now.
+ 1 = erlang:trace_pattern({?MODULE,exported_wrap,1}, false, [global]),
+ verify_trace_info(false, []),
+
+ ok.
+
+verify_trace_info(Global, Local) ->
+ case erlang:trace_info({?MODULE,exported_wrap,1}, all) of
+ {all,false} ->
+ false = Global,
+ [] = Local;
+ {all,Ps} ->
+ io:format("~p\n", [Ps]),
+ [verify_trace_info(P, Global, Local) || P <- Ps]
+ end,
+ global_call(Global, Local),
+ local_call(Local),
+ ok.
+
+verify_trace_info({traced,global}, true, []) -> ok;
+verify_trace_info({traced,local}, false, _) -> ok;
+verify_trace_info({match_spec,[]}, _, _) -> ok;
+verify_trace_info({meta_match_spec,[]}, _, _) -> ok;
+verify_trace_info({LocalFlag,Bool}, _, Local) when is_boolean(Bool) ->
+ try
+ Bool = lists:member(LocalFlag, Local)
+ catch
+ error:_ ->
+ io:format("Line ~p: {~p,~p}, false, ~p\n",
+ [?LINE,LocalFlag,Bool,Local]),
+ ?t:fail()
+ end;
+verify_trace_info({meta,Pid}, false, Local) when is_pid(Pid) ->
+ true = lists:member(meta, Local);
+verify_trace_info({call_time,_}, false, Local) ->
+ true = lists:member(call_time, Local);
+verify_trace_info({call_count,_}, false, Local) ->
+ true = lists:member(call_time, Local).
+
+global_call(Global, Local) ->
+ apply_slave(?MODULE, exported_wrap, [global_call]),
+ case Global of
+ false ->
+ recv_local_call(Local, [global_call]);
+ true ->
+ ?CT(?MODULE, exported_wrap, [global_call])
+ end.
+
+local_call(Local) ->
+ lambda_slave(fun() -> exported_wrap(local_call) end),
+ recv_local_call(Local, [local_call]).
+
+recv_local_call(Local, Args) ->
+ case lists:member(local, Local) of
+ false ->
+ ok;
+ true ->
+ ?CT(?MODULE, exported_wrap, Args)
+ end,
+ case lists:member(meta, Local) of
+ false ->
+ ok;
+ true ->
+ ?CTT(?MODULE, exported_wrap, Args)
+ end,
+ ok.
+
+combinations([_]=One) ->
+ [One];
+combinations([H|T]) ->
+ Cs = combinations(T),
+ [[H|C] || C <- Cs] ++ Cs.
stack_grow_test() ->
?line setup([call,return_to]),
@@ -714,16 +827,10 @@ exception_test(Opts) ->
?line ok.
exceptions() ->
- ?line Ref = make_ref(),
- ?line N = case os:type() of
- vxworks ->
- ?line 2000; % Limited memory on themachines, not actually
- % VxWorks' fault /PaN
- _ ->
- ?line 200000
- end,
- ?line LiL = seq(1, N-1, N), % Long Improper List
- ?line LL = seq(1, N, []), % Long List
+ Ref = make_ref(),
+ N = 200000,
+ LiL = seq(1, N-1, N), % Long Improper List
+ LL = seq(1, N, []), % Long List
[{{erlang,exit}, [done]},
{{erlang,error}, [1.0]},
{{erlang,error}, [Ref,[]]},
diff --git a/erts/emulator/test/trace_port_SUITE.erl b/erts/emulator/test/trace_port_SUITE.erl
index f81cab3114..cc2eadafbc 100644
--- a/erts/emulator/test/trace_port_SUITE.erl
+++ b/erts/emulator/test/trace_port_SUITE.erl
@@ -472,14 +472,9 @@ default_tracer(Config) when is_list(Config) ->
?line M = N,
ok.
-
%%% Help functions.
-huge_data() ->
- case os:type() of
- vxworks -> huge_data(4711);
- _ -> huge_data(16384)
- end.
+huge_data() -> huge_data(16384).
huge_data(0) -> [];
huge_data(N) when N rem 2 == 0 ->
P = huge_data(N div 2),
diff --git a/erts/emulator/test/tuple_SUITE.erl b/erts/emulator/test/tuple_SUITE.erl
index bfc3910742..a3b2764a5d 100644
--- a/erts/emulator/test/tuple_SUITE.erl
+++ b/erts/emulator/test/tuple_SUITE.erl
@@ -20,6 +20,7 @@
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2,
t_size/1, t_tuple_size/1, t_element/1, t_setelement/1,
+ t_insert_element/1, t_delete_element/1,
t_list_to_tuple/1, t_tuple_to_list/1,
t_make_tuple_2/1, t_make_tuple_3/1, t_append_element/1,
build_and_match/1, tuple_with_case/1, tuple_in_guard/1]).
@@ -41,6 +42,7 @@ all() ->
[build_and_match, t_size, t_tuple_size, t_list_to_tuple,
t_tuple_to_list, t_element, t_setelement,
t_make_tuple_2, t_make_tuple_3, t_append_element,
+ t_insert_element, t_delete_element,
tuple_with_case, tuple_in_guard].
groups() ->
@@ -60,40 +62,40 @@ end_per_group(_GroupName, Config) ->
build_and_match(Config) when is_list(Config) ->
- ?line {} = id({}),
- ?line {1} = id({1}),
- ?line {1, 2} = id({1, 2}),
- ?line {1, 2, 3} = id({1, 2, 3}),
- ?line {1, 2, 3, 4} = id({1, 2, 3, 4}),
- ?line {1, 2, 3, 4, 5} = id({1, 2, 3, 4, 5}),
- ?line {1, 2, 3, 4, 5, 6} = id({1, 2, 3, 4, 5, 6}),
- ?line {1, 2, 3, 4, 5, 6} = id({1, 2, 3, 4, 5, 6}),
- ?line {1, 2, 3, 4, 5, 6, 7} = id({1, 2, 3, 4, 5, 6, 7}),
- ?line {1, 2, 3, 4, 5, 6, 7, 8} = id({1, 2, 3, 4, 5, 6, 7, 8}),
+ {} = id({}),
+ {1} = id({1}),
+ {1, 2} = id({1, 2}),
+ {1, 2, 3} = id({1, 2, 3}),
+ {1, 2, 3, 4} = id({1, 2, 3, 4}),
+ {1, 2, 3, 4, 5} = id({1, 2, 3, 4, 5}),
+ {1, 2, 3, 4, 5, 6} = id({1, 2, 3, 4, 5, 6}),
+ {1, 2, 3, 4, 5, 6} = id({1, 2, 3, 4, 5, 6}),
+ {1, 2, 3, 4, 5, 6, 7} = id({1, 2, 3, 4, 5, 6, 7}),
+ {1, 2, 3, 4, 5, 6, 7, 8} = id({1, 2, 3, 4, 5, 6, 7, 8}),
ok.
%% Tests size(Tuple).
t_size(Config) when is_list(Config) ->
- ?line 0 = size({}),
- ?line 1 = size({a}),
- ?line 1 = size({{a}}),
- ?line 2 = size({{a}, {b}}),
- ?line 3 = size({1, 2, 3}),
+ 0 = size({}),
+ 1 = size({a}),
+ 1 = size({{a}}),
+ 2 = size({{a}, {b}}),
+ 3 = size({1, 2, 3}),
ok.
t_tuple_size(Config) when is_list(Config) ->
- ?line 0 = tuple_size(id({})),
- ?line 1 = tuple_size(id({a})),
- ?line 1 = tuple_size(id({{a}})),
- ?line 2 = tuple_size(id({{a},{b}})),
- ?line 3 = tuple_size(id({1,2,3})),
+ 0 = tuple_size(id({})),
+ 1 = tuple_size(id({a})),
+ 1 = tuple_size(id({{a}})),
+ 2 = tuple_size(id({{a},{b}})),
+ 3 = tuple_size(id({1,2,3})),
%% Error cases.
- ?line {'EXIT',{badarg,_}} = (catch tuple_size([])),
- ?line {'EXIT',{badarg,_}} = (catch tuple_size(<<1,2,3>>)),
- ?line error = ludicrous_tuple_size({a,b,c}),
- ?line error = ludicrous_tuple_size([a,b,c]),
+ {'EXIT',{badarg,_}} = (catch tuple_size([])),
+ {'EXIT',{badarg,_}} = (catch tuple_size(<<1,2,3>>)),
+ error = ludicrous_tuple_size({a,b,c}),
+ error = ludicrous_tuple_size([a,b,c]),
ok.
@@ -104,44 +106,44 @@ ludicrous_tuple_size(_) -> error.
%% Tests element/2.
t_element(Config) when is_list(Config) ->
- ?line a = element(1, {a}),
- ?line a = element(1, {a, b}),
+ a = element(1, {a}),
+ a = element(1, {a, b}),
- ?line List = lists:seq(1, 4096),
- ?line Tuple = list_to_tuple(lists:seq(1, 4096)),
- ?line get_elements(List, Tuple, 1),
+ List = lists:seq(1, 4096),
+ Tuple = list_to_tuple(lists:seq(1, 4096)),
+ get_elements(List, Tuple, 1),
- ?line {'EXIT', {badarg, _}} = (catch element(0, id({a,b}))),
- ?line {'EXIT', {badarg, _}} = (catch element(3, id({a,b}))),
- ?line {'EXIT', {badarg, _}} = (catch element(1, id({}))),
- ?line {'EXIT', {badarg, _}} = (catch element(1, id([a,b]))),
- ?line {'EXIT', {badarg, _}} = (catch element(1, id(42))),
- ?line {'EXIT', {badarg, _}} = (catch element(id(1.5), id({a,b}))),
+ {'EXIT', {badarg, _}} = (catch element(0, id({a,b}))),
+ {'EXIT', {badarg, _}} = (catch element(3, id({a,b}))),
+ {'EXIT', {badarg, _}} = (catch element(1, id({}))),
+ {'EXIT', {badarg, _}} = (catch element(1, id([a,b]))),
+ {'EXIT', {badarg, _}} = (catch element(1, id(42))),
+ {'EXIT', {badarg, _}} = (catch element(id(1.5), id({a,b}))),
ok.
get_elements([Element|Rest], Tuple, Pos) ->
- ?line Element = element(Pos, Tuple),
- ?line get_elements(Rest, Tuple, Pos+1);
+ Element = element(Pos, Tuple),
+ get_elements(Rest, Tuple, Pos+1);
get_elements([], _Tuple, _Pos) ->
ok.
%% Tests set_element/3.
t_setelement(Config) when is_list(Config) ->
- ?line {x} = setelement(1, id({1}), x),
- ?line {x,2} = setelement(1, id({1,2}), x),
- ?line {1,x} = setelement(2, id({1,2}), x),
+ {x} = setelement(1, id({1}), x),
+ {x,2} = setelement(1, id({1,2}), x),
+ {1,x} = setelement(2, id({1,2}), x),
- ?line Tuple = list_to_tuple(lists:duplicate(2048, x)),
- ?line NewTuple = set_all_elements(Tuple, 1),
- ?line NewTuple = list_to_tuple(lists:seq(1+7, 2048+7)),
+ Tuple = list_to_tuple(lists:duplicate(2048, x)),
+ NewTuple = set_all_elements(Tuple, 1),
+ NewTuple = list_to_tuple(lists:seq(1+7, 2048+7)),
- ?line {'EXIT', {badarg, _}} = (catch setelement(0, {a, b}, x)),
- ?line {'EXIT', {badarg, _}} = (catch setelement(3, {a, b}, x)),
- ?line {'EXIT', {badarg, _}} = (catch setelement(1, {}, x)),
- ?line {'EXIT', {badarg, _}} = (catch setelement(1, [a, b], x)),
- ?line {'EXIT', {badarg, _}} = (catch setelement(1.5, {a, b}, x)),
+ {'EXIT', {badarg, _}} = (catch setelement(0, {a, b}, x)),
+ {'EXIT', {badarg, _}} = (catch setelement(3, {a, b}, x)),
+ {'EXIT', {badarg, _}} = (catch setelement(1, {}, x)),
+ {'EXIT', {badarg, _}} = (catch setelement(1, [a, b], x)),
+ {'EXIT', {badarg, _}} = (catch setelement(1.5, {a, b}, x)),
%% Nested setelement with literals.
AnotherTuple = id({0,0,a,b,c}),
@@ -159,52 +161,68 @@ set_all_elements(Tuple, Pos) when Pos > size(Tuple) ->
%% Tests list_to_tuple/1.
t_list_to_tuple(Config) when is_list(Config) ->
- ?line {} = list_to_tuple([]),
- ?line {a} = list_to_tuple([a]),
- ?line {a, b} = list_to_tuple([a, b]),
- ?line {a, b, c} = list_to_tuple([a, b, c]),
- ?line {a, b, c, d} = list_to_tuple([a, b, c, d]),
- ?line {a, b, c, d, e} = list_to_tuple([a, b, c, d, e]),
-
- ?line Size = 4096,
- ?line Tuple = list_to_tuple(lists:seq(1, Size)),
- ?line Size = size(Tuple),
-
- ?line {'EXIT', {badarg, _}} = (catch list_to_tuple(id({a,b}))),
- ?line {'EXIT', {badarg, _}} = (catch list_to_tuple(id([a|b]))),
- ?line {'EXIT', {badarg, _}} = (catch list_to_tuple(id([a|b]))),
-
+ {} = list_to_tuple([]),
+ {a} = list_to_tuple([a]),
+ {a, b} = list_to_tuple([a, b]),
+ {a, b, c} = list_to_tuple([a, b, c]),
+ {a, b, c, d} = list_to_tuple([a, b, c, d]),
+ {a, b, c, d, e} = list_to_tuple([a, b, c, d, e]),
+
+ Size = 4096,
+ Tuple = list_to_tuple(lists:seq(1, Size)),
+ Size = size(Tuple),
+
+ {'EXIT', {badarg, _}} = (catch list_to_tuple(id({a,b}))),
+ {'EXIT', {badarg, _}} = (catch list_to_tuple(id([a|b]))),
+ {'EXIT', {badarg, _}} = (catch list_to_tuple(id([a|b]))),
+
+ % test upper boundry, 16777215 elements
+ MaxSize = 1 bsl 24 - 1,
+ MaxTuple = list_to_tuple(lists:seq(1, MaxSize)),
+ MaxSize = size(MaxTuple),
+
+ {'EXIT', {badarg,_}} = (catch list_to_tuple(lists:seq(1, 1 bsl 24))),
ok.
%% Tests tuple_to_list/1.
t_tuple_to_list(Config) when is_list(Config) ->
- ?line [] = tuple_to_list({}),
- ?line [a] = tuple_to_list({a}),
- ?line [a, b] = tuple_to_list({a, b}),
- ?line [a, b, c] = tuple_to_list({a, b, c}),
- ?line [a, b, c, d] = tuple_to_list({a, b, c, d}),
- ?line [a, b, c, d] = tuple_to_list({a, b, c, d}),
-
- ?line Size = 4096,
- ?line List = lists:seq(1, Size),
- ?line Tuple = list_to_tuple(List),
- ?line Size = size(Tuple),
- ?line List = tuple_to_list(Tuple),
-
- ?line {'EXIT', {badarg,_}} = (catch tuple_to_list(id(a))),
- ?line {'EXIT', {badarg,_}} = (catch tuple_to_list(id(42))),
+ [] = tuple_to_list({}),
+ [a] = tuple_to_list({a}),
+ [a, b] = tuple_to_list({a, b}),
+ [a, b, c] = tuple_to_list({a, b, c}),
+ [a, b, c, d] = tuple_to_list({a, b, c, d}),
+ [a, b, c, d] = tuple_to_list({a, b, c, d}),
+
+ Size = 4096,
+ List = lists:seq(1, Size),
+ Tuple = list_to_tuple(List),
+ Size = size(Tuple),
+ List = tuple_to_list(Tuple),
+
+ {'EXIT', {badarg,_}} = (catch tuple_to_list(id(a))),
+ {'EXIT', {badarg,_}} = (catch tuple_to_list(id(42))),
ok.
%% Tests the make_tuple/2 BIF.
t_make_tuple_2(Config) when is_list(Config) ->
- ?line t_make_tuple1([]),
- ?line t_make_tuple1(42),
- ?line t_make_tuple1(a),
- ?line t_make_tuple1({}),
- ?line t_make_tuple1({a}),
- ?line t_make_tuple1(erlang:make_tuple(400, [])),
+ t_make_tuple1([]),
+ t_make_tuple1(42),
+ t_make_tuple1(a),
+ t_make_tuple1({}),
+ t_make_tuple1({a}),
+ t_make_tuple1(erlang:make_tuple(400, [])),
+
+ % test upper boundry, 16777215 elements
+ t_make_tuple(1 bsl 24 - 1, a),
+ {'EXIT', {badarg,_}} = (catch erlang:make_tuple(1 bsl 24, a)),
+
+ {'EXIT', {badarg,_}} = (catch erlang:make_tuple(-1, a)),
+ % 26 bits is the total header arity room (for now)
+ {'EXIT', {badarg,_}} = (catch erlang:make_tuple(1 bsl 26 + 3, a)),
+ % bignum
+ {'EXIT', {badarg,_}} = (catch erlang:make_tuple(1 bsl 65 + 3, a)),
ok.
t_make_tuple1(Element) ->
@@ -222,29 +240,82 @@ t_make_tuple(Size, Element) ->
%% Tests the erlang:make_tuple/3 BIF.
t_make_tuple_3(Config) when is_list(Config) ->
- ?line {} = erlang:make_tuple(0, def, []),
- ?line {def} = erlang:make_tuple(1, def, []),
- ?line {a} = erlang:make_tuple(1, def, [{1,a}]),
- ?line {a,def,c,def,e} = erlang:make_tuple(5, def, [{5,e},{1,a},{3,c}]),
- ?line {a,def,c,def,e} = erlang:make_tuple(5, def,
- [{1,blurf},{5,e},{3,blurf},
- {1,a},{3,c}]),
+ {} = erlang:make_tuple(0, def, []),
+ {def} = erlang:make_tuple(1, def, []),
+ {a} = erlang:make_tuple(1, def, [{1,a}]),
+
+ {a,def,c,def,e} = erlang:make_tuple(5, def, [{5,e},{1,a},{3,c}]),
+ {a,def,c,def,e} = erlang:make_tuple(5, def, [{1,blurf},{5,e},{3,blurf},{1,a},{3,c}]),
+ MaxSize = 1 bsl 16 - 1,
+ MaxTuple = erlang:make_tuple(MaxSize, def, [{1,blurf},{5,e},{3,blurf},{1,a},{3,c}]),
+ MaxSize = size(MaxTuple),
+
+ %% Error cases.
+ {'EXIT',{badarg,_}} = (catch erlang:make_tuple(0, def, [{1,a}])),
+ {'EXIT',{badarg,_}} = (catch erlang:make_tuple(5, def, [{-1,a}])),
+ {'EXIT',{badarg,_}} = (catch erlang:make_tuple(5, def, [{0,a}])),
+ {'EXIT',{badarg,_}} = (catch erlang:make_tuple(5, def, [{6,z}])),
+ {'EXIT',{badarg,_}} = (catch erlang:make_tuple(a, def, [{6,z}])),
+ {'EXIT',{badarg,_}} = (catch erlang:make_tuple(5, def, [{1,a}|b])),
+ {'EXIT',{badarg,_}} = (catch erlang:make_tuple(5, def, [42])),
+ {'EXIT',{badarg,_}} = (catch erlang:make_tuple(5, def, [[a,b,c]])),
+ {'EXIT',{badarg,_}} = (catch erlang:make_tuple(5, def, non_list)),
+ {'EXIT',{badarg,_}} = (catch erlang:make_tuple(1 bsl 24, def, [{5,e},{1,a},{3,c}])),
+
+ ok.
+
+%% Tests the erlang:insert_element/3 BIF.
+t_insert_element(Config) when is_list(Config) ->
+ {a} = erlang:insert_element(1, {}, a),
+ {{b,b},a} = erlang:insert_element(1, {a}, {b,b}),
+ {a,b} = erlang:insert_element(2, {a}, b),
+ [b,def|_] = tuple_to_list(erlang:insert_element(1, erlang:make_tuple(1 bsl 20, def), b)),
+ [def,b|_] = tuple_to_list(erlang:insert_element(2, erlang:make_tuple(1 bsl 20, def), b)),
+ [def,b|_] = lists:reverse(tuple_to_list(erlang:insert_element(1 bsl 20, erlang:make_tuple(1 bsl 20, def), b))),
+ [b,def|_] = lists:reverse(tuple_to_list(erlang:insert_element((1 bsl 20) + 1, erlang:make_tuple(1 bsl 20, def), b))),
+
+ %% Error cases.
+ {'EXIT',{badarg,_}} = (catch erlang:insert_element(1, [], a)),
+ {'EXIT',{badarg,_}} = (catch erlang:insert_element(1, a, a)),
+ {'EXIT',{badarg,_}} = (catch erlang:insert_element(0, {}, a)),
+ {'EXIT',{badarg,_}} = (catch erlang:insert_element(0, {b,b,b,b,b}, a)),
+ {'EXIT',{badarg,_}} = (catch erlang:insert_element(-1, {}, a)),
+ {'EXIT',{badarg,_}} = (catch erlang:insert_element(2, {}, a)),
+ {'EXIT',{badarg,_}} = (catch erlang:insert_element(6, {b,b,b,b}, a)),
+ {'EXIT',{badarg,_}} = (catch erlang:insert_element(1 bsl 20, {b,b,b,b}, a)),
+ ok.
+
+%% Tests the erlang:delete_element/3 BIF.
+t_delete_element(Config) when is_list(Config) ->
+ {} = erlang:delete_element(1, {a}),
+ {{b,b},c} = erlang:delete_element(1, {a,{b,b},c}),
+ {a,b} = erlang:delete_element(2, {a,c,b}),
+ [2,3|_] = tuple_to_list(erlang:delete_element(1, list_to_tuple(lists:seq(1, 1 bsl 20)))),
+ [1,3|_] = tuple_to_list(erlang:delete_element(2, list_to_tuple(lists:seq(1, 1 bsl 20)))),
+ [(1 bsl 20) - 1, (1 bsl 20) - 2 |_] = lists:reverse(tuple_to_list(erlang:delete_element(1 bsl 20, list_to_tuple(lists:seq(1, 1 bsl 20))))),
+ [(1 bsl 20), (1 bsl 20) - 2 |_] = lists:reverse(tuple_to_list(erlang:delete_element((1 bsl 20) - 1, list_to_tuple(lists:seq(1, 1 bsl 20))))),
%% Error cases.
- ?line {'EXIT',{badarg,_}} = (catch erlang:make_tuple(0, def, [{1,a}])),
- ?line {'EXIT',{badarg,_}} = (catch erlang:make_tuple(5, def, [{-1,a}])),
- ?line {'EXIT',{badarg,_}} = (catch erlang:make_tuple(5, def, [{0,a}])),
- ?line {'EXIT',{badarg,_}} = (catch erlang:make_tuple(5, def, [{6,z}])),
- ?line {'EXIT',{badarg,_}} = (catch erlang:make_tuple(a, def, [{6,z}])),
- ?line {'EXIT',{badarg,_}} = (catch erlang:make_tuple(5, def, [{1,a}|b])),
- ?line {'EXIT',{badarg,_}} = (catch erlang:make_tuple(5, def, [42])),
- ?line {'EXIT',{badarg,_}} = (catch erlang:make_tuple(5, def, [[a,b,c]])),
- ?line {'EXIT',{badarg,_}} = (catch erlang:make_tuple(5, def, non_list)),
+ {'EXIT',{badarg,_}} = (catch erlang:delete_element(1, [])),
+ {'EXIT',{badarg,_}} = (catch erlang:delete_element(1, a)),
+ {'EXIT',{badarg,_}} = (catch erlang:delete_element(0, {})),
+ {'EXIT',{badarg,_}} = (catch erlang:delete_element(-1, {})),
+ {'EXIT',{badarg,_}} = (catch erlang:delete_element(1, {})),
+ {'EXIT',{badarg,_}} = (catch erlang:delete_element(0, {b,b,b,b,b})),
+ {'EXIT',{badarg,_}} = (catch erlang:delete_element(5, {b,b,b,b})),
+ {'EXIT',{badarg,_}} = (catch erlang:delete_element(1 bsl 20, {b,c,b,b,b})),
ok.
+
%% Tests the append_element/2 BIF.
t_append_element(Config) when is_list(Config) ->
- t_append_element({}, 2048, 2048).
+ ok = t_append_element({}, 2048, 2048),
+
+ % test upper boundry, 16777215 elements
+ MaxSize = 1 bsl 24 - 1,
+ MaxTuple = list_to_tuple(lists:seq(1, MaxSize)),
+ {'EXIT',{badarg,_}} = (catch erlang:append_element(MaxTuple, a)),
+ ok.
t_append_element(_Tuple, 0, _High) -> ok;
t_append_element(Tuple, N, High) ->
@@ -261,7 +332,7 @@ verify_seq([High|T], High, Lower) ->
%% (This is known to crash earlier versions of BEAM.)
tuple_with_case(Config) when is_list(Config) ->
- ?line {reply, true} = tuple_with_case(),
+ {reply, true} = tuple_with_case(),
ok.
tuple_with_case() ->
@@ -280,21 +351,21 @@ foo() -> ignored.
%% Test to build a tuple in a guard.
tuple_in_guard(Config) when is_list(Config) ->
- ?line Tuple1 = id({a,b}),
- ?line Tuple2 = id({a,b,c}),
- ?line if
- Tuple1 == {element(1, Tuple2),element(2, Tuple2)} ->
- ok;
- true ->
- ?line test_server:fail()
- end,
- ?line if
- Tuple2 == {element(1, Tuple2),element(2, Tuple2),
- element(3, Tuple2)} ->
- ok;
- true ->
- ?line test_server:fail()
- end,
+ Tuple1 = id({a,b}),
+ Tuple2 = id({a,b,c}),
+ if
+ Tuple1 == {element(1, Tuple2),element(2, Tuple2)} ->
+ ok;
+ true ->
+ test_server:fail()
+ end,
+ if
+ Tuple2 == {element(1, Tuple2),element(2, Tuple2),
+ element(3, Tuple2)} ->
+ ok;
+ true ->
+ test_server:fail()
+ end,
ok.
%% Use this function to avoid compile-time evaluation of an expression.
diff --git a/erts/emulator/utils/beam_makeops b/erts/emulator/utils/beam_makeops
index 8fe2402ca8..16a949c2a6 100755
--- a/erts/emulator/utils/beam_makeops
+++ b/erts/emulator/utils/beam_makeops
@@ -1,4 +1,4 @@
-#!/usr/bin/env perl
+#!/usr/bin/env perl -W
#
# %CopyrightBegin%
#
@@ -362,7 +362,7 @@ while (<>) {
$gen_to_spec{"$name/$arity"} = undef;
$num_specific{"$name/$arity"} = 0;
$min_window{"$name/$arity"} = 255;
- $obsolete[$op_num] = $obsolete eq '-';
+ $obsolete[$op_num] = defined $obsolete;
} else { # Unnumbered generic operation.
push(@unnumbered_generic, [$name, $arity]);
$unnumbered{$name,$arity} = 1;
@@ -379,7 +379,7 @@ while (<>) {
if @args > $max_spec_operands;
&syntax_check($name, @args);
my $arity = @args;
- if ($obsolete[$gen_opnum{$name,$arity}]) {
+ if (defined $gen_opnum{$name,$arity} and $obsolete[$gen_opnum{$name,$arity}]) {
error("specific instructions may not be specified for obsolete instructions");
}
push(@{$specific_op{"$name/$arity"}}, [$name, $hot, @args]);
@@ -810,8 +810,8 @@ sub compiler_output {
#
# Generate .hrl file.
#
- my($name) = "$outdir/${module}.hrl";
- open(STDOUT, ">$name") || die "Failed to open $name for writing: $!\n";
+ my($hrl_name) = "$outdir/${module}.hrl";
+ open(STDOUT, ">$hrl_name") || die "Failed to open $hrl_name for writing: $!\n";
&comment('erlang');
for ($i = 0; $i < @tag_type && $i < 8; $i++) {
@@ -1251,8 +1251,8 @@ sub compile_transform {
$arity++ unless $list[1] eq '*';
$_ = [ @list ];
}
-
- if ($obsolete[$gen_opnum{$name,$arity}]) {
+
+ if (defined $gen_opnum{$name,$arity} && $obsolete[$gen_opnum{$name,$arity}]) {
error("obsolete function must not be used in transformations");
}
@@ -1704,14 +1704,15 @@ sub tr_gen_to {
#
my($first_ref) = shift(@code);
my($size, $first, $key) = @$first_ref;
- my($dummy, $op, $arity) = @$first;
+ my($dummy, $arity);
+ ($dummy, $op, $arity) = @$first;
my($comment) = "\n/*\n * Line $line:\n * $orig_transform\n */\n\n";
$min_window{$key} = $min_window
if $min_window{$key} > $min_window;
my $prev_last;
$prev_last = pop(@{$gen_transform{$key}})
- if defined @{$gen_transform{$key}}; # Fail
+ if defined $gen_transform{$key}; # Fail
if ($prev_last && !is_instr($prev_last, 'fail')) {
error("Line $line: A previous transformation shadows '$orig_transform'");
@@ -1719,7 +1720,7 @@ sub tr_gen_to {
unless ($cannot_fail) {
unshift(@code, make_op('', 'try_me_else',
tr_code_len(@code)));
- push(@code, make_op(""), make_op("$key", 'fail'));
+ push(@code, make_op("$key", 'fail'));
}
unshift(@code, make_op($comment));
push(@{$gen_transform{$key}}, @code),
diff --git a/erts/emulator/utils/make_preload b/erts/emulator/utils/make_preload
index 13019d4062..fade9829ca 100755
--- a/erts/emulator/utils/make_preload
+++ b/erts/emulator/utils/make_preload
@@ -2,7 +2,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2011. All Rights Reserved.
+# Copyright Ericsson AB 1999-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -87,6 +87,7 @@ foreach $file (@ARGV) {
my $comment = '';
open(FILE, $file) or error("failed to read $file: $!");
+ binmode(FILE);
$_ = <FILE>;
$_ = beam_strip($_);
close(FILE);
diff --git a/erts/emulator/valgrind/suppress.halfword b/erts/emulator/valgrind/suppress.halfword
new file mode 100644
index 0000000000..8fe448d897
--- /dev/null
+++ b/erts/emulator/valgrind/suppress.halfword
@@ -0,0 +1,56 @@
+# Extra suppressions specific for the halfword emulator.
+
+# --- Suppress all offheap binaries ---
+# Valgrinds leak check does not recognize pointers that are stored
+# at unaligned addresses. In halfword emulator we store 64-bit pointers
+# to offheap data on 32-bit aligned heaps.
+# We solve this by suppressing allocation of all offheap structures
+# that are not referenced by other tables (ie binaries).
+
+{
+Halfword erts_bin_nrml_alloc
+Memcheck:Leak
+...
+fun:erts_bin_nrml_alloc
+...
+}
+
+{
+Halfword erts_bin_realloc
+Memcheck:Leak
+...
+fun:erts_bin_realloc
+...
+}
+
+{
+Halfword erts_bin_realloc_fnf
+Memcheck:Leak
+...
+fun:erts_bin_realloc_fnf
+...
+}
+
+{
+Halfword erts_bin_drv_alloc
+Memcheck:Leak
+...
+fun:erts_bin_drv_alloc
+...
+}
+
+{
+Halfword erts_bin_drv_alloc_fnf
+Memcheck:Leak
+...
+fun:erts_bin_drv_alloc_fnf
+...
+}
+
+{
+Halfword erts_create_magic_binary
+Memcheck:Leak
+...
+fun:erts_create_magic_binary
+...
+}
diff --git a/erts/emulator/valgrind/suppress.patched.3.6.0 b/erts/emulator/valgrind/suppress.patched.3.6.0
index 62ba032520..b3507bdba7 100644
--- a/erts/emulator/valgrind/suppress.patched.3.6.0
+++ b/erts/emulator/valgrind/suppress.patched.3.6.0
@@ -133,26 +133,18 @@ fun:pthread_create@@GLIBC_2.2.5
{
zlib; ok according to zlib developers
Memcheck:Cond
-fun:longest_match
+...
fun:deflate_slow
fun:deflate
}
{
zlib; ok according to zlib developers
Memcheck:Cond
-fun:longest_match
+...
fun:deflate_fast
fun:deflate
}
{
-zlib; ok accordnig to zlib (this one popped up with valgrind-3.6.0)
-Memcheck:Cond
-fun:deflate_slow
-fun:deflate
-fun:zlib_deflate
-fun:zlib_ctl
-}
-{
No leak; pointer into block
Memcheck:Leak
fun:malloc
@@ -275,6 +267,14 @@ obj:*/ssleay.*
}
{
+ Harmless assembler bug in openssl
+ Memcheck:Addr8
+ ...
+ fun:AES_cbc_encrypt
+ ...
+}
+
+{
erts_bits_init_state; Why is this needed?
Memcheck:Leak
PossiblyLost
diff --git a/erts/emulator/valgrind/suppress.standard b/erts/emulator/valgrind/suppress.standard
index 5a129bfd10..beecf1a7b5 100644
--- a/erts/emulator/valgrind/suppress.standard
+++ b/erts/emulator/valgrind/suppress.standard
@@ -120,14 +120,14 @@ fun:pthread_create@@GLIBC_2.2.5
{
zlib; ok according to zlib developers
Memcheck:Cond
-fun:longest_match
+...
fun:deflate_slow
fun:deflate
}
{
zlib; ok according to zlib developers
Memcheck:Cond
-fun:longest_match
+...
fun:deflate_fast
fun:deflate
}
@@ -252,6 +252,14 @@ obj:*/ssleay.*
}
{
+ Harmless assembler bug in openssl
+ Memcheck:Addr8
+ ...
+ fun:AES_cbc_encrypt
+ ...
+}
+
+{
Prebuilt constant terms in os_info_init (PossiblyLost)
Memcheck:Leak
fun:malloc
diff --git a/erts/emulator/zlib/zlib.mk b/erts/emulator/zlib/zlib.mk
index fa1f159fae..ff5ffa5328 100644
--- a/erts/emulator/zlib/zlib.mk
+++ b/erts/emulator/zlib/zlib.mk
@@ -63,12 +63,12 @@ endif # gcov
ifeq ($(TARGET), win32)
$(ZLIB_LIBRARY): $(ZLIB_OBJS)
- $(AR) -out:$@ $(ZLIB_OBJS)
+ $(V_AR) -out:$@ $(ZLIB_OBJS)
else
$(ZLIB_LIBRARY): $(ZLIB_OBJS)
- $(AR) $(ARFLAGS) $@ $(ZLIB_OBJS)
+ $(V_AR) $(ARFLAGS) $@ $(ZLIB_OBJS)
-@ ($(RANLIB) $@ || true) 2>/dev/null
endif
$(ZLIB_OBJDIR)/%.o: zlib/%.c
- $(CC) -c $(ZLIB_CFLAGS) -o $@ $<
+ $(V_CC) -c $(ZLIB_CFLAGS) -o $@ $<
diff --git a/erts/epmd/src/Makefile.in b/erts/epmd/src/Makefile.in
index 577fc77c13..e94674e6f4 100644
--- a/erts/epmd/src/Makefile.in
+++ b/erts/epmd/src/Makefile.in
@@ -128,13 +128,13 @@ clean:
#
$(BINDIR)/$(EPMD): $(EPMD_OBJS) $(ERTS_LIB)
- $(PURIFY) $(LD) $(LDFLAGS) -o $@ $(EPMD_OBJS) $(LIBS)
+ $(ld_verbose)$(PURIFY) $(LD) $(LDFLAGS) -o $@ $(EPMD_OBJS) $(LIBS)
$(OBJDIR)/%.o: %.c epmd.h epmd_int.h
- $(CC) $(CFLAGS) $(EPMD_FLAGS) -o $@ -c $<
+ $(V_CC) $(CFLAGS) $(EPMD_FLAGS) -o $@ -c $<
$(ERTS_LIB):
- cd $(ERL_TOP)/erts/lib_src && $(MAKE) $(TYPE)
+ $(make_verbose)cd $(ERL_TOP)/erts/lib_src && $(MAKE) $(TYPE)
include $(ERL_TOP)/make/otp_release_targets.mk
diff --git a/erts/epmd/src/epmd.c b/erts/epmd/src/epmd.c
index 2267f9b12b..3577abf6ba 100644
--- a/erts/epmd/src/epmd.c
+++ b/erts/epmd/src/epmd.c
@@ -64,7 +64,7 @@ int epmd_dbg(int level,int port) /* Utility to debug epmd... */
if(port)
{
argv[argc++] = "-port";
- sprintf(ibuff,"%d",port);
+ erts_snprintf(ibuff, sizeof(ibuff), "%d",port);
argv[argc++] = ibuff;
}
argv[argc] = NULL;
diff --git a/erts/epmd/src/epmd_cli.c b/erts/epmd/src/epmd_cli.c
index 74408e3ebe..1d4de64b63 100644
--- a/erts/epmd/src/epmd_cli.c
+++ b/erts/epmd/src/epmd_cli.c
@@ -22,6 +22,7 @@
#endif
#include "epmd.h" /* Renamed from 'epmd_r4.h' */
#include "epmd_int.h"
+#include "erl_printf.h" /* erts_snprintf */
/* forward declarations */
@@ -114,16 +115,18 @@ void epmd_call(EpmdVars *g,int what)
epmd_cleanup_exit(g,1);
}
j = ntohl(i);
- if (!g->silent)
- printf("epmd: up and running on port %d with data:\n", j);
+ if (!g->silent) {
+ rval = erts_snprintf(buf, OUTBUF_SIZE,
+ "epmd: up and running on port %d with data:\n", j);
+ write(1, buf, rval);
+ }
while(1) {
- if ((rval = read(fd,buf,1)) <= 0) {
+ if ((rval = read(fd,buf,OUTBUF_SIZE)) <= 0) {
close(fd);
epmd_cleanup_exit(g,0);
}
- buf[rval] = '\0';
if (!g->silent)
- printf("%s",buf);
+ write(1, buf, rval); /* Potentially UTF-8 encoded */
}
}
diff --git a/erts/epmd/src/epmd_int.h b/erts/epmd/src/epmd_int.h
index 14d05c3f19..b25412c905 100644
--- a/erts/epmd/src/epmd_int.h
+++ b/erts/epmd/src/epmd_int.h
@@ -226,13 +226,25 @@
#define MAX_UNREG_COUNT 1000
#define DEBUG_MAX_UNREG_COUNT 5
-/* Maximum length of a node name == atom name */
-#define MAXSYMLEN 255
+/*
+ * Maximum length of a node name == atom name
+ * 255 characters; UTF-8 encoded -> max 255*4
+ */
+#define MAXSYMLEN (255*4)
#define MAX_LISTEN_SOCKETS 16
-#define INBUF_SIZE 1024
-#define OUTBUF_SIZE 1024
+/*
+ * Largest request: ALIVE2_REQ
+ * 2 + 13 + 2*MAXSYMLEN
+ * Largest response: PORT2_RESP
+ * 2 + 14 + 2*MAXSYMLEN
+ *
+ * That is, 3*MAXSYMLEN should be large enough
+ */
+
+#define INBUF_SIZE (3*MAXSYMLEN)
+#define OUTBUF_SIZE (3*MAXSYMLEN)
#define get_int16(s) ((((unsigned char*) (s))[0] << 8) | \
(((unsigned char*) (s))[1]))
diff --git a/erts/epmd/src/epmd_srv.c b/erts/epmd/src/epmd_srv.c
index da575affa1..2a74c4955e 100644
--- a/erts/epmd/src/epmd_srv.c
+++ b/erts/epmd/src/epmd_srv.c
@@ -23,6 +23,7 @@
#endif
#include "epmd.h" /* Renamed from 'epmd_r4.h' */
#include "epmd_int.h"
+#include "erl_printf.h" /* erts_snprintf */
#ifndef INADDR_NONE
# define INADDR_NONE 0xffffffff
@@ -72,7 +73,7 @@ static int conn_open(EpmdVars*,int);
static int conn_close_fd(EpmdVars*,int);
static void node_init(EpmdVars*);
-static Node *node_reg2(EpmdVars*,char*, int, int, unsigned char, unsigned char, int, int, int, char*);
+static Node *node_reg2(EpmdVars*, int, char*, int, int, unsigned char, unsigned char, int, int, int, char*);
static int node_unreg(EpmdVars*,char*);
static int node_unreg_sock(EpmdVars*,int);
@@ -80,6 +81,113 @@ static int reply(EpmdVars*,int,char *,int);
static void dbg_print_buf(EpmdVars*,char *,int);
static void print_names(EpmdVars*);
+static int is_same_str(char *x, char *y)
+{
+ int i = 0;
+ /*
+ * Using strcmp() == 0 is probably ok, but just to be sure,
+ * since we got UTF-8 strings, we do it ourselves.
+ *
+ * We assume null-terminated correctly encoded UTF-8.
+ */
+ while (x[i] == y[i]) {
+ if (x[i] == '\0')
+ return 1;
+ i++;
+ }
+ return 0;
+}
+
+static int copy_str(char *x, char *y)
+{
+ int i = 0;
+ /*
+ * Using strcpy() is probably ok, but just to be sure,
+ * since we got UTF-8 strings, we do it ourselves.
+ *
+ * We assume null-terminated correctly encoded UTF-8.
+ */
+ while (1) {
+ x[i] = y[i];
+ if (y[i] == '\0')
+ return i;
+ i++;
+ }
+}
+
+static int length_str(char *x)
+{
+ int i = 0;
+ /*
+ * Using strlen is probably ok, but just to be sure,
+ * since we got UTF-8 strings, we do it ourselves.
+ *
+ * We assume null-terminated correctly encoded UTF-8.
+ */
+ while (x[i])
+ i++;
+ return i;
+}
+
+static int verify_utf8(const char *src, int sz, int null_term)
+{
+ unsigned char *source = (unsigned char *) src;
+ int size = sz;
+ int num_chars = 0;
+ while (size) {
+ if (null_term && (*source) == 0)
+ return num_chars;
+ if (((*source) & ((unsigned char) 0x80)) == 0) {
+ source++;
+ --size;
+ } else if (((*source) & ((unsigned char) 0xE0)) == 0xC0) {
+ if (size < 2)
+ return -1;
+ if (((source[1] & ((unsigned char) 0xC0)) != 0x80) ||
+ ((*source) < 0xC2) /* overlong */) {
+ return -1;
+ }
+ source += 2;
+ size -= 2;
+ } else if (((*source) & ((unsigned char) 0xF0)) == 0xE0) {
+ if (size < 3)
+ return -1;
+ if (((source[1] & ((unsigned char) 0xC0)) != 0x80) ||
+ ((source[2] & ((unsigned char) 0xC0)) != 0x80) ||
+ (((*source) == 0xE0) && (source[1] < 0xA0)) /* overlong */ ) {
+ return -1;
+ }
+ if ((((*source) & ((unsigned char) 0xF)) == 0xD) &&
+ ((source[1] & 0x20) != 0)) {
+ return -1;
+ }
+ source += 3;
+ size -= 3;
+ } else if (((*source) & ((unsigned char) 0xF8)) == 0xF0) {
+ if (size < 4)
+ return -1;
+ if (((source[1] & ((unsigned char) 0xC0)) != 0x80) ||
+ ((source[2] & ((unsigned char) 0xC0)) != 0x80) ||
+ ((source[3] & ((unsigned char) 0xC0)) != 0x80) ||
+ (((*source) == 0xF0) && (source[1] < 0x90)) /* overlong */) {
+ return -1;
+ }
+ if ((((*source) & ((unsigned char)0x7)) > 0x4U) ||
+ ((((*source) & ((unsigned char)0x7)) == 0x4U) &&
+ ((source[1] & ((unsigned char)0x3F)) > 0xFU))) {
+ return -1;
+ }
+ source += 4;
+ size -= 4;
+ } else {
+ return -1;
+ }
+ ++num_chars;
+ }
+ return num_chars;
+}
+
+
static EPMD_INLINE void select_fd_set(EpmdVars* g, int fd)
{
FD_SET(fd, &g->orig_read_mask);
@@ -524,10 +632,11 @@ static void do_request(g, fd, s, buf, bsize)
}
name = &buf[11];
name[namelen]='\000';
+
extra = &buf[11+namelen+2];
extra[extralen]='\000';
wbuf[0] = EPMD_ALIVE2_RESP;
- if ((node = node_reg2(g, name, fd, eport, nodetype, protocol,
+ if ((node = node_reg2(g, namelen, name, fd, eport, nodetype, protocol,
highvsn, lowvsn, extralen, extra)) == NULL) {
wbuf[1] = 1; /* error */
put_int16(99, wbuf+2);
@@ -572,22 +681,28 @@ static void do_request(g, fd, s, buf, bsize)
{
char *name = &buf[1]; /* Points to node name */
+ int nsz;
Node *node;
-
+
+ nsz = verify_utf8(name, bsize, 0);
+ if (nsz < 1 || 255 < nsz) {
+ dbg_printf(g,0,"invalid node name in PORT2_REQ");
+ return;
+ }
+
wbuf[0] = EPMD_PORT2_RESP;
for (node = g->nodes.reg; node; node = node->next) {
int offset;
- if (strcmp(node->symname, name) == 0) {
+ if (is_same_str(node->symname, name)) {
wbuf[1] = 0; /* ok */
put_int16(node->port,wbuf+2);
wbuf[4] = node->nodetype;
wbuf[5] = node->protocol;
put_int16(node->highvsn,wbuf+6);
put_int16(node->lowvsn,wbuf+8);
- put_int16(strlen(node->symname),wbuf+10);
+ put_int16(length_str(node->symname),wbuf+10);
offset = 12;
- strcpy(wbuf + offset,node->symname);
- offset += strlen(node->symname);
+ offset += copy_str(wbuf + offset,node->symname);
put_int16(node->extralen,wbuf + offset);
offset += 2;
memcpy(wbuf + offset,node->extra,node->extralen);
@@ -628,15 +743,22 @@ static void do_request(g, fd, s, buf, bsize)
for (node = g->nodes.reg; node; node = node->next)
{
- int len;
+ int len = 0;
+ int r;
/* CAREFUL!!! These are parsed by "erl_epmd.erl" so a slight
change in syntax will break < OTP R3A */
- sprintf(wbuf,"name %s at port %d\n",node->symname, node->port);
- len = strlen(wbuf);
+ len += copy_str(&wbuf[len], "name ");
+ len += copy_str(&wbuf[len], node->symname);
+ r = erts_snprintf(&wbuf[len], sizeof(wbuf)-len,
+ " at port %d\n", node->port);
+ if (r < 0)
+ goto failed_names_resp;
+ len += r;
if (reply(g, fd, wbuf, len) != len)
{
+ failed_names_resp:
dbg_tty_printf(g,1,"failed to send NAMES_RESP");
return;
}
@@ -664,16 +786,22 @@ static void do_request(g, fd, s, buf, bsize)
for (node = g->nodes.reg; node; node = node->next)
{
- int len;
+ int len = 0, r;
/* CAREFUL!!! These are parsed by "erl_epmd.erl" so a slight
change in syntax will break < OTP R3A */
- sprintf(wbuf,"active name <%s> at port %d, fd = %d\n",
- node->symname, node->port, node->fd);
- len = strlen(wbuf) + 1;
- if (reply(g, fd,wbuf,len) != len)
+ len += copy_str(&wbuf[len], "active name <");
+ len += copy_str(&wbuf[len], node->symname);
+ r = erts_snprintf(&wbuf[len], sizeof(wbuf)-len,
+ "> at port %d, fd = %d\n",
+ node->port, node->fd);
+ if (r < 0)
+ goto failed_dump_resp;
+ len += r + 1;
+ if (reply(g, fd,wbuf,len) != len)
{
+ failed_dump_resp:
dbg_tty_printf(g,1,"failed to send DUMP_RESP");
return;
}
@@ -681,16 +809,22 @@ static void do_request(g, fd, s, buf, bsize)
for (node = g->nodes.unreg; node; node = node->next)
{
- int len;
+ int len = 0, r;
/* CAREFUL!!! These are parsed by "erl_epmd.erl" so a slight
change in syntax will break < OTP R3A */
- sprintf(wbuf,"old/unused name <%s>, port = %d, fd = %d \n",
- node->symname,node->port, node->fd);
- len = strlen(wbuf) + 1;
- if (reply(g, fd,wbuf,len) != len)
+ len += copy_str(&wbuf[len], "old/unused name <");
+ len += copy_str(&wbuf[len], node->symname);
+ r = erts_snprintf(&wbuf[len], sizeof(wbuf)-len,
+ ">, port = %d, fd = %d \n",
+ node->port, node->fd);
+ if (r < 0)
+ goto failed_dump_resp2;
+ len += r + 1;
+ if (reply(g, fd,wbuf,len) != len)
{
+ failed_dump_resp2:
dbg_tty_printf(g,1,"failed to send DUMP_RESP");
return;
}
@@ -932,7 +1066,7 @@ static int node_unreg(EpmdVars *g,char *name)
Node *node = g->nodes.reg; /* Point to first node */
for (; node; prev = &node->next, node = node->next)
- if (strcmp(node->symname, name) == 0)
+ if (is_same_str(node->symname, name))
{
dbg_tty_printf(g,1,"unregistering '%s:%d', port %d",
node->symname, node->creation, node->port);
@@ -1012,6 +1146,7 @@ static int node_unreg_sock(EpmdVars *g,int fd)
*/
static Node *node_reg2(EpmdVars *g,
+ int namelen,
char* name,
int fd,
int port,
@@ -1024,6 +1159,7 @@ static Node *node_reg2(EpmdVars *g,
{
Node *prev; /* Point to previous node or NULL */
Node *node; /* Point to first node */
+ int sz;
/* Can be NULL; means old style */
if (extra == NULL)
@@ -1031,21 +1167,47 @@ static Node *node_reg2(EpmdVars *g,
/* Fail if node name is too long */
- if (strlen(name) > MAXSYMLEN)
+
+ if (namelen > MAXSYMLEN)
{
- dbg_printf(g,0,"node name is too long (%d) %s", strlen(name), name);
+ too_long_name:
+ dbg_printf(g,0,"node name is too long (%d) %s", namelen, name);
return NULL;
}
+
+ sz = verify_utf8(name, namelen, 0);
+ if (sz > 255)
+ goto too_long_name;
+
+ if (sz < 0) {
+ dbg_printf(g,0,"invalid node name encoding");
+ return NULL;
+ }
+
if (extralen > MAXSYMLEN)
{
- dbg_printf(g,0,"extra data is too long (%d) %s", strlen(name), name);
+#if 0
+ too_long_extra:
+#endif
+ dbg_printf(g,0,"extra data is too long (%d) %s", extralen, extra);
return NULL;
}
+#if 0 /* Should we require valid utf8 here? */
+ sz = verify_utf8(extra, extralen, 0);
+ if (sz > 255)
+ goto too_long_extra;
+
+ if (sz < 0) {
+ dbg_printf(g,0,"invalid extra data encoding");
+ return NULL;
+ }
+#endif
+
/* Fail if it is already registered */
for (node = g->nodes.reg; node; node = node->next)
- if (strcmp(node->symname, name) == 0)
+ if (is_same_str(node->symname, name))
{
dbg_printf(g,0,"node name already occupied %s", name);
return NULL;
@@ -1057,7 +1219,7 @@ static Node *node_reg2(EpmdVars *g,
prev = NULL;
for (node = g->nodes.unreg; node; prev = node, node = node->next)
- if (strcmp(node->symname, name) == 0)
+ if (is_same_str(node->symname, name))
{
dbg_tty_printf(g,1,"reusing slot with same name '%s'", node->symname);
@@ -1125,7 +1287,7 @@ static Node *node_reg2(EpmdVars *g,
node->lowvsn = lowvsn;
node->extralen = extralen;
memcpy(node->extra,extra,extralen);
- strcpy(node->symname,name);
+ copy_str(node->symname,name);
select_fd_set(g, fd);
if (highvsn == 0) {
diff --git a/erts/epmd/test/Makefile b/erts/epmd/test/Makefile
index 868b1c023b..45c8be6809 100644
--- a/erts/epmd/test/Makefile
+++ b/erts/epmd/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1998-2010. All Rights Reserved.
+# Copyright Ericsson AB 1998-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/erts/epmd/test/epmd_SUITE.erl b/erts/epmd/test/epmd_SUITE.erl
index 3f31cd979c..fc0abef400 100644
--- a/erts/epmd/test/epmd_SUITE.erl
+++ b/erts/epmd/test/epmd_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -45,6 +45,8 @@
register_names_1/1,
register_names_2/1,
register_duplicate_name/1,
+ unicode_name/1,
+ long_unicode_name/1,
get_port_nr/1,
slow_get_port_nr/1,
unregister_others_name_1/1,
@@ -107,7 +109,8 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[register_name, register_names_1, register_names_2,
- register_duplicate_name, get_port_nr, slow_get_port_nr,
+ register_duplicate_name, unicode_name, long_unicode_name,
+ get_port_nr, slow_get_port_nr,
unregister_others_name_1, unregister_others_name_2,
register_overflow, name_with_null_inside,
name_null_terminated, stupid_names_req, no_data,
@@ -197,6 +200,37 @@ register_duplicate_name(Config) when is_list(Config) ->
?line ok = close(Sock), % Unregister
ok.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+unicode_name(doc) ->
+ ["Check that we can register and lookup a unicode name"];
+unicode_name(suite) ->
+ [];
+unicode_name(Config) when is_list(Config) ->
+ ok = epmdrun(),
+ NodeName = [16#1f608],
+ {ok,Sock} = register_node_v2(4711, 72, 0, 5, 5, NodeName, []),
+ {ok,NodeInfo} = port_please_v2(NodeName),
+ NodeName = NodeInfo#node_info.node_name,
+ ok = close(Sock),
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+long_unicode_name(doc) ->
+ ["Check that we can register and lookup a long unicode name"];
+long_unicode_name(suite) ->
+ [];
+long_unicode_name(Config) when is_list(Config) ->
+ ok = epmdrun(),
+ BaseChar = 16#1f600,
+ NodeName = lists:seq(BaseChar, BaseChar+200), % will be 800 bytes long
+ {ok,Sock} = register_node_v2(4711, 72, 0, 5, 5, NodeName, []),
+ {ok,NodeInfo} = port_please_v2(NodeName),
+ NodeName = NodeInfo#node_info.node_name,
+ ok = close(Sock),
+ ok.
+
% Internal function to register a node name, no close, i.e. unregister
register_node(Name) ->
@@ -205,9 +239,10 @@ register_node(Name,Port) ->
register_node_v2(Port,$M,0,5,5,Name,"").
register_node_v2(Port, NodeType, Prot, HVsn, LVsn, Name, Extra) ->
+ Utf8Name = unicode:characters_to_binary(Name),
Req = [?EPMD_ALIVE2_REQ, put16(Port), NodeType, Prot,
put16(HVsn), put16(LVsn),
- size16(Name), Name,
+ put16(size(Utf8Name)), binary_to_list(Utf8Name),
size16(Extra), Extra],
case send_req(Req) of
{ok,Sock} ->
@@ -226,7 +261,8 @@ register_node_v2(Port, NodeType, Prot, HVsn, LVsn, Name, Extra) ->
% Internal function to fetch information about a node
port_please_v2(Name) ->
- case send_req([?EPMD_PORT_PLEASE2_REQ, Name]) of
+ case send_req([?EPMD_PORT_PLEASE2_REQ,
+ binary_to_list(unicode:characters_to_binary(Name))]) of
{ok,Sock} ->
case recv_until_sock_closes(Sock) of
{ok, Resp} ->
@@ -247,7 +283,7 @@ parse_port2_resp(Resp) ->
ELen:16,Extra:ELen/binary>> when Res =:= 0 ->
{ok, #node_info{port=Port,node_type=NodeType,prot=Prot,
hvsn=HVsn,lvsn=LVsn,
- node_name=binary_to_list(NodeName),
+ node_name=unicode:characters_to_list(NodeName),
extra=binary_to_list(Extra)}};
_Other ->
test_server:format("invalid port2 resp: ~p~n",
@@ -737,7 +773,7 @@ buffer_overrun_2(doc) ->
["Test security vulnerability in fake extra lengths in alive2_req"];
buffer_overrun_2(Config) when is_list(Config) ->
?line ok = epmdrun(),
- ?line [false | Rest] = [hostile2(N) || N <- lists:seq(255,10000)],
+ ?line [false | Rest] = [hostile2(N) || N <- lists:seq(255*4,10000)],
?line true = alltrue(Rest),
ok.
hostile(N) ->
@@ -880,6 +916,7 @@ no_live_killing(Config) when is_list(Config) ->
?line close(Sock3),
ok.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Terminate all tests with killing epmd.
diff --git a/erts/etc/common/Makefile.in b/erts/etc/common/Makefile.in
index b10f5d9a87..5c1ce51644 100644
--- a/erts/etc/common/Makefile.in
+++ b/erts/etc/common/Makefile.in
@@ -17,6 +17,7 @@
# %CopyrightEnd%
#
+include $(ERL_TOP)/make/output.mk
include $(ERL_TOP)/make/target.mk
ERTS_LIB_TYPEMARKER=.$(TYPE)
@@ -33,11 +34,7 @@ else
ifeq ($(TYPE),purify)
PURIFY = purify
TYPEMARKER =
-ifeq ($(findstring ose,$(TARGET)),ose)
-TYPE_FLAGS = -g -XO -DPURIFY
-else
TYPE_FLAGS = -g -O2 -DPURIFY
-endif
else
override TYPE=opt
@@ -64,6 +61,9 @@ LD = @LD@
LIBS = @LIBS@
LDFLAGS = @LDFLAGS@
+# For clock_gettime in heart
+RTLIBS = @LIBRT@
+
ifeq ($(TARGET),win32)
ifeq ($(TYPE),debug)
CFLAGS = $(subst -O2,-g,@CFLAGS@ @DEFS@ $(TYPE_FLAGS) @WFLAGS@ -I$(SYSDIR) \
@@ -77,25 +77,15 @@ EMUDIR = $(ERL_TOP)/erts/emulator/beam
EMUOSDIR = $(ERL_TOP)/erts/emulator/@ERLANG_OSTYPE@
SYSDIR = $(ERL_TOP)/erts/emulator/sys/@ERLANG_OSTYPE@
DRVDIR = $(ERL_TOP)/erts/emulator/drivers/@ERLANG_OSTYPE@
-VXETC = ../vxworks
UXETC = ../unix
OSEETC = ../ose
WINETC = ../win32
-ifeq ($(findstring vxworks,$(TARGET)), vxworks)
-ERLEXEC = erl.exec
-else
-ifeq ($(findstring ose,$(TARGET)), ose)
-ERLEXEC =
-TAR = @TAR@
-else
ifeq ($(TARGET), win32)
ERLEXEC = erlexec.dll
else
ERLEXEC = erlexec
endif
-endif
-endif
# On windows we always need reentrant libraries.
ifeq ($(TARGET),win32)
@@ -110,42 +100,6 @@ ERTS_LIB = $(ERL_TOP)/erts/lib_src/obj/$(TARGET)/$(TYPE)/MADE
# Release directory specification
# ----------------------------------------------------
-ifeq ($(findstring vxworks,$(TARGET)), vxworks)
-INSTALL_EMBEDDED_PROGS = $(BINDIR)/erl_io $(BINDIR)/rdate $(BINDIR)/vxcall
-INSTALL_EMBEDDED_DATA = $(BINDIR)/erl_script.sam $(VXETC)/resolv.conf
-INSTALL_INCLUDES = $(VXETC)/reclaim.h
-INSTALL_TOP = $(VXETC)/README.VxWorks
-INSTALL_MISC =
-INSTALL_SRC = heart.c $(VXETC)/heart_config.h $(VXETC)/heart_config.c \
- $(VXETC)/erl.exec.c $(VXETC)/rdate.c $(VXETC)/vxcall.c \
- $(VXETC)/erl_io.c
-ERLEXECDIR = $(VXETC)
-INSTALL_LIBS = $(OBJDIR)/reclaim.o
-INSTALL_OBJS = $(OBJDIR)/heart.o
-TEXTFILES = $(BINDIR)/erl_script.sam
-ERLSRV_OBJECTS=
-MC_OUTPUTS=
-ENTRY_LDFLAGS=
-ENTRY_OBJ=
-INSTALL_PROGS = \
- $(INET_GETHOST) \
- $(BINDIR)/heart \
- $(BINDIR)/$(ERLEXEC) \
- $(INSTALL_EMBEDDED_PROGS)
-else
-ifeq ($(findstring ose,$(TARGET)), ose)
-INSTALL_TOP = $(OSEETC)/README.OSE
-INSTALL_ERL_OSE = monolith lm erl_utils drivers port_progs host
-INSTALL_SRC =
-INSTALL_LIBS =
-INSTALL_OBJS =
-INSTALL_INCLUDES =
-INSTALL_PROGS =
-ERLSRV_OBJECTS=
-MC_OUTPUTS=
-ENTRY_LDFLAGS=
-ENTRY_OBJ=
-else
ifeq ($(TARGET),win32)
CFLAGS += -I$(EMUOSDIR) -I$(WINETC)
RC=rc.sh
@@ -207,7 +161,7 @@ endif
PORT_ENTRY_POINT=erl_port_entry
ENTRY_LDFLAGS=-entry:$(PORT_ENTRY_POINT)
-else
+else # UNIX (!win32)
ENTRY_LDFLAGS=
ENTRY_OBJ=
ERLSRV_OBJECTS=
@@ -232,15 +186,13 @@ INSTALL_PROGS = \
$(BINDIR)/$(ERLEXEC) \
$(INSTALL_EMBEDDED_PROGS)
endif
-endif
-endif
.PHONY: etc
etc: $(ENTRY_OBJ) $(INSTALL_PROGS) $(INSTALL_LIBS) $(TEXTFILES) $(INSTALL_TOP_BIN)
# erlexec needs the erts_internal library...
$(ERTS_LIB):
- cd $(ERL_TOP)/erts/lib_src && $(MAKE) $(TYPE)
+ $(V_at)cd $(ERL_TOP)/erts/lib_src && $(MAKE) $(TYPE)
.PHONY: docs
docs:
@@ -298,23 +250,23 @@ endif
ifeq ($(TARGET),win32)
$(BINDIR)/$(ERLEXEC): $(OBJDIR)/erlexec.o $(OBJDIR)/win_erlexec.o $(OBJDIR)/init_file.o $(OBJDIR)/$(ERLRES_OBJ) $(ERTS_LIB)
- $(LD) -dll $(LDFLAGS) -o $@ $(OBJDIR)/erlexec.o $(OBJDIR)/win_erlexec.o $(OBJDIR)/init_file.o $(OBJDIR)/$(ERLRES_OBJ) $(ERTS_INTERNAL_LIBS)
+ $(V_LD) -dll $(LDFLAGS) -o $@ $(OBJDIR)/erlexec.o $(OBJDIR)/win_erlexec.o $(OBJDIR)/init_file.o $(OBJDIR)/$(ERLRES_OBJ) $(ERTS_INTERNAL_LIBS)
$(BINDIR)/erl@EXEEXT@: $(OBJDIR)/erl.o $(OBJDIR)/init_file.o $(OBJDIR)/$(ERLRES_OBJ)
- $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/erl.o $(OBJDIR)/init_file.o $(OBJDIR)/$(ERLRES_OBJ)
+ $(V_LD) $(LDFLAGS) -o $@ $(OBJDIR)/erl.o $(OBJDIR)/init_file.o $(OBJDIR)/$(ERLRES_OBJ)
$(BINDIR)/werl@EXEEXT@: $(OBJDIR)/werl.o $(OBJDIR)/init_file.o $(OBJDIR)/$(ERLRES_OBJ)
- $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/werl.o $(OBJDIR)/init_file.o $(OBJDIR)/$(ERLRES_OBJ)
+ $(V_LD) $(LDFLAGS) -o $@ $(OBJDIR)/werl.o $(OBJDIR)/init_file.o $(OBJDIR)/$(ERLRES_OBJ)
$(BINDIR)/start_erl@EXEEXT@: $(OBJDIR)/start_erl.o
- $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/start_erl.o
+ $(V_LD) $(LDFLAGS) -o $@ $(OBJDIR)/start_erl.o
$(BINDIR)/Install@EXEEXT@: $(OBJDIR)/Install.o $(OBJDIR)/init_file.o
- $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/Install.o $(OBJDIR)/init_file.o
+ $(V_LD) $(LDFLAGS) -o $@ $(OBJDIR)/Install.o $(OBJDIR)/init_file.o
# The service expects to be compiled with $(MT_FLAG) flag.
$(BINDIR)/erlsrv@EXEEXT@: $(ERLSRV_OBJECTS)
- $(LD) $(LDFLAGS) $(MT_FLAG) -o $@ $(ERLSRV_OBJECTS)
+ $(V_LD) $(LDFLAGS) $(MT_FLAG) -o $@ $(ERLSRV_OBJECTS)
# To fix a spooky parallel make build problem on Windows there are some
# false dependencies on the $(MC), $(RC) and .o rules. The theory behind
@@ -329,62 +281,62 @@ $(BINDIR)/erlsrv@EXEEXT@: $(ERLSRV_OBJECTS)
LOGMESS_GENERATED = $(OBJDIR)/LOGMESS-GENERATED
$(MC_OUTPUTS): $(LOGMESS_GENERATED)
$(LOGMESS_GENERATED): $(WINETC)/erlsrv/erlsrv_logmess.mc
- $(MC) -o $(OBJDIR) $(WINETC)/erlsrv/erlsrv_logmess.mc && \
+ $(V_MC) -o $(OBJDIR) $(WINETC)/erlsrv/erlsrv_logmess.mc && \
echo $? >$(LOGMESS_GENERATED)
$(OBJDIR)/$(ERLRES_OBJ): $(WINETC)/erl.rc $(WINETC)/erlang.ico \
$(WINETC)/erl_icon.ico $(WINETC)/hrl_icon.ico \
$(WINETC)/beam_icon.ico $(LOGMESS_GENERATED)
- $(RC) -o $@ -I$(WINETC) $(WINETC)/erl.rc
+ $(V_RC) -o $@ -I$(WINETC) $(WINETC)/erl.rc
ifeq ($(USING_VC), yes)
RC_GENERATED = $(OBJDIR)/erlsrv_logmess.res
$(RC_GENERATED): $(OBJDIR)/erlsrv_logmess.rc $(OBJDIR)/$(ERLRES_OBJ)
- $(RC) -o $(OBJDIR)/erlsrv_logmess.res -I$(OBJDIR) $(OBJDIR)/erlsrv_logmess.rc
+ $(V_RC) -o $(OBJDIR)/erlsrv_logmess.res -I$(OBJDIR) $(OBJDIR)/erlsrv_logmess.rc
else
RC_GENERATED = $(OBJDIR)/erlsrv_logmess.o
$(RC_GENERATED): $(OBJDIR)/erlsrv_logmess.res $(OBJDIR)/$(ERLRES_OBJ)
- $(RC) -o $(OBJDIR)/erlsrv_logmess.o -I$(OBJDIR) $(OBJDIR)/erlsrv_logmess.res
+ $(V_RC) -o $(OBJDIR)/erlsrv_logmess.o -I$(OBJDIR) $(OBJDIR)/erlsrv_logmess.res
endif
# The service expects to be compiled with $(MT_FLAG) flag.
$(OBJDIR)/%.o: $(WINETC)/erlsrv/%.c $(ERLSRV_HEADERS) $(RC_GENERATED)
- $(CC) $(CFLAGS) $(MT_FLAG) -o $@ -c $<
+ $(V_CC) $(CFLAGS) $(MT_FLAG) -o $@ -c $<
$(OBJDIR)/erlsrv_util.o: $(WINETC)/erlsrv/erlsrv_util.c $(ERLSRV_HEADERS) \
$(OBJDIR)/erlsrv_logmess.h $(RC_GENERATED)
- $(CC) $(CFLAGS) -I$(OBJDIR) $(MT_FLAG) -o $@ -c $<
+ $(V_CC) $(CFLAGS) -I$(OBJDIR) $(MT_FLAG) -o $@ -c $<
$(OBJDIR)/werl.o: $(WINETC)/erl.c $(WINETC)/init_file.h $(RC_GENERATED)
- $(CC) $(CFLAGS) -DBUILD_TYPE=\"-$(TYPE)\" -DERL_RUN_SHARED_LIB=1 \
+ $(V_CC) $(CFLAGS) -DBUILD_TYPE=\"-$(TYPE)\" -DERL_RUN_SHARED_LIB=1 \
-DWIN32_WERL -o $@ -c $(WINETC)/erl.c
$(OBJDIR)/erl.o: $(WINETC)/erl.c $(WINETC)/init_file.h $(RC_GENERATED)
- $(CC) $(CFLAGS) -DBUILD_TYPE=\"-$(TYPE)\" -DERL_RUN_SHARED_LIB=1 \
+ $(V_CC) $(CFLAGS) -DBUILD_TYPE=\"-$(TYPE)\" -DERL_RUN_SHARED_LIB=1 \
-o $@ -c $(WINETC)/erl.c
$(OBJDIR)/erlexec.o: $(ERLEXECDIR)/erlexec.c $(RC_GENERATED)
- $(CC) $(CFLAGS) -DBUILD_TYPE=\"-$(TYPE)\" -DERL_RUN_SHARED_LIB=1 \
+ $(V_CC) $(CFLAGS) -DBUILD_TYPE=\"-$(TYPE)\" -DERL_RUN_SHARED_LIB=1 \
-o $@ -c $(ERLEXECDIR)/erlexec.c
$(OBJDIR)/win_erlexec.o: $(WINETC)/win_erlexec.c $(RC_GENERATED)
- $(CC) $(CFLAGS) -DBUILD_TYPE=\"-$(TYPE)\" -DERL_RUN_SHARED_LIB=1 \
+ $(V_CC) $(CFLAGS) -DBUILD_TYPE=\"-$(TYPE)\" -DERL_RUN_SHARED_LIB=1 \
-o $@ -c $(WINETC)/win_erlexec.c
$(OBJDIR)/init_file.o: $(WINETC)/init_file.c $(WINETC)/init_file.h $(RC_GENERATED)
- $(CC) $(CFLAGS) -o $@ -c $(WINETC)/init_file.c
+ $(V_CC) $(CFLAGS) -o $@ -c $(WINETC)/init_file.c
$(OBJDIR)/Install.o: $(WINETC)/Install.c $(WINETC)/init_file.h $(RC_GENERATED)
- $(CC) $(CFLAGS) -o $@ -c $(WINETC)/Install.c
+ $(V_CC) $(CFLAGS) -o $@ -c $(WINETC)/Install.c
$(OBJDIR)/start_erl.o: $(WINETC)/start_erl.c $(RC_GENERATED)
- $(CC) $(CFLAGS) -o $@ -c $(WINETC)/start_erl.c
+ $(V_CC) $(CFLAGS) -o $@ -c $(WINETC)/start_erl.c
$(ENTRY_OBJ): $(ENTRY_SRC) $(RC_GENERATED)
- $(CC) $(CFLAGS) -o $@ -c $(ENTRY_SRC)
+ $(V_CC) $(CFLAGS) -o $@ -c $(ENTRY_SRC)
Install.ini: ../$(TARGET)/Install.src ../../vsn.mk $(TARGET)/Makefile
- sed -e 's;%I_VSN%;$(VSN);' \
+ $(vsn_verbose)sed -e 's;%I_VSN%;$(VSN);' \
-e 's;%I_SYSTEM_VSN%;$(SYSTEM_VSN);' \
../$(TARGET)/Install.src > Install.ini
@@ -396,143 +348,100 @@ endif
# End of windows specific targets.
#---------------------------------------------------------
-ifeq ($(findstring vxworks,$(TARGET)), vxworks)
-$(BINDIR)/heart: $(OBJDIR)/heart.o $(OBJDIR)/heart_config.o
- $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/heart.o $(OBJDIR)/heart_config.o
-
-$(OBJDIR)/heart_config.o: $(VXETC)/heart_config.c
- $(CC) $(CFLAGS) -o $@ -c $(VXETC)/heart_config.c
-
-$(OBJDIR)/reclaim.o: $(VXETC)/reclaim.c
- $(CC) $(CFLAGS) -o $@ -c $(VXETC)/reclaim.c
-
-$(OBJDIR)/heart.o: heart.c
- $(CC) $(CFLAGS) -I$(VXETC) -o $@ -c heart.c
-
-$(BINDIR)/erl_script.sam: $(VXETC)/erl_script.sam.in ../../vsn.mk
- sed -e 's;%VSN%;$(VSN);' \
- $(VXETC)/erl_script.sam.in > $(BINDIR)/erl_script.sam
-else
-
$(BINDIR)/heart@EXEEXT@: $(OBJDIR)/heart.o $(ENTRY_OBJ)
- $(LD) $(LDFLAGS) $(ENTRY_LDFLAGS) -o $@ $(OBJDIR)/heart.o \
- $(ENTRY_OBJ) $(WINDSOCK)
+ $(V_LD) $(LDFLAGS) $(ENTRY_LDFLAGS) -o $@ $(OBJDIR)/heart.o \
+ $(RTLIBS) $(ENTRY_OBJ) $(WINDSOCK)
$(OBJDIR)/heart.o: heart.c $(RC_GENERATED)
- $(CC) $(CFLAGS) -o $@ -c heart.c
-
-endif
-
-
-# VxWorks specific executables and objects ...
-
-$(BINDIR)/erl_io: $(OBJDIR)/erl_io.o
- $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/erl_io.o
-
-$(OBJDIR)/erl_io.o: $(VXETC)/erl_io.c
- $(CC) $(CFLAGS) -o $@ -c $(VXETC)/erl_io.c
-
-$(BINDIR)/rdate: $(OBJDIR)/rdate.o
- $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/rdate.o
-
-$(OBJDIR)/rdate.o: $(VXETC)/rdate.c
- $(CC) $(CFLAGS) -o $@ -c $(VXETC)/rdate.c
-
-$(BINDIR)/vxcall: $(OBJDIR)/vxcall.o
- $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/vxcall.o
-
-$(OBJDIR)/vxcall.o: $(VXETC)/vxcall.c
- $(CC) $(CFLAGS) -o $@ -c $(VXETC)/vxcall.c
-
-
+ $(V_CC) $(CFLAGS) -o $@ -c heart.c
#
# Objects & executables
#
#$(OBJDIR)/%.o: %.c
-# $(CC) $(CFLAGS) -o $@ -c $<
+# $(V_CC) $(CFLAGS) -o $@ -c $<
#
#$(OBJDIR)/%.o: ../unix/%.c
-# $(CC) $(CFLAGS) -o $@ -c $<
+# $(V_CC) $(CFLAGS) -o $@ -c $<
#
#$(BINDIR)/%: $(OBJDIR)/%.o
-# $(PURIFY) $(LD) $(LDFLAGS) -o $@ $< $(LIBS)
+# $(ld_verbose)$(PURIFY) $(LD) $(LDFLAGS) -o $@ $< $(LIBS)
$(OBJDIR)/inet_gethost.o: inet_gethost.c $(RC_GENERATED)
- $(CC) $(CFLAGS) -o $@ -c inet_gethost.c
+ $(V_CC) $(CFLAGS) -o $@ -c inet_gethost.c
$(BINDIR)/inet_gethost@EXEEXT@: $(OBJDIR)/inet_gethost.o $(ENTRY_OBJ) $(ERTS_LIB)
- $(PURIFY) $(LD) $(LDFLAGS) $(ENTRY_LDFLAGS) -o $@ $(OBJDIR)/inet_gethost.o $(ENTRY_OBJ) $(LIBS) $(ERTS_INTERNAL_LIBS)
+ $(ld_verbose)$(PURIFY) $(LD) $(LDFLAGS) $(ENTRY_LDFLAGS) -o $@ $(OBJDIR)/inet_gethost.o $(ENTRY_OBJ) $(LIBS) $(ERTS_INTERNAL_LIBS)
$(BINDIR)/run_erl: $(OBJDIR)/safe_string.o $(OBJDIR)/run_erl.o
- $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/safe_string.o $(OBJDIR)/run_erl.o $(LIBS)
+ $(V_LD) $(LDFLAGS) -o $@ $(OBJDIR)/safe_string.o $(OBJDIR)/run_erl.o $(LIBS)
$(OBJDIR)/run_erl.o: ../unix/run_erl.c $(RC_GENERATED)
- $(CC) $(CFLAGS) -o $@ -c ../unix/run_erl.c
+ $(V_CC) $(CFLAGS) -o $@ -c ../unix/run_erl.c
$(BINDIR)/to_erl: $(OBJDIR)/safe_string.o $(OBJDIR)/to_erl.o
- $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/safe_string.o $(OBJDIR)/to_erl.o
+ $(V_LD) $(LDFLAGS) -o $@ $(OBJDIR)/safe_string.o $(OBJDIR)/to_erl.o
$(OBJDIR)/to_erl.o: ../unix/to_erl.c $(RC_GENERATED)
- $(CC) $(CFLAGS) -o $@ -c ../unix/to_erl.c
+ $(V_CC) $(CFLAGS) -o $@ -c ../unix/to_erl.c
$(BINDIR)/dyn_erl: $(OBJDIR)/safe_string.o $(OBJDIR)/dyn_erl.o
- $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/safe_string.o $(OBJDIR)/dyn_erl.o
+ $(V_LD) $(LDFLAGS) -o $@ $(OBJDIR)/safe_string.o $(OBJDIR)/dyn_erl.o
$(OBJDIR)/dyn_erl.o: ../unix/dyn_erl.c $(RC_GENERATED)
- $(CC) $(CFLAGS) -o $@ -c ../unix/dyn_erl.c
+ $(V_CC) $(CFLAGS) -o $@ -c ../unix/dyn_erl.c
$(OBJDIR)/safe_string.o: ../unix/safe_string.c $(RC_GENERATED)
- $(CC) $(CFLAGS) -o $@ -c ../unix/safe_string.c
+ $(V_CC) $(CFLAGS) -o $@ -c ../unix/safe_string.c
ifneq ($(TARGET),win32)
$(BINDIR)/$(ERLEXEC): $(OBJDIR)/$(ERLEXEC).o $(ERTS_LIB)
- $(PURIFY) $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/$(ERLEXEC).o $(ERTS_INTERNAL_LIBS)
+ $(ld_verbose)$(PURIFY) $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/$(ERLEXEC).o $(ERTS_INTERNAL_LIBS)
$(OBJDIR)/$(ERLEXEC).o: $(ERLEXECDIR)/$(ERLEXEC).c $(RC_GENERATED)
- $(CC) -I$(EMUDIR) $(CFLAGS) -o $@ -c $(ERLEXECDIR)/$(ERLEXEC).c
+ $(V_CC) -I$(EMUDIR) $(CFLAGS) -o $@ -c $(ERLEXECDIR)/$(ERLEXEC).c
endif
$(BINDIR)/erlc@EXEEXT@: $(OBJDIR)/erlc.o $(ERTS_LIB)
- $(PURIFY) $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/erlc.o -L$(OBJDIR) $(LIBS) $(ERTS_INTERNAL_LIBS)
+ $(ld_verbose)$(PURIFY) $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/erlc.o -L$(OBJDIR) $(LIBS) $(ERTS_INTERNAL_LIBS)
$(OBJDIR)/erlc.o: erlc.c $(RC_GENERATED)
- $(CC) $(CFLAGS) -o $@ -c erlc.c
+ $(V_CC) $(CFLAGS) -o $@ -c erlc.c
$(BINDIR)/dialyzer@EXEEXT@: $(OBJDIR)/dialyzer.o $(ERTS_LIB)
- $(PURIFY) $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/dialyzer.o -L$(OBJDIR) $(LIBS) $(ERTS_INTERNAL_LIBS)
+ $(ld_verbose)$(PURIFY) $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/dialyzer.o -L$(OBJDIR) $(LIBS) $(ERTS_INTERNAL_LIBS)
$(OBJDIR)/dialyzer.o: dialyzer.c $(RC_GENERATED)
- $(CC) $(CFLAGS) -o $@ -c dialyzer.c
+ $(V_CC) $(CFLAGS) -o $@ -c dialyzer.c
$(BINDIR)/typer@EXEEXT@: $(OBJDIR)/typer.o $(ERTS_LIB)
- $(PURIFY) $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/typer.o -L$(OBJDIR) $(LIBS) $(ERTS_INTERNAL_LIBS)
+ $(ld_verbose)$(PURIFY) $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/typer.o -L$(OBJDIR) $(LIBS) $(ERTS_INTERNAL_LIBS)
$(OBJDIR)/typer.o: typer.c $(RC_GENERATED)
- $(CC) $(CFLAGS) -o $@ -c typer.c
+ $(V_CC) $(CFLAGS) -o $@ -c typer.c
$(BINDIR)/escript@EXEEXT@: $(OBJDIR)/escript.o $(ERTS_LIB)
- $(PURIFY) $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/escript.o -L$(OBJDIR) $(LIBS) $(ERTS_INTERNAL_LIBS)
+ $(ld_verbose)$(PURIFY) $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/escript.o -L$(OBJDIR) $(LIBS) $(ERTS_INTERNAL_LIBS)
$(OBJDIR)/escript.o: escript.c $(RC_GENERATED)
- $(CC) $(CFLAGS) -o $@ -c escript.c
+ $(V_CC) $(CFLAGS) -o $@ -c escript.c
$(BINDIR)/ct_run@EXEEXT@: $(OBJDIR)/ct_run.o $(ERTS_LIB)
- $(PURIFY) $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/ct_run.o -L$(OBJDIR) $(LIBS) $(ERTS_INTERNAL_LIBS)
+ $(ld_verbose)$(PURIFY) $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/ct_run.o -L$(OBJDIR) $(LIBS) $(ERTS_INTERNAL_LIBS)
$(OBJDIR)/ct_run.o: ct_run.c $(RC_GENERATED)
- $(CC) $(CFLAGS) -o $@ -c ct_run.c
+ $(V_CC) $(CFLAGS) -o $@ -c ct_run.c
Install: ../unix/Install.src ../../vsn.mk $(TARGET)/Makefile
- sed -e 's;%I_VSN%;$(VSN);' \
+ $(vsn_verbose)sed -e 's;%I_VSN%;$(VSN);' \
-e 's;%EMULATOR%;$(EMULATOR);' \
-e 's;%EMULATOR_NUMBER%;$(EMULATOR_NUMBER);' \
-e 's;%I_SYSTEM_VSN%;$(SYSTEM_VSN);' \
../unix/Install.src > Install
erl.src: ../unix/erl.src.src ../../vsn.mk $(TARGET)/Makefile
- sed -e 's;%EMULATOR%;$(EMULATOR);' \
+ $(vsn_verbose)sed -e 's;%EMULATOR%;$(EMULATOR);' \
-e 's;%EMULATOR_NUMBER%;$(EMULATOR_NUMBER);' \
-e 's;%VSN%;$(VSN);' \
../unix/erl.src.src > erl.src
diff --git a/erts/etc/common/dialyzer.c b/erts/etc/common/dialyzer.c
index 70b11b1b36..b8a7a2bf03 100644
--- a/erts/etc/common/dialyzer.c
+++ b/erts/etc/common/dialyzer.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2006-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2006-2012. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
diff --git a/erts/etc/common/erlc.c b/erts/etc/common/erlc.c
index 0223cc5274..f63ba3ee64 100644
--- a/erts/etc/common/erlc.c
+++ b/erts/etc/common/erlc.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1997-2011. All Rights Reserved.
+ * Copyright Ericsson AB 1997-2012. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -335,7 +335,7 @@ main(int argc, char** argv)
/* Push the following options:
* o makedep_phony
*/
- buf = strsave("makedep_add_missing");
+ buf = strsave("makedep_phony");
PUSH2("@option", buf);
break;
default:
diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c
index 790b0ed400..d865164bb0 100644
--- a/erts/etc/common/erlexec.c
+++ b/erts/etc/common/erlexec.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2011. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2012. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -124,9 +124,11 @@ static char *pluss_val_switches[] = {
"bwt",
"cl",
"ct",
+ "tbt",
"wt",
"ws",
"ss",
+ "pp",
NULL
};
/* +h arguments with values */
@@ -245,7 +247,9 @@ static char* config_script = NULL; /* used by option -start_erl and -config */
static HANDLE this_module_handle;
static int run_werl;
-
+static WCHAR *utf8_to_utf16(unsigned char *bytes);
+static char *utf16_to_utf8(WCHAR *wstr);
+static WCHAR *latin1_to_utf16(char *str);
#endif
/*
@@ -263,8 +267,12 @@ static void
set_env(char *key, char *value)
{
#ifdef __WIN32__
- if (!SetEnvironmentVariable((LPCTSTR) key, (LPCTSTR) value))
+ WCHAR *wkey = latin1_to_utf16(key);
+ WCHAR *wvalue = utf8_to_utf16(value);
+ if (!SetEnvironmentVariableW(wkey, wvalue))
error("SetEnvironmentVariable(\"%s\", \"%s\") failed!", key, value);
+ efree(wkey);
+ efree(wvalue);
#else
size_t size = strlen(key) + 1 + strlen(value) + 1;
char *str = emalloc(size);
@@ -277,25 +285,33 @@ set_env(char *key, char *value)
#endif
}
+
static char *
get_env(char *key)
{
#ifdef __WIN32__
DWORD size = 32;
- char *value = NULL;
+ WCHAR *value = NULL;
+ WCHAR *wkey = latin1_to_utf16(key);
+ char *res;
while (1) {
DWORD nsz;
if (value)
efree(value);
- value = emalloc(size);
+ value = emalloc(size*sizeof(WCHAR));
SetLastError(0);
- nsz = GetEnvironmentVariable((LPCTSTR) key, (LPTSTR) value, size);
+ nsz = GetEnvironmentVariableW(wkey, value, size);
if (nsz == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND) {
efree(value);
+ efree(wkey);
return NULL;
}
- if (nsz <= size)
- return value;
+ if (nsz <= size) {
+ efree(wkey);
+ res = utf16_to_utf8(value);
+ efree(value);
+ return res;
+ }
size = nsz;
}
#else
@@ -785,7 +801,9 @@ int main(int argc, char **argv)
case 'A':
case 'b':
case 'i':
+ case 'n':
case 'P':
+ case 'Q':
case 'S':
case 't':
case 'T':
@@ -975,8 +993,7 @@ int main(int argc, char **argv)
if (print_args_exit) {
for (i = 1; i < EargsCnt; i++)
- printf("%s ", Eargsp[i]);
- printf("\n");
+ printf("%s\n", Eargsp[i]);
exit(0);
}
@@ -1090,7 +1107,8 @@ usage_aux(void)
"[-make] [-man [manopts] MANPAGE] [-x] [-emu_args] "
"[-args_file FILENAME] [+A THREADS] [+a SIZE] [+B[c|d|i]] [+c] "
"[+h HEAP_SIZE_OPTION] [+K BOOLEAN] "
- "[+l] [+M<SUBSWITCH> <ARGUMENT>] [+P MAX_PROCS] [+R COMPAT_REL] "
+ "[+l] [+M<SUBSWITCH> <ARGUMENT>] [+P MAX_PROCS] [+Q MAX_PORTS] "
+ "[+R COMPAT_REL] "
"[+r] [+rg READER_GROUPS_LIMIT] [+s SCHEDULER_OPTION] "
"[+S NO_SCHEDULERS:NO_SCHEDULERS_ONLINE] [+T LEVEL] [+V] [+v] "
"[+W<i|w>] [+z MISC_OPTION] [args ...]\n");
@@ -2080,4 +2098,147 @@ possibly_quote(char* arg)
return narg;
}
+/*
+ * Unicode helpers to handle environment and command line parameters on
+ * Windows. We internally handle all environment variables in UTF8,
+ * but put and get the environment using the WCHAR (limited UTF16) interface
+ *
+ * These are simplified to only handle Unicode characters that can fit in
+ * Windows simplified UTF16, i.e. characters that fit in 16 bits.
+ */
+
+static int utf8_len(unsigned char first)
+{
+ if ((first & ((unsigned char) 0x80)) == 0) {
+ return 1;
+ } else if ((first & ((unsigned char) 0xE0)) == 0xC0) {
+ return 2;
+ } else if ((first & ((unsigned char) 0xF0)) == 0xE0) {
+ return 3;
+ } else if ((first & ((unsigned char) 0xF8)) == 0xF0) {
+ return 4;
+ }
+ return 1; /* will be a '?' */
+}
+
+static WCHAR *utf8_to_utf16(unsigned char *bytes)
+{
+ unsigned int unipoint;
+ unsigned char *tmp = bytes;
+ WCHAR *target, *res;
+ int num = 0;
+
+ while (*tmp) {
+ num++;
+ tmp += utf8_len(*tmp);
+ }
+ res = target = emalloc((num + 1) * sizeof(WCHAR));
+ while (*bytes) {
+ if (((*bytes) & ((unsigned char) 0x80)) == 0) {
+ unipoint = (Uint) *bytes;
+ ++bytes;
+ } else if (((*bytes) & ((unsigned char) 0xE0)) == 0xC0) {
+ unipoint =
+ (((Uint) ((*bytes) & ((unsigned char) 0x1F))) << 6) |
+ ((Uint) (bytes[1] & ((unsigned char) 0x3F)));
+ bytes += 2;
+ } else if (((*bytes) & ((unsigned char) 0xF0)) == 0xE0) {
+ unipoint =
+ (((Uint) ((*bytes) & ((unsigned char) 0xF))) << 12) |
+ (((Uint) (bytes[1] & ((unsigned char) 0x3F))) << 6) |
+ ((Uint) (bytes[2] & ((unsigned char) 0x3F)));
+ if (unipoint > 0xFFFF) {
+ unipoint = (unsigned int) '?';
+ }
+ bytes +=3;
+ } else if (((*bytes) & ((unsigned char) 0xF8)) == 0xF0) {
+ unipoint = (unsigned int) '?'; /* Cannot put in a wchar */
+ bytes += 4;
+ } else {
+ unipoint = (unsigned int) '?';
+ }
+ *target++ = (WCHAR) unipoint;
+ }
+ *target = L'\0';
+ return res;
+}
+
+static int put_utf8(WCHAR ch, unsigned char *target, int sz, int *pos)
+{
+ Uint x = (Uint) ch;
+ if (x < 0x80) {
+ if (*pos >= sz) {
+ return -1;
+ }
+ target[(*pos)++] = (unsigned char) x;
+ }
+ else if (x < 0x800) {
+ if (((*pos) + 1) >= sz) {
+ return -1;
+ }
+ target[(*pos)++] = (((unsigned char) (x >> 6)) |
+ ((unsigned char) 0xC0));
+ target[(*pos)++] = (((unsigned char) (x & 0x3F)) |
+ ((unsigned char) 0x80));
+ } else {
+ if ((x >= 0xD800 && x <= 0xDFFF) ||
+ (x == 0xFFFE) ||
+ (x == 0xFFFF)) { /* Invalid unicode range */
+ return -1;
+ }
+ if (((*pos) + 2) >= sz) {
+ return -1;
+ }
+
+ target[(*pos)++] = (((unsigned char) (x >> 12)) |
+ ((unsigned char) 0xE0));
+ target[(*pos)++] = ((((unsigned char) (x >> 6)) & 0x3F) |
+ ((unsigned char) 0x80));
+ target[(*pos)++] = (((unsigned char) (x & 0x3F)) |
+ ((unsigned char) 0x80));
+ }
+ return 0;
+}
+
+static int need_bytes_for_utf8(WCHAR x)
+{
+ if (x < 0x80)
+ return 1;
+ else if (x < 0x800)
+ return 2;
+ else
+ return 3;
+}
+
+static WCHAR *latin1_to_utf16(char *str)
+{
+ int len = strlen(str);
+ int i;
+ WCHAR *wstr = emalloc((len+1) * sizeof(WCHAR));
+ for(i=0;i<len;++i)
+ wstr[i] = (WCHAR) str[i];
+ wstr[len] = L'\0';
+ return wstr;
+}
+
+static char *utf16_to_utf8(WCHAR *wstr)
+{
+ int len = wcslen(wstr);
+ char *result;
+ int i,pos;
+ int reslen = 0;
+ for(i=0;i<len;++i) {
+ reslen += need_bytes_for_utf8(wstr[i]);
+ }
+ result = emalloc(reslen+1);
+ pos = 0;
+ for(i=0;i<len;++i) {
+ if (put_utf8((int) wstr[i], result, reslen, &pos) < 0) {
+ break;
+ }
+ }
+ result[pos] = '\0';
+ return result;
+}
+
#endif
diff --git a/erts/etc/common/escript.c b/erts/etc/common/escript.c
index 9e80ec6656..3566406bf3 100644
--- a/erts/etc/common/escript.c
+++ b/erts/etc/common/escript.c
@@ -264,7 +264,7 @@ append_shebang_args(char* scriptname)
static char linebuf[LINEBUFSZ];
char* ptr = fgets(linebuf, LINEBUFSZ, fd);
- if (ptr != NULL && linebuf[0] == '#' && linebuf[1] == '!') {
+ if (ptr != NULL) {
/* Try to find args on second or third line */
ptr = fgets(linebuf, LINEBUFSZ, fd);
if (ptr != NULL && linebuf[0] == '%' && linebuf[1] == '%' && linebuf[2] == '!') {
diff --git a/erts/etc/common/heart.c b/erts/etc/common/heart.c
index 755e308219..81d797dc7e 100644
--- a/erts/etc/common/heart.c
+++ b/erts/etc/common/heart.c
@@ -66,8 +66,7 @@
* input and output file descriptors (0 and 1). These descriptors
* (and the standard error descriptor 2) must NOT be closed
* explicitely by this program at termination (in UNIX it is
- * taken care of by the operating system itself; in VxWorks
- * it is taken care of by the spawn driver part of the Emulator).
+ * taken care of by the operating system itself).
*
* END OF FILE
*
@@ -75,12 +74,6 @@
* that there is no process at the other end of the connection
* having the connection open for writing (end-of-file).
*
- * HARDWARE WATCHDOG
- *
- * When used with VxWorks(with CPU40), the hardware
- * watchdog is enabled, making sure that the system reboots
- * even if the heart port program malfunctions or the system
- * is completely overloaded.
*/
#ifdef HAVE_CONFIG_H
@@ -93,18 +86,12 @@
#include <fcntl.h>
#include <process.h>
#endif
-#ifdef VXWORKS
-#include "sys.h"
-#endif
/*
* Implement time correction using times() call even on Linuxes
* that can simulate gethrtime with clock_gettime, no use implementing
* a phony gethrtime in this file as the time questions are so infrequent.
*/
-#if defined(CORRET_USING_TIMES) || defined(GETHRTIME_WITH_CLOCK_GETTIME)
-# define HEART_CORRECT_USING_TIMES 1
-#endif
#include <stdio.h>
#include <stddef.h>
@@ -116,31 +103,20 @@
#include <time.h>
#include <errno.h>
-#ifdef VXWORKS
-# include <vxWorks.h>
-# include <ioLib.h>
-# include <selectLib.h>
-# include <netinet/in.h>
-# include <rebootLib.h>
-# include <sysLib.h>
-# include <taskLib.h>
-# include <wdLib.h>
-# include <taskHookLib.h>
-# include <selectLib.h>
-#endif
-#if !defined(__WIN32__) && !defined(VXWORKS)
+#if !defined(__WIN32__)
# include <sys/types.h>
# include <netinet/in.h>
# include <sys/time.h>
# include <unistd.h>
# include <signal.h>
-# if defined(HEART_CORRECT_USING_TIMES)
+# if defined(CORRECT_USING_TIMES)
# include <sys/times.h>
# include <limits.h>
# endif
#endif
-#define HEART_COMMAND_ENV "HEART_COMMAND"
+#define HEART_COMMAND_ENV "HEART_COMMAND"
+#define ERL_CRASH_DUMP_SECONDS_ENV "ERL_CRASH_DUMP_SECONDS"
#define MSG_HDR_SIZE 2
#define MSG_HDR_PLUS_OP_SIZE 3
@@ -156,13 +132,14 @@ struct msg {
};
/* operations */
-#define HEART_ACK 1
-#define HEART_BEAT 2
-#define SHUT_DOWN 3
-#define SET_CMD 4
-#define CLEAR_CMD 5
-#define GET_CMD 6
-#define HEART_CMD 7
+#define HEART_ACK (1)
+#define HEART_BEAT (2)
+#define SHUT_DOWN (3)
+#define SET_CMD (4)
+#define CLEAR_CMD (5)
+#define GET_CMD (6)
+#define HEART_CMD (7)
+#define PREPARING_CRASH (8)
/* Maybe interesting to change */
@@ -190,10 +167,11 @@ unsigned long heart_beat_kill_pid = 0;
#define SOL_WD_TIMEOUT (heart_beat_timeout+heart_beat_boot_delay)
/* reasons for reboot */
-#define R_TIMEOUT 1
-#define R_CLOSED 2
-#define R_ERROR 3
-#define R_SHUT_DOWN 4
+#define R_TIMEOUT (1)
+#define R_CLOSED (2)
+#define R_ERROR (3)
+#define R_SHUT_DOWN (4)
+#define R_CRASHING (5) /* Doing a crash dump and we will wait for it */
/* macros */
@@ -203,8 +181,8 @@ unsigned long heart_beat_kill_pid = 0;
/* prototypes */
-static int message_loop(int,int);
-static void do_terminate(int);
+static int message_loop(int, int);
+static void do_terminate(int, int);
static int notify_ack(int);
static int heart_cmd_reply(int, char *);
static int write_message(int, struct msg *);
@@ -215,6 +193,7 @@ static void print_error(const char *,...);
static void debugf(const char *,...);
static void init_timestamp(void);
static time_t timestamp(time_t *);
+static int wait_until_close_write_or_env_tmo(int);
#ifdef __WIN32__
static BOOL enable_privilege(void);
@@ -353,12 +332,14 @@ static void get_arguments(int argc, char** argv) {
debugf("arguments -ht %d -wt %d -pid %lu\n",h,w,p);
}
-int
-main(int argc, char **argv)
-{
+int main(int argc, char **argv) {
+
+ if (is_env_set("HEART_DEBUG")) {
+ fprintf(stderr, "heart: debug is ON!\r\n");
+ debug_on = 1;
+ }
+
get_arguments(argc,argv);
- if (is_env_set("HEART_DEBUG"))
- debug_on=1;
#ifdef __WIN32__
if (debug_on) {
if(!is_env_set("ERLSRV_SERVICE_NAME")) {
@@ -379,7 +360,7 @@ main(int argc, char **argv)
program_name[sizeof(program_name)-1] = '\0';
notify_ack(erlout_fd);
cmd[0] = '\0';
- do_terminate(message_loop(erlin_fd,erlout_fd));
+ do_terminate(erlin_fd,message_loop(erlin_fd,erlout_fd));
return 0;
}
@@ -413,6 +394,7 @@ message_loop(erlin_fd, erlout_fd)
#endif
while (1) {
+ /* REFACTOR: below to select/tmo function */
#ifdef __WIN32__
wresult = WaitForSingleObject(hevent_dataready,SELECT_TIMEOUT*1000+ 2);
if (wresult == WAIT_FAILED) {
@@ -447,7 +429,8 @@ message_loop(erlin_fd, erlout_fd)
*/
timestamp(&now);
if (now > last_received + heart_beat_timeout) {
- print_error("heart-beat time-out.");
+ print_error("heart-beat time-out, no activity for %lu seconds",
+ (unsigned long) (now - last_received));
return R_TIMEOUT;
}
/*
@@ -506,6 +489,10 @@ message_loop(erlin_fd, erlout_fd)
free_env_val(env);
}
break;
+ case PREPARING_CRASH:
+ /* Erlang has reached a crushdump point (is crashing for sure) */
+ print_error("Erlang is crashing .. (waiting for crash dump file)");
+ return R_CRASHING;
default:
/* ignore all other messages */
break;
@@ -550,8 +537,7 @@ kill_old_erlang(void){
CloseHandle(erlh);
}
}
-#elif !defined(VXWORKS)
-/* Unix eh? */
+#else
static void
kill_old_erlang(void){
pid_t pid;
@@ -570,7 +556,7 @@ kill_old_erlang(void){
}
}
}
-#endif /* Not on VxWorks */
+#endif
#ifdef __WIN32__
void win_system(char *command)
@@ -637,72 +623,130 @@ void win_system(char *command)
* do_terminate
*/
static void
-do_terminate(reason)
- int reason;
-{
+do_terminate(int erlin_fd, int reason) {
/*
When we get here, we have HEART_BEAT_BOOT_DELAY secs to finish
(plus heart_beat_report_delay if under VxWorks), so we don't need
to call wd_reset().
*/
-
+ int ret = 0, tmo=0;
+ char *tmo_env;
+
switch (reason) {
case R_SHUT_DOWN:
break;
+ case R_CRASHING:
+ if (is_env_set(ERL_CRASH_DUMP_SECONDS_ENV)) {
+ tmo_env = get_env(ERL_CRASH_DUMP_SECONDS_ENV);
+ tmo = atoi(tmo_env);
+ print_error("Waiting for dump - timeout set to %d seconds.", tmo);
+ wait_until_close_write_or_env_tmo(tmo);
+ free_env_val(tmo_env);
+ }
+ /* fall through */
case R_TIMEOUT:
- case R_ERROR:
case R_CLOSED:
+ case R_ERROR:
default:
-#if defined(__WIN32__) /* Not VxWorks */
{
- if(!cmd[0]) {
- char *command = get_env(HEART_COMMAND_ENV);
- if(!command)
- print_error("Would reboot. Terminating.");
- else {
- kill_old_erlang();
- /* High prio combined with system() works badly indeed... */
- SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS);
- win_system(command);
- print_error("Executed \"%s\". Terminating.",command);
+#if defined(__WIN32__) /* Not VxWorks */
+ if(!cmd[0]) {
+ char *command = get_env(HEART_COMMAND_ENV);
+ if(!command)
+ print_error("Would reboot. Terminating.");
+ else {
+ kill_old_erlang();
+ /* High prio combined with system() works badly indeed... */
+ SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS);
+ win_system(command);
+ print_error("Executed \"%s\". Terminating.",command);
+ }
+ free_env_val(command);
+ } else {
+ kill_old_erlang();
+ /* High prio combined with system() works badly indeed... */
+ SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS);
+ win_system(&cmd[0]);
+ print_error("Executed \"%s\". Terminating.",cmd);
}
- free_env_val(command);
- }
- else {
- kill_old_erlang();
- /* High prio combined with system() works badly indeed... */
- SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS);
- win_system(&cmd[0]);
- print_error("Executed \"%s\". Terminating.",cmd);
- }
- }
-
#else
- {
- if(!cmd[0]) {
- char *command = get_env(HEART_COMMAND_ENV);
- if(!command)
- print_error("Would reboot. Terminating.");
- else {
- kill_old_erlang();
- /* suppress gcc warning with 'if' */
- if(system(command));
- print_error("Executed \"%s\". Terminating.",command);
+ if(!cmd[0]) {
+ char *command = get_env(HEART_COMMAND_ENV);
+ if(!command)
+ print_error("Would reboot. Terminating.");
+ else {
+ kill_old_erlang();
+ /* suppress gcc warning with 'if' */
+ ret = system(command);
+ print_error("Executed \"%s\" -> %d. Terminating.",command, ret);
+ }
+ free_env_val(command);
+ } else {
+ kill_old_erlang();
+ /* suppress gcc warning with 'if' */
+ ret = system((char*)&cmd[0]);
+ print_error("Executed \"%s\" -> %d. Terminating.",cmd, ret);
}
- free_env_val(command);
- }
- else {
- kill_old_erlang();
- /* suppress gcc warning with 'if' */
- if(system((char*)&cmd[0]));
- print_error("Executed \"%s\". Terminating.",cmd);
- }
+#endif
}
break;
-#endif
} /* switch(reason) */
}
+
+/* Waits until something happens on socket or handle
+ *
+ * Uses global variables erlin_fd or hevent_dataready
+ */
+int wait_until_close_write_or_env_tmo(int tmo) {
+ int i = 0;
+
+#ifdef __WIN32__
+ DWORD wresult;
+ DWORD wtmo = INFINITE;
+
+ if (tmo >= 0) {
+ wtmo = tmo*1000 + 2;
+ }
+
+ wresult = WaitForSingleObject(hevent_dataready, wtmo);
+ if (wresult == WAIT_FAILED) {
+ print_last_error();
+ return -1;
+ }
+
+ if (wresult == WAIT_TIMEOUT) {
+ debugf("wait timed out\n");
+ i = 0;
+ } else {
+ debugf("wait ok\n");
+ i = 1;
+ }
+#else
+ fd_set read_fds;
+ int max_fd;
+ struct timeval timeout;
+ struct timeval *tptr = NULL;
+
+ max_fd = erlin_fd; /* global */
+
+ if (tmo >= 0) {
+ timeout.tv_sec = tmo; /* On Linux timeout is modified by select */
+ timeout.tv_usec = 0;
+ tptr = &timeout;
+ }
+
+ FD_ZERO(&read_fds);
+ FD_SET(erlin_fd, &read_fds);
+ if ((i = select(max_fd + 1, &read_fds, NULLFDS, NULLFDS, tptr)) < 0) {
+ print_error("error in select.");
+ return -1;
+ }
+#endif
+ return i;
+}
+
+
/*
* notify_ack
*
@@ -893,12 +937,13 @@ debugf(const char *format,...)
{
va_list args;
- if (!debug_on) return;
- va_start(args, format);
- fprintf(stderr, "Heart: ");
- vfprintf(stderr, format, args);
- va_end(args);
- fprintf(stderr, "\r\n");
+ if (debug_on) {
+ va_start(args, format);
+ fprintf(stderr, "Heart: ");
+ vfprintf(stderr, format, args);
+ va_end(args);
+ fprintf(stderr, "\r\n");
+ }
}
#ifdef __WIN32__
@@ -1026,60 +1071,30 @@ time_t timestamp(time_t *res)
return r;
}
-#elif defined(VXWORKS)
-
-static WDOG_ID watchdog_id;
-static volatile unsigned elapsed;
-static WIND_TCB *this_task;
-/* A simple variable is enough to lock the time update, as the
- watchdog is run at interrupt level and never preempted. */
-static volatile int lock_time;
-
-static void my_delete_hook(WIND_TCB *tcb)
-{
- if (tcb == this_task) {
- wdDelete(watchdog_id);
- watchdog_id = NULL;
- taskDeleteHookDelete((FUNCPTR) &my_delete_hook);
- }
-}
+#elif defined(HAVE_GETHRTIME) || defined(GETHRTIME_WITH_CLOCK_GETTIME)
-static void my_wd_routine(int count)
-{
- if (watchdog_id != NULL) {
- ++count;
- if (!lock_time) {
- elapsed += count;
- count = 0;
- }
- wdStart(watchdog_id, sysClkRateGet(),
- (FUNCPTR) &my_wd_routine, count);
- }
-}
+#if defined(GETHRTIME_WITH_CLOCK_GETTIME)
+typedef long long SysHrTime;
-void init_timestamp(void)
-{
- lock_time = 0;
- elapsed = 0;
- watchdog_id = wdCreate();
- this_task = (WIND_TCB *) taskIdSelf();
- taskDeleteHookAdd((FUNCPTR) &my_delete_hook);
- wdStart(watchdog_id, sysClkRateGet(),
- (FUNCPTR) &my_wd_routine, 0);
-}
+SysHrTime sys_gethrtime(void);
-time_t timestamp(time_t *res)
+SysHrTime sys_gethrtime(void)
{
- time_t r;
- ++lock_time;
- r = (time_t) elapsed;
- --lock_time;
- if (res != NULL)
- *res = r;
- return r;
+ struct timespec ts;
+ long long result;
+ if (clock_gettime(CLOCK_MONOTONIC,&ts) != 0) {
+ print_error("Fatal, could not get clock_monotonic value, terminating! "
+ "errno = %d\n", errno);
+ exit(1);
+ }
+ result = ((long long) ts.tv_sec) * 1000000000LL +
+ ((long long) ts.tv_nsec);
+ return (SysHrTime) result;
}
-
-#elif defined(HAVE_GETHRTIME)
+#else
+typedef hrtime_t SysHrTime;
+#define sys_gethrtime() gethrtime()
+#endif
void init_timestamp(void)
{
@@ -1087,14 +1102,14 @@ void init_timestamp(void)
time_t timestamp(time_t *res)
{
- hrtime_t ht = gethrtime();
+ SysHrTime ht = sys_gethrtime();
time_t r = (time_t) (ht / 1000000000);
if (res != NULL)
*res = r;
return r;
}
-#elif defined(HEART_CORRECT_USING_TIMES)
+#elif defined(CORRECT_USING_TIMES)
# ifdef NO_SYSCONF
# include <sys/param.h>
diff --git a/erts/etc/common/inet_gethost.c b/erts/etc/common/inet_gethost.c
index e923233ce9..b9a0e6bde3 100644
--- a/erts/etc/common/inet_gethost.c
+++ b/erts/etc/common/inet_gethost.c
@@ -2522,7 +2522,7 @@ static char *format_address(int siz, AddrByte *addr)
*buff='\0';
if (siz <= 4) {
while(siz--) {
- sprintf(tmp,"%d",(int) *addr++);
+ erts_snprintf(tmp, sizeof(tmp), "%d",(int) *addr++);
strcat(buff,tmp);
if(siz) {
strcat(buff,".");
@@ -2531,7 +2531,7 @@ static char *format_address(int siz, AddrByte *addr)
return buff;
}
while(siz--) {
- sprintf(tmp,"%02x",(int) *addr++);
+ erts_snprintf(tmp, sizeof(tmp), "%02x",(int) *addr++);
strcat(buff,tmp);
if(siz) {
strcat(buff,":");
@@ -2548,9 +2548,9 @@ static void debugf(char *format, ...)
va_start(ap,format);
#ifdef WIN32
- sprintf(buff,"%s[%d] (DEBUG):",program_name,(int) GetCurrentThreadId());
+ erts_snprintf(buff, sizeof(buff), "%s[%d] (DEBUG):",program_name,(int) GetCurrentThreadId());
#else
- sprintf(buff,"%s[%d] (DEBUG):",program_name,(int) getpid());
+ erts_snprintf(buff, sizeof(buff), "%s[%d] (DEBUG):",program_name,(int) getpid());
#endif
ptr = buff + strlen(buff);
erts_vsnprintf(ptr,sizeof(buff)-strlen(buff)-2,format,ap);
@@ -2574,7 +2574,7 @@ static void warning(char *format, ...)
va_list ap;
va_start(ap,format);
- sprintf(buff,"%s[%d]: WARNING:",program_name, (int) getpid());
+ erts_snprintf(buff, sizeof(buff), "%s[%d]: WARNING:",program_name, (int) getpid());
ptr = buff + strlen(buff);
erts_vsnprintf(ptr,sizeof(buff)-strlen(buff)-2,format,ap);
strcat(ptr,"\r\n");
@@ -2597,7 +2597,7 @@ static void fatal(char *format, ...)
va_list ap;
va_start(ap,format);
- sprintf(buff,"%s[%d]: FATAL ERROR:",program_name, (int) getpid());
+ erts_snprintf(buff, sizeof(buff), "%s[%d]: FATAL ERROR:",program_name, (int) getpid());
ptr = buff + strlen(buff);
erts_vsnprintf(ptr,sizeof(buff)-strlen(buff)-2,format,ap);
strcat(ptr,"\r\n");
diff --git a/erts/etc/unix/Install.src b/erts/etc/unix/Install.src
index 58f7b38ed0..2dcd070a6d 100644
--- a/erts/etc/unix/Install.src
+++ b/erts/etc/unix/Install.src
@@ -2,7 +2,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2010. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/erts/etc/unix/cerl.src b/erts/etc/unix/cerl.src
index 6a431ce4da..cc7d77fd9a 100644
--- a/erts/etc/unix/cerl.src
+++ b/erts/etc/unix/cerl.src
@@ -2,7 +2,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2003-2011. All Rights Reserved.
+# Copyright Ericsson AB 2003-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -267,11 +267,16 @@ if [ "x$GDB" = "x" ]; then
valgrind_misc_flags="$VALGRIND_MISC_FLAGS"
fi
beam_args=`$EXEC -emu_args_exit ${1+"$@"}`
- # Ahhhh... Need to quote $PROGNAME...
- early_beam_args=`echo $beam_args | sed "s|^\(.*-progname\).*$|\1|g"`
- late_beam_args=`echo $beam_args | sed "s|^$pre_beam_args.*\(-- -home.*\)$|\1|g"`
-
- exec valgrind $valgrind_xml $valgrind_log $valgrind_misc_flags $BINDIR/$EMU_NAME $emu_xargs $early_beam_args "$PROGNAME" $late_beam_args -pz $PRELOADED
+
+ # Time for some argument passing voodoo:
+ # $beam_args is a list of command line arguments separated by newlines.
+ # Make "$@" represent those arguments verbatim (including spaces and quotes).
+ SAVE_IFS="$IFS"
+ IFS='
+'
+ set -- $beam_args
+ IFS="$SAVE_IFS"
+ exec valgrind $valgrind_xml $valgrind_log $valgrind_misc_flags $BINDIR/$EMU_NAME $emu_xargs "$@" -pz $PRELOADED
else
exec $EXEC $eeargs $xargs ${1+"$@"}
fi
@@ -299,10 +304,17 @@ else
;;
esac
- # Set annotation level for gdb in emacs 22 and higher.
- emacs_major=`$EMACS --version | head -1 | sed 's,^[^0-9]*\([0-9]*\).*,\1,g'`
- if [ '!' -z "$emacs_major" -a $emacs_major -gt 21 ]; then
- GDBARGS="--annotate=3 "
+ if [ "$EMACS_ANNOTATE_LEVEL" != "" ]; then
+ GDBARGS="--annotate=$EMACS_ANNOTATE_LEVEL"
+ else
+ # Set annotation level for gdb in emacs 22 and higher. Seems to
+ # be working with level 1 for emacs 22 and level 3 for emacs 23...
+ emacs_major=`$EMACS --version | head -1 | sed 's,^[^0-9]*\([0-9]*\).*,\1,g'`
+ if [ '!' -z "$emacs_major" -a $emacs_major -gt 22 ]; then
+ GDBARGS="--annotate=3 "
+ elif [ '!' -z "$emacs_major" -a $emacs_major -gt 21 ]; then
+ GDBARGS="--annotate=1 "
+ fi
fi
gdbcmd="$gdbcmd $GDBBP \
(insert-string \"source $ROOTDIR/erts/etc/unix/etp-commands\") \
diff --git a/erts/etc/unix/erl.src.src b/erts/etc/unix/erl.src.src
index fa187c5509..ce5d2b5def 100644
--- a/erts/etc/unix/erl.src.src
+++ b/erts/etc/unix/erl.src.src
@@ -2,7 +2,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2009. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/erts/etc/unix/etp-commands b/erts/etc/unix/etp-commands
index aed8a6461a..f059662271 100644
--- a/erts/etc/unix/etp-commands
+++ b/erts/etc/unix/etp-commands
@@ -705,8 +705,6 @@ define etp-ct-name-1
end
end
-
-
define etp-pid-1
# Args: Eterm pid
#
@@ -714,9 +712,17 @@ define etp-pid-1
#
set $etp_pid_1 = (Eterm)($arg0)
if ($etp_pid_1 & 0xF) == 0x3
+ if (etp_arch_bits == 64 && etp_halfword == 0)
+ if (etp_big_endian)
+ set $etp_pid_data = (unsigned) ((((Uint64) $etp_pid_1) >> 36) & 0x0fffffff)
+ else
+ set $etp_pid_data = (unsigned) ((((Uint64) $etp_pid_1) >> 4) & 0x0fffffff)
+ end
+ else
+ set $etp_pid_data = (unsigned) (((((Uint32) $etp_pid_1) >> 4) & ~erts_proc.r.o.pix_mask) | ((((Uint32) $etp_pid_1) >> (erts_proc.r.o.pix_cl_shift + 4)) & erts_proc.r.o.pix_cl_mask) | (((((Uint32) $etp_pid_1) >> 4) & erts_proc.r.o.pix_cli_mask) << erts_proc.r.o.pix_cli_shift))
+ end
# Internal pid
- printf "<0.%u.%u>", (unsigned) ($etp_pid_1>>4)&0x7fff, \
- (unsigned) ($etp_pid_1>>19)&0x1fff
+ printf "<0.%u.%u>", $etp_pid_data & 0x7fff, ($etp_pid_data >> 15) & 0x1fff
else
printf "#NotPid<%#x>", ($arg0)
end
@@ -759,7 +765,6 @@ define etp-extpid-1
end
-
define etp-port-1
# Args: Eterm port
#
@@ -767,8 +772,17 @@ define etp-port-1
#
set $etp_port_1 = (Eterm)($arg0)
if ($etp_port_1 & 0xF) == 0x7
+ if (etp_arch_bits == 64 && etp_halfword == 0)
+ if (etp_big_endian)
+ set $etp_port_data = (unsigned) ((((Uint64) $etp_port_1) >> 36) & 0x0fffffff)
+ else
+ set $etp_port_data = (unsigned) ((((Uint64) $etp_port_1) >> 4) & 0x0fffffff)
+ end
+ else
+ set $etp_port_data = (unsigned) (((((Uint32) $etp_port_1) >> 4) & ~erts_port.r.o.pix_mask) | ((((Uint32) $etp_port_1) >> (erts_port.r.o.pix_cl_shift + 4)) & erts_port.r.o.pix_cl_mask) | (((((Uint32) $etp_port_1) >> 4) & erts_port.r.o.pix_cli_mask) << erts_port.r.o.pix_cli_shift))
+ end
# Internal port
- printf "#Port<0.%u>", (unsigned) ($etp_port_1>>4)&0x3ffff
+ printf "#Port<0.%u>", $etp_port_data
else
printf "#NotPort<%#x>", ($arg0)
end
@@ -1264,23 +1278,32 @@ document etpf-stackdump
%---------------------------------------------------------------------------
end
+define etp-pid2pix-1
+# Args: Eterm
+#
+ if (etp_arch_bits == 64 && etp_halfword == 0)
+ if (etp_big_endian)
+ set $etp_pix = (int) (((Uint64) $arg0) & 0x0fffffff)
+ else
+ set $etp_pix = (int) ((((Uint64) $arg0) >> 32) & 0x0fffffff)
+ end
+ else
+ set $etp_pix = (int) ((((Uint32) $arg0) >> 4) & erts_proc.r.o.pix_mask)
+ end
+end
+
define etp-pix2proc
# Args: Eterm
#
- set $proc = (Process *) *((UWord *) &erts_proc.tab[((int) $arg0)])
+ set $proc = (Process *) *((UWord *) &erts_proc.r.o.tab[((int) $arg0)])
printf "(Process *) %p\n", $proc
end
define etp-pid2proc-1
# Args: Eterm
#
- set $pix = (int) ($arg0 >> 4)
-
- if (erts_proc.pix_cl_mask != 0)
- set $proc = (Process *) *((UWord *) &erts_proc.tab[(((($pix) & erts_proc.pix_cl_mask) << erts_proc.pix_cl_shift) + ((($pix) >> erts_proc.pix_cli_shift) & erts_proc.pix_cli_mask))])
- else
- set $proc =(Process *) *((UWord *) &erts_proc.tab[((($pix) % erts_proc.max) % erts_proc.tab_cache_lines + (($pix) % erts_proc.max) / erts_proc.tab_cache_lines)])
- end
+ etp-pid2pix-1 $arg0
+ set $proc = (Process *) *((UWord *) &erts_proc.r.o.tab[$etp_pix])
end
define etp-pid2proc
@@ -1369,13 +1392,15 @@ define etp-process-info
# Args: Process*
#
printf " Pid: "
- etp-1 $arg0->id
+ etp-1 $arg0->common.id
printf "\n State: "
etp-proc-state $arg0
- if ($arg0->reg)
- printf " Registered name: "
- etp-1 $arg0->reg->name
- printf "\n"
+ if (*(((Uint32 *) &(((Process *) $arg0)->state))) & 0x4) == 0
+ if ($arg0->common.u.alive.reg)
+ printf " Registered name: "
+ etp-1 $arg0->common.u.alive.reg->name
+ printf "\n"
+ end
end
if ($arg0->current)
printf " Current function: "
@@ -1422,9 +1447,9 @@ define etp-processes
printf "No processes, since system isn't initialized!\n"
else
set $proc_ix = 0
- while $proc_ix < erts_proc.max
- set $proc = (Process *) *((UWord *) &erts_proc.tab[$proc_ix])
- if ($proc != (Process *) 0)
+ while $proc_ix < erts_proc.r.o.max
+ set $proc = (Process *) *((UWord *) &erts_proc.r.o.tab[$proc_ix])
+ if ($proc != ((Process *) 0) && $proc != &erts_invalid_process)
printf "---\n"
printf " Pix: %d\n", $proc_ix
etp-process-info $proc
@@ -1443,8 +1468,108 @@ document etp-processes
%---------------------------------------------------------------------------
end
+define etp-port-id2pix-1
+# Args: Eterm
+#
+ if (etp_arch_bits == 64 && etp_halfword == 0)
+ if (etp_big_endian)
+ set $etp_pix = (int) (((Uint64) $arg0) & 0x0fffffff)
+ elser
+ set $etp_pix = (int) ((((Uint64) $arg0) >> 32) & 0x0fffffff)
+ end
+ else
+ set $etp_pix = (int) ((((Uint32) $arg0) >> 4) & erts_port.r.o.pix_mask)
+ end
+end
+
+define etp-pix2port
+# Args: Eterm
+#
+ set $port = (Port *) *((UWord *) &erts_port.r.o.tab[((int) $arg0)])
+ printf "(Port *) %p\n", $port
+end
-define etp-port-status-int
+define etp-id2port-1
+# Args: Eterm
+#
+ etp-port-id2pix-1 $arg0
+ set $port = (Port *) *((UWord *) &erts_port.r.o.tab[((int) $etp_pix)])
+end
+
+define etp-id2port
+# Args: Eterm
+#
+ etp-id2port-1 $arg0
+ printf "(Port *) %p\n", $port
+end
+
+define etp-port-sched-flags-int
+# Args: int
+#
+ if ($arg0 & 0x1)
+ printf " in-run-queue"
+ end
+ if ($arg0 & 0x2)
+ printf " executing"
+ end
+ if ($arg0 & 0x4)
+ printf " have-tasks"
+ end
+ if ($arg0 & 0x8)
+ printf " exited"
+ end
+ if ($arg0 & 0x10)
+ printf " busy-port"
+ end
+ if ($arg0 & 0x20)
+ printf " busy-port-q"
+ end
+ if ($arg0 & 0x40)
+ printf " chk-unset-busy-port-q"
+ end
+ if ($arg0 & 0x80)
+ printf " have-busy-tasks"
+ end
+ if ($arg0 & 0x100)
+ printf " have-nosuspend-tasks"
+ end
+ if ($arg0 & 0x200)
+ printf " parallelism"
+ end
+ if ($arg0 & 0x400)
+ printf " force-sched"
+ end
+ if ($arg0 & 0xfffff800)
+ printf " GARBAGE"
+ end
+ printf "\n"
+end
+
+document etp-port-sched-flags-int
+%---------------------------------------------------------------------------
+% etp-proc-sched-flags-int int
+%
+% Print port sched-flags
+%---------------------------------------------------------------------------
+end
+
+
+define etp-port-sched-flags
+# Args: Port*
+#
+ set $sched_flags_int = *(((Uint32 *) &(((Port *) $arg0)->sched.flags)))
+ etp-port-sched-flags-int $sched_flags_int
+end
+
+document etp-port-sched-flags
+%---------------------------------------------------------------------------
+% etp-proc-sched-flags-int Port *
+%
+% Print port sched-flags
+%---------------------------------------------------------------------------
+end
+
+define etp-port-state-int
# Args: int
#
if ($arg0 & 0x1)
@@ -1463,68 +1588,62 @@ define etp-port-status-int
printf " soft-eof"
end
if ($arg0 & 0x20)
- printf " port-busy"
+ printf " closing"
end
if ($arg0 & 0x40)
- printf " closing"
+ printf " send-closed"
end
if ($arg0 & 0x80)
- printf " send-closed"
+ printf " linebuf-io"
end
if ($arg0 & 0x100)
- printf " linebuf-io"
+ printf " free"
end
if ($arg0 & 0x200)
- printf " immortal"
+ printf " initializing"
end
if ($arg0 & 0x400)
- printf " free"
+ printf " port-specific-lock"
end
if ($arg0 & 0x800)
- printf " free-scheduled"
+ printf " invalid"
end
if ($arg0 & 0x1000)
- printf " initializing"
- end
- if ($arg0 & 0x2000)
- printf " port-specific-lock"
- end
- if ($arg0 & 0x4000)
- printf " invalid"
+ printf " halt"
end
if (etp_debug_compiled)
- if ($arg0 & 0x7fff8000)
+ if ($arg0 & 0x7fffe000)
printf " GARBAGE"
end
else
- if ($arg0 & 0xffff8000)
+ if ($arg0 & 0xffffe000)
printf " GARBAGE"
end
end
printf "\n"
end
-document etp-port-status-int
+document etp-port-state-int
%---------------------------------------------------------------------------
% etp-proc-state-int int
%
-% Print port status
+% Print port state
%---------------------------------------------------------------------------
end
-define etp-port-status
+define etp-port-state
# Args: Port*
#
- set $status_int = *(((Uint32 *) &(((Port *) $arg0)->status)))
- etp-port-status-int $status_int
+ set $state_int = *(((Uint32 *) &(((Port *) $arg0)->state)))
+ etp-port-state-int $state_int
end
-document etp-port-status
+document etp-port-state
%---------------------------------------------------------------------------
% etp-proc-state-int Port *
%
-% Print port status
+% Print port state
%---------------------------------------------------------------------------
end
@@ -1532,17 +1651,22 @@ define etp-port-info
# Args: Port*
#
printf " Port: "
- etp-1 $arg0->id
+ etp-1 $arg0->common.id
printf "\n Name: %s\n", $arg0->name
- printf " Status:"
- etp-port-status $arg0
- if ($arg0->reg)
- printf " Registered name: "
- etp-1 $arg0->reg->name
- printf "\n"
+ printf " State:"
+ etp-port-state $arg0
+ printf " Scheduler flags:"
+ etp-port-sched-flags $arg0
+ if (*(((Uint32 *) &(((Port *) $arg0)->state))) & 0x5C00) == 0
+ if ($arg0->common.u.alive.reg)
+ printf " Registered name: "
+ etp-1 $arg0->common.u.alive.reg->name
+ printf "\n"
+ end
end
printf " Connected: "
- etp-1 $arg0->connected
+ set $connected = *(((Eterm *) &(((Port *) $arg0)->connected)))
+ etp-1 $connected
printf "\n Pointer: (Port *) %p\n", $arg0
end
@@ -1560,13 +1684,15 @@ define etp-ports
printf "No ports, since system isn't initialized!\n"
else
set $port_ix = 0
- while $port_ix < erts_max_ports
- set $port = &erts_port[$port_ix]
- if ($port->status & 0x400) == 0
- # I.e, not free
- printf "---\n"
- printf " Pix: %d\n", $port_ix
- etp-port-info $port
+ while $port_ix < erts_port.r.o.max
+ set $port = (Port *) *((UWord *) &erts_port.r.o.tab[$port_ix])
+ if ($port != ((Port *) 0) && $port != &erts_invalid_port)
+ if (*(((Uint32 *) &(((Port *) $port)->state))) & 0x100) == 0
+ # I.e, not free
+ printf "---\n"
+ printf " Pix: %d\n", $port_ix
+ etp-port-info $port
+ end
end
set $port_ix++
end
@@ -1866,6 +1992,12 @@ define etp-system-info
printf "ERTS version: %s\n", etp_erts_version
printf "Compile date: %s\n", etp_compile_date
printf "Arch: %s\n", etp_arch
+ printf "Endianess: "
+ if (etp_big_endian)
+ printf "Big\n"
+ else
+ printf "Little\n"
+ end
printf "Word size: %d-bit\n", etp_arch_bits
printf "Halfword: "
if (etp_halfword)
@@ -2113,8 +2245,8 @@ define etp-search-heaps-1
end
set $etp_search_heaps_i = 0
set $etp_search_heaps_found = 0
- while $etp_search_heaps_i < erts_proc.max
- set $proc = (Process *) *((UWord *) &erts_proc.tab[$proc_ix])
+ while $etp_search_heaps_i < erts_proc.r.o.max
+ set $proc = (Process *) *((UWord *) &erts_proc.r.o.tab[$proc_ix])
if $proc
if ($proc->heap <= ($arg0)) && \
(($arg0) < $proc->hend)
diff --git a/erts/etc/unix/run_erl.c b/erts/etc/unix/run_erl.c
index 6b350e8bd5..910be3dce8 100644
--- a/erts/etc/unix/run_erl.c
+++ b/erts/etc/unix/run_erl.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2011. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2012. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
diff --git a/erts/etc/vxworks/README.VxWorks b/erts/etc/vxworks/README.VxWorks
deleted file mode 100644
index 299e35b513..0000000000
--- a/erts/etc/vxworks/README.VxWorks
+++ /dev/null
@@ -1,350 +0,0 @@
-
- %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%
-
------------------------------------------------------------------------
-README, Erlang/OTP R11B for VxWorks on PPC860 and PPC603
------------------------------------------------------------------------
-20060515 -- Patrik Nyblom, [email protected]
-
-R11B is a libraries only release for VxWorks. Only the libraries of
-erl_interface (ei+erl_inteface) and ic are expected to be used. Still
-the whole erlang system is distributed, although no support will be
-given for anything else but the libraries. The information in this
-file still applies to the full erlang distribution and parts of it are
-therefore somewhat irrelevant to commercial users.
-
-
-Included OTP applications
--------------------------
-
-appmon
-asn1
-compiler
-cosEvent
-cosNotification
-cosTime
-cosTransaction
-debugger
-erl_interface
-erts
-eva [1]
-ic
-inets [2]
-jinterface
-kernel
-mesh
-mnemosyne
-mnesia [1]
-mnesia_session
-orber
-os_mon
-pman
-runtime_tools
-sasl
-snmp
-stdlib
-tools
-tv
-
-[1] Only ram_copies work, The VxWorks filesystems are not
- reliable enough for disk_copies to be fully supported.
-[2] CGI scripts do not work on VxWorks.
-
-Omitted applications
---------------------
-
-crypto
-emacs
-etk
-gs
-odbc
-parsetools
-toolbar
-ssl
-megaco
-webtools
-
-As `crypto' and `ssl' provides cryptographic functionality to `inets'
-and `snmp', the latter applications will not handle cryptography on
-VxWorks.
-
-Graphical interfaces
---------------------
-
-For applications using graphical interfaces, only the backend part works.
-
-Compilers
----------
-
-All compilers are expected to be run on a cross host. The VxWorks
-systems memory capabilities are too restricting to allow native
-compiling. The expected host system is a Sun Solaris machine, although
-Erlang compilation may be done on most platforms.
-
-Supported boards and configuration (only libraries supported)
-----------------------------------
-The following boards and configurations are supported:
-
-* Force PowerCore 603 with Force pcore603 BSP and VxWorks 3.5.1 (no
- SENS or SENS 1.1 + SPR23938) and a minimum of 32 Mb memory.
-
-* Force Powercore 750 with Force pcore750 BSP and VxWorks 3.5.1 (no
- SENS or SENS 1.1 + SPR23938) and a minimum of 32 Mb memory.
-
-* PSS Core PPC860 processors, only erl_interface (too small main memory).
-
-Most PowerPC boards with FPU are expected to work, but will need to be
-tested by OTP to be fully supported.
-
-The PPC603 build has been compiled with Wind River's `-mlongcall'
-flag (SPR25893) to support arbitrary function calls across more
-than 32 MB of memory.
-
-The PPC860 (PowerQuicc) has no FPU and requires a separate build.
-
-For Erlang to run, the Wind kernel has to be configured with a minimum
-of these variables defined in config.h (or by the Tornado
-configuration tool):
-
- INCLUDE_ANSI_ALL
- INCLUDE_ENV_VARS
- INCLUDE_EXC_HANDLING
- INCLUDE_EXC_TASK
- INCLUDE_FLOATING_POINT
- INCLUDE_FORMATTED_IO
- INCLUDE_IO_SYSTEM
- INCLUDE_LOADER
- INCLUDE_NETWORK
- INCLUDE_NET_INIT
- INCLUDE_NET_SHOW
- INCLUDE_NET_SYM_TBL or INCLUDE_STANDALONE_SYM_TBL
- INCLUDE_PIPES
- INCLUDE_POSIX_FTRUNC
- INCLUDE_RLOGIN or INCLUDE_TELNET (for pty's only)
- INCLUDE_SELECT
- INCLUDE_SEM_BINARY
- INCLUDE_SEM_COUNTING
- INCLUDE_SEM_MUTEX
- INCLUDE_SHELL (*)
- INCLUDE_SHOW_ROUTINES
- INCLUDE_SIGNALS
- INCLUDE_STARTUP_SCRIPT (*)
- INCLUDE_STDIO
- INCLUDE_SYM_TBL
- INCLUDE_TASK_HOOKS
- INCLUDE_TASK_VARS
- INCLUDE_TTY_DEV
- INCLUDE_UNLOADER
- INCLUDE_NFS or INCLUDE_RAMDRV or INCLUDE_DOSFS (i.e. a file system,
- possibly read-only) (**)
-
-(*) Needed for the example startup script, not actually needed in production if
- erlang is set up by a c routine called from usrConfig.c.
-(**) INCLUDE_NFS usually requires the NFS_USER_ID and NFS_GROUP_ID variables
- to be set in config.h
-
-As an erlang system may open a lot of files, it is recommended to raise the
-default NUM_FILES variable to something like 256 in config.h like this:
- #ifdef NUM_FILES
- #undef NUM_FILES
- #endif
- #define NUM_FILES 256
-
-The SENS stack *has* to be of version 1.1 or higher, 1.0 is *not*
-supported and will not work reliably. Upgrades as well as the patch
-for SPR23938 can be found at www.wrs.com (i.e. WindSurf). Also, the
-following constants in $WIND_BASE/target/h/netBufLib.h has to be
-raised to a value of at least four times the default:
-
- NUM_NET_MBLKS
- NUM_64
- NUM_128
- NUM_256
- NUM_512
- NUM_1024
- NUM_2048
-
- NUM_SYS_64
- NUM_SYS_128
- NUM_SYS_256
- NUM_SYS_512
-
-Use the show routines mbufShow and netStackSysPoolShow to verify that
-these pools are not exhausted.
-
-Installation
-------------
-
-To install Erlang on a VxWorks card, the following knowledge is
-expected:
-
-* VxWorks installation and configuration.
-
-* Network (TCP/IP) configuration.
-
-* Erlang basic operation and configuration.
-
-There is no specific install script for erlang on the VxWorks
-platform. There is however an example VxWorks startup file named
-erts-5.0.1/bin/erl_script.sam under the root of an unpacked
-release. There may of course be other things to do in the start
-script, like using the rdate program in the erlang distribution to get
-a correct date and set the TIMEZONE variable.
-
-Please consult the "Embedded System" documentation for further
-information on installation.
-
-Known Bugs and problems
------------------------
-
-We have found the VxWorks/NFS client file system to be unreliable.
-Important file operations like rename, ftruncate, cd and unlink
-doesn't always work as expected. Therefore more complex file using
-parts of OTP, like DETS and disk based mnesia tables cannot be used
-reliably with NFS. Lowering the NFS cache size (global variable
-nfsCacheSize) to 512 gives a more reliable NFS client, but to disk
-base the mnesia tables over NFS is still not a good idea, especially
-as disk based mnesia tables are not supported on VxWorks. Another
-problem with VxWorks file systems is that the error codes they produce
-are not consistent. We have worked around this problem by mapping the
-codes to POSIX ones, by doing this we make the VxWorks Erlang platform
-behave similarly to the UNIX and Windows implementations.
-
-The rename and ftruncate operations over NFS are emulated using
-copying of files. This is mainly for our own test suites and it is not
-recommended to use file:rename and/or file:ftruncate on NFS file
-systems in production.
-
-Floating point operations is somewhat faulty. For instance, testing
-floating point numbers for equality should be done with care. This is
-actually not a bug, IEEE specifies no equality among floating point
-numbers.
-
-Memory handling
----------------
-
-Please read the erl_set_memory_block(3) manual page in the ERTS
-documentation for information concerning memory handling in the erlang
-emulator. Also please observe that reclaim.o has to be loaded and
-reclaim_init() called before any other erlang routines are loaded and
-started. If one wish to use the resource reclamation routines in other
-programs, refer to the header file in `erts-5.0.1/include/reclaim.h'.
-Including that file in your C source makes malloc/realloc/free and
-open/fopen/socket/close etc be redefined to routines that makes the
-memory and files be free'd/closed when the task exits. Still,
-reclaim_init() *has* to be called before any program that uses this is
-started.
-
-Using heart
------------
-
-The default behavior of the heart object file that is part of the
-distribution is that it reboots the target when the Erlang process
-hasn't given it a heart beat in 60 seconds. The normal heart beat rate
-is one beat per five seconds. This makes an effective "software
-watchdog" but there is really no substitute for the real thing --- a
-hardware watchdog. If you want to add a hardware watchdog to the
-system please contact us for directions. If you want to disable the
-reboot you may set the environment variable HEART_DONT_REBOOT (see the
-example erlang start script, erl). Please note that if you DO want the
-card to reboot you mustn't define HEART_DONT_REBOOT at all. E.g. to
-disable heart reboot you may include the following line in the start
-script (as is indeed the case with the example start script).
-
- putenv "HEART_DONT_REBOOT=1"
-
-A few words on writing port program and dynamically loaded drivers for VxWorks
-------------------------------------------------------------------------------
-
-VxWorks has one name-space for all symbols. This makes it harder to
-write C programs whose global symbols doesn't interfere with each
-other. It is a good rule to avoid all globally visible symbols that
-are not absolutely necessary. Due to these facts we use the following
-naming rules that are crucial to follow. (there are more issues
-involved but the issues described here is a good beginning).
-
-Port programs must have a function with the same name as the object
-file. E.g. if you have an object file named `port_test.o' it must
-contain a globally visible function named `port_test'. This is the
-function that will be called when you output data from Erlang to the
-port. (The object file, in this example, should be named
-`port_test.o', but `port_test' will also do).
-
-Also, in an embedded system, it is recommended to load the port
-program into the system before the port program is used. This is to
-avoid the real time degradation dynamical linking in runtime would
-introduce. Use VxWorks command ld < "port_prg" to accomplish this.
-
-Dynamically linked drivers must have a function with the same name as
-the object file with the added suffix `_init'. We recommend the use of
-the macro DRIVER_INIT in `driver.h'. E.g. if you have an object file
-named `echo_drv.eld' it must contain a globally visible function
-`echo_drv_init'. (The object file, in this example, should be named
-`echo_drv.eld' (`eld' is short for Erlang Loadable Driver), but
-`echo_drv.o' and `echo_drv' will both also do). It is also very
-important to initialize all unused function pointer in the
-`driver_entry' struct to NULL (see example below).
-
-Example of dynamically linked driver
-------------------------------------
-
-#include <stdio.h>
-#include "driver.h"
-
-static int erlang_port;
-static long echo_start();
-static int echo_stop(), echo_read();
-
-static struct driver_entry echo_driver_entry = {
- null_func,
- echo_start,
- echo_stop,
- echo_read,
- null_func,
- null_func,
- "echo_drv",
- null_func
-};
-
-int DRIVER_INIT(echo_drv)(void *handle)
-{
- erlang_port = -1;
-
- echo_driver_entry.handle = handle;
- return (int) &echo_driver_entry;
-}
-
-static long echo_start(long port,char *buf)
-{
- if (erlang_port != -1) {
- return -1;
- }
-
- erlang_port = port;
- return port;
-}
-
-static int echo_read(long port, char *buf, int count)
-{
- return driver_output(erlang_port, buf, count);
-}
-
-static int echo_stop()
-{
- return erlang_port = -1;
-}
diff --git a/erts/etc/vxworks/erl.exec.c b/erts/etc/vxworks/erl.exec.c
deleted file mode 100644
index 6b45ebaa39..0000000000
--- a/erts/etc/vxworks/erl.exec.c
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * %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%
- */
-/*
- A simpified version of the 'erl.exec' "startup script".
- Called (e.g. from VxWorks shell) with all arguments in a
- single string, e.g.: erl "-name thisnode -s mymod myfunc".
- These arguments are handled as in 'erl.exec':
- -name
- -sname
- -noshell
- -noinput
- anything else is just passed on to the emulator. Note that there
- is no automatic start of epmd, that -oldshell is implicit, and
- that you need to set current directory appropriately if you want
- auto-load of port programs
-*/
-
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#ifndef DEFAULT_HOMEDIR /* used if environment HOME isn't set */
-#define DEFAULT_HOMEDIR "/"
-#endif
-
-#define ARGLEN 2048 /* Total length of args passed to erl_main */
-#define ARGMAX 64 /* Max no of "extra" args */
-
-static char *erl_cmd = "erl_main -n ";
-
-static toomuch()
-{
- fprintf(stderr, "erl: Too many arguments\n");
- return(-1);
-}
-
-static toolittle(arg)
-char *arg;
-{
- fprintf(stderr, "erl.exec: Missing argument for %s\n", arg);
- return(-1);
-}
-
-erl_exec(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10)
-int arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10;
-{
- char *shell = "-oldshell ", *noshell = "",
- *home, *rootdir, *bindir, *progname;
- char cmd[ARGLEN], eargs[ARGLEN], iargs[ARGLEN];
- char *args[ARGMAX], *arglast = NULL, *argp;
- int nargs = 0, len, i;
-
- if ((rootdir = getenv("ROOTDIR")) == NULL ||
- (bindir = getenv("BINDIR")) == NULL ||
- (progname = getenv("PROGNAME")) == NULL) {
- fprintf(stderr, "erl.exec: ROOTDIR, BINDIR, and PROGNAME must be set.");
- return -1;
- }
- eargs[0] = '\0';
- iargs[0] = '\0';
- if ((home = getenv("HOME")) == NULL)
- home = DEFAULT_HOMEDIR;
- argp = strtok_r((char *)arg1, " \t", &arglast);
- while (argp != NULL) {
- if (strcmp(argp, "-name") == 0) {
- if ((argp = strtok_r((char *)NULL, " \t", &arglast)) == NULL)
- return(toolittle("-name"));
- strcat(iargs, "-name ");
- strcat(iargs, argp);
- strcat(iargs, " ");
- } else if (strcmp(argp, "-sname") == 0) {
- if ((argp = strtok_r((char *)NULL, " \t", &arglast)) == NULL)
- return(toolittle("-sname"));
- strcat(iargs, "-sname ");
- strcat(iargs, argp);
- strcat(iargs, " ");
- } else if (strcmp(argp, "-noshell") == 0) {
- strcat(iargs, "-noshell -noinp_shell ");
- } else if (strcmp(argp, "-noinput") == 0) {
- strcat(iargs, "-noshell -noinput ");
- } else {
- if (nargs > ARGMAX - 1)
- return(toomuch());
- args[nargs++] = argp;
- }
- argp = strtok_r((char *)NULL, " \t", &arglast);
- }
- strcpy(cmd, erl_cmd);
- strcat(cmd, eargs);
- strcat(cmd, " -- -root ");
- strcat(cmd, rootdir);
- strcat(cmd, " -progname ");
- strcat(cmd, progname);
- strcat(cmd, " -- ");
- strcat(cmd, "-home ");
- strcat(cmd, home);
- strcat(cmd, " ");
- strcat(cmd, iargs);
-
- len = strlen(cmd);
- for (i = 0; i < nargs; i++) {
- if (len + strlen(args[i]) + 2 >= ARGLEN)
- return(toomuch());
- cmd[len++] = ' ';
- strcpy(&cmd[len], args[i]);
- len += strlen(args[i]);
- }
- argcall(cmd);
-}
-
diff --git a/erts/etc/vxworks/erl_io.c b/erts/etc/vxworks/erl_io.c
deleted file mode 100644
index 0032b77079..0000000000
--- a/erts/etc/vxworks/erl_io.c
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * %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%
- */
-/* Some stuff to let the Erlang and VxWorks shells coexist peacefully.
- Basically, run Erlang as a spawned task with input redirected to
- the slave side of a pseudo-tty, and connect explicitly to the master
- side of the pseudo-tty to send input to Erlang when desired. */
-
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-#include <stdio.h>
-#include <ioLib.h>
-#include <taskLib.h>
-#include <ptyDrv.h>
-
-extern int spTaskPriority, spTaskOptions;
-
-#define TBUFSIZ 512
-
-#define DEFAULT_STACK_SIZE 100000
-
-static int slavefd = -1, masterfd = -1;
-static run_erl();
-
-/* Frontend to the Erlang startup function - callable from VxWorks shell
- or script. 'arg' is actually a string passed to the real startup. */
-start_erl(arg)
-int arg;
-{
- int stacksize;
- char *stackenv;
-
- /* create and open the pty - we want the master side to be open
- all the time, since closing it probably generates EOF on the
- slave side */
- (void)ptyDevCreate("/pty/erlang.", TBUFSIZ, TBUFSIZ);
- if (slavefd != -1)
- (void)close(slavefd);
- slavefd = open("/pty/erlang.S", O_RDONLY, 0);
- if (masterfd != -1)
- (void)close(masterfd);
- masterfd = open("/pty/erlang.M", O_WRONLY, 0);
-
- /* flush any old leftover garbage */
- (void) ioctl(masterfd, FIOFLUSH, 0);
- if ((stackenv = getenv("ERLSTACKSIZE")) == NULL)
- stacksize = DEFAULT_STACK_SIZE;
- else
- stacksize = atoi(stackenv);
- /* spawn Erlang, via stub below */
- return(taskSpawn("erlang", spTaskPriority, spTaskOptions, stacksize,
- run_erl, arg, 0,0,0,0,0,0,0,0,0));
-}
-
-/* Little stub that runs in the spawned task - we need this to redirect
- stdin reliably (redirections aren't "inherited" in VxWorks) */
-static
-run_erl(arg)
-int arg;
-{
- ioTaskStdSet(0, 0, slavefd); /* redirect stdin to slave side of pty */
-
- /* We don't want to redirect stdout/err since no one will be reading
- from the master side (to_erl - and the open()s above - could be
- made bidirectional, but still the master side would only be read
- when to_erl was running), and output can eventually fill the pty
- buffer and cause the Erlang system to block. Not redirecting
- stdout/err will have the effect that output from Erlang, e.g. the
- shell prompt, will appear on console/rlogin/whatever even when
- to_erl isn't running, which may be confusing - can't win 'em all... */
-
- erl_exec(arg, 0,0,0,0,0,0,0,0); /* call the real startup */
-}
-
-/* Function callable from VxWorks shell to talk to Erlang - stop talking
- and return to VxWorks shell through ^D (EOF) */
-to_erl()
-{
- char buf[TBUFSIZ];
- int cc;
-
- if (masterfd == -1) { /* sanity check */
- fprintf(stderr, "Must start_erl first!\n");
- return(-1);
- }
- while ((cc = read(0, buf, TBUFSIZ)) > 0) /* just pass everything through */
- if (write(masterfd, buf, cc) != cc) {
- fprintf(stderr, "Write to Erlang failed!\n");
- return(-1);
- }
- return(cc);
-}
diff --git a/erts/etc/vxworks/erl_script.sam.in b/erts/etc/vxworks/erl_script.sam.in
deleted file mode 100644
index 81c2b0128d..0000000000
--- a/erts/etc/vxworks/erl_script.sam.in
+++ /dev/null
@@ -1,100 +0,0 @@
-#
-# %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%
-#
-#
-# erl_script.sam
-# Sample VxWorks script to start Erlang
-#
-# Note! This is not a complete or ready to use VxWorks startup script,
-# rather an example of what You should add to Your existing startupscript
-# to execute the erlang emulator at boot.
-#
-# When writing Your own script to start erlang, the paths to
-# the binaries have to be changed to reflect your system.
-#
-# The ROOTDIR variable should not point to a ftp or rcp filesystem unless
-# the erlang machine is run in embedded mode. Loading of modules
-# is far to slow if the erlang binaries are not placed on a real filesystem
-# like NFS or any type of local filesystem.
-#
-
-#
-# Load modules
-#
-
-#
-# First load and initiate the reclaim facility
-#
-ld </home/tornado/erlvxworks/erts-%VSN%/bin/reclaim.o
-reclaim_init()
-
-#
-# Now load the runtime system
-#
-ld </home/tornado/erlvxworks/erts-%VSN%/bin/jam
-ld </home/tornado/erlvxworks/erts-%VSN%/bin/erl.exec
-ld </home/tornado/erlvxworks/erts-%VSN%/bin/erl_io
-ld </home/tornado/erlvxworks/erts-%VSN%/bin/vxcall
-ld </home/tornado/erlvxworks/erts-%VSN%/bin/heart
-ld </home/tornado/erlvxworks/erts-%VSN%/bin/epmd
-
-#
-# Stack sizes
-#
-putenv "ERLSTACKSIZE=100000"
-putenv "ERLPORTSTACKSIZE=100000"
-
-#
-# Activate Round robin scheduling
-#
-kernelTimeSlice 1
-
-#
-# Distribution
-# The VxWorks machines host name
-sethostname "sb001", 5
-# Erlangs internal resolver
-putenv "ERLRESCONF=/home/tornado/erlvxworks/erts-%VSN%/bin/resolv.conf"
-
-#
-# Start epmd (for distribution)
-#
-start_epmd "-daemon"
-
-#
-# Misc environment variables
-#
-putenv "ROOTDIR=/home/tornado/erlvxworks"
-putenv "BINDIR=/home/tornado/erlvxworks/erts-%VSN%/bin"
-putenv "PROGNAME=erl"
-putenv "HOME=/home/tornado/erlvxworks"
-
-#
-# Set heart no reboot mode (to make heart reboot -
-# don't define HEART_DONT_REBOOT at all)
-#
-putenv "HEART_DONT_REBOOT=1"
-
-# To get fullsweep garbage collection on systems with
-# very limited memory, set ERL_FULLSWEEP_AFTER to "0":
-# putenv "ERL_FULLSWEEP_AFTER=0"
-
-#
-# Start Erlang/OTP
-#
-start_erl "-oldshell -heart -sname vxnode -setcookie switch -boot start_sasl"
diff --git a/erts/etc/vxworks/heart_config.c b/erts/etc/vxworks/heart_config.c
deleted file mode 100644
index 7e60e61fbb..0000000000
--- a/erts/etc/vxworks/heart_config.c
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * %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%
- */
-/*
- * A basic heart configure module for VxWorks.
- *
- */
-
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-#include <vxWorks.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <rebootLib.h>
-#include <sysLib.h>
-
-/* wd_init is executed to initialize a watchdog (if one is used). */
-int wd_init(timeout, prio)
- int timeout, prio;
-{
-
-}
-
-/* wd_reset should be called every 5th second from heart */
-void wd_reset()
-{
-
-}
-
-/* This routine is called when erlang has closed */
-void heart_reboot()
-{
- if (getenv("HEART_DONT_REBOOT") != NULL) {
- fprintf(stderr, "heart_config: HEART_DONT_REBOOT set, no reboot ...\n");
- } else {
- fprintf(stderr, "heart_config: rebooting ...\n");
- taskDelay(sysClkRateGet() * 5);
- reboot(BOOT_CLEAR);
- }
-}
-
-
-
-
diff --git a/erts/etc/vxworks/heart_config.h b/erts/etc/vxworks/heart_config.h
deleted file mode 100644
index 5ffaaa8c3f..0000000000
--- a/erts/etc/vxworks/heart_config.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * %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%
- */
-/*
- * This is heart's watchdog interface for VxWorks.
- */
-
-#ifndef _HW_WATCHDOG_H
-#define _HW_WATCHDOG_H
-
-extern void wd_init(int timeout, int prio); /* wd_init initializes the
- watchdog, if one is used. */
-extern void wd_reset(void); /* wd_reset is used by heart to kick
- the watchdog, if one is used. */
-extern void heart_reboot(void); /* reboot is called if heart discovers
- that the Erlang task has stopped sending
- heart beats. It can log system status
- and should reboot VxWorks. */
-
-#endif
diff --git a/erts/etc/vxworks/rdate.c b/erts/etc/vxworks/rdate.c
deleted file mode 100644
index 3e8cc644d0..0000000000
--- a/erts/etc/vxworks/rdate.c
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * %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%
- */
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-
-#include <vxWorks.h>
-#include <timers.h>
-#ifdef NETDB
-#include <netdb.h>
-#endif
-#include <sys/socket.h>
-#include <netinet/in.h>
-
-/*
- rdate("host") - Set the time from "host".
-*/
-
-/* No getservbyname() available... */
-#define TIME_PORT 37
-
-rdate(host)
-char *host;
-{
- u_long haddr;
-#ifdef NETDB
- struct hostent *hp;
-#endif
- struct sockaddr_in saddr;
- int sock;
- u_long net_time;
- struct timespec t;
-
- if ((haddr = inet_addr(host)) == ERROR) {
-#ifdef NETDB
- if ((hp = gethostbyname(host)) == NULL) {
-#else
- if ((haddr = hostGetByName(host)) == ERROR) {
-#endif
- printf("Host not found.\n");
- return(-1);
- }
-#ifdef NETDB
- memcpy(&haddr, hp->h_addr, sizeof(haddr));
-#endif
- }
- memset(&saddr, 0, sizeof(saddr));
- saddr.sin_family = AF_INET;
- memcpy(&saddr.sin_addr, &haddr, sizeof(haddr));
- saddr.sin_port = htons(TIME_PORT);
- if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
- perror("socket");
- return(-1);
- }
- if (connect(sock, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) {
- perror("connect");
- close(sock);
- return(-1);
- }
- if (read(sock, &net_time, 4) != 4) {
- perror("read");
- close(sock);
- return(-1);
- }
- t.tv_sec = ntohl(net_time);
- t.tv_sec -= 2208988800; /* seconds 1900-01-01 -- 1970-01-01 */
- t.tv_nsec = 0;
- clock_settime(CLOCK_REALTIME, &t);
- close(sock);
- return(0);
-}
diff --git a/erts/etc/vxworks/reclaim.c b/erts/etc/vxworks/reclaim.c
deleted file mode 100644
index d8676b3750..0000000000
--- a/erts/etc/vxworks/reclaim.c
+++ /dev/null
@@ -1,551 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 1998-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%
- */
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-
-#include <vxWorks.h>
-#include <version.h>
-#include <string.h>
-#include <types.h>
-#include <sigLib.h>
-#include <ioLib.h>
-#include <iosLib.h>
-#include <fioLib.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <errno.h>
-#include <symLib.h>
-#include <sysLib.h>
-#include <sysSymTbl.h>
-#include <loadLib.h>
-#include <taskLib.h>
-#include <taskVarLib.h>
-#include <taskHookLib.h>
-#include <tickLib.h>
-#include <time.h>
-#include <rngLib.h>
-#include <semLib.h>
-#include <selectLib.h>
-#include <sockLib.h>
-#include <a_out.h>
-#include <wdLib.h>
-#include <timers.h>
-#include <ctype.h>
-#include <sys/stat.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <netinet/tcp.h>
-#include <stdarg.h>
-
-#include <stdio.h>
-#include <math.h>
-#include <limits.h>
-#include <stdlib.h>
-#include <string.h>
-
-
-
-#define RECLAIM_NO_ALIAS /* No #defines for open/malloc/fopen etc */
-#include "reclaim.h"
-#include "reclaim_private.h"
-
-#undef open
-#undef creat
-#undef socket
-#undef accept
-#undef close
-#undef fopen
-#undef fdopen
-#undef freopen
-#undef fclose
-/* XXX Should do opendir/closedir too... */
-#undef malloc
-#undef calloc
-#undef realloc
-#undef free
-#undef cfree
-
-#ifdef _ARCH_PPC
-#define MAX_FILES_SYM_NAME "maxFiles"
-#else
-#define MAX_FILES_SYM_NAME "_maxFiles"
-#endif
-
-
-/*
- * Use another free() function upon task deletion?
- * Note! When changing free function, the new free function will have to
- * be able to cope with ALL previously used malloced areas, that is
- * it has to be able to find out how things are malloced
- * to free them in the right way!
- */
-static FreeFunction reclaim_free_function = NULL;
-
-/* delete hook handling (see below) */
-static int hook_added = 0; /* Initated at first reclaim_init, an extra
- non MT-safe check that we only get
- initialized once */
-
-/* Forward... */
-static void save_reclaim(WIND_TCB *tcbp);
-
-struct mall_data {
- struct mall_data *next;
- char *self;
-};
-
-struct task_data {
- FUNCPTR version; /* To recognize when we have reloaded */
- int max_files; /* It may change... */
- struct fd_set open_fds;
- struct mall_data *mall_data;
- FUNCPTR delete_hook;
- caddr_t hook_data;
- FILE *open_fps[1]; /* Will be max_files long */
-} *task_data = NULL;
-
-static int max_files = 50; /* default configAll.h */
-
-int reclaim_max_files(void)
-{
- return max_files;
-}
-
-#ifdef DEBUG
-#define check_hook() \
-((task_data != NULL || \
- fdprintf(2,"check_hook() TID = 0x%08x, Called from line %d\n", \
- (unsigned int)taskIdSelf(),\
- __LINE__)) && \
-(task_data != NULL || \
- (taskVarAdd(0, (int *)&task_data) == OK && \
- (task_data = (struct task_data *)\
- calloc(1, sizeof(struct task_data) + max_files*sizeof(FILE *))) != NULL && \
- (task_data->version = (FUNCPTR)save_reclaim) != NULL && \
- (task_data->max_files = max_files) != 0 && \
- fdprintf(2,"taskVar Added for 0x%08x\n",(unsigned int)taskIdSelf()))))
-#else
-#define check_hook() \
-(task_data != NULL || \
- (taskVarAdd(0, (int *)&task_data) == OK && \
- (task_data = (struct task_data *)\
- calloc(1, sizeof(struct task_data) + max_files*sizeof(FILE *))) != NULL && \
- (task_data->version = (FUNCPTR)save_reclaim) != NULL && \
- (task_data->max_files = max_files) != 0))
-#endif
-
-/*
- * Global initialization of the reclaim data structures, mainly
- * the max_files variable. This HAS to be called by some task before
- * the first task that utilizes this exit's, preferrably before any
- * task makes the first use of this library.
- */
-STATUS reclaim_init(void)
-{
- int *mp;
- SYM_TYPE type;
- struct task_data *tdp;
- int i;
-
- if (!hook_added) {
- /* race condition */
- ++hook_added;
- /* Try to find the maxFiles value */
- if (symFindByNameAndType(sysSymTbl, MAX_FILES_SYM_NAME, (char **)&mp,
- &type,
- N_EXT | N_BSS, N_EXT | N_BSS) == OK ||
- symFindByNameAndType(sysSymTbl, MAX_FILES_SYM_NAME, (char **)&mp,
- &type,
- N_EXT | N_DATA, N_EXT | N_DATA) == OK) {
-
-#ifdef DEBUG
- fdprintf(2, "Found maxFiles=%d\n", *mp);
-#endif
- if (*mp <= FD_SETSIZE)
- max_files = *mp;
- else
- max_files = FD_SETSIZE;
- }
- if (task_data != NULL && task_data->max_files != max_files) {
- /* fix our own iff we have one */
- if ((tdp = (struct task_data *)
- realloc(task_data, sizeof(struct task_data) +
- max_files*sizeof(FILE *))) != NULL) {
- task_data = tdp;
- for (i = task_data->max_files; i < max_files; i++)
- task_data->open_fps[i] = NULL;
- task_data->max_files = max_files;
- }
- }
- /* Make sure taskVariables are deleted AFTER our hook is run. */
- taskVarInit();
- if(taskDeleteHookAdd((FUNCPTR)save_reclaim) != OK) {
- fprintf(stderr,
- "Panic: taskDeleteHook cannot be added for reclaim.\n");
- return ERROR;
- }
- return OK;
- } else
- return ERROR;
-}
-
-/* N.B.!! We do *not* execute in the context of the dying task here,
- but rather that of tExcTask - we do get a pointer to the task's
- TCB though - this pointer is in fact also the task's ID. */
-static void save_reclaim(WIND_TCB *tcbp)
-{
- int i, var, oldfd;
- struct task_data *tdp;
- struct mall_data *mdp, *mdnextp;
-
- if ((var = taskVarGet((int)tcbp, (int *)&task_data)) != ERROR &&
- var != 0) {
- tdp = (struct task_data *)var;
- if (tdp->version == (FUNCPTR)save_reclaim) { /* Only handle our own */
-#ifdef DEBUG
- fdprintf(2, "Reclaiming for task id 0x%x:\nFiles: ", (int)tcbp);
-#endif
- /* Ugh! VxWorks doesn't even flush stdout/err - we need to
- get at those (which are task-private of course, i.e. we
- can't just do fflush(stdout) here) - we could be really
- pedantic and try to redefine stdout/err (which "are"
- function calls) too, snarfing the values when they are
- used - but besides the overhead this is problematic since
- they are actually #defines already... We'll peek in the
- TCB instead (no documentation of course). And of course,
- we must also meddle with the *file descriptor* indirections,
- or we'll just flush out on tExcTask's descriptors... */
- for (i = 1; i <= 2; i++) {
- if (tcbp->taskStdFp[i] != NULL) {
-#ifdef DEBUG
- fdprintf(2, "fflush(%s) ", i == 1 ? "stdout" : "stderr");
-#endif
- oldfd = ioTaskStdGet(0, i);
- ioTaskStdSet(0, i, tcbp->taskStd[i]);
- fflush(tcbp->taskStdFp[i]);
- ioTaskStdSet(0, i, oldfd);
- }
- }
- for (i = 3; i < tdp->max_files; i++) {
- if (FD_ISSET(i, &tdp->open_fds)) {
-#ifdef DEBUG
- fdprintf(2, "close(%d) ", i);
-#endif
- (void) close(i);
- }
- if (tdp->open_fps[i] != NULL) {
-#ifdef DEBUG
- fdprintf(2, "fclose(%0x%x) ", (int)tdp->open_fps[i]);
-#endif
- (void) fclose(tdp->open_fps[i]);
- }
- }
- i = 0;
- mdp = tdp->mall_data;
- while (mdp != NULL) {
- mdnextp = mdp->next;
- if(reclaim_free_function != NULL)
- (*reclaim_free_function)(mdp->self);
- else
- free(mdp->self);
- i++;
- mdp = mdnextp;
- }
-#ifdef DEBUG
- fdprintf(2, "\nFreeing memory: total %d mallocs\n", i);
-#endif
-
- if (tdp->delete_hook != NULL) {
-#ifdef DEBUG
- fdprintf(2, "Calling delete hook at 0x%08x\n", tdp->delete_hook);
-#endif
- (*tdp->delete_hook)(tdp->hook_data);
-#ifdef DEBUG
- fdprintf(2, "Called delete hook at 0x%08x\n", tdp->delete_hook);
-#endif
- }
-#ifdef DEBUG
- fdprintf(2, "Freeing own mem at 0x%08x\n", tdp);
-#endif
- (void) free((char *)tdp);
-#ifdef DEBUG
- fdprintf(2, "Freed own mem at 0x%08x, done (0x%08x)\n**********\n", tdp,
- taskIdSelf());
- checkStack(0);
-#endif
- }
- }
-#ifdef DEBUG
- else
- fdprintf(2, "No task data found for id 0x%x, var = %d\n", (int)tcbp, var);
-#endif
-}
-
-/*
- * This sets another free function to be used by the task deletion hook.
- * The free function HAS to be able to free ANY type of dynamically allocated
- * memory that can be in the task data list of allocated memory, that is
- * also memory that's allocated before the free function was set.
- * A "user-supplied" free function is GLOBAL to the system!!!
- * A race condition is present, a task delete hook may be running when this
- * function is called, that may not be especially funny...
- */
-void set_reclaim_free_function(FreeFunction f){
- reclaim_free_function = f;
-}
-
-void save_delete_hook(FUNCPTR func, caddr_t parm)
-{
- if (check_hook()) {
- task_data->delete_hook = func;
- task_data->hook_data = parm;
- }
-}
-
-/*
- * plain_malloc is only used by spawn_start; plain_free by call_proc;
- * save_fd is used by both.
- */
-void *plain_malloc(size_t size){
- return(malloc(size));
-}
-
-void *plain_realloc(void *ptr, size_t size){
- return(realloc(ptr, size));
-}
-
-void plain_free(void *ptr){
- free(ptr);
-}
-
-void save_fd(int fd){
- if (fd >= 0 && check_hook() && fd < task_data->max_files)
- FD_SET(fd, &task_data->open_fds);
-}
-
-int save_open(char *path, int flags, /*mode_t mode*/ ...){
- int fd;
- mode_t mode = 0;
- if(flags & O_CREAT){
- va_list pvar;
- va_start(pvar,flags);
-#ifdef __GNUC__
-#warning save_open() gives three known alignment warnings.
-#endif
- mode = va_arg(pvar, mode_t);
- va_end(pvar);
- }
- if ((fd = open(path, flags, mode)) >= 0 && check_hook())
- FD_SET(fd, &task_data->open_fds);
- return(fd);
-}
-
-int save_creat(char *path, int mode){
- int fd;
- if ((fd = creat(path, mode)) >= 0 && check_hook())
- FD_SET(fd, &task_data->open_fds);
- return(fd);
-}
-
-int save_socket(int domain, int type, int protocol){
- int fd;
- if ((fd = socket(domain, type, protocol)) >= 0 && check_hook())
- FD_SET(fd, &task_data->open_fds);
- return(fd);
-}
-
-int save_accept(int s, struct sockaddr *addr, int *addrlen){
- int fd;
- if ((fd = accept(s, addr, addrlen)) >= 0 && check_hook())
- FD_SET(fd, &task_data->open_fds);
- return(fd);
-}
-
-int save_close(int fd){
- if (fd >= 0 && fd <= FD_SETSIZE && check_hook())
- FD_CLR(fd, &task_data->open_fds);
- return(close(fd));
-}
-
-/* The dealing with FILE *'s below isn't strictly correct, we assume
- that one never has several pointing to the same fd - in the unlikely
- event that one does, all but the last one opened is forgotten */
-FILE *save_fopen(const char *filename, char *type){
- FILE *fp;
-
- if ((fp = fopen(filename, type)) != NULL &&
- check_hook() && fileno(fp) < task_data->max_files)
- task_data->open_fps[fileno(fp)] = fp;
- return(fp);
-}
-
-FILE *save_fdopen(int fd, char *type){
- FILE *fp;
-
- if ((fp = fdopen(fd, type)) != NULL &&
- check_hook() && fileno(fp) < task_data->max_files) {
- task_data->open_fps[fileno(fp)] = fp;
- FD_CLR(fd, &task_data->open_fds);
- }
- return(fp);
-}
-
-FILE *save_freopen(char *filename, char *type, FILE *stream){
- FILE *fp;
-
- if (check_hook()) {
- if(fileno(stream) < task_data->max_files &&
- task_data->open_fps[fileno(stream)] == stream)
- task_data->open_fps[fileno(stream)] = NULL;
- if ((fp = freopen(filename, type, stream)) != NULL &&
- fileno(fp) < task_data->max_files)
- task_data->open_fps[fileno(fp)] = fp;
- } else
- fp = freopen(filename, type, stream);
- return(fp);
-}
-
-int save_fclose(FILE *stream){
- if (check_hook() && fileno(stream) < task_data->max_files &&
- task_data->open_fps[fileno(stream)] == stream)
- task_data->open_fps[fileno(stream)] = NULL;
- return(fclose(stream));
-}
-
-/* We link all malloc'ed segments by adding a couple of pointers
- at the *end* - that way we can return the address malloc gave
- (need to make sure we align those pointers) */
-
-/*
- #define MALL_MARGIN 32
- #define save_mall_size(size) save_mall_size1((size) + 2 * MALL_MARGIN)
- #define save_mall_size1(size) \
- (((size) + sizeof(char *) - 1) & (unsigned long)(-sizeof(char*)))
-
- #define save_mall_enq(ptr, mdp) save_mall_enq1((ptr), (mdp) - MALL_MARGIN)
- #define save_mall_enq1(ptr, mdp) \
- (((struct mall_data *)(mdp))->self = (ptr), \
- ((struct mall_data *)(mdp))->next = task_data->mall_data, \
- task_data->mall_data = (struct mall_data *)(mdp))
-*/
-#define save_mall_size(size) \
-(((size) + sizeof(char *) - 1) & (unsigned long)(-sizeof(char*)))
-#define save_mall_enq(ptr, mdp) \
-(((struct mall_data *)(mdp))->self = (ptr), \
- ((struct mall_data *)(mdp))->next = task_data->mall_data, \
- task_data->mall_data = (struct mall_data *)(mdp))
-
-
-#define save_mall_deq(ptr) { \
- struct mall_data *mdp = task_data->mall_data, \
- **prevnext = &task_data->mall_data; \
- while (mdp != NULL && mdp->self != (ptr)) { \
- prevnext = &mdp->next; \
- mdp = mdp->next; \
- } \
- if (mdp != NULL) *prevnext = mdp->next; \
-}
-
-void *save_malloc2(size_t size, MallocFunction mf){
- unsigned msize = save_mall_size(size);
- char *ptr;
-
- if ((ptr = (*mf)(msize + sizeof(struct mall_data))) != NULL &&
- check_hook())
- save_mall_enq((void *) ptr, (void *) (ptr + msize));
- return((void *) ptr);
-}
-
-void *save_malloc(size_t size){
- return save_malloc2(size, &malloc);
-}
-
-void *save_calloc2(size_t nelem, size_t elsize, CallocFunction cf){
- unsigned msize = save_mall_size(nelem * elsize);
- char *ptr;
-
- if ((ptr = (*cf)(1, msize + sizeof(struct mall_data))) != NULL &&
- check_hook())
- save_mall_enq((void *) ptr, (void *) (ptr + msize));
- return((void *) ptr);
-}
-
-void *save_calloc(size_t nelem, size_t elsize){
- return save_calloc2(nelem,elsize,&calloc);
-}
-
-void *save_realloc2(void *optr, size_t size, ReallocFunction rf){
- unsigned msize = save_mall_size(size);
- char *ptr;
-
- /* First we must dequeue the old save block, after that
- we try to realloc, if that succeeds we enqueue the new
- block, if it fails we have to enqueue the old one anew
- so we must deduce the size of that old block first. */
-
- struct mall_data *mdp0 = task_data->mall_data,
- **prevnext0 = &task_data->mall_data;
- while (mdp0 != NULL && mdp0->self != (((char *) optr))) {
- prevnext0 = &mdp0->next;
- mdp0 = mdp0->next;
- }
- /* mdp0 == NULL (can) mean that the block that is realloced
- have been malloced with an (for example) ordinary malloc
- (that is not a save_malloc). This is handled like: no dequeing
- is done of that block, the new block is enqueued */
- if (mdp0 != NULL)
- save_mall_deq(((char *) optr));
-
- if ((ptr = (*rf)(optr, msize + sizeof(struct mall_data))) != NULL &&
- check_hook())
- save_mall_enq((void *) ptr, (void *) (ptr + msize));
- else if (mdp0 != NULL)
- /* re-enqueue the old block that has just been dequeued */
- save_mall_enq(((char *) optr), mdp0);
-
- return((void *) ptr);
-}
-
-void *save_realloc(void *optr, size_t size){
- return save_realloc2(optr,size,&realloc);
-}
-
-void save_free2(void *ptr, FreeFunction ff)
-{
- if (check_hook())
- save_mall_deq(((char *) ptr));
- (*ff)(ptr);
-}
-
-void save_free(void *ptr){
- save_free2(ptr,&free);
-}
-
-void save_cfree2(void *ptr, CfreeFunction cf)
-{
- if (check_hook())
- save_mall_deq(((char *)ptr));
- (*cf)(ptr);
-}
-
-void save_cfree(void *ptr){
- save_cfree2(ptr,&cfree);
-}
-
diff --git a/erts/etc/vxworks/reclaim.h b/erts/etc/vxworks/reclaim.h
deleted file mode 100644
index ca9aa8f6be..0000000000
--- a/erts/etc/vxworks/reclaim.h
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 1998-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 _RECLAIM_H
-#define _RECLAIM_H
-
-
-/* The Erlang release for VxWorks includes a simple mechanism for
- "resource reclamation" at task exit - it allows replacement of the
- functions that open/close "files" and malloc/free memory with versions
- that keep track, to be able to "reclaim" file descriptors and memory
- when a task exits (regardless of *how* it exits).
-
- The interface to this mechanism is made available via this file,
- with the following caveats:
-
- - The interface may change (or perhaps even be removed, though that
- isn't likely until VxWorks itself provides similar functionality)
- in future releases - i.e. you must always use the version of this
- file that comes with the Erlang release you are using.
-
- - Disaster is guaranteed if you use the mechanism incorrectly (see
- below for the correct way), e.g. allocate memory with the "tracking"
- version of malloc() and free it with the "standard" version of free().
-
- - The mechanism (of course) incurs some performance penalty - thus
- for a simple program you may be better off with careful programming,
- making sure that you do whatever close()/free()/etc calls that are
- appropriate at all exit points (though if you need to guard against
- taskDelete() etc, things get messy...).
-
- To use the mechanism, simply program your application normally, i.e.
- use open()/close()/malloc()/free() etc as usual, but #include this
- file before any usage of the relevant functions. NOTE: To avoid the
- "disaster" mentioned above, you *must* #include it in *all* (or none)
- of the files that manipulate a particular file descriptor, allocated
- memory area, etc.
-
- Before any task that uses this utility is loaded (which includes the
- erlang emulator), the reclaim.o object file has to be loaded and
- the function reclaim_init() has to be called. reclaim_init should be called
- only _ONCE_ in a systems lifetime and has only a primitive guard
- against multiple calls (i.e. a global variable is checked). Therefore
- the initialization should occur either in the start script of the system
- or (even better) in the usrInit() part of system initialization. The
- object file itself should be loaded only once, so linking it with the
- kernel is a good idea, linking with each application is an extremely bad
- dito. Make really sure that it's loaded _before_ any application that
- uses it if You want to load it in the startup script.
-
- If You dont want to have #define's for the posix/stdio names
- of the file/memory operations (i.e. no #define malloc save_malloc etc),
- #define RECLAIM_NO_ALIAS in Your source before reclaim.h is included.
-*/
-
-#include <vxWorks.h> /* STATUS, size_t */
-#include <sockLib.h> /* struct sockaddr */
-#include <memLib.h>
-#include <stdio.h> /* FILE */
-
-#if defined(__STDC__)
-#define _RECLAIM_DECL_FUN(RetType, FunName, ParamList) \
-extern RetType FunName ParamList
-#define _RECLAIM_VOID_PTR void *
-#define _RECLAIM_VOID_PARAM void
-#define _RECLAIM_VOID_RETURN void
-#elif defined(__cplusplus)
-#define _RECLAIM_DECL_FUN(RetType, FunName, ParamList) \
-extern "C" RetType FunName ParamList
-#define _RECLAIM_VOID_PTR void *
-#define _RECLAIM_VOID_PARAM
-#define _RECLAIM_VOID_RETURN void
-#else
-#define _RECLAIM_DECL_FUN(RetType, FunName, Ignore) extern RetType FunName()
-#define DECLARE_FUNCTION_TYPE(RetType, Type, PList) typedef RetType (* Type)()
-#define _RECLAIM_VOID_PTR char *
-#define _RECLAIM_VOID_PARAM
-#define _RECLAIM_VOID_RETURN
-#endif /* __STDC__ / __cplusplus */
-
-/* Initialize the facility, on a per system basis. */
-_RECLAIM_DECL_FUN(STATUS, reclaim_init, (_RECLAIM_VOID_PARAM));
-
-/* File descriptor operations */
-_RECLAIM_DECL_FUN(int,save_open,(char *, int, ...));
-_RECLAIM_DECL_FUN(int,save_creat,(char *, int));
-_RECLAIM_DECL_FUN(int,save_socket,(int, int, int));
-_RECLAIM_DECL_FUN(int,save_accept,(int, struct sockaddr *, int *));
-_RECLAIM_DECL_FUN(int,save_close,(int));
-/* Interface to add an fd to what's reclaimed even though it's not open with
- one of the above functions */
-_RECLAIM_DECL_FUN(_RECLAIM_VOID_RETURN, save_fd, (int fd));
-#ifndef RECLAIM_NO_ALIAS
-#define open save_open
-#define creat save_creat
-#define socket save_socket
-#define accept save_accept
-#define close save_close
-#endif
-/* Stdio file operations */
-_RECLAIM_DECL_FUN(FILE *, save_fopen, (const char *, char *));
-_RECLAIM_DECL_FUN(FILE *, save_fdopen, (int, char *));
-_RECLAIM_DECL_FUN(FILE *, save_freopen, (char *, char *, FILE *));
-_RECLAIM_DECL_FUN(int, save_fclose, (FILE *));
-/* XXX Should do opendir/closedir too... */
-#ifndef RECLAIM_NO_ALIAS
-#define fopen save_fopen
-#define fdopen save_fdopen
-#define freopen save_freopen
-#define fclose save_fclose
-#endif
-/* Memory allocation */
-_RECLAIM_DECL_FUN(_RECLAIM_VOID_PTR, save_malloc, (size_t));
-_RECLAIM_DECL_FUN(_RECLAIM_VOID_PTR, save_calloc, (size_t, size_t));
-_RECLAIM_DECL_FUN(_RECLAIM_VOID_PTR, save_realloc,
- (_RECLAIM_VOID_PTR, size_t));
-_RECLAIM_DECL_FUN(void, save_free, (_RECLAIM_VOID_PTR));
-_RECLAIM_DECL_FUN(void, save_cfree, (_RECLAIM_VOID_PTR));
-#ifndef RECLAIM_NO_ALIAS
-#define malloc save_malloc
-#define calloc save_calloc
-#define realloc save_realloc
-#define free save_free
-#define cfree save_cfree
-#endif
-/* Generic interfaces to malloc etc... */
-_RECLAIM_DECL_FUN(_RECLAIM_VOID_PTR, plain_malloc, (size_t));
-_RECLAIM_DECL_FUN(_RECLAIM_VOID_PTR, plain_realloc,
- (_RECLAIM_VOID_PTR, size_t));
-_RECLAIM_DECL_FUN(void, plain_free, (_RECLAIM_VOID_PTR));
-#endif /* _RECLAIM_H */
-
-
-
-
diff --git a/erts/etc/vxworks/reclaim_private.h b/erts/etc/vxworks/reclaim_private.h
deleted file mode 100644
index 4ed935bee2..0000000000
--- a/erts/etc/vxworks/reclaim_private.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 1998-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 _RECLAIM_PRIVATE_H
-#define _RECLAIM_PRIVATE_H
-/*
- * Private header for the reclaim facility, also included in the emulator.
- */
-
-#include "reclaim.h"
-
-/* Typedefs for ANSI memory allocation function pointers */
-typedef void *(*MallocFunction)(size_t);
-typedef void *(*ReallocFunction)(void *, size_t);
-typedef void *(*CallocFunction)(size_t, size_t);
-typedef void (*FreeFunction)(void *);
-typedef STATUS (*CfreeFunction)(char *);
-
-/* Functions for internal use and use by the emulator */
-extern int reclaim_max_files(void);
-extern void set_reclaim_free_function(FreeFunction f);
-extern void save_delete_hook(FUNCPTR func, caddr_t parm);
-extern void *save_malloc2(size_t size, MallocFunction mf);
-extern void *save_calloc2(size_t nelem, size_t elsize, CallocFunction cf);
-extern void *save_realloc2(void *optr, size_t size, ReallocFunction rf);
-extern void save_free2(void *ptr, FreeFunction ff);
-extern void save_cfree2(void *ptr, CfreeFunction ff);
-
-#endif /* _RECLAIM_PRIVATE_H */
diff --git a/erts/etc/vxworks/resolv.conf b/erts/etc/vxworks/resolv.conf
deleted file mode 100644
index 85c89d64c4..0000000000
--- a/erts/etc/vxworks/resolv.conf
+++ /dev/null
@@ -1,6 +0,0 @@
-domain du.uab.ericsson.se
-nameserver 134.138.176.16
-nameserver 136.225.254.224
-nameserver 134.138.128.25
-search du.uab.ericsson.se uab.ericsson.se ericsson.se
-lookup bind file
diff --git a/erts/etc/vxworks/vxcall.c b/erts/etc/vxworks/vxcall.c
deleted file mode 100644
index 3362d05fc5..0000000000
--- a/erts/etc/vxworks/vxcall.c
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * %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%
- */
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-
-#include <vxWorks.h>
-#include <symLib.h>
-#include <sysSymTbl.h>
-#include <a_out.h>
-
-extern char *malloc();
-static STATUS lookup();
-
-/*
- Little utility to convert from Unix' argv,argv calling conventions to
- VxWorks' arg0,arg1,arg2,...
- Will do limited argument parsing - no parenthesis around nor commas
- between the args, which may be "-enclosed strings (without \ escapes),
- '-enclosed characters (also no \ escapes), integers, or symbols.
-*/
-
-int vxcall(argc, argv)
-int argc;
-char **argv;
-{
- int vxarg[10]; /* Max 10 args can be passed */
- FUNCPTR entry;
- SYM_TYPE type;
- int i, l;
-
-#ifdef DEBUG
- fdprintf(2, "vxcall:");
- for (i = 1; i < argc; i++)
- fdprintf(2, " %s", argv[i]);
- fdprintf(2, "\n");
-#endif
- if (lookup(argv[1], N_EXT | N_TEXT, (char **)&entry) != OK)
- return(ERROR);
- /* Do limited "C" parsing of the args */
- for (i = 0; i < 10; i++) {
- if (i < argc - 2) {
- switch (argv[i+2][0]) {
- case '"':
- l = strlen(argv[i+2]) - 1;
- if (argv[i+2][l] != '"')
- return(ERROR);
- /* just strip the quotes - should do \escapes within... */
- vxarg[i] = (int)&argv[i+2][1];
- argv[i+2][l] = '\0';
- break;
- case '\'':
- if (argv[i+2][2] != '\'')
- return(ERROR);
- vxarg[i] = argv[i+2][1]; /* should do \escapes... */
- break;
- case '1': case '2': case '3': case '4':
- case '5': case '6': case '7': case '8': case '9':
- vxarg[i] = atoi(argv[i+2]); /* should do octal, hex, float.. */
- break;
- default:
- if (lookup(argv[i+2], 0, (char **)&vxarg[i]) != OK)
- return(ERROR);
- }
- } else
- vxarg[i] = 0;
- }
-#ifdef DEBUG
- fdprintf(2, "calling 0x%x(0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x)\n",
- entry, vxarg[0], vxarg[1], vxarg[2], vxarg[3], vxarg[4],
- vxarg[5], vxarg[6], vxarg[7], vxarg[8], vxarg[9]);
-#endif
- return((*entry)(vxarg[0], vxarg[1], vxarg[2], vxarg[3], vxarg[4],
- vxarg[5], vxarg[6], vxarg[7], vxarg[8], vxarg[9]));
-}
-
-/* Entry point for unix:cmd in post-4.1 erlang - uses "sh -c 'cmd...'" */
-int sh(argc, argv)
-int argc;
-char **argv;
-{
- int ll = strlen(argv[argc-1]) - 1;
-
-#ifdef DEBUG
- int i;
- fdprintf(2, "sh:");
- for (i = 1; i < argc; i++)
- fdprintf(2, " %s", argv[i]);
- fdprintf(2, "\n");
-#endif
- if (strcmp(argv[1], "-c") != 0 ||
- argv[2][0] != '\'' || argv[argc-1][ll] != '\'')
- return(ERROR);
- argv[argc-1][ll] = '\0'; /* delete trailing ' */
- argv[2]++; /* skip leading ' (*after* the above!) */
- return(vxcall(argc-1, argv+1));
-}
-
-/* Lookup symbol; get address for text symbols, value (assuming int)
- otherwise; return OK or ERROR on failure
- Symbol name is null-terminated and without the leading '_' */
-STATUS
-lookup(sym, stype, value)
-char *sym, **value;
-int stype;
-{
- char buf[256];
- char *symname = buf;
- int len, ret;
- SYM_TYPE type;
-
- len = strlen(sym);
- if (len > 254 && (symname = malloc(len+2)) == NULL)
- return(ERROR);
-#if defined _ARCH_PPC || defined SIMSPARCSOLARIS
- /* GCC for PPC or SIMSPARC doesn't add a leading _ to symbols */
- strcpy(symname, sym);
-#else
- sprintf(symname, "_%s", sym);
-#endif
- ret = (stype != 0) ?
- symFindByNameAndType(sysSymTbl, symname, value, &type, stype, stype) :
- symFindByName(sysSymTbl, symname, value, &type);
- if (symname != buf)
- free(symname);
- if (ret == OK && (type & N_TEXT) == 0) /* get value */
- *value = (char *)*((int *)*value);
- return(ret);
-}
diff --git a/erts/etc/vxworks/wd_example.c b/erts/etc/vxworks/wd_example.c
deleted file mode 100644
index 0e3a6a1cb2..0000000000
--- a/erts/etc/vxworks/wd_example.c
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * %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%
- */
-/*
- * File: frc5te_wd.c
- * Purpose: Watchdog NMI handling for FORCE 5TE
- *
- * Description:
- * The watchdog handler routines are system specific. A program that
- * wants to utilize a hardware watchdog should call wd_init and test
- * the return value. If wd_init returns true (!0); there is a hardware
- * watchdog, and that watchdog has been activated. If no watchdog exists,
- * wd_init returns false (0).
- *
- * To keep the watchdog happy, call wd_reset at least every X seconds,
- * where X is the number of seconds specified in the call to wd_init.
- *
- * The watchdog can be disarmed by setting the variable wd_disarmed to 1,
- * and armed again by setting the same variable to 0. Watchdog status
- * information can be retrieved using the function wd_status.
- *
- */
-
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-#include <frc5e.h>
-#include <logLib.h>
-#include <taskLib.h>
-#include <sysLib.h>
-#include <stdio.h>
-#include "hw_watchdog.h"
-
-/* prototypes */
-extern sysNMIConnect();
-#ifdef __STDC__
-void wd_keeper(int);
-void wd_nmi_int(UINT8);
-void wd_status(void);
-#else
-void wd_keeper();
-void wd_nmi_int();
-void wd_status();
-#endif
-
-#define WD_NMI_MIN_DELAY 0.830 /* Min time before watchdog NMI (in seconds) */
-#define WD_RESET_FREQUENCY (WD_NMI_MIN_DELAY / 2) /* how often the timer is reset */
-
-#define WD_KEEPER_STACK_SIZE 10000
-
-/* global variables */
-extern int spTaskOptions;
-static volatile int wd_count_startval; /* start value set by wd_init */
-static volatile int wd_count; /* counter for wd_keeper */
-volatile int wd_disarmed = 0; /* debug feature */
-
-/* wd_init is executed to initialize the watchdog. It spawns the task */
-/* wd_keeper and returns true (non-zero) if a hardware watchdog exists, */
-/* or returns false (zero) otherwise. */
-int wd_init(timeout, prio)
- int timeout, prio;
-{
- taskSpawn("wd_keeper", prio, spTaskOptions, WD_KEEPER_STACK_SIZE,
- (FUNCPTR)wd_keeper, timeout,0,0,0,0,0,0,0,0,0);
- return !0; /* watchdog exists */
-}
-
-
-/* wd_reset is called as an alive-signal from the supervisor process. */
-/* If there is no call to this function within a certain time, the */
-/* watchdog will reboot the system. */
-void wd_reset()
-{
- wd_count = wd_count_startval;
-}
-
-
-/* wd_keeper runs as a separate task and resets the watchdog timer */
-/* before an NMI is generated. This task uses the counter wd_count to */
-/* decide if it should exit or keep resetting the timer. */
-/* Note! This task must run with higher priority than the application! */
-void wd_keeper(timeout)
- int timeout;
-{
- int wd_delay = sysClkRateGet() * WD_RESET_FREQUENCY;
- wd_count_startval = (int)(timeout / WD_RESET_FREQUENCY);
- wd_count = wd_count_startval;
-
- /* Connect and enable level 15 interrupts */
- sysNMIConnect((VOIDFUNCPTR) wd_nmi_int, WD_NMI, WD_NMI);
- *(char *)FRC5CE_GEN_PURPOSE2_REG |= FRC5CE_NMI_ENABLE;
-
- while ((wd_count > 0) || wd_disarmed) {
- *(char *)FRC5CE_VME_A32MAP_REG |= FRC5CE_WATCHDOG_ENABLE;
- taskDelay(wd_delay);
- if (!wd_disarmed) wd_count--;
- else wd_count = wd_count_startval;
- }
- logMsg("Watchdog keeper exits. No alive signal from application in %d seconds.\n",wd_count_startval * WD_RESET_FREQUENCY,0,0,0,0,0);
-}
-
-
-/* wd_nmi_int is the function connected to the watchdog interrupt. */
-/* It will report the failure to reset the watchdog timer. */
-void wd_nmi_int(type)
- UINT8 type;
-{
- switch(type) {
- case WD_NMI:
- logMsg("Watchdog interrupt! System will reboot.\n",0,0,0,0,0,0);
- break;
- default:
- logMsg("Bad type (%d) in call to watchdog interrupt handler.\n",type,0,0,0,0,0);
- break;
- }
-}
-
-
-/* wd_status displays the current value of the counter. */
-void wd_status()
-{
- fprintf(stderr, "Watchdog is %sarmed.\n", wd_disarmed ? "dis" : "");
- fprintf(stderr, "Counter value: %d\n", wd_count);
- fprintf(stderr, "Start value is: %d (%d seconds)\n",
- wd_count_startval, (int)(wd_count_startval * WD_RESET_FREQUENCY));
-}
diff --git a/erts/etc/win32/Makefile b/erts/etc/win32/Makefile
index 4218be1eff..cc9021da70 100644
--- a/erts/etc/win32/Makefile
+++ b/erts/etc/win32/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2009. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/erts/etc/win32/erlsrv/erlsrv_service.c b/erts/etc/win32/erlsrv/erlsrv_service.c
index 242e2905a9..58738ee445 100644
--- a/erts/etc/win32/erlsrv/erlsrv_service.c
+++ b/erts/etc/win32/erlsrv/erlsrv_service.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2011. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2012. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -104,11 +104,10 @@ static VOID WINAPI handler(DWORD control){
log_debug(buffer);
switch(control){
case SERVICE_CONTROL_STOP:
+ case SERVICE_CONTROL_SHUTDOWN:
set_stop_pending(30000,1);
SetEvent(eventStop);
return;
- case SERVICE_CONTROL_SHUTDOWN:
- return;
default:
reset_current();
break;
diff --git a/erts/etc/win32/nsis/Makefile b/erts/etc/win32/nsis/Makefile
index 4ed4ef2700..7bcecaa264 100644
--- a/erts/etc/win32/nsis/Makefile
+++ b/erts/etc/win32/nsis/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2003-2011. All Rights Reserved.
+# Copyright Ericsson AB 2003-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -62,9 +62,15 @@ else
endif
REDIST_FILE=$(shell (sh ./find_redist.sh || echo ""))
+ifeq ($(MSYSTEM),MINGW32)
+ NICEREDISTFILE=$(shell (msys2win_path.sh -m "$(REDIST_FILE)" 2>/dev/null || echo ""))
+else
+ NICEREDISTFILE=$(shell (cygpath -d -m "$(REDIST_FILE)" 2>/dev/null || echo ""))
+endif
+
REDIST_TARGET=$(shell (sh ./find_redist.sh -n || echo ""))
-REDIST_DLL_VERSION=$(shell (sh ./dll_version_helper.sh || echo ""))
-REDIST_DLL_NAME=$(shell (sh ./dll_version_helper.sh -n || echo ""))
+REDIST_DLL_VERSION=$(shell (sh ./dll_version_helper.sh $(NICEREDISTFILE) || echo ""))
+REDIST_DLL_NAME=$(shell (sh ./dll_version_helper.sh -n $(NICEREDISTFILE) || echo ""))
release_spec:
@NSIS_VER=`makensis /hdrinfo | head -1 | awk '{print $$2}'`; \
@@ -91,7 +97,7 @@ release_spec:
fi;\
if [ '!' -z "$(REDIST_FILE)" -a '!' -z "$(REDIST_DLL_VERSION)" ];\
then \
- cp $(REDIST_FILE) "$(RELEASE_PATH)/$(REDIST_TARGET);"\
+ cp $(REDIST_FILE) "$(RELEASE_PATH)/$(REDIST_TARGET)";\
echo '!define HAVE_REDIST_FILE 1' >> $(VERSION_HEADER); \
echo '!define REDIST_DLL_VERSION "$(REDIST_DLL_VERSION)"' >> $(VERSION_HEADER);\
echo '!define REDIST_DLL_NAME "$(REDIST_DLL_NAME)"' >> $(VERSION_HEADER);\
diff --git a/erts/etc/win32/nsis/dll_version_helper.sh b/erts/etc/win32/nsis/dll_version_helper.sh
index 96e4532b7a..0d9ba4248d 100755
--- a/erts/etc/win32/nsis/dll_version_helper.sh
+++ b/erts/etc/win32/nsis/dll_version_helper.sh
@@ -25,6 +25,13 @@
# echo "8.0.50727.763"
# exit 0
+if [ "$1" = "-n" ]; then
+ SWITCH=$1
+ shift
+else
+ SWITCH=""
+fi
+
cat > hello.c <<EOF
#include <windows.h>
#include <stdio.h>
@@ -42,11 +49,16 @@ if [ '!' -f hello.exe.manifest ]; then
# need another way of getting the version
DLLNAME=`dumpbin.exe -imports hello.exe | egrep MSVCR.*dll`
DLLNAME=`echo $DLLNAME`
+ if [ '!' -z "$1" ]; then
+ FILETOLOOKIN=$1
+ else
+ FILETOLOOKIN=$DLLNAME
+ fi
cat > helper.c <<EOF
#include <windows.h>
#include <stdio.h>
-#define REQ_MODULE "$DLLNAME"
+#define REQ_MODULE "$FILETOLOOKIN"
int main(void)
{
@@ -100,7 +112,7 @@ else
NAME=`grep '<assemblyIdentity' hello.exe.manifest | sed 's,.*name=.[A-Za-z\.]*\([0-9]*\).*,msvcr\1.dll,g' | grep -v '<'`
fi
#rm -f hello.c hello.obj hello.exe hello.exe.manifest helper.c helper.obj helper.exe helper.exe.manifest
-if [ "$1" = "-n" ]; then
+if [ "$SWITCH" = "-n" ]; then
ASKEDFOR=$NAME
else
ASKEDFOR=$VERSION
diff --git a/erts/etc/win32/nsis/erlang20.nsi b/erts/etc/win32/nsis/erlang20.nsi
index c5ada9e3b3..3333c4a9aa 100644
--- a/erts/etc/win32/nsis/erlang20.nsi
+++ b/erts/etc/win32/nsis/erlang20.nsi
@@ -35,6 +35,7 @@
!include "MUI.nsh"
!include "WordFunc.nsh"
+ !include "WinVer.nsh"
;--------------------------------
;Configuration
@@ -341,6 +342,9 @@ FunctionEnd
Function .onInit
Var /GLOBAL archprefix
Var /GLOBAL sysnativedir
+ Var /GLOBAL winvermajor
+ Var /GLOBAL winverminor
+
SectionGetFlags 0 $MYTEMP
StrCmpS ${WINTYPE} "win64" +1 +4
StrCpy $archprefix "amd64"
@@ -348,9 +352,16 @@ Function .onInit
Goto +3
StrCpy $archprefix "x86"
StrCpy $sysnativedir $SYSDIR
- ;MessageBox MB_YESNO "Found $sysnativedir\${REDIST_DLL_NAME}" IDYES FoundLbl
+ ${WinVerGetMajor} $0
+ ${WinVerGetMinor} $1
+ StrCpy $winvermajor $0
+ StrCpy $winverminor $1
IfFileExists $sysnativedir\${REDIST_DLL_NAME} MaybeFoundInSystemLbl
SearchSxSLbl:
+ IntCmp $winvermajor 6 WVCheckMinorLbl WVCheckDoneLbl NotFoundLbl
+ WVCheckMinorLbl:
+ IntCmp $winverminor 1 WVCheckDoneLbl WVCheckDoneLbl NotFoundLbl
+ WVCheckDoneLbl:
FindFirst $0 $1 $WINDIR\WinSxS\$archprefix*
LoopLbl:
StrCmp $1 "" NotFoundLbl
diff --git a/erts/etc/win32/port_entry.c b/erts/etc/win32/port_entry.c
index 49b5ad2f34..30b54035e0 100644
--- a/erts/etc/win32/port_entry.c
+++ b/erts/etc/win32/port_entry.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2009. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2012. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -45,6 +45,8 @@ extern void mainCRTStartup(void);
BOOL WINAPI erl_port_default_handler(DWORD ctrl){
if(ctrl == CTRL_LOGOFF_EVENT)
return TRUE;
+ if(ctrl == CTRL_SHUTDOWN_EVENT)
+ return TRUE;
return FALSE;
}
diff --git a/erts/etc/win32/start_erl.c b/erts/etc/win32/start_erl.c
index 28c8e55bd3..facf79e5ff 100644
--- a/erts/etc/win32/start_erl.c
+++ b/erts/etc/win32/start_erl.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2011. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2012. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -585,6 +585,9 @@ BOOL WINAPI LogoffHandlerRoutine( DWORD dwCtrlType )
if(dwCtrlType == CTRL_LOGOFF_EVENT) {
return TRUE;
}
+ if(dwCtrlType == CTRL_SHUTDOWN_EVENT) {
+ return TRUE;
+ }
return FALSE;
}
diff --git a/erts/include/internal/ethread.h b/erts/include/internal/ethread.h
index 142c26c0ca..aef31e282a 100644
--- a/erts/include/internal/ethread.h
+++ b/erts/include/internal/ethread.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2004-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2004-2012. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -355,10 +355,8 @@ extern ethr_runtime_t ethr_runtime__;
# include "sparc64/ethread.h"
# endif
# endif
-#if 0
# include "gcc/ethread.h"
# include "libatomic_ops/ethread.h"
-#endif
# endif
# elif defined(ETHR_HAVE_LIBATOMIC_OPS)
# include "libatomic_ops/ethread.h"
diff --git a/erts/lib_src/Makefile.in b/erts/lib_src/Makefile.in
index aed889eaef..88083bfe7d 100644
--- a/erts/lib_src/Makefile.in
+++ b/erts/lib_src/Makefile.in
@@ -17,6 +17,7 @@
# %CopyrightEnd%
#
+include $(ERL_TOP)/make/output.mk
include $(ERL_TOP)/make/target.mk
include ../include/internal/$(TARGET)/ethread.mk
@@ -326,6 +327,7 @@ _create_dirs := $(shell mkdir -p $(CREATE_DIRS))
all: $(OBJ_DIR)/MADE
$(OBJ_DIR)/MADE: $(ETHREAD_LIB) $(ERTS_LIBS) $(ERTS_INTERNAL_LIBS)
+ $(gen_verbose)
ifeq ($(OMIT_OMIT_FP),yes)
@echo '* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *'
@echo '* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *'
@@ -335,7 +337,7 @@ ifeq ($(OMIT_OMIT_FP),yes)
@echo '* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *'
@echo '* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *'
endif
- echo $? > $(OBJ_DIR)/MADE
+ $(V_at)echo $? > $(OBJ_DIR)/MADE
#
# The libs ...
@@ -345,93 +347,97 @@ AR_OUT=-out:
AR_FLAGS=
else
AR_OUT=
+ifeq ($(V),0)
+AR_FLAGS=rc
+else
AR_FLAGS=rcv
endif
+endif
ifndef RANLIB
RANLIB=true
endif
$(ETHREAD_LIB): $(ETHREAD_LIB_OBJS)
- $(AR) $(AR_FLAGS) $(AR_OUT)$@ $(ETHREAD_LIB_OBJS)
- $(RANLIB) $@
+ $(V_AR) $(AR_FLAGS) $(AR_OUT)$@ $(ETHREAD_LIB_OBJS)
+ $(V_RANLIB) $@
$(ERTS_INTERNAL_LIB): $(ERTS_INTERNAL_LIB_OBJS)
- $(AR) $(AR_FLAGS) $(AR_OUT)$@ $(ERTS_INTERNAL_LIB_OBJS)
- $(RANLIB) $@
+ $(V_AR) $(AR_FLAGS) $(AR_OUT)$@ $(ERTS_INTERNAL_LIB_OBJS)
+ $(V_RANLIB) $@
$(ERTS_INTERNAL_r_LIB): $(ERTS_INTERNAL_r_LIB_OBJS)
- $(AR) $(AR_FLAGS) $(AR_OUT)$@ $(ERTS_INTERNAL_r_LIB_OBJS)
- $(RANLIB) $@
+ $(V_AR) $(AR_FLAGS) $(AR_OUT)$@ $(ERTS_INTERNAL_r_LIB_OBJS)
+ $(V_RANLIB) $@
$(ERTS_MD_LIB): $(ERTS_MD_LIB_OBJS)
- $(AR) $(AR_FLAGS) $(AR_OUT)$@ $(ERTS_MD_LIB_OBJS)
- $(RANLIB) $@
+ $(V_AR) $(AR_FLAGS) $(AR_OUT)$@ $(ERTS_MD_LIB_OBJS)
+ $(V_RANLIB) $@
$(ERTS_MDd_LIB): $(ERTS_MDd_LIB_OBJS)
- $(AR) $(AR_FLAGS) $(AR_OUT)$@ $(ERTS_MDd_LIB_OBJS)
- $(RANLIB) $@
+ $(V_AR) $(AR_FLAGS) $(AR_OUT)$@ $(ERTS_MDd_LIB_OBJS)
+ $(V_RANLIB) $@
$(ERTS_MT_LIB): $(ERTS_MT_LIB_OBJS)
- $(AR) $(AR_FLAGS) $(AR_OUT)$@ $(ERTS_MT_LIB_OBJS)
- $(RANLIB) $@
+ $(V_AR) $(AR_FLAGS) $(AR_OUT)$@ $(ERTS_MT_LIB_OBJS)
+ $(V_RANLIB) $@
$(ERTS_MTd_LIB): $(ERTS_MTd_LIB_OBJS)
- $(AR) $(AR_FLAGS) $(AR_OUT)$@ $(ERTS_MTd_LIB_OBJS)
- $(RANLIB) $@
+ $(V_AR) $(AR_FLAGS) $(AR_OUT)$@ $(ERTS_MTd_LIB_OBJS)
+ $(V_RANLIB) $@
$(ERTS_r_LIB): $(ERTS_r_LIB_OBJS)
- $(AR) $(AR_FLAGS) $(AR_OUT)$@ $(ERTS_r_LIB_OBJS)
- $(RANLIB) $@
+ $(V_AR) $(AR_FLAGS) $(AR_OUT)$@ $(ERTS_r_LIB_OBJS)
+ $(V_RANLIB) $@
$(ERTS_LIB): $(ERTS_LIB_OBJS)
- $(AR) $(AR_FLAGS) $(AR_OUT)$@ $(ERTS_LIB_OBJS)
- $(RANLIB) $@
+ $(V_AR) $(AR_FLAGS) $(AR_OUT)$@ $(ERTS_LIB_OBJS)
+ $(V_RANLIB) $@
#
# Object files
#
$(r_OBJ_DIR)/ethr_x86_sse2_asm.o: pthread/ethr_x86_sse2_asm.c
- $(CC) -msse2 $(THR_DEFS) $(CFLAGS) $(INCLUDES) -c $< -o $@
+ $(V_CC) -msse2 $(THR_DEFS) $(CFLAGS) $(INCLUDES) -c $< -o $@
$(r_OBJ_DIR)/%.o: common/%.c
- $(CC) $(THR_DEFS) $(CFLAGS) $(INCLUDES) -c $< -o $@
+ $(V_CC) $(THR_DEFS) $(CFLAGS) $(INCLUDES) -c $< -o $@
$(r_OBJ_DIR)/%.o: $(ETHR_THR_LIB_BASE_DIR)/%.c
- $(CC) $(THR_DEFS) $(CFLAGS) $(INCLUDES) -c $< -o $@
+ $(V_CC) $(THR_DEFS) $(CFLAGS) $(INCLUDES) -c $< -o $@
$(OBJ_DIR)/%.o: common/%.c
- $(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@
+ $(V_CC) $(CFLAGS) $(INCLUDES) -c $< -o $@
$(OBJ_DIR)/%.o: $(ETHR_THR_LIB_BASE_DIR)/%.c
- $(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@
+ $(V_CC) $(CFLAGS) $(INCLUDES) -c $< -o $@
# Win32 specific
$(MD_OBJ_DIR)/%.o: common/%.c
- $(CC) $(THR_DEFS) $(CFLAGS) -MD $(INCLUDES) -c $< -o $@
+ $(V_CC) $(THR_DEFS) $(CFLAGS) -MD $(INCLUDES) -c $< -o $@
$(MD_OBJ_DIR)/%.o: $(ETHR_THR_LIB_BASE_DIR)/%.c
- $(CC) $(THR_DEFS) $(CFLAGS) -MD $(INCLUDES) -c $< -o $@
+ $(V_CC) $(THR_DEFS) $(CFLAGS) -MD $(INCLUDES) -c $< -o $@
$(MDd_OBJ_DIR)/%.o: common/%.c
- $(CC) $(THR_DEFS) $(CFLAGS) -MDd $(INCLUDES) -c $< -o $@
+ $(V_CC) $(THR_DEFS) $(CFLAGS) -MDd $(INCLUDES) -c $< -o $@
$(MDd_OBJ_DIR)/%.o: $(ETHR_THR_LIB_BASE_DIR)/%.c
- $(CC) $(THR_DEFS) $(CFLAGS) -MDd $(INCLUDES) -c $< -o $@
+ $(V_CC) $(THR_DEFS) $(CFLAGS) -MDd $(INCLUDES) -c $< -o $@
$(MT_OBJ_DIR)/%.o: common/%.c
- $(CC) $(THR_DEFS) $(CFLAGS) -MT $(INCLUDES) -c $< -o $@
+ $(V_CC) $(THR_DEFS) $(CFLAGS) -MT $(INCLUDES) -c $< -o $@
$(MT_OBJ_DIR)/%.o: $(ETHR_THR_LIB_BASE_DIR)/%.c
- $(CC) $(THR_DEFS) $(CFLAGS) -MT $(INCLUDES) -c $< -o $@
+ $(V_CC) $(THR_DEFS) $(CFLAGS) -MT $(INCLUDES) -c $< -o $@
$(MTd_OBJ_DIR)/%.o: common/%.c
- $(CC) $(THR_DEFS) $(CFLAGS) -MTd $(INCLUDES) -c $< -o $@
+ $(V_CC) $(THR_DEFS) $(CFLAGS) -MTd $(INCLUDES) -c $< -o $@
$(MTd_OBJ_DIR)/%.o: $(ETHR_THR_LIB_BASE_DIR)/%.c
- $(CC) $(THR_DEFS) $(CFLAGS) -MTd $(INCLUDES) -c $< -o $@
+ $(V_CC) $(THR_DEFS) $(CFLAGS) -MTd $(INCLUDES) -c $< -o $@
#
# Install
@@ -560,73 +566,74 @@ DEPEND_MK=$(OBJ_DIR)/depend.mk
.PHONY: depend
depend: $(DEPEND_MK)
$(DEPEND_MK):
- @echo "Generating dependency file $(DEPEND_MK)..."
+ $(gen_verbose)
+ $(V_colon)@echo "Generating dependency file $(DEPEND_MK)..."
@echo "# Generated dependency rules" > $(DEPEND_MK);
@echo "# " >> $(DEPEND_MK);
ifneq ($(strip $(ETHREAD_LIB_SRC)),)
@echo "# ethread lib objects..." >> $(DEPEND_MK);
ifeq ($(USING_VC),yes)
- $(DEP_CC) -MM $(THR_DEFS) $(DEP_FLAGS) $(ETHREAD_LIB_SRC) \
+ $(V_at)$(DEP_CC) -MM $(THR_DEFS) $(DEP_FLAGS) $(ETHREAD_LIB_SRC) \
| $(SED_MD_DEPEND) >> $(DEPEND_MK)
- $(DEP_CC) -MM $(THR_DEFS) $(DEP_FLAGS) $(ETHREAD_LIB_SRC) \
+ $(V_at)$(DEP_CC) -MM $(THR_DEFS) $(DEP_FLAGS) $(ETHREAD_LIB_SRC) \
| $(SED_MDd_DEPEND) >> $(DEPEND_MK)
else
- $(DEP_CC) -MM $(THR_DEFS) $(DEP_FLAGS) $(ETHREAD_LIB_SRC) \
- | $(SED_r_DEPEND) >> $(DEPEND_MK)
+ $(V_at)$(DEP_CC) -MM $(THR_DEFS) $(DEP_FLAGS) $(ETHREAD_LIB_SRC) \
+ | $(SED_r_DEPEND) >$(V_at)> $(DEPEND_MK)
endif
endif
ifneq ($(strip $(ERTS_INTERNAL_LIB_SRCS)),)
ifneq ($(strip $(ETHREAD_LIB_SRC)),)
@echo "# erts_internal_r lib objects..." >> $(DEPEND_MK);
ifeq ($(USING_VC),yes)
- $(DEP_CC) -MM $(THR_DEFS) $(DEP_FLAGS) $(ERTS_INTERNAL_LIB_SRCS) \
+ $(V_at)$(DEP_CC) -MM $(THR_DEFS) $(DEP_FLAGS) $(ERTS_INTERNAL_LIB_SRCS) \
| $(SED_MD_DEPEND) >> $(DEPEND_MK)
- $(DEP_CC) -MM $(THR_DEFS) $(DEP_FLAGS) $(ERTS_INTERNAL_LIB_SRCS) \
+ $(V_at)$(DEP_CC) -MM $(THR_DEFS) $(DEP_FLAGS) $(ERTS_INTERNAL_LIB_SRCS) \
| $(SED_MDd_DEPEND) >> $(DEPEND_MK)
else
- $(DEP_CC) -MM $(THR_DEFS) $(DEP_FLAGS) $(ERTS_INTERNAL_LIB_SRCS) \
+ $(V_at)$(DEP_CC) -MM $(THR_DEFS) $(DEP_FLAGS) $(ERTS_INTERNAL_LIB_SRCS) \
| $(SED_r_DEPEND) >> $(DEPEND_MK)
endif
endif
@echo "# erts_internal lib objects..." >> $(DEPEND_MK);
ifeq ($(USING_VC),yes)
- $(DEP_CC) -MM $(DEP_FLAGS) $(ERTS_INTERNAL_LIB_SRCS) \
+ $(V_at)$(DEP_CC) -MM $(DEP_FLAGS) $(ERTS_INTERNAL_LIB_SRCS) \
| $(SED_MD_DEPEND) >> $(DEPEND_MK)
- $(DEP_CC) -MM $(DEP_FLAGS) $(ERTS_INTERNAL_LIB_SRCS) \
+ $(V_at)$(DEP_CC) -MM $(DEP_FLAGS) $(ERTS_INTERNAL_LIB_SRCS) \
| $(SED_MDd_DEPEND) >> $(DEPEND_MK)
else
- $(DEP_CC) -MM $(DEP_FLAGS) $(ERTS_INTERNAL_LIB_SRCS) \
+ $(V_at)$(DEP_CC) -MM $(DEP_FLAGS) $(ERTS_INTERNAL_LIB_SRCS) \
| $(SED_DEPEND) >> $(DEPEND_MK)
endif
endif
ifneq ($(strip $(ERTS_LIB_SRCS)),)
ifeq ($(USING_VC),yes)
@echo "# erts_MD lib objects..." >> $(DEPEND_MK);
- $(DEP_CC) -MM $(THR_DEFS) $(DEP_FLAGS) $(ERTS_LIB_SRCS) \
+ $(V_at)$(DEP_CC) -MM $(THR_DEFS) $(DEP_FLAGS) $(ERTS_LIB_SRCS) \
| $(SED_MD_DEPEND) >> $(DEPEND_MK)
@echo "# erts_MDd lib objects..." >> $(DEPEND_MK);
- $(DEP_CC) -MM $(THR_DEFS) $(DEP_FLAGS) $(ERTS_LIB_SRCS) \
+ $(V_at)$(DEP_CC) -MM $(THR_DEFS) $(DEP_FLAGS) $(ERTS_LIB_SRCS) \
| $(SED_MDd_DEPEND) >> $(DEPEND_MK)
@echo "# erts_MT lib objects..." >> $(DEPEND_MK);
- $(DEP_CC) -MM $(THR_DEFS) $(DEP_FLAGS) $(ERTS_LIB_SRCS) \
+ $(V_at)$(DEP_CC) -MM $(THR_DEFS) $(DEP_FLAGS) $(ERTS_LIB_SRCS) \
| $(SED_MT_DEPEND) >> $(DEPEND_MK)
@echo "# erts_MTd lib objects..." >> $(DEPEND_MK);
- $(DEP_CC) -MM $(THR_DEFS) $(DEP_FLAGS) $(ERTS_LIB_SRCS) \
+ $(V_at)$(DEP_CC) -MM $(THR_DEFS) $(DEP_FLAGS) $(ERTS_LIB_SRCS) \
| $(SED_MTd_DEPEND) >> $(DEPEND_MK)
@echo "# erts_internal_r lib objects..." >> $(DEPEND_MK);
- $(DEP_CC) -MM $(THR_DEFS) $(DEP_FLAGS) $(ERTS_INTERNAL_LIB_SRCS) \
+ $(V_at)$(DEP_CC) -MM $(THR_DEFS) $(DEP_FLAGS) $(ERTS_INTERNAL_LIB_SRCS) \
| $(SED_MD_DEPEND) >> $(DEPEND_MK)
@echo "# erts_internal_r.debug lib objects..." >> $(DEPEND_MK);
- $(DEP_CC) -MM $(THR_DEFS) $(DEP_FLAGS) $(ERTS_INTERNAL_LIB_SRCS) \
+ $(V_at)$(DEP_CC) -MM $(THR_DEFS) $(DEP_FLAGS) $(ERTS_INTERNAL_LIB_SRCS) \
| $(SED_MDd_DEPEND) >> $(DEPEND_MK)
else
ifneq ($(strip $(ETHREAD_LIB_SRC)),)
@echo "# erts_r lib objects..." >> $(DEPEND_MK);
- $(DEP_CC) -MM $(THR_DEFS) $(DEP_FLAGS) $(ERTS_LIB_SRCS) \
+ $(V_at)$(DEP_CC) -MM $(THR_DEFS) $(DEP_FLAGS) $(ERTS_LIB_SRCS) \
| $(SED_r_DEPEND) >> $(DEPEND_MK)
endif
@echo "# erts lib objects..." >> $(DEPEND_MK);
- $(DEP_CC) -MM $(DEP_FLAGS) $(ERTS_LIB_SRCS) \
+ $(V_at)$(DEP_CC) -MM $(DEP_FLAGS) $(ERTS_LIB_SRCS) \
| $(SED_DEPEND) >> $(DEPEND_MK)
endif
endif
diff --git a/erts/lib_src/common/erl_misc_utils.c b/erts/lib_src/common/erl_misc_utils.c
index 3b123063fa..1b49f69581 100644
--- a/erts/lib_src/common/erl_misc_utils.c
+++ b/erts/lib_src/common/erl_misc_utils.c
@@ -28,8 +28,6 @@
#include "erl_misc_utils.h"
#if defined(__WIN32__)
-#elif defined(VXWORKS)
-# include <selectLib.h>
#else /* UNIX */
# include <stdio.h>
# include <sys/types.h>
@@ -124,6 +122,12 @@
#include <sys/sysctl.h>
#endif
+/* Simplify include for static functions */
+
+#if defined(__linux__) || defined(HAVE_KSTAT) || defined(__WIN32__) || defined(__FreeBSD__)
+# define ERTS_CPU_TOPOLOGY_ENABLED (1)
+#endif
+
static int read_topology(erts_cpu_info_t *cpuinfo);
#if defined(ERTS_HAVE_MISC_UTIL_AFFINITY_MASK__)
@@ -669,6 +673,7 @@ erts_unbind_from_cpu_str(char *str)
}
+#if defined(ERTS_CPU_TOPOLOGY_ENABLED)
static int
pn_cmp(const void *vx, const void *vy)
{
@@ -759,6 +764,7 @@ adjust_processor_nodes(erts_cpu_info_t *cpuinfo, int no_nodes)
}
}
}
+#endif
#ifdef __linux__
diff --git a/erts/lib_src/common/erl_printf_format.c b/erts/lib_src/common/erl_printf_format.c
index 473791dce4..fd25cce7ed 100644
--- a/erts/lib_src/common/erl_printf_format.c
+++ b/erts/lib_src/common/erl_printf_format.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2005-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2005-2012. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -457,6 +457,15 @@ static int fmt_double(fmtfn_t fn,void*arg,double val,
return res;
}
+/* strnlen doesn't exist everywhere */
+static size_t my_strnlen(const char *s, size_t maxlen)
+{
+ size_t i = 0;
+ while (i < maxlen && s[i] != '\0')
+ i++;
+ return i;
+}
+
int erts_printf_format(fmtfn_t fn, void* arg, char* fmt, va_list ap)
{
char* ptr0 = fmt;
@@ -771,9 +780,7 @@ int erts_printf_format(fmtfn_t fn, void* arg, char* fmt, va_list ap)
case FMTC_s: {
char* str = va_arg(ap,char*);
- int len = strlen(str);
- if (precision >= 0 && precision < len)
- len = precision;
+ int len = (precision >= 0) ? my_strnlen(str,precision) : strlen(str);
if (width > 0 && !(fmt & FMTF_adj)) {
if (width > len)
BLANKS(fn, arg, width - len, count);
diff --git a/erts/lib_src/common/ethr_aux.c b/erts/lib_src/common/ethr_aux.c
index 89149b716b..f4ff08d368 100644
--- a/erts/lib_src/common/ethr_aux.c
+++ b/erts/lib_src/common/ethr_aux.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2010-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2010-2012. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
diff --git a/erts/preloaded/ebin/erl_prim_loader.beam b/erts/preloaded/ebin/erl_prim_loader.beam
index 62d161850c..344fa3ecc0 100644
--- a/erts/preloaded/ebin/erl_prim_loader.beam
+++ b/erts/preloaded/ebin/erl_prim_loader.beam
Binary files differ
diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam
index 5e485948c6..4ff729e06c 100644
--- a/erts/preloaded/ebin/erlang.beam
+++ b/erts/preloaded/ebin/erlang.beam
Binary files differ
diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam
new file mode 100644
index 0000000000..af06c2a1cb
--- /dev/null
+++ b/erts/preloaded/ebin/erts_internal.beam
Binary files differ
diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam
index 3bf5777b19..9710204552 100644
--- a/erts/preloaded/ebin/init.beam
+++ b/erts/preloaded/ebin/init.beam
Binary files differ
diff --git a/erts/preloaded/ebin/otp_ring0.beam b/erts/preloaded/ebin/otp_ring0.beam
index ab86e0eab4..0ba1cb8f73 100644
--- a/erts/preloaded/ebin/otp_ring0.beam
+++ b/erts/preloaded/ebin/otp_ring0.beam
Binary files differ
diff --git a/erts/preloaded/ebin/prim_file.beam b/erts/preloaded/ebin/prim_file.beam
index 538aa264d3..b8f71b0c1e 100644
--- a/erts/preloaded/ebin/prim_file.beam
+++ b/erts/preloaded/ebin/prim_file.beam
Binary files differ
diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam
index 941ce02467..a1aca09d1d 100644
--- a/erts/preloaded/ebin/prim_inet.beam
+++ b/erts/preloaded/ebin/prim_inet.beam
Binary files differ
diff --git a/erts/preloaded/ebin/prim_zip.beam b/erts/preloaded/ebin/prim_zip.beam
index bff09897d9..bec1f2cc21 100644
--- a/erts/preloaded/ebin/prim_zip.beam
+++ b/erts/preloaded/ebin/prim_zip.beam
Binary files differ
diff --git a/erts/preloaded/ebin/zlib.beam b/erts/preloaded/ebin/zlib.beam
index 620b495ad4..1ac89dd42a 100644
--- a/erts/preloaded/ebin/zlib.beam
+++ b/erts/preloaded/ebin/zlib.beam
Binary files differ
diff --git a/erts/preloaded/src/Makefile b/erts/preloaded/src/Makefile
index d5f5ba3c37..a224b6a5d4 100644
--- a/erts/preloaded/src/Makefile
+++ b/erts/preloaded/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2008-2010. All Rights Reserved.
+# Copyright Ericsson AB 2008-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -40,7 +40,8 @@ PRE_LOADED_MODULES = \
zlib \
prim_zip \
otp_ring0 \
- erlang
+ erlang \
+ erts_internal
RELSYSDIR = $(RELEASE_PATH)/lib/erts-$(VSN)
# not $(RELEASE_PATH)/erts-$(VSN)/preloaded
diff --git a/erts/preloaded/src/erl_prim_loader.erl b/erts/preloaded/src/erl_prim_loader.erl
index 14a7a2bf20..d36fdeba3f 100644
--- a/erts/preloaded/src/erl_prim_loader.erl
+++ b/erts/preloaded/src/erl_prim_loader.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -49,7 +49,7 @@
prim_read_file_info/2, prim_get_cwd/2]).
%% Used by escript and code
--export([set_primary_archive/3, release_archives/0]).
+-export([set_primary_archive/4, release_archives/0]).
-include_lib("kernel/include/file.hrl").
@@ -222,15 +222,16 @@ get_cwd(Drive) ->
check_file_result(get_cwd, Drive, request({get_cwd,[Drive]})).
-spec set_primary_archive(File :: string() | 'undefined',
- ArchiveBin :: binary() | 'undefined',
- FileInfo :: #file_info{} | 'undefined')
+ ArchiveBin :: binary() | 'undefined',
+ FileInfo :: #file_info{} | 'undefined',
+ ParserFun :: fun())
-> {ok, [string()]} | {error,_}.
-set_primary_archive(undefined, undefined, undefined) ->
- request({set_primary_archive, undefined, undefined, undefined});
-set_primary_archive(File, ArchiveBin, FileInfo)
+set_primary_archive(undefined, undefined, undefined, ParserFun) ->
+ request({set_primary_archive, undefined, undefined, undefined, ParserFun});
+set_primary_archive(File, ArchiveBin, FileInfo, ParserFun)
when is_list(File), is_binary(ArchiveBin), is_record(FileInfo, file_info) ->
- request({set_primary_archive, File, ArchiveBin, FileInfo}).
+ request({set_primary_archive, File, ArchiveBin, FileInfo, ParserFun}).
-spec release_archives() -> 'ok' | {'error', _}.
@@ -318,8 +319,11 @@ loop(State, Parent, Paths) ->
{get_cwd,[_]=Args} ->
{Res,State1} = handle_get_cwd(State, Args),
{Res,State1,Paths};
- {set_primary_archive,File,Bin,FileInfo} ->
- {Res,State1} = handle_set_primary_archive(State, File, Bin, FileInfo),
+ {set_primary_archive,File,ArchiveBin,FileInfo,ParserFun} ->
+ {Res,State1} =
+ handle_set_primary_archive(State, File,
+ ArchiveBin, FileInfo,
+ ParserFun),
{Res,State1,Paths};
release_archives ->
{Res,State1} = handle_release_archives(State),
@@ -359,8 +363,8 @@ handle_get_file(State = #state{loader = efile}, Paths, File) ->
handle_get_file(State = #state{loader = inet}, Paths, File) ->
?SAFE2(inet_get_file_from_port(State, File, Paths), State).
-handle_set_primary_archive(State= #state{loader = efile}, File, Bin, FileInfo) ->
- ?SAFE2(efile_set_primary_archive(State, File, Bin, FileInfo), State).
+handle_set_primary_archive(State= #state{loader = efile}, File, ArchiveBin, FileInfo, ParserFun) ->
+ ?SAFE2(efile_set_primary_archive(State, File, ArchiveBin, FileInfo, ParserFun), State).
handle_release_archives(State= #state{loader = efile}) ->
?SAFE2(efile_release_archives(State), State).
@@ -484,8 +488,10 @@ efile_get_file_from_port3(State, File, [P | Paths]) ->
efile_get_file_from_port3(State, _File, []) ->
{{error,enoent},State}.
-efile_set_primary_archive(#state{prim_state = PS} = State, File, Bin, FileInfo) ->
- {Res, PS2} = prim_set_primary_archive(PS, File, Bin, FileInfo),
+efile_set_primary_archive(#state{prim_state = PS} = State, File,
+ ArchiveBin, FileInfo, ParserFun) ->
+ {Res, PS2} = prim_set_primary_archive(PS, File, ArchiveBin,
+ FileInfo, ParserFun),
{Res,State#state{prim_state = PS2}}.
efile_release_archives(#state{prim_state = PS} = State) ->
@@ -791,7 +797,7 @@ prim_release_archives(PS) ->
prim_do_release_archives(PS, [{ArchiveFile, DictVal} | KeyVals], Acc) ->
Res =
case DictVal of
- {primary, _PrimZip, _FI} ->
+ {primary, _PrimZip, _FI, _ParserFun} ->
ok; % Keep primary archive
{Cache, _FI} ->
debug(PS, {release, cache, ArchiveFile}),
@@ -809,7 +815,7 @@ prim_do_release_archives(PS, [], []) ->
prim_do_release_archives(PS, [], Errors) ->
{{error, Errors}, PS#prim_state{primary_archive = undefined}}.
-prim_set_primary_archive(PS, undefined, undefined, undefined) ->
+prim_set_primary_archive(PS, undefined, undefined, undefined, _ParserFun) ->
debug(PS, {set_primary_archive, clean}),
case PS#prim_state.primary_archive of
undefined ->
@@ -817,48 +823,40 @@ prim_set_primary_archive(PS, undefined, undefined, undefined) ->
debug(PS, {return, Res}),
{Res, PS};
ArchiveFile ->
- {primary, PrimZip, _FI} = erase(ArchiveFile),
+ {primary, PrimZip, _FI, _ParserFun2} = erase(ArchiveFile),
ok = prim_zip:close(PrimZip),
PS2 = PS#prim_state{primary_archive = undefined},
Res = {ok, []},
debug(PS2, {return, Res}),
{Res, PS2}
end;
-prim_set_primary_archive(PS, ArchiveFile, ArchiveBin, #file_info{} = FileInfo)
- when is_list(ArchiveFile), is_binary(ArchiveBin) ->
+
+prim_set_primary_archive(PS, ArchiveFile0, ArchiveBin,
+ #file_info{} = FileInfo, ParserFun)
+ when is_list(ArchiveFile0), is_binary(ArchiveBin) ->
%% Try the archive file
- debug(PS, {set_primary_archive, ArchiveFile, byte_size(ArchiveBin)}),
+ debug(PS, {set_primary_archive, ArchiveFile0, byte_size(ArchiveBin)}),
+ ArchiveFile = real_path(absname(ArchiveFile0)),
{Res3, PS3} =
case PS#prim_state.primary_archive of
undefined ->
- Fun =
- fun({Funny, _GI, _GB}, A) ->
- case Funny of
- ["", "nibe", RevApp] -> % Reverse ebin
- %% Collect ebin directories in archive
- Ebin = reverse(RevApp) ++ "/ebin",
- {true, [Ebin | A]};
- _ ->
- {true, A}
- end
- end,
- Ebins0 = [ArchiveFile],
- case open_archive({ArchiveFile, ArchiveBin}, FileInfo, Ebins0, Fun) of
- {ok, PrimZip, {RevEbins, FI, _}} ->
- Ebins = reverse(RevEbins),
+ case load_prim_archive(ArchiveFile, ArchiveBin, FileInfo) of
+ {ok, PrimZip, FI, Ebins} ->
debug(PS, {set_primary_archive, Ebins}),
- put(ArchiveFile, {primary, PrimZip, FI}),
- {{ok, Ebins}, PS#prim_state{primary_archive = ArchiveFile}};
+ put(ArchiveFile, {primary, PrimZip, FI, ParserFun}),
+ {{ok, Ebins},
+ PS#prim_state{primary_archive = ArchiveFile}};
Error ->
debug(PS, {set_primary_archive, Error}),
{Error, PS}
end;
OldArchiveFile ->
debug(PS, {set_primary_archive, clean}),
- {primary, PrimZip, _FI} = erase(OldArchiveFile),
+ {primary, PrimZip, _FI, _ParserFun} = erase(OldArchiveFile),
ok = prim_zip:close(PrimZip),
PS2 = PS#prim_state{primary_archive = undefined},
- prim_set_primary_archive(PS2, ArchiveFile, ArchiveBin, FileInfo)
+ prim_set_primary_archive(PS2, ArchiveFile, ArchiveBin,
+ FileInfo, ParserFun)
end,
debug(PS3, {return, Res3}),
{Res3, PS3}.
@@ -873,11 +871,11 @@ prim_get_file(PS, File) ->
{Res, PS};
{archive, ArchiveFile, FileInArchive} ->
debug(PS, {archive_get_file, ArchiveFile, FileInArchive}),
- FunnyFile = funny_split(FileInArchive, $/),
+ FileComponents = path_split(FileInArchive),
Fun =
- fun({Funny, _GetInfo, GetBin}, Acc) ->
+ fun({Components, _GetInfo, GetBin}, Acc) ->
if
- Funny =:= FunnyFile ->
+ Components =:= FileComponents ->
{false, {ok, GetBin()}};
true ->
{true, Acc}
@@ -900,11 +898,11 @@ prim_list_dir(PS, Dir) ->
{Res, PS};
{archive, ArchiveFile, FileInArchive} ->
debug(PS, {archive_list_dir, ArchiveFile, FileInArchive}),
- FunnyDir = funny_split(FileInArchive, $/),
+ DirComponents = path_split(FileInArchive),
Fun =
- fun({Funny, _GetInfo, _GetBin}, {Status, Names} = Acc) ->
- case Funny of
- [RevName | FD] when FD =:= FunnyDir ->
+ fun({Components, _GetInfo, _GetBin}, {Status, Names} = Acc) ->
+ case Components of
+ [RevName | DC] when DC =:= DirComponents ->
case RevName of
"" ->
%% The listed directory
@@ -914,16 +912,16 @@ prim_list_dir(PS, Dir) ->
Name = reverse(RevName),
{true, {Status, [Name | Names]}}
end;
- ["", RevName | FD] when FD =:= FunnyDir ->
+ ["", RevName | DC] when DC =:= DirComponents ->
%% Directory
Name = reverse(RevName),
{true, {Status, [Name | Names]}};
- [RevName] when FunnyDir =:= [""] ->
- %% Top file
+ [RevName] when DirComponents =:= [""] ->
+ %% File in top directory
Name = reverse(RevName),
{true, {ok, [Name | Names]}};
- ["", RevName] when FunnyDir =:= [""] ->
- %% Top file
+ ["", RevName] when DirComponents =:= [""] ->
+ %% Directory in top directory
Name = reverse(RevName),
{true, {ok, [Name | Names]}};
_ ->
@@ -962,15 +960,14 @@ prim_read_file_info(PS, File) ->
end;
{archive, ArchiveFile, FileInArchive} ->
debug(PS, {archive_read_file_info, File}),
- FunnyFile = funny_split(FileInArchive, $/),
+ FileComponents = path_split(FileInArchive),
Fun =
- fun({Funny, GetInfo, _GetBin}, Acc) ->
- case Funny of
- [H | T] when H =:= "",
- T =:= FunnyFile ->
+ fun({Components, GetInfo, _GetBin}, Acc) ->
+ case Components of
+ ["" | F] when F =:= FileComponents ->
%% Directory
{false, {ok, GetInfo()}};
- F when F =:= FunnyFile ->
+ F when F =:= FileComponents ->
%% Plain file
{false, {ok, GetInfo()}};
_ ->
@@ -1011,7 +1008,7 @@ apply_archive(PS, Fun, Acc, Archive) ->
%% put(Archive, {Error, FI}),
{Error, PS}
end;
- {primary, PrimZip, FI} ->
+ {primary, PrimZip, FI, ParserFun} ->
case prim_file:read_file_info(Archive) of
{ok, FI2}
when FI#file_info.mtime =:= FI2#file_info.mtime ->
@@ -1022,6 +1019,16 @@ apply_archive(PS, Fun, Acc, Archive) ->
debug(PS, {primary, Error}),
{Error, PS}
end;
+ {ok, FI2} ->
+ ok = clear_cache(Archive, {ok, PrimZip}),
+ case load_prim_archive(Archive, FI2, ParserFun) of
+ {ok, PrimZip2, FI3, _Ebins} ->
+ debug(PS, {cache, {update, Archive}}),
+ put(Archive, {primary, PrimZip2, FI3, ParserFun});
+ Error2 ->
+ debug(PS, {cache, {clear, Error2}})
+ end,
+ apply_archive(PS, Fun, Acc, Archive);
Error ->
debug(PS, {cache, {clear, Error}}),
clear_cache(Archive, {ok, PrimZip}),
@@ -1063,50 +1070,69 @@ open_archive(Archive, Acc, Fun) ->
{error, Reason}
end.
+%% Open the given archive and iterate through all files with an own
+%% wrapper fun in order to identify each file as a component list as
+%% returned from path_split/1.
+%%
+%% In the archive (zip) file, directory elements might or might not be
+%% present. To ensure consistency, a directory element is added if it
+%% does not already exist (ensure_virtual_dirs/6). NOTE that there will
+%% be no such directory element for the top directory of the archive.
open_archive(Archive, FileInfo, Acc, Fun) ->
FakeFI = FileInfo#file_info{type = directory},
Wrapper =
- fun({N, GI, GB}, {A, I, FunnyDirs}) -> % Full iteration at open
- Funny = funny_split(N, $/),
- FunnyDirs2 =
- case Funny of
- ["" | FunnyDir] ->
- [FunnyDir | FunnyDirs];
+ fun({N, GI, GB}, {A, I, Dirs}) ->
+ Components = path_split(N),
+ Dirs2 =
+ case Components of
+ ["" | Dir] ->
+ %% This is a directory
+ [Dir | Dirs];
_ ->
- FunnyDirs
+ %% This is a regular file
+ Dirs
end,
- {Includes, FunnyDirs3, A2} =
- ensure_virtual_dirs(Funny, Fun, FakeFI, [{true, Funny}], FunnyDirs2, A),
- {_Continue, A3} = Fun({Funny, GI, GB}, A2),
- {true, Includes, {A3, I, FunnyDirs3}}
+ {Includes, Dirs3, A2} =
+ ensure_virtual_dirs(Components, Fun, FakeFI,
+ [{true, Components}], Dirs2, A),
+ {_Continue, A3} = Fun({Components, GI, GB}, A2),
+ {true, Includes, {A3, I, Dirs3}}
end,
prim_zip:open(Wrapper, {Acc, FakeFI, []}, Archive).
-ensure_virtual_dirs(Funny, Fun, FakeFI, Includes, FunnyDirs, Acc) ->
- case Funny of
- [_ | FunnyDir] ->
- case lists:member(FunnyDir, FunnyDirs) of % BIF
+ensure_virtual_dirs(Components, Fun, FakeFI, Includes, Dirs, Acc) ->
+ case Components of
+ [_] ->
+ %% Don't add virtual dir for top directory
+ {Includes, Dirs, Acc};
+ [_ | Dir] ->
+ case lists:member(Dir, Dirs) of % BIF
false ->
+ %% The directory does not yet exist - add it
GetInfo = fun() -> FakeFI end,
GetBin = fun() -> <<>> end,
- VirtualDir = ["" | FunnyDir],
+ VirtualDir = ["" | Dir],
Includes2 = [{true, VirtualDir, GetInfo, GetBin} | Includes],
- FunnyDirs2 = [FunnyDir | FunnyDirs],
- {I, F, Acc2} = ensure_virtual_dirs(FunnyDir, Fun, FakeFI, Includes2, FunnyDirs2, Acc),
+ Dirs2 = [Dir | Dirs],
+
+ %% Recursively ensure dir elements on all levels
+ {I, F, Acc2} = ensure_virtual_dirs(Dir, Fun, FakeFI,
+ Includes2, Dirs2, Acc),
+
{_Continue, Acc3} = Fun({VirtualDir, GetInfo, GetBin}, Acc2),
{I, F, Acc3};
true ->
- {reverse(Includes), FunnyDirs, Acc}
- end;
- [] ->
- {reverse(Includes), FunnyDirs, Acc}
+ %% The directory element does already exist
+ %% Recursivly ensure dir elements on all levels
+ ensure_virtual_dirs(Dir,Fun,FakeFI,Includes,Dirs,Acc)
+ end
end.
foldl_archive(PrimZip, Acc, Fun) ->
Wrapper =
- fun({Funny, GI, GB}, A) ->
+ fun({Components, GI, GB}, A) ->
%% Allow partial iteration at foldl
- {Continue, A2} = Fun({Funny, GI, GB}, A),
+ {Continue, A2} = Fun({Components, GI, GB}, A),
{Continue, true, A2}
end,
prim_zip:foldl(Wrapper, Acc, PrimZip).
@@ -1202,17 +1228,32 @@ reverse([A, B]) ->
reverse([A, B | L]) ->
lists:reverse(L, [B, A]). % BIF
-%% Returns all lists in reverse order
-funny_split(List, Sep) ->
- funny_split(List, Sep, [], []).
-
-funny_split([Sep | Tail], Sep, Path, Paths) ->
- funny_split(Tail, Sep, [], [Path | Paths]);
-funny_split([Head | Tail], Sep, Path, Paths) ->
- funny_split(Tail, Sep, [Head | Path], Paths);
-funny_split([], _Sep, Path, Paths) ->
+%% Returns a reversed list of path components, each component itself a
+%% reversed list (string), e.g.
+%% /path/to/file -> ["elif","ot","htap",""]
+%% /path/to/dir/ -> ["","rid","ot","htap",""]
+%% Note the "" marking leading and trailing / (slash).
+path_split(List) ->
+ path_split(List, [], []).
+
+path_split([$/ | Tail], Path, Paths) ->
+ path_split(Tail, [], [Path | Paths]);
+path_split([Head | Tail], Path, Paths) ->
+ path_split(Tail, [Head | Path], Paths);
+path_split([], Path, Paths) ->
[Path | Paths].
+%% The opposite of path_split/1
+path_join(Paths) ->
+ path_join(Paths,[]).
+
+path_join([""],Acc) ->
+ Acc;
+path_join([Path],Acc) ->
+ reverse(Path) ++ Acc;
+path_join([Path|Paths],Acc) ->
+ path_join(Paths,"/" ++ reverse(Path) ++ Acc).
+
name_split(ArchiveFile, File0) ->
File = absname(File0),
do_name_split(ArchiveFile, File).
@@ -1235,26 +1276,22 @@ do_name_split(undefined, File) ->
%% False match. Assume plain file
{file, File}
end;
-do_name_split(ArchiveFile0, File) ->
+do_name_split(ArchiveFile, File) ->
%% Look first in primary archive
- ArchiveFile = absname(ArchiveFile0),
- case string_match(File, ArchiveFile, []) of
+ case string_match(real_path(File), ArchiveFile, []) of
no_match ->
%% Archive or plain file
do_name_split(undefined, File);
{match, _RevPrimArchiveFile, FileInArchive} ->
%% Primary archive
- case FileInArchive of
- [$/ | FileInArchive2] ->
- {archive, ArchiveFile, FileInArchive2};
- _ ->
- {archive, ArchiveFile, FileInArchive}
- end
+ {archive, ArchiveFile, FileInArchive}
end.
string_match([Char | File], [Char | Archive], RevTop) ->
string_match(File, Archive, [Char | RevTop]);
-string_match(File, [], RevTop) ->
+string_match([] = File, [], RevTop) ->
+ {match, RevTop, File};
+string_match([$/ | File], [], RevTop) ->
{match, RevTop, File};
string_match(_File, _Archive, _RevTop) ->
no_match.
@@ -1314,14 +1351,14 @@ absname(Name) ->
case prim_file:get_cwd() of
{ok, Cwd} ->
Cwd ++ "/" ++ Name2;
- {error, _} ->
+ {error, _} ->
Name2
end;
volumerelative ->
case prim_file:get_cwd() of
{ok, Cwd} ->
absname_vr(Name2, Cwd);
- {error, _} ->
+ {error, _} ->
Name2
end
end.
@@ -1348,14 +1385,7 @@ pathtype(Name) when is_list(Name) ->
{unix, _} ->
unix_pathtype(Name);
{win32, _} ->
- win32_pathtype(Name);
- {vxworks, _} ->
- case vxworks_first(Name) of
- {device, _Rest, _Dev} ->
- absolute;
- _ ->
- relative
- end
+ win32_pathtype(Name)
end.
unix_pathtype(Name) ->
@@ -1380,58 +1410,18 @@ win32_pathtype(Name) ->
win32_pathtype([Char | List++Rest]);
[$/, $/|_] ->
absolute;
- [$\\, $/|_] ->
- absolute;
- [$/, $\\|_] ->
- absolute;
- [$\\, $\\|_] ->
- absolute;
[$/|_] ->
volumerelative;
- [$\\|_] ->
- volumerelative;
[C1, C2, List | Rest] when is_list(List) ->
- pathtype([C1, C2|List ++ Rest]);
+ win32_pathtype([C1, C2|List ++ Rest]);
[_Letter, $:, $/|_] ->
absolute;
- [_Letter, $:, $\\|_] ->
- absolute;
[_Letter, $:|_] ->
volumerelative;
_ ->
relative
end.
-vxworks_first(Name) ->
- case Name of
- [] ->
- {not_device, [], []};
- [$/ | T] ->
- vxworks_first2(device, T, [$/]);
- [$\\ | T] ->
- vxworks_first2(device, T, [$/]);
- [H | T] when is_list(H) ->
- vxworks_first(H ++ T);
- [H | T] ->
- vxworks_first2(not_device, T, [H])
- end.
-
-vxworks_first2(Devicep, Name, FirstComp) ->
- case Name of
- [] ->
- {Devicep, [], FirstComp};
- [$/ |T ] ->
- {Devicep, [$/ | T], FirstComp};
- [$\\ | T] ->
- {Devicep, [$/ | T], FirstComp};
- [$: | T]->
- {device, T, [$: | FirstComp]};
- [H | T] when is_list(H) ->
- vxworks_first2(Devicep, H ++ T, FirstComp);
- [H | T] ->
- vxworks_first2(Devicep, T, [H | FirstComp])
- end.
-
normalize(Name, Acc) ->
case Name of
[List | Rest] when is_list(List) ->
@@ -1445,3 +1435,70 @@ normalize(Name, Acc) ->
[] ->
reverse(Acc)
end.
+
+%% Remove .. and . from the path, e.g.
+%% /path/./to/this/../file -> /path/to/file
+%% This includes resolving symlinks.
+%%
+%% This is done to ensure that paths are totally normalized before
+%% comparing to find out if a file is inside the primary archive or
+%% not.
+real_path(Name) ->
+ real_path(Name,reverse(path_split(Name)),[],[]).
+
+real_path(_Name,[],Acc,_Links) ->
+ path_join(Acc);
+real_path(Name,["."|Paths],Acc,Links) ->
+ real_path(Name,Paths,Acc,Links);
+real_path(Name,[".."|Paths],[""]=Acc,Links) ->
+ %% /.. -> / (can't get higher than root)
+ real_path(Name,Paths,Acc,Links);
+real_path(Name,[".."|Paths],[Prev|Acc],Links) when Prev=/=".." ->
+ real_path(Name,Paths,Acc,Links);
+real_path(Name,[Path|Paths],Acc,Links) ->
+ This = [Path|Acc],
+ ThisFile = path_join(This),
+ case lists:member(ThisFile,Links) of
+ true -> % circular!!
+ Name;
+ false ->
+ case prim_file:read_link(ThisFile) of
+ {ok,Link} ->
+ case reverse(path_split(Link)) of
+ [""|_] = LinkPaths ->
+ real_path(Name,LinkPaths++Paths,[],[ThisFile|Links]);
+ LinkPaths ->
+ real_path(Name,LinkPaths++Paths,Acc,[ThisFile|Links])
+ end;
+ _ ->
+ real_path(Name,Paths,This,Links)
+ end
+ end.
+
+load_prim_archive(ArchiveFile, ArchiveBin, #file_info{}=FileInfo) ->
+ Fun = fun({Components, _GI, _GB}, A) ->
+ case Components of
+ ["", "nibe", RevApp] -> % Reverse ebin
+ %% Collect ebin directories in archive
+ Ebin = lists:reverse(RevApp, "/ebin"),
+ {true, [Ebin | A]};
+ _ ->
+ {true, A}
+ end
+ end,
+ Ebins0 = [ArchiveFile],
+ case open_archive({ArchiveFile, ArchiveBin}, FileInfo,
+ Ebins0, Fun) of
+ {ok, PrimZip, {RevEbins, FI, _}} ->
+ Ebins = reverse(RevEbins),
+ {ok, PrimZip, FI, Ebins};
+ Error ->
+ Error
+ end;
+load_prim_archive(ArchiveFile, FileInfo, ParserFun) ->
+ case ParserFun(ArchiveFile) of
+ {ok, ArchiveBin} ->
+ load_prim_archive(ArchiveFile, ArchiveBin, FileInfo);
+ Error ->
+ Error
+ end.
diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl
index 646acf5798..50546b1856 100644
--- a/erts/preloaded/src/erlang.erl
+++ b/erts/preloaded/src/erlang.erl
@@ -79,15 +79,18 @@
-export([bump_reductions/1, byte_size/1, call_on_load_function/1]).
-export([cancel_timer/1, check_old_code/1, check_process_code/2, crc32/1]).
-export([crc32/2, crc32_combine/3, date/0, decode_packet/3]).
+-export([delete_element/2]).
-export([delete_module/1, demonitor/1, demonitor/2, display/1]).
-export([display_nl/0, display_string/1, dist_exit/3, erase/0, erase/1]).
-export([error/1, error/2, exit/1, exit/2, external_size/1]).
-export([external_size/2, finish_after_on_load/2, finish_loading/1, float/1]).
--export([float_to_list/1, fun_info/2, fun_to_list/1, function_exported/3]).
+-export([float_to_list/1, float_to_list/2]).
+-export([fun_info/2, fun_to_list/1, function_exported/3]).
-export([garbage_collect/0, garbage_collect/1]).
-export([garbage_collect_message_area/0, get/0, get/1, get_keys/1]).
-export([get_module_info/1, get_stacktrace/0, group_leader/0]).
-export([group_leader/2, halt/0, halt/1, halt/2, hash/2, hibernate/3]).
+-export([insert_element/3]).
-export([integer_to_list/1, iolist_size/1, iolist_to_binary/1]).
-export([is_alive/0, is_builtin/3, is_process_alive/1, length/1, link/1]).
-export([list_to_atom/1, list_to_binary/1, list_to_bitstr/1]).
@@ -529,6 +532,14 @@ date() ->
decode_packet(_Type, _Bin, _Options) ->
erlang:nif_error(undefined).
+%% delete_element/2
+-spec erlang:delete_element(Index, Tuple1) -> Tuple2 when
+ Index :: pos_integer(),
+ Tuple1 :: tuple(),
+ Tuple2 :: tuple().
+delete_element(_Index, _Tuple1) ->
+ erlang:nif_error(undefined).
+
%% delete_module/1
-spec delete_module(Module) -> true | undefined when
Module :: module().
@@ -701,6 +712,16 @@ float(_Number) ->
float_to_list(_Float) ->
erlang:nif_error(undefined).
+%% float_to_list/2
+-spec float_to_list(Float, Options) -> string() when
+ Float :: float(),
+ Options :: [Option],
+ Option :: {decimals, non_neg_integer()} |
+ {scientific, non_neg_integer()} |
+ compact.
+float_to_list(_Float, _Options) ->
+ erlang:nif_error(undefined).
+
%% fun_info/2
-spec erlang:fun_info(Fun, Item) -> {Item, Info} when
Fun :: function(),
@@ -820,6 +841,15 @@ hash(_Term, _Range) ->
hibernate(_Module, _Function, _Args) ->
erlang:nif_error(undefined).
+%% insert_element/3
+-spec erlang:insert_element(Index, Tuple1, Term) -> Tuple2 when
+ Index :: pos_integer(),
+ Tuple1 :: tuple(),
+ Tuple2 :: tuple(),
+ Term :: term().
+insert_element(_Index, _Tuple1, _Term) ->
+ erlang:nif_error(undefined).
+
%% integer_to_list/1
-spec integer_to_list(Integer) -> string() when
Integer :: integer().
@@ -1075,57 +1105,6 @@ phash2(_Term, _Range) ->
pid_to_list(_Pid) ->
erlang:nif_error(undefined).
-%% port_close/1
--spec port_close(Port) -> true when
- Port :: port() | atom().
-port_close(_Port) ->
- erlang:nif_error(undefined).
-
-%% port_command/2
--spec port_command(Port, Data) -> true when
- Port :: port() | atom(),
- Data :: iodata().
-port_command(_Port, _Data) ->
- erlang:nif_error(undefined).
-
-%% port_command/3
--spec port_command(Port, Data, OptionList) -> boolean() when
- Port :: port() | atom(),
- Data :: iodata(),
- OptionList :: [Option],
- Option :: force | nosuspend.
-port_command(_Port, _Data, _OptionList) ->
- erlang:nif_error(undefined).
-
-%% port_connect/2
--spec port_connect(Port, Pid) -> true when
- Port :: port() | atom(),
- Pid :: pid().
-port_connect(_Port, _Pid) ->
- erlang:nif_error(undefined).
-
-%% port_control/3
--spec port_control(Port, Operation, Data) -> Res when
- Port :: port() | atom(),
- Operation :: integer(),
- Data :: iodata(),
- Res :: string() | binary().
-port_control(_Port, _Operation, _Data) ->
- erlang:nif_error(undefined).
-
-%% port_get_data/1
--spec erlang:port_get_data(P1) -> term() when
- P1 :: port() | atom().
-port_get_data(_P1) ->
- erlang:nif_error(undefined).
-
-%% port_set_data/2
--spec erlang:port_set_data(P1, P2) -> true when
- P1 :: port() | atom(),
- P2 :: term().
-port_set_data(_P1, _P2) ->
- erlang:nif_error(undefined).
-
%% port_to_list/1
-spec erlang:port_to_list(Port) -> string() when
Port :: port().
@@ -1699,60 +1678,12 @@ nodes(_Arg) ->
| in
| out
| binary
- | eof.
+ | eof
+ | {parallelism, Boolean :: boolean()}
+ | hide.
open_port(_PortName,_PortSettings) ->
erlang:nif_error(undefined).
-%% Shadowed by erl_bif_types: erlang:port_call/2
--spec erlang:port_call(Port, Data) -> term() when
- Port :: port() | atom(),
- Data :: term().
-port_call(_Port, _Data) ->
- erlang:nif_error(undefined).
-
-%% Shadowed by erl_bif_types: erlang:port_call/3
--spec erlang:port_call(Port, Operation, Data) -> term() when
- Port :: port() | atom(),
- Operation :: integer(),
- Data :: term().
-port_call(_Port, _Operation, _Data) ->
- erlang:nif_error(undefined).
-
--type port_info_item() ::
- registered_name |
- id |
- connected |
- links |
- name |
- input |
- output |
- os_pid.
-
--type port_info_result_item() ::
- {registered_name, RegName :: atom()} |
- {id, Index :: non_neg_integer()} |
- {connected, Pid :: pid()} |
- {links, Pids :: [pid()]} |
- {name, String :: string()} |
- {input, Bytes :: non_neg_integer()} |
- {output, Bytes :: non_neg_integer()} |
- {os_pid, OsPid :: non_neg_integer() | 'undefined'}.
-
-%% Shadowed by erl_bif_types: erlang:port_info/1
--spec erlang:port_info(Port) -> Result when
- Port :: port() | atom(),
- Result :: [port_info_result_item()] | undefined.
-port_info(_Result) ->
- erlang:nif_error(undefined).
-
-%% Shadowed by erl_bif_types: erlang:port_info/2
--spec erlang:port_info(Port, Item) -> Result when
- Port :: port() | atom(),
- Item :: port_info_item(),
- Result :: port_info_result_item() | undefined.
-port_info(_Result, _Item) ->
- erlang:nif_error(undefined).
-
-type priority_level() ::
low | normal | high | max.
@@ -1836,7 +1767,7 @@ process_flag(_Flag, _Value) ->
{group_leader, GroupLeader :: pid()} |
{heap_size, Size :: non_neg_integer()} |
{initial_call, mfa()} |
- {links, Pids :: [pid()]} |
+ {links, PidsAndPorts :: [pid() | port()]} |
{last_calls, false | (Calls :: [mfa()])} |
{memory, Size :: non_neg_integer()} |
{message_que_len, MessageQueueLen :: non_neg_integer()} |
@@ -2137,6 +2068,8 @@ tuple_to_list(_Tuple) ->
(multi_scheduling) -> disabled | blocked | enabled;
(multi_scheduling_blockers) -> [PID :: pid()];
(otp_release) -> string();
+ (port_count) -> non_neg_integer();
+ (port_limit) -> pos_integer();
(process_count) -> pos_integer();
(process_limit) -> pos_integer();
(procs) -> binary();
@@ -2530,6 +2463,216 @@ suspend_process(P) ->
end.
%%
+%% Port BIFs
+%%
+%% Currently all port BIFs calls the corresponding
+%% erts_internal:port_*() native function which perform
+%% most of the actual work. These native functions should
+%% *never* be called directly by other functionality. The
+%% native functions may be changed, or removed without any
+%% notice whatsoever!
+%%
+%% IMPORTANT NOTE:
+%% When the erts_internal:port_*() native functions return
+%% a reference, they have also internally prepared the
+%% message queue of the caller for a receive that will
+%% unconditionally wait for a message containing this
+%% reference. If the erlang code calling these native
+%% functions do not do this, subsequent receives will not
+%% work as expected! That is, it is of *vital importance*
+%% that the receive is performed as described above!
+%%
+
+-spec port_command(Port, Data) -> 'true' when
+ Port :: port() | atom(),
+ Data :: iodata().
+
+port_command(Port, Data) ->
+ case case erts_internal:port_command(Port, Data, []) of
+ Ref when erlang:is_reference(Ref) -> receive {Ref, Res} -> Res end;
+ Res -> Res
+ end of
+ true -> true;
+ Error -> erlang:error(Error, [Port, Data])
+ end.
+
+-spec port_command(Port, Data, OptionList) -> boolean() when
+ Port :: port() | atom(),
+ Data :: iodata(),
+ Option :: force | nosuspend,
+ OptionList :: [Option].
+
+port_command(Port, Data, Flags) ->
+ case case erts_internal:port_command(Port, Data, Flags) of
+ Ref when erlang:is_reference(Ref) -> receive {Ref, Res} -> Res end;
+ Res -> Res
+ end of
+ Bool when Bool == true; Bool == false -> Bool;
+ Error -> erlang:error(Error, [Port, Data, Flags])
+ end.
+
+-spec port_connect(Port, Pid) -> 'true' when
+ Port :: port() | atom(),
+ Pid :: pid().
+
+port_connect(Port, Pid) ->
+ case case erts_internal:port_connect(Port, Pid) of
+ Ref when erlang:is_reference(Ref) -> receive {Ref, Res} -> Res end;
+ Res -> Res
+ end of
+ true -> true;
+ Error -> erlang:error(Error, [Port, Pid])
+ end.
+
+-spec port_close(Port) -> 'true' when
+ Port :: port() | atom().
+
+port_close(Port) ->
+ case case erts_internal:port_close(Port) of
+ Ref when erlang:is_reference(Ref) -> receive {Ref, Res} -> Res end;
+ Res -> Res
+ end of
+ true -> true;
+ Error -> erlang:error(Error, [Port])
+ end.
+
+-spec port_control(Port, Operation, Data) -> iodata() | binary() when
+ Port :: port() | atom(),
+ Operation :: integer(),
+ Data :: iodata().
+
+port_control(Port, Operation, Data) ->
+ case case erts_internal:port_control(Port, Operation, Data) of
+ Ref when erlang:is_reference(Ref) -> receive {Ref, Res} -> Res end;
+ Res -> Res
+ end of
+ badarg -> erlang:error(badarg, [Port, Operation, Data]);
+ Result -> Result
+ end.
+
+-spec erlang:port_call(Port, Data) -> term() when
+ Port :: port() | atom(),
+ Data :: term().
+
+port_call(Port, Data) ->
+ case case erts_internal:port_call(Port, 0, Data) of
+ Ref when erlang:is_reference(Ref) -> receive {Ref, Res} -> Res end;
+ Res -> Res
+ end of
+ {ok, Result} -> Result;
+ Error -> erlang:error(Error, [Port, Data])
+ end.
+
+-spec erlang:port_call(Port, Operation, Data) -> term() when
+ Port :: port() | atom(),
+ Operation :: integer(),
+ Data :: term().
+
+port_call(Port, Operation, Data) ->
+ case case erts_internal:port_call(Port, Operation, Data) of
+ Ref when erlang:is_reference(Ref) -> receive {Ref, Res} -> Res end;
+ Res -> Res
+ end of
+ {ok, Result} -> Result;
+ Error -> erlang:error(Error, [Port, Operation, Data])
+ end.
+
+-spec erlang:port_info(Port) -> Result when
+ Port :: port() | atom(),
+ ResultItem :: {registered_name, RegisteredName :: atom()}
+ | {id, Index :: non_neg_integer()}
+ | {connected, Pid :: pid()}
+ | {links, Pids :: [pid()]}
+ | {name, String :: string()}
+ | {input, Bytes :: non_neg_integer()}
+ | {output, Bytes :: non_neg_integer()}
+ | {os_pid, OsPid :: non_neg_integer() | 'undefined'},
+ Result :: [ResultItem] | 'undefined'.
+
+port_info(Port) ->
+ case case erts_internal:port_info(Port) of
+ Ref when erlang:is_reference(Ref) -> receive {Ref, Res} -> Res end;
+ Res -> Res
+ end of
+ badarg -> erlang:error(badarg, [Port]);
+ Result -> Result
+ end.
+
+-spec erlang:port_info(Port, connected) -> {connected, Pid} | 'undefined' when
+ Port :: port() | atom(),
+ Pid :: pid();
+ (Port, id) -> {id, Index} | 'undefined' when
+ Port :: port() | atom(),
+ Index :: non_neg_integer();
+ (Port, input) -> {input, Bytes} | 'undefined' when
+ Port :: port() | atom(),
+ Bytes :: non_neg_integer();
+ (Port, links) -> {links, Pids} | 'undefined' when
+ Port :: port() | atom(),
+ Pids :: [pid()];
+ (Port, locking) -> {locking, Locking} | 'undefined' when
+ Port :: port() | atom(),
+ Locking :: 'false' | 'port_level' | 'driver_level';
+ (Port, memory) -> {memory, Bytes} | 'undefined' when
+ Port :: port() | atom(),
+ Bytes :: non_neg_integer();
+ (Port, monitors) -> {monitors, Monitors} | 'undefined' when
+ Port :: port() | atom(),
+ Monitors :: [{process, pid()}];
+ (Port, name) -> {name, Name} | 'undefined' when
+ Port :: port() | atom(),
+ Name :: string();
+ (Port, os_pid) -> {os_pid, OsPid} | 'undefined' when
+ Port :: port() | atom(),
+ OsPid :: non_neg_integer() | 'undefined';
+ (Port, output) -> {output, Bytes} | 'undefined' when
+ Port :: port() | atom(),
+ Bytes :: non_neg_integer();
+ (Port, parallelism) -> {parallelism, Boolean} | 'undefined' when
+ Port :: port() | atom(),
+ Boolean :: boolean();
+ (Port, queue_size) -> {queue_size, Bytes} | 'undefined' when
+ Port :: port() | atom(),
+ Bytes :: non_neg_integer();
+ (Port, registered_name) -> {registered_name, RegisteredName} | [] | 'undefined' when
+ Port :: port() | atom(),
+ RegisteredName :: atom().
+
+port_info(Port, Item) ->
+ case case erts_internal:port_info(Port, Item) of
+ Ref when erlang:is_reference(Ref) -> receive {Ref, Res} -> Res end;
+ Res -> Res
+ end of
+ badarg -> erlang:error(badarg, [Port, Item]);
+ Result -> Result
+ end.
+
+-spec erlang:port_set_data(Port, Data) -> 'true' when
+ Port :: port() | atom(),
+ Data :: term().
+
+port_set_data(Port, Data) ->
+ case case erts_internal:port_set_data(Port, Data) of
+ Ref when erlang:is_reference(Ref) -> receive {Ref, Res} -> Res end;
+ Res -> Res
+ end of
+ badarg -> erlang:error(badarg, [Port, Data]);
+ Result -> Result
+ end.
+
+-spec erlang:port_get_data(Port) -> term() when
+ Port :: port() | atom().
+
+port_get_data(Port) ->
+ case case erts_internal:port_get_data(Port) of
+ Ref when erlang:is_reference(Ref) -> receive {Ref, Res} -> Res end;
+ Res -> Res
+ end of
+ {ok, Data} -> Data;
+ Error -> erlang:error(Error, [Port])
+ end.
+
+%%
%% If the emulator wants to perform a distributed command and
%% a connection is not established to the actual node the following
%% functions are called in order to set up the connection and then
diff --git a/erts/preloaded/src/erts_internal.erl b/erts/preloaded/src/erts_internal.erl
new file mode 100644
index 0000000000..f1c83f4518
--- /dev/null
+++ b/erts/preloaded/src/erts_internal.erl
@@ -0,0 +1,155 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+%% As the module name imply, this module is here for ERTS internal
+%% functionality. As an application programmer you should *never*
+%% call anything in this module directly. Functions exported by
+%% this module may change behaviour or be removed at any time
+%% without any notice whatsoever. Everything in this module is
+%% intentionally left undocumented, and should remain so.
+%%
+
+-module(erts_internal).
+
+-export([await_port_send_result/3]).
+
+-export([port_command/3, port_connect/2, port_close/1,
+ port_control/3, port_call/3, port_set_data/2, port_get_data/1,
+ port_info/1, port_info/2]).
+
+%%
+%% Await result of send to port
+%%
+
+await_port_send_result(Ref, Busy, Ok) ->
+ receive
+ {Ref, false} -> Busy;
+ {Ref, _} -> Ok
+ end.
+
+%%
+%% Statically linked port NIFs
+%%
+
+-spec erts_internal:port_command(Port, Data, OptionList) -> Result when
+ Port :: port() | atom(),
+ Data :: iodata(),
+ OptionList :: [Option],
+ Option :: force | nosuspend,
+ Result :: boolean() | reference() | badarg | notsup.
+port_command(_Port, _Data, _OptionList) ->
+ erlang:nif_error(undefined).
+
+-spec erts_internal:port_connect(Port, Pid) -> Result when
+ Port :: port() | atom(),
+ Pid :: pid(),
+ Result :: true | reference() | badarg.
+port_connect(_Port, _Pid) ->
+ erlang:nif_error(undefined).
+
+-spec erts_internal:port_close(Port) -> Result when
+ Port :: port() | atom(),
+ Result :: true | reference() | badarg.
+port_close(_Port) ->
+ erlang:nif_error(undefined).
+
+-spec erts_internal:port_control(Port, Operation, Data) -> Result when
+ Port :: port() | atom(),
+ Operation :: integer(),
+ Data :: iodata(),
+ Result :: string() | binary() | reference() | badarg.
+port_control(_Port, _Operation, _Data) ->
+ erlang:nif_error(undefined).
+
+-spec erts_internal:port_call(Port, Operation, Data) -> Result when
+ Port :: port() | atom(),
+ Operation :: integer(),
+ Data :: term(),
+ Result :: {ok, term()} | reference() | badarg.
+port_call(_Port, _Operation, _Data) ->
+ erlang:nif_error(undefined).
+
+-spec erts_internal:port_get_data(P1) -> Result when
+ P1 :: port() | atom(),
+ Result :: {ok, term()} | reference() | badarg.
+port_get_data(_P1) ->
+ erlang:nif_error(undefined).
+
+-spec erts_internal:port_set_data(P1, P2) -> Result when
+ P1 :: port() | atom(),
+ P2 :: term(),
+ Result :: true | reference() | badarg.
+port_set_data(_P1, _P2) ->
+ erlang:nif_error(undefined).
+
+-type port_info_1_result_item() ::
+ {registered_name, RegName :: atom()} |
+ {id, Index :: non_neg_integer()} |
+ {connected, Pid :: pid()} |
+ {links, Pids :: [pid()]} |
+ {name, String :: string()} |
+ {input, Bytes :: non_neg_integer()} |
+ {output, Bytes :: non_neg_integer()} |
+ {os_pid, OsPid :: non_neg_integer() | 'undefined'}.
+
+-spec erts_internal:port_info(Port) -> Result when
+ Port :: port() | atom(),
+ Result :: [port_info_1_result_item()] | undefined | reference() | badarg | [].
+port_info(_Result) ->
+ erlang:nif_error(undefined).
+
+-type port_info_2_item() ::
+ registered_name |
+ id |
+ connected |
+ links |
+ name |
+ input |
+ output |
+ os_pid |
+ monitors |
+ memory |
+ parallelism |
+ queue_size |
+ locking.
+
+-type port_info_2_result_item() ::
+ {registered_name, RegName :: atom()} |
+ [] | % No registered name
+ {id, Index :: non_neg_integer()} |
+ {connected, Pid :: pid()} |
+ {links, Pids :: [pid()]} |
+ {name, String :: string()} |
+ {input, Bytes :: non_neg_integer()} |
+ {output, Bytes :: non_neg_integer()} |
+ {os_pid, OsPid :: non_neg_integer() | 'undefined'} |
+ {monitors, Monitors :: [{process, pid()}]} |
+ {memory, MemSz :: non_neg_integer()} |
+ {parallelism, Boolean :: boolean()} |
+ {queue_size, QSz :: non_neg_integer()} |
+ {locking, Locking :: 'false' | 'port_level' | 'driver_level'}.
+
+-spec erts_internal:port_info(Port, Item) -> Result when
+ Port :: port() | atom(),
+ Item :: port_info_2_item(),
+ Result :: port_info_2_result_item() | undefined | reference() | badarg.
+
+port_info(_Result, _Item) ->
+ erlang:nif_error(undefined).
diff --git a/erts/preloaded/src/init.erl b/erts/preloaded/src/init.erl
index c9c434dea0..1d1087c7f2 100644
--- a/erts/preloaded/src/init.erl
+++ b/erts/preloaded/src/init.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -635,8 +635,16 @@ do_unload([M|Mods]) ->
catch erlang:purge_module(M),
do_unload(Mods);
do_unload([]) ->
+ purge_all_hipe_refs(),
ok.
+purge_all_hipe_refs() ->
+ case erlang:system_info(hipe_architecture) of
+ undefined -> ok;
+ _ -> hipe_bifs:remove_refs_from(all)
+ end.
+
+
sub([H|T],L) -> sub(T,del(H,L));
sub([],L) -> L.
@@ -1252,7 +1260,11 @@ get_arguments([]) ->
[].
to_strings([H|T]) when is_atom(H) -> [atom_to_list(H)|to_strings(T)];
-to_strings([H|T]) when is_binary(H) -> [binary_to_list(H)|to_strings(T)];
+to_strings([H|T]) when is_binary(H) -> [try
+ unicode:characters_to_list(H,file:native_name_encoding())
+ catch
+ _:_ -> binary_to_list(H)
+ end|to_strings(T)];
to_strings([]) -> [].
get_argument(Arg,Flags) ->
diff --git a/erts/preloaded/src/prim_file.erl b/erts/preloaded/src/prim_file.erl
index d66f0e09cc..c412b7faf2 100644
--- a/erts/preloaded/src/prim_file.erl
+++ b/erts/preloaded/src/prim_file.erl
@@ -27,7 +27,7 @@
%% Generic file contents operations
-export([open/2, close/1, datasync/1, sync/1, advise/4, position/2, truncate/1,
write/2, pwrite/2, pwrite/3, read/2, read_line/1, pread/2, pread/3,
- copy/3, sendfile/10]).
+ copy/3, sendfile/10, allocate/3]).
%% Specialized file operations
-export([open/1, open/3]).
@@ -100,6 +100,7 @@
-define(FILE_FDATASYNC, 30).
-define(FILE_ADVISE, 31).
-define(FILE_SENDFILE, 32).
+-define(FILE_ALLOCATE, 33).
%% Driver responses
-define(FILE_RESP_OK, 0).
@@ -293,6 +294,11 @@ advise(#file_descriptor{module = ?MODULE, data = {Port, _}},
end.
%% Returns {error, Reason} | ok.
+allocate(#file_descriptor{module = ?MODULE, data = {Port, _}}, Offset, Length) ->
+ Cmd = <<?FILE_ALLOCATE, Offset:64/signed, Length:64/signed>>,
+ drv_command(Port, Cmd).
+
+%% Returns {error, Reason} | ok.
write(#file_descriptor{module = ?MODULE, data = {Port, _}}, Bytes) ->
case drv_command_nt(Port, [?FILE_WRITE,erlang:dt_prepend_vm_tag_data(Bytes)],undefined) of
{ok, _Size} ->
@@ -479,7 +485,7 @@ position(#file_descriptor{module = ?MODULE, data = {Port, _}}, At) ->
{error, Reason}
end.
-%% Returns {error, Reaseon} | ok.
+%% Returns {error, Reason} | ok.
truncate(#file_descriptor{module = ?MODULE, data = {Port, _}}) ->
drv_command(Port, <<?FILE_TRUNCATE>>).
@@ -674,25 +680,7 @@ set_cwd(Dir) ->
set_cwd(Port, Dir) when is_port(Port) ->
set_cwd_int(Port, Dir).
-set_cwd_int(Port, Dir0) ->
- Dir =
- (catch
- case os:type() of
- vxworks ->
- %% chdir on vxworks doesn't support
- %% relative paths
- %% must call get_cwd from here and use
- %% absname/2, since
- %% absname/1 uses file:get_cwd ...
- case get_cwd_int(Port, 0) of
- {ok, AbsPath} ->
- filename:absname(Dir0, AbsPath);
- _Badcwd ->
- Dir0
- end;
- _Else ->
- Dir0
- end),
+set_cwd_int(Port, Dir) ->
%% Dir is now either a string or an EXIT tuple.
%% An EXIT tuple will fail in the following catch.
drv_command(Port, [?FILE_CHDIR, pathname(Dir)]).
diff --git a/erts/preloaded/src/prim_inet.erl b/erts/preloaded/src/prim_inet.erl
index 3c8a059269..21d23159f0 100644
--- a/erts/preloaded/src/prim_inet.erl
+++ b/erts/preloaded/src/prim_inet.erl
@@ -184,7 +184,7 @@ close_pend_loop(S, N) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
bind(S,IP,Port) when is_port(S), is_integer(Port), Port >= 0, Port =< 65535 ->
- case ctl_cmd(S,?INET_REQ_BIND,[?int16(Port),ip_to_bytes(IP)]) of
+ case ctl_cmd(S,?INET_REQ_BIND,enc_value(set, addr, {IP,Port})) of
{ok, [P1,P0]} -> {ok, ?u16(P1, P0)};
{error,_}=Error -> Error
end;
@@ -206,10 +206,10 @@ bindx(S, AddFlag, Addrs) ->
case getprotocol(S) of
sctp ->
%% Really multi-homed "bindx". Stringified args:
- %% [AddFlag, (Port, IP)+]:
+ %% [AddFlag, (AddrBytes see enc_value_2(addr,X))+]:
Args =
[?int8(AddFlag)|
- [[?int16(Port)|ip_to_bytes(IP)] ||
+ [enc_value(set, addr, {IP,Port}) ||
{IP, Port} <- Addrs]],
case ctl_cmd(S, ?SCTP_REQ_BINDX, Args) of
{ok,_} -> {ok, S};
@@ -1062,6 +1062,7 @@ enc_opt(multicast_ttl) -> ?UDP_OPT_MULTICAST_TTL;
enc_opt(multicast_loop) -> ?UDP_OPT_MULTICAST_LOOP;
enc_opt(add_membership) -> ?UDP_OPT_ADD_MEMBERSHIP;
enc_opt(drop_membership) -> ?UDP_OPT_DROP_MEMBERSHIP;
+enc_opt(ipv6_v6only) -> ?INET_OPT_IPV6_V6ONLY;
enc_opt(buffer) -> ?INET_LOPT_BUFFER;
enc_opt(header) -> ?INET_LOPT_HEADER;
enc_opt(active) -> ?INET_LOPT_ACTIVE;
@@ -1071,6 +1072,8 @@ enc_opt(deliver) -> ?INET_LOPT_DELIVER;
enc_opt(exit_on_close) -> ?INET_LOPT_EXITONCLOSE;
enc_opt(high_watermark) -> ?INET_LOPT_TCP_HIWTRMRK;
enc_opt(low_watermark) -> ?INET_LOPT_TCP_LOWTRMRK;
+enc_opt(high_msgq_watermark) -> ?INET_LOPT_TCP_MSGQ_HIWTRMRK;
+enc_opt(low_msgq_watermark) -> ?INET_LOPT_TCP_MSGQ_LOWTRMRK;
enc_opt(send_timeout) -> ?INET_LOPT_TCP_SEND_TIMEOUT;
enc_opt(send_timeout_close) -> ?INET_LOPT_TCP_SEND_TIMEOUT_CLOSE;
enc_opt(delay_send) -> ?INET_LOPT_TCP_DELAY_SEND;
@@ -1115,6 +1118,7 @@ dec_opt(?UDP_OPT_MULTICAST_TTL) -> multicast_ttl;
dec_opt(?UDP_OPT_MULTICAST_LOOP) -> multicast_loop;
dec_opt(?UDP_OPT_ADD_MEMBERSHIP) -> add_membership;
dec_opt(?UDP_OPT_DROP_MEMBERSHIP) -> drop_membership;
+dec_opt(?INET_OPT_IPV6_V6ONLY) -> ipv6_v6only;
dec_opt(?INET_LOPT_BUFFER) -> buffer;
dec_opt(?INET_LOPT_HEADER) -> header;
dec_opt(?INET_LOPT_ACTIVE) -> active;
@@ -1124,6 +1128,8 @@ dec_opt(?INET_LOPT_DELIVER) -> deliver;
dec_opt(?INET_LOPT_EXITONCLOSE) -> exit_on_close;
dec_opt(?INET_LOPT_TCP_HIWTRMRK) -> high_watermark;
dec_opt(?INET_LOPT_TCP_LOWTRMRK) -> low_watermark;
+dec_opt(?INET_LOPT_TCP_MSGQ_HIWTRMRK) -> high_msgq_watermark;
+dec_opt(?INET_LOPT_TCP_MSGQ_LOWTRMRK) -> low_msgq_watermark;
dec_opt(?INET_LOPT_TCP_SEND_TIMEOUT) -> send_timeout;
dec_opt(?INET_LOPT_TCP_SEND_TIMEOUT_CLOSE) -> send_timeout_close;
dec_opt(?INET_LOPT_TCP_DELAY_SEND) -> delay_send;
@@ -1178,6 +1184,7 @@ type_opt_1(recbuf) -> int;
type_opt_1(priority) -> int;
type_opt_1(tos) -> int;
type_opt_1(nodelay) -> bool;
+type_opt_1(ipv6_v6only) -> bool;
%% multicast
type_opt_1(multicast_ttl) -> int;
type_opt_1(multicast_loop) -> bool;
@@ -1218,6 +1225,8 @@ type_opt_1(deliver) ->
type_opt_1(exit_on_close) -> bool;
type_opt_1(low_watermark) -> int;
type_opt_1(high_watermark) -> int;
+type_opt_1(low_msgq_watermark) -> int;
+type_opt_1(high_msgq_watermark) -> int;
type_opt_1(send_timeout) -> time;
type_opt_1(send_timeout_close) -> bool;
type_opt_1(delay_send) -> bool;
diff --git a/erts/start_scripts/Makefile b/erts/start_scripts/Makefile
index 55584bb057..3bf233cbb6 100644
--- a/erts/start_scripts/Makefile
+++ b/erts/start_scripts/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -65,28 +65,28 @@ debug opt script: rel $(INSTALL_SCRIPTS) $(RELEASES_SRC)
rel: $(REL_SCRIPTS)
RELEASES.src:
- $(INSTALL_DIR) $(SS_TMP)
- ( cd $(SS_TMP) && \
+ $(gen_verbose)$(INSTALL_DIR) $(SS_TMP)
+ $(V_at)( cd $(SS_TMP) && \
$(ERL) -noinput +B -eval 'release_handler:create_RELEASES("%ERL_ROOT%", "$(SS_ROOT)", "$(SS_ROOT)/start_sasl.rel", []), halt()')
- mv RELEASES RELEASES.src
+ $(V_at)mv RELEASES RELEASES.src
$(SS_ROOT)/start_clean.script \
$(SS_ROOT)/start_clean.boot: $(SS_ROOT)/start_clean.rel
- $(INSTALL_DIR) $(SS_TMP)
- ( cd $(SS_TMP) && \
+ $(gen_verbose)$(INSTALL_DIR) $(SS_TMP)
+ $(V_at)( cd $(SS_TMP) && \
$(ERLC) $(SASL_FLAGS) $(SCRIPT_PATH) +no_warn_sasl -o $(SS_ROOT) $< )
$(SS_ROOT)/start_sasl.script \
$(SS_ROOT)/start_sasl.boot: $(SS_ROOT)/start_sasl.rel
- $(INSTALL_DIR) $(SS_TMP)
- ( cd $(SS_TMP) && \
+ $(gen_verbose)$(INSTALL_DIR) $(SS_TMP)
+ $(V_at)( cd $(SS_TMP) && \
$(ERLC) $(SASL_FLAGS) $(SCRIPT_PATH) -o $(SS_ROOT) $< )
$(SS_ROOT)/start_clean.rel: $(SS_ROOT)/start_clean.rel.src \
../vsn.mk \
$(LIBPATH)/kernel/vsn.mk \
$(LIBPATH)/stdlib/vsn.mk
- sed -e 's;%SYS_VSN%;$(SYSTEM_VSN);' \
+ $(gen_verbose)sed -e 's;%SYS_VSN%;$(SYSTEM_VSN);' \
-e 's;%ERTS_VSN%;$(VSN);' \
-e 's;%KERNEL_VSN%;$(KERNEL_VSN);' \
-e 's;%STDLIB_VSN%;$(STDLIB_VSN);' \
@@ -97,7 +97,7 @@ $(SS_ROOT)/start_sasl.rel: $(SS_ROOT)/start_sasl.rel.src \
$(LIBPATH)/kernel/vsn.mk \
$(LIBPATH)/stdlib/vsn.mk \
$(LIBPATH)/sasl/vsn.mk
- sed -e 's;%SYS_VSN%;$(SYSTEM_VSN);' \
+ $(gen_verbose)sed -e 's;%SYS_VSN%;$(SYSTEM_VSN);' \
-e 's;%ERTS_VSN%;$(VSN);' \
-e 's;%KERNEL_VSN%;$(KERNEL_VSN);' \
-e 's;%STDLIB_VSN%;$(STDLIB_VSN);' \
@@ -113,7 +113,7 @@ $(SS_ROOT)/start_all_example.rel: $(SS_ROOT)/start_all_example.rel.src \
$(LIBPATH)/mnesia/vsn.mk \
$(LIBPATH)/snmp/vsn.mk \
$(LIBPATH)/inets/vsn.mk
- sed -e 's;%SYS_VSN%;$(SYSTEM_VSN);' \
+ $(gen_verbose)sed -e 's;%SYS_VSN%;$(SYSTEM_VSN);' \
-e 's;%ERTS_VSN%;$(VSN);' \
-e 's;%KERNEL_VSN%;$(KERNEL_VSN);' \
-e 's;%STDLIB_VSN%;$(STDLIB_VSN);' \
@@ -126,33 +126,33 @@ $(SS_ROOT)/start_all_example.rel: $(SS_ROOT)/start_all_example.rel.src \
## Special target used from $(ERL_TOP)/erts/Makefile.
$(ERL_TOP)/bin/start.script:
- $(INSTALL_DIR) $(SS_TMP)
- ( cd $(SS_TMP) && \
+ $(gen_verbose)$(INSTALL_DIR) $(SS_TMP)
+ $(V_at)( cd $(SS_TMP) && \
$(ERLC) $(SCRIPT_PATH) +no_warn_sasl +otp_build -o $@ $(SS_ROOT)/start_clean.rel )
$(ERL_TOP)/bin/start_sasl.script:
- $(INSTALL_DIR) $(SS_TMP)
- ( cd $(SS_TMP) && \
+ $(gen_verbose)$(INSTALL_DIR) $(SS_TMP)
+ $(V_at)( cd $(SS_TMP) && \
$(ERLC) $(SCRIPT_PATH) +otp_build -o $@ $(SS_ROOT)/start_sasl.rel )
$(ERL_TOP)/bin/start_clean.script:
- $(INSTALL_DIR) $(SS_TMP)
- ( cd $(SS_TMP) && \
+ $(gen_verbose)$(INSTALL_DIR) $(SS_TMP)
+ $(V_at)( cd $(SS_TMP) && \
$(ERLC) $(SCRIPT_PATH) +no_warn_sasl +otp_build -o $@ $(SS_ROOT)/start_clean.rel )
## Special target used from system/build/Makefile for source code release bootstrap.
bootstrap_scripts: $(SS_ROOT)/start_clean.rel
- $(INSTALL_DIR) $(TESTROOT)/bin
- $(INSTALL_DIR) $(SS_TMP)
- ( cd $(SS_TMP) && \
+ $(V_at)$(INSTALL_DIR) $(TESTROOT)/bin
+ $(V_at)$(INSTALL_DIR) $(SS_TMP)
+ $(V_at)( cd $(SS_TMP) && \
$(ERLC) $(BOOTSTRAP_SCRIPT_PATH) +otp_build +no_module_tests \
-o $(TESTROOT)/bin/start.script $(SS_ROOT)/start_clean.rel )
- ( cd $(SS_TMP) && \
+ $(V_at)( cd $(SS_TMP) && \
$(ERLC) $(BOOTSTRAP_SCRIPT_PATH) +otp_build +no_module_tests \
-o $(TESTROOT)/bin/start_clean.script $(SS_ROOT)/start_clean.rel )
clean:
- $(RM) $(REL_SCRIPTS) $(INSTALL_SCRIPTS)
+ $(V_at)$(RM) $(REL_SCRIPTS) $(INSTALL_SCRIPTS)
docs:
@@ -163,14 +163,14 @@ docs:
include $(ERL_TOP)/make/otp_release_targets.mk
release_spec: script
- $(INSTALL_DIR) "$(RELEASE_PATH)/releases/$(SYSTEM_VSN)"
+ $(V_at)$(INSTALL_DIR) "$(RELEASE_PATH)/releases/$(SYSTEM_VSN)"
ifneq ($(findstring win32,$(TARGET)),win32)
- $(INSTALL_DATA) RELEASES.src "$(RELEASE_PATH)/releases"
+ $(V_at)$(INSTALL_DATA) RELEASES.src "$(RELEASE_PATH)/releases"
endif
- $(INSTALL_DATA) $(INSTALL_SCRIPTS) $(REL_SCRIPTS) \
+ $(V_at)$(INSTALL_DATA) $(INSTALL_SCRIPTS) $(REL_SCRIPTS) \
"$(RELEASE_PATH)/releases/$(SYSTEM_VSN)"
- $(INSTALL_DATA) start_clean.script "$(RELEASE_PATH)/releases/$(SYSTEM_VSN)/start.script"
- $(INSTALL_DATA) start_clean.boot "$(RELEASE_PATH)/releases/$(SYSTEM_VSN)/start.boot"
+ $(V_at)$(INSTALL_DATA) start_clean.script "$(RELEASE_PATH)/releases/$(SYSTEM_VSN)/start.script"
+ $(V_at)$(INSTALL_DATA) start_clean.boot "$(RELEASE_PATH)/releases/$(SYSTEM_VSN)/start.boot"
release_docs_spec:
diff --git a/erts/test/Makefile b/erts/test/Makefile
index 9097781ae7..6b409e2f1b 100644
--- a/erts/test/Makefile
+++ b/erts/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2010. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -79,7 +79,7 @@ release_spec:
release_tests_spec: opt
$(INSTALL_DIR) "$(RELSYSDIR)"
- $(INSTALL_DATA) system.spec system.dynspec system.spec.vxworks \
+ $(INSTALL_DATA) system.spec system.dynspec \
$(ERL_FILES) $(TARGET_FILES) "$(RELSYSDIR)"
chmod -R u+w "$(RELSYSDIR)"
tar cf - *_SUITE_data utils | (cd "$(RELSYSDIR)"; tar xf -)
diff --git a/erts/test/erl_print_SUITE_data/Makefile.src b/erts/test/erl_print_SUITE_data/Makefile.src
index 96d71c7a98..3d58669c18 100644
--- a/erts/test/erl_print_SUITE_data/Makefile.src
+++ b/erts/test/erl_print_SUITE_data/Makefile.src
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2005-2011. All Rights Reserved.
+# Copyright Ericsson AB 2005-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/erts/test/erl_print_SUITE_data/erl_print_tests.c b/erts/test/erl_print_SUITE_data/erl_print_tests.c
index 82b0c21132..acb213cd3a 100644
--- a/erts/test/erl_print_SUITE_data/erl_print_tests.c
+++ b/erts/test/erl_print_SUITE_data/erl_print_tests.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2005-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2005-2012. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
diff --git a/erts/test/erlc_SUITE.erl b/erts/test/erlc_SUITE.erl
index ada09db975..ab774dbc4f 100644
--- a/erts/test/erlc_SUITE.erl
+++ b/erts/test/erlc_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/erts/test/erlexec_SUITE_data/Makefile.src b/erts/test/erlexec_SUITE_data/Makefile.src
index 970a905c32..2a8decaa4b 100644
--- a/erts/test/erlexec_SUITE_data/Makefile.src
+++ b/erts/test/erlexec_SUITE_data/Makefile.src
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2008-2009. All Rights Reserved.
+# Copyright Ericsson AB 2008-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/erts/test/ethread_SUITE_data/Makefile.src b/erts/test/ethread_SUITE_data/Makefile.src
index f93a31178b..bad133c467 100644
--- a/erts/test/ethread_SUITE_data/Makefile.src
+++ b/erts/test/ethread_SUITE_data/Makefile.src
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2004-2009. All Rights Reserved.
+# Copyright Ericsson AB 2004-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/erts/test/install_SUITE.erl b/erts/test/install_SUITE.erl
index 559d95007c..f1d8dc2587 100644
--- a/erts/test/install_SUITE.erl
+++ b/erts/test/install_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/erts/test/otp_SUITE.erl b/erts/test/otp_SUITE.erl
index 332733e075..51f07b5432 100644
--- a/erts/test/otp_SUITE.erl
+++ b/erts/test/otp_SUITE.erl
@@ -84,13 +84,14 @@ undefined_functions(Config) when is_list(Config) ->
"ExcludedFrom = ~p:_/_,"
"Undef - Undef | ExcludedFrom",
[UndefS,ExcludeFrom]),
- ?line {ok,Undef0} = xref:q(Server, lists:flatten(Q)),
- ?line Undef1 = hipe_filter(Undef0),
- ?line Undef2 = ssl_crypto_filter(Undef1),
- ?line Undef3 = edoc_filter(Undef2),
+ {ok,Undef0} = xref:q(Server, lists:flatten(Q)),
+ Undef1 = hipe_filter(Undef0),
+ Undef2 = ssl_crypto_filter(Undef1),
+ Undef3 = edoc_filter(Undef2),
Undef4 = eunit_filter(Undef3),
Undef5 = dialyzer_filter(Undef4),
- Undef = wx_filter(Undef5),
+ Undef6 = wx_filter(Undef5),
+ Undef = gs_filter(Undef6),
case Undef of
[] -> ok;
@@ -150,8 +151,8 @@ is_hipe_module(Mod) ->
end.
ssl_crypto_filter(Undef) ->
- case {code:lib_dir(crypto),code:lib_dir(ssl)} of
- {{error,bad_name},{error,bad_name}} ->
+ case {app_exists(crypto),app_exists(ssl)} of
+ {false,false} ->
filter(fun({_,{ssl,_,_}}) -> false;
({_,{crypto,_,_}}) -> false;
({_,{ssh,_,_}}) -> false;
@@ -175,12 +176,13 @@ eunit_filter(Undef) ->
end, Undef).
dialyzer_filter(Undef) ->
- case code:lib_dir(dialyzer) of
- {error,bad_name} ->
+ case app_exists(dialyzer) of
+ false ->
filter(fun({_,{dialyzer_callgraph,_,_}}) -> false;
({_,{dialyzer_codeserver,_,_}}) -> false;
({_,{dialyzer_contracts,_,_}}) -> false;
({_,{dialyzer_cl_parse,_,_}}) -> false;
+ ({_,{dialyzer_timing,_,_}}) -> false;
({_,{dialyzer_plt,_,_}}) -> false;
({_,{dialyzer_succ_typings,_,_}}) -> false;
({_,{dialyzer_utils,_,_}}) -> false;
@@ -190,8 +192,8 @@ dialyzer_filter(Undef) ->
end.
wx_filter(Undef) ->
- case code:lib_dir(wx) of
- {error,bad_name} ->
+ case app_exists(wx) of
+ false ->
filter(fun({_,{MaybeWxModule,_,_}}) ->
case atom_to_list(MaybeWxModule) of
"wx"++_ -> false;
@@ -201,6 +203,16 @@ wx_filter(Undef) ->
_ -> Undef
end.
+gs_filter(Undef) ->
+ case code:lib_dir(gs) of
+ {error,bad_name} ->
+ filter(fun({_,{gs,_,_}}) -> false;
+ ({_,{gse,_,_}}) -> false;
+ ({_,{tool_utils,_,_}}) -> false;
+ (_) -> true
+ end, Undef);
+ _ -> Undef
+ end.
deprecated_not_in_obsolete(Config) when is_list(Config) ->
?line Server = ?config(xref_server, Config),
@@ -337,3 +349,16 @@ open_log(Config, Name) ->
close_log(Fd) ->
ok = file:close(Fd).
+
+app_exists(AppAtom) ->
+ case code:lib_dir(AppAtom) of
+ {error,bad_name} ->
+ false;
+ Path ->
+ case file:read_file_info(filename:join(Path,"ebin")) of
+ {ok,_} ->
+ true;
+ _ ->
+ false
+ end
+ end.
diff --git a/erts/test/run_erl_SUITE.erl b/erts/test/run_erl_SUITE.erl
index 6838c8924d..de05e6f206 100644
--- a/erts/test/run_erl_SUITE.erl
+++ b/erts/test/run_erl_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/erts/test/system.spec.vxworks b/erts/test/system.spec.vxworks
deleted file mode 100644
index 378adf56ac..0000000000
--- a/erts/test/system.spec.vxworks
+++ /dev/null
@@ -1,2 +0,0 @@
-{topcase, {dir, "../system_test"}}.
-{skip,{erlc_SUITE, "Not on VxWorks, erlc is a HOST tool."}}
diff --git a/erts/test/utils/gccifier.c b/erts/test/utils/gccifier.c
index a1019f9a72..7e4ffc7281 100644
--- a/erts/test/utils/gccifier.c
+++ b/erts/test/utils/gccifier.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2004-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2004-2012. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
diff --git a/erts/test/utils/gccifier.sh b/erts/test/utils/gccifier.sh
index 4c6ba35db2..978aecf424 100755
--- a/erts/test/utils/gccifier.sh
+++ b/erts/test/utils/gccifier.sh
@@ -2,7 +2,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2005-2009. All Rights Reserved.
+# Copyright Ericsson AB 2005-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/erts/vsn.mk b/erts/vsn.mk
index a420781e9f..7d42bb1d01 100644
--- a/erts/vsn.mk
+++ b/erts/vsn.mk
@@ -18,7 +18,7 @@
#
VSN = 5.10
-SYSTEM_VSN = R16B
+SYSTEM_VSN = R16A
# Port number 4365 in 4.2
# Port number 4366 in 4.3
diff --git a/lib/.gitignore b/lib/.gitignore
index 56b1ed2b84..4125111ebd 100644
--- a/lib/.gitignore
+++ b/lib/.gitignore
@@ -7,6 +7,7 @@
/common_test/doc/src/ct_rpc.xml
/common_test/doc/src/ct_snmp.xml
/common_test/doc/src/ct_ssh.xml
+/common_test/doc/src/ct_netconfc.xml
/common_test/doc/src/ct_telnet.xml
/common_test/doc/src/unix_telnet.xml
diff --git a/lib/appmon/doc/src/Makefile b/lib/appmon/doc/src/Makefile
index 2bf560ee6b..7c93abe6d8 100644
--- a/lib/appmon/doc/src/Makefile
+++ b/lib/appmon/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2010. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/appmon/priv/Makefile b/lib/appmon/priv/Makefile
index 8920923c98..356fe9b842 100644
--- a/lib/appmon/priv/Makefile
+++ b/lib/appmon/priv/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2009. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/appmon/src/Makefile b/lib/appmon/src/Makefile
index 9ee7c92a4d..c42ce068b4 100644
--- a/lib/appmon/src/Makefile
+++ b/lib/appmon/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2009. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -37,13 +37,15 @@ MODULES= \
appmon \
appmon_a \
appmon_dg \
- appmon_info \
appmon_place \
appmon_txt \
appmon_lb \
process_info \
appmon_web
+# appmon_info \ Moved to runtime tools where it belongs
+
+
HRL_FILES= appmon_dg.hrl
ERL_FILES= $(MODULES:%=%.erl)
@@ -81,10 +83,10 @@ docs:
# ----------------------------------------------------
$(APP_TARGET): $(APP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
# ----------------------------------------------------
# Release Target
diff --git a/lib/appmon/src/appmon.app.src b/lib/appmon/src/appmon.app.src
index 2e1aa3ef3b..aa6a08772e 100644
--- a/lib/appmon/src/appmon.app.src
+++ b/lib/appmon/src/appmon.app.src
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,7 +18,7 @@
{application, appmon,
[{description, "DEVTOOLS CXC 138 16"},
{vsn, "%VSN%"},
- {modules, [appmon, appmon_a, appmon_dg, appmon_info,
+ {modules, [appmon, appmon_a, appmon_dg, %% appmon_info, moved to runtime tools
appmon_lb, appmon_place, appmon_txt,process_info,appmon_web]},
{registered,[appmon, appmon_info, appmon_txt,webappmon_server,proc_info]},
{applications, [kernel, stdlib]}]}.
diff --git a/lib/asn1/c_src/Makefile b/lib/asn1/c_src/Makefile
index 903cf32838..70238335c4 100644
--- a/lib/asn1/c_src/Makefile
+++ b/lib/asn1/c_src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2002-2011. All Rights Reserved.
+# Copyright Ericsson AB 2002-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -66,13 +66,8 @@ NIF_SHARED_OBJ_FILE = $(LIBDIR)/asn1_erl_nif.dll
CLIB_FLAGS =
LN=cp
else
-ifeq ($(findstring vxworks,$(TARGET)),vxworks)
-NIF_SHARED_OBJ_FILE = $(LIBDIR)/asn1_erl_nif.eld
-CLIB_FLAGS =
-else
NIF_SHARED_OBJ_FILE = $(LIBDIR)/asn1_erl_nif.so
CLIB_FLAGS = -lc
-endif
LN= ln -s
endif
@@ -99,10 +94,10 @@ docs:
$(OBJDIR)/%.o: %.c
- $(CC) -c $(CFLAGS) -O3 -o $@ $<
+ $(V_CC) -c $(CFLAGS) -O3 -o $@ $<
$(NIF_SHARED_OBJ_FILE): $(NIF_OBJ_FILES)
- $(LD) $(LDFLAGS) -o $(NIF_SHARED_OBJ_FILE) $(NIF_OBJ_FILES) $(CLIB_FLAGS) $(LIBS)
+ $(V_LD) $(LDFLAGS) -o $(NIF_SHARED_OBJ_FILE) $(NIF_OBJ_FILES) $(CLIB_FLAGS) $(LIBS)
# ----------------------------------------------------
# Release Target
diff --git a/lib/asn1/doc/src/Makefile b/lib/asn1/doc/src/Makefile
index 9ef5750353..3b3e1bd8f9 100644
--- a/lib/asn1/doc/src/Makefile
+++ b/lib/asn1/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/asn1/doc/src/asn1_ug.xml b/lib/asn1/doc/src/asn1_ug.xml
index 1b399fb641..6cb251c3e2 100644
--- a/lib/asn1/doc/src/asn1_ug.xml
+++ b/lib/asn1/doc/src/asn1_ug.xml
@@ -186,13 +186,21 @@ END </pre>
The following shows how the compiler
can be called from the Erlang shell:</p>
<pre>
-1><input>asn1ct:compile("People",[ber_bin]).</input>
+1><input>asn1ct:compile("People", [ber]).</input>
+ok
+2> </pre>
+
+ <p>The <c>verbose</c> option can be given to have information
+ about the generated files printed:</p>
+ <pre>
+2><input>asn1ct:compile("People", [ber,verbose]).</input>
Erlang ASN.1 compiling "People.asn"
--{generated,"People.asn1db"}--
--{generated,"People.hrl"}--
--{generated,"People.erl"}--
ok
-2> </pre>
+3> </pre>
+
<p>The ASN.1 module People is now accepted and the abstract syntax tree
is saved in the <c>People.asn1db</c> file, the
generated Erlang code is compiled using the Erlang compiler and
@@ -229,7 +237,7 @@ receive
constructed and encoded using
<c>'People':encode('Person',Answer)</c> which takes an
instance of a defined ASN.1 type and transforms it to a
- (possibly) nested list of bytes according to the BER or PER
+ binary according to the BER or PER
encoding-rules.
<br></br>
The encoder and the decoder can also be run from
@@ -239,24 +247,12 @@ The encoder and the decoder can also be run from
<pre>
2> <input>Rockstar = {'Person',"Some Name",roving,50}.</input>
{'Person',"Some Name",roving,50}
-3> <input>{ok,Bytes} = asn1rt:encode('People','Person',Rockstar).</input>
-{ok,[&lt;&lt;243&gt;&gt;,
- [17],
- [19,9,"Some Name"],
- [2,1,[2]],
- [2,1,"2"]]}
-4> <input>Bin = list_to_binary(Bytes).</input>
-&lt;&lt;243,17,19,9,83,111,109,101,32,78,97,109,101,2,1,2,2,1,50&gt;&gt;
-5> <input>{ok,Person} = asn1rt:decode('People','Person',Bin).</input>
+3> <input>{ok,Bin} = asn1rt:encode('People','Person',Rockstar).</input>
+{ok,&lt;&lt;243,17,19,9,83,111,109,101,32,78,97,109,101,2,1,2,
+ 2,1,50&gt;&gt;}
+4> <input>{ok,Person} = asn1rt:decode('People','Person',Bin).</input>
{ok,{'Person',"Some Name",roving,50}}
-6> </pre>
- <p>Notice that the result from <c>encode</c> is a nested list which
- must be turned into a binary before the call to <c>decode</c>. A
- binary is necessary as input to decode since the module was compiled
- with the <c>ber_bin</c> option
- The reason for returning a nested list is that it is faster to produce
- and the <c>list_to_binary</c> operation is
- performed automatically when the list is sent via the Erlang port mechanism.</p>
+5> </pre>
</section>
<section>
@@ -305,17 +301,15 @@ The encoder and the decoder can also be run from
ASN.1 compiler:</p>
<pre>
erlc Person.asn
-erlc -bper_bin Person.asn
-erlc -bber_bin +optimize ../Example.asn
+erlc -bper Person.asn
+erlc -bber ../Example.asn
erlc -o ../asnfiles -I ../asnfiles -I /usr/local/standards/asn1 Person.asn </pre>
<p>The useful options for the ASN.1 compiler are:</p>
<taglist>
- <tag><c>-b[ber | per | ber_bin | per_bin | uper_bin]</c></tag>
+ <tag><c>-b[ber | per | uper]</c></tag>
<item>
<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
- instead of the <c>ber</c> and <c>per</c> options.</p>
+ default.</p>
</item>
<tag><c>-o OutDirectory</c></tag>
<item>
@@ -339,42 +333,12 @@ erlc -o ../asnfiles -I ../asnfiles -I /usr/local/standards/asn1 Person.asn
</item>
<tag><c>+der</c></tag>
<item>
- <p>DER encoding rule. Only when using <c>-ber</c> or
- <c>-ber_bin</c> option.</p>
- </item>
- <tag><c>+optimize</c></tag>
- <item>
- <p>This flag has effect only when used together with one of
- <c>per_bin</c> or <c>ber_bin</c> flags. It gives time optimized
- code in the generated modules and it uses another runtime module.
- In the <c>per_bin</c> case a nif is used. The
- result from an encode is a binary.</p>
- <p><em>When this flag is used you cannot use the old format</em><c>{TypeName,Value}</c> when you encode values. Since it is
- an unnecessary construct it has been removed in favor of
- performance. It
- is neither admitted to construct SEQUENCE or SET component values
- with the format <c>{ComponentName,Value}</c> since it also is
- unnecessary. The only case were it is necessary is in a CHOICE,
- were you have to pass values to the right component by specifying
- <c>{ComponentName,Value}</c>. See also about
- <seealso marker="#TypeNameValue">{Typename,Value}</seealso> below
- and in the sections for each type.</p>
- </item>
- <tag><c>+driver</c></tag>
- <item>
- <p>As of R15B this means the same as the <c>nif</c> option. Kept for
- backwards compatability reasons.</p>
- </item>
- <tag><c>+nif</c></tag>
- <item>
- <p>Together with the flags <c>ber_bin</c>
- and <c>optimize</c> you choose to use a nif for considerable
- faster encode and decode. </p>
+ <p>DER encoding rule. Only when using <c>-ber</c> option.</p>
</item>
<tag><c>+asn1config</c></tag>
<item>
<p>This functionality works together with the flags
- <c>ber_bin</c> and <c>optimize</c>. You enables the
+ <c>ber</c>. It enables the
specialized decodes, see the <seealso marker="asn1_spec">Specialized Decode</seealso> chapter.
</p>
</item>
@@ -413,7 +377,6 @@ erlc -o ../asnfiles -I ../asnfiles -I /usr/local/standards/asn1 Person.asn
</item>
</taglist>
<p>For a complete description of <c>erlc</c> see Erts Reference Manual.</p>
- <p>For preferred option use see <seealso marker="#preferred option use">Preferred Option Use</seealso> section.</p>
<p>The compiler and other compile-time functions can also be invoked from
the Erlang shell. Below follows a brief
description of the primary functions, for a
@@ -429,9 +392,9 @@ asn1ct:compile("H323-MESSAGES.asn1"). </pre>
<p>which equals:</p>
<pre>
asn1ct:compile("H323-MESSAGES.asn1",[ber]). </pre>
- <p>If one wants PER encoding with optimizations:</p>
+ <p>If one wants PER encoding:</p>
<pre>
-asn1ct:compile("H323-MESSAGES.asn1",[per_bin,optimize]). </pre>
+asn1ct:compile("H323-MESSAGES.asn1",[per]). </pre>
<p>The generic encode and decode functions can be invoked like this:</p>
<pre>
asn1ct:encode('H323-MESSAGES','SomeChoiceType',{call,"octetstring"}).
@@ -443,269 +406,6 @@ asn1ct:decode('H323-MESSAGES','SomeChoiceType',Bytes). </pre>
</section>
<section>
- <marker id="preferred option use"></marker>
- <title>Preferred Option Use</title>
- <p>
- It may not be obvious which compile options best fit a
- situation. This section describes the format of the result of
- encode and decode. It also gives some performance statistics
- when using certain options. Finally there is a recommendation
- which option combinations should be used.
- </p>
- <p>
- The default option is <c>ber</c>. It is the same backend as
- <c>ber_bin</c> except that the result of encode is transformed
- to a flat list. Below is a table that gives the different
- formats of input and output of encode and decode using the
- <em>allowed combinations</em> of coding and optimization
- options: (EAVF stands for how ASN1 values are represented in
- Erlang which is described in the <seealso
- marker="#ASN1Types">ASN1 Types chapter</seealso>)
- </p>
- <table>
- <row>
- <cell align="left" valign="middle"><em>Encoding Rule</em></cell>
- <cell align="left" valign="middle"><em>Compile options, allowed combinations</em></cell>
- <cell align="left" valign="middle"><em>encode input</em></cell>
- <cell align="left" valign="middle"><em>encode output</em></cell>
- <cell align="left" valign="middle"><em>decode input</em></cell>
- <cell align="left" valign="middle"><em>decode output</em></cell>
- </row>
- <row>
- <cell align="left" valign="middle">BER</cell>
- <cell align="left" valign="middle">[ber] (default)</cell>
- <cell align="left" valign="middle">EAVF</cell>
- <cell align="left" valign="middle">flat list</cell>
- <cell align="left" valign="middle">flat list / binary</cell>
- <cell align="left" valign="middle">EAVF</cell>
- </row>
- <row>
- <cell align="left" valign="middle">BER</cell>
- <cell align="left" valign="middle">[ber_bin]</cell>
- <cell align="left" valign="middle">EAVF</cell>
- <cell align="left" valign="middle">iolist</cell>
- <cell align="left" valign="middle">binary</cell>
- <cell align="left" valign="middle">EAVF</cell>
- </row>
- <row>
- <cell align="left" valign="middle">BER</cell>
- <cell align="left" valign="middle"><em>[ber_bin, optimize]</em></cell>
- <cell align="left" valign="middle">EAVF</cell>
- <cell align="left" valign="middle">iolist</cell>
- <cell align="left" valign="middle">binary</cell>
- <cell align="left" valign="middle">EAVF</cell>
- </row>
- <row>
- <cell align="left" valign="middle">BER</cell>
- <cell align="left" valign="middle"><em>[ber_bin, optimize, nif]</em></cell>
- <cell align="left" valign="middle">EAVF</cell>
- <cell align="left" valign="middle">iolist</cell>
- <cell align="left" valign="middle">iolist / binary</cell>
- <cell align="left" valign="middle">EAVF</cell>
- </row>
- <row>
- <cell align="left" valign="middle">PER aligned variant</cell>
- <cell align="left" valign="middle">[per]</cell>
- <cell align="left" valign="middle">EAVF</cell>
- <cell align="left" valign="middle">flat list</cell>
- <cell align="left" valign="middle">flat list</cell>
- <cell align="left" valign="middle">EAVF</cell>
- </row>
- <row>
- <cell align="left" valign="middle">PER aligned variant</cell>
- <cell align="left" valign="middle"><em>[per_bin]</em></cell>
- <cell align="left" valign="middle">EAVF</cell>
- <cell align="left" valign="middle">iolist / binary</cell>
- <cell align="left" valign="middle">binary</cell>
- <cell align="left" valign="middle">EAVF</cell>
- </row>
- <row>
- <cell align="left" valign="middle">PER aligned variant</cell>
- <cell align="left" valign="middle"><em>[per_bin, optimize]</em></cell>
- <cell align="left" valign="middle">EAVF</cell>
- <cell align="left" valign="middle">binary</cell>
- <cell align="left" valign="middle">binary</cell>
- <cell align="left" valign="middle">EAVF</cell>
- </row>
- <row>
- <cell align="left" valign="middle">PER unaligned variant</cell>
- <cell align="left" valign="middle"><em>[uper_bin]</em></cell>
- <cell align="left" valign="middle">EAVF</cell>
- <cell align="left" valign="middle">binary</cell>
- <cell align="left" valign="middle">binary</cell>
- <cell align="left" valign="middle">EAVF</cell>
- </row>
-
- <row>
- <cell align="left" valign="middle">DER</cell>
- <cell align="left" valign="middle">[(ber), der]</cell>
- <cell align="left" valign="middle">EAVF</cell>
- <cell align="left" valign="middle">flat list</cell>
- <cell align="left" valign="middle">flat list / binary</cell>
- <cell align="left" valign="middle">EAVF</cell>
- </row>
- <row>
- <cell align="left" valign="middle">DER</cell>
- <cell align="left" valign="middle">[ber_bin, der]</cell>
- <cell align="left" valign="middle">EAVF</cell>
- <cell align="left" valign="middle">iolist</cell>
- <cell align="left" valign="middle">binary</cell>
- <cell align="left" valign="middle">EAVF</cell>
- </row>
- <row>
- <cell align="left" valign="middle">DER</cell>
- <cell align="left" valign="middle"><em>[ber_bin, optimize, der]</em></cell>
- <cell align="left" valign="middle">EAVF</cell>
- <cell align="left" valign="middle">iolist</cell>
- <cell align="left" valign="middle">binary</cell>
- <cell align="left" valign="middle">EAVF</cell>
- </row>
- <row>
- <cell align="left" valign="middle">DER</cell>
- <cell align="left" valign="middle"><em>[ber_bin, optimize, nif, der]</em></cell>
- <cell align="left" valign="middle">EAVF</cell>
- <cell align="left" valign="middle">iolist</cell>
- <cell align="left" valign="middle">binary</cell>
- <cell align="left" valign="middle">EAVF</cell>
- </row>
-
-
- <tcaption>The output / input formats for different combinations of compile options.</tcaption>
- </table>
- <p>
- Encode / decode speed comparison in one user case for the above
- alternatives (except <c>DER</c>) is showed in the table below. The
- <c>DER</c> alternatives are slower than their corresponding
- <c>BER</c> alternative.
- </p>
-
- <table>
- <row>
- <cell align="left" valign="middle"><em>compile options</em></cell>
- <cell align="left" valign="middle"><em>encode time</em></cell>
- <cell align="left" valign="middle"><em>decode time</em></cell>
- </row>
- <row>
- <cell align="left" valign="middle">[ber]</cell>
- <cell align="left" valign="middle">120</cell>
- <cell align="left" valign="middle">162</cell>
- </row>
- <row>
- <cell align="left" valign="middle">[ber_bin]</cell>
- <cell align="left" valign="middle">124</cell>
- <cell align="left" valign="middle">154</cell>
- </row>
- <row>
- <cell align="left" valign="middle"><em>[ber_bin, optimize]</em></cell>
- <cell align="left" valign="middle">50</cell>
- <cell align="left" valign="middle">78</cell>
- </row>
- <row>
- <cell align="left" valign="middle"><em>[ber_bin, optimize, driver]</em></cell>
- <cell align="left" valign="middle">50</cell>
- <cell align="left" valign="middle">62</cell>
- </row>
- <row>
- <cell align="left" valign="middle">[per]</cell>
- <cell align="left" valign="middle">141</cell>
- <cell align="left" valign="middle">133</cell>
- </row>
- <row>
- <cell align="left" valign="middle"><em>[per_bin]</em></cell>
- <cell align="left" valign="middle">125</cell>
- <cell align="left" valign="middle">123</cell>
- </row>
- <row>
- <cell align="left" valign="middle"><em>[per_bin, optimize]</em></cell>
- <cell align="left" valign="middle">77</cell>
- <cell align="left" valign="middle">72</cell>
- </row>
- <row>
- <cell align="left" valign="middle"><em>[uper_bin]</em></cell>
- <cell align="left" valign="middle">97</cell>
- <cell align="left" valign="middle">104</cell>
- </row>
- <tcaption>
- One example of difference in speed for the compile option alternatives.
- </tcaption>
- </table>
-
- <p>
- The compile options <c>ber</c>, <c>per</c> and
- <c>driver</c> are kept for backwards compatibility and should not be
- used in new code. The nif implementation which replaces the linked-in
- driver has been shown to be about 5-15% faster.
- </p>
- <p>
- You are strongly recommended to use the appropriate alternative
- of the bold typed options. The <c>optimize</c> and
- <c>nif</c> options does not affect the encode or decode
- result, just the time spent in run-time. When <c>ber_bin</c> and
- <c>nif</c> or <c>per_bin</c> and <c>optimize</c> is
- combined the C-code nif is used in chosen parts of encode /
- decode procedure.
- </p>
- <table>
- <row>
- <cell align="left" valign="middle"><em>Compile options, allowed combinations</em></cell>
- <cell align="left" valign="middle"><em>use of nif</em></cell>
- </row>
- <row>
- <cell align="left" valign="middle">[ber]</cell>
- <cell align="left" valign="middle">no</cell>
- </row>
- <row>
- <cell align="left" valign="middle">[ber_bin]</cell>
- <cell align="left" valign="middle">no</cell>
- </row>
- <row>
- <cell align="left" valign="middle"><em>[ber_bin, optimize]</em></cell>
- <cell align="left" valign="middle">no</cell>
- </row>
- <row>
- <cell align="left" valign="middle"><em>[ber_bin, optimize, nif]</em></cell>
- <cell align="left" valign="middle">yes</cell>
- </row>
- <row>
- <cell align="left" valign="middle">[per]</cell>
- <cell align="left" valign="middle">no</cell>
- </row>
- <row>
- <cell align="left" valign="middle"><em>[per_bin]</em></cell>
- <cell align="left" valign="middle">no</cell>
- </row>
- <row>
- <cell align="left" valign="middle"><em>[per_bin, optimize]</em></cell>
- <cell align="left" valign="middle">yes</cell>
- </row>
- <row>
- <cell align="left" valign="middle"><em>[uper_bin]</em></cell>
- <cell align="left" valign="middle">no</cell>
- </row>
-
- <row>
- <cell align="left" valign="middle">[(ber), der]</cell>
- <cell align="left" valign="middle">no</cell>
- </row>
- <row>
- <cell align="left" valign="middle">[ber_bin, der]</cell>
- <cell align="left" valign="middle">no</cell>
- </row>
- <row>
- <cell align="left" valign="middle"><em>[ber_bin, optimize, der]</em></cell>
- <cell align="left" valign="middle">no</cell>
- </row>
- <row>
- <cell align="left" valign="middle"><em>[ber_bin, optimize, nif, der]</em></cell>
- <cell align="left" valign="middle">yes</cell>
- </row>
-
-
- <tcaption>When the ASN1 nif is used.</tcaption>
- </table>
-
- </section>
- <section>
<title>Run-time Functions</title>
<p>A brief description of the major functions is given here. For a
complete description of each function see
@@ -719,9 +419,9 @@ asn1rt:decode('H323-MESSAGES','SomeChoiceType',Bytes). </pre>
'H323-MESSAGES':encode('SomeChoiceType',{call,"octetstring"}).
'H323-MESSAGES':decode('SomeChoiceType',Bytes). </pre>
<p>The asn1 nif is enabled in two occasions: encoding of
- asn1 values when the asn1 spec is compiled with <c>per_bin</c> and
- <c>optimize</c> or decode of encoded asn1 values when the asn1 spec is
- compiled with <c>ber_bin</c>, <c>optimize</c> and <c>nif</c>. In
+ asn1 values when the asn1 spec is compiled with <c>per</c> and
+ or decode of encoded asn1 values when the asn1 spec is
+ compiled with <c>ber</c>. In
those cases the nif will be loaded automatically at the first call
to <c>encode</c>/<c>decode</c>. If one doesn't want the performance
overhead of the nif being loaded at the first call it is possible
@@ -868,26 +568,6 @@ Operational ::= BOOLEAN --ASN.1 definition </pre>
<pre>
Val = true,
{ok,Bytes}=asn1rt:encode(MyModule,'Operational',Val), </pre>
- <p>For historical reasons it is also possible to assign ASN.1 values
- in Erlang using a tuple notation
- with type and value as this</p>
- <pre>
-Val = {'Operational',true} </pre>
- <warning>
- <marker id="warning"></marker>
- <p>The tuple notation, <c>{Typename, Value}</c> is only kept
- because of backward compatibility and may be withdrawn in a
- future release. If the notation is used the <c>Typename</c>
- element must be spelled correctly, otherwise a run-time error
- will occur.
- </p>
- <p>If the ASN.1 module is compiled with the flags
- <c>per_bin</c> or <c>ber_bin</c> and <c>optimize</c> it is not
- allowed to use the tuple notation. That possibility has been
- removed due to performance reasons. Neither is it allowed to
- use the <c>{ComponentName,Value}</c> notation in case of a
- SEQUENCE or SET type.</p>
- </warning>
<p>Below follows a description of how
values of each type can be represented in Erlang.
</p>
@@ -1149,7 +829,7 @@ TextFileVal2 = [88,76,55,44,99,121 .......... a lot of characters here ....]
The following example shows how it works:</p>
<p>In a file <c>PrimStrings.asn1</c> the type <c>BMP</c> is defined as
<br></br>
-<c>BMP ::= BMPString</c> then using BER encoding (<c>ber_bin</c>
+<c>BMP ::= BMPString</c> then using BER encoding (<c>ber</c>
option)the input/output format will be:</p>
<pre>
1> <input>{ok,Bytes1} = asn1rt:encode('PrimStrings','BMP',[{0,0,53,53},{0,0,45,56}]).</input>
@@ -1174,9 +854,9 @@ TextFileVal2 = [88,76,55,44,99,121 .......... a lot of characters here ....]
<c>utf8_list_to_binary</c>, are in the <c>asn1rt</c> module. In
the example below we assume an asn1 definition <c>UTF ::= UTF8String</c> in a module <c>UTF.asn</c>:</p>
<pre>
-1> <input>asn1ct:compile('UTF',[ber_bin]).</input>
+1> <input>asn1ct:compile('UTF',[ber]).</input>
Erlang ASN.1 version "1.4.3.3" compiling "UTF.asn"
-Compiler Options: [ber_bin]
+Compiler Options: [ber]
--{generated,"UTF.asn1db"}--
--{generated,"UTF.erl"}--
ok
@@ -1287,14 +967,6 @@ 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>
-<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>
@@ -1739,12 +1411,9 @@ SS ::= SET {
1> <input>Val = 'Values':tt().</input>
{'TT',77,["kalle","kula"]}
2> <input>{ok,Bytes} = 'Values':encode('TT',Val).</input>
-{ok,["0",
- [18],
- [[[128],[1],"M"],["\\241","\\r",[[[4],[5],"kalle"],[[4],[4],"kula"]]]]]}
-3> <input>FlatBytes = lists:flatten(Bytes).</input>
-[48,18,128,1,77,161,13,4,5,107,97,108,108,101,4,4,107,117,108,97]
-4> <input>'Values':decode('TT',FlatBytes).</input>
+{ok,&lt;&lt;48,18,128,1,77,161,13,4,5,107,97,108,108,101,4,4,
+ 107,117,108,97&gt;&gt;}
+4> <input>'Values':decode('TT',Bytes).</input>
{ok,{'TT',77,["kalle","kula"]}}
5>
</pre>
diff --git a/lib/asn1/doc/src/asn1ct.xml b/lib/asn1/doc/src/asn1ct.xml
index 3be58cbc8e..bb3c6a4f0f 100644
--- a/lib/asn1/doc/src/asn1ct.xml
+++ b/lib/asn1/doc/src/asn1ct.xml
@@ -41,6 +41,19 @@
encode/decode functions. There are also some generic functions which
can be used in during development of applications which handles ASN.1
data (encoded as BER or PER).</p>
+ <note>
+ <p>In R16, the options have been simplified. The back-end is chosen
+ using one of the options <c>ber</c>, <c>per</c>, or <c>uper</c>.
+ The options <c>optimize</c>, <c>nif</c>, and <c>driver</c> options
+ are no longer necessary (and the ASN.1 compiler will print a
+ warning if they are used). The options <c>ber_bin</c>, <c>per_bin</c>,
+ and <c>uper_bin</c> options will still work, but will print a warning.
+ </p>
+ <p>Another change in R16 is that the generated <c>encode/2</c>
+ function (and <c>asn1rt:encode/3</c>) always returns a binary.
+ The <c>encode/2</c> function for the BER back-end used to return
+ an iolist.</p>
+ </note>
</description>
<funcs>
<func>
@@ -50,9 +63,9 @@
<type>
<v>Asn1module = atom() | string()</v>
<v>Options = [Option| OldOption]</v>
- <v>Option = ber_bin | per_bin | uper_bin | der | compact_bit_string |
- noobj | {n2n, EnumTypeName} |{outdir, Dir} | {i, IncludeDir} | optimize |
- nif | asn1config | undec_rest | {inline, OutputName} | inline |
+ <v>Option = ber | per | uper | der | compact_bit_string |
+ noobj | {n2n, EnumTypeName} |{outdir, Dir} | {i, IncludeDir} |
+ asn1config | undec_rest | {inline, OutputName} | inline |
{macro_name_prefix, Prefix} | {record_name_prefix, Prefix} | verbose | warnings_as_errors</v>
<v>OldOption = ber | per</v>
<v>Reason = term()</v>
@@ -107,12 +120,10 @@ File3.asn </pre>
<c>Options</c> is a list with options specific for the asn1
compiler and options that are applied to the Erlang compiler.
The latter are those that not is recognized as asn1 specific.
- For <em>preferred option use</em> see <seealso
- marker="asn1_ug#preferred option use">Preferred Option Use
- section in users guide</seealso>. Available options are:
+ Available options are:
</p>
<taglist>
- <tag><c>ber | ber_bin | per | per_bin | uper_bin</c></tag>
+ <tag><c>ber | per | uper</c></tag>
<item>
<p>
The encoding rule to be used. The supported encoding rules
@@ -120,23 +131,12 @@ File3.asn </pre>
PER aligned (Packed Encoding Rules) and PER unaligned.
If the encoding rule option is omitted <c>ber</c>
is the default.
- The <c>per_bin</c> option means the aligned
- variant. To use the unaligned variant the <c>uper_bin</c>
- option has to be used.
</p>
<p>
The generated Erlang module always gets the same name
as the ASN.1 module and as a consequence of this only one
encoding rule per ASN.1 module can be used at runtime.
</p>
- <p>
- The <c>ber_bin</c> and <c>per_bin</c> options are
- equivalent with the <c>OldOptions</c> <c>ber</c> and <c>per</c>
- with the difference that the generated encoding/decoding
- functions take advantage of the bit syntax, which in most
- cases increases the performance considerably. The result
- from encoding is a binary or an iolist.
- </p>
</item>
<tag><c>der</c></tag>
<item>
@@ -144,7 +144,7 @@ File3.asn </pre>
By this option the Distinguished Encoding Rules (DER) is chosen.
DER is regarded as a specialized variant of the BER encoding
rule, therefore the <c>der</c> option only makes sense together
- with the <c>ber</c> or <c>ber_bin</c> option.
+ with the <c>ber</c> option.
This option
sometimes adds sorting and value checks when encoding, which
implies a slower encoding. The decoding routines are the same
@@ -206,28 +206,6 @@ Binary = binary()
shall be placed. If omitted the files are placed in the
current directory.</p>
</item>
- <tag><c>optimize</c></tag>
- <item>
- <p>This option is only valid together with one of the
- <c>per_bin</c>
- or <c>ber_bin</c> option. It gives time optimized code
- generated and it uses another runtime module and
- in the <c>per_bin</c> case a nif. The result
- in the <c>per_bin</c> case from an encode when compiled
- with this option will be a binary.</p>
- </item>
- <tag><c>driver</c></tag>
- <item>
- <p>As of R15B this means the same as the <c>nif</c> option. Kept for
- backwards compatability reasons.</p>
- </item>
- <tag><c>nif</c></tag>
- <item>
- <p>Option valid together with <c>ber_bin</c> and <c>optimize</c>
- options. It enables the use of several nifs that gives faster
- encode and decode. Nifs are only enabled by the explicit use of
- the option <c>nif</c></p>
- </item>
<tag><c>asn1config</c></tag>
<item>
<p>When one of the specialized decodes, exclusive or
@@ -270,10 +248,6 @@ Binary = binary()
<c>{export, [atom()]}</c> or <c>{export_all, true}</c> option
are provided. The list of atoms are names of chosen asn1
specs from the <c>.set.asn</c> file. </p>
- <p>When used together with <c>nif</c> for <c>ber_bin</c>, the
- asn1 nifs will be used if the <c>asn1rt_nif</c> module is
- available. If it is not available, a slower erlang fallback
- will be used.</p>
</item>
<tag><c>inline</c></tag>
<item>
@@ -327,13 +301,12 @@ Binary = binary()
<type>
<v>Module = Type = atom()</v>
<v>Value = term()</v>
- <v>Bytes = [Int] when integer(Int), Int >= 0, Int =&lt; 255</v>
+ <v>Bytes = binary()</v>
<v>Reason = term()</v>
</type>
<desc>
<p>Encodes <c>Value</c> of <c>Type</c> defined in the ASN.1 module
- <c>Module</c>. Returns a list of bytes if successful. To get as fast execution as
- possible the
+ <c>Module</c>. To get as fast execution as possible the
encode function only performs rudimentary tests that the input
<c>Value</c>
is a correct instance of <c>Type</c>. The length of strings is for example
@@ -348,10 +321,10 @@ Binary = binary()
<type>
<v>Module = Type = atom()</v>
<v>Value = Reason = term()</v>
- <v>Bytes = [Int] when integer(Int), Int >= 0, Int =&lt; 255</v>
+ <v>Bytes = binary()</v>
</type>
<desc>
- <p>Decodes <c>Type</c> from <c>Module</c> from the list of bytes
+ <p>Decodes <c>Type</c> from <c>Module</c> from the binary
<c>Bytes</c>. Returns <c>{ok, Value}</c> if successful.</p>
</desc>
</func>
diff --git a/lib/asn1/doc/src/asn1rt.xml b/lib/asn1/doc/src/asn1rt.xml
index 0c3c257189..f2cac0c9e7 100644
--- a/lib/asn1/doc/src/asn1rt.xml
+++ b/lib/asn1/doc/src/asn1rt.xml
@@ -47,35 +47,34 @@
<type>
<v>Module = Type = atom()</v>
<v>Value = Reason = term()</v>
- <v>Bytes = binary | [Int] when integer(Int), Int >= 0, Int =&lt; 255 | binary</v>
+ <v>Bytes = binary</v>
</type>
<desc>
- <p>Decodes <c>Type</c> from <c>Module</c> from the list of bytes or
- binary <c>Bytes</c>. If the module is compiled with <c>ber_bin</c>
- or <c>per_bin</c> option <c>Bytes</c> must be a binary.
+ <p>Decodes <c>Type</c> from <c>Module</c> from the binary <c>Bytes</c>.
Returns <c>{ok,Value}</c> if successful.</p>
</desc>
</func>
<func>
- <name>encode(Module,Type,Value)-> {ok,BinOrList} | {error,Reason}</name>
+ <name>encode(Module,Type,Value)-> {ok,Bytes} | {error,Reason}</name>
<fsummary>Encode an ASN.1 value.</fsummary>
<type>
<v>Module = Type = atom()</v>
<v>Value = term()</v>
- <v>BinOrList = Bytes | binary()</v>
- <v>Bytes = [Int|binary|Bytes] when integer(Int), Int >= 0, Int =&lt; 255</v>
+ <v>Bytes = binary</v>
<v>Reason = term()</v>
</type>
<desc>
<p>Encodes <c>Value</c> of <c>Type</c> defined in the ASN.1 module
<c>Module</c>. Returns a possibly nested list of bytes and or binaries
- if successful. If <c>Module</c> was compiled with the options <c>per_bin</c> and <c>optimize</c> the result is a binary. To get as
- fast execution as possible the
+ if successful. To get as fast execution as possible the
encode function only performs rudimentary tests that the input
<c>Value</c>
is a correct instance of <c>Type</c>. The length of strings is for example
not always checked. </p>
+ <note>
+ <p>Starting in R16, <c>Bytes</c> is always a binary.</p>
+ </note>
</desc>
</func>
@@ -95,28 +94,6 @@
</func>
<func>
- <name>load_driver() -> ok | {error,Reason}</name>
- <fsummary>Loads the linked-in driver. (deprecated)</fsummary>
- <type>
- <v>Reason = term()</v>
- </type>
- <desc>
- <p>This function is obsolete and will be removed in R16A</p>
- </desc>
- </func>
-
- <func>
- <name>unload_driver() -> ok | {error,Reason}</name>
- <fsummary>Unloads the linked-in driver. (deprecated)</fsummary>
- <type>
- <v>Reason = term()</v>
- </type>
- <desc>
- <p>This function is obsolete and will be removed in R16A</p>
- </desc>
- </func>
-
- <func>
<name>utf8_binary_to_list(UTF8Binary) -> {ok,UnicodeList} | {error,Reason}</name>
<fsummary>Transforms an utf8 encoded binary to a unicode list.</fsummary>
<type>
diff --git a/lib/asn1/doc/src/notes.xml b/lib/asn1/doc/src/notes.xml
index b5422c9083..72b496caf7 100644
--- a/lib/asn1/doc/src/notes.xml
+++ b/lib/asn1/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2004</year><year>2011</year>
+ <year>2004</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -31,6 +31,75 @@
<p>This document describes the changes made to the asn1 application.</p>
+<section><title>Asn1 1.8</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Encoding and decoding of integer ranges can now be done
+ with an upper bound larger than the previous limit of
+ 16^10. The new upper bound in per encoding and decodings
+ for constrained whole numbers is 2^2040 (close to 16^508)</p>
+ <p>
+ Own Id: OTP-10128</p>
+ </item>
+ <item>
+ <p>
+ Per encoding/decoding now works correctly for single
+ value subtyping of an integer type where a subtype is a
+ predefined value. Previously a predefined value could
+ cause a non-valid range-check in the generated Erlang
+ code for per encoding/decoding due to a bug in the
+ constraint checking.</p>
+ <p>
+ Own Id: OTP-10139</p>
+ </item>
+ <item>
+ <p>
+ Fix typo error in selected decode function (Thanks to
+ Artem Teslenko)</p>
+ <p>
+ Own Id: OTP-10152</p>
+ </item>
+ <item>
+ <p>
+ Better error indication when detecting unexpected tags
+ during decoding of BER encoded data.</p>
+ <p>
+ Own Id: OTP-10186</p>
+ </item>
+ <item>
+ <p>
+ asn1rt_check: Fix transform_to_EXTERNAL1990 for binary
+ input (Thanks to Harald Welte)</p>
+ <p>
+ Own Id: OTP-10233</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Add support for multiple ExtensionAdditionGroups</p>
+ <p>
+ Own Id: OTP-10058</p>
+ </item>
+ <item>
+ <p>
+ Add support for extensible enumeration types in n2n
+ generated functions.</p>
+ <p>
+ Own Id: OTP-10144</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Asn1 1.7</title>
<section><title>Improvements and New Features</title>
@@ -414,7 +483,7 @@
ENUMERATION type, the compilation will be terminated with an
error code.<br/>
Below follows an example on how to use the option from the command line with <c>erlc</c>:<br/>
- <c>erlc -bper_bin +optimize +driver +"{n2n,'CauseMisc'}" +"{n2n,'CausePcl'}" MyModyle.asn</c>
+ <c>erlc -bper+"{n2n,'CauseMisc'}" +"{n2n,'CausePcl'}" MyModyle.asn</c>
</p>
<p>
Own Id: OTP-8136 Aux Id: seq11347 </p>
diff --git a/lib/asn1/src/Makefile b/lib/asn1/src/Makefile
index 4bd49aa93b..03e18c565b 100644
--- a/lib/asn1/src/Makefile
+++ b/lib/asn1/src/Makefile
@@ -52,10 +52,9 @@ CT_MODULES= \
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_imm \
asn1ct_value \
asn1ct_tok \
asn1ct_parser2 \
@@ -63,7 +62,6 @@ CT_MODULES= \
RT_MODULES= \
asn1rt \
- asn1rt_per_bin \
asn1rt_ber_bin \
asn1rt_ber_bin_v2 \
asn1rt_per_bin_rt2ct \
@@ -138,13 +136,13 @@ info:
# ----------------------------------------------------
$(EBIN)/asn1ct.$(EMULATOR):asn1ct.erl
- $(ERLC) -b$(EMULATOR) -o$(EBIN) $(ERL_COMPILE_FLAGS) -Dvsn=\"$(VSN)\" $<
+ $(V_ERLC) -b$(EMULATOR) -o$(EBIN) $(ERL_COMPILE_FLAGS) -Dvsn=\"$(VSN)\" $<
$(APP_TARGET): $(APP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
# ----------------------------------------------------
@@ -166,11 +164,32 @@ release_spec: opt
release_docs_spec:
+#
+# Dependencies
+#
-
-
-
-
-
-
-
+$(EBIN)/asn1_app.beam: asn1_app.erl
+$(EBIN)/asn1_db.beam: asn1_db.erl
+$(EBIN)/asn1ct.beam: asn1ct.erl asn1_records.hrl
+$(EBIN)/asn1ct_check.beam: asn1ct_check.erl asn1_records.hrl
+$(EBIN)/asn1ct_constructed_ber_bin_v2.beam: asn1ct_constructed_ber_bin_v2.erl \
+ asn1_records.hrl
+$(EBIN)/asn1ct_constructed_per.beam: asn1ct_constructed_per.erl asn1_records.hrl
+$(EBIN)/asn1ct_gen.beam: asn1ct_gen.erl asn1_records.hrl
+$(EBIN)/asn1ct_gen_ber_bin_v2.beam: asn1ct_gen_ber_bin_v2.erl asn1_records.hrl
+$(EBIN)/asn1ct_gen_per.beam: asn1ct_gen_per.erl asn1_records.hrl
+$(EBIN)/asn1ct_gen_per_rt2ct.beam: asn1ct_gen_per_rt2ct.erl asn1_records.hrl
+$(EBIN)/asn1ct_imm.beam: asn1ct_imm.erl
+$(EBIN)/asn1ct_name.beam: asn1ct_name.erl
+$(EBIN)/asn1ct_parser2.beam: asn1ct_parser2.erl asn1_records.hrl
+$(EBIN)/asn1ct_pretty_format.beam: asn1ct_pretty_format.erl
+$(EBIN)/asn1ct_table.beam: asn1ct_table.erl
+$(EBIN)/asn1ct_tok.beam: asn1ct_tok.erl
+$(EBIN)/asn1ct_value.beam: asn1ct_value.erl asn1_records.hrl
+$(EBIN)/asn1rt.beam: asn1rt.erl
+$(EBIN)/asn1rt_ber_bin.beam: asn1rt_ber_bin.erl asn1_records.hrl
+$(EBIN)/asn1rt_ber_bin_v2.beam: asn1rt_ber_bin_v2.erl
+$(EBIN)/asn1rt_check.beam: asn1rt_check.erl
+$(EBIN)/asn1rt_nif.beam: asn1rt_nif.erl
+$(EBIN)/asn1rt_per_bin_rt2ct.beam: asn1rt_per_bin_rt2ct.erl asn1_records.hrl
+$(EBIN)/asn1rt_uper_bin.beam: asn1rt_uper_bin.erl asn1_records.hrl
diff --git a/lib/asn1/src/asn1.app.src b/lib/asn1/src/asn1.app.src
index 09144ba2f7..64b33a8a30 100644
--- a/lib/asn1/src/asn1.app.src
+++ b/lib/asn1/src/asn1.app.src
@@ -3,7 +3,6 @@
{vsn, "%VSN%"},
{modules, [
asn1rt,
- asn1rt_per_bin,
asn1rt_per_bin_rt2ct,
asn1rt_uper_bin,
asn1rt_ber_bin,
diff --git a/lib/asn1/src/asn1ct.erl b/lib/asn1/src/asn1ct.erl
index 99755a95a6..98877320a0 100644
--- a/lib/asn1/src/asn1ct.erl
+++ b/lib/asn1/src/asn1ct.erl
@@ -85,14 +85,8 @@
compile(File) ->
compile(File,[]).
-compile(File,Options) when is_list(Options) ->
- case lists:member(driver, Options) of %% remove me in R16A!
- true ->
- io:format("Warning: driver option is obsolete and will be removed in R16A, use nif instead!");
- false ->
- ok
- end,
- Options1 = optimize_ber_bin(Options),
+compile(File, Options0) when is_list(Options0) ->
+ Options1 = translate_options(Options0),
Options2 = includes(File,Options1),
Includes = strip_includes(Options2),
in_process(fun() -> compile_proc(File, Includes, Options2) end).
@@ -833,7 +827,7 @@ generate({true,{M,_Module,GenTOrV}},OutFile,EncodingRule,Options) ->
asn1ct_table:new(check_functions),
%% create decoding function names and taglists for partial decode
- case specialized_decode_prepare(EncodingRule,M,GenTOrV,Options) of
+ case (catch specialized_decode_prepare(EncodingRule,M,GenTOrV,Options)) of
{error, Reason} -> warning("Error in configuration file: ~n~p~n",
[Reason], Options,
"Error in configuration file");
@@ -852,8 +846,8 @@ generate({true,{M,_Module,GenTOrV}},OutFile,EncodingRule,Options) ->
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
+ erase(tlv_format), % used in ber
+ erase(class_default_type),% used in ber
asn1ct_table:delete(check_functions),
case Result of
{error,_} ->
@@ -876,14 +870,13 @@ parse_and_save(Module,S) ->
Options = S#state.options,
SourceDir = S#state.sourcedir,
Includes = [I || {i,I} <-Options],
- Options1 = optimize_ber_bin(Options),
-
+
case get_input_file(Module,[SourceDir|Includes]) of
%% search for asn1 source
{file,SuffixedASN1source} ->
- case dbfile_uptodate(SuffixedASN1source,Options1) of
+ case dbfile_uptodate(SuffixedASN1source,Options) of
false ->
- parse_and_save1(S,SuffixedASN1source,Options1,Includes);
+ parse_and_save1(S,SuffixedASN1source,Options,Includes);
_ -> ok
end;
Err ->
@@ -1065,9 +1058,9 @@ get_file_list1(Stream,Dir,Includes,Acc) ->
end.
get_rule(Options) ->
- case [Rule ||Rule <-[per,ber,ber_bin,ber_bin_v2,per_bin,uper_bin],
- Opt <- Options,
- Rule==Opt] of
+ case [Rule || Rule <- [ber,per,uper],
+ Opt <- Options,
+ Rule =:= Opt] of
[Rule] ->
Rule;
[Rule|_] ->
@@ -1079,19 +1072,34 @@ get_rule(Options) ->
get_runtime_mod(Options) ->
RtMod1=
case get_rule(Options) of
- per -> ["asn1rt_per_bin.erl"];
- ber -> ["asn1rt_ber_bin.erl"];
- per_bin ->
- case lists:member(optimize,Options) of
- true -> ["asn1rt_per_bin_rt2ct.erl"];
- _ -> ["asn1rt_per_bin.erl"]
- end;
- ber_bin -> ["asn1rt_ber_bin.erl"];
- ber_bin_v2 -> ["asn1rt_ber_bin_v2.erl"];
- uper_bin -> ["asn1rt_uper_bin.erl"]
+ per -> "asn1rt_per_bin_rt2ct.erl";
+ ber -> ["asn1rt_ber_bin_v2.erl"];
+ uper -> ["asn1rt_uper_bin.erl"]
end,
RtMod1++["asn1rt_check.erl","asn1rt.erl"].
-
+
+%% translate_options(NewOptions) -> OldOptions
+%% Translate the new option names to the old option name.
+%% FIXME. We should rewrite all code to handle the new option names.
+
+translate_options([ber_bin|T]) ->
+ io:format("Warning: The option 'ber_bin' is now called 'ber'.\n"),
+ [ber|translate_options(T)];
+translate_options([per_bin|T]) ->
+ io:format("Warning: The option 'per_bin' is now called 'per'.\n"),
+ [per|translate_options(T)];
+translate_options([uper_bin|T]) ->
+ io:format("Warning: The option 'uper_bin' is now called 'uper'.\n"),
+ translate_options([uper|T]);
+translate_options([nif|T]) ->
+ io:format("Warning: The option 'nif' is no longer needed.\n"),
+ translate_options(T);
+translate_options([optimize|T]) ->
+ io:format("Warning: The option 'optimize' is no longer needed.\n"),
+ translate_options(T);
+translate_options([H|T]) ->
+ [H|translate_options(T)];
+translate_options([]) -> [].
erl_compile(OutFile,Options) ->
% io:format("Options:~n~p~n",[Options]),
@@ -1115,7 +1123,6 @@ remove_asn_flags(Options) ->
X /= optimize,
X /= compact_bit_string,
X /= debug,
- X /= keyed_list,
X /= asn1config,
X /= record_name_prefix].
@@ -1125,12 +1132,6 @@ debug_on(Options) ->
put(asndebug,true);
_ ->
true
- end,
- case lists:member(keyed_list,Options) of
- true ->
- put(asn_keyed_list,true);
- _ ->
- true
end.
igorify_options(Options) ->
@@ -1151,8 +1152,7 @@ generated_file(Name,Options) ->
end.
debug_off(_Options) ->
- erase(asndebug),
- erase(asn_keyed_list).
+ erase(asndebug).
outfile(Base, Ext, Opts) ->
@@ -1168,13 +1168,6 @@ outfile(Base, Ext, Opts) ->
lists:concat([Obase,".",Ext])
end.
-optimize_ber_bin(Options) ->
- case {lists:member(optimize,Options),lists:member(ber_bin,Options)} of
- {true,true} ->
- [ber_bin_v2|Options--[ber_bin]];
- _ -> Options
- end.
-
includes(File,Options) ->
Options2 = include_append(".", Options),
Options3 = include_append(filename:dirname(File), Options2),
@@ -1284,12 +1277,7 @@ make_erl_options(Opts) ->
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];
- uper_bin -> [uper_bin]
+ _ -> [OutputType] % pass through
end,
Options++[errors, {cwd, Cwd}, {outdir, Outdir}|
@@ -1300,35 +1288,35 @@ pretty2(Module,AbsFile) ->
{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)]),
+ 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",
+ 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",
+ 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",
+ 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",
+ 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",
+ 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",
+ lists:foreach(fun(T)-> io:format(F,"~s.\n",
[asn1ct_pretty_format:term(asn1_db:dbget(Module,T))])
end,ObjectSets).
start() ->
@@ -1400,8 +1388,7 @@ test_value(Module, Type, Value) ->
in_process(fun() ->
case catch encode(Module, Type, Value) of
{ok, Bytes} ->
- M = to_atom(Module),
- NewBytes = prepare_bytes(M:encoding_rule(), Bytes),
+ NewBytes = prepare_bytes(Bytes),
case decode(Module, Type, NewBytes) of
{ok, Value} ->
{ok, {Module, Type, Value}};
@@ -1452,18 +1439,8 @@ check(Module, Includes) ->
end
end.
-to_atom(Term) when is_list(Term) -> list_to_atom(Term);
-to_atom(Term) when is_atom(Term) -> Term.
-
-prepare_bytes(ber, Bytes) -> lists:flatten(Bytes);
-prepare_bytes(ber_bin, Bytes) when is_binary(Bytes) -> Bytes;
-prepare_bytes(ber_bin, Bytes) -> list_to_binary(Bytes);
-prepare_bytes(ber_bin_v2, Bytes) when is_binary(Bytes) -> Bytes;
-prepare_bytes(ber_bin_v2, Bytes) -> list_to_binary(Bytes);
-prepare_bytes(per, Bytes) -> lists:flatten(Bytes);
-prepare_bytes(per_bin, Bytes) when is_binary(Bytes) -> Bytes;
-prepare_bytes(per_bin, Bytes) -> list_to_binary(Bytes);
-prepare_bytes(uper_bin, Bytes) -> Bytes.
+prepare_bytes(Bytes) when is_binary(Bytes) -> Bytes;
+prepare_bytes(Bytes) -> list_to_binary(Bytes).
vsn() ->
?vsn.
@@ -1504,7 +1481,7 @@ specialized_decode_prepare(Erule,M,TsAndVs,Options) ->
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 is_tuple(TsAndVs) ->
+partial_decode_prepare(ber,M,TsAndVs,Options) when is_tuple(TsAndVs) ->
%% read configure file
ModName =
diff --git a/lib/asn1/src/asn1ct_check.erl b/lib/asn1/src/asn1ct_check.erl
index 494a2eddd9..dd77085c39 100644
--- a/lib/asn1/src/asn1ct_check.erl
+++ b/lib/asn1/src/asn1ct_check.erl
@@ -61,13 +61,13 @@
-define(TAG_PRIMITIVE(Num),
case S#state.erule of
- ber_bin_v2 ->
+ ber ->
#tag{class='UNIVERSAL',number=Num,type='IMPLICIT',form=0};
_ -> []
end).
-define(TAG_CONSTRUCTED(Num),
case S#state.erule of
- ber_bin_v2 ->
+ ber ->
#tag{class='UNIVERSAL',number=Num,type='IMPLICIT',form=32};
_ -> []
end).
@@ -3262,7 +3262,7 @@ check_type(S=#state{recordtopname=TopName},Type,Ts) when is_record(Ts,type) ->
inlined=IsInlined},
TestFun =
fun(Tref) ->
- {_,MaybeChoice} = get_referenced_type(S,Tref),
+ MaybeChoice = get_non_typedef(S, Tref),
case catch((MaybeChoice#typedef.typespec)#type.def) of
{'CHOICE',_} ->
maybe_illicit_implicit_tag(choice,Tag);
@@ -3347,7 +3347,7 @@ check_type(S=#state{recordtopname=TopName},Type,Ts) when is_record(Ts,type) ->
TempNewDef#newt{
type = check_externaltypereference(S,NewExt),
tag = case S#state.erule of
- ber_bin_v2 ->
+ ber ->
merge_tags(Ct,RefType#type.tag);
_ ->
Ct
@@ -3617,6 +3617,14 @@ check_type(S=#state{recordtopname=TopName},Type,Ts) when is_record(Ts,type) ->
check_type(_S,Type,Ts) ->
exit({error,{asn1,internal_error,Type,Ts}}).
+get_non_typedef(S, Tref0) ->
+ case get_referenced_type(S, Tref0) of
+ {_,#typedef{typespec=#type{def=#'Externaltypereference'{}=Tref}}} ->
+ get_non_typedef(S, Tref);
+ {_,Type} ->
+ Type
+ end.
+
%% tablecinf_choose. A SEQUENCE or SET may be inserted in another
%% SEQUENCE or SET by the COMPONENTS OF directive. If this inserted
%% type is a referenced type that already has been checked it already
@@ -4177,7 +4185,7 @@ check_constraint(S,{'SizeConstraint',Lb}) ->
check_constraint(S,{'SingleValue', L}) when is_list(L) ->
F = fun(A) -> resolv_value(S,A) end,
- {'SingleValue',lists:map(F,L)};
+ {'SingleValue',lists:sort(lists:map(F,L))};
check_constraint(S,{'SingleValue', V}) when is_integer(V) ->
Val = resolv_value(S,V),
@@ -4332,11 +4340,33 @@ permitted_alphabet_merge([C1|Rest],UorI,Acc) ->
%% 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 is_tuple(H) ->
+constraint_merge(St, Cs0) ->
+ Cs = constraint_merge_1(St, Cs0),
+ normalize_cs(Cs).
+
+normalize_cs([{'SingleValue',[V]}|Cs]) ->
+ [{'SingleValue',V}|normalize_cs(Cs)];
+normalize_cs([{'SingleValue',[_|_]=L0}|Cs]) ->
+ [H|T] = L = lists:usort(L0),
+ [case is_range(H, T) of
+ false -> {'SingleValue',L};
+ true -> {'ValueRange',{H,lists:last(T)}}
+ end|normalize_cs(Cs)];
+normalize_cs([{'ValueRange',{Sv,Sv}}|Cs]) ->
+ [{'SingleValue',Sv}|normalize_cs(Cs)];
+normalize_cs([{'ValueRange',{'MIN','MAX'}}|Cs]) ->
+ normalize_cs(Cs);
+normalize_cs(Other) -> Other.
+
+is_range(Prev, [H|T]) when Prev =:= H - 1 -> is_range(H, T);
+is_range(_, [_|_]) -> false;
+is_range(_, []) -> true.
+
+constraint_merge_1(_S, [H]=C) when is_tuple(H) ->
C;
-constraint_merge(_S,[]) ->
+constraint_merge_1(_S, []) ->
[];
-constraint_merge(S,C) ->
+constraint_merge_1(S, C) ->
%% skip all extension but the last extension
C1 = filter_extensions(C),
%% perform all internal level intersections, intersections first
@@ -4359,17 +4389,16 @@ constraint_merge(S,C) ->
%% 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)),
+ RestC = ordsets:subtract(ordsets:from_list(C3),
+ ordsets:from_list(SZs ++ VRs ++ SVs)),
%% get the least common combined constraint. That is the union of each
- %% deep costraint and merge of single value and value range constraints
- NewCs = combine_constraints(S,CombSV,CombVR,CombSZ++RestC),
- [X||X <- lists:flatten(NewCs),
- X /= intersection,
- X /= union].
+ %% deep constraint and merge of single value and value range constraints.
+ %% FIXME: Removing 'intersection' from the flattened list essentially
+ %% means that intersections are converted to unions!
+ Cs = combine_constraints(S, CombSV, CombVR, CombSZ++RestC),
+ [X || X <- lists:flatten(Cs),
+ X =/= intersection,
+ X =/= union].
%% constraint_union(S,C) takes a list of constraints as input and
%% merge them to a union. Unions are performed when two
@@ -4399,16 +4428,16 @@ constraint_union(_S,C) ->
constraint_union1(S,[A={'ValueRange',_},union,B={'ValueRange',_}|Rest],Acc) ->
AunionB = constraint_union_vr([A,B]),
- constraint_union1(S,Rest,Acc ++ AunionB);
+ constraint_union1(S, AunionB++Rest, Acc);
constraint_union1(S,[A={'SingleValue',_},union,B={'SingleValue',_}|Rest],Acc) ->
AunionB = constraint_union_sv(S,[A,B]),
constraint_union1(S,Rest,Acc ++ AunionB);
constraint_union1(S,[A={'SingleValue',_},union,B={'ValueRange',_}|Rest],Acc) ->
AunionB = union_sv_vr(S,A,B),
- constraint_union1(S,Rest,Acc ++ AunionB);
+ constraint_union1(S, AunionB++Rest, Acc);
constraint_union1(S,[A={'ValueRange',_},union,B={'SingleValue',_}|Rest],Acc) ->
AunionB = union_sv_vr(S,B,A),
- constraint_union1(S,Rest,Acc ++ AunionB);
+ constraint_union1(S, AunionB++Rest, Acc);
constraint_union1(S,[union|Rest],Acc) -> %skip when unsupported constraints
constraint_union1(S,Rest,Acc);
constraint_union1(S,[A|Rest],Acc) ->
@@ -4441,15 +4470,8 @@ constraint_union_vr(VR) ->
({_,{A1,_B1}},{_,{A2,_B2}}) when is_integer(A1),is_integer(A2),A1<A2 -> true;
({_,{A,B1}},{_,{A,B2}}) when B1=<B2->true;
(_,_)->false end,
- % sort and remove duplicates
- SortedVR = lists:sort(Fun,VR),
- RemoveDup = fun([],_) ->[];
- ([H],_) -> [H];
- ([H,H|T],F) -> F([H|T],F);
- ([H|T],F) -> [H|F(T,F)]
- end,
-
- constraint_union_vr(RemoveDup(SortedVR,RemoveDup),[]).
+ SortedVR = lists:usort(Fun,VR),
+ constraint_union_vr(SortedVR, []).
constraint_union_vr([],Acc) ->
lists:reverse(Acc);
@@ -4459,8 +4481,8 @@ 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([{_,{Lb2,Ub2}}|Rest], [{_,{Lb1,Ub1}}|Acc])
+ when Ub1 =< Lb2, Ub1 < Ub2 ->
constraint_union_vr(Rest,[{'ValueRange',{Lb1,Ub2}}|Acc]);
constraint_union_vr([{_,{_,Ub2}}|Rest],A=[{_,{_,Ub1}}|_Acc]) when Ub2=<Ub1->
constraint_union_vr(Rest,A);
@@ -4581,9 +4603,11 @@ constraint_intersection(_S,C) ->
constraint_intersection1(S,[A,intersection,B|Rest],Acc) ->
AisecB = c_intersect(S,A,B),
- constraint_intersection1(S,Rest,AisecB++Acc);
+ constraint_intersection1(S, AisecB++Rest, Acc);
constraint_intersection1(S,[A|Rest],Acc) ->
constraint_intersection1(S,Rest,[A|Acc]);
+constraint_intersection1(_, [], [C]) ->
+ C;
constraint_intersection1(_,[],Acc) ->
lists:reverse(Acc).
@@ -5289,7 +5313,7 @@ iof_associated_type(S,[]) ->
AssociateSeq = iof_associated_type1(S,[]),
Tag =
case S#state.erule of
- ber_bin_v2 ->
+ ber ->
[?TAG_CONSTRUCTED(?N_INSTANCE_OF)];
_ -> []
end,
@@ -5320,7 +5344,7 @@ iof_associated_type1(S,C) ->
end,
{ObjIdTag,C1TypeTag}=
case S#state.erule of
- ber_bin_v2 ->
+ ber ->
{[{'UNIVERSAL',8}],
[#tag{class='UNIVERSAL',
number=6,
@@ -5551,8 +5575,9 @@ complist_as_tuple(_Per,[],Acc,Ext,_Acc2,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]).
+is_erule_per(per) -> true;
+is_erule_per(uper) -> true;
+is_erule_per(ber) -> false.
expand_components(S, [{'COMPONENTS OF',Type}|T]) ->
CompList = expand_components2(S,get_referenced_type(S,Type#type.def)),
@@ -5641,7 +5666,7 @@ check_set(S,Type,Components) ->
{true,_} ->
{Sorted,SortedComponents} = sort_components(der,S,NewComponents),
{Sorted,TableCInf,SortedComponents};
- {_,PER} when PER =:= per; PER =:= per_bin; PER =:= uper_bin ->
+ {_,PER} when PER =:= per; PER =:= uper ->
{Sorted,SortedComponents} = sort_components(per,S,NewComponents),
{Sorted,TableCInf,SortedComponents};
_ ->
@@ -5765,7 +5790,7 @@ sort_universal_type(Components) ->
decode_type(I) when is_integer(I) ->
I;
decode_type(T) ->
- asn1ct_gen_ber:decode_type(T).
+ asn1ct_gen_ber_bin_v2:decode_type(T).
untagged_choice(_S,[#'ComponentType'{typespec=#type{tag=[],def={'CHOICE',_}}}|_Rest]) ->
true;
@@ -6884,16 +6909,16 @@ get_taglist(S,{ObjCl,FieldNameList}) when is_record(ObjCl,objectclass),
{TypeFieldName,_} when is_atom(TypeFieldName) -> []%should check if allowed
end;
get_taglist(S,Def) ->
- case lists:member(S#state.erule,[ber_bin_v2]) of
- false ->
+ case S#state.erule of
+ ber ->
+ [];
+ _ ->
case Def of
'ASN1_OPEN_TYPE' -> % open_type has no UNIVERSAL tag as such
[];
_ ->
[asn1ct_gen:def_to_tag(Def)]
- end;
- _ ->
- []
+ end
end.
get_taglist1(S,[#'ComponentType'{name=_Cname,tags=TagL}|Rest]) when is_list(TagL) ->
diff --git a/lib/asn1/src/asn1ct_constructed_ber.erl b/lib/asn1/src/asn1ct_constructed_ber.erl
deleted file mode 100644
index 360de77663..0000000000
--- a/lib/asn1/src/asn1ct_constructed_ber.erl
+++ /dev/null
@@ -1,1596 +0,0 @@
-%%
-%% %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(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,get_record_name_prefix/0]).
-
-% 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 is_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,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;
- {Rl,El} -> Rl ++ El;
- _ -> CompList
- end,
- EncObj =
- case TableConsInfo of
- #simpletableattributes{usedclassfield=Used,
- uniqueclassfield=Unique} when Used /= Unique ->
- false;
- %% ObjectSetRef, name of the object set in constraints
- %%
- %%{ObjectSetRef,AttrN,N,UniqueFieldName}
- #simpletableattributes{objectsetname=ObjectSetRef,
- c_name=AttrN,
- c_index=N,
- usedclassfield=UniqueFieldName,
- uniqueclassfield=UniqueFieldName,
- valueindex=ValueIndex
- } ->
- OSDef =
- case ObjectSetRef 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}),
- {ObjSetMod,ObjSetName} =
- case ObjectSetRef of
- {M,O} ->
- {{asis,M},O};
- O ->
- {"?MODULE",O}
- end,
- emit({" ",ObjSetMod,":'getenc_",ObjSetName,"'(",{asis,UniqueFieldName},
- ", ",nl}),
-% emit({indent(35),"?RT_BER:cindex(",N+1,", Val,",
-% {asis,AttrN},")),",nl}),
- Length = fun(X,_LFun) when is_atom(X) ->
- length(atom_to_list(X));
- (X,_LFun) when is_list(X) ->
- length(X);
- ({X1,X2},LFun) ->
- LFun(X1,LFun) + LFun(X2,LFun)
- end,
- emit([indent(10+Length(ObjectSetRef,Length)),
- "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("asn1rt_check: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 is_record(D,type) ->
- asn1ct_name:start(),
- asn1ct_name:new(tag),
- #'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};
- {Rl,El} -> {Rl ++ El, Rl ++ El};
- _ -> {CList,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 {asn1ct_gen:get_constraint(CT#type.constraint,componentrelation),CT#type.tablecinf} of
-% case {CT#type.constraint,CT#type.tablecinf} of
- {no,[{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,
- RecordName = lists:concat([get_record_name_prefix(),asn1ct_gen:list2rname(Typename)]),
- case gen_dec_sequence_call(Erules,Typename,CompList2,Ext,DecObjInf) of
- no_terms -> % an empty sequence
- emit([nl,nl]),
- demit({"Result = "}), %dbg
- %% return value as record
- asn1ct_name:new(rb),
- emit([" {{'",RecordName,"'}, ",{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),
- {ObjSetMod,ObjSetName} =
- case ObjSet of
- {M,O} ->
- {{asis,M},O};
- _ ->
- {"?MODULE",ObjSet}
- end,
- emit([DecObj," =",nl," ",ObjSetMod,":'getdec_",ObjSetName,"'(",
-% {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 | extensible
- end,
- emit([" {",{next,bytes},",",{curr,rb},"} = ?RT_BER:restbytes2(RemBytes, ",
- {curr,bytes},",",ExtStatus,"),",nl]),
- asn1ct_name:new(rb),
- case Typename of
- ['EXTERNAL'] ->
- emit([" OldFormat={'",RecordName,
- "', "]),
- mkvlist(asn1ct_name:all(term)),
- emit(["},",nl]),
- emit([" ASN11994Format =",nl,
- " asn1rt_check:transform_to_EXTERNAL1994",
- "(OldFormat),",nl]),
- emit([" {ASN11994Format,",{next,bytes},", "]);
- _ ->
- emit([" {{'",RecordName,"', "]),
- 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 is_record(D,type) ->
- gen_encode_sequence(Erules,Typename,D).
-
-gen_decode_set(Erules,Typename,D) when is_record(D,type) ->
- asn1ct_name:start(),
- asn1ct_name:clear(),
- asn1ct_name:new(term),
- asn1ct_name:new(tag),
- #'SET'{components=TCompList0} = D#type.def,
-
- %% filter away extensionAdditiongroup markers
- TCompList = filter_complist(TCompList0),
- Ext = extensible(TCompList),
- ToOptional = fun(mandatory) ->
- 'OPTIONAL';
- (X) -> X
- end,
- CompList = case TCompList of
- {Rl1,El,Rl2} ->
- Rl1 ++ [X#'ComponentType'{prop=ToOptional(Y)}||X = #'ComponentType'{prop=Y}<-El] ++ Rl2;
- {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),
- {ExtFlatten1,ExtFlatten2} =
- case Ext of
- noext -> {"",""};
- _ -> {"lists:flatten(",")"}
- end,
- emit([" 'dec_",asn1ct_gen:list2name(Typename),
- "__result__'(lists:sort(",ExtFlatten1,"SetTerm",ExtFlatten2,"), 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),"_ -> ",nl]),
- case Ext of
- noext ->
- emit([indent(9),"{[], Bytes,0}",nl]);
- _ ->
- asn1ct_name:new(rbCho),
- emit([indent(9),"{RestBytes, ",{curr,rbCho},
- "} = ?RT_BER:skipvalue(Bytes),",nl,
- indent(9),"{[], RestBytes, ",{curr,rbCho},"}",nl])
- end,
- 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}),
- RecordName = lists:concat([get_record_name_prefix(),
- asn1ct_gen:list2rname(Typename)]),
- case gen_dec_set_result(Erules,Typename,CompList) of
- no_terms ->
- %% return value as record
- asn1ct_name:new(rb),
- emit({" {{'",RecordName,"'}, Bytes, Rb}.",nl});
- _ ->
- emit({nl," case ",{curr,termList}," of",nl}),
- emit({" [] -> {{'",RecordName,"', "}),
- 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 is_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 is_record(D,type) ->
- asn1ct_name:start(),
- asn1ct_name:clear(),
- {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 is_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 is_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 when SeqOrSetOf=='SET OF' ->
- emit([indent(3),
- "{asn1rt_check: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),
- mandatory,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 is_record(D,type) ->
- ChoiceTag = D#type.tag,
- {'CHOICE',CompList} = D#type.def,
- Ext = extensible(CompList),
- CompList1 = case CompList of
- {Rl1,El,Rl2} -> Rl1 ++ El ++ Rl2;
- {Rl,El} -> Rl ++ El;
- _ -> CompList
- end,
- gen_enc_choice(Erules,Typename,ChoiceTag,CompList1,Ext),
- emit({nl,nl}).
-
-gen_decode_choice(Erules,Typename,D) when is_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
- {Rl1,El,Rl2} -> Rl1 ++ El ++Rl2;
- {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,textual_order=Order}|Rest],Pos,Ext,EncObj) ->
- asn1ct_name:new(encBytes),
- asn1ct_name:new(encLen),
- CindexPos =
- case Order of
- undefined ->
- Pos;
- _ -> Order % der
- end,
- Element =
- case TopType of
- ['EXTERNAL'] ->
- io_lib:format("?RT_BER:cindex(~w,NewVal,~w)",[CindexPos+1,Cname]);
- _ ->
- io_lib:format("?RT_BER:cindex(~w,Val,~w)",[CindexPos+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)
- when is_list(CompList) ->
- gen_dec_sequence_call1(Erules,TopType, CompList, 1, Ext,DecObjInf,[],[]);
-gen_dec_sequence_call(Erules,TopType,CList,Ext,DecObjInf) ->
- gen_dec_sequence_call2(Erules,TopType,CList,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_call2(_Erules,_TopType,{[],[],[]},_Ext,_DecObjInf) ->
- no_terms;
-gen_dec_sequence_call2(Erules,TopType,{Root1,EList,Root2},_Ext,DecObjInf) ->
- {LA,ArgsAcc} =
- case gen_dec_sequence_call1(Erules,TopType,Root1++EList,1,
- extensible({Root1,EList}),DecObjInf,[],[]) of
- no_terms ->
- {[],[]};
- Res -> Res
- end,
- %% TagList is the tags of Root2 elements from the first up to and
- %% including the first mandatory element.
- TagList = get_root2_taglist(Root2,[]),
- emit({com,nl}),
- asn1ct_name:new(bytes),
- emit([" {",{next,bytes},", ",{next,rb},
- "} = ?RT_BER:skip_ExtensionAdditions(",
- {curr,bytes},", ",{asis,TagList},"),",nl]),
- asn1ct_name:new(rb),
- asn1ct_name:new(bytes),
- gen_dec_sequence_call1(Erules,TopType,Root2,
- length(Root1)+length(EList),noext,
- DecObjInf,LA,ArgsAcc).
-
-%% returns a list of tags of the elements in the component (second
-%% root) list up to and including the first mandatory tag. See 24.6 in
-%% X.680 (7/2002)
-get_root2_taglist([],Acc) ->
- lists:reverse(Acc);
-get_root2_taglist([#'ComponentType'{prop=Prop,typespec=Type}|Rest],Acc) ->
- FirstTag = fun([])->[];
- ([H|_T])->H#tag{class=asn1ct_gen_ber:decode_class(H#tag.class)}
- end(Type#type.tag),
- case Prop of
- mandatory ->
- %% match_tags/ may be used
- %% this is the last tag of interest -> return
- lists:reverse([FirstTag|Acc]);
- _ ->
- get_root2_taglist(Rest,[FirstTag|Acc])
- end.
-
-
-%%----------------------------
-%%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,
-
- Prop1 = case {Prop,Ext} of
- {_,{ext,Epos,_Root2pos}} when Pos < Epos ->
- Prop;
- {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) ->
- ExtCatch = case Ext of
- noext ->"";
- _ -> " catch"
- end,
- TagList = get_all_choice_tags(CompList),
- emit({indent(3),
- {curr,tagList}," = ",{asis,TagList},",",nl}),
- emit({indent(3),
- "case",ExtCatch," ?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) ->
- 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).
-
-
-
-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) ->
- 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 is_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};
- _ ->
- case Type#type.tablecinf of
- [{objfun,_}] -> {{no_attr,"ObjFun"},[]};
- _-> {false,[]}
- end
- end,
- gen_enc_line(Erules,TopType,Cname,Type,"element(2,Val)",9,
- mandatory,Assign,Encobj),
- case {Type#type.def,Encobj} of
- {#'ObjectClassFieldType'{},{no_attr,"ObjFun"}} ->
- emit({",",nl,indent(9),"{",{curr,encBytes},", ",
- {curr,encLen},"}"});
- _ -> ok
- 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 is_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 is_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=C,
- def=#'ObjectClassFieldType'{type={typefield,_}}},
- Element,Indent,OptOrMand=mandatory,EncObj)
- when is_list(Element) ->
- case asn1ct_gen:get_constraint(C,componentrelation) of
- {componentrelation,_,_} ->
- 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,
- ["{",{curr,encBytes},",",{curr,encLen},"} = "],
- EncObj)
- end;
- gen_enc_line(Erules,TopType,Cname,Type,Element,Indent,OptOrMand,EncObj)
- when is_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 is_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{def=#'ObjectClassFieldType'{type={typefield,_},
- fieldname=RefedFieldName}},
- {componentrelation,_,_}} ->
- {_LeadingAttrName,Fun} = EncObj,
- case RefedFieldName of
- {Name,RestFieldNames} when is_atom(Name),Name =/= notype ->
- case OptOrMand of
- mandatory -> ok;
- _ ->
- 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},", ",{curr,tmpLen},
- "} = "}),
- emit({"?RT_BER:encode_open_type(",{curr,tmpBytes},
- ",",{asis,Tag},"),",nl}),
- emit(IndDeep),
- emit({"{",{next,tmpBytes},", ",{curr,tmpLen},"}"})
- end;
- Err ->
- throw({asn1,{'internal error',Err}})
- end;
- _ ->
- 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);
- '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(Erules,TopType,Cname,WhatKind,dec,3),
- emit([DecFunName,", ",{asis,Tag}]);
- _ ->
- {DecFunName, _, _} =
- mkfunname(Erules,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} when CTag =/= [] -> % R9C-0.patch-34
- 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.
- ObjSetName = case ObjSet of
- {deep,OSName,_,_} ->
- OSName;
- _ -> ObjSet
- end,
- {[{ObjSetName,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),
- {ObjSetMod,ObjSetName} =
- case OSet of
- {M,O} ->
- {{asis,M},O};
- _ ->
- {"?MODULE",OSet}
- end,
- emit({",",nl,"ObjFun = ",ObjSetMod,":'getdec_",ObjSetName,"'(",
- {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 is_list(CompList) ->
- noext;
-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 = "%%-------------------------------------------------",
- 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(Erule,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(["fun '",Mod,"':'",DecOrEnc,"_",EType,"'/",Arity]),Mod,
- lists:concat(["'",DecOrEnc,"_",EType,"'"])};
- {constructed,bif} ->
- F =
- lists:concat(["fun '",DecOrEnc,"_",
- asn1ct_gen:list2name([Cname|TopType]),"'/",
- Arity]),
- {F, "?MODULE", F};
- 'ASN1_OPEN_TYPE' ->
- case Arity of
- 3 ->
- F = lists:concat(["fun(A,_,C) -> ?RT_BER:decode_open_type(",Erule,",A,C) end"]),
- {F, "?MODULE", F};
- 4 ->
- F = lists:concat(["fun(A,_,C,_) -> ?RT_BER:decode_open_type(",Erule,",A,C) end"]),
- {F, "?MODULE", F}
- end
- end.
-
-empty_lb(ber) ->
- "[]";
-empty_lb(ber_bin) ->
- "<<>>".
-
-rtmod(ber) ->
- list_to_atom(?RT_BER_BIN);
-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 is_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/asn1/src/asn1ct_constructed_ber_bin_v2.erl b/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl
index 2c4b44996d..78cb9297d8 100644
--- a/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl
+++ b/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl
@@ -32,7 +32,6 @@
-include("asn1_records.hrl").
-import(asn1ct_gen, [emit/1,demit/1,get_record_name_prefix/0]).
--import(asn1ct_constructed_ber,[match_tag/2]).
-define(ASN1CT_GEN_BER,asn1ct_gen_ber_bin_v2).
@@ -913,7 +912,7 @@ gen_dec_choice_cases(Erules,TopType, [H|T]) ->
[DecTag],Type}),
asn1ct:update_gen_state(namelist,Names),
emit([indent(4),{curr,res}," = ",
- match_tag(ber_bin,{FirstT#tag.class,FirstT#tag.number}),
+ match_tag(FirstT#tag.class, FirstT#tag.number),
" -> ",nl]),
emit([indent(8),"{",{asis,Cname},", {'",
asn1ct_gen:list2name([Cname|TopType]),"',",
@@ -928,7 +927,25 @@ gen_dec_choice_cases(Erules,TopType, [H|T]) ->
end,
gen_dec_choice_cases(Erules,TopType, T).
+match_tag(Class, TagNo) when is_integer(TagNo) ->
+ match_tag1(asn1ct_gen_ber_bin_v2:decode_class(Class), TagNo).
+match_tag1(Class, TagNo) when TagNo =< 30 ->
+ io_lib:format("<<~p:2,_:1,~p:5,_/binary>>", [Class bsr 6,TagNo]);
+match_tag1(Class, TagNo) ->
+ Octets = mk_object_val(TagNo),
+ io_lib:format("<<~p:2,_:1,31:5,~s,_/binary>>", [Class bsr 6,Octets]).
+
+mk_object_val(Val) when Val < 16#80 ->
+ integer_to_list(Val);
+mk_object_val(Val) ->
+ mk_object_val(Val bsr 7, [integer_to_list(Val band 16#7F)]).
+
+mk_object_val(0, Acc) ->
+ Acc;
+mk_object_val(Val, Acc) ->
+ I = integer_to_list((Val band 16#7F) bor 16#80),
+ mk_object_val(Val bsr 7, [I,","|Acc]).
%%---------------------------------------
%% Generate the encode/decode code
@@ -1493,10 +1510,6 @@ mkfuncname(TopType,Cname,WhatKind,Prefix,Suffix) ->
end.
empty_lb(ber) ->
- "[]";
-empty_lb(ber_bin) ->
- "<<>>";
-empty_lb(ber_bin_v2) ->
"<<>>".
value_match(Index,Value) when is_atom(Value) ->
diff --git a/lib/asn1/src/asn1ct_constructed_per.erl b/lib/asn1/src/asn1ct_constructed_per.erl
index 5f5138ef23..27070be966 100644
--- a/lib/asn1/src/asn1ct_constructed_per.erl
+++ b/lib/asn1/src/asn1ct_constructed_per.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -76,13 +76,9 @@ gen_encode_constructed(Erule,Typename,D) when is_record(D,type) ->
case {Optionals = optionals(to_textual_order(CompList)),CompList,
is_optimized(Erule)} of
{[],EmptyCL,_} when EmptyCL == {[],[],[]};EmptyCL == {[],[]};EmptyCL == [] ->
- emit(["%%Variable setting just to eliminate ",
- "compiler warning for unused vars!",nl,
- "_Val = ",{curr,val},",",nl]);
+ ok;
{[],_,_} ->
- emit([{next,val}," = ?RT_PER:list_to_record("]),
- emit(["'",asn1ct_gen:list2rname(Typename),"'"]),
- emit([", ",{curr,val},"),",nl]);
+ emit([{next,val}," = ",{curr,val},",",nl]);
{_,_,true} ->
gen_fixoptionals(Optionals),
FixOpts = param_map(fun(Var) ->
@@ -112,16 +108,17 @@ gen_encode_constructed(Erule,Typename,D) when is_record(D,type) ->
emit([
{next,val}," = case [X || X <- [",Elements,
"],X =/= asn1_NOVALUE] of",nl,
- "[] -> ",{curr,val},";",nl,
+ "[] -> setelement(",
+ {asis,ExtActualGroupPos+1},",",
+ {curr,val},",",
+ "asn1_NOVALUE);",nl,
"_ -> setelement(",{asis,ExtActualGroupPos+1},",",
{curr,val},",",
"{extaddgroup,", Elements,"})",nl,
"end,",nl]),
asn1ct_name:new(val)
end,
- lists:foreach(ExtGroupFun,ExtGroupPosLenList);
- _ -> % no extensionAdditionGroup
- ok
+ lists:foreach(ExtGroupFun,ExtGroupPosLenList)
end,
asn1ct_name:new(tmpval),
emit(["Extensions = ?RT_PER:fixextensions(",{asis,Ext},",",
@@ -157,7 +154,7 @@ gen_encode_constructed(Erule,Typename,D) when is_record(D,type) ->
emit([ObjectEncode," = ",nl]),
emit([" ",ObjSetMod,":'getenc_",ObjSetName,"'(",
{asis,UniqueFieldName},", ",nl]),
- El = make_element(N+1,asn1ct_gen:mk_var(asn1ct_name:curr(val)),AttrN),
+ El = make_element(N+1,asn1ct_gen:mk_var(asn1ct_name:curr(val))),
Length = fun(X,_LFun) when is_atom(X) ->
length(atom_to_list(X));
@@ -223,9 +220,74 @@ gen_decode_set(Erules,Typename,D) ->
gen_decode_sequence(Erules,Typename,D) ->
gen_decode_constructed(Erules,Typename,D).
-gen_decode_constructed(Erules,Typename,D) when is_record(D,type) ->
+gen_decode_constructed(Erule, Typename, #type{}=D) ->
+ Imm0 = gen_dec_constructed_imm(Erule, Typename, #type{}=D),
+ Imm = opt_imm(Imm0),
asn1ct_name:start(),
asn1ct_name:clear(),
+ emit_gen_dec_imm(Imm),
+ emit([".",nl,nl]).
+
+opt_imm(Imm0) ->
+ {Imm,_} = opt_imm_1(Imm0, unknown, []),
+ Imm.
+
+opt_imm_1([{imm,Imm0,F}|T], Al0, Acc) ->
+ {Imm,Al} = asn1ct_imm:optimize_alignment(Imm0, Al0),
+ opt_imm_1(T, Al, [{imm,Imm,F}|Acc]);
+opt_imm_1([ignore|T], Al, Acc) ->
+ opt_imm_1(T, Al, Acc);
+opt_imm_1([{ignore,_}=H|T], Al, Acc) ->
+ opt_imm_1(T, Al, [H|Acc]);
+opt_imm_1([{safe,ignore}|T], Al, Acc) ->
+ opt_imm_1(T, Al, Acc);
+opt_imm_1([{safe,_}=H|T], Al, Acc) ->
+ opt_imm_1(T, Al, [H|Acc]);
+opt_imm_1([{group,G0}|T], Al0, Acc) ->
+ {G,Al} = opt_imm_1(G0, Al0, []),
+ opt_imm_1(T, Al, [{group,G}|Acc]);
+opt_imm_1([Emit|T], _, Acc) when is_function(Emit, 1) ->
+ opt_imm_1(T, unknown, [Emit|Acc]);
+opt_imm_1([], Al, Acc) ->
+ {lists:reverse(Acc),Al}.
+
+emit_gen_dec_imm(L) ->
+ emit_gen_dec_imm(L, "", []).
+
+emit_gen_dec_imm([{ignore,Fun}|T], Sep, St0) ->
+ St = Fun(St0),
+ emit_gen_dec_imm(T, Sep, St);
+emit_gen_dec_imm([{group,L}|T], Sep, St0) ->
+ emit(Sep),
+ St = emit_gen_dec_imm_group(L, St0),
+ emit_gen_dec_imm(T, [com,nl], St);
+emit_gen_dec_imm([{imm,Imm,Emit}|T], Sep, St0) ->
+ emit(Sep),
+ St = Emit(Imm, St0),
+ emit_gen_dec_imm(T, [com,nl], St);
+emit_gen_dec_imm([{safe,Item}|T], Sep, St) ->
+ emit_gen_dec_imm([Item|T], Sep, St);
+emit_gen_dec_imm([Emit|T], Sep, St0) ->
+ emit(Sep),
+ St = Emit(St0),
+ emit_gen_dec_imm(T, [com,nl], St);
+emit_gen_dec_imm([], _, _) -> ok.
+
+emit_gen_dec_imm_group([H|T], St0) ->
+ St = emit_gen_dec_group_item(H, St0),
+ emit_gen_dec_imm_group(T, St);
+emit_gen_dec_imm_group([], St) -> St.
+
+emit_gen_dec_group_item({ignore,Fun}, St) ->
+ Fun(St);
+emit_gen_dec_group_item({imm,Imm,Fun}, St) ->
+ Fun(Imm, St);
+emit_gen_dec_group_item({safe,Item}, St) ->
+ emit_gen_dec_group_item(Item, St);
+emit_gen_dec_group_item(Emit, St) ->
+ Emit(St).
+
+gen_dec_constructed_imm(Erule, Typename, #type{}=D) ->
{CompList,TableConsInfo} =
case D#type.def of
#'SEQUENCE'{tablecinf=TCI,components=CL} ->
@@ -235,27 +297,19 @@ gen_decode_constructed(Erules,Typename,D) when is_record(D,type) ->
{CL,TCI} % the textual order is already taken care of
end,
Ext = extensible_dec(CompList),
- MaybeComma1 = case Ext of
- {ext,_Pos,_NumExt} ->
- gen_dec_extension_value("Bytes"),
- {",",nl};
- _ ->
- ""
- end,
+ EmitExt = case Ext of
+ {ext,_Pos,_NumExt} ->
+ gen_dec_extension_value();
+ _ -> ignore
+ 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} =
+ EmitOpt = case Optionals of
+ [] ->
+ ignore;
+ [_|_] ->
+ gen_dec_optionals(Optionals)
+ end,
+ ObjSetInfo =
case TableConsInfo of
%% {ObjectSet,AttrN,N,UniqueFieldName} ->%% N is index of attribute that determines constraint
#simpletableattributes{objectsetname=ObjectSet,
@@ -287,13 +341,19 @@ 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)),
- case asn1ct_name:all(term) of
- [] -> emit(MaybeComma2); % no components at all
- _ -> emit({com,nl})
- end,
+ {DecObjInf,_,_} = ObjSetInfo,
+ EmitComp = gen_dec_components_call(Erule, Typename, CompList,
+ DecObjInf, Ext, length(Optionals)),
+ EmitRest = fun({AccTerm,AccBytes}) ->
+ gen_dec_constructed_imm_2(Typename, CompList,
+ ObjSetInfo,
+ AccTerm, AccBytes)
+ end,
+ [EmitExt,EmitOpt|EmitComp++[{safe,EmitRest}]].
+
+gen_dec_constructed_imm_2(Typename, CompList,
+ ObjSetInfo, AccTerm, AccBytes) ->
+ {_,UniqueFName,ValueIndex} = ObjSetInfo,
case {AccTerm,AccBytes} of
{[],[]} ->
ok;
@@ -335,8 +395,7 @@ gen_decode_constructed(Erules,Typename,D) when is_record(D,type) ->
mkvlist(textual_order(to_encoding_order(CompList),asn1ct_name:all(term))),
emit("},")
end,
- emit({{curr,bytes},"}"}),
- emit({".",nl,nl}).
+ emit({{curr,bytes},"}"}).
textual_order([#'ComponentType'{textual_order=undefined}|_],TermList) ->
TermList;
@@ -518,10 +577,10 @@ gen_decode_sof(Erules,Typename,SeqOrSetOf,D) when is_record(D,type) ->
_ ->
""
end,
- gen_decode_length(SizeConstraint,
- is_optimized(Erules)),
- emit({"'dec_",asn1ct_gen:list2name(Typename),
- "_components'(Num, Bytes1, telltype",ObjFun,", []).",nl}),
+ {Num,Buf} = gen_decode_length(SizeConstraint, Erules),
+ emit([",",nl,
+ "'dec_",asn1ct_gen:list2name(Typename),
+ "_components'(",Num,", ",Buf,ObjFun,", []).",nl,nl]),
NewComponentType =
case ComponentType#type.def of
{'ENUMERATED',_,Component}->
@@ -530,40 +589,13 @@ gen_decode_sof(Erules,Typename,SeqOrSetOf,D) when is_record(D,type) ->
end,
gen_decode_sof_components(Erules,Typename,SeqOrSetOf,NewComponentType).
-%% Logic copied from asn1_per_bin_rt2ct:decode_constrained_number
-gen_decode_length({Lb,Ub},true) when Ub =< 65535, Lb >= 0 ->
- Range = Ub - Lb + 1,
- Call = if
- Range == 1 ->
- "{0,Bytes}";
- Range == 2 ->
- "?RT_PER:getbits(Bytes,1)";
- Range =< 4 ->
- "?RT_PER:getbits(Bytes,2)";
- Range =< 8 ->
- "?RT_PER:getbits(Bytes,3)";
- Range =< 16 ->
- "?RT_PER:getbits(Bytes,4)";
- Range =< 32 ->
- "?RT_PER:getbits(Bytes,5)";
- Range =< 64 ->
- "?RT_PER:getbits(Bytes,6)";
- Range =< 128 ->
- "?RT_PER:getbits(Bytes,7)";
- Range =< 255 ->
- "?RT_PER:getbits(Bytes,8)";
- Range =< 256 ->
- "?RT_PER:getoctets(Bytes,1)";
- Range =< 65536 ->
- "?RT_PER:getoctets(Bytes,2)";
- true ->
- ["exit({not_supported,{integer_range,",Range,"}}"]
- end,
- emit({nl,"{Val,Remain} = ",Call,",",nl}),
- emit({nl,"{Num,Bytes1} = {Val+",Lb,",Remain},",nl});
-gen_decode_length(SizeConstraint,_) ->
- emit({nl,"{Num,Bytes1} = ?RT_PER:decode_length(Bytes,",
- {asis,SizeConstraint},"),",nl}).
+is_aligned(per) -> true;
+is_aligned(uper) -> false.
+
+gen_decode_length(Constraint, Erule) ->
+ emit(["%% Length with constraint ",{asis,Constraint},nl]),
+ Imm = asn1ct_imm:per_dec_length(Constraint, true, is_aligned(Erule)),
+ asn1ct_imm:dec_slim_cg(Imm, "Bytes").
gen_encode_sof_components(Erule,Typename,SeqOrSetOf,Cont) ->
{ObjFun,ObjFun_Var} =
@@ -585,8 +617,7 @@ gen_encode_sof_components(Erule,Typename,SeqOrSetOf,Cont) ->
Conttype = asn1ct_gen:get_inner(Cont#type.def),
Currmod = get(currmod),
- Ctgenmod = list_to_atom(lists:concat(["asn1ct_gen_",per,
- asn1ct_gen:rt2ct_suffix()])),
+ Ctgenmod = asn1ct_gen:ct_gen_module(Erule),
case asn1ct_gen:type(Conttype) of
{primitive,bif} ->
gen_encode_prim_wrapper(Ctgenmod,Erule,Cont,false,"H");
@@ -616,16 +647,15 @@ gen_decode_sof_components(Erule,Typename,SeqOrSetOf,Cont) ->
{"",""}
end,
emit({"'dec_",asn1ct_gen:list2name(Typename),
- "_components'(0, Bytes, _",ObjFun_Var,", Acc) ->",nl,
+ "_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}),
+ "_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()])),
+ Ctgenmod = asn1ct_gen:ct_gen_module(Erule),
CurrMod = get(currmod),
case asn1ct_gen:type(Conttype) of
{primitive,bif} ->
@@ -649,7 +679,7 @@ gen_decode_sof_components(Erule,Typename,SeqOrSetOf,Cont) ->
emit({"'dec_",Conttype,"'(Bytes,telltype),",nl})
end,
emit({indent(3),"'dec_",asn1ct_gen:list2name(Typename),
- "_components'(Num-1, Remain, telltype",ObjFun,", [Term|Acc]).",nl}).
+ "_components'(Num-1, Remain",ObjFun,", [Term|Acc]).",nl}).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -720,10 +750,25 @@ extgrouppos([_|T],ActualPos,VirtualPos,Len,Acc) ->
extgrouppos(T,ActualPos,VirtualPos,Len+1,Acc).
-
-gen_dec_extension_value(_) ->
- emit({"{Ext,",{next,bytes},"} = ?RT_PER:getext(",{curr,bytes},")"}),
- asn1ct_name:new(bytes).
+gen_dec_extension_value() ->
+ Imm0 = {get_bits,1,[1]},
+ E = fun(Imm, _) ->
+ emit(["{Ext,",{next,bytes},"} = "]),
+ BytesVar = asn1ct_gen:mk_var(asn1ct_name:curr(bytes)),
+ asn1ct_imm:dec_code_gen(Imm, BytesVar),
+ asn1ct_name:new(bytes)
+ end,
+ {imm,Imm0,E}.
+
+gen_dec_optionals(Optionals) ->
+ Imm0 = {get_bits,length(Optionals),[1]},
+ E = fun(Imm, _) ->
+ BytesVar = asn1ct_gen:mk_var(asn1ct_name:curr(bytes)),
+ emit(["{Opt,",{next,bytes},"} = "]),
+ asn1ct_imm:dec_code_gen(Imm, BytesVar),
+ asn1ct_name:new(bytes)
+ end,
+ {imm,Imm0,E}.
gen_fixoptionals([{Pos,Def}|R]) ->
asn1ct_name:new(fixopt),
@@ -891,7 +936,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,asn1ct_gen:mk_var(asn1ct_name:curr(val)),Cname),
+ Element = make_element(Pos+1,asn1ct_gen:mk_var(asn1ct_name:curr(val))),
emit({"case ",Element," of",nl}),
% emit({"asn1_DEFAULT -> [];",nl}),
emit({"DFLT when DFLT == asn1_DEFAULT; DFLT == ",{asis,DefaultVal}," -> [];",nl}),
@@ -911,7 +956,7 @@ gen_enc_component_optional(Erule,TopType,Cname,
components=_ExtGroupCompList}},
Pos,DynamicEnc,Ext) when is_integer(Number) ->
- Element = make_element(Pos+1,asn1ct_gen:mk_var(asn1ct_name:curr(val)),Cname),
+ Element = make_element(Pos+1,asn1ct_gen:mk_var(asn1ct_name:curr(val))),
emit({"case ",Element," of",nl}),
emit({"asn1_NOVALUE -> [];",nl}),
@@ -924,7 +969,7 @@ gen_enc_component_optional(Erule,TopType,Cname,
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,asn1ct_gen:mk_var(asn1ct_name:curr(val)),Cname),
+ Element = make_element(Pos+1,asn1ct_gen:mk_var(asn1ct_name:curr(val))),
emit({"case ",Element," of",nl}),
emit({"asn1_NOVALUE -> [];",nl}),
@@ -944,11 +989,10 @@ gen_enc_component_mandatory(Erule,TopType,Cname,Type,Pos,DynamicEnc,Ext) ->
gen_enc_line(Erule,TopType,Cname,Type,[],Pos,DynamicEnc,Ext).
gen_enc_line(Erule,TopType, Cname, Type, [], Pos,DynamicEnc,Ext) ->
- Element = make_element(Pos+1,asn1ct_gen:mk_var(asn1ct_name:curr(val)),Cname),
+ Element = make_element(Pos+1,asn1ct_gen:mk_var(asn1ct_name:curr(val))),
gen_enc_line(Erule,TopType,Cname,Type,Element, Pos,DynamicEnc,Ext);
gen_enc_line(Erule,TopType,Cname,Type,Element, _Pos,DynamicEnc,Ext) ->
- Ctgenmod = list_to_atom(lists:concat(["asn1ct_gen_",per,
- asn1ct_gen:rt2ct_suffix()])),
+ Ctgenmod = asn1ct_gen:ct_gen_module(Erule),
Atype =
case Type of
#type{def=#'ObjectClassFieldType'{type=InnerType}} ->
@@ -1038,53 +1082,80 @@ gen_enc_line(Erule,TopType,Cname,Type,Element, _Pos,DynamicEnc,Ext) ->
emit("))");
_ -> true
end.
-gen_dec_components_call(Erule,TopType,{Root,ExtList},MaybeComma,
+
+gen_dec_components_call(Erule, TopType, {Root,ExtList},
+ DecInfObj, Ext, NumberOfOptionals) ->
+ gen_dec_components_call(Erule,TopType,{Root,ExtList,[]},
+ DecInfObj,Ext,NumberOfOptionals);
+gen_dec_components_call(Erule,TopType,CL={Root1,ExtList,Root2},
DecInfObj,Ext,NumberOfOptionals) ->
- gen_dec_components_call(Erule,TopType,{Root,ExtList,[]},MaybeComma,DecInfObj,Ext,NumberOfOptionals);
-gen_dec_components_call(Erule,TopType,CL={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++Root2, 1, OptTable,
- MaybeComma,DecInfObj,noext,[],[],
- NumberOfOptionals),
- emit([",",nl,"{Extensions,",{next,bytes},"} = "]),
- emit(["?RT_PER:getextension(Ext,",{curr,bytes},"),",nl]),
- asn1ct_name:new(bytes),
+ Init = {ignore,fun(_) -> {[],[]} end},
+ {EmitRoot,Tpos} =
+ gen_dec_comp_calls(Root1++Root2, Erule, TopType, OptTable,
+ DecInfObj, noext, NumberOfOptionals,
+ 1, []),
+ EmitGetExt = gen_dec_get_extension(Erule),
{extgrouppos,ExtGroupPosLen} = extgroup_pos_and_length(CL),
- NewExtList = wrap_extensionAdditionGroups(ExtList,ExtGroupPosLen),
- {_Epos,AccTermE,AccBytesE} =
- gen_dec_components_call1(Erule,TopType,NewExtList,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),
- {AccTerm++AccTermE,AccBytes++AccBytesE};
-
-gen_dec_components_call(Erule,TopType,CompList,MaybeComma,DecInfObj,
- Ext,NumberOfOptionals) ->
+ NewExtList = wrap_extensionAdditionGroups(ExtList, ExtGroupPosLen),
+ {EmitExts,_} = gen_dec_comp_calls(NewExtList, Erule, TopType, OptTable,
+ DecInfObj, Ext, NumberOfOptionals,
+ Tpos, []),
+ NumExtsToSkip = ext_length(ExtList),
+ Finish =
+ fun(St) ->
+ emit([{next,bytes},"= ?RT_PER:skipextensions(",{curr,bytes},",",
+ NumExtsToSkip+1,",Extensions)"]),
+ asn1ct_name:new(bytes),
+ St
+ end,
+ [Init] ++ EmitRoot ++ [EmitGetExt|EmitExts] ++ [Finish];
+gen_dec_components_call(Erule, TopType, CompList, DecInfObj,
+ Ext, NumberOfOptionals) ->
%% The type has no extensionmarker
OptTable = create_optionality_table(CompList),
- {_,AccTerm,AccBytes} =
- gen_dec_components_call1(Erule,TopType, CompList, 1, OptTable,
- MaybeComma,DecInfObj,Ext,[],[],
- NumberOfOptionals),
- {AccTerm,AccBytes}.
-
-
-gen_dec_components_call1(Erule,TopType,
- [C=#'ComponentType'{name=Cname,typespec=Type,prop=Prop,textual_order=TextPos}|Rest],
- Tpos,OptTable,MaybeComma,DecInfObj,Ext,AccTerm,AccBytes,NumberOfOptionals) ->
+ Init = {ignore,fun(_) -> {[],[]} end},
+ {Cs,_} = gen_dec_comp_calls(CompList, Erule, TopType, OptTable,
+ DecInfObj, Ext, NumberOfOptionals,
+ 1, []),
+ [Init|Cs].
+
+gen_dec_get_extension(Erule) ->
+ Imm0 = asn1ct_imm:per_dec_extension_map(is_aligned(Erule)),
+ E = fun(Imm, St) ->
+ emit([nl,"%% Extensions",
+ nl,
+ "{Extensions,",{next,bytes},"} = ",
+ "case Ext of",nl,
+ "0 -> {<<>>,",{curr,bytes},"};",nl,
+ "1 ->",nl]),
+ BytesVar = asn1ct_gen:mk_var(asn1ct_name:curr(bytes)),
+ {Dst,DstBuf} = asn1ct_imm:dec_slim_cg(Imm, BytesVar),
+ emit([com,nl,
+ "{",Dst,",",DstBuf,"}",nl,
+ "end"]),
+ asn1ct_name:new(bytes),
+ St
+ end,
+ {imm,Imm0,E}.
+
+gen_dec_comp_calls([C|Cs], Erule, TopType, OptTable, DecInfObj,
+ Ext, NumberOfOptionals, Tpos, Acc) ->
+ L = gen_dec_comp_call(C, Erule, TopType, Tpos, OptTable, DecInfObj,
+ Ext, NumberOfOptionals),
+ gen_dec_comp_calls(Cs, Erule, TopType, OptTable, DecInfObj,
+ Ext, NumberOfOptionals, Tpos+1, [L|Acc]);
+gen_dec_comp_calls([], _, _, _, _, _, _, Tpos, Acc) ->
+ {lists:append(lists:reverse(Acc)),Tpos}.
+
+gen_dec_comp_call(Comp, Erule, TopType, Tpos, OptTable, DecInfObj,
+ Ext, NumberOfOptionals) ->
+ #'ComponentType'{typespec=Type,prop=Prop,textual_order=TextPos} = Comp,
Pos = case Ext of
noext -> Tpos;
{ext,Epos,_Enum} -> Tpos - Epos + 1
end,
- emit(MaybeComma),
InnerType =
case Type#type.def of
#'ObjectClassFieldType'{type=InType} ->
@@ -1093,109 +1164,128 @@ gen_dec_components_call1(Erule,TopType,
asn1ct_gen:get_inner(Def)
end,
- case InnerType of
- #'Externaltypereference'{type=T} ->
- emit({nl,"%% attribute number ",TextPos," with type ",
- T,nl});
- IT when is_tuple(IT) ->
- emit({nl,"%% attribute number ",TextPos," with type ",
- element(2,IT),nl});
- _ ->
- emit({nl,"%% attribute number ",TextPos," with type ",
- InnerType,nl})
- end,
-
- IsMandatoryAndPredefinedTableC =
- fun(noext,mandatory,{"got objfun through args","ObjFun"}) ->
- true;
- (_,_,{"got objfun through args","ObjFun"}) ->
- false;
- (_,_,_) ->
- true
- end,
- case {InnerType,IsMandatoryAndPredefinedTableC(Ext,Prop,DecInfObj)} of
-%% {typefield,_} when Ext == noext, Prop == mandatory ->
- {{typefield,_},true} ->
- %% DecInfObj /= {"got objfun through args","ObjFun"} |
- %% (DecInfObj == {"got objfun through args","ObjFun"} &
- %% Ext == noext & Prop == mandatory)
- asn1ct_name:new(term),
- asn1ct_name:new(tmpterm),
- emit({"{",{curr,tmpterm},", ",{next,bytes},"} = "});
+ DispType = case InnerType of
+ #'Externaltypereference'{type=T} -> T;
+ IT when is_tuple(IT) -> element(2,IT);
+ _ -> InnerType
+ end,
+ Comment = fun(St) ->
+ emit([nl,"%% attribute number ",TextPos,
+ " with type ",DispType,nl]),
+ St
+ end,
+
+ Preamble =
+ case {InnerType,is_mandatory_predef_tab_c(Ext, Prop, DecInfObj)} of
+ {{typefield,_},true} ->
+ %% DecInfObj /= {"got objfun through args","ObjFun"} |
+ %% (DecInfObj == {"got objfun through args","ObjFun"} &
+ %% Ext == noext & Prop == mandatory)
+ fun(St) ->
+ asn1ct_name:new(term),
+ asn1ct_name:new(tmpterm),
+ emit(["{",{curr,tmpterm},", ",{next,bytes},"} = "]),
+ St
+ end;
%%{objectfield,_,_} when Ext == noext, Prop == mandatory ->
- {{objectfield,_,_},true} ->
- asn1ct_name:new(term),
- asn1ct_name:new(tmpterm),
- emit({"{",{curr,tmpterm},", ",{next,bytes},"} = "});
+ {{objectfield,_,_},true} ->
+ fun(St) ->
+ asn1ct_name:new(term),
+ asn1ct_name:new(tmpterm),
+ emit(["{",{curr,tmpterm},", ",{next,bytes},"} = "]),
+ St
+ end;
_ ->
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
- {noext,mandatory,_} -> ok; % generate nothing
- {noext,_,_} -> %% OPTIONAL or DEFAULT
- OptPos = get_optionality_pos(TextPos,OptTable),
- Element = io_lib:format("Opt band (1 bsl ~w)",[NumberOfOptionals - OptPos]),
- emit(["case ",Element," of",nl]),
- emit([" _Opt",TextPos," when _Opt",TextPos," > 0 ->"]);
- {_,_,false} -> %% extension element, not bitstring
- emit(["case Extensions of",nl]),
- emit([" _ when size(Extensions) >= ",Pos,",element(",Pos,",Extensions) == 1 ->",nl]);
- _ ->
- emit(["case Extensions of",nl]),
- emit([" <<_:",Pos-1,",1:1,_/bitstring>> when bit_size(Extensions) >= ",Pos," ->",nl])
- end,
- put(component_type,{true,C}),
- {TermVar,BytesVar} = gen_dec_line(Erule,TopType,Cname,Type,Tpos,DecInfObj,Ext,Prop),
- erase(component_type),
- case {Ext,Prop} of
- {noext,mandatory} -> true; % generate nothing
- {noext,_} ->
- emit([";",nl,"0 ->"]),
- emit(["{"]),
- gen_dec_component_no_val(Ext,Prop),
- emit({",",{curr,bytes},"}",nl}),
- emit([nl,"end"]);
- _ ->
- emit([";",nl,"_ ->",nl]),
- 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({"}"});
+ fun(St) ->
+ emit(["{{_,"]),
+ emit_extaddgroupTerms(term,ExtGroupCompList1),
+ emit(["}"]),
+ emit([",",{next,bytes},"} = "]),
+ St
+ end;
_ ->
- gen_dec_component_no_val(Ext,Prop)
- end,
- emit({",",{curr,bytes},"}",nl}),
- emit([nl,"end"])
- end,
- asn1ct_name:new(bytes),
- case Rest of
- [] ->
- {Tpos+1,AccTerm++TermVar,AccBytes++BytesVar};
- _ ->
- emit({com,nl}),
- gen_dec_components_call1(Erule,TopType,Rest,Tpos+1,OptTable,
- "",DecInfObj,Ext, AccTerm++TermVar,
- AccBytes++BytesVar,NumberOfOptionals)
- end;
+ fun(St) ->
+ asn1ct_name:new(term),
+ emit(["{",{curr,term}]),
+ emit([",",{next,bytes},"} = "]),
+ St
+ end
+ end
+ end,
-gen_dec_components_call1(_,_TopType,[],Pos,_OptTable,_,_,_,AccTerm,AccBytes,_NumberOfOptionals) ->
- {Pos,AccTerm,AccBytes}.
+ OptOrDef =
+ case {Ext,Prop} of
+ {noext,mandatory} ->
+ ignore;
+ {noext,_} -> %% OPTIONAL or DEFAULT
+ OptPos = get_optionality_pos(TextPos, OptTable),
+ Element = io_lib:format("Opt band (1 bsl ~w)",
+ [NumberOfOptionals - OptPos]),
+ fun(St) ->
+ emit(["case ",Element," of",nl]),
+ emit([" _Opt",TextPos," when _Opt",TextPos," > 0 ->"]),
+ St
+ end;
+ {{ext,_,_},_} -> %Extension
+ fun(St) ->
+ emit(["case Extensions of",nl,
+ " <<_:",Pos-1,",1:1,_/bitstring>> ->",nl]),
+ St
+ end
+ end,
+ Lines = gen_dec_line_imm(Erule, TopType, Comp, Tpos, DecInfObj, Ext),
+ Postamble =
+ case {Ext,Prop} of
+ {noext,mandatory} ->
+ ignore;
+ {noext,_} ->
+ fun(St) ->
+ emit([";",nl,"0 ->"]),
+ emit(["{"]),
+ gen_dec_component_no_val(Ext,Prop),
+ emit({",",{curr,bytes},"}",nl}),
+ emit([nl,"end"]),
+ St
+ end;
+ _ ->
+ fun(St) ->
+ emit([";",nl,"_ ->",nl]),
+ 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"]),
+ St
+ end
+ end,
+ AdvBuffer = {ignore,fun(St) ->
+ asn1ct_name:new(bytes),
+ St
+ end},
+ [{group,[{safe,Comment},{safe,Preamble},
+ {safe,OptOrDef}|Lines]++
+ [{safe,Postamble},{safe,AdvBuffer}]}].
+
+is_mandatory_predef_tab_c(noext, mandatory,
+ {"got objfun through args","ObjFun"}) ->
+ true;
+is_mandatory_predef_tab_c(_, _, {"got objfun through args","ObjFun"}) ->
+ false;
+is_mandatory_predef_tab_c(_,_,_) ->
+ true.
gen_dec_extaddGroup_no_val(Ext,[#'ComponentType'{prop=Prop}])->
gen_dec_component_no_val(Ext,Prop),
@@ -1215,9 +1305,14 @@ gen_dec_component_no_val({ext,_,_},mandatory) ->
emit({"asn1_NOVALUE"}).
-gen_dec_line(Erule,TopType,Cname,Type,Pos,DecInfObj,Ext,Prop) ->
- Ctgenmod = list_to_atom(lists:concat(["asn1ct_gen_",per,
- asn1ct_gen:rt2ct_suffix()])),
+gen_dec_line(Erule, TopType, Comp, Pos, DecInfObj, Ext) ->
+ Imm0 = gen_dec_line_imm(Erule, TopType, Comp, Pos, DecInfObj, Ext),
+ Init = {ignore,fun(_) -> {[],[]} end},
+ Imm = [{group,[Init|Imm0]}],
+ emit_gen_dec_imm(Imm).
+
+gen_dec_line_imm(Erule, TopType, Comp, Pos, DecInfObj, Ext) ->
+ #'ComponentType'{name=Cname,typespec=Type} = Comp,
Atype =
case Type of
#type{def=#'ObjectClassFieldType'{type=InnerType}} ->
@@ -1226,175 +1321,241 @@ gen_dec_line(Erule,TopType,Cname,Type,Pos,DecInfObj,Ext,Prop) ->
asn1ct_gen:get_inner(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_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),"{",{asis,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.
- if
- Ext == noext andalso Prop == mandatory ->
- ok;
- true ->
- asn1ct_name:new(tmpterm),
- asn1ct_name:new(tmpbytes),
- emit([nl," {",{curr,tmpterm},", ",{curr,tmpbytes},"} ="])
- end,
- {Name,RestFieldNames} =
- (Type#type.def)#'ObjectClassFieldType'.fieldname,
- emit(["?RT_PER:decode_open_type(",{curr,bytes},
- ", []),",nl]),
- if
- Ext == noext andalso Prop == mandatory ->
- emit([{curr,term}," =",nl," "]);
- true ->
- emit([" {"])
- end,
- emit(["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"]),
- if
- Ext == noext andalso Prop == mandatory ->
- ok;
- true ->
- emit([",",nl,{curr,tmpbytes},"}"])
- end,
- [];
- _ ->
- emit(["?RT_PER:decode_open_type(",{curr,bytes},
- ", [])"]),
- RefedFieldName =
- (Type#type.def)#'ObjectClassFieldType'.fieldname,
-
- [{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(Erule,Btype,
- BytesVar);
- _ ->
- Ctgenmod:gen_dec_prim(Erule,Type,
- BytesVar)
- end;
- 'ASN1_OPEN_TYPE' ->
- case Type#type.def of
- #'ObjectClassFieldType'{type=OpenType} ->
- Ctgenmod:gen_dec_prim(Erule,#type{def=OpenType},
- BytesVar);
- _ ->
- Ctgenmod:gen_dec_prim(Erule,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,
+ Pre = gen_dec_line_open_type(Erule, Ext, Pos),
+ Decode = gen_dec_line_special(Erule, Atype, TopType, Comp, DecInfObj, Ext),
+ Post =
+ fun({SaveBytes,Finish}) ->
+ {AccTerm,AccBytes} = Finish(),
+ #'ComponentType'{name=Cname} = Comp,
case DecInfObj of
- {Cname,{_,OSet,UniqueFName,ValIndex}} ->
- Term = asn1ct_gen:mk_var(asn1ct_name:curr(term)),
- ValueMatch = value_match(ValIndex,Term),
- {ObjSetMod,ObjSetName} =
- case OSet of
- {M,O} -> {{asis,M},O};
- _ -> {"?MODULE",OSet}
+ {Cname,ObjSet} ->
+ ObjSetRef =
+ case ObjSet of
+ {deep,OSName,_,_} ->
+ OSName;
+ _ -> ObjSet
end,
- emit({",",nl,"ObjFun = ",ObjSetMod,
- ":'getdec_",ObjSetName,"'(",
- {asis,UniqueFName},", ",ValueMatch,")"});
+ {AccTerm++[{ObjSetRef,Cname,
+ asn1ct_gen:mk_var(asn1ct_name:curr(term))}],
+ AccBytes++SaveBytes};
_ ->
- ok
- end,
- []
+ {AccTerm,AccBytes++SaveBytes}
+ end
end,
- case Ext of
- {ext,Ep2,_} when Pos >= Ep2 ->
- emit([", {TmpValx",Pos,",Trem",Pos,"}",nl,"end"]);
- _ -> true
- end,
- %% Prepare return value
+ [Pre,Decode,{safe,Post}].
+
+gen_dec_line_open_type(Erule, {ext,Ep,_}, Pos) when Pos >= Ep ->
+ Imm = asn1ct_imm:per_dec_open_type(is_aligned(Erule)),
+ {safe,fun(St) ->
+ emit(["begin",nl]),
+ BytesVar = asn1ct_gen:mk_var(asn1ct_name:curr(bytes)),
+ {Dst,DstBuf} = asn1ct_imm:dec_slim_cg(Imm, BytesVar),
+ emit([",",nl,"{TmpValx",Pos,",_} = "]),
+ {Dst,
+ fun() ->
+ emit([",",nl,
+ "{TmpValx",Pos,",",DstBuf,"}",nl,
+ "end"]),
+ St
+ end}
+ end};
+gen_dec_line_open_type(_, _, _) ->
+ {safe,fun(St) ->
+ {asn1ct_gen:mk_var(asn1ct_name:curr(bytes)),
+ fun() -> St end}
+ end}.
+
+gen_dec_line_special(Erule, {typefield,_}, _TopType, Comp,
+ DecInfObj, Ext) ->
+ #'ComponentType'{name=Cname,typespec=Type,prop=Prop} = Comp,
+ fun({_BytesVar,PrevSt}) ->
+ case DecInfObj of
+ false -> % This is in a choice with typefield components
+ {Name,RestFieldNames} =
+ (Type#type.def)#'ObjectClassFieldType'.fieldname,
+
+ asn1ct_name:new(reason),
+ Imm = asn1ct_imm:per_dec_open_type(is_aligned(Erule)),
+ BytesVar = asn1ct_gen:mk_var(asn1ct_name:curr(bytes)),
+ {TmpTerm,TempBuf} = asn1ct_imm:dec_slim_cg(Imm, BytesVar),
+ emit([com,nl,
+ {next,bytes}," = ",TempBuf,com,nl,
+ indent(2),"case (catch ObjFun(",
+ {asis,Name},",",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),"{",{asis,Cname},", {",{curr,tmpterm},", ",
+ {next,bytes},"}}",nl]),
+ emit([indent(2),"end"]),
+ {[],PrevSt};
+ {"got objfun through args","ObjFun"} ->
+ %% this is when the generated code gots the
+ %% objfun though arguments on function
+ %% invocation.
+ if
+ Ext == noext andalso Prop == mandatory ->
+ ok;
+ true ->
+ asn1ct_name:new(tmpterm),
+ asn1ct_name:new(tmpbytes),
+ emit([nl," {",{curr,tmpterm},", ",{curr,tmpbytes},"} ="])
+ end,
+ {Name,RestFieldNames} =
+ (Type#type.def)#'ObjectClassFieldType'.fieldname,
+ Imm = asn1ct_imm:per_dec_open_type(is_aligned(Erule)),
+ BytesVar = asn1ct_gen:mk_var(asn1ct_name:curr(bytes)),
+ asn1ct_imm:dec_code_gen(Imm, BytesVar),
+ emit([com,nl]),
+ if
+ Ext == noext andalso Prop == mandatory ->
+ emit([{curr,term}," =",nl," "]);
+ true ->
+ emit([" {"])
+ end,
+ emit(["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"]),
+ if
+ Ext == noext andalso Prop == mandatory ->
+ ok;
+ true ->
+ emit([",",nl,{curr,tmpbytes},"}"])
+ end,
+ {[],PrevSt};
+ _ ->
+ Imm = asn1ct_imm:per_dec_open_type(is_aligned(Erule)),
+ BytesVar = asn1ct_gen:mk_var(asn1ct_name:curr(bytes)),
+ asn1ct_imm:dec_code_gen(Imm, BytesVar),
+ RefedFieldName =
+ (Type#type.def)#'ObjectClassFieldType'.fieldname,
+
+ {[{Cname,RefedFieldName,
+ asn1ct_gen:mk_var(asn1ct_name:curr(term)),
+ asn1ct_gen:mk_var(asn1ct_name:curr(tmpterm)),
+ Prop}],PrevSt}
+ end
+ end;
+gen_dec_line_special(Erule, {objectfield,PrimFieldName1,PFNList}, _TopType,
+ Comp, _DecInfObj, _Ext) ->
+ fun({_BytesVar,PrevSt}) ->
+ Imm = asn1ct_imm:per_dec_open_type(is_aligned(Erule)),
+ BytesVar = asn1ct_gen:mk_var(asn1ct_name:curr(bytes)),
+ asn1ct_imm:dec_code_gen(Imm, BytesVar),
+ #'ComponentType'{name=Cname,prop=Prop} = Comp,
+ SaveBytes = [{Cname,{PrimFieldName1,PFNList},
+ asn1ct_gen:mk_var(asn1ct_name:curr(term)),
+ asn1ct_gen:mk_var(asn1ct_name:curr(tmpterm)),
+ Prop}],
+ {SaveBytes,PrevSt}
+ end;
+gen_dec_line_special(Erule, Atype, TopType, Comp, DecInfObj, _Ext) ->
+ case gen_dec_line_other(Erule, Atype, TopType, Comp) of
+ Fun when is_function(Fun, 1) ->
+ fun({BytesVar,PrevSt}) ->
+ Fun(BytesVar),
+ gen_dec_line_dec_inf(Comp, DecInfObj),
+ {[],PrevSt}
+ end;
+ Imm0 ->
+ {imm,Imm0,
+ fun(Imm, {BytesVar,PrevSt}) ->
+ asn1ct_imm:dec_code_gen(Imm, BytesVar),
+ gen_dec_line_dec_inf(Comp, DecInfObj),
+ {[],PrevSt}
+ end}
+ end.
+
+gen_dec_line_dec_inf(Comp, DecInfObj) ->
+ #'ComponentType'{name=Cname} = Comp,
case DecInfObj of
- {Cname,ObjSet} ->
- ObjSetRef =
- case ObjSet of
- {deep,OSName,_,_} ->
- OSName;
- _ -> ObjSet
+ {Cname,{_,OSet,UniqueFName,ValIndex}} ->
+ Term = asn1ct_gen:mk_var(asn1ct_name:curr(term)),
+ ValueMatch = value_match(ValIndex,Term),
+ {ObjSetMod,ObjSetName} =
+ case OSet of
+ {M,O} -> {{asis,M},O};
+ _ -> {"?MODULE",OSet}
end,
- {[{ObjSetRef,Cname,asn1ct_gen:mk_var(asn1ct_name:curr(term))}],
- SaveBytes};
+ emit({",",nl,"ObjFun = ",ObjSetMod,
+ ":'getdec_",ObjSetName,"'(",
+ {asis,UniqueFName},", ",ValueMatch,")"});
_ ->
- {[],SaveBytes}
+ ok
+ end.
+
+gen_dec_line_other(Erule, Atype, TopType, Comp) ->
+ #'ComponentType'{name=Cname,typespec=Type} = Comp,
+ CurrMod = get(currmod),
+ Ctgenmod = asn1ct_gen:ct_gen_module(Erule),
+ case asn1ct_gen:type(Atype) of
+ #'Externaltypereference'{module=CurrMod,type=EType} ->
+ fun(BytesVar) ->
+ emit({"'dec_",EType,"'(",BytesVar,",telltype)"})
+ end;
+ #'Externaltypereference'{module=Mod,type=EType} ->
+ fun(BytesVar) ->
+ emit({"'",Mod,"':'dec_",EType,"'(",BytesVar,
+ ",telltype)"})
+ end;
+ {primitive,bif} ->
+ case Atype of
+ {fixedtypevaluefield,_,Btype} ->
+ gen_dec_prim(Ctgenmod, Erule, Btype);
+ _ ->
+ gen_dec_prim(Ctgenmod, Erule, Type)
+ end;
+ 'ASN1_OPEN_TYPE' ->
+ case Type#type.def of
+ #'ObjectClassFieldType'{type=OpenType} ->
+ gen_dec_prim(Ctgenmod, Erule, #type{def=OpenType});
+ _ ->
+ gen_dec_prim(Ctgenmod, Erule, Type)
+ end;
+ #typereference{val=Dname} ->
+ fun(BytesVar) ->
+ emit({"'dec_",Dname,"'(",BytesVar,",telltype)"})
+ end;
+ {notype,_} ->
+ fun(BytesVar) ->
+ emit({"'dec_",Atype,"'(",BytesVar,",telltype)"})
+ end;
+ {constructed,bif} ->
+ NewTypename = [Cname|TopType],
+ case Type#type.tablecinf of
+ [{objfun,_}|_R] ->
+ fun(BytesVar) ->
+ emit({"'dec_",asn1ct_gen:list2name(NewTypename),
+ "'(",BytesVar,", telltype, ObjFun)"})
+ end;
+ _ ->
+ fun(BytesVar) ->
+ emit({"'dec_",asn1ct_gen:list2name(NewTypename),
+ "'(",BytesVar,", telltype)"})
+ end
+ end
+ end.
+
+gen_dec_prim(Ctgenmod, Erule, Type) ->
+ case asn1ct_gen_per:gen_dec_imm(Erule, Type) of
+ no ->
+ fun(BytesVar) ->
+ Ctgenmod:gen_dec_prim(Erule, Type, BytesVar)
+ end;
+ Imm ->
+ Imm
end.
gen_enc_choice(Erule,TopType,CompList,Ext) ->
@@ -1515,20 +1676,15 @@ gen_dec_choice1(Erule,TopType,CompList,{ext,ExtPos,ExtNum}) ->
length(CompList)-ExtNum,",Ext ),",nl}),
emit({"{Cname,{Val,NewBytes}} = case Choice + Ext*",ExtPos-1," of",nl}),
gen_dec_choice2(Erule,TopType,CompList,{ext,ExtPos,ExtNum}),
- case Erule of
- per ->
- emit([";",nl,"_ -> {asn1_ExtAlt,",nl,
- " fun() -> ",nl,
- " {XTerm,XBytes} = ?RT_PER:decode_open_type(",
- {curr,bytes},",[]),",nl,
- " {binary_to_list(XTerm),XBytes}",nl,
- " end()}"]);
- _ ->
- emit([";",nl,"_ -> {asn1_ExtAlt, ?RT_PER:decode_open_type(",
- {curr,bytes},",[])}"])
- end,
- emit({nl,"end,",nl}),
- emit({nl,"{{Cname,Val},NewBytes}"}).
+ Imm = asn1ct_imm:per_dec_open_type(is_aligned(Erule)),
+ BytesVar = asn1ct_gen:mk_var(asn1ct_name:curr(bytes)),
+ emit([";",nl,
+ "_ ->",nl]),
+ {TmpTerm,TmpBuf} = asn1ct_imm:dec_slim_cg(Imm, BytesVar),
+ emit([com,nl,
+ "{asn1_ExtAlt,{",TmpTerm,com,TmpBuf,"}}",nl,
+ "end,",nl,nl,
+ "{{Cname,Val},NewBytes}"]).
gen_dec_choice2(Erule,TopType,L,Ext) ->
@@ -1580,22 +1736,17 @@ gen_encode_prim_wrapper(CtgenMod,Erule,Cont,DoTag,Value) ->
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,Val,[_ExtCname],Acc)-> % the last one, no comma needed
+ Element = make_element(I, Val),
make_elements(I+1,Val,[],[Element|Acc]);
-make_elements(I,Val,[ExtCname|Rest],Acc)->
- Element = make_element(I,Val,ExtCname),
+make_elements(I,Val,[_ExtCname|Rest],Acc)->
+ Element = make_element(I, Val),
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 ->
- io_lib:format("?RT_PER:cindex(~w,~s,~w)",[I,Val,Cname]);
- _ ->
- io_lib:format("element(~w,~s)",[I,Val])
- end.
+make_element(I, Val) ->
+ io_lib:format("element(~w,~s)", [I,Val]).
emit_extaddgroupTerms(VarSeries,[_]) ->
asn1ct_name:new(VarSeries),
@@ -1653,22 +1804,12 @@ wrap_extensionAdditionGroups([],_,Acc,_,_) ->
lists:reverse(Acc).
-tuple_notation_allowed() ->
- Options = get(encoding_options),
- not (lists:member(optimize,Options) orelse lists:member(uper_bin,Options)).
-
-wrap_gen_dec_line(Erule,C,TopType,Cname,Type,Pos,DIO,Ext) ->
+wrap_gen_dec_line(Erule,C,TopType,_Cname,_Type,Pos,DIO,Ext) ->
put(component_type,{true,C}),
- gen_dec_line(Erule,TopType,Cname,Type,Pos,DIO,Ext,mandatory),
+ gen_dec_line(Erule, TopType, C#'ComponentType'{prop=mandatory},
+ 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 is_atom(Value) ->
value_match(Index,atom_to_list(Value));
@@ -1685,7 +1826,5 @@ notice_value_match() ->
Module = get(currmod),
put(value_match,{true,Module}).
-is_optimized(per_bin) ->
- lists:member(optimize,get(encoding_options));
-is_optimized(_Erule) ->
- false.
+is_optimized(per) -> true;
+is_optimized(uper) -> false.
diff --git a/lib/asn1/src/asn1ct_gen.erl b/lib/asn1/src/asn1ct_gen.erl
index fda4e1c6d9..57b12ce186 100644
--- a/lib/asn1/src/asn1ct_gen.erl
+++ b/lib/asn1/src/asn1ct_gen.erl
@@ -37,8 +37,7 @@
gen_check_call/7,
get_constraint/2,
insert_once/2,
- rt2ct_suffix/1,
- rt2ct_suffix/0,
+ ct_gen_module/1,
index2suffix/1,
get_record_name_prefix/0]).
-export([pgen/5,
@@ -52,7 +51,7 @@
%% pgen(Outfile, Erules, Module, TypeOrVal, Options)
%% 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
+%% Erules = per | ber
%% Module = atom()
%% TypeOrVal = {TypeList,ValueList}
%% TypeList = ValueList = [atom()]
@@ -83,7 +82,7 @@ pgen_module(OutFile,Erules,Module,
pgen_exports(Erules,Module,TypeOrVal),
pgen_dispatcher(Erules,Module,TypeOrVal),
pgen_info(),
- pgen_typeorval(wrap_ber(Erules),Module,N2nConvEnums,TypeOrVal),
+ pgen_typeorval(Erules,Module,N2nConvEnums,TypeOrVal),
pgen_partial_incomplete_decode(Erules),
% gen_vars(asn1_db:mod_to_vars(Module)),
% gen_tag_table(AllTypes),
@@ -92,8 +91,7 @@ pgen_module(OutFile,Erules,Module,
pgen_typeorval(Erules,Module,N2nConvEnums,{Types,Values,_Ptypes,_Classes,Objects,ObjectSets}) ->
- Rtmod = list_to_atom(lists:concat(["asn1ct_gen_",erule(Erules),
- rt2ct_suffix(Erules)])),
+ Rtmod = ct_gen_module(Erules),
pgen_types(Rtmod,Erules,N2nConvEnums,Module,Types),
pgen_values(Erules,Module,Values),
pgen_objects(Rtmod,Erules,Module,Objects),
@@ -129,28 +127,39 @@ pgen_types(Rtmod,Erules,N2nConvEnums,Module,[H|T]) ->
end,
pgen_types(Rtmod,Erules,N2nConvEnums,Module,T).
+%% Enumerated type with extension marker
pgen_n2nconversion(_Erules,#typedef{name=TypeName,typespec=#type{def={'ENUMERATED',{NN1,NN2}}}}) ->
NN = NN1 ++ NN2,
- pgen_name2numfunc(TypeName,NN),
- pgen_num2namefunc(TypeName,NN);
+ pgen_name2numfunc(TypeName,NN, extension_marker),
+ pgen_num2namefunc(TypeName,NN, extension_marker);
+%% Without extension marker
+pgen_n2nconversion(_Erules,#typedef{name=TypeName,typespec=#type{def={'ENUMERATED',NN}}}) ->
+ pgen_name2numfunc(TypeName,NN, no_extension_marker),
+ pgen_num2namefunc(TypeName,NN, no_extension_marker);
pgen_n2nconversion(_Erules,_) ->
true.
-pgen_name2numfunc(_TypeName,[]) ->
+pgen_name2numfunc(_TypeName,[], _) ->
true;
-pgen_name2numfunc(TypeName,[{Atom,Number}]) ->
+pgen_name2numfunc(TypeName,[{Atom,Number}], extension_marker) ->
+ emit(["name2num_",TypeName,"(",{asis,Atom},") ->",Number,";",nl]),
+ emit(["name2num_",TypeName,"({asn1_enum, Num}) -> Num.",nl,nl]);
+pgen_name2numfunc(TypeName,[{Atom,Number}], _) ->
emit(["name2num_",TypeName,"(",{asis,Atom},") ->",Number,".",nl,nl]);
-pgen_name2numfunc(TypeName,[{Atom,Number}|NNRest]) ->
+pgen_name2numfunc(TypeName,[{Atom,Number}|NNRest], EM) ->
emit(["name2num_",TypeName,"(",{asis,Atom},") ->",Number,";",nl]),
- pgen_name2numfunc(TypeName,NNRest).
+ pgen_name2numfunc(TypeName,NNRest, EM).
-pgen_num2namefunc(_TypeName,[]) ->
+pgen_num2namefunc(_TypeName,[], _) ->
true;
-pgen_num2namefunc(TypeName,[{Atom,Number}]) ->
+pgen_num2namefunc(TypeName,[{Atom,Number}], extension_marker) ->
+ emit(["num2name_",TypeName,"(",Number,") ->",{asis,Atom},";",nl]),
+ emit(["num2name_",TypeName,"(ExtensionNum) -> {asn1_enum, ExtensionNum}.",nl,nl]);
+pgen_num2namefunc(TypeName,[{Atom,Number}], _) ->
emit(["num2name_",TypeName,"(",Number,") ->",{asis,Atom},".",nl,nl]);
-pgen_num2namefunc(TypeName,[{Atom,Number}|NNRest]) ->
+pgen_num2namefunc(TypeName,[{Atom,Number}|NNRest], EM) ->
emit(["num2name_",TypeName,"(",Number,") ->",{asis,Atom},";",nl]),
- pgen_num2namefunc(TypeName,NNRest).
+ pgen_num2namefunc(TypeName,NNRest, EM).
pgen_objects(_,_,_,[]) ->
true;
@@ -185,7 +194,7 @@ pgen_check_defaultval(Erules,Module) ->
end,
gen_check_defaultval(Erules,Module,CheckObjects).
-pgen_partial_decode(Rtmod,Erule,Module) when Erule == ber_bin_v2 ->
+pgen_partial_decode(Rtmod,Erule,Module) when Erule == ber ->
pgen_partial_inc_dec(Rtmod,Erule,Module),
pgen_partial_dec(Rtmod,Erule,Module);
pgen_partial_decode(_,_,_) ->
@@ -229,7 +238,7 @@ pgen_partial_inc_dec1(Rtmod,Erules,Module,[P|Ps]) ->
pgen_partial_inc_dec1(_,_,_,[]) ->
ok.
-gen_partial_inc_dec_refed_funcs(Rtmod,Erule) when Erule == ber_bin_v2 ->
+gen_partial_inc_dec_refed_funcs(Rtmod,Erule) when Erule == ber ->
case asn1ct:next_refed_func() of
[] ->
ok;
@@ -285,8 +294,7 @@ pgen_partial_types1(_,undefined) ->
%% TypeList a decode function will be generated.
traverse_type_structure(Erules,Type,[],FuncName,TopTypeName) ->
%% this is the selected type
- Ctmod = list_to_atom(lists:concat(["asn1ct_gen_",erule(Erules),
- rt2ct_suffix(Erules)])),
+ Ctmod = ct_gen_module(Erules),
TypeDef =
case Type of
#type{} ->
@@ -446,7 +454,7 @@ pgen_partial_incomplete_decode(Erule) ->
_ ->
ok
end.
-pgen_partial_incomplete_decode1(ber_bin_v2) ->
+pgen_partial_incomplete_decode1(ber) ->
case asn1ct:read_config_data(partial_incomplete_decode) of
undefined ->
ok;
@@ -541,20 +549,17 @@ gen_part_decode_funcs(WhatKind,_TypeName,{_,Directive,_,_}) ->
gen_types(Erules,Tname,{RootL1,ExtList,RootL2})
when is_list(RootL1), is_list(RootL2) ->
gen_types(Erules,Tname,RootL1),
- Rtmod = list_to_atom(lists:concat(["asn1ct_gen_",erule(Erules),
- rt2ct_suffix(Erules)])),
+ Rtmod = ct_gen_module(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),
- Rtmod = list_to_atom(lists:concat(["asn1ct_gen_",erule(Erules),
- rt2ct_suffix(Erules)])),
+ Rtmod = ct_gen_module(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]) ->
- Rtmod = list_to_atom(lists:concat(["asn1ct_gen_",erule(Erules),
- rt2ct_suffix(Erules)])),
+ Rtmod = ct_gen_module(Erules),
asn1ct_name:clear(),
Rtmod:gen_encode(Erules,Tname,ComponentType),
asn1ct_name:clear(),
@@ -563,8 +568,7 @@ gen_types(Erules,Tname,[ComponentType|Rest]) ->
gen_types(_,_,[]) ->
true;
gen_types(Erules,Tname,Type) when is_record(Type,type) ->
- Rtmod = list_to_atom(lists:concat(["asn1ct_gen_",erule(Erules),
- rt2ct_suffix(Erules)])),
+ Rtmod = ct_gen_module(Erules),
asn1ct_name:clear(),
Rtmod:gen_encode(Erules,Tname,Type),
asn1ct_name:clear(),
@@ -743,8 +747,7 @@ gen_value(Value) when is_record(Value,valuedef) ->
emit([{asis,V},".",nl,nl]).
gen_encode_constructed(Erules,Typename,InnerType,D) when is_record(D,type) ->
-
- Rtmod = list_to_atom(lists:concat(["asn1ct_constructed_",erule(Erules)])),
+ Rtmod = ct_constructed_module(Erules),
case InnerType of
'SET' ->
Rtmod:gen_encode_set(Erules,Typename,D),
@@ -776,7 +779,7 @@ gen_encode_constructed(Erules,Typename,InnerType,D)
gen_encode_constructed(Erules,Typename,InnerType,D#typedef.typespec).
gen_decode_constructed(Erules,Typename,InnerType,D) when is_record(D,type) ->
- Rtmod = list_to_atom(lists:concat(["asn1ct_constructed_",erule(Erules)])),
+ Rtmod = ct_constructed_module(Erules),
asn1ct:step_in_constructed(), %% updates namelist for exclusive decode
case InnerType of
'SET' ->
@@ -807,27 +810,11 @@ pgen_exports(Erules,_Module,{Types,Values,_,_,Objects,ObjectSets}) ->
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
+ gen_exports1(Types,"dec_",2)
end,
case [X || {n2n,X} <- get(encoding_options)] of
[] -> ok;
@@ -852,16 +839,11 @@ pgen_exports(Erules,_Module,{Types,Values,_,_,Objects,ObjectSets}) ->
gen_exports1(Objects,"enc_",3),
emit({"-export([",nl}),
gen_exports1(Objects,"dec_",4);
- ber_bin_v2 ->
+ ber ->
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)
+ gen_exports1(Objects,"dec_",3)
end
end,
case ObjectSets of
@@ -937,20 +919,17 @@ pgen_dispatcher(Erules,_Module,{Types,_Values,_,_,_Objects,_ObjectSets}) ->
emit(["encoding_rule() ->",nl]),
emit([" ",{asis,Erules},".",nl,nl]),
NoFinalPadding = lists:member(no_final_padding,get(encoding_options)),
- 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)";
- uper_bin when NoFinalPadding == true ->
- "?RT_PER:complete_NFP(encode_disp(Type,Data))";
- uper_bin -> ["?RT_PER:complete(encode_disp(Type,Data))"]
- end,
- EncWrap = case Erules of
- ber -> "wrap_encode(Bytes)";
- _ -> "Bytes"
- end,
+ {Call,BytesAsBinary} =
+ case Erules of
+ per ->
+ {["?RT_PER:complete(encode_disp(Type,Data))"],"Bytes"};
+ ber ->
+ {"encode_disp(Type,Data)","iolist_to_binary(Bytes)"};
+ uper when NoFinalPadding == true ->
+ {"?RT_PER:complete_NFP(encode_disp(Type,Data))","Bytes"};
+ uper ->
+ {["?RT_PER:complete(encode_disp(Type,Data))"],"Bytes"}
+ end,
emit(["encode(Type,Data) ->",nl,
"case catch ",Call," of",nl,
" {'EXIT',{error,Reason}} ->",nl,
@@ -958,42 +937,25 @@ pgen_dispatcher(Erules,_Module,{Types,_Values,_,_,_Objects,_ObjectSets}) ->
" {'EXIT',Reason} ->",nl,
" {error,{asn1,Reason}};",nl,
" {Bytes,_Len} ->",nl,
- " {ok,",EncWrap,"};",nl]),
- case Erules of
- per ->
- emit([" Bytes when is_binary(Bytes) ->",nl,
- " {ok,binary_to_list(Bytes)};",nl,
- " Bytes ->",nl,
- " {ok,binary_to_list(list_to_binary(Bytes))}",nl,
- " end.",nl,nl]);
- _ ->
- emit([" Bytes ->",nl,
- " {ok,",EncWrap,"}",nl,
- "end.",nl,nl])
- end,
-
-% case Erules of
-% ber_bin_v2 ->
-% emit(["decode(Type,Data0) ->",nl]),
-% emit(["{Data,_RestBin} = ?RT_BER:decode(Data0",nif_parameter(),"),",nl]);
-% _ ->
-% emit(["decode(Type,Data) ->",nl])
-% end,
+ " {ok,",BytesAsBinary,"};",nl,
+ " Bytes ->",nl,
+ " {ok,",BytesAsBinary,"}",nl,
+ "end.",nl,nl]),
Return_rest = lists:member(undec_rest,get(encoding_options)),
Data = case {Erules,Return_rest} of
- {ber_bin_v2,true} -> "Data0";
+ {ber,true} -> "Data0";
_ -> "Data"
end,
emit(["decode(Type,",Data,") ->",nl]),
DecAnonymous =
case {Erules,Return_rest} of
- {ber_bin_v2,false} ->
+ {ber,false} ->
io_lib:format("~s~s~s~n",
["element(1,?RT_BER:decode(Data",
nif_parameter(),"))"]);
- {ber_bin_v2,true} ->
+ {ber,true} ->
emit(["{Data,Rest} = ?RT_BER:decode(Data0",
nif_parameter(),"),",nl]),
"Data";
@@ -1001,10 +963,8 @@ pgen_dispatcher(Erules,_Module,{Types,_Values,_,_,_Objects,_ObjectSets}) ->
"Data"
end,
DecWrap = case Erules of
- ber -> "wrap_decode(Data)";
- ber_bin_v2 ->
+ ber ->
DecAnonymous;
- per -> "list_to_binary(Data)";
_ -> "Data"
end,
@@ -1014,32 +974,18 @@ pgen_dispatcher(Erules,_Module,{Types,_Values,_,_,_Objects,_ObjectSets}) ->
" {'EXIT',Reason} ->",nl,
" {error,{asn1,Reason}};",nl]),
case {Erules,Return_rest} of
- {ber_bin_v2,false} ->
+ {ber,false} ->
emit([" Result ->",nl,
" {ok,Result}",nl]);
- {ber_bin_v2,true} ->
+ {ber,true} ->
emit([" Result ->",nl,
" {ok,Result,Rest}",nl]);
- {per,false} ->
- emit([" {X,_Rest} ->",nl,
- " {ok,if_binary2list(X)};",nl,
- " {X,_Rest,_Len} ->",nl,
- " {ok,if_binary2list(X)}",nl]);
{_,false} ->
emit([" {X,_Rest} ->",nl,
" {ok,X};",nl,
" {X,_Rest,_Len} ->",nl,
" {ok,X}",nl]);
- {per,true} ->
- emit([" {X,{_,Rest}} ->",nl,
- " {ok,if_binary2list(X),Rest};",nl,
- " {X,{_,Rest},_Len} ->",nl,
- " {ok,if_binary2list(X),Rest};",nl,
- " {X,Rest} ->",nl,
- " {ok,if_binary2list(X),Rest};",nl,
- " {X,Rest,_Len} ->",nl,
- " {ok,if_binary2list(X),Rest}",nl]);
- {per_bin,true} ->
+ {per,true} ->
emit([" {X,{_,Rest}} ->",nl,
" {ok,X,Rest};",nl,
" {X,{_,Rest},_Len} ->",nl,
@@ -1048,7 +994,7 @@ pgen_dispatcher(Erules,_Module,{Types,_Values,_,_,_Objects,_ObjectSets}) ->
" {ok,X,Rest};",nl,
" {X,Rest,_Len} ->",nl,
" {ok,X,Rest}",nl]);
- {uper_bin,true} ->
+ {uper,true} ->
emit([" {X,{_,Rest}} ->",nl,
" {ok,X,Rest};",nl,
" {X,{_,Rest},_Len} ->",nl,
@@ -1056,34 +1002,14 @@ pgen_dispatcher(Erules,_Module,{Types,_Values,_,_,_Objects,_ObjectSets}) ->
" {X,Rest} ->",nl,
" {ok,X,Rest};",nl,
" {X,Rest,_Len} ->",nl,
- " {ok,X,Rest}",nl]);
- _ ->
- emit([" {X,Rest} ->",nl,
- " {ok,X,Rest};",nl,
- " {X,Rest,_Len} ->",nl,
" {ok,X,Rest}",nl])
end,
emit(["end.",nl,nl]),
- case Erules of
- per ->
- emit(["if_binary2list(B) when is_binary(B) ->",nl,
- " binary_to_list(B);",nl,
- "if_binary2list(L) -> L.",nl,nl]);
- _ ->
- ok
- end,
-
gen_decode_partial_incomplete(Erules),
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();
@@ -1092,17 +1018,10 @@ pgen_dispatcher(Erules,_Module,{Types,_Values,_,_,_Objects,_ObjectSets}) ->
gen_dispatcher(Types,"decode_disp","dec_",",mandatory")
end,
emit([nl]),
-
- 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 ->
+gen_decode_partial_incomplete(ber) ->
case {asn1ct:read_config_data(partial_incomplete_decode),
asn1ct:get_gen_state_field(inc_type_pattern)} of
{undefined,_} ->
@@ -1110,34 +1029,35 @@ gen_decode_partial_incomplete(Erule) when Erule == ber;Erule==ber_bin;
{_,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]),
- emit([" case catch decode_inc_disp(Type,element(1,"
- "?RT_BER:decode(Data0",nif_parameter(),"))) of",nl]),
-% " {Data,_RestBin} = ?RT_BER:decode(Data0),",nl,
-% " case catch decode_inc_disp(Type,Data) of",nl]),
- EmitCaseClauses();
- _ -> ok % add later
- end
+ EmitCaseClauses =
+ fun() ->
+ emit([" {'EXIT',{error,Reason}} ->",nl,
+ " {error,Reason};",nl,
+ " {'EXIT',Reason} ->",nl,
+ " {error,{asn1,Reason}};",nl,
+ " Result ->",nl,
+ " {ok,Result}",nl,
+ " end"])
+ 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([".",nl,nl]),
+ emit(["decode_part(Type, Data0) "
+ "when is_binary(Data0) ->",nl]),
+ emit([" case catch decode_inc_disp(Type,element(1,"
+ "?RT_BER:decode(Data0",nif_parameter(),"))) of",nl]),
+ EmitCaseClauses(),
+ emit([";",nl]),
+ emit(["decode_part(Type, Data0) ->",nl]),
+ emit([" case catch decode_inc_disp(Type, Data0) of",nl]),
+ EmitCaseClauses(),
+ emit([".",nl,nl])
end;
gen_decode_partial_incomplete(_Erule) ->
ok.
@@ -1176,23 +1096,8 @@ gen_partial_inc_dispatcher([],_) ->
" exit({error,{asn1,{undefined_type,Type}}}).",nl]).
nif_parameter() ->
- Options = get(encoding_options),
- case {lists:member(driver,Options),lists:member(nif,Options)} of
- {true,_} -> ",nif";
- {_,true} -> ",nif";
- _ -> ""
- end.
+ ",nif".
-gen_wrapper() ->
- emit(["wrap_encode(Bytes) when is_list(Bytes) ->",nl,
- " binary_to_list(list_to_binary(Bytes));",nl,
- "wrap_encode(Bytes) when is_binary(Bytes) ->",nl,
- " binary_to_list(Bytes);",nl,
- "wrap_encode(Bytes) -> Bytes.",nl,nl]),
- emit(["wrap_decode(Bytes) when is_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);
@@ -1202,19 +1107,16 @@ gen_dispatcher([Flast|_T],FuncName,Prefix,ExtraArg) ->
pgen_info() ->
emit(["info() ->",nl,
- " case ?MODULE:module_info() of",nl,
- " MI when is_list(MI) ->",nl,
- " case lists:keysearch(attributes,1,MI) of",nl,
- " {value,{_,Attributes}} when is_list(Attributes) ->",nl,
- " case lists:keysearch(asn1_info,1,Attributes) of",nl,
- " {value,{_,Info}} when is_list(Info) ->",nl,
- " Info;",nl,
- " _ ->",nl,
- " []",nl,
- " end;",nl,
- " _ ->",nl,
- " []",nl,
- " end",nl,
+ " case ?MODULE:module_info(attributes) of",nl,
+ " Attributes when is_list(Attributes) ->",nl,
+ " case lists:keyfind(asn1_info, 1, Attributes) of",nl,
+ " {_,Info} when is_list(Info) ->",nl,
+ " Info;",nl,
+ " _ ->",nl,
+ " []",nl,
+ " end;",nl,
+ " _ ->",nl,
+ " []",nl,
" end.",nl]).
open_hrl(OutFile,Module) ->
@@ -1485,32 +1387,15 @@ 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_BIN};
- 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
- 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 ->
+ {"RT_PER","asn1rt_per_bin_rt2ct"};
+ ber ->
emit({"%% Generated by the Erlang ASN.1 BER_V2-"
"compiler version, utilizing bit-syntax:",
asn1ct:vsn(),nl}),
{"RT_BER","asn1rt_ber_bin_v2"};
- uper_bin ->
+ uper ->
emit(["%% Generated by the Erlang ASN.1 UNALIGNED"
" PER-compiler version, utilizing"
" bit-syntax:",
@@ -1520,7 +1405,7 @@ gen_head(Erules,Mod,Hrl) ->
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}),
+ emit({"-compile(nowarn_unused_vars).",nl}),
case {Hrl,lists:member(inline,get(encoding_options))} of
{0,_} -> true;
{_,true} -> true;
@@ -1530,7 +1415,7 @@ gen_head(Erules,Mod,Hrl) ->
emit(["-define('",Rtmac,"',",Rtmod,").",nl]),
emit(["-asn1_info([{vsn,'",asn1ct:vsn(),"'},",nl,
" {module,'",Mod,"'},",nl,
- " {options,",io_lib:format("~w",[Options]),"}]).",nl,nl]).
+ " {options,",io_lib:format("~p",[Options]),"}]).",nl,nl]).
gen_hrlhead(Mod) ->
@@ -2008,43 +1893,29 @@ constructed_suffix('SEQUENCE OF',_) ->
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;
-erule(uper_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(_) -> "".
+erule(ber) -> ber;
+erule(per) -> per;
+erule(uper) -> per.
index2suffix(0) ->
"";
index2suffix(N) ->
lists:concat(["_",N]).
+ct_gen_module(ber) ->
+ asn1ct_gen_ber_bin_v2;
+ct_gen_module(per) ->
+ asn1ct_gen_per_rt2ct;
+ct_gen_module(uper) ->
+ asn1ct_gen_per.
+
+ct_constructed_module(ber) ->
+ asn1ct_constructed_ber_bin_v2;
+ct_constructed_module(per) ->
+ asn1ct_constructed_per;
+ct_constructed_module(uper) ->
+ asn1ct_constructed_per.
+
get_constraint(C,Key) ->
case lists:keysearch(Key,1,C) of
false ->
diff --git a/lib/asn1/src/asn1ct_gen_ber.erl b/lib/asn1/src/asn1ct_gen_ber.erl
deleted file mode 100644
index 491ebcb8fd..0000000000
--- a/lib/asn1/src/asn1ct_gen_ber.erl
+++ /dev/null
@@ -1,1749 +0,0 @@
-%%
-%% %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(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]).
--export([extaddgroup2sequence/1]).
-
--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 is_record(Type,typedef) ->
- gen_encode_user(Erules,Type).
-
-%%===============================================================================
-%% encode #{type, {tag, def, constraint}}
-%%===============================================================================
-
-gen_encode(Erules,Typename,Type) when is_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 is_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'{name=Cname,typespec=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 is_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 is_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 is_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 {_,",{curr,enumval},"}->",
- {curr,enumval},";_->", Value," end) of",nl]),
- asn1ct_name:new(enumval),
- emit_enc_enumerated_cases(NamedNumberList,DoTag);
- {'ENUMERATED',NamedNumberList} ->
-
- emit(["case (case ",Value," of {_,",{curr,enumval},"}->",
- {curr,enumval},";_->", Value," end) of",nl]),
- asn1ct_name:new(enumval),
- emit_enc_enumerated_cases(NamedNumberList,DoTag);
-
- 'REAL' ->
- emit_encode_func('real',Constraint,Value,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);
- 'RELATIVE-OID' ->
- emit_encode_func("relative_oid",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);
- TString when TString == 'TeletexString';
- TString == 'T61String' ->
- 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);
- 'UTF8String' ->
- emit_encode_func('UTF8_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);
- #'ObjectClassFieldType'{} ->
- case asn1ct_gen:get_inner(D#type.def) of
- {fixedtypevaluefield,_,InnerType} ->
- gen_encode_prim(Erules,InnerType,DoTag,Value);
- 'ASN1_OPEN_TYPE' ->
- emit_encode_func('open_type', Value,DoTag);
- XX ->
- exit({'can not encode' ,XX})
- end;
- XX ->
- exit({'can not encode' ,XX})
- end.
-
-
-emit_encode_func(Name,Value,Tags) when is_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 is_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 is_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)
- emit([";",nl])
- 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 is_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 is_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'{name=Cname,typespec=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 is_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;
- 'REAL' ->
- emit({"?RT_BER:decode_real(",BytesVar,",",
- {asis,Constraint},","}),
- 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;
- 'RELATIVE-OID' ->
- emit({"?RT_BER:decode_relative_oid(",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;
- TString when TString == 'TeletexString';
- TString == 'T61String' ->
- 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;
- 'UTF8String' ->
- emit({"?RT_BER:decode_UTF8_string",AsBin,"(",
- BytesVar,","}),
- false;
- '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;
- #'ObjectClassFieldType'{} ->
- case asn1ct_gen:get_inner(Att#type.def) of
- {fixedtypevaluefield,_,InnerType} ->
- gen_dec_prim(Erules,InnerType,BytesVar,DoTag,TagIn,Length,Form,OptOrMand),
- false;
- 'ASN1_OPEN_TYPE' ->
- emit(["?RT_BER:decode_open_type(",
- re_wrap_erule(Erules),",",
- BytesVar,","]),
- false;
- XX ->
- exit({'can not decode' ,XX})
- end;
- Other ->
- exit({'can not decode' ,Other})
- end,
-
- NewLength = case DoLength of
- true -> [", ", Length];
- false -> ""
- end,
- NewOptOrMand = case OptOrMand of
- _ when is_list(OptOrMand) -> OptOrMand;
- mandatory -> {asis,mandatory};
- _ -> {asis,opt_or_default}
- end,
- case {TagIn,NewTypeName} of
- {_,#'ObjectClassFieldType'{}} ->
- case asn1ct_gen:get_inner(Att#type.def) of
- 'ASN1_OPEN_TYPE' ->
- emit([{asis,DoTag},")"]);
- _ -> ok
- end;
- {[],'ASN1_OPEN_TYPE'} ->
- emit([{asis,DoTag},")"]);
- {_,'ASN1_OPEN_TYPE'} ->
- emit([TagIn,"++",{asis,DoTag},")"]);
- {[],_} ->
- emit([{asis,DoTag},NewLength,", ",NewOptOrMand,")"]);
- _ when is_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 is_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 is_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'} -> %% OPTIONAL field in class
- EmitFuncClause("Val, _"), %% Value must be anything
- %% already encoded
- emit([" {Val,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) ->
- CurrentMod = get(currmod),
- 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', ",{asis,Name},
- "}})"]);
- {false,{'DEFAULT',_DefaultObject}} ->
- exit({error,{asn1,{"not implemented yet",Name}}});
- {{Name,#'Externalvaluereference'{module=CurrentMod,
- value=TypeName}},_} ->
- EmitFuncClause(" Val, TagIn, [H|T]"),
- emit({indent(3),"'enc_",TypeName,"'(H, Val, TagIn, T)"});
- {{Name,#'Externalvaluereference'{module=M,value=TypeName}},_} ->
- EmitFuncClause(" Val, TagIn, [H|T]"),
- emit({indent(3),"'",M,"':'enc_",TypeName,"'(H, Val, TagIn, T)"});
- {{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 is_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,
- #'Externaltypereference'{module=M,type=T}) ->
- CurrentMod = get(currmod),
- TDef = asn1_db:dbget(M,T),
- Def = TDef#typedef.typespec,
- OTag = Def#type.tag,
- Tag = [X#tag{class=decode_class(X#tag.class)}|| X <- OTag],
- if
- M == CurrentMod ->
- emit({" 'enc_",T,"'(Val, TagIn ++ ",{asis,Tag},")"}),
- [];
- true ->
- emit({" '",M,"':'enc_",T,"'(Val, TagIn ++ ",{asis,Tag},")"}),
- []
- end;
-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("Bytes, _,"),
-% emit([" asn1_NOVALUE"]),
- emit([" {Bytes,[],0}"]),
- [];
- {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) ->
- CurrentMod = get(currmod),
- 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', ",{asis,Name},
- "}})"]);
- {false,{'DEFAULT',_DefaultObject}} ->
- exit({error,{asn1,{"not implemented yet",Name}}});
- {{Name,#'Externalvaluereference'{module=CurrentMod,
- value=TypeName}},_} ->
- EmitFuncClause("Bytes,TagIn,[H|T]"),
- emit({indent(3),"'dec_",TypeName,"'(H, Bytes, TagIn, T)"});
- {{Name,#'Externalvaluereference'{module=M,value=TypeName}},_} ->
- EmitFuncClause("Bytes,TagIn,[H|T]"),
- emit({indent(3),"'",M,"':'dec_",TypeName,
- "'(H, Bytes, TagIn, T)"});
- {{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_constr_type(Erules,[TypeDef|Rest]) when is_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,
- #'Externaltypereference'{module=M,type=T}) ->
- CurrentMod = get(currmod),
- TDef = asn1_db:dbget(M,T),
- Def = TDef#typedef.typespec,
- OTag = Def#type.tag,
- Tag = [X#tag{class=decode_class(X#tag.class)}|| X <- OTag],
- if
- M == CurrentMod ->
- emit({" 'dec_",T,"'(",Bytes,
- ", opt_or_default,TagIn ++ ",{asis,Tag},")"}),
- [];
- true ->
- emit({" '",M,"':'dec_",T,
- "'(",Bytes,", opt_or_default,TagIn ++ ",{asis,Tag},")"}),
- []
- end;
-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,[{_,no_unique_value,_},T|Rest],
- ClName,ClFields,NthObj,Acc) ->
- %% No need to check that this class has property OPTIONAL for the
- %% unique field, it was detected in the previous phase
- gen_objset_enc(ObjSName,UniqueName,[T|Rest],ClName,ClFields,NthObj,Acc);
-gen_objset_enc(ObjSetName,UniqueName,[{_,no_unique_value,_}],
- _ClName,_ClFields,_NthObj,Acc) ->
- %% No need to check that this class has property OPTIONAL for the
- %% unique field, it was detected in the previous phase
- emit_default_getenc(ObjSetName,UniqueName),
- emit({".",nl,nl}),
- Acc;
-gen_objset_enc(ObjSName,UniqueName,
- [{ObjName,Val,Fields},T|Rest],ClName,ClFields,NthObj,Acc)->
- emit({"'getenc_",ObjSName,"'(",{asis,UniqueName},",",{asis,Val},") ->",nl}),
- CurrMod = get(currmod),
- {InternalFunc,NewNthObj}=
- case ObjName of
- {no_mod,no_name} ->
- gen_inlined_enc_funs(Fields,ClFields,ObjSName,NthObj);
- {CurrMod,Name} ->
- emit({" fun 'enc_",Name,"'/4"}),
- {[],NthObj};
- {ModuleName,Name} ->
- emit_ext_fun(enc,ModuleName,Name),
-% emit([" {'",ModuleName,"', 'enc_",Name,"'}"]),
- {[],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}),
- CurrMod = get(currmod),
- {InternalFunc,_}=
- case ObjName of
- {no_mod,no_name} ->
- gen_inlined_enc_funs(Fields,ClFields,ObjSetName,NthObj);
- {CurrMod,Name} ->
- emit({" fun 'enc_",Name,"'/4"}),
- {[],NthObj};
- {ModuleName,Name} ->
- emit_ext_fun(enc,ModuleName,Name),
-% emit([" {'",ModuleName,"', 'enc_",Name,"'}"]),
- {[],NthObj};
- _Other ->
- emit({" fun 'enc_",ObjName,"'/4"}),
- {[],NthObj}
- end,
- emit([";",nl]),
- emit_default_getenc(ObjSetName,UniqueName),
- 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 is_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(ObjSetName,UniqueName,['EXTENSIONMARK','EXTENSIONMARK'|Rest],
- ClName,ClFields,NthObj,Acc) ->
- gen_objset_enc(ObjSetName,UniqueName,['EXTENSIONMARK'|Rest],
- ClName,ClFields,NthObj,Acc);
-gen_objset_enc(ObjSetName,UniqueName,['EXTENSIONMARK'|Rest],
- ClName,ClFields,NthObj,Acc) ->
- gen_objset_enc(ObjSetName,UniqueName,Rest++['EXTENSIONMARK'],
- ClName,ClFields,NthObj,Acc);
-gen_objset_enc(_,_,[],_,_,_,Acc) ->
- Acc.
-
-emit_ext_fun(EncDec,ModuleName,Name) ->
- emit([indent(3),"fun(T,V,_O1,_O2) -> '",ModuleName,"':'",EncDec,"_",
- Name,"'(T,V,_O1,_O2) end"]).
-
-emit_default_getenc(ObjSetName,UniqueName) ->
- emit(["'getenc_",ObjSetName,"'(",{asis,UniqueName},", ErrV) ->",nl]),
- emit([indent(3),"fun(C,V,_,_) -> exit({'Type not compatible with table constraint',{component,C},{value,V}, {unique_name_and_value,",{asis,UniqueName},", ErrV}}) end"]).
-
-%% 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) ->
- CurrMod = get(currmod),
- InternalDefFunName = asn1ct_gen:list2name([NthObj,Name,ObjSetName]),
- case lists:keysearch(Name,1,Fields) of
- {value,{_,Type}} when is_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 is_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);
- {value,{_,#'Externaltypereference'{module=M,type=T}}} ->
- #typedef{typespec=Type} = asn1_db:dbget(M,T),
- OTag = Type#type.tag,
- Tag = [X#tag{class=decode_class(X#tag.class)}|| X <- OTag],
- emit([indent(3),"fun(Type, Val, TagIn, _RestPrimFieldName) ->",nl,
- indent(6),"case Type of",nl]),
- emit([indent(9),{asis,Name}," ->",nl]),
- if
- M == CurrMod ->
- emit([indent(12),"'enc_",T,"'(Val, TagIn ++ ",
- {asis,Tag},")"]);
- true ->
- emit([indent(12),"'",M,"':'enc_",T,"'(Val,TagIn ++",
- {asis,Tag},")"])
- end,
- gen_inlined_enc_funs1(Fields,Rest,ObjSetName,NthObj,[]);
- false ->
- %% This field was not present in the object thus there were no
- %% type in the table and we therefore generate code that returns
- %% the input for application treatment.
- emit([indent(3),"fun(Type, Val, TagIn, _RestPrimFieldName) ->",nl,
- indent(6),"case Type of",nl]),
- emit([indent(9),{asis,Name}," ->",nl]),
- emit([indent(12),"Len = case Val of",nl,
- indent(15),"Bin when is_binary(Bin) -> size(Bin);",nl,
- indent(15),"_ -> length(Val)",nl,indent(12),"end,",nl,
- indent(12),"{Val,Len}"]),
- gen_inlined_enc_funs1(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) ->
- CurrMod = get(currmod),
- InternalDefFunName = asn1ct_gen:list2name([NthObj,Name,ObjSetName]),
- {Acc2,NAdd}=
- case lists:keysearch(Name,1,Fields) of
- {value,{_,Type}} when is_record(Type,type) ->
- emit({";",nl}),
- {Ret,N}=emit_inner_of_fun(Type,InternalDefFunName),
- {Ret++Acc,N};
- {value,{_,Type}} when is_record(Type,typedef) ->
- emit({";",nl,indent(9),{asis,Name}," ->",nl}),
- {Ret,N}=emit_inner_of_fun(Type,InternalDefFunName),
- {Ret++Acc,N};
- {value,{_,#'Externaltypereference'{module=M,type=T}}} ->
- #typedef{typespec=Type} = asn1_db:dbget(M,T),
- OTag = Type#type.tag,
- Tag = [X#tag{class=decode_class(X#tag.class)}|| X <- OTag],
- emit({";",nl,indent(9),{asis,Name}," ->",nl}),
- if
- M == CurrMod ->
- emit([indent(12),"'enc_",T,"'(Val, TagIn ++ ",
- {asis,Tag},")"]);
- true ->
- emit([indent(12),"'",M,"':'enc_",T,"'(Val,TagIn ++",
- {asis,Tag},")"])
- end,
- {Acc,0};
- false ->
- %% This field was not present in the object thus there were no
- %% type in the table and we therefore generate code that returns
- %% the input for application treatment.
- emit([";",nl,indent(9),{asis,Name}," ->",nl]),
- emit([indent(12),"Len = case Val of",nl,
- indent(15),"Bin when is_binary(Bin) -> size(Bin);",nl,
- indent(15),"_ -> length(Val)",nl,indent(12),"end,",nl,
- indent(12),"{Val,Len}"]),
- {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 is_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 is_atom(Def) ->
- emit({indent(9),Def," ->",nl,indent(12)}),
- gen_encode_prim(ber,Type,["TagIn ++ ",{asis,Tag}],"Val");
- TRef when is_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,[{_,no_unique_value,_},T|Rest],
- ClName,ClFields,NthObj)->
- gen_objset_dec(Erules,ObjSName,UniqueName,[T|Rest],ClName,ClFields,
- NthObj);
-gen_objset_dec(_Erules,ObjSetName,UniqueName,[{_,no_unique_value,_}],
- _ClName,_ClFields,_NthObj)->
- emit_default_getdec(ObjSetName,UniqueName),
- emit({".",nl,nl});
-gen_objset_dec(Erules,ObjSName,UniqueName,[{ObjName,Val,Fields},T|Rest],
- ClName,ClFields,NthObj)->
- emit({"'getdec_",ObjSName,"'(",{asis,UniqueName},",",{asis,Val},
- ") ->",nl}),
- CurrMod = get(currmod),
- NewNthObj=
- case ObjName of
- {no_mod,no_name} ->
- gen_inlined_dec_funs(Erules,Fields,ClFields,ObjSName,
- NthObj);
- {CurrMod,Name} ->
- emit({" fun 'dec_",Name,"'/4"}),
- NthObj;
- {ModName,Name} ->
- emit_ext_fun(dec,ModName,Name),
-% emit([" {'",ModName,"', 'dec_",Name,"'}"]),
- 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}),
- CurrMod = get(currmod),
- case ObjName of
- {no_mod,no_name} ->
- gen_inlined_dec_funs(Erules,Fields,ClFields,ObjSetName,
- NthObj);
- {CurrMod,Name} ->
- emit({" fun 'dec_",Name,"'/4"});
- {ModName,Name} ->
- emit_ext_fun(dec,ModName,Name);
-% emit([" {'",ModName,"', 'dec_",Name,"'}"]);
- _Other ->
- emit({" fun 'dec_",ObjName,"'/4"})
- end,
- emit({";",nl}),
- emit_default_getdec(ObjSetName,UniqueName),
- 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 is_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(Erule,ObjSetName,UniqueName,
- ['EXTENSIONMARK','EXTENSIONMARK'|Rest],
- ClName,ClFields,NthObj) ->
- gen_objset_dec(Erule,ObjSetName,UniqueName,['EXTENSIONMARK'|Rest],
- ClName,ClFields,NthObj);
-gen_objset_dec(Erule,ObjSetName,UniqueName,['EXTENSIONMARK'|Rest],
- ClName,ClFields,NthObj) ->
- gen_objset_dec(Erule,ObjSetName,UniqueName,Rest++['EXTENSIONMARK'],
- ClName,ClFields,NthObj);
-gen_objset_dec(_,_,_,[],_,_,_) ->
- ok.
-
-emit_default_getdec(ObjSetName,UniqueName) ->
- emit(["'getdec_",ObjSetName,"'(",{asis,UniqueName},", ErrV) ->",nl]),
- emit([indent(3),"fun(C,V,_,_) -> exit({{component,C},{value,V}, {unique_name_and_value,",{asis,UniqueName},", ErrV}}) end"]).
-
-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,
- CurrMod = get(currmod),
- InternalDefFunName = [NthObj,Name,ObjSetName],
- case lists:keysearch(Name,1,Fields) of
- {value,{_,Type}} when is_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 is_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);
- {value,{_,#'Externaltypereference'{module=M,type=T}}} ->
- #typedef{typespec=Type} = asn1_db:dbget(M,T),
- OTag = Type#type.tag,
- Tag = [X#tag{class=decode_class(X#tag.class)}|| X <- OTag],
- emit({indent(3),"fun(Type, Bytes, TagIn, _RestPrimFieldName) ->",
- nl,indent(6),"case Type of",nl}),
- emit({indent(9),{asis,Name}," ->",nl}),
- if
- M == CurrMod ->
- emit([indent(12),"'dec_",T,"'(Bytes, ",DecProp,
- ", TagIn ++ ",{asis,Tag},")"]);
- true ->
- emit([indent(12),"'",M,"':'dec_",T,"'(Bytes, ",
- DecProp,", TagIn ++ ",{asis,Tag},")"])
- end,
- gen_inlined_dec_funs1(Erules,Fields,Rest,ObjSetName,NthObj);
- false ->
- emit([indent(3),"fun(Type, Bytes, TagIn, _RestPrimFieldName) ->",
- nl,indent(6),"case Type of",nl,
- indent(9),{asis,Name}," ->",nl,
- indent(12),"Len = case Bytes of",nl,
- indent(15),"B when is_binary(B) -> size(B);",nl,
- indent(15),"_ -> length(Bytes)",nl,
- indent(12),"end,",nl,
- indent(12),"{Bytes,[],Len}"]),
- gen_inlined_dec_funs1(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,
- CurrMod = get(currmod),
- InternalDefFunName = [NthObj,Name,ObjSetName],
- N=
- case lists:keysearch(Name,1,Fields) of
- {value,{_,Type}} when is_record(Type,type) ->
- emit({";",nl}),
- emit_inner_of_decfun(Erules,Type,DecProp,InternalDefFunName);
- {value,{_,Type}} when is_record(Type,typedef) ->
- emit({";",nl,indent(9),{asis,Name}," ->",nl}),
- emit_inner_of_decfun(Erules,Type,DecProp,InternalDefFunName);
- {value,{_,#'Externaltypereference'{module=M,type=T}}} ->
- #typedef{typespec=Type} = asn1_db:dbget(M,T),
- OTag = Type#type.tag,
- Tag = [X#tag{class=decode_class(X#tag.class)}|| X <- OTag],
- emit({";",nl,indent(9),{asis,Name}," ->",nl}),
- if
- M == CurrMod ->
- emit([indent(12),"'dec_",T,"'(Bytes, ",DecProp,
- ", TagIn ++ ",{asis,Tag},")"]);
- true ->
- emit([indent(12),"'",M,"':'dec_",T,"'(Bytes, ",
- DecProp,", TagIn ++ ",{asis,Tag},")"])
- end,
- 0;
- false ->
- emit([";",nl,
- indent(9),{asis,Name}," ->",nl,
- indent(12),"Len = case Bytes of",nl,
- indent(15),"B when is_binary(B) -> size(B);",nl,
- indent(15),"_ -> length(Bytes)",nl,
- indent(12),"end,",nl,
- indent(12),"{Bytes,[],Len}"]),
- 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 is_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 is_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('ObjectDescriptor') -> 7;
-decode_type('EXTERNAL') -> 8;
-decode_type('REAL') -> 9;
-decode_type('ENUMERATED') -> 10;
-decode_type('EMBEDDED_PDV') -> 11;
-decode_type('UTF8String') -> 12;
-decode_type('RELATIVE-OID') -> 13;
-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('T61String') -> 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 is_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.
-
-%% 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 597fb0030b..664dfc2086 100644
--- a/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl
+++ b/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl
@@ -114,31 +114,6 @@ gen_encode(Erules,Typename,Type) when is_record(Type,type) ->
_ -> % 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 is_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);
@@ -172,30 +147,6 @@ gen_encode_user(Erules,D) when is_record(D,typedef) ->
"'(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 is_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
@@ -419,7 +370,7 @@ gen_decode_selected(Erules,Type,FuncName) ->
" {Tlv,_} = ?RT_BER:decode(Bin2",asn1ct_gen:nif_parameter(),"),",nl]),
emit("{ok,"),
gen_decode_selected_type(Erules,Type),
- emit(["};",nl," Err -> exit({error,{selctive_decode,Err}})",nl,
+ emit(["};",nl," Err -> exit({error,{selective_decode,Err}})",nl,
" end.",nl]).
gen_decode_selected_type(_Erules,TypeDef) ->
@@ -1504,7 +1455,7 @@ gen_objset_dec(Erules,ObjSetName,_UniqueName,['EXTENSIONMARK'],_ClName,
emit([indent(2),"fun(_,Bytes, _RestPrimFieldName) ->",nl]),
case Erules of
- ber_bin_v2 ->
+ ber ->
emit([indent(4),"case Bytes of",nl,
indent(6),"Bin when is_binary(Bin) -> ",nl,
indent(8),"Bin;",nl,
@@ -1772,19 +1723,6 @@ mkfuncname(WhatKind,DecOrEnc) ->
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 ->
diff --git a/lib/asn1/src/asn1ct_gen_per.erl b/lib/asn1/src/asn1ct_gen_per.erl
index 5f42eacbdc..af19edb908 100644
--- a/lib/asn1/src/asn1ct_gen_per.erl
+++ b/lib/asn1/src/asn1ct_gen_per.erl
@@ -25,6 +25,7 @@
-include("asn1_records.hrl").
%-compile(export_all).
+-export([gen_dec_imm/2]).
-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]).
@@ -78,18 +79,6 @@ gen_encode(Erules,Typename,Type) when is_record(Type,type) ->
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);
@@ -146,16 +135,18 @@ gen_encode_prim(Erules,D,DoTag,Value) when is_record(D,type) ->
case D#type.def of
'INTEGER' ->
emit({"?RT_PER:encode_integer(", %fel
- {asis,effective_constraint(integer,Constraint)},",",Value,")"});
+ {asis,asn1ct_imm:effective_constraint(integer,Constraint)},
+ ",",Value,")"});
{'INTEGER',NamedNumberList} ->
emit({"?RT_PER:encode_integer(",
- {asis,effective_constraint(integer,Constraint)},",",Value,",",
+ {asis,asn1ct_imm:effective_constraint(integer,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}}],
case Erules of
- uper_bin ->
+ uper ->
emit(["case ",Value," of",nl]);
_ ->
emit(["case (case ",Value," of {_,",{curr,enumval},"}-> ",
@@ -168,7 +159,7 @@ gen_encode_prim(Erules,D,DoTag,Value) when is_record(D,type) ->
NewList = [X||{X,_} <- NamedNumberList],
NewC = [{'ValueRange',{0,length(NewList)-1}}],
case Erules of
- uper_bin ->
+ uper ->
emit(["case ",Value," of",nl]);
_ ->
emit(["case (case ",Value," of {_,",{curr,enumval},
@@ -184,7 +175,7 @@ gen_encode_prim(Erules,D,DoTag,Value) when is_record(D,type) ->
{asis,Constraint},",",Value,",",
{asis,NamedNumberList},")"});
'NULL' ->
- emit({"?RT_PER:encode_null(",Value,")"});
+ emit("[]");
'OBJECT IDENTIFIER' ->
emit({"?RT_PER:encode_object_identifier(",Value,")"});
'RELATIVE-OID' ->
@@ -274,7 +265,7 @@ emit_enc_enumerated_cases(Erule, C, [H1,H2|T], Count) ->
-emit_enc_enumerated_case(uper_bin,_C, {asn1_enum,High}, _) ->
+emit_enc_enumerated_case(uper,_C, {asn1_enum,High}, _) ->
emit([
"{asn1_enum,EnumV} when is_integer(EnumV), EnumV > ",High," -> ",
"[<<1:1>>,?RT_PER:encode_small_number(EnumV)]"]);
@@ -284,124 +275,27 @@ emit_enc_enumerated_case(_Per,_C, {asn1_enum,High}, _) ->
"[{bit,1},?RT_PER:encode_small_number(EnumV)]"]);
emit_enc_enumerated_case(_Erule, _C, 'EXT_MARK', _Count) ->
true;
-emit_enc_enumerated_case(uper_bin,_C, {1,EnumName}, Count) ->
+emit_enc_enumerated_case(uper,_C, {1,EnumName}, Count) ->
emit(["'",EnumName,"' -> [<<1:1>>,?RT_PER:encode_small_number(",Count,")]"]);
emit_enc_enumerated_case(_Per,_C, {1,EnumName}, Count) ->
emit(["'",EnumName,"' -> [{bit,1},?RT_PER:encode_small_number(",Count,")]"]);
-emit_enc_enumerated_case(uper_bin,C, {0,EnumName}, Count) ->
+emit_enc_enumerated_case(uper,C, {0,EnumName}, Count) ->
emit(["'",EnumName,"' -> [<<0:1>>,?RT_PER:encode_integer(",{asis,C},", ",Count,")]"]);
emit_enc_enumerated_case(_Per,C, {0,EnumName}, Count) ->
emit(["'",EnumName,"' -> [{bit,0},?RT_PER:encode_integer(",{asis,C},", ",Count,")]"]);
emit_enc_enumerated_case(_Erule, C, EnumName, Count) ->
emit(["'",EnumName,"' -> ?RT_PER:encode_integer(",{asis,C},", ",Count,")"]).
-%% 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),
- greatest_common_range(SV,VR).
-
-effective_constr(_,[]) ->
- [];
-effective_constr('SingleValue',List) ->
- SVList = lists:flatten(lists:map(fun(X)->element(2,X)end,List)),
- % sort and remove duplicates
- SortedSVList = lists:sort(SVList),
- RemoveDup = fun([],_) ->[];
- ([H],_) -> [H];
- ([H,H|T],F) -> F([H|T],F);
- ([H|T],F) -> [H|F(T,F)]
- end,
-
- case RemoveDup(SortedSVList,RemoveDup) of
- [N] ->
- [{'SingleValue',N}];
- L when is_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(SV,VR) ->
- greatest_common_range2(mk_vr(SV),mk_vr(VR)).
-greatest_common_range2({_,Int},{'MIN',Ub}) when is_integer(Int),
- Int > Ub ->
- [{'ValueRange',{'MIN',Int}}];
-greatest_common_range2({_,Int},{Lb,Ub}) when is_integer(Int),
- Int < Lb ->
- [{'ValueRange',{Int,Ub}}];
-greatest_common_range2({_,Int},VR={_Lb,_Ub}) when is_integer(Int) ->
- [{'ValueRange',VR}];
-greatest_common_range2({_,L},{Lb,Ub}) when is_list(L) ->
- Min = least_Lb([Lb|L]),
- Max = greatest_Ub([Ub|L]),
- [{'ValueRange',{Min,Max}}];
-greatest_common_range2({Lb1,Ub1},{Lb2,Ub2}) ->
- Min = least_Lb([Lb1,Lb2]),
- Max = greatest_Ub([Ub1,Ub2]),
- [{'ValueRange',{Min,Max}}].
-
-mk_vr([{Type,I}]) when is_atom(Type), is_integer(I) ->
- {I,I};
-mk_vr([{Type,{Lb,Ub}}]) when is_atom(Type) ->
- {Lb,Ub};
-mk_vr(Other) ->
- Other.
-
-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)
+get_constraint([{Key,V}], Key) ->
+ V;
+get_constraint([], _) ->
+ no;
+get_constraint(C, Key) ->
+ case lists:keyfind(Key, 1, C) of
+ false -> no;
+ {Key,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.
-
-
%% Object code generating for encoding and decoding
%% ------------------------------------------------
@@ -448,7 +342,7 @@ gen_encode_objectfields(Erule,ClassName,[{typefield,Name,OptOrMand}|Rest],
{false,'OPTIONAL'} ->
EmitFuncClause("Val"),
case Erule of
- uper_bin ->
+ uper ->
emit(" Val");
_ ->
emit(" [{octets,Val}]")
@@ -839,7 +733,7 @@ gen_objset_enc(Erule,ObjSetName,_UniqueName,['EXTENSIONMARK'],_ClName,
emit({"'getenc_",ObjSetName,"'(_, _) ->",nl}),
emit({indent(3),"fun(_, Val, _) ->",nl}),
case Erule of
- uper_bin ->
+ uper ->
emit([indent(6),"Val",nl]);
_ ->
emit([indent(6),"[{octets,Val}]",nl])
@@ -889,7 +783,7 @@ gen_inlined_enc_funs(Erule,Fields,[{typefield,Name,_}|Rest],ObjSetName,NthObj) -
emit({indent(9),{asis,Name}," ->",nl}),
emit([indent(12),"'",M,"'",":'enc_",T,"'(Val)"]),
gen_inlined_enc_funs1(Erule,Fields,Rest,ObjSetName,NthObj,[]);
- false when Erule == uper_bin ->
+ false when Erule =:= uper ->
emit([indent(3),"fun(Type,Val,_) ->",nl,
indent(6),"case Type of",nl,
indent(9),{asis,Name}," -> Val",nl]),
@@ -927,7 +821,7 @@ gen_inlined_enc_funs1(Erule,Fields,[{typefield,Name,_}|Rest],ObjSetName,
emit({";",nl,indent(9),{asis,Name}," ->",nl}),
emit([indent(12),"'",M,"'",":'enc_",T,"'(Val)"]),
{Acc,0};
- false when Erule == uper_bin ->
+ false when Erule =:= uper ->
emit([";",nl,
indent(9),{asis,Name}," -> ",nl,
"Val",nl]),
@@ -1037,7 +931,6 @@ 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;
@@ -1053,76 +946,42 @@ emit_default_getdec(ObjSetName,UniqueName) ->
emit([indent(2), "fun(C,V,_,_) -> exit({{component,C},{value,V},{unique_name_and_value,",{asis,UniqueName},",ErrV}}) end"]).
-gen_inlined_dec_funs(Fields,[{typefield,Name,_}|Rest],
- ObjSetName,NthObj) ->
- CurrMod = get(currmod),
- InternalDefFunName = [NthObj,Name,ObjSetName],
- case lists:keysearch(Name,1,Fields) of
- {value,{_,Type}} when is_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 is_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);
- {value,{_,#'Externaltypereference'{module=CurrMod,type=T}}} ->
- emit({indent(3),"fun(Type, Val, _, _) ->",nl,
- indent(6),"case Type of",nl}),
- emit({indent(9),{asis,Name}," ->",nl}),
- emit([indent(12),"'dec_",T,"'(Val, telltype)"]),
- gen_inlined_dec_funs1(Fields,Rest,ObjSetName,NthObj);
- {value,{_,#'Externaltypereference'{module=M,type=T}}} ->
- emit({indent(3),"fun(Type, Val, _, _) ->",nl,
- indent(6),"case Type of",nl}),
- emit({indent(9),{asis,Name}," ->",nl}),
- emit([indent(12),"'",M,"':'dec_",T,"'(Val, telltype)"]),
- gen_inlined_dec_funs1(Fields,Rest,ObjSetName,NthObj);
- false ->
- emit([indent(3),"fun(Type, Val, _, _) ->",nl,
- indent(6),"case Type of",nl,
- indent(9),{asis,Name}," ->{Val,Type}"]),
- gen_inlined_dec_funs1(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) ->
+gen_inlined_dec_funs(Fields, List, ObjSetName, NthObj0) ->
+ emit([indent(3),"fun(Type, Val, _, _) ->",nl,
+ indent(6),"case Type of",nl]),
+ NthObj = gen_inlined_dec_funs1(Fields, List, ObjSetName, "", NthObj0),
+ emit([nl,indent(6),"end",nl,
+ indent(3),"end"]),
NthObj.
-gen_inlined_dec_funs1(Fields,[{typefield,Name,_}|Rest],
- ObjSetName,NthObj) ->
+gen_inlined_dec_funs1(Fields, [{typefield,Name,_}|Rest],
+ ObjSetName, Sep0, NthObj) ->
CurrentMod = get(currmod),
InternalDefFunName = [NthObj,Name,ObjSetName],
- N=case lists:keysearch(Name,1,Fields) of
- {value,{_,Type}} when is_record(Type,type) ->
- emit({";",nl}),
- emit_inner_of_decfun(Type,InternalDefFunName);
- {value,{_,Type}} when is_record(Type,typedef) ->
- emit({";",nl,indent(9),{asis,Name}," ->",nl}),
- emit_inner_of_decfun(Type,InternalDefFunName);
- {value,{_,#'Externaltypereference'{module=CurrentMod,type=T}}} ->
- emit([";",nl,indent(9),{asis,Name}," ->",nl]),
- emit([indent(12),"'dec_",T,"'(Val,telltype)"]),
- 0;
- {value,{_,#'Externaltypereference'{module=M,type=T}}} ->
- emit([";",nl,indent(9),{asis,Name}," ->",nl]),
- emit([indent(12),"'",M,"'",":'dec_",T,"'(Val,telltype)"]),
- 0;
- false ->
- emit([";",nl,
- indent(9),{asis,Name}," ->{Val,Type}"]),
- 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(Sep0),
+ Sep = [";",nl],
+ N = case lists:keyfind(Name, 1, Fields) of
+ {_,#type{}=Type} ->
+ emit_inner_of_decfun(Type, InternalDefFunName);
+ {_,#typedef{}=Type} ->
+ emit([indent(9),{asis,Name}," ->",nl]),
+ emit_inner_of_decfun(Type, InternalDefFunName);
+ {_,#'Externaltypereference'{module=CurrentMod,type=T}} ->
+ emit([indent(9),{asis,Name}," ->",nl,
+ indent(12),"'dec_",T,"'(Val,telltype)"]),
+ 0;
+ {_,#'Externaltypereference'{module=M,type=T}} ->
+ emit([indent(9),{asis,Name}," ->",nl,
+ indent(12),"'",M,"':'dec_",T,"'(Val,telltype)"]),
+ 0;
+ false ->
+ emit([indent(9),{asis,Name}," -> {Val,Type}"]),
+ 0
+ end,
+ gen_inlined_dec_funs1(Fields, Rest, ObjSetName, Sep, NthObj+N);
+gen_inlined_dec_funs1(Fields, [_|Rest], ObjSetName, Sep, NthObj) ->
+ gen_inlined_dec_funs1(Fields, Rest, ObjSetName, Sep, NthObj);
+gen_inlined_dec_funs1(_, [], _, _, NthObj) -> NthObj.
emit_inner_of_decfun(#typedef{name={ExtName,Name},typespec=Type},
InternalDefFunName) ->
@@ -1234,19 +1093,47 @@ gen_decode_user(Erules,D) when is_record(D,typedef) ->
exit({error,{asn1,{unknown,Other}}})
end.
+gen_dec_imm(Erule, #type{def=Name,constraint=C}) ->
+ Aligned = case Erule of
+ uper -> false;
+ per -> true
+ end,
+ gen_dec_imm_1(Name, C, Aligned).
+
+gen_dec_imm_1('ASN1_OPEN_TYPE', Constraint, Aligned) ->
+ imm_decode_open_type(Constraint, Aligned);
+gen_dec_imm_1('ANY', _Constraint, Aligned) ->
+ imm_decode_open_type([], Aligned);
+gen_dec_imm_1('BOOLEAN', _Constr, _Aligned) ->
+ asn1ct_imm:per_dec_boolean();
+gen_dec_imm_1({'ENUMERATED',{Base,Ext}}, _Constr, Aligned) ->
+ asn1ct_imm:per_dec_enumerated(Base, Ext, Aligned);
+gen_dec_imm_1({'ENUMERATED',NamedNumberList}, _Constr, Aligned) ->
+ asn1ct_imm:per_dec_enumerated(NamedNumberList, Aligned);
+gen_dec_imm_1('INTEGER', Constr, Aligned) ->
+ asn1ct_imm:per_dec_integer(Constr, Aligned);
+gen_dec_imm_1({'INTEGER',NamedNumberList}, Constraint, Aligned) ->
+ asn1ct_imm:per_dec_named_integer(Constraint,
+ NamedNumberList,
+ Aligned);
+gen_dec_imm_1('OCTET STRING', Constraint, Aligned) ->
+ SzConstr = get_constraint(Constraint, 'SizeConstraint'),
+ Imm = asn1ct_imm:per_dec_octet_string(SzConstr, Aligned),
+ {convert,binary_to_list,Imm};
+gen_dec_imm_1(_, _, _) -> no.
+
+gen_dec_prim(Erule, Type, BytesVar) ->
+ case gen_dec_imm(Erule, Type) of
+ no ->
+ gen_dec_prim_1(Erule, Type, BytesVar);
+ Imm ->
+ asn1ct_imm:dec_code_gen(Imm, BytesVar)
+ end.
-gen_dec_prim(Erules,Att,BytesVar) ->
- Typename = Att#type.def,
- Constraint = Att#type.constraint,
+gen_dec_prim_1(Erule,
+ #type{def=Typename,constraint=Constraint}=Att,
+ BytesVar) ->
case Typename of
- 'INTEGER' ->
- emit({"?RT_PER:decode_integer(",BytesVar,",",
- {asis,effective_constraint(integer,Constraint)},")"});
- {'INTEGER',NamedNumberList} ->
- emit({"?RT_PER:decode_integer(",BytesVar,",",
- {asis,effective_constraint(integer,Constraint)},",",
- {asis,NamedNumberList},")"});
-
'REAL' ->
emit({"?RT_PER:decode_real(",BytesVar,")"});
@@ -1262,8 +1149,7 @@ gen_dec_prim(Erules,Att,BytesVar) ->
{asis,NamedNumberList},")"})
end;
'NULL' ->
- emit({"?RT_PER:decode_null(",
- BytesVar,")"});
+ emit({"{'NULL',",BytesVar,"}"});
'OBJECT IDENTIFIER' ->
emit({"?RT_PER:decode_object_identifier(",
BytesVar,")"});
@@ -1273,24 +1159,6 @@ gen_dec_prim(Erules,Att,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},")"});
@@ -1328,42 +1196,12 @@ gen_dec_prim(Erules,Att,BytesVar) ->
",",{asis,Constraint},")"});
'UTF8String' ->
emit({"?RT_PER:decode_UTF8String(",BytesVar,")"});
- 'ANY' ->
- case Erules of
- per ->
- emit(["fun() -> {XTerm,YTermXBytes} = ?RT_PER:decode_open_type(",BytesVar,",",{asis,Constraint}, "), {binary_to_list(XTerm),XBytes} end ()"]);
- _ ->
- emit(["?RT_PER:decode_open_type(",BytesVar,",",
- {asis,Constraint}, ")"])
- end;
- '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,")"]);
- _ ->
- case Erules of
- per ->
- emit(["fun() -> {XTerm,XBytes} = ?RT_PER:decode_open_type(",BytesVar,", []), {binary_to_list(XTerm),XBytes} end()"]);
- _ ->
- emit(["?RT_PER:decode_open_type(",BytesVar,",[])"])
- end
- end;
#'ObjectClassFieldType'{} ->
- case asn1ct_gen:get_inner(Att#type.def) of
+ case asn1ct_gen:get_inner(Typename) of
{fixedtypevaluefield,_,InnerType} ->
- gen_dec_prim(Erules,InnerType,BytesVar);
+ gen_dec_prim(Erule, InnerType, BytesVar);
T ->
- gen_dec_prim(Erules,Att#type{def=T},BytesVar)
+ gen_dec_prim(Erule, Att#type{def=T}, BytesVar)
end;
Other ->
exit({'cant decode' ,Other})
@@ -1423,3 +1261,22 @@ extaddgroup2sequence([C|T],ExtNum,Acc) ->
extaddgroup2sequence(T,ExtNum,[C|Acc]);
extaddgroup2sequence([],_,Acc) ->
lists:reverse(Acc).
+
+imm_decode_open_type([#'Externaltypereference'{type=Tname}], Aligned) ->
+ imm_dec_open_type_1(Tname, Aligned);
+imm_decode_open_type([#type{def=#'Externaltypereference'{type=Tname}}],
+ Aligned) ->
+ imm_dec_open_type_1(Tname, Aligned);
+imm_decode_open_type(_, Aligned) ->
+ asn1ct_imm:per_dec_open_type(Aligned).
+
+imm_dec_open_type_1(Type, Aligned) ->
+ D = fun(OpenType, Buf) ->
+ asn1ct_name:new(tmpval),
+ emit(["begin",nl,
+ "{",{curr,tmpval},",_} = ",
+ "dec_",Type,"(",OpenType,", mandatory),",nl,
+ "{",{curr,tmpval},com,Buf,"}",nl,
+ "end"])
+ end,
+ {call,D,asn1ct_imm:per_dec_open_type(Aligned)}.
diff --git a/lib/asn1/src/asn1ct_gen_per_rt2ct.erl b/lib/asn1/src/asn1ct_gen_per_rt2ct.erl
index eda0faad3c..4f4563833f 100644
--- a/lib/asn1/src/asn1ct_gen_per_rt2ct.erl
+++ b/lib/asn1/src/asn1ct_gen_per_rt2ct.erl
@@ -69,18 +69,6 @@ gen_encode(Erules,Typename,Type) when is_record(Type,type) ->
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);
@@ -176,7 +164,7 @@ gen_encode_prim(Erules,D,DoTag,Value) when is_record(D,type) ->
{asis,NamedNumberList},")"})
end;
'NULL' ->
- emit({"?RT_PER:encode_null(",Value,")"});
+ emit("[]");
'OBJECT IDENTIFIER' ->
emit({"?RT_PER:encode_object_identifier(",Value,")"});
'RELATIVE-OID' ->
@@ -417,50 +405,50 @@ emit_enc_octet_string(_Erules,Constraint,Value) ->
asn1ct_name:new(tmpval),
emit({" begin",nl}),
emit({" [",{curr,tmpval},"] = ",Value,",",nl}),
- emit({" [10,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({" [[10,8,",{curr,tmpval},"],[10,8,",
- {next,tmpval},"]]",nl}),
- emit(" end"),
- asn1ct_name:new(tmpval);
- Sv when is_integer(Sv),Sv =< 256 ->
+ emit([" begin",nl,
+ " ",{curr,tmpval}," = ",Value,",",nl,
+ " case length(",{curr,tmpval},") of",nl,
+ " 2 ->",nl,
+ " [[45,16,2]|",{curr,tmpval},"];",nl,
+ " _ ->",nl,
+ " exit({error,{value_out_of_bounds,",
+ {curr,tmpval},"}})",nl,
+ " end",nl,
+ " end"]);
+ Sv when is_integer(Sv), Sv < 256 ->
asn1ct_name:new(tmpval),
- emit({" begin",nl}),
- emit({" case length(",Value,") of",nl}),
- emit([" ",{curr,tmpval}," when ",{curr,tmpval}," == ",Sv," ->"]),
- emit([" [2,20,",{curr,tmpval},",",Value,"];",nl]),
- emit({" _ -> exit({error,{value_out_of_bounds,",
- Value,"}})", nl," end",nl}),
- emit(" end");
+ asn1ct_name:new(tmplen),
+ emit([" begin",nl,
+ " ",{curr,tmpval}," = ",Value,",",nl,
+ " case length(",{curr,tmpval},") of",nl,
+ " ",Sv,"=",{curr,tmplen}," ->",nl,
+ " [20,",{curr,tmplen},"|",{curr,tmpval},"];",nl,
+ " _ ->",nl,
+ " exit({error,{value_out_of_bounds,",
+ {curr,tmpval},"}})",nl,
+ " end",nl,
+ " end"]);
Sv when is_integer(Sv),Sv =< 65535 ->
asn1ct_name:new(tmpval),
- emit({" begin",nl}),
- emit({" case length(",Value,") of",nl}),
- emit([" ",{curr,tmpval}," when ",{curr,tmpval}," == ",Sv," ->"]),
- emit([" [2,21,",{curr,tmpval},",",Value,"];",nl]),
- emit({" _ -> exit({error,{value_out_of_bounds,",
- Value,"}})",nl," end",nl}),
- emit(" end");
+ asn1ct_name:new(tmplen),
+ emit([" begin",nl,
+ " ",{curr,tmpval}," = ",Value,",",nl,
+ " case length(",{curr,tmpval},") of",nl,
+ " ",Sv,"=",{curr,tmplen}," ->",nl,
+ " [<<21,",{curr,tmplen},":16>>|",Value,"];",nl,
+ " _ ->",nl,
+ " exit({error,{value_out_of_bounds,",
+ {curr,tmpval},"}})",nl,
+ " end",nl,
+ " 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}} ->
@@ -624,23 +612,6 @@ get_constraint(C,Key) ->
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,...]
@@ -657,74 +628,9 @@ keysearch_allwithkey(Key,Ix,C,Acc) ->
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);
+ pre_encode(integer, asn1ct_imm:effective_constraint(integer, C));
effective_constraint(bitstring,C) ->
- get_constraint(C,'SizeConstraint').
-
-effective_constr(_,[]) ->
- [];
-effective_constr('SingleValue',List) ->
- SVList = lists:flatten(lists:map(fun(X)->element(2,X)end,List)),
- % sort and remove duplicates
- RemoveDup = fun([],_) ->[];
- ([H],_) -> [H];
- ([H,H|T],F) -> F([H|T],F);
- ([H|T],F) -> [H|F(T,F)]
- end,
-
- case RemoveDup(SVList,RemoveDup) of
- [N] ->
- [{'SingleValue',N}];
- L when is_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 is_integer(Int),
- Int > Ub ->
- [{'ValueRange',{'MIN',Int}}];
-greatest_common_range([{_,Int}],[{_,{Lb,Ub}}]) when is_integer(Int),
- Int < Lb ->
- [{'ValueRange',{Int,Ub}}];
-greatest_common_range([{_,Int}],VR=[{_,{_Lb,_Ub}}]) when is_integer(Int) ->
- VR;
-greatest_common_range([{_,L}],[{_,{Lb,Ub}}]) when is_list(L) ->
- Min = least_Lb([Lb|L]),
- Max = greatest_Ub([Ub|L]),
- [{'ValueRange',{Min,Max}}];
-greatest_common_range([{_,{Lb1,Ub1}}],[{_,{Lb2,Ub2}}]) ->
- Min = least_Lb([Lb1,Lb2]),
- Max = greatest_Ub([Ub1,Ub2]),
- [{'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.
-
-
-
+ asn1ct_imm:effective_constraint(bitstring, C).
pre_encode(integer,[]) ->
[];
@@ -1385,7 +1291,6 @@ 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;
@@ -1401,77 +1306,42 @@ emit_default_getdec(ObjSetName,UniqueName) ->
emit([indent(2), "fun(C,V,_,_) -> exit({{component,C},{value,V},{unique_name_and_value,",{asis,UniqueName},",ErrV}}) end"]).
-gen_inlined_dec_funs(Fields,[{typefield,Name,_}|Rest],
- ObjSetName,NthObj) ->
- CurrMod = get(currmod),
- InternalDefFunName = [NthObj,Name,ObjSetName],
- case lists:keysearch(Name,1,Fields) of
- {value,{_,Type}} when is_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 is_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);
- {value,{_,#'Externaltypereference'{module=CurrMod,type=T}}} ->
- emit({indent(3),"fun(Type, Val, _, _) ->",nl,
- indent(6),"case Type of",nl}),
- emit({indent(9),{asis,Name}," ->",nl}),
- emit([indent(12),"'dec_",T,"'(Val, telltype)"]),
- gen_inlined_dec_funs1(Fields,Rest,ObjSetName,NthObj);
- {value,{_,#'Externaltypereference'{module=M,type=T}}} ->
- emit({indent(3),"fun(Type, Val, _, _) ->",nl,
- indent(6),"case Type of",nl}),
- emit({indent(9),{asis,Name}," ->",nl}),
- emit([indent(12),"'",M,"':'dec_",T,"'(Val, telltype)"]),
- gen_inlined_dec_funs1(Fields,Rest,ObjSetName,NthObj);
- false ->
- emit([indent(3),"fun(Type, Val, _, _) ->",nl,
- indent(6),"case Type of",nl,
- indent(9),{asis,Name}," -> {Val,Type}"]),
- gen_inlined_dec_funs1(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) ->
+gen_inlined_dec_funs(Fields, List, ObjSetName, NthObj0) ->
+ emit([indent(3),"fun(Type, Val, _, _) ->",nl,
+ indent(6),"case Type of",nl]),
+ NthObj = gen_inlined_dec_funs1(Fields, List, ObjSetName, "", NthObj0),
+ emit([nl,indent(6),"end",nl,
+ indent(3),"end"]),
NthObj.
-gen_inlined_dec_funs1(Fields,[{typefield,Name,_}|Rest],
- ObjSetName,NthObj) ->
+gen_inlined_dec_funs1(Fields, [{typefield,Name,_}|Rest],
+ ObjSetName, Sep0, NthObj) ->
CurrentMod = get(currmod),
InternalDefFunName = [NthObj,Name,ObjSetName],
- N=
- case lists:keysearch(Name,1,Fields) of
- {value,{_,Type}} when is_record(Type,type) ->
- emit({";",nl}),
- emit_inner_of_decfun(Type,InternalDefFunName);
- {value,{_,Type}} when is_record(Type,typedef) ->
- emit({";",nl,indent(9),{asis,Name}," ->",nl}),
- emit_inner_of_decfun(Type,InternalDefFunName);
- {value,{_,#'Externaltypereference'{module=CurrentMod,type=T}}} ->
- emit([";",nl,indent(9),{asis,Name}," ->",nl]),
- emit([indent(12),"'dec_",T,"'(Val,telltype)"]),
- 0;
- {value,{_,#'Externaltypereference'{module=M,type=T}}} ->
- emit([";",nl,indent(9),{asis,Name}," ->",nl]),
- emit([indent(12),"'",M,"':'dec_",T,"'(Val,telltype)"]),
- 0;
- false ->
- emit([";",nl,
- indent(9),{asis,Name}," -> {Val,Type}"]),
- 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(Sep0),
+ Sep = [";",nl],
+ N = case lists:keyfind(Name, 1, Fields) of
+ {_,#type{}=Type} ->
+ emit_inner_of_decfun(Type, InternalDefFunName);
+ {_,#typedef{}=Type} ->
+ emit([indent(9),{asis,Name}," ->",nl]),
+ emit_inner_of_decfun(Type, InternalDefFunName);
+ {_,#'Externaltypereference'{module=CurrentMod,type=T}} ->
+ emit([indent(9),{asis,Name}," ->",nl,
+ indent(12),"'dec_",T,"'(Val,telltype)"]),
+ 0;
+ {_,#'Externaltypereference'{module=M,type=T}} ->
+ emit([indent(9),{asis,Name}," ->",nl,
+ indent(12),"'",M,"':'dec_",T,"'(Val,telltype)"]),
+ 0;
+ false ->
+ emit([indent(9),{asis,Name}," -> {Val,Type}"]),
+ 0
+ end,
+ gen_inlined_dec_funs1(Fields, Rest, ObjSetName, Sep, NthObj+N);
+gen_inlined_dec_funs1(Fields, [_|Rest], ObjSetName, Sep, NthObj) ->
+ gen_inlined_dec_funs1(Fields, Rest, ObjSetName, Sep, NthObj);
+gen_inlined_dec_funs1(_, [], _, _, NthObj) -> NthObj.
emit_inner_of_decfun(#typedef{name={ExtName,Name},typespec=Type},
InternalDefFunName) ->
@@ -1591,17 +1461,9 @@ gen_dec_prim(Erules,Att,BytesVar) ->
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},")"});
-
+ asn1ct_gen_per:gen_dec_prim(Erules, Att, BytesVar);
+ {'INTEGER',_NamedNumberList} ->
+ asn1ct_gen_per:gen_dec_prim(Erules, Att, BytesVar);
'REAL' ->
emit(["?RT_PER:decode_real(",BytesVar,")"]);
@@ -1617,8 +1479,7 @@ gen_dec_prim(Erules,Att,BytesVar) ->
{asis,NamedNumberList},")"})
end;
'NULL' ->
- emit({"?RT_PER:decode_null(",
- BytesVar,")"});
+ emit({"{'NULL',",BytesVar,"}"});
'OBJECT IDENTIFIER' ->
emit({"?RT_PER:decode_object_identifier(",
BytesVar,")"});
@@ -1628,23 +1489,13 @@ gen_dec_prim(Erules,Att,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} ->
- NewNNL = [X||{X,_} <- NamedNumberList],
- NewC = effective_constraint(integer,
- [{'ValueRange',{0,length(NewNNL)-1}}]),
- emit_dec_enumerated(BytesVar,NewC,NewNNL);
+ {'ENUMERATED',_} ->
+ asn1ct_gen_per:gen_dec_prim(Erules, Att, BytesVar);
'BOOLEAN'->
- emit({"?RT_PER:decode_boolean(",BytesVar,")"});
+ asn1ct_gen_per:gen_dec_prim(Erules, Att, BytesVar);
'OCTET STRING' ->
- emit_dec_octet_string(Constraint,BytesVar);
+ asn1ct_gen_per:gen_dec_prim(Erules, Att, BytesVar);
'NumericString' ->
emit_dec_known_multiplier_string('NumericString',
@@ -1691,25 +1542,9 @@ gen_dec_prim(Erules,Att,BytesVar) ->
'UTF8String' ->
emit({"?RT_PER:decode_UTF8String(",BytesVar,")"});
'ANY' ->
- emit(["?RT_PER:decode_open_type(",BytesVar,",",
- {asis,Constraint}, ")"]);
+ asn1ct_gen_per:gen_dec_prim(Erules, Att, BytesVar);
'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;
+ asn1ct_gen_per:gen_dec_prim(Erules, Att, BytesVar);
#'ObjectClassFieldType'{} ->
case asn1ct_gen:get_inner(Att#type.def) of
{fixedtypevaluefield,_,InnerType} ->
@@ -1721,88 +1556,6 @@ gen_dec_prim(Erules,Att,BytesVar) ->
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 is_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 is_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,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([Name|Rest],Tmpremain,No) ->
- io_lib:format("~w->{~w,~s};",[No,Name,Tmpremain])++
- 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
diff --git a/lib/asn1/src/asn1ct_imm.erl b/lib/asn1/src/asn1ct_imm.erl
new file mode 100644
index 0000000000..34bb0b8714
--- /dev/null
+++ b/lib/asn1/src/asn1ct_imm.erl
@@ -0,0 +1,626 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, 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(asn1ct_imm).
+-export([per_dec_boolean/0,per_dec_enumerated/2,per_dec_enumerated/3,
+ per_dec_extension_map/1,
+ per_dec_integer/2,per_dec_length/3,per_dec_named_integer/3,
+ per_dec_octet_string/2,per_dec_open_type/1]).
+-export([optimize_alignment/1,optimize_alignment/2,
+ dec_slim_cg/2,dec_code_gen/2]).
+-export([effective_constraint/2]).
+-import(asn1ct_gen, [emit/1]).
+
+-record(st, {var,
+ base}).
+
+dec_slim_cg(Imm0, BytesVar) ->
+ {Imm,_} = optimize_alignment(Imm0),
+ asn1ct_name:new(v),
+ [H|T] = atom_to_list(asn1ct_name:curr(v)) ++ "@",
+ VarBase = [H-($a-$A)|T],
+ St0 = #st{var=0,base=VarBase},
+ {Res,Pre,_} = flatten(Imm, BytesVar, St0),
+ dcg_list_outside(Pre),
+ Res.
+
+dec_code_gen(Imm, BytesVar) ->
+ emit(["begin",nl]),
+ {Dst,DstBuf} = dec_slim_cg(Imm, BytesVar),
+ emit([",",nl,
+ "{",Dst,",",DstBuf,"}",nl,
+ "end"]),
+ ok.
+
+optimize_alignment(Imm) ->
+ opt_al(Imm, unknown).
+
+optimize_alignment(Imm, Al) ->
+ opt_al(Imm, Al).
+
+
+per_dec_boolean() ->
+ {map,{get_bits,1,[1]},[{0,false},{1,true}]}.
+
+per_dec_enumerated(NamedList0, Aligned) ->
+ Constraint = [{'ValueRange',{0,length(NamedList0)-1}}],
+ NamedList = per_dec_enumerated_fix_list(NamedList0, [enum_error], 0),
+ Int = per_dec_integer(Constraint, Aligned),
+ {map,Int,NamedList}.
+
+per_dec_enumerated(BaseNamedList, NamedListExt0, Aligned) ->
+ Base = per_dec_enumerated(BaseNamedList, Aligned),
+ NamedListExt = per_dec_enumerated_fix_list(NamedListExt0,
+ [enum_default], 0),
+ Ext = {map,per_dec_normally_small_number(Aligned),NamedListExt},
+ bit_case(Base, Ext).
+
+per_dec_extension_map(Aligned) ->
+ Len = {add,per_dec_normally_small_number(Aligned),1},
+ {get_bits,Len,[1,bitstring]}.
+
+per_dec_integer(Constraint0, Aligned) ->
+ Constraint = effective_constraint(integer, Constraint0),
+ per_dec_integer_1(Constraint, Aligned).
+
+per_dec_length(SingleValue, _, _Aligned) when is_integer(SingleValue) ->
+ {value,SingleValue};
+per_dec_length({S,S}, _, _Aligned) when is_integer(S) ->
+ {value,S};
+per_dec_length({{_,_}=Constr,_}, AllowZero, Aligned) ->
+ bit_case(per_dec_length(Constr, AllowZero, Aligned),
+ per_dec_length(undefined, AllowZero, Aligned));
+per_dec_length({Lb,Ub}, _AllowZero, Aligned) when is_integer(Lb),
+ is_integer(Lb),
+ Ub =< 65535 ->
+ per_dec_constrained(Lb, Ub, Aligned);
+per_dec_length({_,_}, AllowZero, Aligned) ->
+ decode_unconstrained_length(AllowZero, Aligned);
+per_dec_length(undefined, AllowZero, Aligned) ->
+ decode_unconstrained_length(AllowZero, Aligned).
+
+per_dec_named_integer(Constraint, NamedList0, Aligned) ->
+ Int = per_dec_integer(Constraint, Aligned),
+ NamedList = [{K,V} || {V,K} <- NamedList0] ++ [integer_default],
+ {map,Int,NamedList}.
+
+per_dec_octet_string(Constraint, Aligned) ->
+ dec_string(Constraint, 8, Aligned).
+
+per_dec_open_type(Aligned) ->
+ {get_bits,decode_unconstrained_length(true, Aligned),
+ [8,binary,{align,Aligned}]}.
+
+
+%%%
+%%% Local functions.
+%%%
+
+dec_string(Sv, U, _Aligned) when is_integer(Sv), U*Sv =< 16 ->
+ {get_bits,Sv,[U,binary]};
+dec_string(Sv, U, Aligned) when is_integer(Sv), Sv < 16#10000 ->
+ {get_bits,Sv,[U,binary,{align,Aligned}]};
+dec_string(C, U, Aligned) when is_list(C) ->
+ dec_string({hd(C),lists:max(C)}, U, Aligned);
+dec_string({Sv,Sv}, U, Aligned) ->
+ dec_string(Sv, U, Aligned);
+dec_string({{_,_}=C,_}, U, Aligned) ->
+ bit_case(dec_string(C, U, Aligned),
+ dec_string(no, U, Aligned));
+dec_string({Lb,Ub}, U, Aligned) when Ub < 16#10000 ->
+ Len = per_dec_constrained(Lb, Ub, Aligned),
+ {get_bits,Len,[U,binary,{align,Aligned}]};
+dec_string(_, U, Aligned) ->
+ Al = [{align,Aligned}],
+ DecRest = fun(V, Buf) ->
+ emit(["?RT_PER:decode_fragmented(",V,", ",
+ Buf,", ",U,")"])
+ end,
+ {'case',[{test,{get_bits,1,[1|Al]},0,
+ {value,{get_bits,
+ {get_bits,7,[1]},
+ [U,binary]}}},
+ {test,{get_bits,1,[1|Al]},1,
+ {test,{get_bits,1,[1]},0,
+ {value,{get_bits,
+ {get_bits,14,[1]},
+ [U,binary]}}}},
+ {test,{get_bits,1,[1|Al]},1,
+ {test,{get_bits,1,[1]},1,
+ {value,{call,DecRest,{get_bits,6,[1]}}}}}]}.
+
+per_dec_enumerated_fix_list([{V,_}|T], Tail, N) ->
+ [{N,V}|per_dec_enumerated_fix_list(T, Tail, N+1)];
+per_dec_enumerated_fix_list([], Tail, _) -> Tail.
+
+per_dec_integer_1([{'SingleValue',Value}], _Aligned) ->
+ {value,Value};
+per_dec_integer_1([{'ValueRange',{Lb,'MAX'}}], Aligned) when is_integer(Lb) ->
+ per_dec_unconstrained(Aligned);
+per_dec_integer_1([{'ValueRange',{Lb,Ub}}], Aligned) when is_integer(Lb),
+ is_integer(Ub) ->
+ per_dec_constrained(Lb, Ub, Aligned);
+per_dec_integer_1([{{_,_}=Constr0,_}], Aligned) ->
+ Constr = effective_constraint(integer, [Constr0]),
+ bit_case(per_dec_integer(Constr, Aligned),
+ per_dec_unconstrained(Aligned));
+per_dec_integer_1([], Aligned) ->
+ per_dec_unconstrained(Aligned).
+
+per_dec_unconstrained(Aligned) ->
+ {get_bits,decode_unconstrained_length(false, Aligned),[8,signed]}.
+
+per_dec_constrained(Lb, Ub, false) ->
+ Range = Ub - Lb + 1,
+ Get = {get_bits,uper_num_bits(Range),[1]},
+ add_lb(Lb, Get);
+per_dec_constrained(Lb, Ub, true) ->
+ Range = Ub - Lb + 1,
+ Get = if
+ Range =< 255 ->
+ {get_bits,per_num_bits(Range),[1,unsigned]};
+ Range == 256 ->
+ {get_bits,1,[8,unsigned,{align,true}]};
+ Range =< 65536 ->
+ {get_bits,2,[8,unsigned,{align,true}]};
+ true ->
+ RangeOctLen = byte_size(binary:encode_unsigned(Range - 1)),
+ {get_bits,per_dec_length({1,RangeOctLen}, false, true),
+ [8,unsigned,{align,true}]}
+ end,
+ add_lb(Lb, Get).
+
+add_lb(0, Get) -> Get;
+add_lb(Lb, Get) -> {add,Get,Lb}.
+
+per_dec_normally_small_number(Aligned) ->
+ Small = {get_bits,6,[1]},
+ Unlimited = per_decode_semi_constrained(0, Aligned),
+ bit_case(Small, Unlimited).
+
+per_decode_semi_constrained(Lb, Aligned) ->
+ add_lb(Lb, {get_bits,decode_unconstrained_length(false, Aligned),[8]}).
+
+bit_case(Base, Ext) ->
+ {'case',[{test,{get_bits,1,[1]},0,Base},
+ {test,{get_bits,1,[1]},1,Ext}]}.
+
+decode_unconstrained_length(AllowZero, Aligned) ->
+ Al = [{align,Aligned}],
+ Zero = case AllowZero of
+ false -> [non_zero];
+ true -> []
+ end,
+ {'case',[{test,{get_bits,1,[1|Al]},0,
+ {value,{get_bits,7,[1|Zero]}}},
+ {test,{get_bits,1,[1|Al]},1,
+ {test,{get_bits,1,[1]},0,
+ {value,{get_bits,14,[1|Zero]}}}}]}.
+
+uper_num_bits(N) ->
+ uper_num_bits(N, 1, 0).
+
+uper_num_bits(N, T, B) when N =< T -> B;
+uper_num_bits(N, T, B) -> uper_num_bits(N, T bsl 1, B+1).
+
+per_num_bits(2) -> 1;
+per_num_bits(N) when N =< 4 -> 2;
+per_num_bits(N) when N =< 8 -> 3;
+per_num_bits(N) when N =< 16 -> 4;
+per_num_bits(N) when N =< 32 -> 5;
+per_num_bits(N) when N =< 64 -> 6;
+per_num_bits(N) when N =< 128 -> 7;
+per_num_bits(N) when N =< 255 -> 8.
+
+%%%
+%%% Remove unnecessary aligning to octet boundaries.
+%%%
+
+opt_al({get_bits,E0,Opts0}, A0) ->
+ {E,A1} = opt_al(E0, A0),
+ Opts = opt_al_1(A1, Opts0),
+ A = update_al(A1, E, Opts),
+ {{get_bits,E,Opts},A};
+opt_al({call,Fun,E0}, A0) ->
+ {E,A} = opt_al(E0, A0),
+ {{call,Fun,E},A};
+opt_al({convert,Op,E0}, A0) ->
+ {E,A} = opt_al(E0, A0),
+ {{convert,Op,E},A};
+opt_al({value,E0}, A0) ->
+ {E,A} = opt_al(E0, A0),
+ {{value,E},A};
+opt_al({add,E0,I}, A0) when is_integer(I) ->
+ {E,A} = opt_al(E0, A0),
+ {{add,E,I},A};
+opt_al({test,E0,V,B0}, A0) ->
+ {E,A1} = opt_al(E0, A0),
+ {B,A2} = opt_al(B0, A1),
+ {{test,E,V,B},A2};
+opt_al({'case',Cs0}, A0) ->
+ {Cs,A} = opt_al_cs(Cs0, A0),
+ {{'case',Cs},A};
+opt_al({map,E0,Cs}, A0) ->
+ {E,A} = opt_al(E0, A0),
+ {{map,E,Cs},A};
+opt_al(I, A) when is_integer(I) ->
+ {I,A}.
+
+opt_al_cs([C0|Cs0], A0) ->
+ {C,A1} = opt_al(C0, A0),
+ {Cs,A2} = opt_al_cs(Cs0, A0),
+ {[C|Cs],merge_al(A1, A2)};
+opt_al_cs([], _) -> {[],none}.
+
+merge_al(unknown, _) -> unknown;
+merge_al(Other, none) -> Other;
+merge_al(_, unknown) -> unknown;
+merge_al(I0, I1) ->
+ case {I0 rem 8,I1 rem 8} of
+ {I,I} -> I;
+ {_,_} -> unknown
+ end.
+
+opt_al_1(unknown, Opts) ->
+ Opts;
+opt_al_1(A, Opts0) ->
+ case alignment(Opts0) of
+ none ->
+ Opts0;
+ full ->
+ case A rem 8 of
+ 0 ->
+ %% Already in alignment.
+ proplists:delete(align, Opts0);
+ Bits ->
+ %% Cheaper alignment with a constant padding.
+ Opts1 = proplists:delete(align, Opts0),
+ [{align,8-Bits }|Opts1]
+ end;
+ A -> %Assertion.
+ Opts0
+ end.
+
+update_al(A0, E, Opts) ->
+ A = case alignment(Opts) of
+ none -> A0;
+ full -> 0;
+ Bits when is_integer(A0) ->
+ 0 = (A0 + Bits) rem 8; %Assertion.
+ _ ->
+ 0
+ end,
+ [U] = [U || U <- Opts, is_integer(U)],
+ if
+ U rem 8 =:= 0 -> A;
+ is_integer(A), is_integer(E) -> A + U*E;
+ true -> unknown
+ end.
+
+%%%
+%%% Flatten the intermediate format and assign temporaries.
+%%%
+
+flatten({get_bits,I,U}, Buf0, St0) when is_integer(I) ->
+ {Dst,St} = new_var_pair(St0),
+ Gb = {get_bits,{I,Buf0},U,Dst},
+ flatten_align(Gb, [], St);
+flatten({get_bits,E0,U}, Buf0, St0) ->
+ {E,Pre,St1} = flatten(E0, Buf0, St0),
+ {Dst,St2} = new_var_pair(St1),
+ Gb = {get_bits,E,U,Dst},
+ flatten_align(Gb, Pre, St2);
+flatten({test,{get_bits,I,U},V,E0}, Buf0, St0) when is_integer(I) ->
+ {DstBuf0,St1} = new_var("Buf", St0),
+ Gb = {get_bits,{I,Buf0},U,{V,DstBuf0}},
+ {{_Dst,DstBuf},Pre0,St2} = flatten_align(Gb, [], St1),
+ {E,Pre1,St3} = flatten(E0, DstBuf, St2),
+ {E,Pre0++Pre1,St3};
+flatten({add,E0,I}, Buf0, St0) ->
+ {{Src,Buf},Pre,St1} = flatten(E0, Buf0, St0),
+ {Dst,St} = new_var("Add", St1),
+ {{Dst,Buf},Pre++[{add,Src,I,Dst}],St};
+flatten({'case',Cs0}, Buf0, St0) ->
+ {Dst,St1} = new_var_pair(St0),
+ {Cs1,St} = flatten_cs(Cs0, Buf0, St1),
+ {Al,Cs2} = flatten_hoist_align(Cs1),
+ {Dst,Al++[{'case',Buf0,Cs2,Dst}],St};
+flatten({map,E0,Cs0}, Buf0, St0) ->
+ {{E,DstBuf},Pre,St1} = flatten(E0, Buf0, St0),
+ {Dst,St2} = new_var("Int", St1),
+ Cs = flatten_map_cs(Cs0, E),
+ {{Dst,DstBuf},Pre++[{'map',E,Cs,{Dst,DstBuf}}],St2};
+flatten({value,V0}, Buf0, St0) when is_integer(V0) ->
+ {{V0,Buf0},[],St0};
+flatten({value,V0}, Buf0, St0) ->
+ flatten(V0, Buf0, St0);
+flatten({convert,Op,E0}, Buf0, St0) ->
+ {{E,Buf},Pre,St1} = flatten(E0, Buf0, St0),
+ {Dst,St2} = new_var("Conv", St1),
+ {{Dst,Buf},Pre++[{convert,Op,E,Dst}],St2};
+flatten({call,Fun,E0}, Buf0, St0) ->
+ {Src,Pre,St1} = flatten(E0, Buf0, St0),
+ {Dst,St2} = new_var_pair(St1),
+ {Dst,Pre++[{call,Fun,Src,Dst}],St2}.
+
+flatten_cs([C0|Cs0], Buf, St0) ->
+ {C,Pre,St1} = flatten(C0, Buf, St0),
+ {Cs,St2} = flatten_cs(Cs0, Buf, St0),
+ St3 = St2#st{var=max(St1#st.var, St2#st.var)},
+ {[Pre++[{return,C}]|Cs],St3};
+flatten_cs([], _, St) -> {[],St}.
+
+flatten_map_cs(Cs, Var) ->
+ flatten_map_cs_1(Cs, {Var,Cs}).
+
+flatten_map_cs_1([{K,V}|Cs], DefData) ->
+ [{{asis,K},{asis,V}}|flatten_map_cs_1(Cs, DefData)];
+flatten_map_cs_1([integer_default], {Int,_}) ->
+ [{'_',Int}];
+flatten_map_cs_1([enum_default], {Int,_}) ->
+ [{'_',["{asn1_enum,",Int,"}"]}];
+flatten_map_cs_1([enum_error], {Var,Cs}) ->
+ Vs = [V || {_,V} <- Cs],
+ [{'_',["exit({error,{asn1,{decode_enumerated,{",Var,",",
+ {asis,Vs},"}}}})"]}];
+flatten_map_cs_1([], _) -> [].
+
+flatten_hoist_align([[{align_bits,_,_}=Ab|T]|Cs]) ->
+ flatten_hoist_align_1(Cs, Ab, [T]);
+flatten_hoist_align(Cs) -> {[],Cs}.
+
+flatten_hoist_align_1([[Ab|T]|Cs], Ab, Acc) ->
+ flatten_hoist_align_1(Cs, Ab, [T|Acc]);
+flatten_hoist_align_1([], Ab, Acc) ->
+ {[Ab],lists:reverse(Acc)}.
+
+flatten_align({get_bits,{SrcBits,SrcBuf},U,Dst}=Gb0, Pre, St0) ->
+ case alignment(U) of
+ none ->
+ flatten_align_1(U, Dst, Pre++[Gb0], St0);
+ full ->
+ {PadBits,St1} = new_var("Pad", St0),
+ {DstBuf,St2} = new_var("Buf", St1),
+ Ab = {align_bits,SrcBuf,PadBits},
+ Agb = {get_bits,{PadBits,SrcBuf},[1],{'_',DstBuf}},
+ Gb = {get_bits,{SrcBits,DstBuf},U,Dst},
+ flatten_align_1(U, Dst, Pre++[Ab,Agb,Gb], St2);
+ PadBits when is_integer(PadBits), PadBits > 0 ->
+ {DstBuf,St1} = new_var("Buf", St0),
+ Agb = {get_bits,{PadBits,SrcBuf},[1],{'_',DstBuf}},
+ Gb = {get_bits,{SrcBits,DstBuf},U,Dst},
+ flatten_align_1(U, Dst, Pre++[Agb,Gb], St1)
+ end.
+
+flatten_align_1(U, {D,_}=Dst, Pre, St) ->
+ case is_non_zero(U) of
+ false ->
+ {Dst,Pre,St};
+ true ->
+ {Dst,Pre++[{non_zero,D}],St}
+ end.
+
+new_var_pair(St0) ->
+ {Var,St1} = new_var("V", St0),
+ {Buf,St2} = new_var("Buf", St1),
+ {{Var,Buf},St2}.
+
+new_var(Tag, #st{base=VarBase,var=N}=St) ->
+ {VarBase++Tag++integer_to_list(N),St#st{var=N+1}}.
+
+alignment([{align,false}|_]) -> none;
+alignment([{align,true}|_]) -> full;
+alignment([{align,Bits}|_]) -> Bits;
+alignment([_|T]) -> alignment(T);
+alignment([]) -> none.
+
+is_non_zero(Fl) ->
+ lists:member(non_zero, Fl).
+
+%%%
+%%% Generate Erlang code from the flattened intermediate format.
+%%%
+
+dcg_list_outside([{align_bits,Buf,SzVar}|T]) ->
+ emit([SzVar," = bit_size(",Buf,") band 7"]),
+ iter_dcg_list_outside(T);
+dcg_list_outside([{'case',Buf,Cs,Dst}|T]) ->
+ dcg_case(Buf, Cs, Dst),
+ iter_dcg_list_outside(T);
+dcg_list_outside([{'map',Val,Cs,Dst}|T]) ->
+ dcg_map(Val, Cs, Dst),
+ iter_dcg_list_outside(T);
+dcg_list_outside([{add,S1,S2,Dst}|T]) ->
+ emit([Dst," = ",S1," + ",S2]),
+ iter_dcg_list_outside(T);
+dcg_list_outside([{return,{V,Buf}}|T]) ->
+ emit(["{",V,",",Buf,"}"]),
+ iter_dcg_list_outside(T);
+dcg_list_outside([{call,Fun,{V,Buf},{Dst,DstBuf}}|T]) ->
+ emit(["{",Dst,",",DstBuf,"} = "]),
+ Fun(V, Buf),
+ iter_dcg_list_outside(T);
+dcg_list_outside([{convert,Op,V,Dst}|T]) ->
+ emit([Dst," = ",Op,"(",V,")"]),
+ iter_dcg_list_outside(T);
+dcg_list_outside([{get_bits,{_,Buf0},_,_}|_]=L0) ->
+ emit("<<"),
+ {L,Buf} = dcg_list_inside(L0, buf),
+ emit([Buf,"/bitstring>> = ",Buf0]),
+ iter_dcg_list_outside(L);
+dcg_list_outside([]) ->
+ emit("ignore"),
+ ok.
+
+iter_dcg_list_outside([_|_]=T) ->
+ emit([",",nl]),
+ dcg_list_outside(T);
+iter_dcg_list_outside([]) -> ok.
+
+dcg_case(Buf, Cs, {Dst,DstBuf}) ->
+ emit(["{",Dst,",",DstBuf,"} = case ",Buf," of",nl]),
+ dcg_case_cs(Cs),
+ emit("end").
+
+dcg_case_cs([C|Cs]) ->
+ emit("<<"),
+ {T0,DstBuf} = dcg_list_inside(C, buf),
+ emit([DstBuf,"/bitstring>>"]),
+ T1 = dcg_guard(T0),
+ dcg_list_outside(T1),
+ case Cs of
+ [] -> emit([nl]);
+ [_|_] -> emit([";",nl])
+ end,
+ dcg_case_cs(Cs);
+dcg_case_cs([]) -> ok.
+
+dcg_guard([{non_zero,Src}|T]) ->
+ emit([" when ",Src," =/= 0 ->",nl]),
+ T;
+dcg_guard(T) ->
+ emit([" ->",nl]),
+ T.
+
+dcg_map(Val, Cs, {Dst,_}) ->
+ emit([Dst," = case ",Val," of",nl]),
+ dcg_map_cs(Cs),
+ emit("end").
+
+dcg_map_cs([{K,V}]) ->
+ emit([K," -> ",V,nl]);
+dcg_map_cs([{K,V}|Cs]) ->
+ emit([K," -> ",V,";",nl]),
+ dcg_map_cs(Cs).
+
+dcg_list_inside([{get_bits,{Sz,_},Fl0,{Dst,DstBuf}}|T], _) ->
+ Fl = bit_flags(Fl0, []),
+ emit([mk_dest(Dst),":",Sz,Fl,","]),
+ dcg_list_inside(T, DstBuf);
+dcg_list_inside(L, Dst) -> {L,Dst}.
+
+bit_flags([1|T], Acc) ->
+ bit_flags(T, Acc);
+bit_flags([{align,_}|T], Acc) ->
+ bit_flags(T, Acc);
+bit_flags([non_zero|T], Acc) ->
+ bit_flags(T, Acc);
+bit_flags([U|T], Acc) when is_integer(U) ->
+ bit_flags(T, ["unit:"++integer_to_list(U)|Acc]);
+bit_flags([H|T], Acc) ->
+ bit_flags(T, [atom_to_list(H)|Acc]);
+bit_flags([], []) ->
+ "";
+bit_flags([], Acc) ->
+ "/" ++ bit_flags_1(Acc, "").
+
+bit_flags_1([H|T], Sep) ->
+ Sep ++ H ++ bit_flags_1(T, "-");
+bit_flags_1([], _) -> [].
+
+mk_dest(I) when is_integer(I) ->
+ integer_to_list(I);
+mk_dest(S) -> S.
+
+%% 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];
+effective_constraint(integer, C) ->
+ SVs = get_constraints(C, 'SingleValue'),
+ SV = effective_constr('SingleValue', SVs),
+ VRs = get_constraints(C, 'ValueRange'),
+ VR = effective_constr('ValueRange', VRs),
+ greatest_common_range(SV, VR);
+effective_constraint(bitstring, C) ->
+ get_constraint(C, 'SizeConstraint').
+
+effective_constr(_, []) -> [];
+effective_constr('SingleValue', List) ->
+ SVList = lists:flatten(lists:map(fun(X) -> element(2, X) end, List)),
+ %% Sort and remove duplicates before generating SingleValue or ValueRange
+ %% In case of ValueRange, also check for 'MIN and 'MAX'
+ case lists:usort(SVList) of
+ [N] ->
+ [{'SingleValue',N}];
+ [_|_]=L ->
+ [{'ValueRange',{least_Lb(L),greatest_Ub(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 is_integer(Int), Int > Ub ->
+ [{'ValueRange',{'MIN',Int}}];
+greatest_common_range([{_,Int}],[{_,{Lb,Ub}}])
+ when is_integer(Int), Int < Lb ->
+ [{'ValueRange',{Int,Ub}}];
+greatest_common_range([{_,Int}],VR=[{_,{_Lb,_Ub}}]) when is_integer(Int) ->
+ VR;
+greatest_common_range([{_,L}],[{_,{Lb,Ub}}]) when is_list(L) ->
+ Min = least_Lb([Lb|L]),
+ Max = greatest_Ub([Ub|L]),
+ [{'ValueRange',{Min,Max}}];
+greatest_common_range([{_,{Lb1,Ub1}}], [{_,{Lb2,Ub2}}]) ->
+ Min = least_Lb([Lb1,Lb2]),
+ Max = greatest_Ub([Ub1,Ub2]),
+ [{'ValueRange',{Min,Max}}].
+
+
+least_Lb(L) ->
+ case lists:member('MIN', L) of
+ true -> 'MIN';
+ false -> lists:min(L)
+ end.
+
+greatest_Ub(L) ->
+ case lists:member('MAX', L) of
+ true -> 'MAX';
+ false -> lists:max(L)
+ end.
+
+get_constraint(C, Key) ->
+ case lists:keyfind(Key, 1, C) of
+ false -> no;
+ {_,V} -> V
+ end.
+
+get_constraints([{Key,_}=Pair|T], Key) ->
+ [Pair|get_constraints(T, Key)];
+get_constraints([_|T], Key) ->
+ get_constraints(T, Key);
+get_constraints([], _) -> [].
diff --git a/lib/asn1/src/asn1ct_parser2.erl b/lib/asn1/src/asn1ct_parser2.erl
index 7301f49085..9e1fcce2b1 100644
--- a/lib/asn1/src/asn1ct_parser2.erl
+++ b/lib/asn1/src/asn1ct_parser2.erl
@@ -924,19 +924,8 @@ parse_UnionsRec([{'|',_}|Rest]) ->
{V1,V2} ->
{[V1,union,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 is_list(V2) ->
- {[V1] ++ [union|V2],Rest3};
- {V1,V2} ->
- {[V1,union,V2],Rest3}
- end;
+parse_UnionsRec([{'UNION',Info}|Rest]) ->
+ parse_UnionsRec([{'|',Info}|Rest]);
parse_UnionsRec(Tokens) ->
{[],Tokens}.
@@ -971,20 +960,8 @@ parse_IElemsRec([{'^',_}|Rest]) ->
{V1,V2} ->
{[V1,intersection,V2],Rest3}
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 is_list(V2) ->
- {[V1] ++ [intersection|V2],Rest3};
- {V1,V2} ->
- {[V1,intersection,V2],Rest3}
- end;
+parse_IElemsRec([{'INTERSECTION',Info}|Rest]) ->
+ parse_IElemsRec([{'^',Info}|Rest]);
parse_IElemsRec(Tokens) ->
{[],Tokens}.
diff --git a/lib/asn1/src/asn1ct_value.erl b/lib/asn1/src/asn1ct_value.erl
index 9013baef92..389642c446 100644
--- a/lib/asn1/src/asn1ct_value.erl
+++ b/lib/asn1/src/asn1ct_value.erl
@@ -54,7 +54,7 @@ from_type(M,Typename,Type) when is_record(Type,type) ->
{notype,_} ->
true;
{primitive,bif} ->
- from_type_prim(Type,get_encoding_rule(M));
+ from_type_prim(Type);
'ASN1_OPEN_TYPE' ->
case Type#type.constraint of
[#'Externaltypereference'{type=TrefConstraint}] ->
@@ -164,7 +164,7 @@ gen_list(_,_,_,0) ->
gen_list(M,Typename,Oftype,N) ->
[from_type(M,Typename,Oftype)|gen_list(M,Typename,Oftype,N-1)].
-from_type_prim(D,Erule) ->
+from_type_prim(D) ->
C = D#type.constraint,
case D#type.def of
'INTEGER' ->
@@ -303,12 +303,7 @@ from_type_prim(D,Erule) ->
adjust_list(size_random(C),c_string(C,"BMPString"));
'UTF8String' ->
{ok,Res}=asn1rt:utf8_list_to_binary(adjust_list(random(50),[$U,$T,$F,$8,$S,$t,$r,$i,$n,$g,16#ffff,16#fffffff,16#ffffff,16#fffff,16#fff])),
- case Erule of
- per ->
- binary_to_list(Res);
- _ ->
- Res
- end;
+ Res;
'UniversalString' ->
adjust_list(size_random(C),c_string(C,"UniversalString"));
XX ->
@@ -440,20 +435,9 @@ get_encoding_rule(M) ->
end.
open_type_value(ber) ->
- [4,9,111,112,101,110,95,116,121,112,101];
-open_type_value(ber_bin) ->
-% [4,9,111,112,101,110,95,116,121,112,101];
- <<4,9,111,112,101,110,95,116,121,112,101>>;
-open_type_value(ber_bin_v2) ->
-% [4,9,111,112,101,110,95,116,121,112,101];
<<4,9,111,112,101,110,95,116,121,112,101>>;
-open_type_value(per) ->
- "\n\topen_type"; %octet string value "open_type"
-open_type_value(per_bin) ->
- <<"\n\topen_type">>;
-% <<10,9,111,112,101,110,95,116,121,112,101>>;
open_type_value(_) ->
- [4,9,111,112,101,110,95,116,121,112,101].
+ <<"\n\topen_type">>. %octet string value "open_type"
to_textual_order({Root,Ext}) ->
{to_textual_order(Root),Ext};
diff --git a/lib/asn1/src/asn1rt_ber_bin.erl b/lib/asn1/src/asn1rt_ber_bin.erl
index 22f9f2ecfd..ec1549804b 100644
--- a/lib/asn1/src/asn1rt_ber_bin.erl
+++ b/lib/asn1/src/asn1rt_ber_bin.erl
@@ -19,337 +19,30 @@
%%
-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,
+-export([decode_length/1,
encode_real/2, encode_real/3,
decode_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_relative_oid/2,decode_relative_oid/3,
- encode_restricted_string/4,decode_restricted_string/6,
- encode_universal_string/3,decode_universal_string/5,
- encode_UTF8_string/3, decode_UTF8_string/3,
- 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,skip_ExtensionAdditions/2]).
+ decode_tag/1]).
-include("asn1_records.hrl").
-% the encoding of class of tag bits 8 and 7
+%% 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_UTF8String, 12).
--define('N_RELATIVE-OID',13).
--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 is_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}.
- {RemBytes,byte_size(Bytes)}.
-
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% 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(L, Bytes, Rb) ->
- skipvalue(L, Bytes, Rb, 0).
-
-skipvalue(indefinite, Bytes, Rb, IndefLevel) ->
- {T,Bytes2,R2} = decode_tag(Bytes),
- {{L,Bytes3},R3} = decode_length(Bytes2),
- case {T,L} of
- {_,indefinite} ->
- skipvalue(indefinite,Bytes3,Rb+R2+R3,IndefLevel+1);
- {{0,0,0},0} when IndefLevel =:= 0 ->
- %% See X690 8.1.5 NOTE, end of indefinite content
- {Bytes3,Rb+2};
- {{0,0,0},0} ->
- skipvalue(indefinite,Bytes3,Rb+2,IndefLevel - 1);
- _ ->
- <<_:L/binary, RestBytes/binary>> = Bytes3,
- skipvalue(indefinite,RestBytes,Rb+R2+R3+L, IndefLevel)
- %%{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(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.
-
-%%%
-%% skips byte sequence of Bytes that do not match a tag in Tags
-skip_ExtensionAdditions(Bytes,Tags) ->
- skip_ExtensionAdditions(Bytes,Tags,0).
-skip_ExtensionAdditions(<<>>,_Tags,RmB) ->
- {<<>>,RmB};
-skip_ExtensionAdditions(Bytes,Tags,RmB) ->
- case catch decode_tag(Bytes) of
- {'EXIT',_Reason} ->
- tag_error(no_data,Tags,Bytes,'OPTIONAL');
- {_T={Class,_Form,TagNo},_Bytes2,_R2} ->
- case [X||X=#tag{class=Cl,number=TN} <- Tags,Cl==Class,TN==TagNo] of
- [] ->
- %% skip this TLV and continue with next
- {Bytes3,R3} = skipvalue(Bytes),
- skip_ExtensionAdditions(Bytes3,Tags,RmB+R3);
- _ ->
- {Bytes,RmB}
- end
- end.
-
-%%===============================================================================
-%%===============================================================================
-%%===============================================================================
-%% Optionals, preset not filled optionals with asn1_NOVALUE
-%%===============================================================================
-%%===============================================================================
-%%===============================================================================
-
-% converts a list to a record if necessary
-list_to_record(Name,List) when is_list(List) ->
- list_to_tuple([Name|List]);
-list_to_record(_Name,Tuple) when is_tuple(Tuple) ->
- Tuple.
-
-
-fixoptionals(OptList,Val) when is_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)}}}).
-
+ <<(Class bsr 6):2, (Form bsr 5):1, 31:5,BinOct/binary>>.
%%===============================================================================
%% Decode a tag
@@ -403,33 +96,11 @@ check_tags_i([Tag1|TagRest], Buffer, Rb, OptOrMand) ->
_ ->
check_tags_i(TagRest, Buffer2, Rb + Rb1, mandatory)
end
- end;
-
-check_tags_i([], Buffer, Rb, _) ->
- {[],{{0,0},Buffer,Rb}}.
+ end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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} ->
@@ -491,382 +162,6 @@ encode_one_tag(#tag{class=Class,number=No,type=Type, form = Form}) ->
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 is_list(Val) ->
- {Val, byte_size(list_to_binary(Val))};
-encode_open_type(Val) ->
- {Val, byte_size(Val)}.
-
-%%
-encode_open_type(Val, []) when is_list(Val) ->
- {Val, byte_size(list_to_binary(Val))};
-encode_open_type(Val, []) ->
- {Val, byte_size(Val)};
-encode_open_type(Val, Tag) when is_list(Val) ->
- encode_tags(Tag, Val, byte_size(list_to_binary(Val)));
-encode_open_type(Val, Tag) ->
- encode_tags(Tag, Val, byte_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,
- {_Tag, Len, RemainingBuffer, RemovedBytes} = decode_tag_and_length(Bytes),
- {_RemainingBuffer2, RemovedBytes2} = skipvalue(Len, RemainingBuffer, RemovedBytes),
- N = RemovedBytes2,
- <<Val:N/binary, RemainingBytes/binary>> = Bytes,
-% {Val, RemainingBytes, Len + RemovedBytes}.
- {Val,RemainingBytes,N}.
-
-decode_open_type(<<>>,[]=ExplTag) -> % R9C-0.patch-40
- exit({error, {asn1,{no_optional_tag, ExplTag}}});
-decode_open_type(Bytes,ExplTag) ->
- {Tag, Len, RemainingBuffer, RemovedBytes} = decode_tag_and_length(Bytes),
- case {Tag,ExplTag} of
-% {{Class,Form,32},[#tag{class=Class,number=No,form=32}]} ->
-% {_Tag2, Len2, RemainingBuffer2, RemovedBytes2} = decode_tag_and_length(RemainingBuffer),
-% {_RemainingBuffer3, RemovedBytes3} = skipvalue(Len2, RemainingBuffer2, RemovedBytes2),
-% N = RemovedBytes3,
-% <<_:RemovedBytes/unit:8,Val:N/binary,RemainingBytes/binary>> = Bytes,
-% {Val, RemainingBytes, N + RemovedBytes};
- {{Class,Form,No},[#tag{class=Class,number=No,form=Form}]} ->
- {_RemainingBuffer2, RemovedBytes2} =
- skipvalue(Len, RemainingBuffer),
- N = RemovedBytes2,
- <<_:RemovedBytes/unit:8,Val:N/binary,RemainingBytes/binary>> = Bytes,
- {Val, RemainingBytes, N + RemovedBytes};
- _ ->
- {_RemainingBuffer2, RemovedBytes2} =
- skipvalue(Len, RemainingBuffer, RemovedBytes),
- N = RemovedBytes2,
- <<Val:N/binary, RemainingBytes/binary>> = Bytes,
- {Val, RemainingBytes, N}
- 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 is_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 is_integer(Val) ->
- {EncVal,Len} = encode_integer(C, Val),
- dotag_universal(?N_INTEGER,EncVal,Len);
-encode_integer(C, Val, Tag) when is_integer(Val) ->
- dotag(Tag, ?N_INTEGER, encode_integer(C, Val));
-encode_integer(C,{Name,Val},Tag) when is_atom(Name) ->
- encode_integer(C,Val,Tag);
-encode_integer(_, Val, _) ->
- exit({error,{asn1, {encode_integer, Val}}}).
-
-
-encode_integer(C, Val, NamedNumberList, Tag) when is_atom(Val) ->
- case lists:keyfind(Val, 1, NamedNumberList) of
- {_, 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:keyfind(Val, 2, NamedNumberList) of
- {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 is_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 is_integer(Val) ->
- {EncVal,Len} = encode_integer(false,Val),
- dotag_universal(?N_ENUMERATED,EncVal,Len);
-encode_enumerated(Val, DoTag) when is_integer(Val) ->
- dotag(DoTag, ?N_ENUMERATED, encode_integer(false,Val));
-encode_enumerated({Name,Val}, DoTag) when is_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 is_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 is_atom(Val) ->
- case lists:keyfind(Val, 1, NamedNumberList) of
- {_, NewVal} when DoTag =:= [] ->
- {EncVal,Len} = encode_integer(C,NewVal),
- dotag_universal(?N_ENUMERATED,EncVal,Len);
- {_, 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 is_integer(Val) ->
- dotag(DoTag, ?N_ENUMERATED, encode_integer(C,Val));
-
-encode_enumerated(C, {Name,Val}, NamedNumberList, DoTag) when is_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:keyfind(Val, 2, NamedNumberList) of
- {NamedVal, _} ->
- NamedVal;
- _ ->
- {asn1_enum,Val}
- end.
-
-
%%============================================================================
%%
%% Real value, ITU_T X.690 Chapter 8.5
@@ -1117,513 +412,15 @@ decode_real2(Buffer0, _C, Len, RemBytes1) ->
{{Mantissa, Base, Exp}, Buffer4, RemBytes2+RemBytes3}
end.
+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]).
-%%============================================================================
-%% 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 is_integer(Unused), is_binary(BinBits) ->
- encode_bin_bit_string(C,Bin,NamedBitList,DoTag);
-encode_bit_string(C, [FirstVal | RestVal], NamedBitList, DoTag) when is_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 is_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 is_integer(IntegerVal) ->
- BitListVal = int_to_bitlist(IntegerVal),
- encode_bit_string_bits(C, BitListVal, NamedBitList, DoTag);
-
-encode_bit_string(C, {Name,BitList}, NamedBitList, DoTag) when is_atom(Name) ->
- encode_bit_string(C, BitList, NamedBitList, DoTag).
-
-
-
-int_to_bitlist(0) ->
- [];
-int_to_bitlist(Int) when is_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 (byte_size(BinBits) =:= 0), DoTag =:= [] ->
- %% time optimization of next case
- {[StringType,1,0],3};
- 0 when (byte_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 = byte_size(BinBits) - 1,
- <<BBits:N/binary,LastByte>> = BinBits,
- dotag_universal(StringType,
- [Unused,BBits,(LastByte bsr Num) bsl Num],
- byte_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 = byte_size(BinBits) - 1,
- <<BBits:N/binary,LastByte>> = BinBits,
- dotag(DoTag,StringType,{[Unused,binary_to_list(BBits) ++
- [(LastByte bsr Num) bsl Num]],
- byte_size(BinBits) + 1})
- 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 is_atom(Val) ->
- case lists:keyfind(Val, 1, NamedBitList) of
- {_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 is_list(BitListVal) ->
- {Len,Unused,OctetList} =
- case get_constraint(C,'SizeConstraint') of
- no ->
- encode_bitstring(BitListVal);
- Constr={Min,_Max} when is_integer(Min) ->
- 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({{_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;
-encode_constr_bit_str_bits({Min,Max},BitListVal,_DoTag) ->
- BitLen = length(BitListVal),
- if
- BitLen > Max ->
- exit({error,{asn1,{bitstring_length,{{was,BitLen},
- {maximum,Max}}}}});
- BitLen < Min ->
- exit({error,{asn1,{bitstring_length,{{was,BitLen},
- {minimum,Min}}}}});
- true ->
- 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),
- 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 is_binary(OctetList) ->
- dotag_universal(?N_OCTET_STRING,OctetList,byte_size(OctetList));
-encode_octet_string(_C, OctetList, DoTag) when is_binary(OctetList) ->
- dotag(DoTag, ?N_OCTET_STRING, {OctetList,byte_size(OctetList)});
-encode_octet_string(_C, OctetList, DoTag) when is_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 is_list(OctetList) ->
-%% dotag(DoTag, ?N_OCTET_STRING, {OctetList,length(OctetList)});
-encode_octet_string(C, {Name,OctetList}, DoTag) when is_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 is_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 is_atom(Cname), is_tuple(V) ->
- e_object_identifier(tuple_to_list(V));
-e_object_identifier({Cname, V}) when is_atom(Cname), is_list(V) ->
- e_object_identifier(V);
-e_object_identifier(V) when is_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).
+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]).
%%%%%%%%%%%
@@ -1643,476 +440,6 @@ 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).
-
-%%============================================================================
-%% RELATIVE-OID, ITU_T X.690 Chapter 8.20
-%%
-%% encode Relative Object Identifier
-%%============================================================================
-encode_relative_oid({Name,Val},TagIn) when is_atom(Name) ->
- encode_relative_oid(Val,TagIn);
-encode_relative_oid(Val,TagIn) when is_tuple(Val) ->
- encode_relative_oid(tuple_to_list(Val),TagIn);
-encode_relative_oid(Val,[]) ->
- {EncVal,Len} = enc_relative_oid(Val),
- dotag_universal(?'N_RELATIVE-OID',EncVal,Len);
-encode_relative_oid(Val, DoTag) ->
- dotag(DoTag, ?'N_RELATIVE-OID', enc_relative_oid(Val)).
-
-enc_relative_oid(Val) ->
- lists:mapfoldl(fun(X,AccIn) ->
- {SO,L}=mk_object_val(X),
- {SO,L+AccIn}
- end
- ,0,Val).
-
-%%============================================================================
-%% decode Relative Object Identifier value
-%% (Buffer, HasTag, TotalLen) -> {{ObjId}, Remain, RemovedBytes}
-%%============================================================================
-decode_relative_oid(Buffer, Tags, OptOrMand) ->
- NewTags = new_tags(Tags,#tag{class=?UNIVERSAL,
- number=?'N_RELATIVE-OID'}),
- decode_relative_oid_notag(Buffer, NewTags, OptOrMand).
-
-decode_relative_oid_notag(Buffer, Tags, OptOrMand) ->
- {_RestTags, {_FormLen={_,Len}, Buffer0, Rb0}} =
- check_tags_i(Tags, Buffer, OptOrMand),
- {ObjVals,Buffer01} =
- dec_subidentifiers(Buffer0,0,[],Len),
- {list_to_tuple(ObjVals), Buffer01, Rb0+Len}.
-
-%%============================================================================
-%% Restricted character string types, ITU_T X.690 Chapter 8.21
-%%
-%% encode Numeric Printable Teletex Videotex Visible IA5 Graphic General strings
-%%============================================================================
-encode_restricted_string(_C, OctetList, StringType, [])
- when is_binary(OctetList) ->
- dotag_universal(StringType, OctetList, byte_size(OctetList));
-encode_restricted_string(_C, OctetList, StringType, DoTag)
- when is_binary(OctetList) ->
- dotag(DoTag, StringType, {OctetList, byte_size(OctetList)});
-encode_restricted_string(_C, OctetList, StringType, [])
- when is_list(OctetList) ->
- dotag_universal(StringType, OctetList, length(OctetList));
-encode_restricted_string(_C, OctetList, StringType, DoTag)
- when is_list(OctetList) ->
- dotag(DoTag, StringType, {OctetList, length(OctetList)});
-encode_restricted_string(C,{Name,OctetL},StringType,DoTag) when is_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 is_list(Val) ->
- {length(Val),Val};
- ?N_BIT_STRING when is_tuple(Val) ->
- {(size(element(2,Val))*8) - element(1,Val),Val};
- _ when is_binary(Val) ->
- {byte_size(Val),binary_to_list(Val)};
- _ when is_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;
- {{Lb,_Ub},_Ext=[MinExt|_]} when StrLen >= Lb; StrLen >= MinExt ->
- 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 is_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 is_binary(Val),is_binary(AccVal) ->
- {<<AccVal/binary,Val/binary>>,AccRb+Rb};
- _ when is_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 is_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 UTF8 string
-%%============================================================================
-encode_UTF8_string(_,UTF8String,[]) when is_binary(UTF8String) ->
- dotag_universal(?N_UTF8String,UTF8String,byte_size(UTF8String));
-encode_UTF8_string(_,UTF8String,DoTag) when is_binary(UTF8String) ->
- dotag(DoTag,?N_UTF8String,{UTF8String,byte_size(UTF8String)});
-encode_UTF8_string(_,UTF8String,[]) ->
- dotag_universal(?N_UTF8String,UTF8String,length(UTF8String));
-encode_UTF8_string(_,UTF8String,DoTag) ->
- dotag(DoTag,?N_UTF8String,{UTF8String,length(UTF8String)}).
-
-%%============================================================================
-%% decode UTF8 string
-%%============================================================================
-
-decode_UTF8_string(Buffer, Tags, OptOrMand) ->
- NewTags = new_tags(Tags, #tag{class=?UNIVERSAL,number=?N_UTF8String}),
- decode_UTF8_string_notag(Buffer, NewTags, OptOrMand).
-
-decode_UTF8_string_notag(Buffer, Tags, OptOrMand) ->
- {RestTags, {FormLen, Buffer0, Rb0}} =
- check_tags_i(Tags, Buffer, OptOrMand),
- case FormLen of
- {?CONSTRUCTED,Len} ->
- %% an UTF8String may be encoded as a constructed type
- {Buffer00,RestBytes} = split_list(Buffer0,Len),
- {Val01, Buffer01, Rb01} =
- decode_UTF8_string_notag(Buffer00,RestTags,OptOrMand),
- {Buffer02, Rb02} = restbytes2(RestBytes,Buffer01,noext),
- {Val01, Buffer02, Rb0+Rb01+Rb02};
- {_,Len} ->
- <<Result:Len/binary,RestBuff/binary>> = Buffer0,
- {Result,RestBuff,Rb0 + Len}
- end.
-
-
-%%============================================================================
-%% encode BMP string
-%%============================================================================
-
-encode_BMP_string(C, {Name,BMPString}, DoTag) when is_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 is_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 is_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
%%
@@ -2122,8 +449,6 @@ decode_utc_time_notag(Buffer, Range, Tags, TotalLen, OptOrMand) ->
%% [<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) ->
@@ -2162,242 +487,12 @@ 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_error([], OptOrMand);
-check_if_valid_tag(Bytes, ListOfTags, OptOrMand) when is_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, 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 ->
- TagName;
- false ->
- check_if_valid_tag2(Class_TagNo, T, Tag, OptOrMand)
- end.
-
--spec check_if_valid_tag2_error(term(), atom()) -> no_return().
-
-check_if_valid_tag2_error(Tag,mandatory) ->
- exit({error,{asn1,{invalid_tag,Tag}}});
-check_if_valid_tag2_error(Tag,_) ->
- exit({error,{asn1,{no_optional_tag,Tag}}}).
-
-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) -> 'ObjectDescriptor';
-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) ->
- case Fun3(Bytes, OptOrMand) of
- {_Term, _Remain, 0} ->
- {lists:reverse(Acc),Bytes,Rb};
- {Term, Remain, Rb1} ->
- Fun3(Bytes, OptOrMand),
- decode_set(Rb+Rb1, indefinite, Remain, OptOrMand, Fun3, [Term|Acc])
- end;
-%% {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) ->
- case Fun3(Bytes, OptOrMand) of
- {_Term, _Remain, 0} ->
- {lists:reverse(Acc),Bytes,Rb};
- {Term, Remain, Rb1} ->
- Fun3(Bytes, OptOrMand),
- decode_set(Rb+Rb1, Num-Rb1, Remain, OptOrMand, Fun3, [Term|Acc])
- end.
-%% {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)).
+ Bytes, Len).
dotag_universal(UniversalTag,Bytes,Len) when Len =< 16#7F->
{[UniversalTag,Len,Bytes],2+Len};
@@ -2415,47 +510,6 @@ decode_integer2(Len,<<1:1,B2:7,Bs/binary>>,RemovedBytes) ->
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 is_list(L1), is_list(L2) ->
- %% this case occur when decoding with NNL
- L1 ++ L2.
-
-
-get_constraint(C,Key) ->
- case lists:keyfind(Key,1,C) of
- false ->
- no;
- {_, 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) ->
diff --git a/lib/asn1/src/asn1rt_ber_bin_v2.erl b/lib/asn1/src/asn1rt_ber_bin_v2.erl
index 17e66f77c9..92ca11cf89 100644
--- a/lib/asn1/src/asn1rt_ber_bin_v2.erl
+++ b/lib/asn1/src/asn1rt_ber_bin_v2.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -22,8 +22,7 @@
%% encoding / decoding of BER
-export([decode/1, decode/2, match_tags/2, encode/1, encode/2]).
--export([fixoptionals/2, cindex/3,
- list_to_record/2,
+-export([fixoptionals/2,
encode_tag_val/1,
encode_tags/3,
skip_ExtensionAdditions/2]).
@@ -54,8 +53,6 @@
decode_open_type_as_binary/3]).
-export([decode_primitive_incomplete/2,decode_selective/2]).
-
--export([is_nif_loadable/0]).
% the encoding of class of tag bits 8 and 7
-define(UNIVERSAL, 0).
@@ -135,12 +132,7 @@ encode(Tlv,_) when is_binary(Tlv) ->
encode([Tlv],Method) ->
encode(Tlv,Method);
encode(Tlv, nif) ->
- case is_nif_loadable() of
- true ->
- asn1rt_nif:encode_ber_tlv(Tlv);
- false ->
- encode_erl(Tlv)
- end;
+ asn1rt_nif:encode_ber_tlv(Tlv);
encode(Tlv, _) ->
encode_erl(Tlv).
@@ -178,36 +170,15 @@ decode(B) ->
%% asn1-1.7
decode(B, nif) ->
- case is_nif_loadable() of
- true ->
- case asn1rt_nif:decode_ber_tlv(B) of
- {error, Reason} -> handle_error(Reason, B);
- Else -> Else
- end;
- false ->
- decode(B)
+ case asn1rt_nif:decode_ber_tlv(B) of
+ {error, Reason} -> handle_error(Reason, B);
+ Else -> Else
end;
decode(B,erlang) when is_binary(B) ->
decode_primitive(B);
decode(Tlv,erlang) ->
{Tlv,<<>>}.
-%% Have to check this since asn1 is not guaranteed to be available
-is_nif_loadable() ->
- case application:get_env(asn1, nif_loadable) of
- {ok,R} ->
- R;
- undefined ->
- case catch code:load_file(asn1rt_nif) of
- {module, asn1rt_nif} ->
- application:set_env(asn1, nif_loadable, true),
- true;
- _Else ->
- application:set_env(asn1, nif_loadable, false),
- false
- end
- end.
-
handle_error([],_)->
exit({error,{asn1,{"memory allocation problem"}}});
handle_error({$1,_},L) -> % error in nif
@@ -610,15 +581,8 @@ 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.
+match_tags(Tlv = {Tag,_V},[T|_Tt]) ->
+ exit({error,{asn1,{wrong_tag,{{expected,T},{got,Tag,Tlv}}}}}).
%%%
%% skips components that do not match a tag in Tags
@@ -642,13 +606,6 @@ skip_ExtensionAdditions(TLV=[{Tag,_}|Rest],Tags) ->
%%===============================================================================
%%===============================================================================
-% converts a list to a record if necessary
-list_to_record(Name,List) when is_list(List) ->
- list_to_tuple([Name|List]);
-list_to_record(_Name,Tuple) when is_tuple(Tuple) ->
- Tuple.
-
-
fixoptionals(OptList,Val) when is_list(Val) ->
fixoptionals(OptList,Val,1,[],[]).
diff --git a/lib/asn1/src/asn1rt_check.erl b/lib/asn1/src/asn1rt_check.erl
index d9856901b8..35b993fc71 100644
--- a/lib/asn1/src/asn1rt_check.erl
+++ b/lib/asn1/src/asn1rt_check.erl
@@ -311,7 +311,8 @@ transform_to_EXTERNAL1990([Data_val_desc,Data_value],Acc)
when is_binary(Data_value)->
list_to_tuple(lists:reverse([{'single-ASN1-type',Data_value},
Data_val_desc|Acc]));
-transform_to_EXTERNAL1990([Data_value],Acc) when is_list(Data_value)->
+transform_to_EXTERNAL1990([Data_value],Acc)
+ when is_list(Data_value); is_binary(Data_value) ->
list_to_tuple(lists:reverse([{'octet-aligned',Data_value}|Acc])).
diff --git a/lib/asn1/src/asn1rt_per_bin.erl b/lib/asn1/src/asn1rt_per_bin.erl
deleted file mode 100644
index a124c7553d..0000000000
--- a/lib/asn1/src/asn1rt_per_bin.erl
+++ /dev/null
@@ -1,2287 +0,0 @@
-%%
-%% %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(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,
- encode_real/1, decode_real/1,
- encode_relative_oid/1, decode_relative_oid/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_UTF8String/1, decode_UTF8String/1,
- 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, getbits/2, getoctets/2]).
-
--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 is_tuple(Tuple) ->
- Tuple;
-list_to_record(Name,List) when is_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 is_tuple(Val) ->
- fixoptionals1(OptList,Val,[]);
-
-fixoptionals(OptList,Val) when is_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 is_tuple(Val) ->
- Bits = fixoptionals(OptList,Val,0),
- {Val,{bits,OptLength,Bits}};
-
-fixoptionals([],_Val,Acc) ->
- %% Optbits
- Acc;
-fixoptionals([{Pos,DefVal}|Ot],Val,Acc) ->
- case element(Pos,Val) of
- asn1_DEFAULT -> fixoptionals(Ot,Val,Acc bsl 1);
- DefVal -> fixoptionals(Ot,Val,Acc bsl 1);
- _ -> fixoptionals(Ot,Val,(Acc bsl 1) + 1)
- end;
-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 is_tuple(Bytes) ->
- getbit(Bytes);
-getext(Bytes) when is_binary(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 is_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 is_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 is_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 is_binary(Bin) ->
- getbits({0,Bin},Num).
-
-
-
-% getoctet(Bytes) when is_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 is_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 is_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 is_integer(N), Len1 > 1 ->
- [{bits,1,0}, % the value is in the root set
- encode_integer([{'ValueRange',{0,Len1-1}}],N)];
- N when is_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 is_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 is_integer(N), Len > 1 ->
- encode_integer([{'ValueRange',{0,Len-1}}],N);
- N when is_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 is_integer(Int),C == size(BinBits) ->
- {BinBits,{0,Bin}};
- Int when is_integer(Int) ->
- exit({error,{asn1,{illegal_value,C,BinBits}}})
- 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 is_integer(Int),C == (size(BinBits) - ((8 - Used) rem 8)) ->
- Result;
- Int when is_integer(Int) ->
- exit({error,{asn1,{illegal_value,C,BinBits}}})
- end.
-
-
-decode_fragmented_octets({0,Bin},C) ->
- decode_fragmented_octets(Bin,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 is_integer(Int), C == size(Octets) ->
- {Octets,{0,Bin}};
- Int when is_integer(Int) ->
- exit({error,{asn1,{illegal_value,C,Octets}}})
- 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 is_integer(Int),size(BinOctets) == Int ->
- {BinOctets,Bin2};
- Int when is_integer(Int) ->
- exit({error,{asn1,{illegal_value,C,BinOctets}}})
- 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 is_list(Val) ->
- Bin = list_to_binary(Val),
- [encode_length(undefined,size(Bin)),{octets,Bin}]; % octets implies align
-encode_open_type(_C, Val) when is_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 is_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 is_integer(V) ->
- encode_integer(C,V);
-encode_integer(C,{Name,V},NamedNumberList) when is_atom(Name) ->
- encode_integer(C,V,NamedNumberList).
-
-encode_integer(C,{Name,Val}) when is_atom(Name) ->
- encode_integer(C,Val);
-
-encode_integer([{Rc,_Ec}],Val) when is_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 is_list(C) ->
- case get_constraint(C,'SingleValue') of
- no ->
- encode_integer1(C,Val);
- V when is_integer(V),V == Val ->
- []; % a type restricted to a single value encodes to nothing
- V when is_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 is_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 is_integer(V) ->
- {V,Buffer};
- V when is_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 is_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 is_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 is_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 == 1 ->
- [];
- 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 == 1 ->
- {0,Buffer};
- 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 is_integer(Lb), Lb >= 0 -> % Ub > 65535
- encode_length(undefined,Len);
-encode_length({Vr={Lb,Ub},Ext},Len)
- when Ub =< 65535 ,Lb >= 0, Len=<Ub, is_list(Ext) ->
- %% constrained extensible
- [{bits,1,0},encode_constrained_number(Vr,Len)];
-encode_length({{Lb,_Ub},Ext},Len) when is_list(Ext) ->
- [{bits,1,1},encode_semi_constrained_number(Lb,Len)];
-encode_length(SingleValue,_Len) when is_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(Buffer,{Lb,_}) when is_integer(Lb), Lb >= 0 -> % Ub > 65535
- decode_length(Buffer,undefined);
-decode_length(Buffer,{VR={_Lb,_Ub},Ext}) when is_list(Ext) ->
- case getbit(Buffer) of
- {0,Buffer2} ->
- decode_length(Buffer2, VR);
- {1,Buffer2} ->
- decode_length(Buffer2, undefined)
- end;
-%% {0,Buffer2} = getbit(Buffer),
-%% decode_length(Buffer2, VR);
-
-
-%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 is_integer(SingleValue) ->
- {SingleValue,Buffer}.
-
-
- % X.691:11
-encode_boolean(true) ->
- {bits,1,1};
-encode_boolean(false) ->
- {bits,1,0};
-encode_boolean({Name,Val}) when is_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 is_tuple(Ntup1), is_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 is_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 is_atom(NewVal) -> {NewVal,Buffer3};
- _ -> {{asn1_enum,Val},Buffer3}
- end
- end;
-
-decode_enumerated(Buffer,C,NamedNumberTup) when is_tuple(NamedNumberTup) ->
- {Val,Buffer2} = decode_integer(Buffer,C),
- case catch (element(Val+1,NamedNumberTup)) of
- NewVal when is_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 is_integer(Unused),
- is_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 is_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 is_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 is_integer(V),V=<16 -> % fixed length 16 bits or less
-% pad_list(V,BitList);
-% V when is_integer(V) -> % fixed length 16 bits or more
-% [align,pad_list(V,BitList)]; % should be another case for V >= 65537
-% {Lb,Ub} when is_integer(Lb),is_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 is_list(BitListValue) ->
- BitListToBinary =
- %% fun that transforms a list of 1 and 0 to a tuple:
- %% {UnusedBitsInLastByte, Binary}
- fun([1|T],Acc,N,Fun) ->
- Fun(T,(Acc bsl 1)+1,N+1,Fun);
- ([0|T],Acc,N,Fun) ->
- Fun(T,(Acc bsl 1),N+1,Fun);
- ([_H|_T],_,_,_) ->
- exit({error,{asn1,{bitstring_bitlist,BitListValue}}});
- ([],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;(_)->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 is_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 is_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 is_integer(V),V=<16 ->
- {Unused2,Bin2} = pad_list(V,UnusedAndBin1),
- <<BitVal:V,_:Unused2>> = Bin2,
- {bits,V,BitVal};
- V when is_integer(V) ->
- [align, pad_list(V, UnusedAndBin1)];
- {Lb,Ub} when is_integer(Lb),is_integer(Ub) ->
- [encode_length({Lb,Ub},size(Bin1)*8 - Unused1),
- align,UnusedAndBin1];
- {{Fix,Fix},L} when is_integer(Fix),is_list(L) ->
- %% X.691 � 15.6, the rest of this paragraph is covered by
- %% the last, ie. Sc, clause in this case
- case (size(Bin1)*8)-Unused1 of
- Size when Size =< Fix, Fix =< 16 ->
- {Unused2,Bin2} = pad_list(Fix,UnusedAndBin),
- <<BitVal:Fix,_:Unused2>> = Bin2,
- [{bits,1,0},{bits,Fix,BitVal}];
- Size when Size =< Fix ->
- [{bits,1,0},align, pad_list(Fix, UnusedAndBin1)];
- Size ->
- [{bits,1,1},encode_length(undefined,Size),
- align,UnusedAndBin1]
- end;
- 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,<<>>},C) ->
- case C of
- Int when is_integer(Int),Int > 0 ->
- %% this padding see OTP-4353
- pad_list(Int,{0,<<>>});
- _ -> {0,<<>>}
- end;
-remove_trailing_bin(NamedNumberList, {_Unused,Bin},C) ->
- Size = size(Bin)-1,
- <<Bfront:Size/binary, LastByte:8>> = Bin,
- %% clear the Unused bits to be sure
- 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 is_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 is_integer(Lb) ->
- Lb;
-lower_bound({Lb,_}) when is_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 is_integer(V),V=<16 -> %fixed length 16 bits or less
- compact_bit_string(Buffer,V,NamedNumberList);
- V when is_integer(V),V=<65536 -> %fixed length > 16 bits
- Bytes2 = align(Buffer),
- compact_bit_string(Bytes2,V,NamedNumberList);
- V when is_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 is_integer(Lb),is_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);
- {{Fix,Fix},L} = Sc when is_list(L), is_integer(Fix), Fix =< 16 ->
- %% X.691 �15.6, special case of extension marker
- case decode_length(Buffer,Sc) of
- {Len,Bytes2} when Len > Fix ->
- Bytes3 = align(Bytes2),
- compact_bit_string(Bytes3,Len,NamedNumberList);
- {Len,Bytes2} ->
- compact_bit_string(Bytes2,Len,NamedNumberList)
- end;
- 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 is_integer(Lb),is_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 is_integer(V),V=<16 -> % fixed length 16 bits or less
- bit_list_or_named(Buffer,V,NamedNumberList);
- V when is_integer(V),V=<65536 ->
- Bytes2 = align(Buffer),
- bit_list_or_named(Bytes2,V,NamedNumberList);
- V when is_integer(V) ->
- Bytes2 = align(Buffer),
- {BinBits,_} = decode_fragmented_bits(Bytes2,V),
- bit_list_or_named(BinBits,V,NamedNumberList);
- {{Fix,Fix},L} = Sc when is_list(L), is_integer(Fix), Fix =< 16 ->
- %% X.691 �15.6, special case of extension marker
- case decode_length(Buffer,Sc) of
- {Len,Bytes2} when Len > Fix ->
- Bytes3 = align(Bytes2),
- bit_list_or_named(Bytes3,Len,NamedNumberList);
- {Len,Bytes2} when Len > 16 ->
- Bytes3 = align(Bytes2),
- bit_list_or_named(Bytes3,Len,NamedNumberList);
- {Len,Bytes2} ->
- bit_list_or_named(Bytes2,Len,NamedNumberList)
- end;
- Sc -> %% X.691 �15.6, 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 is_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_string2(C,Val).
-
-encode_octet_string2(C,{_Name,Val}) ->
- encode_octet_string2(C,Val);
-encode_octet_string2(C,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 is_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 is_integer(Sv), Sv =<65535 -> % fixed length
- getoctets_as_list(Bytes,Sv);
- Sv when is_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 is_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 is_atom(Name) ->
- encode_restricted_string(aligned,Val);
-
-encode_restricted_string(aligned,Val) when is_list(Val)->
- [encode_length(undefined,length(Val)),{octets,Val}].
-
-encode_known_multiplier_string(aligned,StringType,C,_Ext,{Name,Val}) when is_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 is_integer(Ub), Ub*NumBits =< 16 ->
- Result;
- 0 ->
- [];
- Ub when is_integer(Ub),Ub =<65535 -> % fixed length
- [align,Result];
- {Ub,Lb} ->
- [encode_length({Ub,Lb},length(Val)),align,Result];
- Vl when is_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 is_integer(Ub), Ub*NumBits =< 16 ->
- chars_decode(Bytes,NumBits,StringType,C,Ub);
- Ub when is_integer(Ub),Ub =<65535 -> % fixed length
- Bytes1 = align(Bytes),
- chars_decode(Bytes1,NumBits,StringType,C,Ub);
- 0 ->
- {[],Bytes};
- Vl when is_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 is_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]).
-
-
-%% UTF8String
-encode_UTF8String(Val) when is_binary(Val) ->
- [encode_length(undefined,size(Val)),{octets,Val}];
-encode_UTF8String(Val) ->
- Bin = list_to_binary(Val),
- encode_UTF8String(Bin).
-
-decode_UTF8String(Bytes) ->
- {Len,Bytes2} = decode_length(Bytes,undefined),
- {Octs,Bytes3} = getoctets_as_list(Bytes2,Len),
- {list_to_binary(Octs),Bytes3}.
-
-
- % X.691:17
-encode_null(_) -> []. % encodes to nothing
-%encode_null({Name,Val}) when is_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 is_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 is_atom(Cname),is_tuple(V) ->
- e_object_identifier(tuple_to_list(V));
-e_object_identifier({Cname,V}) when is_atom(Cname),is_list(V) ->
- e_object_identifier(V);
-e_object_identifier(V) when is_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];
-e_object_element(Num) ->
- [e_o_e(Num bsr 7)|[Num band 2#1111111]].
-e_o_e(Num) when Num < 128 ->
- Num bor 2#10000000;
-e_o_e(Num) ->
- [e_o_e(Num bsr 7)|[(Num band 2#1111111) bor 2#10000000]].
-
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% 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).
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% encode_relative_oid(Val) -> CompleteList
-%% encode_relative_oid({Name,Val}) -> CompleteList
-encode_relative_oid({Name,Val}) when is_atom(Name) ->
- encode_relative_oid(Val);
-encode_relative_oid(Val) when is_tuple(Val) ->
- encode_relative_oid(tuple_to_list(Val));
-encode_relative_oid(Val) when is_list(Val) ->
- Octets = list_to_binary([e_object_element(X)||X <- Val]),
- [encode_length(undefined,size(Octets)),{octets,Octets}].
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% decode_relative_oid(Val) -> {ROID,Rest}
-%% decode_relative_oid({Name,Val}) -> {ROID,Rest}
-decode_relative_oid(Bytes) ->
- {Len,Bytes2} = decode_length(Bytes,undefined),
- {Octs,Bytes3} = getoctets_as_list(Bytes2,Len),
- ObjVals = dec_subidentifiers(Octs,0,[]),
- {list_to_tuple(ObjVals),Bytes3}.
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% encode_real(Val) -> CompleteList
-%% encode_real({Name,Val}) -> CompleteList
-encode_real({Name,Val}) when is_atom(Name) ->
- encode_real(Val);
-encode_real(Real) ->
- {EncVal,Len} = ?RT_COMMON:encode_real([],Real),
- [encode_length(undefined,Len),{octets,EncVal}].
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% decode_real(Val) -> {REALvalue,Rest}
-%% decode_real({Name,Val}) -> {REALvalue,Rest}
-decode_real(Bytes) ->
- {Len,{0,Bytes2}} = decode_length(Bytes,undefined),
- {RealVal,Rest,Len} = ?RT_COMMON:decode_real(Bytes2,Len),
- {RealVal,{0,Rest}}.
-
-
-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 is_list(InList) ->
-% complete1(InList,[]);
-% complete1(InList) ->
-% complete1([InList],[]).
-
-% complete1([{debug,_}|T], Acc) ->
-% complete1(T,Acc);
-% complete1([H|T],Acc) when is_list(H) ->
-% {NewH,NewAcclen} = complete1(H,Acc),
-% complete1(T,NewH,NewAcclen);
-
-% complete1([{0,Bin}|T],Acc,0) when is_binary(Bin) ->
-% complete1(T,[Bin|Acc],0);
-% complete1([{Unused,Bin}|T],Acc,0) when is_integer(Unused),is_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 is_integer(Unused),is_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 ,is_integer(Val) ->
-% % complete1([{octets,<<Val:N/unit:8>>}|T],Acc,Acclen);
-% complete1([{octets,N,Val}|T],Acc,Acclen) when N =< 4 ,is_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 is_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 is_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 is_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 is_list(InList) ->
- complete1(InList,[],[]);
-complete1(InList) ->
- complete1([InList],[],[]).
-
-complete1([],Acc,Bacc) ->
- {Acc,Bacc};
-complete1([H|T],Acc,Bacc) when is_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 is_binary(Bin) ->
- complete1(T,[Acc|Bin],[]);
-complete1([{Unused,Bin}|T],Acc,[]) when is_integer(Unused),is_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 is_integer(Unused),is_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/asn1/src/asn1rt_per_bin_rt2ct.erl b/lib/asn1/src/asn1rt_per_bin_rt2ct.erl
index 750b59aba6..5997232f13 100644
--- a/lib/asn1/src/asn1rt_per_bin_rt2ct.erl
+++ b/lib/asn1/src/asn1rt_per_bin_rt2ct.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,30 +18,25 @@
%%
%%
-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([decode_fragmented/3]).
-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,
+ skipextensions/3, getbit/1, getchoice/3 ]).
+-export([set_choice/3, encode_integer/2, encode_integer/3 ]).
+-export([encode_small_number/1,
+ encode_length/2,
+ encode_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,
+-export([encode_bit_string/3, decode_bit_string/3 ]).
+-export([encode_octet_string/2,
encode_object_identifier/1, decode_object_identifier/1,
encode_real/1, decode_real/1,
encode_relative_oid/1, decode_relative_oid/1,
complete/1]).
-
-export([encode_open_type/2, decode_open_type/2]).
-export([encode_GeneralString/2, decode_GeneralString/2,
@@ -52,19 +47,10 @@
encode_UTF8String/1,decode_UTF8String/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,
+-export([encode_unconstrained_number/1,
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
- ]).
+ decode_known_multiplier_string/5]).
-export([eint_positive/1]).
@@ -74,32 +60,6 @@
-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 is_tuple(Tuple) ->
- Tuple;
-list_to_record(Name,List) when is_list(List) ->
- list_to_tuple([Name|List]).
-
%%--------------------------------------------------------
%% setchoiceext(InRootSet) -> [{bit,X}]
%% X is set to 1 when InRootSet==false
@@ -144,15 +104,6 @@ fixoptionals([Pos|Ot],Val,Acc) ->
end.
-getext(Bytes) when is_bitstring(Bytes) ->
- getbit(Bytes).
-
-getextension(0, Bytes) ->
- {<<>>,Bytes};
-getextension(1, Bytes) ->
- {Len,Bytes2} = decode_small_length(Bytes),
- getbits_as_binary(Len,Bytes2).% {Bin,Bytes3}.
-
fixextensions({ext,ExtPos,ExtNum},Val) ->
case fixextensions(ExtPos,ExtNum+ExtPos,Val,0) of
0 -> [];
@@ -195,14 +146,6 @@ getchoice(Bytes,_,1) ->
getchoice(Bytes,NumChoices,0) ->
decode_constrained_number(Bytes,{0,NumChoices-1}).
-%% old version kept for backward compatibility with generates from R7B01
-getoptionals(Bytes,NumOpt) ->
- getbits_as_binary(NumOpt,Bytes).
-
-%% new version used in generates from r8b_patch/3 and later
-getoptionals2(Bytes,NumOpt) ->
- {_,_} = getbits(Bytes,NumOpt).
-
%% getbits_as_binary(Num,Bytes) -> {Bin,Rest}
%% Num = integer(),
@@ -345,32 +288,6 @@ decode_fragmented_bits(<<0:1,Len:7,Bin/binary>>,C,Acc) ->
exit({error,{asn1,{illegal_value,C,BinBits}}})
end.
-
-decode_fragmented_octets(Bin,C) ->
- decode_fragmented_octets(Bin,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 is_integer(Int), C == size(Octets) ->
- {Octets,Bin};
- Int when is_integer(Int) ->
- exit({error,{asn1,{illegal_value,C,Octets}}})
- 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 is_integer(Int),size(BinOctets) == Int ->
- {BinOctets,Bin2};
- Int when is_integer(Int) ->
- exit({error,{asn1,{illegal_value,C,BinOctets}}})
- end.
-
-
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% encode_open_type(Constraint, Value) -> CompleteList
@@ -455,40 +372,6 @@ 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 is_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 is_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
@@ -508,7 +391,7 @@ decode_small_number(Bytes) ->
0 ->
getbits(Bytes2,6);
1 ->
- decode_semi_constrained_number(Bytes2,0)
+ decode_semi_constrained_number(Bytes2)
end.
%% X.691:10.7 Encoding of a semi-constrained whole number
@@ -531,12 +414,10 @@ encode_semi_constrained_number(Lb,Val) ->
[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) ->
+decode_semi_constrained_number(Bytes) ->
{Len,Bytes2} = decode_length(Bytes,undefined),
{V,Bytes3} = getoctets(Bytes2,Len),
- {V+Lb,Bytes3}.
+ {V,Bytes3}.
encode_constrained_number({Lb,_Ub},_Range,{bits,N},Val) ->
Val2 = Val-Lb,
@@ -605,19 +486,13 @@ encode_constrained_number({Lb,Ub},Val) when Val >= Lb, Ub >= Val ->
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];
+ Range =< (1 bsl (255*8)) ->
+ Octs = binary:encode_unsigned(Val2),
+ RangeOcts = binary:encode_unsigned(Range - 1),
+ OctsLen = erlang:byte_size(Octs),
+ RangeOctsLen = erlang:byte_size(RangeOcts),
+ LengthBitsNeeded = minimum_bits(RangeOctsLen - 1),
+ [10,LengthBitsNeeded,OctsLen-1,20,OctsLen,Octs];
true ->
exit({not_supported,{integer_range,Range}})
end;
@@ -628,13 +503,6 @@ 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} =
@@ -661,23 +529,27 @@ decode_constrained_number(Buffer,{Lb,_Ub},Range) ->
getoctets(Buffer,1);
Range =< 65536 ->
getoctets(Buffer,2);
- Range =< 16#1000000 ->
- {Len,Bytes2} = decode_length(Buffer,{1,3}),
- {Octs,Bytes3} = getoctets_as_bin(Bytes2,Len),
- {dec_pos_integer(Octs),Bytes3};
- Range =< 16#100000000 ->
- {Len,Bytes2} = decode_length(Buffer,{1,4}),
- {Octs,Bytes3} = getoctets_as_bin(Bytes2,Len),
- {dec_pos_integer(Octs),Bytes3};
- Range =< 16#10000000000 ->
- {Len,Bytes2} = decode_length(Buffer,{1,5}),
- {Octs,Bytes3} = getoctets_as_bin(Bytes2,Len),
- {dec_pos_integer(Octs),Bytes3};
+ Range =< (1 bsl (255*8)) ->
+ OList = binary:bin_to_list(binary:encode_unsigned(Range - 1)),
+ RangeOctLen = length(OList),
+ {Len, Bytes} = decode_length(Buffer, {1, RangeOctLen}),
+ {Octs, RestBytes} = getoctets_as_bin(Bytes, Len),
+ {binary:decode_unsigned(Octs), RestBytes};
true ->
exit({not_supported,{integer_range,Range}})
end,
{Val+Lb,Remain}.
+%% For some reason the minimum bits needed in the length field in
+%% the encoding of constrained whole numbers must always be at least 2?
+minimum_bits(N) when N < 4 -> 2;
+minimum_bits(N) when N < 8 -> 3;
+minimum_bits(N) when N < 16 -> 4;
+minimum_bits(N) when N < 32 -> 5;
+minimum_bits(N) when N < 64 -> 6;
+minimum_bits(N) when N < 128 -> 7;
+minimum_bits(_N) -> 8.
+
%% X.691:10.8 Encoding of an unconstrained whole number
encode_unconstrained_number(Val) when Val >= 0 ->
@@ -731,28 +603,6 @@ enint(-1, [B1|T]) when B1 > 127 ->
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_bin(Bytes2,Len),
- {dec_integer(Ints),Bytes3}.
-
-
-dec_pos_integer(Ints) ->
- decpint(Ints).
-dec_integer(Bin = <<0:1,_:7,_/binary>>) ->
- decpint(Bin);
-dec_integer(<<_:1,B:7,BitStr/bitstring>>) ->
- Size = bit_size(BitStr),
- <<I:Size>> = BitStr,
- (-128 + B) bsl bit_size(BitStr) bor I.
-
-
-
-decpint(Bin) ->
- Size = bit_size(Bin),
- <<Int:Size>> = Bin,
- Int.
-
%% X.691:10.9 Encoding of a length determinant
%%encode_small_length(undefined,Len) -> % null means no UpperBound
%% encode_small_number(Len).
@@ -797,18 +647,6 @@ encode_small_length(Len) ->
[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
case align(Buffer) of
<<0:1,Oct:7,Rest/binary>> ->
@@ -851,38 +689,6 @@ decode_length(Buffer,SingleValue) when is_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 is_tuple(Ntup1), is_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 is_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 is_atom(NewVal) -> {NewVal,Buffer3};
- _ -> {{asn1_enum,Val},Buffer3}
- end
- end;
-
-decode_enumerated(Buffer,C,NamedNumberTup) when is_tuple(NamedNumberTup) ->
- {Val,Buffer2} = decode_integer(Buffer,C),
- case catch (element(Val+1,NamedNumberTup)) of
- NewVal when is_atom(NewVal) -> {NewVal,Buffer2};
- _Error -> exit({error,{asn1,{decode_enumerated,{Val,NamedNumberTup}}}})
- end.
%%===============================================================================
%%===============================================================================
@@ -1333,49 +1139,74 @@ 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)];
+ try
+ [encode_length(SZ,Len),2,
+ octets_to_complete(Len,Val)]
+ catch
+ exit:{error,{asn1,{encode_length,_}}} ->
+ encode_fragmented_octet_string(Val)
+ end;
encode_octet_string(SZ,false,Val) when is_list(SZ) ->
Len = length(Val),
- [encode_length({hd(SZ),lists:max(SZ)},Len),2,
- octets_to_complete(Len,Val)];
+ try
+ [encode_length({hd(SZ),lists:max(SZ)},Len),2,
+ octets_to_complete(Len,Val)]
+ catch
+ exit:{error,{asn1,{encode_length,_}}} ->
+ encode_fragmented_octet_string(Val)
+ end;
+encode_octet_string(Sv,false,Val) when is_integer(Sv) ->
+ encode_fragmented_octet_string(Val);
encode_octet_string(no,false,Val) ->
Len = length(Val),
- [encode_length(undefined,Len),2,
- octets_to_complete(Len,Val)];
+ try
+ [encode_length(undefined,Len),2,
+ octets_to_complete(Len,Val)]
+ catch
+ exit:{error,{asn1,{encode_length,_}}} ->
+ encode_fragmented_octet_string(Val)
+ end;
encode_octet_string(C,_,_) ->
exit({error,{not_implemented,C}}).
-
-decode_octet_string(Bytes,Range) ->
- decode_octet_string(Bytes,Range,false).
-
-decode_octet_string(<<B1,Bytes/bitstring>>,1,false) ->
-%% {B1,Bytes2} = getbits(Bytes,8),
- {[B1],Bytes};
-decode_octet_string(<<B1,B2,Bytes/bitstring>>,2,false) ->
-%% {Bs,Bytes2}= getbits(Bytes,16),
-%% {binary_to_list(<<Bs:16>>),Bytes2};
- {[B1,B2],Bytes};
-decode_octet_string(Bytes,Sv,false) when is_integer(Sv),Sv=<65535 ->
- %% Bytes2 = align(Bytes),
- %% getoctets_as_list aligns buffer before it picks octets
- getoctets_as_list(Bytes,Sv);
-decode_octet_string(Bytes,Sv,false) when is_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(Bytes2,Len);
-decode_octet_string(Bytes,Sv,false) when is_list(Sv) ->
- {Len,Bytes2} = decode_length(Bytes,{hd(Sv),lists:max(Sv)}),
-%% Bytes3 = align(Bytes2),
- getoctets_as_list(Bytes2,Len);
-decode_octet_string(Bytes,no,false) ->
- {Len,Bytes2} = decode_length(Bytes,undefined),
-%% Bytes3 = align(Bytes2),
- getoctets_as_list(Bytes2,Len).
+encode_fragmented_octet_string(Val) ->
+ Bin = iolist_to_binary(Val),
+ efos_1(Bin).
+
+efos_1(<<B1:16#C000/binary,B2:16#4000/binary,T/binary>>) ->
+ [20,1,<<3:2,4:6>>,
+ octets_to_complete(16#C000, B1),
+ octets_to_complete(16#4000, B2)|efos_1(T)];
+efos_1(<<B:16#C000/binary,T/binary>>) ->
+ [20,1,<<3:2,3:6>>,octets_to_complete(16#C000, B)|efos_1(T)];
+efos_1(<<B:16#8000/binary,T/binary>>) ->
+ [20,1,<<3:2,2:6>>,octets_to_complete(16#8000, B)|efos_1(T)];
+efos_1(<<B:16#4000/binary,T/binary>>) ->
+ [20,1,<<3:2,1:6>>,octets_to_complete(16#4000, B)|efos_1(T)];
+efos_1(<<>>) ->
+ [20,1,0];
+efos_1(<<B/bitstring>>) ->
+ Len = byte_size(B),
+ [encode_length(undefined, Len),octets_to_complete(Len, B)].
+
+decode_fragmented(SegSz0, Buf0, Unit) ->
+ SegSz = SegSz0 * Unit * ?'16K',
+ <<Res:SegSz/bitstring,Buf/bitstring>> = Buf0,
+ decode_fragmented_1(Buf, Unit, Res).
+
+decode_fragmented_1(<<0:1,N:7,Buf0/bitstring>>, Unit, Res) ->
+ Sz = N*Unit,
+ <<S:Sz/bitstring,Buf/bitstring>> = Buf0,
+ {<<Res/bitstring,S/bitstring>>,Buf};
+decode_fragmented_1(<<1:1,0:1,N:14,Buf0/bitstring>>, Unit, Res) ->
+ Sz = N*Unit,
+ <<S:Sz/bitstring,Buf/bitstring>> = Buf0,
+ {<<Res/bitstring,S/bitstring>>,Buf};
+decode_fragmented_1(<<1:1,1:1,SegSz0:6,Buf0/bitstring>>, Unit, Res0) ->
+ SegSz = SegSz0 * Unit * ?'16K',
+ <<Frag:SegSz/bitstring,Buf/bitstring>> = Buf0,
+ Res = <<Res0/bitstring,Frag/bitstring>>,
+ decode_fragmented_1(Buf, Unit, Res).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -1563,16 +1394,6 @@ chars_decode2(Bytes,{Min,Max,CharInTab},NumBits,Len,Acc) ->
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 is_atom(Name) ->
-% encode_null(Val).
-
-decode_null(Bytes) ->
- {'NULL',Bytes}.
-
-
-
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% encode_UTF8String(Val) -> CompleteList
%% Val -> <<utf8encoded binary>>
diff --git a/lib/asn1/src/asn1rt_uper_bin.erl b/lib/asn1/src/asn1rt_uper_bin.erl
index abe178a69e..fc65d80245 100644
--- a/lib/asn1/src/asn1rt_uper_bin.erl
+++ b/lib/asn1/src/asn1rt_uper_bin.erl
@@ -25,22 +25,20 @@
%%-compile(export_all).
- -export([cindex/3, list_to_record/2]).
- -export([setext/1, fixoptionals/3,
+-export([decode_fragmented/3]).
+-export([setext/1, fixoptionals/3,
fixextensions/2,
- getext/1, getextension/2, skipextensions/3, getbit/1, getchoice/3 ]).
- -export([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,
+ skipextensions/3, getbit/1, getchoice/3 ]).
+-export([set_choice/3, encode_integer/2, encode_integer/3]).
+-export([encode_small_number/1, encode_boolean/1,
+ encode_length/2,
+ encode_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_relative_oid/1, decode_relative_oid/1,
+-export([encode_bit_string/3, decode_bit_string/3 ]).
+-export([encode_octet_string/2,
+ encode_relative_oid/1, decode_relative_oid/1,
encode_object_identifier/1, decode_object_identifier/1,
- encode_real/1, decode_real/1,
+ encode_real/1, decode_real/1,
complete/1, complete_NFP/1]).
@@ -65,19 +63,6 @@
-define('64K',65536).
-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 is_tuple(Tuple) ->
- Tuple;
-list_to_record(Name,List) when is_list(List) ->
- list_to_tuple([Name|List]).
-
-
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% setext(true|false) -> CompleteList
%%
@@ -113,16 +98,6 @@ fixoptionals([Pos|Ot],Val,Acc) ->
end.
-getext(Bytes) when is_bitstring(Bytes) ->
- getbit(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 -> [];
@@ -145,14 +120,15 @@ fixextensions(Pos,ExtPos,Val,Acc) ->
end,
fixextensions(Pos+1,ExtPos,Val,(Acc bsl 1)+Bit).
-skipextensions(Bytes,Nr,ExtensionBitPattern) ->
- case (catch element(Nr,ExtensionBitPattern)) of
- 1 ->
+skipextensions(Bytes,Nr,ExtensionBitstr) when is_bitstring(ExtensionBitstr) ->
+ Prev = Nr - 1,
+ case ExtensionBitstr of
+ <<_:Prev,1:1,_/bitstring>> ->
{_,Bytes2} = decode_open_type(Bytes,[]),
- skipextensions(Bytes2, Nr+1, ExtensionBitPattern);
- 0 ->
- skipextensions(Bytes, Nr+1, ExtensionBitPattern);
- {'EXIT',_} -> % badarg, no more extensions
+ skipextensions(Bytes2, Nr+1, ExtensionBitstr);
+ <<_:Prev,0:1,_/bitstring>> ->
+ skipextensions(Bytes, Nr+1, ExtensionBitstr);
+ _ ->
Bytes
end.
@@ -165,11 +141,6 @@ getchoice(Bytes,NumChoices,0) ->
decode_constrained_number(Bytes,{0,NumChoices-1}).
-%%%%%%%%%%%%%%%
-getoptionals2(Bytes,NumOpt) ->
- getbits(Bytes,NumOpt).
-
-
%% getbits_as_binary(Num,Bytes) -> {{Unused,BinBits},RestBytes},
%% Num = integer(),
%% Bytes = list() | tuple(),
@@ -289,33 +260,6 @@ decode_fragmented_bits(<<0:1,Len:7,BitStr/bitstring>>,C,Acc) ->
exit({error,{asn1,{illegal_value,C,ResBitStr}}})
end.
-
-decode_fragmented_octets({0,Bin},C) ->
- decode_fragmented_octets(Bin,C,[]).
-
-decode_fragmented_octets(<<3:2,Len:6,BitStr/bitstring>>,C,Acc) ->
- FragLen = Len * ?'16K',
- <<Value:FragLen/binary,Rest/bitstring>> = BitStr,
- decode_fragmented_octets(Rest,C,[Value|Acc]);
-decode_fragmented_octets(<<0:1,0:7,Bin/bitstring>>,C,Acc) ->
- Octets = list_to_binary(lists:reverse(Acc)),
- case C of
- Int when is_integer(Int), C == size(Octets) ->
- {Octets,Bin};
- Int when is_integer(Int) ->
- exit({error,{asn1,{illegal_value,C,Octets}}})
- end;
-decode_fragmented_octets(<<0:1,Len:7,BitStr/bitstring>>,C,Acc) ->
- <<Value:Len/binary-unit:8,BitStr2/binary>> = BitStr,
- BinOctets = list_to_binary(lists:reverse([Value|Acc])),
- case C of
- Int when is_integer(Int),size(BinOctets) == Int ->
- {BinOctets,BitStr2};
- Int when is_integer(Int) ->
- exit({error,{asn1,{illegal_value,C,BinOctets}}})
- end.
-
-
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% encode_open_type(Constraint, Value) -> CompleteList
@@ -398,47 +342,6 @@ encode_integer1(C, 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 is_tuple(Rc) ->
- {Ext,Buffer2} = getext(Buffer),
- case Ext of
- 0 -> decode_integer(Buffer2,[Rc]); %% Value in root of constraint
- 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 is_integer(V) ->
- {V,Buffer};
- V when is_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
@@ -455,7 +358,7 @@ decode_small_number(Bytes) ->
0 ->
getbits(Bytes2,6);
1 ->
- decode_semi_constrained_number(Bytes2,0)
+ decode_semi_constrained_number(Bytes2)
end.
%% X.691:10.7 Encoding of a semi-constrained whole number
@@ -479,12 +382,10 @@ encode_semi_constrained_number(Lb,Val) ->
[encode_length(undefined,Size),Bin]
end.
-decode_semi_constrained_number(Bytes,{Lb,_}) ->
- decode_semi_constrained_number(Bytes,Lb);
-decode_semi_constrained_number(Bytes,Lb) ->
+decode_semi_constrained_number(Bytes) ->
{Len,Bytes2} = decode_length(Bytes,undefined),
{V,Bytes3} = getoctets(Bytes2,Len),
- {V+Lb,Bytes3}.
+ {V,Bytes3}.
encode_constrained_number(Range,{Name,Val}) when is_atom(Name) ->
encode_constrained_number(Range,Val);
@@ -566,23 +467,6 @@ enint(-1, [B1|T]) when B1 > 127 ->
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_bin(Bytes2,Len),
- {dec_integer(Ints),Bytes3}.
-
-dec_integer(Bin = <<0:1,_:7,_/bitstring>>) ->
- decpint(Bin);
-dec_integer(<<_:1,B:7,BitStr/bitstring>>) ->
- Size = bit_size(BitStr),
- <<I:Size>> = BitStr,
- (-128 + B) bsl bit_size(BitStr) bor I.
-
-decpint(Bin) ->
- Size = bit_size(Bin),
- <<Int:Size>> = Bin,
- Int.
-
%% X.691:10.9 Encoding of a length determinant
%%encode_small_length(undefined,Len) -> % null means no UpperBound
@@ -597,7 +481,7 @@ encode_length(undefined,Len) -> % un-constrained
Len < 16384 ->
<<2:2,Len:14>>;
true -> % should be able to endode length >= 16384
- exit({error,{asn1,{encode_length,{nyi,above_16k}}}})
+ error({error,{asn1,{encode_length,{nyi,above_16k}}}})
end;
encode_length({0,'MAX'},Len) ->
@@ -623,18 +507,6 @@ encode_small_length(Len) ->
[<<1: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).
-
%% un-constrained
decode_length(<<0:1,Oct:7,Rest/bitstring>>,undefined) ->
{Oct,Rest};
@@ -677,38 +549,6 @@ encode_boolean({Name,Val}) when is_atom(Name) ->
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 is_tuple(Ntup1), is_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 is_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 is_atom(NewVal) -> {NewVal,Buffer3};
- _ -> {{asn1_enum,Val},Buffer3}
- end
- end;
-
-decode_enumerated(Buffer,C,NamedNumberTup) when is_tuple(NamedNumberTup) ->
- {Val,Buffer2} = decode_integer(Buffer,C),
- case catch (element(Val+1,NamedNumberTup)) of
- NewVal when is_atom(NewVal) -> {NewVal,Buffer2};
- _Error -> exit({error,{asn1,{decode_enumerated,{Val,NamedNumberTup}}}})
- end.
-
%%============================================================================
%%============================================================================
@@ -1067,36 +907,71 @@ encode_octet_string(C,Val) ->
list_to_binary(Val);
2 ->
list_to_binary(Val);
- Sv when Sv =<65535, Sv == length(Val) -> % fixed length
- list_to_binary(Val);
- VR = {_,_} ->
- [encode_length(VR,length(Val)),list_to_binary(Val)];
+ {_,_}=VR ->
+ try
+ [encode_length(VR, length(Val)),list_to_binary(Val)]
+ catch
+ error:{error,{asn1,{encode_length,_}}} ->
+ encode_fragmented_octet_string(Val)
+ end;
+ Sv when is_integer(Sv), Sv =:= length(Val) -> % fixed length
+ if
+ Sv =< 65535 ->
+ list_to_binary(Val);
+ true ->
+ encode_fragmented_octet_string(Val)
+ end;
Sv when is_list(Sv) ->
- [encode_length({hd(Sv),lists:max(Sv)},length(Val)),list_to_binary(Val)];
+ try
+ [encode_length({hd(Sv),lists:max(Sv)},
+ length(Val)),list_to_binary(Val)]
+ catch
+ error:{error,{asn1,{encode_length,_}}} ->
+ encode_fragmented_octet_string(Val)
+ end;
no ->
- [encode_length(undefined,length(Val)),list_to_binary(Val)]
+ try
+ [encode_length(undefined, length(Val)),list_to_binary(Val)]
+ catch
+ error:{error,{asn1,{encode_length,_}}} ->
+ encode_fragmented_octet_string(Val)
+ end
end.
-decode_octet_string(Bytes,C) ->
- decode_octet_string1(Bytes,get_constraint(C,'SizeConstraint')).
-decode_octet_string1(<<B1,Bytes/bitstring>>,1) ->
- {[B1],Bytes};
-decode_octet_string1(<<B1,B2,Bytes/bitstring>>,2) ->
- {[B1,B2],Bytes};
-decode_octet_string1(Bytes,Sv) when is_integer(Sv),Sv=<65535 ->
- getoctets_as_list(Bytes,Sv);
-decode_octet_string1(Bytes,Sv) when is_integer(Sv) ->
- decode_fragmented_octets(Bytes,Sv);
-decode_octet_string1(Bytes,{Lb,Ub}) ->
- {Len,Bytes2} = decode_length(Bytes,{Lb,Ub}),
- getoctets_as_list(Bytes2,Len);
-decode_octet_string1(Bytes,Sv) when is_list(Sv) ->
- {Len,Bytes2} = decode_length(Bytes,{hd(Sv),lists:max(Sv)}),
- getoctets_as_list(Bytes2,Len);
-decode_octet_string1(Bytes,no) ->
- {Len,Bytes2} = decode_length(Bytes,undefined),
- getoctets_as_list(Bytes2,Len).
-
+encode_fragmented_octet_string(Val) ->
+ Bin = list_to_binary(Val),
+ efos_1(Bin).
+
+efos_1(<<B:16#10000/binary,T/binary>>) ->
+ [<<3:2,4:6>>,B|efos_1(T)];
+efos_1(<<B:16#C000/binary,T/binary>>) ->
+ [<<3:2,3:6>>,B|efos_1(T)];
+efos_1(<<B:16#8000/binary,T/binary>>) ->
+ [<<3:2,2:6>>,B|efos_1(T)];
+efos_1(<<B:16#4000/binary,T/binary>>) ->
+ [<<3:2,1:6>>,B|efos_1(T)];
+efos_1(<<B/bitstring>>) ->
+ Len = byte_size(B),
+ [encode_length(undefined, Len),B].
+
+decode_fragmented(SegSz0, Buf0, Unit) ->
+ SegSz = SegSz0 * Unit * ?'16K',
+ <<Res:SegSz/bitstring,Buf/bitstring>> = Buf0,
+ decode_fragmented_1(Buf, Unit, Res).
+
+decode_fragmented_1(<<0:1,N:7,Buf0/bitstring>>, Unit, Res) ->
+ Sz = N*Unit,
+ <<S:Sz/bitstring,Buf/bitstring>> = Buf0,
+ {<<Res/bitstring,S/bitstring>>,Buf};
+decode_fragmented_1(<<1:1,0:1,N:14,Buf0/bitstring>>, Unit, Res) ->
+ Sz = N*Unit,
+ <<S:Sz/bitstring,Buf/bitstring>> = Buf0,
+ {<<Res/bitstring,S/bitstring>>,Buf};
+decode_fragmented_1(<<1:1,1:1,SegSz0:6,Buf0/bitstring>>, Unit, Res0) ->
+ SegSz = SegSz0 * Unit * ?'16K',
+ <<Frag:SegSz/bitstring,Buf/bitstring>> = Buf0,
+ Res = <<Res0/bitstring,Frag/bitstring>>,
+ decode_fragmented_1(Buf, Unit, Res).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -1441,12 +1316,6 @@ decode_UTF8String(Bytes) ->
getoctets_as_bin(Bytes2,Len).
- % X.691:17
-encode_null(_) -> []. % encodes to nothing
-
-decode_null(Bytes) ->
- {'NULL',Bytes}.
-
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% encode_object_identifier(Val) -> CompleteList
%% encode_object_identifier({Name,Val}) -> CompleteList
diff --git a/lib/asn1/test/Makefile b/lib/asn1/test/Makefile
index 6e6374baf1..1794d6bb71 100644
--- a/lib/asn1/test/Makefile
+++ b/lib/asn1/test/Makefile
@@ -83,6 +83,7 @@ MODULES= \
testInfObj \
testParameterizedInfObj \
testMergeCompile \
+ testMultipleLevels \
testDeepTConstr \
testTimer \
testMegaco \
diff --git a/lib/asn1/test/asn1_SUITE.erl b/lib/asn1/test/asn1_SUITE.erl
index 3b9a7532c0..325293f35d 100644
--- a/lib/asn1/test/asn1_SUITE.erl
+++ b/lib/asn1/test/asn1_SUITE.erl
@@ -1,3 +1,4 @@
+%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2001-2012. All Rights Reserved.
@@ -19,28 +20,9 @@
-module(asn1_SUITE).
--define(only_per(Func),
- if Rule == per orelse Rule == per_bin -> Func;
- true -> ok
- end).
-define(only_ber(Func),
- if Rule == ber orelse Rule == ber_bin orelse Rule == ber_bin_v2 -> Func;
- true -> ok
- end).
--define(only_uper(Func),
- case Rule of
- uper_bin -> Func;
- _ -> ok
- end).
--define(only_per_nif(Func),
- case {Rule, lists:member(optimize, Opts)} of
- {per_bin, true} -> Func;
- _ -> ok
- end).
--define(only_ber_nif(Func),
- case {Rule, lists:member(nif, Opts)} of
- {ber_bin_v2, true} -> Func;
- _ -> ok
+ if Rule =:= ber -> Func;
+ true -> ok
end).
-compile(export_all).
@@ -54,7 +36,8 @@
suite() -> [{ct_hooks, [ts_install_cth]}].
all() ->
- [{group, parallel},
+ [{group, compile},
+ {group, parallel},
{group, app_test},
{group, appup_test},
@@ -70,21 +53,20 @@ groups() ->
[{compile, parallel([]),
[c_syntax,
c_string,
- c_implicit_before_choice]},
+ c_implicit_before_choice,
+ constraint_equivalence]},
{ber, parallel([]),
[ber_choiceinseq,
% Uses 'SOpttest'
- {group, [], [ber_optional,
- ber_optional_keyed_list]}]},
+ ber_optional]},
{app_test, [], [{asn1_app_test, all}]},
{appup_test, [], [{asn1_appup_test, all}]},
{parallel, parallel([]),
- [{group, compile},
- {group, ber},
+ [{group, ber},
% Uses 'P-Record', 'Constraints', 'MEDIA-GATEWAY-CONTROL'...
{group, [], [parse,
test_driver_load,
@@ -115,6 +97,7 @@ groups() ->
testChoTypeRefPrim,
testChoTypeRefSeq,
testChoTypeRefSet,
+ testMultipleLevels,
testDef,
testOpt,
testSeqDefault,
@@ -197,18 +180,12 @@ groups() ->
testDoubleEllipses,
test_x691,
ticket_6143,
- testExtensionAdditionGroup,
test_OTP_9688]},
{performance, [],
[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,
+ testTimer_uper,
smp]}].
parallel(Options) ->
@@ -242,14 +219,13 @@ init_per_testcase(Func, Config) ->
true = code:add_patha(CaseDir),
Dog = case Func of
- testX420 -> test_server:timetrap({minutes, 90});
- _ -> test_server:timetrap({minutes, 60})
+ testX420 -> ct:timetrap({minutes, 90});
+ _ -> ct:timetrap({minutes, 60})
end,
[{case_dir, CaseDir}, {watchdog, Dog}|Config].
end_per_testcase(_Func, Config) ->
- true = code:del_path(?config(case_dir, Config)),
- test_server:timetrap_cancel(?config(watchdog, Config)).
+ code:del_path(?config(case_dir, Config)).
%%------------------------------------------------------------------------------
%% Test runners
@@ -257,14 +233,8 @@ end_per_testcase(_Func, Config) ->
test(Config, TestF) ->
test(Config, TestF, [per,
- per_bin,
- {per_bin, [optimize]},
- uper_bin,
- ber,
- ber_bin,
- ber_bin_v2,
- % TODO: {ber_bin_v2, [optimize, nif]} ?
- {ber_bin_v2, [nif]}]).
+ uper,
+ ber]).
test(Config, TestF, Rules) ->
Fun = fun(C, R, O) ->
@@ -342,12 +312,12 @@ testCompactBitString(Config, Rule, Opts) ->
asn1_test_lib:compile("PrimStrings", Config,
[Rule, compact_bit_string|Opts]),
testCompactBitString:compact_bit_string(Rule),
- ?only_uper(testCompactBitString:bit_string_unnamed(Rule)),
- ?only_per(testCompactBitString:bit_string_unnamed(Rule)),
- ?only_per_nif(testCompactBitString:ticket_7734(Rule)),
- ?only_per_nif(asn1_test_lib:compile("Constraints", Config,
- [Rule, compact_bit_string|Opts])),
- ?only_per_nif(testCompactBitString:otp_4869(Rule)).
+ testCompactBitString:bit_string_unnamed(Rule),
+ testCompactBitString:bit_string_unnamed(Rule),
+ testCompactBitString:ticket_7734(Rule),
+ asn1_test_lib:compile("Constraints", Config,
+ [Rule, compact_bit_string|Opts]),
+ testCompactBitString:otp_4869(Rule).
testPrimStrings(Config) -> test(Config, fun testPrimStrings/3).
testPrimStrings(Config, Rule, Opts) ->
@@ -371,10 +341,10 @@ testPrimExternal(Config, Rule, Opts) ->
asn1_test_lib:compile_all(["External", "PrimExternal"], Config,
[Rule|Opts]),
testPrimExternal:external(Rule),
- ?only_ber_nif(asn1_test_lib:compile_all(["PrimStrings", "BitStr"], Config,
+ ?only_ber(asn1_test_lib:compile_all(["PrimStrings", "BitStr"], Config,
[Rule|Opts])),
- ?only_ber_nif(testPrimStrings_cases(Rule)),
- ?only_ber_nif(testPrimStrings:more_strings(Rule)).
+ ?only_ber(testPrimStrings_cases(Rule)),
+ ?only_ber(testPrimStrings:more_strings(Rule)).
testChoPrim(Config) -> test(Config, fun testChoPrim/3).
testChoPrim(Config, Rule, Opts) ->
@@ -399,7 +369,7 @@ testChoOptional(Config, Rule, Opts) ->
testChoOptionalImplicitTag(Config) ->
test(Config, fun testChoOptionalImplicitTag/3,
- [ber, ber_bin, ber_bin_v2]).
+ [ber]).
testChoOptionalImplicitTag(Config, Rule, Opts) ->
%% Only meaningful for ber & co
asn1_test_lib:compile("ChoOptionalImplicitTag", Config, [Rule|Opts]),
@@ -430,6 +400,11 @@ testChoTypeRefSet(Config, Rule, Opts) ->
asn1_test_lib:compile("ChoTypeRefSet", Config, [Rule|Opts]),
testChoTypeRefSet:set(Rule).
+testMultipleLevels(Config) -> test(Config, fun testMultipleLevels/3).
+testMultipleLevels(Config, Rule, Opts) ->
+ asn1_test_lib:compile("MultipleLevels", Config, [Rule|Opts]),
+ testMultipleLevels:main(Rule).
+
testDef(Config) -> test(Config, fun testDef/3).
testDef(Config, Rule, Opts) ->
asn1_test_lib:compile("Def", Config, [Rule|Opts]),
@@ -455,7 +430,8 @@ testSeqExtension(Config) -> test(Config, fun testSeqExtension/3).
testSeqExtension(Config, Rule, Opts) ->
asn1_test_lib:compile_all(["External", "SeqExtension"], Config,
[Rule|Opts]),
- testSeqExtension:main(Rule).
+ DataDir = ?config(data_dir, Config),
+ testSeqExtension:main(DataDir, [Rule|Opts]).
testSeqExternal(Config) -> test(Config, fun testSeqExternal/3).
testSeqExternal(Config, Rule, Opts) ->
@@ -515,8 +491,7 @@ testSeqOfCho(Config, Rule, Opts) ->
testSeqOfCho:main(Rule).
testSeqOfIndefinite(Config) ->
- test(Config, fun testSeqOfIndefinite/3,
- [ber, ber_bin, ber_bin_v2, {ber_bin_v2, [nif]}]).
+ test(Config, fun testSeqOfIndefinite/3, [ber]).
testSeqOfIndefinite(Config, Rule, Opts) ->
Files = ["Mvrasn-Constants-1", "Mvrasn-DataTypes-1", "Mvrasn-21-4",
"Mvrasn-20-4", "Mvrasn-19-4", "Mvrasn-18-4", "Mvrasn-17-4",
@@ -632,29 +607,57 @@ c_syntax(Config) ->
"SeqBadComma"]].
c_string(Config) ->
- test(Config, fun c_string/3, [per, per_bin, ber, ber_bin, ber_bin_v2]).
+ test(Config, fun c_string/3, [per, ber]).
c_string(Config, Rule, Opts) ->
asn1_test_lib:compile("String", Config, [Rule|Opts]).
c_implicit_before_choice(Config) ->
- test(Config, fun c_implicit_before_choice/3,
- [ber, ber_bin, ber_bin_v2]).
+ test(Config, fun c_implicit_before_choice/3, [ber]).
c_implicit_before_choice(Config, Rule, Opts) ->
DataDir = ?config(data_dir, Config),
CaseDir = ?config(case_dir, Config),
{error, _R2} = asn1ct:compile(filename:join(DataDir, "CCSNARG3"),
[Rule, {outdir, CaseDir}|Opts]).
+constraint_equivalence(Config) ->
+ DataDir = ?config(data_dir, Config),
+ CaseDir = ?config(case_dir, Config),
+ Asn1Spec = "ConstraintEquivalence",
+ Asn1Src = filename:join(DataDir, Asn1Spec),
+ ok = asn1ct:compile(Asn1Src, [abs,{outdir,CaseDir}]),
+ AbsFile = filename:join(CaseDir, Asn1Spec++".abs"),
+ {ok,Terms} = file:consult(AbsFile),
+ Cs = [begin
+ 'INTEGER' = element(3, Type), %Assertion.
+ Constraints = element(4, Type),
+ Name1 = atom_to_list(Name0),
+ {Name,_} = lists:splitwith(fun(C) -> C =/= $X end, Name1),
+ {Name,Constraints}
+ end || {typedef,_,_,Name0,Type} <- Terms],
+ R = sofs:relation(Cs, [{name,constraint}]),
+ F0 = sofs:relation_to_family(R),
+ F = sofs:to_external(F0),
+ Diff = [E || {_,L}=E <- F, length(L) > 1],
+ case Diff of
+ [] ->
+ ok;
+ [_|_] ->
+ io:put_chars("Not equivalent:\n"),
+ [io:format("~s: ~p\n", [N,D]) || {N,D} <- Diff],
+ test_server:fail(length(Diff))
+ end.
+
parse(Config) ->
[asn1_test_lib:compile(M, Config, [abs]) || M <- test_modules()].
per(Config) ->
- test(Config, fun per/3, [per, per_bin, {per_bin, [optimize]}]).
+ test(Config, fun per/3, [per,uper]).
per(Config, Rule, Opts) ->
[module_test(M, Config, Rule, Opts) || M <- per_modules()].
ber_other(Config) ->
- test(Config, fun ber_other/3, [ber, ber_bin, ber_bin_v2]).
+ test(Config, fun ber_other/3, [ber]).
+
ber_other(Config, Rule, Opts) ->
[module_test(M, Config, Rule, Opts) || M <- ber_modules()].
@@ -669,12 +672,12 @@ module_test(M, Config, Rule, Opts) ->
ber_choiceinseq(Config) ->
- test(Config, fun ber_choiceinseq/3, [ber, ber_bin, ber_bin_v2]).
+ test(Config, fun ber_choiceinseq/3, [ber]).
ber_choiceinseq(Config, Rule, Opts) ->
asn1_test_lib:compile("ChoiceInSeq", Config, [Rule|Opts]).
ber_optional(Config) ->
- test(Config, fun ber_optional/3, [ber, ber_bin, ber_bin_v2]).
+ test(Config, fun ber_optional/3, [ber]).
ber_optional(Config, Rule, Opts) ->
asn1_test_lib:compile("SOpttest", Config, [Rule|Opts]),
V = {'S', {'A', 10, asn1_NOVALUE, asn1_NOVALUE},
@@ -685,21 +688,6 @@ ber_optional(Config, Rule, Opts) ->
V2 = asn1_wrapper:decode('SOpttest', 'S', Bytes),
V = element(2, V2).
-ber_optional_keyed_list(Config) ->
- test(Config, fun ber_optional_keyed_list/3, [ber, ber_bin]).
-ber_optional_keyed_list(Config, Rule, Opts) ->
- asn1_test_lib:compile("SOpttest", Config, [Rule, keyed_list|Opts]),
- Vrecord = {'S', {'A', 10, asn1_NOVALUE, asn1_NOVALUE},
- {'B', asn1_NOVALUE, asn1_NOVALUE, asn1_NOVALUE},
- {'C', asn1_NOVALUE, 111, asn1_NOVALUE}},
- V = [{a, [{scriptKey, 10}]},
- {b, []},
- {c, [{callingPartysCategory, 111}]}],
- {ok, B} = asn1_wrapper:encode('SOpttest', 'S', V),
- Bytes = lists:flatten(B),
- V2 = asn1_wrapper:decode('SOpttest', 'S', Bytes),
- Vrecord = element(2, V2).
-
%% records used by test-case default
-record('Def1', {bool0,
bool1 = asn1_DEFAULT,
@@ -734,7 +722,7 @@ value_bad_enum_test(Config) ->
end.
constructed(Config) ->
- test(Config, fun constructed/3, [ber, ber_bin, ber_bin_v2]).
+ test(Config, fun constructed/3, [ber]).
constructed(Config, Rule, Opts) ->
asn1_test_lib:compile("Constructed", Config, [Rule|Opts]),
{ok, B} = asn1_wrapper:encode('Constructed', 'S', {'S', false}),
@@ -745,7 +733,7 @@ constructed(Config, Rule, Opts) ->
[136, 1, 10] = lists:flatten(B2).
ber_decode_error(Config) ->
- test(Config, fun ber_decode_error/3, [ber, ber_bin, ber_bin_v2]).
+ test(Config, fun ber_decode_error/3, [ber]).
ber_decode_error(Config, Rule, Opts) ->
asn1_test_lib:compile("Constructed", Config, [Rule|Opts]),
ber_decode_error:run(Opts).
@@ -758,14 +746,14 @@ h323test(Config, Rule, Opts) ->
h323test:run(Rule).
per_GeneralString(Config) ->
- test(Config, fun per_GeneralString/3, [per, per_bin]).
+ test(Config, fun per_GeneralString/3, [per]).
per_GeneralString(Config, Rule, Opts) ->
asn1_test_lib:compile("MULTIMEDIA-SYSTEM-CONTROL", Config, [Rule|Opts]),
UI = [109, 64, 1, 57],
{ok, _V} = asn1_wrapper:decode('MULTIMEDIA-SYSTEM-CONTROL',
'MultimediaSystemControlMessage', UI).
-per_open_type(Config) -> test(Config, fun per_open_type/3, [per, per_bin]).
+per_open_type(Config) -> test(Config, fun per_open_type/3, [per]).
per_open_type(Config, Rule, Opts) ->
asn1_test_lib:compile("OpenType", Config, [Rule|Opts]),
{ok, _} = asn1ct:test('OpenType', 'Ot', {'Stype', 10, true}).
@@ -773,28 +761,29 @@ per_open_type(Config, Rule, Opts) ->
testConstraints(Config) -> test(Config, fun testConstraints/3).
testConstraints(Config, Rule, Opts) ->
asn1_test_lib:compile("Constraints", Config, [Rule|Opts]),
+ asn1_test_lib:compile("LargeConstraints", Config, [Rule|Opts]),
testConstraints:int_constraints(Rule).
testSeqIndefinite(Config) ->
- test(Config, fun testSeqIndefinite/3, [ber, ber_bin, ber_bin_v2,
- {ber_bin_v2, [nif]}]).
+ test(Config, fun testSeqIndefinite/3, [ber]).
+
testSeqIndefinite(Config, Rule, Opts) ->
asn1_test_lib:compile("SeqSetIndefinite", Config, [Rule|Opts]),
testSeqIndefinite:main(Rule).
testSetIndefinite(Config) ->
- test(Config, fun testSetIndefinite/3, [ber, ber_bin, ber_bin_v2,
- {ber_bin_v2, [nif]}]).
+ test(Config, fun testSetIndefinite/3, [ber]).
+
testSetIndefinite(Config, Rule, Opts) ->
asn1_test_lib:compile("SeqSetIndefinite", Config, [Rule|Opts]),
testSetIndefinite:main(Rule).
testChoiceIndefinite(Config) ->
- test(Config, fun testChoiceIndefinite/3, [ber, ber_bin, ber_bin_v2,
- {ber_bin_v2, [nif]}]).
+ test(Config, fun testChoiceIndefinite/3, [ber]).
+
testChoiceIndefinite(Config, Rule, Opts) ->
asn1_test_lib:compile("ChoiceIndef", Config, [Rule|Opts]),
testChoiceIndefinite:main(Rule).
@@ -856,7 +845,7 @@ testExport(Config) ->
end.
testImport(Config) ->
- test(Config, fun testImport/3, [ber, ber_bin, ber_bin_v2]).
+ test(Config, fun testImport/3, [ber]).
testImport(Config, Rule, Opts) ->
{error, _} = asn1ct:compile(filename:join(?config(data_dir, Config),
"ImportsFrom"),
@@ -905,8 +894,7 @@ duplicate_tags(Config) ->
{skip, "Runs in asn1_SUITE only"}
end.
-rtUI(Config) -> test(Config, fun rtUI/3, [per, per_bin, ber,
- ber_bin, ber_bin_v2]).
+rtUI(Config) -> test(Config, fun rtUI/3, [per,ber]).
rtUI(Config, Rule, Opts) ->
asn1_test_lib:compile("Prim", Config, [Rule|Opts]),
{ok, _} = asn1rt:info('Prim').
@@ -922,19 +910,19 @@ testINSTANCE_OF(Config, Rule, Opts) ->
testINSTANCE_OF:main(Rule).
testTCAP(Config) ->
- test(Config, fun testTCAP/3,
- [ber, ber_bin, ber_bin_v2, {ber_bin_v2, [nif]}]).
+ test(Config, fun testTCAP/3, [ber]).
testTCAP(Config, Rule, Opts) ->
testTCAP:compile(Config, [Rule|Opts]),
testTCAP:test(Rule, Config),
case Rule of
- ber_bin_v2 -> testTCAP:compile_asn1config(Config, [Rule, asn1config]),
- testTCAP:test_asn1config();
- _ -> ok
+ ber ->
+ testTCAP:compile_asn1config(Config, [Rule, asn1config]),
+ testTCAP:test_asn1config();
+ _ -> ok
end.
testDER(Config) ->
- test(Config, fun testDER/3, [ber, ber_bin, ber_bin_v2]).
+ test(Config, fun testDER/3, [ber]).
testDER(Config, Rule, Opts) ->
asn1_test_lib:compile("DERSpec", Config, [Rule, der|Opts]),
testDER:test(),
@@ -944,7 +932,7 @@ testDER(Config, Rule, Opts) ->
testSeqSetDefaultVal:main(Rule).
specialized_decodes(Config) ->
- test(Config, fun specialized_decodes/3, [ber_bin_v2]).
+ test(Config, fun specialized_decodes/3, [ber]).
specialized_decodes(Config, Rule, Opts) ->
asn1_test_lib:compile_all(["PartialDecSeq.asn",
"PartialDecSeq2.asn",
@@ -952,13 +940,12 @@ specialized_decodes(Config, Rule, Opts) ->
"PartialDecMyHTTP.asn",
"MEDIA-GATEWAY-CONTROL.asn",
"P-Record"],
- Config, [Rule, optimize, asn1config|Opts]),
+ Config, [Rule, asn1config|Opts]),
test_partial_incomplete_decode:test(Config),
test_selective_decode:test().
special_decode_performance(Config) ->
- test(Config, fun special_decode_performance/3,
- [{ber_bin, [optimize]}, {ber_bin_v2, [optimize, nif]}]).
+ test(Config, fun special_decode_performance/3, [ber]).
special_decode_performance(Config, Rule, Opts) ->
Files = ["MEDIA-GATEWAY-CONTROL", "PartialDecSeq"],
asn1_test_lib:compile_all(Files, Config, [Rule, asn1config|Opts]),
@@ -966,19 +953,19 @@ special_decode_performance(Config, Rule, Opts) ->
test_driver_load(Config) ->
- test(Config, fun test_driver_load/3, [{per_bin, [optimize]}]).
+ test(Config, fun test_driver_load/3, [per]).
test_driver_load(Config, Rule, Opts) ->
asn1_test_lib:compile("P-Record", Config, [Rule|Opts]),
test_driver_load:test(5).
test_ParamTypeInfObj(Config) ->
- asn1_test_lib:compile("IN-CS-1-Datatypes", Config, [ber_bin]).
+ asn1_test_lib:compile("IN-CS-1-Datatypes", Config, [ber]).
test_WS_ParamClass(Config) ->
- asn1_test_lib:compile("InformationFramework", Config, [ber_bin]).
+ asn1_test_lib:compile("InformationFramework", Config, [ber]).
test_Defed_ObjectIdentifier(Config) ->
- asn1_test_lib:compile("UsefulDefinitions", Config, [ber_bin]).
+ asn1_test_lib:compile("UsefulDefinitions", Config, [ber]).
testSelectionType(Config) -> test(Config, fun testSelectionType/3).
testSelectionType(Config, Rule, Opts) ->
@@ -986,7 +973,7 @@ testSelectionType(Config, Rule, Opts) ->
{ok, _} = testSelectionTypes:test().
testSSLspecs(Config) ->
- test(Config, fun testSSLspecs/3, [ber, ber_bin, ber_bin_v2, {ber_bin_v2, [optimize]}]).
+ test(Config, fun testSSLspecs/3, [ber]).
testSSLspecs(Config, Rule, Opts) ->
ok = testSSLspecs:compile(Config,
[Rule, compact_bit_string, der|Opts]),
@@ -1010,12 +997,12 @@ test_undecoded_rest(Config, Rule, Opts) ->
ok = test_undecoded_rest:test([], Config),
asn1_test_lib:compile("P-Record", Config, [Rule,undec_rest|Opts]),
case Rule of
- ber_bin_v2 -> ok;
+ ber -> ok;
_ -> test_undecoded_rest:test(undec_rest, Config)
end.
test_inline(Config) ->
- test(Config, fun test_inline/3, [ber, ber_bin, ber_bin_v2]).
+ test(Config, fun test_inline/3, [ber]).
test_inline(Config, Rule, Opts) ->
case code:which(asn1ct) of
cover_compiled ->
@@ -1028,12 +1015,11 @@ test_inline(Config, Rule, Opts) ->
end.
testTcapsystem(Config) ->
- test(Config, fun testTcapsystem/3, [ber, ber_bin, ber_bin_v2]).
+ test(Config, fun testTcapsystem/3, [ber]).
testTcapsystem(Config, Rule, Opts) ->
testTcapsystem:compile(Config, [Rule|Opts]).
-testNBAPsystem(Config) -> test(Config, fun testNBAPsystem/3,
- [per, per_bin, {per_bin, [optimize]}]).
+testNBAPsystem(Config) -> test(Config, fun testNBAPsystem/3, [per]).
testNBAPsystem(Config, Rule, Opts) ->
testNBAPsystem:compile(Config, [Rule|Opts]),
testNBAPsystem:test(Rule, Config).
@@ -1063,21 +1049,23 @@ test_modified_x420(Config) ->
asn1_test_lib:compile_all(Files, Config, [der]),
test_modified_x420:test_io(Config).
+
+testX420() ->
+ [{timetrap,{minutes,90}}].
testX420(Config) ->
- test(Config, fun testX420/3, [ber, ber_bin, ber_bin_v2]).
+ test(Config, fun testX420/3, [ber]).
testX420(Config, Rule, Opts) ->
testX420:compile(Rule, [der|Opts], Config),
ok = testX420:ticket7759(Rule, Config),
testX420:compile(Rule, Opts, Config).
test_x691(Config) ->
- test(Config, fun test_x691/3,
- [per, per_bin, uper_bin, {per_bin, [optimize]}]).
+ test(Config, fun test_x691/3, [per, uper]).
test_x691(Config, Rule, Opts) ->
Files = ["P-RecordA1", "P-RecordA2", "P-RecordA3"],
asn1_test_lib:compile_all(Files, Config, [Rule|Opts]),
test_x691:cases(Rule, case Rule of
- uper_bin -> unaligned;
+ uper -> unaligned;
_ -> aligned
end),
asn1_test_lib:ticket_7708(Config, []),
@@ -1088,14 +1076,14 @@ ticket_6143(Config) ->
testExtensionAdditionGroup(Config) ->
%% FIXME problems with automatic tags [ber_bin], [ber_bin, optimize]
- test(Config, fun testExtensionAdditionGroup/3,
- [per_bin, {per_bin, [optimize]}, uper_bin]).
+ test(Config, fun testExtensionAdditionGroup/3, [per, uper]).
testExtensionAdditionGroup(Config, Rule, Opts) ->
asn1_test_lib:compile("Extension-Addition-Group", Config, [Rule|Opts]),
asn1_test_lib:compile_erlang("extensionAdditionGroup", Config,
[debug_info]),
extensionAdditionGroup:run([Rule|Opts]),
extensionAdditionGroup:run2([Rule|Opts]),
+ extensionAdditionGroup:run3(),
asn1_test_lib:compile("EUTRA-RRC-Definitions", Config, [Rule, {record_name_prefix, "RRC-"}|Opts]),
extensionAdditionGroup:run3([Rule|Opts]).
@@ -1178,46 +1166,17 @@ timer_compile(Config, Rule, Opts) ->
asn1_test_lib:compile_all(["H235-SECURITY-MESSAGES", "H323-MESSAGES"],
Config, [Rule|Opts]).
-testTimer_ber(suite) -> [];
testTimer_ber(Config) ->
timer_compile(Config,ber,[]),
testTimer:go(Config,ber).
-testTimer_ber_bin(suite) -> [];
-testTimer_ber_bin(Config) ->
- timer_compile(Config,ber_bin,[]),
- testTimer:go(Config,ber_bin).
-
-testTimer_ber_bin_opt(suite) -> [];
-testTimer_ber_bin_opt(Config) ->
- timer_compile(Config,ber_bin,[optimize]),
- testTimer:go(Config,ber_bin).
-
-testTimer_ber_bin_opt_driver(suite) -> [];
-testTimer_ber_bin_opt_driver(Config) ->
- timer_compile(Config,ber_bin,[optimize,driver]),
- testTimer:go(Config,ber_bin).
-
-testTimer_per(suite) -> [];
testTimer_per(Config) ->
timer_compile(Config,per,[]),
testTimer:go(Config,per).
-testTimer_per_bin(suite) -> [];
-testTimer_per_bin(Config) ->
- timer_compile(Config,per_bin,[]),
- testTimer:go(Config,per_bin).
-
-testTimer_per_bin_opt(suite) -> [];
-testTimer_per_bin_opt(Config) ->
- timer_compile(Config,per_bin,[optimize]),
- testTimer:go(Config,per_bin).
-
-
-testTimer_uper_bin(suite) -> [];
-testTimer_uper_bin(Config) ->
- timer_compile(Config,uper_bin,[]),
- {comment,_} = testTimer:go(Config,uper_bin).
+testTimer_uper(Config) ->
+ timer_compile(Config,uper,[]),
+ {comment,_} = testTimer:go(Config,uper).
%% Test of multiple-line comment, OTP-8043
testComment(suite) -> [];
@@ -1236,14 +1195,35 @@ testName2Number(Config) ->
0 = 'S1AP-IEs':name2num_CauseMisc('control-processing-overload'),
'unknown-PLMN' = 'S1AP-IEs':num2name_CauseMisc(5),
+
+ %% OTP-10144
+ %% Test that n2n option generates name2num and num2name functions supporting
+ %% values not within the extension root if the enumeration type has an
+ %% extension marker.
+ N2NOptionsExt = [{n2n, 'NoExt'}, {n2n, 'Ext'}, {n2n, 'Ext2'}],
+ asn1_test_lib:compile("EnumN2N", Config, N2NOptionsExt),
+ %% Previously, name2num and num2name was not generated if the type didn't
+ %% have an extension marker:
+ 0 = 'EnumN2N':name2num_NoExt('blue'),
+ 2 = 'EnumN2N':name2num_NoExt('green'),
+ blue = 'EnumN2N':num2name_NoExt(0),
+ green = 'EnumN2N':num2name_NoExt(2),
+
+ %% Test enumeration extension:
+ 7 = 'EnumN2N':name2num_Ext2('orange'),
+ orange = 'EnumN2N':num2name_Ext2(7),
+ %% 7 is not defined in Ext, only in Ext2.
+ {asn1_enum, 7} = 'EnumN2N':num2name_Ext(7),
+ 7 = 'EnumN2N':name2num_Ext({asn1_enum, 7}),
+ 42 = 'EnumN2N':name2num_Ext2({asn1_enum, 42}),
ok.
ticket_7407(Config) ->
- asn1_test_lib:compile("EUTRA-extract-7407", Config, [uper_bin]),
+ asn1_test_lib:compile("EUTRA-extract-7407", Config, [uper]),
asn1_test_lib:ticket_7407_code(true),
asn1_test_lib:compile("EUTRA-extract-7407", Config,
- [uper_bin, no_final_padding]),
+ [uper, no_final_padding]),
asn1_test_lib:ticket_7407_code(false).
smp(suite) -> [];
@@ -1254,7 +1234,7 @@ smp(Config) ->
io:format("smp starting ~p workers\n",[NumOfProcs]),
Msg = {initiatingMessage, testNBAPsystem:cell_setup_req_msg()},
- ok = testNBAPsystem:compile(Config, [per_bin, optimize]),
+ ok = testNBAPsystem:compile(Config, [per]),
enc_dec(NumOfProcs,Msg,2),
@@ -1263,7 +1243,7 @@ smp(Config) ->
{Time1,ok} = timer:tc(?MODULE,enc_dec,[NumOfProcs,Msg, N]),
{Time1S,ok} = timer:tc(?MODULE,enc_dec,[1, Msg, NumOfProcs * N]),
- ok = testNBAPsystem:compile(Config, [ber_bin, optimize, nif]),
+ ok = testNBAPsystem:compile(Config, [ber]),
{Time3,ok} = timer:tc(?MODULE,enc_dec,[NumOfProcs,Msg, N]),
{Time3S,ok} = timer:tc(?MODULE,enc_dec,[1, Msg, NumOfProcs * N]),
@@ -1284,10 +1264,8 @@ per_performance(Config) ->
file:make_dir(NifDir),file:make_dir(ErlDir),
Msg = {initiatingMessage, testNBAPsystem:cell_setup_req_msg()},
- ok = testNBAPsystem:compile([{priv_dir,NifDir}|Config],
- [per_bin, optimize]),
- ok = testNBAPsystem:compile([{priv_dir,ErlDir}|Config],
- [per_bin]),
+ ok = testNBAPsystem:compile([{priv_dir,NifDir}|Config], [per]),
+ ok = testNBAPsystem:compile([{priv_dir,ErlDir}|Config], [per]),
Modules = ['NBAP-CommonDataTypes',
'NBAP-Constants',
@@ -1325,7 +1303,7 @@ per_performance(Config) ->
ber_performance(Config) ->
Msg = {initiatingMessage, testNBAPsystem:cell_setup_req_msg()},
- ok = testNBAPsystem:compile(Config, [ber_bin, optimize, nif]),
+ ok = testNBAPsystem:compile(Config, [ber]),
BerFun = fun() ->
@@ -1499,7 +1477,7 @@ pforeach(_Fun,[],[]) ->
-record('Iu-ReleaseCommand',{first,second}).
ticket7904(Config) ->
- asn1_test_lib:compile("RANAPextract1", Config, [per_bin, optimize]),
+ asn1_test_lib:compile("RANAPextract1", Config, [per]),
Val1 = #'InitiatingMessage'{procedureCode=1,
criticality=ignore,
diff --git a/lib/asn1/test/asn1_SUITE_data/ConstraintEquivalence.asn1 b/lib/asn1/test/asn1_SUITE_data/ConstraintEquivalence.asn1
new file mode 100644
index 0000000000..6a97c1b38e
--- /dev/null
+++ b/lib/asn1/test/asn1_SUITE_data/ConstraintEquivalence.asn1
@@ -0,0 +1,42 @@
+ConstraintEquivalence DEFINITIONS AUTOMATIC TAGS ::=
+BEGIN
+ SingleValueX42 ::= INTEGER (42)
+ SingleValueX1 ::= INTEGER ((42) ^ (42))
+ SingleValueX2 ::= INTEGER ((42) INTERSECTION (42))
+ SingleValueX3 ::= INTEGER ((42) | (42))
+ SingleValueX4 ::= INTEGER ((42) UNION (42))
+ SingleValueX5 ::= INTEGER ((42) INTERSECTION (MIN..MAX))
+ SingleValueX6 ::= INTEGER ((42) INTERSECTION (40..49))
+ SingleValueX7 ::= INTEGER (42..42)
+
+ UnconstrainedX0 ::= INTEGER
+ UnconstrainedX1 ::= INTEGER (MIN..MAX)
+ UnconstrainedX2 ::= INTEGER (1|(MIN..MAX))
+ UnconstrainedX3 ::= INTEGER (1..10|(MIN..MAX))
+ UnconstrainedX4 ::= INTEGER ((MIN..MAX)|9|10)
+ UnconstrainedX5 ::= INTEGER ((MIN..MAX)|10..20)
+ UnconstrainedX6 ::= INTEGER ((MIN..MAX) UNION (10..20))
+
+ RangeX00 ::= INTEGER (5..10)
+ RangeX01 ::= INTEGER (4<..<11)
+ RangeX02 ::= INTEGER (5..<11)
+ RangeX03 ::= INTEGER (4<..10)
+ RangeX04 ::= INTEGER (5|6|7|8|9|10)
+ RangeX05 ::= INTEGER (10|9|8|7|6|5)
+ RangeX06 ::= INTEGER (5|6|7..10)
+
+ RangeX10 ::= INTEGER ((5..6) UNION (7..8) UNION (9|10))
+ RangeX11 ::= INTEGER ((5|6) UNION (7..8) UNION (9|10))
+ RangeX12 ::= INTEGER ((5|6) UNION (7|8) UNION (9|10))
+ RangeX13 ::= INTEGER ((5|6) UNION (7) UNION (8..10))
+ RangeX14 ::= INTEGER ((5|6) UNION (7) UNION (8..10))
+ RangeX15 ::= INTEGER ((5|6) UNION (7) UNION ((8..8)|(9..9)|(10)))
+ RangeX16 ::= INTEGER ((5|6) UNION (7) UNION (7<..<11))
+
+ RangeX20 ::= INTEGER (0..20) (5..10)
+ RangeX21 ::= INTEGER (0..10) (5..20)
+ RangeX22 ::= INTEGER (0..10) (5..20) (MIN..MAX)
+ RangeX23 ::= INTEGER ((0..10) INTERSECTION (5..20) ^ (MIN..MAX))
+ RangeX24 ::= INTEGER ((5|6|7|8|9|10) INTERSECTION (5..20) ^ (MIN..MAX))
+
+END
diff --git a/lib/asn1/test/asn1_SUITE_data/Constraints.py b/lib/asn1/test/asn1_SUITE_data/Constraints.py
index de48c4c2ca..87243121f7 100644
--- a/lib/asn1/test/asn1_SUITE_data/Constraints.py
+++ b/lib/asn1/test/asn1_SUITE_data/Constraints.py
@@ -4,9 +4,14 @@ BEGIN
-- Single Value
SingleValue ::= INTEGER (1)
SingleValue2 ::= INTEGER (1..20)
+predefined INTEGER ::= 1
+SingleValue3 ::= INTEGER (predefined | 5 | 10)
Range2to19 ::= INTEGER (1<..<20)
Range10to20 ::= INTEGER (10..20)
ContainedSubtype ::= INTEGER (INCLUDES Range10to20)
+-- Some ranges for additional constrained number testing.
+LongLong ::= INTEGER (0..18446744073709551615)
+Range256to65536 ::= INTEGER (256..65536)
FixedSize ::= OCTET STRING (SIZE(10))
FixedSize2 ::= OCTET STRING (SIZE(10|20))
VariableSize ::= OCTET STRING (SIZE(1..10))
diff --git a/lib/asn1/test/asn1_SUITE_data/EnumN2N.asn1 b/lib/asn1/test/asn1_SUITE_data/EnumN2N.asn1
new file mode 100644
index 0000000000..a724f2f3f5
--- /dev/null
+++ b/lib/asn1/test/asn1_SUITE_data/EnumN2N.asn1
@@ -0,0 +1,25 @@
+EnumN2N DEFINITIONS AUTOMATIC TAGS ::=
+BEGIN
+
+NoExt ::= ENUMERATED {
+ blue(0),
+ red(1),
+ green(2)
+}
+
+Ext ::= ENUMERATED {
+ blue(0),
+ red(1),
+ green(2),
+ ...
+}
+
+Ext2 ::= ENUMERATED {
+ blue(0),
+ red(1),
+ green(2),
+ ...,
+ orange(7)
+}
+
+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
index cacef8b922..b7cc74ab07 100644
--- a/lib/asn1/test/asn1_SUITE_data/Extension-Addition-Group.asn
+++ b/lib/asn1/test/asn1_SUITE_data/Extension-Addition-Group.asn
@@ -1,7 +1,7 @@
--
-- %CopyrightBegin%
--
--- Copyright Ericsson AB 2001-2010. All Rights Reserved.
+-- Copyright Ericsson AB 2001-2012. All Rights Reserved.
--
-- The contents of this file are subject to the Erlang Public License,
-- Version 1.1, (the "License"); you may not use this file except in
@@ -95,6 +95,27 @@ AS-Config ::= SEQUENCE {
]]
}
+SystemInformationBlockType2 ::= SEQUENCE {
+ timeAlignmentTimerCommon TimeAlignmentTimer,
+ ...,
+ lateNonCriticalExtension OCTET STRING OPTIONAL,
+ [[ ssac-BarringForMMTEL-Voice-r9 AC-BarringConfig OPTIONAL,
+ ssac-BarringForMMTEL-Video-r9 AC-BarringConfig OPTIONAL
+ ]],
+ [[ ac-BarringForCSFB-r10 AC-BarringConfig OPTIONAL
+ ]]
+}
+
+TimeAlignmentTimer ::= ENUMERATED {
+ sf500, sf750, sf1280, sf1920, sf2560, sf5120,
+ sf10240, infinity}
+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))
+}
END
diff --git a/lib/asn1/test/asn1_SUITE_data/LargeConstraints.py b/lib/asn1/test/asn1_SUITE_data/LargeConstraints.py
new file mode 100644
index 0000000000..68c7616b62
--- /dev/null
+++ b/lib/asn1/test/asn1_SUITE_data/LargeConstraints.py
@@ -0,0 +1,9 @@
+LargeConstraints DEFINITIONS ::=
+BEGIN
+
+-- Maximum number that can be encoded as a constrained whole number: 1 bsl (255*8)
+-- The number of octets needed to represent a number cannot be more than 255
+-- As the length field is encoded as a 8-bit bitfield.
+RangeMax ::= INTEGER (1..126238304966058622268417487065116999845484776053576109500509161826268184136202698801551568013761380717534054534851164138648904527931605160527688095259563605939964364716019515983399209962459578542172100149937763938581219604072733422507180056009672540900709554109516816573779593326332288314873251559077853068444977864803391962580800682760017849589281937637993445539366428356761821065267423102149447628375691862210717202025241630303118559188678304314076943801692528246980959705901641444238894928620825482303431806955690226308773426829503900930529395181208739591967195841536053143145775307050594328881077553168201547776)
+
+END
diff --git a/lib/asn1/test/asn1_SUITE_data/MultipleLevels.asn b/lib/asn1/test/asn1_SUITE_data/MultipleLevels.asn
new file mode 100644
index 0000000000..1824e1fa5a
--- /dev/null
+++ b/lib/asn1/test/asn1_SUITE_data/MultipleLevels.asn
@@ -0,0 +1,19 @@
+MultipleLevels DEFINITIONS AUTOMATIC TAGS ::=
+
+BEGIN
+
+Top ::= SEQUENCE {
+ country CountryName,
+ region Name
+}
+
+CountryName ::= CountryName0
+
+CountryName0 ::= Name
+
+Name ::= CHOICE {
+ short PrintableString (SIZE (0..10)),
+ long PrintableString
+}
+
+END
diff --git a/lib/asn1/test/asn1_SUITE_data/PartialDecSeq.asn1config b/lib/asn1/test/asn1_SUITE_data/PartialDecSeq.asn1config
index 19fa3c990e..d388f6cd02 100644
--- a/lib/asn1/test/asn1_SUITE_data/PartialDecSeq.asn1config
+++ b/lib/asn1/test/asn1_SUITE_data/PartialDecSeq.asn1config
@@ -17,6 +17,6 @@
{decode_D_incomplete,['D',[{a,undecoded}]]},
{decode_F_fb_exclusive2,['F',[{fb,[{b,parts},{d,[{da,parts}]}]}]]}, {decode_F_fb_exclusive3,['F',[{fb,[{b,parts},{d,[{da,parts},{dc,[{dcc,undecoded}]}]}]}]]}]}}.
{module_name,'Seq.asn1'}.
-{compile_options,[ber_bin,optimize,debug_info]}.
+{compile_options,[ber,debug_info]}.
{multifile_compile,['M1.asn','M2.asn']}.
diff --git a/lib/asn1/test/asn1_SUITE_data/PrimStrings.asn1 b/lib/asn1/test/asn1_SUITE_data/PrimStrings.asn1
index d287840f30..9b6b34a776 100644
--- a/lib/asn1/test/asn1_SUITE_data/PrimStrings.asn1
+++ b/lib/asn1/test/asn1_SUITE_data/PrimStrings.asn1
@@ -55,6 +55,32 @@ BS1024 ::= BIT STRING (SIZE (1024))
OsExpCon ::= [60] EXPLICIT OCTET STRING
OsExpPri ::= [PRIVATE 61] EXPLICIT OCTET STRING
OsExpApp ::= [APPLICATION 62] EXPLICIT OCTET STRING
+ OsFrag ::= OCTET STRING (SIZE (0..100000))
+ FixedOs65536 ::= OCTET STRING (SIZE (65536))
+ FixedOs65537 ::= OCTET STRING (SIZE (65537))
+
+ OsFixedStrings ::= SEQUENCE {
+ b1 BOOLEAN, -- Unalign
+ s0 OCTET STRING (SIZE (0)),
+ s1 OCTET STRING (SIZE (1)),
+ s2 OCTET STRING (SIZE (2)),
+ s3 OCTET STRING (SIZE (3)),
+ b2 BOOLEAN, -- Unalign
+ s255 OCTET STRING (SIZE (255)),
+ s256 OCTET STRING (SIZE (256)),
+ s257 OCTET STRING (SIZE (257)),
+ i INTEGER (0..1024)
+ }
+
+ OsAlignment ::= SEQUENCE {
+ b1 BOOLEAN,
+ s1 Os,
+ b2 BOOLEAN,
+ s2 OsFrag,
+ b3 BOOLEAN,
+ s3 FixedOs65536,
+ i INTEGER (0..63)
+ }
Ns ::= NumericString
NsCon ::= [70] NumericString
diff --git a/lib/asn1/test/asn1_SUITE_data/SeqExtension.asn1 b/lib/asn1/test/asn1_SUITE_data/SeqExtension.asn1
index bb0a7cca3a..5fda19303a 100644
--- a/lib/asn1/test/asn1_SUITE_data/SeqExtension.asn1
+++ b/lib/asn1/test/asn1_SUITE_data/SeqExtension.asn1
@@ -31,7 +31,35 @@ SeqExt4 ::= SEQUENCE
int INTEGER
}
+SeqExt5 ::= SEQUENCE
+{
+ ...,
+ [[ name OCTET STRING (SIZE (1..8)),
+ shoesize INTEGER ]]
+}
+
+SeqExt6 ::= SEQUENCE
+{
+ -- The spaces between the ellipsis and the comma will prevent them
+ -- from being removed.
+ ... ,
+ [[ i1 [100] INTEGER, i2 [101] INTEGER, i3 [102] INTEGER ]],
+ [[ i4 [104] INTEGER, i5 [105] INTEGER ]],
+ [[ i6 [106] INTEGER, i7 [107] INTEGER ]]
+}
+
SeqExt1X ::= XSeqExt1
SeqExt2X ::= XSeqExt2
+SuperSeq ::= SEQUENCE
+{
+ s1 SeqExt1,
+ s2 SeqExt2,
+ s3 SeqExt3,
+ s4 SeqExt4,
+ s5 SeqExt5,
+ s6 SeqExt6,
+ i INTEGER
+}
+
END
diff --git a/lib/asn1/test/asn1_SUITE_data/extensionAdditionGroup.erl b/lib/asn1/test/asn1_SUITE_data/extensionAdditionGroup.erl
index 5fcec23756..8148381d92 100644
--- a/lib/asn1/test/asn1_SUITE_data/extensionAdditionGroup.erl
+++ b/lib/asn1/test/asn1_SUITE_data/extensionAdditionGroup.erl
@@ -130,3 +130,26 @@ run3(Erule) ->
_ -> exit({expected,Val, got, Val2})
end.
+run3() ->
+ SI = #'SystemInformationBlockType2'{
+ timeAlignmentTimerCommon = sf500,
+ lateNonCriticalExtension = asn1_NOVALUE,
+ 'ssac-BarringForMMTEL-Voice-r9' = asn1_NOVALUE,
+ 'ssac-BarringForMMTEL-Video-r9' = asn1_NOVALUE,
+ 'ac-BarringForCSFB-r10' = asn1_NOVALUE},
+ Barring = #'AC-BarringConfig'{
+ 'ac-BarringFactor' = p00,
+ 'ac-BarringTime' = s4,
+ 'ac-BarringForSpecialAC' = [0,0,0,0,0]},
+ roundtrip(SI),
+ roundtrip(SI#'SystemInformationBlockType2'{
+ 'ssac-BarringForMMTEL-Voice-r9'=Barring}),
+ roundtrip(SI#'SystemInformationBlockType2'{
+ 'ssac-BarringForMMTEL-Video-r9'=Barring}),
+ roundtrip(SI#'SystemInformationBlockType2'{
+ 'ac-BarringForCSFB-r10'=Barring}).
+
+roundtrip(V) ->
+ Mod = 'Extension-Addition-Group',
+ {ok,E} = Mod:encode('SystemInformationBlockType2', V),
+ {ok,V} = Mod:decode('SystemInformationBlockType2', iolist_to_binary(E)).
diff --git a/lib/asn1/test/asn1_SUITE_data/testobj.erl b/lib/asn1/test/asn1_SUITE_data/testobj.erl
index be7ceee7d1..d9f60ca8a3 100644
--- a/lib/asn1/test/asn1_SUITE_data/testobj.erl
+++ b/lib/asn1/test/asn1_SUITE_data/testobj.erl
@@ -883,7 +883,7 @@ initial_ue_ies() ->
cn_domain_indicator() ->
- {'CN-DomainIndicator', 'ps-domain'}.
+ 'ps-domain'.
init_lai() ->
#'ProtocolIE-Field'{
@@ -1279,11 +1279,11 @@ reset() ->
protocolIEs = reset_ies()
}.
reset_ies() ->
- {'Reset_protocolIEs', % this identifier is very unneccesary here
- [reset_cause(),
- cn_domain_ind(), % Se initial Ue
- init_global_rnc_id() % ---- " ----
- ]}.
+ [reset_cause(),
+ cn_domain_ind(), % Se initial Ue
+ init_global_rnc_id() % ---- " ----
+ ].
+
init_global_rnc_id() ->
#'ProtocolIE-Field'{
id = 86, % 86 = id-GlobalRNC-ID
@@ -1323,8 +1323,7 @@ reset_ack() ->
protocolIEs = reset_ack_ies()
}.
reset_ack_ies() ->
- {'ResetAcknowledge_protocolIEs', % very unneccesary
- [cn_domain_ind()]}. % Se initial Ue
+ [cn_domain_ind()]. % Se initial Ue
@@ -1336,13 +1335,12 @@ reset_res(IuSCId) ->
}.
reset_res_ies(IuSCId) ->
- {'ResetResource_protocolIEs', % very unneccesary
- [
- cn_domain_ind() % Se initial Ue
- ,reset_cause() % Se reset
- ,reset_res_list(IuSCId)
- ,init_global_rnc_id_reset_res() % ---- " ----
- ]}.
+ [
+ cn_domain_ind() % Se initial Ue
+ ,reset_cause() % Se reset
+ ,reset_res_list(IuSCId)
+ ,init_global_rnc_id_reset_res() % ---- " ----
+ ].
init_global_rnc_id_reset_res() ->
#'ProtocolIE-Field'{
@@ -1420,24 +1418,7 @@ wrapper_encode(Module,Type,Value) ->
Error
end.
-wrapper_decode(Module,Type,Bytes) ->
- case Module:encoding_rule() of
- ber ->
- asn1rt:decode(Module,Type,Bytes);
- ber_bin when binary(Bytes) ->
- asn1rt:decode(Module,Type,Bytes);
- ber_bin ->
- asn1rt:decode(Module,Type,list_to_binary(Bytes));
- ber_bin_v2 when binary(Bytes) ->
- asn1rt:decode(Module,Type,Bytes);
- ber_bin_v2 ->
- asn1rt:decode(Module,Type,list_to_binary(Bytes));
- per ->
- asn1rt:decode(Module,Type,Bytes);
- per_bin when binary(Bytes) ->
- asn1rt:decode(Module,Type,Bytes);
- per_bin ->
- asn1rt:decode(Module,Type,list_to_binary(Bytes));
- uper_bin ->
- asn1rt:decode(Module,Type,list_to_binary(Bytes))
- end.
+wrapper_decode(Module, Type, Bytes) when is_binary(Bytes) ->
+ asn1rt:decode(Module, Type, Bytes);
+wrapper_decode(Module, Type, Bytes) when is_list(Bytes) ->
+ asn1rt:decode(Module, Type, list_to_binary(Bytes)).
diff --git a/lib/asn1/test/asn1_app_test.erl b/lib/asn1/test/asn1_app_test.erl
index 2c31c3259d..9dbe1b50e0 100644
--- a/lib/asn1/test/asn1_app_test.erl
+++ b/lib/asn1/test/asn1_app_test.erl
@@ -138,7 +138,8 @@ check_asn1ct_modules(Extra) ->
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,asn1ct_table],
+ asn1ct_tok,asn1ct_parser2,asn1ct_table,
+ asn1ct_imm],
case Extra -- ASN1CTMods of
[] ->
ok;
diff --git a/lib/asn1/test/asn1_test_lib.erl b/lib/asn1/test/asn1_test_lib.erl
index 96c04a9436..fda635d0eb 100644
--- a/lib/asn1/test/asn1_test_lib.erl
+++ b/lib/asn1/test/asn1_test_lib.erl
@@ -87,14 +87,14 @@ ticket_7407_compile(Config,Option) ->
?line OutDir = ?config(priv_dir,Config),
?line ok = asn1ct:compile(DataDir ++ "EUTRA-extract-7407",
- [uper_bin, {outdir,OutDir}]++Option).
+ [uper, {outdir,OutDir}]++Option).
ticket_7708(Config,Option) ->
?line DataDir = ?config(data_dir,Config),
?line OutDir = ?config(priv_dir,Config),
?line ok = asn1ct:compile(DataDir ++ "EUTRA-extract-55",
- [uper_bin, {outdir,OutDir}]++Option).
+ [uper, {outdir,OutDir}]++Option).
ticket_7407_code(FinalPadding) ->
@@ -154,7 +154,7 @@ ticket_7678(Config, Option) ->
?line OutDir = ?config(priv_dir,Config),
?line ok = asn1ct:compile(DataDir ++ "UPERDefault",
- [uper_bin, {outdir,OutDir}]++Option),
+ [uper, {outdir,OutDir}]++Option),
?line Val = 'UPERDefault':seq(),
?line {ok,<<0,6,0>>} = 'UPERDefault':encode('Seq',Val),
@@ -167,12 +167,12 @@ ticket_7763(Config) ->
?line OutDir = ?config(priv_dir,Config),
?line ok = asn1ct:compile(DataDir ++ "EUTRA-extract-55",
- [uper_bin, {outdir,OutDir}]),
+ [uper, {outdir,OutDir}]),
Val = {'Seq',15,lists:duplicate(8,0),[0],lists:duplicate(28,0),15,true},
?line {ok,Bin} = 'EUTRA-extract-55':encode('Seq',Val),
?line ok = asn1ct:compile(DataDir ++ "EUTRA-extract-55",
- [uper_bin,compact_bit_string,{outdir,OutDir}]),
+ [uper,compact_bit_string,{outdir,OutDir}]),
CompactVal = {'Seq',15,{0,<<0>>},{7,<<0>>},{4,<<0,0,0,0>>},15,true},
{ok,CompactBin} = 'EUTRA-extract-55':encode('Seq',CompactVal),
diff --git a/lib/asn1/test/asn1_wrapper.erl b/lib/asn1/test/asn1_wrapper.erl
index d515b99ac2..e764d8b4ca 100644
--- a/lib/asn1/test/asn1_wrapper.erl
+++ b/lib/asn1/test/asn1_wrapper.erl
@@ -34,41 +34,16 @@ encode(Module,Type,Value) ->
Error
end.
-decode(Module,Type,Bytes) ->
- case Module:encoding_rule() of
- ber ->
- asn1rt:decode(Module,Type,Bytes);
- ber_bin when is_binary(Bytes) ->
- asn1rt:decode(Module,Type,Bytes);
- ber_bin ->
- asn1rt:decode(Module,Type,list_to_binary(Bytes));
- ber_bin_v2 when is_binary(Bytes) ->
- asn1rt:decode(Module,Type,Bytes);
- ber_bin_v2 ->
- asn1rt:decode(Module,Type,list_to_binary(Bytes));
- per ->
- asn1rt:decode(Module,Type,Bytes);
- per_bin when is_binary(Bytes) ->
- asn1rt:decode(Module,Type,Bytes);
- per_bin ->
- asn1rt:decode(Module,Type,list_to_binary(Bytes));
- uper_bin when is_binary(Bytes) ->
- asn1rt:decode(Module,Type,Bytes);
- uper_bin ->
- asn1rt:decode(Module,Type,list_to_binary(Bytes))
- end.
+decode(Module, Type, Bytes) when is_binary(Bytes) ->
+ asn1rt:decode(Module, Type, Bytes);
+decode(Module, Type, Bytes) when is_list(Bytes) ->
+ asn1rt:decode(Module, Type, list_to_binary(Bytes)).
erule(ber) ->
ber;
-erule(ber_bin) ->
- ber;
-erule(ber_bin_v2) ->
- ber;
erule(per) ->
per;
-erule(per_bin) ->
- per;
-erule(uper_bin) ->
+erule(uper) ->
per.
diff --git a/lib/asn1/test/h323test.erl b/lib/asn1/test/h323test.erl
index b7a7d6e4df..426ae16994 100644
--- a/lib/asn1/test/h323test.erl
+++ b/lib/asn1/test/h323test.erl
@@ -22,7 +22,6 @@
-export([run/1]).
-include_lib("test_server/include/test_server.hrl").
-run(per_bin) -> run();
run(per) -> run();
run(_Rules) -> ok.
diff --git a/lib/asn1/test/testChoExtension.erl b/lib/asn1/test/testChoExtension.erl
index b75cfb6831..067d4d2bf7 100644
--- a/lib/asn1/test/testChoExtension.erl
+++ b/lib/asn1/test/testChoExtension.erl
@@ -25,42 +25,27 @@
extension(_Rules) ->
-
- ?line {ok,Bytes1} = asn1_wrapper:encode('ChoExtension','ChoExt1',{'ChoExt1',{bool,true}}),
- ?line {ok,{bool,true}} =
- asn1_wrapper:decode('ChoExtension','ChoExt1',lists:flatten(Bytes1)),
-
- ?line {ok,Bytes2} = asn1_wrapper:encode('ChoExtension','ChoExt1',{'ChoExt1',{int,33}}),
- ?line {ok,{int,33}} =
- asn1_wrapper:decode('ChoExtension','ChoExt1',lists:flatten(Bytes2)),
+ roundtrip('ChoExt1', {bool,true}),
+ roundtrip('ChoExt1', {int,33}),
%% A trick to encode with another compatible CHOICE type to test reception
%% extension alternative
- ?line {ok,Bytes2x} = asn1_wrapper:encode('ChoExtension','ChoExt1x',{str,"abc"}),
- ?line {ok,Val2x} =
+ {ok,Bytes2x} = asn1_wrapper:encode('ChoExtension','ChoExt1x',{str,"abc"}),
+ {ok,Val2x} =
asn1_wrapper:decode('ChoExtension','ChoExt1',lists:flatten(Bytes2x)),
io:format("Choice extension alternative = ~p~n",[Val2x]),
- ?line {ok,Bytes3} = asn1_wrapper:encode('ChoExtension','ChoExt2',{'ChoExt2',{bool,true}}),
- ?line {ok,{bool,true}} =
- asn1_wrapper:decode('ChoExtension','ChoExt2',lists:flatten(Bytes3)),
-
- ?line {ok,Bytes4} = asn1_wrapper:encode('ChoExtension','ChoExt2',{'ChoExt2',{int,33}}),
- ?line {ok,{int,33}} =
- asn1_wrapper:decode('ChoExtension','ChoExt2',lists:flatten(Bytes4)),
+ roundtrip('ChoExt2', {bool,true}),
+ roundtrip('ChoExt2', {int,33}),
+ roundtrip('ChoExt3', {bool,true}),
+ roundtrip('ChoExt3', {int,33}),
+ roundtrip('ChoExt4', {str,"abc"}),
- ?line {ok,Bytes5} = asn1_wrapper:encode('ChoExtension','ChoExt3',{'ChoExt3',{bool,true}}),
- ?line {ok,{bool,true}} =
- asn1_wrapper:decode('ChoExtension','ChoExt3',lists:flatten(Bytes5)),
-
- ?line {ok,Bytes6} = asn1_wrapper:encode('ChoExtension','ChoExt3',{'ChoExt3',{int,33}}),
- ?line {ok,{int,33}} =
- asn1_wrapper:decode('ChoExtension','ChoExt3',lists:flatten(Bytes6)),
-
- Val7 = {str,"abc"},
- ?line {ok,Bytes7} = asn1_wrapper:encode('ChoExtension','ChoExt4',Val7),
- ?line {ok,Val7} = asn1_wrapper:decode('ChoExtension','ChoExt4',lists:flatten(Bytes7)),
+ ok.
+roundtrip(Type, Value) ->
+ {ok,Encoded} = 'ChoExtension':encode(Type, Value),
+ {ok,Value} = 'ChoExtension':decode(Type, Encoded),
ok.
diff --git a/lib/asn1/test/testChoExternal.erl b/lib/asn1/test/testChoExternal.erl
index b2d171f9c7..5fdee48add 100644
--- a/lib/asn1/test/testChoExternal.erl
+++ b/lib/asn1/test/testChoExternal.erl
@@ -38,62 +38,27 @@ compile(Config, Rules, Optimize) ->
external(_Rules) ->
+ roundtrip('ChoXCho', {boolCho,true}),
+ roundtrip('ChoXCho', {intCho,77}),
- ?line {ok,Bytes11} = asn1_wrapper:encode('ChoExternal','ChoXCho',{'ChoXCho',{boolCho,true}}),
- ?line {ok,{boolCho,true}} = asn1_wrapper:decode('ChoExternal','ChoXCho',lists:flatten(Bytes11)),
-
-
- ?line {ok,Bytes12} = asn1_wrapper:encode('ChoExternal','ChoXCho',{'ChoXCho',{intCho,77}}),
- ?line {ok,{intCho,77}} = asn1_wrapper:decode('ChoExternal','ChoXCho',lists:flatten(Bytes12)),
-
-
-
- ?line {ok,Bytes21} = asn1_wrapper:encode('ChoExternal','ChoXBool',{'ChoXBool',{xbool,true}}),
- ?line {ok,{xbool,true}} = asn1_wrapper:decode('ChoExternal','ChoXBool',lists:flatten(Bytes21)),
-
-
- ?line {ok,Bytes22} = asn1_wrapper:encode('ChoExternal','ChoXBool',{'ChoXBool',{xboolImp,true}}),
- ?line {ok,{xboolImp,true}} = asn1_wrapper:decode('ChoExternal','ChoXBool',lists:flatten(Bytes22)),
-
-
- ?line {ok,Bytes23} = asn1_wrapper:encode('ChoExternal','ChoXBool',{'ChoXBool',{xboolExp,true}}),
- ?line {ok,{xboolExp,true}} = asn1_wrapper:decode('ChoExternal','ChoXBool',lists:flatten(Bytes23)),
-
-
-
- ?line {ok,Bytes31} = asn1_wrapper:encode('ChoExternal','NT',{os,"kalle"}),
- ?line {ok,{os,"kalle"}} = asn1_wrapper:decode('ChoExternal','NT',lists:flatten(Bytes31)),
-
- ?line {ok,Bytes32} = asn1_wrapper:encode('ChoExternal','Exp',{os,"kalle"}),
- ?line {ok,{os,"kalle"}} = asn1_wrapper:decode('ChoExternal','Exp',lists:flatten(Bytes32)),
-
- ?line {ok,Bytes33} = asn1_wrapper:encode('ChoExternal','NTNT',{os,"kalle"}),
- ?line {ok,{os,"kalle"}} = asn1_wrapper:decode('ChoExternal','NTNT',lists:flatten(Bytes33)),
-
- ?line {ok,Bytes34} = asn1_wrapper:encode('ChoExternal','NTExp',{os,"kalle"}),
- ?line {ok,{os,"kalle"}} = asn1_wrapper:decode('ChoExternal','NTExp',lists:flatten(Bytes34)),
-
- ?line {ok,Bytes35} = asn1_wrapper:encode('ChoExternal','ExpNT',{os,"kalle"}),
- ?line {ok,{os,"kalle"}} = asn1_wrapper:decode('ChoExternal','ExpNT',lists:flatten(Bytes35)),
-
- ?line {ok,Bytes36} = asn1_wrapper:encode('ChoExternal','ExpExp',{os,"kalle"}),
- ?line {ok,{os,"kalle"}} = asn1_wrapper:decode('ChoExternal','ExpExp',lists:flatten(Bytes36)),
-
-
-
-
-
- ?line {ok,Bytes41} = asn1_wrapper:encode('ChoExternal','XNTNT',{os,"kalle"}),
- ?line {ok,{os,"kalle"}} = asn1_wrapper:decode('ChoExternal','XNTNT',lists:flatten(Bytes41)),
-
- ?line {ok,Bytes42} = asn1_wrapper:encode('ChoExternal','XNTExp',{os,"kalle"}),
- ?line {ok,{os,"kalle"}} = asn1_wrapper:decode('ChoExternal','XNTExp',lists:flatten(Bytes42)),
-
- ?line {ok,Bytes43} = asn1_wrapper:encode('ChoExternal','XExpNT',{os,"kalle"}),
- ?line {ok,{os,"kalle"}} = asn1_wrapper:decode('ChoExternal','XExpNT',lists:flatten(Bytes43)),
-
- ?line {ok,Bytes44} = asn1_wrapper:encode('ChoExternal','XExpExp',{os,"kalle"}),
- ?line {ok,{os,"kalle"}} = asn1_wrapper:decode('ChoExternal','XExpExp',lists:flatten(Bytes44)),
+ roundtrip('ChoXBool', {xbool,true}),
+ roundtrip('ChoXBool', {xboolImp,true}),
+ roundtrip('ChoXBool', {xboolExp,true}),
+
+ roundtrip('NT', {os,"kalle"}),
+ roundtrip('Exp', {os,"kalle"}),
+ roundtrip('NTNT', {os,"kalle"}),
+ roundtrip('NTExp', {os,"kalle"}),
+ roundtrip('ExpNT', {os,"kalle"}),
+ roundtrip('ExpExp', {os,"kalle"}),
+ roundtrip('XNTNT', {os,"kalle"}),
+ roundtrip('XNTExp', {os,"kalle"}),
+ roundtrip('XExpNT', {os,"kalle"}),
+ roundtrip('XExpExp', {os,"kalle"}),
ok.
+roundtrip(Type, Value) ->
+ {ok,Encoded} = 'ChoExternal':encode(Type, Value),
+ {ok,Value} = 'ChoExternal':decode(Type, Encoded),
+ ok.
diff --git a/lib/asn1/test/testChoRecursive.erl b/lib/asn1/test/testChoRecursive.erl
index 22be26cbce..ee26d124a9 100644
--- a/lib/asn1/test/testChoRecursive.erl
+++ b/lib/asn1/test/testChoRecursive.erl
@@ -28,38 +28,21 @@
-record('ChoRec2_something',{a, b, c}).
recursive(_Rules) ->
-
- ?line {ok,Bytes11} = asn1_wrapper:encode('ChoRecursive','ChoRec',{'ChoRec',{something,
- #'ChoRec_something'{a = 77,
- b = "some octets here",
- c = {'ChoRec',{nothing,'NULL'}}}}}),
- ?line {ok,{something,{'ChoRec_something',77,"some octets here",{nothing,'NULL'}}}} =
- asn1_wrapper:decode('ChoRecursive','ChoRec',lists:flatten(Bytes11)),
-
-
- ?line {ok,Bytes12} = asn1_wrapper:encode('ChoRecursive','ChoRec',{'ChoRec',{nothing,'NULL'}}),
- ?line {ok,{nothing,'NULL'}} =
- asn1_wrapper:decode('ChoRecursive','ChoRec',lists:flatten(Bytes12)),
-
-
-
- ?line {ok,Bytes21} =
- asn1_wrapper:encode('ChoRecursive','ChoRec2',{'ChoRec2',
- {something,
- #'ChoRec2_something'{a = 77,
- b = "some octets here",
- c = {'ChoRec2',
- {nothing,'NULL'}}}}}),
- ?line {ok,{something,{'ChoRec2_something',77,"some octets here",{nothing,'NULL'}}}} =
- asn1_wrapper:decode('ChoRecursive','ChoRec2',lists:flatten(Bytes21)),
-
-
- ?line {ok,Bytes22} =
- asn1_wrapper:encode('ChoRecursive','ChoRec2',{'ChoRec2',{nothing,'NULL'}}),
- ?line {ok,{nothing,'NULL'}} =
- asn1_wrapper:decode('ChoRecursive','ChoRec2',lists:flatten(Bytes22)),
-
-
-
-
+ roundtrip('ChoRec',
+ {something,
+ #'ChoRec_something'{a = 77,
+ b = "some octets here",
+ c = {nothing,'NULL'}}}),
+ roundtrip('ChoRec', {nothing,'NULL'}),
+ roundtrip('ChoRec2',
+ {something,
+ #'ChoRec2_something'{a = 77,
+ b = "some octets here",
+ c = {nothing,'NULL'}}}),
+ roundtrip('ChoRec2', {nothing,'NULL'}),
+ ok.
+
+roundtrip(Type, Value) ->
+ {ok,Encoded} = 'ChoRecursive':encode(Type, Value),
+ {ok,Value} = 'ChoRecursive':decode(Type, Encoded),
ok.
diff --git a/lib/asn1/test/testChoiceIndefinite.erl b/lib/asn1/test/testChoiceIndefinite.erl
index 630efcf27a..b5832c985a 100644
--- a/lib/asn1/test/testChoiceIndefinite.erl
+++ b/lib/asn1/test/testChoiceIndefinite.erl
@@ -23,12 +23,7 @@
-include_lib("test_server/include/test_server.hrl").
-main(per_bin) -> ok;
main(per) -> ok;
-main(ber_bin_v2) ->
- main(ber);
-main(ber_bin) ->
- main(ber);
main(ber) ->
%% Test case related to OTP-4358
%% normal encoding
diff --git a/lib/asn1/test/testCompactBitString.erl b/lib/asn1/test/testCompactBitString.erl
index 9563a31bf3..96d9f0fdcb 100644
--- a/lib/asn1/test/testCompactBitString.erl
+++ b/lib/asn1/test/testCompactBitString.erl
@@ -22,240 +22,132 @@
-export([compact_bit_string/1, bit_string_unnamed/1,otp_4869/1,
ticket_7734/1]).
--include_lib("test_server/include/test_server.hrl").
-
compact_bit_string(Rules) ->
%%==========================================================
%% Bs1 ::= BIT STRING
%%==========================================================
- ?line {ok,Bytes1} = asn1_wrapper:encode('PrimStrings','Bs1',0),
- ?line {ok,{0,<<>>}} =
- asn1_wrapper:decode('PrimStrings','Bs1',lists:flatten(Bytes1)),
-
- ?line {ok,Bytes2} = asn1_wrapper:encode('PrimStrings','Bs1',4),
- ?line {ok,{5,<<32>>}} =
- asn1_wrapper:decode('PrimStrings','Bs1',lists:flatten(Bytes2)),
-
- ?line {ok,Bytes3} = asn1_wrapper:encode('PrimStrings','Bs1',15),
- ?line {ok,{4,<<240>>}} =
- asn1_wrapper:decode('PrimStrings','Bs1',
- lists:flatten(Bytes3)),
-
- ?line {ok,Bytes4} = asn1_wrapper:encode('PrimStrings','Bs1',255),
- ?line {ok,{0,<<255>>}} =
- asn1_wrapper:decode('PrimStrings','Bs1',lists:flatten(Bytes4)),
-
- ?line {ok,Bytes5} = asn1_wrapper:encode('PrimStrings','Bs1',256),
- ?line {ok,{7,<<0,128>>}} =
- asn1_wrapper:decode('PrimStrings','Bs1',lists:flatten(Bytes5)),
-
- ?line {ok,Bytes6} = asn1_wrapper:encode('PrimStrings','Bs1',257),
- ?line {ok,{7,<<128,128>>}} =
- asn1_wrapper:decode('PrimStrings','Bs1',
- lists:flatten(Bytes6)),
-
- ?line {ok,Bytes7} = asn1_wrapper:encode('PrimStrings','Bs1',444),
- ?line {ok,{7,<<61,128>>}} =
- asn1_wrapper:decode('PrimStrings','Bs1',
- lists:flatten(Bytes7)),
-
- ?line {ok,Bytes8} = asn1_wrapper:encode('PrimStrings','Bs1',
- 12345678901234567890),
- ?line {ok,_} = asn1_wrapper:decode('PrimStrings','Bs1',
- lists:flatten(Bytes8)),
-
-%% Removed due to beam cannot handle this big integers
-%% Bs1_1 = 123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890,
-%% ?line {ok,Bytes9} = asn1_wrapper:encode('PrimStrings','Bs1',Bs1_1),
-%% ?line {ok,_} = asn1_wrapper:decode('PrimStrings','Bs1',lists:flatten(Bytes9)),
-
-%% Bs1_2 = 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890,
-%% ?line {ok,Bytes10} = asn1_wrapper:encode('PrimStrings','Bs1',Bs1_2),
-%% ?line {ok,_} = asn1_wrapper:decode('PrimStrings','Bs1',lists:flatten(Bytes10)),
-
- ?line {ok,Bytes11} = asn1_wrapper:encode('PrimStrings','Bs1',
- [1,1,1,1,1,1,1,1]),
- ?line {ok,{0,<<255>>}} = asn1_wrapper:decode('PrimStrings','Bs1',
- lists:flatten(Bytes11)),
-
- ?line {ok,Bytes12} = asn1_wrapper:encode('PrimStrings',
- 'Bs1',
- [0,1,0,0,1,0]),
- ?line {ok,{2,<<72>>}} =
- asn1_wrapper:decode('PrimStrings','Bs1',
- lists:flatten(Bytes12)),
-
- ?line {ok,Bytes13} =
- asn1_wrapper:encode('PrimStrings', 'Bs1',
- [1,0,0,0,0,0,0,0,0]),
- ?line {ok,{7,<<128,0>>}} =
- asn1_wrapper:decode('PrimStrings','Bs1',
- lists:flatten(Bytes13)),
-
-
- ?line {ok,Bytes14} =
- asn1_wrapper:encode('PrimStrings','Bs1',
- [0,1,0,0,1,0,1,1,1,1,1,0,0,0,1,0,0,1,1]),
- ?line {ok,{5,<<75,226,96>>}} =
- asn1_wrapper:decode('PrimStrings','Bs1',lists:flatten(Bytes14)),
-
-
- ?line case asn1_wrapper:erule(Rules) of
- ber ->
- ?line Bytes15 = [35,8,3,2,0,73,3,2,4,32],
- ?line {ok,{4,<<73,32>>}} =
- asn1_wrapper:decode('PrimStrings','Bs1',
- lists:flatten(Bytes15)),
-
- ?line Bytes16 = [35,9,3,2,0,234,3,3,7,156,0],
- ?line {ok,{7,<<234,156,0>>}} =
- asn1_wrapper:decode('PrimStrings','Bs1',
- lists:flatten(Bytes16)),
-
- ?line Bytes17 = [35,128,3,2,0,73,3,2,4,32,0,0],
- ?line {ok,{4,<<73,32>>}} =
- asn1_wrapper:decode('PrimStrings','Bs1',
- lists:flatten(Bytes17)),
-
- ?line Bytes18 = [35,128,3,2,0,234,3,3,7,156,0,0,0],
- ?line {ok,{7,<<234,156,0>>}} =
- asn1_wrapper:decode('PrimStrings','Bs1',
- lists:flatten(Bytes18)),
- ok;
-
- per ->
- ok
- end,
+ roundtrip('Bs1', 0, {0,<<>>}),
+ roundtrip('Bs1', 4, {5,<<2#00100000>>}),
+ roundtrip('Bs1', 15, {4,<<2#11110000>>}),
+ roundtrip('Bs1', 255, {0,<<2#11111111>>}),
+ roundtrip('Bs1', 256, {7,<<16#00,16#80>>}),
+ roundtrip('Bs1', 257, {7,<<16#80,16#80>>}),
+ roundtrip('Bs1', 444, {7,<<16#3D,16#80>>}),
+ roundtrip('Bs1', 12345678901234567890,
+ {0,<<75,80,248,215,49,149,42,213>>}),
+
+ roundtrip('Bs1', [1,1,1,1,1,1,1,1], {0,<<255>>}),
+ roundtrip('Bs1', [0,1,0,0,1,0], {2,<<16#48>>}),
+ roundtrip('Bs1', [1,0,0,0,0,0,0,0,0], {7,<<16#80,0>>}),
+ roundtrip('Bs1', [0,1,0,0,1,0,1,1,1,1,1,0,0,0,1,0,0,1,1],
+ {5,<<75,226,96>>}),
- %% The following case to test OTP-4200
- ?line {ok,Bytes19} =
- asn1_wrapper:encode('PrimStrings','Bs1',{0,<<0,0,1,1>>}),
- ?line {ok,{0,<<0,0,1,1>>}} =
- asn1_wrapper:decode('PrimStrings','Bs1',lists:flatten(Bytes19)),
+ case Rules of
+ ber ->
+ {ok,{4,<<73,32>>}} =
+ 'PrimStrings':decode('Bs1', <<35,8,3,2,0,73,3,2,4,32>>),
+ {ok,{7,<<234,156,0>>}} =
+ 'PrimStrings':decode('Bs1', <<35,9,3,2,0,234,3,3,7,156,0>>),
+ {ok,{4,<<73,32>>}} =
+ 'PrimStrings':decode('Bs1', <<35,128,3,2,0,73,3,2,4,32,0,0>>),
+ {ok,{7,<<234,156,0>>}} =
+ 'PrimStrings':decode('Bs1',
+ <<35,128,3,2,0,234,3,3,7,156,0,0,0>>);
+ _ ->
+ ok
+ end,
+
+ %% Test OTP-4200
+ roundtrip('Bs1', {0,<<0,0,1,1>>}),
%%==========================================================
%% Bs2 ::= BIT STRING {su(0), mo(1), tu(2), we(3), th(4), fr(5), sa(6) } (SIZE (7))
%%==========================================================
-
- ?line {ok,Bytes21} = asn1_wrapper:encode('PrimStrings','Bs2',[mo,tu,fr]),
- ?line {ok,[mo,tu,fr]} = asn1_wrapper:decode('PrimStrings','Bs2',lists:flatten(Bytes21)),
-
- ?line {ok,Bytes22} = asn1_wrapper:encode('PrimStrings','Bs2',[0,1,1,0,0,1,0]),
- ?line {ok,[mo,tu,fr]} = asn1_wrapper:decode('PrimStrings','Bs2',lists:flatten(Bytes22)),
-
- % ?line case asn1_wrapper:erule(Rules) of
-% ber ->
-% ?line {ok,[mo,tu,fr,su,mo,th]} =
-% asn1_wrapper:decode('PrimStrings','Bs2',[35,8,3,2,1,100,3,2,2,200]),
-
-% ?line {ok,[mo,tu,fr,su,mo,th]} =
-% asn1_wrapper:decode('PrimStrings','Bs2',[35,128,3,2,1,100,3,2,2,200,0,0]),
-% ok;
-
-% per ->
-% ok
-% end,
-
-
+
+ roundtrip('Bs2', [mo,tu,fr]),
+ roundtrip('Bs2', [0,1,1,0,0,1,0], [mo,tu,fr]),
%%==========================================================
%% Bs3 ::= BIT STRING {su(0), mo(1), tu(2), we(3), th(4), fr(5), sa(6) } (SIZE (1..7))
%%==========================================================
- ?line {ok,Bytes31} = asn1_wrapper:encode('PrimStrings','Bs3',[mo,tu,fr]),
- ?line {ok,[mo,tu,fr]} = asn1_wrapper:decode('PrimStrings','Bs3',lists:flatten(Bytes31)),
-
- ?line {ok,Bytes32} = asn1_wrapper:encode('PrimStrings','Bs3',[0,1,1,0,0,1,0]),
- ?line {ok,[mo,tu,fr]} = asn1_wrapper:decode('PrimStrings','Bs3',lists:flatten(Bytes32)),
-
-
+ roundtrip('Bs3', [mo,tu,fr]),
+ roundtrip('Bs3', [0,1,1,0,0,1,0], [mo,tu,fr]),
%%==========================================================
%% BsPri ::= [PRIVATE 61] BIT STRING
%%==========================================================
-
- ?line {ok,Bytes41} = asn1_wrapper:encode('PrimStrings','BsPri',45),
- ?line {ok,{2,<<180>>}} =
- asn1_wrapper:decode('PrimStrings','BsPri',lists:flatten(Bytes41)),
-
- ?line {ok,Bytes42} = asn1_wrapper:encode('PrimStrings','BsPri',211),
- ?line {ok,{0,<<203>>}} =
- asn1_wrapper:decode('PrimStrings','BsPri',lists:flatten(Bytes42)),
-
- ?line case asn1_wrapper:erule(Rules) of
- ber ->
- ?line {ok,{5,<<75,226,96>>}} =
- asn1_wrapper:decode('PrimStrings','BsPri',
- [223,61,4,5,75,226,96]),
-
- ?line {ok,{5,<<75,226,96>>}} =
- asn1_wrapper:decode('PrimStrings','BsPri',
- [255,61,128,3,4,5,75,226,96,0,0]),
-
- ?line {ok,{5,<<75,226,96>>}} =
- asn1_wrapper:decode('PrimStrings','BsPri',
- [255,61,9,3,2,0,75,3,3,5,226,96]),
-
- ?line {ok,{5,<<75,226,96>>}} =
- asn1_wrapper:decode('PrimStrings','BsPri',
- [255,61,128,3,2,0,75,3,3,5,226,96,0,0]),
- ok;
-
- per ->
- ok
- end,
-
+
+ roundtrip('BsPri', 45, {2,<<180>>}),
+ roundtrip('BsPri', 211, {0,<<203>>}),
+
+ case Rules of
+ ber ->
+ {ok,{5,<<75,226,96>>}} =
+ 'PrimStrings':decode('BsPri',
+ <<223,61,4,5,75,226,96>>),
+
+ {ok,{5,<<75,226,96>>}} =
+ 'PrimStrings':decode('BsPri',
+ <<255,61,128,3,4,5,75,226,96,0,0>>),
+
+ {ok,{5,<<75,226,96>>}} =
+ 'PrimStrings':decode('BsPri',
+ <<255,61,9,3,2,0,75,3,3,5,226,96>>),
+
+ {ok,{5,<<75,226,96>>}} =
+ 'PrimStrings':decode('BsPri',
+ <<255,61,128,3,2,0,75,3,3,5,226,96,0,0>>),
+ ok;
+ _ ->
+ ok
+ end,
%%==========================================================
%% BsExpPri ::= [PRIVATE 61] EXPLICIT BIT STRING
%%==========================================================
-
- ?line {ok,Bytes51} = asn1_wrapper:encode('PrimStrings','BsExpPri',45),
- ?line {ok,{2,<<180>>}} =
- asn1_wrapper:decode('PrimStrings','BsExpPri',lists:flatten(Bytes51)),
-
- ?line {ok,Bytes52} = asn1_wrapper:encode('PrimStrings','BsExpPri',211),
- ?line {ok,{0,<<203>>}} =
- asn1_wrapper:decode('PrimStrings','BsExpPri',lists:flatten(Bytes52)),
-
- ?line case asn1_wrapper:erule(Rules) of
- ber ->
- ?line {ok,{5,<<75,226,96>>}} =
- asn1_wrapper:decode('PrimStrings','BsExpPri',[255,61,6,3,4,5,75,226,96]),
- ok;
-
- per ->
- ok
- end,
-
- ok.
-ticket_7734(per_bin) ->
- ?line BS = {0,list_to_binary(lists:duplicate(128,0))},
- ?line {ok,BSEnc} = asn1_wrapper:encode('PrimStrings','BS1024',BS),
- ?line {ok,BS} = asn1_wrapper:decode('PrimStrings','BS1024',BSEnc).
+ roundtrip('BsExpPri', 45, {2,<<180>>}),
+ roundtrip('BsExpPri', 211, {0,<<203>>}),
-bit_string_unnamed(Rules) ->
- case asn1_wrapper:erule(Rules) of
+ case Rules of
ber ->
- ok;
- per ->
- ?line {ok,Bytes1} =
- asn1_wrapper:encode('PrimStrings','TransportLayerAddress',
- [0,1,1,0]),
- ?line {ok,{4,<<96>>}} =
- asn1_wrapper:decode('PrimStrings','TransportLayerAddress',
- lists:flatten(Bytes1))
- end.
+ {ok,{5,<<75,226,96>>}} =
+ 'PrimStrings':decode('BsExpPri', <<255,61,6,3,4,5,75,226,96>>);
+ _ ->
+ ok
+ end,
-otp_4869(per_bin) ->
- ?line Val1={'IP',[0],{0,<<62,235,90,50,0,0,0,0,0,0,0,0,0,0,0,0>>},asn1_NOVALUE},
- ?line Val2 = {'IP',[0],[0,0,1,1,1,1,1,0,1,1,1,0,1,0,1,1,0,1,0,1,1,0,1,0,0,0,1,1,0,0,1,0] ++ lists:duplicate(128 - 32,0),asn1_NOVALUE},
+ ok.
- ?line {ok,Bytes1} = asn1_wrapper:encode('Constraints','IP',Val1),
- ?line {ok,Bytes1} = asn1_wrapper:encode('Constraints','IP',Val2);
+ticket_7734(_) ->
+ BS = {0,list_to_binary(lists:duplicate(128, 0))},
+ roundtrip('BS1024', BS).
+
+bit_string_unnamed(_Rules) ->
+ roundtrip('TransportLayerAddress', [0,1,1,0], {4,<<96>>}).
+
+otp_4869(per) ->
+ Val1 = {'IP',[0],{0,<<62,235,90,50,0,0,0,0,0,0,0,0,0,0,0,0>>},asn1_NOVALUE},
+ Val2 = {'IP',[0],[0,0,1,1,1,1,1,0,1,1,1,0,1,0,1,1,0,1,0,1,1,0,
+ 1,0,0,0,1,1,0,0,1,0] ++
+ lists:duplicate(128 - 32, 0),asn1_NOVALUE},
+ {ok,Encoded} = 'Constraints':encode('IP', Val1),
+ {ok,Encoded} = 'Constraints':encode('IP', Val2),
+ ok;
otp_4869(_) ->
ok.
+
+roundtrip(Type, Val) ->
+ roundtrip_1('PrimStrings', Type, Val, Val).
+
+roundtrip(Type, Val1, Val2) ->
+ roundtrip_1('PrimStrings', Type, Val1, Val2).
+
+roundtrip_1(Mod, Type, In, Out) ->
+ {ok,Encoded} = Mod:encode(Type, In),
+ {ok,Out} = Mod:decode(Type, Encoded),
+ ok.
diff --git a/lib/asn1/test/testConstraints.erl b/lib/asn1/test/testConstraints.erl
index 1ce68ec522..c8d9008641 100644
--- a/lib/asn1/test/testConstraints.erl
+++ b/lib/asn1/test/testConstraints.erl
@@ -30,126 +30,127 @@ int_constraints(Rules) ->
%% SingleValue ::= INTEGER (1)
%%==========================================================
- ?line {ok,Bytes1} = asn1_wrapper:encode('Constraints','SingleValue',1),
- ?line {ok,1} = asn1_wrapper:decode('Constraints','SingleValue',
- lists:flatten(Bytes1)),
-
- ?line case asn1_wrapper:erule(Rules) of
- ber ->
- ?line {ok,Bytes2} =
- asn1_wrapper:encode('Constraints','SingleValue',0),
- ?line {error,{asn1,{integer_range,_,0}}} =
- asn1_wrapper:decode('Constraints','SingleValue',
- lists:flatten(Bytes2)),
- ?line {ok,Bytes3} =
- asn1_wrapper:encode('Constraints','SingleValue',1000),
- ?line {error,{asn1,{integer_range,_,1000}}} =
- asn1_wrapper:decode('Constraints','SingleValue',
- lists:flatten(Bytes3));
- per ->
- ?line {error,_Reason1} =
- asn1_wrapper:encode('Constraints','SingleValue',0),
- ?line {error,_Reason2} =
- asn1_wrapper:encode('Constraints','SingleValue',1000)
- end,
-
-
+ range_error(Rules, 'SingleValue', 0),
+ roundtrip('SingleValue', 1),
+ range_error(Rules, 'SingleValue', 2),
+ range_error(Rules, 'SingleValue', 1000),
%%==========================================================
%% SingleValue2 ::= INTEGER (1..20)
%%==========================================================
- ?line {ok,Bytes4} = asn1_wrapper:encode('Constraints','SingleValue2',1),
- ?line {ok,1} = asn1_wrapper:decode('Constraints','SingleValue2',
- lists:flatten(Bytes4)),
-
- ?line {ok,Bytes5} = asn1_wrapper:encode('Constraints','SingleValue2',20),
- ?line {ok,20} = asn1_wrapper:decode('Constraints','SingleValue2',
- lists:flatten(Bytes5)),
-
- ?line case asn1_wrapper:erule(Rules) of
- ber ->
- ?line {ok,Bytes6} =
- asn1_wrapper:encode('Constraints','SingleValue2',0),
- ?line {error,{asn1,{integer_range,{1,20},0}}} =
- asn1_wrapper:decode('Constraints','SingleValue2',
- lists:flatten(Bytes6)),
- ?line {ok,Bytes7} =
- asn1_wrapper:encode('Constraints','SingleValue2',21),
- ?line {error,{asn1,{integer_range,{1,20},21}}} =
- asn1_wrapper:decode('Constraints','SingleValue2',
- lists:flatten(Bytes7));
- per ->
- ?line {error,_Reason3} =
- asn1_wrapper:encode('Constraints','SingleValue',0),
- ?line {error,_Reason4} =
- asn1_wrapper:encode('Constraints','SingleValue',1000)
- end,
+ range_error(Rules, 'SingleValue2', 0),
+ roundtrip('SingleValue2', 1),
+ roundtrip('SingleValue2', 20),
+ range_error(Rules, 'SingleValue2', 21),
+ range_error(Rules, 'SingleValue2', 1000),
+ %%==========================================================
+ %% SingleValue3 ::= INTEGER (Predefined | 5 | 10)
+ %% Testcase for OTP-10139. A single value subtyping of an integer type
+ %% where one value is predefined.
+ %%==========================================================
+ roundtrip('SingleValue3', 1),
+ roundtrip('SingleValue3', 5),
+ roundtrip('SingleValue3', 10),
%%==========================================================
%% Range2to19 ::= INTEGER (1<..<20)
%%==========================================================
- ?line {ok,Bytes8} = asn1_wrapper:encode('Constraints','Range2to19',2),
- ?line {ok,2} = asn1_wrapper:decode('Constraints','Range2to19',lists:flatten(Bytes8)),
-
- ?line {ok,Bytes9} = asn1_wrapper:encode('Constraints','Range2to19',19),
- ?line {ok,19} = asn1_wrapper:decode('Constraints','Range2to19',lists:flatten(Bytes9)),
-
- ?line case asn1_wrapper:erule(Rules) of
- ber ->
- ?line {ok,Bytes10} =
- asn1_wrapper:encode('Constraints','Range2to19',1),
- ?line {error,{asn1,{integer_range,{2,19},1}}} =
- asn1_wrapper:decode('Constraints','Range2to19',
- lists:flatten(Bytes10)),
- ?line {ok,Bytes11} =
- asn1_wrapper:encode('Constraints','Range2to19',20),
- ?line {error,{asn1,{integer_range,{2,19},20}}} =
- asn1_wrapper:decode('Constraints','Range2to19',
- lists:flatten(Bytes11));
- per ->
- ?line {error,_Reason5} =
- asn1_wrapper:encode('Constraints','Range2to19',1),
- ?line {error,_Reason6} =
- asn1_wrapper:encode('Constraints','Range2to19',20)
- end,
+ range_error(Rules, 'Range2to19', 1),
+ roundtrip('Range2to19', 2),
+ roundtrip('Range2to19', 19),
+ range_error(Rules, 'Range2to19', 20),
+
+ %%==========================================================
+ %% Tests for Range above 16^4 up to maximum supported by asn1 assuming the
+ %% octet length field is encoded on max 8 bits
+ %%==========================================================
+ LastNumWithoutLengthEncoding = 65536,
+ roundtrip('Range256to65536', LastNumWithoutLengthEncoding),
+
+ FirstNumWithLengthEncoding = 65537,
+ roundtrip('LargeConstraints', 'RangeMax', FirstNumWithLengthEncoding),
+
+ FirstNumOver16_6 = 16777217,
+ roundtrip('LargeConstraints', 'RangeMax', FirstNumOver16_6),
+ FirstNumOver16_8 = 4294967297,
+ roundtrip('LargeConstraints', 'RangeMax', FirstNumOver16_8),
+
+ FirstNumOver16_10 = 1099511627776,
+ roundtrip('LargeConstraints', 'RangeMax', FirstNumOver16_10),
+
+ FirstNumOver16_10 = 1099511627776,
+ roundtrip('LargeConstraints', 'RangeMax', FirstNumOver16_10),
+
+ HalfMax = 1 bsl (128*8),
+ roundtrip('LargeConstraints', 'RangeMax', HalfMax),
+
+ Max = 1 bsl (255*8),
+ roundtrip('LargeConstraints', 'RangeMax', Max),
+
+ %% Random number within longlong range
+ LongLong = 12672809400538808320,
+ roundtrip('LongLong', LongLong),
%%==========================================================
%% Constraint Combinations (Duboisson p. 285)
%% I ::= INTEGER (0|15..269)
%%==========================================================
- ?line {ok,Bytes12} = asn1_wrapper:encode('Constraints','I',0),
- ?line {ok,0} = asn1_wrapper:decode('Constraints','I',Bytes12),
- ?line {ok,Bytes13} = asn1_wrapper:encode('Constraints','I',20),
- ?line {ok,20} = asn1_wrapper:decode('Constraints','I',Bytes13),
+ range_error(Rules, 'I', -1),
+ roundtrip('I', 0),
+ roundtrip('I', 15),
+ roundtrip('I', 20),
+ roundtrip('I', 269),
+ range_error(Rules, 'I', 270),
%%==========================================================
%% Constraint Combinations (Duboisson p. 285)
%% X1 ::= INTEGER (1..4|8|10|20)
%%==========================================================
- ?line {ok,Bytes14} = asn1_wrapper:encode('Constraints','X1',1),
- ?line {ok,1} = asn1_wrapper:decode('Constraints','X1',Bytes14),
- ?line {ok,Bytes15} = asn1_wrapper:encode('Constraints','X1',20),
- ?line {ok,20} = asn1_wrapper:decode('Constraints','X1',Bytes15),
+ range_error(Rules, 'X1', 0),
+ roundtrip('X1', 1),
+ roundtrip('X1', 4),
+ roundtrip('X1', 8),
+ roundtrip('X1', 10),
+ roundtrip('X1', 20),
+ range_error(Rules, 'X1', 21),
+
%%==========================================================
%% SIZE Constraint (Duboisson p. 268)
%% T ::= IA5String (SIZE (1|2, ..., SIZE (1|2|3)))
%% T2 ::= IA5String (SIZE (1|2, ..., 3))
%%==========================================================
- ?line {ok,Bytes16} = asn1_wrapper:encode('Constraints','T',"IA"),
- ?line {ok,"IA"} = asn1_wrapper:decode('Constraints','T',Bytes16),
- ?line {ok,Bytes17} = asn1_wrapper:encode('Constraints','T2',"IA"),
- ?line {ok,"IA"} = asn1_wrapper:decode('Constraints','T2',Bytes17).
-
+ roundtrip('T', "IA"),
+ roundtrip('T2', "IA").
refed_NNL_name(_Erule) ->
?line {ok,_} = asn1_wrapper:encode('Constraints','AnotherThing',fred),
?line {error,_Reason} =
asn1_wrapper:encode('Constraints','AnotherThing',fred3).
+
+roundtrip(Type, Value) ->
+ roundtrip('Constraints', Type, Value).
+
+roundtrip(Module, Type, Value) ->
+ {ok,Encoded} = Module:encode(Type, Value),
+ {ok,Value} = Module:decode(Type, Encoded),
+ ok.
+
+range_error(ber, Type, Value) ->
+ %% BER: Values outside the effective range should be rejected
+ %% on decode.
+ {ok,Encoded} = 'Constraints':encode(Type, Value),
+ {error,{asn1,{integer_range,_,_}}} = 'Constraints':decode(Type, Encoded),
+ ok;
+range_error(Per, Type, Value) when Per =:= per; Per =:= uper ->
+ %% (U)PER: Values outside the effective range should be rejected
+ %% on encode.
+ {error,_} = 'Constraints':encode(Type, Value),
+ ok.
diff --git a/lib/asn1/test/testDeepTConstr.erl b/lib/asn1/test/testDeepTConstr.erl
index aa3afbb58f..3df7bcbaa0 100644
--- a/lib/asn1/test/testDeepTConstr.erl
+++ b/lib/asn1/test/testDeepTConstr.erl
@@ -26,21 +26,19 @@
-include_lib("test_server/include/test_server.hrl").
main(_Erule) ->
- Val1 = {'FilterItem',
- {substrings,
- {'FilterItem_substrings',
- {2,6},
- [{initial,"SE"},
- {any,"DK"},
- {final,"N"}]}}},
+ Val1 = {substrings,
+ {'FilterItem_substrings',
+ {2,6},
+ [{initial,"SE"},
+ {any,"DK"},
+ {final,"N"}]}},
- Val2 = {'FilterItem',
- {substrings,
- {'FilterItem_substrings',
- {2,6},
- [{initial,"SE"},
- {any,"DK"},
- {final,"NO"}]}}},
+ Val2 = {substrings,
+ {'FilterItem_substrings',
+ {2,6},
+ [{initial,"SE"},
+ {any,"DK"},
+ {final,"NO"}]}},
?line {ok,Bytes1} =
asn1_wrapper:encode('TConstrChoice','FilterItem',Val1),
diff --git a/lib/asn1/test/testEnumExt.erl b/lib/asn1/test/testEnumExt.erl
index c97116413a..0811f20571 100644
--- a/lib/asn1/test/testEnumExt.erl
+++ b/lib/asn1/test/testEnumExt.erl
@@ -23,64 +23,43 @@
-include_lib("test_server/include/test_server.hrl").
-main(Rules) when Rules == per; Rules == per_bin; Rules == uper_bin ->
- io:format("main(~p)~n",[Rules]),
- B32=[32],B64=[64],
+main(Rule) when Rule =:= per; Rule =:= uper ->
+ io:format("main(~p)~n",[Rule]),
+
%% ENUMERATED with extensionmark (value is in root set)
- ?line {ok,B32} = asn1_wrapper:encode('EnumExt','Ext',red),
- ?line {ok,red} = asn1_wrapper:decode('EnumExt','Ext',B32),
+ B32 = <<32>>,
+ B32 = roundtrip('Ext', red),
%% ENUMERATED with extensionmark (value is an extensionvalue)
- ?line {ok,Or} = asn1_wrapper:encode('EnumExt','Ext1',orange),
- ?line {ok,orange} = asn1_wrapper:decode('EnumExt','Ext1',Or),
+ Or = roundtrip('Ext1', orange),
%% unknown extensionvalue
- ?line {ok,{asn1_enum,0}} = asn1_wrapper:decode('EnumExt','Ext',Or),
-
+ {ok,{asn1_enum,0}} = asn1_wrapper:decode('EnumExt','Ext',Or),
%% ENUMERATED no extensionmark
- ?line {ok,B64} = asn1_wrapper:encode('EnumExt','Noext',red),
- ?line {ok,red} = asn1_wrapper:decode('EnumExt','Noext',B64),
+ B64 = <<64>>,
+ B64 = roundtrip('Noext', red),
ok;
-main(ber_bin_v2) ->
- main(ber);
-main(ber_bin) ->
- main(ber);
main(ber) ->
io:format("main(ber)~n",[]),
%% ENUMERATED with extensionmark (value is in root set)
- ?line {ok,Bytes1} = asn1_wrapper:encode('EnumExt','Ext',red),
- ?line {ok,red} = asn1_wrapper:decode('EnumExt','Ext',lists:flatten(Bytes1)),
+ roundtrip('Ext', red),
%% value is an extensionvalue
- ?line {ok,Bytes1_1} = asn1_wrapper:encode('EnumExt','Ext1',orange),
- ?line {ok,{asn1_enum,7}} = asn1_wrapper:decode('EnumExt','Ext',lists:flatten(Bytes1_1)),
-%% ?line {ok,Bytes1_1} = asn1_wrapper:encode('EnumExt','Ext',{asn1_enum,7}),
+ {ok,Bytes1_1} = asn1_wrapper:encode('EnumExt','Ext1',orange),
+ {ok,{asn1_enum,7}} = asn1_wrapper:decode('EnumExt','Ext',lists:flatten(Bytes1_1)),
- %% ENUMERATED no extensionmark
- ?line {ok,Bytes2} = asn1_wrapper:encode('EnumExt','Noext',red),
- ?line {ok,red} = asn1_wrapper:decode('EnumExt','Noext',lists:flatten(Bytes2)),
+ %% ENUMERATED no extensionmark
+ roundtrip('Noext', red),
?line {error,{asn1,_}} = (catch asn1_wrapper:encode('EnumExt','Noext',orange)),
-%% ?line {error,{asn1,_}} = (catch asn1_wrapper:encode('EnumExt','Noext',{asn1_enum,7})),
- ok,
%% ENUMERATED with atom 'com'
- ?line {ok,Bytes3} = asn1_wrapper:encode('EnumExt','Globalstate',{'Globalstate',preop}),
- ?line {ok,preop} = asn1_wrapper:decode('EnumExt','Globalstate',
- lists:flatten(Bytes3)),
- ?line {ok,Bytes4} = asn1_wrapper:encode('EnumExt','Globalstate',{'Globalstate',com}),
- ?line {ok,com} = asn1_wrapper:decode('EnumExt','Globalstate',
- lists:flatten(Bytes4)).
-
-
-
-
-
-
-
-
-
-
-
+ roundtrip('Globalstate', preop),
+ roundtrip('Globalstate', com),
+ ok.
+roundtrip(Type, Value) ->
+ {ok,Encoded} = 'EnumExt':encode(Type, Value),
+ {ok,Value} = 'EnumExt':decode(Type, Encoded),
+ Encoded.
diff --git a/lib/asn1/test/testINSTANCE_OF.erl b/lib/asn1/test/testINSTANCE_OF.erl
index 5986a00ec5..ce411beb92 100644
--- a/lib/asn1/test/testINSTANCE_OF.erl
+++ b/lib/asn1/test/testINSTANCE_OF.erl
@@ -26,7 +26,7 @@
main(Erule) ->
?line {ok,Integer} = asn1_wrapper:encode('INSTANCEOF','Int',3),
- Int = wrap(Erule,Integer),
+ Int = list_to_binary(Integer),
ValotherName = {otherName,{'INSTANCE OF',{2,4},Int}},
VallastName1 = {lastName,{'GeneralName_lastName',{2,4},12}},
VallastName2 = {lastName,{'GeneralName_lastName',{2,3,4},
@@ -61,18 +61,3 @@ test_encdec(_Erule,{lastName,{'GeneralName_lastName',{2,3,4},
ok;
test_encdec(Erule,Res) ->
{error,{Erule,Res}}.
-
-wrap(ber,Int) when is_list(Int) ->
- binary_to_list(list_to_binary(Int));
-wrap(per,Int) when is_list(Int) ->
- binary_to_list(list_to_binary(Int));
-wrap(ber_bin,Int) when is_list(Int) ->
- list_to_binary(Int);
-wrap(ber_bin_v2,Int) when is_list(Int) ->
- list_to_binary(Int);
-wrap(per_bin,Int) when is_list(Int) ->
- list_to_binary(Int);
-wrap(uper_bin,Int) when is_list(Int) ->
- list_to_binary(Int);
-wrap(_,Int) ->
- Int.
diff --git a/lib/asn1/test/testInfObjectClass.erl b/lib/asn1/test/testInfObjectClass.erl
index e639066246..98408502c6 100644
--- a/lib/asn1/test/testInfObjectClass.erl
+++ b/lib/asn1/test/testInfObjectClass.erl
@@ -37,15 +37,12 @@ main(Rule) ->
{component,'ArgumentType'},
{value,_},_}}} = asn1_wrapper:encode('InfClass','Seq',
{'Seq',12,13,1}),
- Bytes2 =
- if
- Rule==per;Rule==per_bin ->
- [1,12,1,11,1,1];
- Rule == uper_bin ->
- <<1,12,1,11,1,1>>;
- true ->
- [48,9,2,1,12,2,1,11,2,1,1]
- end,
+ Bytes2 = case Rule of
+ ber ->
+ <<48,9,2,1,12,2,1,11,2,1,1>>;
+ _ ->
+ <<1,12,1,11,1,1>>
+ end,
?line {error,{asn1,{'Type not compatible with table constraint',
{{component,_},
{value,_B},_}}}} =
diff --git a/lib/asn1/test/testMergeCompile.erl b/lib/asn1/test/testMergeCompile.erl
index 31aa3518f6..8ef7ba3458 100644
--- a/lib/asn1/test/testMergeCompile.erl
+++ b/lib/asn1/test/testMergeCompile.erl
@@ -37,23 +37,17 @@ main(Erule) ->
%% test of RANAP.set.asn1
?line _PIEVal = [{'ProtocolIE-Field',4,ignore,{'Cause',{radioNetwork,{'CauseRadioNetwork','rab-pre-empted'}}}}],
- ?line PIEVal2 = [{'ProtocolIE-Field',4,ignore,{'Cause',{radioNetwork,'rab-pre-empted'}}}],
+ PIEVal2 = [{'ProtocolIE-Field',4,ignore,{radioNetwork,'rab-pre-empted'}}],
?line _PEVal = [{'ProtocolExtensionField',[0]}],
%% ?line EncVal = asn1rt_per_v1:encode_integer([],100),
?line EncVal =
case Erule of
per ->
- [1,100];
- per_bin ->
<<1,100>>;
- uper_bin ->
+ uper ->
<<1,100>>;
ber ->
- [2,1,1];
- ber_bin ->
- <<2,1,1>>;
- ber_bin_v2 ->
- <<2,1,1>>
+ [2,1,1]
end,
?line PEVal2 = [{dummy,1,ignore,EncVal},{dummy,2,reject,EncVal}],
?line Val2 =
@@ -76,7 +70,7 @@ main(Erule) ->
mvrasn(Erule) ->
case Erule of
- Ber when Ber == ber;Ber == ber_bin ->
+ ber ->
?line ok = test(isd),
?line ok = test(isd2),
?line ok = test(dsd),
diff --git a/lib/compiler/test/compilation_SUITE_data/bad_functional_value.erl b/lib/asn1/test/testMultipleLevels.erl
index 126a573e83..ff6d023440 100644
--- a/lib/compiler/test/compilation_SUITE_data/bad_functional_value.erl
+++ b/lib/asn1/test/testMultipleLevels.erl
@@ -1,28 +1,27 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2012. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, 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(bad_functional_value).
-
--export([?MODULE/0,a/0]).
-
-?MODULE() ->
- ok.
+%%
-a() ->
- .list_to_atom("ok").
+-module(testMultipleLevels).
+-export([main/1]).
+main(_) ->
+ Data = {'Top',{short,"abc"},{long,"a long string follows here"}},
+ {ok,B} = 'MultipleLevels':encode('Top', Data),
+ {ok,Data} = 'MultipleLevels':decode('Top', iolist_to_binary(B)).
diff --git a/lib/asn1/test/testParameterizedInfObj.erl b/lib/asn1/test/testParameterizedInfObj.erl
index 68faf08a61..17108e285b 100644
--- a/lib/asn1/test/testParameterizedInfObj.erl
+++ b/lib/asn1/test/testParameterizedInfObj.erl
@@ -86,7 +86,7 @@ main(Erule) ->
ranap(_Erule) ->
- ?line PIEVal2 = [{'ProtocolIE-Field',4,ignore,{'Cause',{radioNetwork,'rab-pre-empted'}}}],
+ PIEVal2 = [{'ProtocolIE-Field',4,ignore,{radioNetwork,'rab-pre-empted'}}],
?line Val2 =
#'InitiatingMessage'{procedureCode=1,
criticality=ignore,
@@ -98,7 +98,7 @@ ranap(_Erule) ->
ok.
-open_type(uper_bin,Val) when is_list(Val) ->
+open_type(uper,Val) when is_list(Val) ->
list_to_binary(Val);
open_type(_,Val) ->
Val.
diff --git a/lib/asn1/test/testPrimStrings.erl b/lib/asn1/test/testPrimStrings.erl
index b1c5172b95..263d9e5ed2 100644
--- a/lib/asn1/test/testPrimStrings.erl
+++ b/lib/asn1/test/testPrimStrings.erl
@@ -338,69 +338,24 @@ octet_string(Rules) ->
ok
end,
+ roundtrip('Os', [47,23,99,255,1]),
+ roundtrip('OsCon', [47,23,99,255,1]),
+ roundtrip('OsPri', [47,23,99,255,1]),
+ roundtrip('OsApp', [47,23,99,255,1]),
-
- ?line {ok,Bytes4} =
- asn1_wrapper:encode('PrimStrings','Os',[47,23,99,255,1]),
- ?line {ok,[47,23,99,255,1]} = asn1_wrapper:decode('PrimStrings','Os',lists:flatten(Bytes4)),
-
- ?line {ok,Bytes5} =
- asn1_wrapper:encode('PrimStrings','OsCon',[47,23,99,255,1]),
- ?line {ok,[47,23,99,255,1]} = asn1_wrapper:decode('PrimStrings','OsCon',lists:flatten(Bytes5)),
-
- ?line {ok,Bytes6} =
- asn1_wrapper:encode('PrimStrings','OsPri',[47,23,99,255,1]),
- ?line {ok,[47,23,99,255,1]} = asn1_wrapper:decode('PrimStrings','OsPri',lists:flatten(Bytes6)),
-
- ?line {ok,Bytes7} =
- asn1_wrapper:encode('PrimStrings','OsApp',[47,23,99,255,1]),
- ?line {ok,[47,23,99,255,1]} = asn1_wrapper:decode('PrimStrings','OsApp',lists:flatten(Bytes7)),
-
- ?line {ok,Bytes8} =
- asn1_wrapper:encode('PrimStrings','OsExpCon',[47,23,99,255,1]),
- ?line {ok,[47,23,99,255,1]} = asn1_wrapper:decode('PrimStrings','OsExpCon',lists:flatten(Bytes8)),
-
- ?line {ok,Bytes9} =
- asn1_wrapper:encode('PrimStrings','OsExpPri',[47,23,99,255,1]),
- ?line {ok,[47,23,99,255,1]} = asn1_wrapper:decode('PrimStrings','OsExpPri',lists:flatten(Bytes9)),
-
- ?line {ok,Bytes10} =
- asn1_wrapper:encode('PrimStrings','OsExpApp',[47,23,99,255,1]),
- ?line {ok,[47,23,99,255,1]} = asn1_wrapper:decode('PrimStrings','OsExpApp',lists:flatten(Bytes10)),
-
- ?line {ok,Bytes11} =
- asn1_wrapper:encode('PrimStrings','Os',[]),
- ?line {ok,[]} = asn1_wrapper:decode('PrimStrings','Os',lists:flatten(Bytes11)),
-
- ?line {ok,Bytes12} =
- asn1_wrapper:encode('PrimStrings','OsApp',[]),
- ?line {ok,[]} = asn1_wrapper:decode('PrimStrings','OsApp',lists:flatten(Bytes12)),
-
- ?line {ok,Bytes13} =
- asn1_wrapper:encode('PrimStrings','OsExpApp',[]),
- ?line {ok,[]} = asn1_wrapper:decode('PrimStrings','OsExpApp',lists:flatten(Bytes13)),
-
-
-
-
-
+ roundtrip('OsExpCon', [47,23,99,255,1]),
+ roundtrip('OsExpPri', [47,23,99,255,1]),
+ roundtrip('OsExpApp', [47,23,99,255,1]),
+ roundtrip('Os', []),
+ roundtrip('OsApp', []),
+ roundtrip('OsExpApp',[]),
OsR = "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890",
- ?line {ok,Bytes21} =
- asn1_wrapper:encode('PrimStrings','Os',OsR),
- ?line {ok,Os1} = asn1_wrapper:decode('PrimStrings','Os',lists:flatten(Bytes21)),
- ?line Os1 = OsR,
- ?line {ok,Bytes22} =
- asn1_wrapper:encode('PrimStrings','OsCon',OsR),
- ?line {ok,Os2} = asn1_wrapper:decode('PrimStrings','OsCon',lists:flatten(Bytes22)),
- ?line Os2 = OsR,
- ?line {ok,Bytes23} =
- asn1_wrapper:encode('PrimStrings','OsExpApp',OsR),
- ?line {ok,Os3} = asn1_wrapper:decode('PrimStrings','OsExpApp',lists:flatten(Bytes23)),
- ?line Os3 = OsR,
-
+ roundtrip('Os', OsR),
+ roundtrip('OsCon', OsR),
+ roundtrip('OsExpApp', OsR),
?line case asn1_wrapper:erule(Rules) of
@@ -416,21 +371,90 @@ octet_string(Rules) ->
ok
end,
+ fragmented_octet_string(Rules),
+ S255 = lists:seq(1, 255),
+ FixedStrings = {'OsFixedStrings',true,"","1","12","345",true,
+ S255,[$a|S255],[$a,$b|S255],397},
+ roundtrip('OsFixedStrings', FixedStrings),
ok.
+fragmented_octet_string(Erules) ->
+ K16 = 1 bsl 14,
+ K32 = K16 + K16,
+ K48 = K32 + K16,
+ K64 = K48 + K16,
+ Lens = [0,1,14,15,16,17,127,128,
+ K16-1,K16,K16+1,K16+(1 bsl 7)-1,K16+(1 bsl 7),K16+(1 bsl 7)+1,
+ K32-1,K32,K32+1,K32+(1 bsl 7)-1,K32+(1 bsl 7),K32+(1 bsl 7)+1,
+ K48-1,K48,K48+1,K48+(1 bsl 7)-1,K48+(1 bsl 7),K48+(1 bsl 7)+1,
+ K64-1,K64,K64+1,K64+(1 bsl 7)-1,K64+(1 bsl 7),K64+(1 bsl 7)+1,
+ K64+K16-1,K64+K16,K64+K16+1],
+ Types = ['Os','OsFrag'],
+ [fragmented_octet_string(Erules, Types, L) || L <- Lens],
+ fragmented_octet_string(Erules, ['FixedOs65536'], 65536),
+ fragmented_octet_string(Erules, ['FixedOs65537'], 65537),
+
+ %% Make sure that octet alignment works.
+ roundtrip('OsAlignment',
+ {'OsAlignment',false,make_value(70000),true,make_value(66666),
+ false,make_value(65536),42}),
+ roundtrip('OsAlignment',
+ {'OsAlignment',false,make_value(0),true,make_value(0),
+ false,make_value(65536),42}),
+ ok.
+fragmented_octet_string(Erules, Types, L) ->
+ Value = make_value(L),
+ [begin
+ Encoded = enc_frag(Erules, Type, Value),
+ {ok,Value} = 'PrimStrings':decode(Type, Encoded)
+ end || Type <- Types],
+ ok.
+
+enc_frag(Erules, Type, Value) ->
+ {ok,Encoded} = 'PrimStrings':encode(Type, Value),
+ case Erules of
+ ber ->
+ Encoded;
+ _ ->
+ %% Validate encoding with our own encoder.
+ Encoded = enc_frag_1(<<>>, list_to_binary(Value))
+ end.
+
+enc_frag_1(Res, Bin0) ->
+ K16 = 1 bsl 14,
+ Sz = byte_size(Bin0),
+ if
+ Sz >= K16 ->
+ F = min(Sz div K16, 4),
+ FragSize = F * K16,
+ <<Frag:FragSize/binary-unit:8,Bin/binary>> = Bin0,
+ enc_frag_1(<<Res/binary,3:2,F:6,Frag/binary>>, Bin);
+ Sz >= 128 ->
+ <<Res/binary,1:1,0:1,Sz:14,Bin0/binary>>;
+ true ->
+ <<Res/binary,0:1,Sz:7,Bin0/binary>>
+ end.
+
+make_value(L) ->
+ make_value(L, 0, []).
+
+make_value(0, _, Acc) ->
+ Acc;
+make_value(N, Byte, Acc) when Byte =< 255 ->
+ make_value(N-1, Byte+7, [Byte|Acc]);
+make_value(N, Byte, Acc) ->
+ make_value(N, Byte band 16#FF, Acc).
-
numeric_string(Rules) ->
%%==========================================================
%% Ns ::= NumericString
%%==========================================================
- ?line {ok,BytesNs2} = asn1_wrapper:encode('PrimStrings','Ns',[]),
- ?line {ok,[]} = asn1_wrapper:decode('PrimStrings','Ns',lists:flatten(BytesNs2)),
+ roundtrip('Ns', []),
?line case asn1_wrapper:erule(Rules) of
ber ->
@@ -455,10 +479,7 @@ numeric_string(Rules) ->
%% NsCon ::= [70] NumericString
%%==========================================================
- ?line {ok,BytesNs12} = asn1_wrapper:encode('PrimStrings','NsCon',[]),
- ?line {ok,[]} = asn1_wrapper:decode('PrimStrings','NsCon',lists:flatten(BytesNs12)),
-
-
+ roundtrip('NsCon', []),
?line case asn1_wrapper:erule(Rules) of
ber ->
@@ -482,10 +503,7 @@ numeric_string(Rules) ->
%% NsExpCon ::= [71] EXPLICIT NumericString
%%==========================================================
- ?line {ok,BytesNs22} = asn1_wrapper:encode('PrimStrings','NsExpCon',[]),
- ?line {ok,[]} = asn1_wrapper:decode('PrimStrings','NsExpCon',lists:flatten(BytesNs22)),
-
-
+ roundtrip('NsExpCon', []),
?line case asn1_wrapper:erule(Rules) of
ber ->
@@ -507,9 +525,6 @@ numeric_string(Rules) ->
ok.
-
-
-
other_strings(_Rules) ->
@@ -517,49 +532,27 @@ other_strings(_Rules) ->
%% Ps ::= PrintableString
%%==========================================================
- ?line {ok,BytesPs1} = asn1_wrapper:encode('PrimStrings','Ps',[47,23,99,75,47]),
- ?line {ok,[47,23,99,75,47]} =
- asn1_wrapper:decode('PrimStrings','Ps',lists:flatten(BytesPs1)),
-
- ?line {ok,BytesPs2} = asn1_wrapper:encode('PrimStrings','Ps',[]),
- ?line {ok,[]} = asn1_wrapper:decode('PrimStrings','Ps',lists:flatten(BytesPs2)),
-
+ roundtrip('Ps', [47,23,99,75,47]),
+ roundtrip('Ps', []),
%%==========================================================
%% Vis ::= VisibleString
%%==========================================================
- ?line {ok,BytesVis1} = asn1_wrapper:encode('PrimStrings','Vis',[47,23,99,75,47]),
- ?line {ok,[47,23,99,75,47]} =
- asn1_wrapper:decode('PrimStrings','Vis',lists:flatten(BytesVis1)),
-
- ?line {ok,BytesVis2} = asn1_wrapper:encode('PrimStrings','Vis',[]),
- ?line {ok,[]} = asn1_wrapper:decode('PrimStrings','Vis',lists:flatten(BytesVis2)),
-
+ roundtrip('Vis', [47,23,99,75,47]),
+ roundtrip('Vis', []),
%%==========================================================
%% IA5 ::= IA5String
%%==========================================================
- ?line {ok,BytesIA51} = asn1_wrapper:encode('PrimStrings','IA5',[47,23,99,75,47]),
- ?line {ok,[47,23,99,75,47]} =
- asn1_wrapper:decode('PrimStrings','IA5',lists:flatten(BytesIA51)),
-
- ?line {ok,BytesIA52} = asn1_wrapper:encode('PrimStrings','IA5',[]),
- ?line {ok,[]} = asn1_wrapper:decode('PrimStrings','IA5',lists:flatten(BytesIA52)),
+ roundtrip('IA5', [47,23,99,75,47]),
+ roundtrip('IA5', []),
-
IA5_1 = "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890",
-
- ?line {ok,BytesIA53} = asn1_wrapper:encode('PrimStrings','IA5',IA5_1),
- ?line {ok,IA5_1r} = asn1_wrapper:decode('PrimStrings','IA5',lists:flatten(BytesIA53)),
- ?line IA5_1 = IA5_1r,
-
-
-
-
+ roundtrip('IA5', IA5_1),
ok.
@@ -568,94 +561,60 @@ more_strings(_Rules) ->
%% Ts ::= TeletexString
%%==========================================================
- ?line {ok,BytesTs1} = asn1_wrapper:encode('PrimStrings','Ts',[47,23,99,75,47]),
- ?line {ok,[47,23,99,75,47]} =
- asn1_wrapper:decode('PrimStrings','Ts',lists:flatten(BytesTs1)),
-
- ?line {ok,BytesTs2} = asn1_wrapper:encode('PrimStrings','Ts',[]),
- ?line {ok,[]} = asn1_wrapper:decode('PrimStrings','Ts',lists:flatten(BytesTs2)),
-
+ roundtrip('Ts', [47,23,99,75,47]),
+ roundtrip('Ts', []),
%%==========================================================
%% Vxs ::= VideotexString
%%==========================================================
- ?line {ok,BytesVxs1} = asn1_wrapper:encode('PrimStrings','Vxs',[47,23,99,75,47]),
- ?line {ok,[47,23,99,75,47]} =
- asn1_wrapper:decode('PrimStrings','Vxs',lists:flatten(BytesVxs1)),
-
- ?line {ok,BytesVxs2} = asn1_wrapper:encode('PrimStrings','Vxs',[]),
- ?line {ok,[]} = asn1_wrapper:decode('PrimStrings','Vxs',lists:flatten(BytesVxs2)),
-
+ roundtrip('Vxs', [47,23,99,75,47]),
+ roundtrip('Vxs', []),
%%==========================================================
%% Grs ::= GraphicString
%%==========================================================
- ?line {ok,BytesGrs1} = asn1_wrapper:encode('PrimStrings','Grs',[47,23,99,75,47]),
- ?line {ok,[47,23,99,75,47]} =
- asn1_wrapper:decode('PrimStrings','Grs',lists:flatten(BytesGrs1)),
-
- ?line {ok,BytesGrs2} = asn1_wrapper:encode('PrimStrings','Grs',[]),
- ?line {ok,[]} = asn1_wrapper:decode('PrimStrings','Grs',lists:flatten(BytesGrs2)),
+ roundtrip('Grs',[47,23,99,75,47]),
+ roundtrip('Grs', []),
%%==========================================================
%% ODesc ::= ObjectDescriptor, test case for OTP-4161
%%==========================================================
- ?line {ok,BytesODesc1} = asn1_wrapper:encode('PrimStrings','ODesc',[79,98,106,101,99,116,68,101,115,99,114,105,112,116,111,114]),
- ?line {ok,[79,98,106,101,99,116,68,101,115,99,114,105,112,116,111,114]} =
- asn1_wrapper:decode('PrimStrings','ODesc',lists:flatten(BytesODesc1)),
-
- ?line {ok,BytesODesc2} = asn1_wrapper:encode('PrimStrings','ODesc',[]),
- ?line {ok,[]} = asn1_wrapper:decode('PrimStrings','ODesc',lists:flatten(BytesODesc2)),
+ roundtrip('ODesc', [79,98,106,101,99,116,68,101,115,99,114,
+ 105,112,116,111,114]),
+ roundtrip('ODesc', []),
%%==========================================================
%% Ges ::= GeneralString
%%==========================================================
- ?line {ok,BytesGes1} = asn1_wrapper:encode('PrimStrings','Ges',[47,23,99,75,47]),
- ?line {ok,[47,23,99,75,47]} =
- asn1_wrapper:decode('PrimStrings','Ges',lists:flatten(BytesGes1)),
-
- ?line {ok,BytesGes2} = asn1_wrapper:encode('PrimStrings','Ges',[]),
- ?line {ok,[]} = asn1_wrapper:decode('PrimStrings','Ges',lists:flatten(BytesGes2)),
-
- ok.
+ roundtrip('Ges', [47,23,99,75,47]),
+ roundtrip('Ges', []),
+ ok.
universal_string(Rules) ->
-
%%==========================================================
%% Us ::= UniversalString
%%==========================================================
- ?line {ok,Bytes1} =
- asn1_wrapper:encode('PrimStrings','Us',[{47,23,99,47},{0,0,55,66}]),
- ?line {ok,[{47,23,99,47},{0,0,55,66}]} =
- asn1_wrapper:decode('PrimStrings','Us',lists:flatten(Bytes1)),
+ roundtrip('Us', [{47,23,99,47},{0,0,55,66}]),
?line {ok,Bytes2} =
asn1_wrapper:encode('PrimStrings','Us',[{47,23,99,255},{0,0,0,201}]),
?line {ok,[{47,23,99,255},201]} =
asn1_wrapper:decode('PrimStrings','Us',lists:flatten(Bytes2)),
- ?line {ok,Bytes3} = asn1_wrapper:encode('PrimStrings','Us',"Universal String"),
- ?line {ok,"Universal String"} =
- asn1_wrapper:decode('PrimStrings','Us',lists:flatten(Bytes3)),
-
- ?line {ok,Bytes4} = asn1_wrapper:encode('PrimStrings','Us',[]),
- ?line {ok,[]} = asn1_wrapper:decode('PrimStrings','Us',lists:flatten(Bytes4)),
-
- ?line {ok,Bytes5} = asn1_wrapper:encode('PrimStrings','Us',[{47,23,99,47}]),
- ?line {ok,[{47,23,99,47}]} =
- asn1_wrapper:decode('PrimStrings','Us',lists:flatten(Bytes5)),
-
+ roundtrip('Us', "Universal String"),
+ roundtrip('Us', []),
+ roundtrip('Us', [{47,23,99,47}]),
?line case asn1_wrapper:erule(Rules) of
ber ->
@@ -670,32 +629,22 @@ universal_string(Rules) ->
Us1 = "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890",
- ?line {ok,Bytes15} = asn1_wrapper:encode('PrimStrings','IA5',Us1),
- ?line {ok,Us1r} = asn1_wrapper:decode('PrimStrings','IA5',lists:flatten(Bytes15)),
- ?line Us1 = Us1r,
-
+ roundtrip('IA5', Us1),
%%==========================================================
%% UsCon ::= [70] UniversalString
%%==========================================================
- ?line {ok,Bytes11} =
- asn1_wrapper:encode('PrimStrings','UsCon',[{47,23,99,255},{0,0,2,201}]),
- ?line {ok,[{47,23,99,255},{0,0,2,201}]} =
- asn1_wrapper:decode('PrimStrings','UsCon',lists:flatten(Bytes11)),
+ roundtrip('UsCon', [{47,23,99,255},{0,0,2,201}]),
?line {ok,Bytes12} =
asn1_wrapper:encode('PrimStrings','UsCon',[{47,23,99,255},{0,0,0,201}]),
?line {ok,[{47,23,99,255},201]} =
asn1_wrapper:decode('PrimStrings','UsCon',lists:flatten(Bytes12)),
- ?line {ok,Bytes13} = asn1_wrapper:encode('PrimStrings','UsCon',"Universal String"),
- ?line {ok,"Universal String"} =
- asn1_wrapper:decode('PrimStrings','UsCon',lists:flatten(Bytes13)),
-
- ?line {ok,Bytes14} = asn1_wrapper:encode('PrimStrings','UsCon',[]),
- ?line {ok,[]} = asn1_wrapper:decode('PrimStrings','UsCon',lists:flatten(Bytes14)),
+ roundtrip('UsCon', "Universal String"),
+ roundtrip('UsCon', []),
?line case asn1_wrapper:erule(Rules) of
ber ->
@@ -712,25 +661,15 @@ universal_string(Rules) ->
%% UsExpCon ::= [71] EXPLICIT UniversalString
%%==========================================================
- ?line {ok,Bytes21} =
- asn1_wrapper:encode('PrimStrings','UsExpCon',[{47,23,99,255},{0,0,2,201}]),
- ?line {ok,[{47,23,99,255},{0,0,2,201}]} =
- asn1_wrapper:decode('PrimStrings','UsExpCon',lists:flatten(Bytes21)),
+ roundtrip('UsExpCon', [{47,23,99,255},{0,0,2,201}]),
?line {ok,Bytes22} =
asn1_wrapper:encode('PrimStrings','UsExpCon',[{47,23,99,255},{0,0,0,201}]),
?line {ok,[{47,23,99,255},201]} =
asn1_wrapper:decode('PrimStrings','UsExpCon',lists:flatten(Bytes22)),
- ?line {ok,Bytes23} =
- asn1_wrapper:encode('PrimStrings','UsExpCon',"Universal String"),
- ?line {ok,"Universal String"} =
- asn1_wrapper:decode('PrimStrings','UsExpCon',lists:flatten(Bytes23)),
-
- ?line {ok,Bytes24} =
- asn1_wrapper:encode('PrimStrings','UsExpCon',[]),
- ?line {ok,[]} =
- asn1_wrapper:decode('PrimStrings','UsExpCon',lists:flatten(Bytes24)),
+ roundtrip('UsExpCon', "Universal String"),
+ roundtrip('UsExpCon', []),
?line case asn1_wrapper:erule(Rules) of
ber ->
@@ -740,12 +679,8 @@ universal_string(Rules) ->
asn1_wrapper:decode('PrimStrings','UsExpCon',lists:flatten([16#BF,16#47,16,60,16#80,28,4,47,23,99,255,28,4,0,0,2,201,0,0]));
_ -> ok
end,
-
-
-ok.
-
-
+ ok.
bmp_string(_Rules) ->
@@ -754,29 +689,18 @@ bmp_string(_Rules) ->
%% BMP ::= BMPString
%%==========================================================
- ?line {ok,Bytes1} =
- asn1_wrapper:encode('PrimStrings','BMP',[{0,0,99,48},{0,0,2,201}]),
- ?line {ok,[{0,0,99,48},{0,0,2,201}]} =
- asn1_wrapper:decode('PrimStrings','BMP',lists:flatten(Bytes1)),
+ roundtrip('BMP', [{0,0,99,48},{0,0,2,201}]),
?line {ok,Bytes2} =
asn1_wrapper:encode('PrimStrings','BMP',[{0,0,0,48},{0,0,2,201}]),
?line {ok,[48,{0,0,2,201}]} =
asn1_wrapper:decode('PrimStrings','BMP',lists:flatten(Bytes2)),
-
- ?line {ok,Bytes3} = asn1_wrapper:encode('PrimStrings','BMP',"BMP String"),
- ?line {ok,"BMP String"} =
- asn1_wrapper:decode('PrimStrings','BMP',lists:flatten(Bytes3)),
-
- ?line {ok,Bytes4} = asn1_wrapper:encode('PrimStrings','BMP',[]),
- ?line {ok,[]} = asn1_wrapper:decode('PrimStrings','BMP',lists:flatten(Bytes4)),
+ roundtrip('BMP', "BMP String"),
+ roundtrip('BMP', []),
BMP1 = "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890",
- ?line {ok,Bytes5} = asn1_wrapper:encode('PrimStrings','BMP',BMP1),
- ?line {ok,BMP1r} = asn1_wrapper:decode('PrimStrings','BMP',lists:flatten(Bytes5)),
- ?line BMP1 = BMP1r,
-
+ roundtrip('BMP', BMP1),
ok.
@@ -790,35 +714,17 @@ times(_Rules) ->
%% Gt ::= GeneralizedTime
%%==========================================================
- ?line {ok,Bytes1} = asn1_wrapper:encode('PrimStrings','Gt',"19970923110723.2"),
- ?line {ok,"19970923110723.2"} =
- asn1_wrapper:decode('PrimStrings','Gt',lists:flatten(Bytes1)),
+ roundtrip('Gt', "19970923110723.2"),
+ roundtrip('Gt', "19970923110723.2Z"),
+ roundtrip('Gt', "19970923110723.2-0500"),
- ?line {ok,Bytes2} = asn1_wrapper:encode('PrimStrings','Gt',"19970923110723.2Z"),
- ?line {ok,"19970923110723.2Z"} =
- asn1_wrapper:decode('PrimStrings','Gt',lists:flatten(Bytes2)),
-
- ?line {ok,Bytes3} = asn1_wrapper:encode('PrimStrings','Gt',"19970923110723.2-0500"),
- ?line {ok,"19970923110723.2-0500"} =
- asn1_wrapper:decode('PrimStrings','Gt',lists:flatten(Bytes3)),
-
-
-
-
-
-
%%==========================================================
%% UTC ::= UTCTime
%%==========================================================
- ?line {ok,Bytes11} = asn1_wrapper:encode('PrimStrings','UTC',"9709211107Z"),
- ?line {ok,"9709211107Z"} =
- asn1_wrapper:decode('PrimStrings','UTC',lists:flatten(Bytes11)),
-
- ?line {ok,Bytes12} = asn1_wrapper:encode('PrimStrings','UTC',"9709211107-0500"),
- ?line {ok,"9709211107-0500"} =
- asn1_wrapper:decode('PrimStrings','UTC',lists:flatten(Bytes12)),
+ roundtrip('UTC', "9709211107Z"),
+ roundtrip('UTC', "9709211107-0500"),
ok.
@@ -917,3 +823,8 @@ wrapper_utf8_binary_to_list(L) when is_list(L) ->
asn1rt:utf8_binary_to_list(list_to_binary(L));
wrapper_utf8_binary_to_list(B) ->
asn1rt:utf8_binary_to_list(B).
+
+roundtrip(Type, Value) ->
+ {ok,Encoded} = 'PrimStrings':encode(Type, Value),
+ {ok,Value} = 'PrimStrings':decode(Type, Encoded),
+ ok.
diff --git a/lib/asn1/test/testSSLspecs.erl b/lib/asn1/test/testSSLspecs.erl
index 51ef134e5f..45c5da50f0 100644
--- a/lib/asn1/test/testSSLspecs.erl
+++ b/lib/asn1/test/testSSLspecs.erl
@@ -42,11 +42,11 @@ compile(Config, Options) ->
asn1_test_lib:compile_all(["PKIX1Explicit93", "PKIX1Implicit93"],
Config, NewOptions).
-compile_inline(Config, Rule) when Rule == ber_bin; Rule == ber_bin_v2 ->
+compile_inline(Config, ber=Rule) ->
DataDir = ?config(data_dir, Config),
CaseDir = ?config(case_dir, Config),
Options = [{i, CaseDir}, {i, DataDir}, Rule,
- der, compact_bit_string, optimize, asn1config, inline],
+ der, compact_bit_string, asn1config, inline],
ok = remove_db_file_inline(CaseDir),
asn1_test_lib:compile("OTP-PKIX.set.asn", Config, Options);
compile_inline(_Config, _Rule) ->
@@ -73,7 +73,7 @@ remove_db_file_inline(Dir) ->
?line ok = remove_db_file(Dir ++ "PKIX1Explicit88.asn1db"),
?line ok = remove_db_file(Dir ++ "PKIX1Implicit88.asn1db").
-run(BER) when BER==ber_bin;BER==ber_bin_v2 ->
+run(ber) ->
run1(1);
run(_) ->
ok.
@@ -100,20 +100,20 @@ transform1(ATAV) ->
?line {ok, ATAVEnc} = 'PKIX1Explicit88':encode('AttributeTypeAndValue',
ATAV),
?line {ok, _ATAVDec} = 'SSL-PKIX':decode('AttributeTypeAndValue',
- list_to_binary(ATAVEnc)).
+ ATAVEnc).
transform2(ATAV) ->
?line {ok, ATAVEnc} = 'PKIX1Explicit88':encode('AttributeTypeAndValue',
ATAV),
?line {ok, _ATAVDec} = 'PKIX1Explicit88':decode('AttributeTypeAndValue',
- list_to_binary(ATAVEnc)).
+ ATAVEnc).
transform4(ATAV) ->
?line {ok, ATAVEnc} = 'PKIX1Explicit88':encode('Attribute',
ATAV),
?line {ok, _ATAVDec} = 'PKIX1Explicit88':decode('Attribute',
- list_to_binary(ATAVEnc)).
+ ATAVEnc).
ex(1) ->
@@ -146,7 +146,7 @@ ex(7) ->
{1,2,840,113549,1,9,1},
[[19,5,111,116,112,67,65]]}.
-run_inline(Rule) when Rule==ber_bin;Rule==ber_bin_v2 ->
+run_inline(ber) ->
Cert = cert(),
?line {ok,{'CertificatePKIX1Explicit88',{Type,UnDec},_,_}} = 'OTP-PKIX':decode_TBSCert_exclusive(Cert),
?line {ok,_} = 'OTP-PKIX':decode_part(Type,UnDec),
diff --git a/lib/asn1/test/testSeqExtension.erl b/lib/asn1/test/testSeqExtension.erl
index 7c77ab87e9..1128d9a7c3 100644
--- a/lib/asn1/test/testSeqExtension.erl
+++ b/lib/asn1/test/testSeqExtension.erl
@@ -20,7 +20,7 @@
-module(testSeqExtension).
-include("External.hrl").
--export([main/1]).
+-export([main/2]).
-include_lib("test_server/include/test_server.hrl").
@@ -28,70 +28,73 @@
-record('SeqExt2',{bool, int}).
-record('SeqExt3',{bool, int}).
-record('SeqExt4',{bool, int}).
-
-
-main(_Rules) ->
-
- ?line {ok,Bytes11} =
- asn1_wrapper:encode('SeqExtension','SeqExt1',#'SeqExt1'{}),
- ?line {ok,{'SeqExt1'}} =
- asn1_wrapper:decode('SeqExtension','SeqExt1',lists:flatten(Bytes11)),
-
- ?line {ok,Bytes21} =
- asn1_wrapper:encode('SeqExtension','SeqExt2',#'SeqExt2'{bool = true,int = 99}),
- ?line {ok,{'SeqExt2',true,99}} =
- asn1_wrapper:decode('SeqExtension','SeqExt2',lists:flatten(Bytes21)),
-
- ?line {ok,Bytes22} =
- asn1_wrapper:encode('SeqExtension','SeqExt2',#'SeqExt2'{int = 99,bool = true}),
- ?line {ok,{'SeqExt2',true,99}} =
- asn1_wrapper:decode('SeqExtension','SeqExt2',lists:flatten(Bytes22)),
-
- ?line {ok,Bytes31} =
- asn1_wrapper:encode('SeqExtension','SeqExt3',#'SeqExt3'{bool = true,int = 99}),
- ?line {ok,{'SeqExt3',true,99}} =
- asn1_wrapper:decode('SeqExtension','SeqExt3',lists:flatten(Bytes31)),
-
- ?line {ok,Bytes32} =
- asn1_wrapper:encode('SeqExtension','SeqExt3',#'SeqExt3'{int = 99,bool = true}),
- ?line {ok,{'SeqExt3',true,99}} =
- asn1_wrapper:decode('SeqExtension','SeqExt3',lists:flatten(Bytes32)),
-
- ?line {ok,Bytes41} =
- asn1_wrapper:encode('SeqExtension','SeqExt4',#'SeqExt4'{bool = true,int = 99}),
- ?line {ok,{'SeqExt4',true,99}} =
- asn1_wrapper:decode('SeqExtension','SeqExt4',lists:flatten(Bytes41)),
-
- ?line {ok,Bytes42} =
- asn1_wrapper:encode('SeqExtension','SeqExt4',#'SeqExt4'{int = 99,bool = true}),
- ?line {ok,{'SeqExt4',true,99}} =
- asn1_wrapper:decode('SeqExtension','SeqExt4',lists:flatten(Bytes42)),
-
-
- % test of extension , not ready
-
- ?line {ok,BytesX11} =
- asn1_wrapper:encode('SeqExtension','SeqExt1',#'SeqExt1'{}),
- ?line {ok,{'SeqExt1'}} =
- asn1_wrapper:decode('SeqExtension','SeqExt1',lists:flatten(BytesX11)),
-
- ?line {ok,BytesX21} =
- asn1_wrapper:encode('SeqExtension','SeqExt2',#'SeqExt2'{bool = true,int = 99}),
- ?line {ok,{'SeqExt2',true,99}} =
- asn1_wrapper:decode('SeqExtension','SeqExt2',lists:flatten(BytesX21)),
-
- ?line {ok,BytesX22} =
- asn1_wrapper:encode('SeqExtension','SeqExt2',#'SeqExt2'{int = 99,bool = true}),
- ?line {ok,{'SeqExt2',true,99}} =
- asn1_wrapper:decode('SeqExtension','SeqExt2',lists:flatten(BytesX22)),
-
-
-
-
-
+-record('SeqExt5',{name, shoesize}).
+-record('SeqExt6',{i1,i2,i3,i4,i5,i6,i7}).
+-record('SuperSeq',{s1,s2,s3,s4,s5,s6,i}).
+
+main(DataDir, Opts) ->
+ roundtrip('SeqExt1', #'SeqExt1'{}),
+
+ roundtrip('SeqExt2', #'SeqExt2'{bool=true,int=99}),
+ roundtrip('SeqExt2', #'SeqExt2'{bool=false,int=42}),
+
+ roundtrip('SeqExt3', #'SeqExt3'{bool=true,int=-77777}),
+ roundtrip('SeqExt3', #'SeqExt3'{bool=false,int=-42000}),
+
+ roundtrip('SeqExt4', #'SeqExt4'{bool=true,int=12345}),
+ roundtrip('SeqExt4', #'SeqExt4'{bool=false,int=123456}),
+
+ roundtrip('SeqExt5', #'SeqExt5'{name="Arne",shoesize=47}),
+
+ %% Encode a value with this version of the specification.
+ BigInt = 128638468966,
+ SuperSeq = #'SuperSeq'{s1=#'SeqExt1'{},
+ s2=#'SeqExt2'{bool=true,int=2345},
+ s3=#'SeqExt3'{bool=false,int=17},
+ s4=#'SeqExt4'{bool=true,int=38739739},
+ s5=#'SeqExt5'{name="Arne",shoesize=47},
+ s6=#'SeqExt6'{i1=531,i2=601,i3=999,
+ i4=777,i5=11953,
+ i6=13553,i7=77777},
+ i=BigInt
+ },
+ {ok,SuperSeqEnc} = 'SeqExtension':encode('SuperSeq', SuperSeq),
+ {ok,SuperSeq} = 'SeqExtension':decode('SuperSeq', SuperSeqEnc),
+
+ %% Remove all extensions from the ASN.1 specification and compile it.
+ CaseDir = filename:dirname(code:which('SeqExtension')),
+ Asn1SrcBase = "SeqExtension.asn1",
+ Asn1SrcFile0 = filename:join(DataDir, Asn1SrcBase),
+ {ok,Src0} = file:read_file(Asn1SrcFile0),
+ %% Remove all declarations following "...," up to the end
+ %% of the SEQUENCE.
+ Src1 = re:replace(Src0, "[.][.][.],[^}]*", "...\n",
+ [global,{return,binary}]),
+ %% Remove the last double bracket group in the SEQUENCE.
+ Src = re:replace(Src1, ",\\s*\\[\\[.*?\\]\\]\\s*\\}", "\n}",
+ [global,{return,binary}]),
+ io:format("~s\n\n", [Src]),
+ Asn1SrcFile = filename:join(CaseDir, Asn1SrcBase),
+ ok = file:write_file(Asn1SrcFile, Src),
+ ok = asn1ct:compile(Asn1SrcFile,
+ [{i,DataDir},{outdir,CaseDir}|Opts]),
+
+ %% Decode the encoded sequence with the version of the spec
+ %% with no extensions following the extension marks
+ %% (except in SeqExt6). The integer 'i' at the end
+ %% of the sequence must still be the correct integer (otherwise
+ %% some extension has not been skipped correctly).
+ {ok,DecodedSuperSeq} = 'SeqExtension':decode('SuperSeq', SuperSeqEnc),
+ #'SuperSeq'{s1={'SeqExt1'},
+ s2=#'SeqExt2'{bool=true,int=2345},
+ s3={'SeqExt3'},
+ s4={'SeqExt4',true},
+ s5={'SeqExt5'},
+ s6={'SeqExt6',531,601,999,777,11953},
+ i=BigInt} = DecodedSuperSeq,
ok.
-
-
-
-
+roundtrip(Type, Value) ->
+ {ok,Encoded} = 'SeqExtension':encode(Type, Value),
+ {ok,Value} = 'SeqExtension':decode(Type, Encoded),
+ ok.
diff --git a/lib/asn1/test/testSeqIndefinite.erl b/lib/asn1/test/testSeqIndefinite.erl
index 25742474bb..c7b8aba523 100644
--- a/lib/asn1/test/testSeqIndefinite.erl
+++ b/lib/asn1/test/testSeqIndefinite.erl
@@ -23,13 +23,7 @@
-include_lib("test_server/include/test_server.hrl").
-
-main(per_bin) -> ok;
main(per) -> ok;
-main(ber_bin_v2) ->
- main(ber);
-main(ber_bin) ->
- main(ber);
main(ber) ->
%% normal encoding
diff --git a/lib/asn1/test/testSeqOf.erl b/lib/asn1/test/testSeqOf.erl
index 0c0bbc3e66..1aa1eab26d 100644
--- a/lib/asn1/test/testSeqOf.erl
+++ b/lib/asn1/test/testSeqOf.erl
@@ -198,19 +198,10 @@ main(Rules) ->
?line {ok,Bytes51} = asn1_wrapper:encode('SeqOf','SeqEmp',#'SeqEmp'{seq1 = [#'Empty'{}]}),
?line {ok,{'SeqEmp',[{'Empty'}]}} = asn1_wrapper:decode('SeqOf','SeqEmp',lists:flatten(Bytes51)),
-
- case Rules of
- ber ->
- ?line {ok,Bytes52} = asn1_wrapper:encode('SeqOfEnum','SeqOfEnum',
- {'SeqOfEnum',[{'Enum',a},{'Enum',b}]}),
- ?line {ok,[a,b]} = asn1_wrapper:decode('SeqOfEnum','SeqOfEnum',
- lists:flatten(Bytes52));
- _ -> ok
- end,
%% tests of OTP-4590
case Rules of
- PER when PER == per; PER == per_bin ->
+ per ->
DayNames = ["Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"],
?line {ok,Bytes60} = asn1_wrapper:encode('XSeqOf','DayNames2',DayNames),
?line {ok,Bytes60} = asn1_wrapper:encode('XSeqOf','DayNames4',DayNames),
diff --git a/lib/asn1/test/testSetIndefinite.erl b/lib/asn1/test/testSetIndefinite.erl
index d8e2b6a9cf..73006da62b 100644
--- a/lib/asn1/test/testSetIndefinite.erl
+++ b/lib/asn1/test/testSetIndefinite.erl
@@ -24,12 +24,7 @@
-include_lib("test_server/include/test_server.hrl").
-main(per_bin) -> ok;
main(per) -> ok;
-main(ber_bin_v2) ->
- main(ber);
-main(ber_bin) ->
- main(ber);
main(ber) ->
%% normal encoding
diff --git a/lib/asn1/test/testSetOptional.erl b/lib/asn1/test/testSetOptional.erl
index 4692941524..bb43ff0a96 100644
--- a/lib/asn1/test/testSetOptional.erl
+++ b/lib/asn1/test/testSetOptional.erl
@@ -21,8 +21,7 @@
-include("External.hrl").
-export([main/1]).
--export([ticket_7533/1,decoder/4]).
--include_lib("test_server/include/test_server.hrl").
+-export([ticket_7533/1]).
-record('SetOpt1',{bool1 = asn1_NOVALUE, int1, set1 = asn1_NOVALUE}).
-record('SetOpt1Imp',{bool1 = asn1_NOVALUE, int1, set1 = asn1_NOVALUE}).
@@ -36,171 +35,64 @@
-record('SetIn',{boolIn, intIn}).
main(_Rules) ->
+ roundtrip('SetOpt1',
+ #'SetOpt1'{bool1=true,int1=15,
+ set1=#'SetIn'{boolIn=true,intIn=66}}),
+ roundtrip('SetOpt1', #'SetOpt1'{int1=15}),
+
+ roundtrip('SetOpt2', #'SetOpt2'{bool2=true,int2=15,
+ set2=#'SetIn'{boolIn=true,intIn=66}}),
+ roundtrip('SetOpt2', #'SetOpt2'{int2=15,bool2=true}),
+
+ roundtrip('SetOpt3', #'SetOpt3'{bool3=true,int3=15,
+ set3=#'SetIn'{boolIn=true,intIn=66}}),
+ roundtrip('SetOpt3', #'SetOpt3'{int3=15}),
+
+ roundtrip('SetOpt1Imp',
+ #'SetOpt1Imp'{bool1=true,int1 = 15,
+ set1=#'SetIn'{boolIn = true,intIn = 66}}),
+ roundtrip('SetOpt1Imp', #'SetOpt1Imp'{int1=15}),
- ?line {ok,Bytes11} =
- asn1_wrapper:encode('SetOptional','SetOpt1',#'SetOpt1'{bool1 = true,
- int1 = 15,
- set1 = #'SetIn'{boolIn = true,
- intIn = 66}}),
- ?line {ok,{'SetOpt1',true,15,{'SetIn',true,66}}} =
- asn1_wrapper:decode('SetOptional','SetOpt1',lists:flatten(Bytes11)),
-
-
- ?line {ok,Bytes12} = asn1_wrapper:encode('SetOptional','SetOpt1',#'SetOpt1'{int1 = 15}),
- ?line {ok,{'SetOpt1',asn1_NOVALUE,15,asn1_NOVALUE}} =
- asn1_wrapper:decode('SetOptional','SetOpt1',lists:flatten(Bytes12)),
-
-
- ?line {ok,Bytes21} =
- asn1_wrapper:encode('SetOptional','SetOpt2',#'SetOpt2'{bool2 = true,
- int2 = 15,
- set2 = #'SetIn'{boolIn = true,
- intIn = 66}}),
- ?line {ok,{'SetOpt2',{'SetIn',true,66},true,15}} =
- asn1_wrapper:decode('SetOptional','SetOpt2',lists:flatten(Bytes21)),
-
-
- ?line {ok,Bytes22} = asn1_wrapper:encode('SetOptional','SetOpt2',#'SetOpt2'{int2 = 15,
- bool2 = true}),
- ?line {ok,{'SetOpt2',asn1_NOVALUE,true,15}} =
- asn1_wrapper:decode('SetOptional','SetOpt2',lists:flatten(Bytes22)),
-
-
-
- ?line {ok,Bytes31} =
- asn1_wrapper:encode('SetOptional','SetOpt3',#'SetOpt3'{bool3 = true,
- int3 = 15,
- set3 = #'SetIn'{boolIn = true,
- intIn = 66}}),
- ?line {ok,{'SetOpt3',true,{'SetIn',true,66},15}} =
- asn1_wrapper:decode('SetOptional','SetOpt3',lists:flatten(Bytes31)),
-
-
- ?line {ok,Bytes32} = asn1_wrapper:encode('SetOptional','SetOpt3',#'SetOpt3'{int3 = 15}),
- ?line {ok,{'SetOpt3',asn1_NOVALUE,asn1_NOVALUE,15}} =
- asn1_wrapper:decode('SetOptional','SetOpt3',lists:flatten(Bytes32)),
-
-
-
-
-
- ?line {ok,Bytes41} =
- asn1_wrapper:encode('SetOptional','SetOpt1Imp',#'SetOpt1Imp'{bool1 = true,
- int1 = 15,
- set1 = #'SetIn'{boolIn = true,
- intIn = 66}}),
- ?line {ok,{'SetOpt1Imp',true,15,{'SetIn',true,66}}} =
- asn1_wrapper:decode('SetOptional','SetOpt1Imp',lists:flatten(Bytes41)),
-
-
- ?line {ok,Bytes42} = asn1_wrapper:encode('SetOptional','SetOpt1Imp',#'SetOpt1Imp'{int1 = 15}),
- ?line {ok,{'SetOpt1Imp',asn1_NOVALUE,15,asn1_NOVALUE}} =
- asn1_wrapper:decode('SetOptional','SetOpt1Imp',lists:flatten(Bytes42)),
-
-
- ?line {ok,Bytes51} =
- asn1_wrapper:encode('SetOptional','SetOpt2Imp',#'SetOpt2Imp'{bool2 = true,
- int2 = 15,
- set2 = #'SetIn'{boolIn = true,
- intIn = 66}}),
- ?line {ok,{'SetOpt2Imp',{'SetIn',true,66},true,15}} =
- asn1_wrapper:decode('SetOptional','SetOpt2Imp',lists:flatten(Bytes51)),
-
-
- ?line {ok,Bytes52} = asn1_wrapper:encode('SetOptional','SetOpt2Imp',#'SetOpt2Imp'{int2 = 15,
- bool2 = true}),
- ?line {ok,{'SetOpt2Imp',asn1_NOVALUE,true,15}} =
- asn1_wrapper:decode('SetOptional','SetOpt2Imp',lists:flatten(Bytes52)),
-
-
-
- ?line {ok,Bytes61} =
- asn1_wrapper:encode('SetOptional','SetOpt3Imp',#'SetOpt3Imp'{bool3 = true,
- int3 = 15,
- set3 = #'SetIn'{boolIn = true,
- intIn = 66}}),
- ?line {ok,{'SetOpt3Imp',true,{'SetIn',true,66},15}} =
- asn1_wrapper:decode('SetOptional','SetOpt3Imp',lists:flatten(Bytes61)),
-
-
- ?line {ok,Bytes62} = asn1_wrapper:encode('SetOptional','SetOpt3Imp',#'SetOpt3Imp'{int3 = 15}),
- ?line {ok,{'SetOpt3Imp',asn1_NOVALUE,asn1_NOVALUE,15}} =
- asn1_wrapper:decode('SetOptional','SetOpt3Imp',lists:flatten(Bytes62)),
-
-
-
-
-
-
- ?line {ok,Bytes71} =
- asn1_wrapper:encode('SetOptional','SetOpt1Exp',#'SetOpt1Exp'{bool1 = true,
- int1 = 15,
- set1 = #'SetIn'{boolIn = true,
- intIn = 66}}),
- ?line {ok,{'SetOpt1Exp',true,15,{'SetIn',true,66}}} =
- asn1_wrapper:decode('SetOptional','SetOpt1Exp',lists:flatten(Bytes71)),
-
-
- ?line {ok,Bytes72} = asn1_wrapper:encode('SetOptional','SetOpt1Exp',#'SetOpt1Exp'{int1 = 15}),
- ?line {ok,{'SetOpt1Exp',asn1_NOVALUE,15,asn1_NOVALUE}} =
- asn1_wrapper:decode('SetOptional','SetOpt1Exp',lists:flatten(Bytes72)),
-
-
- ?line {ok,Bytes81} =
- asn1_wrapper:encode('SetOptional','SetOpt2Exp',#'SetOpt2Exp'{bool2 = true,
- int2 = 15,
- set2 = #'SetIn'{boolIn = true,
- intIn = 66}}),
- ?line {ok,{'SetOpt2Exp',{'SetIn',true,66},true,15}} =
- asn1_wrapper:decode('SetOptional','SetOpt2Exp',lists:flatten(Bytes81)),
-
-
- ?line {ok,Bytes82} = asn1_wrapper:encode('SetOptional','SetOpt2Exp',#'SetOpt2Exp'{int2 = 15,
- bool2 = true}),
- ?line {ok,{'SetOpt2Exp',asn1_NOVALUE,true,15}} =
- asn1_wrapper:decode('SetOptional','SetOpt2Exp',lists:flatten(Bytes82)),
-
-
-
- ?line {ok,Bytes91} =
- asn1_wrapper:encode('SetOptional','SetOpt3Exp',#'SetOpt3Exp'{bool3 = true,
- int3 = 15,
- set3 = #'SetIn'{boolIn = true,
- intIn = 66}}),
- ?line {ok,{'SetOpt3Exp',true,{'SetIn',true,66},15}} =
- asn1_wrapper:decode('SetOptional','SetOpt3Exp',lists:flatten(Bytes91)),
-
-
- ?line {ok,Bytes92} = asn1_wrapper:encode('SetOptional','SetOpt3Exp',#'SetOpt3Exp'{int3 = 15}),
- ?line {ok,{'SetOpt3Exp',asn1_NOVALUE,asn1_NOVALUE,15}} =
- asn1_wrapper:decode('SetOptional','SetOpt3Exp',lists:flatten(Bytes92)),
-
+
+ roundtrip('SetOpt2Imp',
+ #'SetOpt2Imp'{bool2=true,int2=15,
+ set2=#'SetIn'{boolIn=true,intIn=66}}),
+ roundtrip('SetOpt2Imp',#'SetOpt2Imp'{int2=15,bool2=true}),
+
+ roundtrip('SetOpt3Imp',
+ #'SetOpt3Imp'{bool3=true,int3=15,
+ set3=#'SetIn'{boolIn=true,intIn=66}}),
+ roundtrip('SetOpt3Imp', #'SetOpt3Imp'{int3=15}),
+
+ roundtrip('SetOpt1Exp',
+ #'SetOpt1Exp'{bool1=true,int1=15,
+ set1=#'SetIn'{boolIn=true,intIn=66}}),
+ roundtrip('SetOpt1Exp', #'SetOpt1Exp'{int1=15}),
+
+ roundtrip('SetOpt2Exp',
+ #'SetOpt2Exp'{bool2=true,int2=15,
+ set2=#'SetIn'{boolIn=true,intIn=66}}),
+ roundtrip('SetOpt2Exp', #'SetOpt2Exp'{int2=15,bool2=true}),
+ roundtrip('SetOpt3Exp',
+ #'SetOpt3Exp'{bool3=true,int3=15,
+ set3=#'SetIn'{boolIn=true,intIn=66}}),
+ roundtrip('SetOpt3Exp', #'SetOpt3Exp'{int3=15}),
ok.
-ticket_7533(Ber) when Ber == ber; Ber == ber_bin ->
- Val = #'SetOpt1'{bool1 = true,int1=12,set1=#'SetIn'{boolIn=false,intIn=13}},
- ?line {ok,B} = asn1_wrapper:encode('SetOptional','SetOpt1',Val),
- ?line {ok,Val} = asn1_wrapper:decode('SetOptional','SetOpt1',B),
-
- CorruptVal = [49,14,1,1,255,2,1,12] ++ lists:duplicate(8,0),
- Pid = spawn(?MODULE,decoder,[self(),'SetOptional','SetOpt1',CorruptVal]),
- receive
- {ok,Pid,Result} ->
- io:format("Decode result: ~p~n",[Result]),
- ok
- after 10000 ->
- io:format("Decode timeout~n",[]),
- exit(Pid,normal)
- end;
+ticket_7533(Ber) when Ber == ber ->
+ Val = #'SetOpt1'{bool1=true,int1=12,set1=#'SetIn'{boolIn=false,intIn=13}},
+ roundtrip('SetOpt1', Val),
+ CorruptVal = <<49,14,1,1,255,2,1,12,0:8/unit:8>>,
+ {error,_} = 'SetOptional':decode('SetOpt1', CorruptVal),
+ ok;
ticket_7533(_) ->
ok.
-decoder(Parent,Module,Type,Val) ->
- io:format("Decoding~n",[]),
- ?line {ok,Res} = asn1_wrapper:decode(Module,Type,Val),
- io:format("Decode res: ~p~n",[Res]),
- Parent ! {ok,self(),Res}.
+roundtrip(Type, Value) ->
+ {ok,Encoded} = 'SetOptional':encode(Type, Value),
+ {ok,Value} = 'SetOptional':decode(Type, Encoded),
+ ok.
diff --git a/lib/asn1/test/testTCAP.erl b/lib/asn1/test/testTCAP.erl
index 878ce7c070..b723995e40 100644
--- a/lib/asn1/test/testTCAP.erl
+++ b/lib/asn1/test/testTCAP.erl
@@ -37,7 +37,7 @@ compile_asn1config(Config, Options) ->
asn1_test_lib:compile_all(Files, Config, Options),
asn1_test_lib:compile_erlang("TCAPPackage_msg", Config, []).
-test(Erule,_Config) when Erule==ber;Erule==ber_bin;Erule==ber_bin_v2 ->
+test(ber=Erule,_Config) ->
% ?line OutDir = ?config(priv_dir,Config),
%% testing OTP-4798, open type encoded with indefinite length
?line {ok,_Res} = asn1_wrapper:decode('TCAPMessages-simple','MessageType', val_OTP_4798(Erule)),
@@ -81,7 +81,7 @@ test_asn1config() ->
?line Val2 = 'TCAPPackage_msg':val('TransactionPDU'),
?line {ok,B2} = 'TCAPPackage':encode('TransactionPDU',Val2),
- ?line {ok,ExMsg2}='TCAPPackage':decode_TransactionPDU(list_to_binary(B2)),
+ {ok,ExMsg2}='TCAPPackage':decode_TransactionPDU(B2),
?line {_,_,_,{Key2,ExVal2}}=ExMsg2,
?line {ok,_Parts2}='TCAPPackage':decode_part(Key2,ExVal2),
diff --git a/lib/asn1/test/testTimer.erl b/lib/asn1/test/testTimer.erl
index 2d3b777558..cd7ceb5630 100644
--- a/lib/asn1/test/testTimer.erl
+++ b/lib/asn1/test/testTimer.erl
@@ -133,23 +133,7 @@ go(Config,Enc) ->
Module = 'H323-MESSAGES',
Type = 'H323-UserInformation',
Value = val(),
-%% ok = asn1ct:compile(HelpModule,[Enc]),
-
-%% ok = asn1ct:compile(Module,[Enc]),
- ?line {ok,B} = asn1rt:encode(Module,Type,Value),
- Bytes = case Enc of
- ber_bin ->
- list_to_binary(B);
- per_bin when is_list(B) ->
- list_to_binary(B);
- per_bin ->
- B;
- uper_bin ->
- B;
- _ ->
- %%lists:flatten(B)
- list_to_binary(B)
- end,
+ {ok,Bytes} = asn1rt:encode(Module,Type,Value),
CompileOptions = compile_options(),
@@ -181,35 +165,18 @@ encode(N, Module,Type,Value) ->
decode(0, _Module,_Type,_Value,_Erule) ->
done;
decode(N, Module,Type,Value,Erule) ->
- case Erule of
- ber ->
- ?line {ok,_B} = asn1rt:decode(Module,Type,binary_to_list(Value));
- per ->
- ?line {ok,_B} = asn1rt:decode(Module,Type,binary_to_list(Value));
- _ ->
- ?line {ok,_B} = asn1rt:decode(Module,Type,Value)
- end,
+ {ok,_B} = asn1rt:decode(Module,Type,Value),
decode(N-1, Module,Type,Value,Erule).
compile_options() ->
- ?line {ok,Info} = asn1rt:info('H323-MESSAGES'),
- case lists:keysearch(options,1,Info) of
- {_,{_,Opts}} ->
- Opts2 =
- case lists:member(ber_bin_v2,Opts) of
- true ->
- [ber_bin,optimize] ++ lists:delete(optimize,Opts);
- _ ->
- Opts
- end,
- Opts3 = [X||X <- Opts2,
- (X == ber orelse
- X == ber_bin orelse
- X == per orelse
- X == per_bin orelse
- X == optimize orelse
- X == driver)],
- lists:flatten(io_lib:format("~p",[Opts3]));
+ {ok,Info} = asn1rt:info('H323-MESSAGES'),
+ case lists:keyfind(options, 1, Info) of
+ {_,Opts0} ->
+ Opts1 = [X || X <- Opts0,
+ (X =:= ber orelse
+ X =:= per orelse
+ X =:= uper)],
+ lists:flatten(io_lib:format("~p", [Opts1]));
_ ->
"[]"
end.
diff --git a/lib/asn1/test/testTypeValueNotation.erl b/lib/asn1/test/testTypeValueNotation.erl
index cd5223ef23..59f7385f08 100644
--- a/lib/asn1/test/testTypeValueNotation.erl
+++ b/lib/asn1/test/testTypeValueNotation.erl
@@ -21,11 +21,9 @@
-export([main/2]).
--include_lib("test_server/include/test_server.hrl").
-
-record('Seq', {octstr, int, bool, enum, bitstr, null, oid, vstr}).
-main(Rule, Option) ->
+main(_Rule, _Option) ->
Value1 = #'Seq'{octstr = [1, 2, 3, 4],
int = 12,
bool = true,
@@ -34,28 +32,5 @@ main(Rule, Option) ->
null = 'NULL',
oid = {1, 2, 55},
vstr = "Hello World"},
- Value2 = #'Seq'{octstr = {'OctStr', [1, 2, 3, 4]},
- int = {'Int', 12},
- bool = {'Bool', true},
- enum = {'Enum', a},
- bitstr = {'BitStr', [1, 0, 1, 0]},
- null = {'Null', 'NULL'},
- oid = {'OId', {1, 2, 55}},
- vstr = {'VStr', "Hello World"}},
- main(Rule, Option, Value1, Value2).
-
-%% Value2 will fail for ber_bin_v2, per_bin with nifs (optimize) and uper_bin
-main(ber_bin_v2, _, Value1, Value2) -> encode_fail(Value1, Value2);
-main(per_bin, [optimize], Value1, Value2) -> encode_fail(Value1, Value2);
-main(uper_bin, [], Value1, Value2) -> encode_fail(Value1, Value2);
-main(_, _, Value1, Value2) -> encode_normal(Value1, Value2).
-
-encode_normal(Value1, Value2) ->
- {ok, Bytes} = asn1_wrapper:encode('SeqTypeRefPrim', 'Seq', Value1),
- {ok, Bytes} = asn1_wrapper:encode('SeqTypeRefPrim', 'Seq', Value2),
- {ok, Value1} = asn1_wrapper:decode('SeqTypeRefPrim', 'Seq', Bytes).
-
-encode_fail(Value1, Value2) ->
- {ok, Bytes} = asn1_wrapper:encode('SeqTypeRefPrim', 'Seq', Value1),
- {error, _Reason} = asn1_wrapper:encode('SeqTypeRefPrim', 'Seq', Value2),
- {ok, Value1} = asn1_wrapper:decode('SeqTypeRefPrim', 'Seq', Bytes).
+ {ok, Bytes} = asn1_wrapper:encode('SeqTypeRefPrim', 'Seq', Value1),
+ {ok, Value1} = asn1_wrapper:decode('SeqTypeRefPrim', 'Seq', Bytes).
diff --git a/lib/asn1/test/testX420.erl b/lib/asn1/test/testX420.erl
index abdbbfe536..52b20a2c70 100644
--- a/lib/asn1/test/testX420.erl
+++ b/lib/asn1/test/testX420.erl
@@ -34,7 +34,7 @@ compile(Erule, Options, Config) ->
compile_loop(_Erule, [], _Options, _Config) ->
ok;
compile_loop(Erule, [Spec|Specs], Options, Config)
- when Erule == ber; Erule == ber_bin; Erule == ber_bin_v2; Erule == per ->
+ when Erule =:= ber; Erule =:= per ->
CaseDir = ?config(case_dir, Config),
asn1_test_lib:compile(filename:join([x420, Spec]), Config,
[Erule, {i, CaseDir}]),
diff --git a/lib/asn1/test/test_compile_options.erl b/lib/asn1/test/test_compile_options.erl
index 4e732308d8..b973c5fbcc 100644
--- a/lib/asn1/test/test_compile_options.erl
+++ b/lib/asn1/test/test_compile_options.erl
@@ -92,7 +92,8 @@ noobj(Config) ->
file:delete(filename:join([OutDir,'P-Record.beam'])),
file:delete(filename:join([OutDir,'p_record.erl'])),
file:delete(filename:join([OutDir,'p_record.beam'])),
- ?line ok=asn1ct:compile(filename:join([DataDir,"p_record.set.asn"]),[asn1config,ber_bin,optimize,noobj,{outdir,OutDir}]),
+ ok = asn1ct:compile(filename:join([DataDir,"p_record.set.asn"]),
+ [asn1config,ber,noobj,{outdir,OutDir}]),
%% ?line false = code:is_loaded('P-Record'),
%% ?line false = code:is_loaded('p_record'),
?line {error,enoent} =
diff --git a/lib/asn1/test/test_inline.erl b/lib/asn1/test/test_inline.erl
index 62625572e3..e03ad739f9 100644
--- a/lib/asn1/test/test_inline.erl
+++ b/lib/asn1/test/test_inline.erl
@@ -41,16 +41,16 @@ inline1(Config, Rule, Opt) ->
asn1_test_lib:compile("P-Record", Config, [{inline, 'inlined_P_Record'}|Opt]),
test_inline1(),
- ok=remove_inlined_files2(CaseDir, ber_bin_v2),
+ ok=remove_inlined_files2(CaseDir, ber),
case Rule of
- ber_bin_v2 ->
+ ber ->
asn1_test_lib:compile("P-Record", Config,
- [ber_bin, inline, asn1config, optimize|Opt]),
+ [ber, inline, asn1config|Opt]),
test_inline2(Rule, 'P-Record'),
remove_inlined_files3(CaseDir, Rule),
asn1_test_lib:compile("p_record.set.asn", Config,
- [ber_bin, inline, asn1config, optimize|Opt]),
+ [ber, inline, asn1config|Opt]),
test_inline2(Rule, 'p_record'),
remove_inlined_files4(CaseDir, Rule);
_ ->
@@ -71,12 +71,12 @@ test_inline1() ->
?line {ok,_}=asn1_wrapper:decode('inlined_P_Record',
'PersonnelRecord',Bytes).
-test_inline2(ber_bin_v2,Mod) ->
+test_inline2(ber,Mod) ->
PRecMsg = {'PersonnelRecord',{'Name',"Sven","S","Svensson"},
"manager",123,"20000202",{'Name',"Inga","K","Svensson"},
asn1_DEFAULT},
?line {ok,Bytes} = Mod:encode('PersonnelRecord',PRecMsg),
- ?line {ok,_} = Mod:sel_dec(list_to_binary(Bytes));
+ {ok,_} = Mod:sel_dec(Bytes);
test_inline2(_,_) ->
ok.
@@ -243,7 +243,7 @@ remove_inlined_files2(Dir,Rule) ->
?line ok=file:delete(X)
end,[TargetErl,TargetBeam]),
ok.
-remove_inlined_files3(Dir,ber_bin_v2) ->
+remove_inlined_files3(Dir,ber) ->
Erl=filename:join([Dir,"P-Record.erl"]),
Beam=filename:join([Dir,"P-Record.beam"]),
Asn1DB=filename:join([Dir,"P-Record.asn1db"]),
@@ -255,7 +255,7 @@ remove_inlined_files3(Dir,ber_bin_v2) ->
remove_inlined_files3(_,_) ->
ok.
-remove_inlined_files4(Dir,ber_bin_v2) ->
+remove_inlined_files4(Dir,ber) ->
Erl=filename:join([Dir,"p_record.erl"]),
Beam=filename:join([Dir,"p_record.beam"]),
Asn1DB=filename:join([Dir,"p_record.asn1db"]),
diff --git a/lib/asn1/test/test_partial_incomplete_decode.erl b/lib/asn1/test/test_partial_incomplete_decode.erl
index df56c27115..8ede06938d 100644
--- a/lib/asn1/test/test_partial_incomplete_decode.erl
+++ b/lib/asn1/test/test_partial_incomplete_decode.erl
@@ -188,7 +188,7 @@ decode_parts('S1_2',PartDecMsg) ->
msg('F') ->
- {'F',{fb,{'E',35,[{'D',3,true},{'D',4,false},{'D',5,true},{'D',6,true},{'D',7,false},{'D',8,true},{'D',9,true},{'D',10,false},{'D',11,true},{'D',12,true},{'D',13,false},{'D',14,true}],false,{da,[{'A',16,{'D',17,true}}]}}}};
+ {fb,{'E',35,[{'D',3,true},{'D',4,false},{'D',5,true},{'D',6,true},{'D',7,false},{'D',8,true},{'D',9,true},{'D',10,false},{'D',11,true},{'D',12,true},{'D',13,false},{'D',14,true}],false,{da,[{'A',16,{'D',17,true}}]}}};
msg('F3') ->
{fb,{'E',10,[{'D',11,true},{'D',12,false}],false,{dc,{'E_d_dc',13,true,{'E_d_dc_dcc',14,15}}}}};
diff --git a/lib/asn1/test/test_selective_decode.erl b/lib/asn1/test/test_selective_decode.erl
index bb348611da..ebe1296cf3 100644
--- a/lib/asn1/test/test_selective_decode.erl
+++ b/lib/asn1/test/test_selective_decode.erl
@@ -53,7 +53,7 @@ test() ->
msg('F') ->
- {'F',{fb,{'E',35,[{'D',3,true},{'D',4,false},{'D',5,true},{'D',6,true},{'D',7,false},{'D',8,true},{'D',9,true},{'D',10,false},{'D',11,true},{'D',12,true},{'D',13,false},{'D',14,true}],false,{da,[{'A',16,{'D',17,true}}]}}}};
+ {fb,{'E',35,[{'D',3,true},{'D',4,false},{'D',5,true},{'D',6,true},{'D',7,false},{'D',8,true},{'D',9,true},{'D',10,false},{'D',11,true},{'D',12,true},{'D',13,false},{'D',14,true}],false,{da,[{'A',16,{'D',17,true}}]}}};
msg('E') ->
{'E',10,[{'D',11,true},{'D',12,false}],false,{dc,{'E_d_dc',13,true,{'E_d_dc_dcc',14,15}}}};
diff --git a/lib/asn1/test/test_special_decode_performance.erl b/lib/asn1/test/test_special_decode_performance.erl
index 4ac0ff2b27..7dfab1f25a 100644
--- a/lib/asn1/test/test_special_decode_performance.erl
+++ b/lib/asn1/test/test_special_decode_performance.erl
@@ -31,9 +31,9 @@ go(all) ->
{Time_S_c,Time_MGC_c}).
go(N,Mod) ->
- ?line Val = val(Mod),
- ?line {ok,B} = Mod:encode(element(1,Val),Val),
- ?line go(Mod,list_to_binary(B),N).
+ {Type,Val} = val(Mod),
+ {ok,B} = Mod:encode(Type, Val),
+ ?line go(Mod,B,N).
go(Mod,Bin,N) ->
?line FsS = get_selective_funcs(Mod),
@@ -92,7 +92,7 @@ val('PartialDecSeq') ->
{'F',{fb,{'E',12,[{'D',13,true},{'D',14,false},{'D',15,true},{'D',16,false},{'D',13,true},{'D',14,false},{'D',15,true},{'D',16,false},{'D',13,true},{'D',14,false},{'D',15,true},{'D',16,false}],true,{da,[{'A',17,{'D',18,false}},{'A',19,{'D',20,true}},{'A',21,{'D',22,false}},{'A',17,{'D',18,false}},{'A',19,{'D',20,true}},{'A',21,{'D',22,false}},{'A',17,{'D',18,false}},{'A',19,{'D',20,true}},{'A',21,{'D',22,false}},{'A',17,{'D',18,false}},{'A',19,{'D',20,true}},{'A',21,{'D',22,false}},{'A',17,{'D',18,false}},{'A',19,{'D',20,true}},{'A',21,{'D',22,false}},{'A',17,{'D',18,false}},{'A',19,{'D',20,true}},{'A',21,{'D',22,false}}]}}}};
val('MEDIA-GATEWAY-CONTROL') ->
- {'MegacoMessage',asn1_NOVALUE,{'Message',1,{ip4Address,{'IP4Address',[125,125,125,111],55555}},{transactions,[{transactionReply,{'TransactionReply',50007,asn1_NOVALUE,{actionReplies,[{'ActionReply',0,asn1_NOVALUE,asn1_NOVALUE,[{auditValueReply,{auditResult,{'AuditResult',{'TerminationID',[],[255,255,255]},[{mediaDescriptor,{'MediaDescriptor',asn1_NOVALUE,{multiStream,[{'StreamDescriptor',1,{'StreamParms',{'LocalControlDescriptor',sendRecv,asn1_NOVALUE,asn1_NOVALUE,[{'PropertyParm',[0,11,0,7],[[52,48]],asn1_NOVALUE}]},{'LocalRemoteDescriptor',[[{'PropertyParm',[0,0,176,1],[[48]],asn1_NOVALUE},{'PropertyParm',[0,0,176,8],[[73,78,32,73,80,52,32,49,50,53,46,49,50,53,46,49,50,53,46,49,49,49]],asn1_NOVALUE},{'PropertyParm',[0,0,176,15],[[97,117,100,105,111,32,49,49,49,49,32,82,84,80,47,65,86,80,32,32,52]],asn1_NOVALUE},{'PropertyParm',[0,0,176,12],[[112,116,105,109,101,58,51,48]],asn1_NOVALUE}]]},{'LocalRemoteDescriptor',[[{'PropertyParm',[0,0,176,1],[[48]],asn1_NOVALUE},{'PropertyParm',[0,0,176,8],[[73,78,32,73,80,52,32,49,50,52,46,49,50,52,46,49,50,52,46,50,50,50]],asn1_NOVALUE},{'PropertyParm',[0,0,176,15],[[97,117,100,105,111,32,50,50,50,50,32,82,84,80,47,65,86,80,32,32,52]],asn1_NOVALUE},{'PropertyParm',[0,0,176,12],[[112,116,105,109,101,58,51,48]],asn1_NOVALUE}]]}}}]}}},{packagesDescriptor,[{'PackagesItem',[0,11],1},{'PackagesItem',[0,11],1}]},{statisticsDescriptor,[{'StatisticsParameter',[0,12,0,4],[[49,50,48,48]]},{'StatisticsParameter',[0,11,0,2],[[54,50,51,48,48]]},{'StatisticsParameter',[0,12,0,5],[[55,48,48]]},{'StatisticsParameter',[0,11,0,3],[[52,53,49,48,48]]},{'StatisticsParameter',[0,12,0,6],[[48,46,50]]},{'StatisticsParameter',[0,12,0,7],[[50,48]]},{'StatisticsParameter',[0,12,0,8],[[52,48]]}]}]}}}]}]}}}]}}}.
+ {'MegacoMessage',{'MegacoMessage',asn1_NOVALUE,{'Message',1,{ip4Address,{'IP4Address',[125,125,125,111],55555}},{transactions,[{transactionReply,{'TransactionReply',50007,asn1_NOVALUE,{actionReplies,[{'ActionReply',0,asn1_NOVALUE,asn1_NOVALUE,[{auditValueReply,{auditResult,{'AuditResult',{'TerminationID',[],[255,255,255]},[{mediaDescriptor,{'MediaDescriptor',asn1_NOVALUE,{multiStream,[{'StreamDescriptor',1,{'StreamParms',{'LocalControlDescriptor',sendRecv,asn1_NOVALUE,asn1_NOVALUE,[{'PropertyParm',[0,11,0,7],[[52,48]],asn1_NOVALUE}]},{'LocalRemoteDescriptor',[[{'PropertyParm',[0,0,176,1],[[48]],asn1_NOVALUE},{'PropertyParm',[0,0,176,8],[[73,78,32,73,80,52,32,49,50,53,46,49,50,53,46,49,50,53,46,49,49,49]],asn1_NOVALUE},{'PropertyParm',[0,0,176,15],[[97,117,100,105,111,32,49,49,49,49,32,82,84,80,47,65,86,80,32,32,52]],asn1_NOVALUE},{'PropertyParm',[0,0,176,12],[[112,116,105,109,101,58,51,48]],asn1_NOVALUE}]]},{'LocalRemoteDescriptor',[[{'PropertyParm',[0,0,176,1],[[48]],asn1_NOVALUE},{'PropertyParm',[0,0,176,8],[[73,78,32,73,80,52,32,49,50,52,46,49,50,52,46,49,50,52,46,50,50,50]],asn1_NOVALUE},{'PropertyParm',[0,0,176,15],[[97,117,100,105,111,32,50,50,50,50,32,82,84,80,47,65,86,80,32,32,52]],asn1_NOVALUE},{'PropertyParm',[0,0,176,12],[[112,116,105,109,101,58,51,48]],asn1_NOVALUE}]]}}}]}}},{packagesDescriptor,[{'PackagesItem',[0,11],1},{'PackagesItem',[0,11],1}]},{statisticsDescriptor,[{'StatisticsParameter',[0,12,0,4],[[49,50,48,48]]},{'StatisticsParameter',[0,11,0,2],[[54,50,51,48,48]]},{'StatisticsParameter',[0,12,0,5],[[55,48,48]]},{'StatisticsParameter',[0,11,0,3],[[52,53,49,48,48]]},{'StatisticsParameter',[0,12,0,6],[[48,46,50]]},{'StatisticsParameter',[0,12,0,7],[[50,48]]},{'StatisticsParameter',[0,12,0,8],[[52,48]]}]}]}}}]}]}}}]}}}}.
%% val('PartialDecSeq') ->
%% {'F',{fb,{'E',35,[{'D',3,true},{'D',4,false},{'D',5,true},{'D',6,true},{'D',7,false},{'D',8,true},{'D',9,true},{'D',10,false},{'D',11,true},{'D',12,true},{'D',13,false},{'D',14,true}],false,{dc,{'E_d_dc',15,true,{'E_d_dc_dcc',17,4711}}}}}}.
diff --git a/lib/asn1/vsn.mk b/lib/asn1/vsn.mk
index 04f4b22421..81288496e9 100644
--- a/lib/asn1/vsn.mk
+++ b/lib/asn1/vsn.mk
@@ -1,2 +1,2 @@
#next version number to use is 2.0
-ASN1_VSN = 1.7
+ASN1_VSN = 1.8
diff --git a/lib/common_test/doc/src/Makefile b/lib/common_test/doc/src/Makefile
index 2ec6952710..99161ce68a 100644
--- a/lib/common_test/doc/src/Makefile
+++ b/lib/common_test/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2003-2011. All Rights Reserved.
+# Copyright Ericsson AB 2003-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -46,7 +46,8 @@ CT_MODULES = \
ct_rpc \
ct_snmp \
unix_telnet \
- ct_slave
+ ct_slave \
+ ct_netconfc
CT_XML_FILES = $(CT_MODULES:=.xml)
@@ -123,7 +124,7 @@ $(HTMLDIR)/%.gif: %.gif
docs: pdf html man
-$(CT_XML_FILES):
+$(CT_XML_FILES): %.xml: ../../src/%.erl
escript $(DOCGEN)/priv/bin/xml_from_edoc.escript -preprocess true -i $(XMERL_DIR)/include \
-i ../../../test_server/include -i ../../include \
-i ../../../../erts/lib/kernel/include -i ../../../../lib/kernel/include \
diff --git a/lib/common_test/doc/src/common_test_app.xml b/lib/common_test/doc/src/common_test_app.xml
index 6babdb93af..151159ad69 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>2011</year>
+ <year>2003</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -99,11 +99,11 @@
be executed by Common Test. A test case is represented by an atom,
the name of the test case function. A test case group is
represented by a <c>group</c> tuple, where <c>GroupName</c>,
- an atom, is the name of the group (defined in <c>groups/0</c>).
+ an atom, is the name of the group (defined in <c><seealso marker="#Module:groups-0">groups/0</seealso></c>).
Execution properties for groups may also be specified, both
for a top level group and for any of its sub-groups.
Group execution properties specified here, will override
- properties in the group definition (see <c>groups/0</c>).
+ properties in the group definition (see <c><seealso marker="#Module:groups-0">groups/0</seealso></c>).
(With value <c>default</c>, the group definition properties
will be used).</p>
@@ -162,7 +162,7 @@
<v> Func = atom()</v>
<v> Args = list()</v>
<v> Fun = fun()</v>
- <v> Required = Key | {Key,SubKeys}</v>
+ <v> Required = Key | {Key,SubKeys} | {Key,SubKey} | {Key,SubKey,SubKeys}</v>
<v> Key = atom()</v>
<v> SubKeys = SubKey | [SubKey]</v>
<v> SubKey = atom()</v>
@@ -170,7 +170,9 @@
<v> UserData = term()</v>
<v> Conns = [atom()]</v>
<v> CSSFile = string()</v>
- <v> CTHs = [CTHModule | {CTHModule, CTHInitArgs} | {CTHModule, CTHInitArgs, CTHPriority}]</v>
+ <v> CTHs = [CTHModule |</v>
+ <v>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{CTHModule, CTHInitArgs} |</v>
+ <v>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{CTHModule, CTHInitArgs, CTHPriority}]</v>
<v> CTHModule = atom()</v>
<v> CTHInitArgs = term()</v>
</type>
@@ -184,8 +186,8 @@
test cases in the suite).</p>
<p>The <c>timetrap</c> tag sets the maximum time each
- test case is allowed to execute (including <c>init_per_testcase/2</c>
- and <c>end_per_testcase/2</c>). If the timetrap time is
+ test case is allowed to execute (including <c><seealso marker="#Module:init_per_testcase-2">init_per_testcase/2</seealso></c>
+ and <c><seealso marker="#Module:end_per_testcase-2">end_per_testcase/2</seealso></c>). If the timetrap time is
exceeded, the test case fails with reason
<c>timetrap_timeout</c>. A <c>TimeFunc</c> function can be used to
set a new timetrap by returning a <c>TimeVal</c>. It may also be
@@ -201,11 +203,11 @@
in any of the configuration files, all test cases are skipped. For more
information about the 'require' functionality, see the
reference manual for the function
- <c>ct:require/[1,2]</c>.</p>
+ <c><seealso marker="ct#require-1">ct:require/1/2</seealso></c>.</p>
<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>
+ read by calling <c><seealso marker="ct#userdata-2">ct:userdata/2</seealso></c>.</p>
<p>The <c>ct_hooks</c> tag specifies which
<seealso marker="ct_hooks_chapter">Common Test Hooks</seealso>
@@ -264,7 +266,7 @@
<p>This function is called as the last test case in the
suite. It is meant to be used for cleaning up after
- <c>init_per_suite/1</c>.
+ <c><seealso marker="#Module:init_per_suite-1">init_per_suite/1</seealso></c>.
For information on <c>save_config</c>, please see
<seealso marker="dependencies_chapter#save_config">Dependencies
between Test Cases and Suites</seealso> in the User's Guide.</p>
@@ -289,7 +291,7 @@
<v> Func = atom()</v>
<v> Args = list()</v>
<v> Fun = fun()</v>
- <v> Required = Key | {Key,SubKeys}</v>
+ <v> Required = Key | {Key,SubKeys} | {Key,Subkey} | {Key,Subkey,SubKeys}</v>
<v> Key = atom()</v>
<v> SubKeys = SubKey | [SubKey]</v>
<v> SubKey = atom()</v>
@@ -297,8 +299,9 @@
<v> UserData = term()</v>
<v> Conns = [atom()]</v>
<v> CSSFile = string()</v>
- <v> CTHs = [CTHModule | {CTHModule, CTHInitArgs} |
- {CTHModule, CTHInitArgs, CTHPriority}]</v>
+ <v> CTHs = [CTHModule |</v>
+ <v> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{CTHModule, CTHInitArgs} |</v>
+ <v> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{CTHModule, CTHInitArgs, CTHPriority}]</v>
<v> CTHModule = atom()</v>
<v> CTHInitArgs = term()</v>
</type>
@@ -309,13 +312,14 @@
<p>This is the test case group info function. It is supposed to
return a list of tagged tuples that specify various properties
related to the execution of a test case group (i.e. its test cases
- and sub-groups). Properties set by <c>groups/1</c> override
+ and sub-groups). Properties set by
+ <c><seealso marker="#Module:group-1">group/1</seealso></c> override
properties with the same key that have been previously set by
- <c>suite/0</c>.</p>
+ <c><seealso marker="#Module:suite-0">suite/0</seealso></c>.</p>
<p>The <c>timetrap</c> tag sets the maximum time each
- test case is allowed to execute (including <c>init_per_testcase/2</c>
- and <c>end_per_testcase/2</c>). If the timetrap time is
+ test case is allowed to execute (including <c><seealso marker="#Module:init_per_testcase-2">init_per_testcase/2</seealso></c>
+ and <c><seealso marker="#Module:end_per_testcase-2">end_per_testcase/2</seealso></c>). If the timetrap time is
exceeded, the test case fails with reason
<c>timetrap_timeout</c>. A <c>TimeFunc</c> function can be used to
set a new timetrap by returning a <c>TimeVal</c>. It may also be
@@ -330,11 +334,11 @@
in any of the configuration files, all test cases in this group are skipped.
For more information about the 'require' functionality, see the
reference manual for the function
- <c>ct:require/[1,2]</c>.</p>
+ <c><seealso marker="ct#require-1">ct:require/1/2</seealso></c>.</p>
<p>With <c>userdata</c>, it is possible for the user to
specify arbitrary test case group related information which can be
- read by calling <c>ct:userdata/2</c>.</p>
+ read by calling <c><seealso marker="ct#userdata-2">ct:userdata/2</seealso></c>.</p>
<p>The <c>ct_hooks</c> tag specifies which
<seealso marker="ct_hooks_chapter">Common Test Hooks</seealso>
@@ -367,7 +371,7 @@
test case group. It typically contains initializations which are
common for all test cases and sub-groups in the group, and which
shall only be performed once. <c>GroupName</c> is the name of the
- group, as specified in the group definition (see <c>groups/0</c>). The
+ group, as specified in the group definition (see <c><seealso marker="#Module:groups-0">groups/0</seealso></c>). The
<c>Config</c> parameter is the configuration data which can be modified
here. The return value of this function is given as <c>Config</c>
to all test cases and sub-groups in the group. If <c>{skip,Reason}</c>
@@ -396,10 +400,10 @@
<p> OPTIONAL </p>
<p>This function is called after the execution of a test case group is finished.
- It is meant to be used for cleaning up after <c>init_per_group/2</c>.
+ It is meant to be used for cleaning up after <c><seealso marker="#Module:init_per_group-2">init_per_group/2</seealso></c>.
By means of <c>{return_group_result,Status}</c>, it is possible to return a
status value for a nested sub-group. The status can be retrieved in
- <c>end_per_group/2</c> for the group on the level above. The status will also
+ <c><seealso marker="#Module:end_per_group-2">end_per_group/2</seealso></c> for the group on the level above. The status will also
be used by Common Test for deciding if execution of a group should proceed in
case the property <c>sequence</c> or <c>repeat_until_*</c> is set.</p>
@@ -450,7 +454,7 @@
<p> OPTIONAL </p>
<p> This function is called after each test case, and can be used
- to clean up after <c>init_per_testcase/2</c> and the test case.
+ to clean up after <c><seealso marker="#Module:init_per_testcase-2">init_per_testcase/2</seealso></c> and the test case.
Any return value (besides <c>{fail,Reason}</c> and <c>{save_config,SaveConfig}</c>)
is ignored. By returning <c>{fail,Reason}</c>, <c>TestCase</c> will be marked as
failed (even though it was actually successful in the sense that it returned
@@ -476,7 +480,7 @@
<v> Func = atom()</v>
<v> Args = list()</v>
<v> Fun = fun()</v>
- <v> Required = Key | {Key,SubKeys}</v>
+ <v> Required = Key | {Key,SubKeys} | {Key,Subkey} | {Key,Subkey,SubKeys}</v>
<v> Key = atom()</v>
<v> SubKeys = SubKey | [SubKey]</v>
<v> SubKey = atom()</v>
@@ -492,15 +496,15 @@
<p>This is the test case info function. It is supposed to
return a list of tagged tuples that specify various properties
related to the execution of this particular test case.
- Properties set by <c>Testcase/0</c> override
+ Properties set by <c><seealso marker="#Module:Testcase-0">Testcase/0</seealso></c> override
properties that have been previously set for the test case
- by <c>group/1</c> or <c>suite/0</c>.</p>
+ by <c><seealso marker="#Module:group-1">group/1</seealso></c> or <c><seealso marker="#Module:suite-0">suite/0</seealso></c>.</p>
<p>The <c>timetrap</c> tag sets the maximum time the
test case is allowed to execute. If the timetrap time is
exceeded, the test case fails with reason
- <c>timetrap_timeout</c>. <c>init_per_testcase/2</c>
- and <c>end_per_testcase/2</c> are included in the
+ <c>timetrap_timeout</c>. <c><seealso marker="#Module:init_per_testcase-2">init_per_testcase/2</seealso></c>
+ and <c><seealso marker="#Module:end_per_testcase-2">end_per_testcase/2</seealso></c> are included in the
timetrap time. A <c>TimeFunc</c> function can be used to
set a new timetrap by returning a <c>TimeVal</c>. It may also be
used to trigger a timetrap timeout by, at some point, returning a
@@ -514,15 +518,15 @@
configuration files, the test case is skipped. For more
information about the 'require' functionality, see the
reference manual for the function
- <c>ct:require/[1,2]</c>.</p>
+ <c><seealso marker="ct#require-1">ct:require/1/2</seealso></c>.</p>
<p>If <c>timetrap</c> and/or <c>require</c> is not set, the
- default values specified by <c>suite/0</c> (or
- <c>group/1</c>) will be used.</p>
+ default values specified by <c><seealso marker="#Module:suite-0">suite/0</seealso></c> (or
+ <c><seealso marker="#Module:group-1">group/1</seealso></c>) will be used.</p>
<p>With <c>userdata</c>, it is possible for the user to
specify arbitrary test case related information which can be
- read by calling <c>ct:userdata/3</c>.</p>
+ read by calling <c><seealso marker="ct#userdata-3">ct:userdata/3</seealso></c>.</p>
<p>Other tuples than the ones defined will simply be ignored.</p>
@@ -550,7 +554,7 @@
<p>This is the implementation of a test case. Here you must
call the functions you want to test, and do whatever you
need to check the result. If something fails, make sure the
- function causes a runtime error, or call <c>ct:fail/1/2</c>
+ function causes a runtime error, or call <c><seealso marker="ct#fail-1">ct:fail/1/2</seealso></c>
(which also causes the test case process to terminate).</p>
<p>Elements from the <c>Config</c> list can e.g. be read
diff --git a/lib/common_test/doc/src/config_file_chapter.xml b/lib/common_test/doc/src/config_file_chapter.xml
index 6a860bb58b..d90adf8d7b 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>2011</year>
+ <year>2004</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -29,6 +29,8 @@
<file>config_file_chapter.xml</file>
</header>
+ <marker id="top"></marker>
+
<section>
<title>General</title>
@@ -78,7 +80,7 @@
test is skipped (unless a default value has been specified, see the
<seealso marker="write_test_chapter#info_function">test case info
function</seealso> chapter for details). There is also a function
- <c>ct:require/[1,2]</c> which can be called from a test case
+ <c><seealso marker="ct#require-1">ct:require/1/2</seealso></c> which can be called from a test case
in order to check if a specific variable is available. The return
value from this function must be checked explicitly and appropriate
action be taken depending on the result (e.g. to skip the test case
@@ -88,7 +90,7 @@
info-list should look like this:
<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
+ arguments to <c><seealso marker="ct#require-1">ct:require/1/2</seealso></c> which are described in the
reference manual for <seealso marker="ct">ct</seealso>.
<c>AliasName</c> becomes an alias for the configuration variable,
and can be used as reference to the configuration data value.
@@ -101,7 +103,8 @@
(or test case) and improve readability.</item>
</list>
<p>To read the value of a config variable, use the function
- <c>get_config/[1,2,3]</c> which is also described in the reference
+ <c><seealso marker="ct#get_config-1">get_config/1/2/3</seealso></c>
+ which is also described in the reference
manual for <seealso marker="ct">ct</seealso>.</p>
<p>Example:</p>
<pre>
@@ -118,7 +121,7 @@
<section>
<title>Using configuration variables defined in multiple files</title>
<p>If a configuration variable is defined in multiple files and you
- want to access all possible values, you may use the <c>ct:get_config/3</c>
+ want to access all possible values, you may use the <c><seealso marker="ct#get_config-3">ct:get_config/3</seealso></c>
function and specify <c>all</c> in the options list. The values will then
be returned in a list and the order of the elements corresponds to the order
that the config files were specified at startup. Please see
@@ -130,7 +133,7 @@
<marker id="encrypted_config_files"></marker>
<p>It is possible to encrypt configuration files containing sensitive data
if these files must be stored in open and shared directories.</p>
- <p>Call <c>ct:encrypt_config_file/[2,3]</c> to have Common Test encrypt a
+ <p>Call <c><seealso marker="ct#encrypt_config_file-2">ct:encrypt_config_file/2/3</seealso></c> to have Common Test encrypt a
specified file using the DES3 function in the OTP <c>crypto</c> application.
The encrypted file can then be used as a regular configuration file,
in combination with other encrypted files or normal text files. The key
@@ -139,7 +142,7 @@
<c>decrypt_file</c> flag/option, or a key file in a predefined location.</p>
<p>Common Test also provides decryption functions,
- <c>ct:decrypt_config_file/[2,3]</c>, for recreating the original text
+ <c><seealso marker="ct#decrypt_config_file-2">ct:decrypt_config_file/2/3</seealso></c>, for recreating the original text
files.</p>
<p>Please see the <seealso marker="ct">ct</seealso> reference manual for
@@ -149,8 +152,8 @@
<section>
<title>Opening connections by using configuration data</title>
<p>There are two different methods for opening a connection
- by means of the support functions in e.g. <c>ct_ssh</c>, <c>ct_ftp</c>,
- and <c>ct_telnet</c>:</p>
+ by means of the support functions in e.g. <c><seealso marker="ct_ssh">ct_ssh</seealso></c>, <c><seealso marker="ct_ftp">ct_ftp</seealso></c>,
+ and <c><seealso marker="ct_telnet">ct_telnet</seealso></c>:</p>
<list>
<item>Using a configuration target name (an alias) as reference.</item>
<item>Using the configuration variable as reference.</item>
@@ -295,7 +298,7 @@
<pre>
[{ftp_host, [{ftp, "targethost"}, {username, "tester"}, {password, "letmein"}]},
- {lm_directory, "/test/loadmodules"}]</pre>
+ {lm_directory, "/test/loadmodules"}]</pre>
</section>
diff --git a/lib/common_test/doc/src/cover_chapter.xml b/lib/common_test/doc/src/cover_chapter.xml
index b7162cb542..4fa92d5583 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>2011</year>
+ <year>2006</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -100,7 +100,7 @@
<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>
+ call to <c><seealso marker="ct#run_test-1">ct:run_test/1</seealso></c>, by adding a <c>{cover,CoverSpec}</c>
tuple to the <c>Opts</c> argument. Also, you can of course
enable code coverage in your test specifications (read
more in the chapter about
@@ -108,6 +108,33 @@
specifications</seealso>).</p>
</section>
+ <marker id="cover_stop"></marker>
+ <section>
+ <title>Stopping the cover tool when tests are completed</title>
+ <p>By default the Cover tool is automatically stopped when the
+ tests are completed. This causes the original (non cover
+ compiled) modules to be loaded back in to the test node. If a
+ process at this point is still running old code of any of the
+ modules that are cover compiled, meaning that it has not done
+ any fully qualified function call after the cover compilation,
+ the process will now be killed. To avoid this it is possible to
+ set the value of the <c>cover_stop</c> option to
+ <c>false</c>. This means that the modules will stay cover
+ compiled, and it is therefore only recommended if the erlang
+ node(s) under test is terminated after the test is completed
+ or if cover can be manually stopped.</p>
+
+ <p>The option can be set by using the <c>-cover_stop</c> flag with
+ <c>ct_run</c>, by adding <c>{cover_stop,true|false}</c> to the
+ Opts argument to <c><seealso
+ marker="ct#run_test-1">ct:run_test/1</seealso></c>, or by adding
+ a <c>cover_stop</c> term in your test specification (see chapter
+ about <seealso
+ marker="run_test_chapter#test_specifications">test
+ specifications</seealso>).</p>
+
+ </section>
+
<section>
<title>The cover specification file</title>
<p>These are the terms allowed in a cover specification file:</p>
@@ -148,6 +175,11 @@
%% Specific modules to exclude in cover.
{excl_mods, Mods}.
+
+ %% Cross cover compilation
+ %% Tag = atom(), an identifier for a test run
+ %% Mod = [atom()], modules to compile for accumulated analysis
+ {cross,[{Tag,Mods}]}.
</pre>
<p>The <c>incl_dirs_r</c> and <c>excl_dirs_r</c> terms tell Common
@@ -163,6 +195,81 @@
specification file for Common Test).</p>
</section>
+ <marker id="cross_cover"/>
+ <section>
+ <title>Cross cover analysis</title>
+ <p>The cross cover mechanism allows cover analysis of modules
+ across multiple tests. It is useful if some code, e.g. a library
+ module, is used by many different tests and the accumulated cover
+ result is desirable.</p>
+
+ <p>This can of course also be achieved in a more customized way by
+ using the <c>export</c> parameter in the cover specification and
+ analysing the result off line, but the cross cover mechanism is a
+ build in solution which also provides the logging.</p>
+
+ <p>The mechanism is easiest explained via an example:</p>
+
+ <p>Let's say that there are two systems, <c>s1</c> and <c>s2</c>,
+ which are tested in separate test runs. System <c>s1</c> contains
+ a library module <c>m1</c> which is tested by the <c>s1</c> test
+ run and is included in <c>s1</c>'s cover specification:</p>
+
+<code type="none">
+s1.cover:
+ {incl_mods,[m1]}.</code>
+
+ <p>When analysing code coverage, the result for <c>m1</c> can be
+ seen in the cover log in the <c>s1</c> test result.</p>
+
+ <p>Now, let's imagine that since <c>m1</c> is a library module, it
+ is also used quite a bit by system <c>s2</c>. The <c>s2</c> test
+ run does not specifically test <c>m1</c>, but it might still be
+ interesting to see which parts of <c>m1</c> is actually covered by
+ the <c>s2</c> tests. To do this, <c>m1</c> could be included also
+ in <c>s2</c>'s cover specification:</p>
+
+<code type="none">
+s2.cover:
+ {incl_mods,[m1]}.</code>
+
+ <p>This would give an entry for <c>m1</c> also in the cover log
+ for the <c>s2</c> test run. The problem is that this would only
+ reflect the coverage by <c>s2</c> tests, not the accumulated
+ result over <c>s1</c> and <c>s2</c>. And this is where the cross
+ cover mechanism comes in handy.</p>
+
+ <p>If instead the cover specification for <c>s2</c> was like
+ this:</p>
+
+<code type="none">
+s2.cover:
+ {cross,[{s1,[m1]}]}.</code>
+
+ <p>then <c>m1</c> would be cover compiled in the <c>s2</c> test
+ run, but not shown in the coverage log. Instead, if
+ <c>ct_cover:cross_cover_analyse/2</c> is called after both
+ <c>s1</c> and <c>s2</c> test runs are completed, the accumulated
+ result for <c>m1</c> would be available in the cross cover log for
+ the <c>s1</c> test run.</p>
+
+ <p>The call to the analyse function must be like this:</p>
+
+<code type="none">
+ct_cover:cross_cover_analyse(Level, [{s1,S1LogDir},{s2,S2LogDir}]).</code>
+
+ <p>where <c>S1LogDir</c> and <c>S2LogDir</c> are the directories
+ named <c>&lt;TestName&gt;.logs</c> for each test respectively.</p>
+
+ <p>Note the tags <c>s1</c> and <c>s2</c> which are used in the
+ cover specification file and in the call to
+ <c>ct_cover:cross_cover_analyse/2</c>. The point of these are only
+ to map the modules specified in the cover specification to the log
+ directory specified in the call to the analyse function. The name
+ of the tag has no meaning beyond this.</p>
+
+ </section>
+
<section>
<title>Logging</title>
<p>To view the result of a code coverage test, follow the
@@ -170,6 +277,11 @@
takes you to the code coverage overview page. If you have
successfully performed a detailed coverage analysis, you
find links to each individual module coverage page here.</p>
+
+ <p>If cross cover analysis has been performed, and there are
+ accumulated coverage results for the current test, then the -
+ "Coverdata collected over all tests" link will take you to these
+ results.</p>
</section>
</chapter>
diff --git a/lib/common_test/doc/src/ct_hooks.xml b/lib/common_test/doc/src/ct_hooks.xml
index b98c04a850..b3e713c77f 100644
--- a/lib/common_test/doc/src/ct_hooks.xml
+++ b/lib/common_test/doc/src/ct_hooks.xml
@@ -5,7 +5,7 @@
<erlref>
<header>
<copyright>
- <year>2010</year><year>2011</year>
+ <year>2010</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -111,11 +111,12 @@
</func>
<func>
- <name>Module:pre_init_per_suite(SuiteName, Config, CTHState) -&gt;
+ <name>Module:pre_init_per_suite(SuiteName, InitData, CTHState) -&gt;
Result</name>
<fsummary>Called before init_per_suite</fsummary>
<type>
<v>SuiteName = atom()</v>
+ <v>InitData = Config | SkipOrFail</v>
<v>Config = NewConfig = [{Key,Value}]</v>
<v>CTHState = NewCTHState = term()</v>
<v>Result = {Return, NewCTHState}</v>
@@ -140,7 +141,8 @@
<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>InitData</c> is the original config list of the test suite, or
+ a <c>SkipOrFail</c> tuple if a previous CTH has returned this.</p>
<p><c>CTHState</c> is the current internal state of the CTH.</p>
@@ -212,11 +214,12 @@
</func>
<func>
- <name>Module:pre_init_per_group(GroupName, Config, CTHState) -&gt;
+ <name>Module:pre_init_per_group(GroupName, InitData, CTHState) -&gt;
Result</name>
<fsummary>Called before init_per_group</fsummary>
<type>
<v>GroupName = atom()</v>
+ <v>InitData = Config | SkipOrFail</v>
<v>Config = NewConfig = [{Key,Value}]</v>
<v>CTHState = NewCTHState = term()</v>
<v>Result = {NewConfig | SkipOrFail, NewCTHState}</v>
@@ -269,11 +272,12 @@
</func>
<func>
- <name>Module:pre_init_per_testcase(TestcaseName, Config, CTHState) -&gt;
+ <name>Module:pre_init_per_testcase(TestcaseName, InitData, CTHState) -&gt;
Result</name>
<fsummary>Called before init_per_testcase</fsummary>
<type>
<v>TestcaseName = atom()</v>
+ <v>InitData = Config | SkipOrFail</v>
<v>Config = NewConfig = [{Key,Value}]</v>
<v>CTHState = NewCTHState = term()</v>
<v>Result = {NewConfig | SkipOrFail, NewCTHState}</v>
@@ -330,11 +334,12 @@
</func>
<func>
- <name>Module:pre_end_per_group(GroupName, Config, CTHState) -&gt;
+ <name>Module:pre_end_per_group(GroupName, EndData, CTHState) -&gt;
Result</name>
<fsummary>Called before end_per_group</fsummary>
<type>
<v>GroupName = atom()</v>
+ <v>EndData = Config | SkipOrFail</v>
<v>Config = NewConfig = [{Key,Value}]</v>
<v>CTHState = NewCTHState = term()</v>
<v>Result = {NewConfig | SkipOrFail, NewCTHState}</v>
@@ -387,11 +392,12 @@
</func>
<func>
- <name>Module:pre_end_per_suite(SuiteName, Config, CTHState) -&gt;
+ <name>Module:pre_end_per_suite(SuiteName, EndData, CTHState) -&gt;
Result</name>
<fsummary>Called before end_per_suite</fsummary>
<type>
<v>SuiteName = atom()</v>
+ <v>EndData = Config | SkipOrFail</v>
<v>Config = NewConfig = [{Key,Value}]</v>
<v>CTHState = NewCTHState = term()</v>
<v>Result = {NewConfig | SkipOrFail, NewCTHState}</v>
diff --git a/lib/common_test/doc/src/ct_hooks_chapter.xml b/lib/common_test/doc/src/ct_hooks_chapter.xml
index 1dbb841fb0..fe871eb516 100644
--- a/lib/common_test/doc/src/ct_hooks_chapter.xml
+++ b/lib/common_test/doc/src/ct_hooks_chapter.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2011</year><year>2012</year>
+ <year>2011</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -192,12 +192,12 @@
<section>
<title>External configuration data and Logging</title>
<p>It's possible in the CTH to read configuration data values
- by calling <c>ct:get_config/1/2/3</c> (as explained in the
+ by calling <c><seealso marker="ct#get_config-1">ct:get_config/1/2/3</seealso></c> (as explained in the
<seealso marker="config_file_chapter#require_config_data">
External configuration data</seealso>
chapter). The config variables in question must, as always, first have been
<c>required</c> by means of a suite-, group-, or test case info function,
- or the <c>ct:require/1/2</c> function. Note that the latter can also be used
+ or the <c><seealso marker="ct#require-1">ct:require/1/2</seealso></c> function. Note that the latter can also be used
in CT hook functions.</p>
<p>The CT hook functions may call any of the logging functions available
in the <c>ct</c> interface to print information to the log files, or to
@@ -252,6 +252,13 @@
{ok, Handle} -&gt;
{[{db_handle, Handle} | Config], CTHState#state{ handle = Handle }}
end.</code>
+ <note>If using multiple CTHs, the first part of the return tuple will be
+ used as input for the next CTH. So in the case above the next CTH might
+ get <c>{fail,Reason}</c> as the second parameter. If you have many CTHs
+ which interact, it might be a good idea to not let each CTH return
+ <c>fail</c> or <c>skip</c>. Instead return that an action should be taken
+ through the <c>Config</c> list and implement a CTH which at the end takes
+ the correct action. </note>
</section>
@@ -432,14 +439,14 @@ terminate(State) ->
<table>
<row>
- <cell><em>CTH Name</em></cell>
- <cell><em>Is Built-in</em></cell>
- <cell><em>Description</em></cell>
+ <cell align="left"><em>CTH Name</em></cell>
+ <cell align="left"><em>Is Built-in</em></cell>
+ <cell align="left"><em>Description</em></cell>
</row>
<row>
- <cell>cth_log_redirect</cell>
- <cell>yes</cell>
- <cell>Captures all error_logger and SASL logging events and prints them
+ <cell align="left">cth_log_redirect</cell>
+ <cell align="left">yes</cell>
+ <cell align="left">Captures all error_logger and SASL logging events and prints them
to the current test case log. If an event can not be associated with a
testcase it will be printed in the common test framework log. This will
happen for testcases which are run in parallel and events which occur
@@ -448,14 +455,29 @@ terminate(State) ->
using the normal SASL mechanisms. </cell>
</row>
<row>
- <cell>cth_surefire</cell>
- <cell>no</cell>
- <cell>Captures all test results and outputs them as surefire XML into
- a file. The file which is created is by default called junit_report.xml.
- The name can be by setting the path option for this hook. e.g.
- <code>-ct_hooks cth_surefix [{path,"/tmp/report.xml"}]</code>
- Surefire XML can forinstance be used by Jenkins to display test
- results.</cell>
+ <cell align="left">cth_surefire</cell>
+ <cell align="left">no</cell>
+ <cell align="left"><p>Captures all test results and outputs them as surefire
+ XML into a file. The file which is created is by default
+ called junit_report.xml. The file name can be changed by
+ setting the <c>path</c> option for this hook, e.g.</p>
+
+ <code>-ct_hooks cth_surefire [{path,"/tmp/report.xml"}]</code>
+
+ <p>If the <c>url_base</c> option is set, an additional
+ attribute named <c>url</c> will be added to each
+ <c>testsuite</c> and <c>testcase</c> XML element. The value will
+ be constructed from the <c>url_base</c> and a relative path
+ to the test suite or test case log respectively, e.g.</p>
+
+ <code>-ct_hooks cth_surefire [{url_base, "http://myserver.com/"}]</code>
+ <p>will give a url attribute value similar to</p>
+
+ <code>"http://myserver.com/[email protected]_11.19.39/
+x86_64-unknown-linux-gnu.my_test.logs/run.2012-12-12_11.19.39/suite.log.html"</code>
+
+ <p>Surefire XML can for instance be used by Jenkins to display test
+ results.</p></cell>
</row>
</table>
diff --git a/lib/common_test/doc/src/ct_master_chapter.xml b/lib/common_test/doc/src/ct_master_chapter.xml
index f4f0ecad62..9e848e99bb 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>2010</year>
+ <year>2006</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -124,7 +124,8 @@
<p><c>NodeRef = NodeAlias | node() | master</c></p>
<p>A <c>NodeAlias</c> (<c>atom()</c>) is used in a test specification as a
- reference to a node name (so the actual node name only needs to be declared once).
+ reference to a node name (so the actual node name only needs to be declared once,
+ which can of course also be achieved using constants).
The alias is declared with a <c>node</c> term:</p>
<p><c>{node, NodeAlias, NodeName}</c></p>
@@ -141,30 +142,32 @@
CT Master:</p>
<pre>
- {node, node1, ct_node@host_x}.
- {node, node2, ct_node@host_y}.
-
- {logdir, master, "/home/test/master_logs"}.
- {logdir, "/home/test/logs"}.
+ {define, 'Top', "/home/test"}.
+ {define, 'T1', "'Top'/t1"}.
+ {define, 'T2', "'Top'/t2"}.
+ {define, 'T3', "'Top'/t3"}.
+ {define, 'CfgFile', "config.cfg"}.
+ {define, 'Node', ct_node}.
+
+ {node, node1, 'Node@host_x'}.
+ {node, node2, 'Node@host_y'}.
+
+ {logdir, master, "'Top'/master_logs"}.
+ {logdir, "'Top'/logs"}.
- {config, node1, "/home/test/t1/cfg/config.cfg"}.
- {config, node2, "/home/test/t2/cfg/config.cfg"}.
- {config, "/home/test/t3/cfg/config.cfg"}.
+ {config, node1, "'T1'/'CfgFile'"}.
+ {config, node2, "'T2'/'CfgFile'"}.
+ {config, "'T3'/'CfgFile'"}.
- {alias, t1, "/home/test/t1"}.
- {alias, t2, "/home/test/t2"}.
- {alias, t3, "/home/test/t3"}.
+ {suites, node1, 'T1', all}.
+ {skip_suites, node1, 'T1', [t1B_SUITE,t1D_SUITE], "Not implemented"}.
+ {skip_cases, node1, 'T1', t1A_SUITE, [test3,test4], "Irrelevant"}.
+ {skip_cases, node1, 'T1', t1C_SUITE, [test1], "Ignore"}.
- {suites, node1, t1, all}.
- {skip_suites, node1, t1, [t1B_SUITE,t1D_SUITE], "Not implemented"}.
- {skip_cases, node1, t1, t1A_SUITE, [test3,test4], "Irrelevant"}.
- {skip_cases, node1, t1, t1C_SUITE, [test1], "Ignore"}.
+ {suites, node2, 'T2', [t2B_SUITE,t2C_SUITE]}.
+ {cases, node2, 'T2', t2A_SUITE, [test4,test1,test7]}.
- {suites, node2, t2, [t2B_SUITE,t2C_SUITE]}.
- {cases, node2, t2, t2A_SUITE, [test4,test1,test7]}.
-
- {skip_suites, t3, all, "Not implemented"}.
- </pre>
+ {skip_suites, 'T3', all, "Not implemented"}.</pre>
<p>This example specifies the same tests as the original example. But
now if started with a call to <c>ct_master:run(TestSpecName)</c>, the
@@ -190,10 +193,6 @@
name as the Common Test node in question (typically <c>ct@somehost</c> if started
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,
- config files and test directory aliases in the test specifications so that
- current working directory settings are not important.</p></note>
</section>
<section>
diff --git a/lib/common_test/doc/src/ct_run.xml b/lib/common_test/doc/src/ct_run.xml
index 078b9b958c..0750f560b3 100644
--- a/lib/common_test/doc/src/ct_run.xml
+++ b/lib/common_test/doc/src/ct_run.xml
@@ -36,6 +36,8 @@
OS command line.
</comsummary>
+ <marker id="top"></marker>
+
<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
@@ -46,7 +48,7 @@
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
+ called <c><seealso marker="ct#run_test-1">ct:run_test/1</seealso></c>, for starting Common Test from the Erlang
shell (or an Erlang program). Please see the <c>ct</c> man page for
details.</p>
@@ -72,6 +74,10 @@
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>Exit status is set before the program ends. Value <c>0</c> indicates a successful
+ test result, <c>1</c> indicates one or more failed or auto-skipped test cases, and
+ <c>2</c> indicates test execution failure.</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>
@@ -84,18 +90,21 @@
<pre>
ct_run [-dir TestDir1 TestDir2 .. TestDirN] |
[[-dir TestDir] -suite Suite1 Suite2 .. SuiteN
- [[-group Group1 Group2 .. GroupN] [-case Case1 Case2 .. CaseN]]]
+ [[-group Groups1 Groups2 .. GroupsN] [-case Case1 Case2 .. CaseN]]]
[-step [config | keep_inactive]]
[-config ConfigFile1 ConfigFile2 .. ConfigFileN]
[-userconfig CallbackModule1 ConfigString1 and CallbackModule2
- ConfigString2 and .. and CallbackModuleN ConfigStringN]
+ ConfigString2 and .. CallbackModuleN ConfigStringN]
[-decrypt_key Key] | [-decrypt_file KeyFile]
[-label Label]
[-logdir LogDir]
[-logopts LogOpts]
+ [-verbosity GenVLevel | [Category1 VLevel1 and
+ Category2 VLevel2 and .. CategoryN VLevelN]]
[-silent_connections [ConnType1 ConnType2 .. ConnTypeN]]
[-stylesheet CSSFile]
[-cover CoverCfgFile]
+ [-cover_stop Bool]
[-event_handler EvHandler1 EvHandler2 .. EvHandlerN] |
[-event_handler_init EvHandler1 InitArg1 and
EvHandler2 InitArg2 and .. EvHandlerN InitArgN]
@@ -107,7 +116,11 @@
[-repeat N [-force_stop]] |
[-duration HHMMSS [-force_stop]] |
[-until [YYMoMoDD]HHMMSS [-force_stop]]
- [-basic_html]</pre>
+ [-basic_html]
+ [-ct_hooks CTHModule1 CTHOpts1 and CTHModule2 CTHOpts2 and ..
+ CTHModuleN CTHOptsN]
+ [-exit_status ignore_config]
+ </pre>
</section>
<section>
<title>Run tests using test specification</title>
@@ -120,10 +133,13 @@
[-label Label]
[-logdir LogDir]
[-logopts LogOpts]
+ [-verbosity GenVLevel | [Category1 VLevel1 and
+ Category2 VLevel2 and .. CategoryN VLevelN]]
[-allow_user_terms]
[-silent_connections [ConnType1 ConnType2 .. ConnTypeN]]
[-stylesheet CSSFile]
[-cover CoverCfgFile]
+ [-cover_stop Bool]
[-event_handler EvHandler1 EvHandler2 .. EvHandlerN] |
[-event_handler_init EvHandler1 InitArg1 and
EvHandler2 InitArg2 and .. EvHandlerN InitArgN]
@@ -135,7 +151,11 @@
[-repeat N [-force_stop]] |
[-duration HHMMSS [-force_stop]] |
[-until [YYMoMoDD]HHMMSS [-force_stop]]
- [-basic_html]</pre>
+ [-basic_html]
+ [-ct_hooks CTHModule1 CTHOpts1 and CTHModule2 CTHOpts2 and ..
+ CTHModuleN CTHOptsN]
+ [-exit_status ignore_config]
+ </pre>
</section>
<section>
<title>Run tests in web based GUI</title>
@@ -147,6 +167,8 @@
[-userconfig CallbackModule1 ConfigString1 and CallbackModule2
ConfigString2 and .. and CallbackModuleN ConfigStringN]
[-logopts LogOpts]
+ [-verbosity GenVLevel | [Category1 VLevel1 and
+ Category2 VLevel2 and .. CategoryN VLevelN]]
[-decrypt_key Key] | [-decrypt_file KeyFile]
[-include InclDir1 InclDir2 .. InclDirN]
[-no_auto_compile]
diff --git a/lib/common_test/doc/src/event_handler_chapter.xml b/lib/common_test/doc/src/event_handler_chapter.xml
index a5886b9687..10a9b52d39 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>2011</year>
+ <year>2006</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -64,7 +64,7 @@
<marker id="usage"></marker>
<title>Usage</title>
<p>Event handlers may be installed by means of an <c>event_handler</c>
- start flag (<c>ct_run</c>) or option (<c>ct:run_test/1</c>), where the
+ start flag (<c>ct_run</c>) or option (<c><seealso marker="ct#run_test-1">ct:run_test/1</seealso></c>), where the
argument specifies the names of one or more event handler modules.
Example:</p>
<p><c>$ ct_run -suite test/my_SUITE -event_handler handlers/my_evh1
@@ -78,7 +78,7 @@
example).</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>
+ definition (see also <c><seealso marker="ct#run_test-1">ct:run_test/1</seealso></c> in the reference manual):</p>
<pre>
{event_handler,EventHandlers}
@@ -205,7 +205,7 @@
{error,{RunTimeError,StackTrace}} |
{timetrap_timeout,integer()} |
{failed,{Suite,end_per_testcase,FailInfo}}</c>, reason for failure.</p>
- <p><c>RequireInfo = {not_available,atom()}</c>, why require has failed.</p>
+ <p><c>RequireInfo = {not_available,atom() | tuple()}</c>, why require has failed.</p>
<p><c>FailInfo = {timetrap_timeout,integer()} |
{RunTimeError,StackTrace} |
UserTerm</c>,
@@ -233,7 +233,7 @@
reason for auto skipping <c>Func</c>.</p>
<p><c>FailReason = {Suite,ConfigFunc,FailInfo}} |
{Suite,FailedCaseInSequence}</c>, reason for failure.</p>
- <p><c>RequireInfo = {not_available,atom()}</c>, why require has failed.</p>
+ <p><c>RequireInfo = {not_available,atom() | tuple()}</c>, why require has failed.</p>
<p><c>ConfigFunc = init_per_suite | init_per_group</c></p>
<p><c>FailInfo = {timetrap_timeout,integer()} |
{RunTimeError,StackTrace} |
@@ -308,7 +308,7 @@
manager can look like.</p>
<note><p>To ensure that printouts to standard out (or printouts made with
- <c>ct:log/2/3</c> or <c>ct:pal/2/3</c>) get written to the test case log
+ <c><seealso marker="ct#log-2">ct:log/2/3</seealso></c> or <c><seealso marker="ct:pal-2">ct:pal/2/3</seealso></c>) get written to the test case log
file, and not to the Common Test framework log, you can syncronize
with the Common Test server by matching on the <c>tc_start</c> and <c>tc_done</c>
events. In the period between these events, all IO gets directed to the
diff --git a/lib/common_test/doc/src/example_chapter.xml b/lib/common_test/doc/src/example_chapter.xml
index f269dba2cd..2333f92989 100644
--- a/lib/common_test/doc/src/example_chapter.xml
+++ b/lib/common_test/doc/src/example_chapter.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2003</year><year>2010</year>
+ <year>2003</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -28,7 +28,8 @@
<rev></rev>
<file>example_chapter.xml</file>
</header>
-
+
+ <marker id="top"></marker>
<section>
<title>Test suite example</title>
diff --git a/lib/common_test/doc/src/getting_started_chapter.xml b/lib/common_test/doc/src/getting_started_chapter.xml
index 039578dd2e..3cf04bb1a2 100644
--- a/lib/common_test/doc/src/getting_started_chapter.xml
+++ b/lib/common_test/doc/src/getting_started_chapter.xml
@@ -90,7 +90,7 @@
<p>As you can understand from the illustration above, Common Test requires
that a test case generates a runtime error to indicate failure (e.g.
by causing a bad match error or by calling <c>exit/1</c>, preferrably
- through the <c>ct:fail/1,2</c> help function). A succesful execution is
+ through the <c><seealso marker="ct#fail-1">ct:fail/1,2</seealso></c> help function). A succesful execution is
indicated by means of a normal return from the test case function.
</p>
</section>
@@ -214,11 +214,56 @@
<section>
<title>What happens next?</title>
- <p>
- You will find detailed information about the basics introduced here in this
- chapter in the following chapters in the User's Guide, as well as
- presentations of many more useful features. Have fun!
- </p>
+
+ <p>Well, you might already be asking yourself questions such as:</p>
+
+ <list>
+ <item>"How and where can I specify variable data for my tests that mustn't
+ be hard-coded in the test suites (such as host names, addresses,
+ user login data, etc)?" The
+ <seealso marker="config_file_chapter#top">External Configuration Data</seealso>
+ chapter will give you that information.
+ </item>
+ <item>"Is there a way to declare a number of different tests and run them
+ in one session without having to write my own scripts? And can such
+ declarations be used for regression testing?" The
+ <seealso marker="run_test_chapter#test_specifications">Test Specifications</seealso>
+ chapter answers these questions.
+ </item>
+ <item>"Can test cases and/or test runs be automatically repeated?" Learn more about
+ <seealso marker="write_test_chapter#test_case_groups">Test Case Groups</seealso>
+ and also read about start flags/options in the
+ <seealso marker="run_test_chapter#ct_run">Running Tests</seealso> chapter and
+ the Reference Manual.
+ </item>
+ <item>"Will Common Test execute my test cases in sequence or in parallel?" The
+ <seealso marker="write_test_chapter#test_case_groups">Test Case Groups</seealso>
+ section in the Running Tests chapter will give you the answer.
+ </item>
+ <item>"What's the syntax for timetraps (mentioned above), and how do I set them?"
+ This is explained in the
+ <seealso marker="write_test_chapter#timetraps">Timetrap Timeouts</seealso>
+ part of the Writing Test Suites chapter.
+ </item>
+ <item>"What functions are available for logging and printing?" Check the
+ <seealso marker="write_test_chapter#logging">Logging</seealso>
+ section in the Writing Test Suites chapter.
+ </item>
+ <item>"I need data files for my tests. Where do I store them preferrably?"
+ You should read about
+ <seealso marker="write_test_chapter#data_priv_dir">Data and Private
+ Directories</seealso> for information about this.
+ </item>
+ <item>"May I start with a test suite example, please?"
+ <seealso marker="example_chapter#top">Sure!</seealso>
+ </item>
+ </list>
+ <p>You will probably want to get started on your own first test suites now, while
+ at the same time digging deeper into the Common Test User's Guide and Reference Manual.
+ You will find that there's lots more to learn about the things that have been introduced
+ in this chapter. You will of course also be presented many more useful features, such as the
+ ones listed above. Have fun!
+ </p>
</section>
</chapter>
diff --git a/lib/common_test/doc/src/notes.xml b/lib/common_test/doc/src/notes.xml
index 050b8774ca..0345fab8e8 100644
--- a/lib/common_test/doc/src/notes.xml
+++ b/lib/common_test/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2004</year><year>2011</year>
+ <year>2004</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -32,6 +32,496 @@
<file>notes.xml</file>
</header>
+<section><title>Common_Test 1.7</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Severe errors detected by <c>test_server</c> (e.g. if log
+ files directories cannot be created) will now be reported
+ to <c>common_test</c> and noted in the <c>common_test</c>
+ logs.</p>
+ <p>
+ Own Id: OTP-9769 Aux Id: kunagi-202 [113] </p>
+ </item>
+ <item>
+ <p>
+ If a busy test case generated lots of error messages,
+ cth_log_redirect:post_end_per_testcase would crash with a
+ timeout while waiting for the error logger to finish
+ handling all error reports. The default timer was 5
+ seconds. This has now been extended to 5 minutes.</p>
+ <p>
+ Own Id: OTP-10040 Aux Id: kunagi-173 [84] </p>
+ </item>
+ <item>
+ <p>
+ Some bugfixes in <c>ct_snmp:</c></p>
+ <p>
+ <list> <item> ct_snmp will now use the value of the
+ 'agent_vsns' config variable when setting the 'variables'
+ parameter to snmp application agent configuration.
+ Earlier this had to be done separately - i.e. the
+ supported versions had to be specified twice. </item>
+ <item> Snmp application failed to write notify.conf since
+ ct_snmp gave the notify type as a string instead of an
+ atom. This has been corrected. </item> </list></p>
+ <p>
+ Own Id: OTP-10432</p>
+ </item>
+ <item>
+ <p>
+ Some bugfixes in <c>ct_snmp</c>:</p>
+ <p>
+ <list> <item> Functions <c>register_users/2</c>,
+ <c>register_agents/2</c> and <c>register_usm_users/2</c>,
+ and the corresponding <c>unregister_*/1</c> functions
+ were not executable. These are corrected/rewritten.
+ </item> <item> Function <c>update_usm_users/2</c> is
+ removed, and an unregister function is added instead.
+ Update can now be done with unregister_usm_users and then
+ register_usm_users. </item> <item> Functions
+ <c>unregister_*/2</c> are added, so specific
+ users/agents/usm users can be unregistered. </item>
+ <item> Function <c>unload_mibs/1</c> is added for
+ completeness. </item> <item> Overriding configuration
+ files did not work, since the files were written in
+ priv_dir instead of in the configuration dir
+ (priv_dir/conf). This has been corrected. </item> <item>
+ Arguments to <c>register_usm_users/2</c> were faulty
+ documented. This has been corrected. </item> </list></p>
+ <p>
+ Own Id: OTP-10434 Aux Id: kunagi-264 [175] </p>
+ </item>
+ <item>
+ <p>
+ Faulty exported specs in common test has been corrected
+ to <c>ct_netconfc:hook_options/0</c> and
+ <c>inet:hostname/0</c></p>
+ <p>
+ Own Id: OTP-10601</p>
+ </item>
+ <item>
+ <p>
+ The netconf client in common_test did not adjust the
+ window after receiving data. Due to this, the client
+ stopped receiving data after a while. This has been
+ corrected.</p>
+ <p>
+ Own Id: OTP-10646</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Known Bugs and Problems</title>
+ <list>
+ <item>
+ <p>
+ The earlier undocumented cross cover feature for
+ accumulating cover data over multiple tests has now been
+ fixed and documented.</p>
+ <p>
+ Own Id: OTP-9870 Aux Id: kunagi-206 [117] </p>
+ </item>
+ <item>
+ <p>
+ CT drops error reason when groups/0 crashes.</p>
+ <p>
+ Own Id: OTP-10631 Aux Id: kunagi-345 [256] </p>
+ </item>
+ <item>
+ <p>
+ Problem opening sftp connection with ct_ssh.</p>
+ <p>
+ Own Id: OTP-10632 Aux Id: kunagi-346 [257] </p>
+ </item>
+ <item>
+ <p>
+ Event handler on a ct_master node causes hanging.</p>
+ <p>
+ Own Id: OTP-10634 Aux Id: kunagi-347 [258] </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Common_Test 1.6.3.1</title>
+
+ <section><title>Known Bugs and Problems</title>
+ <list>
+ <item>
+ <p>
+ The following corrections/changes are done in the
+ cth_surefire hook:</p>
+ <p>
+ <list> <item> Earlier there would always be a
+ 'properties' element under the 'testsuites' element. This
+ would exist even if there were no 'property' element
+ inside it. This has been changed so if there are no
+ 'property' elements to display, then there will not be a
+ 'properties' element either. </item> <item> The XML file
+ will now (unless other is specified) be stored in the top
+ log directory. Earlier, the default directory would be
+ the current working directory for the erlang node, which
+ would mostly, but not always, be the top log directory.
+ </item> <item> The 'hostname' attribute in the
+ 'testsuite' element would earlier never have the correct
+ value. This has been corrected. </item> <item> The
+ 'errors' attribute in the 'testsuite' element would
+ earlier display the number of failed testcases. This has
+ been changed and will now always have the value 0, while
+ the 'failures' attribute will show the number of failed
+ testcases. </item> <item> A new attribute 'skipped' is
+ added to the 'testsuite' element. This will display the
+ number of skipped testcases. These would earlier be
+ included in the number of failed test cases. </item>
+ <item> The total number of tests displayed by the 'tests'
+ attribute in the 'testsuite' element would earlier
+ include init/end_per_suite and init/end_per_group. This
+ is no longer the case. The 'tests' attribute will now
+ only count "real" test cases. </item> <item> Earlier,
+ auto skipped test cases would have no value in the 'log'
+ attribute. This is now corrected. </item> <item> A new
+ attributes 'log' is added to the 'testsuite' element.
+ </item> <item> A new option named 'url_base' is added for
+ this hook. If this option is used, a new attribute named
+ 'url' will be added to the 'testcase' and 'testsuite'
+ elements. </item> </list></p>
+ <p>
+ Own Id: OTP-10589</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Common_Test 1.6.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The ct:run_test/1 option 'config' only worked with a
+ single config file, not a list of files. This has been
+ fixed.</p>
+ <p>
+ Own Id: OTP-10495</p>
+ </item>
+ <item>
+ <p>
+ ct_netconfc:close_session sometimes returned
+ {error,closed} because the ssh connection was closed
+ (from the server side) before the rpc-reply was received
+ by the client. This is normal and can not be helped. It
+ has been corrected so the return will be 'ok' in this
+ case. Other error situations will still give
+ {error,Reason}.</p>
+ <p>
+ Own Id: OTP-10510 Aux Id: kunagi-320 [231] </p>
+ </item>
+ <item>
+ <p>
+ ct_netconfc:close_session sometimes returned
+ {error,closed} or (if the connection was named)
+ {error,{process_down,Pid,normal}} because the ssh
+ connection was closed (from the server side) before the
+ rpc-reply was received by the client. This is normal and
+ can not be helped. It has been corrected so the return
+ will be 'ok' in this situation.</p>
+ <p>
+ Own Id: OTP-10570</p>
+ </item>
+ <item>
+ <p>
+ Fix bug where ct:require of same name with same config
+ would return name_in_use.</p>
+ <p>
+ Own Id: OTP-10572</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ A new test case group search functionality has been
+ implemented that makes Common Test search automatically
+ through the group definitions tree (the return value of
+ groups/0) and create tests for all paths of nested groups
+ that match the specification. It also allows for
+ specifying unique paths to sub groups in order to avoid
+ execution of unwanted tests. This new feature can be used
+ whenever starting a test run by means of the ct_run
+ program, the ct:run_test/1 API function, or a Test
+ Specification. Details can be found in the Test Case
+ Group Execution section in the Running Tests chapter.</p>
+ <p>
+ Own Id: OTP-10466 Aux Id: kunagi-276 [187] </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Known Bugs and Problems</title>
+ <list>
+ <item>
+ <p>
+ Restore Config data if lost when test case fails.</p>
+ <p>
+ Own Id: OTP-10070 Aux Id: kunagi-175 [86] </p>
+ </item>
+ <item>
+ <p>
+ IO server error in test_server.</p>
+ <p>
+ Own Id: OTP-10125 Aux Id: OTP-10101, kunagi-177 [88] </p>
+ </item>
+ <item>
+ <p>
+ Faulty connection handling in common_test.</p>
+ <p>
+ Own Id: OTP-10126 Aux Id: kunagi-178 [89] </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Common_Test 1.6.2.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The interactive mode (ct_run -shell) would not start
+ properly. This error has been fixed.</p>
+ <p>
+ Own Id: OTP-10414</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Common_Test 1.6.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ If a CT hook function caused a crash, this could in some
+ situations cause Common Test to terminate due to an
+ illegal IO operation. This error has been corrected.</p>
+ <p>
+ Own Id: OTP-10050 Aux Id: seq12039 </p>
+ </item>
+ <item>
+ <p>
+ The Common Test documentation states that timetraps are
+ never active during execution of CT hook functions. This
+ was only true for post hook functions, not for pre hook
+ functions. The code for CT hooks has been modified to
+ behave according to the documentation.</p>
+ <p>
+ Own Id: OTP-10069</p>
+ </item>
+ <item>
+ <p>
+ If a CT hook function would call the exit/1 or throw/1
+ BIF (possibly indirectly, e.g. as a result of a timeout
+ in gen_server:call/3), Common Test would hang. This
+ problem has been fixed.</p>
+ <p>
+ Own Id: OTP-10072 Aux Id: seq12053 </p>
+ </item>
+ <item>
+ <p>
+ The documentation has been updated with information about
+ how to deal with chaining of hooks which return
+ fail/skip.</p>
+ <p>
+ Own Id: OTP-10077 Aux Id: seq12048 </p>
+ </item>
+ <item>
+ <p>
+ When ct_hooks called the id/1 functions of multiple
+ hooks, it would reverse the order of the hooks and call
+ the proceeding init/2 calls in the wrong order. This has
+ been fixed.</p>
+ <p>
+ Own Id: OTP-10135</p>
+ </item>
+ <item>
+ <p>
+ The surefire hook now correctly handles autoskipped
+ initialization and test functions.</p>
+ <p>
+ Own Id: OTP-10158</p>
+ </item>
+ <item>
+ <p>
+ The ct:get_status/0 function failed to report status if a
+ parallel test case group was running at the time of the
+ call. This has been fixed and the return value for the
+ function has been updated. Please see the ct reference
+ manual for details.</p>
+ <p>
+ Own Id: OTP-10172</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ The support for "silent connections" has been updated to
+ include ssh. Also, a silent_connections term has been
+ added to the set of test specification terms.</p>
+ <p>
+ Own Id: OTP-9625 Aux Id: seq11918 </p>
+ </item>
+ <item>
+ <p>
+ It is now possible to specify an arbitrarily large tuple
+ as the requires config data when using require and
+ ct:get_config. See the ct:get_config and ct:require
+ reference manual pages for details about which keys are
+ allowed.</p>
+ <p>
+ This change introduces a backwards incompatability in the
+ <c>ct:require/2</c> interface. Previously when doing
+ <c>ct:require(a_name,{key,subkey})</c>, a_name would be
+ associated with key. This has been changed to that
+ <c>a_name</c> is associated with <c>subkey</c>. This
+ change also effects using <c>require</c> in an
+ suite/group/testcase info function.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-9626 Aux Id: seq11920 </p>
+ </item>
+ <item>
+ <p>
+ The ct_run program now sets the OS process exit status
+ before it ends. Value 0 indicates a successful test
+ result, 1 indicates one or more failed or auto-skipped
+ test cases, and 2 indicates test execution failure.</p>
+ <p>
+ Own Id: OTP-9865 Aux Id: OTP-10087 </p>
+ </item>
+ <item>
+ <p>
+ It is now possible to sort the HTML tables by clicking on
+ the header elements. In order to reset a sorted table,
+ the browser window should simply be refreshed. This
+ feature requires that the browser supports javascript,
+ and has javascript execution enabled. If the 'ct_run
+ -basic_html' flag is used, no javascript code is included
+ in the generated HTML code.</p>
+ <p>
+ Own Id: OTP-9896 Aux Id: seq12034, OTP-9835 </p>
+ </item>
+ <item>
+ <p>
+ A netconf client, ct_netconfc, is added to common_test.
+ It supports basic netconf functionality over SSH. In
+ order to allow testing of both success and failure cases,
+ it is intentionally written to allow non-standard
+ behavior.</p>
+ <p>
+ Own Id: OTP-10025</p>
+ </item>
+ <item>
+ <p>
+ The test specification term {define,Constant,Value} has
+ been introduced, which makes it possible to replace
+ constant names (atom()) with values (term()) in arbitrary
+ test specification terms. The 'define' makes the (now
+ deprecated) 'alias' term obsolete. More details,
+ including examples, can be found in the Test
+ Specifications chapter in the Common Test User's Guide.</p>
+ <p>
+ Own Id: OTP-10049</p>
+ </item>
+ <item>
+ <p>
+ Verbosity levels for log printouts has been added. This
+ makes it possible to specify preferred verbosity for
+ different categories of log printouts, as well as general
+ printouts (such as standard IO), to allow control over
+ which strings get printed and which get ignored. New
+ versions of the Common Test logging functions, ct:log,
+ ct:pal and ct:print, have been introduced, with a new
+ Importance argument added. The Importance value is
+ compared to the verbosity level at runtime. More
+ information can be found in the chapter about Logging in
+ the Common Test User's Guide.</p>
+ <p>
+ Own Id: OTP-10067 Aux Id: seq12050 </p>
+ </item>
+ <item>
+ <p>
+ The return values of ct:run_test/1 and ct:run_testspec/1
+ have been changed from an uninformative 'ok' (independent
+ of the test outcome) to a value,
+ {Ok,Failed,{UserSkipped,AutoSkipped}} (all integers),
+ that presents the final test case result, or a value,
+ {error,Reason}, that informs about fatal test execution
+ failure. See details in the reference manual for ct.</p>
+ <p>
+ Own Id: OTP-10087 Aux Id: OTP-9865 </p>
+ </item>
+ <item>
+ <p>
+ The test specification syntax has been updated with new
+ and missing terms, such as 'define', 'verbosity',
+ 'auto_compile', 'stylesheet', 'silent_connections',
+ 'basic_html' and 'release_shell'. See the Test
+ Specification chapter in the Common Test User's Guide for
+ details.</p>
+ <p>
+ Own Id: OTP-10089 Aux Id: OTP-10049 </p>
+ </item>
+ <item>
+ <p>
+ It is now possible to pause execution of a test case, by
+ calling the ct:break/1/2 function. Execution is resumed
+ with a call to ct:continue/0/1. Break/continue also works
+ for test cases executing in parallel. See the ct
+ reference manual for details.</p>
+ <p>
+ Own Id: OTP-10127</p>
+ </item>
+ <item>
+ <p>
+ It is now possible to send user defined events from a
+ testcase which will be picked up by the installed event
+ handler.</p>
+ <p>
+ Own Id: OTP-10157</p>
+ </item>
+ <item>
+ <p>
+ A new start option, release_shell, for ct:run_test/1, has
+ been added, which makes Common Test release the shell
+ process after the test suite compilation phase is
+ finished. For details, see the Running Tests chapter in
+ the User's Guide.</p>
+ <p>
+ Own Id: OTP-10248 Aux Id: OTP-10127 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Common_Test 1.6.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/common_test/doc/src/ref_man.xml b/lib/common_test/doc/src/ref_man.xml
index a9fdef7359..6fede88434 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>2011</year>
+ <year>2003</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -71,6 +71,7 @@
<xi:include href="ct_cover.xml"/>
<xi:include href="ct_ftp.xml"/>
<xi:include href="ct_ssh.xml"/>
+ <xi:include href="ct_netconfc.xml"/>
<xi:include href="ct_rpc.xml"/>
<xi:include href="ct_snmp.xml"/>
<xi:include href="ct_telnet.xml"/>
diff --git a/lib/common_test/doc/src/run_test_chapter.xml b/lib/common_test/doc/src/run_test_chapter.xml
index 30486d3eec..d5f5d89e05 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>2012</year>
+ <year>2003</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -119,7 +119,7 @@
<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>
+ -group <groups> -case <casenames>]]></c></item>
</list>
<p>Examples:</p>
<p><c>$ ct_run -config $CFGS/sys1.cfg $CFGS/sys2.cfg -dir $SYS1_TEST $SYS2_TEST</c></p>
@@ -137,6 +137,8 @@
<p><c>$ ct_run -suite ./testdir/x_SUITE ./testdir/y_SUITE</c></p>
+ <p>For more details on <seealso marker="run_test_chapter#group_execution">test case group execution</seealso>, please see below.</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>
@@ -153,6 +155,8 @@
<item><c><![CDATA[-stylesheet <css_file>]]></c>, points out a user HTML style sheet (see below).</item>
<item><c><![CDATA[-cover <cover_cfg_file>]]></c>, to perform code coverage test (see
<seealso marker="cover_chapter#cover">Code Coverage Analysis</seealso>).</item>
+ <item><c><![CDATA[-cover_stop <bool>]]></c>, to specify if the cover tool shall be stopped after the test is completed (see
+ <seealso marker="cover_chapter#cover_stop">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
@@ -178,6 +182,8 @@
<item><c><![CDATA[-basic_html]]></c>, switches off html enhancements that might not be compatible with older browsers.</item>
<item><c><![CDATA[-logopts <opts>]]></c>, makes it possible to modify aspects of the logging behaviour, see
<seealso marker="run_test_chapter#logopts">Log options</seealso> below.</item>
+ <item><c><![CDATA[-verbosity <levels>]]></c>, sets <seealso marker="write_test_chapter#logging">verbosity levels
+ for printouts</seealso>.</item>
</list>
<note><p>Directories passed to Common Test may have either relative or absolute paths.</p></note>
@@ -196,62 +202,232 @@
the current working directory of the Erlang Runtime System during the test run!</p>
</note>
- <p>For more information about the <c>ct_run</c> program, see the
- <seealso marker="install_chapter#general">Installation</seealso> chapter.
- </p>
- </section>
-
- <section>
- <title>Running tests from the Web based GUI</title>
-
- <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>
-
+ <p>The <c>ct_run</c> program sets the exit status before shutting down. The following values
+ are defined:</p>
<list>
- <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>
+ <item><c>0</c> indicates a successful testrun, i.e. one without failed or auto-skipped test cases.</item>
+ <item><c>1</c> indicates that one or more test cases have failed, or have been auto-skipped.</item>
+ <item><c>2</c> indicates that the test execution has failed because of e.g. compilation errors, an
+ illegal return value from an info function, etc.</item>
</list>
+ <p>If auto-skipped test cases should not affect the exit status, you may change the default
+ behaviour using start flag:</p>
+ <pre>-exit_status ignore_config</pre>
- <p>From the GUI you can run tests and view the result and the logs.
+ <p>For more information about the <c>ct_run</c> program, see the
+ <seealso marker="ct_run#top">Reference Manual</seealso> and the
+ <seealso marker="install_chapter#general">Installation</seealso> chapter.
</p>
-
- <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[ct_run -vts -browser <browser_start_cmd>]]></c></p>
- <p>Example:</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, Firefox will
- be the default browser on Unix platforms and Internet Explorer on Windows.
- 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>
-
+
<section>
<title>Running tests from the Erlang shell or from an Erlang program</title>
<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>ct_run</c> program described above, only the flags are instead
+ <c><seealso marker="ct#run_test-1">ct:run_test/1</seealso></c>.
+ This function takes the same start parameters as
+ the <c><seealso marker="run_test_chapter#ct_run">ct_run</seealso></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>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>is with <c><seealso marker="ct#run_test-1">ct:run_test/1</seealso></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>
+
+ <p>The function returns the test result, represented by the tuple:
+ <c>{Ok,Failed,{UserSkipped,AutoSkipped}}</c>, where each element is an
+ integer. If test execution fails, the function returns the tuple:
+ <c>{error,Reason}</c>, where the term <c>Reason</c> explains the
+ failure.</p>
+
+ <section>
+ <title>Releasing the Erlang shell</title>
+ <p>During execution of tests, started with
+ <c><seealso marker="ct#run_test-1">ct:run_test/1</seealso></c>,
+ the Erlang shell process, controlling stdin, will remain the top
+ level process of the Common Test system of processes. The result
+ is that the Erlang shell is not available for interaction during
+ the test run. If this is not desirable, maybe because the shell is needed
+ for debugging purposes or for interaction with the SUT during test
+ execution, you may set the <c>release_shell</c> start option to
+ <c>true</c> (in the call to <c>ct:run_test/1</c> or by
+ using the corresponding test specification term, see below). This will
+ make Common Test release the shell immediately after the test suite
+ compilation stage. To accomplish this, a test runner process
+ is spawned to take control of the test execution, and the effect is that
+ <c>ct:run_test/1</c> returns the pid of this process rather than the
+ test result - which instead is printed to tty at the end of the test run.</p>
+ <note><p>Note that in order to use the
+ <c><seealso marker="ct#break-1">ct:break/1/2</seealso></c> and
+ <c><seealso marker="ct#continue-0">ct:continue/0/1</seealso></c> functions,
+ <c>release_shell</c> <em>must</em> be set to <c>true</c>.</p></note>
+ </section>
+
+ <p>For detailed documentation about
+ <c><seealso marker="ct#run_test-1">ct:run_test/1</seealso></c>,
+ please see the
+ <c><seealso marker="ct#run_test-1">ct</seealso></c> manual page.</p>
</section>
+ <marker id="group_execution"></marker>
+ <section>
+ <title>Test case group execution</title>
+
+ <p>With the <c>ct_run</c> flag, or <c>ct:run_test/1</c> option <c>group</c>,
+ one or more test case groups can be specified, optionally in combination
+ with specific test cases. The syntax for specifying groups is as follows
+ (on the command line):</p>
+
+ <pre>
+ <![CDATA[$ ct_run -group <group_names_or_paths> [-case <cases>]]]></pre>
+ <p>or (in the Erlang shell):</p>
+ <pre>
+ <![CDATA[1> ct:run_test([{group,GroupsNamesOrPaths}, {case,Cases}]).]]></pre>
+
+ <p>The <c>group_names_or_paths</c> parameter specifies either one
+ or more group names and/or one or more group paths. At start up,
+ Common Test will search for matching groups in the group definitions
+ tree (i.e. the list returned from <c>Suite:groups/0</c>, please see the
+ <seealso marker="write_test_chapter#test_case_groups">Test case groups</seealso>
+ chapter for details).
+ Given a group name, say <c>g</c>, Common Test will search for all paths
+ that lead to <c>g</c>. By path here we mean a sequence of nested groups,
+ all of which have to be followed in order to get from the top level
+ group to <c>g</c>. Actually, what Common Test needs to do in order to
+ execute the test cases in group <c>g</c>, is to call the
+ <c>init_per_group/2</c> function for each group in the path to
+ <c>g</c>, as well as all corresponding <c>end_per_group/2</c>
+ functions afterwards. The obvious reason for this is that the configuration
+ of a test case in <c>g</c> (and its <c>Config</c> input data) depends on
+ <c>init_per_testcase(TestCase, Config)</c> and its return value, which
+ in turn depends on <c>init_per_group(g, Config)</c> and its return value,
+ which in turn depends on <c>init_per_group/2</c> of the group above
+ <c>g</c>, etc, all the way up to the top level group.</p>
+
+ <p>As you may have already realized, this means that if there is more than
+ one way to locate a group (and its test cases) in a path, the result of the
+ group search operation is a number of tests, all of which will be performed.
+ Common Test actually interprets a group specification that consists of a
+ single name this way:</p>
+
+ <p>"Search and find all paths in the group definitions tree that lead
+ to the specified group and, for each path, create a test which (1) executes
+ all configuration functions in the path to the specified group, then (2)
+ executes all - or all matching - test cases in this group, as well as (3)
+ all - or all matching - test cases in all sub groups of the group".
+ </p>
+
+ <p>It is also possible for the user to specify a specific group path with
+ the <c>group_names_or_paths</c> parameter. With this type of specification it's
+ possible to avoid execution of unwanted groups (in otherwise matching paths),
+ and/or the execution of sub groups. The syntax of the group path is a list of
+ group names in the path, e.g. on the command line:
+ </p>
+ <p><c>$ ct_run -suite "./x_SUITE" -group [g1,g3,g4] -case tc1 tc5</c></p>
+ <p>or similarly in the Erlang shell (requires a list within the groups list):</p>
+ <p><c>1> ct:run_test([{suite,"./x_SUITE"}, {group,[[g1,g3,g4]]}, {testcase,[tc1,tc5]}]).</c></p>
+
+ <p>The last group in the specified path will be the terminating group in
+ the test, i.e. no sub groups following this group will be executed. In the
+ example above, <c>g4</c> is the terminating group, hence Common Test will
+ execute a test that calls all init configuration functions in the path to
+ <c>g4</c>, i.e. <c>g1..g3..g4</c>. It will then call test cases <c>tc1</c>
+ and <c>tc5</c> in <c>g4</c> and finally all end configuration functions in order
+ <c>g4..g3..g1</c>.</p>
+
+ <p>Note that the group path specification doesn't necessarily
+ have to include <em>all</em> groups in the path to the terminating group.
+ Common Test will search for all matching paths if given an incomplete group
+ path.</p>
+
+ <p>Note also that it's possible to combine group names and group paths with the
+ <c>group_names_or_paths</c> parameter. Each element is treated as
+ an individual specification in combination with the <c>cases</c> parameter.
+ See examples below.</p>
+
+ <p>Examples:</p>
+ <pre>
+ -module(x_SUITE).
+ ...
+ %% The group definitions:
+ groups() ->
+ [{top1,[],[tc11,tc12,
+ {sub11,[],[tc12,tc13]},
+ {sub12,[],[tc14,tc15,
+ {sub121,[],[tc12,tc16]}]}]},
+
+ {top2,[],[{group,sub21},{group,sub22}]},
+ {sub21,[],[tc21,{group,sub2X2}]},
+ {sub22,[],[{group,sub221},tc21,tc22,{group,sub2X2}]},
+ {sub221,[],[tc21,tc23]},
+ {sub2X2,[],[tc21,tc24]}].
+ </pre>
+ <br></br>
+ <p><c>$ ct_run -suite "x_SUITE" -group all</c></p>
+ <p><c>1> ct:run_test([{suite,"x_SUITE"}, {group,all}]).</c></p>
+ <p>Two tests will be executed, one for all cases and all sub groups under <c>top1</c>,
+ and one for all under <c>top2</c>. (We would get the same result with
+ <c>-group top1 top2</c>, or <c>{group,[top1,top2]}</c>.</p>
+ <br></br>
+ <p><c>$ ct_run -suite "x_SUITE" -group top1</c></p>
+ <p><c>1> ct:run_test([{suite,"x_SUITE"}, {group,[top1]}]).</c></p>
+ <p>This will execute one test for all cases and sub groups under <c>top1</c>.</p>
+ <br></br>
+ <p><c>$ ct_run -suite "x_SUITE" -group top1 -case tc12</c></p>
+ <p><c>1> ct:run_test([{suite,"x_SUITE"}, {group,[top1]}, {testcase,[tc12]}]).</c></p>
+ <p>This will run a test that executes <c>tc12</c> in <c>top1</c> and any sub group
+ under <c>top1</c> where it can be found (<c>sub11</c> and <c>sub121</c>).</p>
+ <br></br>
+ <p><c>$ ct_run -suite "x_SUITE" -group [top1] -case tc12</c></p>
+ <p><c>1> ct:run_test([{suite,"x_SUITE"}, {group,[[top1]]}, {testcase,[tc12]}]).</c></p>
+ <p>This will execute <c>tc12</c> <em>only</em> in group <c>top1</c>.</p>
+ <br></br>
+ <p><c>$ ct_run -suite "x_SUITE" -group top1 -case tc16</c></p>
+ <p><c>1> ct:run_test([{suite,"x_SUITE"}, {group,[top1]}, {testcase,[tc16]}]).</c></p>
+ <p>This will search <c>top1</c> and all its sub groups for <c>tc16</c> and the result
+ will be that this test case executes in group <c>sub121</c>. (The specific path:
+ <c>-group [sub121]</c> or <c>{group,[[sub121]]}</c>, would have given
+ us the same result in this example).</p>
+ <br></br>
+ <p><c>$ ct_run -suite "x_SUITE" -group sub12 [sub12]</c></p>
+ <p><c>1> ct:run_test([{suite,"x_SUITE"}, {group,[sub12,[sub12]]}]).</c></p>
+ <p>This will execute two tests, one that includes all cases and sub groups under
+ <c>sub12</c>, and one with <em>only</em> the test cases in <c>sub12</c>.</p>
+ <br></br>
+ <p><c>$ ct_run -suite "x_SUITE" -group sub2X2</c></p>
+ <p><c>1> ct:run_test([{suite,"x_SUITE"}, {group,[sub2X2]}]).</c></p>
+ <p>In this example, Common Test will find and execute two tests, one for the path from
+ <c>top2</c> to <c>sub2X2</c> via <c>sub21</c>, and one from <c>top2</c> to <c>sub2X2</c>
+ via <c>sub22</c>.</p>
+ <br></br>
+ <p><c>$ ct_run -suite "x_SUITE" -group [sub21,sub2X2]</c></p>
+ <p><c>1> ct:run_test([{suite,"x_SUITE"}, {group,[[sub21,sub2X2]]}]).</c></p>
+ <p>Here, by specifying the unique path: <c>top2 -> sub21 -> sub2X2</c>, only one test
+ is executed. The second possible path from <c>top2</c> to <c>sub2X2</c> (above)
+ will be discarded.</p>
+ <br></br>
+ <p><c>$ ct_run -suite "x_SUITE" -group [sub22] -case tc22 tc21</c></p>
+ <p><c>1> ct:run_test([{suite,"x_SUITE"}, {group,[[sub22]]}, {testcase,[tc22,tc21]}]).</c></p>
+ <p>In this example only the test cases for <c>sub22</c> will be executed, and in
+ reverse order compared to the group definition.</p>
+ <br></br>
+
+ <p>If a test case that belongs to a group (according to the group definition), is executed
+ without a group specification, i.e. simply by means of (command line):</p>
+ <p><c>$ ct_run -suite "my_SUITE" -case my_tc</c></p>
+ <p>or (Erlang shell):</p>
+ <p><c>1> ct:run_test([{suite,"my_SUITE"}, {testcase,my_tc}]).</c></p>
+ <p>then Common Test ignores the group definition and executes the test case in the scope of the
+ test suite only (no group configuration functions are called).</p>
+
+ <p>The group specification feature, exactly as it has been presented in this section, can also
+ be used in <seealso marker="run_test_chapter#test_specifications">Test
+ Specifications</seealso> (with some extra features added). Please see below.</p>
+ </section>
+
+
<section>
<title>Running the interactive shell mode</title>
@@ -266,9 +442,9 @@
for trying out various operations during test suite development.</p>
<p>To invoke the interactive shell mode, you can start an Erlang shell
- manually and call <c>ct:install/1</c> to install any configuration
+ manually and call <c><seealso marker="ct#install-1">ct:install/1</seealso></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
+ call <c><seealso marker="ct#start_interactive-0">ct:start_interactive/0</seealso></c> to start Common Test. If you use
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>
and/or <c>-userconfig</c> flag. Examples:
@@ -287,7 +463,8 @@
<p>If any functions using "required config data" (e.g. ct_telnet or
ct_ftp functions) are to be called from the erlang shell, config
- data must first be required with <c>ct:require/[1,2]</c>. This is
+ data must first be required with <c><seealso marker="ct#require-1">
+ ct:require/1/2</seealso></c>. This is
equivalent to a <c>require</c> statement in the <seealso
marker="write_test_chapter#suite">Test Suite Info
Function</seealso> or in the <seealso
@@ -314,11 +491,11 @@
is not supported.</p>
<p>If you wish to exit the interactive mode (e.g. to start an
- automated test run with <c>ct:run_test/1</c>), call the function
- <c>ct:stop_interactive/0</c>. This shuts down the
+ automated test run with <c><seealso marker="ct#run_test-1">ct:run_test/1</seealso></c>), call the function
+ <c><seealso marker="ct#stop_interactive-0">ct:stop_interactive/0</seealso></c>. This shuts down the
running <c>ct</c> application. Associations between
configuration names and data created with <c>require</c> are
- consequently deleted. <c>ct:start_interactive/0</c> will get you
+ consequently deleted. <c><seealso marker="ct#start_interactive-0">ct:start_interactive/0</seealso></c> will get you
back into interactive mode, but the previous state is not restored.</p>
</section>
@@ -326,7 +503,7 @@
<title>Step by step execution of test cases with the Erlang Debugger</title>
<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
+ <c>{step,Opts}</c> option to <c><seealso marker="ct#run_test-1">ct:run_test/1</seealso></c>, it is possible
to get the Erlang Debugger started automatically and use its
graphical interface to investigate the state of the current test
case and to execute it step by step and/or set execution breakpoints.</p>
@@ -352,35 +529,38 @@
<marker id="test_specifications"></marker>
<section>
- <title>Using test specifications</title>
+ <title>Test Specifications</title>
<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>
+ Erlang terms. The terms are normally declared in a text file (see
+ <c><seealso marker="ct#run_test-1">ct:run_test/1</seealso></c>), but
+ may also be passed to Common Test on the form of a list (see
+ <c><seealso marker="ct#run_testspec-1">ct:run_testspec/1</seealso></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>ct_run -include</c>, see above).</p>
+ before starting the test, import configuration data (similar to
+ <c>ct_run -config/-userconfig</c>), specify the top level HTML log
+ directory (similar to <c>ct_run -logdir</c>), enable code coverage
+ analysis (similar to <c>ct_run -cover</c>), install Common Test Hooks
+ (similar to <c>ct_run -ch_hooks</c>), install event_handler plugins
+ (similar to <c>ct_run -event_handler</c>), specify include directories
+ that should be passed to the compiler for automatic compilation
+ (similar to <c>ct_run -include</c>), disable the auto compilation
+ feature (similar to <c>ct_run -no_auto_compile</c>), set verbosity
+ levels (similar to <c>ct_run -verbosity</c>), and more.</p>
+ <p>Configuration terms can be combined with <c>ct_run</c> start flags,
+ or <c>ct:run_test/1</c> options. The result will for some flags/options
+ and terms be that the values are merged (e.g. configuration files,
+ include directories, verbosity levels, silent connections), and for
+ others that the start flags/options override the test specification
+ terms (e.g. log directory, label, style sheet, auto compilation).</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>
+ either one or more suites, one or more test case groups (possibly nested),
+ or one or more test cases in a group (or in multiple groups) or in a 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
@@ -391,49 +571,69 @@
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>
+ behaviour, i.e. to instead perform each test sequentially in a "script-like"
+ manner, the term <c>merge_tests</c> can be set to <c>false</c> in
+ the test specification.</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
+ are not executed and show up in the HTML 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
+ 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
+ be a sub group of another group, is specified (or if 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>With the <c>GroupSpec</c> element (below) it's possible to specify
- group execution properties that will override those specified in the
+ <p>The test specification utilizes the same mechanism for specifying
+ test case groups by means of names and paths, as explained in the
+ <seealso marker="run_test_chapter#group_execution">Group Execution</seealso>
+ section above, with the addition of the <c>GroupSpec</c> element
+ described next.</p>
+ <p>The <c>GroupSpec</c> element makes it possible to specify
+ group execution properties that will override those in the
group definition (i.e. in <c>groups/0</c>). Execution properties for
sub-groups may be overridden as well. This feature makes it possible to
change properties of groups at the time of execution,
- without even having to edit the test suite. More detailed documentation,
- and examples, can be found in the
- <seealso marker="write_test_chapter#test_case_groups">
+ without even having to edit the test suite. The very same
+ feature is available for <c>group</c> elements in the <c>Suite:all/0</c>
+ list. Therefore, more detailed documentation, and examples, can be
+ found in the <seealso marker="write_test_chapter#test_case_groups">
Test case groups</seealso> chapter.</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 (Large Scale
- Testing). The node parameters in the init term are only
+ Testing). The node parameters in the <c>init</c> 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
+ Scale Testing</seealso> chapter for information). For more information
+ about the various terms, please see the corresponding sections in the
+ User's Guide, such as e.g. the
+ <seealso marker="run_test_chapter#ct_run"><c>ct_run</c>
+ program</seealso> for an overview of available start flags
+ (since most flags have a corresponding configuration term), and
+ more detailed explanation of e.g.
+ <seealso marker="write_test_chapter#logging">Logging</seealso>
+ (for the <c>verbosity</c>, <c>stylesheet</c> and <c>basic_html</c> terms),
+ <seealso marker="config_file_chapter#top">External Configuration Data</seealso>
+ (for the <c>config</c> and <c>userconfig</c> terms),
<seealso marker="event_handler_chapter#event_handling">Event
- Handling</seealso> chapter.</p>
+ Handling</seealso> (for the <c>event_handler</c> term),
+ <seealso marker="ct_hooks_chapter#installing">Common Test Hooks</seealso>
+ (for the <c>ct_hooks</c> term), etc.</p>
<p>Config terms:</p>
<pre>
+ {merge_tests, Bool}.
+
+ {define, Constant, Value}.
+
{node, NodeAlias, Node}.
{init, InitOptions}.
@@ -442,6 +642,15 @@
{label, Label}.
{label, NodeRefs, Label}.
+ {verbosity, VerbosityLevels}.
+ {verbosity, NodeRefs, VerbosityLevels}.
+
+ {stylesheet, CSSFile}.
+ {stylesheet, NodeRefs, CSSFile}.
+
+ {silent_connections, ConnTypes}.
+ {silent_connections, NodeRefs, ConnTypes}.
+
{multiply_timetraps, N}.
{multiply_timetraps, NodeRefs, N}.
@@ -451,22 +660,29 @@
{cover, CoverSpecFile}.
{cover, NodeRefs, CoverSpecFile}.
+ {cover_stop, Bool}.
+ {cover_stop, NodeRefs, Bool}.
+
{include, IncludeDirs}.
{include, NodeRefs, IncludeDirs}.
+ {auto_compile, Bool},
+ {auto_compile, NodeRefs, Bool},
+
{config, ConfigFiles}.
+ {config, ConfigDir, ConfigBaseNames}.
{config, NodeRefs, ConfigFiles}.
+ {config, NodeRefs, ConfigDir, ConfigBaseNames}.
{userconfig, {CallbackModule, ConfigStrings}}.
{userconfig, NodeRefs, {CallbackModule, ConfigStrings}}.
- {alias, DirAlias, Dir}.
-
- {merge_tests, Bool}.
-
{logdir, LogDir}.
{logdir, NodeRefs, LogDir}.
+ {logopts, LogOpts}.
+ {logopts, NodeRefs, LogOpts}.
+
{create_priv_dir, PrivDirOption}.
{create_priv_dir, NodeRefs, PrivDirOption}.
@@ -479,83 +695,179 @@
{ct_hooks, NodeRefs, CTHModules}.
{enable_builtin_hooks, Bool}.
- </pre>
+
+ {basic_html, Bool}.
+ {basic_html, NodeRefs, Bool}.
+
+ {release_shell, Bool}.</pre>
+
<p>Test terms:</p>
<pre>
- {suites, DirRef, Suites}.
- {suites, NodeRefs, DirRef, Suites}.
+ {suites, Dir, Suites}.
+ {suites, NodeRefs, Dir, Suites}.
- {groups, DirRef, Suite, Groups}.
- {groups, NodeRefsDirRef, Suite, Groups}.
+ {groups, Dir, Suite, Groups}.
+ {groups, NodeRefs, Dir, Suite, Groups}.
- {groups, DirRef, Suite, GroupSpec, {cases,Cases}}.
- {groups, NodeRefsDirRef, Suite, GroupSpec, {cases,Cases}}.
+ {groups, Dir, Suite, Groups, {cases,Cases}}.
+ {groups, NodeRefs, Dir, Suite, Groups, {cases,Cases}}.
- {cases, DirRef, Suite, Cases}.
- {cases, NodeRefs, DirRef, Suite, Cases}.
+ {cases, Dir, Suite, Cases}.
+ {cases, NodeRefs, Dir, Suite, Cases}.
- {skip_suites, DirRef, Suites, Comment}.
- {skip_suites, NodeRefs, DirRef, Suites, Comment}.
+ {skip_suites, Dir, Suites, Comment}.
+ {skip_suites, NodeRefs, Dir, Suites, Comment}.
- {skip_groups, DirRef, Suite, GroupNames, Comment}.
- {skip_groups, NodeRefs, DirRef, Suite, GroupNames, Comment}.
+ {skip_groups, Dir, Suite, GroupNames, Comment}.
+ {skip_groups, NodeRefs, Dir, Suite, GroupNames, Comment}.
- {skip_cases, DirRef, Suite, Cases, Comment}.
- {skip_cases, NodeRefs, DirRef, Suite, Cases, Comment}.
- </pre>
+ {skip_cases, Dir, Suite, Cases, Comment}.
+ {skip_cases, NodeRefs, Dir, Suite, Cases, Comment}.</pre>
+
<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()]
- DirAlias = atom()
- Dir = string()
- LogDir = string()
- PrivDirOption = auto_per_run | auto_per_tc | manual_per_tc
- EventHandlers = atom() | [atom()]
- InitArgs = [term()]
- CTHModules = [CTHModule | {CTHModule, CTHInitArgs} | {CTHModule, CTHInitArgs, CTHPriority}]
- CTHModule = atom()
- CTHInitArgs = term()
- DirRef = DirAlias | Dir
- Suites = atom() | [atom()] | all
- Suite = atom()
- Groups = GroupSpec | [GroupSpec] | all
- GroupSpec = GroupName | {GroupName,Properties} | {GroupName,Properties,GroupSpec}
- GroupName = atom()
- GroupNames = GroupName | [GroupName]
- Cases = atom() | [atom()] | all
- Comment = string() | ""
- </pre>
- <p>Example:</p>
+ Bool = true | false
+ Constant = atom()
+ Value = term()
+ NodeAlias = atom()
+ Node = node()
+ NodeRef = NodeAlias | Node | master
+ NodeRefs = all_nodes | [NodeRef] | NodeRef
+ InitOptions = term()
+ Label = atom() | string()
+ VerbosityLevels = integer() | [{Category,integer()}]
+ Category = atom()
+ CSSFile = string()
+ ConnTypes = all | [atom()]
+ N = integer()
+ CoverSpecFile = string()
+ IncludeDirs = string() | [string()]
+ ConfigFiles = string() | [string()]
+ ConfigDir = string()
+ ConfigBaseNames = string() | [string()]
+ CallbackModule = atom()
+ ConfigStrings = string() | [string()]
+ LogDir = string()
+ LogOpts = [term()]
+ PrivDirOption = auto_per_run | auto_per_tc | manual_per_tc
+ EventHandlers = atom() | [atom()]
+ InitArgs = [term()]
+ CTHModules = [CTHModule |
+ {CTHModule, CTHInitArgs} |
+ {CTHModule, CTHInitArgs, CTHPriority}]
+ CTHModule = atom()
+ CTHInitArgs = term()
+ Dir = string()
+ Suites = atom() | [atom()] | all
+ Suite = atom()
+ Groups = GroupPath | [GroupPath] | GroupSpec | [GroupSpec] | all
+ GroupPath = [GroupName]
+ GroupSpec = GroupName | {GroupName,Properties} | {GroupName,Properties,GroupSpec}
+ GroupName = atom()
+ GroupNames = GroupName | [GroupName]
+ Cases = atom() | [atom()] | all
+ Comment = string() | ""</pre>
+
+ <p>The difference between the <c>config</c> terms above, is that with
+ <c>ConfigDir</c>, <c>ConfigBaseNames</c> is a list of base names,
+ i.e. without directory paths. <c>ConfigFiles</c> must be full names,
+ including paths. E.g, these two terms have the same meaning:</p>
<pre>
- {logdir, "/home/test/logs"}.
+ {config, ["/home/testuser/tests/config/nodeA.cfg",
+ "/home/testuser/tests/config/nodeB.cfg"]}.
+
+ {config, "/home/testuser/tests/config", ["nodeA.cfg","nodeB.cfg"]}.</pre>
+
+ <note><p>Any relative paths specified in the test specification, will be
+ relative to the directory which contains the test specification file, if
+ <c>ct_run -spec TestSpecFile ...</c> or
+ <c>ct:run:test([{spec,TestSpecFile},...])</c>
+ executes the test. The path will be relative to the top level log directory, if
+ <c>ct:run:testspec(TestSpec)</c> executes the test.</p></note>
- {config, "/home/test/t1/cfg/config.cfg"}.
- {config, "/home/test/t2/cfg/config.cfg"}.
- {config, "/home/test/t3/cfg/config.cfg"}.
+ <p>The <c>define</c> term introduces a constant, which is used to
+ replace the name <c>Constant</c> with <c>Value</c>, wherever it's found in
+ the test specification. This replacement happens during an initial iteration
+ through the test specification. Constants may be used anywhere in the test
+ specification, e.g. in arbitrary lists and tuples, and even in strings
+ and inside the value part of other constant definitions! A constant can
+ also be part of a node name, but that is the only place where a constant
+ can be part of an atom.</p>
+
+ <note><p>For the sake of readability, the name of the constant must always
+ begin with an upper case letter, or a <c>$</c>, <c>?</c>, or <c>_</c>.
+ This also means that it must always be single quoted (obviously, since
+ the constant name is actually an atom, not text).</p></note>
+
+ <p>The main benefit of constants is that they can be used to reduce the size
+ (and avoid repetition) of long strings, such as file paths. Compare these
+ terms:</p>
+
+ <pre>
+ %% 1a. no constant
+ {config, "/home/testuser/tests/config", ["nodeA.cfg","nodeB.cfg"]}.
+ {suites, "/home/testuser/tests/suites", all}.
- {alias, t1, "/home/test/t1"}.
- {alias, t2, "/home/test/t2"}.
- {alias, t3, "/home/test/t3"}.
+ %% 1b. with constant
+ {define, 'TESTDIR', "/home/testuser/tests"}.
+ {config, "'TESTDIR'/config", ["nodeA.cfg","nodeB.cfg"]}.
+ {suites, "'TESTDIR'/suites", all}.
+
+ %% 2a. no constants
+ {config, [testnode@host1, testnode@host2], "../config", ["nodeA.cfg","nodeB.cfg"]}.
+ {suites, [testnode@host1, testnode@host2], "../suites", [x_SUITE, y_SUITE]}.
+
+ %% 2b. with constants
+ {define, 'NODE', testnode}.
+ {define, 'NODES', ['NODE'@host1, 'NODE'@host2]}.
+ {config, 'NODES', "../config", ["nodeA.cfg","nodeB.cfg"]}.
+ {suites, 'NODES', "../suites", [x_SUITE, y_SUITE]}.</pre>
+
+ <p>Constants make the test specification term <c>alias</c>, in previous
+ versions of Common Test, redundant. This term has been deprecated but will
+ remain supported in upcoming Common Test releases. Replacing <c>alias</c>
+ terms with <c>define</c> is strongly recommended though! Here's an example
+ of such a replacement:</p>
+
+ <pre>
+ %% using the old alias term
+ {config, "/home/testuser/tests/config/nodeA.cfg"}.
+ {alias, suite_dir, "/home/testuser/tests/suites"}.
+ {groups, suite_dir, x_SUITE, group1}.
+
+ %% replacing with constants
+ {define, 'TestDir', "/home/testuser/tests"}.
+ {define, 'CfgDir', "'TestDir'/config"}.
+ {define, 'SuiteDir', "'TestDir'/suites"}.
+ {config, 'CfgDir', "nodeA.cfg"}.
+ {groups, 'SuiteDir', x_SUITE, group1}.</pre>
+
+ <p>Actually, constants could well replace the <c>node</c> term too, but
+ this still has declarative value, mainly when used in combination
+ with <c>NodeRefs == all_nodes</c> (see types above).</p>
+
+ <p>Here follows a simple test specification example:</p>
+ <pre>
+ {define, 'Top', "/home/test"}.
+ {define, 'T1', "'Top'/t1"}.
+ {define, 'T2', "'Top'/t2"}.
+ {define, 'T3', "'Top'/t3"}.
+ {define, 'CfgFile', "config.cfg"}.
+
+ {logdir, "'Top'/logs"}.
- {suites, t1, all}.
- {skip_suites, t1, [t1B_SUITE,t1D_SUITE], "Not implemented"}.
- {skip_cases, t1, t1A_SUITE, [test3,test4], "Irrelevant"}.
- {skip_cases, t1, t1C_SUITE, [test1], "Ignore"}.
+ {config, ["'T1'/'CfgFile'", "'T2'/'CfgFile'", "'T3'/'CfgFile'"]}.
- {suites, t2, [t2B_SUITE,t2C_SUITE]}.
- {cases, t2, t2A_SUITE, [test4,test1,test7]}.
+ {suites, 'T1', all}.
+ {skip_suites, 'T1', [t1B_SUITE,t1D_SUITE], "Not implemented"}.
+ {skip_cases, 'T1', t1A_SUITE, [test3,test4], "Irrelevant"}.
+ {skip_cases, 'T1', t1C_SUITE, [test1], "Ignore"}.
- {skip_suites, t3, all, "Not implemented"}.
- </pre>
+ {suites, 'T2', [t2B_SUITE,t2C_SUITE]}.
+ {cases, 'T2', t2A_SUITE, [test4,test1,test7]}.
+
+ {skip_suites, 'T3', all, "Not implemented"}.</pre>
+
<p>The example specifies the following:</p>
<list>
<item>The specified logdir directory will be used for storing
@@ -563,8 +875,6 @@
date and time).</item>
<item>The variables in the specified test system config files will be
imported for the test.</item>
- <item>Aliases are given for three test system directories. The suites in
- this example are stored in "/home/test/tX/test".</item>
<item>The first test to run includes all suites for system t1. Excluded from
the test are however the t1B and t1D suites. Also test cases test3 and
test4 in t1A as well as the test1 case in t1C are excluded from
@@ -575,9 +885,9 @@
<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.
+ <p>With the <c>init</c> term it's 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
@@ -586,11 +896,49 @@
<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
+ If <c><seealso marker="ct#run_test-1">ct:run_test/1</seealso></c> is used for starting the tests, the relaxed scanner
mode is enabled by means of the tuple: <c>{allow_user_terms,true}</c></p>
</section>
+
+ <section>
+ <title>Running tests from the Web based GUI</title>
+
+ <p>The web based GUI, VTS, is started with the
+ <c><seealso marker="run_test_chapter#ct_run">ct_run</seealso></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>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>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[ct_run -vts -browser <browser_start_cmd>]]></c></p>
+ <p>Example:</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, Firefox will
+ be the default browser on Unix platforms and Internet Explorer on Windows.
+ 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>
<section>
+ <marker id="log_files"></marker>
<title>Log files</title>
<p>As the execution of the test suites proceed, events are logged in
@@ -718,17 +1066,30 @@
<p>instead of each <c>x</c> printed on a new line, which is the default behaviour.</p>
</section>
+ <section>
+ <marker id="table_sorting"></marker>
+ <title>Sorting HTML table columns</title>
+ <p>By clicking the name in the column header of any table (e.g. "Ok", "Case", "Time", etc),
+ the table rows are sorted in whatever order makes sense for the type of value (e.g.
+ numerical for "Ok" or "Time", and alphabetical for "Case"). The sorting is performed
+ by means of JavaScript code, automatically inserted into the HTML log files. Common Test
+ uses the <url href="http://jquery.com">jQuery</url> library and the
+ <url href="http://tablesorter.com">tablesorter</url> plugin, with customized sorting
+ functions, for this implementation.</p>
+ </section>
</section>
<section>
<marker id="html_stylesheet"></marker>
<title>HTML Style Sheets</title>
- <p>Common Test uses a CSS file to control the look of the HTML
- files generated during test runs. If, for some reason, the
- log files are not displayed correctly in the HTML browser of your
- choice, or you prefer the "pre Common Test v1.6 look"
- of the log files (i.e. not using CSS), use the start flag/option
- <c>basic_html</c> to revert to the old style.</p>
+ <p>Common Test uses an HTML Style Sheet (CSS file) to control the look of
+ the HTML log files generated during test runs. If, for some reason, the
+ log files are not displayed correctly in the browser of your
+ choice, or you prefer a more primitive ("pre Common Test v1.6") look
+ of the logs, use the start flag/option:</p>
+ <pre>basic_html</pre>
+ <p>This disables the use of Style Sheets, as well as JavaScripts (see
+ table sorting above).</p>
<p>Common Test includes an <em>optional</em> feature to allow
user HTML style sheets for customizing printouts. The
@@ -881,75 +1242,82 @@
<section>
<marker id="silent_connections"></marker>
<title>Silent Connections</title>
- <p>The protocol handling processes in Common Test, implemented by ct_telnet, ct_ftp etc,
- do verbose printing to the test case logs. This can be switched off by means
- of the <c>-silent_connections</c> flag:</p>
+ <p>The protocol handling processes in Common Test, implemented by ct_telnet,
+ ct_ssh, ct_ftp etc, do verbose printing to the test case logs. This can be switched off
+ by means of the <c>-silent_connections</c> flag:</p>
<pre>
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>
+ <p>where <c>conn_types</c> specifies <c>ssh, telnet, ftp, rpc</c> and/or <c>snmp</c>.</p>
<p>Example:</p>
<pre>
- ct_run ... -silent_connections telnet ftp</pre>
- <p>switches off logging for telnet and ftp connections.</p>
+ ct_run ... -silent_connections ssh telnet</pre>
+ <p>switches off logging for ssh and telnet connections.</p>
<pre>
ct_run ... -silent_connections</pre>
<p>switches off logging for all connection types.</p>
- <p>Basic and important information such as opening and closing a connection,
- fatal communication error and reconnection attempts will always be printed even
- if logging has been suppressed for the connection type in question. However, operations
- such as sending and receiving data may be performed silently.</p>
+ <p>Fatal communication error and reconnection attempts will always be printed even
+ if logging has been suppressed for the connection type in question. However, operations
+ such as sending and receiving data will be performed silently.</p>
<p>It is possible to also specify <c>silent_connections</c> in a test suite. This is
accomplished by returning a tuple, <c>{silent_connections,ConnTypes}</c>, in the
<c>suite/0</c> or test case info list. If <c>ConnTypes</c> is a list of atoms
- (<c>telnet, ftp, rpc</c> and/or <c>snmp</c>), output for any corresponding connections
+ (<c>ssh, telnet, ftp, rpc</c> and/or <c>snmp</c>), output for any corresponding connections
will be suppressed. Full logging is per default enabled for any connection of type not
specified in <c>ConnTypes</c>. Hence, if <c>ConnTypes</c> is the empty list, logging
is enabled for all connections.</p>
- <p>The <c>silent_connections</c> setting returned from a test case info function overrides,
- for the test case in question, any setting made with <c>suite/0</c> (which is the setting
- used for all cases in the suite). Example:</p>
+ <p>Example:</p>
<pre>
-module(my_SUITE).
+
+ suite() -> [..., {silent_connections,[telnet,ssh]}, ...].
+
...
- suite() -> [..., {silent_connections,[telnet,ftp]}, ...].
- ...
+
my_testcase1() ->
- [{silent_connections,[ftp]}].
+ [{silent_connections,[ssh]}].
+
my_testcase1(_) ->
- ...
+ ...
+
my_testcase2(_) ->
- ...
+ ...
</pre>
<p>In this example, <c>suite/0</c> tells Common Test to suppress
- printouts from telnet and ftp connections. This is valid for
+ printouts from telnet and ssh connections. This is valid for
all test cases. However, <c>my_testcase1/0</c> specifies that
- for this test case, only ftp should be silent. The result is
+ for this test case, only ssh should be silent. The result is
that <c>my_testcase1</c> will get telnet info (if any) printed
- in the log, but not ftp info. <c>my_testcase2</c> will get no
+ in the log, but not ssh info. <c>my_testcase2</c> will get no
info from either connection printed.</p>
- <p>The <c>-silent_connections</c> tag (or
- <c>silent_connections</c> tagged tuple in the call to
- <c>ct:run_test/1</c>) overrides any settings in the test
- suite.</p>
+ <p><c>silent_connections</c> may also be specified with a term
+ in a test specification
+ (see <seealso marker="run_test_chapter#test_specifications">Test
+ Specifications</seealso>). Connections provided with the
+ <c>silent_connections</c> start flag/option, will be merged with
+ any connections listed in the test specification.</p>
+
+ <p>The <c>silent_connections</c> start flag/option and test
+ specification term, overrides any settings made by the info functions
+ inside the test suite.</p>
- <p>Note that in the current Common Test version, the
+ <note><p>Note that in the current Common Test version, the
<c>silent_connections</c> feature only works for telnet
- connections. Support for other connection types will be added
- in future Common Test versions.</p>
+ and ssh connections! Support for other connection types will be added
+ in future Common Test versions.</p></note>
</section>
</chapter>
diff --git a/lib/common_test/doc/src/write_test_chapter.xml b/lib/common_test/doc/src/write_test_chapter.xml
index 7b7e7af8ea..cc8d913994 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>2012</year>
+ <year>2003</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -47,7 +47,7 @@
module for details about these functions.</p>
<p>The CT application also includes other modules named
- <c><![CDATA[ct_<something>]]></c> that
+ <c><![CDATA[ct_<component>]]></c> that
provide various support, mainly simplified use of communication
protocols such as rpc, snmp, ftp, telnet, etc.</p>
@@ -173,7 +173,7 @@
</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>,
+ test case terminates due to a call to <c><seealso marker="ct#abort_current_testcase-1">ct:abort_current_testcase/1</seealso></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
@@ -243,7 +243,8 @@
<note><p>The test case function argument <c>Config</c> should not be
confused with the information that can be retrieved from
- configuration files (using ct:get_config/[1,2]). The Config argument
+ configuration files (using <c><seealso marker="ct#get_config-1">
+ ct:get_config/1/2</seealso></c>). The Config argument
should be used for runtime configuration of the test suite and the
test cases, while configuration files should typically contain data
related to the SUT. These two types of configuration data are handled
@@ -302,7 +303,7 @@
<item>
<p>
Use this to specify arbitrary data related to the testcase. This
- data can be retrieved at any time using the <c>ct:userdata/3</c>
+ data can be retrieved at any time using the <c><seealso marker="ct#userdata-3">ct:userdata/3</seealso></c>
utility function.
</p>
</item>
@@ -338,7 +339,8 @@
<pre>
testcase2() ->
- [{require, unix_telnet, {unix, [telnet, username, password]}},
+ [{require, unix_telnet, unix},
+ {require, {unix, [telnet, username, password]}},
{default_config, unix, [{telnet, "my_telnet_host"},
{username, "aladdin"},
{password, "sesame"}]}}].</pre>
@@ -346,7 +348,8 @@
</taglist>
<p>See the <seealso marker="config_file_chapter#require_config_data">Config files</seealso>
- chapter and the <c>ct:require/[1,2]</c> function in the
+ chapter and the <c><seealso marker="ct#require-1">
+ ct:require/1/2</seealso></c> function in the
<seealso marker="ct">ct</seealso> reference manual for more information about
<c>require</c>.</p>
@@ -823,7 +826,7 @@
Common Test to create one dedicated private directory per
test case and execution instead. This is accomplished by means of
the flag/option: <c>create_priv_dir</c> (to be used with the
- <c>ct_run</c> program, the <c>ct:run_test/1</c> function, or
+ <c>ct_run</c> program, the <c><seealso marker="ct#run_test-1">ct:run_test/1</seealso></c> function, or
as test specification term). There are three possible values
for this option:
<list>
@@ -839,7 +842,7 @@
become very inefficient for test runs with many test cases and/or
repetitions. Therefore, in case the manual version is instead used, the
test case must tell Common Test to create priv_dir when it needs it.
- It does this by calling the function <c>ct:make_priv_dir/0</c>.
+ It does this by calling the function <c><seealso marker="ct#make_priv_dir-0">ct:make_priv_dir/0</seealso></c>.
</p>
<note><p>You should not depend on current working directory for
@@ -883,11 +886,11 @@
of its sub-groups. If a timetrap value is defined by <c>group/1</c>
for a sub-group, it overrides that of its higher level groups. Timetrap
values set by individual test cases (by means of the test case info
- function) overrides both group- and suite- level timetraps.</p>
+ function) override both group- and suite- level timetraps.</p>
<p>It is also possible to dynamically set/reset a timetrap during the
excution of a test case, or configuration function. This is done by calling
- <c>ct:timetrap/1</c>. This function cancels the current timetrap
+ <c><seealso marker="ct#timetrap-1">ct:timetrap/1</seealso></c>. This function cancels the current timetrap
and starts a new one (that stays active until timeout, or end of the
current function).</p>
@@ -900,12 +903,12 @@
<p>If a test case needs to suspend itself for a time that also gets
multipled by <c>multiply_timetraps</c> (and possibly also scaled up if
- <c>scale_timetraps</c> is enabled), the function <c>ct:sleep/1</c>
+ <c>scale_timetraps</c> is enabled), the function <c><seealso marker="ct#sleep-1">ct:sleep/1</seealso></c>
may be used (instead of e.g. <c>timer:sleep/1</c>).</p>
<p>A function (<c>fun/0</c> or <c>MFA</c>) may be specified as
timetrap value in the suite-, group- and test case info function, as
- well as argument to the <c>ct:timetrap/1</c> function. Examples:</p>
+ well as argument to the <c><seealso marker="ct#timetrap-1">ct:timetrap/1</seealso></c> function. Examples:</p>
<p><c>{timetrap,{my_test_utils,timetrap,[?MODULE,system_start]}}</c></p>
<p><c>ct:timetrap(fun() -> my_timetrap(TestCaseName, Config) end)</c></p>
@@ -932,6 +935,97 @@
</section>
<section>
+ <marker id="logging"></marker>
+ <title>Logging - categories and verbosity levels</title>
+ <p>Common Test provides three main functions for printing strings:</p>
+ <list>
+ <item><c>ct:log(Category, Importance, Format, Args)</c></item>
+ <item><c>ct:print(Category, Importance, Format, Args)</c></item>
+ <item><c>ct:pal(Category, Importance, Format, Args)</c></item>
+ </list>
+ <p>The <c>log/1/2/3/4</c> function will print a string to the test case
+ log file. The <c>print/1/2/3/4</c> function will print the string to screen,
+ and the <c>pal/1/2/3/4</c> function will print the same string both to file and
+ screen. (The functions are documented in the <c>ct</c> reference manual).</p>
+
+ <p>The optional <c>Category</c> argument may be used to categorize the
+ log printout, and categories can be used for two things:</p>
+ <list>
+ <item>To compare the importance of the printout to a specific
+ verbosity level, and</item>
+ <item>to format the printout according to a user specific HTML
+ Style Sheet (CSS).</item>
+ </list>
+
+ <p>The <c>Importance</c> argument specifies a level of importance
+ which, compared to a verbosity level (general and/or set per category),
+ determines if the printout should be visible or not. <c>Importance</c>
+ is an arbitrary integer in the range 0..99. Pre-defined constants
+ exist in the <c>ct.hrl</c> header file. The default importance level,
+ <c>?STD_IMPORTANCE</c> (used if the <c>Importance</c> argument is not
+ provided), is 50. This is also the importance used for standard IO, e.g.
+ from printouts made with <c>io:format/2</c>, <c>io:put_chars/1</c>, etc.</p>
+
+ <p><c>Importance</c> is compared to a verbosity level set by means of the
+ <c>verbosity</c> start flag/option. The verbosity level can be set per
+ category and/or generally. The default verbosity level, <c>?STD_VERBOSITY</c>,
+ is 50, i.e. all standard IO gets printed. If a lower verbosity level is set,
+ standard IO printouts will be ignored. Common Test performs the following test:</p>
+ <pre>Importance >= (100-VerbosityLevel)</pre>
+ <p>This also means that verbosity level 0 effectively turns all logging off
+ (with the exception of printouts made by Common Test itself).</p>
+
+ <p>The general verbosity level is not associated with any particular
+ category. This level sets the threshold for the standard IO printouts,
+ uncategorized <c>ct:log/print/pal</c> printouts, as well as
+ printouts for categories with undefined verbosity level.</p>
+
+ <p>Example:</p>
+ <pre>
+ Some printouts during test case execution:
+
+ io:format("1. Standard IO, importance = ~w~n", [?STD_IMPORTANCE]),
+ ct:log("2. Uncategorized, importance = ~w", [?STD_IMPORTANCE]),
+ ct:log(info, "3. Categorized info, importance = ~w", [?STD_IMPORTANCE]]),
+ ct:log(info, ?LOW_IMPORTANCE, "4. Categorized info, importance = ~w", [?LOW_IMPORTANCE]),
+ ct:log(error, "5. Categorized error, importance = ~w", [?HI_IMPORTANCE]),
+ ct:log(error, ?HI_IMPORTANCE, "6. Categorized error, importance = ~w", [?MAX_IMPORTANCE]),
+
+ If starting the test without specifying any verbosity levels:
+
+ $ ct_run ...
+
+ the following gets printed:
+
+ 1. Standard IO, importance = 50
+ 2. Uncategorized, importance = 50
+ 3. Categorized info, importance = 50
+ 5. Categorized error, importance = 75
+ 6. Categorized error, importance = 99
+
+ If starting the test with:
+
+ $ ct_run -verbosity 1 and info 75
+
+ the following gets printed:
+
+ 3. Categorized info, importance = 50
+ 4. Categorized info, importance = 25
+ 6. Categorized error, importance = 99</pre>
+
+ <p>How categories can be mapped to CSS tags is documented in the
+ <seealso marker="run_test_chapter#html_stylesheet">Running Tests</seealso>
+ chapter.</p>
+
+ <p>The <c>Format</c> and <c>Args</c> arguments in <c>ct:log/print/pal</c> are
+ always passed on to the <c>io:format/3</c> function in <c>stdlib</c>
+ (please see the <c>io</c> manual page for details).</p>
+
+ <p>For more information about log files, please see the
+ <seealso marker="run_test_chapter#log_files">Running Tests</seealso> chapter.</p>
+ </section>
+
+ <section>
<title>Illegal dependencies</title>
<p>Even though it is highly efficient to write test suites with
@@ -941,7 +1035,6 @@
Erlang/OTP test suites.</p>
<list>
-
<item>Depending on current directory, and writing there:<br></br>
<p>This is a common error in test suites. It is assumed that
@@ -953,19 +1046,10 @@
</p>
</item>
- <item>Depending on the Clearcase (file version control system)
- paths and files:<br></br>
-
- <p>The test suites are stored in Clearcase but are not
- (necessarily) run within this environment. The directory
- structure may vary from test run to test run.
- </p>
- </item>
-
<item>Depending on execution order:<br></br>
- <p>During development of test suites, no assumption should be made
- (preferrably) about the execution order of the test cases or suites.
+ <p>During development of test suites, no assumption should preferrably
+ be made about the execution order of the test cases or suites.
E.g. a test case should not assume that a server it depends on,
has already been started by a previous test case. There are
several reasons for this:
diff --git a/lib/common_test/include/ct.hrl b/lib/common_test/include/ct.hrl
index 5a77108e1a..bde2709ad1 100644
--- a/lib/common_test/include/ct.hrl
+++ b/lib/common_test/include/ct.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -19,3 +19,16 @@
-include_lib("test_server/include/test_server.hrl").
+%% the log level is used as argument to any CT logging function
+-define(MIN_IMPORTANCE, 0 ).
+-define(LOW_IMPORTANCE, 25).
+-define(STD_IMPORTANCE, 50).
+-define(HI_IMPORTANCE, 75).
+-define(MAX_IMPORTANCE, 99).
+
+%% verbosity thresholds to filter out logging printouts
+-define(MIN_VERBOSITY, 0 ). %% turn logging off
+-define(LOW_VERBOSITY, 25 ).
+-define(STD_VERBOSITY, 50 ).
+-define(HI_VERBOSITY, 75 ).
+-define(MAX_VERBOSITY, 100).
diff --git a/lib/common_test/priv/Makefile.in b/lib/common_test/priv/Makefile.in
index 8be6248e17..5a9fabbe45 100644
--- a/lib/common_test/priv/Makefile.in
+++ b/lib/common_test/priv/Makefile.in
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2003-2011. All Rights Reserved.
+# Copyright Ericsson AB 2003-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -60,6 +60,7 @@ FILES = vts.tool
SCRIPTS =
IMAGES = tile1.jpg
CSS = ct_default.css
+JS = jquery-latest.js jquery.tablesorter.min.js
#
# Rules
@@ -67,15 +68,15 @@ CSS = ct_default.css
include ../../test_server/vsn.mk
debug opt:
- sed -e 's;@CT_VSN@;$(VSN);' \
+ $(V_at)sed -e 's;@CT_VSN@;$(VSN);' \
-e 's;@TS_VSN@;$(TEST_SERVER_VSN);' \
../install.sh.in > install.sh
- chmod 775 install.sh
+ $(V_at)chmod 775 install.sh
docs:
clean:
- rm -f $(SCRIPTS)
+ $(V_at)rm -f $(SCRIPTS)
# ----------------------------------------------------
@@ -86,11 +87,11 @@ include $(ERL_TOP)/make/otp_release_targets.mk
ifeq ($(XNIX),true)
release_spec: opt
$(INSTALL_DIR) "$(RELSYSDIR)/priv"
- $(INSTALL_DATA) $(FILES) $(IMAGES) $(CSS) "$(RELSYSDIR)/priv"
+ $(INSTALL_DATA) $(FILES) $(IMAGES) $(CSS) $(JS) "$(RELSYSDIR)/priv"
else
release_spec: opt
$(INSTALL_DIR) "$(RELSYSDIR)/priv"
- $(INSTALL_DATA) $(FILES) $(IMAGES) $(CSS) "$(RELSYSDIR)/priv"
+ $(INSTALL_DATA) $(FILES) $(IMAGES) $(CSS) $(JS) "$(RELSYSDIR)/priv"
endif
release_docs_spec:
@@ -107,6 +108,7 @@ else
FILES = vts.tool
IMAGES = tile1.jpg
CSS = ct_default.css
+JS = jquery-latest.js jquery.tablesorter.min.js
#
# Rules
@@ -126,7 +128,7 @@ include $(ERL_TOP)/make/otp_release_targets.mk
release_spec: opt
$(INSTALL_DIR) "$(RELSYSDIR)/priv"
- $(INSTALL_DATA) $(FILES) $(IMAGES) $(CSS) "$(RELSYSDIR)/priv"
+ $(INSTALL_DATA) $(FILES) $(IMAGES) $(CSS) $(JS) "$(RELSYSDIR)/priv"
release_docs_spec:
diff --git a/lib/common_test/priv/ct_default.css b/lib/common_test/priv/ct_default.css
index 8ae6990cd8..1188f8f676 100644
--- a/lib/common_test/priv/ct_default.css
+++ b/lib/common_test/priv/ct_default.css
@@ -136,7 +136,18 @@ th {
}
thead th {
- background: #2C5755; text-align: center;
+ background: #3F3F3F; color: #fff;
+ font-family: arial, sans-serif; font-size: 120%;
+ letter-spacing: -0.5px;
+ font-weight: bold; text-align: center;
+ padding-right: .5em; vertical-align: top;
+ text-decoration: underline;
+}
+
+tfoot td {
+ font-family: arial, sans-serif; font-size: 110%;
+ letter-spacing: -0.5px;
+ font-weight: bold;
}
.odd td {
@@ -167,10 +178,6 @@ th a, td a:active {
color: #85ABD5;
}
-tfoot th, tfoot td {
- background: #3F3F3F; color: #fff;
-}
-
th + td {
padding-left: .5em;
}
diff --git a/lib/common_test/priv/jquery-latest.js b/lib/common_test/priv/jquery-latest.js
new file mode 100644
index 0000000000..ac7e7009dc
--- /dev/null
+++ b/lib/common_test/priv/jquery-latest.js
@@ -0,0 +1,154 @@
+/*!
+ * jQuery JavaScript Library v1.4.2
+ * http://jquery.com/
+ *
+ * Copyright 2010, John Resig
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * Includes Sizzle.js
+ * http://sizzlejs.com/
+ * Copyright 2010, The Dojo Foundation
+ * Released under the MIT, BSD, and GPL Licenses.
+ *
+ * Date: Sat Feb 13 22:33:48 2010 -0500
+ */
+(function(A,w){function ma(){if(!c.isReady){try{s.documentElement.doScroll("left")}catch(a){setTimeout(ma,1);return}c.ready()}}function Qa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function X(a,b,d,f,e,j){var i=a.length;if(typeof b==="object"){for(var o in b)X(a,o,b[o],f,e,d);return a}if(d!==w){f=!j&&f&&c.isFunction(d);for(o=0;o<i;o++)e(a[o],b,f?d.call(a[o],o,e(a[o],b)):d,j);return a}return i?
+e(a[0],b):w}function J(){return(new Date).getTime()}function Y(){return false}function Z(){return true}function na(a,b,d){d[0].type=a;return c.event.handle.apply(b,d)}function oa(a){var b,d=[],f=[],e=arguments,j,i,o,k,n,r;i=c.data(this,"events");if(!(a.liveFired===this||!i||!i.live||a.button&&a.type==="click")){a.liveFired=this;var u=i.live.slice(0);for(k=0;k<u.length;k++){i=u[k];i.origType.replace(O,"")===a.type?f.push(i.selector):u.splice(k--,1)}j=c(a.target).closest(f,a.currentTarget);n=0;for(r=
+j.length;n<r;n++)for(k=0;k<u.length;k++){i=u[k];if(j[n].selector===i.selector){o=j[n].elem;f=null;if(i.preType==="mouseenter"||i.preType==="mouseleave")f=c(a.relatedTarget).closest(i.selector)[0];if(!f||f!==o)d.push({elem:o,handleObj:i})}}n=0;for(r=d.length;n<r;n++){j=d[n];a.currentTarget=j.elem;a.data=j.handleObj.data;a.handleObj=j.handleObj;if(j.handleObj.origHandler.apply(j.elem,e)===false){b=false;break}}return b}}function pa(a,b){return"live."+(a&&a!=="*"?a+".":"")+b.replace(/\./g,"`").replace(/ /g,
+"&")}function qa(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function ra(a,b){var d=0;b.each(function(){if(this.nodeName===(a[d]&&a[d].nodeName)){var f=c.data(a[d++]),e=c.data(this,f);if(f=f&&f.events){delete e.handle;e.events={};for(var j in f)for(var i in f[j])c.event.add(this,j,f[j][i],f[j][i].data)}}})}function sa(a,b,d){var f,e,j;b=b&&b[0]?b[0].ownerDocument||b[0]:s;if(a.length===1&&typeof a[0]==="string"&&a[0].length<512&&b===s&&!ta.test(a[0])&&(c.support.checkClone||!ua.test(a[0]))){e=
+true;if(j=c.fragments[a[0]])if(j!==1)f=j}if(!f){f=b.createDocumentFragment();c.clean(a,b,f,d)}if(e)c.fragments[a[0]]=j?f:1;return{fragment:f,cacheable:e}}function K(a,b){var d={};c.each(va.concat.apply([],va.slice(0,b)),function(){d[this]=a});return d}function wa(a){return"scrollTo"in a&&a.document?a:a.nodeType===9?a.defaultView||a.parentWindow:false}var c=function(a,b){return new c.fn.init(a,b)},Ra=A.jQuery,Sa=A.$,s=A.document,T,Ta=/^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/,Ua=/^.[^:#\[\.,]*$/,Va=/\S/,
+Wa=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,Xa=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,P=navigator.userAgent,xa=false,Q=[],L,$=Object.prototype.toString,aa=Object.prototype.hasOwnProperty,ba=Array.prototype.push,R=Array.prototype.slice,ya=Array.prototype.indexOf;c.fn=c.prototype={init:function(a,b){var d,f;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(a==="body"&&!b){this.context=s;this[0]=s.body;this.selector="body";this.length=1;return this}if(typeof a==="string")if((d=Ta.exec(a))&&
+(d[1]||!b))if(d[1]){f=b?b.ownerDocument||b:s;if(a=Xa.exec(a))if(c.isPlainObject(b)){a=[s.createElement(a[1])];c.fn.attr.call(a,b,true)}else a=[f.createElement(a[1])];else{a=sa([d[1]],[f]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}return c.merge(this,a)}else{if(b=s.getElementById(d[2])){if(b.id!==d[2])return T.find(a);this.length=1;this[0]=b}this.context=s;this.selector=a;return this}else if(!b&&/^\w+$/.test(a)){this.selector=a;this.context=s;a=s.getElementsByTagName(a);return c.merge(this,
+a)}else return!b||b.jquery?(b||T).find(a):c(b).find(a);else if(c.isFunction(a))return T.ready(a);if(a.selector!==w){this.selector=a.selector;this.context=a.context}return c.makeArray(a,this)},selector:"",jquery:"1.4.2",length:0,size:function(){return this.length},toArray:function(){return R.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,d){var f=c();c.isArray(a)?ba.apply(f,a):c.merge(f,a);f.prevObject=this;f.context=this.context;if(b===
+"find")f.selector=this.selector+(this.selector?" ":"")+d;else if(b)f.selector=this.selector+"."+b+"("+d+")";return f},each:function(a,b){return c.each(this,a,b)},ready:function(a){c.bindReady();if(c.isReady)a.call(s,c);else Q&&Q.push(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(R.apply(this,arguments),"slice",R.call(arguments).join(","))},map:function(a){return this.pushStack(c.map(this,
+function(b,d){return a.call(b,d,b)}))},end:function(){return this.prevObject||c(null)},push:ba,sort:[].sort,splice:[].splice};c.fn.init.prototype=c.fn;c.extend=c.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,f=false,e,j,i,o;if(typeof a==="boolean"){f=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!c.isFunction(a))a={};if(d===b){a=this;--b}for(;b<d;b++)if((e=arguments[b])!=null)for(j in e){i=a[j];o=e[j];if(a!==o)if(f&&o&&(c.isPlainObject(o)||c.isArray(o))){i=i&&(c.isPlainObject(i)||
+c.isArray(i))?i:c.isArray(o)?[]:{};a[j]=c.extend(f,i,o)}else if(o!==w)a[j]=o}return a};c.extend({noConflict:function(a){A.$=Sa;if(a)A.jQuery=Ra;return c},isReady:false,ready:function(){if(!c.isReady){if(!s.body)return setTimeout(c.ready,13);c.isReady=true;if(Q){for(var a,b=0;a=Q[b++];)a.call(s,c);Q=null}c.fn.triggerHandler&&c(s).triggerHandler("ready")}},bindReady:function(){if(!xa){xa=true;if(s.readyState==="complete")return c.ready();if(s.addEventListener){s.addEventListener("DOMContentLoaded",
+L,false);A.addEventListener("load",c.ready,false)}else if(s.attachEvent){s.attachEvent("onreadystatechange",L);A.attachEvent("onload",c.ready);var a=false;try{a=A.frameElement==null}catch(b){}s.documentElement.doScroll&&a&&ma()}}},isFunction:function(a){return $.call(a)==="[object Function]"},isArray:function(a){return $.call(a)==="[object Array]"},isPlainObject:function(a){if(!a||$.call(a)!=="[object Object]"||a.nodeType||a.setInterval)return false;if(a.constructor&&!aa.call(a,"constructor")&&!aa.call(a.constructor.prototype,
+"isPrototypeOf"))return false;var b;for(b in a);return b===w||aa.call(a,b)},isEmptyObject:function(a){for(var b in a)return false;return true},error:function(a){throw a;},parseJSON:function(a){if(typeof a!=="string"||!a)return null;a=c.trim(a);if(/^[\],:{}\s]*$/.test(a.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return A.JSON&&A.JSON.parse?A.JSON.parse(a):(new Function("return "+
+a))();else c.error("Invalid JSON: "+a)},noop:function(){},globalEval:function(a){if(a&&Va.test(a)){var b=s.getElementsByTagName("head")[0]||s.documentElement,d=s.createElement("script");d.type="text/javascript";if(c.support.scriptEval)d.appendChild(s.createTextNode(a));else d.text=a;b.insertBefore(d,b.firstChild);b.removeChild(d)}},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,b,d){var f,e=0,j=a.length,i=j===w||c.isFunction(a);if(d)if(i)for(f in a){if(b.apply(a[f],
+d)===false)break}else for(;e<j;){if(b.apply(a[e++],d)===false)break}else if(i)for(f in a){if(b.call(a[f],f,a[f])===false)break}else for(d=a[0];e<j&&b.call(d,e,d)!==false;d=a[++e]);return a},trim:function(a){return(a||"").replace(Wa,"")},makeArray:function(a,b){b=b||[];if(a!=null)a.length==null||typeof a==="string"||c.isFunction(a)||typeof a!=="function"&&a.setInterval?ba.call(b,a):c.merge(b,a);return b},inArray:function(a,b){if(b.indexOf)return b.indexOf(a);for(var d=0,f=b.length;d<f;d++)if(b[d]===
+a)return d;return-1},merge:function(a,b){var d=a.length,f=0;if(typeof b.length==="number")for(var e=b.length;f<e;f++)a[d++]=b[f];else for(;b[f]!==w;)a[d++]=b[f++];a.length=d;return a},grep:function(a,b,d){for(var f=[],e=0,j=a.length;e<j;e++)!d!==!b(a[e],e)&&f.push(a[e]);return f},map:function(a,b,d){for(var f=[],e,j=0,i=a.length;j<i;j++){e=b(a[j],j,d);if(e!=null)f[f.length]=e}return f.concat.apply([],f)},guid:1,proxy:function(a,b,d){if(arguments.length===2)if(typeof b==="string"){d=a;a=d[b];b=w}else if(b&&
+!c.isFunction(b)){d=b;b=w}if(!b&&a)b=function(){return a.apply(d||this,arguments)};if(a)b.guid=a.guid=a.guid||b.guid||c.guid++;return b},uaMatch:function(a){a=a.toLowerCase();a=/(webkit)[ \/]([\w.]+)/.exec(a)||/(opera)(?:.*version)?[ \/]([\w.]+)/.exec(a)||/(msie) ([\w.]+)/.exec(a)||!/compatible/.test(a)&&/(mozilla)(?:.*? rv:([\w.]+))?/.exec(a)||[];return{browser:a[1]||"",version:a[2]||"0"}},browser:{}});P=c.uaMatch(P);if(P.browser){c.browser[P.browser]=true;c.browser.version=P.version}if(c.browser.webkit)c.browser.safari=
+true;if(ya)c.inArray=function(a,b){return ya.call(b,a)};T=c(s);if(s.addEventListener)L=function(){s.removeEventListener("DOMContentLoaded",L,false);c.ready()};else if(s.attachEvent)L=function(){if(s.readyState==="complete"){s.detachEvent("onreadystatechange",L);c.ready()}};(function(){c.support={};var a=s.documentElement,b=s.createElement("script"),d=s.createElement("div"),f="script"+J();d.style.display="none";d.innerHTML=" <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";
+var e=d.getElementsByTagName("*"),j=d.getElementsByTagName("a")[0];if(!(!e||!e.length||!j)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(j.getAttribute("style")),hrefNormalized:j.getAttribute("href")==="/a",opacity:/^0.55$/.test(j.style.opacity),cssFloat:!!j.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:s.createElement("select").appendChild(s.createElement("option")).selected,
+parentNode:d.removeChild(d.appendChild(s.createElement("div"))).parentNode===null,deleteExpando:true,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null};b.type="text/javascript";try{b.appendChild(s.createTextNode("window."+f+"=1;"))}catch(i){}a.insertBefore(b,a.firstChild);if(A[f]){c.support.scriptEval=true;delete A[f]}try{delete b.test}catch(o){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function k(){c.support.noCloneEvent=
+false;d.detachEvent("onclick",k)});d.cloneNode(true).fireEvent("onclick")}d=s.createElement("div");d.innerHTML="<input type='radio' name='radiotest' checked='checked'/>";a=s.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var k=s.createElement("div");k.style.width=k.style.paddingLeft="1px";s.body.appendChild(k);c.boxModel=c.support.boxModel=k.offsetWidth===2;s.body.removeChild(k).style.display="none"});a=function(k){var n=
+s.createElement("div");k="on"+k;var r=k in n;if(!r){n.setAttribute(k,"return;");r=typeof n[k]==="function"}return r};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=e=j=null}})();c.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var G="jQuery"+J(),Ya=0,za={};c.extend({cache:{},expando:G,noData:{embed:true,object:true,
+applet:true},data:function(a,b,d){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var f=a[G],e=c.cache;if(!f&&typeof b==="string"&&d===w)return null;f||(f=++Ya);if(typeof b==="object"){a[G]=f;e[f]=c.extend(true,{},b)}else if(!e[f]){a[G]=f;e[f]={}}a=e[f];if(d!==w)a[b]=d;return typeof b==="string"?a[b]:a}},removeData:function(a,b){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var d=a[G],f=c.cache,e=f[d];if(b){if(e){delete e[b];c.isEmptyObject(e)&&c.removeData(a)}}else{if(c.support.deleteExpando)delete a[c.expando];
+else a.removeAttribute&&a.removeAttribute(c.expando);delete f[d]}}}});c.fn.extend({data:function(a,b){if(typeof a==="undefined"&&this.length)return c.data(this[0]);else if(typeof a==="object")return this.each(function(){c.data(this,a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===w){var f=this.triggerHandler("getData"+d[1]+"!",[d[0]]);if(f===w&&this.length)f=c.data(this[0],a);return f===w&&d[1]?this.data(d[0]):f}else return this.trigger("setData"+d[1]+"!",[d[0],b]).each(function(){c.data(this,
+a,b)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var f=c.data(a,b);if(!d)return f||[];if(!f||c.isArray(d))f=c.data(a,b,c.makeArray(d));else f.push(d);return f}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),f=d.shift();if(f==="inprogress")f=d.shift();if(f){b==="fx"&&d.unshift("inprogress");f.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b===
+w)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var Aa=/[\n\t]/g,ca=/\s+/,Za=/\r/g,$a=/href|src|style/,ab=/(button|input)/i,bb=/(button|input|object|select|textarea)/i,
+cb=/^(a|area)$/i,Ba=/radio|checkbox/;c.fn.extend({attr:function(a,b){return X(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(n){var r=c(this);r.addClass(a.call(this,n,r.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1)if(e.className){for(var j=" "+e.className+" ",
+i=e.className,o=0,k=b.length;o<k;o++)if(j.indexOf(" "+b[o]+" ")<0)i+=" "+b[o];e.className=c.trim(i)}else e.className=a}return this},removeClass:function(a){if(c.isFunction(a))return this.each(function(k){var n=c(this);n.removeClass(a.call(this,k,n.attr("class")))});if(a&&typeof a==="string"||a===w)for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1&&e.className)if(a){for(var j=(" "+e.className+" ").replace(Aa," "),i=0,o=b.length;i<o;i++)j=j.replace(" "+b[i]+" ",
+" ");e.className=c.trim(j)}else e.className=""}return this},toggleClass:function(a,b){var d=typeof a,f=typeof b==="boolean";if(c.isFunction(a))return this.each(function(e){var j=c(this);j.toggleClass(a.call(this,e,j.attr("class"),b),b)});return this.each(function(){if(d==="string")for(var e,j=0,i=c(this),o=b,k=a.split(ca);e=k[j++];){o=f?o:!i.hasClass(e);i[o?"addClass":"removeClass"](e)}else if(d==="undefined"||d==="boolean"){this.className&&c.data(this,"__className__",this.className);this.className=
+this.className||a===false?"":c.data(this,"__className__")||""}})},hasClass:function(a){a=" "+a+" ";for(var b=0,d=this.length;b<d;b++)if((" "+this[b].className+" ").replace(Aa," ").indexOf(a)>-1)return true;return false},val:function(a){if(a===w){var b=this[0];if(b){if(c.nodeName(b,"option"))return(b.attributes.value||{}).specified?b.value:b.text;if(c.nodeName(b,"select")){var d=b.selectedIndex,f=[],e=b.options;b=b.type==="select-one";if(d<0)return null;var j=b?d:0;for(d=b?d+1:e.length;j<d;j++){var i=
+e[j];if(i.selected){a=c(i).val();if(b)return a;f.push(a)}}return f}if(Ba.test(b.type)&&!c.support.checkOn)return b.getAttribute("value")===null?"on":b.value;return(b.value||"").replace(Za,"")}return w}var o=c.isFunction(a);return this.each(function(k){var n=c(this),r=a;if(this.nodeType===1){if(o)r=a.call(this,k,n.val());if(typeof r==="number")r+="";if(c.isArray(r)&&Ba.test(this.type))this.checked=c.inArray(n.val(),r)>=0;else if(c.nodeName(this,"select")){var u=c.makeArray(r);c("option",this).each(function(){this.selected=
+c.inArray(c(this).val(),u)>=0});if(!u.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,f){if(!a||a.nodeType===3||a.nodeType===8)return w;if(f&&b in c.attrFn)return c(a)[b](d);f=a.nodeType!==1||!c.isXMLDoc(a);var e=d!==w;b=f&&c.props[b]||b;if(a.nodeType===1){var j=$a.test(b);if(b in a&&f&&!j){if(e){b==="type"&&ab.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed");
+a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:bb.test(a.nodeName)||cb.test(a.nodeName)&&a.href?0:w;return a[b]}if(!c.support.style&&f&&b==="style"){if(e)a.style.cssText=""+d;return a.style.cssText}e&&a.setAttribute(b,""+d);a=!c.support.hrefNormalized&&f&&j?a.getAttribute(b,2):a.getAttribute(b);return a===null?w:a}return c.style(a,b,d)}});var O=/\.(.*)$/,db=function(a){return a.replace(/[^\w\s\.\|`]/g,
+function(b){return"\\"+b})};c.event={add:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){if(a.setInterval&&a!==A&&!a.frameElement)a=A;var e,j;if(d.handler){e=d;d=e.handler}if(!d.guid)d.guid=c.guid++;if(j=c.data(a)){var i=j.events=j.events||{},o=j.handle;if(!o)j.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem,arguments):w};o.elem=a;b=b.split(" ");for(var k,n=0,r;k=b[n++];){j=e?c.extend({},e):{handler:d,data:f};if(k.indexOf(".")>-1){r=k.split(".");
+k=r.shift();j.namespace=r.slice(0).sort().join(".")}else{r=[];j.namespace=""}j.type=k;j.guid=d.guid;var u=i[k],z=c.event.special[k]||{};if(!u){u=i[k]=[];if(!z.setup||z.setup.call(a,f,r,o)===false)if(a.addEventListener)a.addEventListener(k,o,false);else a.attachEvent&&a.attachEvent("on"+k,o)}if(z.add){z.add.call(a,j);if(!j.handler.guid)j.handler.guid=d.guid}u.push(j);c.event.global[k]=true}a=null}}},global:{},remove:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){var e,j=0,i,o,k,n,r,u,z=c.data(a),
+C=z&&z.events;if(z&&C){if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(e in C)c.event.remove(a,e+b)}else{for(b=b.split(" ");e=b[j++];){n=e;i=e.indexOf(".")<0;o=[];if(!i){o=e.split(".");e=o.shift();k=new RegExp("(^|\\.)"+c.map(o.slice(0).sort(),db).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(r=C[e])if(d){n=c.event.special[e]||{};for(B=f||0;B<r.length;B++){u=r[B];if(d.guid===u.guid){if(i||k.test(u.namespace)){f==null&&r.splice(B--,1);n.remove&&n.remove.call(a,u)}if(f!=
+null)break}}if(r.length===0||f!=null&&r.length===1){if(!n.teardown||n.teardown.call(a,o)===false)Ca(a,e,z.handle);delete C[e]}}else for(var B=0;B<r.length;B++){u=r[B];if(i||k.test(u.namespace)){c.event.remove(a,n,u.handler,B);r.splice(B--,1)}}}if(c.isEmptyObject(C)){if(b=z.handle)b.elem=null;delete z.events;delete z.handle;c.isEmptyObject(z)&&c.removeData(a)}}}}},trigger:function(a,b,d,f){var e=a.type||a;if(!f){a=typeof a==="object"?a[G]?a:c.extend(c.Event(e),a):c.Event(e);if(e.indexOf("!")>=0){a.type=
+e=e.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[e]&&c.each(c.cache,function(){this.events&&this.events[e]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===8)return w;a.result=w;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(f=c.data(d,"handle"))&&f.apply(d,b);f=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+e]&&d["on"+e].apply(d,b)===false)a.result=false}catch(j){}if(!a.isPropagationStopped()&&
+f)c.event.trigger(a,b,f,true);else if(!a.isDefaultPrevented()){f=a.target;var i,o=c.nodeName(f,"a")&&e==="click",k=c.event.special[e]||{};if((!k._default||k._default.call(d,a)===false)&&!o&&!(f&&f.nodeName&&c.noData[f.nodeName.toLowerCase()])){try{if(f[e]){if(i=f["on"+e])f["on"+e]=null;c.event.triggered=true;f[e]()}}catch(n){}if(i)f["on"+e]=i;c.event.triggered=false}}},handle:function(a){var b,d,f,e;a=arguments[0]=c.event.fix(a||A.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive;
+if(!b){d=a.type.split(".");a.type=d.shift();f=new RegExp("(^|\\.)"+d.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)")}e=c.data(this,"events");d=e[a.type];if(e&&d){d=d.slice(0);e=0;for(var j=d.length;e<j;e++){var i=d[e];if(b||f.test(i.namespace)){a.handler=i.handler;a.data=i.data;a.handleObj=i;i=i.handler.apply(this,arguments);if(i!==w){a.result=i;if(i===false){a.preventDefault();a.stopPropagation()}}if(a.isImmediatePropagationStopped())break}}}return a.result},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
+fix:function(a){if(a[G])return a;var b=a;a=c.Event(b);for(var d=this.props.length,f;d;){f=this.props[--d];a[f]=b[f]}if(!a.target)a.target=a.srcElement||s;if(a.target.nodeType===3)a.target=a.target.parentNode;if(!a.relatedTarget&&a.fromElement)a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement;if(a.pageX==null&&a.clientX!=null){b=s.documentElement;d=s.body;a.pageX=a.clientX+(b&&b.scrollLeft||d&&d.scrollLeft||0)-(b&&b.clientLeft||d&&d.clientLeft||0);a.pageY=a.clientY+(b&&b.scrollTop||
+d&&d.scrollTop||0)-(b&&b.clientTop||d&&d.clientTop||0)}if(!a.which&&(a.charCode||a.charCode===0?a.charCode:a.keyCode))a.which=a.charCode||a.keyCode;if(!a.metaKey&&a.ctrlKey)a.metaKey=a.ctrlKey;if(!a.which&&a.button!==w)a.which=a.button&1?1:a.button&2?3:a.button&4?2:0;return a},guid:1E8,proxy:c.proxy,special:{ready:{setup:c.bindReady,teardown:c.noop},live:{add:function(a){c.event.add(this,a.origType,c.extend({},a,{handler:oa}))},remove:function(a){var b=true,d=a.origType.replace(O,"");c.each(c.data(this,
+"events").live||[],function(){if(d===this.origType.replace(O,""))return b=false});b&&c.event.remove(this,a.origType,oa)}},beforeunload:{setup:function(a,b,d){if(this.setInterval)this.onbeforeunload=d;return false},teardown:function(a,b){if(this.onbeforeunload===b)this.onbeforeunload=null}}}};var Ca=s.removeEventListener?function(a,b,d){a.removeEventListener(b,d,false)}:function(a,b,d){a.detachEvent("on"+b,d)};c.Event=function(a){if(!this.preventDefault)return new c.Event(a);if(a&&a.type){this.originalEvent=
+a;this.type=a.type}else this.type=a;this.timeStamp=J();this[G]=true};c.Event.prototype={preventDefault:function(){this.isDefaultPrevented=Z;var a=this.originalEvent;if(a){a.preventDefault&&a.preventDefault();a.returnValue=false}},stopPropagation:function(){this.isPropagationStopped=Z;var a=this.originalEvent;if(a){a.stopPropagation&&a.stopPropagation();a.cancelBubble=true}},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=Z;this.stopPropagation()},isDefaultPrevented:Y,isPropagationStopped:Y,
+isImmediatePropagationStopped:Y};var Da=function(a){var b=a.relatedTarget;try{for(;b&&b!==this;)b=b.parentNode;if(b!==this){a.type=a.data;c.event.handle.apply(this,arguments)}}catch(d){}},Ea=function(a){a.type=a.data;c.event.handle.apply(this,arguments)};c.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){c.event.special[a]={setup:function(d){c.event.add(this,b,d&&d.selector?Ea:Da,a)},teardown:function(d){c.event.remove(this,b,d&&d.selector?Ea:Da)}}});if(!c.support.submitBubbles)c.event.special.submit=
+{setup:function(){if(this.nodeName.toLowerCase()!=="form"){c.event.add(this,"click.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="submit"||d==="image")&&c(b).closest("form").length)return na("submit",this,arguments)});c.event.add(this,"keypress.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="text"||d==="password")&&c(b).closest("form").length&&a.keyCode===13)return na("submit",this,arguments)})}else return false},teardown:function(){c.event.remove(this,".specialSubmit")}};
+if(!c.support.changeBubbles){var da=/textarea|input|select/i,ea,Fa=function(a){var b=a.type,d=a.value;if(b==="radio"||b==="checkbox")d=a.checked;else if(b==="select-multiple")d=a.selectedIndex>-1?c.map(a.options,function(f){return f.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},fa=function(a,b){var d=a.target,f,e;if(!(!da.test(d.nodeName)||d.readOnly)){f=c.data(d,"_change_data");e=Fa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data",
+e);if(!(f===w||e===f))if(f!=null||e){a.type="change";return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:fa,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return fa.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return fa.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a,
+"_change_data",Fa(a))}},setup:function(){if(this.type==="file")return false;for(var a in ea)c.event.add(this,a+".specialChange",ea[a]);return da.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return da.test(this.nodeName)}};ea=c.event.special.change.filters}s.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(f){f=c.event.fix(f);f.type=b;return c.event.handle.call(this,f)}c.event.special[b]={setup:function(){this.addEventListener(a,
+d,true)},teardown:function(){this.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,f,e){if(typeof d==="object"){for(var j in d)this[b](j,f,d[j],e);return this}if(c.isFunction(f)){e=f;f=w}var i=b==="one"?c.proxy(e,function(k){c(this).unbind(k,i);return e.apply(this,arguments)}):e;if(d==="unload"&&b!=="one")this.one(d,f,e);else{j=0;for(var o=this.length;j<o;j++)c.event.add(this[j],d,i,f)}return this}});c.fn.extend({unbind:function(a,b){if(typeof a==="object"&&
+!a.preventDefault)for(var d in a)this.unbind(d,a[d]);else{d=0;for(var f=this.length;d<f;d++)c.event.remove(this[d],a,b)}return this},delegate:function(a,b,d,f){return this.live(b,d,f,a)},undelegate:function(a,b,d){return arguments.length===0?this.unbind("live"):this.die(b,null,d,a)},trigger:function(a,b){return this.each(function(){c.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0]){a=c.Event(a);a.preventDefault();a.stopPropagation();c.event.trigger(a,b,this[0]);return a.result}},
+toggle:function(a){for(var b=arguments,d=1;d<b.length;)c.proxy(a,b[d++]);return this.click(c.proxy(a,function(f){var e=(c.data(this,"lastToggle"+a.guid)||0)%d;c.data(this,"lastToggle"+a.guid,e+1);f.preventDefault();return b[e].apply(this,arguments)||false}))},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}});var Ga={focus:"focusin",blur:"focusout",mouseenter:"mouseover",mouseleave:"mouseout"};c.each(["live","die"],function(a,b){c.fn[b]=function(d,f,e,j){var i,o=0,k,n,r=j||this.selector,
+u=j?this:c(this.context);if(c.isFunction(f)){e=f;f=w}for(d=(d||"").split(" ");(i=d[o++])!=null;){j=O.exec(i);k="";if(j){k=j[0];i=i.replace(O,"")}if(i==="hover")d.push("mouseenter"+k,"mouseleave"+k);else{n=i;if(i==="focus"||i==="blur"){d.push(Ga[i]+k);i+=k}else i=(Ga[i]||i)+k;b==="live"?u.each(function(){c.event.add(this,pa(i,r),{data:f,selector:r,handler:e,origType:i,origHandler:e,preType:n})}):u.unbind(pa(i,r),e)}}return this}});c.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error".split(" "),
+function(a,b){c.fn[b]=function(d){return d?this.bind(b,d):this.trigger(b)};if(c.attrFn)c.attrFn[b]=true});A.attachEvent&&!A.addEventListener&&A.attachEvent("onunload",function(){for(var a in c.cache)if(c.cache[a].handle)try{c.event.remove(c.cache[a].handle.elem)}catch(b){}});(function(){function a(g){for(var h="",l,m=0;g[m];m++){l=g[m];if(l.nodeType===3||l.nodeType===4)h+=l.nodeValue;else if(l.nodeType!==8)h+=a(l.childNodes)}return h}function b(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q];
+if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1&&!p){t.sizcache=l;t.sizset=q}if(t.nodeName.toLowerCase()===h){y=t;break}t=t[g]}m[q]=y}}}function d(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q];if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1){if(!p){t.sizcache=l;t.sizset=q}if(typeof h!=="string"){if(t===h){y=true;break}}else if(k.filter(h,[t]).length>0){y=t;break}}t=t[g]}m[q]=y}}}var f=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
+e=0,j=Object.prototype.toString,i=false,o=true;[0,0].sort(function(){o=false;return 0});var k=function(g,h,l,m){l=l||[];var q=h=h||s;if(h.nodeType!==1&&h.nodeType!==9)return[];if(!g||typeof g!=="string")return l;for(var p=[],v,t,y,S,H=true,M=x(h),I=g;(f.exec(""),v=f.exec(I))!==null;){I=v[3];p.push(v[1]);if(v[2]){S=v[3];break}}if(p.length>1&&r.exec(g))if(p.length===2&&n.relative[p[0]])t=ga(p[0]+p[1],h);else for(t=n.relative[p[0]]?[h]:k(p.shift(),h);p.length;){g=p.shift();if(n.relative[g])g+=p.shift();
+t=ga(g,t)}else{if(!m&&p.length>1&&h.nodeType===9&&!M&&n.match.ID.test(p[0])&&!n.match.ID.test(p[p.length-1])){v=k.find(p.shift(),h,M);h=v.expr?k.filter(v.expr,v.set)[0]:v.set[0]}if(h){v=m?{expr:p.pop(),set:z(m)}:k.find(p.pop(),p.length===1&&(p[0]==="~"||p[0]==="+")&&h.parentNode?h.parentNode:h,M);t=v.expr?k.filter(v.expr,v.set):v.set;if(p.length>0)y=z(t);else H=false;for(;p.length;){var D=p.pop();v=D;if(n.relative[D])v=p.pop();else D="";if(v==null)v=h;n.relative[D](y,v,M)}}else y=[]}y||(y=t);y||k.error(D||
+g);if(j.call(y)==="[object Array]")if(H)if(h&&h.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&E(h,y[g])))l.push(t[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&l.push(t[g]);else l.push.apply(l,y);else z(y,l);if(S){k(S,q,l,m);k.uniqueSort(l)}return l};k.uniqueSort=function(g){if(B){i=o;g.sort(B);if(i)for(var h=1;h<g.length;h++)g[h]===g[h-1]&&g.splice(h--,1)}return g};k.matches=function(g,h){return k(g,null,null,h)};k.find=function(g,h,l){var m,q;if(!g)return[];
+for(var p=0,v=n.order.length;p<v;p++){var t=n.order[p];if(q=n.leftMatch[t].exec(g)){var y=q[1];q.splice(1,1);if(y.substr(y.length-1)!=="\\"){q[1]=(q[1]||"").replace(/\\/g,"");m=n.find[t](q,h,l);if(m!=null){g=g.replace(n.match[t],"");break}}}}m||(m=h.getElementsByTagName("*"));return{set:m,expr:g}};k.filter=function(g,h,l,m){for(var q=g,p=[],v=h,t,y,S=h&&h[0]&&x(h[0]);g&&h.length;){for(var H in n.filter)if((t=n.leftMatch[H].exec(g))!=null&&t[2]){var M=n.filter[H],I,D;D=t[1];y=false;t.splice(1,1);if(D.substr(D.length-
+1)!=="\\"){if(v===p)p=[];if(n.preFilter[H])if(t=n.preFilter[H](t,v,l,p,m,S)){if(t===true)continue}else y=I=true;if(t)for(var U=0;(D=v[U])!=null;U++)if(D){I=M(D,t,U,v);var Ha=m^!!I;if(l&&I!=null)if(Ha)y=true;else v[U]=false;else if(Ha){p.push(D);y=true}}if(I!==w){l||(v=p);g=g.replace(n.match[H],"");if(!y)return[];break}}}if(g===q)if(y==null)k.error(g);else break;q=g}return v};k.error=function(g){throw"Syntax error, unrecognized expression: "+g;};var n=k.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
+CLASS:/\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(g){return g.getAttribute("href")}},
+relative:{"+":function(g,h){var l=typeof h==="string",m=l&&!/\W/.test(h);l=l&&!m;if(m)h=h.toLowerCase();m=0;for(var q=g.length,p;m<q;m++)if(p=g[m]){for(;(p=p.previousSibling)&&p.nodeType!==1;);g[m]=l||p&&p.nodeName.toLowerCase()===h?p||false:p===h}l&&k.filter(h,g,true)},">":function(g,h){var l=typeof h==="string";if(l&&!/\W/.test(h)){h=h.toLowerCase();for(var m=0,q=g.length;m<q;m++){var p=g[m];if(p){l=p.parentNode;g[m]=l.nodeName.toLowerCase()===h?l:false}}}else{m=0;for(q=g.length;m<q;m++)if(p=g[m])g[m]=
+l?p.parentNode:p.parentNode===h;l&&k.filter(h,g,true)}},"":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("parentNode",h,m,g,p,l)},"~":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("previousSibling",h,m,g,p,l)}},find:{ID:function(g,h,l){if(typeof h.getElementById!=="undefined"&&!l)return(g=h.getElementById(g[1]))?[g]:[]},NAME:function(g,h){if(typeof h.getElementsByName!=="undefined"){var l=[];
+h=h.getElementsByName(g[1]);for(var m=0,q=h.length;m<q;m++)h[m].getAttribute("name")===g[1]&&l.push(h[m]);return l.length===0?null:l}},TAG:function(g,h){return h.getElementsByTagName(g[1])}},preFilter:{CLASS:function(g,h,l,m,q,p){g=" "+g[1].replace(/\\/g,"")+" ";if(p)return g;p=0;for(var v;(v=h[p])!=null;p++)if(v)if(q^(v.className&&(" "+v.className+" ").replace(/[\t\n]/g," ").indexOf(g)>=0))l||m.push(v);else if(l)h[p]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()},
+CHILD:function(g){if(g[1]==="nth"){var h=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=h[1]+(h[2]||1)-0;g[3]=h[3]-0}g[0]=e++;return g},ATTR:function(g,h,l,m,q,p){h=g[1].replace(/\\/g,"");if(!p&&n.attrMap[h])g[1]=n.attrMap[h];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,h,l,m,q){if(g[1]==="not")if((f.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,h);else{g=k.filter(g[3],h,l,true^q);l||m.push.apply(m,
+g);return false}else if(n.match.POS.test(g[0])||n.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,h,l){return!!k(l[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)},
+text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}},
+setFilters:{first:function(g,h){return h===0},last:function(g,h,l,m){return h===m.length-1},even:function(g,h){return h%2===0},odd:function(g,h){return h%2===1},lt:function(g,h,l){return h<l[3]-0},gt:function(g,h,l){return h>l[3]-0},nth:function(g,h,l){return l[3]-0===h},eq:function(g,h,l){return l[3]-0===h}},filter:{PSEUDO:function(g,h,l,m){var q=h[1],p=n.filters[q];if(p)return p(g,l,h,m);else if(q==="contains")return(g.textContent||g.innerText||a([g])||"").indexOf(h[3])>=0;else if(q==="not"){h=
+h[3];l=0;for(m=h.length;l<m;l++)if(h[l]===g)return false;return true}else k.error("Syntax error, unrecognized expression: "+q)},CHILD:function(g,h){var l=h[1],m=g;switch(l){case "only":case "first":for(;m=m.previousSibling;)if(m.nodeType===1)return false;if(l==="first")return true;m=g;case "last":for(;m=m.nextSibling;)if(m.nodeType===1)return false;return true;case "nth":l=h[2];var q=h[3];if(l===1&&q===0)return true;h=h[0];var p=g.parentNode;if(p&&(p.sizcache!==h||!g.nodeIndex)){var v=0;for(m=p.firstChild;m;m=
+m.nextSibling)if(m.nodeType===1)m.nodeIndex=++v;p.sizcache=h}g=g.nodeIndex-q;return l===0?g===0:g%l===0&&g/l>=0}},ID:function(g,h){return g.nodeType===1&&g.getAttribute("id")===h},TAG:function(g,h){return h==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===h},CLASS:function(g,h){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(h)>-1},ATTR:function(g,h){var l=h[1];g=n.attrHandle[l]?n.attrHandle[l](g):g[l]!=null?g[l]:g.getAttribute(l);l=g+"";var m=h[2];h=h[4];return g==null?m==="!=":m===
+"="?l===h:m==="*="?l.indexOf(h)>=0:m==="~="?(" "+l+" ").indexOf(h)>=0:!h?l&&g!==false:m==="!="?l!==h:m==="^="?l.indexOf(h)===0:m==="$="?l.substr(l.length-h.length)===h:m==="|="?l===h||l.substr(0,h.length+1)===h+"-":false},POS:function(g,h,l,m){var q=n.setFilters[h[2]];if(q)return q(g,l,h,m)}}},r=n.match.POS;for(var u in n.match){n.match[u]=new RegExp(n.match[u].source+/(?![^\[]*\])(?![^\(]*\))/.source);n.leftMatch[u]=new RegExp(/(^(?:.|\r|\n)*?)/.source+n.match[u].source.replace(/\\(\d+)/g,function(g,
+h){return"\\"+(h-0+1)}))}var z=function(g,h){g=Array.prototype.slice.call(g,0);if(h){h.push.apply(h,g);return h}return g};try{Array.prototype.slice.call(s.documentElement.childNodes,0)}catch(C){z=function(g,h){h=h||[];if(j.call(g)==="[object Array]")Array.prototype.push.apply(h,g);else if(typeof g.length==="number")for(var l=0,m=g.length;l<m;l++)h.push(g[l]);else for(l=0;g[l];l++)h.push(g[l]);return h}}var B;if(s.documentElement.compareDocumentPosition)B=function(g,h){if(!g.compareDocumentPosition||
+!h.compareDocumentPosition){if(g==h)i=true;return g.compareDocumentPosition?-1:1}g=g.compareDocumentPosition(h)&4?-1:g===h?0:1;if(g===0)i=true;return g};else if("sourceIndex"in s.documentElement)B=function(g,h){if(!g.sourceIndex||!h.sourceIndex){if(g==h)i=true;return g.sourceIndex?-1:1}g=g.sourceIndex-h.sourceIndex;if(g===0)i=true;return g};else if(s.createRange)B=function(g,h){if(!g.ownerDocument||!h.ownerDocument){if(g==h)i=true;return g.ownerDocument?-1:1}var l=g.ownerDocument.createRange(),m=
+h.ownerDocument.createRange();l.setStart(g,0);l.setEnd(g,0);m.setStart(h,0);m.setEnd(h,0);g=l.compareBoundaryPoints(Range.START_TO_END,m);if(g===0)i=true;return g};(function(){var g=s.createElement("div"),h="script"+(new Date).getTime();g.innerHTML="<a name='"+h+"'/>";var l=s.documentElement;l.insertBefore(g,l.firstChild);if(s.getElementById(h)){n.find.ID=function(m,q,p){if(typeof q.getElementById!=="undefined"&&!p)return(q=q.getElementById(m[1]))?q.id===m[1]||typeof q.getAttributeNode!=="undefined"&&
+q.getAttributeNode("id").nodeValue===m[1]?[q]:w:[]};n.filter.ID=function(m,q){var p=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&p&&p.nodeValue===q}}l.removeChild(g);l=g=null})();(function(){var g=s.createElement("div");g.appendChild(s.createComment(""));if(g.getElementsByTagName("*").length>0)n.find.TAG=function(h,l){l=l.getElementsByTagName(h[1]);if(h[1]==="*"){h=[];for(var m=0;l[m];m++)l[m].nodeType===1&&h.push(l[m]);l=h}return l};g.innerHTML="<a href='#'></a>";
+if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")n.attrHandle.href=function(h){return h.getAttribute("href",2)};g=null})();s.querySelectorAll&&function(){var g=k,h=s.createElement("div");h.innerHTML="<p class='TEST'></p>";if(!(h.querySelectorAll&&h.querySelectorAll(".TEST").length===0)){k=function(m,q,p,v){q=q||s;if(!v&&q.nodeType===9&&!x(q))try{return z(q.querySelectorAll(m),p)}catch(t){}return g(m,q,p,v)};for(var l in g)k[l]=g[l];h=null}}();
+(function(){var g=s.createElement("div");g.innerHTML="<div class='test e'></div><div class='test'></div>";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){n.order.splice(1,0,"CLASS");n.find.CLASS=function(h,l,m){if(typeof l.getElementsByClassName!=="undefined"&&!m)return l.getElementsByClassName(h[1])};g=null}}})();var E=s.compareDocumentPosition?function(g,h){return!!(g.compareDocumentPosition(h)&16)}:
+function(g,h){return g!==h&&(g.contains?g.contains(h):true)},x=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false},ga=function(g,h){var l=[],m="",q;for(h=h.nodeType?[h]:h;q=n.match.PSEUDO.exec(g);){m+=q[0];g=g.replace(n.match.PSEUDO,"")}g=n.relative[g]?g+"*":g;q=0;for(var p=h.length;q<p;q++)k(g,h[q],l);return k.filter(m,l)};c.find=k;c.expr=k.selectors;c.expr[":"]=c.expr.filters;c.unique=k.uniqueSort;c.text=a;c.isXMLDoc=x;c.contains=E})();var eb=/Until$/,fb=/^(?:parents|prevUntil|prevAll)/,
+gb=/,/;R=Array.prototype.slice;var Ia=function(a,b,d){if(c.isFunction(b))return c.grep(a,function(e,j){return!!b.call(e,j,e)===d});else if(b.nodeType)return c.grep(a,function(e){return e===b===d});else if(typeof b==="string"){var f=c.grep(a,function(e){return e.nodeType===1});if(Ua.test(b))return c.filter(b,f,!d);else b=c.filter(b,f)}return c.grep(a,function(e){return c.inArray(e,b)>=0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,f=0,e=this.length;f<e;f++){d=b.length;
+c.find(a,this[f],b);if(f>0)for(var j=d;j<b.length;j++)for(var i=0;i<d;i++)if(b[i]===b[j]){b.splice(j--,1);break}}return b},has:function(a){var b=c(a);return this.filter(function(){for(var d=0,f=b.length;d<f;d++)if(c.contains(this,b[d]))return true})},not:function(a){return this.pushStack(Ia(this,a,false),"not",a)},filter:function(a){return this.pushStack(Ia(this,a,true),"filter",a)},is:function(a){return!!a&&c.filter(a,this).length>0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,j=
+{},i;if(f&&a.length){e=0;for(var o=a.length;e<o;e++){i=a[e];j[i]||(j[i]=c.expr.match.POS.test(i)?c(i,b||this.context):i)}for(;f&&f.ownerDocument&&f!==b;){for(i in j){e=j[i];if(e.jquery?e.index(f)>-1:c(f).is(e)){d.push({selector:i,elem:f});delete j[i]}}f=f.parentNode}}return d}var k=c.expr.match.POS.test(a)?c(a,b||this.context):null;return this.map(function(n,r){for(;r&&r.ownerDocument&&r!==b;){if(k?k.index(r)>-1:c(r).is(a))return r;r=r.parentNode}return null})},index:function(a){if(!a||typeof a===
+"string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a==="string"?c(a,b||this.context):c.makeArray(a);b=c.merge(this.get(),a);return this.pushStack(qa(a[0])||qa(b[0])?b:c.unique(b))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode",
+d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")?
+a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,f){var e=c.map(this,b,d);eb.test(a)||(f=d);if(f&&typeof f==="string")e=c.filter(f,e);e=this.length>1?c.unique(e):e;if((this.length>1||gb.test(f))&&fb.test(a))e=e.reverse();return this.pushStack(e,a,R.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return c.find.matches(a,b)},dir:function(a,b,d){var f=[];for(a=a[b];a&&a.nodeType!==9&&(d===w||a.nodeType!==1||!c(a).is(d));){a.nodeType===
+1&&f.push(a);a=a[b]}return f},nth:function(a,b,d){b=b||1;for(var f=0;a;a=a[d])if(a.nodeType===1&&++f===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var Ja=/ jQuery\d+="(?:\d+|null)"/g,V=/^\s+/,Ka=/(<([\w:]+)[^>]*?)\/>/g,hb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,La=/<([\w:]+)/,ib=/<tbody/i,jb=/<|&#?\w+;/,ta=/<script|<object|<embed|<option|<style/i,ua=/checked\s*(?:[^=]|=\s*.checked.)/i,Ma=function(a,b,d){return hb.test(d)?
+a:b+"></"+d+">"},F={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]};F.optgroup=F.option;F.tbody=F.tfoot=F.colgroup=F.caption=F.thead;F.th=F.td;if(!c.support.htmlSerialize)F._default=[1,"div<div>","</div>"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d=
+c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==w)return this.empty().append((this[0]&&this[0].ownerDocument||s).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this},
+wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})},
+prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,
+this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,f;(f=this[d])!=null;d++)if(!a||c.filter(a,[f]).length){if(!b&&f.nodeType===1){c.cleanData(f.getElementsByTagName("*"));c.cleanData([f])}f.parentNode&&f.parentNode.removeChild(f)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild);
+return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,f=this.ownerDocument;if(!d){d=f.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(Ja,"").replace(/=([^="'>\s]+\/)>/g,'="$1">').replace(V,"")],f)[0]}else return this.cloneNode(true)});if(a===true){ra(this,b);ra(this.find("*"),b.find("*"))}return b},html:function(a){if(a===w)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Ja,
+""):null;else if(typeof a==="string"&&!ta.test(a)&&(c.support.leadingWhitespace||!V.test(a))&&!F[(La.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Ka,Ma);try{for(var b=0,d=this.length;b<d;b++)if(this[b].nodeType===1){c.cleanData(this[b].getElementsByTagName("*"));this[b].innerHTML=a}}catch(f){this.empty().append(a)}}else c.isFunction(a)?this.each(function(e){var j=c(this),i=j.html();j.empty().append(function(){return a.call(this,e,i)})}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&&
+this[0].parentNode){if(c.isFunction(a))return this.each(function(b){var d=c(this),f=d.html();d.replaceWith(a.call(this,b,f))});if(typeof a!=="string")a=c(a).detach();return this.each(function(){var b=this.nextSibling,d=this.parentNode;c(this).remove();b?c(b).before(a):c(d).append(a)})}else return this.pushStack(c(c.isFunction(a)?a():a),"replaceWith",a)},detach:function(a){return this.remove(a,true)},domManip:function(a,b,d){function f(u){return c.nodeName(u,"table")?u.getElementsByTagName("tbody")[0]||
+u.appendChild(u.ownerDocument.createElement("tbody")):u}var e,j,i=a[0],o=[],k;if(!c.support.checkClone&&arguments.length===3&&typeof i==="string"&&ua.test(i))return this.each(function(){c(this).domManip(a,b,d,true)});if(c.isFunction(i))return this.each(function(u){var z=c(this);a[0]=i.call(this,u,b?z.html():w);z.domManip(a,b,d)});if(this[0]){e=i&&i.parentNode;e=c.support.parentNode&&e&&e.nodeType===11&&e.childNodes.length===this.length?{fragment:e}:sa(a,this,o);k=e.fragment;if(j=k.childNodes.length===
+1?(k=k.firstChild):k.firstChild){b=b&&c.nodeName(j,"tr");for(var n=0,r=this.length;n<r;n++)d.call(b?f(this[n],j):this[n],n>0||e.cacheable||this.length>1?k.cloneNode(true):k)}o.length&&c.each(o,Qa)}return this}});c.fragments={};c.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var f=[];d=c(d);var e=this.length===1&&this[0].parentNode;if(e&&e.nodeType===11&&e.childNodes.length===1&&d.length===1){d[b](this[0]);
+return this}else{e=0;for(var j=d.length;e<j;e++){var i=(e>0?this.clone(true):this).get();c.fn[b].apply(c(d[e]),i);f=f.concat(i)}return this.pushStack(f,a,d.selector)}}});c.extend({clean:function(a,b,d,f){b=b||s;if(typeof b.createElement==="undefined")b=b.ownerDocument||b[0]&&b[0].ownerDocument||s;for(var e=[],j=0,i;(i=a[j])!=null;j++){if(typeof i==="number")i+="";if(i){if(typeof i==="string"&&!jb.test(i))i=b.createTextNode(i);else if(typeof i==="string"){i=i.replace(Ka,Ma);var o=(La.exec(i)||["",
+""])[1].toLowerCase(),k=F[o]||F._default,n=k[0],r=b.createElement("div");for(r.innerHTML=k[1]+i+k[2];n--;)r=r.lastChild;if(!c.support.tbody){n=ib.test(i);o=o==="table"&&!n?r.firstChild&&r.firstChild.childNodes:k[1]==="<table>"&&!n?r.childNodes:[];for(k=o.length-1;k>=0;--k)c.nodeName(o[k],"tbody")&&!o[k].childNodes.length&&o[k].parentNode.removeChild(o[k])}!c.support.leadingWhitespace&&V.test(i)&&r.insertBefore(b.createTextNode(V.exec(i)[0]),r.firstChild);i=r.childNodes}if(i.nodeType)e.push(i);else e=
+c.merge(e,i)}}if(d)for(j=0;e[j];j++)if(f&&c.nodeName(e[j],"script")&&(!e[j].type||e[j].type.toLowerCase()==="text/javascript"))f.push(e[j].parentNode?e[j].parentNode.removeChild(e[j]):e[j]);else{e[j].nodeType===1&&e.splice.apply(e,[j+1,0].concat(c.makeArray(e[j].getElementsByTagName("script"))));d.appendChild(e[j])}return e},cleanData:function(a){for(var b,d,f=c.cache,e=c.event.special,j=c.support.deleteExpando,i=0,o;(o=a[i])!=null;i++)if(d=o[c.expando]){b=f[d];if(b.events)for(var k in b.events)e[k]?
+c.event.remove(o,k):Ca(o,k,b.handle);if(j)delete o[c.expando];else o.removeAttribute&&o.removeAttribute(c.expando);delete f[d]}}});var kb=/z-?index|font-?weight|opacity|zoom|line-?height/i,Na=/alpha\([^)]*\)/,Oa=/opacity=([^)]*)/,ha=/float/i,ia=/-([a-z])/ig,lb=/([A-Z])/g,mb=/^-?\d+(?:px)?$/i,nb=/^-?\d/,ob={position:"absolute",visibility:"hidden",display:"block"},pb=["Left","Right"],qb=["Top","Bottom"],rb=s.defaultView&&s.defaultView.getComputedStyle,Pa=c.support.cssFloat?"cssFloat":"styleFloat",ja=
+function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){return X(this,a,b,true,function(d,f,e){if(e===w)return c.curCSS(d,f);if(typeof e==="number"&&!kb.test(f))e+="px";c.style(d,f,e)})};c.extend({style:function(a,b,d){if(!a||a.nodeType===3||a.nodeType===8)return w;if((b==="width"||b==="height")&&parseFloat(d)<0)d=w;var f=a.style||a,e=d!==w;if(!c.support.opacity&&b==="opacity"){if(e){f.zoom=1;b=parseInt(d,10)+""==="NaN"?"":"alpha(opacity="+d*100+")";a=f.filter||c.curCSS(a,"filter")||"";f.filter=
+Na.test(a)?a.replace(Na,b):b}return f.filter&&f.filter.indexOf("opacity=")>=0?parseFloat(Oa.exec(f.filter)[1])/100+"":""}if(ha.test(b))b=Pa;b=b.replace(ia,ja);if(e)f[b]=d;return f[b]},css:function(a,b,d,f){if(b==="width"||b==="height"){var e,j=b==="width"?pb:qb;function i(){e=b==="width"?a.offsetWidth:a.offsetHeight;f!=="border"&&c.each(j,function(){f||(e-=parseFloat(c.curCSS(a,"padding"+this,true))||0);if(f==="margin")e+=parseFloat(c.curCSS(a,"margin"+this,true))||0;else e-=parseFloat(c.curCSS(a,
+"border"+this+"Width",true))||0})}a.offsetWidth!==0?i():c.swap(a,ob,i);return Math.max(0,Math.round(e))}return c.curCSS(a,b,d)},curCSS:function(a,b,d){var f,e=a.style;if(!c.support.opacity&&b==="opacity"&&a.currentStyle){f=Oa.test(a.currentStyle.filter||"")?parseFloat(RegExp.$1)/100+"":"";return f===""?"1":f}if(ha.test(b))b=Pa;if(!d&&e&&e[b])f=e[b];else if(rb){if(ha.test(b))b="float";b=b.replace(lb,"-$1").toLowerCase();e=a.ownerDocument.defaultView;if(!e)return null;if(a=e.getComputedStyle(a,null))f=
+a.getPropertyValue(b);if(b==="opacity"&&f==="")f="1"}else if(a.currentStyle){d=b.replace(ia,ja);f=a.currentStyle[b]||a.currentStyle[d];if(!mb.test(f)&&nb.test(f)){b=e.left;var j=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;e.left=d==="fontSize"?"1em":f||0;f=e.pixelLeft+"px";e.left=b;a.runtimeStyle.left=j}}return f},swap:function(a,b,d){var f={};for(var e in b){f[e]=a.style[e];a.style[e]=b[e]}d.call(a);for(e in b)a.style[e]=f[e]}});if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b=
+a.offsetWidth,d=a.offsetHeight,f=a.nodeName.toLowerCase()==="tr";return b===0&&d===0&&!f?true:b>0&&d>0&&!f?false:c.curCSS(a,"display")==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var sb=J(),tb=/<script(.|\s)*?\/script>/gi,ub=/select|textarea/i,vb=/color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,N=/=\?(&|$)/,ka=/\?/,wb=/(\?|&)_=.*?(&|$)/,xb=/^(\w+:)?\/\/([^\/?#]+)/,yb=/%20/g,zb=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!==
+"string")return zb.call(this,a);else if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var e=a.slice(f,a.length);a=a.slice(0,f)}f="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b==="object"){b=c.param(b,c.ajaxSettings.traditional);f="POST"}var j=this;c.ajax({url:a,type:f,dataType:"html",data:b,complete:function(i,o){if(o==="success"||o==="notmodified")j.html(e?c("<div />").append(i.responseText.replace(tb,"")).find(e):i.responseText);d&&j.each(d,[i.responseText,o,i])}});return this},
+serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ub.test(this.nodeName)||vb.test(this.type))}).map(function(a,b){a=c(this).val();return a==null?null:c.isArray(a)?c.map(a,function(d){return{name:b.name,value:d}}):{name:b.name,value:a}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),
+function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:f})},getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:f})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href,
+global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:A.XMLHttpRequest&&(A.location.protocol!=="file:"||!A.ActiveXObject)?function(){return new A.XMLHttpRequest}:function(){try{return new A.ActiveXObject("Microsoft.XMLHTTP")}catch(a){}},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},etag:{},ajax:function(a){function b(){e.success&&
+e.success.call(k,o,i,x);e.global&&f("ajaxSuccess",[x,e])}function d(){e.complete&&e.complete.call(k,x,i);e.global&&f("ajaxComplete",[x,e]);e.global&&!--c.active&&c.event.trigger("ajaxStop")}function f(q,p){(e.context?c(e.context):c.event).trigger(q,p)}var e=c.extend(true,{},c.ajaxSettings,a),j,i,o,k=a&&a.context||e,n=e.type.toUpperCase();if(e.data&&e.processData&&typeof e.data!=="string")e.data=c.param(e.data,e.traditional);if(e.dataType==="jsonp"){if(n==="GET")N.test(e.url)||(e.url+=(ka.test(e.url)?
+"&":"?")+(e.jsonp||"callback")+"=?");else if(!e.data||!N.test(e.data))e.data=(e.data?e.data+"&":"")+(e.jsonp||"callback")+"=?";e.dataType="json"}if(e.dataType==="json"&&(e.data&&N.test(e.data)||N.test(e.url))){j=e.jsonpCallback||"jsonp"+sb++;if(e.data)e.data=(e.data+"").replace(N,"="+j+"$1");e.url=e.url.replace(N,"="+j+"$1");e.dataType="script";A[j]=A[j]||function(q){o=q;b();d();A[j]=w;try{delete A[j]}catch(p){}z&&z.removeChild(C)}}if(e.dataType==="script"&&e.cache===null)e.cache=false;if(e.cache===
+false&&n==="GET"){var r=J(),u=e.url.replace(wb,"$1_="+r+"$2");e.url=u+(u===e.url?(ka.test(e.url)?"&":"?")+"_="+r:"")}if(e.data&&n==="GET")e.url+=(ka.test(e.url)?"&":"?")+e.data;e.global&&!c.active++&&c.event.trigger("ajaxStart");r=(r=xb.exec(e.url))&&(r[1]&&r[1]!==location.protocol||r[2]!==location.host);if(e.dataType==="script"&&n==="GET"&&r){var z=s.getElementsByTagName("head")[0]||s.documentElement,C=s.createElement("script");C.src=e.url;if(e.scriptCharset)C.charset=e.scriptCharset;if(!j){var B=
+false;C.onload=C.onreadystatechange=function(){if(!B&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){B=true;b();d();C.onload=C.onreadystatechange=null;z&&C.parentNode&&z.removeChild(C)}}}z.insertBefore(C,z.firstChild);return w}var E=false,x=e.xhr();if(x){e.username?x.open(n,e.url,e.async,e.username,e.password):x.open(n,e.url,e.async);try{if(e.data||a&&a.contentType)x.setRequestHeader("Content-Type",e.contentType);if(e.ifModified){c.lastModified[e.url]&&x.setRequestHeader("If-Modified-Since",
+c.lastModified[e.url]);c.etag[e.url]&&x.setRequestHeader("If-None-Match",c.etag[e.url])}r||x.setRequestHeader("X-Requested-With","XMLHttpRequest");x.setRequestHeader("Accept",e.dataType&&e.accepts[e.dataType]?e.accepts[e.dataType]+", */*":e.accepts._default)}catch(ga){}if(e.beforeSend&&e.beforeSend.call(k,x,e)===false){e.global&&!--c.active&&c.event.trigger("ajaxStop");x.abort();return false}e.global&&f("ajaxSend",[x,e]);var g=x.onreadystatechange=function(q){if(!x||x.readyState===0||q==="abort"){E||
+d();E=true;if(x)x.onreadystatechange=c.noop}else if(!E&&x&&(x.readyState===4||q==="timeout")){E=true;x.onreadystatechange=c.noop;i=q==="timeout"?"timeout":!c.httpSuccess(x)?"error":e.ifModified&&c.httpNotModified(x,e.url)?"notmodified":"success";var p;if(i==="success")try{o=c.httpData(x,e.dataType,e)}catch(v){i="parsererror";p=v}if(i==="success"||i==="notmodified")j||b();else c.handleError(e,x,i,p);d();q==="timeout"&&x.abort();if(e.async)x=null}};try{var h=x.abort;x.abort=function(){x&&h.call(x);
+g("abort")}}catch(l){}e.async&&e.timeout>0&&setTimeout(function(){x&&!E&&g("timeout")},e.timeout);try{x.send(n==="POST"||n==="PUT"||n==="DELETE"?e.data:null)}catch(m){c.handleError(e,x,null,m);d()}e.async||g();return x}},handleError:function(a,b,d,f){if(a.error)a.error.call(a.context||a,b,d,f);if(a.global)(a.context?c(a.context):c.event).trigger("ajaxError",[b,a,f])},active:0,httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status===
+1223||a.status===0}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),f=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(f)c.etag[b]=f;return a.status===304||a.status===0},httpData:function(a,b,d){var f=a.getResponseHeader("content-type")||"",e=b==="xml"||!b&&f.indexOf("xml")>=0;a=e?a.responseXML:a.responseText;e&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b===
+"json"||!b&&f.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&f.indexOf("javascript")>=0)c.globalEval(a);return a},param:function(a,b){function d(i,o){if(c.isArray(o))c.each(o,function(k,n){b||/\[\]$/.test(i)?f(i,n):d(i+"["+(typeof n==="object"||c.isArray(n)?k:"")+"]",n)});else!b&&o!=null&&typeof o==="object"?c.each(o,function(k,n){d(i+"["+k+"]",n)}):f(i,o)}function f(i,o){o=c.isFunction(o)?o():o;e[e.length]=encodeURIComponent(i)+"="+encodeURIComponent(o)}var e=[];if(b===w)b=c.ajaxSettings.traditional;
+if(c.isArray(a)||a.jquery)c.each(a,function(){f(this.name,this.value)});else for(var j in a)d(j,a[j]);return e.join("&").replace(yb,"+")}});var la={},Ab=/toggle|show|hide/,Bb=/^([+-]=)?([\d+-.]+)(.*)$/,W,va=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b){if(a||a===0)return this.animate(K("show",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");
+this[a].style.display=d||"";if(c.css(this[a],"display")==="none"){d=this[a].nodeName;var f;if(la[d])f=la[d];else{var e=c("<"+d+" />").appendTo("body");f=e.css("display");if(f==="none")f="block";e.remove();la[d]=f}c.data(this[a],"olddisplay",f)}}a=0;for(b=this.length;a<b;a++)this[a].style.display=c.data(this[a],"olddisplay")||"";return this}},hide:function(a,b){if(a||a===0)return this.animate(K("hide",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");!d&&d!=="none"&&c.data(this[a],
+"olddisplay",c.css(this[a],"display"))}a=0;for(b=this.length;a<b;a++)this[a].style.display="none";return this}},_toggle:c.fn.toggle,toggle:function(a,b){var d=typeof a==="boolean";if(c.isFunction(a)&&c.isFunction(b))this._toggle.apply(this,arguments);else a==null||d?this.each(function(){var f=d?a:c(this).is(":hidden");c(this)[f?"show":"hide"]()}):this.animate(K("toggle",3),a,b);return this},fadeTo:function(a,b,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,d)},
+animate:function(a,b,d,f){var e=c.speed(b,d,f);if(c.isEmptyObject(a))return this.each(e.complete);return this[e.queue===false?"each":"queue"](function(){var j=c.extend({},e),i,o=this.nodeType===1&&c(this).is(":hidden"),k=this;for(i in a){var n=i.replace(ia,ja);if(i!==n){a[n]=a[i];delete a[i];i=n}if(a[i]==="hide"&&o||a[i]==="show"&&!o)return j.complete.call(this);if((i==="height"||i==="width")&&this.style){j.display=c.css(this,"display");j.overflow=this.style.overflow}if(c.isArray(a[i])){(j.specialEasing=
+j.specialEasing||{})[i]=a[i][1];a[i]=a[i][0]}}if(j.overflow!=null)this.style.overflow="hidden";j.curAnim=c.extend({},a);c.each(a,function(r,u){var z=new c.fx(k,j,r);if(Ab.test(u))z[u==="toggle"?o?"show":"hide":u](a);else{var C=Bb.exec(u),B=z.cur(true)||0;if(C){u=parseFloat(C[2]);var E=C[3]||"px";if(E!=="px"){k.style[r]=(u||1)+E;B=(u||1)/z.cur(true)*B;k.style[r]=B+E}if(C[1])u=(C[1]==="-="?-1:1)*u+B;z.custom(B,u,E)}else z.custom(B,u,"")}});return true})},stop:function(a,b){var d=c.timers;a&&this.queue([]);
+this.each(function(){for(var f=d.length-1;f>=0;f--)if(d[f].elem===this){b&&d[f](true);d.splice(f,1)}});b||this.dequeue();return this}});c.each({slideDown:K("show",1),slideUp:K("hide",1),slideToggle:K("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(a,b){c.fn[a]=function(d,f){return this.animate(b,d,f)}});c.extend({speed:function(a,b,d){var f=a&&typeof a==="object"?a:{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};f.duration=c.fx.off?0:typeof f.duration===
+"number"?f.duration:c.fx.speeds[f.duration]||c.fx.speeds._default;f.old=f.complete;f.complete=function(){f.queue!==false&&c(this).dequeue();c.isFunction(f.old)&&f.old.call(this)};return f},easing:{linear:function(a,b,d,f){return d+f*a},swing:function(a,b,d,f){return(-Math.cos(a*Math.PI)/2+0.5)*f+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]||
+c.fx.step._default)(this);if((this.prop==="height"||this.prop==="width")&&this.elem.style)this.elem.style.display="block"},cur:function(a){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];return(a=parseFloat(c.css(this.elem,this.prop,a)))&&a>-10000?a:parseFloat(c.curCSS(this.elem,this.prop))||0},custom:function(a,b,d){function f(j){return e.step(j)}this.startTime=J();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start;
+this.pos=this.state=0;var e=this;f.elem=this.elem;if(f()&&c.timers.push(f)&&!W)W=setInterval(c.fx.tick,13)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(a){var b=J(),d=true;if(a||b>=this.options.duration+this.startTime){this.now=
+this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var f in this.options.curAnim)if(this.options.curAnim[f]!==true)d=false;if(d){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;a=c.data(this.elem,"olddisplay");this.elem.style.display=a?a:this.options.display;if(c.css(this.elem,"display")==="none")this.elem.style.display="block"}this.options.hide&&c(this.elem).hide();if(this.options.hide||this.options.show)for(var e in this.options.curAnim)c.style(this.elem,
+e,this.options.orig[e]);this.options.complete.call(this.elem)}return false}else{e=b-this.startTime;this.state=e/this.options.duration;a=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||a](this.state,e,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=c.timers,b=0;b<a.length;b++)a[b]()||a.splice(b--,1);a.length||
+c.fx.stop()},stop:function(){clearInterval(W);W=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){c.style(a.elem,"opacity",a.now)},_default:function(a){if(a.elem.style&&a.elem.style[a.prop]!=null)a.elem.style[a.prop]=(a.prop==="width"||a.prop==="height"?Math.max(0,a.now):a.now)+a.unit;else a.elem[a.prop]=a.now}}});if(c.expr&&c.expr.filters)c.expr.filters.animated=function(a){return c.grep(c.timers,function(b){return a===b.elem}).length};c.fn.offset="getBoundingClientRect"in s.documentElement?
+function(a){var b=this[0];if(a)return this.each(function(e){c.offset.setOffset(this,a,e)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);var d=b.getBoundingClientRect(),f=b.ownerDocument;b=f.body;f=f.documentElement;return{top:d.top+(self.pageYOffset||c.support.boxModel&&f.scrollTop||b.scrollTop)-(f.clientTop||b.clientTop||0),left:d.left+(self.pageXOffset||c.support.boxModel&&f.scrollLeft||b.scrollLeft)-(f.clientLeft||b.clientLeft||0)}}:function(a){var b=
+this[0];if(a)return this.each(function(r){c.offset.setOffset(this,a,r)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);c.offset.initialize();var d=b.offsetParent,f=b,e=b.ownerDocument,j,i=e.documentElement,o=e.body;f=(e=e.defaultView)?e.getComputedStyle(b,null):b.currentStyle;for(var k=b.offsetTop,n=b.offsetLeft;(b=b.parentNode)&&b!==o&&b!==i;){if(c.offset.supportsFixedPosition&&f.position==="fixed")break;j=e?e.getComputedStyle(b,null):b.currentStyle;
+k-=b.scrollTop;n-=b.scrollLeft;if(b===d){k+=b.offsetTop;n+=b.offsetLeft;if(c.offset.doesNotAddBorder&&!(c.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(b.nodeName))){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=d;d=b.offsetParent}if(c.offset.subtractsBorderForOverflowNotVisible&&j.overflow!=="visible"){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=j}if(f.position==="relative"||f.position==="static"){k+=o.offsetTop;n+=o.offsetLeft}if(c.offset.supportsFixedPosition&&
+f.position==="fixed"){k+=Math.max(i.scrollTop,o.scrollTop);n+=Math.max(i.scrollLeft,o.scrollLeft)}return{top:k,left:n}};c.offset={initialize:function(){var a=s.body,b=s.createElement("div"),d,f,e,j=parseFloat(c.curCSS(a,"marginTop",true))||0;c.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"});b.innerHTML="<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";
+a.insertBefore(b,a.firstChild);d=b.firstChild;f=d.firstChild;e=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=f.offsetTop!==5;this.doesAddBorderForTableAndCells=e.offsetTop===5;f.style.position="fixed";f.style.top="20px";this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15;f.style.position=f.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==j;a.removeChild(b);
+c.offset.initialize=c.noop},bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.curCSS(a,"marginTop",true))||0;d+=parseFloat(c.curCSS(a,"marginLeft",true))||0}return{top:b,left:d}},setOffset:function(a,b,d){if(/static/.test(c.curCSS(a,"position")))a.style.position="relative";var f=c(a),e=f.offset(),j=parseInt(c.curCSS(a,"top",true),10)||0,i=parseInt(c.curCSS(a,"left",true),10)||0;if(c.isFunction(b))b=b.call(a,
+d,e);d={top:b.top-e.top+j,left:b.left-e.left+i};"using"in b?b.using.call(a,d):f.css(d)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),f=/^body|html$/i.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.curCSS(a,"marginTop",true))||0;d.left-=parseFloat(c.curCSS(a,"marginLeft",true))||0;f.top+=parseFloat(c.curCSS(b[0],"borderTopWidth",true))||0;f.left+=parseFloat(c.curCSS(b[0],"borderLeftWidth",true))||0;return{top:d.top-
+f.top,left:d.left-f.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||s.body;a&&!/^body|html$/i.test(a.nodeName)&&c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(f){var e=this[0],j;if(!e)return null;if(f!==w)return this.each(function(){if(j=wa(this))j.scrollTo(!a?f:c(j).scrollLeft(),a?f:c(j).scrollTop());else this[d]=f});else return(j=wa(e))?"pageXOffset"in j?j[a?"pageYOffset":
+"pageXOffset"]:c.support.boxModel&&j.document.documentElement[d]||j.document.body[d]:e[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();c.fn["inner"+b]=function(){return this[0]?c.css(this[0],d,false,"padding"):null};c.fn["outer"+b]=function(f){return this[0]?c.css(this[0],d,false,f?"margin":"border"):null};c.fn[d]=function(f){var e=this[0];if(!e)return f==null?null:this;if(c.isFunction(f))return this.each(function(j){var i=c(this);i[d](f.call(this,j,i[d]()))});return"scrollTo"in
+e&&e.document?e.document.compatMode==="CSS1Compat"&&e.document.documentElement["client"+b]||e.document.body["client"+b]:e.nodeType===9?Math.max(e.documentElement["client"+b],e.body["scroll"+b],e.documentElement["scroll"+b],e.body["offset"+b],e.documentElement["offset"+b]):f===w?c.css(e,d):this.css(d,typeof f==="string"?f:f+"px")}});A.jQuery=A.$=c})(window);
diff --git a/lib/common_test/priv/jquery.tablesorter.min.js b/lib/common_test/priv/jquery.tablesorter.min.js
new file mode 100644
index 0000000000..b8605df1e7
--- /dev/null
+++ b/lib/common_test/priv/jquery.tablesorter.min.js
@@ -0,0 +1,4 @@
+
+(function($){$.extend({tablesorter:new
+function(){var parsers=[],widgets=[];this.defaults={cssHeader:"header",cssAsc:"headerSortUp",cssDesc:"headerSortDown",cssChildRow:"expand-child",sortInitialOrder:"asc",sortMultiSortKey:"shiftKey",sortForce:null,sortAppend:null,sortLocaleCompare:true,textExtraction:"simple",parsers:{},widgets:[],widgetZebra:{css:["even","odd"]},headers:{},widthFixed:false,cancelSelection:true,sortList:[],headerList:[],dateFormat:"us",decimal:'/\.|\,/g',onRenderHeader:null,selectorHeaders:'thead th',debug:false};function benchmark(s,d){log(s+","+(new Date().getTime()-d.getTime())+"ms");}this.benchmark=benchmark;function log(s){if(typeof console!="undefined"&&typeof console.debug!="undefined"){console.log(s);}else{alert(s);}}function buildParserCache(table,$headers){if(table.config.debug){var parsersDebug="";}if(table.tBodies.length==0)return;var rows=table.tBodies[0].rows;if(rows[0]){var list=[],cells=rows[0].cells,l=cells.length;for(var i=0;i<l;i++){var p=false;if($.metadata&&($($headers[i]).metadata()&&$($headers[i]).metadata().sorter)){p=getParserById($($headers[i]).metadata().sorter);}else if((table.config.headers[i]&&table.config.headers[i].sorter)){p=getParserById(table.config.headers[i].sorter);}if(!p){p=detectParserForColumn(table,rows,-1,i);}if(table.config.debug){parsersDebug+="column:"+i+" parser:"+p.id+"\n";}list.push(p);}}if(table.config.debug){log(parsersDebug);}return list;};function detectParserForColumn(table,rows,rowIndex,cellIndex){var l=parsers.length,node=false,nodeValue=false,keepLooking=true;while(nodeValue==''&&keepLooking){rowIndex++;if(rows[rowIndex]){node=getNodeFromRowAndCellIndex(rows,rowIndex,cellIndex);nodeValue=trimAndGetNodeText(table.config,node);if(table.config.debug){log('Checking if value was empty on row:'+rowIndex);}}else{keepLooking=false;}}for(var i=1;i<l;i++){if(parsers[i].is(nodeValue,table,node)){return parsers[i];}}return parsers[0];}function getNodeFromRowAndCellIndex(rows,rowIndex,cellIndex){return rows[rowIndex].cells[cellIndex];}function trimAndGetNodeText(config,node){return $.trim(getElementText(config,node));}function getParserById(name){var l=parsers.length;for(var i=0;i<l;i++){if(parsers[i].id.toLowerCase()==name.toLowerCase()){return parsers[i];}}return false;}function buildCache(table){if(table.config.debug){var cacheTime=new Date();}var totalRows=(table.tBodies[0]&&table.tBodies[0].rows.length)||0,totalCells=(table.tBodies[0].rows[0]&&table.tBodies[0].rows[0].cells.length)||0,parsers=table.config.parsers,cache={row:[],normalized:[]};for(var i=0;i<totalRows;++i){var c=$(table.tBodies[0].rows[i]),cols=[];if(c.hasClass(table.config.cssChildRow)){cache.row[cache.row.length-1]=cache.row[cache.row.length-1].add(c);continue;}cache.row.push(c);for(var j=0;j<totalCells;++j){cols.push(parsers[j].format(getElementText(table.config,c[0].cells[j]),table,c[0].cells[j]));}cols.push(cache.normalized.length);cache.normalized.push(cols);cols=null;};if(table.config.debug){benchmark("Building cache for "+totalRows+" rows:",cacheTime);}return cache;};function getElementText(config,node){var text="";if(!node)return"";if(!config.supportsTextContent)config.supportsTextContent=node.textContent||false;if(config.textExtraction=="simple"){if(config.supportsTextContent){text=node.textContent;}else{if(node.childNodes[0]&&node.childNodes[0].hasChildNodes()){text=node.childNodes[0].innerHTML;}else{text=node.innerHTML;}}}else{if(typeof(config.textExtraction)=="function"){text=config.textExtraction(node);}else{text=$(node).text();}}return text;}function appendToTable(table,cache){if(table.config.debug){var appendTime=new Date()}var c=cache,r=c.row,n=c.normalized,totalRows=n.length,checkCell=(n[0].length-1),tableBody=$(table.tBodies[0]),rows=[];for(var i=0;i<totalRows;i++){var pos=n[i][checkCell];rows.push(r[pos]);if(!table.config.appender){var l=r[pos].length;for(var j=0;j<l;j++){tableBody[0].appendChild(r[pos][j]);}}}if(table.config.appender){table.config.appender(table,rows);}rows=null;if(table.config.debug){benchmark("Rebuilt table:",appendTime);}applyWidget(table);setTimeout(function(){$(table).trigger("sortEnd");},0);};function buildHeaders(table){if(table.config.debug){var time=new Date();}var meta=($.metadata)?true:false;var header_index=computeTableHeaderCellIndexes(table);$tableHeaders=$(table.config.selectorHeaders,table).each(function(index){this.column=header_index[this.parentNode.rowIndex+"-"+this.cellIndex];this.order=formatSortingOrder(table.config.sortInitialOrder);this.count=this.order;if(checkHeaderMetadata(this)||checkHeaderOptions(table,index))this.sortDisabled=true;if(checkHeaderOptionsSortingLocked(table,index))this.order=this.lockedOrder=checkHeaderOptionsSortingLocked(table,index);if(!this.sortDisabled){var $th=$(this).addClass(table.config.cssHeader);if(table.config.onRenderHeader)table.config.onRenderHeader.apply($th);}table.config.headerList[index]=this;});if(table.config.debug){benchmark("Built headers:",time);log($tableHeaders);}return $tableHeaders;};function computeTableHeaderCellIndexes(t){var matrix=[];var lookup={};var thead=t.getElementsByTagName('THEAD')[0];var trs=thead.getElementsByTagName('TR');for(var i=0;i<trs.length;i++){var cells=trs[i].cells;for(var j=0;j<cells.length;j++){var c=cells[j];var rowIndex=c.parentNode.rowIndex;var cellId=rowIndex+"-"+c.cellIndex;var rowSpan=c.rowSpan||1;var colSpan=c.colSpan||1
+var firstAvailCol;if(typeof(matrix[rowIndex])=="undefined"){matrix[rowIndex]=[];}for(var k=0;k<matrix[rowIndex].length+1;k++){if(typeof(matrix[rowIndex][k])=="undefined"){firstAvailCol=k;break;}}lookup[cellId]=firstAvailCol;for(var k=rowIndex;k<rowIndex+rowSpan;k++){if(typeof(matrix[k])=="undefined"){matrix[k]=[];}var matrixrow=matrix[k];for(var l=firstAvailCol;l<firstAvailCol+colSpan;l++){matrixrow[l]="x";}}}}return lookup;}function checkCellColSpan(table,rows,row){var arr=[],r=table.tHead.rows,c=r[row].cells;for(var i=0;i<c.length;i++){var cell=c[i];if(cell.colSpan>1){arr=arr.concat(checkCellColSpan(table,headerArr,row++));}else{if(table.tHead.length==1||(cell.rowSpan>1||!r[row+1])){arr.push(cell);}}}return arr;};function checkHeaderMetadata(cell){if(($.metadata)&&($(cell).metadata().sorter===false)){return true;};return false;}function checkHeaderOptions(table,i){if((table.config.headers[i])&&(table.config.headers[i].sorter===false)){return true;};return false;}function checkHeaderOptionsSortingLocked(table,i){if((table.config.headers[i])&&(table.config.headers[i].lockedOrder))return table.config.headers[i].lockedOrder;return false;}function applyWidget(table){var c=table.config.widgets;var l=c.length;for(var i=0;i<l;i++){getWidgetById(c[i]).format(table);}}function getWidgetById(name){var l=widgets.length;for(var i=0;i<l;i++){if(widgets[i].id.toLowerCase()==name.toLowerCase()){return widgets[i];}}};function formatSortingOrder(v){if(typeof(v)!="Number"){return(v.toLowerCase()=="desc")?1:0;}else{return(v==1)?1:0;}}function isValueInArray(v,a){var l=a.length;for(var i=0;i<l;i++){if(a[i][0]==v){return true;}}return false;}function setHeadersCss(table,$headers,list,css){$headers.removeClass(css[0]).removeClass(css[1]);var h=[];$headers.each(function(offset){if(!this.sortDisabled){h[this.column]=$(this);}});var l=list.length;for(var i=0;i<l;i++){h[list[i][0]].addClass(css[list[i][1]]);}}function fixColumnWidth(table,$headers){var c=table.config;if(c.widthFixed){var colgroup=$('<colgroup>');$("tr:first td",table.tBodies[0]).each(function(){colgroup.append($('<col>').css('width',$(this).width()));});$(table).prepend(colgroup);};}function updateHeaderSortCount(table,sortList){var c=table.config,l=sortList.length;for(var i=0;i<l;i++){var s=sortList[i],o=c.headerList[s[0]];o.count=s[1];o.count++;}}function multisort(table,sortList,cache){if(table.config.debug){var sortTime=new Date();}var dynamicExp="var sortWrapper = function(a,b) {",l=sortList.length;for(var i=0;i<l;i++){var c=sortList[i][0];var order=sortList[i][1];var s=(table.config.parsers[c].type=="text")?((order==0)?makeSortFunction("text","asc",c):makeSortFunction("text","desc",c)):((order==0)?makeSortFunction("numeric","asc",c):makeSortFunction("numeric","desc",c));var e="e"+i;dynamicExp+="var "+e+" = "+s;dynamicExp+="if("+e+") { return "+e+"; } ";dynamicExp+="else { ";}var orgOrderCol=cache.normalized[0].length-1;dynamicExp+="return a["+orgOrderCol+"]-b["+orgOrderCol+"];";for(var i=0;i<l;i++){dynamicExp+="}; ";}dynamicExp+="return 0; ";dynamicExp+="}; ";if(table.config.debug){benchmark("Evaling expression:"+dynamicExp,new Date());}eval(dynamicExp);cache.normalized.sort(sortWrapper);if(table.config.debug){benchmark("Sorting on "+sortList.toString()+" and dir "+order+" time:",sortTime);}return cache;};function makeSortFunction(type,direction,index){var a="a["+index+"]",b="b["+index+"]";if(type=='text'&&direction=='asc'){return"("+a+" == "+b+" ? 0 : ("+a+" === null ? Number.POSITIVE_INFINITY : ("+b+" === null ? Number.NEGATIVE_INFINITY : ("+a+" < "+b+") ? -1 : 1 )));";}else if(type=='text'&&direction=='desc'){return"("+a+" == "+b+" ? 0 : ("+a+" === null ? Number.POSITIVE_INFINITY : ("+b+" === null ? Number.NEGATIVE_INFINITY : ("+b+" < "+a+") ? -1 : 1 )));";}else if(type=='numeric'&&direction=='asc'){return"("+a+" === null && "+b+" === null) ? 0 :("+a+" === null ? Number.POSITIVE_INFINITY : ("+b+" === null ? Number.NEGATIVE_INFINITY : "+a+" - "+b+"));";}else if(type=='numeric'&&direction=='desc'){return"("+a+" === null && "+b+" === null) ? 0 :("+a+" === null ? Number.POSITIVE_INFINITY : ("+b+" === null ? Number.NEGATIVE_INFINITY : "+b+" - "+a+"));";}};function makeSortText(i){return"((a["+i+"] < b["+i+"]) ? -1 : ((a["+i+"] > b["+i+"]) ? 1 : 0));";};function makeSortTextDesc(i){return"((b["+i+"] < a["+i+"]) ? -1 : ((b["+i+"] > a["+i+"]) ? 1 : 0));";};function makeSortNumeric(i){return"a["+i+"]-b["+i+"];";};function makeSortNumericDesc(i){return"b["+i+"]-a["+i+"];";};function sortText(a,b){if(table.config.sortLocaleCompare)return a.localeCompare(b);return((a<b)?-1:((a>b)?1:0));};function sortTextDesc(a,b){if(table.config.sortLocaleCompare)return b.localeCompare(a);return((b<a)?-1:((b>a)?1:0));};function sortNumeric(a,b){return a-b;};function sortNumericDesc(a,b){return b-a;};function getCachedSortType(parsers,i){return parsers[i].type;};this.construct=function(settings){return this.each(function(){if(!this.tHead||!this.tBodies)return;var $this,$document,$headers,cache,config,shiftDown=0,sortOrder;this.config={};config=$.extend(this.config,$.tablesorter.defaults,settings);$this=$(this);$.data(this,"tablesorter",config);$headers=buildHeaders(this);this.config.parsers=buildParserCache(this,$headers);cache=buildCache(this);var sortCSS=[config.cssDesc,config.cssAsc];fixColumnWidth(this);$headers.click(function(e){var totalRows=($this[0].tBodies[0]&&$this[0].tBodies[0].rows.length)||0;if(!this.sortDisabled&&totalRows>0){$this.trigger("sortStart");var $cell=$(this);var i=this.column;this.order=this.count++%2;if(this.lockedOrder)this.order=this.lockedOrder;if(!e[config.sortMultiSortKey]){config.sortList=[];if(config.sortForce!=null){var a=config.sortForce;for(var j=0;j<a.length;j++){if(a[j][0]!=i){config.sortList.push(a[j]);}}}config.sortList.push([i,this.order]);}else{if(isValueInArray(i,config.sortList)){for(var j=0;j<config.sortList.length;j++){var s=config.sortList[j],o=config.headerList[s[0]];if(s[0]==i){o.count=s[1];o.count++;s[1]=o.count%2;}}}else{config.sortList.push([i,this.order]);}};setTimeout(function(){setHeadersCss($this[0],$headers,config.sortList,sortCSS);appendToTable($this[0],multisort($this[0],config.sortList,cache));},1);return false;}}).mousedown(function(){if(config.cancelSelection){this.onselectstart=function(){return false};return false;}});$this.bind("update",function(){var me=this;setTimeout(function(){me.config.parsers=buildParserCache(me,$headers);cache=buildCache(me);},1);}).bind("updateCell",function(e,cell){var config=this.config;var pos=[(cell.parentNode.rowIndex-1),cell.cellIndex];cache.normalized[pos[0]][pos[1]]=config.parsers[pos[1]].format(getElementText(config,cell),cell);}).bind("sorton",function(e,list){$(this).trigger("sortStart");config.sortList=list;var sortList=config.sortList;updateHeaderSortCount(this,sortList);setHeadersCss(this,$headers,sortList,sortCSS);appendToTable(this,multisort(this,sortList,cache));}).bind("appendCache",function(){appendToTable(this,cache);}).bind("applyWidgetId",function(e,id){getWidgetById(id).format(this);}).bind("applyWidgets",function(){applyWidget(this);});if($.metadata&&($(this).metadata()&&$(this).metadata().sortlist)){config.sortList=$(this).metadata().sortlist;}if(config.sortList.length>0){$this.trigger("sorton",[config.sortList]);}applyWidget(this);});};this.addParser=function(parser){var l=parsers.length,a=true;for(var i=0;i<l;i++){if(parsers[i].id.toLowerCase()==parser.id.toLowerCase()){a=false;}}if(a){parsers.push(parser);};};this.addWidget=function(widget){widgets.push(widget);};this.formatFloat=function(s){var i=parseFloat(s);return(isNaN(i))?0:i;};this.formatInt=function(s){var i=parseInt(s);return(isNaN(i))?0:i;};this.isDigit=function(s,config){return/^[-+]?\d*$/.test($.trim(s.replace(/[,.']/g,'')));};this.clearTableBody=function(table){if($.browser.msie){function empty(){while(this.firstChild)this.removeChild(this.firstChild);}empty.apply(table.tBodies[0]);}else{table.tBodies[0].innerHTML="";}};}});$.fn.extend({tablesorter:$.tablesorter.construct});var ts=$.tablesorter;ts.addParser({id:"text",is:function(s){return true;},format:function(s){return $.trim(s.toLocaleLowerCase());},type:"text"});ts.addParser({id:"digit",is:function(s,table){var c=table.config;return $.tablesorter.isDigit(s,c);},format:function(s){return $.tablesorter.formatFloat(s);},type:"numeric"});ts.addParser({id:"currency",is:function(s){return/^[£$€?.]/.test(s);},format:function(s){return $.tablesorter.formatFloat(s.replace(new RegExp(/[£$€]/g),""));},type:"numeric"});ts.addParser({id:"ipAddress",is:function(s){return/^\d{2,3}[\.]\d{2,3}[\.]\d{2,3}[\.]\d{2,3}$/.test(s);},format:function(s){var a=s.split("."),r="",l=a.length;for(var i=0;i<l;i++){var item=a[i];if(item.length==2){r+="0"+item;}else{r+=item;}}return $.tablesorter.formatFloat(r);},type:"numeric"});ts.addParser({id:"url",is:function(s){return/^(https?|ftp|file):\/\/$/.test(s);},format:function(s){return jQuery.trim(s.replace(new RegExp(/(https?|ftp|file):\/\//),''));},type:"text"});ts.addParser({id:"isoDate",is:function(s){return/^\d{4}[\/-]\d{1,2}[\/-]\d{1,2}$/.test(s);},format:function(s){return $.tablesorter.formatFloat((s!="")?new Date(s.replace(new RegExp(/-/g),"/")).getTime():"0");},type:"numeric"});ts.addParser({id:"percent",is:function(s){return/\%$/.test($.trim(s));},format:function(s){return $.tablesorter.formatFloat(s.replace(new RegExp(/%/g),""));},type:"numeric"});ts.addParser({id:"usLongDate",is:function(s){return s.match(new RegExp(/^[A-Za-z]{3,10}\.? [0-9]{1,2}, ([0-9]{4}|'?[0-9]{2}) (([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(AM|PM)))$/));},format:function(s){return $.tablesorter.formatFloat(new Date(s).getTime());},type:"numeric"});ts.addParser({id:"shortDate",is:function(s){return/\d{1,2}[\/\-]\d{1,2}[\/\-]\d{2,4}/.test(s);},format:function(s,table){var c=table.config;s=s.replace(/\-/g,"/");if(c.dateFormat=="us"){s=s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/,"$3/$1/$2");}else if(c.dateFormat=="uk"){s=s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/,"$3/$2/$1");}else if(c.dateFormat=="dd/mm/yy"||c.dateFormat=="dd-mm-yy"){s=s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{2})/,"$1/$2/$3");}return $.tablesorter.formatFloat(new Date(s).getTime());},type:"numeric"});ts.addParser({id:"time",is:function(s){return/^(([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(am|pm)))$/.test(s);},format:function(s){return $.tablesorter.formatFloat(new Date("2000/01/01 "+s).getTime());},type:"numeric"});ts.addParser({id:"metadata",is:function(s){return false;},format:function(s,table,cell){var c=table.config,p=(!c.parserMetadataName)?'sortValue':c.parserMetadataName;return $(cell).metadata()[p];},type:"numeric"});ts.addWidget({id:"zebra",format:function(table){if(table.config.debug){var time=new Date();}var $tr,row=-1,odd;$("tr:visible",table.tBodies[0]).each(function(i){$tr=$(this);if(!$tr.hasClass(table.config.cssChildRow))row++;odd=(row%2==0);$tr.removeClass(table.config.widgetZebra.css[odd?0:1]).addClass(table.config.widgetZebra.css[odd?1:0])});if(table.config.debug){$.tablesorter.benchmark("Applying Zebra widget",time);}}});})(jQuery); \ No newline at end of file
diff --git a/lib/common_test/src/Makefile b/lib/common_test/src/Makefile
index 037a686963..eb35a43d99 100644
--- a/lib/common_test/src/Makefile
+++ b/lib/common_test/src/Makefile
@@ -70,14 +70,19 @@ MODULES= \
ct_hooks\
ct_hooks_lock\
cth_log_redirect\
- cth_surefire
+ cth_surefire \
+ ct_netconfc \
+ ct_conn_log_h \
+ cth_conn_log \
+ ct_groups
TARGET_MODULES= $(MODULES:%=$(EBIN)/%)
BEAM_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR))
ERL_FILES= $(MODULES:=.erl)
HRL_FILES = \
- ct_util.hrl
+ ct_util.hrl \
+ ct_netconfc.hrl
EXTERNAL_HRL_FILES = \
../include/ct.hrl \
../include/ct_event.hrl
@@ -122,10 +127,10 @@ clean:
# Special Build Targets
# ----------------------------------------------------
$(APP_TARGET): $(APP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
# ----------------------------------------------------
# Release Target
diff --git a/lib/common_test/src/common_test.app.src b/lib/common_test/src/common_test.app.src
index ae9a51faeb..18c1dec784 100644
--- a/lib/common_test/src/common_test.app.src
+++ b/lib/common_test/src/common_test.app.src
@@ -33,6 +33,8 @@
ct_master_event,
ct_master_logs,
ct_master_status,
+ ct_netconfc,
+ ct_conn_log_h,
ct_repeat,
ct_rpc,
ct_run,
@@ -49,6 +51,7 @@
ct_config_xml,
ct_slave,
cth_log_redirect,
+ cth_conn_log,
cth_surefire
]},
{registered, [ct_logs,
diff --git a/lib/common_test/src/ct.erl b/lib/common_test/src/ct.erl
index 571d99029f..8eafdff29f 100644
--- a/lib/common_test/src/ct.erl
+++ b/lib/common_test/src/ct.erl
@@ -25,24 +25,24 @@
%%%
%%% <p><strong>Test Suite Support Macros</strong></p>
%%%
-%%% <p>The <code>config</code> macro is defined in <code>ct.hrl</code>. This
+%%% <p>The <c>config</c> macro is defined in <c>ct.hrl</c>. This
%%% macro should be used to retrieve information from the
-%%% <code>Config</code> variable sent to all test cases. It is used with two
+%%% <c>Config</c> variable sent to all test cases. It is used with two
%%% arguments, where the first is the name of the configuration
-%%% variable you wish to retrieve, and the second is the <code>Config</code>
+%%% variable you wish to retrieve, and the second is the <c>Config</c>
%%% variable supplied to the test case.</p>
%%%
%%% <p>Possible configuration variables include:</p>
%%% <ul>
-%%% <li><code>data_dir</code> - Data file directory.</li>
-%%% <li><code>priv_dir</code> - Scratch file directory.</li>
-%%% <li>Whatever added by <code>init_per_suite/1</code> or
-%%% <code>init_per_testcase/2</code> in the test suite.</li>
+%%% <li><c>data_dir</c> - Data file directory.</li>
+%%% <li><c>priv_dir</c> - Scratch file directory.</li>
+%%% <li>Whatever added by <c>init_per_suite/1</c> or
+%%% <c>init_per_testcase/2</c> in the test suite.</li>
%%% </ul>
%%% @type var_name() = atom(). A variable name which is specified when
-%%% <code>ct:require/2</code> is called,
-%%% e.g. <code>ct:require(mynodename,{node,[telnet]})</code>
+%%% <c>ct:require/2</c> is called,
+%%% e.g. <c>ct:require(mynodename,{node,[telnet]})</c>
%%%
%%% @type target_name() = var_name(). The name of a target.
%%%
@@ -51,6 +51,8 @@
-module(ct).
+-include("ct.hrl").
+
%% Command line user interface for running tests
-export([install/1, run/1, run/2, run/3,
run_test/1, run_testspec/1, step/3, step/4,
@@ -60,13 +62,15 @@
-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,
+ log/1, log/2, log/3, log/4,
+ print/1, print/2, print/3, print/4,
+ pal/1, pal/2, pal/3, pal/4,
capture_start/0, capture_stop/0, capture_get/0, capture_get/1,
fail/1, fail/2, comment/1, comment/2, make_priv_dir/0,
testcases/2, userdata/2, userdata/3,
- timetrap/1, get_timetrap_info/0, sleep/1]).
+ timetrap/1, get_timetrap_info/0, sleep/1,
+ notify/2, sync_notify/2,
+ break/1, break/2, continue/0, continue/1]).
%% New API for manipulating with config handlers
-export([add_config/2, remove_config/2]).
@@ -95,10 +99,10 @@
%%% <p>Run this function once before first test.</p>
%%%
%%% <p>Example:<br/>
-%%% <code>install([{config,["config_node.ctc","config_user.ctc"]}])</code>.</p>
+%%% <c>install([{config,["config_node.ctc","config_user.ctc"]}])</c>.</p>
%%%
%%% <p>Note that this function is automatically run by the
-%%% <code>ct_run</code> program.</p>
+%%% <c>ct_run</c> program.</p>
install(Opts) ->
ct_run:install(Opts).
@@ -111,10 +115,10 @@ install(Opts) ->
%%%
%%% @doc Run the given test case(s).
%%%
-%%% <p>Requires that <code>ct:install/1</code> has been run first.</p>
+%%% <p>Requires that <c>ct:install/1</c> has been run first.</p>
%%%
%%% <p>Suites (*_SUITE.erl) files must be stored in
-%%% <code>TestDir</code> or <code>TestDir/test</code>. All suites
+%%% <c>TestDir</c> or <c>TestDir/test</c>. All suites
%%% will be compiled when test is run.</p>
run(TestDir,Suite,Cases) ->
ct_run:run(TestDir,Suite,Cases).
@@ -144,18 +148,21 @@ run(TestDirs) ->
%%% {config,CfgFiles} | {userconfig, UserConfig} |
%%% {allow_user_terms,Bool} | {logdir,LogDir} |
%%% {silent_connections,Conns} | {stylesheet,CSSFile} |
-%%% {cover,CoverSpecFile} | {step,StepOpts} |
+%%% {cover,CoverSpecFile} | {cover_stop,Bool} | {step,StepOpts} |
%%% {event_handler,EventHandlers} | {include,InclDirs} |
%%% {auto_compile,Bool} | {create_priv_dir,CreatePrivDir} |
%%% {multiply_timetraps,M} | {scale_timetraps,Bool} |
%%% {repeat,N} | {duration,DurTime} | {until,StopTime} |
%%% {force_stop,Bool} | {decrypt,DecryptKeyOrFile} |
-%%% {refresh_logs,LogDir} | {logopts,LogOpts} | {basic_html,Bool} |
-%%% {ct_hooks, CTHs} | {enable_builtin_hooks,Bool}
+%%% {refresh_logs,LogDir} | {logopts,LogOpts} |
+%%% {verbosity,VLevels} | {basic_html,Bool} |
+%%% {ct_hooks, CTHs} | {enable_builtin_hooks,Bool} |
+%%% {release_shell,Bool}
%%% TestDirs = [string()] | string()
%%% Suites = [string()] | [atom()] | string() | atom()
%%% Cases = [atom()] | atom()
-%%% Groups = [atom()] | atom()
+%%% Groups = GroupNameOrPath | [GroupNameOrPath]
+%%% GroupNameOrPath = [atom()] | atom() | all
%%% TestSpecs = [string()] | string()
%%% Label = string() | atom()
%%% CfgFiles = [string()] | string()
@@ -182,26 +189,45 @@ run(TestDirs) ->
%%% DecryptFile = string()
%%% LogOpts = [LogOpt]
%%% LogOpt = no_nl | no_src
+%%% VLevels = VLevel | [{Category,VLevel}]
+%%% VLevel = integer()
+%%% Category = atom()
%%% 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>.
+%%% Result = {Ok,Failed,{UserSkipped,AutoSkipped}} | TestRunnerPid | {error,Reason}
+%%% Ok = integer()
+%%% Failed = integer()
+%%% UserSkipped = integer()
+%%% AutoSkipped = integer()
+%%% TestRunnerPid = pid()
+%%% Reason = term()
+%%% @doc <p>Run tests as specified by the combination of options in <c>Opts</c>.
%%% 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>ct_run</code>
-%%% program. Configuration files specified in <code>Opts</code> will be
-%%% installed automatically at startup.
+%%% <seealso marker="ct_run#ct_run"><c>ct_run</c></seealso> program.
+%%% Note that here a <c>TestDir</c> can be used to point out the path to
+%%% a <c>Suite</c>. Note also that the option <c>testcase</c>
+%%% corresponds to the <c>-case</c> option in the <c>ct_run</c>
+%%% program. Configuration files specified in <c>Opts</c> will be
+%%% installed automatically at startup.</p>
+%%% <p><c>TestRunnerPid</c> is returned if <c>release_shell == true</c>
+%%% (see <c>break/1</c> for details).</p>
+%%% <p><c>Reason</c> indicates what type of error has been encountered.</p>
run_test(Opts) ->
ct_run:run_test(Opts).
%%%-----------------------------------------------------------------
%%% @spec run_testspec(TestSpec) -> Result
%%% TestSpec = [term()]
-%%% @doc Run test specified by <code>TestSpec</code>. The terms are
+%%% Result = {Ok,Failed,{UserSkipped,AutoSkipped}} | {error,Reason}
+%%% Ok = integer()
+%%% Failed = integer()
+%%% UserSkipped = integer()
+%%% AutoSkipped = integer()
+%%% Reason = term()
+%%% @doc Run test specified by <c>TestSpec</c>. The terms are
%%% the same as those used in test specification files.
+%%% <p><c>Reason</c> indicates what type of error has been encountered.</p>
run_testspec(TestSpec) ->
ct_run:run_testspec(TestSpec).
@@ -221,8 +247,8 @@ step(TestDir,Suite,Case) ->
%%% Opt = config | keep_inactive
%%%
%%% @doc Step through a test case with the debugger. If the
-%%% <code>config</code> option has been given, breakpoints will
-%%% be set also on the configuration functions in <code>Suite</code>.
+%%% <c>config</c> option has been given, breakpoints will
+%%% be set also on the configuration functions in <c>Suite</c>.
%%% @see run/3
step(TestDir,Suite,Case,Opts) ->
ct_run:step(TestDir,Suite,Case,Opts).
@@ -234,22 +260,23 @@ 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 OS command line with <code>ct_run -shell
-%%% [-config File...]</code>.</p>
+%%% started from the OS command line with <c>ct_run -shell
+%%% [-config File...]</c>.</p>
%%%
%%% <p>If any functions using "required config data" (e.g. telnet or
%%% ftp functions) are to be called from the erlang shell, config data
-%%% must first be required with <code>ct:require/2</code>.</p>
+%%% must first be required with <c>ct:require/2</c>.</p>
%%%
%%% <p>Example:<br/>
-%%% <code>&gt; ct:require(unix_telnet, unix).</code><br/>
-%%% <code>ok</code><br/>
-%%% <code>&gt; ct_telnet:open(unix_telnet).</code><br/>
-%%% <code>{ok,&lt;0.105.0&gt;}</code><br/>
-%%% <code>&gt; ct_telnet:cmd(unix_telnet, "ls .").</code><br/>
-%%% <code>{ok,["ls","file1 ...",...]}</code></p>
+%%% <c>&gt; ct:require(unix_telnet, unix).</c><br/>
+%%% <c>ok</c><br/>
+%%% <c>&gt; ct_telnet:open(unix_telnet).</c><br/>
+%%% <c>{ok,&lt;0.105.0&gt;}</c><br/>
+%%% <c>&gt; ct_telnet:cmd(unix_telnet, "ls .").</c><br/>
+%%% <c>{ok,["ls","file1 ...",...]}</c></p>
start_interactive() ->
- ct_util:start(interactive).
+ ct_util:start(interactive),
+ ok.
%%%-----------------------------------------------------------------
%%% @spec stop_interactive() -> ok
@@ -257,7 +284,8 @@ start_interactive() ->
%%% @doc Exit the interactive mode.
%%% @see start_interactive/0
stop_interactive() ->
- ct_util:stop(normal).
+ ct_util:stop(normal),
+ ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% MISC INTERFACE
@@ -265,27 +293,34 @@ stop_interactive() ->
%%%-----------------------------------------------------------------
%%% @spec require(Required) -> ok | {error,Reason}
-%%% Required = Key | {Key,SubKeys}
+%%% Required = Key | {Key,SubKeys} | {Key,SubKey,SubKeys}
%%% Key = atom()
%%% SubKeys = SubKey | [SubKey]
%%% SubKey = atom()
%%%
-%%% @doc Check if the required configuration is available.
+%%% @doc Check if the required configuration is available. It is possible
+%%% to specify arbitrarily deep tuples as <c>Required</c>. Note that it is
+%%% only the last element of the tuple which can be a list of <c>SubKey</c>s.
%%%
-%%% <p>Example: require the variable <code>myvar</code>:<br/>
-%%% <code>ok = ct:require(myvar)</code></p>
+%%% <p>Example 1: require the variable <c>myvar</c>:</p>
+%%% <pre>ok = ct:require(myvar).</pre>
%%%
%%% <p>In this case the config file must at least contain:</p>
-%%% <pre>
-%%% {myvar,Value}.</pre>
+%%% <pre>{myvar,Value}.</pre>
%%%
-%%% <p>Example: require the variable <code>myvar</code> with
-%%% subvariable <code>sub1</code>:<br/>
-%%% <code>ok = ct:require({myvar,sub1})</code></p>
+%%% <p>Example 2: require the key <c>myvar</c> with
+%%% subkeys <c>sub1</c> and <c>sub2</c>:</p>
+%%% <pre>ok = ct:require({myvar,[sub1,sub2]}).</pre>
%%%
%%% <p>In this case the config file must at least contain:</p>
-%%% <pre>
-%%% {myvar,[{sub1,Value}]}.</pre>
+%%% <pre>{myvar,[{sub1,Value},{sub2,Value}]}.</pre>
+%%%
+%%% <p>Example 3: require the key <c>myvar</c> with
+%%% subkey <c>sub1</c> with <c>subsub1</c>:</p>
+%%% <pre>ok = ct:require({myvar,sub1,sub2}).</pre>
+%%%
+%%% <p>In this case the config file must at least contain:</p>
+%%% <pre>{myvar,[{sub1,[{sub2,Value}]}]}.</pre>
%%%
%%% @see require/2
%%% @see get_config/1
@@ -297,30 +332,36 @@ require(Required) ->
%%%-----------------------------------------------------------------
%%% @spec require(Name,Required) -> ok | {error,Reason}
%%% Name = atom()
-%%% Required = Key | {Key,SubKeys}
+%%% Required = Key | {Key,SubKey} | {Key,SubKey,SubKey}
+%%% SubKey = Key
%%% Key = atom()
-%%% SubKeys = SubKey | [SubKey]
-%%% SubKey = atom()
%%%
%%% @doc Check if the required configuration is available, and give it
-%%% a name.
+%%% a name. The semantics for <c>Required</c> is the same as in
+%%% <c>required/1</c> except that it is not possible to specify a list
+%%% of <c>SubKey</c>s.
%%%
-%%% <p>If the requested data is available, the main entry will be
-%%% associated with <code>Name</code> so that the value of the element
-%%% can be read with <code>get_config/1,2</code> provided
-%%% <code>Name</code> instead of the <code>Key</code>.</p>
+%%% <p>If the requested data is available, the sub entry will be
+%%% associated with <c>Name</c> so that the value of the element
+%%% can be read with <c>get_config/1,2</c> provided
+%%% <c>Name</c> instead of the whole <c>Required</c> term.</p>
%%%
%%% <p>Example: Require one node with a telnet connection and an
-%%% ftp connection. Name the node <code>a</code>:<br/> <code>ok =
-%%% ct:require(a,{node,[telnet,ftp]}).</code><br/> All references
-%%% to this node may then use the node name. E.g. you can fetch a
-%%% file over ftp like this:<br/>
-%%% <code>ok = ct:ftp_get(a,RemoteFile,LocalFile).</code></p>
+%%% ftp connection. Name the node <c>a</c>:
+%%% <pre>ok = ct:require(a,{machine,node}).</pre>
+%%% All references to this node may then use the node name.
+%%% E.g. you can fetch a file over ftp like this:</p>
+%%% <pre>ok = ct:ftp_get(a,RemoteFile,LocalFile).</pre>
%%%
%%% <p>For this to work, the config file must at least contain:</p>
-%%% <pre>
-%%% {node,[{telnet,IpAddr},
-%%% {ftp,IpAddr}]}.</pre>
+%%% <pre>{machine,[{node,[{telnet,IpAddr},{ftp,IpAddr}]}]}.</pre>
+%%%
+%%% <note><p>The behaviour of this function changed radically in common_test
+%%% 1.6.2. In order too keep some backwards compatability it is still possible
+%%% to do: <br/><c>ct:require(a,{node,[telnet,ftp]}).</c><br/>
+%%% This will associate the name <c>a</c> with the top level <c>node</c> entry.
+%%% For this to work, the config file must at least contain:<br/>
+%%% <c>{node,[{telnet,IpAddr},{ftp,IpAddr}]}.</c></p></note>
%%%
%%% @see require/1
%%% @see get_config/1
@@ -343,7 +384,7 @@ get_config(Required,Default) ->
%%%-----------------------------------------------------------------
%%% @spec get_config(Required,Default,Opts) -> ValueOrElement
-%%% Required = KeyOrName | {KeyOrName,SubKey}
+%%% Required = KeyOrName | {KeyOrName,SubKey} | {KeyOrName,SubKey,SubKey}
%%% KeyOrName = atom()
%%% SubKey = atom()
%%% Default = term()
@@ -355,43 +396,41 @@ get_config(Required,Default) ->
%%%
%%% <p>This function returns the matching value(s) or config element(s),
%%% given a config variable key or its associated name
-%%% (if one has been specified with <code>require/2</code> or a
+%%% (if one has been specified with <c>require/2</c> or a
%%% require statement).</p>
%%%
%%% <p>Example, given the following config file:</p>
%%% <pre>
%%% {unix,[{telnet,IpAddr},
-%%% {username,Username},
-%%% {password,Password}]}.</pre>
-%%% <p><code>get_config(unix,Default) ->
+%%% {user,[{username,Username},
+%%% {password,Password}]}]}.</pre>
+%%% <p><c>ct:get_config(unix,Default) ->
%%% [{telnet,IpAddr},
-%%% {username,Username},
-%%% {password,Password}]</code><br/>
-%%% <code>get_config({unix,telnet},Default) -> IpAddr</code><br/>
-%%% <code>get_config({unix,ftp},Default) -> Default</code><br/>
-%%% <code>get_config(unknownkey,Default) -> Default</code></p>
+%%% {user, [{username,Username},
+%%% {password,Password}]}]</c><br/>
+%%% <c>ct:get_config({unix,telnet},Default) -> IpAddr</c><br/>
+%%% <c>ct:get_config({unix,user,username},Default) -> Username</c><br/>
+%%% <c>ct:get_config({unix,ftp},Default) -> Default</c><br/>
+%%% <c>ct:get_config(unknownkey,Default) -> Default</c></p>
%%%
%%% <p>If a config variable key has been associated with a name (by
-%%% means of <code>require/2</code> or a require statement), the name
+%%% means of <c>require/2</c> or a require statement), the name
%%% may be used instead of the key to read the value:</p>
%%%
-%%% <p><code>require(myhost,unix) -> ok</code><br/>
-%%% <code>get_config(myhost,Default) ->
-%%% [{telnet,IpAddr},
-%%% {username,Username},
-%%% {password,Password}]</code></p>
+%%% <p><c>ct:require(myuser,{unix,user}) -> ok.</c><br/>
+%%% <c>ct:get_config(myuser,Default) ->
+%%% [{username,Username},
+%%% {password,Password}]</c></p>
%%%
%%% <p>If a config variable is defined in multiple files and you want to
-%%% access all possible values, use the <code>all</code> option. The
+%%% access all possible values, use the <c>all</c> option. The
%%% values will be returned in a list and the order of the elements
%%% corresponds to the order that the config files were specified at
%%% startup.</p>
%%%
%%% <p>If you want config elements (key-value tuples) returned as result
-%%% instead of values, use the <code>element</code> option.
-%%% The returned elements will then be on the form <code>{KeyOrName,Value}</code>,
-%%% or (in case a subkey has been specified)
-%%% <code>{{KeyOrName,SubKey},Value}</code></p>
+%%% instead of values, use the <c>element</c> option.
+%%% The returned elements will then be on the form <c>{Required,Value}</c></p>
%%%
%%% @see get_config/1
%%% @see get_config/2
@@ -402,7 +441,7 @@ get_config(Required,Default,Opts) ->
%%%-----------------------------------------------------------------
%%% @spec reload_config(Required) -> ValueOrElement
-%%% Required = KeyOrName | {KeyOrName,SubKey}
+%%% Required = KeyOrName | {KeyOrName,SubKey} | {KeyOrName,SubKey,SubKey}
%%% KeyOrName = atom()
%%% SubKey = atom()
%%% ValueOrElement = term()
@@ -421,25 +460,41 @@ reload_config(Required)->
%%%-----------------------------------------------------------------
%%% @spec log(Format) -> ok
-%%% @equiv log(default,Format,[])
+%%% @equiv log(default,50,Format,[])
log(Format) ->
- log(default,Format,[]).
+ log(default,?STD_IMPORTANCE,Format,[]).
%%%-----------------------------------------------------------------
%%% @spec log(X1,X2) -> ok
-%%% X1 = Category | Format
+%%% X1 = Category | Importance | Format
%%% X2 = Format | Args
-%%% @equiv log(Category,Format,Args)
+%%% @equiv log(Category,Importance,Format,Args)
log(X1,X2) ->
- {Category,Format,Args} =
- if is_atom(X1) -> {X1,X2,[]};
- is_list(X1) -> {default,X1,X2}
+ {Category,Importance,Format,Args} =
+ if is_atom(X1) -> {X1,?STD_IMPORTANCE,X2,[]};
+ is_integer(X1) -> {default,X1,X2,[]};
+ is_list(X1) -> {default,?STD_IMPORTANCE,X1,X2}
end,
- log(Category,Format,Args).
+ log(Category,Importance,Format,Args).
%%%-----------------------------------------------------------------
-%%% @spec log(Category,Format,Args) -> ok
+%%% @spec log(X1,X2,X3) -> ok
+%%% X1 = Category | Importance
+%%% X2 = Importance | Format
+%%% X3 = Format | Args
+%%% @equiv log(Category,Importance,Format,Args)
+log(X1,X2,X3) ->
+ {Category,Importance,Format,Args} =
+ if is_atom(X1), is_integer(X2) -> {X1,X2,X3,[]};
+ is_atom(X1), is_list(X2) -> {X1,?STD_IMPORTANCE,X2,X3};
+ is_integer(X1) -> {default,X1,X2,X3}
+ end,
+ log(Category,Importance,Format,Args).
+
+%%%-----------------------------------------------------------------
+%%% @spec log(Category,Importance,Format,Args) -> ok
%%% Category = atom()
+%%% Importance = integer()
%%% Format = string()
%%% Args = list()
%%%
@@ -448,30 +503,52 @@ log(X1,X2) ->
%%% <p>This function is meant for printing a string directly from a
%%% test case to the test case log file.</p>
%%%
-%%% <p>Default <code>Category</code> is <code>default</code> and
-%%% default <code>Args</code> is <code>[]</code>.</p>
-log(Category,Format,Args) ->
- ct_logs:tc_log(Category,Format,Args).
+%%% <p>Default <c>Category</c> is <c>default</c>,
+%%% default <c>Importance</c> is <c>?STD_IMPORTANCE</c>,
+%%% and default value for <c>Args</c> is <c>[]</c>.</p>
+%%% <p>Please see the User's Guide for details on <c>Category</c>
+%%% and <c>Importance</c>.</p>
+log(Category,Importance,Format,Args) ->
+ ct_logs:tc_log(Category,Importance,Format,Args).
%%%-----------------------------------------------------------------
%%% @spec print(Format) -> ok
-%%% @equiv print(default,Format,[])
+%%% @equiv print(default,50,Format,[])
print(Format) ->
- print(default,Format,[]).
+ print(default,?STD_IMPORTANCE,Format,[]).
%%%-----------------------------------------------------------------
-%%% @equiv print(Category,Format,Args)
+%%% @spec print(X1,X2) -> ok
+%%% X1 = Category | Importance | Format
+%%% X2 = Format | Args
+%%% @equiv print(Category,Importance,Format,Args)
print(X1,X2) ->
- {Category,Format,Args} =
- if is_atom(X1) -> {X1,X2,[]};
- is_list(X1) -> {default,X1,X2}
+ {Category,Importance,Format,Args} =
+ if is_atom(X1) -> {X1,?STD_IMPORTANCE,X2,[]};
+ is_integer(X1) -> {default,X1,X2,[]};
+ is_list(X1) -> {default,?STD_IMPORTANCE,X1,X2}
end,
- print(Category,Format,Args).
+ print(Category,Importance,Format,Args).
%%%-----------------------------------------------------------------
-%%% @spec print(Category,Format,Args) -> ok
+%%% @spec print(X1,X2,X3) -> ok
+%%% X1 = Category | Importance
+%%% X2 = Importance | Format
+%%% X3 = Format | Args
+%%% @equiv print(Category,Importance,Format,Args)
+print(X1,X2,X3) ->
+ {Category,Importance,Format,Args} =
+ if is_atom(X1), is_integer(X2) -> {X1,X2,X3,[]};
+ is_atom(X1), is_list(X2) -> {X1,?STD_IMPORTANCE,X2,X3};
+ is_integer(X1) -> {default,X1,X2,X3}
+ end,
+ print(Category,Importance,Format,Args).
+
+%%%-----------------------------------------------------------------
+%%% @spec print(Category,Importance,Format,Args) -> ok
%%% Category = atom()
+%%% Importance = integer()
%%% Format = string()
%%% Args = list()
%%%
@@ -480,33 +557,52 @@ print(X1,X2) ->
%%% <p>This function is meant for printing a string from a test case
%%% to the console.</p>
%%%
-%%% <p>Default <code>Category</code> is <code>default</code> and
-%%% default <code>Args</code> is <code>[]</code>.</p>
-print(Category,Format,Args) ->
- ct_logs:tc_print(Category,Format,Args).
+%%% <p>Default <c>Category</c> is <c>default</c>,
+%%% default <c>Importance</c> is <c>?STD_IMPORTANCE</c>,
+%%% and default value for <c>Args</c> is <c>[]</c>.</p>
+%%% <p>Please see the User's Guide for details on <c>Category</c>
+%%% and <c>Importance</c>.</p>
+print(Category,Importance,Format,Args) ->
+ ct_logs:tc_print(Category,Importance,Format,Args).
%%%-----------------------------------------------------------------
%%% @spec pal(Format) -> ok
-%%% @equiv pal(default,Format,[])
+%%% @equiv pal(default,50,Format,[])
pal(Format) ->
- pal(default,Format,[]).
+ pal(default,?STD_IMPORTANCE,Format,[]).
%%%-----------------------------------------------------------------
%%% @spec pal(X1,X2) -> ok
-%%% X1 = Category | Format
+%%% X1 = Category | Importance | Format
%%% X2 = Format | Args
-%%% @equiv pal(Category,Format,Args)
+%%% @equiv pal(Category,Importance,Format,Args)
pal(X1,X2) ->
- {Category,Format,Args} =
- if is_atom(X1) -> {X1,X2,[]};
- is_list(X1) -> {default,X1,X2}
+ {Category,Importance,Format,Args} =
+ if is_atom(X1) -> {X1,?STD_IMPORTANCE,X2,[]};
+ is_integer(X1) -> {default,X1,X2,[]};
+ is_list(X1) -> {default,?STD_IMPORTANCE,X1,X2}
end,
- pal(Category,Format,Args).
+ pal(Category,Importance,Format,Args).
%%%-----------------------------------------------------------------
-%%% @spec pal(Category,Format,Args) -> ok
+%%% @spec pal(X1,X2,X3) -> ok
+%%% X1 = Category | Importance
+%%% X2 = Importance | Format
+%%% X3 = Format | Args
+%%% @equiv pal(Category,Importance,Format,Args)
+pal(X1,X2,X3) ->
+ {Category,Importance,Format,Args} =
+ if is_atom(X1), is_integer(X2) -> {X1,X2,X3,[]};
+ is_atom(X1), is_list(X2) -> {X1,?STD_IMPORTANCE,X2,X3};
+ is_integer(X1) -> {default,X1,X2,X3}
+ end,
+ pal(Category,Importance,Format,Args).
+
+%%%-----------------------------------------------------------------
+%%% @spec pal(Category,Importance,Format,Args) -> ok
%%% Category = atom()
+%%% Importance = integer()
%%% Format = string()
%%% Args = list()
%%%
@@ -515,10 +611,13 @@ pal(X1,X2) ->
%%% <p>This function is meant for printing a string from a test case,
%%% both to the test case log file and to the console.</p>
%%%
-%%% <p>Default <code>Category</code> is <code>default</code> and
-%%% default <code>Args</code> is <code>[]</code>.</p>
-pal(Category,Format,Args) ->
- ct_logs:tc_pal(Category,Format,Args).
+%%% <p>Default <c>Category</c> is <c>default</c>,
+%%% default <c>Importance</c> is <c>?STD_IMPORTANCE</c>,
+%%% and default value for <c>Args</c> is <c>[]</c>.</p>
+%%% <p>Please see the User's Guide for details on <c>Category</c>
+%%% and <c>Importance</c>.</p>
+pal(Category,Importance,Format,Args) ->
+ ct_logs:tc_pal(Category,Importance,Format,Args).
%%%-----------------------------------------------------------------
%%% @spec capture_start() -> ok
@@ -535,7 +634,7 @@ capture_start() ->
%%% @spec capture_stop() -> ok
%%%
%%% @doc Stop capturing text strings (a session started with
-%%% <code>capture_start/0</code>).
+%%% <c>capture_start/0</c>).
%%%
%%% @see capture_start/0
%%% @see capture_get/1
@@ -558,9 +657,9 @@ capture_get() ->
%%%
%%% @doc Return and purge the list of text strings buffered
%%% during the latest session of capturing printouts to stdout.
-%%% With <code>ExclCategories</code> it's possible to specify
-%%% log categories that should be ignored in <code>ListOfStrings</code>.
-%%% If <code>ExclCategories = []</code>, no filtering takes place.
+%%% With <c>ExclCategories</c> it's possible to specify
+%%% log categories that should be ignored in <c>ListOfStrings</c>.
+%%% If <c>ExclCategories = []</c>, no filtering takes place.
%%%
%%% @see capture_start/0
%%% @see capture_stop/0
@@ -585,7 +684,7 @@ capture_get([]) ->
%%% Reason = term()
%%%
%%% @doc Terminate a test case with the given error
-%%% <code>Reason</code>.
+%%% <c>Reason</c>.
fail(Reason) ->
try
exit({test_case_failed,Reason})
@@ -605,7 +704,7 @@ fail(Reason) ->
%%%
%%% @doc Terminate a test case with an error message specified
%%% by a format string and a list of values (used as arguments to
-%%% <code>io_lib:format/2</code>).
+%%% <c>io_lib:format/2</c>).
fail(Format, Args) ->
try io_lib:format(Format, Args) of
Str ->
@@ -628,11 +727,11 @@ fail(Format, Args) ->
%%% @spec comment(Comment) -> void()
%%% Comment = term()
%%%
-%%% @doc Print the given <code>Comment</code> in the comment field in
+%%% @doc Print the given <c>Comment</c> in the comment field in
%%% the table on the test suite result page.
%%%
%%% <p>If called several times, only the last comment is printed.
-%%% The test case return value <code>{comment,Comment}</code>
+%%% The test case return value <c>{comment,Comment}</c>
%%% overwrites the string set by this function.</p>
comment(Comment) when is_list(Comment) ->
Formatted =
@@ -655,10 +754,10 @@ comment(Comment) ->
%%% @doc Print the formatted string in the comment field in
%%% the table on the test suite result page.
%%%
-%%% <p>The <code>Format</code> and <code>Args</code> arguments are
-%%% used in call to <code>io_lib:format/2</code> in order to create
-%%% the comment string. The behaviour of <code>comment/2</code> is
-%%% otherwise the same as the <code>comment/1</code> function (see
+%%% <p>The <c>Format</c> and <c>Args</c> arguments are
+%%% used in call to <c>io_lib:format/2</c> in order to create
+%%% the comment string. The behaviour of <c>comment/2</c> is
+%%% otherwise the same as the <c>comment/1</c> function (see
%%% above for details).</p>
comment(Format, Args) when is_list(Format), is_list(Args) ->
Formatted =
@@ -703,11 +802,11 @@ get_target_name(Handle) ->
%%% @doc Parse the printout from an SQL table and return a list of tuples.
%%%
%%% <p>The printout to parse would typically be the result of a
-%%% <code>select</code> command in SQL. The returned
-%%% <code>Table</code> is a list of tuples, where each tuple is a row
+%%% <c>select</c> command in SQL. The returned
+%%% <c>Table</c> is a list of tuples, where each tuple is a row
%%% in the table.</p>
%%%
-%%% <p><code>Heading</code> is a tuple of strings representing the
+%%% <p><c>Heading</c> is a tuple of strings representing the
%%% headings of each column in the table.</p>
parse_table(Data) ->
ct_util:parse_table(Data).
@@ -779,8 +878,8 @@ make_and_load(Dir, Suite) ->
%%% SuiteUserData = [term()]
%%% Reason = term()
%%%
-%%% @doc Returns any data specified with the tag <code>userdata</code>
-%%% in the list of tuples returned from <code>Suite:suite/0</code>.
+%%% @doc Returns any data specified with the tag <c>userdata</c>
+%%% in the list of tuples returned from <c>Suite:suite/0</c>.
userdata(TestDir, Suite) ->
case make_and_load(TestDir, Suite) of
E = {error,_} ->
@@ -815,9 +914,9 @@ get_userdata(_BadTerm, Spec) ->
%%% TCUserData = [term()]
%%% Reason = term()
%%%
-%%% @doc Returns any data specified with the tag <code>userdata</code>
-%%% in the list of tuples returned from <code>Suite:group(GroupName)</code>
-%%% or <code>Suite:Case()</code>.
+%%% @doc Returns any data specified with the tag <c>userdata</c>
+%%% in the list of tuples returned from <c>Suite:group(GroupName)</c>
+%%% or <c>Suite:Case()</c>.
userdata(TestDir, Suite, {group,GroupName}) ->
case make_and_load(TestDir, Suite) of
E = {error,_} ->
@@ -840,8 +939,9 @@ userdata(TestDir, Suite, Case) when is_atom(Case) ->
%%%-----------------------------------------------------------------
%%% @spec get_status() -> TestStatus | {error,Reason} | no_tests_running
%%% TestStatus = [StatusElem]
-%%% StatusElem = {current,{Suite,TestCase}} | {successful,Successful} |
+%%% StatusElem = {current,TestCaseInfo} | {successful,Successful} |
%%% {failed,Failed} | {skipped,Skipped} | {total,Total}
+%%% TestCaseInfo = {Suite,TestCase} | [{Suite,TestCase}]
%%% Suite = atom()
%%% TestCase = atom()
%%% Successful = integer()
@@ -853,7 +953,8 @@ userdata(TestDir, Suite, Case) when is_atom(Case) ->
%%% Reason = term()
%%%
%%% @doc Returns status of ongoing test. The returned list contains info about
-%%% which test case is currently executing, as well as counters for
+%%% which test case is currently executing (a list of cases when a
+%%% parallel test case group is executing), as well as counters for
%%% successful, failed, skipped, and total test cases so far.
get_status() ->
case get_testdata(curr_tc) of
@@ -878,20 +979,25 @@ get_testdata(Key) ->
Error;
{'EXIT',_Reason} ->
no_tests_running;
+ undefined ->
+ {error,no_testdata};
+ [CurrTC] when Key == curr_tc ->
+ {ok,CurrTC};
Data ->
{ok,Data}
end.
%%%-----------------------------------------------------------------
-%%% @spec abort_current_testcase(Reason) -> ok | {error,no_testcase_running}
+%%% @spec abort_current_testcase(Reason) -> ok | {error,ErrorReason}
%%% Reason = term()
+%%% ErrorReason = no_testcase_running | parallel_group
%%%
%%% @doc <p>When calling this function, the currently executing test case will be aborted.
%%% It is the user's responsibility to know for sure which test case is currently
%%% executing. The function is therefore only safe to call from a function which
%%% has been called (or synchronously invoked) by the test case.</p>
%%%
-%%% <p><code>Reason</code>, the reason for aborting the test case, is printed
+%%% <p><c>Reason</c>, the reason for aborting the test case, is printed
%%% in the test case log.</p>
abort_current_testcase(Reason) ->
test_server_ctrl:abort_current_testcase(Reason).
@@ -904,13 +1010,13 @@ abort_current_testcase(Reason) ->
%%% Reason = term()
%%%
%%% @doc <p>This function encrypts the source config file with DES3 and
-%%% saves the result in file <code>EncryptFileName</code>. The key,
+%%% saves the result in file <c>EncryptFileName</c>. The key,
%%% a string, must be available in a text file named
-%%% <code>.ct_config.crypt</code> in the current directory, or the
+%%% <c>.ct_config.crypt</c> in the current directory, or the
%%% home directory of the user (it is searched for in that order).</p>
%%% <p>See the Common Test User's Guide for information about using
%%% encrypted config files when running tests.</p>
-%%% <p>See the <code>crypto</code> application for details on DES3
+%%% <p>See the <c>crypto</c> application for details on DES3
%%% encryption/decryption.</p>
encrypt_config_file(SrcFileName, EncryptFileName) ->
ct_config:encrypt_config_file(SrcFileName, EncryptFileName).
@@ -924,13 +1030,13 @@ encrypt_config_file(SrcFileName, EncryptFileName) ->
%%% Reason = term()
%%%
%%% @doc <p>This function encrypts the source config file with DES3 and
-%%% saves the result in the target file <code>EncryptFileName</code>.
+%%% saves the result in the target file <c>EncryptFileName</c>.
%%% The encryption key to use is either the value in
-%%% <code>{key,Key}</code> or the value stored in the file specified
-%%% by <code>{file,File}</code>.</p>
+%%% <c>{key,Key}</c> or the value stored in the file specified
+%%% by <c>{file,File}</c>.</p>
%%% <p>See the Common Test User's Guide for information about using
%%% encrypted config files when running tests.</p>
-%%% <p>See the <code>crypto</code> application for details on DES3
+%%% <p>See the <c>crypto</c> application for details on DES3
%%% encryption/decryption.</p>
encrypt_config_file(SrcFileName, EncryptFileName, KeyOrFile) ->
ct_config:encrypt_config_file(SrcFileName, EncryptFileName, KeyOrFile).
@@ -942,11 +1048,11 @@ encrypt_config_file(SrcFileName, EncryptFileName, KeyOrFile) ->
%%% TargetFileName = string()
%%% Reason = term()
%%%
-%%% @doc <p>This function decrypts <code>EncryptFileName</code>, previously
-%%% generated with <code>encrypt_config_file/2/3</code>. The original
+%%% @doc <p>This function decrypts <c>EncryptFileName</c>, previously
+%%% generated with <c>encrypt_config_file/2/3</c>. The original
%%% file contents is saved in the target file. The encryption key, a
%%% string, must be available in a text file named
-%%% <code>.ct_config.crypt</code> in the current directory, or the
+%%% <c>.ct_config.crypt</c> 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_config:decrypt_config_file(EncryptFileName, TargetFileName).
@@ -959,8 +1065,8 @@ decrypt_config_file(EncryptFileName, TargetFileName) ->
%%% KeyOrFile = {key,string()} | {file,string()}
%%% Reason = term()
%%%
-%%% @doc <p>This function decrypts <code>EncryptFileName</code>, previously
-%%% generated with <code>encrypt_config_file/2/3</code>. The original
+%%% @doc <p>This function decrypts <c>EncryptFileName</c>, previously
+%%% generated with <c>encrypt_config_file/2/3</c>. The original
%%% 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) ->
@@ -977,7 +1083,7 @@ decrypt_config_file(EncryptFileName, TargetFileName, KeyOrFile) ->
%%% 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>
+%%% <c>remove_config/2</c> function.</p>
add_config(Callback, Config)->
ct_config:add_config(Callback, Config).
@@ -1006,9 +1112,9 @@ remove_config(Callback, Config) ->
%%% A = list()
%%%
%%% @doc <p>Use this function to set a new timetrap for the running test case.
-%%% If the argument is <code>Func</code>, the timetrap will be triggered
-%%% when this function returns. <code>Func</code> may also return a new
-%%% <code>Time</code> value, which in that case will be the value for the
+%%% If the argument is <c>Func</c>, the timetrap will be triggered
+%%% when this function returns. <c>Func</c> may also return a new
+%%% <c>Time</c> value, which in that case will be the value for the
%%% new timetrap.</p>
timetrap(Time) ->
test_server:timetrap_cancel(),
@@ -1047,3 +1153,131 @@ sleep({seconds,Ss}) ->
sleep(trunc(Ss * 1000));
sleep(Time) ->
test_server:adjusted_sleep(Time).
+
+%%%-----------------------------------------------------------------
+%%% @spec notify(Name,Data) -> ok
+%%% Name = atom()
+%%% Data = term()
+%%%
+%%% @doc <p>Sends a asynchronous notification of type <c>Name</c> with
+%%% <c>Data</c>to the common_test event manager. This can later be
+%%% caught by any installed event manager. </p>
+%%% @see //stdlib/gen_event
+notify(Name,Data) ->
+ ct_event:notify(Name, Data).
+
+%%%-----------------------------------------------------------------
+%%% @spec sync_notify(Name,Data) -> ok
+%%% Name = atom()
+%%% Data = term()
+%%%
+%%% @doc <p>Sends a synchronous notification of type <c>Name</c> with
+%%% <c>Data</c>to the common_test event manager. This can later be
+%%% caught by any installed event manager. </p>
+%%% @see //stdlib/gen_event
+sync_notify(Name,Data) ->
+ ct_event:sync_notify(Name, Data).
+
+%%%-----------------------------------------------------------------
+%%% @spec break(Comment) -> ok | {error,Reason}
+%%% Comment = string()
+%%% Reason = {multiple_cases_running,TestCases} |
+%%% 'enable break with release_shell option'
+%%% TestCases = [atom()]
+%%%
+%%% @doc <p>This function will cancel any active timetrap and pause the
+%%% execution of the current test case until the user calls the
+%%% <c>continue/0</c> function. It gives the user the opportunity
+%%% to interact with the erlang node running the tests, e.g. for
+%%% debugging purposes or for manually executing a part of the
+%%% test case. If a parallel group is executing, <c>break/2</c>
+%%% should be called instead.</p>
+%%% <p>A cancelled timetrap will not be automatically
+%%% reactivated after the break, but must be started exlicitly with
+%%% <c>ct:timetrap/1</c></p>
+%%% <p>In order for the break/continue functionality to work,
+%%% Common Test must release the shell process controlling stdin.
+%%% This is done by setting the <c>release_shell</c> start option
+%%% to <c>true</c>. See the User's Guide for more information.</p>
+
+break(Comment) ->
+ case {ct_util:get_testdata(starter),
+ ct_util:get_testdata(release_shell)} of
+ {ct,ReleaseSh} when ReleaseSh /= true ->
+ Warning = "ct:break/1 can only be used if release_shell == true.\n",
+ ct_logs:log("Warning!", Warning, []),
+ io:format(user, "Warning! " ++ Warning, []),
+ {error,'enable break with release_shell option'};
+ _ ->
+ case get_testdata(curr_tc) of
+ {ok,{_,_TestCase}} ->
+ test_server:break(?MODULE, Comment);
+ {ok,Cases} when is_list(Cases) ->
+ {error,{'multiple cases running',
+ [TC || {_,TC} <- Cases]}};
+ Error = {error,_} ->
+ Error;
+ Error ->
+ {error,Error}
+ end
+ end.
+
+%%%-----------------------------------------------------------------
+%%% @spec break(TestCase, Comment) -> ok | {error,Reason}
+%%% TestCase = atom()
+%%% Comment = string()
+%%% Reason = 'test case not running' |
+%%% 'enable break with release_shell option'
+%%%
+%%% @doc <p>This function works the same way as <c>break/1</c>,
+%%% only the <c>TestCase</c> argument makes it possible to
+%%% pause a test case executing in a parallel group. The
+%%% <c>continue/1</c> function should be used to resume
+%%% execution of <c>TestCase</c>.</p>
+%%% <p>See <c>break/1</c> for more details.</p>
+break(TestCase, Comment) ->
+ case {ct_util:get_testdata(starter),
+ ct_util:get_testdata(release_shell)} of
+ {ct,ReleaseSh} when ReleaseSh /= true ->
+ Warning = "ct:break/2 can only be used if release_shell == true.\n",
+ ct_logs:log("Warning!", Warning, []),
+ io:format(user, "Warning! " ++ Warning, []),
+ {error,'enable break with release_shell option'};
+ _ ->
+ case get_testdata(curr_tc) of
+ {ok,Cases} when is_list(Cases) ->
+ case lists:keymember(TestCase, 2, Cases) of
+ true ->
+ test_server:break(?MODULE, TestCase, Comment);
+ false ->
+ {error,'test case not running'}
+ end;
+ {ok,{_,TestCase}} ->
+ test_server:break(?MODULE, TestCase, Comment);
+ Error = {error,_} ->
+ Error;
+ Error ->
+ {error,Error}
+ end
+ end.
+
+%%%-----------------------------------------------------------------
+%%% @spec continue() -> ok
+%%%
+%%% @doc <p>This function must be called in order to continue after a
+%%% test case (not executing in a parallel group) has called
+%%% <c>break/1</c>.</p>
+continue() ->
+ test_server:continue().
+
+%%%-----------------------------------------------------------------
+%%% @spec continue(TestCase) -> ok
+%%% TestCase = atom()
+%%%
+%%% @doc <p>This function must be called in order to continue after a
+%%% test case has called <c>break/2</c>. If the paused test case,
+%%% <c>TestCase</c>, executes in a parallel group, this
+%%% function - rather than <c>continue/0</c> - must be used
+%%% in order to let the test case proceed.</p>
+continue(TestCase) ->
+ test_server:continue(TestCase).
diff --git a/lib/common_test/src/ct_config.erl b/lib/common_test/src/ct_config.erl
index 9277af5bc1..b1d709bc75 100644
--- a/lib/common_test/src/ct_config.erl
+++ b/lib/common_test/src/ct_config.erl
@@ -1,7 +1,7 @@
%%--------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -122,8 +122,8 @@ return({To,Ref},Result) ->
loop(StartDir) ->
receive
- {{require,Name,Tag,SubTags},From} ->
- Result = do_require(Name,Tag,SubTags),
+ {{require,Name,Key},From} ->
+ Result = do_require(Name,Key),
return(From,Result),
loop(StartDir);
{{set_default_config,{Config,Scope}},From} ->
@@ -168,16 +168,19 @@ 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.
+ lists:flatmap(fun({config,[_|_] = FileOrFiles}) ->
+ case {io_lib:printable_list(FileOrFiles),
+ io_lib:printable_list(hd(FileOrFiles))} of
+ {false,true} ->
+ FileOrFiles;
+ {true,false} ->
+ [FileOrFiles];
+ _ ->
+ []
+ end;
+ (_) ->
+ []
+ end,Opts).
process_user_configs(Opts, Acc) ->
case lists:keytake(userconfig, 1, Opts) of
@@ -319,75 +322,58 @@ 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
+ case get_config({KeyOrName}, Default, Opts) of
+ %% If only an atom is given, then we need to unwrap the
+ %% key if it is returned
+ {{KeyOrName}, Val} ->
+ {KeyOrName, Val};
+ [{{KeyOrName}, _Val}|_] = Res ->
+ [{K, Val} || {{K},Val} <- Res, K == KeyOrName];
+ Else ->
+ Else
end;
-get_config({KeyOrName,SubKey},Default,Opts) ->
- case lookup_config(KeyOrName) of
+%% This useage of get_config is only used by internal ct functions
+%% and may change at any time
+get_config({DeepKey,SubKey}, Default, Opts) when is_tuple(DeepKey) ->
+ get_config(erlang:append_element(DeepKey, SubKey), Default, Opts);
+get_config(KeyOrName,Default,Opts) when is_tuple(KeyOrName) ->
+ case lookup_config(element(1,KeyOrName)) of
[] ->
- Default;
+ format_value([Default],KeyOrName,Opts);
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
+ NewVals =
+ lists:map(
+ fun({Val}) ->
+ get_config(tl(tuple_to_list(KeyOrName)),
+ Val,Default,Opts)
+ end,Vals),
+ format_value(NewVals,KeyOrName,Opts)
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)
+get_config([],Vals,_Default,_Opts) ->
+ Vals;
+get_config([[]],Vals,Default,Opts) ->
+ get_config([],Vals,Default,Opts);
+%% This case is used by {require,{unix,[port,host]}} functionality
+get_config([SubKeys], Vals, Default, _Opts) when is_list(SubKeys) ->
+ case do_get_config(SubKeys, Vals, []) of
+ {ok, SubVals} ->
+ [SubVal || {_,SubVal} <- SubVals];
+
+ _ ->
+ Default
end;
-get_subconfig(SubKeys,[],[],_) ->
- {error,{not_available,SubKeys}};
-get_subconfig(_SubKeys,[],Mapped,_) ->
- {ok,Mapped}.
+get_config([Key|Rest], Vals, Default, Opts) ->
+ case do_get_config([Key], Vals, []) of
+ {ok, [{Key,NewVals}]} ->
+ get_config(Rest, NewVals, Default, Opts);
+ _ ->
+ Default
+ end.
+do_get_config([Key|_], Available, _Mapped) when not is_list(Available) ->
+ {error,{not_available,Key}};
do_get_config([Key|Required],Available,Mapped) ->
case lists:keysearch(Key,1,Available) of
{value,{Key,Value}} ->
@@ -403,8 +389,7 @@ do_get_config([],_Available,Mapped) ->
get_all_config() ->
ets:select(?attr_table,[{#ct_conf{name='$1',key='$2',value='$3',
default='$4',_='_'},
- [],
- [{{'$1','$2','$3','$4'}}]}]).
+ [],[{{'$1','$2','$3','$4'}}]}]).
lookup_config(KeyOrName) ->
case lookup_name(KeyOrName) of
@@ -415,13 +400,23 @@ lookup_config(KeyOrName) ->
end.
lookup_name(Name) ->
- ets:select(?attr_table,[{#ct_conf{ref='$1',value='$2',name=Name,_='_'},
- [],
- [{{'$1','$2'}}]}]).
+ ets:select(?attr_table,[{#ct_conf{value='$1',name=Name,_='_'},
+ [],[{{'$1'}}]}]).
lookup_key(Key) ->
- ets:select(?attr_table,[{#ct_conf{key=Key,ref='$1',value='$2',name='_UNDEF',_='_'},
- [],
- [{{'$1','$2'}}]}]).
+ ets:select(?attr_table,[{#ct_conf{key=Key,value='$1',name='_UNDEF',_='_'},
+ [],[{{'$1'}}]}]).
+
+format_value([SubVal|_] = SubVals, KeyOrName, Opts) ->
+ case {lists:member(all,Opts),lists:member(element,Opts)} of
+ {true,true} ->
+ [{KeyOrName,Val} || Val <- SubVals];
+ {true,false} ->
+ [Val || Val <- SubVals];
+ {false,true} ->
+ {KeyOrName,SubVal};
+ {false,false} ->
+ SubVal
+ end.
lookup_handler_for_config({Key, _Subkey}) ->
lookup_handler_for_config(Key);
@@ -475,65 +470,79 @@ release_allocated([H|T]) ->
release_allocated([]) ->
ok.
-allocate(Name,Key,SubKeys) ->
- case ets:match_object(?attr_table,#ct_conf{key=Key,name='_UNDEF',_='_'}) of
- [] ->
+allocate(Name,Key) ->
+ Ref = make_ref(),
+ case get_config(Key,Ref,[all,element]) of
+ [{_,Ref}] ->
{error,{not_available,Key}};
- Available ->
- case allocate_subconfig(Name,SubKeys,Available,false) of
- ok ->
- ok;
- Error ->
- Error
- end
+ Configs ->
+ associate(Name,Key,Configs),
+ ok
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) ->
+
+associate('_UNDEF',_Key,_Configs) ->
ok;
-allocate_subconfig(_Name,SubKeys,[],false) ->
- {error,{not_available,SubKeys}}.
+associate(Name,{Key,SubKeys},Configs) when is_atom(Key), is_list(SubKeys) ->
+ associate_int(Name,Configs,"true");
+associate(Name,_Key,Configs) ->
+ associate_int(Name,Configs,os:getenv("COMMON_TEST_ALIAS_TOP")).
+
+associate_int(Name,Configs,"true") ->
+ lists:map(fun({K,_Config}) ->
+ Cs = ets:match_object(
+ ?attr_table,
+ #ct_conf{key=element(1,K),
+ name='_UNDEF',_='_'}),
+ [ets:insert(?attr_table,C#ct_conf{name=Name})
+ || C <- Cs]
+ end,Configs);
+associate_int(Name,Configs,_) ->
+ lists:map(fun({K,Config}) ->
+ Key = if is_tuple(K) -> element(1,K);
+ is_atom(K) -> K
+ end,
+
+ Cs = ets:match_object(
+ ?attr_table,
+ #ct_conf{key=Key,
+ name='_UNDEF',_='_'}),
+ [ets:insert(?attr_table,C#ct_conf{name=Name,
+ value=Config})
+ || C <- Cs]
+ end,Configs).
+
+
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) when is_atom(Key); is_tuple(Key) ->
+ allocate('_UNDEF',Key);
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,Key) when is_atom(Name),is_atom(Key) orelse is_tuple(Key) ->
+ call({require,Name,Key});
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) ->
+do_require(Name,Key) ->
case get_key_from_name(Name) of
{error,_} ->
- allocate(Name,Key,SubKeys);
- {ok,Key} ->
+ allocate(Name,Key);
+ {ok,NameKey} when NameKey == Key;
+ is_tuple(Key) andalso element(1,Key) == NameKey ->
%% 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
+ R = make_ref(),
+ case get_config(Key,R,[]) of
+ R ->
+ {error,{not_available,Key}};
+ {error,_} = Error ->
+ Error;
+ _Error ->
+ ok
end;
{ok,OtherKey} ->
{error,{name_in_use,Name,OtherKey}}
@@ -760,13 +769,13 @@ check_config_files(Configs) ->
end,
lists:keysearch(error, 1, lists:flatten(lists:map(ConfigChecker, Configs))).
-prepare_user_configs([ConfigString|UserConfigs], Acc, new) ->
+prepare_user_configs([CallbackMod|UserConfigs], Acc, new) ->
prepare_user_configs(UserConfigs,
- [{list_to_atom(ConfigString), []}|Acc],
+ [{list_to_atom(CallbackMod),[]}|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([ConfigString|UserConfigs], [{LastMod,LastList}|Acc], cur) ->
prepare_user_configs(UserConfigs,
[{LastMod, [ConfigString|LastList]}|Acc],
cur);
diff --git a/lib/common_test/src/ct_conn_log_h.erl b/lib/common_test/src/ct_conn_log_h.erl
new file mode 100644
index 0000000000..d7bd18606b
--- /dev/null
+++ b/lib/common_test/src/ct_conn_log_h.erl
@@ -0,0 +1,236 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, 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_conn_log_h).
+
+%%%
+%%% A handler that can be connected to the error_logger event
+%%% handler. Writes all ct connection events. See comments in
+%%% cth_conn_log for more information.
+%%%
+
+-include("ct_util.hrl").
+
+-export([init/1,
+ handle_event/2, handle_call/2, handle_info/2,
+ terminate/2]).
+
+-record(state, {group_leader,logs=[]}).
+
+-define(WIDTH,80).
+
+%%%-----------------------------------------------------------------
+%%% Callbacks
+init({GL,Logs}) ->
+ open_files(Logs,#state{group_leader=GL}).
+
+open_files([{ConnMod,{LogType,Logs}}|T],State) ->
+ case do_open_files(Logs,[]) of
+ {ok,Fds} ->
+ open_files(T,State#state{logs=[{ConnMod,{LogType,Fds}} |
+ State#state.logs]});
+ Error ->
+ Error
+ end;
+open_files([],State) ->
+ {ok,State}.
+
+
+do_open_files([{Tag,File}|Logs],Acc) ->
+ case file:open(File, [write]) of
+ {ok,Fd} ->
+ do_open_files(Logs,[{Tag,Fd}|Acc]);
+ {error,Reason} ->
+ {error,{could_not_open_log,File,Reason}}
+ end;
+do_open_files([],Acc) ->
+ {ok,lists:reverse(Acc)}.
+
+handle_event({_Type, GL, _Msg}, State) when node(GL) /= node() ->
+ {ok, State};
+handle_event({_Type,_GL,{Pid,{ct_connection,Action,ConnName},Report}},State) ->
+ %% NOTE: if the format of this event is changed
+ %% ({ct_connection,Action,ConnName}) then remember to change
+ %% test_server_h:report_receiver as well!!!
+ Info = conn_info(Pid,#conn_log{name=ConnName,action=Action}),
+ write_report(now(),Info,Report,State),
+ {ok, State};
+handle_event({_Type,_GL,{Pid,Info=#conn_log{},Report}},State) ->
+ %% NOTE: if the format of this event is changed
+ %% (Info=#conn_log{}) then remember to change
+ %% test_server_h:report_receiver as well!!!
+ write_report(now(),conn_info(Pid,Info),Report,State),
+ {ok, State};
+handle_event({error_report,_,{Pid,_,[{ct_connection,ConnName}|R]}},State) ->
+ %% Error reports from connection
+ write_error(now(),conn_info(Pid,#conn_log{name=ConnName}),R,State),
+ {ok, State};
+handle_event(_, State) ->
+ {ok, State}.
+
+handle_info(_, State) ->
+ {ok, State}.
+
+handle_call(_Query, State) ->
+ {ok, {error, bad_query}, State}.
+
+terminate(_,#state{logs=Logs}) ->
+ [file:close(Fd) || {_,{_,Fds}} <- Logs, {_,Fd} <- Fds],
+ ok.
+
+
+%%%-----------------------------------------------------------------
+%%% Writing reports
+write_report(Time,#conn_log{module=ConnMod}=Info,Data,State) ->
+ {LogType,Fd} = get_log(Info,State),
+ io:format(Fd,"~n~s~s~s",[format_head(ConnMod,LogType,Time),
+ format_title(LogType,Info),
+ format_data(ConnMod,LogType,Data)]).
+
+write_error(Time,#conn_log{module=ConnMod}=Info,Report,State) ->
+ case get_log(Info,State) of
+ {html,_} ->
+ %% The error will anyway be written in the html log by the
+ %% sasl error handler, so don't write it again.
+ ok;
+ {LogType,Fd} ->
+ io:format(Fd,"~n~s~s~s",[format_head(ConnMod,LogType,Time," ERROR"),
+ format_title(LogType,Info),
+ format_error(LogType,Report)])
+ end.
+
+get_log(Info,State) ->
+ case proplists:get_value(Info#conn_log.module,State#state.logs) of
+ {html,_} ->
+ {html,State#state.group_leader};
+ {LogType,Fds} ->
+ {LogType,get_fd(Info,Fds)};
+ undefined ->
+ {html,State#state.group_leader}
+ end.
+
+get_fd(#conn_log{name=undefined},Fds) ->
+ proplists:get_value(default,Fds);
+get_fd(#conn_log{name=ConnName},Fds) ->
+ case proplists:get_value(ConnName,Fds) of
+ undefined ->
+ proplists:get_value(default,Fds);
+ Fd ->
+ Fd
+ end.
+
+%%%-----------------------------------------------------------------
+%%% Formatting
+format_head(ConnMod,LogType,Time) ->
+ format_head(ConnMod,LogType,Time,"").
+
+format_head(ConnMod,raw,Time,Text) ->
+ io_lib:format("~n~p, ~p~s, ",[now_to_time(Time),ConnMod,Text]);
+format_head(ConnMod,_,Time,Text) ->
+ Head = pad_char_end(?WIDTH,pretty_head(now_to_time(Time),ConnMod,Text),$=),
+ io_lib:format("~n~s",[Head]).
+
+format_title(raw,#conn_log{client=Client}=Info) ->
+ io_lib:format("Client ~p ~s ~s",[Client,actionstr(Info),serverstr(Info)]);
+format_title(_,Info) ->
+ Title = pad_char_end(?WIDTH,pretty_title(Info),$=),
+ io_lib:format("~n~s", [Title]).
+
+format_data(_,_,NoData) when NoData == ""; NoData == <<>> ->
+ "";
+format_data(ConnMod,LogType,Data) ->
+ ConnMod:format_data(LogType,Data).
+
+format_error(raw,Report) ->
+ io_lib:format("~n~p~n",[Report]);
+format_error(pretty,Report) ->
+ [io_lib:format("~n ~p: ~p",[K,V]) || {K,V} <- Report].
+
+
+
+
+%%%-----------------------------------------------------------------
+%%% Helpers
+conn_info(LoggingProc, #conn_log{client=undefined} = ConnInfo) ->
+ conn_info(ConnInfo#conn_log{client=LoggingProc});
+conn_info(_, ConnInfo) ->
+ conn_info(ConnInfo).
+
+conn_info(#conn_log{client=Client, module=undefined} = ConnInfo) ->
+ case ets:lookup(ct_connections,Client) of
+ [#conn{address=Address,callback=Callback}] ->
+ ConnInfo#conn_log{address=Address,module=Callback};
+ [] ->
+ ConnInfo
+ end;
+conn_info(ConnInfo) ->
+ ConnInfo.
+
+
+now_to_time({_,_,MicroS}=Now) ->
+ {calendar:now_to_local_time(Now),MicroS}.
+
+pretty_head({{{Y,Mo,D},{H,Mi,S}},MicroS},ConnMod,Text0) ->
+ Text = string:to_upper(atom_to_list(ConnMod) ++ Text0),
+ io_lib:format("= ~s ==== ~s-~s-~p::~s:~s:~s,~s ",
+ [Text,t(D),month(Mo),Y,t(H),t(Mi),t(S),
+ micro2milli(MicroS)]).
+
+pretty_title(#conn_log{client=Client}=Info) ->
+ io_lib:format("= Client ~p ~s Server ~s ",
+ [Client,actionstr(Info),serverstr(Info)]).
+
+actionstr(#conn_log{action=send}) -> "----->";
+actionstr(#conn_log{action=recv}) -> "<-----";
+actionstr(#conn_log{action=open}) -> "opened session to";
+actionstr(#conn_log{action=close}) -> "closed session to";
+actionstr(_) -> "<---->".
+
+serverstr(#conn_log{name=undefined,address=Address}) ->
+ io_lib:format("~p",[Address]);
+serverstr(#conn_log{name=Alias,address=Address}) ->
+ io_lib:format("~p(~p)",[Alias,Address]).
+
+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".
+
+micro2milli(X) ->
+ pad0(3,integer_to_list(X div 1000)).
+
+t(X) ->
+ pad0(2,integer_to_list(X)).
+
+pad0(N,Str) ->
+ M = length(Str),
+ lists:duplicate(N-M,$0) ++ Str.
+
+pad_char_end(N,Str,Char) ->
+ case length(lists:flatten(Str)) of
+ M when M<N -> Str ++ lists:duplicate(N-M,Char);
+ _ -> Str
+ end.
diff --git a/lib/common_test/src/ct_cover.erl b/lib/common_test/src/ct_cover.erl
index d39f50ba00..ae671c750a 100644
--- a/lib/common_test/src/ct_cover.erl
+++ b/lib/common_test/src/ct_cover.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -24,7 +24,7 @@
-module(ct_cover).
--export([get_spec/1, add_nodes/1, remove_nodes/1]).
+-export([get_spec/1, add_nodes/1, remove_nodes/1, cross_cover_analyse/2]).
-include("ct_util.hrl").
@@ -100,6 +100,22 @@ remove_nodes(Nodes) ->
%%%-----------------------------------------------------------------
+%%% @spec cross_cover_analyse(Level,Tests) -> ok
+%%% Level = overview | details
+%%% Tests = [{Tag,Dir}]
+%%% Tag = atom()
+%%% Dir = string()
+%%%
+%%% @doc Accumulate cover results over multiple tests.
+%%% See the chapter about <seealso
+%%% marker="cover_chapter#cross_cover">cross cover
+%%% analysis</seealso> in the users's guide.
+%%%
+cross_cover_analyse(Level,Tests) ->
+ test_server_ctrl:cross_cover_analyse(Level,Tests).
+
+
+%%%-----------------------------------------------------------------
%%% @hidden
%% Read cover specification file and return the parsed info.
@@ -249,9 +265,11 @@ get_app_info(App=#cover{app=Name}, [{excl_mods,Name,Mods1}|Terms]) ->
Mods = App#cover.excl_mods,
get_app_info(App#cover{excl_mods=Mods++Mods1},Terms);
-get_app_info(App=#cover{app=Name}, [{cross_apps,Name,AppMods1}|Terms]) ->
- AppMods = App#cover.cross,
- get_app_info(App#cover{cross=AppMods++AppMods1},Terms);
+get_app_info(App=#cover{app=none}, [{cross,Cross}|Terms]) ->
+ get_app_info(App, [{cross,none,Cross}|Terms]);
+get_app_info(App=#cover{app=Name}, [{cross,Name,Cross1}|Terms]) ->
+ Cross = App#cover.cross,
+ get_app_info(App#cover{cross=Cross++Cross1},Terms);
get_app_info(App=#cover{app=none}, [{src_dirs,Dirs}|Terms]) ->
get_app_info(App, [{src_dirs,none,Dirs}|Terms]);
@@ -354,10 +372,10 @@ remove_excludes_and_dups(CoverData=#cover{excl_mods=Excl,incl_mods=Incl}) ->
files2mods(Info=#cover{excl_mods=ExclFs,
incl_mods=InclFs,
- cross=CrossFs}) ->
+ cross=Cross}) ->
Info#cover{excl_mods=files2mods1(ExclFs),
incl_mods=files2mods1(InclFs),
- cross=files2mods1(CrossFs)}.
+ cross=[{Tag,files2mods1(Fs)} || {Tag,Fs} <- Cross]}.
files2mods1([M|Fs]) when is_atom(M) ->
[M|files2mods1(Fs)];
diff --git a/lib/common_test/src/ct_event.erl b/lib/common_test/src/ct_event.erl
index 3e79898ad1..49e0635d79 100644
--- a/lib/common_test/src/ct_event.erl
+++ b/lib/common_test/src/ct_event.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -31,7 +31,7 @@
%% API
-export([start_link/0, add_handler/0, add_handler/1, stop/0]).
--export([notify/1, sync_notify/1]).
+-export([notify/1, notify/2, sync_notify/1,sync_notify/2]).
-export([is_alive/0]).
%% gen_event callbacks
@@ -90,6 +90,13 @@ notify(Event) ->
end.
%%--------------------------------------------------------------------
+%% Function: notify(Name,Data) -> ok
+%% Description: Asynchronous notification to event manager.
+%%--------------------------------------------------------------------
+notify(Name, Data) ->
+ notify(#event{ name = Name, data = Data}).
+
+%%--------------------------------------------------------------------
%% Function: sync_notify(Event) -> ok
%% Description: Synchronous notification to event manager.
%%--------------------------------------------------------------------
@@ -102,6 +109,13 @@ sync_notify(Event) ->
end.
%%--------------------------------------------------------------------
+%% Function: sync_notify(Name,Data) -> ok
+%% Description: Synchronous notification to event manager.
+%%--------------------------------------------------------------------
+sync_notify(Name,Data) ->
+ sync_notify(#event{ name = Name, data = Data}).
+
+%%--------------------------------------------------------------------
%% Function: is_alive() -> true | false
%% Description: Check if Event Manager is alive.
%%--------------------------------------------------------------------
diff --git a/lib/common_test/src/ct_framework.erl b/lib/common_test/src/ct_framework.erl
index 11575cd0fb..c1abf27e9f 100644
--- a/lib/common_test/src/ct_framework.erl
+++ b/lib/common_test/src/ct_framework.erl
@@ -27,13 +27,11 @@
-export([init_tc/3, end_tc/3, end_tc/4, get_suite/2, get_all_cases/1]).
-export([report/2, warn/1, error_notification/4]).
--export([get_logopts/0, format_comment/1, get_html_wrapper/3]).
+-export([get_logopts/0, format_comment/1, get_html_wrapper/4]).
-export([error_in_suite/1, init_per_suite/1, end_per_suite/1,
init_per_group/2, end_per_group/2]).
--export([make_all_conf/3, make_conf/5]).
-
-include("ct_event.hrl").
-include("ct_util.hrl").
@@ -72,18 +70,25 @@ init_tc(Mod,Func,Config) ->
{Suite,{suite0_failed,_}=Failure} ->
{skip,Failure};
_ ->
- ct_util:set_testdata({curr_tc,{Suite,Func}}),
+ ct_util:update_testdata(curr_tc,
+ fun(undefined) ->
+ [{Suite,Func}];
+ (Running) ->
+ [{Suite,Func}|Running]
+ end, [create]),
case ct_util:read_suite_data({seq,Suite,Func}) of
undefined ->
init_tc1(Mod,Suite,Func,Config);
Seq when is_atom(Seq) ->
case ct_util:read_suite_data({seq,Suite,Seq}) of
[Func|TCs] -> % this is the 1st case in Seq
- %% make sure no cases in this seq are marked as failed
- %% from an earlier execution in the same suite
+ %% make sure no cases in this seq are
+ %% marked as failed from an earlier execution
+ %% in the same suite
lists:foreach(
fun(TC) ->
- ct_util:save_suite_data({seq,Suite,TC},Seq)
+ ct_util:save_suite_data({seq,Suite,TC},
+ Seq)
end, TCs);
_ ->
ok
@@ -204,20 +209,23 @@ init_tc2(Mod,Suite,Func,SuiteInfo,MergeResult,Config) ->
data={Mod,FuncSpec}}),
case catch configure(MergedInfo,MergedInfo,SuiteInfo,
- FuncSpec,Config) of
+ FuncSpec,[],Config) of
{suite0_failed,Reason} ->
- ct_util:set_testdata({curr_tc,{Mod,{suite0_failed,{require,Reason}}}}),
+ ct_util:set_testdata({curr_tc,{Mod,{suite0_failed,
+ {require,Reason}}}}),
{skip,{require_failed_in_suite0,Reason}};
{error,Reason} ->
{auto_skip,{require_failed,Reason}};
{'EXIT',Reason} ->
{auto_skip,Reason};
- {ok,Config1} ->
+ {ok,PostInitHook,Config1} ->
case get('$test_server_framework_test') of
undefined ->
- ct_suite_init(Suite, FuncSpec, Config1);
+ ct_suite_init(Suite, FuncSpec, PostInitHook, Config1);
Fun ->
- case Fun(init_tc, Config1) of
+ PostInitHookResult = do_post_init_hook(PostInitHook,
+ Config1),
+ case Fun(init_tc, [PostInitHookResult ++ Config1]) of
NewConfig when is_list(NewConfig) ->
{ok,NewConfig};
Else ->
@@ -226,14 +234,28 @@ init_tc2(Mod,Suite,Func,SuiteInfo,MergeResult,Config) ->
end
end.
-ct_suite_init(Suite, Func, [Config]) when is_list(Config) ->
+ct_suite_init(Suite, Func, PostInitHook, Config) when is_list(Config) ->
case ct_hooks:init_tc(Suite, Func, Config) of
NewConfig when is_list(NewConfig) ->
- {ok, [NewConfig]};
+ PostInitHookResult = do_post_init_hook(PostInitHook, NewConfig),
+ {ok, [PostInitHookResult ++ NewConfig]};
Else ->
Else
end.
+do_post_init_hook(PostInitHook, Config) ->
+ lists:flatmap(fun({Tag,Fun}) ->
+ case lists:keysearch(Tag,1,Config) of
+ {value,_} ->
+ [];
+ false ->
+ case Fun() of
+ {error,_} -> [];
+ Result -> [{Tag,Result}]
+ end
+ end
+ end, PostInitHook).
+
add_defaults(Mod,Func, GroupPath) ->
Suite = get_suite_name(Mod, GroupPath),
case (catch Suite:suite()) of
@@ -453,15 +475,16 @@ timetrap_first([],Info,[]) ->
timetrap_first([],Info,Found) ->
?rev(Found) ++ ?rev(Info).
-configure([{require,Required}|Rest],Info,SuiteInfo,Scope,Config) ->
+configure([{require,Required}|Rest],
+ Info,SuiteInfo,Scope,PostInitHook,Config) ->
case ct:require(Required) of
ok ->
- configure(Rest,Info,SuiteInfo,Scope,Config);
+ configure(Rest,Info,SuiteInfo,Scope,PostInitHook,Config);
Error = {error,Reason} ->
case required_default('_UNDEF',Required,Info,
SuiteInfo,Scope) of
ok ->
- configure(Rest,Info,SuiteInfo,Scope,Config);
+ configure(Rest,Info,SuiteInfo,Scope,PostInitHook,Config);
_ ->
case lists:keymember(Required,2,SuiteInfo) of
true ->
@@ -471,14 +494,15 @@ configure([{require,Required}|Rest],Info,SuiteInfo,Scope,Config) ->
end
end
end;
-configure([{require,Name,Required}|Rest],Info,SuiteInfo,Scope,Config) ->
+configure([{require,Name,Required}|Rest],
+ Info,SuiteInfo,Scope,PostInitHook,Config) ->
case ct:require(Name,Required) of
ok ->
- configure(Rest,Info,SuiteInfo,Scope,Config);
+ configure(Rest,Info,SuiteInfo,Scope,PostInitHook,Config);
Error = {error,Reason} ->
case required_default(Name,Required,Info,SuiteInfo,Scope) of
ok ->
- configure(Rest,Info,SuiteInfo,Scope,Config);
+ configure(Rest,Info,SuiteInfo,Scope,PostInitHook,Config);
_ ->
case lists:keymember(Name,2,SuiteInfo) of
true ->
@@ -488,17 +512,24 @@ configure([{require,Name,Required}|Rest],Info,SuiteInfo,Scope,Config) ->
end
end
end;
-configure([{timetrap,off}|Rest],Info,SuiteInfo,Scope,Config) ->
- configure(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) ->
- {ok,[Config]}.
+configure([{timetrap,off}|Rest],Info,SuiteInfo,Scope,PostInitHook,Config) ->
+ configure(Rest,Info,SuiteInfo,Scope,PostInitHook,Config);
+configure([{timetrap,Time}|Rest],Info,SuiteInfo,Scope,PostInitHook,Config) ->
+ PostInitHook1 =
+ [{watchdog,fun() -> case test_server:get_timetrap_info() of
+ undefined ->
+ test_server:timetrap(Time);
+ _ ->
+ {error,already_set}
+ end
+ end} | PostInitHook],
+ configure(Rest,Info,SuiteInfo,Scope,PostInitHook1,Config);
+configure([{ct_hooks,Hook}|Rest],Info,SuiteInfo,Scope,PostInitHook,Config) ->
+ configure(Rest,Info,SuiteInfo,Scope,PostInitHook,[{ct_hooks,Hook}|Config]);
+configure([_|Rest],Info,SuiteInfo,Scope,PostInitHook,Config) ->
+ configure(Rest,Info,SuiteInfo,Scope,PostInitHook,Config);
+configure([],_,_,_,PostInitHook,Config) ->
+ {ok,PostInitHook,Config}.
%% the require element in Info may come from suite/0 and
%% should be scoped 'suite', or come from the group info
@@ -562,10 +593,8 @@ end_tc(Mod,Func,TCPid,Result,Args,Return) ->
%% in case Mod == ct_framework, lookup the suite name
Suite = get_suite_name(Mod, Args),
- case lists:keysearch(watchdog,1,Args) of
- {value,{watchdog,Dog}} -> test_server:timetrap_cancel(Dog);
- false -> ok
- end,
+ test_server:timetrap_cancel(),
+
%% save the testcase process pid so that it can be used
%% to look up the attached trace window later
case ct_util:get_testdata(interpret) of
@@ -633,7 +662,22 @@ end_tc(Mod,Func,TCPid,Result,Args,Return) ->
end,
ct_util:reset_silent_connections(),
-
+
+ %% reset the curr_tc state, or delete this TC from the list of
+ %% executing cases (if in a parallel group)
+ ClearCurrTC = fun(Running = [_,_|_]) ->
+ lists:keydelete(Func,2,Running);
+ ({_,{suite0_failed,_}}) ->
+ undefined;
+ ([{_,CurrTC}]) when CurrTC == Func ->
+ undefined;
+ (undefined) ->
+ undefined;
+ (Unexpected) ->
+ exit({error,{reset_curr_tc,{Mod,Func},Unexpected}})
+ end,
+ ct_util:update_testdata(curr_tc,ClearCurrTC),
+
case FinalResult of
{skip,{sequence_failed,_,_}} ->
%% ct_logs:init_tc is never called for a skipped test case
@@ -830,13 +874,13 @@ get_suite(Mod, all) ->
{'EXIT',_} ->
get_all(Mod, []);
GroupDefs when is_list(GroupDefs) ->
- case catch find_groups(Mod, all, all, GroupDefs) of
+ case catch ct_groups: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]]}];
ConfTests ->
- get_all(Mod, ConfTests)
+ get_all(Mod, ConfTests)
end;
_ ->
E = "Bad return value from "++atom_to_list(Mod)++":groups/0",
@@ -855,7 +899,7 @@ get_suite(Mod, Group={conf,Props,_Init,TCs,_End}) ->
{'EXIT',_} ->
[Group];
GroupDefs when is_list(GroupDefs) ->
- case catch find_groups(Mod, Name, TCs, GroupDefs) of
+ case catch ct_groups: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
@@ -870,12 +914,13 @@ get_suite(Mod, Group={conf,Props,_Init,TCs,_End}) ->
%% init/end functions for top groups will be executed
case catch ?val(name, element(2, hd(ConfTests))) of
Name -> % top group
- delete_subs(ConfTests, ConfTests);
+ ct_groups:delete_subs(ConfTests, ConfTests);
_ ->
[]
end;
false ->
- ConfTests1 = delete_subs(ConfTests, ConfTests),
+ ConfTests1 = ct_groups:delete_subs(ConfTests,
+ ConfTests),
case ?val(override, Props) of
undefined ->
ConfTests1;
@@ -884,9 +929,9 @@ get_suite(Mod, Group={conf,Props,_Init,TCs,_End}) ->
ORSpec ->
ORSpec1 = if is_tuple(ORSpec) -> [ORSpec];
true -> ORSpec end,
- search_and_override(ConfTests1,
- ORSpec1, Mod)
- end
+ ct_groups:search_and_override(ConfTests1,
+ ORSpec1, Mod)
+ end
end
end;
_ ->
@@ -930,234 +975,6 @@ get_all_cases1(_, []) ->
%%%-----------------------------------------------------------------
-find_groups(Mod, Name, TCs, GroupDefs) ->
- Found = find(Mod, Name, TCs, GroupDefs, [], GroupDefs, false),
- trim(Found).
-
-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;
-
-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)});
-
-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(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)});
-
-find(_Mod, _Name, _TCs, [], _Known, _Defs, false) ->
- ['$NOMATCH'];
-
-find(_Mod, _Name, _TCs, [], _Known, _Defs, _Found) ->
- [].
-
-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 = ?val(name, Props),
- [Conf || Conf = {conf,Props0,_,_,_} <- Confs,
- Name =/= ?val(name, Props0)].
-
-is_sub({conf,Props,_,_,_}=Conf, [{conf,_,_,Tests,_} | Confs]) ->
- Name = ?val(name, Props),
- case lists:any(fun({conf,Props0,_,_,_}) ->
- case ?val(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;
- false ->
- E = "Invalid group "++atom_to_list(Name)++
- " in "++atom_to_list(Mod)++":groups/0",
- 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,ExtraProps} =
- case erlang:function_exported(Mod,init_per_group,2) of
- true ->
- {{Mod,init_per_group},{Mod,end_per_group},[]};
- false ->
- ct_logs:log("TEST INFO", "init_per_group/2 and "
- "end_per_group/2 missing for group "
- "~p in ~p, using default.",
- [Name,Mod]),
- {{?MODULE,init_per_group},
- {?MODULE,end_per_group},
- [{suite,Mod}]}
- end,
- {conf,[{name,Name}|Props++ExtraProps],InitConf,TestSpec,EndConf}.
-
-%%%-----------------------------------------------------------------
-
get_all(Mod, ConfTests) ->
case catch apply(Mod, all, []) of
{'EXIT',_} ->
@@ -1172,133 +989,24 @@ get_all(Mod, ConfTests) ->
[{?MODULE,error_in_suite,[[{error,What}]]}];
SeqsAndTCs ->
%% expand group references in all() using ConfTests
- case catch expand_groups(SeqsAndTCs, ConfTests, Mod) of
+ case catch ct_groups:expand_groups(SeqsAndTCs,
+ ConfTests,
+ Mod) of
{error,_} = Error ->
[{?MODULE,error_in_suite,[[Error]]}];
Tests ->
- delete_subs(Tests, Tests)
+ ct_groups:delete_subs(Tests, Tests)
end
end;
Skip = {skip,_Reason} ->
Skip;
_ ->
Reason =
- list_to_atom("Bad return value from "++atom_to_list(Mod)++":all/0"),
+ list_to_atom("Bad return value from "++
+ atom_to_list(Mod)++":all/0"),
[{?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) ->
- expand_groups({group,Name,default,[]}, ConfTests, Mod);
-expand_groups({group,Name,default}, ConfTests, Mod) ->
- expand_groups({group,Name,default,[]}, ConfTests, Mod);
-expand_groups({group,Name,ORProps}, ConfTests, Mod) when is_list(ORProps) ->
- expand_groups({group,Name,ORProps,[]}, ConfTests, Mod);
-expand_groups({group,Name,ORProps,SubORSpec}, ConfTests, Mod) ->
- FindConf =
- fun(Conf = {conf,Props,Init,Ts,End}) ->
- case ?val(name, Props) of
- Name when ORProps == default ->
- [Conf];
- Name ->
- [{conf,[{name,Name}|ORProps],Init,Ts,End}];
- _ ->
- []
- end
- end,
- case lists:flatmap(FindConf, ConfTests) of
- [] ->
- throw({error,invalid_ref_msg(Name, Mod)});
- Matching when SubORSpec == [] ->
- Matching;
- Matching ->
- override_props(Matching, SubORSpec, Name,Mod)
- end;
-expand_groups(SeqOrTC, _ConfTests, _Mod) ->
- SeqOrTC.
-
-%% search deep for the matching conf test and modify it and any
-%% sub tests according to the override specification
-search_and_override([Conf = {conf,Props,Init,Tests,End}], ORSpec, Mod) ->
- Name = ?val(name, Props),
- case lists:keysearch(Name, 1, ORSpec) of
- {value,{Name,default}} ->
- [Conf];
- {value,{Name,ORProps}} ->
- [{conf,[{name,Name}|ORProps],Init,Tests,End}];
- {value,{Name,default,[]}} ->
- [Conf];
- {value,{Name,default,SubORSpec}} ->
- override_props([Conf], SubORSpec, Name,Mod);
- {value,{Name,ORProps,SubORSpec}} ->
- override_props([{conf,[{name,Name}|ORProps],
- Init,Tests,End}], SubORSpec, Name,Mod);
- _ ->
- [{conf,Props,Init,search_and_override(Tests,ORSpec,Mod),End}]
- end.
-
-%% Modify the Tests element according to the override specification
-override_props([{conf,Props,Init,Tests,End} | Confs], SubORSpec, Name,Mod) ->
- {Subs,SubORSpec1} = override_sub_props(Tests, [], SubORSpec, Mod),
- [{conf,Props,Init,Subs,End} | override_props(Confs, SubORSpec1, Name,Mod)];
-override_props([], [], _,_) ->
- [];
-override_props([], SubORSpec, Name,Mod) ->
- Es = [invalid_ref_msg(Name, element(1,Spec), Mod) || Spec <- SubORSpec],
- throw({error,Es}).
-
-override_sub_props([], New, ORSpec, _) ->
- {?rev(New),ORSpec};
-override_sub_props([T = {conf,Props,Init,Tests,End} | Ts],
- New, ORSpec, Mod) ->
- Name = ?val(name, Props),
- case lists:keysearch(Name, 1, ORSpec) of
- {value,Spec} -> % group found in spec
- Props1 =
- case element(2, Spec) of
- default -> Props;
- ORProps -> [{name,Name} | ORProps]
- end,
- case catch element(3, Spec) of
- Undef when Undef == [] ; 'EXIT' == element(1, Undef) ->
- override_sub_props(Ts, [{conf,Props1,Init,Tests,End} | New],
- lists:keydelete(Name, 1, ORSpec), Mod);
- SubORSpec when is_list(SubORSpec) ->
- case override_sub_props(Tests, [], SubORSpec, Mod) of
- {Subs,[]} ->
- override_sub_props(Ts, [{conf,Props1,Init,
- Subs,End} | New],
- lists:keydelete(Name, 1, ORSpec),
- Mod);
- {_,NonEmptySpec} ->
- Es = [invalid_ref_msg(Name, element(1, GrRef),
- Mod) || GrRef <- NonEmptySpec],
- throw({error,Es})
- end;
- BadGrSpec ->
- throw({error,{invalid_form,BadGrSpec}})
- end;
- _ -> % not a group in spec
- override_sub_props(Ts, [T | New], ORSpec, Mod)
- end;
-override_sub_props([TC | Ts], New, ORSpec, Mod) ->
- override_sub_props(Ts, [TC | New], ORSpec, Mod).
-
-invalid_ref_msg(Name, Mod) ->
- E = "Invalid reference to group "++
- atom_to_list(Name)++" in "++
- atom_to_list(Mod)++":all/0",
- list_to_atom(E).
-
-invalid_ref_msg(Name0, Name1, Mod) ->
- E = "Invalid reference to group "++
- atom_to_list(Name1)++" from "++atom_to_list(Name0)++
- " in "++atom_to_list(Mod)++":all/0",
- list_to_atom(E).
-
%%!============================================================
%%! The support for sequences by means of using sequences/0
%%! will be removed in OTP R15. The code below is only kept
@@ -1483,6 +1191,12 @@ report(What,Data) ->
end;
tests_done ->
ok;
+ severe_error ->
+ ct_event:sync_notify(#event{name=What,
+ node=node(),
+ data=Data}),
+ ct_util:set_testdata({What,Data}),
+ ok;
tc_start ->
%% Data = {{Suite,Func},LogFileName}
ct_event:sync_notify(#event{name=tc_logfile,
@@ -1634,5 +1348,5 @@ format_comment(Comment) ->
%%%-----------------------------------------------------------------
%%% @spec get_html_wrapper(TestName, PrintLabel, Cwd) -> Header
-get_html_wrapper(TestName, PrintLabel, Cwd) ->
- ct_logs:get_ts_html_wrapper(TestName, PrintLabel, Cwd).
+get_html_wrapper(TestName, PrintLabel, Cwd, TableCols) ->
+ ct_logs:get_ts_html_wrapper(TestName, PrintLabel, Cwd, TableCols).
diff --git a/lib/common_test/src/ct_ftp.erl b/lib/common_test/src/ct_ftp.erl
index 5db73066a3..8790393b36 100644
--- a/lib/common_test/src/ct_ftp.erl
+++ b/lib/common_test/src/ct_ftp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -66,6 +66,7 @@
%%% {unix,[{ftp,IpAddr},
%%% {username,Username},
%%% {password,Password}]}.</pre>
+%%% @see ct:require/2
put(KeyOrName,LocalFile,RemoteFile) ->
Fun = fun(Ftp) -> send(Ftp,LocalFile,RemoteFile) end,
open_and_do(KeyOrName,Fun).
@@ -85,6 +86,7 @@ put(KeyOrName,LocalFile,RemoteFile) ->
%%%
%%% <p>The config file must be as for put/3.</p>
%%% @see put/3
+%%% @see ct:require/2
get(KeyOrName,RemoteFile,LocalFile) ->
Fun = fun(Ftp) -> recv(Ftp,RemoteFile,LocalFile) end,
open_and_do(KeyOrName,Fun).
@@ -105,6 +107,10 @@ get(KeyOrName,RemoteFile,LocalFile) ->
%%% simply use <code>Key</code>, the configuration variable name, to
%%% specify the target. Note that a connection that has no associated target
%%% name can only be closed with the handle value.</p>
+%%%
+%%% <p>See <c>ct:require/2</c> for how to create a new <c>Name</c></p>
+%%%
+%%% @see ct:require/2
open(KeyOrName) ->
case ct_util:get_key_from_name(KeyOrName) of
{ok,node} ->
diff --git a/lib/common_test/src/ct_gen_conn.erl b/lib/common_test/src/ct_gen_conn.erl
index 5aab4dd2dd..1f01d84601 100644
--- a/lib/common_test/src/ct_gen_conn.erl
+++ b/lib/common_test/src/ct_gen_conn.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -27,7 +27,7 @@
-compile(export_all).
-export([start/4, stop/1]).
--export([call/2, do_within_time/2]).
+-export([call/2, call/3, return/2, do_within_time/2]).
-ifdef(debug).
-define(dbg,true).
@@ -39,17 +39,24 @@
name,
address,
init_data,
+ reconnect = true,
+ forward = false,
+ use_existing = true,
+ old = false,
conn_pid,
cb_state,
ct_util_server}).
%%%-----------------------------------------------------------------
-%%% @spec start(Name,Address,InitData,CallbackMod) ->
+%%% @spec start(Address,InitData,CallbackMod,Opts) ->
%%% {ok,Handle} | {error,Reason}
%%% Name = term()
%%% CallbackMod = atom()
%%% InitData = term()
%%% Address = term()
+%%% Opts = [Opt]
+%%% Opt = {name,Name} | {use_existing_connection,boolean()} |
+%%% {reconnect,boolean()} | {forward_messages,boolean()}
%%%
%%% @doc Open a connection and start the generic connection owner process.
%%%
@@ -60,50 +67,67 @@
%%% <code>InitData</code> and returna
%%% <code>{ok,ConnectionPid,State}</code> or
%%% <code>{error,Reason}</code>.</p>
+%%%
+%%% If no name is given, the <code>Name</code> argument in init/3 will
+%%% have the value <code>undefined</code>.
+%%%
+%%% The callback modules must also export
+%%% ```
+%%% handle_msg(Msg,From,State) -> {reply,Reply,State} |
+%%% {noreply,State} |
+%%% {stop,Reply,State}
+%%% terminate(ConnectionPid,State) -> term()
+%%% close(Handle) -> term()
+%%% '''
+%%%
+%%% The <code>close/1</code> callback function is actually a callback
+%%% for ct_util, for closing registered connections when
+%%% ct_util_server is terminated. <code>Handle</code> is the Pid of
+%%% the ct_gen_conn process.
+%%%
+%%% If option <code>reconnect</code> is <code>true</code>, then the
+%%% callback must also export
+%%% ```
+%%% reconnect(Address,State) -> {ok,ConnectionPid,State}
+%%% '''
+%%%
+%%% If option <code>forward_messages</code> is <ocde>true</code>, then
+%%% the callback must also export
+%%% ```
+%%% handle_msg(Msg,State) -> {noreply,State} | {stop,State}
+%%% '''
+%%%
+%%% An old interface still exists. This is used by ct_telnet, ct_ftp
+%%% and ct_ssh. The start function then has an explicit
+%%% <code>Name</code> argument, and no <code>Opts</code> argument. The
+%%% callback must export:
+%%%
+%%% ```
+%%% init(Name,Address,InitData) -> {ok,ConnectionPid,State}
+%%% handle_msg(Msg,State) -> {Reply,State}
+%%% reconnect(Address,State) -> {ok,ConnectionPid,State}
+%%% terminate(ConnectionPid,State) -> term()
+%%% close(Handle) -> term()
+%%% '''
+%%%
+start(Address,InitData,CallbackMod,Opts) when is_list(Opts) ->
+ do_start(Address,InitData,CallbackMod,Opts);
start(Name,Address,InitData,CallbackMod) ->
- case ct_util:does_connection_exist(Name,Address,CallbackMod) of
- {ok,Pid} ->
- log("ct_gen_conn:start","Using existing connection!\n",[]),
- {ok,Pid};
- false ->
- Self = self(),
- Pid = spawn(fun() ->
- init_gen(Self, #gen_opts{callback=CallbackMod,
- name=Name,
- address=Address,
- init_data=InitData})
- end),
- MRef = erlang:monitor(process,Pid),
- receive
- {connected,Pid} ->
- erlang:demonitor(MRef, [flush]),
- ct_util:register_connection(Name,Address,CallbackMod,Pid),
- {ok,Pid};
- {Error,Pid} ->
- receive {'DOWN',MRef,process,_,_} -> ok end,
- Error;
- {'DOWN',MRef,process,_,Reason} ->
- log("ct_gen_conn:start",
- "Connection process died: ~p\n",
- [Reason]),
- {error,{connection_process_died,Reason}}
- end
- end.
-
+ do_start(Address,InitData,CallbackMod,[{name,Name},{old,true}]).
%%%-----------------------------------------------------------------
%%% @spec stop(Handle) -> ok
%%% Handle = handle()
%%%
-%%% @doc Close the telnet connection and stop the process managing it.
+%%% @doc Close the connection and stop the process managing it.
stop(Pid) ->
- call(Pid,stop).
+ call(Pid,stop,5000).
%%%-----------------------------------------------------------------
%%% @spec log(Heading,Format,Args) -> ok
%%%
%%% @doc Log activities on the current connection (tool-internal use only).
-%%% @see ct_logs:log/3
+%%% @see ct_logs:log/3
log(Heading,Format,Args) ->
log(log,[Heading,Format,Args]).
@@ -111,7 +135,7 @@ log(Heading,Format,Args) ->
%%% @spec start_log(Heading) -> ok
%%%
%%% @doc Log activities on the current connection (tool-internal use only).
-%%% @see ct_logs:start_log/1
+%%% @see ct_logs:start_log/1
start_log(Heading) ->
log(start_log,[Heading]).
@@ -119,7 +143,7 @@ start_log(Heading) ->
%%% @spec cont_log(Format,Args) -> ok
%%%
%%% @doc Log activities on the current connection (tool-internal use only).
-%%% @see ct_logs:cont_log/2
+%%% @see ct_logs:cont_log/2
cont_log(Format,Args) ->
log(cont_log,[Format,Args]).
@@ -127,7 +151,7 @@ cont_log(Format,Args) ->
%%% @spec end_log() -> ok
%%%
%%% @doc Log activities on the current connection (tool-internal use only).
-%%% @see ct_logs:end_log/0
+%%% @see ct_logs:end_log/0
end_log() ->
log(end_log,[]).
@@ -148,10 +172,10 @@ do_within_time(Fun,Timeout) ->
Silent = get(silent),
TmpPid = spawn_link(fun() -> put(silent,Silent),
R = Fun(),
- Self ! {self(),R}
+ Self ! {self(),R}
end),
ConnPid = get(conn_pid),
- receive
+ receive
{TmpPid,Result} ->
Result;
{'EXIT',ConnPid,_Reason}=M ->
@@ -159,7 +183,7 @@ do_within_time(Fun,Timeout) ->
exit(TmpPid,kill),
self() ! M,
{error,connection_closed}
- after
+ after
Timeout ->
exit(TmpPid,kill),
receive
@@ -176,12 +200,66 @@ do_within_time(Fun,Timeout) ->
%%%=================================================================
%%% Internal functions
-call(Pid,Msg) ->
+do_start(Address,InitData,CallbackMod,Opts0) ->
+ Opts = check_opts(Opts0,#gen_opts{callback=CallbackMod,
+ address=Address,
+ init_data=InitData}),
+ case ct_util:does_connection_exist(Opts#gen_opts.name,
+ Address,CallbackMod) of
+ {ok,Pid} when Opts#gen_opts.use_existing ->
+ log("ct_gen_conn:start","Using existing connection!\n",[]),
+ {ok,Pid};
+ {ok,Pid} when not Opts#gen_opts.use_existing ->
+ {error,{connection_exists,Pid}};
+ false ->
+ do_start(Opts)
+ end.
+
+do_start(Opts) ->
+ Self = self(),
+ Pid = spawn(fun() -> init_gen(Self, Opts) end),
+ MRef = erlang:monitor(process,Pid),
+ receive
+ {connected,Pid} ->
+ erlang:demonitor(MRef, [flush]),
+ ct_util:register_connection(Opts#gen_opts.name, Opts#gen_opts.address,
+ Opts#gen_opts.callback, Pid),
+ {ok,Pid};
+ {Error,Pid} ->
+ receive {'DOWN',MRef,process,_,_} -> ok end,
+ Error;
+ {'DOWN',MRef,process,_,Reason} ->
+ log("ct_gen_conn:start",
+ "Connection process died: ~p\n",
+ [Reason]),
+ {error,{connection_process_died,Reason}}
+ end.
+
+check_opts(Opts0) ->
+ check_opts(Opts0,#gen_opts{}).
+
+check_opts([{name,Name}|T],Opts) ->
+ check_opts(T,Opts#gen_opts{name=Name});
+check_opts([{reconnect,Bool}|T],Opts) ->
+ check_opts(T,Opts#gen_opts{reconnect=Bool});
+check_opts([{forward_messages,Bool}|T],Opts) ->
+ check_opts(T,Opts#gen_opts{forward=Bool});
+check_opts([{use_existing_connection,Bool}|T],Opts) ->
+ check_opts(T,Opts#gen_opts{use_existing=Bool});
+check_opts([{old,Bool}|T],Opts) ->
+ check_opts(T,Opts#gen_opts{old=Bool});
+check_opts([],Opts) ->
+ Opts.
+
+call(Pid, Msg) ->
+ call(Pid, Msg, infinity).
+
+call(Pid, Msg, Timeout) ->
MRef = erlang:monitor(process,Pid),
Ref = make_ref(),
Pid ! {Msg,{self(),Ref}},
receive
- {Ref, Result} ->
+ {Ref, Result} ->
erlang:demonitor(MRef, [flush]),
case Result of
{retry,_Data} ->
@@ -189,8 +267,15 @@ call(Pid,Msg) ->
Other ->
Other
end;
- {'DOWN',MRef,process,_,Reason} ->
+ {'DOWN',MRef,process,_,Reason} ->
{error,{process_down,Pid,Reason}}
+ after Timeout ->
+ erlang:demonitor(MRef, [flush]),
+ log("ct_gen_conn",
+ "Connection process ~p not responding. Killing now!",
+ [Pid]),
+ exit(Pid, kill),
+ {error,{process_down,Pid,forced_termination}}
end.
return({To,Ref},Result) ->
@@ -198,36 +283,47 @@ return({To,Ref},Result) ->
init_gen(Parent,Opts) ->
process_flag(trap_exit,true),
- CtUtilServer = whereis(ct_util_server),
- link(CtUtilServer),
put(silent,false),
- case catch (Opts#gen_opts.callback):init(Opts#gen_opts.name,
- Opts#gen_opts.address,
- Opts#gen_opts.init_data) of
+ try (Opts#gen_opts.callback):init(Opts#gen_opts.name,
+ Opts#gen_opts.address,
+ Opts#gen_opts.init_data) of
{ok,ConnPid,State} when is_pid(ConnPid) ->
link(ConnPid),
put(conn_pid,ConnPid),
+ CtUtilServer = whereis(ct_util_server),
+ link(CtUtilServer),
Parent ! {connected,self()},
loop(Opts#gen_opts{conn_pid=ConnPid,
cb_state=State,
ct_util_server=CtUtilServer});
{error,Reason} ->
Parent ! {{error,Reason},self()}
+ catch
+ throw:{error,Reason} ->
+ Parent ! {{error,Reason},self()}
end.
loop(Opts) ->
receive
{'EXIT',Pid,Reason} when Pid==Opts#gen_opts.conn_pid ->
- log("Connection down!\nOpening new!","Reason: ~p\nAddress: ~p\n",
- [Reason,Opts#gen_opts.address]),
- case reconnect(Opts) of
- {ok, NewPid, NewState} ->
- link(NewPid),
- put(conn_pid,NewPid),
- loop(Opts#gen_opts{conn_pid=NewPid,cb_state=NewState});
- Error ->
+ case Opts#gen_opts.reconnect of
+ true ->
+ log("Connection down!\nOpening new!",
+ "Reason: ~p\nAddress: ~p\n",
+ [Reason,Opts#gen_opts.address]),
+ case reconnect(Opts) of
+ {ok, NewPid, NewState} ->
+ link(NewPid),
+ put(conn_pid,NewPid),
+ loop(Opts#gen_opts{conn_pid=NewPid,cb_state=NewState});
+ Error ->
+ ct_util:unregister_connection(self()),
+ log("Reconnect failed. Giving up!","Reason: ~p\n",
+ [Error])
+ end;
+ false ->
ct_util:unregister_connection(self()),
- log("Reconnect failed. Giving up!","Reason: ~p\n",[Error])
+ log("Connection closed!","Reason: ~p\n",[Reason])
end;
{'EXIT',Pid,Reason} ->
case Opts#gen_opts.ct_util_server of
@@ -252,24 +348,40 @@ loop(Opts) ->
loop(Opts);
{{retry,{_Error,_Name,_CPid,Msg}}, From} ->
log("Rerunning command","Connection reestablished. Rerunning command...",[]),
- {Return,NewState} =
+ {Return,NewState} =
(Opts#gen_opts.callback):handle_msg(Msg,Opts#gen_opts.cb_state),
return(From, Return),
- loop(Opts#gen_opts{cb_state=NewState});
- {Msg,From={Pid,_Ref}} when is_pid(Pid) ->
- {Return,NewState} =
+ loop(Opts#gen_opts{cb_state=NewState});
+ {Msg,From={Pid,_Ref}} when is_pid(Pid), Opts#gen_opts.old==true ->
+ {Return,NewState} =
(Opts#gen_opts.callback):handle_msg(Msg,Opts#gen_opts.cb_state),
return(From, Return),
- loop(Opts#gen_opts{cb_state=NewState})
+ loop(Opts#gen_opts{cb_state=NewState});
+ {Msg,From={Pid,_Ref}} when is_pid(Pid) ->
+ case (Opts#gen_opts.callback):handle_msg(Msg,From,
+ Opts#gen_opts.cb_state) of
+ {reply,Reply,NewState} ->
+ return(From,Reply),
+ loop(Opts#gen_opts{cb_state=NewState});
+ {noreply,NewState} ->
+ loop(Opts#gen_opts{cb_state=NewState});
+ {stop,Reply,NewState} ->
+ ct_util:unregister_connection(self()),
+ (Opts#gen_opts.callback):terminate(Opts#gen_opts.conn_pid,
+ NewState),
+ return(From,Reply)
+ end;
+ Msg when Opts#gen_opts.forward==true ->
+ case (Opts#gen_opts.callback):handle_msg(Msg,Opts#gen_opts.cb_state) of
+ {noreply,NewState} ->
+ loop(Opts#gen_opts{cb_state=NewState});
+ {stop,NewState} ->
+ ct_util:unregister_connection(self()),
+ (Opts#gen_opts.callback):terminate(Opts#gen_opts.conn_pid,
+ NewState)
+ end
end.
-nozero({ok,S}) when is_list(S) ->
- {ok,[C || C <- S,
- C=/=0,
- C=/=13]};
-nozero(M) ->
- M.
-
reconnect(Opts) ->
(Opts#gen_opts.callback):reconnect(Opts#gen_opts.address,
Opts#gen_opts.cb_state).
@@ -277,10 +389,8 @@ reconnect(Opts) ->
log(Func,Args) ->
case get(silent) of
- true when not ?dbg->
+ true when not ?dbg->
ok;
_ ->
apply(ct_logs,Func,Args)
end.
-
-
diff --git a/lib/common_test/src/ct_groups.erl b/lib/common_test/src/ct_groups.erl
new file mode 100644
index 0000000000..74ab5e5439
--- /dev/null
+++ b/lib/common_test/src/ct_groups.erl
@@ -0,0 +1,599 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2004-2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, 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 callback module.
+%%%
+%%% <p>This module contains CT internal help functions for searching
+%%% through groups specification trees and producing resulting
+%%% tests.</p>
+
+-module(ct_groups).
+
+-export([find_groups/4]).
+-export([make_all_conf/3, make_all_conf/4, make_conf/5]).
+-export([delete_subs/2]).
+-export([expand_groups/3, search_and_override/3]).
+
+-define(val(Key, List), proplists:get_value(Key, List)).
+-define(val(Key, List, Def), proplists:get_value(Key, List, Def)).
+-define(rev(L), lists:reverse(L)).
+
+find_groups(Mod, GrNames, TCs, GroupDefs) when is_atom(GrNames) ;
+ (length(GrNames) == 1) ->
+ find_groups1(Mod, GrNames, TCs, GroupDefs);
+
+find_groups(Mod, Groups, TCs, GroupDefs) when Groups /= [] ->
+ lists:append([find_groups1(Mod, [GrNames], TCs, GroupDefs) ||
+ GrNames <- Groups]);
+
+find_groups(_Mod, [], _TCs, _GroupDefs) ->
+ [].
+
+%% GrNames == atom(): Single group name, perform full search
+%% GrNames == list(): List of groups, find all matching paths
+%% GrNames == [list()]: Search path terminated by last group in GrNames
+find_groups1(Mod, GrNames, TCs, GroupDefs) ->
+ {GrNames1,FindAll} =
+ case GrNames of
+ Name when is_atom(Name), Name /= all ->
+ {[Name],true};
+ [Path] when is_list(Path) ->
+ {Path,false};
+ Path ->
+ {Path,true}
+ end,
+ TCs1 = if (is_atom(TCs) and (TCs /= all)) or is_tuple(TCs) ->
+ [TCs];
+ true ->
+ TCs
+ end,
+ Found = find(Mod, GrNames1, TCs1, GroupDefs, [],
+ GroupDefs, FindAll),
+ [Conf || Conf <- Found, Conf /= 'NOMATCH'].
+
+%% Locate all groups
+find(Mod, all, all, [{Name,Props,Tests} | Gs], Known, Defs, _)
+ when is_atom(Name), is_list(Props), is_list(Tests) ->
+ cyclic_test(Mod, Name, Known),
+ trim(make_conf(Mod, Name, Props,
+ find(Mod, all, all, Tests, [Name | Known],
+ Defs, true))) ++
+ find(Mod, all, all, Gs, Known, Defs, true);
+
+%% Locate particular TCs in all groups
+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),
+ Tests1 = rm_unwanted_tcs(Tests, TCs, []),
+ trim(make_conf(Mod, Name, Props,
+ find(Mod, all, TCs, Tests1, [Name | Known],
+ Defs, true))) ++
+ find(Mod, all, TCs, Gs, Known, Defs, true);
+
+%% Found next group is in search path
+find(Mod, [Name|GrNames]=SPath, TCs, [{Name,Props,Tests} | Gs], Known,
+ Defs, FindAll) when is_atom(Name), is_list(Props), is_list(Tests) ->
+ cyclic_test(Mod, Name, Known),
+ Tests1 = rm_unwanted_tcs(Tests, TCs, GrNames),
+ trim(make_conf(Mod, Name, Props,
+ find(Mod, GrNames, TCs, Tests1, [Name|Known],
+ Defs, FindAll))) ++
+ find(Mod, SPath, TCs, Gs, Known, Defs, FindAll);
+
+%% Group path terminated, stop the search
+find(Mod, [], TCs, Tests, _Known, _Defs, false) ->
+ Cases = lists:flatmap(fun(TC) when is_atom(TC), TCs == all ->
+ [{Mod,TC}];
+ ({group,_}) ->
+ [];
+ ({_,_}=TC) when TCs == all ->
+ [TC];
+ (TC) ->
+ if is_atom(TC) ->
+ Tuple = {Mod,TC},
+ case lists:member(Tuple, TCs) of
+ true ->
+ [Tuple];
+ false ->
+ case lists:member(TC, TCs) of
+ true -> [{Mod,TC}];
+ false -> []
+ end
+ end;
+ true ->
+ []
+ end
+ end, Tests),
+ if Cases == [] -> ['NOMATCH'];
+ true -> Cases
+ end;
+
+%% No more groups
+find(_Mod, [_|_], _TCs, [], _Known, _Defs, _) ->
+ ['NOMATCH'];
+
+%% Found group not next in search path
+find(Mod, GrNames, TCs, [{Name,Props,Tests} | Gs], Known,
+ Defs, FindAll) when is_atom(Name), is_list(Props), is_list(Tests) ->
+ cyclic_test(Mod, Name, Known),
+ Tests1 = rm_unwanted_tcs(Tests, TCs, GrNames),
+ trim(make_conf(Mod, Name, Props,
+ find(Mod, GrNames, TCs, Tests1, [Name|Known],
+ Defs, FindAll))) ++
+ find(Mod, GrNames, TCs, Gs, Known, Defs, FindAll);
+
+%% A nested group defined on top level found
+find(Mod, GrNames, TCs, [{group,Name1} | Gs], Known, Defs, FindAll)
+ when is_atom(Name1) ->
+ find(Mod, GrNames, TCs, [expand(Mod, Name1, Defs) | Gs], Known,
+ Defs, FindAll);
+
+%% Undocumented remote group feature, use with caution
+find(Mod, GrNames, TCs, [{group, ExtMod, ExtGrp} | Gs], Known,
+ Defs, FindAll) when is_atom(ExtMod), is_atom(ExtGrp) ->
+ ExternalDefs = ExtMod:groups(),
+ ExternalTCs = find(ExtMod, ExtGrp, TCs, [{group, ExtGrp}],
+ [], ExternalDefs, FindAll),
+ ExternalTCs ++ find(Mod, GrNames, TCs, Gs, Known, Defs, FindAll);
+
+%% Group definition without properties, add an empty property list
+find(Mod, GrNames, TCs, [{Name1,Tests} | Gs], Known, Defs, FindAll)
+ when is_atom(Name1), is_list(Tests) ->
+ find(Mod, GrNames, TCs, [{Name1,[],Tests} | Gs], Known, Defs, FindAll);
+
+%% Save, and keep searching
+find(Mod, GrNames, TCs, [{ExternalTC, Case} = TC | Gs], Known,
+ Defs, FindAll) when is_atom(ExternalTC),
+ is_atom(Case) ->
+ [TC | find(Mod, GrNames, TCs, Gs, Known, Defs, FindAll)];
+
+%% Save test case
+find(Mod, GrNames, all, [TC | Gs], Known,
+ Defs, FindAll) when is_atom(TC) ->
+ [{Mod,TC} | find(Mod, GrNames, all, Gs, Known, Defs, FindAll)];
+
+%% Save test case
+find(Mod, GrNames, all, [{M,TC} | Gs], Known,
+ Defs, FindAll) when is_atom(M), M /= group, is_atom(TC) ->
+ [{M,TC} | find(Mod, GrNames, all, Gs, Known, Defs, FindAll)];
+
+%% Check if test case should be saved
+find(Mod, GrNames, TCs, [TC | Gs], Known,
+ Defs, FindAll) when is_atom(TC) orelse
+ ((size(TC) == 2) and (element(1,TC) /= group)) ->
+ Case =
+ if is_atom(TC) ->
+ Tuple = {Mod,TC},
+ case lists:member(Tuple, TCs) of
+ true ->
+ Tuple;
+ false ->
+ case lists:member(TC, TCs) of
+ true -> {Mod,TC};
+ false -> []
+ end
+ end;
+ true ->
+ case lists:member(TC, TCs) of
+ true -> {Mod,TC};
+ false -> []
+ end
+ end,
+ if Case == [] ->
+ find(Mod, GrNames, TCs, Gs, Known, Defs, FindAll);
+ true ->
+ [Case | find(Mod, GrNames, TCs, Gs, Known, Defs, FindAll)]
+ end;
+
+%% Unexpeted term in group list
+find(Mod, _GrNames, _TCs, [BadTerm | _Gs], Known, _Defs, _FindAll) ->
+ Where = if length(Known) == 0 ->
+ atom_to_list(Mod)++":groups/0";
+ true ->
+ "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)});
+
+%% No more groups
+find(_Mod, _GrNames, _TCs, [], _Known, _Defs, _) ->
+ [].
+
+%%%-----------------------------------------------------------------
+
+%% We have to always search bottom up to only remove a branch
+%% if there's 'NOMATCH' in the leaf (otherwise, the branch should
+%% be kept)
+
+trim({conf,Props,Init,Tests,End}) ->
+ try trim(Tests) of
+ [] -> [];
+ Tests1 -> [{conf,Props,Init,Tests1,End}]
+ catch
+ throw:_ -> []
+ end;
+
+trim(Tests) when is_list(Tests) ->
+ %% we need to compare the result of trimming each test on this
+ %% level, and only let a 'NOMATCH' fail the level if no
+ %% successful sub group can be found
+ Tests1 =
+ lists:flatmap(fun(Test) ->
+ IsConf = case Test of
+ {conf,_,_,_,_} ->
+ true;
+ _ ->
+ false
+ end,
+ try trim_test(Test) of
+ [] -> [];
+ Test1 when IsConf -> [{conf,Test1}];
+ Test1 -> [Test1]
+ catch
+ throw:_ -> ['NOMATCH']
+ end
+ end, Tests),
+ case lists:keymember(conf, 1, Tests1) of
+ true -> % at least one successful group
+ lists:flatmap(fun({conf,Test}) -> [Test];
+ ('NOMATCH') -> []; % ignore any 'NOMATCH'
+ (Test) -> [Test]
+ end, Tests1);
+ false ->
+ case lists:member('NOMATCH', Tests1) of
+ true ->
+ throw('NOMATCH');
+ false ->
+ Tests1
+ end
+ end.
+
+trim_test({conf,Props,Init,Tests,End}) ->
+ case trim(Tests) of
+ [] ->
+ [];
+ Tests1 ->
+ {conf,Props,Init,Tests1,End}
+ end;
+
+trim_test('NOMATCH') ->
+ throw('NOMATCH');
+
+trim_test(Test) ->
+ Test.
+
+%% GrNames is [] if the terminating group has been found. From
+%% that point, all specified test should be included (as well as
+%% sub groups for deeper search).
+rm_unwanted_tcs(Tests, all, []) ->
+ Tests;
+
+rm_unwanted_tcs(Tests, TCs, []) ->
+ sort_tests(lists:flatmap(fun(Test) when is_tuple(Test),
+ (size(Test) > 2) ->
+ [Test];
+ (Test={group,_}) ->
+ [Test];
+ (Test={_M,TC}) ->
+ case lists:member(TC, TCs) of
+ true -> [Test];
+ false -> []
+ end;
+ (Test) when is_atom(Test) ->
+ case lists:keysearch(Test, 2, TCs) of
+ {value,_} ->
+ [Test];
+ _ ->
+ case lists:member(Test, TCs) of
+ true -> [Test];
+ false -> []
+ end
+ end;
+ (Test) -> [Test]
+ end, Tests), TCs);
+
+rm_unwanted_tcs(Tests, _TCs, _) ->
+ [Test || Test <- Tests, not is_atom(Test)].
+
+%% make sure the order of tests is according to the order in TCs
+sort_tests(Tests, TCs) when is_list(TCs)->
+ lists:sort(fun(T1, T2) ->
+ case {is_tc(T1),is_tc(T2)} of
+ {true,true} ->
+ (position(T1, TCs) =<
+ position(T2, TCs));
+ {false,true} ->
+ (position(T2, TCs) == (length(TCs)+1));
+ _ -> true
+
+ end
+ end, Tests);
+sort_tests(Tests, _) ->
+ Tests.
+
+is_tc(T) when is_atom(T) -> true;
+is_tc({group,_}) -> false;
+is_tc({_M,T}) when is_atom(T) -> true;
+is_tc(_) -> false.
+
+position(T, TCs) ->
+ position(T, TCs, 1).
+
+position(T, [T|_TCs], Pos) ->
+ Pos;
+position(T, [{_,T}|_TCs], Pos) ->
+ Pos;
+position({M,T}, [T|_TCs], Pos) when M /= group ->
+ Pos;
+position(T, [_|TCs], Pos) ->
+ position(T, TCs, Pos+1);
+position(_, [], Pos) ->
+ Pos.
+
+%%%-----------------------------------------------------------------
+
+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 = ?val(name, Props),
+ [Conf || Conf = {conf,Props0,_,_,_} <- Confs,
+ Name =/= ?val(name, Props0)].
+
+is_sub({conf,Props,_,_,_}=Conf, [{conf,_,_,Tests,_} | Confs]) ->
+ Name = ?val(name, Props),
+ case lists:any(fun({conf,Props0,_,_,_}) ->
+ case ?val(name, Props0) of
+ N when N == Name ->
+ true;
+ _ ->
+ false
+ end;
+ (_) ->
+ false
+ end, Tests) of
+ true ->
+ true;
+ false ->
+ is_sub(Conf, Tests) orelse is_sub(Conf, Confs)
+ end;
+
+is_sub(Conf, [_TC | Tests]) ->
+ is_sub(Conf, Tests);
+
+is_sub(_Conf, []) ->
+ false.
+
+
+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;
+ false ->
+ E = "Invalid group "++atom_to_list(Name)++
+ " in "++atom_to_list(Mod)++":groups/0",
+ throw({error,list_to_atom(E)})
+ end.
+
+make_all_conf(Dir, Mod, Props, TestSpec) ->
+ case code:is_loaded(Mod) of
+ false ->
+ code:load_abs(filename:join(Dir,atom_to_list(Mod)));
+ _ ->
+ ok
+ end,
+ make_all_conf(Mod, Props, TestSpec).
+
+make_all_conf(Mod, Props, TestSpec) ->
+ case catch apply(Mod, groups, []) of
+ {'EXIT',_} ->
+ exit({invalid_group_definition,Mod});
+ GroupDefs when is_list(GroupDefs) ->
+ case catch find_groups(Mod, all, TestSpec, GroupDefs) of
+ {error,_} = Error ->
+ %% this makes test_server call error_in_suite as first
+ %% (and only) test case so we can report Error properly
+ [{ct_framework,error_in_suite,[[Error]]}];
+ [] ->
+ exit({invalid_group_spec,Mod});
+ _ConfTests ->
+ make_conf(Mod, all, Props, TestSpec)
+ 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,ExtraProps} =
+ case erlang:function_exported(Mod,init_per_group,2) of
+ true ->
+ {{Mod,init_per_group},{Mod,end_per_group},[]};
+ false ->
+ ct_logs:log("TEST INFO", "init_per_group/2 and "
+ "end_per_group/2 missing for group "
+ "~p in ~p, using default.",
+ [Name,Mod]),
+ {{ct_framework,init_per_group},
+ {ct_framework,end_per_group},
+ [{suite,Mod}]}
+ end,
+ {conf,[{name,Name}|Props++ExtraProps],InitConf,TestSpec,EndConf}.
+
+%%%-----------------------------------------------------------------
+
+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) ->
+ expand_groups({group,Name,default,[]}, ConfTests, Mod);
+expand_groups({group,Name,default}, ConfTests, Mod) ->
+ expand_groups({group,Name,default,[]}, ConfTests, Mod);
+expand_groups({group,Name,ORProps}, ConfTests, Mod) when is_list(ORProps) ->
+ expand_groups({group,Name,ORProps,[]}, ConfTests, Mod);
+expand_groups({group,Name,ORProps,SubORSpec}, ConfTests, Mod) ->
+ FindConf =
+ fun(Conf = {conf,Props,Init,Ts,End}) ->
+ case ?val(name, Props) of
+ Name when ORProps == default ->
+ [Conf];
+ Name ->
+ Props1 = case ?val(suite, Props) of
+ undefined ->
+ ORProps;
+ SuiteName ->
+ [{suite,SuiteName}|ORProps]
+ end,
+ [{conf,[{name,Name}|Props1],Init,Ts,End}];
+ _ ->
+ []
+ end
+ end,
+ case lists:flatmap(FindConf, ConfTests) of
+ [] ->
+ throw({error,invalid_ref_msg(Name, Mod)});
+ Matching when SubORSpec == [] ->
+ Matching;
+ Matching ->
+ override_props(Matching, SubORSpec, Name,Mod)
+ end;
+expand_groups(SeqOrTC, _ConfTests, _Mod) ->
+ SeqOrTC.
+
+%% search deep for the matching conf test and modify it and any
+%% sub tests according to the override specification
+search_and_override([Conf = {conf,Props,Init,Tests,End}], ORSpec, Mod) ->
+ InsProps = fun(GrName, undefined, Ps) ->
+ [{name,GrName} | Ps];
+ (GrName, Suite, Ps) ->
+ [{name,GrName}, {suite,Suite} | Ps]
+ end,
+ Name = ?val(name, Props),
+ Suite = ?val(suite, Props),
+ case lists:keysearch(Name, 1, ORSpec) of
+ {value,{Name,default}} ->
+ [Conf];
+ {value,{Name,ORProps}} ->
+ [{conf,InsProps(Name,Suite,ORProps),Init,Tests,End}];
+ {value,{Name,default,[]}} ->
+ [Conf];
+ {value,{Name,default,SubORSpec}} ->
+ override_props([Conf], SubORSpec, Name,Mod);
+ {value,{Name,ORProps,SubORSpec}} ->
+ override_props([{conf,InsProps(Name,Suite,ORProps),
+ Init,Tests,End}], SubORSpec, Name,Mod);
+ _ ->
+ [{conf,Props,Init,search_and_override(Tests,ORSpec,Mod),End}]
+ end.
+
+%% Modify the Tests element according to the override specification
+override_props([{conf,Props,Init,Tests,End} | Confs], SubORSpec, Name,Mod) ->
+ {Subs,SubORSpec1} = override_sub_props(Tests, [], SubORSpec, Mod),
+ [{conf,Props,Init,Subs,End} | override_props(Confs, SubORSpec1, Name,Mod)];
+override_props([], [], _,_) ->
+ [];
+override_props([], SubORSpec, Name,Mod) ->
+ Es = [invalid_ref_msg(Name, element(1,Spec), Mod) || Spec <- SubORSpec],
+ throw({error,Es}).
+
+override_sub_props([], New, ORSpec, _) ->
+ {?rev(New),ORSpec};
+override_sub_props([T = {conf,Props,Init,Tests,End} | Ts],
+ New, ORSpec, Mod) ->
+ Name = ?val(name, Props),
+ Suite = ?val(suite, Props),
+ case lists:keysearch(Name, 1, ORSpec) of
+ {value,Spec} -> % group found in spec
+ Props1 =
+ case element(2, Spec) of
+ default -> Props;
+ ORProps when Suite == undefined -> [{name,Name} | ORProps];
+ ORProps -> [{name,Name}, {suite,Suite} | ORProps]
+ end,
+ case catch element(3, Spec) of
+ Undef when Undef == [] ; 'EXIT' == element(1, Undef) ->
+ override_sub_props(Ts, [{conf,Props1,Init,Tests,End} | New],
+ lists:keydelete(Name, 1, ORSpec), Mod);
+ SubORSpec when is_list(SubORSpec) ->
+ case override_sub_props(Tests, [], SubORSpec, Mod) of
+ {Subs,[]} ->
+ override_sub_props(Ts, [{conf,Props1,Init,
+ Subs,End} | New],
+ lists:keydelete(Name, 1, ORSpec),
+ Mod);
+ {_,NonEmptySpec} ->
+ Es = [invalid_ref_msg(Name, element(1, GrRef),
+ Mod) || GrRef <- NonEmptySpec],
+ throw({error,Es})
+ end;
+ BadGrSpec ->
+ throw({error,{invalid_form,BadGrSpec}})
+ end;
+ _ -> % not a group in spec
+ override_sub_props(Ts, [T | New], ORSpec, Mod)
+ end;
+override_sub_props([TC | Ts], New, ORSpec, Mod) ->
+ override_sub_props(Ts, [TC | New], ORSpec, Mod).
+
+invalid_ref_msg(Name, Mod) ->
+ E = "Invalid reference to group "++
+ atom_to_list(Name)++" in "++
+ atom_to_list(Mod)++":all/0",
+ list_to_atom(E).
+
+invalid_ref_msg(Name0, Name1, Mod) ->
+ E = "Invalid reference to group "++
+ atom_to_list(Name1)++" from "++atom_to_list(Name0)++
+ " in "++atom_to_list(Mod)++":all/0",
+ list_to_atom(E).
diff --git a/lib/common_test/src/ct_hooks.erl b/lib/common_test/src/ct_hooks.erl
index 0fe6e03079..1bcc63738e 100644
--- a/lib/common_test/src/ct_hooks.erl
+++ b/lib/common_test/src/ct_hooks.erl
@@ -48,7 +48,7 @@
%% @doc Called before any suites are started
-spec init(State :: term()) -> ok |
- {error, Reason :: term()}.
+ {fail, Reason :: term()}.
init(Opts) ->
call(get_new_hooks(Opts, undefined) ++ get_builtin_hooks(Opts),
ok, init, []).
@@ -192,12 +192,12 @@ call([{Hook, call_id, NextFun} | Rest], Config, Meta, Hooks) ->
case lists:keyfind(NewId, #ct_hook_config.id, Hooks) of
false when NextFun =:= undefined ->
{Hooks ++ [NewHook],
- [{NewId, call_init} | Rest]};
+ Rest ++ [{NewId, call_init}]};
ExistingHook when is_tuple(ExistingHook) ->
{Hooks, Rest};
_ ->
{Hooks ++ [NewHook],
- [{NewId, call_init}, {NewId,NextFun} | Rest]}
+ Rest ++ [{NewId, call_init}, {NewId,NextFun}]}
end,
call(resort(NewRest,NewHooks,Meta), Config, Meta, NewHooks)
catch Error:Reason ->
@@ -353,11 +353,10 @@ pos(Id,[_|Rest],Num) ->
pos(Id,Rest,Num+1).
-
catch_apply(M,F,A, Default) ->
try
apply(M,F,A)
- catch error:Reason ->
+ catch _: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 ->
diff --git a/lib/common_test/src/ct_logs.erl b/lib/common_test/src/ct_logs.erl
index 1ccbdc3718..0b7a8bb075 100644
--- a/lib/common_test/src/ct_logs.erl
+++ b/lib/common_test/src/ct_logs.erl
@@ -28,23 +28,25 @@
-module(ct_logs).
--export([init/1,close/2,init_tc/1,end_tc/1]).
--export([get_log_dir/0,get_log_dir/1]).
--export([log/3,start_log/1,cont_log/2,end_log/0]).
--export([set_stylesheet/2,clear_stylesheet/1]).
--export([add_external_logs/1,add_link/3]).
+-export([init/2, close/2, init_tc/1, end_tc/1]).
+-export([get_log_dir/0, get_log_dir/1]).
+-export([log/3, start_log/1, cont_log/2, end_log/0]).
+-export([set_stylesheet/2, clear_stylesheet/1]).
+-export([add_external_logs/1, add_link/3]).
-export([make_last_run_index/0]).
-export([make_all_suites_index/1,make_all_runs_index/1]).
--export([get_ts_html_wrapper/3]).
--export([xhtml/2, locate_default_css_file/0, make_relative/1]).
+-export([get_ts_html_wrapper/4]).
+-export([xhtml/2, locate_priv_file/1, make_relative/1]).
+-export([insert_javascript/1]).
%% Logging stuff directly from testcase
--export([tc_log/3,tc_log/4,tc_log_async/3,tc_print/3,tc_pal/3,ct_log/3,
- basic_html/0]).
+-export([tc_log/3, tc_log/4, tc_log_async/3, tc_print/3, tc_print/4,
+ tc_pal/3, tc_pal/4, ct_log/3, basic_html/0]).
%% Simulate logger process for use without ct environment running
-export([simulate/0]).
+-include("ct.hrl").
-include("ct_event.hrl").
-include("ct_util.hrl").
-include_lib("kernel/include/file.hrl").
@@ -56,7 +58,6 @@
-define(all_runs_name, "all_runs.html").
-define(index_name, "index.html").
-define(totals_name, "totals.info").
--define(css_default, "ct_default.css").
-define(table_color1,"#ADD8E6").
-define(table_color2,"#E4F0FE").
@@ -79,9 +80,9 @@
%%% started. A new directory named ct_run.&lt;timestamp&gt; is created
%%% and all logs are stored under this directory.</p>
%%%
-init(Mode) ->
+init(Mode, Verbosity) ->
Self = self(),
- Pid = spawn_link(fun() -> logger(Self,Mode) end),
+ Pid = spawn_link(fun() -> logger(Self, Mode, Verbosity) end),
MRef = erlang:monitor(process,Pid),
receive
{started,Pid,Result} ->
@@ -240,7 +241,7 @@ end_tc(TCPid) ->
%%% activity it is. <code>Format</code> and <code>Args</code> is the
%%% data to log (as in <code>io:format(Format,Args)</code>).</p>
log(Heading,Format,Args) ->
- cast({log,sync,self(),group_leader(),
+ cast({log,sync,self(),group_leader(),ct_internal,?MAX_IMPORTANCE,
[{int_header(),[log_timestamp(now()),Heading]},
{Format,Args},
{int_footer(),[]}]}),
@@ -262,7 +263,7 @@ log(Heading,Format,Args) ->
%%% @see cont_log/2
%%% @see end_log/0
start_log(Heading) ->
- cast({log,sync,self(),group_leader(),
+ cast({log,sync,self(),group_leader(),ct_internal,?MAX_IMPORTANCE,
[{int_header(),[log_timestamp(now()),Heading]}]}),
ok.
@@ -277,7 +278,8 @@ cont_log([],[]) ->
ok;
cont_log(Format,Args) ->
maybe_log_timestamp(),
- cast({log,sync,self(),group_leader(),[{Format,Args}]}),
+ cast({log,sync,self(),group_leader(),ct_internal,?MAX_IMPORTANCE,
+ [{Format,Args}]}),
ok.
%%%-----------------------------------------------------------------
@@ -288,7 +290,8 @@ cont_log(Format,Args) ->
%%% @see start_log/1
%%% @see cont_log/2
end_log() ->
- cast({log,sync,self(),group_leader(),[{int_footer(), []}]}),
+ cast({log,sync,self(),group_leader(),ct_internal,?MAX_IMPORTANCE,
+ [{int_footer(), []}]}),
ok.
@@ -321,10 +324,16 @@ add_link(Heading,File,Type) ->
[filename:join("log_private",File),Type,File]).
-
%%%-----------------------------------------------------------------
%%% @spec tc_log(Category,Format,Args) -> ok
+%%% @equiv tc_log(Category,?STD_IMPORTANCE,Format,Args)
+tc_log(Category,Format,Args) ->
+ tc_log(Category,?STD_IMPORTANCE,Format,Args).
+
+%%%-----------------------------------------------------------------
+%%% @spec tc_log(Category,Importance,Format,Args) -> ok
%%% Category = atom()
+%%% Importance = integer()
%%% Format = string()
%%% Args = list()
%%%
@@ -333,19 +342,26 @@ add_link(Heading,File,Type) ->
%%% <p>This function is called by <code>ct</code> when logging
%%% stuff directly from a testcase (i.e. not from within the CT
%%% framework).</p>
-tc_log(Category,Format,Args) ->
- tc_log(Category,"User",Format,Args).
+tc_log(Category,Importance,Format,Args) ->
+ tc_log(Category,Importance,"User",Format,Args).
-tc_log(Category,Printer,Format,Args) ->
- cast({log,sync,self(),group_leader(),[{div_header(Category,Printer),[]},
- {Format,Args},
- {div_footer(),[]}]}),
+tc_log(Category,Importance,Printer,Format,Args) ->
+ cast({log,sync,self(),group_leader(),Category,Importance,
+ [{div_header(Category,Printer),[]},
+ {Format,Args},
+ {div_footer(),[]}]}),
ok.
-
%%%-----------------------------------------------------------------
%%% @spec tc_log_async(Category,Format,Args) -> ok
+%%% @equiv tc_log_async(Category,?STD_IMPORTANCE,Format,Args)
+tc_log_async(Category,Format,Args) ->
+ tc_log_async(Category,?STD_IMPORTANCE,Format,Args).
+
+%%%-----------------------------------------------------------------
+%%% @spec tc_log_async(Category,Importance,Format,Args) -> ok
%%% Category = atom()
+%%% Importance = integer()
%%% Format = string()
%%% Args = list()
%%%
@@ -356,40 +372,66 @@ tc_log(Category,Printer,Format,Args) ->
%%% to avoid deadlocks when e.g. the hook that handles SASL printouts
%%% prints to the test case log file at the same time test server
%%% asks ct_logs for an html wrapper.</p>
-tc_log_async(Category,Format,Args) ->
- cast({log,async,self(),group_leader(),[{div_header(Category),[]},
- {Format,Args},
- {div_footer(),[]}]}),
+tc_log_async(Category,Importance,Format,Args) ->
+ cast({log,async,self(),group_leader(),Category,Importance,
+ [{div_header(Category),[]},
+ {Format,Args},
+ {div_footer(),[]}]}),
ok.
+%%%-----------------------------------------------------------------
+%%% @spec tc_print(Category,Format,Args)
+%%% @equiv tc_print(Category,?STD_IMPORTANCE,Format,Args)
+tc_print(Category,Format,Args) ->
+ tc_print(Category,?STD_IMPORTANCE,Format,Args).
%%%-----------------------------------------------------------------
-%%% @spec tc_print(Category,Format,Args) -> ok
+%%% @spec tc_print(Category,Importance,Format,Args) -> ok
%%% Category = atom()
+%%% Importance = integer()
%%% Format = string()
%%% Args = list()
%%%
%%% @doc Console printout from a testcase.
%%%
%%% <p>This function is called by <code>ct</code> when printing
-%%% stuff a testcase on the user console.</p>
-tc_print(Category,Format,Args) ->
- Head = get_heading(Category),
- io:format(user, lists:concat([Head,Format,"\n\n"]), Args),
- ok.
+%%% stuff from a testcase on the user console.</p>
+tc_print(Category,Importance,Format,Args) ->
+ VLvl = case ct_util:get_testdata({verbosity,Category}) of
+ undefined ->
+ ct_util:get_testdata({verbosity,'$unspecified'});
+ {error,bad_invocation} ->
+ ?MAX_VERBOSITY;
+ Val ->
+ Val
+ end,
+ if Importance >= (100-VLvl) ->
+ Head = get_heading(Category),
+ io:format(user, lists:concat([Head,Format,"\n\n"]), Args),
+ ok;
+ true ->
+ ok
+ end.
get_heading(default) ->
- io_lib:format("-----------------------------"
+ io_lib:format("\n-----------------------------"
"-----------------------\n~s\n",
[log_timestamp(now())]);
get_heading(Category) ->
- io_lib:format("-----------------------------"
+ io_lib:format("\n-----------------------------"
"-----------------------\n~s ~w\n",
[log_timestamp(now()),Category]).
%%%-----------------------------------------------------------------
%%% @spec tc_pal(Category,Format,Args) -> ok
+%%% @equiv tc_pal(Category,?STD_IMPORTANCE,Format,Args) -> ok
+tc_pal(Category,Format,Args) ->
+ tc_pal(Category,?STD_IMPORTANCE,Format,Args).
+
+%%%-----------------------------------------------------------------
+%%% @spec tc_pal(Category,Importance,Format,Args) -> ok
%%% Category = atom()
+%%% Importance = integer()
%%% Format = string()
%%% Args = list()
%%%
@@ -398,16 +440,17 @@ get_heading(Category) ->
%%% <p>This function is called by <code>ct</code> when logging
%%% stuff directly from a testcase. The info is written both in the
%%% log and on the console.</p>
-tc_pal(Category,Format,Args) ->
- tc_print(Category,Format,Args),
- cast({log,sync,self(),group_leader(),[{div_header(Category),[]},
- {Format,Args},
- {div_footer(),[]}]}),
+tc_pal(Category,Importance,Format,Args) ->
+ tc_print(Category,Importance,Format,Args),
+ cast({log,sync,self(),group_leader(),Category,Importance,
+ [{div_header(Category),[]},
+ {Format,Args},
+ {div_footer(),[]}]}),
ok.
%%%-----------------------------------------------------------------
-%%% @spec tc_pal(Category,Format,Args) -> ok
+%%% @spec ct_pal(Category,Format,Args) -> ok
%%% Category = atom()
%%% Format = string()
%%% Args = list()
@@ -445,7 +488,7 @@ maybe_log_timestamp() ->
{MS,S,_} ->
ok;
_ ->
- cast({log,sync,self(),group_leader(),
+ cast({log,sync,self(),group_leader(),ct_internal,?MAX_IMPORTANCE,
[{"<i>~s</i>",[log_timestamp({MS,S,US})]}]})
end.
@@ -469,7 +512,7 @@ log_timestamp({MS,S,US}) ->
stylesheet,
async_print_jobs}).
-logger(Parent,Mode) ->
+logger(Parent, Mode, Verbosity) ->
register(?MODULE,self()),
%%! Below is a temporary workaround for the limitation of
@@ -502,26 +545,27 @@ logger(Parent,Mode) ->
%% dir) so logs are independent of Common Test installation
{ok,Cwd} = file:get_cwd(),
CTPath = code:lib_dir(common_test),
- CSSFileSrc = filename:join(filename:join(CTPath, "priv"),
- ?css_default),
- CSSFileDestTop = filename:join(Cwd, ?css_default),
- CSSFileDestRun = filename:join(AbsDir, ?css_default),
- case file:copy(CSSFileSrc, CSSFileDestTop) of
- {error,Reason0} ->
+ PrivFiles = [?css_default,?jquery_script,?tablesorter_script],
+ PrivFilesSrc = [filename:join(filename:join(CTPath, "priv"), F) ||
+ F <- PrivFiles],
+ PrivFilesDestTop = [filename:join(Cwd, F) || F <- PrivFiles],
+ PrivFilesDestRun = [filename:join(AbsDir, F) || F <- PrivFiles],
+ case copy_priv_files(PrivFilesSrc, PrivFilesDestTop) of
+ {error,Src1,Dest1,Reason1} ->
io:format(user, "ERROR! "++
- "CSS file ~p could not be copied to ~p. "++
- "Reason: ~p~n",
- [CSSFileSrc,CSSFileDestTop,Reason0]),
- exit({css_file_error,CSSFileDestTop});
- _ ->
- case file:copy(CSSFileSrc, CSSFileDestRun) of
- {error,Reason1} ->
+ "Priv file ~p could not be copied to ~p. "++
+ "Reason: ~p~n",
+ [Src1,Dest1,Reason1]),
+ exit({priv_file_error,Dest1});
+ ok ->
+ case copy_priv_files(PrivFilesSrc, PrivFilesDestRun) of
+ {error,Src2,Dest2,Reason2} ->
io:format(user, "ERROR! "++
- "CSS file ~p could not be copied to ~p. "++
- "Reason: ~p~n",
- [CSSFileSrc,CSSFileDestRun,Reason1]),
- exit({css_file_error,CSSFileDestRun});
- _ ->
+ "Priv file ~p could not be copied to ~p. "++
+ "Reason: ~p~n",
+ [Src2,Dest2,Reason2]),
+ exit({priv_file_error,Dest2});
+ ok ->
ok
end
end
@@ -541,6 +585,23 @@ logger(Parent,Mode) ->
[log_timestamp(now()),"Common Test Logger started"]),
Parent ! {started,self(),{Time,filename:absname("")}},
set_evmgr_gl(CtLogFd),
+
+ %% save verbosity levels in dictionary for fast lookups
+ io:format(CtLogFd, "\nVERBOSITY LEVELS:\n", []),
+ case proplists:get_value('$unspecified', Verbosity) of
+ undefined -> ok;
+ GenLvl -> io:format(CtLogFd, "~-25s~3w~n",
+ ["general level",GenLvl])
+ end,
+ [begin put({verbosity,Cat},VLvl),
+ if Cat == '$unspecified' ->
+ ok;
+ true ->
+ io:format(CtLogFd, "~-25w~3w~n", [Cat,VLvl])
+ end
+ end || {Cat,VLvl} <- Verbosity],
+ io:nl(CtLogFd),
+
logger_loop(#logger_state{parent=Parent,
log_dir=AbsDir,
start_time=Time,
@@ -549,31 +610,58 @@ logger(Parent,Mode) ->
tc_groupleaders=[],
async_print_jobs=[]}).
+copy_priv_files([SrcF | SrcFs], [DestF | DestFs]) ->
+ case file:copy(SrcF, DestF) of
+ {error,Reason} ->
+ {error,SrcF,DestF,Reason};
+ _ ->
+ copy_priv_files(SrcFs, DestFs)
+ end;
+copy_priv_files([], []) ->
+ ok.
+
logger_loop(State) ->
receive
- {log,SyncOrAsync,Pid,GL,List} ->
- case get_groupleader(Pid, GL, State) of
- {tc_log,TCGL,TCGLs} ->
- case erlang:is_process_alive(TCGL) of
- true ->
- State1 = print_to_log(SyncOrAsync, Pid, TCGL,
- List, State),
- logger_loop(State1#logger_state{tc_groupleaders =
- TCGLs});
- false ->
- %% Group leader is dead, so write to the
- %% CtLog instead
- Fd = State#logger_state.ct_log_fd,
- [begin io:format(Fd,Str,Args),io:nl(Fd) end ||
+ {log,SyncOrAsync,Pid,GL,Category,Importance,List} ->
+ VLvl = case Category of
+ ct_internal ->
+ ?MAX_VERBOSITY;
+ _ ->
+ case get({verbosity,Category}) of
+ undefined -> get({verbosity,'$unspecified'});
+ Val -> Val
+ end
+ end,
+ if Importance >= (100-VLvl) ->
+ case get_groupleader(Pid, GL, State) of
+ {tc_log,TCGL,TCGLs} ->
+ case erlang:is_process_alive(TCGL) of
+ true ->
+ State1 = print_to_log(SyncOrAsync, Pid,
+ TCGL, List, State),
+ logger_loop(State1#logger_state{
+ tc_groupleaders = TCGLs});
+ false ->
+ %% Group leader is dead, so write to the
+ %% CtLog instead
+ Fd = State#logger_state.ct_log_fd,
+ [begin io:format(Fd,Str,Args),
+ io:nl(Fd) end || {Str,Args} <- List],
+ logger_loop(State)
+ end;
+ {ct_log,Fd,TCGLs} ->
+ [begin io:format(Fd,Str,Args),io:nl(Fd) end ||
{Str,Args} <- List],
- logger_loop(State)
+ logger_loop(State#logger_state{
+ tc_groupleaders = TCGLs})
end;
- {ct_log,Fd,TCGLs} ->
- [begin io:format(Fd,Str,Args),io:nl(Fd) end ||
- {Str,Args} <- List],
- logger_loop(State#logger_state{tc_groupleaders = TCGLs})
- end;
+ true ->
+ logger_loop(State)
+ end;
{{init_tc,TCPid,GL,RefreshLog},From} ->
+ %% make sure no IO for this test case from the
+ %% CT logger gets rejected
+ test_server:permit_io(GL, self()),
print_style(GL, State#logger_state.stylesheet),
set_evmgr_gl(GL),
TCGLs = add_tc_gl(TCPid,GL,State),
@@ -659,13 +747,24 @@ create_io_fun(FromPid, State) ->
print_to_log(sync, FromPid, TCGL, List, State) ->
IoFun = create_io_fun(FromPid, State),
- io:format(TCGL, "~s", [lists:foldl(IoFun, [], List)]),
+ %% in some situations (exceptions), the printout is made from the
+ %% test server IO process and there's no valid group leader to send to
+ IoProc = if FromPid /= TCGL -> TCGL;
+ true -> State#logger_state.ct_log_fd
+ end,
+ io:format(IoProc, "~s", [lists:foldl(IoFun, [], List)]),
State;
print_to_log(async, FromPid, TCGL, List, State) ->
IoFun = create_io_fun(FromPid, State),
+ %% in some situations (exceptions), the printout is made from the
+ %% test server IO process and there's no valid group leader to send to
+ IoProc = if FromPid /= TCGL -> TCGL;
+ true -> State#logger_state.ct_log_fd
+ end,
Printer = fun() ->
- io:format(TCGL, "~s", [lists:foldl(IoFun, [], List)])
+ test_server:permit_io(IoProc, self()),
+ io:format(IoProc, "~s", [lists:foldl(IoFun, [], List)])
end,
case State#logger_state.async_print_jobs of
[] ->
@@ -770,7 +869,7 @@ set_evmgr_gl(GL) ->
open_ctlog() ->
{ok,Fd} = file:open(?ct_log_name,[write]),
- io:format(Fd, header("Common Test Framework Log"), []),
+ io:format(Fd, header("Common Test Framework Log", {[],[1,2],[]}), []),
case file:consult(ct_run:variables_file_name("../")) of
{ok,Vars} ->
io:format(Fd, config_table(Vars), []);
@@ -1080,14 +1179,14 @@ total_row(Success, Fail, UserSkip, AutoSkip, NotBuilt, All) ->
integer_to_list(UserSkip),integer_to_list(AutoSkip)}
end,
[xhtml("<tr valign=top>\n",
- ["<tr class=\"",odd_or_even(),"\">\n"]),
+ ["</tbody>\n<tfoot>\n<tr class=\"",odd_or_even(),"\">\n"]),
"<td><b>Total</b></td>\n", Label, TimestampCell,
"<td align=right><b>",integer_to_list(Success),"<b></td>\n",
"<td align=right><b>",integer_to_list(Fail),"<b></td>\n",
"<td align=right>",integer_to_list(AllSkip),
" (",UserSkipStr,"/",AutoSkipStr,")</td>\n",
"<td align=right><b>",integer_to_list(NotBuilt),"<b></td>\n",
- AllInfo, "</tr>\n"].
+ AllInfo, "</tr>\n</tfoot>\n"].
not_built(_BaseName,_LogDir,_All,[]) ->
0;
@@ -1144,10 +1243,12 @@ index_header(Label, StartTime) ->
Head =
case Label of
undefined ->
- header("Test Results", format_time(StartTime));
+ header("Test Results", format_time(StartTime),
+ {[],[1],[2,3,4,5]});
_ ->
header("Test Results for '" ++ Label ++ "'",
- format_time(StartTime))
+ format_time(StartTime),
+ {[],[1],[2,3,4,5]})
end,
[Head |
["<center>\n",
@@ -1159,15 +1260,17 @@ index_header(Label, StartTime) ->
"\">COMMON TEST FRAMEWORK LOG</a>\n</div>"]),
xhtml("<br>\n", "<br /><br /><br />\n"),
xhtml(["<table border=\"3\" cellpadding=\"5\" "
- "bgcolor=\"",?table_color3,"\">\n"], "<table>\n"),
+ "bgcolor=\"",?table_color3,"\">\n"],
+ ["<table id=\"",?sortable_table_name,"\">\n",
+ "<thead>\n<tr>\n"]),
"<th><b>Test Name</b></th>\n",
xhtml(["<th><font color=\"",?table_color3,"\">_</font>Ok"
"<font color=\"",?table_color3,"\">_</font></th>\n"],
"<th>Ok</th>\n"),
"<th>Failed</th>\n",
"<th>Skipped", xhtml("<br>", "<br />"), "(User/Auto)</th>\n"
- "<th>Missing", xhtml("<br>", "<br />"), "Suites</th>\n"
- "\n"]].
+ "<th>Missing", xhtml("<br>", "<br />"), "Suites</th>\n",
+ xhtml("", "</tr>\n</thead>\n<tbody>\n")]].
all_suites_index_header() ->
{ok,Cwd} = file:get_cwd(),
@@ -1180,12 +1283,14 @@ all_suites_index_header(IndexDir) ->
AllRunsLink = xhtml(["<a href=\"",?all_runs_name,"\">",AllRuns,"</a>\n"],
["<div id=\"button_holder\" class=\"btn\">\n"
"<a href=\"",?all_runs_name,"\">",AllRuns,"</a>\n</div>"]),
- [header("Test Results") |
+ [header("Test Results", {[3],[1,2,8,9,10],[4,5,6,7]}) |
["<center>\n",
AllRunsLink,
xhtml("<br><br>\n", "<br /><br />\n"),
xhtml(["<table border=\"3\" cellpadding=\"5\" "
- "bgcolor=\"",?table_color2,"\">\n"], "<table>\n"),
+ "bgcolor=\"",?table_color2,"\">\n"],
+ ["<table id=\"",?sortable_table_name,"\">\n",
+ "<thead>\n<tr>\n"]),
"<th>Test Name</th>\n",
"<th>Label</th>\n",
"<th>Test Run Started</th>\n",
@@ -1198,7 +1303,7 @@ all_suites_index_header(IndexDir) ->
"<th>Node</th>\n",
"<th>CT Log</th>\n",
"<th>Old Runs</th>\n",
- "\n"]].
+ xhtml("", "</tr>\n</thead>\n<tbody>\n")]].
all_runs_header() ->
{ok,Cwd} = file:get_cwd(),
@@ -1210,10 +1315,12 @@ all_runs_header() ->
"<a href=\"",?index_name,
"\">TEST INDEX PAGE</a>\n</div>"]),
xhtml("<br>\n", "<br /><br />\n")],
- [header(Title) |
+ [header(Title, {[1],[2,3,5],[4,6,7,8,9,10]}) |
["<center>\n", IxLink,
xhtml(["<table border=\"3\" cellpadding=\"5\" "
- "bgcolor=\"",?table_color1,"\">\n"], "<table>\n"),
+ "bgcolor=\"",?table_color1,"\">\n"],
+ ["<table id=\"",?sortable_table_name,"\">\n",
+ "<thead>\n<tr>\n"]),
"<th><b>History</b></th>\n"
"<th><b>Node</b></th>\n"
"<th><b>Label</b></th>\n"
@@ -1225,23 +1332,29 @@ all_runs_header() ->
"<th>Ok</th>\n"),
"<th>Failed</th>\n"
"<th>Skipped<br>(User/Auto)</th>\n"
- "<th>Missing<br>Suites</th>\n"
- "\n"]].
+ "<th>Missing<br>Suites</th>\n",
+ xhtml("", "</tr>\n</thead>\n<tbody>\n")]].
-header(Title) ->
- header1(Title, "").
-header(Title, SubTitle) ->
- header1(Title, SubTitle).
+header(Title, TableCols) ->
+ header1(Title, "", TableCols).
+header(Title, SubTitle, TableCols) ->
+ header1(Title, SubTitle, TableCols).
-header1(Title, SubTitle) ->
+header1(Title, SubTitle, TableCols) ->
SubTitleHTML = if SubTitle =/= "" ->
["<center>\n",
"<h3>" ++ SubTitle ++ "</h3>\n",
xhtml("</center>\n<br>\n", "</center>\n<br />\n")];
- true -> xhtml("<br>\n", "<br />\n")
+ true -> xhtml("<br>", "<br />")
end,
CSSFile = xhtml(fun() -> "" end,
- fun() -> make_relative(locate_default_css_file()) end),
+ fun() -> make_relative(locate_priv_file(?css_default)) end),
+ JQueryFile =
+ xhtml(fun() -> "" end,
+ fun() -> make_relative(locate_priv_file(?jquery_script)) end),
+ TableSorterFile =
+ xhtml(fun() -> "" end,
+ fun() -> make_relative(locate_priv_file(?tablesorter_script)) end),
[xhtml(["<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n",
"<html>\n"],
["<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n",
@@ -1252,7 +1365,17 @@ header1(Title, SubTitle) ->
"<title>" ++ Title ++ " " ++ SubTitle ++ "</title>\n",
"<meta http-equiv=\"cache-control\" content=\"no-cache\">\n",
xhtml("",
- ["<link rel=\"stylesheet\" href=\"",CSSFile,"\" type=\"text/css\">"]),
+ ["<link rel=\"stylesheet\" href=\"",CSSFile,"\" type=\"text/css\">\n"]),
+ xhtml("",
+ ["<script type=\"text/javascript\" src=\"",JQueryFile,
+ "\"></script>\n"]),
+ xhtml("",
+ ["<script type=\"text/javascript\" src=\"",TableSorterFile,
+ "\"></script>\n"]),
+ xhtml(fun() -> "" end,
+ fun() -> insert_javascript({tablesorter,?sortable_table_name,
+ TableCols})
+ end),
"</head>\n",
body_tag(),
"<center>\n",
@@ -1264,6 +1387,10 @@ index_footer() ->
["</table>\n"
"</center>\n" | footer()].
+all_runs_index_footer() ->
+ ["</tbody>\n</table>\n"
+ "</center>\n" | footer()].
+
footer() ->
["<center>\n",
xhtml("<br><br>\n<hr>\n", "<br /><br />\n"),
@@ -1275,7 +1402,8 @@ footer() ->
xhtml("<br>\n", "<br />\n"),
xhtml("</font></p>\n", "</div>\n"),
"</center>\n"
- "</body>\n"].
+ "</body>\n"
+ "</html>\n"].
body_tag() ->
@@ -1291,7 +1419,7 @@ current_time() ->
format_time({{Y, Mon, D}, {H, Min, S}}) ->
Weekday = weekday(calendar:day_of_the_week(Y, Mon, D)),
- lists:flatten(io_lib:format("~s ~s ~p ~w ~2.2.0w:~2.2.0w:~2.2.0w",
+ lists:flatten(io_lib:format("~s ~s ~2.2.0w ~w ~2.2.0w:~2.2.0w:~2.2.0w",
[Weekday, month(Mon), D, Y, H, Min, S])).
weekday(1) -> "Mon";
@@ -1417,8 +1545,12 @@ config_table_header() ->
[
xhtml(["<h2>Configuration</h2>\n"
"<table border=\"3\" cellpadding=\"5\" bgcolor=\"",?table_color1,"\"\n"],
- "<h4>CONFIGURATION</h4>\n<table>\n"),
- "<tr><th>Key</th><th>Value</th></tr>\n"].
+ ["<h4>CONFIGURATION</h4>\n",
+ "<table id=\"",?sortable_table_name,"\">\n",
+ "<thead>\n"]),
+ "<tr><th>Key</th><th>Value</th></tr>\n",
+ xhtml("", "</thead>\n<tbody>\n")
+ ].
config_table1([{Key,Value}|Vars]) ->
[xhtml(["<tr><td>", atom_to_list(Key), "</td>\n",
@@ -1428,7 +1560,7 @@ config_table1([{Key,Value}|Vars]) ->
"<td>", io_lib:format("~p",[Value]), "</td>\n</tr>\n"]) |
config_table1(Vars)];
config_table1([]) ->
- ["</table>\n"].
+ ["</tbody>\n</table>\n"].
make_all_runs_index(When) ->
@@ -1442,7 +1574,8 @@ make_all_runs_index(When) ->
DirsSorted = (catch sort_all_runs(Dirs)),
Header = all_runs_header(),
Index = [runentry(Dir) || Dir <- DirsSorted],
- Result = file:write_file(AbsName,Header++Index++index_footer()),
+ Result = file:write_file(AbsName,Header++Index++
+ all_runs_index_footer()),
if When == start -> ok;
true -> io:put_chars("done\n")
end,
@@ -1981,7 +2114,7 @@ simulate() ->
simulate_logger_loop() ->
receive
- {log,_,_,_,List} ->
+ {log,_,_,_,_,_,List} ->
S = [[io_lib:format(Str,Args),io_lib:nl()] || {Str,Args} <- List],
io:format("~s",[S]),
simulate_logger_loop();
@@ -2078,34 +2211,34 @@ basic_html() ->
end.
%%%-----------------------------------------------------------------
-%%% @spec locate_default_css_file() -> CSSFile
+%%% @spec locate_priv_file(FileName) -> PrivFile
%%%
%%% @doc
%%%
-locate_default_css_file() ->
+locate_priv_file(FileName) ->
{ok,CWD} = file:get_cwd(),
- CSSFileInCwd = filename:join(CWD, ?css_default),
- case filelib:is_file(CSSFileInCwd) of
+ PrivFileInCwd = filename:join(CWD, FileName),
+ case filelib:is_file(PrivFileInCwd) of
true ->
- CSSFileInCwd;
+ PrivFileInCwd;
false ->
- CSSResultFile =
+ PrivResultFile =
case {whereis(?MODULE),self()} of
{Self,Self} ->
%% executed on the ct_logs process
- filename:join(get(ct_run_dir), ?css_default);
+ filename:join(get(ct_run_dir), FileName);
_ ->
%% executed on other process than ct_logs
{ok,RunDir} = get_log_dir(true),
- filename:join(RunDir, ?css_default)
+ filename:join(RunDir, FileName)
end,
- case filelib:is_file(CSSResultFile) of
+ case filelib:is_file(PrivResultFile) of
true ->
- CSSResultFile;
+ PrivResultFile;
false ->
%% last resort, try use css file in CT installation
CTPath = code:lib_dir(common_test),
- filename:join(filename:join(CTPath, "priv"), ?css_default)
+ filename:join(filename:join(CTPath, "priv"), FileName)
end
end.
@@ -2144,7 +2277,7 @@ make_relative1(DirTs, CwdTs) ->
%%%
%%% @doc
%%%
-get_ts_html_wrapper(TestName, PrintLabel, Cwd) ->
+get_ts_html_wrapper(TestName, PrintLabel, Cwd, TableCols) ->
TestName1 = if is_list(TestName) ->
lists:flatten(TestName);
true ->
@@ -2204,17 +2337,36 @@ get_ts_html_wrapper(TestName, PrintLabel, Cwd) ->
"Open Telecom Platform</a><br />\n",
"Updated: <!date>", current_time(), "<!/date>",
"<br />\n</div>\n"],
- CSSFile = xhtml(fun() -> "" end,
- fun() -> make_relative(locate_default_css_file(), Cwd) end),
+ CSSFile =
+ xhtml(fun() -> "" end,
+ fun() -> make_relative(locate_priv_file(?css_default),
+ Cwd)
+ end),
+ JQueryFile =
+ xhtml(fun() -> "" end,
+ fun() -> make_relative(locate_priv_file(?jquery_script),
+ Cwd)
+ end),
+ TableSorterFile =
+ xhtml(fun() -> "" end,
+ fun() -> make_relative(locate_priv_file(?tablesorter_script),
+ Cwd)
+ end),
+ TableSorterScript =
+ xhtml(fun() -> "" end,
+ fun() -> insert_javascript({tablesorter,
+ ?sortable_table_name,
+ TableCols}) end),
{xhtml,
["<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n",
"\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n",
"<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n",
"<head>\n<title>", TestName1, "</title>\n",
"<meta http-equiv=\"cache-control\" content=\"no-cache\">\n",
- "<link rel=\"stylesheet\" href=\"", CSSFile, "\" type=\"text/css\">",
- "</head>\n","<body>\n",
- LabelStr, "\n"],
+ "<link rel=\"stylesheet\" href=\"", CSSFile, "\" type=\"text/css\">\n",
+ "<script type=\"text/javascript\" src=\"", JQueryFile, "\"></script>\n",
+ "<script type=\"text/javascript\" src=\"", TableSorterFile, "\"></script>\n"] ++
+ TableSorterScript ++ ["</head>\n","<body>\n", LabelStr, "\n"],
["<center>\n<br /><hr /><p>\n",
"<a href=\"", AllRuns,
"\">Test run history\n</a> | ",
@@ -2222,3 +2374,89 @@ get_ts_html_wrapper(TestName, PrintLabel, Cwd) ->
"\">Top level test index\n</a>\n</p>\n",
Copyright,"</center>\n</body>\n</html>\n"]}
end.
+
+insert_javascript({tablesorter,_TableName,undefined}) ->
+ [];
+
+insert_javascript({tablesorter,TableName,
+ {DateCols,TextCols,ValCols}}) ->
+ Headers =
+ lists:flatten(
+ lists:sort(
+ lists:flatmap(fun({Sorter,Cols}) ->
+ [lists:flatten(
+ io_lib:format(" ~w: "
+ "{ sorter: '~s' },\n",
+ [Col-1,Sorter])) || Col<-Cols]
+ end, [{"CTDateSorter",DateCols},
+ {"CTTextSorter",TextCols},
+ {"CTValSorter",ValCols}]))),
+ Headers1 = string:substr(Headers, 1, length(Headers)-2),
+
+ ["<script type=\"text/javascript\">\n",
+ "// Parser for date format, e.g: Wed Jul 4 2012 11:24:15\n",
+ "var monthNames = {};\n",
+ "monthNames[\"Jan\"] = \"01\"; monthNames[\"Feb\"] = \"02\";\n",
+ "monthNames[\"Mar\"] = \"03\"; monthNames[\"Apr\"] = \"04\";\n",
+ "monthNames[\"May\"] = \"05\"; monthNames[\"Jun\"] = \"06\";\n",
+ "monthNames[\"Jul\"] = \"07\"; monthNames[\"Aug\"] = \"08\";\n",
+ "monthNames[\"Sep\"] = \"09\"; monthNames[\"Oct\"] = \"10\";\n",
+ "monthNames[\"Nov\"] = \"11\"; monthNames[\"Dec\"] = \"12\";\n",
+ "$.tablesorter.addParser({\n",
+ " id: 'CTDateSorter',\n",
+ " is: function(s) {\n",
+ " return false; },\n",
+ " format: function(s) {\n",
+ %% place empty cells, "-" and "?" at the bottom
+ " if (s.length < 2) return 999999999;\n",
+ " else {\n",
+ %% match out each date element
+ " var date = s.match(/(\\w{3})\\s(\\w{3})\\s(\\d{2})\\s(\\d{4})\\s(\\d{2}):(\\d{2}):(\\d{2})/);\n",
+ " var y = date[4]; var mo = monthNames[date[2]]; var d = String(date[3]);\n",
+ " var h = String(date[5]); var mi = String(date[6]); var sec = String(date[7]);\n",
+ " return (parseInt('' + y + mo + d + h + mi + sec)); }},\n",
+ " type: 'numeric' });\n",
+
+ "// Parser for general text format\n",
+ "$.tablesorter.addParser({\n",
+ " id: 'CTTextSorter',\n",
+ " is: function(s) {\n",
+ " return false; },\n",
+ " format: function(s) {\n",
+ %% place empty cells, "?" and "-" at the bottom
+ " if (s.length < 1) return 'zzzzzzzz';\n",
+ " else if (s == \"?\") return 'zzzzzzz';\n",
+ " else if (s == \"-\") return 'zzzzzz';\n",
+ " else if (s == \"FAILED\") return 'A';\n",
+ " else if (s == \"SKIPPED\") return 'B';\n",
+ " else if (s == \"OK\") return 'C';\n",
+ " else return '' + s; },\n",
+ " type: 'text' });\n",
+
+ "// Parser for numerical values\n",
+ "$.tablesorter.addParser({\n",
+ " id: 'CTValSorter',\n",
+ " is: function(s) {\n",
+ " return false; },\n",
+ " format: function(s) {\n"
+ %% place empty cells and "?" at the bottom
+ " if (s.length < 1) return '-2';\n",
+ " else if (s == \"?\") return '-1';\n",
+ %% look for skip value, eg "3 (2/1)"
+ " else if ((s.search(/(\\d{1,})\\s/)) >= 0) {\n",
+ " var num = s.match(/(\\d{1,})\\s/);\n",
+ %% return only the total skip value for sorting
+ " return (parseInt('' + num[1])); }\n",
+ " else if ((s.search(/(\\d{1,})\\.(\\d{3})s/)) >= 0) {\n",
+ " var num = s.match(/(\\d{1,})\\.(\\d{3})/);\n",
+ " if (num[1] == \"0\") return (parseInt('' + num[2]));\n",
+ " else return (parseInt('' + num[1] + num[2])); }\n",
+ " else return '' + s; },\n",
+ " type: 'numeric' });\n",
+
+ "$(document).ready(function() {\n",
+ " $(\"#",TableName,"\").tablesorter({\n",
+ " headers: { \n", Headers1, "\n }\n });\n",
+ " $(\"#",TableName,"\").trigger(\"update\");\n",
+ " $(\"#",TableName,"\").trigger(\"appendCache\");\n",
+ "});\n</script>\n"].
diff --git a/lib/common_test/src/ct_master.erl b/lib/common_test/src/ct_master.erl
index 042c5ba267..f29eba605c 100644
--- a/lib/common_test/src/ct_master.erl
+++ b/lib/common_test/src/ct_master.erl
@@ -51,7 +51,7 @@
%%% {testcase,Cases} | {spec,TestSpecs} | {allow_user_terms,Bool} |
%%% {logdir,LogDir} | {event_handler,EventHandlers} |
%%% {silent_connections,Conns} | {cover,CoverSpecFile} |
-%%% {userconfig, UserCfgFiles}
+%%% {cover_stop,Bool} | {userconfig, UserCfgFiles}
%%% CfgFiles = string() | [string()]
%%% TestDirs = string() | [string()]
%%% Suites = atom() | [atom()]
@@ -696,8 +696,9 @@ status(MasterPid,Event) ->
log(To,Heading,Str,Args) ->
if To == all ; To == tty ->
- Str1 = ["=== ",Heading," ===\n",io_lib:format(Str,Args),"\n"],
- io:format(Str1,[]);
+ Chars = ["=== ",Heading," ===\n",
+ io_lib:format(Str,Args),"\n"],
+ io:put_chars(Chars);
true ->
ok
end,
diff --git a/lib/common_test/src/ct_master_logs.erl b/lib/common_test/src/ct_master_logs.erl
index 2a951bc5cf..84f175c0a9 100644
--- a/lib/common_test/src/ct_master_logs.erl
+++ b/lib/common_test/src/ct_master_logs.erl
@@ -26,6 +26,8 @@
-export([start/2, make_all_runs_index/0, log/3, nodedir/2,
stop/0]).
+-include("ct_util.hrl").
+
-record(state, {log_fd, start_time, logdir, rundir,
nodedir_ix_fd, nodes, nodedirs=[]}).
@@ -33,7 +35,6 @@
-define(all_runs_name, "master_runs.html").
-define(nodedir_index_name, "index.html").
-define(details_file_name,"details.info").
--define(css_default, "ct_default.css").
-define(table_color,"lightblue").
%%%--------------------------------------------------------------------
@@ -95,29 +96,30 @@ init(Parent,LogDir,Nodes) ->
put(basic_html, true);
BasicHtml ->
put(basic_html, BasicHtml),
- %% copy stylesheet to log dir (both top dir and test run
+ %% copy priv files to log dir (both top dir and test run
%% dir) so logs are independent of Common Test installation
CTPath = code:lib_dir(common_test),
- CSSFileSrc = filename:join(filename:join(CTPath, "priv"),
- ?css_default),
- CSSFileDestTop = filename:join(LogDir, ?css_default),
- CSSFileDestRun = filename:join(RunDirAbs, ?css_default),
- case file:copy(CSSFileSrc, CSSFileDestTop) of
- {error,Reason0} ->
+ PrivFiles = [?css_default,?jquery_script,?tablesorter_script],
+ PrivFilesSrc = [filename:join(filename:join(CTPath, "priv"), F) ||
+ F <- PrivFiles],
+ PrivFilesDestTop = [filename:join(LogDir, F) || F <- PrivFiles],
+ PrivFilesDestRun = [filename:join(RunDirAbs, F) || F <- PrivFiles],
+ case copy_priv_files(PrivFilesSrc, PrivFilesDestTop) of
+ {error,Src1,Dest1,Reason1} ->
io:format(user, "ERROR! "++
- "CSS file ~p could not be copied to ~p. "++
+ "Priv file ~p could not be copied to ~p. "++
"Reason: ~p~n",
- [CSSFileSrc,CSSFileDestTop,Reason0]),
- exit({css_file_error,CSSFileDestTop});
- _ ->
- case file:copy(CSSFileSrc, CSSFileDestRun) of
- {error,Reason1} ->
+ [Src1,Dest1,Reason1]),
+ exit({priv_file_error,Dest1});
+ ok ->
+ case copy_priv_files(PrivFilesSrc, PrivFilesDestRun) of
+ {error,Src2,Dest2,Reason2} ->
io:format(user, "ERROR! "++
- "CSS file ~p could not be copied to ~p. "++
+ "Priv file ~p could not be copied to ~p. "++
"Reason: ~p~n",
- [CSSFileSrc,CSSFileDestRun,Reason1]),
- exit({css_file_error,CSSFileDestRun});
- _ ->
+ [Src2,Dest2,Reason2]),
+ exit({priv_file_error,Dest2});
+ ok ->
ok
end
end
@@ -132,7 +134,7 @@ init(Parent,LogDir,Nodes) ->
io:format(CtLogFd,int_header(),[log_timestamp(now()),"Test Nodes\n"]),
io:format(CtLogFd,"~s\n",[NodeStr]),
- io:format(CtLogFd,int_footer()++"\n",[]),
+ io:put_chars(CtLogFd,[int_footer(),"\n"]),
NodeDirIxFd = open_nodedir_index(RunDirAbs,Time),
Parent ! {started,self(),{Time,RunDirAbs}},
@@ -146,6 +148,16 @@ init(Parent,LogDir,Nodes) ->
{N,""}
end,Nodes)}).
+copy_priv_files([SrcF | SrcFs], [DestF | DestFs]) ->
+ case file:copy(SrcF, DestF) of
+ {error,Reason} ->
+ {error,SrcF,DestF,Reason};
+ _ ->
+ copy_priv_files(SrcFs, DestFs)
+ end;
+copy_priv_files([], []) ->
+ ok.
+
loop(State) ->
receive
{log,_From,List} ->
@@ -190,24 +202,21 @@ loop(State) ->
open_ct_master_log(Dir) ->
FullName = filename:join(Dir,?ct_master_log_name),
{ok,Fd} = file:open(FullName,[write]),
- io:format(Fd,header("Common Test Master Log"),[]),
+ io:put_chars(Fd,header("Common Test Master Log", {[],[1,2],[]})),
%% maybe add config info here later
- io:format(Fd, config_table([]), []),
- io:format(Fd,
- "<style>\n"
- "div.ct_internal { background:lightgrey; color:black }\n"
- "div.default { background:lightgreen; color:black }\n"
- "</style>\n",
- []),
- io:format(Fd,
- xhtml("<br><h2>Progress Log</h2>\n<pre>\n",
- "<br /><h2>Progress Log</h2>\n<pre>\n"),
- []),
+ io:put_chars(Fd,config_table([])),
+ io:put_chars(Fd,
+ "<style>\n"
+ "div.ct_internal { background:lightgrey; color:black }\n"
+ "div.default { background:lightgreen; color:black }\n"
+ "</style>\n"),
+ io:put_chars(Fd,
+ xhtml("<br><h2>Progress Log</h2>\n<pre>\n",
+ "<br /><h2>Progress Log</h2>\n<pre>\n")),
Fd.
close_ct_master_log(Fd) ->
- io:format(Fd,"</pre>",[]),
- io:format(Fd,footer(),[]),
+ io:put_chars(Fd,["</pre>",footer()]),
file:close(Fd).
config_table(Vars) ->
@@ -216,11 +225,14 @@ config_table(Vars) ->
config_table_header() ->
["<h2>Configuration</h2>\n",
xhtml(["<table border=\"3\" cellpadding=\"5\" "
- "bgcolor=\"",?table_color,"\"\n"], "<table>\n"),
- "<tr><th>Key</th><th>Value</th></tr>\n"].
+ "bgcolor=\"",?table_color,"\"\n"],
+ ["<table id=\"",?sortable_table_name,"\">\n",
+ "<thead>\n"]),
+ "<tr><th>Key</th><th>Value</th></tr>\n",
+ xhtml("", "</thead>\n<tbody>\n")].
config_table1([]) ->
- ["</table>\n"].
+ ["</tbody>\n</table>\n"].
int_header() ->
"<div class=\"ct_internal\"><b>*** CT MASTER ~s *** ~s</b>".
@@ -233,31 +245,33 @@ int_footer() ->
open_nodedir_index(Dir,StartTime) ->
FullName = filename:join(Dir,?nodedir_index_name),
{ok,Fd} = file:open(FullName,[write]),
- io:format(Fd,nodedir_index_header(StartTime),[]),
+ io:put_chars(Fd,nodedir_index_header(StartTime)),
Fd.
print_nodedir(Node,RunDir,Fd) ->
Index = filename:join(RunDir,"index.html"),
- io:format(Fd,
- ["<tr>\n"
- "<td align=center>",atom_to_list(Node),"</td>\n",
- "<td align=left><a href=\"",Index,"\">",Index,"</a></td>\n",
- "</tr>\n"],[]),
+ io:put_chars(Fd,
+ ["<tr>\n"
+ "<td align=center>",atom_to_list(Node),"</td>\n",
+ "<td align=left><a href=\"",Index,"\">",Index,"</a></td>\n",
+ "</tr>\n"]),
ok.
close_nodedir_index(Fd) ->
- io:format(Fd,index_footer(),[]),
+ io:put_chars(Fd,index_footer()),
file:close(Fd).
nodedir_index_header(StartTime) ->
- [header("Log Files " ++ format_time(StartTime)) |
+ [header("Log Files " ++ format_time(StartTime), {[],[1,2],[]}) |
["<center>\n",
"<p><a href=\"",?ct_master_log_name,"\">Common Test Master Log</a></p>",
xhtml(["<table border=\"3\" cellpadding=\"5\" "
- "bgcolor=\"",?table_color,"\">\n"], "<table>\n"),
+ "bgcolor=\"",?table_color,"\">\n"],
+ ["<table id=\"",?sortable_table_name,"\">\n",
+ "<thead>\n<tr>\n"]),
"<th><b>Node</b></th>\n",
"<th><b>Log</b></th>\n",
- "\n"]].
+ xhtml("", "</tr>\n</thead>\n<tbody>\n")]].
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% All Run Index functions %%%
@@ -315,14 +329,16 @@ runentry(Dir) ->
"</tr>\n"].
all_runs_header() ->
- [header("Master Test Runs") |
+ [header("Master Test Runs", {[1],[2,3],[]}) |
["<center>\n",
xhtml(["<table border=\"3\" cellpadding=\"5\" "
- "bgcolor=\"",?table_color,"\">\n"], "<table>\n"),
+ "bgcolor=\"",?table_color,"\">\n"],
+ ["<table id=\"",?sortable_table_name,"\">\n",
+ "<thead>\n<tr>\n"]),
"<th><b>History</b></th>\n"
"<th><b>Master Host</b></th>\n"
- "<th><b>Test Nodes</b></th>\n"
- "\n"]].
+ "<th><b>Test Nodes</b></th>\n",
+ xhtml("", "</tr></thead>\n<tbody>\n")]].
timestamp(Dir) ->
[S,Min,H,D,M,Y|_] = lists:reverse(string:tokens(Dir,".-_")),
@@ -346,9 +362,16 @@ read_details_file(Dir) ->
%%% Internal functions
%%%--------------------------------------------------------------------
-header(Title) ->
+header(Title, TableCols) ->
CSSFile = xhtml(fun() -> "" end,
- fun() -> make_relative(locate_default_css_file()) end),
+ fun() -> make_relative(locate_priv_file(?css_default)) end),
+ JQueryFile =
+ xhtml(fun() -> "" end,
+ fun() -> make_relative(locate_priv_file(?jquery_script)) end),
+ TableSorterFile =
+ xhtml(fun() -> "" end,
+ fun() -> make_relative(locate_priv_file(?tablesorter_script)) end),
+
[xhtml(["<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n",
"<html>\n"],
["<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n",
@@ -360,6 +383,16 @@ header(Title) ->
"<meta http-equiv=\"cache-control\" content=\"no-cache\">\n",
xhtml("",
["<link rel=\"stylesheet\" href=\"",CSSFile,"\" type=\"text/css\">"]),
+ xhtml("",
+ ["<script type=\"text/javascript\" src=\"",JQueryFile,
+ "\"></script>\n"]),
+ xhtml("",
+ ["<script type=\"text/javascript\" src=\"",TableSorterFile,
+ "\"></script>\n"]),
+ xhtml(fun() -> "" end,
+ fun() -> ct_logs:insert_javascript({tablesorter,
+ ?sortable_table_name,
+ TableCols}) end),
"</head>\n",
body_tag(),
"<center>\n",
@@ -367,7 +400,7 @@ header(Title) ->
"</center>\n"].
index_footer() ->
- ["</table>\n"
+ ["</tbody>\n</table>\n"
"</center>\n" | footer()].
footer() ->
@@ -393,7 +426,7 @@ current_time() ->
format_time({{Y, Mon, D}, {H, Min, S}}) ->
Weekday = weekday(calendar:day_of_the_week(Y, Mon, D)),
- lists:flatten(io_lib:format("~s ~s ~p ~w ~2.2.0w:~2.2.0w:~2.2.0w",
+ lists:flatten(io_lib:format("~s ~s ~2.2.0w ~w ~2.2.0w:~2.2.0w:~2.2.0w",
[Weekday, month(Mon), D, Y, H, Min, S])).
weekday(1) -> "Mon";
@@ -446,8 +479,8 @@ basic_html() ->
xhtml(HTML, XHTML) ->
ct_logs:xhtml(HTML, XHTML).
-locate_default_css_file() ->
- ct_logs:locate_default_css_file().
+locate_priv_file(File) ->
+ ct_logs:locate_priv_file(File).
make_relative(Dir) ->
ct_logs:make_relative(Dir).
diff --git a/lib/common_test/src/ct_netconfc.erl b/lib/common_test/src/ct_netconfc.erl
new file mode 100644
index 0000000000..1ccbc86d8f
--- /dev/null
+++ b/lib/common_test/src/ct_netconfc.erl
@@ -0,0 +1,1858 @@
+%%----------------------------------------------------------------------
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, 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_netconfc.erl
+%%
+%% Description:
+%% This file contains the Netconf client interface
+%%
+%% @author Support
+%%
+%% @doc Netconf client module.
+%%
+%% <p>The Netconf client is compliant with RFC4741 and RFC4742.</p>
+%%
+%% <p> For each server to test against, the following entry can be
+%% added to a configuration file:</p>
+%%
+%% <p>`{server_id(),options()}.'</p>
+%%
+%% <p> The `server_id()' or an associated `target_name()' (see
+%% {@link ct}) shall then be used in calls to {@link open/2}.</p>
+%%
+%% <p>If no configuration exists for a server, a session can still be
+%% opened by calling {@link open/2} with all necessary options given
+%% in the call. The first argument to {@link open/2} can then be any
+%% atom.</p>
+%%
+%% == Logging ==
+%%
+%% The netconf server uses the `error_logger' for logging of netconf
+%% traffic. A special purpose error handler is implemented in
+%% `ct_conn_log_h'. To use this error handler, add the `cth_conn_log'
+%% hook in your test suite, e.g.
+%%
+%% ```
+%% suite() ->
+%% [{ct_hooks, [{cth_conn_log, [{conn_mod(),hook_options()}]}]}].
+%%'''
+%%
+%% The `conn_mod()' is the name of the common_test module implementing
+%% the connection protocol, e.g. `ct_netconfc'.
+%%
+%% The hook option `log_type' specifies the type of logging:
+%%
+%% <dl>
+%% <dt>`raw'</dt>
+%% <dd>The sent and received netconf data is logged to a separate
+%% text file as is without any formatting. A link to the file is
+%% added to the test case HTML log.</dd>
+%%
+%% <dt>`pretty'</dt>
+%% <dd>The sent and received netconf data is logged to a separate
+%% text file with XML data nicely indented. A link to the file is
+%% added to the test case HTML log.</dd>
+%%
+%% <dt>`html (default)'</dt>
+%% <dd>The sent and received netconf traffic is pretty printed
+%% directly in the test case HTML log.</dd>
+%%
+%% <dt>`silent'</dt>
+%% <dd>Netconf traffic is not logged.</dd>
+%% </dl>
+%%
+%% By default, all netconf traffic is logged in one single log
+%% file. However, it is possible to have different connections logged
+%% in separate files. To do this, use the hook option `hosts' and
+%% list the names of the servers/connections that will be used in the
+%% suite. Note that the connections must be named for this to work,
+%% i.e. they must be opened with {@link open/2}.
+%%
+%% The `hosts' option has no effect if `log_type' is set to `html' or
+%% `silent'.
+%%
+%% The hook options can also be specified in a configuration file with
+%% the configuration variable `ct_conn_log':
+%%
+%% ```
+%% {ct_conn_log,[{conn_mod(),hook_options()}]}.
+%% '''
+%%
+%% For example:
+%%
+%% ```
+%% {ct_conn_log,[{ct_netconfc,[{log_type,pretty},
+%% {hosts,[key_or_name()]}]}]}
+%% '''
+%%
+%% <b>Note</b> that hook options specified in a configuration file
+%% will overwrite the hardcoded hook options in the test suite.
+%%
+%% === Logging example 1 ===
+%%
+%% The following `ct_hooks' statement will cause pretty printing of
+%% netconf traffic to separate logs for the connections named
+%% `nc_server1' and `nc_server2'. Any other connections will be logged
+%% to default netconf log.
+%%
+%% ```
+%% suite() ->
+%% [{ct_hooks, [{cth_conn_log, [{ct_netconfc,[{log_type,pretty}},
+%% {hosts,[nc_server1,nc_server2]}]}
+%% ]}]}].
+%%'''
+%%
+%% Connections must be opened like this:
+%%
+%% ```
+%% open(nc_server1,[...]),
+%% open(nc_server2,[...]).
+%% '''
+%%
+%% === Logging example 2 ===
+%%
+%% The following configuration file will cause raw logging of all
+%% netconf traffic into one single text file.
+%%
+%% ```
+%% {ct_conn_log,[{ct_netconfc,[{log_type,raw}]}]}.
+%% '''
+%%
+%% The `ct_hooks' statement must look like this:
+%%
+%% ```
+%% suite() ->
+%% [{ct_hooks, [{cth_conn_log, []}]}].
+%% '''
+%%
+%% The same `ct_hooks' statement without the configuration file would
+%% cause HTML logging of all netconf connections into the test case
+%% HTML log.
+%%
+%% == Notifications ==
+%%
+%% The netconf client is also compliant with RFC5277 NETCONF Event
+%% Notifications, which defines a mechanism for an asynchronous
+%% message notification delivery service for the netconf protocol.
+%%
+%% Specific functions to support this are {@link
+%% create_subscription/6} and {@link get_event_streams/3}. (The
+%% functions also exist with other arities.)
+%%
+%% @end
+%%----------------------------------------------------------------------
+-module(ct_netconfc).
+
+-include("ct_netconfc.hrl").
+-include("ct_util.hrl").
+-include_lib("xmerl/include/xmerl.hrl").
+
+%%----------------------------------------------------------------------
+%% External exports
+%%----------------------------------------------------------------------
+-export([open/1,
+ open/2,
+ only_open/1,
+ only_open/2,
+ hello/1,
+ hello/2,
+ close_session/1,
+ close_session/2,
+ kill_session/2,
+ kill_session/3,
+ send/2,
+ send/3,
+ send_rpc/2,
+ send_rpc/3,
+ lock/2,
+ lock/3,
+ unlock/2,
+ unlock/3,
+ get/2,
+ get/3,
+ get_config/3,
+ get_config/4,
+ edit_config/3,
+ edit_config/4,
+ delete_config/2,
+ delete_config/3,
+ copy_config/3,
+ copy_config/4,
+ action/2,
+ action/3,
+ create_subscription/1,
+ create_subscription/2,
+ create_subscription/3,
+ create_subscription/4,
+ create_subscription/5,
+ create_subscription/6,
+ get_event_streams/2,
+ get_event_streams/3,
+ get_capabilities/1,
+ get_capabilities/2,
+ get_session_id/1,
+ get_session_id/2]).
+
+%%----------------------------------------------------------------------
+%% Exported types
+%%----------------------------------------------------------------------
+-export_type([hook_options/0,
+ conn_mod/0,
+ log_type/0,
+ key_or_name/0,
+ notification/0]).
+
+%%----------------------------------------------------------------------
+%% Internal exports
+%%----------------------------------------------------------------------
+%% ct_gen_conn callbacks
+-export([init/3,
+ handle_msg/3,
+ handle_msg/2,
+ terminate/2,
+ close/1]).
+
+%% ct_conn_log callback
+-export([format_data/2]).
+
+%%----------------------------------------------------------------------
+%% Internal defines
+%%----------------------------------------------------------------------
+-define(APPLICATION,?MODULE).
+-define(VALID_SSH_OPTS,[user, password, user_dir]).
+-define(DEFAULT_STREAM,"NETCONF").
+
+-define(error(ConnName,Report),
+ error_logger:error_report([{ct_connection,ConnName},
+ {client,self()},
+ {module,?MODULE},
+ {line,?LINE} |
+ Report])).
+
+-define(is_timeout(T), (is_integer(T) orelse T==infinity)).
+-define(is_filter(F),
+ (is_atom(F) orelse (is_tuple(F) andalso is_atom(element(1,F))))).
+-define(is_string(S), (is_list(S) andalso is_integer(hd(S)))).
+
+%%----------------------------------------------------------------------
+%% Records
+%%----------------------------------------------------------------------
+%% Client state
+-record(state, {host,
+ port,
+ connection, % #connection
+ capabilities,
+ session_id,
+ msg_id = 1,
+ hello_status,
+ buff = <<>>,
+ pending = [], % [#pending]
+ event_receiver}).% pid
+
+%% Run-time client options.
+-record(options, {ssh = [], % Options for the ssh application
+ host,
+ port = ?DEFAULT_PORT,
+ timeout = ?DEFAULT_TIMEOUT,
+ name}).
+
+%% Connection reference
+-record(connection, {reference, % {CM,Ch}
+ host,
+ port,
+ name}).
+
+%% Pending replies from server
+-record(pending, {tref, % timer ref (returned from timer:xxx)
+ ref, % pending ref
+ msg_id,
+ op,
+ caller}).% pid which sent the request
+
+%%----------------------------------------------------------------------
+%% Type declarations
+%%----------------------------------------------------------------------
+-type client() :: handle() | server_id() | target_name().
+-type handle() :: term().
+%% An opaque reference for a connection (netconf session). See {@link
+%% ct} for more information.
+
+-type server_id() :: atom().
+%% A `ServerId' which exists in a configuration file.
+-type target_name() :: atom().
+%% A name which is associated to a `server_id()' via a
+%% `require' statement or a call to {@link ct:require/2} in the
+%% test suite.
+-type key_or_name() :: server_id() | target_name().
+
+-type options() :: [option()].
+%% Options used for setting up ssh connection to a netconf server.
+
+-type option() :: {ssh,host()} | {port,inet:port_number()} | {user,string()} |
+ {password,string()} | {user_dir,string()} |
+ {timeout,timeout()}.
+-type host() :: inet:hostname() | inet:ip_address().
+
+-type notification() :: {notification, xml_attributes(), notification_content()}.
+-type notification_content() :: [event_time()|simple_xml()].
+-type event_time() :: {eventTime,xml_attributes(),[xs_datetime()]}.
+
+-type stream_name() :: string().
+-type streams() :: [{stream_name(),[stream_data()]}].
+-type stream_data() :: {description,string()} |
+ {replaySupport,string()} |
+ {replayLogCreationTime,string()} |
+ {replayLogAgedTime,string()}.
+%% See XML Schema for Event Notifications found in RFC5277 for further
+%% detail about the data format for the string values.
+
+-type hook_options() :: [hook_option()].
+%% Options that can be given to `cth_conn_log' in the `ct_hook' statement.
+-type hook_option() :: {log_type,log_type()} |
+ {hosts,[key_or_name()]}.
+-type log_type() :: raw | pretty | html | silent.
+%-type error_handler() :: module().
+-type conn_mod() :: ct_netconfc.
+
+-type error_reason() :: term().
+
+-type simple_xml() :: {xml_tag(), xml_attributes(), xml_content()} |
+ {xml_tag(), xml_content()} |
+ xml_tag().
+%% <p>This type is further described in the documentation for the
+%% <tt>Xmerl</tt> application.</p>
+-type xml_tag() :: atom().
+-type xml_attributes() :: [{xml_attribute_tag(),xml_attribute_value()}].
+-type xml_attribute_tag() :: atom().
+-type xml_attribute_value() :: string().
+-type xml_content() :: [simple_xml() | iolist()].
+-type xpath() :: {xpath,string()}.
+
+-type netconf_db() :: running | startup | candidate.
+-type xs_datetime() :: string().
+%% This date and time identifyer has the same format as the XML type
+%% dateTime and compliant to RFC3339. The format is
+%% ```[-]CCYY-MM-DDThh:mm:ss[.s][Z|(+|-)hh:mm]'''
+
+%%----------------------------------------------------------------------
+%% External interface functions
+%%----------------------------------------------------------------------
+
+%%----------------------------------------------------------------------
+-spec open(Options) -> Result when
+ Options :: options(),
+ Result :: {ok,handle()} | {error,error_reason()}.
+%% @doc Open a netconf session and exchange `hello' messages.
+%%
+%% If the server options are specified in a configuration file, or if
+%% a named client is needed for logging purposes (see {@section
+%% Logging}) use {@link open/2} instead.
+%%
+%% The opaque `handler()' reference which is returned from this
+%% function is required as client identifier when calling any other
+%% function in this module.
+%%
+%% The `timeout' option (milli seconds) is used when setting up
+%% the ssh connection and when waiting for the hello message from the
+%% server. It is not used for any other purposes during the lifetime
+%% of the connection.
+%%
+%% @end
+%%----------------------------------------------------------------------
+open(Options) ->
+ open(Options,#options{},[],true).
+
+%%----------------------------------------------------------------------
+-spec open(KeyOrName, ExtraOptions) -> Result when
+ KeyOrName :: key_or_name(),
+ ExtraOptions :: options(),
+ Result :: {ok,handle()} | {error,error_reason()}.
+%% @doc Open a named netconf session and exchange `hello' messages.
+%%
+%% If `KeyOrName' is a configured `server_id()' or a
+%% `target_name()' associated with such an ID, then the options
+%% for this server will be fetched from the configuration file.
+%
+%% The `ExtraOptions' argument will be added to the options found in
+%% the configuration file. If the same options are given, the values
+%% from the configuration file will overwrite `ExtraOptions'.
+%%
+%% If the server is not specified in a configuration file, use {@link
+%% open/1} instead.
+%%
+%% The opaque `handle()' reference which is returned from this
+%% function can be used as client identifier when calling any other
+%% function in this module. However, if `KeyOrName' is a
+%% `target_name()', i.e. if the server is named via a call to
+%% `ct:require/2' or a `require' statement in the test
+%% suite, then this name may be used instead of the `handle()'.
+%%
+%% The `timeout' option (milli seconds) is used when setting up
+%% the ssh connection and when waiting for the hello message from the
+%% server. It is not used for any other purposes during the lifetime
+%% of the connection.
+%%
+%% @see ct:require/2
+%% @end
+%%----------------------------------------------------------------------
+open(KeyOrName, ExtraOpts) ->
+ open(KeyOrName, ExtraOpts, true).
+
+open(KeyOrName, ExtraOpts, Hello) ->
+ SortedExtra = lists:keysort(1,ExtraOpts),
+ SortedConfig = lists:keysort(1,ct:get_config(KeyOrName,[])),
+ AllOpts = lists:ukeymerge(1,SortedConfig,SortedExtra),
+ open(AllOpts,#options{name=KeyOrName},[{name,KeyOrName}],Hello).
+
+open(OptList,InitOptRec,NameOpt,Hello) ->
+ case check_options(OptList,undefined,undefined,InitOptRec) of
+ {Host,Port,Options} ->
+ case ct_gen_conn:start({Host,Port},Options,?MODULE,
+ NameOpt ++ [{reconnect,false},
+ {use_existing_connection,false},
+ {forward_messages,true}]) of
+ {ok,Client} when Hello==true ->
+ case hello(Client,Options#options.timeout) of
+ ok ->
+ {ok,Client};
+ Error ->
+ Error
+ end;
+ Other ->
+ Other
+ end;
+ Error ->
+ Error
+ end.
+
+
+%%----------------------------------------------------------------------
+-spec only_open(Options) -> Result when
+ Options :: options(),
+ Result :: {ok,handle()} | {error,error_reason()}.
+%% @doc Open a netconf session, but don't send `hello'.
+%%
+%% As {@link open/1} but does not send a `hello' message.
+%%
+%% @end
+%%----------------------------------------------------------------------
+only_open(Options) ->
+ open(Options,#options{},[],false).
+
+%%----------------------------------------------------------------------
+-spec only_open(KeyOrName,ExtraOptions) -> Result when
+ KeyOrName :: key_or_name(),
+ ExtraOptions :: options(),
+ Result :: {ok,handle()} | {error,error_reason()}.
+%% @doc Open a name netconf session, but don't send `hello'.
+%%
+%% As {@link open/2} but does not send a `hello' message.
+%%
+%% @end
+%%----------------------------------------------------------------------
+only_open(KeyOrName, ExtraOpts) ->
+ open(KeyOrName, ExtraOpts, false).
+
+%%----------------------------------------------------------------------
+%% @spec hello(Client) -> Result
+%% @equiv hello(Client, infinity)
+hello(Client) ->
+ hello(Client,?DEFAULT_TIMEOUT).
+
+%%----------------------------------------------------------------------
+-spec hello(Client,Timeout) -> Result when
+ Client :: handle(),
+ Timeout :: timeout(),
+ Result :: ok | {error,error_reason()}.
+%% @doc Exchange `hello' messages with the server.
+%%
+%% Sends a `hello' message to the server and waits for the return.
+%%
+%% @end
+%%----------------------------------------------------------------------
+hello(Client,Timeout) ->
+ call(Client, {hello, Timeout}).
+
+%%----------------------------------------------------------------------
+%% @spec get_session_id(Client) -> Result
+%% @equiv get_session_id(Client, infinity)
+get_session_id(Client) ->
+ get_session_id(Client, ?DEFAULT_TIMEOUT).
+
+%%----------------------------------------------------------------------
+-spec get_session_id(Client, Timeout) -> Result when
+ Client :: client(),
+ Timeout :: timeout(),
+ Result :: pos_integer() | {error,error_reason()}.
+%% @doc Returns the session id associated with the given client.
+%%
+%% @end
+%%----------------------------------------------------------------------
+get_session_id(Client, Timeout) ->
+ call(Client, get_session_id, Timeout).
+
+%%----------------------------------------------------------------------
+%% @spec get_capabilities(Client) -> Result
+%% @equiv get_capabilities(Client, infinity)
+get_capabilities(Client) ->
+ get_capabilities(Client, ?DEFAULT_TIMEOUT).
+
+%%----------------------------------------------------------------------
+-spec get_capabilities(Client, Timeout) -> Result when
+ Client :: client(),
+ Timeout :: timeout(),
+ Result :: [string()] | {error,error_reason()}.
+%% @doc Returns the server side capabilities
+%%
+%% The following capability identifiers, defined in RFC 4741, can be returned:
+%%
+%% <ul>
+%% <li>`"urn:ietf:params:netconf:base:1.0"'</li>
+%% <li>`"urn:ietf:params:netconf:capability:writable-running:1.0"'</li>
+%% <li>`"urn:ietf:params:netconf:capability:candidate:1.0"'</li>
+%% <li>`"urn:ietf:params:netconf:capability:confirmed-commit:1.0"'</li>
+%% <li>`"urn:ietf:params:netconf:capability:rollback-on-error:1.0"'</li>
+%% <li>`"urn:ietf:params:netconf:capability:startup:1.0"'</li>
+%% <li>`"urn:ietf:params:netconf:capability:url:1.0"'</li>
+%% <li>`"urn:ietf:params:netconf:capability:xpath:1.0"'</li>
+%% </ul>
+%%
+%% Note, additional identifiers may exist, e.g. server side namespace.
+%%
+%% @end
+%%----------------------------------------------------------------------
+get_capabilities(Client, Timeout) ->
+ call(Client, get_capabilities, Timeout).
+
+%% @private
+send(Client, SimpleXml) ->
+ send(Client, SimpleXml, ?DEFAULT_TIMEOUT).
+%% @private
+send(Client, SimpleXml, Timeout) ->
+ call(Client,{send, Timeout, SimpleXml}).
+
+%% @private
+send_rpc(Client, SimpleXml) ->
+ send_rpc(Client, SimpleXml, ?DEFAULT_TIMEOUT).
+%% @private
+send_rpc(Client, SimpleXml, Timeout) ->
+ call(Client,{send_rpc, SimpleXml, Timeout}).
+
+
+
+%%----------------------------------------------------------------------
+%% @spec lock(Client, Target) -> Result
+%% @equiv lock(Client, Target, infinity)
+lock(Client, Target) ->
+ lock(Client, Target,?DEFAULT_TIMEOUT).
+
+%%----------------------------------------------------------------------
+-spec lock(Client, Target, Timeout) -> Result when
+ Client :: client(),
+ Target :: netconf_db(),
+ Timeout :: timeout(),
+ Result :: ok | {error,error_reason()}.
+%% @doc Unlock configuration target.
+%%
+%% Which target parameters that can be used depends on if
+%% `:candidate' and/or `:startup' are supported by the
+%% server. If successfull, the configuration system of the device is
+%% not available to other clients (Netconf, CORBA, SNMP etc). Locks
+%% are intended to be short-lived.
+%%
+%% The operations {@link kill_session/2} or {@link kill_session/3} can
+%% be used to force the release of a lock owned by another Netconf
+%% session. How this is achieved by the server side is implementation
+%% specific.
+%%
+%% @end
+%%----------------------------------------------------------------------
+lock(Client, Target, Timeout) ->
+ call(Client,{send_rpc_op,lock,[Target],Timeout}).
+
+%%----------------------------------------------------------------------
+%% @spec unlock(Client, Target) -> Result
+%% @equiv unlock(Client, Target, infinity)
+unlock(Client, Target) ->
+ unlock(Client, Target,?DEFAULT_TIMEOUT).
+
+%%----------------------------------------------------------------------
+-spec unlock(Client, Target, Timeout) -> Result when
+ Client :: client(),
+ Target :: netconf_db(),
+ Timeout :: timeout(),
+ Result :: ok | {error,error_reason()}.
+%% @doc Unlock configuration target.
+%%
+%% If the client earlier has aquired a lock, via {@link lock/2} or
+%% {@link lock/3}, this operation release the associated lock. To be
+%% able to access another target than `running', the server must
+%% support `:candidate' and/or `:startup'.
+%%
+%% @end
+%%----------------------------------------------------------------------
+unlock(Client, Target, Timeout) ->
+ call(Client, {send_rpc_op, unlock, [Target], Timeout}).
+
+%%----------------------------------------------------------------------
+%% @spec get(Client, Filter) -> Result
+%% @equiv get(Client, Filter, infinity)
+get(Client, Filter) ->
+ get(Client, Filter, ?DEFAULT_TIMEOUT).
+
+%%----------------------------------------------------------------------
+-spec get(Client, Filter, Timeout) -> Result when
+ Client :: client(),
+ Filter :: simple_xml() | xpath(),
+ Timeout :: timeout(),
+ Result :: {ok,simple_xml()} | {error,error_reason()}.
+%% @doc Get data.
+%%
+%% This operation returns both configuration and state data from the
+%% server.
+%%
+%% Filter type `xpath' can only be used if the server supports
+%% `:xpath'.
+%%
+%% @end
+%%----------------------------------------------------------------------
+get(Client, Filter, Timeout) ->
+ call(Client,{send_rpc_op, get, [Filter], Timeout}).
+
+%%----------------------------------------------------------------------
+%% @spec get_config(Client, Source, Filter) -> Result
+%% @equiv get_config(Client, Source, Filter, infinity)
+get_config(Client, Source, Filter) ->
+ get_config(Client, Source, Filter, ?DEFAULT_TIMEOUT).
+
+%%----------------------------------------------------------------------
+-spec get_config(Client, Source, Filter, Timeout) -> Result when
+ Client :: client(),
+ Source :: netconf_db(),
+ Filter :: simple_xml() | xpath(),
+ Timeout :: timeout(),
+ Result :: {ok,simple_xml()} | {error,error_reason()}.
+%% @doc Get configuration data.
+%%
+%% To be able to access another source than `running', the server
+%% must advertise `:candidate' and/or `:startup'.
+%%
+%% Filter type `xpath' can only be used if the server supports
+%% `:xpath'.
+%%
+%%
+%% @end
+%%----------------------------------------------------------------------
+get_config(Client, Source, Filter, Timeout) ->
+ call(Client, {send_rpc_op, get_config, [Source, Filter], Timeout}).
+
+%%----------------------------------------------------------------------
+%% @spec edit_config(Client, Target, Config) -> Result
+%% @equiv edit_config(Client, Target, Config, infinity)
+edit_config(Client, Target, Config) ->
+ edit_config(Client, Target, Config, ?DEFAULT_TIMEOUT).
+
+%%----------------------------------------------------------------------
+-spec edit_config(Client, Target, Config, Timeout) -> Result when
+ Client :: client(),
+ Target :: netconf_db(),
+ Config :: simple_xml(),
+ Timeout :: timeout(),
+ Result :: ok | {error,error_reason()}.
+%% @doc Edit configuration data.
+%%
+%% Per default only the running target is available, unless the server
+%% include `:candidate' or `:startup' in its list of
+%% capabilities.
+%%
+%% @end
+%%----------------------------------------------------------------------
+edit_config(Client, Target, Config, Timeout) ->
+ call(Client, {send_rpc_op, edit_config, [Target,Config], Timeout}).
+
+
+%%----------------------------------------------------------------------
+%% @spec delete_config(Client, Target) -> Result
+%% @equiv delete_config(Client, Target, infinity)
+delete_config(Client, Target) ->
+ delete_config(Client, Target, ?DEFAULT_TIMEOUT).
+
+%%----------------------------------------------------------------------
+-spec delete_config(Client, Target, Timeout) -> Result when
+ Client :: client(),
+ Target :: startup | candidate,
+ Timeout :: timeout(),
+ Result :: ok | {error,error_reason()}.
+%% @doc Delete configuration data.
+%%
+%% The running configuration cannot be deleted and `:candidate'
+%% or `:startup' must be advertised by the server.
+%%
+%% @end
+%%----------------------------------------------------------------------
+delete_config(Client, Target, Timeout) when Target == startup;
+ Target == candidate ->
+ call(Client,{send_rpc_op, delete_config, [Target], Timeout}).
+
+%%----------------------------------------------------------------------
+%% @spec copy_config(Client, Source, Target) -> Result
+%% @equiv copy_config(Client, Source, Target, infinity)
+copy_config(Client, Source, Target) ->
+ copy_config(Client, Source, Target, ?DEFAULT_TIMEOUT).
+
+%%----------------------------------------------------------------------
+-spec copy_config(Client, Target, Source, Timeout) -> Result when
+ Client :: client(),
+ Target :: netconf_db(),
+ Source :: netconf_db(),
+ Timeout :: timeout(),
+ Result :: ok | {error,error_reason()}.
+%% @doc Copy configuration data.
+%%
+%% Which source and target options that can be issued depends on the
+%% capabilities supported by the server. I.e. `:candidate' and/or
+%% `:startup' are required.
+%%
+%% @end
+%%----------------------------------------------------------------------
+copy_config(Client, Target, Source, Timeout) ->
+ call(Client,{send_rpc_op, copy_config, [Target, Source], Timeout}).
+
+%%----------------------------------------------------------------------
+%% @spec action(Client, Action) -> Result
+%% @equiv action(Client, Action, infinity)
+action(Client,Action) ->
+ action(Client,Action,?DEFAULT_TIMEOUT).
+
+%%----------------------------------------------------------------------
+-spec action(Client, Action, Timeout) -> Result when
+ Client :: client(),
+ Action :: simple_xml(),
+ Timeout :: timeout(),
+ Result :: {ok,simple_xml()} | {error,error_reason()}.
+%% @doc Execute an action.
+%%
+%% @end
+%%----------------------------------------------------------------------
+action(Client,Action,Timeout) ->
+ call(Client,{send_rpc_op, action, [Action], Timeout}).
+
+%%----------------------------------------------------------------------
+create_subscription(Client) ->
+ create_subscription(Client,?DEFAULT_STREAM,?DEFAULT_TIMEOUT).
+
+create_subscription(Client,Timeout)
+ when ?is_timeout(Timeout) ->
+ create_subscription(Client,?DEFAULT_STREAM,Timeout);
+create_subscription(Client,Stream)
+ when is_list(Stream) ->
+ create_subscription(Client,Stream,?DEFAULT_TIMEOUT);
+create_subscription(Client,Filter)
+ when ?is_filter(Filter) ->
+ create_subscription(Client,?DEFAULT_STREAM,Filter,
+ ?DEFAULT_TIMEOUT).
+
+create_subscription(Client,Stream,Timeout)
+ when is_list(Stream) andalso
+ ?is_timeout(Timeout) ->
+ call(Client,{send_rpc_op,{create_subscription,self()},
+ [Stream,undefined,undefined,undefined],
+ Timeout});
+create_subscription(Client,StartTime,StopTime)
+ when is_list(StartTime) andalso
+ is_list(StopTime) ->
+ create_subscription(Client,?DEFAULT_STREAM,StartTime,StopTime,
+ ?DEFAULT_TIMEOUT);
+create_subscription(Client,Filter,Timeout)
+ when ?is_filter(Filter) andalso
+ ?is_timeout(Timeout) ->
+ create_subscription(Client,?DEFAULT_STREAM,Filter,Timeout);
+create_subscription(Client,Stream,Filter)
+ when is_list(Stream) andalso
+ ?is_filter(Filter) ->
+ create_subscription(Client,Stream,Filter,?DEFAULT_TIMEOUT).
+
+create_subscription(Client,StartTime,StopTime,Timeout)
+ when is_list(StartTime) andalso
+ is_list(StopTime) andalso
+ ?is_timeout(Timeout) ->
+ create_subscription(Client,?DEFAULT_STREAM,StartTime,StopTime,Timeout);
+create_subscription(Client,Stream,StartTime,StopTime)
+ when is_list(Stream) andalso
+ is_list(StartTime) andalso
+ is_list(StopTime) ->
+ create_subscription(Client,Stream,StartTime,StopTime,?DEFAULT_TIMEOUT);
+create_subscription(Client,Filter,StartTime,StopTime)
+ when ?is_filter(Filter) andalso
+ is_list(StartTime) andalso
+ is_list(StopTime) ->
+ create_subscription(Client,?DEFAULT_STREAM,Filter,
+ StartTime,StopTime,?DEFAULT_TIMEOUT);
+create_subscription(Client,Stream,Filter,Timeout)
+ when is_list(Stream) andalso
+ ?is_filter(Filter) andalso
+ ?is_timeout(Timeout) ->
+ call(Client,{send_rpc_op,{create_subscription,self()},
+ [Stream,Filter,undefined,undefined],
+ Timeout}).
+
+create_subscription(Client,Stream,StartTime,StopTime,Timeout)
+ when is_list(Stream) andalso
+ is_list(StartTime) andalso
+ is_list(StopTime) andalso
+ ?is_timeout(Timeout) ->
+ call(Client,{send_rpc_op,{create_subscription,self()},
+ [Stream,undefined,StartTime,StopTime],
+ Timeout});
+create_subscription(Client,Stream,Filter,StartTime,StopTime)
+ when is_list(Stream) andalso
+ ?is_filter(Filter) andalso
+ is_list(StartTime) andalso
+ is_list(StopTime) ->
+ create_subscription(Client,Stream,Filter,StartTime,StopTime,?DEFAULT_TIMEOUT).
+
+%%----------------------------------------------------------------------
+-spec create_subscription(Client, Stream, Filter,StartTime, StopTime, Timeout) ->
+ Result when
+ Client :: client(),
+ Stream :: stream_name(),
+ Filter :: simple_xml(),
+ StartTime :: xs_datetime(),
+ StopTime :: xs_datetime(),
+ Timeout :: timeout(),
+ Result :: ok | {error,error_reason()}.
+%% @doc Create a subscription for event notifications.
+%%
+%% This function sets up a subscription for netconf event
+%% notifications of the given stream type, matching the given
+%% filter. The calling process will receive notifications as messages
+%% of type `notification()'.
+%%
+%% <dl>
+%% <dt>Stream:</dt>
+%% <dd> An optional parameter that indicates which stream of events
+%% is of interest. If not present, events in the default NETCONF
+%% stream will be sent.</dd>
+%%
+%% <dt>Filter:</dt>
+%% <dd>An optional parameter that indicates which subset of all
+%% possible events is of interest. The format of this parameter is
+%% the same as that of the filter parameter in the NETCONF protocol
+%% operations. If not present, all events not precluded by other
+%% parameters will be sent. See section 3.6 for more information on
+%% filters.</dd>
+%%
+%% <dt>StartTime:</dt>
+%% <dd>An optional parameter used to trigger the replay feature and
+%% indicate that the replay should start at the time specified. If
+%% `StartTime' is not present, this is not a replay subscription.
+%% It is not valid to specify start times that are later than the
+%% current time. If the `StartTime' specified is earlier than the
+%% log can support, the replay will begin with the earliest
+%% available notification. This parameter is of type dateTime and
+%% compliant to [RFC3339]. Implementations must support time
+%% zones.</dd>
+%%
+%% <dt>StopTime:</dt>
+%% <dd>An optional parameter used with the optional replay feature
+%% to indicate the newest notifications of interest. If `StopTime'
+%% is not present, the notifications will continue until the
+%% subscription is terminated. Must be used with and be later than
+%% `StartTime'. Values of `StopTime' in the future are valid. This
+%% parameter is of type dateTime and compliant to [RFC3339].
+%% Implementations must support time zones.</dd>
+%% </dl>
+%%
+%% See RFC5277 for further details about the event notification
+%% mechanism.
+%%
+%% @end
+%%----------------------------------------------------------------------
+create_subscription(Client,Stream,Filter,StartTime,StopTime,Timeout) ->
+ call(Client,{send_rpc_op,{create_subscription, self()},
+ [Stream,Filter,StartTime,StopTime],
+ Timeout}).
+
+%%----------------------------------------------------------------------
+%% @spec get_event_streams(Client, Timeout) -> Result
+%% @equiv get_event_streams(Client, [], Timeout)
+get_event_streams(Client,Timeout) when is_integer(Timeout); Timeout==infinity ->
+ get_event_streams(Client,[],Timeout);
+
+%%----------------------------------------------------------------------
+%% @spec get_event_streams(Client, Streams) -> Result
+%% @equiv get_event_streams(Client, Streams, infinity)
+get_event_streams(Client,Streams) when is_list(Streams) ->
+ get_event_streams(Client,Streams,?DEFAULT_TIMEOUT).
+
+%%----------------------------------------------------------------------
+-spec get_event_streams(Client, Streams, Timeout)
+ -> Result when
+ Client :: client(),
+ Streams :: [stream_name()],
+ Timeout :: timeout(),
+ Result :: {ok,streams()} | {error,error_reason()}.
+%% @doc Send a request to get the given event streams.
+%%
+%% `Streams' is a list of stream names. The following filter will
+%% be sent to the netconf server in a `get' request:
+%%
+%% ```
+%% <netconf xmlns="urn:ietf:params:xml:ns:netmod:notification">
+%% <streams>
+%% <stream>
+%% <name>StreamName1</name>
+%% </stream>
+%% <stream>
+%% <name>StreamName2</name>
+%% </stream>
+%% ...
+%% </streams>
+%% </netconf>
+%% '''
+%%
+%% If `Streams' is an empty list, ALL streams will be requested
+%% by sending the following filter:
+%%
+%% ```
+%% <netconf xmlns="urn:ietf:params:xml:ns:netmod:notification">
+%% <streams/>
+%% </netconf>
+%% '''
+%%
+%% If more complex filtering is needed, a use {@link get/2} or {@link
+%% get/3} and specify the exact filter according to XML Schema for
+%% Event Notifications found in RFC5277.
+%%
+%% @end
+%%----------------------------------------------------------------------
+get_event_streams(Client,Streams,Timeout) ->
+ call(Client,{get_event_streams,Streams,Timeout}).
+
+
+%%----------------------------------------------------------------------
+%% @spec close_session(Client) -> Result
+%% @equiv close_session(Client, infinity)
+close_session(Client) ->
+ close_session(Client, ?DEFAULT_TIMEOUT).
+
+%%----------------------------------------------------------------------
+-spec close_session(Client, Timeout) -> Result when
+ Client :: client(),
+ Timeout :: timeout(),
+ Result :: ok | {error,error_reason()}.
+%% @doc Request graceful termination of the session associated with the client.
+%%
+%% When a netconf server receives a `close-session' request, it
+%% will gracefully close the session. The server will release any
+%% locks and resources associated with the session and gracefully
+%% close any associated connections. Any NETCONF requests received
+%% after a `close-session' request will be ignored.
+%%
+%% @end
+%%----------------------------------------------------------------------
+close_session(Client, Timeout) ->
+ call(Client,{send_rpc_op, close_session, [], Timeout}, true).
+
+
+%%----------------------------------------------------------------------
+%% @spec kill_session(Client, SessionId) -> Result
+%% @equiv kill_session(Client, SessionId, infinity)
+kill_session(Client, SessionId) ->
+ kill_session(Client, SessionId, ?DEFAULT_TIMEOUT).
+
+%%----------------------------------------------------------------------
+-spec kill_session(Client, SessionId, Timeout) -> Result when
+ Client :: client(),
+ SessionId :: pos_integer(),
+ Timeout :: timeout(),
+ Result :: ok | {error,error_reason()}.
+%% @doc Force termination of the session associated with the supplied
+%% session id.
+%%
+%% The server side shall abort any operations currently in process,
+%% release any locks and resources associated with the session, and
+%% close any associated connections.
+%%
+%% Only if the server is in the confirmed commit phase, the
+%% configuration will be restored to its state before entering the
+%% confirmed commit phase. Otherwise, no configuration roll back will
+%% be performed.
+%%
+%% If the given `SessionId' is equal to the current session id,
+%% an error will be returned.
+%%
+%% @end
+%% ----------------------------------------------------------------------
+kill_session(Client, SessionId, Timeout) ->
+ call(Client,{send_rpc_op, kill_session, [SessionId], Timeout}).
+
+
+%%----------------------------------------------------------------------
+%% Callback functions
+%%----------------------------------------------------------------------
+
+%% @private
+init(_KeyOrName,{_Host,_Port},Options) ->
+ case ssh_open(Options) of
+ {ok, Connection} ->
+ log(Connection,open),
+ {ConnPid,_} = Connection#connection.reference,
+ {ok, ConnPid, #state{connection = Connection}};
+ {error,Reason}->
+ {error,Reason}
+ end.
+
+%% @private
+terminate(_, #state{connection=Connection}) ->
+ ssh_close(Connection),
+ log(Connection,close),
+ ok.
+
+%% @private
+handle_msg({hello,Timeout}, From,
+ #state{connection=Connection,hello_status=HelloStatus} = State) ->
+ case do_send(Connection, client_hello()) of
+ ok ->
+ case HelloStatus of
+ undefined ->
+ {Ref,TRef} = set_request_timer(Timeout),
+ {noreply, State#state{hello_status=#pending{tref=TRef,
+ ref=Ref,
+ caller=From}}};
+ received ->
+ {reply, ok, State#state{hello_status=done}};
+ {error,Reason} ->
+ {stop, {error,Reason}, State}
+ end;
+ Error ->
+ {stop, Error, State}
+ end;
+handle_msg(_, _From, #state{session_id=undefined} = State) ->
+ %% Hello is not yet excanged - this shall never happen
+ {reply,{error,waiting_for_hello},State};
+handle_msg(get_capabilities, _From, #state{capabilities = Caps} = State) ->
+ {reply, Caps, State};
+handle_msg(get_session_id, _From, #state{session_id = Id} = State) ->
+ {reply, Id, State};
+handle_msg({send, Timeout, SimpleXml}, From,
+ #state{connection=Connection,pending=Pending} = State) ->
+ case do_send(Connection, SimpleXml) of
+ ok ->
+ {Ref,TRef} = set_request_timer(Timeout),
+ {noreply, State#state{pending=[#pending{tref=TRef,
+ ref=Ref,
+ caller=From} | Pending]}};
+ Error ->
+ {reply, Error, State}
+ end;
+handle_msg({send_rpc, SimpleXml, Timeout}, From, State) ->
+ do_send_rpc(undefined, SimpleXml, Timeout, From, State);
+handle_msg({send_rpc_op, Op, Data, Timeout}, From, State) ->
+ SimpleXml = encode_rpc_operation(Op,Data),
+ do_send_rpc(Op, SimpleXml, Timeout, From, State);
+handle_msg({get_event_streams=Op,Streams,Timeout}, From, State) ->
+ Filter = {netconf,?NETMOD_NOTIF_NAMESPACE_ATTR,
+ [{streams,[{stream,[{name,[Name]}]} || Name <- Streams]}]},
+ SimpleXml = encode_rpc_operation(get,[Filter]),
+ do_send_rpc(Op, SimpleXml, Timeout, From, State).
+
+handle_msg({ssh_cm, CM, {data, Ch, _Type, Data}}, State) ->
+ ssh_connection:adjust_window(CM,Ch,size(Data)),
+ handle_data(Data, State);
+handle_msg({ssh_cm, _CM, _SshCloseMsg}, State) ->
+ %% _SshCloseMsg can probably be one of
+ %% {eof,Ch}
+ %% {exit_status,Ch,Status}
+ %% {exit_signal,Ch,ExitSignal,ErrorMsg,LanguageString}
+ %% {signal,Ch,Signal}
+
+ %% This might e.g. happen if the server terminates the connection,
+ %% as in kill-session (or if ssh:close is called from somewhere
+ %% unexpected).
+
+ %%! Log this??
+ %%! Currently the log will say that the client closed the
+ %%! connection - due to terminate/2
+
+ {stop, State};
+handle_msg({Ref,timeout},
+ #state{hello_status=#pending{ref=Ref,caller=Caller}} = State) ->
+ ct_gen_conn:return(Caller,{error,{hello_session_failed,timeout}}),
+ {stop,State#state{hello_status={error,timeout}}};
+handle_msg({Ref,timeout},#state{pending=Pending} = State) ->
+ {value,#pending{caller=Caller},Pending1} =
+ lists:keytake(Ref,#pending.ref,Pending),
+ ct_gen_conn:return(Caller,{error,timeout}),
+ {noreply,State#state{pending=Pending1}}.
+
+%% @private
+%% Called by ct_util_server to close registered connections before terminate.
+close(Client) ->
+ case get_handle(Client) of
+ {ok,Pid} ->
+ case ct_gen_conn:stop(Pid) of
+ {error,{process_down,Pid,noproc}} ->
+ {error,already_closed};
+ Result ->
+ Result
+ end;
+ Error ->
+ Error
+ end.
+
+
+%%----------------------------------------------------------------------
+%% Internal functions
+%%----------------------------------------------------------------------
+call(Client, Msg) ->
+ call(Client, Msg, infinity, false).
+call(Client, Msg, Timeout) when is_integer(Timeout); Timeout==infinity ->
+ call(Client, Msg, Timeout, false);
+call(Client, Msg, WaitStop) when is_boolean(WaitStop) ->
+ call(Client, Msg, infinity, WaitStop).
+call(Client, Msg, Timeout, WaitStop) ->
+ case get_handle(Client) of
+ {ok,Pid} ->
+ case ct_gen_conn:call(Pid,Msg,Timeout) of
+ {error,{process_down,Pid,noproc}} ->
+ {error,no_such_client};
+ {error,{process_down,Pid,normal}} when WaitStop ->
+ %% This will happen when server closes connection
+ %% before clien received rpc-reply on
+ %% close-session.
+ ok;
+ {error,{process_down,Pid,normal}} ->
+ {error,closed};
+ {error,{process_down,Pid,Reason}} ->
+ {error,{closed,Reason}};
+ Other when WaitStop ->
+ MRef = erlang:monitor(process,Pid),
+ receive
+ {'DOWN',MRef,process,Pid,Normal} when Normal==normal;
+ Normal==noproc ->
+ Other;
+ {'DOWN',MRef,process,Pid,Reason} ->
+ {error,{{closed,Reason},Other}}
+ after Timeout ->
+ erlang:demonitor(MRef, [flush]),
+ {error,{timeout,Other}}
+ end;
+ Other ->
+ Other
+ end;
+ Error ->
+ Error
+ end.
+
+get_handle(Client) when is_pid(Client) ->
+ {ok,Client};
+get_handle(Client) ->
+ case ct_util:get_connections(Client, ?MODULE) of
+ {ok,[{Pid,_}]} ->
+ {ok,Pid};
+ {ok,[]} ->
+ {error,{no_connection_found,Client}};
+ {ok,Conns} ->
+ {error,{multiple_connections_found,Client,Conns}};
+ Error ->
+ Error
+ end.
+
+check_options([], undefined, _Port, _Options) ->
+ {error, no_host_address};
+check_options([], _Host, undefined, _Options) ->
+ {error, no_port};
+check_options([], Host, Port, Options) ->
+ {Host,Port,Options};
+check_options([{ssh, Host}|T], _, Port, #options{} = Options) ->
+ check_options(T, Host, Port, Options#options{host=Host});
+check_options([{port,Port}|T], Host, _, #options{} = Options) ->
+ check_options(T, Host, Port, Options#options{port=Port});
+check_options([{timeout, Timeout}|T], Host, Port, Options)
+ when is_integer(Timeout); Timeout==infinity ->
+ check_options(T, Host, Port, Options#options{timeout = Timeout});
+check_options([{X,_}=Opt|T], Host, Port, #options{ssh=SshOpts}=Options) ->
+ case lists:member(X,?VALID_SSH_OPTS) of
+ true ->
+ check_options(T, Host, Port, Options#options{ssh=[Opt|SshOpts]});
+ false ->
+ {error, {invalid_option, Opt}}
+ end.
+
+%%%-----------------------------------------------------------------
+set_request_timer(infinity) ->
+ {undefined,undefined};
+set_request_timer(T) ->
+ Ref = make_ref(),
+ {ok,TRef} = timer:send_after(T,{Ref,timeout}),
+ {Ref,TRef}.
+
+
+%%%-----------------------------------------------------------------
+client_hello() ->
+ {hello, ?NETCONF_NAMESPACE_ATTR,
+ [{capabilities,
+ [{capability,[?NETCONF_BASE_CAP++?NETCONF_BASE_CAP_VSN]}]}]}.
+
+%%%-----------------------------------------------------------------
+
+encode_rpc_operation(Lock,[Target]) when Lock==lock; Lock==unlock ->
+ {Lock,[{target,[Target]}]};
+encode_rpc_operation(get,[Filter]) ->
+ {get,filter(Filter)};
+encode_rpc_operation(get_config,[Source,Filter]) ->
+ {'get-config',[{source,[Source]}] ++ filter(Filter)};
+encode_rpc_operation(edit_config,[Target,Config]) ->
+ {'edit-config',[{target,[Target]},{config,[Config]}]};
+encode_rpc_operation(delete_config,[Target]) ->
+ {'delete-config',[{target,[Target]}]};
+encode_rpc_operation(copy_config,[Target,Source]) ->
+ {'copy-config',[{target,[Target]},{source,[Source]}]};
+encode_rpc_operation(action,[Action]) ->
+ {action,?ACTION_NAMESPACE_ATTR,[{data,[Action]}]};
+encode_rpc_operation(kill_session,[SessionId]) ->
+ {'kill-session',[{'session-id',[integer_to_list(SessionId)]}]};
+encode_rpc_operation(close_session,[]) ->
+ 'close-session';
+encode_rpc_operation({create_subscription,_},
+ [Stream,Filter,StartTime,StopTime]) ->
+ {'create-subscription',?NETCONF_NOTIF_NAMESPACE_ATTR,
+ [{stream,[Stream]}] ++
+ filter(Filter) ++
+ maybe_element(startTime,StartTime) ++
+ maybe_element(stopTime,StopTime)}.
+
+filter(undefined) ->
+ [];
+filter({xpath,Filter}) when ?is_string(Filter) ->
+ [{filter,[{type,"xpath"},{select, Filter}],[]}];
+filter(Filter) ->
+ [{filter,[{type,"subtree"}],[Filter]}].
+
+maybe_element(_,undefined) ->
+ [];
+maybe_element(Tag,Value) ->
+ [{Tag,[Value]}].
+
+%%%-----------------------------------------------------------------
+%%% Send XML data to server
+do_send_rpc(PendingOp,SimpleXml,Timeout,Caller,
+ #state{connection=Connection,msg_id=MsgId,pending=Pending} = State) ->
+ case do_send_rpc(Connection, MsgId, SimpleXml) of
+ ok ->
+ {Ref,TRef} = set_request_timer(Timeout),
+ {noreply, State#state{msg_id=MsgId+1,
+ pending=[#pending{tref=TRef,
+ ref=Ref,
+ msg_id=MsgId,
+ op=PendingOp,
+ caller=Caller} | Pending]}};
+ Error ->
+ {reply, Error, State#state{msg_id=MsgId+1}}
+ end.
+
+do_send_rpc(Connection, MsgId, SimpleXml) ->
+ do_send(Connection,
+ {rpc,
+ [{'message-id',MsgId} | ?NETCONF_NAMESPACE_ATTR],
+ [SimpleXml]}).
+
+do_send(Connection, SimpleXml) ->
+ Xml=to_xml_doc(SimpleXml),
+ log(Connection,send,Xml),
+ ssh_send(Connection, Xml).
+
+to_xml_doc(Simple) ->
+ Prolog = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>",
+ Xml = list_to_binary(xmerl:export_simple([Simple],
+ xmerl_xml,
+ [#xmlAttribute{name=prolog,
+ value=Prolog}])),
+ <<Xml/binary,?END_TAG/binary>>.
+
+%%%-----------------------------------------------------------------
+%%% Parse and handle received XML data
+handle_data(NewData,#state{connection=Connection,buff=Buff} = State) ->
+ log(Connection,recv,NewData),
+ Data = <<Buff/binary,NewData/binary>>,
+ case xmerl_sax_parser:stream(<<>>,
+ [{continuation_fun,fun sax_cont/1},
+ {continuation_state,{Data,Connection,false}},
+ {event_fun,fun sax_event/3},
+ {event_state,[]}]) of
+ {ok, Simple, Rest} ->
+ decode(Simple,State#state{buff=Rest});
+ {fatal_error,_Loc,Reason,_EndTags,_EventState} ->
+ ?error(Connection#connection.name,[{parse_error,Reason},
+ {data,Data}]),
+ case Reason of
+ {could_not_fetch_data,Msg} ->
+ handle_msg(Msg,State#state{buff = <<>>});
+ _Other ->
+ Pending1 =
+ case State#state.pending of
+ [] ->
+ [];
+ Pending ->
+ %% Assuming the first request gets the
+ %% first answer
+ P=#pending{tref=TRef,caller=Caller} =
+ lists:last(Pending),
+ timer:cancel(TRef),
+ Reason1 = {failed_to_parse_received_data,Reason},
+ ct_gen_conn:return(Caller,{error,Reason1}),
+ lists:delete(P,Pending)
+ end,
+ {noreply,State#state{pending=Pending1,buff = <<>>}}
+ end
+ end.
+
+%%%-----------------------------------------------------------------
+%%% Parsing of XML data
+%% Contiuation function for the sax parser
+sax_cont(done) ->
+ {<<>>,done};
+sax_cont({Data,Connection,false}) ->
+ case binary:split(Data,[?END_TAG],[]) of
+ [All] ->
+ %% No end tag found. Remove what could be a part
+ %% of an end tag from the data and save for next
+ %% iteration
+ SafeSize = size(All)-5,
+ <<New:SafeSize/binary,Save:5/binary>> = All,
+ {New,{Save,Connection,true}};
+ [_Msg,_Rest]=Msgs ->
+ %% We have at least one full message. Any excess data will
+ %% be returned from xmerl_sax_parser:stream/2 in the Rest
+ %% parameter.
+ {list_to_binary(Msgs),done}
+ end;
+sax_cont({Data,Connection,true}) ->
+ case ssh_receive_data() of
+ {ok,Bin} ->
+ log(Connection,recv,Bin),
+ sax_cont({<<Data/binary,Bin/binary>>,Connection,false});
+ {error,Reason} ->
+ throw({could_not_fetch_data,Reason})
+ end.
+
+
+
+%% Event function for the sax parser. It builds a simple XML structure.
+%% Care is taken to keep namespace attributes and prefixes as in the original XML.
+sax_event(Event,_Loc,State) ->
+ sax_event(Event,State).
+
+sax_event({startPrefixMapping, Prefix, Uri},Acc) ->
+ %% startPrefixMapping will always come immediately before the
+ %% startElement where the namespace is defined.
+ [{xmlns,{Prefix,Uri}}|Acc];
+sax_event({startElement,_Uri,_Name,QN,Attrs},Acc) ->
+ %% Pick out any namespace attributes inserted due to a
+ %% startPrefixMapping event.The rest of Acc will then be only
+ %% elements.
+ {NsAttrs,NewAcc} = split_attrs_and_elements(Acc,[]),
+ Tag = qn_to_tag(QN),
+ [{Tag,NsAttrs ++ parse_attrs(Attrs),[]}|NewAcc];
+sax_event({endElement,_Uri,_Name,_QN},[{Name,Attrs,Cont},{Parent,PA,PC}|Acc]) ->
+ [{Parent,PA,[{Name,Attrs,lists:reverse(Cont)}|PC]}|Acc];
+sax_event(endDocument,[{Tag,Attrs,Cont}]) ->
+ {Tag,Attrs,lists:reverse(Cont)};
+sax_event({characters,String},[{Name,Attrs,Cont}|Acc]) ->
+ [{Name,Attrs,[String|Cont]}|Acc];
+sax_event(_Event,State) ->
+ State.
+
+split_attrs_and_elements([{xmlns,{Prefix,Uri}}|Rest],Attrs) ->
+ split_attrs_and_elements(Rest,[{xmlnstag(Prefix),Uri}|Attrs]);
+split_attrs_and_elements(Elements,Attrs) ->
+ {Attrs,Elements}.
+
+xmlnstag([]) ->
+ xmlns;
+xmlnstag(Prefix) ->
+ list_to_atom("xmlns:"++Prefix).
+
+qn_to_tag({[],Name}) ->
+ list_to_atom(Name);
+qn_to_tag({Prefix,Name}) ->
+ list_to_atom(Prefix ++ ":" ++ Name).
+
+parse_attrs([{_Uri, [], Name, Value}|Attrs]) ->
+ [{list_to_atom(Name),Value}|parse_attrs(Attrs)];
+parse_attrs([{_Uri, Prefix, Name, Value}|Attrs]) ->
+ [{list_to_atom(Prefix ++ ":" ++ Name),Value}|parse_attrs(Attrs)];
+parse_attrs([]) ->
+ [].
+
+
+%%%-----------------------------------------------------------------
+%%% Decoding of parsed XML data
+decode({Tag,Attrs,_}=E, #state{connection=Connection,pending=Pending}=State) ->
+ ConnName = Connection#connection.name,
+ case get_local_name_atom(Tag) of
+ 'rpc-reply' ->
+ case get_msg_id(Attrs) of
+ undefined ->
+ case Pending of
+ [#pending{msg_id=MsgId}] ->
+ ?error(ConnName,[{warning,rpc_reply_missing_msg_id},
+ {assuming,MsgId}]),
+ decode_rpc_reply(MsgId,E,State);
+ _ ->
+ ?error(ConnName,[{error,rpc_reply_missing_msg_id}]),
+ {noreply,State}
+ end;
+ MsgId ->
+ decode_rpc_reply(MsgId,E,State)
+ end;
+ hello ->
+ case State#state.hello_status of
+ undefined ->
+ case decode_hello(E) of
+ {ok,SessionId,Capabilities} ->
+ {noreply,State#state{session_id = SessionId,
+ capabilities = Capabilities,
+ hello_status = received}};
+ {error,Reason} ->
+ {noreply,State#state{hello_status = {error,Reason}}}
+ end;
+ #pending{tref=TRef,caller=Caller} ->
+ timer:cancel(TRef),
+ case decode_hello(E) of
+ {ok,SessionId,Capabilities} ->
+ ct_gen_conn:return(Caller,ok),
+ {noreply,State#state{session_id = SessionId,
+ capabilities = Capabilities,
+ hello_status = done}};
+ {error,Reason} ->
+ ct_gen_conn:return(Caller,{error,Reason}),
+ {stop,State#state{hello_status={error,Reason}}}
+ end;
+ Other ->
+ ?error(ConnName,[{got_unexpected_hello,E},
+ {hello_status,Other}]),
+ {noreply,State}
+ end;
+ notification ->
+ EventReceiver = State#state.event_receiver,
+ EventReceiver ! E,
+ {noreply,State};
+ Other ->
+ %% Result of send/2, when not sending an rpc request - or
+ %% if netconf server sends noise. Can handle this only if
+ %% there is just one pending that matches (i.e. has
+ %% undefined msg_id and op)
+ case [P || P = #pending{msg_id=undefined,op=undefined} <- Pending] of
+ [#pending{tref=TRef,
+ caller=Caller}] ->
+ timer:cancel(TRef),
+ ct_gen_conn:return(Caller,E),
+ {noreply,State#state{pending=[]}};
+ _ ->
+ ?error(ConnName,[{got_unexpected_msg,Other},
+ {expecting,Pending}]),
+ {noreply,State}
+ end
+
+ end.
+
+get_msg_id(Attrs) ->
+ case lists:keyfind('message-id',1,Attrs) of
+ {_,Str} ->
+ list_to_integer(Str);
+ false ->
+ undefined
+ end.
+
+decode_rpc_reply(MsgId,{_,Attrs,Content0}=E,#state{pending=Pending} = State) ->
+ case lists:keytake(MsgId,#pending.msg_id,Pending) of
+ {value, #pending{tref=TRef,op=Op,caller=Caller}, Pending1} ->
+ timer:cancel(TRef),
+ Content = forward_xmlns_attr(Attrs,Content0),
+ {CallerReply,{ServerReply,State2}} =
+ do_decode_rpc_reply(Op,Content,State#state{pending=Pending1}),
+ ct_gen_conn:return(Caller,CallerReply),
+ {ServerReply,State2};
+ false ->
+ %% Result of send/2, when receiving a correct
+ %% rpc-reply. Can handle this only if there is just one
+ %% pending that matches (i.e. has undefined msg_id and op)
+ case [P || P = #pending{msg_id=undefined,op=undefined} <- Pending] of
+ [#pending{tref=TRef,
+ msg_id=undefined,
+ op=undefined,
+ caller=Caller}] ->
+ timer:cancel(TRef),
+ ct_gen_conn:return(Caller,E),
+ {noreply,State#state{pending=[]}};
+ _ ->
+ ConnName = (State#state.connection)#connection.name,
+ ?error(ConnName,[{got_unexpected_msg_id,MsgId},
+ {expecting,Pending}]),
+ {noreply,State}
+ end
+ end.
+
+do_decode_rpc_reply(Op,Result,State)
+ when Op==lock; Op==unlock; Op==edit_config; Op==delete_config;
+ Op==copy_config; Op==kill_session ->
+ {decode_ok(Result),{noreply,State}};
+do_decode_rpc_reply(Op,Result,State)
+ when Op==get; Op==get_config; Op==action ->
+ {decode_data(Result),{noreply,State}};
+do_decode_rpc_reply(close_session,Result,State) ->
+ case decode_ok(Result) of
+ ok -> {ok,{stop,State}};
+ Other -> {Other,{noreply,State}}
+ end;
+do_decode_rpc_reply({create_subscription,Caller},Result,State) ->
+ case decode_ok(Result) of
+ ok ->
+ {ok,{noreply,State#state{event_receiver=Caller}}};
+ Other ->
+ {Other,{noreply,State}}
+ end;
+do_decode_rpc_reply(get_event_streams,Result,State) ->
+ {decode_streams(decode_data(Result)),{noreply,State}};
+do_decode_rpc_reply(undefined,Result,State) ->
+ {Result,{noreply,State}}.
+
+
+
+decode_ok([{Tag,Attrs,Content}]) ->
+ case get_local_name_atom(Tag) of
+ ok ->
+ ok;
+ 'rpc-error' ->
+ {error,forward_xmlns_attr(Attrs,Content)};
+ _Other ->
+ {error,{unexpected_rpc_reply,[{Tag,Attrs,Content}]}}
+ end;
+decode_ok(Other) ->
+ {error,{unexpected_rpc_reply,Other}}.
+
+decode_data([{Tag,Attrs,Content}]) ->
+ case get_local_name_atom(Tag) of
+ data ->
+ %% Since content of data has nothing from the netconf
+ %% namespace, we remove the parent's xmlns attribute here
+ %% - just to make the result cleaner
+ {ok,forward_xmlns_attr(remove_xmlnsattr_for_tag(Tag,Attrs),Content)};
+ 'rpc-error' ->
+ {error,forward_xmlns_attr(Attrs,Content)};
+ _Other ->
+ {error,{unexpected_rpc_reply,[{Tag,Attrs,Content}]}}
+ end;
+decode_data(Other) ->
+ {error,{unexpected_rpc_reply,Other}}.
+
+get_qualified_name(Tag) ->
+ case string:tokens(atom_to_list(Tag),":") of
+ [TagStr] -> {[],TagStr};
+ [PrefixStr,TagStr] -> {PrefixStr,TagStr}
+ end.
+
+get_local_name_atom(Tag) ->
+ {_,TagStr} = get_qualified_name(Tag),
+ list_to_atom(TagStr).
+
+
+%% Remove the xmlns attr that points to the tag. I.e. if the tag has a
+%% prefix, remove {'xmlns:prefix',_}, else remove default {xmlns,_}.
+remove_xmlnsattr_for_tag(Tag,Attrs) ->
+ {Prefix,_TagStr} = get_qualified_name(Tag),
+ XmlnsTag = xmlnstag(Prefix),
+ case lists:keytake(XmlnsTag,1,Attrs) of
+ {value,_,NoNsAttrs} ->
+ NoNsAttrs;
+ false ->
+ Attrs
+ end.
+
+%% Take all xmlns attributes from the parent's attribute list and
+%% forward into all childrens' attribute lists. But do not overwrite
+%% any.
+forward_xmlns_attr(ParentAttrs,Children) ->
+ do_forward_xmlns_attr(get_all_xmlns_attrs(ParentAttrs,[]),Children).
+
+do_forward_xmlns_attr(XmlnsAttrs,[{ChT,ChA,ChC}|Children]) ->
+ ChA1 = add_xmlns_attrs(XmlnsAttrs,ChA),
+ [{ChT,ChA1,ChC} | do_forward_xmlns_attr(XmlnsAttrs,Children)];
+do_forward_xmlns_attr(_XmlnsAttrs,[]) ->
+ [].
+
+add_xmlns_attrs([{Key,_}=A|XmlnsAttrs],ChA) ->
+ case lists:keymember(Key,1,ChA) of
+ true ->
+ add_xmlns_attrs(XmlnsAttrs,ChA);
+ false ->
+ add_xmlns_attrs(XmlnsAttrs,[A|ChA])
+ end;
+add_xmlns_attrs([],ChA) ->
+ ChA.
+
+get_all_xmlns_attrs([{xmlns,_}=Default|Attrs],XmlnsAttrs) ->
+ get_all_xmlns_attrs(Attrs,[Default|XmlnsAttrs]);
+get_all_xmlns_attrs([{Key,_}=Attr|Attrs],XmlnsAttrs) ->
+ case atom_to_list(Key) of
+ "xmlns:"++_Prefix ->
+ get_all_xmlns_attrs(Attrs,[Attr|XmlnsAttrs]);
+ _ ->
+ get_all_xmlns_attrs(Attrs,XmlnsAttrs)
+ end;
+get_all_xmlns_attrs([],XmlnsAttrs) ->
+ XmlnsAttrs.
+
+
+%% Decode server hello to pick out session id and capabilities
+decode_hello({hello,_Attrs,Hello}) ->
+ case lists:keyfind('session-id',1,Hello) of
+ {'session-id',_,[SessionId]} ->
+ case lists:keyfind(capabilities,1,Hello) of
+ {capabilities,_,Capabilities} ->
+ case decode_caps(Capabilities,[],false) of
+ {ok,Caps} ->
+ {ok,list_to_integer(SessionId),Caps};
+ Error ->
+ Error
+ end;
+ false ->
+ {error,{incorrect_hello,capabilities_not_found}}
+ end;
+ false ->
+ {error,{incorrect_hello,no_session_id_found}}
+ end.
+
+decode_caps([{capability,[],[?NETCONF_BASE_CAP++Vsn=Cap]} |Caps], Acc, _) ->
+ case Vsn of
+ ?NETCONF_BASE_CAP_VSN ->
+ decode_caps(Caps, [Cap|Acc], true);
+ _ ->
+ {error,{incompatible_base_capability_vsn,Vsn}}
+ end;
+decode_caps([{capability,[],[Cap]}|Caps],Acc,Base) ->
+ decode_caps(Caps,[Cap|Acc],Base);
+decode_caps([H|_T],_,_) ->
+ {error,{unexpected_capability_element,H}};
+decode_caps([],_,false) ->
+ {error,{incorrect_hello,no_base_capability_found}};
+decode_caps([],Acc,true) ->
+ {ok,lists:reverse(Acc)}.
+
+
+%% Return a list of {Name,Data}, where data is a {Tag,Value} list for each stream
+decode_streams({error,Reason}) ->
+ {error,Reason};
+decode_streams({ok,[{netconf,_,Streams}]}) ->
+ {ok,decode_streams(Streams)};
+decode_streams([{streams,_,Streams}]) ->
+ decode_streams(Streams);
+decode_streams([{stream,_,Stream} | Streams]) ->
+ {name,_,[Name]} = lists:keyfind(name,1,Stream),
+ [{Name,[{Tag,Value} || {Tag,_,[Value]} <- Stream, Tag /= name]}
+ | decode_streams(Streams)];
+decode_streams([]) ->
+ [].
+
+
+%%%-----------------------------------------------------------------
+%%% Logging
+
+log(Connection,Action) ->
+ log(Connection,Action,<<>>).
+log(#connection{host=Host,port=Port,name=Name},Action,Data) ->
+ error_logger:info_report(#conn_log{client=self(),
+ address={Host,Port},
+ name=Name,
+ action=Action,
+ module=?MODULE},
+ Data).
+
+
+%% Log callback - called from the error handler process
+format_data(raw,Data) ->
+ io_lib:format("~n~s~n",[hide_password(Data)]);
+format_data(pretty,Data) ->
+ io_lib:format("~n~s~n",[indent(Data)]);
+format_data(html,Data) ->
+ io_lib:format("~n~s~n",[html_format(Data)]).
+
+%%%-----------------------------------------------------------------
+%%% Hide password elements from XML data
+hide_password(Bin) ->
+ re:replace(Bin,<<"(<password[^>]*>)[^<]*(</password>)">>,<<"\\1*****\\2">>,
+ [global,{return,binary}]).
+
+%%%-----------------------------------------------------------------
+%%% HTML formatting
+html_format(Bin) ->
+ binary:replace(indent(Bin),<<"<">>,<<"&lt;">>,[global]).
+
+%%%-----------------------------------------------------------------
+%%% Indentation of XML code
+indent(Bin) ->
+ String = normalize(hide_password(Bin)),
+ IndentedString =
+ case erase(part_of_line) of
+ undefined ->
+ indent1(String,[]);
+ Part ->
+ indent1(lists:reverse(Part)++String,erase(indent))
+ end,
+ list_to_binary(IndentedString).
+
+%% Normalizes the XML document by removing all space and newline
+%% between two XML tags.
+%% Returns a list, no matter if the input was a list or a binary.
+normalize(Str) ->
+ re:replace(Str,<<">[ \r\n\t]+<">>,<<"><">>,[global,{return,list}]).
+
+
+indent1("<?"++Rest1,Indent1) ->
+ %% Prolog
+ {Line,Rest2,Indent2} = indent_line(Rest1,Indent1,[$?,$<]),
+ Line++indent1(Rest2,Indent2);
+indent1("</"++Rest1,Indent1) ->
+ %% Stop tag
+ {Line,Rest2,Indent2} = indent_line1(Rest1,Indent1,[$/,$<]),
+ "\n"++Line++indent1(Rest2,Indent2);
+indent1("<"++Rest1,Indent1) ->
+ %% Start- or empty tag
+ put(tag,get_tag(Rest1)),
+ {Line,Rest2,Indent2} = indent_line(Rest1,Indent1,[$<]),
+ "\n"++Line++indent1(Rest2,Indent2);
+indent1([H|T],Indent) ->
+ [H|indent1(T,Indent)];
+indent1([],_Indent) ->
+ [].
+
+indent_line("?>"++Rest,Indent,Line) ->
+ %% Prolog
+ {lists:reverse(Line)++"?>",Rest,Indent};
+indent_line("/></"++Rest,Indent,Line) ->
+ %% Empty tag, and stop of parent tag -> one step out in indentation
+ {Indent++lists:reverse(Line)++"/>","</"++Rest,Indent--" "};
+indent_line("/>"++Rest,Indent,Line) ->
+ %% Empty tag, then probably next tag -> keep indentation
+ {Indent++lists:reverse(Line)++"/>",Rest,Indent};
+indent_line("></"++Rest,Indent,Line) ->
+ LastTag = erase(tag),
+ case get_tag(Rest) of
+ LastTag ->
+ %% Start and stop tag, but no content
+ indent_line1(Rest,Indent,[$/,$<,$>|Line]);
+ _ ->
+ %% Stop tag completed, and then stop tag of parent -> one step out
+ {Indent++lists:reverse(Line)++">","</"++Rest,Indent--" "}
+ end;
+indent_line("><"++Rest,Indent,Line) ->
+ %% Stop tag completed, and new tag comming -> keep indentation
+ {Indent++lists:reverse(Line)++">","<"++Rest," "++Indent};
+indent_line("</"++Rest,Indent,Line) ->
+ %% Stop tag starting -> search for end of this tag
+ indent_line1(Rest,Indent,[$/,$<|Line]);
+indent_line([H|T],Indent,Line) ->
+ indent_line(T,Indent,[H|Line]);
+indent_line([],Indent,Line) ->
+ %% The line is not complete - will be continued later
+ put(part_of_line,Line),
+ put(indent,Indent),
+ {[],[],Indent}.
+
+indent_line1("></"++Rest,Indent,Line) ->
+ %% Stop tag completed, and then stop tag of parent -> one step out
+ {Indent++lists:reverse(Line)++">","</"++Rest,Indent--" "};
+indent_line1(">"++Rest,Indent,Line) ->
+ %% Stop tag completed -> keep indentation
+ {Indent++lists:reverse(Line)++">",Rest,Indent};
+indent_line1([H|T],Indent,Line) ->
+ indent_line1(T,Indent,[H|Line]);
+indent_line1([],Indent,Line) ->
+ %% The line is not complete - will be continued later
+ put(part_of_line,Line),
+ put(indent,Indent),
+ {[],[],Indent}.
+
+get_tag("/>"++_) ->
+ [];
+get_tag(">"++_) ->
+ [];
+get_tag([H|T]) ->
+ [H|get_tag(T)];
+get_tag([]) ->
+ %% The line is not complete - will be continued later.
+ [].
+
+
+%%%-----------------------------------------------------------------
+%%% SSH stuff
+ssh_receive_data() ->
+ receive
+ {ssh_cm, CM, {data, Ch, _Type, Data}} ->
+ ssh_connection:adjust_window(CM,Ch,size(Data)),
+ {ok, Data};
+ {ssh_cm, _CM, {Closed, _Ch}} = X when Closed == closed; Closed == eof ->
+ {error,X};
+ {_Ref,timeout} = X ->
+ {error,X}
+ end.
+
+ssh_open(#options{host=Host,timeout=Timeout,port=Port,ssh=SshOpts,name=Name}) ->
+ case ssh:connect(Host, Port,
+ [{user_interaction,false},
+ {silently_accept_hosts, true}|SshOpts]) of
+ {ok,CM} ->
+ case ssh_connection:session_channel(CM, Timeout) of
+ {ok,Ch} ->
+ case ssh_connection:subsystem(CM, Ch, "netconf", Timeout) of
+ success ->
+ {ok, #connection{reference = {CM,Ch},
+ host = Host,
+ port = Port,
+ name = Name}};
+ failure ->
+ ssh:close(CM),
+ {error,{ssh,could_not_execute_netconf_subsystem}}
+ end;
+ {error, Reason} ->
+ ssh:close(CM),
+ {error,{ssh,could_not_open_channel,Reason}};
+ Other ->
+ %% Bug in ssh?? got {closed,0} here once...
+ {error,{ssh,unexpected_from_session_channel,Other}}
+ end;
+ {error,Reason} ->
+ {error,{ssh,could_not_connect_to_server,Reason}}
+ end.
+
+ssh_send(#connection{reference = {CM,Ch}}, Data) ->
+ case ssh_connection:send(CM, Ch, Data) of
+ ok -> ok;
+ {error,Reason} -> {error,{ssh,failed_to_send_data,Reason}}
+ end.
+
+ssh_close(#connection{reference = {CM,_Ch}}) ->
+ ssh:close(CM).
+
+
+%%----------------------------------------------------------------------
+%% END OF MODULE
+%%----------------------------------------------------------------------
diff --git a/lib/common_test/src/ct_netconfc.hrl b/lib/common_test/src/ct_netconfc.hrl
new file mode 100644
index 0000000000..295a61a98b
--- /dev/null
+++ b/lib/common_test/src/ct_netconfc.hrl
@@ -0,0 +1,58 @@
+%%--------------------------------------------------------------------
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, 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_netconfc.hrl
+%%
+%% Description:
+%% This file defines constant values and records used by the
+%% netconf client ct_netconfc.
+%%
+%% @author Support
+%% @doc Netconf Client Interface.
+%% @end
+%%----------------------------------------------------------------------
+%%----------------------------------------------------------------------
+
+
+%% Default port number (RFC 4742/IANA).
+-define(DEFAULT_PORT, 830).
+
+%% Default timeout to wait for netconf server to reply to a request
+-define(DEFAULT_TIMEOUT, infinity). %% msec
+
+%% Namespaces
+-define(NETCONF_NAMESPACE_ATTR,[{xmlns,?NETCONF_NAMESPACE}]).
+-define(ACTION_NAMESPACE_ATTR,[{xmlns,?ACTION_NAMESPACE}]).
+-define(NETCONF_NOTIF_NAMESPACE_ATTR,[{xmlns,?NETCONF_NOTIF_NAMESPACE}]).
+-define(NETMOD_NOTIF_NAMESPACE_ATTR,[{xmlns,?NETMOD_NOTIF_NAMESPACE}]).
+
+-define(NETCONF_NAMESPACE,"urn:ietf:params:xml:ns:netconf:base:1.0").
+-define(ACTION_NAMESPACE,"urn:com:ericsson:ecim:1.0").
+-define(NETCONF_NOTIF_NAMESPACE,
+ "urn:ietf:params:xml:ns:netconf:notification:1.0").
+-define(NETMOD_NOTIF_NAMESPACE,"urn:ietf:params:xml:ns:netmod:notification").
+
+%% Capabilities
+-define(NETCONF_BASE_CAP,"urn:ietf:params:netconf:base:").
+-define(NETCONF_BASE_CAP_VSN,"1.0").
+
+%% Misc
+-define(END_TAG,<<"]]>]]>">>).
+
+-define(FORMAT(_F, _A), lists:flatten(io_lib:format(_F, _A))).
diff --git a/lib/common_test/src/ct_repeat.erl b/lib/common_test/src/ct_repeat.erl
index 8ecd82f771..a47309c6ee 100644
--- a/lib/common_test/src/ct_repeat.erl
+++ b/lib/common_test/src/ct_repeat.erl
@@ -41,72 +41,86 @@ loop_test(If,Args) when is_list(Args) ->
case get_loop_info(Args) of
no_loop ->
false;
- {error,E} ->
+ E = {error,_} ->
io:format("Common Test error: ~p\n\n",[E]),
file:set_cwd(Cwd),
E;
{repeat,N} ->
io:format("\nCommon Test: Will repeat tests ~w times.\n\n",[N]),
Args1 = [{loop_info,[{repeat,1,N}]} | Args],
- loop(If,repeat,0,N,undefined,Args1,undefined),
- file:set_cwd(Cwd);
+ Result = loop(If,repeat,0,N,undefined,Args1,undefined,[]),
+ file:set_cwd(Cwd),
+ Result;
{stop_time,StopTime} ->
- case remaining_time(StopTime) of
- 0 ->
- io:format("\nCommon Test: No time left to run tests.\n\n",[]),
- ok;
- Secs ->
- io:format("\nCommon Test: Will repeat tests for ~s.\n\n",
- [ts(Secs)]),
- TPid =
- case lists:keymember(force_stop,1,Args) of
- true ->
- CtrlPid = self(),
- spawn(fun() -> stop_after(CtrlPid,Secs) end);
- false ->
- undefined
- end,
- Args1 = [{loop_info,[{stop_time,Secs,StopTime,1}]} | Args],
- loop(If,stop_time,0,Secs,StopTime,Args1,TPid)
- end,
- file:set_cwd(Cwd)
+ Result =
+ case remaining_time(StopTime) of
+ 0 ->
+ io:format("\nCommon Test: "
+ "No time left to run tests.\n\n",[]),
+ {error,not_enough_time};
+ Secs ->
+ io:format("\nCommon Test: "
+ "Will repeat tests for ~s.\n\n",[ts(Secs)]),
+ TPid =
+ case lists:keymember(force_stop,1,Args) of
+ true ->
+ CtrlPid = self(),
+ spawn(fun() -> stop_after(CtrlPid,Secs) end);
+ false ->
+ undefined
+ end,
+ Args1 = [{loop_info,[{stop_time,Secs,StopTime,1}]} | Args],
+ loop(If,stop_time,0,Secs,StopTime,Args1,TPid,[])
+ end,
+ file:set_cwd(Cwd),
+ Result
end.
-loop(_,repeat,N,N,_,_Args,_) ->
- ok;
+loop(_,repeat,N,N,_,_Args,_,AccResult) ->
+ lists:reverse(AccResult);
-loop(If,Type,N,Data0,Data1,Args,TPid) ->
+loop(If,Type,N,Data0,Data1,Args,TPid,AccResult) ->
Pid = spawn_tester(If,self(),Args),
receive
{'EXIT',Pid,Reason} ->
- io:format("Test run crashed! This could be an internal error "
- "- please report!\n\n"
- "~p\n\n",[Reason]),
- cancel(TPid),
- {error,Reason};
+ case Reason of
+ {user_error,What} ->
+ io:format("\nTest run failed!\nReason: ~p\n\n\n", [What]),
+ cancel(TPid),
+ {error,What};
+ _ ->
+ io:format("Test run crashed! This could be an internal error "
+ "- please report!\n\n"
+ "~p\n\n\n",[Reason]),
+ cancel(TPid),
+ {error,Reason}
+ end;
{Pid,{error,Reason}} ->
- io:format("\nTest run failed!\nReason: ~p\n\n",[Reason]),
+ io:format("\nTest run failed!\nReason: ~p\n\n\n",[Reason]),
cancel(TPid),
{error,Reason};
{Pid,Result} ->
if Type == repeat ->
- io:format("\nTest run ~w(~w) complete.\n\n",[N+1,Data0]),
+ io:format("\nTest run ~w(~w) complete.\n\n\n",[N+1,Data0]),
lists:keydelete(loop_info,1,Args),
Args1 = [{loop_info,[{repeat,N+2,Data0}]} | Args],
- loop(If,repeat,N+1,Data0,Data1,Args1,TPid);
+ loop(If,repeat,N+1,Data0,Data1,Args1,TPid,[Result|AccResult]);
Type == stop_time ->
case remaining_time(Data1) of
0 ->
- io:format("\nTest time (~s) has run out.\n\n",[ts(Data0)]),
+ io:format("\nTest time (~s) has run out.\n\n\n",
+ [ts(Data0)]),
cancel(TPid),
- Result;
+ lists:reverse([Result|AccResult]);
Secs ->
io:format("\n~s of test time remaining, "
- "starting run #~w...\n\n",[ts(Secs),N+2]),
+ "starting run #~w...\n\n\n",
+ [ts(Secs),N+2]),
lists:keydelete(loop_info,1,Args),
ST = {stop_time,Data0,Data1,N+2},
Args1 = [{loop_info,[ST]} | Args],
- loop(If,stop_time,N+1,Data0,Data1,Args1,TPid)
+ loop(If,stop_time,N+1,Data0,Data1,Args1,TPid,
+ [Result|AccResult])
end
end
end.
diff --git a/lib/common_test/src/ct_run.erl b/lib/common_test/src/ct_run.erl
index 46aec04ec1..eb05c90ba8 100644
--- a/lib/common_test/src/ct_run.erl
+++ b/lib/common_test/src/ct_run.erl
@@ -39,33 +39,46 @@
%% Misc internal functions
-export([variables_file_name/1,script_start1/2,run_test2/1]).
+-include("ct.hrl").
-include("ct_event.hrl").
-include("ct_util.hrl").
-define(abs(Name), filename:absname(Name)).
-define(testdir(Name, Suite), ct_util:get_testdir(Name, Suite)).
+-define(EXIT_STATUS_TEST_SUCCESSFUL, 0).
+-define(EXIT_STATUS_TEST_CASE_FAILED, 1).
+-define(EXIT_STATUS_TEST_RUN_FAILED, 2).
+
+-define(default_verbosity, [{default,?MAX_VERBOSITY},
+ {'$unspecified',?MAX_VERBOSITY}]).
+
-record(opts, {label,
profile,
vts,
shell,
cover,
+ cover_stop,
coverspec,
step,
logdir,
logopts = [],
+ basic_html,
+ verbosity = [],
config = [],
event_handlers = [],
ct_hooks = [],
enable_builtin_hooks,
include = [],
- silent_connections,
+ auto_compile,
+ silent_connections = [],
stylesheet,
multiply_timetraps = 1,
scale_timetraps = false,
create_priv_dir,
testspecs = [],
- tests}).
+ tests,
+ starter}).
%%%-----------------------------------------------------------------
%%% @spec script_start() -> void()
@@ -102,7 +115,8 @@ script_start() ->
end, Flags)
end,
%% used for purpose of testing the run_test interface
- io:format(user, "~n-------------------- START ARGS --------------------~n", []),
+ 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),
@@ -110,7 +124,8 @@ script_start() ->
[EnvStartOpts,EnvArgs]),
Merged = merge_arguments(CtArgs ++ EnvArgs),
io:format(user, "--- Merged args:~n~p~n", [FlagFilter(Merged)]),
- io:format(user, "----------------------------------------------------~n~n", []),
+ io:format(user, "-----------------------------------"
+ "-----------------~n~n", []),
Merged;
_ ->
merge_arguments(CtArgs)
@@ -122,46 +137,107 @@ script_start() ->
script_start(Args) ->
Tracing = start_trace(Args),
- Res =
- case ct_repeat:loop_test(script, Args) of
- false ->
- {ok,Cwd} = file:get_cwd(),
- CTVsn =
- case filename:basename(code:lib_dir(common_test)) of
- CTBase when is_list(CTBase) ->
- case string:tokens(CTBase, "-") of
- ["common_test",Vsn] -> " v"++Vsn;
- _ -> ""
- end
- end,
- 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
- {'EXIT',Pid,Reason} ->
- case Reason of
- {user_error,What} ->
- io:format("\nTest run failed!\nReason: ~p\n\n", [What]),
- {error,What};
- _ ->
- io:format("Test run crashed! This could be an internal error "
- "- please report!\n\n"
- "~p\n\n", [Reason]),
- {error,Reason}
- end;
- {Pid,{error,Reason}} ->
- io:format("\nTest run failed! Reason:\n~p\n\n",[Reason]),
- {error,Reason};
- {Pid,Result} ->
- Result
- end;
- Result ->
- Result
- end,
+ case ct_repeat:loop_test(script, Args) of
+ false ->
+ {ok,Cwd} = file:get_cwd(),
+ CTVsn =
+ case filename:basename(code:lib_dir(common_test)) of
+ CTBase when is_list(CTBase) ->
+ case string:tokens(CTBase, "-") of
+ ["common_test",Vsn] -> " v"++Vsn;
+ _ -> ""
+ end
+ end,
+ 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
+ {'EXIT',Pid,Reason} ->
+ case Reason of
+ {user_error,What} ->
+ io:format("\nTest run failed!\nReason: ~p\n\n\n",
+ [What]),
+ finish(Tracing, ?EXIT_STATUS_TEST_RUN_FAILED, Args);
+ _ ->
+ io:format("Test run crashed! "
+ "This could be an internal error "
+ "- please report!\n\n"
+ "~p\n\n\n", [Reason]),
+ finish(Tracing, ?EXIT_STATUS_TEST_RUN_FAILED, Args)
+ end;
+ {Pid,{error,Reason}} ->
+ io:format("\nTest run failed! Reason:\n~p\n\n\n",[Reason]),
+ finish(Tracing, ?EXIT_STATUS_TEST_RUN_FAILED, Args);
+ {Pid,Result} ->
+ io:nl(),
+ finish(Tracing, analyze_test_result(Result, Args), Args)
+ end;
+ {error,_LoopReason} ->
+ finish(Tracing, ?EXIT_STATUS_TEST_RUN_FAILED, Args);
+ Result ->
+ io:nl(),
+ finish(Tracing, analyze_test_result(Result, Args), Args)
+ end.
+
+%% analyze the result of one test run, or many (in case of looped test)
+analyze_test_result(ok, _) ->
+ ?EXIT_STATUS_TEST_SUCCESSFUL;
+analyze_test_result({error,_Reason}, _) ->
+ ?EXIT_STATUS_TEST_RUN_FAILED;
+analyze_test_result({_Ok,Failed,{_UserSkipped,AutoSkipped}}, Args) ->
+ if Failed > 0 ->
+ ?EXIT_STATUS_TEST_CASE_FAILED;
+ true ->
+ case AutoSkipped of
+ 0 ->
+ ?EXIT_STATUS_TEST_SUCCESSFUL;
+ _ ->
+ case get_start_opt(exit_status,
+ fun([ExitOpt]) -> ExitOpt end,
+ Args) of
+ undefined ->
+ ?EXIT_STATUS_TEST_CASE_FAILED;
+ "ignore_config" ->
+ ?EXIT_STATUS_TEST_SUCCESSFUL
+ end
+ end
+ end;
+analyze_test_result([Result|Rs], Args) ->
+ case analyze_test_result(Result, Args) of
+ ?EXIT_STATUS_TEST_SUCCESSFUL ->
+ analyze_test_result(Rs, Args);
+ Other ->
+ Other
+ end;
+analyze_test_result([], _) ->
+ ?EXIT_STATUS_TEST_SUCCESSFUL;
+analyze_test_result(interactive_mode, _) ->
+ interactive_mode;
+analyze_test_result(Unknown, _) ->
+ io:format("\nTest run failed! Reason:\n~p\n\n\n",[Unknown]),
+ ?EXIT_STATUS_TEST_RUN_FAILED.
+
+finish(Tracing, ExitStatus, Args) ->
stop_trace(Tracing),
timer:sleep(1000),
- io:nl(),
- Res.
+ if ExitStatus == interactive_mode ->
+ interactive_mode;
+ true ->
+ %% it's possible to tell CT to finish execution with a call
+ %% to a different function than the normal halt/1 BIF
+ %% (meant to be used mainly for reading the CT exit status)
+ case get_start_opt(halt_with,
+ fun([HaltMod,HaltFunc]) ->
+ {list_to_atom(HaltMod),
+ list_to_atom(HaltFunc)} end,
+ Args) of
+ undefined ->
+ halt(ExitStatus);
+ {M,F} ->
+ apply(M, F, [ExitStatus])
+ end
+ end.
script_start1(Parent, Args) ->
%% read general start flags
@@ -170,9 +246,11 @@ script_start1(Parent, 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),
+ CoverStop = get_start_opt(cover_stop, fun([CS]) -> list_to_atom(CS) end, Args),
LogDir = get_start_opt(logdir, fun([LogD]) -> LogD end, Args),
LogOpts = get_start_opt(logopts, fun(Os) -> [list_to_atom(O) || O <- Os] end,
[], Args),
+ Verbosity = verbosity_args2opts(Args),
MultTT = get_start_opt(multiply_timetraps,
fun([MT]) -> list_to_integer(MT) end, 1, Args),
ScaleTT = get_start_opt(scale_timetraps,
@@ -206,7 +284,7 @@ script_start1(Parent, Args) ->
end
end,
%% no_auto_compile + include
- IncludeDirs =
+ {AutoCompile,IncludeDirs} =
case proplists:get_value(no_auto_compile, Args) of
undefined ->
application:set_env(common_test, auto_compile, true),
@@ -222,46 +300,53 @@ script_start1(Parent, Args) ->
case os:getenv("CT_INCLUDE_PATH") of
false ->
application:set_env(common_test, include, InclDirs),
- InclDirs;
+ {undefined,InclDirs};
CtInclPath ->
AllInclDirs =
string:tokens(CtInclPath,[$:,$ ,$,]) ++ InclDirs,
application:set_env(common_test, include, AllInclDirs),
- AllInclDirs
+ {undefined,AllInclDirs}
end;
_ ->
application:set_env(common_test, auto_compile, false),
- []
+ {false,[]}
end,
%% silent connections
SilentConns =
get_start_opt(silent_connections,
- fun(["all"]) -> [];
+ fun(["all"]) -> [all];
(Conns) -> [list_to_atom(Conn) || Conn <- Conns]
- end, Args),
+ 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,
+ BasicHtml = case proplists:get_value(basic_html, Args) of
+ undefined ->
+ application:set_env(common_test, basic_html, false),
+ undefined;
+ _ ->
+ application:set_env(common_test, basic_html, true),
+ true
+ end,
StartOpts = #opts{label = Label, profile = Profile,
- vts = Vts, shell = Shell, cover = Cover,
+ vts = Vts, shell = Shell,
+ cover = Cover, cover_stop = CoverStop,
logdir = LogDir, logopts = LogOpts,
+ basic_html = BasicHtml,
+ verbosity = Verbosity,
event_handlers = EvHandlers,
ct_hooks = CTHooks,
enable_builtin_hooks = EnableBuiltinHooks,
+ auto_compile = AutoCompile,
include = IncludeDirs,
silent_connections = SilentConns,
stylesheet = Stylesheet,
multiply_timetraps = MultTT,
scale_timetraps = ScaleTT,
- create_priv_dir = CreatePrivDir},
+ create_priv_dir = CreatePrivDir,
+ starter = script},
%% check if log files should be refreshed or go on to run tests...
Result = run_or_refresh(StartOpts, Args),
@@ -325,9 +410,18 @@ script_start2(StartOpts = #opts{vts = undefined,
AllLogOpts = merge_vals([StartOpts#opts.logopts,
SpecStartOpts#opts.logopts]),
-
- Cover = choose_val(StartOpts#opts.cover,
- SpecStartOpts#opts.cover),
+ AllVerbosity =
+ merge_keyvals([StartOpts#opts.verbosity,
+ SpecStartOpts#opts.verbosity]),
+ AllSilentConns =
+ merge_vals([StartOpts#opts.silent_connections,
+ SpecStartOpts#opts.silent_connections]),
+ Cover =
+ choose_val(StartOpts#opts.cover,
+ SpecStartOpts#opts.cover),
+ CoverStop =
+ choose_val(StartOpts#opts.cover_stop,
+ SpecStartOpts#opts.cover_stop),
MultTT =
choose_val(StartOpts#opts.multiply_timetraps,
SpecStartOpts#opts.multiply_timetraps),
@@ -352,21 +446,54 @@ script_start2(StartOpts = #opts{vts = undefined,
StartOpts#opts.enable_builtin_hooks,
SpecStartOpts#opts.enable_builtin_hooks),
+ Stylesheet =
+ choose_val(StartOpts#opts.stylesheet,
+ SpecStartOpts#opts.stylesheet),
+
AllInclude = merge_vals([StartOpts#opts.include,
SpecStartOpts#opts.include]),
application:set_env(common_test, include, AllInclude),
+
+ AutoCompile =
+ case choose_val(StartOpts#opts.auto_compile,
+ SpecStartOpts#opts.auto_compile) of
+ undefined ->
+ true;
+ ACBool ->
+ application:set_env(common_test,
+ auto_compile,
+ ACBool),
+ ACBool
+ end,
+
+ BasicHtml =
+ case choose_val(StartOpts#opts.basic_html,
+ SpecStartOpts#opts.basic_html) of
+ undefined ->
+ false;
+ BHBool ->
+ application:set_env(common_test, basic_html,
+ BHBool),
+ BHBool
+ end,
{TS,StartOpts#opts{label = Label,
profile = Profile,
testspecs = Specs,
cover = Cover,
+ cover_stop = CoverStop,
logdir = LogDir,
logopts = AllLogOpts,
+ basic_html = BasicHtml,
+ verbosity = AllVerbosity,
+ silent_connections = AllSilentConns,
config = SpecStartOpts#opts.config,
event_handlers = AllEvHs,
ct_hooks = AllCTHooks,
enable_builtin_hooks =
EnableBuiltinHooks,
+ stylesheet = Stylesheet,
+ auto_compile = AutoCompile,
include = AllInclude,
multiply_timetraps = MultTT,
scale_timetraps = ScaleTT,
@@ -519,8 +646,10 @@ script_start4(#opts{label = Label, profile = Profile,
event_handlers = EvHandlers,
ct_hooks = CTHooks,
logopts = LogOpts,
+ verbosity = Verbosity,
enable_builtin_hooks = EnableBuiltinHooks,
logdir = LogDir, testspecs = Specs}, _Args) ->
+
%% label - used by ct_logs
application:set_env(common_test, test_label, Label),
@@ -536,11 +665,12 @@ script_start4(#opts{label = Label, profile = Profile,
{ct_hooks, CTHooks},
{enable_builtin_hooks,EnableBuiltinHooks}]) of
ok ->
- ct_util:start(interactive, LogDir),
+ ct_util:start(interactive, LogDir,
+ add_verbosity_defaults(Verbosity)),
ct_util:set_testdata({logopts, LogOpts}),
log_ts_names(Specs),
io:nl(),
- ok;
+ interactive_mode;
Error ->
Error
end;
@@ -553,7 +683,7 @@ script_start4(#opts{vts = true, cover = Cover}, _) ->
%% Add support later (maybe).
io:format("\nCan't run cover in vts mode.\n\n", [])
end,
- erlang:halt();
+ {error,no_cover_in_vts_mode};
script_start4(#opts{shell = true, cover = Cover}, _) ->
case Cover of
@@ -562,7 +692,8 @@ script_start4(#opts{shell = true, cover = Cover}, _) ->
_ ->
%% Add support later (maybe).
io:format("\nCan't run cover in interactive mode.\n\n", [])
- end;
+ end,
+ {error,no_cover_in_interactive_mode};
script_start4(Opts = #opts{tests = Tests}, Args) ->
do_run(Tests, [], Opts, Args).
@@ -579,6 +710,7 @@ script_usage() ->
"\n\t[-dir TestDir1 TestDir2 .. TestDirN] |"
"\n\t[-suite Suite [-case Case]]"
"\n\t[-logopts LogOpt1 LogOpt2 .. LogOptN]"
+ "\n\t[-verbosity GenVLvl | [CategoryVLvl1 .. CategoryVLvlN]]"
"\n\t[-include InclDir1 InclDir2 .. InclDirN]"
"\n\t[-no_auto_compile]"
"\n\t[-multiply_timetraps N]"
@@ -593,11 +725,13 @@ script_usage() ->
"\n\t[-userconfig CallbackModule ConfigFile1 .. ConfigFileN]"
"\n\t[-decrypt_key Key] | [-decrypt_file KeyFile]"
"\n\t[-logdir LogDir]"
+ "\n\t[-logopts LogOpt1 LogOpt2 .. LogOptN]"
+ "\n\t[-verbosity GenVLvl | [CategoryVLvl1 .. CategoryVLvlN]]"
"\n\t[-silent_connections [ConnType1 ConnType2 .. ConnTypeN]]"
- "\n\t[-stylesheet CSSFile]"
+ "\n\t[-stylesheet CSSFile]"
"\n\t[-cover CoverCfgFile]"
+ "\n\t[-cover_stop Bool]"
"\n\t[-event_handler EvHandler1 EvHandler2 .. EvHandlerN]"
- "\n\t[-logopts LogOpt1 LogOpt2 .. LogOptN]"
"\n\t[-ct_hooks CTHook1 CTHook2 .. CTHookN]"
"\n\t[-include InclDir1 InclDir2 .. InclDirN]"
"\n\t[-no_auto_compile]"
@@ -613,12 +747,14 @@ script_usage() ->
"\n\t[-config ConfigFile1 ConfigFile2 .. ConfigFileN]"
"\n\t[-decrypt_key Key] | [-decrypt_file KeyFile]"
"\n\t[-logdir LogDir]"
+ "\n\t[-logopts LogOpt1 LogOpt2 .. LogOptN]"
+ "\n\t[-verbosity GenVLvl | [CategoryVLvl1 .. CategoryVLvlN]]"
"\n\t[-allow_user_terms]"
"\n\t[-silent_connections [ConnType1 ConnType2 .. ConnTypeN]]"
"\n\t[-stylesheet CSSFile]"
"\n\t[-cover CoverCfgFile]"
+ "\n\t[-cover_stop Bool]"
"\n\t[-event_handler EvHandler1 EvHandler2 .. EvHandlerN]"
- "\n\t[-logopts LogOpt1 LogOpt2 .. LogOptN]"
"\n\t[-ct_hooks CTHook1 CTHook2 .. CTHookN]"
"\n\t[-include InclDir1 InclDir2 .. InclDirN]"
"\n\t[-no_auto_compile]"
@@ -702,7 +838,7 @@ run_test(StartOpts) when is_list(StartOpts) ->
Ref = monitor(process, CTPid),
receive
{'DOWN',Ref,process,CTPid,{user_error,Error}} ->
- Error;
+ {error,Error};
{'DOWN',Ref,process,CTPid,Other} ->
Other
end.
@@ -739,8 +875,10 @@ run_test2(StartOpts) ->
(Lbl) when is_atom(Lbl) -> atom_to_list(Lbl)
end, StartOpts),
%% profile
- Profile = get_start_opt(profile, fun(Prof) when is_list(Prof) -> Prof;
- (Prof) when is_atom(Prof) -> atom_to_list(Prof)
+ Profile = get_start_opt(profile, fun(Prof) when is_list(Prof) ->
+ Prof;
+ (Prof) when is_atom(Prof) ->
+ atom_to_list(Prof)
end, StartOpts),
%% logdir
LogDir = get_start_opt(logdir, fun(LD) when is_list(LD) -> LD end,
@@ -748,6 +886,19 @@ run_test2(StartOpts) ->
%% logopts
LogOpts = get_start_opt(logopts, value, [], StartOpts),
+ %% verbosity
+ Verbosity =
+ get_start_opt(verbosity,
+ fun(VLvls) when is_list(VLvls) ->
+ lists:map(fun(VLvl = {_Cat,_Lvl}) ->
+ VLvl;
+ (Lvl) ->
+ {'$unspecified',Lvl}
+ end, VLvls);
+ (VLvl) when is_integer(VLvl) ->
+ [{'$unspecified',VLvl}]
+ end, [], StartOpts),
+
%% config & userconfig
CfgFiles = ct_config:get_config_file_list(StartOpts),
@@ -786,9 +937,9 @@ run_test2(StartOpts) ->
%% silent connections
SilentConns = get_start_opt(silent_connections,
- fun(all) -> [];
+ fun(all) -> [all];
(Conns) -> Conns
- end, StartOpts),
+ end, [], StartOpts),
%% stylesheet
Stylesheet = get_start_opt(stylesheet,
fun(SS) -> ?abs(SS) end,
@@ -796,6 +947,7 @@ run_test2(StartOpts) ->
%% code coverage
Cover = get_start_opt(cover,
fun(CoverFile) -> ?abs(CoverFile) end, StartOpts),
+ CoverStop = get_start_opt(cover_stop, value, StartOpts),
%% timetrap manipulation
MultiplyTT = get_start_opt(multiply_timetraps, value, 1, StartOpts),
@@ -805,7 +957,7 @@ run_test2(StartOpts) ->
CreatePrivDir = get_start_opt(create_priv_dir, value, StartOpts),
%% auto compile & include files
- Include =
+ {AutoCompile,Include} =
case proplists:get_value(auto_compile, StartOpts) of
undefined ->
application:set_env(common_test, auto_compile, true),
@@ -821,16 +973,16 @@ run_test2(StartOpts) ->
case os:getenv("CT_INCLUDE_PATH") of
false ->
application:set_env(common_test, include, InclDirs),
- InclDirs;
+ {undefined,InclDirs};
CtInclPath ->
InclDirs1 = string:tokens(CtInclPath, [$:,$ ,$,]),
AllInclDirs = InclDirs1++InclDirs,
application:set_env(common_test, include, AllInclDirs),
- AllInclDirs
+ {undefined,AllInclDirs}
end;
ACBool ->
application:set_env(common_test, auto_compile, ACBool),
- []
+ {ACBool,[]}
end,
%% decrypt config file
@@ -844,28 +996,36 @@ run_test2(StartOpts) ->
end,
%% 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)
+ BasicHtml =
+ case proplists:get_value(basic_html, StartOpts) of
+ undefined ->
+ application:set_env(common_test, basic_html, false),
+ undefined;
+ BasicHtmlBool ->
+ application:set_env(common_test, basic_html, BasicHtmlBool),
+ BasicHtmlBool
end,
%% stepped execution
Step = get_start_opt(step, value, StartOpts),
Opts = #opts{label = Label, profile = Profile,
- cover = Cover, step = Step, logdir = LogDir,
- logopts = LogOpts, config = CfgFiles,
+ cover = Cover, cover_stop = CoverStop,
+ step = Step, logdir = LogDir,
+ logopts = LogOpts, basic_html = BasicHtml,
+ config = CfgFiles,
+ verbosity = Verbosity,
event_handlers = EvHandlers,
ct_hooks = CTHooks,
enable_builtin_hooks = EnableBuiltinHooks,
+ auto_compile = AutoCompile,
include = Include,
silent_connections = SilentConns,
stylesheet = Stylesheet,
multiply_timetraps = MultiplyTT,
scale_timetraps = ScaleTT,
- create_priv_dir = CreatePrivDir},
+ create_priv_dir = CreatePrivDir,
+ starter = ct},
%% test specification
case proplists:get_value(spec, StartOpts) of
@@ -894,7 +1054,7 @@ run_spec_file(Relaxed,
log_ts_names(AbsSpecs),
case catch ct_testspec:collect_tests_from_file(AbsSpecs, Relaxed) of
{Error,CTReason} when Error == error ; Error == 'EXIT' ->
- exit(CTReason);
+ exit({error,CTReason});
TS ->
SpecOpts = get_data_for_node(TS, node()),
Label = choose_val(Opts#opts.label,
@@ -905,9 +1065,17 @@ run_spec_file(Relaxed,
SpecOpts#opts.logdir),
AllLogOpts = merge_vals([Opts#opts.logopts,
SpecOpts#opts.logopts]),
+ Stylesheet = choose_val(Opts#opts.stylesheet,
+ SpecOpts#opts.stylesheet),
+ AllVerbosity = merge_keyvals([Opts#opts.verbosity,
+ SpecOpts#opts.verbosity]),
+ AllSilentConns = merge_vals([Opts#opts.silent_connections,
+ SpecOpts#opts.silent_connections]),
AllConfig = merge_vals([CfgFiles, SpecOpts#opts.config]),
Cover = choose_val(Opts#opts.cover,
SpecOpts#opts.cover),
+ CoverStop = choose_val(Opts#opts.cover_stop,
+ SpecOpts#opts.cover_stop),
MultTT = choose_val(Opts#opts.multiply_timetraps,
SpecOpts#opts.multiply_timetraps),
ScaleTT = choose_val(Opts#opts.scale_timetraps,
@@ -918,21 +1086,46 @@ run_spec_file(Relaxed,
SpecOpts#opts.event_handlers]),
AllInclude = merge_vals([Opts#opts.include,
SpecOpts#opts.include]),
-
AllCTHooks = merge_vals([Opts#opts.ct_hooks,
- SpecOpts#opts.ct_hooks]),
+ SpecOpts#opts.ct_hooks]),
EnableBuiltinHooks = choose_val(Opts#opts.enable_builtin_hooks,
SpecOpts#opts.enable_builtin_hooks),
application:set_env(common_test, include, AllInclude),
+ AutoCompile = case choose_val(Opts#opts.auto_compile,
+ SpecOpts#opts.auto_compile) of
+ undefined ->
+ true;
+ ACBool ->
+ application:set_env(common_test, auto_compile,
+ ACBool),
+ ACBool
+ end,
+
+ BasicHtml = case choose_val(Opts#opts.basic_html,
+ SpecOpts#opts.basic_html) of
+ undefined ->
+ false;
+ BHBool ->
+ application:set_env(common_test, basic_html,
+ BHBool),
+ BHBool
+ end,
+
Opts1 = Opts#opts{label = Label,
profile = Profile,
cover = Cover,
+ cover_stop = CoverStop,
logdir = which(logdir, LogDir),
logopts = AllLogOpts,
+ stylesheet = Stylesheet,
+ basic_html = BasicHtml,
+ verbosity = AllVerbosity,
+ silent_connections = AllSilentConns,
config = AllConfig,
event_handlers = AllEvHs,
+ auto_compile = AutoCompile,
include = AllInclude,
testspecs = AbsSpecs,
multiply_timetraps = MultTT,
@@ -948,20 +1141,20 @@ run_spec_file(Relaxed,
{Run,Skip} = ct_testspec:prepare_tests(TS, node()),
reformat_result(catch do_run(Run, Skip, Opts1, StartOpts));
{error,GCFReason} ->
- exit(GCFReason)
+ exit({error,GCFReason})
end
end.
run_prepared(Run, Skip, Opts = #opts{logdir = LogDir,
- config = CfgFiles },
+ config = CfgFiles},
StartOpts) ->
LogDir1 = which(logdir, LogDir),
case check_and_install_configfiles(CfgFiles, LogDir1, Opts) of
ok ->
reformat_result(catch do_run(Run, Skip, Opts#opts{logdir = LogDir1},
StartOpts));
- {error,Reason} ->
- exit(Reason)
+ {error,_Reason} = Error ->
+ exit(Error)
end.
check_config_file(Callback, File)->
@@ -969,7 +1162,7 @@ check_config_file(Callback, File)->
false ->
case code:load_file(Callback) of
{module,_} -> ok;
- {error,Why} -> exit({cant_load_callback_module,Why})
+ {error,Why} -> exit({error,{cant_load_callback_module,Why}})
end;
_ ->
ok
@@ -980,16 +1173,17 @@ check_config_file(Callback, File)->
{ok,{config,_}}->
File;
{error,{wrong_config,Message}}->
- exit({wrong_config,{Callback,Message}});
+ exit({error,{wrong_config,{Callback,Message}}});
{error,{nofile,File}}->
- exit({no_such_file,?abs(File)})
+ exit({error,{no_such_file,?abs(File)}})
end.
run_dir(Opts = #opts{logdir = LogDir,
config = CfgFiles,
event_handlers = EvHandlers,
ct_hooks = CTHook,
- enable_builtin_hooks = EnableBuiltinHooks }, StartOpts) ->
+ enable_builtin_hooks = EnableBuiltinHooks},
+ StartOpts) ->
LogDir1 = which(logdir, LogDir),
Opts1 = Opts#opts{logdir = LogDir1},
AbsCfgFiles =
@@ -1002,7 +1196,8 @@ run_dir(Opts = #opts{logdir = LogDir,
{module,Callback}->
ok;
{error,_}->
- exit({no_such_module,Callback})
+ exit({error,{no_such_module,
+ Callback}})
end
end,
{Callback,
@@ -1015,7 +1210,7 @@ run_dir(Opts = #opts{logdir = LogDir,
{ct_hooks, CTHook},
{enable_builtin_hooks,EnableBuiltinHooks}], LogDir1) of
ok -> ok;
- {error,IReason} -> exit(IReason)
+ {error,_IReason} = IError -> exit(IError)
end,
case {proplists:get_value(dir, StartOpts),
proplists:get_value(suite, StartOpts),
@@ -1057,7 +1252,7 @@ run_dir(Opts = #opts{logdir = LogDir,
[], Opts1, StartOpts));
{undefined,[Hd,_|_],_GsAndCs} when not is_integer(Hd) ->
- exit(multiple_suites_and_cases);
+ exit({error,multiple_suites_and_cases});
{undefined,Suite=[Hd|Tl],GsAndCs} when is_integer(Hd) ;
(is_list(Hd) and (Tl == [])) ;
@@ -1067,10 +1262,10 @@ run_dir(Opts = #opts{logdir = LogDir,
[], Opts1, StartOpts));
{[Hd,_|_],_Suites,[]} when is_list(Hd) ; not is_integer(Hd) ->
- exit(multiple_dirs_and_suites);
+ exit({error,multiple_dirs_and_suites});
{undefined,undefined,GsAndCs} when GsAndCs /= [] ->
- exit(incorrect_start_options);
+ exit({error,incorrect_start_options});
{Dir,Suite,GsAndCs} when is_integer(hd(Dir)) ;
(is_atom(Dir) and (Dir /= undefined)) ;
@@ -1079,7 +1274,7 @@ run_dir(Opts = #opts{logdir = LogDir,
Dir1 = if is_atom(Dir) -> atom_to_list(Dir);
true -> Dir end,
if Suite == undefined ->
- exit(incorrect_start_options);
+ exit({error,incorrect_start_options});
is_integer(hd(Suite)) ;
(is_atom(Suite) and (Suite /= undefined)) ;
@@ -1091,16 +1286,18 @@ run_dir(Opts = #opts{logdir = LogDir,
reformat_result(catch do_run(tests(Dir2, Mod),
[], Opts1, StartOpts));
_ ->
- reformat_result(catch do_run(tests(Dir2, Mod, GsAndCs),
+ reformat_result(catch do_run(tests(Dir2, Mod,
+ GsAndCs),
[], Opts1, StartOpts))
end;
is_list(Suite) -> % multiple suites
case [suite_to_test(Dir1, S) || S <- Suite] of
[_,_|_] when GsAndCs /= [] ->
- exit(multiple_suites_and_cases);
+ exit({error,multiple_suites_and_cases});
[{Dir2,Mod}] when GsAndCs /= [] ->
- reformat_result(catch do_run(tests(Dir2, Mod, GsAndCs),
+ reformat_result(catch do_run(tests(Dir2, Mod,
+ GsAndCs),
[], Opts1, StartOpts));
DirMods ->
reformat_result(catch do_run(tests(DirMods),
@@ -1109,10 +1306,10 @@ run_dir(Opts = #opts{logdir = LogDir,
end;
{undefined,undefined,[]} ->
- exit(no_test_specified);
+ exit({error,no_test_specified});
{Dir,Suite,GsAndCs} ->
- exit({incorrect_start_options,{Dir,Suite,GsAndCs}})
+ exit({error,{incorrect_start_options,{Dir,Suite,GsAndCs}}})
end.
%%%-----------------------------------------------------------------
@@ -1157,7 +1354,7 @@ run_testspec2(File) when is_list(File), is_integer(hd(File)) ->
run_testspec2(TestSpec) ->
case catch ct_testspec:collect_tests_from_list(TestSpec, false) of
{E,CTReason} when E == error ; E == 'EXIT' ->
- exit(CTReason);
+ exit({error,CTReason});
TS ->
Opts = get_data_for_node(TS, node()),
@@ -1179,8 +1376,8 @@ run_testspec2(TestSpec) ->
include = AllInclude},
{Run,Skip} = ct_testspec:prepare_tests(TS, node()),
reformat_result(catch do_run(Run, Skip, Opts1, []));
- {error,GCFReason} ->
- exit(GCFReason)
+ {error,_GCFReason} = GCFError ->
+ exit(GCFError)
end
end.
@@ -1188,12 +1385,18 @@ get_data_for_node(#testspec{label = Labels,
profile = Profiles,
logdir = LogDirs,
logopts = LogOptsList,
+ basic_html = BHs,
+ stylesheet = SSs,
+ verbosity = VLvls,
+ silent_connections = SilentConnsList,
cover = CoverFs,
+ cover_stop = CoverStops,
config = Cfgs,
userconfig = UsrCfgs,
event_handler = EvHs,
ct_hooks = CTHooks,
enable_builtin_hooks = EnableBuiltinHooks,
+ auto_compile = ACs,
include = Incl,
multiply_timetraps = MTs,
scale_timetraps = STs,
@@ -1208,7 +1411,18 @@ get_data_for_node(#testspec{label = Labels,
undefined -> [];
LOs -> LOs
end,
+ BasicHtml = proplists:get_value(Node, BHs),
+ Stylesheet = proplists:get_value(Node, SSs),
+ Verbosity = case proplists:get_value(Node, VLvls) of
+ undefined -> [];
+ Lvls -> Lvls
+ end,
+ SilentConns = case proplists:get_value(Node, SilentConnsList) of
+ undefined -> [];
+ SCs -> SCs
+ end,
Cover = proplists:get_value(Node, CoverFs),
+ CoverStop = proplists:get_value(Node, CoverStops),
MT = proplists:get_value(Node, MTs),
ST = proplists:get_value(Node, STs),
CreatePrivDir = proplists:get_value(Node, PDs),
@@ -1216,16 +1430,23 @@ get_data_for_node(#testspec{label = Labels,
[CBF || {N,CBF} <- UsrCfgs, N==Node],
EvHandlers = [{H,A} || {N,H,A} <- EvHs, N==Node],
FiltCTHooks = [Hook || {N,Hook} <- CTHooks, N==Node],
+ AutoCompile = proplists:get_value(Node, ACs),
Include = [I || {N,I} <- Incl, N==Node],
#opts{label = Label,
profile = Profile,
logdir = LogDir,
logopts = LogOpts,
+ basic_html = BasicHtml,
+ stylesheet = Stylesheet,
+ verbosity = Verbosity,
+ silent_connections = SilentConns,
cover = Cover,
+ cover_stop = CoverStop,
config = ConfigFiles,
event_handlers = EvHandlers,
ct_hooks = FiltCTHooks,
enable_builtin_hooks = EnableBuiltinHooks,
+ auto_compile = AutoCompile,
include = Include,
multiply_timetraps = MT,
scale_timetraps = ST,
@@ -1267,6 +1488,14 @@ choose_val(V0, _V1) ->
merge_vals(Vs) ->
lists:append(Vs).
+merge_keyvals(Vs) ->
+ make_unique(lists:append(Vs)).
+
+make_unique([Elem={Key,_} | Elems]) ->
+ [Elem | make_unique(proplists:delete(Key, Elems))];
+make_unique([]) ->
+ [].
+
listify([C|_]=Str) when is_integer(C) -> [Str];
listify(L) when is_list(L) -> L;
listify(E) -> [E].
@@ -1326,17 +1555,36 @@ 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}.
+ if (Cs == all) or (Cs == [all]) or (Cs == ["all"]) -> all;
+ true -> [ensure_atom(C) || C <- listify(Cs)]
+ end;
+groups_and_cases(GOrGs, Cs) when (is_atom(GOrGs) orelse
+ (is_list(GOrGs) andalso
+ (is_atom(hd(GOrGs)) orelse
+ (is_list(hd(GOrGs)) andalso
+ is_atom(hd(hd(GOrGs))))))) ->
+ if (Cs == undefined) or (Cs == []) or
+ (Cs == all) or (Cs == [all]) or (Cs == ["all"]) ->
+ [{GOrGs,all}];
+ true ->
+ [{GOrGs,[ensure_atom(C) || C <- listify(Cs)]}]
+ end;
+groups_and_cases(Gs, Cs) when is_integer(hd(hd(Gs))) ->
+ %% if list of strings, this comes from 'ct_run -group G1 G2 ...' and
+ %% we need to parse the strings
+ Gs1 =
+ if (Gs == [all]) or (Gs == ["all"]) ->
+ all;
+ true ->
+ lists:map(fun(G) ->
+ {ok,Ts,_} = erl_scan:string(G++"."),
+ {ok,Term} = erl_parse:parse_term(Ts),
+ Term
+ end, Gs)
+ end,
+ groups_and_cases(Gs1, Cs);
+groups_and_cases(Gs, Cs) ->
+ {error,{incorrect_group_or_case_option,Gs,Cs}}.
tests(TestDir, Suites, []) when is_list(TestDir), is_integer(hd(TestDir)) ->
[{?testdir(TestDir,Suites),ensure_atom(Suites),all}];
@@ -1366,17 +1614,11 @@ do_run(Tests, Misc, LogDir, LogOpts) when is_list(Misc),
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, [], Opts#opts{logdir = LogDir}, []);
do_run(Tests, Skip, Opts, Args) when is_record(Opts, opts) ->
- #opts{label = Label, profile = Profile, cover = Cover} = Opts,
+ #opts{label = Label, profile = Profile, cover = Cover,
+ verbosity = VLvls} = Opts,
%% label - used by ct_logs
TestLabel =
if Label == undefined -> undefined;
@@ -1397,7 +1639,7 @@ do_run(Tests, Skip, Opts, Args) when is_record(Opts, opts) ->
case code:which(test_server) of
non_existing ->
- exit({error,no_path_to_test_server});
+ {error,no_path_to_test_server};
_ ->
Opts1 = if Cover == undefined ->
Opts;
@@ -1406,7 +1648,13 @@ do_run(Tests, Skip, Opts, Args) when is_record(Opts, opts) ->
{error,Reason} ->
exit({error,Reason});
CoverSpec ->
- Opts#opts{coverspec = CoverSpec}
+ CoverStop =
+ case Opts#opts.cover_stop of
+ undefined -> true;
+ Stop -> Stop
+ end,
+ Opts#opts{coverspec = CoverSpec,
+ cover_stop = CoverStop}
end
end,
%% This env variable is used by test_server to determine
@@ -1418,77 +1666,135 @@ do_run(Tests, Skip, Opts, Args) when is_record(Opts, opts) ->
"ct_framework" ->
ok;
Other ->
- erlang:display(list_to_atom("Note: TEST_SERVER_FRAMEWORK = " ++ Other))
+ erlang:display(
+ list_to_atom(
+ "Note: TEST_SERVER_FRAMEWORK = " ++ Other))
end,
- case ct_util:start(Opts#opts.logdir) of
+ Verbosity = add_verbosity_defaults(VLvls),
+ case ct_util:start(Opts#opts.logdir, Verbosity) of
{error,interactive_mode} ->
io:format("CT is started in interactive mode. "
- "To exit this mode, run ct:stop_interactive().\n"
+ "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 stylesheet info
- ct_util:set_testdata({stylesheet,Opts#opts.stylesheet}),
- %% save logopts
- ct_util:set_testdata({logopts,Opts#opts.logopts}),
- %% enable silent connections
- case Opts#opts.silent_connections of
- [] ->
- Conns = ct_util:override_silence_all_connections(),
- ct_logs:log("Silent connections", "~p", [Conns]);
- Conns when is_list(Conns) ->
- ct_util:override_silence_connections(Conns),
- ct_logs:log("Silent connections", "~p", [Conns]);
- _ ->
- ok
- end,
- log_ts_names(Opts1#opts.testspecs),
- TestSuites = suite_tuples(Tests),
-
- {_TestSuites1,SuiteMakeErrors,AllMakeErrors} =
- case application:get_env(common_test, auto_compile) of
- {ok,false} ->
- {TestSuites1,SuitesNotFound} =
- verify_suites(TestSuites),
- {TestSuites1,SuitesNotFound,SuitesNotFound};
- _ ->
- {SuiteErrs,HelpErrs} = auto_compile(TestSuites),
- {TestSuites,SuiteErrs,SuiteErrs++HelpErrs}
- end,
+ ct_util:set_testdata({starter,Opts#opts.starter}),
+ compile_and_run(Tests, Skip,
+ Opts1#opts{verbosity=Verbosity}, Args)
+ end
+ end.
- case continue(AllMakeErrors) of
- true ->
- SavedErrors = save_make_errors(SuiteMakeErrors),
- ct_repeat:log_loop_info(Args),
+compile_and_run(Tests, Skip, Opts, Args) ->
+ %% save stylesheet info
+ ct_util:set_testdata({stylesheet,Opts#opts.stylesheet}),
+ %% save logopts
+ ct_util:set_testdata({logopts,Opts#opts.logopts}),
+ %% enable silent connections
+ case Opts#opts.silent_connections of
+ [] ->
+ ok;
+ Conns ->
+ case lists:member(all, Conns) of
+ true ->
+ Conns1 = ct_util:override_silence_all_connections(),
+ ct_logs:log("Silent connections", "~p", [Conns1]);
+ false ->
+ ct_util:override_silence_connections(Conns),
+ ct_logs:log("Silent connections", "~p", [Conns])
+ end
+ end,
+ log_ts_names(Opts#opts.testspecs),
+ TestSuites = suite_tuples(Tests),
+
+ {_TestSuites1,SuiteMakeErrors,AllMakeErrors} =
+ case application:get_env(common_test, auto_compile) of
+ {ok,false} ->
+ {TestSuites1,SuitesNotFound} =
+ verify_suites(TestSuites),
+ {TestSuites1,SuitesNotFound,SuitesNotFound};
+ _ ->
+ {SuiteErrs,HelpErrs} = auto_compile(TestSuites),
+ {TestSuites,SuiteErrs,SuiteErrs++HelpErrs}
+ end,
+
+ case continue(AllMakeErrors) of
+ true ->
+ SavedErrors = save_make_errors(SuiteMakeErrors),
+ ct_repeat:log_loop_info(Args),
+
+ try final_tests(Tests,Skip,SavedErrors) of
+ {Tests1,Skip1} ->
+ ReleaseSh = proplists:get_value(release_shell, Args),
+ ct_util:set_testdata({release_shell,ReleaseSh}),
+ possibly_spawn(ReleaseSh == true, Tests1, Skip1, Opts)
+ catch
+ _:BadFormat ->
+ {error,BadFormat}
+ end;
+ false ->
+ io:nl(),
+ ct_util:stop(clean),
+ BadMods =
+ lists:foldr(
+ fun({{_,_},Ms}, Acc) ->
+ Ms ++ lists:foldl(
+ fun(M, Acc1) ->
+ lists:delete(M, Acc1)
+ end, Acc, Ms)
+ end, [], AllMakeErrors),
+ {error,{make_failed,BadMods}}
+ end.
- {Tests1,Skip1} = final_tests(Tests,Skip,SavedErrors),
+%% keep the shell as the top controlling process
+possibly_spawn(false, Tests, Skip, Opts) ->
+ TestResult = (catch do_run_test(Tests, Skip, Opts)),
+ case TestResult of
+ {EType,_} = Error when EType == user_error;
+ EType == error ->
+ ct_util:stop(clean),
+ exit(Error);
+ _ ->
+ ct_util:stop(normal),
+ TestResult
+ end;
- R = (catch do_run_test(Tests1, Skip1, Opts1)),
- case R of
- {EType,_} = Error when EType == user_error ;
+%% we must return control to the shell now, so we spawn
+%% a test supervisor process to keep an eye on the test run
+possibly_spawn(true, Tests, Skip, Opts) ->
+ CTUtilSrv = whereis(ct_util_server),
+ Supervisor =
+ fun() ->
+ process_flag(trap_exit, true),
+ link(CTUtilSrv),
+ TestRun =
+ fun() ->
+ TestResult = (catch do_run_test(Tests, Skip, Opts)),
+ case TestResult 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),
- BadMods =
- lists:foldr(
- fun({{_,_},Ms}, Acc) ->
- Ms ++ lists:foldl(
- fun(M, Acc1) ->
- lists:delete(M, Acc1)
- end, Acc, Ms)
- end, [], AllMakeErrors),
- {error,{make_failed,BadMods}}
- end
- end
- end.
+ exit({ok,TestResult})
+ end
+ end,
+ TestRunPid = spawn_link(TestRun),
+ receive
+ {'EXIT',TestRunPid,{ok,TestResult}} ->
+ io:format(user, "~nCommon Test returned ~p~n~n",
+ [TestResult]);
+ {'EXIT',TestRunPid,Error} ->
+ exit(Error)
+ end
+ end,
+ unlink(CTUtilSrv),
+ SupPid = spawn(Supervisor),
+ io:format(user, "~nTest control handed over to process ~p~n~n",
+ [SupPid]),
+ SupPid.
%% attempt to compile the modules specified in TestSuites
auto_compile(TestSuites) ->
@@ -1504,11 +1810,13 @@ auto_compile(TestSuites) ->
end,
SuiteMakeErrors =
lists:flatmap(fun({TestDir,Suite} = TS) ->
- case run_make(suites, TestDir, Suite, UserInclude) of
+ case run_make(suites, TestDir,
+ Suite, UserInclude) of
{error,{make_failed,Bad}} ->
[{TS,Bad}];
{error,_} ->
- [{TS,[filename:join(TestDir,"*_SUITE")]}];
+ [{TS,[filename:join(TestDir,
+ "*_SUITE")]}];
_ ->
[]
end
@@ -1547,23 +1855,29 @@ verify_suites(TestSuites) ->
{[DS|Found],NotFound};
true ->
Beam = filename:join(TestDir,
- atom_to_list(Suite)++".beam"),
+ atom_to_list(Suite)++
+ ".beam"),
case filelib:is_regular(Beam) of
true ->
{[DS|Found],NotFound};
false ->
case code:is_loaded(Suite) of
{file,SuiteFile} ->
- %% test suite is already loaded and
- %% since auto_compile == false,
+ %% 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};
+ %% loaded the beam file
+ %% explicitly
+ ActualDir =
+ filename:dirname(SuiteFile),
+ {[{ActualDir,Suite}|Found],
+ NotFound};
false ->
Name =
filename:join(TestDir,
- atom_to_list(Suite)),
+ atom_to_list(
+ Suite)),
io:format(user,
"Suite ~w not found"
"in directory ~s~n",
@@ -1581,7 +1895,8 @@ verify_suites(TestSuites) ->
ActualDir = filename:dirname(SuiteFile),
{[{ActualDir,Suite}|Found],NotFound};
false ->
- io:format(user, "Directory ~s is invalid~n", [Dir]),
+ io:format(user, "Directory ~s is "
+ "invalid~n", [Dir]),
Name = filename:join(Dir, atom_to_list(Suite)),
{Found,[{DS,[Name]}|NotFound]}
end
@@ -1595,7 +1910,8 @@ save_make_errors([]) ->
save_make_errors(Errors) ->
Suites = get_bad_suites(Errors,[]),
ct_logs:log("MAKE RESULTS",
- "Error compiling or locating the following suites: ~n~p",[Suites]),
+ "Error compiling or locating the "
+ "following suites: ~n~p",[Suites]),
%% save the info for logger
file:write_file(?missing_suites_info,term_to_binary(Errors)),
Errors.
@@ -1616,8 +1932,9 @@ step(TestDir, Suite, Case) ->
%%%-----------------------------------------------------------------
%%% @hidden
%%% @equiv ct:step/4
-step(TestDir, Suite, Case, Opts) when is_list(TestDir), is_atom(Suite), is_atom(Case),
- Suite =/= all, Case =/= all ->
+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}]).
@@ -1685,22 +2002,21 @@ final_tests1([{TestDir,Suite,GrsOrCs}|Tests], Final, Skip, Bad) when
%% 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, []);
+ [ct_groups:make_conf(TestDir, Suite, all, [], all)];
({skipped,Group,TCs}) ->
- [ct_framework:make_conf(TestDir, Suite,
- Group, [skipped], TCs)];
- ({GrSpec = {Group,_},TCs}) ->
+ [ct_groups:make_conf(TestDir, Suite,
+ Group, [skipped], TCs)];
+ ({GrSpec = {GroupName,_},TCs}) ->
Props = [{override,GrSpec}],
- [ct_framework:make_conf(TestDir, Suite,
- Group, Props, TCs)];
- ({GrSpec = {Group,_,_},TCs}) ->
+ [ct_groups:make_conf(TestDir, Suite,
+ GroupName, Props, TCs)];
+ ({GrSpec = {GroupName,_,_},TCs}) ->
Props = [{override,GrSpec}],
- [ct_framework:make_conf(TestDir, Suite,
- Group, Props, TCs)];
- ({Group,TCs}) ->
- [ct_framework:make_conf(TestDir, Suite,
- Group, [], TCs)];
+ [ct_groups:make_conf(TestDir, Suite,
+ GroupName, Props, TCs)];
+ ({GroupOrGroups,TCs}) ->
+ [ct_groups:make_conf(TestDir, Suite,
+ GroupOrGroups, [], TCs)];
(TC) ->
[TC]
end, GrsOrCs),
@@ -1712,12 +2028,12 @@ final_tests1([], Final, Skip, _Bad) ->
{lists:reverse(Final),Skip}.
final_skip([{TestDir,Suite,{all,all},Reason}|Skips], Final) ->
- SkipConf = ct_framework:make_conf(TestDir, Suite, all, [], all),
+ SkipConf = ct_groups: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),
+ Conf = ct_groups:make_conf(TestDir, Suite, Group, [], TCs),
Skip = {TestDir,Suite,Conf,Reason},
final_skip(Skips, [Skip|Final]);
@@ -1735,9 +2051,11 @@ continue(_MakeErrors) ->
case set_group_leader_same_as_shell() of
true ->
S = self(),
- io:format("Failed to compile or locate one or more test suites\n"
+ io:format("Failed to compile or locate one "
+ "or more test suites\n"
"Press \'c\' to continue or \'a\' to abort.\n"
- "Will continue in 15 seconds if no answer is given!\n"),
+ "Will continue in 15 seconds if no "
+ "answer is given!\n"),
Pid = spawn(fun() ->
case io:get_line('(c/a) ') of
"c\n" ->
@@ -1769,7 +2087,8 @@ set_group_leader_same_as_shell() ->
end
end,
case [P || P <- processes(), GS2or3(P),
- true == lists:keymember(shell,1,element(2,process_info(P,dictionary)))] of
+ true == lists:keymember(shell,1,
+ element(2,process_info(P,dictionary)))] of
[GL|_] ->
group_leader(GL, self());
[] ->
@@ -1815,12 +2134,14 @@ do_run_test(Tests, Skip, Opts) ->
incl_mods = CovIncl,
cross = CovCross,
src = _CovSrc}} ->
- ct_logs:log("COVER INFO","Using cover specification file: ~s~n"
+ 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
@@ -1828,7 +2149,8 @@ do_run_test(Tests, Skip, Opts) ->
true ->
DelResult = file:delete(CovExport),
ct_logs:log("COVER INFO",
- "Warning! Export file ~s already exists. "
+ "Warning! "
+ "Export file ~s already exists. "
"Deleting with result: ~p",
[CovExport,DelResult]);
false ->
@@ -1838,13 +2160,15 @@ do_run_test(Tests, Skip, Opts) ->
%% tell test_server which modules should be cover compiled
%% note that actual compilation is done when tests start
test_server_ctrl:cover(CovApp, CovFile, CovExcl, CovIncl,
- CovCross, CovExport, CovLevel),
+ CovCross, CovExport, CovLevel,
+ Opts#opts.cover_stop),
%% save cover data (used e.g. to add nodes dynamically)
ct_util:set_testdata({cover,CovData}),
%% start cover on specified nodes
if (CovNodes /= []) and (CovNodes /= undefined) ->
ct_logs:log("COVER INFO",
- "Nodes included in cover session: ~w",
+ "Nodes included in cover "
+ "session: ~w",
[CovNodes]),
cover:start(CovNodes);
true ->
@@ -1869,17 +2193,27 @@ do_run_test(Tests, Skip, Opts) ->
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,
-
+ %% if the verbosity level is set lower than ?STD_IMPORTANCE, tell
+ %% test_server to ignore stdout printouts to the test case log file
+ case proplists:get_value(default, Opts#opts.verbosity) of
+ VLvl when is_integer(VLvl), (?STD_IMPORTANCE < (100-VLvl)) ->
+ test_server_ctrl:reject_io_reqs(true);
+ _Lower ->
+ ok
+ end,
test_server_ctrl:multiply_timetraps(Opts#opts.multiply_timetraps),
test_server_ctrl:scale_timetraps(Opts#opts.scale_timetraps),
- test_server_ctrl:create_priv_dir(choose_val(Opts#opts.create_priv_dir,
- auto_per_run)),
+ test_server_ctrl:create_priv_dir(choose_val(
+ Opts#opts.create_priv_dir,
+ auto_per_run)),
ct_event:notify(#event{name=start_info,
node=node(),
data={NoOfTests,NoOfSuites,NoOfCases}}),
@@ -1898,9 +2232,24 @@ do_run_test(Tests, Skip, Opts) ->
maybe_cleanup_interpret(Suite, Opts#opts.step)
end, CleanUp),
[code:del_path(Dir) || Dir <- AddedToPath],
- ok;
+
+ %% If a severe error has occurred in the test_server,
+ %% we will generate an exception here.
+ case ct_util:get_testdata(severe_error) of
+ undefined -> ok;
+ SevereError ->
+ ct_logs:log("SEVERE ERROR", "~p\n", [SevereError]),
+ exit(SevereError)
+ end,
+
+ case ct_util:get_testdata(stats) of
+ Stats = {_Ok,_Failed,{_UserSkipped,_AutoSkipped}} ->
+ Stats;
+ _ ->
+ {error,test_result_unknown}
+ end;
Error ->
- Error
+ exit(Error)
end.
delete_dups([S | Suites]) ->
@@ -1957,9 +2306,11 @@ add_jobs([{TestDir,all,_}|Tests], Skip, Opts, CleanUp) ->
wait_for_idle(),
add_jobs(Tests, Skip, Opts, CleanUp)
end;
-add_jobs([{TestDir,[Suite],all}|Tests], Skip, Opts, CleanUp) when is_atom(Suite) ->
+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) ->
+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
@@ -1974,7 +2325,8 @@ add_jobs([{TestDir,Suite,all}|Tests], Skip, Opts, CleanUp) ->
ok ->
Name = get_name(TestDir) ++ "." ++ atom_to_list(Suite),
case catch test_server_ctrl:add_module_with_skip(Name, [Suite],
- skiplist(TestDir,Skip)) of
+ skiplist(TestDir,
+ Skip)) of
{'EXIT',_} ->
CleanUp;
_ ->
@@ -1997,15 +2349,24 @@ add_jobs([{TestDir,Suite,Confs}|Tests], Skip, Opts, CleanUp) when
GrTestName =
case Confs of
[Conf] ->
- "." ++ atom_to_list(Group(Conf)) ++ TCTestName(TestCases(Conf));
+ case Group(Conf) of
+ GrName when is_atom(GrName) ->
+ "." ++ atom_to_list(GrName) ++
+ TCTestName(TestCases(Conf));
+ _ ->
+ ".groups" ++ TCTestName(TestCases(Conf))
+ end;
_ ->
".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
+ case catch test_server_ctrl:add_conf_with_skip(TestName,
+ Suite,
+ Confs,
+ skiplist(TestDir,
+ Skip)) of
{'EXIT',_} ->
CleanUp;
_ ->
@@ -2017,18 +2378,21 @@ add_jobs([{TestDir,Suite,Confs}|Tests], Skip, Opts, CleanUp) when
end;
%% test case
-add_jobs([{TestDir,Suite,[Case]}|Tests], Skip, Opts, CleanUp) when is_atom(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) ->
+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",
+ Name = get_name(TestDir) ++ "." ++ atom_to_list(Suite) ++ ".cases",
case catch test_server_ctrl:add_cases_with_skip(Name, Suite, Cases1,
- skiplist(TestDir,Skip)) of
+ skiplist(TestDir,
+ Skip)) of
{'EXIT',_} ->
CleanUp;
_ ->
@@ -2044,7 +2408,8 @@ add_jobs([{TestDir,Suite,Case}|Tests], Skip, Opts, CleanUp) when is_atom(Case) -
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
+ skiplist(TestDir,
+ Skip)) of
{'EXIT',_} ->
CleanUp;
_ ->
@@ -2079,7 +2444,8 @@ skiplist(Dir, [{Dir,all,Cmt}|Skip]) ->
%% we need to turn 'all' into list of modules since
%% test_server doesn't do skips on Dir level
Ss = filelib:wildcard(filename:join(Dir, "*_SUITE.beam")),
- [{list_to_atom(filename:basename(S,".beam")),Cmt} || S <- Ss] ++ skiplist(Dir,Skip);
+ [{list_to_atom(filename:basename(S,".beam")),Cmt} || S <- Ss] ++
+ skiplist(Dir,Skip);
skiplist(Dir, [{Dir,S,Cmt}|Skip]) ->
[{S,Cmt} | skiplist(Dir, Skip)];
skiplist(Dir, [{Dir,S,C,Cmt}|Skip]) ->
@@ -2139,8 +2505,10 @@ run_make(Targets, TestDir0, Mod, UserInclude) ->
FileTest = fun(F, suites) -> is_suite(F);
(F, helpmods) -> not is_suite(F)
end,
- Files = lists:flatmap(fun({F,out_of_date}) ->
- case FileTest(F, Targets) of
+ Files =
+ lists:flatmap(fun({F,out_of_date}) ->
+ case FileTest(F,
+ Targets) of
true -> [F];
false -> []
end;
@@ -2276,6 +2644,9 @@ merge_arguments([LogDir={logdir,_}|Args], Merged) ->
merge_arguments([CoverFile={cover,_}|Args], Merged) ->
merge_arguments(Args, handle_arg(replace, CoverFile, Merged));
+merge_arguments([CoverStop={cover_stop,_}|Args], Merged) ->
+ merge_arguments(Args, handle_arg(replace, CoverStop, Merged));
+
merge_arguments([{'case',TC}|Args], Merged) ->
merge_arguments(Args, handle_arg(merge, {testcase,TC}, Merged));
@@ -2357,7 +2728,6 @@ parse_cth_args(String) ->
String
end.
-
event_handler_args2opts(Args) ->
case proplists:get_value(event_handler, Args) of
undefined ->
@@ -2380,6 +2750,42 @@ event_handler_init_args2opts([EH, Arg]) ->
event_handler_init_args2opts([]) ->
[].
+verbosity_args2opts(Args) ->
+ case proplists:get_value(verbosity, Args) of
+ undefined ->
+ [];
+ VArgs ->
+ GetVLvls =
+ fun("and", {new,SoFar}) when is_list(SoFar) ->
+ {new,SoFar};
+ ("and", {Lvl,SoFar}) when is_list(SoFar) ->
+ {new,[{'$unspecified',list_to_integer(Lvl)} | SoFar]};
+ (CatOrLvl, {new,SoFar}) when is_list(SoFar) ->
+ {CatOrLvl,SoFar};
+ (Lvl, {Cat,SoFar}) ->
+ {new,[{list_to_atom(Cat),list_to_integer(Lvl)} | SoFar]}
+ end,
+ case lists:foldl(GetVLvls, {new,[]}, VArgs) of
+ {new,Parsed} ->
+ Parsed;
+ {Lvl,Parsed} ->
+ [{'$unspecified',list_to_integer(Lvl)} | Parsed]
+ end
+ end.
+
+add_verbosity_defaults(VLvls) ->
+ case {proplists:get_value('$unspecified', VLvls),
+ proplists:get_value(default, VLvls)} of
+ {undefined,undefined} ->
+ ?default_verbosity ++ VLvls;
+ {Lvl,undefined} ->
+ [{default,Lvl} | VLvls];
+ {undefined,_Lvl} ->
+ [{'$unspecified',?MAX_VERBOSITY} | VLvls];
+ _ ->
+ VLvls
+ end.
+
%% 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
@@ -2446,21 +2852,40 @@ make_abs1([], Path) ->
%% 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]}];
+ lists:flatmap(fun({exit_status,ExitStatusOpt}) when is_atom(ExitStatusOpt) ->
+ [{exit_status,[atom_to_list(ExitStatusOpt)]}];
+ ({halt_with,{HaltM,HaltF}}) ->
+ [{halt_with,[atom_to_list(HaltM),
+ atom_to_list(HaltF)]}];
+ ({interactive_mode,true}) ->
+ [{shell,[]}];
+ ({config,CfgFile}) when is_integer(hd(CfgFile)) ->
+ [{ct_config,[CfgFile]}];
+ ({config,CfgFiles}) when is_list(hd(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"]
+ 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)}];
+ ({group,G}) when is_atom(G) ->
+ [{group,[atom_to_list(G)]}];
+ ({group,Gs}) when is_list(Gs) ->
+ LOfGStrs = [lists:flatten(io_lib:format("~w",[G])) ||
+ G <- Gs],
+ [{group,LOfGStrs}];
({testcase,Case}) when is_atom(Case) ->
[{'case',[atom_to_list(Case)]}];
({testcase,Cases}) ->
@@ -2492,7 +2917,7 @@ opts2args(EnvStartOpts) ->
({decrypt,{file,File}}) ->
[{ct_decrypt_file,[File]}];
({basic_html,true}) ->
- ({basic_html,[]});
+ [{basic_html,[]}];
({basic_html,false}) ->
[];
({event_handler,EH}) when is_atom(EH) ->
@@ -2505,12 +2930,32 @@ opts2args(EnvStartOpts) ->
({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"]
+ [atom_to_list(EH),
+ ArgStr,"and"]
end, EHs),
[_LastAnd|StrsR] = lists:reverse(lists:flatten(Strs)),
[{event_handler_init,lists:reverse(StrsR)}];
({logopts,LOs}) when is_list(LOs) ->
[{logopts,[atom_to_list(LO) || LO <- LOs]}];
+ ({verbosity,?default_verbosity}) ->
+ [];
+ ({verbosity,VLvl}) when is_integer(VLvl) ->
+ [{verbosity,[integer_to_list(VLvl)]}];
+ ({verbosity,VLvls}) when is_list(VLvls) ->
+ VLvlArgs =
+ lists:flatmap(fun({'$unspecified',Lvl}) ->
+ [integer_to_list(Lvl),
+ "and"];
+ ({Cat,Lvl}) ->
+ [atom_to_list(Cat),
+ integer_to_list(Lvl),
+ "and"];
+ (Lvl) ->
+ [integer_to_list(Lvl),
+ "and"]
+ end, VLvls),
+ [_LastAnd|VLvlArgsR] = lists:reverse(VLvlArgs),
+ [{verbosity,lists:reverse(VLvlArgsR)}];
({ct_hooks,[]}) ->
[];
({ct_hooks,CTHs}) when is_list(CTHs) ->
diff --git a/lib/common_test/src/ct_slave.erl b/lib/common_test/src/ct_slave.erl
index aa3413fa89..1fd8c04f8b 100644
--- a/lib/common_test/src/ct_slave.erl
+++ b/lib/common_test/src/ct_slave.erl
@@ -1,7 +1,7 @@
%%--------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -37,7 +37,7 @@
-record(options, {username, password, boot_timeout, init_timeout,
startup_timeout, startup_functions, monitor_master,
- kill_if_fail, erl_flags}).
+ kill_if_fail, erl_flags, env}).
%%%-----------------------------------------------------------------
%%% @spec start(Node) -> Result
@@ -85,7 +85,8 @@ start(Host, Node) ->
%%% {startup_functions, StartupFunctions} |
%%% {monitor_master, Monitor} |
%%% {kill_if_fail, KillIfFail} |
-%%% {erl_flags, ErlangFlags}
+%%% {erl_flags, ErlangFlags} |
+%%% {env, [{EnvVar,Value}]}
%%% Username = string()
%%% Password = string()
%%% BootTimeout = integer()
@@ -99,6 +100,8 @@ start(Host, Node) ->
%%% Monitor = bool()
%%% KillIfFail = bool()
%%% ErlangFlags = string()
+%%% EnvVar = string()
+%%% Value = string()
%%% Result = {ok, NodeName} | {error, already_started, NodeName} |
%%% {error, started_not_connected, NodeName} |
%%% {error, boot_timeout, NodeName} |
@@ -152,6 +155,9 @@ start(Host, Node) ->
%%% <p>Option <code>erlang_flags</code> specifies, which flags will be added
%%% to the parameters of the <code>erl</code> executable.</p>
%%%
+%%% <p>Option <code>env</code> specifies a list of environment variables
+%%% that will extended the environment.</p>
+%%%
%%% <p>Special return values are:
%%% <list>
%%% <item><code>{error, already_started, NodeName}</code> - if the node with
@@ -233,10 +239,12 @@ fetch_options(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, []),
+ EnvVars = get_option_value(env, 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}.
+ monitor_master=Monitor, kill_if_fail=KillIfFail,
+ erl_flags=ErlFlags, env=EnvVars}.
% send a message when slave node is started
% @hidden
@@ -306,11 +314,19 @@ do_start(Host, 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->
+ case test_server:is_cover() of
+ true ->
+ MainCoverNode = cover:get_main_node(),
+ rpc:call(MainCoverNode,cover,start,[ENode]);
+ false ->
+ ok
+ end,
call_functions(ENode, Functions2),
receive
{node_started, ENode}->
@@ -365,9 +381,9 @@ get_cmd(Node, Flags) ->
% spawn node locally
spawn_local_node(Node, Options) ->
- ErlFlags = Options#options.erl_flags,
+ #options{env=Env,erl_flags=ErlFlags} = Options,
Cmd = get_cmd(Node, ErlFlags),
- open_port({spawn, Cmd}, [stream]).
+ open_port({spawn, Cmd}, [stream,{env,Env}]).
% start crypto and ssh if not yet started
check_for_ssh_running() ->
@@ -386,9 +402,10 @@ check_for_ssh_running() ->
% spawn node remotely
spawn_remote_node(Host, Node, Options) ->
- Username = Options#options.username,
- Password = Options#options.password,
- ErlFlags = Options#options.erl_flags,
+ #options{username=Username,
+ password=Password,
+ erl_flags=ErlFlags,
+ env=Env} = Options,
SSHOptions = case {Username, Password} of
{[], []}->
[];
@@ -400,8 +417,17 @@ spawn_remote_node(Host, Node, Options) ->
check_for_ssh_running(),
{ok, SSHConnRef} = ssh:connect(atom_to_list(Host), 22, SSHOptions),
{ok, SSHChannelId} = ssh_connection:session_channel(SSHConnRef, infinity),
+ ssh_setenv(SSHConnRef, SSHChannelId, Env),
ssh_connection:exec(SSHConnRef, SSHChannelId, get_cmd(Node, ErlFlags), infinity).
+
+ssh_setenv(SSHConnRef, SSHChannelId, [{Var, Value} | Vars])
+ when is_list(Var), is_list(Value) ->
+ success = ssh_connection:setenv(SSHConnRef, SSHChannelId,
+ Var, Value, infinity),
+ ssh_setenv(SSHConnRef, SSHChannelId, Vars);
+ssh_setenv(_SSHConnRef, _SSHChannelId, []) -> ok.
+
% call functions on a remote Erlang node
call_functions(_Node, []) ->
ok;
@@ -423,8 +449,29 @@ wait_for_node_alive(Node, N) ->
% call init:stop on a remote node
do_stop(ENode) ->
+ {Cover,MainCoverNode} =
+ case test_server:is_cover() of
+ true ->
+ Main = cover:get_main_node(),
+ rpc:call(Main,cover,flush,[ENode]),
+ {true,Main};
+ false ->
+ {false,undefined}
+ end,
spawn(ENode, init, stop, []),
- wait_for_node_dead(ENode, 5).
+ case wait_for_node_dead(ENode, 5) of
+ {ok,ENode} ->
+ if Cover ->
+ %% To avoid that cover is started again if a node
+ %% with the same name is started later.
+ rpc:call(MainCoverNode,cover,stop,[ENode]);
+ true ->
+ ok
+ end,
+ {ok,ENode};
+ Error ->
+ Error
+ end.
% wait N seconds until node is disconnected
wait_for_node_dead(Node, 0) ->
diff --git a/lib/common_test/src/ct_snmp.erl b/lib/common_test/src/ct_snmp.erl
index 8fe63e8ed1..71038bd4f4 100644
--- a/lib/common_test/src/ct_snmp.erl
+++ b/lib/common_test/src/ct_snmp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -39,7 +39,7 @@
%%% %%% Manager config
%%% [{start_manager, boolean()} % Optional - default is true
%%% {users, [{user_name(), [call_back_module(), user_data()]}]}, %% Optional
-%%% {usm_users, [{usm_user_name(), usm_config()}]},%% Optional - snmp v3 only
+%%% {usm_users, [{usm_user_name(), [usm_config()]}]},%% Optional - snmp v3 only
%%% % managed_agents is optional
%%% {managed_agents,[{agent_name(), [user_name(), agent_ip(), agent_port(), [agent_config()]]}]},
%%% {max_msg_size, integer()}, % Optional - default is 484
@@ -130,7 +130,7 @@
%%% @type agent_config() = {Item, Value}
%%% @type user_name() = atom()
%%% @type usm_user_name() = string()
-%%% @type usm_config() = string()
+%%% @type usm_config() = {Item, Value}
%%% @type call_back_module() = atom()
%%% @type user_data() = term()
%%% @type oids() = [oid()]
@@ -157,8 +157,9 @@
%%% API
-export([start/2, start/3, stop/1, get_values/3, get_next_values/3, set_values/4,
set_info/1, register_users/2, register_agents/2, register_usm_users/2,
- unregister_users/1, unregister_agents/1, update_usm_users/2,
- load_mibs/1]).
+ unregister_users/1, unregister_users/2, unregister_agents/1,
+ unregister_agents/2, unregister_usm_users/1, unregister_usm_users/2,
+ load_mibs/1, unload_mibs/1]).
%% Manager values
-define(CT_SNMP_LOG_FILE, "ct_snmp_set.log").
@@ -250,10 +251,8 @@ stop(Config) ->
%%%
%%% @doc Issues a synchronous snmp get request.
get_values(Agent, Oids, MgrAgentConfName) ->
- [Uid, AgentIp, AgentUdpPort | _] =
- agent_conf(Agent, MgrAgentConfName),
- {ok, SnmpReply, _} =
- snmpm:g(Uid, AgentIp, AgentUdpPort, Oids),
+ [Uid | _] = agent_conf(Agent, MgrAgentConfName),
+ {ok, SnmpReply, _} = snmpm:sync_get2(Uid, target_name(Agent), Oids),
SnmpReply.
%%% @spec get_next_values(Agent, Oids, MgrAgentConfName) -> SnmpReply
@@ -265,10 +264,8 @@ get_values(Agent, Oids, MgrAgentConfName) ->
%%%
%%% @doc Issues a synchronous snmp get next request.
get_next_values(Agent, Oids, MgrAgentConfName) ->
- [Uid, AgentIp, AgentUdpPort | _] =
- agent_conf(Agent, MgrAgentConfName),
- {ok, SnmpReply, _} =
- snmpm:gn(Uid, AgentIp, AgentUdpPort, Oids),
+ [Uid | _] = agent_conf(Agent, MgrAgentConfName),
+ {ok, SnmpReply, _} = snmpm:sync_get_next2(Uid, target_name(Agent), Oids),
SnmpReply.
%%% @spec set_values(Agent, VarsAndVals, MgrAgentConfName, Config) -> SnmpReply
@@ -282,13 +279,11 @@ get_next_values(Agent, Oids, MgrAgentConfName) ->
%%% @doc Issues a synchronous snmp set request.
set_values(Agent, VarsAndVals, MgrAgentConfName, Config) ->
PrivDir = ?config(priv_dir, Config),
- [Uid, AgentIp, AgentUdpPort | _] =
- agent_conf(Agent, MgrAgentConfName),
+ [Uid | _] = agent_conf(Agent, MgrAgentConfName),
Oids = lists:map(fun({Oid, _, _}) -> Oid end, VarsAndVals),
- {ok, SnmpGetReply, _} =
- snmpm:g(Uid, AgentIp, AgentUdpPort, Oids),
- {ok, SnmpSetReply, _} =
- snmpm:s(Uid, AgentIp, AgentUdpPort, VarsAndVals),
+ TargetName = target_name(Agent),
+ {ok, SnmpGetReply, _} = snmpm:sync_get2(Uid, TargetName, Oids),
+ {ok, SnmpSetReply, _} = snmpm:sync_set2(Uid, TargetName, VarsAndVals),
case SnmpSetReply of
{noError, 0, _} when PrivDir /= false ->
log(PrivDir, Agent, SnmpGetReply, VarsAndVals);
@@ -328,12 +323,23 @@ set_info(Config) ->
%%% Reason = term()
%%%
%%% @doc Register the manager entity (=user) responsible for specific agent(s).
-%%% Corresponds to making an entry in users.conf
+%%% Corresponds to making an entry in users.conf.
+%%%
+%%% This function will try to register the given users, without
+%%% checking if any of them already exist. In order to change an
+%%% already registered user, the user must first be unregistered.
register_users(MgrAgentConfName, Users) ->
- {snmp, SnmpVals} = ct:get_config(MgrAgentConfName),
- NewSnmpVals = lists:keyreplace(users, 1, SnmpVals, {users, Users}),
- ct_config:update_config(MgrAgentConfName, {snmp, NewSnmpVals}),
- setup_users(Users).
+ case setup_users(Users) of
+ ok ->
+ SnmpVals = ct:get_config(MgrAgentConfName),
+ OldUsers = ct:get_config({MgrAgentConfName,users},[]),
+ NewSnmpVals = lists:keystore(users, 1, SnmpVals,
+ {users, Users ++ OldUsers}),
+ ct_config:update_config(MgrAgentConfName, NewSnmpVals),
+ ok;
+ Error ->
+ Error
+ end.
%%% @spec register_agents(MgrAgentConfName, ManagedAgents) -> ok | {error, Reason}
%%%
@@ -343,12 +349,24 @@ register_users(MgrAgentConfName, Users) ->
%%%
%%% @doc Explicitly instruct the manager to handle this agent.
%%% Corresponds to making an entry in agents.conf
+%%%
+%%% This function will try to register the given managed agents,
+%%% without checking if any of them already exist. In order to change
+%%% an already registered managed agent, the agent must first be
+%%% unregistered.
register_agents(MgrAgentConfName, ManagedAgents) ->
- {snmp, SnmpVals} = ct:get_config(MgrAgentConfName),
- NewSnmpVals = lists:keyreplace(managed_agents, 1, SnmpVals,
- {managed_agents, ManagedAgents}),
- ct_config:update_config(MgrAgentConfName, {snmp, NewSnmpVals}),
- setup_managed_agents(ManagedAgents).
+ case setup_managed_agents(MgrAgentConfName,ManagedAgents) of
+ ok ->
+ SnmpVals = ct:get_config(MgrAgentConfName),
+ OldAgents = ct:get_config({MgrAgentConfName,managed_agents},[]),
+ NewSnmpVals = lists:keystore(managed_agents, 1, SnmpVals,
+ {managed_agents,
+ ManagedAgents ++ OldAgents}),
+ ct_config:update_config(MgrAgentConfName, NewSnmpVals),
+ ok;
+ Error ->
+ Error
+ end.
%%% @spec register_usm_users(MgrAgentConfName, UsmUsers) -> ok | {error, Reason}
%%%
@@ -358,60 +376,115 @@ register_agents(MgrAgentConfName, ManagedAgents) ->
%%%
%%% @doc Explicitly instruct the manager to handle this USM user.
%%% Corresponds to making an entry in usm.conf
+%%%
+%%% This function will try to register the given users, without
+%%% checking if any of them already exist. In order to change an
+%%% already registered user, the user must first be unregistered.
register_usm_users(MgrAgentConfName, UsmUsers) ->
- {snmp, SnmpVals} = ct:get_config(MgrAgentConfName),
- NewSnmpVals = lists:keyreplace(users, 1, SnmpVals, {usm_users, UsmUsers}),
- ct_config:update_config(MgrAgentConfName, {snmp, NewSnmpVals}),
EngineID = ct:get_config({MgrAgentConfName, engine_id}, ?ENGINE_ID),
- setup_usm_users(UsmUsers, EngineID).
+ case setup_usm_users(UsmUsers, EngineID) of
+ ok ->
+ SnmpVals = ct:get_config(MgrAgentConfName),
+ OldUsmUsers = ct:get_config({MgrAgentConfName,usm_users},[]),
+ NewSnmpVals = lists:keystore(usm_users, 1, SnmpVals,
+ {usm_users, UsmUsers ++ OldUsmUsers}),
+ ct_config:update_config(MgrAgentConfName, NewSnmpVals),
+ ok;
+ Error ->
+ Error
+ end.
-%%% @spec unregister_users(MgrAgentConfName) -> ok | {error, Reason}
+%%% @spec unregister_users(MgrAgentConfName) -> ok
%%%
%%% MgrAgentConfName = atom()
%%% Reason = term()
%%%
-%%% @doc Removes information added when calling register_users/2.
+%%% @doc Unregister all users.
unregister_users(MgrAgentConfName) ->
- Users = lists:map(fun({UserName, _}) -> UserName end,
- ct:get_config({MgrAgentConfName, users})),
- {snmp, SnmpVals} = ct:get_config(MgrAgentConfName),
- NewSnmpVals = lists:keyreplace(users, 1, SnmpVals, {users, []}),
- ct_config:update_config(MgrAgentConfName, {snmp, NewSnmpVals}),
- takedown_users(Users).
+ Users = [Id || {Id,_} <- ct:get_config({MgrAgentConfName, users},[])],
+ unregister_users(MgrAgentConfName,Users).
-%%% @spec unregister_agents(MgrAgentConfName) -> ok | {error, Reason}
+%%% @spec unregister_users(MgrAgentConfName,Users) -> ok
%%%
%%% MgrAgentConfName = atom()
+%%% Users = [user_name()]
%%% Reason = term()
%%%
-%%% @doc Removes information added when calling register_agents/2.
+%%% @doc Unregister the given users.
+unregister_users(MgrAgentConfName,Users) ->
+ takedown_users(Users),
+ SnmpVals = ct:get_config(MgrAgentConfName),
+ AllUsers = ct:get_config({MgrAgentConfName, users},[]),
+ RemainingUsers = lists:filter(fun({Id,_}) ->
+ not lists:member(Id,Users)
+ end,
+ AllUsers),
+ NewSnmpVals = lists:keyreplace(users, 1, SnmpVals, {users,RemainingUsers}),
+ ct_config:update_config(MgrAgentConfName, NewSnmpVals),
+ ok.
+
+%%% @spec unregister_agents(MgrAgentConfName) -> ok
+%%%
+%%% MgrAgentConfName = atom()
+%%% Reason = term()
+%%%
+%%% @doc Unregister all managed agents.
unregister_agents(MgrAgentConfName) ->
- ManagedAgents = lists:map(fun({_, [Uid, AgentIP, AgentPort, _]}) ->
- {Uid, AgentIP, AgentPort}
- end,
- ct:get_config({MgrAgentConfName, managed_agents})),
- {snmp, SnmpVals} = ct:get_config(MgrAgentConfName),
- NewSnmpVals = lists:keyreplace(managed_agents, 1, SnmpVals,
- {managed_agents, []}),
- ct_config:update_config(MgrAgentConfName, {snmp, NewSnmpVals}),
- takedown_managed_agents(ManagedAgents).
+ ManagedAgents = [AgentName ||
+ {AgentName, _} <-
+ ct:get_config({MgrAgentConfName,managed_agents},[])],
+ unregister_agents(MgrAgentConfName,ManagedAgents).
+%%% @spec unregister_agents(MgrAgentConfName,ManagedAgents) -> ok
+%%%
+%%% MgrAgentConfName = atom()
+%%% ManagedAgents = [agent_name()]
+%%% Reason = term()
+%%%
+%%% @doc Unregister the given managed agents.
+unregister_agents(MgrAgentConfName,ManagedAgents) ->
+ takedown_managed_agents(MgrAgentConfName, ManagedAgents),
+ SnmpVals = ct:get_config(MgrAgentConfName),
+ AllAgents = ct:get_config({MgrAgentConfName,managed_agents},[]),
+ RemainingAgents = lists:filter(fun({Name,_}) ->
+ not lists:member(Name,ManagedAgents)
+ end,
+ AllAgents),
+ NewSnmpVals = lists:keyreplace(managed_agents, 1, SnmpVals,
+ {managed_agents,RemainingAgents}),
+ ct_config:update_config(MgrAgentConfName, NewSnmpVals),
+ ok.
-%%% @spec update_usm_users(MgrAgentConfName, UsmUsers) -> ok | {error, Reason}
+%%% @spec unregister_usm_users(MgrAgentConfName) -> ok
%%%
%%% MgrAgentConfName = atom()
-%%% UsmUsers = usm_users()
%%% Reason = term()
%%%
-%%% @doc Alters information added when calling register_usm_users/2.
-update_usm_users(MgrAgentConfName, UsmUsers) ->
-
- {snmp, SnmpVals} = ct:get_config(MgrAgentConfName),
- NewSnmpVals = lists:keyreplace(usm_users, 1, SnmpVals,
- {usm_users, UsmUsers}),
- ct_config:update_config(MgrAgentConfName, {snmp, NewSnmpVals}),
+%%% @doc Unregister all usm users.
+unregister_usm_users(MgrAgentConfName) ->
+ UsmUsers = [Id || {Id,_} <- ct:get_config({MgrAgentConfName, usm_users},[])],
+ unregister_usm_users(MgrAgentConfName,UsmUsers).
+
+%%% @spec unregister_usm_users(MgrAgentConfName,UsmUsers) -> ok
+%%%
+%%% MgrAgentConfName = atom()
+%%% UsmUsers = [usm_user_name()]
+%%% Reason = term()
+%%%
+%%% @doc Unregister the given usm users.
+unregister_usm_users(MgrAgentConfName,UsmUsers) ->
EngineID = ct:get_config({MgrAgentConfName, engine_id}, ?ENGINE_ID),
- do_update_usm_users(UsmUsers, EngineID).
+ takedown_usm_users(UsmUsers,EngineID),
+ SnmpVals = ct:get_config(MgrAgentConfName),
+ AllUsmUsers = ct:get_config({MgrAgentConfName, usm_users},[]),
+ RemainingUsmUsers = lists:filter(fun({Id,_}) ->
+ not lists:member(Id,UsmUsers)
+ end,
+ AllUsmUsers),
+ NewSnmpVals = lists:keyreplace(usm_users, 1, SnmpVals,
+ {usm_users,RemainingUsmUsers}),
+ ct_config:update_config(MgrAgentConfName, NewSnmpVals),
+ ok.
%%% @spec load_mibs(Mibs) -> ok | {error, Reason}
%%%
@@ -423,6 +496,15 @@ update_usm_users(MgrAgentConfName, UsmUsers) ->
load_mibs(Mibs) ->
snmpa:load_mibs(snmp_master_agent, Mibs).
+%%% @spec unload_mibs(Mibs) -> ok | {error, Reason}
+%%%
+%%% Mibs = [MibName]
+%%% MibName = string()
+%%% Reason = term()
+%%%
+%%% @doc Unload the mibs from the agent 'snmp_master_agent'.
+unload_mibs(Mibs) ->
+ snmpa:unload_mibs(snmp_master_agent, Mibs).
%%%========================================================================
%%% Internal functions
@@ -486,9 +568,8 @@ setup_agent(true, AgentConfName, SnmpConfName,
file:make_dir(DbDir),
snmp_config:write_agent_snmp_files(ConfDir, Vsns, ManagerIP, TrapUdp,
AgentIP, AgentUdp, SysName,
- atom_to_list(NotifType),
- SecType, Passwd, AgentEngineID,
- AgentMaxMsgSize),
+ NotifType, SecType, Passwd,
+ AgentEngineID, AgentMaxMsgSize),
override_default_configuration(Config, AgentConfName),
@@ -497,7 +578,8 @@ setup_agent(true, AgentConfName, SnmpConfName,
{verbosity, trace}]},
{agent_type, master},
{agent_verbosity, trace},
- {net_if, [{verbosity, trace}]}],
+ {net_if, [{verbosity, trace}]},
+ {versions, Vsns}],
ct:get_config({SnmpConfName,agent})),
application:set_env(snmp, agent, SnmpEnv).
%%%---------------------------------------------------------------------------
@@ -535,65 +617,61 @@ manager_register(true, MgrAgentConfName) ->
setup_usm_users(UsmUsers, EngineID),
setup_users(Users),
- setup_managed_agents(Agents).
+ setup_managed_agents(MgrAgentConfName,Agents).
%%%---------------------------------------------------------------------------
setup_users(Users) ->
- lists:foreach(fun({Id, [Module, Data]}) ->
- snmpm:register_user(Id, Module, Data)
- end, Users).
+ while_ok(fun({Id, [Module, Data]}) ->
+ snmpm:register_user(Id, Module, Data)
+ end, Users).
%%%---------------------------------------------------------------------------
-setup_managed_agents([]) ->
- ok;
-
-setup_managed_agents([{_, [Uid, AgentIp, AgentUdpPort, AgentConf]} |
- Rest]) ->
- NewAgentIp = case AgentIp of
- IpTuple when is_tuple(IpTuple) ->
- IpTuple;
- HostName when is_list(HostName) ->
- {ok,Hostent} = inet:gethostbyname(HostName),
- [IpTuple|_] = Hostent#hostent.h_addr_list,
- IpTuple
- end,
- ok = snmpm:register_agent(Uid, NewAgentIp, AgentUdpPort),
- lists:foreach(fun({Item, Val}) ->
- snmpm:update_agent_info(Uid, NewAgentIp,
- AgentUdpPort, Item, Val)
- end, AgentConf),
- setup_managed_agents(Rest).
+setup_managed_agents(AgentConfName,Agents) ->
+ Fun =
+ fun({AgentName, [Uid, AgentIp, AgentUdpPort, AgentConf0]}) ->
+ NewAgentIp = case AgentIp of
+ IpTuple when is_tuple(IpTuple) ->
+ IpTuple;
+ HostName when is_list(HostName) ->
+ {ok,Hostent} = inet:gethostbyname(HostName),
+ [IpTuple|_] = Hostent#hostent.h_addr_list,
+ IpTuple
+ end,
+ AgentConf =
+ case lists:keymember(engine_id,1,AgentConf0) of
+ true ->
+ AgentConf0;
+ false ->
+ DefaultEngineID =
+ ct:get_config({AgentConfName,agent_engine_id},
+ ?AGENT_ENGINE_ID),
+ [{engine_id,DefaultEngineID}|AgentConf0]
+ end,
+ snmpm:register_agent(Uid, target_name(AgentName),
+ [{address,NewAgentIp},{port,AgentUdpPort} |
+ AgentConf])
+ end,
+ while_ok(Fun,Agents).
%%%---------------------------------------------------------------------------
setup_usm_users(UsmUsers, EngineID)->
- lists:foreach(fun({UsmUser, Conf}) ->
- snmpm:register_usm_user(EngineID, UsmUser, Conf)
- end, UsmUsers).
+ while_ok(fun({UsmUser, Conf}) ->
+ snmpm:register_usm_user(EngineID, UsmUser, Conf)
+ end, UsmUsers).
%%%---------------------------------------------------------------------------
takedown_users(Users) ->
- lists:foreach(fun({Id}) ->
+ lists:foreach(fun(Id) ->
snmpm:unregister_user(Id)
end, Users).
%%%---------------------------------------------------------------------------
-takedown_managed_agents([{Uid, AgentIp, AgentUdpPort} |
- Rest]) ->
- NewAgentIp = case AgentIp of
- IpTuple when is_tuple(IpTuple) ->
- IpTuple;
- HostName when is_list(HostName) ->
- {ok,Hostent} = inet:gethostbyname(HostName),
- [IpTuple|_] = Hostent#hostent.h_addr_list,
- IpTuple
- end,
- ok = snmpm:unregister_agent(Uid, NewAgentIp, AgentUdpPort),
- takedown_managed_agents(Rest);
-
-takedown_managed_agents([]) ->
- ok.
+takedown_managed_agents(MgrAgentConfName,ManagedAgents) ->
+ lists:foreach(fun(AgentName) ->
+ [Uid | _] = agent_conf(AgentName, MgrAgentConfName),
+ snmpm:unregister_agent(Uid, target_name(AgentName))
+ end, ManagedAgents).
%%%---------------------------------------------------------------------------
-do_update_usm_users(UsmUsers, EngineID) ->
- lists:foreach(fun({UsmUser, {Item, Val}}) ->
- snmpm:update_usm_user_info(EngineID, UsmUser,
- Item, Val)
- end, UsmUsers).
+takedown_usm_users(UsmUsers, EngineID) ->
+ lists:foreach(fun(Id) ->
+ snmpm:unregister_usm_user(EngineID, Id)
+ end, UsmUsers).
%%%---------------------------------------------------------------------------
log(PrivDir, Agent, {_, _, Varbinds}, NewVarsAndVals) ->
@@ -657,7 +735,7 @@ override_contexts(Config, {data_dir_file, File}) ->
override_contexts(Config, ContextInfo);
override_contexts(Config, Contexts) ->
- Dir = ?config(priv_dir, Config),
+ Dir = filename:join(?config(priv_dir, Config),"conf"),
File = filename:join(Dir,"context.conf"),
file:delete(File),
snmp_config:write_agent_context_config(Dir, "", Contexts).
@@ -673,7 +751,7 @@ override_sysinfo(Config, {data_dir_file, File}) ->
override_sysinfo(Config, SysInfo);
override_sysinfo(Config, SysInfo) ->
- Dir = ?config(priv_dir, Config),
+ Dir = filename:join(?config(priv_dir, Config),"conf"),
File = filename:join(Dir,"standard.conf"),
file:delete(File),
snmp_config:write_agent_standard_config(Dir, "", SysInfo).
@@ -688,7 +766,7 @@ override_target_address(Config, {data_dir_file, File}) ->
override_target_address(Config, TargetAddressConf);
override_target_address(Config, TargetAddressConf) ->
- Dir = ?config(priv_dir, Config),
+ Dir = filename:join(?config(priv_dir, Config),"conf"),
File = filename:join(Dir,"target_addr.conf"),
file:delete(File),
snmp_config:write_agent_target_addr_config(Dir, "", TargetAddressConf).
@@ -704,7 +782,7 @@ override_target_params(Config, {data_dir_file, File}) ->
override_target_params(Config, TargetParamsConf);
override_target_params(Config, TargetParamsConf) ->
- Dir = ?config(priv_dir, Config),
+ Dir = filename:join(?config(priv_dir, Config),"conf"),
File = filename:join(Dir,"target_params.conf"),
file:delete(File),
snmp_config:write_agent_target_params_config(Dir, "", TargetParamsConf).
@@ -719,7 +797,7 @@ override_notify(Config, {data_dir_file, File}) ->
override_notify(Config, NotifyConf);
override_notify(Config, NotifyConf) ->
- Dir = ?config(priv_dir, Config),
+ Dir = filename:join(?config(priv_dir, Config),"conf"),
File = filename:join(Dir,"notify.conf"),
file:delete(File),
snmp_config:write_agent_notify_config(Dir, "", NotifyConf).
@@ -734,7 +812,7 @@ override_usm(Config, {data_dir_file, File}) ->
override_usm(Config, UsmConf);
override_usm(Config, UsmConf) ->
- Dir = ?config(priv_dir, Config),
+ Dir = filename:join(?config(priv_dir, Config),"conf"),
File = filename:join(Dir,"usm.conf"),
file:delete(File),
snmp_config:write_agent_usm_config(Dir, "", UsmConf).
@@ -749,7 +827,7 @@ override_community(Config, {data_dir_file, File}) ->
override_community(Config, CommunityConf);
override_community(Config, CommunityConf) ->
- Dir = ?config(priv_dir, Config),
+ Dir = filename:join(?config(priv_dir, Config),"conf"),
File = filename:join(Dir,"community.conf"),
file:delete(File),
snmp_config:write_agent_community_config(Dir, "", CommunityConf).
@@ -765,7 +843,20 @@ override_vacm(Config, {data_dir_file, File}) ->
override_vacm(Config, VacmConf);
override_vacm(Config, VacmConf) ->
- Dir = ?config(priv_dir, Config),
- File = filename:join(Dir,"vacm.conf"),
+ Dir = filename:join(?config(priv_dir, Config),"conf"),
+ File = filename:join(Dir,"vacm.conf"),
file:delete(File),
snmp_config:write_agent_vacm_config(Dir, "", VacmConf).
+
+%%%---------------------------------------------------------------------------
+
+target_name(Agent) ->
+ atom_to_list(Agent).
+
+while_ok(Fun,[H|T]) ->
+ case Fun(H) of
+ ok -> while_ok(Fun,T);
+ Error -> Error
+ end;
+while_ok(_Fun,[]) ->
+ ok.
diff --git a/lib/common_test/src/ct_ssh.erl b/lib/common_test/src/ct_ssh.erl
index aebb28bc42..c6ea27b10e 100644
--- a/lib/common_test/src/ct_ssh.erl
+++ b/lib/common_test/src/ct_ssh.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -133,10 +133,11 @@ connect(KeyOrName, ExtraOpts) when is_list(ExtraOpts) ->
%%% is used to identify the connection, this name may
%%% be used as connection reference for subsequent calls.
%%% It's only possible to have one open connection at a time
-%%% associated with <code>Name</code>. If <code>Key</code> is
+%%% associated with <code>Name</code>. If <code>Key</code> is
%%% used, the returned handle must be used for subsequent calls
%%% (multiple connections may be opened using the config
-%%% data specified by <code>Key</code>).</p>
+%%% data specified by <code>Key</code>). See <c>ct:require/2</c>
+%%% for how to create a new <c>Name</c></p>
%%%
%%% <p><code>ConnType</code> will always override the type
%%% specified in the address tuple in the configuration data (and
@@ -152,6 +153,8 @@ connect(KeyOrName, ExtraOpts) when is_list(ExtraOpts) ->
%%% The extra options will override any existing options with the
%%% same key in the config data. For details on valid SSH
%%% options, see the documentation for the OTP ssh application.</p>
+%%%
+%%% @see ct:require/2
connect(KeyOrName, ConnType, ExtraOpts) ->
case ct:get_config(KeyOrName) of
undefined ->
@@ -182,19 +185,22 @@ connect(KeyOrName, ConnType, ExtraOpts) ->
undefined ->
{ssh,undefined,AllOpts};
SFTPAddr ->
- log(heading(connect,KeyOrName),
- "Note: Opening ssh connection to sftp host.\n",
+ try_log(heading(connect,KeyOrName),
+ "Note: Opening ssh connection "
+ "to sftp host.\n",
[]),
{ssh,SFTPAddr,
- [{ssh,SFTPAddr}|proplists:delete(sftp, AllOpts)]}
+ [{ssh,SFTPAddr} |
+ proplists:delete(sftp, AllOpts)]}
end;
undefined when ConnType == sftp ->
case proplists:get_value(ssh, AllOpts) of
undefined ->
{sftp,undefined,AllOpts};
SSHAddr ->
- log(heading(connect,KeyOrName),
- "Note: Opening sftp connection to ssh host.\n",
+ try_log(heading(connect,KeyOrName),
+ "Note: Opening sftp connection "
+ "to ssh host.\n",
[]),
{sftp,SSHAddr,
[{sftp,SSHAddr}|proplists:delete(ssh, AllOpts)]}
@@ -209,15 +215,15 @@ connect(KeyOrName, ConnType, ExtraOpts) ->
[{not_available,{KeyOrName,ConnType1}}]),
{error,{not_available,{KeyOrName,ConnType1}}};
{_,undefined} ->
- log(heading(connect,KeyOrName),
- "Opening ~w connection to ~p:22\n",
- [ConnType1,Addr]),
+ try_log(heading(connect,KeyOrName),
+ "Opening ~w connection to ~p:22\n",
+ [ConnType1,Addr]),
ct_gen_conn:start(KeyOrName, {ConnType1,Addr,22},
AllOpts1, ?MODULE);
{_,Port} ->
- log(heading(connect,KeyOrName),
- "Opening ~w connection to ~p:~w\n",
- [ConnType1,Addr,Port]),
+ try_log(heading(connect,KeyOrName),
+ "Opening ~w connection to ~p:~w\n",
+ [ConnType1,Addr,Port]),
ct_gen_conn:start(KeyOrName, {ConnType1,Addr,Port},
AllOpts1, ?MODULE)
end
@@ -232,7 +238,7 @@ connect(KeyOrName, ConnType, ExtraOpts) ->
disconnect(SSH) ->
case get_handle(SSH) of
{ok,Pid} ->
- log(heading(disconnect,SSH), "Handle: ~p", [Pid]),
+ try_log(heading(disconnect,SSH), "Handle: ~p", [Pid], 5000),
case ct_gen_conn:stop(Pid) of
{error,{process_down,Pid,noproc}} ->
{error,already_closed};
@@ -968,8 +974,9 @@ init(KeyOrName, {ConnType,Addr,Port}, AllOpts) ->
Error;
Ok ->
SSHRef = element(2, Ok),
- log(heading(init,KeyOrName),
- "Opened ~w connection:\nHost: ~p (~p)\nUser: ~p\nPassword: ~p\n",
+ try_log(heading(init,KeyOrName),
+ "Opened ~w connection:\n"
+ "Host: ~p (~p)\nUser: ~p\nPassword: ~p\n",
[ConnType,Addr,Port,User,lists:duplicate(length(Password),$*)]),
{ok,SSHRef,#state{ssh_ref=SSHRef, conn_type=ConnType,
target=KeyOrName}}
@@ -978,25 +985,26 @@ init(KeyOrName, {ConnType,Addr,Port}, AllOpts) ->
%% @hidden
handle_msg(sftp_connect, State) ->
#state{ssh_ref=SSHRef, target=Target} = State,
- log(heading(sftp_connect,Target), "SSH Ref: ~p", [SSHRef]),
+ try_log(heading(sftp_connect,Target), "SSH Ref: ~p", [SSHRef]),
{ssh_sftp:start_channel(SSHRef),State};
handle_msg({session_open,TO}, State) ->
#state{ssh_ref=SSHRef, target=Target} = State,
- log(heading(session_open,Target), "SSH Ref: ~p, Timeout: ~p", [SSHRef,TO]),
+ try_log(heading(session_open,Target), "SSH Ref: ~p, Timeout: ~p",
+ [SSHRef,TO]),
{ssh_connection:session_channel(SSHRef, TO),State};
handle_msg({session_close,Chn}, State) ->
#state{ssh_ref=SSHRef, target=Target} = State,
- log(heading(session_close,Target), "SSH Ref: ~p, Chn: ~p", [SSHRef,Chn]),
+ try_log(heading(session_close,Target), "SSH Ref: ~p, Chn: ~p", [SSHRef,Chn]),
{ssh_connection:close(SSHRef, Chn),State};
handle_msg({exec,Chn,Command,TO}, State) ->
#state{ssh_ref=SSHRef, target=Target} = State,
Chn1 =
if Chn == undefined ->
- log(heading(exec,Target),
- "Opening channel for exec, SSH Ref: ~p", [SSHRef]),
+ try_log(heading(exec,Target),
+ "Opening channel for exec, SSH Ref: ~p", [SSHRef]),
case ssh_connection:session_channel(SSHRef, TO) of
{ok,C} -> C;
CErr -> CErr
@@ -1009,9 +1017,9 @@ handle_msg({exec,Chn,Command,TO}, State) ->
log(heading(exec,Target), "Opening channel failed: ~p", [ChnError]),
{ChnError,State};
_ ->
- log(heading(exec,Target),
- "SSH Ref: ~p, Chn: ~p, Command: ~p, Timeout: ~p",
- [SSHRef,Chn1,Command,TO]),
+ try_log(heading(exec,Target),
+ "SSH Ref: ~p, Chn: ~p, Command: ~p, Timeout: ~p",
+ [SSHRef,Chn1,Command,TO]),
case ssh_connection:exec(SSHRef, Chn1, Command, TO) of
success ->
Result = do_recv_response(SSHRef, Chn1, [], close, TO),
@@ -1024,24 +1032,24 @@ handle_msg({exec,Chn,Command,TO}, State) ->
handle_msg({receive_response,Chn,End,TO}, State) ->
#state{ssh_ref=SSHRef, target=Target} = State,
- log(heading(receive_response,Target),
- "SSH Ref: ~p, Chn: ~p, Timeout: ~p", [SSHRef,Chn,TO]),
+ try_log(heading(receive_response,Target),
+ "SSH Ref: ~p, Chn: ~p, Timeout: ~p", [SSHRef,Chn,TO]),
Result = do_recv_response(SSHRef, Chn, [], End, TO),
{Result,State};
handle_msg({send,Chn,Type,Data,TO}, State) ->
#state{ssh_ref=SSHRef, target=Target} = State,
- log(heading(send,Target),
- "SSH Ref: ~p, Chn: ~p, Type: ~p, Timeout: ~p~n"
- "Data: ~p", [SSHRef,Chn,Type,TO,Data]),
+ try_log(heading(send,Target),
+ "SSH Ref: ~p, Chn: ~p, Type: ~p, Timeout: ~p~n"
+ "Data: ~p", [SSHRef,Chn,Type,TO,Data]),
Result = ssh_connection:send(SSHRef, Chn, Type, Data, TO),
{Result,State};
handle_msg({send_and_receive,Chn,Type,Data,End,TO}, State) ->
#state{ssh_ref=SSHRef, target=Target} = State,
- log(heading(send_and_receive,Target),
- "SSH Ref: ~p, Chn: ~p, Type: ~p, Timeout: ~p~n"
- "Data: ~p", [SSHRef,Chn,Type,TO,Data]),
+ try_log(heading(send_and_receive,Target),
+ "SSH Ref: ~p, Chn: ~p, Type: ~p, Timeout: ~p~n"
+ "Data: ~p", [SSHRef,Chn,Type,TO,Data]),
case ssh_connection:send(SSHRef, Chn, Type, Data, TO) of
ok ->
Result = do_recv_response(SSHRef, Chn, [], End, TO),
@@ -1052,137 +1060,162 @@ handle_msg({send_and_receive,Chn,Type,Data,End,TO}, State) ->
handle_msg({subsystem,Chn,Subsystem,TO}, State) ->
#state{ssh_ref=SSHRef, target=Target} = State,
- log(heading(subsystem,Target),
- "SSH Ref: ~p, Chn: ~p, Subsys: ~p, Timeout: ~p",
- [SSHRef,Chn,Subsystem,TO]),
+ try_log(heading(subsystem,Target),
+ "SSH Ref: ~p, Chn: ~p, Subsys: ~p, Timeout: ~p",
+ [SSHRef,Chn,Subsystem,TO]),
Result = ssh_connection:subsystem(SSHRef, Chn, Subsystem, TO),
{Result,State};
%% --- SFTP Commands ---
handle_msg({read_file,Srv,File}=Cmd, S=#state{ssh_ref=SSHRef}) ->
- log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
+ try_log(heading(sftp,S#state.target),
+ "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:read_file(ref(Srv,SSHRef), File),S};
handle_msg({write_file,Srv,File,Iolist}=Cmd, S=#state{ssh_ref=SSHRef}) ->
- log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
+ try_log(heading(sftp,S#state.target),
+ "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:write_file(ref(Srv,SSHRef), File, Iolist),S};
handle_msg({list_dir,Srv,Path}=Cmd, S=#state{ssh_ref=SSHRef}) ->
- log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
+ try_log(heading(sftp,S#state.target),
+ "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:list_dir(ref(Srv,SSHRef), Path),S};
handle_msg({open,Srv,File,Mode}=Cmd, S=#state{ssh_ref=SSHRef}) ->
- log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
+ try_log(heading(sftp,S#state.target),
+ "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:open(ref(Srv,SSHRef), File, Mode),S};
handle_msg({opendir,Srv,Path}=Cmd, S=#state{ssh_ref=SSHRef}) ->
- log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
+ try_log(heading(sftp,S#state.target),
+ "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:opendir(ref(Srv,SSHRef), Path),S};
handle_msg({close,Srv,Handle}=Cmd, S=#state{ssh_ref=SSHRef}) ->
- log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
+ try_log(heading(sftp,S#state.target),
+ "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:close(ref(Srv,SSHRef), Handle),S};
handle_msg({read,Srv,Handle,Len}=Cmd, S=#state{ssh_ref=SSHRef}) ->
- log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
+ try_log(heading(sftp,S#state.target),
+ "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:read(ref(Srv,SSHRef), Handle, Len),S};
handle_msg({pread,Srv,Handle,Position,Length}=Cmd, S=#state{ssh_ref=SSHRef}) ->
- log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
+ try_log(heading(sftp,S#state.target),
+ "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:pread(ref(Srv,SSHRef),Handle,Position,Length),S};
handle_msg({aread,Srv,Handle,Len}=Cmd, S=#state{ssh_ref=SSHRef}) ->
- log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
+ try_log(heading(sftp,S#state.target),
+ "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:aread(ref(Srv,SSHRef), Handle, Len),S};
handle_msg({apread,Srv,Handle,Position,Length}=Cmd, S=#state{ssh_ref=SSHRef}) ->
- log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
+ try_log(heading(sftp,S#state.target),
+ "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:apread(ref(Srv,SSHRef), Handle, Position, Length),S};
handle_msg({write,Srv,Handle,Data}=Cmd, S=#state{ssh_ref=SSHRef}) ->
- log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
+ try_log(heading(sftp,S#state.target),
+ "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:write(ref(Srv,SSHRef), Handle, Data),S};
handle_msg({pwrite,Srv,Handle,Position,Data}=Cmd, S=#state{ssh_ref=SSHRef}) ->
- log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
+ try_log(heading(sftp,S#state.target),
+ "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:pwrite(ref(Srv,SSHRef), Handle, Position, Data),S};
handle_msg({awrite,Srv,Handle,Data}=Cmd, S=#state{ssh_ref=SSHRef}) ->
- log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
+ try_log(heading(sftp,S#state.target),
+ "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:awrite(ref(Srv,SSHRef), Handle, Data),S};
handle_msg({apwrite,Srv,Handle,Position,Data}=Cmd, S=#state{ssh_ref=SSHRef}) ->
- log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
+ try_log(heading(sftp,S#state.target),
+ "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:apwrite(ref(Srv,SSHRef), Handle, Position, Data),S};
handle_msg({position,Srv,Handle,Location}=Cmd, S=#state{ssh_ref=SSHRef}) ->
- log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
+ try_log(heading(sftp,S#state.target),
+ "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:position(ref(Srv,SSHRef), Handle, Location),S};
handle_msg({read_file_info,Srv,Name}=Cmd, S=#state{ssh_ref=SSHRef}) ->
- log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
+ try_log(heading(sftp,S#state.target),
+ "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:read_file_info(ref(Srv,SSHRef), Name),S};
handle_msg({get_file_info,Srv,Handle}=Cmd, S=#state{ssh_ref=SSHRef}) ->
- log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
+ try_log(heading(sftp,S#state.target),
+ "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:get_file_info(ref(Srv,SSHRef), Handle),S};
handle_msg({read_link_info,Srv,Name}=Cmd, S=#state{ssh_ref=SSHRef}) ->
- log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
+ try_log(heading(sftp,S#state.target),
+ "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:read_link_info(ref(Srv,SSHRef), Name),S};
handle_msg({write_file_info,Srv,Name,Info}=Cmd, S=#state{ssh_ref=SSHRef}) ->
- log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
+ try_log(heading(sftp,S#state.target),
+ "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:write_file_info(ref(Srv,SSHRef), Name, Info),S};
handle_msg({read_link,Srv,Name}=Cmd, S=#state{ssh_ref=SSHRef}) ->
- log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
+ try_log(heading(sftp,S#state.target),
+ "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:read_link(ref(Srv,SSHRef), Name),S};
handle_msg({make_symlink,Srv,Name,Target}=Cmd, S=#state{ssh_ref=SSHRef}) ->
- log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
+ try_log(heading(sftp,S#state.target),
+ "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:make_symlink(ref(Srv,SSHRef), Name, Target),S};
handle_msg({rename,Srv,OldName,NewName}=Cmd, S=#state{ssh_ref=SSHRef}) ->
- log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
+ try_log(heading(sftp,S#state.target),
+ "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:rename(ref(Srv,SSHRef), OldName, NewName),S};
handle_msg({delete,Srv,Name}=Cmd, S=#state{ssh_ref=SSHRef}) ->
- log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
+ try_log(heading(sftp,S#state.target),
+ "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:delete(ref(Srv,SSHRef), Name),S};
handle_msg({make_dir,Srv,Name}=Cmd, S=#state{ssh_ref=SSHRef}) ->
- log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
+ try_log(heading(sftp,S#state.target),
+ "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:make_dir(ref(Srv,SSHRef), Name),S};
handle_msg({del_dir,Srv,Name}=Cmd, S=#state{ssh_ref=SSHRef}) ->
- log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
+ try_log(heading(sftp,S#state.target),
+ "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:del_dir(ref(Srv,SSHRef), Name),S}.
%% @hidden
@@ -1197,12 +1230,12 @@ close(SSHRef) ->
terminate(SSHRef, State) ->
case State#state.conn_type of
ssh ->
- log(heading(disconnect_ssh,State#state.target),
- "SSH Ref: ~p",[SSHRef]),
+ try_log(heading(disconnect_ssh,State#state.target),
+ "SSH Ref: ~p",[SSHRef], 5000),
ssh:close(SSHRef);
sftp ->
- log(heading(disconnect_sftp,State#state.target),
- "SFTP Ref: ~p",[SSHRef]),
+ try_log(heading(disconnect_sftp,State#state.target),
+ "SFTP Ref: ~p",[SSHRef], 5000),
ssh_sftp:stop_channel(SSHRef)
end.
@@ -1217,7 +1250,7 @@ do_recv_response(SSH, Chn, Data, End, Timeout) ->
{ssh_cm, SSH, {open,Chn,RemoteChn,{session}}} ->
debug("RECVD open"),
{ok,{open,Chn,RemoteChn,{session}}};
-
+
{ssh_cm, SSH, {closed,Chn}} ->
ssh_connection:close(SSH, Chn),
debug("CLSD~n~p ~p", [SSH,Chn]),
@@ -1245,38 +1278,38 @@ do_recv_response(SSH, Chn, Data, End, Timeout) ->
{ssh_cm, SSH, {exit_signal,Chn,Signal,Err,_Lang}} ->
debug("RECVD exit_signal~n~p ~p~n~p ~p", [SSH,Chn,Signal,Err]),
do_recv_response(SSH, Chn, Data, End, Timeout);
-%% {ok,{exit_signal,Chn,Signal,Err,_Lang}};
+ %% {ok,{exit_signal,Chn,Signal,Err,_Lang}};
{ssh_cm, SSH, {exit_status,Chn,Status}} ->
debug("RECVD exit_status~n~p ~p~n~p", [SSH,Chn,Status]),
do_recv_response(SSH, Chn, Data, End, Timeout);
-%% {ok,{exit_status,Chn,_Status}};
+ %% {ok,{exit_status,Chn,_Status}};
-%% --- INTERACTIVE MESSAGES - NOT HANDLED ---
-%%
-%% {ssh_cm, SSH, {subsystem,Chn,WantReply,Name}} ->
-%% debug("RECVD SUBS WNTRPLY~n~p ~p~n~p~n~p",
-%% [SSH,Chn,WantReply]),
-%% ssh_connection:reply_request(SSH, WantReply, success, Chn),
-%% do_recv_response(SSH, Chn, Data, End, Timeout);
-
-%% {ssh_cm, SSH, {shell,WantReply}} ->
-%% debug("RECVD SHELL WNTRPLY~n~p ~p~n~p~n~p",
-%% [SSH,Chn,WantReply]),
-%% ssh_connection:reply_request(SSH, WantReply, success, Chn),
-%% do_recv_response(SSH,Chn,Data,End,Timeout);
-
-%% {ssh_cm, SSH, {pty,Chn,WantReply,Pty}} ->
-%% debug("RECVD PTY WNTRPLY~n~p ~p~n~p~n~p",
-%% [SSH,Chn,WantReply,Pty]),
-%% ssh_connection:reply_request(SSH, WantReply, success, Chn),
-%% do_recv_response(SSH, Chn, Data, End, Timeout);
-
-%% {ssh_cm, SSH, WCh={window_change,_Chn,_Width,_Height,_PixWidth,_PixHeight}} ->
-%% debug("RECVD WINCH"),
-%% {ok,WCh};
-
+ %% --- INTERACTIVE MESSAGES - NOT HANDLED ---
+ %%
+ %% {ssh_cm, SSH, {subsystem,Chn,WantReply,Name}} ->
+ %% debug("RECVD SUBS WNTRPLY~n~p ~p~n~p~n~p",
+ %% [SSH,Chn,WantReply]),
+ %% ssh_connection:reply_request(SSH, WantReply, success, Chn),
+ %% do_recv_response(SSH, Chn, Data, End, Timeout);
+
+ %% {ssh_cm, SSH, {shell,WantReply}} ->
+ %% debug("RECVD SHELL WNTRPLY~n~p ~p~n~p~n~p",
+ %% [SSH,Chn,WantReply]),
+ %% ssh_connection:reply_request(SSH, WantReply, success, Chn),
+ %% do_recv_response(SSH,Chn,Data,End,Timeout);
+
+ %% {ssh_cm, SSH, {pty,Chn,WantReply,Pty}} ->
+ %% debug("RECVD PTY WNTRPLY~n~p ~p~n~p~n~p",
+ %% [SSH,Chn,WantReply,Pty]),
+ %% ssh_connection:reply_request(SSH, WantReply, success, Chn),
+ %% do_recv_response(SSH, Chn, Data, End, Timeout);
+
+ %% {ssh_cm, SSH, WCh={window_change,_Chn,_Width,_Height,_PixWidth,_PixHeight}} ->
+ %% debug("RECVD WINCH"),
+ %% {ok,WCh};
+
Other ->
debug("UNEXPECTED MESSAGE~n~p ~p~n~p", [SSH,Chn,Other]),
do_recv_response(SSH, Chn, Data, End, Timeout)
@@ -1307,9 +1340,12 @@ get_handle(SSH) ->
%%%-----------------------------------------------------------------
%%%
call(SSH, Msg) ->
+ call(SSH, Msg, infinity).
+
+call(SSH, Msg, Timeout) ->
case get_handle(SSH) of
{ok,Pid} ->
- ct_gen_conn:call(Pid, Msg);
+ ct_gen_conn:call(Pid, Msg, Timeout);
Error ->
Error
end.
@@ -1318,13 +1354,13 @@ call(SSH, Msg) ->
%%%
ref(sftp, SSHRef) -> SSHRef;
ref(Server, _) -> Server.
-
+
%%%-----------------------------------------------------------------
%%%
mod(Cmd) ->
[Op,_Server|Args] = tuple_to_list(Cmd),
list_to_tuple([Op|Args]).
-
+
%%%-----------------------------------------------------------------
%%%
heading(Function, Ref) ->
@@ -1335,6 +1371,20 @@ heading(Function, Ref) ->
log(Heading, Str, Args) ->
ct_gen_conn:log(Heading, Str, Args).
+%%%-----------------------------------------------------------------
+%%%
+try_log(Heading, Str, Args) ->
+ try_log(Heading, Str, Args, infinity).
+
+try_log(Heading, Str, Args, Timeout) ->
+ case ct_util:is_silenced(ssh, Timeout) of
+ true ->
+ ok;
+ false ->
+ ct_gen_conn:log(Heading, Str, Args);
+ _Error ->
+ ok
+ end.
%%%-----------------------------------------------------------------
%%%
@@ -1342,5 +1392,5 @@ debug(Str) ->
debug(Str, []).
debug(_Str, _Args) ->
-%% io:format("~n--- ct_ssh debug ---~n" ++ _Str ++ "~n", _Args),
+ %% io:format("~n--- ct_ssh debug ---~n" ++ _Str ++ "~n", _Args),
ok.
diff --git a/lib/common_test/src/ct_telnet.erl b/lib/common_test/src/ct_telnet.erl
index f4a551e3ff..b13c050e32 100644
--- a/lib/common_test/src/ct_telnet.erl
+++ b/lib/common_test/src/ct_telnet.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -155,6 +155,8 @@ open(KeyOrName,ConnType,TargetMod) ->
%%% <p><code>TargetMod</code> is a module which exports the functions
%%% <code>connect(Ip,Port,KeepAlive,Extra)</code> and <code>get_prompt_regexp()</code>
%%% for the given <code>TargetType</code> (e.g. <code>unix_telnet</code>).</p>
+%%%
+%%% @see ct:require/2
open(KeyOrName,ConnType,TargetMod,Extra) ->
case ct:get_config({KeyOrName,ConnType}) of
undefined ->
diff --git a/lib/common_test/src/ct_testspec.erl b/lib/common_test/src/ct_testspec.erl
index 4c05f57520..202d8f9373 100644
--- a/lib/common_test/src/ct_testspec.erl
+++ b/lib/common_test/src/ct_testspec.erl
@@ -29,6 +29,8 @@
-include("ct_util.hrl").
+-define(testspec_fields, record_info(fields, testspec)).
+
%%%------------------------------------------------------------------
%%% NOTE:
%%% Multiple testspecs may be used as input with the result that
@@ -46,7 +48,8 @@
%%% Version 1 - extract and return all tests and skips for Node
%%% (incl all_nodes)
%%%-------------------------------------------------------------------
-prepare_tests(TestSpec,Node) when is_record(TestSpec,testspec), is_atom(Node) ->
+prepare_tests(TestSpec,Node) when is_record(TestSpec,testspec),
+ is_atom(Node) ->
case lists:keysearch(Node,1,prepare_tests(TestSpec)) of
{value,{Node,Run,Skip}} ->
{Run,Skip};
@@ -249,22 +252,23 @@ collect_tests_from_file1([Spec|Specs],TestSpec,Relaxed) ->
SpecDir = filename:dirname(filename:absname(Spec)),
case file:consult(Spec) of
{ok,Terms} ->
- TestSpec1 = collect_tests(Terms,
- TestSpec#testspec{spec_dir=SpecDir},
- Relaxed),
- collect_tests_from_file1(Specs,TestSpec1,Relaxed);
+ case collect_tests(Terms,
+ TestSpec#testspec{spec_dir=SpecDir},
+ Relaxed) of
+ TS = #testspec{tests=Tests, logdir=LogDirs} when Specs == [] ->
+ LogDirs1 = lists:delete(".",LogDirs) ++ ["."],
+ TS#testspec{tests=lists:flatten(Tests), logdir=LogDirs1};
+ TS = #testspec{alias = As, nodes = Ns} ->
+ TS1 = TS#testspec{alias = lists:reverse(As),
+ nodes = lists:reverse(Ns)},
+ collect_tests_from_file1(Specs,TS1,Relaxed)
+ end;
{error,Reason} ->
ReasonStr =
lists:flatten(io_lib:format("~s",
[file:format_error(Reason)])),
throw({error,{Spec,ReasonStr}})
- end;
-collect_tests_from_file1([],TS=#testspec{config=Cfgs,event_handler=EvHs,
- include=Incl,tests=Tests},_) ->
- TS#testspec{config=lists:reverse(Cfgs),
- event_handler=lists:reverse(EvHs),
- include=lists:reverse(Incl),
- tests=lists:flatten(Tests)}.
+ end.
collect_tests_from_list(Terms,Relaxed) ->
collect_tests_from_list(Terms,[node()],Relaxed).
@@ -278,30 +282,163 @@ collect_tests_from_list(Terms,Nodes,Relaxed) when is_list(Nodes) ->
E = {error,_} ->
E;
TS ->
- #testspec{config=Cfgs,event_handler=EvHs,include=Incl,tests=Tests} = TS,
- TS#testspec{config=lists:reverse(Cfgs),
- event_handler=lists:reverse(EvHs),
- include=lists:reverse(Incl),
- tests=lists:flatten(Tests)}
+ #testspec{tests=Tests, logdir=LogDirs} = TS,
+ LogDirs1 = lists:delete(".",LogDirs) ++ ["."],
+ TS#testspec{tests=lists:flatten(Tests), logdir=LogDirs1}
end.
collect_tests(Terms,TestSpec,Relaxed) ->
put(relaxed,Relaxed),
- TestSpec1 = get_global(Terms,TestSpec),
- TestSpec2 = get_all_nodes(Terms,TestSpec1),
- {Terms2, TestSpec3} = filter_init_terms(Terms, [], TestSpec2),
+ Terms1 = replace_names(Terms),
+ TestSpec1 = get_global(Terms1,TestSpec),
+ TestSpec2 = get_all_nodes(Terms1,TestSpec1),
+ {Terms2, TestSpec3} = filter_init_terms(Terms1, [], TestSpec2),
add_tests(Terms2,TestSpec3).
-get_global([{merge_tests, Bool} | Ts], Spec) ->
- get_global(Ts,Spec#testspec{ merge_tests = Bool });
+%% replace names (atoms) in the testspec matching those in 'define' terms by
+%% searching recursively through tuples and lists
+replace_names(Terms) ->
+ Defs =
+ lists:flatmap(fun(Def={define,Name,_Replacement}) ->
+ %% check that name follows convention
+ if not is_atom(Name) ->
+ throw({illegal_name_in_testspec,Name});
+ true ->
+ [First|_] = atom_to_list(Name),
+ if ((First == $?) or (First == $$)
+ or (First == $_)
+ or ((First >= $A)
+ and (First =< $Z))) ->
+ [Def];
+ true ->
+ throw({illegal_name_in_testspec,
+ Name})
+ end
+ end;
+ (_) -> []
+ end, Terms),
+ DefProps = replace_names_in_defs(Defs,[]),
+ replace_names(Terms,[],DefProps).
+
+replace_names_in_defs([Def|Left],ModDefs) ->
+ [{define,Name,Replacement}] = replace_names([Def],[],ModDefs),
+ replace_names_in_defs(Left,[{Name,Replacement}|ModDefs]);
+replace_names_in_defs([],ModDefs) ->
+ ModDefs.
+
+replace_names([Term|Ts],Modified,Defs) when is_tuple(Term) ->
+ [TypeTag|Data] = tuple_to_list(Term),
+ Term1 = list_to_tuple([TypeTag|replace_names_in_elems(Data,[],Defs)]),
+ replace_names(Ts,[Term1|Modified],Defs);
+replace_names([Term|Ts],Modified,Defs) when is_atom(Term) ->
+ case proplists:get_value(Term,Defs) of
+ undefined ->
+ replace_names(Ts,[Term|Modified],Defs);
+ Replacement ->
+ replace_names(Ts,[Replacement|Modified],Defs)
+ end;
+replace_names([Term=[Ch|_]|Ts],Modified,Defs) when is_integer(Ch) ->
+ %% Term *could* be a string, attempt to search through it
+ Term1 = replace_names_in_string(Term,Defs),
+ replace_names(Ts,[Term1|Modified],Defs);
+replace_names([Term|Ts],Modified,Defs) ->
+ replace_names(Ts,[Term|Modified],Defs);
+replace_names([],Modified,_Defs) ->
+ lists:reverse(Modified).
+
+replace_names_in_elems([Elem|Es],Modified,Defs) when is_tuple(Elem) ->
+ Elem1 = list_to_tuple(replace_names_in_elems(tuple_to_list(Elem),[],Defs)),
+ replace_names_in_elems(Es,[Elem1|Modified],Defs);
+replace_names_in_elems([Elem|Es],Modified,Defs) when is_atom(Elem) ->
+ case proplists:get_value(Elem,Defs) of
+ undefined ->
+ %% if Term is a node name, check it for replacements as well
+ Elem1 = replace_names_in_node(Elem,Defs),
+ replace_names_in_elems(Es,[Elem1|Modified],Defs);
+ Replacement ->
+ replace_names_in_elems(Es,[Replacement|Modified],Defs)
+ end;
+replace_names_in_elems([Elem=[Ch|_]|Es],Modified,Defs) when is_integer(Ch) ->
+ %% Term *could* be a string, attempt to search through it
+ case replace_names_in_string(Elem,Defs) of
+ Elem ->
+ List = replace_names_in_elems(Elem,[],Defs),
+ replace_names_in_elems(Es,[List|Modified],Defs);
+ Elem1 ->
+ replace_names_in_elems(Es,[Elem1|Modified],Defs)
+ end;
+replace_names_in_elems([Elem|Es],Modified,Defs) when is_list(Elem) ->
+ List = replace_names_in_elems(Elem,[],Defs),
+ replace_names_in_elems(Es,[List|Modified],Defs);
+replace_names_in_elems([Elem|Es],Modified,Defs) ->
+ replace_names_in_elems(Es,[Elem|Modified],Defs);
+replace_names_in_elems([],Modified,_Defs) ->
+ lists:reverse(Modified).
+
+replace_names_in_string(Term,Defs=[{Name,Replacement=[Ch|_]}|Ds])
+ when is_integer(Ch) ->
+ try re:replace(Term,[$'|atom_to_list(Name)]++"'",
+ Replacement,[{return,list}]) of
+ Term -> % no match, proceed
+ replace_names_in_string(Term,Ds);
+ Term1 ->
+ replace_names_in_string(Term1,Defs)
+ catch
+ _:_ -> Term % Term is not a string
+ end;
+replace_names_in_string(Term,[_|Ds]) ->
+ replace_names_in_string(Term,Ds);
+replace_names_in_string(Term,[]) ->
+ Term.
+
+replace_names_in_node(Node,Defs) ->
+ String = atom_to_list(Node),
+ case lists:member($@,String) of
+ true ->
+ list_to_atom(replace_names_in_node1(String,Defs));
+ false ->
+ Node
+ end.
+
+replace_names_in_node1(NodeStr,Defs=[{Name,Replacement}|Ds]) ->
+ ReplStr = case Replacement of
+ [Ch|_] when is_integer(Ch) -> Replacement;
+ _ when is_atom(Replacement) -> atom_to_list(Replacement);
+ _ -> false
+ end,
+ if ReplStr == false ->
+ replace_names_in_node1(NodeStr,Ds);
+ true ->
+ case re:replace(NodeStr,atom_to_list(Name),
+ ReplStr,[{return,list}]) of
+ NodeStr -> % no match, proceed
+ replace_names_in_node1(NodeStr,Ds);
+ NodeStr1 ->
+ replace_names_in_node1(NodeStr1,Defs)
+ end
+ end;
+replace_names_in_node1(NodeStr,[]) ->
+ NodeStr.
+
+
+%% global terms that will be used for analysing all other terms in the spec
+get_global([{merge_tests,Bool} | Ts], Spec) ->
+ get_global(Ts,Spec#testspec{merge_tests=Bool});
+
+%% the 'define' term replaces the 'alias' and 'node' terms, but we need to keep
+%% the latter two for backwards compatibility...
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}) ->
- get_global(Ts,Spec#testspec{nodes=[{Ref,Node}|lists:keydelete(Node,2,Refs)]});
-get_global([_|Ts],Spec) -> get_global(Ts,Spec);
-get_global([],Spec) -> Spec.
+ get_global(Ts,Spec#testspec{nodes=[{Ref,Node} |
+ lists:keydelete(Node,2,Refs)]});
-get_absfile(Callback, FullName,#testspec{spec_dir=SpecDir}) ->
+get_global([_|Ts],Spec) ->
+ get_global(Ts,Spec);
+get_global([],Spec=#testspec{nodes=Ns, alias=As}) ->
+ Spec#testspec{nodes=lists:reverse(Ns), alias=lists:reverse(As)}.
+
+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(),
@@ -329,29 +466,45 @@ get_absfile(FullName,#testspec{spec_dir=SpecDir}) ->
get_absdir(Dir,#testspec{spec_dir=SpecDir}) ->
get_absname(Dir,SpecDir).
-get_absname(TestDir,SpecDir) ->
- AbsName = filename:absname(TestDir,SpecDir),
- TestDirName = filename:basename(AbsName),
- Path = filename:dirname(AbsName),
- TopDir = filename:basename(Path),
- Path1 =
- case TopDir of
- "." ->
- [_|Rev] = lists:reverse(filename:split(Path)),
- filename:join(lists:reverse(Rev));
- ".." ->
- [_,_|Rev] = lists:reverse(filename:split(Path)),
- filename:join(lists:reverse(Rev));
- _ ->
- Path
- end,
- filename:join(Path1,TestDirName).
+get_absname(Dir,SpecDir) ->
+ AbsName = filename:absname(Dir,SpecDir),
+ shorten_path(AbsName,SpecDir).
+
+shorten_path(Path,SpecDir) ->
+ case shorten_split_path(filename:split(Path),[]) of
+ [] ->
+ [Root|_] = filename:split(SpecDir),
+ Root;
+ Short ->
+ filename:join(Short)
+ end.
+
+shorten_split_path([".."|Path],SoFar) ->
+ shorten_split_path(Path,tl(SoFar));
+shorten_split_path(["."|Path],SoFar) ->
+ shorten_split_path(Path,SoFar);
+shorten_split_path([Dir|Path],SoFar) ->
+ shorten_split_path(Path,[Dir|SoFar]);
+shorten_split_path([],SoFar) ->
+ lists:reverse(SoFar).
%% go through all tests and register all nodes found
get_all_nodes([{suites,Nodes,_,_}|Ts],Spec) when is_list(Nodes) ->
get_all_nodes(Ts,save_nodes(Nodes,Spec));
get_all_nodes([{suites,Node,_,_}|Ts],Spec) ->
get_all_nodes(Ts,save_nodes([Node],Spec));
+get_all_nodes([{groups,[Char|_],_,_,_}|Ts],Spec) when is_integer(Char) ->
+ get_all_nodes(Ts,Spec);
+get_all_nodes([{groups,Nodes,_,_,_}|Ts],Spec) when is_list(Nodes) ->
+ get_all_nodes(Ts,save_nodes(Nodes,Spec));
+get_all_nodes([{groups,Nodes,_,_,_,_}|Ts],Spec) when is_list(Nodes) ->
+ get_all_nodes(Ts,save_nodes(Nodes,Spec));
+get_all_nodes([{groups,_,_,_,{cases,_}}|Ts],Spec) ->
+ get_all_nodes(Ts,Spec);
+get_all_nodes([{groups,Node,_,_,_}|Ts],Spec) ->
+ get_all_nodes(Ts,save_nodes([Node],Spec));
+get_all_nodes([{groups,Node,_,_,_,_}|Ts],Spec) ->
+ get_all_nodes(Ts,save_nodes([Node],Spec));
get_all_nodes([{cases,Nodes,_,_,_}|Ts],Spec) when is_list(Nodes) ->
get_all_nodes(Ts,save_nodes(Nodes,Spec));
get_all_nodes([{cases,Node,_,_,_}|Ts],Spec) ->
@@ -360,74 +513,93 @@ get_all_nodes([{skip_suites,Nodes,_,_,_}|Ts],Spec) when is_list(Nodes) ->
get_all_nodes(Ts,save_nodes(Nodes,Spec));
get_all_nodes([{skip_suites,Node,_,_,_}|Ts],Spec) ->
get_all_nodes(Ts,save_nodes([Node],Spec));
+get_all_nodes([{skip_groups,[Char|_],_,_,_,_}|Ts],Spec) when is_integer(Char) ->
+ get_all_nodes(Ts,Spec);
+get_all_nodes([{skip_groups,Nodes,_,_,_,_}|Ts],Spec) when is_list(Nodes) ->
+ get_all_nodes(Ts,save_nodes(Nodes,Spec));
+get_all_nodes([{skip_groups,Node,_,_,_,_}|Ts],Spec) ->
+ get_all_nodes(Ts,save_nodes([Node],Spec));
+get_all_nodes([{skip_groups,Nodes,_,_,_,_,_}|Ts],Spec) when is_list(Nodes) ->
+ get_all_nodes(Ts,save_nodes(Nodes,Spec));
+get_all_nodes([{skip_groups,Node,_,_,_,_,_}|Ts],Spec) ->
+ get_all_nodes(Ts,save_nodes([Node],Spec));
get_all_nodes([{skip_cases,Nodes,_,_,_,_}|Ts],Spec) when is_list(Nodes) ->
get_all_nodes(Ts,save_nodes(Nodes,Spec));
get_all_nodes([{skip_cases,Node,_,_,_,_}|Ts],Spec) ->
get_all_nodes(Ts,save_nodes([Node],Spec));
-get_all_nodes([_|Ts],Spec) ->
+get_all_nodes([_Other|Ts],Spec) ->
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,
+filter_init_terms([{init,InitOptions}|Ts],NewTerms,Spec) ->
+ filter_init_terms([{init,list_nodes(Spec),InitOptions}|Ts],
+ NewTerms,Spec);
+filter_init_terms([{init,all_nodes,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}->
+ 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]),
+ 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}->
+ {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).
+ 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 =
@@ -446,267 +618,18 @@ save_nodes(Nodes,Spec=#testspec{nodes=NodeRefs}) ->
end
end
end,NodeRefs,Nodes),
- Spec#testspec{nodes=NodeRefs1}.
+ Spec#testspec{nodes=NodeRefs1}.
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"}]
-%% means all nodes should write to /tmp/logdir
-%% except ct1@finwe that should use /tmp/logdir2.
-
-%% --- logdir ---
-add_tests([{logdir,all_nodes,Dir}|Ts],Spec) ->
- Dirs = Spec#testspec.logdir,
- Tests = [{logdir,N,get_absdir(Dir,Spec)} ||
- N <- list_nodes(Spec),
- lists:keymember(ref2node(N,Spec#testspec.nodes),
- 1,Dirs) == false],
- add_tests(Tests++Ts,Spec);
-add_tests([{logdir,Nodes,Dir}|Ts],Spec) when is_list(Nodes) ->
- Ts1 = separate(Nodes,logdir,[Dir],Ts,Spec#testspec.nodes),
- add_tests(Ts1,Spec);
-add_tests([{logdir,Node,Dir}|Ts],Spec) ->
- Dirs = Spec#testspec.logdir,
- Dirs1 = [{ref2node(Node,Spec#testspec.nodes),get_absdir(Dir,Spec)} |
- lists:keydelete(ref2node(Node,Spec#testspec.nodes),1,Dirs)],
- add_tests(Ts,Spec#testspec{logdir=Dirs1});
-add_tests([{logdir,Dir}|Ts],Spec) ->
- add_tests([{logdir,all_nodes,Dir}|Ts],Spec);
-
-%% --- logopts ---
-add_tests([{logopts,all_nodes,Opts}|Ts],Spec) ->
- LogOpts = Spec#testspec.logopts,
- Tests = [{logopts,N,Opts} ||
- N <- list_nodes(Spec),
- lists:keymember(ref2node(N,Spec#testspec.nodes),1,
- LogOpts) == false],
- add_tests(Tests++Ts,Spec);
-add_tests([{logopts,Nodes,Opts}|Ts],Spec) when is_list(Nodes) ->
- Ts1 = separate(Nodes,logopts,[Opts],Ts,Spec#testspec.nodes),
- add_tests(Ts1,Spec);
-add_tests([{logopts,Node,Opts}|Ts],Spec) ->
- LogOpts = Spec#testspec.logopts,
- LogOpts1 = [{ref2node(Node,Spec#testspec.nodes),Opts} |
- lists:keydelete(ref2node(Node,Spec#testspec.nodes),
- 1,LogOpts)],
- add_tests(Ts,Spec#testspec{logopts=LogOpts1});
-add_tests([{logopts,Opts}|Ts],Spec) ->
- add_tests([{logopts,all_nodes,Opts}|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)),
- add_tests(Tests++Ts,Spec);
-add_tests([{cover,Nodes,File}|Ts],Spec) when is_list(Nodes) ->
- Ts1 = separate(Nodes,cover,[File],Ts,Spec#testspec.nodes),
- add_tests(Ts1,Spec);
-add_tests([{cover,Node,File}|Ts],Spec) ->
- CoverFs = Spec#testspec.cover,
- CoverFs1 = [{ref2node(Node,Spec#testspec.nodes),get_absfile(File,Spec)} |
- lists:keydelete(ref2node(Node,Spec#testspec.nodes),1,CoverFs)],
- add_tests(Ts,Spec#testspec{cover=CoverFs1});
-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);
-
-%% --- create_priv_dir ---
-add_tests([{create_priv_dir,all_nodes,PD}|Ts],Spec) ->
- Tests = lists:map(fun(N) -> {create_priv_dir,N,PD} end, list_nodes(Spec)),
- add_tests(Tests++Ts,Spec);
-add_tests([{create_priv_dir,Nodes,PD}|Ts],Spec) when is_list(Nodes) ->
- Ts1 = separate(Nodes,create_priv_dir,[PD],Ts,Spec#testspec.nodes),
- add_tests(Ts1,Spec);
-add_tests([{create_priv_dir,Node,PD}|Ts],Spec) ->
- PDs = Spec#testspec.create_priv_dir,
- PDs1 = [{ref2node(Node,Spec#testspec.nodes),PD} |
- lists:keydelete(ref2node(Node,Spec#testspec.nodes),1,PDs)],
- add_tests(Ts,Spec#testspec{create_priv_dir=PDs1});
-add_tests([{create_priv_dir,PD}|Ts],Spec) ->
- add_tests([{create_priv_dir,all_nodes,PD}|Ts],Spec);
-
-%% --- config ---
-add_tests([{config,all_nodes,Files}|Ts],Spec) ->
- Tests = lists:map(fun(N) -> {config,N,Files} end, list_nodes(Spec)),
- add_tests(Tests++Ts,Spec);
-add_tests([{config,Nodes,Files}|Ts],Spec) when is_list(Nodes) ->
- Ts1 = separate(Nodes,config,[Files],Ts,Spec#testspec.nodes),
- add_tests(Ts1,Spec);
-add_tests([{config,Node,[F|Fs]}|Ts],Spec) when is_list(F) ->
- Cfgs = Spec#testspec.config,
- Node1 = ref2node(Node,Spec#testspec.nodes),
- add_tests([{config,Node,Fs}|Ts],
- Spec#testspec{config=[{Node1,get_absfile(F,Spec)}|Cfgs]});
-add_tests([{config,_Node,[]}|Ts],Spec) ->
- add_tests(Ts,Spec);
-add_tests([{config,Node,F}|Ts],Spec) ->
- 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)),
- add_tests(Tests++Ts,Spec);
-add_tests([{event_handler,all_nodes,Hs,Args}|Ts],Spec) when is_list(Args) ->
- Tests = lists:map(fun(N) -> {event_handler,N,Hs,Args} end, list_nodes(Spec)),
- add_tests(Tests++Ts,Spec);
-add_tests([{event_handler,Hs}|Ts],Spec) ->
- add_tests([{event_handler,all_nodes,Hs,[]}|Ts],Spec);
-add_tests([{event_handler,HsOrNodes,HsOrArgs}|Ts],Spec) ->
- case is_noderef(HsOrNodes,Spec#testspec.nodes) of
- true -> % HsOrNodes == Nodes, HsOrArgs == Hs
- case {HsOrNodes,HsOrArgs} of
- {Nodes,Hs} when is_list(Nodes) ->
- Ts1 = separate(Nodes,event_handler,[Hs,[]],Ts,
- Spec#testspec.nodes),
- add_tests(Ts1,Spec);
- {_Node,[]} ->
- add_tests(Ts,Spec);
- {Node,HOrHs} ->
- EvHs = Spec#testspec.event_handler,
- Node1 = ref2node(Node,Spec#testspec.nodes),
- case HOrHs of
- [H|Hs] when is_atom(H) ->
- add_tests([{event_handler,Node,Hs}|Ts],
- Spec#testspec{event_handler=[{Node1,H,[]}|EvHs]});
- H when is_atom(H) ->
- add_tests(Ts,Spec#testspec{event_handler=[{Node1,H,[]}|EvHs]})
- end
- end;
- false -> % HsOrNodes == Hs, HsOrArgs == Args
- add_tests([{event_handler,all_nodes,HsOrNodes,HsOrArgs}|Ts],Spec)
- end;
-add_tests([{event_handler,Nodes,Hs,Args}|Ts],Spec) when is_list(Nodes) ->
- Ts1 = separate(Nodes,event_handler,[Hs,Args],Ts,Spec#testspec.nodes),
- add_tests(Ts1,Spec);
-add_tests([{event_handler,Node,[H|Hs],Args}|Ts],Spec) when is_atom(H) ->
- EvHs = Spec#testspec.event_handler,
- Node1 = ref2node(Node,Spec#testspec.nodes),
- add_tests([{event_handler,Node,Hs,Args}|Ts],
- Spec#testspec{event_handler=[{Node1,H,Args}|EvHs]});
-add_tests([{event_handler,_Node,[],_Args}|Ts],Spec) ->
- add_tests(Ts,Spec);
-add_tests([{event_handler,Node,H,Args}|Ts],Spec) when is_atom(H) ->
- EvHs = Spec#testspec.event_handler,
- 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);
-
-%% -- enable_builtin_hooks --
-add_tests([{enable_builtin_hooks,Bool}|Ts],Spec) ->
- add_tests(Ts, Spec#testspec{ enable_builtin_hooks = Bool });
-
-%% --- include ---
-add_tests([{include,all_nodes,InclDirs}|Ts],Spec) ->
- Tests = lists:map(fun(N) -> {include,N,InclDirs} end, list_nodes(Spec)),
- add_tests(Tests++Ts,Spec);
-add_tests([{include,Nodes,InclDirs}|Ts],Spec) when is_list(Nodes) ->
- Ts1 = separate(Nodes,include,[InclDirs],Ts,Spec#testspec.nodes),
- add_tests(Ts1,Spec);
-add_tests([{include,Node,[D|Ds]}|Ts],Spec) when is_list(D) ->
- Dirs = Spec#testspec.include,
- Node1 = ref2node(Node,Spec#testspec.nodes),
- add_tests([{include,Node,Ds}|Ts],
- Spec#testspec{include=[{Node1,get_absdir(D,Spec)}|Dirs]});
-add_tests([{include,_Node,[]}|Ts],Spec) ->
- add_tests(Ts,Spec);
-add_tests([{include,Node,D}|Ts],Spec) ->
- add_tests([{include,Node,[D]}|Ts],Spec);
-add_tests([{include,InclDirs}|Ts],Spec) ->
- add_tests([{include,all_nodes,InclDirs}|Ts],Spec);
+%% -----------------------------------------------------
+%% / \
+%% | When adding test/config terms, remember to update |
+%% | valid_terms/0 also! |
+%% \ /
+%% -----------------------------------------------------
%% --- suites ---
add_tests([{suites,all_nodes,Dir,Ss}|Ts],Spec) ->
@@ -719,7 +642,7 @@ add_tests([{suites,Nodes,Dir,Ss}|Ts],Spec) when is_list(Nodes) ->
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),
+ ref2dir(Dir,Spec),
Ss,Tests, Spec#testspec.merge_tests),
add_tests(Ts,Spec#testspec{tests=Tests1});
@@ -739,20 +662,22 @@ add_tests([{groups,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([{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),
+ ref2dir(Dir,Spec),
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),
+ ref2dir(Dir,Spec),
Suite,Gs,TCs,Tests,
Spec#testspec.merge_tests),
add_tests(Ts,Spec#testspec{tests=Tests1});
@@ -768,7 +693,7 @@ add_tests([{cases,Nodes,Dir,Suite,Cs}|Ts],Spec) when is_list(Nodes) ->
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),
+ ref2dir(Dir,Spec),
Suite,Cs,Tests, Spec#testspec.merge_tests),
add_tests(Ts,Spec#testspec{tests=Tests1});
@@ -783,7 +708,7 @@ add_tests([{skip_suites,Nodes,Dir,Ss,Cmt}|Ts],Spec) when is_list(Nodes) ->
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),
+ ref2dir(Dir,Spec),
Ss,Cmt,Tests,
Spec#testspec.merge_tests),
add_tests(Ts,Spec#testspec{tests=Tests1});
@@ -792,7 +717,8 @@ add_tests([{skip_suites,Node,Dir,Ss,Cmt}|Ts],Spec) ->
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,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) ->
@@ -800,20 +726,22 @@ add_tests([{skip_groups,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([{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),
+ ref2dir(Dir,Spec),
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),
+ ref2dir(Dir,Spec),
Suite,Gs,TCs,Cmt,Tests,
Spec#testspec.merge_tests),
add_tests(Ts,Spec#testspec{tests=Tests1});
@@ -829,45 +757,101 @@ add_tests([{skip_cases,Nodes,Dir,Suite,Cs,Cmt}|Ts],Spec) when is_list(Nodes) ->
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),
+ ref2dir(Dir,Spec),
Suite,Cs,Cmt,Tests,Spec#testspec.merge_tests),
add_tests(Ts,Spec#testspec{tests=Tests1});
+%% --- various configuration terms ---
+add_tests([{config,Nodes,CfgDir,Files}|Ts],Spec) when is_list(Nodes);
+ Nodes == all_nodes ->
+ add_tests([{config,Nodes,{CfgDir,Files}}|Ts],Spec);
+add_tests([{config,Node,CfgDir,FileOrFiles}|Ts],Spec) ->
+ add_tests([{config,Node,{CfgDir,FileOrFiles}}|Ts],Spec);
+add_tests([{config,CfgDir=[Ch|_],Files}|Ts],Spec) when is_integer(Ch) ->
+ add_tests([{config,all_nodes,{CfgDir,Files}}|Ts],Spec);
+
+add_tests([{event_handler,Nodes,Hs,Args}|Ts],Spec) when is_list(Nodes);
+ Nodes == all_nodes ->
+ add_tests([{event_handler,Nodes,{Hs,Args}}|Ts],Spec);
+add_tests([{event_handler,Node,HOrHs,Args}|Ts],Spec) ->
+ add_tests([{event_handler,Node,{HOrHs,Args}}|Ts],Spec);
+
+add_tests([{enable_builtin_hooks,Bool}|Ts],Spec) ->
+ add_tests(Ts, Spec#testspec{enable_builtin_hooks = Bool});
+
+add_tests([{release_shell,Bool}|Ts],Spec) ->
+ add_tests(Ts, Spec#testspec{release_shell = Bool});
+
%% --- handled/errors ---
+add_tests([{define,_,_}|Ts],Spec) -> % handled
+ add_tests(Ts,Spec);
+
add_tests([{alias,_,_}|Ts],Spec) -> % handled
add_tests(Ts,Spec);
add_tests([{node,_,_}|Ts],Spec) -> % handled
add_tests(Ts,Spec);
-add_tests([{merge_tests, _} | Ts], Spec) -> % handled
+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.
-add_tests([Other|Ts],Spec) when is_tuple(Other) ->
- [Name|_] = tuple_to_list(Other),
- case lists:keymember(Name,1,valid_terms()) of
- true -> % halt
- throw({error,{bad_term_in_spec,Other}});
- false -> % ignore
- case get(relaxed) of
- true ->
- %% warn if name resembles a CT term
- case resembles_ct_term(Name,size(Other)) of
- true ->
- io:format("~nSuspicious term, please check:~n"
- "~p~n", [Other]);
- false ->
- ok
- end,
- add_tests(Ts,Spec);
- false ->
- throw({error,{undefined_term_in_spec,Other}})
- end
+%% --------------------------------------------------
+%% / \
+%% | General add_tests/2 clauses below will work for |
+%% | most test spec configuration terms |
+%% \ /
+%% --------------------------------------------------
+
+%% create one test entry per known node and reinsert
+add_tests([Term={Tag,all_nodes,Data}|Ts],Spec) ->
+ case check_term(Term) of
+ valid ->
+ Tests = [{Tag,Node,Data} || Node <- list_nodes(Spec),
+ should_be_added(Tag,Node,Data,Spec)],
+ add_tests(Tests++Ts,Spec);
+ invalid -> % ignore term
+ add_tests(Ts,Spec)
end;
-
+%% create one test entry per node in Nodes and reinsert
+add_tests([{Tag,[],Data}|Ts],Spec) ->
+ add_tests([{Tag,all_nodes,Data}|Ts],Spec);
+add_tests([{Tag,String=[Ch|_],Data}|Ts],Spec) when is_integer(Ch) ->
+ add_tests([{Tag,all_nodes,{String,Data}}|Ts],Spec);
+add_tests([{Tag,NodesOrOther,Data}|Ts],Spec) when is_list(NodesOrOther) ->
+ case lists:all(fun(Test) -> is_node(Test,Spec#testspec.nodes)
+ end, NodesOrOther) of
+ true ->
+ Ts1 = separate(NodesOrOther,Tag,[Data],Ts,Spec#testspec.nodes),
+ add_tests(Ts1,Spec);
+ false ->
+ add_tests([{Tag,all_nodes,{NodesOrOther,Data}}|Ts],Spec)
+ end;
+%% update data for testspec term of type Tag
+add_tests([Term={Tag,NodeOrOther,Data}|Ts],Spec) ->
+ case is_node(NodeOrOther,Spec#testspec.nodes) of
+ true ->
+ case check_term(Term) of
+ valid ->
+ Node = ref2node(NodeOrOther,Spec#testspec.nodes),
+ NodeIxData =
+ update_recorded(Tag,Node,Spec) ++
+ handle_data(Tag,Node,Data,Spec),
+ add_tests(Ts,mod_field(Spec,Tag,NodeIxData));
+ invalid -> % ignore term
+ add_tests(Ts,Spec)
+ end;
+ false ->
+ add_tests([{Tag,all_nodes,{NodeOrOther,Data}}|Ts],Spec)
+ end;
+%% this test should be added for all known nodes
+add_tests([Term={Tag,Data}|Ts],Spec) ->
+ case check_term(Term) of
+ valid ->
+ add_tests([{Tag,all_nodes,Data}|Ts],Spec);
+ invalid ->
+ add_tests(Ts,Spec)
+ end;
+%% some other data than a tuple
add_tests([Other|Ts],Spec) ->
case get(relaxed) of
true ->
@@ -879,6 +863,120 @@ add_tests([Other|Ts],Spec) ->
add_tests([],Spec) -> % done
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.
+check_term(Term) ->
+ Size = size(Term),
+ [Name|_] = tuple_to_list(Term),
+ Valid = valid_terms(),
+ case lists:member({Name,Size},Valid) of
+ true ->
+ valid;
+ false ->
+ case lists:keymember(Name,1,Valid) of
+ true -> % halt
+ throw({error,{bad_term_in_spec,Term}});
+ false -> % ignore
+ case get(relaxed) of
+ true ->
+ %% warn if name resembles a CT term
+ case resembles_ct_term(Name,size(Term)) of
+ true ->
+ io:format("~nSuspicious term, "
+ "please check:~n"
+ "~p~n", [Term]),
+ invalid;
+ false ->
+ invalid
+ end;
+ false ->
+ throw({error,{undefined_term_in_spec,Term}})
+ end
+ end
+ end.
+
+%% specific data handling before saving in testspec record, e.g.
+%% converting relative paths to absolute for directories and files
+%% (introduce a clause *only* if the data value needs processing)
+handle_data(logdir,Node,Dir,Spec) ->
+ [{Node,ref2dir(Dir,Spec)}];
+handle_data(cover,Node,File,Spec) ->
+ [{Node,get_absfile(File,Spec)}];
+handle_data(cover_stop,Node,Stop,_Spec) ->
+ [{Node,Stop}];
+handle_data(include,Node,Dirs=[D|_],Spec) when is_list(D) ->
+ [{Node,ref2dir(Dir,Spec)} || Dir <- Dirs];
+handle_data(include,Node,Dir=[Ch|_],Spec) when is_integer(Ch) ->
+ handle_data(include,Node,[Dir],Spec);
+handle_data(config,Node,File=[Ch|_],Spec) when is_integer(Ch) ->
+ handle_data(config,Node,[File],Spec);
+handle_data(config,Node,{CfgDir,File=[Ch|_]},Spec) when is_integer(Ch) ->
+ handle_data(config,Node,{CfgDir,[File]},Spec);
+handle_data(config,Node,Files=[F|_],Spec) when is_list(F) ->
+ [{Node,get_absfile(File,Spec)} || File <- Files];
+handle_data(config,Node,{CfgDir,Files=[F|_]},Spec) when is_list(F) ->
+ [{Node,filename:join(ref2dir(CfgDir,Spec),File)} || File <- Files];
+handle_data(userconfig,Node,CBs,Spec) when is_list(CBs) ->
+ [{Node,{Callback,get_absfile(Callback,Config,Spec)}} ||
+ {Callback,Config} <- CBs];
+handle_data(userconfig,Node,CB,Spec) when is_tuple(CB) ->
+ handle_data(userconfig,Node,[CB],Spec);
+handle_data(event_handler,Node,H,Spec) when is_atom(H) ->
+ handle_data(event_handler,Node,{[H],[]},Spec);
+handle_data(event_handler,Node,{H,Args},Spec) when is_atom(H) ->
+ handle_data(event_handler,Node,{[H],Args},Spec);
+handle_data(event_handler,Node,Hs,_Spec) when is_list(Hs) ->
+ [{Node,EvH,[]} || EvH <- Hs];
+handle_data(event_handler,Node,{Hs,Args},_Spec) when is_list(Hs) ->
+ [{Node,EvH,Args} || EvH <- Hs];
+handle_data(ct_hooks,Node,Hooks,_Spec) when is_list(Hooks) ->
+ [{Node,Hook} || Hook <- Hooks ];
+handle_data(ct_hooks,Node,Hook,_Spec) ->
+ [{Node,Hook}];
+handle_data(stylesheet,Node,CSSFile,Spec) ->
+ [{Node,get_absfile(CSSFile,Spec)}];
+handle_data(verbosity,Node,VLvls,_Spec) when is_integer(VLvls) ->
+ [{Node,[{'$unspecified',VLvls}]}];
+handle_data(verbosity,Node,VLvls,_Spec) when is_list(VLvls) ->
+ VLvls1 = lists:map(fun(VLvl = {_Cat,_Lvl}) -> VLvl;
+ (Lvl) -> {'$unspecified',Lvl} end, VLvls),
+ [{Node,VLvls1}];
+handle_data(silent_connections,Node,all,_Spec) ->
+ [{Node,[all]}];
+handle_data(silent_connections,Node,Conn,_Spec) when is_atom(Conn) ->
+ [{Node,[Conn]}];
+handle_data(silent_connections,Node,Conns,_Spec) ->
+ [{Node,Conns}];
+handle_data(_Tag,Node,Data,_Spec) ->
+ [{Node,Data}].
+
+%% check if duplicates should be saved or not
+should_be_added(Tag,Node,_Data,Spec) ->
+ if
+ %% list terms *without* possible duplicates here
+ Tag == logdir; Tag == logopts;
+ Tag == basic_html; Tag == label;
+ Tag == auto_compile; Tag == stylesheet;
+ Tag == verbosity; Tag == silent_connections ->
+ lists:keymember(ref2node(Node,Spec#testspec.nodes),1,
+ read_field(Spec,Tag)) == false;
+ %% for terms *with* possible duplicates
+ true ->
+ true
+ end.
+
+%% check if previous elements for Node should be deleted
+update_recorded(Tag,Node,Spec) ->
+ if Tag == config; Tag == userconfig; Tag == event_handler;
+ Tag == ct_hooks; Tag == include ->
+ read_field(Spec,Tag);
+ true ->
+ %% delete previous value for Tag
+ lists:keydelete(Node,1,read_field(Spec,Tag))
+ end.
+
+%% create one test term per node
separate(Nodes,Tag,Data,Tests,Refs) ->
Separated = separate(Nodes,Tag,Data,Refs),
Separated ++ Tests.
@@ -886,7 +984,25 @@ separate([N|Ns],Tag,Data,Refs) ->
[list_to_tuple([Tag,ref2node(N,Refs)|Data])|separate(Ns,Tag,Data,Refs)];
separate([],_,_,_) ->
[].
-
+
+%% read the value for FieldName in record Rec#testspec
+read_field(Rec, FieldName) ->
+ catch lists:foldl(fun(F, Pos) when F == FieldName ->
+ throw(element(Pos, Rec));
+ (_,Pos) ->
+ Pos+1
+ end,2,?testspec_fields).
+
+%% modify the value for FieldName in record Rec#testspec
+mod_field(Rec, FieldName, NewVal) ->
+ [_testspec|RecList] = tuple_to_list(Rec),
+ RecList1 =
+ (catch lists:foldl(fun(F, {Prev,[_OldVal|Rest]}) when F == FieldName ->
+ throw(lists:reverse(Prev) ++ [NewVal|Rest]);
+ (_,{Prev,[Field|Rest]}) ->
+ {[Field|Prev],Rest}
+ end,{[],RecList},?testspec_fields)),
+ list_to_tuple([testspec|RecList1]).
%% Representation:
%% {{Node,Dir},[{Suite1,[GrOrCase11,GrOrCase12,...]},
@@ -912,20 +1028,24 @@ insert_groups(Node,Dir,Suite,Group,Cases,Tests,MergeTests)
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],
+ Groups1 = [if is_list(Gr) -> % preserve group path
+ {[Gr],Cases};
+ true ->
+ {Gr,Cases} end || 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) ->
+ Groups1 = [if is_list(Gr) -> % preserve group path
+ {[Gr],Cases};
+ true ->
+ {Gr,Cases} end || Gr <- 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),
+ Suites1 = insert_groups1(Suite,Groups1,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)
@@ -948,13 +1068,13 @@ insert_groups1(Suite,Groups,Suites0) ->
insert_groups2(_Groups,all) ->
all;
-insert_groups2([Group={GrName,Cases}|Groups],GrAndCases) ->
- case lists:keysearch(GrName,1,GrAndCases) of
- {value,{GrName,all}} ->
+insert_groups2([Group={Gr,Cases}|Groups],GrAndCases) ->
+ case lists:keysearch(Gr,1,GrAndCases) of
+ {value,{Gr,all}} ->
GrAndCases;
- {value,{GrName,Cases0}} ->
+ {value,{Gr,Cases0}} ->
Cases1 = insert_in_order(Cases,Cases0),
- insert_groups2(Groups,insert_in_order({GrName,Cases1},GrAndCases));
+ insert_groups2(Groups,insert_in_order({Gr,Cases1},GrAndCases));
false ->
insert_groups2(Groups,insert_in_order(Group,GrAndCases))
end;
@@ -1094,33 +1214,40 @@ ref2node(all_nodes,_Refs) ->
ref2node(master,_Refs) ->
master;
ref2node(RefOrNode,Refs) ->
- case string:chr(atom_to_list(RefOrNode),$@) of
- 0 -> % a ref
+ case lists:member($@,atom_to_list(RefOrNode)) of
+ false -> % a ref
case lists:keysearch(RefOrNode,1,Refs) of
{value,{RefOrNode,Node}} ->
Node;
false ->
throw({error,{noderef_missing,RefOrNode}})
end;
- _ -> % a node
+ true -> % a node
RefOrNode
end.
-ref2dir(Ref,Refs) when is_atom(Ref) ->
+ref2dir(Ref,Spec) ->
+ ref2dir(Ref,Spec#testspec.alias,Spec).
+
+ref2dir(Ref,Refs,Spec) when is_atom(Ref) ->
case lists:keysearch(Ref,1,Refs) of
{value,{Ref,Dir}} ->
- Dir;
+ get_absdir(Dir,Spec);
false ->
throw({error,{alias_missing,Ref}})
end;
-ref2dir(Dir,_) when is_list(Dir) ->
- Dir.
-
-is_noderef(What,Nodes) when is_atom(What) ->
- is_noderef([What],Nodes);
-is_noderef([master|_],_Nodes) ->
+ref2dir(Dir,_,Spec) when is_list(Dir) ->
+ get_absdir(Dir,Spec);
+ref2dir(What,_,_) ->
+ throw({error,{invalid_directory_name,What}}).
+
+is_node(What,Nodes) when is_atom(What) ->
+ is_node([What],Nodes);
+is_node([master|_],_Nodes) ->
true;
-is_noderef([What|_],Nodes) ->
+is_node(What={N,H},Nodes) when is_atom(N), is_atom(H) ->
+ is_node([What],Nodes);
+is_node([What|_],Nodes) ->
case lists:keymember(What,1,Nodes) or
lists:keymember(What,2,Nodes) of
true ->
@@ -1128,24 +1255,34 @@ is_noderef([What|_],Nodes) ->
false ->
false
end;
-is_noderef([],_) ->
+is_node([],_) ->
false.
valid_terms() ->
[
+ {define,3},
{node,3},
{cover,2},
{cover,3},
+ {cover_stop,2},
+ {cover_stop,3},
{config,2},
{config,3},
+ {config,4},
{userconfig,2},
{userconfig,3},
{alias,3},
- {merge_tests,1},
+ {merge_tests,2},
{logdir,2},
{logdir,3},
{logopts,2},
{logopts,3},
+ {basic_html,2},
+ {basic_html,3},
+ {verbosity,2},
+ {verbosity,3},
+ {silent_connections,2},
+ {silent_connections,3},
{label,2},
{label,3},
{event_handler,2},
@@ -1153,13 +1290,18 @@ valid_terms() ->
{event_handler,4},
{ct_hooks,2},
{ct_hooks,3},
- {enable_builtin_hooks,1},
+ {enable_builtin_hooks,2},
+ {release_shell,2},
{multiply_timetraps,2},
{multiply_timetraps,3},
{scale_timetraps,2},
{scale_timetraps,3},
{include,2},
{include,3},
+ {auto_compile,2},
+ {auto_compile,3},
+ {stylesheet,2},
+ {stylesheet,3},
{suites,3},
{suites,4},
{groups,4},
@@ -1174,7 +1316,8 @@ valid_terms() ->
{skip_groups,7},
{skip_cases,5},
{skip_cases,6},
- {create_priv_dir,2}
+ {create_priv_dir,2},
+ {create_priv_dir,3}
].
%% this function "guesses" if the user has misspelled a term name
diff --git a/lib/common_test/src/ct_util.erl b/lib/common_test/src/ct_util.erl
index 9d6ee3c8b9..cf891ed043 100644
--- a/lib/common_test/src/ct_util.erl
+++ b/lib/common_test/src/ct_util.erl
@@ -25,7 +25,8 @@
%%%
-module(ct_util).
--export([start/0,start/1,start/2,stop/1,update_last_run_index/0]).
+-export([start/0,start/1,start/2,start/3,
+ stop/1,update_last_run_index/0]).
-export([register_connection/4,unregister_connection/1,
does_connection_exist/3,get_key_from_name/1]).
@@ -36,14 +37,15 @@
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,
- set_testdata_async/1, update_testdata/2]).
+ delete_testdata/0, delete_testdata/1,
+ set_testdata/1, get_testdata/1, get_testdata/2,
+ set_testdata_async/1, update_testdata/2, update_testdata/3]).
-export([override_silence_all_connections/0, override_silence_connections/1,
get_overridden_silenced_connections/0,
delete_overridden_silenced_connections/0,
- silence_all_connections/0, silence_connections/1, is_silenced/1,
- reset_silent_connections/0]).
+ silence_all_connections/0, silence_connections/1,
+ is_silenced/1, is_silenced/2, reset_silent_connections/0]).
-export([get_mode/0, create_table/3, read_opts/0]).
@@ -64,9 +66,13 @@
-export([get_profile_data/0, get_profile_data/1,
get_profile_data/2, open_url/3]).
+-include("ct.hrl").
-include("ct_event.hrl").
-include("ct_util.hrl").
+-define(default_verbosity, [{default,?MAX_VERBOSITY},
+ {'$unspecified',?MAX_VERBOSITY}]).
+
-record(suite_data, {key,name,value}).
%%%-----------------------------------------------------------------
@@ -85,18 +91,21 @@
%%%
%%% @see ct
start() ->
- start(normal,".").
+ start(normal, ".", ?default_verbosity).
start(LogDir) when is_list(LogDir) ->
- start(normal,LogDir);
+ start(normal, LogDir, ?default_verbosity);
start(Mode) ->
- start(Mode,".").
+ start(Mode, ".", ?default_verbosity).
+
+start(LogDir, Verbosity) when is_list(LogDir) ->
+ start(normal, LogDir, Verbosity).
-start(Mode,LogDir) ->
+start(Mode, LogDir, Verbosity) ->
case whereis(ct_util_server) of
undefined ->
S = self(),
- Pid = spawn_link(fun() -> do_start(S,Mode,LogDir) end),
+ Pid = spawn_link(fun() -> do_start(S, Mode, LogDir, Verbosity) end),
receive
{Pid,started} -> Pid;
{Pid,Error} -> exit(Error);
@@ -113,7 +122,7 @@ start(Mode,LogDir) ->
end
end.
-do_start(Parent,Mode,LogDir) ->
+do_start(Parent, Mode, LogDir, Verbosity) ->
process_flag(trap_exit,true),
register(ct_util_server,self()),
create_table(?conn_table,#conn.handle),
@@ -173,7 +182,7 @@ do_start(Parent,Mode,LogDir) ->
false ->
ok
end,
- {StartTime,TestLogDir} = ct_logs:init(Mode),
+ {StartTime,TestLogDir} = ct_logs:init(Mode, Verbosity),
ct_event:notify(#event{name=test_start,
node=node(),
@@ -193,7 +202,7 @@ do_start(Parent,Mode,LogDir) ->
self() ! {{stop,{self(),{user_error,CTHReason}}},
{Parent,make_ref()}}
end,
- loop(Mode,[],StartDir).
+ loop(Mode, [{{verbosity,Cat},Lvl} || {Cat,Lvl} <- Verbosity], StartDir).
create_table(TableName,KeyPos) ->
create_table(TableName,set,KeyPos).
@@ -243,7 +252,10 @@ delete_testdata(Key) ->
call({delete_testdata, Key}).
update_testdata(Key, Fun) ->
- call({update_testdata, Key, Fun}).
+ update_testdata(Key, Fun, []).
+
+update_testdata(Key, Fun, Opts) ->
+ call({update_testdata, Key, Fun, Opts}).
set_testdata(TestData) ->
call({set_testdata, TestData}).
@@ -254,6 +266,9 @@ set_testdata_async(TestData) ->
get_testdata(Key) ->
call({get_testdata, Key}).
+get_testdata(Key, Timeout) ->
+ call({get_testdata, Key}, Timeout).
+
set_cwd(Dir) ->
call({set_cwd,Dir}).
@@ -321,7 +336,7 @@ loop(Mode,TestData,StartDir) ->
return(From,undefined)
end,
loop(From,TestData,StartDir);
- {{update_testdata,Key,Fun},From} ->
+ {{update_testdata,Key,Fun,Opts},From} ->
TestData1 =
case lists:keysearch(Key,1,TestData) of
{value,{Key,Val}} ->
@@ -329,8 +344,15 @@ loop(Mode,TestData,StartDir) ->
return(From,NewVal),
[{Key,NewVal}|lists:keydelete(Key,1,TestData)];
_ ->
- return(From,undefined),
- TestData
+ case lists:member(create,Opts) of
+ true ->
+ InitVal = Fun(undefined),
+ return(From,InitVal),
+ [{Key,InitVal}|TestData];
+ false ->
+ return(From,undefined),
+ TestData
+ end
end,
loop(From,TestData1,StartDir);
{{set_cwd,Dir},From} ->
@@ -369,14 +391,25 @@ loop(Mode,TestData,StartDir) ->
{'EXIT',_Pid,normal} ->
loop(Mode,TestData,StartDir);
{'EXIT',Pid,Reason} ->
- %% Let process crash in case of error, this shouldn't happen!
- io:format("\n\nct_util_server got EXIT from ~p: ~p\n\n",
- [Pid,Reason]),
- file:set_cwd(StartDir),
- exit(Reason)
+ case ets:lookup(?conn_table,Pid) of
+ [#conn{address=A,callback=CB}] ->
+ %% A connection crashed - remove the connection but don't die
+ ct_logs:tc_log_async(ct_error_notify,
+ "Connection process died: "
+ "Pid: ~p, Address: ~p, Callback: ~p\n"
+ "Reason: ~p\n\n",
+ [Pid,A,CB,Reason]),
+ catch CB:close(Pid),
+ loop(Mode,TestData,StartDir);
+ _ ->
+ %% Let process crash in case of error, this shouldn't happen!
+ io:format("\n\nct_util_server got EXIT from ~p: ~p\n\n",
+ [Pid,Reason]),
+ file:set_cwd(StartDir),
+ exit(Reason)
+ end
end.
-
close_connections([#conn{handle=Handle,callback=CB}|Conns]) ->
CB:close(Handle),
close_connections(Conns);
@@ -508,7 +541,7 @@ close_connections() ->
%%%
%%% @doc
override_silence_all_connections() ->
- Protocols = [telnet,ftp,rpc,snmp],
+ Protocols = [telnet,ftp,rpc,snmp,ssh],
override_silence_connections(Protocols),
Protocols.
@@ -545,7 +578,10 @@ silence_connections(Conns) when is_list(Conns) ->
set_testdata({silent_connections,Conns1}).
is_silenced(Conn) ->
- case get_testdata(silent_connections) of
+ is_silenced(Conn, infinity).
+
+is_silenced(Conn, Timeout) ->
+ case get_testdata(silent_connections, Timeout) of
Conns when is_list(Conns) ->
case lists:keysearch(Conn,1,Conns) of
{value,{Conn,true}} ->
@@ -553,6 +589,8 @@ is_silenced(Conn) ->
_ ->
false
end;
+ Error = {error,_} ->
+ Error;
_ ->
false
end.
@@ -827,19 +865,28 @@ get_profile_data(Profile, Key, StartDir) ->
%%%-----------------------------------------------------------------
%%% Internal functions
call(Msg) ->
- case whereis(ct_util_server) of
- undefined ->
+ call(Msg, infinity).
+
+call(Msg, Timeout) ->
+ case {self(),whereis(ct_util_server)} of
+ {_,undefined} ->
{error,ct_util_server_not_running};
- Pid ->
+ {Pid,Pid} ->
+ %% the caller is ct_util_server, which must
+ %% be a mistake
+ {error,bad_invocation};
+ {Self,Pid} ->
MRef = erlang:monitor(process, Pid),
Ref = make_ref(),
- ct_util_server ! {Msg,{self(),Ref}},
+ ct_util_server ! {Msg,{Self,Ref}},
receive
{Ref, Result} ->
erlang:demonitor(MRef, [flush]),
Result;
{'DOWN',MRef,process,_,Reason} ->
{error,{ct_util_server_down,Reason}}
+ after
+ Timeout -> {error,timeout}
end
end.
diff --git a/lib/common_test/src/ct_util.hrl b/lib/common_test/src/ct_util.hrl
index 6b016e95df..c9c6514fa4 100644
--- a/lib/common_test/src/ct_util.hrl
+++ b/lib/common_test/src/ct_util.hrl
@@ -34,13 +34,20 @@
profile=[],
logdir=["."],
logopts=[],
+ basic_html=[],
+ verbosity=[],
+ silent_connections=[],
cover=[],
+ cover_stop=[],
config=[],
userconfig=[],
event_handler=[],
ct_hooks=[],
enable_builtin_hooks=true,
+ release_shell=false,
include=[],
+ auto_compile=[],
+ stylesheet=[],
multiply_timetraps=[],
scale_timetraps=[],
create_priv_dir=[],
@@ -64,3 +71,11 @@
-define(ct_config_txt, ct_config_plain).
-define(ct_profile_file, ".common_test").
+
+-define(css_default, "ct_default.css").
+-define(sortable_table_name, "SortableTable").
+-define(jquery_script, "jquery-latest.js").
+-define(tablesorter_script, "jquery.tablesorter.min.js").
+
+%% Logging information for error handler
+-record(conn_log, {client, name, address, action, module}).
diff --git a/lib/common_test/src/cth_conn_log.erl b/lib/common_test/src/cth_conn_log.erl
new file mode 100644
index 0000000000..255f3ec78a
--- /dev/null
+++ b/lib/common_test/src/cth_conn_log.erl
@@ -0,0 +1,124 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%----------------------------------------------------------------------
+%% CT hook for logging of connections.
+%%
+%% HookOptions can be hardcoded in the test suite:
+%%
+%% suite() ->
+%% [{ct_hooks, [{cth_conn_log,
+%% [{ct_netconfc:conn_mod(),ct_netconfc:hook_options()}]}]}].
+%%
+%% or specified in a configuration file:
+%%
+%% {ct_conn_log,[{ct_netconfc:conn_mod(),ct_netconfc:hook_options()}]}.
+%%
+%% The conn_mod() is the common test module implementing the protocol,
+%% e.g. ct_netconfc, ct_telnet, etc. This module must log by calling
+%%
+%% error_logger:info_report(ConnLogInfo,Data).
+%% ConnLogInfo = #conn_log{} | {ct_connection,Action,ConnName}
+%% Action = open | close | send | recv | term()
+%% ConnName = atom() - The 'KeyOrName' argument used when opening the connection
+%%
+%% ct_conn_log_h will print to html log or separate file (depending on
+%% log_type() option). conn_mod() must implement and export
+%%
+%% format_data(log_type(), Data).
+%%
+%% If logging to separate file, ct_conn_log_h will also log error
+%% reports which are witten like this:
+%%
+%% error_logger:error_report([{ct_connection,ConnName} | Report]).
+%%
+%%----------------------------------------------------------------------
+-module(cth_conn_log).
+
+-include_lib("common_test/include/ct.hrl").
+
+-export([init/2,
+ pre_init_per_testcase/3,
+ post_end_per_testcase/4]).
+
+-spec init(Id, HookOpts) -> Result when
+ Id :: term(),
+ HookOpts :: ct_netconfc:hook_options(),
+ Result :: {ok,[{ct_netconfc:conn_mod(),
+ {ct_netconfc:log_type(),[ct_netconfc:key_or_name()]}}]}.
+init(_Id, HookOpts) ->
+ ConfOpts = ct:get_config(ct_conn_log,[]),
+ {ok,merge_log_info(ConfOpts,HookOpts)}.
+
+merge_log_info([{Mod,ConfOpts}|ConfList],HookList) ->
+ {Opts,HookList1} =
+ case lists:keytake(Mod,1,HookList) of
+ false ->
+ {ConfOpts,HookList};
+ {value,{_,HookOpts},HL1} ->
+ {ConfOpts ++ HookOpts, HL1} % ConfOpts overwrites HookOpts!
+ end,
+ [{Mod,get_log_opts(Opts)} | merge_log_info(ConfList,HookList1)];
+merge_log_info([],HookList) ->
+ [{Mod,get_log_opts(Opts)} || {Mod,Opts} <- HookList].
+
+get_log_opts(Opts) ->
+ LogType = proplists:get_value(log_type,Opts,html),
+ Hosts = proplists:get_value(hosts,Opts,[]),
+ {LogType,Hosts}.
+
+
+pre_init_per_testcase(TestCase,Config,CthState) ->
+ Logs =
+ lists:map(
+ fun({ConnMod,{LogType,Hosts}}) ->
+ case LogType of
+ LogType when LogType==raw; LogType==pretty ->
+ Dir = ?config(priv_dir,Config),
+ TCStr = atom_to_list(TestCase),
+ ConnModStr = atom_to_list(ConnMod),
+ DefLogName = TCStr ++ "-" ++ ConnModStr ++ ".txt",
+ DefLog = filename:join(Dir,DefLogName),
+ Ls = [{Host,
+ filename:join(Dir,TCStr ++ "-"++
+ atom_to_list(Host) ++ "-" ++
+ ConnModStr ++
+ ".txt")}
+ || Host <- Hosts]
+ ++[{default,DefLog}],
+ Str =
+ "<table borders=1>"
+ "<b>" ++ ConnModStr ++ " logs:</b>\n" ++
+ [io_lib:format(
+ "<tr><td>~p</td><td><a href=~p>~s</a></td></tr>",
+ [S,L,filename:basename(L)])
+ || {S,L} <- Ls] ++
+ "</table>",
+ io:format(Str,[]),
+ {ConnMod,{LogType,Ls}};
+ _ ->
+ {ConnMod,{LogType,[]}}
+ end
+ end,
+ CthState),
+ error_logger:add_report_handler(ct_conn_log_h,{group_leader(),Logs}),
+ {Config,CthState}.
+
+post_end_per_testcase(_TestCase,_Config,Return,CthState) ->
+ error_logger:delete_report_handler(ct_conn_log_h),
+ {Return,CthState}.
diff --git a/lib/common_test/src/cth_log_redirect.erl b/lib/common_test/src/cth_log_redirect.erl
index 77f57c6195..78ae70f37e 100644
--- a/lib/common_test/src/cth_log_redirect.erl
+++ b/lib/common_test/src/cth_log_redirect.erl
@@ -54,7 +54,7 @@ post_init_per_group(_Group, _Config, Result, State) ->
post_end_per_testcase(_TC, _Config, Result, State) ->
%% Make sure that the event queue is flushed
%% before ending this test case.
- gen_event:call(error_logger, ?MODULE, flush),
+ gen_event:call(error_logger, ?MODULE, flush, 300000),
{Result, State}.
pre_end_per_group(Group, Config, {ct_log, Group}) ->
diff --git a/lib/common_test/src/cth_surefire.erl b/lib/common_test/src/cth_surefire.erl
index c42f956b3a..e6eaad8d48 100644
--- a/lib/common_test/src/cth_surefire.erl
+++ b/lib/common_test/src/cth_surefire.erl
@@ -1,3 +1,22 @@
+%%--------------------------------------------------------------------
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, 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 handling test specifications.
%%%
%%% <p>This module creates a junit report of the test run if plugged in
@@ -27,18 +46,28 @@
-export([terminate/1]).
-record(state, { filepath, axis, properties, package, hostname,
- curr_suite, curr_suite_ts, curr_group = [], curr_tc,
- curr_log_dir, timer, tc_log,
+ curr_suite, curr_suite_ts, curr_group = [],
+ curr_log_dir, timer, tc_log, url_base,
test_cases = [],
test_suites = [] }).
--record(testcase, { log, group, classname, name, time, failure, timestamp }).
--record(testsuite, { errors, failures, hostname, name, tests,
+-record(testcase, { log, url, group, classname, name, time, result, timestamp }).
+-record(testsuite, { errors, failures, skipped, hostname, name, tests,
time, timestamp, id, package,
- properties, testcases }).
+ properties, testcases, log, url }).
+
+-define(default_report,"junit_report.xml").
+-define(suite_log,"suite.log.html").
+
+%% Number of dirs from log root to testcase log file.
+%% ct_run.<node>.<timestamp>/<test_name>/run.<timestamp>/<tc_log>.html
+-define(log_depth,3).
id(Opts) ->
- filename:absname(proplists:get_value(path, Opts, "junit_report.xml")).
+ case proplists:get_value(path, Opts) of
+ undefined -> ?default_report;
+ Path -> filename:absname(Path)
+ end.
init(Path, Opts) ->
{ok, Host} = inet:gethostname(),
@@ -47,23 +76,37 @@ init(Path, Opts) ->
package = proplists:get_value(package,Opts),
axis = proplists:get_value(axis,Opts,[]),
properties = proplists:get_value(properties,Opts,[]),
+ url_base = proplists:get_value(url_base,Opts),
timer = now() }.
+pre_init_per_suite(Suite,Config,#state{ test_cases = [] } = State) ->
+ TcLog = proplists:get_value(tc_logfile,Config),
+ CurrLogDir = filename:dirname(TcLog),
+ Path =
+ case State#state.filepath of
+ ?default_report ->
+ RootDir = get_test_root(TcLog),
+ filename:join(RootDir,?default_report);
+ P ->
+ P
+ end,
+ {Config, init_tc(State#state{ filepath = Path,
+ curr_suite = Suite,
+ curr_suite_ts = now(),
+ curr_log_dir = CurrLogDir},
+ Config) };
pre_init_per_suite(Suite,Config,State) ->
- {Config, init_tc(State#state{ curr_suite = Suite, curr_suite_ts = now() },
- Config) }.
+ %% Have to close the previous suite
+ pre_init_per_suite(Suite,Config,close_suite(State)).
post_init_per_suite(_Suite,Config, Result, State) ->
{Result, end_tc(init_per_suite,Config,Result,State)}.
-pre_end_per_suite(_Suite,Config,State) -> {Config, init_tc(State, Config)}.
+pre_end_per_suite(_Suite,Config,State) ->
+ {Config, init_tc(State, Config)}.
post_end_per_suite(_Suite,Config,Result,State) ->
- NewState = end_tc(end_per_suite,Config,Result,State),
- TCs = NewState#state.test_cases,
- Suite = get_suite(NewState, TCs),
- {Result, State#state{ test_cases = [],
- test_suites = [Suite | State#state.test_suites]}}.
+ {Result, end_tc(end_per_suite,Config,Result,State)}.
pre_init_per_group(Group,Config,State) ->
{Config, init_tc(State#state{ curr_group = [Group|State#state.curr_group]},
@@ -72,81 +115,132 @@ pre_init_per_group(Group,Config,State) ->
post_init_per_group(_Group,Config,Result,State) ->
{Result, end_tc(init_per_group,Config,Result,State)}.
-pre_end_per_group(_Group,Config,State) -> {Config, init_tc(State, Config)}.
+pre_end_per_group(_Group,Config,State) ->
+ {Config, init_tc(State, Config)}.
post_end_per_group(_Group,Config,Result,State) ->
NewState = end_tc(end_per_group, Config, Result, State),
{Result, NewState#state{ curr_group = tl(NewState#state.curr_group)}}.
-pre_init_per_testcase(_TC,Config,State) -> {Config, init_tc(State, Config)}.
+pre_init_per_testcase(_TC,Config,State) ->
+ {Config, init_tc(State, Config)}.
post_end_per_testcase(TC,Config,Result,State) ->
{Result, end_tc(TC,Config, Result,State)}.
+on_tc_fail(_TC, _Res, State = #state{test_cases = []}) ->
+ State;
on_tc_fail(_TC, Res, State) ->
TCs = State#state.test_cases,
- TC = hd(State#state.test_cases),
- NewTC = TC#testcase{ failure =
- {fail,lists:flatten(io_lib:format("~p",[Res]))} },
+ TC = hd(TCs),
+ NewTC = TC#testcase{
+ result =
+ {fail,lists:flatten(io_lib:format("~p",[Res]))} },
State#state{ test_cases = [NewTC | tl(TCs)]}.
+on_tc_skip(Tc,{Type,_Reason} = Res, State0) when Type == tc_auto_skip ->
+ TcStr = atom_to_list(Tc),
+ State =
+ case State0#state.test_cases of
+ [#testcase{name=TcStr}|TCs] ->
+ State0#state{test_cases=TCs};
+ _ ->
+ State0
+ end,
+ do_tc_skip(Res, end_tc(Tc,[],Res,init_tc(State,[])));
+on_tc_skip(_Tc, _Res, State = #state{test_cases = []}) ->
+ State;
on_tc_skip(_Tc, Res, State) ->
+ do_tc_skip(Res, State).
+
+do_tc_skip(Res, State) ->
TCs = State#state.test_cases,
- TC = hd(State#state.test_cases),
+ TC = hd(TCs),
NewTC = TC#testcase{
- failure =
+ result =
{skipped,lists:flatten(io_lib:format("~p",[Res]))} },
State#state{ test_cases = [NewTC | tl(TCs)]}.
+init_tc(State, Config) when is_list(Config) == false ->
+ State#state{ timer = now(), tc_log = "" };
init_tc(State, Config) ->
State#state{ timer = now(),
- tc_log = proplists:get_value(tc_logfile, Config)}.
+ tc_log = proplists:get_value(tc_logfile, Config, [])}.
end_tc(Func, Config, Res, State) when is_atom(Func) ->
end_tc(atom_to_list(Func), Config, Res, State);
end_tc(Name, _Config, _Res, State = #state{ curr_suite = Suite,
curr_group = Groups,
- timer = TS, tc_log = Log } ) ->
+ curr_log_dir = CurrLogDir,
+ timer = TS,
+ tc_log = Log0,
+ url_base = UrlBase } ) ->
+ Log =
+ case Log0 of
+ "" ->
+ LowerSuiteName = string:to_lower(atom_to_list(Suite)),
+ filename:join(CurrLogDir,LowerSuiteName++"."++Name++".html");
+ _ ->
+ Log0
+ end,
+ Url = make_url(UrlBase,Log),
ClassName = atom_to_list(Suite),
PGroup = string:join([ atom_to_list(Group)||
Group <- lists:reverse(Groups)],"."),
TimeTakes = io_lib:format("~f",[timer:now_diff(now(),TS) / 1000000]),
State#state{ test_cases = [#testcase{ log = Log,
+ url = Url,
timestamp = now_to_string(TS),
classname = ClassName,
group = PGroup,
name = Name,
time = TimeTakes,
- failure = passed }| State#state.test_cases]}.
-
-get_suite(State, TCs) ->
- Total = length(TCs),
- Succ = length(lists:filter(fun(#testcase{ failure = F }) ->
- F == passed
- end,TCs)),
- Fail = Total - Succ,
+ result = passed }|
+ State#state.test_cases],
+ tc_log = ""}. % so old tc_log is not set if next is on_tc_skip
+close_suite(#state{ test_cases = [] } = State) ->
+ State;
+close_suite(#state{ test_cases = TCs, url_base = UrlBase } = State) ->
+ {Total,Fail,Skip} = count_tcs(TCs,0,0,0),
TimeTaken = timer:now_diff(now(),State#state.curr_suite_ts) / 1000000,
- #testsuite{ name = atom_to_list(State#state.curr_suite),
- package = State#state.package,
- time = io_lib:format("~f",[TimeTaken]),
- timestamp = now_to_string(State#state.curr_suite_ts),
- errors = Fail, tests = Total, testcases = lists:reverse(TCs) }.
+ SuiteLog = filename:join(State#state.curr_log_dir,?suite_log),
+ SuiteUrl = make_url(UrlBase,SuiteLog),
+ Suite = #testsuite{ name = atom_to_list(State#state.curr_suite),
+ package = State#state.package,
+ hostname = State#state.hostname,
+ time = io_lib:format("~f",[TimeTaken]),
+ timestamp = now_to_string(State#state.curr_suite_ts),
+ errors = 0,
+ failures = Fail,
+ skipped = Skip,
+ tests = Total,
+ testcases = lists:reverse(TCs),
+ log = SuiteLog,
+ url = SuiteUrl},
+ State#state{ test_cases = [],
+ test_suites = [Suite | State#state.test_suites]}.
-terminate(State) ->
- {ok,D} = file:open(State#state.filepath,[write]),
+terminate(State = #state{ test_cases = [] }) ->
+ {ok,D} = file:open(State#state.filepath,[write,{encoding,utf8}]),
io:format(D, "<?xml version=\"1.0\" encoding= \"UTF-8\" ?>", []),
io:format(D, to_xml(State), []),
catch file:sync(D),
- catch file:close(D).
+ catch file:close(D);
+terminate(State) ->
+ %% Have to close the last suite
+ terminate(close_suite(State)).
+
-to_xml(#testcase{ group = Group, classname = CL, log = L, name = N, time = T, timestamp = TS, failure = F}) ->
+
+to_xml(#testcase{ group = Group, classname = CL, log = L, url = U, name = N, time = T, timestamp = TS, result = R}) ->
["<testcase ",
- [["group=\"",Group,"\""]||Group /= ""]," "
+ [["group=\"",Group,"\" "]||Group /= ""],
"name=\"",N,"\" "
"time=\"",T,"\" "
- "timestamp=\"",TS,"\" "
+ "timestamp=\"",TS,"\" ",
+ [["url=\"",U,"\" "]||U /= undefined],
"log=\"",L,"\">",
- case F of
+ case R of
passed ->
[];
{skipped,Reason} ->
@@ -156,22 +250,29 @@ to_xml(#testcase{ group = Group, classname = CL, log = L, name = N, time = T, ti
["<failure message=\"Test ",N," in ",CL," failed!\" type=\"crash\">",
sanitize(Reason),"</failure>"]
end,"</testcase>"];
-to_xml(#testsuite{ package = P, hostname = H, errors = E, time = Time,
- timestamp = TS, tests = T, name = N, testcases = Cases }) ->
+to_xml(#testsuite{ package = P, hostname = H, errors = E, failures = F,
+ skipped = S, time = Time, timestamp = TS, tests = T, name = N,
+ testcases = Cases, log = Log, url = Url }) ->
["<testsuite ",
[["package=\"",P,"\" "]||P /= undefined],
- [["hostname=\"",P,"\" "]||H /= undefined],
- [["name=\"",N,"\" "]||N /= undefined],
- [["time=\"",Time,"\" "]||Time /= undefined],
- [["timestamp=\"",TS,"\" "]||TS /= undefined],
+ "hostname=\"",H,"\" "
+ "name=\"",N,"\" "
+ "time=\"",Time,"\" "
+ "timestamp=\"",TS,"\" "
"errors=\"",integer_to_list(E),"\" "
- "tests=\"",integer_to_list(T),"\">",
+ "failures=\"",integer_to_list(F),"\" "
+ "skipped=\"",integer_to_list(S),"\" "
+ "tests=\"",integer_to_list(T),"\" ",
+ [["url=\"",Url,"\" "]||Url /= undefined],
+ "log=\"",Log,"\">",
[to_xml(Case) || Case <- Cases],
"</testsuite>"];
to_xml(#state{ test_suites = TestSuites, axis = Axis, properties = Props }) ->
["<testsuites>",properties_to_xml(Axis,Props),
[to_xml(TestSuite) || TestSuite <- TestSuites],"</testsuites>"].
+properties_to_xml([],[]) ->
+ [];
properties_to_xml(Axis,Props) ->
["<properties>",
[["<property name=\"",Name,"\" axis=\"yes\" value=\"",Value,"\" />"] || {Name,Value} <- Axis],
@@ -197,3 +298,37 @@ sanitize([]) ->
now_to_string(Now) ->
{{YY,MM,DD},{HH,Mi,SS}} = calendar:now_to_local_time(Now),
io_lib:format("~p-~2..0B-~2..0BT~2..0B:~2..0B:~2..0B",[YY,MM,DD,HH,Mi,SS]).
+
+make_url(undefined,_) ->
+ undefined;
+make_url(_,[]) ->
+ undefined;
+make_url(UrlBase0,Log) ->
+ UrlBase = string:strip(UrlBase0,right,$/),
+ RelativeLog = get_relative_log_url(Log),
+ string:join([UrlBase,RelativeLog],"/").
+
+get_test_root(Log) ->
+ LogParts = filename:split(Log),
+ filename:join(lists:sublist(LogParts,1,length(LogParts)-?log_depth)).
+
+get_relative_log_url(Log) ->
+ LogParts = filename:split(Log),
+ Start = length(LogParts)-?log_depth,
+ Length = ?log_depth+1,
+ string:join(lists:sublist(LogParts,Start,Length),"/").
+
+count_tcs([#testcase{name=ConfCase}|TCs],Ok,F,S)
+ when ConfCase=="init_per_suite";
+ ConfCase=="end_per_suite";
+ ConfCase=="init_per_group";
+ ConfCase=="end_per_group" ->
+ count_tcs(TCs,Ok,F,S);
+count_tcs([#testcase{result=passed}|TCs],Ok,F,S) ->
+ count_tcs(TCs,Ok+1,F,S);
+count_tcs([#testcase{result={fail,_}}|TCs],Ok,F,S) ->
+ count_tcs(TCs,Ok,F+1,S);
+count_tcs([#testcase{result={skipped,_}}|TCs],Ok,F,S) ->
+ count_tcs(TCs,Ok,F,S+1);
+count_tcs([],Ok,F,S) ->
+ {Ok+F+S,F,S}.
diff --git a/lib/common_test/test/Makefile b/lib/common_test/test/Makefile
index 560a0b0d5a..d469d03e04 100644
--- a/lib/common_test/test/Makefile
+++ b/lib/common_test/test/Makefile
@@ -39,13 +39,25 @@ MODULES= \
ct_sequence_1_SUITE \
ct_repeat_1_SUITE \
ct_testspec_1_SUITE \
+ ct_testspec_2_SUITE \
ct_skip_SUITE \
ct_error_SUITE \
ct_test_server_if_1_SUITE \
ct_config_SUITE \
ct_master_SUITE \
ct_misc_1_SUITE \
- ct_hooks_SUITE
+ ct_hooks_SUITE \
+ ct_netconfc_SUITE \
+ ct_basic_html_SUITE \
+ ct_auto_compile_SUITE \
+ ct_verbosity_SUITE \
+ ct_shell_SUITE \
+ ct_system_error_SUITE \
+ ct_snmp_SUITE \
+ ct_group_leader_SUITE \
+ ct_cover_SUITE \
+ ct_groups_search_SUITE \
+ ct_surefire_SUITE
ERL_FILES= $(MODULES:%=%.erl)
@@ -99,7 +111,7 @@ release_spec: opt
release_tests_spec:
$(INSTALL_DIR) "$(RELSYSDIR)"
$(INSTALL_DATA) $(ERL_FILES) $(COVERFILE) "$(RELSYSDIR)"
- $(INSTALL_DATA) common_test.spec "$(RELSYSDIR)"
+ $(INSTALL_DATA) common_test.spec common_test.cover "$(RELSYSDIR)"
chmod -R u+w "$(RELSYSDIR)"
@tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -)
diff --git a/lib/common_test/test/common_test.cover b/lib/common_test/test/common_test.cover
new file mode 100644
index 0000000000..3aa49623e7
--- /dev/null
+++ b/lib/common_test/test/common_test.cover
@@ -0,0 +1,10 @@
+%% -*- erlang -*-
+{incl_app,common_test,details}.
+{cross,common_test,[{test_server,[erl2html2,
+ test_server,
+ test_server_ctrl,
+ test_server_gl,
+ test_server_h,
+ test_server_io,
+ test_server_node,
+ test_server_sup]}]}.
diff --git a/lib/common_test/test/common_test.spec b/lib/common_test/test/common_test.spec
index 8755b08117..8bec66d6f2 100644
--- a/lib/common_test/test/common_test.spec
+++ b/lib/common_test/test/common_test.spec
@@ -1 +1 @@
-{suites,"../common_test_test",all}. \ No newline at end of file
+{suites,"../common_test_test",all}.
diff --git a/lib/common_test/test/ct_auto_compile_SUITE.erl b/lib/common_test/test/ct_auto_compile_SUITE.erl
new file mode 100644
index 0000000000..cc546ed30d
--- /dev/null
+++ b/lib/common_test/test/ct_auto_compile_SUITE.erl
@@ -0,0 +1,187 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2009-2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, 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_auto_compile_SUITE
+%%%
+%%% Description:
+%%%
+%%%
+%%% The suites used for the test are located in the data directory.
+%%%-------------------------------------------------------------------
+-module(ct_auto_compile_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() ->
+ [ac_flag, ac_spec].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+
+%%--------------------------------------------------------------------
+%% TEST CASES
+%%--------------------------------------------------------------------
+
+%%%-----------------------------------------------------------------
+%%%
+ac_flag(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ PrivDir = ?config(priv_dir, Config),
+ file:copy(filename:join(DataDir, "bad_SUITE.erl"),
+ filename:join(PrivDir, "bad_SUITE.erl")),
+ Suite = filename:join(DataDir, "dummy_SUITE"),
+ compile:file(Suite, [{outdir,PrivDir}]),
+ {Opts,ERPid} = setup([{dir,PrivDir},
+ {auto_compile,false},
+ {label,"ac_flag"}],
+ Config),
+
+ ok = ct_test_support:run(Opts, Config),
+ Events = ct_test_support:get_events(ERPid, Config),
+
+ ct_test_support:log_events(ac_flag,
+ reformat(Events, ?eh),
+ PrivDir,
+ Opts),
+
+ TestEvents = events_to_check(ac_flag),
+ ok = ct_test_support:verify_events(TestEvents, Events, Config).
+
+%%%-----------------------------------------------------------------
+%%%
+ac_spec(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ PrivDir = ?config(priv_dir, Config),
+ file:copy(filename:join(DataDir, "bad_SUITE.erl"),
+ filename:join(PrivDir, "bad_SUITE.erl")),
+ TestSpec = [{label,ac_spec},
+ {auto_compile,false},
+ {suites,PrivDir,all}],
+ FileName = filename:join(?config(priv_dir, Config),"ac_spec.spec"),
+ {ok,Dev} = file:open(FileName, [write]),
+ [io:format(Dev, "~p.~n", [Term]) || Term <- TestSpec],
+ file:close(Dev),
+
+ {Opts,ERPid} = setup([{spec,FileName}], Config),
+ ok = ct_test_support:run(Opts, Config),
+ Events = ct_test_support:get_events(ERPid, Config),
+
+ ct_test_support:log_events(ac_spec,
+ reformat(Events, ?eh),
+ PrivDir,
+ Opts),
+
+ TestEvents = events_to_check(ac_spec),
+ ok = ct_test_support:verify_events(TestEvents, Events, Config).
+
+
+%%%-----------------------------------------------------------------
+%%% 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(ac_flag) ->
+ [
+ {ct_test_support_eh,start_logging,{'DEF','RUNDIR'}},
+ {ct_test_support_eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {ct_test_support_eh,start_info,{1,1,3}},
+ {ct_test_support_eh,tc_start,{dummy_SUITE,init_per_suite}},
+ {ct_test_support_eh,tc_done,{dummy_SUITE,init_per_suite,ok}},
+ {ct_test_support_eh,test_stats,{1,1,{1,0}}},
+ {ct_test_support_eh,tc_start,{dummy_SUITE,end_per_suite}},
+ {ct_test_support_eh,tc_done,{dummy_SUITE,end_per_suite,ok}},
+ {ct_test_support_eh,test_done,{'DEF','STOP_TIME'}},
+ {ct_test_support_eh,stop_logging,[]}
+ ];
+
+test_events(ac_spec) ->
+ [
+ {ct_test_support_eh,start_logging,{'DEF','RUNDIR'}},
+ {ct_test_support_eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {ct_test_support_eh,start_info,{1,1,3}},
+ {ct_test_support_eh,tc_start,{dummy_SUITE,init_per_suite}},
+ {ct_test_support_eh,tc_done,{dummy_SUITE,init_per_suite,ok}},
+ {ct_test_support_eh,test_stats,{1,1,{1,0}}},
+ {ct_test_support_eh,tc_start,{dummy_SUITE,end_per_suite}},
+ {ct_test_support_eh,tc_done,{dummy_SUITE,end_per_suite,ok}},
+ {ct_test_support_eh,test_done,{'DEF','STOP_TIME'}},
+ {ct_test_support_eh,stop_logging,[]}
+ ].
diff --git a/lib/stdlib/test/erl_eval_helper.erl b/lib/common_test/test/ct_auto_compile_SUITE_data/bad_SUITE.erl
index 6863b40108..b270c28849 100644
--- a/lib/stdlib/test/erl_eval_helper.erl
+++ b/lib/common_test/test/ct_auto_compile_SUITE_data/bad_SUITE.erl
@@ -1,28 +1,23 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2009-2012. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, 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%
--module(erl_eval_helper, [Base]).
-
--export([add/1]).
-
-add(Arg) ->
- Base+Arg.
-
+-module(bad_SUITE).
+-compile(export_all).
+bad_bad_suite
diff --git a/lib/common_test/test/ct_auto_compile_SUITE_data/dummy_SUITE.erl b/lib/common_test/test/ct_auto_compile_SUITE_data/dummy_SUITE.erl
new file mode 100644
index 0000000000..0bb2e388c7
--- /dev/null
+++ b/lib/common_test/test/ct_auto_compile_SUITE_data/dummy_SUITE.erl
@@ -0,0 +1,130 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2009-2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, 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(dummy_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) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% @spec end_per_suite(Config0) -> void() | {save_config,Config1}
+%% Config0 = Config1 = [tuple()]
+%% @end
+%%--------------------------------------------------------------------
+end_per_suite(_Config) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% @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(_GroupName, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% @spec end_per_group(GroupName, Config0) ->
+%% void() | {save_config,Config1}
+%% GroupName = atom()
+%% Config0 = Config1 = [tuple()]
+%% @end
+%%--------------------------------------------------------------------
+end_per_group(_GroupName, _Config) ->
+ 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() ->
+ [].
+
+%%--------------------------------------------------------------------
+%% @spec all() -> GroupsAndTestCases | {skip,Reason}
+%% GroupsAndTestCases = [{group,GroupName} | TestCase]
+%% GroupName = atom()
+%% TestCase = atom()
+%% Reason = term()
+%% @end
+%%--------------------------------------------------------------------
+all() ->
+ [ok,fail,skip].
+
+
+ok(_Config) ->
+ ok.
+
+fail(Config) ->
+ tuple_to_list(Config),
+ ok.
+
+skip(_Config) ->
+ {skip,"should be skipped"}.
diff --git a/lib/common_test/test/ct_basic_html_SUITE.erl b/lib/common_test/test/ct_basic_html_SUITE.erl
new file mode 100644
index 0000000000..a5f2e6197e
--- /dev/null
+++ b/lib/common_test/test/ct_basic_html_SUITE.erl
@@ -0,0 +1,180 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2009-2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, 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_basic_html_SUITE
+%%%
+%%% Description:
+%%%
+%%%
+%%% The suites used for the test are located in the data directory.
+%%%-------------------------------------------------------------------
+-module(ct_basic_html_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() ->
+ [basic_flag, basic_spec].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+
+%%--------------------------------------------------------------------
+%% TEST CASES
+%%--------------------------------------------------------------------
+
+%%%-----------------------------------------------------------------
+%%%
+basic_flag(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ Suites = [filename:join(DataDir, "babbling_SUITE")],
+ {Opts,ERPid} = setup([{suite,Suites},
+ {basic_html,true},
+ {label,"basic_flag"}],
+ Config),
+
+ ok = ct_test_support:run(Opts, Config),
+ Events = ct_test_support:get_events(ERPid, Config),
+
+ ct_test_support:log_events(basic_flag,
+ reformat(Events, ?eh),
+ ?config(priv_dir, Config),
+ Opts),
+
+ TestEvents = events_to_check(basic_flag),
+ ok = ct_test_support:verify_events(TestEvents, Events, Config).
+
+%%%-----------------------------------------------------------------
+%%%
+basic_spec(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ TestSpec = [{label,basic_spec},
+ {basic_html,true},
+ {suites,DataDir,babbling_SUITE}],
+ FileName = filename:join(?config(priv_dir, Config),"basic_spec.spec"),
+ {ok,Dev} = file:open(FileName, [write]),
+ [io:format(Dev, "~p.~n", [Term]) || Term <- TestSpec],
+ file:close(Dev),
+
+ {Opts,ERPid} = setup([{spec,FileName}], Config),
+ ok = ct_test_support:run(Opts, Config),
+ Events = ct_test_support:get_events(ERPid, Config),
+
+ ct_test_support:log_events(basic_spec,
+ reformat(Events, ?eh),
+ ?config(priv_dir, Config),
+ Opts),
+
+ TestEvents = events_to_check(basic_spec),
+ ok = ct_test_support:verify_events(TestEvents, Events, Config).
+
+
+%%%-----------------------------------------------------------------
+%%% 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(basic_flag) ->
+ [
+ {ct_test_support_eh,start_logging,{'DEF','RUNDIR'}},
+ {ct_test_support_eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {ct_test_support_eh,start_info,{1,1,3}},
+ {ct_test_support_eh,tc_start,{babbling_SUITE,init_per_suite}},
+ {ct_test_support_eh,tc_done,{babbling_SUITE,init_per_suite,ok}},
+ {ct_test_support_eh,test_stats,{1,1,{1,0}}},
+ {ct_test_support_eh,tc_start,{babbling_SUITE,end_per_suite}},
+ {ct_test_support_eh,tc_done,{babbling_SUITE,end_per_suite,ok}},
+ {ct_test_support_eh,test_done,{'DEF','STOP_TIME'}},
+ {ct_test_support_eh,stop_logging,[]}
+ ];
+
+test_events(basic_spec) ->
+ [
+ {ct_test_support_eh,start_logging,{'DEF','RUNDIR'}},
+ {ct_test_support_eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {ct_test_support_eh,start_info,{1,1,3}},
+ {ct_test_support_eh,tc_start,{babbling_SUITE,init_per_suite}},
+ {ct_test_support_eh,tc_done,{babbling_SUITE,init_per_suite,ok}},
+ {ct_test_support_eh,test_stats,{1,1,{1,0}}},
+ {ct_test_support_eh,tc_start,{babbling_SUITE,end_per_suite}},
+ {ct_test_support_eh,tc_done,{babbling_SUITE,end_per_suite,ok}},
+ {ct_test_support_eh,test_done,{'DEF','STOP_TIME'}},
+ {ct_test_support_eh,stop_logging,[]}
+ ].
diff --git a/lib/common_test/test/ct_basic_html_SUITE_data/babbling_SUITE.erl b/lib/common_test/test/ct_basic_html_SUITE_data/babbling_SUITE.erl
new file mode 100644
index 0000000000..7a3ccf30e0
--- /dev/null
+++ b/lib/common_test/test/ct_basic_html_SUITE_data/babbling_SUITE.erl
@@ -0,0 +1,130 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2009-2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, 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(babbling_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) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% @spec end_per_suite(Config0) -> void() | {save_config,Config1}
+%% Config0 = Config1 = [tuple()]
+%% @end
+%%--------------------------------------------------------------------
+end_per_suite(_Config) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% @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(_GroupName, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% @spec end_per_group(GroupName, Config0) ->
+%% void() | {save_config,Config1}
+%% GroupName = atom()
+%% Config0 = Config1 = [tuple()]
+%% @end
+%%--------------------------------------------------------------------
+end_per_group(_GroupName, _Config) ->
+ 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() ->
+ [].
+
+%%--------------------------------------------------------------------
+%% @spec all() -> GroupsAndTestCases | {skip,Reason}
+%% GroupsAndTestCases = [{group,GroupName} | TestCase]
+%% GroupName = atom()
+%% TestCase = atom()
+%% Reason = term()
+%% @end
+%%--------------------------------------------------------------------
+all() ->
+ [ok,fail,skip].
+
+
+ok(_Config) ->
+ ok.
+
+fail(Config) ->
+ tuple_to_list(Config),
+ ok.
+
+skip(_Config) ->
+ {skip,"should be skipped"}.
diff --git a/lib/common_test/test/ct_config_SUITE.erl b/lib/common_test/test/ct_config_SUITE.erl
index 18218bee47..d92be9ec6e 100644
--- a/lib/common_test/test/ct_config_SUITE.erl
+++ b/lib/common_test/test/ct_config_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -88,7 +88,8 @@ 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, [filename:join(DataDir, "config/shadow.txt"),
+ filename:join(DataDir, "config/config.txt")]}],
["config_static_SUITE"]).
install_config(Config) when is_list(Config) ->
@@ -106,7 +107,8 @@ 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")}},
+ [{userconfig, {ct_config_xml, filename:join(DataDir, "config/config.xml")}},
+ {config, filename:join(DataDir, "config/shadow.txt")}],
["config_static_SUITE"]).
userconfig_dynamic(Config) when is_list(Config) ->
@@ -121,7 +123,8 @@ testspec_legacy(Config) when is_list(Config) ->
make_spec(DataDir, ConfigDir,
"spec_legacy.spec",
[config_static_SUITE],
- [{config, filename:join(DataDir, "config/config.txt")}]),
+ [{config, filename:join(DataDir, "config/shadow.txt")},
+ {config, filename:join(DataDir, "config/config.txt")}]),
run_test(config_static_SUITE,
Config,
{spec, filename:join(ConfigDir, "spec_legacy.spec")},
@@ -134,7 +137,8 @@ testspec_static(Config) when is_list(Config) ->
make_spec(DataDir, ConfigDir,
"spec_static.spec",
[config_static_SUITE],
- [{userconfig, {ct_config_xml, filename:join(DataDir, "config/config.xml")}}]),
+ [{userconfig, {ct_config_xml, filename:join(DataDir, "config/config.xml")}},
+ {config, filename:join(DataDir, "config/shadow.txt")}]),
run_test(config_static_SUITE,
Config,
{spec, filename:join(ConfigDir, "spec_static.spec")},
@@ -170,6 +174,7 @@ run_test(Name, Config, CTConfig, SuiteNames)->
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,
@@ -179,13 +184,15 @@ run_test(Name, Config, CTConfig, SuiteNames)->
ExpEvents = events_to_check(Name),
ok = ct_test_support:verify_events(ExpEvents, TestEvents, Config).
-setup_env(Test, Config, CTConfig) ->
+setup_env(Test, Config, CTConfig) when is_list(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],
+ Opts = Opts0 ++ [Test,{event_handler,{?eh,EvHArgs}} | CTConfig],
ERPid = ct_test_support:start_event_receiver(Config),
- {Opts,ERPid}.
+ {Opts,ERPid};
+setup_env(Test, Config, CTConfig) ->
+ setup_env(Test, Config, [CTConfig]).
reformat_events(Events, EH) ->
ct_test_support:reformat(Events, EH).
@@ -202,40 +209,50 @@ events_to_check(_, 0) ->
events_to_check(Test, N) ->
expected_events(Test) ++ events_to_check(Test, N-1).
+-define(ok(Name,Suite,Stat),{?eh,tc_start,{Suite,Name}},
+ {?eh,tc_done,{Suite,Name,ok}},
+ {?eh,test_stats,Stat}).
+-define(nok(Name,Suite,Reason,Stat),{?eh,tc_start,{Suite,Name}},
+ {?eh,tc_done,{Suite,Name,Reason}},
+ {?eh,test_stats,Stat}).
+
+-define(sok(Name,Stat),?ok(Name,config_static_SUITE,Stat)).
+-define(snok(Name,Reason,Stat),?nok(Name,config_static_SUITE,Reason,Stat)).
+
+-define(dok(Name,Stat),?ok(Name,config_dynamic_SUITE,Stat)).
+-define(dnok(Name,Reason,Stat),?nok(Name,config_dynamic_SUITE,Reason,Stat)).
+
expected_events(config_static_SUITE)->
[
{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
- {?eh,start_info,{1,1,8}},
+ {?eh,start_info,{1,1,'_'}},
{?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,[alias,x1]}}}},
- {?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}}},
+ ?sok(test_get_config_simple,{1,0,{0,0}}),
+ ?sok(test_get_config_nested,{2,0,{0,0}}),
+ ?sok(test_get_config_deep_nested,{3,0,{0,0}}),
+ ?sok(test_default_suitewide,{4,0,{0,0}}),
+ ?snok(test_config_name_already_in_use1,
+ {skipped,{config_name_already_in_use,[x1]}},{4,0,{1,0}}),
+ ?sok(test_default_tclocal,{5,0,{1,0}}),
+ ?snok(test_config_name_already_in_use2,
+ {skipped,{config_name_already_in_use,[alias,x1]}},{5,0,{2,0}}),
+ ?sok(test_alias_tclocal,{6,0,{2,0}}),
+ ?sok(test_get_config_undefined,{7,0,{2,0}}),
+ ?sok(test_require_subvals,{8,0,{2,0}}),
+ ?snok(test_require_subvals2,
+ {skipped,{require_failed,
+ {not_available,{gen_cfg,[a,b,c,d]}}}},{8,0,{2,1}}),
+ ?sok(test_require_deep_config,{9,0,{2,1}}),
+ ?sok(test_shadow_all,{10,0,{2,1}}),
+ ?sok(test_element,{11,0,{2,1}}),
+ ?sok(test_shadow_all_element,{12,0,{2,1}}),
+ ?sok(test_internal_deep,{13,0,{2,1}}),
+ ?sok(test_alias_tclocal_nested,{14,0,{2,1}}),
+ ?sok(test_alias_tclocal_nested_backward_compat,{15,0,{2,1}}),
+ ?sok(test_alias_tclocal_nested_backward_compat_subvals,{16,0,{2,1}}),
+ ?sok(test_config_same_name_already_in_use,{17,0,{2,1}}),
{?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'}},
@@ -246,29 +263,14 @@ expected_events(config_dynamic_SUITE)->
[
{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
- {?eh,start_info,{1,1,5}},
+ {?eh,start_info,{1,1,'_'}},
{?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}}},
+ ?dok(test_get_known_variable,{1,0,{0,0}}),
+ ?dok(test_localtime_update,{2,0,{0,0}}),
+ ?dok(test_server_pid,{3,0,{0,0}}),
+ ?dok(test_disappearable_variable,{4,0,{0,0}}),
+ ?dok(test_disappearable_variable_alias,{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'}},
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
index fcbffcd7f3..e4bcc5ba6b 100644
--- a/lib/common_test/test/ct_config_SUITE_data/config/config.txt
+++ b/lib/common_test/test/ct_config_SUITE_data/config/config.txt
@@ -2,7 +2,8 @@
{gen_cfg,
[
{a,a_value},
- {b,b_value}
+ {b,b_value},
+ {c,[{d,d_value}]}
]}.
{gen_cfg2,
[
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
index 0a3e5f2e31..8eeff1482f 100644
--- a/lib/common_test/test/ct_config_SUITE_data/config/config.xml
+++ b/lib/common_test/test/ct_config_SUITE_data/config/config.xml
@@ -3,6 +3,7 @@
<gen_cfg>
<a>a_value</a>
<b>b_value</b>
+ <c><d>d_value</d></c>
</gen_cfg>
<gen_cfg2>
<c>"Hello, world!"</c>
diff --git a/lib/common_test/test/ct_config_SUITE_data/config/shadow.txt b/lib/common_test/test/ct_config_SUITE_data/config/shadow.txt
new file mode 100644
index 0000000000..865bf9255a
--- /dev/null
+++ b/lib/common_test/test/ct_config_SUITE_data/config/shadow.txt
@@ -0,0 +1,12 @@
+{x, suite}.
+{gen_cfg3,
+ [
+ {l,
+ [
+ {m,
+ [
+ {n, "n"},
+ {o, 'o'}
+ ]}
+ ]}
+ ]}.
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
index 8751a2e8f3..19f1dab4af 100644
--- 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
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -46,7 +46,7 @@ suite() ->
{require, gen_cfg3},
{require, alias, gen_cfg},
%% x1 default value
- {x1, {x,suite}}
+ {default_config, x1, {x,suite}}
].
init_per_suite(Config) ->
@@ -55,14 +55,25 @@ init_per_suite(Config) ->
end_per_suite(_) ->
ok.
-all() -> [test_get_config_simple, test_get_config_nested, test_default_suitewide,
+all() -> [test_get_config_simple, test_get_config_nested,
+ test_get_config_deep_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) ->
+ test_get_config_undefined,
+ test_require_subvals,test_require_subvals2,test_require_deep_config,
+ test_shadow_all,test_element,test_shadow_all_element,
+ test_internal_deep, test_alias_tclocal_nested,
+ test_alias_tclocal_nested_backward_compat,
+ test_alias_tclocal_nested_backward_compat_subvals,
+ test_config_same_name_already_in_use
+].
+
+init_per_testcase(_,Config) ->
Config.
+end_per_testcase(test_alias_tclocal_nested_backward_compat, _) ->
+ os:putenv("COMMON_TEST_ALIAS_TOP",""),
+ ok;
end_per_testcase(_, _) ->
ok.
@@ -76,6 +87,11 @@ test_get_config_nested(_)->
a_value = ct:get_config({gen_cfg, a}),
ok.
+%% test getting a deep nested value
+test_get_config_deep_nested(_)->
+ d_value = ct:get_config({gen_cfg, c, d}),
+ ok.
+
%% test suite-wide default value
test_default_suitewide(_)->
suite = ct:get_config(x1),
@@ -109,15 +125,83 @@ test_config_name_already_in_use2(_) ->
ct:fail("Test should've been skipped, you shouldn't see this!"),
ok.
+
+test_config_same_name_already_in_use() ->
+ [].
+test_config_same_name_already_in_use(_) ->
+ ok = ct:require(x2,{gen_cfg,c}),
+ ok = ct:require(x2,{gen_cfg,c}).
+
%% test aliases
test_alias_tclocal() ->
[{require,newalias,gen_cfg}].
-test_alias_tclocal(_) ->
- A = [{a,a_value},{b,b_value}] = ct:get_config(newalias),
+test_alias_tclocal(C) when is_list(C) ->
+ test_alias_tclocal(newalias);
+test_alias_tclocal(Alias) when is_atom(Alias) ->
+ A = [{a,a_value},{b,b_value},{c,[{d,d_value}]}] = ct:get_config(Alias),
A = ct:get_config(gen_cfg),
+ B = b_value = ct:get_config({Alias,b}),
+ B = ct:get_config({gen_cfg,b}),
ok.
+%% test nested aliases
+test_alias_tclocal_nested() ->
+ [{require,newalias2,{gen_cfg,c}}].
+test_alias_tclocal_nested(_) ->
+ A = [{d,d_value}] = ct:get_config(newalias2),
+ A = ct:get_config({gen_cfg,c}),
+ B = d_value = ct:get_config({newalias2,d}),
+ B = ct:get_config({gen_cfg,c,d}),
+ ok.
+
+%% test nested aliases backward compat option
+test_alias_tclocal_nested_backward_compat() ->
+ os:putenv("COMMON_TEST_ALIAS_TOP","true"),
+ [{require,newalias3,{gen_cfg,c}}].
+test_alias_tclocal_nested_backward_compat(_) ->
+ test_alias_tclocal(newalias3).
+
+%% test nested aliases backward compat option
+test_alias_tclocal_nested_backward_compat_subvals() ->
+ [{require,newalias4,{gen_cfg,[c]}}].
+test_alias_tclocal_nested_backward_compat_subvals(_) ->
+ test_alias_tclocal(newalias4).
+
%% test for getting undefined variables
test_get_config_undefined(_) ->
undefined = ct:get_config(y1),
ok.
+
+test_require_subvals() ->
+ [{require, {gen_cfg,[a,b,c]}}].
+test_require_subvals(_) ->
+ ok.
+
+test_require_subvals2() ->
+ [{require, {gen_cfg,[a,b,c,d]}}].
+test_require_subvals2(_) ->
+ ct:fail("Test should've been skipped, you shouldn't see this!"),
+ ok.
+
+test_require_deep_config() ->
+ [{require, {gen_cfg3, m, n}}].
+test_require_deep_config(_) ->
+ ok.
+
+
+test_shadow_all(_) ->
+ ["n","N"] = ct:get_config({gen_cfg3,l, m, n}, [], [all]).
+
+test_element(_) ->
+ {{gen_cfg3,l, m, n},"n"} = ct:get_config({gen_cfg3,l, m, n}, [], [element]).
+
+test_shadow_all_element(_) ->
+ [{{gen_cfg3,l, m, n},"n"},{{gen_cfg3,l, m, n},"N"}] =
+ ct:get_config({gen_cfg3,l, m, n}, [], [all,element]).
+
+%% The tests below are needed to verify that things like ct:telnet can use
+%% nested configs
+test_internal_deep(_) ->
+ "n" = ct:get_config({{gen_cfg3,l,m},n}),
+ a_value = ct:get_config({{gen_cfg},a}),
+ undefined = ct:get_config({{gen_cfg3,l,m},p}).
diff --git a/lib/common_test/test/ct_config_info_SUITE.erl b/lib/common_test/test/ct_config_info_SUITE.erl
index 40da377ee5..10fe8286dd 100644
--- a/lib/common_test/test/ct_config_info_SUITE.erl
+++ b/lib/common_test/test/ct_config_info_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -123,8 +123,7 @@ test_events(config_info) ->
{?eh,tc_done,{config_info_1_SUITE,init_per_suite,ok}},
[{?eh,tc_start,{config_info_1_SUITE,{init_per_group,g1,[]}}},
- {?eh,tc_done,{config_info_1_SUITE,
- {init_per_group,unknown,[]},
+ {?eh,tc_done,{config_info_1_SUITE,{init_per_group,g1,[]},
{failed,{timetrap_timeout,350}}}},
{?eh,tc_auto_skip,{config_info_1_SUITE,t11,
{failed,{config_info_1_SUITE,init_per_group,{timetrap_timeout,350}}}}},
@@ -136,14 +135,12 @@ test_events(config_info) ->
{?eh,tc_done,{config_info_1_SUITE,{init_per_group,g2,[]},ok}},
{?eh,tc_done,{config_info_1_SUITE,t21,ok}},
{?eh,tc_start,{config_info_1_SUITE,{end_per_group,g2,[]}}},
- {?eh,tc_done,{config_info_1_SUITE,
- {end_per_group,unknown,[]},
+ {?eh,tc_done,{config_info_1_SUITE,{end_per_group,g2,[]},
{failed,{timetrap_timeout,450}}}}],
[{?eh,tc_start,{config_info_1_SUITE,{init_per_group,g3,[]}}},
{?eh,tc_done,{config_info_1_SUITE,{init_per_group,g3,[]},ok}},
[{?eh,tc_start,{config_info_1_SUITE,{init_per_group,g4,[]}}},
- {?eh,tc_done,{config_info_1_SUITE,
- {init_per_group,unknown,[]},
+ {?eh,tc_done,{config_info_1_SUITE,{init_per_group,g4,[]},
{failed,{timetrap_timeout,400}}}},
{?eh,tc_auto_skip,{config_info_1_SUITE,t41,
{failed,{config_info_1_SUITE,init_per_group,
@@ -164,8 +161,7 @@ test_events(config_info) ->
{?eh,tc_done,{config_info_1_SUITE,{init_per_group,g5,[]},ok}},
{?eh,tc_done,{config_info_1_SUITE,t51,ok}},
{?eh,tc_start,{config_info_1_SUITE,{end_per_group,g5,[]}}},
- {?eh,tc_done,{config_info_1_SUITE,
- {end_per_group,unknown,[]},
+ {?eh,tc_done,{config_info_1_SUITE,{end_per_group,g5,[]},
{failed,{timetrap_timeout,400}}}}],
{?eh,tc_start,{config_info_1_SUITE,{end_per_group,g3,[]}}},
{?eh,tc_done,{config_info_1_SUITE,{end_per_group,g3,[]},ok}}],
diff --git a/lib/common_test/test/ct_cover_SUITE.erl b/lib/common_test/test/ct_cover_SUITE.erl
new file mode 100644
index 0000000000..cb49dc423f
--- /dev/null
+++ b/lib/common_test/test/ct_cover_SUITE.erl
@@ -0,0 +1,312 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, 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_cover_SUITE
+%%%
+%%% Description:
+%%% Test code cover analysis support
+%%%
+%%%-------------------------------------------------------------------
+-module(ct_cover_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(suite, cover_SUITE).
+-define(mod, cover_test_mod).
+
+%%--------------------------------------------------------------------
+%% 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) ->
+ case test_server:is_cover() of
+ true ->
+ {skip,"Test server is running cover already - skipping"};
+ false ->
+ ct_test_support:init_per_suite(Config)
+ end.
+
+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) ->
+ Node = fullname(existing_node),
+ case lists:member(Node,nodes()) of
+ true -> rpc:call(Node,erlang,halt,[]);
+ false -> ok
+ end,
+ ct_test_support:end_per_testcase(TestCase, Config).
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [
+ default,
+ cover_stop_true,
+ cover_stop_false,
+ slave,
+ slave_start_slave,
+ cover_node_option,
+ ct_cover_add_remove_nodes,
+ otp_9956,
+ cross
+ ].
+
+%%--------------------------------------------------------------------
+%% TEST CASES
+%%--------------------------------------------------------------------
+
+%% Check that cover is collected from test node
+%% Also check that cover is by default stopped after test is completed
+default(Config) ->
+ {ok,Events} = run_test(default,Config),
+ false = check_cover(Config),
+ check_calls(Events,1),
+ ok.
+
+%% Check that cover is stopped when cover_stop option is set to true
+cover_stop_true(Config) ->
+ {ok,_Events} = run_test(cover_stop_true,[{cover_stop,true}],Config),
+ false = check_cover(Config).
+
+%% Check that cover is not stopped when cover_stop option is set to false
+cover_stop_false(Config) ->
+ {ok,_Events} = run_test(cover_stop_false,[{cover_stop,false}],Config),
+ {true,[],[?mod]} = check_cover(Config),
+ CTNode = proplists:get_value(ct_node, Config),
+ ok = rpc:call(CTNode,cover,stop,[]),
+ false = check_cover(Config),
+ ok.
+
+%% Let test node start a slave node - check that cover is collected
+%% from both nodes
+slave(Config) ->
+ {ok,Events} = run_test(slave,slave,[],Config),
+ check_calls(Events,2),
+ ok.
+
+%% Let test node start a slave node which in turn starts another slave
+%% node - check that cover is collected from all three nodes
+slave_start_slave(Config) ->
+ {ok,Events} = run_test(slave_start_slave,slave_start_slave,[],Config),
+ check_calls(Events,3),
+ ok.
+
+%% Start a slave node before test starts - the node is listed in cover
+%% spec file.
+%% Check that cover is collected from test node and slave node.
+cover_node_option(Config) ->
+ {ok, HostStr}=inet:gethostname(),
+ Host = list_to_atom(HostStr),
+ DataDir = ?config(data_dir,Config),
+ {ok,Node} = ct_slave:start(Host,existing_node,
+ [{erl_flags,"-pa " ++ DataDir}]),
+ false = check_cover(Node),
+ CoverSpec = default_cover_file_content() ++ [{nodes,[Node]}],
+ CoverFile = create_cover_file(cover_node_option,CoverSpec,Config),
+ {ok,Events} = run_test(cover_node_option,cover_node_option,
+ [{cover,CoverFile}],Config),
+ check_calls(Events,2),
+ {ok,Node} = ct_slave:stop(existing_node),
+ ok.
+
+%% Test ct_cover:add_nodes/1 and ct_cover:remove_nodes/1
+%% Check that cover is collected from added node
+ct_cover_add_remove_nodes(Config) ->
+ {ok, HostStr}=inet:gethostname(),
+ Host = list_to_atom(HostStr),
+ DataDir = ?config(data_dir,Config),
+ {ok,Node} = ct_slave:start(Host,existing_node,
+ [{erl_flags,"-pa " ++ DataDir}]),
+ false = check_cover(Node),
+ {ok,Events} = run_test(ct_cover_add_remove_nodes,ct_cover_add_remove_nodes,
+ [],Config),
+ check_calls(Events,2),
+ {ok,Node} = ct_slave:stop(existing_node),
+ ok.
+
+%% Test that the test suite itself can be cover compiled and that
+%% data_dir is set correctly (OTP-9956)
+otp_9956(Config) ->
+ CoverFile = create_cover_file(otp_9956,[{incl_mods,[?suite]}],Config),
+ {ok,Events} = run_test(otp_9956,otp_9956,[{cover,CoverFile}],Config),
+ check_calls(Events,{?suite,otp_9956,1},1),
+ ok.
+
+%% Test cross cover mechanism
+cross(Config) ->
+ {ok,Events1} = run_test(cross1,Config),
+ check_calls(Events1,1),
+
+ CoverFile2 = create_cover_file(cross1,[{cross,[{cross1,[?mod]}]}],Config),
+ {ok,Events2} = run_test(cross2,[{cover,CoverFile2}],Config),
+ check_calls(Events2,1),
+
+ %% Get the log dirs for each test and run cross cover analyse
+ [D11,D12] = lists:sort(get_run_dirs(Events1)),
+ [D21,D22] = lists:sort(get_run_dirs(Events2)),
+
+ ct_cover:cross_cover_analyse(details,[{cross1,D11},{cross2,D21}]),
+ ct_cover:cross_cover_analyse(details,[{cross1,D12},{cross2,D22}]),
+
+ %% Get the cross cover logs and read for each test
+ [C11,C12,C21,C22] =
+ [filename:join(D,"cross_cover.html") || D <- [D11,D12,D21,D22]],
+
+ {ok,CrossData} = file:read_file(C11),
+ {ok,CrossData} = file:read_file(C12),
+
+ {ok,Def} = file:read_file(C21),
+ {ok,Def} = file:read_file(C22),
+
+ %% A simple test: just check that the test module exists in the
+ %% log from cross1 test, and that it does not exist in the log
+ %% from cross2 test.
+ TestMod = list_to_binary(atom_to_list(?mod)),
+ {_,_} = binary:match(CrossData,TestMod),
+ nomatch = binary:match(Def,TestMod),
+ {_,_} = binary:match(Def,
+ <<"No cross cover modules exist for this application">>),
+
+ ok.
+
+
+%%%-----------------------------------------------------------------
+%%% HELP FUNCTIONS
+%%%-----------------------------------------------------------------
+run_test(Label,Config) ->
+ run_test(Label,[],Config).
+run_test(Label,ExtraOpts,Config) ->
+ run_test(Label,default,ExtraOpts,Config).
+run_test(Label,Testcase,ExtraOpts,Config) ->
+ DataDir = ?config(data_dir, Config),
+ Suite = filename:join(DataDir, ?suite),
+ CoverFile =
+ case proplists:get_value(cover,ExtraOpts) of
+ undefined ->
+ create_default_cover_file(Label,Config);
+ CF ->
+ CF
+ end,
+ RestOpts = lists:keydelete(cover,1,ExtraOpts),
+ {Opts,ERPid} = setup([{suite,Suite},{testcase,Testcase},
+ {cover,CoverFile},{label,Label}] ++ RestOpts, Config),
+ execute(Label, Testcase, Opts, ERPid, 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}.
+
+execute(Name, Testcase, Opts, ERPid, Config) ->
+ ok = ct_test_support:run(Opts, Config),
+ Events = ct_test_support:get_events(ERPid, Config),
+
+ ct_test_support:log_events(Name,
+ reformat(Events, ?eh),
+ ?config(priv_dir, Config),
+ Opts),
+ TestEvents = events_to_check(Testcase),
+ R = ct_test_support:verify_events(TestEvents, Events, Config),
+ {R,Events}.
+
+reformat(Events, EH) ->
+ ct_test_support:reformat(Events, EH).
+
+events_to_check(Testcase) ->
+ OneTest =
+ [{?eh,start_logging,{'DEF','RUNDIR'}}] ++
+ [{?eh,tc_done,{?suite,Testcase,ok}}] ++
+ [{?eh,stop_logging,[]}],
+
+ %% 2 tests (ct:run_test + script_start) is default
+ OneTest ++ OneTest.
+
+check_cover(Config) when is_list(Config) ->
+ CTNode = proplists:get_value(ct_node, Config),
+ check_cover(CTNode);
+check_cover(Node) when is_atom(Node) ->
+ case rpc:call(Node,test_server,is_cover,[]) of
+ true ->
+ {true,
+ rpc:call(Node,cover,which_nodes,[]),
+ rpc:call(Node,cover,modules,[])};
+ false ->
+ false
+ end.
+
+%% Get the log dir "run.<timestamp>" for all (both!) tests
+get_run_dirs(Events) ->
+ [filename:dirname(TCLog) ||
+ {ct_test_support_eh,
+ {event,tc_logfile,_Node,
+ {{?suite,init_per_suite},TCLog}}} <- Events].
+
+%% Check that each coverlog includes N calls to ?mod:foo/0
+check_calls(Events,N) ->
+ check_calls(Events,{?mod,foo,0},N).
+check_calls(Events,MFA,N) ->
+ CoverLogs = [filename:join(D,"all.coverdata") || D <- get_run_dirs(Events)],
+ do_check_logs(CoverLogs,MFA,N).
+
+do_check_logs([CoverLog|CoverLogs],{Mod,_,_} = MFA,N) ->
+ {ok,_} = cover:start(),
+ ok = cover:import(CoverLog),
+ {ok,Calls} = cover:analyse(Mod,calls,function),
+ ok = cover:stop(),
+ {MFA,N} = lists:keyfind(MFA,1,Calls),
+ do_check_logs(CoverLogs,MFA,N);
+do_check_logs([],_,_) ->
+ ok.
+
+fullname(Name) ->
+ {ok,Host} = inet:gethostname(),
+ list_to_atom(atom_to_list(Name) ++ "@" ++ Host).
+
+default_cover_file_content() ->
+ [{incl_mods,[?mod]}].
+
+create_default_cover_file(Filename,Config) ->
+ create_cover_file(Filename,default_cover_file_content(),Config).
+
+create_cover_file(Filename,Terms,Config) ->
+ PrivDir = ?config(priv_dir,Config),
+ File = filename:join(PrivDir,Filename) ++ ".cover",
+ {ok,Fd} = file:open(File,[write]),
+ lists:foreach(fun(Term) ->
+ file:write(Fd,io_lib:format("~p.~n",[Term]))
+ end,Terms),
+ ok = file:close(Fd),
+ File.
diff --git a/lib/common_test/test/ct_cover_SUITE_data/cover_SUITE.erl b/lib/common_test/test/ct_cover_SUITE_data/cover_SUITE.erl
new file mode 100644
index 0000000000..fdc3323f0a
--- /dev/null
+++ b/lib/common_test/test/ct_cover_SUITE_data/cover_SUITE.erl
@@ -0,0 +1,156 @@
+%%--------------------------------------------------------------------
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, 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: cover_SUITE.erl
+%%
+%% Description:
+%% This file contains the test cases for the code coverage support
+%%
+%% @author Support
+%% @doc Test of code coverage support in common_test
+%% @end
+%%----------------------------------------------------------------------
+%%----------------------------------------------------------------------
+-module(cover_SUITE).
+-include_lib("common_test/include/ct.hrl").
+
+-compile(export_all).
+
+%% Default timetrap timeout (set in init_per_testcase).
+-define(default_timeout, ?t:minutes(1)).
+
+suite() ->
+ [].
+
+all() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(Config) ->
+ Config.
+
+init_per_testcase(_Case, Config) ->
+ Dog = test_server:timetrap(?default_timeout),
+ [{watchdog, Dog}|Config].
+
+end_per_testcase(Case, Config) ->
+ %% try apply(?MODULE,Case,[cleanup,Config])
+ %% catch error:undef -> ok
+ %% end,
+
+ kill_slaves(Case,nodes()),
+ Dog=?config(watchdog, Config),
+ test_server:timetrap_cancel(Dog),
+ ok.
+
+%%%-----------------------------------------------------------------
+%%% Test cases
+break(_Config) ->
+ test_server:break(""),
+ ok.
+
+default(Config) ->
+ cover_compiled = code:which(cover_test_mod),
+ cover_test_mod:foo(),
+ ok.
+
+slave(Config) ->
+ cover_compiled = code:which(cover_test_mod),
+ cover_test_mod:foo(),
+ N1 = nodename(slave,1),
+ {ok,Node} = ct_slave:start(N1),
+ cover_compiled = rpc:call(Node,code,which,[cover_test_mod]),
+ rpc:call(Node,cover_test_mod,foo,[]),
+ {ok,Node} = ct_slave:stop(N1),
+ ok.
+
+slave_start_slave(Config) ->
+ cover_compiled = code:which(cover_test_mod),
+ cover_test_mod:foo(),
+ N1 = nodename(slave_start_slave,1),
+ N2 = nodename(slave_start_slave,2),
+ {ok,Node} = ct_slave:start(N1),
+ cover_compiled = rpc:call(Node,code,which,[cover_test_mod]),
+ rpc:call(Node,cover_test_mod,foo,[]),
+ {ok,Node2} = rpc:call(Node,ct_slave,start,[N2]),
+ rpc:call(Node2,cover_test_mod,foo,[]),
+ {ok,Node2} = rpc:call(Node,ct_slave,stop,[N2]),
+ {ok,Node} = ct_slave:stop(N1),
+ ok.
+
+cover_node_option(Config) ->
+ cover_compiled = code:which(cover_test_mod),
+ cover_test_mod:foo(),
+ Node = fullname(existing_node),
+ cover_compiled = rpc:call(Node,code,which,[cover_test_mod]),
+ rpc:call(Node,cover_test_mod,foo,[]),
+ ok.
+
+ct_cover_add_remove_nodes(Config) ->
+ cover_compiled = code:which(cover_test_mod),
+ cover_test_mod:foo(),
+ Node = fullname(existing_node),
+ Beam = rpc:call(Node,code,which,[cover_test_mod]),
+ false = (Beam == cover_compiled),
+
+ rpc:call(Node,cover_test_mod,foo,[]), % should not be collected
+ {ok,[Node]} = ct_cover:add_nodes([Node]),
+ cover_compiled = rpc:call(Node,code,which,[cover_test_mod]),
+ rpc:call(Node,cover_test_mod,foo,[]), % should be collected
+ ok = ct_cover:remove_nodes([Node]),
+ rpc:call(Node,cover_test_mod,foo,[]), % should not be collected
+
+ Beam = rpc:call(Node,code,which,[cover_test_mod]),
+
+ ok.
+
+otp_9956(Config) ->
+ cover_compiled = code:which(?MODULE),
+ DataDir = ?config(data_dir,Config),
+ absolute = filename:pathtype(DataDir),
+ true = filelib:is_dir(DataDir),
+ ok.
+
+
+%%%-----------------------------------------------------------------
+%%% Internal
+nodename(Case,N) ->
+ list_to_atom(nodeprefix(Case) ++ integer_to_list(N)).
+
+nodeprefix(Case) ->
+ atom_to_list(?MODULE) ++ "_" ++ atom_to_list(Case) ++ "_node".
+
+
+fullname(Name) ->
+ {ok,Host} = inet:gethostname(),
+ list_to_atom(atom_to_list(Name) ++ "@" ++ Host).
+
+kill_slaves(Case, [Node|Nodes]) ->
+ Prefix = nodeprefix(Case),
+ case lists:prefix(Prefix,atom_to_list(Node)) of
+ true ->
+ rpc:call(Node,erlang,halt,[]);
+ _ ->
+ ok
+ end,
+ kill_slaves(Case,Nodes);
+kill_slaves(_,[]) ->
+ ok.
diff --git a/lib/common_test/test/ct_cover_SUITE_data/cover_SUITE_data/.gitignore b/lib/common_test/test/ct_cover_SUITE_data/cover_SUITE_data/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/common_test/test/ct_cover_SUITE_data/cover_SUITE_data/.gitignore
diff --git a/lib/common_test/test/ct_cover_SUITE_data/cover_test_mod.erl b/lib/common_test/test/ct_cover_SUITE_data/cover_test_mod.erl
new file mode 100644
index 0000000000..d4f69452c3
--- /dev/null
+++ b/lib/common_test/test/ct_cover_SUITE_data/cover_test_mod.erl
@@ -0,0 +1,4 @@
+-module(cover_test_mod).
+-compile(export_all).
+foo() ->
+ ok.
diff --git a/lib/common_test/test/ct_error_SUITE.erl b/lib/common_test/test/ct_error_SUITE.erl
index c9ee47e01b..6d90b29f41 100644
--- a/lib/common_test/test/ct_error_SUITE.erl
+++ b/lib/common_test/test/ct_error_SUITE.erl
@@ -61,7 +61,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[cfg_error, lib_error, no_compile, timetrap_end_conf,
timetrap_normal, timetrap_extended, timetrap_parallel,
- timetrap_fun, misc_errors].
+ timetrap_fun, timetrap_fun_group, misc_errors].
groups() ->
[].
@@ -251,6 +251,24 @@ timetrap_fun(Config) when is_list(Config) ->
%%%-----------------------------------------------------------------
%%%
+timetrap_fun_group(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_8_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_fun_group,
+ reformat(Events, ?eh),
+ ?config(priv_dir, Config),
+ Opts),
+
+ TestEvents = events_to_check(timetrap_fun_group),
+ ok = ct_test_support:verify_events(TestEvents, Events, Config).
+
+%%%-----------------------------------------------------------------
+%%%
misc_errors(Config) when is_list(Config) ->
DataDir = ?config(data_dir, Config),
Join = fun(D, S) -> filename:join(D, "error/test/"++S) end,
@@ -429,8 +447,7 @@ test_events(cfg_error) ->
{'EXIT',{init_per_group_fails,g1}}}}}}],
[{?eh,tc_start,{cfg_error_8_SUITE,{init_per_group,g2,[]}}},
- {?eh,tc_done,{cfg_error_8_SUITE,
- {init_per_group,unknown,[]},
+ {?eh,tc_done,{cfg_error_8_SUITE,{init_per_group,g2,[]},
{failed,{timetrap_timeout,2000}}}},
{?eh,tc_auto_skip,{cfg_error_8_SUITE,tc1,
{failed,{cfg_error_8_SUITE,init_per_group,
@@ -500,7 +517,7 @@ test_events(cfg_error) ->
{?eh,tc_done,{cfg_error_8_SUITE,tc1,ok}},
{?eh,test_stats,{9,0,{0,14}}},
{?eh,tc_start,{cfg_error_8_SUITE,{end_per_group,g12,[]}}},
- {?eh,tc_done,{cfg_error_8_SUITE,{end_per_group,unknown,[]},
+ {?eh,tc_done,{cfg_error_8_SUITE,{end_per_group,g12,[]},
{failed,{timetrap_timeout,2000}}}}],
{?eh,tc_start,{cfg_error_8_SUITE,end_per_suite}},
@@ -878,11 +895,11 @@ test_events(timetrap_fun) ->
{failed,{timetrap_timeout,{'$approx',1000}}}}},
{?eh,test_stats,{0,5,{0,0}}},
{?eh,tc_start,{timetrap_5_SUITE,tc1}},
- {?eh,tc_done,{undefined,undefined,{user_timetrap_error,
+ {?eh,tc_done,{timetrap_5_SUITE,tc1,{user_timetrap_error,
{kaboom,'_'}}}},
{?eh,test_stats,{0,6,{0,0}}},
{?eh,tc_start,{timetrap_5_SUITE,tc2}},
- {?eh,tc_done,{undefined,undefined,{user_timetrap_error,
+ {?eh,tc_done,{timetrap_5_SUITE,tc2,{user_timetrap_error,
{kaboom,'_'}}}},
{?eh,test_stats,{0,7,{0,0}}},
{?eh,tc_start,{timetrap_5_SUITE,tc3}},
@@ -937,7 +954,7 @@ test_events(timetrap_fun) ->
{?eh,tc_done,{timetrap_5_SUITE,end_per_suite,ok}},
{?eh,tc_start,{timetrap_6_SUITE,init_per_suite}},
- {?eh,tc_done,{undefined,undefined,{user_timetrap_error,
+ {?eh,tc_done,{timetrap_6_SUITE,init_per_suite,{user_timetrap_error,
{kaboom,'_'}}}},
{?eh,tc_auto_skip,{timetrap_6_SUITE,tc0,
{failed,{timetrap_6_SUITE,init_per_suite,
@@ -971,11 +988,423 @@ test_events(timetrap_fun) ->
{?eh,stop_logging,[]}
];
+test_events(timetrap_fun_group) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{1,1,58}},
+ {?eh,tc_start,{timetrap_8_SUITE,init_per_suite}},
+ {?eh,tc_done,{timetrap_8_SUITE,init_per_suite,ok}},
+
+ [{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,g0,[]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{init_per_group,g0,[]},ok}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc0}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc0,
+ {failed,{timetrap_timeout,{'$approx',1000}}}}},
+ {?eh,test_stats,{0,1,{0,0}}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc2}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc2,
+ {failed,{timetrap_timeout,{'$approx',500}}}}},
+ {?eh,test_stats,{0,2,{0,0}}},
+ {?eh,tc_start,{timetrap_8_SUITE,{end_per_group,g0,[]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{end_per_group,g0,[]},ok}}],
+
+ [{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,g1,[]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{init_per_group,g1,[]},ok}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc0}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc0,
+ {failed,{timetrap_timeout,{'$approx',1000}}}}},
+ {?eh,test_stats,{0,3,{0,0}}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc2}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc2,
+ {failed,{timetrap_timeout,{'$approx',500}}}}},
+ {?eh,test_stats,{0,4,{0,0}}},
+ {?eh,tc_start,{timetrap_8_SUITE,{end_per_group,g1,[]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{end_per_group,g1,[]},ok}}],
+
+ [{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,g2,[]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{init_per_group,g2,[]},ok}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc1}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc1,
+ {failed,{timetrap_timeout,{'$approx',1000}}}}},
+ {?eh,test_stats,{0,5,{0,0}}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc2}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc2,
+ {failed,{timetrap_timeout,{'$approx',500}}}}},
+ {?eh,test_stats,{0,6,{0,0}}},
+ {?eh,tc_start,{timetrap_8_SUITE,{end_per_group,g2,[]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{end_per_group,g2,[]},ok}}],
+
+ [{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,g3,[]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{init_per_group,g3,[]},ok}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc4}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc4,
+ {failed,{timetrap_timeout,{'$approx',2000}}}}},
+ {?eh,test_stats,{0,7,{0,0}}},
+ [{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,g1,[]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{init_per_group,g1,[]},ok}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc0}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc0,
+ {failed,{timetrap_timeout,{'$approx',1000}}}}},
+ {?eh,test_stats,{0,8,{0,0}}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc2}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc2,
+ {failed,{timetrap_timeout,{'$approx',500}}}}},
+ {?eh,test_stats,{0,9,{0,0}}},
+ {?eh,tc_start,{timetrap_8_SUITE,{end_per_group,g1,[]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{end_per_group,g1,[]},ok}}],
+ [{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,g2,[]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{init_per_group,g2,[]},ok}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc1}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc1,
+ {failed,{timetrap_timeout,{'$approx',1000}}}}},
+ {?eh,test_stats,{0,10,{0,0}}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc2}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc2,
+ {failed,{timetrap_timeout,{'$approx',500}}}}},
+ {?eh,test_stats,{0,11,{0,0}}},
+ {?eh,tc_start,{timetrap_8_SUITE,{end_per_group,g2,[]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{end_per_group,g2,[]},ok}}],
+ {?eh,tc_start,{timetrap_8_SUITE,{end_per_group,g3,[]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{end_per_group,g3,[]},ok}}],
+
+ [{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,g4,[]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{init_per_group,g4,[]},
+ {user_timetrap_error,{kaboom,'_'}}}},
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,tc0,
+ {failed,{timetrap_8_SUITE,init_per_group,
+ {user_timetrap_error,{kaboom,'_'}}}}}},
+ {?eh,test_stats,{0,11,{0,1}}},
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,tc2,
+ {failed,{timetrap_8_SUITE,init_per_group,
+ {user_timetrap_error,{kaboom,'_'}}}}}},
+ {?eh,test_stats,{0,11,{0,2}}},
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,end_per_group,
+ {failed,{timetrap_8_SUITE,init_per_group,
+ {user_timetrap_error,{kaboom,'_'}}}}}}],
+
+ [{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,g5,[]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{init_per_group,g5,[]},
+ {user_timetrap_error,{kaboom,'_'}}}},
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,tc0,
+ {failed,{timetrap_8_SUITE,init_per_group,
+ {user_timetrap_error,{kaboom,'_'}}}}}},
+ {?eh,test_stats,{0,11,{0,3}}},
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,tc2,
+ {failed,{timetrap_8_SUITE,init_per_group,
+ {user_timetrap_error,{kaboom,'_'}}}}}},
+ {?eh,test_stats,{0,11,{0,4}}},
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,end_per_group,
+ {failed,{timetrap_8_SUITE,init_per_group,
+ {user_timetrap_error,{kaboom,'_'}}}}}}],
+
+ [{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,g6,[]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{init_per_group,g6,[]},
+ {failed,{timetrap_timeout,{'$approx',500}}}}},
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,tc0,
+ {failed,{timetrap_8_SUITE,init_per_group,
+ {timetrap_timeout,'_'}}}}},
+ {?eh,test_stats,{0,11,{0,5}}},
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,tc2,
+ {failed,{timetrap_8_SUITE,init_per_group,
+ {timetrap_timeout,'_'}}}}},
+ {?eh,test_stats,{0,11,{0,6}}},
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,end_per_group,
+ {failed,{timetrap_8_SUITE,init_per_group,
+ {timetrap_timeout,'_'}}}}}],
+
+ [{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,g7,[]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{init_per_group,g7,[]},ok}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc5}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc5,ok}},
+ {?eh,test_stats,{1,11,{0,6}}},
+ {?eh,tc_start,{timetrap_8_SUITE,{end_per_group,g7,[]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{end_per_group,g7,[]},
+ {user_timetrap_error,{kaboom,'_'}}}}],
+
+ [{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,g8,[]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{init_per_group,g8,[]},ok}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc5}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc5,ok}},
+ {?eh,test_stats,{2,11,{0,6}}},
+ {?eh,tc_start,{timetrap_8_SUITE,{end_per_group,g8,[]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{end_per_group,g8,[]},
+ {failed,{timetrap_timeout,{'$approx',500}}}}}],
+
+ [{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,g9,[]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{init_per_group,g9,[]},ok}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc5}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc5,ok}},
+ {?eh,test_stats,{3,11,{0,6}}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc0}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc0,
+ {user_timetrap_error,{kaboom,'_'}}}},
+ {?eh,test_stats,{3,12,{0,6}}},
+ {?eh,tc_start,{timetrap_8_SUITE,{end_per_group,g9,[]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{end_per_group,g9,[]},ok}}],
+
+ [{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,g10,[]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{init_per_group,g10,[]},ok}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc0}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc0,
+ {user_timetrap_error,{kaboom,'_'}}}},
+ {?eh,test_stats,{3,13,{0,6}}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc5}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc5,ok}},
+ {?eh,test_stats,{4,13,{0,6}}},
+ {?eh,tc_start,{timetrap_8_SUITE,{end_per_group,g10,[]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{end_per_group,g10,[]},ok}}],
+
+ [{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,g11,[]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{init_per_group,g11,[]},ok}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc3}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc3,
+ {failed,{timetrap_timeout,{'$approx',4000}}}}},
+ {?eh,test_stats,{4,14,{0,6}}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc2}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc2,
+ {failed,{timetrap_timeout,{'$approx',500}}}}},
+ {?eh,test_stats,{4,15,{0,6}}},
+ {?eh,tc_start,{timetrap_8_SUITE,{end_per_group,g11,[]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{end_per_group,g11,[]},ok}}],
+
+ {parallel,
+ [{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,pg0,[parallel]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{init_per_group,pg0,[parallel]},ok}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc0}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc0,
+ {failed,{timetrap_timeout,{'$approx',1000}}}}},
+ {?eh,test_stats,{4,16,{0,6}}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc2}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc2,
+ {failed,{timetrap_timeout,{'$approx',500}}}}},
+ {?eh,test_stats,{4,17,{0,6}}},
+ {?eh,tc_start,{timetrap_8_SUITE,{end_per_group,pg0,[parallel]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{end_per_group,pg0,[parallel]},ok}}]},
+
+ {parallel,
+ [{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,pg1,[parallel]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{init_per_group,pg1,[parallel]},ok}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc0}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc0,
+ {failed,{timetrap_timeout,{'$approx',1000}}}}},
+ {?eh,test_stats,{4,18,{0,6}}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc2}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc2,
+ {failed,{timetrap_timeout,{'$approx',500}}}}},
+ {?eh,test_stats,{4,19,{0,6}}},
+ {?eh,tc_start,{timetrap_8_SUITE,{end_per_group,pg1,[parallel]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{end_per_group,pg1,[parallel]},ok}}]},
+
+ {parallel,
+ [{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,pg2,[parallel]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{init_per_group,pg2,[parallel]},ok}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc1}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc1,
+ {failed,{timetrap_timeout,{'$approx',1000}}}}},
+ {?eh,test_stats,{4,20,{0,6}}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc2}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc2,
+ {failed,{timetrap_timeout,{'$approx',500}}}}},
+ {?eh,test_stats,{4,21,{0,6}}},
+ {?eh,tc_start,{timetrap_8_SUITE,{end_per_group,pg2,[parallel]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{end_per_group,pg2,[parallel]},ok}}]},
+
+ {parallel,
+ [{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,pg3,[parallel]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{init_per_group,pg3,[parallel]},ok}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc4}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc4,
+ {failed,{timetrap_timeout,{'$approx',2000}}}}},
+ {?eh,test_stats,{4,22,{0,6}}},
+ {parallel,
+ [{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,pg1,[parallel]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{init_per_group,pg1,[parallel]},
+ ok}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc0}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc0,
+ {failed,{timetrap_timeout,{'$approx',1000}}}}},
+ {?eh,test_stats,{4,23,{0,6}}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc2}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc2,
+ {failed,{timetrap_timeout,{'$approx',500}}}}},
+ {?eh,test_stats,{4,24,{0,6}}},
+ {?eh,tc_start,{timetrap_8_SUITE,{end_per_group,pg1,[parallel]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{end_per_group,pg1,[parallel]},
+ ok}}]},
+ {parallel,
+ [{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,pg2,[parallel]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{init_per_group,pg2,[parallel]},
+ ok}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc1}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc1,
+ {failed,{timetrap_timeout,{'$approx',1000}}}}},
+ {?eh,test_stats,{4,25,{0,6}}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc2}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc2,
+ {failed,{timetrap_timeout,{'$approx',500}}}}},
+ {?eh,test_stats,{4,26,{0,6}}},
+ {?eh,tc_start,{timetrap_8_SUITE,{end_per_group,pg2,[parallel]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{end_per_group,pg2,[parallel]},
+ ok}}]},
+ {?eh,tc_start,{timetrap_8_SUITE,{end_per_group,pg3,[parallel]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{end_per_group,pg3,[parallel]},ok}}]},
+
+ {parallel,
+ [{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,pg4,[parallel]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{init_per_group,pg4,[parallel]},
+ {user_timetrap_error,{kaboom,'_'}}}},
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,tc0,
+ {failed,{timetrap_8_SUITE,init_per_group,
+ {user_timetrap_error,{kaboom,'_'}}}}}},
+ {?eh,test_stats,{4,26,{0,7}}},
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,tc2,
+ {failed,{timetrap_8_SUITE,init_per_group,
+ {user_timetrap_error,{kaboom,'_'}}}}}},
+ {?eh,test_stats,{4,26,{0,8}}},
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,end_per_group,
+ {failed,{timetrap_8_SUITE,init_per_group,
+ {user_timetrap_error,{kaboom,'_'}}}}}}]},
+
+ {parallel,
+ [{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,pg5,[parallel]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{init_per_group,pg5,[parallel]},
+ {user_timetrap_error,{kaboom,'_'}}}},
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,tc0,
+ {failed,{timetrap_8_SUITE,init_per_group,
+ {user_timetrap_error,{kaboom,'_'}}}}}},
+ {?eh,test_stats,{4,26,{0,9}}},
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,tc2,
+ {failed,{timetrap_8_SUITE,init_per_group,
+ {user_timetrap_error,{kaboom,'_'}}}}}},
+ {?eh,test_stats,{4,26,{0,10}}},
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,end_per_group,
+ {failed,{timetrap_8_SUITE,init_per_group,
+ {user_timetrap_error,{kaboom,'_'}}}}}}]},
+
+ {parallel,
+ [{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,pg6,[parallel]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{init_per_group,pg6,[parallel]},
+ {failed,{timetrap_timeout,{'$approx',500}}}}},
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,tc0,
+ {failed,{timetrap_8_SUITE,init_per_group,
+ {timetrap_timeout,'_'}}}}},
+ {?eh,test_stats,{4,26,{0,11}}},
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,tc2,
+ {failed,{timetrap_8_SUITE,init_per_group,
+ {timetrap_timeout,'_'}}}}},
+ {?eh,test_stats,{4,26,{0,12}}},
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,end_per_group,
+ {failed,{timetrap_8_SUITE,init_per_group,
+ {timetrap_timeout,'_'}}}}}]},
+
+ {parallel,
+ [{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,pg7,[parallel]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{init_per_group,pg7,[parallel]},ok}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc5}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc5,ok}},
+ {?eh,test_stats,{5,26,{0,12}}},
+ {?eh,tc_start,{timetrap_8_SUITE,{end_per_group,pg7,[parallel]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{end_per_group,pg7,[parallel]},
+ {user_timetrap_error,{kaboom,'_'}}}}]},
+
+ {parallel,
+ [{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,pg8,[parallel]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{init_per_group,pg8,[parallel]},ok}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc5}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc5,ok}},
+ {?eh,test_stats,{6,26,{0,12}}},
+ {?eh,tc_start,{timetrap_8_SUITE,{end_per_group,pg8,[parallel]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{end_per_group,pg8,[parallel]},
+ {failed,{timetrap_timeout,{'$approx',500}}}}}]},
+
+ {parallel,
+ [{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,pg9,[parallel]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{init_per_group,pg9,[parallel]},ok}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc5}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc5,ok}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc0}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc0,
+ {user_timetrap_error,{kaboom,'_'}}}},
+ %% Due to parallelism only checking final test stat in group
+ {?eh,test_stats,{7,27,{0,12}}},
+ {?eh,tc_start,{timetrap_8_SUITE,{end_per_group,pg9,[parallel]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{end_per_group,pg9,[parallel]},ok}}]},
+
+ {parallel,
+ [{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,pg10,[parallel]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{init_per_group,pg10,[parallel]},ok}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc0}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc0,
+ {user_timetrap_error,{kaboom,'_'}}}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc5}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc5,ok}},
+ %% Due to parallelism only checking final test stat in group
+ {?eh,test_stats,{8,28,{0,12}}},
+ {?eh,tc_start,{timetrap_8_SUITE,{end_per_group,pg10,[parallel]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{end_per_group,pg10,[parallel]},ok}}]},
+
+ {parallel,
+ [{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,pg11,[parallel]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{init_per_group,pg11,[parallel]},ok}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc3}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc3,
+ {failed,{timetrap_timeout,{'$approx',4000}}}}},
+ {?eh,test_stats,{8,29,{0,12}}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc2}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc2,
+ {failed,{timetrap_timeout,{'$approx',500}}}}},
+ {?eh,test_stats,{8,30,{0,12}}},
+ {?eh,tc_start,{timetrap_8_SUITE,{end_per_group,pg11,[parallel]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{end_per_group,pg11,[parallel]},ok}}]},
+
+ [{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,sg1,[sequence]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{init_per_group,sg1,[sequence]},ok}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc5}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc5,ok}},
+ {?eh,test_stats,{9,30,{0,12}}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc0}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc0,
+ {user_timetrap_error,{kaboom,'_'}}}},
+ {?eh,test_stats,{9,31,{0,12}}},
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,tc1,
+ {failed,{timetrap_8_SUITE,tc0}}}},
+ {?eh,test_stats,{9,31,{0,13}}},
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,tc2,
+ {failed,{timetrap_8_SUITE,tc0}}}},
+ {?eh,test_stats,{9,31,{0,14}}},
+ {?eh,tc_start,{timetrap_8_SUITE,{end_per_group,sg1,[sequence]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{end_per_group,sg1,[sequence]},ok}}],
+
+ [{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,sg2,[sequence]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{init_per_group,sg2,[sequence]},ok}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc5}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc5,ok}},
+ {?eh,test_stats,{10,31,{0,14}}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc0}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc0,
+ {failed,{timetrap_timeout,{'$approx',1000}}}}},
+ {?eh,test_stats,{10,32,{0,14}}},
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,tc1,
+ {failed,{timetrap_8_SUITE,tc0}}}},
+ {?eh,test_stats,{10,32,{0,15}}},
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,tc2,
+ {failed,{timetrap_8_SUITE,tc0}}}},
+ {?eh,test_stats,{10,32,{0,16}}},
+ {?eh,tc_start,{timetrap_8_SUITE,{end_per_group,sg2,[sequence]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{end_per_group,sg2,[sequence]},ok}}],
+
+ {?eh,tc_start,{timetrap_8_SUITE,end_per_suite}},
+ {?eh,tc_done,{timetrap_8_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}
+ ];
+
test_events(misc_errors) ->
[
{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
- {?eh,start_info,{1,1,7}},
+ {?eh,start_info,{1,1,9}},
{?eh,tc_start,{misc_error_1_SUITE,ct_fail_1}},
{?eh,tc_done,{misc_error_1_SUITE,ct_fail_1,
{failed,{error,{test_case_failed,{error,this_is_expected}}}}}},
@@ -1002,7 +1431,12 @@ test_events(misc_errors) ->
{?eh,tc_start,{misc_error_1_SUITE,killed_by_signal_2}},
{?eh,tc_done,{misc_error_1_SUITE,killed_by_signal_2,
{failed,testcase_aborted_or_killed}}},
- {?eh,test_stats,{0,7,{0,0}}},
+ {parallel,
+ [{?eh,tc_start,{misc_error_1_SUITE,p1}},
+ {?eh,tc_done,{misc_error_1_SUITE,p1,ok}},
+ {?eh,tc_start,{misc_error_1_SUITE,p2}},
+ {?eh,tc_done,{misc_error_1_SUITE,p2,ok}}]},
+ {?eh,test_stats,{2,7,{0,0}}},
{?eh,test_done,{'DEF','STOP_TIME'}},
{?eh,stop_logging,[]}
].
diff --git a/lib/common_test/test/ct_error_SUITE_data/error/test/misc_error_1_SUITE.erl b/lib/common_test/test/ct_error_SUITE_data/error/test/misc_error_1_SUITE.erl
index 99c3ed05ec..61f3fa7e59 100644
--- a/lib/common_test/test/ct_error_SUITE_data/error/test/misc_error_1_SUITE.erl
+++ b/lib/common_test/test/ct_error_SUITE_data/error/test/misc_error_1_SUITE.erl
@@ -96,7 +96,7 @@ end_per_testcase(_TestCase, _Config) ->
%% N = integer() | forever
%%--------------------------------------------------------------------
groups() ->
- [].
+ [{p,[parallel],[p1,p2]}].
%%--------------------------------------------------------------------
%% Function: all() -> GroupsAndTestCases | {skip,Reason}
@@ -107,7 +107,8 @@ groups() ->
%%--------------------------------------------------------------------
all() ->
[ct_fail_1, ct_fail_2, ct_fail_3, ts_fail_1, ts_fail_2,
- killed_by_signal_1, killed_by_signal_2].
+ killed_by_signal_1, killed_by_signal_2,
+ {group,p}].
ct_fail_1(_) ->
ct:fail({error,this_is_expected}),
@@ -152,3 +153,10 @@ killed_by_signal_2(_) ->
end),
ct:sleep(1000),
exit(this_should_not_be_seen).
+
+p1(_) ->
+ {error,parallel_group} = ct:abort_current_testcase(aborted),
+ ok.
+
+p2(_) ->
+ receive after 1000 -> ok end.
diff --git a/lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_8_SUITE.erl b/lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_8_SUITE.erl
new file mode 100644
index 0000000000..ff138f38b5
--- /dev/null
+++ b/lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_8_SUITE.erl
@@ -0,0 +1,258 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, 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_8_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+-define(TO, 4).
+
+%%--------------------------------------------------------------------
+%% Function: suite() -> Info
+%% Info = [tuple()]
+%%--------------------------------------------------------------------
+suite() ->
+ [{timetrap,{timetrap_utils,timetrap_val,[{seconds,?TO}]}}].
+
+%%--------------------------------------------------------------------
+%% 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(G6, Config) when G6==g6; G6==pg6 ->
+ ct:sleep({seconds,1}),
+ Config;
+init_per_group(_GroupName, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% Function: end_per_group(GroupName, Config0) ->
+%% void() | {save_config,Config1}
+%% GroupName = atom()
+%% Config0 = Config1 = [tuple()]
+%%--------------------------------------------------------------------
+end_per_group(G7or8, _Config) when G7or8==g7; G7or8==pg7; G7or8==g8; G7or8==pg8 ->
+ ct:sleep({seconds,5}),
+ ok;
+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(_, _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() ->
+ [
+ {g0,[],[tc0,tc2]}, % group override suite and tc overrides group
+ {g1,[],[tc0,tc2]}, % group override suite and tc overrides group
+ {g2,[],[tc1,tc2]}, % tc override group
+ {g3,[],[tc4,{group,g1},{group,g2}]}, % subgroup override group
+ {g4,[],[tc0,tc2]}, % exit during init_per_group
+ {g5,[],[tc0,tc2]}, % exit during init_per_group
+ {g6,[],[tc0,tc2]}, % timeout during init_per_group
+ {g7,[],[tc5]}, % exit during end_per_group
+ {g8,[],[tc5]}, % timeout during end_per_group
+ {g9,[],[tc5,tc0]}, % exit during testcase
+ {g10,[],[tc0,tc5]}, % exit during testcase
+ {g11,[],[tc3,tc2]}, % suite is valid if nothing else is specified
+ {pg0,[parallel],[tc0,tc2]}, % group override suite and tc overrides group
+ {pg1,[parallel],[tc0,tc2]}, % group override suite and tc overrides group
+ {pg2,[parallel],[tc1,tc2]}, % tc override group
+ {pg3,[parallel],[tc4,{group,pg1},{group,pg2}]}, % subgroup override group
+ {pg4,[parallel],[tc0,tc2]}, % exit during init_per_group
+ {pg5,[parallel],[tc0,tc2]}, % exit during init_per_group
+ {pg6,[parallel],[tc0,tc2]}, % timeout during init_per_group
+ {pg7,[parallel],[tc5]}, % exit during end_per_group
+ {pg8,[parallel],[tc5]}, % timeout during end_per_group
+ {pg9,[parallel],[tc5,tc0]}, % exit during testcase
+ {pg10,[parallel],[tc0,tc5]},% exit during testcase
+ {pg11,[parallel],[tc3,tc2]},% suite is valid if nothing else is specified
+ {sg1,[sequence],[tc5,tc0,tc1,tc2]}, % exit during sequencial testcase
+ {sg2,[sequence],[tc5,tc0,tc1,tc2]}].% timeout during sequencial testcase
+
+group(g0) ->
+ [{timetrap,{timetrap_utils,timetrap_val,[{seconds,1}]}}];
+group(g1) ->
+ [{timetrap,fun() -> timetrap_utils:timetrap_val(1000) end}];
+group(g2) ->
+ [{timetrap,fun() -> timetrap_utils:timetrap_val(3000) end}];
+group(g3) ->
+ [{timetrap,fun() -> timetrap_utils:timetrap_val(2000) end}];
+group(g4) ->
+ [{timetrap,{timetrap_utils,timetrap_exit,[kaboom]}}];
+group(g5) ->
+ [{timetrap,fun() -> exit(kaboom) end}];
+group(g6) ->
+ [{timetrap,{timetrap_utils,timetrap_val,[500]}}];
+group(g7) ->
+ [{timetrap,fun() -> ct:sleep(1000),exit(kaboom) end}];
+group(g8) ->
+ [{timetrap,{timetrap_utils,timetrap_val,[500]}}];
+group(g9) ->
+ [{timetrap,fun() -> ct:sleep(1000),exit(kaboom) end}];
+group(g10) ->
+ [{timetrap,fun() -> ct:sleep(1000),exit(kaboom) end}];
+group(g11) ->
+ [];
+group(pg0) ->
+ [{timetrap,{timetrap_utils,timetrap_val,[{seconds,1}]}}];
+group(pg1) ->
+ [{timetrap,fun() -> timetrap_utils:timetrap_val(1000) end}];
+group(pg2) ->
+ [{timetrap,fun() -> timetrap_utils:timetrap_val(3000) end}];
+group(pg3) ->
+ [{timetrap,fun() -> timetrap_utils:timetrap_val(2000) end}];
+group(pg4) ->
+ [{timetrap,{timetrap_utils,timetrap_exit,[kaboom]}}];
+group(pg5) ->
+ [{timetrap,fun() -> exit(kaboom) end}];
+group(pg6) ->
+ [{timetrap,{timetrap_utils,timetrap_val,[500]}}];
+group(pg7) ->
+ [{timetrap,fun() -> ct:sleep(1000),exit(kaboom) end}];
+group(pg8) ->
+ [{timetrap,{timetrap_utils,timetrap_val,[500]}}];
+group(pg9) ->
+ [{timetrap,fun() -> ct:sleep(1000),exit(kaboom) end}];
+group(pg10) ->
+ [{timetrap,fun() -> ct:sleep(1000),exit(kaboom) end}];
+group(pg11) ->
+ [];
+group(sg1) ->
+ [{timetrap,fun() -> ct:sleep(1000),exit(kaboom) end}];
+group(sg2) ->
+ [{timetrap,{timetrap_utils,timetrap_val,[{seconds,1}]}}].
+
+
+%%--------------------------------------------------------------------
+%% Function: all() -> GroupsAndTestCases | {skip,Reason}
+%% GroupsAndTestCases = [{group,GroupName} | TestCase]
+%% GroupName = atom()
+%% TestCase = atom()
+%% Reason = term()
+%%--------------------------------------------------------------------
+all() ->
+ [
+ {group,g0},
+ {group,g1},
+ {group,g2},
+ {group,g3},
+ {group,g4},
+ {group,g5},
+ {group,g6},
+ {group,g7},
+ {group,g8},
+ {group,g9},
+ {group,g10},
+ {group,g11},
+ {group,pg0},
+ {group,pg1},
+ {group,pg2},
+ {group,pg3},
+ {group,pg4},
+ {group,pg5},
+ {group,pg6},
+ {group,pg7},
+ {group,pg8},
+ {group,pg9},
+ {group,pg10},
+ {group,pg11},
+ {group,sg1},
+ {group,sg2}].
+
+
+
+tc0(_) ->
+ ct:comment("TO set by group"),
+ ct:sleep({seconds,5}),
+ ok.
+
+tc1() ->
+ [{timetrap,{timetrap_utils,timetrap_val,[1000]}}].
+tc1(_) ->
+ ct:comment("TO after 1 sec"),
+ ct:sleep({seconds,2}),
+ ok.
+
+tc2() ->
+ [{timetrap,fun() -> timetrap_utils:timetrap_val(500) end}].
+tc2(_) ->
+ ct:comment("TO after 0.5 sec"),
+ ct:sleep({seconds,2}),
+ ok.
+
+tc3(_) ->
+ ct:comment(io_lib:format("TO after ~w sec", [?TO])),
+ ct:sleep({seconds,5}),
+ ok.
+
+tc4(_) ->
+ ct:comment("TO set by group"),
+ ct:sleep({seconds,5}),
+ ok.
+
+tc5(_) ->
+ ct:comment("No TO in this testcase, maybe later"),
+ ok.
diff --git a/lib/common_test/test/ct_group_leader_SUITE.erl b/lib/common_test/test/ct_group_leader_SUITE.erl
new file mode 100644
index 0000000000..cde3061d6a
--- /dev/null
+++ b/lib/common_test/test/ct_group_leader_SUITE.erl
@@ -0,0 +1,181 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, 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_system_error_SUITE
+%%%
+%%% Description:
+%%%
+%%% Test the group leader functionality in the test_server application.
+%%%-------------------------------------------------------------------
+-module(ct_group_leader_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() ->
+ [
+ basic
+ ].
+
+%%--------------------------------------------------------------------
+%% TEST CASES
+%%--------------------------------------------------------------------
+
+%%%-----------------------------------------------------------------
+%%%
+basic(Config) ->
+ TC = basic,
+ DataDir = ?config(data_dir, Config),
+ Suite = filename:join(DataDir, "group_leader_SUITE"),
+ {Opts,ERPid} = setup([{suite,Suite},{label,TC}], Config),
+ SuiteLog = execute(TC, Opts, ERPid, Config),
+ {ok,Data} = file:read_file(SuiteLog),
+ Lines = binary:split(Data, <<"\n">>, [global]),
+ {ok,RE} = re:compile("(\\S+):(\\S+)$"),
+ Cases0 = [begin
+ {match,[M,F]} = re:run(Case, RE, [{capture,all_but_first,list}]),
+ {list_to_atom(M),list_to_atom(F)}
+ end || <<"=case ",Case/binary>> <- Lines],
+ Cases = [MF || {_,F}=MF <- Cases0,
+ F =/= init_per_suite,
+ F =/= end_per_suite,
+ F =/= init_per_group,
+ F =/= end_per_group],
+ io:format("~p\n", [Cases]),
+ [] = verify_cases(events_to_check(TC), Cases, false),
+ ok.
+
+verify_cases([{parallel,P}|Ts], Cases0, Par) ->
+ Cases = verify_cases(P, Cases0, true),
+ verify_cases(Ts, Cases, Par);
+verify_cases([{?eh,tc_done,{M,F,_}}|Ts], Cases0, false) ->
+ [{M,F}|Cases] = Cases0,
+ verify_cases(Ts, Cases, false);
+verify_cases([{?eh,tc_done,{M,F,_}}|Ts], Cases0, true) ->
+ case lists:member({M,F}, Cases0) of
+ true ->
+ Cases = Cases0 -- [{M,F}],
+ verify_cases(Ts, Cases, true);
+ false ->
+ io:format("~p not found\n", [{M,F}]),
+ ?t:fail()
+ end;
+verify_cases([{?eh,_,_}|Ts], Cases, Par) ->
+ verify_cases(Ts, Cases, Par);
+verify_cases([], Cases, _) ->
+ Cases;
+verify_cases([List|Ts], Cases0, Par) when is_list(List) ->
+ Cases = verify_cases(List, Cases0, false),
+ verify_cases(Ts, Cases, Par).
+
+%%%-----------------------------------------------------------------
+%%% 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}.
+
+execute(Name, Opts, ERPid, Config) ->
+ ok = ct_test_support:run(Opts, Config),
+ Events = ct_test_support:get_events(ERPid, Config),
+
+ ct_test_support:log_events(Name,
+ reformat(Events, ?eh),
+ ?config(priv_dir, Config),
+ Opts),
+
+ TestEvents = events_to_check(Name),
+ ok = ct_test_support:verify_events(TestEvents, Events, Config),
+ {event,tc_logfile,_,{_,File}} =
+ lists:keyfind(tc_logfile, 2, [Ev || {?eh,Ev} <- Events]),
+ LogDir = filename:dirname(File),
+ filename:join(LogDir, "suite.log").
+
+reformat(Events, EH) ->
+ ct_test_support:reformat(Events, EH).
+
+%%%-----------------------------------------------------------------
+%%% TEST EVENTS
+%%%-----------------------------------------------------------------
+
+events_to_check(_Test) ->
+ [{?eh,tc_done,{group_leader_SUITE,tc1,ok}},
+ {parallel,[{?eh,tc_start,{group_leader_SUITE,p1}},
+ {?eh,tc_done,{group_leader_SUITE,p1,ok}},
+ {?eh,tc_start,{group_leader_SUITE,p2}},
+ {?eh,tc_done,{group_leader_SUITE,p2,ok}}]},
+ {?eh,tc_done,{group_leader_SUITE,p_restart_my_io_server,ok}},
+ {?eh,tc_done,{group_leader_SUITE,p3,ok}},
+ {parallel,[
+ {?eh,tc_start,{group_leader_SUITE,p10}},
+ {?eh,tc_start,{group_leader_SUITE,p11}},
+ {?eh,tc_done,{group_leader_SUITE,p10,ok}},
+ {?eh,tc_done,{group_leader_SUITE,p11,ok}},
+ [{?eh,tc_done,{group_leader_SUITE,s1,ok}},
+ {?eh,tc_done,{group_leader_SUITE,s2,ok}},
+ {?eh,tc_done,{group_leader_SUITE,s3,ok}}],
+ {?eh,tc_start,{group_leader_SUITE,p12}},
+ {?eh,tc_done,{group_leader_SUITE,p12,ok}},
+ [{?eh,tc_done,{group_leader_SUITE,s4,ok}},
+ {?eh,tc_done,{group_leader_SUITE,s5,ok}}],
+ {?eh,tc_start,{group_leader_SUITE,p13}},
+ {?eh,tc_done,{group_leader_SUITE,p13,ok}} ]},
+ {?eh,tc_done,{group_leader_SUITE,cap1,ok}},
+ {?eh,tc_done,{group_leader_SUITE,cap2,ok}},
+ {parallel,[{?eh,tc_start,{group_leader_SUITE,cap1}},
+ {?eh,tc_done,{group_leader_SUITE,cap1,ok}},
+ {?eh,tc_start,{group_leader_SUITE,cap2}},
+ {?eh,tc_done,{group_leader_SUITE,cap2,ok}}]},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}
+ ].
diff --git a/lib/common_test/test/ct_group_leader_SUITE_data/group_leader_SUITE.erl b/lib/common_test/test/ct_group_leader_SUITE_data/group_leader_SUITE.erl
new file mode 100644
index 0000000000..3f1844b4ae
--- /dev/null
+++ b/lib/common_test/test/ct_group_leader_SUITE_data/group_leader_SUITE.erl
@@ -0,0 +1,252 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, 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_leader_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+%%--------------------------------------------------------------------
+%% @spec suite() -> Info
+%% Info = [tuple()]
+%% @end
+%%--------------------------------------------------------------------
+suite() ->
+ [{timetrap,{seconds,10}}].
+
+%%--------------------------------------------------------------------
+%% @spec init_per_suite(Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%% @end
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ start_my_io_server(),
+ Config.
+
+%%--------------------------------------------------------------------
+%% @spec end_per_suite(Config0) -> void() | {save_config,Config1}
+%% Config0 = Config1 = [tuple()]
+%% @end
+%%--------------------------------------------------------------------
+end_per_suite(_Config) ->
+ my_io_server ! die,
+ ok.
+
+%%--------------------------------------------------------------------
+%% @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(_GroupName, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% @spec end_per_group(GroupName, Config0) ->
+%% void() | {save_config,Config1}
+%% GroupName = atom()
+%% Config0 = Config1 = [tuple()]
+%% @end
+%%--------------------------------------------------------------------
+end_per_group(_GroupName, _Config) ->
+ 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() ->
+ [{p,[parallel],[p1,p2]},
+ {p_restart,[parallel],[p_restart_my_io_server]},
+ {seq,[],[s1,s2,s3]},
+ {seq2,[],[s4,s5]},
+ {seq_in_par,[parallel],[p10,p11,{group,seq},p12,{group,seq2},p13]},
+ {capture_io,[parallel],[cap1,cap2]}].
+
+%%--------------------------------------------------------------------
+%% @spec all() -> GroupsAndTestCases | {skip,Reason}
+%% GroupsAndTestCases = [{group,GroupName} | TestCase]
+%% GroupName = atom()
+%% TestCase = atom()
+%% Reason = term()
+%% @end
+%%--------------------------------------------------------------------
+all() ->
+ [tc1,{group,p},{group,p_restart},p3,
+ {group,seq_in_par},
+ cap1,cap2,
+ {group,capture_io}].
+
+tc1(_C) ->
+ ok.
+
+p1(_) ->
+ %% OTP-10101:
+ %%
+ %% External apps/processes started by init_per_suite (common operation),
+ %% will inherit the group leader of the init_per_suite process, i.e. the
+ %% test_server test case control process (executing run_test_case_msgloop/7).
+ %% If, later, a parallel test case triggers the external app to print with
+ %% e.g. io:format() (also common operation), the calling process will hang!
+ %% The reason for this is that a parallel test case has a dedicated IO
+ %% server process, other than the central test case control process. The
+ %% latter process is not executing run_test_case_msgloop/7 and will not
+ %% respond to IO messages. The process is still group leader for the
+ %% external app, however, which is wrong. It's the IO process for the
+ %% parallel test case that should be group leader - but only for the
+ %% particular invokation, since other parallel test cases could be
+ %% invoking the external app too.
+ print("hej\n").
+
+p2(_) ->
+ print("hopp\n").
+
+p_restart_my_io_server(_) ->
+ %% Restart the IO server and change its group leader. This used
+ %% to set to the group leader to a process that would soon die.
+ Ref = erlang:monitor(process, my_io_server),
+ my_io_server ! die,
+ receive
+ {'DOWN',Ref,_,_,_} ->
+ start_my_io_server()
+ end.
+
+p3(_) ->
+ %% OTP-10125. This would crash since the group leader process
+ %% for the my_io_server had died.
+ print("hoppsan\n").
+
+print(String) ->
+ my_io_server ! {print,self(),String},
+ receive
+ {printed,String} ->
+ ok
+ end.
+
+start_my_io_server() ->
+ Parent = self(),
+ Pid = spawn(fun() -> my_io_server(Parent) end),
+ receive
+ {Pid,started} ->
+ io:format("~p\n", [process_info(Pid)]),
+ ok
+ end.
+
+my_io_server(Parent) ->
+ register(my_io_server, self()),
+ Parent ! {self(),started},
+ my_io_server_loop().
+
+my_io_server_loop() ->
+ receive
+ {print,From,String} ->
+ io:put_chars(String),
+ From ! {printed,String},
+ my_io_server_loop();
+ die ->
+ ok
+ end.
+
+p10(_) ->
+ receive after 1 -> ok end.
+
+p11(_) ->
+ ok.
+
+p12(_) ->
+ ok.
+
+p13(_) ->
+ ok.
+
+s1(_) ->
+ ok.
+
+s2(_) ->
+ ok.
+
+s3(_) ->
+ ok.
+
+s4(_) ->
+ ok.
+
+s5(_) ->
+ ok.
+
+cap1(_) ->
+ ct:capture_start(),
+ IO = gen_io(cap1, 10, []),
+ ct:capture_stop(),
+ IO = ct:capture_get(),
+ ok.
+
+cap2(_) ->
+ ct:capture_start(),
+ {Pid,Ref} = spawn_monitor(fun() ->
+ exit(gen_io(cap2, 42, []))
+ end),
+ receive
+ {'DOWN',Ref,process,Pid,IO} ->
+ ct:capture_stop(),
+ IO = ct:capture_get(),
+ ok
+ end.
+
+gen_io(_, 0, Acc) ->
+ lists:reverse(Acc);
+gen_io(Label, N, Acc) ->
+ S = lists:flatten(io_lib:format("~s: ~p\n", [Label,N])),
+ io:put_chars(S),
+ gen_io(Label, N-1, [S|Acc]).
diff --git a/lib/common_test/test/ct_groups_search_SUITE.erl b/lib/common_test/test/ct_groups_search_SUITE.erl
new file mode 100644
index 0000000000..6b1c1f4634
--- /dev/null
+++ b/lib/common_test/test/ct_groups_search_SUITE.erl
@@ -0,0 +1,1245 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2009-2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, 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:
+%%%
+%%% Description:
+%%%
+%%%
+%%% The suites used for the test are located in the data directory.
+%%%
+%%% The group(s) and case(s) are specified according to this:
+%%%
+%%% Tests = ct_groups:find_groups(Mod, GroupPaths, TestCases, GroupDef)
+%%%
+%%% GroupPaths = GroupPath | [GroupPath]
+%%% GroupPath = atom() | [atom()]
+%%%
+%%% CT will find all paths that include GroupPath. GroupPath can be a
+%%% single group, or a list of groups along the path to TestCases.
+%%% If GroupPath is the latter, the last group in the list must be
+%%% the "terminating" group in the path, or it will be impossible to
+%%% execute test cases in higher level groups *only*, as in this case:
+%%% groups() -> [{g1,[],[tc1,{g2,[],[tc2]}]}].
+%%% Compare: find_groups(x, g1, all, groups()), and
+%%% find_groups(x, [[g1]], all, groups())
+%%%
+%%% Some examples:
+%%%
+%%% GroupPaths = g1, means find all paths with g1 included
+%%% GroupPaths = [g1], -''-
+%%% GroupPaths = [g1,g2], search twice - once for g1 and once for g2
+%%% GroupPaths = [[g1,g2]], find cases under group g1 and sub group g2
+%%% GroupPaths = [[g1,g2],[g1,g3]], find cases for g1-g2 AND g1-g3
+%%%
+%%% TestCases = all | atom() | [atom()]
+%%%
+%%%-------------------------------------------------------------------
+
+-module(ct_groups_search_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("common_test/src/ct_util.hrl").
+
+
+-define(eh, ct_test_support_eh).
+
+-define(M1, groups_search_dummy_1_SUITE).
+-define(M2, groups_search_dummy_2_SUITE).
+
+%%--------------------------------------------------------------------
+%% TEST SERVER CALLBACK FUNCTIONS
+%%--------------------------------------------------------------------
+
+init_per_suite(Config) ->
+ DataDir = proplists:get_value(data_dir, Config),
+ code:add_patha(DataDir),
+ M1Erl = filename:join(DataDir, atom_to_list(?M1)++".erl"),
+ M2Erl = filename:join(DataDir, atom_to_list(?M2)++".erl"),
+ {ok,?M1} = compile:file(M1Erl, [{outdir,DataDir}]),
+ {ok,?M2} = compile:file(M2Erl, [{outdir,DataDir}]),
+ {module,?M1} = code:load_file(?M1),
+ {module,?M2} = code:load_file(?M2),
+
+ 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]}].
+
+groups() ->
+ [
+ {find_groups,[],[all_groups,
+ testcases_in_all_groups,
+ all_in_top_group1,
+ all_in_top_group2,
+ all_in_sub_group1,
+ all_in_sub_group2,
+ testcase_in_top_group1,
+ testcase_in_top_group2,
+ testcase_in_sub_group1,
+ testcase_in_sub_group2,
+ testcase_in_top_groups1,
+ testcase_in_top_groups2,
+ testcase_in_top_groups3,
+ testcase_in_top_groups4,
+ testcase_in_top_groups5,
+ testcase_in_top_groups6,
+ testcase_in_top_groups7,
+ testcase_in_sub_groups1,
+ testcase_in_sub_groups2,
+ testcase_in_sub_groups3,
+ testcase_in_sub_groups4,
+ testcase_in_sub_groups5,
+ testcase_in_sub_groups6,
+ testcase_in_sub_groups7,
+ testcase_in_sub_groups8,
+ testcase_in_sub_groups9,
+ testcase_in_sub_groups10,
+ testcase_in_sub_groups11,
+ testcase_in_sub_groups12,
+ testcase_in_sub_groups13,
+ bad_testcase_in_sub_groups1]},
+
+ {run_groups,[sequence],[run_groups_with_options,
+ run_groups_with_testspec]}
+ ].
+
+all() ->
+ [{group,find_groups,[parallel]},
+ {group,run_groups}].
+
+
+
+%%--------------------------------------------------------------------
+%% TEST CASES CHECKING RETURN VALUE ONLY
+%%--------------------------------------------------------------------
+
+all_groups(_) ->
+ GPath = all, TCs = all,
+
+ Found = ct_groups:find_groups(?M1, GPath, TCs, groups1()),
+
+ Top1 = ct_groups:find_groups(?M1, top1, TCs, groups1()),
+ Top2 = ct_groups:find_groups(?M1, top2, TCs, groups1()),
+
+ All = Top1 ++ Top2 ++ [{conf,[{name,sub2}],
+ {?M1,init_per_group},
+ [{?M1,sub2_tc1},{?M1,sub2_tc2}],
+ {?M1,end_per_group}}],
+
+ All = Found,
+
+ {?M1,GPath,TCs,Top1++Top2}.
+
+%%%-----------------------------------------------------------------
+%%%
+testcases_in_all_groups(_) ->
+ GPath = all, TCs = [tc3,sub_tc2],
+
+ Found = ct_groups:find_groups(?M2, GPath, TCs, groups2()),
+
+ [Top1 =
+ {conf,[{name,top1}],{?M2,init_per_group},
+ [{?M2,tc3},
+ {conf,[{name,sub11}],
+ {?M2,init_per_group},[{?M2,tc3},{?M2,sub_tc2}],
+ {?M2,end_per_group}},
+ {conf,[{name,sub12}],
+ {?M2,init_per_group},
+ [{?M2,tc3},{?M2,sub_tc2},
+ {conf,[{name,sub121}],
+ {?M2,init_per_group},[{?M2,tc3},{?M2,sub_tc2}],
+ {?M2,end_per_group}}],
+ {?M2,end_per_group}}],
+ {?M2,end_per_group}},
+
+ Top2 =
+ {conf,[{name,top2}],{?M2,init_per_group},
+ [{?M2,tc3},
+ {conf,[{name,sub21}],
+ {?M2,init_per_group},
+ [{?M2,tc3},{?M2,sub_tc2},
+ {conf,[{name,sub2xx}],
+ {?M2,init_per_group},[{?M2,tc3},{?M2,sub_tc2}],
+ {?M2,end_per_group}}],
+ {?M2,end_per_group}},
+
+ {conf,[{name,sub22}],
+ {?M2,init_per_group},
+ [{?M2,tc3},{?M2,sub_tc2},
+ {conf,[{name,sub221}],
+ {?M2,init_per_group},[{?M2,tc3},{?M2,sub_tc2}],
+ {?M2,end_per_group}},
+ {conf,[{name,sub2xx}],
+ {?M2,init_per_group},[{?M2,tc3},{?M2,sub_tc2}],
+ {?M2,end_per_group}}],
+ {?M2,end_per_group}}],
+ {?M2,end_per_group}},
+
+ {conf,[{name,sub21}],
+ {?M2,init_per_group},
+ [{?M2,tc3},{?M2,sub_tc2},
+ {conf,[{name,sub2xx}],
+ {?M2,init_per_group},[{?M2,tc3},{?M2,sub_tc2}],{?M2,end_per_group}}],
+ {?M2,end_per_group}},
+
+ {conf,[{name,sub22}],
+ {?M2,init_per_group},
+ [{?M2,tc3},{?M2,sub_tc2},
+ {conf,[{name,sub221}],
+ {?M2,init_per_group},[{?M2,tc3},{?M2,sub_tc2}],{?M2,end_per_group}},
+ {conf,[{name,sub2xx}],
+ {?M2,init_per_group},[{?M2,tc3},{?M2,sub_tc2}],{?M2,end_per_group}}],
+ {?M2,end_per_group}},
+
+ {conf,[{name,sub221}],
+ {?M2,init_per_group},[{?M2,tc3},{?M2,sub_tc2}],{?M2,end_per_group}},
+
+ {conf,[{name,sub2xx}],
+ {?M2,init_per_group},[{?M2,tc3},{?M2,sub_tc2}],{?M2,end_per_group}}]
+
+ = Found,
+
+ {?M2,GPath,TCs,[Top1,Top2]}.
+
+%%%-----------------------------------------------------------------
+%%%
+all_in_top_group1(_) ->
+ GPath= top1, TCs = all,
+
+ Found = ct_groups:find_groups(?M1, GPath, TCs, groups1()),
+
+ [{conf,[{name,top1}],
+ {?M1,init_per_group},
+ [{?M1,top1_tc1},{?M1,top1_tc2},
+ {conf,[{name,sub1}],
+ {?M1,init_per_group},
+ [{?M1,sub1_tc1},{?M1,sub1_tc2}],
+ {?M1,end_per_group}}],
+ {?M1,end_per_group}}] = Found,
+
+ {?M1,GPath,TCs,Found}.
+
+%%%-----------------------------------------------------------------
+%%%
+all_in_top_group2(_) ->
+ GPath= top2, TCs = all,
+
+ Found = ct_groups:find_groups(?M1, GPath, TCs, groups1()),
+
+ [{conf,[{name,top2}],
+ {?M1,init_per_group},
+ [{conf,[{name,sub2}],
+ {?M1,init_per_group},
+ [{?M1,sub2_tc1},{?M1,sub2_tc2}],
+ {?M1,end_per_group}},
+ {?M1,top2_tc1},{?M1,top2_tc2}],
+ {?M1,end_per_group}}] = Found,
+
+ {?M1,GPath,TCs,Found}.
+
+%%%-----------------------------------------------------------------
+%%%
+all_in_sub_group1(_) ->
+ GPath = sub1, TCs = all,
+
+ Found = ct_groups:find_groups(?M1, GPath, TCs, groups1()),
+
+ [{conf,[{name,top1}],
+ {?M1,init_per_group},
+ [{conf,[{name,sub1}],
+ {?M1,init_per_group},
+ [{?M1,sub1_tc1},{?M1,sub1_tc2}],
+ {?M1,end_per_group}}],
+ {?M1,end_per_group}}] = Found,
+
+ {?M1,GPath,TCs,Found}.
+
+%%%-----------------------------------------------------------------
+%%%
+all_in_sub_group2(_) ->
+ GPath = sub2, TCs = all,
+
+ Found = ct_groups:find_groups(?M1, GPath, TCs, groups1()),
+
+ [Top2 =
+ {conf,[{name,top2}],
+ {?M1,init_per_group},
+ [{conf,[{name,sub2}],
+ {?M1,init_per_group},
+ [{?M1,sub2_tc1},{?M1,sub2_tc2}],
+ {?M1,end_per_group}}],
+ {?M1,end_per_group}},
+
+ {conf,[{name,sub2}],
+ {?M1,init_per_group},
+ [{?M1,sub2_tc1},{?M1,sub2_tc2}],
+ {?M1,end_per_group}}] = Found,
+
+ {?M1,GPath,TCs,Top2}.
+
+%%%-----------------------------------------------------------------
+%%%
+testcase_in_top_group1(_) ->
+ GPath = top1, TCs = [top1_tc2],
+
+ Found = ct_groups:find_groups(?M1, GPath, TCs, groups1()),
+
+ [{conf,[{name,top1}],
+ {?M1,init_per_group},
+ [{?M1,top1_tc2}],
+ {?M1,end_per_group}}] = Found,
+
+ {?M1,GPath,TCs,Found}.
+
+%%%-----------------------------------------------------------------
+%%%
+testcase_in_top_group2(_) ->
+ GPath = top2, TCs = [top2_tc2],
+
+ Found = ct_groups:find_groups(?M1, GPath, TCs, groups1()),
+
+ [{conf,[{name,top2}],
+ {?M1,init_per_group},
+ [{?M1,top2_tc2}],
+ {?M1,end_per_group}}] = Found,
+
+ {?M1,GPath,TCs,Found}.
+
+%%%-----------------------------------------------------------------
+%%%
+testcase_in_sub_group1(_) ->
+ GPath = sub1, TCs = [sub1_tc2],
+
+ Found = ct_groups:find_groups(?M1, GPath, TCs, groups1()),
+
+ [{conf,[{name,top1}],
+ {?M1,init_per_group},
+ [{conf,[{name,sub1}],
+ {?M1,init_per_group},
+ [{?M1,sub1_tc2}],
+ {?M1,end_per_group}}],
+ {?M1,end_per_group}}] = Found,
+
+ {?M1,GPath,TCs,Found}.
+
+%%%-----------------------------------------------------------------
+%%%
+testcase_in_sub_group2(_) ->
+ GPath = sub2, TCs = [sub2_tc2],
+
+ Found = ct_groups:find_groups(?M1, GPath, TCs, groups1()),
+
+ [Top2 =
+ {conf,[{name,top2}],
+ {?M1,init_per_group},
+ [{conf,[{name,sub2}],
+ {?M1,init_per_group},
+ [{?M1,sub2_tc2}],
+ {?M1,end_per_group}}],
+ {?M1,end_per_group}},
+
+ {conf,[{name,sub2}],
+ {?M1,init_per_group},
+ [{?M1,sub2_tc2}],
+ {?M1,end_per_group}}] = Found,
+
+ {?M1,GPath,TCs,Top2}.
+
+%%%-----------------------------------------------------------------
+%%%
+testcase_in_top_groups1(_) ->
+ GPath = [top1,top2], TCs = all,
+
+ Found = ct_groups:find_groups(?M2, GPath, TCs, groups2()),
+
+ [{conf,[{name,top1}],
+ {?M2,init_per_group},
+ [{?M2,top1_tc1},{?M2,top_tc2},{?M2,tc3},
+ {conf,[{name,sub11}],
+ {?M2,init_per_group},
+ [{?M2,sub11_tc1},{?M2,sub_tc2},{?M2,tc3}],
+ {?M2,end_per_group}},
+ {conf,[{name,sub12}],
+ {?M2,init_per_group},
+ [{?M2,sub12_tc1},{?M2,sub_tc2},{?M2,tc3},
+ {conf,[{name,sub121}],
+ {?M2,init_per_group},
+ [{?M2,sub121_tc1},{?M2,sub_tc2},{?M2,tc3}],
+ {?M2,end_per_group}}],
+ {?M2,end_per_group}}],
+ {?M2,end_per_group}},
+
+ {conf,[{name,top2}],
+ {?M2,init_per_group},
+ [{conf,[{name,sub21}],
+ {?M2,init_per_group},
+ [{?M2,sub21_tc1},{?M2,sub_tc2},{?M2,tc3},
+ {conf,[{name,sub2xx}],
+ {?M2,init_per_group},
+ [{?M2,sub2xx_tc1},{?M2,sub_tc2},{?M2,tc3}],
+ {?M2,end_per_group}}],
+ {?M2,end_per_group}},
+ {?M2,top2_tc1},{?M2,top_tc2},{?M2,tc3},
+ {conf,[{name,sub22}],
+ {?M2,init_per_group},
+ [{conf,[{name,sub221}],
+ {?M2,init_per_group},
+ [{?M2,sub221_tc1},{?M2,sub_tc2},{?M2,tc3}],
+ {?M2,end_per_group}},
+ {?M2,sub22_tc1},{?M2,sub_tc2},{?M2,tc3},
+ {conf,[{name,sub2xx}],
+ {?M2,init_per_group},
+ [{?M2,sub2xx_tc1},{?M2,sub_tc2},{?M2,tc3}],
+ {?M2,end_per_group}}],
+ {?M2,end_per_group}}],
+ {?M2,end_per_group}}] = Found,
+
+ {?M2,GPath,TCs,Found}.
+
+%%%-----------------------------------------------------------------
+%%%
+testcase_in_top_groups2(_) ->
+ GPath = [top1,top2], TCs = tc3,
+
+ Found = ct_groups:find_groups(?M2, GPath, TCs, groups2()),
+
+ [{conf,[{name,top1}],
+ {?M2,init_per_group},
+ [{?M2,tc3},
+ {conf,[{name,sub11}],
+ {?M2,init_per_group},
+ [{?M2,tc3}],
+ {?M2,end_per_group}},
+ {conf,[{name,sub12}],
+ {?M2,init_per_group},
+ [{?M2,tc3},
+ {conf,[{name,sub121}],
+ {?M2,init_per_group},
+ [{?M2,tc3}],
+ {?M2,end_per_group}}],
+ {?M2,end_per_group}}],
+ {?M2,end_per_group}},
+
+ {conf,[{name,top2}],
+ {?M2,init_per_group},
+ [{?M2,tc3},
+ {conf,[{name,sub21}],
+ {?M2,init_per_group},
+ [{?M2,tc3},
+ {conf,[{name,sub2xx}],
+ {?M2,init_per_group},
+ [{?M2,tc3}],
+ {?M2,end_per_group}}],
+ {?M2,end_per_group}},
+
+ {conf,[{name,sub22}],
+ {?M2,init_per_group},
+ [{?M2,tc3},
+ {conf,[{name,sub221}],
+ {?M2,init_per_group},
+ [{?M2,tc3}],
+ {?M2,end_per_group}},
+ {conf,[{name,sub2xx}],
+ {?M2,init_per_group},
+ [{?M2,tc3}],
+ {?M2,end_per_group}}],
+ {?M2,end_per_group}}],
+ {?M2,end_per_group}}] = Found,
+
+ {?M2,GPath,TCs,Found}.
+
+%%%-----------------------------------------------------------------
+%%%
+testcase_in_top_groups3(_) ->
+ GPath = [top1,top2], TCs = top1_tc1,
+
+ Found = ct_groups:find_groups(?M2, GPath, TCs, groups2()),
+
+ [{conf,[{name,top1}],
+ {?M2,init_per_group},
+ [{?M2,top1_tc1}],
+ {?M2,end_per_group}}] = Found,
+
+ {?M2,GPath,TCs,Found}.
+
+%%%-----------------------------------------------------------------
+%%%
+testcase_in_top_groups4(_) ->
+ GPath = [top1,top2], TCs = sub2xx_tc1,
+
+ Found = ct_groups:find_groups(?M2, GPath, TCs, groups2()),
+
+ [{conf,[{name,top2}],
+ {?M2,init_per_group},
+ [{conf,[{name,sub21}],
+ {?M2,init_per_group},
+ [{conf,[{name,sub2xx}],
+ {?M2,init_per_group},
+ [{?M2,sub2xx_tc1}],
+ {?M2,end_per_group}}],
+ {?M2,end_per_group}},
+ {conf,[{name,sub22}],
+ {?M2,init_per_group},
+ [{conf,[{name,sub2xx}],
+ {?M2,init_per_group},
+ [{?M2,sub2xx_tc1}],
+ {?M2,end_per_group}}],
+ {?M2,end_per_group}}],
+ {?M2,end_per_group}}] = Found,
+
+ {?M2,GPath,TCs,Found}.
+
+%%%-----------------------------------------------------------------
+%%%
+testcase_in_top_groups5(_) ->
+ GPath = [top1,top2], TCs = [sub21_tc1,sub22_tc1],
+
+ Found = ct_groups:find_groups(?M2, [top1,top2], [sub21_tc1,sub22_tc1],
+ groups2()),
+
+ [{conf,[{name,top2}],
+ {?M2,init_per_group},
+ [{conf,[{name,sub21}],
+ {?M2,init_per_group},
+ [{?M2,sub21_tc1}],
+ {?M2,end_per_group}},
+ {conf,[{name,sub22}],
+ {?M2,init_per_group},
+ [{?M2,sub22_tc1}],
+ {?M2,end_per_group}}],
+ {?M2,end_per_group}}] = Found,
+
+ {?M2,GPath,TCs,Found}.
+
+%%%-----------------------------------------------------------------
+%%%
+testcase_in_top_groups6(_) ->
+ GPath = [[top1],[top2]], TCs = tc3,
+
+ Found = ct_groups:find_groups(?M2, GPath, TCs, groups2()),
+
+ [{conf,[{name,top1}],
+ {?M2,init_per_group},
+ [{?M2,tc3}],
+ {?M2,end_per_group}},
+ {conf,[{name,top2}],
+ {?M2,init_per_group},
+ [{?M2,tc3}],
+ {?M2,end_per_group}}] = Found,
+
+ {?M2,GPath,TCs,Found}.
+
+%%%-----------------------------------------------------------------
+%%%
+testcase_in_top_groups7(_) ->
+ GPath = [[top1],[top2]], TCs = all,
+
+ Found = ct_groups:find_groups(?M2, GPath, TCs, groups2()),
+
+ [{conf,[{name,top1}],
+ {?M2,init_per_group},
+ [{?M2,top1_tc1},
+ {?M2,top_tc2},
+ {?M2,tc3}],
+ {?M2,end_per_group}},
+ {conf,[{name,top2}],
+ {?M2,init_per_group},
+ [{?M2,top2_tc1},
+ {?M2,top_tc2},
+ {?M2,tc3}],
+ {?M2,end_per_group}}] = Found,
+
+ {?M2,GPath,TCs,Found}.
+
+%%%-----------------------------------------------------------------
+%%%
+testcase_in_sub_groups1(_) ->
+ GPath = [sub121], TCs = tc3,
+
+ Found = ct_groups:find_groups(?M2, sub121, tc3, groups2()),
+ Found = ct_groups:find_groups(?M2, [sub121], tc3, groups2()),
+
+ [{conf,[{name,top1}],
+ {?M2,init_per_group},
+ [{conf,[{name,sub12}],
+ {?M2,init_per_group},
+ [{conf,[{name,sub121}],
+ {?M2,init_per_group},
+ [{?M2,tc3}],
+ {?M2,end_per_group}}],
+ {?M2,end_per_group}}],
+ {?M2,end_per_group}}] = Found,
+
+ {?M2,GPath,TCs,Found}.
+
+%%%-----------------------------------------------------------------
+%%%
+testcase_in_sub_groups2(_) ->
+ GPath = sub12, TCs = tc3,
+
+ Found = ct_groups:find_groups(?M2, sub12, tc3, groups2()),
+ Found = ct_groups:find_groups(?M2, [sub12], tc3, groups2()),
+
+ [{conf,[{name,top1}],
+ {?M2,init_per_group},
+ [{conf,[{name,sub12}],
+ {?M2,init_per_group},
+ [{?M2,tc3},
+ {conf,[{name,sub121}],
+ {?M2,init_per_group},
+ [{?M2,tc3}],
+ {?M2,end_per_group}}],
+ {?M2,end_per_group}}],
+ {?M2,end_per_group}}] = Found,
+
+ FoundX = ct_groups:find_groups(?M2, [[sub12]], tc3, groups2()),
+
+ [{conf,[{name,top1}],
+ {?M2,init_per_group},
+ [{conf,[{name,sub12}],
+ {?M2,init_per_group},
+ [{?M2,tc3}],
+ {?M2,end_per_group}}],
+ {?M2,end_per_group}}] = FoundX,
+
+ {?M2,GPath,TCs,Found}.
+
+%%%-----------------------------------------------------------------
+%%%
+testcase_in_sub_groups3(_) ->
+ GPath = [sub121,sub221], TCs = all,
+
+ Found = ct_groups:find_groups(?M2, GPath, TCs, groups2()),
+
+ [Top1 =
+ {conf,[{name,top1}],
+ {?M2,init_per_group},
+ [{conf,[{name,sub12}],
+ {?M2,init_per_group},
+ [{conf,[{name,sub121}],
+ {?M2,init_per_group},
+ [{?M2,sub121_tc1},
+ {?M2,sub_tc2},
+ {?M2,tc3}],
+ {?M2,end_per_group}}],
+ {?M2,end_per_group}}],
+ {?M2,end_per_group}},
+
+ Top2 =
+ {conf,[{name,top2}],
+ {?M2,init_per_group},
+ [{conf,[{name,sub22}],
+ {?M2,init_per_group},
+ [{conf,[{name,sub221}],
+ {?M2,init_per_group},
+ [{?M2,sub221_tc1},
+ {?M2,sub_tc2},
+ {?M2,tc3}],
+ {?M2,end_per_group}}],
+ {?M2,end_per_group}}],
+ {?M2,end_per_group}},
+
+ {conf,[{name,sub22}],
+ {?M2,init_per_group},
+ [{conf,[{name,sub221}],
+ {?M2,init_per_group},
+ [{?M2,sub221_tc1},
+ {?M2,sub_tc2},
+ {?M2,tc3}],
+ {?M2,end_per_group}}],
+ {?M2,end_per_group}},
+
+ {conf,[{name,sub221}],
+ {?M2,init_per_group},
+ [{?M2,sub221_tc1},
+ {?M2,sub_tc2},
+ {?M2,tc3}],
+ {?M2,end_per_group}}] = Found,
+
+ {?M2,GPath,TCs,[Top1,Top2]}.
+
+%%%-----------------------------------------------------------------
+%%%
+testcase_in_sub_groups4(_) ->
+ GPath = [top1,sub21], TCs = sub_tc2,
+
+ Found = ct_groups:find_groups(?M2, GPath, TCs, groups2()),
+
+ [Top1 =
+ {conf,[{name,top1}],
+ {?M2,init_per_group},
+ [{conf,[{name,sub11}],
+ {?M2,init_per_group},
+ [{?M2,sub_tc2}],
+ {?M2,end_per_group}},
+ {conf,[{name,sub12}],
+ {?M2,init_per_group},
+ [{?M2,sub_tc2},
+ {conf,[{name,sub121}],
+ {?M2,init_per_group},
+ [{?M2,sub_tc2}],
+ {?M2,end_per_group}}],
+ {?M2,end_per_group}}],
+ {?M2,end_per_group}},
+
+ Top2 =
+ {conf,[{name,top2}],
+ {?M2,init_per_group},
+ [{conf,[{name,sub21}],
+ {?M2,init_per_group},
+ [{?M2,sub_tc2},
+ {conf,[{name,sub2xx}],
+ {?M2,init_per_group},
+ [{?M2,sub_tc2}],
+ {?M2,end_per_group}}],
+ {?M2,end_per_group}}],
+ {?M2,end_per_group}},
+
+ {conf,[{name,sub21}],
+ {?M2,init_per_group},
+ [{?M2,sub_tc2},
+ {conf,[{name,sub2xx}],
+ {?M2,init_per_group},
+ [{?M2,sub_tc2}],
+ {?M2,end_per_group}}],
+ {?M2,end_per_group}}] = Found,
+
+ {?M2,GPath,TCs,[Top1,Top2]}.
+
+%%%-----------------------------------------------------------------
+%%%
+testcase_in_sub_groups5(_) ->
+ GPath = [[top1,sub12]], TCs = sub12_tc1,
+
+ Found = ct_groups:find_groups(?M2, GPath, TCs, groups2()),
+
+ [{conf,[{name,top1}],
+ {?M2,init_per_group},
+ [{conf,[{name,sub12}],
+ {?M2,init_per_group},
+ [{?M2,sub12_tc1}],
+ {?M2,end_per_group}}],
+ {?M2,end_per_group}}] = Found,
+
+ {?M2,GPath,TCs,Found}.
+
+%%%-----------------------------------------------------------------
+%%%
+testcase_in_sub_groups6(_) ->
+ GPath = [[top1,sub12]], TCs = [sub_tc2],
+
+ Found = ct_groups:find_groups(?M2, GPath, TCs, groups2()),
+
+ [{conf,[{name,top1}],
+ {?M2,init_per_group},
+ [{conf,[{name,sub12}],
+ {?M2,init_per_group},
+ [{?M2,sub_tc2}],
+ {?M2,end_per_group}}],
+ {?M2,end_per_group}}] = Found,
+
+ {?M2,GPath,TCs,Found}.
+
+%%%-----------------------------------------------------------------
+%%%
+testcase_in_sub_groups7(_) ->
+ GPath = [[top1,sub12]], TCs = [sub12_tc1,sub_tc2],
+
+ Found = ct_groups:find_groups(?M2, GPath, TCs, groups2()),
+
+ [{conf,[{name,top1}],
+ {?M2,init_per_group},
+ [{conf,[{name,sub12}],
+ {?M2,init_per_group},
+ [{?M2,sub12_tc1},
+ {?M2,sub_tc2}],
+ {?M2,end_per_group}}],
+ {?M2,end_per_group}}] = Found,
+
+ {?M2,GPath,TCs,Found}.
+
+%%%-----------------------------------------------------------------
+%%%
+testcase_in_sub_groups8(_) ->
+ GPath = [[top2,sub22]], TCs = [sub22_tc1,sub_tc2],
+
+ Found = ct_groups:find_groups(?M2, GPath, TCs, groups2()),
+
+ [{conf,[{name,top2}],
+ {?M2,init_per_group},
+ [{conf,[{name,sub22}],
+ {?M2,init_per_group},
+ [{?M2,sub22_tc1},
+ {?M2,sub_tc2}],
+ {?M2,end_per_group}}],
+ {?M2,end_per_group}}] = Found,
+
+ {?M2,GPath,TCs,Found}.
+
+%%%-----------------------------------------------------------------
+%%%
+testcase_in_sub_groups9(_) ->
+ GPath = [[sub2xx]], TCs = tc3,
+
+ Found = ct_groups:find_groups(?M2, sub2xx, tc3, groups2()),
+ Found = ct_groups:find_groups(?M2, [[sub2xx]], tc3, groups2()),
+
+ [Top2 =
+ {conf,[{name,top2}],
+ {?M2,init_per_group},
+ [{conf,[{name,sub21}],
+ {?M2,init_per_group},
+ [{conf,[{name,sub2xx}],
+ {?M2,init_per_group},
+ [{?M2,tc3}],
+ {?M2,end_per_group}}],
+ {?M2,end_per_group}},
+ {conf,[{name,sub22}],
+ {?M2,init_per_group},
+ [{conf,[{name,sub2xx}],
+ {?M2,init_per_group},
+ [{?M2,tc3}],
+ {?M2,end_per_group}}],
+ {?M2,end_per_group}}],
+ {?M2,end_per_group}},
+
+ {conf,[{name,sub21}],
+ {?M2,init_per_group},
+ [{conf,[{name,sub2xx}],
+ {?M2,init_per_group},
+ [{?M2,tc3}],
+ {?M2,end_per_group}}],
+ {?M2,end_per_group}},
+
+ {conf,[{name,sub22}],
+ {?M2,init_per_group},
+ [{conf,[{name,sub2xx}],
+ {?M2,init_per_group},
+ [{?M2,tc3}],
+ {?M2,end_per_group}}],
+ {?M2,end_per_group}},
+
+ {conf,[{name,sub2xx}],
+ {?M2,init_per_group},
+ [{?M2,tc3}],
+ {?M2,end_per_group}}] = Found,
+
+ {?M2,GPath,TCs,Top2}.
+
+%%%-----------------------------------------------------------------
+%%%
+testcase_in_sub_groups10(_) ->
+ GPath = [[sub22,sub2xx]], TCs = tc3,
+
+ Found = ct_groups:find_groups(?M2, GPath, TCs, groups2()),
+
+ [Top2 =
+ {conf,[{name,top2}],
+ {?M2,init_per_group},
+ [{conf,[{name,sub22}],
+ {?M2,init_per_group},
+ [{conf,[{name,sub2xx}],
+ {?M2,init_per_group},
+ [{?M2,tc3}],
+ {?M2,end_per_group}}],
+ {?M2,end_per_group}}],
+ {?M2,end_per_group}},
+
+ {conf,[{name,sub22}],
+ {?M2,init_per_group},
+ [{conf,[{name,sub2xx}],
+ {?M2,init_per_group},
+ [{?M2,tc3}],
+ {?M2,end_per_group}}],
+ {?M2,end_per_group}}] = Found,
+
+ {?M2,GPath,TCs,Top2}.
+
+%%%-----------------------------------------------------------------
+%%%
+testcase_in_sub_groups11(_) ->
+ GPath = [[top1,sub12,sub121]], TCs = all,
+
+ Found = ct_groups:find_groups(?M2, GPath, TCs, groups2()),
+
+ [{conf,[{name,top1}],
+ {?M2,init_per_group},
+ [{conf,[{name,sub12}],
+ {?M2,init_per_group},
+ [{conf,[{name,sub121}],
+ {?M2,init_per_group},
+ [{?M2,sub121_tc1},
+ {?M2,sub_tc2},
+ {?M2,tc3}],
+ {?M2,end_per_group}}],
+ {?M2,end_per_group}}],
+ {?M2,end_per_group}}] = Found,
+
+ {?M2,GPath,TCs,Found}.
+
+%%%-----------------------------------------------------------------
+%%%
+testcase_in_sub_groups12(_) ->
+ GPath = [[top2,sub2xx]], TCs = [sub2xx_tc1,tc3],
+
+ Found = ct_groups:find_groups(?M2, GPath, TCs, groups2()),
+
+ [{conf,[{name,top2}],
+ {?M2,init_per_group},
+ [{conf,[{name,sub21}],
+ {?M2,init_per_group},
+ [{conf,[{name,sub2xx}],
+ {?M2,init_per_group},
+ [{?M2,sub2xx_tc1},
+ {?M2,tc3}],
+ {?M2,end_per_group}}],
+ {?M2,end_per_group}},
+ {conf,[{name,sub22}],
+ {?M2,init_per_group},
+ [{conf,[{name,sub2xx}],
+ {?M2,init_per_group},
+ [{?M2,sub2xx_tc1},
+ {?M2,tc3}],
+ {?M2,end_per_group}}],
+ {?M2,end_per_group}}],
+ {?M2,end_per_group}}] = Found,
+
+ {?M2,GPath,TCs,Found}.
+
+%%%-----------------------------------------------------------------
+%%%
+testcase_in_sub_groups13(_) ->
+ GPath = [[top2,sub22,sub2xx]], TCs = [top2_tc1,sub2xx_tc1,tc3],
+
+ Found = ct_groups:find_groups(?M2, GPath, TCs, groups2()),
+
+ [{conf,[{name,top2}],
+ {?M2,init_per_group},
+ [{conf,[{name,sub22}],
+ {?M2,init_per_group},
+ [{conf,[{name,sub2xx}],
+ {?M2,init_per_group},
+ [{?M2,sub2xx_tc1},
+ {?M2,tc3}],
+ {?M2,end_per_group}}],
+ {?M2,end_per_group}}],
+ {?M2,end_per_group}}] = Found,
+
+ {?M2,GPath,TCs,Found}.
+
+%%%-----------------------------------------------------------------
+%%%
+bad_testcase_in_sub_groups1(_) ->
+ GPath = [sub2xx], TCs = [top2_tc1],
+
+ Found = ct_groups:find_groups(?M2, GPath, TCs, groups2()),
+
+ [] = Found,
+
+ {?M2,GPath,TCs,Found}.
+
+%%%-----------------------------------------------------------------
+%%%
+bad_testcase_in_sub_groups2(_) ->
+ GPath = [sub12,sub2xx], TCs = [top1_tc1,top2_tc1],
+
+ Found = ct_groups:find_groups(?M2, GPath, TCs, groups2()),
+
+ [] = Found,
+
+ {?M2,GPath,TCs,Found}.
+
+%%%-----------------------------------------------------------------
+%%% CASES EXECUTING THE TESTS
+%%%-----------------------------------------------------------------
+
+run_groups_with_options(Config) ->
+ DataDir = ?config(data_dir, Config),
+
+ {M1All,M1Rest,M2All,M2Rest} = get_all_groups_and_cases(Config),
+
+ M1AllGrs = lists:flatmap(fun({Path,_,_}) when is_atom(hd(Path)) -> Path;
+ ({Path,_,_}) when is_list(hd(Path)) -> Path;
+ ({Path,_,_}) -> [Path]
+ end, M1All),
+
+ %% ct:pal("NOW RUNNING M1 TEST: ~p", [M1All]),
+
+ {OptsM11,ERPidM11} = setup([{dir,DataDir},{suite,?M1},
+ {group,M1AllGrs},{label,m1_all_cases}], Config),
+ M1AllGrInfo = {M1AllGrs,lists:flatten([Found || {_,_,Found} <- M1All])},
+ ok = execute(m1_all_cases, M1AllGrInfo, OptsM11, ERPidM11, Config),
+
+ lists:foldl(
+ fun({GrPath,TCs,Found}, N) ->
+ TestName = list_to_atom("m1_spec_cases_" ++ integer_to_list(N)),
+ %% ct:pal("NOW RUNNING M1 TEST ~p: ~p + ~p",
+ %% [TestName,GrPath,TCs]),
+ {OptsM12,ERPidM12} = setup([{dir,DataDir},{suite,?M1},
+ {group,GrPath},{testcase,TCs},
+ {label,TestName}], Config),
+ ok = execute(TestName, {GrPath,TCs,Found},
+ OptsM12, ERPidM12, Config),
+ N+1
+ end, 1, M1Rest),
+
+ %% ct:pal("NOW RUNNING M2 TEST: ~p", [M2All]),
+
+ M2AllGrs = lists:flatmap(fun({Path,_,_}) when is_atom(hd(Path)) -> Path;
+ ({Path,_,_}) when is_list(hd(Path)) -> Path;
+ ({Path,_,_}) -> [Path]
+ end, M2All),
+
+
+ {OptsM21,ERPidM21} = setup([{dir,DataDir},{suite,?M2},
+ {group,M2AllGrs},{testcase,all},
+ {label,m2_all_cases}], Config),
+ M2AllGrInfo = {M2AllGrs,lists:flatten([Found || {_,_,Found} <- M2All])},
+ ok = execute(m2_all_cases, M2AllGrInfo, OptsM21, ERPidM21, Config),
+
+ lists:foldl(
+ fun({GrPath,TCs,Found}, N) ->
+ TestName = list_to_atom("m2_spec_cases_" ++ integer_to_list(N)),
+ %% ct:pal("NOW RUNNING M2 TEST ~p: ~p + ~p", [TestName,GrPath,TCs]),
+ {OptsM22,ERPidM22} = setup([{dir,DataDir},{suite,?M2},
+ {group,GrPath},{testcase,TCs},
+ {label,TestName}], Config),
+ ok = execute(TestName, {GrPath,TCs,Found},
+ OptsM22, ERPidM22, Config),
+ N+1
+ end, 1, M2Rest),
+ ok.
+
+
+%%%-----------------------------------------------------------------
+%%%
+run_groups_with_testspec(Config) ->
+ Name = run_groups_with_testspec,
+ DataDir = ?config(data_dir, Config),
+ PrivDir = ?config(priv_dir, Config),
+
+ {M1All,M1Rest,M2All,M2Rest} = get_all_groups_and_cases(Config),
+
+ M1AllGrs = lists:flatmap(fun({Path,_,_}) when is_atom(hd(Path)) -> Path;
+ ({Path,_,_}) when is_list(hd(Path)) -> Path;
+ ({Path,_,_}) -> [Path]
+ end, M1All),
+ M1AllTerm = {groups,DataDir,?M1,M1AllGrs},
+
+ M1RestTerms = lists:map(
+ fun({GrPath,TCs,_}) ->
+ {groups,DataDir,?M1,GrPath,{cases,TCs}}
+ end, M1Rest),
+
+ M2AllGrs = lists:flatmap(fun({Path,_,_}) when is_atom(hd(Path)) -> Path;
+ ({Path,_,_}) when is_list(hd(Path)) -> Path;
+ ({Path,_,_}) -> [Path]
+ end, M2All),
+ M2AllTerm = {groups,DataDir,?M2,M2AllGrs,{cases,all}},
+
+ M2RestTerms = lists:map(
+ fun({GrPath,TCs,_}) ->
+ {groups,DataDir,?M2,GrPath,{cases,TCs}}
+ end, M2Rest),
+
+ GroupTerms = lists:flatten([M1AllTerm,
+ M1RestTerms,
+ M2AllTerm,
+ M2RestTerms]),
+
+ TestSpec = [{merge_tests,false},
+ {label,Name}] ++ GroupTerms,
+
+ ct:pal("Here's the test spec:~n~p", [TestSpec]),
+
+ TestSpecName = ct_test_support:write_testspec(TestSpec, PrivDir,
+ "groups_search_spec"),
+
+ {Opts,ERPid} = setup([{spec,TestSpecName}], Config),
+ GroupInfo =
+ [{M1AllTerm,lists:flatten([Found || {_,_,Found} <- M1All])} |
+ M1Rest] ++
+ [{M2AllTerm,lists:flatten([Found || {_,_,Found} <- M2All])} |
+ M2Rest],
+ ok = execute(Name, GroupInfo, Opts, ERPid, Config).
+
+%%%-----------------------------------------------------------------
+%%% HELP FUNCTIONS
+%%%-----------------------------------------------------------------
+
+groups1() ->
+ [{top1,[],[top1_tc1,top1_tc2,{sub1,[],[sub1_tc1,sub1_tc2]}]},
+ {top2,[],[{group,sub2},top2_tc1,top2_tc2]},
+ {sub2,[],[sub2_tc1,sub2_tc2]}].
+
+groups2() ->
+ [{top1,[],[top1_tc1,top_tc2,tc3,
+ {sub11,[],[sub11_tc1,sub_tc2,tc3]},
+ {sub12,[],[sub12_tc1,sub_tc2,tc3,
+ {sub121,[],[sub121_tc1,sub_tc2,tc3]}]}]},
+ {top2,[],[{group,sub21},top2_tc1,top_tc2,tc3,{group,sub22}]},
+ {sub21,[],[sub21_tc1,sub_tc2,tc3,{group,sub2xx}]},
+ {sub22,[],[{group,sub221},sub22_tc1,sub_tc2,tc3,{group,sub2xx}]},
+ {sub221,[],[sub221_tc1,sub_tc2,tc3]},
+ {sub2xx,[],[sub2xx_tc1,sub_tc2,tc3]}].
+
+get_all_groups_and_cases(Config) ->
+ {value,{_,_,FindGrTCs}} = lists:keysearch(find_groups, 1, groups()),
+
+ MGTFs = [apply(?MODULE, TC, [Config]) || TC <- FindGrTCs],
+
+ ct:pal("Extracted data from ~p test cases", [length(MGTFs)]),
+
+ lists:foldr(fun({M,Gs,TCs,F},
+ {M11,M12,M21,M22}) ->
+ case {M,Gs,TCs} of
+ {?M1,all,_} -> {M11,[{Gs,TCs,F}|M12],M21,M22};
+ {?M1,_,all} -> {[{Gs,all,F}|M11],M12,M21,M22};
+ {?M1,_,_} -> {M11,[{Gs,TCs,F}|M12],M21,M22};
+ {?M2,all,_} -> {M11,M12,M21,[{Gs,TCs,F}|M22]};
+ {?M2,_,all} -> {M11,M12,[{Gs,all,F}|M21],M22};
+ {?M2,_,_} -> {M11,M12,M21,[{Gs,TCs,F}|M22]}
+ end
+ end, {[],[],[],[]}, MGTFs).
+
+%%%-----------------------------------------------------------------
+
+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}.
+
+execute(Name, TestParams, Opts, ERPid, Config) ->
+ ok = ct_test_support:run(Opts, Config),
+ Events = ct_test_support:get_events(ERPid, Config),
+ Events1 = reformat(Events, ?eh),
+ ct_test_support:log_events(Name,
+ Events1,
+ ?config(priv_dir, Config),
+ Opts),
+ verify_events(Name, TestParams, Events1).
+
+reformat(Events, EH) ->
+ ct_test_support:reformat(Events, EH).
+
+%%%-----------------------------------------------------------------
+%%% TEST EVENTS
+verify_events(Name, Params, Events) ->
+ %% 2 tests (ct:run_test + script_start) is default
+ verify_events(Name, Params, Events, 2).
+
+verify_events(_, _, _, 0) ->
+ ok;
+verify_events(Name, Params, Events, N) ->
+ test_events(Name, Params, Events),
+ verify_events(Name, Params, Events, N-1).
+
+%%%-----------------------------------------------------------------
+%%% check run_groups_with_options
+
+test_events(TestName, {GrPath,Found}, Events) ->
+ test_events(TestName, {GrPath,all,Found}, Events);
+
+test_events(TestName, {GrPath,TCs,Found}, Events)
+ when TestName /= run_groups_with_testspec ->
+ try check_events(Events, flatten_tests(Found)) of
+ ok -> ok
+ catch
+ throw:Reason ->
+ ct:pal("Test failed for ~p with group path ~p and cases ~p"
+ "~nReason: ~p", [TestName,GrPath,TCs,Reason]),
+ throw(failed)
+ end;
+
+%%%-----------------------------------------------------------------
+%%% check run_groups_with_testspec
+
+test_events(run_groups_with_testspec, Params, Events) ->
+ AllFound = lists:flatmap(fun({_All,Found}) when is_tuple(Found) ->
+ [Found];
+ ({_All,Found}) ->
+ Found;
+ ({_Gr,_TCs,Found}) when is_tuple(Found) ->
+ [Found];
+ ({_Gr,_TCs,Found}) ->
+ Found
+ end, Params),
+ try check_events(Events, flatten_tests(AllFound)) of
+ ok -> ok
+ catch
+ throw:Reason ->
+ ct:pal("Test failed for run_groups_with_testspec."
+ "~nReason: ~p", [Reason]),
+ throw(failed)
+ end.
+
+flatten_tests({conf,[{name,G}|_],{Mod,_I},Tests,_E}) ->
+ lists:flatten([{group,Mod,G} | flatten_tests(Tests)]);
+flatten_tests([{conf,[{name,G}|_],{Mod,_I},Tests,_E} | Confs]) ->
+ lists:flatten([{group,Mod,G} | flatten_tests(Tests)]) ++
+ lists:flatten(flatten_tests(Confs));
+flatten_tests([{_Mod,_TC} = Case | Tests]) ->
+ lists:flatten([Case | flatten_tests(Tests)]);
+flatten_tests([]) ->
+ [].
+
+check_events([{_,tc_start,{Mod,{init_per_group,G,_}}} | Evs],
+ [{group,Mod,G} | Check]) ->
+ check_events(Evs, Check);
+check_events([{_,tc_start,{Mod,TC}} | Evs],
+ [{Mod,TC} | Check]) when is_atom(TC) ->
+ check_events(Evs, Check);
+check_events([{_,tc_start,{Mod,{init_per_group,G,_}}} | _Evs], Check) ->
+ ct:pal("CHECK FAILED!~nGroup ~p in ~p not found in ~p.",
+ [G,Mod,Check]),
+ throw({test_not_found,{Mod,G}});
+check_events([{_,tc_start,{Mod,TC}} | _Evs], Check)
+ when is_atom(TC), TC /= init_per_suite, TC /= end_per_suite ->
+ ct:pal("CHECK FAILED!~nCase ~p in ~p not found in ~p.",
+ [TC,Mod,Check]),
+ throw({test_not_found,{Mod,TC}});
+check_events([Group | Evs], Check) when is_list(Group) ->
+ Check1 = check_events(Group, Check),
+ check_events(Evs, Check1);
+check_events(_, []) ->
+ ok;
+check_events([Elem | Evs], Check) when is_tuple(Elem) ->
+ check_events(Evs, Check);
+check_events([], Check = [_|_]) ->
+ ct:pal("CHECK FAILED!~nTests remain: ~p", [Check]),
+ throw({tests_remain,Check});
+check_events([Wut | _],_) ->
+ throw({unexpected,Wut}).
+
diff --git a/lib/common_test/test/ct_groups_search_SUITE_data/groups_search_dummy_1_SUITE.erl b/lib/common_test/test/ct_groups_search_SUITE_data/groups_search_dummy_1_SUITE.erl
new file mode 100644
index 0000000000..1c5b572f92
--- /dev/null
+++ b/lib/common_test/test/ct_groups_search_SUITE_data/groups_search_dummy_1_SUITE.erl
@@ -0,0 +1,83 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2009-2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, 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_search_dummy_1_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+
+all() ->
+ [{group,top1},
+ {group,top2}].
+
+groups() ->
+ [{top1,[],[top1_tc1,top1_tc2,{sub1,[],[sub1_tc1,sub1_tc2]}]},
+ {top2,[],[{group,sub2},top2_tc1,top2_tc2]},
+ {sub2,[],[sub2_tc1,sub2_tc2]}].
+
+%%%-----------------------------------------------------------------
+%%% CONFIG FUNCS
+%%%-----------------------------------------------------------------
+
+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(_, Config) ->
+ Config.
+
+end_per_group(_, _Config) ->
+ ok.
+
+%%%-----------------------------------------------------------------
+%%% TEST CASES
+%%%-----------------------------------------------------------------
+
+top1_tc1(_) ->
+ ok.
+
+top1_tc2(_) ->
+ ok.
+
+sub1_tc1(_) ->
+ ok.
+
+sub1_tc2(_) ->
+ ok.
+
+top2_tc1(_) ->
+ ok.
+
+top2_tc2(_) ->
+ ok.
+
+sub2_tc1(_) ->
+ ok.
+
+sub2_tc2(_) ->
+ ok.
diff --git a/lib/common_test/test/ct_groups_search_SUITE_data/groups_search_dummy_2_SUITE.erl b/lib/common_test/test/ct_groups_search_SUITE_data/groups_search_dummy_2_SUITE.erl
new file mode 100644
index 0000000000..060012de29
--- /dev/null
+++ b/lib/common_test/test/ct_groups_search_SUITE_data/groups_search_dummy_2_SUITE.erl
@@ -0,0 +1,102 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2009-2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, 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_search_dummy_2_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+
+all() ->
+ [{group,top1},
+ {group,top2}].
+
+groups() ->
+ [{top1,[],[top1_tc1,top_tc2,tc3,
+ {sub11,[],[sub11_tc1,sub_tc2,tc3]},
+ {sub12,[],[sub12_tc1,sub_tc2,tc3,
+ {sub121,[],[sub121_tc1,sub_tc2,tc3]}]}]},
+
+ {top2,[],[{group,sub21},top2_tc1,top_tc2,tc3,{group,sub22}]},
+ {sub21,[],[sub21_tc1,sub_tc2,tc3,{group,sub2xx}]},
+ {sub22,[],[{group,sub221},sub22_tc1,sub_tc2,tc3,{group,sub2xx}]},
+ {sub221,[],[sub221_tc1,sub_tc2,tc3]},
+ {sub2xx,[],[sub2xx_tc1,sub_tc2,tc3]}].
+
+%%%-----------------------------------------------------------------
+%%% CONFIG FUNCS
+%%%-----------------------------------------------------------------
+
+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(_, Config) ->
+ Config.
+
+end_per_group(_, _Config) ->
+ ok.
+
+%%%------------------------------------------------------------------
+%%% TEST CASES
+%%%------------------------------------------------------------------
+
+top1_tc1(_) ->
+ ok.
+
+top_tc2(_) ->
+ ok.
+
+tc3(_) ->
+ ok.
+
+sub_tc2(_) ->
+ ok.
+
+sub11_tc1(_) ->
+ ok.
+
+sub12_tc1(_) ->
+ ok.
+
+sub121_tc1(_) ->
+ ok.
+
+top2_tc1(_) ->
+ ok.
+
+sub21_tc1(_) ->
+ ok.
+
+sub22_tc1(_) ->
+ ok.
+
+sub221_tc1(_) ->
+ ok.
+
+sub2xx_tc1(_) ->
+ ok.
diff --git a/lib/common_test/test/ct_hooks_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE.erl
index 405df1e978..796a0832d7 100644
--- a/lib/common_test/test/ct_hooks_SUITE.erl
+++ b/lib/common_test/test/ct_hooks_SUITE.erl
@@ -64,7 +64,7 @@ end_per_testcase(TestCase, Config) ->
suite() ->
- [{timetrap,{seconds,20}}].
+ [{timetrap,{minutes,1}}].
all() ->
all(suite).
diff --git a/lib/common_test/test/ct_master_SUITE.erl b/lib/common_test/test/ct_master_SUITE.erl
index 27243a0067..b89d7d4dc5 100644
--- a/lib/common_test/test/ct_master_SUITE.erl
+++ b/lib/common_test/test/ct_master_SUITE.erl
@@ -117,14 +117,8 @@ ct_master_test(Config) when is_list(Config) ->
reformat(Events, ?eh),
PrivDir, []),
- 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.
+ TestEvents = events_to_check(ct_master_test),
+ ok = find_events(NodeNames, TestEvents, Events, Config).
%%%-----------------------------------------------------------------
%%% HELP FUNCTIONS
@@ -153,13 +147,18 @@ make_spec(DataDir, FileName, NodeNames, Suites, Config) ->
CM = [{config,master,filename:join(DataDir,"master/config.txt")}],
+ Env = [{"THIS_MUST_BE_SET","yes"},
+ {"SO_MUST_THIS","value"}],
NS = lists:map(
fun(NodeName) ->
{init,NodeName,[
{node_start,[{startup_functions,[]},
- {monitor_master,true}]},
- {eval,{erlang,nodes,[]}}
- ]
+ {monitor_master,true},
+ {boot_timeout,10},
+ {init_timeout,10},
+ {startup_timeout,10},
+ {env,Env}]},
+ {eval,{erlang,nodes,[]}}]
}
end,
NodeNames),
@@ -199,7 +198,6 @@ run_test(_Name, FileName, Config) ->
[{FileName,ok}] = ct_test_support:run({ct_master,run,[FileName]},
[{ct_master,basic_html,[true]}],
Config),
- timer:sleep(5000),
[{FileName,ok}] = ct_test_support:run({ct_master,run,[FileName]},
[{ct_master,basic_html,[false]}],
Config).
@@ -210,28 +208,26 @@ 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).
+
+find_events(NodeNames, TestEvents, Events, Config) ->
+ [begin
+ Node = add_host(Node0),
+ io:format("Searching for events for node: ~s", [Node]),
+ ok = ct_test_support:verify_events(TestEvents, Events, Node, Config),
+ io:nl()
+ end || Node0 <- NodeNames],
+ ok.
add_host(NodeName) ->
{ok, HostName} = inet:gethostname(),
list_to_atom(atom_to_list(NodeName)++"@"++HostName).
-expected_events(_) ->
- [].
+events_to_check(_) ->
+ [{?eh,tc_start,{master_SUITE,first_testcase}},
+ {?eh,tc_done,{master_SUITE,first_testcase,ok}},
+ {?eh,tc_start,{master_SUITE,second_testcase}},
+ {?eh,tc_done,{master_SUITE,second_testcase,ok}},
+ {?eh,tc_start,{master_SUITE,third_testcase}},
+ {?eh,tc_done,{master_SUITE,third_testcase,ok}},
+ {?eh,tc_start,{master_SUITE,env_vars}},
+ {?eh,tc_done,{master_SUITE,env_vars,ok}}].
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
index 032d69ad9f..8a5009ad62 100644
--- 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
@@ -39,7 +39,8 @@ init_per_suite(Config) ->
end_per_suite(_) ->
ok.
-all() -> [first_testcase, second_testcase, third_testcase].
+all() -> [first_testcase, second_testcase, third_testcase,
+ env_vars].
init_per_testcase(_, Config) ->
Config.
@@ -56,3 +57,9 @@ second_testcase(_)->
third_testcase(_)->
A = 4,
A = 2*2.
+
+env_vars(_) ->
+ io:format("~p\n", [os:getenv()]),
+ "yes" = os:getenv("THIS_MUST_BE_SET"),
+ "value" = os:getenv("SO_MUST_THIS"),
+ ok.
diff --git a/lib/common_test/test/ct_misc_1_SUITE.erl b/lib/common_test/test/ct_misc_1_SUITE.erl
index cb17af9ab5..9ff4e6a65f 100644
--- a/lib/common_test/test/ct_misc_1_SUITE.erl
+++ b/lib/common_test/test/ct_misc_1_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -106,7 +106,7 @@ beam_me_up(Config) when is_list(Config) ->
{Opts,ERPid} = setup([{suite,Suites},{auto_compile,false}], Config),
- ok = ct_test_support:run(ct, run_test, [Opts], Config),
+ {_Ok,_Fail,_Skip} = 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,
diff --git a/lib/common_test/test/ct_netconfc_SUITE.erl b/lib/common_test/test/ct_netconfc_SUITE.erl
new file mode 100644
index 0000000000..c89a4cdabe
--- /dev/null
+++ b/lib/common_test/test/ct_netconfc_SUITE.erl
@@ -0,0 +1,128 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2009-2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, 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_netconfc_SUITE
+%%%
+%%% Description:
+%%% Test ct_netconfc module
+%%%
+%%%-------------------------------------------------------------------
+-module(ct_netconfc_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) ->
+ case application:load(crypto) of
+ {error,Reason} when Reason=/={already_loaded,crypto} ->
+ {skip, Reason};
+ _ ->
+ ct_test_support:init_per_suite(Config)
+ end.
+
+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() ->
+ [
+ default
+ ].
+
+%%--------------------------------------------------------------------
+%% TEST CASES
+%%--------------------------------------------------------------------
+
+%%%-----------------------------------------------------------------
+%%%
+default(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ Suite = filename:join(DataDir, "netconfc1_SUITE"),
+ CfgFile = filename:join(DataDir, "netconfc1.cfg"),
+ {Opts,ERPid} = setup([{suite,Suite},{config,CfgFile},
+ {label,default}], Config),
+
+ ok = execute(default, Opts, ERPid, Config).
+
+
+%%%-----------------------------------------------------------------
+%%% 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}.
+
+execute(Name, Opts, ERPid, Config) ->
+ ok = ct_test_support:run(Opts, Config),
+ Events = ct_test_support:get_events(ERPid, Config),
+
+ ct_test_support:log_events(Name,
+ reformat(Events, ?eh),
+ ?config(priv_dir, Config),
+ Opts),
+
+ TestEvents = events_to_check(Name,Config),
+ ct_test_support:verify_events(TestEvents, Events, Config).
+
+reformat(Events, EH) ->
+ ct_test_support:reformat(Events, EH).
+
+%%%-----------------------------------------------------------------
+%%% TEST EVENTS
+%%%-----------------------------------------------------------------
+events_to_check(default,Config) ->
+ {module,_} = code:load_abs(filename:join(?config(data_dir,Config),
+ netconfc1_SUITE)),
+ TCs = netconfc1_SUITE:all(),
+ code:purge(netconfc1_SUITE),
+ code:delete(netconfc1_SUITE),
+
+ OneTest =
+ [{?eh,start_logging,{'DEF','RUNDIR'}}] ++
+ [{?eh,tc_done,{netconfc1_SUITE,TC,ok}} || TC <- TCs] ++
+ [{?eh,stop_logging,[]}],
+
+ %% 2 tests (ct:run_test + script_start) is default
+ OneTest ++ OneTest.
diff --git a/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1.cfg b/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1.cfg
new file mode 100644
index 0000000000..b431301df6
--- /dev/null
+++ b/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1.cfg
@@ -0,0 +1,6 @@
+%% -*- erlang -*-
+{netconf1,[{ssh,"127.0.0.1"},
+ {port,2060},
+ {user,"xxx"},
+ {password,"xxx"}]}.
+{ct_conn_log,[{ct_netconfc,[{log_type,pretty}]}]}. %overrides args to cth_conn_log
diff --git a/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl b/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl
new file mode 100644
index 0000000000..54526e8e83
--- /dev/null
+++ b/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl
@@ -0,0 +1,1129 @@
+%%--------------------------------------------------------------------
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, 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_netconfc_SUITE.erl
+%%
+%% Description:
+%% This file contains the test cases for the ct_netconfc API.
+%%
+%% @author Support
+%% @doc Netconf Client Interface.
+%% @end
+%%----------------------------------------------------------------------
+%%----------------------------------------------------------------------
+-module(netconfc1_SUITE).
+-include_lib("common_test/include/ct.hrl").
+-include_lib("common_test/src/ct_netconfc.hrl").
+-include_lib("public_key/include/public_key.hrl").
+
+-compile(export_all).
+
+%% Default timetrap timeout (set in init_per_testcase).
+-define(default_timeout, ?t:minutes(1)).
+
+-define(NS,ns).
+-define(LOCALHOST, "127.0.0.1").
+-define(SSH_PORT, 2060).
+
+-define(DEFAULT_SSH_OPTS,[{ssh,?LOCALHOST},
+ {port,?SSH_PORT},
+ {user,"xxx"},
+ {password,"xxx"}]).
+-define(DEFAULT_SSH_OPTS(Dir), ?DEFAULT_SSH_OPTS++[{user_dir,Dir}]).
+
+-define(ok,ok).
+
+suite() ->
+ [{ct_hooks, [{cth_conn_log,
+ [{ct_netconfc,[{log_type,html}, %will be overwritten by config
+ {hosts,[my_named_connection,netconf1]}]
+ }]
+ }]
+ }].
+
+all() ->
+ case os:find_executable("ssh") of
+ false ->
+ {skip, "SSH not installed on host"};
+ _ ->
+ [hello,
+ hello_from_server_first,
+ hello_named,
+ hello_configured,
+ hello_configured_extraopts,
+ hello_required,
+ hello_required_exists,
+ hello_global_pwd,
+ hello_no_session_id,
+ hello_incomp_base_vsn,
+ hello_no_base_cap,
+ hello_no_caps,
+ no_server_hello,
+ no_client_hello,
+ get_session_id,
+ get_capabilities,
+ faulty_user,
+ faulty_passwd,
+ faulty_port,
+ no_host,
+ no_port,
+ invalid_opt,
+ get,
+ get_xpath,
+ get_config,
+ get_config_xpath,
+ edit_config,
+ copy_config,
+ delete_config,
+ lock,
+ unlock,
+ kill_session,
+ get_no_such_client,
+ action,
+ send_any_rpc,
+ send_any,
+ hide_password,
+ not_proper_xml,
+ prefixed_namespace,
+ receive_chunked_data,
+ timeout_receive_chunked_data,
+ close_while_waiting_for_chunked_data,
+ connection_crash,
+ get_event_streams,
+ create_subscription,
+ receive_event
+ ]
+ end.
+
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+init_per_testcase(_Case, Config) ->
+ ets:delete_all_objects(ns_tab),
+ 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 catch {crypto:start(), ssh:start()} of
+ {ok, ok} ->
+ {ok, _} = get_id_keys(Config),
+ make_dsa_files(Config),
+ Server = ?NS:start(?config(data_dir,Config)),
+ [{server,Server}|Config];
+ _ ->
+ {skip, "Crypto and/or SSH could not be started!"}
+ end.
+
+end_per_suite(Config) ->
+ PrivDir = ?config(priv_dir, Config),
+ ?NS:stop(?config(server,Config)),
+ ssh:stop(),
+ crypto:stop(),
+ remove_id_keys(PrivDir),
+ Config.
+
+hello(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,Client} = open_success(DataDir),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client),
+ ok.
+
+hello_from_server_first(Config) ->
+ DataDir = ?config(data_dir,Config),
+ ?NS:hello(1),
+ {ok,Client} = ct_netconfc:only_open(?DEFAULT_SSH_OPTS(DataDir)),
+ ct:sleep(500),
+ ?NS:expect(hello),
+ ?ok = ct_netconfc:hello(Client),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client),
+ ok.
+
+hello_named(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,Client} = open_success(any_name,DataDir),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client),
+ ok.
+
+hello_configured() ->
+ [{require, netconf1}].
+hello_configured(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,Client} = open_configured_success(netconf1,DataDir),
+ ?NS:expect_do_reply('close-session',close,ok),
+ {error, {no_such_name,netconf1}} = ct_netconfc:close_session(netconf1),
+ ?ok = ct_netconfc:close_session(Client),
+ ok.
+
+hello_configured_extraopts() ->
+ [{require, netconf1}].
+hello_configured_extraopts(Config) ->
+ DataDir = ?config(data_dir,Config),
+ %% Test that the cofiguration overwrites the ExtraOpts parameter
+ %% to ct_netconfc:open/2.
+ {ok,Client} = open_configured_success(netconf1,DataDir,[{password,"faulty"}]),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client),
+ ok.
+
+hello_required() ->
+ [{require, my_named_connection, netconf1}].
+hello_required(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,_Client} = open_configured_success(my_named_connection,DataDir),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(my_named_connection),
+ ok.
+
+hello_required_exists() ->
+ [{require, my_named_connection, netconf1}].
+hello_required_exists(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,_Client1} = open_configured_success(my_named_connection,DataDir),
+
+ %% Check that same name can not be used twice
+ {error,{connection_exists,_Client1}} =
+ ct_netconfc:open(my_named_connection,[{user_dir,DataDir}]),
+
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(my_named_connection),
+ timer:sleep(500),
+
+ %% Then check that it can be used again after the first is closed
+ {ok,_Client2} = open_configured_success(my_named_connection,DataDir),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(my_named_connection),
+ ok.
+
+hello_global_pwd(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,Client} = open_success(DataDir,[{user,"any-user"},
+ {password,"global-xxx"}]),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client),
+ ok.
+
+hello_no_session_id(Config) ->
+ DataDir = ?config(data_dir,Config),
+ ?NS:hello(no_session_id),
+ ?NS:expect(no_session_id,hello),
+ {error,{incorrect_hello,no_session_id_found}} = open(DataDir),
+ ok.
+
+hello_incomp_base_vsn(Config) ->
+ DataDir = ?config(data_dir,Config),
+ ?NS:hello(1,{base,"1.1"}),
+ ?NS:expect(hello),
+ {error,{incompatible_base_capability_vsn,"1.1"}} = open(DataDir),
+ ok.
+
+hello_no_base_cap(Config) ->
+ DataDir = ?config(data_dir,Config),
+ ?NS:hello(1,no_base),
+ ?NS:expect(hello),
+ {error,{incorrect_hello,no_base_capability_found}} = open(DataDir),
+ ok.
+
+hello_no_caps(Config) ->
+ DataDir = ?config(data_dir,Config),
+ ?NS:hello(1,no_caps),
+ ?NS:expect(hello),
+ {error,{incorrect_hello,capabilities_not_found}} = open(DataDir),
+ ok.
+
+no_server_hello(Config) ->
+ DataDir = ?config(data_dir,Config),
+ ?NS:expect(undefined,hello),
+ {error,{hello_session_failed,timeout}} = open(DataDir,[{timeout,2000}]),
+ ok.
+
+no_client_hello(Config) ->
+ DataDir = ?config(data_dir,Config),
+ ?NS:hello(1),
+ {ok,Client} = ct_netconfc:only_open(?DEFAULT_SSH_OPTS(DataDir)),
+
+ %% Allow server hello to arrive
+ ct:sleep(500),
+
+ %% Tell server to receive a get request and then die without
+ %% replying since no hello has been received. (is this correct
+ %% behavoiur??)
+ ?NS:expect_do(get,close),
+ {error,closed} = ct_netconfc:get(Client,whatever),
+ ok.
+
+get_session_id(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,Client} = open_success(DataDir),
+
+ 1 = ct_netconfc:get_session_id(Client),
+
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client),
+ ok.
+
+get_capabilities(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,Client} = open_success(DataDir),
+
+ Caps = ct_netconfc:get_capabilities(Client),
+ BaseCap = ?NETCONF_BASE_CAP ++ ?NETCONF_BASE_CAP_VSN,
+ [BaseCap,"urn:ietf:params:netconf:capability:writable-running:1.0" |_] = Caps,
+
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client),
+ ok.
+
+faulty_user(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {error,{ssh,could_not_connect_to_server,
+ "Unable to connect using the available authentication methods"}} =
+ open(DataDir,[{user,"yyy"}]),
+ ok.
+
+faulty_passwd(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {error,{ssh,could_not_connect_to_server,
+ "Unable to connect using the available authentication methods"}} =
+ open(DataDir,[{password,"yyy"}]),
+ ok.
+
+faulty_port(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {error,{ssh,could_not_connect_to_server,econnrefused}} =
+ open(DataDir,[{port,2062}]),
+ ok.
+
+no_host(Config) ->
+ DataDir = ?config(data_dir,Config),
+ Opts = lists:keydelete(ssh,1,?DEFAULT_SSH_OPTS(DataDir)),
+ {error,no_host_address} = ct_netconfc:open(Opts),
+ ok.
+
+no_port(Config) ->
+ DataDir = ?config(data_dir,Config),
+ Opts = lists:keydelete(port,1,?DEFAULT_SSH_OPTS(DataDir)),
+ {error,no_port} = ct_netconfc:open(Opts),
+ ok.
+
+invalid_opt(Config) ->
+ DataDir = ?config(data_dir,Config),
+ Opts1 = ?DEFAULT_SSH_OPTS(DataDir) ++ [{timeout,invalidvalue}],
+ {error,{invalid_option,{timeout,invalidvalue}}} = ct_netconfc:open(Opts1),
+ Opts2 = ?DEFAULT_SSH_OPTS(DataDir) ++ [{some_other_opt,true}],
+ {error,{invalid_option,{some_other_opt,true}}} = ct_netconfc:open(Opts2),
+ ok.
+
+get(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,Client} = open_success(DataDir),
+ Data = [{server,[{xmlns,"myns"}],[{name,[],["myserver"]}]}],
+ ?NS:expect_reply('get',{data,Data}),
+ {ok,Data} = ct_netconfc:get(Client,{server,[{xmlns,"myns"}],[]}),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client),
+ ok.
+
+get_xpath(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,Client} = open_success(DataDir),
+ Data = [{server,[{xmlns,"myns"}],[{name,[],["myserver"]}]}],
+ ?NS:expect_reply({'get',xpath},{data,Data}),
+ {ok,Data} = ct_netconfc:get(Client,{xpath,"/server"}),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client),
+ ok.
+
+get_config(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,Client} = open_success(DataDir),
+ Data = [{server,[{xmlns,"myns"}],[{name,[],["myserver"]}]}],
+ ?NS:expect_reply('get-config',{data,Data}),
+ {ok,Data} = ct_netconfc:get_config(Client,running,
+ {server,[{xmlns,"myns"}],[]}),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client),
+ ok.
+
+get_config_xpath(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,Client} = open_success(DataDir),
+ Data = [{server,[{xmlns,"myns"}],[{name,[],["myserver"]}]}],
+ ?NS:expect_reply({'get-config',xpath},{data,Data}),
+ {ok,Data} = ct_netconfc:get_config(Client,running,{xpath,"/server"}),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client),
+ ok.
+
+edit_config(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,Client} = open_success(DataDir),
+ ?NS:expect_reply('edit-config',ok),
+ ?ok = ct_netconfc:edit_config(Client,running,
+ {server,[{xmlns,"myns"}],
+ [{name,["myserver"]}]}),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client),
+ ok.
+
+copy_config(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,Client} = open_success(DataDir),
+ ?NS:expect_reply('copy-config',ok),
+ ?ok = ct_netconfc:copy_config(Client,startup,running),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client),
+ ok.
+
+delete_config(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,Client} = open_success(DataDir),
+ ?NS:expect_reply('delete-config',ok),
+ ?ok = ct_netconfc:delete_config(Client,startup),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client),
+ ok.
+
+lock(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,Client} = open_success(DataDir),
+ ?NS:expect_reply('lock',ok),
+ ?ok = ct_netconfc:lock(Client,running),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client),
+ ok.
+
+unlock(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,Client} = open_success(DataDir),
+ ?NS:expect_reply('unlock',ok),
+ ?ok = ct_netconfc:unlock(Client,running),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client),
+ ok.
+
+kill_session(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,Client} = open_success(DataDir),
+
+ ?NS:hello(2),
+ ?NS:expect(2,hello),
+ {ok,_OtherClient} = open(DataDir),
+
+ ?NS:expect_do_reply('kill-session',{kill,2},ok),
+ ?ok = ct_netconfc:kill_session(Client,2),
+
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client),
+
+ ok.
+
+get_no_such_client(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,Client} = open_success(DataDir),
+
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client),
+ case ct_netconfc:get(Client,{server,[{xmlns,"myns"}],[]}) of
+ {error,no_such_client} ->
+ ok;
+ {error,closed} ->
+ %% Means that the Client process was not terminated before the call.
+ %% Give it one more go.
+ {error,no_such_client} =
+ ct_netconfc:get(Client,{server,[{xmlns,"myns"}],[]})
+ end,
+ ok.
+
+action(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,Client} = open_success(DataDir),
+ Data = [{myactionreturn,[{xmlns,"myns"}],["value"]}],
+ ?NS:expect_reply(action,{data,Data}),
+ {ok,Data} = ct_netconfc:action(Client,{myaction,[{xmlns,"myns"}],[]}),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client),
+ ok.
+
+send_any_rpc(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,Client} = open_success(DataDir),
+ Data = [{server,[{xmlns,"myns"}],[{name,[],["myserver"]}]}],
+ GetConf = {'get-config',
+ [{source,["running"]},
+ {filter,[{type,"subtree"}],
+ [{server,[{xmlns,"myns"}],[]}]}]},
+ ?NS:expect_reply('get-config',{data,Data}),
+ [{data,?NETCONF_NAMESPACE_ATTR,Data}] = ct_netconfc:send_rpc(Client,GetConf),
+
+ EditConf = {'edit-config',
+ [{target,["running"]},
+ {config,[{server,[{xmlns,"myns"}],
+ [{name,["myserver"]}]}]}]},
+ ?NS:expect_reply('edit-config',ok),
+ [{ok,?NETCONF_NAMESPACE_ATTR,[]}] = ct_netconfc:send_rpc(Client,EditConf),
+
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client),
+ ok.
+
+send_any(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,Client} = open_success(DataDir),
+
+ %% Correct get-config rpc
+ Data = [{server,[{xmlns,"myns"}],[{name,[],["myserver"]}]}],
+ RpcAttr1 = ?NETCONF_NAMESPACE_ATTR ++ [{'message-id',"1"}],
+ RpcGetConf = {rpc,RpcAttr1,
+ [{'get-config',
+ [{source,["running"]},
+ {filter,[{type,"subtree"}],
+ [{server,[{xmlns,"myns"}],[]}]}]}]},
+ ?NS:expect_reply('get-config',{data,Data}),
+ {'rpc-reply',RpcAttr1,[{data,_,Data}]} = ct_netconfc:send(Client,RpcGetConf),
+
+ %% Correct edit-config rpc
+ RpcAttr2 = ?NETCONF_NAMESPACE_ATTR ++ [{'message-id',"2"}],
+ RpcEditConf = {rpc,RpcAttr2,
+ [{'edit-config',
+ [{target,["running"]},
+ {config,[{server,[{xmlns,"myns"}],
+ [{name,["myserver"]}]}]}]}]},
+ ?NS:expect_reply('edit-config',ok),
+ {'rpc-reply',RpcAttr2,[{ok,_,[]}]} = ct_netconfc:send(Client,RpcEditConf),
+
+ %% Send any data
+ ?NS:expect_reply(any,{ok,[],[]}),
+ {ok,_,[]} = ct_netconfc:send(Client,{any,[],[]}),
+
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client),
+ ok.
+
+hide_password(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,Client} = open_success(DataDir),
+ Password = "my_very_secret_password",
+ Data = [{passwords,[{xmlns,"myns"}],
+ [{password,[{xmlns,"pwdns"}],[Password]},
+ {password,[],[Password]}]}],
+ ?NS:expect_reply('get',{data,Data}),
+ ct:capture_start(), % in case of html logging
+ {ok,Data} = ct_netconfc:get(Client,{passwords,[{xmlns,"myns"}],[]}),
+ ct:capture_stop(),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client),
+
+ Log = filename:join(?config(priv_dir,Config),"hide_password-netconf.txt"),
+
+ Text =
+ case file:read_file(Log) of
+ {ok,Bin} ->
+ Bin;
+ _NoLog ->
+ %% Assume html logging
+ list_to_binary(ct:capture_get())
+ end,
+
+ nomatch = binary:match(Text,list_to_binary(Password)),
+
+ ok.
+
+not_proper_xml(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,Client} = open_success(DataDir),
+ NS = list_to_binary(?NETCONF_NAMESPACE),
+ NotProper = <<"<rpc-reply message-id=\"1\" xmlns=\"",
+ NS/binary,"\"><data></rpc-reply>">>,
+ ?NS:expect_reply('get',NotProper),
+ {error,{failed_to_parse_received_data,_}} =
+ ct_netconfc:get(Client,{server,[{xmlns,"myns"}],[]}),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client),
+ ok.
+
+prefixed_namespace(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,Client} = open_success(DataDir),
+ NS = list_to_binary(?NETCONF_NAMESPACE),
+
+ %% Test that data element can be properly decoded and that
+ %% prefixed namespace attributes (exepct the netconf namespace)
+ %% are forwarded to the content of the data element - i.e. that
+ %% the xmlns:my is forwarded from the rpc-reply element to the
+ %% server element below.
+ Data = <<"<nc:rpc-reply message-id=\"1\" xmlns:nc=\"",
+ NS/binary,"\" xmlns:my=\"myns\"><nc:data><my:server>",
+ "<my:name my:lang=\"en\">myserver</my:name></my:server>"
+ "</nc:data></nc:rpc-reply>">>,
+ ?NS:expect_reply('get',Data),
+ {ok,[{'my:server',[{'xmlns:my',"myns"}],
+ [{'my:name',[{'my:lang',"en"}],["myserver"]}]}]} =
+ ct_netconfc:get(Client,{server,[{xmlns,"myns"}],[]}),
+
+ Ok = <<"<nc:rpc-reply message-id=\"2\" xmlns:nc=\"",
+ NS/binary,"\"><nc:ok/></nc:rpc-reply>">>,
+ ?NS:expect_reply('edit-config',Ok),
+ ?ok = ct_netconfc:edit_config(Client,running,
+ {server,[{xmlns,"myns"}],
+ [{name,["myserver"]}]}),
+
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client),
+ ok.
+
+%% Test that the client can parse data which is received in chunks,
+%% i.e. when the complete rpc-reply is not contained in one single ssh
+%% data message.
+receive_chunked_data(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,Client} = open_success(DataDir),
+
+ %% Construct the data to return from netconf server
+ Data = [{servers,[{xmlns,"myns"}],
+ [{server,[],[{name,[],["server0"]}]},
+ {server,[],[{name,[],["server1"]}]},
+ {server,[],[{name,[],["server2"]}]},
+ {server,[],[{name,[],["server3"]}]},
+ {server,[],[{name,[],["server4"]}]},
+ {server,[],[{name,[],["server5"]}]},
+ {server,[],[{name,[],["server6"]}]},
+ {server,[],[{name,[],["server7"]}]},
+ {server,[],[{name,[],["server8"]}]},
+ {server,[],[{name,[],["server9"]}]}]
+ }],
+ Rpc = {'rpc-reply',?NETCONF_NAMESPACE_ATTR ++ [{'message-id',"1"}],
+ [{data,Data}]},
+ Xml = list_to_binary(xmerl:export_simple_element(Rpc,xmerl_xml)),
+ Netconf =
+ <<"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n",
+ Xml/binary,"\n",?END_TAG/binary>>,
+
+ %% Split the data in some chunks
+ PartLength = size(Netconf) div 3,
+ <<Part1:PartLength/binary,Part2:PartLength/binary,Part3:PartLength/binary,
+ Part4/binary>> = Netconf,
+
+ %% Spawn a process which will wait a bit for the client to send
+ %% the request (below), then order the server to the chunks of the
+ %% rpc-reply one by one.
+ spawn(fun() -> timer:sleep(500),?NS:hupp(send,Part1),
+ timer:sleep(100),?NS:hupp(send,Part2),
+ timer:sleep(100),?NS:hupp(send,Part3),
+ timer:sleep(100),?NS:hupp(send,Part4)
+ end),
+
+ %% Order server to expect a get - then the process above will make
+ %% sure the rpc-reply is sent.
+ ?NS:expect('get'),
+ {ok,Data} = ct_netconfc:get(Client,{server,[{xmlns,"myns"}],[]}),
+
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client),
+ ok.
+
+%% Same as receive_chunked_data, but timeout waiting for last part.
+timeout_receive_chunked_data(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,Client} = open_success(DataDir),
+
+ %% Construct the data to return from netconf server
+ Data = [{servers,[{xmlns,"myns"}],
+ [{server,[],[{name,[],["server0"]}]},
+ {server,[],[{name,[],["server1"]}]},
+ {server,[],[{name,[],["server2"]}]},
+ {server,[],[{name,[],["server3"]}]},
+ {server,[],[{name,[],["server4"]}]},
+ {server,[],[{name,[],["server5"]}]},
+ {server,[],[{name,[],["server6"]}]},
+ {server,[],[{name,[],["server7"]}]},
+ {server,[],[{name,[],["server8"]}]},
+ {server,[],[{name,[],["server9"]}]}]
+ }],
+ Rpc = {'rpc-reply',?NETCONF_NAMESPACE_ATTR ++ [{'message-id',"1"}],
+ [{data,Data}]},
+ Xml = list_to_binary(xmerl:export_simple_element(Rpc,xmerl_xml)),
+ Netconf =
+ <<"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n",
+ Xml/binary,"\n",?END_TAG/binary>>,
+
+ %% Split the data in some chunks
+ PartLength = size(Netconf) div 3,
+ <<Part1:PartLength/binary,Part2:PartLength/binary,_Part3:PartLength/binary,
+ _Part4/binary>> = Netconf,
+
+ %% Spawn a process which will wait a bit for the client to send
+ %% the request (below), then order the server to the chunks of the
+ %% rpc-reply one by one.
+ spawn(fun() -> timer:sleep(500),?NS:hupp(send,Part1),
+ timer:sleep(100),?NS:hupp(send,Part2)
+ end),
+
+ %% Order server to expect a get - then the process above will make
+ %% sure the rpc-reply is sent - but only a part of it - then timeout.
+ ?NS:expect('get'),
+ {error,timeout} = ct_netconfc:get(Client,{server,[{xmlns,"myns"}],[]},2000),
+
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client),
+ ok.
+
+%% Same as receive_chunked_data, but timeout waiting for last part.
+close_while_waiting_for_chunked_data(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,Client} = open_success(DataDir),
+
+ %% Construct the data to return from netconf server
+ Data = [{servers,[{xmlns,"myns"}],
+ [{server,[],[{name,[],["server0"]}]},
+ {server,[],[{name,[],["server1"]}]},
+ {server,[],[{name,[],["server2"]}]},
+ {server,[],[{name,[],["server3"]}]},
+ {server,[],[{name,[],["server4"]}]},
+ {server,[],[{name,[],["server5"]}]},
+ {server,[],[{name,[],["server6"]}]},
+ {server,[],[{name,[],["server7"]}]},
+ {server,[],[{name,[],["server8"]}]},
+ {server,[],[{name,[],["server9"]}]}]
+ }],
+ Rpc = {'rpc-reply',?NETCONF_NAMESPACE_ATTR ++ [{'message-id',"1"}],
+ [{data,Data}]},
+ Xml = list_to_binary(xmerl:export_simple_element(Rpc,xmerl_xml)),
+ Netconf =
+ <<"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n",
+ Xml/binary,"\n",?END_TAG/binary>>,
+
+ %% Split the data in some chunks
+ PartLength = size(Netconf) div 3,
+ <<Part1:PartLength/binary,Part2:PartLength/binary,_Part3:PartLength/binary,
+ _Part4/binary>> = Netconf,
+
+ %% Spawn a process which will wait a bit for the client to send
+ %% the request (below), then order the server to the chunks of the
+ %% rpc-reply one by one.
+ spawn(fun() -> timer:sleep(500),?NS:hupp(send,Part1),
+ timer:sleep(100),?NS:hupp(send,Part2),
+ timer:sleep(100),?NS:hupp(kill)
+ end),
+
+ %% Order server to expect a get - then the process above will make
+ %% sure the rpc-reply is sent - but only a part of it - then close.
+ ?NS:expect('get'),
+ {error,closed} = ct_netconfc:get(Client,{server,[{xmlns,"myns"}],[]},2000),
+ ok.
+
+connection_crash(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,Client} = open_success(DataDir),
+
+ %% Test that if the test survives killing the connection
+ %% process. Earlier this caused ct_util_server to terminate, and
+ %% this aborting the complete test run.
+ spawn(fun() -> timer:sleep(500),exit(Client,kill) end),
+ ?NS:expect(get),
+ {error,{closed,killed}}=ct_netconfc:get(Client,{server,[{xmlns,"myns"}],[]}),
+ ok.
+
+get_event_streams(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,Client} = open_success(DataDir),
+ StreamNames = ["NETCONF","stream1","stream2"],
+ Streams = [{N,[{description,"descr of " ++ N}]} || N <- StreamNames],
+ StreamsXml = [{stream,[{name,[N]}|[{Tag,[Value]} || {Tag,Value} <- Data]]}
+ || {N,Data} <- Streams],
+ ReplyData = [{netconf,?NETMOD_NOTIF_NAMESPACE_ATTR,[{streams,StreamsXml}]}],
+ ?NS:expect_reply('get',{data,ReplyData}),
+ {ok,Streams} = ct_netconfc:get_event_streams(Client,StreamNames),
+
+ ?NS:expect_reply('get',{data,ReplyData}),
+ {ok,Streams} = ct_netconfc:get_event_streams(Client,StreamNames,5000),
+
+ ?NS:expect('get'),
+ {error,timeout} = ct_netconfc:get_event_streams(Client,100),
+
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client),
+ ok.
+
+create_subscription(Config) ->
+ DataDir = ?config(data_dir,Config),
+
+ %% All defaults
+ {ok,Client1} = open_success(DataDir),
+ ?NS:expect_reply({'create-subscription',[stream]},ok),
+ ?ok = ct_netconfc:create_subscription(Client1),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client1),
+
+ %% All defaults with timeout
+ {ok,Client1a} = open_success(DataDir),
+ ?NS:expect_reply({'create-subscription',[stream]},ok),
+ ?ok = ct_netconfc:create_subscription(Client1a,5000),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client1a),
+
+ %% All defaults timing out
+ {ok,Client1b} = open_success(DataDir),
+ ?NS:expect({'create-subscription',[stream]}),
+ {error,timeout} = ct_netconfc:create_subscription(Client1b,100),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client1b),
+
+ %% Stream
+ {ok,Client2} = open_success(DataDir),
+ ?NS:expect_reply({'create-subscription',[stream]},ok),
+ Stream = "some_stream",
+ ?ok = ct_netconfc:create_subscription(Client2,Stream),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client2),
+
+ %% Filter
+ {ok,Client3} = open_success(DataDir),
+ ?NS:expect_reply({'create-subscription',[stream,filter]},ok),
+ Filter = {notification,?NETMOD_NOTIF_NAMESPACE_ATTR,
+ [eventTime]},
+ ?ok = ct_netconfc:create_subscription(Client3,Filter),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client3),
+
+ %% Filter with timeout
+ {ok,Client3a} = open_success(DataDir),
+ ?NS:expect_reply({'create-subscription',[stream,filter]},ok),
+ ?ok = ct_netconfc:create_subscription(Client3a,Filter,5000),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client3a),
+
+ %% Filter timing out
+ {ok,Client3b} = open_success(DataDir),
+ ?NS:expect({'create-subscription',[stream,filter]}),
+ {error,timeout}=ct_netconfc:create_subscription(Client3b,Filter,100),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client3b),
+
+ %% Stream and filter
+ {ok,Client4} = open_success(DataDir),
+ ?NS:expect_reply({'create-subscription',[stream,filter]},ok),
+ ?ok = ct_netconfc:create_subscription(Client4,Stream,Filter),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client4),
+
+ %% Start/stop time
+ {ok,Client5} = open_success(DataDir),
+ ?NS:expect_reply({'create-subscription',[stream,startTime,stopTime]},ok),
+ StartTime = xs_datetime({D,{H,M,S}}= calendar:local_time()),
+ StopTime = xs_datetime({D,{H+2,M,S}}),
+ ?ok = ct_netconfc:create_subscription(Client5,StartTime,StopTime),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client5),
+
+ %% Start/stop time with timeout
+ {ok,Client5a} = open_success(DataDir),
+ ?NS:expect_reply({'create-subscription',[stream,startTime,stopTime]},ok),
+ ?ok = ct_netconfc:create_subscription(Client5a,StartTime,StopTime,5000),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client5a),
+
+ %% Start/stop time timing out
+ {ok,Client5b} = open_success(DataDir),
+ ?NS:expect({'create-subscription',[stream,startTime,stopTime]}),
+ {error,timeout} =
+ ct_netconfc:create_subscription(Client5b,StartTime,StopTime,100),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client5b),
+
+ %% Stream and start/stop time
+ {ok,Client6} = open_success(DataDir),
+ ?NS:expect_reply({'create-subscription',[stream,startTime,stopTime]},ok),
+ ?ok = ct_netconfc:create_subscription(Client6,Stream,StartTime,StopTime),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client6),
+
+ %% Filter and start/stop time
+ {ok,Client7} = open_success(DataDir),
+ ?NS:expect_reply({'create-subscription',[stream,filter,startTime,stopTime]},
+ ok),
+ ?ok = ct_netconfc:create_subscription(Client7,Filter,
+ StartTime,StopTime),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client7),
+
+ %% Stream, filter and start/stop time
+ {ok,Client8} = open_success(DataDir),
+ ?NS:expect_reply({'create-subscription',[stream,filter,startTime,stopTime]},
+ ok),
+ ?ok = ct_netconfc:create_subscription(Client8,Stream,Filter,
+ StartTime,StopTime),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client8),
+
+ ok.
+
+receive_event(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,Client} = open_success(DataDir),
+ ?NS:expect_reply({'create-subscription',[stream]},ok),
+ ?ok = ct_netconfc:create_subscription(Client),
+
+ ?NS:hupp(send_event),
+
+ receive
+ %% Matching ?NS:make_msg(event)
+ {notification,?NETCONF_NOTIF_NAMESPACE_ATTR,
+ [{eventTime,[],[_Time]},
+ {event,[{xmlns,"http://my.namespaces.com/event"}],
+ [{severity,_,_},
+ {description,_,_}]}]} ->
+ ok;
+ Other ->
+ ct:fail({got_unexpected_while_waiting_for_event, Other})
+ after 3000 ->
+ ct:fail(timeout_waiting_for_event)
+ end,
+
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client),
+
+ ok.
+
+%%%-----------------------------------------------------------------
+
+break(_Config) ->
+ test_server:break("break test case").
+
+br() ->
+ test_server:break("").
+
+%%%-----------------------------------------------------------------
+%% Open a netconf session which is not specified in a config file
+open_success(Dir) ->
+ open_success(Dir,[]).
+
+%% Open a netconf session which is not specified in a config file, and
+%% give som extra options in addition to the test defaults.
+open_success(Dir,ExtraOpts) when is_list(Dir), is_list(ExtraOpts) ->
+ ?NS:hello(1), % tell server to send hello with session id 1
+ ?NS:expect(hello), % tell server to expect a hello message from client
+ open(Dir,ExtraOpts);
+
+%% Open a named netconf session which is not specified in a config file
+open_success(KeyOrName,Dir) when is_atom(KeyOrName), is_list(Dir) ->
+ ?NS:hello(1),
+ ?NS:expect(hello),
+ ct_netconfc:open(KeyOrName,?DEFAULT_SSH_OPTS(Dir)).
+
+open(Dir) ->
+ open(Dir,[]).
+open(Dir,ExtraOpts) ->
+ Opts = lists:ukeymerge(1,lists:keysort(1,ExtraOpts),
+ lists:keysort(1,?DEFAULT_SSH_OPTS(Dir))),
+ ct_netconfc:open(Opts).
+
+%%%-----------------------------------------------------------------
+%%% Open a netconf session which is specified in a config file
+%%% KeyOrName is the config key (server_id()) or name given in a
+%%% require statement (target_name()).
+open_configured_success(KeyOrName,Dir) when is_atom(KeyOrName) ->
+ open_configured_success(KeyOrName,Dir,[]).
+open_configured_success(KeyOrName,Dir,ExtraOpts) when is_atom(KeyOrName) ->
+ ?NS:hello(1),
+ ?NS:expect(hello),
+ ct_netconfc:open(KeyOrName,[{user_dir,Dir}|ExtraOpts]).
+
+%%%-----------------------------------------------------------------
+%%% Convert erlang datetime to the simplest variant of XML dateTime
+xs_datetime({{Y,M,D},{H,Mi,S}}) ->
+ lists:flatten(
+ io_lib:format("~p-~s-~sT~s:~s:~s",[Y,pad(M),pad(D),pad(H),pad(Mi),pad(S)])).
+
+pad(I) when I<10 ->
+ "0"++integer_to_list(I);
+pad(I) ->
+ integer_to_list(I).
+
+
+%%%-----------------------------------------------------------------
+%%% BEGIN SSH key management
+%% copy private keys to given dir from ~/.ssh
+get_id_keys(Config) ->
+ DstDir = ?config(priv_dir, Config),
+ SrcDir = filename:join(os:getenv("HOME"), ".ssh"),
+ RsaOk = copyfile(SrcDir, DstDir, "id_rsa"),
+ DsaOk = copyfile(SrcDir, DstDir, "id_dsa"),
+ case {RsaOk, DsaOk} of
+ {{ok, _}, {ok, _}} -> {ok, both};
+ {{ok, _}, _} -> {ok, rsa};
+ {_, {ok, _}} -> {ok, dsa};
+ {Error, _} -> Error
+ end.
+
+%% Remove later on. Use make_dsa_files instead.
+remove_id_keys(Config) ->
+ Dir = ?config(priv_dir, Config),
+ file:delete(filename:join(Dir, "id_rsa")),
+ file:delete(filename:join(Dir, "id_dsa")).
+
+
+make_dsa_files(Config) ->
+ make_dsa_files(Config, rfc4716_public_key).
+make_dsa_files(Config, Type) ->
+ {DSA, EncodedKey} = gen_dsa(128, 20),
+ PKey = DSA#'DSAPrivateKey'.y,
+ P = DSA#'DSAPrivateKey'.p,
+ Q = DSA#'DSAPrivateKey'.q,
+ G = DSA#'DSAPrivateKey'.g,
+ Dss = #'Dss-Parms'{p=P, q=Q, g=G},
+ {ok, Hostname} = inet:gethostname(),
+ {ok, {A, B, C, D}} = inet:getaddr(Hostname, inet),
+ IP = lists:concat([A, ".", B, ".", C, ".", D]),
+ Attributes = [], % Could be [{comment,"user@" ++ Hostname}],
+ HostNames = [{hostnames,[IP, IP]}],
+ PublicKey = [{{PKey, Dss}, Attributes}],
+ KnownHosts = [{{PKey, Dss}, HostNames}],
+
+ KnownHostsEnc = public_key:ssh_encode(KnownHosts, known_hosts),
+ KnownHosts = public_key:ssh_decode(KnownHostsEnc, known_hosts),
+
+ PublicKeyEnc = public_key:ssh_encode(PublicKey, Type),
+
+ SystemTmpDir = ?config(data_dir, Config),
+ filelib:ensure_dir(SystemTmpDir),
+ file:make_dir(SystemTmpDir),
+
+ DSAFile = filename:join(SystemTmpDir, "ssh_host_dsa_key.pub"),
+ file:delete(DSAFile),
+
+ DSAPrivateFile = filename:join(SystemTmpDir, "ssh_host_dsa_key"),
+ file:delete(DSAPrivateFile),
+
+ KHFile = filename:join(SystemTmpDir, "known_hosts"),
+ file:delete(KHFile),
+
+ PemBin = public_key:pem_encode([EncodedKey]),
+
+ file:write_file(DSAFile, PublicKeyEnc),
+ file:write_file(KHFile, KnownHostsEnc),
+ file:write_file(DSAPrivateFile, PemBin),
+ ok.
+
+%%--------------------------------------------------------------------
+%% Creates a dsa key (OBS: for testing only)
+%% the sizes are in bytes
+%% gen_dsa(::integer()) -> {::atom(), ::binary(), ::opaque()}
+%%--------------------------------------------------------------------
+gen_dsa(LSize,NSize) when is_integer(LSize), is_integer(NSize) ->
+ Key = gen_dsa2(LSize, NSize),
+ {Key, encode_key(Key)}.
+
+encode_key(Key = #'DSAPrivateKey'{}) ->
+ Der = public_key:der_encode('DSAPrivateKey', Key),
+ {'DSAPrivateKey', Der, not_encrypted}.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% 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.
+
+copyfile(SrcDir, DstDir, Fn) ->
+ file:copy(filename:join(SrcDir, Fn),
+ filename:join(DstDir, Fn)).
+
+%%% END SSH key management
+%%%-----------------------------------------------------------------
diff --git a/lib/common_test/test/ct_netconfc_SUITE_data/ns.erl b/lib/common_test/test/ct_netconfc_SUITE_data/ns.erl
new file mode 100644
index 0000000000..2427f37f52
--- /dev/null
+++ b/lib/common_test/test/ct_netconfc_SUITE_data/ns.erl
@@ -0,0 +1,555 @@
+%%--------------------------------------------------------------------
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, 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 netconf server used for testing of netconfc
+-module(ns).
+
+%-compile(export_all).
+-include_lib("common_test/src/ct_netconfc.hrl").
+
+
+%%%-----------------------------------------------------------------
+%%% API
+-export([start/1,
+ stop/1,
+ hello/1,
+ hello/2,
+ expect/1,
+ expect/2,
+ expect_reply/2,
+ expect_reply/3,
+ expect_do/2,
+ expect_do/3,
+ expect_do_reply/3,
+ expect_do_reply/4,
+ hupp/1,
+ hupp/2]).
+
+%%%-----------------------------------------------------------------
+%%% ssh_channel callbacks
+-export([init/1,
+ terminate/2,
+ handle_ssh_msg/2,
+ handle_msg/2]).
+
+%%%-----------------------------------------------------------------
+%% Server specifications
+-define(SERVER_DATA_NAMESPACE, "ClientTest").
+-define(CAPABILITIES,?CAPABILITIES_VSN("1.0")).
+-define(CAPABILITIES_VSN(Vsn),
+ [
+ ?NETCONF_BASE_CAP ++ Vsn,
+ "urn:ietf:params:netconf:capability:writable-running:1.0",
+ "urn:ietf:params:netconf:capability:candidate:1.0",
+ "urn:ietf:params:netconf:capability:confirmed-commit:1.0",
+ "urn:ietf:params:netconf:capability:rollback-on-error:1.0",
+ "urn:ietf:params:netconf:capability:startup:1.0",
+ "urn:ietf:params:netconf:capability:url:1.0",
+ "urn:ietf:params:netconf:capability:xpath:1.0",
+ "urn:ietf:params:netconf:capability:notification:1.0",
+ "urn:ietf:params:netconf:capability:interleave:1.0",
+ ?ACTION_NAMESPACE,
+ ?SERVER_DATA_NAMESPACE
+ ]).
+-define(SSH_PORT, 2060).
+-define(ssh_config(Dir),[{port, ?SSH_PORT},
+ {interface, {127,0,0,1}},
+ {system_dir, Dir},
+ {user_dir, Dir},
+ {user_passwords, [{"xxx","xxx"}]},
+ {password, "global-xxx"}]).
+
+%% Some help for debugging
+%-define(dbg(F,A),io:format(F,A)).
+-define(dbg(F,A),ok).
+-define(dbg_event(Event,Expect),
+ ?dbg("Event: ~p~nExpected: ~p~n",[Event,Expect])).
+
+%% State
+-record(session, {cb,
+ connection,
+ buffer = <<>>,
+ session_id}).
+
+
+%%%-----------------------------------------------------------------
+%%% API
+
+%% Start the netconf server and use the given directory as system_dir
+%% and user_dir
+start(Dir) ->
+ spawn(fun() -> init_server(Dir) end).
+
+%% Stop the netconf server
+stop(Pid) ->
+ Pid ! {stop,self()},
+ receive stopped -> ok end.
+
+%% Set the session id for the hello message.
+%% If this is not called prior to starting the session, no hello
+%% message will be sent.
+%% 'Stuff' indicates some special handling to e.g. provoke error cases
+hello(SessionId) ->
+ hello(SessionId,undefined).
+hello(SessionId,Stuff) ->
+ insert(hello,{SessionId,Stuff}).
+
+%% Tell server to expect the given message without doing any further
+%% actions. To be called directly before sending a request.
+expect(Expect) ->
+ expect_do_reply(Expect,undefined,undefined).
+expect(SessionId,Expect) ->
+ expect_do_reply(SessionId,Expect,undefined,undefined).
+
+%% Tell server to expect the given message and reply with the give
+%% reply. To be called directly before sending a request.
+expect_reply(Expect,Reply) ->
+ expect_do_reply(Expect,undefined,Reply).
+expect_reply(SessionId,Expect,Reply) ->
+ expect_do_reply(SessionId,Expect,undefined,Reply).
+
+%% Tell server to expect the given message and perform an action. To
+%% be called directly before sending a request.
+expect_do(Expect,Do) ->
+ expect_do_reply(Expect,Do,undefined).
+expect_do(SessionId,Expect,Do) ->
+ expect_do_reply(SessionId,Expect,Do,undefined).
+
+%% Tell server to expect the given message, perform an action and
+%% reply with the given reply. To be called directly before sending a
+%% request.
+expect_do_reply(Expect,Do,Reply) ->
+ add_expect(1,{Expect,Do,Reply}).
+expect_do_reply(SessionId,Expect,Do,Reply) ->
+ add_expect(SessionId,{Expect,Do,Reply}).
+
+%% Hupp the server - i.e. tell it to do something -
+%% e.g. hupp(send_event) will cause send_event(State) to be called on
+%% the session channel process.
+hupp(send_event) ->
+ hupp(send,[make_msg(event)]);
+hupp(kill) ->
+ hupp(1,fun hupp_kill/1,[]).
+
+hupp(send,Data) ->
+ hupp(1,fun hupp_send/2,[Data]).
+
+hupp(SessionId,Fun,Args) when is_function(Fun) ->
+ [{_,Pid}] = lookup({channel_process,SessionId}),
+ Pid ! {hupp,Fun,Args}.
+
+%%%-----------------------------------------------------------------
+%%% Main loop of the netconf server
+init_server(Dir) ->
+ register(main_ns_proc,self()),
+ ets:new(ns_tab,[set,named_table,public]),
+ Config = ?ssh_config(Dir),
+ {_,Host} = lists:keyfind(interface, 1, Config),
+ {_,Port} = lists:keyfind(port, 1, Config),
+ Opts = lists:filter(fun({Key,_}) ->
+ lists:member(Key,[system_dir,
+ password,
+ user_passwords,
+ pwdfun])
+ end,
+ Config),
+ {ok, Daemon} =
+ ssh:daemon(Host, Port,
+ [{subsystems,[{"netconf",{?MODULE,[]}}]}
+ |Opts]),
+ loop(Daemon).
+
+loop(Daemon) ->
+ receive
+ {stop,From} ->
+ ssh:stop_daemon(Daemon),
+ From ! stopped;
+ {table_trans,Fun,Args,From} ->
+ %% Simple transaction mechanism for ets table
+ R = apply(Fun,Args),
+ From ! {table_trans_done,R},
+ loop(Daemon)
+ end.
+
+%%----------------------------------------------------------------------
+%% Behaviour callback functions (ssh_channel)
+%%----------------------------------------------------------------------
+init([]) ->
+ {ok, undefined}.
+
+terminate(_Reason, _State) ->
+ ok.
+
+handle_ssh_msg({ssh_cm,CM,{data, Ch, _Type = 0, Data}}, State) ->
+ %% io:format("~p~n",[{self(),Data,CM,Ch,State}]),
+ data_for_channel(CM, Ch, Data, State);
+handle_ssh_msg({ssh_cm,CM,{closed, Ch}}, State) ->
+ %% erlang:display({self(),closed,CM,Ch,State}),
+ stop_channel(CM, Ch, State);
+handle_ssh_msg({ssh_cm,CM,{eof, Ch}}, State) ->
+ %% erlang:display({self(),eof,CM,Ch,State}),
+ data_for_channel(CM,Ch, <<>>, State).
+
+
+handle_msg({'EXIT', _Pid, _Reason}, State) ->
+ {ok, State};
+handle_msg({ssh_channel_up,Ch,CM},undefined) ->
+ %% erlang:display({self(),up,CM,Ch}),
+ ConnRef = {CM,Ch},
+ SessionId = maybe_hello(ConnRef),
+ insert({channel_process,SessionId},self()), % used to hupp the server
+ {ok, #session{connection = ConnRef,
+ session_id = SessionId}};
+handle_msg({hupp,Fun,Args},State) ->
+ {ok,apply(Fun,Args ++ [State])}.
+
+data_for_channel(CM, Ch, Data, State) ->
+ try data(Data, State) of
+ {ok, NewState} ->
+ case erase(stop) of
+ true ->
+ stop_channel(CM, Ch, NewState);
+ _ ->
+ {ok, NewState}
+ end
+ catch
+ Class:Reason ->
+ Stacktrace = erlang:get_stacktrace(),
+ error_logger:error_report([{?MODULE, data_for_channel},
+ {request, Data},
+ {buffer, State#session.buffer},
+ {reason, {Class, Reason}},
+ {stacktrace, Stacktrace}]),
+ stop_channel(CM, Ch, State)
+ end.
+
+data(Data, State = #session{connection = ConnRef,
+ buffer = Buffer,
+ session_id = SessionId}) ->
+ AllData = <<Buffer/binary,Data/binary>>,
+ case find_endtag(AllData) of
+ {ok,Msgs,Rest} ->
+ [check_expected(SessionId,ConnRef,Msg) || Msg <- Msgs],
+ {ok,State#session{buffer=Rest}};
+ need_more ->
+ {ok,State#session{buffer=AllData}}
+ end.
+
+stop_channel(CM, Ch, State) ->
+ ssh:close(CM),
+ {stop, Ch, State}.
+
+
+%%%-----------------------------------------------------------------
+%%% Functions to trigg via hupp/1:
+
+%% Send data spontaneously - e.g. an event
+hupp_send(Data,State = #session{connection = ConnRef}) ->
+ send(ConnRef,Data),
+ State.
+hupp_kill(State = #session{connection = ConnRef}) ->
+ kill(ConnRef),
+ State.
+
+%%%-----------------------------------------------------------------
+%%% Internal functions
+
+
+%%% Send ssh data to the client
+send({CM,Ch},Data) ->
+ ssh_connection:send(CM, Ch, Data).
+
+%%% Kill ssh connection
+kill({CM,_Ch}) ->
+ ssh:close(CM).
+
+add_expect(SessionId,Add) ->
+ table_trans(fun do_add_expect/2,[SessionId,Add]).
+
+table_trans(Fun,Args) ->
+ S = self(),
+ case whereis(main_ns_proc) of
+ S ->
+ apply(Fun,Args);
+ Pid ->
+ Pid ! {table_trans,Fun,Args,self()},
+ receive
+ {table_trans_done,Result} ->
+ Result
+ after 5000 ->
+ exit(table_trans_timeout)
+ end
+ end.
+
+do_add_expect(SessionId,Add) ->
+ case lookup({expect,SessionId}) of
+ [] ->
+ insert({expect,SessionId},[Add]);
+ [{_,First}] ->
+ insert({expect,SessionId},First ++ [Add])
+ end,
+ ok.
+
+do_get_expect(SessionId) ->
+ case lookup({expect,SessionId}) of
+ [{_,[{Expect,Do,Reply}|Rest]}] ->
+ insert({expect,SessionId},Rest),
+ {Expect,Do,Reply};
+ _ ->
+ error
+ end.
+
+insert(Key,Value) ->
+ ets:insert(ns_tab,{Key,Value}).
+lookup(Key) ->
+ ets:lookup(ns_tab,Key).
+
+maybe_hello(ConnRef) ->
+ case lookup(hello) of
+ [{hello,{SessionId,Stuff}}] ->
+ %% erlang:display({SessionId,Stuff}),
+ ets:delete(ns_tab,hello),
+ insert({session,SessionId},ConnRef),
+ reply(ConnRef,{hello,SessionId,Stuff}),
+ SessionId;
+ [] ->
+ undefined
+ end.
+
+find_endtag(Data) ->
+ case binary:split(Data,[?END_TAG],[global]) of
+ [Data] ->
+ need_more;
+ Msgs ->
+ {ok,lists:sublist(Msgs,length(Msgs)-1),lists:last(Msgs)}
+ end.
+
+check_expected(SessionId,ConnRef,Msg) ->
+ %% io:format("~p~n",[{check_expected,SessionId,Msg}]),
+ case table_trans(fun do_get_expect/1,[SessionId]) of
+ {Expect,Do,Reply} ->
+ %% erlang:display({got,io_lib:format("~s",[Msg])}),
+ %% erlang:display({expected,Expect}),
+ match(Msg,Expect),
+ do(ConnRef, Do),
+ reply(ConnRef,Reply);
+ error ->
+ timer:sleep(1000),
+ exit({error,{got_unexpected,SessionId,Msg,ets:tab2list(ns_tab)}})
+ end.
+
+match(Msg,Expect) ->
+ ?dbg("Match: ~p~n",[Msg]),
+ {ok,ok,<<>>} = xmerl_sax_parser:stream(Msg,[{event_fun,fun event/3},
+ {event_state,Expect}]).
+
+event(Event,_Loc,Expect) ->
+ ?dbg_event(Event,Expect),
+ event(Event,Expect).
+
+event(startDocument,Expect) -> match(Expect);
+event({startElement,_,Name,_,Attrs},[{se,Name}|Match]) ->
+ msg_id(Name,Attrs),
+ Match;
+event({startElement,_,Name,_,Attrs},[ignore,{se,Name}|Match]) ->
+ msg_id(Name,Attrs),
+ Match;
+event({startElement,_,Name,_,Attrs},[{se,Name,As}|Match]) ->
+ msg_id(Name,Attrs),
+ match_attrs(Name,As,Attrs),
+ Match;
+event({startElement,_,Name,_,Attrs},[ignore,{se,Name,As}|Match]) ->
+ msg_id(Name,Attrs),
+ match_attrs(Name,As,Attrs),
+ Match;
+event({startPrefixMapping,_,Ns},[{ns,Ns}|Match]) -> Match;
+event({startPrefixMapping,_,Ns},[ignore,{ns,Ns}|Match]) -> Match;
+event({endPrefixMapping,_},Match) -> Match;
+event({endElement,_,Name,_},[{ee,Name}|Match]) -> Match;
+event({endElement,_,Name,_},[ignore,{ee,Name}|Match]) -> Match;
+event(endDocument,Match) when Match==[]; Match==[ignore] -> ok;
+event(_,[ignore|_]=Match) -> Match;
+event(Event,Match) -> throw({nomatch,{Event,Match}}).
+
+msg_id("rpc",Attrs) ->
+ case lists:keyfind("message-id",3,Attrs) of
+ {_,_,_,Str} -> put(msg_id,Str);
+ false -> erase(msg_id)
+ end;
+msg_id(_,_) ->
+ ok.
+
+match_attrs(Name,[{Key,Value}|As],Attrs) ->
+ case lists:keyfind(atom_to_list(Key),3,Attrs) of
+ {_,_,_,Value} -> match_attrs(Name,As,Attrs);
+ false -> throw({missing_attr,Key,Name,Attrs});
+ _ -> throw({faulty_attr_value,Key,Name,Attrs})
+ end;
+match_attrs(_,[],_) ->
+ ok.
+
+do(ConnRef, close) ->
+ ets:match_delete(ns_tab,{{session,'_'},ConnRef}),
+ put(stop,true);
+do(_ConnRef, {kill,SessionId}) ->
+ case lookup({session,SessionId}) of
+ [{_,Owner}] ->
+ ets:delete(ns_tab,{session,SessionId}),
+ kill(Owner);
+ _ ->
+ exit({no_session_to_kill,SessionId})
+ end;
+do(_, undefined) ->
+ ok.
+
+reply(_,undefined) ->
+ ?dbg("no reply~n",[]),
+ ok;
+reply(ConnRef,Reply) ->
+ ?dbg("Reply: ~p~n",[Reply]),
+ send(ConnRef, make_msg(Reply)).
+
+from_simple(Simple) ->
+ list_to_binary(xmerl:export_simple_element(Simple,xmerl_xml)).
+
+xml(Content) ->
+ <<"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n",
+ Content/binary,"\n",?END_TAG/binary>>.
+
+rpc_reply(Content) when is_binary(Content) ->
+ MsgId = case erase(msg_id) of
+ undefined -> <<>>;
+ Id -> list_to_binary([" message-id=\"",Id,"\""])
+ end,
+ <<"<rpc-reply xmlns=\"",?NETCONF_NAMESPACE,"\"",MsgId/binary,">\n",
+ Content/binary,"\n</rpc-reply>">>;
+rpc_reply(Content) ->
+ rpc_reply(list_to_binary(Content)).
+
+session_id(no_session_id) ->
+ <<>>;
+session_id(SessionId0) ->
+ SessionId = list_to_binary(integer_to_list(SessionId0)),
+ <<"<session-id>",SessionId/binary,"</session-id>\n">>.
+
+capabilities(undefined) ->
+ CapsXml = list_to_binary([["<capability>",C,"</capability>\n"]
+ || C <- ?CAPABILITIES]),
+ <<"<capabilities>\n",CapsXml/binary,"</capabilities>\n">>;
+capabilities({base,Vsn}) ->
+ CapsXml = list_to_binary([["<capability>",C,"</capability>\n"]
+ || C <- ?CAPABILITIES_VSN(Vsn)]),
+ <<"<capabilities>\n",CapsXml/binary,"</capabilities>\n">>;
+capabilities(no_base) ->
+ [_|Caps] = ?CAPABILITIES,
+ CapsXml = list_to_binary([["<capability>",C,"</capability>\n"] || C <- Caps]),
+ <<"<capabilities>\n",CapsXml/binary,"</capabilities>\n">>;
+capabilities(no_caps) ->
+ <<>>.
+
+%%%-----------------------------------------------------------------
+%%% Match received netconf message from the client. Add a new clause
+%%% for each new message to recognize. The clause argument shall match
+%%% the Expect argument in expect/1, expect_reply/2 or
+%%% expect_do_reply/3.
+%%%
+%%% match(term()) -> [Match].
+%%% Match = ignore | {se,Name} | {se,Name,Attrs} | {ee,Name} | {ns,Namespace}
+%%% Name = string()
+%%% Attrs = [{atom(),string()}]
+%%% Namespace = string()
+%%%
+%%% 'se' means start element, 'ee' means end element - i.e. to match
+%%% an XML element you need one 'se' entry and one 'ee' entry with the
+%%% same name in the match list.
+match(hello) ->
+ [ignore,{se,"hello"},ignore,{ee,"hello"},ignore];
+match('close-session') ->
+ [ignore,{se,"rpc"},{se,"close-session"},
+ {ee,"close-session"},{ee,"rpc"},ignore];
+match('edit-config') ->
+ [ignore,{se,"rpc"},{se,"edit-config"},{se,"target"},ignore,{ee,"target"},
+ {se,"config"},ignore,{ee,"config"},{ee,"edit-config"},{ee,"rpc"},ignore];
+match('get') ->
+ match({get,subtree});
+match({'get',FilterType}) ->
+ [ignore,{se,"rpc"},{se,"get"},{se,"filter",[{type,atom_to_list(FilterType)}]},
+ ignore,{ee,"filter"},{ee,"get"},{ee,"rpc"},ignore];
+match('get-config') ->
+ match({'get-config',subtree});
+match({'get-config',FilterType}) ->
+ [ignore,{se,"rpc"},{se,"get-config"},{se,"source"},ignore,{ee,"source"},
+ {se,"filter",[{type,atom_to_list(FilterType)}]},ignore,{ee,"filter"},
+ {ee,"get-config"},{ee,"rpc"},ignore];
+match('copy-config') ->
+ [ignore,{se,"rpc"},{se,"copy-config"},{se,"target"},ignore,{ee,"target"},
+ {se,"source"},ignore,{ee,"source"},{ee,"copy-config"},{ee,"rpc"},ignore];
+match('delete-config') ->
+ [ignore,{se,"rpc"},{se,"delete-config"},{se,"target"},ignore,{ee,"target"},
+ {ee,"delete-config"},{ee,"rpc"},ignore];
+match('lock') ->
+ [ignore,{se,"rpc"},{se,"lock"},{se,"target"},ignore,{ee,"target"},
+ {ee,"lock"},{ee,"rpc"},ignore];
+match('unlock') ->
+ [ignore,{se,"rpc"},{se,"unlock"},{se,"target"},ignore,{ee,"target"},
+ {ee,"unlock"},{ee,"rpc"},ignore];
+match('kill-session') ->
+ [ignore,{se,"rpc"},{se,"kill-session"},{se,"session-id"},ignore,
+ {ee,"session-id"},{ee,"kill-session"},{ee,"rpc"},ignore];
+match(action) ->
+ [ignore,{se,"rpc"},{ns,?ACTION_NAMESPACE},{se,"action"},{se,"data"},ignore,
+ {ee,"data"},{ee,"action"},{ee,"rpc"},ignore];
+match({'create-subscription',Content}) ->
+ [ignore,{se,"rpc"},{ns,?NETCONF_NOTIF_NAMESPACE},
+ {se,"create-subscription"}] ++
+ lists:flatmap(fun(X) ->
+ [{se,atom_to_list(X)},ignore,{ee,atom_to_list(X)}]
+ end, Content) ++
+ [{ee,"create-subscription"},{ee,"rpc"},ignore];
+match(any) ->
+ [ignore].
+
+
+
+%%%-----------------------------------------------------------------
+%%% Make message to send to the client.
+%%% Add a new clause for each new message that shall be sent. The
+%%% clause shall match the Reply argument in expect_reply/2 or
+%%% expect_do_reply/3.
+make_msg({hello,SessionId,Stuff}) ->
+ SessionIdXml = session_id(SessionId),
+ CapsXml = capabilities(Stuff),
+ xml(<<"<hello xmlns=\"",?NETCONF_NAMESPACE,"\">\n",CapsXml/binary,
+ SessionIdXml/binary,"</hello>">>);
+make_msg(ok) ->
+ xml(rpc_reply("<ok/>"));
+make_msg({data,Data}) ->
+ xml(rpc_reply(from_simple({data,Data})));
+make_msg(event) ->
+ xml(<<"<notification xmlns=\"",?NETCONF_NOTIF_NAMESPACE,"\">"
+ "<eventTime>2012-06-14T14:50:54+02:00</eventTime>"
+ "<event xmlns=\"http://my.namespaces.com/event\">"
+ "<severity>major</severity>"
+ "<description>Something terrible happened</description>"
+ "</event>"
+ "</notification>">>);
+make_msg(Xml) when is_binary(Xml) ->
+ xml(Xml);
+make_msg(Simple) when is_tuple(Simple) ->
+ xml(from_simple(Simple)).
diff --git a/lib/common_test/test/ct_priv_dir_SUITE_data/priv_dir_SUITE.erl b/lib/common_test/test/ct_priv_dir_SUITE_data/priv_dir_SUITE.erl
index 423cb2999b..7704a29768 100644
--- a/lib/common_test/test/ct_priv_dir_SUITE_data/priv_dir_SUITE.erl
+++ b/lib/common_test/test/ct_priv_dir_SUITE_data/priv_dir_SUITE.erl
@@ -1,11 +1,21 @@
-%%%-------------------------------------------------------------------
-%%% @author Peter Andersson <[email protected]>
-%%% @copyright (C) 2012, Peter Andersson
-%%% @doc
-%%%
-%%% @end
-%%% Created : 23 Jan 2012 by Peter Andersson <[email protected]>
-%%%-------------------------------------------------------------------
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2009-2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, 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(priv_dir_SUITE).
-compile(export_all).
diff --git a/lib/common_test/test/ct_shell_SUITE.erl b/lib/common_test/test/ct_shell_SUITE.erl
new file mode 100644
index 0000000000..4b8c43d800
--- /dev/null
+++ b/lib/common_test/test/ct_shell_SUITE.erl
@@ -0,0 +1,133 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, 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_shell_SUITE
+%%%
+%%% Description:
+%%% Test that the interactive mode starts properly
+%%%
+%%% The suites used for the test are located in the data directory.
+%%%-------------------------------------------------------------------
+-module(ct_shell_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
+%%--------------------------------------------------------------------
+
+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() ->
+ [start_interactive].
+
+%%--------------------------------------------------------------------
+%% TEST CASES
+%%--------------------------------------------------------------------
+
+%%%-----------------------------------------------------------------
+%%%
+start_interactive(Config) ->
+ DataDir = ?config(data_dir, Config),
+ CfgFile = filename:join(DataDir, "cfgdata"),
+
+ {Opts,ERPid} = setup([{interactive_mode,true},{config,CfgFile}],
+ Config),
+ CTNode = proplists:get_value(ct_node, Config),
+ Level = proplists:get_value(trace_level, Config),
+ 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, "Calling ct_run:script_start() on ~p~n",
+ [CTNode]),
+
+ interactive_mode = rpc:call(CTNode, ct_run, script_start, []),
+
+ ok = rpc:call(CTNode, ct, require, [key1]),
+ value1 = rpc:call(CTNode, ct, get_config, [key1]),
+ ok = rpc:call(CTNode, ct, require, [x,key2]),
+ value2 = rpc:call(CTNode, ct, get_config, [x]),
+
+ ok = rpc:call(CTNode, ct, stop_interactive, []),
+
+ case rpc:call(CTNode, erlang, whereis, [ct_util_server]) of
+ undefined ->
+ ok;
+ _ ->
+ test_server:format(Level,
+ "ct_util_server not stopped on ~p yet, waiting 5 s...~n",
+ [CTNode]),
+ timer:sleep(5000),
+ undefined = rpc:call(CTNode, erlang, whereis, [ct_util_server])
+ end,
+ Events = ct_test_support:get_events(ERPid, Config),
+
+ ct_test_support:log_events(start_interactive,
+ reformat(Events, ?eh),
+ ?config(priv_dir, Config),
+ Opts),
+ TestEvents = test_events(start_interactive),
+ ok = ct_test_support:verify_events(TestEvents, Events, Config).
+
+%%%-----------------------------------------------------------------
+%%% 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).
+
+%%%-----------------------------------------------------------------
+%%% TEST EVENTS
+%%%-----------------------------------------------------------------
+
+test_events(start_interactive) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}
+ ].
diff --git a/lib/common_test/test/ct_shell_SUITE_data/cfgdata b/lib/common_test/test/ct_shell_SUITE_data/cfgdata
new file mode 100644
index 0000000000..23a40ad21a
--- /dev/null
+++ b/lib/common_test/test/ct_shell_SUITE_data/cfgdata
@@ -0,0 +1,2 @@
+{key1,value1}.
+{key2,value2}.
diff --git a/lib/common_test/test/ct_snmp_SUITE.erl b/lib/common_test/test/ct_snmp_SUITE.erl
new file mode 100644
index 0000000000..f8b4543770
--- /dev/null
+++ b/lib/common_test/test/ct_snmp_SUITE.erl
@@ -0,0 +1,141 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, 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_snmp_SUITE
+%%%
+%%% Description:
+%%% Test ct_snmp module
+%%%
+%%%-------------------------------------------------------------------
+-module(ct_snmp_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() ->
+ [
+ default
+ ].
+
+%%--------------------------------------------------------------------
+%% TEST CASES
+%%--------------------------------------------------------------------
+
+%%%-----------------------------------------------------------------
+%%%
+default(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ Suite = filename:join(DataDir, "snmp_SUITE"),
+ CfgFile = filename:join(DataDir, "snmp.cfg"),
+ {Opts,ERPid} = setup([{suite,Suite},{config,CfgFile},
+ {label,default}], Config),
+
+ ok = execute(default, Opts, ERPid, Config).
+
+
+%%%-----------------------------------------------------------------
+%%% 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}.
+
+execute(Name, Opts, ERPid, Config) ->
+ ok = ct_test_support:run(Opts, Config),
+ Events = ct_test_support:get_events(ERPid, Config),
+
+ ct_test_support:log_events(Name,
+ reformat(Events, ?eh),
+ ?config(priv_dir, Config),
+ Opts),
+
+ TestEvents = events_to_check(Name,Config),
+ ct_test_support:verify_events(TestEvents, Events, Config).
+
+reformat(Events, EH) ->
+ ct_test_support:reformat(Events, EH).
+
+%%%-----------------------------------------------------------------
+%%% TEST EVENTS
+%%%-----------------------------------------------------------------
+events_to_check(_TestName,Config) ->
+ {module,_} = code:load_abs(filename:join(?config(data_dir,Config),
+ snmp_SUITE)),
+ TCs = get_tcs(),
+ code:purge(snmp_SUITE),
+ code:delete(snmp_SUITE),
+
+ OneTest =
+ [{?eh,start_logging,{'DEF','RUNDIR'}}] ++
+ [{?eh,tc_done,{snmp_SUITE,TC,ok}} || TC <- TCs] ++
+ [{?eh,stop_logging,[]}],
+
+ %% 2 tests (ct:run_test + script_start) is default
+ OneTest ++ OneTest.
+
+
+get_tcs() ->
+ All = snmp_SUITE:all(),
+ Groups =
+ try snmp_SUITE:groups()
+ catch error:undef -> []
+ end,
+ flatten_tcs(All,Groups).
+
+flatten_tcs([H|T],Groups) when is_atom(H) ->
+ [H|flatten_tcs(T,Groups)];
+flatten_tcs([{group,Group}|T],Groups) ->
+ TCs = proplists:get_value(Group,Groups),
+ flatten_tcs(TCs ++ T,Groups);
+flatten_tcs([],_) ->
+ [].
diff --git a/lib/common_test/test/ct_snmp_SUITE_data/snmp.cfg b/lib/common_test/test/ct_snmp_SUITE_data/snmp.cfg
new file mode 100644
index 0000000000..895e097de6
--- /dev/null
+++ b/lib/common_test/test/ct_snmp_SUITE_data/snmp.cfg
@@ -0,0 +1,44 @@
+%% -*- erlang -*-
+{snmp1, [{start_agent,true},
+ {users,[{user_name,[snmpm_user_default,[]]}]},
+ {managed_agents,[{agent_name, [user_name, {127,0,0,1}, 4000,
+ [{engine_id,"ct_snmp-test-engine"},
+ {version,v2}]]}]},
+ {engine_id,"ct_snmp-test-engine"},
+ {agent_vsns,[v2]}
+ ]}.
+{snmp2, [{start_agent,true},
+ {engine_id,"ct_snmp-test-engine"}
+ ]}.
+{snmp3, [{start_agent,true},
+ {engine_id,"ct_snmp-test-engine"},
+ {agent_vsns,[v1,v2,v3]},
+ {agent_contexts,{data_dir_file,"context.conf"}},
+ {agent_usm,{data_dir_file,"usm.conf"}},
+ {agent_community,{data_dir_file,"community.conf"}},
+ {agent_notify_def,{data_dir_file,"notify.conf"}},
+ {agent_sysinfo,{data_dir_file,"standard.conf"}},
+ {agent_target_address_def,{data_dir_file,"target_addr.conf"}},
+ {agent_target_param_def,{data_dir_file,"target_params.conf"}},
+ {agent_vacm,{data_dir_file,"vacm.conf"}}]}.
+{snmp_app1,[{manager, [{config, [{verbosity, silence}]},
+ {server,[{verbosity,silence}]},
+ {net_if,[{verbosity,silence}]},
+ {versions,[v2]}
+ ]},
+ {agent, [{config, [{verbosity, silence}]},
+ {net_if,[{verbosity,silence}]},
+ {mib_server,[{verbosity,silence}]},
+ {local_db,[{verbosity,silence}]},
+ {agent_verbosity,silence}
+ ]}]}.
+{snmp_app2,[{manager, [{config, [{verbosity, silence}]},
+ {server,[{verbosity,silence}]},
+ {net_if,[{verbosity,silence}]}
+ ]},
+ {agent, [{config, [{verbosity, silence}]},
+ {net_if,[{verbosity,silence}]},
+ {mib_server,[{verbosity,silence}]},
+ {local_db,[{verbosity,silence}]},
+ {agent_verbosity,silence}
+ ]}]}.
diff --git a/lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE.erl b/lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE.erl
new file mode 100644
index 0000000000..16b2b5690c
--- /dev/null
+++ b/lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE.erl
@@ -0,0 +1,395 @@
+%%--------------------------------------------------------------------
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, 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_snmp_SUITE.erl
+%%
+%% Description:
+%% This file contains the test cases for the ct_snmp API.
+%%
+%% @author Support
+%% @doc Test of SNMP support in common_test
+%% @end
+%%----------------------------------------------------------------------
+%%----------------------------------------------------------------------
+-module(snmp_SUITE).
+-include_lib("common_test/include/ct.hrl").
+-include_lib("snmp/include/STANDARD-MIB.hrl").
+-include_lib("snmp/include/SNMP-USER-BASED-SM-MIB.hrl").
+-include_lib("snmp/include/snmp_types.hrl").
+
+-compile(export_all).
+
+%% Default timetrap timeout (set in init_per_testcase).
+-define(default_timeout, ?t:minutes(1)).
+
+-define(AGENT_UDP, 4000).
+
+suite() ->
+ [
+ {require, snmp1, snmp1},
+ {require, snmp_app1, snmp_app1},
+ {require, snmp2, snmp2},
+ {require, snmp_app2, snmp_app2},
+ {require, snmp3, snmp3}
+ ].
+
+all() ->
+ [start_stop,
+ {group,get_set},
+ {group,register},
+ {group,override},
+ set_info].
+
+
+groups() ->
+ [{get_set,[get_values,
+ get_next_values,
+ set_values,
+ load_mibs]},
+ {register,[register_users,
+ register_users_fail,
+ register_agents,
+ register_agents_fail,
+ register_usm_users,
+ register_usm_users_fail]},
+ {override,[override_usm,
+ override_standard,
+ override_context,
+ override_community,
+ override_notify,
+ override_target_addr,
+ override_target_params,
+ override_vacm]}].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(Config) ->
+ Config.
+
+init_per_group(get_set, Config) ->
+ ok = ct_snmp:start(Config,snmp1,snmp_app1),
+ Config;
+init_per_group(register, Config) ->
+ ok = ct_snmp:start(Config,snmp2,snmp_app2),
+ Config;
+init_per_group(_, Config) ->
+ ok = ct_snmp:start(Config,snmp3,snmp_app2),
+ Config.
+
+end_per_group(_Group, Config) ->
+ catch ct_snmp:stop(Config),
+ Config.
+
+init_per_testcase(_Case, Config) ->
+ Dog = test_server:timetrap(?default_timeout),
+ [{watchdog, Dog}|Config].
+
+end_per_testcase(Case, Config) ->
+ try apply(?MODULE,Case,[cleanup,Config])
+ catch error:undef -> ok
+ end,
+ Dog=?config(watchdog, Config),
+ test_server:timetrap_cancel(Dog),
+ ok.
+
+%%%-----------------------------------------------------------------
+%%% Test cases
+break(_Config) ->
+ test_server:break(""),
+ ok.
+
+start_stop(Config) ->
+ ok = ct_snmp:start(Config,snmp1,snmp_app1),
+ timer:sleep(1000),
+ {snmp,_,_} = lists:keyfind(snmp,1,application:which_applications()),
+ [_|_] = filelib:wildcard("*/*.conf",?config(priv_dir,Config)),
+
+ ok = ct_snmp:stop(Config),
+ timer:sleep(1000),
+ false = lists:keyfind(snmp,1,application:which_applications()),
+ [] = filelib:wildcard("*/*.conf",?config(priv_dir,Config)),
+ ok.
+
+get_values(_Config) ->
+ Oids1 = [?sysDescr_instance, ?sysName_instance],
+ {noError,_,V1} = ct_snmp:get_values(agent_name,Oids1,snmp1),
+ [#varbind{oid=?sysDescr_instance,value="Erlang SNMP agent"},
+ #varbind{oid=?sysName_instance,value="ct_test"}] = V1,
+ ok.
+
+get_next_values(_Config) ->
+ Oids2 = [?system],
+ {noError,_,V2} = ct_snmp:get_next_values(agent_name,Oids2,snmp1),
+ [#varbind{oid=?sysDescr_instance,value="Erlang SNMP agent"}] = V2,
+ ok.
+
+set_values(Config) ->
+ Oid3 = ?sysName_instance,
+ NewName = "ct_test changed by " ++ atom_to_list(?MODULE),
+ VarsAndVals = [{Oid3,s,NewName}],
+ {noError,_,_} =
+ ct_snmp:set_values(agent_name,VarsAndVals,snmp1,Config),
+
+ Oids4 = [?sysName_instance],
+ {noError,_,V4} = ct_snmp:get_values(agent_name,Oids4,snmp1),
+ [#varbind{oid=?sysName_instance,value=NewName}] = V4,
+
+ ok.
+
+load_mibs(_Config) ->
+ [{'SNMPv2-MIB',_}=SnmpV2Mib] = snmpa:which_mibs(),
+ Mib = filename:join([code:priv_dir(snmp),"mibs","SNMP-USER-BASED-SM-MIB"]),
+ ok = ct_snmp:load_mibs([Mib]),
+ TwoMibs = [_,_] = snmpa:which_mibs(),
+ [{'SNMP-USER-BASED-SM-MIB',_}] = lists:delete(SnmpV2Mib,TwoMibs),
+ ok = ct_snmp:unload_mibs([Mib]),
+ [{'SNMPv2-MIB',_}] = snmpa:which_mibs(),
+ ok.
+
+
+register_users(_Config) ->
+ [] = snmpm:which_users(),
+ ok = ct_snmp:register_users(snmp2,[{reg_user1,[snmpm_user_default,[]]}]),
+ [_] = snmpm:which_users(),
+ [_] = ct:get_config({snmp2,users}),
+ ok = ct_snmp:register_users(snmp2,[{reg_user2,[snmpm_user_default,[]]}]),
+ [_,_] = snmpm:which_users(),
+ [_,_] = ct:get_config({snmp2,users}),
+ ok = ct_snmp:register_users(snmp2,[{reg_user3,[snmpm_user_default,[]]}]),
+ [_,_,_] = snmpm:which_users(),
+ [_,_,_] = ct:get_config({snmp2,users}),
+ ok = ct_snmp:unregister_users(snmp2,[reg_user3]),
+ [_,_] = snmpm:which_users(),
+ [_,_] = ct:get_config({snmp2,users}),
+ ok = ct_snmp:unregister_users(snmp2),
+ [] = snmpm:which_users(),
+ [] = ct:get_config({snmp2,users}),
+ ok.
+register_users(cleanup,_Config) ->
+ ct_snmp:unregister_users(snmp2).
+
+register_users_fail(_Config) ->
+ [] = snmpm:which_users(),
+ {error,_} = ct_snmp:register_users(snmp2,[{reg_user3,[unknown_module,[]]}]),
+ [] = snmpm:which_users(),
+ ok.
+register_users_fail(cleanup,_Config) ->
+ ct_snmp:unregister_users(snmp2).
+
+register_agents(_Config) ->
+ {ok, HostName} = inet:gethostname(),
+ {ok, Addr} = inet:getaddr(HostName, inet),
+
+ [] = snmpm:which_agents(),
+ ok = ct_snmp:register_users(snmp2,[{reg_user1,[snmpm_user_default,[]]}]),
+ ok = ct_snmp:register_agents(snmp2,[{reg_agent1,
+ [reg_user1,Addr,?AGENT_UDP,[]]}]),
+ [_] = snmpm:which_agents(),
+ [_] = ct:get_config({snmp2,managed_agents}),
+ ok = ct_snmp:register_agents(snmp2,[{reg_agent2,
+ [reg_user1,Addr,?AGENT_UDP,[]]}]),
+ [_,_] = snmpm:which_agents(),
+ [_,_] = ct:get_config({snmp2,managed_agents}),
+
+ ok = ct_snmp:register_agents(snmp2,[{reg_agent3,
+ [reg_user1,Addr,?AGENT_UDP,[]]}]),
+ [_,_,_] = snmpm:which_agents(),
+ [_,_,_] = ct:get_config({snmp2,managed_agents}),
+
+ ok = ct_snmp:unregister_agents(snmp2,[reg_agent3]),
+ [_,_] = snmpm:which_agents(),
+ [_,_] = ct:get_config({snmp2,managed_agents}),
+
+ ok = ct_snmp:unregister_agents(snmp2),
+ ok = ct_snmp:unregister_users(snmp2),
+ [] = snmpm:which_agents(),
+ [] = ct:get_config({snmp2,managed_agents}),
+ ok.
+register_agents(cleanup,_Config) ->
+ ct_snmp:unregister_agents(snmp2),
+ ct_snmp:unregister_users(snmp2).
+
+register_agents_fail(_Config) ->
+ {ok, HostName} = inet:gethostname(),
+ {ok, Addr} = inet:getaddr(HostName, inet),
+
+ [] = snmpm:which_agents(),
+ {error,_}
+ = ct_snmp:register_agents(snmp2,[{reg_agent3,
+ [unknown_user,Addr,?AGENT_UDP,[]]}]),
+ [] = snmpm:which_agents(),
+ ok.
+register_agents_fail(cleanup,_Config) ->
+ ct_snmp:unregister_agents(snmp2).
+
+register_usm_users(_Config) ->
+ [] = snmpm:which_usm_users(),
+ ok = ct_snmp:register_usm_users(snmp2,[{"reg_usm_user1",[]}]),
+ [_] = snmpm:which_usm_users(),
+ [_] = ct:get_config({snmp2,usm_users}),
+ ok = ct_snmp:register_usm_users(snmp2,[{"reg_usm_user2",[]}]),
+ [_,_] = snmpm:which_usm_users(),
+ [_,_] = ct:get_config({snmp2,usm_users}),
+ ok = ct_snmp:register_usm_users(snmp2,[{"reg_usm_user3",[]}]),
+ [_,_,_] = snmpm:which_usm_users(),
+ [_,_,_] = ct:get_config({snmp2,usm_users}),
+ ok = ct_snmp:unregister_usm_users(snmp2,["reg_usm_user3"]),
+ [_,_] = snmpm:which_usm_users(),
+ [_,_] = ct:get_config({snmp2,usm_users}),
+ ok = ct_snmp:unregister_usm_users(snmp2),
+ [] = snmpm:which_usm_users(),
+ [] = ct:get_config({snmp2,usm_users}),
+ ok.
+register_usm_users(cleanup,_Config) ->
+ ct_snmp:unregister_usm_users(snmp2).
+
+register_usm_users_fail(_Config) ->
+ [] = snmpm:which_usm_users(),
+ {error,_}
+ = ct_snmp:register_usm_users(snmp2,[{"reg_usm_user3",
+ [{sec_name,invalid_data_type}]}]),
+ [] = snmpm:which_usm_users(),
+ ok.
+register_usm_users_fail(cleanup,_Config) ->
+ ct_snmp:unregister_usm_users(snmp2).
+
+%% Test that functionality for overriding default configuration file
+%% works - i.e. that the files are written and that the configuration
+%% is actually used.
+%%
+%% Note that the config files used in this test case do not
+%% necessarily make up a reasonable configuration for the snmp
+%% application...
+override_usm(Config) ->
+ DataDir = ?config(data_dir,Config),
+ PrivDir = ?config(priv_dir,Config),
+ ConfDir = filename:join(PrivDir,"conf"),
+
+ Mib = filename:join([code:priv_dir(snmp),"mibs","SNMP-USER-BASED-SM-MIB"]),
+ ok = ct_snmp:load_mibs([Mib]),
+
+ %% Check that usm.conf is overwritten
+ {ok,MyUsm} = snmpa_conf:read_usm_config(DataDir),
+ {ok,UsedUsm} = snmpa_conf:read_usm_config(ConfDir),
+ true = (MyUsm == UsedUsm),
+
+ %% Check that the usm user is actually configured...
+ [{Index,"secname"}] =
+ snmp_user_based_sm_mib:usmUserTable(get_next,?usmUserEntry,[3]),
+ true = lists:suffix("usm_user_name",Index),
+ ok.
+
+override_standard(Config) ->
+ DataDir = ?config(data_dir,Config),
+ PrivDir = ?config(priv_dir,Config),
+ ConfDir = filename:join(PrivDir,"conf"),
+
+ %% Check that standard.conf is overwritten
+ {ok,MyStandard} = snmpa_conf:read_standard_config(DataDir),
+ {ok,UsedStandard} = snmpa_conf:read_standard_config(ConfDir),
+ true = (MyStandard == UsedStandard),
+
+ %% Check that the values from standard.conf is actually configured...
+ {value,"name for override test"} = snmp_standard_mib:sysName(get),
+ {value,"agent for ct_snmp override test"} = snmp_standard_mib:sysDescr(get),
+ ok.
+
+override_context(Config) ->
+ DataDir = ?config(data_dir,Config),
+ PrivDir = ?config(priv_dir,Config),
+ ConfDir = filename:join(PrivDir,"conf"),
+
+ %% Check that context.conf is overwritten
+ {ok,MyContext} = snmpa_conf:read_context_config(DataDir),
+ {ok,UsedContext} = snmpa_conf:read_context_config(ConfDir),
+ true = (MyContext == UsedContext),
+ ok.
+
+override_community(Config) ->
+ DataDir = ?config(data_dir,Config),
+ PrivDir = ?config(priv_dir,Config),
+ ConfDir = filename:join(PrivDir,"conf"),
+
+ %% Check that community.conf is overwritten
+ {ok,MyCommunity} = snmpa_conf:read_community_config(DataDir),
+ {ok,UsedCommunity} = snmpa_conf:read_community_config(ConfDir),
+ true = (MyCommunity == UsedCommunity),
+ ok.
+
+override_notify(Config) ->
+ DataDir = ?config(data_dir,Config),
+ PrivDir = ?config(priv_dir,Config),
+ ConfDir = filename:join(PrivDir,"conf"),
+
+ %% Check that notify.conf is overwritten
+ {ok,MyNotify} = snmpa_conf:read_notify_config(DataDir),
+ {ok,UsedNotify} = snmpa_conf:read_notify_config(ConfDir),
+ true = (MyNotify == UsedNotify),
+ ok.
+
+override_target_addr(Config) ->
+ DataDir = ?config(data_dir,Config),
+ PrivDir = ?config(priv_dir,Config),
+ ConfDir = filename:join(PrivDir,"conf"),
+
+ %% Check that target_addr.conf is overwritten
+ {ok,MyTargetAddr} = snmpa_conf:read_target_addr_config(DataDir),
+ {ok,UsedTargetAddr} = snmpa_conf:read_target_addr_config(ConfDir),
+ true = (MyTargetAddr == UsedTargetAddr),
+ ok.
+
+override_target_params(Config) ->
+ DataDir = ?config(data_dir,Config),
+ PrivDir = ?config(priv_dir,Config),
+ ConfDir = filename:join(PrivDir,"conf"),
+
+ %% Check that target_params.conf is overwritten
+ {ok,MyTargetParams} = snmpa_conf:read_target_params_config(DataDir),
+ {ok,UsedTargetParams} = snmpa_conf:read_target_params_config(ConfDir),
+ true = (MyTargetParams == UsedTargetParams),
+ ok.
+
+override_vacm(Config) ->
+ DataDir = ?config(data_dir,Config),
+ PrivDir = ?config(priv_dir,Config),
+ ConfDir = filename:join(PrivDir,"conf"),
+
+ %% Check that vacm.conf is overwritten
+ {ok,MyVacm} = snmpa_conf:read_vacm_config(DataDir),
+ {ok,UsedVacm} = snmpa_conf:read_vacm_config(ConfDir),
+ true = (MyVacm == UsedVacm),
+ ok.
+
+
+
+
+%% NOTE!! This test must always be executed last in the suite, and
+%% should match all set requests performed in the suite. I.e. if you
+%% add a set request, you must add an entry in the return value of
+%% ct_snmp:set_info/1 below.
+set_info(Config) ->
+ %% From test case set_values/1:
+ Oid1 = ?sysName_instance,
+ NewValue1 = "ct_test changed by " ++ atom_to_list(?MODULE),
+
+ %% The test...
+ [{_AgentName,_,[{Oid1,_,NewValue1}]}]
+ = ct_snmp:set_info(Config),
+ ok.
diff --git a/lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/community.conf b/lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/community.conf
new file mode 100644
index 0000000000..5a64df6605
--- /dev/null
+++ b/lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/community.conf
@@ -0,0 +1 @@
+{"public", "public", "initial", "", ""}.
diff --git a/lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/context.conf b/lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/context.conf
new file mode 100644
index 0000000000..feed5e1d11
--- /dev/null
+++ b/lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/context.conf
@@ -0,0 +1 @@
+"testcontext".
diff --git a/lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/notify.conf b/lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/notify.conf
new file mode 100644
index 0000000000..367ba3aa4b
--- /dev/null
+++ b/lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/notify.conf
@@ -0,0 +1 @@
+{"standard inform", "std_inform", inform}.
diff --git a/lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/standard.conf b/lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/standard.conf
new file mode 100644
index 0000000000..79908fb355
--- /dev/null
+++ b/lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/standard.conf
@@ -0,0 +1,7 @@
+{sysDescr, "agent for ct_snmp override test"}.
+{sysObjectID, [1,2,3]}.
+{sysContact, "[email protected]"}.
+{sysLocation, "erlang"}.
+{sysServices, 72}.
+{snmpEnableAuthenTraps, enabled}.
+{sysName, "name for override test"}.
diff --git a/lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/target_addr.conf b/lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/target_addr.conf
new file mode 100644
index 0000000000..d02672a074
--- /dev/null
+++ b/lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/target_addr.conf
@@ -0,0 +1,2 @@
+{"target1", snmpUDPDomain, [147,214,122,73], 5000, 1500, 3, "std_trap", "target_v3", "", [], 2048}.
+{"target2", snmpUDPDomain, [147,214,122,73], 5000, 1500, 3, "std_inform", "target_v3", "", [], 2048}.
diff --git a/lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/target_params.conf b/lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/target_params.conf
new file mode 100644
index 0000000000..5a9a619422
--- /dev/null
+++ b/lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/target_params.conf
@@ -0,0 +1 @@
+{"target_v3", v3, usm, "initial", noAuthNoPriv}.
diff --git a/lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/usm.conf b/lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/usm.conf
new file mode 100644
index 0000000000..d6e245914e
--- /dev/null
+++ b/lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/usm.conf
@@ -0,0 +1 @@
+{"ct_snmp-test-engine","usm_user_name","secname",zeroDotZero,usmNoAuthProtocol,"","",usmNoPrivProtocol,"","","","",""}.
diff --git a/lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/vacm.conf b/lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/vacm.conf
new file mode 100644
index 0000000000..158fe02e3b
--- /dev/null
+++ b/lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/vacm.conf
@@ -0,0 +1,6 @@
+{vacmSecurityToGroup, usm, "initial", "initial"}.
+{vacmAccess, "initial", "", any, noAuthNoPriv, exact, "restricted", "", "restricted"}.
+{vacmAccess, "initial", "", usm, authNoPriv, exact, "internet", "internet", "internet"}.
+{vacmAccess, "initial", "", usm, authPriv, exact, "internet", "internet", "internet"}.
+{vacmViewTreeFamily, "restricted", [1,3,6,1], included, null}.
+{vacmViewTreeFamily, "internet", [1,3,6,1], included, null}.
diff --git a/lib/common_test/test/ct_surefire_SUITE.erl b/lib/common_test/test/ct_surefire_SUITE.erl
new file mode 100644
index 0000000000..69e98cef48
--- /dev/null
+++ b/lib/common_test/test/ct_surefire_SUITE.erl
@@ -0,0 +1,351 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, 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_surefire_SUITE
+%%%
+%%% Description:
+%%% Test cth_surefire hook
+%%%
+%%%-------------------------------------------------------------------
+-module(ct_surefire_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+-include_lib("xmerl/include/xmerl.hrl").
+
+-define(eh, ct_test_support_eh).
+
+-define(url_base,"http://my.host.com/").
+
+%%--------------------------------------------------------------------
+%% 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() ->
+ [
+ default,
+ absolute_path,
+ relative_path,
+ url,
+ logdir
+ ].
+
+%%--------------------------------------------------------------------
+%% TEST CASES
+%%--------------------------------------------------------------------
+
+%%%-----------------------------------------------------------------
+%%%
+default(Config) when is_list(Config) ->
+ run(default,[cth_surefire],Config),
+ PrivDir = ?config(priv_dir,Config),
+ XmlRe = filename:join([PrivDir,"*","junit_report.xml"]),
+ check_xml(default,XmlRe).
+
+absolute_path(Config) when is_list(Config) ->
+ PrivDir = ?config(priv_dir,Config),
+ Path = filename:join(PrivDir,"abspath.xml"),
+ run(absolute_path,[{cth_surefire,[{path,Path}]}],Config),
+ check_xml(absolute_path,Path).
+
+relative_path(Config) when is_list(Config) ->
+ Path = "relpath.xml",
+ run(relative_path,[{cth_surefire,[{path,Path}]}],Config),
+ PrivDir = ?config(priv_dir,Config),
+ XmlRe = filename:join([PrivDir,"*",Path]),
+ check_xml(relative_path,XmlRe).
+
+url(Config) when is_list(Config) ->
+ Path = "url.xml",
+ run(url,[{cth_surefire,[{url_base,?url_base},
+ {path,Path}]}],Config),
+ PrivDir = ?config(priv_dir,Config),
+ XmlRe = filename:join([PrivDir,"*",Path]),
+ check_xml(url,XmlRe).
+
+logdir(Config) when is_list(Config) ->
+ PrivDir = ?config(priv_dir,Config),
+ LogDir = filename:join(PrivDir,"specific_logdir"),
+ file:make_dir(LogDir),
+ Path = "logdir.xml",
+ run(logdir,[{cth_surefire,[{path,Path}]}],Config,[{logdir,LogDir}]),
+ PrivDir = ?config(priv_dir,Config),
+ XmlRe = filename:join([LogDir,"*",Path]),
+ check_xml(logdir,XmlRe).
+
+%%%-----------------------------------------------------------------
+%%% HELP FUNCTIONS
+%%%-----------------------------------------------------------------
+run(Case,CTHs,Config) ->
+ run(Case,CTHs,Config,[]).
+run(Case,CTHs,Config,ExtraOpts) ->
+ DataDir = ?config(data_dir, Config),
+ Suite = filename:join(DataDir, "surefire_SUITE"),
+ {Opts,ERPid} = setup([{suite,Suite},{ct_hooks,CTHs},{label,Case}|ExtraOpts],
+ Config),
+ ok = execute(Case, Opts, ERPid, Config).
+
+setup(Test, Config) ->
+ Opts0 = ct_test_support:get_opts(Config),
+ Opts1 =
+ case lists:keymember(logdir,1,Test) of
+ true -> lists:keydelete(logdir,1,Opts0);
+ false -> Opts0
+ end,
+ Level = ?config(trace_level, Config),
+ EvHArgs = [{cbm,ct_test_support},{trace_level,Level}],
+ Opts = Opts1 ++ [{event_handler,{?eh,EvHArgs}}|Test],
+ ERPid = ct_test_support:start_event_receiver(Config),
+ {Opts,ERPid}.
+
+execute(Name, Opts, ERPid, Config) ->
+ ok = ct_test_support:run(Opts, Config),
+ Events = ct_test_support:get_events(ERPid, Config),
+
+ ct_test_support:log_events(Name,
+ reformat(Events, ?eh),
+ ?config(priv_dir, Config),
+ Opts),
+
+ TestEvents = events_to_check(Name),
+ ct_test_support:verify_events(TestEvents, Events, Config).
+
+reformat(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) ->
+ test_events(Test) ++ events_to_check(Test, N-1).
+
+test_events(_) ->
+ [{?eh,start_logging,'_'},
+ {?eh,start_info,{1,1,9}},
+ {?eh,tc_start,{surefire_SUITE,init_per_suite}},
+ {?eh,tc_done,{surefire_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{surefire_SUITE,tc_ok}},
+ {?eh,tc_done,{surefire_SUITE,tc_ok,ok}},
+ {?eh,test_stats,{1,0,{0,0}}},
+ {?eh,tc_start,{surefire_SUITE,tc_fail}},
+ {?eh,tc_done,{surefire_SUITE,tc_fail,
+ {failed,{error,{test_case_failed,"this test should fail"}}}}},
+ {?eh,test_stats,{1,1,{0,0}}},
+ {?eh,tc_start,{surefire_SUITE,tc_skip}},
+ {?eh,tc_done,{surefire_SUITE,tc_skip,{skipped,"this test is skipped"}}},
+ {?eh,test_stats,{1,1,{1,0}}},
+ {?eh,tc_start,{surefire_SUITE,tc_autoskip_require}},
+ {?eh,tc_done,{surefire_SUITE,tc_autoskip_require,
+ {skipped,{require_failed,'_'}}}},
+ {?eh,test_stats,{1,1,{1,1}}},
+ [{?eh,tc_start,{surefire_SUITE,{init_per_group,g,[]}}},
+ {?eh,tc_done,{surefire_SUITE,{init_per_group,g,[]},ok}},
+ {?eh,tc_start,{surefire_SUITE,tc_ok}},
+ {?eh,tc_done,{surefire_SUITE,tc_ok,ok}},
+ {?eh,test_stats,{2,1,{1,1}}},
+ {?eh,tc_start,{surefire_SUITE,tc_fail}},
+ {?eh,tc_done,{surefire_SUITE,tc_fail,
+ {failed,{error,{test_case_failed,"this test should fail"}}}}},
+ {?eh,test_stats,{2,2,{1,1}}},
+ {?eh,tc_start,{surefire_SUITE,tc_skip}},
+ {?eh,tc_done,{surefire_SUITE,tc_skip,{skipped,"this test is skipped"}}},
+ {?eh,test_stats,{2,2,{2,1}}},
+ {?eh,tc_start,{surefire_SUITE,tc_autoskip_require}},
+ {?eh,tc_done,{surefire_SUITE,tc_autoskip_require,
+ {skipped,{require_failed,'_'}}}},
+ {?eh,test_stats,{2,2,{2,2}}},
+ {?eh,tc_start,{surefire_SUITE,{end_per_group,g,[]}}},
+ {?eh,tc_done,{surefire_SUITE,{end_per_group,g,[]},ok}}],
+ [{?eh,tc_start,{surefire_SUITE,{init_per_group,g_fail,[]}}},
+ {?eh,tc_done,{surefire_SUITE,{init_per_group,g_fail,[]},
+ {failed,{error,all_cases_should_be_skipped}}}},
+ {?eh,tc_auto_skip,{surefire_SUITE,tc_ok,
+ {failed,
+ {surefire_SUITE,init_per_group,
+ {'EXIT',all_cases_should_be_skipped}}}}},
+ {?eh,test_stats,{2,2,{2,3}}},
+ {?eh,tc_auto_skip,{surefire_SUITE,end_per_group,
+ {failed,
+ {surefire_SUITE,init_per_group,
+ {'EXIT',all_cases_should_be_skipped}}}}}],
+ {?eh,tc_start,{surefire_SUITE,end_per_suite}},
+ {?eh,tc_done,{surefire_SUITE,end_per_suite,ok}},
+ {?eh,stop_logging,[]}].
+
+
+%%%-----------------------------------------------------------------
+%%% Check generated xml log files
+check_xml(Case,XmlRe) ->
+ case filelib:wildcard(XmlRe) of
+ [] ->
+ ct:fail("No xml files found with regexp ~p~n", [XmlRe]);
+ [_] = Xmls when Case==absolute_path ->
+ do_check_xml(Case,Xmls);
+ [_,_] = Xmls ->
+ do_check_xml(Case,Xmls)
+ end.
+
+%% Allowed structure:
+%% <testsuites>
+%% <testsuite>
+%% <properties>
+%% <property/>
+%% ...
+%% </properties>
+%% <testcase>
+%% [<failure/> | <error/> | <skipped/> ]
+%% </testcase>
+%% ...
+%% </testsuite>
+%% ...
+%% </testsuites>
+do_check_xml(Case,[Xml|Xmls]) ->
+ ct:log("Checking <a href=~p>~s</a>~n",[Xml,Xml]),
+ {E,_} = xmerl_scan:file(Xml),
+ Expected = events_to_result(lists:flatten(test_events(Case))),
+ ParseResult = testsuites(Case,E),
+ ct:log("Expecting: ~p~n",[[Expected]]),
+ ct:log("Actual : ~p~n",[ParseResult]),
+ [Expected] = ParseResult,
+ do_check_xml(Case,Xmls);
+do_check_xml(_,[]) ->
+ ok.
+
+%% Scanning the XML to get the same type of result as events_to_result/1
+testsuites(Case,#xmlElement{name=testsuites,content=TS}) ->
+ %% OTP-10589 - move properties element to <testsuite>
+ false = lists:keytake(properties,#xmlElement.name,TS),
+ testsuite(Case,TS).
+
+testsuite(Case,[#xmlElement{name=testsuite,content=TC,attributes=A}|TS]) ->
+ {ET,EF,ES} = events_to_numbers(lists:flatten(test_events(Case))),
+ {T,E,F,S} = get_numbers_from_attrs(A,false,false,false,false),
+ ct:log("Expecting total:~p, error:~p, failure:~p, skipped:~p~n",[ET,0,EF,ES]),
+ ct:log("Actual total:~p, error:~p, failure:~p, skipped:~p~n",[T,E,F,S]),
+ {ET,0,EF,ES} = {T,E,F,S},
+
+ %% properties should only be there if given a options to hook
+ false = lists:keytake(properties,#xmlElement.name,TC),
+ %% system-out and system-err is not used by common_test
+ false = lists:keytake('system-out',#xmlElement.name,TC),
+ false = lists:keytake('system-err',#xmlElement.name,TC),
+ R=testcase(Case,TC),
+ [R|testsuite(Case,TS)];
+testsuite(_Case,[]) ->
+ [].
+
+testcase(url=Case,[#xmlElement{name=testcase,attributes=A,content=C}|TC]) ->
+ R = failed_or_skipped(C),
+ case R of
+ [s] ->
+ case lists:keyfind(url,#xmlAttribute.name,A) of
+ false -> ok;
+ #xmlAttribute{value=UrlAttr} ->
+ lists:keyfind(url,#xmlAttribute.name,A),
+ true = lists:prefix(?url_base,UrlAttr)
+ end;
+ _ ->
+ #xmlAttribute{value=UrlAttr} =
+ lists:keyfind(url,#xmlAttribute.name,A),
+ true = lists:prefix(?url_base,UrlAttr)
+ end,
+ [R|testcase(Case,TC)];
+testcase(Case,[#xmlElement{name=testcase,attributes=A,content=C}|TC]) ->
+ false = lists:keyfind(url,#xmlAttribute.name,A),
+ R = failed_or_skipped(C),
+ [R|testcase(Case,TC)];
+testcase(_Case,[]) ->
+ [].
+
+failed_or_skipped([#xmlElement{name=failure}|E]) ->
+ [f|failed_or_skipped(E)];
+failed_or_skipped([#xmlElement{name=error}|E]) ->
+ [e|failed_or_skipped(E)];
+failed_or_skipped([#xmlElement{name=skipped}|E]) ->
+ [s|failed_or_skipped(E)];
+failed_or_skipped([]) ->
+ [].
+
+%% Using the expected events to produce the expected result of the XML scanning.
+%% The result is a list of test suites:
+%% Testsuites = [Testsuite]
+%% Testsuite = [Testcase]
+%% Testcase = [] | [f] | [s], indicating ok, failed and skipped respectively
+events_to_result([{?eh,tc_done,{_Suite,_Case,R}}|E]) ->
+ [result(R)|events_to_result(E)];
+events_to_result([{?eh,tc_auto_skip,_}|E]) ->
+ [[s]|events_to_result(E)];
+events_to_result([_|E]) ->
+ events_to_result(E);
+events_to_result([]) ->
+ [].
+
+result(ok) ->[];
+result({skipped,_}) -> [s];
+result({failed,_}) -> [f].
+
+%% Using the expected events' last test_stats element to produce the
+%% expected number of totla, errors, failed and skipped testcases.
+events_to_numbers(E) ->
+ RevE = lists:reverse(E),
+ {?eh,test_stats,{Ok,F,{US,AS}}} = lists:keyfind(test_stats,2,RevE),
+ {Ok+F+US+AS,F,US+AS}.
+
+get_numbers_from_attrs([#xmlAttribute{name=tests,value=X}|A],false,E,F,S) ->
+ get_numbers_from_attrs(A,list_to_integer(X),E,F,S);
+get_numbers_from_attrs([#xmlAttribute{name=errors,value=X}|A],T,false,F,S) ->
+ get_numbers_from_attrs(A,T,list_to_integer(X),F,S);
+get_numbers_from_attrs([#xmlAttribute{name=failures,value=X}|A],T,E,false,S) ->
+ get_numbers_from_attrs(A,T,E,list_to_integer(X),S);
+get_numbers_from_attrs([#xmlAttribute{name=skipped,value=X}|A],T,E,F,false) ->
+ get_numbers_from_attrs(A,T,E,F,list_to_integer(X));
+get_numbers_from_attrs([_|A],T,E,F,S) ->
+ get_numbers_from_attrs(A,T,E,F,S);
+get_numbers_from_attrs([],T,E,F,S) ->
+ {T,E,F,S}.
diff --git a/lib/common_test/test/ct_surefire_SUITE_data/surefire_SUITE.erl b/lib/common_test/test/ct_surefire_SUITE_data/surefire_SUITE.erl
new file mode 100644
index 0000000000..677aee46c5
--- /dev/null
+++ b/lib/common_test/test/ct_surefire_SUITE_data/surefire_SUITE.erl
@@ -0,0 +1,92 @@
+%%--------------------------------------------------------------------
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, 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: surefire_SUITE.erl
+%%
+%% Description:
+%% This file contains the test cases for cth_surefire.
+%%
+%% @author Support
+%% @doc Test of surefire support in common_test
+%% @end
+%%----------------------------------------------------------------------
+%%----------------------------------------------------------------------
+-module(surefire_SUITE).
+-include_lib("common_test/include/ct.hrl").
+
+-compile(export_all).
+
+%% Default timetrap timeout (set in init_per_testcase).
+-define(default_timeout, ?t:minutes(1)).
+
+all() ->
+ testcases() ++ [{group,g},{group,g_fail}].
+
+groups() ->
+ [{g,testcases()},
+ {g_fail,[tc_ok]}].
+
+testcases() ->
+ [tc_ok,
+ tc_fail,
+ tc_skip,
+ tc_autoskip_require].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(Config) ->
+ Config.
+
+init_per_group(g_fail, _Config) ->
+ exit(all_cases_should_be_skipped);
+init_per_group(_, Config) ->
+ Config.
+
+end_per_group(_Group, Config) ->
+ Config.
+
+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.
+
+%%%-----------------------------------------------------------------
+%%% Test cases
+break(_Config) ->
+ test_server:break(""),
+ ok.
+
+tc_ok(_Config) ->
+ ok.
+
+tc_fail(_Config) ->
+ ct:fail("this test should fail").
+
+tc_skip(_Config) ->
+ {skip,"this test is skipped"}.
+
+tc_autoskip_require() ->
+ [{require,whatever}].
+tc_autoskip_require(Config) ->
+ ct:fail("this test should never be executed - it should be autoskipped").
diff --git a/lib/common_test/test/ct_system_error_SUITE.erl b/lib/common_test/test/ct_system_error_SUITE.erl
new file mode 100644
index 0000000000..f2d6ef4b1b
--- /dev/null
+++ b/lib/common_test/test/ct_system_error_SUITE.erl
@@ -0,0 +1,132 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, 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_system_error_SUITE
+%%%
+%%% Description:
+%%%
+%%% Test that severe system errors (such as failure to write logs) are
+%%% noticed and handled.
+%%%-------------------------------------------------------------------
+-module(ct_system_error_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() ->
+ [
+ test_server_failing_logs
+ ].
+
+%%--------------------------------------------------------------------
+%% TEST CASES
+%%--------------------------------------------------------------------
+
+%%%-----------------------------------------------------------------
+%%%
+test_server_failing_logs(Config) ->
+ TC = test_server_failing_logs,
+ DataDir = ?config(data_dir, Config),
+ Suite = filename:join(DataDir, "a_SUITE"),
+ {Opts,ERPid} = setup([{suite,Suite},{label,TC}], Config),
+ crash_test_server(Config),
+ {error,{cannot_create_log_dir,__}} = ct_test_support:run(Opts, Config),
+ Events = ct_test_support:get_events(ERPid, Config),
+ ct_test_support:log_events(TC,
+ reformat(Events, ?eh),
+ ?config(priv_dir, Config),
+ Opts),
+
+ TestEvents = events_to_check(TC),
+ ok = ct_test_support:verify_events(TestEvents, Events, Config).
+
+crash_test_server(Config) ->
+ DataDir = ?config(data_dir, Config),
+ Root = proplists:get_value(logdir, ct_test_support:get_opts(Config)),
+ [$@|Host] = lists:dropwhile(fun(C) ->
+ C =/= $@
+ end, atom_to_list(node())),
+ Format = filename:join(Root,
+ "ct_run.ct@" ++ Host ++
+ ".~4..0w-~2..0w-~2..0w_"
+ "~2..0w.~2..0w.~2..0w"),
+ [C2,C1|_] = lists:reverse(filename:split(DataDir)),
+ LogDir = C1 ++ "." ++ C2 ++ ".a_SUITE.logs",
+ T = calendar:datetime_to_gregorian_seconds(calendar:local_time()),
+ [begin
+ {{Y,Mon,D},{H,Min,S}} =
+ calendar:gregorian_seconds_to_datetime(T+Offset),
+ Dir0 = io_lib:format(Format, [Y,Mon,D,H,Min,S]),
+ Dir = lists:flatten(Dir0),
+ file:make_dir(Dir),
+ File = filename:join(Dir, LogDir),
+ file:write_file(File, "anything goes\n")
+ end || Offset <- lists:seq(0, 20)],
+ ok.
+
+%%%-----------------------------------------------------------------
+%%% 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).
+
+%%%-----------------------------------------------------------------
+%%% TEST EVENTS
+%%%-----------------------------------------------------------------
+
+events_to_check(_Test) ->
+ [{?eh,severe_error,{cannot_create_log_dir,{'_','_'}}}].
diff --git a/lib/common_test/test/ct_system_error_SUITE_data/a_SUITE.erl b/lib/common_test/test/ct_system_error_SUITE_data/a_SUITE.erl
new file mode 100644
index 0000000000..c6e3ddfd5d
--- /dev/null
+++ b/lib/common_test/test/ct_system_error_SUITE_data/a_SUITE.erl
@@ -0,0 +1,122 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, 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(a_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+%%--------------------------------------------------------------------
+%% @spec suite() -> Info
+%% Info = [tuple()]
+%% @end
+%%--------------------------------------------------------------------
+suite() ->
+ [{timetrap,{seconds,10}}].
+
+%%--------------------------------------------------------------------
+%% @spec init_per_suite(Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%% @end
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% @spec end_per_suite(Config0) -> void() | {save_config,Config1}
+%% Config0 = Config1 = [tuple()]
+%% @end
+%%--------------------------------------------------------------------
+end_per_suite(_Config) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% @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(_GroupName, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% @spec end_per_group(GroupName, Config0) ->
+%% void() | {save_config,Config1}
+%% GroupName = atom()
+%% Config0 = Config1 = [tuple()]
+%% @end
+%%--------------------------------------------------------------------
+end_per_group(_GroupName, _Config) ->
+ 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() ->
+ [].
+
+%%--------------------------------------------------------------------
+%% @spec all() -> GroupsAndTestCases | {skip,Reason}
+%% GroupsAndTestCases = [{group,GroupName} | TestCase]
+%% GroupName = atom()
+%% TestCase = atom()
+%% Reason = term()
+%% @end
+%%--------------------------------------------------------------------
+all() ->
+ [tc1].
+
+tc1(_C) ->
+ ok.
diff --git a/lib/common_test/test/ct_test_support.erl b/lib/common_test/test/ct_test_support.erl
index 02246b5763..8814570e99 100644
--- a/lib/common_test/test/ct_test_support.erl
+++ b/lib/common_test/test/ct_test_support.erl
@@ -32,9 +32,11 @@
run/2, run/3, 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/4,
+ verify_events/3, verify_events/4, reformat/2, log_events/4,
join_abs_dirs/2]).
+-export([ct_test_halt/1]).
+
-include_lib("kernel/include/file.hrl").
%%%-----------------------------------------------------------------
@@ -115,8 +117,7 @@ end_per_suite(Config) ->
CTNode = proplists:get_value(ct_node, Config),
PrivDir = proplists:get_value(priv_dir, Config),
true = rpc:call(CTNode, code, del_path, [filename:join(PrivDir,"")]),
- cover:stop(CTNode),
- slave:stop(CTNode),
+ slave_stop(CTNode),
ok.
%%%-----------------------------------------------------------------
@@ -147,8 +148,7 @@ end_per_testcase(_TestCase, Config) ->
case wait_for_ct_stop(CTNode) of
%% Common test was not stopped to we restart node.
false ->
- cover:stop(CTNode),
- slave:stop(CTNode),
+ slave_stop(CTNode),
start_slave(Config,proplists:get_value(trace_level,Config)),
{fail, "Could not stop common_test"};
true ->
@@ -229,8 +229,9 @@ run(Opts, Config) when is_list(Opts) ->
%% 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]),
-
+ CtRunTestResult = rpc:call(CTNode, ct, run_test, [Opts]),
+ test_server:format(Level, "~n[RUN #1] Got return value ~p~n",
+ [CtRunTestResult]),
case rpc:call(CTNode, erlang, whereis, [ct_util_server]) of
undefined ->
ok;
@@ -242,17 +243,36 @@ run(Opts, Config) when is_list(Opts) ->
undefined = rpc:call(CTNode, erlang, whereis, [ct_util_server])
end,
%% 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} ->
+ Opts1 = [{halt_with,{?MODULE,ct_test_halt}} | Opts],
+ test_server:format(Level, "Saving start opts on ~p: ~p~n",
+ [CTNode, Opts1]),
+ rpc:call(CTNode, application, set_env,
+ [common_test, run_test_start_opts, Opts1]),
+ test_server:format(Level, "[RUN #2] Calling ct_run:script_start() on ~p~n",
+ [CTNode]),
+ ExitStatus = rpc:call(CTNode, ct_run, script_start, []),
+ test_server:format(Level, "[RUN #2] Got exit status value ~p~n",
+ [ExitStatus]),
+ case {CtRunTestResult,ExitStatus} of
+ {{_Ok,Failed,{_UserSkipped,_AutoSkipped}},1} when Failed > 0 ->
+ ok;
+ {{_Ok,0,{_UserSkipped,AutoSkipped}},ExitStatus} when AutoSkipped > 0 ->
+ case proplists:get_value(exit_status, Opts1) of
+ ignore_config when ExitStatus == 1 ->
+ {error,{wrong_exit_status,ExitStatus}};
+ _ ->
+ ok
+ end;
+ {{error,_}=Error,ExitStatus} ->
+ if ExitStatus /= 2 ->
+ {error,{wrong_exit_status,ExitStatus}};
+ ExitStatus == 2 ->
+ Error
+ end;
+ {{_Ok,0,{_UserSkipped,_AutoSkipped}},0} ->
ok;
- {E,_} when E =/= ok ->
- E;
- {_,E} when E =/= ok ->
- E
+ Unexpected ->
+ {error,{unexpected_return_value,Unexpected}}
end.
run(M, F, A, Config) ->
@@ -272,6 +292,10 @@ run({M,F,A}, InitCalls, Config) ->
[M, F, A, CTNode]),
rpc:call(CTNode, M, F, A).
+%% this is the last function that ct_run:script_start() calls, so the
+%% return value here is what rpc:call/4 above returns
+ct_test_halt(ExitStatus) ->
+ ExitStatus.
%%%-----------------------------------------------------------------
%%% wait_for_ct_stop/1
@@ -338,6 +362,14 @@ verify_events(TEvs, Evs, Config) ->
ok
end.
+verify_events(TEvs, Evs, Node, Config) ->
+ case catch verify_events1(TEvs, Evs, Node, Config) of
+ {'EXIT',Reason} ->
+ Reason;
+ _ ->
+ 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]),
@@ -586,8 +618,11 @@ locate({parallel,TEvs}, Node, Evs, Config) ->
fun({EH,#event{name=tc_auto_skip,
node=EvNode,
data={Mod,end_per_group,Reason}}}) when
- EH == TEH, EvNode == Node, Mod == M, Reason == R ->
- false;
+ EH == TEH, EvNode == Node, Mod == M ->
+ case match_data(R, Reason) of
+ match -> false;
+ _ -> true
+ end;
({EH,#event{name=stop_logging,
node=EvNode,data=_}}) when
EH == TEH, EvNode == Node ->
@@ -601,23 +636,12 @@ locate({parallel,TEvs}, Node, Evs, Config) ->
[_AutoSkip | RemEvs2] ->
{Done,RemEvs2,length(RemEvs2)}
end;
- %% match other event than test case
- (TEv={TEH,N,D}, Acc) when D == '_' ->
- case [E || E={EH,#event{name=Name,
- node=EvNode,
- data=_}} <- Evs1,
- EH == TEH, EvNode == Node, Name == N] of
- [] ->
- exit({unmatched,TEv});
- _ ->
- test_server:format("Found ~p!", [TEv]),
- Acc
- end;
(TEv={TEH,N,D}, Acc) ->
case [E || E={EH,#event{name=Name,
node=EvNode,
data=Data}} <- Evs1,
- EH == TEH, EvNode == Node, Name == N, Data == D] of
+ EH == TEH, EvNode == Node, Name == N,
+ match == match_data(D,Data)] of
[] ->
exit({unmatched,TEv});
_ ->
@@ -976,33 +1000,39 @@ locate({TEH,Name,Data}, Node, [{TEH,#event{name=Name,
data = EvData,
node = Node}}|Evs],
Config) ->
- try match_data(Data, EvData) of
+ case match_data(Data, EvData) of
match ->
- {Config,Evs}
- catch _:_ ->
+ {Config,Evs};
+ _ ->
nomatch
end;
locate({_TEH,_Name,_Data}, _Node, [_|_Evs], _Config) ->
nomatch.
-match_data(D,D) ->
+match_data(Data, EvData) ->
+ try do_match_data(Data, EvData)
+ catch _:_ ->
+ nomatch
+ end.
+
+do_match_data(D,D) ->
match;
-match_data('_',_) ->
+do_match_data('_',_) ->
match;
-match_data(Fun,Data) when is_function(Fun) ->
+do_match_data(Fun,Data) when is_function(Fun) ->
Fun(Data);
-match_data('$proplist',Proplist) ->
- match_data(
+do_match_data('$proplist',Proplist) ->
+ do_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([],[]) ->
+do_match_data([H1|MatchT],[H2|ValT]) ->
+ do_match_data(H1,H2),
+ do_match_data(MatchT,ValT);
+do_match_data(Tuple1,Tuple2) when is_tuple(Tuple1),is_tuple(Tuple2) ->
+ do_match_data(tuple_to_list(Tuple1),tuple_to_list(Tuple2));
+do_match_data([],[]) ->
match.
result_match({SkipOrFail,{ErrorInd,{Why,'_'}}},
@@ -1013,10 +1043,13 @@ result_match({SkipOrFail,{ErrorInd,{EMod,EFunc,{Why,'_'}}}},
true;
result_match({failed,{timetrap_timeout,{'$approx',Num}}},
{failed,{timetrap_timeout,Value}}) ->
- if Value >= trunc(Num-0.02*Num),
- Value =< trunc(Num+0.02*Num) -> true;
+ if Value >= trunc(Num-0.05*Num),
+ Value =< trunc(Num+0.05*Num) -> true;
true -> false
end;
+result_match({user_timetrap_error,{Why,'_'}},
+ {user_timetrap_error,{Why,_Stack}}) ->
+ true;
result_match(Result, Result) ->
true;
result_match(_, _) ->
@@ -1233,3 +1266,22 @@ rm_files([F | Fs]) ->
rm_files([]) ->
ok.
+%%%-----------------------------------------------------------------
+%%%
+slave_stop(Node) ->
+ Cover = test_server:is_cover(),
+ if Cover-> cover:flush(Node);
+ true -> ok
+ end,
+ erlang:monitor_node(Node, true),
+ slave:stop(Node),
+ receive
+ {nodedown, Node} ->
+ if Cover -> cover:stop(Node);
+ true -> ok
+ end
+ after 5000 ->
+ erlang:monitor_node(Node, false),
+ receive {nodedown, Node} -> ok after 0 -> ok end %flush
+ end,
+ ok.
diff --git a/lib/common_test/test/ct_testspec_1_SUITE.erl b/lib/common_test/test/ct_testspec_1_SUITE.erl
index b6dcf63fdf..6a4a4acd80 100644
--- a/lib/common_test/test/ct_testspec_1_SUITE.erl
+++ b/lib/common_test/test/ct_testspec_1_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -58,7 +58,7 @@ end_per_testcase(TestCase, Config) ->
suite() -> [{ct_hooks,[ts_install_cth]}].
-all() ->
+all() ->
[all_suites, skip_all_suites, suite, skip_suite,
all_testcases, skip_all_testcases, testcase,
skip_testcase, all_groups, skip_all_groups, group,
@@ -67,23 +67,23 @@ all() ->
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,
+ sub_skipped_by_top, testcase_many_groups,
+ order_of_tests_many_dirs_no_merge_tests,
+ order_of_tests_many_suites_no_merge_tests,
+ order_of_suites_many_dirs_no_merge_tests,
+ order_of_groups_many_dirs_no_merge_tests,
+ order_of_groups_many_suites_no_merge_tests,
+ order_of_tests_many_dirs,
+ order_of_tests_many_suites,
+ order_of_suites_many_dirs,
+ order_of_groups_many_dirs,
+ order_of_groups_many_suites,
+ order_of_tests_many_suites_with_skip_no_merge_tests,
+ order_of_tests_many_suites_with_skip,
all_plus_one_tc_no_merge_tests,
all_plus_one_tc].
-groups() ->
+groups() ->
[].
init_per_group(_GroupName, Config) ->
@@ -373,19 +373,19 @@ sub_skipped_by_top(Config) when is_list(Config) ->
%%%-----------------------------------------------------------------
%%%
-testcase_in_multiple_groups(Config) when is_list(Config) ->
+testcase_many_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).
+ setup_and_execute(testcase_many_groups, TestSpec, Config).
%%%-----------------------------------------------------------------
%%%
-order_of_tests_in_multiple_dirs_no_merge_tests(Config) when is_list(Config) ->
+order_of_tests_many_dirs_no_merge_tests(Config) when is_list(Config) ->
DataDir = ?config(data_dir, Config),
TestDir1 = filename:join(DataDir, "groups_1"),
@@ -395,13 +395,13 @@ order_of_tests_in_multiple_dirs_no_merge_tests(Config) when is_list(Config) ->
{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,
+ setup_and_execute(order_of_tests_many_dirs_no_merge_tests,
TestSpec, Config).
%%%-----------------------------------------------------------------
%%%
-order_of_tests_in_multiple_suites_no_merge_tests(Config) when is_list(Config) ->
+order_of_tests_many_suites_no_merge_tests(Config) when is_list(Config) ->
DataDir = ?config(data_dir, Config),
TestDir1 = filename:join(DataDir, "groups_1"),
@@ -410,13 +410,13 @@ order_of_tests_in_multiple_suites_no_merge_tests(Config) when is_list(Config) ->
{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,
+ setup_and_execute(order_of_tests_many_suites_no_merge_tests,
TestSpec, Config).
%%%-----------------------------------------------------------------
%%%
-order_of_suites_in_multiple_dirs_no_merge_tests(Config) when is_list(Config) ->
+order_of_suites_many_dirs_no_merge_tests(Config) when is_list(Config) ->
DataDir = ?config(data_dir, Config),
TestDir1 = filename:join(DataDir, "groups_1"),
@@ -426,13 +426,13 @@ order_of_suites_in_multiple_dirs_no_merge_tests(Config) when is_list(Config) ->
{suites,TestDir2,groups_22_SUITE},
{suites,TestDir1,groups_11_SUITE}],
- setup_and_execute(order_of_suites_in_multiple_dirs_no_merge_tests,
+ setup_and_execute(order_of_suites_many_dirs_no_merge_tests,
TestSpec, Config).
%%%-----------------------------------------------------------------
%%%
-order_of_groups_in_multiple_dirs_no_merge_tests(Config) when is_list(Config) ->
+order_of_groups_many_dirs_no_merge_tests(Config) when is_list(Config) ->
DataDir = ?config(data_dir, Config),
TestDir1 = filename:join(DataDir, "groups_1"),
@@ -442,13 +442,13 @@ order_of_groups_in_multiple_dirs_no_merge_tests(Config) when is_list(Config) ->
{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,
+ setup_and_execute(order_of_groups_many_dirs_no_merge_tests,
TestSpec, Config).
%%%-----------------------------------------------------------------
%%%
-order_of_groups_in_multiple_suites_no_merge_tests(Config)
+order_of_groups_many_suites_no_merge_tests(Config)
when is_list(Config) ->
DataDir = ?config(data_dir, Config),
@@ -458,13 +458,13 @@ order_of_groups_in_multiple_suites_no_merge_tests(Config)
{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,
+ setup_and_execute(order_of_groups_many_suites_no_merge_tests,
TestSpec, Config).
%%%-----------------------------------------------------------------
%%%
-order_of_tests_in_multiple_suites_with_skip_no_merge_tests(Config)
+order_of_tests_many_suites_with_skip_no_merge_tests(Config)
when is_list(Config) ->
DataDir = ?config(data_dir, Config),
@@ -477,14 +477,14 @@ order_of_tests_in_multiple_suites_with_skip_no_merge_tests(Config)
{skip_cases,TestDir1,groups_12_SUITE,[testcase_1b],"Skip it"}],
setup_and_execute(
- order_of_tests_in_multiple_suites_with_skip_no_merge_tests,
+ order_of_tests_many_suites_with_skip_no_merge_tests,
TestSpec, Config).
%%%-----------------------------------------------------------------
%%%
-order_of_tests_in_multiple_dirs(Config) when is_list(Config) ->
+order_of_tests_many_dirs(Config) when is_list(Config) ->
DataDir = ?config(data_dir, Config),
TestDir1 = filename:join(DataDir, "groups_1"),
@@ -493,13 +493,13 @@ order_of_tests_in_multiple_dirs(Config) when is_list(Config) ->
{cases,TestDir2,groups_22_SUITE,[testcase_1]},
{cases,TestDir1,groups_12_SUITE,[testcase_1b]}],
- setup_and_execute(order_of_tests_in_multiple_dirs,
+ setup_and_execute(order_of_tests_many_dirs,
TestSpec, Config).
%%%-----------------------------------------------------------------
%%%
-order_of_tests_in_multiple_suites(Config) when is_list(Config) ->
+order_of_tests_many_suites(Config) when is_list(Config) ->
DataDir = ?config(data_dir, Config),
TestDir1 = filename:join(DataDir, "groups_1"),
@@ -507,13 +507,13 @@ order_of_tests_in_multiple_suites(Config) when is_list(Config) ->
{cases,TestDir1,groups_11_SUITE,[testcase_1]},
{cases,TestDir1,groups_12_SUITE,[testcase_1b]}],
- setup_and_execute(order_of_tests_in_multiple_suites,
+ setup_and_execute(order_of_tests_many_suites,
TestSpec, Config).
%%%-----------------------------------------------------------------
%%%
-order_of_suites_in_multiple_dirs(Config) when is_list(Config) ->
+order_of_suites_many_dirs(Config) when is_list(Config) ->
DataDir = ?config(data_dir, Config),
TestDir1 = filename:join(DataDir, "groups_1"),
@@ -522,13 +522,13 @@ order_of_suites_in_multiple_dirs(Config) when is_list(Config) ->
{suites,TestDir2,groups_22_SUITE},
{suites,TestDir1,groups_11_SUITE}],
- setup_and_execute(order_of_suites_in_multiple_dirs,
+ setup_and_execute(order_of_suites_many_dirs,
TestSpec, Config).
%%%-----------------------------------------------------------------
%%%
-order_of_groups_in_multiple_dirs(Config) when is_list(Config) ->
+order_of_groups_many_dirs(Config) when is_list(Config) ->
DataDir = ?config(data_dir, Config),
TestDir1 = filename:join(DataDir, "groups_1"),
@@ -537,13 +537,13 @@ order_of_groups_in_multiple_dirs(Config) when is_list(Config) ->
{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,
+ setup_and_execute(order_of_groups_many_dirs,
TestSpec, Config).
%%%-----------------------------------------------------------------
%%%
-order_of_groups_in_multiple_suites(Config) when is_list(Config) ->
+order_of_groups_many_suites(Config) when is_list(Config) ->
DataDir = ?config(data_dir, Config),
TestDir1 = filename:join(DataDir, "groups_1"),
@@ -551,13 +551,13 @@ order_of_groups_in_multiple_suites(Config) when is_list(Config) ->
{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,
+ setup_and_execute(order_of_groups_many_suites,
TestSpec, Config).
%%%-----------------------------------------------------------------
%%%
-order_of_tests_in_multiple_suites_with_skip(Config) when is_list(Config) ->
+order_of_tests_many_suites_with_skip(Config) when is_list(Config) ->
DataDir = ?config(data_dir, Config),
TestDir1 = filename:join(DataDir, "groups_1"),
@@ -567,7 +567,7 @@ order_of_tests_in_multiple_suites_with_skip(Config) when is_list(Config) ->
{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,
+ setup_and_execute(order_of_tests_many_suites_with_skip,
TestSpec, Config).
%%%-----------------------------------------------------------------
@@ -621,7 +621,9 @@ setup_and_execute(TCName, TestSpec, 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),
+ {_Ok,_Failed,{_USkipped,_ASkipped}} =
+ ct_test_support:run(ct, run_testspec, [TestSpec1], Config),
+
Events = ct_test_support:get_events(ERPid, Config),
ct_test_support:log_events(TCName,
@@ -1202,10 +1204,10 @@ test_events(sub_skipped_by_top) ->
{negative,{?eh,tc_start,'_'},{?eh,stop_logging,'_'}}
];
-test_events(testcase_in_multiple_groups) ->
+test_events(testcase_many_groups) ->
[];
-test_events(order_of_tests_in_multiple_dirs_no_merge_tests) ->
+test_events(order_of_tests_many_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,
@@ -1217,7 +1219,7 @@ test_events(order_of_tests_in_multiple_dirs_no_merge_tests) ->
{failed,{error,{test_case_failed,no_group_data}}}}},
{?eh,stop_logging,[]}
];
-test_events(order_of_tests_in_multiple_suites_no_merge_tests) ->
+test_events(order_of_tests_many_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,'_'}},
@@ -1227,7 +1229,7 @@ test_events(order_of_tests_in_multiple_suites_no_merge_tests) ->
{?eh,tc_done,{groups_12_SUITE,testcase_1b,'_'}},
{?eh,stop_logging,[]}
];
-test_events(order_of_suites_in_multiple_dirs_no_merge_tests) ->
+test_events(order_of_suites_many_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,'_'}},
@@ -1242,7 +1244,7 @@ test_events(order_of_suites_in_multiple_dirs_no_merge_tests) ->
{?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) ->
+test_events(order_of_groups_many_dirs_no_merge_tests) ->
[{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,tc_start, {groups_12_SUITE,{init_per_group,test_group_1a,'_'}}},
@@ -1255,7 +1257,7 @@ test_events(order_of_groups_in_multiple_dirs_no_merge_tests) ->
{?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) ->
+test_events(order_of_groups_many_suites_no_merge_tests) ->
[{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,tc_start, {groups_12_SUITE,{init_per_group,test_group_1a,'_'}}},
@@ -1268,7 +1270,7 @@ test_events(order_of_groups_in_multiple_suites_no_merge_tests) ->
{?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) ->
+test_events(order_of_tests_many_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,'_'}},
@@ -1280,7 +1282,7 @@ test_events(order_of_tests_in_multiple_suites_with_skip_no_merge_tests) ->
{?eh,stop_logging,[]}
];
-test_events(order_of_tests_in_multiple_dirs) ->
+test_events(order_of_tests_many_dirs) ->
[{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,tc_start,{groups_12_SUITE,testcase_1a}},
{?eh,tc_done,
@@ -1294,7 +1296,7 @@ test_events(order_of_tests_in_multiple_dirs) ->
{?eh,tc_done,{groups_22_SUITE,testcase_1,ok}},
{?eh,stop_logging,[]}
];
-test_events(order_of_tests_in_multiple_suites) ->
+test_events(order_of_tests_many_suites) ->
[{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,tc_start,{groups_12_SUITE,testcase_1a}},
{?eh,tc_done,{groups_12_SUITE,testcase_1a,'_'}},
@@ -1306,7 +1308,7 @@ test_events(order_of_tests_in_multiple_suites) ->
{?eh,tc_done,{groups_11_SUITE,testcase_1,ok}},
{?eh,stop_logging,[]}
];
-test_events(order_of_suites_in_multiple_dirs) ->
+test_events(order_of_suites_many_dirs) ->
[{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,tc_start,{groups_12_SUITE,init_per_suite}},
{?eh,tc_done,{groups_12_SUITE,init_per_suite,'_'}},
@@ -1323,7 +1325,7 @@ test_events(order_of_suites_in_multiple_dirs) ->
{?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) ->
+test_events(order_of_groups_many_dirs) ->
[{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,tc_start, {groups_12_SUITE,{init_per_group,test_group_1a,'_'}}},
@@ -1336,7 +1338,7 @@ test_events(order_of_groups_in_multiple_dirs) ->
{?eh,tc_done, {groups_22_SUITE,{end_per_group,test_group_1a,'_'},'_'}},
{?eh,stop_logging,[]}];
-test_events(order_of_groups_in_multiple_suites) ->
+test_events(order_of_groups_many_suites) ->
[{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,tc_start, {groups_12_SUITE,{init_per_group,test_group_1a,'_'}}},
@@ -1350,7 +1352,7 @@ test_events(order_of_groups_in_multiple_suites) ->
{?eh,stop_logging,[]}];
-test_events(order_of_tests_in_multiple_suites_with_skip) ->
+test_events(order_of_tests_many_suites_with_skip) ->
[{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,tc_start,{groups_12_SUITE,testcase_1a}},
{?eh,tc_done,{groups_12_SUITE,testcase_1a,'_'}},
diff --git a/lib/common_test/test/ct_testspec_2_SUITE.erl b/lib/common_test/test/ct_testspec_2_SUITE.erl
new file mode 100644
index 0000000000..9d2dc84ad3
--- /dev/null
+++ b/lib/common_test/test/ct_testspec_2_SUITE.erl
@@ -0,0 +1,759 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2009-2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, 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_2_SUITE
+%%%
+%%% Description:
+%%% Test test specifications
+%%%
+%%% The suites used for the test are located in the data directory.
+%%%-------------------------------------------------------------------
+-module(ct_testspec_2_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("common_test/src/ct_util.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.
+
+%% suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [basic_compatible_no_nodes,
+ basic_compatible_nodes,
+ unknown_terms,
+ no_merging,
+ multiple_specs,
+ misc_config_terms,
+ define_names_1].
+
+
+%%--------------------------------------------------------------------
+%% VALID TEST SPEC TERMS (R15B02):
+%%
+%% {node,3}
+%% {cover,2}
+%% {cover,3}
+%% {config,2}
+%% {config,3}
+%% {config,4}
+%% {userconfig,2}
+%% {userconfig,3}
+%% {alias,3}
+%% {merge_tests,2}
+%% {logdir,2}
+%% {logdir,3}
+%% {logopts,2}
+%% {logopts,3}
+%% {basic_html,2}
+%% {basic_html,3}
+%% {verbosity,2}
+%% {verbosity,3}
+%% {silent_connections,2}
+%% {silent_connections,3}
+%% {label,2}
+%% {label,3}
+%% {event_handler,2}
+%% {event_handler,3}
+%% {event_handler,4}
+%% {ct_hooks,2}
+%% {ct_hooks,3}
+%% {enable_builtin_hooks,2}
+%% {release_shell,2}
+%% {multiply_timetraps,2}
+%% {multiply_timetraps,3}
+%% {scale_timetraps,2}
+%% {scale_timetraps,3}
+%% {include,2}
+%% {include,3}
+%% {auto_compile,2}
+%% {auto_compile,3}
+%% {stylesheet,2}
+%% {stylesheet,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}
+%% {create_priv_dir,2}
+%%
+%%--------------------------------------------------------------------
+
+%%--------------------------------------------------------------------
+%% TEST CASES
+%%--------------------------------------------------------------------
+
+%%%-----------------------------------------------------------------
+%%%
+basic_compatible_no_nodes(_Config) ->
+
+ AliasDir1 = "../tests/to1",
+ AliasDir2 = "../tests/to2",
+ CfgDir1 = "../cfgs/to1/x.cfg",
+ CfgDir2 = ["../cfgs/to2/x.cfg","../cfgs/to2/y.cfg"],
+ LogDir = "../logs",
+ IncludeDir1 = "../../include",
+ IncludeDir2 = ["../tests/to1/include","../tests/to2/include"],
+
+ Spec =
+ [
+ {label,"basic_compatible_no_nodes"},
+ {alias,to1,AliasDir1},
+ {alias,to2,AliasDir2},
+ {config,CfgDir1},
+ {config,CfgDir2},
+ {userconfig,{?MODULE,"cfg_str1"}},
+ {userconfig,{?MODULE,"cfg_str2"}},
+ {logdir,LogDir},
+ {logopts,[no_nl]},
+ {event_handler,evh1,[1]},
+ {event_handler,[evh2,evh3],[[2,3]]},
+ {ct_hooks,[{cth_mod1,[]}]},
+ {ct_hooks,[{cth_mod2,[]}]},
+ {multiply_timetraps,2},
+ {include,IncludeDir1},
+ {include,IncludeDir2},
+ {suites,to1,[x_SUITE]},
+ {groups,to1,y_SUITE,[g1,g2]},
+ {cases,to1,y_SUITE,[tc1,tc2]},
+ {skip_suites,to1,z_SUITE,"skipped"},
+ {suites,to2,[x_SUITE,y_SUITE]},
+ {skip_groups,to2,x_SUITE,[g1,g2],"skipped"},
+ {skip_cases,to2,y_SUITE,[tc1,tc2],"skipped"}
+ ],
+
+ {ok,SpecDir} = file:get_cwd(),
+
+ ListResult = ct_testspec:collect_tests_from_list(Spec, false),
+ ct:pal("TESTSPEC RECORD FROM LIST:~n~p~n", [rec2proplist(ListResult)]),
+ SpecFile = ct_test_support:write_testspec(Spec,SpecDir,
+ "basic_compatible_no_nodes.spec"),
+ FileResult = ct_testspec:collect_tests_from_file([SpecFile], false),
+ ct:pal("TESTSPEC RECORD FROM FILE:~n~p~n", [rec2proplist(FileResult)]),
+
+ Node = node(),
+ LogDirV = get_absdir(filename:join(SpecDir,"../logs")),
+ Alias1V = get_absdir(filename:join(SpecDir,AliasDir1)),
+ Alias2V = get_absdir(filename:join(SpecDir,AliasDir2)),
+ CFGs = [{Node,get_absdir(filename:join(SpecDir,CfgDir))} ||
+ CfgDir <- [CfgDir1 | CfgDir2]],
+ Incls = [{Node,get_absdir(filename:join(SpecDir,IncludeDir))} ||
+ IncludeDir <- [IncludeDir1 | IncludeDir2]],
+
+ Verify = #testspec{spec_dir = SpecDir,
+ nodes = [{undefined,Node}],
+ init = [],
+ label = [{Node,"basic_compatible_no_nodes"}],
+ logdir = [{Node,LogDirV},"."],
+ logopts = [{Node,[no_nl]}],
+ basic_html = [],
+ cover = [],
+ config = CFGs,
+ userconfig = [{Node,{?MODULE,"cfg_str1"}},
+ {Node,{?MODULE,"cfg_str2"}}],
+ event_handler = [{Node,evh1,[1]},
+ {Node,evh2,[[2,3]]},
+ {Node,evh3,[[2,3]]}],
+ ct_hooks = [{Node,{cth_mod1,[]}},
+ {Node,{cth_mod2,[]}}],
+ enable_builtin_hooks = true,
+ release_shell = false,
+ include = Incls,
+ auto_compile = [],
+ stylesheet = [],
+ multiply_timetraps = [{Node,2}],
+ scale_timetraps = [],
+ create_priv_dir = [],
+ alias = [{to1,Alias1V},{to2,Alias2V}],
+ tests = [{{Node,Alias1V},
+ [{x_SUITE,[all]},
+ {y_SUITE,[{g1,all},{g2,all},tc1,tc2]},
+ {z_SUITE,[{all,{skip,"skipped"}}]}]},
+ {{Node,Alias2V},
+ [{x_SUITE,[all,
+ {{g1,all},{skip,"skipped"}},
+ {{g2,all},{skip,"skipped"}}]},
+ {y_SUITE,[all,
+ {tc1,{skip,"skipped"}},
+ {tc2,{skip,"skipped"}}]}]}],
+ merge_tests = true},
+
+ verify_result(Verify,ListResult,FileResult).
+
+%%%-----------------------------------------------------------------
+%%%
+basic_compatible_nodes(_Config) ->
+
+ Node1 = node1@host1,
+ Node2 = node2@host2,
+ TODir1 = "../tests/to1",
+ TODir2 = "../tests/to2",
+ CfgDir1 = "../cfgs/to1/x.cfg",
+ CfgDir2 = ["../cfgs/to2/x.cfg","../cfgs/to2/y.cfg"],
+ LogDir = "../logs",
+ MasterLogDir = "../master_logs",
+ IncludeDir1 = "../../include",
+ IncludeDir2 = ["../tests/to1/include","../tests/to2/include"],
+
+ Spec =
+ [
+ {node,n1,Node1},
+ {node,n2,Node2},
+ {init,[n1],[{node_start,[{callback_module,cbm}]}]},
+ {init,n2,[{node_start,[]}]},
+ {init,all_nodes,{eval,{mod,func,[]}}},
+ {label,"basic_compatible_nodes"},
+ {label,n1,basic_compatible_nodes_1},
+ {config,n1,CfgDir1},
+ {config,n2,CfgDir2},
+ {userconfig,{?MODULE,"cfg_str1"}},
+ {userconfig,{?MODULE,"cfg_str2"}},
+ {logdir,all_nodes,LogDir},
+ {logdir,master,MasterLogDir},
+ {logopts,node2@host2,[no_nl]},
+ {event_handler,master,evh1,[1]},
+ {event_handler,[n1,n2],[evh2,evh3],[[2,3]]},
+ {ct_hooks,all_nodes,[{cth_mod1,[]}]},
+ {ct_hooks,[{cth_mod2,[]}]},
+ {multiply_timetraps,node1@host1,2},
+ {include,n1,IncludeDir1},
+ {include,[n1,n2],IncludeDir2},
+ {suites,n1,TODir1,[x_SUITE]},
+ {groups,n1,TODir1,y_SUITE,[g1,g2]},
+ {cases,n1,TODir1,y_SUITE,[tc1,tc2]},
+ {skip_suites,n1,TODir1,z_SUITE,"skipped"},
+ {suites,n2,TODir2,[x_SUITE,y_SUITE]},
+ {skip_groups,n2,TODir2,x_SUITE,[g1,g2],"skipped"},
+ {skip_cases,n2,TODir2,y_SUITE,[tc1,tc2],"skipped"}
+ ],
+
+ {ok,SpecDir} = file:get_cwd(),
+
+ ListResult = ct_testspec:collect_tests_from_list(Spec, false),
+ ct:pal("TESTSPEC RECORD FROM LIST:~n~p~n", [rec2proplist(ListResult)]),
+ SpecFile = ct_test_support:write_testspec(Spec,SpecDir,
+ "basic_compatible_nodes.spec"),
+ FileResult = ct_testspec:collect_tests_from_file([SpecFile], false),
+ ct:pal("TESTSPEC RECORD FROM FILE:~n~p~n", [rec2proplist(FileResult)]),
+
+ Node = node(),
+ LogDirV = get_absdir(filename:join(SpecDir,"../logs")),
+ MasterLogDirV = get_absdir(filename:join(SpecDir,"../master_logs")),
+ TO1V = get_absdir(filename:join(SpecDir,TODir1)),
+ TO2V = get_absdir(filename:join(SpecDir,TODir2)),
+ CFGs = [{Node1,get_absdir(filename:join(SpecDir,CfgDir1))} |
+ [{Node2,get_absdir(filename:join(SpecDir,CfgDir))} || CfgDir <- CfgDir2]],
+ Incls = [{Node1,get_absdir(filename:join(SpecDir,IncludeDir1))} |
+ [{Node1,get_absdir(filename:join(SpecDir,IncludeDir))} ||
+ IncludeDir <- IncludeDir2] ++
+ [{Node2,get_absdir(filename:join(SpecDir,IncludeDir))} ||
+ IncludeDir <- IncludeDir2]],
+
+ Verify = #testspec{spec_dir = SpecDir,
+ nodes = [{undefined,Node},{n1,Node1},{n2,Node2}],
+ init = [{Node1,[{node_start,[{callback_module,cbm}]},
+ {eval,[{mod,func,[]}]}]},
+ {Node2,[{node_start,[{callback_module,ct_slave}]},
+ {eval,[{mod,func,[]}]}]},
+ {Node,[{node_start,[]},
+ {eval,[{mod,func,[]}]}]}],
+ label = [{Node,"basic_compatible_nodes"},
+ {Node2,"basic_compatible_nodes"},
+ {Node1,basic_compatible_nodes_1}],
+ logdir = [{Node,LogDirV},{Node1,LogDirV},{Node2,LogDirV},
+ {master,MasterLogDirV},"."],
+ logopts = [{Node2,[no_nl]}],
+ basic_html = [],
+ cover = [],
+ config = CFGs,
+ userconfig = [{Node,{?MODULE,"cfg_str1"}},
+ {Node1,{?MODULE,"cfg_str1"}},
+ {Node2,{?MODULE,"cfg_str1"}},
+ {Node,{?MODULE,"cfg_str2"}},
+ {Node1,{?MODULE,"cfg_str2"}},
+ {Node2,{?MODULE,"cfg_str2"}}],
+ event_handler = [{master,evh1,[1]},
+ {Node1,evh2,[[2,3]]},
+ {Node1,evh3,[[2,3]]},
+ {Node2,evh2,[[2,3]]},
+ {Node2,evh3,[[2,3]]}],
+ ct_hooks = [{Node,{cth_mod1,[]}},
+ {Node1,{cth_mod1,[]}},
+ {Node2,{cth_mod1,[]}},
+ {Node,{cth_mod2,[]}},
+ {Node1,{cth_mod2,[]}},
+ {Node2,{cth_mod2,[]}}],
+ enable_builtin_hooks = true,
+ release_shell = false,
+ include = Incls,
+ auto_compile = [],
+ stylesheet = [],
+ multiply_timetraps = [{Node1,2}],
+ scale_timetraps = [],
+ create_priv_dir = [],
+ tests = [{{Node1,TO1V},
+ [{x_SUITE,[all]},
+ {y_SUITE,[{g1,all},{g2,all},tc1,tc2]},
+ {z_SUITE,[{all,{skip,"skipped"}}]}]},
+ {{Node2,TO2V},
+ [{x_SUITE,[all,
+ {{g1,all},{skip,"skipped"}},
+ {{g2,all},{skip,"skipped"}}]},
+ {y_SUITE,[all,
+ {tc1,{skip,"skipped"}},
+ {tc2,{skip,"skipped"}}]}]}],
+ merge_tests = true},
+
+ verify_result(Verify,ListResult,FileResult).
+
+%%%-----------------------------------------------------------------
+%%%
+unknown_terms(Config) ->
+ PrivDir = ?config(priv_dir, Config),
+
+ Spec1 = [{suites,PrivDir,all},
+ {userdata,"I've got news for you"}],
+ {error,{undefined_term_in_spec,{userdata,_}}} =
+ (catch ct_testspec:collect_tests_from_list(Spec1, false)),
+ true = is_record(ct_testspec:collect_tests_from_list(Spec1, true),
+ testspec),
+
+ Spec2 = [{logdir,{logdir,PrivDir}}],
+ {error,{invalid_directory_name,_}} =
+ (catch ct_testspec:collect_tests_from_list(Spec2, false)),
+
+ Spec3 = [{suite,PrivDir,all}],
+ {error,{undefined_term_in_spec,{suite,_,_}}} =
+ (catch ct_testspec:collect_tests_from_list(Spec3, false)),
+ true = is_record(ct_testspec:collect_tests_from_list(Spec3, true), testspec),
+
+ Spec4 = [{suites,PrivDir,all},
+ {skip_suites,PrivDir,x_SUITE}],
+ {error,{bad_term_in_spec,{skip_suites,_,_}}} =
+ (catch ct_testspec:collect_tests_from_list(Spec4, false)),
+ {error,{bad_term_in_spec,{skip_suites,_,_}}} =
+ (catch ct_testspec:collect_tests_from_list(Spec4, true)),
+
+ Spec5 = [{configs,all_nodes,PrivDir}],
+ {error,{undefined_term_in_spec,{configs,_,_}}} =
+ (catch ct_testspec:collect_tests_from_list(Spec5, false)),
+ true = is_record(ct_testspec:collect_tests_from_list(Spec5, true), testspec),
+
+ ok.
+
+%%%-----------------------------------------------------------------
+%%%
+no_merging(_Config) ->
+ Node1 = node1@host1,
+ Node2 = node2@host2,
+ TODir1 = "../tests/to1",
+ TODir2 = "../tests/to2",
+ Spec =
+ [
+ {merge_tests,false},
+ {node,n1,Node1},
+ {node,n2,Node2},
+ {suites,n1,TODir1,[x_SUITE]},
+ {groups,n1,TODir1,y_SUITE,[g1,g2]},
+ {cases,n1,TODir1,y_SUITE,[tc1,tc2]},
+ {skip_suites,n1,TODir1,z_SUITE,"skipped"},
+ {suites,n2,TODir2,[x_SUITE,y_SUITE]},
+ {skip_groups,n2,TODir2,x_SUITE,[g1,g2],"skipped"},
+ {skip_cases,n2,TODir2,y_SUITE,[tc1,tc2],"skipped"}
+ ],
+
+ {ok,SpecDir} = file:get_cwd(),
+
+ ListResult = ct_testspec:collect_tests_from_list(Spec, false),
+ ct:pal("TESTSPEC RECORD FROM LIST:~n~p~n", [rec2proplist(ListResult)]),
+ SpecFile = ct_test_support:write_testspec(Spec,SpecDir,
+ "no_merging.spec"),
+ FileResult = ct_testspec:collect_tests_from_file([SpecFile], false),
+ ct:pal("TESTSPEC RECORD FROM FILE:~n~p~n", [rec2proplist(FileResult)]),
+
+ Node = node(),
+ TO1V = get_absdir(filename:join(SpecDir,TODir1)),
+ TO2V = get_absdir(filename:join(SpecDir,TODir2)),
+
+ Verify = #testspec{merge_tests = false,
+ spec_dir = SpecDir,
+ nodes = [{undefined,Node},{n1,Node1},{n2,Node2}],
+ tests = [{{Node1,TO1V},
+ [{x_SUITE,[all]}]},
+ {{Node1,TO1V},
+ [{y_SUITE,[{g1,all},{g2,all}]}]},
+ {{Node1,TO1V},
+ [{y_SUITE,[tc1,tc2]}]},
+ {{Node1,TO1V},
+ [{z_SUITE,[{all,{skip,"skipped"}}]}]},
+ {{Node2,TO2V},
+ [{x_SUITE,[all]}]},
+ {{Node2,TO2V},
+ [{y_SUITE,[all]}]},
+ {{Node2,TO2V},
+ [{x_SUITE,[{{g1,all},{skip,"skipped"}},
+ {{g2,all},{skip,"skipped"}}]}]},
+ {{Node2,TO2V},
+ [{y_SUITE,[{tc1,{skip,"skipped"}},
+ {tc2,{skip,"skipped"}}]}]}]},
+
+ verify_result(Verify,ListResult,FileResult).
+
+%%%-----------------------------------------------------------------
+%%%
+multiple_specs(_Config) ->
+ Node1 = node1@host1,
+ Node2 = node2@host2,
+ TODir1 = "../tests/to1",
+ TODir2 = "../tests/to2",
+ CfgDir1 = "../cfgs/to1/x.cfg",
+ CfgDir2 = ["../cfgs/to2/x.cfg","../cfgs/to2/y.cfg"],
+ LogDir = "../logs",
+ Spec1 =
+ [
+ {node,n1,Node1},
+ {node,n2,Node2},
+ {alias,to1,TODir1},
+ {alias,to2,TODir2},
+ {label,"multiple_specs1"},
+ {config,n1,CfgDir1},
+ {config,n2,CfgDir2},
+ {logdir,all_nodes,LogDir}
+ ],
+ Spec2 =
+ [
+ {merge_tests,false},
+ {label,"multiple_specs2"},
+ {suites,n1,TODir1,[x_SUITE]},
+ {groups,n1,TODir1,y_SUITE,[g1,g2]},
+ {cases,n1,TODir1,y_SUITE,[tc1,tc2]},
+ {skip_suites,n1,TODir1,z_SUITE,"skipped"},
+ {suites,n2,TODir2,[x_SUITE,y_SUITE]},
+ {skip_groups,n2,TODir2,x_SUITE,[g1,g2],"skipped"},
+ {skip_cases,n2,TODir2,y_SUITE,[tc1,tc2],"skipped"}
+ ],
+
+ {ok,SpecDir} = file:get_cwd(),
+ SpecFile1 = ct_test_support:write_testspec(Spec1,SpecDir,
+ "multiple_specs.1.spec"),
+ SpecFile2 = ct_test_support:write_testspec(Spec2,SpecDir,
+ "multiple_specs.2.spec"),
+ FileResult = ct_testspec:collect_tests_from_file([SpecFile1,SpecFile2],
+ false),
+ ct:pal("TESTSPEC RECORD FROM FILE:~n~p~n", [rec2proplist(FileResult)]),
+
+ Node = node(),
+ TO1V = get_absdir(filename:join(SpecDir,TODir1)),
+ TO2V = get_absdir(filename:join(SpecDir,TODir2)),
+ CFGs = [{Node1,get_absdir(filename:join(SpecDir,CfgDir1))} |
+ [{Node2,get_absdir(filename:join(SpecDir,CfgDir))} || CfgDir <- CfgDir2]],
+ LogDirV = get_absdir(filename:join(SpecDir,"../logs")),
+
+ Verify = #testspec{merge_tests = false,
+ spec_dir = SpecDir,
+ nodes = [{undefined,Node},{n1,Node1},{n2,Node2}],
+ alias = [{to1,TO1V},{to2,TO2V}],
+ label = [{Node,"multiple_specs1"},
+ {Node1,"multiple_specs1"},
+ {Node2,"multiple_specs1"}],
+ logdir = [{Node,LogDirV},{Node1,LogDirV},{Node2,LogDirV},"."],
+ config = CFGs,
+ tests = [{{Node1,TO1V},
+ [{x_SUITE,[all]}]},
+ {{Node1,TO1V},
+ [{y_SUITE,[{g1,all},{g2,all}]}]},
+ {{Node1,TO1V},
+ [{y_SUITE,[tc1,tc2]}]},
+ {{Node1,TO1V},
+ [{z_SUITE,[{all,{skip,"skipped"}}]}]},
+ {{Node2,TO2V},
+ [{x_SUITE,[all]}]},
+ {{Node2,TO2V},
+ [{y_SUITE,[all]}]},
+ {{Node2,TO2V},
+ [{x_SUITE,[{{g1,all},{skip,"skipped"}},
+ {{g2,all},{skip,"skipped"}}]}]},
+ {{Node2,TO2V},
+ [{y_SUITE,[{tc1,{skip,"skipped"}},
+ {tc2,{skip,"skipped"}}]}]}]},
+
+ verify_result(Verify,FileResult,FileResult).
+
+%%%-----------------------------------------------------------------
+%%%
+misc_config_terms(_Config) ->
+ CfgDir = "../cfgs/to1",
+
+ Spec =
+ [{node,x,n1@h1},{node,y,n2@h2},
+
+ {config,CfgDir,"a.cfg"},
+ {config,n1@h1,CfgDir,"b.cfg"},
+ {config,all_nodes,CfgDir,"c.cfg"},
+ {config,all_nodes,filename:join(CfgDir,"d.cfg")},
+
+ {basic_html,true},
+ {basic_html,n1@h1,false},
+ {basic_html,n2@h2,true},
+
+ {silent_connections,n1@h1,all},
+ {silent_connections,n2@h2,[ssh]},
+
+ {enable_builtin_hooks,false},
+
+ {release_shell,true},
+
+ {auto_compile,false},
+ {auto_compile,n1@h1,true},
+ {auto_compile,n2@h2,false},
+
+ {stylesheet,"../css"},
+ {stylesheet,n1@h1,"./n1/css"},
+ {stylesheet,n2@h2,"./n2/css"},
+
+ {create_priv_dir,[auto_per_tc]},
+ {create_priv_dir,n1@h1,[manual_per_tc]},
+ {create_priv_dir,n2@h2,[auto_per_run]}
+ ],
+
+ {ok,SpecDir} = file:get_cwd(),
+
+ ListResult = ct_testspec:collect_tests_from_list(Spec, false),
+ ct:pal("TESTSPEC RECORD FROM LIST:~n~p~n", [rec2proplist(ListResult)]),
+ SpecFile = ct_test_support:write_testspec(Spec,SpecDir,
+ "misc_config_terms.spec"),
+ FileResult = ct_testspec:collect_tests_from_file([SpecFile], false),
+ ct:pal("TESTSPEC RECORD FROM FILE:~n~p~n", [rec2proplist(FileResult)]),
+
+ Node = node(),
+ CfgA = get_absdir(filename:join(filename:join(SpecDir,CfgDir), "a.cfg")),
+ CfgB = get_absdir(filename:join(filename:join(SpecDir,CfgDir), "b.cfg")),
+ CfgC = get_absdir(filename:join(filename:join(SpecDir,CfgDir), "c.cfg")),
+ CfgD = get_absdir(filename:join(filename:join(SpecDir,CfgDir), "d.cfg")),
+ CSS = get_absdir(filename:join(SpecDir,"../css")),
+ CSS1 = get_absdir(filename:join(SpecDir,"./n1/css")),
+ CSS2 = get_absdir(filename:join(SpecDir,"./n2/css")),
+
+ Verify = #testspec{spec_dir = SpecDir,
+ nodes = [{undefined,Node},{x,n1@h1},{y,n2@h2}],
+ basic_html = [{Node,true},{n1@h1,false},{n2@h2,true}],
+ silent_connections = [{n1@h1,[all]},{n2@h2,[ssh]}],
+ config = [{Node,CfgA},
+ {n1@h1,CfgA},
+ {n2@h2,CfgA},
+ {n1@h1,CfgB},
+ {Node,CfgC},
+ {n1@h1,CfgC},
+ {n2@h2,CfgC},
+ {Node,CfgD},
+ {n1@h1,CfgD},
+ {n2@h2,CfgD}],
+ enable_builtin_hooks = false,
+ release_shell = true,
+ auto_compile = [{Node,false},
+ {n1@h1,true},
+ {n2@h2,false}],
+ stylesheet = [{Node,CSS},
+ {n1@h1,CSS1},
+ {n2@h2,CSS2}],
+ create_priv_dir = [{Node,[auto_per_tc]},
+ {n1@h1,[manual_per_tc]},
+ {n2@h2,[auto_per_run]}]
+ },
+
+ verify_result(Verify,ListResult,FileResult).
+
+%%%-----------------------------------------------------------------
+%%%
+define_names_1(_Config) ->
+ Spec =
+ [
+ {define,'HOST','eniac'},
+ {define,'NODE1',testnode1},
+ {define,'NODE2',testnode2},
+ {define,'NODES',['NODE1@HOST',
+ 'NODE2@HOST']},
+ {define,'TOPDIR',".."},
+ {define,'TO1',"to1"},
+ {define,'TO2',"to2"},
+ {define,'LOGDIR',"'TOPDIR'/logdir"},
+ {define,'LOGDIR1',"'TOPDIR'/logdir1"},
+ {define,'LOGDIR2',"'TOPDIR'/logdir2"},
+ {define,'CFGDIR',"'TOPDIR'/cfgs"},
+ {define,'CFGFILES',["cfgX","cfgY"]},
+ {define,'TESTDIR',"'TOPDIR'/test"},
+ {define,'TO1DIR',"'TESTDIR'/'TO1'"},
+ {define,'TO2DIR',"'TESTDIR'/'TO2'"},
+ {define,'EXSUITE',ex_SUITE},
+ {define,'EXGRS',[g1,g2]},
+
+ {logdir,'LOGDIR'},
+ {logdir,'NODE1@HOST','LOGDIR1'},
+ {logdir,'NODE2@HOST','LOGDIR2'},
+
+ {config,["a.cfg","b.cfg"]},
+ {config,'NODES',"./'CFGDIR'/c.cfg"},
+ {config,'CFGDIR',["d.cfg","e.cfg"]},
+ {config,'NODE2@HOST','CFGDIR','CFGFILES'},
+
+ {suites,'NODE1@HOST','TO1DIR',all},
+ {suites,'NODES','TO2DIR',all},
+
+ {groups,'TO1DIR','EXSUITE','EXGRS'}
+ ],
+
+ {ok,SpecDir} = file:get_cwd(),
+
+ ListResult = ct_testspec:collect_tests_from_list(Spec, false),
+ ct:pal("TESTSPEC RECORD FROM LIST:~n~p~n", [rec2proplist(ListResult)]),
+ SpecFile = ct_test_support:write_testspec(Spec,SpecDir,
+ "define_names_1.spec"),
+ FileResult = ct_testspec:collect_tests_from_file([SpecFile], false),
+ ct:pal("TESTSPEC RECORD FROM FILE:~n~p~n", [rec2proplist(FileResult)]),
+
+ N = node(),
+ N1 = testnode1@eniac,
+ N2 = testnode2@eniac,
+ Join = fun(Dir) -> shorten_path(filename:join(SpecDir,Dir),SpecDir) end,
+
+ Verify = #testspec{spec_dir = SpecDir,
+ nodes = [{undefined,N2},
+ {undefined,N1},
+ {undefined,N}],
+ config = [{N2,Join("a.cfg")},{N2,Join("b.cfg")},
+ {N1,Join("a.cfg")},{N1,Join("b.cfg")},
+ {N,Join("a.cfg")},{N,Join("b.cfg")},
+ {N1,Join("../cfgs/c.cfg")},{N2,Join("../cfgs/c.cfg")},
+ {N2,Join("../cfgs/d.cfg")},{N2,Join("../cfgs/e.cfg")},
+ {N1,Join("../cfgs/d.cfg")},{N1,Join("../cfgs/e.cfg")},
+ {N,Join("../cfgs/d.cfg")},{N,Join("../cfgs/e.cfg")},
+ {N2,Join("../cfgs/cfgX")},{N2,Join("../cfgs/cfgY")}],
+ logdir = [{N,Join("../logdir")},
+ {N1,Join("../logdir1")},
+ {N2,Join("../logdir2")},
+ "."],
+ tests = [{{N1,Join("../test/to1")},[{all,[all]}]},
+ {{N1,Join("../test/to2")},[{all,[all]}]},
+ {{N2,Join("../test/to2")},[{all,[all]}]},
+ {{N2,Join("../test/to1")},
+ [{ex_SUITE,[{g1,all},{g2,all}]}]},
+ {{N,Join("../test/to1")},
+ [{ex_SUITE,[{g1,all},{g2,all}]}]}]
+ },
+ verify_result(Verify,ListResult,FileResult).
+
+
+%%%-----------------------------------------------------------------
+%%% HELP FUNCTIONS
+%%%-----------------------------------------------------------------
+
+verify_result(Verify,ListResult,FileResult) ->
+ {_,TSLTuples} = rec2proplist(ListResult),
+ {_,TSFTuples} = rec2proplist(FileResult),
+ {_,VTuples} = rec2proplist(Verify),
+ VResult =
+ (catch lists:foldl(fun({Tag,Val},{[{Tag,Val}|TSL],[{Tag,Val}|TSF]}) ->
+ {TSL,TSF};
+ ({Tag,Val},{[{_Tag,TSLVal}|_TSL],[{Tag,Val}|_TSF]}) ->
+ exit({ts_list_mismatch,Tag,Val,TSLVal});
+ ({Tag,Val},{[{Tag,Val}|_TSL],[{_Tag,TSFVal}|_TSF]}) ->
+ exit({ts_file_mismatch,Tag,Val,TSFVal});
+ ({Tag,Val},{_,[{_Tag,TSFVal}|_TSF]}) ->
+ exit({ts_mismatch,Tag,Val,TSFVal})
+ end, {TSLTuples,TSFTuples}, VTuples)),
+ case VResult of
+ {'EXIT',Reason} ->
+ ct:fail(Reason);
+ _ ->
+ ok
+ end,
+ ok.
+
+
+check_parameter(S="cfg_str1") ->
+ {ok,{config,S}};
+check_parameter(S="cfg_str2") ->
+ {ok,{config,S}}.
+read_config(S) ->
+ {ok,[{cfg,S}]}.
+
+rec2proplist(E={error,_What}) ->
+ exit({invalid_testspec_record,E});
+rec2proplist(Rec) ->
+ [RecName|RecList] = tuple_to_list(Rec),
+ FieldNames =
+ if RecName == testspec ->
+ record_info(fields, testspec);
+ true ->
+ undefined
+ end,
+ {RecName,combine_names_and_vals(FieldNames,RecList)}.
+
+combine_names_and_vals([FN|FNs], [V|Vs]) ->
+ [{FN,V} | combine_names_and_vals(FNs, Vs)];
+combine_names_and_vals([], []) ->
+ [];
+combine_names_and_vals(_, _) ->
+ [].
+
+get_absdir(Dir) ->
+ shorten_path(filename:absname(Dir),Dir).
+
+shorten_path(Path,SpecDir) ->
+ case shorten_split_path(filename:split(Path),[]) of
+ [] ->
+ [Root|_] = filename:split(SpecDir),
+ Root;
+ Short ->
+ filename:join(Short)
+ end.
+
+shorten_split_path([".."|Path],SoFar) ->
+ shorten_split_path(Path,tl(SoFar));
+shorten_split_path(["."|Path],SoFar) ->
+ shorten_split_path(Path,SoFar);
+shorten_split_path([Dir|Path],SoFar) ->
+ shorten_split_path(Path,[Dir|SoFar]);
+shorten_split_path([],SoFar) ->
+ lists:reverse(SoFar).
diff --git a/lib/common_test/test/ct_verbosity_SUITE.erl b/lib/common_test/test/ct_verbosity_SUITE.erl
new file mode 100644
index 0000000000..349319de94
--- /dev/null
+++ b/lib/common_test/test/ct_verbosity_SUITE.erl
@@ -0,0 +1,244 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2009-2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, 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_verbosity_SUITE
+%%%
+%%% Description:
+%%% Test that verbosity levels vs the importance parameter works as
+%%% expected.
+%%%
+%%%-------------------------------------------------------------------
+-module(ct_verbosity_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() ->
+ [
+ no_levels,
+ general_level_low,
+ general_level_std,
+ general_level_hi,
+ change_default,
+ combine_categories,
+ testspec_only,
+ merge_with_testspec
+ ].
+
+%%--------------------------------------------------------------------
+%% TEST CASES
+%%--------------------------------------------------------------------
+
+%%%-----------------------------------------------------------------
+%%%
+no_levels(Config) ->
+ TC = no_levels,
+ DataDir = ?config(data_dir, Config),
+ Suite = filename:join(DataDir, "io_test_SUITE"),
+ {Opts,ERPid} = setup([{suite,Suite},{label,TC}], Config),
+ ok = execute(TC, Opts, ERPid, Config).
+
+%%%-----------------------------------------------------------------
+%%%
+general_level_low(Config) ->
+ TC = general_level_low,
+ DataDir = ?config(data_dir, Config),
+ Suite = filename:join(DataDir, "io_test_SUITE"),
+ {Opts,ERPid} = setup([{suite,Suite},{label,TC},
+ {verbosity,0}], Config),
+ ok = execute(TC, Opts, ERPid, Config).
+
+%%%-----------------------------------------------------------------
+%%%
+general_level_std(Config) ->
+ TC = general_level_std,
+ DataDir = ?config(data_dir, Config),
+ Suite = filename:join(DataDir, "io_test_SUITE"),
+ {Opts,ERPid} = setup([{suite,Suite},{label,TC},
+ {verbosity,50}], Config),
+ ok = execute(TC, Opts, ERPid, Config).
+
+%%%-----------------------------------------------------------------
+%%%
+general_level_hi(Config) ->
+ TC = general_level_high,
+ DataDir = ?config(data_dir, Config),
+ Suite = filename:join(DataDir, "io_test_SUITE"),
+ {Opts,ERPid} = setup([{suite,Suite},{label,TC},
+ {verbosity,100}], Config),
+ ok = execute(TC, Opts, ERPid, Config).
+
+%%%-----------------------------------------------------------------
+%%%
+change_default(Config) ->
+ TC = change_default,
+ DataDir = ?config(data_dir, Config),
+ Suite = filename:join(DataDir, "io_test_SUITE"),
+ {Opts,ERPid} = setup([{suite,Suite},{label,TC},
+ {verbosity,[{default,49}]}], Config),
+ ok = execute(TC, Opts, ERPid, Config).
+
+%%%-----------------------------------------------------------------
+%%%
+combine_categories(Config) ->
+ TC = combine_categories,
+ DataDir = ?config(data_dir, Config),
+ Suite = filename:join(DataDir, "io_test_SUITE"),
+ {Opts,ERPid} = setup([{suite,Suite},{label,TC},
+ {verbosity,[{error,?HI_VERBOSITY},
+ {default,?LOW_VERBOSITY}]}], Config),
+ ok = execute(TC, Opts, ERPid, Config).
+
+%%%-----------------------------------------------------------------
+%%%
+testspec_only(Config) ->
+ TC = testspec_only,
+ DataDir = ?config(data_dir, Config),
+ PrivDir = ?config(priv_dir, Config),
+
+ TestSpec = [{verbosity,[{default,1},{error,75},100]},
+ {suites,DataDir,[io_test_SUITE]},
+ {label,TC}],
+
+ TestSpecName = ct_test_support:write_testspec(TestSpec, PrivDir,
+ "verbosity_1_spec"),
+ {Opts,ERPid} = setup([{spec,TestSpecName}], Config),
+
+ ok = execute(TC, Opts, ERPid, Config).
+
+%%%-----------------------------------------------------------------
+%%%
+merge_with_testspec(Config) ->
+ TC = merge_with_testspec,
+ DataDir = ?config(data_dir, Config),
+ PrivDir = ?config(priv_dir, Config),
+
+ TestSpec = [{verbosity,[{default,100},{error,100}]},
+ {suites,DataDir,[io_test_SUITE]},
+ {label,TC}],
+
+ TestSpecName = ct_test_support:write_testspec(TestSpec, PrivDir,
+ "verbosity_2_spec"),
+
+ %% below should override verbosity categories in testspec
+ {Opts,ERPid} = setup([{spec,TestSpecName},
+ {verbosity,[{default,0},0]}],
+ Config),
+
+ ok = execute(TC, Opts, ERPid, Config).
+
+%%%-----------------------------------------------------------------
+%%% 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}.
+
+execute(Name, Opts, ERPid, Config) ->
+ ok = ct_test_support:run(Opts, Config),
+ Events = ct_test_support:get_events(ERPid, Config),
+
+ ct_test_support:log_events(Name,
+ reformat(Events, ?eh),
+ ?config(priv_dir, Config),
+ Opts),
+
+ TestEvents = events_to_check(Name),
+ ct_test_support:verify_events(TestEvents, Events, Config).
+
+reformat(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) ->
+ test_events(Test) ++ events_to_check(Test, N-1).
+
+
+test_events(_) ->
+ [
+ {?eh,tc_done,{io_test_SUITE,tc1,ok}},
+ {?eh,tc_done,{io_test_SUITE,tc2,ok}},
+ {?eh,tc_done,{io_test_SUITE,tc3,ok}},
+
+ {parallel,
+ [
+ {?eh,tc_start,{io_test_SUITE,tc1}},
+ {?eh,tc_start,{io_test_SUITE,tc2}},
+ {?eh,tc_start,{io_test_SUITE,tc3}},
+ {?eh,tc_done,{io_test_SUITE,tc1,ok}},
+ {?eh,tc_done,{io_test_SUITE,tc2,ok}},
+ {?eh,tc_done,{io_test_SUITE,tc3,ok}},
+ {parallel,
+ [
+ {?eh,tc_start,{io_test_SUITE,tc1}},
+ {?eh,tc_start,{io_test_SUITE,tc2}},
+ {?eh,tc_start,{io_test_SUITE,tc3}},
+ {?eh,tc_done,{io_test_SUITE,tc1,ok}},
+ {?eh,tc_done,{io_test_SUITE,tc2,ok}},
+ {?eh,tc_done,{io_test_SUITE,tc3,ok}},
+ {?eh,test_stats,{9,0,{0,0}}}
+ ]}
+ ]},
+
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}
+ ].
+
diff --git a/lib/common_test/test/ct_verbosity_SUITE_data/io_test_SUITE.erl b/lib/common_test/test/ct_verbosity_SUITE_data/io_test_SUITE.erl
new file mode 100644
index 0000000000..946e1c1989
--- /dev/null
+++ b/lib/common_test/test/ct_verbosity_SUITE_data/io_test_SUITE.erl
@@ -0,0 +1,156 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2009-2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, 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_test_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+%%--------------------------------------------------------------------
+%% @spec suite() -> Info
+%% Info = [tuple()]
+%% @end
+%%--------------------------------------------------------------------
+suite() ->
+ [{timetrap,{seconds,10}}].
+
+%%--------------------------------------------------------------------
+%% @spec init_per_suite(Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%% @end
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% @spec end_per_suite(Config0) -> void() | {save_config,Config1}
+%% Config0 = Config1 = [tuple()]
+%% @end
+%%--------------------------------------------------------------------
+end_per_suite(_Config) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% @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(_GroupName, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% @spec end_per_group(GroupName, Config0) ->
+%% void() | {save_config,Config1}
+%% GroupName = atom()
+%% Config0 = Config1 = [tuple()]
+%% @end
+%%--------------------------------------------------------------------
+end_per_group(_GroupName, _Config) ->
+ 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() ->
+ [{g1, [parallel], [tc1,tc2,tc3,{group,g2}]},
+ {g2, [parallel], [tc1,tc2,tc3]}].
+
+%%--------------------------------------------------------------------
+%% @spec all() -> GroupsAndTestCases | {skip,Reason}
+%% GroupsAndTestCases = [{group,GroupName} | TestCase]
+%% GroupName = atom()
+%% TestCase = atom()
+%% Reason = term()
+%% @end
+%%--------------------------------------------------------------------
+all() ->
+ [tc1,tc2,tc3,{group,g1}].
+
+tc1(_C) ->
+ io:format("This is an io:format(~p)~n", [[]]),
+ ct:log("ct:log(default)", []),
+ ct:log(?STD_IMPORTANCE, "ct:log(default,~p)", [?STD_IMPORTANCE]),
+ ct:log(error, "ct:log(error)", []),
+ ct:log(error, ?STD_IMPORTANCE, "ct:log(error,~p)", [?STD_IMPORTANCE]),
+ ct:log(1, "ct:log(default,~p)", [1]),
+ ct:log(error, 1, "ct:log(error,~p)", [1]),
+ ct:log(99, "ct:log(default,~p)", [99]),
+ ct:log(error, 99, "ct:log(error,~p)", [99]),
+ ok.
+
+tc2(_C) ->
+ io:format("This is an io:format(~p)~n", [[]]),
+ ct:pal("ct:pal(default)", []),
+ ct:pal(?STD_IMPORTANCE, "ct:pal(default,~p)", [?STD_IMPORTANCE]),
+ ct:pal(error, "ct:pal(error)", []),
+ ct:pal(error, ?STD_IMPORTANCE, "ct:pal(error,~p)", [?STD_IMPORTANCE]),
+ ct:pal(1, "ct:pal(default,~p)", [1]),
+ ct:pal(error, 1, "ct:pal(error,~p)", [1]),
+ ct:pal(99, "ct:pal(default,~p)", [99]),
+ ct:pal(error, 99, "ct:pal(error,~p)", [99]),
+ ok.
+
+tc3(_C) ->
+ io:format("This is an io:format(~p)~n", [[]]),
+ ct:print("ct:print(default)", []),
+ ct:print(?STD_IMPORTANCE, "ct:print(default,~p)", [?STD_IMPORTANCE]),
+ ct:print(error, "ct:print(error)", []),
+ ct:print(error, ?STD_IMPORTANCE, "ct:print(error,~p)", [?STD_IMPORTANCE]),
+ ct:print(1, "ct:print(default,~p)", [1]),
+ ct:print(error, 1, "ct:print(error,~p)", [1]),
+ ct:print(99, "ct:print(default,~p)", [99]),
+ ct:print(error, 99, "ct:print(error,~p)", [99]),
+ ok.
diff --git a/lib/common_test/vsn.mk b/lib/common_test/vsn.mk
index b94f7f7593..c92fb2ca37 100644
--- a/lib/common_test/vsn.mk
+++ b/lib/common_test/vsn.mk
@@ -1 +1 @@
-COMMON_TEST_VSN = 1.6.1
+COMMON_TEST_VSN = 1.7
diff --git a/lib/compiler/doc/src/Makefile b/lib/compiler/doc/src/Makefile
index 308e9c4d02..13a73a5d31 100644
--- a/lib/compiler/doc/src/Makefile
+++ b/lib/compiler/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2009. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/compiler/doc/src/compile.xml b/lib/compiler/doc/src/compile.xml
index b577e5ca4f..ddaae2655d 100644
--- a/lib/compiler/doc/src/compile.xml
+++ b/lib/compiler/doc/src/compile.xml
@@ -294,6 +294,12 @@ module.beam: module.erl \
describing what it is doing.</p>
</item>
+ <tag><c>{source,FileName}</c></tag>
+ <item>
+ <p>Sets the value of the source, as returned by
+ <c>module_info(compile)</c>.</p>
+ </item>
+
<tag><c>{outdir,Dir}</c></tag>
<item>
<p>Sets a new directory for the object code. The current
@@ -310,7 +316,7 @@ module.beam: module.erl \
<item>
<p>Add <c>Dir</c> to the list of directories to be searched
when including a file. When encountering an
- <c>-include</c> or <c>-include_dir</c> directive,
+ <c>-include</c> or <c>-include_lib</c> directive,
the compiler searches for header files in the following
directories:</p>
<list type="ordered">
@@ -810,6 +816,32 @@ pi() -> 3.1416.
</section>
<section>
+ <title>Inlining of list functions</title>
+ <p>The compiler can also inline a variety of list manipulation functions
+ from the stdlib's lists module.</p>
+
+ <p>This feature must be explicitly enabled with a compiler option or a
+ <c>-compile()</c> attribute in the source module.</p>
+
+ <p>To enable inlining of list functions, use the <c>inline_list_funcs</c>
+ option.</p>
+
+ <p>The following functions are inlined:</p>
+ <list type="bulleted">
+ <item><seealso marker="stdlib:lists#all/2">lists:all/2</seealso></item>
+ <item><seealso marker="stdlib:lists#any/2">lists:any/2</seealso></item>
+ <item><seealso marker="stdlib:lists#foreach/2">lists:foreach/2</seealso></item>
+ <item><seealso marker="stdlib:lists#map/2">lists:map/2</seealso></item>
+ <item><seealso marker="stdlib:lists#flatmap/2">lists:flatmap/2</seealso></item>
+ <item><seealso marker="stdlib:lists#filter/2">lists:filter/2</seealso></item>
+ <item><seealso marker="stdlib:lists#foldl/3">lists:foldl/3</seealso></item>
+ <item><seealso marker="stdlib:lists#foldr/3">lists:foldr/3</seealso></item>
+ <item><seealso marker="stdlib:lists#mapfoldl/3">lists:mapfoldl/3</seealso></item>
+ <item><seealso marker="stdlib:lists#mapfoldr/3">lists:mapfoldr/3</seealso></item>
+ </list>
+ </section>
+
+ <section>
<title>Parse Transformations</title>
<p>Parse transformations are used when a programmer wants to use
diff --git a/lib/compiler/doc/src/notes.xml b/lib/compiler/doc/src/notes.xml
index e2a921a6f2..6d51074d4a 100644
--- a/lib/compiler/doc/src/notes.xml
+++ b/lib/compiler/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2004</year><year>2011</year>
+ <year>2004</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -31,6 +31,82 @@
<p>This document describes the changes made to the Compiler
application.</p>
+<section><title>Compiler 4.8.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Modules with very many functions would compile very
+ slowly.</p>
+ <p>
+ Own Id: OTP-10123</p>
+ </item>
+ <item>
+ <p>
+ <c>compile:forms/2</c> will now use a
+ {source,SourceFilePath} to set the source returned by
+ <c>module_info(compile)</c> (Thanks to Jos� Valim)</p>
+ <p>
+ Own Id: OTP-10150</p>
+ </item>
+ <item>
+ <p>
+ A process which had enabled trap_exit would receive EXIT
+ messages after calling the compiler. (Thanks to Jeremy
+ Heater.)</p>
+ <p>
+ Own Id: OTP-10171</p>
+ </item>
+ <item>
+ <p>
+ Fix messages ordering with column numbers</p>
+ <p>
+ Own Id: OTP-10183</p>
+ </item>
+ <item>
+ <p>
+ sys_pre_expand: Fix BASE never being set</p>
+ <p>
+ Commit a612e99fb5aaa934fe5a8591db0f083d7fa0b20a turned
+ module attributes from 2-tuples to 3-tuples but forgot to
+ update get_base/1, breaking BASE for parametric modules.</p>
+ <p>
+ Own Id: OTP-10184</p>
+ </item>
+ <item>
+ <p>
+ The compiler will now issue a warning if literal tuple
+ funs are used. For example, {erlang,is_tuple}(X) will now
+ generate a warning.</p>
+ <p>
+ Own Id: OTP-10185</p>
+ </item>
+ <item>
+ <p>
+ The compiler will now warn for illegal sizes for segments
+ in binary construction. For example,
+ &lt;&lt;X:(2.5)&gt;&gt; will now cause the compiler to
+ issue a warning.</p>
+ <p>
+ Own Id: OTP-10197</p>
+ </item>
+ <item>
+ <p>
+ Fix the erlc -MP flag</p>
+ <p>
+ Because of a copy-and-paste error in erlc.c, the -MP flag
+ had the same effect as -MG. As a workaround, you had to
+ pass +makedep_phony to enable the MP option. This patch
+ makes -MP work as intended.</p>
+ <p>
+ Own Id: OTP-10211</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Compiler 4.8.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/compiler/src/Makefile b/lib/compiler/src/Makefile
index 56c45d369c..8d54dffd73 100644
--- a/lib/compiler/src/Makefile
+++ b/lib/compiler/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2011. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -45,6 +45,7 @@ RELSYSDIR = $(RELEASE_PATH)/lib/compiler-$(VSN)
# Target Specs
# ----------------------------------------------------
MODULES = \
+ beam_a \
beam_asm \
beam_block \
beam_bool \
@@ -65,6 +66,7 @@ MODULES = \
beam_type \
beam_utils \
beam_validator \
+ beam_z \
cerl \
cerl_clauses \
cerl_inline \
@@ -80,7 +82,6 @@ MODULES = \
sys_core_dsetel \
sys_core_fold \
sys_core_inline \
- sys_expand_pmod \
sys_pre_attributes \
sys_pre_expand \
v3_codegen \
@@ -143,19 +144,19 @@ clean:
# ----------------------------------------------------
$(APP_TARGET): $(APP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
$(EGEN)/beam_opcodes.erl $(EGEN)/beam_opcodes.hrl: genop.tab
- $(PERL) $(ERL_TOP)/erts/emulator/utils/beam_makeops -compiler -outdir $(EGEN) $<
+ $(gen_verbose)$(PERL) $(ERL_TOP)/erts/emulator/utils/beam_makeops -compiler -outdir $(EGEN) $<
$(EBIN)/beam_asm.beam: $(ESRC)/beam_asm.erl $(EGEN)/beam_opcodes.hrl
- $(ERLC) $(ERL_COMPILE_FLAGS) -DCOMPILER_VSN='"$(VSN)"' -o$(EBIN) $<
+ $(V_ERLC) $(ERL_COMPILE_FLAGS) -DCOMPILER_VSN='"$(VSN)"' -o$(EBIN) $<
$(EBIN)/cerl_inline.beam: $(ESRC)/cerl_inline.erl
- $(ERLC) $(ERL_COMPILE_FLAGS) +nowarn_shadow_vars -o$(EBIN) $<
+ $(V_ERLC) $(ERL_COMPILE_FLAGS) +nowarn_shadow_vars -o$(EBIN) $<
# ----------------------------------------------------
# Release Target
diff --git a/lib/compiler/src/beam_a.erl b/lib/compiler/src/beam_a.erl
new file mode 100644
index 0000000000..1c51226314
--- /dev/null
+++ b/lib/compiler/src/beam_a.erl
@@ -0,0 +1,97 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, 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: Run directly after code generation to do any normalization
+%% or preparation to simplify the optimization passes.
+%% (Mandatory.)
+
+-module(beam_a).
+
+-export([module/2]).
+
+module({Mod,Exp,Attr,Fs0,Lc}, _Opt) ->
+ Fs = [function(F) || F <- Fs0],
+ {ok,{Mod,Exp,Attr,Fs,Lc}}.
+
+function({function,Name,Arity,CLabel,Is0}) ->
+ try
+ %% Rename certain operations to simplify the optimization passes.
+ Is1 = rename_instrs(Is0),
+
+ %% Remove unusued labels for cleanliness and to help
+ %% optimization passes and HiPE.
+ Is = beam_jump:remove_unused_labels(Is1),
+ {function,Name,Arity,CLabel,Is}
+ catch
+ Class:Error ->
+ Stack = erlang:get_stacktrace(),
+ io:fwrite("Function: ~w/~w\n", [Name,Arity]),
+ erlang:raise(Class, Error, Stack)
+ end.
+
+rename_instrs([{apply_last,A,N}|Is]) ->
+ [{apply,A},{deallocate,N},return|rename_instrs(Is)];
+rename_instrs([{call_last,A,F,N}|Is]) ->
+ [{call,A,F},{deallocate,N},return|rename_instrs(Is)];
+rename_instrs([{call_ext_last,A,F,N}|Is]) ->
+ [{call_ext,A,F},{deallocate,N},return|rename_instrs(Is)];
+rename_instrs([{call_only,A,F}|Is]) ->
+ [{call,A,F},return|rename_instrs(Is)];
+rename_instrs([{call_ext_only,A,F}|Is]) ->
+ [{call_ext,A,F},return|rename_instrs(Is)];
+rename_instrs([I|Is]) ->
+ [rename_instr(I)|rename_instrs(Is)];
+rename_instrs([]) -> [].
+
+rename_instr({bs_put_binary=I,F,Sz,U,Fl,Src}) ->
+ {bs_put,F,{I,U,Fl},[Sz,Src]};
+rename_instr({bs_put_float=I,F,Sz,U,Fl,Src}) ->
+ {bs_put,F,{I,U,Fl},[Sz,Src]};
+rename_instr({bs_put_integer=I,F,Sz,U,Fl,Src}) ->
+ {bs_put,F,{I,U,Fl},[Sz,Src]};
+rename_instr({bs_put_utf8=I,F,Fl,Src}) ->
+ {bs_put,F,{I,Fl},[Src]};
+rename_instr({bs_put_utf16=I,F,Fl,Src}) ->
+ {bs_put,F,{I,Fl},[Src]};
+rename_instr({bs_put_utf32=I,F,Fl,Src}) ->
+ {bs_put,F,{I,Fl},[Src]};
+%% rename_instr({bs_put_string,_,_}=I) ->
+%% {bs_put,{f,0},I,[]};
+rename_instr({bs_add=I,F,[Src1,Src2,U],Dst}) when is_integer(U) ->
+ {bif,I,F,[Src1,Src2,{integer,U}],Dst};
+rename_instr({bs_utf8_size=I,F,Src,Dst}) ->
+ {bif,I,F,[Src],Dst};
+rename_instr({bs_utf16_size=I,F,Src,Dst}) ->
+ {bif,I,F,[Src],Dst};
+rename_instr({bs_init2=I,F,Sz,Extra,Live,Flags,Dst}) ->
+ {bs_init,F,{I,Extra,Flags},Live,[Sz],Dst};
+rename_instr({bs_init_bits=I,F,Sz,Extra,Live,Flags,Dst}) ->
+ {bs_init,F,{I,Extra,Flags},Live,[Sz],Dst};
+rename_instr({bs_append=I,F,Sz,Extra,Live,U,Src,Flags,Dst}) ->
+ {bs_init,F,{I,Extra,U,Flags},Live,[Sz,Src],Dst};
+rename_instr({bs_private_append=I,F,Sz,U,Src,Flags,Dst}) ->
+ {bs_init,F,{I,U,Flags},none,[Sz,Src],Dst};
+rename_instr(bs_init_writable=I) ->
+ {bs_init,{f,0},I,1,[{x,0}],{x,0}};
+rename_instr({select_val=I,Reg,Fail,{list,List}}) ->
+ {select,I,Reg,Fail,List};
+rename_instr({select_tuple_arity=I,Reg,Fail,{list,List}}) ->
+ {select,I,Reg,Fail,List};
+rename_instr(send) ->
+ {call_ext,2,send};
+rename_instr(I) -> I.
diff --git a/lib/compiler/src/beam_block.erl b/lib/compiler/src/beam_block.erl
index cd568097fa..3e0050382c 100644
--- a/lib/compiler/src/beam_block.erl
+++ b/lib/compiler/src/beam_block.erl
@@ -31,19 +31,16 @@ module({Mod,Exp,Attr,Fs0,Lc0}, _Opt) ->
function({function,Name,Arity,CLabel,Is0}, Lc0) ->
try
- %% Extra labels may thwart optimizations.
- Is1 = beam_jump:remove_unused_labels(Is0),
-
%% Collect basic blocks and optimize them.
- Is2 = blockify(Is1),
- Is3 = embed_lines(Is2),
- Is4 = move_allocates(Is3),
- Is5 = beam_utils:live_opt(Is4),
- Is6 = opt_blocks(Is5),
- Is7 = beam_utils:delete_live_annos(Is6),
+ Is1 = blockify(Is0),
+ Is2 = embed_lines(Is1),
+ 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(Is7, Lc0),
+ {Is,Lc} = bsm_opt(Is6, Lc0),
%% Done.
{{function,Name,Arity,CLabel,Is},Lc}
@@ -74,9 +71,9 @@ blockify([{bs_save2,R,Point}=I,{test,is_eq_exact,_,_}=Test,
%% Do other peep-hole optimizations.
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],
+ [{select,select_val,Reg,{f,Fail},
+ [{atom,false},{f,_}=BrFalse,
+ {atom,true}=AtomTrue,{f,_}=BrTrue]}|Is]=Is0],
[{block,Bl}|_]=Acc) ->
case is_last_bool(Bl, Reg) of
false ->
@@ -89,9 +86,9 @@ blockify([{test,is_atom,{f,Fail},[Reg]}=I|
{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],
+ [{select,select_val,Reg,{f,Fail},
+ [{atom,true}=AtomTrue,{f,_}=BrTrue,
+ {atom,false},{f,_}=BrFalse]}|Is]=Is0],
[{block,Bl}|_]=Acc) ->
case is_last_bool(Bl, Reg) of
false ->
@@ -423,8 +420,8 @@ 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({bs_put,_,{bs_put_integer,_,_},_}) -> true;
+is_bs_put({bs_put,_,{bs_put_float,_,_},_}) -> true;
is_bs_put(_) -> false.
collect_bs_puts(Is) ->
@@ -439,20 +436,24 @@ collect_bs_puts_1([I|Is]=Is0, Acc) ->
opt_bs_puts(Is) ->
opt_bs_1(Is, []).
-opt_bs_1([{bs_put_float,Fail,{integer,Sz},1,Flags0,Src}=I0|Is], Acc) ->
+opt_bs_1([{bs_put,Fail,
+ {bs_put_float,1,Flags0},[{integer,Sz},Src]}=I0|Is], Acc) ->
try eval_put_float(Src, Sz, Flags0) of
<<Int:Sz>> ->
Flags = force_big(Flags0),
- I = {bs_put_integer,Fail,{integer,Sz},1,Flags,{integer,Int}},
+ I = {bs_put,Fail,{bs_put_integer,1,Flags},
+ [{integer,Sz},{integer,Int}]},
opt_bs_1([I|Is], Acc)
catch
error:_ ->
opt_bs_1(Is, [I0|Acc])
end;
-opt_bs_1([{bs_put_integer,_,{integer,8},1,_,{integer,_}}|_]=IsAll, Acc0) ->
+opt_bs_1([{bs_put,_,{bs_put_integer,1,_},[{integer,8},{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 ->
+opt_bs_1([{bs_put,Fail,{bs_put_integer,1,F},[{integer,Sz},{integer,N}]}=I|Is0],
+ Acc) when Sz > 8 ->
case field_endian(F) of
big ->
%% We can do this optimization for any field size without risk
@@ -466,14 +467,14 @@ opt_bs_1([{bs_put_integer,Fail,{integer,Sz},1,F,{integer,N}}=I|Is0], Acc) when S
%% an explosion in code size.
<<Int:Sz>> = <<N:Sz/little>>,
Flags = force_big(F),
- Is = [{bs_put_integer,Fail,{integer,Sz},1,
- Flags,{integer,Int}}|Is0],
+ Is = [{bs_put,Fail,{bs_put_integer,1,Flags},
+ [{integer,Sz},{integer,Int}]}|Is0],
opt_bs_1(Is, Acc);
_ -> %native or too wide little field
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([{bs_put,Fail,{Op,U,F},[{integer,Sz},Src]}|Is], Acc) when U > 1 ->
+ opt_bs_1([{bs_put,Fail,{Op,1,F},[{integer,U*Sz},Src]}|Is], Acc);
opt_bs_1([I|Is], Acc) ->
opt_bs_1(Is, [I|Acc]);
opt_bs_1([], Acc) -> reverse(Acc).
@@ -489,17 +490,17 @@ eval_put_float(Src, Sz, Flags) when Sz =< 256 -> %Only evaluate if Sz is reasona
value({integer,I}) -> I;
value({float,F}) -> F.
-bs_collect_string(Is, [{bs_put_string,Len,{string,Str}}|Acc]) ->
+bs_collect_string(Is, [{bs_put,_,{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],
+bs_coll_str_1([{bs_put,_,{bs_put_integer,U,_},[{integer,Sz},{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]}.
+ {Is,[{bs_put,{f,0},{bs_put_string,Len,{string,reverse(StrAcc)}},[]}|IsAcc]}.
field_endian({field_flags,F}) -> field_endian_1(F).
@@ -531,15 +532,17 @@ bs_split_int(N, Sz, Fail, Acc) ->
bs_split_int_1(N, FirstByteSz, Sz, Fail, Acc).
bs_split_int_1(-1, _, Sz, Fail, Acc) when Sz > 64 ->
- I = {bs_put_integer,Fail,{integer,Sz},1,{field_flags,[big]},{integer,-1}},
+ I = {bs_put,Fail,{bs_put_integer,1,{field_flags,[big]}},
+ [{integer,Sz},{integer,-1}]},
[I|Acc];
bs_split_int_1(0, _, Sz, Fail, Acc) when Sz > 64 ->
- I = {bs_put_integer,Fail,{integer,Sz},1,{field_flags,[big]},{integer,0}},
+ I = {bs_put,Fail,{bs_put_integer,1,{field_flags,[big]}},
+ [{integer,Sz},{integer,0}]},
[I|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}},
+ I = {bs_put,Fail,{bs_put_integer,1,{field_flags,[big]}},
+ [{integer,ByteSz},{integer,N band Mask}]},
bs_split_int_1(N bsr ByteSz, 8, Sz-ByteSz, Fail, [I|Acc]);
bs_split_int_1(_, _, _, _, Acc) -> Acc.
@@ -577,9 +580,9 @@ bsm_reroute([{bs_restore2,Reg,Save}=I|Is], D, _, Acc) ->
bsm_reroute(Is, D, {Reg,Save}, [I|Acc]);
bsm_reroute([{label,_}=I|Is], D, S, Acc) ->
bsm_reroute(Is, D, S, [I|Acc]);
-bsm_reroute([{select_val,Reg,F0,{list,Lbls0}}|Is], D, {_,Save}=S, Acc0) ->
+bsm_reroute([{select,select_val,Reg,F0,Lbls0}|Is], D, {_,Save}=S, Acc0) ->
[F|Lbls] = bsm_subst_labels([F0|Lbls0], Save, D),
- Acc = [{select_val,Reg,F,{list,Lbls}}|Acc0],
+ Acc = [{select,select_val,Reg,F,Lbls}|Acc0],
bsm_reroute(Is, D, S, Acc);
bsm_reroute([{test,TestOp,F0,TestArgs}=I|Is], D, {_,Save}=S, Acc0) ->
F = bsm_subst_label(F0, Save, D),
@@ -615,10 +618,6 @@ bsm_opt_2([{test,bs_skip_bits2,F,[Ctx,{integer,I1},Unit1,_]}|Is],
[{test,bs_skip_bits2,F,[Ctx,{integer,I2},Unit2,Flags]}|Acc]) ->
bsm_opt_2(Is, [{test,bs_skip_bits2,F,
[Ctx,{integer,I1*Unit1+I2*Unit2},1,Flags]}|Acc]);
-bsm_opt_2([{test,bs_match_string,F,[Ctx,Bin1]},
- {test,bs_match_string,F,[Ctx,Bin2]}|Is], Acc) ->
- I = {test,bs_match_string,F,[Ctx,<<Bin1/bitstring,Bin2/bitstring>>]},
- bsm_opt_2([I|Is], Acc);
bsm_opt_2([I|Is], Acc) ->
bsm_opt_2(Is, [I|Acc]);
bsm_opt_2([], Acc) -> reverse(Acc).
diff --git a/lib/compiler/src/beam_bool.erl b/lib/compiler/src/beam_bool.erl
index d9ea6f5a70..81be262d6d 100644
--- a/lib/compiler/src/beam_bool.erl
+++ b/lib/compiler/src/beam_bool.erl
@@ -168,18 +168,18 @@ bopt_block(Reg, Fail, OldIs, [{block,Bl0}|Acc0], St0) ->
end.
%% ensure_opt_safe(OriginalCode, OptCode, FollowingCode, Fail,
-%% ReversedPreceedingCode, State) -> ok
+%% ReversedPrecedingCode, State) -> ok
%% 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.
%%
%% Throw an exception if the optimization is not safe.
%%
-ensure_opt_safe(Bl, NewCode, OldIs, Fail, PreceedingCode, St) ->
+ensure_opt_safe(Bl, NewCode, OldIs, Fail, PrecedingCode, St) ->
%% Here are the conditions that must be true for the
%% optimization to be safe.
%%
- %% 1. If a register is INITIALIZED by PreceedingCode,
+ %% 1. If a register is INITIALIZED by PrecedingCode,
%% then if that register assigned a value in the original
%% code, but not in the optimized code, it must be UNUSED or KILLED
%% in the code that follows.
@@ -190,29 +190,50 @@ ensure_opt_safe(Bl, NewCode, OldIs, Fail, PreceedingCode, St) ->
%% by the code that follows.
%%
%% 3. Any register that is assigned a value in the optimized
- %% code must be UNUSED or KILLED in the following code
- %% (because the register might be assigned the wrong value,
- %% and even if the value is right it might no longer be
- %% assigned on *all* paths leading to its use).
+ %% code must be UNUSED or KILLED in the following code,
+ %% unless we can be sure that it is always assigned the same
+ %% value.
- InitInPreceeding = initialized_regs(PreceedingCode),
+ InitInPreceding = initialized_regs(PrecedingCode),
PrevDst = dst_regs(Bl),
NewDst = dst_regs(NewCode),
NotSet = ordsets:subtract(PrevDst, NewDst),
- MustBeKilled = ordsets:subtract(NotSet, InitInPreceeding),
- MustBeUnused = ordsets:subtract(ordsets:union(NotSet, NewDst), MustBeKilled),
+ MustBeKilled = ordsets:subtract(NotSet, InitInPreceding),
case all_killed(MustBeKilled, OldIs, Fail, St) of
false -> throw(all_registers_not_killed);
true -> ok
end,
+ Same = assigned_same_value(Bl, NewCode),
+ MustBeUnused = ordsets:subtract(ordsets:union(NotSet, NewDst),
+ ordsets:union(MustBeKilled, Same)),
case none_used(MustBeUnused, OldIs, Fail, St) of
false -> throw(registers_used);
true -> ok
end,
ok.
+%% assigned_same_value(OldCode, NewCodeReversed) -> [DestinationRegs]
+%% Return an ordset with a list of all y registers that are always
+%% assigned the same value in the old and new code. Currently, we
+%% are very conservative in that we only consider identical move
+%% instructions in the same order.
+%%
+assigned_same_value(Old, New) ->
+ case reverse(New) of
+ [{block,Bl}|_] ->
+ assigned_same_value(Old, Bl, []);
+ _ ->
+ ordsets:new()
+ end.
+
+assigned_same_value([{set,[{y,_}=D],[S],move}|T1],
+ [{set,[{y,_}=D],[S],move}|T2], Acc) ->
+ assigned_same_value(T1, T2, [D|Acc]);
+assigned_same_value(_, _, Acc) ->
+ ordsets:from_list(Acc).
+
update_fail_label([{set,_,_,move}=I|Is], Fail, Acc) ->
update_fail_label(Is, Fail, [I|Acc]);
update_fail_label([{set,Ds,As,{bif,N,{f,_}}}|Is], Fail, Acc) ->
diff --git a/lib/compiler/src/beam_bsm.erl b/lib/compiler/src/beam_bsm.erl
index 1217f7f777..02794a8e18 100644
--- a/lib/compiler/src/beam_bsm.erl
+++ b/lib/compiler/src/beam_bsm.erl
@@ -204,16 +204,6 @@ btb_reaches_match_1(Is, Regs, D) ->
btb_reaches_match_2([{block,Bl}|Is], Regs0, D) ->
Regs = btb_reaches_match_block(Bl, Regs0),
btb_reaches_match_1(Is, Regs, D);
-btb_reaches_match_2([{call_only,Arity,{f,Lbl}}|_], Regs0, D) ->
- Regs = btb_kill_not_live(Arity, Regs0),
- btb_tail_call(Lbl, Regs, D);
-btb_reaches_match_2([{call_ext_only,Arity,Func}|_], Regs0, D) ->
- Regs = btb_kill_not_live(Arity, Regs0),
- btb_tail_call(Func, Regs, D);
-btb_reaches_match_2([{call_last,Arity,{f,Lbl},_}|_], Regs0, D) ->
- Regs1 = btb_kill_not_live(Arity, Regs0),
- Regs = btb_kill_yregs(Regs1),
- btb_tail_call(Lbl, Regs, D);
btb_reaches_match_2([{call,Arity,{f,Lbl}}|Is], Regs, D) ->
btb_call(Arity, Lbl, Regs, Is, D);
btb_reaches_match_2([{apply,Arity}|Is], Regs, D) ->
@@ -222,19 +212,16 @@ btb_reaches_match_2([{call_fun,Live}=I|Is], Regs, D) ->
btb_call(Live, I, Regs, Is, D);
btb_reaches_match_2([{make_fun2,_,_,_,Live}|Is], Regs, D) ->
btb_call(Live, make_fun2, Regs, Is, D);
-btb_reaches_match_2([{call_ext,Arity,{extfunc,Mod,Name,Arity}=Func}|Is], Regs0, D) ->
+btb_reaches_match_2([{call_ext,Arity,Func}=I|Is], Regs0, D) ->
%% Allow us scanning beyond the call in case the match
%% context is saved on the stack.
- case erl_bifs:is_exit_bif(Mod, Name, Arity) of
+ case beam_jump:is_exit_instruction(I) of
false ->
btb_call(Arity, Func, Regs0, Is, D);
true ->
Regs = btb_kill_not_live(Arity, Regs0),
btb_tail_call(Func, Regs, D)
end;
-btb_reaches_match_2([{call_ext_last,Arity,_,_}=I|_], Regs, D) ->
- btb_ensure_not_used(btb_regs_from_arity(Arity), I, Regs),
- D;
btb_reaches_match_2([{kill,Y}|Is], Regs, D) ->
btb_reaches_match_1(Is, btb_kill([Y], Regs), D);
btb_reaches_match_2([{deallocate,_}|Is], Regs0, D) ->
@@ -254,21 +241,45 @@ btb_reaches_match_2([{bif,_,{f,F},Ss,Dst}=I|Is], Regs0, D0) ->
Regs = btb_kill([Dst], Regs0),
D = btb_follow_branch(F, Regs, D0),
btb_reaches_match_1(Is, Regs, D);
-btb_reaches_match_2([{test,bs_start_match2,_,_,[Ctx,_],Ctx}|Is], Regs, D) ->
- case btb_context_regs(Regs) of
- [Ctx] ->
- D;
- CtxRegs ->
- case member(Ctx, CtxRegs) of
- false -> btb_reaches_match_2(Is, Regs, D);
- true -> btb_error(unsuitable_bs_start_match)
+btb_reaches_match_2([{test,bs_start_match2,{f,F},Live,[Ctx,_],Ctx}=I|Is],
+ Regs0, D0) ->
+ CtxRegs = btb_context_regs(Regs0),
+ case member(Ctx, CtxRegs) of
+ false ->
+ %% This bs_start_match2 instruction does not use "our"
+ %% match state. Therefore we can continue the search
+ %% for another bs_start_match2 instruction.
+ D = btb_follow_branch(F, Regs0, D0),
+ Regs = btb_kill_not_live(Live, Regs0),
+ btb_reaches_match_2(Is, Regs, D);
+ true ->
+ %% OK. This instruction will use "our" match state,
+ %% but we must make sure that all other copies of the
+ %% match state are killed in the code that follows
+ %% the instruction. (We know that the fail branch cannot
+ %% be taken in this case.)
+ OtherCtxRegs = CtxRegs -- [Ctx],
+ case btb_are_all_unused(OtherCtxRegs, Is, D0) of
+ false -> btb_error({OtherCtxRegs,not_all_unused_after,I});
+ true -> D0
end
end;
-btb_reaches_match_2([{test,bs_start_match2,_,_,[Bin,_],Ctx}|Is], Regs, D) ->
- CtxRegs = btb_context_regs(Regs),
+btb_reaches_match_2([{test,bs_start_match2,{f,F},Live,[Bin,_],Ctx}|Is],
+ Regs0, D0) ->
+ CtxRegs = btb_context_regs(Regs0),
case member(Bin, CtxRegs) orelse member(Ctx, CtxRegs) of
- false -> btb_reaches_match_2(Is, Regs, D);
- true -> btb_error(unsuitable_bs_start_match)
+ false ->
+ %% This bs_start_match2 does not reference any copy of the
+ %% match state. Therefore it can safely be passed on the
+ %% way to another (perhaps more suitable) bs_start_match2
+ %% instruction.
+ D = btb_follow_branch(F, Regs0, D0),
+ Regs = btb_kill_not_live(Live, Regs0),
+ btb_reaches_match_2(Is, Regs, D);
+ true ->
+ %% This variant of the bs_start_match2 instruction does
+ %% not accept a match state as source.
+ btb_error(unsuitable_bs_start_match)
end;
btb_reaches_match_2([{test,_,{f,F},Ss}=I|Is], Regs, D0) ->
btb_ensure_not_used(Ss, I, Regs),
@@ -278,12 +289,7 @@ btb_reaches_match_2([{test,_,{f,F},_,Ss,_}=I|Is], Regs, D0) ->
btb_ensure_not_used(Ss, I, Regs),
D = btb_follow_branch(F, Regs, D0),
btb_reaches_match_1(Is, Regs, D);
-btb_reaches_match_2([{select_val,Src,{f,F},{list,Conds}}=I|Is], Regs, D0) ->
- btb_ensure_not_used([Src], I, Regs),
- D1 = btb_follow_branch(F, Regs, D0),
- D = btb_follow_branches(Conds, Regs, D1),
- btb_reaches_match_1(Is, Regs, D);
-btb_reaches_match_2([{select_tuple_arity,Src,{f,F},{list,Conds}}=I|Is], Regs, D0) ->
+btb_reaches_match_2([{select,_,Src,{f,F},Conds}=I|Is], Regs, D0) ->
btb_ensure_not_used([Src], I, Regs),
D1 = btb_follow_branch(F, Regs, D0),
D = btb_follow_branches(Conds, Regs, D1),
@@ -293,46 +299,11 @@ btb_reaches_match_2([{jump,{f,Lbl}}|_], Regs, #btb{index=Li}=D) ->
btb_reaches_match_2(Is, Regs, D);
btb_reaches_match_2([{label,_}|Is], Regs, D) ->
btb_reaches_match_2(Is, Regs, D);
-btb_reaches_match_2([{bs_add,{f,0},_,Dst}|Is], Regs, D) ->
- btb_reaches_match_1(Is, btb_kill([Dst], Regs), D);
-btb_reaches_match_2([bs_init_writable|Is], Regs0, D) ->
- Regs = btb_kill_not_live(0, Regs0),
- btb_reaches_match_1(Is, Regs, D);
-btb_reaches_match_2([{bs_init2,{f,0},_,_,_,_,Dst}|Is], Regs, D) ->
- btb_reaches_match_1(Is, btb_kill([Dst], Regs), D);
-btb_reaches_match_2([{bs_init_bits,{f,0},_,_,_,_,Dst}|Is], Regs, D) ->
- btb_reaches_match_1(Is, btb_kill([Dst], Regs), D);
-btb_reaches_match_2([{bs_append,{f,0},_,_,_,_,Src,_,Dst}=I|Is], Regs, D) ->
- btb_ensure_not_used([Src], I, Regs),
- btb_reaches_match_1(Is, btb_kill([Dst], Regs), D);
-btb_reaches_match_2([{bs_private_append,{f,0},_,_,Src,_,Dst}=I|Is], Regs, D) ->
- btb_ensure_not_used([Src], I, Regs),
- btb_reaches_match_1(Is, btb_kill([Dst], Regs), D);
-btb_reaches_match_2([{bs_put_integer,{f,0},_,_,_,Src}=I|Is], Regs, D) ->
- btb_ensure_not_used([Src], I, Regs),
- btb_reaches_match_1(Is, Regs, D);
-btb_reaches_match_2([{bs_put_float,{f,0},_,_,_,Src}=I|Is], Regs, D) ->
- btb_ensure_not_used([Src], I, Regs),
- btb_reaches_match_1(Is, Regs, D);
-btb_reaches_match_2([{bs_put_binary,{f,0},_,_,_,Src}=I|Is], Regs, D) ->
- btb_ensure_not_used([Src], I, Regs),
- btb_reaches_match_1(Is, Regs, D);
-btb_reaches_match_2([{bs_put_string,_,_}|Is], Regs, D) ->
- btb_reaches_match_1(Is, Regs, D);
-btb_reaches_match_2([{bs_utf8_size,_,Src,Dst}=I|Is], Regs, D) ->
- btb_ensure_not_used([Src], I, Regs),
- btb_reaches_match_1(Is, btb_kill([Dst], Regs), D);
-btb_reaches_match_2([{bs_utf16_size,_,Src,Dst}=I|Is], Regs, D) ->
- btb_ensure_not_used([Src], I, Regs),
+btb_reaches_match_2([{bs_init,{f,0},_,_,Ss,Dst}=I|Is], Regs, D) ->
+ btb_ensure_not_used(Ss, I, Regs),
btb_reaches_match_1(Is, btb_kill([Dst], Regs), D);
-btb_reaches_match_2([{bs_put_utf8,_,_,Src}=I|Is], Regs, D) ->
- btb_ensure_not_used([Src], I, Regs),
- btb_reaches_match_1(Is, Regs, D);
-btb_reaches_match_2([{bs_put_utf16,_,_,Src}=I|Is], Regs, D) ->
- btb_ensure_not_used([Src], I, Regs),
- btb_reaches_match_1(Is, Regs, D);
-btb_reaches_match_2([{bs_put_utf32,_,_,Src}=I|Is], Regs, D) ->
- btb_ensure_not_used([Src], I, Regs),
+btb_reaches_match_2([{bs_put,{f,0},_,Ss}=I|Is], Regs, D) ->
+ btb_ensure_not_used(Ss, I, Regs),
btb_reaches_match_1(Is, Regs, D);
btb_reaches_match_2([{bs_restore2,Src,_}=I|Is], Regs0, D) ->
case btb_contains_context(Src, Regs0) of
@@ -340,11 +311,11 @@ btb_reaches_match_2([{bs_restore2,Src,_}=I|Is], Regs0, D) ->
btb_reaches_match_1(Is, Regs0, D);
true ->
%% Check that all other copies of the context registers
- %% are killed by the following instructions.
+ %% are unused by the following instructions.
Regs = btb_kill([Src], Regs0),
CtxRegs = btb_context_regs(Regs),
- case btb_are_all_killed(CtxRegs, Is, D) of
- false -> btb_error({CtxRegs,not_all_killed_after,I});
+ case btb_are_all_unused(CtxRegs, Is, D) of
+ false -> btb_error({CtxRegs,not_all_unused_after,I});
true -> D#btb{must_not_save=true}
end
end;
@@ -354,11 +325,11 @@ btb_reaches_match_2([{bs_context_to_binary,Src}=I|Is], Regs0, D) ->
btb_reaches_match_1(Is, Regs0, D);
true ->
%% Check that all other copies of the context registers
- %% are killed by the following instructions.
+ %% are unused by the following instructions.
Regs = btb_kill([Src], Regs0),
CtxRegs = btb_context_regs(Regs),
- case btb_are_all_killed(CtxRegs, Is, D) of
- false -> btb_error({CtxRegs,not_all_killed_after,I});
+ case btb_are_all_unused(CtxRegs, Is, D) of
+ false -> btb_error({CtxRegs,not_all_unused_after,I});
true -> D#btb{must_not_save=true}
end
end;
@@ -389,13 +360,16 @@ btb_call(Arity, Lbl, Regs0, Is, D0) ->
%% First handle the call as if it were a tail call.
D = btb_tail_call(Lbl, Regs, D0),
- %% No problem so far, but now we must make sure that
- %% we don't have any copies of the match context
- %% tucked away in an y register.
+ %% No problem so far (the called function can handle a
+ %% match context). Now we must make sure that the rest
+ %% of this function following the call does not attempt
+ %% to use the match context in case there is a copy
+ %% tucked away in a y register.
RegList = btb_context_regs(Regs),
- case [R || {y,_}=R <- RegList] of
- [] -> D;
- [_|_] -> btb_error({multiple_uses,RegList})
+ YRegs = [R || {y,_}=R <- RegList],
+ case btb_are_all_unused(YRegs, Is, D) of
+ true -> D;
+ false -> btb_error({multiple_uses,RegList})
end;
true ->
%% No match context in any x register. It could have been
@@ -475,21 +449,12 @@ btb_reaches_match_block([{set,Ds,Ss,_}=I|Is], Regs0) ->
btb_reaches_match_block([], Regs) ->
Regs.
-%% btb_regs_from_arity(Arity) -> [Register])
-%% Create a list of x registers from a function arity.
-
-btb_regs_from_arity(Arity) ->
- btb_regs_from_arity_1(Arity, []).
-
-btb_regs_from_arity_1(0, Acc) -> Acc;
-btb_regs_from_arity_1(N, Acc) -> btb_regs_from_arity_1(N-1, [{x,N-1}|Acc]).
-
%% btb_are_all_killed([Register], [Instruction], D) -> true|false
-%% Test whether all of the register are killed in the instruction stream.
+%% Test whether all of the register are unused in the instruction stream.
-btb_are_all_killed(RegList, Is, #btb{index=Li}) ->
+btb_are_all_unused(RegList, Is, #btb{index=Li}) ->
all(fun(R) ->
- beam_utils:is_killed(R, Is, Li)
+ beam_utils:is_not_used(R, Is, Li)
end, RegList).
%% btp_regs_from_list([Register]) -> RegisterSet.
diff --git a/lib/compiler/src/beam_clean.erl b/lib/compiler/src/beam_clean.erl
index a7994ab3b3..26ba93b91c 100644
--- a/lib/compiler/src/beam_clean.erl
+++ b/lib/compiler/src/beam_clean.erl
@@ -74,10 +74,6 @@ 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_fun2,{f,L},_,_,_}|Is], Sets) ->
update_work_list(Is, add_to_work_list(L, Sets));
update_work_list([_|Is], Sets) ->
@@ -200,7 +196,7 @@ replace([{test,Test,{f,Lbl},Ops}|Is], Acc, D) ->
replace(Is, [{test,Test,{f,label(Lbl, D)},Ops}|Acc], D);
replace([{test,Test,{f,Lbl},Live,Ops,Dst}|Is], Acc, D) ->
replace(Is, [{test,Test,{f,label(Lbl, D)},Live,Ops,Dst}|Acc], D);
-replace([{select_val,R,{f,Fail0},{list,Vls0}}|Is], Acc, D) ->
+replace([{select,I,R,{f,Fail0},Vls0}|Is], Acc, D) ->
Vls1 = map(fun ({f,L}) -> {f,label(L, D)};
(Other) -> Other end, Vls0),
Fail = label(Fail0, D),
@@ -210,12 +206,8 @@ replace([{select_val,R,{f,Fail0},{list,Vls0}}|Is], Acc, D) ->
%% Convert to a plain jump.
replace(Is, [{jump,{f,Fail}}|Acc], D);
Vls ->
- replace(Is, [{select_val,R,{f,Fail},{list,Vls}}|Acc], D)
+ replace(Is, [{select,I,R,{f,Fail},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) ->
@@ -236,37 +228,12 @@ replace([{gc_bif,Name,{f,Lbl},Live,As,R}|Is], Acc, D) when Lbl =/= 0 ->
replace(Is, [{gc_bif,Name,{f,label(Lbl, D)},Live,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_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_init_bits,{f,Lbl},Sz,Words,R,F,Dst}|Is], Acc, D) when Lbl =/= 0 ->
- replace(Is, [{bs_init_bits,{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_utf8=I,{f,Lbl},Fl,Val}|Is], Acc, D) when Lbl =/= 0 ->
- replace(Is, [{I,{f,label(Lbl, D)},Fl,Val}|Acc], D);
-replace([{bs_put_utf16=I,{f,Lbl},Fl,Val}|Is], Acc, D) when Lbl =/= 0 ->
- replace(Is, [{I,{f,label(Lbl, D)},Fl,Val}|Acc], D);
-replace([{bs_put_utf32=I,{f,Lbl},Fl,Val}|Is], Acc, D) when Lbl =/= 0 ->
- replace(Is, [{I,{f,label(Lbl, D)},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_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_append,{f,Lbl},_,_,_,_,_,_,_}=I0|Is], Acc, D) when Lbl =/= 0 ->
- I = setelement(2, I0, {f,label(Lbl, D)}),
- replace(Is, [I|Acc], D);
-replace([{bs_utf8_size=I,{f,Lbl},Src,Dst}|Is], Acc, D) when Lbl =/= 0 ->
- replace(Is, [{I,{f,label(Lbl, D)},Src,Dst}|Acc], D);
-replace([{bs_utf16_size=I,{f,Lbl},Src,Dst}|Is], Acc, D) when Lbl =/= 0 ->
- replace(Is, [{I,{f,label(Lbl, D)},Src,Dst}|Acc], D);
+replace([{bs_init,{f,Lbl},Info,Live,Ss,Dst}|Is], Acc, D) when Lbl =/= 0 ->
+ replace(Is, [{bs_init,{f,label(Lbl, D)},Info,Live,Ss,Dst}|Acc], D);
+replace([{bs_put,{f,Lbl},Info,Ss}|Is], Acc, D) when Lbl =/= 0 ->
+ replace(Is, [{bs_put,{f,label(Lbl, D)},Info,Ss}|Acc], D);
replace([I|Is], Acc, D) ->
replace(Is, [I|Acc], D);
replace([], Acc, _) -> Acc.
diff --git a/lib/compiler/src/beam_dead.erl b/lib/compiler/src/beam_dead.erl
index 5f12a98f09..92d8e5acb3 100644
--- a/lib/compiler/src/beam_dead.erl
+++ b/lib/compiler/src/beam_dead.erl
@@ -182,7 +182,7 @@ forward(Is, Lc) ->
forward([{block,[]}|Is], D, Lc, Acc) ->
%% Empty blocks can prevent optimizations.
forward(Is, D, Lc, Acc);
-forward([{select_val,Reg,_,{list,List}}=I|Is], D0, Lc, Acc) ->
+forward([{select,select_val,Reg,_,List}=I|Is], D0, Lc, Acc) ->
D = update_value_dict(List, Reg, D0),
forward(Is, D, Lc, [I|Acc]);
forward([{label,Lbl}=LblI,{block,[{set,[Dst],[Lit],move}|BlkIs]}=Blk|Is], D, Lc, Acc) ->
@@ -271,11 +271,11 @@ backward([{test,is_eq_exact,Fail,[Dst,{integer,Arity}]}=I|
end;
backward([{label,Lbl}=L|Is], D, Acc) ->
backward(Is, beam_utils:index_label(Lbl, Acc, D), [L|Acc]);
-backward([{select_val,Reg,{f,Fail0},{list,List0}}|Is], D, Acc) ->
+backward([{select,select_val,Reg,{f,Fail0},List0}|Is], D, Acc) ->
List = shortcut_select_list(List0, Reg, D, []),
Fail1 = shortcut_label(Fail0, D),
Fail = shortcut_bs_test(Fail1, Is, D),
- Sel = {select_val,Reg,{f,Fail},{list,List}},
+ Sel = {select,select_val,Reg,{f,Fail},List},
backward(Is, D, [Sel|Acc]);
backward([{jump,{f,To0}},{move,Src,Reg}=Move0|Is], D, Acc) ->
{To,Move} = case Src of
@@ -382,7 +382,7 @@ shortcut_select_label(To0, Reg, Val, D) ->
case beam_utils:code_at(To0, D) of
[{jump,{f,To}}|_] ->
shortcut_select_label(To, Reg, Val, D);
- [{test,is_atom,_,[Reg]},{select_val,Reg,{f,Fail},{list,Map}}|_] ->
+ [{test,is_atom,_,[Reg]},{select,select_val,Reg,{f,Fail},Map}|_] ->
To = find_select_val(Map, Val, Fail),
shortcut_select_label(To, Reg, Val, D);
[{test,is_eq_exact,{f,_},[Reg,{atom,Val}]},{label,To}|_] when is_atom(Val) ->
@@ -472,10 +472,10 @@ combine_eqs(To, [Reg,{Type,_}=Lit1]=Ops, D, [{label,L1}|_])
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}}|_] ->
+ {select,select_val,Reg,{f,F2},[Lit1,{f,L1},Lit2,{f,L2}]};
+ [{select,select_val,Reg,{f,F2},[{Type,_}|_]=List0}|_] ->
List = remove_from_list(Lit1, List0),
- {select_val,Reg,{f,F2},{list,[Lit1,{f,L1}|List]}};
+ {select,select_val,Reg,{f,F2},[Lit1,{f,L1}|List]};
_Is ->
{test,is_eq_exact,{f,To},Ops}
end;
@@ -527,6 +527,8 @@ count_bits_matched([{test,_,_,_,[_,Sz,U,{field_flags,_}],_}|Is], SavePoint, Bits
{integer,N} -> count_bits_matched(Is, SavePoint, Bits+N*U);
_ -> count_bits_matched(Is, SavePoint, Bits)
end;
+count_bits_matched([{test,bs_match_string,_,[_,Bits,_]}|Is], SavePoint, Bits0) ->
+ count_bits_matched(Is, SavePoint, Bits0+Bits);
count_bits_matched([{test,_,_,_}|Is], SavePoint, Bits) ->
count_bits_matched(Is, SavePoint, Bits);
count_bits_matched([{bs_save2,Reg,SavePoint}|_], {Reg,SavePoint}, Bits) ->
diff --git a/lib/compiler/src/beam_except.erl b/lib/compiler/src/beam_except.erl
index fb1a43cd9e..14e9943f88 100644
--- a/lib/compiler/src/beam_except.erl
+++ b/lib/compiler/src/beam_except.erl
@@ -65,10 +65,6 @@ function_1(Is0) ->
translate([{call_ext,Ar,{extfunc,erlang,error,Ar}}=I|Is], St, Acc) ->
translate_1(Ar, I, Is, St, Acc);
-translate([{call_ext_only,Ar,{extfunc,erlang,error,Ar}}=I|Is], St, Acc) ->
- translate_1(Ar, I, Is, St, Acc);
-translate([{call_ext_last,Ar,{extfunc,erlang,error,Ar},_}=I|Is], St, Acc) ->
- translate_1(Ar, I, Is, St, Acc);
translate([I|Is], St, Acc) ->
translate(Is, St, [I|Acc]);
translate([], _, Acc) ->
diff --git a/lib/compiler/src/beam_flatten.erl b/lib/compiler/src/beam_flatten.erl
index 6c7cb849aa..04232d8fd2 100644
--- a/lib/compiler/src/beam_flatten.erl
+++ b/lib/compiler/src/beam_flatten.erl
@@ -79,49 +79,28 @@ norm_allocate({nozero,Ns,Nh,Inits}, Regs) ->
%% insert_alloc_in_bs_init(ReverseInstructionStream, AllocationInfo) ->
%% impossible | ReverseInstructionStream'
-%% A bs_init2/6 instruction should not be followed by a test heap instruction.
+%% A bs_init/6 instruction should not be followed by a test heap instruction.
%% Given the AllocationInfo from a test heap instruction, merge the
-%% allocation amounts into the previous bs_init2/6 instruction (if any).
+%% allocation amounts into the previous bs_init/6 instruction (if any).
%%
-insert_alloc_in_bs_init([I|_]=Is, Alloc) ->
- case is_bs_constructor(I) of
- false -> impossible;
- true -> insert_alloc_1(Is, Alloc, [])
- end.
-
-insert_alloc_1([{bs_init2=Op,Fail,Bs,Ws1,Regs,F,Dst}|Is], {_,nostack,Ws2,[]}, Acc) ->
- Al = beam_utils:combine_heap_needs(Ws1, Ws2),
- I = {Op,Fail,Bs,Al,Regs,F,Dst},
- reverse(Acc, [I|Is]);
-insert_alloc_1([{bs_init_bits=Op,Fail,Bs,Ws1,Regs,F,Dst}|Is], {_,nostack,Ws2,[]}, Acc) ->
- Al = beam_utils:combine_heap_needs(Ws1, Ws2),
- I = {Op,Fail,Bs,Al,Regs,F,Dst},
- reverse(Acc, [I|Is]);
-insert_alloc_1([{bs_append,Fail,Sz,Ws1,Regs,U,Bin,Fl,Dst}|Is],
- {_,nostack,Ws2,[]}, Acc) ->
+insert_alloc_in_bs_init([{bs_put,_,_,_}=I|Is], Alloc) ->
+ %% The instruction sequence ends with an bs_put/4 instruction.
+ %% We'll need to search backwards for the bs_init/6 instruction.
+ insert_alloc_1(Is, Alloc, [I]);
+insert_alloc_in_bs_init(_, _) -> impossible.
+
+insert_alloc_1([{bs_init=Op,Fail,Info0,Live,Ss,Dst}|Is],
+ {_,nostack,Ws2,[]}, Acc) when is_integer(Live) ->
+ %% The number of extra heap words is always in the second position
+ %% in the Info tuple.
+ Ws1 = element(2, Info0),
Al = beam_utils:combine_heap_needs(Ws1, Ws2),
- I = {bs_append,Fail,Sz,Al,Regs,U,Bin,Fl,Dst},
+ Info = setelement(2, Info0, Al),
+ I = {Op,Fail,Info,Live,Ss,Dst},
reverse(Acc, [I|Is]);
-insert_alloc_1([I|Is], Alloc, Acc) ->
+insert_alloc_1([{bs_put,_,_,_}=I|Is], Alloc, Acc) ->
insert_alloc_1(Is, Alloc, [I|Acc]).
-
-%% is_bs_constructor(Instruction) -> true|false.
-%% Test whether the instruction is a bit syntax construction
-%% instruction that can occur at the end of a bit syntax
-%% construction. (Since an empty binary would be expressed
-%% as a literal, the bs_init2/6 instruction will not occur
-%% at the end and therefore it is no need to test for it here.)
-%%
-is_bs_constructor({bs_put_integer,_,_,_,_,_}) -> true;
-is_bs_constructor({bs_put_utf8,_,_,_}) -> true;
-is_bs_constructor({bs_put_utf16,_,_,_}) -> true;
-is_bs_constructor({bs_put_utf32,_,_,_}) -> true;
-is_bs_constructor({bs_put_float,_,_,_,_,_}) -> true;
-is_bs_constructor({bs_put_binary,_,_,_,_,_}) -> true;
-is_bs_constructor({bs_put_string,_,_}) -> true;
-is_bs_constructor(_) -> false.
-
%% opt(Is0) -> Is
%% Simple peep-hole optimization to move a {move,Any,{x,0}} past
%% any kill up to the next call instruction. (To give the loader
diff --git a/lib/compiler/src/beam_jump.erl b/lib/compiler/src/beam_jump.erl
index db67d24514..636c299e47 100644
--- a/lib/compiler/src/beam_jump.erl
+++ b/lib/compiler/src/beam_jump.erl
@@ -20,7 +20,7 @@
-module(beam_jump).
--export([module/2,module_labels/1,
+-export([module/2,
is_unreachable_after/1,is_exit_instruction/1,
remove_unused_labels/1,is_label_used_in/2]).
@@ -46,10 +46,13 @@
%%% 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.
+%%% (2) Short sequences starting with a label and ending in 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, but only if the
+%%% the block is not entered via a fallthrough. The purpose of this move
+%%% is to allow further optimizations at the place from which the
+%%% code was moved (a jump around the block could be replaced with a
+%%% fallthrough).
%%%
%%% (3) Any unreachable code is removed. Unreachable code is code
%%% after jump, call_last and other instructions which never
@@ -130,13 +133,6 @@ module({Mod,Exp,Attr,Fs0,Lc}, _Opt) ->
Fs = [function(F) || F <- Fs0],
{ok,{Mod,Exp,Attr,Fs,Lc}}.
-module_labels({Mod,Exp,Attr,Fs,Lc}) ->
- {Mod,Exp,Attr,[function_labels(F) || F <- Fs],Lc}.
-
-function_labels({function,Name,Arity,CLabel,Asm0}) ->
- Asm = remove_unused_labels(Asm0),
- {function,Name,Arity,CLabel,Asm}.
-
%% function(Function) -> Function'
%% Optimize jumps and branches.
%%
@@ -224,6 +220,8 @@ extract_seq([{line,_}=Line|Is], Acc) ->
extract_seq(Is, [Line|Acc]);
extract_seq([{block,_}=Bl|Is], Acc) ->
extract_seq_1(Is, [Bl|Acc]);
+extract_seq([{bs_context_to_binary,_}=I|Is], Acc) ->
+ extract_seq_1(Is, [I|Acc]);
extract_seq([{label,_}|_]=Is, Acc) ->
extract_seq_1(Is, Acc);
extract_seq(_, _) -> no.
@@ -232,6 +230,9 @@ extract_seq_1([{line,_}=Line|Is], Acc) ->
extract_seq_1(Is, [Line|Acc]);
extract_seq_1([{label,_},{func_info,_,_,_}|_], _) ->
no;
+extract_seq_1([{label,Lbl},{jump,{f,Lbl}}|_], _) ->
+ %% Don't move a sequence which have a fallthrough entering it.
+ no;
extract_seq_1([{label,_}=Lbl|Is], Acc) ->
{yes,[Lbl|Acc],Is};
extract_seq_1(_, _) -> no.
@@ -260,43 +261,39 @@ find_fixpoint(OptFun, Is0) ->
Is -> find_fixpoint(OptFun, Is)
end.
-opt([{test,Test0,{f,Lnum}=Lbl,Ops}=I|Is0], Acc, St) ->
- case Is0 of
- [{jump,{f,Lnum}}|Is] ->
- %% We have
- %% Test Label Ops
- %% jump Label
- %% The test instruction is definitely not needed.
- %% The jump instruction is not needed if there is
- %% a definition of Label following the jump instruction.
- case is_label_defined(Is, Lnum) of
- false ->
- %% The jump instruction is still needed.
- opt(Is0, [I|Acc], label_used(Lbl, St));
- true ->
- %% Neither the test nor the jump are needed.
- opt(Is, Acc, St)
- end;
- [{jump,To}|Is] ->
- case is_label_defined(Is, Lnum) of
- false ->
+opt([{test,_,{f,L}=Lbl,_}=I|[{jump,{f,L}}|_]=Is], Acc, St) ->
+ %% We have
+ %% Test Label Ops
+ %% jump Label
+ %% The test instruction is not needed if the test is pure
+ %% (it modifies neither registers nor bit syntax state).
+ case beam_utils:is_pure_test(I) of
+ false ->
+ %% Test is not pure; we must keep it.
+ opt(Is, [I|Acc], label_used(Lbl, St));
+ true ->
+ %% The test is pure and its failure label is the same
+ %% as in the jump that follows -- thus it is not needed.
+ opt(Is, Acc, St)
+ end;
+opt([{test,Test0,{f,L}=Lbl,Ops}=I|[{jump,To}|Is]=Is0], Acc, St) ->
+ case is_label_defined(Is, L) of
+ false ->
+ opt(Is0, [I|Acc], label_used(Lbl, St));
+ true ->
+ case invert_test(Test0) of
+ not_possible ->
opt(Is0, [I|Acc], label_used(Lbl, St));
- true ->
- case invert_test(Test0) of
- not_possible ->
- opt(Is0, [I|Acc], label_used(Lbl, St));
- Test ->
- opt([{test,Test,To,Ops}|Is], Acc, St)
- end
- end;
- _Other ->
- opt(Is0, [I|Acc], label_used(Lbl, St))
+ Test ->
+ %% Invert the test and remove the jump.
+ opt([{test,Test,To,Ops}|Is], Acc, St)
+ end
end;
+opt([{test,_,{f,_}=Lbl,_}=I|Is], Acc, St) ->
+ opt(Is, [I|Acc], label_used(Lbl, St));
opt([{test,_,{f,_}=Lbl,_,_,_}=I|Is], Acc, St) ->
opt(Is, [I|Acc], label_used(Lbl, St));
-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) ->
+opt([{select,_,_R,Fail,Vls}=I|Is], Acc, St) ->
skip_unreachable(Is, [I|Acc], label_used([Fail|Vls], St));
opt([{label,L}=I|Is], Acc, #st{entry=L}=St) ->
%% NEVER move the entry label.
@@ -412,14 +409,8 @@ is_label_used(L, St) ->
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({select,_What,_R,_Lbl,_Cases}) -> true;
is_unreachable_after({loop_rec_end,_}) -> true;
is_unreachable_after({wait,_}) -> true;
is_unreachable_after(I) -> is_exit_instruction(I).
@@ -430,10 +421,6 @@ is_unreachable_after(I) -> is_exit_instruction(I).
is_exit_instruction({call_ext,_,{extfunc,M,F,A}}) ->
erl_bifs:is_exit_bif(M, F, A);
-is_exit_instruction({call_ext_last,_,{extfunc,M,F,A},_}) ->
- erl_bifs:is_exit_bif(M, F, A);
-is_exit_instruction({call_ext_only,_,{extfunc,M,F,A}}) ->
- erl_bifs:is_exit_bif(M, F, A);
is_exit_instruction(if_end) -> true;
is_exit_instruction({case_end,_}) -> true;
is_exit_instruction({try_case_end,_}) -> true;
@@ -516,9 +503,7 @@ ulbl({test,_,Fail,_}, Used) ->
mark_used(Fail, Used);
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) ->
+ulbl({select,_,_,Fail,Vls}, Used) ->
mark_used_list(Vls, mark_used(Fail, Used));
ulbl({'try',_,Lbl}, Used) ->
mark_used(Lbl, Used);
@@ -538,29 +523,9 @@ ulbl({bif,_Name,Lbl,_As,_R}, Used) ->
mark_used(Lbl, Used);
ulbl({gc_bif,_Name,Lbl,_Live,_As,_R}, Used) ->
mark_used(Lbl, Used);
-ulbl({bs_init2,Lbl,_,_,_,_,_}, Used) ->
- mark_used(Lbl, Used);
-ulbl({bs_init_bits,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_put_utf8,Lbl,_Fl,_Val}, Used) ->
- mark_used(Lbl, Used);
-ulbl({bs_put_utf16,Lbl,_Fl,_Val}, Used) ->
- mark_used(Lbl, Used);
-ulbl({bs_put_utf32,Lbl,_Fl,_Val}, Used) ->
- mark_used(Lbl, Used);
-ulbl({bs_add,Lbl,_,_}, Used) ->
- mark_used(Lbl, Used);
-ulbl({bs_append,Lbl,_,_,_,_,_,_,_}, Used) ->
- mark_used(Lbl, Used);
-ulbl({bs_utf8_size,Lbl,_,_}, Used) ->
+ulbl({bs_init,Lbl,_,_,_,_}, Used) ->
mark_used(Lbl, Used);
-ulbl({bs_utf16_size,Lbl,_,_}, Used) ->
+ulbl({bs_put,Lbl,_,_}, Used) ->
mark_used(Lbl, Used);
ulbl(_, Used) -> Used.
diff --git a/lib/compiler/src/beam_peep.erl b/lib/compiler/src/beam_peep.erl
index f39fc50b95..a199aa50ed 100644
--- a/lib/compiler/src/beam_peep.erl
+++ b/lib/compiler/src/beam_peep.erl
@@ -120,13 +120,13 @@ peep([{test,Op,_,Ops}=I|Is], SeenTests0, Acc) ->
peep(Is, SeenTests, [I|Acc])
end
end;
-peep([{select_val,Src,Fail,
- {list,[{atom,false},{f,L},{atom,true},{f,L}]}}|
+peep([{select,select_val,Src,Fail,
+ [{atom,false},{f,L},{atom,true},{f,L}]}|
[{label,L}|_]=Is], SeenTests, Acc) ->
I = {test,is_boolean,Fail,[Src]},
peep([I|Is], SeenTests, Acc);
-peep([{select_val,Src,Fail,
- {list,[{atom,true},{f,L},{atom,false},{f,L}]}}|
+peep([{select,select_val,Src,Fail,
+ [{atom,true},{f,L},{atom,false},{f,L}]}|
[{label,L}|_]=Is], SeenTests, Acc) ->
I = {test,is_boolean,Fail,[Src]},
peep([I|Is], SeenTests, Acc);
diff --git a/lib/compiler/src/beam_receive.erl b/lib/compiler/src/beam_receive.erl
index bd1f44f66b..fe95a7e35b 100644
--- a/lib/compiler/src/beam_receive.erl
+++ b/lib/compiler/src/beam_receive.erl
@@ -84,13 +84,29 @@ function({function,Name,Arity,Entry,Is}) ->
erlang:raise(Class, Error, Stack)
end.
+opt([{call_ext,A,{extfunc,erlang,spawn_monitor,A}}=I0|Is0], D, Acc)
+ when A =:= 1; A =:= 3 ->
+ case ref_in_tuple(Is0) of
+ no ->
+ opt(Is0, D, [I0|Acc]);
+ {yes,Regs,Is1,MatchReversed} ->
+ %% The call creates a brand new reference. Now
+ %% search for a receive statement in the same
+ %% function that will match against the reference.
+ case opt_recv(Is1, Regs, D) of
+ no ->
+ opt(Is0, D, [I0|Acc]);
+ {yes,Is,Lbl} ->
+ opt(Is, D, MatchReversed++[I0,{recv_mark,{f,Lbl}}|Acc])
+ end
+ end;
opt([{call_ext,Arity,{extfunc,erlang,Name,Arity}}=I|Is0], D, Acc) ->
case creates_new_ref(Name, Arity) of
true ->
%% The call creates a brand new reference. Now
%% search for a receive statement in the same
%% function that will match against the reference.
- case opt_recv(Is0, D) of
+ case opt_recv(Is0, regs_init_x0(), D) of
no ->
opt(Is0, D, [I|Acc]);
{yes,Is,Lbl} ->
@@ -104,19 +120,34 @@ opt([I|Is], D, Acc) ->
opt([], _, Acc) ->
reverse(Acc).
+ref_in_tuple([{test,is_tuple,_,[{x,0}]}=I1,
+ {test,test_arity,_,[{x,0},2]}=I2,
+ {block,[{set,[_],[{x,0}],{get_tuple_element,0}},
+ {set,[Dst],[{x,0}],{get_tuple_element,1}}|Bl]}=I3|Is]) ->
+ ref_in_tuple_1(Bl, Dst, Is, [I3,I2,I1]);
+ref_in_tuple([{test,is_tuple,_,[{x,0}]}=I1,
+ {test,test_arity,_,[{x,0},2]}=I2,
+ {block,[{set,[Dst],[{x,0}],{get_tuple_element,1}}|Bl]}=I3|Is]) ->
+ ref_in_tuple_1(Bl, Dst, Is, [I3,I2,I1]);
+ref_in_tuple(_) -> no.
+
+ref_in_tuple_1(Bl, Dst, Is, MatchReversed) ->
+ Regs0 = regs_init_singleton(Dst),
+ Regs = opt_update_regs_bl(Bl, Regs0),
+ {yes,Regs,Is,MatchReversed}.
+
%% creates_new_ref(Name, Arity) -> true|false.
%% Return 'true' if the BIF Name/Arity will create a new reference.
creates_new_ref(monitor, 2) -> true;
creates_new_ref(make_ref, 0) -> true;
creates_new_ref(_, _) -> false.
-%% opt_recv([Instruction], LabelIndex) -> no|{yes,[Instruction]}
+%% opt_recv([Instruction], Regs, LabelIndex) -> no|{yes,[Instruction]}
%% Search for a receive statement that will only retrieve messages
%% that contain the newly created reference (which is currently in {x,0}).
-opt_recv(Is, D) ->
- R = regs_init_x0(),
+opt_recv(Is, Regs, D) ->
L = gb_sets:empty(),
- opt_recv(Is, D, R, L, []).
+ opt_recv(Is, D, Regs, L, []).
opt_recv([{label,L}=Lbl,{loop_rec,{f,Fail},_}=Loop|Is], D, R0, _, Acc) ->
R = regs_kill_not_live(0, R0),
@@ -157,8 +188,6 @@ opt_update_regs({call_fun,_}, R, L) ->
{regs_kill_not_live(0, R),L};
opt_update_regs({kill,Y}, R, L) ->
{regs_kill([Y], R),L};
-opt_update_regs(send, R, L) ->
- {regs_kill_not_live(0, R),L};
opt_update_regs({'catch',_,{f,Lbl}}, R, L) ->
{R,gb_sets:add(Lbl, L)};
opt_update_regs({catch_end,_}, R, L) ->
@@ -253,10 +282,7 @@ opt_ref_used_1([{test,is_ne_exact,{f,Fail},Args}|Is], RefReg, D, Done0, Regs) ->
opt_ref_used_1([{test,_,{f,Fail},_}|Is], RefReg, D, Done0, Regs) ->
Done = opt_ref_used_at(Fail, RefReg, D, Done0, Regs),
opt_ref_used_1(Is, RefReg, D, Done, Regs);
-opt_ref_used_1([{select_tuple_arity,_,{f,Fail},{list,List}}|_], RefReg, D, Done, Regs) ->
- Lbls = [F || {f,F} <- List] ++ [Fail],
- opt_ref_used_in_all(Lbls, RefReg, D, Done, Regs);
-opt_ref_used_1([{select_val,_,{f,Fail},{list,List}}|_], RefReg, D, Done, Regs) ->
+opt_ref_used_1([{select,_,_,{f,Fail},List}|_], RefReg, D, Done, Regs) ->
Lbls = [F || {f,F} <- List] ++ [Fail],
opt_ref_used_in_all(Lbls, RefReg, D, Done, Regs);
opt_ref_used_1([{label,Lbl}|Is], RefReg, D, Done, Regs) ->
@@ -323,6 +349,12 @@ opt_ref_used_bl([], Regs) -> Regs.
regs_init() ->
{0,0}.
+%% regs_init_singleton(Register) -> RegisterSet
+%% Return a set that only contains one register.
+
+regs_init_singleton(Reg) ->
+ regs_add(Reg, regs_init()).
+
%% regs_init_x0() -> RegisterSet
%% Return a set that only contains the {x,0} register.
diff --git a/lib/compiler/src/beam_trim.erl b/lib/compiler/src/beam_trim.erl
index 5f4fa3b1f8..d95db1f681 100644
--- a/lib/compiler/src/beam_trim.erl
+++ b/lib/compiler/src/beam_trim.erl
@@ -172,38 +172,16 @@ remap([{bif,Name,Fail,Ss,D}|Is], Map, Acc) ->
remap([{gc_bif,Name,Fail,Live,Ss,D}|Is], Map, Acc) ->
I = {gc_bif,Name,Fail,Live,[Map(S) || S <- Ss],Map(D)},
remap(Is, Map, [I|Acc]);
-remap([{bs_add,Fail,[SrcA,SrcB,U],D}|Is], Map, Acc) ->
- I = {bs_add,Fail,[Map(SrcA),Map(SrcB),U],Map(D)},
+remap([{bs_init,Fail,Info,Live,Ss0,Dst0}|Is], Map, Acc) ->
+ Ss = [Map(Src) || Src <- Ss0],
+ Dst = Map(Dst0),
+ I = {bs_init,Fail,Info,Live,Ss,Dst},
remap(Is, Map, [I|Acc]);
-remap([{bs_append=Op,Fail,Bits,Heap,Live,Unit,Bin,Flags,D}|Is], Map, Acc) ->
- I = {Op,Fail,Map(Bits),Heap,Live,Unit,Map(Bin),Flags,Map(D)},
- remap(Is, Map, [I|Acc]);
-remap([{bs_private_append=Op,Fail,Bits,Unit,Bin,Flags,D}|Is], Map, Acc) ->
- I = {Op,Fail,Map(Bits),Unit,Map(Bin),Flags,Map(D)},
- remap(Is, Map, [I|Acc]);
-remap([bs_init_writable=I|Is], Map, Acc) ->
- remap(Is, Map, [I|Acc]);
-remap([{bs_init2,Fail,Src,Live,U,Flags,D}|Is], Map, Acc) ->
- I = {bs_init2,Fail,Map(Src),Live,U,Flags,Map(D)},
- remap(Is, Map, [I|Acc]);
-remap([{bs_init_bits,Fail,Src,Live,U,Flags,D}|Is], Map, Acc) ->
- I = {bs_init_bits,Fail,Map(Src),Live,U,Flags,Map(D)},
- remap(Is, Map, [I|Acc]);
-remap([{bs_put_binary=Op,Fail,Src,U,Flags,D}|Is], Map, Acc) ->
- I = {Op,Fail,Map(Src),U,Flags,Map(D)},
- remap(Is, Map, [I|Acc]);
-remap([{bs_put_integer=Op,Fail,Src,U,Flags,D}|Is], Map, Acc) ->
- I = {Op,Fail,Map(Src),U,Flags,Map(D)},
- remap(Is, Map, [I|Acc]);
-remap([{bs_put_float=Op,Fail,Src,U,Flags,D}|Is], Map, Acc) ->
- I = {Op,Fail,Map(Src),U,Flags,Map(D)},
- remap(Is, Map, [I|Acc]);
-remap([{bs_put_string,_,_}=I|Is], Map, Acc) ->
+remap([{bs_put=Op,Fail,Info,Ss}|Is], Map, Acc) ->
+ I = {Op,Fail,Info,[Map(S) || S <- Ss]},
remap(Is, Map, [I|Acc]);
remap([{kill,Y}|T], Map, Acc) ->
remap(T, Map, [{kill,Map(Y)}|Acc]);
-remap([send=I|T], Map, Acc) ->
- remap(T, Map, [I|Acc]);
remap([{make_fun2,_,_,_,_}=I|T], Map, Acc) ->
remap(T, Map, [I|Acc]);
remap([{deallocate,N}|Is], Map, Acc) ->
@@ -217,12 +195,6 @@ remap([{test,Name,Fail,Live,Ss,Dst}|Is], Map, Acc) ->
remap(Is, Map, [I|Acc]);
remap([return|_]=Is, _, Acc) ->
reverse(Acc, Is);
-remap([{call_last,Ar,Name,N}|Is], Map, Acc) ->
- I = {call_last,Ar,Name,Map({frame_size,N})},
- reverse(Acc, [I|Is]);
-remap([{call_ext_last,Ar,Name,N}|Is], Map, Acc) ->
- I = {call_ext_last,Ar,Name,Map({frame_size,N})},
- reverse(Acc, [I|Is]);
remap([{line,_}=I|Is], Map, Acc) ->
remap(Is, Map, [I|Acc]).
@@ -280,8 +252,8 @@ frame_size([{call_fun,_}|Is], Safe) ->
frame_size(Is, Safe);
frame_size([{call,_,_}|Is], Safe) ->
frame_size(Is, Safe);
-frame_size([{call_ext,A,{extfunc,M,F,A}}|Is], Safe) ->
- case erl_bifs:is_exit_bif(M, F, A) of
+frame_size([{call_ext,_,_}=I|Is], Safe) ->
+ case beam_jump:is_exit_instruction(I) of
true -> throw(not_possible);
false -> frame_size(Is, Safe)
end;
@@ -295,35 +267,15 @@ frame_size([{test,_,{f,L},_}|Is], Safe) ->
frame_size_branch(L, Is, Safe);
frame_size([{test,_,{f,L},_,_,_}|Is], Safe) ->
frame_size_branch(L, Is, Safe);
-frame_size([{bs_add,{f,L},_,_}|Is], Safe) ->
+frame_size([{bs_init,{f,L},_,_,_,_}|Is], Safe) ->
frame_size_branch(L, Is, Safe);
-frame_size([{bs_append,{f,L},_,_,_,_,_,_,_}|Is], Safe) ->
+frame_size([{bs_put,{f,L},_,_}|Is], Safe) ->
frame_size_branch(L, Is, Safe);
-frame_size([{bs_private_append,{f,L},_,_,_,_,_}|Is], Safe) ->
- frame_size_branch(L, Is, Safe);
-frame_size([bs_init_writable|Is], Safe) ->
- frame_size(Is, Safe);
-frame_size([{bs_init2,{f,L},_,_,_,_,_}|Is], Safe) ->
- frame_size_branch(L, Is, Safe);
-frame_size([{bs_init_bits,{f,L},_,_,_,_,_}|Is], Safe) ->
- frame_size_branch(L, Is, Safe);
-frame_size([{bs_put_binary,{f,L},_,_,_,_}|Is], Safe) ->
- frame_size_branch(L, Is, Safe);
-frame_size([{bs_put_integer,{f,L},_,_,_,_}|Is], Safe) ->
- frame_size_branch(L, Is, Safe);
-frame_size([{bs_put_float,{f,L},_,_,_,_}|Is], Safe) ->
- frame_size_branch(L, Is, Safe);
-frame_size([{bs_put_string,_,_}|Is], Safe) ->
- frame_size(Is, Safe);
frame_size([{kill,_}|Is], Safe) ->
frame_size(Is, Safe);
-frame_size([send|Is], Safe) ->
- frame_size(Is, Safe);
frame_size([{make_fun2,_,_,_,_}|Is], Safe) ->
frame_size(Is, Safe);
frame_size([{deallocate,N}|_], _) -> N;
-frame_size([{call_last,_,_,N}|_], _) -> N;
-frame_size([{call_ext_last,_,_,N}|_], _) -> N;
frame_size([{line,_}|Is], Safe) ->
frame_size(Is, Safe);
frame_size([_|_], _) -> throw(not_possible).
diff --git a/lib/compiler/src/beam_type.erl b/lib/compiler/src/beam_type.erl
index 6f0ffb5b25..7392f99fb6 100644
--- a/lib/compiler/src/beam_type.erl
+++ b/lib/compiler/src/beam_type.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -29,10 +29,17 @@ module({Mod,Exp,Attr,Fs0,Lc}, _Opts) ->
{ok,{Mod,Exp,Attr,Fs,Lc}}.
function({function,Name,Arity,CLabel,Asm0}) ->
- Asm1 = beam_utils:live_opt(Asm0),
- Asm2 = opt(Asm1, [], tdb_new()),
- Asm = beam_utils:delete_live_annos(Asm2),
- {function,Name,Arity,CLabel,Asm}.
+ try
+ Asm1 = beam_utils:live_opt(Asm0),
+ Asm2 = opt(Asm1, [], tdb_new()),
+ Asm = beam_utils:delete_live_annos(Asm2),
+ {function,Name,Arity,CLabel,Asm}
+ catch
+ Class:Error ->
+ Stack = erlang:get_stacktrace(),
+ io:fwrite("Function: ~w/~w\n", [Name,Arity]),
+ erlang:raise(Class, Error, Stack)
+ end.
%% opt([Instruction], Accumulator, TypeDb) -> {[Instruction'],TypeDb'}
%% Keep track of type information; try to simplify.
diff --git a/lib/compiler/src/beam_utils.erl b/lib/compiler/src/beam_utils.erl
index abcd93f280..8af0447f63 100644
--- a/lib/compiler/src/beam_utils.erl
+++ b/lib/compiler/src/beam_utils.erl
@@ -87,7 +87,7 @@ is_killed_at(R, Lbl, D) when is_integer(Lbl) ->
%% across branches.
is_not_used(R, Is, D) ->
- St = #live{bl=fun check_used_block/2,lbl=D,res=gb_trees:empty()},
+ St = #live{bl=check_used_block_fun(D),lbl=D,res=gb_trees:empty()},
case check_liveness(R, Is, St) of
{killed,_} -> true;
{used,_} -> false;
@@ -102,7 +102,7 @@ is_not_used(R, Is, D) ->
%% across branches.
is_not_used_at(R, Lbl, D) ->
- St = #live{bl=fun check_used_block/2,lbl=D,res=gb_trees:empty()},
+ St = #live{bl=check_used_block_fun(D),lbl=D,res=gb_trees:empty()},
case check_liveness_at(R, Lbl, St) of
{killed,_} -> true;
{used,_} -> false;
@@ -263,26 +263,14 @@ check_liveness(R, [{test,_,{f,Fail},As}|Is], St0) ->
{_,_}=Other -> Other
end
end;
-check_liveness(R, [{test,_,{f,Fail},Live,Ss,_}|Is], St0) ->
- case R of
- {x,X} ->
- case X < Live orelse member(R, Ss) of
- true -> {used,St0};
- false -> check_liveness_at(R, Fail, St0)
- end;
- {y,_} ->
- case check_liveness_at(R, Fail, St0) of
- {killed,St} -> check_liveness(R, Is, St);
- {_,_}=Other -> Other
- end
- end;
-check_liveness(R, [{select_val,R,_,_}|_], St) ->
- {used,St};
-check_liveness(R, [{select_val,_,Fail,{list,Branches}}|_], St) ->
- check_liveness_everywhere(R, [Fail|Branches], St);
-check_liveness(R, [{select_tuple_arity,R,_,_}|_], St) ->
+check_liveness(R, [{test,Op,Fail,Live,Ss,Dst}|Is], St) ->
+ %% Check this instruction as a block to get a less conservative
+ %% result if the caller is is_not_used/3.
+ Block = [{set,[Dst],Ss,{alloc,Live,{bif,Op,Fail}}}],
+ check_liveness(R, [{block,Block}|Is], St);
+check_liveness(R, [{select,_,R,_,_}|_], St) ->
{used,St};
-check_liveness(R, [{select_tuple_arity,_,Fail,{list,Branches}}|_], St) ->
+check_liveness(R, [{select,_,_,Fail,Branches}|_], St) ->
check_liveness_everywhere(R, [Fail|Branches], St);
check_liveness(R, [{jump,{f,F}}|_], St) ->
check_liveness_at(R, F, St);
@@ -301,37 +289,33 @@ check_liveness(R, [{kill,R}|_], St) ->
{killed,St};
check_liveness(R, [{kill,_}|Is], St) ->
check_liveness(R, Is, St);
-check_liveness(R, [bs_init_writable|Is], St) ->
- if
- R =:= {x,0} -> {used,St};
- true -> check_liveness(R, Is, St)
- end;
-check_liveness(R, [{bs_private_append,_,Bits,_,Bin,_,Dst}|Is], St) ->
- case R of
- Bits -> {used,St};
- Bin -> {used,St};
- Dst -> {killed,St};
- _ -> check_liveness(R, Is, St)
+check_liveness(R, [{bs_init,_,_,none,Ss,Dst}|Is], St) ->
+ case member(R, Ss) of
+ true ->
+ {used,St};
+ false ->
+ if
+ R =:= Dst -> {killed,St};
+ true -> check_liveness(R, Is, St)
+ end
end;
-check_liveness(R, [{bs_append,_,Bits,_,_,_,Bin,_,Dst}|Is], St) ->
+check_liveness(R, [{bs_init,_,_,Live,Ss,Dst}|Is], St) ->
case R of
- Bits -> {used,St};
- Bin -> {used,St};
- Dst -> {killed,St};
- _ -> check_liveness(R, Is, St)
- end;
-check_liveness(R, [{bs_init2,_,_,_,_,_,Dst}|Is], St) ->
- if
- R =:= Dst -> {killed,St};
- true -> check_liveness(R, Is, St)
- end;
-check_liveness(R, [{bs_init_bits,_,_,_,_,_,Dst}|Is], St) ->
- if
- R =:= Dst -> {killed,St};
- true -> check_liveness(R, Is, St)
+ {x,X} ->
+ case X < Live orelse member(R, Ss) of
+ true -> {used,St};
+ false -> {killed,St}
+ end;
+ {y,_} ->
+ case member(R, Ss) of
+ true -> {used,St};
+ false ->
+ if
+ R =:= Dst -> {killed,St};
+ true -> check_liveness(R, Is, St)
+ end
+ end
end;
-check_liveness(R, [{bs_put_string,_,_}|Is], St) ->
- check_liveness(R, Is, St);
check_liveness(R, [{deallocate,_}|Is], St) ->
case R of
{y,_} -> {killed,St};
@@ -339,29 +323,20 @@ check_liveness(R, [{deallocate,_}|Is], St) ->
end;
check_liveness(R, [return|_], St) ->
check_liveness_live_ret(R, 1, St);
-check_liveness(R, [{call_last,Live,_,_}|_], St) ->
- check_liveness_live_ret(R, Live, St);
-check_liveness(R, [{call_only,Live,_}|_], St) ->
- check_liveness_live_ret(R, Live, St);
-check_liveness(R, [{call_ext_last,Live,_,_}|_], St) ->
- check_liveness_live_ret(R, Live, St);
-check_liveness(R, [{call_ext_only,Live,_}|_], St) ->
- check_liveness_live_ret(R, Live, St);
check_liveness(R, [{call,Live,_}|Is], St) ->
case R of
{x,X} when X < Live -> {used,St};
{x,_} -> {killed,St};
{y,_} -> check_liveness(R, Is, St)
end;
-check_liveness(R, [{call_ext,Live,Func}|Is], St) ->
+check_liveness(R, [{call_ext,Live,_}=I|Is], St) ->
case R of
{x,X} when X < Live ->
{used,St};
{x,_} ->
{killed,St};
{y,_} ->
- {extfunc,Mod,Name,Arity} = Func,
- case erl_bifs:is_exit_bif(Mod, Name, Arity) of
+ case beam_jump:is_exit_instruction(I) of
false ->
check_liveness(R, Is, St);
true ->
@@ -387,14 +362,6 @@ check_liveness(R, [{apply,Args}|Is], St) ->
{x,_} -> {killed,St};
{y,_} -> check_liveness(R, Is, St)
end;
-check_liveness(R, [{apply_last,Args,_}|_], St) ->
- check_liveness_live_ret(R, Args+2, St);
-check_liveness(R, [send|Is], St) ->
- case R of
- {x,X} when X < 2 -> {used,St};
- {x,_} -> {killed,St};
- {y,_} -> check_liveness(R, Is, St)
- end;
check_liveness({x,R}, [{'%live',Live}|Is], St) ->
if
R < Live -> check_liveness(R, Is, St);
@@ -429,25 +396,9 @@ check_liveness(R, [{gc_bif,Op,{f,Fail},Live,Ss,D}|Is], St0) ->
Other
end
end;
-check_liveness(R, [{bs_add,{f,0},Ss,D}|Is], St) ->
+check_liveness(R, [{bs_put,{f,0},_,Ss}|Is], St) ->
case member(R, Ss) of
true -> {used,St};
- false when R =:= D -> {killed,St};
- false -> 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};
- false -> check_liveness(R, Is, St)
- end;
-check_liveness(R, [{bs_put_integer,{f,0},Sz,_,_,Src}|Is], St) ->
- case member(R, [Sz,Src]) of
- true -> {used,St};
- false -> check_liveness(R, Is, St)
- end;
-check_liveness(R, [{bs_put_float,{f,0},Sz,_,_,Src}|Is], St) ->
- case member(R, [Sz,Src]) of
- true -> {used,St};
false -> check_liveness(R, Is, St)
end;
check_liveness(R, [{bs_restore2,S,_}|Is], St) ->
@@ -472,6 +423,16 @@ check_liveness(R, [{make_fun2,_,_,_,NumFree}|Is], St) ->
{x,_} -> {killed,St};
_ -> check_liveness(R, Is, St)
end;
+check_liveness({x,_}=R, [{'catch',_,_}|Is], St) ->
+ %% All x registers will be killed if an exception occurs.
+ %% Therefore we only need to check the liveness for the
+ %% instructions following the catch instruction.
+ check_liveness(R, Is, St);
+check_liveness({x,_}=R, [{'try',_,_}|Is], St) ->
+ %% All x registers will be killed if an exception occurs.
+ %% Therefore we only need to check the liveness for the
+ %% instructions inside the 'try' block.
+ check_liveness(R, Is, St);
check_liveness(R, [{try_end,Y}|Is], St) ->
case R of
Y ->
@@ -602,26 +563,50 @@ check_killed_block(_, []) -> transparent.
%%
%% (Unknown instructions will cause an exception.)
-check_used_block({x,X}=R, [{set,_,_,{alloc,Live,_}}|Is]) ->
+check_used_block_fun(D) ->
+ fun(R, Is) -> check_used_block(R, Is, D) end.
+
+check_used_block({x,X}=R, [{set,Ds,Ss,{alloc,Live,Op}}|Is], D) ->
if
X >= Live -> killed;
- true -> check_used_block(R, Is)
+ true ->
+ case member(R, Ss) orelse
+ is_reg_used_at(R, Op, D) of
+ true -> used;
+ false ->
+ case member(R, Ds) of
+ true -> killed;
+ false -> check_used_block(R, Is, D)
+ end
+ end
end;
-check_used_block(R, [{set,Ds,Ss,_Op}|Is]) ->
- case member(R, Ss) of
+check_used_block(R, [{set,Ds,Ss,Op}|Is], D) ->
+ case member(R, Ss) orelse
+ is_reg_used_at(R, Op, D) of
true -> used;
false ->
case member(R, Ds) of
true -> killed;
- false -> check_used_block(R, Is)
+ false -> check_used_block(R, Is, D)
end
end;
-check_used_block(R, [{'%live',Live}|Is]) ->
+check_used_block(R, [{'%live',Live}|Is], D) ->
case R of
{x,X} when X >= Live -> killed;
- _ -> check_used_block(R, Is)
+ _ -> check_used_block(R, Is, D)
end;
-check_used_block(_, []) -> transparent.
+check_used_block(_, [], _) -> transparent.
+
+is_reg_used_at(R, {gc_bif,_,{f,Lbl}}, D) ->
+ is_reg_used_at_1(R, Lbl, D);
+is_reg_used_at(R, {bif,_,{f,Lbl}}, D) ->
+ is_reg_used_at_1(R, Lbl, D);
+is_reg_used_at(_, _, _) -> false.
+
+is_reg_used_at_1(_, 0, _) ->
+ false;
+is_reg_used_at_1(R, Lbl, D) ->
+ not is_not_used_at(R, Lbl, D).
index_labels_1([{label,Lbl}|Is0], Acc) ->
Is = lists:dropwhile(fun({label,_}) -> true;
@@ -654,49 +639,21 @@ combine_alloc_lists_1([]) -> [].
live_opt([{bs_context_to_binary,Src}=I|Is], Regs0, D, Acc) ->
Regs = x_live([Src], Regs0),
live_opt(Is, Regs, D, [I|Acc]);
-live_opt([{bs_add,Fail,[Src1,Src2,_],Dst}=I|Is], Regs0, D, Acc) ->
- Regs1 = x_live([Src1,Src2], x_dead([Dst], Regs0)),
- Regs = live_join_label(Fail, D, Regs1),
- live_opt(Is, Regs, D, [I|Acc]);
-live_opt([{bs_init2,Fail,_,_,Live,_,_}=I|Is], _, D, Acc) ->
- Regs1 = live_call(Live),
+live_opt([{bs_init,Fail,_,none,Ss,Dst}=I|Is], Regs0, D, Acc) ->
+ Regs1 = x_live(Ss, x_dead([Dst], Regs0)),
Regs = live_join_label(Fail, D, Regs1),
live_opt(Is, Regs, D, [I|Acc]);
-live_opt([{bs_init_bits,Fail,Src1,_,Live,_,Src2}=I|Is], _, D, Acc) ->
- Regs1 = live_call(Live),
- Regs2 = x_live([Src1,Src2], Regs1),
- Regs = live_join_label(Fail, D, Regs2),
+live_opt([{bs_init,Fail,Info,Live0,Ss,Dst}|Is], Regs0, D, Acc) ->
+ Regs1 = x_dead([Dst], Regs0),
+ Live = live_regs(Regs1),
+ true = Live =< Live0, %Assertion.
+ Regs2 = live_call(Live),
+ Regs3 = x_live(Ss, Regs2),
+ Regs = live_join_label(Fail, D, Regs3),
+ I = {bs_init,Fail,Info,Live,Ss,Dst},
live_opt(Is, Regs, D, [I|Acc]);
-live_opt([{bs_append,Fail,Src1,_,Live,_,Src2,_,Dst}=I|Is], _Regs0, D, Acc) ->
- Regs1 = x_dead([Dst], x_live([Src1,Src2], live_call(Live))),
- Regs = live_join_label(Fail, D, Regs1),
- live_opt(Is, Regs, D, [I|Acc]);
-live_opt([{bs_private_append,Fail,Src1,_,Src2,_,Dst}=I|Is], Regs0, D, Acc) ->
- Regs1 = x_live([Src1,Src2], x_dead([Dst], Regs0)),
- Regs = live_join_label(Fail, D, Regs1),
- live_opt(Is, Regs, D, [I|Acc]);
-live_opt([{bs_put_binary,Fail,Src1,_,_,Src2}=I|Is], Regs0, D, Acc) ->
- Regs1 = x_live([Src1,Src2], Regs0),
- Regs = live_join_label(Fail, D, Regs1),
- live_opt(Is, Regs, D, [I|Acc]);
-live_opt([{bs_put_float,Fail,Src1,_,_,Src2}=I|Is], Regs0, D, Acc) ->
- Regs1 = x_live([Src1,Src2], Regs0),
- Regs = live_join_label(Fail, D, Regs1),
- live_opt(Is, Regs, D, [I|Acc]);
-live_opt([{bs_put_integer,Fail,Src1,_,_,Src2}=I|Is], Regs0, D, Acc) ->
- Regs1 = x_live([Src1,Src2], Regs0),
- Regs = live_join_label(Fail, D, Regs1),
- live_opt(Is, Regs, D, [I|Acc]);
-live_opt([{bs_put_utf8,Fail,_,Src}=I|Is], Regs0, D, Acc) ->
- Regs1 = x_live([Src], Regs0),
- Regs = live_join_label(Fail, D, Regs1),
- live_opt(Is, Regs, D, [I|Acc]);
-live_opt([{bs_put_utf16,Fail,_,Src}=I|Is], Regs0, D, Acc) ->
- Regs1 = x_live([Src], Regs0),
- Regs = live_join_label(Fail, D, Regs1),
- live_opt(Is, Regs, D, [I|Acc]);
-live_opt([{bs_put_utf32,Fail,_,Src}=I|Is], Regs0, D, Acc) ->
- Regs1 = x_live([Src], Regs0),
+live_opt([{bs_put,Fail,_,Ss}=I|Is], Regs0, D, Acc) ->
+ Regs1 = x_live(Ss, Regs0),
Regs = live_join_label(Fail, D, Regs1),
live_opt(Is, Regs, D, [I|Acc]);
live_opt([{bs_restore2,Src,_}=I|Is], Regs0, D, Acc) ->
@@ -705,14 +662,6 @@ live_opt([{bs_restore2,Src,_}=I|Is], Regs0, D, Acc) ->
live_opt([{bs_save2,Src,_}=I|Is], Regs0, D, Acc) ->
Regs = x_live([Src], Regs0),
live_opt(Is, Regs, D, [I|Acc]);
-live_opt([{bs_utf8_size,Fail,Src,Dst}=I|Is], Regs0, D, Acc) ->
- Regs1 = x_live([Src], x_dead([Dst], Regs0)),
- Regs = live_join_label(Fail, D, Regs1),
- live_opt(Is, Regs, D, [I|Acc]);
-live_opt([{bs_utf16_size,Fail,Src,Dst}=I|Is], Regs0, D, Acc) ->
- Regs1 = x_live([Src], x_dead([Dst], Regs0)),
- Regs = live_join_label(Fail, D, Regs1),
- live_opt(Is, Regs, D, [I|Acc]);
live_opt([{test,bs_start_match2,Fail,Live,[Src,_],_}=I|Is], _, D, Acc) ->
Regs0 = live_call(Live),
Regs1 = x_live([Src], Regs0),
@@ -741,33 +690,22 @@ live_opt([{badmatch,Src}=I|Is], _, D, Acc) ->
live_opt([{case_end,Src}=I|Is], _, D, Acc) ->
Regs = x_live([Src], 0),
live_opt(Is, Regs, D, [I|Acc]);
+live_opt([{try_case_end,Src}=I|Is], _, D, Acc) ->
+ Regs = x_live([Src], 0),
+ live_opt(Is, Regs, D, [I|Acc]);
live_opt([if_end=I|Is], _, D, Acc) ->
Regs = 0,
live_opt(Is, Regs, D, [I|Acc]);
-live_opt([bs_init_writable=I|Is], _, D, Acc) ->
- live_opt(Is, live_call(1), D, [I|Acc]);
live_opt([{call,Arity,_}=I|Is], _, D, Acc) ->
live_opt(Is, live_call(Arity), D, [I|Acc]);
live_opt([{call_ext,Arity,_}=I|Is], _, D, Acc) ->
live_opt(Is, live_call(Arity), D, [I|Acc]);
live_opt([{call_fun,Arity}=I|Is], _, D, Acc) ->
live_opt(Is, live_call(Arity+1), D, [I|Acc]);
-live_opt([{call_last,Arity,_,_}=I|Is], _, D, Acc) ->
- live_opt(Is, live_call(Arity), D, [I|Acc]);
-live_opt([{call_ext_last,Arity,_,_}=I|Is], _, D, Acc) ->
- live_opt(Is, live_call(Arity), D, [I|Acc]);
live_opt([{apply,Arity}=I|Is], _, D, Acc) ->
live_opt(Is, live_call(Arity+2), D, [I|Acc]);
-live_opt([{apply_last,Arity,_}=I|Is], _, D, Acc) ->
- live_opt(Is, live_call(Arity+2), D, [I|Acc]);
-live_opt([{call_only,Arity,_}=I|Is], _, D, Acc) ->
- live_opt(Is, live_call(Arity), D, [I|Acc]);
-live_opt([{call_ext_only,Arity,_}=I|Is], _, D, Acc) ->
- live_opt(Is, live_call(Arity), D, [I|Acc]);
live_opt([{make_fun2,_,_,_,Arity}=I|Is], _, D, Acc) ->
live_opt(Is, live_call(Arity), D, [I|Acc]);
-live_opt([send=I|Is], _, D, Acc) ->
- live_opt(Is, live_call(2), D, [I|Acc]);
live_opt([{test,_,Fail,Ss}=I|Is], Regs0, D, Acc) ->
Regs1 = x_live(Ss, Regs0),
Regs = live_join_label(Fail, D, Regs1),
@@ -777,16 +715,14 @@ live_opt([{test,_,Fail,Live,Ss,_}=I|Is], _, D, Acc) ->
Regs1 = x_live(Ss, Regs0),
Regs = live_join_label(Fail, D, Regs1),
live_opt(Is, Regs, D, [I|Acc]);
-live_opt([{select_val,Src,Fail,{list,List}}=I|Is], Regs0, D, Acc) ->
+live_opt([{select,_,Src,Fail,List}=I|Is], Regs0, D, Acc) ->
Regs1 = x_live([Src], Regs0),
Regs = live_join_labels([Fail|List], D, Regs1),
live_opt(Is, Regs, D, [I|Acc]);
-live_opt([{select_tuple_arity,Src,Fail,{list,List}}=I|Is], Regs0, D, Acc) ->
- Regs1 = x_live([Src], Regs0),
- Regs = live_join_labels([Fail|List], D, Regs1),
- live_opt(Is, Regs, D, [I|Acc]);
-live_opt([{'try',_,Fail}=I|Is], Regs0, D, Acc) ->
- Regs = live_join_label(Fail, D, Regs0),
+live_opt([{'try',_,_}=I|Is], Regs, D, Acc) ->
+ %% If an exeption happens, all x registers will be killed.
+ %% Therefore, we should only base liveness of the code inside
+ %% the try.
live_opt(Is, Regs, D, [I|Acc]);
live_opt([{try_case,_}=I|Is], _, D, Acc) ->
live_opt(Is, live_call(1), D, [I|Acc]);
@@ -796,14 +732,10 @@ live_opt([timeout=I|Is], _, D, Acc) ->
live_opt(Is, 0, D, [I|Acc]);
%% Transparent instructions - they neither use nor modify x registers.
-live_opt([{bs_put_string,_,_}=I|Is], Regs, D, Acc) ->
- live_opt(Is, Regs, D, [I|Acc]);
live_opt([{deallocate,_}=I|Is], Regs, D, Acc) ->
live_opt(Is, Regs, D, [I|Acc]);
live_opt([{kill,_}=I|Is], Regs, D, Acc) ->
live_opt(Is, Regs, D, [I|Acc]);
-live_opt([{try_case_end,_}=I|Is], Regs, D, Acc) ->
- live_opt(Is, Regs, D, [I|Acc]);
live_opt([{try_end,_}=I|Is], Regs, D, Acc) ->
live_opt(Is, Regs, D, [I|Acc]);
live_opt([{loop_rec_end,_}=I|Is], Regs, D, Acc) ->
@@ -826,13 +758,24 @@ live_opt([{allocate_heap,_,_,Live}=I|Is], _, D, Acc) ->
live_opt([], _, _, Acc) -> Acc.
-live_opt_block([{set,[],[],{alloc,Live,_}}=I|Is], _, D, Acc) ->
- live_opt_block(Is, live_call(Live), D, [I|Acc]);
-live_opt_block([{set,Ds,Ss,Op}=I|Is], Regs0, D, Acc) ->
- Regs = case Op of
- {alloc,Live,_} -> live_call(Live);
- _ -> x_live(Ss, x_dead(Ds, Regs0))
- end,
+live_opt_block([{set,Ds,Ss,Op}=I0|Is], Regs0, D, Acc) ->
+ Regs1 = x_live(Ss, x_dead(Ds, Regs0)),
+ {I,Regs} = case Op of
+ {alloc,Live0,Alloc} ->
+ %% The life-time analysis used by the code generator
+ %% is sometimes too conservative, so it may be
+ %% possible to lower the number of live registers
+ %% based on the exact liveness information.
+ %% The main benefit is that more optimizations that
+ %% depend on liveness information (such as the
+ %% beam_bool and beam_dead passes) may be applied.
+ Live = live_regs(Regs1),
+ true = Live =< Live0, %Assertion.
+ I1 = {set,Ds,Ss,{alloc,Live,Alloc}},
+ {I1,live_call(Live)};
+ _ ->
+ {I0,Regs1}
+ end,
case Ds of
[{x,X}] ->
case (not is_live(X, Regs0)) andalso Op =:= move of
diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl
index a52e7bb761..29758b8fb4 100644
--- a/lib/compiler/src/beam_validator.erl
+++ b/lib/compiler/src/beam_validator.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -783,15 +783,27 @@ valfun_4({bs_utf16_size,{f,Fail},A,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));
-valfun_4({bs_init2,{f,Fail},_,Heap,Live,_,Dst}, Vst0) ->
+valfun_4({bs_init2,{f,Fail},Sz,Heap,Live,_,Dst}, Vst0) ->
verify_live(Live, Vst0),
+ if
+ is_integer(Sz) ->
+ ok;
+ true ->
+ assert_term(Sz, Vst0)
+ end,
Vst1 = heap_alloc(Heap, Vst0),
Vst2 = branch_state(Fail, Vst1),
Vst3 = prune_x_regs(Live, Vst2),
Vst = bs_zero_bits(Vst3),
set_type_reg(binary, Dst, Vst);
-valfun_4({bs_init_bits,{f,Fail},_,Heap,Live,_,Dst}, Vst0) ->
+valfun_4({bs_init_bits,{f,Fail},Sz,Heap,Live,_,Dst}, Vst0) ->
verify_live(Live, Vst0),
+ if
+ is_integer(Sz) ->
+ ok;
+ true ->
+ assert_term(Sz, Vst0)
+ end,
Vst1 = heap_alloc(Heap, Vst0),
Vst2 = branch_state(Fail, Vst1),
Vst3 = prune_x_regs(Live, Vst2),
diff --git a/lib/compiler/src/beam_z.erl b/lib/compiler/src/beam_z.erl
new file mode 100644
index 0000000000..8c6b0c916d
--- /dev/null
+++ b/lib/compiler/src/beam_z.erl
@@ -0,0 +1,79 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, 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: Run right before beam_asm to do any final fix-ups or clean-ups.
+%% (Mandatory.)
+
+-module(beam_z).
+
+-export([module/2]).
+
+module({Mod,Exp,Attr,Fs0,Lc}, _Opt) ->
+ Fs = [function(F) || F <- Fs0],
+ {ok,{Mod,Exp,Attr,Fs,Lc}}.
+
+function({function,Name,Arity,CLabel,Is0}) ->
+ try
+ Is = undo_renames(Is0),
+ {function,Name,Arity,CLabel,Is}
+ catch
+ Class:Error ->
+ Stack = erlang:get_stacktrace(),
+ io:fwrite("Function: ~w/~w\n", [Name,Arity]),
+ erlang:raise(Class, Error, Stack)
+ end.
+
+undo_renames([{call_ext,2,send}|Is]) ->
+ [send|undo_renames(Is)];
+undo_renames([{apply,A},{deallocate,N},return|Is]) ->
+ [{apply_last,A,N}|undo_renames(Is)];
+undo_renames([{call,A,F},{deallocate,N},return|Is]) ->
+ [{call_last,A,F,N}|undo_renames(Is)];
+undo_renames([{call_ext,A,F},{deallocate,N},return|Is]) ->
+ [{call_ext_last,A,F,N}|undo_renames(Is)];
+undo_renames([{call,A,F},return|Is]) ->
+ [{call_only,A,F}|undo_renames(Is)];
+undo_renames([{call_ext,A,F},return|Is]) ->
+ [{call_ext_only,A,F}|undo_renames(Is)];
+undo_renames([I|Is]) ->
+ [undo_rename(I)|undo_renames(Is)];
+undo_renames([]) -> [].
+
+undo_rename({bs_put,F,{I,U,Fl},[Sz,Src]}) ->
+ {I,F,Sz,U,Fl,Src};
+undo_rename({bs_put,F,{I,Fl},[Src]}) ->
+ {I,F,Fl,Src};
+undo_rename({bs_put,{f,0},{bs_put_string,_,_}=I,[]}) ->
+ I;
+undo_rename({bif,bs_add=I,F,[Src1,Src2,{integer,U}],Dst}) ->
+ {I,F,[Src1,Src2,U],Dst};
+undo_rename({bif,bs_utf8_size=I,F,[Src],Dst}) ->
+ {I,F,Src,Dst};
+undo_rename({bif,bs_utf16_size=I,F,[Src],Dst}) ->
+ {I,F,Src,Dst};
+undo_rename({bs_init,F,{I,U,Flags},none,[Sz,Src],Dst}) ->
+ {I,F,Sz,U,Src,Flags,Dst};
+undo_rename({bs_init,F,{I,Extra,Flags},Live,[Sz],Dst}) ->
+ {I,F,Sz,Extra,Live,Flags,Dst};
+undo_rename({bs_init,F,{I,Extra,U,Flags},Live,[Sz,Src],Dst}) ->
+ {I,F,Sz,Extra,Live,U,Src,Flags,Dst};
+undo_rename({bs_init,_,bs_init_writable=I,_,_,_}) ->
+ I;
+undo_rename({select,I,Reg,Fail,List}) ->
+ {I,Reg,Fail,{list,List}};
+undo_rename(I) -> I.
diff --git a/lib/compiler/src/compile.erl b/lib/compiler/src/compile.erl
index 9b505ad15c..5f394f0b65 100644
--- a/lib/compiler/src/compile.erl
+++ b/lib/compiler/src/compile.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -146,10 +146,17 @@ env_default_opts() ->
do_compile(Input, Opts0) ->
Opts = expand_opts(Opts0),
- Self = self(),
- Serv = spawn_link(fun() -> internal(Self, Input, Opts) end),
+ {Pid,Ref} =
+ spawn_monitor(fun() ->
+ exit(try
+ internal(Input, Opts)
+ catch
+ error:Reason ->
+ {error,Reason}
+ end)
+ end),
receive
- {Serv,Rep} -> Rep
+ {'DOWN',Ref,process,Pid,Rep} -> Rep
end.
expand_opts(Opts0) ->
@@ -217,6 +224,8 @@ format_error({delete_temp,File,Error}) ->
[File,file:format_error(Error)]);
format_error({parse_transform,M,R}) ->
io_lib:format("error in parse transform '~s': ~p", [M, R]);
+format_error({undef_parse_transform,M}) ->
+ io_lib:format("undefined parse transform '~s'", [M]);
format_error({core_transform,M,R}) ->
io_lib:format("error in core transform '~s': ~p", [M, R]);
format_error({crash,Pass,Reason}) ->
@@ -239,18 +248,16 @@ format_error({module_name,Mod,Filename}) ->
abstract_code=[], %Abstract code for debugger.
options=[] :: [option()], %Options for compilation
mod_options=[] :: [option()], %Options for module_info
+ encoding=none :: none | epp:source_coding(),
errors=[],
warnings=[]}).
-internal(Master, Input, Opts) ->
- Master ! {self(), try internal(Input, Opts)
- catch error:Reason -> {error, Reason}
- end}.
-
-internal({forms,Forms}, Opts) ->
- {_,Ps} = passes(forms, Opts),
- internal_comp(Ps, "", "", #compile{code=Forms,options=Opts,
- mod_options=Opts});
+internal({forms,Forms}, Opts0) ->
+ {_,Ps} = passes(forms, Opts0),
+ Source = proplists:get_value(source, Opts0, ""),
+ Opts1 = proplists:delete(source, Opts0),
+ Compile = #compile{code=Forms,options=Opts1,mod_options=Opts1},
+ internal_comp(Ps, Source, "", Compile);
internal({file,File}, Opts) ->
{Ext,Ps} = passes(file, Opts),
Compile = #compile{options=Opts,mod_options=Opts},
@@ -547,12 +554,12 @@ select_list_passes_1([{iff,Flag,{done,Ext}}|Ps], Opts, Acc) ->
end;
select_list_passes_1([{iff=Op,Flag,List0}|Ps], Opts, Acc) when is_list(List0) ->
case select_list_passes(List0, Opts) of
- {done,_}=Done -> Done;
+ {done,List} -> {done,reverse(Acc) ++ List};
{not_done,List} -> select_list_passes_1(Ps, Opts, [{Op,Flag,List}|Acc])
end;
select_list_passes_1([{unless=Op,Flag,List0}|Ps], Opts, Acc) when is_list(List0) ->
case select_list_passes(List0, Opts) of
- {done,_}=Done -> Done;
+ {done,List} -> {done,reverse(Acc) ++ List};
{not_done,List} -> select_list_passes_1(Ps, Opts, [{Op,Flag,List}|Acc])
end;
select_list_passes_1([P|Ps], Opts, Acc) ->
@@ -626,7 +633,8 @@ kernel_passes() ->
asm_passes() ->
%% Assembly level optimisations.
[{delay,
- [{unless,no_postopt,
+ [{pass,beam_a},
+ {unless,no_postopt,
[{pass,beam_block},
{iff,dblk,{listing,"block"}},
{unless,no_except,{pass,beam_except}},
@@ -653,13 +661,11 @@ asm_passes() ->
{iff,dtrim,{listing,"trim"}},
{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}]},
+ %% If post optimizations are turned off, we still
+ %% need to do a few clean-ups to code.
+ {iff,no_postopt,[{pass,beam_clean}]},
+ {pass,beam_z},
{iff,dopt,{listing,"optimize"}},
{iff,'S',{listing,"S"}},
{iff,'to_asm',{done,"S"}}]},
@@ -729,8 +735,9 @@ collect_asm([X | Rest], R) ->
beam_consult_asm(St) ->
case file:consult(St#compile.ifile) of
{ok, Forms0} ->
+ Encoding = epp:read_encoding(St#compile.ifile),
{Module, Forms} = preprocess_asm_forms(Forms0),
- {ok,St#compile{module=Module, code=Forms}};
+ {ok,St#compile{module=Module, code=Forms, encoding=Encoding}};
{error,E} ->
Es = [{St#compile.ifile,[{none,?MODULE,{open,E}}]}],
{error,St#compile{errors=St#compile.errors ++ Es}}
@@ -772,7 +779,8 @@ parse_module(St) ->
R = epp:parse_file(St#compile.ifile, IncludePath, pre_defs(Opts)),
case R of
{ok,Forms} ->
- {ok,St#compile{code=Forms}};
+ Encoding = epp:read_encoding(St#compile.ifile),
+ {ok,St#compile{code=Forms,encoding=Encoding}};
{error,E} ->
Es = [{St#compile.ifile,[{none,?MODULE,{epp,E}}]}],
{error,St#compile{errors=St#compile.errors ++ Es}}
@@ -846,6 +854,10 @@ foldl_transform(St, [T|Ts]) ->
{error,Es,Ws} ->
{error,St#compile{warnings=St#compile.warnings ++ Ws,
errors=St#compile.errors ++ Es}};
+ {'EXIT',{undef,_}} ->
+ Es = [{St#compile.ifile,[{none,compile,
+ {undef_parse_transform,T}}]}],
+ {error,St#compile{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}};
@@ -883,7 +895,6 @@ 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).
@@ -895,11 +906,8 @@ add_default_base(St, Forms) ->
F = St#compile.filename,
case F of
"" ->
- M = case get_module(Forms) of
- PackageModule when is_list(PackageModule) -> last(PackageModule);
- M0 -> M0
- end,
- St#compile{base = atom_to_list(M)};
+ M = get_module(Forms),
+ St#compile{base=atom_to_list(M)};
_ ->
St
end.
@@ -1232,10 +1240,6 @@ random_bytes_1(N, Acc) -> random_bytes_1(N-1, [random:uniform(255)|Acc]).
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,mod_options=Opts0}=St) ->
Source = filename:absname(File),
@@ -1334,16 +1338,12 @@ save_binary(#compile{code=none}=St) -> {ok,St};
save_binary(#compile{module=Mod,ofile=Outfile,
options=Opts}=St) ->
%% Test that the module name and output file name match.
- %% We must take care to not completely break a packaged module
- %% (even though packages still is as an experimental, unsupported
- %% feature) - so we will extract the last part of a packaged
- %% module name and compare only that.
case member(no_error_module_mismatch, Opts) of
true ->
save_binary_1(St);
false ->
Base = filename:rootname(filename:basename(Outfile)),
- case lists:last(packages:split(Mod)) of
+ case atom_to_list(Mod) of
Base ->
save_binary_1(St);
_ ->
@@ -1419,28 +1419,28 @@ report_warnings(#compile{options=Opts,warnings=Ws0}) ->
end.
format_message(F, P, [{{Line,Column}=Loc,Mod,E}|Es]) ->
- M = {{F,Loc},io_lib:format("~s:~w:~w ~s~s\n",
+ M = {{F,Loc},io_lib:format("~s:~w:~w ~s~ts\n",
[F,Line,Column,P,Mod:format_error(E)])},
[M|format_message(F, P, Es)];
format_message(F, P, [{Line,Mod,E}|Es]) ->
- M = {{F,{Line,0}},io_lib:format("~s:~w: ~s~s\n",
+ M = {{F,{Line,0}},io_lib:format("~s:~w: ~s~ts\n",
[F,Line,P,Mod:format_error(E)])},
[M|format_message(F, P, Es)];
format_message(F, P, [{Mod,E}|Es]) ->
- M = {none,io_lib:format("~s: ~s~s\n", [F,P,Mod:format_error(E)])},
+ M = {none,io_lib:format("~s: ~s~ts\n", [F,P,Mod:format_error(E)])},
[M|format_message(F, P, Es)];
format_message(_, _, []) -> [].
%% list_errors(File, ErrorDescriptors) -> ok
list_errors(F, [{{Line,Column},Mod,E}|Es]) ->
- io:fwrite("~s:~w:~w: ~s\n", [F,Line,Column,Mod:format_error(E)]),
+ io:fwrite("~s:~w:~w: ~ts\n", [F,Line,Column,Mod:format_error(E)]),
list_errors(F, Es);
list_errors(F, [{Line,Mod,E}|Es]) ->
- io:fwrite("~s:~w: ~s\n", [F,Line,Mod:format_error(E)]),
+ io:fwrite("~s:~w: ~ts\n", [F,Line,Mod:format_error(E)]),
list_errors(F, Es);
list_errors(F, [{Mod,E}|Es]) ->
- io:fwrite("~s: ~s\n", [F,Mod:format_error(E)]),
+ io:fwrite("~s: ~ts\n", [F,Mod:format_error(E)]),
list_errors(F, Es);
list_errors(_F, []) -> ok.
@@ -1496,10 +1496,12 @@ src_listing(Ext, St) ->
Ext, St).
do_src_listing(Lf, Fs) ->
- foreach(fun (F) -> io:put_chars(Lf, [erl_pp:form(F),"\n"]) end,
+ Opts = [lists:keyfind(encoding, 1, io:getopts(Lf))],
+ foreach(fun (F) -> io:put_chars(Lf, [erl_pp:form(F, Opts),"\n"]) end,
Fs).
-listing(Ext, St) ->
+listing(Ext, St0) ->
+ St = St0#compile{encoding = none},
listing(fun(Lf, Fs) -> beam_listing:module(Lf, Fs) end, Ext, St).
listing(LFun, Ext, St) ->
@@ -1507,6 +1509,7 @@ listing(LFun, Ext, St) ->
case file:open(Lfile, [write,delayed_write]) of
{ok,Lf} ->
Code = restore_expanded_types(Ext, St#compile.code),
+ output_encoding(Lf, St),
LFun(Lf, Code),
ok = file:close(Lf),
{ok,St};
@@ -1515,6 +1518,12 @@ listing(LFun, Ext, St) ->
{error,St#compile{errors=St#compile.errors ++ Es}}
end.
+output_encoding(F, #compile{encoding = none}) ->
+ ok = io:setopts(F, [{encoding, epp:default_encoding()}]);
+output_encoding(F, #compile{encoding = Encoding}) ->
+ ok = io:setopts(F, [{encoding, Encoding}]),
+ ok = io:fwrite(F, <<"%% ~s\n">>, [epp:encoding_to_string(Encoding)]).
+
restore_expanded_types("P", Fs) ->
epp:restore_typed_record_fields(Fs);
restore_expanded_types("E", {M,I,Fs0}) ->
diff --git a/lib/compiler/src/compiler.app.src b/lib/compiler/src/compiler.app.src
index 1133882728..9a02121d8b 100644
--- a/lib/compiler/src/compiler.app.src
+++ b/lib/compiler/src/compiler.app.src
@@ -20,6 +20,7 @@
[{description, "ERTS CXC 138 10"},
{vsn, "%VSN%"},
{modules, [
+ beam_a,
beam_asm,
beam_block,
beam_bool,
@@ -40,6 +41,7 @@
beam_type,
beam_utils,
beam_validator,
+ beam_z,
cerl,
cerl_clauses,
cerl_inline,
@@ -55,7 +57,6 @@
sys_core_dsetel,
sys_core_fold,
sys_core_inline,
- sys_expand_pmod,
sys_pre_attributes,
sys_pre_expand,
v3_codegen,
diff --git a/lib/compiler/src/core_lint.erl b/lib/compiler/src/core_lint.erl
index b513a8965c..21296a8b66 100644
--- a/lib/compiler/src/core_lint.erl
+++ b/lib/compiler/src/core_lint.erl
@@ -247,7 +247,8 @@ gbody(E, Def, Rt, St0) ->
false -> St1
end.
-gexpr(#c_var{name=N}, Def, _Rt, St) -> expr_var(N, Def, St);
+gexpr(#c_var{name=N}, Def, _Rt, St) when is_atom(N); is_integer(N) ->
+ expr_var(N, Def, St);
gexpr(#c_literal{}, _Def, _Rt, St) -> St;
gexpr(#c_cons{hd=H,tl=T}, Def, _Rt, St) ->
gexpr_list([H,T], Def, St);
diff --git a/lib/compiler/src/core_scan.erl b/lib/compiler/src/core_scan.erl
index 5aab8ae855..0ca2f57dde 100644
--- a/lib/compiler/src/core_scan.erl
+++ b/lib/compiler/src/core_scan.erl
@@ -1,7 +1,8 @@
+%% -*- coding: utf-8 -*-
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -31,16 +32,16 @@
%% 173 - 176 { - ~ punctuation
%% 177 DEL control
%% 200 - 237 control
-%% 240 - 277 NBSP - � punctuation
-%% 300 - 326 � - � uppercase
-%% 327 � punctuation
-%% 330 - 336 � - � uppercase
-%% 337 - 366 � - � lowercase
-%% 367 � punctuation
-%% 370 - 377 � - � lowercase
+%% 240 - 277 NBSP - ¿ punctuation
+%% 300 - 326 À - Ö uppercase
+%% 327 × punctuation
+%% 330 - 336 Ø - Þ uppercase
+%% 337 - 366 ß - ö lowercase
+%% 367 ÷ punctuation
+%% 370 - 377 ø - ÿ lowercase
%%
%% Many punctuation characters region have special meaning. Must
-%% watch using � \327, bvery close to x \170
+%% watch using × \327, bvery close to x \170
-module(core_scan).
@@ -239,11 +240,11 @@ 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 /= $� ->
+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 /= $� ->
+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);
@@ -308,9 +309,9 @@ 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 >= $ß, C =< $ÿ, C /= $÷ -> true;
name_char(C) when C >= $A, C =< $Z -> true;
-name_char(C) when C >= $�, C =< $�, C /= $� -> true;
+name_char(C) when C >= $À, C =< $Þ, C /= $× -> true;
name_char(C) when C >= $0, C =< $9 -> true;
name_char($_) -> true;
name_char($@) -> true;
diff --git a/lib/compiler/src/sys_core_fold.erl b/lib/compiler/src/sys_core_fold.erl
index 18fba7962b..fbd7452301 100644
--- a/lib/compiler/src/sys_core_fold.erl
+++ b/lib/compiler/src/sys_core_fold.erl
@@ -686,11 +686,14 @@ call_1(#c_call{anno=Anno}, lists, all, [Arg1,Arg2], Sub) ->
C1 = #c_clause{pats=[#c_cons{hd=X, tl=Xs}], guard=#c_literal{val=true},
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},
+ C2 = #c_clause{pats=[#c_literal{val=[]}],
+ guard=#c_call{module=#c_literal{val=erlang},
+ name=#c_literal{val=is_function},
+ args=[F, #c_literal{val=1}]},
body=#c_literal{val=true}},
- Err2 = #c_tuple{es=[#c_literal{val='function_clause'}, Xs]},
+ Err2 = #c_tuple{es=[#c_literal{val='function_clause'}, F, Xs]},
C3 = #c_clause{pats=[Xs], guard=#c_literal{val=true},
- body=match_fail(Anno, Err2)},
+ body=match_fail([{function_name,{'lists^all',1}}|Anno], Err2)},
Fun = #c_fun{vars=[Xs],
body=#c_case{arg=Xs, clauses=[C1, C2, C3]}},
L = #c_var{name='L'},
@@ -713,11 +716,14 @@ call_1(#c_call{anno=Anno}, lists, any, [Arg1,Arg2], Sub) ->
C1 = #c_clause{pats=[#c_cons{hd=X, tl=Xs}], guard=#c_literal{val=true},
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},
+ C2 = #c_clause{pats=[#c_literal{val=[]}],
+ guard=#c_call{module=#c_literal{val=erlang},
+ name=#c_literal{val=is_function},
+ args=[F, #c_literal{val=1}]},
body=#c_literal{val=false}},
- Err2 = #c_tuple{es=[#c_literal{val='function_clause'}, Xs]},
+ Err2 = #c_tuple{es=[#c_literal{val='function_clause'}, F, Xs]},
C3 = #c_clause{pats=[Xs], guard=#c_literal{val=true},
- body=match_fail(Anno, Err2)},
+ body=match_fail([{function_name,{'lists^any',1}}|Anno], Err2)},
Fun = #c_fun{vars=[Xs],
body=#c_case{arg=Xs, clauses=[C1, C2, C3]}},
L = #c_var{name='L'},
@@ -733,11 +739,14 @@ call_1(#c_call{anno=Anno}, lists, foreach, [Arg1,Arg2], Sub) ->
C1 = #c_clause{pats=[#c_cons{hd=X, tl=Xs}], guard=#c_literal{val=true},
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},
+ C2 = #c_clause{pats=[#c_literal{val=[]}],
+ guard=#c_call{module=#c_literal{val=erlang},
+ name=#c_literal{val=is_function},
+ args=[F, #c_literal{val=1}]},
body=#c_literal{val=ok}},
- Err = #c_tuple{es=[#c_literal{val='function_clause'}, Xs]},
+ Err = #c_tuple{es=[#c_literal{val='function_clause'}, F, Xs]},
C3 = #c_clause{pats=[Xs], guard=#c_literal{val=true},
- body=match_fail(Anno, Err)},
+ body=match_fail([{function_name,{'lists^foreach',1}}|Anno], Err)},
Fun = #c_fun{vars=[Xs],
body=#c_case{arg=Xs, clauses=[C1, C2, C3]}},
L = #c_var{name='L'},
@@ -756,14 +765,18 @@ call_1(#c_call{anno=Anno}, lists, map, [Arg1,Arg2], Sub) ->
op=F,
args=[X]},
body=#c_cons{hd=H,
+ anno=[compiler_generated],
tl=#c_apply{anno=Anno,
op=Loop,
args=[Xs]}}}},
- C2 = #c_clause{pats=[#c_literal{val=[]}], guard=#c_literal{val=true},
+ C2 = #c_clause{pats=[#c_literal{val=[]}],
+ guard=#c_call{module=#c_literal{val=erlang},
+ name=#c_literal{val=is_function},
+ args=[F, #c_literal{val=1}]},
body=#c_literal{val=[]}},
- Err = #c_tuple{es=[#c_literal{val='function_clause'}, Xs]},
+ Err = #c_tuple{es=[#c_literal{val='function_clause'}, F, Xs]},
C3 = #c_clause{pats=[Xs], guard=#c_literal{val=true},
- body=match_fail(Anno, Err)},
+ body=match_fail([{function_name,{'lists^map',1}}|Anno], Err)},
Fun = #c_fun{vars=[Xs],
body=#c_case{arg=Xs, clauses=[C1, C2, C3]}},
L = #c_var{name='L'},
@@ -780,18 +793,21 @@ call_1(#c_call{anno=Anno}, lists, flatmap, [Arg1,Arg2], Sub) ->
C1 = #c_clause{pats=[#c_cons{hd=X, tl=Xs}], guard=#c_literal{val=true},
body=#c_let{vars=[H],
arg=#c_apply{anno=Anno, op=F, args=[X]},
- body=#c_call{anno=Anno,
+ body=#c_call{anno=[compiler_generated|Anno],
module=#c_literal{val=erlang},
name=#c_literal{val='++'},
args=[H,
#c_apply{anno=Anno,
op=Loop,
args=[Xs]}]}}},
- C2 = #c_clause{pats=[#c_literal{val=[]}], guard=#c_literal{val=true},
+ C2 = #c_clause{pats=[#c_literal{val=[]}],
+ guard=#c_call{module=#c_literal{val=erlang},
+ name=#c_literal{val=is_function},
+ args=[F, #c_literal{val=1}]},
body=#c_literal{val=[]}},
- Err = #c_tuple{es=[#c_literal{val='function_clause'}, Xs]},
+ Err = #c_tuple{es=[#c_literal{val='function_clause'}, F, Xs]},
C3 = #c_clause{pats=[Xs], guard=#c_literal{val=true},
- body=match_fail(Anno, Err)},
+ body=match_fail([{function_name,{'lists^flatmap',1}}|Anno], Err)},
Fun = #c_fun{vars=[Xs],
body=#c_case{arg=Xs, clauses=[C1, C2, C3]}},
L = #c_var{name='L'},
@@ -807,7 +823,7 @@ call_1(#c_call{anno=Anno}, lists, filter, [Arg1,Arg2], Sub) ->
B = #c_var{name='B'},
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_cons{hd=X, tl=Xs}},
+ body=#c_cons{anno=[compiler_generated], hd=X, tl=Xs}},
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},
@@ -821,11 +837,14 @@ call_1(#c_call{anno=Anno}, lists, filter, [Arg1,Arg2], Sub) ->
op=Loop,
args=[Xs]},
body=Case}}},
- C2 = #c_clause{pats=[#c_literal{val=[]}], guard=#c_literal{val=true},
+ C2 = #c_clause{pats=[#c_literal{val=[]}],
+ guard=#c_call{module=#c_literal{val=erlang},
+ name=#c_literal{val=is_function},
+ args=[F, #c_literal{val=1}]},
body=#c_literal{val=[]}},
- Err2 = #c_tuple{es=[#c_literal{val='function_clause'}, Xs]},
+ Err2 = #c_tuple{es=[#c_literal{val='function_clause'}, F, Xs]},
C3 = #c_clause{pats=[Xs], guard=#c_literal{val=true},
- body=match_fail(Anno, Err2)},
+ body=match_fail([{function_name,{'lists^filter',1}}|Anno], Err2)},
Fun = #c_fun{vars=[Xs],
body=#c_case{arg=Xs, clauses=[C1, C2, C3]}},
L = #c_var{name='L'},
@@ -845,10 +864,14 @@ call_1(#c_call{anno=Anno}, lists, foldl, [Arg1,Arg2,Arg3], Sub) ->
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]},
+ C2 = #c_clause{pats=[#c_literal{val=[]}],
+ guard=#c_call{module=#c_literal{val=erlang},
+ name=#c_literal{val=is_function},
+ args=[F, #c_literal{val=2}]},
+ body=A},
+ Err = #c_tuple{es=[#c_literal{val='function_clause'}, F, A, Xs]},
C3 = #c_clause{pats=[Xs], guard=#c_literal{val=true},
- body=match_fail(Anno, Err)},
+ body=match_fail([{function_name,{'lists^foldl',2}}|Anno], Err)},
Fun = #c_fun{vars=[Xs, A],
body=#c_case{arg=Xs, clauses=[C1, C2, C3]}},
L = #c_var{name='L'},
@@ -868,10 +891,14 @@ call_1(#c_call{anno=Anno}, lists, foldr, [Arg1,Arg2,Arg3], Sub) ->
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]},
+ C2 = #c_clause{pats=[#c_literal{val=[]}],
+ guard=#c_call{module=#c_literal{val=erlang},
+ name=#c_literal{val=is_function},
+ args=[F, #c_literal{val=2}]},
+ body=A},
+ Err = #c_tuple{es=[#c_literal{val='function_clause'}, F, A, Xs]},
C3 = #c_clause{pats=[Xs], guard=#c_literal{val=true},
- body=match_fail(Anno, Err)},
+ body=match_fail([{function_name,{'lists^foldr',2}}|Anno], Err)},
Fun = #c_fun{vars=[Xs, A],
body=#c_case{arg=Xs, clauses=[C1, C2, C3]}},
L = #c_var{name='L'},
@@ -901,7 +928,10 @@ call_1(#c_call{anno=Anno}, lists, mapfoldl, [Arg1,Arg2,Arg3], Sub) ->
op=Loop,
args=[Xs, Avar]},
#c_tuple{es=[Xs, Avar]},
- #c_tuple{es=[#c_cons{hd=X, tl=Xs}, Avar]})
+ #c_tuple{anno=[compiler_generated],
+ es=[#c_cons{anno=[compiler_generated],
+ hd=X, tl=Xs},
+ Avar]})
%%% Multiple-value version
%%% #c_let{vars=[Xs,A],
%%% %% The tuple here will be optimised
@@ -910,14 +940,18 @@ call_1(#c_call{anno=Anno}, lists, mapfoldl, [Arg1,Arg2,Arg3], Sub) ->
%%% body=#c_values{es=[#c_cons{hd=X, tl=Xs},
%%% A]}}
)},
- C2 = #c_clause{pats=[#c_literal{val=[]}], guard=#c_literal{val=true},
+ C2 = #c_clause{pats=[#c_literal{val=[]}],
+ guard=#c_call{module=#c_literal{val=erlang},
+ name=#c_literal{val=is_function},
+ args=[F, #c_literal{val=2}]},
%%% Tuple passing version
- body=#c_tuple{es=[#c_literal{val=[]}, Avar]}},
+ body=#c_tuple{anno=[compiler_generated],
+ es=[#c_literal{val=[]}, Avar]}},
%%% Multiple-value version
%%% body=#c_values{es=[#c_literal{val=[]}, A]}},
- Err = #c_tuple{es=[#c_literal{val='function_clause'}, Xs]},
+ Err = #c_tuple{es=[#c_literal{val='function_clause'}, F, Avar, Xs]},
C3 = #c_clause{pats=[Xs], guard=#c_literal{val=true},
- body=match_fail(Anno, Err)},
+ body=match_fail([{function_name,{'lists^mapfoldl',2}}|Anno], Err)},
Fun = #c_fun{vars=[Xs, Avar],
body=#c_case{arg=Xs, clauses=[C1, C2, C3]}},
L = #c_var{name='L'},
@@ -955,7 +989,9 @@ call_1(#c_call{anno=Anno}, lists, mapfoldr, [Arg1,Arg2,Arg3], Sub) ->
#c_tuple{es=[Xs, 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]}))
+ #c_tuple{anno=[compiler_generated],
+ es=[#c_cons{anno=[compiler_generated],
+ hd=X, tl=Xs}, Avar]}))
%%% Multiple-value version
%%% body=#c_let{vars=[Xs,A],
%%% %% The tuple will be optimised away
@@ -965,14 +1001,18 @@ call_1(#c_call{anno=Anno}, lists, mapfoldr, [Arg1,Arg2,Arg3], Sub) ->
%%% #c_values{es=[#c_cons{hd=X, tl=Xs},
%%% A]})}
},
- C2 = #c_clause{pats=[#c_literal{val=[]}], guard=#c_literal{val=true},
+ C2 = #c_clause{pats=[#c_literal{val=[]}],
+ guard=#c_call{module=#c_literal{val=erlang},
+ name=#c_literal{val=is_function},
+ args=[F, #c_literal{val=2}]},
%%% Tuple passing version
- body=#c_tuple{es=[#c_literal{val=[]}, Avar]}},
+ body=#c_tuple{anno=[compiler_generated],
+ es=[#c_literal{val=[]}, Avar]}},
%%% Multiple-value version
%%% body=#c_values{es=[#c_literal{val=[]}, A]}},
- Err = #c_tuple{es=[#c_literal{val='function_clause'}, Xs]},
+ Err = #c_tuple{es=[#c_literal{val='function_clause'}, F, Avar, Xs]},
C3 = #c_clause{pats=[Xs], guard=#c_literal{val=true},
- body=match_fail(Anno, Err)},
+ body=match_fail([{function_name,{'lists^mapfoldr',2}}|Anno], Err)},
Fun = #c_fun{vars=[Xs, Avar],
body=#c_case{arg=Xs, clauses=[C1, C2, C3]}},
L = #c_var{name='L'},
@@ -2632,16 +2672,19 @@ bsm_nonempty([#c_clause{pats=Ps}|Cs], Pos) ->
bsm_nonempty([], _ ) -> false.
%% bsm_ensure_no_partition(Cs, Pos) -> ok (exception if problem)
-%% We must make sure that binary matching is not partitioned between
+%% We must make sure that matching is not partitioned between
%% variables like this:
%% foo(<<...>>) -> ...
-%% foo(Var) when ... -> ...
-%% foo(<<...>>) ->
+%% foo(<Variable>) when ... -> ...
+%% foo(<Any non-variable pattern>) ->
%% If there is such partition, we are not allowed to reuse the binary variable
-%% for the match context. Also, arguments to the left of the argument that
-%% is matched against a binary, are only allowed to be simple variables, not
-%% used in guards. The reason is that we must know that the binary is only
-%% matched in one place.
+%% for the match context.
+%%
+%% Also, arguments to the left of the argument that is matched
+%% against a binary, are only allowed to be simple variables, not
+%% used in guards. The reason is that we must know that the binary is
+%% only matched in one place (i.e. there must be only one bs_start_match2
+%% instruction emitted).
bsm_ensure_no_partition(Cs, Pos) ->
bsm_ensure_no_partition_1(Cs, Pos, before).
@@ -2649,6 +2692,12 @@ bsm_ensure_no_partition(Cs, Pos) ->
%% Loop through each clause.
bsm_ensure_no_partition_1([#c_clause{pats=Ps,guard=G}|Cs], Pos, State0) ->
State = bsm_ensure_no_partition_2(Ps, Pos, G, simple_vars, State0),
+ case State of
+ 'after' ->
+ bsm_ensure_no_partition_after(Cs, Pos);
+ _ ->
+ ok
+ end,
bsm_ensure_no_partition_1(Cs, Pos, State);
bsm_ensure_no_partition_1([], _, _) -> ok.
@@ -2658,8 +2707,7 @@ bsm_ensure_no_partition_2([#c_binary{}=Where|_], 1, _, Vstate, State) ->
before when Vstate =:= simple_vars -> within;
before -> bsm_problem(Where, Vstate);
within when Vstate =:= simple_vars -> within;
- within -> bsm_problem(Where, Vstate);
- 'after' -> bsm_problem(Where, bin_partition)
+ within -> bsm_problem(Where, Vstate)
end;
bsm_ensure_no_partition_2([#c_alias{}=Alias|_], 1, N, Vstate, State) ->
%% Retrieve the real pattern that the alias refers to and check that.
@@ -2708,6 +2756,15 @@ bsm_ensure_no_partition_2([#c_var{name=V}|Ps], N, G, Vstate, S) ->
bsm_ensure_no_partition_2([_|Ps], N, G, _, S) ->
bsm_ensure_no_partition_2(Ps, N-1, G, bin_argument_order, S).
+bsm_ensure_no_partition_after([#c_clause{pats=Ps}|Cs], Pos) ->
+ case nth(Pos, Ps) of
+ #c_var{} ->
+ bsm_ensure_no_partition_after(Cs, Pos);
+ P ->
+ bsm_problem(P, bin_partition)
+ end;
+bsm_ensure_no_partition_after([], _) -> ok.
+
bsm_could_match_binary(#c_alias{pat=P}) -> bsm_could_match_binary(P);
bsm_could_match_binary(#c_cons{}) -> false;
bsm_could_match_binary(#c_tuple{}) -> false;
@@ -2832,7 +2889,7 @@ format_error(useless_building) ->
format_error(bin_opt_alias) ->
"INFO: the '=' operator will prevent delayed sub binary optimization";
format_error(bin_partition) ->
- "INFO: non-consecutive clauses that match binaries "
+ "INFO: matching non-variables after a previous clause matching a variable "
"will prevent delayed sub binary optimization";
format_error(bin_left_var_used_in_guard) ->
"INFO: a variable to the left of the binary pattern is used in a guard; "
diff --git a/lib/compiler/src/sys_expand_pmod.erl b/lib/compiler/src/sys_expand_pmod.erl
deleted file mode 100644
index da644b4f0b..0000000000
--- a/lib/compiler/src/sys_expand_pmod.erl
+++ /dev/null
@@ -1,433 +0,0 @@
-%%
-%% %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(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 automatic '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) when F =/= new ->
- case ordsets:is_element(E, St#pmod.predef) of
- true -> E;
- false -> {F, A + 1}
- end;
-update_function_name(E, _St) ->
- E.
-
-update_forms([{function,L,N,A,Cs}|Fs],St) when N =/= new ->
- [{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) when Name0 =/= new ->
- {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}=Var,_St) -> Var;
-pattern({match,Line,L0,R0},St) ->
- L1 = pattern(L0,St),
- R1 = pattern(R0,St),
- {match,Line,L1,R1};
-pattern({integer,_Line,_I}=Integer,_St) -> Integer;
-pattern({char,_Line,_C}=Char,_St) -> Char;
-pattern({float,_Line,_F}=Float,_St) -> Float;
-pattern({atom,_Line,_A}=Atom,_St) -> Atom;
-pattern({string,_Line,_S}=String,_St) -> String;
-pattern({nil,_Line}=Nil,_St) -> Nil;
-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}=Char,T},R},St) ->
- pattern({cons,Li,Char,{op,Li,'++',T,R}},St);
-pattern({op,_Line,'++',{cons,Li,{integer,_L2,_I}=Integer,T},R},St) ->
- pattern({cons,Li,Integer,{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}=Op4,_St) -> Op4;
-pattern({op,_Line,_Op,_L,_R}=Op5,_St) -> Op5.
-
-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 is_atom(Atom) ->
- [Atom | bit_types(Rest)];
-bit_types([{Atom, Integer} | Rest]) when is_atom(Atom), is_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 is_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}=Var,_St) -> Var;
-% %% 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
-% end;
-gexpr({integer,_Line,_I}=Integer,_St) -> Integer;
-gexpr({char,_Line,_C}=Char,_St) -> Char;
-gexpr({float,_Line,_F}=Float,_St) -> Float;
-gexpr({atom,_Line,_A}=Atom,_St) -> Atom;
-gexpr({string,_Line,_S}=String,_St) -> String;
-gexpr({nil,_Line}=Nil,_St) -> Nil;
-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}=Atom,As0},St) ->
- true = erl_internal:guard_bif(F, length(As0)),
- As1 = gexpr_list(As0,St),
- {call,Line,Atom,As1};
-%% 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) ->
- As1 = gexpr_list(As0,St),
- {call,Line,{remote,La,{atom,Lb,erlang},{atom,Lc,is_record}},As1};
-%% Guard BIFs can be remote, but only in the module erlang...
-gexpr({call,Line,{remote,La,{atom,Lb,erlang},{atom,Lc,F}},As0},St) ->
- A = length(As0),
- true =
- erl_internal:guard_bif(F, A) orelse erl_internal:arith_op(F, A) orelse
- erl_internal:comp_op(F, A) orelse erl_internal:bool_op(F, A),
- As1 = gexpr_list(As0,St),
- {call,Line,{remote,La,{atom,Lb,erlang},{atom,Lc,F}},As1};
-%% Unfortunately, writing calls as {M,F}(...) is also allowed.
-gexpr({call,Line,{tuple,La,[{atom,Lb,erlang},{atom,Lc,F}]},As0},St) ->
- A = length(As0),
- true =
- erl_internal:guard_bif(F, A) orelse erl_internal:arith_op(F, A) orelse
- erl_internal:comp_op(F, A) orelse erl_internal:bool_op(F, A),
- As1 = gexpr_list(As0,St),
- {call,Line,{tuple,La,[{atom,Lb,erlang},{atom,Lc,F}]},As1};
-gexpr({bin,Line,Fs},St) ->
- Fs2 = pattern_grp(Fs,St),
- {bin,Line,Fs2};
-gexpr({op,Line,Op,A0},St) ->
- true = erl_internal:arith_op(Op, 1) orelse erl_internal:bool_op(Op, 1),
- A1 = gexpr(A0,St),
- {op,Line,Op,A1};
-gexpr({op,Line,Op,L0,R0},St) ->
- true =
- Op =:= 'andalso' orelse Op =:= 'orelse' orelse
- erl_internal:arith_op(Op, 2) orelse
- erl_internal:bool_op(Op, 2) orelse erl_internal:comp_op(Op, 2),
- L1 = gexpr(L0,St),
- R1 = gexpr(R0,St),
- {op,Line,Op,L1,R1}.
-
-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}=Var,_St) ->
- Var;
-% 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
-% end;
-expr({integer,_Line,_I}=Integer,_St) -> Integer;
-expr({float,_Line,_F}=Float,_St) -> Float;
-expr({atom,_Line,_A}=Atom,_St) -> Atom;
-expr({string,_Line,_S}=String,_St) -> String;
-expr({char,_Line,_C}=Char,_St) -> Char;
-expr({nil,_Line}=Nil,_St) -> Nil;
-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_bc_quals(Qs0,St),
- E1 = expr(E0,St),
- {lc,Line,E1,Qs1};
-expr({bc,Line,E0,Qs0},St) ->
- Qs1 = lc_bc_quals(Qs0,St),
- E1 = expr(E0,St),
- {bc,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',_,{function,_,_,_}}=ExtFun,_St) ->
- ExtFun;
-expr({'fun',Line,Body,Info},St) ->
- case Body of
- {clauses,Cs0} ->
- Cs1 = fun_clauses(Cs0,St),
- {'fun',Line,{clauses,Cs1},Info};
- {function,F,A} = Function ->
- {F1,A1} = update_function_name({F,A},St),
- if A1 =:= A ->
- {'fun',Line,Function,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} = Fun4 -> %This is an error in lint!
- {'fun',Line,Fun4,Info}
- end;
-expr({call,Lc,{atom,_,instance}=Name,As0},St) ->
- %% All local functions 'instance(...)' are static by definition,
- %% so they do not take a 'THIS' argument when called
- As1 = expr_list(As0,St),
- {call,Lc,Name,As1};
-expr({call,Lc,{atom,_,new}=Name,As0},St) ->
- %% All local functions 'new(...)' are static by definition,
- %% so they do not take a 'THIS' argument when called
- 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}=Atom,As0},St) ->
- %% Local function call - needs THIS parameter.
- As1 = expr_list(As0,St),
- {call,Lc,Atom,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_bc_quals([{generate,Line,P0,E0}|Qs],St) ->
- E1 = expr(E0,St),
- P1 = pattern(P0,St),
- [{generate,Line,P1,E1}|lc_bc_quals(Qs,St)];
-lc_bc_quals([{b_generate,Line,P0,E0}|Qs],St) ->
- E1 = expr(E0,St),
- P1 = pattern(P0,St),
- [{b_generate,Line,P1,E1}|lc_bc_quals(Qs,St)];
-lc_bc_quals([E0|Qs],St) ->
- E1 = expr(E0,St),
- [E1|lc_bc_quals(Qs,St)];
-lc_bc_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/compiler/src/sys_pre_expand.erl b/lib/compiler/src/sys_pre_expand.erl
index 68bc83433e..7d918a55ed 100644
--- a/lib/compiler/src/sys_pre_expand.erl
+++ b/lib/compiler/src/sys_pre_expand.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -28,17 +28,14 @@
%% Main entry point.
-export([module/2]).
--import(ordsets, [from_list/1,add_element/2,union/2]).
+-import(ordsets, [from_list/1,union/2]).
-import(lists, [member/2,foldl/3,foldr/3]).
-include("../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
attributes=[], %Attributes
callbacks=[], %Callbacks
@@ -67,12 +64,8 @@ module(Fs0, Opts0) ->
%% 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(),
@@ -80,88 +73,20 @@ module(Fs0, Opts0) ->
},
%% Expand the functions.
{Tfs,St1} = forms(Fs, define_functions(Fs, St0)),
- {Efs,St2} = expand_pmod(Tfs, St1),
%% Get the correct list of exported functions.
- Exports = case member(export_all, St2#expand.compile) of
- true -> gb_sets:to_list(St2#expand.defined);
- false -> St2#expand.exports
+ Exports = case member(export_all, St1#expand.compile) of
+ true -> gb_sets:to_list(St1#expand.defined);
+ false -> St1#expand.exports
end,
%% Generate all functions from stored info.
- {Ats,St3} = module_attrs(St2#expand{exports = Exports}),
+ {Ats,St3} = module_attrs(St1#expand{exports = Exports}),
{Mfs,St4} = module_predef_funcs(St3),
- {St4#expand.module, St4#expand.exports, Ats ++ Efs ++ Mfs,
+ {St4#expand.module, St4#expand.exports, Ats ++ Tfs ++ Mfs,
St4#expand.compile}.
compiler_options(Forms) ->
lists:flatten([C || {attribute,_,compile,C} <- Forms]).
-expand_pmod(Fs0, St0) ->
- case St0#expand.parameters of
- undefined ->
- {Fs0,St0};
- Ps0 ->
- Base = get_base(St0#expand.attributes),
- Ps = if is_atom(Base) ->
- ['BASE' | Ps0];
- true ->
- Ps0
- end,
- Def = gb_sets:to_list(St0#expand.defined),
- {Fs1,Xs,Ds} = sys_expand_pmod:forms(Fs0, Ps,
- St0#expand.exports,
- Def),
- St1 = St0#expand{exports=Xs,defined=gb_sets:from_list(Ds)},
- {Fs2,St2} = add_instance(Ps, Fs1, St1),
- {Fs3,St3} = ensure_new(Base, Ps0, Fs2, St2),
- {Fs3,St3#expand{attributes = [{abstract, 0, [true]}
- | St3#expand.attributes]}}
- end.
-
-get_base(As) ->
- case lists:keyfind(extends, 1, As) of
- {extends,[Base]} when is_atom(Base) ->
- Base;
- _ ->
- []
- end.
-
-ensure_new(Base, Ps, Fs, St) ->
- case has_new(Fs) of
- true ->
- {Fs, St};
- false ->
- add_new(Base, Ps, Fs, St)
- end.
-
-has_new([{function,_L,new,_A,_Cs} | _Fs]) ->
- true;
-has_new([_ | Fs]) ->
- has_new(Fs);
-has_new([]) ->
- false.
-
-add_new(Base, Ps, Fs, St) ->
- Vs = [{var,0,V} || V <- Ps],
- As = if is_atom(Base) ->
- [{call,0,{remote,0,{atom,0,Base},{atom,0,new}},Vs} | Vs];
- true ->
- Vs
- end,
- Body = [{call,0,{atom,0,instance},As}],
- add_func(new, Vs, Body, Fs, St).
-
-add_instance(Ps, Fs, St) ->
- Vs = [{var,0,V} || V <- Ps],
- AbsMod = [{tuple,0,[{atom,0,St#expand.module}|Vs]}],
- add_func(instance, Vs, AbsMod, Fs, St).
-
-add_func(Name, Args, Body, Fs, St) ->
- A = length(Args),
- F = {function,0,Name,A,[{clause,0,Args,[],Body}]},
- NA = {Name,A},
- {[F|Fs],St#expand{exports=add_element(NA, St#expand.exports),
- defined=gb_sets:add_element(NA, St#expand.defined)}}.
-
%% define_function(Form, State) -> State.
%% Add function to defined if form is a function.
@@ -241,15 +166,9 @@ forms([], St) -> {[],St}.
%% attribute(Attribute, Value, Line, State) -> State'.
%% Process an attribute, this just affects the state.
-attribute(module, {Module, As}, _L, St) ->
- M = package_to_string(Module),
- St#expand{module=list_to_atom(M),
- package=packages:strip_last(M),
- parameters=As};
attribute(module, Module, _L, St) ->
- M = package_to_string(Module),
- St#expand{module=list_to_atom(M),
- package=packages:strip_last(M)};
+ true = is_atom(Module),
+ St#expand{module=Module};
attribute(export, Es, _L, St) ->
St#expand{exports=union(from_list(Es), St#expand.exports)};
attribute(import, Is, _L, St) ->
@@ -312,8 +231,6 @@ pattern({tuple,Line,Ps}, St0) ->
%%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({bin,Line,Es0}, St0) ->
{Es1,St1} = pattern_bin(Es0, St0),
{{bin,Line,Es1},St1};
@@ -404,8 +321,6 @@ expr({tuple,Line,Es0}, St0) ->
%%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, St) ->
- {expand_package(M, St),St}; % must be a package name
expr({bin,Line,Es0}, St0) ->
{Es1,St1} = expr_bin(Es0, St0),
{{bin,Line,Es1},St1};
@@ -448,15 +363,9 @@ expr({call,Line,{atom,La,N}=Atom,As0}, St0) ->
end
end
end;
-expr({call,Line,{record_field,_,_,_}=M,As0}, St0) ->
- expr({call,Line,expand_package(M, St0),As0}, St0);
-expr({call,Line,{remote,Lr,M,F},As0}, St0) ->
- M1 = expand_package(M, St0),
- {[M2,F1|As1],St1} = expr_list([M1,F|As0], St0),
- {{call,Line,{remote,Lr,M2,F1},As1},St1};
-expr({call,Line,{tuple,_,[{atom,_,_}=M,{atom,_,_}=F]},As}, St) ->
- %% Rewrite {Mod,Function}(Args...) to Mod:Function(Args...).
- expr({call,Line,{remote,Line,M,F},As}, St);
+expr({call,Line,{remote,Lr,M0,F},As0}, St0) ->
+ {[M1,F1|As1],St1} = expr_list([M0,F|As0], St0),
+ {{call,Line,{remote,Lr,M1,F1},As1},St1};
expr({call,Line,F,As0}, St0) ->
{[Fun1|As1],St1} = expr_list([F|As0], St0),
{{call,Line,Fun1,As1},St1};
@@ -669,32 +578,6 @@ 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 is_atom(A) -> atom_to_list(A);
-package_to_string(L) when is_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.
-
%% import(Line, Imports, State) ->
%% State'
%% imported(Name, Arity, State) ->
@@ -702,15 +585,10 @@ expand_package(M, _St) ->
%% Handle import declarations and test 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)),
+import({Mod,Fs}, St) ->
+ true = is_atom(Mod),
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)}.
+ St#expand{imports=add_imports(Mod, Mfs, St#expand.imports)}.
add_imports(Mod, [F|Fs], Is) ->
add_imports(Mod, Fs, orddict:store(F, Mod, Is));
diff --git a/lib/compiler/src/v3_codegen.erl b/lib/compiler/src/v3_codegen.erl
index be15495672..6a13495523 100644
--- a/lib/compiler/src/v3_codegen.erl
+++ b/lib/compiler/src/v3_codegen.erl
@@ -123,15 +123,24 @@ cg_fun(Les, Hvs, Vdb, AtomMod, NameArity, Anno, St0) ->
put_reg(V, Reg)
end, [], Hvs),
stk=[]}, 0, Vdb),
- {B,_Aft,St} = cg_list(Les, 0, Vdb, Bef,
+ {B0,_Aft,St} = cg_list(Les, 0, Vdb, Bef,
St3#cg{bfail=0,
ultimate_failure=UltimateMatchFail,
is_top_block=true}),
+ B = fix_bs_match_strings(B0),
{Name,Arity} = NameArity,
Asm = [{label,Fi},line(Anno),{func_info,AtomMod,{atom,Name},Arity},
{label,Fl}|B++[{label,UltimateMatchFail},if_end]],
{Asm,Fl,St}.
+fix_bs_match_strings([{test,bs_match_string,F,[Ctx,BinList]}|Is])
+ when is_list(BinList) ->
+ I = {test,bs_match_string,F,[Ctx,list_to_bitstring(BinList)]},
+ [I|fix_bs_match_strings(Is)];
+fix_bs_match_strings([I|Is]) ->
+ [I|fix_bs_match_strings(Is)];
+fix_bs_match_strings([]) -> [].
+
%% cg(Lkexpr, Vdb, StackReg, State) -> {[Ainstr],StackReg,State}.
%% Generate code for a kexpr.
%% Split function into two steps for clarity, not efficiency.
@@ -370,6 +379,7 @@ bsm_rename_ctx(#l{ke={test,_,_}}=L, _, _, _) -> L;
bsm_rename_ctx(#l{ke={bif,_,_,_}}=L, _, _, _) -> L;
bsm_rename_ctx(#l{ke={gc_bif,_,_,_}}=L, _, _, _) -> L;
bsm_rename_ctx(#l{ke={set,_,_}}=L, _, _, _) -> L;
+bsm_rename_ctx(#l{ke={call,_,_,_}}=L, _, _, _) -> L;
bsm_rename_ctx(#l{ke={block,_}}=L, Old, _, false) ->
%% This block is not inside a protected. The match context variable cannot
%% possibly be live inside the block.
@@ -713,7 +723,22 @@ select_bin_seg(#l{ke={val_clause,{bin_int,Ctx,Sz,U,Fs,Val,Es},B},i=I,vdb=Vdb},
I, Vdb, Bef, Ctx, St0),
{Bis,Aft,St2} = match_cg(B, Fail, Int, St1),
CtxReg = fetch_var(Ctx, Bef),
- {[{bs_restore2,CtxReg,{Ctx,Ivar}}|Mis] ++ Bis,Aft,St2}.
+ Is = case Mis ++ Bis of
+ [{test,bs_match_string,F,[OtherCtx,Bin1]},
+ {bs_save2,OtherCtx,_},
+ {bs_restore2,OtherCtx,_},
+ {test,bs_match_string,F,[OtherCtx,Bin2]}|Is0] ->
+ %% We used to do this optimization later, but it
+ %% turns out that in huge functions with many
+ %% bs_match_string instructions, it's a big win
+ %% to do the combination now. To avoid copying the
+ %% binary data again and again, we'll combine bitstrings
+ %% in a list and convert all of it to a bitstring later.
+ [{test,bs_match_string,F,[OtherCtx,[Bin1,Bin2]]}|Is0];
+ Is0 ->
+ Is0
+ end,
+ {[{bs_restore2,CtxReg,{Ctx,Ivar}}|Is],Aft,St2}.
select_extract_int([{var,Tl}], Val, {integer,Sz}, U, Fs, Vf,
I, Vdb, Bef, Ctx, St) ->
@@ -1385,22 +1410,32 @@ catch_cg(C, {var,R}, Le, Vdb, Bef, St0) ->
%%
%% 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),
+ [S1,S2] = cg_reg_args(Es, Bef),
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}], {binary,Segs}, Le, Vdb, Bef,
#cg{in_catch=InCatch, bfail=Bfail}=St) ->
+ %% At run-time, binaries are constructed in three stages:
+ %% 1) First the size of the binary is calculated.
+ %% 2) Then the binary is allocated.
+ %% 3) Then each field in the binary is constructed.
+ %% For simplicity, we use the target register to also hold the
+ %% size of the binary. Therefore the target register must *not*
+ %% be one of the source registers.
+
+ %% First allocate the target register.
Int0 = Bef#sr{reg=put_reg(R, Bef#sr.reg)},
Target = fetch_reg(R, Int0#sr.reg),
- Fail = {f,Bfail},
+
+ %% Also allocate a scratch register for size calculations.
Temp = find_scratch_reg(Int0#sr.reg),
+
+ %% First generate the code that constructs each field.
+ Fail = {f,Bfail},
PutCode = cg_bin_put(Segs, Fail, Bef),
{Sis,Int1} =
case InCatch of
@@ -1409,6 +1444,8 @@ set_cg([{var,R}], {binary,Segs}, Le, Vdb, Bef,
end,
MaxRegs = max_reg(Bef#sr.reg),
Aft = clear_dead(Int1, Le#l.i, Vdb),
+
+ %% Now generate the complete code for constructing the binary.
Code = cg_binary(PutCode, Target, Temp, Fail, MaxRegs, Le#l.a),
{Sis++Code,Aft,St};
set_cg([{var,R}], Con, Le, Vdb, Bef, St) ->
@@ -1418,10 +1455,8 @@ set_cg([{var,R}], Con, Le, Vdb, Bef, St) ->
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}];
Other ->
- [{move,Other,Ret}]
+ [{move,cg_reg_arg(Other, Int),Ret}]
end,
{Ais,clear_dead(Int, Le#l.i, Vdb),St}.
@@ -1575,8 +1610,7 @@ cg_gen_binsize([], _, _, _, _, Acc) -> Acc.
%% cg_bin_opt(Code0) -> Code
%% Optimize the size calculations for binary construction.
-cg_bin_opt([{move,Size,D},{bs_append,Fail,D,Extra,Regs0,U,Bin,Flags,D}|Is]) ->
- Regs = cg_bo_newregs(Regs0, D),
+cg_bin_opt([{move,Size,D},{bs_append,Fail,D,Extra,Regs,U,Bin,Flags,D}|Is]) ->
cg_bin_opt([{bs_append,Fail,Size,Extra,Regs,U,Bin,Flags,D}|Is]);
cg_bin_opt([{move,Size,D},{bs_private_append,Fail,D,U,Bin,Flags,D}|Is]) ->
cg_bin_opt([{bs_private_append,Fail,Size,U,Bin,Flags,D}|Is]);
@@ -1584,9 +1618,8 @@ 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},{Op,Fail,D,Extra,Regs0,Flags,D}|Is])
+cg_bin_opt([{move,{integer,Bytes},D},{Op,Fail,D,Extra,Regs,Flags,D}|Is])
when Op =:= bs_init2; Op =:= bs_init_bits ->
- Regs = cg_bo_newregs(Regs0, D),
cg_bin_opt([{Op,Fail,Bytes,Extra,Regs,Flags,D}|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]);
@@ -1594,20 +1627,9 @@ 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,
+ S1 = cg_reg_arg(S0, Bef),
+ E1 = cg_reg_arg(E0, Bef),
{Format,Op} = case T of
integer -> {plain,bs_put_integer};
utf8 -> {utf,bs_put_utf8};
@@ -1625,9 +1647,7 @@ cg_bin_put({bin_seg,[],S0,U,T,Fs,[E0,Next]}, Fail, Bef) ->
cg_bin_put({bin_end,[]}, _, _) -> [].
cg_build_args(As, Bef) ->
- map(fun ({var,V}) -> {put,fetch_var(V, Bef)};
- (Other) -> {put,Other}
- end, As).
+ [{put,cg_reg_arg(A, Bef)} || A <- As].
%% return_cg([Val], Le, Vdb, Bef, St) -> {[Ainstr],Aft,St}.
%% break_cg([Val], Le, Vdb, Bef, St) -> {[Ainstr],Aft,St}.
@@ -1906,27 +1926,13 @@ fetch_var(V, Sr) ->
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.
-%% 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).
@@ -1937,10 +1943,6 @@ 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}].
-% 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).
@@ -1957,9 +1959,6 @@ 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)).
-
replace_reg_contents(Old, New, [{I,Old}|Rs]) -> [{I,New}|Rs];
replace_reg_contents(Old, New, [R|Rs]) -> [R|replace_reg_contents(Old, New, Rs)].
diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl
index 242196c593..01042cc56f 100644
--- a/lib/compiler/src/v3_core.erl
+++ b/lib/compiler/src/v3_core.erl
@@ -823,6 +823,13 @@ bitstr({bin_element,_,E0,Size0,[Type,{unit,Unit}|Flags]}, St0) ->
{_,_} ->
throw(bad_binary)
end,
+ case Size1 of
+ #c_var{} -> ok;
+ #c_literal{val=Sz} when is_integer(Sz), Sz >= 0 -> ok;
+ #c_literal{val=undefined} -> ok;
+ #c_literal{val=all} -> ok;
+ _ -> throw(bad_binary)
+ end,
{#c_bitstr{val=E1,size=Size1,
unit=#c_literal{val=Unit},
type=#c_literal{val=Type},
diff --git a/lib/compiler/src/v3_kernel.erl b/lib/compiler/src/v3_kernel.erl
index c4e7b45aac..8ef71e1346 100644
--- a/lib/compiler/src/v3_kernel.erl
+++ b/lib/compiler/src/v3_kernel.erl
@@ -81,7 +81,7 @@
-export([module/2,format_error/1]).
-import(lists, [map/2,foldl/3,foldr/3,mapfoldl/3,splitwith/2,member/2,
- keymember/3,keyfind/3]).
+ keymember/3,keyfind/3,partition/2]).
-import(ordsets, [add_element/2,del_element/2,union/2,union/1,subtract/2]).
-import(cerl, [c_tuple/1]).
@@ -278,11 +278,12 @@ expr(#c_binary{anno=A,segments=Cv}, Sub, St0) ->
{#k_binary{anno=A,segs=Kv},Ep,St1}
catch
throw:bad_element_size ->
+ St1 = add_warning(get_line(A), bad_segment_size, A, St0),
Erl = #c_literal{val=erlang},
Name = #c_literal{val=error},
Args = [#c_literal{val=badarg}],
Error = #c_call{anno=A,module=Erl,name=Name,args=Args},
- expr(Error, Sub, St0)
+ expr(Error, Sub, St1)
end;
expr(#c_fun{anno=A,vars=Cvs,body=Cb}, Sub0, #kern{ff=OldFF,func=Func}=St0) ->
FA = case OldFF of
@@ -1080,9 +1081,44 @@ select_bin_con(Cs0) ->
end, Cs0),
select_bin_con_1(Cs1).
+
select_bin_con_1(Cs) ->
try
- select_bin_int(Cs)
+ %% The usual way to match literals is to first extract the
+ %% value to a register, and then compare the register to the
+ %% literal value. Extracting the value is good if we need
+ %% compare it more than once.
+ %%
+ %% But we would like to combine the extracting and the
+ %% comparing into a single instruction if we know that
+ %% a binary segment must contain specific integer value
+ %% or the matching will fail, like in this example:
+ %%
+ %% <<42:8,...>> ->
+ %% <<42:8,...>> ->
+ %% .
+ %% .
+ %% .
+ %% <<42:8,...>> ->
+ %% <<>> ->
+ %%
+ %% The first segment must either contain the integer 42
+ %% or the binary must end for the match to succeed.
+ %%
+ %% The way we do is to replace the generic #k_bin_seg{}
+ %% record with a #k_bin_int{} record if all clauses will
+ %% select the same literal integer (except for one or more
+ %% clauses that will end the binary).
+
+ {BinSegs0,BinEnd} =
+ partition(fun (C) ->
+ clause_con(C) =:= k_bin_seg
+ end, Cs),
+ BinSegs = select_bin_int(BinSegs0),
+ case BinEnd of
+ [] -> BinSegs;
+ [_|_] -> BinSegs ++ [{k_bin_end,BinEnd}]
+ end
catch
throw:not_possible ->
select_bin_con_2(Cs)
@@ -1096,7 +1132,7 @@ select_bin_con_2([]) -> [].
%% select_bin_int([Clause]) -> {k_bin_int,[Clause]}
%% If the first pattern in each clause selects the same integer,
-%% rewrite all clauses to use #k_bin_int{} (which will later to
+%% rewrite all clauses to use #k_bin_int{} (which will later be
%% translated to a bs_match_string/4 instruction).
%%
%% If it is not possible to do this rewrite, a 'not_possible'
@@ -1345,7 +1381,7 @@ clause_arg(#iclause{pats=[Arg|_]}) -> Arg.
clause_con(C) -> arg_con(clause_arg(C)).
-clause_val(C) -> arg_val(clause_arg(C)).
+clause_val(C) -> arg_val(clause_arg(C), C).
is_var_clause(C) -> clause_con(C) =:= k_var.
@@ -1376,7 +1412,7 @@ arg_con(Arg) ->
#k_var{} -> k_var
end.
-arg_val(Arg) ->
+arg_val(Arg, C) ->
case arg_arg(Arg) of
#k_literal{val=Lit} -> Lit;
#k_int{val=I} -> I;
@@ -1384,7 +1420,13 @@ arg_val(Arg) ->
#k_atom{val=A} -> A;
#k_tuple{es=Es} -> length(Es);
#k_bin_seg{size=S,unit=U,type=T,flags=Fs} ->
- {set_kanno(S, []),U,T,Fs}
+ case S of
+ #k_var{name=V} ->
+ #iclause{isub=Isub} = C,
+ {#k_var{name=get_vsub(V, Isub)},U,T,Fs};
+ _ ->
+ {set_kanno(S, []),U,T,Fs}
+ end
end.
%% ubody_used_vars(Expr, State) -> [UsedVar]
@@ -1827,7 +1869,9 @@ format_error({nomatch_shadow,Line}) ->
format_error(nomatch_shadow) ->
"this clause cannot match because a previous clause always matches";
format_error(bad_call) ->
- "invalid module and/or function name; this call will always fail".
+ "invalid module and/or function name; this call will always fail";
+format_error(bad_segment_size) ->
+ "binary construction will fail because of a type mismatch".
add_warning(none, Term, Anno, #kern{ws=Ws}=St) ->
File = get_file(Anno),
diff --git a/lib/compiler/test/Makefile b/lib/compiler/test/Makefile
index e047166ade..b9c5be09ce 100644
--- a/lib/compiler/test/Makefile
+++ b/lib/compiler/test/Makefile
@@ -10,7 +10,7 @@ MODULES= \
apply_SUITE \
beam_validator_SUITE \
beam_disasm_SUITE \
- beam_expect_SUITE \
+ beam_except_SUITE \
bs_bincomp_SUITE \
bs_bit_binaries_SUITE \
bs_construct_SUITE \
@@ -29,7 +29,6 @@ MODULES= \
match_SUITE \
misc_SUITE \
num_bif_SUITE \
- pmod_SUITE \
receive_SUITE \
record_SUITE \
trycatch_SUITE \
@@ -39,7 +38,7 @@ MODULES= \
NO_OPT= \
andor \
apply \
- beam_expect \
+ beam_except \
bs_construct \
bs_match \
bs_utf \
diff --git a/lib/compiler/test/andor_SUITE.erl b/lib/compiler/test/andor_SUITE.erl
index f7388f1614..fe69aeeb43 100644
--- a/lib/compiler/test/andor_SUITE.erl
+++ b/lib/compiler/test/andor_SUITE.erl
@@ -29,11 +29,12 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
test_lib:recompile(?MODULE),
- [t_case, t_and_or, t_andalso, t_orelse, inside, overlap,
- combined, in_case, before_and_inside_if].
+ [{group,p}].
groups() ->
- [].
+ [{p,test_lib:parallel(),
+ [t_case,t_and_or,t_andalso,t_orelse,inside,overlap,
+ combined,in_case,before_and_inside_if]}].
init_per_suite(Config) ->
Config.
diff --git a/lib/compiler/test/beam_expect_SUITE.erl b/lib/compiler/test/beam_except_SUITE.erl
index 6f216eac4f..6b55224a42 100644
--- a/lib/compiler/test/beam_expect_SUITE.erl
+++ b/lib/compiler/test/beam_except_SUITE.erl
@@ -16,7 +16,7 @@
%%
%% %CopyrightEnd%
%%
--module(beam_expect_SUITE).
+-module(beam_except_SUITE).
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2,
diff --git a/lib/compiler/test/beam_validator_SUITE.erl b/lib/compiler/test/beam_validator_SUITE.erl
index 902867bc19..c84c83795a 100644
--- a/lib/compiler/test/beam_validator_SUITE.erl
+++ b/lib/compiler/test/beam_validator_SUITE.erl
@@ -47,17 +47,18 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
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].
+ [beam_files,{group,p}].
groups() ->
- [].
+ [{p,test_lib:parallel(),
+ [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]}].
init_per_suite(Config) ->
Config.
diff --git a/lib/compiler/test/bs_bit_binaries_SUITE.erl b/lib/compiler/test/bs_bit_binaries_SUITE.erl
index 30276f1259..897b4769f1 100644
--- a/lib/compiler/test/bs_bit_binaries_SUITE.erl
+++ b/lib/compiler/test/bs_bit_binaries_SUITE.erl
@@ -34,13 +34,15 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
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].
+ [{group,p}].
groups() ->
- [].
+ [{p,test_lib:parallel(),
+ [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]}].
+
init_per_suite(Config) ->
Config.
diff --git a/lib/compiler/test/bs_construct_SUITE.erl b/lib/compiler/test/bs_construct_SUITE.erl
index 31c7890f26..4ea5235bb6 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-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -36,12 +36,14 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
test_lib:recompile(?MODULE),
- [two, test1, fail, float_bin, in_guard, in_catch,
- nasty_literals, side_effect, opt, otp_7556, float_arith,
- otp_8054].
+ [{group,p}].
groups() ->
- [].
+ [{p,test_lib:parallel(),
+ [two,test1,fail,float_bin,in_guard,in_catch,
+ nasty_literals,side_effect,opt,otp_7556,float_arith,
+ otp_8054]}].
+
init_per_suite(Config) ->
Config.
@@ -360,6 +362,11 @@ in_catch(Config) when is_list(Config) ->
?line <<255>> = small(255, <<1,2,3,4,5,6,7,8,9>>),
?line <<1,2>> = small(<<7,8,9,10>>, 258),
?line <<>> = small(<<1,2,3,4,5>>, <<7,8,9,10>>),
+
+ <<15,240,0,42>> = small2(255, 42),
+ <<7:20>> = small2(<<1,2,3>>, 7),
+ <<300:12>> = small2(300, <<1,2,3>>),
+ <<>> = small2(<<1>>, <<2>>),
ok.
small(A, B) ->
@@ -381,6 +388,25 @@ small(A, B) ->
end,
<<ResA/binary,ResB/binary>>.
+small2(A, B) ->
+ case begin
+ case catch <<A:12>> of
+ {'EXIT',_} -> <<>>;
+ ResA0 -> ResA0
+ end
+ end of
+ ResA -> ok
+ end,
+ case begin
+ case catch <<B:20>> of
+ {'EXIT',_} -> <<>>;
+ ResB0 -> ResB0
+ end
+ end of
+ ResB -> ok
+ end,
+ <<ResA/binary-unit:1,ResB/binary-unit:1>>.
+
nasty_literals(Config) when is_list(Config) ->
case erlang:system_info(endian) of
big ->
@@ -468,6 +494,10 @@ opt(Config) when is_list(Config) ->
?line {'EXIT',_} = (catch <<<<23,56,0,2>>:64/float>>),
?line {'EXIT',_} = (catch <<<<23,56,0,2:7>>/binary>>),
+ %% Test constant propagation - there should be a warning.
+ BadSz = 2.5,
+ {'EXIT',_} = (catch <<<<N,56,0,2>>:BadSz/binary>>),
+
case id(false) of
true -> ?line opt_dont_call_me();
false -> ok
diff --git a/lib/compiler/test/bs_match_SUITE.erl b/lib/compiler/test/bs_match_SUITE.erl
index 01b7568122..e8a92c509e 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-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -33,7 +33,8 @@
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,
- cover_beam_bool/1]).
+ cover_beam_bool/1,matched_out_size/1,follow_fail_branch/1,
+ no_partition/1]).
-export([coverage_id/1,coverage_external_ignore/2]).
@@ -44,19 +45,22 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
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,
- cover_beam_bool].
+ [{group,p}].
groups() ->
- [].
+ [{p,test_lib:parallel(),
+ [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,matched_out_size,follow_fail_branch,
+ no_partition]}].
+
init_per_suite(Config) ->
Config.
@@ -800,12 +804,29 @@ matching_and_andalso(Config) when is_list(Config) ->
?line {'EXIT',{function_clause,_}} = (catch matching_and_andalso_1(<<1,2,3>>, -8)),
?line {'EXIT',{function_clause,_}} = (catch matching_and_andalso_1(<<1,2,3>>, blurf)),
?line {'EXIT',{function_clause,_}} = (catch matching_and_andalso_1(<<1,2,3>>, 19)),
+
+ {"abc",<<"xyz">>} = matching_and_andalso_2("abc", <<"-xyz">>),
+ {"abc",<<"">>} = matching_and_andalso_2("abc", <<($a-1)>>),
+ {"abc",<<"">>} = matching_and_andalso_2("abc", <<($z+1)>>),
+ {"abc",<<"">>} = matching_and_andalso_2("abc", <<($A-1)>>),
+ {"abc",<<"">>} = matching_and_andalso_2("abc", <<($Z+1)>>),
+ error = matching_and_andalso_2([], <<>>),
+ error = matching_and_andalso_2([], <<$A>>),
+ error = matching_and_andalso_2([], <<$Z>>),
+ error = matching_and_andalso_2([], <<$a>>),
+ error = matching_and_andalso_2([], <<$z>>),
ok.
matching_and_andalso_1(<<Bitmap/binary>>, K)
when is_integer(K) andalso size(Bitmap) >= K andalso 0 < K ->
ok.
+matching_and_andalso_2(Datetime, <<H,T/binary>>)
+ when not ((H >= $a) andalso (H =< $z)) andalso
+ not ((H >= $A) andalso (H =< $Z)) ->
+ {Datetime,T};
+matching_and_andalso_2(_, _) -> error.
+
%% Thanks to Tomas Stejskal.
otp_7188(Config) when is_list(Config) ->
MP3 = <<84,65,71,68,117,154,105,232,107,121,0,0,0,0,0,0,0,0,0,0,
@@ -1062,6 +1083,101 @@ do_cover_beam_bool(Bin, X) when X > 0 ->
do_cover_beam_bool(<<_,Bin/binary>>, X) ->
do_cover_beam_bool(Bin, X+1).
+matched_out_size(Config) when is_list(Config) ->
+ {253,16#DEADBEEF} = mos_int(<<8,253,16#DEADBEEF:32>>),
+ {6,16#BEEFDEAD} = mos_int(<<3,6:3,16#BEEFDEAD:32>>),
+ {53,16#CAFEDEADBEEFCAFE} = mos_int(<<16,53:16,16#CAFEDEADBEEFCAFE:64>>),
+ {23,16#CAFEDEADBEEFCAFE} = mos_int(<<5,23:5,16#CAFEDEADBEEFCAFE:64>>),
+
+ {<<1,2,3>>,4} = mos_bin(<<3,1,2,3,4,3>>),
+ {<<1,2,3,7>>,19,42} = mos_bin(<<4,1,2,3,7,19,4,42>>),
+ <<1,2,3,7>> = mos_bin(<<4,1,2,3,7,"abcdefghij">>),
+
+ ok.
+
+mos_int(<<L,I:L,X:32>>) ->
+ {I,X};
+mos_int(<<L,I:L,X:64>>) ->
+ {I,X}.
+
+mos_bin(<<L,Bin:L/binary,X:8,L>>) ->
+ L = byte_size(Bin),
+ {Bin,X};
+mos_bin(<<L,Bin:L/binary,X:8,L,Y:8>>) ->
+ L = byte_size(Bin),
+ {Bin,X,Y};
+mos_bin(<<L,Bin:L/binary,"abcdefghij">>) ->
+ L = byte_size(Bin),
+ Bin.
+
+follow_fail_branch(_) ->
+ 42 = ffb_1(<<0,1>>, <<0>>),
+ 8 = ffb_1(<<0,1>>, [a]),
+ 42 = ffb_2(<<0,1>>, <<0>>, 17),
+ 8 = ffb_2(<<0,1>>, [a], 0),
+ ok.
+
+ffb_1(<<_,T/bitstring>>, List) ->
+ case List of
+ <<_>> ->
+ 42;
+ [_|_] ->
+ %% The fail branch of the bs_start_match2 instruction
+ %% pointing to here would be ignored, making the compiler
+ %% incorrectly assume that the delayed sub-binary
+ %% optimization was safe.
+ bit_size(T)
+ end.
+
+ffb_2(<<_,T/bitstring>>, List, A) ->
+ case List of
+ <<_>> when A =:= 17 -> 42;
+ [_|_] -> bit_size(T)
+ end.
+
+no_partition(_) ->
+ one = no_partition_1(<<"string">>, a1),
+ {two,<<"string">>} = no_partition_1(<<"string">>, a2),
+ {two,<<>>} = no_partition_1(<<>>, a2),
+ {two,a} = no_partition_1(a, a2),
+ three = no_partition_1(undefined, a3),
+ {four,a,[]} = no_partition_1([a], a4),
+ {five,a,b} = no_partition_1({a,b}, a5),
+
+ one = no_partition_2(<<"string">>, a1),
+ two = no_partition_2(<<"string">>, a2),
+ two = no_partition_2(<<>>, a2),
+ two = no_partition_2(a, a2),
+ three = no_partition_2(undefined, a3),
+ four = no_partition_2(42, a4),
+ five = no_partition_2([], a5),
+ six = no_partition_2(42.0, a6),
+ ok.
+
+no_partition_1(<<"string">>, a1) ->
+ one;
+no_partition_1(V, a2) ->
+ {two,V};
+no_partition_1(undefined, a3) ->
+ three;
+no_partition_1([H|T], a4) ->
+ {four,H,T};
+no_partition_1({A,B}, a5) ->
+ {five,A,B}.
+
+no_partition_2(<<"string">>, a1) ->
+ one;
+no_partition_2(_, a2) ->
+ two;
+no_partition_2(undefined, a3) ->
+ three;
+no_partition_2(42, a4) ->
+ four;
+no_partition_2([], a5) ->
+ five;
+no_partition_2(42.0, a6) ->
+ six.
+
check(F, R) ->
R = F().
diff --git a/lib/compiler/test/compilation_SUITE.erl b/lib/compiler/test/compilation_SUITE.erl
index fed7bec7d4..f8f74e6f7a 100644
--- a/lib/compiler/test/compilation_SUITE.erl
+++ b/lib/compiler/test/compilation_SUITE.erl
@@ -28,26 +28,29 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
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,
- {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,split_cases].
+ [self_compile_old_inliner,self_compile,
+ {group,p}].
groups() ->
- [{vsn, [], [vsn_1, vsn_2, vsn_3]}].
+ [{vsn,[parallel],[vsn_1,vsn_2,vsn_3]},
+ {p,test_lib:parallel(),
+ [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,
+ 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,split_cases]}].
init_per_suite(Config) ->
Config.
@@ -140,7 +143,6 @@ split({int, N}, <<N:16,B:N/binary,T/binary>>) ->
?comp(live_var).
?comp(trycatch_4).
-?comp(bad_functional_value).
?comp(catch_in_catch).
@@ -623,7 +625,7 @@ string_table(Config) when is_list(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,
+ ?line {"StrT", <<"stringtable">>} = StringTableChunk,
ok.
otp_8949_a(Config) when is_list(Config) ->
diff --git a/lib/compiler/test/compile_SUITE.erl b/lib/compiler/test/compile_SUITE.erl
index 512fa0e4ac..229e5a98a1 100644
--- a/lib/compiler/test/compile_SUITE.erl
+++ b/lib/compiler/test/compile_SUITE.erl
@@ -25,9 +25,9 @@
-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,
+ file_1/1, forms_2/1, module_mismatch/1, big_file/1, outdir/1,
binary/1, makedep/1, cond_and_ifdef/1, listings/1, listings_big/1,
- other_output/1, package_forms/1, encrypted_abstr/1,
+ other_output/1, encrypted_abstr/1,
bad_record_use1/1, bad_record_use2/1, strict_record/1,
missing_testheap/1, cover/1, env/1, core/1, asm/1,
sys_pre_attributes/1]).
@@ -42,9 +42,9 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
test_lib:recompile(?MODULE),
- [app_test, file_1, module_mismatch, big_file, outdir,
+ [app_test, file_1, forms_2, module_mismatch, big_file, outdir,
binary, makedep, cond_and_ifdef, listings, listings_big,
- other_output, package_forms, encrypted_abstr,
+ other_output, encrypted_abstr,
{group, bad_record_use}, strict_record,
missing_testheap, cover, env, core, asm,
sys_pre_attributes].
@@ -76,6 +76,9 @@ app_test(Config) when is_list(Config) ->
file_1(Config) when is_list(Config) ->
?line Dog = test_server:timetrap(test_server:minutes(5)),
+
+ process_flag(trap_exit, true),
+
?line {Simple, Target} = files(Config, "file_1"),
?line {ok, Cwd} = file:get_cwd(),
?line ok = file:set_cwd(filename:dirname(Target)),
@@ -102,9 +105,37 @@ file_1(Config) when is_list(Config) ->
%% Cleanup.
?line ok = file:delete(Target),
?line ok = file:del_dir(filename:dirname(Target)),
+
+ %% There should not be any messages in the messages.
+ receive
+ Any ->
+ ?t:fail({unexpected,Any})
+ after 10 ->
+ ok
+ end,
+
?line test_server:timetrap_cancel(Dog),
ok.
+forms_2(Config) when is_list(Config) ->
+ Src = "/foo/bar",
+ AbsSrc = filename:absname(Src),
+ {ok,simple,Binary} = compile:forms([{attribute,1,module,simple}],
+ [binary,{source,Src}]),
+ code:load_binary(simple, Src, Binary),
+ Info = simple:module_info(compile),
+
+ %% Test that the proper source is returned.
+ AbsSrc = proplists:get_value(source, Info),
+
+ %% Ensure that the options are not polluted with 'source'.
+ [] = proplists:get_value(options, Info),
+
+ %% Cleanup.
+ true = code:delete(simple),
+ false = code:purge(simple),
+ ok.
+
module_mismatch(Config) when is_list(Config) ->
?line DataDir = ?config(data_dir, Config),
?line File = filename:join(DataDir, "wrong_module_name.erl"),
@@ -211,6 +242,12 @@ makedep(Config) when is_list(Config) ->
[makedep,{makedep_output,Target}|IncludeOptions]),
?line {ok,Mf6} = file:read_file(Target),
?line BasicMf2 = makedep_canonicalize_result(Mf6, DataDir),
+ %% Rule with creating phony target.
+ ?line PhonyMfName = SimpleRootname ++ "-phony.mk",
+ ?line {ok,PhonyMf} = file:read_file(PhonyMfName),
+ ?line {ok,_,Mf7} = compile:file(Simple,
+ [binary,makedep,makedep_phony|IncludeOptions]),
+ ?line PhonyMf = makedep_canonicalize_result(Mf7, DataDir),
?line ok = file:delete(Target),
?line ok = file:del_dir(filename:dirname(Target)),
@@ -373,32 +410,6 @@ other_output(Config) when is_list(Config) ->
?line test_server:timetrap_cancel(Dog),
ok.
-package_forms(Config) when is_list(Config) ->
- Fs = [{attribute,1,file,{"./p.erl",1}},
- {attribute,1,module,[p,p]},
- {attribute,3,compile,export_all},
- {attribute,1,file,
- {"/clearcase/otp/erts/lib/stdlib/include/qlc.hrl",1}},
- {attribute,6,file,{"./p.erl",6}},
- {function,7,q,0,
- [{clause,7,[],[],
- [{call,8,
- {remote,8,{atom,8,qlc},{atom,8,q}},
- [{tuple,-8,
- [{atom,-8,qlc_lc},
- {'fun',-8,
- {clauses,
- [{clause,-8,[],[],
- [{tuple,-8,
- [{atom,-8,simple_v1},
- {atom,-8,'X'},
- {'fun',-8,{clauses,[{clause,-8,[],[],[{nil,8}]}]}},
- {integer,-8,8}]}]}]}},
- {atom,-8,undefined}]}]}]}]},
- {eof,9}],
- {ok,'p.p',_} = compile:forms(Fs, ['S',report]),
- ok.
-
encrypted_abstr(Config) when is_list(Config) ->
?line Dog = test_server:timetrap(test_server:minutes(10)),
?line {Simple,Target} = files(Config, "encrypted_abstr"),
diff --git a/lib/compiler/test/compile_SUITE_data/simple-phony.mk b/lib/compiler/test/compile_SUITE_data/simple-phony.mk
new file mode 100644
index 0000000000..c84bcedd2a
--- /dev/null
+++ b/lib/compiler/test/compile_SUITE_data/simple-phony.mk
@@ -0,0 +1,3 @@
+simple.beam: $(srcdir)/simple.erl $(srcdir)/include/simple.hrl
+
+$(srcdir)/include/simple.hrl:
diff --git a/lib/compiler/test/core_SUITE.erl b/lib/compiler/test/core_SUITE.erl
index 06185bfc34..a40dc32d59 100644
--- a/lib/compiler/test/core_SUITE.erl
+++ b/lib/compiler/test/core_SUITE.erl
@@ -43,11 +43,13 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
test_lib:recompile(?MODULE),
- [dehydrated_itracer,nested_tries,seq_in_guard,make_effect_seq,
- eval_is_boolean,unsafe_case,nomatch_shadow,reversed_annos].
+ [{group,p}].
groups() ->
- [].
+ [{p,test_lib:parallel(),
+ [dehydrated_itracer,nested_tries,seq_in_guard,make_effect_seq,
+ eval_is_boolean,unsafe_case,nomatch_shadow,reversed_annos]}].
+
init_per_suite(Config) ->
Config.
diff --git a/lib/compiler/test/core_fold_SUITE.erl b/lib/compiler/test/core_fold_SUITE.erl
index 54bd52947e..2adc71c237 100644
--- a/lib/compiler/test/core_fold_SUITE.erl
+++ b/lib/compiler/test/core_fold_SUITE.erl
@@ -31,11 +31,13 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
test_lib:recompile(?MODULE),
- [t_element, setelement, t_length, append, t_apply, bifs,
- eq, nested_call_in_case, guard_try_catch, coverage].
+ [{group,p}].
groups() ->
- [].
+ [{p,test_lib:parallel(),
+ [t_element,setelement,t_length,append,t_apply,bifs,
+ eq,nested_call_in_case,guard_try_catch,coverage]}].
+
init_per_suite(Config) ->
Config.
diff --git a/lib/compiler/test/error_SUITE.erl b/lib/compiler/test/error_SUITE.erl
index eb5e50818e..859c4571ea 100644
--- a/lib/compiler/test/error_SUITE.erl
+++ b/lib/compiler/test/error_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -22,16 +22,21 @@
-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]).
+ head_mismatch_line/1,warnings_as_errors/1, bif_clashes/1,
+ transforms/1]).
+
+%% Used by transforms/1 test case.
+-export([parse_transform/2]).
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
test_lib:recompile(?MODULE),
- [head_mismatch_line, warnings_as_errors, bif_clashes].
+ [{group,p}].
groups() ->
- [].
+ [{p,test_lib:parallel(),
+ [head_mismatch_line,warnings_as_errors,bif_clashes,transforms]}].
init_per_suite(Config) ->
Config.
@@ -216,6 +221,24 @@ warnings_as_errors(Config) when is_list(Config) ->
ok.
+transforms(Config) ->
+ Ts1 = [{undef_parse_transform,
+ <<"
+ -compile({parse_transform,non_existing}).
+ ">>,
+ [return],
+ {error,[{none,compile,{undef_parse_transform,non_existing}}],[]}}],
+ [] = run(Config, Ts1),
+ Ts2 = <<"
+ -compile({parse_transform,",?MODULE_STRING,"}).
+ ">>,
+ {error,[{none,compile,{parse_transform,?MODULE,{too_bad,_}}}],[]} =
+ run_test(Ts2, test_filename(Config), [], dont_write_beam),
+ ok.
+
+parse_transform(_, _) ->
+ error(too_bad).
+
run(Config, Tests) ->
?line File = test_filename(Config),
@@ -260,12 +283,14 @@ filter(X) ->
%% Compiles a test module and returns the list of errors and warnings.
test_filename(Conf) ->
- Filename = "errors_test.erl",
+ Filename = ["errors_test_",test_lib:uniq(),".erl"],
DataDir = ?config(priv_dir, Conf),
filename:join(DataDir, Filename).
run_test(Test0, File, Warnings, WriteBeam) ->
- ?line Test = ["-module(errors_test). ", Test0],
+ ModName = filename:rootname(filename:basename(File), ".erl"),
+ Mod = list_to_atom(ModName),
+ Test = ["-module(",ModName,"). ",Test0],
?line Opts = case WriteBeam of
dont_write_beam ->
[binary,return_errors|Warnings];
@@ -279,17 +304,17 @@ run_test(Test0, File, Warnings, WriteBeam) ->
%% Test result of compilation.
?line Res = case compile:file(File, Opts) of
- {ok,errors_test,_,[{_File,Ws}]} ->
+ {ok,Mod,_,[{_File,Ws}]} ->
%io:format("compile:file(~s,~p) ->~n~p~n",
% [File,Opts,Ws]),
{warning,Ws};
- {ok,errors_test,_,[]} ->
+ {ok,Mod,_,[]} ->
%io:format("compile:file(~s,~p) ->~n~p~n",
% [File,Opts,Ws]),
[];
- {ok,errors_test,[{_File,Ws}]} ->
+ {ok,Mod,[{_File,Ws}]} ->
{warning,Ws};
- {ok,errors_test,[]} ->
+ {ok,Mod,[]} ->
[];
{error,[{XFile,Es}],Ws} = _ZZ when is_list(XFile) ->
%io:format("compile:file(~s,~p) ->~n~p~n",
diff --git a/lib/compiler/test/guard_SUITE.erl b/lib/compiler/test/guard_SUITE.erl
index 40711783ed..66c0b9a295 100644
--- a/lib/compiler/test/guard_SUITE.erl
+++ b/lib/compiler/test/guard_SUITE.erl
@@ -39,17 +39,18 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
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, t_tuple_size, binary_part,
- bad_constants].
+ [{group,p}].
groups() ->
- [].
+ [{p,test_lib:parallel(),
+ [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,
+ bad_constants]}].
init_per_suite(Config) ->
Config.
diff --git a/lib/compiler/test/guard_SUITE_data/guard_SUITE_tuple_size.S b/lib/compiler/test/guard_SUITE_data/guard_SUITE_tuple_size.S
index c0bf04ed8f..cffb792920 100644
--- a/lib/compiler/test/guard_SUITE_data/guard_SUITE_tuple_size.S
+++ b/lib/compiler/test/guard_SUITE_data/guard_SUITE_tuple_size.S
@@ -19,10 +19,10 @@
{get_tuple_element,{x,0},1,{x,2}}.
{get_tuple_element,{x,0},2,{x,3}}.
{get_tuple_element,{x,0},3,{x,4}}.
- {gc_bif,'+',{f,0},5,[{x,1},{x,2}],{x,0}}.
- {gc_bif,'+',{f,0},5,[{x,0},{x,3}],{x,0}}.
- {gc_bif,'+',{f,0},5,[{x,0},{x,4}],{x,0}}.
- {gc_bif,'+',{f,0},5,[{x,0},{x,5}],{x,0}}.
+ {gc_bif,'+',{f,0},6,[{x,1},{x,2}],{x,0}}.
+ {gc_bif,'+',{f,0},6,[{x,0},{x,3}],{x,0}}.
+ {gc_bif,'+',{f,0},6,[{x,0},{x,4}],{x,0}}.
+ {gc_bif,'+',{f,0},6,[{x,0},{x,5}],{x,0}}.
return.
{label,3}.
{badmatch,{x,0}}.
diff --git a/lib/compiler/test/inline_SUITE.erl b/lib/compiler/test/inline_SUITE.erl
index 2e17d3fde6..6dc7548437 100644
--- a/lib/compiler/test/inline_SUITE.erl
+++ b/lib/compiler/test/inline_SUITE.erl
@@ -32,17 +32,22 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
test_lib:recompile(?MODULE),
- [attribute, bsdecode, bsdes, barnes2, decode1, smith,
- itracer, pseudoknot, comma_splitter, lists, really_inlined, otp_7223,
- coverage].
+ [{group,p}].
groups() ->
- [].
+ [{p,test_lib:parallel(),
+ [attribute,bsdecode,bsdes,barnes2,decode1,smith,
+ itracer,pseudoknot,comma_splitter,lists,really_inlined,otp_7223,
+ coverage]}].
init_per_suite(Config) ->
- Config.
+ Pa = "-pa " ++ filename:dirname(code:which(?MODULE)),
+ {ok,Node} = start_node(compiler, Pa),
+ [{testing_node,Node}|Config].
-end_per_suite(_Config) ->
+end_per_suite(Config) ->
+ Node = ?config(testing_node, Config),
+ ?t:stop_node(Node),
ok.
init_per_group(_GroupName, Config) ->
@@ -81,6 +86,7 @@ attribute(Config) when is_list(Config) ->
?comp(comma_splitter).
try_inline(Mod, Config) ->
+ Node = ?config(testing_node, Config),
?line Src = filename:join(?config(data_dir, Config), atom_to_list(Mod)),
?line Out = ?config(priv_dir,Config),
@@ -89,8 +95,6 @@ try_inline(Mod, Config) ->
?line {ok,Mod} = compile:file(Src, [{outdir,Out},report,bin_opt_info,clint]),
?line Dog = test_server:timetrap(test_server:minutes(10)),
- ?line Pa = "-pa " ++ filename:dirname(code:which(?MODULE)),
- ?line {ok,Node} = start_node(compiler, Pa),
?line NormalResult = rpc:call(Node, ?MODULE, load_and_call, [Out,Mod]),
?line test_server:timetrap_cancel(Dog),
@@ -125,7 +129,6 @@ try_inline(Mod, Config) ->
%% Delete Beam file.
?line ok = file:delete(filename:join(Out, atom_to_list(Mod)++code:objfile_extension())),
- ?line ?t:stop_node(Node),
ok.
compare(Same, Same) -> ok;
@@ -255,6 +258,49 @@ lists(Config) when is_list(Config) ->
%% Cleanup.
erase(?MODULE),
+
+ {'EXIT',{function_clause,[{?MODULE,_,[_,not_a_list],_}|_]}} =
+ (catch lists:map(fun (X) -> X end, not_a_list)),
+ {'EXIT',{function_clause,[{?MODULE,_,[_,not_a_list],_}|_]}} =
+ (catch lists:flatmap(fun (X) -> X end, not_a_list)),
+ {'EXIT',{function_clause,[{?MODULE,_,[_,not_a_list],_}|_]}} =
+ (catch lists:foreach(fun (X) -> X end, not_a_list)),
+ {'EXIT',{function_clause,[{?MODULE,_,[_,not_a_list],_}|_]}} =
+ (catch lists:filter(fun (_) -> true end, not_a_list)),
+ {'EXIT',{function_clause,[{?MODULE,_,[_,not_a_list],_}|_]}} =
+ (catch lists:any(fun (_) -> false end, not_a_list)),
+ {'EXIT',{function_clause,[{?MODULE,_,[_,not_a_list],_}|_]}} =
+ (catch lists:all(fun (_) -> true end, not_a_list)),
+ {'EXIT',{function_clause,[{?MODULE,_,[_,acc,not_a_list],_}|_]}} =
+ (catch lists:foldl(fun (X, Acc) -> {X,Acc} end, acc, not_a_list)),
+ {'EXIT',{function_clause,[{?MODULE,_,[_,acc,not_a_list],_}|_]}} =
+ (catch lists:foldr(fun (X, Acc) -> {X,Acc} end, acc, not_a_list)),
+ {'EXIT',{function_clause,[{?MODULE,_,[_,acc,not_a_list],_}|_]}} =
+ (catch lists:mapfoldl(fun (X, Acc) -> {X,Acc} end, acc, not_a_list)),
+ {'EXIT',{function_clause,[{?MODULE,_,[_,acc,not_a_list],_}|_]}} =
+ (catch lists:mapfoldr(fun (X, Acc) -> {X,Acc} end, acc, not_a_list)),
+
+ {'EXIT',{function_clause,[{?MODULE,_,[not_a_function,[]],_}|_]}} =
+ (catch lists:map(not_a_function, [])),
+ {'EXIT',{function_clause,[{?MODULE,_,[not_a_function,[]],_}|_]}} =
+ (catch lists:flatmap(not_a_function, [])),
+ {'EXIT',{function_clause,[{?MODULE,_,[not_a_function,[]],_}|_]}} =
+ (catch lists:foreach(not_a_function, [])),
+ {'EXIT',{function_clause,[{?MODULE,_,[not_a_function,[]],_}|_]}} =
+ (catch lists:filter(not_a_function, [])),
+ {'EXIT',{function_clause,[{?MODULE,_,[not_a_function,[]],_}|_]}} =
+ (catch lists:any(not_a_function, [])),
+ {'EXIT',{function_clause,[{?MODULE,_,[not_a_function,[]],_}|_]}} =
+ (catch lists:all(not_a_function, [])),
+ {'EXIT',{function_clause,[{?MODULE,_,[not_a_function,acc,[]],_}|_]}} =
+ (catch lists:foldl(not_a_function, acc, [])),
+ {'EXIT',{function_clause,[{?MODULE,_,[not_a_function,acc,[]],_}|_]}} =
+ (catch lists:foldr(not_a_function, acc, [])),
+ {'EXIT',{function_clause,[{?MODULE,_,[not_a_function,acc,[]],_}|_]}} =
+ (catch lists:mapfoldl(not_a_function, acc, [])),
+ {'EXIT',{function_clause,[{?MODULE,_,[not_a_function,acc,[]],_}|_]}} =
+ (catch lists:mapfoldr(not_a_function, acc, [])),
+
ok.
my_apply(M, F, A, Init) ->
@@ -293,9 +339,9 @@ otp_7223_2({a}) ->
1.
coverage(Config) when is_list(Config) ->
- ?line Src = filename:join(?config(data_dir, Config), bsdecode),
- ?line Out = ?config(priv_dir,Config),
- ?line {ok,Mod} = compile:file(Src, [{outdir,Out},report,{inline,0},clint]),
- ?line {ok,Mod} = compile:file(Src, [{outdir,Out},report,{inline,20},verbose,clint]),
- ?line ok = file:delete(filename:join(Out, "bsdecode"++code:objfile_extension())),
+ Mod = bsdecode,
+ Src = filename:join(?config(data_dir, Config), Mod),
+ {ok,Mod,_} = compile:file(Src, [binary,report,{inline,0},clint]),
+ {ok,Mod,_} = compile:file(Src, [binary,report,{inline,20},
+ verbose,clint]),
ok.
diff --git a/lib/compiler/test/match_SUITE.erl b/lib/compiler/test/match_SUITE.erl
index 9406d7de8f..de44926d81 100644
--- a/lib/compiler/test/match_SUITE.erl
+++ b/lib/compiler/test/match_SUITE.erl
@@ -30,11 +30,13 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
test_lib:recompile(?MODULE),
- [pmatch, mixed, aliases, match_in_call, untuplify,
- shortcut_boolean, letify_guard, selectify, underscore, coverage].
+ [{group,p}].
groups() ->
- [].
+ [{p,test_lib:parallel(),
+ [pmatch,mixed,aliases,match_in_call,untuplify,
+ shortcut_boolean,letify_guard,selectify,underscore,coverage]}].
+
init_per_suite(Config) ->
Config.
diff --git a/lib/compiler/test/misc_SUITE.erl b/lib/compiler/test/misc_SUITE.erl
index 5e13a93c52..44c7161530 100644
--- a/lib/compiler/test/misc_SUITE.erl
+++ b/lib/compiler/test/misc_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -57,11 +57,12 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
-spec all() -> misc_SUITE_test_cases().
all() ->
test_lib:recompile(?MODULE),
- [tobias, empty_string, md5, silly_coverage,
- confused_literals, integer_encoding, override_bif].
+ [{group,p}].
groups() ->
- [].
+ [{p,[],%%test_lib:parallel(),
+ [tobias,empty_string,md5,silly_coverage,
+ confused_literals,integer_encoding,override_bif]}].
init_per_suite(Config) ->
Config.
@@ -182,6 +183,14 @@ silly_coverage(Config) when is_list(Config) ->
CodegenInput = {?MODULE,[{foo,0}],[],[{function,foo,0,[a|b],a,b,[]}]},
?line expect_error(fun() -> v3_codegen:module(CodegenInput, []) end),
+ %% beam_a
+ BeamAInput = {?MODULE,[{foo,0}],[],
+ [{function,foo,0,2,
+ [{label,1},
+ {func_info,{atom,?MODULE},{atom,foo},0},
+ {label,2}|non_proper_list]}],99},
+ expect_error(fun() -> beam_a:module(BeamAInput, []) end),
+
%% beam_block
BlockInput = {?MODULE,[{foo,0}],[],
[{function,foo,0,2,
@@ -190,6 +199,15 @@ silly_coverage(Config) when is_list(Config) ->
{label,2}|non_proper_list]}],99},
?line expect_error(fun() -> beam_block:module(BlockInput, []) end),
+ %% beam_type
+ TypeInput = {?MODULE,[{foo,0}],[],
+ [{function,foo,0,2,
+ [{label,1},
+ {line,loc},
+ {func_info,{atom,?MODULE},{atom,foo},0},
+ {label,2}|non_proper_list]}],99},
+ expect_error(fun() -> beam_type:module(TypeInput, []) end),
+
%% beam_except
ExceptInput = {?MODULE,[{foo,0}],[],
[{function,foo,0,2,
@@ -254,6 +272,13 @@ silly_coverage(Config) when is_list(Config) ->
{block,[a|b]}]}],0},
?line expect_error(fun() -> beam_receive:module(ReceiveInput, []) end),
+ BeamZInput = {?MODULE,[{foo,0}],[],
+ [{function,foo,0,2,
+ [{label,1},
+ {func_info,{atom,?MODULE},{atom,foo},0},
+ {label,2}|non_proper_list]}],99},
+ expect_error(fun() -> beam_z:module(BeamZInput, []) end),
+
ok.
expect_error(Fun) ->
diff --git a/lib/compiler/test/pmod_SUITE.erl b/lib/compiler/test/pmod_SUITE.erl
deleted file mode 100644
index 5dd09a7245..0000000000
--- a/lib/compiler/test/pmod_SUITE.erl
+++ /dev/null
@@ -1,121 +0,0 @@
-%%
-%% %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(pmod_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,
- basic/1, otp_8447/1]).
-
--include_lib("test_server/include/test_server.hrl").
-
-suite() -> [{ct_hooks,[ts_install_cth]}].
-
-all() ->
- test_lib:recompile(?MODULE),
- [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].
-
-end_per_testcase(Case, Config) when is_atom(Case), is_list(Config) ->
- Dog = ?config(watchdog, Config),
- ?t:timetrap_cancel(Dog),
- ok.
-
-basic(Config) when is_list(Config) ->
- ?line basic_1(Config, []),
- ?line basic_1(Config, [inline]),
- ?line basic_1(Config, [{inline,500},inline]),
- ok.
-
-basic_1(Config, Opts) ->
- io:format("Options: ~p\n", [Opts]),
- ?line ok = compile_load(pmod_basic, Config, Opts),
-
- ?line Prop1 = pmod_basic:new([{a,xb},{b,true},{c,false}]),
- ?line Prop2 = pmod_basic:new([{y,zz}]),
- ?line io:format("Prop1 = ~p\n", [Prop1]),
- ?line io:format("Prop2 = ~p\n", [Prop2]),
-
- ?line {a,xb} = Prop1:lookup(a),
- ?line none = Prop1:lookup(glurf),
- ?line false = Prop1:or_props([]),
- ?line true = Prop1:or_props([b,c]),
- ?line true = Prop1:or_props([b,d]),
- ?line false = Prop1:or_props([d]),
-
- ?line none = Prop2:lookup(kalle),
- ?line {y,zz} = Prop2:lookup(y),
- ?line {a,xb} = Prop1:lookup(a),
-
- ?line Prop3 = Prop1:prepend({blurf,true}),
- ?line io:format("Prop3 = ~p\n", [Prop3]),
- ?line {blurf,true} = Prop3:lookup(blurf),
-
- Prop4 = Prop3:append(42),
- ?line io:format("Prop4 = ~p\n", [Prop4]),
- ?line {42,5} = Prop4:stupid_sum(),
-
- %% Some record guards.
- ?line ok = Prop4:bar({s,0}),
- ?line ok = Prop4:bar_bar({s,blurf}),
- ?line error = Prop4:bar_bar({s,a,b}),
- ?line error = Prop4:bar_bar([]),
-
- %% Call from a fun.
- Fun = fun(Arg) -> Prop4:bar(Arg) end,
- ?line ok = Fun({s,0}),
-
- [{y,[1,2]},{x,[5,19]}] = Prop4:collapse([{y,[2,1]},{x,[19,5]}]),
- ok.
-
-otp_8447(Config) when is_list(Config) ->
- ?line P = pmod_basic:new(foo),
- ?line [0,0,1,1,1,0,0,1] = P:bc1(),
- ?line <<10:4>> = P:bc2(),
- ok.
-
-compile_load(Module, Conf, Opts) ->
- ?line Dir = ?config(data_dir,Conf),
- ?line Src = filename:join(Dir, atom_to_list(Module)),
- ?line Out = ?config(priv_dir,Conf),
- ?line CompRc = compile:file(Src, [report,{outdir,Out}|Opts]),
- ?line {ok,Module} = CompRc,
- ?line code:purge(Module),
- ?line {module,Module} =
- code:load_abs(filename:join(Out, atom_to_list(Module))),
- ok.
diff --git a/lib/compiler/test/pmod_SUITE_data/pmod_basic.erl b/lib/compiler/test/pmod_SUITE_data/pmod_basic.erl
deleted file mode 100644
index 19cce452dc..0000000000
--- a/lib/compiler/test/pmod_SUITE_data/pmod_basic.erl
+++ /dev/null
@@ -1,83 +0,0 @@
-%%
-%% %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(pmod_basic, [Props]).
-
--export([lookup/1,or_props/1,prepend/1,append/1,stupid_sum/0]).
--export([bar/1,bar_bar/1]).
--export([bc1/0, bc2/0]).
--export([collapse/1]).
-
-lookup(Key) ->
- proplists:lookup(Key, Props).
-
-or_props(Keys) ->
- Res = or_props_1(Keys, false),
- true = is_bool(Res), %is_bool/1 does not use Props.
- Res.
-
-prepend(Term) ->
- new([Term|Props]).
-
-append(Term) ->
- pmod_basic:new(Props++[Term]).
-
-or_props_1([K|Ks], Acc) ->
- or_props_1(Ks, proplists:get_bool(K, Props) or Acc);
-or_props_1([], Acc) -> Acc.
-
-is_bool(true) -> true;
-is_bool(false) -> true;
-is_bool(_) -> false.
-
-stupid_sum() ->
- put(counter, 0),
- Res = stupid_sum_1(Props, 0),
- {Res,get(counter)}.
-
-stupid_sum_1([H|T], Sum0) ->
- try add(Sum0, H) of
- Sum -> stupid_sum_1(T, Sum)
- catch
- error:_ -> stupid_sum_1(T, Sum0)
- after
- bump()
- end;
-stupid_sum_1([], Sum) -> Sum.
-
-bump() ->
- put(counter, get(counter)+1).
-
-add(A, B) ->
- A+B.
-
--record(s, {a}).
-
-bar(S) when S#s.a == 0 -> ok.
-
-bar_bar(S) when is_record(S, s) -> ok;
-bar_bar(_) -> error.
-
-bc1() ->
- [A || <<A:1>> <= <<"9">> ].
-
-bc2() ->
- << <<A:1>> || A <- [1,0,1,0] >>.
-
-collapse(L) ->
- lists:keymap(fun lists:sort/1, 2, L).
diff --git a/lib/compiler/test/receive_SUITE.erl b/lib/compiler/test/receive_SUITE.erl
index 2a67615e5e..82c823b789 100644
--- a/lib/compiler/test/receive_SUITE.erl
+++ b/lib/compiler/test/receive_SUITE.erl
@@ -40,10 +40,12 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
test_lib:recompile(?MODULE),
- [recv, coverage, otp_7980, ref_opt, export].
+ [{group,p}].
groups() ->
- [].
+ [{p,test_lib:parallel(),
+ [recv,coverage,otp_7980,ref_opt,export]}].
+
init_per_suite(Config) ->
Config.
diff --git a/lib/compiler/test/receive_SUITE_data/ref_opt/no_4.erl b/lib/compiler/test/receive_SUITE_data/ref_opt/no_4.erl
new file mode 100644
index 0000000000..3ce222176b
--- /dev/null
+++ b/lib/compiler/test/receive_SUITE_data/ref_opt/no_4.erl
@@ -0,0 +1,12 @@
+-module(no_4).
+-compile(export_all).
+
+?MODULE() ->
+ ok.
+
+f(X) ->
+ {Pid,Ref} = spawn_monitor(fun() -> ok end),
+ r(Pid, Ref).
+
+r(_, _) ->
+ ok.
diff --git a/lib/compiler/test/receive_SUITE_data/ref_opt/yes_10.erl b/lib/compiler/test/receive_SUITE_data/ref_opt/yes_10.erl
new file mode 100644
index 0000000000..7ce6e6103c
--- /dev/null
+++ b/lib/compiler/test/receive_SUITE_data/ref_opt/yes_10.erl
@@ -0,0 +1,13 @@
+-module(yes_10).
+-compile(export_all).
+
+?MODULE() ->
+ ok.
+
+f() ->
+ Ref = make_ref(),
+ receive
+ %% Artifical example to cover more code in beam_receive.
+ {X,Y} when Ref =/= X, Ref =:= Y ->
+ ok
+ end.
diff --git a/lib/compiler/test/receive_SUITE_data/ref_opt/yes_11.erl b/lib/compiler/test/receive_SUITE_data/ref_opt/yes_11.erl
new file mode 100644
index 0000000000..62f439fc42
--- /dev/null
+++ b/lib/compiler/test/receive_SUITE_data/ref_opt/yes_11.erl
@@ -0,0 +1,21 @@
+-module(yes_11).
+-compile(export_all).
+
+?MODULE() ->
+ ok.
+
+%% Artifical example to cover more code in beam_receive.
+do_call(Process, Request) ->
+ Mref = erlang:monitor(process, Process),
+ Process ! Request,
+ receive
+ {X,Y,Z} when Mref =/= X, Z =:= 42, Mref =:= Y ->
+ error;
+ {X,Y,_} when Mref =/= X, Mref =:= Y ->
+ error;
+ {Mref, Reply} ->
+ erlang:demonitor(Mref, [flush]),
+ {ok, Reply};
+ {'DOWN', Mref, _, _, _} ->
+ error
+ end.
diff --git a/lib/compiler/test/receive_SUITE_data/ref_opt/yes_12.erl b/lib/compiler/test/receive_SUITE_data/ref_opt/yes_12.erl
new file mode 100644
index 0000000000..efcfed6059
--- /dev/null
+++ b/lib/compiler/test/receive_SUITE_data/ref_opt/yes_12.erl
@@ -0,0 +1,12 @@
+-module(yes_12).
+-compile(export_all).
+
+?MODULE() ->
+ ok.
+
+f() ->
+ {_,Ref} = spawn_monitor(fun() -> ok end),
+ receive
+ {'DOWN',Ref,_,_,Reason} ->
+ Reason
+ end.
diff --git a/lib/compiler/test/receive_SUITE_data/ref_opt/yes_13.erl b/lib/compiler/test/receive_SUITE_data/ref_opt/yes_13.erl
new file mode 100644
index 0000000000..9e93d12ed6
--- /dev/null
+++ b/lib/compiler/test/receive_SUITE_data/ref_opt/yes_13.erl
@@ -0,0 +1,12 @@
+-module(yes_13).
+-compile(export_all).
+
+?MODULE() ->
+ ok.
+
+f() ->
+ {Pid,Ref} = spawn_monitor(fun() -> ok end),
+ receive
+ {'DOWN',Ref,process,Pid,Reason} ->
+ Reason
+ end.
diff --git a/lib/compiler/test/record_SUITE.erl b/lib/compiler/test/record_SUITE.erl
index 363422ec7e..96f3712be9 100644
--- a/lib/compiler/test/record_SUITE.erl
+++ b/lib/compiler/test/record_SUITE.erl
@@ -42,12 +42,14 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
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, coverage].
+ [{group,p}].
groups() ->
- [].
+ [{p,test_lib:parallel(),
+ [errors,record_test_2,record_test_3,
+ record_access_in_guards,guard_opt,eval_once,foobar,
+ missing_test_heap,nested_access,coverage]}].
+
init_per_suite(Config) ->
Config.
diff --git a/lib/compiler/test/test_lib.erl b/lib/compiler/test/test_lib.erl
index 2295592a38..996c369705 100644
--- a/lib/compiler/test/test_lib.erl
+++ b/lib/compiler/test/test_lib.erl
@@ -20,7 +20,8 @@
-include("test_server.hrl").
-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]).
+-export([recompile/1,parallel/0,uniq/0,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
@@ -43,6 +44,18 @@ smoke_disasm(File) when is_list(File) ->
Res = beam_disasm:file(File),
{beam_file,_Mod} = {element(1, Res),element(2, Res)}.
+parallel() ->
+ case ?t:is_cover() orelse erlang:system_info(schedulers) =:= 1 of
+ true -> [];
+ false -> [parallel]
+ end.
+
+uniq() ->
+ U0 = erlang:ref_to_list(make_ref()),
+ U1 = re:replace(U0, "^#Ref", ""),
+ U = re:replace(U1, "[^[A-Za-z0-9_]+", "_", [global]),
+ re:replace(U, "_*$", "", [{return,list}]).
+
%% Retrieve the "interesting" compiler options (options for optimization
%% and compatibility) for the given module.
diff --git a/lib/compiler/test/trycatch_SUITE.erl b/lib/compiler/test/trycatch_SUITE.erl
index 29119c0f5d..4530d08c77 100644
--- a/lib/compiler/test/trycatch_SUITE.erl
+++ b/lib/compiler/test/trycatch_SUITE.erl
@@ -32,13 +32,15 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
test_lib:recompile(?MODULE),
- [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, get_in_try].
+ [{group,p}].
groups() ->
- [].
+ [{p,test_lib:parallel(),
+ [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,get_in_try]}].
+
init_per_suite(Config) ->
Config.
diff --git a/lib/compiler/test/warnings_SUITE.erl b/lib/compiler/test/warnings_SUITE.erl
index f6a572abfa..9ce0df5ec4 100644
--- a/lib/compiler/test/warnings_SUITE.erl
+++ b/lib/compiler/test/warnings_SUITE.erl
@@ -55,12 +55,13 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
test_lib:recompile(?MODULE),
- [pattern, pattern2, pattern3, pattern4, guard,
- bad_arith, bool_cases, bad_apply, files, effect,
- bin_opt_info, bin_construction].
+ [{group,p}].
groups() ->
- [].
+ [{p,test_lib:parallel(),
+ [pattern,pattern2,pattern3,pattern4,guard,
+ bad_arith,bool_cases,bad_apply,files,effect,
+ bin_opt_info,bin_construction]}].
init_per_suite(Config) ->
Config.
@@ -556,9 +557,10 @@ run(Config, Tests) ->
%% Compiles a test module and returns the list of errors and warnings.
run_test(Conf, Test0, Warnings) ->
- Filename = 'warnings_test.erl',
+ Mod = "warnings_"++test_lib:uniq(),
+ Filename = Mod ++ ".erl",
?line DataDir = ?privdir,
- ?line Test = ["-module(warnings_test). ", Test0],
+ Test = ["-module(", Mod, "). ", Test0],
?line File = filename:join(DataDir, Filename),
?line Opts = [binary,export_all,return|Warnings],
?line ok = file:write_file(File, Test),
diff --git a/lib/compiler/vsn.mk b/lib/compiler/vsn.mk
index c9c28cf9a2..b77c82309f 100644
--- a/lib/compiler/vsn.mk
+++ b/lib/compiler/vsn.mk
@@ -1 +1 @@
-COMPILER_VSN = 4.8.1
+COMPILER_VSN = 4.8.2
diff --git a/lib/cosEvent/doc/src/Makefile b/lib/cosEvent/doc/src/Makefile
index 4a0a7429e9..1b867852e4 100644
--- a/lib/cosEvent/doc/src/Makefile
+++ b/lib/cosEvent/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2011. All Rights Reserved.
+# Copyright Ericsson AB 1999-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/cosEvent/src/Makefile b/lib/cosEvent/src/Makefile
index 1825beacc5..2864ee0538 100644
--- a/lib/cosEvent/src/Makefile
+++ b/lib/cosEvent/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -168,9 +168,9 @@ clean:
rm -f errs core *~
$(APP_TARGET): $(APP_SRC)
- sed -e 's;%VSN%;$(VSN);' $(APP_SRC) > $(APP_TARGET)
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $(APP_SRC) > $(APP_TARGET)
$(APPUP_TARGET): $(APPUP_SRC)
- sed -e 's;%VSN%;$(VSN);' $(APPUP_SRC) > $(APPUP_TARGET)
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $(APPUP_SRC) > $(APPUP_TARGET)
docs:
@@ -179,12 +179,12 @@ docs:
# ----------------------------------------------------
IDL-GENERATED: CosEventChannelAdmin.idl cosEventApp.idl CosEventComm.idl
- erlc $(ERL_IDL_FLAGS) +'{cfgfile,"CosEventChannelAdmin.cfg"}' CosEventChannelAdmin.idl
- mv $(GEN_HRL_FILES1) $(EXTERNAL_INC_PATH)
- erlc $(ERL_IDL_FLAGS) +'{cfgfile,"cosEventApp.cfg"}' cosEventApp.idl
- erlc $(ERL_IDL_FLAGS) CosEventComm.idl
- mv $(GEN_HRL_FILES3) $(EXTERNAL_INC_PATH)
- >IDL-GENERATED
+ $(gen_verbose)erlc $(ERL_IDL_FLAGS) +'{cfgfile,"CosEventChannelAdmin.cfg"}' CosEventChannelAdmin.idl
+ $(V_at)mv $(GEN_HRL_FILES1) $(EXTERNAL_INC_PATH)
+ $(V_at)erlc $(ERL_IDL_FLAGS) +'{cfgfile,"cosEventApp.cfg"}' cosEventApp.idl
+ $(V_at)erlc $(ERL_IDL_FLAGS) CosEventComm.idl
+ $(V_at)mv $(GEN_HRL_FILES3) $(EXTERNAL_INC_PATH)
+ $(V_at)>IDL-GENERATED
$(GEN_FILES): IDL-GENERATED
diff --git a/lib/cosEvent/test/Makefile b/lib/cosEvent/test/Makefile
index d5350d8293..c5afcdb7e4 100644
--- a/lib/cosEvent/test/Makefile
+++ b/lib/cosEvent/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2011. All Rights Reserved.
+# Copyright Ericsson AB 1999-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/cosEventDomain/doc/src/Makefile b/lib/cosEventDomain/doc/src/Makefile
index 4e54b0fb55..f820ee5dc0 100644
--- a/lib/cosEventDomain/doc/src/Makefile
+++ b/lib/cosEventDomain/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2001-2011. All Rights Reserved.
+# Copyright Ericsson AB 2001-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/cosEventDomain/src/Makefile b/lib/cosEventDomain/src/Makefile
index 41a31b332f..b4b74bd192 100644
--- a/lib/cosEventDomain/src/Makefile
+++ b/lib/cosEventDomain/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2001-2011. All Rights Reserved.
+# Copyright Ericsson AB 2001-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -141,9 +141,9 @@ clean:
rm -f errs core *~
$(APP_TARGET): $(APP_SRC)
- sed -e 's;%VSN%;$(VSN);' $(APP_SRC) > $(APP_TARGET)
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $(APP_SRC) > $(APP_TARGET)
$(APPUP_TARGET): $(APPUP_SRC)
- sed -e 's;%VSN%;$(VSN);' $(APPUP_SRC) > $(APPUP_TARGET)
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $(APPUP_SRC) > $(APPUP_TARGET)
docs:
@@ -151,9 +151,9 @@ docs:
# Special Build Targets
# ----------------------------------------------------
IDL-GENERATED: CosEventDomainAdmin.idl
- erlc $(ERL_IDL_FLAGS) +'{cfgfile,"CosEventDomainAdmin.cfg"}' CosEventDomainAdmin.idl
- mv $(GEN_HRL_FILES) $(EXTERNAL_INC_PATH)
- >IDL-GENERATED
+ $(gen_verbose)erlc $(ERL_IDL_FLAGS) +'{cfgfile,"CosEventDomainAdmin.cfg"}' CosEventDomainAdmin.idl
+ $(V_at)mv $(GEN_HRL_FILES) $(EXTERNAL_INC_PATH)
+ $(V_at)>IDL-GENERATED
$(GEN_FILES): IDL-GENERATED
diff --git a/lib/cosEventDomain/test/Makefile b/lib/cosEventDomain/test/Makefile
index 9349067eb5..5b59cca401 100644
--- a/lib/cosEventDomain/test/Makefile
+++ b/lib/cosEventDomain/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2001-2011. All Rights Reserved.
+# Copyright Ericsson AB 2001-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/cosFileTransfer/doc/src/Makefile b/lib/cosFileTransfer/doc/src/Makefile
index 706dec00ce..d1c6616f94 100644
--- a/lib/cosFileTransfer/doc/src/Makefile
+++ b/lib/cosFileTransfer/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2000-2011. All Rights Reserved.
+# Copyright Ericsson AB 2000-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/cosFileTransfer/src/Makefile b/lib/cosFileTransfer/src/Makefile
index 1bb765db73..9d3abb9bba 100644
--- a/lib/cosFileTransfer/src/Makefile
+++ b/lib/cosFileTransfer/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2000-2011. All Rights Reserved.
+# Copyright Ericsson AB 2000-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -151,10 +151,10 @@ clean:
rm -f errs core *~
$(APP_TARGET): $(APP_SRC)
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
docs:
@@ -162,9 +162,9 @@ docs:
# Special Build Targets
# ----------------------------------------------------
IDL-GENERATED: CosFileTransfer.idl
- erlc $(ERL_IDL_FLAGS) +'{cfgfile,"CosFileTransfer.cfg"}' CosFileTransfer.idl
- mv $(LOCAL_HRL_FILES) $(EXTERNAL_INC_PATH)
- >IDL-GENERATED
+ $(gen_verbose)erlc $(ERL_IDL_FLAGS) +'{cfgfile,"CosFileTransfer.cfg"}' CosFileTransfer.idl
+ $(V_at)mv $(LOCAL_HRL_FILES) $(EXTERNAL_INC_PATH)
+ $(V_at)>IDL-GENERATED
$(GEN_FILES): IDL-GENERATED
diff --git a/lib/cosFileTransfer/test/Makefile b/lib/cosFileTransfer/test/Makefile
index 177ad7616b..35f6cc1ac2 100644
--- a/lib/cosFileTransfer/test/Makefile
+++ b/lib/cosFileTransfer/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2000-2011. All Rights Reserved.
+# Copyright Ericsson AB 2000-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/cosFileTransfer/test/fileTransfer_SUITE.erl b/lib/cosFileTransfer/test/fileTransfer_SUITE.erl
index 79a234bd28..18a591a7af 100644
--- a/lib/cosFileTransfer/test/fileTransfer_SUITE.erl
+++ b/lib/cosFileTransfer/test/fileTransfer_SUITE.erl
@@ -292,13 +292,8 @@ fts_ftp_file_api(Config) ->
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.
+ ?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).
fts_native_file_api(doc) -> ["CosFileTransfer NATIVE FileTransferSession API tests.", ""];
fts_native_file_api(suite) -> [];
@@ -311,15 +306,10 @@ fts_native_file_api(Config) ->
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.
+ ?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).
@@ -817,23 +807,13 @@ create_node(Name, Port, Retries, 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:start(Host, Name, Args).
slave_sup() ->
process_flag(trap_exit, true),
receive
{'EXIT', _, _} ->
- case os:type() of
- vxworks ->
- erlang:halt();
- _ ->
- ignore
- end
+ ignore
end.
@@ -850,12 +830,7 @@ destroy_node(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.
+ slave:stop(Node).
-endif.
%%------------------------------------------------------------
diff --git a/lib/cosNotification/doc/src/Makefile b/lib/cosNotification/doc/src/Makefile
index d970818928..800d633909 100644
--- a/lib/cosNotification/doc/src/Makefile
+++ b/lib/cosNotification/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2000-2011. All Rights Reserved.
+# Copyright Ericsson AB 2000-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/cosNotification/src/Makefile b/lib/cosNotification/src/Makefile
index 7b8b73aa11..13153bd7a1 100644
--- a/lib/cosNotification/src/Makefile
+++ b/lib/cosNotification/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2011. All Rights Reserved.
+# Copyright Ericsson AB 1999-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -332,10 +332,10 @@ clean:
rm -f errs core *~
$(APP_TARGET): $(APP_SRC)
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
docs:
@@ -344,16 +344,16 @@ docs:
# ----------------------------------------------------
IDL-GENERATED: CosNotification.idl CosNotifyChannelAdmin.idl \
CosNotifyFilter.idl cosNotificationAppComm.idl CosNotifyComm.idl
- erlc $(ERL_IDL_FLAGS) +'{cfgfile,"CosNotification.cfg"}' CosNotification.idl
- mv $(GEN_NOTIFICATION_HRL_FILES) $(EXTERNAL_INC_PATH)
- erlc $(ERL_IDL_FLAGS) +'{cfgfile,"CosNotifyChannelAdmin.cfg"}' CosNotifyChannelAdmin.idl
- mv $(GEN_CHANNELADMIN_HRL_FILES) $(EXTERNAL_INC_PATH)
- erlc $(ERL_IDL_FLAGS) +'{cfgfile,"CosNotifyFilter.cfg"}' CosNotifyFilter.idl
- mv $(GEN_NOTIFYFILTER_HRL_FILES) $(EXTERNAL_INC_PATH)
- erlc $(ERL_IDL_FLAGS) +'{cfgfile,"cosNotificationComm.cfg"}' cosNotificationAppComm.idl
- erlc $(ERL_IDL_FLAGS) +'{cfgfile,"CosNotifyComm.cfg"}' CosNotifyComm.idl
- mv $(GEN_NOTIFYCOMM_HRL_FILES) $(EXTERNAL_INC_PATH)
- >IDL-GENERATED
+ $(gen_verbose)erlc $(ERL_IDL_FLAGS) +'{cfgfile,"CosNotification.cfg"}' CosNotification.idl
+ $(V_at)mv $(GEN_NOTIFICATION_HRL_FILES) $(EXTERNAL_INC_PATH)
+ $(V_at)erlc $(ERL_IDL_FLAGS) +'{cfgfile,"CosNotifyChannelAdmin.cfg"}' CosNotifyChannelAdmin.idl
+ $(V_at)mv $(GEN_CHANNELADMIN_HRL_FILES) $(EXTERNAL_INC_PATH)
+ $(V_at)erlc $(ERL_IDL_FLAGS) +'{cfgfile,"CosNotifyFilter.cfg"}' CosNotifyFilter.idl
+ $(V_at)mv $(GEN_NOTIFYFILTER_HRL_FILES) $(EXTERNAL_INC_PATH)
+ $(V_at)erlc $(ERL_IDL_FLAGS) +'{cfgfile,"cosNotificationComm.cfg"}' cosNotificationAppComm.idl
+ $(V_at)erlc $(ERL_IDL_FLAGS) +'{cfgfile,"CosNotifyComm.cfg"}' CosNotifyComm.idl
+ $(V_at)mv $(GEN_NOTIFYCOMM_HRL_FILES) $(EXTERNAL_INC_PATH)
+ $(V_at)>IDL-GENERATED
$(IDL_GEN_ERL_FILES) $(IDL_GEN_HRL_FILES): IDL-GENERATED
diff --git a/lib/cosNotification/test/Makefile b/lib/cosNotification/test/Makefile
index 17b60a8e9f..8c18ae7956 100644
--- a/lib/cosNotification/test/Makefile
+++ b/lib/cosNotification/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2011. All Rights Reserved.
+# Copyright Ericsson AB 1999-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/cosProperty/doc/src/Makefile b/lib/cosProperty/doc/src/Makefile
index 9bfacc27bd..1624479808 100644
--- a/lib/cosProperty/doc/src/Makefile
+++ b/lib/cosProperty/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2000-2011. All Rights Reserved.
+# Copyright Ericsson AB 2000-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/cosProperty/src/Makefile b/lib/cosProperty/src/Makefile
index cb2c56743c..d7b75d8bc5 100644
--- a/lib/cosProperty/src/Makefile
+++ b/lib/cosProperty/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2000-2011. All Rights Reserved.
+# Copyright Ericsson AB 2000-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -151,10 +151,10 @@ clean:
rm -f errs core *~
$(APP_TARGET): $(APP_SRC)
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
docs:
@@ -162,9 +162,9 @@ docs:
# Special Build Targets
# ----------------------------------------------------
IDL-GENERATED: CosProperty.idl
- erlc $(ERL_IDL_FLAGS) +'{cfgfile,"CosProperty.cfg"}' CosProperty.idl
- mv $(LOCAL_HRL_FILES) $(EXTERNAL_INC_PATH)
- >IDL-GENERATED
+ $(gen_verbose)erlc $(ERL_IDL_FLAGS) +'{cfgfile,"CosProperty.cfg"}' CosProperty.idl
+ $(V_at)mv $(LOCAL_HRL_FILES) $(EXTERNAL_INC_PATH)
+ $(V_at)>IDL-GENERATED
$(GEN_FILES): IDL-GENERATED
diff --git a/lib/cosProperty/test/Makefile b/lib/cosProperty/test/Makefile
index d85b8a655a..ba634c77cf 100644
--- a/lib/cosProperty/test/Makefile
+++ b/lib/cosProperty/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2000-2011. All Rights Reserved.
+# Copyright Ericsson AB 2000-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/cosTime/doc/src/Makefile b/lib/cosTime/doc/src/Makefile
index f89c022c4a..2ed55a3bfa 100644
--- a/lib/cosTime/doc/src/Makefile
+++ b/lib/cosTime/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2000-2011. All Rights Reserved.
+# Copyright Ericsson AB 2000-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/cosTime/src/Makefile b/lib/cosTime/src/Makefile
index 35f16edcef..0ab2b414ce 100644
--- a/lib/cosTime/src/Makefile
+++ b/lib/cosTime/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2000-2011. All Rights Reserved.
+# Copyright Ericsson AB 2000-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -166,10 +166,10 @@ clean:
rm -f errs core *~
$(APP_TARGET): $(APP_SRC)
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
docs:
@@ -177,13 +177,13 @@ docs:
# Special Build Targets
# ----------------------------------------------------
IDL-GENERATED: TimeBase.idl CosTime.idl CosTimerEvent.idl
- erlc $(ERL_IDL_FLAGS) TimeBase.idl
- mv $(GEN_TIMEBASE_HRL_FILES) $(EXTERNAL_INC_PATH)
- erlc $(ERL_IDL_FLAGS) +'{cfgfile,"CosTime.cfg"}' CosTime.idl
- mv $(GEN_COSTIME_HRL_FILES) $(EXTERNAL_INC_PATH)
- erlc $(ERL_IDL_FLAGS) +'{cfgfile,"CosTimerEvent.cfg"}' CosTimerEvent.idl
- mv $(GEN_COSTIMEREVENT_HRL_FILES) $(EXTERNAL_INC_PATH)
- >IDL-GENERATED
+ $(gen_verbose)erlc $(ERL_IDL_FLAGS) TimeBase.idl
+ $(V_at)mv $(GEN_TIMEBASE_HRL_FILES) $(EXTERNAL_INC_PATH)
+ $(V_at)erlc $(ERL_IDL_FLAGS) +'{cfgfile,"CosTime.cfg"}' CosTime.idl
+ $(V_at)mv $(GEN_COSTIME_HRL_FILES) $(EXTERNAL_INC_PATH)
+ $(V_at)erlc $(ERL_IDL_FLAGS) +'{cfgfile,"CosTimerEvent.cfg"}' CosTimerEvent.idl
+ $(V_at)mv $(GEN_COSTIMEREVENT_HRL_FILES) $(EXTERNAL_INC_PATH)
+ $(V_at)>IDL-GENERATED
$(GEN_FILES): IDL-GENERATED
diff --git a/lib/cosTime/test/Makefile b/lib/cosTime/test/Makefile
index ab4d140537..89d5a4a38e 100644
--- a/lib/cosTime/test/Makefile
+++ b/lib/cosTime/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2000-2011. All Rights Reserved.
+# Copyright Ericsson AB 2000-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/cosTransactions/doc/src/Makefile b/lib/cosTransactions/doc/src/Makefile
index 4edad9829b..7247c0dec7 100644
--- a/lib/cosTransactions/doc/src/Makefile
+++ b/lib/cosTransactions/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2011. All Rights Reserved.
+# Copyright Ericsson AB 1999-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/cosTransactions/examples/Makefile b/lib/cosTransactions/examples/Makefile
index 0408cad75d..348f506e2e 100644
--- a/lib/cosTransactions/examples/Makefile
+++ b/lib/cosTransactions/examples/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2009. All Rights Reserved.
+# Copyright Ericsson AB 1999-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/cosTransactions/src/Makefile b/lib/cosTransactions/src/Makefile
index 0922a21641..7b4a9cf858 100644
--- a/lib/cosTransactions/src/Makefile
+++ b/lib/cosTransactions/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2011. All Rights Reserved.
+# Copyright Ericsson AB 1999-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -145,10 +145,10 @@ clean:
rm -f errs core *~
$(APP_TARGET): $(APP_SRC)
- sed -e 's;%VSN%;$(VSN);' $(APP_SRC) > $(APP_TARGET)
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $(APP_SRC) > $(APP_TARGET)
$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
docs:
@@ -156,9 +156,9 @@ docs:
# Special Build Targets
# ----------------------------------------------------
IDL-GENERATED: CosTransactions.idl
- erlc $(ERL_IDL_FLAGS) +'{cfgfile,"CosTransactions.cfg"}' CosTransactions.idl
- mv $(GEN_HRL_FILES) $(EXTERNAL_INC_PATH)
- >IDL-GENERATED
+ $(gen_verbose)erlc $(ERL_IDL_FLAGS) +'{cfgfile,"CosTransactions.cfg"}' CosTransactions.idl
+ $(V_at)mv $(GEN_HRL_FILES) $(EXTERNAL_INC_PATH)
+ $(V_at)>IDL-GENERATED
$(GEN_FILES): IDL-GENERATED
diff --git a/lib/cosTransactions/test/Makefile b/lib/cosTransactions/test/Makefile
index 80f4b6c624..53ff93ba16 100644
--- a/lib/cosTransactions/test/Makefile
+++ b/lib/cosTransactions/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2011. All Rights Reserved.
+# Copyright Ericsson AB 1999-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/crypto/c_src/Makefile.in b/lib/crypto/c_src/Makefile.in
index cfc9a447e0..a20ddff05c 100644
--- a/lib/crypto/c_src/Makefile.in
+++ b/lib/crypto/c_src/Makefile.in
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2011. All Rights Reserved.
+# Copyright Ericsson AB 1999-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -59,8 +59,6 @@ TYPE_FLAGS = $(CFLAGS)
endif
endif
-ALL_CFLAGS = $(TYPE_FLAGS) $(INCLUDES)
-
# ----------------------------------------------------
# Release directory specification
# ----------------------------------------------------
@@ -69,13 +67,16 @@ RELSYSDIR = $(RELEASE_PATH)/lib/crypto-$(VSN)
# ----------------------------------------------------
# Misc Macros
# ----------------------------------------------------
-OBJS = $(OBJDIR)/crypto$(TYPEMARKER).o
+CRYPTO_OBJS = $(OBJDIR)/crypto$(TYPEMARKER).o
+CALLBACK_OBJS = $(OBJDIR)/crypto_callback$(TYPEMARKER).o
NIF_MAKEFILE = $(PRIVDIR)/Makefile
ifeq ($(findstring win32,$(TARGET)), win32)
NIF_LIB = $(LIBDIR)/crypto$(TYPEMARKER).dll
+CALLBACK_LIB = $(LIBDIR)/crypto_callback$(TYPEMARKER).dll
else
NIF_LIB = $(LIBDIR)/crypto$(TYPEMARKER).so
+CALLBACK_LIB = $(LIBDIR)/crypto_callback$(TYPEMARKER).so
endif
ifeq ($(HOST_OS),)
@@ -86,43 +87,69 @@ DYNAMIC_CRYPTO_LIB=@SSL_DYNAMIC_ONLY@
ifeq ($(DYNAMIC_CRYPTO_LIB),yes)
SSL_DED_LD_RUNTIME_LIBRARY_PATH = @SSL_DED_LD_RUNTIME_LIBRARY_PATH@
CRYPTO_LINK_LIB=$(SSL_DED_LD_RUNTIME_LIBRARY_PATH) -L$(SSL_LIBDIR) -l$(SSL_CRYPTO_LIBNAME)
+EXTRA_FLAGS = -DHAVE_DYNAMIC_CRYPTO_LIB
else
SSL_DED_LD_RUNTIME_LIBRARY_PATH=
CRYPTO_LINK_LIB=$(SSL_LIBDIR)/lib$(SSL_CRYPTO_LIBNAME).a
+EXTRA_FLAGS =
+CRYPTO_OBJS := $(CRYPTO_OBJS) $(CALLBACK_OBJS)
+CALLBACK_OBJS =
+CALLBACK_LIB =
endif
+ALL_CFLAGS = $(TYPE_FLAGS) $(EXTRA_FLAGS) $(INCLUDES)
+
# ----------------------------------------------------
# Targets
# ----------------------------------------------------
_create_dirs := $(shell mkdir -p $(OBJDIR) $(LIBDIR))
-debug opt valgrind: $(NIF_LIB)
+debug opt valgrind: $(NIF_LIB) $(CALLBACK_LIB)
$(OBJDIR)/%$(TYPEMARKER).o: %.c
- $(INSTALL_DIR) $(OBJDIR)
- $(CC) -c -o $@ $(ALL_CFLAGS) $<
+ $(V_at)$(INSTALL_DIR) $(OBJDIR)
+ $(V_CC) -c -o $@ $(ALL_CFLAGS) $<
+
+$(LIBDIR)/crypto$(TYPEMARKER).so: $(CRYPTO_OBJS)
+ $(V_at)$(INSTALL_DIR) $(LIBDIR)
+ $(V_LD) $(LDFLAGS) -o $@ $^ $(LDLIBS) $(CRYPTO_LINK_LIB)
-$(LIBDIR)/crypto$(TYPEMARKER).so: $(OBJS)
- $(INSTALL_DIR) $(LIBDIR)
- $(LD) $(LDFLAGS) -o $@ $^ $(LDLIBS) $(CRYPTO_LINK_LIB)
+$(LIBDIR)/crypto$(TYPEMARKER).dll: $(CRYPTO_OBJS)
+ $(V_at)$(INSTALL_DIR) $(LIBDIR)
+ $(V_LD) $(LDFLAGS) -o $@ $(SSL_DED_LD_RUNTIME_LIBRARY_PATH) -L$(SSL_LIBDIR) $(CRYPTO_OBJS) -l$(SSL_CRYPTO_LIBNAME) -l$(SSL_SSL_LIBNAME)
+
+ifeq ($(DYNAMIC_CRYPTO_LIB),yes)
+$(LIBDIR)/crypto_callback$(TYPEMARKER).so: $(CALLBACK_OBJS)
+ $(INSTALL_DIR) $(LIBDIR)
+ $(LD) $(LDFLAGS) -o $@ $^ $(LDLIBS)
-$(LIBDIR)/crypto$(TYPEMARKER).dll: $(OBJS)
+$(LIBDIR)/crypto_callback$(TYPEMARKER).dll: $(CALLBACK_OBJS)
$(INSTALL_DIR) $(LIBDIR)
- $(LD) $(LDFLAGS) -o $@ $(SSL_DED_LD_RUNTIME_LIBRARY_PATH) -L$(SSL_LIBDIR) $(OBJS) -l$(SSL_CRYPTO_LIBNAME) -l$(SSL_SSL_LIBNAME)
+ $(LD) $(LDFLAGS) -o $@ $(CALLBACK_OBJS)
+endif
+
clean:
ifeq ($(findstring win32,$(TARGET)), win32)
rm -f $(LIBDIR)/crypto.dll
rm -f $(LIBDIR)/crypto.debug.dll
+ rm -f $(LIBDIR)/crypto_callback.dll
+ rm -f $(LIBDIR)/crypto_callback.debug.dll
else
rm -f $(LIBDIR)/crypto.so
rm -f $(LIBDIR)/crypto.debug.so
rm -f $(LIBDIR)/crypto.valgrind.so
+ rm -f $(LIBDIR)/crypto_callback.so
+ rm -f $(LIBDIR)/crypto_callback.debug.so
+ rm -f $(LIBDIR)/crypto_callback.valgrind.so
endif
rm -f $(OBJDIR)/crypto.o
rm -f $(OBJDIR)/crypto.debug.o
rm -f $(OBJDIR)/crypto.valgrind.o
+ rm -f $(OBJDIR)/crypto_callback.o
+ rm -f $(OBJDIR)/crypto_callback.debug.o
+ rm -f $(OBJDIR)/crypto_callback.valgrind.o
rm -f core *~
docs:
@@ -136,8 +163,12 @@ release_spec: opt
$(INSTALL_DIR) "$(RELSYSDIR)/priv/obj"
$(INSTALL_DIR) "$(RELSYSDIR)/priv/lib"
$(INSTALL_DATA) $(NIF_MAKEFILE) "$(RELSYSDIR)/priv/obj"
- $(INSTALL_PROGRAM) $(OBJS) "$(RELSYSDIR)/priv/obj"
+ $(INSTALL_PROGRAM) $(CRYPTO_OBJS) "$(RELSYSDIR)/priv/obj"
$(INSTALL_PROGRAM) $(NIF_LIB) "$(RELSYSDIR)/priv/lib"
+ifeq ($(DYNAMIC_CRYPTO_LIB),yes)
+ $(INSTALL_PROGRAM) $(CALLBACK_OBJS) "$(RELSYSDIR)/priv/obj"
+ $(INSTALL_PROGRAM) $(CALLBACK_LIB) "$(RELSYSDIR)/priv/lib"
+endif
release_docs_spec:
diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c
index 4be593e208..e77e5fb8f0 100644
--- a/lib/crypto/c_src/crypto.c
+++ b/lib/crypto/c_src/crypto.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2010-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2010-2012. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -44,6 +44,7 @@
#include <openssl/md5.h>
#include <openssl/md4.h>
#include <openssl/sha.h>
+#include <openssl/ripemd.h>
#include <openssl/bn.h>
#include <openssl/objects.h>
#include <openssl/rc4.h>
@@ -53,6 +54,12 @@
#include <openssl/evp.h>
#include <openssl/hmac.h>
+#include "crypto_callback.h"
+
+#if OPENSSL_VERSION_NUMBER >= 0x00908000L && !defined(OPENSSL_NO_SHA224) && defined(NID_sha224)\
+ && !defined(OPENSSL_NO_SHA256) /* disabled like this in my sha.h (?) */
+# define HAVE_SHA224
+#endif
#if OPENSSL_VERSION_NUMBER >= 0x00908000L && !defined(OPENSSL_NO_SHA256) && defined(NID_sha256)
# define HAVE_SHA256
#endif
@@ -63,6 +70,9 @@
#if OPENSSL_VERSION_NUMBER >= 0x00908000L && !defined(OPENSSL_NO_SHA512) && defined(NID_sha512)
# define HAVE_SHA512
#endif
+#if OPENSSL_VERSION_NUMBER >= 0x0090705FL
+# define HAVE_DES_ede3_cfb_encrypt
+#endif
#ifdef VALGRIND
# include <valgrind/memcheck.h>
@@ -121,7 +131,6 @@
/* NIF interface declarations */
static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info);
-static int reload(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info);
static int upgrade(ErlNifEnv* env, void** priv_data, void** old_priv_data, ERL_NIF_TERM load_info);
static void unload(ErlNifEnv* env, void* priv_data);
@@ -131,14 +140,26 @@ static ERL_NIF_TERM md5(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM md5_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM md5_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM md5_final(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM ripemd160(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM ripemd160_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM ripemd160_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM ripemd160_final(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM sha(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM sha_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM sha_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM sha_final(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM sha224_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM sha224_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM sha224_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM sha224_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM sha256_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM sha256_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM sha256_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM sha256_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM sha384_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM sha384_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM sha384_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM sha384_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM sha512_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM sha512_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM sha512_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
@@ -149,6 +170,10 @@ static ERL_NIF_TERM md4_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv
static ERL_NIF_TERM md4_final(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM md5_mac_n(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM sha_mac_n(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM sha224_mac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM sha256_mac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM sha384_mac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM sha512_mac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM hmac_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM hmac_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM hmac_final(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
@@ -156,7 +181,7 @@ static ERL_NIF_TERM des_cbc_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM a
static ERL_NIF_TERM des_cfb_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
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 des_ede3_cfb_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM des_ede3_cfb_crypt_nif(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 aes_ctr_stream_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
@@ -188,25 +213,35 @@ static ERL_NIF_TERM bf_ecb_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar
static ERL_NIF_TERM blowfish_ofb64_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
-/* openssl callbacks */
-#ifdef OPENSSL_THREADS
-static void locking_function(int mode, int n, const char *file, int line);
-static unsigned long id_function(void);
-static struct CRYPTO_dynlock_value* dyn_create_function(const char *file,
- int line);
-static void dyn_lock_function(int mode, struct CRYPTO_dynlock_value* ptr,
- const char *file, int line);
-static void dyn_destroy_function(struct CRYPTO_dynlock_value *ptr,
- const char *file, int line);
-#endif /* OPENSSL_THREADS */
/* helpers */
+static void init_digest_types(ErlNifEnv* env);
static void hmac_md5(unsigned char *key, int klen,
unsigned char *dbuf, int dlen,
unsigned char *hmacbuf);
static void hmac_sha1(unsigned char *key, int klen,
unsigned char *dbuf, int dlen,
unsigned char *hmacbuf);
+#ifdef HAVE_SHA224
+static void hmac_sha224(unsigned char *key, int klen,
+ unsigned char *dbuf, int dlen,
+ unsigned char *hmacbuf);
+#endif
+#ifdef HAVE_SHA256
+static void hmac_sha256(unsigned char *key, int klen,
+ unsigned char *dbuf, int dlen,
+ unsigned char *hmacbuf);
+#endif
+#ifdef HAVE_SHA384
+static void hmac_sha384(unsigned char *key, int klen,
+ unsigned char *dbuf, int dlen,
+ unsigned char *hmacbuf);
+#endif
+#ifdef HAVE_SHA512
+static void hmac_sha512(unsigned char *key, int klen,
+ unsigned char *dbuf, int dlen,
+ unsigned char *hmacbuf);
+#endif
static int library_refc = 0; /* number of users of this dynamic library */
@@ -216,14 +251,26 @@ static ErlNifFunc nif_funcs[] = {
{"md5_init", 0, md5_init},
{"md5_update", 2, md5_update},
{"md5_final", 1, md5_final},
+ {"ripemd160", 1, ripemd160},
+ {"ripemd160_init", 0, ripemd160_init},
+ {"ripemd160_update", 2, ripemd160_update},
+ {"ripemd160_final", 1, ripemd160_final},
{"sha", 1, sha},
{"sha_init", 0, sha_init},
{"sha_update", 2, sha_update},
{"sha_final", 1, sha_final},
+ {"sha224_nif", 1, sha224_nif},
+ {"sha224_init_nif", 0, sha224_init_nif},
+ {"sha224_update_nif", 2, sha224_update_nif},
+ {"sha224_final_nif", 1, sha224_final_nif},
{"sha256_nif", 1, sha256_nif},
{"sha256_init_nif", 0, sha256_init_nif},
{"sha256_update_nif", 2, sha256_update_nif},
{"sha256_final_nif", 1, sha256_final_nif},
+ {"sha384_nif", 1, sha384_nif},
+ {"sha384_init_nif", 0, sha384_init_nif},
+ {"sha384_update_nif", 2, sha384_update_nif},
+ {"sha384_final_nif", 1, sha384_final_nif},
{"sha512_nif", 1, sha512_nif},
{"sha512_init_nif", 0, sha512_init_nif},
{"sha512_update_nif", 2, sha512_update_nif},
@@ -234,6 +281,10 @@ static ErlNifFunc nif_funcs[] = {
{"md4_final", 1, md4_final},
{"md5_mac_n", 3, md5_mac_n},
{"sha_mac_n", 3, sha_mac_n},
+ {"sha224_mac_nif", 3, sha224_mac_nif},
+ {"sha256_mac_nif", 3, sha256_mac_nif},
+ {"sha384_mac_nif", 3, sha384_mac_nif},
+ {"sha512_mac_nif", 3, sha512_mac_nif},
{"hmac_init", 2, hmac_init},
{"hmac_update", 2, hmac_update},
{"hmac_final", 1, hmac_final},
@@ -242,7 +293,7 @@ static ErlNifFunc nif_funcs[] = {
{"des_cfb_crypt", 4, des_cfb_crypt},
{"des_ecb_crypt", 3, des_ecb_crypt},
{"des_ede3_cbc_crypt", 6, des_ede3_cbc_crypt},
- {"des_ede3_cfb_crypt", 6, des_ede3_cfb_crypt},
+ {"des_ede3_cfb_crypt_nif", 6, des_ede3_cfb_crypt_nif},
{"aes_cfb_128_crypt", 4, aes_cfb_128_crypt},
{"aes_ctr_encrypt", 3, aes_ctr_encrypt},
{"aes_ctr_decrypt", 3, aes_ctr_encrypt},
@@ -276,7 +327,7 @@ static ErlNifFunc nif_funcs[] = {
{"blowfish_ofb64_encrypt", 3, blowfish_ofb64_encrypt}
};
-ERL_NIF_INIT(crypto,nif_funcs,load,reload,upgrade,unload)
+ERL_NIF_INIT(crypto,nif_funcs,load,NULL,upgrade,unload)
#define MD5_CTX_LEN (sizeof(MD5_CTX))
@@ -284,22 +335,26 @@ ERL_NIF_INIT(crypto,nif_funcs,load,reload,upgrade,unload)
#define MD5_LEN_96 12
#define MD4_CTX_LEN (sizeof(MD4_CTX))
#define MD4_LEN 16
+#define RIPEMD160_CTX_LEN (sizeof(RIPEMD160_CTX))
+#define RIPEMD160_LEN 20
#define SHA_CTX_LEN (sizeof(SHA_CTX))
#define SHA_LEN 20
#define SHA_LEN_96 12
+#define SHA224_LEN (224/8)
#define SHA256_LEN (256/8)
#define SHA384_LEN (384/8)
#define SHA512_LEN (512/8)
#define HMAC_INT_LEN 64
+#define HMAC_INT2_LEN 128
#define HMAC_IPAD 0x36
#define HMAC_OPAD 0x5c
-static ErlNifRWLock** lock_vec = NULL; /* Static locks used by openssl */
static ERL_NIF_TERM atom_true;
static ERL_NIF_TERM atom_false;
static ERL_NIF_TERM atom_sha;
+static ERL_NIF_TERM atom_sha224;
static ERL_NIF_TERM atom_sha256;
static ERL_NIF_TERM atom_sha384;
static ERL_NIF_TERM atom_sha512;
@@ -320,60 +375,67 @@ static ERL_NIF_TERM atom_check_failed;
static ERL_NIF_TERM atom_unknown;
static ERL_NIF_TERM atom_none;
static ERL_NIF_TERM atom_notsup;
+static ERL_NIF_TERM atom_digest;
+/*
+#define PRINTF_ERR0(FMT) enif_fprintf(stderr, FMT "\n")
+#define PRINTF_ERR1(FMT, A1) enif_fprintf(stderr, FMT "\n", A1)
+*/
+#define PRINTF_ERR0(FMT)
+#define PRINTF_ERR1(FMT,A1)
-static int is_ok_load_info(ErlNifEnv* env, ERL_NIF_TERM load_info)
+#ifdef HAVE_DYNAMIC_CRYPTO_LIB
+static int change_basename(char* buf, int bufsz, const char* newfile)
{
- int i;
- return enif_get_int(env,load_info,&i) && i == 101;
-}
-static void* crypto_alloc(size_t size)
-{
- return enif_alloc(size);
+ char* p = strrchr(buf, '/');
+ p = (p == NULL) ? buf : p + 1;
+
+ if ((p - buf) + strlen(newfile) >= bufsz) {
+ PRINTF_ERR0("CRYPTO: lib name too long");
+ return 0;
+ }
+ strcpy(p, newfile);
+ return 1;
}
-static void* crypto_realloc(void* ptr, size_t size)
+
+static void error_handler(void* null, const char* errstr)
{
- return enif_realloc(ptr, size);
-}
-static void crypto_free(void* ptr)
-{
- enif_free(ptr);
+ PRINTF_ERR1("CRYPTO LOADING ERROR: '%s'", errstr);
}
+#endif /* HAVE_DYNAMIC_CRYPTO_LIB */
-static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
+static int init(ErlNifEnv* env, ERL_NIF_TERM load_info)
{
ErlNifSysInfo sys_info;
- CRYPTO_set_mem_functions(crypto_alloc, crypto_realloc, crypto_free);
-
- if (!is_ok_load_info(env, load_info)) {
- return -1;
+ get_crypto_callbacks_t* funcp;
+ struct crypto_callbacks* ccb;
+ int nlocks = 0;
+ int tpl_arity;
+ const ERL_NIF_TERM* tpl_array;
+ int vernum;
+ char lib_buf[1000];
+
+ /* load_info: {201, "/full/path/of/this/library"} */
+ if (!enif_get_tuple(env, load_info, &tpl_arity, &tpl_array)
+ || tpl_arity != 2
+ || !enif_get_int(env, tpl_array[0], &vernum)
+ || vernum != 201
+ || enif_get_string(env, tpl_array[1], lib_buf, sizeof(lib_buf), ERL_NIF_LATIN1) <= 0) {
+
+ PRINTF_ERR1("CRYPTO: Invalid load_info '%T'", load_info);
+ return 0;
}
-
-#ifdef OPENSSL_THREADS
- enif_system_info(&sys_info, sizeof(sys_info));
-
- if (sys_info.scheduler_threads > 1) {
- int i;
- 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));
-
- for (i=CRYPTO_num_locks()-1; i>=0; --i) {
- lock_vec[i] = enif_rwlock_create("crypto_stat");
- if (lock_vec[i]==NULL) return -1;
- }
- CRYPTO_set_locking_callback(locking_function);
- CRYPTO_set_id_callback(id_function);
- CRYPTO_set_dynlock_create_callback(dyn_create_function);
- CRYPTO_set_dynlock_lock_callback(dyn_lock_function);
- CRYPTO_set_dynlock_destroy_callback(dyn_destroy_function);
+ if (library_refc > 0) {
+ /* Repeated loading of this library (module upgrade).
+ * Atoms and callbacks are already set, we are done.
+ */
+ return 1;
}
- /* else no need for locks */
-#endif /* OPENSSL_THREADS */
atom_true = enif_make_atom(env,"true");
atom_false = enif_make_atom(env,"false");
atom_sha = enif_make_atom(env,"sha");
+ atom_sha224 = enif_make_atom(env,"sha224");
atom_sha256 = enif_make_atom(env,"sha256");
atom_sha384 = enif_make_atom(env,"sha384");
atom_sha512 = enif_make_atom(env,"sha512");
@@ -393,38 +455,79 @@ static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
atom_unknown = enif_make_atom(env,"unknown");
atom_none = enif_make_atom(env,"none");
atom_notsup = enif_make_atom(env,"notsup");
+ atom_digest = enif_make_atom(env,"digest");
- *priv_data = NULL;
- library_refc++;
- return 0;
-}
+ init_digest_types(env);
-static int reload(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
-{
- if (*priv_data != NULL) {
- return -1; /* Don't know how to do that */
+#ifdef HAVE_DYNAMIC_CRYPTO_LIB
+ {
+ void* handle;
+ if (!change_basename(lib_buf, sizeof(lib_buf), "crypto_callback")) {
+ return 0;
+ }
+ if (!(handle = enif_dlopen(lib_buf, &error_handler, NULL))) {
+ return 0;
+ }
+ if (!(funcp = (get_crypto_callbacks_t*) enif_dlsym(handle, "get_crypto_callbacks",
+ &error_handler, NULL))) {
+ return 0;
+ }
}
- if (library_refc == 0) {
- /* No support for real library upgrade. The tricky thing is to know
- when to (re)set the callbacks for allocation and locking. */
- return -2;
+#else /* !HAVE_DYNAMIC_CRYPTO_LIB */
+ funcp = &get_crypto_callbacks;
+#endif
+
+#ifdef OPENSSL_THREADS
+ enif_system_info(&sys_info, sizeof(sys_info));
+ if (sys_info.scheduler_threads > 1) {
+ nlocks = CRYPTO_num_locks();
}
- if (!is_ok_load_info(env, load_info)) {
+ /* else no need for locks */
+#endif
+
+ ccb = (*funcp)(nlocks);
+
+ if (!ccb || ccb->sizeof_me != sizeof(*ccb)) {
+ PRINTF_ERR0("Invalid 'crypto_callbacks'");
+ return 0;
+ }
+
+ CRYPTO_set_mem_functions(ccb->crypto_alloc, ccb->crypto_realloc, ccb->crypto_free);
+
+#ifdef OPENSSL_THREADS
+ if (nlocks > 0) {
+ CRYPTO_set_locking_callback(ccb->locking_function);
+ CRYPTO_set_id_callback(ccb->id_function);
+ CRYPTO_set_dynlock_create_callback(ccb->dyn_create_function);
+ CRYPTO_set_dynlock_lock_callback(ccb->dyn_lock_function);
+ CRYPTO_set_dynlock_destroy_callback(ccb->dyn_destroy_function);
+ }
+#endif /* OPENSSL_THREADS */
+ return 1;
+}
+
+static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
+{
+ if (!init(env, load_info)) {
return -1;
}
- return 0;
+
+ *priv_data = NULL;
+ library_refc++;
+ return 0;
}
static int upgrade(ErlNifEnv* env, void** priv_data, void** old_priv_data,
ERL_NIF_TERM load_info)
{
- int i;
if (*old_priv_data != NULL) {
return -1; /* Don't know how to do that */
}
- i = reload(env,priv_data,load_info);
- if (i != 0) {
- return i;
+ if (*priv_data != NULL) {
+ return -1; /* Don't know how to do that */
+ }
+ if (!init(env, load_info)) {
+ return -1;
}
library_refc++;
return 0;
@@ -432,20 +535,7 @@ static int upgrade(ErlNifEnv* env, void** priv_data, void** old_priv_data,
static void unload(ErlNifEnv* env, void* priv_data)
{
- if (--library_refc <= 0) {
- CRYPTO_cleanup_all_ex_data();
-
- if (lock_vec != NULL) {
- int i;
- for (i=CRYPTO_num_locks()-1; i>=0; --i) {
- if (lock_vec[i] != NULL) {
- enif_rwlock_destroy(lock_vec[i]);
- }
- }
- enif_free(lock_vec);
- }
- }
- /*else NIF library still used by other (new) module code */
+ --library_refc;
}
static ERL_NIF_TERM info_lib(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
@@ -457,12 +547,21 @@ static ERL_NIF_TERM info_lib(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]
const char* ver = SSLeay_version(SSLEAY_VERSION);
unsigned ver_sz = strlen(ver);
ERL_NIF_TERM name_term, ver_term;
+ int ver_num = OPENSSL_VERSION_NUMBER;
+ /* R16:
+ * Ignore library version number from SSLeay() and instead show header
+ * version. Otherwise user might try to call a function that is implemented
+ * by a newer library but not supported by the headers used at compile time.
+ * Example: DES_ede3_cfb_encrypt in 0.9.7i but not in 0.9.7d.
+ *
+ * Version string is still from library though.
+ */
memcpy(enif_make_new_binary(env, name_sz, &name_term), libname, name_sz);
memcpy(enif_make_new_binary(env, ver_sz, &ver_term), ver, ver_sz);
return enif_make_list1(env, enif_make_tuple3(env, name_term,
- enif_make_int(env, SSLeay()),
+ enif_make_int(env, ver_num),
ver_term));
}
@@ -512,6 +611,53 @@ static ERL_NIF_TERM md5_final(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[
return ret;
}
+static ERL_NIF_TERM ripemd160(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (Data) */
+ ErlNifBinary ibin;
+ ERL_NIF_TERM ret;
+
+ if (!enif_inspect_iolist_as_binary(env, argv[0], &ibin)) {
+ return enif_make_badarg(env);
+ }
+ RIPEMD160((unsigned char *) ibin.data, ibin.size,
+ enif_make_new_binary(env,RIPEMD160_LEN, &ret));
+ return ret;
+}
+static ERL_NIF_TERM ripemd160_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* () */
+ ERL_NIF_TERM ret;
+ RIPEMD160_Init((RIPEMD160_CTX *) enif_make_new_binary(env, RIPEMD160_CTX_LEN, &ret));
+ return ret;
+}
+static ERL_NIF_TERM ripemd160_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (Context, Data) */
+ RIPEMD160_CTX* new_ctx;
+ ErlNifBinary ctx_bin, data_bin;
+ ERL_NIF_TERM ret;
+ if (!enif_inspect_binary(env, argv[0], &ctx_bin)
+ || ctx_bin.size != RIPEMD160_CTX_LEN
+ || !enif_inspect_iolist_as_binary(env, argv[1], &data_bin)) {
+ return enif_make_badarg(env);
+ }
+ new_ctx = (RIPEMD160_CTX*) enif_make_new_binary(env,RIPEMD160_CTX_LEN, &ret);
+ memcpy(new_ctx, ctx_bin.data, RIPEMD160_CTX_LEN);
+ RIPEMD160_Update(new_ctx, data_bin.data, data_bin.size);
+ return ret;
+}
+static ERL_NIF_TERM ripemd160_final(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (Context) */
+ ErlNifBinary ctx_bin;
+ RIPEMD160_CTX ctx_clone;
+ ERL_NIF_TERM ret;
+ if (!enif_inspect_binary(env, argv[0], &ctx_bin) || ctx_bin.size != RIPEMD160_CTX_LEN) {
+ return enif_make_badarg(env);
+ }
+ memcpy(&ctx_clone, ctx_bin.data, RIPEMD160_CTX_LEN); /* writable */
+ RIPEMD160_Final(enif_make_new_binary(env, RIPEMD160_LEN, &ret), &ctx_clone);
+ return ret;
+}
+
+
static ERL_NIF_TERM sha(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{/* (Data) */
ErlNifBinary ibin;
@@ -557,6 +703,67 @@ static ERL_NIF_TERM sha_final(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[
return ret;
}
+static ERL_NIF_TERM sha224_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (Data) */
+#ifdef HAVE_SHA224
+ ErlNifBinary ibin;
+ ERL_NIF_TERM ret;
+
+ if (!enif_inspect_iolist_as_binary(env, argv[0], &ibin)) {
+ return enif_make_badarg(env);
+ }
+ SHA224((unsigned char *) ibin.data, ibin.size,
+ enif_make_new_binary(env,SHA224_LEN, &ret));
+ return ret;
+#else
+ return atom_notsup;
+#endif
+}
+static ERL_NIF_TERM sha224_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* () */
+#ifdef HAVE_SHA224
+ ERL_NIF_TERM ret;
+ SHA224_Init((SHA256_CTX *) enif_make_new_binary(env, sizeof(SHA256_CTX), &ret));
+ return ret;
+#else
+ return atom_notsup;
+#endif
+}
+static ERL_NIF_TERM sha224_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (Context, Data) */
+#ifdef HAVE_SHA224
+ SHA256_CTX* new_ctx;
+ ErlNifBinary ctx_bin, data_bin;
+ ERL_NIF_TERM ret;
+ if (!enif_inspect_binary(env, argv[0], &ctx_bin) || ctx_bin.size != sizeof(SHA256_CTX)
+ || !enif_inspect_iolist_as_binary(env, argv[1], &data_bin)) {
+ return enif_make_badarg(env);
+ }
+ new_ctx = (SHA256_CTX*) enif_make_new_binary(env,sizeof(SHA256_CTX), &ret);
+ memcpy(new_ctx, ctx_bin.data, sizeof(SHA256_CTX));
+ SHA224_Update(new_ctx, data_bin.data, data_bin.size);
+ return ret;
+#else
+ return atom_notsup;
+#endif
+}
+static ERL_NIF_TERM sha224_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (Context) */
+#ifdef HAVE_SHA224
+ ErlNifBinary ctx_bin;
+ SHA256_CTX ctx_clone;
+ ERL_NIF_TERM ret;
+ if (!enif_inspect_binary(env, argv[0], &ctx_bin) || ctx_bin.size != sizeof(SHA256_CTX)) {
+ return enif_make_badarg(env);
+ }
+ memcpy(&ctx_clone, ctx_bin.data, sizeof(SHA256_CTX)); /* writable */
+ SHA224_Final(enif_make_new_binary(env, SHA224_LEN, &ret), &ctx_clone);
+ return ret;
+#else
+ return atom_notsup;
+#endif
+}
+
static ERL_NIF_TERM sha256_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{/* (Data) */
#ifdef HAVE_SHA256
@@ -618,6 +825,67 @@ static ERL_NIF_TERM sha256_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TER
#endif
}
+static ERL_NIF_TERM sha384_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (Data) */
+#ifdef HAVE_SHA384
+ ErlNifBinary ibin;
+ ERL_NIF_TERM ret;
+
+ if (!enif_inspect_iolist_as_binary(env, argv[0], &ibin)) {
+ return enif_make_badarg(env);
+ }
+ SHA384((unsigned char *) ibin.data, ibin.size,
+ enif_make_new_binary(env,SHA384_LEN, &ret));
+ return ret;
+#else
+ return atom_notsup;
+#endif
+}
+static ERL_NIF_TERM sha384_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* () */
+#ifdef HAVE_SHA384
+ ERL_NIF_TERM ret;
+ SHA384_Init((SHA512_CTX *) enif_make_new_binary(env, sizeof(SHA512_CTX), &ret));
+ return ret;
+#else
+ return atom_notsup;
+#endif
+}
+static ERL_NIF_TERM sha384_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (Context, Data) */
+#ifdef HAVE_SHA384
+ SHA512_CTX* new_ctx;
+ ErlNifBinary ctx_bin, data_bin;
+ ERL_NIF_TERM ret;
+ if (!enif_inspect_binary(env, argv[0], &ctx_bin) || ctx_bin.size != sizeof(SHA512_CTX)
+ || !enif_inspect_iolist_as_binary(env, argv[1], &data_bin)) {
+ return enif_make_badarg(env);
+ }
+ new_ctx = (SHA512_CTX*) enif_make_new_binary(env,sizeof(SHA512_CTX), &ret);
+ memcpy(new_ctx, ctx_bin.data, sizeof(SHA512_CTX));
+ SHA384_Update(new_ctx, data_bin.data, data_bin.size);
+ return ret;
+#else
+ return atom_notsup;
+#endif
+}
+static ERL_NIF_TERM sha384_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (Context) */
+#ifdef HAVE_SHA384
+ ErlNifBinary ctx_bin;
+ SHA512_CTX ctx_clone;
+ ERL_NIF_TERM ret;
+ if (!enif_inspect_binary(env, argv[0], &ctx_bin) || ctx_bin.size != sizeof(SHA512_CTX)) {
+ return enif_make_badarg(env);
+ }
+ memcpy(&ctx_clone, ctx_bin.data, sizeof(SHA512_CTX)); /* writable */
+ SHA384_Final(enif_make_new_binary(env, SHA384_LEN, &ret), &ctx_clone);
+ return ret;
+#else
+ return atom_notsup;
+#endif
+}
+
static ERL_NIF_TERM sha512_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{/* (Data) */
#ifdef HAVE_SHA512
@@ -760,6 +1028,95 @@ static ERL_NIF_TERM sha_mac_n(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[
return ret;
}
+static ERL_NIF_TERM sha224_mac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (Key, Data, MacSize) */
+#ifdef HAVE_SHA224
+ unsigned char hmacbuf[SHA224_DIGEST_LENGTH];
+ ErlNifBinary key, data;
+ unsigned mac_sz;
+ ERL_NIF_TERM ret;
+
+ if (!enif_inspect_iolist_as_binary(env, argv[0], &key)
+ || !enif_inspect_iolist_as_binary(env, argv[1], &data)
+ || !enif_get_uint(env,argv[2],&mac_sz) || mac_sz > SHA224_DIGEST_LENGTH) {
+ return enif_make_badarg(env);
+ }
+ hmac_sha224(key.data, key.size, data.data, data.size, hmacbuf);
+ memcpy(enif_make_new_binary(env, mac_sz, &ret),
+ hmacbuf, mac_sz);
+ return ret;
+#else
+ return atom_notsup;
+#endif
+}
+
+static ERL_NIF_TERM sha256_mac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (Key, Data, MacSize) */
+#ifdef HAVE_SHA256
+ unsigned char hmacbuf[SHA256_DIGEST_LENGTH];
+ ErlNifBinary key, data;
+ unsigned mac_sz;
+ ERL_NIF_TERM ret;
+
+ if (!enif_inspect_iolist_as_binary(env, argv[0], &key)
+ || !enif_inspect_iolist_as_binary(env, argv[1], &data)
+ || !enif_get_uint(env,argv[2],&mac_sz) || mac_sz > SHA256_DIGEST_LENGTH) {
+ return enif_make_badarg(env);
+ }
+ hmac_sha256(key.data, key.size, data.data, data.size, hmacbuf);
+ memcpy(enif_make_new_binary(env, mac_sz, &ret),
+ hmacbuf, mac_sz);
+ return ret;
+#else
+ return atom_notsup;
+#endif
+}
+
+static ERL_NIF_TERM sha384_mac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (Key, Data, MacSize) */
+#ifdef HAVE_SHA384
+ unsigned char hmacbuf[SHA384_DIGEST_LENGTH];
+ ErlNifBinary key, data;
+ unsigned mac_sz;
+ ERL_NIF_TERM ret;
+
+ if (!enif_inspect_iolist_as_binary(env, argv[0], &key)
+ || !enif_inspect_iolist_as_binary(env, argv[1], &data)
+ || !enif_get_uint(env,argv[2],&mac_sz) || mac_sz > SHA384_DIGEST_LENGTH) {
+ return enif_make_badarg(env);
+ }
+ hmac_sha384(key.data, key.size, data.data, data.size, hmacbuf);
+ memcpy(enif_make_new_binary(env, mac_sz, &ret),
+ hmacbuf, mac_sz);
+ return ret;
+#else
+ return atom_notsup;
+#endif
+}
+
+
+static ERL_NIF_TERM sha512_mac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (Key, Data, MacSize) */
+#ifdef HAVE_SHA512
+ unsigned char hmacbuf[SHA512_DIGEST_LENGTH];
+ ErlNifBinary key, data;
+ unsigned mac_sz;
+ ERL_NIF_TERM ret;
+
+ if (!enif_inspect_iolist_as_binary(env, argv[0], &key)
+ || !enif_inspect_iolist_as_binary(env, argv[1], &data)
+ || !enif_get_uint(env,argv[2],&mac_sz) || mac_sz > SHA512_DIGEST_LENGTH) {
+ return enif_make_badarg(env);
+ }
+ hmac_sha512(key.data, key.size, data.data, data.size, hmacbuf);
+ memcpy(enif_make_new_binary(env, mac_sz, &ret),
+ hmacbuf, mac_sz);
+ return ret;
+#else
+ return atom_notsup;
+#endif
+}
+
static ERL_NIF_TERM hmac_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{/* (Type, Key) */
ErlNifBinary key;
@@ -768,6 +1125,18 @@ static ERL_NIF_TERM hmac_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[
const EVP_MD *md;
if (argv[0] == atom_sha) md = EVP_sha1();
+#ifdef HAVE_SHA224
+ else if (argv[0] == atom_sha224) md = EVP_sha224();
+#endif
+#ifdef HAVE_SHA256
+ else if (argv[0] == atom_sha256) md = EVP_sha256();
+#endif
+#ifdef HAVE_SHA384
+ else if (argv[0] == atom_sha384) md = EVP_sha384();
+#endif
+#ifdef HAVE_SHA512
+ else if (argv[0] == atom_sha512) md = EVP_sha512();
+#endif
else if (argv[0] == atom_md5) md = EVP_md5();
else if (argv[0] == atom_ripemd160) md = EVP_ripemd160();
else goto badarg;
@@ -919,8 +1288,9 @@ static ERL_NIF_TERM des_ede3_cbc_crypt(ErlNifEnv* env, int argc, const ERL_NIF_T
return ret;
}
-static ERL_NIF_TERM des_ede3_cfb_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+static ERL_NIF_TERM des_ede3_cfb_crypt_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{/* (Key1, Key2, Key3, IVec, Text/Cipher, IsEncrypt) */
+#ifdef HAVE_DES_ede3_cfb_encrypt
ErlNifBinary key1, key2, key3, ivec, text;
DES_key_schedule schedule1, schedule2, schedule3;
DES_cblock ivec_clone; /* writable copy */
@@ -942,6 +1312,9 @@ static ERL_NIF_TERM des_ede3_cfb_crypt(ErlNifEnv* env, int argc, const ERL_NIF_T
8, text.size, &schedule1, &schedule2, &schedule3,
&ivec_clone, (argv[5] == atom_true));
return ret;
+#else
+ return atom_notsup;
+#endif
}
static ERL_NIF_TERM aes_cfb_128_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
@@ -954,8 +1327,7 @@ static ERL_NIF_TERM aes_cfb_128_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TE
if (!enif_inspect_iolist_as_binary(env, argv[0], &key) || key.size != 16
|| !enif_inspect_binary(env, argv[1], &ivec) || ivec.size != 16
- || !enif_inspect_iolist_as_binary(env, argv[2], &text)
- || text.size % 16 != 0) {
+ || !enif_inspect_iolist_as_binary(env, argv[2], &text)) {
return enif_make_badarg(env);
}
@@ -1208,14 +1580,43 @@ 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[])
-{/* (DigestType,Data,Signature,Key=[P, Q, G, Y]) */
+{/* (DigestType|none, Data|{digest,Digest}, Signature,Key=[P, Q, G, Y]) */
ErlNifBinary data_bin, sign_bin;
BIGNUM *dsa_p = NULL, *dsa_q = NULL, *dsa_g = NULL, *dsa_y = NULL;
unsigned char hmacbuf[SHA_DIGEST_LENGTH];
+ unsigned char* digest;
ERL_NIF_TERM head, tail;
+ const ERL_NIF_TERM* tpl_terms;
+ int tpl_arity;
DSA *dsa;
int i;
+ if (argv[0] == atom_sha) {
+ if (enif_get_tuple(env, argv[1], &tpl_arity, &tpl_terms)) {
+ if (tpl_arity != 2 || tpl_terms[0] != atom_digest
+ || !enif_inspect_binary(env, tpl_terms[1], &data_bin)
+ || data_bin.size != SHA_DIGEST_LENGTH) {
+
+ return enif_make_badarg(env);
+ }
+ digest = data_bin.data;
+ }
+ else {
+ if (!inspect_mpint(env, argv[1], &data_bin)) {
+ return enif_make_badarg(env);
+ }
+ SHA1(data_bin.data+4, data_bin.size-4, hmacbuf);
+ digest = hmacbuf;
+ }
+ }
+ else if (argv[0] == atom_none && enif_inspect_binary(env, argv[1], &data_bin)
+ && data_bin.size == SHA_DIGEST_LENGTH) {
+ digest = data_bin.data;
+ }
+ else {
+ return enif_make_badarg(env);
+ }
+
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)
@@ -1226,23 +1627,13 @@ 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);
}
- 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;
@@ -1250,23 +1641,134 @@ static ERL_NIF_TERM dss_verify(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv
dsa->g = dsa_g;
dsa->priv_key = NULL;
dsa->pub_key = dsa_y;
- i = DSA_verify(0, hmacbuf, SHA_DIGEST_LENGTH,
+ i = DSA_verify(0, digest, SHA_DIGEST_LENGTH,
sign_bin.data+4, sign_bin.size-4, dsa);
DSA_free(dsa);
return(i > 0) ? atom_true : atom_false;
}
+
+static void md5_digest(unsigned char* in, unsigned int in_len, unsigned char* out)
+{
+ MD5(in, in_len, out);
+}
+static void sha1_digest(unsigned char* in, unsigned int in_len, unsigned char* out)
+{
+ SHA1(in, in_len, out);
+}
+#ifdef HAVE_SHA224
+static void sha224_digest(unsigned char* in, unsigned int in_len, unsigned char* out)
+{
+ SHA224(in, in_len, out);
+}
+#endif
+#ifdef HAVE_SHA256
+static void sha256_digest(unsigned char* in, unsigned int in_len, unsigned char* out)
+{
+ SHA256(in, in_len, out);
+}
+#endif
+#ifdef HAVE_SHA384
+static void sha384_digest(unsigned char* in, unsigned int in_len, unsigned char* out)
+{
+ SHA384(in, in_len, out);
+}
+#endif
+#ifdef HAVE_SHA512
+static void sha512_digest(unsigned char* in, unsigned int in_len, unsigned char* out)
+{
+ SHA512(in, in_len, out);
+}
+#endif
+
+struct digest_type_t {
+ const char* type_str;
+ unsigned len; /* 0 if notsup */
+ int NID_type;
+ void (*funcp)(unsigned char* in, unsigned int in_len, unsigned char* out);
+ ERL_NIF_TERM type_atom;
+};
+
+struct digest_type_t digest_types[] =
+{
+ {"md5", MD5_DIGEST_LENGTH, NID_md5, md5_digest},
+ {"sha", SHA_DIGEST_LENGTH, NID_sha1, sha1_digest},
+ {"sha224",
+#ifdef HAVE_SHA224
+ SHA224_LEN, NID_sha224, sha224_digest
+#else
+ 0
+#endif
+ },
+ {"sha256",
+#ifdef HAVE_SHA256
+ SHA256_LEN, NID_sha256, sha256_digest
+#else
+ 0
+#endif
+ },
+ {"sha384",
+#ifdef HAVE_SHA384
+ SHA384_LEN, NID_sha384, sha384_digest
+#else
+ 0
+#endif
+ },
+ {"sha512",
+#ifdef HAVE_SHA512
+ SHA512_LEN, NID_sha512, sha512_digest
+#else
+ 0
+#endif
+ },
+ {NULL}
+};
+
+static void init_digest_types(ErlNifEnv* env)
+{
+ struct digest_type_t* p = digest_types;
+
+ for (p = digest_types; p->type_str; p++) {
+ p->type_atom = enif_make_atom(env, p->type_str);
+ }
+
+}
+
+static struct digest_type_t* get_digest_type(ERL_NIF_TERM type)
+{
+ struct digest_type_t* p = NULL;
+ for (p = digest_types; p->type_str; p++) {
+ if (type == p->type_atom) {
+ return p;
+ }
+ }
+ return NULL;
+}
+
static ERL_NIF_TERM rsa_verify_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
-{/* (Type, Data, Signature, Key=[E,N]) */
+{/* (Type, Data|{digest,Digest}, Signature, Key=[E,N]) */
ErlNifBinary data_bin, sign_bin;
unsigned char hmacbuf[SHA512_LEN];
ERL_NIF_TERM head, tail, ret;
int i;
- RSA* rsa = RSA_new();
+ RSA* rsa;
const ERL_NIF_TERM type = argv[0];
+ const ERL_NIF_TERM* tpl_terms;
+ int tpl_arity;
+ struct digest_type_t* digp = NULL;
+ unsigned char* digest = NULL;
- if (!inspect_mpint(env, argv[1], &data_bin)
- || !inspect_mpint(env, argv[2], &sign_bin)
+ digp = get_digest_type(type);
+ if (!digp) {
+ return enif_make_badarg(env);
+ }
+ if (!digp->len) {
+ return atom_notsup;
+ }
+
+ rsa = RSA_new();
+
+ if (!inspect_mpint(env, argv[2], &sign_bin)
|| !enif_get_list_cell(env, argv[3], &head, &tail)
|| !get_bn_from_mpint(env, head, &rsa->e)
|| !enif_get_list_cell(env, tail, &head, &tail)
@@ -1274,59 +1776,38 @@ static ERL_NIF_TERM rsa_verify_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM
|| !enif_is_empty_list(env, tail)) {
ret = enif_make_badarg(env);
+ goto done;
}
- else {
- if (type == atom_sha) {
- SHA1(data_bin.data+4, data_bin.size-4, hmacbuf);
- i = RSA_verify(NID_sha1, hmacbuf, SHA_DIGEST_LENGTH,
- sign_bin.data+4, sign_bin.size-4, rsa);
- }
- else if (type == atom_sha256) {
- #ifdef HAVE_SHA256
- SHA256(data_bin.data+4, data_bin.size-4, hmacbuf);
- i = RSA_verify(NID_sha256, hmacbuf, SHA256_LEN,
- sign_bin.data+4, sign_bin.size-4, rsa);
- #else
- ret = atom_notsup;
- goto done;
- #endif
- }
- else if (type == atom_sha384) {
- #ifdef HAVE_SHA384
- SHA384(data_bin.data+4, data_bin.size-4, hmacbuf);
- i = RSA_verify(NID_sha384, hmacbuf, SHA384_LEN,
- sign_bin.data+4, sign_bin.size-4, rsa);
- #else
- ret = atom_notsup;
- goto done;
- #endif
- }
- else if (type == atom_sha512) {
- #ifdef HAVE_SHA512
- SHA512(data_bin.data+4, data_bin.size-4, hmacbuf);
- i = RSA_verify(NID_sha512, hmacbuf, SHA512_LEN,
- sign_bin.data+4, sign_bin.size-4, rsa);
- #else
- ret = atom_notsup;
- goto done;
- #endif
- }
- else if (type == atom_md5) {
- MD5(data_bin.data+4, data_bin.size-4, hmacbuf);
- i = RSA_verify(NID_md5, hmacbuf, MD5_DIGEST_LENGTH,
- sign_bin.data+4, sign_bin.size-4, rsa);
- }
- else {
+ if (enif_get_tuple(env, argv[1], &tpl_arity, &tpl_terms)) {
+ if (tpl_arity != 2 || tpl_terms[0] != atom_digest
+ || !enif_inspect_binary(env, tpl_terms[1], &data_bin)
+ || data_bin.size != digp->len) {
+
ret = enif_make_badarg(env);
goto done;
}
- ret = (i==1 ? atom_true : atom_false);
- }
+ digest = data_bin.data;
+ }
+ else if (inspect_mpint(env, argv[1], &data_bin)) {
+ digest = hmacbuf;
+ digp->funcp(data_bin.data+4, data_bin.size-4, digest);
+ }
+ else {
+ ret = enif_make_badarg(env);
+ goto done;
+ }
+
+ i = RSA_verify(digp->NID_type, digest, digp->len,
+ sign_bin.data+4, sign_bin.size-4, rsa);
+
+ ret = (i==1 ? atom_true : atom_false);
+
done:
RSA_free(rsa);
return ret;
}
+
static ERL_NIF_TERM aes_cbc_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{/* (Key, IVec, Data, IsEncrypt) */
ErlNifBinary key_bin, ivec_bin, data_bin;
@@ -1485,40 +1966,59 @@ static int get_rsa_private_key(ErlNifEnv* env, ERL_NIF_TERM key, RSA *rsa)
}
static ERL_NIF_TERM rsa_sign_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
-{/* (Type,Data,Key=[E,N,D]|[E,N,D,P1,P2,E1,E2,C]) */
+{/* (Type, Data|{digest,Digest}, Key=[E,N,D]|[E,N,D,P1,P2,E1,E2,C]) */
ErlNifBinary data_bin, ret_bin;
- unsigned char hmacbuf[SHA_DIGEST_LENGTH];
+ unsigned char hmacbuf[SHA512_LEN];
unsigned rsa_s_len;
- RSA *rsa = RSA_new();
- int i, is_sha;
-
- if (argv[0] == atom_sha) is_sha = 1;
- else if (argv[0] == atom_md5) is_sha = 0;
- else goto badarg;
+ RSA* rsa;
+ int i;
+ const ERL_NIF_TERM* tpl_terms;
+ int tpl_arity;
+ struct digest_type_t *digp;
+ unsigned char* digest;
- if (!inspect_mpint(env,argv[1],&data_bin)
- || !get_rsa_private_key(env, argv[2], rsa)) {
- badarg:
- RSA_free(rsa);
+ digp = get_digest_type(argv[0]);
+ if (!digp) {
return enif_make_badarg(env);
}
- 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);
- i = RSA_sign(NID_sha1, hmacbuf, SHA_DIGEST_LENGTH,
- ret_bin.data, &rsa_s_len, rsa);
+ if (!digp->len) {
+ return atom_notsup;
+ }
+
+ if (enif_get_tuple(env, argv[1], &tpl_arity, &tpl_terms)) {
+ if (tpl_arity != 2 || tpl_terms[0] != atom_digest
+ || !enif_inspect_binary(env, tpl_terms[1], &data_bin)
+ || data_bin.size != digp->len) {
+
+ return enif_make_badarg(env);
+ }
+ digest = data_bin.data;
}
else {
- MD5(data_bin.data+4, data_bin.size-4, hmacbuf);
- ERL_VALGRIND_ASSERT_MEM_DEFINED(hmacbuf, MD5_DIGEST_LENGTH);
- i = RSA_sign(NID_md5, hmacbuf,MD5_DIGEST_LENGTH,
- ret_bin.data, &rsa_s_len, rsa);
+ if (!inspect_mpint(env,argv[1],&data_bin)) {
+ return enif_make_badarg(env);
+ }
+ digest = hmacbuf;
+ digp->funcp(data_bin.data+4, data_bin.size-4, digest);
+ }
+
+ rsa = RSA_new();
+ if (!get_rsa_private_key(env, argv[2], rsa)) {
+ RSA_free(rsa);
+ return enif_make_badarg(env);
}
+
+
+ enif_alloc_binary(RSA_size(rsa), &ret_bin);
+
+ ERL_VALGRIND_ASSERT_MEM_DEFINED(digest, digp->len);
+ i = RSA_sign(digp->NID_type, digest, digp->len,
+ ret_bin.data, &rsa_s_len, rsa);
+
RSA_free(rsa);
if (i) {
ERL_VALGRIND_MAKE_MEM_DEFINED(ret_bin.data, rsa_s_len);
- if (rsa_s_len != data_bin.size) {
+ if (rsa_s_len != ret_bin.size) {
enif_realloc_binary(&ret_bin, rsa_s_len);
ERL_VALGRIND_ASSERT_MEM_DEFINED(ret_bin.data, rsa_s_len);
}
@@ -1530,15 +2030,49 @@ static ERL_NIF_TERM rsa_sign_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar
}
}
+
static ERL_NIF_TERM dss_sign_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
-{/* (DigesType, Data, Key=[P,Q,G,PrivKey]) */
+{/* (DigesType|none, Data|{digest,Digest}, Key=[P,Q,G,PrivKey]) */
ErlNifBinary data_bin, ret_bin;
ERL_NIF_TERM head, tail;
unsigned char hmacbuf[SHA_DIGEST_LENGTH];
unsigned int dsa_s_len;
- DSA* dsa = DSA_new();
+ const ERL_NIF_TERM* tpl_terms;
+ int tpl_arity;
+ unsigned char* digest = NULL;
+ DSA* dsa;
int i;
+ if (argv[0] == atom_sha) {
+ if (enif_get_tuple(env, argv[1], &tpl_arity, &tpl_terms)) {
+ if (tpl_arity != 2 || tpl_terms[0] != atom_digest
+ || !enif_inspect_binary(env, tpl_terms[1], &data_bin)
+ || data_bin.size != SHA_DIGEST_LENGTH) {
+
+ return enif_make_badarg(env);
+ }
+ digest = data_bin.data;
+ }
+ else {
+ if (!inspect_mpint(env,argv[1],&data_bin)) {
+ return enif_make_badarg(env);
+ }
+ SHA1(data_bin.data+4, data_bin.size-4, hmacbuf);
+ digest = hmacbuf;
+ }
+ }
+ else if (argv[0] == atom_none
+ && enif_inspect_binary(env,argv[1],&data_bin)
+ && data_bin.size == SHA_DIGEST_LENGTH) {
+
+ digest = data_bin.data;
+ }
+ else {
+ return enif_make_badarg(env);
+ }
+
+ dsa = DSA_new();
+
dsa->pub_key = NULL;
if (!enif_get_list_cell(env, argv[2], &head, &tail)
|| !get_bn_from_mpint(env, head, &dsa->p)
@@ -1549,23 +2083,12 @@ 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);
}
enif_alloc_binary(DSA_size(dsa), &ret_bin);
- i = DSA_sign(NID_sha1, hmacbuf, SHA_DIGEST_LENGTH,
+ i = DSA_sign(NID_sha1, digest, SHA_DIGEST_LENGTH,
ret_bin.data, &dsa_s_len, dsa);
DSA_free(dsa);
if (i) {
@@ -1579,6 +2102,7 @@ static ERL_NIF_TERM dss_sign_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar
}
}
+
static int rsa_pad(ERL_NIF_TERM term, int* padding)
{
if (term == atom_rsa_pkcs1_padding) {
@@ -1907,59 +2431,6 @@ static ERL_NIF_TERM blowfish_ofb64_encrypt(ErlNifEnv* env, int argc, const ERL_N
-#ifdef OPENSSL_THREADS /* vvvvvvvvvvvvvvv OPENSSL_THREADS vvvvvvvvvvvvvvvv */
-
-static INLINE void locking(int mode, ErlNifRWLock* lock)
-{
- switch (mode) {
- case CRYPTO_LOCK|CRYPTO_READ:
- enif_rwlock_rlock(lock);
- break;
- case CRYPTO_LOCK|CRYPTO_WRITE:
- enif_rwlock_rwlock(lock);
- break;
- case CRYPTO_UNLOCK|CRYPTO_READ:
- enif_rwlock_runlock(lock);
- break;
- case CRYPTO_UNLOCK|CRYPTO_WRITE:
- enif_rwlock_rwunlock(lock);
- break;
- default:
- ASSERT(!"Invalid lock mode");
- }
-}
-
-/* Callback from openssl for static locking
- */
-static void locking_function(int mode, int n, const char *file, int line)
-{
- ASSERT(n>=0 && n<CRYPTO_num_locks());
-
- locking(mode, lock_vec[n]);
-}
-
-/* Callback from openssl for thread id
- */
-static unsigned long id_function(void)
-{
- return(unsigned long) enif_thread_self();
-}
-
-/* Callbacks for dynamic locking, not used by current openssl version (0.9.8)
- */
-static struct CRYPTO_dynlock_value* dyn_create_function(const char *file, int line) {
- return(struct CRYPTO_dynlock_value*) enif_rwlock_create("crypto_dyn");
-}
-static void dyn_lock_function(int mode, struct CRYPTO_dynlock_value* ptr,const char *file, int line)
-{
- locking(mode, (ErlNifRWLock*)ptr);
-}
-static void dyn_destroy_function(struct CRYPTO_dynlock_value *ptr, const char *file, int line)
-{
- enif_rwlock_destroy((ErlNifRWLock*)ptr);
-}
-
-#endif /* ^^^^^^^^^^^^^^^^^^^^^^ OPENSSL_THREADS ^^^^^^^^^^^^^^^^^^^^^^ */
/* HMAC */
@@ -2040,3 +2511,166 @@ static void hmac_sha1(unsigned char *key, int klen,
SHA1_Final((unsigned char *) hmacbuf, &ctx);
}
+#ifdef HAVE_SHA224
+static void hmac_sha224(unsigned char *key, int klen,
+ unsigned char *dbuf, int dlen,
+ unsigned char *hmacbuf)
+{
+ SHA256_CTX ctx;
+ char ipad[HMAC_INT_LEN];
+ char opad[HMAC_INT_LEN];
+ unsigned char nkey[SHA224_DIGEST_LENGTH];
+ int i;
+
+ /* Change key if longer than 64 bytes */
+ if (klen > HMAC_INT_LEN) {
+ SHA224(key, klen, nkey);
+ key = nkey;
+ klen = SHA224_DIGEST_LENGTH;
+ }
+
+ memset(ipad, '\0', sizeof(ipad));
+ memset(opad, '\0', sizeof(opad));
+ memcpy(ipad, key, klen);
+ memcpy(opad, key, klen);
+
+ for (i = 0; i < HMAC_INT_LEN; i++) {
+ ipad[i] ^= HMAC_IPAD;
+ opad[i] ^= HMAC_OPAD;
+ }
+
+ /* inner SHA */
+ SHA224_Init(&ctx);
+ SHA224_Update(&ctx, ipad, HMAC_INT_LEN);
+ SHA224_Update(&ctx, dbuf, dlen);
+ SHA224_Final((unsigned char *) hmacbuf, &ctx);
+ /* outer SHA */
+ SHA224_Init(&ctx);
+ SHA224_Update(&ctx, opad, HMAC_INT_LEN);
+ SHA224_Update(&ctx, hmacbuf, SHA224_DIGEST_LENGTH);
+ SHA224_Final((unsigned char *) hmacbuf, &ctx);
+}
+#endif
+
+#ifdef HAVE_SHA256
+static void hmac_sha256(unsigned char *key, int klen,
+ unsigned char *dbuf, int dlen,
+ unsigned char *hmacbuf)
+{
+ SHA256_CTX ctx;
+ char ipad[HMAC_INT_LEN];
+ char opad[HMAC_INT_LEN];
+ unsigned char nkey[SHA256_DIGEST_LENGTH];
+ int i;
+
+ /* Change key if longer than 64 bytes */
+ if (klen > HMAC_INT_LEN) {
+ SHA256(key, klen, nkey);
+ key = nkey;
+ klen = SHA256_DIGEST_LENGTH;
+ }
+
+ memset(ipad, '\0', sizeof(ipad));
+ memset(opad, '\0', sizeof(opad));
+ memcpy(ipad, key, klen);
+ memcpy(opad, key, klen);
+
+ for (i = 0; i < HMAC_INT_LEN; i++) {
+ ipad[i] ^= HMAC_IPAD;
+ opad[i] ^= HMAC_OPAD;
+ }
+
+ /* inner SHA */
+ SHA256_Init(&ctx);
+ SHA256_Update(&ctx, ipad, HMAC_INT_LEN);
+ SHA256_Update(&ctx, dbuf, dlen);
+ SHA256_Final((unsigned char *) hmacbuf, &ctx);
+ /* outer SHA */
+ SHA256_Init(&ctx);
+ SHA256_Update(&ctx, opad, HMAC_INT_LEN);
+ SHA256_Update(&ctx, hmacbuf, SHA256_DIGEST_LENGTH);
+ SHA256_Final((unsigned char *) hmacbuf, &ctx);
+}
+#endif
+
+#ifdef HAVE_SHA384
+static void hmac_sha384(unsigned char *key, int klen,
+ unsigned char *dbuf, int dlen,
+ unsigned char *hmacbuf)
+{
+ SHA512_CTX ctx;
+ char ipad[HMAC_INT2_LEN];
+ char opad[HMAC_INT2_LEN];
+ unsigned char nkey[SHA384_DIGEST_LENGTH];
+ int i;
+
+ /* Change key if longer than 64 bytes */
+ if (klen > HMAC_INT2_LEN) {
+ SHA384(key, klen, nkey);
+ key = nkey;
+ klen = SHA384_DIGEST_LENGTH;
+ }
+
+ memset(ipad, '\0', sizeof(ipad));
+ memset(opad, '\0', sizeof(opad));
+ memcpy(ipad, key, klen);
+ memcpy(opad, key, klen);
+
+ for (i = 0; i < HMAC_INT2_LEN; i++) {
+ ipad[i] ^= HMAC_IPAD;
+ opad[i] ^= HMAC_OPAD;
+ }
+
+ /* inner SHA */
+ SHA384_Init(&ctx);
+ SHA384_Update(&ctx, ipad, HMAC_INT2_LEN);
+ SHA384_Update(&ctx, dbuf, dlen);
+ SHA384_Final((unsigned char *) hmacbuf, &ctx);
+ /* outer SHA */
+ SHA384_Init(&ctx);
+ SHA384_Update(&ctx, opad, HMAC_INT2_LEN);
+ SHA384_Update(&ctx, hmacbuf, SHA384_DIGEST_LENGTH);
+ SHA384_Final((unsigned char *) hmacbuf, &ctx);
+}
+#endif
+
+#ifdef HAVE_SHA512
+static void hmac_sha512(unsigned char *key, int klen,
+ unsigned char *dbuf, int dlen,
+ unsigned char *hmacbuf)
+{
+ SHA512_CTX ctx;
+ char ipad[HMAC_INT2_LEN];
+ char opad[HMAC_INT2_LEN];
+ unsigned char nkey[SHA512_DIGEST_LENGTH];
+ int i;
+
+ /* Change key if longer than 64 bytes */
+ if (klen > HMAC_INT2_LEN) {
+ SHA512(key, klen, nkey);
+ key = nkey;
+ klen = SHA512_DIGEST_LENGTH;
+ }
+
+ memset(ipad, '\0', sizeof(ipad));
+ memset(opad, '\0', sizeof(opad));
+ memcpy(ipad, key, klen);
+ memcpy(opad, key, klen);
+
+ for (i = 0; i < HMAC_INT2_LEN; i++) {
+ ipad[i] ^= HMAC_IPAD;
+ opad[i] ^= HMAC_OPAD;
+ }
+
+ /* inner SHA */
+ SHA512_Init(&ctx);
+ SHA512_Update(&ctx, ipad, HMAC_INT2_LEN);
+ SHA512_Update(&ctx, dbuf, dlen);
+ SHA512_Final((unsigned char *) hmacbuf, &ctx);
+ /* outer SHA */
+ SHA512_Init(&ctx);
+ SHA512_Update(&ctx, opad, HMAC_INT2_LEN);
+ SHA512_Update(&ctx, hmacbuf, SHA512_DIGEST_LENGTH);
+ SHA512_Final((unsigned char *) hmacbuf, &ctx);
+}
+#endif
diff --git a/lib/crypto/c_src/crypto_callback.c b/lib/crypto/c_src/crypto_callback.c
new file mode 100644
index 0000000000..81106b4cc2
--- /dev/null
+++ b/lib/crypto/c_src/crypto_callback.c
@@ -0,0 +1,165 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2012. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, 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 <string.h>
+#include <openssl/opensslconf.h>
+
+#include "erl_nif.h"
+#include "crypto_callback.h"
+
+#ifdef DEBUG
+ # define ASSERT(e) \
+ ((void) ((e) ? 1 : (fprintf(stderr,"Assert '%s' failed at %s:%d\n",\
+ #e, __FILE__, __LINE__), abort(), 0)))
+#else
+ # define ASSERT(e) ((void) 1)
+#endif
+
+#ifdef __GNUC__
+ # define INLINE __inline__
+#elif defined(__WIN32__)
+ # define INLINE __forceinline
+#else
+ # define INLINE
+#endif
+
+#ifdef __WIN32__
+# define DLLEXPORT __declspec(dllexport)
+#else
+# define DLLEXPORT
+#endif
+
+/* to be dlsym'ed */
+DLLEXPORT struct crypto_callbacks* get_crypto_callbacks(int nlocks);
+
+
+static ErlNifRWLock** lock_vec = NULL; /* Static locks used by openssl */
+
+static void* crypto_alloc(size_t size)
+{
+ return enif_alloc(size);
+}
+static void* crypto_realloc(void* ptr, size_t size)
+{
+ return enif_realloc(ptr, size);
+}
+static void crypto_free(void* ptr)
+{
+ enif_free(ptr);
+}
+
+
+#ifdef OPENSSL_THREADS /* vvvvvvvvvvvvvvv OPENSSL_THREADS vvvvvvvvvvvvvvvv */
+
+#include <openssl/crypto.h>
+
+static INLINE void locking(int mode, ErlNifRWLock* lock)
+{
+ switch (mode) {
+ case CRYPTO_LOCK|CRYPTO_READ:
+ enif_rwlock_rlock(lock);
+ break;
+ case CRYPTO_LOCK|CRYPTO_WRITE:
+ enif_rwlock_rwlock(lock);
+ break;
+ case CRYPTO_UNLOCK|CRYPTO_READ:
+ enif_rwlock_runlock(lock);
+ break;
+ case CRYPTO_UNLOCK|CRYPTO_WRITE:
+ enif_rwlock_rwunlock(lock);
+ break;
+ default:
+ ASSERT(!"Invalid lock mode");
+ }
+}
+
+static void locking_function(int mode, int n, const char *file, int line)
+{
+ ASSERT(n>=0 && n<CRYPTO_num_locks());
+
+ locking(mode, lock_vec[n]);
+}
+
+static unsigned long id_function(void)
+{
+ return (unsigned long) enif_thread_self();
+}
+
+/* Dynamic locking, not used by current openssl version (0.9.8)
+ */
+static struct CRYPTO_dynlock_value* dyn_create_function(const char *file, int line)
+{
+ return (struct CRYPTO_dynlock_value*) enif_rwlock_create("crypto_dyn");
+}
+static void dyn_lock_function(int mode, struct CRYPTO_dynlock_value* ptr,const char *file, int line)
+{
+ locking(mode, (ErlNifRWLock*)ptr);
+}
+static void dyn_destroy_function(struct CRYPTO_dynlock_value *ptr, const char *file, int line)
+{
+ enif_rwlock_destroy((ErlNifRWLock*)ptr);
+}
+
+#endif /* ^^^^^^^^^^^^^^^^^^^^^^ OPENSSL_THREADS ^^^^^^^^^^^^^^^^^^^^^^ */
+
+DLLEXPORT struct crypto_callbacks* get_crypto_callbacks(int nlocks)
+{
+ static int is_initialized = 0;
+ static struct crypto_callbacks the_struct = {
+ sizeof(struct crypto_callbacks),
+
+ &crypto_alloc,
+ &crypto_realloc,
+ &crypto_free,
+
+#ifdef OPENSSL_THREADS
+ &locking_function,
+ &id_function,
+ &dyn_create_function,
+ &dyn_lock_function,
+ &dyn_destroy_function
+#endif /* OPENSSL_THREADS */
+ };
+
+ if (!is_initialized) {
+#ifdef OPENSSL_THREADS
+ if (nlocks > 0) {
+ int i;
+ lock_vec = enif_alloc(nlocks*sizeof(*lock_vec));
+ if (lock_vec==NULL) return NULL;
+ memset(lock_vec, 0, nlocks*sizeof(*lock_vec));
+
+ for (i=nlocks-1; i>=0; --i) {
+ lock_vec[i] = enif_rwlock_create("crypto_stat");
+ if (lock_vec[i]==NULL) return NULL;
+ }
+ }
+#endif
+ is_initialized = 1;
+ }
+ return &the_struct;
+}
+
+#ifdef HAVE_DYNAMIC_CRYPTO_LIB
+/* This is not really a NIF library, but we use ERL_NIF_INIT in order to
+ * get access to the erl_nif API (on Windows).
+ */
+ERL_NIF_INIT(dummy, (ErlNifFunc*)NULL , NULL, NULL, NULL, NULL)
+#endif
+
diff --git a/lib/crypto/c_src/crypto_callback.h b/lib/crypto/c_src/crypto_callback.h
new file mode 100644
index 0000000000..23ecba3e5d
--- /dev/null
+++ b/lib/crypto/c_src/crypto_callback.h
@@ -0,0 +1,46 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2012. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, 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 crypto_callbacks
+{
+ size_t sizeof_me;
+
+ void* (*crypto_alloc)(size_t size);
+ void* (*crypto_realloc)(void* ptr, size_t size);
+ void (*crypto_free)(void* ptr);
+
+ /* openssl callbacks */
+ #ifdef OPENSSL_THREADS
+ void (*locking_function)(int mode, int n, const char *file, int line);
+ unsigned long (*id_function)(void);
+ struct CRYPTO_dynlock_value* (*dyn_create_function)(const char *file,
+ int line);
+ void (*dyn_lock_function)(int mode, struct CRYPTO_dynlock_value* ptr,
+ const char *file, int line);
+ void (*dyn_destroy_function)(struct CRYPTO_dynlock_value *ptr,
+ const char *file, int line);
+ #endif /* OPENSSL_THREADS */
+};
+
+typedef struct crypto_callbacks* get_crypto_callbacks_t(int nlocks);
+
+#ifndef HAVE_DYNAMIC_CRYPTO_LIB
+struct crypto_callbacks* get_crypto_callbacks(int nlocks);
+#endif
+
diff --git a/lib/crypto/doc/src/crypto.xml b/lib/crypto/doc/src/crypto.xml
index 19db6c9dd4..14c77c873f 100644..100755
--- a/lib/crypto/doc/src/crypto.xml
+++ b/lib/crypto/doc/src/crypto.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1999</year><year>2011</year>
+ <year>1999</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -115,6 +115,12 @@ Mpint() = <![CDATA[<<ByteLen:32/integer-big, Bytes:ByteLen/binary>>]]>
> <input>info_lib().</input>
[{&lt;&lt;"OpenSSL"&gt;&gt;,9469983,&lt;&lt;"OpenSSL 0.9.8a 11 Oct 2005"&gt;&gt;}]
</pre>
+ <note><p>
+ From OTP R16 the <em>numeric version</em> represents the version of the OpenSSL
+ <em>header files</em> (<c>openssl/opensslv.h</c>) used when crypto was compiled.
+ The text variant represents the OpenSSL library used at runtime.
+ In earlier OTP versions both numeric and text was taken from the library.
+ </p></note>
</desc>
</func>
<func>
@@ -256,6 +262,61 @@ Mpint() = <![CDATA[<<ByteLen:32/integer-big, Bytes:ByteLen/binary>>]]>
</desc>
</func>
<func>
+ <name>hash(Type, Data) -> Digest</name>
+ <fsummary></fsummary>
+ <type>
+ <v>Type = md4 | md5 | ripemd160 | sha | sha224 | sha256 | sha384 | sha512</v>
+ <v>Data = iodata()</v>
+ <v>Digest = binary()</v>
+ </type>
+ <desc>
+ <p>Computes a message digest of type <c>Type</c> from <c>Data</c>.</p>
+ <p>May throw exception <c>notsup</c> in case the chosen <c>Type</c>
+ is not supported by the underlying OpenSSL implementation.</p>
+ </desc>
+ </func>
+ <func>
+ <name>hash_init(Type) -> Context</name>
+ <fsummary></fsummary>
+ <type>
+ <v>Type = md4 | md5 | ripemd160 | sha | sha224 | sha256 | sha384 | sha512</v>
+ </type>
+ <desc>
+ <p>Initializes the context for streaming hash operations. <c>Type</c> determines
+ which digest to use. The returned context should be used as argument
+ to <seealso marker="#hash_update/2">hash_update</seealso>.</p>
+ <p>May throw exception <c>notsup</c> in case the chosen <c>Type</c>
+ is not supported by the underlying OpenSSL implementation.</p>
+ </desc>
+ </func>
+ <func>
+ <name>hash_update(Context, Data) -> NewContext</name>
+ <fsummary></fsummary>
+ <type>
+ <v>Data = iodata()</v>
+ </type>
+ <desc>
+ <p>Updates the digest represented by <c>Context</c> using the given <c>Data</c>. <c>Context</c>
+ must have been generated using <seealso marker="#hash_init/1">hash_init</seealso>
+ or a previous call to this function. <c>Data</c> can be any length. <c>NewContext</c>
+ must be passed into the next call to <c>hash_update</c>
+ or <seealso marker="#hash_final/1">hash_final</seealso>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>hash_final(Context) -> Digest</name>
+ <fsummary></fsummary>
+ <type>
+ <v>Digest = binary()</v>
+ </type>
+ <desc>
+ <p>Finalizes the hash operation referenced by <c>Context</c> returned
+ from a previous call to <seealso marker="#hash_update/2">hash_update</seealso>.
+ The size of <c>Digest</c> is determined by the type of hash
+ function used to generate it.</p>
+ </desc>
+ </func>
+ <func>
<name>md5_mac(Key, Data) -> Mac</name>
<fsummary>Compute an <c>MD5 MAC</c>message authentification code</fsummary>
<type>
@@ -282,10 +343,27 @@ Mpint() = <![CDATA[<<ByteLen:32/integer-big, Bytes:ByteLen/binary>>]]>
</desc>
</func>
<func>
+ <name>hmac(Type, Key, Data) -> Mac</name>
+ <name>hmac(Type, Key, Data, MacLength) -> Mac</name>
+ <fsummary></fsummary>
+ <type>
+ <v>Type = md5 | sha | sha224 | sha256 | sha384 | sha512</v>
+ <v>Key = iodata()</v>
+ <v>Data = iodata()</v>
+ <v>MacLength = integer()</v>
+ <v>Mac = binary()</v>
+ </type>
+ <desc>
+ <p>Computes a HMAC of type <c>Type</c> from <c>Data</c> using
+ <c>Key</c> as the authentication key.</p> <c>MacLength</c>
+ will limit the size of the resultant <c>Mac</c>.
+ </desc>
+ </func>
+ <func>
<name>hmac_init(Type, Key) -> Context</name>
<fsummary></fsummary>
<type>
- <v>Type = sha | md5 | ripemd160</v>
+ <v>Type = md5 | ripemd160 | sha | sha224 | sha256 | sha384 | sha512</v>
<v>Key = iolist() | binary()</v>
<v>Context = binary()</v>
</type>
@@ -497,6 +575,8 @@ Mpint() = <![CDATA[<<ByteLen:32/integer-big, Bytes:ByteLen/binary>>]]>
keys, and <c>IVec</c> is an arbitrary initializing
vector. The lengths of each of <c>Key1</c>, <c>Key2</c>,
<c>Key3</c> and <c>IVec</c> must be 64 bits (8 bytes).</p>
+ <p>May throw exception <c>notsup</c> for old OpenSSL
+ versions (0.9.7) that does not support this encryption mode.</p>
</desc>
</func>
<func>
@@ -514,6 +594,8 @@ Mpint() = <![CDATA[<<ByteLen:32/integer-big, Bytes:ByteLen/binary>>]]>
and <c>IVec</c> must have the same values as those used when
encrypting. The lengths of <c>Key1</c>, <c>Key2</c>,
<c>Key3</c>, and <c>IVec</c> must be 64 bits (8 bytes).</p>
+ <p>May throw exception <c>notsup</c> for old OpenSSL
+ versions (0.9.7) that does not support this encryption mode.</p>
</desc>
</func>
@@ -643,16 +725,14 @@ Mpint() = <![CDATA[<<ByteLen:32/integer-big, Bytes:ByteLen/binary>>]]>
<func>
<name>aes_cfb_128_encrypt(Key, IVec, Text) -> Cipher</name>
- <name>aes_cbc_128_encrypt(Key, IVec, Text) -> Cipher</name>
- <fsummary>Encrypt <c>Text</c>according to AES in Cipher Feedback mode or Cipher Block Chaining mode</fsummary>
+ <fsummary>Encrypt <c>Text</c>according to AES in Cipher Feedback 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 Cipher Feedback
- mode (CFB) or Cipher Block Chaining mode (CBC). <c>Text</c>
- must be a multiple of 128 bits (16 bytes). <c>Key</c> is the
+ mode (CFB). <c>Key</c> is the
AES key, and <c>IVec</c> is an arbitrary initializing vector.
The lengths of <c>Key</c> and <c>IVec</c> must be 128 bits
(16 bytes).</p>
@@ -660,15 +740,45 @@ Mpint() = <![CDATA[<<ByteLen:32/integer-big, Bytes:ByteLen/binary>>]]>
</func>
<func>
<name>aes_cfb_128_decrypt(Key, IVec, Cipher) -> Text</name>
+ <fsummary>Decrypt <c>Cipher</c>according to AES in Cipher Feedback 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 Cipher Feedback Mode (CFB).
+ <c>Key</c> is the AES key, and <c>IVec</c> is an arbitrary
+ initializing vector. <c>Key</c> and <c>IVec</c> must have
+ the same values as those used when encrypting. The lengths of
+ <c>Key</c> and <c>IVec</c> must be 128 bits (16 bytes).</p>
+ </desc>
+ </func>
+ <func>
+ <name>aes_cbc_128_encrypt(Key, IVec, Text) -> Cipher</name>
+ <fsummary>Encrypt <c>Text</c>according to AES in Cipher Block Chaining 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 Cipher Block Chaining
+ mode (CBC). <c>Text</c>
+ must be a multiple of 128 bits (16 bytes). <c>Key</c> is the
+ AES key, and <c>IVec</c> is an arbitrary initializing vector.
+ The lengths of <c>Key</c> and <c>IVec</c> must be 128 bits
+ (16 bytes).</p>
+ </desc>
+ </func>
+ <func>
<name>aes_cbc_128_decrypt(Key, IVec, Cipher) -> Text</name>
- <fsummary>Decrypt <c>Cipher</c>according to AES in Cipher Feedback mode or Cipher Block Chaining mode</fsummary>
+ <fsummary>Decrypt <c>Cipher</c>according to AES in Cipher Block Chaining mode</fsummary>
<type>
<v>Key = Cipher = iolist() | binary()</v>
<v>IVec = Text = binary()</v>
</type>
<desc>
- <p>Decrypts <c>Cipher</c> according to Cipher Feedback Mode (CFB)
- or Cipher Block Chaining mode (CBC).
+ <p>Decrypts <c>Cipher</c> according to AES in Cipher Block
+ Chaining mode (CBC).
<c>Key</c> is the AES key, and <c>IVec</c> is an arbitrary
initializing vector. <c>Key</c> and <c>IVec</c> must have
the same values as those used when encrypting. <c>Cipher</c>
@@ -865,11 +975,13 @@ Mpint() = <![CDATA[<<ByteLen:32/integer-big, Bytes:ByteLen/binary>>]]>
</func>
<func>
- <name>rsa_sign(Data, Key) -> Signature</name>
- <name>rsa_sign(DigestType, Data, Key) -> Signature</name>
+ <name>rsa_sign(DataOrDigest, Key) -> Signature</name>
+ <name>rsa_sign(DigestType, DataOrDigest, Key) -> Signature</name>
<fsummary>Sign the data using rsa with the given key.</fsummary>
<type>
+ <v>DataOrDigest = Data | {digest,Digest}</v>
<v>Data = Mpint</v>
+ <v>Digest = binary()</v>
<v>Key = [E, N, D] | [E, N, D, P1, P2, E1, E2, C]</v>
<v>E, N, D = Mpint</v>
<d>Where <c>E</c> is the public exponent, <c>N</c> is public modulus and
@@ -879,37 +991,40 @@ Mpint() = <![CDATA[<<ByteLen:32/integer-big, Bytes:ByteLen/binary>>]]>
the calculation faster. <c>P1,P2</c> are first and second prime factors.
<c>E1,E2</c> are first and second exponents. <c>C</c> is the CRT coefficient.
Terminology is taken from RFC 3447.</d>
- <v>DigestType = md5 | sha</v>
+ <v>DigestType = md5 | sha | sha224 | sha256 | sha384 | sha512</v>
<d>The default <c>DigestType</c> is sha.</d>
<v>Mpint = binary()</v>
<v>Signature = binary()</v>
</type>
<desc>
- <p>Calculates a <c>DigestType</c> digest of the <c>Data</c>
- and creates a RSA signature with the private key <c>Key</c>
- of the digest.</p>
+ <p>Creates a RSA signature with the private key <c>Key</c>
+ of a digest. The digest is either calculated as a
+ <c>DigestType</c> digest of <c>Data</c> or a precalculated
+ binary <c>Digest</c>.</p>
</desc>
</func>
<func>
- <name>rsa_verify(Data, Signature, Key) -> Verified</name>
- <name>rsa_verify(DigestType, Data, Signature, Key) -> Verified </name>
+ <name>rsa_verify(DataOrDigest, Signature, Key) -> Verified</name>
+ <name>rsa_verify(DigestType, DataOrDigest, Signature, Key) -> Verified </name>
<fsummary>Verify the digest and signature using rsa with given public key.</fsummary>
<type>
<v>Verified = boolean()</v>
+ <v>DataOrDigest = Data | {digest|Digest}</v>
<v>Data, Signature = Mpint</v>
+ <v>Digest = binary()</v>
<v>Key = [E, N]</v>
<v>E, N = Mpint</v>
<d>Where <c>E</c> is the public exponent and <c>N</c> is public modulus.</d>
- <v>DigestType = md5 | sha | sha256 | sha384 | sha512</v>
- <d> The default <c>DigestType</c> is sha.</d>
+ <v>DigestType = md5 | sha | sha224 | sha256 | sha384 | sha512</v>
+ <d>The default <c>DigestType</c> is sha.</d>
<v>Mpint = binary()</v>
</type>
<desc>
- <p>Calculates a <c>DigestType</c> digest of the <c>Data</c>
- and verifies that the digest matches the RSA signature using the
+ <p>Verifies that a digest matches the RSA signature using the
signer's public key <c>Key</c>.
- </p>
+ The digest is either calculated as a <c>DigestType</c>
+ digest of <c>Data</c> or a precalculated binary <c>Digest</c>.</p>
<p>May throw exception <c>notsup</c> in case the chosen <c>DigestType</c>
is not supported by the underlying OpenSSL implementation.</p>
</desc>
@@ -1022,45 +1137,52 @@ Mpint() = <![CDATA[<<ByteLen:32/integer-big, Bytes:ByteLen/binary>>]]>
</func>
<func>
- <name>dss_sign(Data, Key) -> Signature</name>
- <name>dss_sign(DigestType, Data, Key) -> Signature</name>
+ <name>dss_sign(DataOrDigest, Key) -> Signature</name>
+ <name>dss_sign(DigestType, DataOrDigest, Key) -> Signature</name>
<fsummary>Sign the data using dsa with given private key.</fsummary>
<type>
- <v>DigestType = sha | none (default is sha)</v>
- <v>Data = Mpint | ShaDigest</v>
+ <v>DigestType = sha</v>
+ <v>DataOrDigest = Mpint | {digest,Digest}</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>ShaDigest = binary() with length 20 bytes</v>
+ <v>Digest = binary() with length 20 bytes</v>
<v>Signature = binary()</v>
</type>
<desc>
- <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>
+ <p>Creates a DSS signature with the private key <c>Key</c> of
+ a digest. The digest is either calculated as a SHA1
+ digest of <c>Data</c> or a precalculated binary <c>Digest</c>.</p>
+ <p>A deprecated feature is having <c>DigestType = 'none'</c>
+ in which case <c>DataOrDigest</c> is a precalculated SHA1
+ digest.</p>
</desc>
</func>
<func>
- <name>dss_verify(Data, Signature, Key) -> Verified</name>
- <name>dss_verify(DigestType, Data, Signature, Key) -> Verified</name>
+ <name>dss_verify(DataOrDigest, Signature, Key) -> Verified</name>
+ <name>dss_verify(DigestType, DataOrDigest, Signature, Key) -> Verified</name>
<fsummary>Verify the data and signature using dsa with given public key.</fsummary>
<type>
<v>Verified = boolean()</v>
- <v>DigestType = sha | none</v>
+ <v>DigestType = sha</v>
+ <v>DataOrDigest = Mpint | {digest,Digest}</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>ShaDigest = binary() with length 20 bytes</v>
+ <v>Digest = binary() with length 20 bytes</v>
</type>
<desc>
- <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>
+ <p>Verifies that a digest matches the DSS signature using the
+ public key <c>Key</c>. The digest is either calculated as a SHA1
+ digest of <c>Data</c> or is a precalculated binary <c>Digest</c>.</p>
+ <p>A deprecated feature is having <c>DigestType = 'none'</c>
+ in which case <c>DataOrDigest</c> is a precalculated SHA1
+ digest binary.</p>
</desc>
</func>
diff --git a/lib/crypto/doc/src/crypto_app.xml b/lib/crypto/doc/src/crypto_app.xml
index 1c01e3f099..6573a56f4c 100644
--- a/lib/crypto/doc/src/crypto_app.xml
+++ b/lib/crypto/doc/src/crypto_app.xml
@@ -62,7 +62,7 @@
<section>
<title>OpenSSL libraries</title>
<p>The current implementation of the Erlang Crypto application is
- based on the <em>OpenSSL</em> package version 0.9.7 or higher.
+ based on the <em>OpenSSL</em> package version 0.9.8 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,
diff --git a/lib/crypto/doc/src/notes.xml b/lib/crypto/doc/src/notes.xml
index 3a44550ae2..4178ca2b08 100644
--- a/lib/crypto/doc/src/notes.xml
+++ b/lib/crypto/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>1999</year><year>2011</year>
+ <year>1999</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -30,6 +30,53 @@
</header>
<p>This document describes the changes made to the Crypto application.</p>
+<section><title>Crypto 2.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Remove unnecessary dependency to libssl from crypto NIF
+ library. This dependency was introduced by accident in
+ R14B04.</p>
+ <p>
+ Own Id: OTP-10064</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Add crypto and public_key support for the hash functions
+ SHA224, SHA256, SHA384 and SHA512 and also hmac and
+ rsa_sign/verify support using these hash functions.
+ Thanks to Andreas Schultz for making a prototype.</p>
+ <p>
+ Own Id: OTP-9908</p>
+ </item>
+ <item>
+ <p>
+ Optimize RSA private key handling in <c>crypto</c> and
+ <c>public_key</c>.</p>
+ <p>
+ Own Id: OTP-10065</p>
+ </item>
+ <item>
+ <p>
+ Make <c>crypto:aes_cfb_128_encrypt</c> and
+ <c>crypto:aes_cfb_128_decrypt</c> handle data and cipher
+ with arbitrary length. (Thanks to Stefan Zegenhagen)</p>
+ <p>
+ Own Id: OTP-10136</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Crypto 2.1</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/crypto/src/Makefile b/lib/crypto/src/Makefile
index ddafb9168f..5c200742ac 100644
--- a/lib/crypto/src/Makefile
+++ b/lib/crypto/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2010. All Rights Reserved.
+# Copyright Ericsson AB 1999-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -70,10 +70,10 @@ clean:
rm -f errs core *~
$(APP_TARGET): $(APP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
docs:
diff --git a/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl
index d7aac27825..aa89f6cc61 100644
--- a/lib/crypto/src/crypto.erl
+++ b/lib/crypto/src/crypto.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -22,13 +22,20 @@
-module(crypto).
-export([start/0, stop/0, info/0, info_lib/0, version/0]).
+-export([hash/2, hash_init/1, hash_update/2, hash_final/1]).
-export([md4/1, md4_init/0, md4_update/2, md4_final/1]).
-export([md5/1, md5_init/0, md5_update/2, md5_final/1]).
-export([sha/1, sha_init/0, sha_update/2, sha_final/1]).
+-export([sha224/1, sha224_init/0, sha224_update/2, sha224_final/1]).
-export([sha256/1, sha256_init/0, sha256_update/2, sha256_final/1]).
+-export([sha384/1, sha384_init/0, sha384_update/2, sha384_final/1]).
-export([sha512/1, sha512_init/0, sha512_update/2, sha512_final/1]).
-export([md5_mac/2, md5_mac_96/2, sha_mac/2, sha_mac/3, sha_mac_96/2]).
--export([hmac_init/2, hmac_update/2, hmac_final/1, hmac_final_n/2]).
+-export([sha224_mac/2, sha224_mac/3]).
+-export([sha256_mac/2, sha256_mac/3]).
+-export([sha384_mac/2, sha384_mac/3]).
+-export([sha512_mac/2, sha512_mac/3]).
+-export([hmac/3, hmac/4, hmac_init/2, hmac_update/2, hmac_final/1, hmac_final_n/2]).
-export([des_cbc_encrypt/3, des_cbc_decrypt/3, des_cbc_ivec/1]).
-export([des_ecb_encrypt/2, des_ecb_decrypt/2]).
-export([des_cfb_encrypt/3, des_cfb_decrypt/3, des_cfb_ivec/2]).
@@ -64,16 +71,18 @@
-define(FUNC_LIST, [md4, md4_init, md4_update, md4_final,
md5, md5_init, md5_update, md5_final,
sha, sha_init, sha_update, sha_final,
- sha256, sha256_init, sha256_update, sha256_final,
- sha512, sha512_init, sha512_update, sha512_final,
+ sha224, sha224_init, sha224_update, sha224_final,
+ sha256, sha256_init, sha256_update, sha256_final,
+ sha384, sha384_init, sha384_update, sha384_final,
+ sha512, sha512_init, sha512_update, sha512_final,
md5_mac, md5_mac_96,
sha_mac, sha_mac_96,
- sha_mac_init, sha_mac_update, sha_mac_final,
+ sha224_mac, sha256_mac, sha384_mac, sha512_mac,
des_cbc_encrypt, des_cbc_decrypt,
des_cfb_encrypt, des_cfb_decrypt,
des_ecb_encrypt, des_ecb_decrypt,
- des_ede3_cbc_encrypt, des_ede3_cbc_decrypt,
- des_ede3_cfb_encrypt, des_ede3_cfb_decrypt,
+ des3_cbc_encrypt, des3_cbc_decrypt,
+ des3_cfb_encrypt, des3_cfb_decrypt,
aes_cfb_128_encrypt, aes_cfb_128_decrypt,
rand_bytes,
strong_rand_bytes,
@@ -93,17 +102,25 @@
aes_cbc_256_encrypt, aes_cbc_256_decrypt,
aes_ctr_encrypt, aes_ctr_decrypt,
aes_ctr_stream_init, aes_ctr_stream_encrypt, aes_ctr_stream_decrypt,
+ aes_cbc_ivec, blowfish_cbc_encrypt, blowfish_cbc_decrypt,
+ blowfish_cfb64_encrypt, blowfish_cfb64_decrypt,
+ blowfish_ecb_encrypt, blowfish_ecb_decrypt, blowfish_ofb64_encrypt,
+ des_cbc_ivec, des_cfb_ivec, erlint, mpint,
+ hash, hash_init, hash_update, hash_final,
+ hmac, hmac_init, hmac_update, hmac_final, hmac_final_n, info,
+ rc2_cbc_encrypt, rc2_cbc_decrypt,
info_lib]).
--type rsa_digest_type() :: 'md5' | 'sha' | 'sha256' | 'sha384' | 'sha512'.
+-type rsa_digest_type() :: 'md5' | 'sha' | 'sha224' | 'sha256' | 'sha384' | 'sha512'.
-type dss_digest_type() :: 'none' | 'sha'.
+-type data_or_digest() :: binary() | {digest, binary()}.
-type crypto_integer() :: binary() | integer().
-define(nif_stub,nif_stub_error(?LINE)).
-on_load(on_load/0).
--define(CRYPTO_NIF_VSN,101).
+-define(CRYPTO_NIF_VSN,201).
on_load() ->
LibBaseName = "crypto",
@@ -129,7 +146,7 @@ on_load() ->
end
end,
Lib = filename:join([PrivDir, "lib", LibName]),
- Status = case erlang:load_nif(Lib, ?CRYPTO_NIF_VSN) of
+ Status = case erlang:load_nif(Lib, {?CRYPTO_NIF_VSN,Lib}) of
ok -> ok;
{error, {load_failed, _}}=Error1 ->
ArchLibDir =
@@ -141,7 +158,7 @@ on_load() ->
[] -> Error1;
_ ->
ArchLib = filename:join([ArchLibDir, LibName]),
- erlang:load_nif(ArchLib, ?CRYPTO_NIF_VSN)
+ erlang:load_nif(ArchLib, {?CRYPTO_NIF_VSN,ArchLib})
end;
Error1 -> Error1
end,
@@ -171,7 +188,7 @@ info_lib() -> ?nif_stub.
%% (no version): Driver implementation
%% 2.0 : NIF implementation, requires OTP R14
version() -> ?CRYPTO_VSN.
-
+
%% Below Key and Data are binaries or IO-lists. IVec is a binary.
%% Output is always a binary. Context is a binary.
@@ -179,6 +196,50 @@ version() -> ?CRYPTO_VSN.
%% MESSAGE DIGESTS
%%
+-spec hash(_, iodata()) -> binary().
+hash(md5, Data) -> md5(Data);
+hash(md4, Data) -> md4(Data);
+hash(sha, Data) -> sha(Data);
+hash(ripemd160, Data) -> ripemd160(Data);
+hash(sha224, Data) -> sha224(Data);
+hash(sha256, Data) -> sha256(Data);
+hash(sha384, Data) -> sha384(Data);
+hash(sha512, Data) -> sha512(Data).
+
+-spec hash_init('md5'|'md4'|'ripemd160'|
+ 'sha'|'sha224'|'sha256'|'sha384'|'sha512') -> any().
+
+hash_init(md5) -> {md5, md5_init()};
+hash_init(md4) -> {md4, md4_init()};
+hash_init(sha) -> {sha, sha_init()};
+hash_init(ripemd160) -> {ripemd160, ripemd160_init()};
+hash_init(sha224) -> {sha224, sha224_init()};
+hash_init(sha256) -> {sha256, sha256_init()};
+hash_init(sha384) -> {sha384, sha384_init()};
+hash_init(sha512) -> {sha512, sha512_init()}.
+
+-spec hash_update(_, iodata()) -> any().
+
+hash_update({md5,Context}, Data) -> {md5, md5_update(Context,Data)};
+hash_update({md4,Context}, Data) -> {md4, md4_update(Context,Data)};
+hash_update({sha,Context}, Data) -> {sha, sha_update(Context,Data)};
+hash_update({ripemd160,Context}, Data) -> {ripemd160, ripemd160_update(Context,Data)};
+hash_update({sha224,Context}, Data) -> {sha224, sha224_update(Context,Data)};
+hash_update({sha256,Context}, Data) -> {sha256, sha256_update(Context,Data)};
+hash_update({sha384,Context}, Data) -> {sha384, sha384_update(Context,Data)};
+hash_update({sha512,Context}, Data) -> {sha512, sha512_update(Context,Data)}.
+
+-spec hash_final(_) -> binary().
+
+hash_final({md5,Context}) -> md5_final(Context);
+hash_final({md4,Context}) -> md4_final(Context);
+hash_final({sha,Context}) -> sha_final(Context);
+hash_final({ripemd160,Context}) -> ripemd160_final(Context);
+hash_final({sha224,Context}) -> sha224_final(Context);
+hash_final({sha256,Context}) -> sha256_final(Context);
+hash_final({sha384,Context}) -> sha384_final(Context);
+hash_final({sha512,Context}) -> sha512_final(Context).
+
%%
%% MD5
%%
@@ -207,6 +268,20 @@ md4_update(_Context, _Data) -> ?nif_stub.
md4_final(_Context) -> ?nif_stub.
%%
+%% RIPEMD160
+%%
+
+-spec ripemd160(iodata()) -> binary().
+-spec ripemd160_init() -> binary().
+-spec ripemd160_update(binary(), iodata()) -> binary().
+-spec ripemd160_final(binary()) -> binary().
+
+ripemd160(_Data) -> ?nif_stub.
+ripemd160_init() -> ?nif_stub.
+ripemd160_update(_Context, _Data) -> ?nif_stub.
+ripemd160_final(_Context) -> ?nif_stub.
+
+%%
%% SHA
%%
-spec sha(iodata()) -> binary().
@@ -220,6 +295,40 @@ sha_update(_Context, _Data) -> ?nif_stub.
sha_final(_Context) -> ?nif_stub.
%
+%% SHA224
+%%
+-spec sha224(iodata()) -> binary().
+-spec sha224_init() -> binary().
+-spec sha224_update(binary(), iodata()) -> binary().
+-spec sha224_final(binary()) -> binary().
+
+sha224(Data) ->
+ case sha224_nif(Data) of
+ notsup -> erlang:error(notsup);
+ Bin -> Bin
+ end.
+sha224_init() ->
+ case sha224_init_nif() of
+ notsup -> erlang:error(notsup);
+ Bin -> Bin
+ end.
+sha224_update(Context, Data) ->
+ case sha224_update_nif(Context, Data) of
+ notsup -> erlang:error(notsup);
+ Bin -> Bin
+ end.
+sha224_final(Context) ->
+ case sha224_final_nif(Context) of
+ notsup -> erlang:error(notsup);
+ Bin -> Bin
+ end.
+
+sha224_nif(_Data) -> ?nif_stub.
+sha224_init_nif() -> ?nif_stub.
+sha224_update_nif(_Context, _Data) -> ?nif_stub.
+sha224_final_nif(_Context) -> ?nif_stub.
+
+%
%% SHA256
%%
-spec sha256(iodata()) -> binary().
@@ -254,6 +363,40 @@ sha256_update_nif(_Context, _Data) -> ?nif_stub.
sha256_final_nif(_Context) -> ?nif_stub.
%
+%% SHA384
+%%
+-spec sha384(iodata()) -> binary().
+-spec sha384_init() -> binary().
+-spec sha384_update(binary(), iodata()) -> binary().
+-spec sha384_final(binary()) -> binary().
+
+sha384(Data) ->
+ case sha384_nif(Data) of
+ notsup -> erlang:error(notsup);
+ Bin -> Bin
+ end.
+sha384_init() ->
+ case sha384_init_nif() of
+ notsup -> erlang:error(notsup);
+ Bin -> Bin
+ end.
+sha384_update(Context, Data) ->
+ case sha384_update_nif(Context, Data) of
+ notsup -> erlang:error(notsup);
+ Bin -> Bin
+ end.
+sha384_final(Context) ->
+ case sha384_final_nif(Context) of
+ notsup -> erlang:error(notsup);
+ Bin -> Bin
+ end.
+
+sha384_nif(_Data) -> ?nif_stub.
+sha384_init_nif() -> ?nif_stub.
+sha384_update_nif(_Context, _Data) -> ?nif_stub.
+sha384_final_nif(_Context) -> ?nif_stub.
+
+%
%% SHA512
%%
-spec sha512(iodata()) -> binary().
@@ -294,11 +437,28 @@ sha512_final_nif(_Context) -> ?nif_stub.
%%
%% HMAC (multiple hash options)
%%
+
+-spec hmac(_, iodata(), iodata()) -> binary().
+-spec hmac(_, iodata(), iodata(), integer()) -> binary().
-spec hmac_init(atom(), iodata()) -> binary().
-spec hmac_update(binary(), iodata()) -> binary().
-spec hmac_final(binary()) -> binary().
-spec hmac_final_n(binary(), integer()) -> binary().
+hmac(md5, Key, Data) -> md5_mac(Key, Data);
+hmac(sha, Key, Data) -> sha_mac(Key, Data);
+hmac(sha224, Key, Data) -> sha224_mac(Key, Data);
+hmac(sha256, Key, Data) -> sha256_mac(Key, Data);
+hmac(sha384, Key, Data) -> sha384_mac(Key, Data);
+hmac(sha512, Key, Data) -> sha512_mac(Key, Data).
+
+hmac(md5, Key, Data, Size) -> md5_mac_n(Key, Data, Size);
+hmac(sha, Key, Data, Size) -> sha_mac(Key, Data, Size);
+hmac(sha224, Key, Data, Size) -> sha224_mac(Key, Data, Size);
+hmac(sha256, Key, Data, Size) -> sha256_mac(Key, Data, Size);
+hmac(sha384, Key, Data, Size) -> sha384_mac(Key, Data, Size);
+hmac(sha512, Key, Data, Size) -> sha512_mac(Key, Data, Size).
+
hmac_init(_Type, _Key) -> ?nif_stub.
hmac_update(_Context, _Data) -> ? nif_stub.
hmac_final(_Context) -> ? nif_stub.
@@ -336,6 +496,70 @@ sha_mac_96(Key, Data) ->
sha_mac_n(_Key,_Data,_MacSz) -> ?nif_stub.
%%
+%% SHA224_MAC
+%%
+-spec sha224_mac(iodata(), iodata()) -> binary().
+
+sha224_mac(Key, Data) ->
+ sha224_mac(Key, Data, 224 div 8).
+
+sha224_mac(Key, Data, Size) ->
+ case sha224_mac_nif(Key, Data, Size) of
+ notsup -> erlang:error(notsup);
+ Bin -> Bin
+ end.
+
+sha224_mac_nif(_Key,_Data,_MacSz) -> ?nif_stub.
+
+%%
+%% SHA256_MAC
+%%
+-spec sha256_mac(iodata(), iodata()) -> binary().
+
+sha256_mac(Key, Data) ->
+ sha256_mac(Key, Data, 256 div 8).
+
+sha256_mac(Key, Data, Size) ->
+ case sha256_mac_nif(Key, Data, Size) of
+ notsup -> erlang:error(notsup);
+ Bin -> Bin
+ end.
+
+sha256_mac_nif(_Key,_Data,_MacSz) -> ?nif_stub.
+
+%%
+%% SHA384_MAC
+%%
+-spec sha384_mac(iodata(), iodata()) -> binary().
+
+sha384_mac(Key, Data) ->
+ sha384_mac(Key, Data, 384 div 8).
+
+sha384_mac(Key, Data, Size) ->
+ case sha384_mac_nif(Key, Data, Size) of
+ notsup -> erlang:error(notsup);
+ Bin -> Bin
+ end.
+
+sha384_mac_nif(_Key,_Data,_MacSz) -> ?nif_stub.
+
+%%
+%% SHA512_MAC
+%%
+-spec sha512_mac(iodata(), iodata()) -> binary().
+
+sha512_mac(Key, Data) ->
+ sha512_mac(Key, Data, 512 div 8).
+
+sha512_mac(Key, Data, MacSz) ->
+ case sha512_mac_nif(Key, Data, MacSz) of
+ notsup -> erlang:error(notsup);
+ Bin -> Bin
+ end.
+
+sha512_mac_nif(_Key,_Data,_MacSz) -> ?nif_stub.
+
+%%
%% CRYPTO FUNCTIONS
%%
@@ -415,12 +639,12 @@ des_ecb_crypt(_Key, _Data, _IsEncrypt) -> ?nif_stub.
binary().
des3_cbc_encrypt(Key1, Key2, Key3, IVec, Data) ->
- des_ede3_cbc_encrypt(Key1, Key2, Key3, IVec, Data).
+ des_ede3_cbc_crypt(Key1, Key2, Key3, IVec, Data, true).
des_ede3_cbc_encrypt(Key1, Key2, Key3, IVec, Data) ->
des_ede3_cbc_crypt(Key1, Key2, Key3, IVec, Data, true).
des3_cbc_decrypt(Key1, Key2, Key3, IVec, Data) ->
- des_ede3_cbc_decrypt(Key1, Key2, Key3, IVec, Data).
+ des_ede3_cbc_crypt(Key1, Key2, Key3, IVec, Data, false).
des_ede3_cbc_decrypt(Key1, Key2, Key3, IVec, Data) ->
des_ede3_cbc_crypt(Key1, Key2, Key3, IVec, Data, false).
@@ -435,16 +659,18 @@ des_ede3_cbc_crypt(_Key1, _Key2, _Key3, _IVec, _Data, _IsEncrypt) -> ?nif_stub.
binary().
des3_cfb_encrypt(Key1, Key2, Key3, IVec, Data) ->
- des_ede3_cfb_encrypt(Key1, Key2, Key3, IVec, Data).
-des_ede3_cfb_encrypt(Key1, Key2, Key3, IVec, Data) ->
des_ede3_cfb_crypt(Key1, Key2, Key3, IVec, Data, true).
des3_cfb_decrypt(Key1, Key2, Key3, IVec, Data) ->
- des_ede3_cfb_decrypt(Key1, Key2, Key3, IVec, Data).
-des_ede3_cfb_decrypt(Key1, Key2, Key3, IVec, Data) ->
des_ede3_cfb_crypt(Key1, Key2, Key3, IVec, Data, false).
-des_ede3_cfb_crypt(_Key1, _Key2, _Key3, _IVec, _Data, _IsEncrypt) -> ?nif_stub.
+des_ede3_cfb_crypt(Key1, Key2, Key3, IVec, Data, IsEncrypt) ->
+ case des_ede3_cfb_crypt_nif(Key1,Key2,Key3,IVec,Data,IsEncrypt) of
+ notsup -> erlang:error(notsup);
+ Bin -> Bin
+ end.
+
+des_ede3_cfb_crypt_nif(_Key1, _Key2, _Key3, _IVec, _Data, _IsEncrypt) -> ?nif_stub.
%%
%% Blowfish
@@ -576,10 +802,10 @@ 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()]) ->
+-spec dss_verify(data_or_digest(), binary(), [binary()]) -> boolean().
+-spec dss_verify(dss_digest_type(), data_or_digest(), binary(), [binary()]) -> boolean().
+-spec rsa_verify(data_or_digest(), binary(), [binary()]) -> boolean().
+-spec rsa_verify(rsa_digest_type(), data_or_digest(), binary(), [binary()]) ->
boolean().
%% Key = [P,Q,G,Y] P,Q,G=DSSParams Y=PublicKey
@@ -590,8 +816,8 @@ dss_verify(_Type,_Data,_Signature,_Key) -> ?nif_stub.
% Key = [E,N] E=PublicExponent N=PublicModulus
rsa_verify(Data,Signature,Key) ->
rsa_verify_nif(sha, Data,Signature,Key).
-rsa_verify(Type, Data, Signature, Key) ->
- case rsa_verify_nif(Type, Data, Signature, Key) of
+rsa_verify(Type, DataOrDigest, Signature, Key) ->
+ case rsa_verify_nif(Type, DataOrDigest, Signature, Key) of
notsup -> erlang:error(notsup);
Bool -> Bool
end.
@@ -603,27 +829,27 @@ rsa_verify_nif(_Type, _Data, _Signature, _Key) -> ?nif_stub.
%% DSS, RSA - sign
%%
%% Key = [P,Q,G,X] P,Q,G=DSSParams X=PrivateKey
--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]);
+-spec dss_sign(data_or_digest(), [binary()]) -> binary().
+-spec dss_sign(dss_digest_type(), data_or_digest(), [binary()]) -> binary().
+-spec rsa_sign(data_or_digest(), [binary()]) -> binary().
+-spec rsa_sign(rsa_digest_type(), data_or_digest(), [binary()]) -> binary().
+
+dss_sign(DataOrDigest,Key) ->
+ dss_sign(sha,DataOrDigest,Key).
+dss_sign(Type, DataOrDigest, Key) ->
+ case dss_sign_nif(Type,DataOrDigest,Key) of
+ error -> erlang:error(badkey, [DataOrDigest, Key]);
Sign -> Sign
end.
dss_sign_nif(_Type,_Data,_Key) -> ?nif_stub.
%% Key = [E,N,D] E=PublicExponent N=PublicModulus D=PrivateExponent
-rsa_sign(Data,Key) ->
- rsa_sign(sha, Data, Key).
-rsa_sign(Type, Data, Key) ->
- case rsa_sign_nif(Type,Data,Key) of
- error -> erlang:error(badkey, [Type,Data,Key]);
+rsa_sign(DataOrDigest,Key) ->
+ rsa_sign(sha, DataOrDigest, Key).
+rsa_sign(Type, DataOrDigest, Key) ->
+ case rsa_sign_nif(Type,DataOrDigest,Key) of
+ error -> erlang:error(badkey, [Type,DataOrDigest,Key]);
Sign -> Sign
end.
diff --git a/lib/crypto/test/crypto_SUITE.erl b/lib/crypto/test/crypto_SUITE.erl
index 627c966dfb..142f06677a 100644
--- a/lib/crypto/test/crypto_SUITE.erl
+++ b/lib/crypto/test/crypto_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -33,9 +33,15 @@
sha_update/1,
hmac_update_sha/1,
hmac_update_sha_n/1,
+ hmac_update_sha256/1,
+ hmac_update_sha512/1,
hmac_update_md5/1,
hmac_update_md5_io/1,
hmac_update_md5_n/1,
+ hmac_rfc2202/1,
+ hmac_rfc4231/1,
+ ripemd160/1,
+ ripemd160_update/1,
sha256/1,
sha256_update/1,
sha512/1,
@@ -61,7 +67,9 @@
rsa_verify_test/1,
dsa_verify_test/1,
rsa_sign_test/1,
+ rsa_sign_hash_test/1,
dsa_sign_test/1,
+ dsa_sign_hash_test/1,
rsa_encrypt_decrypt/1,
dh/1,
exor_test/1,
@@ -81,14 +89,18 @@ groups() ->
[{info, [sequence],[info, {group, rest}]},
{rest, [],
[md5, md5_update, md4, md4_update, md5_mac,
- md5_mac_io, sha, sha_update,
- hmac_update_sha, hmac_update_sha_n, hmac_update_md5_n,
- hmac_update_md5_io, hmac_update_md5,
+ md5_mac_io, ripemd160, ripemd160_update, sha, sha_update,
+ sha256, sha256_update, sha512, sha512_update,
+ hmac_update_sha, hmac_update_sha_n, hmac_update_sha256, hmac_update_sha512,
+ hmac_update_md5_n, hmac_update_md5_io, hmac_update_md5,
+ hmac_rfc2202, hmac_rfc4231,
des_cbc, aes_cfb, aes_cbc,
+ des_cfb, des_cfb_iter, des3_cbc, des3_cfb, rc2_cbc,
aes_cbc_iter, aes_ctr, aes_ctr_stream, des_cbc_iter, des_ecb,
rand_uniform_test, strong_rand_test,
rsa_verify_test, dsa_verify_test, rsa_sign_test,
- dsa_sign_test, rsa_encrypt_decrypt, dh, exor_test,
+ rsa_sign_hash_test, dsa_sign_test, dsa_sign_hash_test,
+ rsa_encrypt_decrypt, dh, exor_test,
rc4_test, rc4_stream_test, mod_exp_test, blowfish_cfb64,
smp]}].
@@ -185,7 +197,16 @@ info(Config) when is_list(Config) ->
{skip,"Missing crypto application"};
{_,_} ->
?line crypto:start(),
- ?line crypto:info(),
+ ?line Info = crypto:info(),
+ ?line Exports = lists:usort([F || {F,_} <- crypto:module_info(exports)]),
+ ?line [] = Info -- Exports,
+ ?line NotInInfo = Exports -- Info,
+ io:format("NotInInfo = ~p\n", [NotInInfo]),
+ BlackList = lists:sort([des_ede3_cbc_decrypt, des_ede3_cbc_encrypt,
+ dh_check, dh_generate_parameters,
+ module_info, start, stop, version]),
+ ?line BlackList = NotInInfo,
+
?line InfoLib = crypto:info_lib(),
?line [_|_] = InfoLib,
F = fun([{Name,VerN,VerS}|T],Me) ->
@@ -335,6 +356,51 @@ hmac_update_sha(Config) when is_list(Config) ->
?line Mac = crypto:hmac_final(Ctx3),
?line Exp = crypto:sha_mac(Key, lists:flatten([Data, Data2])),
?line m(Exp, Mac).
+
+hmac_update_sha256(doc) ->
+ ["Generate an SHA256 HMAC using hmac_init, hmac_update, and hmac_final. "
+ "Expected values for examples are generated using crypto:sha256_mac." ];
+hmac_update_sha256(suite) ->
+ [];
+hmac_update_sha256(Config) when is_list(Config) ->
+ if_098(fun() -> hmac_update_sha256_do() end).
+
+
+hmac_update_sha256_do() ->
+ ?line Key = hexstr2bin("00010203101112132021222330313233"
+ "04050607141516172425262734353637"
+ "08090a0b18191a1b28292a2b38393a3b"
+ "0c0d0e0f1c1d1e1f2c2d2e2f3c3d3e3f"),
+ ?line Data = "Sampl",
+ ?line Data2 = "e #1",
+ ?line Ctx = crypto:hmac_init(sha256, Key),
+ ?line Ctx2 = crypto:hmac_update(Ctx, Data),
+ ?line Ctx3 = crypto:hmac_update(Ctx2, Data2),
+ ?line Mac = crypto:hmac_final(Ctx3),
+ ?line Exp = crypto:sha256_mac(Key, lists:flatten([Data, Data2])),
+ ?line m(Exp, Mac).
+
+hmac_update_sha512(doc) ->
+ ["Generate an SHA512 HMAC using hmac_init, hmac_update, and hmac_final. "
+ "Expected values for examples are generated using crypto:sha512_mac." ];
+hmac_update_sha512(suite) ->
+ [];
+hmac_update_sha512(Config) when is_list(Config) ->
+ if_098(fun() -> hmac_update_sha512_do() end).
+
+hmac_update_sha512_do() ->
+ ?line Key = hexstr2bin("00010203101112132021222330313233"
+ "04050607141516172425262734353637"
+ "08090a0b18191a1b28292a2b38393a3b"
+ "0c0d0e0f1c1d1e1f2c2d2e2f3c3d3e3f"),
+ ?line Data = "Sampl",
+ ?line Data2 = "e #1",
+ ?line Ctx = crypto:hmac_init(sha512, Key),
+ ?line Ctx2 = crypto:hmac_update(Ctx, Data),
+ ?line Ctx3 = crypto:hmac_update(Ctx2, Data2),
+ ?line Mac = crypto:hmac_final(Ctx3),
+ ?line Exp = crypto:sha512_mac(Key, lists:flatten([Data, Data2])),
+ ?line m(Exp, Mac).
hmac_update_md5(doc) ->
["Generate an MD5 HMAC using hmac_init, hmac_update, and hmac_final. "
@@ -354,7 +420,528 @@ hmac_update_md5(Config) when is_list(Config) ->
?line Mac2 = crypto:hmac_final(CtxD),
?line Exp2 = crypto:md5_mac(Key2, lists:flatten([Long1, Long2, Long3])),
?line m(Exp2, Mac2).
+
+hmac_rfc2202(doc) ->
+ ["Generate an HMAC using hmac, md5_mac, and sha_mac."
+ "Test vectors are taken from RFC-2202."];
+hmac_rfc2202(suite) ->
+ [];
+hmac_rfc2202(Config) when is_list(Config) ->
+ hmac_rfc2202_md5(),
+ hmac_rfc2202_sha().
+
+hmac_rfc2202_md5() ->
+ %% Test case 1
+ Case1Key = binary:copy(<<16#0b>>, 16),
+ Case1Data = <<"Hi There">>,
+ Case1Exp = hexstr2bin("9294727a3638bb1c13f48ef8158bfc9d"),
+
+ ?line Case1Mac_1 = crypto:md5_mac(Case1Key, Case1Data),
+ ?line Case1Mac_2 = crypto:hmac(md5, Case1Key, Case1Data),
+ ?line m(Case1Exp, Case1Mac_1),
+ ?line m(Case1Exp, Case1Mac_2),
+
+ %% Test case 2
+ Case2Key = <<"Jefe">>,
+ Case2Data = <<"what do ya want for nothing?">>,
+ Case2Exp = hexstr2bin("750c783e6ab0b503eaa86e310a5db738"),
+
+ ?line Case2Mac_1 = crypto:md5_mac(Case2Key, Case2Data),
+ ?line Case2Mac_2 = crypto:hmac(md5, Case2Key, Case2Data),
+ ?line m(Case2Exp, Case2Mac_1),
+ ?line m(Case2Exp, Case2Mac_2),
+
+ %% Test case 3
+ Case3Key = binary:copy(<<16#aa>>, 16),
+ Case3Data = binary:copy(<<16#dd>>, 50),
+ Case3Exp = hexstr2bin("56be34521d144c88dbb8c733f0e8b3f6"),
+
+ ?line Case3Mac_1 = crypto:md5_mac(Case3Key, Case3Data),
+ ?line Case3Mac_2 = crypto:hmac(md5, Case3Key, Case3Data),
+ ?line m(Case3Exp, Case3Mac_1),
+ ?line m(Case3Exp, Case3Mac_2),
+
+ %% Test case 4
+ Case4Key = list_to_binary(lists:seq(1, 16#19)),
+ Case4Data = binary:copy(<<16#cd>>, 50),
+ Case4Exp = hexstr2bin("697eaf0aca3a3aea3a75164746ffaa79"),
+
+ ?line Case4Mac_1 = crypto:md5_mac(Case4Key, Case4Data),
+ ?line Case4Mac_2 = crypto:hmac(md5, Case4Key, Case4Data),
+ ?line m(Case4Exp, Case4Mac_1),
+ ?line m(Case4Exp, Case4Mac_2),
+
+ %% Test case 5
+ Case5Key = binary:copy(<<16#0c>>, 16),
+ Case5Data = "Test With Truncation",
+ Case5Exp = hexstr2bin("56461ef2342edc00f9bab995690efd4c"),
+ Case5Exp96 = hexstr2bin("56461ef2342edc00f9bab995"),
+
+ ?line Case5Mac_1 = crypto:md5_mac(Case5Key, Case5Data),
+ ?line Case5Mac_2 = crypto:hmac(md5, Case5Key, Case5Data),
+ ?line Case5Mac96_1 = crypto:md5_mac_96(Case5Key, Case5Data),
+ ?line Case5Mac96_2 = crypto:hmac(md5, Case5Key, Case5Data, 12),
+ ?line m(Case5Exp, Case5Mac_1),
+ ?line m(Case5Exp, Case5Mac_2),
+ ?line m(Case5Exp96, Case5Mac96_1),
+ ?line m(Case5Exp96, Case5Mac96_2),
+
+ %% Test case 6
+ Case6Key = binary:copy(<<16#aa>>, 80),
+ Case6Data = <<"Test Using Larger Than Block-Size Key - Hash Key First">>,
+ Case6Exp = hexstr2bin("6b1ab7fe4bd7bf8f0b62e6ce61b9d0cd"),
+
+ ?line Case6Mac_1 = crypto:md5_mac(Case6Key, Case6Data),
+ ?line Case6Mac_2 = crypto:hmac(md5, Case6Key, Case6Data),
+ ?line m(Case6Exp, Case6Mac_1),
+ ?line m(Case6Exp, Case6Mac_2),
+
+ %% Test case 7
+ Case7Key = binary:copy(<<16#aa>>, 80),
+ Case7Data = <<"Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data">>,
+ Case7Exp = hexstr2bin("6f630fad67cda0ee1fb1f562db3aa53e"),
+
+ ?line Case7Mac_1 = crypto:md5_mac(Case7Key, Case7Data),
+ ?line Case7Mac_2 = crypto:hmac(md5, Case7Key, Case7Data),
+ ?line m(Case7Exp, Case7Mac_1),
+ ?line m(Case7Exp, Case7Mac_2).
+
+hmac_rfc2202_sha() ->
+ %% Test case 1
+ Case1Key = binary:copy(<<16#0b>>, 20),
+ Case1Data = <<"Hi There">>,
+ Case1Exp = hexstr2bin("b617318655057264e28bc0b6fb378c8ef146be00"),
+
+ ?line Case1Mac_1 = crypto:sha_mac(Case1Key, Case1Data),
+ ?line Case1Mac_2 = crypto:hmac(sha, Case1Key, Case1Data),
+ ?line m(Case1Exp, Case1Mac_1),
+ ?line m(Case1Exp, Case1Mac_2),
+
+ %% Test case 2
+ Case2Key = <<"Jefe">>,
+ Case2Data = <<"what do ya want for nothing?">>,
+ Case2Exp = hexstr2bin("effcdf6ae5eb2fa2d27416d5f184df9c259a7c79"),
+
+ ?line Case2Mac_1 = crypto:sha_mac(Case2Key, Case2Data),
+ ?line Case2Mac_2 = crypto:hmac(sha, Case2Key, Case2Data),
+ ?line m(Case2Exp, Case2Mac_1),
+ ?line m(Case2Exp, Case2Mac_2),
+
+ %% Test case 3
+ Case3Key = binary:copy(<<16#aa>>, 20),
+ Case3Data = binary:copy(<<16#dd>>, 50),
+ Case3Exp = hexstr2bin("125d7342b9ac11cd91a39af48aa17b4f63f175d3"),
+
+ ?line Case3Mac_1 = crypto:sha_mac(Case3Key, Case3Data),
+ ?line Case3Mac_2 = crypto:hmac(sha, Case3Key, Case3Data),
+ ?line m(Case3Exp, Case3Mac_1),
+ ?line m(Case3Exp, Case3Mac_2),
+
+ %% Test case 4
+ Case4Key = list_to_binary(lists:seq(1, 16#19)),
+ Case4Data = binary:copy(<<16#cd>>, 50),
+ Case4Exp = hexstr2bin("4c9007f4026250c6bc8414f9bf50c86c2d7235da"),
+
+ ?line Case4Mac_1 = crypto:sha_mac(Case4Key, Case4Data),
+ ?line Case4Mac_2 = crypto:hmac(sha, Case4Key, Case4Data),
+ ?line m(Case4Exp, Case4Mac_1),
+ ?line m(Case4Exp, Case4Mac_2),
+
+ %% Test case 5
+ Case5Key = binary:copy(<<16#0c>>, 20),
+ Case5Data = "Test With Truncation",
+ Case5Exp = hexstr2bin("4c1a03424b55e07fe7f27be1d58bb9324a9a5a04"),
+ Case5Exp96 = hexstr2bin("4c1a03424b55e07fe7f27be1"),
+
+ ?line Case5Mac_1 = crypto:sha_mac(Case5Key, Case5Data),
+ ?line Case5Mac_2 = crypto:hmac(sha, Case5Key, Case5Data),
+ ?line Case5Mac96_1 = crypto:sha_mac_96(Case5Key, Case5Data),
+ ?line Case5Mac96_2 = crypto:hmac(sha, Case5Key, Case5Data, 12),
+ ?line m(Case5Exp, Case5Mac_1),
+ ?line m(Case5Exp, Case5Mac_2),
+ ?line m(Case5Exp96, Case5Mac96_1),
+ ?line m(Case5Exp96, Case5Mac96_2),
+
+ %% Test case 6
+ Case6Key = binary:copy(<<16#aa>>, 80),
+ Case6Data = <<"Test Using Larger Than Block-Size Key - Hash Key First">>,
+ Case6Exp = hexstr2bin("aa4ae5e15272d00e95705637ce8a3b55ed402112"),
+
+ ?line Case6Mac_1 = crypto:sha_mac(Case6Key, Case6Data),
+ ?line Case6Mac_2 = crypto:hmac(sha, Case6Key, Case6Data),
+ ?line m(Case6Exp, Case6Mac_1),
+ ?line m(Case6Exp, Case6Mac_2),
+
+ %% Test case 7
+ Case7Key = binary:copy(<<16#aa>>, 80),
+ Case7Data = <<"Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data">>,
+ Case7Exp = hexstr2bin("e8e99d0f45237d786d6bbaa7965c7808bbff1a91"),
+
+ ?line Case7Mac_1 = crypto:sha_mac(Case7Key, Case7Data),
+ ?line Case7Mac_2 = crypto:hmac(sha, Case7Key, Case7Data),
+ ?line m(Case7Exp, Case7Mac_1),
+ ?line m(Case7Exp, Case7Mac_2).
+
+hmac_rfc4231(doc) ->
+ ["Generate an HMAC using crypto:shaXXX_mac, hmac, and hmac_init, hmac_update, and hmac_final. "
+ "Testvectors are take from RFC4231." ];
+hmac_rfc4231(suite) ->
+ [];
+hmac_rfc4231(Config) when is_list(Config) ->
+ if_098(fun() -> hmac_rfc4231_do() end).
+
+hmac_rfc4231_do() ->
+ %% Test Case 1
+ Case1Key = binary:copy(<<16#0b>>, 20),
+ Case1Data = <<"Hi There">>,
+ Case1Exp224 = hexstr2bin("896fb1128abbdf196832107cd49df33f"
+ "47b4b1169912ba4f53684b22"),
+ Case1Exp256 = hexstr2bin("b0344c61d8db38535ca8afceaf0bf12b"
+ "881dc200c9833da726e9376c2e32cff7"),
+ Case1Exp384 = hexstr2bin("afd03944d84895626b0825f4ab46907f"
+ "15f9dadbe4101ec682aa034c7cebc59c"
+ "faea9ea9076ede7f4af152e8b2fa9cb6"),
+ Case1Exp512 = hexstr2bin("87aa7cdea5ef619d4ff0b4241a1d6cb0"
+ "2379f4e2ce4ec2787ad0b30545e17cde"
+ "daa833b7d6b8a702038b274eaea3f4e4"
+ "be9d914eeb61f1702e696c203a126854"),
+
+ ?line Case1Ctx224 = crypto:hmac_init(sha224, Case1Key),
+ ?line Case1Ctx224_2 = crypto:hmac_update(Case1Ctx224, Case1Data),
+ ?line Case1Mac224_1 = crypto:hmac_final(Case1Ctx224_2),
+ ?line Case1Mac224_2 = crypto:sha224_mac(Case1Key, Case1Data),
+ ?line Case1Mac224_3 = crypto:hmac(sha224, Case1Key, Case1Data),
+ ?line m(Case1Exp224, Case1Mac224_1),
+ ?line m(Case1Exp224, Case1Mac224_2),
+ ?line m(Case1Exp224, Case1Mac224_3),
+
+ ?line Case1Ctx256 = crypto:hmac_init(sha256, Case1Key),
+ ?line Case1Ctx256_2 = crypto:hmac_update(Case1Ctx256, Case1Data),
+ ?line Case1Mac256_1 = crypto:hmac_final(Case1Ctx256_2),
+ ?line Case1Mac256_2 = crypto:sha256_mac(Case1Key, Case1Data),
+ ?line Case1Mac256_3 = crypto:hmac(sha256, Case1Key, Case1Data),
+ ?line m(Case1Exp256, Case1Mac256_1),
+ ?line m(Case1Exp256, Case1Mac256_2),
+ ?line m(Case1Exp256, Case1Mac256_3),
+
+ ?line Case1Ctx384 = crypto:hmac_init(sha384, Case1Key),
+ ?line Case1Ctx384_2 = crypto:hmac_update(Case1Ctx384, Case1Data),
+ ?line Case1Mac384_1 = crypto:hmac_final(Case1Ctx384_2),
+ ?line Case1Mac384_2 = crypto:sha384_mac(Case1Key, Case1Data),
+ ?line Case1Mac384_3 = crypto:hmac(sha384, Case1Key, Case1Data),
+ ?line m(Case1Exp384, Case1Mac384_1),
+ ?line m(Case1Exp384, Case1Mac384_2),
+ ?line m(Case1Exp384, Case1Mac384_3),
+
+ ?line Case1Ctx512 = crypto:hmac_init(sha512, Case1Key),
+ ?line Case1Ctx512_2 = crypto:hmac_update(Case1Ctx512, Case1Data),
+ ?line Case1Mac512_1 = crypto:hmac_final(Case1Ctx512_2),
+ ?line Case1Mac512_2 = crypto:sha512_mac(Case1Key, Case1Data),
+ ?line Case1Mac512_3 = crypto:hmac(sha512, Case1Key, Case1Data),
+ ?line m(Case1Exp512, Case1Mac512_1),
+ ?line m(Case1Exp512, Case1Mac512_2),
+ ?line m(Case1Exp512, Case1Mac512_3),
+
+ %% Test Case 2
+ Case2Key = <<"Jefe">>,
+ Case2Data = <<"what do ya want for nothing?">>,
+ Case2Exp224 = hexstr2bin("a30e01098bc6dbbf45690f3a7e9e6d0f"
+ "8bbea2a39e6148008fd05e44"),
+ Case2Exp256 = hexstr2bin("5bdcc146bf60754e6a042426089575c7"
+ "5a003f089d2739839dec58b964ec3843"),
+ Case2Exp384 = hexstr2bin("af45d2e376484031617f78d2b58a6b1b"
+ "9c7ef464f5a01b47e42ec3736322445e"
+ "8e2240ca5e69e2c78b3239ecfab21649"),
+ Case2Exp512 = hexstr2bin("164b7a7bfcf819e2e395fbe73b56e0a3"
+ "87bd64222e831fd610270cd7ea250554"
+ "9758bf75c05a994a6d034f65f8f0e6fd"
+ "caeab1a34d4a6b4b636e070a38bce737"),
+
+ ?line Case2Ctx224 = crypto:hmac_init(sha224, Case2Key),
+ ?line Case2Ctx224_2 = crypto:hmac_update(Case2Ctx224, Case2Data),
+ ?line Case2Mac224_1 = crypto:hmac_final(Case2Ctx224_2),
+ ?line Case2Mac224_2 = crypto:sha224_mac(Case2Key, Case2Data),
+ ?line Case2Mac224_3 = crypto:hmac(sha224, Case2Key, Case2Data),
+ ?line m(Case2Exp224, Case2Mac224_1),
+ ?line m(Case2Exp224, Case2Mac224_2),
+ ?line m(Case2Exp224, Case2Mac224_3),
+
+ ?line Case2Ctx256 = crypto:hmac_init(sha256, Case2Key),
+ ?line Case2Ctx256_2 = crypto:hmac_update(Case2Ctx256, Case2Data),
+ ?line Case2Mac256_1 = crypto:hmac_final(Case2Ctx256_2),
+ ?line Case2Mac256_2 = crypto:sha256_mac(Case2Key, Case2Data),
+ ?line Case2Mac256_3 = crypto:hmac(sha256, Case2Key, Case2Data),
+ ?line m(Case2Exp256, Case2Mac256_1),
+ ?line m(Case2Exp256, Case2Mac256_2),
+ ?line m(Case2Exp256, Case2Mac256_3),
+
+ ?line Case2Ctx384 = crypto:hmac_init(sha384, Case2Key),
+ ?line Case2Ctx384_2 = crypto:hmac_update(Case2Ctx384, Case2Data),
+ ?line Case2Mac384_1 = crypto:hmac_final(Case2Ctx384_2),
+ ?line Case2Mac384_2 = crypto:sha384_mac(Case2Key, Case2Data),
+ ?line Case2Mac384_3 = crypto:hmac(sha384, Case2Key, Case2Data),
+ ?line m(Case2Exp384, Case2Mac384_1),
+ ?line m(Case2Exp384, Case2Mac384_2),
+ ?line m(Case2Exp384, Case2Mac384_3),
+
+ ?line Case2Ctx512 = crypto:hmac_init(sha512, Case2Key),
+ ?line Case2Ctx512_2 = crypto:hmac_update(Case2Ctx512, Case2Data),
+ ?line Case2Mac512_1 = crypto:hmac_final(Case2Ctx512_2),
+ ?line Case2Mac512_2 = crypto:sha512_mac(Case2Key, Case2Data),
+ ?line Case2Mac512_3 = crypto:hmac(sha512, Case2Key, Case2Data),
+ ?line m(Case2Exp512, Case2Mac512_1),
+ ?line m(Case2Exp512, Case2Mac512_2),
+ ?line m(Case2Exp512, Case2Mac512_3),
+
+ %% Test Case 3
+ Case3Key = binary:copy(<<16#aa>>, 20),
+ Case3Data = binary:copy(<<16#dd>>, 50),
+ Case3Exp224 = hexstr2bin("7fb3cb3588c6c1f6ffa9694d7d6ad264"
+ "9365b0c1f65d69d1ec8333ea"),
+ Case3Exp256 = hexstr2bin("773ea91e36800e46854db8ebd09181a7"
+ "2959098b3ef8c122d9635514ced565fe"),
+ Case3Exp384 = hexstr2bin("88062608d3e6ad8a0aa2ace014c8a86f"
+ "0aa635d947ac9febe83ef4e55966144b"
+ "2a5ab39dc13814b94e3ab6e101a34f27"),
+ Case3Exp512 = hexstr2bin("fa73b0089d56a284efb0f0756c890be9"
+ "b1b5dbdd8ee81a3655f83e33b2279d39"
+ "bf3e848279a722c806b485a47e67c807"
+ "b946a337bee8942674278859e13292fb"),
+
+ ?line Case3Ctx224 = crypto:hmac_init(sha224, Case3Key),
+ ?line Case3Ctx224_2 = crypto:hmac_update(Case3Ctx224, Case3Data),
+ ?line Case3Mac224_1 = crypto:hmac_final(Case3Ctx224_2),
+ ?line Case3Mac224_2 = crypto:sha224_mac(Case3Key, Case3Data),
+ ?line Case3Mac224_3 = crypto:hmac(sha224, Case3Key, Case3Data),
+ ?line m(Case3Exp224, Case3Mac224_1),
+ ?line m(Case3Exp224, Case3Mac224_2),
+ ?line m(Case3Exp224, Case3Mac224_3),
+
+ ?line Case3Ctx256 = crypto:hmac_init(sha256, Case3Key),
+ ?line Case3Ctx256_2 = crypto:hmac_update(Case3Ctx256, Case3Data),
+ ?line Case3Mac256_1 = crypto:hmac_final(Case3Ctx256_2),
+ ?line Case3Mac256_2 = crypto:sha256_mac(Case3Key, Case3Data),
+ ?line Case3Mac256_3 = crypto:hmac(sha256, Case3Key, Case3Data),
+ ?line m(Case3Exp256, Case3Mac256_1),
+ ?line m(Case3Exp256, Case3Mac256_2),
+ ?line m(Case3Exp256, Case3Mac256_3),
+
+ ?line Case3Ctx384 = crypto:hmac_init(sha384, Case3Key),
+ ?line Case3Ctx384_2 = crypto:hmac_update(Case3Ctx384, Case3Data),
+ ?line Case3Mac384_1 = crypto:hmac_final(Case3Ctx384_2),
+ ?line Case3Mac384_2 = crypto:sha384_mac(Case3Key, Case3Data),
+ ?line Case3Mac384_3 = crypto:hmac(sha384, Case3Key, Case3Data),
+ ?line m(Case3Exp384, Case3Mac384_1),
+ ?line m(Case3Exp384, Case3Mac384_2),
+ ?line m(Case3Exp384, Case3Mac384_3),
+
+ ?line Case3Ctx512 = crypto:hmac_init(sha512, Case3Key),
+ ?line Case3Ctx512_2 = crypto:hmac_update(Case3Ctx512, Case3Data),
+ ?line Case3Mac512_1 = crypto:hmac_final(Case3Ctx512_2),
+ ?line Case3Mac512_2 = crypto:sha512_mac(Case3Key, Case3Data),
+ ?line Case3Mac512_3 = crypto:hmac(sha512, Case3Key, Case3Data),
+ ?line m(Case3Exp512, Case3Mac512_1),
+ ?line m(Case3Exp512, Case3Mac512_2),
+ ?line m(Case3Exp512, Case3Mac512_3),
+
+ %% Test Case 4
+ Case4Key = list_to_binary(lists:seq(1, 16#19)),
+ Case4Data = binary:copy(<<16#cd>>, 50),
+ Case4Exp224 = hexstr2bin("6c11506874013cac6a2abc1bb382627c"
+ "ec6a90d86efc012de7afec5a"),
+ Case4Exp256 = hexstr2bin("82558a389a443c0ea4cc819899f2083a"
+ "85f0faa3e578f8077a2e3ff46729665b"),
+ Case4Exp384 = hexstr2bin("3e8a69b7783c25851933ab6290af6ca7"
+ "7a9981480850009cc5577c6e1f573b4e"
+ "6801dd23c4a7d679ccf8a386c674cffb"),
+ Case4Exp512 = hexstr2bin("b0ba465637458c6990e5a8c5f61d4af7"
+ "e576d97ff94b872de76f8050361ee3db"
+ "a91ca5c11aa25eb4d679275cc5788063"
+ "a5f19741120c4f2de2adebeb10a298dd"),
+
+ ?line Case4Ctx224 = crypto:hmac_init(sha224, Case4Key),
+ ?line Case4Ctx224_2 = crypto:hmac_update(Case4Ctx224, Case4Data),
+ ?line Case4Mac224_1 = crypto:hmac_final(Case4Ctx224_2),
+ ?line Case4Mac224_2 = crypto:sha224_mac(Case4Key, Case4Data),
+ ?line Case4Mac224_3 = crypto:hmac(sha224, Case4Key, Case4Data),
+ ?line m(Case4Exp224, Case4Mac224_1),
+ ?line m(Case4Exp224, Case4Mac224_2),
+ ?line m(Case4Exp224, Case4Mac224_3),
+
+ ?line Case4Ctx256 = crypto:hmac_init(sha256, Case4Key),
+ ?line Case4Ctx256_2 = crypto:hmac_update(Case4Ctx256, Case4Data),
+ ?line Case4Mac256_1 = crypto:hmac_final(Case4Ctx256_2),
+ ?line Case4Mac256_2 = crypto:sha256_mac(Case4Key, Case4Data),
+ ?line Case4Mac256_3 = crypto:hmac(sha256, Case4Key, Case4Data),
+ ?line m(Case4Exp256, Case4Mac256_1),
+ ?line m(Case4Exp256, Case4Mac256_2),
+ ?line m(Case4Exp256, Case4Mac256_3),
+
+ ?line Case4Ctx384 = crypto:hmac_init(sha384, Case4Key),
+ ?line Case4Ctx384_2 = crypto:hmac_update(Case4Ctx384, Case4Data),
+ ?line Case4Mac384_1 = crypto:hmac_final(Case4Ctx384_2),
+ ?line Case4Mac384_2 = crypto:sha384_mac(Case4Key, Case4Data),
+ ?line Case4Mac384_3 = crypto:hmac(sha384, Case4Key, Case4Data),
+ ?line m(Case4Exp384, Case4Mac384_1),
+ ?line m(Case4Exp384, Case4Mac384_2),
+ ?line m(Case4Exp384, Case4Mac384_3),
+
+ ?line Case4Ctx512 = crypto:hmac_init(sha512, Case4Key),
+ ?line Case4Ctx512_2 = crypto:hmac_update(Case4Ctx512, Case4Data),
+ ?line Case4Mac512_1 = crypto:hmac_final(Case4Ctx512_2),
+ ?line Case4Mac512_2 = crypto:sha512_mac(Case4Key, Case4Data),
+ ?line Case4Mac512_3 = crypto:hmac(sha512, Case4Key, Case4Data),
+ ?line m(Case4Exp512, Case4Mac512_1),
+ ?line m(Case4Exp512, Case4Mac512_2),
+ ?line m(Case4Exp512, Case4Mac512_3),
+
+ %% Test Case 5
+ Case5Key = binary:copy(<<16#0c>>, 20),
+ Case5Data = <<"Test With Truncation">>,
+ Case5Exp224 = hexstr2bin("0e2aea68a90c8d37c988bcdb9fca6fa8"),
+ Case5Exp256 = hexstr2bin("a3b6167473100ee06e0c796c2955552b"),
+ Case5Exp384 = hexstr2bin("3abf34c3503b2a23a46efc619baef897"),
+ Case5Exp512 = hexstr2bin("415fad6271580a531d4179bc891d87a6"),
+
+ ?line Case5Ctx224 = crypto:hmac_init(sha224, Case5Key),
+ ?line Case5Ctx224_2 = crypto:hmac_update(Case5Ctx224, Case5Data),
+ ?line Case5Mac224_1 = crypto:hmac_final_n(Case5Ctx224_2, 16),
+ ?line Case5Mac224_2 = crypto:sha224_mac(Case5Key, Case5Data, 16),
+ ?line Case5Mac224_3 = crypto:hmac(sha224, Case5Key, Case5Data, 16),
+ ?line m(Case5Exp224, Case5Mac224_1),
+ ?line m(Case5Exp224, Case5Mac224_2),
+ ?line m(Case5Exp224, Case5Mac224_3),
+
+ ?line Case5Ctx256 = crypto:hmac_init(sha256, Case5Key),
+ ?line Case5Ctx256_2 = crypto:hmac_update(Case5Ctx256, Case5Data),
+ ?line Case5Mac256_1 = crypto:hmac_final_n(Case5Ctx256_2, 16),
+ ?line Case5Mac256_2 = crypto:sha256_mac(Case5Key, Case5Data, 16),
+ ?line Case5Mac256_3 = crypto:hmac(sha256, Case5Key, Case5Data, 16),
+ ?line m(Case5Exp256, Case5Mac256_1),
+ ?line m(Case5Exp256, Case5Mac256_2),
+ ?line m(Case5Exp256, Case5Mac256_3),
+
+ ?line Case5Ctx384 = crypto:hmac_init(sha384, Case5Key),
+ ?line Case5Ctx384_2 = crypto:hmac_update(Case5Ctx384, Case5Data),
+ ?line Case5Mac384_1 = crypto:hmac_final_n(Case5Ctx384_2, 16),
+ ?line Case5Mac384_2 = crypto:sha384_mac(Case5Key, Case5Data, 16),
+ ?line Case5Mac384_3 = crypto:hmac(sha384, Case5Key, Case5Data, 16),
+ ?line m(Case5Exp384, Case5Mac384_1),
+ ?line m(Case5Exp384, Case5Mac384_2),
+ ?line m(Case5Exp384, Case5Mac384_3),
+
+ ?line Case5Ctx512 = crypto:hmac_init(sha512, Case5Key),
+ ?line Case5Ctx512_2 = crypto:hmac_update(Case5Ctx512, Case5Data),
+ ?line Case5Mac512_1 = crypto:hmac_final_n(Case5Ctx512_2, 16),
+ ?line Case5Mac512_2 = crypto:sha512_mac(Case5Key, Case5Data, 16),
+ ?line Case5Mac512_3 = crypto:hmac(sha512, Case5Key, Case5Data, 16),
+ ?line m(Case5Exp512, Case5Mac512_1),
+ ?line m(Case5Exp512, Case5Mac512_2),
+ ?line m(Case5Exp512, Case5Mac512_3),
+
+ %% Test Case 6
+ Case6Key = binary:copy(<<16#aa>>, 131),
+ Case6Data = <<"Test Using Larger Than Block-Size Key - Hash Key First">>,
+ Case6Exp224 = hexstr2bin("95e9a0db962095adaebe9b2d6f0dbce2"
+ "d499f112f2d2b7273fa6870e"),
+ Case6Exp256 = hexstr2bin("60e431591ee0b67f0d8a26aacbf5b77f"
+ "8e0bc6213728c5140546040f0ee37f54"),
+ Case6Exp384 = hexstr2bin("4ece084485813e9088d2c63a041bc5b4"
+ "4f9ef1012a2b588f3cd11f05033ac4c6"
+ "0c2ef6ab4030fe8296248df163f44952"),
+ Case6Exp512 = hexstr2bin("80b24263c7c1a3ebb71493c1dd7be8b4"
+ "9b46d1f41b4aeec1121b013783f8f352"
+ "6b56d037e05f2598bd0fd2215d6a1e52"
+ "95e64f73f63f0aec8b915a985d786598"),
+
+ ?line Case6Ctx224 = crypto:hmac_init(sha224, Case6Key),
+ ?line Case6Ctx224_2 = crypto:hmac_update(Case6Ctx224, Case6Data),
+ ?line Case6Mac224_1 = crypto:hmac_final(Case6Ctx224_2),
+ ?line Case6Mac224_2 = crypto:sha224_mac(Case6Key, Case6Data),
+ ?line Case6Mac224_3 = crypto:hmac(sha224, Case6Key, Case6Data),
+ ?line m(Case6Exp224, Case6Mac224_1),
+ ?line m(Case6Exp224, Case6Mac224_2),
+ ?line m(Case6Exp224, Case6Mac224_3),
+
+ ?line Case6Ctx256 = crypto:hmac_init(sha256, Case6Key),
+ ?line Case6Ctx256_2 = crypto:hmac_update(Case6Ctx256, Case6Data),
+ ?line Case6Mac256_1 = crypto:hmac_final(Case6Ctx256_2),
+ ?line Case6Mac256_2 = crypto:sha256_mac(Case6Key, Case6Data),
+ ?line Case6Mac256_3 = crypto:hmac(sha256, Case6Key, Case6Data),
+ ?line m(Case6Exp256, Case6Mac256_1),
+ ?line m(Case6Exp256, Case6Mac256_2),
+ ?line m(Case6Exp256, Case6Mac256_3),
+
+ ?line Case6Ctx384 = crypto:hmac_init(sha384, Case6Key),
+ ?line Case6Ctx384_2 = crypto:hmac_update(Case6Ctx384, Case6Data),
+ ?line Case6Mac384_1 = crypto:hmac_final(Case6Ctx384_2),
+ ?line Case6Mac384_2 = crypto:sha384_mac(Case6Key, Case6Data),
+ ?line Case6Mac384_3 = crypto:hmac(sha384, Case6Key, Case6Data),
+ ?line m(Case6Exp384, Case6Mac384_1),
+ ?line m(Case6Exp384, Case6Mac384_2),
+ ?line m(Case6Exp384, Case6Mac384_3),
+
+ ?line Case6Ctx512 = crypto:hmac_init(sha512, Case6Key),
+ ?line Case6Ctx512_2 = crypto:hmac_update(Case6Ctx512, Case6Data),
+ ?line Case6Mac512_1 = crypto:hmac_final(Case6Ctx512_2),
+ ?line Case6Mac512_2 = crypto:sha512_mac(Case6Key, Case6Data),
+ ?line Case6Mac512_3 = crypto:hmac(sha512, Case6Key, Case6Data),
+ ?line m(Case6Exp512, Case6Mac512_1),
+ ?line m(Case6Exp512, Case6Mac512_2),
+ ?line m(Case6Exp512, Case6Mac512_3),
+ %% Test Case 7
+ Case7Key = binary:copy(<<16#aa>>, 131),
+ Case7Data = <<"This is a test using a larger than block-size key and a larger t",
+ "han block-size data. The key needs to be hashed before being use",
+ "d by the HMAC algorithm.">>,
+ Case7Exp224 = hexstr2bin("3a854166ac5d9f023f54d517d0b39dbd"
+ "946770db9c2b95c9f6f565d1"),
+ Case7Exp256 = hexstr2bin("9b09ffa71b942fcb27635fbcd5b0e944"
+ "bfdc63644f0713938a7f51535c3a35e2"),
+ Case7Exp384 = hexstr2bin("6617178e941f020d351e2f254e8fd32c"
+ "602420feb0b8fb9adccebb82461e99c5"
+ "a678cc31e799176d3860e6110c46523e"),
+ Case7Exp512 = hexstr2bin("e37b6a775dc87dbaa4dfa9f96e5e3ffd"
+ "debd71f8867289865df5a32d20cdc944"
+ "b6022cac3c4982b10d5eeb55c3e4de15"
+ "134676fb6de0446065c97440fa8c6a58"),
+
+ ?line Case7Ctx224 = crypto:hmac_init(sha224, Case7Key),
+ ?line Case7Ctx224_2 = crypto:hmac_update(Case7Ctx224, Case7Data),
+ ?line Case7Mac224_1 = crypto:hmac_final(Case7Ctx224_2),
+ ?line Case7Mac224_2 = crypto:sha224_mac(Case7Key, Case7Data),
+ ?line Case7Mac224_3 = crypto:hmac(sha224, Case7Key, Case7Data),
+ ?line m(Case7Exp224, Case7Mac224_1),
+ ?line m(Case7Exp224, Case7Mac224_2),
+ ?line m(Case7Exp224, Case7Mac224_3),
+
+ ?line Case7Ctx256 = crypto:hmac_init(sha256, Case7Key),
+ ?line Case7Ctx256_2 = crypto:hmac_update(Case7Ctx256, Case7Data),
+ ?line Case7Mac256_1 = crypto:hmac_final(Case7Ctx256_2),
+ ?line Case7Mac256_2 = crypto:sha256_mac(Case7Key, Case7Data),
+ ?line Case7Mac256_3 = crypto:hmac(sha256, Case7Key, Case7Data),
+ ?line m(Case7Exp256, Case7Mac256_1),
+ ?line m(Case7Exp256, Case7Mac256_2),
+ ?line m(Case7Exp256, Case7Mac256_3),
+
+ ?line Case7Ctx384 = crypto:hmac_init(sha384, Case7Key),
+ ?line Case7Ctx384_2 = crypto:hmac_update(Case7Ctx384, Case7Data),
+ ?line Case7Mac384_1 = crypto:hmac_final(Case7Ctx384_2),
+ ?line Case7Mac384_2 = crypto:sha384_mac(Case7Key, Case7Data),
+ ?line Case7Mac384_3 = crypto:hmac(sha384, Case7Key, Case7Data),
+ ?line m(Case7Exp384, Case7Mac384_1),
+ ?line m(Case7Exp384, Case7Mac384_2),
+ ?line m(Case7Exp384, Case7Mac384_3),
+
+ ?line Case7Ctx512 = crypto:hmac_init(sha512, Case7Key),
+ ?line Case7Ctx512_2 = crypto:hmac_update(Case7Ctx512, Case7Data),
+ ?line Case7Mac512_1 = crypto:hmac_final(Case7Ctx512_2),
+ ?line Case7Mac512_2 = crypto:sha512_mac(Case7Key, Case7Data),
+ ?line Case7Mac512_3 = crypto:hmac(sha512, Case7Key, Case7Data),
+ ?line m(Case7Exp512, Case7Mac512_1),
+ ?line m(Case7Exp512, Case7Mac512_2),
+ ?line m(Case7Exp512, Case7Mac512_3).
hmac_update_md5_io(doc) ->
["Generate an MD5 HMAC using hmac_init, hmac_update, and hmac_final. "
@@ -388,7 +975,33 @@ hmac_update_md5_n(Config) when is_list(Config) ->
?line Mac = crypto:hmac_final_n(Ctx3, 12),
?line Exp = crypto:md5_mac_96(Key, lists:flatten([Data, Data2])),
?line m(Exp, Mac).
-
+%%
+%%
+ripemd160(doc) ->
+ ["Generate RIPEMD160 message digests and check the result."];
+ripemd160(suite) ->
+ [];
+ripemd160(Config) when is_list(Config) ->
+ ?line m(crypto:hash(ripemd160,"abc"),
+ hexstr2bin("8EB208F7E05D987A9B044A8E98C6B087F15A0BFC")),
+ ?line m(crypto:hash(ripemd160,"abcdbcdecdefdefgefghfghighijhijkijkljklmklm"
+ "nlmnomnopnopq"),
+ hexstr2bin("12A053384A9C0C88E405A06C27DCF49ADA62EB2B")).
+
+
+%%
+%%
+ripemd160_update(doc) ->
+ ["Generate RIPEMD160 message digests by using ripemd160_init,"
+ "ripemd160_update, and ripemd160_final and check the result."];
+ripemd160_update(suite) ->
+ [];
+ripemd160_update(Config) when is_list(Config) ->
+ ?line Ctx = crypto:hash_init(ripemd160),
+ ?line Ctx1 = crypto:hash_update(Ctx, "abcdbcdecdefdefgefghfghighi"),
+ ?line Ctx2 = crypto:hash_update(Ctx1, "jhijkijkljklmklmnlmnomnopnopq"),
+ ?line m(crypto:hash_final(Ctx2),
+ hexstr2bin("12A053384A9C0C88E405A06C27DCF49ADA62EB2B")).
%%
%%
@@ -412,6 +1025,9 @@ sha256(doc) ->
sha256(suite) ->
[];
sha256(Config) when is_list(Config) ->
+ if_098(fun() -> sha256_do() end).
+
+sha256_do() ->
?line m(crypto:sha256("abc"),
hexstr2bin("BA7816BF8F01CFEA4141"
"40DE5DAE2223B00361A396177A9CB410FF61F20015AD")),
@@ -428,6 +1044,9 @@ sha256_update(doc) ->
sha256_update(suite) ->
[];
sha256_update(Config) when is_list(Config) ->
+ if_098(fun() -> sha256_update_do() end).
+
+sha256_update_do() ->
?line Ctx = crypto:sha256_init(),
?line Ctx1 = crypto:sha256_update(Ctx, "abcdbcdecdefdefgefghfghighi"),
?line Ctx2 = crypto:sha256_update(Ctx1, "jhijkijkljklmklmnlmnomnopnopq"),
@@ -444,6 +1063,9 @@ sha512(doc) ->
sha512(suite) ->
[];
sha512(Config) when is_list(Config) ->
+ if_098(fun() -> sha512_do() end).
+
+sha512_do() ->
?line m(crypto:sha512("abc"),
hexstr2bin("DDAF35A193617ABACC417349AE20413112E6FA4E89A97EA2"
"0A9EEEE64B55D39A2192992A274FC1A836BA3C23A3FEEBBD"
@@ -462,6 +1084,9 @@ sha512_update(doc) ->
sha512_update(suite) ->
[];
sha512_update(Config) when is_list(Config) ->
+ if_098(fun() -> sha512_update_do() end).
+
+sha512_update_do() ->
?line Ctx = crypto:sha512_init(),
?line Ctx1 = crypto:sha512_update(Ctx, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmn"),
?line Ctx2 = crypto:sha512_update(Ctx1, "hijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"),
@@ -662,6 +1287,12 @@ des3_cfb(doc) ->
des3_cfb(suite) ->
[];
des3_cfb(Config) when is_list(Config) ->
+ case openssl_version() of
+ V when V < 16#90705F -> {skipped,"OpenSSL version too old"};
+ _ -> des3_cfb_do()
+ end.
+
+des3_cfb_do() ->
?line Key1 = hexstr2bin("0123456789abcdef"),
?line Key2 = hexstr2bin("fedcba9876543210"),
?line Key3 = hexstr2bin("0f2d4b6987a5c3e1"),
@@ -717,10 +1348,19 @@ aes_cfb(Config) when is_list(Config) ->
?line Key = hexstr2bin("2b7e151628aed2a6abf7158809cf4f3c"),
?line IVec = hexstr2bin("000102030405060708090a0b0c0d0e0f"),
?line Plain = hexstr2bin("6bc1bee22e409f96e93d7e117393172a"),
- ?line Cipher = crypto:aes_cfb_128_encrypt(Key, IVec, Plain),
- ?line m(Cipher, hexstr2bin("3b3fd92eb72dad20333449f8e83cfb4a")),
- ?line m(Plain,
- crypto:aes_cfb_128_decrypt(Key, IVec, Cipher)).
+ ?line Cipher = hexstr2bin("3b3fd92eb72dad20333449f8e83cfb4a"),
+
+ %% Try all prefixes of plain and cipher.
+ aes_cfb_do(byte_size(Plain), Plain, Cipher, Key, IVec).
+
+aes_cfb_do(N, Plain, Cipher, Key, IVec) when N >= 0 ->
+ <<P:N/binary, _/binary>> = Plain,
+ <<C:N/binary, _/binary>> = Cipher,
+ ?line C = crypto:aes_cfb_128_encrypt(Key, IVec, P),
+ ?line P = crypto:aes_cfb_128_decrypt(Key, IVec, C),
+ aes_cfb_do(N-1, Plain, Cipher, Key, IVec);
+aes_cfb_do(_, _, _, _, _) -> ok.
+
%%
%%
@@ -1207,6 +1847,33 @@ rsa_sign_test(Config) when is_list(Config) ->
ok.
+rsa_sign_hash_test(doc) ->
+ "rsa_sign_hash testing";
+rsa_sign_hash_test(suite) ->
+ [];
+rsa_sign_hash_test(Config) when is_list(Config) ->
+ PubEx = 65537,
+ PrivEx = 7531712708607620783801185371644749935066152052780368689827275932079815492940396744378735701395659435842364793962992309884847527234216715366607660219930945,
+ Mod = 7919488123861148172698919999061127847747888703039837999377650217570191053151807772962118671509138346758471459464133273114654252861270845708312601272799123,
+ Msg = <<"7896345786348756234 Hejsan Svejsan, erlang crypto debugger"
+ "09812312908312378623487263487623412039812 huagasd">>,
+
+ PrivKey = [crypto:mpint(PubEx), crypto:mpint(Mod), crypto:mpint(PrivEx)],
+ PubKey = [crypto:mpint(PubEx), crypto:mpint(Mod)],
+ MD5 = crypto:md5(sized_binary(Msg)),
+ SHA = crypto:sha(sized_binary(Msg)),
+ ?line Sig1 = crypto:rsa_sign(sha, {digest,SHA}, PrivKey),
+ ?line m(crypto:rsa_verify(sha, {digest,SHA}, sized_binary(Sig1),PubKey), true),
+
+ ?line Sig2 = crypto:rsa_sign(md5, {digest,MD5}, PrivKey),
+ ?line m(crypto:rsa_verify(md5, {digest,MD5}, sized_binary(Sig2),PubKey), true),
+
+ ?line m(Sig1 =:= Sig2, false),
+ ?line m(crypto:rsa_verify(md5, {digest,MD5}, sized_binary(Sig1),PubKey), false),
+ ?line m(crypto:rsa_verify(sha, {digest,SHA}, sized_binary(Sig2),PubKey), false),
+
+ ok.
+
dsa_sign_test(doc) ->
"dsa_sign testing";
dsa_sign_test(suite) ->
@@ -1237,6 +1904,37 @@ dsa_sign_test(Config) when is_list(Config) ->
ok.
+dsa_sign_hash_test(doc) ->
+ "dsa_sign_hash testing";
+dsa_sign_hash_test(suite) ->
+ [];
+dsa_sign_hash_test(Config) when is_list(Config) ->
+ Msg = <<"7896345786348756234 Hejsan Svejsan, erlang crypto debugger"
+ "09812312908312378623487263487623412039812 huagasd">>,
+ SHA = crypto:sha(sized_binary(Msg)),
+
+ PubKey = _Y = 25854665488880835237281628794585130313500176551981812527054397586638455298000483144002221850980183404910190346416063318160497344811383498859129095184158800144312512447497510551471331451396405348497845813002058423110442376886564659959543650802132345311573634832461635601376738282831340827591903548964194832978,
+ PrivKey = _X = 441502407453038284293378221372000880210588566361,
+ ParamP = 109799869232806890760655301608454668257695818999841877165019612946154359052535682480084145133201304812979481136659521529774182959764860329095546511521488413513097576425638476458000255392402120367876345280670101492199681798674053929238558140260669578407351853803102625390950534052428162468100618240968893110797,
+ ParamQ = 1349199015905534965792122312016505075413456283393,
+ ParamG = 18320614775012672475365915366944922415598782131828709277168615511695849821411624805195787607930033958243224786899641459701930253094446221381818858674389863050420226114787005820357372837321561754462061849169568607689530279303056075793886577588606958623645901271866346406773590024901668622321064384483571751669,
+
+ Params = [crypto:mpint(ParamP), crypto:mpint(ParamQ), crypto:mpint(ParamG)],
+ ?line Sig1 = crypto:dss_sign(sha, {digest,SHA}, Params ++ [crypto:mpint(PrivKey)]),
+
+ ?line m(crypto:dss_verify(none, SHA, sized_binary(Sig1),
+ Params ++ [crypto:mpint(PubKey)]), true),
+
+ ?line m(crypto:dss_verify(sized_binary(one_bit_wrong(Msg)), sized_binary(Sig1),
+ Params ++ [crypto:mpint(PubKey)]), false),
+
+ ?line m(crypto:dss_verify(sized_binary(Msg), sized_binary(one_bit_wrong(Sig1)),
+ Params ++ [crypto:mpint(PubKey)]), false),
+
+ %%?line Bad = crypto:dss_sign(sized_binary(Msg), [Params, crypto:mpint(PubKey)]),
+
+ ok.
+
rsa_encrypt_decrypt(doc) ->
["Test rsa_public_encrypt and rsa_private_decrypt functions."];
@@ -1421,7 +2119,9 @@ 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, strong_rand_test,
rsa_verify_test, exor_test, rc4_test, rc4_stream_test, mod_exp_test,
- hmac_update_md5, hmac_update_sha, aes_ctr_stream },
+ hmac_update_md5, hmac_update_sha, hmac_update_sha256, hmac_update_sha512,
+ hmac_rfc2202, hmac_rfc4231,
+ aes_ctr_stream },
F = element(random:uniform(size(Funcs)),Funcs),
%%io:format("worker ~p calling ~p\n",[self(),F]),
@@ -1548,3 +2248,18 @@ my_dss_sign(Data,Key) ->
?line S3 = crypto:dss_sign(none, crypto:sha(Raw), Key),
[S1,S2,S3].
+openssl_version() ->
+ case crypto:info_lib() of
+ [{<<"OpenSSL">>,LibVer,_}] when is_integer(LibVer) ->
+ LibVer;
+ _ ->
+ undefined
+ end.
+
+if_098(Fun) ->
+ case openssl_version() of
+ V when V < 16#908000 ->
+ {skipped,"OpenSSL version too old"};
+ _ ->
+ Fun()
+ end.
diff --git a/lib/crypto/vsn.mk b/lib/crypto/vsn.mk
index 7e82e47d38..ccfb1fd66e 100644
--- a/lib/crypto/vsn.mk
+++ b/lib/crypto/vsn.mk
@@ -1 +1 @@
-CRYPTO_VSN = 2.1
+CRYPTO_VSN = 2.2
diff --git a/lib/debugger/doc/src/Makefile b/lib/debugger/doc/src/Makefile
index bc11ed7f46..ba62e41f6b 100644
--- a/lib/debugger/doc/src/Makefile
+++ b/lib/debugger/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2009. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/debugger/doc/src/notes.xml b/lib/debugger/doc/src/notes.xml
index 593bf715bf..66638a59f6 100644
--- a/lib/debugger/doc/src/notes.xml
+++ b/lib/debugger/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2004</year><year>2011</year>
+ <year>2004</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
diff --git a/lib/debugger/priv/Makefile b/lib/debugger/priv/Makefile
index 2f3002331b..cc19aceaf6 100644
--- a/lib/debugger/priv/Makefile
+++ b/lib/debugger/priv/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2009. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/debugger/src/Makefile b/lib/debugger/src/Makefile
index 1f9d6f2058..cadde8cd1b 100644
--- a/lib/debugger/src/Makefile
+++ b/lib/debugger/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -112,10 +112,10 @@ clean:
rm -f errs core *~
$(APP_TARGET): $(APP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
docs:
diff --git a/lib/debugger/src/dbg_debugged.erl b/lib/debugger/src/dbg_debugged.erl
index 4d9ffc4f3b..c21ad486e8 100644
--- a/lib/debugger/src/dbg_debugged.erl
+++ b/lib/debugger/src/dbg_debugged.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -104,7 +104,8 @@ handle_command(Command) ->
reply({apply,M,F,As}) ->
{value, erlang:apply(M,F,As)};
reply({eval,Expr,Bs}) ->
- erl_eval:expr(Expr, Bs). % {value, Value, Bs2}
+ %% Bindings is an orddict (sort them)
+ erl_eval:expr(Expr, lists:sort(Bs)). % {value, Value, Bs2}
%% Demonitor and delete message from inbox
%%
diff --git a/lib/debugger/src/dbg_ieval.erl b/lib/debugger/src/dbg_ieval.erl
index 2e88c35741..3c084c53ac 100644
--- a/lib/debugger/src/dbg_ieval.erl
+++ b/lib/debugger/src/dbg_ieval.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -249,7 +249,7 @@ meta_loop(Debugged, Bs, #ieval{level=Le} = Ieval) ->
{sys, Debugged, {value,Val}} ->
{value, Val, Bs};
{sys, Debugged, {value,Val,Bs2}} ->
- {value, Val, Bs2};
+ {value, Val, merge_bindings(Bs2, Bs, Ieval)};
{sys, Debugged, {exception,{Class,Reason,Stk}}} ->
case get(exit_info) of
@@ -1248,7 +1248,7 @@ if_clauses([], Bs, Ieval) ->
exception(error, if_clause, Bs, Ieval).
%% case_clauses(Value, Clauses, Bindings, Error, Ieval)
-%% Error = try_clause � case_clause
+%% Error = try_clause | case_clause
case_clauses(Val, [{clause,_,[P],G,B}|Cs], Bs0, Error, Ieval) ->
case match(P, Val, Bs0) of
{match,Bs} ->
diff --git a/lib/debugger/src/dbg_ui_trace.erl b/lib/debugger/src/dbg_ui_trace.erl
index d318987f60..8017069c50 100644
--- a/lib/debugger/src/dbg_ui_trace.erl
+++ b/lib/debugger/src/dbg_ui_trace.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -31,10 +31,10 @@
pid, % pid() Debugged process
meta, % pid() Meta process
- status, % {Status,Mod,Line} � {exit,Where,Reason}
- % Status = init � idle | break
- % | wait_break � wait_running
- % � running
+ status, % {Status,Mod,Line} | {exit,Where,Reason}
+ % Status = init | idle | break
+ % | wait_break | wait_running
+ % | running
% Where={Mod,Line} | null
cm, % atom() | undefined Current module
@@ -718,7 +718,7 @@ menus() ->
%% enable(Status) -> [MenuItem]
%% Status = init % when first message from Meta has arrived
-%% | idle | break | exit | wait_break � wait_running | running
+%% | idle | break | exit | wait_break | wait_running | running
enable(init) -> [];
enable(idle) -> ['Stop','Kill'];
enable(break) -> ['Step','Next','Continue','Finish','Skip',
diff --git a/lib/debugger/src/dbg_ui_trace_win.erl b/lib/debugger/src/dbg_ui_trace_win.erl
index 1b439cbf18..beb3fbd71e 100644
--- a/lib/debugger/src/dbg_ui_trace_win.erl
+++ b/lib/debugger/src/dbg_ui_trace_win.erl
@@ -418,8 +418,8 @@ clear_breaks(WinInfo, Mod) ->
%%--------------------------------------------------------------------
%% display(Arg)
%% Arg = idle | {Status,Mod,Line} | {running,Mod}
-%% � {exit,Where,Reason} | {text,Text}
-%% Status = break | wait � Level
+%% | {exit,Where,Reason} | {text,Text}
+%% Status = break | wait | Level
%% Level = int()
%% Mod = atom()
%% Line = integer()
diff --git a/lib/debugger/src/dbg_wx_code.erl b/lib/debugger/src/dbg_wx_code.erl
index 99826d9bdb..9853a5dbae 100644
--- a/lib/debugger/src/dbg_wx_code.erl
+++ b/lib/debugger/src/dbg_wx_code.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -30,6 +30,21 @@
-define(stc, wxStyledTextCtrl).
+%% For wx-2.9 usage
+-ifndef(wxSTC_ERLANG_COMMENT_FUNCTION).
+-define(wxSTC_ERLANG_COMMENT_FUNCTION, 14).
+-define(wxSTC_ERLANG_COMMENT_MODULE, 15).
+-define(wxSTC_ERLANG_COMMENT_DOC, 16).
+-define(wxSTC_ERLANG_COMMENT_DOC_MACRO, 17).
+-define(wxSTC_ERLANG_ATOM_QUOTED, 18).
+-define(wxSTC_ERLANG_MACRO_QUOTED, 19).
+-define(wxSTC_ERLANG_RECORD_QUOTED, 20).
+-define(wxSTC_ERLANG_NODE_NAME_QUOTED, 21).
+-define(wxSTC_ERLANG_BIFS, 22).
+-define(wxSTC_ERLANG_MODULES, 23).
+-define(wxSTC_ERLANG_MODULES_ATT, 24).
+-endif.
+
code_area(Parent) ->
FixedFont = wxFont:new(10, ?wxFONTFAMILY_TELETYPE, ?wxNORMAL, ?wxNORMAL,[]),
%%Ed = wxStyledTextCtrl:new(Parent, [{size, {700, 500}}]),
@@ -58,7 +73,21 @@ code_area(Parent) ->
{?wxSTC_ERLANG_MACRO, {40,144,170}},
{?wxSTC_ERLANG_RECORD, {40,100,20}},
{?wxSTC_ERLANG_SEPARATOR,{0,0,0}},
- {?wxSTC_ERLANG_NODE_NAME,{0,0,0}}],
+ {?wxSTC_ERLANG_NODE_NAME,{0,0,0}},
+ %% Optional 2.9 stuff
+ {?wxSTC_ERLANG_COMMENT_FUNCTION, {160,53,35}},
+ {?wxSTC_ERLANG_COMMENT_MODULE, {160,53,35}},
+ {?wxSTC_ERLANG_COMMENT_DOC, {160,53,35}},
+ {?wxSTC_ERLANG_COMMENT_DOC_MACRO, {160,53,35}},
+ {?wxSTC_ERLANG_ATOM_QUOTED, {0,0,0}},
+ {?wxSTC_ERLANG_MACRO_QUOTED, {40,144,170}},
+ {?wxSTC_ERLANG_RECORD_QUOTED, {40,100,20}},
+ {?wxSTC_ERLANG_NODE_NAME_QUOTED, {0,0,0}},
+ {?wxSTC_ERLANG_BIFS, {130,40,172}},
+ {?wxSTC_ERLANG_MODULES, {64,102,244}},
+ {?wxSTC_ERLANG_MODULES_ATT, {64,102,244}}
+ ],
+
SetStyle = fun({Style, Color}) ->
?stc:styleSetFont(Ed, Style, FixedFont),
?stc:styleSetForeground(Ed, Style, Color)
diff --git a/lib/debugger/src/dbg_wx_filedialog_win.erl b/lib/debugger/src/dbg_wx_filedialog_win.erl
index f109652a70..c8ecb7b5d4 100644
--- a/lib/debugger/src/dbg_wx_filedialog_win.erl
+++ b/lib/debugger/src/dbg_wx_filedialog_win.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -381,7 +381,6 @@ show_completion(Wanted, State = #state{text=TC, win=Win, list=LC, completion=Com
Last = wxTextCtrl:getLastPosition(TC),
wxTextCtrl:setSelection(TC, Start, Last),
destroy_completion(Comp),
- wxWindow:setFocus(TC),
State#state{ptext=Path, completion=undefined};
Paths when Comp =:= undefined ->
{PosX,PosY} = wxListCtrl:getPosition(LC),
@@ -406,14 +405,16 @@ show_completion(Wanted, State = #state{text=TC, win=Win, list=LC, completion=Com
%% wxListBox:connect(LB, command_listbox_doubleclicked),
wxListBox:connect(LB, command_listbox_selected),
wxWindow:show(Temp),
+ %% setFocus does a select all on 2.9 sigh..
+ {Start, Last} = wxTextCtrl:getSelection(TC),
wxWindow:setFocus(TC),
+ wxTextCtrl:setSelection(TC, Start, Last),
State#state{completion = {Temp, LB}, ptext=Wanted};
Paths ->
{_Temp, LB} = Comp,
wxListBox:clear(LB),
Files = [filename:basename(File) || File <- Paths],
- wxListBox:insertItems(LB,Files,0),
- wxWindow:setFocus(TC),
+ Files /= [] andalso wxListBox:insertItems(LB,Files,0),
State#state{ptext=Wanted}
end.
diff --git a/lib/debugger/src/dbg_wx_settings.erl b/lib/debugger/src/dbg_wx_settings.erl
index 3be93c495c..94ecc5781d 100644
--- a/lib/debugger/src/dbg_wx_settings.erl
+++ b/lib/debugger/src/dbg_wx_settings.erl
@@ -49,31 +49,35 @@ save(Win, Pos, SFile) ->
open_win(Win, Pos, SFile, Str, What) ->
{SDir, SFileName} =
- if
- %% If settings are saved for the first time, and to
- %% the default directory HOME/erlang.tools/debugger,
- %% make sure the directory exists, or create it if
- %% desired and possible
- SFile==default -> {default_settings_dir(Win), "NoName.state"};
- true -> {filename:dirname(SFile), filename:basename(SFile)}
- end,
-
+ if
+ %% If settings are saved for the first time, and to
+ %% the default directory HOME/erlang.tools/debugger,
+ %% make sure the directory exists, or create it if
+ %% desired and possible
+ SFile==default -> {default_settings_dir(Win), "NoName.state"};
+ true -> {filename:dirname(SFile), filename:basename(SFile)}
+ end,
+
FD = wxFileDialog:new(Win, [{message,Str},{pos, Pos},
- {defaultDir,SDir},
- {defaultFile,SFileName},
- {wildCard, "*.state"},
- {style,What}]),
+ {defaultDir,SDir},
+ {defaultFile,SFileName},
+ {wildCard, "*.state"},
+ {style,What}]),
case wxFileDialog:showModal(FD) of
- ?wxID_OK ->
- File = wxFileDialog:getFilename(FD),
- Dir = wxFileDialog:getDirectory(FD),
- wxFileDialog:destroy(FD),
- {ok, filename:join(Dir,File)};
- _ ->
- wxFileDialog:destroy(FD),
- cancel
+ ?wxID_OK ->
+ case wxFileDialog:getPaths(FD) of
+ [NewFile] ->
+ wxFileDialog:destroy(FD),
+ {ok, NewFile};
+ _ ->
+ wxFileDialog:destroy(FD),
+ cancel
+ end;
+ _ ->
+ wxFileDialog:destroy(FD),
+ cancel
end.
-
+
default_settings_dir(Win) ->
{ok, [[Home]]} = init:get_argument(home),
DefDir = filename:join([Home, ".erlang_tools", "debugger"]),
diff --git a/lib/debugger/src/dbg_wx_trace.erl b/lib/debugger/src/dbg_wx_trace.erl
index 2fdf39ba5a..0a3cac905f 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-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -32,10 +32,10 @@
pid, % pid() Debugged process
meta, % pid() Meta process
- status, % {Status,Mod,Line} � {exit,Where,Reason}
- % Status = init � idle | break
- % | wait_break � wait_running
- % � running
+ status, % {Status,Mod,Line} | {exit,Where,Reason}
+ % Status = init | idle | break
+ % | wait_break | wait_running
+ % | running
% Where={Mod,Line} | null
cm, % atom() | undefined Current module
@@ -513,7 +513,7 @@ gui_cmd({edit, {Var, Val}}, State) ->
cancel ->
State;
{Var, Term} ->
- Cmd = atom_to_list(Var)++"="++io_lib:format("~p", [Term]),
+ Cmd = atom_to_list(Var)++"="++io_lib:format("~w", [Term]),
gui_cmd({user_command, lists:flatten(Cmd)}, State)
end.
@@ -740,7 +740,7 @@ menus() ->
%% enable(Status) -> [MenuItem]
%% Status = init % when first message from Meta has arrived
-%% | idle | break | exit | wait_break � wait_running | running
+%% | idle | break | exit | wait_break | wait_running | running
enable(init) -> [];
enable(idle) -> ['Stop','Kill'];
enable(break) -> ['Step','Next','Continue','Finish','Skip',
diff --git a/lib/debugger/src/dbg_wx_trace_win.erl b/lib/debugger/src/dbg_wx_trace_win.erl
index 68e8e0b844..8b206ccd78 100644
--- a/lib/debugger/src/dbg_wx_trace_win.erl
+++ b/lib/debugger/src/dbg_wx_trace_win.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -19,7 +19,6 @@
%%
-module(dbg_wx_trace_win).
--compile([{nowarn_deprecated_function,{gs,config,2}}]).
%% External exports
-export([init/0, stop/1]).
@@ -431,8 +430,8 @@ clear_breaks(WinInfo, Mod) ->
%%--------------------------------------------------------------------
%% display(Arg)
%% Arg = idle | {Status,Mod,Line} | {running,Mod}
-%% � {exit,Where,Reason} | {text,Text}
-%% Status = break | wait � Level
+%% | {exit,Where,Reason} | {text,Text}
+%% Status = break | wait | Level
%% Level = int()
%% Mod = atom()
%% Line = integer()
@@ -481,13 +480,9 @@ display(#winInfo{window=Win, sb=Sb},Arg) ->
%% Contents = string()
%% Note: remove_code/2 should not be used for currently shown module.
%%--------------------------------------------------------------------
-is_shown(WinInfo, Mod) ->
- case lists:keyfind(Mod, 1, WinInfo#winInfo.editors) of
- {Mod, Editor} ->
- gs:config(Editor, raise), %% BUGBUG
- {true, WinInfo#winInfo{editor={Mod, Editor}}};
- false -> false
- end.
+is_shown(_WinInfo, _Mod) ->
+ %% Previously cached modules here, nyi so return false
+ false.
show_code(WinInfo = #winInfo{editor={_, Ed}}, Mod, Contents) ->
%% Insert code and update breakpoints, if any
@@ -572,7 +567,7 @@ update_bindings(#winInfo{bind=#sub{out=BA}}, Bs) ->
wx:foldl(fun({Var,Val},Row) ->
wxListCtrl:insertItem(BA, Row, ""),
wxListCtrl:setItem(BA, Row, 0, dbg_wx_win:to_string(Var)),
- wxListCtrl:setItem(BA, Row, 1, dbg_wx_win:to_string("~500P",[Val, 80])),
+ wxListCtrl:setItem(BA, Row, 1, dbg_wx_win:to_string("~99999tP",[Val, 20])),
Row+1
end, 0, Bs),
put(bindings,Bs),
diff --git a/lib/debugger/src/dbg_wx_win.erl b/lib/debugger/src/dbg_wx_win.erl
index faf3cc178f..3cb6edd953 100644
--- a/lib/debugger/src/dbg_wx_win.erl
+++ b/lib/debugger/src/dbg_wx_win.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -252,9 +252,9 @@ notify(Win,Message) ->
%%--------------------------------------------------------------------
entry(Parent, Title, Prompt, {Type, Value}) ->
- Ted = wxTextEntryDialog:new(Parent, to_string(Prompt),
- [{caption, to_string(Title)},
- {value, to_string(Value)}]),
+ Ted = wxTextEntryDialog:new(Parent, to_string(Prompt),
+ [{caption, to_string(Title)},
+ {value, to_string("~999999tp",Value)}]),
case wxDialog:showModal(Ted) of
?wxID_OK ->
@@ -306,7 +306,7 @@ to_string([]) -> "";
to_string(List) when is_list(List) ->
List;
to_string(Term) ->
- io_lib:format("~p",[Term]).
+ io_lib:format("~tp",[Term]).
to_string(Format,Args) ->
io_lib:format(Format, Args).
diff --git a/lib/debugger/src/int.erl b/lib/debugger/src/int.erl
index b3a8a07f03..1c9f2eddd1 100644
--- a/lib/debugger/src/int.erl
+++ b/lib/debugger/src/int.erl
@@ -626,18 +626,18 @@ find_src(Beam) ->
find_beam(Mod, Src) ->
SrcDir = filename:dirname(Src),
- BeamFile = packages:last(Mod) ++ code:objfile_extension(),
+ BeamFile = atom_to_list(Mod) ++ code:objfile_extension(),
File = filename:join(SrcDir, BeamFile),
case is_file(File) of
true -> File;
- false -> find_beam_1(Mod, SrcDir)
+ false -> find_beam_1(Mod, BeamFile, SrcDir)
end.
-find_beam_1(Mod, SrcDir) ->
- RootDir = find_root_dir(SrcDir, packages:first(Mod)),
+find_beam_1(Mod, BeamFile, SrcDir) ->
+ RootDir = filename:dirname(SrcDir),
EbinDir = filename:join(RootDir, "ebin"),
CodePath = [EbinDir | code:get_path()],
- BeamFile = to_path(Mod) ++ code:objfile_extension(),
+ BeamFile = atom_to_list(Mod) ++ code:objfile_extension(),
lists:foldl(fun(_, Beam) when is_list(Beam) -> Beam;
(Dir, error) ->
File = filename:join(Dir, BeamFile),
@@ -649,14 +649,6 @@ find_beam_1(Mod, SrcDir) ->
error,
CodePath).
-to_path(X) ->
- filename:join(packages:split(X)).
-
-find_root_dir(Dir, [_|Ss]) ->
- find_root_dir(filename:dirname(Dir), Ss);
-find_root_dir(Dir, []) ->
- filename:dirname(Dir).
-
check_beam(BeamBin) when is_binary(BeamBin) ->
case beam_lib:chunks(BeamBin, [abstract_code,exports]) of
{ok,{_Mod,[{abstract_code,no_abstract_code}|_]}} ->
@@ -711,14 +703,11 @@ scan_module_name_2(_, _) ->
scan_module_name_3(Ts) ->
case erl_parse:parse_form(Ts) of
- {ok, {attribute,_,module,{M,_}}} -> module_atom(M);
- {ok, {attribute,_,module,M}} -> module_atom(M);
+ {ok, {attribute,_,module,{M,_}}} -> M;
+ {ok, {attribute,_,module,M}} -> M;
_ -> error
end.
-module_atom(A) when is_atom(A) -> A;
-module_atom(L) when is_list(L) -> list_to_atom(packages:concat(L)).
-
%%--Stop interpreting modules-----------------------------------------
del_mod(AbsMod, Dist) ->
diff --git a/lib/debugger/test/Makefile b/lib/debugger/test/Makefile
index bf948ce8b0..5aa290c61b 100644
--- a/lib/debugger/test/Makefile
+++ b/lib/debugger/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1998-2011. All Rights Reserved.
+# Copyright Ericsson AB 1998-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/debugger/test/erl_eval_SUITE.erl b/lib/debugger/test/erl_eval_SUITE.erl
index a92251e1af..c675f704b4 100644
--- a/lib/debugger/test/erl_eval_SUITE.erl
+++ b/lib/debugger/test/erl_eval_SUITE.erl
@@ -216,13 +216,13 @@ guard_4(doc) ->
guard_4(suite) ->
[];
guard_4(Config) when is_list(Config) ->
- ?line check(fun() -> if {erlang,'+'}(3,a) -> true ; true -> false end end,
- "if {erlang,'+'}(3,a) -> true ; true -> false end.",
- false),
- ?line check(fun() -> if {erlang,is_integer}(3) -> true ; true -> false end
- end,
- "if {erlang,is_integer}(3) -> true ; true -> false end.",
- true),
+ check(fun() -> if erlang:'+'(3,a) -> true ; true -> false end end,
+ "if erlang:'+'(3,a) -> true ; true -> false end.",
+ false),
+ check(fun() -> if erlang:is_integer(3) -> true ; true -> false end
+ end,
+ "if erlang:is_integer(3) -> true ; true -> false end.",
+ true),
?line check(fun() -> [X || X <- [1,2,3], erlang:is_integer(X)] end,
"[X || X <- [1,2,3], erlang:is_integer(X)].",
[1,2,3]),
@@ -230,11 +230,11 @@ guard_4(Config) when is_list(Config) ->
end,
"if is_atom(is_integer(a)) -> true ; true -> false end.",
true),
- ?line check(fun() -> if {erlang,is_atom}({erlang,is_integer}(a)) -> true;
- true -> false end end,
- "if {erlang,is_atom}({erlang,is_integer}(a)) -> true; "
- "true -> false end.",
- true),
+ check(fun() -> if erlang:is_atom(erlang:is_integer(a)) -> true;
+ true -> false end end,
+ "if erlang:is_atom(erlang:is_integer(a)) -> true; "
+ "true -> false end.",
+ true),
?line check(fun() -> if is_atom(3+a) -> true ; true -> false end end,
"if is_atom(3+a) -> true ; true -> false end.",
false),
@@ -1060,11 +1060,6 @@ do_funs(LFH, EFH) ->
concat(["begin F1 = fun(F,N) -> apply(", M,
",count_down,[F, N]) end, F1(F1,1000) end."]),
0, ['F1'], LFH, EFH),
- ?line check(fun() -> F1 = fun(F,N) -> {?MODULE,count_down}(F,N)
- end, F1(F1, 1000) end,
- concat(["begin F1 = fun(F,N) -> {", M,
- ",count_down}(F, N) end, F1(F1,1000) end."]),
- 0, ['F1'], LFH, EFH),
?line check(fun() -> F = fun(F,N) when N > 0 -> apply(F,[F,N-1]);
(_F,0) -> ok end,
F(F, 1000)
@@ -1096,11 +1091,6 @@ do_funs(LFH, EFH) ->
true = {2,3} == F(2) end,
"begin F = fun(X) -> A = 1+X, {X,A} end,
true = {2,3} == F(2) end.", true, ['F'], LFH, EFH),
- ?line check(fun() -> F = fun(X) -> {erlang,'+'}(X,2) end,
- true = 3 == F(1) end,
- "begin F = fun(X) -> {erlang,'+'}(X,2) end,"
- " true = 3 == F(1) end.", true, ['F'],
- LFH, EFH),
?line check(fun() -> F = fun(X) -> byte_size(X) end,
?MODULE:do_apply(F,<<"hej">>) end,
concat(["begin F = fun(X) -> size(X) end,",
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
index c9ac6931e2..0d7883f067 100644
--- 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
@@ -76,12 +76,6 @@ apply_test(Fun) ->
[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}.
diff --git a/lib/dialyzer/doc/src/dialyzer.xml b/lib/dialyzer/doc/src/dialyzer.xml
index 4080dfdf77..5e0c9b51e3 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>2011</year>
+ <year>2006</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -288,7 +288,11 @@ Option :: {files, [Filename :: string()]}
| {include_dirs, [DirName :: string()]}
| {output_file, FileName :: string()}
| {output_plt, FileName :: string()}
- | {analysis_type, 'succ_typings' | 'plt_add' | 'plt_build' | 'plt_check' | 'plt_remove'}
+ | {analysis_type, 'succ_typings' |
+ 'plt_add' |
+ 'plt_build' |
+ 'plt_check' |
+ 'plt_remove'}
| {warnings, [WarnOpts]}
| {get_warnings, bool()}
diff --git a/lib/dialyzer/doc/src/dialyzer_chapter.xml b/lib/dialyzer/doc/src/dialyzer_chapter.xml
index d15069991e..be75f1cc65 100644
--- a/lib/dialyzer/doc/src/dialyzer_chapter.xml
+++ b/lib/dialyzer/doc/src/dialyzer_chapter.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2006</year><year>2009</year>
+ <year>2006</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -140,7 +140,9 @@
<code type="none">
- dialyzer --build_plt -r $ERL_TOP/lib/stdlib/ebin $ERL_TOP/lib/kernel/ebin $ERL_TOP/lib/mnesia/ebin
+ dialyzer --build_plt -r $ERL_TOP/lib/stdlib/ebin\
+ $ERL_TOP/lib/kernel/ebin \
+ $ERL_TOP/lib/mnesia/ebin
</code>
<p>Dialyzer will look if there is an environment variable called
diff --git a/lib/dialyzer/doc/src/notes.xml b/lib/dialyzer/doc/src/notes.xml
index 934cdba437..f485e4d03b 100644
--- a/lib/dialyzer/doc/src/notes.xml
+++ b/lib/dialyzer/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2006</year><year>2011</year>
+ <year>2006</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -31,6 +31,82 @@
<p>This document describes the changes made to the Dialyzer
application.</p>
+<section><title>Dialyzer 2.5.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>Fix a crash in race condition detection</p> <p>Remove
+ old untested experimental extension</p> <p>Respect
+ {plt_check,false} option when using dialyzer:run/1</p>
+ <p>Fix handling of tuple set remote types appearing in
+ tuple sets</p>
+ <p>
+ Own Id: OTP-10464</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Dialyzer 2.5.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Correct handling of type names in contracts. Fix
+ crash related to contract checking. Do not rewrite
+ unchanged PLT. </p>
+ <p>
+ Own Id: OTP-10083</p>
+ </item>
+ <item>
+ <p>
+ Stop a forgotten server process</p>
+ <p>
+ Dialyzer forgot to stop a server process before finishing
+ its analysis. This is a concurrency error detected by
+ Concuerror. Changes to fix warnings identified by running
+ dialyzer -Wunmatched_returns. Thanks to Kostis Sagonas.</p>
+ <p>
+ Own Id: OTP-10231</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p> Bug fixes and improvements of
+ <c>dialyzer_typesig</c>. </p>
+ <p>
+ Own Id: OTP-10082</p>
+ </item>
+ <item>
+ <p>
+ Add parallel dialyzer support</p>
+ <p>
+ Own Id: OTP-10103</p>
+ </item>
+ <item>
+ <p> An alternative implementation of the solver in
+ <c>dialyzer_typesig</c> has been introduced. It is faster
+ than the original implementation. </p>
+ <p>
+ Own Id: OTP-10110</p>
+ </item>
+ <item>
+ <p> Bugs in <c>erl_types:t_inf()</c> (HiPE) and in
+ <c>dialyzer_dataflow</c> (Dialyzer) have been fixed. </p>
+ <p>
+ Own Id: OTP-10191</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Dialyzer 2.5.1</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/dialyzer/src/Makefile b/lib/dialyzer/src/Makefile
index 63cc1c98f1..bb2edd419a 100644
--- a/lib/dialyzer/src/Makefile
+++ b/lib/dialyzer/src/Makefile
@@ -108,22 +108,22 @@ clean:
# ----------------------------------------------------
$(EBIN)/dialyzer_cl_parse.$(EMULATOR): dialyzer_cl_parse.erl ../vsn.mk
- erlc -W $(ERL_COMPILE_FLAGS) -DVSN="\"v$(VSN)\"" -o$(EBIN) dialyzer_cl_parse.erl
+ $(erlc_verbose)erlc -W $(ERL_COMPILE_FLAGS) -DVSN="\"v$(VSN)\"" -o$(EBIN) dialyzer_cl_parse.erl
$(EBIN)/dialyzer_plt.$(EMULATOR): dialyzer_plt.erl ../vsn.mk
- erlc -W $(ERL_COMPILE_FLAGS) -DVSN="\"v$(VSN)\"" -o$(EBIN) dialyzer_plt.erl
+ $(erlc_verbose)erlc -W $(ERL_COMPILE_FLAGS) -DVSN="\"v$(VSN)\"" -o$(EBIN) dialyzer_plt.erl
$(EBIN)/dialyzer_gui.$(EMULATOR): dialyzer_gui.erl ../vsn.mk
- erlc -W $(ERL_COMPILE_FLAGS) -DVSN="\"v$(VSN)\"" -o$(EBIN) dialyzer_gui.erl
+ $(erlc_verbose)erlc -W $(ERL_COMPILE_FLAGS) -DVSN="\"v$(VSN)\"" -o$(EBIN) dialyzer_gui.erl
$(EBIN)/dialyzer_gui_wx.$(EMULATOR): dialyzer_gui_wx.erl ../vsn.mk
- erlc -W $(ERL_COMPILE_FLAGS) -DVSN="\"v$(VSN)\"" -o$(EBIN) dialyzer_gui_wx.erl
+ $(erlc_verbose)erlc -W $(ERL_COMPILE_FLAGS) -DVSN="\"v$(VSN)\"" -o$(EBIN) dialyzer_gui_wx.erl
$(APP_TARGET): $(APP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
# ---------------------------------------------------------------------
# dependencies -- I wish they were somehow automatically generated
diff --git a/lib/dialyzer/src/dialyzer.erl b/lib/dialyzer/src/dialyzer.erl
index 3e3c12405f..63c51e219a 100644
--- a/lib/dialyzer/src/dialyzer.erl
+++ b/lib/dialyzer/src/dialyzer.erl
@@ -2,7 +2,7 @@
%%-----------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -162,14 +162,17 @@ run(Opts) ->
{error, Msg} ->
throw({dialyzer_error, Msg});
OptsRecord ->
- case cl_check_init(OptsRecord) of
- {ok, ?RET_NOTHING_SUSPICIOUS} ->
- case dialyzer_cl:start(OptsRecord) of
- {?RET_DISCREPANCIES, Warnings} -> Warnings;
- {?RET_NOTHING_SUSPICIOUS, []} -> []
- end;
- {error, ErrorMsg1} ->
- throw({dialyzer_error, ErrorMsg1})
+ case OptsRecord#options.check_plt of
+ true ->
+ case cl_check_init(OptsRecord) of
+ {ok, ?RET_NOTHING_SUSPICIOUS} -> ok;
+ {error, ErrorMsg1} -> throw({dialyzer_error, ErrorMsg1})
+ end;
+ false -> ok
+ end,
+ case dialyzer_cl:start(OptsRecord) of
+ {?RET_DISCREPANCIES, Warnings} -> Warnings;
+ {?RET_NOTHING_SUSPICIOUS, []} -> []
end
catch
throw:{dialyzer_error, ErrorMsg} ->
@@ -380,8 +383,6 @@ message_to_string({pattern_match_cov, [Pat, Type]}) ->
message_to_string({unmatched_return, [Type]}) ->
io_lib:format("Expression produces a value of type ~s,"
" but this value is unmatched\n", [Type]);
-message_to_string({unused_fun, []}) ->
- io_lib:format("Function will never be called\n", []);
message_to_string({unused_fun, [F, A]}) ->
io_lib:format("Function ~w/~w will never be called\n", [F, A]);
%%----- Warnings for specs and contracts -------------------
diff --git a/lib/dialyzer/src/dialyzer.hrl b/lib/dialyzer/src/dialyzer.hrl
index 1b999a7b99..105a174e31 100644
--- a/lib/dialyzer/src/dialyzer.hrl
+++ b/lib/dialyzer/src/dialyzer.hrl
@@ -2,7 +2,7 @@
%%%
%%% %CopyrightBegin%
%%%
-%%% Copyright Ericsson AB 2006-2011. All Rights Reserved.
+%%% Copyright Ericsson AB 2006-2012. All Rights Reserved.
%%%
%%% The contents of this file are subject to the Erlang Public License,
%%% Version 1.1, (the "License"); you may not use this file except in
@@ -111,6 +111,7 @@
-type rep_mode() :: 'quiet' | 'normal' | 'verbose'.
-type start_from() :: 'byte_code' | 'src_code'.
-type mfa_or_funlbl() :: label() | mfa().
+-type solver() :: 'v1' | 'v2'.
%%--------------------------------------------------------------------
%% Record declarations used by various files
@@ -129,7 +130,8 @@
behaviours_chk = false :: boolean(),
timing = false :: boolean() | 'debug',
timing_server :: dialyzer_timing:timing_server(),
- callgraph_file = "" :: file:filename()}).
+ callgraph_file = "" :: file:filename(),
+ solvers :: [solver()]}).
-record(options, {files = [] :: [file:filename()],
files_rec = [] :: [file:filename()],
@@ -149,7 +151,8 @@
output_format = formatted :: format(),
filename_opt = basename :: fopt(),
callgraph_file = "" :: file:filename(),
- check_plt = true :: boolean()}).
+ check_plt = true :: boolean(),
+ solvers = [] :: [solver()]}).
-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 3bbde12481..86618a4915 100644
--- a/lib/dialyzer/src/dialyzer_analysis_callgraph.erl
+++ b/lib/dialyzer/src/dialyzer_analysis_callgraph.erl
@@ -53,7 +53,8 @@
plt :: dialyzer_plt:plt(),
start_from = byte_code :: start_from(),
use_contracts = true :: boolean(),
- timing_server :: dialyzer_timing:timing_server()
+ timing_server :: dialyzer_timing:timing_server(),
+ solvers :: [solver()]
}).
-record(server_state, {parent :: pid(), legal_warnings :: [dial_warn_tag()]}).
@@ -136,7 +137,8 @@ analysis_start(Parent, Analysis) ->
parent = Parent,
start_from = Analysis#analysis.start_from,
use_contracts = Analysis#analysis.use_contracts,
- timing_server = Analysis#analysis.timing_server
+ timing_server = Analysis#analysis.timing_server,
+ solvers = Analysis#analysis.solvers
},
Files = ordsets:from_list(Analysis#analysis.files),
{Callgraph, NoWarn, TmpCServer0} = compile_and_store(Files, State),
@@ -182,6 +184,7 @@ analysis_start(Parent, Analysis) ->
false -> Callgraph
end,
State3 = analyze_callgraph(NewCallgraph, State2#analysis_state{plt = Plt1}),
+ dialyzer_callgraph:dispose_race_server(NewCallgraph),
rcv_and_send_ext_types(Parent),
NonExports = sets:subtract(sets:from_list(AllNodes), Exports),
NonExportsList = sets:to_list(NonExports),
@@ -192,20 +195,21 @@ analysis_start(Parent, Analysis) ->
analyze_callgraph(Callgraph, #analysis_state{codeserver = Codeserver,
doc_plt = DocPlt,
timing_server = TimingServer,
- parent = Parent} = State) ->
+ parent = Parent,
+ solvers = Solvers} = State) ->
Plt = dialyzer_plt:insert_callbacks(State#analysis_state.plt, Codeserver),
{NewPlt, NewDocPlt} =
case State#analysis_state.analysis_type of
plt_build ->
NewPlt0 =
dialyzer_succ_typings:analyze_callgraph(Callgraph, Plt, Codeserver,
- TimingServer, Parent),
+ TimingServer, Solvers, Parent),
{NewPlt0, DocPlt};
succ_typings ->
NoWarn = State#analysis_state.no_warn_unused,
{Warnings, NewPlt0, NewDocPlt0} =
dialyzer_succ_typings:get_warnings(Callgraph, Plt, DocPlt, Codeserver,
- NoWarn, TimingServer, Parent),
+ NoWarn, TimingServer, Solvers, Parent),
send_warnings(State#analysis_state.parent, Warnings),
{NewPlt0, NewDocPlt0}
end,
@@ -322,13 +326,6 @@ cleanup_callgraph(#analysis_state{plt = InitPlt, parent = Parent,
ModuleDeps = dialyzer_callgraph:module_deps(Callgraph),
send_mod_deps(Parent, ModuleDeps),
{Callgraph1, ExtCalls} = dialyzer_callgraph:remove_external(Callgraph),
- RelevantAPICalls =
- dialyzer_behaviours:get_behaviour_apis([gen_server]),
- BehaviourAPICalls = [Call || {_From, To} = Call <- ExtCalls,
- lists:member(To, RelevantAPICalls)],
- Callgraph2 =
- dialyzer_callgraph:put_behaviour_api_calls(BehaviourAPICalls,
- Callgraph1),
ExtCalls1 = [Call || Call = {_From, To} <- ExtCalls,
not dialyzer_plt:contains_mfa(InitPlt, To)],
{BadCalls1, RealExtCalls} =
@@ -351,7 +348,7 @@ cleanup_callgraph(#analysis_state{plt = InitPlt, parent = Parent,
true ->
send_ext_calls(Parent, lists:usort([To || {_From, To} <- RealExtCalls]))
end,
- Callgraph2.
+ Callgraph1.
compile_src(File, Includes, Defines, Callgraph, CServer, UseContracts) ->
DefaultIncludes = default_includes(filename:dirname(File)),
@@ -516,9 +513,10 @@ rcv_and_send_ext_types(Parent) ->
Self ! {Self, done},
case rcv_ext_types(Self, []) of
[] -> ok;
- ExtTypes -> Parent ! {Self, ext_types, ExtTypes}
- end,
- ok.
+ ExtTypes ->
+ Parent ! {Self, ext_types, ExtTypes},
+ ok
+ end.
rcv_ext_types(Self, ExtTypes) ->
receive
diff --git a/lib/dialyzer/src/dialyzer_behaviours.erl b/lib/dialyzer/src/dialyzer_behaviours.erl
index b84071b95c..36aef2a37f 100644
--- a/lib/dialyzer/src/dialyzer_behaviours.erl
+++ b/lib/dialyzer/src/dialyzer_behaviours.erl
@@ -30,11 +30,9 @@
-module(dialyzer_behaviours).
--export([check_callbacks/5, get_behaviour_apis/1,
- translate_behaviour_api_call/5, translatable_behaviours/1,
- translate_callgraph/3]).
+-export([check_callbacks/5]).
--export_type([behaviour/0, behaviour_api_dict/0]).
+-export_type([behaviour/0]).
%%--------------------------------------------------------------------
@@ -224,103 +222,3 @@ get_line([]) -> -1.
get_file([{file, File}|_]) -> File;
get_file([_|Tail]) -> get_file(Tail).
-
-%%-----------------------------------------------------------------------------
-
--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([behaviour()]) -> [mfa()].
-
-get_behaviour_apis(Behaviours) ->
- get_behaviour_apis(Behaviours, []).
-
--spec translate_behaviour_api_call(dialyzer_callgraph:mfa_or_funlbl(),
- [erl_types:erl_type()],
- [dialyzer_races:core_vars()],
- module(),
- behaviour_api_dict()) ->
- {dialyzer_callgraph:mfa_or_funlbl(),
- [erl_types:erl_type()],
- [dialyzer_races:core_vars()]}
- | 'plain_call'.
-
-translate_behaviour_api_call(_Fun, _ArgTypes, _Args, _Module, []) ->
- plain_call;
-translate_behaviour_api_call({Module, Fun, Arity}, ArgTypes, Args,
- CallbackModule, BehApiInfo) ->
- case lists:keyfind(Module, 1, BehApiInfo) of
- false -> plain_call;
- {Module, Calls} ->
- case lists:keyfind({Fun, Arity}, 1, Calls) of
- false -> plain_call;
- {{Fun, Arity}, {CFun, CArity, COrder}} ->
- {{CallbackModule, CFun, CArity},
- [nth_or_0(N, ArgTypes, erl_types:t_any()) || N <-COrder],
- [nth_or_0(N, Args, bypassed) || N <-COrder]}
- end
- end;
-translate_behaviour_api_call(_Fun, _ArgTypes, _Args, _Module, _BehApiInfo) ->
- plain_call.
-
--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 <-
- dialyzer_callgraph:get_behaviour_api_calls(Callgraph),
- M =:= Behaviour],
- Calls = [{{Behaviour, API, Arity}, Callback} ||
- {{API, Arity}, Callback} <- behaviour_api_calls(Behaviour)],
- DirectCalls = [{From, {Module, Fun, Arity}} ||
- {From, To} <- UsedCalls,{API, {Fun, Arity, _Ord}} <- Calls,
- To =:= API],
- dialyzer_callgraph:add_edges(DirectCalls, Callgraph),
- translate_callgraph(Behaviours, Module, Callgraph);
-translate_callgraph([], _Module, Callgraph) ->
- Callgraph.
-
-get_behaviour_apis([], Acc) ->
- Acc;
-get_behaviour_apis([Behaviour | Rest], Acc) ->
- MFAs = [{Behaviour, Fun, Arity} ||
- {{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]}},
- {{start_link, 4}, {init, 1, [3]}},
- {{start, 3}, {init, 1, [2]}},
- {{start, 4}, {init, 1, [3]}},
- {{call, 2}, {handle_call, 3, [2, 0, 0]}},
- {{call, 3}, {handle_call, 3, [2, 0, 0]}},
- {{multi_call, 2}, {handle_call, 3, [2, 0, 0]}},
- {{multi_call, 3}, {handle_call, 3, [3, 0, 0]}},
- {{multi_call, 4}, {handle_call, 3, [3, 0, 0]}},
- {{cast, 2}, {handle_cast, 2, [2, 0]}},
- {{abcast, 2}, {handle_cast, 2, [2, 0]}},
- {{abcast, 3}, {handle_cast, 2, [3, 0]}}];
-behaviour_api_calls(_Other) ->
- [].
diff --git a/lib/dialyzer/src/dialyzer_cl.erl b/lib/dialyzer/src/dialyzer_cl.erl
index 5d253e77fa..6732d96b98 100644
--- a/lib/dialyzer/src/dialyzer_cl.erl
+++ b/lib/dialyzer/src/dialyzer_cl.erl
@@ -2,7 +2,7 @@
%%-------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -398,7 +398,8 @@ do_analysis(Files, Options, Plt, PltInfo) ->
timing = Options#options.timing,
plt = Plt,
use_contracts = Options#options.use_contracts,
- callgraph_file = Options#options.callgraph_file},
+ callgraph_file = Options#options.callgraph_file,
+ solvers = Options#options.solvers},
State3 = start_analysis(State2, InitAnalysis),
{T1, _} = statistics(wall_clock),
Return = cl_loop(State3),
diff --git a/lib/dialyzer/src/dialyzer_cl_parse.erl b/lib/dialyzer/src/dialyzer_cl_parse.erl
index 205b97ccf9..2ea3d3af5a 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-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -198,6 +198,9 @@ cl(["--gui"|T]) ->
cl(["--wx"|T]) ->
put(dialyzer_options_mode, {gui, wx}),
cl(T);
+cl(["--solver",Solver|T]) -> % not documented
+ append_var(dialyzer_solvers, [list_to_atom(Solver)]),
+ cl(T);
cl([H|_] = L) ->
case filelib:is_file(H) orelse filelib:is_dir(H) of
true ->
@@ -258,6 +261,7 @@ init() ->
put(dialyzer_filename_opt, basename),
put(dialyzer_options_check_plt, DefaultOpts#options.check_plt),
put(dialyzer_timing, DefaultOpts#options.timing),
+ put(dialyzer_solvers, DefaultOpts#options.solvers),
ok.
append_defines([Def, Val]) ->
@@ -311,7 +315,8 @@ common_options() ->
{report_mode, get(dialyzer_options_report_mode)},
{use_spec, get(dialyzer_options_use_contracts)},
{warnings, get(dialyzer_warnings)},
- {check_plt, get(dialyzer_options_check_plt)}].
+ {check_plt, get(dialyzer_options_check_plt)},
+ {solvers, get(dialyzer_solvers)}].
%%-----------------------------------------------------------------------
diff --git a/lib/dialyzer/src/dialyzer_contracts.erl b/lib/dialyzer/src/dialyzer_contracts.erl
index 76f23d00b9..157c951f77 100644
--- a/lib/dialyzer/src/dialyzer_contracts.erl
+++ b/lib/dialyzer/src/dialyzer_contracts.erl
@@ -2,7 +2,7 @@
%%-----------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -254,14 +254,35 @@ check_extraneous([C|Cs], SuccType) ->
end.
check_extraneous_1(Contract, SuccType) ->
- CRngs = erl_types:t_elements(erl_types:t_fun_range(Contract)),
+ CRng = erl_types:t_fun_range(Contract),
+ CRngs = erl_types:t_elements(CRng),
STRng = erl_types:t_fun_range(SuccType),
?debug("CR = ~p\nSR = ~p\n", [CRngs, STRng]),
- case [CR || CR <- CRngs, erl_types:t_is_none(erl_types:t_inf(CR, STRng, opaque))] of
- [] -> ok;
+ case [CR || CR <- CRngs,
+ erl_types:t_is_none(erl_types:t_inf(CR, STRng, opaque))] of
+ [] ->
+ CRngList = list_part(CRng),
+ STRngList = list_part(STRng),
+ case is_not_nil_list(CRngList) andalso is_not_nil_list(STRngList) of
+ false -> ok;
+ true ->
+ CRngElements = erl_types:t_list_elements(CRngList),
+ STRngElements = erl_types:t_list_elements(STRngList),
+ Inf = erl_types:t_inf(CRngElements, STRngElements, opaque),
+ case erl_types:t_is_none(Inf) of
+ true -> {error, invalid_contract};
+ false -> ok
+ end
+ end;
CRs -> {error, {extra_range, erl_types:t_sup(CRs), STRng}}
end.
+list_part(Type) ->
+ erl_types:t_inf(erl_types:t_list(), Type, opaque).
+
+is_not_nil_list(Type) ->
+ erl_types:t_is_list(Type) andalso not erl_types:t_is_nil(Type).
+
%% This is the heart of the "range function"
-spec process_contracts([contract_pair()], [erl_types:erl_type()]) -> erl_types:erl_type().
diff --git a/lib/dialyzer/src/dialyzer_coordinator.erl b/lib/dialyzer/src/dialyzer_coordinator.erl
index 5719132215..c3b7ce29af 100644
--- a/lib/dialyzer/src/dialyzer_coordinator.erl
+++ b/lib/dialyzer/src/dialyzer_coordinator.erl
@@ -2,7 +2,7 @@
%%-----------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -116,7 +116,7 @@ spawn_jobs(Mode, Jobs, InitData, Timing) ->
fun(Job, Count) ->
Pid = dialyzer_worker:launch(Mode, Job, InitData, Coordinator),
case TypesigOrDataflow of
- true -> true = ets:insert(SCCtoPID, {Job, Pid});
+ true -> true = ets:insert(SCCtoPID, {Job, Pid}), ok;
false -> request_activation(Regulator, Pid)
end,
Count + 1
@@ -217,7 +217,8 @@ request_activation({_Collector, Regulator, _SCCtoPID}) ->
wait_activation().
request_activation(Regulator, Pid) ->
- Regulator ! {req, Pid}.
+ Regulator ! {req, Pid},
+ ok.
spawn_regulator() ->
InitTickets = dialyzer_utils:parallelism(),
diff --git a/lib/dialyzer/src/dialyzer_dataflow.erl b/lib/dialyzer/src/dialyzer_dataflow.erl
index cb376daf68..6956850f1a 100644
--- a/lib/dialyzer/src/dialyzer_dataflow.erl
+++ b/lib/dialyzer/src/dialyzer_dataflow.erl
@@ -91,9 +91,8 @@
warning_mode = false :: boolean(),
warnings = [] :: [dial_warning()],
work :: {[_], [_], set()},
- module :: module(),
- behaviour_api_dict = [] ::
- dialyzer_behaviours:behaviour_api_dict()}).
+ module :: module()
+ }).
-record(map, {dict = dict:new() :: dict(),
subst = dict:new() :: dict(),
@@ -135,38 +134,15 @@ get_fun_types(Tree, Plt, Callgraph, Records) ->
analyze_module(Tree, Plt, Callgraph, Records, GetWarnings) ->
debug_pp(Tree, false),
Module = cerl:atom_val(cerl:module_name(Tree)),
- RaceDetection = dialyzer_callgraph:get_race_detection(Callgraph),
- BehaviourTranslations =
- case RaceDetection of
- true -> dialyzer_behaviours:translatable_behaviours(Tree);
- false -> []
- end,
TopFun = cerl:ann_c_fun([{label, top}], [], Tree),
- State =
- state__new(Callgraph, TopFun, Plt, Module, Records, BehaviourTranslations),
+ State = state__new(Callgraph, TopFun, Plt, Module, Records),
State1 = state__race_analysis(not GetWarnings, State),
State2 = analyze_loop(State1),
case GetWarnings of
true ->
State3 = state__set_warning_mode(State2),
State4 = analyze_loop(State3),
-
- %% EXPERIMENTAL: Turn all behaviour API calls into calls to the
- %% respective callback module's functions.
-
- case BehaviourTranslations of
- [] -> dialyzer_races:race(State4);
- Behaviours ->
- Digraph = dialyzer_callgraph:get_digraph(State4#state.callgraph),
- TranslatedCallgraph =
- dialyzer_behaviours:translate_callgraph(Behaviours, Module,
- Callgraph),
- St =
- dialyzer_races:race(State4#state{callgraph = TranslatedCallgraph}),
- FinalCallgraph = dialyzer_callgraph:put_digraph(Digraph,
- St#state.callgraph),
- St#state{callgraph = FinalCallgraph}
- end;
+ dialyzer_races:race(State4);
false ->
State2
end.
@@ -530,21 +506,8 @@ handle_apply_or_call([{TypeOfApply, {Fun, Sig, Contr, LocalRet}}|Left],
Ann = cerl:get_ann(Tree),
File = get_file(Ann),
Line = abs(get_line(Ann)),
-
- %% EXPERIMENTAL: Turn a behaviour's API call into a call to the
- %% respective callback module's function.
-
- Module = State#state.module,
- BehApiDict = State#state.behaviour_api_dict,
- {RealFun, RealArgTypes, RealArgs} =
- case dialyzer_behaviours:translate_behaviour_api_call(Fun, ArgTypes,
- Args, Module,
- BehApiDict) of
- plain_call -> {Fun, ArgTypes, Args};
- BehaviourAPI -> BehaviourAPI
- end,
- dialyzer_races:store_race_call(RealFun, RealArgTypes, RealArgs,
- {File, Line}, State);
+ dialyzer_races:store_race_call(Fun, ArgTypes, Args,
+ {File, Line}, State);
false -> State
end,
FailedConj = any_none([RetWithoutLocal|NewArgTypes]),
@@ -2468,14 +2431,18 @@ join_maps(Maps, MapOut) ->
Keys = ordsets:from_list(dict:fetch_keys(Dict) ++ dict:fetch_keys(Subst)),
join_maps(Keys, Maps, MapOut).
-join_maps([Key|Left], Maps, MapOut) ->
+join_maps(Keys, Maps, MapOut) ->
+ KTs = join_maps_collect(Keys, Maps, MapOut),
+ lists:foldl(fun({K, T}, M) -> enter_type(K, T, M) end, MapOut, KTs).
+
+join_maps_collect([Key|Left], Maps, MapOut) ->
Type = join_maps_one_key(Maps, Key, t_none()),
case t_is_equal(lookup_type(Key, MapOut), Type) of
- true -> join_maps(Left, Maps, MapOut);
- false -> join_maps(Left, Maps, enter_type(Key, Type, MapOut))
+ true -> join_maps_collect(Left, Maps, MapOut);
+ false -> [{Key, Type} | join_maps_collect(Left, Maps, MapOut)]
end;
-join_maps([], _Maps, MapOut) ->
- MapOut.
+join_maps_collect([], _Maps, _MapOut) ->
+ [].
join_maps_one_key([Map|Left], Key, AccType) ->
case t_is_any(AccType) of
@@ -2707,7 +2674,7 @@ determine_mode(Type, Opaques) ->
%%%
%%% ===========================================================================
-state__new(Callgraph, Tree, Plt, Module, Records, BehaviourTranslations) ->
+state__new(Callgraph, Tree, Plt, Module, Records) ->
Opaques = erl_types:module_builtin_opaques(Module) ++
erl_types:t_opaque_from_records(Records),
TreeMap = build_tree_map(Tree),
@@ -2721,7 +2688,7 @@ state__new(Callgraph, Tree, Plt, Module, Records, BehaviourTranslations) ->
#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_dict = BehaviourTranslations}.
+ module = Module}.
state__warning_mode(#state{warning_mode = WM}) ->
WM.
@@ -2802,7 +2769,7 @@ state__get_warnings(#state{tree_map = TreeMap, fun_tab = FunTab,
true ->
{Warn, Msg} =
case dialyzer_callgraph:lookup_name(FunLbl, Callgraph) of
- error -> {true, {unused_fun, []}};
+ error -> {false, {}};
{ok, {_M, F, A} = MFA} ->
{not sets:is_element(MFA, NoWarnUnused),
{unused_fun, [F, A]}}
diff --git a/lib/dialyzer/src/dialyzer_gui_wx.erl b/lib/dialyzer/src/dialyzer_gui_wx.erl
index b26aa695bd..c6f7c56227 100644
--- a/lib/dialyzer/src/dialyzer_gui_wx.erl
+++ b/lib/dialyzer/src/dialyzer_gui_wx.erl
@@ -192,48 +192,50 @@ create_window(Wx, #options{init_plts = InitPltFiles} = DialyzerOptions) ->
RunButtons = wxBoxSizer:new(?wxHORIZONTAL),
Buttons = wxFlexGridSizer:new(3),
- wxSizer:add(ChooseButtons, DeleteButton, ?BorderOpt),
- wxSizer:add(ChooseButtons, DeleteAllButton, ?BorderOpt),
- wxSizer:add(ChooseItem, Lab1, Center),
- wxSizer:add(ChooseItem, ChosenBox, Opts),
- wxSizer:add(ChooseItem, ChooseButtons, ?BorderOpt),
- wxSizer:add(FileTypeItem, OptionsLabel),
- wxSizer:add(FileTypeItem, FileType, [{border, 5}, {flag, ?wxALL}]),
- wxSizer:add(LogItem, LogLabel, Center),
- wxSizer:add(LogItem, LogBox, Opts3),
- wxSizer:add(LogItem, ClearLogButton, ?BorderOpt),
- wxSizer:add(FileItem, FileLabel),
- wxSizer:add(FileItem, FilePicker),
- wxSizer:add(DirItem, DirLabel),
- wxSizer:add(DirItem, DirPicker),
- wxSizer:add(AddDirButtons, AddDirButton, ?BorderOpt),
- wxSizer:add(AddDirButtons, AddRecButton, ?BorderOpt),
- wxSizer:add(FileDirItem, FileItem),
- wxSizer:add(FileDirItem, AddButton, ?BorderOpt),
- wxSizer:add(FileDirItem, DirItem, ?BorderOpt),
- wxSizer:add(FileDirItem, AddDirButtons, ?BorderOpt),
- wxSizer:add(WarnButtons, ExplainWarnButton, ?BorderOpt),
- wxSizer:add(WarnButtons, ClearWarningsButton, ?BorderOpt),
- wxSizer:add(RunButtons, RunButton, ?BorderOpt),
- wxSizer:add(RunButtons, StopButton, ?BorderOpt),
- wxSizer:add(Buttons, WarnButtons),
- wxSizer:add(Buttons, wxStaticText:new(Frame, ?LABEL7, ""), [{flag, ?wxEXPAND}]),
- wxSizer:add(Buttons, RunButtons),
- wxFlexGridSizer:addGrowableCol(Buttons, 1),
- wxSizer:add(WarningsItem, WarningsLabel, Center),
- wxSizer:add(WarningsItem, WarningsBox, Opts3),
- wxSizer:add(WarningsItem, Buttons, [{flag, ?wxEXPAND bor ?wxALL},?Border]),
-
- wxSizer:add(Left, ChooseItem, Opts),
- wxSizer:add(Left, FileDirItem, [{proportion, 1}, {border, 60}, {flag, ?wxTOP}]),
- wxSizer:add(RightUp, FileTypeItem, ?BorderOpt),
- wxSizer:add(RightUp, LogItem, Opts3),
- wxSizer:add(Right, RightUp, Opts3),
- wxSizer:add(Right, WarningsItem, Opts3),
- wxSizer:add(Top, Left, Opts),
- wxSizer:add(Top, Right, Opts3),
-
- wxSizer:add(All, Top, Opts),
+ _ = wxSizer:add(ChooseButtons, DeleteButton, ?BorderOpt),
+ _ = wxSizer:add(ChooseButtons, DeleteAllButton, ?BorderOpt),
+ _ = wxSizer:add(ChooseItem, Lab1, Center),
+ _ = wxSizer:add(ChooseItem, ChosenBox, Opts),
+ _ = wxSizer:add(ChooseItem, ChooseButtons, ?BorderOpt),
+ _ = wxSizer:add(FileTypeItem, OptionsLabel),
+ _ = wxSizer:add(FileTypeItem, FileType, [{border, 5}, {flag, ?wxALL}]),
+ _ = wxSizer:add(LogItem, LogLabel, Center),
+ _ = wxSizer:add(LogItem, LogBox, Opts3),
+ _ = wxSizer:add(LogItem, ClearLogButton, ?BorderOpt),
+ _ = wxSizer:add(FileItem, FileLabel),
+ _ = wxSizer:add(FileItem, FilePicker),
+ _ = wxSizer:add(DirItem, DirLabel),
+ _ = wxSizer:add(DirItem, DirPicker),
+ _ = wxSizer:add(AddDirButtons, AddDirButton, ?BorderOpt),
+ _ = wxSizer:add(AddDirButtons, AddRecButton, ?BorderOpt),
+ _ = wxSizer:add(FileDirItem, FileItem),
+ _ = wxSizer:add(FileDirItem, AddButton, ?BorderOpt),
+ _ = wxSizer:add(FileDirItem, DirItem, ?BorderOpt),
+ _ = wxSizer:add(FileDirItem, AddDirButtons, ?BorderOpt),
+ _ = wxSizer:add(WarnButtons, ExplainWarnButton, ?BorderOpt),
+ _ = wxSizer:add(WarnButtons, ClearWarningsButton, ?BorderOpt),
+ _ = wxSizer:add(RunButtons, RunButton, ?BorderOpt),
+ _ = wxSizer:add(RunButtons, StopButton, ?BorderOpt),
+ _ = wxSizer:add(Buttons, WarnButtons),
+ _ = wxSizer:add(Buttons, wxStaticText:new(Frame, ?LABEL7, ""),
+ [{flag, ?wxEXPAND}]),
+ _ = wxSizer:add(Buttons, RunButtons),
+ _ = wxFlexGridSizer:addGrowableCol(Buttons, 1),
+ _ = wxSizer:add(WarningsItem, WarningsLabel, Center),
+ _ = wxSizer:add(WarningsItem, WarningsBox, Opts3),
+ _ = wxSizer:add(WarningsItem, Buttons,
+ [{flag, ?wxEXPAND bor ?wxALL}, ?Border]),
+ _ = wxSizer:add(Left, ChooseItem, Opts),
+ _ = wxSizer:add(Left, FileDirItem,
+ [{proportion, 1}, {border, 60}, {flag, ?wxTOP}]),
+ _ = wxSizer:add(RightUp, FileTypeItem, ?BorderOpt),
+ _ = wxSizer:add(RightUp, LogItem, Opts3),
+ _ = wxSizer:add(Right, RightUp, Opts3),
+ _ = wxSizer:add(Right, WarningsItem, Opts3),
+ _ = wxSizer:add(Top, Left, Opts),
+ _ = wxSizer:add(Top, Right, Opts3),
+
+ _ = wxSizer:add(All, Top, Opts),
wxWindow:setSizer(Frame, All),
wxWindow:setSizeHints(Frame, {1150,600}),
wxWindow:show(Frame),
@@ -294,91 +296,67 @@ create_window(Wx, #options{init_plts = InitPltFiles} = DialyzerOptions) ->
createFileMenu() ->
FileMenu = wxMenu:new(),
- wxMenu:append(FileMenu, wxMenuItem:new([{id, ?menuID_FILE_SAVE_WARNINGS},
- {text, "Save &Warnings"}])),
- wxMenu:append(FileMenu, wxMenuItem:new([{id, ?menuID_FILE_SAVE_LOG},
- {text, "Save &Log"}])),
- wxMenu:append(FileMenu, wxMenuItem:new([{id, ?menuID_FILE_QUIT},
- {text, "E&xit\tAlt-X"}])),
+ _ = wxMenu:append(FileMenu, wxMenuItem:new([{id, ?menuID_FILE_SAVE_WARNINGS},
+ {text, "Save &Warnings"}])),
+ _ = wxMenu:append(FileMenu, wxMenuItem:new([{id, ?menuID_FILE_SAVE_LOG},
+ {text, "Save &Log"}])),
+ _ = wxMenu:append(FileMenu, wxMenuItem:new([{id, ?menuID_FILE_QUIT},
+ {text, "E&xit\tAlt-X"}])),
FileMenu.
createWarningsMenu() ->
WarningsMenu = wxMenu:new(),
- wxMenu:appendCheckItem(WarningsMenu,
- ?menuID_WARN_MATCH_FAILURES,
- "Match failures"),
- wxMenu:check(WarningsMenu, ?menuID_WARN_MATCH_FAILURES, true),
- wxMenu:appendCheckItem(WarningsMenu,
- ?menuID_WARN_FAIL_FUN_CALLS,
- "Failing function calls"),
- wxMenu:check(WarningsMenu, ?menuID_WARN_FAIL_FUN_CALLS, true),
- wxMenu:appendCheckItem(WarningsMenu,
- ?menuID_WARN_BAD_FUN,
- "Bad fun applications"),
- wxMenu:check(WarningsMenu, ?menuID_WARN_BAD_FUN, true),
- wxMenu:appendCheckItem(WarningsMenu,
- ?menuID_WARN_OPAQUE,
- "Opaqueness violations"),
- wxMenu:check(WarningsMenu, ?menuID_WARN_OPAQUE, true),
- wxMenu:appendCheckItem(WarningsMenu,
- ?menuID_WARN_LIST_CONSTR,
- "Improper list constructions"),
- wxMenu:check(WarningsMenu, ?menuID_WARN_LIST_CONSTR, true),
- wxMenu:appendCheckItem(WarningsMenu,
- ?menuID_WARN_UNUSED_FUN,
- "Unused functions"),
- wxMenu:check(WarningsMenu, ?menuID_WARN_UNUSED_FUN, true),
- wxMenu:appendCheckItem(WarningsMenu,
- ?menuID_WARN_ERROR_HANDLING_FUN,
- "Error handling functions"),
- wxMenu:appendCheckItem(WarningsMenu,
- ?menuID_WARN_NO_RETURN_FUN,
- "Functions of no return"),
- wxMenu:check(WarningsMenu, ?menuID_WARN_NO_RETURN_FUN, true),
- wxMenu:appendCheckItem(WarningsMenu,
- ?menuID_WARN_UNEXPORTED_FUN,
- "Call to unexported function"),
- wxMenu:check(WarningsMenu, ?menuID_WARN_UNEXPORTED_FUN, true),
- wxMenu:appendCheckItem(WarningsMenu,
- ?menuID_WARN_RACE_CONDITIONS,
- "Possible race conditions"),
- wxMenu:appendCheckItem(WarningsMenu,
- ?menuID_WARN_WRONG_CONTRACTS,
- "Wrong contracts"),
- wxMenu:check(WarningsMenu, ?menuID_WARN_WRONG_CONTRACTS, true),
- wxMenu:appendCheckItem(WarningsMenu,
- ?menuID_WARN_CONTRACT_SYNTAX,
- "Wrong contract syntax"),
- wxMenu:check(WarningsMenu, ?menuID_WARN_CONTRACT_SYNTAX, true),
+ addCheckedItem(WarningsMenu, ?menuID_WARN_MATCH_FAILURES, "Match failures"),
+ addCheckedItem(WarningsMenu, ?menuID_WARN_FAIL_FUN_CALLS,
+ "Failing function calls"),
+ addCheckedItem(WarningsMenu, ?menuID_WARN_BAD_FUN, "Bad fun applications"),
+ addCheckedItem(WarningsMenu, ?menuID_WARN_OPAQUE, "Opaqueness violations"),
+ addCheckedItem(WarningsMenu, ?menuID_WARN_LIST_CONSTR,
+ "Improper list constructions"),
+ addCheckedItem(WarningsMenu, ?menuID_WARN_UNUSED_FUN, "Unused functions"),
+ _ = wxMenu:appendCheckItem(WarningsMenu, ?menuID_WARN_ERROR_HANDLING_FUN,
+ "Error handling functions"),
+ addCheckedItem(WarningsMenu, ?menuID_WARN_NO_RETURN_FUN,
+ "Functions of no return"),
+ addCheckedItem(WarningsMenu, ?menuID_WARN_UNEXPORTED_FUN,
+ "Call to unexported function"),
+ _ = wxMenu:appendCheckItem(WarningsMenu, ?menuID_WARN_RACE_CONDITIONS,
+ "Possible race conditions"),
+ addCheckedItem(WarningsMenu, ?menuID_WARN_WRONG_CONTRACTS, "Wrong contracts"),
+ addCheckedItem(WarningsMenu, ?menuID_WARN_CONTRACT_SYNTAX,
+ "Wrong contract syntax"),
WarningsMenu.
+addCheckedItem(Menu, ItemId, Str) ->
+ _ = wxMenu:appendCheckItem(Menu, ItemId, Str),
+ wxMenu:check(Menu, ItemId, true).
+
createPltMenu() ->
PltMenu = wxMenu:new(),
- wxMenu:appendCheckItem(PltMenu,
- ?menuID_PLT_INIT_EMPTY,
- "Init with empty PLT"),
- wxMenu:append(PltMenu, wxMenuItem:new([{id, ?menuID_PLT_SHOW_CONTENTS},
- {text, "Show contents"}])),
- wxMenu:append(PltMenu, wxMenuItem:new([{id, ?menuID_PLT_SEARCH_CONTENTS},
- {text, "Search contents"}])),
+ _ = wxMenu:appendCheckItem(PltMenu, ?menuID_PLT_INIT_EMPTY,
+ "Init with empty PLT"),
+ _ = wxMenu:append(PltMenu, wxMenuItem:new([{id, ?menuID_PLT_SHOW_CONTENTS},
+ {text, "Show contents"}])),
+ _ = wxMenu:append(PltMenu, wxMenuItem:new([{id, ?menuID_PLT_SEARCH_CONTENTS},
+ {text, "Search contents"}])),
PltMenu.
createOptionsMenu() ->
OptsMenu = wxMenu:new(),
- wxMenu:append(OptsMenu, wxMenuItem:new([{id, ?menuID_OPTIONS_MACRO},
- {text, "Manage Macro Definitions"}])),
- wxMenu:append(OptsMenu, wxMenuItem:new([{id, ?menuID_OPTIONS_INCLUDE_DIR},
- {text, "Manage Include Directories"}])),
+ _ = wxMenu:append(OptsMenu, wxMenuItem:new([{id, ?menuID_OPTIONS_MACRO},
+ {text, "Manage Macro Definitions"}])),
+ _ = wxMenu:append(OptsMenu, wxMenuItem:new([{id, ?menuID_OPTIONS_INCLUDE_DIR},
+ {text, "Manage Include Directories"}])),
OptsMenu.
createHelpMenu() ->
HelpMenu = wxMenu:new(),
- wxMenu:append(HelpMenu, wxMenuItem:new([{id, ?menuID_HELP_MANUAL},
- {text, "Manual"}])),
- wxMenu:append(HelpMenu, wxMenuItem:new([{id, ?menuID_HELP_WARNING_OPTIONS},
- {text, "Warning Options"}])),
- wxMenu:append(HelpMenu, wxMenuItem:new([{id, ?menuID_HELP_ABOUT},
- {text, "About"}])),
+ _ = wxMenu:append(HelpMenu, wxMenuItem:new([{id, ?menuID_HELP_MANUAL},
+ {text, "Manual"}])),
+ _ = wxMenu:append(HelpMenu, wxMenuItem:new([{id, ?menuID_HELP_WARNING_OPTIONS},
+ {text, "Warning Options"}])),
+ _ = wxMenu:append(HelpMenu, wxMenuItem:new([{id, ?menuID_HELP_ABOUT},
+ {text, "About"}])),
HelpMenu.
%% ----------------------------------------------------------------
@@ -583,20 +561,20 @@ search_doc_plt(#gui_state{gui = Wx} = State) ->
ArLayout = wxBoxSizer:new(?wxVERTICAL),
Buttons = wxBoxSizer:new(?wxHORIZONTAL),
- wxSizer:add(ModLayout, ModLabel, ?BorderOpt),
- wxSizer:add(ModLayout,ModText, ?BorderOpt),
- wxSizer:add(FunLayout, FunLabel, ?BorderOpt),
- wxSizer:add(FunLayout,FunText, ?BorderOpt),
- wxSizer:add(ArLayout, ArLabel, ?BorderOpt),
- wxSizer:add(ArLayout,ArText, ?BorderOpt),
- wxSizer:add(Buttons, SearchButton, ?BorderOpt),
- wxSizer:add(Buttons,Cancel, ?BorderOpt),
-
- wxSizer:add(Top, ModLayout),
- wxSizer:add(Top, FunLayout),
- wxSizer:add(Top, ArLayout),
- wxSizer:add(Layout, Top,[{flag, ?wxALIGN_CENTER}]),
- wxSizer:add(Layout, Buttons,[{flag, ?wxALIGN_CENTER bor ?wxBOTTOM}]),
+ _ = wxSizer:add(ModLayout, ModLabel, ?BorderOpt),
+ _ = wxSizer:add(ModLayout, ModText, ?BorderOpt),
+ _ = wxSizer:add(FunLayout, FunLabel, ?BorderOpt),
+ _ = wxSizer:add(FunLayout,FunText, ?BorderOpt),
+ _ = wxSizer:add(ArLayout, ArLabel, ?BorderOpt),
+ _ = wxSizer:add(ArLayout,ArText, ?BorderOpt),
+ _ = wxSizer:add(Buttons, SearchButton, ?BorderOpt),
+ _ = wxSizer:add(Buttons,Cancel, ?BorderOpt),
+
+ _ = wxSizer:add(Top, ModLayout),
+ _ = wxSizer:add(Top, FunLayout),
+ _ = wxSizer:add(Top, ArLayout),
+ _ = wxSizer:add(Layout, Top,[{flag, ?wxALIGN_CENTER}]),
+ _ = wxSizer:add(Layout, Buttons,[{flag, ?wxALIGN_CENTER bor ?wxBOTTOM}]),
wxFrame:connect(Dialog, close_window),
wxWindow:setSizer(Dialog, Layout),
wxFrame:show(Dialog),
@@ -654,14 +632,15 @@ error_sms(State, Message) ->
output_sms(State, ?DIALYZER_ERROR_TITLE, Message, error).
output_sms(#gui_state{frame = Frame}, Title, Message, Type) ->
- case Type of
- error ->
- MessageWin = wxMessageDialog:new(Frame,Message,[{caption, Title},{style, ?wxOK bor ?wxICON_ERROR}]);
- info ->
- MessageWin = wxMessageDialog:new(Frame,Message,[{caption, Title},{style, ?wxOK bor ?wxICON_INFORMATION}])
- end,
+ Style = case Type of
+ error -> ?wxOK bor ?wxICON_ERROR;
+ info -> ?wxOK bor ?wxICON_INFORMATION
+ end,
+ Options = [{caption, Title}, {style, Style}],
+ MessageWin = wxMessageDialog:new(Frame, Message, Options),
wxWindow:setSizeHints(MessageWin, {350,100}),
- wxDialog:showModal(MessageWin).
+ wxDialog:showModal(MessageWin),
+ ok.
free_editor(#gui_state{gui = Wx, frame = Frame}, Title, Contents0) ->
Contents = lists:flatten(Contents0),
@@ -685,8 +664,9 @@ free_editor(#gui_state{gui = Wx, frame = Frame}, Title, Contents0) ->
wxButton:connect(Ok, command_button_clicked),
Layout = wxBoxSizer:new(?wxVERTICAL),
- wxSizer:add(Layout, Editor, ?BorderOpt),
- wxSizer:add(Layout, Ok, [{flag, ?wxALIGN_CENTER bor ?wxBOTTOM bor ?wxALL}, ?Border]),
+ _ = wxSizer:add(Layout, Editor, ?BorderOpt),
+ Flag = ?wxALIGN_CENTER bor ?wxBOTTOM bor ?wxALL,
+ _ = wxSizer:add(Layout, Ok, [{flag, Flag}, ?Border]),
wxWindow:setSizer(Win, Layout),
wxWindow:show(Win),
show_info_loop(Frame, Win).
@@ -696,7 +676,7 @@ show_info_loop(Frame, Win) ->
#wx{id = ?Message_Ok, event = #wxCommand{type = command_button_clicked}} ->
wxWindow:destroy(Win);
#wx{id = ?Message, event = #wxClose{type = close_window}} ->
- wxWindow:destroy(Win);
+ wxWindow:destroy(Win);
#wx{event = #wxClose{type = close_window}} ->
wxWindow:destroy(Frame)
end.
@@ -921,13 +901,14 @@ save_file(#gui_state{frame = Frame, warnings_box = WBox, log = Log} = State, Typ
{message, Message},
{style,?wxFD_SAVE bor ?wxFD_OVERWRITE_PROMPT}]),
case wxFileDialog:showModal(FileDialog) of
- ?wxID_OK -> Path = wxFileDialog:getPath(FileDialog),
- case wxTextCtrl:saveFile(Box,[{file,Path}]) of
- true -> ok;
- false -> error_sms(State,"Could not write to file:\n" ++ Path)
- end;
+ ?wxID_OK ->
+ Path = wxFileDialog:getPath(FileDialog),
+ case wxTextCtrl:saveFile(Box,[{file,Path}]) of
+ true -> ok;
+ false -> error_sms(State, "Could not write to file:\n" ++ Path)
+ end;
?wxID_CANCEL -> wxWindow:destroy(FileDialog);
- _ -> error_sms(State,"Could not write to file:\n")
+ _ -> error_sms(State, "Could not write to file:\n")
end
end.
@@ -961,16 +942,16 @@ include_dialog(#gui_state{gui = Wx, frame = Frame, options = Options}) ->
Buttons = wxBoxSizer:new(?wxHORIZONTAL),
Buttons1 = wxBoxSizer:new(?wxHORIZONTAL),
- wxSizer:add(Layout, DirLabel, [{flag, ?wxALIGN_CENTER_HORIZONTAL}]),
- wxSizer:add(Layout, DirPicker, [{flag, ?wxALIGN_CENTER_HORIZONTAL}]),
- wxSizer:add(Layout,AddButton, [{flag, ?wxALIGN_CENTER_HORIZONTAL bor ?wxALL}, ?Border]),
- wxSizer:add(Layout,Box, [{flag, ?wxALIGN_CENTER_HORIZONTAL bor ?wxALL}, ?Border]),
- wxSizer:add(Buttons, DeleteButton, ?BorderOpt),
- wxSizer:add(Buttons, DeleteAllButton, ?BorderOpt),
- wxSizer:add(Layout,Buttons, [{flag, ?wxALIGN_CENTER_HORIZONTAL}]),
- wxSizer:add(Buttons1, Ok, ?BorderOpt),
- wxSizer:add(Buttons1,Cancel, ?BorderOpt),
- wxSizer:add(Layout,Buttons1,[{flag, ?wxALIGN_RIGHT bor ?wxBOTTOM}]),
+ _ = wxSizer:add(Layout, DirLabel, [{flag, ?wxALIGN_CENTER_HORIZONTAL}]),
+ _ = wxSizer:add(Layout, DirPicker, [{flag, ?wxALIGN_CENTER_HORIZONTAL}]),
+ _ = wxSizer:add(Layout,AddButton, [{flag, ?wxALIGN_CENTER_HORIZONTAL bor ?wxALL}, ?Border]),
+ _ = wxSizer:add(Layout,Box, [{flag, ?wxALIGN_CENTER_HORIZONTAL bor ?wxALL}, ?Border]),
+ _ = wxSizer:add(Buttons, DeleteButton, ?BorderOpt),
+ _ = wxSizer:add(Buttons, DeleteAllButton, ?BorderOpt),
+ _ = wxSizer:add(Layout,Buttons, [{flag, ?wxALIGN_CENTER_HORIZONTAL}]),
+ _ = wxSizer:add(Buttons1, Ok, ?BorderOpt),
+ _ = wxSizer:add(Buttons1,Cancel, ?BorderOpt),
+ _ = wxSizer:add(Layout,Buttons1,[{flag, ?wxALIGN_RIGHT bor ?wxBOTTOM}]),
wxFrame:connect(Dialog, close_window),
wxWindow:setSizer(Dialog, Layout),
@@ -1058,21 +1039,21 @@ macro_dialog(#gui_state{gui = Wx, frame = Frame, options = Options}) ->
Buttons = wxBoxSizer:new(?wxHORIZONTAL),
Buttons1 = wxBoxSizer:new(?wxHORIZONTAL),
- wxSizer:add(MacroItem, MacroLabel, ?BorderOpt),
- wxSizer:add(MacroItem, MacroText, ?BorderOpt),
- wxSizer:add(TermItem, TermLabel, ?BorderOpt),
- wxSizer:add(TermItem, TermText, ?BorderOpt),
- wxSizer:add(Item, MacroItem),
- wxSizer:add(Item, TermItem),
- wxSizer:add(Layout, Item, [{flag, ?wxALIGN_CENTER_HORIZONTAL}]),
- wxSizer:add(Layout, AddButton, [{flag, ?wxALIGN_CENTER_HORIZONTAL bor ?wxALL}, ?Border]),
- wxSizer:add(Layout, Box, [{flag, ?wxALIGN_CENTER_HORIZONTAL bor ?wxALL}, ?Border]),
- wxSizer:add(Buttons, DeleteButton, ?BorderOpt),
- wxSizer:add(Buttons, DeleteAllButton, ?BorderOpt),
- wxSizer:add(Layout, Buttons, [{flag, ?wxALIGN_CENTER_HORIZONTAL}]),
- wxSizer:add(Buttons1, Ok, ?BorderOpt),
- wxSizer:add(Buttons1, Cancel, ?BorderOpt),
- wxSizer:add(Layout, Buttons1, [{flag, ?wxALIGN_RIGHT bor ?wxBOTTOM}]),
+ _ = wxSizer:add(MacroItem, MacroLabel, ?BorderOpt),
+ _ = wxSizer:add(MacroItem, MacroText, ?BorderOpt),
+ _ = wxSizer:add(TermItem, TermLabel, ?BorderOpt),
+ _ = wxSizer:add(TermItem, TermText, ?BorderOpt),
+ _ = wxSizer:add(Item, MacroItem),
+ _ = wxSizer:add(Item, TermItem),
+ _ = wxSizer:add(Layout, Item, [{flag, ?wxALIGN_CENTER_HORIZONTAL}]),
+ _ = wxSizer:add(Layout, AddButton, [{flag, ?wxALIGN_CENTER_HORIZONTAL bor ?wxALL}, ?Border]),
+ _ = wxSizer:add(Layout, Box, [{flag, ?wxALIGN_CENTER_HORIZONTAL bor ?wxALL}, ?Border]),
+ _ = wxSizer:add(Buttons, DeleteButton, ?BorderOpt),
+ _ = wxSizer:add(Buttons, DeleteAllButton, ?BorderOpt),
+ _ = wxSizer:add(Layout, Buttons, [{flag, ?wxALIGN_CENTER_HORIZONTAL}]),
+ _ = wxSizer:add(Buttons1, Ok, ?BorderOpt),
+ _ = wxSizer:add(Buttons1, Cancel, ?BorderOpt),
+ _ = wxSizer:add(Layout, Buttons1, [{flag, ?wxALIGN_RIGHT bor ?wxBOTTOM}]),
wxFrame:connect(Dialog, close_window),
wxWindow:setSizer(Dialog, Layout),
@@ -1216,13 +1197,14 @@ show_explanation(#gui_state{gui = Wx} = State, Explanation) ->
wxButton:connect(Ok, command_button_clicked),
Layout = wxBoxSizer:new(?wxVERTICAL),
Buttons = wxBoxSizer:new(?wxHORIZONTAL),
- wxSizer:add(Buttons, ExplButton, ?BorderOpt),
- wxSizer:add(Buttons, Ok, ?BorderOpt),
- wxSizer:add(Layout, Editor,[{flag, ?wxALIGN_CENTER_HORIZONTAL bor ?wxALL}, ?Border]),
- wxSizer:add(Layout, Buttons,[{flag, ?wxALIGN_CENTER_HORIZONTAL}]),
+ _ = wxSizer:add(Buttons, ExplButton, ?BorderOpt),
+ _ = wxSizer:add(Buttons, Ok, ?BorderOpt),
+ _ = wxSizer:add(Layout, Editor, [{flag, ?wxALIGN_CENTER_HORIZONTAL bor ?wxALL}, ?Border]),
+ _ = wxSizer:add(Layout, Buttons,[{flag, ?wxALIGN_CENTER_HORIZONTAL}]),
wxWindow:setSizer(Win, Layout),
wxWindow:show(Win),
- show_explanation_loop(State#gui_state{explanation_box = Editor}, Win, Explanation)
+ NewState = State#gui_state{explanation_box = Editor},
+ show_explanation_loop(NewState, Win, Explanation)
end.
show_explanation_loop(#gui_state{frame = Frame, expl_pid = ExplPid} = State, Win, Explanation) ->
diff --git a/lib/dialyzer/src/dialyzer_options.erl b/lib/dialyzer/src/dialyzer_options.erl
index a1e316d6cc..06672e595f 100644
--- a/lib/dialyzer/src/dialyzer_options.erl
+++ b/lib/dialyzer/src/dialyzer_options.erl
@@ -2,7 +2,7 @@
%%-----------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -196,6 +196,9 @@ build_options([{OptionName, Value} = Term|Rest], Options) ->
build_options(Rest, Options#options{callgraph_file = Value});
timing ->
build_options(Rest, Options#options{timing = Value});
+ solvers ->
+ assert_solvers(Value),
+ build_options(Rest, Options#options{solvers = Value});
_ ->
bad_option("Unknown dialyzer command line option", Term)
end;
@@ -257,6 +260,15 @@ is_plt_mode(plt_remove) -> true;
is_plt_mode(plt_check) -> true;
is_plt_mode(succ_typings) -> false.
+assert_solvers([]) ->
+ ok;
+assert_solvers([v1|Terms]) ->
+ assert_solvers(Terms);
+assert_solvers([v2|Terms]) ->
+ assert_solvers(Terms);
+assert_solvers([Term|_]) ->
+ bad_option("Illegal value for solver", Term).
+
-spec build_warnings([atom()], [dial_warning()]) -> [dial_warning()].
build_warnings([Opt|Opts], Warnings) ->
diff --git a/lib/dialyzer/src/dialyzer_races.erl b/lib/dialyzer/src/dialyzer_races.erl
index cdb9f25999..2aa8343bce 100644
--- a/lib/dialyzer/src/dialyzer_races.erl
+++ b/lib/dialyzer/src/dialyzer_races.erl
@@ -1758,7 +1758,10 @@ compare_var_list(Var, VarList, RaceVarMap) ->
ets_list_args(MaybeList) ->
case is_list(MaybeList) of
- true -> [ets_tuple_args(T) || T <- MaybeList];
+ true ->
+ try [ets_tuple_args(T) || T <- MaybeList]
+ catch _:_ -> [?no_label]
+ end;
false -> [ets_tuple_args(MaybeList)]
end.
diff --git a/lib/dialyzer/src/dialyzer_succ_typings.erl b/lib/dialyzer/src/dialyzer_succ_typings.erl
index 9ca5a66dab..84379642bf 100644
--- a/lib/dialyzer/src/dialyzer_succ_typings.erl
+++ b/lib/dialyzer/src/dialyzer_succ_typings.erl
@@ -28,8 +28,8 @@
-module(dialyzer_succ_typings).
-export([analyze_callgraph/3,
- analyze_callgraph/5,
- get_warnings/7
+ analyze_callgraph/6,
+ get_warnings/8
]).
-export([
@@ -75,6 +75,7 @@
no_warn_unused :: set(),
parent = none :: parent(),
timing_server :: dialyzer_timing:timing_server(),
+ solvers :: [solver()],
plt :: dialyzer_plt:plt()}).
%%--------------------------------------------------------------------
@@ -84,28 +85,29 @@
dialyzer_plt:plt().
analyze_callgraph(Callgraph, Plt, Codeserver) ->
- analyze_callgraph(Callgraph, Plt, Codeserver, none, none).
+ analyze_callgraph(Callgraph, Plt, Codeserver, none, [], none).
-spec analyze_callgraph(dialyzer_callgraph:callgraph(), dialyzer_plt:plt(),
dialyzer_codeserver:codeserver(),
- dialyzer_timing:timing_server(), parent()) ->
+ dialyzer_timing:timing_server(),
+ [solver()], parent()) ->
dialyzer_plt:plt().
-analyze_callgraph(Callgraph, Plt, Codeserver, TimingServer, Parent) ->
+analyze_callgraph(Callgraph, Plt, Codeserver, TimingServer, Solvers, Parent) ->
NewState =
init_state_and_get_success_typings(Callgraph, Plt, Codeserver,
- TimingServer, Parent),
+ TimingServer, Solvers, Parent),
dialyzer_plt:restore_full_plt(NewState#st.plt, Plt).
%%--------------------------------------------------------------------
init_state_and_get_success_typings(Callgraph, Plt, Codeserver,
- TimingServer, Parent) ->
+ TimingServer, Solvers, Parent) ->
{SCCs, Callgraph1} =
?timing(TimingServer, "order", dialyzer_callgraph:finalize(Callgraph)),
State = #st{callgraph = Callgraph1, plt = dialyzer_plt:get_mini_plt(Plt),
codeserver = Codeserver, parent = Parent,
- timing_server = TimingServer},
+ timing_server = TimingServer, solvers = Solvers},
get_refined_success_typings(SCCs, State).
get_refined_success_typings(SCCs, #st{callgraph = Callgraph,
@@ -136,14 +138,14 @@ get_refined_success_typings(SCCs, #st{callgraph = Callgraph,
-type doc_plt() :: 'undefined' | dialyzer_plt:plt().
-spec get_warnings(dialyzer_callgraph:callgraph(), dialyzer_plt:plt(),
doc_plt(), dialyzer_codeserver:codeserver(), set(),
- dialyzer_timing:timing_server(), pid()) ->
+ dialyzer_timing:timing_server(), [solver()], pid()) ->
{[dial_warning()], dialyzer_plt:plt(), doc_plt()}.
get_warnings(Callgraph, Plt, DocPlt, Codeserver,
- NoWarnUnused, TimingServer, Parent) ->
+ NoWarnUnused, TimingServer, Solvers, Parent) ->
InitState =
init_state_and_get_success_typings(Callgraph, Plt, Codeserver,
- TimingServer, Parent),
+ TimingServer, Solvers, Parent),
NewState = InitState#st{no_warn_unused = NoWarnUnused},
Mods = dialyzer_callgraph:modules(NewState#st.callgraph),
MiniPlt = NewState#st.plt,
@@ -222,9 +224,10 @@ postprocess_dataflow_warns([{?WARN_CONTRACT_RANGE, {CallF, CallL}, Msg}|Rest],
refine_succ_typings(Modules, #st{codeserver = Codeserver,
callgraph = Callgraph,
plt = Plt,
- timing_server = Timing} = State) ->
+ timing_server = Timing,
+ solvers = Solvers} = State) ->
?debug("Module postorder: ~p\n", [Modules]),
- Init = {Codeserver, Callgraph, Plt},
+ Init = {Codeserver, Callgraph, Plt, Solvers},
NotFixpoint =
?timing(Timing, "refine",
dialyzer_coordinator:parallel_job(dataflow, Modules, Init, Timing)),
@@ -236,22 +239,22 @@ refine_succ_typings(Modules, #st{codeserver = Codeserver,
-spec find_depends_on(scc() | module(), fixpoint_init_data()) -> [scc()].
-find_depends_on(SCC, {_Codeserver, Callgraph, _Plt}) ->
+find_depends_on(SCC, {_Codeserver, Callgraph, _Plt, _Solvers}) ->
dialyzer_callgraph:get_depends_on(SCC, Callgraph).
-spec find_required_by(scc() | module(), fixpoint_init_data()) -> [scc()].
-find_required_by(SCC, {_Codeserver, Callgraph, _Plt}) ->
+find_required_by(SCC, {_Codeserver, Callgraph, _Plt, _Solvers}) ->
dialyzer_callgraph:get_required_by(SCC, Callgraph).
-spec lookup_names([label()], fixpoint_init_data()) -> [mfa_or_funlbl()].
-lookup_names(Labels, {_Codeserver, Callgraph, _Plt}) ->
+lookup_names(Labels, {_Codeserver, Callgraph, _Plt, _Solvers}) ->
[lookup_name(F, Callgraph) || F <- Labels].
-spec refine_one_module(module(), dataflow_init_data()) -> [label()]. % ordset
-refine_one_module(M, {CodeServer, Callgraph, Plt}) ->
+refine_one_module(M, {CodeServer, Callgraph, Plt, _Solvers}) ->
ModCode = dialyzer_codeserver:lookup_mod_code(M, CodeServer),
AllFuns = collect_fun_info([ModCode]),
Records = dialyzer_codeserver:lookup_mod_records(M, CodeServer),
@@ -322,8 +325,9 @@ compare_types_1([], [], _Strict, NotFixpoint) ->
end.
find_succ_typings(SCCs, #st{codeserver = Codeserver, callgraph = Callgraph,
- plt = Plt, timing_server = Timing} = State) ->
- Init = {Codeserver, Callgraph, Plt},
+ plt = Plt, timing_server = Timing,
+ solvers = Solvers} = State) ->
+ Init = {Codeserver, Callgraph, Plt, Solvers},
NotFixpoint =
?timing(Timing, "typesig",
dialyzer_coordinator:parallel_job(typesig, SCCs, Init, Timing)),
@@ -335,7 +339,7 @@ find_succ_typings(SCCs, #st{codeserver = Codeserver, callgraph = Callgraph,
-spec find_succ_types_for_scc(scc(), typesig_init_data()) -> [mfa_or_funlbl()].
-find_succ_types_for_scc(SCC, {Codeserver, Callgraph, Plt}) ->
+find_succ_types_for_scc(SCC, {Codeserver, Callgraph, Plt, Solvers}) ->
SCC_Info = [{MFA,
dialyzer_codeserver:lookup_mfa_code(MFA, Codeserver),
dialyzer_codeserver:lookup_mod_records(M, Codeserver)}
@@ -348,8 +352,8 @@ find_succ_types_for_scc(SCC, {Codeserver, Callgraph, Plt}) ->
AllFuns = collect_fun_info([Fun || {_MFA, {_Var, Fun}, _Rec} <- SCC_Info]),
PropTypes = get_fun_types_from_plt(AllFuns, Callgraph, Plt),
%% Assume that the PLT contains the current propagated types
- FunTypes =
- dialyzer_typesig:analyze_scc(SCC_Info, Label, Callgraph, Plt, PropTypes),
+ FunTypes = dialyzer_typesig:analyze_scc(SCC_Info, Label, Callgraph,
+ Plt, PropTypes, Solvers),
AllFunSet = sets:from_list([X || {X, _} <- AllFuns]),
FilteredFunTypes =
dict:filter(fun(X, _) -> sets:is_element(X, AllFunSet) end, FunTypes),
diff --git a/lib/dialyzer/src/dialyzer_typesig.erl b/lib/dialyzer/src/dialyzer_typesig.erl
index e997eedf76..0df003a035 100644
--- a/lib/dialyzer/src/dialyzer_typesig.erl
+++ b/lib/dialyzer/src/dialyzer_typesig.erl
@@ -28,7 +28,7 @@
-module(dialyzer_typesig).
--export([analyze_scc/5]).
+-export([analyze_scc/6]).
-export([get_safe_underapprox/2]).
-import(erl_types,
@@ -78,6 +78,8 @@
-record(constraint_list, {type :: 'conj' | 'disj',
list :: [constr()],
deps :: [dep()],
+ masks :: [{dep(),[non_neg_integer()]}] |
+ {'d',dict()},
id :: {'list', dep()}}).
-type constraint_list() :: #constraint_list{}.
@@ -109,7 +111,8 @@
records = dict:new() :: dict(),
opaques = [] :: [erl_types:erl_type()],
scc = [] :: [type_var()],
- mfas :: [tuple()]
+ mfas :: [tuple()],
+ solvers = [] :: [solver()]
}).
%%-----------------------------------------------------------------------------
@@ -121,8 +124,10 @@
%%-define(DEBUG_CONSTRAINTS, true).
-ifdef(DEBUG).
-define(DEBUG_NAME_MAP, true).
+-define(DEBUG_LOOP_DETECTION, true).
-endif.
%%-define(DEBUG_NAME_MAP, true).
+%%-define(DEBUG_LOOP_DETECTION, true).
-ifdef(DEBUG).
-define(debug(__String, __Args), io:format(__String, __Args)).
@@ -141,7 +146,7 @@
%%-----------------------------------------------------------------------------
%% Analysis of strongly connected components.
%%
-%% analyze_scc(SCC, NextLabel, CallGraph, PLT, PropTypes) -> FunTypes
+%% analyze_scc(SCC, NextLabel, CallGraph, PLT, PropTypes, Solvers) -> FunTypes
%%
%% SCC - [{MFA, Def, Records}]
%% where Def = {Var, Fun} as in the Core Erlang module definitions.
@@ -154,15 +159,17 @@
%% about functions that can be called by this SCC.
%% PropTypes - A dictionary.
%% FunTypes - A dictionary.
+%% Solvers - User specified solvers.
%%-----------------------------------------------------------------------------
-spec analyze_scc(typesig_scc(), label(),
dialyzer_callgraph:callgraph(),
- dialyzer_plt:plt(), dict()) -> dict().
+ dialyzer_plt:plt(), dict(), [solver()]) -> dict().
-analyze_scc(SCC, NextLabel, CallGraph, Plt, PropTypes) ->
+analyze_scc(SCC, NextLabel, CallGraph, Plt, PropTypes, Solvers0) ->
+ Solvers = solvers(Solvers0),
assert_format_of_scc(SCC),
- State1 = new_state(SCC, NextLabel, CallGraph, Plt, PropTypes),
+ State1 = new_state(SCC, NextLabel, CallGraph, Plt, PropTypes, Solvers),
DefSet = add_def_list([Var || {_MFA, {Var, _Fun}, _Rec} <- SCC], sets:new()),
State2 = traverse_scc(SCC, DefSet, State1),
State3 = state__finalize(State2),
@@ -176,6 +183,9 @@ assert_format_of_scc([{_MFA, {_Var, _Fun}, _Records}|Left]) ->
assert_format_of_scc([]) ->
ok.
+solvers([]) -> [v2];
+solvers(Solvers) -> Solvers.
+
%% ============================================================================
%%
%% Gets the constraints by traversing the code.
@@ -1663,7 +1673,7 @@ get_bif_test_constr(Dst, Arg, Type, State) ->
solve([Fun], State) ->
?debug("============ Analyzing Fun: ~w ===========\n",
[debug_lookup_name(Fun)]),
- solve_fun(Fun, dict:new(), State);
+ solve_fun(Fun, map_new(), State);
solve([_|_] = SCC, State) ->
?debug("============ Analyzing SCC: ~w ===========\n",
[[debug_lookup_name(F) || F <- SCC]]),
@@ -1672,14 +1682,14 @@ solve([_|_] = SCC, State) ->
false -> {false, State};
SplitSCC -> {SplitSCC, minimize_state(State)}
end,
- solve_scc(SCC, Parallel, dict:new(), NewState, false).
+ solve_scc(SCC, Parallel, map_new(), NewState, false).
solve_fun(Fun, FunMap, State) ->
Cs = state__get_cs(Fun, State),
Deps = get_deps(Cs),
Ref = mk_constraint_ref(Fun, Deps),
%% Note that functions are always considered to succeed.
- {ok, _MapDict, NewMap} = solve_ref_or_list(Ref, FunMap, dict:new(), State),
+ NewMap = solve(Fun, Ref, FunMap, State),
NewType = lookup_type(Fun, NewMap),
NewFunMap1 = case state__get_rec_var(Fun, State) of
error -> FunMap;
@@ -1694,7 +1704,7 @@ solve_scc(SCC, Parallel, Map, State, TryingUnit) ->
Types = unsafe_lookup_type_list(Funs, Map),
RecTypes = [t_limit(Type, ?TYPE_LIMIT) || Type <- Types],
CleanMap = lists:foldl(fun(Fun, AccFunMap) ->
- dict:erase(t_var_name(Fun), AccFunMap)
+ erase_type(t_var_name(Fun), AccFunMap)
end, Map, SCC),
Map1 = enter_type_lists(Vars, RecTypes, CleanMap),
?debug("Checking SCC: ~w\n", [[debug_lookup_name(F) || F <- SCC]]),
@@ -1758,7 +1768,8 @@ minimize_state(#state{
fun_arities = FunArities,
self_rec = SelfRec,
prop_types = {d, PropTypes},
- opaques = Opaques
+ opaques = Opaques,
+ solvers = Solvers
}) ->
ETSCMap = ets:new(cmap,[{read_concurrency, true}]),
ETSPropTypes = ets:new(prop_types,[{read_concurrency, true}]),
@@ -1770,7 +1781,8 @@ minimize_state(#state{
fun_arities = FunArities,
self_rec = SelfRec,
prop_types = {e, ETSPropTypes},
- opaques = Opaques
+ opaques = Opaques,
+ solvers = Solvers
}.
dispose_state(#state{cmap = {e, ETSCMap},
@@ -1842,7 +1854,7 @@ scc_fold_fun(F, FunMap, State) ->
Deps = get_deps(state__get_cs(F, State)),
Cs = mk_constraint_ref(F, Deps),
%% Note that functions are always considered to succeed.
- {ok, _NewMapDict, Map} = solve_ref_or_list(Cs, FunMap, dict:new(), State),
+ Map = solve(F, Cs, FunMap, State),
NewType0 = unsafe_lookup_type(F, Map),
NewType = t_limit(NewType0, ?TYPE_LIMIT),
NewFunMap = case state__get_rec_var(F, State) of
@@ -1855,15 +1867,440 @@ scc_fold_fun(F, FunMap, State) ->
format_type(NewType)]),
NewFunMap.
+solve(Fun, Cs, FunMap, State) ->
+ Solvers = State#state.solvers,
+ R = [solver(S, solve_fun(S, Fun, Cs, FunMap, State)) || S <- Solvers],
+ check_solutions(R, Fun, no_solver, no_map).
+
+solver(Solver, SolveFun) ->
+ ?debug("Start solver ~w\n", [Solver]),
+ try timer:tc(SolveFun) of
+ {Time, {ok, Map}} ->
+ ?debug("End solver ~w (~w microsecs)\n", [Solver, Time]),
+ {Solver, Map, Time};
+ {_, _R} ->
+ ?debug("Solver ~w returned unexpected result:\n ~P\n",
+ [Solver, _R, 60]),
+ throw(error)
+ catch E:R ->
+ io:format("Solver ~w failed: ~w:~p\n ~p\n",
+ [Solver, E, R, erlang:get_stacktrace()]),
+ throw(error)
+ end.
+
+solve_fun(v1, _Fun, Cs, FunMap, State) ->
+ fun() ->
+ {ok, _MapDict, NewMap} = solve_ref_or_list(Cs, FunMap, dict:new(), State),
+ {ok, NewMap}
+ end;
+solve_fun(v2, Fun, _Cs, FunMap, State) ->
+ fun() -> v2_solve_ref(Fun, FunMap, State) end.
+
+check_solutions([], _Fun, _S, Map) ->
+ Map;
+check_solutions([{S1,Map1,_Time1}|Maps], Fun, S, Map) ->
+ ?debug("Solver ~w needed ~w microsecs\n", [S1, _Time1]),
+ case Map =:= no_map orelse sane_maps(Map, Map1, [Fun], S, S1) of
+ true ->
+ check_solutions(Maps, Fun, S1, Map1);
+ false ->
+ ?debug("Constraint solvers do not agree on ~w\n", [Fun]),
+ pp_map(atom_to_list(S), Map),
+ pp_map(atom_to_list(S1), Map1),
+ io:format("A bug was found. Please report it, and use the option "
+ "`--solver v1' until the bug has been fixed.\n"),
+ throw(error)
+ end.
+
+sane_maps(Map1, Map2, Keys, _S1, _S2) ->
+ lists:all(fun(Key) ->
+ V1 = unsafe_lookup_type(Key, Map1),
+ V2 = unsafe_lookup_type(Key, Map2),
+ case t_is_equal(V1, V2) of
+ true -> true;
+ false ->
+ ?debug("Constraint solvers do not agree on ~w\n", [Key]),
+ ?debug("~w: ~s\n",
+ [_S1, format_type(unsafe_lookup_type(Key, Map1))]),
+ ?debug("~w: ~s\n",
+ [_S2, format_type(unsafe_lookup_type(Key, Map2))]),
+ false
+ end
+ end, Keys).
+
+%% Solver v2
+
+-record(v2_state, {constr_data = dict:new() :: dict(),
+ state :: #state{}}).
+
+v2_solve_ref(Fun, Map, State) ->
+ V2State = #v2_state{state = State},
+ {ok, NewMap, _, _} = v2_solve_reference(Fun, Map, V2State),
+ {ok, NewMap}.
+
+v2_solve(#constraint{}=C, Map, V2State) ->
+ State = V2State#v2_state.state,
+ case solve_one_c(C, Map, State#state.opaques) of
+ error ->
+ report_failed_constraint(C, Map),
+ {error, V2State};
+ {ok, {NewMap, U}} ->
+ {ok, NewMap, V2State, U}
+ end;
+v2_solve(#constraint_list{type = disj}=C, Map, V2State) ->
+ v2_solve_disjunct(C, Map, V2State);
+v2_solve(#constraint_list{type = conj}=C, Map, V2State) ->
+ v2_solve_conjunct(C, Map, V2State);
+v2_solve(#constraint_ref{id = Id}, Map, V2State) ->
+ v2_solve_reference(Id, Map, V2State).
+
+v2_solve_reference(Id, Map, V2State0) ->
+ ?debug("Checking ref to fun: ~w\n", [debug_lookup_name(Id)]),
+ pp_map("Map", Map),
+ pp_constr_data("solve_ref", V2State0),
+ Map1 = restore_local_map(V2State0, Id, Map),
+ State = V2State0#v2_state.state,
+ Cs = state__get_cs(Id, State),
+ Res =
+ case state__is_self_rec(Id, State) of
+ true -> v2_solve_self_recursive(Cs, Map1, Id, t_none(), V2State0);
+ false -> v2_solve(Cs, Map1, V2State0)
+ end,
+ {FunType, V2State} =
+ case Res of
+ {error, V2State1} ->
+ ?debug("Error solving for function ~p\n", [debug_lookup_name(Id)]),
+ Arity = state__fun_arity(Id, State),
+ FunType0 =
+ case state__prop_domain(t_var_name(Id), State) of
+ error -> t_fun(Arity, t_none());
+ {ok, Dom} -> t_fun(Dom, t_none())
+ end,
+ {FunType0, V2State1};
+ {ok, NewMap, V2State1, U} ->
+ ?debug("Done solving fun: ~p\n", [debug_lookup_name(Id)]),
+ FunType0 = lookup_type(Id, NewMap),
+ V2State2 = save_local_map(V2State1, Id, U, NewMap),
+ {FunType0, V2State2}
+ end,
+ ?debug("ref Id=~w Assigned ~s\n", [Id, format_type(FunType)]),
+ {NewMap1, U1} = enter_var_type(Id, FunType, Map),
+ {NewMap2, U2} =
+ case state__get_rec_var(Id, State) of
+ {ok, Var} -> enter_var_type(Var, FunType, NewMap1);
+ error -> {NewMap1, []}
+ end,
+ {ok, NewMap2, V2State, lists:umerge(U1, U2)}.
+
+v2_solve_self_recursive(Cs, Map, Id, RecType0, V2State0) ->
+ ?debug("Solving self recursive ~w\n", [debug_lookup_name(Id)]),
+ State = V2State0#v2_state.state,
+ {ok, RecVar} = state__get_rec_var(Id, State),
+ ?debug("OldRecType ~s\n", [format_type(RecType0)]),
+ RecType = t_limit(RecType0, ?TYPE_LIMIT),
+ {Map1, U0} = enter_var_type(RecVar, RecType, Map),
+ V2State1 = save_updated_vars1(V2State0, Cs, U0), % Probably not necessary
+ case v2_solve(Cs, Map1, V2State1) of
+ {error, _V2State}=Error ->
+ case t_is_none(RecType0) of
+ true ->
+ %% Try again and assume that this is a non-terminating function.
+ Arity = state__fun_arity(Id, State),
+ NewRecType = t_fun(lists:duplicate(Arity, t_any()), t_unit()),
+ v2_solve_self_recursive(Cs, Map, Id, NewRecType, V2State0);
+ false ->
+ Error
+ end;
+ {ok, NewMap, V2State, U} ->
+ pp_map("recursive finished", NewMap),
+ NewRecType = unsafe_lookup_type(Id, NewMap),
+ case t_is_equal(NewRecType, RecType0) of
+ true ->
+ {NewMap2, U1} = enter_var_type(RecVar, NewRecType, NewMap),
+ {ok, NewMap2, V2State, lists:umerge(U, U1)};
+ false ->
+ v2_solve_self_recursive(Cs, Map, Id, NewRecType, V2State0)
+ end
+ end.
+
+enter_var_type(Var, Type, Map0) ->
+ {Map, Vs} = enter_type2(Var, Type, Map0),
+ {Map, [t_var_name(V) || V <- Vs]}.
+
+v2_solve_disjunct(Disj, Map, V2State0) ->
+ #constraint_list{type = disj, id = _Id, list = Cs, masks = Masks} = Disj,
+ ?debug("disjunct Id=~w~n", [_Id]),
+ pp_map("Map", Map),
+ pp_constr_data("disjunct", V2State0),
+ case get_flags(V2State0, Disj) of
+ {V2State1, failed_list} -> {error, V2State1}; % cannot happen
+ {V2State1, Flags} when Flags =/= [] ->
+ {ok, V2State, Eval, UL, MapL0, Uneval, Failed} =
+ v2_solve_disj(Flags, Cs, 1, Map, V2State1, [], [], [], [], false),
+ ?debug("disj ending _Id=~w Eval=~w, |Uneval|=~w |UL|=~w~n",
+ [_Id, Eval, length(Uneval), length(UL)]),
+ if Eval =:= [], Uneval =:= [] ->
+ {error, failed_list(Disj, V2State0)};
+ true ->
+ {Is0, UnIds} = lists:unzip(Uneval),
+ MapL = [restore_local_map(V2State, Id, Map) ||
+ Id <- UnIds] ++ MapL0,
+ %% If some branch has just failed every variable of the
+ %% non-failed branches need to be checked, not just the
+ %% updated ones.
+ U0 = case Failed of
+ false -> lists:umerge(UL);
+ true -> constrained_keys(MapL)
+ end,
+ if U0 =:= [] -> {ok, Map, V2State, []};
+ true ->
+ NotFailed = lists:umerge(Is0, Eval),
+ U1 = [V || V <- U0,
+ var_occurs_everywhere(V, Masks, NotFailed)],
+ NewMap = join_maps(U1, MapL, Map),
+ pp_map("NewMap", NewMap),
+ U = updated_vars_only(U1, Map, NewMap),
+ ?debug("disjunct finished _Id=~w\n", [_Id]),
+ {ok, NewMap, V2State, U}
+ end
+ end
+ end.
+
+var_occurs_everywhere(V, Masks, NotFailed) ->
+ ordsets:is_subset(NotFailed, get_mask(V, Masks)).
+
+v2_solve_disj([I|Is], [C|Cs], I, Map0, V2State0, UL, MapL, Eval, Uneval,
+ Failed0) ->
+ Id = C#constraint_list.id,
+ Map1 = restore_local_map(V2State0, Id, Map0),
+ case v2_solve(C, Map1, V2State0) of
+ {error, V2State} ->
+ ?debug("disj error I=~w~n", [I]),
+ Failed = Failed0 orelse not is_failed_list(C, V2State0),
+ v2_solve_disj(Is, Cs, I+1, Map0, V2State, UL, MapL, Eval, Uneval, Failed);
+ {ok, Map, V2State1, U} ->
+ ?debug("disj I=~w U=~w~n", [I, U]),
+ V2State = save_local_map(V2State1, Id, U, Map),
+ pp_map("DMap", Map),
+ v2_solve_disj(Is, Cs, I+1, Map0, V2State, [U|UL], [Map|MapL],
+ [I|Eval], Uneval, Failed0)
+ end;
+v2_solve_disj([], [], _I, _Map, V2State, UL, MapL, Eval, Uneval, Failed) ->
+ {ok, V2State, lists:reverse(Eval), UL, MapL, lists:reverse(Uneval), Failed};
+v2_solve_disj(Is, [C|Cs], I, Map, V2State, UL, MapL, Eval, Uneval0, Failed) ->
+ Uneval = [{I,C#constraint_list.id} ||
+ not is_failed_list(C, V2State)] ++ Uneval0,
+ v2_solve_disj(Is, Cs, I+1, Map, V2State, UL, MapL, Eval, Uneval, Failed).
+
+save_local_map(#v2_state{constr_data = ConData}=V2State, Id, U, Map) ->
+ Part0 = [{V,dict:fetch(V, Map)} || V <- U],
+ Part1 =
+ case dict:find(Id, ConData) of
+ error -> []; % cannot happen
+ {ok, {Part2,[]}} -> Part2
+ end,
+ ?debug("save local map Id=~w:\n", [Id]),
+ Part = lists:ukeymerge(1, lists:keysort(1, Part0), Part1),
+ pp_map("New Part", dict:from_list(Part0)),
+ pp_map("Old Part", dict:from_list(Part1)),
+ pp_map(" => Part", dict:from_list(Part)),
+ V2State#v2_state{constr_data = dict:store(Id, {Part,[]}, ConData)}.
+
+restore_local_map(#v2_state{constr_data = ConData}, Id, Map0) ->
+ case dict:find(Id, ConData) of
+ error -> Map0;
+ {ok, failed} -> Map0;
+ {ok, {[],_}} -> Map0;
+ {ok, {Part0,U}} ->
+ Part = [{K,V} || {K,V} <- Part0, not lists:member(K, U)],
+ ?debug("restore local map Id=~w U=~w\n", [Id, U]),
+ pp_map("Part", dict:from_list(Part)),
+ pp_map("Map0", Map0),
+ Map = lists:foldl(fun({K,V}, D) -> dict:store(K, V, D)end, Map0, Part),
+ pp_map("Map", Map),
+ Map
+ end.
+
+v2_solve_conjunct(Conj, Map, V2State0) ->
+ #constraint_list{type = conj, list = Cs} = Conj,
+ ?debug("conjunct Id=~w~n", [Conj#constraint_list.id]),
+ IsFlat = case Cs of [#constraint{}|_] -> true; _ -> false end,
+ case get_flags(V2State0, Conj) of
+ {V2State, failed_list} -> {error, V2State};
+ {V2State, Flags} ->
+ v2_solve_conj(Flags, Cs, 1, Map, Conj, IsFlat, V2State, [], [], [],
+ Map, Flags)
+ end.
+
+%% LastMap and LastFlags are used for loop detection.
+v2_solve_conj([I|Is], [Cs|Tail], I, Map0, Conj, IsFlat, V2State0,
+ UL, NewFs0, VarsUp, LastMap, LastFlags) ->
+ ?debug("conj Id=~w I=~w~n", [Conj#constraint_list.id, I]),
+ true = IsFlat =:= is_record(Cs, constraint),
+ pp_constr_data("conj", V2State0),
+ case v2_solve(Cs, Map0, V2State0) of
+ {error, V2State1} -> {error, failed_list(Conj, V2State1)};
+ {ok, Map, V2State1, []} ->
+ v2_solve_conj(Is, Tail, I+1, Map, Conj, IsFlat, V2State1,
+ UL, NewFs0, VarsUp, LastMap, LastFlags);
+ {ok, Map, V2State1, U} when IsFlat -> % optimization
+ %% It is ensured by enumerate_constraints() that every
+ %% #constraint{} has a conjunct as parent, and that such a
+ %% parent has nothing but #constraint{}:s as children, a fact
+ %% which is used here to simplify the flag calculation.
+ Mask = lists:umerge([get_mask(V, Conj#constraint_list.masks) || V <- U]),
+ {Is1, NewF} = add_mask_to_flags(Is, Mask, I, []),
+ NewFs = [NewF|NewFs0],
+ v2_solve_conj(Is1, Tail, I+1, Map, Conj, IsFlat, V2State1,
+ [U|UL], NewFs, VarsUp, LastMap, LastFlags);
+ {ok, Map, V2State1, U} ->
+ #constraint_list{masks = Masks, list = AllCs} = Conj,
+ M = lists:keydelete(I, 1, vars_per_child(U, Masks)),
+ {V2State2, NewF0} = save_updated_vars_list(AllCs, M, V2State1),
+ {NewF, F} = lists:splitwith(fun(J) -> J < I end, NewF0),
+ Is1 = lists:umerge(Is, F),
+ NewFs = [NewF|NewFs0],
+ v2_solve_conj(Is1, Tail, I+1, Map, Conj, IsFlat, V2State2,
+ [U|UL], NewFs, VarsUp, LastMap, LastFlags)
+ end;
+v2_solve_conj([], _Cs, _I, Map, Conj, IsFlat, V2State, UL, NewFs, VarsUp,
+ LastMap, LastFlags) ->
+ U = lists:umerge(UL),
+ case lists:umerge(NewFs) of
+ [] ->
+ ?debug("conjunct finished Id=~w\n", [Conj#constraint_list.id]),
+ {ok, Map, V2State, lists:umerge([U|VarsUp])};
+ NewFlags when NewFlags =:= LastFlags, Map =:= LastMap ->
+ %% A loop was detected! The cause is some bug, possibly in erl_types.
+ %% The evaluation continues, but the results can be wrong.
+ report_detected_loop(Conj),
+ {ok, Map, V2State, lists:umerge([U|VarsUp])};
+ NewFlags ->
+ #constraint_list{type = conj, list = Cs} = Conj,
+ v2_solve_conj(NewFlags, Cs, 1, Map, Conj, IsFlat, V2State,
+ [], [], [U|VarsUp], Map, NewFlags)
+ end;
+v2_solve_conj(Is, [_|Tail], I, Map, Conj, IsFlat, V2State, UL, NewFs, VarsUp,
+ LastMap, LastFlags) ->
+ v2_solve_conj(Is, Tail, I+1, Map, Conj, IsFlat, V2State, UL, NewFs, VarsUp,
+ LastMap, LastFlags).
+
+-ifdef(DEBUG_LOOP_DETECTION).
+report_detected_loop(Conj) ->
+ io:format("A loop was detected in ~w\n", [Conj#constraint_list.id]).
+-else.
+report_detected_loop(_) ->
+ ok.
+-endif.
+
+add_mask_to_flags(Flags, [Im|M], I, L) when I > Im ->
+ add_mask_to_flags(Flags, M, I, [Im|L]);
+add_mask_to_flags(Flags, [_|M], _I, L) ->
+ {lists:umerge(Flags, M), lists:reverse(L)}.
+
+get_mask(V, {d, Masks}) ->
+ case dict:find(V, Masks) of
+ error -> [];
+ {ok, M} -> M
+ end;
+get_mask(V, Masks) ->
+ case lists:keyfind(V, 1, Masks) of
+ false -> [];
+ {V, M} -> M
+ end.
+
+get_flags(#v2_state{constr_data = ConData}=V2State0, C) ->
+ #constraint_list{id = Id, list = Cs, masks = Masks} = C,
+ case dict:find(Id, ConData) of
+ error ->
+ ?debug("get_flags Id=~w Flags=all ~w\n", [Id, length(Cs)]),
+ V2State = V2State0#v2_state{constr_data = dict:store(Id, {[],[]}, ConData)},
+ {V2State, lists:seq(1, length(Cs))};
+ {ok, failed} ->
+ {V2State0, failed_list};
+ {ok, {Part,U}} when U =/= [] ->
+ ?debug("get_flags Id=~w U=~w\n", [Id, U]),
+ V2State = V2State0#v2_state{constr_data = dict:store(Id, {Part,[]}, ConData)},
+ save_updated_vars_list(Cs, vars_per_child(U, Masks), V2State)
+ end.
+
+vars_per_child(U, Masks) ->
+ family([{I, V} || V <- lists:usort(U), I <- get_mask(V, Masks)]).
+
+save_updated_vars_list(Cs, IU, V2State) ->
+ save_updated_vars_list1(Cs, IU, V2State, 1, []).
+
+save_updated_vars_list1([C|Cs], [{I,U}|IU], V2State0, I, Is) ->
+ V2State = save_updated_vars(C, U, V2State0),
+ save_updated_vars_list1(Cs, IU, V2State, I+1, [I|Is]);
+save_updated_vars_list1([], [], V2State, _I, Is) ->
+ {V2State, lists:reverse(Is)};
+save_updated_vars_list1([_|Cs], IU, V2State, I, Is) ->
+ save_updated_vars_list1(Cs, IU, V2State, I+1, Is).
+
+save_updated_vars(#constraint{}, _, V2State) ->
+ V2State;
+save_updated_vars(#constraint_list{}=C, U, V2State0) ->
+ save_updated_vars1(V2State0, C, U);
+save_updated_vars(#constraint_ref{id = Id}, U, V2State) ->
+ Cs = state__get_cs(Id, V2State#v2_state.state),
+ save_updated_vars(Cs, U, V2State).
+
+save_updated_vars1(V2State, C, U) ->
+ #v2_state{constr_data = ConData} = V2State,
+ #constraint_list{id = Id} = C,
+ case dict:find(Id, ConData) of
+ error -> V2State; % error means everything is flagged
+ {ok, failed} -> V2State;
+ {ok, {Part,U0}} ->
+ %% Duplicates are not so common; let masks/2 remove them.
+ U1 = U ++ U0,
+ V2State#v2_state{constr_data = dict:store(Id, {Part,U1}, ConData)}
+ end.
+
+-ifdef(DEBUG).
+pp_constr_data(_Tag, #v2_state{constr_data = D}) ->
+ io:format("Constr data at ~p\n", [_Tag]),
+ _ = [begin
+ case _PartU of
+ {_Part, _U} ->
+ io:format("Id: ~w Vars: ~w\n", [_Id, _U]),
+ [pp_map("Part", dict:from_list(_Part)) || _Part =/= []];
+ failed ->
+ io:format("Id: ~w failed list\n", [_Id])
+ end
+ end ||
+ {_Id, _PartU} <- lists:keysort(1, dict:to_list(D))],
+ ok.
+
+-else.
+pp_constr_data(_Tag, _V2State) ->
+ ok.
+-endif.
+
+failed_list(#constraint_list{id = Id}, #v2_state{constr_data = D}=V2State) ->
+ ?debug("error list ~w~n", [Id]),
+ V2State#v2_state{constr_data = dict:store(Id, failed, D)}.
+
+is_failed_list(#constraint_list{id = Id}, #v2_state{constr_data = D}) ->
+ dict:find(Id, D) =:= {ok, failed}.
+
+%% Solver v1
+
solve_ref_or_list(#constraint_ref{id = Id, deps = Deps},
Map, MapDict, State) ->
{OldLocalMap, Check} =
case dict:find(Id, MapDict) of
- error -> {dict:new(), false};
+ error -> {map_new(), false};
{ok, M} -> {M, true}
end,
?debug("Checking ref to fun: ~w\n", [debug_lookup_name(Id)]),
+ %% Note: mk_constraint_ref() has already removed Id from Deps. The
+ %% reason for doing it there is that it makes it easy for
+ %% calculate_masks() to make the corresponding adjustment for
+ %% version v2.
CheckDeps = ordsets:del_element(t_var_name(Id), Deps),
+ true = CheckDeps =:= Deps,
case Check andalso maps_are_equal(OldLocalMap, Map, CheckDeps) of
true ->
?debug("Equal\n", []),
@@ -1892,6 +2329,7 @@ solve_ref_or_list(#constraint_ref{id = Id, deps = Deps},
FunType0 = lookup_type(Id, NewMap),
{NewMapDict0, FunType0}
end,
+ ?debug(" Id=~w Assigned ~s\n", [Id, format_type(FunType)]),
NewMap1 = enter_type(Id, FunType, Map),
NewMap2 =
case state__get_rec_var(Id, State) of
@@ -1904,7 +2342,7 @@ solve_ref_or_list(#constraint_list{type=Type, list = Cs, deps = Deps, id = Id},
Map, MapDict, State) ->
{OldLocalMap, Check} =
case dict:find(Id, MapDict) of
- error -> {dict:new(), false};
+ error -> {map_new(), false};
{ok, M} -> {M, true}
end,
?debug("Checking ref to list: ~w\n", [Id]),
@@ -1926,7 +2364,7 @@ solve_self_recursive(Cs, Map, MapDict, Id, RecType0, State) ->
{ok, RecVar} = state__get_rec_var(Id, State),
?debug("OldRecType ~s\n", [format_type(RecType0)]),
RecType = t_limit(RecType0, ?TYPE_LIMIT),
- Map1 = enter_type(RecVar, RecType, dict:erase(t_var_name(Id), Map)),
+ Map1 = enter_type(RecVar, RecType, erase_type(t_var_name(Id), Map)),
pp_map("Map1", Map1),
case solve_ref_or_list(Cs, Map1, MapDict, State) of
{error, _} = Error ->
@@ -1994,14 +2432,9 @@ solve_cs([#constraint_list{} = C|Tail], Map, MapDict, State) ->
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(lookup_type(C#constraint.lhs, Map)),
- C#constraint.op,
- format_type(C#constraint.rhs),
- format_type(lookup_type(C#constraint.rhs, Map))]),
+ report_failed_constraint(C, Map),
{error, MapDict};
- {ok, NewMap} ->
+ {ok, {NewMap, _U}} ->
solve_cs(Tail, NewMap, MapDict, State)
end;
solve_cs([], Map, MapDict, _State) ->
@@ -2022,7 +2455,11 @@ solve_one_c(#constraint{lhs = Lhs, rhs = Rhs, op = Op}, Map, Opaques) ->
eq ->
case solve_subtype(Lhs, Inf, Map, Opaques) of
error -> error;
- {ok, Map1} -> solve_subtype(Rhs, Inf, Map1, Opaques)
+ {ok, {Map1, U1}} ->
+ case solve_subtype(Rhs, Inf, Map1, Opaques) of
+ error -> error;
+ {ok, {Map2, U2}} -> {ok, {Map2, lists:umerge(U1, U2)}}
+ end
end
end
end.
@@ -2045,20 +2482,34 @@ solve_subtype(Type, Inf, Map, Opaques) ->
end.
%% end.
+report_failed_constraint(_C, _Map) ->
+ ?debug("+++++++++++\nFailed: ~s :: ~s ~w ~s :: ~s\n+++++++++++\n",
+ [format_type(_C#constraint.lhs),
+ format_type(lookup_type(_C#constraint.lhs, _Map)),
+ _C#constraint.op,
+ format_type(_C#constraint.rhs),
+ format_type(lookup_type(_C#constraint.rhs, _Map))]).
+
%% ============================================================================
%%
%% Maps and types.
%%
%% ============================================================================
+map_new() ->
+ dict:new().
+
join_maps([Map]) ->
Map;
join_maps(Maps) ->
- Keys = lists:foldl(fun(TmpMap, AccKeys) ->
- [Key || Key <- AccKeys, dict:is_key(Key, TmpMap)]
- end,
- dict:fetch_keys(hd(Maps)), tl(Maps)),
- join_maps(Keys, Maps, dict:new()).
+ Keys = constrained_keys(Maps),
+ join_maps(Keys, Maps, map_new()).
+
+constrained_keys(Maps) ->
+ lists:foldl(fun(TmpMap, AccKeys) ->
+ [Key || Key <- AccKeys, dict:is_key(Key, TmpMap)]
+ end,
+ dict:fetch_keys(hd(Maps)), tl(Maps)).
join_maps([Key|Left], Maps = [Map|MapsLeft], AccMap) ->
NewType = join_one_key(Key, MapsLeft, lookup_type(Key, Map)),
@@ -2121,13 +2572,13 @@ enter_type(Key, Val, Map) when is_integer(Key) ->
?debug("Entering ~s :: ~s\n", [format_type(t_var(Key)), format_type(Val)]),
case t_is_any(Val) of
true ->
- dict:erase(Key, Map);
+ erase_type(Key, Map);
false ->
LimitedVal = t_limit(Val, ?INTERNAL_TYPE_LIMIT),
case dict:find(Key, Map) of
{ok, LimitedVal} -> Map;
- {ok, _} -> dict:store(Key, LimitedVal, Map);
- error -> dict:store(Key, LimitedVal, Map)
+ {ok, _} -> map_store(Key, LimitedVal, Map);
+ error -> map_store(Key, LimitedVal, Map)
end
end;
enter_type(Key, Val, Map) ->
@@ -2135,13 +2586,13 @@ enter_type(Key, Val, Map) ->
KeyName = t_var_name(Key),
case t_is_any(Val) of
true ->
- dict:erase(KeyName, Map);
+ erase_type(KeyName, Map);
false ->
LimitedVal = t_limit(Val, ?INTERNAL_TYPE_LIMIT),
case dict:find(KeyName, Map) of
{ok, LimitedVal} -> Map;
- {ok, _} -> dict:store(KeyName, LimitedVal, Map);
- error -> dict:store(KeyName, LimitedVal, Map)
+ {ok, _} -> map_store(KeyName, LimitedVal, Map);
+ error -> map_store(KeyName, LimitedVal, Map)
end
end.
@@ -2151,11 +2602,25 @@ enter_type_lists([Key|KeyTail], [Val|ValTail], Map) ->
enter_type_lists([], [], Map) ->
Map.
-enter_type_list([{Key, Val}|Tail], Map) ->
+enter_type_list(KeyVals, Map) ->
+ enter_type_list(KeyVals, Map, []).
+
+enter_type_list([{Key, Val}|Tail], Map, U0) ->
+ {Map1,U1} = enter_type2(Key, Val, Map),
+ enter_type_list(Tail, Map1, U1++U0);
+enter_type_list([], Map, U) ->
+ {Map, ordsets:from_list(U)}.
+
+enter_type2(Key, Val, Map) ->
Map1 = enter_type(Key, Val, Map),
- enter_type_list(Tail, Map1);
-enter_type_list([], Map) ->
- Map.
+ {Map1, [Key || not is_same(Key, Map, Map1)]}.
+
+map_store(Key, Val, Map) ->
+ ?debug("Storing ~w :: ~s\n", [Key, format_type(Val)]),
+ dict:store(Key, Val, Map).
+
+erase_type(Key, Map) ->
+ dict:erase(Key, Map).
lookup_type_list(List, Map) ->
[lookup_type(X, Map) || X <- List].
@@ -2206,19 +2671,24 @@ mk_var_no_lit(Var) ->
mk_var_no_lit_list(List) ->
[mk_var_no_lit(X) || X <- List].
+updated_vars_only(U, OldMap, NewMap) ->
+ [V || V <- U, not is_same(V, OldMap, NewMap)].
+
+is_same(Key, Map1, Map2) ->
+ t_is_equal(lookup_type(Key, Map1), lookup_type(Key, Map2)).
+
pp_map(_S, _Map) ->
?debug("\t~s: ~p\n",
[_S, [{X, lists:flatten(format_type(Y))} ||
{X, Y} <- lists:keysort(1, dict:to_list(_Map))]]).
-
%% ============================================================================
%%
%% The State.
%%
%% ============================================================================
-new_state(SCC0, NextLabel, CallGraph, Plt, PropTypes) ->
+new_state(SCC0, NextLabel, CallGraph, Plt, PropTypes, Solvers) ->
List = [{MFA, Var} || {MFA, {Var, _Fun}, _Rec} <- SCC0],
NameMap = dict:from_list(List),
MFAs = [MFA || {MFA, _Var} <- List],
@@ -2235,7 +2705,7 @@ new_state(SCC0, NextLabel, CallGraph, Plt, PropTypes) ->
end,
#state{callgraph = CallGraph, name_map = NameMap, next_label = NextLabel,
prop_types = {d, PropTypes}, plt = Plt, scc = ordsets:from_list(SCC),
- mfas = MFAs, self_rec = SelfRec}.
+ mfas = MFAs, self_rec = SelfRec, solvers = Solvers}.
state__set_rec_dict(State, RecDict) ->
State#state{records = RecDict}.
@@ -2458,7 +2928,7 @@ mk_constraint(Lhs, Op, Rhs) ->
case Deps =:= [] of
true ->
%% This constraint is constant. Solve it immediately.
- case solve_one_c(C, dict:new(), []) of
+ case solve_one_c(C, map_new(), []) of
error -> throw(error);
_ ->
%% This is always true, keep it anyway for logistic reasons
@@ -2481,8 +2951,9 @@ constraint_opnd_is_any(Type) -> t_is_any(Type).
-ifdef(DEBUG).
--spec mk_fun_var(fun((_) -> erl_types:erl_type()), [erl_types:erl_type()],
- integer()) -> #fun_var{}.
+-spec mk_fun_var(integer(),
+ fun((_) -> erl_types:erl_type()),
+ [erl_types:erl_type()]) -> #fun_var{}.
mk_fun_var(Line, Fun, Types) ->
Deps = [t_var_name(Var) || Var <- t_collect_vars(t_product(Types))],
@@ -2530,7 +3001,9 @@ mk_constraints([], _Op, []) ->
[].
mk_constraint_ref(Id, Deps) ->
- #constraint_ref{id = Id, deps = Deps}.
+ %% See also solve_ref_or_list(), #constraint_ref{}.
+ Ds = ordsets:del_element(t_var_name(Id), Deps),
+ #constraint_ref{id = Id, deps = Ds}.
mk_constraint_list(Type, List) ->
List1 = ordsets:from_list(lift_lists(Type, List)),
@@ -2680,7 +3153,7 @@ enumerate_constraints([#constraint_list{type = conj, list = List} = C|Tail],
NewDeep =:= [] -> {NewFlat, N2};
true ->
TmpCList = mk_conj_constraint_list(NewFlat),
- {[TmpCList#constraint_list{id = {list, N2}} | NewDeep],
+ {[TmpCList#constraint_list{id = {list, N2}}| NewDeep],
N2 + 1}
end,
NewAcc = [C#constraint_list{list = NewList, id = {list, N3}}|Acc],
@@ -2725,7 +3198,9 @@ order_fun_constraints([#constraint_list{list = List, type = Type} = C|Tail],
end,
lists:mapfoldl(FoldFun, State, List)
end,
- NewAcc = [update_constraint_list(C, NewList)|Acc],
+ C1 = update_constraint_list(C, NewList),
+ Masks = calculate_masks(NewList, 1, []),
+ NewAcc = [update_masks(C1, Masks)|Acc],
order_fun_constraints(Tail, Funs, NewAcc, NewState);
order_fun_constraints([#constraint{} = C|Tail], Funs, Acc, State) ->
order_fun_constraints(Tail, Funs, [C|Acc], State);
@@ -2733,6 +3208,22 @@ order_fun_constraints([], Funs, Acc, State) ->
NewState = order_fun_constraints(Funs, State),
{lists:reverse(Acc)++Funs, NewState}.
+update_masks(C, Masks) ->
+ C#constraint_list{masks = Masks}.
+
+-define(VARS_LIMIT, 50).
+
+calculate_masks([C|Cs], I, L0) ->
+ calculate_masks(Cs, I+1, [{V, I} || V <- get_deps(C)] ++ L0);
+calculate_masks([], _I, L) ->
+ M = family(L),
+ case length(M) > ?VARS_LIMIT of
+ true ->
+ {d, dict:from_list(M)};
+ false ->
+ M
+ end.
+
%% ============================================================================
%%
%% Utilities.
@@ -2810,6 +3301,9 @@ lookup_record(Records, Tag, Arity) ->
error
end.
+family(L) ->
+ sofs:to_external(sofs:rel2fam(sofs:relation(L))).
+
%% ============================================================================
%%
%% Pretty printer and debug facilities.
@@ -2834,8 +3328,8 @@ format_type(Type) ->
join_chars([], _Sep) ->
[];
-join_chars([H | T], Sep) ->
- [H | [[Sep,X] || X <- T]].
+join_chars([H|T], Sep) ->
+ [H|[[Sep,X] || X <- T]].
debug_lookup_name(Var) ->
case dict:find(t_var_name(Var), get(dialyzer_typesig_map)) of
diff --git a/lib/dialyzer/src/dialyzer_utils.erl b/lib/dialyzer/src/dialyzer_utils.erl
index 149e777e1f..8046b48838 100644
--- a/lib/dialyzer/src/dialyzer_utils.erl
+++ b/lib/dialyzer/src/dialyzer_utils.erl
@@ -2,7 +2,7 @@
%%-----------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/dialyzer/src/dialyzer_worker.erl b/lib/dialyzer/src/dialyzer_worker.erl
index 50b2e31ed8..1338997899 100644
--- a/lib/dialyzer/src/dialyzer_worker.erl
+++ b/lib/dialyzer/src/dialyzer_worker.erl
@@ -2,7 +2,7 @@
%%-----------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/dialyzer/test/options1_SUITE_data/src/compiler/core_scan.erl b/lib/dialyzer/test/options1_SUITE_data/src/compiler/core_scan.erl
index a97270b9f3..f4e609bf5b 100644
--- a/lib/dialyzer/test/options1_SUITE_data/src/compiler/core_scan.erl
+++ b/lib/dialyzer/test/options1_SUITE_data/src/compiler/core_scan.erl
@@ -1,3 +1,4 @@
+%% -*- coding: utf-8 -*-
%% ``The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
@@ -30,16 +31,16 @@
%% 173 - 176 { - ~ punctuation
%% 177 DEL control
%% 200 - 237 control
-%% 240 - 277 NBSP - � punctuation
-%% 300 - 326 � - � uppercase
-%% 327 � punctuation
-%% 330 - 336 � - � uppercase
-%% 337 - 366 � - � lowercase
-%% 367 � punctuation
-%% 370 - 377 � - � lowercase
+%% 240 - 277 NBSP - ¿ punctuation
+%% 300 - 326 À - Ö uppercase
+%% 327 × punctuation
+%% 330 - 336 Ø - Þ uppercase
+%% 337 - 366 ß - ö lowercase
+%% 367 ÷ punctuation
+%% 370 - 377 ø - ÿ lowercase
%%
%% Many punctuation characters region have special meaning. Must
-%% watch using � \327, bvery close to x \170
+%% watch using × \327, bvery close to x \170
-module(core_scan).
@@ -266,11 +267,11 @@ 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 /= $� ->
+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 /= $� ->
+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);
@@ -335,9 +336,9 @@ 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 >= $ß, C =< $ÿ, C /= $÷ -> true;
name_char(C) when C >= $A, C =< $Z -> true;
-name_char(C) when C >= $�, C =< $�, C /= $� -> true;
+name_char(C) when C >= $À, C =< $Þ, C /= $× -> true;
name_char(C) when C >= $0, C =< $9 -> true;
name_char($_) -> true;
name_char($@) -> true;
diff --git a/lib/dialyzer/test/options1_SUITE_data/src/compiler/sys_pre_expand.erl b/lib/dialyzer/test/options1_SUITE_data/src/compiler/sys_pre_expand.erl
index 08bc6cb147..41b7cb248d 100644
--- a/lib/dialyzer/test/options1_SUITE_data/src/compiler/sys_pre_expand.erl
+++ b/lib/dialyzer/test/options1_SUITE_data/src/compiler/sys_pre_expand.erl
@@ -149,14 +149,12 @@ forms([], St) -> {[],St}.
%% 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),
+ true = is_atom(Module),
+ St#expand{module=Module,
parameters=As};
attribute(module, Module, St) ->
- M = package_to_string(Module),
- St#expand{module=list_to_atom(M),
- package = packages:strip_last(M)};
+ true = is_atom(Module),
+ St#expand{module=Module};
attribute(export, Es, St) ->
St#expand{exports=union(from_list(Es), St#expand.exports)};
attribute(import, Is, St) ->
@@ -226,8 +224,6 @@ pattern({tuple,Line,Ps}, St0) ->
%%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) ->
@@ -401,8 +397,6 @@ expr({tuple,Line,Es0}, Vs, St0) ->
%%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);
@@ -483,10 +477,7 @@ expr({call,Line,{atom,La,N},As0}, Vs, St0) ->
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),
+expr({call,Line,{remote,Lr,M1,F},As0}, Vs, 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) ->
@@ -922,32 +913,6 @@ 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.
@@ -1005,15 +970,10 @@ new_in_all(Before, Region) ->
%% 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)),
+import({Mod,Fs}, St) ->
+ true = is_atom(Mod),
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)}.
+ St#expand{imports=add_imports(Mod, Mfs, St#expand.imports)}.
add_imports(Mod, [F|Fs], Is) ->
add_imports(Mod, Fs, orddict:store(F, Mod, Is));
diff --git a/lib/dialyzer/test/r9c_SUITE_data/results/asn1 b/lib/dialyzer/test/r9c_SUITE_data/results/asn1
index 292275dd6e..c11105b76d 100644
--- a/lib/dialyzer/test/r9c_SUITE_data/results/asn1
+++ b/lib/dialyzer/test/r9c_SUITE_data/results/asn1
@@ -68,7 +68,6 @@ asn1rt_check.erl:100: The variable _ can never match since previous clauses comp
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())
@@ -76,7 +75,6 @@ asn1rt_per.erl:1237: The call erlang:'not'('implemented') will never return sinc
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()
@@ -94,7 +92,6 @@ asn1rt_per_bin.erl:487: The variable _ can never match since previous clauses co
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()
@@ -103,4 +100,3 @@ asn1rt_per_bin_rt2ct.erl:484: The variable _ can never match since previous clau
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_SUITE_data/results/mnesia b/lib/dialyzer/test/r9c_SUITE_data/results/mnesia
index b397d37523..1aac46f5b2 100644
--- a/lib/dialyzer/test/r9c_SUITE_data/results/mnesia
+++ b/lib/dialyzer/test/r9c_SUITE_data/results/mnesia
@@ -1,13 +1,12 @@
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_backup.erl:49: Callback info about the mnesia_backup behaviour is not available
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_checkpoint.erl:894: The call sys:handle_system_msg(Msg::any(),From::any(),'no_parent','mnesia_checkpoint',[],Cp::#checkpoint_args{}) breaks the contract (Msg,From,Parent,Module,Debug,Misc) -> Void when is_subtype(Msg,term()), is_subtype(From,{pid(),Tag::_}), is_subtype(Parent,pid()), is_subtype(Module,module()), is_subtype(Debug,[dbg_opt()]), is_subtype(Misc,term()), is_subtype(Void,term())
+mnesia_checkpoint.erl:894: The call sys:handle_system_msg(Msg::any(),From::any(),'no_parent','mnesia_checkpoint',[],Cp::#checkpoint_args{}) breaks the contract (Msg,From,Parent,Module,Debug,Misc) -> no_return() when is_subtype(Msg,term()), is_subtype(From,{pid(),Tag::_}), is_subtype(Parent,pid()), is_subtype(Module,module()), is_subtype(Debug,[dbg_opt()]), is_subtype(Misc,term())
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',_,_}
diff --git a/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_check.erl b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_check.erl
index 2f0ada122e..4335d8efae 100644
--- a/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_check.erl
+++ b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_check.erl
@@ -4599,7 +4599,7 @@ get_simple_table_info1(S,#'ComponentType'{typespec=TS},[],Path) ->
%% o.w. the asn1 code is wrong.
#type{def=OCFT,constraint=Cnstr} = TS,
case Cnstr of
- [{simpletable,_OSRef}]�->
+ [{simpletable,_OSRef}] ->
#'ObjectClassFieldType'{classname=ClRef,
class=ObjectClass,
fieldname=FieldName} = OCFT,
diff --git a/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_gen.erl b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_gen.erl
index 5d2f7a13bd..5b33be9eff 100644
--- a/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_gen.erl
+++ b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_gen.erl
@@ -148,7 +148,7 @@ 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)]),
+% io:format("Partial incomplete decode gen not started: ~w~n",[asn1ct:get_gen_state_field(active)]),
ok;
% [] ->
% ok;
@@ -295,7 +295,7 @@ gen_part_decode_funcs([_H|T],N) ->
gen_part_decode_funcs([],N) ->
if
N > 0 ->
- .emit([".",nl]);
+ emit([".",nl]);
true ->
ok
end.
diff --git a/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1rt_ber_bin.erl b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1rt_ber_bin.erl
index 6064515a7e..b0786200fc 100644
--- a/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1rt_ber_bin.erl
+++ b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1rt_ber_bin.erl
@@ -276,7 +276,7 @@ encode_tag_val({Class, Form, TagNo}) ->
<<(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??!!
+encode_tag_val(Tag) when (Tag =< 255) -> Tag; %% is this function used??!!
%% asumes correct bitpattern of 0-5
encode_tag_val(Tag) -> encode_tag_val2(Tag,[]).
diff --git a/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1rt_ber_bin_v2.erl b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1rt_ber_bin_v2.erl
index 50a91cf201..2f25e35cd3 100644
--- a/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1rt_ber_bin_v2.erl
+++ b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1rt_ber_bin_v2.erl
@@ -531,7 +531,7 @@ encode_tag_val({Class, Form, TagNo}) ->
<<(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??!!
+encode_tag_val(Tag) when (Tag =< 255) -> Tag; %% is this function used??!!
%% asumes correct bitpattern of 0-5
encode_tag_val(Tag) -> encode_tag_val2(Tag,[]).
diff --git a/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1rt_per_bin_rt2ct.erl b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1rt_per_bin_rt2ct.erl
index 9f02ad4466..4781c81955 100644
--- a/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1rt_per_bin_rt2ct.erl
+++ b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1rt_per_bin_rt2ct.erl
@@ -1198,7 +1198,7 @@ encode_bin_bit_string(C,UnusedAndBin={_,_},NamedBitList) ->
% case get_constraint(C,'SizeConstraint') of
% 0 ->
-% []; % borde avg�ras i compile-time
+% []; % should be dont in compile time
% V when integer(V),V=<16 ->
% {Unused2,Bin2} = pad_list(V,UnusedAndBin1),
% <<BitVal:V,_:Unused2>> = Bin2,
diff --git a/lib/dialyzer/test/r9c_SUITE_data/src/mnesia/mnesia.erl b/lib/dialyzer/test/r9c_SUITE_data/src/mnesia/mnesia.erl
index b4f03fab03..17cc9a953d 100644
--- a/lib/dialyzer/test/r9c_SUITE_data/src/mnesia/mnesia.erl
+++ b/lib/dialyzer/test/r9c_SUITE_data/src/mnesia/mnesia.erl
@@ -1806,7 +1806,6 @@ system_info2(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();
@@ -1840,7 +1839,6 @@ system_info_items(yes) ->
dump_log_time_threshold,
dump_log_update_in_place,
dump_log_write_threshold,
- embedded_mnemosyne,
event_module,
extra_db_nodes,
fallback_activated,
diff --git a/lib/dialyzer/test/r9c_SUITE_data/src/mnesia/mnesia_monitor.erl b/lib/dialyzer/test/r9c_SUITE_data/src/mnesia/mnesia_monitor.erl
index b64419d5a8..a1c25b5120 100644
--- a/lib/dialyzer/test/r9c_SUITE_data/src/mnesia/mnesia_monitor.erl
+++ b/lib/dialyzer/test/r9c_SUITE_data/src/mnesia/mnesia_monitor.erl
@@ -631,7 +631,6 @@ env() ->
dump_log_time_threshold,
dump_log_update_in_place,
dump_log_write_threshold,
- embedded_mnemosyne,
event_module,
extra_db_nodes,
ignore_fallback_at_startup,
@@ -660,8 +659,6 @@ 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) ->
@@ -703,7 +700,6 @@ 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
diff --git a/lib/dialyzer/test/r9c_SUITE_data/src/mnesia/mnesia_sup.erl b/lib/dialyzer/test/r9c_SUITE_data/src/mnesia/mnesia_sup.erl
index 78609ffdde..970e0e5f47 100644
--- a/lib/dialyzer/test/r9c_SUITE_data/src/mnesia/mnesia_sup.erl
+++ b/lib/dialyzer/test/r9c_SUITE_data/src/mnesia/mnesia_sup.erl
@@ -57,9 +57,8 @@ init() ->
Event = event_procs(),
Kernel = kernel_procs(),
- Mnemosyne = mnemosyne_procs(),
- {ok, {Flags, Event ++ Kernel ++ Mnemosyne}}.
+ {ok, {Flags, Event ++ Kernel}}.
event_procs() ->
KillAfter = timer:seconds(30),
@@ -72,16 +71,6 @@ kernel_procs() ->
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
@@ -107,11 +96,8 @@ add_event_handler() ->
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
@@ -128,9 +114,3 @@ ensure_dead(Name) ->
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/race_SUITE_data/results/ets_insert_args10 b/lib/dialyzer/test/race_SUITE_data/results/ets_insert_args10
new file mode 100644
index 0000000000..c3c9b12bdd
--- /dev/null
+++ b/lib/dialyzer/test/race_SUITE_data/results/ets_insert_args10
@@ -0,0 +1,2 @@
+
+ets_insert_args10.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_args10.erl on line 8
diff --git a/lib/dialyzer/test/race_SUITE_data/src/ets_insert_args10.erl b/lib/dialyzer/test/race_SUITE_data/src/ets_insert_args10.erl
new file mode 100644
index 0000000000..c897a34af0
--- /dev/null
+++ b/lib/dialyzer/test/race_SUITE_data/src/ets_insert_args10.erl
@@ -0,0 +1,19 @@
+%% 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_args10).
+-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])]),
+ A = {counter, 0},
+ B = [],
+ ets:insert(foo, [A|B]),
+ 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/small_SUITE_data/results/contracts_with_subtypes b/lib/dialyzer/test/small_SUITE_data/results/contracts_with_subtypes
index 8dc0361b0d..4850f3ff0c 100644
--- a/lib/dialyzer/test/small_SUITE_data/results/contracts_with_subtypes
+++ b/lib/dialyzer/test/small_SUITE_data/results/contracts_with_subtypes
@@ -6,7 +6,7 @@ contracts_with_subtypes.erl:109: The call contracts_with_subtypes:rec_arg({'b',{
contracts_with_subtypes.erl:110: The call contracts_with_subtypes:rec_arg({'a',{'b',{'a','b'}}}) breaks the contract (Arg) -> 'ok' when is_subtype(Arg,{'a',A} | {'b',B}), is_subtype(A,'a' | {'b',B}), is_subtype(B,'b' | {'a',A})
contracts_with_subtypes.erl:111: The call contracts_with_subtypes:rec_arg({'b',{'a',{'b','a'}}}) breaks the contract (Arg) -> 'ok' when is_subtype(Arg,{'a',A} | {'b',B}), is_subtype(A,'a' | {'b',B}), is_subtype(B,'b' | {'a',A})
contracts_with_subtypes.erl:142: The pattern 1 can never match the type binary() | string()
-contracts_with_subtypes.erl:145: The pattern 'alpha' can never match the type {'ok',X} | {'ok',X,binary() | string()}
+contracts_with_subtypes.erl:145: The pattern 'alpha' can never match the type {'ok',_} | {'ok',_,binary() | string()}
contracts_with_subtypes.erl:147: The pattern 42 can never match the type {'ok',_} | {'ok',_,binary() | string()}
contracts_with_subtypes.erl:163: The pattern 'alpha' can never match the type {'ok',X}
contracts_with_subtypes.erl:165: The pattern 42 can never match the type {'ok',X}
diff --git a/lib/dialyzer/test/small_SUITE_data/results/empty_list_infimum b/lib/dialyzer/test/small_SUITE_data/results/empty_list_infimum
new file mode 100644
index 0000000000..6eaf60b91d
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/results/empty_list_infimum
@@ -0,0 +1,2 @@
+
+empty_list_infimum.erl:38: Invalid type specification for function empty_list_infimum:list_vhost_permissions/1. The success typing is (_) -> [[{_,_}]] \ No newline at end of file
diff --git a/lib/dialyzer/test/small_SUITE_data/results/fun_ref_match b/lib/dialyzer/test/small_SUITE_data/results/fun_ref_match
index 60b34530b4..e69de29bb2 100644
--- a/lib/dialyzer/test/small_SUITE_data/results/fun_ref_match
+++ b/lib/dialyzer/test/small_SUITE_data/results/fun_ref_match
@@ -1,2 +0,0 @@
-
-fun_ref_match.erl:14: Function will never be called
diff --git a/lib/dialyzer/test/small_SUITE_data/results/port_info_test b/lib/dialyzer/test/small_SUITE_data/results/port_info_test
index 863a3d61df..53d20a415b 100644
--- a/lib/dialyzer/test/small_SUITE_data/results/port_info_test
+++ b/lib/dialyzer/test/small_SUITE_data/results/port_info_test
@@ -1,7 +1,7 @@
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: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' | 'os_pid' | 'output' | 'registered_name',atom() | pid() | [pid() | char()] | integer()}
+port_info_test.erl:28: The pattern {'gazonk', _} can never match the type 'undefined' | [] | {'connected' | 'id' | 'input' | 'links' | 'locking' | 'memory' | 'monitors' | 'name' | 'os_pid' | 'output' | 'parallelism' | 'queue_size' | 'registered_name',atom() | pid() | [pid() | char() | {'process',pid()}] | non_neg_integer()}
port_info_test.erl:32: The pattern {'os_pid', "42"} can never match the type 'undefined' | {'os_pid','undefined' | non_neg_integer()}
diff --git a/lib/dialyzer/test/small_SUITE_data/results/record_creation_diffs b/lib/dialyzer/test/small_SUITE_data/results/record_creation_diffs
new file mode 100644
index 0000000000..f00c4b10ff
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/results/record_creation_diffs
@@ -0,0 +1,3 @@
+
+record_creation_diffs.erl:10: Function foo/1 has no local return
+record_creation_diffs.erl:11: Record construction #bar{some_list::{'this','is','a','tuple'}} violates the declared type of field some_list::'undefined' | [any()]
diff --git a/lib/dialyzer/test/small_SUITE_data/src/comm_layer/comm_connection.erl b/lib/dialyzer/test/small_SUITE_data/src/comm_layer/comm_connection.erl
index 48cc50ae21..5a8f9710d6 100644
--- a/lib/dialyzer/test/small_SUITE_data/src/comm_layer/comm_connection.erl
+++ b/lib/dialyzer/test/small_SUITE_data/src/comm_layer/comm_connection.erl
@@ -1,4 +1,5 @@
-% Copyright 2008 Konrad-Zuse-Zentrum f�r Informationstechnik Berlin
+%% -*- coding: utf-8 -*-
+% 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.
@@ -21,7 +22,7 @@
%%% Created : 18 Apr 2008 by Thorsten Schuett <[email protected]>
%%%-------------------------------------------------------------------
%% @author Thorsten Schuett <[email protected]>
-%% @copyright 2008 Konrad-Zuse-Zentrum f�r Informationstechnik Berlin
+%% @copyright 2008 Konrad-Zuse-Zentrum für Informationstechnik Berlin
%% @version $Id $
-module(comm_layer_dir.comm_connection).
diff --git a/lib/dialyzer/test/small_SUITE_data/src/comm_layer/comm_port.erl b/lib/dialyzer/test/small_SUITE_data/src/comm_layer/comm_port.erl
index e8169b4673..d9fcb5e625 100644
--- a/lib/dialyzer/test/small_SUITE_data/src/comm_layer/comm_port.erl
+++ b/lib/dialyzer/test/small_SUITE_data/src/comm_layer/comm_port.erl
@@ -1,4 +1,5 @@
-% Copyright 2008 Konrad-Zuse-Zentrum f�r Informationstechnik Berlin
+%% -*- coding: utf-8 -*-
+% 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.
@@ -20,7 +21,7 @@
%%% Created : 18 Apr 2008 by Thorsten Schuett <[email protected]>
%%%-------------------------------------------------------------------
%% @author Thorsten Schuett <[email protected]>
-%% @copyright 2008 Konrad-Zuse-Zentrum f�r Informationstechnik Berlin
+%% @copyright 2008 Konrad-Zuse-Zentrum für Informationstechnik Berlin
%% @version $Id $
-module(comm_layer_dir.comm_port).
diff --git a/lib/dialyzer/test/small_SUITE_data/src/empty_list_infimum.erl b/lib/dialyzer/test/small_SUITE_data/src/empty_list_infimum.erl
new file mode 100644
index 0000000000..b58fa732cb
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/src/empty_list_infimum.erl
@@ -0,0 +1,57 @@
+%%
+%% The Original Code is RabbitMQ.
+%%
+%% The Initial Developer of the Original Code is VMware, Inc.
+%%
+
+-module(empty_list_infimum).
+
+-record(permission, {configure, write, read}).
+-record(user_vhost, {username, virtual_host}).
+-record(user_permission, {user_vhost, permission}).
+
+%%----------------------------------------------------------------------------
+
+-export([i_delete/1]).
+
+-type(vhost() :: binary()).
+
+-type(info_key() :: atom()).
+-type(info_keys() :: [info_key()]).
+
+-type(info() :: {info_key(), any()}).
+-type(infos() :: [info()]).
+
+%%----------------------------------------------------------------------------
+
+-spec i_delete(vhost()) -> 'ok'.
+
+i_delete(VHostPath) ->
+ [ok || _ <- list_vhost_permissions(VHostPath)],
+ ok.
+
+%%----------------------------------------------------------------------------
+
+vhost_perms_info_keys() ->
+ [user, configure, write, read].
+
+-spec list_vhost_permissions(vhost()) -> infos().
+
+list_vhost_permissions(VHostPath) ->
+ list_permissions(vhost_perms_info_keys(), rabbit_foo:some_list()).
+
+filter_props(Keys, Props) ->
+ [T || T = {K, _} <- Props, lists:member(K, Keys)].
+
+list_permissions(Keys, SomeList) ->
+ [filter_props(Keys, [{user, Username},
+ {vhost, VHostPath},
+ {configure, ConfigurePerm},
+ {write, WritePerm},
+ {read, ReadPerm}]) ||
+ #user_permission{user_vhost = #user_vhost{username = Username,
+ virtual_host = VHostPath},
+ permission = #permission{configure = ConfigurePerm,
+ write = WritePerm,
+ read = ReadPerm}} <-
+ SomeList].
diff --git a/lib/dialyzer/test/small_SUITE_data/src/record_creation_diffs.erl b/lib/dialyzer/test/small_SUITE_data/src/record_creation_diffs.erl
new file mode 100644
index 0000000000..e813459f8e
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/src/record_creation_diffs.erl
@@ -0,0 +1,11 @@
+-module(record_creation_diffs).
+
+-export([foo/1]).
+
+-record(bar, {
+ some_atom :: atom(),
+ some_list :: list()
+ }).
+
+foo(Input) ->
+ #bar{some_atom = Input, some_list = {this,is,a,tuple}}.
diff --git a/lib/dialyzer/test/small_SUITE_data/src/remote_tuple_set.erl b/lib/dialyzer/test/small_SUITE_data/src/remote_tuple_set.erl
new file mode 100644
index 0000000000..6c440ed04c
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/src/remote_tuple_set.erl
@@ -0,0 +1,8 @@
+-module(remote_tuple_set).
+
+-export([parse_cidr/0]).
+
+-spec parse_cidr() -> {inet:address_family(),1,2} | {error}.
+
+parse_cidr() ->
+ {inet,1,2}.
diff --git a/lib/dialyzer/test/small_SUITE_data/src/unknown_arity_function_spec.erl b/lib/dialyzer/test/small_SUITE_data/src/unknown_arity_function_spec.erl
new file mode 100644
index 0000000000..c7d7459614
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/src/unknown_arity_function_spec.erl
@@ -0,0 +1,10 @@
+-module(unknown_arity_function_spec).
+
+-export([test/2]).
+
+%-type t() :: 42 | fun((...) -> t()).
+%-type f() :: fun((...) -> 42).
+
+-spec test(fun((...) -> 42), list()) -> 42.
+test(F, L) ->
+ 42 = apply(F, L).
diff --git a/lib/dialyzer/vsn.mk b/lib/dialyzer/vsn.mk
index edafcc4afb..f344fe0604 100644
--- a/lib/dialyzer/vsn.mk
+++ b/lib/dialyzer/vsn.mk
@@ -1 +1 @@
-DIALYZER_VSN = 2.5.1
+DIALYZER_VSN = 2.5.3
diff --git a/lib/diameter/.gitignore b/lib/diameter/.gitignore
index 5afcbedc23..8b13789179 100644
--- a/lib/diameter/.gitignore
+++ b/lib/diameter/.gitignore
@@ -1,58 +1 @@
-# Match at any level.
-*~
-autom4te.cache
-
-# Compiler derivatives
-#
-# Do not use too creative wildcards.
-# Those might ignore files that should not be ignored.
-
-i686-pc-linux-gnu
-x86_64-unknown-linux-gnu
-i386-apple-darwin[0-9]*.[0-9]*.[0-9]*
-sparc-sun-solaris[0-9]*.[0-9]*
-i386-pc-solaris[0-9]*.[0-9]*
-i386-unknown-freebsd[0-9]*.[0-9]*
-tile-tilera-linux-gnu
-powerpc-unknown-linux-gnu
-
-# Mac OS X
-a.out.dSYM/
-
-# Anchored from $DIAMETER_TOP
-/config.log
-/config.status
-
-/Makefile
-/configure
-
-
-# General patterns for applications in lib.
-#
-# Assume that all test/Emakefiles are generated.
-#
-# Any application with a checked-in test/Emakefile should
-# use a negative pattern in its own .gitignore.
-
-#
-# Files generated by configure.
-#
-
-/configure
-/config.log
-/config.status
-
-
-#
-# Generated documentation. (ie. not doc/src)
-#
-
-/doc/[^s]*
-
-
-#
-# Files generated when building/running tests
-#
-
-/test/*.log
diff --git a/lib/diameter/Makefile b/lib/diameter/Makefile
new file mode 100644
index 0000000000..9961d627cf
--- /dev/null
+++ b/lib/diameter/Makefile
@@ -0,0 +1,32 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2010-2012. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, 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
+include subdirs.mk
+
+SUB_DIRECTORIES = $(SUB_DIRS) doc/src
+SPECIAL_TARGETS =
+
+include $(ERL_TOP)/make/otp_subdir.mk
+
+info:
+ @echo "APP_VSN = $(APP_VSN)"
+
+.PHONY: info
diff --git a/lib/diameter/Makefile.in b/lib/diameter/Makefile.in
deleted file mode 100644
index cf38c26045..0000000000
--- a/lib/diameter/Makefile.in
+++ /dev/null
@@ -1,88 +0,0 @@
-#
-# %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%
-
-ifneq ($(ERL_TOP),)
-include $(ERL_TOP)/make/target.mk
-include $(ERL_TOP)/make/$(TARGET)/otp.mk
-else
-include $(DIAMETER_TOP)/make/target.mk
-include $(DIAMETER_TOP)/make/$(TARGET)/rules.mk
-endif
-
-# ----------------------------------------------------
-# Application version
-# ----------------------------------------------------
-include vsn.mk
-VSN=$(DIAMETER_VSN)
-
-DIAMETER_TOP = @DIAMETER_TOP@
-
-
-# ----------------------------------------------------
-# Common Macros
-# ----------------------------------------------------
-
-include subdirs.mk
-
-SUB_DIRECTORIES = $(SUB_DIRS) doc/src
-
-SPECIAL_TARGETS =
-
-ifneq ($(ERL_TOP),)
-ifneq ($(PREFIX),)
-CONFIGURE_OPTS += --prefix=$(PREFIX)
-endif
-endif
-
-
-# ----------------------------------------------------
-# Default Subdir Targets
-# ----------------------------------------------------
-ifneq ($(ERL_TOP),)
-include $(ERL_TOP)/make/otp_subdir.mk
-else
-include $(DIAMETER_TOP)/make/subdir.mk
-endif
-
-.PHONY: reconf conf info version dialyzer
-
-reconf:
- autoconf
-
-conf: do_configure
-
-do_configure: configure
- ./configure $(CONFIGURE_OPTS)
-
-configure: configure.in
- autoconf
-
-info:
- @echo "APP_VSN: $(APP_VSN)"
- @echo "DIAMETER_VSN: $(DIAMETER_VSN)"
-
-version:
- @echo "$(VSN)"
-
-
-dialyzer:
- (cd ./ebin; \
- dialyzer --build_plt \
- --output_plt ../priv/diameter.plt \
- -r ../../diameter/ebin \
- --verbose)
diff --git a/lib/diameter/aclocal.m4 b/lib/diameter/aclocal.m4
deleted file mode 100644
index 2abb47dba2..0000000000
--- a/lib/diameter/aclocal.m4
+++ /dev/null
@@ -1,65 +0,0 @@
-dnl
-dnl %CopyrightBegin%
-dnl
-dnl Copyright Ericsson AB 1998-2011. All Rights Reserved.
-dnl
-dnl The contents of this file are subject to the Erlang Public License,
-dnl Version 1.1, (the "License"); you may not use this file except in
-dnl compliance with the License. You should have received a copy of the
-dnl Erlang Public License along with this software. If not, it can be
-dnl retrieved online at http://www.erlang.org/.
-dnl
-dnl Software distributed under the License is distributed on an "AS IS"
-dnl basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-dnl the License for the specific language governing rights and limitations
-dnl under the License.
-dnl
-dnl %CopyrightEnd%
-dnl
-
-dnl
-dnl aclocal.m4
-dnl
-dnl Local macros used in configure.in. The Local Macros which
-dnl could/should be part of autoconf are prefixed LM_, macros specific
-dnl to the Erlang system are prefixed ERL_.
-dnl
-
-dnl ----------------------------------------------------------------------
-dnl
-dnl LM_PROG_INSTALL_DIR
-dnl
-dnl Figure out how to create directories with parents.
-dnl (In my opinion INSTALL_DIR is a bad name, MKSUBDIRS or something is better)
-dnl
-dnl We prefer 'install -d', but use 'mkdir -p' if it exists.
-dnl If none of these methods works, we give up.
-dnl
-
-
-AC_DEFUN(LM_PROG_INSTALL_DIR,
-[AC_CACHE_CHECK(how to create a directory including parents,
-ac_cv_prog_mkdir_p,
-[
-temp_name_base=config.$$
-temp_name=$temp_name_base/x/y/z
-$INSTALL -d $temp_name >/dev/null 2>&1
-ac_cv_prog_mkdir_p=none
-if test -d $temp_name; then
- ac_cv_prog_mkdir_p="$INSTALL -d"
-else
- mkdir -p $temp_name >/dev/null 2>&1
- if test -d $temp_name; then
- ac_cv_prog_mkdir_p="mkdir -p"
- fi
-fi
-rm -fr $temp_name_base
-])
-
-case "${ac_cv_prog_mkdir_p}" in
- none) AC_MSG_ERROR(don't know how create directories with parents) ;;
- *) INSTALL_DIR="$ac_cv_prog_mkdir_p" AC_SUBST(INSTALL_DIR) ;;
-esac
-])
-
-
diff --git a/lib/diameter/autoconf/config.guess b/lib/diameter/autoconf/config.guess
deleted file mode 100755
index 38a833903b..0000000000
--- a/lib/diameter/autoconf/config.guess
+++ /dev/null
@@ -1,1519 +0,0 @@
-#! /bin/sh
-# Attempt to guess a canonical system name.
-# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
-# 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation,
-# Inc.
-
-timestamp='2007-05-17'
-
-# This file is free software; you can redistribute it and/or modify it
-# under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program 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
-# General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
-# 02110-1301, USA.
-#
-# As a special exception to the GNU General Public License, if you
-# distribute this file as part of a program that contains a
-# configuration script generated by Autoconf, you may include it under
-# the same distribution terms that you use for the rest of that program.
-
-
-# Originally written by Per Bothner <[email protected]>.
-# Please send patches to <[email protected]>. Submit a context
-# diff and a properly formatted ChangeLog entry.
-#
-# This script attempts to guess a canonical system name similar to
-# config.sub. If it succeeds, it prints the system name on stdout, and
-# exits with 0. Otherwise, it exits with 1.
-#
-# The plan is that this can be called by configure scripts if you
-# don't specify an explicit build system type.
-
-me=`echo "$0" | sed -e 's,.*/,,'`
-
-usage="\
-Usage: $0 [OPTION]
-
-Output the configuration name of the system \`$me' is run on.
-
-Operation modes:
- -h, --help print this help, then exit
- -t, --time-stamp print date of last modification, then exit
- -v, --version print version number, then exit
-
-Report bugs and patches to <[email protected]>."
-
-version="\
-GNU config.guess ($timestamp)
-
-Originally written by Per Bothner.
-Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
-Free Software Foundation, Inc.
-
-This is free software; see the source for copying conditions. There is NO
-warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
-
-help="
-Try \`$me --help' for more information."
-
-# Parse command line
-while test $# -gt 0 ; do
- case $1 in
- --time-stamp | --time* | -t )
- echo "$timestamp" ; exit ;;
- --version | -v )
- echo "$version" ; exit ;;
- --help | --h* | -h )
- echo "$usage"; exit ;;
- -- ) # Stop option processing
- shift; break ;;
- - ) # Use stdin as input.
- break ;;
- -* )
- echo "$me: invalid option $1$help" >&2
- exit 1 ;;
- * )
- break ;;
- esac
-done
-
-if test $# != 0; then
- echo "$me: too many arguments$help" >&2
- exit 1
-fi
-
-trap 'exit 1' 1 2 15
-
-# CC_FOR_BUILD -- compiler used by this script. Note that the use of a
-# compiler to aid in system detection is discouraged as it requires
-# temporary files to be created and, as you can see below, it is a
-# headache to deal with in a portable fashion.
-
-# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
-# use `HOST_CC' if defined, but it is deprecated.
-
-# Portable tmp directory creation inspired by the Autoconf team.
-
-set_cc_for_build='
-trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ;
-trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ;
-: ${TMPDIR=/tmp} ;
- { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } ||
- { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } ||
- { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } ||
- { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ;
-dummy=$tmp/dummy ;
-tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ;
-case $CC_FOR_BUILD,$HOST_CC,$CC in
- ,,) echo "int x;" > $dummy.c ;
- for c in cc gcc c89 c99 ; do
- if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then
- CC_FOR_BUILD="$c"; break ;
- fi ;
- done ;
- if test x"$CC_FOR_BUILD" = x ; then
- CC_FOR_BUILD=no_compiler_found ;
- fi
- ;;
- ,,*) CC_FOR_BUILD=$CC ;;
- ,*,*) CC_FOR_BUILD=$HOST_CC ;;
-esac ; set_cc_for_build= ;'
-
-# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
-# ([email protected] 1994-08-24)
-if (test -f /.attbin/uname) >/dev/null 2>&1 ; then
- PATH=$PATH:/.attbin ; export PATH
-fi
-
-UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
-UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
-UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown
-UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
-
-# Note: order is significant - the case branches are not exclusive.
-
-case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
- *:NetBSD:*:*)
- # NetBSD (nbsd) targets should (where applicable) match one or
- # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*,
- # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently
- # switched to ELF, *-*-netbsd* would select the old
- # object file format. This provides both forward
- # compatibility and a consistent mechanism for selecting the
- # object file format.
- #
- # Note: NetBSD doesn't particularly care about the vendor
- # portion of the name. We always set it to "unknown".
- sysctl="sysctl -n hw.machine_arch"
- UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \
- /usr/sbin/$sysctl 2>/dev/null || echo unknown)`
- case "${UNAME_MACHINE_ARCH}" in
- armeb) machine=armeb-unknown ;;
- arm*) machine=arm-unknown ;;
- sh3el) machine=shl-unknown ;;
- sh3eb) machine=sh-unknown ;;
- sh5el) machine=sh5le-unknown ;;
- *) machine=${UNAME_MACHINE_ARCH}-unknown ;;
- esac
- # The Operating System including object format, if it has switched
- # to ELF recently, or will in the future.
- case "${UNAME_MACHINE_ARCH}" in
- arm*|i386|m68k|ns32k|sh3*|sparc|vax)
- eval $set_cc_for_build
- if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
- | grep __ELF__ >/dev/null
- then
- # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
- # Return netbsd for either. FIX?
- os=netbsd
- else
- os=netbsdelf
- fi
- ;;
- *)
- os=netbsd
- ;;
- esac
- # The OS release
- # Debian GNU/NetBSD machines have a different userland, and
- # thus, need a distinct triplet. However, they do not need
- # kernel version information, so it can be replaced with a
- # suitable tag, in the style of linux-gnu.
- case "${UNAME_VERSION}" in
- Debian*)
- release='-gnu'
- ;;
- *)
- release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
- ;;
- esac
- # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
- # contains redundant information, the shorter form:
- # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
- echo "${machine}-${os}${release}"
- exit ;;
- *:OpenBSD:*:*)
- UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'`
- echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE}
- exit ;;
- *:ekkoBSD:*:*)
- echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE}
- exit ;;
- *:SolidBSD:*:*)
- echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE}
- exit ;;
- macppc:MirBSD:*:*)
- echo powerpc-unknown-mirbsd${UNAME_RELEASE}
- exit ;;
- *:MirBSD:*:*)
- echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE}
- exit ;;
- alpha:OSF1:*:*)
- case $UNAME_RELEASE in
- *4.0)
- UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
- ;;
- *5.*)
- UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
- ;;
- esac
- # According to Compaq, /usr/sbin/psrinfo has been available on
- # OSF/1 and Tru64 systems produced since 1995. I hope that
- # covers most systems running today. This code pipes the CPU
- # types through head -n 1, so we only detect the type of CPU 0.
- ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1`
- case "$ALPHA_CPU_TYPE" in
- "EV4 (21064)")
- UNAME_MACHINE="alpha" ;;
- "EV4.5 (21064)")
- UNAME_MACHINE="alpha" ;;
- "LCA4 (21066/21068)")
- UNAME_MACHINE="alpha" ;;
- "EV5 (21164)")
- UNAME_MACHINE="alphaev5" ;;
- "EV5.6 (21164A)")
- UNAME_MACHINE="alphaev56" ;;
- "EV5.6 (21164PC)")
- UNAME_MACHINE="alphapca56" ;;
- "EV5.7 (21164PC)")
- UNAME_MACHINE="alphapca57" ;;
- "EV6 (21264)")
- UNAME_MACHINE="alphaev6" ;;
- "EV6.7 (21264A)")
- UNAME_MACHINE="alphaev67" ;;
- "EV6.8CB (21264C)")
- UNAME_MACHINE="alphaev68" ;;
- "EV6.8AL (21264B)")
- UNAME_MACHINE="alphaev68" ;;
- "EV6.8CX (21264D)")
- UNAME_MACHINE="alphaev68" ;;
- "EV6.9A (21264/EV69A)")
- UNAME_MACHINE="alphaev69" ;;
- "EV7 (21364)")
- UNAME_MACHINE="alphaev7" ;;
- "EV7.9 (21364A)")
- UNAME_MACHINE="alphaev79" ;;
- esac
- # A Pn.n version is a patched version.
- # A Vn.n version is a released version.
- # A Tn.n version is a released field test version.
- # A Xn.n version is an unreleased experimental baselevel.
- # 1.2 uses "1.2" for uname -r.
- echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
- exit ;;
- Alpha\ *:Windows_NT*:*)
- # How do we know it's Interix rather than the generic POSIX subsystem?
- # Should we change UNAME_MACHINE based on the output of uname instead
- # of the specific Alpha model?
- echo alpha-pc-interix
- exit ;;
- 21064:Windows_NT:50:3)
- echo alpha-dec-winnt3.5
- exit ;;
- Amiga*:UNIX_System_V:4.0:*)
- echo m68k-unknown-sysv4
- exit ;;
- *:[Aa]miga[Oo][Ss]:*:*)
- echo ${UNAME_MACHINE}-unknown-amigaos
- exit ;;
- *:[Mm]orph[Oo][Ss]:*:*)
- echo ${UNAME_MACHINE}-unknown-morphos
- exit ;;
- *:OS/390:*:*)
- echo i370-ibm-openedition
- exit ;;
- *:z/VM:*:*)
- echo s390-ibm-zvmoe
- exit ;;
- *:OS400:*:*)
- echo powerpc-ibm-os400
- exit ;;
- arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
- echo arm-acorn-riscix${UNAME_RELEASE}
- exit ;;
- arm:riscos:*:*|arm:RISCOS:*:*)
- echo arm-unknown-riscos
- exit ;;
- SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
- echo hppa1.1-hitachi-hiuxmpp
- exit ;;
- Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
- # [email protected] (Earle F. Ake) contributed MIS and NILE.
- if test "`(/bin/universe) 2>/dev/null`" = att ; then
- echo pyramid-pyramid-sysv3
- else
- echo pyramid-pyramid-bsd
- fi
- exit ;;
- NILE*:*:*:dcosx)
- echo pyramid-pyramid-svr4
- exit ;;
- DRS?6000:unix:4.0:6*)
- echo sparc-icl-nx6
- exit ;;
- DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*)
- case `/usr/bin/uname -p` in
- sparc) echo sparc-icl-nx7; exit ;;
- esac ;;
- sun4H:SunOS:5.*:*)
- echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
- exit ;;
- sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
- echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
- exit ;;
- i86pc:SunOS:5.*:* | ix86xen:SunOS:5.*:*)
- echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
- exit ;;
- sun4*:SunOS:6*:*)
- # According to config.sub, this is the proper way to canonicalize
- # SunOS6. Hard to guess exactly what SunOS6 will be like, but
- # it's likely to be more like Solaris than SunOS4.
- echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
- exit ;;
- sun4*:SunOS:*:*)
- case "`/usr/bin/arch -k`" in
- Series*|S4*)
- UNAME_RELEASE=`uname -v`
- ;;
- esac
- # Japanese Language versions have a version number like `4.1.3-JL'.
- echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`
- exit ;;
- sun3*:SunOS:*:*)
- echo m68k-sun-sunos${UNAME_RELEASE}
- exit ;;
- sun*:*:4.2BSD:*)
- UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
- test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3
- case "`/bin/arch`" in
- sun3)
- echo m68k-sun-sunos${UNAME_RELEASE}
- ;;
- sun4)
- echo sparc-sun-sunos${UNAME_RELEASE}
- ;;
- esac
- exit ;;
- aushp:SunOS:*:*)
- echo sparc-auspex-sunos${UNAME_RELEASE}
- exit ;;
- # The situation for MiNT is a little confusing. The machine name
- # can be virtually everything (everything which is not
- # "atarist" or "atariste" at least should have a processor
- # > m68000). The system name ranges from "MiNT" over "FreeMiNT"
- # to the lowercase version "mint" (or "freemint"). Finally
- # the system name "TOS" denotes a system which is actually not
- # MiNT. But MiNT is downward compatible to TOS, so this should
- # be no problem.
- atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
- echo m68k-atari-mint${UNAME_RELEASE}
- exit ;;
- atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
- echo m68k-atari-mint${UNAME_RELEASE}
- exit ;;
- *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
- echo m68k-atari-mint${UNAME_RELEASE}
- exit ;;
- milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
- echo m68k-milan-mint${UNAME_RELEASE}
- exit ;;
- hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
- echo m68k-hades-mint${UNAME_RELEASE}
- exit ;;
- *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
- echo m68k-unknown-mint${UNAME_RELEASE}
- exit ;;
- m68k:machten:*:*)
- echo m68k-apple-machten${UNAME_RELEASE}
- exit ;;
- powerpc:machten:*:*)
- echo powerpc-apple-machten${UNAME_RELEASE}
- exit ;;
- RISC*:Mach:*:*)
- echo mips-dec-mach_bsd4.3
- exit ;;
- RISC*:ULTRIX:*:*)
- echo mips-dec-ultrix${UNAME_RELEASE}
- exit ;;
- VAX*:ULTRIX*:*:*)
- echo vax-dec-ultrix${UNAME_RELEASE}
- exit ;;
- 2020:CLIX:*:* | 2430:CLIX:*:*)
- echo clipper-intergraph-clix${UNAME_RELEASE}
- exit ;;
- mips:*:*:UMIPS | mips:*:*:RISCos)
- eval $set_cc_for_build
- sed 's/^ //' << EOF >$dummy.c
-#ifdef __cplusplus
-#include <stdio.h> /* for printf() prototype */
- int main (int argc, char *argv[]) {
-#else
- int main (argc, argv) int argc; char *argv[]; {
-#endif
- #if defined (host_mips) && defined (MIPSEB)
- #if defined (SYSTYPE_SYSV)
- printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0);
- #endif
- #if defined (SYSTYPE_SVR4)
- printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0);
- #endif
- #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
- printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0);
- #endif
- #endif
- exit (-1);
- }
-EOF
- $CC_FOR_BUILD -o $dummy $dummy.c &&
- dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` &&
- SYSTEM_NAME=`$dummy $dummyarg` &&
- { echo "$SYSTEM_NAME"; exit; }
- echo mips-mips-riscos${UNAME_RELEASE}
- exit ;;
- Motorola:PowerMAX_OS:*:*)
- echo powerpc-motorola-powermax
- exit ;;
- Motorola:*:4.3:PL8-*)
- echo powerpc-harris-powermax
- exit ;;
- Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)
- echo powerpc-harris-powermax
- exit ;;
- Night_Hawk:Power_UNIX:*:*)
- echo powerpc-harris-powerunix
- exit ;;
- m88k:CX/UX:7*:*)
- echo m88k-harris-cxux7
- exit ;;
- m88k:*:4*:R4*)
- echo m88k-motorola-sysv4
- exit ;;
- m88k:*:3*:R3*)
- echo m88k-motorola-sysv3
- exit ;;
- AViiON:dgux:*:*)
- # DG/UX returns AViiON for all architectures
- UNAME_PROCESSOR=`/usr/bin/uname -p`
- if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ]
- then
- if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \
- [ ${TARGET_BINARY_INTERFACE}x = x ]
- then
- echo m88k-dg-dgux${UNAME_RELEASE}
- else
- echo m88k-dg-dguxbcs${UNAME_RELEASE}
- fi
- else
- echo i586-dg-dgux${UNAME_RELEASE}
- fi
- exit ;;
- M88*:DolphinOS:*:*) # DolphinOS (SVR3)
- echo m88k-dolphin-sysv3
- exit ;;
- M88*:*:R3*:*)
- # Delta 88k system running SVR3
- echo m88k-motorola-sysv3
- exit ;;
- XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
- echo m88k-tektronix-sysv3
- exit ;;
- Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
- echo m68k-tektronix-bsd
- exit ;;
- *:IRIX*:*:*)
- echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`
- exit ;;
- ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
- echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id
- exit ;; # Note that: echo "'`uname -s`'" gives 'AIX '
- i*86:AIX:*:*)
- echo i386-ibm-aix
- exit ;;
- ia64:AIX:*:*)
- if [ -x /usr/bin/oslevel ] ; then
- IBM_REV=`/usr/bin/oslevel`
- else
- IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
- fi
- echo ${UNAME_MACHINE}-ibm-aix${IBM_REV}
- exit ;;
- *:AIX:2:3)
- if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
- eval $set_cc_for_build
- sed 's/^ //' << EOF >$dummy.c
- #include <sys/systemcfg.h>
-
- main()
- {
- if (!__power_pc())
- exit(1);
- puts("powerpc-ibm-aix3.2.5");
- exit(0);
- }
-EOF
- if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy`
- then
- echo "$SYSTEM_NAME"
- else
- echo rs6000-ibm-aix3.2.5
- fi
- elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
- echo rs6000-ibm-aix3.2.4
- else
- echo rs6000-ibm-aix3.2
- fi
- exit ;;
- *:AIX:*:[45])
- IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
- if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then
- IBM_ARCH=rs6000
- else
- IBM_ARCH=powerpc
- fi
- if [ -x /usr/bin/oslevel ] ; then
- IBM_REV=`/usr/bin/oslevel`
- else
- IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
- fi
- echo ${IBM_ARCH}-ibm-aix${IBM_REV}
- exit ;;
- *:AIX:*:*)
- echo rs6000-ibm-aix
- exit ;;
- ibmrt:4.4BSD:*|romp-ibm:BSD:*)
- echo romp-ibm-bsd4.4
- exit ;;
- ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and
- echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to
- exit ;; # report: romp-ibm BSD 4.3
- *:BOSX:*:*)
- echo rs6000-bull-bosx
- exit ;;
- DPX/2?00:B.O.S.:*:*)
- echo m68k-bull-sysv3
- exit ;;
- 9000/[34]??:4.3bsd:1.*:*)
- echo m68k-hp-bsd
- exit ;;
- hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
- echo m68k-hp-bsd4.4
- exit ;;
- 9000/[34678]??:HP-UX:*:*)
- HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
- case "${UNAME_MACHINE}" in
- 9000/31? ) HP_ARCH=m68000 ;;
- 9000/[34]?? ) HP_ARCH=m68k ;;
- 9000/[678][0-9][0-9])
- if [ -x /usr/bin/getconf ]; then
- sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
- sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
- case "${sc_cpu_version}" in
- 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0
- 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1
- 532) # CPU_PA_RISC2_0
- case "${sc_kernel_bits}" in
- 32) HP_ARCH="hppa2.0n" ;;
- 64) HP_ARCH="hppa2.0w" ;;
- '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20
- esac ;;
- esac
- fi
- if [ "${HP_ARCH}" = "" ]; then
- eval $set_cc_for_build
- sed 's/^ //' << EOF >$dummy.c
-
- #define _HPUX_SOURCE
- #include <stdlib.h>
- #include <unistd.h>
-
- int main ()
- {
- #if defined(_SC_KERNEL_BITS)
- long bits = sysconf(_SC_KERNEL_BITS);
- #endif
- long cpu = sysconf (_SC_CPU_VERSION);
-
- switch (cpu)
- {
- case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
- case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
- case CPU_PA_RISC2_0:
- #if defined(_SC_KERNEL_BITS)
- switch (bits)
- {
- case 64: puts ("hppa2.0w"); break;
- case 32: puts ("hppa2.0n"); break;
- default: puts ("hppa2.0"); break;
- } break;
- #else /* !defined(_SC_KERNEL_BITS) */
- puts ("hppa2.0"); break;
- #endif
- default: puts ("hppa1.0"); break;
- }
- exit (0);
- }
-EOF
- (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy`
- test -z "$HP_ARCH" && HP_ARCH=hppa
- fi ;;
- esac
- if [ ${HP_ARCH} = "hppa2.0w" ]
- then
- eval $set_cc_for_build
-
- # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating
- # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler
- # generating 64-bit code. GNU and HP use different nomenclature:
- #
- # $ CC_FOR_BUILD=cc ./config.guess
- # => hppa2.0w-hp-hpux11.23
- # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess
- # => hppa64-hp-hpux11.23
-
- if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) |
- grep __LP64__ >/dev/null
- then
- HP_ARCH="hppa2.0w"
- else
- HP_ARCH="hppa64"
- fi
- fi
- echo ${HP_ARCH}-hp-hpux${HPUX_REV}
- exit ;;
- ia64:HP-UX:*:*)
- HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
- echo ia64-hp-hpux${HPUX_REV}
- exit ;;
- 3050*:HI-UX:*:*)
- eval $set_cc_for_build
- sed 's/^ //' << EOF >$dummy.c
- #include <unistd.h>
- int
- main ()
- {
- long cpu = sysconf (_SC_CPU_VERSION);
- /* The order matters, because CPU_IS_HP_MC68K erroneously returns
- true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct
- results, however. */
- if (CPU_IS_PA_RISC (cpu))
- {
- switch (cpu)
- {
- case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
- case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
- case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
- default: puts ("hppa-hitachi-hiuxwe2"); break;
- }
- }
- else if (CPU_IS_HP_MC68K (cpu))
- puts ("m68k-hitachi-hiuxwe2");
- else puts ("unknown-hitachi-hiuxwe2");
- exit (0);
- }
-EOF
- $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` &&
- { echo "$SYSTEM_NAME"; exit; }
- echo unknown-hitachi-hiuxwe2
- exit ;;
- 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )
- echo hppa1.1-hp-bsd
- exit ;;
- 9000/8??:4.3bsd:*:*)
- echo hppa1.0-hp-bsd
- exit ;;
- *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
- echo hppa1.0-hp-mpeix
- exit ;;
- hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
- echo hppa1.1-hp-osf
- exit ;;
- hp8??:OSF1:*:*)
- echo hppa1.0-hp-osf
- exit ;;
- i*86:OSF1:*:*)
- if [ -x /usr/sbin/sysversion ] ; then
- echo ${UNAME_MACHINE}-unknown-osf1mk
- else
- echo ${UNAME_MACHINE}-unknown-osf1
- fi
- exit ;;
- parisc*:Lites*:*:*)
- echo hppa1.1-hp-lites
- exit ;;
- C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
- echo c1-convex-bsd
- exit ;;
- C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
- if getsysinfo -f scalar_acc
- then echo c32-convex-bsd
- else echo c2-convex-bsd
- fi
- exit ;;
- C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
- echo c34-convex-bsd
- exit ;;
- C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
- echo c38-convex-bsd
- exit ;;
- C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
- echo c4-convex-bsd
- exit ;;
- CRAY*Y-MP:*:*:*)
- echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
- exit ;;
- CRAY*[A-Z]90:*:*:*)
- echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \
- | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
- -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
- -e 's/\.[^.]*$/.X/'
- exit ;;
- CRAY*TS:*:*:*)
- echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
- exit ;;
- CRAY*T3E:*:*:*)
- echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
- exit ;;
- CRAY*SV1:*:*:*)
- echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
- exit ;;
- *:UNICOS/mp:*:*)
- echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
- exit ;;
- F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
- FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
- FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
- FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
- echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
- exit ;;
- 5000:UNIX_System_V:4.*:*)
- FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
- FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'`
- echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
- exit ;;
- i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
- echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
- exit ;;
- sparc*:BSD/OS:*:*)
- echo sparc-unknown-bsdi${UNAME_RELEASE}
- exit ;;
- *:BSD/OS:*:*)
- echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
- exit ;;
- *:FreeBSD:*:*)
- case ${UNAME_MACHINE} in
- pc98)
- echo i386-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
- amd64)
- echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
- *)
- echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
- esac
- exit ;;
- i*:CYGWIN*:*)
- echo ${UNAME_MACHINE}-pc-cygwin
- exit ;;
- *:MINGW*:*)
- echo ${UNAME_MACHINE}-pc-mingw32
- exit ;;
- i*:windows32*:*)
- # uname -m includes "-pc" on this system.
- echo ${UNAME_MACHINE}-mingw32
- exit ;;
- i*:PW*:*)
- echo ${UNAME_MACHINE}-pc-pw32
- exit ;;
- *:Interix*:[3456]*)
- case ${UNAME_MACHINE} in
- x86)
- echo i586-pc-interix${UNAME_RELEASE}
- exit ;;
- EM64T | authenticamd)
- echo x86_64-unknown-interix${UNAME_RELEASE}
- exit ;;
- esac ;;
- [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*)
- echo i${UNAME_MACHINE}-pc-mks
- exit ;;
- i*:Windows_NT*:* | Pentium*:Windows_NT*:*)
- # How do we know it's Interix rather than the generic POSIX subsystem?
- # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we
- # UNAME_MACHINE based on the output of uname instead of i386?
- echo i586-pc-interix
- exit ;;
- i*:UWIN*:*)
- echo ${UNAME_MACHINE}-pc-uwin
- exit ;;
- amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*)
- echo x86_64-unknown-cygwin
- exit ;;
- p*:CYGWIN*:*)
- echo powerpcle-unknown-cygwin
- exit ;;
- prep*:SunOS:5.*:*)
- echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
- exit ;;
- *:GNU:*:*)
- # the GNU system
- echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
- exit ;;
- *:GNU/*:*:*)
- # other systems with GNU libc and userland
- echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu
- exit ;;
- i*86:Minix:*:*)
- echo ${UNAME_MACHINE}-pc-minix
- exit ;;
- arm*:Linux:*:*)
- echo ${UNAME_MACHINE}-unknown-linux-gnu
- exit ;;
- avr32*:Linux:*:*)
- echo ${UNAME_MACHINE}-unknown-linux-gnu
- exit ;;
- cris:Linux:*:*)
- echo cris-axis-linux-gnu
- exit ;;
- crisv32:Linux:*:*)
- echo crisv32-axis-linux-gnu
- exit ;;
- frv:Linux:*:*)
- echo frv-unknown-linux-gnu
- exit ;;
- ia64:Linux:*:*)
- echo ${UNAME_MACHINE}-unknown-linux-gnu
- exit ;;
- m32r*:Linux:*:*)
- echo ${UNAME_MACHINE}-unknown-linux-gnu
- exit ;;
- m68*:Linux:*:*)
- echo ${UNAME_MACHINE}-unknown-linux-gnu
- exit ;;
- mips:Linux:*:*)
- eval $set_cc_for_build
- sed 's/^ //' << EOF >$dummy.c
- #undef CPU
- #undef mips
- #undef mipsel
- #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
- CPU=mipsel
- #else
- #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
- CPU=mips
- #else
- CPU=
- #endif
- #endif
-EOF
- eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n '
- /^CPU/{
- s: ::g
- p
- }'`"
- test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; }
- ;;
- mips64:Linux:*:*)
- eval $set_cc_for_build
- sed 's/^ //' << EOF >$dummy.c
- #undef CPU
- #undef mips64
- #undef mips64el
- #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
- CPU=mips64el
- #else
- #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
- CPU=mips64
- #else
- CPU=
- #endif
- #endif
-EOF
- eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n '
- /^CPU/{
- s: ::g
- p
- }'`"
- test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; }
- ;;
- or32:Linux:*:*)
- echo or32-unknown-linux-gnu
- exit ;;
- ppc:Linux:*:*)
- echo powerpc-unknown-linux-gnu
- exit ;;
- ppc64:Linux:*:*)
- echo powerpc64-unknown-linux-gnu
- exit ;;
- alpha:Linux:*:*)
- case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
- EV5) UNAME_MACHINE=alphaev5 ;;
- EV56) UNAME_MACHINE=alphaev56 ;;
- PCA56) UNAME_MACHINE=alphapca56 ;;
- PCA57) UNAME_MACHINE=alphapca56 ;;
- EV6) UNAME_MACHINE=alphaev6 ;;
- EV67) UNAME_MACHINE=alphaev67 ;;
- EV68*) UNAME_MACHINE=alphaev68 ;;
- esac
- objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null
- if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi
- echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC}
- exit ;;
- parisc:Linux:*:* | hppa:Linux:*:*)
- # Look for CPU level
- case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
- PA7*) echo hppa1.1-unknown-linux-gnu ;;
- PA8*) echo hppa2.0-unknown-linux-gnu ;;
- *) echo hppa-unknown-linux-gnu ;;
- esac
- exit ;;
- parisc64:Linux:*:* | hppa64:Linux:*:*)
- echo hppa64-unknown-linux-gnu
- exit ;;
- s390:Linux:*:* | s390x:Linux:*:*)
- echo ${UNAME_MACHINE}-ibm-linux
- exit ;;
- sh64*:Linux:*:*)
- echo ${UNAME_MACHINE}-unknown-linux-gnu
- exit ;;
- sh*:Linux:*:*)
- echo ${UNAME_MACHINE}-unknown-linux-gnu
- exit ;;
- sparc:Linux:*:* | sparc64:Linux:*:*)
- echo ${UNAME_MACHINE}-unknown-linux-gnu
- exit ;;
- tile:Linux:*:*)
- echo tile-unknown-linux-gnu
- exit ;;
- vax:Linux:*:*)
- echo ${UNAME_MACHINE}-dec-linux-gnu
- exit ;;
- x86_64:Linux:*:*)
- echo x86_64-unknown-linux-gnu
- exit ;;
- xtensa:Linux:*:*)
- echo xtensa-unknown-linux-gnu
- exit ;;
- i*86:Linux:*:*)
- # The BFD linker knows what the default object file format is, so
- # first see if it will tell us. cd to the root directory to prevent
- # problems with other programs or directories called `ld' in the path.
- # Set LC_ALL=C to ensure ld outputs messages in English.
- ld_supported_targets=`cd /; LC_ALL=C ld --help 2>&1 \
- | sed -ne '/supported targets:/!d
- s/[ ][ ]*/ /g
- s/.*supported targets: *//
- s/ .*//
- p'`
- case "$ld_supported_targets" in
- elf32-i386)
- TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu"
- ;;
- a.out-i386-linux)
- echo "${UNAME_MACHINE}-pc-linux-gnuaout"
- exit ;;
- coff-i386)
- echo "${UNAME_MACHINE}-pc-linux-gnucoff"
- exit ;;
- "")
- # Either a pre-BFD a.out linker (linux-gnuoldld) or
- # one that does not give us useful --help.
- echo "${UNAME_MACHINE}-pc-linux-gnuoldld"
- exit ;;
- esac
- # Determine whether the default compiler is a.out or elf
- eval $set_cc_for_build
- sed 's/^ //' << EOF >$dummy.c
- #include <features.h>
- #ifdef __ELF__
- # ifdef __GLIBC__
- # if __GLIBC__ >= 2
- LIBC=gnu
- # else
- LIBC=gnulibc1
- # endif
- # else
- LIBC=gnulibc1
- # endif
- #else
- #if defined(__INTEL_COMPILER) || defined(__PGI) || defined(__SUNPRO_C) || defined(__SUNPRO_CC)
- LIBC=gnu
- #else
- LIBC=gnuaout
- #endif
- #endif
- #ifdef __dietlibc__
- LIBC=dietlibc
- #endif
-EOF
- eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n '
- /^LIBC/{
- s: ::g
- p
- }'`"
- test x"${LIBC}" != x && {
- echo "${UNAME_MACHINE}-pc-linux-${LIBC}"
- exit
- }
- test x"${TENTATIVE}" != x && { echo "${TENTATIVE}"; exit; }
- ;;
- i*86:DYNIX/ptx:4*:*)
- # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
- # earlier versions are messed up and put the nodename in both
- # sysname and nodename.
- echo i386-sequent-sysv4
- exit ;;
- i*86:UNIX_SV:4.2MP:2.*)
- # Unixware is an offshoot of SVR4, but it has its own version
- # number series starting with 2...
- # I am not positive that other SVR4 systems won't match this,
- # I just have to hope. -- rms.
- # Use sysv4.2uw... so that sysv4* matches it.
- echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
- exit ;;
- i*86:OS/2:*:*)
- # If we were able to find `uname', then EMX Unix compatibility
- # is probably installed.
- echo ${UNAME_MACHINE}-pc-os2-emx
- exit ;;
- i*86:XTS-300:*:STOP)
- echo ${UNAME_MACHINE}-unknown-stop
- exit ;;
- i*86:atheos:*:*)
- echo ${UNAME_MACHINE}-unknown-atheos
- exit ;;
- i*86:syllable:*:*)
- echo ${UNAME_MACHINE}-pc-syllable
- exit ;;
- i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*)
- echo i386-unknown-lynxos${UNAME_RELEASE}
- exit ;;
- i*86:*DOS:*:*)
- echo ${UNAME_MACHINE}-pc-msdosdjgpp
- exit ;;
- i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*)
- UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'`
- if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
- echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL}
- else
- echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL}
- fi
- exit ;;
- i*86:*:5:[678]*)
- # UnixWare 7.x, OpenUNIX and OpenServer 6.
- case `/bin/uname -X | grep "^Machine"` in
- *486*) UNAME_MACHINE=i486 ;;
- *Pentium) UNAME_MACHINE=i586 ;;
- *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
- esac
- echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}
- exit ;;
- i*86:*:3.2:*)
- if test -f /usr/options/cb.name; then
- UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
- echo ${UNAME_MACHINE}-pc-isc$UNAME_REL
- elif /bin/uname -X 2>/dev/null >/dev/null ; then
- UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`
- (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486
- (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \
- && UNAME_MACHINE=i586
- (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \
- && UNAME_MACHINE=i686
- (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \
- && UNAME_MACHINE=i686
- echo ${UNAME_MACHINE}-pc-sco$UNAME_REL
- else
- echo ${UNAME_MACHINE}-pc-sysv32
- fi
- exit ;;
- pc:*:*:*)
- # Left here for compatibility:
- # uname -m prints for DJGPP always 'pc', but it prints nothing about
- # the processor, so we play safe by assuming i386.
- echo i386-pc-msdosdjgpp
- exit ;;
- Intel:Mach:3*:*)
- echo i386-pc-mach3
- exit ;;
- paragon:*:*:*)
- echo i860-intel-osf1
- exit ;;
- i860:*:4.*:*) # i860-SVR4
- if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
- echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4
- else # Add other i860-SVR4 vendors below as they are discovered.
- echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4
- fi
- exit ;;
- mini*:CTIX:SYS*5:*)
- # "miniframe"
- echo m68010-convergent-sysv
- exit ;;
- mc68k:UNIX:SYSTEM5:3.51m)
- echo m68k-convergent-sysv
- exit ;;
- M680?0:D-NIX:5.3:*)
- echo m68k-diab-dnix
- exit ;;
- M68*:*:R3V[5678]*:*)
- test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;;
- 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0)
- OS_REL=''
- test -r /etc/.relid \
- && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
- /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
- && { echo i486-ncr-sysv4.3${OS_REL}; exit; }
- /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
- && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;
- 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
- /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
- && { echo i486-ncr-sysv4; exit; } ;;
- m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
- echo m68k-unknown-lynxos${UNAME_RELEASE}
- exit ;;
- mc68030:UNIX_System_V:4.*:*)
- echo m68k-atari-sysv4
- exit ;;
- TSUNAMI:LynxOS:2.*:*)
- echo sparc-unknown-lynxos${UNAME_RELEASE}
- exit ;;
- rs6000:LynxOS:2.*:*)
- echo rs6000-unknown-lynxos${UNAME_RELEASE}
- exit ;;
- PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*)
- echo powerpc-unknown-lynxos${UNAME_RELEASE}
- exit ;;
- SM[BE]S:UNIX_SV:*:*)
- echo mips-dde-sysv${UNAME_RELEASE}
- exit ;;
- RM*:ReliantUNIX-*:*:*)
- echo mips-sni-sysv4
- exit ;;
- RM*:SINIX-*:*:*)
- echo mips-sni-sysv4
- exit ;;
- *:SINIX-*:*:*)
- if uname -p 2>/dev/null >/dev/null ; then
- UNAME_MACHINE=`(uname -p) 2>/dev/null`
- echo ${UNAME_MACHINE}-sni-sysv4
- else
- echo ns32k-sni-sysv
- fi
- exit ;;
- PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
- echo i586-unisys-sysv4
- exit ;;
- *:UNIX_System_V:4*:FTX*)
- # From Gerald Hewes <[email protected]>.
- # How about differentiating between stratus architectures? -djm
- echo hppa1.1-stratus-sysv4
- exit ;;
- *:*:*:FTX*)
- echo i860-stratus-sysv4
- exit ;;
- i*86:VOS:*:*)
- echo ${UNAME_MACHINE}-stratus-vos
- exit ;;
- *:VOS:*:*)
- echo hppa1.1-stratus-vos
- exit ;;
- mc68*:A/UX:*:*)
- echo m68k-apple-aux${UNAME_RELEASE}
- exit ;;
- news*:NEWS-OS:6*:*)
- echo mips-sony-newsos6
- exit ;;
- R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
- if [ -d /usr/nec ]; then
- echo mips-nec-sysv${UNAME_RELEASE}
- else
- echo mips-unknown-sysv${UNAME_RELEASE}
- fi
- exit ;;
- BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only.
- echo powerpc-be-beos
- exit ;;
- BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only.
- echo powerpc-apple-beos
- exit ;;
- BePC:BeOS:*:*) # BeOS running on Intel PC compatible.
- echo i586-pc-beos
- exit ;;
- SX-4:SUPER-UX:*:*)
- echo sx4-nec-superux${UNAME_RELEASE}
- exit ;;
- SX-5:SUPER-UX:*:*)
- echo sx5-nec-superux${UNAME_RELEASE}
- exit ;;
- SX-6:SUPER-UX:*:*)
- echo sx6-nec-superux${UNAME_RELEASE}
- exit ;;
- SX-7:SUPER-UX:*:*)
- echo sx7-nec-superux${UNAME_RELEASE}
- exit ;;
- SX-8:SUPER-UX:*:*)
- echo sx8-nec-superux${UNAME_RELEASE}
- exit ;;
- SX-8R:SUPER-UX:*:*)
- echo sx8r-nec-superux${UNAME_RELEASE}
- exit ;;
- Power*:Rhapsody:*:*)
- echo powerpc-apple-rhapsody${UNAME_RELEASE}
- exit ;;
- *:Rhapsody:*:*)
- echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}
- exit ;;
- *:Darwin:*:*)
- UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown
- case $UNAME_PROCESSOR in
- unknown) UNAME_PROCESSOR=powerpc ;;
- esac
- echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
- exit ;;
- *:procnto*:*:* | *:QNX:[0123456789]*:*)
- UNAME_PROCESSOR=`uname -p`
- if test "$UNAME_PROCESSOR" = "x86"; then
- UNAME_PROCESSOR=i386
- UNAME_MACHINE=pc
- fi
- echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE}
- exit ;;
- *:QNX:*:4*)
- echo i386-pc-qnx
- exit ;;
- NSE-?:NONSTOP_KERNEL:*:*)
- echo nse-tandem-nsk${UNAME_RELEASE}
- exit ;;
- NSR-?:NONSTOP_KERNEL:*:*)
- echo nsr-tandem-nsk${UNAME_RELEASE}
- exit ;;
- *:NonStop-UX:*:*)
- echo mips-compaq-nonstopux
- exit ;;
- BS2000:POSIX*:*:*)
- echo bs2000-siemens-sysv
- exit ;;
- DS/*:UNIX_System_V:*:*)
- echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE}
- exit ;;
- *:Plan9:*:*)
- # "uname -m" is not consistent, so use $cputype instead. 386
- # is converted to i386 for consistency with other x86
- # operating systems.
- if test "$cputype" = "386"; then
- UNAME_MACHINE=i386
- else
- UNAME_MACHINE="$cputype"
- fi
- echo ${UNAME_MACHINE}-unknown-plan9
- exit ;;
- *:TOPS-10:*:*)
- echo pdp10-unknown-tops10
- exit ;;
- *:TENEX:*:*)
- echo pdp10-unknown-tenex
- exit ;;
- KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
- echo pdp10-dec-tops20
- exit ;;
- XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
- echo pdp10-xkl-tops20
- exit ;;
- *:TOPS-20:*:*)
- echo pdp10-unknown-tops20
- exit ;;
- *:ITS:*:*)
- echo pdp10-unknown-its
- exit ;;
- SEI:*:*:SEIUX)
- echo mips-sei-seiux${UNAME_RELEASE}
- exit ;;
- *:DragonFly:*:*)
- echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
- exit ;;
- *:*VMS:*:*)
- UNAME_MACHINE=`(uname -p) 2>/dev/null`
- case "${UNAME_MACHINE}" in
- A*) echo alpha-dec-vms ; exit ;;
- I*) echo ia64-dec-vms ; exit ;;
- V*) echo vax-dec-vms ; exit ;;
- esac ;;
- *:XENIX:*:SysV)
- echo i386-pc-xenix
- exit ;;
- i*86:skyos:*:*)
- echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//'
- exit ;;
- i*86:rdos:*:*)
- echo ${UNAME_MACHINE}-pc-rdos
- exit ;;
-esac
-
-#echo '(No uname command or uname output not recognized.)' 1>&2
-#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2
-
-eval $set_cc_for_build
-cat >$dummy.c <<EOF
-#ifdef _SEQUENT_
-# include <sys/types.h>
-# include <sys/utsname.h>
-#endif
-main ()
-{
-#if defined (sony)
-#if defined (MIPSEB)
- /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed,
- I don't know.... */
- printf ("mips-sony-bsd\n"); exit (0);
-#else
-#include <sys/param.h>
- printf ("m68k-sony-newsos%s\n",
-#ifdef NEWSOS4
- "4"
-#else
- ""
-#endif
- ); exit (0);
-#endif
-#endif
-
-#if defined (__arm) && defined (__acorn) && defined (__unix)
- printf ("arm-acorn-riscix\n"); exit (0);
-#endif
-
-#if defined (hp300) && !defined (hpux)
- printf ("m68k-hp-bsd\n"); exit (0);
-#endif
-
-#if defined (NeXT)
-#if !defined (__ARCHITECTURE__)
-#define __ARCHITECTURE__ "m68k"
-#endif
- int version;
- version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
- if (version < 4)
- printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
- else
- printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
- exit (0);
-#endif
-
-#if defined (MULTIMAX) || defined (n16)
-#if defined (UMAXV)
- printf ("ns32k-encore-sysv\n"); exit (0);
-#else
-#if defined (CMU)
- printf ("ns32k-encore-mach\n"); exit (0);
-#else
- printf ("ns32k-encore-bsd\n"); exit (0);
-#endif
-#endif
-#endif
-
-#if defined (__386BSD__)
- printf ("i386-pc-bsd\n"); exit (0);
-#endif
-
-#if defined (sequent)
-#if defined (i386)
- printf ("i386-sequent-dynix\n"); exit (0);
-#endif
-#if defined (ns32000)
- printf ("ns32k-sequent-dynix\n"); exit (0);
-#endif
-#endif
-
-#if defined (_SEQUENT_)
- struct utsname un;
-
- uname(&un);
-
- if (strncmp(un.version, "V2", 2) == 0) {
- printf ("i386-sequent-ptx2\n"); exit (0);
- }
- if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
- printf ("i386-sequent-ptx1\n"); exit (0);
- }
- printf ("i386-sequent-ptx\n"); exit (0);
-
-#endif
-
-#if defined (vax)
-# if !defined (ultrix)
-# include <sys/param.h>
-# if defined (BSD)
-# if BSD == 43
- printf ("vax-dec-bsd4.3\n"); exit (0);
-# else
-# if BSD == 199006
- printf ("vax-dec-bsd4.3reno\n"); exit (0);
-# else
- printf ("vax-dec-bsd\n"); exit (0);
-# endif
-# endif
-# else
- printf ("vax-dec-bsd\n"); exit (0);
-# endif
-# else
- printf ("vax-dec-ultrix\n"); exit (0);
-# endif
-#endif
-
-#if defined (alliant) && defined (i860)
- printf ("i860-alliant-bsd\n"); exit (0);
-#endif
-
- exit (1);
-}
-EOF
-
-$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` &&
- { echo "$SYSTEM_NAME"; exit; }
-
-# Apollos put the system type in the environment.
-
-test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; }
-
-# Convex versions that predate uname can use getsysinfo(1)
-
-if [ -x /usr/convex/getsysinfo ]
-then
- case `getsysinfo -f cpu_type` in
- c1*)
- echo c1-convex-bsd
- exit ;;
- c2*)
- if getsysinfo -f scalar_acc
- then echo c32-convex-bsd
- else echo c2-convex-bsd
- fi
- exit ;;
- c34*)
- echo c34-convex-bsd
- exit ;;
- c38*)
- echo c38-convex-bsd
- exit ;;
- c4*)
- echo c4-convex-bsd
- exit ;;
- esac
-fi
-
-cat >&2 <<EOF
-$0: unable to guess system type
-
-This script, last modified $timestamp, has failed to recognize
-the operating system you are using. It is advised that you
-download the most up to date version of the config scripts from
-
- http://savannah.gnu.org/cgi-bin/viewcvs/*checkout*/config/config/config.guess
-and
- http://savannah.gnu.org/cgi-bin/viewcvs/*checkout*/config/config/config.sub
-
-If the version you run ($0) is already up to date, please
-send the following data and any information you think might be
-pertinent to <[email protected]> in order to provide the needed
-information to handle your system.
-
-config.guess timestamp = $timestamp
-
-uname -m = `(uname -m) 2>/dev/null || echo unknown`
-uname -r = `(uname -r) 2>/dev/null || echo unknown`
-uname -s = `(uname -s) 2>/dev/null || echo unknown`
-uname -v = `(uname -v) 2>/dev/null || echo unknown`
-
-/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`
-/bin/uname -X = `(/bin/uname -X) 2>/dev/null`
-
-hostinfo = `(hostinfo) 2>/dev/null`
-/bin/universe = `(/bin/universe) 2>/dev/null`
-/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null`
-/bin/arch = `(/bin/arch) 2>/dev/null`
-/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null`
-/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
-
-UNAME_MACHINE = ${UNAME_MACHINE}
-UNAME_RELEASE = ${UNAME_RELEASE}
-UNAME_SYSTEM = ${UNAME_SYSTEM}
-UNAME_VERSION = ${UNAME_VERSION}
-EOF
-
-exit 1
-
-# Local variables:
-# eval: (add-hook 'write-file-hooks 'time-stamp)
-# time-stamp-start: "timestamp='"
-# time-stamp-format: "%:y-%02m-%02d"
-# time-stamp-end: "'"
-# End:
diff --git a/lib/diameter/autoconf/config.sub b/lib/diameter/autoconf/config.sub
deleted file mode 100755
index f43233b104..0000000000
--- a/lib/diameter/autoconf/config.sub
+++ /dev/null
@@ -1,1630 +0,0 @@
-#! /bin/sh
-# Configuration validation subroutine script.
-# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
-# 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation,
-# Inc.
-
-timestamp='2007-04-29'
-
-# This file is (in principle) common to ALL GNU software.
-# The presence of a machine in this file suggests that SOME GNU software
-# can handle that machine. It does not imply ALL GNU software can.
-#
-# This file is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program 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 General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
-# 02110-1301, USA.
-#
-# As a special exception to the GNU General Public License, if you
-# distribute this file as part of a program that contains a
-# configuration script generated by Autoconf, you may include it under
-# the same distribution terms that you use for the rest of that program.
-
-
-# Please send patches to <[email protected]>. Submit a context
-# diff and a properly formatted ChangeLog entry.
-#
-# Configuration subroutine to validate and canonicalize a configuration type.
-# Supply the specified configuration type as an argument.
-# If it is invalid, we print an error message on stderr and exit with code 1.
-# Otherwise, we print the canonical config type on stdout and succeed.
-
-# This file is supposed to be the same for all GNU packages
-# and recognize all the CPU types, system types and aliases
-# that are meaningful with *any* GNU software.
-# Each package is responsible for reporting which valid configurations
-# it does not support. The user should be able to distinguish
-# a failure to support a valid configuration from a meaningless
-# configuration.
-
-# The goal of this file is to map all the various variations of a given
-# machine specification into a single specification in the form:
-# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
-# or in some cases, the newer four-part form:
-# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
-# It is wrong to echo any other type of specification.
-
-me=`echo "$0" | sed -e 's,.*/,,'`
-
-usage="\
-Usage: $0 [OPTION] CPU-MFR-OPSYS
- $0 [OPTION] ALIAS
-
-Canonicalize a configuration name.
-
-Operation modes:
- -h, --help print this help, then exit
- -t, --time-stamp print date of last modification, then exit
- -v, --version print version number, then exit
-
-Report bugs and patches to <[email protected]>."
-
-version="\
-GNU config.sub ($timestamp)
-
-Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
-Free Software Foundation, Inc.
-
-This is free software; see the source for copying conditions. There is NO
-warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
-
-help="
-Try \`$me --help' for more information."
-
-# Parse command line
-while test $# -gt 0 ; do
- case $1 in
- --time-stamp | --time* | -t )
- echo "$timestamp" ; exit ;;
- --version | -v )
- echo "$version" ; exit ;;
- --help | --h* | -h )
- echo "$usage"; exit ;;
- -- ) # Stop option processing
- shift; break ;;
- - ) # Use stdin as input.
- break ;;
- -* )
- echo "$me: invalid option $1$help"
- exit 1 ;;
-
- *local*)
- # First pass through any local machine types.
- echo $1
- exit ;;
-
- * )
- break ;;
- esac
-done
-
-case $# in
- 0) echo "$me: missing argument$help" >&2
- exit 1;;
- 1) ;;
- *) echo "$me: too many arguments$help" >&2
- exit 1;;
-esac
-
-# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).
-# Here we must recognize all the valid KERNEL-OS combinations.
-maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
-case $maybe_os in
- nto-qnx* | linux-gnu* | linux-dietlibc | linux-newlib* | linux-uclibc* | \
- uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | \
- storm-chaos* | os2-emx* | rtmk-nova*)
- os=-$maybe_os
- basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
- ;;
- *)
- basic_machine=`echo $1 | sed 's/-[^-]*$//'`
- if [ $basic_machine != $1 ]
- then os=`echo $1 | sed 's/.*-/-/'`
- else os=; fi
- ;;
-esac
-
-### Let's recognize common machines as not being operating systems so
-### that things like config.sub decstation-3100 work. We also
-### recognize some manufacturers as not being operating systems, so we
-### can provide default operating systems below.
-case $os in
- -sun*os*)
- # Prevent following clause from handling this invalid input.
- ;;
- -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \
- -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \
- -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \
- -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
- -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
- -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
- -apple | -axis | -knuth | -cray)
- os=
- basic_machine=$1
- ;;
- -sim | -cisco | -oki | -wec | -winbond)
- os=
- basic_machine=$1
- ;;
- -scout)
- ;;
- -wrs)
- os=-vxworks
- basic_machine=$1
- ;;
- -chorusos*)
- os=-chorusos
- basic_machine=$1
- ;;
- -chorusrdb)
- os=-chorusrdb
- basic_machine=$1
- ;;
- -hiux*)
- os=-hiuxwe2
- ;;
- -sco6)
- os=-sco5v6
- basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
- ;;
- -sco5)
- os=-sco3.2v5
- basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
- ;;
- -sco4)
- os=-sco3.2v4
- basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
- ;;
- -sco3.2.[4-9]*)
- os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
- basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
- ;;
- -sco3.2v[4-9]*)
- # Don't forget version if it is 3.2v4 or newer.
- basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
- ;;
- -sco5v6*)
- # Don't forget version if it is 3.2v4 or newer.
- basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
- ;;
- -sco*)
- os=-sco3.2v2
- basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
- ;;
- -udk*)
- basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
- ;;
- -isc)
- os=-isc2.2
- basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
- ;;
- -clix*)
- basic_machine=clipper-intergraph
- ;;
- -isc*)
- basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
- ;;
- -lynx*)
- os=-lynxos
- ;;
- -ptx*)
- basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'`
- ;;
- -windowsnt*)
- os=`echo $os | sed -e 's/windowsnt/winnt/'`
- ;;
- -psos*)
- os=-psos
- ;;
- -mint | -mint[0-9]*)
- basic_machine=m68k-atari
- os=-mint
- ;;
-esac
-
-# Decode aliases for certain CPU-COMPANY combinations.
-case $basic_machine in
- # Recognize the basic CPU types without company name.
- # Some are omitted here because they have special meanings below.
- 1750a | 580 \
- | a29k \
- | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
- | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
- | am33_2.0 \
- | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr | avr32 \
- | bfin \
- | c4x | clipper \
- | d10v | d30v | dlx | dsp16xx \
- | fido | fr30 | frv \
- | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
- | i370 | i860 | i960 | ia64 \
- | ip2k | iq2000 \
- | m32c | m32r | m32rle | m68000 | m68k | m88k \
- | maxq | mb | microblaze | mcore | mep \
- | mips | mipsbe | mipseb | mipsel | mipsle \
- | mips16 \
- | mips64 | mips64el \
- | mips64vr | mips64vrel \
- | mips64orion | mips64orionel \
- | mips64vr4100 | mips64vr4100el \
- | mips64vr4300 | mips64vr4300el \
- | mips64vr5000 | mips64vr5000el \
- | mips64vr5900 | mips64vr5900el \
- | mipsisa32 | mipsisa32el \
- | mipsisa32r2 | mipsisa32r2el \
- | mipsisa64 | mipsisa64el \
- | mipsisa64r2 | mipsisa64r2el \
- | mipsisa64sb1 | mipsisa64sb1el \
- | mipsisa64sr71k | mipsisa64sr71kel \
- | mipstx39 | mipstx39el \
- | mn10200 | mn10300 \
- | mt \
- | msp430 \
- | nios | nios2 \
- | ns16k | ns32k \
- | or32 \
- | pdp10 | pdp11 | pj | pjl \
- | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \
- | pyramid \
- | score \
- | sh | sh[1234] | sh[24]a | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \
- | sh64 | sh64le \
- | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \
- | sparcv8 | sparcv9 | sparcv9b | sparcv9v \
- | spu | strongarm \
- | tahoe | thumb | tic4x | tic80 | tron \
- | v850 | v850e \
- | we32k \
- | x86 | xc16x | xscale | xscalee[bl] | xstormy16 | xtensa \
- | z8k)
- basic_machine=$basic_machine-unknown
- ;;
- m6811 | m68hc11 | m6812 | m68hc12)
- # Motorola 68HC11/12.
- basic_machine=$basic_machine-unknown
- os=-none
- ;;
- m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k)
- ;;
- ms1)
- basic_machine=mt-unknown
- ;;
-
- # We use `pc' rather than `unknown'
- # because (1) that's what they normally are, and
- # (2) the word "unknown" tends to confuse beginning users.
- i*86 | x86_64)
- basic_machine=$basic_machine-pc
- ;;
- # Object if more than one company name word.
- *-*-*)
- echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
- exit 1
- ;;
- # Recognize the basic CPU types with company name.
- 580-* \
- | a29k-* \
- | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
- | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
- | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \
- | arm-* | armbe-* | armle-* | armeb-* | armv*-* \
- | avr-* | avr32-* \
- | bfin-* | bs2000-* \
- | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \
- | clipper-* | craynv-* | cydra-* \
- | d10v-* | d30v-* | dlx-* \
- | elxsi-* \
- | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \
- | h8300-* | h8500-* \
- | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
- | i*86-* | i860-* | i960-* | ia64-* \
- | ip2k-* | iq2000-* \
- | m32c-* | m32r-* | m32rle-* \
- | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
- | m88110-* | m88k-* | maxq-* | mcore-* \
- | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
- | mips16-* \
- | mips64-* | mips64el-* \
- | mips64vr-* | mips64vrel-* \
- | mips64orion-* | mips64orionel-* \
- | mips64vr4100-* | mips64vr4100el-* \
- | mips64vr4300-* | mips64vr4300el-* \
- | mips64vr5000-* | mips64vr5000el-* \
- | mips64vr5900-* | mips64vr5900el-* \
- | mipsisa32-* | mipsisa32el-* \
- | mipsisa32r2-* | mipsisa32r2el-* \
- | mipsisa64-* | mipsisa64el-* \
- | mipsisa64r2-* | mipsisa64r2el-* \
- | mipsisa64sb1-* | mipsisa64sb1el-* \
- | mipsisa64sr71k-* | mipsisa64sr71kel-* \
- | mipstx39-* | mipstx39el-* \
- | mmix-* \
- | mt-* \
- | msp430-* \
- | nios-* | nios2-* \
- | none-* | np1-* | ns16k-* | ns32k-* \
- | orion-* \
- | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
- | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \
- | pyramid-* \
- | romp-* | rs6000-* \
- | sh-* | sh[1234]-* | sh[24]a-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \
- | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
- | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \
- | sparclite-* \
- | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | strongarm-* | sv1-* | sx?-* \
- | tahoe-* | thumb-* \
- | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
- | tron-* \
- | v850-* | v850e-* | vax-* \
- | we32k-* \
- | x86-* | x86_64-* | xc16x-* | xps100-* | xscale-* | xscalee[bl]-* \
- | xstormy16-* | xtensa-* \
- | ymp-* \
- | z8k-*)
- ;;
- # Recognize the various machine names and aliases which stand
- # for a CPU type and a company and sometimes even an OS.
- 386bsd)
- basic_machine=i386-unknown
- os=-bsd
- ;;
- 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
- basic_machine=m68000-att
- ;;
- 3b*)
- basic_machine=we32k-att
- ;;
- a29khif)
- basic_machine=a29k-amd
- os=-udi
- ;;
- abacus)
- basic_machine=abacus-unknown
- ;;
- adobe68k)
- basic_machine=m68010-adobe
- os=-scout
- ;;
- alliant | fx80)
- basic_machine=fx80-alliant
- ;;
- altos | altos3068)
- basic_machine=m68k-altos
- ;;
- am29k)
- basic_machine=a29k-none
- os=-bsd
- ;;
- amd64)
- basic_machine=x86_64-pc
- ;;
- amd64-*)
- basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'`
- ;;
- amdahl)
- basic_machine=580-amdahl
- os=-sysv
- ;;
- amiga | amiga-*)
- basic_machine=m68k-unknown
- ;;
- amigaos | amigados)
- basic_machine=m68k-unknown
- os=-amigaos
- ;;
- amigaunix | amix)
- basic_machine=m68k-unknown
- os=-sysv4
- ;;
- apollo68)
- basic_machine=m68k-apollo
- os=-sysv
- ;;
- apollo68bsd)
- basic_machine=m68k-apollo
- os=-bsd
- ;;
- aux)
- basic_machine=m68k-apple
- os=-aux
- ;;
- balance)
- basic_machine=ns32k-sequent
- os=-dynix
- ;;
- c90)
- basic_machine=c90-cray
- os=-unicos
- ;;
- convex-c1)
- basic_machine=c1-convex
- os=-bsd
- ;;
- convex-c2)
- basic_machine=c2-convex
- os=-bsd
- ;;
- convex-c32)
- basic_machine=c32-convex
- os=-bsd
- ;;
- convex-c34)
- basic_machine=c34-convex
- os=-bsd
- ;;
- convex-c38)
- basic_machine=c38-convex
- os=-bsd
- ;;
- cray | j90)
- basic_machine=j90-cray
- os=-unicos
- ;;
- craynv)
- basic_machine=craynv-cray
- os=-unicosmp
- ;;
- cr16c)
- basic_machine=cr16c-unknown
- os=-elf
- ;;
- crds | unos)
- basic_machine=m68k-crds
- ;;
- crisv32 | crisv32-* | etraxfs*)
- basic_machine=crisv32-axis
- ;;
- cris | cris-* | etrax*)
- basic_machine=cris-axis
- ;;
- crx)
- basic_machine=crx-unknown
- os=-elf
- ;;
- da30 | da30-*)
- basic_machine=m68k-da30
- ;;
- decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)
- basic_machine=mips-dec
- ;;
- decsystem10* | dec10*)
- basic_machine=pdp10-dec
- os=-tops10
- ;;
- decsystem20* | dec20*)
- basic_machine=pdp10-dec
- os=-tops20
- ;;
- delta | 3300 | motorola-3300 | motorola-delta \
- | 3300-motorola | delta-motorola)
- basic_machine=m68k-motorola
- ;;
- delta88)
- basic_machine=m88k-motorola
- os=-sysv3
- ;;
- djgpp)
- basic_machine=i586-pc
- os=-msdosdjgpp
- ;;
- dpx20 | dpx20-*)
- basic_machine=rs6000-bull
- os=-bosx
- ;;
- dpx2* | dpx2*-bull)
- basic_machine=m68k-bull
- os=-sysv3
- ;;
- ebmon29k)
- basic_machine=a29k-amd
- os=-ebmon
- ;;
- elxsi)
- basic_machine=elxsi-elxsi
- os=-bsd
- ;;
- encore | umax | mmax)
- basic_machine=ns32k-encore
- ;;
- es1800 | OSE68k | ose68k | ose | OSE)
- basic_machine=m68k-ericsson
- os=-ose
- ;;
- fx2800)
- basic_machine=i860-alliant
- ;;
- genix)
- basic_machine=ns32k-ns
- ;;
- gmicro)
- basic_machine=tron-gmicro
- os=-sysv
- ;;
- go32)
- basic_machine=i386-pc
- os=-go32
- ;;
- h3050r* | hiux*)
- basic_machine=hppa1.1-hitachi
- os=-hiuxwe2
- ;;
- h8300hms)
- basic_machine=h8300-hitachi
- os=-hms
- ;;
- h8300xray)
- basic_machine=h8300-hitachi
- os=-xray
- ;;
- h8500hms)
- basic_machine=h8500-hitachi
- os=-hms
- ;;
- harris)
- basic_machine=m88k-harris
- os=-sysv3
- ;;
- hp300-*)
- basic_machine=m68k-hp
- ;;
- hp300bsd)
- basic_machine=m68k-hp
- os=-bsd
- ;;
- hp300hpux)
- basic_machine=m68k-hp
- os=-hpux
- ;;
- hp3k9[0-9][0-9] | hp9[0-9][0-9])
- basic_machine=hppa1.0-hp
- ;;
- hp9k2[0-9][0-9] | hp9k31[0-9])
- basic_machine=m68000-hp
- ;;
- hp9k3[2-9][0-9])
- basic_machine=m68k-hp
- ;;
- hp9k6[0-9][0-9] | hp6[0-9][0-9])
- basic_machine=hppa1.0-hp
- ;;
- hp9k7[0-79][0-9] | hp7[0-79][0-9])
- basic_machine=hppa1.1-hp
- ;;
- hp9k78[0-9] | hp78[0-9])
- # FIXME: really hppa2.0-hp
- basic_machine=hppa1.1-hp
- ;;
- hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
- # FIXME: really hppa2.0-hp
- basic_machine=hppa1.1-hp
- ;;
- hp9k8[0-9][13679] | hp8[0-9][13679])
- basic_machine=hppa1.1-hp
- ;;
- hp9k8[0-9][0-9] | hp8[0-9][0-9])
- basic_machine=hppa1.0-hp
- ;;
- hppa-next)
- os=-nextstep3
- ;;
- hppaosf)
- basic_machine=hppa1.1-hp
- os=-osf
- ;;
- hppro)
- basic_machine=hppa1.1-hp
- os=-proelf
- ;;
- i370-ibm* | ibm*)
- basic_machine=i370-ibm
- ;;
-# I'm not sure what "Sysv32" means. Should this be sysv3.2?
- i*86v32)
- basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
- os=-sysv32
- ;;
- i*86v4*)
- basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
- os=-sysv4
- ;;
- i*86v)
- basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
- os=-sysv
- ;;
- i*86sol2)
- basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
- os=-solaris2
- ;;
- i386mach)
- basic_machine=i386-mach
- os=-mach
- ;;
- i386-vsta | vsta)
- basic_machine=i386-unknown
- os=-vsta
- ;;
- iris | iris4d)
- basic_machine=mips-sgi
- case $os in
- -irix*)
- ;;
- *)
- os=-irix4
- ;;
- esac
- ;;
- isi68 | isi)
- basic_machine=m68k-isi
- os=-sysv
- ;;
- m88k-omron*)
- basic_machine=m88k-omron
- ;;
- magnum | m3230)
- basic_machine=mips-mips
- os=-sysv
- ;;
- merlin)
- basic_machine=ns32k-utek
- os=-sysv
- ;;
- mingw32)
- basic_machine=i386-pc
- os=-mingw32
- ;;
- mingw32ce)
- basic_machine=arm-unknown
- os=-mingw32ce
- ;;
- miniframe)
- basic_machine=m68000-convergent
- ;;
- *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*)
- basic_machine=m68k-atari
- os=-mint
- ;;
- mips3*-*)
- basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`
- ;;
- mips3*)
- basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown
- ;;
- monitor)
- basic_machine=m68k-rom68k
- os=-coff
- ;;
- morphos)
- basic_machine=powerpc-unknown
- os=-morphos
- ;;
- msdos)
- basic_machine=i386-pc
- os=-msdos
- ;;
- ms1-*)
- basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'`
- ;;
- mvs)
- basic_machine=i370-ibm
- os=-mvs
- ;;
- ncr3000)
- basic_machine=i486-ncr
- os=-sysv4
- ;;
- netbsd386)
- basic_machine=i386-unknown
- os=-netbsd
- ;;
- netwinder)
- basic_machine=armv4l-rebel
- os=-linux
- ;;
- news | news700 | news800 | news900)
- basic_machine=m68k-sony
- os=-newsos
- ;;
- news1000)
- basic_machine=m68030-sony
- os=-newsos
- ;;
- news-3600 | risc-news)
- basic_machine=mips-sony
- os=-newsos
- ;;
- necv70)
- basic_machine=v70-nec
- os=-sysv
- ;;
- next | m*-next )
- basic_machine=m68k-next
- case $os in
- -nextstep* )
- ;;
- -ns2*)
- os=-nextstep2
- ;;
- *)
- os=-nextstep3
- ;;
- esac
- ;;
- nh3000)
- basic_machine=m68k-harris
- os=-cxux
- ;;
- nh[45]000)
- basic_machine=m88k-harris
- os=-cxux
- ;;
- nindy960)
- basic_machine=i960-intel
- os=-nindy
- ;;
- mon960)
- basic_machine=i960-intel
- os=-mon960
- ;;
- nonstopux)
- basic_machine=mips-compaq
- os=-nonstopux
- ;;
- np1)
- basic_machine=np1-gould
- ;;
- nsr-tandem)
- basic_machine=nsr-tandem
- ;;
- op50n-* | op60c-*)
- basic_machine=hppa1.1-oki
- os=-proelf
- ;;
- openrisc | openrisc-*)
- basic_machine=or32-unknown
- ;;
- os400)
- basic_machine=powerpc-ibm
- os=-os400
- ;;
- OSE68000 | ose68000)
- basic_machine=m68000-ericsson
- os=-ose
- ;;
- os68k)
- basic_machine=m68k-none
- os=-os68k
- ;;
- pa-hitachi)
- basic_machine=hppa1.1-hitachi
- os=-hiuxwe2
- ;;
- paragon)
- basic_machine=i860-intel
- os=-osf
- ;;
- pbd)
- basic_machine=sparc-tti
- ;;
- pbb)
- basic_machine=m68k-tti
- ;;
- pc532 | pc532-*)
- basic_machine=ns32k-pc532
- ;;
- pc98)
- basic_machine=i386-pc
- ;;
- pc98-*)
- basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'`
- ;;
- pentium | p5 | k5 | k6 | nexgen | viac3)
- basic_machine=i586-pc
- ;;
- pentiumpro | p6 | 6x86 | athlon | athlon_*)
- basic_machine=i686-pc
- ;;
- pentiumii | pentium2 | pentiumiii | pentium3)
- basic_machine=i686-pc
- ;;
- pentium4)
- basic_machine=i786-pc
- ;;
- pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
- basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'`
- ;;
- pentiumpro-* | p6-* | 6x86-* | athlon-*)
- basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
- ;;
- pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*)
- basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
- ;;
- pentium4-*)
- basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'`
- ;;
- pn)
- basic_machine=pn-gould
- ;;
- power) basic_machine=power-ibm
- ;;
- ppc) basic_machine=powerpc-unknown
- ;;
- ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
- ;;
- ppcle | powerpclittle | ppc-le | powerpc-little)
- basic_machine=powerpcle-unknown
- ;;
- ppcle-* | powerpclittle-*)
- basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'`
- ;;
- ppc64) basic_machine=powerpc64-unknown
- ;;
- ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'`
- ;;
- ppc64le | powerpc64little | ppc64-le | powerpc64-little)
- basic_machine=powerpc64le-unknown
- ;;
- ppc64le-* | powerpc64little-*)
- basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'`
- ;;
- ps2)
- basic_machine=i386-ibm
- ;;
- pw32)
- basic_machine=i586-unknown
- os=-pw32
- ;;
- rdos)
- basic_machine=i386-pc
- os=-rdos
- ;;
- rom68k)
- basic_machine=m68k-rom68k
- os=-coff
- ;;
- rm[46]00)
- basic_machine=mips-siemens
- ;;
- rtpc | rtpc-*)
- basic_machine=romp-ibm
- ;;
- s390 | s390-*)
- basic_machine=s390-ibm
- ;;
- s390x | s390x-*)
- basic_machine=s390x-ibm
- ;;
- sa29200)
- basic_machine=a29k-amd
- os=-udi
- ;;
- sb1)
- basic_machine=mipsisa64sb1-unknown
- ;;
- sb1el)
- basic_machine=mipsisa64sb1el-unknown
- ;;
- sde)
- basic_machine=mipsisa32-sde
- os=-elf
- ;;
- sei)
- basic_machine=mips-sei
- os=-seiux
- ;;
- sequent)
- basic_machine=i386-sequent
- ;;
- sh)
- basic_machine=sh-hitachi
- os=-hms
- ;;
- sh5el)
- basic_machine=sh5le-unknown
- ;;
- sh64)
- basic_machine=sh64-unknown
- ;;
- sparclite-wrs | simso-wrs)
- basic_machine=sparclite-wrs
- os=-vxworks
- ;;
- sps7)
- basic_machine=m68k-bull
- os=-sysv2
- ;;
- spur)
- basic_machine=spur-unknown
- ;;
- st2000)
- basic_machine=m68k-tandem
- ;;
- stratus)
- basic_machine=i860-stratus
- os=-sysv4
- ;;
- sun2)
- basic_machine=m68000-sun
- ;;
- sun2os3)
- basic_machine=m68000-sun
- os=-sunos3
- ;;
- sun2os4)
- basic_machine=m68000-sun
- os=-sunos4
- ;;
- sun3os3)
- basic_machine=m68k-sun
- os=-sunos3
- ;;
- sun3os4)
- basic_machine=m68k-sun
- os=-sunos4
- ;;
- sun4os3)
- basic_machine=sparc-sun
- os=-sunos3
- ;;
- sun4os4)
- basic_machine=sparc-sun
- os=-sunos4
- ;;
- sun4sol2)
- basic_machine=sparc-sun
- os=-solaris2
- ;;
- sun3 | sun3-*)
- basic_machine=m68k-sun
- ;;
- sun4)
- basic_machine=sparc-sun
- ;;
- sun386 | sun386i | roadrunner)
- basic_machine=i386-sun
- ;;
- sv1)
- basic_machine=sv1-cray
- os=-unicos
- ;;
- symmetry)
- basic_machine=i386-sequent
- os=-dynix
- ;;
- t3e)
- basic_machine=alphaev5-cray
- os=-unicos
- ;;
- t90)
- basic_machine=t90-cray
- os=-unicos
- ;;
- tic54x | c54x*)
- basic_machine=tic54x-unknown
- os=-coff
- ;;
- tic55x | c55x*)
- basic_machine=tic55x-unknown
- os=-coff
- ;;
- tic6x | c6x*)
- basic_machine=tic6x-unknown
- os=-coff
- ;;
- tx39)
- basic_machine=mipstx39-unknown
- ;;
- tx39el)
- basic_machine=mipstx39el-unknown
- ;;
- tile*)
- basic_machine=tile-tilera
- os=-linux-gnu
- ;;
- toad1)
- basic_machine=pdp10-xkl
- os=-tops20
- ;;
- tower | tower-32)
- basic_machine=m68k-ncr
- ;;
- tpf)
- basic_machine=s390x-ibm
- os=-tpf
- ;;
- udi29k)
- basic_machine=a29k-amd
- os=-udi
- ;;
- ultra3)
- basic_machine=a29k-nyu
- os=-sym1
- ;;
- v810 | necv810)
- basic_machine=v810-nec
- os=-none
- ;;
- vaxv)
- basic_machine=vax-dec
- os=-sysv
- ;;
- vms)
- basic_machine=vax-dec
- os=-vms
- ;;
- vpp*|vx|vx-*)
- basic_machine=f301-fujitsu
- ;;
- vxworks960)
- basic_machine=i960-wrs
- os=-vxworks
- ;;
- vxworks68)
- basic_machine=m68k-wrs
- os=-vxworks
- ;;
- vxworks29k)
- basic_machine=a29k-wrs
- os=-vxworks
- ;;
- w65*)
- basic_machine=w65-wdc
- os=-none
- ;;
- w89k-*)
- basic_machine=hppa1.1-winbond
- os=-proelf
- ;;
- xbox)
- basic_machine=i686-pc
- os=-mingw32
- ;;
- xps | xps100)
- basic_machine=xps100-honeywell
- ;;
- ymp)
- basic_machine=ymp-cray
- os=-unicos
- ;;
- z8k-*-coff)
- basic_machine=z8k-unknown
- os=-sim
- ;;
- none)
- basic_machine=none-none
- os=-none
- ;;
-
-# Here we handle the default manufacturer of certain CPU types. It is in
-# some cases the only manufacturer, in others, it is the most popular.
- w89k)
- basic_machine=hppa1.1-winbond
- ;;
- op50n)
- basic_machine=hppa1.1-oki
- ;;
- op60c)
- basic_machine=hppa1.1-oki
- ;;
- romp)
- basic_machine=romp-ibm
- ;;
- mmix)
- basic_machine=mmix-knuth
- ;;
- rs6000)
- basic_machine=rs6000-ibm
- ;;
- vax)
- basic_machine=vax-dec
- ;;
- pdp10)
- # there are many clones, so DEC is not a safe bet
- basic_machine=pdp10-unknown
- ;;
- pdp11)
- basic_machine=pdp11-dec
- ;;
- we32k)
- basic_machine=we32k-att
- ;;
- sh[1234] | sh[24]a | sh[34]eb | sh[1234]le | sh[23]ele)
- basic_machine=sh-unknown
- ;;
- sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v)
- basic_machine=sparc-sun
- ;;
- cydra)
- basic_machine=cydra-cydrome
- ;;
- orion)
- basic_machine=orion-highlevel
- ;;
- orion105)
- basic_machine=clipper-highlevel
- ;;
- mac | mpw | mac-mpw)
- basic_machine=m68k-apple
- ;;
- pmac | pmac-mpw)
- basic_machine=powerpc-apple
- ;;
- *-unknown)
- # Make sure to match an already-canonicalized machine name.
- ;;
- *)
- echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
- exit 1
- ;;
-esac
-
-# Here we canonicalize certain aliases for manufacturers.
-case $basic_machine in
- *-digital*)
- basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'`
- ;;
- *-commodore*)
- basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'`
- ;;
- *)
- ;;
-esac
-
-# Decode manufacturer-specific aliases for certain operating systems.
-
-if [ x"$os" != x"" ]
-then
-case $os in
- # First match some system type aliases
- # that might get confused with valid system types.
- # -solaris* is a basic system type, with this one exception.
- -solaris1 | -solaris1.*)
- os=`echo $os | sed -e 's|solaris1|sunos4|'`
- ;;
- -solaris)
- os=-solaris2
- ;;
- -svr4*)
- os=-sysv4
- ;;
- -unixware*)
- os=-sysv4.2uw
- ;;
- -gnu/linux*)
- os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
- ;;
- # First accept the basic system types.
- # The portable systems comes first.
- # Each alternative MUST END IN A *, to match a version number.
- # -sysv* is not here because it comes later, after sysvr4.
- -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
- | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\
- | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \
- | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
- | -aos* \
- | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
- | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
- | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \
- | -openbsd* | -solidbsd* \
- | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \
- | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
- | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
- | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
- | -chorusos* | -chorusrdb* \
- | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
- | -mingw32* | -linux-gnu* | -linux-newlib* | -linux-uclibc* \
- | -uxpv* | -beos* | -mpeix* | -udk* \
- | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
- | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
- | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
- | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
- | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
- | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \
- | -skyos* | -haiku* | -rdos* | -toppers* | -drops*)
- # Remember, each alternative MUST END IN *, to match a version number.
- ;;
- -qnx*)
- case $basic_machine in
- x86-* | i*86-*)
- ;;
- *)
- os=-nto$os
- ;;
- esac
- ;;
- -nto-qnx*)
- ;;
- -nto*)
- os=`echo $os | sed -e 's|nto|nto-qnx|'`
- ;;
- -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \
- | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \
- | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*)
- ;;
- -mac*)
- os=`echo $os | sed -e 's|mac|macos|'`
- ;;
- -linux-dietlibc)
- os=-linux-dietlibc
- ;;
- -linux*)
- os=`echo $os | sed -e 's|linux|linux-gnu|'`
- ;;
- -sunos5*)
- os=`echo $os | sed -e 's|sunos5|solaris2|'`
- ;;
- -sunos6*)
- os=`echo $os | sed -e 's|sunos6|solaris3|'`
- ;;
- -opened*)
- os=-openedition
- ;;
- -os400*)
- os=-os400
- ;;
- -wince*)
- os=-wince
- ;;
- -osfrose*)
- os=-osfrose
- ;;
- -osf*)
- os=-osf
- ;;
- -utek*)
- os=-bsd
- ;;
- -dynix*)
- os=-bsd
- ;;
- -acis*)
- os=-aos
- ;;
- -atheos*)
- os=-atheos
- ;;
- -syllable*)
- os=-syllable
- ;;
- -386bsd)
- os=-bsd
- ;;
- -ctix* | -uts*)
- os=-sysv
- ;;
- -nova*)
- os=-rtmk-nova
- ;;
- -ns2 )
- os=-nextstep2
- ;;
- -nsk*)
- os=-nsk
- ;;
- # Preserve the version number of sinix5.
- -sinix5.*)
- os=`echo $os | sed -e 's|sinix|sysv|'`
- ;;
- -sinix*)
- os=-sysv4
- ;;
- -tpf*)
- os=-tpf
- ;;
- -triton*)
- os=-sysv3
- ;;
- -oss*)
- os=-sysv3
- ;;
- -svr4)
- os=-sysv4
- ;;
- -svr3)
- os=-sysv3
- ;;
- -sysvr4)
- os=-sysv4
- ;;
- # This must come after -sysvr4.
- -sysv*)
- ;;
- -ose*)
- os=-ose
- ;;
- -es1800*)
- os=-ose
- ;;
- -xenix)
- os=-xenix
- ;;
- -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
- os=-mint
- ;;
- -aros*)
- os=-aros
- ;;
- -kaos*)
- os=-kaos
- ;;
- -zvmoe)
- os=-zvmoe
- ;;
- -none)
- ;;
- *)
- # Get rid of the `-' at the beginning of $os.
- os=`echo $os | sed 's/[^-]*-//'`
- echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2
- exit 1
- ;;
-esac
-else
-
-# Here we handle the default operating systems that come with various machines.
-# The value should be what the vendor currently ships out the door with their
-# machine or put another way, the most popular os provided with the machine.
-
-# Note that if you're going to try to match "-MANUFACTURER" here (say,
-# "-sun"), then you have to tell the case statement up towards the top
-# that MANUFACTURER isn't an operating system. Otherwise, code above
-# will signal an error saying that MANUFACTURER isn't an operating
-# system, and we'll never get to this point.
-
-case $basic_machine in
- score-*)
- os=-elf
- ;;
- spu-*)
- os=-elf
- ;;
- *-acorn)
- os=-riscix1.2
- ;;
- arm*-rebel)
- os=-linux
- ;;
- arm*-semi)
- os=-aout
- ;;
- c4x-* | tic4x-*)
- os=-coff
- ;;
- # This must come before the *-dec entry.
- pdp10-*)
- os=-tops20
- ;;
- pdp11-*)
- os=-none
- ;;
- *-dec | vax-*)
- os=-ultrix4.2
- ;;
- m68*-apollo)
- os=-domain
- ;;
- i386-sun)
- os=-sunos4.0.2
- ;;
- m68000-sun)
- os=-sunos3
- # This also exists in the configure program, but was not the
- # default.
- # os=-sunos4
- ;;
- m68*-cisco)
- os=-aout
- ;;
- mep-*)
- os=-elf
- ;;
- mips*-cisco)
- os=-elf
- ;;
- mips*-*)
- os=-elf
- ;;
- or32-*)
- os=-coff
- ;;
- *-tti) # must be before sparc entry or we get the wrong os.
- os=-sysv3
- ;;
- sparc-* | *-sun)
- os=-sunos4.1.1
- ;;
- *-be)
- os=-beos
- ;;
- *-haiku)
- os=-haiku
- ;;
- *-ibm)
- os=-aix
- ;;
- *-knuth)
- os=-mmixware
- ;;
- *-wec)
- os=-proelf
- ;;
- *-winbond)
- os=-proelf
- ;;
- *-oki)
- os=-proelf
- ;;
- *-hp)
- os=-hpux
- ;;
- *-hitachi)
- os=-hiux
- ;;
- i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
- os=-sysv
- ;;
- *-cbm)
- os=-amigaos
- ;;
- *-dg)
- os=-dgux
- ;;
- *-dolphin)
- os=-sysv3
- ;;
- m68k-ccur)
- os=-rtu
- ;;
- m88k-omron*)
- os=-luna
- ;;
- *-next )
- os=-nextstep
- ;;
- *-sequent)
- os=-ptx
- ;;
- *-crds)
- os=-unos
- ;;
- *-ns)
- os=-genix
- ;;
- i370-*)
- os=-mvs
- ;;
- *-next)
- os=-nextstep3
- ;;
- *-gould)
- os=-sysv
- ;;
- *-highlevel)
- os=-bsd
- ;;
- *-encore)
- os=-bsd
- ;;
- *-sgi)
- os=-irix
- ;;
- *-siemens)
- os=-sysv4
- ;;
- *-masscomp)
- os=-rtu
- ;;
- f30[01]-fujitsu | f700-fujitsu)
- os=-uxpv
- ;;
- *-rom68k)
- os=-coff
- ;;
- *-*bug)
- os=-coff
- ;;
- *-apple)
- os=-macos
- ;;
- *-atari*)
- os=-mint
- ;;
- *)
- os=-none
- ;;
-esac
-fi
-
-# Here we handle the case where we know the os, and the CPU type, but not the
-# manufacturer. We pick the logical manufacturer.
-vendor=unknown
-case $basic_machine in
- *-unknown)
- case $os in
- -riscix*)
- vendor=acorn
- ;;
- -sunos*)
- vendor=sun
- ;;
- -aix*)
- vendor=ibm
- ;;
- -beos*)
- vendor=be
- ;;
- -hpux*)
- vendor=hp
- ;;
- -mpeix*)
- vendor=hp
- ;;
- -hiux*)
- vendor=hitachi
- ;;
- -unos*)
- vendor=crds
- ;;
- -dgux*)
- vendor=dg
- ;;
- -luna*)
- vendor=omron
- ;;
- -genix*)
- vendor=ns
- ;;
- -mvs* | -opened*)
- vendor=ibm
- ;;
- -os400*)
- vendor=ibm
- ;;
- -ptx*)
- vendor=sequent
- ;;
- -tpf*)
- vendor=ibm
- ;;
- -vxsim* | -vxworks* | -windiss*)
- vendor=wrs
- ;;
- -aux*)
- vendor=apple
- ;;
- -hms*)
- vendor=hitachi
- ;;
- -mpw* | -macos*)
- vendor=apple
- ;;
- -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
- vendor=atari
- ;;
- -vos*)
- vendor=stratus
- ;;
- esac
- basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`
- ;;
-esac
-
-echo $basic_machine$os
-exit
-
-# Local variables:
-# eval: (add-hook 'write-file-hooks 'time-stamp)
-# time-stamp-start: "timestamp='"
-# time-stamp-format: "%:y-%02m-%02d"
-# time-stamp-end: "'"
-# End:
diff --git a/lib/diameter/autoconf/configure.vxworks b/lib/diameter/autoconf/configure.vxworks
deleted file mode 100755
index 33aa497680..0000000000
--- a/lib/diameter/autoconf/configure.vxworks
+++ /dev/null
@@ -1,147 +0,0 @@
-#!/bin/sh
-#
-# %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%
-#
-# Author:
-# Patrik Winroth
-#
-
-
-# vxworks_ppc860 vxworks_ppc603 vxworks_ppc603_longcall vxworks_cpu32 vxworks_sparc
-# vxworks_ppc750 vxworks_simso
-
-case $# in
-1) host=$1 ;;
-*) echo "usage: configure.vxworks host-configuration"; exit 1 ;;
-esac
-
-case $1 in
-vxworks_cpu32) ;;
-vxworks_ppc750) ;;
-vxworks_ppc860) ;;
-vxworks_ppc603) ;;
-vxworks_ppc603_nolongcall) ;;
-vxworks_sparc) ;;
-vxworks_simso) ;;
-vxworks_simlinux) ;;
-vxworks_ppc32) ;;
-*) echo "usage: configure.vxworks TARGET";
- echo "where TARGET is one of vxworks_cpu32, vxworks_ppc750, vxworks_ppc860, vxworks_ppc603, vxworks_ppc603_nolongcall, vxworks_sparc, vxworks_simso, vxworks_simlinux, vxworks_ppc32"; exit 1;;
-esac
-
-if [ "x$ERL_TOP" = x ]; then
- echo "You need to set ERL_TOP!"
- exit 1
-fi
-
-
-target=$host
-
-# Find out the HOST and WIND_BASE environment
-HOST_TYPE=${HOST_TYPE:=sun4-solaris2}
-case $1 in
-vxworks_ppc750) VXTOP=Tornado2.2 ;;
-vxworks_simso) VXTOP=WindRiver ;;
-vxworks_simlinux) VXTOP=WindRiver ;;
-vxworks_ppc32) VXTOP=WindRiver ;;
-*) VXTOP=wind ;;
-esac
-
-WIND_BASE=${WIND_BASE:=`ypmatch tornado passwd | awk -F: '{print $6}'`/$VXTOP}
-
-# These are created by autoconf.
-MKDIRS="${ERL_TOP}/lib/os_mon/priv/bin/$target
- ${ERL_TOP}/lib/os_mon/priv/obj/$target
- ${ERL_TOP}/lib/orber/priv/obj/$target
- ${ERL_TOP}/lib/orber/priv/bin/$target
- ${ERL_TOP}/lib/ic/priv/lib/$target
- ${ERL_TOP}/lib/ic/priv/obj/$target
- ${ERL_TOP}/lib/asn1/priv/lib/$target
- ${ERL_TOP}/lib/asn1/priv/obj/$target
- ${ERL_TOP}/lib/erl_interface/obj/$target
- ${ERL_TOP}/lib/erl_interface/obj.debug/$target
- ${ERL_TOP}/lib/erl_interface/bin/$target
- ${ERL_TOP}/lib/runtime_tools/priv/lib/$target
- ${ERL_TOP}/lib/runtime_tools/priv/obj/$target
- ${ERL_TOP}/erts/obj/$target
- ${ERL_TOP}/erts/obj.debug/$target
- ${ERL_TOP}/bin/$target"
-
-for dir in $MKDIRS; do
- test ! -d "$dir" && mkdir -p "$dir"
-done
-
-#
-# Create Makefiles for vxWorks.
-#
-my_root=${ERL_TOP}/erts/emulator
-emu_test=$my_root/test
-beam=$my_root/beam
-erts_lib_src=${ERL_TOP}/erts/lib_src
-erts_incl=${ERL_TOP}/erts/include
-erts_incl_intrnl=${ERL_TOP}/erts/include/internal
-etcdir=${ERL_TOP}/erts/etc/common
-erlint_dir=${ERL_TOP}/lib/erl_interface/src
-epmd_dir=${ERL_TOP}/erts/epmd/src
-os_mon_dir=${ERL_TOP}/lib/os_mon/c_src
-orber_dir=${ERL_TOP}/lib/orber/c_src
-ic_dir=${ERL_TOP}/lib/ic/c_src
-asn1_dir=${ERL_TOP}/lib/asn1/c_src
-internal_tools_dir=${ERL_TOP}
-libdir=${ERL_TOP}/lib
-tsdir=$libdir/test_server/src
-zlibdir=${ERL_TOP}/erts/emulator/zlib
-runtime_tools_dir=${ERL_TOP}/lib/runtime_tools/c_src
-tools_dir=${ERL_TOP}/lib/tools/c_src
-
-CONFIG_FILES="${ERL_TOP}/erts/emulator/$host/Makefile
- $erts_lib_src/$host/Makefile
- $erts_incl/$host/erl_int_sizes_config.h
- $erts_incl_intrnl/$host/ethread.mk
- $erts_incl_intrnl/$host/ethread_header_config.h
- $etcdir/$host/Makefile
- $erlint_dir/$host/Makefile
- $erlint_dir/$host/eidefs.mk
- $epmd_dir/$host/Makefile
- $internal_tools_dir/make/$host/otp.mk
- $os_mon_dir/$host/Makefile
- $zlibdir/$host/Makefile
- $ic_dir/$host/Makefile
- $asn1_dir/$host/Makefile
- $runtime_tools_dir/$host/Makefile
- $tools_dir/$host/Makefile
- $orber_dir/$host/Makefile"
-
-for file in $CONFIG_FILES; do
- new_name=`echo $file|sed "s%/$host/%/$target/%"`
- dir=`echo $new_name|sed 's%/[^/][^/]*$%%'`
- if test "$dir" != "$new_name" && test "$dir" != .; then
- test ! -d "$dir" && mkdir "$dir"
- fi
-
- sole_name=`echo $file|sed "s%.*$target/%%"`
- in_file=`echo $dir|sed "s%/[^/][^/]*$%/$sole_name.in%"`
- echo "creating $new_name"
- sed -f vxworks/sed.$target -f vxworks/sed.general \
- -e "s,@HOST_TYPE@,$HOST_TYPE,g" \
- -e "s,@WIND_BASE@,$WIND_BASE,g" \
- -e "s,@TARGET@,$target,g" \
- $in_file > $new_name
-done
-
-
diff --git a/lib/diameter/autoconf/install-sh b/lib/diameter/autoconf/install-sh
deleted file mode 100755
index a5897de6ea..0000000000
--- a/lib/diameter/autoconf/install-sh
+++ /dev/null
@@ -1,519 +0,0 @@
-#!/bin/sh
-# install - install a program, script, or datafile
-
-scriptversion=2006-12-25.00
-
-# This originates from X11R5 (mit/util/scripts/install.sh), which was
-# later released in X11R6 (xc/config/util/install.sh) with the
-# following copyright and license.
-#
-# Copyright (C) 1994 X Consortium
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to
-# deal in the Software without restriction, including without limitation the
-# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-# sell copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
-# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
-# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-#
-# Except as contained in this notice, the name of the X Consortium shall not
-# be used in advertising or otherwise to promote the sale, use or other deal-
-# ings in this Software without prior written authorization from the X Consor-
-# tium.
-#
-#
-# FSF changes to this file are in the public domain.
-#
-# Calling this script install-sh is preferred over install.sh, to prevent
-# `make' implicit rules from creating a file called install from it
-# when there is no Makefile.
-#
-# This script is compatible with the BSD install script, but was written
-# from scratch.
-
-nl='
-'
-IFS=" "" $nl"
-
-# set DOITPROG to echo to test this script
-
-# Don't use :- since 4.3BSD and earlier shells don't like it.
-doit=${DOITPROG-}
-if test -z "$doit"; then
- doit_exec=exec
-else
- doit_exec=$doit
-fi
-
-# Put in absolute file names if you don't have them in your path;
-# or use environment vars.
-
-chgrpprog=${CHGRPPROG-chgrp}
-chmodprog=${CHMODPROG-chmod}
-chownprog=${CHOWNPROG-chown}
-cmpprog=${CMPPROG-cmp}
-cpprog=${CPPROG-cp}
-mkdirprog=${MKDIRPROG-mkdir}
-mvprog=${MVPROG-mv}
-rmprog=${RMPROG-rm}
-stripprog=${STRIPPROG-strip}
-
-posix_glob='?'
-initialize_posix_glob='
- test "$posix_glob" != "?" || {
- if (set -f) 2>/dev/null; then
- posix_glob=
- else
- posix_glob=:
- fi
- }
-'
-
-posix_mkdir=
-
-# Desired mode of installed file.
-mode=0755
-
-chgrpcmd=
-chmodcmd=$chmodprog
-chowncmd=
-mvcmd=$mvprog
-rmcmd="$rmprog -f"
-stripcmd=
-
-src=
-dst=
-dir_arg=
-dst_arg=
-
-copy_on_change=false
-no_target_directory=
-
-usage="\
-Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
- or: $0 [OPTION]... SRCFILES... DIRECTORY
- or: $0 [OPTION]... -t DIRECTORY SRCFILES...
- or: $0 [OPTION]... -d DIRECTORIES...
-
-In the 1st form, copy SRCFILE to DSTFILE.
-In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
-In the 4th, create DIRECTORIES.
-
-Options:
- --help display this help and exit.
- --version display version info and exit.
-
- -c (ignored)
- -C install only if different (preserve the last data modification time)
- -d create directories instead of installing files.
- -g GROUP $chgrpprog installed files to GROUP.
- -m MODE $chmodprog installed files to MODE.
- -o USER $chownprog installed files to USER.
- -s $stripprog installed files.
- -t DIRECTORY install into DIRECTORY.
- -T report an error if DSTFILE is a directory.
-
-Environment variables override the default commands:
- CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG
- RMPROG STRIPPROG
-"
-
-while test $# -ne 0; do
- case $1 in
- -c) ;;
-
- -C) copy_on_change=true;;
-
- -d) dir_arg=true;;
-
- -g) chgrpcmd="$chgrpprog $2"
- shift;;
-
- --help) echo "$usage"; exit $?;;
-
- -m) mode=$2
- case $mode in
- *' '* | *' '* | *'
-'* | *'*'* | *'?'* | *'['*)
- echo "$0: invalid mode: $mode" >&2
- exit 1;;
- esac
- shift;;
-
- -o) chowncmd="$chownprog $2"
- shift;;
-
- -s) stripcmd=$stripprog;;
-
- -t) dst_arg=$2
- shift;;
-
- -T) no_target_directory=true;;
-
- --version) echo "$0 $scriptversion"; exit $?;;
-
- --) shift
- break;;
-
- -*) echo "$0: invalid option: $1" >&2
- exit 1;;
-
- *) break;;
- esac
- shift
-done
-
-if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
- # When -d is used, all remaining arguments are directories to create.
- # When -t is used, the destination is already specified.
- # Otherwise, the last argument is the destination. Remove it from $@.
- for arg
- do
- if test -n "$dst_arg"; then
- # $@ is not empty: it contains at least $arg.
- set fnord "$@" "$dst_arg"
- shift # fnord
- fi
- shift # arg
- dst_arg=$arg
- done
-fi
-
-if test $# -eq 0; then
- if test -z "$dir_arg"; then
- echo "$0: no input file specified." >&2
- exit 1
- fi
- # It's OK to call `install-sh -d' without argument.
- # This can happen when creating conditional directories.
- exit 0
-fi
-
-if test -z "$dir_arg"; then
- trap '(exit $?); exit' 1 2 13 15
-
- # Set umask so as not to create temps with too-generous modes.
- # However, 'strip' requires both read and write access to temps.
- case $mode in
- # Optimize common cases.
- *644) cp_umask=133;;
- *755) cp_umask=22;;
-
- *[0-7])
- if test -z "$stripcmd"; then
- u_plus_rw=
- else
- u_plus_rw='% 200'
- fi
- cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
- *)
- if test -z "$stripcmd"; then
- u_plus_rw=
- else
- u_plus_rw=,u+rw
- fi
- cp_umask=$mode$u_plus_rw;;
- esac
-fi
-
-for src
-do
- # Protect names starting with `-'.
- case $src in
- -*) src=./$src;;
- esac
-
- if test -n "$dir_arg"; then
- dst=$src
- dstdir=$dst
- test -d "$dstdir"
- dstdir_status=$?
- else
-
- # Waiting for this to be detected by the "$cpprog $src $dsttmp" command
- # might cause directories to be created, which would be especially bad
- # if $src (and thus $dsttmp) contains '*'.
- if test ! -f "$src" && test ! -d "$src"; then
- echo "$0: $src does not exist." >&2
- exit 1
- fi
-
- if test -z "$dst_arg"; then
- echo "$0: no destination specified." >&2
- exit 1
- fi
-
- dst=$dst_arg
- # Protect names starting with `-'.
- case $dst in
- -*) dst=./$dst;;
- esac
-
- # If destination is a directory, append the input filename; won't work
- # if double slashes aren't ignored.
- if test -d "$dst"; then
- if test -n "$no_target_directory"; then
- echo "$0: $dst_arg: Is a directory" >&2
- exit 1
- fi
- dstdir=$dst
- dst=$dstdir/`basename "$src"`
- dstdir_status=0
- else
- # Prefer dirname, but fall back on a substitute if dirname fails.
- dstdir=`
- (dirname "$dst") 2>/dev/null ||
- expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
- X"$dst" : 'X\(//\)[^/]' \| \
- X"$dst" : 'X\(//\)$' \| \
- X"$dst" : 'X\(/\)' \| . 2>/dev/null ||
- echo X"$dst" |
- sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
- s//\1/
- q
- }
- /^X\(\/\/\)[^/].*/{
- s//\1/
- q
- }
- /^X\(\/\/\)$/{
- s//\1/
- q
- }
- /^X\(\/\).*/{
- s//\1/
- q
- }
- s/.*/./; q'
- `
-
- test -d "$dstdir"
- dstdir_status=$?
- fi
- fi
-
- obsolete_mkdir_used=false
-
- if test $dstdir_status != 0; then
- case $posix_mkdir in
- '')
- # Create intermediate dirs using mode 755 as modified by the umask.
- # This is like FreeBSD 'install' as of 1997-10-28.
- umask=`umask`
- case $stripcmd.$umask in
- # Optimize common cases.
- *[2367][2367]) mkdir_umask=$umask;;
- .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;
-
- *[0-7])
- mkdir_umask=`expr $umask + 22 \
- - $umask % 100 % 40 + $umask % 20 \
- - $umask % 10 % 4 + $umask % 2
- `;;
- *) mkdir_umask=$umask,go-w;;
- esac
-
- # With -d, create the new directory with the user-specified mode.
- # Otherwise, rely on $mkdir_umask.
- if test -n "$dir_arg"; then
- mkdir_mode=-m$mode
- else
- mkdir_mode=
- fi
-
- posix_mkdir=false
- case $umask in
- *[123567][0-7][0-7])
- # POSIX mkdir -p sets u+wx bits regardless of umask, which
- # is incompatible with FreeBSD 'install' when (umask & 300) != 0.
- ;;
- *)
- tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
- trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0
-
- if (umask $mkdir_umask &&
- exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1
- then
- if test -z "$dir_arg" || {
- # Check for POSIX incompatibilities with -m.
- # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
- # other-writeable bit of parent directory when it shouldn't.
- # FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
- ls_ld_tmpdir=`ls -ld "$tmpdir"`
- case $ls_ld_tmpdir in
- d????-?r-*) different_mode=700;;
- d????-?--*) different_mode=755;;
- *) false;;
- esac &&
- $mkdirprog -m$different_mode -p -- "$tmpdir" && {
- ls_ld_tmpdir_1=`ls -ld "$tmpdir"`
- test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
- }
- }
- then posix_mkdir=:
- fi
- rmdir "$tmpdir/d" "$tmpdir"
- else
- # Remove any dirs left behind by ancient mkdir implementations.
- rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null
- fi
- trap '' 0;;
- esac;;
- esac
-
- if
- $posix_mkdir && (
- umask $mkdir_umask &&
- $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
- )
- then :
- else
-
- # The umask is ridiculous, or mkdir does not conform to POSIX,
- # or it failed possibly due to a race condition. Create the
- # directory the slow way, step by step, checking for races as we go.
-
- case $dstdir in
- /*) prefix='/';;
- -*) prefix='./';;
- *) prefix='';;
- esac
-
- eval "$initialize_posix_glob"
-
- oIFS=$IFS
- IFS=/
- $posix_glob set -f
- set fnord $dstdir
- shift
- $posix_glob set +f
- IFS=$oIFS
-
- prefixes=
-
- for d
- do
- test -z "$d" && continue
-
- prefix=$prefix$d
- if test -d "$prefix"; then
- prefixes=
- else
- if $posix_mkdir; then
- (umask=$mkdir_umask &&
- $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
- # Don't fail if two instances are running concurrently.
- test -d "$prefix" || exit 1
- else
- case $prefix in
- *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
- *) qprefix=$prefix;;
- esac
- prefixes="$prefixes '$qprefix'"
- fi
- fi
- prefix=$prefix/
- done
-
- if test -n "$prefixes"; then
- # Don't fail if two instances are running concurrently.
- (umask $mkdir_umask &&
- eval "\$doit_exec \$mkdirprog $prefixes") ||
- test -d "$dstdir" || exit 1
- obsolete_mkdir_used=true
- fi
- fi
- fi
-
- if test -n "$dir_arg"; then
- { test -z "$chowncmd" || $doit $chowncmd "$dst"; } &&
- { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } &&
- { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false ||
- test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1
- else
-
- # Make a couple of temp file names in the proper directory.
- dsttmp=$dstdir/_inst.$$_
- rmtmp=$dstdir/_rm.$$_
-
- # Trap to clean up those temp files at exit.
- trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
-
- # Copy the file name to the temp name.
- (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") &&
-
- # and set any options; do chmod last to preserve setuid bits.
- #
- # If any of these fail, we abort the whole thing. If we want to
- # ignore errors from any of these, just make sure not to ignore
- # errors from the above "$doit $cpprog $src $dsttmp" command.
- #
- { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } &&
- { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } &&
- { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } &&
- { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&
-
- # If -C, don't bother to copy if it wouldn't change the file.
- if $copy_on_change &&
- old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` &&
- new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` &&
-
- eval "$initialize_posix_glob" &&
- $posix_glob set -f &&
- set X $old && old=:$2:$4:$5:$6 &&
- set X $new && new=:$2:$4:$5:$6 &&
- $posix_glob set +f &&
-
- test "$old" = "$new" &&
- $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
- then
- rm -f "$dsttmp"
- else
- # Rename the file to the real destination.
- $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null ||
-
- # The rename failed, perhaps because mv can't rename something else
- # to itself, or perhaps because mv is so ancient that it does not
- # support -f.
- {
- # Now remove or move aside any old file at destination location.
- # We try this two ways since rm can't unlink itself on some
- # systems and the destination file might be busy for other
- # reasons. In this case, the final cleanup might fail but the new
- # file should still install successfully.
- {
- test ! -f "$dst" ||
- $doit $rmcmd -f "$dst" 2>/dev/null ||
- { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
- { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }
- } ||
- { echo "$0: cannot unlink or rename $dst" >&2
- (exit 1); exit 1
- }
- } &&
-
- # Now rename the file to the real destination.
- $doit $mvcmd "$dsttmp" "$dst"
- }
- fi || exit 1
-
- trap '' 0
- fi
-done
-
-# Local variables:
-# eval: (add-hook 'write-file-hooks 'time-stamp)
-# time-stamp-start: "scriptversion="
-# time-stamp-format: "%:y-%02m-%02d.%02H"
-# time-stamp-end: "$"
-# End:
diff --git a/lib/diameter/autoconf/vxworks/sed.general b/lib/diameter/autoconf/vxworks/sed.general
deleted file mode 100644
index 9199983e16..0000000000
--- a/lib/diameter/autoconf/vxworks/sed.general
+++ /dev/null
@@ -1,124 +0,0 @@
-#
-# %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%
-#
-# Author: Patrik Winroth
-#
-# This sed program file is intended to be used when creating Makefiles
-# for vxworks from the generic Makefile.in that is found in a number
-# of directories (see configure.vxworks).
-#
-# This is the general part that is common for all architectures.
-#
-
-# Size of data types.
-s|^#undef SIZEOF_CHAR|#define SIZEOF_CHAR 1|
-s|^#undef SIZEOF_SHORT|#define SIZEOF_SHORT 2|
-s|^#undef SIZEOF_INT|#define SIZEOF_INT 4|
-s|^#undef SIZEOF_LONG_LONG|#define SIZEOF_LONG_LONG 8|
-s|^#undef SIZEOF_LONG$|#define SIZEOF_LONG 4|
-
-# General stuff.
-s|@erts_rootdir@|/clearcase/otp/erts|
-
-s|@LIBOBJS@|$(OBJDIR)/elib_malloc.o|
-s|@DLOAD_LIB@||
-s|@LDFLAGS@||
-# FIXME: A bit strange to clear out remaining DED_*
-s|@DED_LDFLAGS@||
-s|@DED_CFLAGS@||
-s|@STATIC_CFLAGS@||
-s|@GCCLIB@|libgcc.a|
-s|@DEFS@||
-s|@DEXPORT@||
-s|@DCFLAGS@||
-s|@THR_DEFS@||
-s|@THR_LIBS@||
-s|@THR_LIB_NAME@||
-s|@THR_X_LIBS@||
-s|@ETHR_X_LIBS@||
-s|@ETHR_LIBS@||
-s|@ETHR_LIB_NAME@||
-s|@ETHR_DEFS@||
-s|@ETHR_THR_LIB_BASE@||
-s|@EMU_THR_DEFS@||
-s|@EMU_THR_LIBS@||
-s|@EMU_THR_LIB_NAME@|ethread|
-s|@ERTS_ENABLE_KERNEL_POLL@|no|
-s|@cc_root@|/clearcase/otp/|
-# Define VxWorks even though cross-compiling.
-s|@HCFLAGS@|-DVXWORKS|
-s|@HCLIBS@||
-s|@ENABLE_ALLOC_TYPE_VARS@||
-s|@TERMCAP_LIB@||
-s|@ERTS_BUILD_SMP_EMU@|no|
-s|@HAVE_VALGRIND@|no|
-s|@EXEEXT@||
-s|@WITH_SCTP@||
-
-# HiPE
-s|@HIPE_ENABLED@||
-s|@PERFCTR_PATH@||
-s|@USE_PERFCTR@||
-
-# m4
-s|@OPSYS@|noopsys|
-
-# Conditional inclusion of applications
-s|@HIPE_APP@||
-s|@SSL_APP@|ssl|
-s|@CRYPTO_APP@|crypto|
-s|@SSH_APP@|ssh|
-
-# The target tools prefix, prepended to all cc,ld,as etc commands
-s|@TTPREFIX@|GCC_EXEC_PREFIX=@WIND_BASE@/host/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/host/@HOST_TYPE@/bin/|
-
-# Install programs etc
-s|@PERL@|perl|
-s|@INSTALL@|/usr/ucb/install -c|
-s|@INSTALL_PROGRAM@|${INSTALL}|
-s|@INSTALL_SCRIPT@|${INSTALL}|
-s|@INSTALL_DATA@|${INSTALL} -m 644|
-s|@INSTALL_DIR@|$(INSTALL) -d|
-s|@RM@|/bin/rm|
-s|@MKDIR@|/bin/mkdir|
-s|@ERLANG_OSTYPE@|vxworks|
-s|@vxworks_reclaim@|reclaim.h|
-s|@os_mon_programs@||
-s|@erlexec@|erl.exec|
-s|@EMU_LIBOBJS@||
-
-# General CFLAGS
-s|@GENERAL_CFLAGS@|-DHAVE_LOCALTIME_R -DHAVE_GMTIME_R -DENABLE_ELIB_MALLOC -DELIB_HEAP_USER -DELIB_SORTED_BLOCKS -DWORDS_BIGENDIAN -DELIB_DONT_INITIALIZE -DSIZEOF_CHAR=1 -DSIZEOF_SHORT=2 -DSIZEOF_INT=4 -DSIZEOF_LONG=4 -DSIZEOF_LONG_LONG=8 -DSIZEOF_VOID_P=4 -DERTS_USE_PORT_TASKS=1|g
-s|@WFLAGS@||
-
-# Thread flags for eidefs.mk (erl_interface)
-s|@EI_THREADS@|false|
-
-# Make java code compile although we don't test it on VxWorks (no license)
-s|@JAVAC@|javac|
-
-# What is this anyway?
-# Disable it and see what breaks.
-#s|@ded_soname@||
-
-# Only variable substituted directly
-s|$(LDFLAGS)|-r -d|
-s|@LIBRT@||
-# XXX What is EFFLAGS? Not used in the emulator Makefile.in anyway.
-s|$(EFLAGS)|-DENABLE_ELIB_MALLOC -DELIB_HEAP_USER -DELIB_SORTED_BLOCKS|
-
diff --git a/lib/diameter/autoconf/vxworks/sed.vxworks_cpu32 b/lib/diameter/autoconf/vxworks/sed.vxworks_cpu32
deleted file mode 100644
index 5a1590e786..0000000000
--- a/lib/diameter/autoconf/vxworks/sed.vxworks_cpu32
+++ /dev/null
@@ -1,45 +0,0 @@
-#
-# %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%
-#
-# Author: Patrik Winroth
-#
-# This sed program file is intended to be used when creating Makefiles for vxworks
-# from the generic Makefile.in that is found in a number of directories (see configure.vxworks)
-#
-#
-#
-s|@host@|vxworks_cpu32|
-s|@system_type@|vxworks_cpu32|
-s|@CC@|@TTPREFIX@cc68k|
-s|@HCC@|gcc|
-s|@LD@|@TTPREFIX@ld68k|
-s|@LIBS@||
-s|@DED_LD@|@TTPREFIX@ld68k|
-s|@DED_CFLAGS@|@CFLAGS@|
-s|@DEBUG_FLAGS@|-g|
-s|@GCCLIB_PATH@|@WIND_BASE@/host/@HOST_TYPE@/lib/gcc-lib/m68k-wrs-vxworks/cygnus-2.7.2-960126/m68000/msoft-float/libgcc.a|
-s|@RANLIB@|@TTPREFIX@ranlib68k|
-s|@AR@|@TTPREFIX@ar68k|
-s|@STRIP@|@TTPREFIX@strip68k|
-s|@SYMPREFIX@|_|
-s|@GCCLIBFLAGS@|-L@WIND_BASE@/host/@HOST_TYPE@/lib/gcc-lib/m68k-wrs-vxworks/cygnus-2.7.2-960126/m68000/msoft-float -lgcc|
-
-s|@LIB_CFLAGS@|@CFLAGS@|
-
-s|@CFLAGS@|@GENERAL_CFLAGS@ -DCPU=CPU32 -mnobitfield -DWANT_NONBLOCKING -DHAVE_MEMMOVE -DVXWORKS -I@WIND_BASE@/target/h -fno-builtin -nostdinc -fvolatile -msoft-float|
-#s|@LIB_CFLAGS@|@GENERAL_CFLAGS@ -DCPU=CPU32 -mnobitfield -DWANT_NONBLOCKING -DHAVE_MEMMOVE -DVXWORKS -I@WIND_BASE@/target/h -fno-builtin -nostdinc -fvolatile -msoft-float|
diff --git a/lib/diameter/autoconf/vxworks/sed.vxworks_ppc32 b/lib/diameter/autoconf/vxworks/sed.vxworks_ppc32
deleted file mode 100644
index 9104b24ed3..0000000000
--- a/lib/diameter/autoconf/vxworks/sed.vxworks_ppc32
+++ /dev/null
@@ -1,52 +0,0 @@
-#
-# %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%
-#
-# Author: Peter Andersson
-#
-# This sed program file is intended to be used when creating Makefiles for vxworks
-# from the generic Makefile.in that is found in a number of directories (see configure.vxworks)
-#
-#
-#
-s|@host@|vxworks_ppc32|
-s|@system_type@|vxworks_ppc32|
-s|@ARCH@|ppc32|
-s|@CC@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/ccppc -mlongcall|
-s|@HCC@|gcc|
-s|@LD@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/ldppc|
-s|@STRIP@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/workbench-2.3/@HOST_TYPE@/bin/stripppc|
-s|@SYMPREFIX@||
-s|@LIBS@||
-s|@GCCLIBFLAGS@|-L@WIND_BASE@/vxworks-6.3/target/lib/ppc/PPC32/common -lgcc|
-s|@DED_LD@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/ldppc|
-s|@DED_CFLAGS@|@CFLAGS@|
-s|@DEBUG_CFLAGS@|@CFLAGS@|
-# generate dwarf debug code on PPC ..
-s|@DEBUG_FLAGS@|-gdwarf|
-# remove -g option
-s|TYPE_FLAGS = -g |TYPE_FLAGS = |
-s|@GCCLIB_PATH@|@WIND_BASE@/vxworks-6.3/target/lib/ppc/PPC32/common/libgcc.a|
-s|@RANLIB@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/ranlibppc|
-s|@AR@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/arppc|
-# -Dasm(X)= is for beam
-
-s|@LIB_CFLAGS@|@CFLAGS@|
-
-s|@CFLAGS@|@GENERAL_CFLAGS@ -DCPU=PPC32 -DTOOL_FAMILY=gnu -DTOOL=gnu -DWANT_NONBLOCKING -DHAVE_SENS -DHAVE_MEMMOVE -DVXWORKS -I@WIND_BASE@/vxworks-6.3/target/h -I@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc/powerpc-wrs-vxworks/3.4.4/include -I@WIND_BASE@/vxworks-6.3/target/h/wrn/coreip -mstrict-align -fvolatile -fno-builtin |
-
-#s|@LIB_CFLAGS@|@GENERAL_CFLAGS@ -DCPU=PPC32 -DTOOL_FAMILY=gnu -DTOOL=gnu -DWANT_NONBLOCKING -DHAVE_SENS -DHAVE_MEMMOVE -DVXWORKS -I@WIND_BASE@/vxworks-6.3/target/h/wrn/coreip -I@WIND_BASE@/vxworks-6.3/target/h -mstrict-align -fvolatile -fno-builtin |
diff --git a/lib/diameter/autoconf/vxworks/sed.vxworks_ppc603 b/lib/diameter/autoconf/vxworks/sed.vxworks_ppc603
deleted file mode 100644
index e1dd7c5afd..0000000000
--- a/lib/diameter/autoconf/vxworks/sed.vxworks_ppc603
+++ /dev/null
@@ -1,51 +0,0 @@
-#
-# %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%
-#
-# Author: Patrik Winroth
-#
-# This sed program file is intended to be used when creating Makefiles for vxworks
-# from the generic Makefile.in that is found in a number of directories (see configure.vxworks)
-#
-#
-#
-s|@host@|vxworks_ppc603|
-s|@system_type@|vxworks_ppc603|
-s|@ARCH@|ppc603|
-s|@CC@|@TTPREFIX@ccppc -mlongcall|
-s|@HCC@|gcc|
-s|@LD@|@TTPREFIX@ldppc|
-s|@STRIP@|@TTPREFIX@stripppc|
-s|@SYMPREFIX@||
-s|@LIBS@||
-s|@GCCLIBFLAGS@|-L@WIND_BASE@/host/@HOST_TYPE@/lib/gcc-lib/powerpc-wrs-vxworks/cygnus-2.7.2-960126 -lgcc|
-s|@DED_LD@|@TTPREFIX@ldppc|
-s|@DED_CFLAGS@|@CFLAGS@|
-s|@DEBUG_CFLAGS@|@CFLAGS@|
-# generate dwarf debug code on PPC ..
-s|@DEBUG_FLAGS@|-gdwarf|
-# remove -g option
-s|TYPE_FLAGS = -g |TYPE_FLAGS = |
-s|@GCCLIB_PATH@|@WIND_BASE@/host/@HOST_TYPE@/lib/gcc-lib/powerpc-wrs-vxworks/cygnus-2.7.2-960126/libgcc.a|
-s|@RANLIB@|@TTPREFIX@ranlibppc|
-s|@AR@|@TTPREFIX@arppc|
-# -Dasm(X)= is for beam
-
-s|@LIB_CFLAGS@|@CFLAGS@|
-
-s|@CFLAGS@|@GENERAL_CFLAGS@ -DCPU=PPC603 -DWANT_NONBLOCKING -DHAVE_MEMMOVE -DVXWORKS -I@WIND_BASE@/target/h -mstrict-align -fvolatile -fno-builtin -fno-for-scope -D_GNU_TOOL|
-#s|@LIB_CFLAGS@|@GENERAL_CFLAGS@ -DCPU=PPC603 -DWANT_NONBLOCKING -DHAVE_MEMMOVE -DVXWORKS -I@WIND_BASE@/target/h -mstrict-align -fvolatile -fno-builtin -fno-for-scope -D_GNU_TOOL|
diff --git a/lib/diameter/autoconf/vxworks/sed.vxworks_ppc603_nolongcall b/lib/diameter/autoconf/vxworks/sed.vxworks_ppc603_nolongcall
deleted file mode 100644
index 5d7673d323..0000000000
--- a/lib/diameter/autoconf/vxworks/sed.vxworks_ppc603_nolongcall
+++ /dev/null
@@ -1,51 +0,0 @@
-#
-# %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%
-#
-# Author: Patrik Winroth
-#
-# This sed program file is intended to be used when creating Makefiles for vxworks
-# from the generic Makefile.in that is found in a number of directories (see configure.vxworks)
-#
-#
-#
-s|@host@|vxworks_ppc603|
-s|@system_type@|vxworks_ppc603|
-s|@ARCH@|ppc603|
-s|@CC@|@TTPREFIX@ccppc|
-s|@HCC@|gcc|
-s|@LD@|@TTPREFIX@ldppc|
-s|@STRIP@|@TTPREFIX@stripppc|
-s|@SYMPREFIX@||
-s|@LIBS@||
-s|@GCCLIBFLAGS@|-L@WIND_BASE@/host/@HOST_TYPE@/lib/gcc-lib/powerpc-wrs-vxworks/cygnus-2.7.2-960126 -lgcc|
-s|@DED_LD@|@TTPREFIX@ldppc|
-s|@DED_CFLAGS@|@CFLAGS@|
-# generate dwarf debug code on PPC ..
-s|@DEBUG_FLAGS@|-gdwarf|
-# remove -g option
-s|TYPE_FLAGS = -g |TYPE_FLAGS = |
-s|@GCCLIB_PATH@|@WIND_BASE@/host/@HOST_TYPE@/lib/gcc-lib/powerpc-wrs-vxworks/cygnus-2.7.2-960126/libgcc.a|
-s|@RANLIB@|@TTPREFIX@ranlibppc|
-s|@AR@|@TTPREFIX@arppc|
-# -Dasm(X)= is for beam
-
-s|@LIB_CFLAGS@|@CFLAGS@|
-
-s|@CFLAGS@|@GENERAL_CFLAGS@ -DCPU=PPC603 -DWANT_NONBLOCKING -DHAVE_MEMMOVE -DVXWORKS -I@WIND_BASE@/target/h -mstrict-align -fvolatile -fno-builtin -fno-for-scope -D_GNU_TOOL|
-#s|@LIB_CFLAGS@|@GENERAL_CFLAGS@ -DCPU=PPC603 -DWANT_NONBLOCKING -DHAVE_MEMMOVE -DVXWORKS -I@WIND_BASE@/target/h -mstrict-align -fvolatile -fno-builtin -fno-for-scope -D_GNU_TOOL|
-
diff --git a/lib/diameter/autoconf/vxworks/sed.vxworks_ppc860 b/lib/diameter/autoconf/vxworks/sed.vxworks_ppc860
deleted file mode 100644
index 7a3c32df5b..0000000000
--- a/lib/diameter/autoconf/vxworks/sed.vxworks_ppc860
+++ /dev/null
@@ -1,50 +0,0 @@
-#
-# %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%
-#
-# Author: Patrik Winroth
-#
-# This sed program file is intended to be used when creating Makefiles for vxworks
-# from the generic Makefile.in that is found in a number of directories (see configure.vxworks)
-#
-#
-#
-s|@host@|vxworks_ppc860|
-s|@system_type@|vxworks_ppc860|
-s|@ARCH@|ppc860|
-s|@CC@|@TTPREFIX@ccppc -mlongcall|
-s|@HCC@|gcc|
-s|@LD@|@TTPREFIX@ldppc|
-s|@STRIP@|@TTPREFIX@stripppc|
-s|@SYMPREFIX@||
-s|@LIBS@||
-s|@GCCLIBFLAGS@|-L@WIND_BASE@/host/@HOST_TYPE@/lib/gcc-lib/powerpc-wrs-vxworks/cygnus-2.7.2-960126/soft-float -lgcc|
-s|@DED_LD@|@TTPREFIX@ldppc|
-s|@DED_CFLAGS@|@CFLAGS@|
-s|@DEBUG_CFLAGS@|@CFLAGS@|
-# generate dwarf debug code on PPC ..
-s|@DEBUG_FLAGS@|-gdwarf|
-# remove -g option (go for dwarf)
-s|TYPE_FLAGS = -g |TYPE_FLAGS = |
-s|@GCCLIB_PATH@|@WIND_BASE@/host/@HOST_TYPE@/lib/gcc-lib/powerpc-wrs-vxworks/cygnus-2.7.2-960126/soft-float/libgcc.a|
-s|@RANLIB@|@TTPREFIX@ranlibppc|
-s|@AR@|@TTPREFIX@arppc|
-
-s|@LIB_CFLAGS@|@CFLAGS@|
-
-s|@CFLAGS@|@GENERAL_CFLAGS@ -DCPU=PPC860 -DWANT_NONBLOCKING -DHAVE_MEMMOVE -DVXWORKS -I@WIND_BASE@/target/h -mcpu=860 -fvolatile -fno-builtin -fno-for-scope -msoft-float -D_GNU_TOOL -nostdinc|
-#s|@LIB_CFLAGS@|@GENERAL_CFLAGS@ -DCPU=PPC860 -DWANT_NONBLOCKING -DHAVE_MEMMOVE -DVXWORKS -I@WIND_BASE@/target/h -mcpu=powerpc -fvolatile -fno-builtin -fno-for-scope -msoft-float -D_GNU_TOOL -nostdinc|
diff --git a/lib/diameter/autoconf/vxworks/sed.vxworks_simlinux b/lib/diameter/autoconf/vxworks/sed.vxworks_simlinux
deleted file mode 100644
index 56eae6507c..0000000000
--- a/lib/diameter/autoconf/vxworks/sed.vxworks_simlinux
+++ /dev/null
@@ -1,59 +0,0 @@
-#
-# %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%
-#
-# Author: Peter Andersson
-#
-# This sed program file is intended to be used when creating Makefiles for vxworks
-# from the generic Makefile.in that is found in a number of directories (see configure.vxworks)
-#
-#
-#
-s|@host@|vxworks_simlinux|
-s|@system_type@|vxworks_simlinux|
-s|@ARCH@|simlinux|
-
-s|@CC@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/ccpentium|
-
-s|@HCC@|gcc|
-
-s|@LD@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/ldpentium|
-
-#s|@STRIP@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/strip|
-s|@STRIP@||
-
-s|@SYMPREFIX@||
-s|@LIBS@||
-s|@GCCLIBFLAGS@|-L@WIND_BASE@/vxworks-6.3/target/lib/simlinux/SIMLINUX/common -lgcc|
-
-s|@DED_LD@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/ldpentium|
-
-s|@DED_CFLAGS@|@CFLAGS@|
-s|@DEBUG_CFLAGS@|@CFLAGS@|
-# remove -g option
-s|TYPE_FLAGS = -g |TYPE_FLAGS = |
-s|@GCCLIB_PATH@|@WIND_BASE@/vxworks-6.3/target/lib/simlinux/SIMLINUX/common/libgcc.a|
-
-s|@RANLIB@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/ranlibpentium|
-
-s|@AR@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/arpentium|
-
-# -Dasm(X)= is for beam
-s|@LIB_CFLAGS@|@CFLAGS@|
-
-s|@CFLAGS@|@GENERAL_CFLAGS@ -DCPU=SIMLINUX -DTOOL_FAMILY=gnu -DTOOL=gnu -DWANT_NONBLOCKING -DHAVE_SENS -DHAVE_MEMMOVE -DVXWORKS -DDEBUG -I@WIND_BASE@/vxworks-6.3/target/h -I@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc/i586-wrs-vxworks/3.4.4/include -I@WIND_BASE@/vxworks-6.3/target/h/wrn/coreip -fvolatile -fno-builtin |
-#s|@LIB_CFLAGS@|@GENERAL_CFLAGS@ -DCPU=SIMLINUX -DTOOL_FAMILY=gnu -DTOOL=gnu -DWANT_NONBLOCKING -DHAVE_SENS -DHAVE_MEMMOVE -DVXWORKS -DDEBUG -I@WIND_BASE@/vxworks-6.3/target/h -I@WIND_BASE@/vxworks-6.3/target/h/wrn/coreip -mstrict-align -fvolatile -fno-builtin |
diff --git a/lib/diameter/autoconf/vxworks/sed.vxworks_simso b/lib/diameter/autoconf/vxworks/sed.vxworks_simso
deleted file mode 100644
index 6b845d31de..0000000000
--- a/lib/diameter/autoconf/vxworks/sed.vxworks_simso
+++ /dev/null
@@ -1,64 +0,0 @@
-#
-# %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%
-#
-# Author: Peter Andersson
-#
-# This sed program file is intended to be used when creating Makefiles for vxworks
-# from the generic Makefile.in that is found in a number of directories (see configure.vxworks)
-#
-#
-#
-s|@host@|vxworks_simso|
-s|@system_type@|vxworks_simso|
-s|@ARCH@|simso|
-
-# Tornado2.2: s|@CC@|@TTPREFIX@ccsimso|
-s|@CC@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/ccsparc|
-
-s|@HCC@|gcc|
-
-# Tornado2.2: s|@LD@|@TTPREFIX@ldsimso|
-s|@LD@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/ldsparc|
-
-# Tornado2.2: s|@STRIP@|@TTPREFIX@stripsimso|
-s|@STRIP@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/stripsparc|
-
-s|@SYMPREFIX@||
-s|@LIBS@||
-s|@GCCLIBFLAGS@|-L@WIND_BASE@/vxworks-6.3/target/lib/simso/SIMSPARCSOLARIS/common -lgcc|
-
-# Tornado2.2: s|@DED_LD@|@TTPREFIX@ldsimso|
-s|@DED_LD@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/ldsparc|
-
-s|@DED_CFLAGS@|@CFLAGS@|
-s|@DEBUG_CFLAGS@|@CFLAGS@|
-# remove -g option
-s|TYPE_FLAGS = -g |TYPE_FLAGS = |
-s|@GCCLIB_PATH@|@WIND_BASE@/vxworks-6.3/target/lib/simso/SIMSPARCSOLARIS/common/libgcc.a|
-
-# Tornado2.2: s|@RANLIB@|@TTPREFIX@ranlibsimso|
-s|@RANLIB@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/ranlibsparc|
-
-# Tornado2.2: s|@AR@|arsimso|
-s|@AR@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/arsparc|
-
-# -Dasm(X)= is for beam
-s|@LIB_CFLAGS@|@CFLAGS@|
-
-s|@CFLAGS@|@GENERAL_CFLAGS@ -DCPU=SIMSPARCSOLARIS -DTOOL_FAMILY=gnu -DTOOL=gnu -DWANT_NONBLOCKING -DHAVE_SENS -DHAVE_MEMMOVE -DVXWORKS -DDEBUG -I@WIND_BASE@/vxworks-6.3/target/h -I@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc/sparc-wrs-vxworks/3.4.4/include -I@WIND_BASE@/vxworks-6.3/target/h/wrn/coreip -fvolatile -fno-builtin |
-#s|@LIB_CFLAGS@|@GENERAL_CFLAGS@ -DCPU=SIMSPARCSOLARIS -DTOOL_FAMILY=gnu -DTOOL=gnu -DWANT_NONBLOCKING -DHAVE_SENS -DHAVE_MEMMOVE -DVXWORKS -DDEBUG -I@WIND_BASE@/vxworks-6.3/target/h -I@WIND_BASE@/vxworks-6.3/target/h/wrn/coreip -mstrict-align -fvolatile -fno-builtin |
diff --git a/lib/diameter/autoconf/vxworks/sed.vxworks_sparc b/lib/diameter/autoconf/vxworks/sed.vxworks_sparc
deleted file mode 100644
index 6f637d8746..0000000000
--- a/lib/diameter/autoconf/vxworks/sed.vxworks_sparc
+++ /dev/null
@@ -1,38 +0,0 @@
-#
-# %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%
-#
-# Author: Patrik Winroth
-#
-# This sed program file is intended to be used when creating Makefiles for vxworks
-# from the generic Makefile.in that is found in a number of directories (see configure.vxworks)
-#
-
-# ccsparc -O2 doesn't work when compiling "rundir"/gc.c - signal 11 is generated when trying
-# therefore it is compiled with -O1 instead, which works - get a new ccsparc !
-s/\$(COMPILE\.emu) -o \$@ -c gc\.c/$(CC) @CFLAGS@ @DEFS@ -O1 $(BEAM_MODE) -I$(SYSDIR) -I$(EMUDIR) -I. $(CPPFLAGS) -c -o $@ -c gc.c/
-s/@host@/vxworks_sparc/
-s/@system_type@/vxworks_sparc/
-s/@CC@/\/home\/gandalf\/bsproj\/tools\/vw-gnu\/solaris.sparc\/bin\/ccsparc/
-s/@HCC@/gcc/
-s/@LD@/\/home\/gandalf\/bsproj\/tools\/vw-gnu\/solaris.sparc\/bin\/ldsparc/
-s/@DEBUG_FLAGS@/-g/
-s/@GCCLIB_PATH@/\/home\/gandalf\/bsproj\/tools\/vw-gnu\/solaris.sparc\/lib\/gcc-lib\/sparc-wrs-vxworks\/cygnus-2.2.3.1\/libgcc.a/
-s/@RANLIB@/ranlibsparc/
-s/@AR@/arsparc/
-s/@CFLAGS@/-I\/home\/gandalf\/bsproj\/BS.2\/UOS\/vw\/5.2\/h -DWANT_NONBLOCKING -DHAVE_MEMMOVE -DCPU=SPARC -DVXWORKS -fno-builtin -nostdinc/
-
diff --git a/lib/diameter/configure.in b/lib/diameter/configure.in
deleted file mode 100644
index 8acfb28fed..0000000000
--- a/lib/diameter/configure.in
+++ /dev/null
@@ -1,137 +0,0 @@
-dnl Process this file with autoconf to produce a configure script.
-
-dnl %CopyrightBegin%
-dnl
-dnl Copyright Ericsson AB 1998-2011. All Rights Reserved.
-dnl
-dnl The contents of this file are subject to the Erlang Public License,
-dnl Version 1.1, (the "License"); you may not use this file except in
-dnl compliance with the License. You should have received a copy of the
-dnl Erlang Public License along with this software. If not, it can be
-dnl retrieved online at http://www.erlang.org/.
-dnl
-dnl Software distributed under the License is distributed on an "AS IS"
-dnl basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-dnl the License for the specific language governing rights and limitations
-dnl under the License.
-dnl
-dnl %CopyrightEnd%
-
-if test "x$no_recursion" != "xyes" -a "x$OVERRIDE_CONFIG_CACHE" = "x"; then
- # We do not want to use a common cache!
- cache_file=/dev/null
-fi
-
-AC_INIT(vsn.mk)
-
-dnl <STANDALONE DIAMETER>
-dnl This is needed for diameters own environment to rock,
-dnl but since we are now integrated into OTP, we skip it.
-dnl In order to build stand-alone we need atleast 2.63...
-dnl AC_PREREQ(2.63)
-dnl </STANDALONE DIAMETER>
-
-dnl LM_PRECIOUS_VARS
-
-
-dnl The OTP source tree is the default "top",
-dnl but we can also define our own top: DIAMETER_TOP
-
-if test -n "$ERL_TOP" || test -d $ERL_TOP ; then
- erl_top=${ERL_TOP}
- AC_CONFIG_AUX_DIRS($erl_top/erts/autoconf)
- DIAMETER_TOP=${ERL_TOP}/lib/diameter
-else
- AC_ARG_VAR(DIAMETER_TOP, [Diameter top source directory])
- if test -n "$DIAMETER_TOP" || test -d $DIAMETER_TOP ; then
- AC_CONFIG_AUX_DIRS(autoconf)
- fi
-
- dnl <STANDALONE DIAMETER>
- dnl AC_ERLANG_SUBST_ROOT_DIR
- dnl AC_ERLANG_SUBST_LIB_DIR
- dnl AC_ERLANG_CHECK_LIB([erl_docgen],
- dnl [echo "erl_docgen version \"$ERLANG_LIB_VER_erl_docgen\""
- dnl echo "is installed in \"$ERLANG_LIB_DIR_erl_docgen\""],
- dnl [AC_MSG_ERROR([erl_docgen was not found!])])
- dnl AC_ERLANG_CHECK_LIB([test_server],
- dnl [echo "test_server version \"$ERLANG_LIB_VER_test_server\""
- dnl echo "is installed in \"$ERLANG_LIB_DIR_test_server\""],
- dnl [AC_MSG_ERROR([test_server was not found!])])
- dnl </STANDALONE DIAMETER>
-
-fi
-
-AC_SUBST(DIAMETER_TOP)
-export DIAMETER_TOP
-
-if test "X$host" != "Xfree_source" -a "X$host" != "Xwin32"; then
- AC_CANONICAL_HOST
-fi
-
-TARGET=$host
-AC_SUBST(TARGET)
-
-if test "x$erl_top" = "x"; then
- dnl STANDALONE DIAMETER
- AC_CHECK_PROGS(XSLTPROC, xsltproc)
- if test -z "$XSLTPROC"; then
- echo "xsltproc" >> doc/CONF_INFO
- AC_MSG_WARN([No 'xsltproc' command found: the documentation can not be built])
- fi
-
- AC_CHECK_PROGS(FOP, fop)
- if test -z "$FOP"; then
- AC_MSG_ERROR([No 'fop' command found: the documentation can not be built])
- fi
-fi
-
-dnl
-dnl We can live with Solaris /usr/ucb/install
-dnl
-case $host in
- *-*-solaris*|free_source)
- if test -x /usr/ucb/install; then
- INSTALL="/usr/ucb/install -c"
- fi
- ;;
- *)
- ;;
-esac
-AC_PROG_INSTALL
-LM_PROG_INSTALL_DIR
-
-case $host_os in
- darwin*)
- dnl Need to preserve modification time on archives;
- dnl otherwise, ranlib has to be run on archives
- dnl again after installation.
- INSTALL_DATA="$INSTALL_DATA -p";;
- *)
- ;;
-esac
-
-dnl
-dnl Fix for Tilera install permissions
-dnl
-
-case $build in
- *tile*)
- INSTALL_PROGRAM="$INSTALL_PROGRAM -m755"
- INSTALL_SCRIPT="$INSTALL_SCRIPT -m755"
- ;;
- *)
- ;;
-esac
-
-
-dnl <STANDALONE DIAMETER>
-dnl AC_ERLANG_NEED_ERL([$PATH])
-dnl AC_ERLANG_NEED_ERLC([$PATH])
-dnl </STANDALONE DIAMETER>
-
-AC_OUTPUT(
- Makefile:Makefile.in
- make/$host/rules.mk:make/rules.mk.in
- )
-
diff --git a/lib/diameter/doc/.gitignore b/lib/diameter/doc/.gitignore
new file mode 100644
index 0000000000..b634bdd7ba
--- /dev/null
+++ b/lib/diameter/doc/.gitignore
@@ -0,0 +1,4 @@
+
+/html/
+/man*/
+/pdf/
diff --git a/lib/diameter/doc/src/.gitignore b/lib/diameter/doc/src/.gitignore
index feeb378fd8..5776e1cc76 100644
--- a/lib/diameter/doc/src/.gitignore
+++ b/lib/diameter/doc/src/.gitignore
@@ -1,2 +1,3 @@
/depend.mk
+/seehere.ent
diff --git a/lib/diameter/doc/src/Makefile b/lib/diameter/doc/src/Makefile
index cdf63e7fb6..0cbe1f000f 100644
--- a/lib/diameter/doc/src/Makefile
+++ b/lib/diameter/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2010-2011. All Rights Reserved.
+# Copyright Ericsson AB 2010-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -16,13 +16,8 @@
#
# %CopyrightEnd%
-ifneq ($(ERL_TOP),)
include $(ERL_TOP)/make/target.mk
include $(ERL_TOP)/make/$(TARGET)/otp.mk
-else
-include $(DIAMETER_TOP)/make/target.mk
-include $(DIAMETER_TOP)/make/$(TARGET)/rules.mk
-endif
include ../../vsn.mk
@@ -39,7 +34,8 @@ XML_REF_FILES = $(XML_REF1_FILES) $(XML_REF3_FILES) $(XML_REF4_FILES)
XML_FILES = $(BOOK_FILES) $(XML_APPLICATION_FILES) \
$(XML_REF_FILES) \
- $(XML_PART_FILES) $(XML_CHAPTER_FILES)
+ $(XML_PART_FILES) $(XML_CHAPTER_FILES) \
+ seealso.ent
INTERNAL_HTML_FILES = $(TECHNICAL_DESCR_FILES:%.xml=$(HTMLDIR)/%.html)
@@ -98,7 +94,7 @@ html: gifs $(HTML_REF_MAN_FILE)
clean clean_docs: clean_pdf clean_html clean_man
rm -f errs core *~
- rm -f depend.mk
+ rm -f depend.mk seehere.ent
clean_pdf:
rm -f $(PDFDIR)/*
@@ -155,11 +151,8 @@ info:
# ----------------------------------------------------
# Release Target
# ----------------------------------------------------
-ifneq ($(ERL_TOP),)
+
include $(ERL_TOP)/make/otp_release_targets.mk
-else
-include $(DIAMETER_TOP)/make/release_targets.mk
-endif
release_docs_spec: $(LOCAL)docs
$(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf"
@@ -178,8 +171,10 @@ release_docs_spec: $(LOCAL)docs
release_spec:
-depend.mk: depend.sed $(XML_REF_FILES) $(XML_CHAPTER_FILES) Makefile
- (for f in $(XML_REF_FILES) $(XML_CHAPTER_FILES); do \
+depend.mk: depend.sed Makefile seealso.ent \
+ $(XML_REF_FILES) $(XML_CHAPTER_FILES)
+ $(gen_verbose)sed -f seehere.sed seealso.ent > seehere.ent
+ $(V_at)(for f in $(XML_REF_FILES) $(XML_CHAPTER_FILES); do \
sed -f $< $$f | sed "s@%FILE%@`basename $$f .xml`@g"; \
done) \
> $@
diff --git a/lib/diameter/doc/src/diameter.xml b/lib/diameter/doc/src/diameter.xml
index 93e2603c10..b7669b760b 100644
--- a/lib/diameter/doc/src/diameter.xml
+++ b/lib/diameter/doc/src/diameter.xml
@@ -1,11 +1,22 @@
<?xml version="1.0" encoding="latin1" ?>
-<!DOCTYPE erlref SYSTEM "erlref.dtd">
+<!DOCTYPE erlref SYSTEM "erlref.dtd" [
+ <!ENTITY make_ref
+ '<seealso marker="erts:erlang#make_ref-0">erlang:make_ref/0</seealso>'>
+ <!ENTITY transport_module
+ '<seealso marker="diameter_transport">transport module</seealso>'>
+ <!ENTITY dictionary
+ '<seealso marker="diameter_dict">dictionary</seealso>'>
+ <!ENTITY % also SYSTEM "seealso.ent" >
+ <!ENTITY % here SYSTEM "seehere.ent" >
+ %also;
+ %here;
+]>
<erlref>
<header>
<copyright>
-<year>2011</year>
+<year>2011</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -43,35 +54,30 @@ under the License.
<description>
<p>
-This module provides the interface with which a user
-creates a service that sends and receives messages using the
-Diameter protocol as defined in RFC 3588.</p>
+This module provides the interface with which a user can
+implement a Diameter node that sends and receives messages using the
+Diameter protocol as defined in &the_rfc;.</p>
<p>
Basic usage consists of creating a representation of a
-locally implemented Diameter peer and its capabilities with <seealso
-marker="#start_service">start_service/2</seealso>, adding transport
-capability using <seealso
-marker="#add_transport">add_transport/2</seealso> and sending Diameter
-requests and receiving Diameter answers with <seealso
-marker="#call">call/4</seealso>.
+locally implemented Diameter node and its capabilities with
+&start_service;, adding transport capability using
+&add_transport; and sending Diameter
+requests and receiving Diameter answers with &call;.
Incoming Diameter requests are communicated as callbacks to a
-<seealso
-marker="diameter_app">diameter_app(3)</seealso> callback modules as
+&man_app; callback modules as
specified in the service configuration.</p>
<p>
-Beware the difference between <em>diameter application</em> and
-<em>Diameter application</em>.
+Beware the difference between <em>diameter</em> (not capitalised) and
+<em>Diameter</em> (capitalised).
The former refers to the Erlang application named diameter whose main
-api is defined here, the latter to an application of the Diameter
-protocol in the sense of RFC 3588.
-More generally, capitalized Diameter refers to the RFC
-and diameter to this implementation.</p>
+api is defined here, the latter to Diameter protocol in the sense of
+&the_rfc;.</p>
<p>
-The diameter application must be started before calling functions in
-this module.</p>
+The diameter application must be started before calling most functions
+in this module.</p>
</description>
@@ -84,13 +90,15 @@ this module.</p>
<tag><c>Address()</c></tag>
<tag><c>DiameterIdentity()</c></tag>
+<tag><c>Grouped()</c></tag>
+<tag><c>OctetString()</c></tag>
<tag><c>Time()</c></tag>
<tag><c>Unsigned32()</c></tag>
<tag><c>UTF8String()</c></tag>
<item>
<p>
-Types corresponding to RFC 3588 AVP Data Formats.
-Defined in <seealso marker="diameter_dict">diameter_dict(4)</seealso>.</p>
+Types corresponding to &the_rfc; AVP Data Formats.
+Defined in &dict_data_types;.</p>
<marker id="application_alias"/>
</item>
@@ -99,10 +107,9 @@ Defined in <seealso marker="diameter_dict">diameter_dict(4)</seealso>.</p>
<item>
<p>
A name identifying a Diameter application in
-service configuration passed to <seealso
-marker="#start_service">start_service/2</seealso> and passed to
-<seealso marker="#call">call/4</seealso> when sending requests
-belonging to the application.</p>
+service configuration.
+Passed to &call; when sending requests
+defined by the application.</p>
<marker id="application_module"/>
</item>
@@ -111,28 +118,22 @@ belonging to the application.</p>
| [Mod | ExtraArgs]
| #diameter_callback{}</c></tag>
<item>
-<code>
+<pre>
Mod = atom()
ExtraArgs = list()
-</code>
+</pre>
<p>
-A module implementing the callback interface defined in <seealso
-marker="diameter_app">diameter_app(3)</seealso>, along with any
+A module implementing the callback interface defined in &man_app;,
+along with any
extra arguments to be appended to those documented for the interface.
-Any extra arguments are appended to the documented list of arguments for
-each function.
-Note that additional arguments specific to an outgoing request be
-specified to <seealso marker="#call">call/4</seealso>, in which case
-the call-specific arguments are appended to any specified with the
-callback module.</p>
+Note that extra arguments specific to an outgoing request can be
+specified to &call;, in which case
+those are are appended to any module-specific extra arguments.</p>
<p>
Specifying a <c>#diameter_callback{}</c> record allows individual
-functions to be configured in place of the usual <seealso
-marker="diameter_app">diameter_app(3)</seealso> callbacks, with
-default implementations provided by module <c>diameter_callback</c>
-unless otherwise specified.
+functions to be configured in place of the usual &man_app; callbacks.
See that module for details.</p>
<marker id="application_opt"/>
@@ -142,18 +143,18 @@ See that module for details.</p>
<item>
<p>
-Options defining a Diameter application as configured in an
-<c>application</c> option passed to
-<seealso marker="#start_service">start_service/2</seealso>.</p>
+Options defining a Diameter application.
+Has one the following types.</p>
<taglist>
-<tag><c>{alias, application_alias()}</c></tag>
+<tag><c>{alias, &application_alias;}</c></tag>
<item>
<p>
An unique identifier for the application in the scope of the
service.
-Optional, defaults to the value of the <c>dictionary</c> option.</p>
+Defaults to the value of the <c>dictionary</c> option if
+unspecified.</p>
</item>
<tag><c>{dictionary, atom()}</c></tag>
@@ -162,47 +163,43 @@ Optional, defaults to the value of the <c>dictionary</c> option.</p>
The name of an encode/decode module for the Diameter
messages defined by the application.
These modules are generated from a specification file whose format is
-documented in <seealso
-marker="diameter_dict">diameter_dict(4)</seealso>.</p>
+documented in &man_dict;.</p>
</item>
-<tag><c>{module, application_module()}</c></tag>
+<tag><c>{module, &application_module;}</c></tag>
<item>
<p>
-A callback module with which messages of the Diameter application are
+The callback module with which messages of the Diameter application are
handled.
-See <seealso marker="diameter_app">diameter_app(3)</seealso> for
-information on the required interface and semantics.</p>
+See &man_app; for the required interface and semantics.</p>
</item>
<tag><c>{state, term()}</c></tag>
<item>
<p>
The initial callback state.
-Defaults to the value of the <c>alias</c> option if unspecified.
-The prevailing state is passed to certain
-<seealso marker="diameter_app">diameter_app(3)</seealso>
-callbacks, which can then return a new state.</p>
+The prevailing state is passed to some
+&man_app;
+callbacks, which can then return a new state.
+Defaults to the value of the <c>alias</c> option if unspecified.</p>
</item>
<tag><c>{call_mutates_state, true|false}</c></tag>
<item>
<p>
-Specifies whether or not the <seealso
-marker="diameter_app#pick_peer">pick_peer/4</seealso>
-application callback (following from a call to
-<seealso marker="#call">call/4</seealso>)
-can modifiy state,
+Specifies whether or not the &app_pick_peer;
+application callback can modify the application state,
Defaults to <c>false</c> if unspecified.</p>
+<note>
<p>
-Note that <seealso
-marker="diameter_app#pick_peer">pick_peer</seealso> callbacks are
-serialized when these are allowed to modify state, which is a
+&app_pick_peer; callbacks
+are serialized when these are allowed to modify state, which is a
potential performance bottleneck.
A simple Diameter client may suffer no ill effects from using mutable
-state but a server or agent that responds to incoming request but
-sending its own requests should probably avoid it.</p>
+state but a server or agent that responds to incoming request should
+probably avoid it.</p>
+</note>
</item>
<tag><c>{answer_errors, callback|report|discard}</c></tag>
@@ -210,18 +207,16 @@ sending its own requests should probably avoid it.</p>
<p>
Determines the manner in which incoming answer messages containing
decode errors are handled.
-If <c>callback</c> then errors result in a <seealso
-marker="diameter_app#handle_answer">handle_answer/4</seealso>
-callback in the same fashion as for <seealso
-marker="diameter_app#handle_request">handle_request/3</seealso>, in the
-<c>errors</c> field of the <c>diameter_packet</c> record passed into
-the callback.
+If <c>callback</c> then errors result in a &app_handle_answer;
+callback in the same fashion as for &app_handle_request;, with
+errors communicated in the <c>errors</c> field of the
+<c>#diameter_packet{}</c> record passed to the callback.
If <c>report</c> then an answer containing errors is discarded
without a callback and a warning report is written to the log.
If <c>discard</c> then an answer containing errors is silently
discarded without a callback.
In both the <c>report</c> and <c>discard</c> cases the return value
-for the <seealso marker="#call">call/4</seealso> invocation in
+for the &call; invocation in
question is as if a callback had taken place and returned
<c>{error, failure}</c>.</p>
@@ -238,8 +233,9 @@ Defaults to <c>report</c> if unspecified.</p>
<item>
<p>
-Options available to <seealso marker="#call">call/4</seealso> when
-sending an outgoing Diameter request.</p>
+Options available to &call; when
+sending an outgoing Diameter request.
+Has one of the following types.</p>
<taglist>
@@ -248,23 +244,23 @@ sending an outgoing Diameter request.</p>
<p>
Extra arguments to append to callbacks to the callback
module in question.
-These are appended to any extra arguments configured with the callback
+These are appended to any extra arguments configured on the callback
itself.
Multiple options append to the argument list.</p>
</item>
-<tag><c>{filter, peer_filter()}</c></tag>
+<tag><c>{filter, &peer_filter;}</c></tag>
<item>
<p>
A filter to apply to the list of available peers before passing them to
-the <seealso marker="diameter_app#pick_peer">pick_peer/4</seealso>
+the &app_pick_peer;
callback for the application in question.
Multiple options are equivalent a single <c>all</c> filter on the
corresponding list of filters.
Defaults to <c>none</c>.</p>
</item>
-<tag><c>{timeout, Unsigned32()}</c></tag>
+<tag><c>{timeout, &dict_Unsigned32;}</c></tag>
<item>
<p>
The number of milliseconds after which the request should
@@ -275,21 +271,17 @@ Defaults to 5000.</p>
<tag><c>detach</c></tag>
<item>
<p>
-Causes <seealso marker="#call">call/4</seealso> to return <c>ok</c> as
+Causes &call; to return <c>ok</c> as
soon as the request in
question has been encoded instead of waiting for and returning
-the result from a subsequent
-<seealso marker="diameter_app#handle_answer">handle_answer/4</seealso>
-or <seealso
-marker="diameter_app#handle_error">handle_error/4</seealso>
-callback.</p>
+the result from a subsequent &app_handle_answer; or
+&app_handle_error; callback.</p>
</item>
</taglist>
<p>
-An invalid option will cause <seealso marker="#call">call/4</seealso>
-to fail.</p>
+An invalid option will cause &call; to fail.</p>
<marker id="capability"/>
</item>
@@ -298,121 +290,54 @@ to fail.</p>
<item>
<p>
-AVP values used in outgoing CER/CEA messages during capabilities exchange.
-Can be configured both on a service and a transport, the latter taking
-precedence over the former.</p>
+AVP values sent in outgoing CER or CEA messages during capabilities
+exchange.
+Can be configured both on a service and a transport, values specified
+on the latter taking precedence over any specified on the former.
+Has one of the following types.</p>
<taglist>
-<tag><c>{'Origin-Host', DiameterIdentity()}</c></tag>
-<item>
-<p>
-Value of the Origin-Host AVP in outgoing messages.</p>
-</item>
-
-<tag><c>{'Origin-Realm', DiameterIdentity()}</c></tag>
-<item>
-<p>
-Value of the Origin-Realm AVP in outgoing messages.</p>
-</item>
-
-<tag><c>{'Host-IP-Address', [Address()]}</c></tag>
-<item>
-<p>
-Values of Host-IP-Address AVPs.
-Optional.</p>
-
-<p>
-The list of addresses is available to the start function of a
-transport module, which either uses them as is or returns a new list
-(typically as configured as <c>transport_config()</c> on the
-transport module in question) in order for the correct list of
-addresses to be sent in capabilities exchange messages.</p>
-</item>
-
-<tag><c>{'Vendor-Id', Unsigned32()}</c></tag>
-<item>
-<p>
-Value of the Vendor-Id AVP sent in an outgoing capabilities
-exchange message.</p>
-</item>
-
-<tag><c>{'Product-Name', UTF8String()}</c></tag>
+<tag><c>{'Origin-Host', &dict_DiameterIdentity;}</c></tag>
+<tag><c>{'Origin-Realm', &dict_DiameterIdentity;}</c></tag>
+<tag><c>{'Host-IP-Address', [&dict_Address;]}</c></tag>
<item>
<p>
-Value of the Product-Name AVP sent in an outgoing capabilities
-exchange message.</p>
+An address list is available to the start function of a
+&transport_module;, which
+can return a new list for use in the subsequent CER or CEA.
+Host-IP-Address need not be specified if the transport start function
+returns an address list.</p>
</item>
-<tag><c>{'Origin-State-Id', Unsigned32()}</c></tag>
+<tag><c>{'Vendor-Id', &dict_Unsigned32;}</c></tag>
+<tag><c>{'Product-Name', &dict_UTF8String;}</c></tag>
+<tag><c>{'Origin-State-Id', &dict_Unsigned32;}</c></tag>
<item>
<p>
-Value of Origin-State-Id to be included in outgoing messages sent by
-diameter itself.
-In particular, the AVP will be included in CER/CEA and DWR/DWA messages.
-Optional.</p>
-
-<p>
+Origin-State-Id is optional but will be included in outgoing messages
+sent by diameter itself: CER/CEA, DWR/DWA and DPR/DPA.
Setting a value of <c>0</c> (zero) is equivalent to not setting a
-value as documented in RFC 3588.
-The function <seealso
-marker="#origin_state_id">origin_state_id/0</seealso>
-can be used as to retrieve a value that is set when the diameter
+value as documented in &the_rfc;.
+The function &origin_state_id;
+can be used as to retrieve a value that is computed when the diameter
application is started.</p>
</item>
-<tag><c>{'Supported-Vendor-Id', [Unsigned32()]}</c></tag>
-<item>
-<p>
-Values of Supported-Vendor-Id AVPs sent in an outgoing
-capabilities exchange message.
-Optional, defaults to the empty list.</p>
-</item>
-
-<tag><c>{'Auth-Application-Id', [Unsigned32()]}</c></tag>
-<item>
-<p>
-Values of Auth-Application-Id AVPs sent in an outgoing
-capabilities exchange message.
-Optional, defaults to the empty list.</p>
-</item>
-
-<tag><c>{'Inband-Security-Id', [Unsigned32()]}</c></tag>
+<tag><c>{'Supported-Vendor-Id', [&dict_Unsigned32;]}</c></tag>
+<tag><c>{'Auth-Application-Id', [&dict_Unsigned32;]}</c></tag>
+<tag><c>{'Inband-Security-Id', [&dict_Unsigned32;]}</c></tag>
<item>
<p>
-Values of Inband-Security-Id AVPs sent in an outgoing
-capabilities exchange message.
-Optional, defaults to the empty list, which is equivalent to a
-list containing only 0 (= NO_INBAND_SECURITY).</p>
-
-<p>
+Inband-Security-Id defaults to the empty list, which is equivalent to a
+list containing only 0 (= NO_INBAND_SECURITY).
If 1 (= TLS) is specified then TLS is selected if the CER/CEA received
from the peer offers it.</p>
</item>
-<tag><c>{'Acct-Application-Id', [Unsigned32()]}</c></tag>
-<item>
-<p>
-Values of Acct-Application-Id AVPs sent in an outgoing
-capabilities exchange message.
-Optional, defaults to the empty list.</p>
-</item>
-
-<tag><c>{'Vendor-Specific-Application-Id', [Grouped()]}</c></tag>
-<item>
-<p>
-Values of Vendor-Specific-Application-Id AVPs sent in
-an outgoing capabilities exchange message.
-Optional, defaults to the empty list.</p>
-</item>
-
-<tag><c>{'Firmware-Revision', Unsigned32()}</c></tag>
-<item>
-<p>
-Value of the Firmware-Revision AVP sent in an outgoing capabilities
-exchange message.
-Optional.</p>
-</item>
+<tag><c>{'Acct-Application-Id', [&dict_Unsigned32;]}</c></tag>
+<tag><c>{'Vendor-Specific-Application-Id', [&dict_Grouped;]}</c></tag>
+<tag><c>{'Firmware-Revision', &dict_Unsigned32;}</c></tag>
</taglist>
@@ -429,7 +354,7 @@ It is an error to specify duplicate tuples.</p>
An expression that can be evaluated as a function in the following
sense.</p>
-<code>
+<pre>
eval([{M,F,A} | T]) ->
apply(M, F, T ++ A);
eval([[F|A] | T]) ->
@@ -438,17 +363,22 @@ eval([F|A]) ->
apply(F, A);
eval(F) ->
eval([F]).
-</code>
+</pre>
<p>
-Applying an evaluable() <c>E</c> to an argument list <c>A</c>
+Applying an <c>&evaluable;</c>
+<c>E</c> to an argument list <c>A</c>
is meant in the sense of <c>eval([E|A])</c>.</p>
+<warning>
<p>
-Beware of using local funs (that is, fun expressions not of the
-form <c>fun Module:Name/Arity</c>) in situations in which the fun is
-not short-lived and code is to be upgraded at runtime since any
-processes retaining such a fun will have a reference to old code.</p>
+Beware of using fun expressions of the form <c>fun Name/Arity</c> in
+situations in which the fun is not short-lived
+and code is to be upgraded at runtime since any processes retaining
+such a fun will have a reference to old code.
+In particular, such a value is typically inappropriate in
+configuration passed to &start_service; or &add_transport;.</p>
+</warning>
<marker id="peer_filter"/>
</item>
@@ -456,10 +386,8 @@ processes retaining such a fun will have a reference to old code.</p>
<tag><c>peer_filter() = term()</c></tag>
<item>
<p>
-A filter passed to <seealso marker="#call">call/4</seealso>
-in order to select candidate peers for a
-<seealso marker="diameter_app#pick_peer">pick_peer/4</seealso>
-callback.
+A filter passed to &call; in order to select candidate peers for a
+&app_pick_peer; callback.
Has one of the following types.</p>
<taglist>
@@ -490,63 +418,73 @@ or any peer if the request does not contain
a <c>Destination-Realm</c> AVP.</p>
</item>
-<tag><c>{host, any|DiameterIdentity()}</c></tag>
+<tag><c>{host, any|&dict_DiameterIdentity;}</c></tag>
<item>
<p>
Matches only those peers whose <c>Origin-Host</c> has the
specified value, or all peers if the atom <c>any</c>.</p>
</item>
-<tag><c>{realm, any|DiameterIdentity()</c></tag>
+<tag><c>{realm, any|&dict_DiameterIdentity;</c></tag>
<item>
<p>
Matches only those peers whose <c>Origin-Realm</c> has the
-value, or all peers if the atom <c>any</c>.</p>
+specified value, or all peers if the atom <c>any</c>.</p>
</item>
-<tag><c>{eval, evaluable()}</c></tag>
+<tag><c>{eval, &evaluable;}</c></tag>
<item>
<p>
-Matches only those peers for which the specified evaluable() returns
+Matches only those peers for which the specified
+<c>&evaluable;</c> returns
<c>true</c> on the connection's <c>diameter_caps</c> record.
Any other return value or exception is equivalent to <c>false</c>.</p>
</item>
-<tag><c>{neg, peer_filter()}</c></tag>
+<tag><c>{neg, &peer_filter;}</c></tag>
<item>
<p>
Matches only those peers not matched by the specified filter.</p>
</item>
-<tag><c>{all, [peer_filter()]}</c></tag>
+<tag><c>{all, [&peer_filter;]}</c></tag>
<item>
<p>
-Matches only those peers matched by each filter of the specified list.</p>
+Matches only those peers matched by each filter in the specified list.</p>
</item>
-<tag><c>{any, [peer_filter()]}</c></tag>
+<tag><c>{any, [&peer_filter;]}</c></tag>
<item>
<p>
-Matches only those peers matched by at least one filter of the
+Matches only those peers matched by at least one filter in the
specified list.</p>
</item>
</taglist>
<p>
-Note that the <c>host</c> and <c>realm</c> filters examine the
-outgoing request as passed to <seealso marker="#call">call/4</seealso>,
-assuming that this is a record- or list-valued message() as documented
-in <seealso marker="diameter_app">diameter_app(3)</seealso>, and that
-the message contains at most one of each AVP.
-If this is not the case then the <c>{host|realm, DiameterIdentity()}</c>
+An invalid filter is equivalent to <c>{any,[]}</c>, a filter
+that matches no peer.</p>
+
+<note>
+<p>
+The <c>host</c> and <c>realm</c> filters examine the
+outgoing request as passed to &call;,
+assuming that this is a record- or list-valued <c>&codec_message;</c>,
+and that the message contains at most one of each AVP.
+If this is not the case then the <c>{host|realm, &dict_DiameterIdentity;}</c>
filters must be used to achieve the desired result.
-Note also that an empty host/realm (which should not be typical)
-is equivalent to an unspecified one for the purposes of filtering.</p>
+An empty <c>&dict_DiameterIdentity;</c>
+(which should not be typical)
+matches all hosts/realms for the purposes of filtering.</p>
+</note>
+<warning>
<p>
-An invalid filter is equivalent to <c>{any, []}</c>, a filter
-that matches no peer.</p>
+A <c>host</c> filter is not typically desirable when setting
+Destination-Host since it will remove peer agents from the
+candidates list.</p>
+</warning>
<marker id="service_event"/>
</item>
@@ -554,41 +492,52 @@ that matches no peer.</p>
<tag><c>service_event() = #diameter_event{}</c></tag>
<item>
<p>
-Event message sent to processes that have subscribed using <seealso
-marker="#subscribe">subscribe/1</seealso>.</p>
+An event message sent to processes that have subscribed to these using
+&subscribe;.</p>
<p>
-The <c>info</c> field of the event record can be one of the
-following.</p>
+The <c>info</c> field of the event record can have one of the
+following types.</p>
<taglist>
+<tag><c>start</c></tag>
+<tag><c>stop</c></tag>
+
+<item>
+<p>
+The service is being started or stopped.
+No event precedes a <c>start</c> event.
+No event follows a <c>stop</c> event and this event
+implies the termination of all transport processes.</p>
+</item>
+
<tag><c>{up, Ref, Peer, Config, Pkt}</c></tag>
<tag><c>{up, Ref, Peer, Config}</c></tag>
<tag><c>{down, Ref, Peer, Config}</c></tag>
<item>
-<code>
-Ref = transport_ref()
-Peer = diameter_app:peer()
-Config = {connect|listen, [transport_opt()]}
+<pre>
+Ref = &transport_ref;
+Peer = &app_peer;
+Config = {connect|listen, [&transport_opt;]}
Pkt = #diameter_packet{}
-</code>
+</pre>
<p>
The RFC 3539 watchdog state machine has
-transitioned into (<c>up</c>) or out of (<c>down</c>) the open
+transitioned into (<c>up</c>) or out of (<c>down</c>) the OKAY
state.
-If a <c>diameter_packet</c> record is present in an <c>up</c> tuple
-then there has been an exchange of capabilities exchange messages and
-the record contains the received CER or CEA, otherwise the
-connection has reestablished without the loss or transport
+If a <c>#diameter_packet{}</c> record is present in an <c>up</c> event
+then there has been a capabilties exchange on a newly established
+transport connection and the record contains the received CER or CEA.
+Otherwise a connection has reestablished without the loss or
connectivity.</p>
<p>
-Note that a single up/down event for a given peer corresponds to
-as many <seealso marker="diameter_app#peer_up">peer_up/peer_down</seealso>
-callbacks as there are Diameter applications shared by the peer,
-as determined during capablilities exchange.
+Note that a single <c>up</c>/<c>down</c> event for a given peer
+corresponds to one &app_peer_up;/&app_peer_down;
+callback for each of the Diameter applications negotiated during
+capablilities exchange.
That is, the event communicates connectivity with the
peer as a whole while the callbacks communicate connectivity with
respect to individual Diameter applications.</p>
@@ -596,102 +545,128 @@ respect to individual Diameter applications.</p>
<tag><c>{reconnect, Ref, Opts}</c></tag>
<item>
-<code>
-Ref = transport_ref()
-Opts = [transport_opt()]
-</code>
+<pre>
+Ref = &transport_ref;
+Opts = [&transport_opt;]
+</pre>
<p>
A connecting transport is attempting to establish/reestablish a
-transport connection with a peer following <c>reconnect_timer</c> or
-<c>watchdog_timer</c> expiry.</p>
+transport connection with a peer following &reconnect_timer; or
+&watchdog_timer; expiry.</p>
</item>
<tag><c>{closed, Ref, Reason, Config}</c></tag>
<item>
-<code>
-Ref = transport_ref()
-Config = {connect|listen, [transport_opt()]}
-</code>
+<pre>
+Ref = &transport_ref;
+Config = {connect|listen, [&transport_opt;]}
+</pre>
<p>
-Capabilities exchange has failed. <c>Reason</c> can be one of
-the following.</p>
+Capabilities exchange has failed.
+<c>Reason</c> can have one of the following types.</p>
<taglist>
<tag><c>{'CER', Result, Caps, Pkt}</c></tag>
<item>
-<code>
+<pre>
Result = ResultCode | {capabilities_cb, CB, ResultCode|discard}
Caps = #diameter_caps{}
Pkt = #diameter_packet{}
ResultCode = integer()
-CB = evaluable()
-</code>
+CB = &evaluable;
+</pre>
<p>
An incoming CER has been answered with the indicated result code or
discarded.
-The capabilities record contains pairs of values for the the local
-node and remote peer.
-The packet record contains the CER in question.
+<c>Caps</c> contains pairs of values for the the local node and remote
+peer.
+<c>Pkt</c> contains the CER in question.
In the case of rejection by a capabilities callback, the tuple
-indicates the rejecting callback.</p>
+contains the rejecting callback.</p>
</item>
<tag><c>{'CER', Caps, {ResultCode, Pkt}}</c></tag>
<item>
-<code>
+<pre>
ResultCode = integer()
Caps = #diameter_caps{}
Pkt = #diameter_packet{}
-</code>
+</pre>
<p>
An incoming CER contained errors and has been answered with the
indicated result code.
-The capabilities record contains only values for the the local
-node.
-The packet record contains the CER in question.</p>
+<c>Caps</c> contains only values for the the local node.
+<c>Pkt</c> contains the CER in question.</p>
+</item>
+
+<tag><c>{'CER', timeout}</c></tag>
+<item>
+<p>
+An expected CER was not received within &capx_timeout; of
+connection establishment.</p>
</item>
<tag><c>{'CEA', Result, Caps, Pkt}</c></tag>
<item>
-<code>
+<pre>
Result = integer() | atom() | {capabilities_cb, CB, ResultCode|discard}
Caps = #diameter_caps{}
Pkt = #diameter_packet{}
ResultCode = integer()
-</code>
+</pre>
<p>
An incoming CEA has been rejected for the indicated reason.
An integer-valued <c>Result</c> indicates the result code sent
by the peer.
-The capabilities record contains pairs of values for the the local
-node and remote peer.
-The packet record contains the CEA in question.
+<c>Caps</c> contains pairs of values for the the local node and remote
+peer.
+<c>Pkt</c> contains the CEA in question.
In the case of rejection by a capabilities callback, the tuple
-indicates the rejecting callback.</p>
+contains the rejecting callback.</p>
</item>
<tag><c>{'CEA', Caps, Pkt}</c></tag>
<item>
-<code>
+<pre>
Caps = #diameter_caps{}
Pkt = #diameter_packet{}
-</code>
+</pre>
<p>
-An incoming CER contained errors and has been rejected.
-The capabilities record contains only values for the the local node.
-The packet record contains the CEA in question.</p>
+An incoming CEA contained errors and has been rejected.
+<c>Caps</c> contains only values for the the local node.
+<c>Pkt</c> contains the CEA in question.</p>
+</item>
+
+<tag><c>{'CEA', timeout}</c></tag>
+<item>
+<p>
+An expected CEA was not received within &capx_timeout;
+of connection establishment.</p>
</item>
</taglist>
</item>
+<tag><c>{watchdog, Ref, PeerRef, {From, To}, Config}</c></tag>
+<item>
+<pre>
+Ref = &transport_ref;
+PeerRef = &app_peer_ref;
+From, To = initial | okay | suspect | down | reopen
+Config = {connect|listen, [transport_opt()]}
+</pre>
+
+<p>
+An RFC 3539 watchdog state machine has changed state.</p>
+</item>
+
</taglist>
<p>
@@ -704,12 +679,11 @@ info fields of forms other than the above.</p>
<tag><c>service_name() = term()</c></tag>
<item>
<p>
-The name of a service as passed to <seealso
-marker="#start_service">start_service/2</seealso> and with which the
+The name of a service as passed to &start_service; and with which the
service is identified.
There can be at most one service with a given name on a given node.
-Note that <c>erlang:make_ref/0</c> can be used to generate a service name
-that is somewhat unique.</p>
+Note that &make_ref;
+can be used to generate a service name that is somewhat unique.</p>
<marker id="service_opt"/>
</item>
@@ -717,29 +691,80 @@ that is somewhat unique.</p>
<tag><c>service_opt()</c></tag>
<item>
<p>
-Options accepted by <seealso
-marker="#start_service">start_service/2</seealso>.
-Can be any <c>capability()</c> tuple as
-well as the following.</p>
+An option passed to &start_service;.
+Can be any <c>&capability;</c> as well as the following.</p>
<taglist>
-<tag><c>{application, [application_opt()]}</c></tag>
+<tag><c>{application, [&application_opt;]}</c></tag>
<item>
<p>
Defines a Diameter application supported by the service.</p>
<p>
-A service must define one application for each Diameter application it
-intends to support.
-For an outgoing Diameter request, the application is specified by
-passing the desired application's <c>application_alias()</c> to
-<seealso marker="#call">call/4</seealso>, while for an
+A service must configure one tuple for each Diameter
+application it intends to support.
+For an outgoing Diameter request, the relevant <c>&application_alias;</c> is
+passed to &call;, while for an
incoming request the application identifier in the message
-header determines the application (and callback module), the
-application identifier being specified in the <seealso
-marker="diameter_dict">dictionary</seealso> file defining the
-application.</p>
+header determines the application, the identifier being specified in
+the application's &dictionary; file.</p>
+</item>
+
+<tag><c>{restrict_connections, false
+ | node
+ | nodes
+ | [node()]
+ | evaluable()}</c></tag>
+<item>
+<p>
+Specifies the degree to which multiple transport connections to the
+same peer are accepted by the service.</p>
+
+<p>
+If type <c>[node()]</c> then a connection is rejected if another already
+exists on any of the specified nodes.
+Values of type <c>false</c>, <c>node</c>, <c>nodes</c> or
+&evaluable; are equivalent to
+values <c>[]</c>, <c>[node()]</c>, <c>[node()|nodes()]</c> and the
+evaluated value, respectively, evaluation of each expression taking
+place whenever a new connection is to be established.
+Note that <c>false</c> allows an unlimited number of connections to be
+established with the same peer.</p>
+
+<p>
+Multiple connections are independent and governed
+by their own peer and watchdog state machines.</p>
+
+<p>
+Defaults to <c>nodes</c>.</p>
+</item>
+
+<tag><c>{sequence, {H,N} | &evaluable;}</c></tag>
+<item>
+<p>
+Specifies a constant value <c>H</c> for the topmost <c>32-N</c> bits of
+of 32-bit End-to-End and Hop-by-Hop identifiers generated
+by the service, either explicity or as a return value of a function
+to be evaluated at &start_service;.
+In particular, an identifier <c>Id</c> is mapped to a new identifier
+as follows.</p>
+<pre>
+(H bsl N) bor (Id band ((1 bsl N) - 1))
+</pre>
+<p>
+Note that &the_rfc; requires that End-to-End identifiers remain unique
+for a period of at least 4 minutes and that this and the call rate
+places a lower bound on the appropriate values of <c>N</c>:
+at a rate of <c>R</c> requests per second an <c>N</c>-bit counter
+traverses all of its values in <c>(1 bsl N) div (R*60)</c> minutes so
+the bound is <c>4*R*60 =&lt; 1 bsl N</c>.</p>
+
+<p><c>N</c> must lie in the range <c>0..32</c> and <c>H</c> must be a
+non-negative integer less than <c>1 bsl (32-N)</c>.</p>
+
+<p>
+Defaults to <c>{0,32}</c>.</p>
</item>
</taglist>
@@ -750,159 +775,312 @@ application.</p>
<tag><c>transport_opt()</c></tag>
<item>
<p>
-Options accepted by <seealso
-marker="#add_transport">add_transport/2</seealso>.</p>
+An option passed to &add_transport;.
+Has one of the following types.</p>
<taglist>
-<tag><c>{transport_module, atom()}</c></tag>
+<marker id="applications"/>
+<tag><c>{applications, [&application_alias;]}</c></tag>
<item>
<p>
-A module implementing a transport process as defined in <seealso
-marker="diameter_transport">diameter_transport(3)</seealso>.
-Defaults to <c>diameter_tcp</c> if unspecified.</p>
+The list of Diameter applications to which the transport should be
+restricted.
+Defaults to all applications configured on the service in question.
+Applications not configured on the service in question are ignored.</p>
+</item>
+
+<marker id="capabilities"/>
+<tag><c>{capabilities, [&capability;]}</c></tag>
+<item>
+<p>
+AVP's used to construct outgoing CER/CEA messages.
+Values take precedence over any specified on the service in
+question.</p>
<p>
-The interface required of a transport module is documented in <seealso
-marker="diameter_transport">diameter_transport(3)</seealso>.</p>
+Specifying a capability as a transport option
+may be particularly appropriate for Inband-Security-Id, in case
+TLS is desired over TCP as implemented by &man_tcp;.</p>
</item>
-<tag><c>{transport_config, term()}</c></tag>
+<marker id="capabilities_cb"/>
+<tag><c>{capabilities_cb, &evaluable;}</c></tag>
<item>
<p>
-A term passed as the third argument to the <seealso
-marker="diameter_transport#start">start/3</seealso> function of
-the relevant <c>transport_module</c> in order to start a transport process.
-Defaults to the empty list if unspecified.</p>
-</item>
+A callback invoked upon reception of CER/CEA during capabilities
+exchange in order to ask whether or not the connection should
+be accepted.
+Applied to the <c>&transport_ref;</c> and
+<c>#diameter_caps{}</c> record of the connection.</p>
+
+<p>
+The return value can have one of the following types.</p>
-<tag><c>{applications, [application_alias()]}</c></tag>
+<taglist>
+<tag><c>ok</c></tag>
<item>
<p>
-The list of Diameter applications to which usage of the transport
-should be restricted.
-Defaults to all applications configured on the service
-in question.</p>
+Accept the connection.</p>
</item>
-<tag><c>{capabilities, [capability()]}</c></tag>
+<tag><c>integer()</c></tag>
<item>
<p>
-AVP's used to construct outgoing CER/CEA messages.
-Any AVP specified takes precedence over a corresponding value specified
-for the service in question.</p>
+Causes an incoming CER to be answered with the specified Result-Code.</p>
+</item>
+<tag><c>discard</c></tag>
+<item>
<p>
-Specifying a capability as a transport option
-may be particularly appropriate for Inband-Security-Id in case
-TLS is desired over TCP as implemented by
-<seealso marker="diameter_tcp">diameter_tcp(3)</seealso> but
-not over SCTP as implemented by
-<seealso marker="diameter_sctp">diameter_sctp(3)</seealso>.</p>
+Causes an incoming CER to be discarded without CEA being sent.</p>
</item>
-<tag><c>{capabilities_cb, evaluable()}</c></tag>
+<tag><c>unknown</c></tag>
<item>
<p>
-A callback invoked upon reception of CER/CEA during capabilities
-exchange in order to ask whether or not the connection should
-be accepted.
-Applied to the transport reference (as returned by <seealso
-marker="#add_transport">add_transport/2</seealso>) and
-<c>diameter_caps</c> record of the connection.
-Returning <c>ok</c> accepts the connection.
-Returning <c>integer()</c> causes an incoming
-CER to be answered with the specified Result-Code.
-Returning <c>discard</c> causes an incoming CER to
-be discarded.
-Returning <c>unknown</c> is equivalent to returning <c>3010</c>,
-DIAMETER_UNKNOWN_PEER.
-Returning anything but <c>ok</c> or a 2xxx series result
-code causes the transport connection to be broken.</p>
+Equivalent to returning <c>3010</c>, DIAMETER_UNKNOWN_PEER.</p>
+</item>
+</taglist>
<p>
-Multiple <c>capabilities_cb</c> options can be specified, in which
+Returning anything but <c>ok</c> or a 2xxx series result
+code causes the transport connection to be broken.
+Multiple &capabilities_cb;
+options can be specified, in which
case the corresponding callbacks are applied until either all return
<c>ok</c> or one does not.</p>
</item>
-<tag><c>{watchdog_timer, TwInit}</c></tag>
+<marker id="capx_timeout"/>
+<tag><c>{capx_timeout, &dict_Unsigned32;}</c></tag>
<item>
-<code>
-TwInit = Unsigned32()
- | {M,F,A}
-</code>
+<p>
+The number of milliseconds after which a transport process having an
+established transport connection will be terminated if the expected
+capabilities exchange message (CER or CEA) is not received from the peer.
+For a connecting transport, the timing reconnection attempts is
+governed by &watchdog_timer; or
+&reconnect_timer; expiry.
+For a listening transport, the peer determines the timing.</p>
<p>
-The RFC 3539 watchdog timer.
-An integer value is interpreted as the RFC's TwInit in milliseconds,
-a jitter of &plusmn; 2 seconds being added at each rearming of the
-timer to compute the RFC's Tw.
-An MFA is expected to return the RFC's Tw directly, with jitter
-applied, allowing the jitter calculation to be performed by
-the callback.</p>
+Defaults to 10000.</p>
+</item>
+
+<marker id="disconnect_cb"/>
+<tag><c>{disconnect_cb, &evaluable;}</c></tag>
+<item>
<p>
-An integer value must be at least 6000 as required by RFC 3539.
-Defaults to 30000 if unspecified.</p>
+A callback invoked prior to terminating the transport process of a
+transport connection having watchdog state <c>OKAY</c>.
+Applied to <c>Reason=transport|service|application</c> and the
+<c>&transport_ref;</c> and
+<c>&app_peer;</c>
+in question, <c>Reason</c> indicating whether the the diameter
+application is being stopped, the service in question is being stopped
+at &stop_service; or
+the transport in question is being removed at &remove_transport;,
+respectively.</p>
+
+<p>
+The return value can have one of the following types.</p>
+
+<taglist>
+<tag><c>{dpr, [option()]}</c></tag>
+<item>
+<p>
+Causes Disconnect-Peer-Request to be sent to the peer, the transport
+process being terminated following reception of
+Disconnect-Peer-Answer or timeout.
+An <c>option()</c> can be one of the following.</p>
+
+<taglist>
+<tag><c>{cause, 0|rebooting|1|busy|2|goaway}</c></tag>
+<item>
+<p>
+The Disconnect-Cause to send, <c>REBOOTING</c>, <c>BUSY</c> and
+<c>DO_NOT_WANT_TO_TALK_TO_YOU</c> respectively.
+Defaults to <c>rebooting</c> for <c>Reason=service|application</c> and
+<c>goaway</c> for <c>Reason=transport</c>.</p>
</item>
+<tag><c>{timeout, &dict_Unsigned32;}</c></tag>
+<item>
+<p>
+The number of milliseconds after which the transport process is
+terminated if DPA has not been received.
+Defaults to 1000.</p>
+</item>
+</taglist>
+</item>
+
+<tag><c>dpr</c></tag>
+<item>
+<p>
+Equivalent to <c>{dpr, []}</c>.</p>
+</item>
+
+<tag><c>close</c></tag>
+<item>
+<p>
+Causes the transport process to be terminated without
+Disconnect-Peer-Request being sent to the peer.</p>
+</item>
+
+<tag><c>ignore</c></tag>
+<item>
+<p>
+Equivalent to not having configured the callback.</p>
+</item>
+</taglist>
+
+<p>
+Multiple &disconnect_cb;
+options can be specified, in which
+case the corresponding callbacks are applied until one of them returns
+a value other than <c>ignore</c>.
+All callbacks returning <c>ignore</c> is equivalent to not having
+configured them.</p>
+
+<p>
+Defaults to a single callback returning <c>dpr</c>.</p>
+</item>
+
+<marker id="reconnect_timer"/>
<tag><c>{reconnect_timer, Tc}</c></tag>
<item>
-<code>
-Tc = Unsigned32()
-</code>
+<pre>
+Tc = &dict_Unsigned32;
+</pre>
<p>
-For a connecting transport, the RFC 3588 Tc timer, in milliseconds.
-Note that this timer determines the frequency with which the transport
+For a connecting transport, the &the_rfc; Tc timer, in milliseconds.
+Note that this timer determines the frequency with which a transport
will attempt to establish a connection with its peer only <em>before</em>
an initial connection is established: once there is an initial
-connection it's watchdog_timer that determines the frequency of
-reconnection attempts, as required by RFC 3539.</p>
+connection it's &watchdog_timer; that determines the
+frequency of reconnection attempts, as required by RFC 3539.</p>
<p>
For a listening transport, the timer specifies the time after which a
previously connected peer will be forgotten: a connection after this time is
regarded as an initial connection rather than a reestablishment,
-causing the RFC 3539 state machine to pass to state OPEN rather than
+causing the RFC 3539 state machine to pass to state OKAY rather than
REOPEN.
-Note that these semantics are not goverened by the RFC and
-that a listening transport's reconnect_timer should be greater than its
-peers's Tc plus jitter.</p>
+Note that these semantics are not governed by the RFC and
+that a listening transport's &reconnect_timer; should be greater
+than its peer's Tw plus jitter.</p>
<p>
Defaults to 30000 for a connecting transport and 60000 for a listening
transport.</p>
</item>
+<marker id="transport_config"/>
+<tag><c>{transport_config, term()}</c></tag>
+<tag><c>{transport_config, term(), &dict_Unsigned32;}</c></tag>
+<item>
+<p>
+A term passed as the third argument to the &transport_start; function of
+the relevant &transport_module; in order to
+start a transport process.
+Defaults to the empty list if unspecified.</p>
+
+<p>
+The 3-tuple form additionally specifies an interval, in milliseconds,
+after which a started transport process should be terminated if it has
+not yet established a connection.
+For example, the following options on a connecting transport
+request a connection with one peer over SCTP or another
+(typically the same) over TCP.</p>
+
+<pre>
+{transport_module, diameter_sctp}
+{transport_config, SctpOpts, 5000}
+{transport_module, diameter_tcp}
+{transport_config, TcpOpts}
+</pre>
+
+<p>
+To listen on both SCTP and TCP, define one transport for each.</p>
+</item>
+
+<marker id="transport_module"/>
+<tag><c>{transport_module, atom()}</c></tag>
+<item>
+<p>
+A module implementing a transport process as defined in &man_transport;.
+Defaults to <c>diameter_tcp</c> if unspecified.</p>
+
+<p>
+Multiple <c>transport_module</c> and &transport_config;
+options are allowed.
+The order of these is significant in this case (and only in this case),
+a <c>transport_module</c> being paired with the first
+&transport_config;
+following it in the options list, or the default value for trailing
+modules.
+Transport starts will be attempted with each of the
+modules in order until one establishes a connection within the
+corresponding timeout (see below) or all fail.</p>
+</item>
+
+<marker id="watchdog_timer"/>
+<tag><c>{watchdog_timer, TwInit}</c></tag>
+<item>
+<pre>
+TwInit = &dict_Unsigned32;
+ | {M,F,A}
+</pre>
+
+<p>
+The RFC 3539 watchdog timer.
+An integer value is interpreted as the RFC's TwInit in milliseconds,
+a jitter of &plusmn; 2 seconds being added at each rearming of the
+timer to compute the RFC's Tw.
+An MFA is expected to return the RFC's Tw directly, with jitter
+applied, allowing the jitter calculation to be performed by
+the callback.</p>
+
+<p>
+An integer value must be at least 6000 as required by RFC 3539.
+Defaults to 30000 if unspecified.</p>
+</item>
+
</taglist>
<p>
Unrecognized options are silently ignored but are returned unmodified
-by <seealso
-marker="#service_info">service_info/2</seealso> and can be referred to
-in predicate functions passed to <seealso
-marker="#remove_transport">remove_transport/2</seealso>.</p>
+by &service_info; and can be referred to
+in predicate functions passed to &remove_transport;.</p>
+<marker id="transport_ref"/>
+</item>
+
+<tag><c>transport_ref() = reference()</c></tag>
+<item>
+<p>
+An reference returned by &add_transport; that
+identifies the configuration.</p>
</item>
</taglist>
</section>
-<marker id="add_transport"/>
<funcs>
<!-- ===================================================================== -->
<func>
-<name>add_transport(SvcName, {connect|listen, Options})
+<name>add_transport(SvcName, {connect|listen, [Opt]})
-> {ok, Ref} | {error, Reason}</name>
<fsummary>Add transport capability to a service.</fsummary>
<type>
-<v>SvcName = service_name()</v>
-<v>Options = [transport_opt()]</v>
-<v>Ref = ref()</v>
+<v>SvcName = &service_name;</v>
+<v>Opt = &transport_opt;</v>
+<v>Ref = &transport_ref;</v>
<v>Reason = term()</v>
</type>
<desc>
@@ -910,8 +1088,8 @@ marker="#remove_transport">remove_transport/2</seealso>.</p>
Add transport capability to a service.</p>
<p>
-The service will start a transport process(es) in order to establish a
-connection with the peer, either by connecting to the peer
+The service will start transport processes as required in order to
+establish a connection with the peer, either by connecting to the peer
(<c>connect</c>) or by accepting incoming connection requests
(<c>listen</c>).
A connecting transport establishes transport connections with at most
@@ -921,84 +1099,77 @@ one peer, an listening transport potentially with many.</p>
The diameter application takes responsibility for exchanging
CER/CEA with the peer.
Upon successful completion of capabilities exchange the service
-calls each relevant application module's <seealso
-marker="diameter_app#peer_up">peer_up/3</seealso> callback
+calls each relevant application module's &app_peer_up; callback
after which the caller can exchange Diameter messages with the peer over
the transport.
In addition to CER/CEA, the service takes responsibility for the
-handling of DWR/DWA and required by RFC 3539 as well as for DPR/DPA.</p>
+handling of DWR/DWA and required by RFC 3539, as well as for DPR/DPA.</p>
<p>
The returned reference uniquely identifies the transport within the
scope of the service.
Note that the function returns before a transport connection has been
-established.
+established.</p>
+
+<note>
+<p>
It is not an error to add a transport to a service that has not yet
been configured: a service can be started after configuring
-transports.</p>
+its transports.</p>
+</note>
-<marker id="call"/>
</desc>
</func>
<!-- ===================================================================== -->
<func>
-<name>call(SvcName, App, Request, Options) -> ok | Answer | {error, Reason}</name>
+<name>call(SvcName, App, Request, [Opt]) -> Answer | ok | {error, Reason}</name>
<fsummary>Send a Diameter request message.</fsummary>
<type>
-<v>SvcName = service_name()</v>
-<v>App = application_alias()</v>
-<v>Request = diameter_app:message() | term()</v>
+<v>SvcName = &service_name;</v>
+<v>App = &application_alias;</v>
+<v>Request = &codec_message;</v>
<v>Answer = term()</v>
-<v>Options = [call_opt()]</v>
+<v>Opt = &call_opt;</v>
</type>
<desc>
<p>
-Send a Diameter request message and possibly return the answer or error.</p>
+Send a Diameter request message.</p>
<p>
-<c>App</c> identifies the Diameter application in which the request is
+<c>App</c> specifies the Diameter application in which the request is
defined and callbacks to the corresponding callback module
-will follow as described below and in <seealso
-marker="diameter_app">diameter_app(3)</seealso>.
-Unless the <c>detach</c> option has been specified to cause an earlier
-return, the call returns either when an answer message is received
-from the peer or an error occurs.
-In the case of an answer, the return value is as returned by a
-<seealso
-marker="diameter_app#handle_answer">handle_answer/4</seealso>
-callback.
-In the case of an error, whether or not the error is returned directly
-by diameter or from a <seealso
-marker="diameter_app#handle_error">handle_error/4</seealso>
+will follow as described below and in &man_app;.
+Unless the <c>detach</c> option is specified, the call returns either
+when an answer message is received from the peer or an error occurs.
+In the answer case, the return value is as returned by a
+&app_handle_answer; callback.
+In the error case, whether or not the error is returned directly
+by diameter or from a &app_handle_error;
callback depends on whether or not the outgoing request is
-successfully encoded for transmission from the peer, the cases being
+successfully encoded for transmission to the peer, the cases being
documented below.</p>
<p>
If there are no suitable peers, or if
-<seealso marker="diameter_app#pick_peer">pick_peer/4</seealso>
-rejects them by returning 'false', then <c>{error, no_connection}</c>
+&app_pick_peer;
+rejects them by returning <c>false</c>, then <c>{error,no_connection}</c>
is returned.
-Otherwise <seealso marker="diameter_app#pick_peer">pick_peer/4</seealso>
-is followed by a
-<seealso
-marker="diameter_app#prepare_request">prepare_request/3</seealso>
-callback, the message is encoded and sent.</p>
+Otherwise &app_pick_peer; is followed by a
+&app_prepare_request; callback, the message is encoded and then sent.</p>
<p>
There are several error cases which may prevent an
answer from being received and passed to a
-<seealso marker="diameter_app#handle_answer">handle_answer/4</seealso>
-callback:</p>
+&app_handle_answer; callback:</p>
<list>
<item>
<p>
If the initial encode of the outgoing request
-fails, then the request process fails and <c>{error, encode}</c>
+fails, then the request process fails and <c>{error,encode}</c>
is returned.</p>
</item>
@@ -1006,16 +1177,14 @@ is returned.</p>
<p>
If the request is successfully encoded and sent but
the answer times out then a
-<seealso marker="diameter_app#handle_error">handle_error/4</seealso>
-callback takes place with <c>Reason = timeout</c>.</p>
+&app_handle_error; callback takes place with <c>Reason = timeout</c>.</p>
</item>
<item>
<p>
If the request is successfully encoded and sent but the service in
question is stopped before an answer is received then a
-<seealso marker="diameter_app#handle_error">handle_error/4</seealso>
-callback takes place <c>Reason = cancel</c>.</p>
+&app_handle_error; callback takes place with <c>Reason = cancel</c>.</p>
</item>
<item>
@@ -1024,25 +1193,18 @@ If the transport connection with the peer goes down after the request
has been sent but before an answer has been received then an attempt
is made to resend the request to an alternate peer.
If no such peer is available, or if the subsequent
-<seealso marker="diameter_app#pick_peer">pick_peer/4</seealso>
-callback rejects the candidates, then a
-<seealso marker="diameter_app#handle_error">handle_error/4</seealso>
-callback takes place with <c>Reason = failover</c>.
-If a peer is selected then a
-<seealso
-marker="diameter_app#prepare_retransmit">prepare_retransmit/3</seealso>
+&app_pick_peer; callback rejects the candidates, then a
+&app_handle_error; callback takes place with <c>Reason = failover</c>.
+If a peer is selected then a &app_prepare_retransmit;
callback takes place, after which the semantics are the same as
-following an initial
-<seealso marker="diameter_app#prepare_request">
-prepare_request/3</seealso>
-callback.</p>
+following an initial &app_prepare_request; callback.</p>
</item>
<item>
<p>
If an encode error takes place during
retransmission then the request process fails and
-<c>{error, failure}</c> is returned.</p>
+<c>{error,failure}</c> is returned.</p>
</item>
<item>
@@ -1050,7 +1212,7 @@ retransmission then the request process fails and
If an application callback made in processing the request fails
(pick_peer, prepare_request, prepare_retransmit,
handle_answer or handle_error) then either
-<c>{error, encode}</c> or <c>{error, failure}</c>
+<c>{error,encode}</c> or <c>{error,failure}</c>
is returned depending on whether or not there has been an
attempt to send the request over the transport.</p>
</item>
@@ -1058,18 +1220,17 @@ attempt to send the request over the transport.</p>
</list>
<p>
-Note that <c>{error, encode}</c> is the only return value which
+Note that <c>{error,encode}</c> is the only return value which
guarantees that the request has <em>not</em> been sent over the
-transport.</p>
+transport connection.</p>
-<marker id="origin_state_id"/>
</desc>
</func>
<!-- ===================================================================== -->
<func>
-<name>origin_state_id() -> Unsigned32()</name>
+<name>origin_state_id() -> &dict_Unsigned32;</name>
<fsummary>Returns a reasonable Origin-State-Id.</fsummary>
<desc>
<p>
@@ -1078,26 +1239,26 @@ outgoing messages.</p>
<p>
The value returned is the number of seconds since 19680120T031408Z,
-the first value that can be encoded as a Diameter Time(),
+the first value that can be encoded as a Diameter <c>&dict_Time;</c>,
at the time the diameter application was started.</p>
-<marker id="remove_transport"/>
</desc>
</func>
<!-- ===================================================================== -->
<func>
-<name>remove_transport(SvcName, Pred) -> ok</name>
+<name>remove_transport(SvcName, Pred) -> ok | {error, Reason}</name>
<fsummary>Remove previously added transports.</fsummary>
<type>
-<v>SvcName = service_name()</v>
-<v>Pred = Fun | MFA | ref() | list() | true | false</v>
+<v>SvcName = &service_name;</v>
+<v>Pred = Fun | MFA | &transport_ref; | list() | true | false</v>
<v></v>
-<v>Fun = fun((reference(), connect|listen, list()) -> boolean())</v>
-<v>&nbsp;&nbsp;&nbsp; | fun((reference(), list()) -> boolean())</v>
+<v>Fun = fun((&transport_ref;, connect|listen, list()) -> boolean())</v>
+<v>&nbsp;&nbsp;&nbsp; | fun((&transport_ref;, list()) -> boolean())</v>
<v>&nbsp;&nbsp;&nbsp; | fun((list()) -> boolean())</v>
<v>MFA = {atom(), atom(), list()}</v>
+<v>Reason = term()</v>
</type>
<desc>
<p>
@@ -1107,50 +1268,329 @@ Remove previously added transports.</p>
<c>Pred</c> determines which transports to remove.
An arity-3-valued <c>Pred</c> removes all transports for which
<c>Pred(Ref, Type, Opts)</c> returns <c>true</c>, where <c>Type</c> and
-<c>Opts</c> are as passed to <seealso
-marker="#add_transport">add_transport/2</seealso> and <c>Ref</c> is
-as returned by the corresponding call.
+<c>Opts</c> are as passed to &add_transport; and <c>Ref</c> is
+as returned by it.
The remaining forms are equivalent to an arity-3 fun as follows.</p>
-<code>
-Pred = fun(reference(), list()): fun(Ref, _, Opts) -> Pred(Ref, Opts) end
-Pred = fun(list()): fun(_, _, Opts) -> Pred(Opts) end
-Pred = reference(): fun(Ref, _, _) -> Pred == Ref end
-Pred = list(): fun(_, _, Opts) -> [] == Pred -- Opts end
-Pred = true: fun(_, _, _) -> true end
-Pred = false: fun(_, _, _) -> false end
+<pre>
+Pred = fun(transport_ref(), list()): fun(Ref, _, Opts) -> Pred(Ref, Opts) end
+Pred = fun(list()): fun(_, _, Opts) -> Pred(Opts) end
+Pred = transport_ref(): fun(Ref, _, _) -> Pred == Ref end
+Pred = list(): fun(_, _, Opts) -> [] == Pred -- Opts end
+Pred = true: fun(_, _, _) -> true end
+Pred = false: fun(_, _, _) -> false end
Pred = {M,F,A}: fun(Ref, Type, Opts) -> apply(M, F, [Ref, Type, Opts | A]) end
-</code>
+</pre>
<p>
-Removing a transport causes all associated transport connections to
-be broken.
-A base application DPR message with
-Disconnect-Cause <c>DO_NOT_WANT_TO_TALK_TO_YOU</c> will be sent
-to each connected peer before disassociating the transport configuration
-from the service and terminating the transport upon reception of
-DPA or timeout.</p>
-
-<!-- TODO: document the timeout value, possibly make configurable. -->
+Removing a transport causes the corresponding transport processes to
+be terminated.
+Whether or not a DPR message is sent to a peer is
+controlled by value of &disconnect_cb;
+configured on the transport.</p>
-<marker id="service_info"/>
</desc>
</func>
<!-- ===================================================================== -->
<func>
-<name>service_info(SvcName, Item) -> Value</name>
-<fsummary>Return specific information about a started service.</fsummary>
+<name>service_info(SvcName, Info) -> term()</name>
+<fsummary>Return information about a started service.</fsummary>
<type>
-<v>SvcName = service_name()</v>
-<v>Value = term()</v>
+<v>SvcName = &service_name;</v>
+<v>Info = Item | [Info]</v>
+<v>Item = atom()</v>
</type>
<desc>
<p>
-Return information about a started service.</p>
+Return information about a started service.
+Requesting info for an unknown service causes <c>undefined</c> to be
+returned.
+Requesting a list of items causes a tagged list to be
+returned.</p>
+
+<p>
+<c>Item</c> can be one of the following.</p>
+
+<taglist>
+
+<tag><c>'Origin-Host'</c></tag>
+<tag><c>'Origin-Realm'</c></tag>
+<tag><c>'Vendor-Id'</c></tag>
+<tag><c>'Product-Name'</c></tag>
+<tag><c>'Origin-State-Id'</c></tag>
+<tag><c>'Host-IP-Address'</c></tag>
+<tag><c>'Supported-Vendor'</c></tag>
+<tag><c>'Auth-Application-Id'</c></tag>
+<tag><c>'Inband-Security-Id'</c></tag>
+<tag><c>'Acct-Application-Id'</c></tag>
+<tag><c>'Vendor-Specific-Application-Id'</c></tag>
+<tag><c>'Firmware-Revision'</c></tag>
+<item>
+<p>
+Return a capability value as configured with &start_service;.</p>
+</item>
+
+<tag><c>applications</c></tag>
+<item>
+<p>
+Return the list of applications as configured with &start_service;.
+</p>
+</item>
+
+<tag><c>capabilities</c></tag>
+<item>
+<p>
+Return a tagged list of all capabilities values as configured with
+&start_service;.</p>
+</item>
+
+<tag><c>transport</c></tag>
+<item>
+<p>
+Return a list containing one entry for each of the service's transport
+as configured with &add_transport;.
+Each entry is a tagged list containing both configuration and
+information about established peer connections.
+An example return value with for a client service with Origin-Host
+"client.example.com" configured with a single transport connected to
+"server.example.com" might look as follows.</p>
+
+<pre>
+[[{ref,#Ref&lt;0.0.0.93>},
+ {type,connect},
+ {options,[{transport_module,diameter_tcp},
+ {transport_config,[{ip,{127,0,0,1}},
+ {raddr,{127,0,0,1}},
+ {rport,3868},
+ {reuseaddr,true}]}]},
+ {watchdog,{&lt;0.66.0>,{1346,171491,996448},okay}},
+ {peer,{&lt;0.67.0>,{1346,171491,999906}}},
+ {apps,[{0,common}]},
+ {caps,[{origin_host,{"client.example.com","server.example.com"}},
+ {origin_realm,{"example.com","example.com"}},
+ {host_ip_address,{[{127,0,0,1}],[{127,0,0,1}]}},
+ {vendor_id,{0,193}},
+ {product_name,{"Client","Server"}},
+ {origin_state_id,{[],[]}},
+ {supported_vendor_id,{[],[]}},
+ {auth_application_id,{[0],[0]}},
+ {inband_security_id,{[],[0]}},
+ {acct_application_id,{[],[]}},
+ {vendor_specific_application_id,{[],[]}},
+ {firmware_revision,{[],[]}},
+ {avp,{[],[]}}]},
+ {port,[{owner,&lt;0.69.0>},
+ {module,diameter_tcp},
+ {socket,{{127,0,0,1},48758}},
+ {peer,{{127,0,0,1},3868}},
+ {statistics,[{recv_oct,656},
+ {recv_cnt,6},
+ {recv_max,148},
+ {recv_avg,109},
+ {recv_dvi,19},
+ {send_oct,836},
+ {send_cnt,6},
+ {send_max,184},
+ {send_avg,139},
+ {send_pend,0}]}]},
+ {statistics,[{{{0,258,0},recv},3},
+ {{{0,258,1},send},3},
+ {{{0,257,0},recv},1},
+ {{{0,257,1},send},1},
+ {{{0,258,0},recv,{'Result-Code',2001}},3},
+ {{{0,280,1},recv},2},
+ {{{0,280,0},send},2}]}]]
+</pre>
+
+<p>
+Here <c>ref</c> is a <c>&transport_ref;</c> and <c>options</c>
+the corresponding <c>&transport_opt;</c> list passed to
+&add_transport;.
+The <c>watchdog</c> entry shows the state of a connection's RFC 3539 watchdog
+state machine.
+The <c>peer</c> entry identifies the <c>&app_peer_ref;</c> for
+which there will have been &app_peer_up; callbacks for the
+Diameter applications identified by the <c>apps</c> entry,
+<c>common</c> being the <c>&application_alias;</c>.
+The <c>caps</c> entry identifies the capabilities sent by the local
+node and received from the peer during capabilities exchange.
+The <c>port</c> entry displays socket-level information about the
+transport connection.
+The <c>statistics</c> entry presents Diameter-level counters,
+an entry like <c>{{{0,280,1},recv},2}</c> saying that the client has
+received 2 DWR messages: <c>{0,280,1} = {Application_Id, Command_Code,
+R_Flag}</c>.</p>
+
+<p>
+Note that <c>watchdog</c>, <c>peer</c>, <c>apps</c>, <c>caps</c>
+and <c>port</c> entries depend on connectivity
+with the peer and may not be present.
+Note also that the <c>statistics</c> entry presents values acuumulated
+during the lifetime of the transport configuration.</p>
+
+<p>
+A listening transport presents its information slightly differently
+since there may be multiple accepted connections for the same
+<c>&transport_ref;</c>.
+The <c>transport</c> info returned by a server with a single client
+connection might look as follows.</p>
+
+<pre>
+[[{ref,#Ref&lt;0.0.0.61>},
+ {type,listen},
+ {options,[{transport_module,diameter_tcp},
+ {transport_config,[{reuseaddr,true},
+ {ip,{127,0,0,1}},
+ {port,3868}]}]},
+ {accept,[[{watchdog,{&lt;0.56.0>,{1346,171481,226895},okay}},
+ {peer,{&lt;0.58.0>,{1346,171491,999511}}},
+ {apps,[{0,common}]},
+ {caps,[{origin_host,{"server.example.com","client.example.com"}},
+ {origin_realm,{"example.com","example.com"}},
+ {host_ip_address,{[{127,0,0,1}],[{127,0,0,1}]}},
+ {vendor_id,{193,0}},
+ {product_name,{"Server","Client"}},
+ {origin_state_id,{[],[]}},
+ {supported_vendor_id,{[],[]}},
+ {auth_application_id,{[0],[0]}},
+ {inband_security_id,{[],[]}},
+ {acct_application_id,{[],[]}},
+ {vendor_specific_application_id,{[],[]}},
+ {firmware_revision,{[],[]}},
+ {avp,{[],[]}}]},
+ {port,[{owner,&lt;0.62.0>},
+ {module,diameter_tcp},
+ {socket,{{127,0,0,1},3868}},
+ {peer,{{127,0,0,1},48758}},
+ {statistics,[{recv_oct,1576},
+ {recv_cnt,16},
+ {recv_max,184},
+ {recv_avg,98},
+ {recv_dvi,26},
+ {send_oct,1396},
+ {send_cnt,16},
+ {send_max,148},
+ {send_avg,87},
+ {send_pend,0}]}]}],
+ [{watchdog,{&lt;0.72.0>,{1346,171491,998404},initial}}]]},
+ {statistics,[{{{0,280,0},recv},7},
+ {{{0,280,1},send},7},
+ {{{0,258,0},send,{'Result-Code',2001}},3},
+ {{{0,258,1},recv},3},
+ {{{0,258,0},send},3},
+ {{{0,280,1},recv},5},
+ {{{0,280,0},send},5},
+ {{{0,257,1},recv},1},
+ {{{0,257,0},send},1}]}]]
+</pre>
+
+<p>
+The information presented here is as in the <c>connect</c> case except
+that the client connections are grouped under an <c>accept</c> tuple.</p>
+
+</item>
+
+<tag><c>connections</c></tag>
+<item>
+<p>
+Return a list containing one entry for every established transport
+connection whose watchdog state machine is not in the <c>down</c>
+state.
+This is a flat view of <c>transport</c> info which lists only active
+connections and for which Diameter-level statistics are accumulated
+only for the lifetime of the transport connection.
+A return value for the server above might look as follows.</p>
+
+<pre>
+[[{ref,#Ref&lt;0.0.0.61>},
+ {type,accept},
+ {options,[{transport_module,diameter_tcp},
+ {transport_config,[{reuseaddr,true},
+ {ip,{127,0,0,1}},
+ {port,3868}]}]},
+ {watchdog,{&lt;0.56.0>,{1346,171481,226895},okay}},
+ {peer,{&lt;0.58.0>,{1346,171491,999511}}},
+ {apps,[{0,common}]},
+ {caps,[{origin_host,{"server.example.com","client.example.com"}},
+ {origin_realm,{"example.com","example.com"}},
+ {host_ip_address,{[{127,0,0,1}],[{127,0,0,1}]}},
+ {vendor_id,{193,0}},
+ {product_name,{"Server","Client"}},
+ {origin_state_id,{[],[]}},
+ {supported_vendor_id,{[],[]}},
+ {auth_application_id,{[0],[0]}},
+ {inband_security_id,{[],[]}},
+ {acct_application_id,{[],[]}},
+ {vendor_specific_application_id,{[],[]}},
+ {firmware_revision,{[],[]}},
+ {avp,{[],[]}}]},
+ {port,[{owner,&lt;0.62.0>},
+ {module,diameter_tcp},
+ {socket,{{127,0,0,1},3868}},
+ {peer,{{127,0,0,1},48758}},
+ {statistics,[{recv_oct,10124},
+ {recv_cnt,132},
+ {recv_max,184},
+ {recv_avg,76},
+ {recv_dvi,9},
+ {send_oct,10016},
+ {send_cnt,132},
+ {send_max,148},
+ {send_avg,75},
+ {send_pend,0}]}]},
+ {statistics,[{{{0,280,0},recv},62},
+ {{{0,280,1},send},62},
+ {{{0,258,0},send,{'Result-Code',2001}},3},
+ {{{0,258,1},recv},3},
+ {{{0,258,0},send},3},
+ {{{0,280,1},recv},66},
+ {{{0,280,0},send},66},
+ {{{0,257,1},recv},1},
+ {{{0,257,0},send},1}]}]]
+</pre>
+
+<p>
+Note that there may be multiple entries with the same <c>ref</c>, in
+contrast to <c>transport</c> info.</p>
+</item>
+
+<tag><c>statistics</c></tag>
+<item>
+<p>
+Return a <c>{{Counter, Ref}, non_neg_integer()}</c> list of counter values.
+<c>Ref</c> can be either a <c>&transport_ref;</c>
+or a <c>&app_peer_ref;</c>.
+Entries for the latter are folded into corresponding entries for the
+former as peer connections go down.
+Entries for both are removed at &remove_transport;.
+The Diameter-level statistics returned by <c>transport</c> and
+<c>connections</c> info are based upon these entries.</p>
+</item>
+
+<tag><c>&app_peer_ref;</c></tag>
+<item>
+<p>
+Return transport configuration associated with a single peer, as
+passed to &add_transport;.
+The returned list is empty if the peer is unknown.
+Otherwise it contains the <c>ref</c>, <c>type</c> and <c>options</c>
+tuples as in <c>transport</c> and <c>connections</c> info above.
+For example:</p>
+
+<pre>
+[{ref,#Ref&lt;0.0.0.61>},
+ {type,accept},
+ {options,[{transport_module,diameter_tcp},
+ {transport_config,[{reuseaddr,true},
+ {ip,{127,0,0,1}},
+ {port,3868}]}]}]
+</pre>
+</item>
+
+</taglist>
-<marker id="services"/>
</desc>
</func>
@@ -1160,34 +1600,32 @@ Return information about a started service.</p>
<name>services() -> [SvcName]</name>
<fsummary>Return the list of started services.</fsummary>
<type>
-<v>SvcName = service_name()</v>
+<v>SvcName = &service_name;</v>
</type>
<desc>
<p>
Return the list of started services.</p>
-<marker id="session_id"/>
</desc>
</func>
<!-- ===================================================================== -->
<func>
-<name>session_id(Ident) -> OctetString()</name>
+<name>session_id(Ident) -> &dict_OctetString;</name>
<fsummary>Return a value for a Session-Id AVP.</fsummary>
<type>
-<v>Ident = DiameterIdentity()</v>
+<v>Ident = &dict_DiameterIdentity;</v>
</type>
<desc>
<p>
Return a value for a Session-Id AVP.</p>
<p>
-The value has the form required by section 8.8 of RFC 3588.
+The value has the form required by section 8.8 of &the_rfc;.
Ident should be the Origin-Host of the peer from which
the message containing the returned value will be sent.</p>
-<marker id="start"/>
</desc>
</func>
@@ -1201,10 +1639,9 @@ Start the diameter application.</p>
<p>
The diameter application must be started before starting a service.
-In a production system this will typically be accomplished by a boot
+In a production system this is typically accomplished by a boot
file, not by calling <c>start/0</c> explicitly.</p>
-<marker id="start_service"/>
</desc>
</func>
@@ -1213,8 +1650,8 @@ file, not by calling <c>start/0</c> explicitly.</p>
<name>start_service(SvcName, Options) -> ok | {error, Reason}</name>
<fsummary>Start a Diameter service.</fsummary>
<type>
-<v>SvcName = service_name()</v>
-<v>Options = [service_opt()]</v>
+<v>SvcName = &service_name;</v>
+<v>Options = [&service_opt;]</v>
<v>Reason = term()</v>
</type>
<desc>
@@ -1222,13 +1659,19 @@ file, not by calling <c>start/0</c> explicitly.</p>
Start a diameter service.</p>
<p>
-A service defines a locally-implemented Diameter peer, specifying the
-capabilities of the peer to be used during capabilities exchange and
-the Diameter applications that it supports.
-Transports are added to a service using <seealso
-marker="#add_transport">add_transport/2</seealso>.</p>
+A service defines a locally-implemented Diameter node, specifying the
+capabilities to be advertised during capabilities exchange.
+Transports are added to a service using &add_transport;.
+</p>
+
+<note>
+<p>
+A transport can both override its service's
+capabilities and restrict its supported Diameter applications so
+"service = Diameter node as identified by Origin-Host" is not
+necessarily the case.</p>
+</note>
-<marker id="stop_service"/>
</desc>
</func>
@@ -1243,7 +1686,6 @@ Stop the diameter application.</p>
<p>
</p>
-<marker id="stop_service"/>
</desc>
</func>
@@ -1252,14 +1694,25 @@ Stop the diameter application.</p>
<name>stop_service(SvcName) -> ok | {error, Reason}</name>
<fsummary>Stop a Diameter service.</fsummary>
<type>
-<v>SvcName = service_name()</v>
+<v>SvcName = &service_name;</v>
<v>Reason = term()</v>
</type>
<desc>
<p>
Stop a diameter service.</p>
-<marker id="subscribe"/>
+<p>
+Stopping a service causes all associated transport connections to be
+broken.
+A DPR message with be sent as in the case of &remove_transport;.</p>
+
+<note>
+<p>
+Stopping a service does not remove any associated transports:
+&remove_transport; must
+be called to remove transport configuration.</p>
+</note>
+
</desc>
</func>
@@ -1269,17 +1722,19 @@ Stop a diameter service.</p>
<name>subscribe(SvcName) -> true</name>
<fsummary>Subscribe to event messages.</fsummary>
<type>
-<v>SvcName = service_name()</v>
+<v>SvcName = &service_name;</v>
</type>
<desc>
<p>
-Subscribe to <c>service_event()</c> messages from a service.</p>
+Subscribe to <c>&service_event;</c> messages from
+a service.</p>
<p>
It is not an error to subscribe to events from a service
-that does not yet exist.</p>
+that does not yet exist.
+Doing so before adding transports is required to guarantee the
+reception of all related events.</p>
-<marker id="unsubscribe"/>
</desc>
</func>
@@ -1289,7 +1744,7 @@ that does not yet exist.</p>
<name>unsubscribe(SvcName) -> true</name>
<fsummary>Unsubscribe to event messages.</fsummary>
<type>
-<v>SvcName = service_name()</v>
+<v>SvcName = &service_name;</v>
</type>
<desc>
<p>
@@ -1306,9 +1761,7 @@ Unsubscribe to event messages from a service.</p>
<title>SEE ALSO</title>
<p>
-<seealso marker="diameter_app">diameter_app(3)</seealso>,
-<seealso marker="diameter_transport">diameter_transport(3)</seealso>,
-<seealso marker="diameter_dict">diameter_dict(4)</seealso></p>
+&man_app;, &man_transport;, &man_dict;</p>
</section>
diff --git a/lib/diameter/doc/src/diameter_app.xml b/lib/diameter/doc/src/diameter_app.xml
index a9ae0ebbec..f4db625c71 100644
--- a/lib/diameter/doc/src/diameter_app.xml
+++ b/lib/diameter/doc/src/diameter_app.xml
@@ -1,11 +1,19 @@
<?xml version="1.0" encoding="latin1" ?>
-<!DOCTYPE erlref SYSTEM "erlref.dtd">
+<!DOCTYPE erlref SYSTEM "erlref.dtd" [
+ <!ENTITY message '<seealso marker="#message">message()</seealso>'>
+ <!ENTITY dict
+ '<seealso marker="diameter_dict#MESSAGE_RECORDS">diameter_dict(4)</seealso>'>
+ <!ENTITY % also SYSTEM "seealso.ent" >
+ <!ENTITY % here SYSTEM "seehere.ent" >
+ %also;
+ %here;
+]>
<erlref>
<header>
<copyright>
-<year>2011</year>
+<year>2011</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -41,15 +49,13 @@ Callback module of a Diameter application.</modulesummary>
<description>
<p>
-A diameter service as started by <seealso
-marker="diameter#start_service">diameter:start_service/2</seealso>
+A diameter service as started by &mod_start_service;
configures one of more Diameter applications, each of whose
configuration specifies a callback that handles messages specific to
-its application.
-The messages and AVPs of the Diameter application are defined in a
+the application.
+The messages and AVPs of the application are defined in a
dictionary file whose format is documented in
-<seealso marker="diameter_dict">diameter_dict(4)</seealso>
-while the callback module is documented here.
+&man_dict; while the callback module is documented here.
The callback module implements the Diameter application-specific
functionality of a service.</p>
@@ -60,26 +66,24 @@ The functions themselves are of three distinct flavours:</p>
<list>
<item>
<p>
-<seealso marker="#peer_up">peer_up/3</seealso> and
-<seealso marker="#peer_down">peer_down/3</seealso> signal the
+&peer_up; and &peer_down; signal the
attainment or loss of connectivity with a Diameter peer.</p>
</item>
<item>
<p>
-<seealso marker="#pick_peer">pick_peer/4</seealso>,
-<seealso marker="#prepare_request">prepare_request/3</seealso>,
-<seealso marker="#prepare_retransmit">prepare_retransmit/3</seealso>,
-<seealso marker="#handle_answer">handle_answer/4</seealso>
-and <seealso marker="#handle_error">handle_error/4</seealso> are (or may
-be) called as a consequence of a call to <seealso
-marker="diameter#call">diameter:call/4</seealso> to send an outgoing
+&pick_peer;,
+&prepare_request;,
+&prepare_retransmit;,
+&handle_answer;
+and &handle_error; are (or may be) called as a consequence of a call
+to &mod_call; to send an outgoing
Diameter request message.</p>
</item>
<item>
<p>
-<seealso marker="#handle_request">handle_request/3</seealso>
+&handle_request;
is called in response to an incoming Diameter request message.</p>
</item>
@@ -92,10 +96,9 @@ is called in response to an incoming Diameter request message.</p>
The arities given for the the callback functions here assume no extra
arguments.
All functions will also be passed any extra arguments configured with
-the callback module itself when calling <seealso
-marker="diameter#start_service">diameter:start_service/2</seealso>
+the callback module itself when calling &mod_start_service;
and, for the call-specific callbacks, any extra arguments passed to
-<seealso marker="diameter#call">diameter:call/4</seealso>.</p>
+&mod_call;.</p>
</note>
<!-- ===================================================================== -->
@@ -106,11 +109,14 @@ and, for the call-specific callbacks, any extra arguments passed to
<taglist>
+<marker id="capabilities"/>
+
<tag><c>capabilities() = #diameter_caps{}</c></tag>
<item>
<p>
A record containing the identities of
-the local and remote Diameter peers having an established transport
+the local Diameter node and the remote Diameter peer having an
+established transport
connection, as well as the capabilities as
determined by capabilities exchange.
Each field of the record is a 2-tuple consisting of
@@ -119,78 +125,51 @@ Optional or possibly multiple values are encoded as lists of values,
mandatory values as the bare value.</p>
</item>
-<tag><c>message() = record() | list()</c></tag>
+<marker id="message"/>
+
+<tag><c>message() = &codec_message;</c></tag>
<item>
<p>
The representation of a Diameter message as passed to
-<seealso marker="diameter#call">diameter:call/4</seealso>.
-The record representation is as outlined in
-<seealso
-marker="diameter_dict#MESSAGE_RECORDS">diameter_dict(4)</seealso>:
-a message as defined in a dictionary file is encoded as a record with
-one field for each component AVP.
-Equivalently, a message can also be encoded as a list whose head is
-the atom-valued message name (the record name minus any
-prefix specified in the relevant dictionary file) and whose tail is a
-list of <c>{FieldName, FieldValue}</c> pairs.</p>
-
-<p>
-A third representation allows a message to be specified as a list
-whose head is a <c>diameter_header</c> record and whose tail is a list
-of <c>diameter_avp</c> records.
-This representation is used by diameter itself when relaying requests
-as directed by the return value of a
-<seealso marker="#handle_request">handle_request/3</seealso>
-callback.
-It differs from the other other two in that it bypasses the checks for
-messages that do not agree with their definitions in the dictionary in
-question (since relays agents must handle arbitrary request): messages
-are sent exactly as specified.</p>
+&mod_call; or returned from a &handle_request; callback.</p>
</item>
-<tag><c>packet() = #diameter_packet{}</c></tag>
+<marker id="packet"/>
+
+<tag><c>packet() = &codec_packet;</c></tag>
<item>
<p>
-A container for incoming and outgoing Diameters message that's passed
+A container for incoming and outgoing Diameter messages that's passed
through encode/decode and transport.
-Fields of a packet() record should not be set in return values except
-as documented.</p>
+Fields should not be set in return values except as documented.</p>
</item>
+<marker id="peer_ref"/>
+
<tag><c>peer_ref() = term()</c></tag>
<item>
<p>
-A term identifying a transport connection with a Diameter peer.
-Should be treated opaquely.</p>
+A term identifying a transport connection with a Diameter peer.</p>
</item>
-<tag><c>peer() = {peer_ref(), capabilities()}</c></tag>
+<marker id="peer"/>
+
+<tag><c>peer() = {&peer_ref;, &capabilities;}</c></tag>
<item>
<p>
A tuple representing a Diameter peer connection.</p>
</item>
-<tag><c>service_name() = term()</c></tag>
-<item>
-<p>
-The service supporting the Diameter application.
-Specified to <seealso
-marker="diameter#start_service">diameter:start_service/2</seealso>
-when starting the service.</p>
-</item>
+<marker id="state"/>
<tag><c>state() = term()</c></tag>
<item>
<p>
The state maintained by the application callback functions
-<seealso marker="#peer_up">peer_up/3</seealso>,
-<seealso marker="#peer_down">peer_down/3</seealso> and (optionally)
-<seealso marker="#pick_peer">pick_peer/4</seealso>.
+&peer_up;, &peer_down; and (optionally) &pick_peer;.
The initial state is configured in the call to
-<seealso
-marker="diameter#start_service">diameter:start_service/2</seealso>
-that configures the application on a service.
+&mod_start_service; that configures the application on a service.
Callback functions returning a state are evaluated in a common
service-specific process while
those not returning state are evaluated in a request-specific
@@ -211,18 +190,33 @@ process.</p>
<name>Mod:peer_up(SvcName, Peer, State) -> NewState</name>
<fsummary>Invoked when a transport connection has been established</fsummary>
<type>
-<v>SvcName = service_name()</v>
-<v>Peer = peer()</v>
-<v>State = NewState = state()</v>
+<v>SvcName = &mod_service_name;</v>
+<v>Peer = &peer;</v>
+<v>State = NewState = &state;</v>
</type>
<desc>
<p>
-Invoked when a transport connection has been established
-and a successful capabilities exchange has indicated that the peer
-supports the Diameter application of the application on which
-the callback module in question has been configured.</p>
+Invoked to signal the availability of a peer connection.
+In particular, capabilities exchange with the peer has indicated
+support for the application in question, the RFC 3539 watchdog state
+machine for the connection has reached state <c>OKAY</c> and Diameter
+messages can be both sent and received.</p>
+
+<note>
+<p>
+A watchdog state machine can reach state <c>OKAY</c> from state
+<c>SUSPECT</c> without a new capabilities exchange taking place.
+A new transport connection (and capabilities exchange) results in a
+new peer_ref().</p>
+</note>
+
+<note>
+<p>
+There is no requirement that a callback return before incoming
+requests are received: &handle_request; callbacks must be
+handled independently of &peer_up; and &peer_down;.</p>
+</note>
-<marker id="peer_down"/>
</desc>
</func>
@@ -230,78 +224,87 @@ the callback module in question has been configured.</p>
<name>Mod:peer_down(SvcName, Peer, State) -> NewState</name>
<fsummary>Invoked when a transport connection has been lost.</fsummary>
<type>
-<v>SvcName = service_name()</v>
-<v>Peer = peer()</v>
-<v>State = NewState = state()</v>
+<v>SvcName = &mod_service_name;</v>
+<v>Peer = &peer;</v>
+<v>State = NewState = &state;</v>
</type>
<desc>
<p>
-Invoked when a transport connection has been lost following a previous
-call to <seealso marker="#peer_up">peer_up/3</seealso>.</p>
+Invoked to signal that a peer connection is no longer available
+following a previous call to &peer_up;.
+In particular, that the RFC 3539 watchdog state machine for the
+connection has left state <c>OKAY</c> and the peer will no longer be a
+candidate in &pick_peer; callbacks.</p>
-<marker id="pick_peer"/>
</desc>
</func>
<func>
-<name>Mod:pick_peer(Candidates, Reserved, SvcName, State)
- -> {ok, Peer} | {Peer, NewState} | false</name>
+<name>Mod:pick_peer(Candidates, _Reserved, SvcName, State)
+ -> Selection | false</name>
<fsummary>Select a target peer for an outgoing request.</fsummary>
<type>
-<v>Candidates = [peer()]</v>
-<v>Peer = peer() | false</v>
-<v>SvcName = service_name()</v>
-<v>State = NewState = state()</v>
+<v>Candidates = [&peer;]</v>
+<v>SvcName = &mod_service_name;</v>
+<v>State = NewState = &state;</v>
+<v>Selection = {ok, Peer} | {Peer, NewState}</v>
+<v>Peer = &peer; | false</v>
</type>
<desc>
<p>
-Invoked as a consequence of a call to <seealso
-marker="diameter#call">diameter:call/4</seealso> to select a destination
-peer for an outgoing request, the return value indicating the selected peer.
-A new application state can also be returned but only if the Diameter
-application in question was
-configured with the option <c>call_mutates_state</c> set to
-<c>true</c>, as documented for <seealso
-marker="diameter#start_service">diameter:start_service/2</seealso>.</p>
-
-<p>
-The candidate peers list will only include those
-which are selected by any <c>filter</c> option specified in the call to
-<seealso marker="diameter#call">diameter:call/4</seealso>, and only
-those which have indicated support for the Diameter application in
-question.
+Invoked as a consequence of a call to &mod_call; to select a destination
+peer for an outgoing request.
+The return value indicates the selected peer.</p>
+
+<p>
+The candidate list contains only those peers that have advertised
+support for the Diameter application in question during capabilities
+exchange, that have not be excluded by a <c>filter</c> option in
+the call to &mod_call;
+and whose watchdog state machine is in the <c>OKAY</c> state.
The order of the elements is unspecified except that any
peers whose Origin-Host and Origin-Realm matches that of the
outgoing request (in the sense of a <c>{filter, {all, [host, realm]}}</c>
-option to <seealso marker="diameter#call">diameter:call/4</seealso>)
+option to &mod_call;)
will be placed at the head of the list.</p>
<p>
-The return values <c>false</c> and <c>{false, State}</c> are
-equivalent when callback state is mutable, as are
-<c>{ok, Peer}</c> and <c>{Peer, State}</c>.
-Returning a peer as <c>false</c> causes <c>{error, no_connection}</c>
-to be returned from <seealso marker="diameter#call">diameter:call/4</seealso>.
-Returning a peer() from an initial pick_peer/4 callback will result in a
-<seealso marker="#prepare_request">prepare_request/3</seealso> callback
-followed by either <seealso
-marker="#handle_answer">handle_answer/4</seealso>
-or <seealso marker="#handle_error">handle_error/4</seealso> depending
+A callback that returns a peer() will be followed by a
+&prepare_request;
+callback and, if the latter indicates that the request should be sent,
+by either &handle_answer;
+or &handle_error; depending
on whether or not an answer message is received from the peer.
-If transport with the peer is lost before this then a new <seealso
-marker="#pick_peer">pick_peer/4</seealso> callback takes place to
-select an alternate peer.</p>
-
-<p>
-Note that there is no guarantee that a <seealso
-marker="#pick_peer">pick_peer/4</seealso> callback to select
-an alternate peer will be followed by any additional callbacks, only
-that the initial <seealso
-marker="#pick_peer">pick_peer/4</seealso> will be, since a
+If the transport becomes unavailable after &prepare_request; then a
+new &pick_peer; callback may take place to
+failover to an alternate peer, after which &prepare_retransmit; takes the
+place of &prepare_request; in resending the
+request.
+There is no guarantee that a &pick_peer; callback to select
+an alternate peer will be followed by any additional callbacks since a
retransmission to an alternate peer is abandoned if an answer is
received from a previously selected peer.</p>
-<marker id="prepare_request"/>
+<p>
+Returning <c>false</c> or <c>{false, NewState}</c> causes <c>{error,
+no_connection}</c> to be returned from &mod_call;.</p>
+
+<p>
+The return values <c>false</c> and <c>{false, State}</c> (that is,
+<c>NewState = State</c>) are equivalent, as are <c>{ok, Peer}</c> and
+<c>{Peer, State}</c>.</p>
+
+<note>
+<p>
+The return value <c>{Peer, NewState}</c> is only allowed if
+the Diameter application in question was configured with the
+&mod_application_opt; <c>{call_mutates_state, true}</c>.
+Otherwise, the <c>State</c> argument is always
+the intial value as configured on the application, not any subsequent
+value returned by a &peer_up;
+or &peer_down; callback.</p>
+</note>
+
</desc>
</func>
@@ -310,74 +313,81 @@ received from a previously selected peer.</p>
<name>Mod:prepare_request(Packet, SvcName, Peer) -> Action</name>
<fsummary>Return a request for encoding and transport.</fsummary>
<type>
-<v>Packet = packet()</v>
-<v>SvcName = service_name()</v>
-<v>Peer = peer()</v>
-<v>Action = {send, packet() | message()} | {discard, Reason} | discard</v>
+<v>Packet = &packet;</v>
+<v>SvcName = &mod_service_name;</v>
+<v>Peer = &peer;</v>
+<v>Action = Send | Discard | {eval_packet, Action, PostF}</v>
+<v>Send = {send, &packet; | &message;}</v>
+<v>Discard = {discard, Reason} | discard</v>
+<v>PostF = &mod_evaluable;}</v>
</type>
<desc>
<p>
Invoked to return a request for encoding and transport.
-Allows the sender to access the selected peer's capabilities
-in order to set (for example) <c>Destination-Host</c> and/or
-<c>Destination-Realm</c> in the outgoing request, although the
-callback need not be limited to this usage.
+Allows the sender to use the selected peer's capabilities
+to modify the outgoing request.
Many implementations may simply want to return <c>{send, Packet}</c></p>
<p>
-A returned packet() should set the request to be encoded in its
+A returned &packet; should set the
+request to be encoded in its
<c>msg</c> field and can set the <c>transport_data</c> field in order
-to pass information to the transport module.
-Extra arguments passed to <seealso
-marker="diameter#call">diameter:call/4</seealso> can be used to
-communicate transport data to the callback.
-A returned packet() can also set the <c>header</c> field to a
-<c>diameter_header</c> record in order to specify values that should
-be preserved in the outgoing request, although this should typically
-not be necessary and allows the callback to set header values
-inappropriately.
+to pass information to the transport process.
+Extra arguments passed to &mod_call; can be used to
+communicate transport (or any other) data to the callback.</p>
+
+<p>
+A returned &packet; can set
+the <c>header</c> field to a
+<c>#diameter_header{}</c> to specify values that should
+be preserved in the outgoing request, values otherwise being those in
+the header record contained in <c>Packet</c>.
A returned <c>length</c>, <c>cmd_code</c> or <c>application_id</c> is
ignored.</p>
<p>
+A returned <c>PostF</c> will be evaluated on any encoded
+<c>#diameter_packet{}</c> prior to transmission, the <c>bin</c> field
+containing the encoded binary.
+The return value is ignored.</p>
+
+<p>
Returning <c>{discard, Reason}</c> causes the request to be aborted
-and the <seealso
-marker="diameter#call">diameter:call/4</seealso> for which the
+and the &mod_call; for which the
callback has taken place to return <c>{error, Reason}</c>.
Returning <c>discard</c> is equivalent to returning <c>{discard,
discarded}</c>.</p>
-<marker id="prepare_retransmit"/>
</desc>
</func>
<func>
-<name>Mod:prepare_retransmit(Packet, SvcName, Peer) -> Result</name>
+<name>Mod:prepare_retransmit(Packet, SvcName, Peer) -> Action</name>
<fsummary>Return a request for encoding and retransmission.</fsummary>
<type>
-<v>Packet = packet()</v>
-<v>SvcName = service_name()</v>
-<v>Peer = peer()</v>
-<v>Result = {send, packet() | message()} | {discard, Reason} | discard</v>
+<v>Packet = &packet;</v>
+<v>SvcName = &mod_service_name;</v>
+<v>Peer = &peer;</v>
+<v>Action = Send | Discard | {eval_packet, Action, PostF}</v>
+<v>Send = {send, &packet; | &message;}</v>
+<v>Discard = {discard, Reason} | discard</v>
+<v>PostF = &mod_evaluable;}</v>
</type>
<desc>
<p>
Invoked to return a request for encoding and retransmission.
-Has the same role as <seealso
-marker="#prepare_request">prepare_request/3</seealso> in the case that
+Has the same role as &prepare_request; in the case that
a peer connection is lost an an alternate peer selected but the
-argument packet() is as returned by the initial
-<c>prepare_request/3</c>.</p>
+argument &packet; is as returned
+by the initial &prepare_request;.</p>
<p>
Returning <c>{discard, Reason}</c> causes the request to be aborted
-and a <seealso
-marker="#handle_error">handle_error/4</seealso> callback to
+and a &handle_error; callback to
take place with <c>Reason</c> as initial argument.
Returning <c>discard</c> is equivalent to returning <c>{discard,
discarded}</c>.</p>
-<marker id="handle_answer"/>
</desc>
</func>
@@ -385,51 +395,43 @@ discarded}</c>.</p>
<name>Mod:handle_answer(Packet, Request, SvcName, Peer) -> Result</name>
<fsummary>Receive an answer message from a peer.</fsummary>
<type>
-<v>Packet = packet()</v>
-<v>Request = message()</v>
-<v>SvcName = service_name()</v>
-<v>Peer = peer()</v>
+<v>Packet = &packet;</v>
+<v>Request = &message;</v>
+<v>SvcName = &mod_service_name;</v>
+<v>Peer = &peer;</v>
<v>Result = term()</v>
</type>
<desc>
<p>
Invoked when an answer message is received from a peer.
-The return value is returned from the call to <seealso
-marker="diameter#call">diameter:call/4</seealso> for which the
-callback takes place unless the <c>detach</c> option was
-specified.</p>
+The return value is returned from &mod_call; unless the
+<c>detach</c> option was specified.</p>
<p>
-The decoded answer record is in the <c>msg</c> field of the argument
-packet(),
-the undecoded binary in the <c>packet</c> field.
+The decoded answer record and undecoded binary are in the <c>msg</c>
+and <c>bin</c> fields of the argument
+&packet; respectively.
<c>Request</c> is the outgoing request message as was returned from
-<seealso marker="#prepare_request">prepare_request/3</seealso> or
-<seealso marker="#prepare_retransmit">prepare_retransmit/3</seealso>
-before the request was passed to the transport.</p>
+&prepare_request; or &prepare_retransmit;.</p>
<p>
-For any given call to <seealso
-marker="diameter#call">diameter:call/4</seealso> there is at most one
-call to the handle_answer callback of the application in question: any
+For any given call to &mod_call; there is at most one
+&handle_answer; callback: any
duplicate answer (due to retransmission or otherwise) is discarded.
-Similarly, only one of <c>handle_answer/4</c> or <c>handle_error/4</c> is
-called for any given request.</p>
+Similarly, only one of &handle_answer; or
+&handle_error; is called.</p>
<p>
By default, an incoming answer message that cannot be successfully
-decoded causes the request process in question to fail, causing the
-relevant call to <seealso
-marker="diameter#call">diameter:call/4</seealso>
-to return <c>{error, failure} (unless the <c>detach</c> option was
-specified)</c>.
-In particular, there is no <c>handle_error/4</c> callback in this
+decoded causes the request process to fail, causing
+&mod_call;
+to return <c>{error, failure}</c> unless the <c>detach</c> option was
+specified.
+In particular, there is no &handle_error; callback in this
case.
-Application configuration may change this behaviour as described for
-<seealso
-marker="diameter#start_service">diameter:start_service/2</seealso>.</p>
+The &mod_application_opt;
+<c>answer_errors</c> can be set to change this behaviour.</p>
-<marker id="handle_error"/>
</desc>
</func>
@@ -438,30 +440,26 @@ marker="diameter#start_service">diameter:start_service/2</seealso>.</p>
<fsummary>Return an error from a outgoing request.</fsummary>
<type>
<v>Reason = timeout | failover | term()</v>
-<v>Request = message()</v>
-<v>SvcName = service_name()</v>
-<v>Peer = peer()</v>
+<v>Request = &message;</v>
+<v>SvcName = &mod_service_name;</v>
+<v>Peer = &peer;</v>
<v>Result = term()</v>
</type>
<desc>
<p>
-Invoked when an error occurs before an answer message is received from
-a peer in response to an outgoing request.
-The return value is returned from the call to <seealso
-marker="diameter#call">diameter:call/4</seealso> for which the
-callback takes place (unless the <c>detach</c> option was
-specified).</p>
+Invoked when an error occurs before an answer message is received
+in response to an outgoing request.
+The return value is returned from &mod_call; unless the
+<c>detach</c> option was specified.</p>
<p>
Reason <c>timeout</c> indicates that an answer message has not been
-received within the required time.
+received within the time specified with the corresponding &mod_call_opt;.
Reason <c>failover</c> indicates
that the transport connection to the peer to which the request has
-been sent has been lost but that not alternate node was available,
-possibly because a <seealso marker="#pick_peer">pick_peer/4</seealso>
-callback returned false.</p>
+been sent has become unavailable and that not alternate peer was
+not selected.</p>
-<marker id="handle_request"/>
</desc>
</func>
@@ -469,54 +467,54 @@ callback returned false.</p>
<name>Mod:handle_request(Packet, SvcName, Peer) -> Action</name>
<fsummary>Receive an incoming request.</fsummary>
<type>
-<v>Packet = packet()</v>
+<v>Packet = &packet;</v>
<v>SvcName = term()</v>
-<v>Peer = peer()</v>
-<v>Action = Reply | {relay, Opts} | discard | {eval, Action, PostF}</v>
-<v>Reply = {reply, message()}
+<v>Peer = &peer;</v>
+<v>Action = Reply
+ | {relay, [Opt]}
+ | discard
+ | {eval|eval_packet, Action, PostF}</v>
+<v>Reply = {reply, &packet; | &message;}
| {protocol_error, 3000..3999}</v>
-<v>Opts = diameter:call_opts()</v>
-<v>PostF = diameter:evaluable()</v>
+<v>Opt = &mod_call_opt;</v>
+<v>PostF = &mod_evaluable;</v>
</type>
<desc>
<p>
Invoked when a request message is received from a peer.
The application in which the callback takes place (that is, the
-callback module as configured with <seealso
-marker="diameter#start_service">diameter:start_service/2</seealso>)
+callback module as configured with &mod_start_service;)
is determined by the Application Identifier in the header of the
incoming request message, the selected module being the one
-whose corresponding <seealso
-marker="diameter_dict#MESSAGE_RECORDS">dictionary</seealso> declares
+whose corresponding dictionary declares
itself as defining either the application in question or the Relay
application.</p>
<p>
-The argument packet() has the following signature.</p>
+The argument &packet; has the following signature.</p>
-<code>
+<pre>
#diameter_packet{header = #diameter_header{},
avps = [#diameter_avp{}],
msg = record() | undefined,
- errors = ['Unsigned32'() | {'Unsigned32'(), #diameter_avp{}}],
+ errors = [&dict_Unsigned32; | {&dict_Unsigned32;, #diameter_avp{}}],
bin = binary(),
transport_data = term()}
-</code>
+</pre>
<p>
-The <c>msg</c> field will be <c>undefined</c> only in case the request has
+The <c>msg</c> field will be <c>undefined</c> in case the request has
been received in the relay application.
Otherwise it contains the record representing the request as outlined
-in <seealso
-marker="diameter_dict#MESSAGE_RECORDS">diameter_dict(4)</seealso>.</p>
+in &dict;.</p>
<p>
The <c>errors</c> field specifies any Result-Code's identifying errors
that were encountered in decoding the request.
In this case diameter will set both Result-Code and
Failed-AVP AVP's in a returned
-answer message() before sending it to the peer:
-the returned message() need only set any other required AVP's.
+answer &message; before sending it to the peer:
+the returned &message; need only set any other required AVP's.
Note that the errors detected by diameter are all of the 5xxx series
(Permanent Failures).
The <c>errors</c> list is empty if the request has been received in
@@ -526,19 +524,24 @@ the relay application.</p>
The <c>transport_data</c> field contains an arbitrary term passed into
diameter from the transport module in question, or the atom
<c>undefined</c> if the transport specified no data.
-The term is preserved in the packet() containing any answer message
-sent back to the transport process unless another value is explicitly
-specified.</p>
+The term is preserved if a &message; is returned but must be set
+explicitly in a returned &packet;.</p>
<p>
The semantics of each of the possible return values are as follows.</p>
<taglist>
-<tag><c>{reply, message()}</c></tag>
+<tag><c>{reply, &packet; | &message;}</c></tag>
<item>
<p>
-Send the specified answer message to the peer.</p>
+Send the specified answer message to the peer.
+In the case of a &packet;, the
+message to be sent must be set in the
+<c>msg</c> field and the <c>header</c> field can be set to a
+<c>#diameter_header{}</c> to specify values that should be
+preserved in the outgoing answer, appropriate values otherwise
+being set by diameter.</p>
</item>
<tag><c>{protocol_error, 3000..3999}</c></tag>
@@ -547,15 +550,15 @@ Send the specified answer message to the peer.</p>
Send an answer message to the peer containing the specified
protocol error.
Equivalent to</p>
-<code>
+<pre>
{reply, ['answer-message' | Avps]
-</code>
+</pre>
<p>
where <c>Avps</c> sets the Origin-Host, Origin-Realm, the specified
Result-Code and (if the request sent one) Session-Id AVP's.</p>
<p>
-Note that RFC 3588 mandates that only answers with a 3xxx series
+Note that &the_rfc; mandates that only answers with a 3xxx series
Result-Code (protocol errors) may set the E bit.
Returning a non-3xxx value in a <c>protocol_error</c> tuple
will cause the request process in question to fail.</p>
@@ -568,37 +571,47 @@ Relay a request to another peer in the role of a Diameter relay agent.
If a routing loop is detected then the request is answered with
3005 (DIAMETER_LOOP_DETECTED).
Otherwise a Route-Record AVP (containing the sending peer's Origin-Host) is
-added to the request and <seealso marker="#pick_peer">pick_peer/4</seealso>
-and subsequent callbacks take place just as if <seealso
-marker="diameter#call">diameter:call/4</seealso> had been called
+added to the request and &pick_peer;
+and subsequent callbacks take place just as if &mod_call; had been called
explicitly.
The End-to-End Identifier of the incoming request is preserved in the
header of the relayed request.</p>
<p>
The returned <c>Opts</c> should not specify <c>detach</c>.
-A subsequent <seealso marker="#handle_answer">handle_answer/4</seealso>
+A subsequent &handle_answer;
callback for the relayed request must return its first
-argument, the <c>diameter_packet</c> record containing the answer
+argument, the <c>#diameter_packet{}</c> record containing the answer
message.
Note that the <c>extra</c> option can be specified to supply arguments
that can distinguish the relay case from others if so desired.
Any other return value (for example, from a
-<seealso marker="#handle_error">handle_error/4</seealso> callback)
+&handle_error; callback)
causes the request to be answered with 3002 (DIAMETER_UNABLE_TO_DELIVER).</p>
</item>
<tag><c>discard</c></tag>
<item>
<p>
-Discard the request.</p>
+Discard the request.
+No answer message is sent to the peer.</p>
</item>
<tag><c>{eval, Action, PostF}</c></tag>
<item>
<p>
Handle the request as if <c>Action</c> has been returned and then
-evaluate <c>PostF</c> in the request process.</p>
+evaluate <c>PostF</c> in the request process.
+The return value is ignored.</p>
+</item>
+
+<tag><c>{eval_packet, Action, PostF}</c></tag>
+<item>
+<p>
+Like <c>eval</c> but evaluate <c>PostF</c> on any encoded
+<c>#diameter_packet{}</c> prior to transmission, the <c>bin</c> field
+containing the encoded binary.
+The return value is ignored.</p>
</item>
</taglist>
diff --git a/lib/diameter/doc/src/diameter_codec.xml b/lib/diameter/doc/src/diameter_codec.xml
new file mode 100644
index 0000000000..4a77d5435b
--- /dev/null
+++ b/lib/diameter/doc/src/diameter_codec.xml
@@ -0,0 +1,389 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE erlref SYSTEM "erlref.dtd" [
+ <!ENTITY records
+ '<seealso marker="diameter_dict#MESSAGE_RECORDS">diameter_dict(4)</seealso>'>
+ <!ENTITY types
+ '<seealso marker="diameter_dict#DATA_TYPES">diameter_dict(4)</seealso>'>
+ <!ENTITY % also SYSTEM "seealso.ent" >
+ <!ENTITY % here SYSTEM "seehere.ent" >
+ %also;
+ %here;
+]>
+
+<erlref>
+<header>
+<copyright>
+<year>2012</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>diameter_codec(3)</title>
+<prepared>Anders Svensson</prepared>
+<responsible></responsible>
+<docno></docno>
+<approved></approved>
+<checked></checked>
+<date></date>
+<rev></rev>
+<file>diameter_codec.xml</file>
+</header>
+
+<module>diameter_codec</module>
+<modulesummary>Decode and encode of Diameter messages.</modulesummary>
+
+<description>
+
+<p>
+Incoming Diameter messages are decoded from binary() before being
+communicated to &man_app; callbacks.
+Similarly, outgoing Diameter messages are encoded into binary() before
+being passed to the appropriate &man_transport; module for
+transmission.
+The functions in this module implement this encode/decode.</p>
+
+<note>
+<p>
+Calls to this module are made by diameter itself as a consequence of
+configuration passed to &mod_start_service;.
+The encode/decode functions may also be useful for other purposes (eg.
+test) but the diameter user does not need to call them explicitly when
+sending and receiving messages using &mod_call; and the callback
+interface documented in &man_app;.</p>
+</note>
+
+<p>
+The &header; and &packet; records below
+are defined in diameter.hrl, which can be included as follows.</p>
+
+<pre>
+-include_lib("diameter/include/diameter.hrl").
+</pre>
+
+<p>
+Application-specific records are definied in the hrl
+files resulting from dictionary file compilation.</p>
+
+</description>
+
+<!-- ===================================================================== -->
+
+<section>
+<title>DATA TYPES</title>
+
+<p></p>
+
+<taglist>
+
+<marker id="integers"/>
+
+<tag><c>uint8()&nbsp; = 0..255</c></tag>
+<tag><c>uint24() = 0..16777215</c></tag>
+<tag><c>uint32() = 0..4294967295</c></tag>
+<item>
+<p>
+8-bit, 24-bit and 32-bit integers occurring in Diameter and AVP
+headers.</p>
+</item>
+
+<marker id="avp"/>
+
+<tag><c>avp() = #diameter_avp{}</c></tag>
+<item>
+<p>
+The application-neutral representation of an AVP.
+Primarily intended for use by relay applications that need to handle
+arbitrary Diameter applications.
+A service implementing a specific Diameter application
+(for which it configures a dictionary) can manipulate values of type
+&message; instead.</p>
+
+<p>
+Fields have the following types.</p>
+
+<taglist>
+
+<tag><c>code = uint32()</c></tag>
+<tag><c>is_mandatory = boolean()</c></tag>
+<tag><c>need_encryption = boolean()</c></tag>
+<tag><c>vendor_id = uint32() | undefined</c></tag>
+<item>
+<p>
+Values in the AVP header, corresponding to AVP Code, the M flag, P
+flags and Vendor-ID respectivelty.
+A Vendor-ID other than <c>undefined</c> implies a set V flag.</p>
+</item>
+
+<tag><c>data = iolist()</c></tag>
+<item>
+<p>
+The data bytes of the AVP.</p>
+</item>
+
+<tag><c>name = atom()</c></tag>
+<item>
+<p>
+The name of the AVP as defined in the dictionary file in question, or
+<c>undefined</c> if the AVP is unknown to the dictionary file in
+question.</p>
+</item>
+
+<tag><c>value = term()</c></tag>
+<item>
+<p>
+The decoded value of an AVP.
+Will be <c>undefined</c> on decode if the data bytes could
+not be decoded or the AVP is unknown.
+The type of a decoded value is as document in &types;.</p>
+</item>
+
+<tag><c>type = atom()</c></tag>
+<item>
+<p>
+The type of the AVP as specified in the dictionary file in question
+(or one it inherits).
+Possible types are <c>undefined</c> and the Diameter types:
+<c>OctetString</c>, <c>Integer32</c>, <c>Integer64</c>,
+<c>Unsigned32</c>, <c>Unsigned64</c>, <c>Float32</c>, <c>Float64</c>,
+<c>Grouped</c>, <c>Enumerated</c>, <c>Address</c>, <c>Time</c>,
+<c>UTF8String</c>, <c>DiameterIdentity</c>, <c>DiameterURI</c>,
+<c>IPFilterRule</c> and <c>QoSFilterRule</c>.</p>
+</item>
+
+</taglist>
+
+</item>
+
+<marker id="dictionary"/>
+
+<tag><c>dictionary() = module()</c></tag>
+<item>
+
+<p>
+The name of a generated dictionary module as generated by &man_compile;
+or &make_codec;.
+The interface provided by a dictionary module is an
+implementation detail that may change.</p>
+</item>
+
+<marker id="header"/>
+
+<tag><c>header() = #diameter_header{}</c></tag>
+<item>
+<p>
+The record representation of the Diameter header.
+Values in a &packet; returned by &decode; are as extracted from the
+incoming message.
+Values set in an &packet; passed to &encode; are preserved in the
+encoded binary(), with the exception of <c>length</c>, <c>cmd_code</c>
+and <c>application_id</c>, all of which are determined by the
+&dictionary; in question.</p>
+
+<note>
+<p>
+It is not necessary to set header fields explicitly in outgoing
+messages as diameter itself will set appropriate values.
+Setting inappropriate values can be useful for test purposes.</p>
+</note>
+
+<p>
+Fields have the following types.</p>
+
+<taglist>
+
+<tag><c>version = uint8()</c></tag>
+<tag><c>length = uint24()</c></tag>
+<tag><c>cmd_code = uint24()</c></tag>
+<tag><c>application_id = uint32()</c></tag>
+<tag><c>hop_by_hop_id = uint32()</c></tag>
+<tag><c>end_to_end_id = uint32()</c></tag>
+<item>
+<p>
+Values of the Version, Message Length, Command-Code, Application-ID,
+Hop-by-Hop Identifier and End-to-End Identifier fields of the Diameter
+header.</p>
+</item>
+
+<tag><c>is_request = boolean()</c></tag>
+<tag><c>is_proxiable = boolean()</c></tag>
+<tag><c>is_error = boolean()</c></tag>
+<tag><c>is_retransmitted = boolean()</c></tag>
+<item>
+<p>
+Values correspoding to the R(equest), P(roxiable), E(rror)
+and T(Potentially re-transmitted message) flags of the Diameter
+header.</p>
+</item>
+
+</taglist>
+
+</item>
+
+<marker id="message"/>
+
+<tag><c>message() = record() | list()</c></tag>
+<item>
+<p>
+The representation of a Diameter message as passed to
+&mod_call; or returned from a &app_handle_request; callback.
+The record representation is as outlined in &records;:
+a message as defined in a dictionary file is encoded as a record with
+one field for each component AVP.
+Equivalently, a message can also be encoded as a list whose head is
+the atom-valued message name (as specified in the relevant dictionary
+file) and whose tail is a list of <c>{AvpName, AvpValue}</c> pairs.</p>
+
+<p>
+Another list-valued representation allows a message to be specified
+as a list whose head is a &header; and whose tail is an &avp; list.
+This representation is used by diameter itself when relaying requests
+as directed by the return value of a &app_handle_request; callback.
+It differs from the other other two in that it bypasses the checks for
+messages that do not agree with their definitions in the dictionary in
+question: messages are sent exactly as specified.</p>
+
+</item>
+
+<marker id="packet"/>
+
+<tag><c>packet() = #diameter_packet{}</c></tag>
+<item>
+<p>
+A container for incoming and outgoing Diameter messages.
+Fields have the following types.</p>
+
+<taglist>
+
+<tag><c>header = &header; | undefined</c></tag>
+<item>
+<p>
+The Diameter header of the message.
+Can be (and typically should be) <c>undefined</c> for an outgoing
+message in a non-relay application, in which case diameter provides
+appropriate values.</p>
+</item>
+
+<tag><c>avps = [&avp;] | undefined</c></tag>
+<item>
+<p>
+The AVPs of the message.
+Ignored for an outgoing message if the <c>msg</c> field is set to a
+value other than <c>undefined</c>.</p>
+</item>
+
+<tag><c>msg = &message; | undefined</c></tag>
+<item>
+<p>
+The incoming/outgoing message.
+For an incoming message, a record if the message can be
+decoded in a non-relay application, <c>undefined</c> otherwise.
+For an outgoing message, setting a <c>[&header; | &avp;]</c> list is
+equivalent to setting the <c>header</c> and <c>avps</c> fields to the
+corresponding values.</p>
+
+<warning>
+<p>
+A record-valued <c>msg</c> field does <b>not</b> imply an absence of
+decode errors.
+The <c>errors</c> field should also be examined.</p>
+</warning>
+
+</item>
+
+<tag><c>bin = binary()</c></tag>
+<item>
+<p>
+The incoming message prior to encode or the outgoing message after
+encode.</p>
+</item>
+
+<tag><c>errors = [5000..5999 | {5000..5999, avp()}]</c></tag>
+<item>
+<p>
+Errors detected at decode of an incoming message, as identified by
+a corresponding 5xxx series Result-Code (Permanent Failures).
+For an incoming request, these should be used to formulate an
+appropriate answer as documented for the &app_handle_request;
+callback in &man_app;.
+For an incoming answer, the &mod_application_opt;
+<c>answer_errors</c> determines the behaviour.</p>
+</item>
+
+<tag><c>transport_data = term()</c></tag>
+<item>
+<p>
+An arbitrary term of meaning only to the transport process in
+question, as documented in &man_transport;.</p>
+</item>
+
+</taglist>
+
+</item>
+
+</taglist>
+
+</section>
+
+<!-- ===================================================================== -->
+
+<funcs>
+
+<func>
+<name>decode(Mod, Bin) -> Pkt</name>
+<fsummary>Decode a Diameter message.</fsummary>
+<type>
+<v>Mod = &dictionary;</v>
+<v>Bin = binary()</v>
+<v>Pkt = &packet;</v>
+</type>
+<desc>
+
+<p>
+Decode a Diameter message.</p>
+
+</desc>
+</func>
+
+<func>
+<name>encode(Mod, Msg) -> Pkt</name>
+<fsummary>Encode a Diameter message.</fsummary>
+<type>
+<v>Mod = &dictionary;</v>
+<v>Msg = &message; | &packet;</v>
+<v>Pkt = &packet;</v>
+</type>
+<desc>
+
+<p>
+Encode a Diameter message.
+</p>
+
+</desc>
+</func>
+
+</funcs>
+
+<!-- ===================================================================== -->
+<!-- ===================================================================== -->
+
+<section>
+<title>SEE ALSO</title>
+
+<p>
+&man_compile;, &man_app;, &man_dict;, &man_make;</p>
+
+</section>
+
+</erlref>
diff --git a/lib/diameter/doc/src/diameter_compile.xml b/lib/diameter/doc/src/diameter_compile.xml
index 60e08d41da..0bd7ad1789 100644
--- a/lib/diameter/doc/src/diameter_compile.xml
+++ b/lib/diameter/doc/src/diameter_compile.xml
@@ -1,10 +1,17 @@
<?xml version="1.0" encoding="iso-8859-1" ?>
-<!DOCTYPE comref SYSTEM "comref.dtd">
+<!DOCTYPE comref SYSTEM "comref.dtd" [
+ <!ENTITY dictionary
+ '<seealso marker="diameter_dict">dictionary file</seealso>'>
+ <!ENTITY % also SYSTEM "seealso.ent" >
+ <!ENTITY % here SYSTEM "seehere.ent" >
+ %also;
+ %here;
+]>
<comref>
<header>
<copyright>
-<year>2011</year>
+<year>2011</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -31,12 +38,14 @@ supplied.
<description>
<p>
-The diameterc utility is used to transform diameter
-<seealso marker="diameter_dict">dictionary files</seealso>
-into Erlang source.
-The resulting source implements the interface diameter requires
+The diameterc utility is used to compile a diameter
+&dictionary; into Erlang source.
+The resulting source implements the interface diameter required
to encode and decode the dictionary's messages and AVP's.</p>
+<p>
+The module &man_make; provides an alternate compilation interface.</p>
+
</description>
<section>
@@ -47,64 +56,52 @@ to encode and decode the dictionary's messages and AVP's.</p>
<tag><![CDATA[diameterc [<options>] <file>]]></tag>
<item>
<p>
-Transforms a single dictionary file. Valid options are as follows.</p>
-
-<!-- Leave -h/d/v undocumented, except for the usage message from the
- utility itself. -->
-
-<taglist>
-<tag><![CDATA[-o <dir>]]></tag>
-<item>
-<p>
-Specifies the directory into which the generated source should be written.
-Defaults to the current working directory.</p>
-</item>
+Compile a single dictionary file to Erlang source.
+Valid options are as follows.</p>
<tag><![CDATA[-i <dir>]]></tag>
<item>
<p>
-Specifies a directory to add to the code path.
-Use to point at beam files corresponding to dictionaries
-inherited by the one being compiled using <c>@inherits</c> or
-<c>--inherits</c>.
-Inheritance is a beam dependency, not an erl/hrl dependency.</p>
+Prepend the specified directory to the code path.
+Use to point at beam files compiled from inherited dictionaries,
+<c>&dict_inherits;</c> in a dictionary file creating a beam
+dependency, not an erl/hrl dependency.</p>
<p>
Multiple <c>-i</c> options can be specified.</p>
</item>
-<tag><![CDATA[-E]]></tag>
+<taglist>
+<tag><![CDATA[-o <dir>]]></tag>
<item>
<p>
-Supresses erl generation.</p>
+Write generated source to the specified directory.
+Defaults to the current working directory.</p>
</item>
+<tag><![CDATA[-E]]></tag>
<tag><![CDATA[-H]]></tag>
<item>
<p>
-Supresses hrl generation.</p>
+Supress erl and hrl generation, respectively.</p>
</item>
<tag><![CDATA[--name <name>]]></tag>
-<item>
-<p>
-Set <c>@name</c> in the dictionary file.
-Overrides any setting in the file itself.</p>
-</item>
-
<tag><![CDATA[--prefix <prefix>]]></tag>
<item>
<p>
-Set <c>@prefix</c> in the dictionary file.
+Set <c>&dict_name;</c> or <c>&dict_prefix;</c> to the specified
+string.
Overrides any setting in the file itself.</p>
</item>
<tag><![CDATA[--inherits <dict>]]></tag>
<item>
<p>
-Append an <c>@inherits</c> to the dictionary file.
-Specifying <c>'-'</c> as the dictionary has the effect of clearing
-any previous inherits, effectively ignoring previous inherits.</p>
+Append &dict_inherits; of the specified module.
+Specifying <c>"-"</c> has the effect of discarding clearing any
+previous inherits, both in the dictionary file and on the options
+list.</p>
<p>
Multiple <c>--inherits</c> options can be specified.</p>
@@ -130,20 +127,10 @@ Returns 0 on success, non-zero on failure.</p>
<!-- ===================================================================== -->
<section>
-<title>BUGS</title>
-
-<p>
-The identification of errors in the source file is poor.</p>
-
-</section>
-
-<!-- ===================================================================== -->
-
-<section>
<title>SEE ALSO</title>
<p>
-<seealso marker="diameter_dict">diameter_dict(4)</seealso></p>
+&man_make;, &man_dict;</p>
</section>
diff --git a/lib/diameter/doc/src/diameter_dict.xml b/lib/diameter/doc/src/diameter_dict.xml
index cc638dbc18..8b0687a22e 100644
--- a/lib/diameter/doc/src/diameter_dict.xml
+++ b/lib/diameter/doc/src/diameter_dict.xml
@@ -1,11 +1,22 @@
<?xml version="1.0" encoding="latin1" ?>
-<!DOCTYPE erlref SYSTEM "fileref.dtd">
+<!DOCTYPE erlref SYSTEM "fileref.dtd" [
+ <!ENTITY format
+ '<seealso marker="#FILE_FORMAT">FILE FORMAT</seealso>'>
+ <!ENTITY records
+ '<seealso marker="#MESSAGE_RECORDS">MESSAGE RECORDS</seealso>'>
+ <!ENTITY types
+ '<seealso marker="#DATA_TYPES">DATA TYPES</seealso>'>
+ <!ENTITY % also SYSTEM "seealso.ent" >
+ <!ENTITY % here SYSTEM "seehere.ent" >
+ %also;
+ %here;
+]>
<fileref>
<header>
<copyright>
-<year>2011</year>
+<year>2011</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -40,36 +51,33 @@ under the License.
<description>
<p>
-A diameter service as configured with <seealso
-marker="diameter#start_service">diameter:start_service/2</seealso>
+A diameter service, as configured with &mod_start_service;,
specifies one or more supported Diameter applications.
Each Diameter application specifies a dictionary module that knows how
to encode and decode its messages and AVPs.
The dictionary module is in turn generated from a file that defines
these messages and AVPs.
-The format of such a file is defined in
-<seealso marker="#FILE_FORMAT">FILE FORMAT</seealso> below.
+The format of such a file is defined in &format; below.
Users add support for their specific applications by creating
dictionary files, compiling them to Erlang modules using
-<seealso marker="diameterc">diameterc</seealso> and configuring the
+either &man_compile; or &man_make; and configuring the
resulting dictionaries modules on a service.</p>
<p>
-The codec generation also results in a hrl file that defines records
-for the messages and grouped AVPs defined for the application, these
-records being what a user of the diameter application sends and receives.
-(Modulo other available formats as discussed in <seealso
-marker="diameter_app">diameter_app(3)</seealso>.)
+Dictionary module generation also results in a hrl file that defines
+records for the messages and Grouped AVPs defined by the
+dictionary, these records being what a user of the diameter
+application sends and receives, modulo other possible formats as
+discussed in &man_app;.
These records and the underlying Erlang data types corresponding to
-Diameter data formats are discussed in <seealso
-marker="#MESSAGE_RECORDS">MESSAGE RECORDS</seealso> and <seealso
-marker="#DATA_TYPES">DATA TYPES</seealso> respectively.
-The generated hrl also contains defines for the possible values of
+Diameter data formats are discussed in &records; and &types;
+respectively.
+The generated hrl also contains macro definitions for the possible values of
AVPs of type Enumerated.</p>
<p>
The diameter application includes three dictionary modules
-corresponding to applications defined in section 2.4 of RFC 3588:
+corresponding to applications defined in section 2.4 of &the_rfc;:
<c>diameter_gen_base_rfc3588</c> for the Diameter Common Messages
application with application identifier 0,
<c>diameter_gen_accounting</c> for the Diameter Base Accounting
@@ -108,6 +116,8 @@ The order in which sections are specified is unimportant.</p>
<taglist>
+<marker id="id"/>
+
<tag><c>@id Number</c></tag>
<item>
<p>
@@ -125,12 +135,14 @@ is used to identify the relevant dictionary module.</p>
<p>
Example:</p>
-<code>
+<pre>
@id 16777231
-</code>
+</pre>
</item>
+<marker id="name"/>
+
<tag><c>@name Mod</c></tag>
<item>
<p>
@@ -146,12 +158,14 @@ with existing modules in the system.</p>
<p>
Example:</p>
-<code>
+<pre>
@name etsi_e2
-</code>
+</pre>
</item>
+<marker id="prefix"/>
+
<tag><c>@prefix Name</c></tag>
<item>
<p>
@@ -169,12 +183,14 @@ different Diameter applications.</p>
<p>
Example:</p>
-<code>
+<pre>
@prefix etsi_e2
-</code>
+</pre>
</item>
+<marker id="vendor"/>
+
<tag><c>@vendor Number Name</c></tag>
<item>
<p>
@@ -189,12 +205,14 @@ The section has empty content.</p>
<p>
Example:</p>
-<code>
+<pre>
@vendor 13019 ETSI
-</code>
+</pre>
</item>
+<marker id="avp_vendor_id"/>
+
<tag><c>@avp_vendor_id Number</c></tag>
<item>
<p>
@@ -205,16 +223,18 @@ The section content consists of AVP names.</p>
<p>
Example:</p>
-<code>
+<pre>
@avp_vendor_id 2937
WWW-Auth
Domain-Index
Region-Set
-</code>
+</pre>
</item>
+<marker id="inherits"/>
+
<tag><c>@inherits Mod</c></tag>
<item>
<p>
@@ -238,18 +258,20 @@ is equivalent to using <c>@avp_vendor_id</c> with a copy of the
dictionary's definitions but the former makes for easier reuse.</p>
<p>
-All dictionaries should typically inherit RFC3588 AVPs from
+All dictionaries should typically inherit &the_rfc; AVPs from
<c>diameter_gen_base_rfc3588</c>.</p>
<p>
Example:</p>
-<code>
+<pre>
@inherits diameter_gen_base_rfc3588
-</code>
+</pre>
</item>
+<marker id="avp_types"/>
+
<tag><c>@avp_types</c></tag>
<item>
<p>
@@ -260,7 +282,7 @@ The section consists of definitions of the form</p>
<p>
where Code is the integer AVP code, Type identifies an AVP Data Format
-as defined in <seealso marker="#DATA_TYPES">DATA TYPES</seealso> below,
+as defined in section &types; below,
and Flags is a string of V, M and P characters indicating the flags to be
set on an outgoing AVP or a single <c>'-'</c> (minus) character if
none are to be set.</p>
@@ -268,20 +290,22 @@ none are to be set.</p>
<p>
Example:</p>
-<code>
+<pre>
@avp_types
Location-Information 350 Grouped MV
Requested-Information 353 Enumerated V
-</code>
+</pre>
+<warning>
<p>
-Note that the P flag has been deprecated by the Diameter Maintenance
-and Extensions Working Group of the IETF: diameter will set the P flag
-to 0 as mandated by the current draft standard.</p>
+The P flag has been deprecated by &the_rfc;.</p>
+</warning>
</item>
+<marker id="custom_types"/>
+
<tag><c>@custom_types Mod</c></tag>
<item>
<p>
@@ -296,13 +320,15 @@ encode/decode.</p>
<p>
Example:</p>
-<code>
+<pre>
@custom_types rfc4005_avps
Framed-IP-Address
-</code>
+</pre>
</item>
+<marker id="codecs"/>
+
<tag><c>@codecs Mod</c></tag>
<item>
<p>
@@ -313,22 +339,24 @@ Like <c>@custom_types</c> but requires the specified module to export
<p>
Example:</p>
-<code>
+<pre>
@codecs rfc4005_avps
Framed-IP-Address
-</code>
+</pre>
</item>
+<marker id="messages"/>
+
<tag><c>@messages</c></tag>
<item>
<p>
Defines the messages of the application.
The section content consists of definitions of the form specified in
-section 3.2 of RFC 3588, "Command Code ABNF specification".</p>
+section 3.2 of &the_rfc;, "Command Code Format Specification".</p>
<!-- RFC 4740 RTR/RTA -->
-<code>
+<pre>
@messages
RTR ::= &lt; Diameter Header: 287, REQ, PXY >
@@ -361,35 +389,39 @@ RTA ::= &lt; Diameter Header: 287, PXY >
* [ Proxy-Info ]
* [ Route-Record ]
* [ AVP ]
-</code>
+</pre>
</item>
+<marker id="grouped"/>
+
<tag><c>@grouped</c></tag>
<item>
<p>
Defines the contents of the AVPs of the application having type
Grouped.
The section content consists of definitions of the form specified in
-section 4.4 of RFC 3588, "Grouped AVP Values".</p>
+section 4.4 of &the_rfc;, "Grouped AVP Values".</p>
<p>
Example:</p>
-<code>
+<pre>
@grouped
SIP-Deregistration-Reason ::= &lt; AVP Header: 383 >
{ SIP-Reason-Code }
[ SIP-Reason-Info ]
* [ AVP ]
-</code>
+</pre>
<p>
Specifying a Vendor-Id in the definition of a grouped AVP is
equivalent to specifying it with <c>@avp_vendor_id</c>.</p>
</item>
+<marker id="enum"/>
+
<tag><c>@enum Name</c></tag>
<item>
<p>
@@ -406,16 +438,18 @@ otherwise defined in another dictionary.</p>
<p>
Example:</p>
-<code>
+<pre>
@enum SIP-Reason-Code
PERMANENT_TERMINATION 0
NEW_SIP_SERVER_ASSIGNED 1
SIP_SERVER_CHANGE 2
REMOVE_SIP_SERVER 3
-</code>
+</pre>
</item>
+<marker id="end"/>
+
<tag><c>@end</c></tag>
<item>
<p>
@@ -442,28 +476,28 @@ The hrl generated from a dictionary specification defines records for the
messages and grouped AVPs defined in <c>@messages</c> and
<c>@grouped</c> sections.
For each message or grouped AVP definition, a record is defined whose
-name is the message or AVP name prefixed with any dictionary prefix
-defined with <c>@prefix</c> and whose fields are the names of the AVPs
+name is the message or AVP name, prefixed with any dictionary prefix
+defined with <c>@prefix</c>, and whose fields are the names of the AVPs
contained in the message or grouped AVP in the order specified in the
definition in question.
For example, the grouped AVP</p>
-<code>
+<pre>
SIP-Deregistration-Reason ::= &lt; AVP Header: 383 >
{ SIP-Reason-Code }
[ SIP-Reason-Info ]
* [ AVP ]
-</code>
+</pre>
<p>
will result in the following record definition given an empty
prefix.</p>
-<code>
+<pre>
-record('SIP-Deregistration-Reason' {'SIP-Reason-Code',
'SIP-Reason-Info',
'AVP'}).
-</code>
+</pre>
<p>
The values encoded in the fields of generated records depends on the
@@ -471,7 +505,7 @@ type and number of times the AVP can occur.
In particular, an AVP which is specified as occurring exactly once is
encoded as a value of the AVP's type while an AVP with any other
specification is encoded as a list of values of the AVP's type.
-The AVP's type is as specified in the AVP definition, the RFC 3588
+The AVP's type is as specified in the AVP definition, the &the_rfc;
types being described below.</p>
<marker id="DATA_TYPES"/>
@@ -484,13 +518,11 @@ types being described below.</p>
<p>
The data formats defined in sections 4.2 ("Basic AVP Data
-Formats") and 4.3 ("Derived AVP Data Formats") of RFC 3588 are encoded
+Formats") and 4.3 ("Derived AVP Data Formats") of &the_rfc; are encoded
as values of the types defined here.
-Values are passed to <seealso
-marker="diameter#call">diameter:call/4</seealso>
+Values are passed to &mod_call;
in a request record when sending a request, returned in a resulting
-answer record and passed to a <seealso
-marker="diameter_app#handle_request">handle_request</seealso>
+answer record and passed to a &app_handle_request;
callback upon reception of an incoming request.</p>
<p>
@@ -505,7 +537,7 @@ callback upon reception of an incoming request.</p>
<marker id="Float64"/>
<marker id="Grouped"/>
-<code>
+<pre>
OctetString() = [0..255]
Integer32() = -2147483647..2147483647
Integer64() = -9223372036854775807..9223372036854775807
@@ -514,7 +546,7 @@ Unsigned64() = 0..18446744073709551615
Float32() = '-infinity' | float() | infinity
Float64() = '-infinity' | float() | infinity
Grouped() = record()
-</code>
+</pre>
<p>
On encode, an OctetString() can be specified as an iolist(),
@@ -528,10 +560,10 @@ section.</p>
<em>Derived AVP Data Formats</em></p>
<marker id="Address"/>
-<code>
+<pre>
Address() = OctetString()
| tuple()
-</code>
+</pre>
<p>
On encode, an OctetString() IPv4 address is parsed in the usual
@@ -543,7 +575,7 @@ An IPv6 tuple() has length 8 and contains values of type 0..65535.
The tuple representation is used on decode.</p>
<marker id="Time"/>
-<code>
+<pre>
Time() = {date(), time()}
where
@@ -557,19 +589,19 @@ where
Hour = 0..23
Minute = 0..59
Second = 0..59
-</code>
+</pre>
<p>
Additionally, values that can be encoded are
-limited by way of their encoding as four octets as required by RFC
-3588 with the required extension from RFC 2030.
+limited by way of their encoding as four octets as required by
+&the_rfc; with the required extension from RFC 2030.
In particular, only values between <c>{{1968,1,20},{3,14,8}}</c>
and <c>{{2104,2,26},{9,42,23}}</c> (both inclusive) can be encoded.</p>
<marker id="UTF8String"/>
-<code>
+<pre>
UTF8String() = [integer()]
-</code>
+</pre>
<p>
List elements are the UTF-8 encodings of the individual characters
@@ -577,15 +609,15 @@ in the string.
Invalid codepoints will result in encode/decode failure.</p>
<marker id="DiameterIdentity"/>
-<code>
+<pre>
DiameterIdentity() = OctetString()
-</code>
+</pre>
<p>
A value must have length at least 1.</p>
<marker id="DiameterURI"/>
-<code>
+<pre>
DiameterURI() = OctetString()
| #diameter_URI{type = Type,
fqdn = FQDN,
@@ -600,19 +632,19 @@ where
Port = integer()
Transport = sctp | tcp
Protocol = diameter | radius | 'tacacs+'
-</code>
+</pre>
<p>
On encode, fields port, transport and protocol default to 3868, sctp
and diameter respectively.
The grammar of an OctetString-valued DiameterURI() is as specified in
-section 4.3 of RFC 3588.
+section 4.3 of &the_rfc;.
The record representation is used on decode.</p>
<marker id="Enumerated"/>
-<code>
+<pre>
Enumerated() = Integer32()
-</code>
+</pre>
<p>
On encode, values can be specified using the macros defined in a
@@ -620,10 +652,10 @@ dictionary's hrl file.</p>
<marker id="IPFilterRule"/>
<marker id="QoSFilterRule"/>
-<code>
+<pre>
IPFilterRule() = OctetString()
QoSFilterRule() = OctetString()
-</code>
+</pre>
<p>
Values of these types are not currently parsed by diameter.</p>
@@ -637,9 +669,7 @@ Values of these types are not currently parsed by diameter.</p>
<title>SEE ALSO</title>
<p>
-<seealso marker="diameterc">diameterc(1)</seealso>,
-<seealso marker="diameter">diameter(3)</seealso>,
-<seealso marker="diameter_app">diameter_app(3)</seealso></p>
+&man_compile;, &man_main;, &man_app;, &man_codec;, &man_make;</p>
</section>
diff --git a/lib/diameter/doc/src/diameter_examples.xml b/lib/diameter/doc/src/diameter_examples.xml
index 966d1f1eee..1fd7755695 100644
--- a/lib/diameter/doc/src/diameter_examples.xml
+++ b/lib/diameter/doc/src/diameter_examples.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
-<year>2011</year>
+<year>2011</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
@@ -37,7 +37,6 @@ under the License.
<!-- ===================================================================== -->
<p>
-To be written.
Example code can be found in the diameter application's
<c>examples</c> subdirectory.</p>
diff --git a/lib/diameter/doc/src/diameter_intro.xml b/lib/diameter/doc/src/diameter_intro.xml
index ef08002a8b..fd578ccf45 100644
--- a/lib/diameter/doc/src/diameter_intro.xml
+++ b/lib/diameter/doc/src/diameter_intro.xml
@@ -1,10 +1,13 @@
<?xml version="1.0" encoding="latin1" ?>
-<!DOCTYPE chapter SYSTEM "chapter.dtd">
+<!DOCTYPE chapter SYSTEM "chapter.dtd" [
+ <!ENTITY % also SYSTEM "seealso.ent">
+ %also;
+]>
<chapter>
<header>
<copyright>
-<year>2011</year>
+<year>2011</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
@@ -35,7 +38,7 @@ under the License.
<p>
The diameter application is an implementation of the Diameter protocol
-as defined by RFC 3588.
+as defined by &the_rfc;.
It supports arbitrary Diameter applications by way of a
<em>dictionary</em> interface that allows messages and AVP's to be
defined and input into diameter as configuration.
@@ -44,11 +47,11 @@ agent.
This chapter provides a short overview of the application.</p>
<p>
-A Diameter peer is implemented by configuring a <em>service</em> and
+A Diameter node is implemented by configuring a <em>service</em> and
one or more <em>transports</em> using the interface module
<seealso marker="diameter">diameter</seealso>.
The service configuration defines the Diameter applications to be
-supported by the peer and, typically, the capabilities that it should
+supported by the node and, typically, the capabilities that it should
send to remote peers at capabilities exchange upon the establishment
of transport connections.
A transport is configured on a service and provides protocol-specific
@@ -57,16 +60,17 @@ diameter and implemented by a transport module.
The diameter application provides two transport modules: <seealso
marker="diameter_tcp">diameter_tcp</seealso> and <seealso
marker="diameter_sctp">diameter_sctp</seealso> for transport over TCP
-(using <c>gen_tcp</c>) and SCTP (using <c>gen_sctp</c>) respectively.
+(using <seealso marker="kernel:gen_tcp">gen_tcp</seealso>) and SCTP
+(using <seealso marker="kernel:gen_sctp">gen_sctp</seealso>) respectively.
Other transports can be provided by any module that implements
diameter's <seealso marker="diameter_transport">transport
interface</seealso>.</p>
<p>
-While a service typically implements a single Diameter peer (as
+While a service typically implements a single Diameter node (as
identified by an Origin-Host AVP), transports can themselves be
-associated with capabilities AVP's so that a single service be used to
-implement more than one Diameter peer.</p>
+associated with capabilities AVP's so that a single service can be
+used to implement more than one Diameter node.</p>
<p>
Each Diameter application defined on a service is configured with a
@@ -76,19 +80,19 @@ diameter communicates the connectivity of remote peers, requests peer
selection for outgoing requests, and communicates the reception of
incoming Diameter request and answer messages.
An application using diameter implements these application callback
-modules to provide the functionality of the Diameter peer(s) it
+modules to provide the functionality of the Diameter node(s) it
implements.</p>
<p>
-Each Diameter application is also configured with one or more
-dictionary modules
+Each Diameter application is also configured with a
+dictionary module
that provide encode/decode functionality for outgoing/incoming
-Diameter messages.
-A module is generated from a <seealso
-marker="diameter_dict">specification file</seealso> using the <seealso
+Diameter messages belonging to the application.
+A dictionary module is generated from a <seealso
+marker="diameter_dict">dictionary file</seealso> using the <seealso
marker="diameterc">diameterc</seealso> utility.
-Dictionaries for the RFC 3588 Diameter Common Messages, Base
-Accounting and Relay applications are provided by the diameter
+Dictionaries for the &the_rfc; Diameter Common Messages, Base
+Accounting and Relay applications are provided with the diameter
application.</p>
</chapter>
diff --git a/lib/diameter/doc/src/diameter_make.xml b/lib/diameter/doc/src/diameter_make.xml
new file mode 100644
index 0000000000..da6124310e
--- /dev/null
+++ b/lib/diameter/doc/src/diameter_make.xml
@@ -0,0 +1,144 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE erlref SYSTEM "erlref.dtd" [
+ <!ENTITY filename
+ '<seealso marker="kernel:file#type-name">file:name()</seealso>'>
+ <!ENTITY dictionary
+ '<seealso marker="diameter_dict">dictionary file</seealso>'>
+ <!ENTITY % also SYSTEM "seealso.ent" >
+ <!ENTITY % here SYSTEM "seehere.ent" >
+ %also;
+ %here;
+]>
+
+<erlref>
+<header>
+<copyright>
+<year>2012</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>diameter_make(3)</title>
+<prepared>Anders Svensson</prepared>
+<responsible></responsible>
+<docno></docno>
+<approved></approved>
+<checked></checked>
+<date></date>
+<rev></rev>
+<file>diameter_make.xml</file>
+</header>
+
+<module>diameter_make</module>
+<modulesummary>Diameter dictionary compilation.</modulesummary>
+
+<description>
+
+<p>
+The function &codec; is used to compile a diameter
+&dictionary; into Erlang source.
+The resulting source implements the interface diameter required
+to encode and decode the dictionary's messages and AVP's.</p>
+
+<p>
+The utility &man_compile; provides an alternate compilation
+interface.</p>
+
+</description>
+
+<!-- ===================================================================== -->
+
+<funcs>
+
+<func>
+<name>codec(Path::string(), [Opt]) -> ok | {error, Reason}</name>
+<fsummary>Compile a dictionary file into Erlang source.</fsummary>
+<desc>
+
+<p>
+Compile a single dictionary file to Erlang source.
+<c>Opt</c> can have the following types.</p>
+
+<taglist>
+
+<tag><c>{include, Dir::string()}</c></tag>
+<item>
+<p>
+Prepend the specified directory to the code path.
+Use to point at beam files compiled from inherited dictionaries,
+<c>&dict_inherits;</c> in a dictionary file creating a beam
+dependency, not an erl/hrl dependency.</p>
+
+<p>
+Multiple <c>include</c> options can be specified.</p>
+</item>
+
+<tag><c>{outdir, Dir::string()}</c></tag>
+<item>
+<p>
+Write generated source to the specified directory.
+Defaults to the current working directory.</p>
+</item>
+
+<tag><c>{name|prefix, string()}</c></tag>
+<item>
+<p>
+Set <c>&dict_name;</c> or <c>&dict_prefix;</c> to the specified
+string.
+Overrides any setting in the file itself.</p>
+</item>
+
+<tag><c>{inherits, Mod::string()}</c></tag>
+<item>
+<p>
+Append &dict_inherits; of the specified module.
+Specifying <c>"-"</c> has the effect of discarding clearing any
+previous inherits, both in the dictionary file and on the options
+list.</p>
+
+<p>
+Multiple <c>inherits</c> options can be specified.</p>
+</item>
+
+</taglist>
+
+</desc>
+</func>
+
+</funcs>
+
+<!-- ===================================================================== -->
+
+<section>
+<title>BUGS</title>
+
+<p>
+All options are string-valued.
+In particular, it is not currently possible to
+an &dict_inherits; module as an atom() or a path as a &filename;</p>
+
+</section>
+
+<!-- ===================================================================== -->
+
+<section>
+<title>SEE ALSO</title>
+
+<p>
+&man_compile;, &man_dict;</p>
+
+</section>
+
+</erlref>
diff --git a/lib/diameter/doc/src/diameter_sctp.xml b/lib/diameter/doc/src/diameter_sctp.xml
index c1e839b8e1..5e3fd5eaf1 100644
--- a/lib/diameter/doc/src/diameter_sctp.xml
+++ b/lib/diameter/doc/src/diameter_sctp.xml
@@ -1,10 +1,21 @@
<?xml version="1.0" encoding="latin1" ?>
-<!DOCTYPE erlref SYSTEM "erlref.dtd">
+<!DOCTYPE erlref SYSTEM "erlref.dtd" [
+ <!ENTITY gen_sctp '<seealso marker="kernel:gen_sctp">gen_sctp(3)</seealso>'>
+ <!ENTITY gen_sctp_open1
+ '<seealso marker="kernel:gen_sctp#open-1">gen_sctp:open/1</seealso>'>
+ <!ENTITY ip_address
+ '<seealso marker="kernel:inet#type-ip_address">inet:ip_address()</seealso>'>
+ <!ENTITY inet '<seealso marker="kernel:inet">inet(3)</seealso>'>
+ <!ENTITY % also SYSTEM "seealso.ent" >
+ <!ENTITY % here SYSTEM "seehere.ent" >
+ %also;
+ %here;
+]>
<erlref>
<header>
<copyright>
-<year>2011</year>
+<year>2011</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -38,12 +49,11 @@ under the License.
<description>
<p>
-This module implements diameter transport over SCTP using gen_sctp.
+This module implements diameter transport over SCTP using &gen_sctp;.
It can be specified as the value of a transport_module option to
-<seealso
-marker="diameter#add_transport">diameter:add_transport/2</seealso>
+&mod_add_transport;
and implements the behaviour documented in
-<seealso marker="diameter_transport">diameter_transport(3)</seealso>.</p>
+&man_transport;.</p>
<marker id="start"/>
</description>
@@ -58,29 +68,28 @@ and implements the behaviour documented in
<fsummary>Start a transport process.</fsummary>
<type>
<v>Type = connect | accept</v>
-<v>Ref = reference()</v>
+<v>Ref = &mod_transport_ref;</v>
<v>Svc = #diameter_service{}</v>
-<v>Opt = {raddr, ip_address()} | {rport, integer()} | term()</v>
+<v>Opt = {raddr, &ip_address;} | {rport, integer()} | term()</v>
<v>Pid = pid()</v>
-<v>LAddr = ip_address()</v>
+<v>LAddr = &ip_address;</v>
<v>Reason = term()</v>
</type>
<desc>
<p>
-The start function required by <seealso
-marker="diameter_transport#start">diameter_transport(3)</seealso>.</p>
+The start function required by &man_transport;.</p>
<p>
The only diameter_sctp-specific argument is the options list.
Options <c>raddr</c> and <c>rport</c> specify the remote address
and port for a connecting transport and not valid for a listening
-transport.
-The former is required while latter defaults to 3868 if unspecified.
+transport: the former is required while latter defaults to 3868 if
+unspecified.
More than one <c>raddr</c> option can be specified, in which case the
connecting transport in question attempts each in sequence until
an association is established.
-Remaining options are any accepted by gen_sctp:open/1, with the exception
+Remaining options are any accepted by &gen_sctp_open1;, with the exception
of options <c>mode</c>, <c>binary</c>, <c>list</c>, <c>active</c>
and <c>sctp_events</c>.
Note that options <c>ip</c> and <c>port</c> specify the local address
@@ -88,30 +97,40 @@ and port respectively.</p>
<p>
Multiple <c>ip</c> options can be specified for a multihomed peer.
-If none are specified then the values of Host-IP-Address
-on the service are used. (In particular, one of these must be specified.)
+If none are specified then the values of <c>Host-IP-Address</c>
+in the <c>#diameter_service{}</c> record are used.
+(In particular, one of these must be specified.)
Option <c>port</c> defaults to 3868 for a listening transport and 0 for a
connecting transport.</p>
+<warning>
+<p>
+An insufficiently large receive buffer may result in a peer having to
+resend incoming messages: set the &inet; option <c>recbuf</c> to increase
+the buffer size.</p>
+
+<p>
+An insufficiently large send buffer may result in outgoing messages
+being discarded: set the &inet; option <c>sndbuf</c> to increase
+the buffer size.</p>
+</warning>
+
<p>
diameter_sctp uses the <c>transport_data</c> field of
-the <c>diameter_packet</c> record to communicate the stream on which an
+the <c>#diameter_packet{}</c> record to communicate the stream on which an
inbound message has been received, or on which an outbound message
should be sent: the value will be of the form <c>{stream, Id}</c>
-on an inbound message passed to a <seealso
-marker="diameter_app#handle_request">handle_request</seealso> or <seealso
-marker="diameter_app#handle_answer">handle_answer</seealso> callback.
-For an outbound message, either <c>undefined</c> (explicitly of
-by specifying the outbound message as a <c>binary()</c>) or a tuple
-should be set in the return value of <seealso
-marker="diameter_app#handle_request">handle_request</seealso>
+on an inbound message passed to a &app_handle_request; or
+&app_handle_answer; callback.
+For an outbound message, either <c>undefined</c> (explicitly or
+by receiving the outbound message as a <c>binary()</c>) or a tuple
+should be set in the return value of &app_handle_request;
(typically by retaining the value passed into this function)
-or <seealso
-marker="diameter_app#prepare_request">prepare_request</seealso>.
-The value <c>undefined</c> uses a "next outbound stream" id and then
-increments this modulo the total number outbound streams. That
-is, successive values of <c>undefined</c> cycle through all outbound
-streams.</p>
+or &app_prepare_request;.
+The value <c>undefined</c> uses a "next outbound stream" id and
+increments this modulo the total number outbound streams.
+That is, successive values of <c>undefined</c> cycle through all
+outbound streams.</p>
<!-- TODO: Some way of getting at the number of available outbound -->
<!-- streams. -->
@@ -128,7 +147,7 @@ streams.</p>
<title>SEE ALSO</title>
<p>
-<seealso marker="diameter_transport">diameter_transport(3)</seealso></p>
+&man_main;, &man_transport;, &gen_sctp;, &inet;</p>
</section>
diff --git a/lib/diameter/doc/src/diameter_soc.xml b/lib/diameter/doc/src/diameter_soc.xml
index 6b9ef9f756..16f6b9d5bb 100644
--- a/lib/diameter/doc/src/diameter_soc.xml
+++ b/lib/diameter/doc/src/diameter_soc.xml
@@ -1,11 +1,15 @@
<?xml version="1.0" encoding="latin1" ?>
-<!DOCTYPE chapter SYSTEM "chapter.dtd">
+<!DOCTYPE chapter SYSTEM "chapter.dtd" [
+ <!ENTITY % also SYSTEM "seealso.ent" >
+ %also;
+]>
<chapter>
<header>
<copyright>
<year>2011</year>
+<year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
@@ -41,38 +45,20 @@ Known points of questionable or non-compliance.</p>
<!-- ===================================================================== -->
<section>
-<title>RFC 3588</title>
+<title>&the_rfc;</title>
<list>
<item>
<p>
-The End-to-End Security framework (section 2.9) isn't implemented
-since it is largely unspecified.
-The document that was to describe it
-(reference [AAACMS]) was abandoned in an uncompleted state several
-years ago and the current draft RFC deprecates the framework,
-including the P Flag in the AVP header.</p>
-</item>
-
-<item>
-<p>
-There is no TLS support over SCTP.
-RFC 3588 requires that a Diameter server support TLS but in
-practise this seems to mean TLS over SCTP since there are limitations
-with running over SCTP: see RFC 6083 (DTLS over SCTP), which is a
-response to RFC 3436 (TLS over SCTP).
-The current RFC 3588 draft acknowledges this by equating
-TLS with TLS/TCP and DTLS/SCTP but we do not yet support DTLS.</p>
+There is no support for DTLS over SCTP.</p>
</item>
<item>
<p>
There is no explicit support for peer discovery (section 5.2).
It can possibly be implemented on top of diameter as is but this is
-probably something that diameter should do.
-The current draft deprecates portions of the original RFC's mechanisms
-however.</p>
+probably something that diameter should do.</p>
</item>
<item>
diff --git a/lib/diameter/doc/src/diameter_tcp.xml b/lib/diameter/doc/src/diameter_tcp.xml
index e6b53383c0..e3b8c733b7 100644
--- a/lib/diameter/doc/src/diameter_tcp.xml
+++ b/lib/diameter/doc/src/diameter_tcp.xml
@@ -1,10 +1,32 @@
<?xml version="1.0" encoding="latin1" ?>
-<!DOCTYPE erlref SYSTEM "erlref.dtd">
+<!DOCTYPE erlref SYSTEM "erlref.dtd" [
+ <!ENTITY gen_tcp_connect3
+ '<seealso marker="kernel:gen_tcp#connect-3">gen_tcp:connect/3</seealso>'>
+ <!ENTITY gen_tcp_listen2
+ '<seealso marker="kernel:gen_tcp#listen-2">gen_tcp:listen/2</seealso>'>
+ <!ENTITY ip_address
+ '<seealso marker="kernel:inet#type-ip_address">inet:ip_address()</seealso>'>
+ <!ENTITY ssl_connect2
+ '<seealso marker="ssl:ssl#connect-2">ssl:connect/2</seealso>'>
+ <!ENTITY ssl_connect3
+ '<seealso marker="ssl:ssl#connect-3">ssl:connect/3</seealso>'>
+ <!ENTITY ssl_accept2
+ '<seealso marker="ssl:ssl#ssl_accept-2">ssl:ssl_accept/2</seealso>'>
+ <!ENTITY ssl_listen2
+ '<seealso marker="ssl:ssl#listen-2">ssl:listen/2</seealso>'>
+ <!ENTITY gen_tcp '<seealso marker="kernel:gen_tcp">gen_tcp(3)</seealso>'>
+ <!ENTITY inet '<seealso marker="kernel:inet">inet(3)</seealso>'>
+ <!ENTITY ssl '<seealso marker="ssl:ssl">ssl(3)</seealso>'>
+ <!ENTITY % also SYSTEM "seealso.ent" >
+ <!ENTITY % here SYSTEM "seehere.ent" >
+ %also;
+ %here;
+]>
<erlref>
<header>
<copyright>
-<year>2011</year>
+<year>2011</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -38,14 +60,13 @@ under the License.
<description>
<p>
-This module implements diameter transport over TCP using gen_tcp.
-It can be specified as the value of a transport_module option to
-<seealso
-marker="diameter#add_transport">diameter:add_transport/2</seealso>
+This module implements diameter transport over TCP using &gen_tcp;.
+It can be specified as the value of a <c>transport_module</c> option to
+&mod_add_transport;
and implements the behaviour documented in
-<seealso marker="diameter_transport">diameter_transport(3)</seealso>.
+&man_transport;.
TLS security is supported, both as an upgrade following
-capabilities exchange as specified by RFC 3588 and
+capabilities exchange as specified by &the_rfc; and
at connection establishment as in the current draft standard.</p>
<p>
@@ -65,23 +86,22 @@ before configuring TLS capability on diameter transports.</p>
<fsummary>Start a transport process.</fsummary>
<type>
<v>Type = connect | accept</v>
-<v>Ref = reference()</v>
+<v>Ref = &mod_transport_ref;</v>
<v>Svc = #diameter_service{}</v>
-<v>Opt = OwnOpt | SslOpt | OtherOpt</v>
+<v>Opt = OwnOpt | SslOpt | TcpOpt</v>
<v>Pid = pid()</v>
-<v>LAddr = ip_address()</v>
+<v>LAddr = &ip_address;</v>
<v>Reason = term()</v>
-<v>OwnOpt = {raddr, ip_address()}
+<v>OwnOpt = {raddr, &ip_address;}
| {rport, integer()}
| {port, integer()}</v>
<v>SslOpt = {ssl_options, true | list()}</v>
-<v>OtherOpt = term()</v>
+<v>TcpOpt = term()</v>
</type>
<desc>
<p>
-The start function required by <seealso
-marker="diameter_transport#start">diameter_transport(3)</seealso>.</p>
+The start function required by &man_transport;.</p>
<p>
The only diameter_tcp-specific argument is the options list.
@@ -89,28 +109,29 @@ Options <c>raddr</c> and <c>rport</c> specify the remote address
and port for a connecting transport and are not valid for a listening
transport.
Option <c>ssl_options</c> must be specified for a transport
-that must be able to support TLS: a value of <c>true</c> results in a
+that should support TLS: a value of <c>true</c> results in a
TLS handshake immediately upon connection establishment while
-list() specifies options to be passed to ssl:connect/2 of ssl:ssl_accept/2
+<c>list()</c> specifies options to be passed to &ssl_connect2; or
+&ssl_accept2;
after capabilities exchange if TLS is negotiated.
-Remaining options are any accepted by ssl:connect/3 or gen_tcp:connect/3 for
-a connecting transport, or ssl:listen/3 or gen_tcp:listen/2 for
+Remaining options are any accepted by &ssl_connect3; or
+&gen_tcp_connect3; for
+a connecting transport, or &ssl_listen2; or &gen_tcp_listen2; for
a listening transport, depending on whether or not <c>{ssl_options, true}</c>
has been specified.
-Options <c>binary</c>, <c>packet</c> and <c>active</c> cannot be specified.
+Options <c>binary</c>,
+<c>packet</c> and <c>active</c> cannot be specified.
Also, option <c>port</c> can be specified for a listening transport
to specify the local listening port, the default being the standardized
3868 if unspecified.
-Note that option <c>ip</c> specifies the local address.</p>
+Note that the option <c>ip</c> specifies the local address.</p>
<p>
An <c>ssl_options</c> list must be specified if and only if
-the transport in question has specified an Inband-Security-Id
-AVP with value TLS on the relevant call to
-<seealso
-marker="diameter#start_service">start_service/2</seealso> or
-<seealso
-marker="diameter#add_transport">add_transport/2</seealso>,
+the transport in question has set <c>Inband-Security-Id</c> to
+1 (<c>TLS</c>), as
+specified to either &mod_start_service; or
+&mod_add_transport;,
so that the transport process will receive notification of
whether or not to commence with a TLS handshake following capabilities
exchange.
@@ -122,9 +143,9 @@ that will not be forthcoming, which will eventually cause the RFC 3539
watchdog to take down the connection.</p>
<p>
-If the service specifies more than one Host-IP-Address and
-option <c>ip</c> is unspecified then then the
-first of the service's addresses is used as the local address.</p>
+If the <c>#diameter_service{}</c> record has more than one
+<c>Host-IP-Address</c> and option <c>ip</c> is unspecified then the
+first of the these addresses is used as the local address.</p>
<p>
The returned local address list has length one.</p>
@@ -141,8 +162,7 @@ The returned local address list has length one.</p>
<title>SEE ALSO</title>
<p>
-<seealso marker="diameter">diameter(3)</seealso>,
-<seealso marker="diameter_transport">diameter_transport(3)</seealso></p>
+&man_main;, &man_transport;, &gen_tcp;, &inet;, &ssl;</p>
</section>
diff --git a/lib/diameter/doc/src/diameter_transport.xml b/lib/diameter/doc/src/diameter_transport.xml
index 087a90b099..55b531155f 100644
--- a/lib/diameter/doc/src/diameter_transport.xml
+++ b/lib/diameter/doc/src/diameter_transport.xml
@@ -1,10 +1,18 @@
<?xml version="1.0" encoding="latin1" ?>
-<!DOCTYPE erlref SYSTEM "erlref.dtd">
+<!DOCTYPE erlref SYSTEM "erlref.dtd" [
+ <!ENTITY message '<seealso marker="#message">message()</seealso>'>
+ <!ENTITY ip_address
+ '<seealso marker="kernel:inet#type-ip_address">inet:ip_address()</seealso>'>
+ <!ENTITY % also SYSTEM "seealso.ent" >
+ <!ENTITY % here SYSTEM "seehere.ent" >
+ %also;
+ %here;
+]>
<erlref>
<header>
<copyright>
-<year>2011</year>
+<year>2011</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -38,79 +46,116 @@ under the License.
<description>
<p>
-A module specified as a <c>transport_module</c> to <seealso
-marker="diameter#add_transport">diameter:add_transport/2</seealso>
+A module specified as a <c>transport_module</c> to &mod_add_transport;
must implement the interface documented here.
The interface consists of a function with which
diameter starts a transport process and a message interface with which
the transport process communicates with the process that starts it (aka its
parent).</p>
-<marker id="start"/>
</description>
<!-- ===================================================================== -->
+<section>
+<title>DATA TYPES</title>
+
+<taglist>
+
+<marker id="message"/>
+
+<tag><c>message() = binary() | &codec_packet;</c></tag>
+<item>
+<p>
+A Diameter message as passed over the transport interface.</p>
+
+<p>
+For an inbound message from a transport process, a &codec_packet; must
+contain the received message in its <c>bin</c> field.
+In the case of an inbound request, any value set in the
+<c>transport_data</c> field will passed back to the transport module
+in the corresponding answer message, unless the sender supplies
+another value.</p>
+
+<p>
+For an outbound message to a transport process, a &codec_packet; has a
+value other than <c>undefined</c> in its <c>transport_data</c> field
+and has the binary() to send in its <c>bin</c> field.</p>
+</item>
+
+</taglist>
+
+</section>
+
+<!-- ===================================================================== -->
+
<funcs>
<func>
-<name>Mod:start({Type, Ref}, Svc, Opts)
- -> {ok, Pid} | {ok, Pid, LAddrs} | {error, Reason}</name>
+<name>Mod:start({Type, Ref}, Svc, Config)
+ -> {ok, Pid}
+ | {ok, Pid, LAddrs}
+ | {error, Reason}</name>
<fsummary>Start a transport process.</fsummary>
<type>
<v>Type = connect | accept</v>
-<v>Ref = reference()</v>
+<v>Ref = &mod_transport_ref;</v>
<v>Svc = #diameter_service{}</v>
-<v>Opts = term()</v>
+<v>Config = term()</v>
<v>Pid = pid()</v>
-<v>LAddrs = [ip_address()]</v>
+<v>LAddrs = [&ip_address;]</v>
<v>Reason = term()</v>
</type>
<desc>
<p>
Start a transport process.
-Called by diameter as a consequence of a call to <seealso
-marker="diameter#add_transport">diameter:add_transport/2</seealso> in
+Called by diameter as a consequence of a call to &mod_add_transport; in
order to establish or accept a transport connection respectively.
A transport process maintains a connection with a single remote peer.</p>
<p>
-The first argument indicates whether the transport process in question
-is being started for a connecting (<c>connect</c>) or listening
-(<c>accept</c>) transport.
+<c>Type</c> indicates whether the transport process in question
+is being started for a connecting (<c>Type=connect</c>) or listening
+(<c>Type=accept</c>) transport.
In the latter case, transport processes are started as required to
-accept connections from multiple peers.
-Ref is in each case the same value that was returned from the
-call to <seealso
-marker="diameter#add_transport">diameter:add_transport/2</seealso>
+accept connections from multiple peers.</p>
+
+<p>
+Ref is the value that was returned from the call to &mod_add_transport;
that has lead to starting of a transport process.</p>
<p>
+<c>Svc</c> contains the capabilities passed to &mod_start_service; and
+&mod_add_transport;, values passed to the latter overriding those
+passed to the former.</p>
+
+<p>
+<c>Config</c> is as passed in <c>transport_config</c> tuple in the
+&mod_transport_opt; list passed to &mod_add_transport;.</p>
+
+<p>
+The start function should use the <c>Host-IP-Address</c> list and/or
+<c>Config</c> to select an appropriate list of local IP addresses,
+and should return this list if different from the
+<c>#diameter_service{}</c> addresses.
+The returned list is used to populate <c>Host-IP-Address</c> AVPs in
+outgoing capabilities exchange messages, the
+<c>#diameter_service{}</c> addresses being used otherwise.</p>
+
+<p>
A transport process must implement the message interface documented below.
It should retain the pid of its parent, monitor the parent and terminate if
it dies.
It should not link to the parent.
It should exit if its transport connection with its peer is lost.</p>
-<p>
-Transport processes for a given service are started sequentially.</p>
-
-<p>
-The start function should use the 'Host-IP-Address' list on the service,
-namely <c>Svc#diameter_service.host_ip_address</c>, and/or
-<c>Opts</c> to select an appropriate list of local IP addresses,
-and should return this list if different from the service addresses.
-The returned list is used to populate 'Host-IP-Address' AVPs in outgoing
-capabilities exchange messages, the service addresses being used
-otherwise.</p>
-
-<marker id="MESSAGES"/>
</desc>
</func>
</funcs>
<!-- ===================================================================== -->
+<marker id="MESSAGES"/>
<section>
<title>MESSAGES</title>
@@ -120,27 +165,24 @@ All messages sent over the transport interface are of the
form <c>{diameter, term()}</c>.</p>
<p>
-A transport process can expect the following messages from
-diameter.</p>
+A transport process can expect messages of the following types from
+its parent.</p>
<taglist>
-<tag><c>{diameter, {send, Packet}}</c></tag>
+<tag><c>{diameter, {send, &message;}}</c></tag>
<item>
<p>
-An outbound Diameter message.
-Packet can be either <c>binary()</c> (the message to be sent)
-or a <c>#diameter_packet{}</c> whose <c>transport_data</c> field
-containes a value other than undefined.</p>
+An outbound Diameter message.</p>
</item>
<tag><c>{diameter, {close, Pid}}</c></tag>
<item>
<p>
-A request to close the transport connection.
-The transport process should terminate after closing the
-connection.
-Pid is the pid() of the parent process.</p>
+A request to terminate the transport process after having received DPA
+in response to DPR.
+The transport process should exit.
+<c>Pid</c> is the pid() of the parent process.</p>
</item>
<tag><c>{diameter, {tls, Ref, Type, Bool}}</c></tag>
@@ -148,17 +190,17 @@ Pid is the pid() of the parent process.</p>
<p>
Indication of whether or not capabilities exchange has selected
inband security using TLS.
-Ref is a reference() that must be included in the
+<c>Ref</c> is a reference() that must be included in the
<c>{diameter, {tls, Ref}}</c> reply message to the transport's
parent process (see below).
-Type is either <c>connect</c> or <c>accept</c> depending on
+<c>Type</c> is either <c>connect</c> or <c>accept</c> depending on
whether the process has been started for a connecting or listening
transport respectively.
-Bool is a boolean() indicating whether or not the transport connection
-should be upgraded to TLS.</p>
+<c>Bool</c> is a boolean() indicating whether or not the transport
+connection should be upgraded to TLS.</p>
<p>
-If TLS is requested (Bool = true) then a connecting process should
+If TLS is requested (<c>Bool=true</c>) then a connecting process should
initiate a TLS handshake with the peer and an accepting process should
prepare to accept a handshake.
A successful handshake should be followed by a <c>{diameter, {tls, Ref}}</c>
@@ -174,7 +216,7 @@ TLS.</p>
</taglist>
<p>
-A transport process should send the following messages
+A transport process should send messages of the following types
to its parent.</p>
<taglist>
@@ -182,41 +224,32 @@ to its parent.</p>
<tag><c>{diameter, {self(), connected}}</c></tag>
<item>
<p>
-Inform the parent that the transport process with Type = accept has
+Inform the parent that the transport process with <c>Type=accept</c> has
established a connection with the peer.
-Not sent if the transport process has Type = connect.</p>
+Not sent if the transport process has <c>Type=connect</c>.</p>
</item>
<tag><c>{diameter, {self(), connected, Remote}}</c></tag>
<item>
<p>
-Inform the parent that the transport process with Type = connect
+Inform the parent that the transport process with <c>Type=connect</c>
has established a connection with a peer.
-Not sent if the transport process has Type = accept.
-Remote is an arbitrary term that uniquely identifies the remote
+Not sent if the transport process has <c>Type=accept</c>.
+<c>Remote</c> is an arbitrary term that uniquely identifies the remote
endpoint to which the transport has connected.</p>
</item>
-<tag><c>{diameter, {recv, Packet}}</c></tag>
+<tag><c>{diameter, {recv, &message;}}</c></tag>
<item>
<p>
-An inbound Diameter message.
-Packet can be either <c>binary()</c> (the message to be sent)
-or <c>#diameter_packet{}</c>
-whose <c>packet</c> field contains a <c>binary()</c>.
-Any value (other than undefined) set in
-the <c>transport_data</c> field will be passed back with a
-corresponding answer message in the case that the inbound message is a
-request unless the sender sets another value.
-How the <c>transport_data</c> is used/interpreted is up to the
-transport module.</p>
+An inbound Diameter message.</p>
</item>
<tag><c>{diameter, {tls, Ref}}</c></tag>
<item>
<p>
Acknowledgment of a successful TLS handshake.
-Ref is the reference() received in the
+<c>Ref</c> is the reference() received in the
<c>{diameter, {tls, Ref, Type, Bool}}</c> message in response
to which the reply is sent.
A transport must exit if a handshake is not successful.</p>
@@ -233,8 +266,7 @@ A transport must exit if a handshake is not successful.</p>
<title>SEE ALSO</title>
<p>
-<seealso marker="diameter_tcp">diameter_tcp(3)</seealso>,
-<seealso marker="diameter_sctp">diameter_sctp(3)</seealso></p>
+&man_tcp;, &man_sctp;</p>
</section>
diff --git a/lib/diameter/doc/src/diameter_using.xml b/lib/diameter/doc/src/diameter_using.xml
index 809b76bdf3..7d9c0cd492 100644
--- a/lib/diameter/doc/src/diameter_using.xml
+++ b/lib/diameter/doc/src/diameter_using.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
-<year>2011</year>
+<year>2011</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
@@ -23,7 +23,7 @@ under the License.
</legalnotice>
-<title>Using the stack</title>
+<title>Usage</title>
<prepared></prepared>
<responsible></responsible>
<docno></docno>
diff --git a/lib/diameter/doc/src/files.mk b/lib/diameter/doc/src/files.mk
index 79d53abceb..00ced3d91e 100644
--- a/lib/diameter/doc/src/files.mk
+++ b/lib/diameter/doc/src/files.mk
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2010-2011. All Rights Reserved.
+# Copyright Ericsson AB 2010-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -26,6 +26,8 @@ XML_REF1_FILES = \
XML_REF3_FILES = \
diameter.xml \
diameter_app.xml \
+ diameter_codec.xml \
+ diameter_make.xml \
diameter_transport.xml \
diameter_tcp.xml \
diameter_sctp.xml
diff --git a/lib/diameter/doc/src/notes.xml b/lib/diameter/doc/src/notes.xml
index 6e364a3200..d241e2bd19 100644
--- a/lib/diameter/doc/src/notes.xml
+++ b/lib/diameter/doc/src/notes.xml
@@ -1,11 +1,17 @@
<?xml version="1.0" encoding="latin1" ?>
-<!DOCTYPE chapter SYSTEM "chapter.dtd">
+<!DOCTYPE chapter SYSTEM "chapter.dtd" [
+ <!ENTITY % also SYSTEM "seealso.ent" >
+ <!ENTITY % here SYSTEM "seehere.ent" >
+ %also;
+ %here;
+]>
<chapter>
<header>
<copyright>
<year>2011</year>
+<year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -36,6 +42,306 @@ first.</p>
<!-- ===================================================================== -->
+<section><title>Diameter 1.3.1</title>
+
+ <section><title>Known Bugs and Problems</title>
+ <list>
+ <item>
+ <p>
+ Fix function clause resulting from use of an eval
+ callback.</p>
+ <p>
+ Own Id: OTP-10685</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Diameter 1.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix faulty handling of Origin-State-Id and faulty config
+ values.</p>
+ <p>
+ The former was expected in a list despite the
+ documentation requiring (correctly) an integer. A bare
+ value for a list-valued capability was not handled.</p>
+ <p>
+ Own Id: OTP-10440</p>
+ </item>
+ <item>
+ <p>
+ Fix timing of up/down events.</p>
+ <p>
+ Previously, a call to diameter:call/4 following a peer_up
+ callback might incorrectly return {error, no_connection},
+ depending on timing. Both events now follow the
+ corresponding callbacks.</p>
+ <p>
+ Own Id: OTP-10459</p>
+ </item>
+ <item>
+ <p>
+ Make diameter:service_info/2 usable in peer_up, peer_down
+ and pick_peer callbacks.</p>
+ <p>
+ Except for in pick_peer when {call_mutates_state, false},
+ it would previously hang indefinitely.</p>
+ <p>
+ Own Id: OTP-10460</p>
+ </item>
+ <item>
+ <p>
+ Verify that End-to-End and Hop-by-Hop Identifiers in an
+ incoming CEA/DPA match those sent in the corresponding
+ CER/DPR.</p>
+ <p>
+ The values were previously ignored. Answers whose
+ identifiers do not match are handled as unexpected.</p>
+ <p>
+ Own Id: OTP-10565</p>
+ </item>
+ <item>
+ <p>
+ Fix formatting problems in PDF documentation.</p>
+ <p>
+ In particular, text corresponding to links in HTML was
+ omitted in preformatted blocks. There are still issues
+ with indentation but this is not diameter-specific.</p>
+ <p>
+ Own Id: OTP-10583</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Let prepare_request, prepare_retransmit and
+ handle_request callbacks return a function to be invoked
+ on outgoing messages after encode.</p>
+ <p>
+ This allows encoded messages to be logged for example.</p>
+ <p>
+ Own Id: OTP-10441</p>
+ </item>
+ <item>
+ <p>
+ Add service_opt() 'restrict_connections' to allow
+ multiple transport connections with the same peer.</p>
+ <p>
+ Own Id: OTP-10443</p>
+ </item>
+ <item>
+ <p>
+ Add service_opt() 'sequence' to allow the masking of a
+ constant onto the topmost bits of End-to-End and
+ Hop-by-Hop identifiers.</p>
+ <p>
+ This allows the same service on different nodes to use
+ distinct values in outgoing request messages.</p>
+ <p>
+ Own Id: OTP-10445</p>
+ </item>
+ <item>
+ <p>
+ Add diameter:service_info(PeerRef) to return the
+ transport_ref() and transport_opt() list of the
+ corresponding transport.</p>
+ <p>
+ This allows easy access to these from diameter_app
+ callbacks that only get peer_ref() as an argument.</p>
+ <p>
+ Own Id: OTP-10470</p>
+ </item>
+ <item>
+ <p>
+ Add reference pages diameter_codec(3) and
+ diameter_make(3).</p>
+ <p>
+ Own Id: OTP-10471</p>
+ </item>
+ <item>
+ <p>
+ Add events for service start and stop.</p>
+ <p>
+ Own Id: OTP-10492</p>
+ </item>
+ <item>
+ <p>
+ Add transport_opt() 'disconnect_cb' to make the sending
+ of DPR configurable.</p>
+ <p>
+ Whether or not DPR should be sent at application stop,
+ service stop or transport removal is determined by the
+ value returned by the callback, as is the
+ Disconnect-Cause and timeout if DPA is not received.</p>
+ <p>
+ Own Id: OTP-10493</p>
+ </item>
+ <item>
+ <p>
+ Add transport_opt() 'capx_timeout' for the timeout
+ associated with non-reception of CER/CEA.</p>
+ <p>
+ Own Id: OTP-10554</p>
+ </item>
+ <item>
+ <p>
+ Allow a handle_request callback to return a
+ #diameter_packet{}.</p>
+ <p>
+ This allows an answer to set transport_data and header
+ fields.</p>
+ <p>
+ Own Id: OTP-10566</p>
+ </item>
+ <item>
+ <p>
+ Update documentation for RFC 6733.</p>
+ <p>
+ RFC 3588 is now obsolete.</p>
+ <p>
+ Own Id: OTP-10568</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Diameter 1.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix broken Result-Code setting and Destination-Host/Realm
+ extraction.</p>
+ <p>
+ Result-Code was assumed to have arity 1 when setting this
+ value in an answer to a request containing AVP decode
+ errors. Destination-Host/Realm were only correctly
+ extracted from messages in the common application.</p>
+ <p>
+ Own Id: OTP-10202</p>
+ </item>
+ <item>
+ <p>
+ Handle insufficient capabilities configuration more
+ gracefully.</p>
+ <p>
+ A transport that does not have sufficient capabilities
+ configuration in order to encode CER/CEA will now emit an
+ error report noting the configuration error and exit
+ instead of failing. The error is not detected at
+ diameter:add_transport/2 since there is no requirement
+ that a service be configured before its transports.</p>
+ <p>
+ Own Id: OTP-10203</p>
+ </item>
+ <item>
+ <p>
+ Ensure a failing peer_up/down callback does not affect
+ transport connections to other peers.</p>
+ <p>
+ Such a failure would previously have taken down all of a
+ service's connections.</p>
+ <p>
+ Own Id: OTP-10215</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Statistics related to Diameter messages can be retrieved
+ using diameter:service_info/2.</p>
+ <p>
+ Both Diameter and socket-level statistics are available,
+ for both incoming and outgoing messages.</p>
+ <p>
+ Own Id: OTP-9608</p>
+ </item>
+ <item>
+ <p>
+ Allow multiple transport_module/config to
+ diameter:add_transport/2.</p>
+ <p>
+ Multiple values are attempted in sequence until one
+ results in an established connection. This provides a way
+ for a connecting transport to specify configuration in
+ order of preference. (For example, SCTP before TCP.)</p>
+ <p>
+ Own Id: OTP-9885</p>
+ </item>
+ <item>
+ <p>
+ Add events for state transitions in the RFC 3539 watchdog
+ state machine.</p>
+ <p>
+ The watchdog state is also available through
+ diameter:service_info/2.</p>
+ <p>
+ Own Id: OTP-10212</p>
+ </item>
+ <item>
+ <p>
+ Add diameter:service_info(SvcName, connections).</p>
+ <p>
+ This provides an alternative to
+ diameter:service_info(SvcName, transport) that presents
+ information per established connection instead of per
+ transport reference.</p>
+ <p>
+ Own Id: OTP-10213</p>
+ </item>
+ <item>
+ <p>
+ Assorted documentation corrections/improvements.</p>
+ <p>
+ Own Id: OTP-10216</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Diameter 1.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix fault in sending of 'closed' events.</p>
+ <p>
+ The fault made it possible for the 'closed' event not to
+ be sent following a failed capabilities exchange.</p>
+ <p>
+ Own Id: OTP-9824</p>
+ </item>
+ <item>
+ <p>
+ Fix faulty diameterc -name/-prefix.</p>
+ <p>
+ A minor blunder when introducing the new dictionary
+ parser in diameter-1.0 broke these options.</p>
+ <p>
+ Own Id: OTP-9826</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Diameter 1.0</title>
<section><title>Fixed Bugs and Malfunctions</title>
@@ -295,8 +601,7 @@ Known issues or limitations:</p>
Some agent-related functionality is not entirely complete.
In particular, support for proxy agents, that advertise specific
Diameter applications but otherwise relay messages in much the same
-way as relay agents (for which a <seealso
-marker="diameter_app#handle_request">handle_request/3</seealso>
+way as relay agents (for which a handle_request
callback can return a <c>relay</c> tuple), will be completed in an
upcoming release.
There may also be more explicit support for redirect agents, although
@@ -328,8 +633,7 @@ could likely be expanded upon.</p>
<item>
<p>
-The function <seealso
-marker="diameter#service_info">diameter:service_info/2</seealso>
+The function diameter:service_info/2
can be used to retrieve information about a started service
(statistics, information about connected peers, etc) but
this is not yet documented and both the input and output may change
diff --git a/lib/diameter/doc/src/ref_man.xml b/lib/diameter/doc/src/ref_man.xml
index 137ce79094..4b99fe716d 100644
--- a/lib/diameter/doc/src/ref_man.xml
+++ b/lib/diameter/doc/src/ref_man.xml
@@ -6,6 +6,7 @@
<header>
<copyright>
<year>2011</year>
+<year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -40,7 +41,9 @@ applications on top of the Diameter protocol. </p>
<xi:include href="diameter.xml"/>
<xi:include href="diameter_compile.xml"/>
<xi:include href="diameter_app.xml"/>
+<xi:include href="diameter_codec.xml"/>
<xi:include href="diameter_dict.xml"/>
+<xi:include href="diameter_make.xml"/>
<xi:include href="diameter_transport.xml"/>
<xi:include href="diameter_tcp.xml"/>
<xi:include href="diameter_sctp.xml"/>
diff --git a/lib/diameter/doc/src/seealso.ent b/lib/diameter/doc/src/seealso.ent
new file mode 100644
index 0000000000..4647c42f85
--- /dev/null
+++ b/lib/diameter/doc/src/seealso.ent
@@ -0,0 +1,132 @@
+<?xml version="1.0" encoding="iso-8859-1" ?>
+
+<!--
+
+%CopyrightBegin%
+
+Copyright Ericsson AB 2012. All Rights Reserved.
+
+The contents of this file are subject to the Erlang Public License,
+Version 1.1, (the "License"); you may not use this file except in
+compliance with the License. You should have received a copy of the
+Erlang Public License along with this software. If not, it can be
+retrieved online at http://www.erlang.org/.
+
+Software distributed under the License is distributed on an "AS IS"
+basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+the License for the specific language governing rights and limitations
+under the License.
+
+%CopyrightEnd%
+
+-->
+
+<!--
+
+Entities for cross references: less to type, easier to read and
+less error prone.
+
+Additional intra-document entities are generated by seehere.sed.
+That each definition is on a single (hideously long) line is
+significant.
+
+-->
+
+<!ENTITY the_rfc 'RFC 6733'>
+
+<!-- diameter -->
+
+<!ENTITY mod_add_transport '<seealso marker="diameter#add_transport-2">diameter:add_transport/2</seealso>'>
+<!ENTITY mod_call '<seealso marker="diameter#call-4">diameter:call/4</seealso>'>
+<!ENTITY mod_origin_state_id '<seealso marker="diameter#origin_state_id-0">diameter:origin_state_id/0</seealso>'>
+<!ENTITY mod_remove_transport '<seealso marker="diameter#remove_transport-2">diameter:remove_transport/2</seealso>'>
+<!ENTITY mod_service_info '<seealso marker="diameter#service_info-2">diameter:service_info/2</seealso>'>
+<!ENTITY mod_services '<seealso marker="diameter#services-0">diameter:services/0</seealso>'>
+<!ENTITY mod_start_service '<seealso marker="diameter#start_service-2">diameter:start_service/2</seealso>'>
+<!ENTITY mod_stop_service '<seealso marker="diameter#stop_service-1">diameter:stop_service/1</seealso>'>
+<!ENTITY mod_subscribe '<seealso marker="diameter#subscribe-1">diameter:subscribe/1</seealso>'>
+
+<!ENTITY mod_application_alias '<seealso marker="diameter#application_alias">diameter:application_alias()</seealso>'>
+<!ENTITY mod_application_module '<seealso marker="diameter#application_module">diameter:application_module()</seealso>'>
+<!ENTITY mod_application_opt '<seealso marker="diameter#application_opt">diameter:application_opt()</seealso>'>
+<!ENTITY mod_call_opt '<seealso marker="diameter#call_opt">diameter:call_opt()</seealso>'>
+<!ENTITY mod_capability '<seealso marker="diameter#capability">diameter:capability()</seealso>'>
+<!ENTITY mod_evaluable '<seealso marker="diameter#evaluable">diameter:evaluable()</seealso>'>
+<!ENTITY mod_peer_filter '<seealso marker="diameter#peer_filter">diameter:peer_filter()</seealso>'>
+<!ENTITY mod_service_event '<seealso marker="diameter#service_event">diameter:service_event()</seealso>'>
+<!ENTITY mod_service_name '<seealso marker="diameter#service_name">diameter:service_name()</seealso>'>
+<!ENTITY mod_service_opt '<seealso marker="diameter#service_opt">diameter:service_opt()</seealso>'>
+<!ENTITY mod_transport_opt '<seealso marker="diameter#transport_opt">diameter:transport_opt()</seealso>'>
+<!ENTITY mod_transport_ref '<seealso marker="diameter#transport_ref">diameter:transport_ref()</seealso>'>
+
+<!ENTITY capabilities_cb '<seealso marker="#capabilities_cb">capabilities_cb</seealso>'>
+<!ENTITY capx_timeout '<seealso marker="#capx_timeout">capx_timeout</seealso>'>
+<!ENTITY disconnect_cb '<seealso marker="#disconnect_cb">disconnect_cb</seealso>'>
+<!ENTITY transport_config '<seealso marker="#transport_config">transport_config</seealso>'>
+<!ENTITY transport_module '<seealso marker="#transport_module">transport_module</seealso>'>
+<!ENTITY reconnect_timer '<seealso marker="#reconnect_timer">reconnect_timer</seealso>'>
+<!ENTITY watchdog_timer '<seealso marker="#watchdog_timer">watchdog_timer</seealso>'>
+
+<!-- diameter_app -->
+
+<!ENTITY app_handle_answer '<seealso marker="diameter_app#Mod:handle_answer-4">handle_answer/4</seealso>'>
+<!ENTITY app_handle_request '<seealso marker="diameter_app#Mod:handle_request-3">handle_request/3</seealso>'>
+<!ENTITY app_handle_error '<seealso marker="diameter_app#Mod:handle_error-4">handle_error/4</seealso>'>
+<!ENTITY app_peer_down '<seealso marker="diameter_app#Mod:peer_down-3">peer_up/3</seealso>'>
+<!ENTITY app_peer_up '<seealso marker="diameter_app#Mod:peer_up-3">peer_up/3</seealso>'>
+<!ENTITY app_pick_peer '<seealso marker="diameter_app#Mod:pick_peer-4">pick_peer/4</seealso>'>
+<!ENTITY app_prepare_retransmit '<seealso marker="diameter_app#Mod:prepare_retransmit-3">prepare_retransmit/3</seealso>'>
+<!ENTITY app_prepare_request '<seealso marker="diameter_app#Mod:prepare_request-3">prepare_request/3</seealso>'>
+
+<!ENTITY app_capabilities '<seealso marker="diameter_app#capabilities">diameter_app:capabilities()</seealso>'>
+<!ENTITY app_peer '<seealso marker="diameter_app#peer">diameter_app:peer()</seealso>'>
+<!ENTITY app_peer_ref '<seealso marker="diameter_app#peer_ref">diameter_app:peer_ref()</seealso>'>
+<!ENTITY app_state '<seealso marker="diameter_app#state">diameter_app:state()</seealso>'>
+
+<!-- diameter_codec -->
+
+<!ENTITY codec_encode '<seealso marker="diameter_codec#encode-2">diameter_codec:encode/2</seealso>'>
+<!ENTITY codec_decode '<seealso marker="diameter_codec#decode-2">diameter_codec:decode/2</seealso>'>
+
+<!ENTITY codec_avp '<seealso marker="diameter_codec#avp">diameter_codec:avp()</seealso>'>
+<!ENTITY codec_header '<seealso marker="diameter_codec#header">diameter_codec:header()</seealso>'>
+<!ENTITY codec_dictionary '<seealso marker="diameter_codec#dictionary">diameter_codec:dictionary()</seealso>'>
+<!ENTITY codec_message '<seealso marker="diameter_codec#message">diameter_codec:message()</seealso>'>
+<!ENTITY codec_packet '<seealso marker="diameter_codec#packet">diameter_codec:packet()</seealso>'>
+
+<!-- diameter_dict -->
+
+<!ENTITY dict_data_types '<seealso marker="diameter_dict#DATA_TYPES">diameter_dict(4)</seealso>'>
+
+<!ENTITY dict_Address '<seealso marker="diameter_dict#DATA_TYPES">Address()</seealso>'>
+<!ENTITY dict_DiameterIdentity '<seealso marker="diameter_dict#DATA_TYPES">DiameterIdentity()</seealso>'>
+<!ENTITY dict_Grouped '<seealso marker="diameter_dict#DATA_TYPES">Grouped()</seealso>'>
+<!ENTITY dict_OctetString '<seealso marker="diameter_dict#DATA_TYPES">OctetString()</seealso>'>
+<!ENTITY dict_Time '<seealso marker="diameter_dict#DATA_TYPES">Time()</seealso>'>
+<!ENTITY dict_UTF8String '<seealso marker="diameter_dict#DATA_TYPES">UTF8String()</seealso>'>
+<!ENTITY dict_Unsigned32 '<seealso marker="diameter_dict#DATA_TYPES">Unsigned32()</seealso>'>
+
+<!ENTITY dict_name '<seealso marker="diameter_dict#name">@name</seealso>'>
+<!ENTITY dict_prefix '<seealso marker="diameter_dict#prefix">@prefix</seealso>'>
+<!ENTITY dict_inherits '<seealso marker="diameter_dict#inherits">@inherits</seealso>'>
+
+<!-- diameter_make -->
+
+<!ENTITY make_codec '<seealso marker="diameter_make#codec-2">diameter_make:codec/2</seealso>'>
+
+<!-- diameter_transport -->
+
+<!ENTITY transport_start
+ '<seealso marker="diameter_transport#Mod:start-3">start/3</seealso>'>
+
+<!-- reference pages -->
+
+<!ENTITY man_compile '<seealso marker="diameterc">diameterc(1)</seealso>'>
+<!ENTITY man_main '<seealso marker="diameter">diameter(3)</seealso>'>
+<!ENTITY man_app '<seealso marker="diameter_app">diameter_app(3)</seealso>'>
+<!ENTITY man_codec '<seealso marker="diameter_codec">diameter_codec(3)</seealso>'>
+<!ENTITY man_dict '<seealso marker="diameter_dict">diameter_dict(4)</seealso>'>
+<!ENTITY man_make '<seealso marker="diameter_make">diameter_make(3)</seealso>'>
+<!ENTITY man_transport '<seealso marker="diameter_transport">diameter_transport(3)</seealso>'>
+<!ENTITY man_sctp '<seealso marker="diameter_sctp">diameter_sctp(3)</seealso>'>
+<!ENTITY man_tcp '<seealso marker="diameter_tcp">diameter_tcp(3)</seealso>'>
diff --git a/lib/diameter/doc/src/seehere.sed b/lib/diameter/doc/src/seehere.sed
new file mode 100644
index 0000000000..c62a783d40
--- /dev/null
+++ b/lib/diameter/doc/src/seehere.sed
@@ -0,0 +1,35 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2012. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+
+#
+# Map entities for inter-document references to ones for
+# intra-document references like this:
+#
+# <!ENTITY aaa_xxx '<seealso marker="bbb#yyy">ccc:zzz</seealso>'>
+#
+# ===>
+#
+# <!ENTITY xxx '<seealso marker="#yyy">zzz</seealso>'>
+#
+
+/<!ENTITY/!d
+/#/!d
+/"#/d
+s@ [^_]*_@ @
+s@"[^#]*#@"#@
+s@>[^:]*:@>@
diff --git a/lib/diameter/doc/standard/draft-ietf-dime-capablities-update-07.txt b/lib/diameter/doc/standard/draft-ietf-dime-capablities-update-07.txt
deleted file mode 100644
index bb7ec2d08c..0000000000
--- a/lib/diameter/doc/standard/draft-ietf-dime-capablities-update-07.txt
+++ /dev/null
@@ -1,392 +0,0 @@
-
-
-
-Network Working Group K. Jiao
-Internet-Draft Huawei
-Intended status: Standards Track G. Zorn
-Expires: April 27, 2011 Network Zen
- October 24, 2010
-
-
- The Diameter Capabilities Update Application
- draft-ietf-dime-capablities-update-07
-
-Abstract
-
- This document defines a new Diameter application and associated
- command codes. The Capabilities Update application is intended to
- allow the dynamic update of certain Diameter peer capabilities while
- the peer-to-peer connection is in the open state.
-
-Status of this Memo
-
- This Internet-Draft is submitted in full conformance with the
- provisions of BCP 78 and BCP 79.
-
- Internet-Drafts are working documents of the Internet Engineering
- Task Force (IETF). Note that other groups may also distribute
- working documents as Internet-Drafts. The list of current Internet-
- Drafts is at http://datatracker.ietf.org/drafts/current/.
-
- Internet-Drafts are draft documents valid for a maximum of six months
- and may be updated, replaced, or obsoleted by other documents at any
- time. It is inappropriate to use Internet-Drafts as reference
- material or to cite them other than as "work in progress."
-
- This Internet-Draft will expire on April 27, 2011.
-
-Copyright Notice
-
- Copyright (c) 2010 IETF Trust and the persons identified as the
- document authors. All rights reserved.
-
- This document is subject to BCP 78 and the IETF Trust's Legal
- Provisions Relating to IETF Documents
- (http://trustee.ietf.org/license-info) in effect on the date of
- publication of this document. Please review these documents
- carefully, as they describe your rights and restrictions with respect
- to this document. Code Components extracted from this document must
- include Simplified BSD License text as described in Section 4.e of
- the Trust Legal Provisions and are provided without warranty as
- described in the Simplified BSD License.
-
-
-
-Jiao & Zorn Expires April 27, 2011 [Page 1]
-
-Internet-Draft Diameter Capabilities Update October 2010
-
-
-Table of Contents
-
- 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 3
- 2. Specification of Requirements . . . . . . . . . . . . . . . . . 3
- 3. Diameter Protocol Considerations . . . . . . . . . . . . . . . 3
- 4. Capabilities Update . . . . . . . . . . . . . . . . . . . . . . 3
- 4.1. Command-Code Values . . . . . . . . . . . . . . . . . . . . 4
- 4.1.1. Capabilities-Update-Request . . . . . . . . . . . . . . 5
- 4.1.2. Capabilities-Update-Answer . . . . . . . . . . . . . . 5
- 5. Security Considerations . . . . . . . . . . . . . . . . . . . . 6
- 6. IANA Considerations . . . . . . . . . . . . . . . . . . . . . . 6
- 6.1. Application Identifier . . . . . . . . . . . . . . . . . . 6
- 6.2. Command Codes . . . . . . . . . . . . . . . . . . . . . . . 6
- 7. Contributors . . . . . . . . . . . . . . . . . . . . . . . . . 6
- 8. Acknowledgements . . . . . . . . . . . . . . . . . . . . . . . 6
- 9. References . . . . . . . . . . . . . . . . . . . . . . . . . . 6
- 9.1. Normative References . . . . . . . . . . . . . . . . . . . 6
- 9.2. Informative References . . . . . . . . . . . . . . . . . . 7
- Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . . 7
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Jiao & Zorn Expires April 27, 2011 [Page 2]
-
-Internet-Draft Diameter Capabilities Update October 2010
-
-
-1. Introduction
-
- Capabilities exchange is an important component of the Diameter Base
- Protocol [I-D.ietf-dime-rfc3588bis], allowing peers to exchange
- identities and Diameter capabilities (protocol version number,
- supported Diameter applications, security mechanisms, etc.). As
- defined in RFC 3588, however, the capabilities exchange process takes
- place only once, at the inception of a transport connection between a
- given pair of peers. Therefore, if a peer's capabilities change (due
- to software update, for example), the existing connection(s) must be
- torn down (along with all of the associated user sessions) and
- restarted before the modified capabilities can be advertised.
-
- This document defines a new Diameter application intended to allow
- the dynamic update of a subset of Diameter peer capabilities over an
- existing connection. Because the Capabilities Update application
- specified herein operates over an existing transport connection,
- modification of certain capabilities is prohibited. Specifically,
- modifying the security mechanism in use is not allowed; if the
- security method used between a pair of peers is changed the affected
- connection MUST be restarted.
-
-
-2. Specification of Requirements
-
- The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
- "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
- document are to be interpreted as described in RFC 2119 [RFC2119].
-
-
-3. Diameter Protocol Considerations
-
- This section details the relationship of the Diameter Capabilities
- Update application to the Diameter Base Protocol.
-
- This document specifies Diameter Application-ID <TBD1>. Diameter
- nodes conforming to this specification MUST advertise support by
- including the value <TBD1> in the Auth-Application-Id of the
- Capabilities-Exchange-Request (CER) and Capabilities-Exchange-Answer
- (CEA) commands [I-D.ietf-dime-rfc3588bis].
-
-
-4. Capabilities Update
-
- When the capabilities of a Diameter node conforming to this
- specification change, it MUST notify all of the nodes with which it
- has an open transport connection and which have also advertised
- support for the Capabilities Update application using the
-
-
-
-Jiao & Zorn Expires April 27, 2011 [Page 3]
-
-Internet-Draft Diameter Capabilities Update October 2010
-
-
- Capabilities-Update-Request (CUR) message (Section 4.1.1). This
- message allows the update of a peer's capabilities (supported
- Diameter applications, etc.).
-
- A Diameter node only issues a given command to those peers that have
- advertised support for the Diameter application that defines the
- command; a Diameter node must cache the supported applications in
- order to ensure that unrecognized commands and/or Attribute-Value
- Pairs (AVPs) are not unnecessarily sent to a peer.
-
- The receiver of the CUR MUST determine common applications by
- computing the intersection of its own set of supported Application Id
- against all of the application identifier AVPs (Auth-Application-Id,
- Acct-Application-Id and Vendor-Specific- Application-Id) present in
- the CUR. The value of the Vendor-Id AVP in the Vendor-Specific-
- Application-Id MUST NOT be used during computation.
-
- If the receiver of a CUR does not have any applications in common
- with the sender then it MUST return a Capabilities-Update-Answer
- (CUA) (Section 4.1.2) with the Result-Code AVP set to
- DIAMETER_NO_COMMON_APPLICATION [I-D.ietf-dime-rfc3588bis], and SHOULD
- disconnect the transport layer connection; however, if active
- sessions are using the connection, peers MAY delay disconnection
- until the sessions can be redirected or gracefully terminated. Note
- that receiving a CUA from a peer advertising itself as a Relay (see
- [I-D.ietf-dime-rfc3588bis], Section 2.4) MUST be interpreted as
- having common applications with the peer.
-
- As for CER/CEA messages, the CUR and CUA messages MUST NOT be
- proxied, redirected or relayed.
-
- Even though the CUR/CUA messages cannot be proxied, it is still
- possible for an upstream agent to receive a message for which there
- are no peers available to handle the application that corresponds to
- the Command-Code. This could happen if, for example, the peers are
- too busy or down. In such instances, the 'E' bit MUST be set in the
- answer message with the Result-Code AVP set to
- DIAMETER_UNABLE_TO_DELIVER to inform the downstream peer to take
- action (e.g., re-routing requests to an alternate peer).
-
-4.1. Command-Code Values
-
- This section defines Command-Code [I-D.ietf-dime-rfc3588bis] values
- that MUST be supported by all Diameter implementations conforming to
- this specification. The following Command Codes are defined (using
- modified ABNF [I-D.ietf-dime-rfc3588bis]) in this document:
- Capabilities-Update-Request (CUR, Section 4.1.1) and Capabilities-
- Update-Answer (CUA, Section 4.1.2).
-
-
-
-Jiao & Zorn Expires April 27, 2011 [Page 4]
-
-Internet-Draft Diameter Capabilities Update October 2010
-
-
-4.1.1. Capabilities-Update-Request
-
- The Capabilities-Update-Request (CUR), indicated by the Command-Code
- set to <CUCC> and the Command Flags' 'R' bit set, is sent to update
- local capabilities. Upon detection of a transport failure, this
- message MUST NOT be sent to an alternate peer.
-
- When Diameter is run over the Stream Control Transmission Protocol
- [RFC4960], which allows connections to span multiple interfaces and
- multiple IP addresses, the Capabilities-Update-Request message MUST
- contain one Host-IP-Address AVP for each potential IP address that
- may be locally used when transmitting Diameter messages.
-
- Message Format
-
- <CUR> ::= < Diameter Header: <CUCC>, REQ >
- { Origin-Host }
- { Origin-Realm }
- 1* { Host-IP-Address }
- { Vendor-Id }
- { Product-Name }
- [ Origin-State-Id ]
- * [ Supported-Vendor-Id ]
- * [ Auth-Application-Id ]
- * [ Acct-Application-Id ]
- * [ Vendor-Specific-Application-Id ]
- [ Firmware-Revision ]
- * [ AVP ]
-
-4.1.2. Capabilities-Update-Answer
-
- The Capabilities-Update-Answer, indicated by the Command-Code set to
- <CUCC> and the Command Flags' 'R' bit cleared, is sent in response to
- a CUR message.
-
- Message Format
-
- <CUA> ::= < Diameter Header: <CUCC> >
- { Origin-Host }
- { Origin-Realm }
- { Result-Code }
- [ Error-Message ]
- * [ AVP ]
-
-
-
-
-
-
-
-
-Jiao & Zorn Expires April 27, 2011 [Page 5]
-
-Internet-Draft Diameter Capabilities Update October 2010
-
-
-5. Security Considerations
-
- The security considerations applicable to the Diameter Base Protocol
- [I-D.ietf-dime-rfc3588bis] are also applicable to this document.
-
-
-6. IANA Considerations
-
- This section explains the criteria to be used by the IANA for
- assignment of numbers within namespaces used within this document.
-
-6.1. Application Identifier
-
- This specification assigns the value <TBD1> from the Application
- Identifiers namespace [I-D.ietf-dime-rfc3588bis]. See Section 3 for
- the assignment of the namespace in this specification.
-
-6.2. Command Codes
-
- This specification assigns the value <CUCC> from the Command Codes
- namespace [I-D.ietf-dime-rfc3588bis]. See Section 4.1 for the
- assignment of the namespace in this specification.
-
-
-7. Contributors
-
- This document is based upon work done by Tina Tsou.
-
-
-8. Acknowledgements
-
- Thanks to Sebastien Decugis, Niklas Neumann, Subash Comerica, Lionel
- Morand, Dan Romascanu, Dan Harkins and Ravi for helpful review and
- discussion.
-
-
-9. References
-
-9.1. Normative References
-
- [I-D.ietf-dime-rfc3588bis]
- Fajardo, V., Arkko, J., Loughney, J., and G. Zorn,
- "Diameter Base Protocol", draft-ietf-dime-rfc3588bis-25
- (work in progress), September 2010.
-
- [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
- Requirement Levels", BCP 14, RFC 2119, March 1997.
-
-
-
-
-Jiao & Zorn Expires April 27, 2011 [Page 6]
-
-Internet-Draft Diameter Capabilities Update October 2010
-
-
-9.2. Informative References
-
- [RFC4960] Stewart, R., "Stream Control Transmission Protocol",
- RFC 4960, September 2007.
-
-
-Authors' Addresses
-
- Jiao Kang
- Huawei Technologies
- Section B1, Huawei Industrial Base
- Bantian, Longgang District
- Shenzhen 518129
- P.R. China
-
- Phone: +86 755 2878-6690
-
-
- Glen Zorn
- Network Zen
- 227/358 Thanon Sanphawut
- Bang Na, Bangkok 10260
- Thailand
-
- Phone: +66 (0) 87-040-4617
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Jiao & Zorn Expires April 27, 2011 [Page 7]
-
diff --git a/lib/diameter/doc/standard/draft-ietf-dime-rfc3588bis-26.txt b/lib/diameter/doc/standard/rfc6733.txt
index 87b9562f93..2f5a477347 100644
--- a/lib/diameter/doc/standard/draft-ietf-dime-rfc3588bis-26.txt
+++ b/lib/diameter/doc/standard/rfc6733.txt
@@ -1,62 +1,72 @@
-DIME V. Fajardo, Ed.
-Internet-Draft Telcordia Technologies
-Obsoletes: 3588 (if approved) J. Arkko
-Intended status: Standards Track Ericsson Research
-Expires: July 24, 2011 J. Loughney
+
+
+
+Internet Engineering Task Force (IETF) V. Fajardo, Ed.
+Request for Comments: 6733 Telcordia Technologies
+Obsoletes: 3588, 5719 J. Arkko
+Category: Standards Track Ericsson Research
+ISSN: 2070-1721 J. Loughney
Nokia Research Center
- G. Zorn
+ G. Zorn, Ed.
Network Zen
- January 20, 2011
+ October 2012
Diameter Base Protocol
- draft-ietf-dime-rfc3588bis-26.txt
Abstract
The Diameter base protocol is intended to provide an Authentication,
- Authorization and Accounting (AAA) framework for applications such as
- network access or IP mobility in both local and roaming situations.
- This document specifies the message format, transport, error
- reporting, accounting and security services used by all Diameter
- applications. The Diameter base protocol as defined in this document
- must be supported by all Diameter implementations.
+ Authorization, and Accounting (AAA) framework for applications such
+ as network access or IP mobility in both local and roaming
+ situations. This document specifies the message format, transport,
+ error reporting, accounting, and security services used by all
+ Diameter applications. The Diameter base protocol as defined in this
+ document obsoletes RFC 3588 and RFC 5719, and it must be supported by
+ all new Diameter implementations.
-Status of this Memo
+Status of This Memo
- This Internet-Draft is submitted in full conformance with the
- provisions of BCP 78 and BCP 79.
+ This is an Internet Standards Track document.
- Internet-Drafts are working documents of the Internet Engineering
- Task Force (IETF). Note that other groups may also distribute
- working documents as Internet-Drafts. The list of current Internet-
- Drafts is at http://datatracker.ietf.org/drafts/current/.
+ This document is a product of the Internet Engineering Task Force
+ (IETF). It represents the consensus of the IETF community. It has
+ received public review and has been approved for publication by the
+ Internet Engineering Steering Group (IESG). Further information on
+ Internet Standards is available in Section 2 of RFC 5741.
+
+ Information about the current status of this document, any errata,
+ and how to provide feedback on it may be obtained at
+ http://www.rfc-editor.org/info/rfc6733.
- Internet-Drafts are draft documents valid for a maximum of six months
- and may be updated, replaced, or obsoleted by other documents at any
- time. It is inappropriate to use Internet-Drafts as reference
- material or to cite them other than as "work in progress."
- This Internet-Draft will expire on July 24, 2011.
-Copyright Notice
- Copyright (c) 2011 IETF Trust and the persons identified as the
- document authors. All rights reserved.
- This document is subject to BCP 78 and the IETF Trust's Legal
- Provisions Relating to IETF Documents
-Fajardo, et al. Expires July 24, 2011 [Page 1]
+
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 1]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
+Copyright Notice
+
+ Copyright (c) 2012 IETF Trust and the persons identified as the
+ document authors. All rights reserved.
+
+ This document is subject to BCP 78 and the IETF Trust's Legal
+ Provisions Relating to IETF Documents
(http://trustee.ietf.org/license-info) in effect on the date of
publication of this document. Please review these documents
carefully, as they describe your rights and restrictions with respect
@@ -65,256 +75,303 @@ Internet-Draft Diameter Base Protocol January 2011
the Trust Legal Provisions and are provided without warranty as
described in the Simplified BSD License.
+ This document may contain material from IETF Documents or IETF
+ Contributions published or made publicly available before November
+ 10, 2008. The person(s) controlling the copyright in some of this
+ material may not have granted the IETF Trust the right to allow
+ modifications of such material outside the IETF Standards Process.
+ Without obtaining an adequate license from the person(s) controlling
+ the copyright in such materials, this document may not be modified
+ outside the IETF Standards Process, and derivative works of it may
+ not be created outside the IETF Standards Process, except to format
+ it for publication as an RFC or to translate it into languages other
+ than English.
Table of Contents
- 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 6
- 1.1. Diameter Protocol . . . . . . . . . . . . . . . . . . . . 8
- 1.1.1. Description of the Document Set . . . . . . . . . . . 9
- 1.1.2. Conventions Used in This Document . . . . . . . . . . 10
- 1.1.3. Changes from RFC3588 . . . . . . . . . . . . . . . . 10
- 1.2. Terminology . . . . . . . . . . . . . . . . . . . . . . . 12
- 1.3. Approach to Extensibility . . . . . . . . . . . . . . . . 17
- 1.3.1. Defining New AVP Values . . . . . . . . . . . . . . . 18
- 1.3.2. Creating New AVPs . . . . . . . . . . . . . . . . . . 18
- 1.3.3. Creating New Commands . . . . . . . . . . . . . . . . 18
- 1.3.4. Creating New Diameter Applications . . . . . . . . . 19
- 2. Protocol Overview . . . . . . . . . . . . . . . . . . . . . . 21
- 2.1. Transport . . . . . . . . . . . . . . . . . . . . . . . . 22
- 2.1.1. SCTP Guidelines . . . . . . . . . . . . . . . . . . . 23
- 2.2. Securing Diameter Messages . . . . . . . . . . . . . . . 24
- 2.3. Diameter Application Compliance . . . . . . . . . . . . . 24
- 2.4. Application Identifiers . . . . . . . . . . . . . . . . . 24
- 2.5. Connections vs. Sessions . . . . . . . . . . . . . . . . 25
- 2.6. Peer Table . . . . . . . . . . . . . . . . . . . . . . . 26
- 2.7. Routing Table . . . . . . . . . . . . . . . . . . . . . . 27
- 2.8. Role of Diameter Agents . . . . . . . . . . . . . . . . . 28
- 2.8.1. Relay Agents . . . . . . . . . . . . . . . . . . . . 29
- 2.8.2. Proxy Agents . . . . . . . . . . . . . . . . . . . . 30
- 2.8.3. Redirect Agents . . . . . . . . . . . . . . . . . . . 31
- 2.8.4. Translation Agents . . . . . . . . . . . . . . . . . 32
- 2.9. Diameter Path Authorization . . . . . . . . . . . . . . . 33
- 3. Diameter Header . . . . . . . . . . . . . . . . . . . . . . . 35
- 3.1. Command Codes . . . . . . . . . . . . . . . . . . . . . . 38
- 3.2. Command Code ABNF specification . . . . . . . . . . . . . 38
- 3.3. Diameter Command Naming Conventions . . . . . . . . . . . 41
- 4. Diameter AVPs . . . . . . . . . . . . . . . . . . . . . . . . 42
- 4.1. AVP Header . . . . . . . . . . . . . . . . . . . . . . . 42
- 4.1.1. Optional Header Elements . . . . . . . . . . . . . . 43
- 4.2. Basic AVP Data Formats . . . . . . . . . . . . . . . . . 44
- 4.3. Derived AVP Data Formats . . . . . . . . . . . . . . . . 45
- 4.3.1. Common Derived AVPs . . . . . . . . . . . . . . . . . 45
- 4.4. Grouped AVP Values . . . . . . . . . . . . . . . . . . . 52
-
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 2]
-
-Internet-Draft Diameter Base Protocol January 2011
-
-
- 4.4.1. Example AVP with a Grouped Data type . . . . . . . . 53
- 4.5. Diameter Base Protocol AVPs . . . . . . . . . . . . . . . 56
- 5. Diameter Peers . . . . . . . . . . . . . . . . . . . . . . . 59
- 5.1. Peer Connections . . . . . . . . . . . . . . . . . . . . 59
- 5.2. Diameter Peer Discovery . . . . . . . . . . . . . . . . . 60
- 5.3. Capabilities Exchange . . . . . . . . . . . . . . . . . . 61
- 5.3.1. Capabilities-Exchange-Request . . . . . . . . . . . . 63
- 5.3.2. Capabilities-Exchange-Answer . . . . . . . . . . . . 63
- 5.3.3. Vendor-Id AVP . . . . . . . . . . . . . . . . . . . . 64
- 5.3.4. Firmware-Revision AVP . . . . . . . . . . . . . . . . 64
- 5.3.5. Host-IP-Address AVP . . . . . . . . . . . . . . . . . 64
- 5.3.6. Supported-Vendor-Id AVP . . . . . . . . . . . . . . . 65
- 5.3.7. Product-Name AVP . . . . . . . . . . . . . . . . . . 65
- 5.4. Disconnecting Peer connections . . . . . . . . . . . . . 65
- 5.4.1. Disconnect-Peer-Request . . . . . . . . . . . . . . . 66
- 5.4.2. Disconnect-Peer-Answer . . . . . . . . . . . . . . . 66
- 5.4.3. Disconnect-Cause AVP . . . . . . . . . . . . . . . . 66
- 5.5. Transport Failure Detection . . . . . . . . . . . . . . . 67
- 5.5.1. Device-Watchdog-Request . . . . . . . . . . . . . . . 67
- 5.5.2. Device-Watchdog-Answer . . . . . . . . . . . . . . . 67
- 5.5.3. Transport Failure Algorithm . . . . . . . . . . . . . 68
- 5.5.4. Failover and Failback Procedures . . . . . . . . . . 68
- 5.6. Peer State Machine . . . . . . . . . . . . . . . . . . . 69
- 5.6.1. Incoming connections . . . . . . . . . . . . . . . . 71
- 5.6.2. Events . . . . . . . . . . . . . . . . . . . . . . . 72
- 5.6.3. Actions . . . . . . . . . . . . . . . . . . . . . . . 73
- 5.6.4. The Election Process . . . . . . . . . . . . . . . . 75
- 6. Diameter message processing . . . . . . . . . . . . . . . . . 76
- 6.1. Diameter Request Routing Overview . . . . . . . . . . . . 76
- 6.1.1. Originating a Request . . . . . . . . . . . . . . . . 77
- 6.1.2. Sending a Request . . . . . . . . . . . . . . . . . . 77
- 6.1.3. Receiving Requests . . . . . . . . . . . . . . . . . 78
- 6.1.4. Processing Local Requests . . . . . . . . . . . . . . 78
- 6.1.5. Request Forwarding . . . . . . . . . . . . . . . . . 78
- 6.1.6. Request Routing . . . . . . . . . . . . . . . . . . . 78
- 6.1.7. Predictive Loop Avoidance . . . . . . . . . . . . . . 79
- 6.1.8. Redirecting Requests . . . . . . . . . . . . . . . . 79
- 6.1.9. Relaying and Proxying Requests . . . . . . . . . . . 80
- 6.2. Diameter Answer Processing . . . . . . . . . . . . . . . 82
- 6.2.1. Processing received Answers . . . . . . . . . . . . . 82
- 6.2.2. Relaying and Proxying Answers . . . . . . . . . . . . 82
- 6.3. Origin-Host AVP . . . . . . . . . . . . . . . . . . . . . 83
- 6.4. Origin-Realm AVP . . . . . . . . . . . . . . . . . . . . 83
- 6.5. Destination-Host AVP . . . . . . . . . . . . . . . . . . 83
- 6.6. Destination-Realm AVP . . . . . . . . . . . . . . . . . . 84
- 6.7. Routing AVPs . . . . . . . . . . . . . . . . . . . . . . 84
- 6.7.1. Route-Record AVP . . . . . . . . . . . . . . . . . . 84
- 6.7.2. Proxy-Info AVP . . . . . . . . . . . . . . . . . . . 84
-
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 3]
-
-Internet-Draft Diameter Base Protocol January 2011
-
-
- 6.7.3. Proxy-Host AVP . . . . . . . . . . . . . . . . . . . 85
- 6.7.4. Proxy-State AVP . . . . . . . . . . . . . . . . . . . 85
- 6.8. Auth-Application-Id AVP . . . . . . . . . . . . . . . . . 85
- 6.9. Acct-Application-Id AVP . . . . . . . . . . . . . . . . . 85
- 6.10. Inband-Security-Id AVP . . . . . . . . . . . . . . . . . 85
- 6.11. Vendor-Specific-Application-Id AVP . . . . . . . . . . . 86
- 6.12. Redirect-Host AVP . . . . . . . . . . . . . . . . . . . . 87
- 6.13. Redirect-Host-Usage AVP . . . . . . . . . . . . . . . . . 87
- 6.14. Redirect-Max-Cache-Time AVP . . . . . . . . . . . . . . . 88
- 7. Error Handling . . . . . . . . . . . . . . . . . . . . . . . 90
- 7.1. Result-Code AVP . . . . . . . . . . . . . . . . . . . . . 92
- 7.1.1. Informational . . . . . . . . . . . . . . . . . . . . 92
- 7.1.2. Success . . . . . . . . . . . . . . . . . . . . . . . 93
- 7.1.3. Protocol Errors . . . . . . . . . . . . . . . . . . . 93
- 7.1.4. Transient Failures . . . . . . . . . . . . . . . . . 94
- 7.1.5. Permanent Failures . . . . . . . . . . . . . . . . . 95
- 7.2. Error Bit . . . . . . . . . . . . . . . . . . . . . . . . 98
- 7.3. Error-Message AVP . . . . . . . . . . . . . . . . . . . . 99
- 7.4. Error-Reporting-Host AVP . . . . . . . . . . . . . . . . 99
- 7.5. Failed-AVP AVP . . . . . . . . . . . . . . . . . . . . . 99
- 7.6. Experimental-Result AVP . . . . . . . . . . . . . . . . . 100
- 7.7. Experimental-Result-Code AVP . . . . . . . . . . . . . . 100
- 8. Diameter User Sessions . . . . . . . . . . . . . . . . . . . 101
- 8.1. Authorization Session State Machine . . . . . . . . . . . 102
- 8.2. Accounting Session State Machine . . . . . . . . . . . . 107
- 8.3. Server-Initiated Re-Auth . . . . . . . . . . . . . . . . 112
- 8.3.1. Re-Auth-Request . . . . . . . . . . . . . . . . . . . 112
- 8.3.2. Re-Auth-Answer . . . . . . . . . . . . . . . . . . . 113
- 8.4. Session Termination . . . . . . . . . . . . . . . . . . . 114
- 8.4.1. Session-Termination-Request . . . . . . . . . . . . . 115
- 8.4.2. Session-Termination-Answer . . . . . . . . . . . . . 115
- 8.5. Aborting a Session . . . . . . . . . . . . . . . . . . . 116
- 8.5.1. Abort-Session-Request . . . . . . . . . . . . . . . . 116
- 8.5.2. Abort-Session-Answer . . . . . . . . . . . . . . . . 117
- 8.6. Inferring Session Termination from Origin-State-Id . . . 118
- 8.7. Auth-Request-Type AVP . . . . . . . . . . . . . . . . . . 118
- 8.8. Session-Id AVP . . . . . . . . . . . . . . . . . . . . . 119
- 8.9. Authorization-Lifetime AVP . . . . . . . . . . . . . . . 120
- 8.10. Auth-Grace-Period AVP . . . . . . . . . . . . . . . . . . 121
- 8.11. Auth-Session-State AVP . . . . . . . . . . . . . . . . . 121
- 8.12. Re-Auth-Request-Type AVP . . . . . . . . . . . . . . . . 121
- 8.13. Session-Timeout AVP . . . . . . . . . . . . . . . . . . . 122
- 8.14. User-Name AVP . . . . . . . . . . . . . . . . . . . . . . 122
- 8.15. Termination-Cause AVP . . . . . . . . . . . . . . . . . . 122
- 8.16. Origin-State-Id AVP . . . . . . . . . . . . . . . . . . . 124
- 8.17. Session-Binding AVP . . . . . . . . . . . . . . . . . . . 124
- 8.18. Session-Server-Failover AVP . . . . . . . . . . . . . . . 125
- 8.19. Multi-Round-Time-Out AVP . . . . . . . . . . . . . . . . 126
-
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 4]
-
-Internet-Draft Diameter Base Protocol January 2011
-
-
- 8.20. Class AVP . . . . . . . . . . . . . . . . . . . . . . . . 126
- 8.21. Event-Timestamp AVP . . . . . . . . . . . . . . . . . . . 126
- 9. Accounting . . . . . . . . . . . . . . . . . . . . . . . . . 127
- 9.1. Server Directed Model . . . . . . . . . . . . . . . . . . 127
- 9.2. Protocol Messages . . . . . . . . . . . . . . . . . . . . 128
- 9.3. Accounting Application Extension and Requirements . . . . 128
- 9.4. Fault Resilience . . . . . . . . . . . . . . . . . . . . 129
- 9.5. Accounting Records . . . . . . . . . . . . . . . . . . . 129
- 9.6. Correlation of Accounting Records . . . . . . . . . . . . 130
- 9.7. Accounting Command-Codes . . . . . . . . . . . . . . . . 131
- 9.7.1. Accounting-Request . . . . . . . . . . . . . . . . . 131
- 9.7.2. Accounting-Answer . . . . . . . . . . . . . . . . . . 132
- 9.8. Accounting AVPs . . . . . . . . . . . . . . . . . . . . . 133
- 9.8.1. Accounting-Record-Type AVP . . . . . . . . . . . . . 133
- 9.8.2. Acct-Interim-Interval AVP . . . . . . . . . . . . . . 134
- 9.8.3. Accounting-Record-Number AVP . . . . . . . . . . . . 135
- 9.8.4. Acct-Session-Id AVP . . . . . . . . . . . . . . . . . 135
- 9.8.5. Acct-Multi-Session-Id AVP . . . . . . . . . . . . . . 135
- 9.8.6. Accounting-Sub-Session-Id AVP . . . . . . . . . . . . 135
- 9.8.7. Accounting-Realtime-Required AVP . . . . . . . . . . 136
- 10. AVP Occurrence Table . . . . . . . . . . . . . . . . . . . . 137
- 10.1. Base Protocol Command AVP Table . . . . . . . . . . . . . 137
- 10.2. Accounting AVP Table . . . . . . . . . . . . . . . . . . 138
- 11. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 140
- 11.1. Changes to AVP Header Allocation . . . . . . . . . . . . 140
- 11.2. Diameter Header . . . . . . . . . . . . . . . . . . . . . 140
- 11.3. AVP Values . . . . . . . . . . . . . . . . . . . . . . . 140
- 11.3.1. Experimental-Result-Code AVP . . . . . . . . . . . . 140
- 11.4. Diameter TCP, SCTP, TLS/TCP and DTLS/SCTP Port Numbers . 141
- 11.5. S-NAPTR Parameters . . . . . . . . . . . . . . . . . . . 141
- 12. Diameter protocol related configurable parameters . . . . . . 142
- 13. Security Considerations . . . . . . . . . . . . . . . . . . . 143
- 13.1. TLS/TCP and DTLS/SCTP Usage . . . . . . . . . . . . . . . 143
- 13.2. Peer-to-Peer Considerations . . . . . . . . . . . . . . . 144
- 14. References . . . . . . . . . . . . . . . . . . . . . . . . . 145
- 14.1. Normative References . . . . . . . . . . . . . . . . . . 145
- 14.2. Informational References . . . . . . . . . . . . . . . . 147
- Appendix A. Acknowledgements . . . . . . . . . . . . . . . . . . 149
- A.1. RFC3588bis . . . . . . . . . . . . . . . . . . . . . . . 149
- A.2. RFC3588 . . . . . . . . . . . . . . . . . . . . . . . . . 150
- Appendix B. S-NAPTR Example . . . . . . . . . . . . . . . . . . 151
- Appendix C. Duplicate Detection . . . . . . . . . . . . . . . . 152
- Appendix D. Internationalized Domain Names . . . . . . . . . . . 154
- Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . 155
-
-
-
-
-
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 5]
-
-Internet-Draft Diameter Base Protocol January 2011
+ 1. Introduction ....................................................7
+ 1.1. Diameter Protocol ..........................................9
+ 1.1.1. Description of the Document Set ....................10
+ 1.1.2. Conventions Used in This Document ..................11
+ 1.1.3. Changes from RFC 3588 ..............................11
+ 1.2. Terminology ...............................................12
+ 1.3. Approach to Extensibility .................................17
+ 1.3.1. Defining New AVP Values ............................18
+ 1.3.2. Creating New AVPs ..................................18
+ 1.3.3. Creating New Commands ..............................18
+ 1.3.4. Creating New Diameter Applications .................19
+ 2. Protocol Overview ..............................................20
+ 2.1. Transport .................................................22
+ 2.1.1. SCTP Guidelines ....................................23
+ 2.2. Securing Diameter Messages ................................24
+ 2.3. Diameter Application Compliance ...........................24
+ 2.4. Application Identifiers ...................................24
+ 2.5. Connections vs. Sessions ..................................25
+ 2.6. Peer Table ................................................26
+
+
+
+Fajardo, et al. Standards Track [Page 2]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ 2.7. Routing Table .............................................27
+ 2.8. Role of Diameter Agents ...................................28
+ 2.8.1. Relay Agents .......................................30
+ 2.8.2. Proxy Agents .......................................31
+ 2.8.3. Redirect Agents ....................................31
+ 2.8.4. Translation Agents .................................32
+ 2.9. Diameter Path Authorization ...............................33
+ 3. Diameter Header ................................................34
+ 3.1. Command Codes .............................................37
+ 3.2. Command Code Format Specification .........................38
+ 3.3. Diameter Command Naming Conventions .......................40
+ 4. Diameter AVPs ..................................................40
+ 4.1. AVP Header ................................................41
+ 4.1.1. Optional Header Elements ...........................42
+ 4.2. Basic AVP Data Formats ....................................43
+ 4.3. Derived AVP Data Formats ..................................44
+ 4.3.1. Common Derived AVP Data Formats ....................44
+ 4.4. Grouped AVP Values ........................................51
+ 4.4.1. Example AVP with a Grouped Data Type ...............52
+ 4.5. Diameter Base Protocol AVPs ...............................55
+ 5. Diameter Peers .................................................58
+ 5.1. Peer Connections ..........................................58
+ 5.2. Diameter Peer Discovery ...................................59
+ 5.3. Capabilities Exchange .....................................60
+ 5.3.1. Capabilities-Exchange-Request ......................62
+ 5.3.2. Capabilities-Exchange-Answer .......................63
+ 5.3.3. Vendor-Id AVP ......................................63
+ 5.3.4. Firmware-Revision AVP ..............................64
+ 5.3.5. Host-IP-Address AVP ................................64
+ 5.3.6. Supported-Vendor-Id AVP ............................64
+ 5.3.7. Product-Name AVP ...................................64
+ 5.4. Disconnecting Peer Connections ............................64
+ 5.4.1. Disconnect-Peer-Request ............................65
+ 5.4.2. Disconnect-Peer-Answer .............................65
+ 5.4.3. Disconnect-Cause AVP ...............................66
+ 5.5. Transport Failure Detection ...............................66
+ 5.5.1. Device-Watchdog-Request ............................67
+ 5.5.2. Device-Watchdog-Answer .............................67
+ 5.5.3. Transport Failure Algorithm ........................67
+ 5.5.4. Failover and Failback Procedures ...................67
+ 5.6. Peer State Machine ........................................68
+ 5.6.1. Incoming Connections ...............................71
+ 5.6.2. Events .............................................71
+ 5.6.3. Actions ............................................72
+ 5.6.4. The Election Process ...............................74
+
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 3]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ 6. Diameter Message Processing ....................................74
+ 6.1. Diameter Request Routing Overview .........................74
+ 6.1.1. Originating a Request ..............................75
+ 6.1.2. Sending a Request ..................................76
+ 6.1.3. Receiving Requests .................................76
+ 6.1.4. Processing Local Requests ..........................76
+ 6.1.5. Request Forwarding .................................77
+ 6.1.6. Request Routing ....................................77
+ 6.1.7. Predictive Loop Avoidance ..........................77
+ 6.1.8. Redirecting Requests ...............................78
+ 6.1.9. Relaying and Proxying Requests .....................79
+ 6.2. Diameter Answer Processing ................................80
+ 6.2.1. Processing Received Answers ........................81
+ 6.2.2. Relaying and Proxying Answers ......................81
+ 6.3. Origin-Host AVP ...........................................81
+ 6.4. Origin-Realm AVP ..........................................82
+ 6.5. Destination-Host AVP ......................................82
+ 6.6. Destination-Realm AVP .....................................82
+ 6.7. Routing AVPs ..............................................83
+ 6.7.1. Route-Record AVP ...................................83
+ 6.7.2. Proxy-Info AVP .....................................83
+ 6.7.3. Proxy-Host AVP .....................................83
+ 6.7.4. Proxy-State AVP ....................................83
+ 6.8. Auth-Application-Id AVP ...................................83
+ 6.9. Acct-Application-Id AVP ...................................84
+ 6.10. Inband-Security-Id AVP ...................................84
+ 6.11. Vendor-Specific-Application-Id AVP .......................84
+ 6.12. Redirect-Host AVP ........................................85
+ 6.13. Redirect-Host-Usage AVP ..................................85
+ 6.14. Redirect-Max-Cache-Time AVP ..............................87
+ 7. Error Handling .................................................87
+ 7.1. Result-Code AVP ...........................................89
+ 7.1.1. Informational ......................................90
+ 7.1.2. Success ............................................90
+ 7.1.3. Protocol Errors ....................................90
+ 7.1.4. Transient Failures .................................92
+ 7.1.5. Permanent Failures .................................92
+ 7.2. Error Bit .................................................95
+ 7.3. Error-Message AVP .........................................96
+ 7.4. Error-Reporting-Host AVP ..................................96
+ 7.5. Failed-AVP AVP ............................................96
+ 7.6. Experimental-Result AVP ...................................97
+ 7.7. Experimental-Result-Code AVP ..............................97
+ 8. Diameter User Sessions .........................................98
+ 8.1. Authorization Session State Machine .......................99
+ 8.2. Accounting Session State Machine .........................104
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 4]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ 8.3. Server-Initiated Re-Auth .................................110
+ 8.3.1. Re-Auth-Request ...................................110
+ 8.3.2. Re-Auth-Answer ....................................110
+ 8.4. Session Termination ......................................111
+ 8.4.1. Session-Termination-Request .......................112
+ 8.4.2. Session-Termination-Answer ........................113
+ 8.5. Aborting a Session .......................................113
+ 8.5.1. Abort-Session-Request .............................114
+ 8.5.2. Abort-Session-Answer ..............................114
+ 8.6. Inferring Session Termination from Origin-State-Id .......115
+ 8.7. Auth-Request-Type AVP ....................................116
+ 8.8. Session-Id AVP ...........................................116
+ 8.9. Authorization-Lifetime AVP ...............................117
+ 8.10. Auth-Grace-Period AVP ...................................118
+ 8.11. Auth-Session-State AVP ..................................118
+ 8.12. Re-Auth-Request-Type AVP ................................118
+ 8.13. Session-Timeout AVP .....................................119
+ 8.14. User-Name AVP ...........................................119
+ 8.15. Termination-Cause AVP ...................................120
+ 8.16. Origin-State-Id AVP .....................................120
+ 8.17. Session-Binding AVP .....................................120
+ 8.18. Session-Server-Failover AVP .............................121
+ 8.19. Multi-Round-Time-Out AVP ................................122
+ 8.20. Class AVP ...............................................122
+ 8.21. Event-Timestamp AVP .....................................122
+ 9. Accounting ....................................................123
+ 9.1. Server Directed Model ....................................123
+ 9.2. Protocol Messages ........................................124
+ 9.3. Accounting Application Extension and Requirements ........124
+ 9.4. Fault Resilience .........................................125
+ 9.5. Accounting Records .......................................125
+ 9.6. Correlation of Accounting Records ........................126
+ 9.7. Accounting Command Codes .................................127
+ 9.7.1. Accounting-Request ................................127
+ 9.7.2. Accounting-Answer .................................128
+ 9.8. Accounting AVPs ..........................................129
+ 9.8.1. Accounting-Record-Type AVP ........................129
+ 9.8.2. Acct-Interim-Interval AVP .........................130
+ 9.8.3. Accounting-Record-Number AVP ......................131
+ 9.8.4. Acct-Session-Id AVP ...............................131
+ 9.8.5. Acct-Multi-Session-Id AVP .........................131
+ 9.8.6. Accounting-Sub-Session-Id AVP .....................131
+ 9.8.7. Accounting-Realtime-Required AVP ..................132
+ 10. AVP Occurrence Tables ........................................132
+ 10.1. Base Protocol Command AVP Table .........................133
+ 10.2. Accounting AVP Table ....................................134
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 5]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ 11. IANA Considerations ..........................................135
+ 11.1. AVP Header ..............................................135
+ 11.1.1. AVP Codes ........................................136
+ 11.1.2. AVP Flags ........................................136
+ 11.2. Diameter Header .........................................136
+ 11.2.1. Command Codes ....................................136
+ 11.2.2. Command Flags ....................................137
+ 11.3. AVP Values ..............................................137
+ 11.3.1. Experimental-Result-Code AVP .....................137
+ 11.3.2. Result-Code AVP Values ...........................137
+ 11.3.3. Accounting-Record-Type AVP Values ................137
+ 11.3.4. Termination-Cause AVP Values .....................137
+ 11.3.5. Redirect-Host-Usage AVP Values ...................137
+ 11.3.6. Session-Server-Failover AVP Values ...............137
+ 11.3.7. Session-Binding AVP Values .......................137
+ 11.3.8. Disconnect-Cause AVP Values ......................138
+ 11.3.9. Auth-Request-Type AVP Values .....................138
+ 11.3.10. Auth-Session-State AVP Values ...................138
+ 11.3.11. Re-Auth-Request-Type AVP Values .................138
+ 11.3.12. Accounting-Realtime-Required AVP Values .........138
+ 11.3.13. Inband-Security-Id AVP (code 299) ...............138
+ 11.4. _diameters Service Name and Port Number Registration ....138
+ 11.5. SCTP Payload Protocol Identifiers .......................139
+ 11.6. S-NAPTR Parameters ......................................139
+ 12. Diameter Protocol-Related Configurable Parameters ............139
+ 13. Security Considerations ......................................140
+ 13.1. TLS/TCP and DTLS/SCTP Usage .............................140
+ 13.2. Peer-to-Peer Considerations .............................141
+ 13.3. AVP Considerations ......................................141
+ 14. References ...................................................142
+ 14.1. Normative References ....................................142
+ 14.2. Informative References ..................................144
+ Appendix A. Acknowledgements .....................................147
+ A.1. This Document .............................................147
+ A.2. RFC 3588 ..................................................148
+ Appendix B. S-NAPTR Example ......................................148
+ Appendix C. Duplicate Detection ..................................149
+ Appendix D. Internationalized Domain Names .......................151
+
+
+
+
+
+
+
+
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 6]
+
+RFC 6733 Diameter Base Protocol October 2012
1. Introduction
- Authentication, Authorization and Accounting (AAA) protocols such as
+ Authentication, Authorization, and Accounting (AAA) protocols such as
TACACS [RFC1492] and RADIUS [RFC2865] were initially deployed to
provide dial-up PPP [RFC1661] and terminal server access. Over time,
AAA support was needed on many new access technologies, the scale and
complexity of AAA networks grew, and AAA was also used on new
- applications (such as voice over IP). This lead to new demands on
- AAA protocols.
+ applications (such as voice over IP). This led to new demands on AAA
+ protocols.
Network access requirements for AAA protocols are summarized in
- [RFC2989]. These include:
-
+ Aboba, et al. [RFC2989]. These include:
Failover
- [RFC2865] does not define failover mechanisms, and as a result,
+ [RFC2865] does not define failover mechanisms and, as a result,
failover behavior differs between implementations. In order to
provide well-defined failover behavior, Diameter supports
- application-layer acknowledgements, and defines failover
- algorithms and the associated state machine. This is described in
- Section 5.5 and [RFC3539].
+ application-layer acknowledgements and defines failover algorithms
+ and the associated state machine.
Transmission-level security
- [RFC2865] defines an application-layer authentication and
- integrity scheme that is required only for use with Response
+ RADIUS [RFC2865] defines an application-layer authentication and
+ integrity scheme that is required only for use with response
packets. While [RFC2869] defines an additional authentication and
integrity mechanism, use is only required during Extensible
- Authentication Protocol (EAP) sessions. While attribute-hiding is
- supported, [RFC2865] does not provide support for per-packet
- confidentiality. In accounting, [RFC2866] assumes that replay
- protection is provided by the backend billing server, rather than
- within the protocol itself.
+ Authentication Protocol (EAP) [RFC3748] sessions. While attribute
+ hiding is supported, [RFC2865] does not provide support for per-
+ packet confidentiality. In accounting, [RFC2866] assumes that
+ replay protection is provided by the backend billing server rather
+ than within the protocol itself.
While [RFC3162] defines the use of IPsec with RADIUS, support for
IPsec is not required. In order to provide universal support for
@@ -322,50 +379,48 @@ Internet-Draft Diameter Base Protocol January 2011
domain AAA deployments, Diameter provides support for TLS/TCP and
DTLS/SCTP. Security is discussed in Section 13.
-
Reliable transport
-
RADIUS runs over UDP, and does not define retransmission behavior;
as a result, reliability varies between implementations. As
described in [RFC2975], this is a major issue in accounting, where
+ packet loss may translate directly into revenue loss. In order to
-Fajardo, et al. Expires July 24, 2011 [Page 6]
-
-Internet-Draft Diameter Base Protocol January 2011
- packet loss may translate directly into revenue loss. In order to
- provide well defined transport behavior, Diameter runs over
- reliable transport mechanisms (TCP, SCTP) as defined in [RFC3539].
+Fajardo, et al. Standards Track [Page 7]
+
+RFC 6733 Diameter Base Protocol October 2012
- Agent support
- [RFC2865] does not provide for explicit support for agents,
- including Proxies, Redirects and Relays. Since the expected
- behavior is not defined, it varies between implementations.
- Diameter defines agent behavior explicitly; this is described in
- Section 2.8.
+ provide well-defined transport behavior, Diameter runs over
+ reliable transport mechanisms (TCP, Stream Control Transmission
+ Protocol (SCTP)) as defined in [RFC3539].
+
+ Agent support
+ RADIUS does not provide for explicit support for agents, including
+ proxies, redirects, and relays. Since the expected behavior is
+ not defined, it varies between implementations. Diameter defines
+ agent behavior explicitly; this is described in Section 2.8.
Server-initiated messages
- While RADIUS server-initiated messages are defined in [RFC5176],
+ While server-initiated messages are defined in RADIUS [RFC5176],
support is optional. This makes it difficult to implement
features such as unsolicited disconnect or re-authentication/
re-authorization on demand across a heterogeneous deployment. To
- tackle this issue, support for server-initiated messages is
+ address this issue, support for server-initiated messages is
mandatory in Diameter.
-
Transition support
While Diameter does not share a common protocol data unit (PDU)
with RADIUS, considerable effort has been expended in enabling
- backward compatibility with RADIUS, so that the two protocols may
+ backward compatibility with RADIUS so that the two protocols may
be deployed in the same network. Initially, it is expected that
Diameter will be deployed within new network devices, as well as
within gateways enabling communication between legacy RADIUS
@@ -373,9 +428,8 @@ Internet-Draft Diameter Base Protocol January 2011
support to be added to legacy networks, by addition of a gateway
or server speaking both RADIUS and Diameter.
- In addition to addressing the above requirements, Diameter also
- provides support for the following:
-
+ In addition to addressing the above requirements, Diameter also
+ provides support for the following:
Capability negotiation
@@ -383,19 +437,19 @@ Internet-Draft Diameter Base Protocol January 2011
a mandatory/non-mandatory flag for attributes. Since RADIUS
clients and servers are not aware of each other's capabilities,
they may not be able to successfully negotiate a mutually
- acceptable service, or in some cases, even be aware of what
+ acceptable service or, in some cases, even be aware of what
service has been implemented. Diameter includes support for error
+ handling (Section 7), capability negotiation (Section 5.3), and
+ mandatory/non-mandatory Attribute-Value Pairs (AVPs)
+ (Section 4.1).
-Fajardo, et al. Expires July 24, 2011 [Page 7]
-
-Internet-Draft Diameter Base Protocol January 2011
- handling (Section 7), capability negotiation (Section 5.3), and
- mandatory/non-mandatory Attribute-Value Pairs (AVPs) (Section
- 4.1).
+Fajardo, et al. Standards Track [Page 8]
+
+RFC 6733 Diameter Base Protocol October 2012
Peer discovery and configuration
@@ -403,7 +457,7 @@ Internet-Draft Diameter Base Protocol January 2011
RADIUS implementations typically require that the name or address
of servers or clients be manually configured, along with the
corresponding shared secrets. This results in a large
- administrative burden, and creates the temptation to reuse the
+ administrative burden and creates the temptation to reuse the
RADIUS shared secret, which can result in major security
vulnerabilities if the Request Authenticator is not globally and
temporally unique as required in [RFC2865]. Through DNS, Diameter
@@ -411,7 +465,6 @@ Internet-Draft Diameter Base Protocol January 2011
of dynamic session keys is enabled via transmission-level
security.
-
Over time, the capabilities of Network Access Server (NAS) devices
have increased substantially. As a result, while Diameter is a
considerably more sophisticated protocol than RADIUS, it remains
@@ -427,42 +480,41 @@ Internet-Draft Diameter Base Protocol January 2011
o Error notification
- o Extensibility, through addition of new applications, commands and
- AVPs (required in [RFC2989]).
+ o Extensibility, required in [RFC2989], through addition of new
+ applications, commands, and AVPs
- o Basic services necessary for applications, such as handling of
+ o Basic services necessary for applications, such as the handling of
user sessions or accounting
All data delivered by the protocol is in the form of AVPs. Some of
these AVP values are used by the Diameter protocol itself, while
others deliver data associated with particular applications that
employ Diameter. AVPs may be arbitrarily added to Diameter messages,
- the only restriction being that the Augmented Backus-Naur Form (ABNF,
- [RFC5234]) Command Code syntax specification (Section 3.2) is
- satisfied. AVPs are used by the base Diameter protocol to support
- the following required features:
+ the only restriction being that the Command Code Format (CCF)
+ specification (Section 3.2) be satisfied. AVPs are used by the base
+ Diameter protocol to support the following required features:
+ o Transporting of user authentication information, for the purposes
+ of enabling the Diameter server to authenticate the user
+ o Transporting of service-specific authorization information,
+ between client and servers, allowing the peers to decide whether a
+ user's access request should be granted
-Fajardo, et al. Expires July 24, 2011 [Page 8]
-
-Internet-Draft Diameter Base Protocol January 2011
- o Transporting of user authentication information, for the purposes
- of enabling the Diameter server to authenticate the user.
+Fajardo, et al. Standards Track [Page 9]
+
+RFC 6733 Diameter Base Protocol October 2012
- o Transporting of service-specific authorization information,
- between client and servers, allowing the peers to decide whether a
- user's access request should be granted.
o Exchanging resource usage information, which may be used for
accounting purposes, capacity planning, etc.
- o Routing, relaying, proxying and redirecting of Diameter messages
- through a server hierarchy.
+ o Routing, relaying, proxying, and redirecting of Diameter messages
+ through a server hierarchy
- The Diameter base protocol satisfies the minimum requirements for an
+ The Diameter base protocol satisfies the minimum requirements for a
AAA protocol, as specified by [RFC2989]. The base protocol may be
used by itself for accounting purposes only, or it may be used with a
Diameter application, such as Mobile IPv4 [RFC4004], or network
@@ -473,16 +525,16 @@ Internet-Draft Diameter Base Protocol January 2011
many applications might provide functionality not provided by
Diameter. Therefore, it is imperative that the designers of new
applications understand their requirements before using Diameter.
- See Section 2.4 for more information on Diameter applications.
+ See Section 1.3.4 for more information on Diameter applications.
Any node can initiate a request. In that sense, Diameter is a peer-
- to-peer protocol. In this document, a Diameter Client is a device at
+ to-peer protocol. In this document, a Diameter client is a device at
the edge of the network that performs access control, such as a
Network Access Server (NAS) or a Foreign Agent (FA). A Diameter
client generates Diameter messages to request authentication,
authorization, and accounting services for the user. A Diameter
agent is a node that does not provide local user authentication or
- authorization services; agents include proxies, redirects and relay
+ authorization services; agents include proxies, redirects, and relay
agents. A Diameter server performs authentication and/or
authorization of the user. A Diameter node may act as an agent for
certain requests while acting as a server for others.
@@ -494,33 +546,33 @@ Internet-Draft Diameter Base Protocol January 2011
The Diameter specification consists of an updated version of the base
protocol specification (this document) and the Transport Profile
- [RFC3539]. This document obsoletes RFC 3588. A summary of the base
- protocol updates included in this document can be found in
- Section 1.1.3.
-
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 9]
-
-Internet-Draft Diameter Base Protocol January 2011
-
+ [RFC3539]. This document obsoletes both RFC 3588 and RFC 5719. A
+ summary of the base protocol updates included in this document can be
+ found in Section 1.1.3.
This document defines the base protocol specification for AAA, which
includes support for accounting. There are also a myriad of
applications documents describing applications that use this base
- specification for Authentication, Authorization and Accounting.
+ specification for Authentication, Authorization, and Accounting.
These application documents specify how to use the Diameter protocol
within the context of their application.
+
+
+Fajardo, et al. Standards Track [Page 10]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
The Transport Profile document [RFC3539] discusses transport layer
issues that arise with AAA protocols and recommendations on how to
overcome these issues. This document also defines the Diameter
failover algorithm and state machine.
- Clarifications on the Routing of Diameter Request based on Username
- and the Realm [RFC5729] defines specific behavior on how to route
- request based on the content of the User-Name AVP (Attribute Value
- Pair).
+ "Clarifications on the Routing of Diameter Request Based on the
+ Username and the Realm" [RFC5729] defines specific behavior on how to
+ route requests based on the content of the User-Name AVP (Attribute
+ Value Pair).
1.1.2. Conventions Used in This Document
@@ -528,100 +580,100 @@ Internet-Draft Diameter Base Protocol January 2011
"SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
document are to be interpreted as described in [RFC2119].
-1.1.3. Changes from RFC3588
+1.1.3. Changes from RFC 3588
This document obsoletes RFC 3588 but is fully backward compatible
with that document. The changes introduced in this document focus on
- fixing issues that have surfaced during implementation of [RFC3588].
- An overview of some the major changes are given below.
-
- o Deprecated the use of Inband-Security AVP for negotiating
- transport layer security. It has been generally considered that
- bootstrapping of TLS via Inband-Security AVP creates certain
- security risk because it does not completely protect the
- information carried in the CER (Capabilities Exchange Request)/CEA
- (Capabilities Exchange Answer). This version of Diameter adopted
- a common approach of defining a well-known secured port that peers
- should use when communicating via TLS/TCP and DTLS/SCTP. This new
- approach augments the existing Inband-Security negotiation but
- does not completely replace it. The old method is kept for
- backwards compatibility reasons.
+ fixing issues that have surfaced during the implementation of
+ Diameter (RFC 3588). An overview of some the major changes are given
+ below.
+
+ o Deprecated the use of the Inband-Security AVP for negotiating
+ Transport Layer Security (TLS) [RFC5246]. It has been generally
+ considered that bootstrapping of TLS via Inband-Security AVP
+ creates certain security risks because it does not completely
+ protect the information carried in the CER/CEA (Capabilities-
+ Exchange-Request/Capabilities-Exchange-Answer). This version of
+ Diameter adopts the common approach of defining a well-known
+ secured port that peers should use when communicating via TLS/TCP
+ and DTLS/SCTP. This new approach augments the existing in-band
+ security negotiation, but it does not completely replace it. The
+ old method is kept for backward compatibility reasons.
o Deprecated the exchange of CER/CEA messages in the open state.
- This feature was implied in the peer state machine table of
- [RFC3588] but it was not clearly defined anywhere else in that
+ This feature was implied in the peer state machine table of RFC
+ 3588, but it was not clearly defined anywhere else in that
document. As work on this document progressed, it became clear
- that the multiplicity of meaning and use of Application Id AVPs in
+ that the multiplicity of meaning and use of Application-Id AVPs in
the CER/CEA messages (and the messages themselves) is seen as an
+ abuse of the Diameter extensibility rules and thus required
+ simplification. Capabilities exchange in the open state has been
+ re-introduced in a separate specification [RFC6737], which clearly
+ defines new commands for this feature.
-Fajardo, et al. Expires July 24, 2011 [Page 10]
-
-Internet-Draft Diameter Base Protocol January 2011
- abuse of the Diameter extensibility rules and thus required
- simplification. It is assumed that the capabilities exchange in
- the open state will be re-introduced in a separate specification
- which clearly defines new commands for this feature.
+Fajardo, et al. Standards Track [Page 11]
+
+RFC 6733 Diameter Base Protocol October 2012
+
- o Simplified Security Requirements. The use of a secured transport
+ o Simplified security requirements. The use of a secured transport
for exchanging Diameter messages remains mandatory. However, TLS/
- TCP and DTLS/SCTP has become the primary method of securing
- Diameter and IPsec is a secondary alternative. See Section 13 for
- details. The support for the End-to-End security framework
- (E2ESequence AVP and 'P'-bit in the AVP header) has also been
+ TCP and DTLS/SCTP have become the primary methods of securing
+ Diameter with IPsec as a secondary alternative. See Section 13
+ for details. The support for the End-to-End security framework
+ (E2E-Sequence AVP and 'P'-bit in the AVP header) has also been
deprecated.
- o Diameter Extensibility Changes. This includes fixes to the
+ o Changed Diameter extensibility. This includes fixes to the
Diameter extensibility description (Section 1.3 and others) to
better aid Diameter application designers; in addition, the new
specification relaxes the policy with respect to the allocation of
- command codes for vendor-specific uses.
-
- o Application Id Usage. Clarify the proper use of Application Id
- information which can be found in multiple places within a
- Diameter message. This includes correlating Application Ids found
- in the message headers and AVPs. These changes also clearly
- specify the proper Application Id value to use for specific base
- protocol messages (ASR/ASA, STR/STA) as well as clarifying the
- content and use of Vendor-Specific-Application-Id.
-
- o Routing Fixes. This document more clearly specifies what
- information (AVPs and Application Id) can be used for making
+ Command Codes for vendor-specific uses.
+
+ o Clarified Application Id usage. Clarify the proper use of
+ Application Id information, which can be found in multiple places
+ within a Diameter message. This includes correlating Application
+ Ids found in the message headers and AVPs. These changes also
+ clearly specify the proper Application Id value to use for
+ specific base protocol messages (ASR/ASA, STR/STA) as well as
+ clarify the content and use of Vendor-Specific-Application-Id.
+
+ o Clarified routing fixes. This document more clearly specifies
+ what information (AVPs and Application Ids) can be used for making
general routing decisions. A rule for the prioritization of
redirect routing criteria when multiple route entries are found
- via redirects has also been added (See Section 6.13 for details).
+ via redirects has also been added (see Section 6.13).
- o Simplification of Diameter Peer Discovery. The Diameter discovery
+ o Simplified Diameter peer discovery. The Diameter discovery
process now supports only widely used discovery schemes; the rest
have been deprecated (see Section 5.2 for details).
- There are many other many miscellaneous fixes that have been
- introduced in this document that may not be considered significant
- but they are important nonetheless. Examples are removal of obsolete
- types, fixes to command ABNFs, fixes to the state machine,
- clarification of the election process, message validation, fixes to
- Failed-AVP and Result-Code AVP values, etc. A comprehensive list of
- changes is not shown here for practical reasons.
-
-
-
+ There are many other miscellaneous fixes that have been introduced in
+ this document that may not be considered significant, but they have
+ value nonetheless. Examples are removal of obsolete types, fixes to
+ the state machine, clarification of the election process, message
+ validation, fixes to Failed-AVP and Result-Code AVP values, etc. All
+ of the errata filed against RFC 3588 prior to the publication of this
+ document have been addressed. A comprehensive list of changes is not
+ shown here for practical reasons.
+1.2. Terminology
+ AAA
+ Authentication, Authorization, and Accounting.
-Fajardo, et al. Expires July 24, 2011 [Page 11]
-
-Internet-Draft Diameter Base Protocol January 2011
-1.2. Terminology
- AAA
- Authentication, Authorization and Accounting.
+Fajardo, et al. Standards Track [Page 12]
+
+RFC 6733 Diameter Base Protocol October 2012
ABNF
@@ -631,14 +683,12 @@ Internet-Draft Diameter Base Protocol January 2011
is used to define message exchanges in a bi-directional
communications protocol.
-
Accounting
The act of collecting information on resource usage for the
- purpose of capacity planning, auditing, billing or cost
+ purpose of capacity planning, auditing, billing, or cost
allocation.
-
Accounting Record
An accounting record represents a summary of the resource
@@ -647,114 +697,102 @@ Internet-Draft Diameter Base Protocol January 2011
accounting events or accounting events from several devices
serving the same user.
-
Authentication
The act of verifying the identity of an entity (subject).
-
Authorization
The act of determining whether a requesting entity (subject) will
be allowed access to a resource (object).
-
- AVP
+ Attribute-Value Pair (AVP)
The Diameter protocol consists of a header followed by one or more
Attribute-Value-Pairs (AVPs). An AVP includes a header and is
used to encapsulate protocol-specific data (e.g., routing
- information) as well as authentication, authorization or
+ information) as well as authentication, authorization, or
+ accounting information.
+ Command Code Format (CCF)
+ A modified form of ABNF used to define Diameter commands (see
+ Section 3.2).
-Fajardo, et al. Expires July 24, 2011 [Page 12]
-
-Internet-Draft Diameter Base Protocol January 2011
+ Diameter Agent
+ A Diameter Agent is a Diameter node that provides relay, proxy,
+ redirect, or translation services.
- accounting information.
- Diameter Agent
- A Diameter Agent is a Diameter Node that provides either relay,
- proxy, redirect or translation services.
+Fajardo, et al. Standards Track [Page 13]
+
+RFC 6733 Diameter Base Protocol October 2012
Diameter Client
- A Diameter Client is a Diameter Node that supports Diameter client
- applications as well as the base protocol. Diameter Clients are
+ A Diameter client is a Diameter node that supports Diameter client
+ applications as well as the base protocol. Diameter clients are
often implemented in devices situated at the edge of a network and
provide access control services for that network. Typical
- examples of Diameter Clients include the Network Access Server
+ examples of Diameter clients include the Network Access Server
(NAS) and the Mobile IP Foreign Agent (FA).
-
Diameter Node
- A Diameter Node is a host process that implements the Diameter
- protocol, and acts either as a Client, Agent or Server.
-
+ A Diameter node is a host process that implements the Diameter
+ protocol and acts as either a client, an agent, or a server.
Diameter Peer
- If a Diameter Node shares a direct transport connection with
- another Diameter Node, it is a Diameter Peer to that Diameter
- Node.
-
+ Two Diameter nodes sharing a direct TCP or SCTP transport
+ connection are called Diameter peers.
Diameter Server
- A Diameter Server is a Diameter Node that handles authentication,
- authorization and accounting requests for a particular realm. By
- its very nature, a Diameter Server must support Diameter server
+ A Diameter server is a Diameter node that handles authentication,
+ authorization, and accounting requests for a particular realm. By
+ its very nature, a Diameter server must support Diameter server
applications in addition to the base protocol.
-
Downstream
Downstream is used to identify the direction of a particular
- Diameter message from the Home Server towards the Diameter Client.
-
-
-
-
-
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 13]
-
-Internet-Draft Diameter Base Protocol January 2011
-
+ Diameter message from the home server towards the Diameter client.
Home Realm
A Home Realm is the administrative domain with which the user
maintains an account relationship.
-
Home Server
- A Diameter Server which serves the Home Realm.
-
+ A Diameter server that serves the Home Realm.
- Interim accounting
+ Interim Accounting
An interim accounting message provides a snapshot of usage during
- a user's session. It is typically implemented in order to provide
- for partial accounting of a user's session in the case a device
- reboot or other network problem prevents the delivery of a session
- summary message or session record.
+ a user's session. Typically, it is implemented in order to
+ provide for partial accounting of a user's session in case a
+ device reboot or other network problem prevents the delivery of a
+ session summary message or session record.
+
+
+
+
+Fajardo, et al. Standards Track [Page 14]
+
+RFC 6733 Diameter Base Protocol October 2012
Local Realm
A local realm is the administrative domain providing services to a
user. An administrative domain may act as a local realm for
- certain users, while being a home realm for others.
-
+ certain users while being a home realm for others.
Multi-session
@@ -764,60 +802,56 @@ Internet-Draft Diameter Base Protocol January 2011
leg of the bundle would be a session while the entire bundle would
be a multi-session.
-
Network Access Identifier
The Network Access Identifier, or NAI [RFC4282], is used in the
Diameter protocol to extract a user's identity and realm. The
identity is used to identify the user during authentication and/or
- authorization, while the realm is used for message routing
+ authorization while the realm is used for message routing
purposes.
-
-
-
-
-
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 14]
-
-Internet-Draft Diameter Base Protocol January 2011
-
-
Proxy Agent or Proxy
In addition to forwarding requests and responses, proxies make
policy decisions relating to resource usage and provisioning.
- This is typically accomplished by tracking the state of NAS
- devices. While proxies typically do not respond to client
- Requests prior to receiving a Response from the server, they may
- originate Reject messages in cases where policies are violated.
- As a result, proxies need to understand the semantics of the
- messages passing through them, and may not support all Diameter
+ Typically, this is accomplished by tracking the state of NAS
+ devices. While proxies usually do not respond to client requests
+ prior to receiving a response from the server, they may originate
+ Reject messages in cases where policies are violated. As a
+ result, proxies need to understand the semantics of the messages
+ passing through them, and they may not support all Diameter
applications.
-
Realm
The string in the NAI that immediately follows the '@' character.
- NAI realm names are required to be unique, and are piggybacked on
+ NAI realm names are required to be unique and are piggybacked on
the administration of the DNS namespace. Diameter makes use of
the realm, also loosely referred to as domain, to determine
- whether messages can be satisfied locally, or whether they must be
+ whether messages can be satisfied locally or whether they must be
routed or redirected. In RADIUS, realm names are not necessarily
piggybacked on the DNS namespace but may be independent of it.
- Real-time Accounting
+
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 15]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ Real-Time Accounting
Real-time accounting involves the processing of information on
- resource usage within a defined time window. Time constraints are
- typically imposed in order to limit financial risk. The Diameter
- Credit Control Application [RFC4006] is an example of an
+ resource usage within a defined time window. Typically, time
+ constraints are imposed in order to limit financial risk. The
+ Diameter Credit-Control Application [RFC4006] is an example of an
application that defines real-time accounting functionality.
-
Relay Agent or Relay
Relays forward requests and responses based on routing-related
@@ -827,20 +861,9 @@ Internet-Draft Diameter Base Protocol January 2011
the semantics of messages or non-routing AVPs, and are capable of
handling any Diameter application or message type. Since relays
make decisions based on information in routing AVPs and realm
- forwarding tables they do not keep state on NAS resource usage or
+ forwarding tables, they do not keep state on NAS resource usage or
sessions in progress.
-
-
-
-
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 15]
-
-Internet-Draft Diameter Base Protocol January 2011
-
-
Redirect Agent
Rather than forwarding requests and responses between clients and
@@ -850,11 +873,10 @@ Internet-Draft Diameter Base Protocol January 2011
client and server. Redirect agents do not originate messages and
are capable of handling any message type, although they may be
configured only to redirect messages of certain types, while
- acting as relay or proxy agents for other types. As with proxy
+ acting as relay or proxy agents for other types. As with relay
agents, redirect agents do not keep state with respect to sessions
or NAS resources.
-
Session
A session is a related progression of events devoted to a
@@ -863,14 +885,19 @@ Internet-Draft Diameter Base Protocol January 2011
packets with the same Session-Id are considered to be part of the
same session.
-
Stateful Agent
A stateful agent is one that maintains session state information,
by keeping track of all authorized active sessions. Each
authorized session is bound to a particular service, and its state
- is considered active either until it is notified otherwise, or by
- expiration.
+ is considered active either until it is notified otherwise or
+ until expiration.
+
+
+
+Fajardo, et al. Standards Track [Page 16]
+
+RFC 6733 Diameter Base Protocol October 2012
Sub-session
@@ -881,53 +908,33 @@ Internet-Draft Diameter Base Protocol January 2011
during the same session) or serially. These changes in sessions
are tracked with the Accounting-Sub-Session-Id.
-
- Transaction state
+ Transaction State
The Diameter protocol requires that agents maintain transaction
state, which is used for failover purposes. Transaction state
- implies that upon forwarding a request, the Hop-by-Hop identifier
+ implies that upon forwarding a request, the Hop-by-Hop Identifier
is saved; the field is replaced with a locally unique identifier,
which is restored to its original value when the corresponding
-
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 16]
-
-Internet-Draft Diameter Base Protocol January 2011
-
-
answer is received. The request's state is released upon receipt
of the answer. A stateless agent is one that only maintains
transaction state.
-
Translation Agent
- A translation agent is a stateful Diameter node that performs
- protocol translation between Diameter and another AAA protocol,
- such as RADIUS.
-
-
- Transport Connection
-
- A transport connection is a TCP or SCTP connection existing
- directly between two Diameter peers, otherwise known as a Peer-to-
- Peer Connection.
-
+ A translation agent (TLA in Figure 4) is a stateful Diameter node
+ that performs protocol translation between Diameter and another
+ AAA protocol, such as RADIUS.
Upstream
Upstream is used to identify the direction of a particular
- Diameter message from the Diameter Client towards the Home Server.
-
+ Diameter message from the Diameter client towards the home server.
User
The entity or device requesting or using some resource, in support
of which a Diameter client has generated a request.
-
1.3. Approach to Extensibility
The Diameter protocol is designed to be extensible, using several
@@ -941,107 +948,108 @@ Internet-Draft Diameter Base Protocol January 2011
o Creating new applications
- From the point of view of extensibility Diameter authentication,
- authorization and accounting applications are treated in the same
- way.
-
-Fajardo, et al. Expires July 24, 2011 [Page 17]
+Fajardo, et al. Standards Track [Page 17]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
- Note: Protocol designers should try to re-use existing functionality,
+ From the point of view of extensibility, Diameter authentication,
+ authorization, and accounting applications are treated in the same
+ way.
+
+ Note: Protocol designers should try to reuse existing functionality,
namely AVP values, AVPs, commands, and Diameter applications. Reuse
simplifies standardization and implementation. To avoid potential
- interoperability issues it is important to ensure that the semantics
- of the re-used features are well understood. Given that Diameter can
- also carry RADIUS attributes as Diameter AVPs, such re-use
- considerations apply also to existing RADIUS attributes that may be
+ interoperability issues, it is important to ensure that the semantics
+ of the reused features are well understood. Given that Diameter can
+ also carry RADIUS attributes as Diameter AVPs, such reuse
+ considerations also apply to existing RADIUS attributes that may be
useful in a Diameter application.
-1.3.1. Defining New AVP Values
+1.3.1. Defining New AVP Values
In order to allocate a new AVP value for AVPs defined in the Diameter
- Base protocol, the IETF needs to approve a new RFC that describes the
+ base protocol, the IETF needs to approve a new RFC that describes the
AVP value. IANA considerations for these AVP values are discussed in
- Section 11.4.
+ Section 11.3.
The allocation of AVP values for other AVPs is guided by the IANA
considerations of the document that defines those AVPs. Typically,
- allocation of new values for an AVP defined in an IETF RFC should
- require IETF Review [RFC5226], whereas values for vendor-specific
- AVPs can be allocated by the vendor.
+ allocation of new values for an AVP defined in an RFC would require
+ IETF Review [RFC5226], whereas values for vendor-specific AVPs can be
+ allocated by the vendor.
-1.3.2. Creating New AVPs
+1.3.2. Creating New AVPs
A new AVP being defined MUST use one of the data types listed in
- Section 4.2 or Section 4.3. If an appropriate derived data type is
- already defined, it SHOULD be used instead of a base data type to
- encourage reusability and good design practice.
+ Sections 4.2 or 4.3. If an appropriate derived data type is already
+ defined, it SHOULD be used instead of a base data type to encourage
+ reusability and good design practice.
In the event that a logical grouping of AVPs is necessary, and
multiple "groups" are possible in a given command, it is recommended
that a Grouped AVP be used (see Section 4.4).
The creation of new AVPs can happen in various ways. The recommended
- approach is to define a new general-purpose AVP in a standards track
- RFC approved by the IETF. However, as described in Section 11.1.1
- there are also other mechanisms.
+ approach is to define a new general-purpose AVP in a Standards Track
+ RFC approved by the IETF. However, as described in Section 11.1.1,
+ there are other mechanisms.
-1.3.3. Creating New Commands
+1.3.3. Creating New Commands
A new Command Code MUST be allocated when required AVPs (those
- indicated as {AVP} in the ABNF definition) are added to, deleted from
+ indicated as {AVP} in the CCF definition) are added to, deleted from,
or redefined in (for example, by changing a required AVP into an
optional one) an existing command.
- Furthermore, if the transport characteristics of a command are
- changed (for example, with respect to the number of round trips
- required) a new Command Code MUST be registered.
-
-Fajardo, et al. Expires July 24, 2011 [Page 18]
+Fajardo, et al. Standards Track [Page 18]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
+
+ Furthermore, if the transport characteristics of a command are
+ changed (for example, with respect to the number of round trips
+ required), a new Command Code MUST be registered.
- A change to the ABNF of a command, such as described above, MUST
+ A change to the CCF of a command, such as described above, MUST
result in the definition of a new Command Code. This subsequently
- leads to the need to define a new Diameter Application for any
- application that will use that new Command.
-
- The IANA considerations for commands are discussed in Section 11.2.1.
-
-1.3.4. Creating New Diameter Applications
-
- Every Diameter application specification MUST have an IANA assigned
- Application Id (see Section 2.4 and Section 11.3). The managed
- Application Id space is flat and there is no relationship between
- different Diameter applications with respect to their Application
- Ids. As such, there is no versioning support provided by these
- application Ids itself; every Diameter application is a standalone
- application. If the application has a relationship with other
- Diameter applications, such a relationship is not known to Diameter.
-
- Before describing the rules for creating new Diameter applications it
- is important to discuss the semantics of the AVPs occurrences as
- stated in the ABNF and the M-bit flag (Section 4.1) for an AVP.
- There is no relationship imposed between the two; they are set
+ leads to the need to define a new Diameter application for any
+ application that will use that new command.
+
+ The IANA considerations for Command Codes are discussed in
+ Section 3.1.
+
+1.3.4. Creating New Diameter Applications
+
+ Every Diameter application specification MUST have an IANA-assigned
+ Application Id (see Section 2.4). The managed Application ID space
+ is flat, and there is no relationship between different Diameter
+ applications with respect to their Application Ids. As such, there
+ is no versioning support provided by these Application Ids
+ themselves; every Diameter application is a standalone application.
+ If the application has a relationship with other Diameter
+ applications, such a relationship is not known to Diameter.
+
+ Before describing the rules for creating new Diameter applications,
+ it is important to discuss the semantics of the AVP occurrences as
+ stated in the CCF and the M-bit flag (Section 4.1) for an AVP. There
+ is no relationship imposed between the two; they are set
independently.
- o The ABNF indicates what AVPs are placed into a Diameter Command by
- the sender of that Command. Often, since there are multiple modes
- of protocol interactions many of the AVPs are indicated as
+ o The CCF indicates what AVPs are placed into a Diameter command by
+ the sender of that command. Often, since there are multiple modes
+ of protocol interactions, many of the AVPs are indicated as
optional.
o The M-bit allows the sender to indicate to the receiver whether or
not understanding the semantics of an AVP and its content is
mandatory. If the M-bit is set by the sender and the receiver
- does not understand the AVP or the values carried within that AVP
+ does not understand the AVP or the values carried within that AVP,
then a failure is generated (see Section 7).
It is the decision of the protocol designer when to develop a new
@@ -1050,77 +1058,48 @@ Internet-Draft Diameter Base Protocol January 2011
of the following criteria are met:
- M-bit Setting
- An AVP with the M-bit in the MUST column of the AVP flag table is
- added to an existing Command/Application.
- An AVP with the M-bit in the MAY column of the AVP flag table is
- added to an existing Command/Application.
-Fajardo, et al. Expires July 24, 2011 [Page 19]
+Fajardo, et al. Standards Track [Page 19]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
+
+ M-bit Setting
+
+ An AVP with the M-bit in the MUST column of the AVP flag table is
+ added to an existing Command/Application. An AVP with the M-bit
+ in the MAY column of the AVP flag table is added to an existing
+ Command/Application.
Note: The M-bit setting for a given AVP is relevant to an
- Application and each command within that application which
- includes the AVP. That is, if an AVP appears in two commands for
+ Application and each command within that application that includes
+ the AVP. That is, if an AVP appears in two commands for
application Foo and the M-bit settings are different in each
command, then there should be two AVP flag tables describing when
to set the M-bit.
Commands
- A new command is used within the existing application either
- because an additional command is added, an existing command has
+ A new command is used within the existing application because
+ either an additional command is added, an existing command has
been modified so that a new Command Code had to be registered, or
a command has been deleted.
- If the ABNF definition of a command allows it, an implementation may
+ AVP Flag bits
+
+ If an existing application changes the meaning/semantics of its
+ AVP Flags or adds new flag bits, then a new Diameter application
+ MUST be created.
+
+ If the CCF definition of a command allows it, an implementation may
add arbitrary optional AVPs with the M-bit cleared (including vendor-
specific AVPs) to that command without needing to define a new
application. Please refer to Section 11.1.1 for details.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 20]
-
-Internet-Draft Diameter Base Protocol January 2011
-
-
2. Protocol Overview
The base Diameter protocol concerns itself with establishing
@@ -1137,9 +1116,17 @@ Internet-Draft Diameter Base Protocol January 2011
The initial request for authentication and/or authorization of a user
would include the Session-Id AVP. The Session-Id is then used in all
subsequent messages to identify the user's session (see Section 8 for
- more information). The communicating party may accept the request,
- or reject it by returning an answer message with the Result-Code AVP
- set to indicate an error occurred. The specific behavior of the
+
+
+
+Fajardo, et al. Standards Track [Page 20]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ more information). The communicating party may accept the request or
+ reject it by returning an answer message with the Result-Code AVP set
+ to indicate that an error occurred. The specific behavior of the
Diameter server or client receiving a request depends on the Diameter
application employed.
@@ -1153,30 +1140,23 @@ Internet-Draft Diameter Base Protocol January 2011
applications. For authentication and authorization, it is always
extended for a particular application.
- Diameter Clients MUST support the base protocol, which includes
+ Diameter clients MUST support the base protocol, which includes
accounting. In addition, they MUST fully support each Diameter
application that is needed to implement the client's service, e.g.,
- NASREQ and/or Mobile IPv4. A Diameter Client MUST be referred to as
- "Diameter X Client" where X is the application which it supports, and
- not a "Diameter Client".
+ Network Access Server Requirements (NASREQ) [RFC2881] and/or Mobile
+ IPv4. A Diameter client MUST be referred to as "Diameter X Client"
+ where X is the application that it supports and not a "Diameter
+ Client".
- Diameter Servers MUST support the base protocol, which includes
+ Diameter servers MUST support the base protocol, which includes
accounting. In addition, they MUST fully support each Diameter
application that is needed to implement the intended service, e.g.,
- NASREQ and/or Mobile IPv4. A Diameter Server MUST be referred to as
- "Diameter X Server" where X is the application which it supports, and
+ NASREQ and/or Mobile IPv4. A Diameter server MUST be referred to as
+ "Diameter X Server" where X is the application that it supports, and
not a "Diameter Server".
- Diameter Relays and redirect agents are transparent to the Diameter
- applications but they MUST support the Diameter base protocol, which
-
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 21]
-
-Internet-Draft Diameter Base Protocol January 2011
-
-
+ Diameter relays and redirect agents are transparent to the Diameter
+ applications, but they MUST support the Diameter base protocol, which
includes accounting, and all Diameter applications.
Diameter proxies MUST support the base protocol, which includes
@@ -1186,55 +1166,75 @@ Internet-Draft Diameter Base Protocol January 2011
"Diameter X Proxy" where X is the application which it supports, and
not a "Diameter Proxy".
+
+
+
+
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 21]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
2.1. Transport
The Diameter Transport profile is defined in [RFC3539].
- The base Diameter protocol is run on port 3868 for both TCP [RFC793]
- and SCTP [RFC4960]. For TLS [RFC5246] and DTLS [RFC4347], a Diameter
- node that initiate a connection prior to any message exchanges MUST
- run on port [TBD]. It is assumed that TLS is run on top of TCP when
- it is used and DTLS is run on top of SCTP when it is used.
+ The base Diameter protocol is run on port 3868 for both TCP [RFC0793]
+ and SCTP [RFC4960]. For TLS [RFC5246] and Datagram Transport Layer
+ Security (DTLS) [RFC6347], a Diameter node that initiates a
+ connection prior to any message exchanges MUST run on port 5658. It
+ is assumed that TLS is run on top of TCP when it is used, and DTLS is
+ run on top of SCTP when it is used.
If the Diameter peer does not support receiving TLS/TCP and DTLS/SCTP
- connections on port [TBD], i.e. the peer complies only with
- [RFC3588], then the initiator MAY revert to using TCP or SCTP and on
- port 3868. Note that this scheme is kept for the purpose of
- backwards compatibility only and that there are inherent security
- vulnerabilities when the initial CER/CEA messages are sent un-
- protected (see Section 5.6).
+ connections on port 5658 (i.e., the peer complies only with RFC
+ 3588), then the initiator MAY revert to using TCP or SCTP on port
+ 3868. Note that this scheme is kept only for the purpose of backward
+ compatibility and that there are inherent security vulnerabilities
+ when the initial CER/CEA messages are sent unprotected (see
+ Section 5.6).
- Diameter clients MUST support either TCP or SCTP, while agents and
- servers SHOULD support both.
+ Diameter clients MUST support either TCP or SCTP; agents and servers
+ SHOULD support both.
A Diameter node MAY initiate connections from a source port other
than the one that it declares it accepts incoming connections on, and
- MUST be prepared to receive connections on port 3868 for TCP or SCTP
- and port [TBD] for TLS/TCP and DTLS/SCTP connections. A given
- Diameter instance of the peer state machine MUST NOT use more than
- one transport connection to communicate with a given peer, unless
- multiple instances exist on the peer in which case a separate
- connection per process is allowed.
+ it MUST always be prepared to receive connections on port 3868 for
+ TCP or SCTP and port 5658 for TLS/TCP and DTLS/SCTP connections.
+ When DNS-based peer discovery (Section 5.2) is used, the port numbers
+ received from SRV records take precedence over the default ports
+ (3868 and 5658).
+
+ A given Diameter instance of the peer state machine MUST NOT use more
+ than one transport connection to communicate with a given peer,
+ unless multiple instances exist on the peer, in which, case a
+ separate connection per process is allowed.
When no transport connection exists with a peer, an attempt to
- connect SHOULD be periodically made. This behavior is handled via
+ connect SHOULD be made periodically. This behavior is handled via
the Tc timer (see Section 12 for details), whose recommended value is
30 seconds. There are certain exceptions to this rule, such as when
a peer has terminated the transport connection stating that it does
not wish to communicate.
When connecting to a peer and either zero or more transports are
- specified, TLS SHOULD be tried first, followed by DTLS, then by TCP
+ specified, TLS SHOULD be tried first, followed by DTLS, then by TCP,
+ and finally by SCTP. See Section 5.2 for more information on peer
+ discovery.
-Fajardo, et al. Expires July 24, 2011 [Page 22]
-
-Internet-Draft Diameter Base Protocol January 2011
- and finally by SCTP. See Section 5.2 for more information on peer
- discovery.
+Fajardo, et al. Standards Track [Page 22]
+
+RFC 6733 Diameter Base Protocol October 2012
+
Diameter implementations SHOULD be able to interpret ICMP protocol
port unreachable messages as explicit indications that the server is
@@ -1253,18 +1253,18 @@ Internet-Draft Diameter Base Protocol January 2011
Diameter messages SHOULD be mapped into SCTP streams in a way that
avoids head-of-the-line (HOL) blocking. Among different ways of
performing the mapping that fulfill this requirement it is
- RECOMMENDED that a Diameter node sends every Diameter message
- (request or response) over the stream zero with the unordered flag
- set. However, Diameter nodes MAY select and implement other design
- alternatives for avoiding HOL blocking such as using multiple streams
- with the unordered flag cleared (as originally instructed in
- RFC3588). On the receiving side, a Diameter entity MUST be ready to
- receive Diameter messages over any stream and it is free to return
- responses over a different stream. This way, both sides manage the
- available streams in the sending direction, independently of the
- streams chosen by the other side to send a particular Diameter
- message. These messages can be out-of-order and belong to different
- Diameter sessions.
+ RECOMMENDED that a Diameter node send every Diameter message (request
+ or response) over stream zero with the unordered flag set. However,
+ Diameter nodes MAY select and implement other design alternatives for
+ avoiding HOL blocking such as using multiple streams with the
+ unordered flag cleared (as originally instructed in RFC 3588). On
+ the receiving side, a Diameter entity MUST be ready to receive
+ Diameter messages over any stream, and it is free to return responses
+ over a different stream. This way, both sides manage the available
+ streams in the sending direction, independently of the streams chosen
+ by the other side to send a particular Diameter message. These
+ messages can be out-of-order and belong to different Diameter
+ sessions.
Out-of-order delivery has special concerns during a connection
establishment and termination. When a connection is established, the
@@ -1275,30 +1275,37 @@ Internet-Draft Diameter Base Protocol January 2011
the connection. In order to avoid this race condition, the receiver
side SHOULD NOT use out-of-order delivery methods until the first
message has been received from the initiator, proving that it has
- moved to I-Open state. To trigger such message, the receiver side
- could send a DWR immediatly after sending CEA. Upon reception of the
- corresponding DWA, the receiver side should start using out-of-order
- delivery methods to counter the HOL blocking.
+ moved to I-Open state. To trigger such a message, the receiver side
+ could send a DWR immediately after sending a CEA. Upon reception of
+ the corresponding DWA, the receiver side should start using out-of-
+ order delivery methods to counter the HOL blocking.
Another race condition may occur when DPR and DPA messages are used.
+ Both DPR and DPA are small in size; thus, they may be delivered to
+ the peer faster than application messages when an out-of-order
+ delivery mechanism is used. Therefore, it is possible that a DPR/DPA
-Fajardo, et al. Expires July 24, 2011 [Page 23]
+Fajardo, et al. Standards Track [Page 23]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
+
+ exchange completes while application messages are still in transit,
+ resulting in a loss of these messages. An implementation could
+ mitigate this race condition, for example, using timers, and wait for
+ a short period of time for pending application level messages to
+ arrive before proceeding to disconnect the transport connection.
+ Eventually, lost messages are handled by the retransmission mechanism
+ described in Section 5.5.4.
- Both DPR and DPA are small in size, thus they may be delivered faster
- to the peer than application messages when out-of-order delivery
- mechanism is used. Therefore, it is possible that a DPR/DPA exchange
- completes while application messages are still in transit, resulting
- to a loss of these messages. An implementation could mitigate this
- race condition, for example, using timers and wait for a short period
- of time for pending application level messages to arrive before
- proceeding to disconnect the transport connection. Eventually, lost
- messages are handled by the retransmission mechanism described in
- Section 5.5.4.
+ A Diameter agent SHOULD use dedicated payload protocol identifiers
+ (PPIDs) for clear text and encrypted SCTP DATA chunks instead of only
+ using the unspecified payload protocol identifier (value 0). For
+ this purpose, two PPID values are allocated: the PPID value 46 is for
+ Diameter messages in clear text SCTP DATA chunks, and the PPID value
+ 47 is for Diameter messages in protected DTLS/SCTP DATA chunks.
2.2. Securing Diameter Messages
@@ -1307,7 +1314,7 @@ Internet-Draft Diameter Base Protocol January 2011
the use of TLS/TCP and DTLS/SCTP. If desired, alternative security
mechanisms that are independent of Diameter, such as IPsec [RFC4301],
can be deployed to secure connections between peers. The Diameter
- protocol MUST NOT be used without any security mechanism.
+ protocol MUST NOT be used without one of TLS, DTLS, or IPsec.
2.3. Diameter Application Compliance
@@ -1318,37 +1325,40 @@ Internet-Draft Diameter Base Protocol January 2011
Implementations MAY add arbitrary optional AVPs with the M-bit
cleared (including vendor-specific AVPs) to a command defined in an
- application, but only if the command's ABNF syntax specification
+ application, but only if the command's CCF syntax specification
allows for it. Please refer to Section 11.1.1 for details.
2.4. Application Identifiers
- Each Diameter application MUST have an IANA assigned Application Id
- (see Section 11.3). The base protocol does not require an
- Application Id since its support is mandatory. During the
- capabilities exchange, Diameter nodes inform their peers of locally
- supported applications. Furthermore, all Diameter messages contain
- an Application Id, which is used in the message forwarding process.
+ Each Diameter application MUST have an IANA-assigned Application ID.
+ The base protocol does not require an Application Id since its
+ support is mandatory. During the capabilities exchange, Diameter
+ nodes inform their peers of locally supported applications.
+ Furthermore, all Diameter messages contain an Application Id, which
+ is used in the message forwarding process.
- The following Application Id values are defined:
- Diameter Common Messages 0
- Diameter Base Accounting 3
- Relay 0xffffffff
- Relay and redirect agents MUST advertise the Relay Application
-Fajardo, et al. Expires July 24, 2011 [Page 24]
+
+Fajardo, et al. Standards Track [Page 24]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
+
+ The following Application Id values are defined:
+
+ Diameter common message 0
+ Diameter base accounting 3
+ Relay 0xffffffff
- Identifier, while all other Diameter nodes MUST advertise locally
- supported applications. The receiver of a Capabilities Exchange
- message advertising Relay service MUST assume that the sender
- supports all current and future applications.
+ Relay and redirect agents MUST advertise the Relay Application ID,
+ while all other Diameter nodes MUST advertise locally supported
+ applications. The receiver of a Capabilities Exchange message
+ advertising relay service MUST assume that the sender supports all
+ current and future applications.
Diameter relay and proxy agents are responsible for finding an
upstream server that supports the application of a particular
@@ -1358,16 +1368,15 @@ Internet-Draft Diameter Base Protocol January 2011
2.5. Connections vs. Sessions
This section attempts to provide the reader with an understanding of
- the difference between connection and session, which are terms used
- extensively throughout this document.
+ the difference between "connection" and "session", which are terms
+ used extensively throughout this document.
- A connection refers to a transport level connection between two peers
+ A connection refers to a transport-level connection between two peers
that is used to send and receive Diameter messages. A session is a
- logical concept at the application layer existing between the
+ logical concept at the application layer that exists between the
Diameter client and the Diameter server; it is identified via the
Session-Id AVP.
-
+--------+ +-------+ +--------+
| Client | | Relay | | Server |
+--------+ +-------+ +--------+
@@ -1377,100 +1386,93 @@ Internet-Draft Diameter Base Protocol January 2011
<----------------------------->
User session x
- Figure 1: Diameter connections and sessions
+ Figure 1: Diameter Connections and Sessions
In the example provided in Figure 1, peer connection A is established
- between the Client and the Relay. Peer connection B is established
- between the Relay and the Server. User session X spans from the
- Client via the Relay to the Server. Each "user" of a service causes
+ between the client and the relay. Peer connection B is established
+ between the relay and the server. User session X spans from the
+ client via the relay to the server. Each "user" of a service causes
an auth request to be sent, with a unique session identifier. Once
accepted by the server, both the client and the server are aware of
the session.
- It is important to note that there is no relationship between a
- connection and a session, and that Diameter messages for multiple
- sessions are all multiplexed through a single connection. Also note
- that Diameter messages pertaining to the session, both application
- specific and those that are defined in this document such as ASR/ASA,
- RAR/RAA and STR/STA MUST carry the Application Id of the application.
-Fajardo, et al. Expires July 24, 2011 [Page 25]
+Fajardo, et al. Standards Track [Page 25]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
- Diameter messages pertaining to peer connection establishment and
- maintenance such as CER/CEA, DWR/DWA and DPR/DPA MUST carry an
- Application Id of zero (0).
+ It is important to note that there is no relationship between a
+ connection and a session, and that Diameter messages for multiple
+ sessions are all multiplexed through a single connection. Also, note
+ that Diameter messages pertaining to the session, both application-
+ specific and those that are defined in this document such as ASR/ASA,
+ RAR/RAA, and STR/STA, MUST carry the Application Id of the
+ application. Diameter messages pertaining to peer connection
+ establishment and maintenance such as CER/CEA, DWR/DWA, and DPR/DPA
+ MUST carry an Application Id of zero (0).
2.6. Peer Table
- The Diameter Peer Table is used in message forwarding, and referenced
- by the Routing Table. A Peer Table entry contains the following
- fields:
+ The Diameter peer table is used in message forwarding and is
+ referenced by the routing table. A peer table entry contains the
+ following fields:
- Host identity
+ Host Identity
- Following the conventions described for the DiameterIdentity
- derived AVP data format in Section 4.3. This field contains the
+ Following the conventions described for the DiameterIdentity-
+ derived AVP data format in Section 4.3.1, this field contains the
contents of the Origin-Host (Section 6.3) AVP found in the CER or
CEA message.
-
StatusT
- This is the state of the peer entry, and MUST match one of the
+ This is the state of the peer entry, and it MUST match one of the
values listed in Section 5.6.
-
Static or Dynamic
Specifies whether a peer entry was statically configured or
dynamically discovered.
-
- Expiration time
+ Expiration Time
Specifies the time at which dynamically discovered peer table
- entries are to be either refreshed, or expired.
-
+ entries are to be either refreshed or expired. If public key
+ certificates are used for Diameter security (e.g., with TLS), this
+ value MUST NOT be greater than the expiry times in the relevant
+ certificates.
TLS/TCP and DTLS/SCTP Enabled
Specifies whether TLS/TCP and DTLS/SCTP is to be used when
communicating with the peer.
-
Additional security information, when needed (e.g., keys,
- certificates)
-
-
+ certificates).
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 26]
+Fajardo, et al. Standards Track [Page 26]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
2.7. Routing Table
All Realm-Based routing lookups are performed against what is
- commonly known as the Routing Table (see Section 12). A Routing
- Table Entry contains the following fields:
+ commonly known as the routing table (see Section 12). Each routing
+ table entry contains the following fields:
Realm Name
- This is the field that is MUST be used as a primary key in the
+ This is the field that MUST be used as a primary key in the
routing table lookups. Note that some implementations perform
their lookups based on longest-match-from-the-right on the realm
rather than requiring an exact match.
-
Application Identifier
An application is identified by an Application Id. A route entry
@@ -1478,21 +1480,19 @@ Internet-Draft Diameter Base Protocol January 2011
the message header. This field MUST be used as a secondary key
field in routing table lookups.
-
Local Action
The Local Action field is used to identify how a message should be
treated. The following actions are supported:
-
- 1. LOCAL - Diameter messages that can be satisfied locally, and
- do not need to be routed to another Diameter entity.
+ 1. LOCAL - Diameter messages that can be satisfied locally and do
+ not need to be routed to another Diameter entity.
2. RELAY - All Diameter messages that fall within this category
- MUST be routed to a next hop Diameter entity that is indicated
+ MUST be routed to a next-hop Diameter entity that is indicated
by the identifier described below. Routing is done without
modifying any non-routing AVPs. See Section 6.1.9 for
- relaying guidelines
+ relaying guidelines.
3. PROXY - All Diameter messages that fall within this category
MUST be routed to a next Diameter entity that is indicated by
@@ -1504,47 +1504,53 @@ Internet-Draft Diameter Base Protocol January 2011
4. REDIRECT - Diameter messages that fall within this category
MUST have the identity of the home Diameter server(s)
appended, and returned to the sender of the message. See
- Section 6.1.8 for redirect guidelines.
+ Section 6.1.8 for redirection guidelines.
+
+
+
-Fajardo, et al. Expires July 24, 2011 [Page 27]
+Fajardo, et al. Standards Track [Page 27]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
Server Identifier
- One or more servers to which the message is to be routed. These
- servers MUST also be present in the Peer table. When the Local
- Action is set to RELAY or PROXY, this field contains the identity
- of the server(s) the message MUST be routed to. When the Local
+ The identity of one or more servers to which the message is to be
+ routed. This identity MUST also be present in the Host Identity
+ field of the peer table (Section 2.6). When the Local Action is
+ set to RELAY or PROXY, this field contains the identity of the
+ server(s) to which the message MUST be routed. When the Local
Action field is set to REDIRECT, this field contains the identity
- of one or more servers the message MUST be redirected to.
+ of one or more servers to which the message MUST be redirected.
Static or Dynamic
Specifies whether a route entry was statically configured or
dynamically discovered.
- Expiration time
+ Expiration Time
Specifies the time at which a dynamically discovered route table
- entry expires.
+ entry expires. If public key certificates are used for Diameter
+ security (e.g., with TLS), this value MUST NOT be greater than the
+ expiry time in the relevant certificates.
It is important to note that Diameter agents MUST support at least
- one of the LOCAL, RELAY, PROXY or REDIRECT modes of operation.
+ one of the LOCAL, RELAY, PROXY, or REDIRECT modes of operation.
Agents do not need to support all modes of operation in order to
- conform with the protocol specification, but MUST follow the protocol
- compliance guidelines in Section 2. Relay agents and proxies MUST
- NOT reorder AVPs.
+ conform with the protocol specification, but they MUST follow the
+ protocol compliance guidelines in Section 2. Relay agents and
+ proxies MUST NOT reorder AVPs.
The routing table MAY include a default entry that MUST be used for
any requests not matching any of the other entries. The routing
table MAY consist of only such an entry.
When a request is routed, the target server MUST have advertised the
- Application Id (see Section 2.4) for the given message, or have
+ Application Id (see Section 2.4) for the given message or have
advertised itself as a relay or proxy agent. Otherwise, an error is
returned with the Result-Code AVP set to DIAMETER_UNABLE_TO_DELIVER.
@@ -1552,23 +1558,24 @@ Internet-Draft Diameter Base Protocol January 2011
In addition to clients and servers, the Diameter protocol introduces
relay, proxy, redirect, and translation agents, each of which is
- defined in Section 1.3. These Diameter agents are useful for several
+ defined in Section 1.2. Diameter agents are useful for several
reasons:
o They can distribute administration of systems to a configurable
grouping, including the maintenance of security associations.
- o They can be used for concentration of requests from an number of
- co-located or distributed NAS equipment sets to a set of like user
- groups.
-Fajardo, et al. Expires July 24, 2011 [Page 28]
+Fajardo, et al. Standards Track [Page 28]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
+ o They can be used for concentration of requests from a number of
+ co-located or distributed NAS equipment sets to a set of like user
+ groups.
+
o They can do value-added processing to the requests or responses.
o They can be used for load balancing.
@@ -1578,7 +1585,7 @@ Internet-Draft Diameter Base Protocol January 2011
The Diameter protocol requires that agents maintain transaction
state, which is used for failover purposes. Transaction state
- implies that upon forwarding a request, its Hop-by-Hop identifier is
+ implies that upon forwarding a request, its Hop-by-Hop Identifier is
saved; the field is replaced with a locally unique identifier, which
is restored to its original value when the corresponding answer is
received. The request's state is released upon receipt of the
@@ -1593,7 +1600,7 @@ Internet-Draft Diameter Base Protocol January 2011
A stateful agent is one that maintains session state information by
keeping track of all authorized active sessions. Each authorized
session is bound to a particular service, and its state is considered
- active either until the agent is notified otherwise, or the session
+ active until either the agent is notified otherwise or the session
expires. Each authorized session has an expiration, which is
communicated by Diameter servers via the Session-Timeout AVP.
@@ -1604,40 +1611,43 @@ Internet-Draft Diameter Base Protocol January 2011
o Limiting resources authorized to a particular user
- o Per user or transaction auditing
+ o Per-user or per-transaction auditing
A Diameter agent MAY act in a stateful manner for some requests and
be stateless for others. A Diameter implementation MAY act as one
- type of agent for some requests, and as another type of agent for
+ type of agent for some requests and as another type of agent for
others.
-2.8.1. Relay Agents
- Relay Agents are Diameter agents that accept requests and route
- messages to other Diameter nodes based on information found in the
- messages (e.g., Destination-Realm). This routing decision is
- performed using a list of supported realms, and known peers. This is
-Fajardo, et al. Expires July 24, 2011 [Page 29]
+
+Fajardo, et al. Standards Track [Page 29]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
+
+2.8.1. Relay Agents
- known as the Routing Table, as is defined further in Section 2.7.
+ Relay agents are Diameter agents that accept requests and route
+ messages to other Diameter nodes based on information found in the
+ messages (e.g., the value of the Destination-Realm AVP Section 6.6).
+ This routing decision is performed using a list of supported realms
+ and known peers. This is known as the routing table, as is defined
+ further in Section 2.7.
Relays may, for example, be used to aggregate requests from multiple
Network Access Servers (NASes) within a common geographical area
- (POP). The use of Relays is advantageous since it eliminates the
- need for NASes to be configured with the necessary security
- information they would otherwise require to communicate with Diameter
- servers in other realms. Likewise, this reduces the configuration
- load on Diameter servers that would otherwise be necessary when NASes
- are added, changed or deleted.
+ (Point of Presence, POP). The use of relays is advantageous since it
+ eliminates the need for NASes to be configured with the necessary
+ security information they would otherwise require to communicate with
+ Diameter servers in other realms. Likewise, this reduces the
+ configuration load on Diameter servers that would otherwise be
+ necessary when NASes are added, changed, or deleted.
Relays modify Diameter messages by inserting and removing routing
- information, but do not modify any other portion of a message.
+ information, but they do not modify any other portion of a message.
Relays SHOULD NOT maintain session state but MUST maintain
transaction state.
@@ -1650,44 +1660,46 @@ Internet-Draft Diameter Base Protocol January 2011
Figure 2: Relaying of Diameter messages
- The example provided in Figure 2 depicts a request issued from NAS,
+ The example provided in Figure 2 depicts a request issued from a NAS,
which is an access device, for the user [email protected]. Prior to
- issuing the request, NAS performs a Diameter route lookup, using
+ issuing the request, the NAS performs a Diameter route lookup, using
"example.com" as the key, and determines that the message is to be
- relayed to DRL, which is a Diameter Relay. DRL performs the same
- route lookup as NAS, and relays the message to HMS, which is
- example.com's Home Diameter Server. HMS identifies that the request
- can be locally supported (via the realm), processes the
+ relayed to a DRL, which is a Diameter relay. The DRL performs the
+ same route lookup as the NAS, and relays the message to the HMS,
+ which is example.com's home server. The HMS identifies that the
+ request can be locally supported (via the realm), processes the
authentication and/or authorization request, and replies with an
- answer, which is routed back to NAS using saved transaction state.
-
- Since Relays do not perform any application level processing, they
- provide relaying services for all Diameter applications, and
- therefore MUST advertise the Relay Application Id.
+ answer, which is routed back to the NAS using saved transaction
+ state.
-2.8.2. Proxy Agents
+ Since relays do not perform any application-level processing, they
+ provide relaying services for all Diameter applications; therefore,
+ they MUST advertise the Relay Application Id.
- Similarly to relays, proxy agents route Diameter messages using the
- Diameter Routing Table. However, they differ since they modify
- messages to implement policy enforcement. This requires that proxies
- maintain the state of their downstream peers (e.g., access devices)
- to enforce resource usage, provide admission control, and
- provisioning.
-Fajardo, et al. Expires July 24, 2011 [Page 30]
+Fajardo, et al. Standards Track [Page 30]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
+
+2.8.2. Proxy Agents
+
+ Similar to relays, proxy agents route Diameter messages using the
+ Diameter routing table. However, they differ since they modify
+ messages to implement policy enforcement. This requires that proxies
+ maintain the state of their downstream peers (e.g., access devices)
+ to enforce resource usage, provide admission control, and provide
+ provisioning.
Proxies may, for example, be used in call control centers or access
- ISPs that provide outsourced connections, they can monitor the number
- and types of ports in use, and make allocation and admission
- decisions according to their configuration.
+ ISPs that provide outsourced connections; they can monitor the number
+ and type of ports in use and make allocation and admission decisions
+ according to their configuration.
Since enforcing policies requires an understanding of the service
- being provided, Proxies MUST only advertise the Diameter applications
+ being provided, proxies MUST only advertise the Diameter applications
they support.
2.8.3. Redirect Agents
@@ -1709,18 +1721,12 @@ Internet-Draft Diameter Base Protocol January 2011
The example provided in Figure 3 depicts a request issued from the
access device, NAS, for the user [email protected]. The message is
forwarded by the NAS to its relay, DRL, which does not have a routing
- entry in its Diameter Routing Table for example.com. DRL has a
+ entry in its Diameter routing table for example.com. The DRL has a
default route configured to DRD, which is a redirect agent that
- returns a redirect notification to DRL, as well as HMS' contact
- information. Upon receipt of the redirect notification, DRL
- establishes a transport connection with HMS, if one doesn't already
- exist, and forwards the request to it.
-
-
-
-
-
-
+ returns a redirect notification to DRL, as well as the HMS' contact
+ information. Upon receipt of the redirect notification, the DRL
+ establishes a transport connection with the HMS, if one doesn't
+ already exist, and forwards the request to it.
@@ -1729,12 +1735,9 @@ Internet-Draft Diameter Base Protocol January 2011
-
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 31]
+Fajardo, et al. Standards Track [Page 31]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
+------+
@@ -1755,10 +1758,10 @@ Internet-Draft Diameter Base Protocol January 2011
Figure 3: Redirecting a Diameter Message
- Since redirect agents do not perform any application level
+ Since redirect agents do not perform any application-level
processing, they provide relaying services for all Diameter
- applications, and therefore MUST advertise the Relay Application
- Identifier.
+ applications; therefore, they MUST advertise the Relay Application
+ ID.
2.8.4. Translation Agents
@@ -1773,7 +1776,7 @@ Internet-Draft Diameter Base Protocol January 2011
MUST maintain transaction state.
Translation of messages can only occur if the agent recognizes the
- application of a particular request, and therefore translation agents
+ application of a particular request; therefore, translation agents
MUST only advertise their locally supported applications.
+------+ ---------> +------+ ---------> +------+
@@ -1788,23 +1791,24 @@ Internet-Draft Diameter Base Protocol January 2011
-Fajardo, et al. Expires July 24, 2011 [Page 32]
+Fajardo, et al. Standards Track [Page 32]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
2.9. Diameter Path Authorization
- As noted in Section 2.2, Diameter provides transmission level
+ As noted in Section 2.2, Diameter provides transmission-level
security for each connection using TLS/TCP and DTLS/SCTP. Therefore,
- each connection can be authenticated, replay and integrity protected.
+ each connection can be authenticated and can be replay and integrity
+ protected.
- In addition to authenticating each connection, each connection as
- well as the entire session MUST also be authorized. Before
- initiating a connection, a Diameter Peer MUST check that its peers
- are authorized to act in their roles. For example, a Diameter peer
- may be authentic, but that does not mean that it is authorized to act
- as a Diameter Server advertising a set of Diameter applications.
+ In addition to authenticating each connection, the entire session
+ MUST also be authorized. Before initiating a connection, a Diameter
+ peer MUST check that its peers are authorized to act in their roles.
+ For example, a Diameter peer may be authentic, but that does not mean
+ that it is authorized to act as a Diameter server advertising a set
+ of Diameter applications.
Prior to bringing up a connection, authorization checks are performed
at each connection along the path. Diameter capabilities negotiation
@@ -1815,7 +1819,7 @@ Internet-Draft Diameter Base Protocol January 2011
As noted in Section 6.1.9, a relay or proxy agent MUST append a
Route-Record AVP to all requests forwarded. The AVP contains the
- identity of the peer the request was received from.
+ identity of the peer from which the request was received.
The home Diameter server, prior to authorizing a session, MUST check
the Route-Record AVPs to make sure that the route traversed by the
@@ -1823,7 +1827,7 @@ Internet-Draft Diameter Base Protocol January 2011
realm may not wish to honor requests that have been routed through an
untrusted realm. By authorizing a request, the home Diameter server
is implicitly indicating its willingness to engage in the business
- transaction as specified by the contractual relationship between the
+ transaction as specified by any contractual relationship between the
server and the previous hop. A DIAMETER_AUTHORIZATION_REJECTED error
message (see Section 7.1.5) is sent if the route traversed by the
request is unacceptable.
@@ -1839,72 +1843,24 @@ Internet-Draft Diameter Base Protocol January 2011
willingness to take on financial risk relative to the session. A
local realm may wish to limit this exposure, for example, by
establishing credit limits for intermediate realms and refusing to
- accept responses which would violate those limits. By issuing an
- accounting request corresponding to the authorization response, the
+ accept responses that would violate those limits. By issuing an
-Fajardo, et al. Expires July 24, 2011 [Page 33]
+Fajardo, et al. Standards Track [Page 33]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
+ accounting request corresponding to the authorization response, the
local realm implicitly indicates its agreement to provide the service
indicated in the authorization response. If the service cannot be
provided by the local realm, then a DIAMETER_UNABLE_TO_COMPLY error
message MUST be sent within the accounting request; a Diameter client
receiving an authorization response for a service that it cannot
- perform MUST NOT substitute an alternate service, and then send
+ perform MUST NOT substitute an alternate service and then send
accounting requests for the alternate service instead.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 34]
-
-Internet-Draft Diameter Base Protocol January 2011
-
-
3. Diameter Header
A summary of the Diameter header format is shown below. The fields
@@ -1915,7 +1871,7 @@ Internet-Draft Diameter Base Protocol January 2011
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Version | Message Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | command flags | Command-Code |
+ | Command Flags | Command Code |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Application-ID |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
@@ -1935,13 +1891,23 @@ Internet-Draft Diameter Base Protocol January 2011
The Message Length field is three octets and indicates the length
of the Diameter message including the header fields and the padded
- AVPs. Thus the message length field is always a multiple of 4.
+ AVPs. Thus, the Message Length field is always a multiple of 4.
Command Flags
The Command Flags field is eight bits. The following bits are
assigned:
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 34]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+
|R P E T r r r r|
@@ -1952,127 +1918,113 @@ Internet-Draft Diameter Base Protocol January 2011
If set, the message is a request. If cleared, the message is
an answer.
-
-
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 35]
-
-Internet-Draft Diameter Base Protocol January 2011
-
-
P(roxiable)
- If set, the message MAY be proxied, relayed or redirected. If
+ If set, the message MAY be proxied, relayed, or redirected. If
cleared, the message MUST be locally processed.
-
E(rror)
If set, the message contains a protocol error, and the message
- will not conform to the ABNF described for this command.
+ will not conform to the CCF described for this command.
Messages with the 'E' bit set are commonly referred to as error
- messages. This bit MUST NOT be set in request messages. See
- Section 7.2.
+ messages. This bit MUST NOT be set in request messages (see
+ Section 7.2).
-
- T(Potentially re-transmitted message)
+ T(Potentially retransmitted message)
This flag is set after a link failover procedure, to aid the
removal of duplicate requests. It is set when resending
requests not yet acknowledged, as an indication of a possible
duplicate due to a link failure. This bit MUST be cleared when
- sending a request for the first time, otherwise the sender MUST
- set this flag. Diameter agents only need to be concerned about
- the number of requests they send based on a single received
- request; retransmissions by other entities need not be tracked.
- Diameter agents that receive a request with the T flag set,
- MUST keep the T flag set in the forwarded request. This flag
- MUST NOT be set if an error answer message (e.g., a protocol
- error) has been received for the earlier message. It can be
- set only in cases where no answer has been received from the
- server for a request and the request is sent again. This flag
- MUST NOT be set in answer messages.
-
+ sending a request for the first time; otherwise, the sender
+ MUST set this flag. Diameter agents only need to be concerned
+ about the number of requests they send based on a single
+ received request; retransmissions by other entities need not be
+ tracked. Diameter agents that receive a request with the T
+ flag set, MUST keep the T flag set in the forwarded request.
+ This flag MUST NOT be set if an error answer message (e.g., a
+ protocol error) has been received for the earlier message. It
+ can be set only in cases where no answer has been received from
+ the server for a request, and the request has been sent again.
+ This flag MUST NOT be set in answer messages.
r(eserved)
- These flag bits are reserved for future use, and MUST be set to
- zero, and ignored by the receiver.
+ These flag bits are reserved for future use; they MUST be set
+ to zero and ignored by the receiver.
- Command-Code
- The Command-Code field is three octets, and is used in order to
- communicate the command associated with the message. The 24-bit
- address space is managed by IANA (see Section 11.2.1).
- Command-Code values 16,777,214 and 16,777,215 (hexadecimal values
- FFFFFE -FFFFFF) are reserved for experimental use (See Section
- 11.3).
-Fajardo, et al. Expires July 24, 2011 [Page 36]
+Fajardo, et al. Standards Track [Page 35]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ Command Code
+ The Command Code field is three octets and is used in order to
+ communicate the command associated with the message. The 24-bit
+ address space is managed by IANA (see Section 3.1). Command Code
+ values 16,777,214 and 16,777,215 (hexadecimal values FFFFFE-
+ FFFFFF) are reserved for experimental use (see Section 11.2).
- Application-ID
+ Application-ID
- Application-ID is four octets and is used to identify to which
- application the message is applicable for. The application can be
- an authentication application, an accounting application or a
- vendor specific application. See Section 11.3 for the possible
- values that the application-id may use.
+ Application-ID is four octets and is used to identify for which
+ application the message is applicable. The application can be an
+ authentication application, an accounting application, or a
+ vendor-specific application.
- The value of the application-id field in the header MUST be the
- same as any relevant application-id AVPs contained in the message.
+ The value of the Application-ID field in the header MUST be the
+ same as any relevant Application-Id AVPs contained in the message.
Hop-by-Hop Identifier
The Hop-by-Hop Identifier is an unsigned 32-bit integer field (in
- network byte order) and aids in matching requests and replies.
- The sender MUST ensure that the Hop-by-Hop identifier in a request
- is unique on a given connection at any given time, and MAY attempt
- to ensure that the number is unique across reboots. The sender of
- an Answer message MUST ensure that the Hop-by-Hop Identifier field
- contains the same value that was found in the corresponding
- request. The Hop-by-Hop identifier is normally a monotonically
- increasing number, whose start value was randomly generated. An
- answer message that is received with an unknown Hop-by-Hop
- Identifier MUST be discarded.
-
+ network byte order) that aids in matching requests and replies.
+ The sender MUST ensure that the Hop-by-Hop Identifier in a request
+ is unique on a given connection at any given time, and it MAY
+ attempt to ensure that the number is unique across reboots. The
+ sender of an answer message MUST ensure that the Hop-by-Hop
+ Identifier field contains the same value that was found in the
+ corresponding request. The Hop-by-Hop Identifier is normally a
+ monotonically increasing number, whose start value was randomly
+ generated. An answer message that is received with an unknown
+ Hop-by-Hop Identifier MUST be discarded.
End-to-End Identifier
The End-to-End Identifier is an unsigned 32-bit integer field (in
- network byte order) and is used to detect duplicate messages.
- Upon reboot implementations MAY set the high order 12 bits to
+ network byte order) that is used to detect duplicate messages.
+ Upon reboot, implementations MAY set the high order 12 bits to
contain the low order 12 bits of current time, and the low order
20 bits to a random value. Senders of request messages MUST
insert a unique identifier on each message. The identifier MUST
remain locally unique for a period of at least 4 minutes, even
- across reboots. The originator of an Answer message MUST ensure
+ across reboots. The originator of an answer message MUST ensure
that the End-to-End Identifier field contains the same value that
was found in the corresponding request. The End-to-End Identifier
MUST NOT be modified by Diameter agents of any kind. The
- combination of the Origin-Host (see Section 6.3) and this field is
+ combination of the Origin-Host AVP (Section 6.3) and this field is
used to detect duplicates. Duplicate requests SHOULD cause the
- same answer to be transmitted (modulo the hop-by-hop Identifier
- field and any routing AVPs that may be present), and MUST NOT
- affect any state that was set when the original request was
- processed. Duplicate answer messages that are to be locally
- consumed (see Section 6.2) SHOULD be silently discarded.
-
-
+ same answer to be transmitted (modulo the Hop-by-Hop Identifier
-Fajardo, et al. Expires July 24, 2011 [Page 37]
+Fajardo, et al. Standards Track [Page 36]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
+ field and any routing AVPs that may be present), and they MUST NOT
+ affect any state that was set when the original request was
+ processed. Duplicate answer messages that are to be locally
+ consumed (see Section 6.2) SHOULD be silently discarded.
+
AVPs
AVPs are a method of encapsulating information relevant to the
@@ -2080,17 +2032,17 @@ Internet-Draft Diameter Base Protocol January 2011
3.1. Command Codes
- Each command Request/Answer pair is assigned a command code, and the
+ Each command Request/Answer pair is assigned a Command Code, and the
sub-type (i.e., request or answer) is identified via the 'R' bit in
the Command Flags field of the Diameter header.
-
- Every Diameter message MUST contain a command code in its header's
- Command-Code field, which is used to determine the action that is to
+ Every Diameter message MUST contain a Command Code in its header's
+ Command Code field, which is used to determine the action that is to
be taken for a particular message. The following Command Codes are
defined in the Diameter base protocol:
- Command-Name Abbrev. Code Reference
+ Section
+ Command Name Abbrev. Code Reference
--------------------------------------------------------
Abort-Session-Request ASR 274 8.5.1
Abort-Session-Answer ASA 274 8.5.2
@@ -2111,140 +2063,142 @@ Internet-Draft Diameter Base Protocol January 2011
Session-Termination- STA 275 8.4.2
Answer
-3.2. Command Code ABNF specification
- Every Command Code defined MUST include a corresponding ABNF
- specification, which is used to define the AVPs that MUST or MAY be
- present when sending the message. The following format is used in
- the definition:
- command-def = <command-name> "::=" diameter-message
- command-name = diameter-name
-Fajardo, et al. Expires July 24, 2011 [Page 38]
-
-Internet-Draft Diameter Base Protocol January 2011
- diameter-name = ALPHA *(ALPHA / DIGIT / "-")
+Fajardo, et al. Standards Track [Page 37]
+
+RFC 6733 Diameter Base Protocol October 2012
- diameter-message = header [ *fixed] [ *required] [ *optional]
- header = "<" "Diameter Header:" command-id
- [r-bit] [p-bit] [e-bit] [application-id] ">"
+3.2. Command Code Format Specification
- application-id = 1*DIGIT
+ Every Command Code defined MUST include a corresponding Command Code
+ Format (CCF) specification, which is used to define the AVPs that
+ MUST or MAY be present when sending the message. The following ABNF
+ specifies the CCF used in the definition:
- command-id = 1*DIGIT
- ; The Command Code assigned to the command
+ command-def = "<" command-name ">" "::=" diameter-message
- r-bit = ", REQ"
- ; If present, the 'R' bit in the Command
- ; Flags is set, indicating that the message
- ; is a request, as opposed to an answer.
+ command-name = diameter-name
- p-bit = ", PXY"
- ; If present, the 'P' bit in the Command
- ; Flags is set, indicating that the message
- ; is proxiable.
+ diameter-name = ALPHA *(ALPHA / DIGIT / "-")
- e-bit = ", ERR"
- ; If present, the 'E' bit in the Command
- ; Flags is set, indicating that the answer
- ; message contains a Result-Code AVP in
- ; the "protocol error" class.
+ diameter-message = header *fixed *required *optional
- fixed = [qual] "<" avp-spec ">"
- ; Defines the fixed position of an AVP
+ header = "<Diameter-Header:" command-id
+ [r-bit] [p-bit] [e-bit] [application-id]">"
- required = [qual] "{" avp-spec "}"
- ; The AVP MUST be present and can appear
- ; anywhere in the message.
+ application-id = 1*DIGIT
+ command-id = 1*DIGIT
+ ; The Command Code assigned to the command.
- optional = [qual] "[" avp-name "]"
- ; The avp-name in the 'optional' rule cannot
- ; evaluate to any AVP Name which is included
- ; in a fixed or required rule. The AVP can
- ; appear anywhere in the message.
- ;
- ; NOTE: "[" and "]" have a slightly different
- ; meaning than in ABNF (RFC 5234]). These braces
- ; cannot be used to express optional fixed rules
- ; (such as an optional ICV at the end). To do this,
- ; the convention is '0*1fixed'.
+ r-bit = ", REQ"
+ ; If present, the 'R' bit in the Command
+ ; Flags is set, indicating that the message
+ ; is a request as opposed to an answer.
+ p-bit = ", PXY"
+ ; If present, the 'P' bit in the Command
+ ; Flags is set, indicating that the message
+ ; is proxiable.
+ e-bit = ", ERR"
+ ; If present, the 'E' bit in the Command
+ ; Flags is set, indicating that the answer
+ ; message contains a Result-Code AVP in
+ ; the "protocol error" class.
+ fixed = [qual] "<" avp-spec ">"
+ ; Defines the fixed position of an AVP.
-Fajardo, et al. Expires July 24, 2011 [Page 39]
-
-Internet-Draft Diameter Base Protocol January 2011
+ required = [qual] "{" avp-spec "}"
+ ; The AVP MUST be present and can appear
+ ; anywhere in the message.
- qual = [min] "*" [max]
- ; See ABNF conventions, RFC 5234 Section 4.
- ; The absence of any qualifiers depends on
- ; whether it precedes a fixed, required, or
- ; optional rule. If a fixed or required rule has
- ; no qualifier, then exactly one such AVP MUST
- ; be present. If an optional rule has no
- ; qualifier, then 0 or 1 such AVP may be
- ; present. If an optional rule has a qualifier,
- ; then the value of min MUST be 0 if present.
- min = 1*DIGIT
- ; The minimum number of times the element may
- ; be present. If absent, the default value is zero
- ; for fixed and optional rules and one for required
- ; rules. The value MUST be at least one for for
- ; required rules.
- max = 1*DIGIT
- ; The maximum number of times the element may
- ; be present. If absent, the default value is
- ; infinity. A value of zero implies the AVP MUST
- ; NOT be present.
- avp-spec = diameter-name
- ; The avp-spec has to be an AVP Name, defined
- ; in the base or extended Diameter
- ; specifications.
-
- avp-name = avp-spec / "AVP"
- ; The string "AVP" stands for *any* arbitrary AVP
- ; Name, not otherwise listed in that command code
- ; definition. Addition this AVP is recommended for
- ; all command ABNFs to allow for extensibility.
+Fajardo, et al. Standards Track [Page 38]
+
+RFC 6733 Diameter Base Protocol October 2012
- The following is a definition of a fictitious command code:
+ optional = [qual] "[" avp-name "]"
+ ; The avp-name in the 'optional' rule cannot
+ ; evaluate to any AVP Name that is included
+ ; in a fixed or required rule. The AVP can
+ ; appear anywhere in the message.
+ ;
+ ; NOTE: "[" and "]" have a slightly different
+ ; meaning than in ABNF. These braces
+ ; cannot be used to express optional fixed rules
+ ; (such as an optional ICV at the end). To do
+ ; this, the convention is '0*1fixed'.
- Example-Request ::= < Diameter Header: 9999999, REQ, PXY >
- { User-Name }
- * { Origin-Host }
- * [ AVP ]
+ qual = [min] "*" [max]
+ ; See ABNF conventions, RFC 5234, Section 4.
+ ; The absence of any qualifier depends on
+ ; whether it precedes a fixed, required, or
+ ; optional rule. If a fixed or required rule has
+ ; no qualifier, then exactly one such AVP MUST
+ ; be present. If an optional rule has no
+ ; qualifier, then 0 or 1 such AVP may be
+ ; present. If an optional rule has a qualifier,
+ ; then the value of min MUST be 0 if present.
+ min = 1*DIGIT
+ ; The minimum number of times the element may
+ ; be present. If absent, the default value is 0
+ ; for fixed and optional rules and 1 for
+ ; required rules. The value MUST be at least 1
+ ; for required rules.
+ max = 1*DIGIT
+ ; The maximum number of times the element may
+ ; be present. If absent, the default value is
+ ; infinity. A value of 0 implies the AVP MUST
+ ; NOT be present.
+ avp-spec = diameter-name
+ ; The avp-spec has to be an AVP Name, defined
+ ; in the base or extended Diameter
+ ; specifications.
+ avp-name = avp-spec / "AVP"
+ ; The string "AVP" stands for *any* arbitrary AVP
+ ; Name, not otherwise listed in that Command Code
+ ; definition. The inclusion of this string
+ ; is recommended for all CCFs to allow for
+ ; extensibility.
-Fajardo, et al. Expires July 24, 2011 [Page 40]
+Fajardo, et al. Standards Track [Page 39]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ The following is a definition of a fictitious Command Code:
+ Example-Request ::= < Diameter Header: 9999999, REQ, PXY >
+ { User-Name }
+ 1* { Origin-Host }
+ * [ AVP ]
3.3. Diameter Command Naming Conventions
Diameter command names typically includes one or more English words
- followed by the verb Request or Answer. Each English word is
+ followed by the verb "Request" or "Answer". Each English word is
delimited by a hyphen. A three-letter acronym for both the request
and answer is also normally provided.
@@ -2253,10 +2207,10 @@ Internet-Draft Diameter Base Protocol January 2011
the acronyms are STR and STA, respectively.
Both the request and the answer for a given command share the same
- command code. The request is identified by the R(equest) bit in the
+ Command Code. The request is identified by the R(equest) bit in the
Diameter header set to one (1), to ask that a particular action be
performed, such as authorizing a user or terminating a session. Once
- the receiver has completed the request it issues the corresponding
+ the receiver has completed the request, it issues the corresponding
answer, which includes a result code that communicates one of the
following:
@@ -2274,38 +2228,25 @@ Internet-Draft Diameter Base Protocol January 2011
Additional information, encoded within AVPs, may also be included in
answer messages.
+4. Diameter AVPs
+ Diameter AVPs carry specific authentication, accounting,
+ authorization, and routing information as well as configuration
+ details for the request and reply.
-
-
-
-
-
-
-
-
-
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 41]
+Fajardo, et al. Standards Track [Page 40]
-Internet-Draft Diameter Base Protocol January 2011
-
-
-4. Diameter AVPs
+RFC 6733 Diameter Base Protocol October 2012
- Diameter AVPs carry specific authentication, accounting,
- authorization and routing information as well as configuration
- details for the request and reply.
Each AVP of type OctetString MUST be padded to align on a 32-bit
boundary, while other AVP types align naturally. A number of zero-
- valued bytes are added to the end of the AVP Data field till a word
+ valued bytes are added to the end of the AVP Data field until a word
boundary is reached. The length of the padding is not reflected in
the AVP Length field.
@@ -2330,92 +2271,96 @@ Internet-Draft Diameter Base Protocol January 2011
The AVP Code, combined with the Vendor-Id field, identifies the
attribute uniquely. AVP numbers 1 through 255 are reserved for
- re-use of RADIUS attributes, without setting the Vendor-Id field.
+ reuse of RADIUS attributes, without setting the Vendor-Id field.
AVP numbers 256 and above are used for Diameter, which are
- allocated by IANA (see Section 11.1).
-
+ allocated by IANA (see Section 11.1.1).
AVP Flags
The AVP Flags field informs the receiver how each attribute must
- be handled. The 'r' (reserved) bits are unused and SHOULD be set
- to 0. Note that subsequent Diameter applications MAY define
- additional bits within the AVP Header, and an unrecognized bit
- SHOULD be considered an error. The 'P' bit has been reserved for
- future usage of end-to-end security. At the time of writing there
- are no end-to-end security mechanisms specified therefore the 'P'
- bit SHOULD be set to 0.
+ be handled. New Diameter applications SHOULD NOT define
+ additional AVP Flag bits. However, note that new Diameter
+ applications MAY define additional bits within the AVP header, and
+ an unrecognized bit SHOULD be considered an error. The sender of
+ the AVP MUST set 'R' (reserved) bits to 0 and the receiver SHOULD
+ ignore all 'R' (reserved) bits. The 'P' bit has been reserved for
+ future usage of end-to-end security. At the time of writing,
+ there are no end-to-end security mechanisms specified; therefore,
+ the 'P' bit SHOULD be set to 0.
+
+ The 'M' bit, known as the Mandatory bit, indicates whether the
+ receiver of the AVP MUST parse and understand the semantics of the
+ AVP including its content. The receiving entity MUST return an
+ appropriate error message if it receives an AVP that has the M-bit
-Fajardo, et al. Expires July 24, 2011 [Page 42]
+Fajardo, et al. Standards Track [Page 41]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
- The 'M' Bit, known as the Mandatory bit, indicates whether the
- receiver of the AVP MUST parse and understand the semantic of the
- AVP including its content. The receiving entity MUST return an
- appropriate error message if it receives an AVP that has the M-bit
set but does not understand it. An exception applies when the AVP
is embedded within a Grouped AVP. See Section 4.4 for details.
- Diameter Relay and redirect agents MUST NOT reject messages with
+ Diameter relay and redirect agents MUST NOT reject messages with
unrecognized AVPs.
The 'M' bit MUST be set according to the rules defined in the
- application specification which introduces or re-uses this AVP.
- Within a given application, the M-bit setting for an AVP is either
- defined for all command types or for each command type.
+ application specification that introduces or reuses this AVP.
+ Within a given application, the M-bit setting for an AVP is
+ defined either for all command types or for each command type.
- AVPs with the 'M' bit cleared are informational only and a
- receiver that receives a message with such an AVP that is not
- supported, or whose value is not supported, MAY simply ignore the
- AVP.
+ AVPs with the 'M' bit cleared are informational only; a receiver
+ that receives a message with such an AVP that is not supported, or
+ whose value is not supported, MAY simply ignore the AVP.
The 'V' bit, known as the Vendor-Specific bit, indicates whether
the optional Vendor-ID field is present in the AVP header. When
- set the AVP Code belongs to the specific vendor code address
+ set, the AVP Code belongs to the specific vendor code address
space.
AVP Length
The AVP Length field is three octets, and indicates the number of
- octets in this AVP including the AVP Code, AVP Length, AVP Flags,
- Vendor-ID field (if present) and the AVP data. If a message is
- received with an invalid attribute length, the message MUST be
- rejected.
+ octets in this AVP including the AVP Code field, AVP Length field,
+ AVP Flags field, Vendor-ID field (if present), and the AVP Data
+ field. If a message is received with an invalid attribute length,
+ the message MUST be rejected.
4.1.1. Optional Header Elements
- The AVP Header contains one optional field. This field is only
+ The AVP header contains one optional field. This field is only
present if the respective bit-flag is enabled.
-
Vendor-ID
The Vendor-ID field is present if the 'V' bit is set in the AVP
Flags field. The optional four-octet Vendor-ID field contains the
- IANA assigned "SMI Network Management Private Enterprise Codes"
- [RFC3232] value, encoded in network byte order. Any vendor or
- standardization organization that are also treated like vendors in
- the IANA managed "SMI Network Management Private Enterprise Codes"
- space wishing to implement a vendor-specific Diameter AVP MUST use
- their own Vendor-ID along with their privately managed AVP address
+ IANA-assigned "SMI Network Management Private Enterprise Codes"
+ [ENTERPRISE] value, encoded in network byte order. Any vendors or
+ standardization organizations that are also treated like vendors
+ in the IANA-managed "SMI Network Management Private Enterprise
+ Codes" space wishing to implement a vendor-specific Diameter AVP
+ MUST use their own Vendor-ID along with their privately managed
+ AVP address space, guaranteeing that they will not collide with
+ any other vendor's vendor-specific AVP(s) or with future IETF
+ AVPs.
-Fajardo, et al. Expires July 24, 2011 [Page 43]
-
-Internet-Draft Diameter Base Protocol January 2011
- space, guaranteeing that they will not collide with any other
- vendor's vendor-specific AVP(s), nor with future IETF AVPs.
- A vendor ID value of zero (0) corresponds to the IETF adopted AVP
- values, as managed by the IANA. Since the absence of the vendor
- ID field implies that the AVP in question is not vendor specific,
- implementations MUST NOT use the zero (0) vendor ID.
+Fajardo, et al. Standards Track [Page 42]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ A Vendor-ID value of zero (0) corresponds to the IETF-adopted AVP
+ values, as managed by IANA. Since the absence of the Vendor-ID
+ field implies that the AVP in question is not vendor specific,
+ implementations MUST NOT use the value of zero (0) for the
+ Vendor-ID field.
4.2. Basic AVP Data Formats
@@ -2426,49 +2371,45 @@ Internet-Draft Diameter Base Protocol January 2011
type derived from the base data types. In the event that a new Basic
AVP Data Format is needed, a new version of this RFC MUST be created.
-
OctetString
The data contains arbitrary data of variable length. Unless
otherwise noted, the AVP Length field MUST be set to at least 8
- (12 if the 'V' bit is enabled). AVP Values of this type that are
- not a multiple of four-octets in length is followed by the
- necessary padding so that the next AVP (if any) will start on a
- 32-bit boundary.
-
+ (12 if the 'V' bit is enabled). AVP values of this type that are
+ not a multiple of 4 octets in length are followed by the necessary
+ padding so that the next AVP (if any) will start on a 32-bit
+ boundary.
Integer32
- 32 bit signed value, in network byte order. The AVP Length field
+ 32-bit signed value, in network byte order. The AVP Length field
MUST be set to 12 (16 if the 'V' bit is enabled).
-
Integer64
- 64 bit signed value, in network byte order. The AVP Length field
+ 64-bit signed value, in network byte order. The AVP Length field
MUST be set to 16 (20 if the 'V' bit is enabled).
-
Unsigned32
- 32 bit unsigned value, in network byte order. The AVP Length
+ 32-bit unsigned value, in network byte order. The AVP Length
field MUST be set to 12 (16 if the 'V' bit is enabled).
+ Unsigned64
+ 64-bit unsigned value, in network byte order. The AVP Length
+ field MUST be set to 16 (20 if the 'V' bit is enabled).
-Fajardo, et al. Expires July 24, 2011 [Page 44]
-
-Internet-Draft Diameter Base Protocol January 2011
- Unsigned64
- 64 bit unsigned value, in network byte order. The AVP Length
- field MUST be set to 16 (20 if the 'V' bit is enabled).
+Fajardo, et al. Standards Track [Page 43]
+
+RFC 6733 Diameter Base Protocol October 2012
Float32
@@ -2478,7 +2419,6 @@ Internet-Draft Diameter Base Protocol January 2011
network byte order. The AVP Length field MUST be set to 12 (16 if
the 'V' bit is enabled).
-
Float64
This represents floating point values of double precision as
@@ -2486,73 +2426,69 @@ Internet-Draft Diameter Base Protocol January 2011
network byte order. The AVP Length field MUST be set to 16 (20 if
the 'V' bit is enabled).
-
Grouped
- The Data field is specified as a sequence of AVPs. Each of these
- AVPs follows - in the order in which they are specified -
- including their headers and padding. The AVP Length field is set
- to 8 (12 if the 'V' bit is enabled) plus the total length of all
- included AVPs, including their headers and padding. Thus the AVP
- length field of an AVP of type Grouped is always a multiple of 4.
-
+ The Data field is specified as a sequence of AVPs. These AVPs are
+ concatenated -- including their headers and padding -- in the
+ order in which they are specified and the result encapsulated in
+ the Data field. The AVP Length field is set to 8 (12 if the 'V'
+ bit is enabled) plus the total length of all included AVPs,
+ including their headers and padding. Thus, the AVP Length field
+ of an AVP of type Grouped is always a multiple of 4.
4.3. Derived AVP Data Formats
In addition to using the Basic AVP Data Formats, applications may
define data formats derived from the Basic AVP Data Formats. An
application that defines new Derived AVP Data Formats MUST include
- them in a section entitled "Derived AVP Data Formats", using the same
+ them in a section titled "Derived AVP Data Formats", using the same
format as the definitions below. Each new definition MUST be either
defined or listed with a reference to the RFC that defines the
format.
-4.3.1. Common Derived AVPs
+4.3.1. Common Derived AVP Data Formats
The following are commonly used Derived AVP Data Formats.
+ Address
+ The Address format is derived from the OctetString Basic AVP
+ Format. It is a discriminated union representing, for example, a
+ 32-bit (IPv4) [RFC0791] or 128-bit (IPv6) [RFC4291] address, most
+ significant octet first. The first two octets of the Address AVP
+ represent the AddressType, which contains an Address Family,
+ defined in [IANAADFAM]. The AddressType is used to discriminate
+ the content and format of the remaining octets.
-Fajardo, et al. Expires July 24, 2011 [Page 45]
+Fajardo, et al. Standards Track [Page 44]
-Internet-Draft Diameter Base Protocol January 2011
-
-
- Address
-
- The Address format is derived from the OctetString AVP Base
- Format. It is a discriminated union, representing, for example a
- 32-bit (IPv4) [RFC791] or 128-bit (IPv6) [RFC4291] address, most
- significant octet first. The first two octets of the Address AVP
- represents the AddressType, which contains an Address Family
- defined in [IANAADFAM]. The AddressType is used to discriminate
- the content and format of the remaining octets.
+RFC 6733 Diameter Base Protocol October 2012
Time
- The Time format is derived from the OctetString AVP Base Format.
+ The Time format is derived from the OctetString Basic AVP Format.
The string MUST contain four octets, in the same format as the
first four bytes are in the NTP timestamp format. The NTP
- Timestamp format is defined in Chapter 3 of [RFC5905].
+ timestamp format is defined in Section 3 of [RFC5905].
This represents the number of seconds since 0h on 1 January 1900
with respect to the Coordinated Universal Time (UTC).
- On 6h 28m 16s UTC, 7 February 2036 the time value will overflow.
- SNTP [RFC5905] describes a procedure to extend the time to 2104.
- This procedure MUST be supported by all Diameter nodes.
-
+ On 6h 28m 16s UTC, 7 February 2036, the time value will overflow.
+ Simple Network Time Protocol (SNTP) [RFC5905] describes a
+ procedure to extend the time to 2104. This procedure MUST be
+ supported by all Diameter nodes.
UTF8String
- The UTF8String format is derived from the OctetString AVP Base
- Format. This is a human readable string represented using the
+ The UTF8String format is derived from the OctetString Basic AVP
+ Format. This is a human-readable string represented using the
ISO/IEC IS 10646-1 character set, encoded as an OctetString using
- the UTF-8 [RFC3629] transformation format described in RFC 3629.
+ the UTF-8 transformation format [RFC3629].
Since additional code points are added by amendments to the 10646
standard from time to time, implementations MUST be prepared to
@@ -2570,78 +2506,79 @@ Internet-Draft Diameter Base Protocol January 2011
or software, an alternative means of entry and display, such as
hexadecimal, MAY be provided.
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 46]
-
-Internet-Draft Diameter Base Protocol January 2011
-
-
For information encoded in 7-bit US-ASCII, the UTF-8 charset is
identical to the US-ASCII charset.
UTF-8 may require multiple bytes to represent a single character /
- code point; thus the length of an UTF8String in octets may be
+ code point; thus, the length of a UTF8String in octets may be
different from the number of characters encoded.
Note that the AVP Length field of an UTF8String is measured in
- octets, not characters.
+ octets not characters.
+
+
+
+
+Fajardo, et al. Standards Track [Page 45]
+
+RFC 6733 Diameter Base Protocol October 2012
+
DiameterIdentity
- The DiameterIdentity format is derived from the OctetString AVP
- Base Format.
+ The DiameterIdentity format is derived from the OctetString Basic
+ AVP Format.
DiameterIdentity = FQDN/Realm
-
- DiameterIdentity value is used to uniquely identify either:
+ The DiameterIdentity value is used to uniquely identify either:
* A Diameter node for purposes of duplicate connection and
routing loop detection.
- * A Realm to determine whether messages can be satisfied locally,
+ * A Realm to determine whether messages can be satisfied locally
or whether they must be routed or redirected.
+ When a DiameterIdentity value is used to identify a Diameter node,
+ the contents of the string MUST be the Fully Qualified Domain Name
+ (FQDN) of the Diameter node. If multiple Diameter nodes run on
+ the same host, each Diameter node MUST be assigned a unique
+ DiameterIdentity. If a Diameter node can be identified by several
+ FQDNs, a single FQDN should be picked at startup and used as the
+ only DiameterIdentity for that node, whatever the connection on
+ which it is sent. In this document, note that DiameterIdentity is
+ in ASCII form in order to be compatible with existing DNS
+ infrastructure. See Appendix D for interactions between the
+ Diameter protocol and Internationalized Domain Names (IDNs).
- When a DiameterIdentity is used to identify a Diameter node the
- contents of the string MUST be the FQDN of the Diameter node. If
- multiple Diameter nodes run on the same host, each Diameter node
- MUST be assigned a unique DiameterIdentity. If a Diameter node
- can be identified by several FQDNs, a single FQDN should be picked
- at startup, and used as the only DiameterIdentity for that node,
- whatever the connection it is sent on. Note that in this
- document, DiameterIdentity is in ASCII form in order to be
- compatible with existing DNS infrastructure. See Appendix D for
- interactions between the Diameter protocol and Internationalized
- Domain Name (IDNs).
+ DiameterURI
+ The DiameterURI MUST follow the Uniform Resource Identifiers (RFC
+ 3986) syntax [RFC3986] rules specified below:
- DiameterURI
+ "aaa://" FQDN [ port ] [ transport ] [ protocol ]
- The DiameterURI MUST follow the Uniform Resource Identifiers (URI)
- syntax [RFC3986] rules specified below:
+ ; No transport security
+ "aaas://" FQDN [ port ] [ transport ] [ protocol ]
+ ; Transport security used
+ FQDN = < Fully Qualified Domain Name >
-Fajardo, et al. Expires July 24, 2011 [Page 47]
-
-Internet-Draft Diameter Base Protocol January 2011
- "aaa://" FQDN [ port ] [ transport ] [ protocol ]
- ; No transport security
- "aaas://" FQDN [ port ] [ transport ] [ protocol ]
- ; Transport security used
- FQDN = Fully Qualified Host Name
+Fajardo, et al. Standards Track [Page 46]
+
+RFC 6733 Diameter Base Protocol October 2012
+
port = ":" 1*DIGIT
@@ -2649,8 +2586,9 @@ Internet-Draft Diameter Base Protocol January 2011
; incoming connections.
; If absent, the default Diameter port
; (3868) is assumed if no transport
- ; security is used and port (TBD) when
- ; transport security (TLS/TCP and DTLS/SCTP) is used.
+ ; security is used and port 5658 when
+ ; transport security (TLS/TCP and DTLS/SCTP)
+ ; is used.
transport = ";transport=" transport-protocol
@@ -2660,46 +2598,47 @@ Internet-Draft Diameter Base Protocol January 2011
; UDP MUST NOT be used when the aaa-protocol
; field is set to diameter.
- transport-protocol = ( "tcp" / "sctp" / "udp" )
+ transport-protocol = ( "tcp" / "sctp" / "udp" )
- protocol = ";protocol=" aaa-protocol
+ protocol = ";protocol=" aaa-protocol
; If absent, the default AAA protocol
; is Diameter.
- aaa-protocol = ( "diameter" / "radius" / "tacacs+" )
+ aaa-protocol = ( "diameter" / "radius" / "tacacs+" )
- The following are examples of valid Diameter host identities:
+ The following are examples of valid Diameter host identities:
- aaa://host.example.com;transport=tcp
- aaa://host.example.com:6666;transport=tcp
- aaa://host.example.com;protocol=diameter
- aaa://host.example.com:6666;protocol=diameter
- aaa://host.example.com:6666;transport=tcp;protocol=diameter
- aaa://host.example.com:1813;transport=udp;protocol=radius
+ aaa://host.example.com;transport=tcp
+ aaa://host.example.com:6666;transport=tcp
+ aaa://host.example.com;protocol=diameter
+ aaa://host.example.com:6666;protocol=diameter
+ aaa://host.example.com:6666;transport=tcp;protocol=diameter
+ aaa://host.example.com:1813;transport=udp;protocol=radius
+ Enumerated
+ The Enumerated format is derived from the Integer32 Basic AVP
+ Format. The definition contains a list of valid values and their
+ interpretation and is described in the Diameter application
+ introducing the AVP.
-Fajardo, et al. Expires July 24, 2011 [Page 48]
-
-Internet-Draft Diameter Base Protocol January 2011
- Enumerated
- Enumerated is derived from the Integer32 AVP Base Format. The
- definition contains a list of valid values and their
- interpretation and is described in the Diameter application
- introducing the AVP.
+
+Fajardo, et al. Standards Track [Page 47]
+
+RFC 6733 Diameter Base Protocol October 2012
IPFilterRule
- The IPFilterRule format is derived from the OctetString AVP Base
+ The IPFilterRule format is derived from the OctetString Basic AVP
Format and uses the ASCII charset. The rule syntax is a modified
subset of ipfw(8) from FreeBSD. Packets may be filtered based on
the following information that is associated with it:
@@ -2713,13 +2652,13 @@ Internet-Draft Diameter Base Protocol January 2011
IP options
ICMP types
- Rules for the appropriate direction are evaluated in order, with
- the first matched rule terminating the evaluation. Each packet is
- evaluated once. If no rule matches, the packet is dropped if the
- last rule evaluated was a permit, and passed if the last rule was
- a deny.
+ Rules for the appropriate direction are evaluated in order, with the
+ first matched rule terminating the evaluation. Each packet is
+ evaluated once. If no rule matches, the packet is dropped if the
+ last rule evaluated was a permit, and passed if the last rule was a
+ deny.
- IPFilterRule filters MUST follow the format:
+ IPFilterRule filters MUST follow the format:
action dir proto from src to dst [options]
@@ -2737,22 +2676,28 @@ Internet-Draft Diameter Base Protocol January 2011
The <address/mask> may be specified as:
ipno An IPv4 or IPv6 number in dotted-
quad or canonical IPv6 form. Only
+ this exact IP number will match the
+ rule.
+
+
+
+
+
+
-Fajardo, et al. Expires July 24, 2011 [Page 49]
+Fajardo, et al. Standards Track [Page 48]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
- this exact IP number will match the
- rule.
ipno/bits An IP number as above with a mask
width of the form 192.0.2.10/24. In
this case, all IP numbers from
192.0.2.0 to 192.0.2.255 will match.
The bit width MUST be valid for the
- IP version and the IP number MUST
+ IP version, and the IP number MUST
NOT have bits set beyond the mask.
For a match to occur, the same IP
version must be present in the
@@ -2765,7 +2710,7 @@ Internet-Draft Diameter Base Protocol January 2011
is the address or set of addresses
assigned to the terminal. For IPv4,
a typical first rule is often "deny
- in ip! assigned"
+ in ip! assigned".
The sense of the match can be inverted by
preceding an address with the not modifier (!),
@@ -2773,7 +2718,7 @@ Internet-Draft Diameter Base Protocol January 2011
instead. This does not affect the selection of
port numbers.
- With the TCP, UDP and SCTP protocols, optional
+ With the TCP, UDP, and SCTP protocols, optional
ports may be specified as:
{port/port-port}[,ports[,...]]
@@ -2796,33 +2741,35 @@ Internet-Draft Diameter Base Protocol January 2011
-Fajardo, et al. Expires July 24, 2011 [Page 50]
+
+
+Fajardo, et al. Standards Track [Page 49]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
- ipoptions spec
- Match if the IP header contains the comma
- separated list of options specified in spec. The
+ ipoptions spec
+ Match if the IP header contains the comma-separated
+ list of options specified in spec. The
supported IP options are:
ssrr (strict source route), lsrr (loose source
- route), rr (record packet route) and ts
+ route), rr (record packet route), and ts
(timestamp). The absence of a particular option
may be denoted with a '!'.
tcpoptions spec
- Match if the TCP header contains the comma
- separated list of options specified in spec. The
+ Match if the TCP header contains the comma-separated
+ list of options specified in spec. The
supported TCP options are:
mss (maximum segment size), window (tcp window
advertisement), sack (selective ack), ts (rfc1323
- timestamp) and cc (rfc1644 t/tcp connection
+ timestamp), and cc (rfc1644 t/tcp connection
count). The absence of a particular option may
be denoted with a '!'.
- established
+ established
TCP packets only. Match packets that have the RST
or ACK bits set.
@@ -2832,10 +2779,10 @@ Internet-Draft Diameter Base Protocol January 2011
tcpflags spec
TCP packets only. Match if the TCP header
- contains the comma separated list of flags
+ contains the comma-separated list of flags
specified in spec. The supported TCP flags are:
- fin, syn, rst, psh, ack and urg. The absence of a
+ fin, syn, rst, psh, ack, and urg. The absence of a
particular flag may be denoted with a '!'. A rule
that contains a tcpflags specification can never
match a fragmented packet that has a non-zero
@@ -2852,9 +2799,9 @@ Internet-Draft Diameter Base Protocol January 2011
-Fajardo, et al. Expires July 24, 2011 [Page 51]
+Fajardo, et al. Standards Track [Page 50]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
echo reply (0), destination unreachable (3),
@@ -2863,21 +2810,20 @@ Internet-Draft Diameter Base Protocol January 2011
solicitation (10), time-to-live exceeded (11), IP
header bad (12), timestamp request (13),
timestamp reply (14), information request (15),
- information reply (16), address mask request (17)
+ information reply (16), address mask request (17),
and address mask reply (18).
- There is one kind of packet that the access device MUST always
- discard, that is an IP fragment with a fragment offset of one.
- This is a valid packet, but it only has one use, to try to
- circumvent firewalls.
-
- An access device that is unable to interpret or apply a deny rule
- MUST terminate the session. An access device that is unable to
- interpret or apply a permit rule MAY apply a more restrictive
- rule. An access device MAY apply deny rules of its own before the
- supplied rules, for example to protect the access device owner's
- infrastructure.
+ There is one kind of packet that the access device MUST always
+ discard, that is an IP fragment with a fragment offset of one. This
+ is a valid packet, but it only has one use, to try to circumvent
+ firewalls.
+ An access device that is unable to interpret or apply a deny rule
+ MUST terminate the session. An access device that is unable to
+ interpret or apply a permit rule MAY apply a more restrictive rule.
+ An access device MAY apply deny rules of its own before the supplied
+ rules, for example to protect the access device owner's
+ infrastructure.
4.4. Grouped AVP Values
@@ -2885,75 +2831,76 @@ Internet-Draft Diameter Base Protocol January 2011
implies that the Data field is actually a sequence of AVPs. It is
possible to include an AVP with a Grouped type within a Grouped type,
that is, to nest them. AVPs within an AVP of type Grouped have the
- same padding requirements as non-Grouped AVPs, as defined in Section
- 4.
+ same padding requirements as non-Grouped AVPs, as defined in
+ Section 4.4.
The AVP Code numbering space of all AVPs included in a Grouped AVP is
- the same as for non-grouped AVPs. Receivers of a Grouped AVP that
+ the same as for non-Grouped AVPs. Receivers of a Grouped AVP that
does not have the 'M' (mandatory) bit set and one or more of the
encapsulated AVPs within the group has the 'M' (mandatory) bit set
MAY simply be ignored if the Grouped AVP itself is unrecognized. The
rule applies even if the encapsulated AVP with its 'M' (mandatory)
- bit set is further encapsulated within other sub-groups; i.e. other
+ bit set is further encapsulated within other sub-groups, i.e., other
Grouped AVPs embedded within the Grouped AVP.
- Every Grouped AVP defined MUST include a corresponding grammar, using
- ABNF [RFC5234] (with modifications), as defined below.
-
+ Every Grouped AVP definition MUST include a corresponding grammar,
+ using ABNF [RFC5234] (with modifications), as defined below.
+ grouped-avp-def = "<" name ">" "::=" avp
+ name-fmt = ALPHA *(ALPHA / DIGIT / "-")
-Fajardo, et al. Expires July 24, 2011 [Page 52]
+Fajardo, et al. Standards Track [Page 51]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
- grouped-avp-def = <name> "::=" avp
-
- name-fmt = ALPHA *(ALPHA / DIGIT / "-")
-
name = name-fmt
; The name has to be the name of an AVP,
; defined in the base or extended Diameter
; specifications.
- avp = header [ *fixed] [ *required] [ *optional]
+ avp = header *fixed *required *optional
header = "<" "AVP-Header:" avpcode [vendor] ">"
avpcode = 1*DIGIT
- ; The AVP Code assigned to the Grouped AVP
+ ; The AVP Code assigned to the Grouped AVP.
vendor = 1*DIGIT
; The Vendor-ID assigned to the Grouped AVP.
; If absent, the default value of zero is
; used.
-4.4.1. Example AVP with a Grouped Data type
+4.4.1. Example AVP with a Grouped Data Type
The Example-AVP (AVP Code 999999) is of type Grouped and is used to
clarify how Grouped AVP values work. The Grouped Data field has the
- following ABNF grammar:
-
-
-
-
-
-
-
-
+ following CCF grammar:
+ Example-AVP ::= < AVP Header: 999999 >
+ { Origin-Host }
+ 1*{ Session-Id }
+ *[ AVP ]
+ An Example-AVP with Grouped Data follows.
+ The Origin-Host AVP (Section 6.3) is required. In this case:
+ Origin-Host = "example.com".
+ One or more Session-Ids must follow. Here there are two:
+ Session-Id =
+ "grump.example.com:33041;23432;893;0AF3B81"
+ Session-Id =
+ "grump.example.com:33054;23561;2358;0AF3B82"
@@ -2964,31 +2911,11 @@ Internet-Draft Diameter Base Protocol January 2011
-Fajardo, et al. Expires July 24, 2011 [Page 53]
+Fajardo, et al. Standards Track [Page 52]
-Internet-Draft Diameter Base Protocol January 2011
-
-
- Example-AVP ::= < AVP Header: 999999 >
- { Origin-Host }
- 1*{ Session-Id }
- *[ AVP ]
-
- An Example-AVP with Grouped Data follows.
-
- The Origin-Host AVP is required (Section 6.3). In this case:
-
- Origin-Host = "example.com".
+RFC 6733 Diameter Base Protocol October 2012
- One or more Session-Ids must follow. Here there are two:
-
- Session-Id =
- "grump.example.com:33041;23432;893;0AF3B81"
-
- Session-Id =
- "grump.example.com:33054;23561;2358;0AF3B82"
-
optional AVPs included are
Recovery-Policy = <binary>
@@ -3007,25 +2934,42 @@ Internet-Draft Diameter Base Protocol January 2011
41d018d56fe938f3cbf089aac12a912a2f0d1923a9390e5f789cb2e5067
d3427475e49968f841
- The data for the optional AVPs is represented in hex since the format
- of these AVPs is neither known at the time of definition of the
- Example-AVP group, nor (likely) at the time when the example instance
- of this AVP is interpreted - except by Diameter implementations which
- support the same set of AVPs. The encoding example illustrates how
- padding is used and how length fields are calculated. Also note that
- AVPs may be present in the Grouped AVP value which the receiver
- cannot interpret (here, the Recover-Policy and Futuristic-Acct-Record
- AVPs). The length of the Example-AVP is the sum of all the length of
- the member AVPs including their padding plus the Example-AVP header
+ The data for the optional AVPs is represented in hexadecimal form
+ since the format of these AVPs is not known at the time of definition
+ of the Example-AVP group nor (likely) at the time when the example
+ instance of this AVP is interpreted -- except by Diameter
+ implementations that support the same set of AVPs. The encoding
+ example illustrates how padding is used and how length fields are
+ calculated. Also, note that AVPs may be present in the Grouped AVP
+ value that the receiver cannot interpret (here, the Recover-Policy
+ and Futuristic-Acct-Record AVPs). The length of the Example-AVP is
+ the sum of all the length of the member AVPs, including their
+ padding, plus the Example-AVP header size.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-Fajardo, et al. Expires July 24, 2011 [Page 54]
-
-Internet-Draft Diameter Base Protocol January 2011
- size.
+
+Fajardo, et al. Standards Track [Page 53]
+
+RFC 6733 Diameter Base Protocol October 2012
This AVP would be encoded as follows:
@@ -3076,15 +3020,18 @@ Internet-Draft Diameter Base Protocol January 2011
-Fajardo, et al. Expires July 24, 2011 [Page 55]
+
+
+
+Fajardo, et al. Standards Track [Page 54]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
4.5. Diameter Base Protocol AVPs
The following table describes the Diameter AVPs defined in the base
- protocol, their AVP Code values, types, possible flag values.
+ protocol, their AVP Code values, types, and possible flag values.
Due to space constraints, the short form DiamIdent is used to
represent DiameterIdentity.
@@ -3132,9 +3079,9 @@ Internet-Draft Diameter Base Protocol January 2011
-Fajardo, et al. Expires July 24, 2011 [Page 56]
+Fajardo, et al. Standards Track [Page 55]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
+----------+
@@ -3154,7 +3101,7 @@ Internet-Draft Diameter Base Protocol January 2011
Record-Number | | |
Accounting- 480 9.8.1 Enumerated | M | V |
Record-Type | | |
- Accounting- 44 9.8.4 OctetString| M | V |
+ Acct- 44 9.8.4 OctetString| M | V |
Session-Id | | |
Accounting- 287 9.8.6 Unsigned64 | M | V |
Sub-Session-Id | | |
@@ -3188,9 +3135,9 @@ Internet-Draft Diameter Base Protocol January 2011
-Fajardo, et al. Expires July 24, 2011 [Page 57]
+Fajardo, et al. Standards Track [Page 56]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
+----------+
@@ -3244,9 +3191,9 @@ Internet-Draft Diameter Base Protocol January 2011
-Fajardo, et al. Expires July 24, 2011 [Page 58]
+Fajardo, et al. Standards Track [Page 57]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
5. Diameter Peers
@@ -3258,25 +3205,25 @@ Internet-Draft Diameter Base Protocol January 2011
Connections between diameter peers are established using their valid
DiameterIdentity. A Diameter node initiating a connection to a peer
- MUST know the peers DiameterIdentity. Methods for discovering a
+ MUST know the peer's DiameterIdentity. Methods for discovering a
Diameter peer can be found in Section 5.2.
- Although a Diameter node may have many possible peers that it is able
- to communicate with, it may not be economical to have an established
- connection to all of them. At a minimum, a Diameter node SHOULD have
- an established connection with two peers per realm, known as the
- primary and secondary peers. Of course, a node MAY have additional
- connections, if it is deemed necessary. Typically, all messages for
- a realm are sent to the primary peer, but in the event that failover
- procedures are invoked, any pending requests are sent to the
- secondary peer. However, implementations are free to load balance
- requests between a set of peers.
-
- Note that a given peer MAY act as a primary for a given realm, while
+ Although a Diameter node may have many possible peers with which it
+ is able to communicate, it may not be economical to have an
+ established connection to all of them. At a minimum, a Diameter node
+ SHOULD have an established connection with two peers per realm, known
+ as the primary and secondary peers. Of course, a node MAY have
+ additional connections, if it is deemed necessary. Typically, all
+ messages for a realm are sent to the primary peer but, in the event
+ that failover procedures are invoked, any pending requests are sent
+ to the secondary peer. However, implementations are free to load
+ balance requests between a set of peers.
+
+ Note that a given peer MAY act as a primary for a given realm while
acting as a secondary for another realm.
When a peer is deemed suspect, which could occur for various reasons,
- including not receiving a DWA within an allotted timeframe, no new
+ including not receiving a DWA within an allotted time frame, no new
requests should be forwarded to the peer, but failover procedures are
invoked. When an active peer is moved to this mode, additional
connections SHOULD be established to ensure that the necessary number
@@ -3284,15 +3231,14 @@ Internet-Draft Diameter Base Protocol January 2011
There are two ways that a peer is removed from the suspect peer list:
-
1. The peer is no longer reachable, causing the transport connection
- to be shutdown. The peer is moved to the closed state.
+ to be shut down. The peer is moved to the closed state.
- 2. Three watchdog messages are exchanged with accepted round trip
+ 2. Three watchdog messages are exchanged with accepted round-trip
times, and the connection to the peer is considered stabilized.
In the event the peer being removed is either the primary or
- secondary, an alternate peer SHOULD replace the deleted peer, and
+ secondary, an alternate peer SHOULD replace the deleted peer and
assume the role of either primary or secondary.
@@ -3300,99 +3246,106 @@ Internet-Draft Diameter Base Protocol January 2011
-Fajardo, et al. Expires July 24, 2011 [Page 59]
+
+Fajardo, et al. Standards Track [Page 58]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
5.2. Diameter Peer Discovery
- Allowing for dynamic Diameter agent discovery will make it possible
- for simpler and more robust deployment of Diameter services. In
- order to promote interoperable implementations of Diameter peer
- discovery, the following mechanisms are described. These are based
- on existing IETF standards. The first option (manual configuration)
- MUST be supported by all Diameter nodes, while the latter option
- (DNS) MAY be supported.
+ Allowing for dynamic Diameter agent discovery makes possible simpler
+ and more robust deployment of Diameter services. In order to promote
+ interoperable implementations of Diameter peer discovery, the
+ following mechanisms (manual configuration and DNS) are described.
+ These are based on existing IETF standards. Both mechanisms MUST be
+ supported by all Diameter implementations; either MAY be used.
There are two cases where Diameter peer discovery may be performed.
The first is when a Diameter client needs to discover a first-hop
Diameter agent. The second case is when a Diameter agent needs to
- discover another agent - for further handling of a Diameter
- operation. In both cases, the following 'search order' is
- recommended:
-
+ discover another agent for further handling of a Diameter operation.
+ In both cases, the following 'search order' is recommended:
- 1. The Diameter implementation consults its list of static
+ 1. The Diameter implementation consults its list of statically
(manually) configured Diameter agent locations. These will be
used if they exist and respond.
-
2. The Diameter implementation performs a NAPTR query for a server
- in a particular realm. The Diameter implementation has to know
- in advance which realm to look for a Diameter agent. This could
- be deduced, for example, from the 'realm' in a NAI that a
- Diameter implementation needed to perform a Diameter operation
- on.
+ in a particular realm. The Diameter implementation has to know,
+ in advance, in which realm to look for a Diameter agent. This
+ could be deduced, for example, from the 'realm' in an NAI on
+ which a Diameter implementation needed to perform a Diameter
+ operation.
The NAPTR usage in Diameter follows the S-NAPTR DDDS application
[RFC3958] in which the SERVICE field includes tags for the
desired application and supported application protocol. The
application service tag for a Diameter application is 'aaa' and
the supported application protocol tags are 'diameter.tcp',
- 'diameter.sctp', 'diameter.dtls' or 'diameter.tls.tcp'.
+ 'diameter.sctp', 'diameter.dtls', or 'diameter.tls.tcp'
+ [RFC6408].
The client can follow the resolution process defined by the
- S-NAPTR DDDS [RFC3958] application to find a matching SRV, A or
+ S-NAPTR DDDS [RFC3958] application to find a matching SRV, A, or
AAAA record of a suitable peer. The domain suffixes in the NAPTR
replacement field SHOULD match the domain of the original query.
An example can be found in Appendix B.
3. If no NAPTR records are found, the requester directly queries for
- SRV records '_diameter._sctp'.realm, '_diameter._dtls'.realm,
- '_diameter._tcp'.realm and '_diameter._tls'.realm depending on
- the requesters network protocol capabilities. If SRV records are
- found then the requester can perform address record query (A RR's
+ one of the following SRV records: for Diameter over TCP, use
+ "_diameter._tcp.realm"; for Diameter over TLS, use
+ "_diameters._tcp.realm"; for Diameter over SCTP, use
+ "_diameter._sctp.realm"; for Diameter over DTLS, use
+ "_diameters._sctp.realm". If SRV records are found, then the
+ requester can perform address record query (A RR's and/or AAAA
+
-Fajardo, et al. Expires July 24, 2011 [Page 60]
+Fajardo, et al. Standards Track [Page 59]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
- and/or AAAA RR's) for the target hostname specified in the SRV
- records. If no SRV records are found, the requester gives up.
+ RR's) for the target hostname specified in the SRV records
+ following the rules given in [RFC2782]. If no SRV records are
+ found, the requester gives up.
If the server is using a site certificate, the domain name in the
NAPTR query and the domain name in the replacement field MUST both be
valid based on the site certificate handed out by the server in the
- TLS/TCP and DTLS/SCTP or IKE exchange. Similarly, the domain name in
- the SRV query and the domain name in the target in the SRV record
- MUST both be valid based on the same site certificate. Otherwise, an
- attacker could modify the DNS records to contain replacement values
- in a different domain, and the client could not validate that this
- was the desired behavior, or the result of an attack.
-
- Also, the Diameter Peer MUST check to make sure that the discovered
+ TLS/TCP and DTLS/SCTP or Internet Key Exchange Protocol (IKE)
+ exchange. Similarly, the domain name in the SRV query and the domain
+ name in the target in the SRV record MUST both be valid based on the
+ same site certificate. Otherwise, an attacker could modify the DNS
+ records to contain replacement values in a different domain, and the
+ client could not validate whether this was the desired behavior or
+ the result of an attack.
+
+ Also, the Diameter peer MUST check to make sure that the discovered
peers are authorized to act in its role. Authentication via IKE or
TLS/TCP and DTLS/SCTP, or validation of DNS RRs via DNSSEC is not
sufficient to conclude this. For example, a web server may have
obtained a valid TLS/TCP and DTLS/SCTP certificate, and secured RRs
may be included in the DNS, but this does not imply that it is
- authorized to act as a Diameter Server.
-
- Authorization can be achieved for example, by configuration of a
- Diameter Server CA. Alternatively this can be achieved by definition
- of OIDs within TLS/TCP and DTLS/SCTP or IKE certificates so as to
- signify Diameter Server authorization.
-
- A dynamically discovered peer causes an entry in the Peer Table (see
+ authorized to act as a Diameter server.
+
+ Authorization can be achieved, for example, by the configuration of a
+ Diameter server Certification Authority (CA). The server CA issues a
+ certificate to the Diameter server, which includes an Object
+ Identifier (OID) to indicate the subject is a Diameter server in the
+ Extended Key Usage extension [RFC5280]. This certificate is then
+ used during TLS/TCP, DTLS/SCTP, or IKE security negotiation.
+ However, note that, at the time of writing, no Diameter server
+ Certification Authorities exist.
+
+ A dynamically discovered peer causes an entry in the peer table (see
Section 2.6) to be created. Note that entries created via DNS MUST
- expire (or be refreshed) within the DNS TTL. If a peer is discovered
- outside of the local realm, a routing table entry (see Section 2.7)
- for the peer's realm is created. The routing table entry's
- expiration MUST match the peer's expiration value.
+ expire (or be refreshed) within the DNS Time to Live (TTL). If a
+ peer is discovered outside of the local realm, a routing table entry
+ (see Section 2.7) for the peer's realm is created. The routing table
+ entry's expiration MUST match the peer's expiration value.
5.3. Capabilities Exchange
@@ -3400,35 +3353,36 @@ Internet-Draft Diameter Base Protocol January 2011
exchange the Capabilities Exchange messages, as specified in the peer
state machine (see Section 5.6). This message allows the discovery
of a peer's identity and its capabilities (protocol version number,
- supported Diameter applications, security mechanisms, etc.)
+ the identifiers of supported Diameter applications, security
+ mechanisms, etc.).
- The receiver only issues commands to its peers that have advertised
- support for the Diameter application that defines the command. A
- Diameter node MUST cache the supported applications in order to
- ensure that unrecognized commands and/or AVPs are not unnecessarily
- sent to a peer.
-
- A receiver of a Capabilities-Exchange-Req (CER) message that does not
-Fajardo, et al. Expires July 24, 2011 [Page 61]
+Fajardo, et al. Standards Track [Page 60]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
- have any applications in common with the sender MUST return a
+ The receiver only issues commands to its peers that have advertised
+ support for the Diameter application that defines the command. A
+ Diameter node MUST cache the supported Application Ids in order to
+ ensure that unrecognized commands and/or AVPs are not unnecessarily
+ sent to a peer.
+
+ A receiver of a Capabilities-Exchange-Request (CER) message that does
+ not have any applications in common with the sender MUST return a
Capabilities-Exchange-Answer (CEA) with the Result-Code AVP set to
- DIAMETER_NO_COMMON_APPLICATION, and SHOULD disconnect the transport
+ DIAMETER_NO_COMMON_APPLICATION and SHOULD disconnect the transport
layer connection. Note that receiving a CER or CEA from a peer
- advertising itself as a Relay (see Section 2.4) MUST be interpreted
+ advertising itself as a relay (see Section 2.4) MUST be interpreted
as having common applications with the peer.
The receiver of the Capabilities-Exchange-Request (CER) MUST
determine common applications by computing the intersection of its
- own set of supported Application Id against all of the application
- identifier AVPs (Auth-Application-Id, Acct-Application-Id and Vendor-
- Specific-Application-Id) present in the CER. The value of the
+ own set of supported Application Ids against all of the
+ Application-Id AVPs (Auth-Application-Id, Acct-Application-Id, and
+ Vendor-Specific-Application-Id) present in the CER. The value of the
Vendor-Id AVP in the Vendor-Specific-Application-Id MUST NOT be used
during computation. The sender of the Capabilities-Exchange-Answer
(CEA) SHOULD include all of its supported applications as a hint to
@@ -3438,12 +3392,13 @@ Internet-Draft Diameter Base Protocol January 2011
and DTLS/SCTP connection prior to the CER/CEA exchange. This
protects the capabilities information of both peers. To support
older Diameter implementations that do not fully conform to this
- document, the transport security MAY still be negotiated via Inband-
- Security AVP. In this case, the receiver of a Capabilities-Exchange-
- Req (CER) message that does not have any security mechanisms in
- common with the sender MUST return a Capabilities-Exchange-Answer
- (CEA) with the Result-Code AVP set to DIAMETER_NO_COMMON_SECURITY,
- and SHOULD disconnect the transport layer connection.
+ document, the transport security MAY still be negotiated via an
+ Inband-Security AVP. In this case, the receiver of a Capabilities-
+ Exchange-Request (CER) message that does not have any security
+ mechanisms in common with the sender MUST return a Capabilities-
+ Exchange-Answer (CEA) with the Result-Code AVP set to
+ DIAMETER_NO_COMMON_SECURITY and SHOULD disconnect the transport layer
+ connection.
CERs received from unknown peers MAY be silently discarded, or a CEA
MAY be issued with the Result-Code AVP set to DIAMETER_UNKNOWN_PEER.
@@ -3455,34 +3410,34 @@ Internet-Draft Diameter Base Protocol January 2011
failure, all the pending transactions destined to the unknown peer
can be discarded.
- The CER and CEA messages MUST NOT be proxied, redirected or relayed.
+ The CER and CEA messages MUST NOT be proxied, redirected, or relayed.
- Since the CER/CEA messages cannot be proxied, it is still possible
- that an upstream agent receives a message for which it has no
- available peers to handle the application that corresponds to the
- Command-Code. In such instances, the 'E' bit is set in the answer
- message (see Section 7.) with the Result-Code AVP set to
- DIAMETER_UNABLE_TO_DELIVER to inform the downstream to take action
- (e.g., re-routing request to an alternate peer).
-
-Fajardo, et al. Expires July 24, 2011 [Page 62]
+Fajardo, et al. Standards Track [Page 61]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
+ Since the CER/CEA messages cannot be proxied, it is still possible
+ that an upstream agent will receive a message for which it has no
+ available peers to handle the application that corresponds to the
+ Command Code. In such instances, the 'E' bit is set in the answer
+ message (Section 7) with the Result-Code AVP set to
+ DIAMETER_UNABLE_TO_DELIVER to inform the downstream agent to take
+ action (e.g., re-routing request to an alternate peer).
+
With the exception of the Capabilities-Exchange-Request message, a
message of type Request that includes the Auth-Application-Id or
Acct-Application-Id AVPs, or a message with an application-specific
- command code, MAY only be forwarded to a host that has explicitly
+ Command Code MAY only be forwarded to a host that has explicitly
advertised support for the application (or has advertised the Relay
Application Id).
5.3.1. Capabilities-Exchange-Request
- The Capabilities-Exchange-Request (CER), indicated by the Command-
+ The Capabilities-Exchange-Request (CER), indicated by the Command
Code set to 257 and the Command Flags' 'R' bit set, is sent to
exchange local capabilities. Upon detection of a transport failure,
this message MUST NOT be sent to an alternate peer.
@@ -3490,7 +3445,7 @@ Internet-Draft Diameter Base Protocol January 2011
When Diameter is run over SCTP [RFC4960] or DTLS/SCTP [RFC6083],
which allow for connections to span multiple interfaces and multiple
IP addresses, the Capabilities-Exchange-Request message MUST contain
- one Host-IP- Address AVP for each potential IP address that MAY be
+ one Host-IP-Address AVP for each potential IP address that MAY be
locally used when transmitting Diameter messages.
Message Format
@@ -3510,9 +3465,20 @@ Internet-Draft Diameter Base Protocol January 2011
[ Firmware-Revision ]
* [ AVP ]
+
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 62]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
5.3.2. Capabilities-Exchange-Answer
- The Capabilities-Exchange-Answer (CEA), indicated by the Command-Code
+ The Capabilities-Exchange-Answer (CEA), indicated by the Command Code
set to 257 and the Command Flags' 'R' bit cleared, is sent in
response to a CER message.
@@ -3522,13 +3488,6 @@ Internet-Draft Diameter Base Protocol January 2011
one Host-IP-Address AVP for each potential IP address that MAY be
locally used when transmitting Diameter messages.
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 63]
-
-Internet-Draft Diameter Base Protocol January 2011
-
-
Message Format
<CEA> ::= < Diameter Header: 257 >
@@ -3552,22 +3511,34 @@ Internet-Draft Diameter Base Protocol January 2011
5.3.3. Vendor-Id AVP
The Vendor-Id AVP (AVP Code 266) is of type Unsigned32 and contains
- the IANA "SMI Network Management Private Enterprise Codes" [RFC3232]
- value assigned to the vendor of the Diameter device. It is
+ the IANA "SMI Network Management Private Enterprise Codes"
+ [ENTERPRISE] value assigned to the Diameter Software vendor. It is
envisioned that the combination of the Vendor-Id, Product-Name
- (Section 5.3.7) and the Firmware-Revision (Section 5.3.4) AVPs may
+ (Section 5.3.7), and Firmware-Revision (Section 5.3.4) AVPs may
provide useful debugging information.
- A Vendor-Id value of zero in the CER or CEA messages is reserved and
+ A Vendor-Id value of zero in the CER or CEA message is reserved and
indicates that this field is ignored.
+
+
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 63]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
5.3.4. Firmware-Revision AVP
The Firmware-Revision AVP (AVP Code 267) is of type Unsigned32 and is
used to inform a Diameter peer of the firmware revision of the
issuing device.
- For devices that do not have a firmware revision (general purpose
+ For devices that do not have a firmware revision (general-purpose
computers running Diameter software modules, for instance), the
revision of the Diameter software module may be reported instead.
@@ -3577,77 +3548,70 @@ Internet-Draft Diameter Base Protocol January 2011
to inform a Diameter peer of the sender's IP address. All source
addresses that a Diameter node expects to use with SCTP [RFC4960] or
DTLS/SCTP [RFC6083] MUST be advertised in the CER and CEA messages by
-
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 64]
-
-Internet-Draft Diameter Base Protocol January 2011
-
-
including a Host-IP-Address AVP for each address.
5.3.6. Supported-Vendor-Id AVP
The Supported-Vendor-Id AVP (AVP Code 265) is of type Unsigned32 and
contains the IANA "SMI Network Management Private Enterprise Codes"
- [RFC3232] value assigned to a vendor other than the device vendor but
- including the application vendor. This is used in the CER and CEA
- messages in order to inform the peer that the sender supports (a
- subset of) the vendor-specific AVPs defined by the vendor identified
+ [ENTERPRISE] value assigned to a vendor other than the device vendor
+ but including the application vendor. This is used in the CER and
+ CEA messages in order to inform the peer that the sender supports (a
+ subset of) the Vendor-Specific AVPs defined by the vendor identified
in this AVP. The value of this AVP MUST NOT be set to zero.
Multiple instances of this AVP containing the same value SHOULD NOT
be sent.
5.3.7. Product-Name AVP
- The Product-Name AVP (AVP Code 269) is of type UTF8String, and
- contains the vendor assigned name for the product. The Product-Name
+ The Product-Name AVP (AVP Code 269) is of type UTF8String and
+ contains the vendor-assigned name for the product. The Product-Name
AVP SHOULD remain constant across firmware revisions for the same
product.
-5.4. Disconnecting Peer connections
+5.4. Disconnecting Peer Connections
When a Diameter node disconnects one of its transport connections,
- its peer cannot know the reason for the disconnect, and will most
- likely assume that a connectivity problem occurred, or that the peer
+ its peer cannot know the reason for the disconnect and will most
+ likely assume that a connectivity problem occurred or that the peer
has rebooted. In these cases, the peer may periodically attempt to
reconnect, as stated in Section 2.1. In the event that the
- disconnect was a result of either a shortage of internal resources,
- or simply that the node in question has no intentions of forwarding
- any Diameter messages to the peer in the foreseeable future, a
- periodic connection request would not be welcomed. The
- Disconnection-Reason AVP contains the reason the Diameter node issued
- the Disconnect-Peer-Request message.
+ disconnect was a result of either a shortage of internal resources or
+ simply that the node in question has no intentions of forwarding any
+ Diameter messages to the peer in the foreseeable future, a periodic
- The Disconnect-Peer-Request message is used by a Diameter node to
- inform its peer of its intent to disconnect the transport layer, and
- that the peer shouldn't reconnect unless it has a valid reason to do
- so (e.g., message to be forwarded). Upon receipt of the message, the
- Disconnect-Peer-Answer is returned, which SHOULD contain an error if
- messages have recently been forwarded, and are likely in flight,
- which would otherwise cause a race condition.
- The receiver of the Disconnect-Peer-Answer initiates the transport
- disconnect. The sender of the Disconnect-Peer-Answer should be able
- to detect the transport closure and cleanup the connection.
+Fajardo, et al. Standards Track [Page 64]
+
+RFC 6733 Diameter Base Protocol October 2012
+ connection request would not be welcomed. The Disconnection-Reason
+ AVP contains the reason the Diameter node issued the Disconnect-Peer-
+ Request message.
-Fajardo, et al. Expires July 24, 2011 [Page 65]
-
-Internet-Draft Diameter Base Protocol January 2011
+ The Disconnect-Peer-Request message is used by a Diameter node to
+ inform its peer of its intent to disconnect the transport layer and
+ that the peer shouldn't reconnect unless it has a valid reason to do
+ so (e.g., message to be forwarded). Upon receipt of the message, the
+ Disconnect-Peer-Answer message is returned, which SHOULD contain an
+ error if messages have recently been forwarded, and are likely in
+ flight, which would otherwise cause a race condition.
+ The receiver of the Disconnect-Peer-Answer message initiates the
+ transport disconnect. The sender of the Disconnect-Peer-Answer
+ message should be able to detect the transport closure and clean up
+ the connection.
5.4.1. Disconnect-Peer-Request
- The Disconnect-Peer-Request (DPR), indicated by the Command-Code set
+ The Disconnect-Peer-Request (DPR), indicated by the Command Code set
to 282 and the Command Flags' 'R' bit set, is sent to a peer to
- inform its intentions to shutdown the transport connection. Upon
- detection of a transport failure, this message MUST NOT be sent to an
- alternate peer.
+ inform it of its intentions to shut down the transport connection.
+ Upon detection of a transport failure, this message MUST NOT be sent
+ to an alternate peer.
Message Format
@@ -3659,28 +3623,14 @@ Internet-Draft Diameter Base Protocol January 2011
5.4.2. Disconnect-Peer-Answer
- The Disconnect-Peer-Answer (DPA), indicated by the Command-Code set
+ The Disconnect-Peer-Answer (DPA), indicated by the Command Code set
to 282 and the Command Flags' 'R' bit cleared, is sent as a response
to the Disconnect-Peer-Request message. Upon receipt of this
- message, the transport connection is shutdown.
+ message, the transport connection is shut down.
- Message Format
- <DPA> ::= < Diameter Header: 282 >
- { Result-Code }
- { Origin-Host }
- { Origin-Realm }
- [ Error-Message ]
- [ Failed-AVP ]
- * [ AVP ]
-5.4.3. Disconnect-Cause AVP
- The Disconnect-Cause AVP (AVP Code 273) is of type Enumerated. A
- Diameter node MUST include this AVP in the Disconnect-Peer-Request
- message to inform the peer of the reason for its intention to
- shutdown the transport connection. The following values are
- supported:
@@ -3689,29 +3639,44 @@ Internet-Draft Diameter Base Protocol January 2011
+Fajardo, et al. Standards Track [Page 65]
+
+RFC 6733 Diameter Base Protocol October 2012
+ Message Format
+
+ <DPA> ::= < Diameter Header: 282 >
+ { Result-Code }
+ { Origin-Host }
+ { Origin-Realm }
+ [ Error-Message ]
+ [ Failed-AVP ]
+ * [ AVP ]
-Fajardo, et al. Expires July 24, 2011 [Page 66]
-
-Internet-Draft Diameter Base Protocol January 2011
+5.4.3. Disconnect-Cause AVP
+
+ The Disconnect-Cause AVP (AVP Code 273) is of type Enumerated. A
+ Diameter node MUST include this AVP in the Disconnect-Peer-Request
+ message to inform the peer of the reason for its intention to shut
+ down the transport connection. The following values are supported:
- REBOOTING 0
- A scheduled reboot is imminent. Receiver of DPR with above result
- code MAY attempt reconnection.
+ REBOOTING 0
+ A scheduled reboot is imminent. A receiver of a DPR with
+ above result code MAY attempt reconnection.
- BUSY 1
- The peer's internal resources are constrained, and it has
- determined that the transport connection needs to be closed.
- Receiver of DPR with above result code SHOULD NOT attempt
- reconnection.
+ BUSY 1
+ The peer's internal resources are constrained, and it has
+ determined that the transport connection needs to be closed.
+ A receiver of a DPR with above result code SHOULD NOT attempt
+ reconnection.
- DO_NOT_WANT_TO_TALK_TO_YOU 2
- The peer has determined that it does not see a need for the
- transport connection to exist, since it does not expect any
- messages to be exchanged in the near future. Receiver of DPR
- with above result code SHOULD NOT attempt reconnection.
+ DO_NOT_WANT_TO_TALK_TO_YOU 2
+ The peer has determined that it does not see a need for the
+ transport connection to exist, since it does not expect any
+ messages to be exchanged in the near future. A receiver of a
+ DPR with above result code SHOULD NOT attempt reconnection.
5.5. Transport Failure Detection
@@ -3723,9 +3688,21 @@ Internet-Draft Diameter Base Protocol January 2011
Watchdog-Answer messages, defined in this section, are used to pro-
actively detect transport failures.
+
+
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 66]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
5.5.1. Device-Watchdog-Request
- The Device-Watchdog-Request (DWR), indicated by the Command-Code set
+ The Device-Watchdog-Request (DWR), indicated by the Command Code set
to 280 and the Command Flags' 'R' bit set, is sent to a peer when no
traffic has been exchanged between two peers (see Section 5.5.3).
Upon detection of a transport failure, this message MUST NOT be sent
@@ -3741,18 +3718,10 @@ Internet-Draft Diameter Base Protocol January 2011
5.5.2. Device-Watchdog-Answer
- The Device-Watchdog-Answer (DWA), indicated by the Command-Code set
+ The Device-Watchdog-Answer (DWA), indicated by the Command Code set
to 280 and the Command Flags' 'R' bit cleared, is sent as a response
to the Device-Watchdog-Request message.
-
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 67]
-
-Internet-Draft Diameter Base Protocol January 2011
-
-
Message Format
<DWA> ::= < Diameter Header: 280 >
@@ -3767,7 +3736,7 @@ Internet-Draft Diameter Base Protocol January 2011
5.5.3. Transport Failure Algorithm
The transport failure algorithm is defined in [RFC3539]. All
- Diameter implementations MUST support the algorithm defined in the
+ Diameter implementations MUST support the algorithm defined in that
specification in order to be compliant to the Diameter base protocol.
5.5.4. Failover and Failback Procedures
@@ -3775,7 +3744,17 @@ Internet-Draft Diameter Base Protocol January 2011
In the event that a transport failure is detected with a peer, it is
necessary for all pending request messages to be forwarded to an
alternate agent, if possible. This is commonly referred to as
- failover.
+ "failover".
+
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 67]
+
+RFC 6733 Diameter Base Protocol October 2012
+
In order for a Diameter node to perform failover procedures, it is
necessary for the node to maintain a pending message queue for a
@@ -3783,16 +3762,16 @@ Internet-Draft Diameter Base Protocol January 2011
request is removed from the queue. The Hop-by-Hop Identifier field
is used to match the answer with the queued request.
- When a transport failure is detected, if possible all messages in the
- queue are sent to an alternate agent with the T flag set. On booting
- a Diameter client or agent, the T flag is also set on any records
- still remaining to be transmitted in non-volatile storage. An
- example of a case where it is not possible to forward the message to
- an alternate server is when the message has a fixed destination, and
- the unavailable peer is the message's final destination (see
- Destination-Host AVP). Such an error requires that the agent return
- an answer message with the 'E' bit set and the Result-Code AVP set to
- DIAMETER_UNABLE_TO_DELIVER.
+ When a transport failure is detected, if possible, all messages in
+ the queue are sent to an alternate agent with the T flag set. On
+ booting a Diameter client or agent, the T flag is also set on any
+ remaining records in non-volatile storage that are still waiting to
+ be transmitted. An example of a case where it is not possible to
+ forward the message to an alternate server is when the message has a
+ fixed destination, and the unavailable peer is the message's final
+ destination (see Destination-Host AVP). Such an error requires that
+ the agent return an answer message with the 'E' bit set and the
+ Result-Code AVP set to DIAMETER_UNABLE_TO_DELIVER.
It is important to note that multiple identical requests or answers
MAY be received as a result of a failover. The End-to-End Identifier
@@ -3801,17 +3780,9 @@ Internet-Draft Diameter Base Protocol January 2011
As described in Section 2.1, a connection request should be
periodically attempted with the failed peer in order to re-establish
-
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 68]
-
-Internet-Draft Diameter Base Protocol January 2011
-
-
the transport connection. Once a connection has been successfully
established, messages can once again be forwarded to the peer. This
- is commonly referred to as failback.
+ is commonly referred to as "failback".
5.6. Peer State Machine
@@ -3824,16 +3795,24 @@ Internet-Draft Diameter Base Protocol January 2011
This state machine is closely coupled with the state machine
described in [RFC3539], which is used to open, close, failover,
- probe, and reopen transport connections. Note in particular that
+ probe, and reopen transport connections. In particular, note that
[RFC3539] requires the use of watchdog messages to probe connections.
For Diameter, DWR and DWA messages are to be used.
- I- is used to represent the initiator (connecting) connection, while
- the R- is used to represent the responder (listening) connection.
- The lack of a prefix indicates that the event or action is the same
- regardless of the connection on which the event occurred.
+ The I- prefix is used to represent the initiator (connecting)
+ connection, while the R- prefix is used to represent the responder
+ (listening) connection. The lack of a prefix indicates that the
+ event or action is the same regardless of the connection on which the
+ event occurred.
+
+
+
+Fajardo, et al. Standards Track [Page 68]
+
+RFC 6733 Diameter Base Protocol October 2012
+
- The stable states that a state machine may be in are Closed, I-Open
+ The stable states that a state machine may be in are Closed, I-Open,
and R-Open; all other states are intermediate. Note that I-Open and
R-Open are equivalent except for whether the initiator or responder
transport connection is used for communication.
@@ -3848,23 +3827,15 @@ Internet-Draft Diameter Base Protocol January 2011
the results of an election on one peer are guaranteed to be the
inverse of the results on the other.
- For TLS/TCP and DTLS/SCTP usage, TLS/TCP and DTLS/SCTP handshake
+ For TLS/TCP and DTLS/SCTP usage, a TLS/TCP and DTLS/SCTP handshake
SHOULD begin when both ends are in the closed state prior to any
Diameter message exchanges. The TLS/TCP and DTLS/SCTP connection
SHOULD be established before sending any CER or CEA message to secure
and protect the capabilities information of both peers. The TLS/TCP
and DTLS/SCTP connection SHOULD be disconnected when the state
machine moves to the closed state. When connecting to responders
- that do not conform to this document (i.e. older Diameter
+ that do not conform to this document (i.e., older Diameter
implementations that are not prepared to received TLS/TCP and DTLS/
-
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 69]
-
-Internet-Draft Diameter Base Protocol January 2011
-
-
SCTP connections in the closed state), the initial TLS/TCP and DTLS/
SCTP connection attempt will fail. The initiator MAY then attempt to
connect via TCP or SCTP and initiate the TLS/TCP and DTLS/SCTP
@@ -3878,6 +3849,25 @@ Internet-Draft Diameter Base Protocol January 2011
Any implementation that produces equivalent results is considered
compliant.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 69]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
state event action next state
-----------------------------------------------------------------
Closed Start I-Snd-Conn-Req Wait-Conn-Ack
@@ -3914,13 +3904,6 @@ Internet-Draft Diameter Base Protocol January 2011
R-Conn-CER R-Reject Wait-Returns
Timeout Error Closed
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 70]
-
-Internet-Draft Diameter Base Protocol January 2011
-
-
R-Open Send-Message R-Snd-Message R-Open
R-Rcv-Message Process R-Open
R-Rcv-DWR Process-DWR, R-Open
@@ -3928,10 +3911,19 @@ Internet-Draft Diameter Base Protocol January 2011
R-Rcv-DWA Process-DWA R-Open
R-Conn-CER R-Reject R-Open
Stop R-Snd-DPR Closing
- R-Rcv-DPR R-Snd-DPA, Closed
- R-Disc
+ R-Rcv-DPR R-Snd-DPA Closing
R-Peer-Disc R-Disc Closed
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 70]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
I-Open Send-Message I-Snd-Message I-Open
I-Rcv-Message Process I-Open
I-Rcv-DWR Process-DWR, I-Open
@@ -3939,8 +3931,7 @@ Internet-Draft Diameter Base Protocol January 2011
I-Rcv-DWA Process-DWA I-Open
R-Conn-CER R-Reject I-Open
Stop I-Snd-DPR Closing
- I-Rcv-DPR I-Snd-DPA, Closed
- I-Disc
+ I-Rcv-DPR I-Snd-DPA Closing
I-Peer-Disc I-Disc Closed
Closing I-Rcv-DPA I-Disc Closed
@@ -3949,88 +3940,44 @@ Internet-Draft Diameter Base Protocol January 2011
I-Peer-Disc I-Disc Closed
R-Peer-Disc R-Disc Closed
-5.6.1. Incoming connections
+5.6.1. Incoming Connections
When a connection request is received from a Diameter peer, it is
not, in the general case, possible to know the identity of that peer
until a CER is received from it. This is because host and port
- determine the identity of a Diameter peer; and the source port of an
- incoming connection is arbitrary. Upon receipt of CER, the identity
- of the connecting peer can be uniquely determined from Origin-Host.
+ determine the identity of a Diameter peer; the source port of an
+ incoming connection is arbitrary. Upon receipt of a CER, the
+ identity of the connecting peer can be uniquely determined from the
+ Origin-Host.
For this reason, a Diameter peer must employ logic separate from the
state machine to receive connection requests, accept them, and await
- CER. Once CER arrives on a new connection, the Origin-Host that
- identifies the peer is used to locate the state machine associated
- with that peer, and the new connection and CER are passed to the
- state machine as an R-Conn-CER event.
+ the CER. Once the CER arrives on a new connection, the Origin-Host
+ that identifies the peer is used to locate the state machine
+ associated with that peer, and the new connection and CER are passed
+ to the state machine as an R-Conn-CER event.
The logic that handles incoming connections SHOULD close and discard
- the connection if any message other than CER arrives, or if an
+ the connection if any message other than a CER arrives or if an
implementation-defined timeout occurs prior to receipt of CER.
-
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 71]
-
-Internet-Draft Diameter Base Protocol January 2011
-
-
Because handling of incoming connections up to and including receipt
- of CER requires logic, separate from that of any individual state
+ of a CER requires logic, separate from that of any individual state
machine associated with a particular peer, it is described separately
in this section rather than in the state machine above.
5.6.2. Events
Transitions and actions in the automaton are caused by events. In
- this section, we will ignore the -I and -R prefix, since the actual
- event would be identical, but would occur on one of two possible
+ this section, we will ignore the I- and R- prefixes, since the actual
+ event would be identical, but it would occur on one of two possible
connections.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 72]
+Fajardo, et al. Standards Track [Page 71]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
Start The Diameter application has signaled that a
@@ -4053,7 +4000,8 @@ Internet-Draft Diameter Base Protocol January 2011
Rcv-CEA A CEA message from the peer was received.
- Rcv-Non-CEA A message other than CEA from the peer was received.
+ Rcv-Non-CEA A message, other than a CEA, from the peer was
+ received.
Peer-Disc A disconnection indication from the peer was received.
@@ -4066,7 +4014,7 @@ Internet-Draft Diameter Base Protocol January 2011
Send-Message A message is to be sent.
- Rcv-Message A message other than CER, CEA, DPR, DPA, DWR or DWA
+ Rcv-Message A message other than CER, CEA, DPR, DPA, DWR, or DWA
was received.
Stop The Diameter application has signaled that a
@@ -4077,16 +4025,15 @@ Internet-Draft Diameter Base Protocol January 2011
Actions in the automaton are caused by events and typically indicate
the transmission of packets and/or an action to be taken on the
- connection. In this section we will ignore the I- and R-prefix,
- since the actual action would be identical, but would occur on one of
- two possible connections.
-
+ connection. In this section, we will ignore the I- and R- prefixes,
+ since the actual action would be identical, but it would occur on one
+ of two possible connections.
-Fajardo, et al. Expires July 24, 2011 [Page 73]
+Fajardo, et al. Standards Track [Page 72]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
Snd-Conn-Req A transport connection is initiated with the peer.
@@ -4098,11 +4045,12 @@ Internet-Draft Diameter Base Protocol January 2011
is disconnected.
Process-CER The CER associated with the R-Conn-CER is processed.
+
Snd-CER A CER message is sent to the peer.
Snd-CEA A CEA message is sent to the peer.
- Cleanup If necessary, the connection is shutdown, and any
+ Cleanup If necessary, the connection is shut down, and any
local resources are freed.
Error The transport layer connection is disconnected,
@@ -4139,10 +4087,9 @@ Internet-Draft Diameter Base Protocol January 2011
-
-Fajardo, et al. Expires July 24, 2011 [Page 74]
+Fajardo, et al. Standards Track [Page 73]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
5.6.4. The Election Process
@@ -4150,134 +4097,97 @@ Internet-Draft Diameter Base Protocol January 2011
The election is performed on the responder. The responder compares
the Origin-Host received in the CER with its own Origin-Host as two
streams of octets. If the local Origin-Host lexicographically
- succeeds the received Origin-Host a Win-Election event is issued
- locally. Diameter identities are in ASCII form therefore the lexical
- comparison is consistent with DNS case insensitivity where octets
- that fall in the ASCII range 'a' through 'z' MUST compare equally to
- their upper-case counterparts between 'A' and 'Z'. See Appendix D
- for interactions between the Diameter protocol and Internationalized
- Domain Name (IDNs).
+ succeeds the received Origin-Host, a Win-Election event is issued
+ locally. Diameter identities are in ASCII form; therefore, the
+ lexical comparison is consistent with DNS case insensitivity, where
+ octets that fall in the ASCII range 'a' through 'z' MUST compare
+ equally to their uppercase counterparts between 'A' and 'Z'. See
+ Appendix D for interactions between the Diameter protocol and
+ Internationalized Domain Name (IDNs).
The winner of the election MUST close the connection it initiated.
Historically, maintaining the responder side of a connection was more
efficient than maintaining the initiator side. However, current
practices makes this distinction irrelevant.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 75]
-
-Internet-Draft Diameter Base Protocol January 2011
-
-
-6. Diameter message processing
+6. Diameter Message Processing
This section describes how Diameter requests and answers are created
and processed.
6.1. Diameter Request Routing Overview
- A request is sent towards its final destination using a combination
- of the Destination-Realm and Destination-Host AVPs, in one of these
- three combinations:
+ A request is sent towards its final destination using one of the
+ following three combinations of the Destination-Realm and
+ Destination-Host AVPs:
- o a request that is not able to be proxied (such as CER) MUST NOT
+ o A request that is not able to be proxied (such as a CER) MUST NOT
contain either Destination-Realm or Destination-Host AVPs.
- o a request that needs to be sent to a home server serving a
+ o A request that needs to be sent to a home server serving a
specific realm, but not to a specific server (such as the first
- request of a series of round-trips), MUST contain a Destination-
- Realm AVP, but MUST NOT contain a Destination-Host AVP. For
+ request of a series of round trips), MUST contain a Destination-
+ Realm AVP but MUST NOT contain a Destination-Host AVP. For
Diameter clients, the value of the Destination-Realm AVP MAY be
extracted from the User-Name AVP, or other methods.
- o otherwise, a request that needs to be sent to a specific home
- server among those serving a given realm, MUST contain both the
+ o Otherwise, a request that needs to be sent to a specific home
+ server among those serving a given realm MUST contain both the
Destination-Realm and Destination-Host AVPs.
The Destination-Host AVP is used as described above when the
destination of the request is fixed, which includes:
- o Authentication requests that span multiple round trips
+ o Authentication requests that span multiple round trips.
+
+
+
+
+Fajardo, et al. Standards Track [Page 74]
+
+RFC 6733 Diameter Base Protocol October 2012
+
o A Diameter message that uses a security mechanism that makes use
of a pre-established session key shared between the source and the
final destination of the message.
- o Server initiated messages that MUST be received by a specific
+ o Server-initiated messages that MUST be received by a specific
Diameter client (e.g., access device), such as the Abort-Session-
Request message, which is used to request that a particular user's
session be terminated.
- Note that an agent can forward a request to a host described in the
- Destination-Host AVP only if the host in question is included in its
- peer table (see Section 2.7). Otherwise, the request is routed based
- on the Destination-Realm only (see Sections 6.1.6).
+ Note that an agent can only forward a request to a host described in
+ the Destination-Host AVP if the host in question is included in its
+ peer table (see Section 2.6). Otherwise, the request is routed based
+ on the Destination-Realm only (see Section 6.1.6).
When a message is received, the message is processed in the following
order:
-
-
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 76]
-
-Internet-Draft Diameter Base Protocol January 2011
-
-
o If the message is destined for the local host, the procedures
listed in Section 6.1.4 are followed.
o If the message is intended for a Diameter peer with whom the local
host is able to directly communicate, the procedures listed in
- Section 6.1.5 are followed. This is known as Request Forwarding.
+ Section 6.1.5 are followed. This is known as "Request
+ Forwarding".
- o The procedures listed in Section 6.1.6 are followed, which is
- known as Request Routing.
+ o The procedure listed in Section 6.1.6 is followed, which is known
+ as "Request Routing".
- o If none of the above is successful, an answer is returned with the
- Result-Code set to DIAMETER_UNABLE_TO_DELIVER, with the E-bit set.
+ o If none of the above are successful, an answer is returned with
+ the Result-Code set to DIAMETER_UNABLE_TO_DELIVER, with the 'E'
+ bit set.
For routing of Diameter messages to work within an administrative
domain, all Diameter nodes within the realm MUST be peers.
- Note the processing rules contained in this section are intended to
- be used as general guidelines to Diameter developers. Certain
- implementations MAY use different methods than the ones described
- here, and still comply with the protocol specification. See Section
- 7 for more detail on error handling.
+ The overview contained in this section (6.1) is intended to provide
+ general guidelines to Diameter developers. Implementations are free
+ to use different methods than the ones described here as long as they
+ conform to the requirements specified in Sections 6.1.1 through
+ 6.1.9. See Section 7 for more details on error handling.
6.1.1. Originating a Request
@@ -4285,33 +4195,35 @@ Internet-Draft Diameter Base Protocol January 2011
described in the application definition for that specific request,
the following procedures MUST be followed:
- o the Command-Code is set to the appropriate value
- o the 'R' bit is set
- o the End-to-End Identifier is set to a locally unique value
- o the Origin-Host and Origin-Realm AVPs MUST be set to the
- appropriate values, used to identify the source of the message
- o the Destination-Host and Destination-Realm AVPs MUST be set to the
- appropriate values as described in Section 6.1.
+Fajardo, et al. Standards Track [Page 75]
+
+RFC 6733 Diameter Base Protocol October 2012
-6.1.2. Sending a Request
- When sending a request, originated either locally, or as the result
- of a forwarding or routing operation, the following procedures SHOULD
- be followed:
+ o the Command Code is set to the appropriate value;
- o The Hop-by-Hop Identifier SHOULD be set to a locally unique value.
+ o the 'R' bit is set;
+ o the End-to-End Identifier is set to a locally unique value;
+ o the Origin-Host and Origin-Realm AVPs MUST be set to the
+ appropriate values, used to identify the source of the message;
+ and
+ o the Destination-Host and Destination-Realm AVPs MUST be set to the
+ appropriate values, as described in Section 6.1.
-Fajardo, et al. Expires July 24, 2011 [Page 77]
-
-Internet-Draft Diameter Base Protocol January 2011
+6.1.2. Sending a Request
+ When sending a request, originated either locally or as the result of
+ a forwarding or routing operation, the following procedures SHOULD be
+ followed:
+
+ o The Hop-by-Hop Identifier SHOULD be set to a locally unique value.
o The message SHOULD be saved in the list of pending requests.
@@ -4328,25 +4240,34 @@ Internet-Draft Diameter Base Protocol January 2011
6.1.4. Processing Local Requests
A request is known to be for local consumption when one of the
- following conditions occur:
+ following conditions occurs:
- o The Destination-Host AVP contains the local host's identity,
+ o The Destination-Host AVP contains the local host's identity;
o The Destination-Host AVP is not present, the Destination-Realm AVP
contains a realm the server is configured to process locally, and
- the Diameter application is locally supported, or
+ the Diameter application is locally supported; or
o Both the Destination-Host and the Destination-Realm are not
present.
+
+
+
+
+Fajardo, et al. Standards Track [Page 76]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
When a request is locally processed, the rules in Section 6.2 should
be used to generate the corresponding answer.
6.1.5. Request Forwarding
- Request forwarding is done using the Diameter Peer Table. The
- Diameter peer table contains all of the peers that the local node is
- able to directly communicate with.
+ Request forwarding is done using the Diameter peer table. The
+ Diameter peer table contains all of the peers with which the local
+ node is able to directly communicate.
When a request is received, and the host encoded in the Destination-
Host AVP is one that is present in the peer table, the message SHOULD
@@ -4354,29 +4275,21 @@ Internet-Draft Diameter Base Protocol January 2011
6.1.6. Request Routing
- Diameter request message routing is done via realms and application
- identifiers. A Diameter message that may be forwarded by Diameter
- agents (proxies, redirect or relay agents) MUST include the target
+ Diameter request message routing is done via realms and Application
+ Ids. A Diameter message that may be forwarded by Diameter agents
+ (proxies, redirect agents, or relay agents) MUST include the target
realm in the Destination-Realm AVP. Request routing SHOULD rely on
the Destination-Realm AVP and the Application Id present in the
request message header to aid in the routing decision. The realm MAY
be retrieved from the User-Name AVP, which is in the form of a
-
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 78]
-
-Internet-Draft Diameter Base Protocol January 2011
-
-
Network Access Identifier (NAI). The realm portion of the NAI is
inserted in the Destination-Realm AVP.
Diameter agents MAY have a list of locally supported realms and
- applications, and MAY have a list of externally supported realms and
- applications. When a request is received that includes a realm
+ applications, and they MAY have a list of externally supported realms
+ and applications. When a request is received that includes a realm
and/or application that is not locally supported, the message is
- routed to the peer configured in the Routing Table (see Section 2.7).
+ routed to the peer configured in the routing table (see Section 2.7).
Realm names and Application Ids are the minimum supported routing
criteria, additional information may be needed to support redirect
@@ -4385,13 +4298,23 @@ Internet-Draft Diameter Base Protocol January 2011
6.1.7. Predictive Loop Avoidance
Before forwarding or routing a request, Diameter agents, in addition
- to processing done in Section 6.1.3, SHOULD check for the presence of
- candidate route's peer identity in any of the Route-Record AVPs. In
- an event of the agent detecting the presence of a candidate route's
- peer identity in a Route-Record AVP, the agent MUST ignore such route
- for the Diameter request message and attempt alternate routes if any.
- In case all the candidate routes are eliminated by the above
- criteria, the agent SHOULD return DIAMETER_UNABLE_TO_DELIVER message.
+ to performing the processing described in Section 6.1.3, SHOULD check
+ for the presence of a candidate route's peer identity in any of the
+ Route-Record AVPs. In the event of the agent detecting the presence
+ of a candidate route's peer identity in a Route-Record AVP, the agent
+ MUST ignore such a route for the Diameter request message and attempt
+ alternate routes if any exist. In case all the candidate routes are
+ eliminated by the above criteria, the agent SHOULD return a
+ DIAMETER_UNABLE_TO_DELIVER message.
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 77]
+
+RFC 6733 Diameter Base Protocol October 2012
+
6.1.8. Redirecting Requests
@@ -4399,7 +4322,7 @@ Internet-Draft Diameter Base Protocol January 2011
to REDIRECT, it MUST reply with an answer message with the 'E' bit
set, while maintaining the Hop-by-Hop Identifier in the header, and
include the Result-Code AVP to DIAMETER_REDIRECT_INDICATION. Each of
- the servers associated with the routing entry are added in separate
+ the servers associated with the routing entry are added in a separate
Redirect-Host AVP.
+------------------+
@@ -4417,21 +4340,13 @@ Internet-Draft Diameter Base Protocol January 2011
| Agent |<-------------| Server |
+-------------+ 4. Answer +-------------+
-
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 79]
-
-Internet-Draft Diameter Base Protocol January 2011
-
-
Figure 5: Diameter Redirect Agent
- The receiver of the answer message with the 'E' bit set, and the
- Result-Code AVP set to DIAMETER_REDIRECT_INDICATION uses the hop-by-
- hop field in the Diameter header to identify the request in the
- pending message queue (see Section 5.3) that is to be redirected. If
- no transport connection exists with the new agent, one is created,
+ The receiver of an answer message with the 'E' bit set and the
+ Result-Code AVP set to DIAMETER_REDIRECT_INDICATION uses the Hop-by-
+ Hop Identifier in the Diameter header to identify the request in the
+ pending message queue (see Section 5.5.4) that is to be redirected.
+ If no transport connection exists with the new peer, one is created,
and the request is sent directly to it.
Multiple Redirect-Host AVPs are allowed. The receiver of the answer
@@ -4440,17 +4355,26 @@ Internet-Draft Diameter Base Protocol January 2011
When the Redirect-Host-Usage AVP included in the answer message has a
non-zero value, a route entry for the redirect indications is created
- and cached by the receiver. The redirect usage for such route entry
- is set by the value of Redirect-Host-Usage AVP and the lifetime of
- the cached route entry is set by Redirect-Max-Cache-Time AVP value.
+ and cached by the receiver. The redirect usage for such a route
+ entry is set by the value of Redirect-Host-Usage AVP and the lifetime
+ of the cached route entry is set by Redirect-Max-Cache-Time AVP
+ value.
It is possible that multiple redirect indications can create multiple
cached route entries differing only in their redirect usage and the
peer to forward messages to. As an example, two(2) route entries
that are created by two(2) redirect indications results in two(2)
+
+
+
+Fajardo, et al. Standards Track [Page 78]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
cached routes for the same realm and Application Id. However, one
- has a redirect usage of ALL_SESSION where matching request will be
- forwarded to one peer and the other has a redirect usage of ALL_REALM
+ has a redirect usage of ALL_SESSION, where matching requests will be
+ forwarded to one peer; the other has a redirect usage of ALL_REALM,
where request are forwarded to another peer. Therefore, an incoming
request that matches the realm and Application Id of both routes will
need additional resolution. In such a case, a routing precedence
@@ -4460,32 +4384,24 @@ Internet-Draft Diameter Base Protocol January 2011
6.1.9. Relaying and Proxying Requests
A relay or proxy agent MUST append a Route-Record AVP to all requests
- forwarded. The AVP contains the identity of the peer the request was
- received from.
+ forwarded. The AVP contains the identity of the peer from which the
+ request was received.
- The Hop-by-Hop identifier in the request is saved, and replaced with
- a locally unique value. The source of the request is also saved,
- which includes the IP address, port and protocol.
+ The Hop-by-Hop Identifier in the request is saved and replaced with a
+ locally unique value. The source of the request is also saved, which
+ includes the IP address, port, and protocol.
A relay or proxy agent MAY include the Proxy-Info AVP in requests if
it requires access to any local state information when the
corresponding response is received. The Proxy-Info AVP has security
- implications as state information is distribute to other entities.
- As such, it is RECOMMMENDED to protect the content of the Proxy-Info
- AVP with cryptographic mechanisms, for example by using a keyed
-
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 80]
-
-Internet-Draft Diameter Base Protocol January 2011
-
-
- message digest. Such a mechanism, however, requires the management
- of keys, although only locally at the Diameter server. Still, a full
- description of the management of the keys used to protect the Proxy-
- Info AVP is beyond the scope of this document. Below is a list of
- commonly recommended:
+ implications as state information is distributed to other entities.
+ As such, it is RECOMMENDED that the content of the Proxy-Info AVP be
+ protected with cryptographic mechanisms, for example, by using a
+ keyed message digest such as HMAC-SHA1 [RFC2104]. Such a mechanism,
+ however, requires the management of keys, although only locally at
+ the Diameter server. Still, a full description of the management of
+ the keys used to protect the Proxy-Info AVP is beyond the scope of
+ this document. Below is a list of common recommendations:
o The keys should be generated securely following the randomness
recommendations in [RFC4086].
@@ -4494,32 +4410,39 @@ Internet-Draft Diameter Base Protocol January 2011
least 128 bits in strength.
o The keys should not be used for any other purpose than generating
- and verifying tickets.
+ and verifying instances of the Proxy-Info AVP.
o The keys should be changed regularly.
- o The keys should be changed if the ticket format or cryptographic
+ o The keys should be changed if the AVP format or cryptographic
protection algorithms change.
The message is then forwarded to the next hop, as identified in the
- Routing Table.
+ routing table.
+
+
+
+
+Fajardo, et al. Standards Track [Page 79]
+
+RFC 6733 Diameter Base Protocol October 2012
+
Figure 6 provides an example of message routing using the procedures
listed in these sections.
- (Origin-Host=nas.example.net) (Origin-Host=nas.example.net)
- (Origin-Realm=example.net) (Origin-Realm=example.net)
- (Destination-Realm=example.com) (Destination-
- Realm=example.com)
+ (Origin-Host=nas.example.net) (Origin-Host=nas.example.net)
+ (Origin-Realm=example.net) (Origin-Realm=example.net)
+ (Destination-Realm=example.com) (Destination-Realm=example.com)
(Route-Record=nas.example.net)
- +------+ ------> +------+ ------> +------+
- | | (Request) | | (Request) | |
- | NAS +-------------------+ DRL +-------------------+ HMS |
- | | | | | |
- +------+ <------ +------+ <------ +------+
- example.net (Answer) example.net (Answer) example.com
- (Origin-Host=hms.example.com) (Origin-Host=hms.example.com)
- (Origin-Realm=example.com) (Origin-Realm=example.com)
+ +------+ ------> +------+ ------> +------+
+ | | (Request) | | (Request) | |
+ | NAS +-------------------+ DRL +-------------------+ HMS |
+ | | | | | |
+ +------+ <------ +------+ <------ +------+
+ example.net (Answer) example.net (Answer) example.com
+ (Origin-Host=hms.example.com) (Origin-Host=hms.example.com)
+ (Origin-Realm=example.com) (Origin-Realm=example.com)
Figure 6: Routing of Diameter messages
@@ -4527,15 +4450,7 @@ Internet-Draft Diameter Base Protocol January 2011
incoming messages. At a minimum, validation of the message header
and relevant routing AVPs has to be done when relaying messages.
Proxy agents may optionally perform more in-depth message validation
- for applications it is interested in.
-
-
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 81]
-
-Internet-Draft Diameter Base Protocol January 2011
-
+ for applications in which it is interested.
6.2. Diameter Answer Processing
@@ -4544,7 +4459,7 @@ Internet-Draft Diameter Base Protocol January 2011
additional procedures that MAY be discussed in the Diameter
application defining the command:
- o The same Hop-by-Hop identifier in the request is used in the
+ o The same Hop-by-Hop Identifier in the request is used in the
answer.
o The local host's identity is encoded in the Origin-Host AVP.
@@ -4561,15 +4476,23 @@ Internet-Draft Diameter Base Protocol January 2011
o Any Proxy-Info AVPs in the request MUST be added to the answer
message, in the same order they were present in the request.
+
+
+
+Fajardo, et al. Standards Track [Page 80]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
o The 'P' bit is set to the same value as the one in the request.
o The same End-to-End identifier in the request is used in the
answer.
- Note that the error messages (see Section 7.3) are also subjected to
+ Note that the error messages (see Section 7) are also subjected to
the above processing rules.
-6.2.1. Processing received Answers
+6.2.1. Processing Received Answers
A Diameter client or proxy MUST match the Hop-by-Hop Identifier in an
answer received against the list of pending requests. The
@@ -4579,31 +4502,23 @@ Internet-Draft Diameter Base Protocol January 2011
6.2.2. Relaying and Proxying Answers
- If the answer is for a request which was proxied or relayed, the
- agent MUST restore the original value of the Diameter header's Hop-
- by-Hop Identifier field.
+ If the answer is for a request that was proxied or relayed, the agent
+ MUST restore the original value of the Diameter header's Hop-by-Hop
+ Identifier field.
If the last Proxy-Info AVP in the message is targeted to the local
Diameter server, the AVP MUST be removed before the answer is
-
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 82]
-
-Internet-Draft Diameter Base Protocol January 2011
-
-
forwarded.
If a relay or proxy agent receives an answer with a Result-Code AVP
indicating a failure, it MUST NOT modify the contents of the AVP.
- Any additional local errors detected SHOULD be logged, but not
+ Any additional local errors detected SHOULD be logged but not
reflected in the Result-Code AVP. If the agent receives an answer
message with a Result-Code AVP indicating success, and it wishes to
modify the AVP to indicate an error, it MUST modify the Result-Code
AVP to contain the appropriate error in the message destined towards
- the access device as well as include the Error-Reporting-Host AVP and
- it MUST issue an STR on behalf of the access device towards the
+ the access device as well as include the Error-Reporting-Host AVP; it
+ MUST also issue an STR on behalf of the access device towards the
Diameter server.
The agent MUST then send the answer to the host that it received the
@@ -4612,10 +4527,19 @@ Internet-Draft Diameter Base Protocol January 2011
6.3. Origin-Host AVP
The Origin-Host AVP (AVP Code 264) is of type DiameterIdentity, and
- MUST be present in all Diameter messages. This AVP identifies the
+ it MUST be present in all Diameter messages. This AVP identifies the
endpoint that originated the Diameter message. Relay agents MUST NOT
modify this AVP.
+
+
+
+
+Fajardo, et al. Standards Track [Page 81]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
The value of the Origin-Host AVP is guaranteed to be unique within a
single host.
@@ -4638,17 +4562,9 @@ Internet-Draft Diameter Base Protocol January 2011
The Destination-Host AVP (AVP Code 293) is of type DiameterIdentity.
This AVP MUST be present in all unsolicited agent initiated messages,
- MAY be present in request messages, and MUST NOT be present in Answer
+ MAY be present in request messages, and MUST NOT be present in answer
messages.
-
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 83]
-
-Internet-Draft Diameter Base Protocol January 2011
-
-
The absence of the Destination-Host AVP will cause a message to be
sent to any Diameter server supporting the application within the
realm specified in Destination-Realm AVP.
@@ -4658,20 +4574,28 @@ Internet-Draft Diameter Base Protocol January 2011
6.6. Destination-Realm AVP
- The Destination-Realm AVP (AVP Code 283) is of type DiameterIdentity,
- and contains the realm the message is to be routed to. The
- Destination-Realm AVP MUST NOT be present in Answer messages.
- Diameter Clients insert the realm portion of the User-Name AVP.
+ The Destination-Realm AVP (AVP Code 283) is of type DiameterIdentity
+ and contains the realm to which the message is to be routed. The
+ Destination-Realm AVP MUST NOT be present in answer messages.
+ Diameter clients insert the realm portion of the User-Name AVP.
Diameter servers initiating a request message use the value of the
Origin-Realm AVP from a previous message received from the intended
target host (unless it is known a priori). When present, the
Destination-Realm AVP is used to perform message routing decisions.
- An ABNF for a request message that includes the Destination-Realm AVP
+ The CCF for a request message that includes the Destination-Realm AVP
SHOULD list the Destination-Realm AVP as a required AVP (an AVP
- indicated as {AVP}) otherwise the message is inherently a non-
+ indicated as {AVP}); otherwise, the message is inherently a non-
routable message.
+
+
+
+Fajardo, et al. Standards Track [Page 82]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
This AVP SHOULD be placed as close to the Diameter header as
possible.
@@ -4692,19 +4616,11 @@ Internet-Draft Diameter Base Protocol January 2011
The Proxy-Info AVP (AVP Code 284) is of type Grouped. This AVP
contains the identity and local state information of the Diameter
node that creates and adds it to a message. The Grouped Data field
- has the following ABNF grammar:
+ has the following CCF grammar:
Proxy-Info ::= < AVP Header: 284 >
{ Proxy-Host }
{ Proxy-State }
-
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 84]
-
-Internet-Draft Diameter Base Protocol January 2011
-
-
* [ AVP ]
6.7.3. Proxy-Host AVP
@@ -4728,10 +4644,18 @@ Internet-Draft Diameter Base Protocol January 2011
Application-Id AVP MUST match the Application Id present in the
Diameter message header.
+
+
+
+Fajardo, et al. Standards Track [Page 83]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
6.9. Acct-Application-Id AVP
The Acct-Application-Id AVP (AVP Code 259) is of type Unsigned32 and
- is used in order to advertise support of the Accounting portion of an
+ is used in order to advertise support of the accounting portion of an
application (see Section 2.4). If present in a message other than
CER and CEA, the value of the Acct-Application-Id AVP MUST match the
Application Id present in the Diameter message header.
@@ -4740,10 +4664,10 @@ Internet-Draft Diameter Base Protocol January 2011
The Inband-Security-Id AVP (AVP Code 299) is of type Unsigned32 and
is used in order to advertise support of the security portion of the
- application. The use of this AVP in CER and CEA messages is no
- longer recommended. Instead, discovery of a Diameter entities
- security capabilities can be done either through static configuration
- or via Diameter Peer Discovery described in Section 5.2.
+ application. The use of this AVP in CER and CEA messages is NOT
+ RECOMMENDED. Instead, discovery of a Diameter entity's security
+ capabilities can be done either through static configuration or via
+ Diameter Peer Discovery as described in Section 5.2.
The following values are supported:
@@ -4753,36 +4677,37 @@ Internet-Draft Diameter Base Protocol January 2011
This peer does not support TLS/TCP and DTLS/SCTP. This is the
default value, if the AVP is omitted.
-
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 85]
-
-Internet-Draft Diameter Base Protocol January 2011
-
-
TLS 1
- This node supports TLS/TCP and DTLS/SCTP security, as defined by
- [RFC5246].
+ This node supports TLS/TCP [RFC5246] and DTLS/SCTP [RFC6083]
+ security.
6.11. Vendor-Specific-Application-Id AVP
The Vendor-Specific-Application-Id AVP (AVP Code 260) is of type
Grouped and is used to advertise support of a vendor-specific
- Diameter Application. Exactly one instance of either Auth-
+ Diameter application. Exactly one instance of either Auth-
Application-Id or Acct-Application-Id AVP MUST be present. The
Application Id carried by either Auth-Application-Id or Acct-
- Application-Id AVP MUST comply with vendor specific Application Id
- assignment described in Sec 11.3. It MUST also match the Application
- Id present in the Diameter header except when used in a CER or CEA
- message.
+ Application-Id AVP MUST comply with vendor-specific Application Id
+ assignment described in Section 11.3. It MUST also match the
+ Application Id present in the Diameter header except when used in a
+ CER or CEA message.
The Vendor-Id AVP is an informational AVP pertaining to the vendor
who may have authorship of the vendor-specific Diameter application.
It MUST NOT be used as a means of defining a completely separate
vendor-specific Application Id space.
+
+
+
+
+Fajardo, et al. Standards Track [Page 84]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
The Vendor-Specific-Application-Id AVP SHOULD be placed as close to
the Diameter header as possible.
@@ -4795,9 +4720,9 @@ Internet-Draft Diameter Base Protocol January 2011
A Vendor-Specific-Application-Id AVP MUST contain exactly one of
either Auth-Application-Id or Acct-Application-Id. If a Vendor-
- Specific-Application-Id is received without any of these two AVPs,
+ Specific-Application-Id is received without one of these two AVPs,
then the recipient SHOULD issue an answer with a Result-Code set to
- DIAMETER_MISSING_AVP. The answer SHOULD also include a Failed-AVP
+ DIAMETER_MISSING_AVP. The answer SHOULD also include a Failed-AVP,
which MUST contain an example of an Auth-Application-Id AVP and an
Acct-Application-Id AVP.
@@ -4805,22 +4730,13 @@ Internet-Draft Diameter Base Protocol January 2011
Auth-Application-Id and Acct-Application-Id, then the recipient MUST
issue an answer with Result-Code set to
DIAMETER_AVP_OCCURS_TOO_MANY_TIMES. The answer MUST also include a
- Failed-AVP which MUST contain the received Auth-Application-Id AVP
+ Failed-AVP, which MUST contain the received Auth-Application-Id AVP
and Acct-Application-Id AVP.
-
-
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 86]
-
-Internet-Draft Diameter Base Protocol January 2011
-
-
6.12. Redirect-Host AVP
The Redirect-Host AVP (AVP Code 292) is of type DiameterURI. One or
- more of instances of this AVP MUST be present if the answer message's
+ more instances of this AVP MUST be present if the answer message's
'E' bit is set and the Result-Code AVP is set to
DIAMETER_REDIRECT_INDICATION.
@@ -4836,63 +4752,55 @@ Internet-Draft Diameter Base Protocol January 2011
This AVP MAY be present in answer messages whose 'E' bit is set and
the Result-Code AVP is set to DIAMETER_REDIRECT_INDICATION.
- When present, this AVP provides a hints about how the routing entry
+ When present, this AVP provides hints about how the routing entry
resulting from the Redirect-Host is to be used. The following values
are supported:
+
+
+Fajardo, et al. Standards Track [Page 85]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
DONT_CACHE 0
The host specified in the Redirect-Host AVP SHOULD NOT be cached.
This is the default value.
-
ALL_SESSION 1
All messages within the same session, as defined by the same value
of the Session-ID AVP SHOULD be sent to the host specified in the
Redirect-Host AVP.
-
ALL_REALM 2
All messages destined for the realm requested SHOULD be sent to
the host specified in the Redirect-Host AVP.
-
REALM_AND_APPLICATION 3
All messages for the application requested to the realm specified
SHOULD be sent to the host specified in the Redirect-Host AVP.
-
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 87]
-
-Internet-Draft Diameter Base Protocol January 2011
-
-
ALL_APPLICATION 4
All messages for the application requested SHOULD be sent to the
host specified in the Redirect-Host AVP.
-
ALL_HOST 5
All messages that would be sent to the host that generated the
Redirect-Host SHOULD be sent to the host specified in the
- Redirect- Host AVP.
-
+ Redirect-Host AVP.
ALL_USER 6
All messages for the user requested SHOULD be sent to the host
specified in the Redirect-Host AVP.
-
-
When multiple cached routes are created by redirect indications and
they differ only in redirect usage and peers to forward requests to
(see Section 6.1.8), a precedence rule MUST be applied to the
@@ -4902,6 +4810,16 @@ Internet-Draft Diameter Base Protocol January 2011
other as they appear. The order is as follows:
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 86]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
1. ALL_SESSION
2. ALL_USER
@@ -4917,58 +4835,31 @@ Internet-Draft Diameter Base Protocol January 2011
6.14. Redirect-Max-Cache-Time AVP
The Redirect-Max-Cache-Time AVP (AVP Code 262) is of type Unsigned32.
- This AVP MUST be present in answer messages whose 'E' bit is set, the
- Result-Code AVP is set to DIAMETER_REDIRECT_INDICATION and the
- Redirect-Host-Usage AVP set to a non-zero value.
-
-
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 88]
-
-Internet-Draft Diameter Base Protocol January 2011
-
+ This AVP MUST be present in answer messages whose 'E' bit is set,
+ whose Result-Code AVP is set to DIAMETER_REDIRECT_INDICATION, and
+ whose Redirect-Host-Usage AVP set to a non-zero value.
This AVP contains the maximum number of seconds the peer and route
table entries, created as a result of the Redirect-Host, SHOULD be
cached. Note that once a host is no longer reachable, any associated
- cache, peer and routing table entries MUST be deleted.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ cache, peer, and routing table entries MUST be deleted.
+7. Error Handling
+ There are two different types of errors in Diameter; protocol errors
+ and application errors. A protocol error is one that occurs at the
+ base protocol level and MAY require per-hop attention (e.g., a
+ message routing error). Application errors, on the other hand,
+ generally occur due to a problem with a function specified in a
+ Diameter application (e.g., user authentication, missing AVP).
+ Result-Code AVP values that are used to report protocol errors MUST
+ only be present in answer messages whose 'E' bit is set. When a
+ request message is received that causes a protocol error, an answer
+ message is returned with the 'E' bit set, and the Result-Code AVP is
+ set to the appropriate protocol error value. As the answer is sent
+ back towards the originator of the request, each proxy or relay agent
+ MAY take action on the message.
@@ -4980,27 +4871,10 @@ Internet-Draft Diameter Base Protocol January 2011
-Fajardo, et al. Expires July 24, 2011 [Page 89]
+Fajardo, et al. Standards Track [Page 87]
-Internet-Draft Diameter Base Protocol January 2011
-
-
-7. Error Handling
+RFC 6733 Diameter Base Protocol October 2012
- There are two different types of errors in Diameter; protocol and
- application errors. A protocol error is one that occurs at the base
- protocol level, and MAY require per hop attention (e.g., message
- routing error). Application errors, on the other hand, generally
- occur due to a problem with a function specified in a Diameter
- application (e.g., user authentication, missing AVP).
-
- Result-Code AVP values that are used to report protocol errors MUST
- only be present in answer messages whose 'E' bit is set. When a
- request message is received that causes a protocol error, an answer
- message is returned with the 'E' bit set, and the Result-Code AVP is
- set to the appropriate protocol error value. As the answer is sent
- back towards the originator of the request, each proxy or relay agent
- MAY take action on the message.
1. Request +---------+ Link Broken
+-------------------------->|Diameter |----///----+
@@ -5014,7 +4888,7 @@ Internet-Draft Diameter Base Protocol January 2011
| Relay 3 |-----------+
+---------+
- Figure 7: Example of Protocol Error causing answer message
+ Figure 7: Example of Protocol Error Causing Answer Message
Figure 7 provides an example of a message forwarded upstream by a
Diameter relay. When the message is received by Relay 2, and it
@@ -5032,40 +4906,40 @@ Internet-Draft Diameter Base Protocol January 2011
+---------+ 4. Answer +---------+ 3. Answer +---------+
(Missing AVP) (Missing AVP)
- Figure 8: Example of Application Error Answer message
-
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 90]
-
-Internet-Draft Diameter Base Protocol January 2011
-
+ Figure 8: Example of Application Error Answer Message
Figure 8 provides an example of a Diameter message that caused an
application error. When application errors occur, the Diameter
- entity reporting the error clears the 'R' bit in the Command Flags,
+ entity reporting the error clears the 'R' bit in the Command Flags
and adds the Result-Code AVP with the proper value. Application
- errors do not require any proxy or relay agent involvement, and
- therefore the message would be forwarded back to the originator of
+ errors do not require any proxy or relay agent involvement;
+ therefore, the message would be forwarded back to the originator of
the request.
In the case where the answer message itself contains errors, any
related session SHOULD be terminated by sending an STR or ASR
message. The Termination-Cause AVP in the STR MAY be filled with the
appropriate value to indicate the cause of the error. An application
- MAY also send an application-specific request instead of STR or ASR
- to signal the error in the case where no state is maintained or to
- allow for some form of error recovery with the corresponding Diameter
- entity.
+ MAY also send an application-specific request instead of an STR or
+ ASR message to signal the error in the case where no state is
+ maintained or to allow for some form of error recovery with the
+ corresponding Diameter entity.
+
+
+
+Fajardo, et al. Standards Track [Page 88]
+
+RFC 6733 Diameter Base Protocol October 2012
+
There are certain Result-Code AVP application errors that require
additional AVPs to be present in the answer. In these cases, the
Diameter node that sets the Result-Code AVP to indicate the error
- MUST add the AVPs. Examples are:
+ MUST add the AVPs. Examples are as follows:
o A request with an unrecognized AVP is received with the 'M' bit
- (Mandatory bit) set, causes an answer to be sent with the Result-
- Code AVP set to DIAMETER_AVP_UNSUPPORTED, and the Failed-AVP AVP
+ (Mandatory bit) set causes an answer to be sent with the Result-
+ Code AVP set to DIAMETER_AVP_UNSUPPORTED and the Failed-AVP AVP
containing the offending AVP.
o A request with an AVP that is received with an unrecognized value
@@ -5073,46 +4947,46 @@ Internet-Draft Diameter Base Protocol January 2011
DIAMETER_INVALID_AVP_VALUE, with the Failed-AVP AVP containing the
AVP causing the error.
- o A received command which is missing AVP(s) that are defined as
- required in the commands ABNF; examples are AVPs indicated as
+ o A received command that is missing AVPs that are defined as
+ required in the commands CCF; examples are AVPs indicated as
{AVP}. The receiver issues an answer with the Result-Code set to
- DIAMETER_MISSING_AVP, and creates an AVP with the AVP Code and
+ DIAMETER_MISSING_AVP and creates an AVP with the AVP Code and
other fields set as expected in the missing AVP. The created AVP
- is then added to the Failed- AVP AVP.
+ is then added to the Failed-AVP AVP.
The Result-Code AVP describes the error that the Diameter node
encountered in its processing. In case there are multiple errors,
the Diameter node MUST report only the first error it encountered
- (detected possibly in some implementation dependent order). The
+ (detected possibly in some implementation-dependent order). The
specific errors that can be described by this AVP are described in
the following section.
+7.1. Result-Code AVP
+ The Result-Code AVP (AVP Code 268) is of type Unsigned32 and
+ indicates whether a particular request was completed successfully or
+ an error occurred. All Diameter answer messages in IETF-defined
+ Diameter application specifications MUST include one Result-Code AVP.
+ A non-successful Result-Code AVP (one containing a non-2xxx value
+ other than DIAMETER_REDIRECT_INDICATION) MUST include the Error-
+ Reporting-Host AVP if the host setting the Result-Code AVP is
+ different from the identity encoded in the Origin-Host AVP.
+ The Result-Code data field contains an IANA-managed 32-bit address
+ space representing errors (see Section 11.3.2). Diameter provides
+ the following classes of errors, all identified by the thousands
+ digit in the decimal notation:
-Fajardo, et al. Expires July 24, 2011 [Page 91]
-
-Internet-Draft Diameter Base Protocol January 2011
-7.1. Result-Code AVP
- The Result-Code AVP (AVP Code 268) is of type Unsigned32 and
- indicates whether a particular request was completed successfully or
- whether an error occurred. All Diameter answer messages in IETF
- defined Diameter application specification MUST include one Result-
- Code AVP. A non-successful Result-Code AVP (one containing a non
- 2xxx value other than DIAMETER_REDIRECT_INDICATION) MUST include the
- Error-Reporting-Host AVP if the host setting the Result-Code AVP is
- different from the identity encoded in the Origin-Host AVP.
+Fajardo, et al. Standards Track [Page 89]
+
+RFC 6733 Diameter Base Protocol October 2012
- The Result-Code data field contains an IANA-managed 32-bit address
- space representing errors (see Section 11.4). Diameter provides the
- following classes of errors, all identified by the thousands digit in
- the decimal notation:
o 1xxx (Informational)
@@ -5124,7 +4998,7 @@ Internet-Draft Diameter Base Protocol January 2011
o 5xxx (Permanent Failure)
- A non-recognized class (one whose first digit is not defined in this
+ An unrecognized class (one whose first digit is not defined in this
section) MUST be handled as a permanent failure.
7.1.1. Informational
@@ -5133,7 +5007,6 @@ Internet-Draft Diameter Base Protocol January 2011
requester that a request could not be satisfied, and additional
action is required on its part before access is granted.
-
DIAMETER_MULTI_ROUND_AUTH 1001
This informational error is returned by a Diameter server to
@@ -5141,24 +5014,11 @@ Internet-Draft Diameter Base Protocol January 2011
used requires multiple round trips, and a subsequent request needs
to be issued in order for access to be granted.
-
-
-
-
-
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 92]
-
-Internet-Draft Diameter Base Protocol January 2011
-
-
7.1.2. Success
Errors that fall within the Success category are used to inform a
peer that a request has been successfully completed.
-
DIAMETER_SUCCESS 2001
The request was successfully completed.
@@ -5174,27 +5034,33 @@ Internet-Draft Diameter Base Protocol January 2011
Errors that fall within the Protocol Error category SHOULD be treated
on a per-hop basis, and Diameter proxies MAY attempt to correct the
error, if it is possible. Note that these errors MUST only be used
- in answer messages whose 'E' bit is set. This document omits some
- error codes defined in [RFC3588]. To provide backward compatibility
- with [RFC3588] implementations these error code values are not re-
- used and hence the error codes values enumerated below are non-
- sequential.
+ in answer messages whose 'E' bit is set.
+
+
+Fajardo, et al. Standards Track [Page 90]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ DIAMETER_COMMAND_UNSUPPORTED 3001
+
+ This error code is used when a Diameter entity receives a message
+ with a Command Code that it does not support.
+
DIAMETER_UNABLE_TO_DELIVER 3002
- This error is given when Diameter can not deliver the message to
+ This error is given when Diameter cannot deliver the message to
the destination, either because no host within the realm
supporting the required application was available to process the
- request, or because Destination-Host AVP was given without the
+ request or because the Destination-Host AVP was given without the
associated Destination-Realm AVP.
-
DIAMETER_REALM_NOT_SERVED 3003
The intended realm of the request is not recognized.
-
DIAMETER_TOO_BUSY 3004
When returned, a Diameter node SHOULD attempt to send the message
@@ -5202,13 +5068,6 @@ Internet-Draft Diameter Base Protocol January 2011
specific server is requested, and it cannot provide the requested
service.
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 93]
-
-Internet-Draft Diameter Base Protocol January 2011
-
-
DIAMETER_LOOP_DETECTED 3005
An agent detected a loop while trying to get the message to the
@@ -5216,121 +5075,110 @@ Internet-Draft Diameter Base Protocol January 2011
if one is available, but the peer reporting the error has
identified a configuration problem.
-
DIAMETER_REDIRECT_INDICATION 3006
A redirect agent has determined that the request could not be
- satisfied locally and the initiator of the request SHOULD direct
+ satisfied locally, and the initiator of the request SHOULD direct
the request directly to the server, whose contact information has
been added to the response. When set, the Redirect-Host AVP MUST
be present.
-
DIAMETER_APPLICATION_UNSUPPORTED 3007
A request was sent for an application that is not supported.
+ DIAMETER_INVALID_HDR_BITS 3008
- DIAMETER_INVALID_BIT_IN_HEADER 3011
+ A request was received whose bits in the Diameter header were set
+ either to an invalid combination or to a value that is
+ inconsistent with the Command Code's definition.
- This error is returned when a reserved bit in the Diameter header
- is set to one (1) or the bits in the Diameter header defined in
- Section 3 are set incorrectly.
- DIAMETER_INVALID_MESSAGE_LENGTH 3012
+Fajardo, et al. Standards Track [Page 91]
+
+RFC 6733 Diameter Base Protocol October 2012
- This error is returned when a request is received with an invalid
- message length.
+ DIAMETER_INVALID_AVP_BITS 3009
+
+ A request was received that included an AVP whose flag bits are
+ set to an unrecognized value or that is inconsistent with the
+ AVP's definition.
+
+ DIAMETER_UNKNOWN_PEER 3010
+
+ A CER was received from an unknown peer.
7.1.4. Transient Failures
Errors that fall within the transient failures category are used to
inform a peer that the request could not be satisfied at the time it
- was received, but MAY be able to satisfy the request in the future.
+ was received but MAY be able to satisfy the request in the future.
Note that these errors MUST be used in answer messages whose 'E' bit
is not set.
-
DIAMETER_AUTHENTICATION_REJECTED 4001
The authentication process for the user failed, most likely due to
an invalid password used by the user. Further attempts MUST only
-
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 94]
-
-Internet-Draft Diameter Base Protocol January 2011
-
-
be tried after prompting the user for a new password.
-
DIAMETER_OUT_OF_SPACE 4002
A Diameter node received the accounting request but was unable to
commit it to stable storage due to a temporary lack of space.
-
ELECTION_LOST 4003
The peer has determined that it has lost the election process and
has therefore disconnected the transport connection.
-
7.1.5. Permanent Failures
Errors that fall within the permanent failures category are used to
- inform the peer that the request failed, and should not be attempted
+ inform the peer that the request failed and should not be attempted
again. Note that these errors SHOULD be used in answer messages
whose 'E' bit is not set. In error conditions where it is not
- possible or efficient to compose application-specific answer grammar
- then answer messages with E-bit set and complying to the grammar
- described in 7.2 MAY also be used for permanent errors.
+ possible or efficient to compose application-specific answer grammar,
+ answer messages with the 'E' bit set and which comply to the grammar
+ described in Section 7.2 MAY also be used for permanent errors.
- To provide backward compatibility with existing implementations that
- follow [RFC3588], some of the error values that have previously been
- used in this category by [RFC3588] will not be re-used. Therefore
- the error values enumerated here may be non-sequential.
- DIAMETER_AVP_UNSUPPORTED 5001
- The peer received a message that contained an AVP that is not
- recognized or supported and was marked with the Mandatory bit. A
- Diameter message with this error MUST contain one or more Failed-
- AVP AVP containing the AVPs that caused the failure.
- DIAMETER_UNKNOWN_SESSION_ID 5002
- The request contained an unknown Session-Id.
+Fajardo, et al. Standards Track [Page 92]
+
+RFC 6733 Diameter Base Protocol October 2012
- DIAMETER_AUTHORIZATION_REJECTED 5003
- A request was received for which the user could not be authorized.
- This error could occur if the service requested is not permitted
+ DIAMETER_AVP_UNSUPPORTED 5001
+ The peer received a message that contained an AVP that is not
+ recognized or supported and was marked with the 'M' (Mandatory)
+ bit. A Diameter message with this error MUST contain one or more
+ Failed-AVP AVPs containing the AVPs that caused the failure.
+ DIAMETER_UNKNOWN_SESSION_ID 5002
-Fajardo, et al. Expires July 24, 2011 [Page 95]
-
-Internet-Draft Diameter Base Protocol January 2011
+ The request contained an unknown Session-Id.
+ DIAMETER_AUTHORIZATION_REJECTED 5003
+ A request was received for which the user could not be authorized.
+ This error could occur if the service requested is not permitted
to the user.
-
DIAMETER_INVALID_AVP_VALUE 5004
The request contained an AVP with an invalid value in its data
portion. A Diameter message indicating this error MUST include
the offending AVPs within a Failed-AVP AVP.
-
DIAMETER_MISSING_AVP 5005
The request did not contain an AVP that is required by the Command
@@ -5340,21 +5188,28 @@ Internet-Draft Diameter Base Protocol January 2011
Vendor-Id if applicable. The value field of the missing AVP
should be of correct minimum length and contain zeroes.
-
DIAMETER_RESOURCES_EXCEEDED 5006
A request was received that cannot be authorized because the user
has already expended allowed resources. An example of this error
- condition is a user that is restricted to one dial-up PPP port,
- attempts to establish a second PPP connection.
-
+ condition is when a user that is restricted to one dial-up PPP
+ port attempts to establish a second PPP connection.
DIAMETER_CONTRADICTING_AVPS 5007
The Home Diameter server has detected AVPs in the request that
- contradicted each other, and is not willing to provide service to
- the user. The Failed-AVP AVPs MUST be present which contains the
- AVPs that contradicted each other.
+ contradicted each other, and it is not willing to provide service
+ to the user. The Failed-AVP AVP MUST be present, which contain
+ the AVPs that contradicted each other.
+
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 93]
+
+RFC 6733 Diameter Base Protocol October 2012
DIAMETER_AVP_NOT_ALLOWED 5008
@@ -5363,22 +5218,12 @@ Internet-Draft Diameter Base Protocol January 2011
Failed-AVP AVP MUST be included and contain a copy of the
offending AVP.
-
DIAMETER_AVP_OCCURS_TOO_MANY_TIMES 5009
A message was received that included an AVP that appeared more
often than permitted in the message definition. The Failed-AVP
AVP MUST be included and contain a copy of the first instance of
-
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 96]
-
-Internet-Draft Diameter Base Protocol January 2011
-
-
- the offending AVP that exceeded the maximum number of occurrences
-
+ the offending AVP that exceeded the maximum number of occurrences.
DIAMETER_NO_COMMON_APPLICATION 5010
@@ -5386,18 +5231,21 @@ Internet-Draft Diameter Base Protocol January 2011
whereby no applications are common between the CER sending peer
and the CER receiving peer.
-
DIAMETER_UNSUPPORTED_VERSION 5011
This error is returned when a request was received, whose version
number is unsupported.
-
DIAMETER_UNABLE_TO_COMPLY 5012
This error is returned when a request is rejected for unspecified
reasons.
+ DIAMETER_INVALID_BIT_IN_HEADER 5013
+
+ This error is returned when a reserved bit in the Diameter header
+ is set to one (1) or the bits in the Diameter header are set
+ incorrectly.
DIAMETER_INVALID_AVP_LENGTH 5014
@@ -5407,56 +5255,41 @@ Internet-Draft Diameter Base Protocol January 2011
value exceeds the message length or is less than the minimum AVP
header length, it is sufficient to include the offending AVP
header and a zero filled payload of the minimum required length
- for the payloads data type. If the AVP is a grouped AVP, the
- grouped AVP header with an empty payload would be sufficient to
+ for the payloads data type. If the AVP is a Grouped AVP, the
+ Grouped AVP header with an empty payload would be sufficient to
indicate the offending AVP. In the case where the offending AVP
header cannot be fully decoded when the AVP length is less than
- the minimum AVP header length, it is sufficient to include an
- offending AVP header that is formulated by padding the incomplete
- AVP header with zero up to the minimum AVP header length.
-
-
- DIAMETER_NO_COMMON_SECURITY 5017
-
- This error is returned when a CER message is received, and there
- are no common security mechanisms supported between the peers. A
- Capabilities-Exchange-Answer (CEA) MUST be returned with the
- Result-Code AVP set to DIAMETER_NO_COMMON_SECURITY.
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 97]
+Fajardo, et al. Standards Track [Page 94]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
- DIAMETER_UNKNOWN_PEER 5018
-
- A CER was received from an unknown peer.
-
-
- DIAMETER_COMMAND_UNSUPPORTED 5019
-
- This error code is used when a Diameter entity receives a message
- with a Command Code that it does not support.
-
+ the minimum AVP header length, it is sufficient to include an
+ offending AVP header that is formulated by padding the incomplete
+ AVP header with zero up to the minimum AVP header length.
- DIAMETER_INVALID_HDR_BITS 5020
+ DIAMETER_INVALID_MESSAGE_LENGTH 5015
- A request was received whose bits in the Diameter header were
- either set to an invalid combination, or to a value that is
- inconsistent with the command code's definition.
+ This error is returned when a request is received with an invalid
+ message length.
+ DIAMETER_INVALID_AVP_BIT_COMBO 5016
- DIAMETER_INVALID_AVP_BITS 5021
+ The request contained an AVP with which is not allowed to have the
+ given value in the AVP Flags field. A Diameter message indicating
+ this error MUST include the offending AVPs within a Failed-AVP
+ AVP.
- A request was received that included an AVP whose flag bits are
- set to an unrecognized value, or that is inconsistent with the
- AVP's definition.
+ DIAMETER_NO_COMMON_SECURITY 5017
+ This error is returned when a CER message is received, and there
+ are no common security mechanisms supported between the peers. A
+ Capabilities-Exchange-Answer (CEA) message MUST be returned with
+ the Result-Code AVP set to DIAMETER_NO_COMMON_SECURITY.
7.2. Error Bit
@@ -5465,12 +5298,12 @@ Internet-Draft Diameter Base Protocol January 2011
the 'E' bit MUST NOT be sent as a response to an answer message.
Note that a message with the 'E' bit set is still subjected to the
processing rules defined in Section 6.2. When set, the answer
- message will not conform to the ABNF specification for the command,
- and will instead conform to the following ABNF:
+ message will not conform to the CCF specification for the command;
+ instead, it and will conform to the following CCF:
Message Format
- <answer-message> ::= < Diameter Header: code, ERR [PXY] >
+ <answer-message> ::= < Diameter Header: code, ERR [, PXY] >
0*1< Session-Id >
{ Origin-Host }
{ Origin-Realm }
@@ -5481,16 +5314,16 @@ Internet-Draft Diameter Base Protocol January 2011
[ Failed-AVP ]
[ Experimental-Result ]
* [ Proxy-Info ]
+ * [ AVP ]
-Fajardo, et al. Expires July 24, 2011 [Page 98]
+
+Fajardo, et al. Standards Track [Page 95]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
- * [ AVP ]
-
Note that the code used in the header is the same than the one found
in the request message, but with the 'R' bit cleared and the 'E' bit
set. The 'P' bit in the header is set to the same value as the one
@@ -5499,10 +5332,10 @@ Internet-Draft Diameter Base Protocol January 2011
7.3. Error-Message AVP
The Error-Message AVP (AVP Code 281) is of type UTF8String. It MAY
- accompany a Result-Code AVP as a human readable error message. The
+ accompany a Result-Code AVP as a human-readable error message. The
Error-Message AVP is not intended to be useful in an environment
where error messages are processed automatically. It SHOULD NOT be
- expected that the content of this AVP is parsed by network entities.
+ expected that the content of this AVP be parsed by network entities.
7.4. Error-Reporting-Host AVP
@@ -5511,8 +5344,8 @@ Internet-Draft Diameter Base Protocol January 2011
host that sent the Result-Code AVP to a value other than 2001
(Success), only if the host setting the Result-Code is different from
the one encoded in the Origin-Host AVP. This AVP is intended to be
- used for troubleshooting purposes, and MUST be set when the Result-
- Code AVP indicates a failure.
+ used for troubleshooting purposes, and it MUST be set when the
+ Result-Code AVP indicates a failure.
7.5. Failed-AVP AVP
@@ -5520,43 +5353,44 @@ Internet-Draft Diameter Base Protocol January 2011
debugging information in cases where a request is rejected or not
fully processed due to erroneous information in a specific AVP. The
value of the Result-Code AVP will provide information on the reason
- for the Failed-AVP AVP. A Diameter message SHOULD contain only one
- Failed-AVP that corresponds to the error indicated by the Result-Code
- AVP. For practical purposes, this Failed-AVP would typically refer
- to the first AVP processing error that a Diameter node encounters.
+ for the Failed-AVP AVP. A Diameter answer message SHOULD contain an
+ instance of the Failed-AVP AVP that corresponds to the error
+ indicated by the Result-Code AVP. For practical purposes, this
+ Failed-AVP would typically refer to the first AVP processing error
+ that a Diameter node encounters.
The possible reasons for this AVP are the presence of an improperly
constructed AVP, an unsupported or unrecognized AVP, an invalid AVP
value, the omission of a required AVP, the presence of an explicitly
- excluded AVP (see tables in Section 10), or the presence of two or
- more occurrences of an AVP which is restricted to 0, 1, or 0-1
+ excluded AVP (see tables in Section 10) or the presence of two or
+ more occurrences of an AVP that is restricted to 0, 1, or 0-1
occurrences.
A Diameter message SHOULD contain one Failed-AVP AVP, containing the
entire AVP that could not be processed successfully. If the failure
reason is omission of a required AVP, an AVP with the missing AVP
- code, the missing vendor id, and a zero filled payload of the minimum
+ code, the missing Vendor-Id, and a zero-filled payload of the minimum
required length for the omitted AVP will be added. If the failure
+ reason is an invalid AVP length where the reported length is less
-Fajardo, et al. Expires July 24, 2011 [Page 99]
+Fajardo, et al. Standards Track [Page 96]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
- reason is an invalid AVP length where the reported length is less
than the minimum AVP header length or greater than the reported
- message length, a copy of the offending AVP header and a zero filled
+ message length, a copy of the offending AVP header and a zero-filled
payload of the minimum required length SHOULD be added.
- In the case where the offending AVP is embedded within a grouped AVP,
- the Failed-AVP MAY contain the grouped AVP which in turn contains the
- single offending AVP. The same method MAY be employed if the grouped
- AVP itself is embedded in yet another grouped AVP and so on. In this
- case, the Failed-AVP MAY contain the grouped AVP hierarchy up to the
- single offending AVP. This enables the recipient to detect the
- location of the offending AVP when embedded in a group.
+ In the case where the offending AVP is embedded within a Grouped AVP,
+ the Failed-AVP MAY contain the grouped AVP, which in turn contains
+ the single offending AVP. The same method MAY be employed if the
+ grouped AVP itself is embedded in yet another grouped AVP and so on.
+ In this case, the Failed-AVP MAY contain the grouped AVP hierarchy up
+ to the single offending AVP. This enables the recipient to detect
+ the location of the offending AVP when embedded in a group.
AVP Format
@@ -5577,7 +5411,7 @@ Internet-Draft Diameter Base Protocol January 2011
{ Experimental-Result-Code }
The Vendor-Id AVP (see Section 5.3.3) in this grouped AVP identifies
- the vendor responsible for the assignment of the result code which
+ the vendor responsible for the assignment of the result code that
follows. All Diameter answer messages defined in vendor-specific
applications MUST include either one Result-Code AVP or one
Experimental-Result AVP.
@@ -5590,23 +5424,24 @@ Internet-Draft Diameter Base Protocol January 2011
It is recommended that vendor-specific result codes follow the same
conventions given for the Result-Code AVP regarding the different
- types of result codes and the handling of errors (for non 2xxx
+ types of result codes and the handling of errors (for non-2xxx
values).
-Fajardo, et al. Expires July 24, 2011 [Page 100]
+
+Fajardo, et al. Standards Track [Page 97]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
8. Diameter User Sessions
In general, Diameter can provide two different types of services to
applications. The first involves authentication and authorization,
- and can optionally make use of accounting. The second only makes use
- of accounting.
+ and it can optionally make use of accounting. The second only makes
+ use of accounting.
When a service makes use of the authentication and/or authorization
portion of an application, and a user requests access to the network,
@@ -5614,27 +5449,28 @@ Internet-Draft Diameter Base Protocol January 2011
auth request is defined in a service-specific Diameter application
(e.g., NASREQ). The request contains a Session-Id AVP, which is used
in subsequent messages (e.g., subsequent authorization, accounting,
- etc) relating to the user's session. The Session-Id AVP is a means
+ etc.) relating to the user's session. The Session-Id AVP is a means
for the client and servers to correlate a Diameter message with a
user session.
- When a Diameter server authorizes a user to use network resources for
- a finite amount of time, and it is willing to extend the
- authorization via a future request, it MUST add the Authorization-
- Lifetime AVP to the answer message. The Authorization-Lifetime AVP
- defines the maximum number of seconds a user MAY make use of the
- resources before another authorization request is expected by the
- server. The Auth-Grace-Period AVP contains the number of seconds
- following the expiration of the Authorization-Lifetime, after which
- the server will release all state information related to the user's
- session. Note that if payment for services is expected by the
- serving realm from the user's home realm, the Authorization-Lifetime
- AVP, combined with the Auth-Grace-Period AVP, implies the maximum
- length of the session the home realm is willing to be fiscally
- responsible for. Services provided past the expiration of the
- Authorization-Lifetime and Auth-Grace-Period AVPs are the
- responsibility of the access device. Of course, the actual cost of
- services rendered is clearly outside the scope of the protocol.
+ When a Diameter server authorizes a user to implement network
+ resources for a finite amount of time, and it is willing to extend
+ the authorization via a future request, it MUST add the
+ Authorization- Lifetime AVP to the answer message. The
+ Authorization-Lifetime AVP defines the maximum number of seconds a
+ user MAY make use of the resources before another authorization
+ request is expected by the server. The Auth-Grace-Period AVP
+ contains the number of seconds following the expiration of the
+ Authorization-Lifetime, after which the server will release all state
+ information related to the user's session. Note that if payment for
+ services is expected by the serving realm from the user's home realm,
+ the Authorization-Lifetime AVP, combined with the Auth-Grace-Period
+ AVP, implies the maximum length of the session for which the home
+ realm is willing to be fiscally responsible. Services provided past
+ the expiration of the Authorization-Lifetime and Auth-Grace-Period
+ AVPs are the responsibility of the access device. Of course, the
+ actual cost of services rendered is clearly outside the scope of the
+ protocol.
An access device that does not expect to send a re-authorization or a
session termination request to the server MAY include the Auth-
@@ -5648,32 +5484,33 @@ Internet-Draft Diameter Base Protocol January 2011
value NO_STATE_MAINTAINED MUST NOT be set in subsequent re-
authorization requests and answers.
- The base protocol does not include any authorization request
-Fajardo, et al. Expires July 24, 2011 [Page 101]
+Fajardo, et al. Standards Track [Page 98]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
+ The base protocol does not include any authorization request
messages, since these are largely application-specific and are
defined in a Diameter application document. However, the base
protocol does define a set of messages that are used to terminate
user sessions. These are used to allow servers that maintain state
information to free resources.
- When a service only makes use of the Accounting portion of the
+ When a service only makes use of the accounting portion of the
Diameter protocol, even in combination with an application, the
Session-Id is still used to identify user sessions. However, the
session termination messages are not used, since a session is
signaled as being terminated by issuing an accounting stop message.
Diameter may also be used for services that cannot be easily
- categorized as authentication, authorization or accounting (e.g.,
- certain 3GPP IMS interfaces). In such cases, the finite state
+ categorized as authentication, authorization, or accounting (e.g.,
+ certain Third Generation Partnership Project Internet Multimedia
+ System (3GPP IMS) interfaces). In such cases, the finite state
machine defined in subsequent sections may not be applicable.
- Therefore, the applications itself MAY need to define its own finite
+ Therefore, the application itself MAY need to define its own finite
state machine. However, such application-specific state machines
SHOULD follow the general state machine framework outlined in this
document such as the use of Session-Id AVPs and the use of STR/STA,
@@ -5681,12 +5518,12 @@ Internet-Draft Diameter Base Protocol January 2011
8.1. Authorization Session State Machine
- This section contains a set of finite state machines, representing
- the life cycle of Diameter sessions, and which MUST be observed by
- all Diameter implementations that make use of the authentication
- and/or authorization portion of a Diameter application. The term
- Service-Specific below refers to a message defined in a Diameter
- application (e.g., Mobile IPv4, NASREQ).
+ This section contains a set of finite state machines, which represent
+ the life cycle of Diameter sessions and which MUST be observed by all
+ Diameter implementations that make use of the authentication and/or
+ authorization portion of a Diameter application. The term "Service-
+ Specific" below refers to a message defined in a Diameter application
+ (e.g., Mobile IPv4, NASREQ).
There are four different authorization session state machines
supported in the Diameter base protocol. The first two describe a
@@ -5700,26 +5537,25 @@ Internet-Draft Diameter Base Protocol January 2011
When a session is moved to the Idle state, any resources that were
allocated for the particular session must be released. Any event not
- listed in the state machines MUST be considered as an error
- condition, and an answer, if applicable, MUST be returned to the
- originator of the message.
+ listed in the state machines MUST be considered an error condition,
+ and an answer, if applicable, MUST be returned to the originator of
+ the message.
- In the case that an application does not support re-auth, the state
-
-Fajardo, et al. Expires July 24, 2011 [Page 102]
+Fajardo, et al. Standards Track [Page 99]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
- transitions related to server-initiated re-auth when both client and
- server session maintains state (e.g., Send RAR, Pending, Receive RAA)
- MAY be ignored.
+ In the case that an application does not support re-auth, the state
+ transitions related to server-initiated re-auth, when both client and
+ server sessions maintain state (e.g., Send RAR, Pending, Receive
+ RAA), MAY be ignored.
- In the state table, the event 'Failure to send X' means that the
+ In the state table, the event "Failure to send X" means that the
Diameter agent is unable to send command X to the desired
- destination. This could be due to the peer being down, or due to the
+ destination. This could be due to the peer being down or due to the
peer sending back a transient failure or temporary protocol error
notification DIAMETER_TOO_BUSY or DIAMETER_LOOP_DETECTED in the
Result-Code AVP of the corresponding Answer command. The event 'X
@@ -5731,8 +5567,8 @@ Internet-Draft Diameter Base Protocol January 2011
CLIENT, STATEFUL
State Event Action New State
---------------------------------------------------------------
- Idle Client or Device Requests Send Pending
- access service
+ Idle Client or device requests Send Pending
+ access service-
specific
auth req
@@ -5748,39 +5584,38 @@ Internet-Draft Diameter Base Protocol January 2011
UNKNOWN_
SESSION_ID
- Pending Successful Service-specific Grant Open
+ Pending Successful service-specific Grant Open
authorization answer Access
received with default
Auth-Session-State value
- Pending Successful Service-specific Sent STR Discon
- authorization answer received
+ Pending Successful service-specific Sent STR Discon
+ authorization answer received,
but service not provided
Pending Error processing successful Sent STR Discon
- Service-specific authorization
+ service-specific authorization
answer
-
-Fajardo, et al. Expires July 24, 2011 [Page 103]
+Fajardo, et al. Standards Track [Page 100]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
- Pending Failed Service-specific Cleanup Idle
+ Pending Failed service-specific Clean up Idle
authorization answer received
Open User or client device Send Open
- requests access to service service
+ requests access to service service-
specific
auth req
- Open Successful Service-specific Provide Open
- authorization answer received Service
+ Open Successful service-specific Provide Open
+ authorization answer received service
- Open Failed Service-specific Discon. Idle
+ Open Failed service-specific Discon. Idle
authorization answer user/device
received.
@@ -5796,10 +5631,10 @@ Internet-Draft Diameter Base Protocol January 2011
Discon.
user/device
- Open Session-Timeout Expires on Send STR Discon
- Access Device
+ Open Session-Timeout expires on Send STR Discon
+ access device
- Open ASR Received, Send ASA Discon
+ Open ASR received, Send ASA Discon
client will comply with
with request to end the Result-Code =
session = SUCCESS,
@@ -5814,17 +5649,18 @@ Internet-Draft Diameter Base Protocol January 2011
Auth-Grace-Period expires on
access device
- Discon ASR Received Send ASA Discon
+ Discon ASR received Send ASA Discon
+
- Discon STA Received Discon. Idle
-Fajardo, et al. Expires July 24, 2011 [Page 104]
+Fajardo, et al. Standards Track [Page 101]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
+ Discon STA received Discon. Idle
user/device
The following state machine is observed by a server when it is
@@ -5835,31 +5671,34 @@ Internet-Draft Diameter Base Protocol January 2011
---------------------------------------------------------------
Idle Service-specific authorization Send Open
request received, and successful
- user is authorized serv.
+ user is authorized service-
specific
answer
Idle Service-specific authorization Send Idle
- request received, and failed serv.
- user is not authorized specific
+ request received, and failed
+ user is not authorized service-
+ specific
answer
Open Service-specific authorization Send Open
request received, and user successful
- is authorized serv. specific
+ is authorized service-
+ specific
answer
Open Service-specific authorization Send Idle
- request received, and user failed serv.
- is not authorized specific
+ request received, and user failed
+ is not authorized service-
+ specific
answer,
- Cleanup
+ Clean up
Open Home server wants to confirm Send RAR Pending
authentication and/or
authorization of the user
- Pending Received RAA with a failed Cleanup Idle
+ Pending Received RAA with a failed Clean up Idle
Result-Code
Pending Received RAA with Result-Code Update Open
@@ -5868,32 +5707,33 @@ Internet-Draft Diameter Base Protocol January 2011
Open Home server wants to Send ASR Discon
terminate the service
- Open Authorization-Lifetime (and Cleanup Idle
- Auth-Grace-Period) expires
- on home server.
- Open Session-Timeout expires on Cleanup Idle
-Fajardo, et al. Expires July 24, 2011 [Page 105]
+Fajardo, et al. Standards Track [Page 102]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
+ Open Authorization-Lifetime (and Clean up Idle
+ Auth-Grace-Period) expires
+ on home server
+
+ Open Session-Timeout expires on Clean up Idle
home server
Discon Failure to send ASR Wait, Discon
resend ASR
- Discon ASR successfully sent and Cleanup Idle
+ Discon ASR successfully sent and Clean up Idle
ASA Received with Result-Code
- Not ASA Received None No Change.
+ Not ASA Received None No Change
Discon
Any STR Received Send STA, Idle
- Cleanup.
+ Clean up
The following state machine is observed by a client when state is not
maintained on the server:
@@ -5901,48 +5741,47 @@ Internet-Draft Diameter Base Protocol January 2011
CLIENT, STATELESS
State Event Action New State
---------------------------------------------------------------
- Idle Client or Device Requests Send Pending
- access service
+ Idle Client or device requests Send Pending
+ access service-
specific
auth req
- Pending Successful Service-specific Grant Open
- authorization answer Access
+ Pending Successful service-specific Grant Open
+ authorization answer access
received with Auth-Session-
State set to
NO_STATE_MAINTAINED
- Pending Failed Service-specific Cleanup Idle
+ Pending Failed service-specific Clean up Idle
authorization answer
received
- Open Session-Timeout Expires on Discon. Idle
- Access Device user/device
+ Open Session-Timeout expires on Discon. Idle
+ access device user/device
Open Service to user is terminated Discon. Idle
user/device
- The following state machine is observed by a server when it is not
- maintaining state for the session:
-
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 106]
+Fajardo, et al. Standards Track [Page 103]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
+
+ The following state machine is observed by a server when it is not
+ maintaining state for the session:
SERVER, STATELESS
State Event Action New State
---------------------------------------------------------------
- Idle Service-specific authorization Send serv. Idle
- request received, and specific
- successfully processed answer
+ Idle Service-specific authorization Send Idle
+ request received, and service-
+ successfully processed specific
+ answer
8.2. Accounting Session State Machine
@@ -5960,8 +5799,8 @@ Internet-Draft Diameter Base Protocol January 2011
machine in this section described below.
The default server side state machine requires the reception of
- accounting records in any order and at any time, and does not place
- any standards requirement on the processing of these records.
+ accounting records in any order and at any time, and it does not
+ place any standards requirement on the processing of these records.
Implementations of Diameter may perform checking, ordering,
correlation, fraud detection, and other tasks based on these records.
AVPs may need to be inspected as a part of these tasks. The tasks
@@ -5970,7 +5809,7 @@ Internet-Draft Diameter Base Protocol January 2011
or even policy dependent, they are not standardized by the Diameter
specifications. Applications MAY define requirements on when to
accept accounting records based on the used value of Accounting-
- Realtime-Required AVP, credit limits checks, and so on.
+ Realtime-Required AVP, credit-limit checks, and so on.
However, the Diameter base protocol defines one optional server side
state machine that MAY be followed by applications that require
@@ -5978,31 +5817,31 @@ Internet-Draft Diameter Base Protocol January 2011
that such tracking is incompatible with the ability to sustain long
duration connectivity problems. Therefore, the use of this state
machine is recommended only in applications where the value of the
- Accounting-Realtime-Required AVP is DELIVER_AND_GRANT, and hence
+ Accounting-Realtime-Required AVP is DELIVER_AND_GRANT; hence,
accounting connectivity problems are required to cause the serviced
user to be disconnected. Otherwise, records produced by the client
- may be lost by the server which no longer accepts them after the
- connectivity is re-established. This state machine is the third
- state machine in this section. The state machine is supervised by a
- supervision session timer Ts, which the value should be reasonably
-Fajardo, et al. Expires July 24, 2011 [Page 107]
+Fajardo, et al. Standards Track [Page 104]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
- higher than the Acct_Interim_Interval value. Ts MAY be set to two
- times the value of the Acct_Interim_Interval so as to avoid the
- accounting session in the Diameter server to change to Idle state in
- case of short transient network failure.
+ may be lost by the server, which no longer accepts them after the
+ connectivity is re-established. This state machine is the third
+ state machine in this section. The state machine is supervised by a
+ supervision session timer Ts, whose value should be reasonably higher
+ than the Acct_Interim_Interval value. Ts MAY be set to two times the
+ value of the Acct_Interim_Interval so as to avoid the accounting
+ session in the Diameter server to change to Idle state in case of
+ short transient network failure.
Any event not listed in the state machines MUST be considered as an
error condition, and a corresponding answer, if applicable, MUST be
returned to the originator of the message.
- In the state table, the event 'Failure to send' means that the
+ In the state table, the event "Failure to send" means that the
Diameter client is unable to communicate with the desired
destination. This could be due to the peer being down, or due to the
peer sending back a transient failure or temporary protocol error
@@ -6010,17 +5849,17 @@ Internet-Draft Diameter Base Protocol January 2011
DIAMETER_LOOP_DETECTED in the Result-Code AVP of the Accounting
Answer command.
- The event 'Failed answer' means that the Diameter client received a
+ The event "Failed answer" means that the Diameter client received a
non-transient failure notification in the Accounting Answer command.
- Note that the action 'Disconnect user/dev' MUST have an effect also
- to the authorization session state table, e.g., cause the STR message
+ Note that the action "Disconnect user/dev" MUST also have an effect
+ on the authorization session state table, e.g., cause the STR message
to be sent, if the given application has both authentication/
authorization and accounting portions.
- The states PendingS, PendingI, PendingL, PendingE and PendingB stand
+ The states PendingS, PendingI, PendingL, PendingE, and PendingB stand
for pending states to wait for an answer to an accounting request
- related to a Start, Interim, Stop, Event or buffered record,
+ related to a Start, Interim, Stop, Event, or buffered record,
respectively.
CLIENT, ACCOUNTING
@@ -6037,36 +5876,36 @@ Internet-Draft Diameter Base Protocol January 2011
Idle Records in storage Send PendingB
record
- PendingS Successful accounting Open
- start answer received
-
- PendingS Failure to send and buffer Store Open
-Fajardo, et al. Expires July 24, 2011 [Page 108]
+Fajardo, et al. Standards Track [Page 105]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
- space available and realtime Start
+ PendingS Successful accounting Open
+ start answer received
+
+ PendingS Failure to send and buffer Store Open
+ space available and real time Start
not equal to DELIVER_AND_GRANT Record
PendingS Failure to send and no buffer Open
- space available and realtime
+ space available and real time
equal to GRANT_AND_LOSE
PendingS Failure to send and no Disconnect Idle
buffer space available and user/dev
- realtime not equal to
+ real time not equal to
GRANT_AND_LOSE
PendingS Failed accounting start answer Open
- received and realtime equal
+ received and real time equal
to GRANT_AND_LOSE
PendingS Failed accounting start answer Disconnect Idle
- received and realtime not user/dev
+ received and real time not user/dev
equal to GRANT_AND_LOSE
PendingS User service terminated Store PendingS
@@ -6077,6 +5916,7 @@ Internet-Draft Diameter Base Protocol January 2011
accounting
interim
record
+
Open User service terminated Send PendingL
accounting
stop req.
@@ -6087,34 +5927,35 @@ Internet-Draft Diameter Base Protocol January 2011
PendingI Failure to send and (buffer Store Open
space available or old interim
record can be overwritten) record
- and realtime not equal to
+ and real time not equal to
DELIVER_AND_GRANT
- PendingI Failure to send and no buffer Open
- space available and realtime
- equal to GRANT_AND_LOSE
- PendingI Failure to send and no Disconnect Idle
- buffer space available and user/dev
-Fajardo, et al. Expires July 24, 2011 [Page 109]
+Fajardo, et al. Standards Track [Page 106]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
+
+ PendingI Failure to send and no buffer Open
+ space available and real time
+ equal to GRANT_AND_LOSE
- realtime not equal to
+ PendingI Failure to send and no Disconnect Idle
+ buffer space available and user/dev
+ real time not equal to
GRANT_AND_LOSE
PendingI Failed accounting interim Open
- answer received and realtime
+ answer received and real time
equal to GRANT_AND_LOSE
PendingI Failed accounting interim Disconnect Idle
answer received and user/dev
- realtime not equal to
+ real time not equal to
GRANT_AND_LOSE
PendingI User service terminated Store PendingI
@@ -6148,6 +5989,13 @@ Internet-Draft Diameter Base Protocol January 2011
space available stop
record
+
+
+Fajardo, et al. Standards Track [Page 107]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
PendingL Failure to send and no buffer Idle
space available
@@ -6155,124 +6003,133 @@ Internet-Draft Diameter Base Protocol January 2011
received
-
-Fajardo, et al. Expires July 24, 2011 [Page 110]
-
-Internet-Draft Diameter Base Protocol January 2011
-
-
SERVER, STATELESS ACCOUNTING
State Event Action New State
---------------------------------------------------------------
Idle Accounting start request Send Idle
- received, and successfully accounting
+ received and successfully accounting
processed. start
answer
Idle Accounting event request Send Idle
- received, and successfully accounting
+ received and successfully accounting
processed. event
answer
- Idle Interim record received, Send Idle
+ Idle Interim record received Send Idle
and successfully processed. accounting
interim
answer
Idle Accounting stop request Send Idle
- received, and successfully accounting
+ received and successfully accounting
processed stop answer
- Idle Accounting request received, Send Idle
+ Idle Accounting request received; Send Idle
no space left to store accounting
- records answer,
+ records answer;
Result-Code =
OUT_OF_
SPACE
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 108]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
SERVER, STATEFUL ACCOUNTING
State Event Action New State
---------------------------------------------------------------
Idle Accounting start request Send Open
- received, and successfully accounting
+ received and successfully accounting
processed. start
- answer,
+ answer;
Start Ts
Idle Accounting event request Send Idle
- received, and successfully accounting
+ received and successfully accounting
processed. event
answer
-
- Idle Accounting request received, Send Idle
+ Idle Accounting request received; Send Idle
no space left to store accounting
- records answer,
-
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 111]
-
-Internet-Draft Diameter Base Protocol January 2011
-
-
+ records answer;
Result-Code =
OUT_OF_
SPACE
- Open Interim record received, Send Open
+ Open Interim record received Send Open
and successfully processed. accounting
interim
- answer,
+ answer;
Restart Ts
Open Accounting stop request Send Idle
- received, and successfully accounting
- processed stop answer,
+ received and successfully accounting
+ processed stop answer;
Stop Ts
- Open Accounting request received, Send Idle
+ Open Accounting request received; Send Idle
no space left to store accounting
- records answer,
+ records answer;
Result-Code =
OUT_OF_
- SPACE,
+ SPACE;
Stop Ts
Open Session supervision timer Ts Stop Ts Idle
expired
+
+
+
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 109]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
8.3. Server-Initiated Re-Auth
A Diameter server may initiate a re-authentication and/or re-
authorization service for a particular session by issuing a Re-Auth-
Request (RAR).
- For example, for pre-paid services, the Diameter server that
+ For example, for prepaid services, the Diameter server that
originally authorized a session may need some confirmation that the
user is still using the services.
- An access device that receives a RAR message with Session-Id equal to
- a currently active session MUST initiate a re-auth towards the user,
- if the service supports this particular feature. Each Diameter
- application MUST state whether server-initiated re-auth is supported,
- since some applications do not allow access devices to prompt the
- user for re-auth.
+ An access device that receives an RAR message with the Session-Id
+ equal to a currently active session MUST initiate a re-auth towards
+ the user, if the service supports this particular feature. Each
+ Diameter application MUST state whether server-initiated re-auth is
+ supported, since some applications do not allow access devices to
+ prompt the user for re-auth.
8.3.1. Re-Auth-Request
- The Re-Auth-Request (RAR), indicated by the Command-Code set to 258
+ The Re-Auth-Request (RAR), indicated by the Command Code set to 258
and the message flags' 'R' bit set, may be sent by any server to the
access device that is providing session service, to request that the
-
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 112]
-
-Internet-Draft Diameter Base Protocol January 2011
-
-
user be re-authenticated and/or re-authorized.
@@ -6294,15 +6151,22 @@ Internet-Draft Diameter Base Protocol January 2011
8.3.2. Re-Auth-Answer
- The Re-Auth-Answer (RAA), indicated by the Command-Code set to 258
+ The Re-Auth-Answer (RAA), indicated by the Command Code set to 258
and the message flags' 'R' bit clear, is sent in response to the RAR.
- The Result-Code AVP MUST be present, and indicates the disposition of
- the request.
+ The Result-Code AVP MUST be present, and it indicates the disposition
+ of the request.
+
+
+
+
+Fajardo, et al. Standards Track [Page 110]
+
+RFC 6733 Diameter Base Protocol October 2012
+
A successful RAA message MUST be followed by an application-specific
authentication and/or authorization message.
-
Message Format
<RAA> ::= < Diameter Header: 258, PXY >
@@ -6321,14 +6185,6 @@ Internet-Draft Diameter Base Protocol January 2011
* [ Proxy-Info ]
* [ AVP ]
-
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 113]
-
-Internet-Draft Diameter Base Protocol January 2011
-
-
8.4. Session Termination
It is necessary for a Diameter server that authorized a session, for
@@ -6356,6 +6212,14 @@ Internet-Draft Diameter Base Protocol January 2011
It is also possible that a session that was authorized is never
actually started due to action of a proxy. For example, a proxy may
+
+
+
+Fajardo, et al. Standards Track [Page 111]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
modify an authorization answer, converting the result from success to
failure, prior to forwarding the message to the access device. If
the answer did not contain an Auth-Session-State AVP with the value
@@ -6366,36 +6230,27 @@ Internet-Draft Diameter Base Protocol January 2011
A Diameter server that receives an STR message MUST clean up
resources (e.g., session state) associated with the Session-Id
- specified in the STR, and return a Session-Termination-Answer.
+ specified in the STR and return a Session-Termination-Answer.
A Diameter server also MUST clean up resources when the Session-
Timeout expires, or when the Authorization-Lifetime and the Auth-
- Grace-Period AVPs expires without receipt of a re-authorization
+ Grace-Period AVPs expire without receipt of a re-authorization
request, regardless of whether an STR for that session is received.
The access device is not expected to provide service beyond the
expiration of these timers; thus, expiration of either of these
timers implies that the access device may have unexpectedly shut
down.
-
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 114]
-
-Internet-Draft Diameter Base Protocol January 2011
-
-
8.4.1. Session-Termination-Request
- The Session-Termination-Request (STR), indicated by the Command-Code
+ The Session-Termination-Request (STR), indicated by the Command Code
set to 275 and the Command Flags' 'R' bit set, is sent by a Diameter
- client or by a Diameter proxy to inform the Diameter Server that an
+ client or by a Diameter proxy to inform the Diameter server that an
authenticated and/or authorized session is being terminated.
+ Message Format
- Message Format
-
- <STR> ::= < Diameter Header: 275, REQ, PXY >
+ <STR> ::= < Diameter Header: 275, REQ, PXY >
< Session-Id >
{ Origin-Host }
{ Origin-Realm }
@@ -6410,40 +6265,33 @@ Internet-Draft Diameter Base Protocol January 2011
* [ Route-Record ]
* [ AVP ]
-8.4.2. Session-Termination-Answer
-
- The Session-Termination-Answer (STA), indicated by the Command-Code
- set to 275 and the message flags' 'R' bit clear, is sent by the
- Diameter Server to acknowledge the notification that the session has
- been terminated. The Result-Code AVP MUST be present, and MAY
- contain an indication that an error occurred while servicing the STR.
-
- Upon sending or receipt of the STA, the Diameter Server MUST release
- all resources for the session indicated by the Session-Id AVP. Any
- intermediate server in the Proxy-Chain MAY also release any
- resources, if necessary.
-
-
-
-
-
+Fajardo, et al. Standards Track [Page 112]
+
+RFC 6733 Diameter Base Protocol October 2012
+8.4.2. Session-Termination-Answer
-Fajardo, et al. Expires July 24, 2011 [Page 115]
-
-Internet-Draft Diameter Base Protocol January 2011
+ The Session-Termination-Answer (STA), indicated by the Command Code
+ set to 275 and the message flags' 'R' bit clear, is sent by the
+ Diameter server to acknowledge the notification that the session has
+ been terminated. The Result-Code AVP MUST be present, and it MAY
+ contain an indication that an error occurred while servicing the STR.
+ Upon sending or receipt of the STA, the Diameter server MUST release
+ all resources for the session indicated by the Session-Id AVP. Any
+ intermediate server in the Proxy-Chain MAY also release any
+ resources, if necessary.
- Message Format
+ Message Format
- <STA> ::= < Diameter Header: 275, PXY >
+ <STA> ::= < Diameter Header: 275, PXY >
< Session-Id >
{ Result-Code }
{ Origin-Host }
@@ -6473,31 +6321,29 @@ Internet-Draft Diameter Base Protocol January 2011
An access device that receives an ASR with Session-ID equal to a
currently active session MAY stop the session. Whether the access
- device stops the session or not is implementation- and/or
- configuration-dependent. For example, an access device may honor
+ device stops the session or not is implementation and/or
+ configuration dependent. For example, an access device may honor
ASRs from certain agents only. In any case, the access device MUST
+
+
+
+Fajardo, et al. Standards Track [Page 113]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
respond with an Abort-Session-Answer, including a Result-Code AVP to
indicate what action it took.
8.5.1. Abort-Session-Request
- The Abort-Session-Request (ASR), indicated by the Command-Code set to
+ The Abort-Session-Request (ASR), indicated by the Command Code set to
274 and the message flags' 'R' bit set, may be sent by any Diameter
server or any Diameter proxy to the access device that is providing
session service, to request that the session identified by the
Session-Id be stopped.
-
-
-
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 116]
-
-Internet-Draft Diameter Base Protocol January 2011
-
-
- Message Format
+ Message Format
<ASR> ::= < Diameter Header: 274, REQ, PXY >
< Session-Id >
@@ -6514,20 +6360,35 @@ Internet-Draft Diameter Base Protocol January 2011
8.5.2. Abort-Session-Answer
- The Abort-Session-Answer (ASA), indicated by the Command-Code set to
+ The Abort-Session-Answer (ASA), indicated by the Command Code set to
274 and the message flags' 'R' bit clear, is sent in response to the
- ASR. The Result-Code AVP MUST be present, and indicates the
+ ASR. The Result-Code AVP MUST be present and indicates the
disposition of the request.
If the session identified by Session-Id in the ASR was successfully
- terminated, Result-Code is set to DIAMETER_SUCCESS. If the session
- is not currently active, Result-Code is set to
+ terminated, the Result-Code is set to DIAMETER_SUCCESS. If the
+ session is not currently active, the Result-Code is set to
DIAMETER_UNKNOWN_SESSION_ID. If the access device does not stop the
- session for any other reason, Result-Code is set to
+ session for any other reason, the Result-Code is set to
DIAMETER_UNABLE_TO_COMPLY.
- Message Format
+
+
+
+
+
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 114]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ Message Format
<ASA> ::= < Diameter Header: 274, PXY >
< Session-Id >
@@ -6545,14 +6406,6 @@ Internet-Draft Diameter Base Protocol January 2011
* [ Proxy-Info ]
* [ AVP ]
-
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 117]
-
-Internet-Draft Diameter Base Protocol January 2011
-
-
8.6. Inferring Session Termination from Origin-State-Id
The Origin-State-Id is used to allow detection of terminated sessions
@@ -6560,94 +6413,96 @@ Internet-Draft Diameter Base Protocol January 2011
shutdown of an access device.
A Diameter client or access device increments the value of the
- Origin-State-Id every time it is started or powered-up. The new
+ Origin-State-Id every time it is started or powered up. The new
Origin-State-Id is then sent in the CER/CEA message immediately upon
connection to the server. The Diameter server receiving the new
Origin-State-Id can determine whether the sending Diameter client had
- abruptly shutdown by comparing the old value of the Origin-State-Id
+ abruptly shut down by comparing the old value of the Origin-State-Id
it has kept for that specific client is less than the new value and
whether it has un-terminated sessions originating from that client.
An access device can also include the Origin-State-Id in request
- messages other than CER if there are relays or proxies in between the
- access device and the server. In this case, however, the server
+ messages other than the CER if there are relays or proxies in between
+ the access device and the server. In this case, however, the server
cannot discover that the access device has been restarted unless and
- until it receives a new request from it. Therefore this mechanism is
- more opportunistic across proxies and relays.
+ until it receives a new request from it. Therefore, this mechanism
+ is more opportunistic across proxies and relays.
The Diameter server may assume that all sessions that were active
prior to detection of a client restart have been terminated. The
Diameter server MAY clean up all session state associated with such
- lost sessions, and MAY also issues STRs for all such lost sessions
+ lost sessions, and it MAY also issue STRs for all such lost sessions
that were authorized on upstream servers, to allow session state to
be cleaned up globally.
+
+
+
+
+Fajardo, et al. Standards Track [Page 115]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
8.7. Auth-Request-Type AVP
The Auth-Request-Type AVP (AVP Code 274) is of type Enumerated and is
included in application-specific auth requests to inform the peers
- whether a user is to be authenticated only, authorized only or both.
+ whether a user is to be authenticated only, authorized only, or both.
Note any value other than both MAY cause RADIUS interoperability
issues. The following values are defined:
-
AUTHENTICATE_ONLY 1
- The request being sent is for authentication only, and MUST
- contain the relevant application specific authentication AVPs that
+ The request being sent is for authentication only, and it MUST
+ contain the relevant application-specific authentication AVPs that
are needed by the Diameter server to authenticate the user.
-
-
-
-
-
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 118]
-
-Internet-Draft Diameter Base Protocol January 2011
-
-
AUTHORIZE_ONLY 2
- The request being sent is for authorization only, and MUST contain
- the application-specific authorization AVPs that are necessary to
- identify the service being requested/offered.
-
+ The request being sent is for authorization only, and it MUST
+ contain the application-specific authorization AVPs that are
+ necessary to identify the service being requested/offered.
AUTHORIZE_AUTHENTICATE 3
The request contains a request for both authentication and
authorization. The request MUST include both the relevant
- application-specific authentication information, and authorization
+ application-specific authentication information and authorization
information necessary to identify the service being requested/
offered.
-
8.8. Session-Id AVP
The Session-Id AVP (AVP Code 263) is of type UTF8String and is used
to identify a specific session (see Section 8). All messages
- pertaining to a specific session MUST include only one Session-Id AVP
- and the same value MUST be used throughout the life of a session.
- When present, the Session-Id SHOULD appear immediately following the
- Diameter Header (see Section 3).
+ pertaining to a specific session MUST include only one Session-Id
+ AVP, and the same value MUST be used throughout the life of a
+ session. When present, the Session-Id SHOULD appear immediately
+ following the Diameter header (see Section 3).
The Session-Id MUST be globally and eternally unique, as it is meant
to uniquely identify a user session without reference to any other
- information, and may be needed to correlate historical authentication
- information with accounting information. The Session-Id includes a
- mandatory portion and an implementation-defined portion; a
- recommended format for the implementation-defined portion is outlined
- below.
+ information, and it may be needed to correlate historical
+ authentication information with accounting information. The
+ Session-Id includes a mandatory portion and an implementation-defined
+ portion; a recommended format for the implementation-defined portion
+ is outlined below.
The Session-Id MUST begin with the sender's identity encoded in the
- DiameterIdentity type (see Section 4.4). The remainder of the
- Session-Id is delimited by a ";" character, and MAY be any sequence
- that the client can guarantee to be eternally unique; however, the
- following format is recommended, (square brackets [] indicate an
- optional element):
+ DiameterIdentity type (see Section 4.3.1). The remainder of the
+ Session-Id is delimited by a ";" character, and it MAY be any
+
+
+
+Fajardo, et al. Standards Track [Page 116]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ sequence that the client can guarantee to be eternally unique;
+ however, the following format is recommended, (square brackets []
+ indicate an optional element):
<DiameterIdentity>;<high 32 bits>;<low 32 bits>[;<optional value>]
@@ -6657,22 +6512,14 @@ Internet-Draft Diameter Base Protocol January 2011
processors. At startup, the high 32 bits of the 64-bit value MAY be
initialized to the time in NTP format [RFC5905], and the low 32 bits
MAY be initialized to zero. This will for practical purposes
-
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 119]
-
-Internet-Draft Diameter Base Protocol January 2011
-
-
eliminate the possibility of overlapping Session-Ids after a reboot,
assuming the reboot process takes longer than a second.
Alternatively, an implementation MAY keep track of the increasing
value in non-volatile memory.
- <optional value> is implementation specific but may include a modem's
- device Id, a layer 2 address, timestamp, etc.
+ <optional value> is implementation specific, but it may include a
+ modem's device Id, a Layer 2 address, timestamp, etc.
Example, in which there is no optional value:
@@ -6683,45 +6530,44 @@ Internet-Draft Diameter Base Protocol January 2011
accesspoint7.example.com;1876543210;523;[email protected]
The Session-Id is created by the Diameter application initiating the
- session, which in most cases is done by the client. Note that a
- Session-Id MAY be used for both the authentication, authorization and
- accounting commands of a given application.
+ session, which, in most cases, is done by the client. Note that a
+ Session-Id MAY be used for both the authentication, authorization,
+ and accounting commands of a given application.
8.9. Authorization-Lifetime AVP
The Authorization-Lifetime AVP (AVP Code 291) is of type Unsigned32
and contains the maximum number of seconds of service to be provided
to the user before the user is to be re-authenticated and/or re-
- authorized. Care should be taken when the Authorization- Lifetime
- value is determined, since a low, non-zero, value could create
+ authorized. Care should be taken when the Authorization-Lifetime
+ value is determined, since a low, non-zero value could create
significant Diameter traffic, which could congest both the network
and the agents.
A value of zero (0) means that immediate re-auth is necessary by the
access device. The absence of this AVP, or a value of all ones
- (meaning all bits in the 32 bit field are set to one) means no re-
+ (meaning all bits in the 32-bit field are set to one) means no re-
auth is expected.
+
+
+Fajardo, et al. Standards Track [Page 117]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
If both this AVP and the Session-Timeout AVP are present in a
message, the value of the latter MUST NOT be smaller than the
Authorization-Lifetime AVP.
An Authorization-Lifetime AVP MAY be present in re-authorization
- messages, and contains the number of seconds the user is authorized
- to receive service from the time the re-auth answer message is
- received by the access device.
+ messages, and it contains the number of seconds the user is
+ authorized to receive service from the time the re-auth answer
+ message is received by the access device.
This AVP MAY be provided by the client as a hint of the maximum
lifetime that it is willing to accept. The server MUST return a
-
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 120]
-
-Internet-Draft Diameter Base Protocol January 2011
-
-
- value that is equal to, or smaller, than the one provided by the
+ value that is equal to, or smaller than, the one provided by the
client.
8.10. Auth-Grace-Period AVP
@@ -6739,7 +6585,6 @@ Internet-Draft Diameter Base Protocol January 2011
the value in the server's answer message is binding. The following
values are supported:
-
STATE_MAINTAINED 0
This value is used to specify that session state is being
@@ -6747,46 +6592,42 @@ Internet-Draft Diameter Base Protocol January 2011
message when service to the user is terminated. This is the
default value.
-
NO_STATE_MAINTAINED 1
This value is used to specify that no session termination messages
will be sent by the access device upon expiration of the
Authorization-Lifetime.
-
8.12. Re-Auth-Request-Type AVP
The Re-Auth-Request-Type AVP (AVP Code 285) is of type Enumerated and
is included in application-specific auth answers to inform the client
of the action expected upon expiration of the Authorization-Lifetime.
- If the answer message contains an Authorization-Lifetime AVP with a
- positive value, the Re-Auth-Request-Type AVP MUST be present in an
- answer message. The following values are defined:
- AUTHORIZE_ONLY 0
- An authorization only re-auth is expected upon expiration of the
- Authorization-Lifetime. This is the default value if the AVP is
+Fajardo, et al. Standards Track [Page 118]
+
+RFC 6733 Diameter Base Protocol October 2012
-Fajardo, et al. Expires July 24, 2011 [Page 121]
-
-Internet-Draft Diameter Base Protocol January 2011
+ If the answer message contains an Authorization-Lifetime AVP with a
+ positive value, the Re-Auth-Request-Type AVP MUST be present in an
+ answer message. The following values are defined:
+ AUTHORIZE_ONLY 0
+ An authorization only re-auth is expected upon expiration of the
+ Authorization-Lifetime. This is the default value if the AVP is
not present in answer messages that include the Authorization-
Lifetime.
-
AUTHORIZE_AUTHENTICATE 1
An authentication and authorization re-auth is expected upon
expiration of the Authorization-Lifetime.
-
8.13. Session-Timeout AVP
The Session-Timeout AVP (AVP Code 27) [RFC2865] is of type Unsigned32
@@ -6799,10 +6640,10 @@ Internet-Draft Diameter Base Protocol January 2011
A session that terminates on an access device due to the expiration
of the Session-Timeout MUST cause an STR to be issued, unless both
the access device and the home server had previously agreed that no
- session termination messages would be sent (see Section 8.11).
+ session termination messages would be sent (see Section 8).
A Session-Timeout AVP MAY be present in a re-authorization answer
- message, and contains the remaining number of seconds from the
+ message, and it contains the remaining number of seconds from the
beginning of the re-auth.
A value of zero, or the absence of this AVP, means that this session
@@ -6810,7 +6651,7 @@ Internet-Draft Diameter Base Protocol January 2011
This AVP MAY be provided by the client as a hint of the maximum
timeout that it is willing to accept. However, the server MAY return
- a value that is equal to, or smaller, than the one provided by the
+ a value that is equal to, or smaller than, the one provided by the
client.
8.14. User-Name AVP
@@ -6819,83 +6660,29 @@ Internet-Draft Diameter Base Protocol January 2011
contains the User-Name, in a format consistent with the NAI
specification [RFC4282].
-8.15. Termination-Cause AVP
-
- The Termination-Cause AVP (AVP Code 295) is of type Enumerated, and
- is used to indicate the reason why a session was terminated on the
- access device. The following values are defined:
-
-Fajardo, et al. Expires July 24, 2011 [Page 122]
+Fajardo, et al. Standards Track [Page 119]
-Internet-Draft Diameter Base Protocol January 2011
-
-
- DIAMETER_LOGOUT 1
-
- The user initiated a disconnect
-
-
- DIAMETER_SERVICE_NOT_PROVIDED 2
-
- This value is used when the user disconnected prior to the receipt
- of the authorization answer message.
-
-
- DIAMETER_BAD_ANSWER 3
-
- This value indicates that the authorization answer received by the
- access device was not processed successfully.
-
-
- DIAMETER_ADMINISTRATIVE 4
-
- The user was not granted access, or was disconnected, due to
- administrative reasons, such as the receipt of a Abort-Session-
- Request message.
-
-
- DIAMETER_LINK_BROKEN 5
-
- The communication to the user was abruptly disconnected.
-
-
- DIAMETER_AUTH_EXPIRED 6
-
- The user's access was terminated since its authorized session time
- has expired.
-
-
- DIAMETER_USER_MOVED 7
-
- The user is receiving services from another access device.
-
-
- DIAMETER_SESSION_TIMEOUT 8
+RFC 6733 Diameter Base Protocol October 2012
- The user's session has timed out, and service has been terminated.
+8.15. Termination-Cause AVP
-
-
-
-
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 123]
-
-Internet-Draft Diameter Base Protocol January 2011
-
+ The Termination-Cause AVP (AVP Code 295) is of type Enumerated, and
+ is used to indicate the reason why a session was terminated on the
+ access device. The currently assigned values for this AVP can be
+ found in the IANA registry for Termination-Cause AVP Values
+ [IANATCV].
8.16. Origin-State-Id AVP
The Origin-State-Id AVP (AVP Code 278), of type Unsigned32, is a
monotonically increasing value that is advanced whenever a Diameter
- entity restarts with loss of previous state, for example upon reboot.
- Origin-State-Id MAY be included in any Diameter message, including
- CER.
+ entity restarts with loss of previous state, for example, upon
+ reboot. Origin-State-Id MAY be included in any Diameter message,
+ including CER.
A Diameter entity issuing this AVP MUST create a higher value for
this AVP each time its state is reset. A Diameter entity MAY set
@@ -6911,20 +6698,34 @@ Internet-Draft Diameter Base Protocol January 2011
message, it allows other Diameter entities to infer that sessions
associated with a lower Origin-State-Id are no longer active. If an
access device does not intend for such inferences to be made, it MUST
- either not include Origin-State-Id in any message, or set its value
- to 0.
+ either not include Origin-State-Id in any message or set its value to
+ 0.
8.17. Session-Binding AVP
- The Session-Binding AVP (AVP Code 270) is of type Unsigned32, and MAY
- be present in application-specific authorization answer messages. If
- present, this AVP MAY inform the Diameter client that all future
+ The Session-Binding AVP (AVP Code 270) is of type Unsigned32, and it
+ MAY be present in application-specific authorization answer messages.
+ If present, this AVP MAY inform the Diameter client that all future
application-specific re-auth and Session-Termination-Request messages
for this session MUST be sent to the same authorization server.
- This field is a bit mask, and the following bits have been defined:
+
+
+
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 120]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ This field is a bit mask, and the following bits have been defined:
+
RE_AUTH 1
When set, future re-auth messages for this session MUST NOT
@@ -6932,23 +6733,13 @@ Internet-Draft Diameter Base Protocol January 2011
value, the Destination-Host AVP MUST be present in all re-auth
messages for this session.
-
STR 2
When set, the STR message for this session MUST NOT include the
Destination-Host AVP. When cleared, the default value, the
-
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 124]
-
-Internet-Draft Diameter Base Protocol January 2011
-
-
Destination-Host AVP MUST be present in the STR message for this
session.
-
ACCOUNTING 4
When set, all accounting messages for this session MUST NOT
@@ -6956,10 +6747,9 @@ Internet-Draft Diameter Base Protocol January 2011
value, the Destination-Host AVP, if known, MUST be present in all
accounting messages for this session.
-
8.18. Session-Server-Failover AVP
- The Session-Server-Failover AVP (AVP Code 271) is of type Enumerated,
+ The Session-Server-Failover AVP (AVP Code 271) is of type Enumerated
and MAY be present in application-specific authorization answer
messages that either do not include the Session-Binding AVP or
include the Session-Binding AVP with any of the bits set to a zero
@@ -6970,11 +6760,24 @@ Internet-Draft Diameter Base Protocol January 2011
The following values are supported:
-
REFUSE_SERVICE 0
If either the re-auth or the STR message delivery fails, terminate
- service with the user, and do not attempt any subsequent attempts.
+ service with the user and do not attempt any subsequent attempts.
+
+
+
+
+
+
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 121]
+
+RFC 6733 Diameter Base Protocol October 2012
TRY_AGAIN 1
@@ -6982,34 +6785,23 @@ Internet-Draft Diameter Base Protocol January 2011
If either the re-auth or the STR message delivery fails, resend
the failed message without the Destination-Host AVP present.
-
ALLOW_SERVICE 2
If re-auth message delivery fails, assume that re-authorization
succeeded. If STR message delivery fails, terminate the session.
-
TRY_AGAIN_ALLOW_SERVICE 3
If either the re-auth or the STR message delivery fails, resend
the failed message without the Destination-Host AVP present. If
-
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 125]
-
-Internet-Draft Diameter Base Protocol January 2011
-
-
the second delivery fails for re-auth, assume re-authorization
succeeded. If the second delivery fails for STR, terminate the
session.
-
8.19. Multi-Round-Time-Out AVP
- The Multi-Round-Time-Out AVP (AVP Code 272) is of type Unsigned32,
- and SHOULD be present in application-specific authorization answer
+ The Multi-Round-Time-Out AVP (AVP Code 272) is of type Unsigned32 and
+ SHOULD be present in application-specific authorization answer
messages whose Result-Code AVP is set to DIAMETER_MULTI_ROUND_AUTH.
This AVP contains the maximum number of seconds that the access
device MUST provide the user in responding to an authentication
@@ -7031,37 +6823,24 @@ Internet-Draft Diameter Base Protocol January 2011
8.21. Event-Timestamp AVP
- The Event-Timestamp (AVP Code 55) is of type Time, and MAY be
- included in an Accounting-Request and Accounting-Answer messages to
- record the time that the reported event occurred, in seconds since
- January 1, 1900 00:00 UTC.
-
-
+ The Event-Timestamp (AVP Code 55) is of type Time and MAY be included
+ in an Accounting-Request and Accounting-Answer messages to record the
+ time that the reported event occurred, in seconds since January 1,
+ 1900 00:00 UTC.
-
-
-
-
-
-
-
-
-
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 126]
+Fajardo, et al. Standards Track [Page 122]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
9. Accounting
This accounting protocol is based on a server directed model with
capabilities for real-time delivery of accounting information.
- Several fault resilience methods [RFC2975] have been built in to the
+ Several fault resilience methods [RFC2975] have been built into the
protocol in order minimize loss of accounting data in various fault
situations and under different assumptions about the capabilities of
the used devices.
@@ -7075,15 +6854,16 @@ Internet-Draft Diameter Base Protocol January 2011
timeliness requirements.
As discussed in [RFC2975], real-time transfer of accounting records
- is a requirement, such as the need to perform credit limit checks and
+ is a requirement, such as the need to perform credit-limit checks and
fraud detection. Note that batch accounting is not a requirement,
and is therefore not supported by Diameter. Should batched
accounting be required in the future, a new Diameter application will
need to be created, or it could be handled using another protocol.
- Note, however, that even if at the Diameter layer accounting requests
- are processed one by one, transport protocols used under Diameter
- typically batch several requests in the same packet under heavy
- traffic conditions. This may be sufficient for many applications.
+ Note, however, that even if at the Diameter layer, accounting
+ requests are processed one by one; transport protocols used under
+ Diameter typically batch several requests in the same packet under
+ heavy traffic conditions. This may be sufficient for many
+ applications.
The authorization server (chain) directs the selection of proper
transfer strategy, based on its knowledge of the user and
@@ -7097,7 +6877,7 @@ Internet-Draft Diameter Base Protocol January 2011
records from the Diameter client is delayed or unsuccessful.
The Diameter accounting server MAY override the interim interval or
- the realtime requirements by including the Acct-Interim-Interval or
+ the real-time requirements by including the Acct-Interim-Interval or
Accounting-Realtime-Required AVP in the Accounting-Answer message.
When one of these AVPs is present, the latest value received SHOULD
be used in further accounting activities for the same session.
@@ -7107,16 +6887,15 @@ Internet-Draft Diameter Base Protocol January 2011
-
-Fajardo, et al. Expires July 24, 2011 [Page 127]
+Fajardo, et al. Standards Track [Page 123]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
9.2. Protocol Messages
A Diameter node that receives a successful authentication and/or
- authorization messages from the Diameter server SHOULD collect
+ authorization message from the Diameter server SHOULD collect
accounting information for the session. The Accounting-Request
message is used to transmit the accounting information to the
Diameter server, which MUST reply with the Accounting-Answer message
@@ -7129,12 +6908,12 @@ Internet-Draft Diameter Base Protocol January 2011
9.3. Accounting Application Extension and Requirements
- Each Diameter application (e.g., NASREQ, MobileIP), SHOULD define
- their Service-Specific AVPs that MUST be present in the Accounting-
- Request message in a section entitled "Accounting AVPs". The
- application MUST assume that the AVPs described in this document will
- be present in all Accounting messages, so only their respective
- service-specific AVPs need to be defined in that section.
+ Each Diameter application (e.g., NASREQ, Mobile IP) SHOULD define its
+ service-specific AVPs that MUST be present in the Accounting-Request
+ message in a section titled "Accounting AVPs". The application MUST
+ assume that the AVPs described in this document will be present in
+ all Accounting messages, so only their respective service-specific
+ AVPs need to be defined in that section.
Applications have the option of using one or both of the following
accounting application extension models:
@@ -7150,40 +6929,37 @@ Internet-Draft Diameter Base Protocol January 2011
advertise the Diameter base accounting Application Id during
capabilities exchange.
-
Coupled Accounting Service
- The accounting messages will carry the Application Id of the
+ The accounting message will carry the Application Id of the
application that is using it. The application itself will process
the received accounting records or forward them to an accounting
server. There is no accounting application advertisement required
- during capabilities exchange and the accounting messages will be
- routed the same as any of the other application messages.
-
+ during capabilities exchange, and the accounting messages will be
+ routed the same way as any of the other application messages.
+ In cases where an application does not define its own accounting
+ service, it is preferred that the split accounting model be used.
-Fajardo, et al. Expires July 24, 2011 [Page 128]
+Fajardo, et al. Standards Track [Page 124]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
- In cases where an application does not define its own accounting
- service, it is preferred that the split accounting model be used.
-
9.4. Fault Resilience
- Diameter Base protocol mechanisms are used to overcome small message
- loss and network faults of temporary nature.
+ Diameter base protocol mechanisms are used to overcome small message
+ loss and network faults of a temporary nature.
Diameter peers acting as clients MUST implement the use of failover
to guard against server failures and certain network failures.
Diameter peers acting as agents or related off-line processing
systems MUST detect duplicate accounting records caused by the
- sending of same record to several servers and duplication of messages
- in transit. This detection MUST be based on the inspection of the
- Session-Id and Accounting-Record-Number AVP pairs. Appendix C
+ sending of the same record to several servers and duplication of
+ messages in transit. This detection MUST be based on the inspection
+ of the Session-Id and Accounting-Record-Number AVP pairs. Appendix C
discusses duplicate detection needs and implementation issues.
Diameter clients MAY have non-volatile memory for the safe storage of
@@ -7191,23 +6967,23 @@ Internet-Draft Diameter Base Protocol January 2011
partitions, and server failures. If such memory is available, the
client SHOULD store new accounting records there as soon as the
records are created and until a positive acknowledgement of their
- reception from the Diameter Server has been received. Upon a reboot,
- the client MUST starting sending the records in the non-volatile
- memory to the accounting server with appropriate modifications in
+ reception from the Diameter server has been received. Upon a reboot,
+ the client MUST start sending the records in the non-volatile memory
+ to the accounting server with the appropriate modifications in
termination cause, session length, and other relevant information in
the records.
A further application of this protocol may include AVPs to control
- how many accounting records may at most be stored in the Diameter
- client without committing them to the non-volatile memory or
+ the maximum number of accounting records that may be stored in the
+ Diameter client without committing them to the non-volatile memory or
transferring them to the Diameter server.
The client SHOULD NOT remove the accounting data from any of its
memory areas before the correct Accounting-Answer has been received.
- The client MAY remove oldest, undelivered or yet unacknowledged
- accounting data if it runs out of resources such as memory. It is an
- implementation dependent matter for the client to accept new sessions
- under this condition.
+ The client MAY remove the oldest, undelivered, or as yet
+ unacknowledged accounting data if it runs out of resources such as
+ memory. It is an implementation-dependent matter for the client to
+ accept new sessions under this condition.
9.5. Accounting Records
@@ -7217,15 +6993,17 @@ Internet-Draft Diameter Base Protocol January 2011
Different types of accounting records are sent depending on the
actual type of accounted service and the authorization server's
+ directions for interim accounting. If the accounted service is a
+
+
-Fajardo, et al. Expires July 24, 2011 [Page 129]
+Fajardo, et al. Standards Track [Page 125]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
- directions for interim accounting. If the accounted service is a
one-time event, meaning that the start and stop of the event are
simultaneous, then the Accounting-Record-Type AVP MUST be present and
set to the value EVENT_RECORD.
@@ -7251,11 +7029,11 @@ Internet-Draft Diameter Base Protocol January 2011
session.
A particular value of Accounting-Sub-Session-Id MUST appear only in
- one sequence of accounting records from a DIAMETER client, except for
+ one sequence of accounting records from a Diameter client, except for
the purposes of retransmission. The one sequence that is sent MUST
be either one record with Accounting-Record-Type AVP set to the value
- EVENT_RECORD, or several records starting with one having the value
- START_RECORD, followed by zero or more INTERIM_RECORD and a single
+ EVENT_RECORD or several records starting with one having the value
+ START_RECORD, followed by zero or more INTERIM_RECORDs and a single
STOP_RECORD. A particular Diameter application specification MUST
define the type of sequences that MUST be used.
@@ -7265,20 +7043,21 @@ Internet-Draft Diameter Base Protocol January 2011
accounting records with a specific application session by using the
Session-Id of the particular application session in the accounting
messages. Accounting messages MAY also use a different Session-Id
- from that of the application sessions in which case other session
+ from that of the application sessions, in which case, other session-
related information is needed to perform correlation.
In cases where an application requires multiple accounting sub-
- session, an Accounting-Sub-Session-Id AVP is used to differentiate
+ sessions, an Accounting-Sub-Session-Id AVP is used to differentiate
each sub-session. The Session-Id would remain constant for all sub-
- sessions and is be used to correlate all the sub-sessions to a
+ sessions and is used to correlate all the sub-sessions to a
particular application session. Note that receiving a STOP_RECORD
-Fajardo, et al. Expires July 24, 2011 [Page 130]
+
+Fajardo, et al. Standards Track [Page 126]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
with no Accounting-Sub-Session-Id AVP when sub-sessions were
@@ -7290,36 +7069,36 @@ Internet-Draft Diameter Base Protocol January 2011
record may span multiple different Diameter applications and sessions
used by the same user at a given time. In such cases, the Acct-
Multi-Session-Id AVP is used. The Acct-Multi-Session-Id AVP SHOULD
- be signaled by the server to the access device (typically during
+ be signaled by the server to the access device (typically, during
authorization) when it determines that a request belongs to an
existing session. The access device MUST then include the Acct-
Multi-Session-Id AVP in all subsequent accounting messages.
The Acct-Multi-Session-Id AVP MAY include the value of the original
- Session-Id. It's contents are implementation specific, but MUST be
- globally unique across other Acct-Multi-Session-Id, and MUST NOT
+ Session-Id. Its contents are implementation specific, but the MUST
+ be globally unique across other Acct-Multi-Session-Ids and MUST NOT
change during the life of a session.
A Diameter application document MUST define the exact concept of a
- session that is being accounted, and MAY define the concept of a
+ session that is being accounted, and it MAY define the concept of a
multi-session. For instance, the NASREQ DIAMETER application treats
- a single PPP connection to a Network Access Server as one session,
- and a set of Multilink PPP sessions as one multi-session.
+ a single PPP connection to a Network Access Server as one session and
+ a set of Multilink PPP sessions as one multi-session.
-9.7. Accounting Command-Codes
+9.7. Accounting Command Codes
- This section defines Command-Code values that MUST be supported by
- all Diameter implementations that provide Accounting services.
+ This section defines Command Code values that MUST be supported by
+ all Diameter implementations that provide accounting services.
9.7.1. Accounting-Request
- The Accounting-Request (ACR) command, indicated by the Command-Code
+ The Accounting-Request (ACR) command, indicated by the Command Code
field set to 271 and the Command Flags' 'R' bit set, is sent by a
Diameter node, acting as a client, in order to exchange accounting
information with a peer.
- The AVP listed below SHOULD include service-specific accounting AVPs,
- as described in Section 9.3.
+ In addition to the AVPs listed below, Accounting-Request messages
+ SHOULD include service-specific accounting AVPs.
@@ -7332,9 +7111,9 @@ Internet-Draft Diameter Base Protocol January 2011
-Fajardo, et al. Expires July 24, 2011 [Page 131]
+Fajardo, et al. Standards Track [Page 127]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
Message Format
@@ -7363,16 +7142,16 @@ Internet-Draft Diameter Base Protocol January 2011
9.7.2. Accounting-Answer
- The Accounting-Answer (ACA) command, indicated by the Command-Code
+ The Accounting-Answer (ACA) command, indicated by the Command Code
field set to 271 and the Command Flags' 'R' bit cleared, is used to
acknowledge an Accounting-Request command. The Accounting-Answer
command contains the same Session-Id as the corresponding request.
- Only the target Diameter Server, known as the home Diameter Server,
+ Only the target Diameter server, known as the home Diameter server,
SHOULD respond with the Accounting-Answer command.
- The AVP listed below SHOULD include service-specific accounting AVPs,
- as described in Section 9.3.
+ In addition to the AVPs listed below, Accounting-Answer messages
+ SHOULD include service-specific accounting AVPs.
@@ -7388,9 +7167,9 @@ Internet-Draft Diameter Base Protocol January 2011
-Fajardo, et al. Expires July 24, 2011 [Page 132]
+Fajardo, et al. Standards Track [Page 128]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
Message Format
@@ -7429,13 +7208,12 @@ Internet-Draft Diameter Base Protocol January 2011
and contains the type of accounting record being sent. The following
values are currently defined for the Accounting-Record-Type AVP:
-
EVENT_RECORD 1
An Accounting Event Record is used to indicate that a one-time
event has occurred (meaning that the start and end of the event
are simultaneous). This record contains all information relevant
- to the service, and is the only record of the service.
+ to the service, and it is the only record of the service.
@@ -7444,20 +7222,20 @@ Internet-Draft Diameter Base Protocol January 2011
-Fajardo, et al. Expires July 24, 2011 [Page 133]
+
+Fajardo, et al. Standards Track [Page 129]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
START_RECORD 2
- An Accounting Start, Interim, and Stop Records are used to
- indicate that a service of a measurable length has been given. An
- Accounting Start Record is used to initiate an accounting session,
+ Accounting Start, Interim, and Stop Records are used to indicate
+ that a service of a measurable length has been given. An
+ Accounting Start Record is used to initiate an accounting session
and contains accounting information that is relevant to the
initiation of the session.
-
INTERIM_RECORD 3
An Interim Accounting Record contains cumulative accounting
@@ -7468,14 +7246,12 @@ Internet-Draft Diameter Base Protocol January 2011
applications. The selection of whether to use INTERIM_RECORD
records is done by the Acct-Interim-Interval AVP.
-
STOP_RECORD 4
An Accounting Stop Record is sent to terminate an accounting
session and contains cumulative accounting information relevant to
the existing session.
-
9.8.2. Acct-Interim-Interval AVP
The Acct-Interim-Interval AVP (AVP Code 85) is of type Unsigned32 and
@@ -7483,31 +7259,31 @@ Internet-Draft Diameter Base Protocol January 2011
client. The client uses information in this AVP to decide how and
when to produce accounting records. With different values in this
AVP, service sessions can result in one, two, or two+N accounting
- records, based on the needs of the home-organization. The following
+ records, based on the needs of the home organization. The following
accounting record production behavior is directed by the inclusion of
this AVP:
-
1. The omission of the Acct-Interim-Interval AVP or its inclusion
with Value field set to 0 means that EVENT_RECORD, START_RECORD,
and STOP_RECORD are produced, as appropriate for the service.
-
2. The inclusion of the AVP with Value field set to a non-zero value
means that INTERIM_RECORD records MUST be produced between the
START_RECORD and STOP_RECORD records. The Value field of this
AVP is the nominal interval between these records in seconds.
+ The Diameter node that originates the accounting information,
+ known as the client, MUST produce the first INTERIM_RECORD record
+ roughly at the time when this nominal interval has elapsed from
+
+
-Fajardo, et al. Expires July 24, 2011 [Page 134]
+Fajardo, et al. Standards Track [Page 130]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
- The Diameter node that originates the accounting information,
- known as the client, MUST produce the first INTERIM_RECORD record
- roughly at the time when this nominal interval has elapsed from
the START_RECORD, the next one again as the interval has elapsed
once more, and so on until the session ends and a STOP_RECORD
record is produced.
@@ -7522,10 +7298,10 @@ Internet-Draft Diameter Base Protocol January 2011
The Accounting-Record-Number AVP (AVP Code 485) is of type Unsigned32
and identifies this record within one session. As Session-Id AVPs
are globally unique, the combination of Session-Id and Accounting-
- Record-Number AVPs is also globally unique, and can be used in
+ Record-Number AVPs is also globally unique and can be used in
matching accounting records with confirmations. An easy way to
produce unique numbers is to set the value to 0 for records of type
- EVENT_RECORD and START_RECORD, and set the value to 1 for the first
+ EVENT_RECORD and START_RECORD and set the value to 1 for the first
INTERIM_RECORD, 2 for the second, and so on until the value for
STOP_RECORD is one more than for the last INTERIM_RECORD.
@@ -7539,10 +7315,10 @@ Internet-Draft Diameter Base Protocol January 2011
The Acct-Multi-Session-Id AVP (AVP Code 50) is of type UTF8String,
following the format specified in Section 8.8. The Acct-Multi-
- Session-Id AVP is used to link together multiple related accounting
- sessions, where each session would have a unique Session-Id, but the
- same Acct-Multi-Session-Id AVP. This AVP MAY be returned by the
- Diameter server in an authorization answer, and MUST be used in all
+ Session-Id AVP is used to link multiple related accounting sessions,
+ where each session would have a unique Session-Id but the same Acct-
+ Multi-Session-Id AVP. This AVP MAY be returned by the Diameter
+ server in an authorization answer, and it MUST be used in all
accounting messages for the given session.
9.8.6. Accounting-Sub-Session-Id AVP
@@ -7553,18 +7329,17 @@ Internet-Draft Diameter Base Protocol January 2011
session, and the value of this AVP MUST be monotonically increased by
one for all new sub-sessions. The absence of this AVP implies no
sub-sessions are in use, with the exception of an Accounting-Request
+ whose Accounting-Record-Type is set to STOP_RECORD. A STOP_RECORD
+ message with no Accounting-Sub-Session-Id AVP present will signal the
+ termination of all sub-sessions for a given Session-Id.
-Fajardo, et al. Expires July 24, 2011 [Page 135]
+Fajardo, et al. Standards Track [Page 131]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
- whose Accounting-Record-Type is set to STOP_RECORD. A STOP_RECORD
- message with no Accounting-Sub-Session-Id AVP present will signal the
- termination of all sub-sessions for a given Session-Id.
-
9.8.7. Accounting-Realtime-Required AVP
The Accounting-Realtime-Required AVP (AVP Code 483) is of type
@@ -7574,7 +7349,6 @@ Internet-Draft Diameter Base Protocol January 2011
if the sending of accounting records to the accounting server has
been temporarily prevented due to, for instance, a network problem.
-
DELIVER_AND_GRANT 1
The AVP with Value field set to DELIVER_AND_GRANT means that the
@@ -7584,7 +7358,6 @@ Internet-Draft Diameter Base Protocol January 2011
the accounting record stream to a backup server is not a reason to
discontinue the service to the user.
-
GRANT_AND_STORE 2
The AVP with Value field set to GRANT_AND_STORE means that service
@@ -7594,60 +7367,51 @@ Internet-Draft Diameter Base Protocol January 2011
This is the default behavior if the AVP isn't included in the
reply from the authorization server.
-
GRANT_AND_LOSE 3
The AVP with Value field set to GRANT_AND_LOSE means that service
SHOULD be granted even if the records cannot be delivered or
stored.
+10. AVP Occurrence Tables
+ The following tables present the AVPs defined in this document and
+ specify in which Diameter messages they MAY or MAY NOT be present.
+ AVPs that occur only inside a Grouped AVP are not shown in these
+ tables.
+ The tables use the following symbols:
+ 0 The AVP MUST NOT be present in the message.
+ 0+ Zero or more instances of the AVP MAY be present in the
+ message.
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 136]
+Fajardo, et al. Standards Track [Page 132]
-Internet-Draft Diameter Base Protocol January 2011
-
-
-10. AVP Occurrence Table
-
- The following tables presents the AVPs defined in this document, and
- specifies in which Diameter messages they MAY be present or not.
- AVPs that occur only inside a Grouped AVP are not shown in this
- table.
+RFC 6733 Diameter Base Protocol October 2012
- The table uses the following symbols:
+ 0-1 Zero or one instance of the AVP MAY be present in the message.
+ It is considered an error if there are more than one instance
+ of the AVP.
- 0 The AVP MUST NOT be present in the message.
+ 1 One instance of the AVP MUST be present in the message.
- 0+ Zero or more instances of the AVP MAY be present in the
- message.
-
- 0-1 Zero or one instance of the AVP MAY be present in the message.
- It is considered an error if there are more than one instance of
- the AVP.
-
- 1 One instance of the AVP MUST be present in the message.
-
- 1+ At least one instance of the AVP MUST be present in the
- message.
+ 1+ At least one instance of the AVP MUST be present in the
+ message.
10.1. Base Protocol Command AVP Table
- The table in this section is limited to the non-accounting Command
+ The table in this section is limited to the non-Accounting Command
Codes defined in this specification.
+-----------------------------------------------+
- | Command-Code |
+ | Command Code |
+---+---+---+---+---+---+---+---+---+---+---+---+
Attribute Name |CER|CEA|DPR|DPA|DWR|DWA|RAR|RAA|ASR|ASA|STR|STA|
--------------------+---+---+---+---+---+---+---+---+---+---+---+---+
@@ -7665,22 +7429,29 @@ Internet-Draft Diameter Base Protocol January 2011
Class |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |0+ |0+ |
Destination-Host |0 |0 |0 |0 |0 |0 |1 |0 |1 |0 |0-1|0 |
Destination-Realm |0 |0 |0 |0 |0 |0 |1 |0 |1 |0 |1 |0 |
-
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 137]
-
-Internet-Draft Diameter Base Protocol January 2011
-
-
Disconnect-Cause |0 |0 |1 |0 |0 |0 |0 |0 |0 |0 |0 |0 |
Error-Message |0 |0-1|0 |0-1|0 |0-1|0 |0-1|0 |0-1|0 |0-1|
Error-Reporting-Host|0 |0 |0 |0 |0 |0 |0 |0-1|0 |0-1|0 |0-1|
- Failed-AVP |0 |0+ |0 |0+ |0 |0+ |0 |0+ |0 |0+ |0 |0+ |
+ Failed-AVP |0 |0-1|0 |0-1|0 |0-1|0 |0-1|0 |0-1|0 |0-1|
Firmware-Revision |0-1|0-1|0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |
Host-IP-Address |1+ |1+ |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |
Inband-Security-Id |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |
Multi-Round-Time-Out|0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |
+
+
+
+
+
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 133]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
Origin-Host |1 |1 |1 |1 |1 |1 |1 |1 |1 |1 |1 |1 |
Origin-Realm |1 |1 |1 |1 |1 |1 |1 |1 |1 |1 |1 |1 |
Origin-State-Id |0-1|0-1|0 |0 |0-1|0-1|0-1|0-1|0-1|0-1|0-1|0-1|
@@ -7724,9 +7495,17 @@ Internet-Draft Diameter Base Protocol January 2011
-Fajardo, et al. Expires July 24, 2011 [Page 138]
+
+
+
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 134]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
+-----------+
@@ -7749,6 +7528,7 @@ Internet-Draft Diameter Base Protocol January 2011
Destination-Realm | 1 | 0 |
Error-Reporting-Host | 0 | 0+ |
Event-Timestamp | 0-1 | 0-1 |
+ Failed-AVP | 0 | 0-1 |
Origin-Host | 1 | 1 |
Origin-Realm | 1 | 1 |
Proxy-Info | 0+ | 0+ |
@@ -7760,144 +7540,220 @@ Internet-Draft Diameter Base Protocol January 2011
Vendor-Specific-Application-Id| 0-1 | 0-1 |
------------------------------+-----+-----+
+11. IANA Considerations
+ This section provides guidance to the Internet Assigned Numbers
+ Authority (IANA) regarding registration of values related to the
+ Diameter protocol, in accordance with [RFC5226]. Existing IANA
+ registries and assignments put in place by RFC 3588 remain the same
+ unless explicitly updated or deprecated in this section.
+11.1. AVP Header
+ As defined in Section 4, the AVP header contains three fields that
+ require IANA namespace management: the AVP Code, Vendor-ID, and Flags
+ fields.
-
-
-
-
-
-
-
-
-
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 139]
+Fajardo, et al. Standards Track [Page 135]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
-11. IANA Considerations
+11.1.1. AVP Codes
- This section provides guidance to the Internet Assigned Numbers
- Authority (IANA) regarding registration of values related to the
- Diameter protocol, in accordance with BCP 26 [RFC5226]. The policies
- and procedures for the IANA put in place by [RFC3588] applies here.
- The criteria used by the IANA for assignment of numbers within this
- namespace remains the same unless otherwise stated in this section.
- Existing assignments remains the same unless explicitly updated or
- deprecated in this secion.
+ There are multiple namespaces. Vendors can have their own AVP Codes
+ namespace that will be identified by their Vendor-ID (also known as
+ Enterprise-Number), and they control the assignments of their vendor-
+ specific AVP Codes within their own namespace. The absence of a
+ Vendor-ID or a Vendor-ID value of zero (0) identifies the IETF AVP
+ Codes namespace, which is under IANA control. The AVP Codes and
+ sometimes possible values in an AVP are controlled and maintained by
+ IANA. AVP Code 0 is not used. AVP Codes 1-255 are managed
+ separately as RADIUS Attribute Types. Where a Vendor-Specific AVP is
+ implemented by more than one vendor, allocation of global AVPs should
+ be encouraged instead.
+
+ AVPs may be allocated following Expert Review (by a Designated
+ Expert) with Specification Required [RFC5226]. A block allocation
+ (release of more than three AVPs at a time for a given purpose)
+ requires IETF Review [RFC5226].
-11.1. Changes to AVP Header Allocation
+11.1.2. AVP Flags
- For AVP Headers, the only change is the AVP code block allocations.
- Block allocation (release of more than 3 at a time for a given
- purpose) now only require IETF Review as opposed to an IETF
- Consensus.
+ Section 4.1 describes the existing AVP Flags. The remaining bits can
+ only be assigned via a Standards Action [RFC5226].
11.2. Diameter Header
- For the Diameter Header, the command code namespace allocation has
+11.2.1. Command Codes
+
+ For the Diameter header, the Command Code namespace allocation has
changed. The new allocation rules are as follows:
- The command code values 256 - 8,388,607 (0x100 to 0x7fffff) are
+ The Command Code values 256 - 8,388,607 (0x100 to 0x7fffff) are
for permanent, standard commands, allocated by IETF Review
[RFC5226].
The values 8,388,608 - 16,777,213 (0x800000 - 0xfffffd) are
- reserved for vendor-specific command codes, to be allocated on a
+ reserved for vendor-specific Command Codes, to be allocated on a
First Come, First Served basis by IANA [RFC5226]. The request to
IANA for a Vendor-Specific Command Code SHOULD include a reference
- to a publicly available specification which documents the command
+ to a publicly available specification that documents the command
in sufficient detail to aid in interoperability between
independent implementations. If the specification cannot be made
- publicly available, the request for a vendor-specific command code
+ publicly available, the request for a vendor-specific Command Code
MUST include the contact information of persons and/or entities
responsible for authoring and maintaining the command.
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 136]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ The values 16,777,214 and 16,777,215 (hexadecimal values 0xfffffe
+ - 0xffffff) are reserved for experimental commands. As these
+ codes are only for experimental and testing purposes, no guarantee
+ is made for interoperability between Diameter peers using
+ experimental commands.
+
+11.2.2. Command Flags
+
+ Section 3 describes the existing Command Flags field. The remaining
+ bits can only be assigned via a Standards Action [RFC5226].
+
11.3. AVP Values
For AVP values, the Experimental-Result-Code AVP value allocation has
- been added. The new rule is as follows:
+ been added; see Section 11.3.1. The old AVP value allocation rule,
+ IETF Consensus, has been updated to IETF Review as per [RFC5226], and
+ affected AVPs are listed as reminders.
11.3.1. Experimental-Result-Code AVP
Values for this AVP are purely local to the indicated vendor, and no
IANA registry is maintained for them.
+11.3.2. Result-Code AVP Values
+ New values are available for assignment via IETF Review [RFC5226].
+11.3.3. Accounting-Record-Type AVP Values
-Fajardo, et al. Expires July 24, 2011 [Page 140]
-
-Internet-Draft Diameter Base Protocol January 2011
+ New values are available for assignment via IETF Review [RFC5226].
+
+11.3.4. Termination-Cause AVP Values
+ New values are available for assignment via IETF Review [RFC5226].
-11.4. Diameter TCP, SCTP, TLS/TCP and DTLS/SCTP Port Numbers
+11.3.5. Redirect-Host-Usage AVP Values
- Updated port number assignments are described in this section. The
- IANA has assigned port number 3868 for TCP and SCTP. The port number
- [TBD] has been assigned for TLS/TCP and DTLS/SCTP.
+ New values are available for assignment via IETF Review [RFC5226].
-11.5. S-NAPTR Parameters
+11.3.6. Session-Server-Failover AVP Values
- This document registers a new S-NAPTR Application Service Tag value
- of "aaa".
+ New values are available for assignment via IETF Review [RFC5226].
- This document also registers the following S-NAPTR Application
- Protocol Tags:
+11.3.7. Session-Binding AVP Values
- Tag | Protocol
- -------------------|---------
- diameter.tcp | TCP
- diameter.sctp | SCTP
- diameter.tls.tcp | TLS/TCP
- diameter.dtls.sctp | DTLS/SCTP
+ New values are available for assignment via IETF Review [RFC5226].
+Fajardo, et al. Standards Track [Page 137]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+11.3.8. Disconnect-Cause AVP Values
+ New values are available for assignment via IETF Review [RFC5226].
+11.3.9. Auth-Request-Type AVP Values
+ New values are available for assignment via IETF Review [RFC5226].
+11.3.10. Auth-Session-State AVP Values
+ New values are available for assignment via IETF Review [RFC5226].
+11.3.11. Re-Auth-Request-Type AVP Values
+ New values are available for assignment via IETF Review [RFC5226].
+11.3.12. Accounting-Realtime-Required AVP Values
+ New values are available for assignment via IETF Review [RFC5226].
+11.3.13. Inband-Security-Id AVP (code 299)
+ The use of this AVP has been deprecated.
+11.4. _diameters Service Name and Port Number Registration
+ IANA has registered the "_diameters" service name and assigned port
+ numbers for TLS/TCP and DTLS/SCTP according to the guidelines given
+ in [RFC6335].
+ Service Name: _diameters
+ Transport Protocols: TCP, SCTP
+ Assignee: IESG <[email protected]>
+ Contact: IETF Chair <[email protected]>
+ Description: Diameter over TLS/TCP and DTLS/SCTP
+ Reference: RFC 6733
+ Port Number: 5868, from the User Range
-Fajardo, et al. Expires July 24, 2011 [Page 141]
+
+
+Fajardo, et al. Standards Track [Page 138]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
+
+
+11.5. SCTP Payload Protocol Identifiers
+
+ Two SCTP payload protocol identifiers have been registered in the
+ SCTP Payload Protocol Identifiers registry:
+
+
+ Value | SCTP Payload Protocol Identifier
+ -------|-----------------------------------
+ 46 | Diameter in a SCTP DATA chunk
+ 47 | Diameter in a DTLS/SCTP DATA chunk
+
+11.6. S-NAPTR Parameters
-12. Diameter protocol related configurable parameters
+ The following tag has been registered in the S-NAPTR Application
+ Protocol Tags registry:
+
+ Tag | Protocol
+ -------------------|---------
+ diameter.dtls.sctp | DTLS/SCTP
+
+12. Diameter Protocol-Related Configurable Parameters
This section contains the configurable parameters that are found
throughout this document:
@@ -7915,8 +7771,8 @@ Internet-Draft Diameter Base Protocol January 2011
A Diameter proxy server routes messages based on the realm portion
of a Network Access Identifier (NAI). The server MUST have a
table of Realm Names, and the address of the peer to which the
- message must be forwarded to. The routing table MAY also include
- a "default route", which is typically used for all messages that
+ message must be forwarded. The routing table MAY also include a
+ "default route", which is typically used for all messages that
cannot be locally processed.
Tc timer
@@ -7927,30 +7783,9 @@ Internet-Draft Diameter Base Protocol January 2011
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 142]
+Fajardo, et al. Standards Track [Page 139]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
13. Security Considerations
@@ -7959,34 +7794,34 @@ Internet-Draft Diameter Base Protocol January 2011
[RFC5246] or DTLS/SCTP [RFC6083]. Additional security mechanisms
such as IPsec [RFC4301] MAY also be deployed to secure connections
between peers. However, all Diameter base protocol implementations
- MUST support the use of TLS/TCP and DTLS/SCTP and the Diameter
- protocol MUST NOT be used without any security mechanism.
+ MUST support the use of TLS/TCP and DTLS/SCTP, and the Diameter
+ protocol MUST NOT be used without one of TLS, DTLS, or IPsec.
If a Diameter connection is to be protected via TLS/TCP and DTLS/SCTP
or IPsec, then TLS/TCP and DTLS/SCTP or IPsec/IKE SHOULD begin prior
to any Diameter message exchange. All security parameters for TLS/
TCP and DTLS/SCTP or IPsec are configured independent of the Diameter
- protocol. All Diameter message will be sent through the TLS/TCP and
+ protocol. All Diameter messages will be sent through the TLS/TCP and
DTLS/SCTP or IPsec connection after a successful setup.
For TLS/TCP and DTLS/SCTP connections to be established in the open
state, the CER/CEA exchange MUST include an Inband-Security-ID AVP
with a value of TLS/TCP and DTLS/SCTP. The TLS/TCP and DTLS/SCTP
- handshake will begin when both ends successfully reached the open
+ handshake will begin when both ends successfully reach the open
state, after completion of the CER/CEA exchange. If the TLS/TCP and
DTLS/SCTP handshake is successful, all further messages will be sent
- via TLS/TCP and DTLS/SCTP. If the handshake fails, both ends move to
- the closed state. See Sections 13.1 for more details.
+ via TLS/TCP and DTLS/SCTP. If the handshake fails, both ends MUST
+ move to the closed state. See Section 13.1 for more details.
13.1. TLS/TCP and DTLS/SCTP Usage
Diameter nodes using TLS/TCP and DTLS/SCTP for security MUST mutually
authenticate as part of TLS/TCP and DTLS/SCTP session establishment.
In order to ensure mutual authentication, the Diameter node acting as
- TLS/TCP and DTLS/SCTP server MUST request a certificate from the
+ the TLS/TCP and DTLS/SCTP server MUST request a certificate from the
Diameter node acting as TLS/TCP and DTLS/SCTP client, and the
- Diameter node acting as TLS/TCP and DTLS/SCTP client MUST be prepared
- to supply a certificate on request.
+ Diameter node acting as the TLS/TCP and DTLS/SCTP client MUST be
+ prepared to supply a certificate on request.
Diameter nodes MUST be able to negotiate the following TLS/TCP and
DTLS/SCTP cipher suites:
@@ -8000,16 +7835,24 @@ Internet-Draft Diameter Base Protocol January 2011
TLS_RSA_WITH_AES_128_CBC_SHA
- Diameter nodes MAY negotiate other TLS/TCP and DTLS/SCTP cipher
-Fajardo, et al. Expires July 24, 2011 [Page 143]
+
+Fajardo, et al. Standards Track [Page 140]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
+
+ Note that it is quite possible that support for the
+ TLS_RSA_WITH_AES_128_CBC_SHA cipher suite will be REQUIRED at some
+ future date. Diameter nodes MAY negotiate other TLS/TCP and DTLS/
+ SCTP cipher suites.
- suites.
+ If public key certificates are used for Diameter security (for
+ example, with TLS), the value of the expiration times in the routing
+ and peer tables MUST NOT be greater than the expiry time in the
+ relevant certificates.
13.2. Peer-to-Peer Considerations
@@ -8027,67 +7870,105 @@ Internet-Draft Diameter Base Protocol January 2011
peers may not be known beforehand and therefore peer discovery may be
required.
+13.3. AVP Considerations
+ Diameter AVPs often contain security-sensitive data; for example,
+ user passwords and location data, network addresses and cryptographic
+ keys. The following AVPs defined in this document are considered to
+ be security-sensitive:
+ o Acct-Interim-Interval
+ o Accounting-Realtime-Required
+ o Acct-Multi-Session-Id
+ o Accounting-Record-Number
+ o Accounting-Record-Type
+ o Accounting-Session-Id
+ o Accounting-Sub-Session-Id
+ o Class
+Fajardo, et al. Standards Track [Page 141]
+
+RFC 6733 Diameter Base Protocol October 2012
+ o Session-Id
+ o Session-Binding
+ o Session-Server-Failover
+ o User-Name
+ Diameter messages containing these or any other AVPs considered to be
+ security-sensitive MUST only be sent protected via mutually
+ authenticated TLS or IPsec. In addition, those messages MUST NOT be
+ sent via intermediate nodes unless there is end-to-end security
+ between the originator and recipient or the originator has locally
+ trusted configuration that indicates that end-to-end security is not
+ needed. For example, end-to-end security may not be required in the
+ case where an intermediary node is known to be operated as part of
+ the same administrative domain as the endpoints so that an ability to
+ successfully compromise the intermediary would imply a high
+ probability of being able to compromise the endpoints as well. Note
+ that no end-to-end security mechanism is specified in this document.
+14. References
+14.1. Normative References
+ [FLOATPOINT]
+ Institute of Electrical and Electronics Engineers, "IEEE
+ Standard for Binary Floating-Point Arithmetic, ANSI/IEEE
+ Standard 754-1985", August 1985.
+ [IANAADFAM]
+ IANA, "Address Family Numbers",
+ <http://www.iana.org/assignments/address-family-numbers>.
+ [RFC0791] Postel, J., "Internet Protocol", STD 5, RFC 791,
+ September 1981.
+ [RFC0793] Postel, J., "Transmission Control Protocol", STD 7,
+ RFC 793, September 1981.
+ [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+ [RFC3492] Costello, A., "Punycode: A Bootstring encoding of Unicode
+ for Internationalized Domain Names in Applications
+ (IDNA)", RFC 3492, March 2003.
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 144]
+Fajardo, et al. Standards Track [Page 142]
-Internet-Draft Diameter Base Protocol January 2011
-
-
-14. References
-
-14.1. Normative References
-
- [FLOATPOINT]
- Institute of Electrical and Electronics Engineers, "IEEE
- Standard for Binary Floating-Point Arithmetic, ANSI/IEEE
- Standard 754-1985", August 1985.
+RFC 6733 Diameter Base Protocol October 2012
- [IANAADFAM]
- IANA,, "Address Family Numbers",
- http://www.iana.org/assignments/address-family-numbers.
- [RADTYPE] IANA,, "RADIUS Types",
- http://www.iana.org/assignments/radius-types.
+ [RFC3539] Aboba, B. and J. Wood, "Authentication, Authorization and
+ Accounting (AAA) Transport Profile", RFC 3539, June 2003.
- [RFC791] Postel, J., "Internet Protocol", RFC 791, September 1981.
+ [RFC3629] Yergeau, F., "UTF-8, a transformation format of ISO
+ 10646", STD 63, RFC 3629, November 2003.
- [RFC793] Postel, J., "Transmission Control Protocol", RFC 793,
- January 1981.
+ [RFC3958] Daigle, L. and A. Newton, "Domain-Based Application
+ Service Location Using SRV RRs and the Dynamic Delegation
+ Discovery Service (DDDS)", RFC 3958, January 2005.
- [RFC3539] Aboba, B. and J. Wood, "Authentication, Authorization and
- Accounting (AAA) Transport Profile", RFC 3539, June 2003.
+ [RFC3986] Berners-Lee, T., Fielding, R., and L. Masinter, "Uniform
+ Resource Identifier (URI): Generic Syntax", STD 66,
+ RFC 3986, January 2005.
[RFC4004] Calhoun, P., Johansson, T., Perkins, C., Hiller, T., and
P. McCann, "Diameter Mobile IPv4 Application", RFC 4004,
@@ -8101,51 +7982,44 @@ Internet-Draft Diameter Base Protocol January 2011
Loughney, "Diameter Credit-Control Application", RFC 4006,
August 2005.
- [RFC5234] Crocker, D. and P. Overell, "Augmented BNF for Syntax
- Specifications: ABNF", STD 68, RFC 5234, January 2008.
-
- [RFC3588] Calhoun, P., Loughney, J., Guttman, E., Zorn, G., and J.
- Arkko, "Diameter Base Protocol", RFC 3588, September 2003.
+ [RFC4086] Eastlake, D., Schiller, J., and S. Crocker, "Randomness
+ Requirements for Security", BCP 106, RFC 4086, June 2005.
- [RFC5226] Narten, T. and H. Alvestrand, "Guidelines for Writing an
- IANA Considerations Section in RFCs", BCP 26, RFC 5226,
- May 2008.
+ [RFC4282] Aboba, B., Beadles, M., Arkko, J., and P. Eronen, "The
+ Network Access Identifier", RFC 4282, December 2005.
[RFC4291] Hinden, R. and S. Deering, "IP Version 6 Addressing
Architecture", RFC 4291, February 2006.
+ [RFC4960] Stewart, R., "Stream Control Transmission Protocol",
+ RFC 4960, September 2007.
+ [RFC5226] Narten, T. and H. Alvestrand, "Guidelines for Writing an
+ IANA Considerations Section in RFCs", BCP 26, RFC 5226,
+ May 2008.
-Fajardo, et al. Expires July 24, 2011 [Page 145]
-
-Internet-Draft Diameter Base Protocol January 2011
-
+ [RFC5234] Crocker, D. and P. Overell, "Augmented BNF for Syntax
+ Specifications: ABNF", STD 68, RFC 5234, January 2008.
- [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
- Requirement Levels", BCP 14, RFC 2119, March 1997.
+ [RFC5246] Dierks, T. and E. Rescorla, "The Transport Layer Security
+ (TLS) Protocol Version 1.2", RFC 5246, August 2008.
- [RFC4282] Aboba, B., Beadles, M., Arkko, J., and P. Eronen, "The
- Network Access Identifier", RFC 4282, December 2005.
- [RFC4086] Eastlake, D., Schiller, J., and S. Crocker, "Randomness
- Requirements for Security", BCP 106, RFC 4086, June 2005.
- [RFC4960] Stewart, R., "Stream Control Transmission Protocol",
- RFC 4960, September 2007.
- [RFC3958] Daigle, L. and A. Newton, "Domain-Based Application
- Service Location Using SRV RRs and the Dynamic Delegation
- Discovery Service (DDDS)", RFC 3958, January 2005.
+Fajardo, et al. Standards Track [Page 143]
+
+RFC 6733 Diameter Base Protocol October 2012
- [RFC5246] Dierks, T. and E. Rescorla, "The Transport Layer Security
- (TLS) Protocol Version 1.2", RFC 5246, August 2008.
- [RFC3986] Berners-Lee, T., Fielding, R., and L. Masinter, "Uniform
- Resource Identifier (URI): Generic Syntax", STD 66,
- RFC 3986, January 2005.
+ [RFC5280] Cooper, D., Santesson, S., Farrell, S., Boeyen, S.,
+ Housley, R., and W. Polk, "Internet X.509 Public Key
+ Infrastructure Certificate and Certificate Revocation List
+ (CRL) Profile", RFC 5280, May 2008.
- [RFC3629] Yergeau, F., "UTF-8, a transformation format of ISO
- 10646", STD 63, RFC 3629, November 2003.
+ [RFC5729] Korhonen, J., Jones, M., Morand, L., and T. Tsou,
+ "Clarifications on the Routing of Diameter Requests Based
+ on the Username and the Realm", RFC 5729, December 2009.
[RFC5890] Klensin, J., "Internationalized Domain Names for
Applications (IDNA): Definitions and Document Framework",
@@ -8154,103 +8028,120 @@ Internet-Draft Diameter Base Protocol January 2011
[RFC5891] Klensin, J., "Internationalized Domain Names in
Applications (IDNA): Protocol", RFC 5891, August 2010.
- [RFC3492] Costello, A., "Punycode: A Bootstring encoding of Unicode
- for Internationalized Domain Names in Applications
- (IDNA)", RFC 3492, March 2003.
-
- [RFC5729] Korhonen, J., Jones, M., Morand, L., and T. Tsou,
- "Clarifications on the Routing of Diameter Requests Based
- on the Username and the Realm", RFC 5729, December 2009.
-
- [RFC4347] Rescorla, E. and N. Modadugu, "Datagram Transport Layer
- Security", RFC 4347, April 2006.
-
[RFC6083] Tuexen, M., Seggelmann, R., and E. Rescorla, "Datagram
Transport Layer Security (DTLS) for Stream Control
Transmission Protocol (SCTP)", RFC 6083, January 2011.
+ [RFC6347] Rescorla, E. and N. Modadugu, "Datagram Transport Layer
+ Security Version 1.2", RFC 6347, January 2012.
+ [RFC6408] Jones, M., Korhonen, J., and L. Morand, "Diameter
+ Straightforward-Naming Authority Pointer (S-NAPTR) Usage",
+ RFC 6408, November 2011.
+14.2. Informative References
-Fajardo, et al. Expires July 24, 2011 [Page 146]
-
-Internet-Draft Diameter Base Protocol January 2011
+ [ENTERPRISE] IANA, "SMI Network Management Private Enterprise
+ Codes",
+ <http://www.iana.org/assignments/enterprise-numbers>.
+ [IANATCV] IANA, "Termination-Cause AVP Values (code 295)",
+ <http://www.iana.org/assignments/aaa-parameters/
+ aaa-parameters.xml#aaa-parameters-16>.
-14.2. Informational References
+ [RFC1492] Finseth, C., "An Access Control Protocol, Sometimes
+ Called TACACS", RFC 1492, July 1993.
- [RFC2989] Aboba, B., Calhoun, P., Glass, S., Hiller, T., McCann, P.,
- Shiino, H., Walsh, P., Zorn, G., Dommety, G., Perkins, C.,
- Patil, B., Mitton, D., Manning, S., Beadles, M., Chen, X.,
- Sivalingham, S., Hameed, A., Munson, M., Jacobs, S., Lim,
- B., Hirschman, B., Hsu, R., Koo, H., Lipford, M.,
- Campbell, E., Xu, Y., Baba, S., and E. Jaques, "Criteria
- for Evaluating AAA Protocols for Network Access",
- RFC 2989, November 2000.
+ [RFC1661] Simpson, W., "The Point-to-Point Protocol (PPP)",
+ STD 51, RFC 1661, July 1994.
- [RFC2975] Aboba, B., Arkko, J., and D. Harrington, "Introduction to
- Accounting Management", RFC 2975, October 2000.
+ [RFC2104] Krawczyk, H., Bellare, M., and R. Canetti, "HMAC:
+ Keyed-Hashing for Message Authentication", RFC 2104,
+ February 1997.
- [RFC3232] Reynolds, J., "Assigned Numbers: RFC 1700 is Replaced by
- an On-line Database", RFC 3232, January 2002.
- [RFC5176] Chiba, M., Dommety, G., Eklund, M., Mitton, D., and B.
- Aboba, "Dynamic Authorization Extensions to Remote
- Authentication Dial In User Service (RADIUS)", RFC 5176,
- January 2008.
- [RFC1661] Simpson, W., "The Point-to-Point Protocol (PPP)", STD 51,
- RFC 1661, July 1994.
- [RFC2866] Rigney, C., "RADIUS Accounting", RFC 2866, June 2000.
- [RFC2869] Rigney, C., Willats, W., and P. Calhoun, "RADIUS
- Extensions", RFC 2869, June 2000.
+Fajardo, et al. Standards Track [Page 144]
+
+RFC 6733 Diameter Base Protocol October 2012
- [RFC2865] Rigney, C., Willens, S., Rubens, A., and W. Simpson,
- "Remote Authentication Dial In User Service (RADIUS)",
- RFC 2865, June 2000.
- [RFC3162] Aboba, B., Zorn, G., and D. Mitton, "RADIUS and IPv6",
- RFC 3162, August 2001.
+ [RFC2782] Gulbrandsen, A., Vixie, P., and L. Esibov, "A DNS RR
+ for specifying the location of services (DNS SRV)",
+ RFC 2782, February 2000.
- [RFC4301] Kent, S. and K. Seo, "Security Architecture for the
- Internet Protocol", RFC 4301, December 2005.
+ [RFC2865] Rigney, C., Willens, S., Rubens, A., and W. Simpson,
+ "Remote Authentication Dial In User Service (RADIUS)",
+ RFC 2865, June 2000.
- [RFC5905] Mills, D., Martin, J., Burbank, J., and W. Kasch, "Network
- Time Protocol Version 4: Protocol and Algorithms
- Specification", RFC 5905, June 2010.
+ [RFC2866] Rigney, C., "RADIUS Accounting", RFC 2866, June 2000.
- [RFC1492] Finseth, C., "An Access Control Protocol, Sometimes Called
- TACACS", RFC 1492, July 1993.
+ [RFC2869] Rigney, C., Willats, W., and P. Calhoun, "RADIUS
+ Extensions", RFC 2869, June 2000.
- [RFC4690] Klensin, J., Faltstrom, P., Karp, C., and IAB, "Review and
+ [RFC2881] Mitton, D. and M. Beadles, "Network Access Server
+ Requirements Next Generation (NASREQNG) NAS Model",
+ RFC 2881, July 2000.
+ [RFC2975] Aboba, B., Arkko, J., and D. Harrington, "Introduction
+ to Accounting Management", RFC 2975, October 2000.
+ [RFC2989] Aboba, B., Calhoun, P., Glass, S., Hiller, T., McCann,
+ P., Shiino, H., Walsh, P., Zorn, G., Dommety, G.,
+ Perkins, C., Patil, B., Mitton, D., Manning, S.,
+ Beadles, M., Chen, X., Sivalingham, S., Hameed, A.,
+ Munson, M., Jacobs, S., Lim, B., Hirschman, B., Hsu,
+ R., Koo, H., Lipford, M., Campbell, E., Xu, Y., Baba,
+ S., and E. Jaques, "Criteria for Evaluating AAA
+ Protocols for Network Access", RFC 2989, November 2000.
-Fajardo, et al. Expires July 24, 2011 [Page 147]
-
-Internet-Draft Diameter Base Protocol January 2011
+ [RFC3162] Aboba, B., Zorn, G., and D. Mitton, "RADIUS and IPv6",
+ RFC 3162, August 2001.
+ [RFC3748] Aboba, B., Blunk, L., Vollbrecht, J., Carlson, J., and
+ H. Levkowetz, "Extensible Authentication Protocol
+ (EAP)", RFC 3748, June 2004.
- Recommendations for Internationalized Domain Names
- (IDNs)", RFC 4690, September 2006.
+ [RFC4301] Kent, S. and K. Seo, "Security Architecture for the
+ Internet Protocol", RFC 4301, December 2005.
- [RFC5461] Gont, F., "TCP's Reaction to Soft Errors", RFC 5461,
- February 2009.
+ [RFC4690] Klensin, J., Faltstrom, P., Karp, C., and IAB, "Review
+ and Recommendations for Internationalized Domain Names
+ (IDNs)", RFC 4690, September 2006.
- [RFC5927] Gont, F., "ICMP Attacks against TCP", RFC 5927, July 2010.
+ [RFC5176] Chiba, M., Dommety, G., Eklund, M., Mitton, D., and B.
+ Aboba, "Dynamic Authorization Extensions to Remote
+ Authentication Dial In User Service (RADIUS)",
+ RFC 5176, January 2008.
- [RFC3692] Narten, T., "Assigning Experimental and Testing Numbers
- Considered Useful", BCP 82, RFC 3692, January 2004.
+Fajardo, et al. Standards Track [Page 145]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+ [RFC5461] Gont, F., "TCP's Reaction to Soft Errors", RFC 5461,
+ February 2009.
+ [RFC5905] Mills, D., Martin, J., Burbank, J., and W. Kasch,
+ "Network Time Protocol Version 4: Protocol and
+ Algorithms Specification", RFC 5905, June 2010.
+ [RFC5927] Gont, F., "ICMP Attacks against TCP", RFC 5927,
+ July 2010.
+ [RFC6335] Cotton, M., Eggert, L., Touch, J., Westerlund, M., and
+ S. Cheshire, "Internet Assigned Numbers Authority
+ (IANA) Procedures for the Management of the Service
+ Name and Transport Protocol Port Number Registry",
+ BCP 165, RFC 6335, August 2011.
+ [RFC6737] Kang, J. and G. Zorn, "The Diameter Capabilities Update
+ Application", RFC 6737, October 2012.
@@ -8284,80 +8175,83 @@ Internet-Draft Diameter Base Protocol January 2011
-Fajardo, et al. Expires July 24, 2011 [Page 148]
+Fajardo, et al. Standards Track [Page 146]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
Appendix A. Acknowledgements
-A.1. RFC3588bis
+A.1. This Document
The authors would like to thank the following people that have
provided proposals and contributions to this document:
To Vishnu Ram and Satendra Gera for their contributions on
- Capabilities Updates, Predictive Loop Avoidance as well as many other
- technical proposals. To Tolga Asveren for his insights and
+ capabilities updates, predictive loop avoidance, as well as many
+ other technical proposals. To Tolga Asveren for his insights and
contributions on almost all of the proposed solutions incorporated
- into this document. To Timothy Smith for helping on the Capabilities
- Updates and other topics. To Tony Zhang for providing fixes to loop
- holes on composing Failed-AVPs as well as many other issues and
+ into this document. To Timothy Smith for helping on the capabilities
+ Update and other topics. To Tony Zhang for providing fixes to
+ loopholes on composing Failed-AVPs as well as many other issues and
topics. To Jan Nordqvist for clearly stating the usage of
- Application Ids. To Anders Kristensen for providing needed technical
+ Application Ids. To Anders Kristensen for providing needed technical
opinions. To David Frascone for providing invaluable review of the
document. To Mark Jones for providing clarifying text on vendor
- command codes and other vendor specific indicators.
+ command codes and other vendor-specific indicators. To Victor
+ Pascual and Sebastien Decugis for new text and recommendations on
+ SCTP/DTLS. To Jouni Korhonen for taking over the editing task and
+ resolving last bits from versions 27 through 29.
- Special thanks to the Diameter extensibility design team which helped
- resolve the tricky question of mandatory AVPs and ABNF semantics.
- The members of this team are as follows:
+ Special thanks to the Diameter extensibility design team, which
+ helped resolve the tricky question of mandatory AVPs and ABNF
+ semantics. The members of this team are as follows:
Avi Lior, Jari Arkko, Glen Zorn, Lionel Morand, Mark Jones, Tolga
- Asveren Jouni Korhonen, Glenn McGregor.
+ Asveren, Jouni Korhonen, and Glenn McGregor.
Special thanks also to people who have provided invaluable comments
and inputs especially in resolving controversial issues:
- Glen Zorn, Yoshihiro Ohba, Marco Stura, and Pasi Eronen.
+ Glen Zorn, Yoshihiro Ohba, Marco Stura, Stephen Farrel, Pete Resnick,
+ Peter Saint-Andre, Robert Sparks, Krishna Prasad, Sean Turner, Barry
+ Leiba, and Pasi Eronen.
Finally, we would like to thank the original authors of this
document:
- Pat Calhoun, John Loughney, Jari Arkko, Erik Guttman and Glen Zorn.
+ Pat Calhoun, John Loughney, Jari Arkko, Erik Guttman, and Glen Zorn.
Their invaluable knowledge and experience has given us a robust and
flexible AAA protocol that many people have seen great value in
adopting. We greatly appreciate their support and stewardship for
the continued improvements of Diameter as a protocol. We would also
like to extend our gratitude to folks aside from the authors who have
- assisted and contributed to the original version of this document.
- Their efforts significantly contributed to the success of Diameter.
-
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 149]
+Fajardo, et al. Standards Track [Page 147]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
-A.2. RFC3588
+ assisted and contributed to the original version of this document.
+ Their efforts significantly contributed to the success of Diameter.
+
+A.2. RFC 3588
The authors would like to thank Nenad Trifunovic, Tony Johansson and
Pankaj Patel for their participation in the pre-IETF Document Reading
- Party. Allison Mankin, Jonathan Wood and Bernard Aboba provided
- invaluable assistance in working out transport issues, and similarly
- with Steven Bellovin in the security area.
+ Party. Allison Mankin, Jonathan Wood, and Bernard Aboba provided
+ invaluable assistance in working out transport issues and this was
+ also the case with Steven Bellovin in the security area.
Paul Funk and David Mitton were instrumental in getting the Peer
State Machine correct, and our deep thanks go to them for their time.
Text in this document was also provided by Paul Funk, Mark Eklund,
- Mark Jones and Dave Spence. Jacques Caron provided many great
+ Mark Jones, and Dave Spence. Jacques Caron provided many great
comments as a result of a thorough review of the spec.
The authors would also like to acknowledge the following people for
@@ -8367,96 +8261,57 @@ A.2. RFC3588
David Frascone, Daniel C. Fox, Lol Grant, Ignacio Goyret, Nancy
Greene, Peter Heitman, Fredrik Johansson, Mark Jones, Martin Julien,
Bob Kopacz, Paul Krumviede, Fergal Ladley, Ryan Moats, Victor Muslin,
- Kenneth Peirce, John Schnizlein, Sumit Vakil, John R. Vollbrecht and
+ Kenneth Peirce, John Schnizlein, Sumit Vakil, John R. Vollbrecht, and
Jeff Weisberg.
Finally, Pat Calhoun would like to thank Sun Microsystems since most
of the effort put into this document was done while he was in their
employ.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 150]
-
-Internet-Draft Diameter Base Protocol January 2011
-
-
Appendix B. S-NAPTR Example
As an example, consider a client that wishes to resolve aaa:
- example1.com. The client performs a NAPTR query for that domain, and
- the following NAPTR records are returned:
+ ex1.example.com. The client performs a NAPTR query for that domain,
+ and the following NAPTR records are returned:
;; order pref flags service regexp replacement
IN NAPTR 50 50 "s" "aaa:diameter.tls.tcp" ""
- _diameter._tls.example1.com
+ _diameter._tls.ex1.example.com
IN NAPTR 100 50 "s" "aaa:diameter.tcp" ""
- _aaa._tcp.example1.com
+ _aaa._tcp.ex1.example.com
IN NAPTR 150 50 "s" "aaa:diameter.sctp" ""
- _diameter._sctp.example1.com
+ _diameter._sctp.ex1.example.com
- This indicates that the server supports TLS, TCP and SCTP in that
+ This indicates that the server supports TLS, TCP, and SCTP in that
order. If the client supports TLS, TLS will be used, targeted to a
- host determined by an SRV lookup of _diameter._tls.example1.com.
+
+
+
+Fajardo, et al. Standards Track [Page 148]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ host determined by an SRV lookup of _diameter._tls.ex1.example.com.
That lookup would return:
;; Priority Weight Port Target
- IN SRV 0 1 5060 server1.example1.com
- IN SRV 0 2 5060 server2.example1.com
+ IN SRV 0 1 5060 server1.ex1.example.com
+ IN SRV 0 2 5060 server2.ex1.example.com
As an alternative example, a client that wishes to resolve aaa:
- example2.com. The client performs a NAPTR query for that domain, and
- the following NAPTR records are returned:
+ ex2.example.com. The client performs a NAPTR query for that domain,
+ and the following NAPTR records are returned:
;; order pref flags service regexp replacement
IN NAPTR 150 50 "a" "aaa:diameter.tls.tcp" ""
- server1.example2.com
+ server1.ex2.example.com
IN NAPTR 150 50 "a" "aaa:diameter.tls.tcp" ""
- server2.example2.com
+ server2.ex2.example.com
This indicates that the server supports TCP available at the returned
host names.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 151]
-
-Internet-Draft Diameter Base Protocol January 2011
-
-
Appendix C. Duplicate Detection
As described in Section 9.4, accounting record duplicate detection is
@@ -8464,14 +8319,14 @@ Appendix C. Duplicate Detection
reasons:
o Failover to an alternate server. Where close to real-time
- performance is required, failover thresholds need to be kept low
- and this may lead to an increased likelihood of duplicates.
- Failover can occur at the client or within Diameter agents.
-
- o Failure of a client or agent after sending of a record from non-
- volatile memory, but prior to receipt of an application layer ACK
- and deletion of the record. record to be sent. This will result
- in retransmission of the record soon after the client or agent has
+ performance is required, failover thresholds need to be kept low.
+ This may lead to an increased likelihood of duplicates. Failover
+ can occur at the client or within Diameter agents.
+
+ o Failure of a client or agent after sending a record from non-
+ volatile memory, but prior to receipt of an application-layer ACK
+ and deletion of the record to be sent. This will result in
+ retransmission of the record soon after the client or agent has
rebooted.
o Duplicates received from RADIUS gateways. Since the
@@ -8481,48 +8336,48 @@ Appendix C. Duplicate Detection
o Implementation problems and misconfiguration.
- The T flag is used as an indication of an application layer
+ The T flag is used as an indication of an application-layer
retransmission event, e.g., due to failover to an alternate server.
It is defined only for request messages sent by Diameter clients or
agents. For instance, after a reboot, a client may not know whether
+
+
+
+Fajardo, et al. Standards Track [Page 149]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
it has already tried to send the accounting records in its non-
volatile memory before the reboot occurred. Diameter servers MAY use
the T flag as an aid when processing requests and detecting duplicate
messages. However, servers that do this MUST ensure that duplicates
are found even when the first transmitted request arrives at the
server after the retransmitted request. It can be used only in cases
- where no answer has been received from the Server for a request and
+ where no answer has been received from the server for a request and
the request is sent again, (e.g., due to a failover to an alternate
peer, due to a recovered primary peer or due to a client re-sending a
stored record from non-volatile memory such as after reboot of a
client or agent).
- In some cases the Diameter accounting server can delay the duplicate
+ In some cases, the Diameter accounting server can delay the duplicate
detection and accounting record processing until a post-processing
phase takes place. At that time records are likely to be sorted
according to the included User-Name and duplicate elimination is easy
- in this case. In other situations it may be necessary to perform
+ in this case. In other situations, it may be necessary to perform
real-time duplicate detection, such as when credit limits are imposed
or real-time fraud detection is desired.
-
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 152]
-
-Internet-Draft Diameter Base Protocol January 2011
-
-
In general, only generation of duplicates due to failover or re-
sending of records in non-volatile storage can be reliably detected
- by Diameter clients or agents. In such cases the Diameter client or
- agents can mark the message as possible duplicate by setting the T
+ by Diameter clients or agents. In such cases, the Diameter client or
+ agents can mark the message as a possible duplicate by setting the T
flag. Since the Diameter server is responsible for duplicate
- detection, it can choose to make use of the T flag or not, in order
- to optimize duplicate detection. Since the T flag does not affect
- interoperability, and may not be needed by some servers, generation
- of the T flag is REQUIRED for Diameter clients and agents, but MAY be
- implemented by Diameter servers.
+ detection, it can choose whether or not to make use of the T flag, in
+ order to optimize duplicate detection. Since the T flag does not
+ affect interoperability, and it may not be needed by some servers,
+ generation of the T flag is REQUIRED for Diameter clients and agents,
+ but it MAY be implemented by Diameter servers.
As an example, it can be usually be assumed that duplicates appear
within a time window of longest recorded network partition or device
@@ -8535,40 +8390,39 @@ Internet-Draft Diameter Base Protocol January 2011
The following is an example of how the T flag may be used by the
server to detect duplicate requests.
-
A Diameter server MAY check the T flag of the received message to
determine if the record is a possible duplicate. If the T flag is
set in the request message, the server searches for a duplicate
within a configurable duplication time window backward and
forward. This limits database searching to those records where
- the T flag is set. In a well run network, network partitions and
+ the T flag is set. In a well-run network, network partitions and
+
+
+
+Fajardo, et al. Standards Track [Page 150]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
device faults will presumably be rare events, so this approach
represents a substantial optimization of the duplicate detection
process. During failover, it is possible for the original record
- to be received after the T flag marked record, due to differences
+ to be received after the T-flag-marked record, due to differences
in network delays experienced along the path by the original and
duplicate transmissions. The likelihood of this occurring
increases as the failover interval is decreased. In order to be
- able to detect out of order duplicates, the Diameter server should
- use backward and forward time windows when performing duplicate
- checking for the T flag marked request. For example, in order to
- allow time for the original record to exit the network and be
- recorded by the accounting server, the Diameter server can delay
- processing records with the T flag set until a time period
- TIME_WAIT + RECORD_PROCESSING_TIME has elapsed after the closing
- of the original transport connection. After this time period has
- expired, then it may check the T flag marked records against the
+ able to detect duplicates that are out of order, the Diameter
+ server should use backward and forward time windows when
+ performing duplicate checking for the T-flag-marked request. For
+ example, in order to allow time for the original record to exit
+ the network and be recorded by the accounting server, the Diameter
+ server can delay processing records with the T flag set until a
+ time period TIME_WAIT + RECORD_PROCESSING_TIME has elapsed after
+ the closing of the original transport connection. After this time
+ period, it may check the T-flag-marked records against the
database with relative assurance that the original records, if
sent, have been received and recorded.
-
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 153]
-
-Internet-Draft Diameter Base Protocol January 2011
-
-
Appendix D. Internationalized Domain Names
To be compatible with the existing DNS infrastructure and simplify
@@ -8579,14 +8433,7 @@ Appendix D. Internationalized Domain Names
recommendations in [RFC4690] and [RFC5890]. Applications that
provide support for IDNs outside of the Diameter protocol but
interacting with it SHOULD use the representation and conversion
- framework described in [RFC5890], [RFC5891] and [RFC3492].
-
-
-
-
-
-
-
+ framework described in [RFC5890], [RFC5891], and [RFC3492].
@@ -8608,21 +8455,9 @@ Appendix D. Internationalized Domain Names
-
-
-
-
-
-
-
-
-
-
-
-
-Fajardo, et al. Expires July 24, 2011 [Page 154]
+Fajardo, et al. Standards Track [Page 151]
-Internet-Draft Diameter Base Protocol January 2011
+RFC 6733 Diameter Base Protocol October 2012
Authors' Addresses
@@ -8634,7 +8469,7 @@ Authors' Addresses
USA
Phone: +1-908-421-1845
Jari Arkko
@@ -8643,7 +8478,7 @@ Authors' Addresses
Finland
Phone: +358 40 5079256
John Loughney
@@ -8653,17 +8488,17 @@ Authors' Addresses
US
Phone: +1-650-283-8068
- Glenn Zorn
+ Glen Zorn (editor)
Network Zen
- 1310 East Thomas Street
- Seattle, WA 98102
- US
+ 227/358 Thanon Sanphawut
+ Bang Na, Bangkok 10260
+ Thailand
- Phone:
+ Phone: +66 (0) 87-0404617
@@ -8676,6 +8511,5 @@ Authors' Addresses
-Fajardo, et al. Expires July 24, 2011 [Page 155]
+Fajardo, et al. Standards Track [Page 152]
-
diff --git a/lib/diameter/doc/standard/rfc6737.txt b/lib/diameter/doc/standard/rfc6737.txt
new file mode 100644
index 0000000000..50aa33e98f
--- /dev/null
+++ b/lib/diameter/doc/standard/rfc6737.txt
@@ -0,0 +1,339 @@
+
+
+
+
+
+
+Internet Engineering Task Force (IETF) K. Jiao
+Request for Comments: 6737 Huawei
+Category: Standards Track G. Zorn
+ISSN: 2070-1721 Network Zen
+ October 2012
+
+
+ The Diameter Capabilities Update Application
+
+Abstract
+
+ This document defines a new Diameter application and associated
+ Command Codes. The Capabilities Update application is intended to
+ allow the dynamic update of certain Diameter peer capabilities while
+ the peer-to-peer connection is in the open state.
+
+Status of This Memo
+
+ This is an Internet Standards Track document.
+
+ This document is a product of the Internet Engineering Task Force
+ (IETF). It represents the consensus of the IETF community. It has
+ received public review and has been approved for publication by the
+ Internet Engineering Steering Group (IESG). Further information on
+ Internet Standards is available in Section 2 of RFC 5741.
+
+ Information about the current status of this document, any errata,
+ and how to provide feedback on it may be obtained at
+ http://www.rfc-editor.org/info/rfc6737.
+
+Copyright Notice
+
+ Copyright (c) 2012 IETF Trust and the persons identified as the
+ document authors. All rights reserved.
+
+ This document is subject to BCP 78 and the IETF Trust's Legal
+ Provisions Relating to IETF Documents
+ (http://trustee.ietf.org/license-info) in effect on the date of
+ publication of this document. Please review these documents
+ carefully, as they describe your rights and restrictions with respect
+ to this document. Code Components extracted from this document must
+ include Simplified BSD License text as described in Section 4.e of
+ the Trust Legal Provisions and are provided without warranty as
+ described in the Simplified BSD License.
+
+
+
+
+
+
+
+Jiao & Zorn Standards Track [Page 1]
+
+RFC 6737 Diameter Capabilities Update October 2012
+
+
+Table of Contents
+
+ 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 2
+ 2. Specification of Requirements . . . . . . . . . . . . . . . . . 2
+ 3. Diameter Protocol Considerations . . . . . . . . . . . . . . . 3
+ 4. Capabilities Update . . . . . . . . . . . . . . . . . . . . . . 3
+ 4.1. Command Code Values . . . . . . . . . . . . . . . . . . . . 4
+ 4.1.1. Capabilities-Update-Request . . . . . . . . . . . . . . 4
+ 4.1.2. Capabilities-Update-Answer . . . . . . . . . . . . . . 5
+ 5. Security Considerations . . . . . . . . . . . . . . . . . . . . 5
+ 6. IANA Considerations . . . . . . . . . . . . . . . . . . . . . . 5
+ 6.1. Application Identifier . . . . . . . . . . . . . . . . . . 5
+ 6.2. Command Codes . . . . . . . . . . . . . . . . . . . . . . . 5
+ 7. Contributors . . . . . . . . . . . . . . . . . . . . . . . . . 5
+ 8. Acknowledgements . . . . . . . . . . . . . . . . . . . . . . . 5
+ 9. References . . . . . . . . . . . . . . . . . . . . . . . . . . 6
+ 9.1. Normative References . . . . . . . . . . . . . . . . . . . 6
+ 9.2. Informative References . . . . . . . . . . . . . . . . . . 6
+
+1. Introduction
+
+ Capabilities exchange is an important component of the Diameter base
+ protocol [RFC6733], allowing peers to exchange identities and
+ Diameter capabilities (protocol version number, supported Diameter
+ applications, security mechanisms, etc.). As defined in RFC 3588,
+ however, the capabilities exchange process takes place only once, at
+ the inception of a transport connection between a given pair of
+ peers. Therefore, if a peer's capabilities change (due to a software
+ update, for example), the existing connection(s) must be torn down
+ (along with all of the associated user sessions) and restarted before
+ the modified capabilities can be advertised.
+
+ This document defines a new Diameter application intended to allow
+ the dynamic update of a subset of Diameter peer capabilities over an
+ existing connection. Because the Capabilities Update application
+ specified herein operates over an existing transport connection,
+ modification of certain capabilities is prohibited. Specifically,
+ modifying the security mechanism in use is not allowed; if the
+ security method used between a pair of peers is changed, the affected
+ connection MUST be restarted.
+
+2. Specification of Requirements
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+ document are to be interpreted as described in RFC 2119 [RFC2119].
+
+
+
+
+
+Jiao & Zorn Standards Track [Page 2]
+
+RFC 6737 Diameter Capabilities Update October 2012
+
+
+3. Diameter Protocol Considerations
+
+ This section details the relationship of the Diameter Capabilities
+ Update application to the Diameter base protocol.
+
+ This document specifies Diameter Application-Id 10. Diameter nodes
+ conforming to this specification MUST advertise support by including
+ the value 10 in the Auth-Application-Id of the Capabilities-Exchange-
+ Request (CER) and Capabilities-Exchange-Answer (CEA) commands
+ [RFC6733].
+
+4. Capabilities Update
+
+ When the capabilities of a Diameter node conforming to this
+ specification change, the node MUST notify all of the nodes with
+ which it has an open transport connection and which have also
+ advertised support for the Capabilities Update application using the
+ Capabilities-Update-Request (CUR) message (Section 4.1.1). This
+ message allows the update of a peer's capabilities (supported
+ Diameter applications, etc.).
+
+ A Diameter node only issues a given command to those peers that have
+ advertised support for the Diameter application that defines the
+ command; a Diameter node must cache the supported applications in
+ order to ensure that unrecognized commands and/or Attribute-Value
+ Pairs (AVPs) are not unnecessarily sent to a peer.
+
+ The receiver of the CUR MUST determine common applications by
+ computing the intersection of its own set of supported Application
+ Ids against all of the Application-Id AVPs (Auth-Application-Id,
+ Acct-Application-Id, and Vendor-Specific-Application-Id) present in
+ the CUR. The value of the Vendor-Id AVP in the Vendor-Specific-
+ Application-Id MUST NOT be used during computation.
+
+ If the receiver of a CUR does not have any applications in common
+ with the sender, then it MUST return a Capabilities-Update-Answer
+ (CUA) (Section 4.1.2) with the Result-Code AVP set to
+ DIAMETER_NO_COMMON_APPLICATION [RFC6733], and it SHOULD disconnect
+ the transport-layer connection. However, if active sessions are
+ using the connection, peers MAY delay disconnection until the
+ sessions can be redirected or gracefully terminated. Note that
+ receiving a CUA from a peer advertising itself as a relay (see
+ [RFC6733], Section 2.4) MUST be interpreted as having common
+ applications with the peer.
+
+ As for CER/CEA messages, the CUR and CUA messages MUST NOT be
+ proxied, redirected, or relayed.
+
+
+
+
+Jiao & Zorn Standards Track [Page 3]
+
+RFC 6737 Diameter Capabilities Update October 2012
+
+
+ Even though the CUR/CUA messages cannot be proxied, it is still
+ possible for an upstream agent to receive a message for which there
+ are no peers available to handle the application that corresponds to
+ the Command Code. This could happen if, for example, the peers are
+ too busy or down. In such instances, the 'E' bit MUST be set in the
+ answer message with the Result-Code AVP set to
+ DIAMETER_UNABLE_TO_DELIVER to inform the downstream peer to take
+ action (e.g., re-routing requests to an alternate peer).
+
+4.1. Command Code Values
+
+ This section defines Command Code [RFC6733] values that MUST be
+ supported by all Diameter implementations conforming to this
+ specification. The following Command Codes are defined in this
+ document: Capabilities-Update-Request (CUR, Section 4.1.1), and
+ Capabilities-Update-Answer (CUA, Section 4.1.2). The Diameter
+ Command Code Format (CCF) ([RFC6733], Section 3.2) is used in the
+ definitions.
+
+4.1.1. Capabilities-Update-Request
+
+ The Capabilities-Update-Request (CUR), indicated by the Command Code
+ set to 328 and the Command Flags' 'R' bit set, is sent to update
+ local capabilities. Upon detection of a transport failure, this
+ message MUST NOT be sent to an alternate peer.
+
+ When Diameter is run over the Stream Control Transmission Protocol
+ (SCTP) [RFC4960], which allows connections to span multiple
+ interfaces and multiple IP addresses, the Capabilities-Update-Request
+ message MUST contain one Host-IP-Address AVP for each potential IP
+ address that may be locally used when transmitting Diameter messages.
+
+ Message Format
+
+ <CUR> ::= < Diameter Header: 328, REQ >
+ { Origin-Host }
+ { Origin-Realm }
+ 1* { Host-IP-Address }
+ { Vendor-Id }
+ { Product-Name }
+ [ Origin-State-Id ]
+ * [ Supported-Vendor-Id ]
+ * [ Auth-Application-Id ]
+ * [ Acct-Application-Id ]
+ * [ Vendor-Specific-Application-Id ]
+ [ Firmware-Revision ]
+ * [ AVP ]
+
+
+
+
+Jiao & Zorn Standards Track [Page 4]
+
+RFC 6737 Diameter Capabilities Update October 2012
+
+
+4.1.2. Capabilities-Update-Answer
+
+ The Capabilities-Update-Answer, indicated by the Command Code set to
+ 328 and the Command Flags' 'R' bit cleared, is sent in response to a
+ CUR message.
+
+ Message Format
+
+ <CUA> ::= < Diameter Header: 328 >
+ { Origin-Host }
+ { Origin-Realm }
+ { Result-Code }
+ [ Error-Message ]
+ * [ AVP ]
+
+5. Security Considerations
+
+ The security considerations applicable to the Diameter base protocol
+ [RFC6733] are also applicable to this document.
+
+6. IANA Considerations
+
+ This section explains the criteria to be used by the IANA for
+ assignment of numbers within namespaces used within this document.
+
+6.1. Application Identifier
+
+ This specification assigns the value 10 (Diameter Capabilities
+ Update) from the Application Identifiers namespace [RFC6733]. See
+ Section 3 for the assignment of the namespace in this specification.
+
+6.2. Command Codes
+
+ This specification assigns the value 328 (Capabilities-Update-
+ Request/Capabilities-Update-Answer (CUR/CUA)) from the Command Codes
+ namespace [RFC6733]. See Section 4.1 for the assignment of the
+ namespace in this specification.
+
+7. Contributors
+
+ This document is based upon work done by Tina Tsou.
+
+8. Acknowledgements
+
+ Thanks to Sebastien Decugis, Niklas Neumann, Subash Comerica, Lionel
+ Morand, Dan Romascanu, Dan Harkins, and Ravi for helpful review and
+ discussion.
+
+
+
+
+Jiao & Zorn Standards Track [Page 5]
+
+RFC 6737 Diameter Capabilities Update October 2012
+
+
+9. References
+
+9.1. Normative References
+
+ [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [RFC6733] Fajardo, V., Arkko, J., Loughney, J., and G. Zorn,
+ "Diameter Base Protocol", RFC 6733, October 2012.
+
+9.2. Informative References
+
+ [RFC4960] Stewart, R., "Stream Control Transmission Protocol",
+ RFC 4960, September 2007.
+
+Authors' Addresses
+
+ Jiao Kang
+ Huawei Technologies
+ Section F1, Huawei Industrial Base
+ Bantian, Longgang District
+ Shenzhen 518129
+ P.R. China
+
+
+
+ Glen Zorn
+ Network Zen
+ 227/358 Thanon Sanphawut
+ Bang Na, Bangkok 10260
+ Thailand
+
+ Phone: +66 (0) 909-201060
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Jiao & Zorn Standards Track [Page 6]
+
diff --git a/lib/diameter/examples/code/peer.erl b/lib/diameter/examples/code/peer.erl
index b07cd32b98..8fdeba57bf 100644
--- a/lib/diameter/examples/code/peer.erl
+++ b/lib/diameter/examples/code/peer.erl
@@ -117,6 +117,11 @@ server(T) ->
%%
%% Return config for a connecting transport.
+client({all, LA, RA, RP}) ->
+ [[M,{K,C}], T]
+ = [client({P, LA, RA, RP}) || P <- [sctp,tcp]],
+ [M, {K,C,2000} | T];
+
client({T, LA, RA, RP}) ->
[{transport_module, tmod(T)},
{transport_config, [{ip, addr(LA)},
diff --git a/lib/diameter/include/diameter.hrl b/lib/diameter/include/diameter.hrl
index 4273262015..beb577afaf 100644
--- a/lib/diameter/include/diameter.hrl
+++ b/lib/diameter/include/diameter.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -139,7 +139,6 @@
init_state, %% option 'state', initial callback state
id, %% 32-bit unsigned application identifier = Dict:id()
mutable = false, %% boolean(), do traffic callbacks modify state?
- answer_errors = report}). %% | callback | discard
- %% how to handle containing errors?
+ options = [{answer_errors, report}]}). %% | callback | discard
-endif. %% -ifdef(diameter_hrl).
diff --git a/lib/diameter/make/release_targets.mk b/lib/diameter/make/release_targets.mk
deleted file mode 100644
index 5a3b585cbc..0000000000
--- a/lib/diameter/make/release_targets.mk
+++ /dev/null
@@ -1,92 +0,0 @@
-#
-# %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%
-#
-
-ifeq ($(TOPDOC),)
-$(HTMLDIR)/index.html: $(XML_FILES)
- date=`date +"%B %e %Y"`; \
- $(XSLTPROC) --noout --stringparam outdir $(HTMLDIR) --stringparam docgen "$(DOCGEN)" --stringparam topdocdir "$(TOPDOCDIR)" \
- --stringparam pdfdir "$(PDFDIR)" \
- --stringparam gendate "$$date" --stringparam appname "$(APPLICATION)" --stringparam appver "$(VSN)" --xinclude \
- -path $(DOCGEN)/priv/docbuilder_dtd -path $(DOCGEN)/priv/dtd_html_entities $(DOCGEN)/priv/xsl/db_html.xsl book.xml
-endif
-
-$(HTMLDIR)/users_guide.html: $(XML_FILES)
- date=`date +"%B %e %Y"`; \
- $(XSLTPROC) --noout --stringparam outdir $(HTMLDIR) --stringparam docgen "$(DOCGEN)" --stringparam topdocdir "$(TOPDOCDIR)" \
- --stringparam pdfdir "$(PDFDIR)" \
- --stringparam gendate "$$date" --stringparam appname "$(APPLICATION)" --stringparam appver "$(VSN)" --xinclude \
- -path $(DOCGEN)/priv/docbuilder_dtd -path $(DOCGEN)/priv/dtd_html_entities $(DOCGEN)/priv/xsl/db_html.xsl book.xml
-
-
-%.fo: $(XML_FILES)
- date=`date +"%B %e %Y"`; \
- $(XSLTPROC) --stringparam docgen "$(DOCGEN)" --stringparam gendate "$$date" --stringparam appname "$(APPLICATION)" \
- --stringparam appver "$(VSN)" --xinclude \
- -path $(DOCGEN)/priv/docbuilder_dtd -path $(DOCGEN)/priv/dtd_html_entities $(DOCGEN)/priv/xsl/db_pdf.xsl book.xml > $@
-
-
-
-# ------------------------------------------------------------------------
-# The following targets just exist in the documentation directory
-# ------------------------------------------------------------------------
-ifneq ($(XML_FILES),)
-
-# ----------------------------------------------------
-# Generation of application index data
-# ----------------------------------------------------
-$(HTMLDIR)/$(APPLICATION).eix: $(XML_FILES)
- date=`date +"%B %e %Y"`; \
- $(XSLTPROC) --stringparam docgen "$(DOCGEN)" \
- --stringparam gendate "$$date" --stringparam appname "$(APPLICATION)" --stringparam appver "$(VSN)" --xinclude \
- -path $(DOCGEN)/priv/docbuilder_dtd -path $(DOCGEN)/priv/dtd_html_entities $(DOCGEN)/priv/xsl/db_eix.xsl book.xml > $@
-
-docs:
-#docs: $(HTMLDIR)/$(APPLICATION).eix
-
-# ----------------------------------------------------
-# Local documentation target for testing
-# ----------------------------------------------------
-local_docs: TOPDOCDIR=.
-local_docs: local_copy_of_topdefs docs
-
-local_html: TOPDOCDIR=.
-local_html: local_copy_of_topdefs html
-
-local_copy_of_topdefs:
- $(INSTALL) $(DOCGEN)/priv/css/otp_doc.css $(HTMLDIR)
- $(INSTALL) $(DOCGEN)/priv/images/erlang-logo.png $(HTMLDIR)
- $(INSTALL) $(DOCGEN)/priv/images/erlang-logo.gif $(HTMLDIR)
- $(INSTALL_DIR) $(HTMLDIR)/js/flipmenu
- $(INSTALL) $(DOCGEN)/priv/js/flipmenu/flip_closed.gif \
- $(DOCGEN)/priv/js/flipmenu/flip_open.gif \
- $(DOCGEN)/priv/js/flipmenu/flip_static.gif \
- $(DOCGEN)/priv/js/flipmenu/flipmenu.js $(HTMLDIR)/js/flipmenu
-
-endif
-
-# ----------------------------------------------------
-# Standard release target
-# ----------------------------------------------------
-
-ifneq ($(PREFIX),)
-
-release release_docs release_tests release_html:
- $(MAKE) $(MFLAGS) RELEASE_PATH=$(PREFIX) $(TARGET_MAKEFILE) $@_spec
-
-endif
diff --git a/lib/diameter/make/rules.mk.in b/lib/diameter/make/rules.mk.in
deleted file mode 100644
index cd3c297d75..0000000000
--- a/lib/diameter/make/rules.mk.in
+++ /dev/null
@@ -1,193 +0,0 @@
-#-*-makefile-*- ; force emacs to enter makefile-mode
-# ----------------------------------------------------
-# %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%
-
-.SUFFIXES: .erl .beam .yrl .hrl .xml .xmlsrc .html \
- .3 .1 .pdf .fo .el .elc
-
-# ----------------------------------------------------
-# Common macros
-# ----------------------------------------------------
-DEFAULT_TARGETS = opt debug release release_docs clean docs
-
-
-# Slash separated list of return values from $(origin VAR)
-# that are untrusted - set default in this file instead.
-# The list is not space separated since some return values
-# contain space, and we want to use $(findstring ...) to
-# search the list.
-DUBIOUS_ORIGINS = /undefined/environment/
-
-
-# # ----------------------------------------------------
-# # TARGET definition
-# # ----------------------------------------------------
-# # TARGET = @TARGET@
-# ifneq ($(OVERRIDE_TARGET),)
-# ifneq ($(TARGET), $(OVERRIDE_TARGET))
-# $(warning overriding $$(TARGET) = \
-# "$(TARGET)" \
-# with \
-# $$(OVERRIDE_TARGET) = \
-# "$(OVERRIDE_TARGET)")
-# override TARGET := $(OVERRIDE_TARGET)
-# endif
-# endif
-#
-
-# ----------------------------------------------------
-# Command macros
-# ----------------------------------------------------
-PREFIX = @prefix@
-INSTALL = @INSTALL@
-INSTALL_DIR = @INSTALL_DIR@
-INSTALL_PROGRAM = @INSTALL_PROGRAM@
-INSTALL_SCRIPT = @INSTALL_SCRIPT@
-INSTALL_DATA = @INSTALL_DATA@
-
-
-# ----------------------------------------------------
-# Erlang language section
-# ----------------------------------------------------
-ERL_ROOT_DIR = @ERLANG_ROOT_DIR@
-ERL_LIB_DIR = @ERLANG_LIB_DIR@
-DOCGEN_DIR = @ERLANG_LIB_DIR_erl_docgen@
-TEST_SERVER_DIR = @ERLANG_LIB_VER_test_server@
-EMULATOR = beam
-ERL_COMPILE_FLAGS += +debug_info
-ERLC_WFLAGS = -W
-ERLC = $(ERL_ROOT_DIR)/bin/erlc $(ERLC_WFLAGS) $(ERLC_FLAGS)
-ERL = $(ERL_ROOT_DIR)/bin/erl -boot start_clean
-#ERLC = @ERLC@ $(ERLC_WFLAGS) $(ERLC_FLAGS)
-#ERL = @ERL@ -boot start_clean
-
-ifneq (,$(findstring $(origin EBIN),$(DUBIOUS_ORIGINS)))
-EBIN = ../../ebin
-endif
-
-# Generated (non ebin) files...
-ifneq (,$(findstring $(origin EGEN),$(DUBIOUS_ORIGINS)))
-EGEN = .
-endif
-
-ifneq (,$(findstring $(origin ESRC),$(DUBIOUS_ORIGINS)))
-ESRC = .
-endif
-
-$(EBIN)/%.beam: $(EGEN)/%.erl
- $(ERLC) $(ERL_COMPILE_FLAGS) -o$(EBIN) $<
-
-$(EBIN)/%.beam: $(ESRC)/%.erl
- $(ERLC) $(ERL_COMPILE_FLAGS) -o$(EBIN) $<
-
-.erl.beam:
- $(ERLC) $(ERL_COMPILE_FLAGS) -o$(dir $@) $<
-
-
-#
-# When .erl files are automatically created GNU make removes them if
-# they were the result of a chain of implicit rules. To prevent this
-# we say that all .erl files are "precious".
-#
-.PRECIOUS: %.erl %.fo
-
-
-# ----------------------------------------------------
-# Documentation section
-# ----------------------------------------------------
-# export VSN
-
-# TOPDOCDIR=../../../../doc
-
-DOCDIR = ..
-
-PDFDIR=$(DOCDIR)/pdf
-
-HTMLDIR = $(DOCDIR)/html
-
-MAN1DIR = $(DOCDIR)/man1
-MAN2DIR = $(DOCDIR)/man2
-MAN3DIR = $(DOCDIR)/man3
-MAN4DIR = $(DOCDIR)/man4
-MAN6DIR = $(DOCDIR)/man6
-MAN9DIR = $(DOCDIR)/man9
-
-# HTML & GIF files that always are generated and must be delivered
-XML_COLL_FILES = $(XML_APPLICATION_FILES) $(XML_PART_FILES)
-DEFAULT_HTML_FILES = \
- $(XML_COLL_FILES:%.xml=$(HTMLDIR)/%_frame.html) \
- $(XML_COLL_FILES:%.xml=$(HTMLDIR)/%_first.html) \
- $(XML_COLL_FILES:%.xml=$(HTMLDIR)/%_term.html) \
- $(XML_COLL_FILES:%.xml=$(HTMLDIR)/%_cite.html) \
- $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%_index.html) \
- $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.kwc) \
- $(HTMLDIR)/index.html
-
-DEFAULT_GIF_FILES = $(HTMLDIR)/min_head.gif
-
-#
-# Flags & Commands
-#
-XSLTPROC = @XSLTPROC@
-FOP = @FOP@
-
-DOCGEN=$(DOCGEN_DIR)
-
-$(MAN1DIR)/%.1:: %.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 $<
-
-
-$(MAN2DIR)/%.2:: %.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 $<
-
-
-$(MAN3DIR)/%.3:: %.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 $<
-
-# left for compatibility
-$(MAN4DIR)/%.4:: %.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 $<
-
-$(MAN4DIR)/%.5:: %.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 $<
-
-# left for compatibility
-$(MAN6DIR)/%.6:: %_app.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 $<
-
-$(MAN6DIR)/%.7:: %_app.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 $<
-
-$(MAN9DIR)/%.9:: %.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 $<
-
-
-.xmlsrc.xml:
- escript $(DOCGEN)/priv/bin/codeline_preprocessing.escript $< $@
-
-.fo.pdf:
- $(FOP) -fo $< -pdf $@
-
diff --git a/lib/diameter/make/subdir.mk b/lib/diameter/make/subdir.mk
deleted file mode 100644
index 24b08080ae..0000000000
--- a/lib/diameter/make/subdir.mk
+++ /dev/null
@@ -1,53 +0,0 @@
-#
-# %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%
-#
-# Make include file for otp
-
-.PHONY: debug opt release docs release_docs tests release_tests \
- clean depend valgrind
-
-#
-# Targets that don't affect documentation directories
-#
-opt debug release docs release_docs tests release_tests clean depend valgrind:
- @set -e ; \
- app_pwd=`pwd` ; \
- if test -f vsn.mk; then \
- echo "=== Entering application" `basename $$app_pwd` ; \
- fi ; \
- case "$(MAKE)" in *clearmake*) tflag="-T";; *) tflag="";; esac; \
- for d in $(SUB_DIRS); do \
- if test -f $$d/SKIP ; then \
- echo "=== Skipping subdir $$d, reason:" ; \
- cat $$d/SKIP ; \
- echo "===" ; \
- else \
- if test ! -d $$d ; then \
- echo "=== Skipping subdir $$d, it is missing" ; \
- else \
- xflag="" ; \
- if test -f $$d/ignore_config_record.inf; then \
- xflag=$$tflag ; \
- fi ; \
- (cd $$d && $(MAKE) $$xflag $@) || exit $$? ; \
- fi ; \
- fi ; \
- done ; \
- if test -f vsn.mk; then \
- echo "=== Leaving application" `basename $$app_pwd` ; \
- fi
diff --git a/lib/diameter/make/target.mk b/lib/diameter/make/target.mk
deleted file mode 100644
index 4ae470b9e2..0000000000
--- a/lib/diameter/make/target.mk
+++ /dev/null
@@ -1,33 +0,0 @@
-ifeq ($(OVERRIDE_TARGET),)
-
-ifeq ($(TARGET),)
-
-TARGET := $(shell $(DIAMETER_TOP)/autoconf/config.guess)
-
-else
-
-endif
-
-else
-
-ifneq ($(TARGET),)
-
-ifneq ($(TARGET), $(OVERRIDE_TARGET))
-$(warning overriding $$(TARGET) = \
- "$(TARGET)" \
- with \
- $$(OVERRIDE_TARGET) = \
- "$(OVERRIDE_TARGET)")
-else
-endif
-
-override TARGET := $(OVERRIDE_TARGET)
-
-else
-
-TARGET := $(OVERRIDE_TARGET)
-
-endif
-
-endif
-
diff --git a/lib/diameter/src/.gitignore b/lib/diameter/src/.gitignore
index feeb378fd8..cc06720fd1 100644
--- a/lib/diameter/src/.gitignore
+++ b/lib/diameter/src/.gitignore
@@ -1,2 +1,3 @@
/depend.mk
+/otp.plt
diff --git a/lib/diameter/src/Makefile b/lib/diameter/src/Makefile
index de2eca0279..3cbcbf536e 100644
--- a/lib/diameter/src/Makefile
+++ b/lib/diameter/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2010-2011. All Rights Reserved.
+# Copyright Ericsson AB 2010-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -16,13 +16,8 @@
#
# %CopyrightEnd%
-ifeq ($(ERL_TOP),)
-include $(DIAMETER_TOP)/make/target.mk
-include $(DIAMETER_TOP)/make/$(TARGET)/rules.mk
-else
include $(ERL_TOP)/make/target.mk
include $(ERL_TOP)/make/$(TARGET)/otp.mk
-endif
# ----------------------------------------------------
# Application version
@@ -124,7 +119,7 @@ ERL_COMPILE_FLAGS += \
# erl/hrl from dictionary file.
gen/diameter_gen_%.erl gen/diameter_gen_%.hrl: dict/%.dia
- ../bin/diameterc -o gen -i $(EBIN) $<
+ $(dia_verbose)../bin/diameterc -o gen -i $(EBIN) $<
opt: $(TARGET_FILES)
@@ -133,17 +128,19 @@ debug:
# The dictionary parser.
gen/$(DICT_YRL).erl: compiler/$(DICT_YRL).yrl
- $(ERLC) -Werror -o $(@D) $<
+ $(yecc_verbose)$(ERLC) -Werror -o $(@D) $<
# Generate the app file.
$(APP_TARGET): $(APP_SRC) ../vsn.mk modules.mk
- M=`echo $(notdir $(APP_MODULES)) | tr ' ' ,`; \
+ $(gen_verbose)M=`echo $(notdir $(APP_MODULES)) | tr ' ' ,`; \
+ R=`echo $(REGISTERED) | tr ' ' ,`; \
sed -e 's;%VSN%;$(VSN);' \
-e "s;%MODULES%;$$M;" \
+ -e "s;%REGISTERED%;$$R;" \
$< > $@
$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
app: $(APP_TARGET) $(APPUP_TARGET)
dict: $(DICT_ERLS)
@@ -181,15 +178,36 @@ clean:
rm -f $(TARGET_FILES) gen/*
rm -f depend.mk
+realclean: clean
+ rm -f ../ebin/*
+# Not $(EBIN) just to be a bit paranoid
+
+PLT = ./otp.plt
+
+plt:
+ dialyzer --build_plt \
+ --apps erts stdlib kernel \
+ xmerl ssl public_key crypto \
+ compiler syntax_tools runtime_tools \
+ --output_plt $(PLT) \
+ --verbose
+
+dialyze: opt $(PLT)
+ dialyzer --plt $(PLT) \
+ --verbose \
+ -Wno_improper_lists \
+ $(EBIN)/diameter_gen_base_rfc3588.$(EMULATOR) \
+ $(patsubst %, $(EBIN)/%.$(EMULATOR), \
+ $(notdir $(RT_MODULES) $(CT_MODULES)))
+# Omit all but the common dictionary module since these
+# (diameter_gen_relay in particular) generate warning depending on how
+# much of the included diameter_gen.hrl they use.
+
# ----------------------------------------------------
# Release targets
# ----------------------------------------------------
-ifeq ($(ERL_TOP),)
-include $(DIAMETER_TOP)/make/release_targets.mk
-else
include $(ERL_TOP)/make/otp_release_targets.mk
-endif
# Can't $(INSTALL_DIR) more than one directory at a time on Solaris.
@@ -238,17 +256,18 @@ depend: depend.mk
# Generate dependencies makefile.
depend.mk: depend.sed $(MODULES:%=%.erl) Makefile
- (for f in $(MODULES); do \
+ $(gen_verbose)(for f in $(MODULES); do \
(echo $$f; cat $$f.erl) | sed -f $<; \
done) \
> $@
-include depend.mk
-.PHONY: app clean depend dict info release_subdir
+.PHONY: app clean realclean depend dict info release_subdir
.PHONY: debug opt release_docs_spec release_spec
.PHONY: $(TARGET_DIRS:%/=%) $(TARGET_DIRS:%/=release_src_%)
.PHONY: $(EXAMPLE_DIRS:%/=release_examples_%)
+.PHONY: plt dialyze
# Keep intermediate files.
.SECONDARY: $(DICT_ERLS) $(DICT_HRLS) gen/$(DICT_YRL:%=%.erl)
diff --git a/lib/diameter/src/base/diameter.app.src b/lib/diameter/src/base/diameter.app.src
index c092fdb022..7e17cd6c9f 100644
--- a/lib/diameter/src/base/diameter.app.src
+++ b/lib/diameter/src/base/diameter.app.src
@@ -21,7 +21,7 @@
[{description, "Diameter protocol"},
{vsn, "%VSN%"},
{modules, [%MODULES%]},
- {registered, []},
+ {registered, [%REGISTERED%]},
{applications, [stdlib, kernel]},
{env, []},
{mod, {diameter_app, []}}
diff --git a/lib/diameter/src/base/diameter.appup.src b/lib/diameter/src/base/diameter.appup.src
index 2ebdad598f..a04a387918 100644
--- a/lib/diameter/src/base/diameter.appup.src
+++ b/lib/diameter/src/base/diameter.appup.src
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -20,15 +20,21 @@
{"%VSN%",
[
- {"0.9", [{restart_application, diameter}]},
- {"0.10", [{restart_application, diameter}]},
- {"1.0", [{update, diameter_service},
- {update, diameter_watchdog}]}
+ {"0.9", [{restart_application, diameter}]},
+ {"0.10", [{restart_application, diameter}]},
+ {"1.0", [{restart_application, diameter}]},
+ {"1.1", [{restart_application, diameter}]},
+ {"1.2", [{restart_application, diameter}]},
+ {"1.2.1", [{restart_application, diameter}]},
+ {"1.3", [{load_module, diameter_service}]}
],
[
- {"0.9", [{restart_application, diameter}]},
- {"0.10", [{restart_application, diameter}]},
- {"1.0", [{update, diameter_watchdog},
- {update, diameter_service}]}
+ {"0.9", [{restart_application, diameter}]},
+ {"0.10", [{restart_application, diameter}]},
+ {"1.0", [{restart_application, diameter}]},
+ {"1.1", [{restart_application, diameter}]},
+ {"1.2", [{restart_application, diameter}]},
+ {"1.2.1", [{restart_application, diameter}]},
+ {"1.3", [{load_module, diameter_service}]}
]
}.
diff --git a/lib/diameter/src/base/diameter.erl b/lib/diameter/src/base/diameter.erl
index 336f0c1f2d..8f9901907a 100644
--- a/lib/diameter/src/base/diameter.erl
+++ b/lib/diameter/src/base/diameter.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -44,6 +44,8 @@
stop/0]).
-export_type([evaluable/0,
+ restriction/0,
+ sequence/0,
app_alias/0,
service_name/0,
capability/0,
@@ -280,11 +282,23 @@ call(SvcName, App, Message) ->
| fun()
| maybe_improper_list(evaluable(), list()).
+-type sequence()
+ :: {'Unsigned32'(), 0..32}.
+
+-type restriction()
+ :: false
+ | node
+ | nodes
+ | [node()]
+ | evaluable().
+
%% Options passed to start_service/2
-type service_opt()
:: capability()
- | {application, [application_opt()]}.
+ | {application, [application_opt()]}
+ | {restrict_connections, restriction()}
+ | {sequence, sequence() | evaluable()}.
-type application_opt()
:: {alias, app_alias()}
@@ -312,9 +326,12 @@ call(SvcName, App, Message) ->
-type transport_opt()
:: {transport_module, atom()}
| {transport_config, any()}
+ | {transport_config, any(), non_neg_integer() | infinity}
| {applications, [app_alias()]}
| {capabilities, [capability()]}
| {capabilities_cb, evaluable()}
+ | {capx_timeout, 'Unsigned32'()}
+ | {disconnect_cb, evaluable()}
| {watchdog_timer, 'Unsigned32'() | {module(), atom(), list()}}
| {reconnect_timer, 'Unsigned32'()}
| {private, any()}.
@@ -322,10 +339,11 @@ call(SvcName, App, Message) ->
%% Predicate passed to remove_transport/2
-type transport_pred()
- :: fun((reference(), connect|listen, list()) -> boolean())
- | fun((reference(), list()) -> boolean())
+ :: fun((transport_ref(), connect|listen, list()) -> boolean())
+ | fun((transport_ref(), list()) -> boolean())
| fun((list()) -> boolean())
- | reference()
+ | transport_ref()
+ | boolean()
| list()
| {connect|listen, transport_pred()}
| {atom(), atom(), list()}.
diff --git a/lib/diameter/src/base/diameter_capx.erl b/lib/diameter/src/base/diameter_capx.erl
index 6c4d60ee9b..c6c3d2934d 100644
--- a/lib/diameter/src/base/diameter_capx.erl
+++ b/lib/diameter/src/base/diameter_capx.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -141,7 +141,9 @@ cap('Host-IP-Address', Vs)
when is_list(Vs) ->
lists:map(fun ipaddr/1, Vs);
-cap('Firmware-Revision', V) ->
+cap(K, V)
+ when K == 'Firmware-Revision';
+ K == 'Origin-State-Id' ->
[V];
cap(_, Vs)
@@ -149,7 +151,7 @@ cap(_, Vs)
Vs;
cap(K, V) ->
- ?THROW({invalid, K, V}).
+ ?THROW({invalid, {K,V}}).
ipaddr(A) ->
try
diff --git a/lib/diameter/src/base/diameter_codec.erl b/lib/diameter/src/base/diameter_codec.erl
index fe1212b7e0..0b0bfe3f0a 100644
--- a/lib/diameter/src/base/diameter_codec.erl
+++ b/lib/diameter/src/base/diameter_codec.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -63,9 +63,9 @@ encode(Mod, #diameter_packet{} = Pkt) ->
e(Mod, Pkt)
catch
error: Reason ->
- %% Be verbose rather than letting the emulator truncate the
- %% error report.
- X = {Reason, ?STACK},
+ %% Be verbose since a crash report may be truncated and
+ %% encode errors are self-inflicted.
+ X = {?MODULE, encode, {Reason, ?STACK}},
diameter_lib:error_report(X, {?MODULE, encode, [Mod, Pkt]}),
exit(X)
end;
@@ -91,7 +91,8 @@ e(_, #diameter_packet{msg = [#diameter_header{} = Hdr | As]} = Pkt) ->
Flags = make_flags(0, Hdr),
- Pkt#diameter_packet{bin = <<Vsn:8, Length:24,
+ Pkt#diameter_packet{header = Hdr,
+ bin = <<Vsn:8, Length:24,
Flags:8, Code:24,
Aid:32,
Hid:32,
@@ -192,9 +193,11 @@ encode_avps(Avps) ->
msg_header(Mod, 'answer-message' = MsgName, Header) ->
?BASE = Mod,
- #diameter_header{cmd_code = Code} = Header,
- {_, Flags, ApplId} = ?BASE:msg_header(MsgName),
- {Code, Flags, ApplId};
+ #diameter_header{application_id = Aid,
+ cmd_code = Code}
+ = Header,
+ {-1, Flags, ?DIAMETER_APP_ID_COMMON} = ?BASE:msg_header(MsgName),
+ {Code, Flags, Aid};
msg_header(Mod, MsgName, _) ->
Mod:msg_header(MsgName).
@@ -332,6 +335,9 @@ decode_header(_) ->
%% wraparound counter. The 8-bit counter is incremented each time the
%% system is restarted.
+sequence_numbers({_,_} = T) ->
+ T;
+
sequence_numbers(#diameter_packet{bin = Bin})
when is_binary(Bin) ->
sequence_numbers(Bin);
diff --git a/lib/diameter/src/base/diameter_config.erl b/lib/diameter/src/base/diameter_config.erl
index 9253af0de2..63d28f25a2 100644
--- a/lib/diameter/src/base/diameter_config.erl
+++ b/lib/diameter/src/base/diameter_config.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -97,6 +97,9 @@
-record(monitor, {mref = make_ref() :: reference(),
service}). %% name
+%% The default sequence mask.
+-define(NOMASK, {0,32}).
+
%% Time to lay low before restarting a dead service.
-define(RESTART_SLEEP, 2000).
@@ -519,6 +522,7 @@ rm(SvcName, L) ->
Refs = lists:map(fun(#transport{ref = R}) -> R end, L),
case stop_transport(SvcName, Refs) of
ok ->
+ diameter_stats:flush(Refs),
lists:foreach(fun delete_object/1, L);
{error, _} = No ->
No
@@ -548,9 +552,11 @@ make_config(SvcName, Opts) ->
ok = encode_CER(COpts),
- Os = split(Opts, [{[fun erlang:is_boolean/1], false, share_peers},
- {[fun erlang:is_boolean/1], false, use_shared_peers},
- {[fun erlang:is_pid/1, false], false, monitor}]),
+ Os = split(Opts, fun opt/2, [{false, share_peers},
+ {false, use_shared_peers},
+ {false, monitor},
+ {?NOMASK, sequence},
+ {nodes, restrict_connections}]),
%% share_peers and use_shared_peers are currently undocumented.
#service{name = SvcName,
@@ -558,11 +564,66 @@ make_config(SvcName, Opts) ->
capabilities = Caps},
options = Os}.
+split(Opts, F, Defs) ->
+ [{K, F(K, get_opt(K, Opts, D))} || {D,K} <- Defs].
+
+opt(K, false = B)
+ when K /= sequence ->
+ B;
+
+opt(K, true = B)
+ when K == share_peer;
+ K == use_shared_peers ->
+ B;
+
+opt(monitor, P)
+ when is_pid(P) ->
+ P;
+
+opt(restrict_connections, T)
+ when T == node;
+ T == nodes;
+ T == [];
+ is_atom(hd(T)) ->
+ T;
+
+opt(restrict_connections = K, F) ->
+ try diameter_lib:eval(F) of %% no guarantee that it won't fail later
+ Nodes when is_list(Nodes) ->
+ F;
+ V ->
+ ?THROW({value, {K,V}})
+ catch
+ E:R ->
+ ?THROW({value, {K, E, R, ?STACK}})
+ end;
+
+opt(sequence, {_,_} = T) ->
+ sequence(T);
+
+opt(sequence = K, F) ->
+ try diameter_lib:eval(F) of
+ T -> sequence(T)
+ catch
+ E:R ->
+ ?THROW({value, {K, E, R, ?STACK}})
+ end;
+
+opt(K, _) ->
+ ?THROW({value, K}).
+
+sequence({H,N} = T)
+ when 0 =< N, N =< 32, 0 =< H, 0 == H bsr N ->
+ T;
+
+sequence(_) ->
+ ?THROW({value, sequence}).
+
make_caps(Caps, Opts) ->
case diameter_capx:make_caps(Caps, Opts) of
{ok, T} ->
T;
- {error, {Reason, _}} ->
+ {error, Reason} ->
?THROW(Reason)
end.
@@ -600,7 +661,7 @@ app_acc({application, Opts}, Acc) ->
module = init_mod(Mod),
init_state = ModS,
mutable = init_mutable(M),
- answer_errors = init_answers(A)}
+ options = [{answer_errors, init_answers(A)}]}
| Acc];
app_acc(_, Acc) ->
Acc.
@@ -662,21 +723,6 @@ get_opt(Key, List, Def) ->
_ -> ?THROW({arity, Key})
end.
-split(Opts, Defs) ->
- [{K, value(D, Opts)} || {_,_,K} = D <- Defs].
-
-value({Preds, Def, Key}, Opts) ->
- V = get_opt(Key, Opts, Def),
- lists:any(fun(P) -> pred(P,V) end, Preds)
- orelse ?THROW({value, Key}),
- V.
-
-pred(F, V)
- when is_function(F) ->
- F(V);
-pred(T, V) ->
- T == V.
-
cb(M,F) ->
try M:F() of
V -> V
diff --git a/lib/diameter/src/base/diameter_peer.erl b/lib/diameter/src/base/diameter_peer.erl
index 3e78c4caef..1b2f32ddff 100644
--- a/lib/diameter/src/base/diameter_peer.erl
+++ b/lib/diameter/src/base/diameter_peer.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -27,12 +27,15 @@
up/2]).
%% ... and the stack.
--export([start/3,
+-export([start/1,
send/2,
close/1,
abort/1,
notify/2]).
+%% Old interface only called from old code.
+-export([start/3]). %% < diameter-1.2 (R15B02)
+
%% Server start.
-export([start_link/0]).
@@ -57,6 +60,11 @@
%% Server state.
-record(state, {id = now()}).
+%% Default transport_module/config.
+-define(DEFAULT_TMOD, diameter_tcp).
+-define(DEFAULT_TCFG, []).
+-define(DEFAULT_TTMO, infinity).
+
%%% ---------------------------------------------------------------------------
%%% # notify/2
%%% ---------------------------------------------------------------------------
@@ -68,9 +76,119 @@ notify(SvcName, T) ->
%%% # start/3
%%% ---------------------------------------------------------------------------
-start(T, Opts, #diameter_service{} = Svc) ->
- {Mod, Cfg} = split_transport(Opts),
- apply(Mod, start, [T, Svc, Cfg]).
+%% From old code: make it restart.
+start(_T, _Opts, #diameter_service{}) ->
+ {error, restart}.
+
+%%% ---------------------------------------------------------------------------
+%%% # start/1
+%%% ---------------------------------------------------------------------------
+
+-spec start({T, [Opt], #diameter_service{}})
+ -> {TPid, [Addr], Tmo, Data}
+ | {error, [term()]}
+ when T :: {connect|accept, diameter:transport_ref()},
+ Opt :: diameter:transport_opt(),
+ TPid :: pid(),
+ Addr :: inet:ip_address(),
+ Tmo :: non_neg_integer() | infinity,
+ Data :: {{T, Mod, Cfg}, [Mod], [{T, [Mod], Cfg}], [Err]},
+ Mod :: module(),
+ Cfg :: term(),
+ Err :: term()
+ ; ({#diameter_service{}, Tmo, Data})
+ -> {TPid, [Addr], Tmo, Data}
+ | {error, [term()]}
+ when TPid :: pid(),
+ Addr :: inet:ip_address(),
+ Tmo :: non_neg_integer() | infinity,
+ Data :: {{T, Mod, Cfg}, [Mod], [{T, [Mod], Cfg}], [Err]},
+ T :: {connect|accept, diameter:transport_ref()},
+ Mod :: module(),
+ Cfg :: term(),
+ Err :: term().
+
+%% Initial start.
+start({T, Opts, #diameter_service{} = Svc}) ->
+ start(T, Svc, pair(Opts, [], []), []);
+
+%% Subsequent start.
+start({#diameter_service{} = Svc, Tmo, {{T, _, Cfg}, Ms, Rest, Errs}}) ->
+ start(T, Ms, Cfg, Svc, Tmo, Rest, Errs).
+
+%% pair/3
+%%
+%% Pair transport modules with config.
+
+%% Another transport_module: accumulate it.
+pair([{transport_module, M} | Rest], Mods, Acc) ->
+ pair(Rest, [M|Mods], Acc);
+
+%% Another transport_config: accumulate another tuple.
+pair([{transport_config = T, C} | Rest], Mods, Acc) ->
+ pair([{T, C, ?DEFAULT_TTMO} | Rest], Mods, Acc);
+pair([{transport_config, C, Tmo} | Rest], Mods, Acc) ->
+ pair(Rest, [], acc({Mods, C, Tmo}, Acc));
+
+pair([_ | Rest], Mods, Acc) ->
+ pair(Rest, Mods, Acc);
+
+%% No transport_module or transport_config: defaults.
+pair([], [], []) ->
+ [{[?DEFAULT_TMOD], ?DEFAULT_TCFG, ?DEFAULT_TTMO}];
+
+%% One transport_module, one transport_config.
+pair([], [M], [{[], Cfg, Tmo}]) ->
+ [{[M], Cfg, Tmo}];
+
+%% Trailing transport_module: default transport_config.
+pair([], [_|_] = Mods, Acc) ->
+ lists:reverse(acc({Mods, ?DEFAULT_TCFG, ?DEFAULT_TTMO}, Acc));
+
+pair([], [], Acc) ->
+ lists:reverse(def(Acc)).
+
+%% acc/2
+
+acc(T, Acc) ->
+ [T | def(Acc)].
+
+%% def/1
+%%
+%% Default module of previous pair if none were specified.
+
+def([{[], Cfg, Tmo} | Acc]) ->
+ [{[?DEFAULT_TMOD], Cfg, Tmo} | Acc];
+def(Acc) ->
+ Acc.
+
+%% start/4
+
+start(T, Svc, [{Ms, Cfg, Tmo} | Rest], Errs) ->
+ start(T, Ms, Cfg, Svc, Tmo, Rest, Errs);
+
+start(_, _, [], Errs) ->
+ {error, Errs}.
+
+%% start/7
+
+start(T, [], _, Svc, _, Rest, Errs) ->
+ start(T, Svc, Rest, Errs);
+
+start(T, [M|Ms], Cfg, Svc, Tmo, Rest, Errs) ->
+ case start(M, [T, Svc, Cfg]) of
+ {ok, TPid} ->
+ {TPid, [], Tmo, {{T, M, Cfg}, Ms, Rest, Errs}};
+ {ok, TPid, [_|_] = Addrs} ->
+ {TPid, Addrs, Tmo, {{T, M, Cfg}, Ms, Rest, Errs}};
+ E ->
+ start(T, Ms, Cfg, Svc, Tmo, Rest, [E|Errs])
+ end.
+
+%% start/2
+
+start(Mod, Args) ->
+ apply(Mod, start, Args).
%%% ---------------------------------------------------------------------------
%%% # up/[12]
@@ -204,21 +322,6 @@ bang(undefined = No, _) ->
bang(Pid, T) ->
Pid ! T.
-%% split_transport/1
-%%
-%% Split options into transport module, transport config and
-%% remaining options.
-
-split_transport(Opts) ->
- {[M,C], _} = proplists:split(Opts, [transport_module,
- transport_config]),
- {value(M, diameter_tcp), value(C, [])}.
-
-value([{_,V}], _) ->
- V;
-value([], V) ->
- V.
-
%% call/1
call(Request) ->
diff --git a/lib/diameter/src/base/diameter_peer_fsm.erl b/lib/diameter/src/base/diameter_peer_fsm.erl
index 99644814d2..858870566f 100644
--- a/lib/diameter/src/base/diameter_peer_fsm.erl
+++ b/lib/diameter/src/base/diameter_peer_fsm.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -48,37 +48,64 @@
-include("diameter_internal.hrl").
-include("diameter_gen_base_rfc3588.hrl").
+%% Values of Disconnect-Cause in DPR.
-define(GOAWAY, ?'DIAMETER_BASE_DISCONNECT-CAUSE_DO_NOT_WANT_TO_TALK_TO_YOU').
-define(REBOOT, ?'DIAMETER_BASE_DISCONNECT-CAUSE_REBOOTING').
+-define(BUSY, ?'DIAMETER_BASE_DISCONNECT-CAUSE_BUSY').
-define(NO_INBAND_SECURITY, 0).
-define(TLS, 1).
+%% Keys in process dictionary.
+-define(CB_KEY, cb). %% capabilities callback
+-define(DPR_KEY, dpr). %% disconnect callback
+-define(DWA_KEY, dwa). %% outgoing DWA
+-define(REF_KEY, ref). %% transport_ref()
+-define(Q_KEY, q). %% transport start queue
+-define(START_KEY, start). %% start of connected transport
+-define(SEQUENCE_KEY, mask). %% mask for sequence numbers
+-define(RESTRICT_KEY, restrict). %% nodes for connection check
+
+%% The default sequence mask.
+-define(NOMASK, {0,32}).
+
%% A 2xxx series Result-Code. Not necessarily 2001.
-define(IS_SUCCESS(N), 2 == (N) div 1000).
+%% Guards.
+-define(IS_UINT32(N), (is_integer(N) andalso 0 =< N andalso 0 == N bsr 32)).
+-define(IS_TIMEOUT(N), ?IS_UINT32(N)).
+-define(IS_CAUSE(N), N == ?REBOOT; N == rebooting;
+ N == ?GOAWAY; N == goaway;
+ N == ?BUSY; N == busy).
+
%% RFC 3588:
%%
%% Timeout An application-defined timer has expired while waiting
%% for some event.
%%
-define(EVENT_TIMEOUT, 10000).
+%% Default timeout for reception of CER/CEA.
-%% How long to wait for a DPA in response to DPR before simply
-%% aborting. Used to distinguish between shutdown and not but there's
-%% not really any need. Stopping a service will require a timeout if
-%% the peer doesn't answer DPR so the value should be short-ish.
+%% Default timeout for DPA in response to DPR. A bit short but the
+%% timeout used to be hardcoded. (So it could be worse.)
-define(DPA_TIMEOUT, 1000).
+-type uint32() :: diameter:'Unsigned32'().
+
-record(state,
- {state = 'Wait-Conn-Ack' %% state of RFC 3588 Peer State Machine
- :: 'Wait-Conn-Ack' | recv_CER | 'Wait-CEA' | 'Open',
+ {state %% of RFC 3588 Peer State Machine
+ :: 'Wait-Conn-Ack' %% old code
+ | {'Wait-Conn-Ack', uint32()}
+ | recv_CER
+ | 'Wait-CEA' %% old code
+ | {'Wait-CEA', uint32(), uint32()}
+ | 'Open',
mode :: accept | connect | {connect, reference()},
- parent :: pid(),
- transport :: pid(),
+ parent :: pid(), %% watchdog process
+ transport :: pid(), %% transport process
service :: #diameter_service{},
- dpr = false :: false | {diameter:'Unsigned32'(),
- diameter:'Unsigned32'()}}).
+ dpr = false :: false | {uint32(), uint32()}}).
%% | hop by hop and end to end identifiers
%% There are non-3588 states possible as a consequence of 5.6.1 of the
@@ -115,16 +142,21 @@
%%% Output: Pid
%%% ---------------------------------------------------------------------------
+-spec start(T, [Opt], #diameter_service{} %% from old code
+ | {diameter:sequence(),
+ diameter:restriction(),
+ #diameter_service{}})
+ -> pid()
+ when T :: {connect|accept, diameter:transport_ref()},
+ Opt :: diameter:transport_opt().
+
%% diameter_config requires a non-empty list of applications on the
%% service but diameter_service then constrains the list to any
%% specified on the transport in question. Check here that the list is
%% still non-empty.
-start({_, Ref} = Type, Opts, #diameter_service{applications = Apps} = Svc) ->
- [] /= Apps orelse ?ERROR({no_apps, Type, Opts}),
- T = {self(), Type, Opts, Svc},
- {ok, Pid} = diameter_peer_fsm_sup:start_child(T),
- diameter_stats:reg(Pid, Ref),
+start({_,_} = Type, Opts, MS) ->
+ {ok, Pid} = diameter_peer_fsm_sup:start_child({self(), Type, Opts, MS}),
Pid.
start_link(T) ->
@@ -143,18 +175,31 @@ init(T) ->
proc_lib:init_ack({ok, self()}),
gen_server:enter_loop(?MODULE, [], i(T)).
-i({WPid, T, Opts, #diameter_service{capabilities = Caps} = Svc0}) ->
- putr(dwa, dwa(Caps)),
+i({WPid, Type, Opts, #diameter_service{} = Svc}) -> %% from old code
+ i({WPid, Type, Opts, {?NOMASK, [node() | nodes()], Svc}});
+
+i({WPid, T, Opts, {Mask, Nodes, #diameter_service{applications = Apps,
+ capabilities = LCaps}
+ = Svc}}) ->
+ [] /= Apps orelse ?ERROR({no_apps, T, Opts}),
+ putr(?DWA_KEY, dwa(LCaps)),
{M, Ref} = T,
- {[Ts], Rest} = proplists:split(Opts, [capabilities_cb]),
- putr(capabilities_cb, {Ref, [F || {_,F} <- Ts]}),
- {ok, TPid, Svc} = start_transport(T, Rest, Svc0),
- erlang:monitor(process, TPid),
+ diameter_stats:reg(Ref),
+ {[Cs,Ds], Rest} = proplists:split(Opts, [capabilities_cb, disconnect_cb]),
+ putr(?CB_KEY, {Ref, [F || {_,F} <- Cs]}),
+ putr(?DPR_KEY, [F || {_, F} <- Ds]),
+ putr(?REF_KEY, Ref),
+ putr(?SEQUENCE_KEY, Mask),
+ putr(?RESTRICT_KEY, Nodes),
erlang:monitor(process, WPid),
- #state{parent = WPid,
+ {TPid, Addrs} = start_transport(T, Rest, Svc),
+ Tmo = proplists:get_value(capx_timeout, Opts, ?EVENT_TIMEOUT),
+ ?IS_TIMEOUT(Tmo) orelse ?ERROR({invalid, {capx_timeout, Tmo}}),
+ #state{state = {'Wait-Conn-Ack', Tmo},
+ parent = WPid,
transport = TPid,
mode = M,
- service = Svc}.
+ service = svc(Svc, Addrs)}.
%% The transport returns its local ip addresses so that different
%% transports on the same service can use different local addresses.
%% The local addresses are put into Host-IP-Address avps here when
@@ -164,18 +209,56 @@ i({WPid, T, Opts, #diameter_service{capabilities = Caps} = Svc0}) ->
%% watchdog start (start/2) succeeds regardless so as not to crash the
%% service.
-start_transport(T, Opts, Svc) ->
- case diameter_peer:start(T, Opts, Svc) of
- {ok, TPid} ->
- {ok, TPid, Svc};
- {ok, TPid, [_|_] = Addrs} ->
- #diameter_service{capabilities = Caps0} = Svc,
- Caps = Caps0#diameter_caps{host_ip_address = Addrs},
- {ok, TPid, Svc#diameter_service{capabilities = Caps}};
+start_transport(T, Opts, #diameter_service{capabilities = LCaps} = Svc) ->
+ Addrs0 = LCaps#diameter_caps.host_ip_address,
+ start_transport(Addrs0, {T, Opts, Svc}).
+
+start_transport(Addrs0, T) ->
+ case diameter_peer:start(T) of
+ {TPid, Addrs, Tmo, Data} ->
+ erlang:monitor(process, TPid),
+ q_next(TPid, Addrs0, Tmo, Data),
+ {TPid, addrs(Addrs, Addrs0)};
No ->
exit({shutdown, No})
end.
+addrs([], Addrs0) ->
+ Addrs0;
+addrs(Addrs, _) ->
+ Addrs.
+
+svc(Svc, []) ->
+ Svc;
+svc(Svc, Addrs) ->
+ readdr(Svc, Addrs).
+
+readdr(#diameter_service{capabilities = LCaps0} = Svc, Addrs) ->
+ LCaps = LCaps0#diameter_caps{host_ip_address = Addrs},
+ Svc#diameter_service{capabilities = LCaps}.
+
+%% The 4-tuple Data returned from diameter_peer:start/1 identifies the
+%% transport module/config use to start the transport process in
+%% question as well as any alternates to try if a connection isn't
+%% established within Tmo.
+q_next(TPid, Addrs0, Tmo, {_,_,_,_} = Data) ->
+ send_after(Tmo, {connection_timeout, TPid}),
+ putr(?Q_KEY, {Addrs0, Tmo, Data}).
+
+%% Connection has been established: retain the started
+%% pid/module/config in the process dictionary. This is a part of the
+%% interface defined by this module, so that the transport pid can be
+%% found when constructing service_info (in order to extract further
+%% information from it).
+keep_transport(TPid) ->
+ {_, _, {{_,_,_} = T, _, _, _}} = eraser(?Q_KEY),
+ putr(?START_KEY, {TPid, T}).
+
+send_after(infinity, _) ->
+ ok;
+send_after(Tmo, T) ->
+ erlang:send_after(Tmo, self(), T).
+
%% handle_call/3
handle_call(_, _, State) ->
@@ -202,14 +285,27 @@ handle_info(T, #state{} = State) ->
?LOG(stop, T),
x(T, State)
catch
+ exit: {diameter_codec, encode, _} = Reason ->
+ close_wd(Reason, State#state.parent),
+ ?LOG(stop, Reason),
+ %% diameter_codec:encode/2 emits an error report. Only
+ %% indicate the probable reason here.
+ diameter_lib:info_report(probable_configuration_error,
+ insufficient_capabilities),
+ {stop, {shutdown, Reason}, State};
{?MODULE, Tag, Reason} ->
?LOG(Tag, {Reason, T}),
{stop, {shutdown, Reason}, State}
end.
-%% The form of the exception caught here is historical. It's
+%% The form of the throw caught here is historical. It's
%% significant that it's not a 2-tuple, as in ?FAILURE(Reason),
%% since these are caught elsewhere.
+%% Note that there's no guarantee that the service and transport
+%% capabilities are good enough to build a CER/CEA that can be
+%% succesfully encoded. It's not checked at diameter:add_transport/2
+%% since this can be called before creating the service.
+
x(Reason, #state{} = S) ->
close_wd(Reason, S),
{stop, {shutdown, Reason}, S}.
@@ -238,34 +334,62 @@ eraser(Key) ->
%% transition/2
+%% Started in old code.
+transition(T, #state{state = 'Wait-Conn-Ack' = PS} = S) ->
+ transition(T, S#state{state = {PS, ?EVENT_TIMEOUT}});
+
%% Connection to peer.
transition({diameter, {TPid, connected, Remote}},
- #state{state = PS,
+ #state{transport = TPid,
+ state = PS,
mode = M}
= S) ->
- 'Wait-Conn-Ack' = PS, %% assert
+ {'Wait-Conn-Ack', _} = PS, %% assert
connect = M, %%
- send_CER(S#state{mode = {M, Remote},
- transport = TPid});
+ keep_transport(TPid),
+ send_CER(S#state{mode = {M, Remote}});
%% Connection from peer.
transition({diameter, {TPid, connected}},
- #state{state = PS,
+ #state{transport = TPid,
+ state = PS,
mode = M,
parent = Pid}
= S) ->
- 'Wait-Conn-Ack' = PS, %% assert
+ {'Wait-Conn-Ack', Tmo} = PS, %% assert
accept = M, %%
+ keep_transport(TPid),
Pid ! {accepted, self()},
- start_timer(S#state{state = recv_CER,
- transport = TPid});
+ start_timer(Tmo, S#state{state = recv_CER});
+
+%% Connection established after receiving a connection_timeout
+%% message. This may be followed by an incoming message which arrived
+%% before the transport was killed and this can't be distinguished
+%% from one from the transport that's been started to replace it.
+transition({diameter, {_, connected}}, _) ->
+ {stop, connection_timeout};
+transition({diameter, {_, connected, _}}, _) ->
+ {stop, connection_timeout};
+
+%% Connection has timed out: start an alternate.
+transition({connection_timeout = T, TPid},
+ #state{transport = TPid,
+ state = {'Wait-Conn-Ack', _}}
+ = S) ->
+ exit(TPid, {shutdown, T}),
+ start_next(S);
+
+%% Connect timeout after connection or alternate start: ignore.
+transition({connection_timeout, _}, _) ->
+ ok;
%% Incoming message from the transport.
transition({diameter, {recv, Pkt}}, S) ->
recv(Pkt, S);
%% Timeout when still in the same state ...
-transition({timeout, PS}, #state{state = PS}) ->
+transition({timeout = T, PS}, #state{state = PS} = S) ->
+ close({capx(PS), T}, S),
stop;
%% ... or not.
@@ -277,25 +401,19 @@ transition({send, Msg}, #state{transport = TPid}) ->
send(TPid, Msg),
ok;
-%% Request for graceful shutdown.
-transition({shutdown, Pid}, #state{parent = Pid, dpr = false} = S) ->
- dpr(?GOAWAY, S);
-transition({shutdown, Pid}, #state{parent = Pid}) ->
+%% Messages from old (diameter_service) code.
+transition(shutdown = T, #state{parent = Pid} = S) ->
+ transition({T, Pid, service}, S); %% Reason irrelevant: old code has no cb
+
+%% Request for graceful shutdown at remove_transport, stop_service of
+%% application shutdown.
+transition({shutdown = T, Pid}, S) ->
+ transition({T, Pid, transport}, S);
+transition({shutdown, Pid, Reason}, #state{parent = Pid, dpr = false} = S) ->
+ dpr(Reason, S);
+transition({shutdown, Pid, _}, #state{parent = Pid}) ->
ok;
-%% Application shutdown.
-transition(shutdown, #state{dpr = false} = S) ->
- dpr(?REBOOT, S);
-transition(shutdown, _) -> %% DPR already send: ensure expected timeout
- dpa_timer(),
- ok;
-
-%% Request to close the transport connection.
-transition({close = T, Pid}, #state{parent = Pid,
- transport = TPid}) ->
- diameter_peer:close(TPid),
- {stop, T};
-
%% DPA reception has timed out.
transition(dpa_timeout, _) ->
stop;
@@ -305,14 +423,21 @@ transition({resolve_port, _Pid} = T, #state{transport = TPid}) ->
TPid ! T,
ok;
-%% Parent or transport has died.
-transition({'DOWN', _, process, P, _},
- #state{parent = Pid,
- transport = TPid})
- when P == Pid;
- P == TPid ->
+%% Parent has died.
+transition({'DOWN', _, process, WPid, _},
+ #state{parent = WPid}) ->
stop;
+%% Transport has died before connection timeout.
+transition({'DOWN', _, process, TPid, _},
+ #state{transport = TPid}
+ = S) ->
+ start_next(S);
+
+%% Transport has died after connection timeout.
+transition({'DOWN', _, process, _, _}, _) ->
+ ok;
+
%% State query.
transition({state, Pid}, #state{state = S, transport = TPid}) ->
Pid ! {self(), [S, TPid]},
@@ -320,20 +445,43 @@ transition({state, Pid}, #state{state = S, transport = TPid}) ->
%% Crash on anything unexpected.
+capx(recv_CER) ->
+ 'CER';
+capx({'Wait-CEA', _, _}) ->
+ 'CEA'.
+
+%% start_next/1
+
+start_next(#state{service = Svc0} = S) ->
+ case getr(?Q_KEY) of
+ {Addrs0, Tmo, Data} ->
+ Svc = readdr(Svc0, Addrs0),
+ {TPid, Addrs} = start_transport(Addrs0, {Svc, Tmo, Data}),
+ S#state{transport = TPid,
+ service = svc(Svc, Addrs)};
+ undefined ->
+ stop
+ end.
+
%% send_CER/1
-send_CER(#state{mode = {connect, Remote},
- service = #diameter_service{capabilities = Caps},
+send_CER(#state{state = {'Wait-Conn-Ack', Tmo},
+ mode = {connect, Remote},
+ service = #diameter_service{capabilities = LCaps},
transport = TPid}
= S) ->
- OH = Caps#diameter_caps.origin_host,
+ OH = LCaps#diameter_caps.origin_host,
req_send_CER(OH, Remote)
orelse
- close({already_connected, Remote, Caps}, S),
+ close({already_connected, Remote, LCaps}, S),
CER = build_CER(S),
?LOG(send, 'CER'),
- send(TPid, encode(CER)),
- start_timer(S#state{state = 'Wait-CEA'}).
+ #diameter_packet{header = #diameter_header{end_to_end_id = Eid,
+ hop_by_hop_id = Hid}}
+ = Pkt
+ = encode(CER),
+ send(TPid, Pkt),
+ start_timer(Tmo, S#state{state = {'Wait-CEA', Hid, Eid}}).
%% Register ourselves as connecting to the remote endpoint in
%% question. This isn't strictly necessary since a peer implementing
@@ -345,23 +493,36 @@ send_CER(#state{mode = {connect, Remote},
req_send_CER(OriginHost, Remote) ->
register_everywhere({?MODULE, connection, OriginHost, {remote, Remote}}).
-%% start_timer/1
+%% start_timer/2
-start_timer(#state{state = PS} = S) ->
- erlang:send_after(?EVENT_TIMEOUT, self(), {timeout, PS}),
+start_timer(Tmo, #state{state = PS} = S) ->
+ erlang:send_after(Tmo, self(), {timeout, PS}),
S.
%% build_CER/1
-build_CER(#state{service = #diameter_service{capabilities = Caps}}) ->
- {ok, CER} = diameter_capx:build_CER(Caps),
+build_CER(#state{service = #diameter_service{capabilities = LCaps}}) ->
+ {ok, CER} = diameter_capx:build_CER(LCaps),
CER.
%% encode/1
encode(Rec) ->
- #diameter_packet{bin = Bin} = diameter_codec:encode(?BASE, Rec),
- Bin.
+ Seq = diameter_session:sequence(sequence()),
+ Hdr = #diameter_header{version = ?DIAMETER_VERSION,
+ end_to_end_id = Seq,
+ hop_by_hop_id = Seq},
+ diameter_codec:encode(?BASE, #diameter_packet{header = Hdr,
+ msg = Rec}).
+
+sequence() ->
+ case getr(?SEQUENCE_KEY) of
+ {_,_} = Mask ->
+ Mask;
+ undefined -> %% started in old code
+ putr(?SEQUENCE_KEY, ?NOMASK),
+ ?NOMASK
+ end.
%% recv/2
@@ -420,7 +581,14 @@ discard(Reason, F, A) ->
%% rcv/3
%% Incoming CEA.
-rcv('CEA', Pkt, #state{state = 'Wait-CEA'} = S) ->
+rcv('CEA',
+ #diameter_packet{header = #diameter_header{end_to_end_id = Eid,
+ hop_by_hop_id = Hid}}
+ = Pkt,
+ #state{state = {'Wait-CEA' = T, Hid, Eid}}
+ = S) ->
+ handle_CEA(Pkt, S#state{state = T});
+rcv('CEA', Pkt, #state{state = 'Wait-CEA'} = S) -> %% old code
handle_CEA(Pkt, S);
%% Incoming CER
@@ -440,16 +608,16 @@ rcv(N, Pkt, S)
N == 'DPR' ->
handle_request(N, Pkt, S);
-%% DPA even though we haven't sent DPR: ignore.
-rcv('DPA', _Pkt, #state{dpr = false}) ->
- ok;
-
-%% DPA in response to DPR. We could check the sequence numbers but
-%% don't bother, just close.
-rcv('DPA' = N, _Pkt, #state{transport = TPid}) ->
+%% DPA in response to DPR and with the expected identifiers.
+rcv('DPA' = N,
+ #diameter_packet{header = #diameter_header{end_to_end_id = Eid,
+ hop_by_hop_id = Hid}},
+ #state{transport = TPid,
+ dpr = {Hid, Eid}}) ->
diameter_peer:close(TPid),
{stop, N};
+%% Ignore anything else, an unsolicited DPA in particular.
rcv(_, _, _) ->
ok.
@@ -649,7 +817,7 @@ rc([RC|_]) ->
%% answer/2
answer('DWR', _) ->
- getr(dwa);
+ getr(?DWA_KEY);
answer(Name, #state{service = #diameter_service{capabilities = Caps}}) ->
a(Name, Caps).
@@ -667,8 +835,8 @@ a('CER', #diameter_caps{vendor_id = Vid,
{'Product-Name', Name},
{'Origin-State-Id', OSI}];
-a('DPR', #diameter_caps{origin_host = Host,
- origin_realm = Realm}) ->
+a('DPR', #diameter_caps{origin_host = {Host, _},
+ origin_realm = {Realm, _}}) ->
['DPA', {'Origin-Host', Host},
{'Origin-Realm', Realm}].
@@ -749,15 +917,15 @@ caps(#state{service = Svc}) ->
%% caps_cb/1
caps_cb(Caps) ->
- {Ref, Ts} = eraser(capabilities_cb),
- ccb(Ts, [Ref, Caps]).
+ {Ref, Ts} = eraser(?CB_KEY),
+ caps_cb(Ts, [Ref, Caps]).
-ccb([], _) ->
+caps_cb([], _) ->
ok;
-ccb([F | Rest], T) ->
+caps_cb([F | Rest], T) ->
case diameter_lib:eval([F|T]) of
ok ->
- ccb(Rest, T);
+ caps_cb(Rest, T);
N when ?IS_SUCCESS(N) -> %% 2xxx result code: accept immediately
N;
Res ->
@@ -776,7 +944,9 @@ rejected(N)
%% open/5
-open(Pkt, SupportedApps, Caps, {Type, IS}, #state{parent = Pid} = S) ->
+open(Pkt, SupportedApps, Caps, {Type, IS}, #state{parent = Pid,
+ service = Svc}
+ = S) ->
#diameter_caps{origin_host = {_,_} = H,
inband_security_id = {LS,_}}
= Caps,
@@ -784,7 +954,9 @@ open(Pkt, SupportedApps, Caps, {Type, IS}, #state{parent = Pid} = S) ->
tls_ack(lists:member(?TLS, LS), Caps, Type, IS, S),
Pid ! {open, self(), H, {Caps, SupportedApps, Pkt}},
- S#state{state = 'Open'}.
+ %% Replace capabilities record with local/remote pairs.
+ S#state{state = 'Open',
+ service = Svc#diameter_service{capabilities = Caps}}.
%% We've advertised TLS support: tell the transport the result
%% and expect a reply when the handshake is complete.
@@ -837,38 +1009,148 @@ dwa(#diameter_caps{origin_host = OH,
{'Origin-State-Id', OSI}].
%% dpr/2
+%%
+%% The RFC isn't clear on whether DPR should be send in a non-Open
+%% state. The Peer State Machine transitions it documents aren't
+%% exhaustive (no Stop in Wait-I-CEA for example) so assume it's up to
+%% the implementation and transition to Closed (ie. die) if we haven't
+%% yet reached Open.
+
+%% Connection is open, DPR has not been sent.
+dpr(Reason, #state{state = 'Open',
+ dpr = false,
+ service = #diameter_service{capabilities = Caps}}
+ = S) ->
+ case getr(?DPR_KEY) of
+ CBs when is_list(CBs) ->
+ Ref = getr(?REF_KEY),
+ Peer = {self(), Caps},
+ dpr(CBs, [Reason, Ref, Peer], S);
+ undefined -> %% started in old code
+ send_dpr(Reason, [], S)
+ end;
-dpr(Cause, #state{transport = TPid,
- service = #diameter_service{capabilities = Caps}}
- = S) ->
- #diameter_caps{origin_host = OH,
- origin_realm = OR}
+%% Connection is open, DPR already sent.
+dpr(_, #state{state = 'Open'}) ->
+ ok;
+
+%% Connection not open.
+dpr(_Reason, _S) ->
+ stop.
+
+%% dpr/3
+%%
+%% Note that an implementation that wants to do something
+%% transport_module-specific can lookup the pid of the transport
+%% process and contact it. (eg. diameter:service_info/2)
+
+dpr([CB|Rest], [Reason | _] = Args, S) ->
+ try diameter_lib:eval([CB | Args]) of
+ {dpr, Opts} when is_list(Opts) ->
+ send_dpr(Reason, Opts, S);
+ dpr ->
+ send_dpr(Reason, [], S);
+ close = T ->
+ {stop, {disconnect_cb, T}};
+ ignore ->
+ dpr(Rest, Args, S);
+ T ->
+ No = {disconnect_cb, T},
+ diameter_lib:error_report(invalid, No),
+ {stop, No}
+ catch
+ E:R ->
+ No = {disconnect_cb, E, R, ?STACK},
+ diameter_lib:error_report(failure, No),
+ {stop, No}
+ end;
+
+dpr([], [Reason | _], S) ->
+ send_dpr(Reason, [], S).
+
+-record(opts, {cause, timeout = ?DPA_TIMEOUT}).
+
+send_dpr(Reason, Opts, #state{transport = TPid,
+ service = #diameter_service{capabilities = Caps}}
+ = S) ->
+ #opts{cause = Cause, timeout = Tmo}
+ = lists:foldl(fun opt/2,
+ #opts{cause = case Reason of
+ transport -> ?GOAWAY;
+ _ -> ?REBOOT
+ end,
+ timeout = ?DPA_TIMEOUT},
+ Opts),
+ #diameter_caps{origin_host = {OH, _},
+ origin_realm = {OR, _}}
= Caps,
- Bin = encode(['DPR', {'Origin-Host', OH},
+ #diameter_packet{header = #diameter_header{end_to_end_id = Eid,
+ hop_by_hop_id = Hid}}
+ = Pkt
+ = encode(['DPR', {'Origin-Host', OH},
{'Origin-Realm', OR},
{'Disconnect-Cause', Cause}]),
- send(TPid, Bin),
- dpa_timer(),
+ send(TPid, Pkt),
+ dpa_timer(Tmo),
?LOG(send, 'DPR'),
- S#state{dpr = diameter_codec:sequence_numbers(Bin)}.
-
-dpa_timer() ->
- erlang:send_after(?DPA_TIMEOUT, self(), dpa_timeout).
+ S#state{dpr = {Hid, Eid}}.
+
+opt({timeout, Tmo}, Rec)
+ when ?IS_TIMEOUT(Tmo) ->
+ Rec#opts{timeout = Tmo};
+opt({cause, Cause}, Rec)
+ when ?IS_CAUSE(Cause) ->
+ Rec#opts{cause = cause(Cause)};
+opt(T, _) ->
+ ?ERROR({invalid_option, T}).
+
+cause(rebooting) -> ?REBOOT;
+cause(goaway) -> ?GOAWAY;
+cause(busy) -> ?BUSY;
+cause(N)
+ when ?IS_CAUSE(N) ->
+ N;
+cause(N) ->
+ ?ERROR({invalid_cause, N}).
+
+dpa_timer(Tmo) ->
+ erlang:send_after(Tmo, self(), dpa_timeout).
%% register_everywhere/1
%%
%% Register a term and ensure it's not registered elsewhere. Note that
%% two process that simultaneously register the same term may well
%% both fail to do so this isn't foolproof.
+%%
+%% Everywhere is no longer everywhere, it's where a
+%% restrict_connections service_opt() specifies.
register_everywhere(T) ->
- diameter_reg:add_new(T)
- andalso unregistered(T).
+ reg(getr(?RESTRICT_KEY), T).
+
+reg(Nodes, T) ->
+ add(lists:member(node(), Nodes), T) andalso unregistered(Nodes, T).
+
+add(true, T) ->
+ diameter_reg:add_new(T);
+add(false, T) ->
+ diameter_reg:add(T).
+
+%% unregistered
+%%
+%% Ensure that the term in question isn't registered on other nodes.
+
+unregistered(Nodes, T) ->
+ {ResL, _} = rpc:multicall(Nodes, ?MODULE, match, [{node(), T}]),
+ lists:all(fun nomatch/1, ResL).
+
+nomatch({badrpc, {'EXIT', {undef, _}}}) -> %% no diameter on remote node
+ true;
+nomatch(L) ->
+ [] == L.
-unregistered(T) ->
- {ResL, _} = rpc:multicall(?MODULE, match, [{node(), T}]),
- lists:all(fun(L) -> [] == L end, ResL).
+%% match/1
match({Node, _})
when Node == node() ->
diff --git a/lib/diameter/src/base/diameter_reg.erl b/lib/diameter/src/base/diameter_reg.erl
index 882b9da238..619b12ecad 100644
--- a/lib/diameter/src/base/diameter_reg.erl
+++ b/lib/diameter/src/base/diameter_reg.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -30,7 +30,8 @@
add_new/1,
del/1,
repl/2,
- match/1]).
+ match/1,
+ wait/1]).
-export([start_link/0]).
@@ -65,27 +66,22 @@
%% Table entry containing the Term -> Pid mapping.
-define(MAPPING(Term, Pid), {Term, Pid}).
--record(state, {id = now()}).
-
-%%% ----------------------------------------------------------
-%%% # add(T)
-%%%
-%%% Input: Term = term()
-%%%
-%%% Output: true
-%%%
-%%% Description: Associate the specified term with self(). The list of pids
-%%% having this or other assocations can be retrieved using
-%%% match/1.
-%%%
-%%% An association is removed when the calling process dies
-%%% or as a result of calling del/1. Adding the same term
-%%% more than once is equivalent to adding it exactly once.
-%%%
-%%% Note that since match/1 takes a pattern as argument,
-%%% specifying a term that contains match variables is
-%%% probably not a good idea
-%%% ----------------------------------------------------------
+-record(state, {id = now(),
+ q = []}). %% [{From, Pat}]
+
+%% ===========================================================================
+%% # add(T)
+%%
+%% Associate the specified term with self(). The list of pids having
+%% this or other assocations can be retrieved using match/1.
+%%
+%% An association is removed when the calling process dies or as a
+%% result of calling del/1. Adding the same term more than once is
+%% equivalent to adding it exactly once.
+%%
+%% Note that since match/1 takes a pattern as argument, specifying a
+%% term that contains match variables is probably not a good idea
+%% ===========================================================================
-spec add(any())
-> true.
@@ -93,17 +89,12 @@
add(T) ->
call({add, fun ets:insert/2, T, self()}).
-%%% ----------------------------------------------------------
-%%% # add_new(T)
-%%%
-%%% Input: T = term()
-%%%
-%%% Output: true | false
-%%%
-%%% Description: Like add/1 but only one process is allowed to have the
-%%% the association, false being returned if an association
-%%% already exists.
-%%% ----------------------------------------------------------
+%% ===========================================================================
+%% # add_new(T)
+%%
+%% Like add/1 but only one process is allowed to have the the
+%% association, false being returned if an association already exists.
+%% ===========================================================================
-spec add_new(any())
-> boolean().
@@ -111,16 +102,12 @@ add(T) ->
add_new(T) ->
call({add, fun insert_new/2, T, self()}).
-%%% ----------------------------------------------------------
-%%% # repl(T, NewT)
-%%%
-%%% Input: T, NewT = term()
-%%%
-%%% Output: true | false
-%%%
-%%% Description: Like add/1 but only replace an existing association on T,
-%%% false being returned if it doesn't exist.
-%%% ----------------------------------------------------------
+%% ===========================================================================
+%% # repl(T, NewT)
+%%
+%% Like add/1 but only replace an existing association on T, false
+%% being returned if it doesn't exist.
+%% ===========================================================================
-spec repl(any(), any())
-> boolean().
@@ -128,15 +115,11 @@ add_new(T) ->
repl(T, U) ->
call({repl, T, U, self()}).
-%%% ----------------------------------------------------------
-%%% # del(Term)
-%%%
-%%% Input: Term = term()
-%%%
-%%% Output: true
-%%%
-%%% Description: Remove any existing association of Term with self().
-%%% ----------------------------------------------------------
+%% ===========================================================================
+%% # del(Term)
+%%
+%% Remove any existing association of Term with self().
+%% ===========================================================================
-spec del(any())
-> true.
@@ -144,20 +127,16 @@ repl(T, U) ->
del(T) ->
call({del, T, self()}).
-%%% ----------------------------------------------------------
-%%% # match(Pat)
-%%%
-%%% Input: Pat = pattern in the sense of ets:match_object/2.
-%%%
-%%% Output: list of {Term, Pid}
-%%%
-%%% Description: Return the list of associations whose Term, as specified
-%%% to add/1 or add_new/1, matches the specified pattern.
-%%%
-%%% Note that there's no guarantee that the returned processes
-%%% are still alive. (Although one that isn't will soon have
-%%% its associations removed.)
-%%% ----------------------------------------------------------
+%% ===========================================================================
+%% # match(Pat)
+%%
+%% Return the list of associations whose Term, as specified to add/1
+%% or add_new/1, matches the specified pattern.
+%%
+%% Note that there's no guarantee that the returned processes are
+%% still alive. (Although one that isn't will soon have its
+%% associations removed.)
+%% ===========================================================================
-spec match(tuple())
-> [{term(), pid()}].
@@ -165,9 +144,17 @@ del(T) ->
match(Pat) ->
ets:match_object(?TABLE, ?MAPPING(Pat, '_')).
-%% ---------------------------------------------------------
-%% EXPORTED INTERNAL FUNCTIONS
-%% ---------------------------------------------------------
+%% ===========================================================================
+%% # wait(Pat)
+%%
+%% Like match/1 but return only when the result is non-empty or fails.
+%% It's up to the caller to ensure that the wait won't be forever.
+%% ===========================================================================
+
+wait(Pat) ->
+ call({wait, Pat}).
+
+%% ===========================================================================
start_link() ->
ServerName = {local, ?SERVER},
@@ -182,7 +169,7 @@ uptime() ->
%% pids/0
%%
-%% Output: list of {Pid, [Term, ...]}
+%% Return: list of {Pid, [Term, ...]}
pids() ->
to_list(fun swap/1).
@@ -202,89 +189,100 @@ id(T) -> T.
%% terms/0
%%
-%% Output: list of {Term, [Pid, ...]}
+%% Return: list of {Term, [Pid, ...]}
terms() ->
to_list(fun id/1).
swap({X,Y}) -> {Y,X}.
-%%% ----------------------------------------------------------
-%%% # init(Role)
-%%%
-%%% Output: {ok, State}
-%%% ----------------------------------------------------------
+%% ----------------------------------------------------------
+%% # init/1
+%% ----------------------------------------------------------
init(_) ->
ets:new(?TABLE, [bag, named_table]),
{ok, #state{}}.
-%%% ----------------------------------------------------------
-%%% # handle_call(Request, From, State)
-%%% ----------------------------------------------------------
+%% ----------------------------------------------------------
+%% # handle_call/3
+%% ----------------------------------------------------------
-handle_call({add, Fun, Key, Pid}, _, State) ->
+handle_call(Req, From, S)
+ when not is_record(S, state) ->
+ handle_call(Req, From, upgrade(S));
+
+handle_call({add, Fun, Key, Pid}, _, S) ->
B = Fun(?TABLE, {Key, Pid}),
monitor(B andalso no_monitor(Pid), Pid),
- {reply, B, State};
+ {reply, B, pending(B, S)};
-handle_call({del, Key, Pid}, _, State) ->
- {reply, ets:delete_object(?TABLE, ?MAPPING(Key, Pid)), State};
+handle_call({del, Key, Pid}, _, S) ->
+ {reply, ets:delete_object(?TABLE, ?MAPPING(Key, Pid)), S};
-handle_call({repl, T, U, Pid}, _, State) ->
+handle_call({repl, T, U, Pid}, _, S) ->
MatchSpec = [{?MAPPING('$1', Pid),
[{'=:=', '$1', {const, T}}],
['$_']}],
- {reply, repl(ets:select(?TABLE, MatchSpec), U, Pid), State};
+ {reply, repl(ets:select(?TABLE, MatchSpec), U, Pid), S};
+
+handle_call({wait, Pat}, From, #state{q = Q} = S) ->
+ case find(Pat) of
+ {ok, L} ->
+ {reply, L, S};
+ false ->
+ {noreply, S#state{q = [{From, Pat} | Q]}}
+ end;
-handle_call(state, _, State) ->
- {reply, State, State};
+handle_call(state, _, S) ->
+ {reply, S, S};
-handle_call(uptime, _, #state{id = Time} = State) ->
- {reply, diameter_lib:now_diff(Time), State};
+handle_call(uptime, _, #state{id = Time} = S) ->
+ {reply, diameter_lib:now_diff(Time), S};
-handle_call(Req, From, State) ->
+handle_call(Req, From, S) ->
?UNEXPECTED([Req, From]),
- {reply, nok, State}.
+ {reply, nok, S}.
-%%% ----------------------------------------------------------
-%%% # handle_cast(Request, State)
-%%% ----------------------------------------------------------
+%% ----------------------------------------------------------
+%% # handle_cast/2
+%% ----------------------------------------------------------
-handle_cast(Msg, State)->
+handle_cast(Msg, S)->
?UNEXPECTED([Msg]),
- {noreply, State}.
+ {noreply, S}.
-%%% ----------------------------------------------------------
-%%% # handle_info(Request, State)
-%%% ----------------------------------------------------------
+%% ----------------------------------------------------------
+%% # handle_info/2
+%% ----------------------------------------------------------
-handle_info({'DOWN', MRef, process, Pid, _}, State) ->
+handle_info({'DOWN', MRef, process, Pid, _}, S) ->
ets:delete_object(?TABLE, ?MONITOR(Pid, MRef)),
ets:match_delete(?TABLE, ?MAPPING('_', Pid)),
- {noreply, State};
+ {noreply, S};
-handle_info(Info, State) ->
+handle_info(Info, S) ->
?UNEXPECTED([Info]),
- {noreply, State}.
+ {noreply, S}.
-%%% ----------------------------------------------------------
-%%% # terminate(Reason, State)
-%%% ----------------------------------------------------------
+%% ----------------------------------------------------------
+%% # terminate/2
+%% ----------------------------------------------------------
terminate(_Reason, _State)->
ok.
-%%% ----------------------------------------------------------
-%%% # code_change(OldVsn, State, Extra)
-%%% ----------------------------------------------------------
+%% ----------------------------------------------------------
+%% # code_change/3
+%% ----------------------------------------------------------
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
-%% ---------------------------------------------------------
-%% INTERNAL FUNCTIONS
-%% ---------------------------------------------------------
+%% ===========================================================================
+
+upgrade(S) ->
+ #state{} = list_to_tuple(tuple_to_list(S) ++ [[]]).
monitor(true, Pid) ->
ets:insert(?TABLE, ?MONITOR(Pid, erlang:monitor(process, Pid)));
@@ -321,6 +319,37 @@ repl([?MAPPING(_, Pid) = M], Key, Pid) ->
repl([], _, _) ->
false.
+%% pending/1
+
+pending(true, #state{q = [_|_] = Q} = S) ->
+ S#state{q = q(lists:reverse(Q), [])}; %% retain reply order
+pending(_, S) ->
+ S.
+
+q([], Q) ->
+ Q;
+q([{From, Pat} = T | Rest], Q) ->
+ case find(Pat) of
+ {ok, L} ->
+ gen_server:reply(From, L),
+ q(Rest, Q);
+ false ->
+ q(Rest, [T|Q])
+ end.
+
+%% find/1
+
+find(Pat) ->
+ try match(Pat) of
+ [] ->
+ false;
+ L ->
+ {ok, L}
+ catch
+ _:_ ->
+ {ok, []}
+ end.
+
%% call/1
call(Request) ->
diff --git a/lib/diameter/src/base/diameter_service.erl b/lib/diameter/src/base/diameter_service.erl
index 3dfdcee2b2..b5584ca0d0 100644
--- a/lib/diameter/src/base/diameter_service.erl
+++ b/lib/diameter/src/base/diameter_service.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -43,8 +43,7 @@
subscriptions/0,
services/0,
services/1,
- whois/1,
- flush_stats/1]).
+ whois/1]).
%% test/debug
-export([call_module/3,
@@ -65,13 +64,33 @@
-include_lib("diameter/include/diameter.hrl").
-include("diameter_internal.hrl").
+%% The states mirrored by peer_up/peer_down callbacks.
-define(STATE_UP, up).
-define(STATE_DOWN, down).
+-type op_state() :: ?STATE_UP
+ | ?STATE_DOWN.
+
+%% The RFC 3539 watchdog states that are now maintained, albeit
+%% along with the old up/down. okay = up, else down.
+-define(WD_INITIAL, initial).
+-define(WD_OKAY, okay).
+-define(WD_SUSPECT, suspect).
+-define(WD_DOWN, down).
+-define(WD_REOPEN, reopen).
+
+-type wd_state() :: ?WD_INITIAL
+ | ?WD_OKAY
+ | ?WD_SUSPECT
+ | ?WD_DOWN
+ | ?WD_REOPEN.
+
-define(DEFAULT_TC, 30000). %% RFC 3588 ch 2.1
-define(DEFAULT_TIMEOUT, 5000). %% for outgoing requests
-define(RESTART_TC, 1000). %% if restart was this recent
+-define(RELAY, ?DIAMETER_DICT_RELAY).
+
%% Used to be able to swap this with anything else dict-like but now
%% rely on the fact that a service's #state{} record does not change
%% in storing in it ?STATE table and not always going through the
@@ -88,6 +107,12 @@
%% process.
-define(STATE_TABLE, ?MODULE).
+%% The default sequence mask.
+-define(NOMASK, {0,32}).
+
+%% The default restrict_connections.
+-define(RESTRICT, nodes).
+
%% Workaround for dialyzer's lack of understanding of match specs.
-type match(T)
:: T | '_' | '$1' | '$2' | '$3' | '$4'.
@@ -95,15 +120,18 @@
%% State of service gen_server.
-record(state,
{id = now(),
- service_name, %% as passed to start_service/2, key in ?STATE_TABLE
+ service_name, %% as passed to start_service/2, key in ?STATE_TABLE
service :: #diameter_service{},
- peerT = ets_new(peers) :: ets:tid(), %% #peer{} at start_fsm
- connT = ets_new(conns) :: ets:tid(), %% #conn{} at connection_up
- share_peers = false :: boolean(), %% broadcast peers to remote nodes?
- use_shared_peers = false :: boolean(), %% use broadcasted peers?
+ peerT = ets_new(peers) :: ets:tid(),%% #peer{} at start_fsm
+ connT = ets_new(conns) :: ets:tid(),%% #conn{} at connection_up/reopen
shared_peers = ?Dict:new(), %% Alias -> [{TPid, Caps}, ...]
local_peers = ?Dict:new(), %% Alias -> [{TPid, Caps}, ...]
- monitor = false :: false | pid()}). %% process to die with
+ monitor = false :: false | pid(), %% process to die with
+ options
+ :: [{sequence, diameter:sequence()} %% sequence mask
+ | {restrict_connections, diameter:restriction()}
+ | {share_peers, boolean()} %% broadcast peers to remote nodes?
+ | {use_shared_peers, boolean()}]}).%% use broadcasted peers?
%% shared_peers reflects the peers broadcast from remote nodes. Note
%% that the state term itself doesn't change, which is relevant for
%% the stateless application callbacks since the state is retrieved
@@ -111,18 +139,30 @@
%% service record is used to determine whether or not we need to call
%% the process for a pick_peer callback.
-%% Record representing a watchdog process.
+%% Record representing a watchdog process as implemented by
+%% diameter_watchdog. The term "peer" here is historical, made
+%% especially confusing by the fact that a peer_ref() in the
+%% documentation is the key of a #conn{} record, not a #peer{} record.
+%% The name is also unfortunate given the meaning of peer in the
+%% Diameter sense.
-record(peer,
{pid :: match(pid()),
type :: match(connect | accept),
ref :: match(reference()), %% key into diameter_config
options :: match([diameter:transport_opt()]),%% from start_transport
- op_state = ?STATE_DOWN :: match(?STATE_DOWN | ?STATE_UP),
+ op_state = {?STATE_DOWN, ?WD_INITIAL}
+ :: match(op_state() | {op_state(), wd_state()}),
started = now(), %% at process start
conn = false :: match(boolean() | pid())}).
- %% true at accept, pid() at connection_up (connT key)
-
-%% Record representing a peer_fsm process.
+ %% true at accepted, pid() at connection_up or reopen
+
+%% Record representing a peer process as implemented by
+%% diameter_peer_fsm. The term "conn" is historical. Despite the name
+%% here, comments refer to watchdog and peer processes, that are keys
+%% in #peer{} and #conn{} records respectively. To add to the
+%% confusion, a #request.transport is a peer process = key in a
+%% #conn{} record. The actual transport process (that the peer process
+%% knows about and that has a transport connection) isn't seen here.
-record(conn,
{pid :: pid(),
apps :: [{0..16#FFFFFFFF, diameter:app_alias()}], %% {Id, Alias}
@@ -136,10 +176,9 @@
handler :: match(pid()), %% request process
transport :: match(pid()), %% peer process
caps :: match(#diameter_caps{}),
- app :: match(diameter:app_alias()), %% #diameter_app.alias
- dictionary :: match(module()), %% #diameter_app.dictionary
- module :: match([module() | list()]),
- %% #diameter_app.module
+ app :: match(diameter:app_alias()),%% #diameter_app.alias
+ dictionary :: match(module()), %% #diameter_app.dictionary
+ module :: match([module() | list()]), %% #diameter_app.module
filter :: match(diameter:peer_filter()),
packet :: match(#diameter_packet{})}).
@@ -150,20 +189,6 @@
timeout = ?DEFAULT_TIMEOUT :: 0..16#FFFFFFFF,
detach = false :: boolean()}).
-%% Since RFC 3588 requires that a Diameter agent not modify End-to-End
-%% Identifiers, the possibility of explicitly setting an End-to-End
-%% Identifier would be needed to be able to implement an agent in
-%% which one side of the communication is not implemented on top of
-%% diameter. For example, Diameter being sent or received encapsulated
-%% in some other protocol, or even another Diameter stack in a
-%% non-Erlang environment. (Not that this is likely to be a normal
-%% case.)
-%%
-%% The implemented solution is not an option but to respect any header
-%% values set in a diameter_header record returned from a
-%% prepare_request callback. A call to diameter:call/4 can communicate
-%% values to the callback using the 'extra' option if so desired.
-
%%% ---------------------------------------------------------------------------
%%% # start(SvcName)
%%% ---------------------------------------------------------------------------
@@ -216,20 +241,20 @@ stop_transport(SvcName, [_|_] = Refs) ->
%%% ---------------------------------------------------------------------------
info(SvcName, Item) ->
- info_rc(call_service_by_name(SvcName, {info, Item})).
-
-info_rc({error, _}) ->
- undefined;
-info_rc(Info) ->
- Info.
+ case find_state(SvcName) of
+ #state{} = S ->
+ service_info(Item, S);
+ false ->
+ undefined
+ end.
%%% ---------------------------------------------------------------------------
%%% # receive_message(TPid, Pkt, MessageData)
%%% ---------------------------------------------------------------------------
-%% Handle an incoming message in the watchdog process. This used to
-%% come through the service process but this avoids that becoming a
-%% bottleneck.
+%% Handle an incoming Diameter message in the watchdog process. This
+%% used to come through the service process but this avoids that
+%% becoming a bottleneck.
receive_message(TPid, Pkt, T)
when is_pid(TPid) ->
@@ -309,21 +334,39 @@ call_rc(_, _, Sent) ->
%% In the process spawned for the outgoing request.
call(SvcName, App, Msg, Opts, Caller) ->
- c(ets:lookup(?STATE_TABLE, SvcName), App, Msg, Opts, Caller).
+ c(find_state(SvcName), App, Msg, Opts, Caller).
-c([#state{service_name = SvcName} = S], App, Msg, Opts, Caller) ->
+c(#state{service_name = Svc, options = [{_, Mask} | _]} = S,
+ App,
+ Msg,
+ Opts,
+ Caller) ->
case find_transport(App, Msg, Opts, S) of
{_,_,_} = T ->
- send_request(T, Msg, Opts, Caller, SvcName);
+ send_request(T, Mask, Msg, Opts, Caller, Svc);
false ->
{error, no_connection};
{error, _} = No ->
No
end;
-c([], _, _, _, _) ->
+c(false, _, _, _, _) ->
{error, no_service}.
+%% find_state/1
+
+find_state(SvcName) ->
+ fs(ets:lookup(?STATE_TABLE, SvcName)).
+
+fs([#state{} = S]) ->
+ S;
+
+fs([S]) -> %% inserted from old code
+ upgrade(S);
+
+fs([]) ->
+ false.
+
%% make_options/1
make_options(Options) ->
@@ -388,15 +431,6 @@ whois(SvcName) ->
undefined
end.
-%%% ---------------------------------------------------------------------------
-%%% # flush_stats/1
-%%%
-%%% Output: list of {{SvcName, Alias, Counter}, Value}
-%%% ---------------------------------------------------------------------------
-
-flush_stats(TPid) ->
- diameter_stats:flush(TPid).
-
%% ===========================================================================
%% ===========================================================================
@@ -428,6 +462,10 @@ i(_, false) ->
%%% # handle_call(Req, From, State)
%%% ---------------------------------------------------------------------------
+handle_call(T, From, S)
+ when not is_record(S, state) ->
+ handle_call(T, From, upgrade(S));
+
handle_call(state, _, S) ->
{reply, S, S};
@@ -451,16 +489,25 @@ handle_call({pick_peer, Local, Remote, App}, _From, S) ->
handle_call({call_module, AppMod, Req}, From, S) ->
call_module(AppMod, Req, From, S);
+%% Call from old code.
handle_call({info, Item}, _From, S) ->
{reply, service_info(Item, S), S};
handle_call(stop, _From, S) ->
- shutdown(S),
+ shutdown(service, S),
{stop, normal, ok, S};
%% The server currently isn't guaranteed to be dead when the caller
%% gets the reply. We deal with this in the call to the server,
%% stating a monitor that waits for DOWN before returning.
+%% Watchdog is asking for the sequence mask.
+handle_call(sequence, _From, #state{options = [{_, Mask} | _]} = S) ->
+ {reply, Mask, S};
+
+%% Watchdog is asking for the nodes restriction.
+handle_call(restriction, _From, #state{options = [_,_,_,{_,R} | _]} = S) ->
+ {reply, R, S};
+
handle_call(Req, From, S) ->
unexpected(handle_call, [Req, From], S),
{reply, nok, S}.
@@ -477,15 +524,16 @@ handle_cast(Req, S) ->
%%% # handle_info(Req, State)
%%% ---------------------------------------------------------------------------
-handle_info(T,S) ->
+handle_info(T, #state{} = S) ->
case transition(T,S) of
ok ->
{noreply, S};
- #state{} = NS ->
- {noreply, NS};
{stop, Reason} ->
{stop, {shutdown, Reason}, S}
- end.
+ end;
+
+handle_info(T, S) ->
+ handle_info(T, upgrade(S)).
%% transition/2
@@ -496,15 +544,26 @@ transition({accepted, Pid, TPid}, S) ->
%% Peer process has a new open connection.
transition({connection_up, Pid, T}, S) ->
- connection_up(Pid, T, S);
+ connection_up(Pid, T, S),
+ ok;
+
+%% Watchdog has a new connection that will be opened after DW[RA]
+%% exchange. This message was added long after connection_up, to
+%% communicate the information as soon as it's available. Leave
+%% connection_up as is it for now, duplicated information and all.
+transition({reopen, Pid, T}, S) ->
+ reopen(Pid, T, S),
+ ok;
-%% Peer process has left state open.
+%% Watchdog has left state OKAY.
transition({connection_down, Pid}, S) ->
- connection_down(Pid, S);
+ connection_down(Pid, S),
+ ok;
-%% Peer process has returned to state open.
+%% Watchdog has returned to state OKAY.
transition({connection_up, Pid}, S) ->
- connection_up(Pid, S);
+ connection_up(Pid, S),
+ ok;
%% Accepting transport has lost connectivity.
transition({close, Pid}, S) ->
@@ -516,29 +575,61 @@ transition({reconnect, Pid}, S) ->
reconnect(Pid, S),
ok;
+%% Watchdog is sending notification of a state transition. Note that
+%% the connection_up/down messages are pre-date this message and are
+%% still used. A watchdog message will follow these and communicate
+%% the same state as was set in handling connection_up/down.
+transition({watchdog, Pid, {TPid, From, To}}, #state{service_name = SvcName,
+ peerT = PeerT}) ->
+ #peer{ref = Ref, type = T, options = Opts, op_state = {OS,_}}
+ = P
+ = fetch(PeerT, Pid),
+ insert(PeerT, P#peer{op_state = {OS, To}}),
+ send_event(SvcName, {watchdog, Ref, TPid, {From, To}, {T, Opts}}),
+ ok;
+%% Death of a watchdog process (#peer.pid) results in the removal of
+%% it's peer and any associated conn record when 'DOWN' is received
+%% (after this) but the states will be {?STATE_UP, ?WD_DOWN} for a
+%% short time. (No real problem since ?WD_* is only used in
+%% service_info.) We set ?WD_OKAY as a consequence of connection_up
+%% since we know a watchdog is coming. We can't set anything at
+%% connection_down since we don't know if the subsequent watchdog
+%% message will be ?WD_DOWN or ?WD_SUSPECT. We don't (yet) set
+%% ?STATE_* as a consequence of a watchdog message since this requires
+%% changing some of the matching on ?STATE_*.
+%%
+%% Death of a peer process process (#conn.pid, #peer.conn) results in
+%% connection_down followed by watchdog ?WD_DOWN. The latter doesn't
+%% result in the conn record being deleted since 'DOWN' from death of
+%% its watchdog doesn't (yet) deal with the record having been
+%% removed.
+
%% Monitor process has died. Just die with a reason that tells
%% diameter_config about the happening. If a cleaner shutdown is
%% required then someone should stop us.
transition({'DOWN', MRef, process, _, Reason}, #state{monitor = MRef}) ->
{stop, {monitor, Reason}};
-%% Local peer process has died.
+%% Local watchdog process has died.
transition({'DOWN', _, process, Pid, Reason}, S)
when node(Pid) == node() ->
- peer_down(Pid, Reason, S);
+ peer_down(Pid, Reason, S),
+ ok;
-%% Remote service wants to know about shared transports.
+%% Remote service wants to know about shared peers.
transition({service, Pid}, S) ->
share_peers(Pid, S),
ok;
%% Remote service is communicating a shared peer.
transition({peer, TPid, Aliases, Caps}, S) ->
- remote_peer_up(TPid, Aliases, Caps, S);
+ remote_peer_up(TPid, Aliases, Caps, S),
+ ok;
%% Remote peer process has died.
transition({'DOWN', _, process, TPid, _}, S) ->
- remote_peer_down(TPid, S);
+ remote_peer_down(TPid, S),
+ ok;
%% Restart after tc expiry.
transition({tc_timeout, T}, S) ->
@@ -552,18 +643,48 @@ transition({failover, TRef, Seqs}, S) ->
failover(TRef, Seqs, S),
ok;
+%% Ensure upgraded state is stored in state table.
+transition(upgrade, _) ->
+ ok;
+
transition(Req, S) ->
unexpected(handle_info, [Req], S),
ok.
+%% upgrade/1
+
+upgrade({state, Id, Svc, Name, Svc, PT, CT, SB, UB, SD, LD, MPid}) ->
+ S = #state{id = Id,
+ service_name = Name,
+ service = Svc,
+ peerT = PT,
+ connT = CT,
+ shared_peers = SD,
+ local_peers = LD,
+ monitor = MPid,
+ options = [{sequence, ?NOMASK},
+ {share_peers, SB},
+ {use_shared_peers, UB},
+ {restrict_connections, ?RESTRICT}]},
+ upgrade_insert(S),
+ S.
+
+upgrade_insert(#state{service = #diameter_service{pid = Pid}} = S) ->
+ if Pid == self() ->
+ ets:insert(?STATE_TABLE, S);
+ true ->
+ Pid ! upgrade
+ end.
+
%%% ---------------------------------------------------------------------------
%%% # terminate(Reason, State)
%%% ---------------------------------------------------------------------------
terminate(Reason, #state{service_name = Name} = S) ->
+ send_event(Name, stop),
ets:delete(?STATE_TABLE, Name),
shutdown == Reason %% application shutdown
- andalso shutdown(S).
+ andalso shutdown(application, S).
%%% ---------------------------------------------------------------------------
%%% # code_change(FromVsn, State, Extra)
@@ -646,41 +767,49 @@ mod_state(Alias, ModS) ->
%%% # shutdown/2
%%% ---------------------------------------------------------------------------
-shutdown(Refs, #state{peerT = PeerT}) ->
- ets:foldl(fun(P,ok) -> s(P, Refs), ok end, ok, PeerT).
-
-s(#peer{ref = Ref, pid = Pid}, Refs) ->
- s(lists:member(Ref, Refs), Pid);
-
-s(true, Pid) ->
- Pid ! {shutdown, self()}; %% 'DOWN' will cleanup as usual
-s(false, _) ->
- ok.
-
-%%% ---------------------------------------------------------------------------
-%%% # shutdown/1
-%%% ---------------------------------------------------------------------------
+%% remove_transport: ask watchdogs to terminate their transport.
+shutdown(Refs, #state{peerT = PeerT})
+ when is_list(Refs) ->
+ ets:foldl(fun(P,ok) -> sp(P, Refs), ok end, ok, PeerT);
-shutdown(#state{peerT = PeerT}) ->
+%% application/service shutdown: ask transports to terminate themselves.
+shutdown(Reason, #state{peerT = PeerT}) ->
%% A transport might not be alive to receive the shutdown request
%% but give those that are a chance to shutdown gracefully.
- wait(fun st/2, PeerT),
+ shutdown(conn, Reason, PeerT),
%% Kill the watchdogs explicitly in case there was no transport.
- wait(fun sw/2, PeerT).
+ shutdown(peer, Reason, PeerT).
-wait(Fun, T) ->
- diameter_lib:wait(ets:foldl(Fun, [], T)).
+%% sp/2
-st(#peer{conn = B}, Acc)
- when is_boolean(B) ->
- Acc;
-st(#peer{conn = Pid}, Acc) ->
- Pid ! shutdown,
- [Pid | Acc].
+sp(#peer{ref = Ref, pid = Pid}, Refs) ->
+ lists:member(Ref, Refs)
+ andalso (Pid ! {shutdown, self()}). %% 'DOWN' cleans up
+
+%% shutdown/3
+
+shutdown(Who, Reason, T) ->
+ diameter_lib:wait(ets:foldl(fun(X,A) -> shutdown(Who, X, Reason, A) end,
+ [],
+ T)).
+
+shutdown(conn = Who, #peer{op_state = {OS,_}} = P, Reason, Acc) ->
+ shutdown(Who, P#peer{op_state = OS}, Reason, Acc);
-sw(#peer{pid = Pid}, Acc) ->
+shutdown(conn,
+ #peer{pid = Pid, op_state = ?STATE_UP, conn = TPid},
+ Reason,
+ Acc) ->
+ TPid ! {shutdown, Pid, Reason},
+ [TPid | Acc];
+
+shutdown(peer, #peer{pid = Pid}, _Reason, Acc)
+ when is_pid(Pid) ->
exit(Pid, shutdown),
- [Pid | Acc].
+ [Pid | Acc];
+
+shutdown(_, #peer{}, _, Acc) ->
+ Acc.
%%% ---------------------------------------------------------------------------
%%% # call_service/2
@@ -730,6 +859,7 @@ i(SvcName) ->
true = ets:insert_new(?STATE_TABLE, S),
%% Start fsms for each transport.
+ send_event(SvcName, start),
lists:foreach(fun(T) -> start_fsm(T,S) end, CL),
init_shared(S),
@@ -740,9 +870,8 @@ cfg_acc({SvcName, #diameter_service{applications = Apps} = Rec, Opts},
lists:foreach(fun init_mod/1, Apps),
S = #state{service_name = SvcName,
service = Rec#diameter_service{pid = self()},
- share_peers = get_value(share_peers, Opts),
- use_shared_peers = get_value(use_shared_peers, Opts),
- monitor = mref(get_value(monitor, Opts))},
+ monitor = mref(get_value(monitor, Opts)),
+ options = service_options(Opts)},
{S, Acc};
cfg_acc({_Ref, Type, _Opts} = T, {S, Acc})
@@ -750,15 +879,24 @@ cfg_acc({_Ref, Type, _Opts} = T, {S, Acc})
Type == listen ->
{S, [T | Acc]}.
+service_options(Opts) ->
+ [{sequence, proplists:get_value(sequence, Opts, ?NOMASK)},
+ {share_peers, get_value(share_peers, Opts)},
+ {use_shared_peers, get_value(use_shared_peers, Opts)},
+ {restrict_connections, proplists:get_value(restrict_connections,
+ Opts,
+ ?RESTRICT)}].
+%% The order of options is significant since we match against the list.
+
mref(false = No) ->
No;
mref(P) ->
erlang:monitor(process, P).
-init_shared(#state{use_shared_peers = true,
+init_shared(#state{options = [_, _, {_, true} | _],
service_name = Svc}) ->
diameter_peer:notify(Svc, {service, self()});
-init_shared(#state{use_shared_peers = false}) ->
+init_shared(#state{options = [_, _, {_, false} | _]}) ->
ok.
init_mod(#diameter_app{alias = Alias,
@@ -821,9 +959,8 @@ start(Ref, Type, Opts, #state{peerT = PeerT,
Pid.
%% Note that the service record passed into the watchdog is the merged
-%% record so that each watchdog (and peer_fsm) may get a different
-%% record. This record is what is passed back into application
-%% callbacks.
+%% record so that each watchdog may get a different record. This
+%% record is what is passed back into application callbacks.
s(Type, Ref, T) ->
case diameter_watchdog:start({Type, Ref}, T) of
@@ -874,20 +1011,25 @@ accepted(Pid, _TPid, #state{peerT = PeerT} = S) ->
#peer{ref = Ref, type = accept = T, conn = false, options = Opts}
= P
= fetch(PeerT, Pid),
- insert(PeerT, P#peer{conn = true}), %% mark replacement transport started
- start(Ref, T, Opts, S). %% start new peer
+ insert(PeerT, P#peer{conn = true}), %% mark replacement as started
+ start(Ref, T, Opts, S). %% start new watchdog
fetch(Tid, Key) ->
[T] = ets:lookup(Tid, Key),
- T.
+ case T of
+ #peer{op_state = ?STATE_UP} = P ->
+ P#peer{op_state = {?STATE_UP, ?WD_OKAY}};
+ #peer{op_state = ?STATE_DOWN} = P ->
+ P#peer{op_state = {?STATE_DOWN, ?WD_DOWN}};
+ _ ->
+ T
+ end.
%%% ---------------------------------------------------------------------------
%%% # connection_up/3
-%%%
-%%% Output: #state{}
%%% ---------------------------------------------------------------------------
-%% Peer process has reached the open state.
+%% Watchdog process has reached state OKAY.
connection_up(Pid, {TPid, {Caps, SApps, Pkt}}, #state{peerT = PeerT,
connT = ConnT}
@@ -902,9 +1044,29 @@ connection_up(Pid, {TPid, {Caps, SApps, Pkt}}, #state{peerT = PeerT,
connection_up([Pkt], P#peer{conn = TPid}, C, S).
%%% ---------------------------------------------------------------------------
+%%% # reopen/3
+%%% ---------------------------------------------------------------------------
+
+%% Note that this connection_up/3 rewrites the same #conn{} now
+%% written here. Both do so in case reopen has not happened in old
+%% code.
+
+reopen(Pid, {TPid, {Caps, SApps, _Pkt}}, #state{peerT = PeerT,
+ connT = ConnT}) ->
+ P = fetch(PeerT, Pid),
+ C = #conn{pid = TPid,
+ apps = SApps,
+ caps = Caps,
+ peer = Pid},
+
+ insert(ConnT, C),
+ #peer{op_state = {?STATE_DOWN, _}}
+ = P,
+ insert(PeerT, P#peer{op_state = {?STATE_DOWN, ?WD_REOPEN},
+ conn = TPid}).
+
+%%% ---------------------------------------------------------------------------
%%% # connection_up/2
-%%%
-%%% Output: #state{}
%%% ---------------------------------------------------------------------------
%% Peer process has transitioned back into the open state. Note that there
@@ -925,18 +1087,16 @@ connection_up(T, P, C, #state{peerT = PeerT,
service
= #diameter_service{applications = Apps}}
= S) ->
- #peer{conn = TPid, op_state = ?STATE_DOWN}
+ #peer{conn = TPid, op_state = {?STATE_DOWN, _}}
= P,
#conn{apps = SApps, caps = Caps}
= C,
- insert(PeerT, P#peer{op_state = ?STATE_UP}),
+ insert(PeerT, P#peer{op_state = {?STATE_UP, ?WD_OKAY}}),
request_peer_up(TPid),
- report_status(up, P, C, S, T),
- S#state{local_peers = insert_local_peer(SApps,
- {{TPid, Caps}, {SvcName, Apps}},
- LDict)}.
+ insert_local_peer(SApps, {{TPid, Caps}, {SvcName, Apps}}, LDict),
+ report_status(up, P, C, S, T).
insert_local_peer(SApps, T, LDict) ->
lists:foldl(fun(A,D) -> ilp(A, T, D) end, LDict, SApps).
@@ -945,56 +1105,62 @@ ilp({Id, Alias}, {TC, SA}, LDict) ->
init_conn(Id, Alias, TC, SA),
?Dict:append(Alias, TC, LDict).
-init_conn(Id, Alias, TC, {SvcName, Apps}) ->
+init_conn(Id, Alias, {TPid, _} = TC, {SvcName, Apps}) ->
#diameter_app{module = ModX,
id = Id} %% assert
= find_app(Alias, Apps),
- peer_cb({ModX, peer_up, [SvcName, TC]}, Alias).
+ peer_cb({ModX, peer_up, [SvcName, TC]}, Alias)
+ orelse exit(TPid, kill). %% fake transport failure
+
+%% find_app/2
find_app(Alias, Apps) ->
- lists:keyfind(Alias, #diameter_app.alias, Apps).
+ case lists:keyfind(Alias, #diameter_app.alias, Apps) of
+ #diameter_app{options = E} = A when is_atom(E) -> %% upgrade
+ A#diameter_app{options = [{answer_errors, E}]};
+ A ->
+ A
+ end.
-%% A failing peer callback brings down the service. In the case of
-%% peer_up we could just kill the transport and emit an error but for
-%% peer_down we have no way to cleanup any state change that peer_up
-%% may have introduced.
+%% Don't bring down the service (and all associated connections)
+%% regardless of what happens.
peer_cb(MFA, Alias) ->
try state_cb(MFA, Alias) of
ModS ->
- mod_state(Alias, ModS)
+ mod_state(Alias, ModS),
+ true
catch
- E: Reason ->
- ?ERROR({E, Reason, MFA, ?STACK})
+ E:R ->
+ diameter_lib:error_report({failure, {E, R, Alias, ?STACK}}, MFA),
+ false
end.
%%% ---------------------------------------------------------------------------
%%% # connection_down/2
-%%%
-%%% Output: #state{}
%%% ---------------------------------------------------------------------------
-%% Peer process has transitioned out of the open state.
+%% Watchdog has transitioned out of state OKAY.
connection_down(Pid, #state{peerT = PeerT,
connT = ConnT}
= S) ->
- #peer{op_state = ?STATE_UP, %% assert
+ #peer{op_state = {?STATE_UP, WS}, %% assert
conn = TPid}
= P
= fetch(PeerT, Pid),
C = fetch(ConnT, TPid),
- insert(PeerT, P#peer{op_state = ?STATE_DOWN}),
+ insert(PeerT, P#peer{op_state = {?STATE_DOWN, WS}}),
connection_down(P,C,S).
%% connection_down/3
-connection_down(#peer{op_state = ?STATE_DOWN}, _, S) ->
- S;
+connection_down(#peer{op_state = {?STATE_DOWN, _}}, _, _) ->
+ ok;
connection_down(#peer{conn = TPid,
- op_state = ?STATE_UP}
+ op_state = {?STATE_UP, _}}
= P,
#conn{caps = Caps,
apps = SApps}
@@ -1004,12 +1170,8 @@ connection_down(#peer{conn = TPid,
local_peers = LDict}
= S) ->
report_status(down, P, C, S, []),
- NewS = S#state{local_peers
- = remove_local_peer(SApps,
- {{TPid, Caps}, {SvcName, Apps}},
- LDict)},
- request_peer_down(TPid, NewS),
- NewS.
+ remove_local_peer(SApps, {{TPid, Caps}, {SvcName, Apps}}, LDict),
+ request_peer_down(TPid, S).
remove_local_peer(SApps, T, LDict) ->
lists:foldl(fun(A,D) -> rlp(A, T, D) end, LDict, SApps).
@@ -1028,11 +1190,9 @@ down_conn(Id, Alias, TC, {SvcName, Apps}) ->
%%% ---------------------------------------------------------------------------
%%% # peer_down/3
-%%%
-%%% Output: #state{}
%%% ---------------------------------------------------------------------------
-%% Peer process has died.
+%% Watchdog process has died.
peer_down(Pid, Reason, #state{peerT = PeerT} = S) ->
P = fetch(PeerT, Pid),
@@ -1043,7 +1203,7 @@ peer_down(Pid, Reason, #state{peerT = PeerT} = S) ->
%% Send an event at connection establishment failure.
closed({shutdown, {close, _TPid, Reason}},
- #peer{op_state = ?STATE_DOWN,
+ #peer{op_state = {?STATE_DOWN, _},
ref = Ref,
type = Type,
options = Opts},
@@ -1052,12 +1212,12 @@ closed({shutdown, {close, _TPid, Reason}},
closed(_, _, _) ->
ok.
-%% The peer has never come up ...
-peer_down(#peer{conn = B}, S)
+%% The watchdog has never reached OKAY ...
+peer_down(#peer{conn = B}, _)
when is_boolean(B) ->
- S;
+ ok;
-%% ... or it has.
+%% ... or maybe it has.
peer_down(#peer{conn = TPid} = P, #state{connT = ConnT} = S) ->
#conn{} = C = fetch(ConnT, TPid),
ets:delete_object(ConnT, C),
@@ -1085,7 +1245,7 @@ restart(#peer{ref = Ref,
started = Time}) ->
{Time, {Ref, T, Opts}};
-%% ... or it has: a replacement transport has already been spawned.
+%% ... or it has: a replacement has already been spawned.
restart(#peer{type = accept}) ->
false.
@@ -1111,8 +1271,8 @@ default_tc(connect, Opts) ->
default_tc(accept, _) ->
0.
-%% Bound tc below if the peer was restarted recently to avoid
-%% continuous in case of faulty config or other problems.
+%% Bound tc below if the watchdog was restarted recently to avoid
+%% continuous restarted in case of faulty config or other problems.
tc(Time, Tc) ->
choose(Tc > ?RESTART_TC
orelse timer:now_diff(now(), Time) > 1000*?RESTART_TC,
@@ -1234,7 +1394,7 @@ cm([_,_|_], _, _, _) ->
multiple.
%%% ---------------------------------------------------------------------------
-%%% # send_request/5
+%%% # send_request/6
%%% ---------------------------------------------------------------------------
%% Send an outgoing request in its dedicated process.
@@ -1247,71 +1407,90 @@ cm([_,_|_], _, _, _) ->
%% The mod field of the #diameter_app{} here includes any extra
%% arguments passed to diameter:call/2.
-send_request({TPid, Caps, App}, Msg, Opts, Caller, SvcName) ->
+send_request({TPid, Caps, App} = T, Mask, Msg, Opts, Caller, SvcName) ->
#diameter_app{module = ModX}
= App,
- Pkt = make_request_packet(Msg),
-
- case cb(ModX, prepare_request, [Pkt, SvcName, {TPid, Caps}]) of
- {send, P} ->
- send_request(make_request_packet(P, Pkt),
- TPid,
- Caps,
- App,
- Opts,
- Caller,
- SvcName);
- {discard, Reason} ->
- {error, Reason};
- discard ->
- {error, discarded};
- T ->
- ?ERROR({invalid_return, prepare_request, App, T})
- end.
+ Pkt = make_prepare_packet(Mask, Msg),
+
+ send_req(cb(ModX, prepare_request, [Pkt, SvcName, {TPid, Caps}]),
+ Pkt,
+ T,
+ Opts,
+ Caller,
+ SvcName,
+ []).
+
+send_req({send, P}, Pkt, T, Opts, Caller, SvcName, Fs) ->
+ send_req(make_request_packet(P, Pkt), T, Opts, Caller, SvcName, Fs);
+
+send_req({discard, Reason} , _, _, _, _, _, _) ->
+ {error, Reason};
+
+send_req(discard, _, _, _, _, _, _) ->
+ {error, discarded};
+
+send_req({eval_packet, RC, F}, Pkt, T, Opts, Caller, SvcName, Fs) ->
+ send_req(RC, Pkt, T, Opts, Caller, SvcName, [F|Fs]);
-%% make_request_packet/1
+send_req(E, _, {_, _, App}, _, _, _, _) ->
+ ?ERROR({invalid_return, prepare_request, App, E}).
+
+%% make_prepare_packet/2
%%
%% Turn an outgoing request as passed to call/4 into a diameter_packet
%% record in preparation for a prepare_request callback.
-make_request_packet(Bin)
+make_prepare_packet(_, Bin)
when is_binary(Bin) ->
#diameter_packet{header = diameter_codec:decode_header(Bin),
bin = Bin};
-make_request_packet(#diameter_packet{msg = [#diameter_header{} = Hdr | Avps]}
- = Pkt) ->
- Pkt#diameter_packet{msg = [make_request_header(Hdr) | Avps]};
+make_prepare_packet(Mask, #diameter_packet{msg = [#diameter_header{} = Hdr
+ | Avps]}
+ = Pkt) ->
+ Pkt#diameter_packet{msg = [make_prepare_header(Mask, Hdr) | Avps]};
-make_request_packet(#diameter_packet{header = Hdr} = Pkt) ->
- Pkt#diameter_packet{header = make_request_header(Hdr)};
+make_prepare_packet(Mask, #diameter_packet{header = Hdr} = Pkt) ->
+ Pkt#diameter_packet{header = make_prepare_header(Mask, Hdr)};
-make_request_packet(Msg) ->
- make_request_packet(#diameter_packet{msg = Msg}).
+make_prepare_packet(Mask, Msg) ->
+ make_prepare_packet(Mask, #diameter_packet{msg = Msg}).
-%% make_request_header/1
+%% make_prepare_header/2
-make_request_header(undefined) ->
- Seq = diameter_session:sequence(),
- make_request_header(#diameter_header{end_to_end_id = Seq,
+make_prepare_header(Mask, undefined) ->
+ Seq = diameter_session:sequence(Mask),
+ make_prepare_header(#diameter_header{end_to_end_id = Seq,
hop_by_hop_id = Seq});
-make_request_header(#diameter_header{version = undefined} = Hdr) ->
- make_request_header(Hdr#diameter_header{version = ?DIAMETER_VERSION});
+make_prepare_header(Mask, #diameter_header{end_to_end_id = undefined,
+ hop_by_hop_id = undefined}
+ = H) ->
+ Seq = diameter_session:sequence(Mask),
+ make_prepare_header(H#diameter_header{end_to_end_id = Seq,
+ hop_by_hop_id = Seq});
+
+make_prepare_header(Mask, #diameter_header{end_to_end_id = undefined} = H) ->
+ Seq = diameter_session:sequence(Mask),
+ make_prepare_header(H#diameter_header{end_to_end_id = Seq});
+
+make_prepare_header(Mask, #diameter_header{hop_by_hop_id = undefined} = H) ->
+ Seq = diameter_session:sequence(Mask),
+ make_prepare_header(H#diameter_header{hop_by_hop_id = Seq});
+
+make_prepare_header(_, Hdr) ->
+ make_prepare_header(Hdr).
-make_request_header(#diameter_header{end_to_end_id = undefined} = H) ->
- Seq = diameter_session:sequence(),
- make_request_header(H#diameter_header{end_to_end_id = Seq});
+%% make_prepare_header/1
-make_request_header(#diameter_header{hop_by_hop_id = undefined} = H) ->
- Seq = diameter_session:sequence(),
- make_request_header(H#diameter_header{hop_by_hop_id = Seq});
+make_prepare_header(#diameter_header{version = undefined} = Hdr) ->
+ make_prepare_header(Hdr#diameter_header{version = ?DIAMETER_VERSION});
-make_request_header(#diameter_header{} = Hdr) ->
+make_prepare_header(#diameter_header{} = Hdr) ->
Hdr;
-make_request_header(T) ->
+make_prepare_header(T) ->
?ERROR({invalid_header, T}).
%% make_request_packet/2
@@ -1321,7 +1500,7 @@ make_request_header(T) ->
make_request_packet(Bin, _)
when is_binary(Bin) ->
- make_request_packet(Bin);
+ make_prepare_packet(false, Bin);
make_request_packet(#diameter_packet{msg = [#diameter_header{} | _]}
= Pkt,
@@ -1333,7 +1512,7 @@ make_request_packet(#diameter_packet{msg = [#diameter_header{} | _]}
%% This is primarily so that the end to end and hop by hop identifiers
%% are retained.
make_request_packet(#diameter_packet{header = Hdr} = Pkt,
- #diameter_packet{header = Hdr0}) ->
+ #diameter_packet{header = Hdr0}) ->
Pkt#diameter_packet{header = fold_record(Hdr0, Hdr)};
make_request_packet(Msg, Pkt) ->
@@ -1346,16 +1525,16 @@ fold_record(undefined, R) ->
fold_record(Rec, R) ->
diameter_lib:fold_tuple(2, Rec, R).
-%% send_request/7
+%% send_req/6
-send_request(Pkt, TPid, Caps, App, Opts, Caller, SvcName) ->
+send_req(Pkt, {TPid, Caps, App}, Opts, Caller, SvcName, Fs) ->
#diameter_app{alias = Alias,
dictionary = Dict,
module = ModX,
- answer_errors = AE}
+ options = [{answer_errors, AE} | _]}
= App,
- EPkt = encode(Dict, Pkt),
+ EPkt = encode(Dict, Pkt, Fs),
#options{filter = Filter,
timeout = Timeout}
@@ -1436,6 +1615,13 @@ msg(#diameter_packet{msg = undefined, bin = Bin}) ->
msg(#diameter_packet{msg = Msg}) ->
Msg.
+%% encode/3
+
+encode(Dict, Pkt, Fs) ->
+ P = encode(Dict, Pkt),
+ eval_packet(P, Fs),
+ P.
+
%% encode/2
%% Note that prepare_request can return a diameter_packet containing
@@ -1517,38 +1703,51 @@ send(Pid, Pkt) ->
%% retransmit/4
-retransmit({TPid, Caps, #diameter_app{alias = Alias} = App},
- #request{app = Alias,
- packet = Pkt}
+retransmit({TPid, Caps, #diameter_app{alias = Alias} = App} = T,
+ #request{app = Alias, packet = Pkt0}
= Req,
SvcName,
Timeout) ->
- have_request(Pkt, TPid) %% Don't failover to a peer we've
+ have_request(Pkt0, TPid) %% Don't failover to a peer we've
andalso ?THROW(timeout), %% already sent to.
- case cb(App, prepare_retransmit, [Pkt, SvcName, {TPid, Caps}]) of
- {send, P} ->
- retransmit(make_request_packet(P, Pkt), TPid, Caps, Req, Timeout);
- {discard, Reason} ->
- ?THROW(Reason);
- discard ->
- ?THROW(discarded);
- T ->
- ?ERROR({invalid_return, prepare_retransmit, App, T})
- end.
+ #diameter_packet{header = Hdr0} = Pkt0,
+ Hdr = Hdr0#diameter_header{is_retransmitted = true},
+ Pkt = Pkt0#diameter_packet{header = Hdr},
+
+ resend_req(cb(App, prepare_retransmit, [Pkt, SvcName, {TPid, Caps}]),
+ T,
+ Req#request{packet = Pkt},
+ Timeout,
+ []).
+
+resend_req({send, P}, T, #request{packet = Pkt} = Req, Timeout, Fs) ->
+ retransmit(make_request_packet(P, Pkt), T, Req, Timeout, Fs);
+
+resend_req({discard, Reason}, _, _, _, _) ->
+ ?THROW(Reason);
-%% retransmit/5
+resend_req(discard, _, _, _, _) ->
+ ?THROW(discarded);
-retransmit(Pkt, TPid, Caps, #request{dictionary = Dict} = Req, Timeout) ->
- EPkt = encode(Dict, Pkt),
+resend_req({eval_packet, RC, F}, T, Req, Timeout, Fs) ->
+ resend_req(RC, T, Req, Timeout, [F|Fs]);
+
+resend_req(T, {_, _, App}, _, _, _) ->
+ ?ERROR({invalid_return, prepare_retransmit, App, T}).
- NewReq = Req#request{transport = TPid,
- packet = Pkt,
- caps = Caps},
+%% retransmit/6
- ?LOG(retransmission, NewReq),
- TRef = send_request(TPid, EPkt, NewReq, Timeout),
- {TRef, NewReq}.
+retransmit(Pkt, {TPid, Caps, _}, #request{dictionary = D} = Req0, Tmo, Fs) ->
+ EPkt = encode(D, Pkt, Fs),
+
+ Req = Req0#request{transport = TPid,
+ packet = Pkt,
+ caps = Caps},
+
+ ?LOG(retransmission, Req),
+ TRef = send_request(TPid, EPkt, Req, Tmo),
+ {TRef, Req}.
%% store_request/4
@@ -1620,10 +1819,13 @@ request_peer_down(TPid, S) ->
%%% recv_request/3
%%% ---------------------------------------------------------------------------
-recv_request(TPid, Pkt, {ConnT, SvcName, Apps}) ->
+recv_request(TPid, Pkt, {ConnT, SvcName, Apps}) -> %% upgrade
+ recv_request(TPid, Pkt, {ConnT, SvcName, Apps, ?NOMASK});
+
+recv_request(TPid, Pkt, {ConnT, SvcName, Apps, Mask}) ->
try ets:lookup(ConnT, TPid) of
[C] ->
- recv_request(C, TPid, Pkt, SvcName, Apps);
+ recv_request(C, TPid, Pkt, SvcName, Apps, Mask);
[] -> %% transport has gone down
ok
catch
@@ -1633,7 +1835,12 @@ recv_request(TPid, Pkt, {ConnT, SvcName, Apps}) ->
%% recv_request/5
-recv_request(#conn{apps = SApps, caps = Caps}, TPid, Pkt, SvcName, Apps) ->
+recv_request(#conn{apps = SApps, caps = Caps},
+ TPid,
+ Pkt,
+ SvcName,
+ Apps,
+ Mask) ->
#diameter_caps{origin_host = {OH,_},
origin_realm = {OR,_}}
= Caps,
@@ -1645,6 +1852,7 @@ recv_request(#conn{apps = SApps, caps = Caps}, TPid, Pkt, SvcName, Apps) ->
{SvcName, OH, OR},
TPid,
Apps,
+ Mask,
Caps,
Pkt).
@@ -1670,20 +1878,24 @@ keyfind([Key | Rest], Pos, L) ->
T
end.
-%% recv_request/6
+%% recv_request/7
-recv_request({Id, Alias}, T, TPid, Apps, Caps, Pkt) ->
+recv_request({Id, Alias}, T, TPid, Apps, Mask, Caps, Pkt) ->
#diameter_app{dictionary = Dict}
= A
= find_app(Alias, Apps),
- recv_request(T, {TPid, Caps}, A, diameter_codec:decode(Id, Dict, Pkt));
+ recv_request(T,
+ {TPid, Caps},
+ A,
+ Mask,
+ diameter_codec:decode(Id, Dict, Pkt));
%% Note that the decode is different depending on whether or not Id is
%% ?APP_ID_RELAY.
%% DIAMETER_APPLICATION_UNSUPPORTED 3007
%% A request was sent for an application that is not supported.
-recv_request(false, T, TPid, _, _, Pkt) ->
+recv_request(false, T, TPid, _, _, _, Pkt) ->
As = collect_avps(Pkt),
protocol_error(3007, T, TPid, Pkt#diameter_packet{avps = As}).
@@ -1695,7 +1907,7 @@ collect_avps(Pkt) ->
As
end.
-%% recv_request/4
+%% recv_request/5
%% Wrong number of bits somewhere in the message: reply.
%%
@@ -1704,7 +1916,7 @@ collect_avps(Pkt) ->
%% set to an unrecognized value, or that is inconsistent with the
%% AVP's definition.
%%
-recv_request(T, {TPid, _}, _, #diameter_packet{errors = [Bs | _]} = Pkt)
+recv_request(T, {TPid, _}, _, _, #diameter_packet{errors = [Bs | _]} = Pkt)
when is_bitstring(Bs) ->
protocol_error(3009, T, TPid, Pkt);
@@ -1719,6 +1931,7 @@ recv_request(T, {TPid, _}, _, #diameter_packet{errors = [Bs | _]} = Pkt)
recv_request(T,
{TPid, _},
#diameter_app{id = Id},
+ _,
#diameter_packet{header = #diameter_header{is_proxiable = P},
msg = M}
= Pkt)
@@ -1736,6 +1949,7 @@ recv_request(T,
recv_request(T,
{TPid, _},
_,
+ _,
#diameter_packet{header = #diameter_header{is_error = true}}
= Pkt) ->
protocol_error(3008, T, TPid, Pkt);
@@ -1744,14 +1958,20 @@ recv_request(T,
%% in the relay application. Don't distinguish between the two since
%% each application has its own callback config. That is, the user can
%% easily distinguish between the two cases.
-recv_request(T, TC, App, Pkt) ->
- request_cb(T, TC, App, examine(Pkt)).
+recv_request(T, TC, App, Mask, Pkt) ->
+ request_cb(T, TC, App, Mask, examine(Pkt)).
%% Note that there may still be errors but these aren't protocol
%% (3xxx) errors that lead to an answer-message.
-request_cb({SvcName, _OH, _OR} = T, TC, App, Pkt) ->
- request_cb(cb(App, handle_request, [Pkt, SvcName, TC]), App, T, TC, Pkt).
+request_cb({SvcName, _OH, _OR} = T, TC, App, Mask, Pkt) ->
+ request_cb(cb(App, handle_request, [Pkt, SvcName, TC]),
+ App,
+ Mask,
+ T,
+ TC,
+ [],
+ Pkt).
%% examine/1
%%
@@ -1771,7 +1991,7 @@ examine(#diameter_packet{errors = Es} = Pkt) ->
Pkt#diameter_packet{errors = [5011 | Es]}.
%% It's odd/unfortunate that this isn't a protocol error.
-%% request_cb/5
+%% request_cb/7
%% A reply may be an answer-message, constructed either here or by
%% the handle_request callback. The header from the incoming request
@@ -1781,21 +2001,23 @@ examine(#diameter_packet{errors = Es} = Pkt) ->
request_cb({reply, Ans},
#diameter_app{dictionary = Dict},
_,
+ _,
{TPid, _},
+ Fs,
Pkt) ->
- reply(Ans, Dict, TPid, Pkt);
+ reply(Ans, Dict, TPid, Fs, Pkt);
%% An 3xxx result code, for which the E-bit is set in the header.
-request_cb({protocol_error, RC}, _, T, {TPid, _}, Pkt)
+request_cb({protocol_error, RC}, _, _, T, {TPid, _}, Fs, Pkt)
when 3000 =< RC, RC < 4000 ->
- protocol_error(RC, T, TPid, Pkt);
+ protocol_error(RC, T, TPid, Fs, Pkt);
%% RFC 3588 says we must reply 3001 to anything unrecognized or
%% unsupported. 'noreply' is undocumented (and inappropriately named)
%% backwards compatibility for this, protocol_error the documented
%% alternative.
-request_cb(noreply, _, T, {TPid, _}, Pkt) ->
- protocol_error(3001, T, TPid, Pkt);
+request_cb(noreply, _, _, T, {TPid, _}, Fs, Pkt) ->
+ protocol_error(3001, T, TPid, Fs, Pkt);
%% Relay a request to another peer. This is equivalent to doing an
%% explicit call/4 with the message in question except that (1) a loop
@@ -1815,38 +2037,57 @@ request_cb(noreply, _, T, {TPid, _}, Pkt) ->
request_cb({A, Opts},
#diameter_app{id = Id}
= App,
+ Mask,
T,
TC,
+ Fs,
Pkt)
when A == relay, Id == ?APP_ID_RELAY;
A == proxy, Id /= ?APP_ID_RELAY;
A == resend ->
- resend(Opts, App, T, TC, Pkt);
+ resend(Opts, App, Mask, T, TC, Fs, Pkt);
-request_cb(discard, _, _, _, _) ->
+request_cb(discard, _, _, _, _, _, _) ->
ok;
-request_cb({eval, RC, F}, App, T, TC, Pkt) ->
- request_cb(RC, App, T, TC, Pkt),
+request_cb({eval_packet, RC, F}, App, Mask, T, TC, Fs, Pkt) ->
+ request_cb(RC, App, Mask, T, TC, [F|Fs], Pkt);
+
+request_cb({eval, RC, F}, App, Mask, T, TC, Fs, Pkt) ->
+ request_cb(RC, App, Mask, T, TC, Fs, Pkt),
diameter_lib:eval(F).
-%% protocol_error/4
+%% protocol_error/5
-protocol_error(RC, {_, OH, OR}, TPid, #diameter_packet{avps = Avps} = Pkt) ->
+protocol_error(RC, {_, OH, OR}, TPid, Fs, Pkt) ->
+ #diameter_packet{avps = Avps, errors = Es} = Pkt,
?LOG({error, RC}, Pkt),
- reply(answer_message({OH, OR, RC}, Avps), ?BASE, TPid, Pkt).
+ reply(answer_message({OH, OR, RC}, Avps),
+ ?BASE,
+ TPid,
+ Fs,
+ Pkt#diameter_packet{errors = [RC | Es]}).
+%% Note that reply/5 may set the result code once more. It's set in
+%% answer_message/2 in case reply/5 doesn't.
+
+%% protocol_error/4
+
+protocol_error(RC, T, TPid, Pkt) ->
+ protocol_error(RC, T, TPid, [], Pkt).
-%% resend/5
+%% resend/7
%%
%% Resend a message as a relay or proxy agent.
resend(Opts,
#diameter_app{} = App,
+ Mask,
{_SvcName, OH, _OR} = T,
{_TPid, _Caps} = TC,
+ Fs,
#diameter_packet{avps = Avps} = Pkt) ->
{Code, _Flags, Vid} = ?BASE:avp_header('Route-Record'),
- resend(is_loop(Code, Vid, OH, Avps), Opts, App, T, TC, Pkt).
+ resend(is_loop(Code, Vid, OH, Avps), Opts, App, Mask, T, TC, Fs, Pkt).
%% DIAMETER_LOOP_DETECTED 3005
%% An agent detected a loop while trying to get the message to the
@@ -1854,8 +2095,8 @@ resend(Opts,
%% if one is available, but the peer reporting the error has
%% identified a configuration problem.
-resend(true, _, _, T, {TPid, _}, Pkt) -> %% Route-Record loop
- protocol_error(3005, T, TPid, Pkt);
+resend(true, _, _, _, T, {TPid, _}, Fs, Pkt) -> %% Route-Record loop
+ protocol_error(3005, T, TPid, Fs, Pkt);
%% 6.1.8. Relaying and Proxying Requests
%%
@@ -1866,16 +2107,18 @@ resend(true, _, _, T, {TPid, _}, Pkt) -> %% Route-Record loop
resend(false,
Opts,
App,
+ Mask,
{SvcName, _, _} = T,
{TPid, #diameter_caps{origin_host = {_, OH}}},
+ Fs,
#diameter_packet{header = Hdr0,
avps = Avps}
= Pkt) ->
Route = #diameter_avp{data = {?BASE, 'Route-Record', OH}},
- Seq = diameter_session:sequence(),
+ Seq = diameter_session:sequence(Mask),
Hdr = Hdr0#diameter_header{hop_by_hop_id = Seq},
Msg = [Hdr, Route | Avps],
- resend(call(SvcName, App, Msg, Opts), T, TPid, Pkt).
+ resend(call(SvcName, App, Msg, Opts), T, TPid, Fs, Pkt).
%% The incoming request is relayed with the addition of a
%% Route-Record. Note the requirement on the return from call/4 below,
%% which places a requirement on the value returned by the
@@ -1901,15 +2144,18 @@ resend(#diameter_packet{bin = B}
= Pkt,
_,
TPid,
+ Fs,
#diameter_packet{header = #diameter_header{hop_by_hop_id = Id},
transport_data = TD}) ->
- send(TPid, Pkt#diameter_packet{bin = diameter_codec:hop_by_hop_id(Id, B),
- transport_data = TD});
+ P = Pkt#diameter_packet{bin = diameter_codec:hop_by_hop_id(Id, B),
+ transport_data = TD},
+ eval_packet(P, Fs),
+ send(TPid, P);
%% TODO: counters
%% Or not: DIAMETER_UNABLE_TO_DELIVER.
-resend(_, T, TPid, Pkt) ->
- protocol_error(3002, T, TPid, Pkt).
+resend(_, T, TPid, Fs, Pkt) ->
+ protocol_error(3002, T, TPid, Fs, Pkt).
%% is_loop/4
%%
@@ -1931,46 +2177,69 @@ is_loop(Code, Vid, OH, [_ | Avps])
is_loop(Code, Vid, OH, Avps) ->
is_loop(Code, Vid, ?BASE:avp(encode, OH, 'Route-Record'), Avps).
-%% reply/4
+%% reply/5
%%
%% Send a locally originating reply.
+%% Skip the setting of Result-Code and Failed-AVP's below. This is
+%% currently undocumented.
+reply([Msg], Dict, TPid, Fs, Pkt)
+ when is_list(Msg);
+ is_tuple(Msg) ->
+ reply(Msg, Dict, TPid, Fs, Pkt#diameter_packet{errors = []});
+
%% No errors or a diameter_header/avp list.
-reply(Msg, Dict, TPid, #diameter_packet{errors = Es,
- transport_data = TD}
- = ReqPkt)
+reply(Msg, Dict, TPid, Fs, #diameter_packet{errors = Es} = ReqPkt)
when [] == Es;
is_record(hd(Msg), diameter_header) ->
Pkt = diameter_codec:encode(Dict, make_answer_packet(Msg, ReqPkt)),
- incr(send, Pkt, TPid), %% count result codes in sent answers
- send(TPid, Pkt#diameter_packet{transport_data = TD});
+ eval_packet(Pkt, Fs),
+ incr(send, Pkt, Dict, TPid), %% count result codes in sent answers
+ send(TPid, Pkt);
%% Or not: set Result-Code and Failed-AVP AVP's.
-reply(Msg, Dict, TPid, #diameter_packet{errors = [H|_] = Es} = Pkt) ->
+reply(Msg, Dict, TPid, Fs, #diameter_packet{errors = [H|_] = Es} = Pkt) ->
reply(rc(Msg, rc(H), [A || {_,A} <- Es], Dict),
Dict,
TPid,
+ Fs,
Pkt#diameter_packet{errors = []}).
+eval_packet(Pkt, Fs) ->
+ lists:foreach(fun(F) -> diameter_lib:eval([F,Pkt]) end, Fs).
+
%% make_answer_packet/2
+%% A reply message clears the R and T flags and retains the P flag.
+%% The E flag will be set at encode. 6.2 of 3588 requires the same P
+%% flag on an answer as on the request. A #diameter_packet{} returned
+%% from a handle_request callback can circumvent this by setting its
+%% own header values.
+make_answer_packet(#diameter_packet{header = Hdr,
+ msg = Msg,
+ transport_data = TD},
+ #diameter_packet{header = ReqHdr}) ->
+ Hdr0 = ReqHdr#diameter_header{version = ?DIAMETER_VERSION,
+ is_request = false,
+ is_error = undefined,
+ is_retransmitted = false},
+ #diameter_packet{header = fold_record(Hdr0, Hdr),
+ msg = Msg,
+ transport_data = TD};
+
%% Binaries and header/avp lists are sent as-is.
-make_answer_packet(Bin, _)
+make_answer_packet(Bin, #diameter_packet{transport_data = TD})
when is_binary(Bin) ->
- #diameter_packet{bin = Bin};
-make_answer_packet([#diameter_header{} | _] = Msg, _) ->
- #diameter_packet{msg = Msg};
-
-%% Otherwise a reply message clears the R and T flags and retains the
-%% P flag. The E flag will be set at encode. 6.2 of 3588 requires the
-%% same P flag on an answer as on the request.
-make_answer_packet(Msg, #diameter_packet{header = ReqHdr}) ->
- Hdr = ReqHdr#diameter_header{version = ?DIAMETER_VERSION,
- is_request = false,
- is_error = undefined,
- is_retransmitted = false},
- #diameter_packet{header = Hdr,
- msg = Msg}.
+ #diameter_packet{bin = Bin,
+ transport_data = TD};
+make_answer_packet([#diameter_header{} | _] = Msg,
+ #diameter_packet{transport_data = TD}) ->
+ #diameter_packet{msg = Msg,
+ transport_data = TD};
+
+%% Otherwise, preserve transport_data.
+make_answer_packet(Msg, #diameter_packet{transport_data = TD} = Pkt) ->
+ make_answer_packet(#diameter_packet{msg = Msg, transport_data = TD}, Pkt).
%% rc/1
@@ -1981,9 +2250,15 @@ rc(RC) ->
%% rc/4
+rc(#diameter_packet{msg = Rec} = Pkt, RC, Failed, Dict) ->
+ Pkt#diameter_packet{msg = rc(Rec, RC, Failed, Dict)};
+
rc(Rec, RC, Failed, Dict)
when is_integer(RC) ->
- set(Rec, [{'Result-Code', RC} | failed_avp(Rec, Failed, Dict)], Dict).
+ set(Rec,
+ lists:append([rc(Rec, {'Result-Code', RC}, Dict),
+ failed_avp(Rec, Failed, Dict)]),
+ Dict).
%% Reply as name and tuple list ...
set([_|_] = Ans, Avps, _) ->
@@ -1993,6 +2268,22 @@ set([_|_] = Ans, Avps, _) ->
set(Rec, Avps, Dict) ->
Dict:'#set-'(Avps, Rec).
+%% rc/3
+%%
+%% Turn the result code into a list if its optional and only set it if
+%% the arity is 1 or {0,1}. In other cases (which probably shouldn't
+%% exist in practise) we can't know what's appropriate.
+
+rc([MsgName | _], {'Result-Code' = K, RC} = T, Dict) ->
+ case Dict:avp_arity(MsgName, 'Result-Code') of
+ 1 -> [T];
+ {0,1} -> [{K, [RC]}];
+ _ -> []
+ end;
+
+rc(Rec, T, Dict) ->
+ rc([Dict:rec2msg(element(1, Rec))], T, Dict).
+
%% failed_avp/3
failed_avp(_, [] = No, _) ->
@@ -2200,44 +2491,39 @@ handle_answer(SvcName, _, {error, Req, Reason}) ->
handle_answer(SvcName,
AnswerErrors,
{answer, #request{dictionary = Dict} = Req, Pkt}) ->
- a(examine(diameter_codec:decode(Dict, Pkt)),
- SvcName,
- AnswerErrors,
- Req).
+ answer(examine(diameter_codec:decode(Dict, Pkt)),
+ SvcName,
+ AnswerErrors,
+ Req).
%% We don't really need to do a full decode if we're a relay and will
%% just resend with a new hop by hop identifier, but might a proxy
%% want to examine the answer?
-a(#diameter_packet{errors = []}
- = Pkt,
- SvcName,
- AE,
- #request{transport = TPid,
- caps = Caps,
- packet = P}
- = Req) ->
+answer(Pkt, SvcName, AE, #request{transport = TPid,
+ dictionary = Dict}
+ = Req) ->
try
- incr(in, Pkt, TPid)
+ incr(recv, Pkt, Dict, TPid)
of
- _ ->
- cb(Req, handle_answer, [Pkt, msg(P), SvcName, {TPid, Caps}])
+ _ -> a(Pkt, SvcName, AE, Req)
catch
exit: {invalid_error_bit, _} = E ->
- e(Pkt#diameter_packet{errors = [E]}, SvcName, AE, Req)
- end;
+ a(Pkt#diameter_packet{errors = [E]}, SvcName, AE, Req)
+ end.
-a(#diameter_packet{} = Pkt, SvcName, AE, Req) ->
- e(Pkt, SvcName, AE, Req).
+a(#diameter_packet{errors = Es} = Pkt, SvcName, AE, #request{transport = TPid,
+ caps = Caps,
+ packet = P}
+ = Req)
+ when [] == Es;
+ callback == AE ->
+ cb(Req, handle_answer, [Pkt, msg(P), SvcName, {TPid, Caps}]);
-e(Pkt, SvcName, callback, #request{transport = TPid,
- caps = Caps,
- packet = Pkt}
- = Req) ->
- cb(Req, handle_answer, [Pkt, msg(Pkt), SvcName, {TPid, Caps}]);
-e(Pkt, SvcName, report, Req) ->
+a(Pkt, SvcName, report, Req) ->
x(errors, handle_answer, [SvcName, Req, Pkt]);
-e(Pkt, SvcName, discard, Req) ->
+
+a(Pkt, SvcName, discard, Req) ->
x({errors, handle_answer, [SvcName, Req, Pkt]}).
%% Note that we don't check that the application id in the answer's
@@ -2249,17 +2535,19 @@ e(Pkt, SvcName, discard, Req) ->
%% Increment a stats counter for an incoming or outgoing message.
%% TODO: fix
-incr(_, #diameter_packet{msg = undefined}, _) ->
+incr(_, #diameter_packet{msg = undefined}, _, _) ->
ok;
-incr(Dir, Pkt, TPid)
- when is_pid(TPid) ->
+incr(recv = D, #diameter_packet{header = H, errors = [_|_]}, _, TPid) ->
+ incr(TPid, {diameter_codec:msg_id(H), D, error});
+
+incr(Dir, Pkt, Dict, TPid) ->
#diameter_packet{header = #diameter_header{is_error = E}
= Hdr,
msg = Rec}
= Pkt,
- RC = int(get_avp_value(?BASE, 'Result-Code', Rec)),
+ RC = int(get_avp_value(Dict, 'Result-Code', Rec)),
PE = is_protocol_error(RC),
%% Check that the E bit is set only for 3xxx result codes.
@@ -2267,15 +2555,21 @@ incr(Dir, Pkt, TPid)
orelse (E andalso PE)
orelse x({invalid_error_bit, RC}, answer, [Dir, Pkt]),
- Ctr = rc_counter(Rec, RC),
- is_tuple(Ctr)
- andalso incr(TPid, {diameter_codec:msg_id(Hdr), Dir, Ctr}).
+ irc(TPid, Hdr, Dir, rc_counter(Dict, Rec, RC)).
+
+irc(_, _, _, undefined) ->
+ false;
+
+irc(TPid, Hdr, Dir, Ctr) ->
+ incr(TPid, {diameter_codec:msg_id(Hdr), Dir, Ctr}).
%% incr/2
incr(TPid, Counter) ->
diameter_stats:incr(Counter, TPid, 1).
+%% error_counter/2
+
%% RFC 3588, 7.6:
%%
%% All Diameter answer messages defined in vendor-specific
@@ -2285,26 +2579,27 @@ incr(TPid, Counter) ->
%% Maintain statistics assuming one or the other, not both, which is
%% surely the intent of the RFC.
-rc_counter(_, RC)
- when is_integer(RC) ->
- {'Result-Code', RC};
-rc_counter(Rec, _) ->
- rcc(get_avp_value(?BASE, 'Experimental-Result', Rec)).
+rc_counter(Dict, Rec, undefined) ->
+ er(get_avp_value(Dict, 'Experimental-Result', Rec));
+rc_counter(_, _, RC) ->
+ {'Result-Code', RC}.
%% Outgoing answers may be in any of the forms messages can be sent
%% in. Incoming messages will be records. We're assuming here that the
%% arity of the result code AVP's is 0 or 1.
-rcc([{_,_,RC} = T])
- when is_integer(RC) ->
+er([{_,_,N} = T | _])
+ when is_integer(N) ->
T;
-rcc({_,_,RC} = T)
- when is_integer(RC) ->
+er({_,_,N} = T)
+ when is_integer(N) ->
T;
-rcc(_) ->
+er(_) ->
undefined.
-int([N])
+%% Extract the first good looking integer. There's no guarantee
+%% that what we're looking for has arity 1.
+int([N|_])
when is_integer(N) ->
N;
int(N)
@@ -2349,8 +2644,11 @@ rt(#request{packet = #diameter_packet{msg = undefined}}, _) ->
false; %% TODO: Not what we should do.
%% ... or not.
-rt(#request{packet = #diameter_packet{msg = Msg}} = Req, S) ->
- find_transport(get_destination(Msg), Req, S).
+rt(#request{packet = #diameter_packet{msg = Msg},
+ dictionary = Dict}
+ = Req,
+ S) ->
+ find_transport(get_destination(Dict, Msg), Req, S).
%%% ---------------------------------------------------------------------------
%%% # report_status/5
@@ -2383,7 +2681,7 @@ send_event(#diameter_event{service = SvcName} = E) ->
%%% # share_peer/5
%%% ---------------------------------------------------------------------------
-share_peer(up, Caps, Aliases, TPid, #state{share_peers = true,
+share_peer(up, Caps, Aliases, TPid, #state{options = [_, {_, true} | _],
service_name = Svc}) ->
diameter_peer:notify(Svc, {peer, TPid, Aliases, Caps});
@@ -2394,11 +2692,11 @@ share_peer(_, _, _, _, _) ->
%%% # share_peers/2
%%% ---------------------------------------------------------------------------
-share_peers(Pid, #state{share_peers = true,
+share_peers(Pid, #state{options = [_, {_, true} | _],
local_peers = PDict}) ->
?Dict:fold(fun(A,Ps,ok) -> sp(Pid, A, Ps), ok end, ok, PDict);
-share_peers(_, #state{share_peers = false}) ->
+share_peers(_, _) ->
ok.
sp(Pid, Alias, Peers) ->
@@ -2408,39 +2706,31 @@ sp(Pid, Alias, Peers) ->
%%% # remote_peer_up/4
%%% ---------------------------------------------------------------------------
-remote_peer_up(Pid, Aliases, Caps, #state{use_shared_peers = true,
+remote_peer_up(Pid, Aliases, Caps, #state{options = [_, _, {_, true} | _],
service = Svc,
- shared_peers = PDict}
- = S) ->
+ shared_peers = PDict}) ->
#diameter_service{applications = Apps} = Svc,
- Update = lists:filter(fun(A) ->
- lists:keymember(A, #diameter_app.alias, Apps)
- end,
- Aliases),
- S#state{shared_peers = rpu(Pid, Caps, PDict, Update)};
+ Key = #diameter_app.alias,
+ As = lists:filter(fun(A) -> lists:keymember(A, Key, Apps) end, Aliases),
+ rpu(Pid, Caps, PDict, As);
-remote_peer_up(_, _, _, #state{use_shared_peers = false} = S) ->
- S.
+remote_peer_up(_, _, _, #state{options = [_, _, {_, false} | _]}) ->
+ ok.
rpu(_, _, PDict, []) ->
PDict;
rpu(Pid, Caps, PDict, Aliases) ->
erlang:monitor(process, Pid),
T = {Pid, Caps},
- lists:foldl(fun(A,D) -> ?Dict:append(A, T, D) end,
- PDict,
- Aliases).
+ lists:foreach(fun(A) -> ?Dict:append(A, T, PDict) end, Aliases).
%%% ---------------------------------------------------------------------------
%%% # remote_peer_down/2
%%% ---------------------------------------------------------------------------
-remote_peer_down(Pid, #state{use_shared_peers = true,
- shared_peers = PDict}
- = S) ->
- S#state{shared_peers = lists:foldl(fun(A,D) -> rpd(Pid, A, D) end,
- PDict,
- ?Dict:fetch_keys(PDict))}.
+remote_peer_down(Pid, #state{options = [_, _, {_, true} | _],
+ shared_peers = PDict}) ->
+ lists:foreach(fun(A) -> rpd(Pid, A, PDict) end, ?Dict:fetch_keys(PDict)).
rpd(Pid, Alias, PDict) ->
?Dict:update(Alias, fun(Ps) -> lists:keydelete(Pid, 1, Ps) end, PDict).
@@ -2462,12 +2752,12 @@ find_transport({alias, Alias}, Msg, Opts, #state{service = Svc} = S) ->
find_transport(#diameter_app{} = App, Msg, Opts, S) ->
ft(App, Msg, Opts, S).
-ft(#diameter_app{module = Mod} = App, Msg, Opts, S) ->
+ft(#diameter_app{module = Mod, dictionary = Dict} = App, Msg, Opts, S) ->
#options{filter = Filter,
extra = Xtra}
= Opts,
pick_peer(App#diameter_app{module = Mod ++ Xtra},
- get_destination(Msg),
+ get_destination(Dict, Msg),
Filter,
S);
ft(false = No, _, _, _) ->
@@ -2503,11 +2793,11 @@ find_transport([_,_] = RH,
Filter,
S).
-%% get_destination/1
+%% get_destination/2
-get_destination(Msg) ->
- [str(get_avp_value(?BASE, 'Destination-Realm', Msg)),
- str(get_avp_value(?BASE, 'Destination-Host', Msg))].
+get_destination(Dict, Msg) ->
+ [str(get_avp_value(Dict, 'Destination-Realm', Msg)),
+ str(get_avp_value(Dict, 'Destination-Host', Msg))].
%% This is not entirely correct. The avp could have an arity 1, in
%% which case an empty list is a DiameterIdentity of length 0 rather
@@ -2531,6 +2821,15 @@ str(T) ->
%% question. The third form allows messages to be sent as is, without
%% a dictionary, which is needed in the case of relay agents, for one.
+%% Messages will be header/avps list as a relay and the only AVP's we
+%% look for are in the common dictionary. This is required since the
+%% relay dictionary doesn't inherit the common dictionary (which maybe
+%% it should).
+get_avp_value(?RELAY, Name, Msg) ->
+ get_avp_value(?BASE, Name, Msg);
+
+%% Message sent as a header/avps list, probably a relay case but not
+%% necessarily.
get_avp_value(Dict, Name, [#diameter_header{} | Avps]) ->
try
{Code, _, VId} = Dict:avp_header(Name),
@@ -2544,6 +2843,7 @@ get_avp_value(Dict, Name, [#diameter_header{} | Avps]) ->
undefined
end;
+%% Outgoing message as a name/values list.
get_avp_value(_, Name, [_MsgName | Avps]) ->
case lists:keyfind(Name, 1, Avps) of
{_, V} ->
@@ -2552,6 +2852,11 @@ get_avp_value(_, Name, [_MsgName | Avps]) ->
undefined
end;
+%% Record might be an answer message in the common dictionary.
+get_avp_value(Dict, Name, Rec)
+ when Dict /= ?BASE, element(1, Rec) == 'diameter_base_answer-message' ->
+ get_avp_value(?BASE, Name, Rec);
+
%% Message is typically a record but not necessarily: diameter:call/4
%% can be passed an arbitrary term.
get_avp_value(Dict, Name, Rec) ->
@@ -2746,20 +3051,59 @@ transports(#state{peerT = PeerT}) ->
'Vendor-Specific-Application-Id',
'Firmware-Revision']).
+%% The config returned by diameter:service_info(SvcName, all).
-define(ALL_INFO, [capabilities,
applications,
transport,
pending,
- statistics]).
+ options]).
+
+%% The rest.
+-define(OTHER_INFO, [connections,
+ name,
+ peers,
+ statistics]).
-service_info(Items, S)
- when is_list(Items) ->
- [{complete(I), service_info(I,S)} || I <- Items];
service_info(Item, S)
when is_atom(Item) ->
- service_info(Item, S, true).
+ case tagged_info(Item, S) of
+ {_, T} -> T;
+ undefined = No -> No
+ end;
+
+service_info(Items, S) ->
+ tagged_info(Items, S).
+
+tagged_info(Item, S)
+ when is_atom(Item) ->
+ case complete(Item) of
+ {value, I} ->
+ {I, complete_info(I,S)};
+ false ->
+ undefined
+ end;
-service_info(Item, #state{service = Svc} = S, Complete) ->
+tagged_info(TPid, #state{peerT = PT, connT = CT})
+ when is_pid(TPid) ->
+ try
+ [#conn{peer = Pid}] = ets:lookup(CT, TPid),
+ [#peer{ref = Ref, type = Type, options = Opts}] = ets:lookup(PT, Pid),
+ [{ref, Ref},
+ {type, Type},
+ {options, Opts}]
+ catch
+ error:_ ->
+ []
+ end;
+
+tagged_info(Items, S)
+ when is_list(Items) ->
+ [T || I <- Items, T <- [tagged_info(I,S)], T /= undefined, T /= []];
+
+tagged_info(_, _) ->
+ undefined.
+
+complete_info(Item, #state{service = Svc} = S) ->
case Item of
name ->
S#state.service_name;
@@ -2802,85 +3146,176 @@ service_info(Item, #state{service = Svc} = S, Complete) ->
capabilities -> service_info(?CAP_INFO, S);
applications -> info_apps(S);
transport -> info_transport(S);
+ options -> info_options(S);
pending -> info_pending(S);
- statistics -> info_stats(S);
- keys -> ?ALL_INFO ++ ?CAP_INFO; %% mostly for test
+ keys -> ?ALL_INFO ++ ?CAP_INFO ++ ?OTHER_INFO;
all -> service_info(?ALL_INFO, S);
- _ when Complete -> service_info(complete(Item), S, false);
- _ -> undefined
+ statistics -> info_stats(S);
+ connections -> info_connections(S);
+ peers -> info_peers(S)
end.
+complete(I)
+ when I == keys;
+ I == all ->
+ {value, I};
complete(Pre) ->
P = atom_to_list(Pre),
- case [I || I <- [name | ?ALL_INFO] ++ ?CAP_INFO,
+ case [I || I <- ?ALL_INFO ++ ?CAP_INFO ++ ?OTHER_INFO,
lists:prefix(P, atom_to_list(I))]
of
- [I] -> I;
- _ -> Pre
+ [I] -> {value, I};
+ _ -> false
end.
+%% info_stats/1
+
info_stats(#state{peerT = PeerT}) ->
- Peers = ets:select(PeerT, [{#peer{ref = '$1', conn = '$2', _ = '_'},
- [{'is_pid', '$2'}],
- [['$1', '$2']]}]),
- diameter_stats:read(lists:append(Peers)).
-%% TODO: include peer identities in return value
-
-info_transport(#state{peerT = PeerT, connT = ConnT}) ->
- dict:fold(fun it/3,
+ MatchSpec = [{#peer{ref = '$1', conn = '$2', _ = '_'},
+ [{'is_pid', '$2'}],
+ [['$1', '$2']]}],
+ try ets:select(PeerT, MatchSpec) of
+ L ->
+ diameter_stats:read(lists:append(L))
+ catch
+ error: badarg -> [] %% service has gone down
+ end.
+
+%% info_transport/1
+%%
+%% One entry per configured transport. Statistics for each entry are
+%% the accumulated values for the ref and associated peer pids.
+
+info_transport(S) ->
+ PeerD = peer_dict(S, config_dict(S)),
+ RefsD = dict:map(fun(_, Ls) -> [P || L <- Ls, {peer, {P,_}} <- L] end,
+ PeerD),
+ Refs = lists:append(dict:fold(fun(R, Ps, A) -> [[R|Ps] | A] end,
+ [],
+ RefsD)),
+ Stats = diameter_stats:read(Refs),
+ dict:fold(fun(R, Ls, A) ->
+ Ps = dict:fetch(R, RefsD),
+ [[{ref, R} | transport(Ls)] ++ [stats([R|Ps], Stats)]
+ | A]
+ end,
[],
- ets:foldl(fun(T,A) -> it_acc(ConnT, A, T) end,
- dict:new(),
- PeerT)).
-
-it(Ref, [[{type, connect} | _] = L], Acc) ->
- [[{ref, Ref} | L] | Acc];
-it(Ref, [[{type, accept}, {options, Opts} | _] | _] = L, Acc) ->
- [[{ref, Ref},
- {type, listen},
- {options, Opts},
- {accept, [lists:nthtail(2,A) || A <- L]}]
- | Acc].
-%% Each entry has the same Opts. (TODO)
-
-it_acc(ConnT, Acc, #peer{pid = Pid,
- type = Type,
- ref = Ref,
- options = Opts,
- op_state = OS,
- started = T,
- conn = TPid}) ->
+ PeerD).
+
+%% Only a config entry for a listening transport: use it.
+transport([[{type, listen}, _] = L]) ->
+ L ++ [{accept, []}];
+
+%% Only one config or peer entry for a connecting transport: use it.
+transport([[{type, connect} | _] = L]) ->
+ L;
+
+%% Peer entries: discard config. Note that the peer entries have
+%% length at least 3.
+transport([[_,_] | L]) ->
+ transport(L);
+
+%% Possibly many peer entries for a listening transport. Note that all
+%% have the same options by construction, which is not terribly space
+%% efficient. (TODO: all entries for the same Ref should share options.)
+transport([[{type, accept}, {options, Opts} | _] | _] = Ls) ->
+ [{type, listen},
+ {options, Opts},
+ {accept, [lists:nthtail(2,L) || L <- Ls]}].
+
+peer_dict(#state{peerT = PeerT, connT = ConnT}, Dict0) ->
+ try ets:tab2list(PeerT) of
+ L ->
+ lists:foldl(fun(T,A) -> peer_acc(ConnT, A, T) end, Dict0, L)
+ catch
+ error: badarg -> Dict0 %% service has gone down
+ end.
+
+peer_acc(ConnT, Acc, #peer{pid = Pid,
+ type = Type,
+ ref = Ref,
+ options = Opts,
+ op_state = OS,
+ started = T,
+ conn = TPid}) ->
+ WS = wd_state(OS),
dict:append(Ref,
[{type, Type},
{options, Opts},
- {watchdog, {Pid, T, OS}}
- | info_conn(ConnT, TPid)],
+ {watchdog, {Pid, T, WS}}
+ | info_conn(ConnT, TPid, WS /= ?WD_DOWN)],
Acc).
-info_conn(ConnT, TPid) ->
- info_conn(ets:lookup(ConnT, TPid)).
+info_conn(ConnT, TPid, true)
+ when is_pid(TPid) ->
+ try ets:lookup(ConnT, TPid) of
+ T -> info_conn(T)
+ catch
+ error: badarg -> [] %% service has gone down
+ end;
+info_conn(_, _, _) ->
+ [].
+
+%% The point of extracting the config here is so that 'transport' info
+%% has one entry for each transport ref, the peer table only
+%% containing entries that have a living watchdog.
+
+config_dict(#state{service_name = SvcName}) ->
+ lists:foldl(fun config_acc/2,
+ dict:new(),
+ diameter_config:lookup(SvcName)).
+
+config_acc({Ref, T, Opts}, Dict)
+ when T == listen;
+ T == connect ->
+ dict:store(Ref, [[{type, T}, {options, Opts}]], Dict);
+config_acc(_, Dict) ->
+ Dict.
+
+wd_state({_,S}) ->
+ S;
+wd_state(?STATE_UP) ->
+ ?WD_OKAY;
+wd_state(?STATE_DOWN) ->
+ ?WD_DOWN.
info_conn([#conn{pid = Pid, apps = SApps, caps = Caps, started = T}]) ->
[{peer, {Pid, T}},
{apps, SApps},
- {caps, info_caps(Caps)}];
+ {caps, info_caps(Caps)}
+ | try [{port, info_port(Pid)}] catch _:_ -> [] end];
info_conn([] = No) ->
No.
+%% Extract information that the processes involved are expected to
+%% "publish" in their process dictionaries. Simple but backhanded.
+info_port(Pid) ->
+ {_, PD} = process_info(Pid, dictionary),
+ {_, T} = lists:keyfind({diameter_peer_fsm, start}, 1, PD),
+ {TPid, {_Type, TMod, _Cfg}} = T,
+ {_, TD} = process_info(TPid, dictionary),
+ {_, Data} = lists:keyfind({TMod, info}, 1, TD),
+ [{owner, TPid},
+ {module, TMod}
+ | try TMod:info(Data) catch _:_ -> [] end].
+
+%% Use the fields names from diameter_caps instead of
+%% diameter_base_CER to distinguish between the 2-tuple values
+%% compared to the single capabilities values. Note also that the
+%% returned list is tagged 'caps' rather than 'capabilities' to
+%% emphasize the difference.
info_caps(#diameter_caps{} = C) ->
lists:zip(record_info(fields, diameter_caps), tl(tuple_to_list(C))).
info_apps(#state{service = #diameter_service{applications = Apps}}) ->
lists:map(fun mk_app/1, Apps).
-mk_app(#diameter_app{alias = Alias,
- dictionary = Dict,
- module = ModX,
- id = Id}) ->
- [{alias, Alias},
- {dictionary, Dict},
- {module, ModX},
- {id, Id}].
+mk_app(#diameter_app{} = A) ->
+ lists:zip(record_info(fields, diameter_app), tl(tuple_to_list(A))).
+
+%% info_pending/1
+%%
+%% One entry for each outgoing request whose answer is outstanding.
info_pending(#state{} = S) ->
MatchSpec = [{{'$1',
@@ -2894,4 +3329,67 @@ info_pending(#state{} = S) ->
{{transport, '$2'}},
{{from, '$3'}}]}}]}],
- ets:select(?REQUEST_TABLE, MatchSpec).
+ try
+ ets:select(?REQUEST_TABLE, MatchSpec)
+ catch
+ error: badarg -> [] %% service has gone down
+ end.
+
+%% info_connections/1
+%%
+%% One entry per transport connection. Statistics for each entry are
+%% for the peer pid only.
+
+info_connections(S) ->
+ ConnL = conn_list(S),
+ Stats = diameter_stats:read([P || L <- ConnL, {peer, {P,_}} <- L]),
+ [L ++ [stats([P], Stats)] || L <- ConnL, {peer, {P,_}} <- L].
+
+conn_list(S) ->
+ lists:append(dict:fold(fun conn_acc/3, [], peer_dict(S, dict:new()))).
+
+conn_acc(Ref, Peers, Acc) ->
+ [[[{ref, Ref} | L] || L <- Peers, lists:keymember(peer, 1, L)]
+ | Acc].
+
+stats(Refs, Stats) ->
+ {statistics, dict:to_list(lists:foldl(fun(R,D) ->
+ stats_acc(R, D, Stats)
+ end,
+ dict:new(),
+ Refs))}.
+
+stats_acc(Ref, Dict, Stats) ->
+ lists:foldl(fun({C,N}, D) -> dict:update_counter(C, N, D) end,
+ Dict,
+ proplists:get_value(Ref, Stats, [])).
+
+%% info_peers/1
+%%
+%% One entry per peer Origin-Host. Statistics for each entry are
+%% accumulated values for all peer pids.
+
+info_peers(S) ->
+ {PeerD, RefD} = lists:foldl(fun peer_acc/2,
+ {dict:new(), dict:new()},
+ conn_list(S)),
+ Refs = lists:append(dict:fold(fun(_, Rs, A) -> [Rs|A] end,
+ [],
+ RefD)),
+ Stats = diameter_stats:read(Refs),
+ dict:fold(fun(OH, Cs, A) ->
+ Rs = dict:fetch(OH, RefD),
+ [{OH, [{connections, Cs}, stats(Rs, Stats)]} | A]
+ end,
+ [],
+ PeerD).
+
+peer_acc(Peer, {PeerD, RefD}) ->
+ [{TPid, _}, [{origin_host, {_, OH}} | _]]
+ = [proplists:get_value(K, Peer) || K <- [peer, caps]],
+ {dict:append(OH, Peer, PeerD), dict:append(OH, TPid, RefD)}.
+
+%% info_options/1
+
+info_options(S) ->
+ S#state.options.
diff --git a/lib/diameter/src/base/diameter_session.erl b/lib/diameter/src/base/diameter_session.erl
index 4c468f207c..3b236f109a 100644
--- a/lib/diameter/src/base/diameter_session.erl
+++ b/lib/diameter/src/base/diameter_session.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -20,6 +20,7 @@
-module(diameter_session).
-export([sequence/0,
+ sequence/1,
session_id/1,
origin_state_id/0]).
@@ -30,7 +31,7 @@
-define(INT32, 16#FFFFFFFF).
%% ---------------------------------------------------------------------------
-%% # sequence/0
+%% # sequence/0-1
%%
%% Output: 32-bit
%% ---------------------------------------------------------------------------
@@ -77,6 +78,15 @@ sequence() ->
Instr = {_Pos = 2, _Incr = 1, _Threshold = ?INT32, _SetVal = 0},
ets:update_counter(diameter_sequence, sequence, Instr).
+-spec sequence(diameter:sequence())
+ -> diameter:'Unsigned32'().
+
+sequence({_,32}) ->
+ sequence();
+
+sequence({H,N}) ->
+ (H bsl N) bor (sequence() band (1 bsl N - 1)).
+
%% ---------------------------------------------------------------------------
%% # origin_state_id/0
%% ---------------------------------------------------------------------------
diff --git a/lib/diameter/src/base/diameter_stats.erl b/lib/diameter/src/base/diameter_stats.erl
index 71479afa95..70727d068e 100644
--- a/lib/diameter/src/base/diameter_stats.erl
+++ b/lib/diameter/src/base/diameter_stats.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -22,14 +22,13 @@
%%
-module(diameter_stats).
--compile({no_auto_import, [monitor/2]}).
-behaviour(gen_server).
--export([reg/1, reg/2,
- incr/1, incr/2, incr/3,
+-export([reg/2, reg/1,
+ incr/3, incr/1,
read/1,
- flush/0, flush/1]).
+ flush/1]).
%% supervisor callback
-export([start_link/0]).
@@ -48,123 +47,105 @@
-include("diameter_internal.hrl").
-%% ets table containing stats. reg(Pid, Ref) inserts a {Pid, Ref},
-%% incr(Counter, X, N) updates the counter keyed at {Counter, X}, and
-%% Pid death causes counters keyed on {Counter, Pid} to be deleted and
-%% added to those keyed on {Counter, Ref}.
+%% ets table containing 2-tuple stats. reg(Pid, Ref) inserts a {Pid,
+%% Ref}, incr(Counter, X, N) updates the counter keyed at {Counter,
+%% X}, and Pid death causes counters keyed on {Counter, Pid} to be
+%% deleted and added to those keyed on {Counter, Ref}.
-define(TABLE, ?MODULE).
%% Name of registered server.
-define(SERVER, ?MODULE).
-%% Entries in the table.
--define(REC(Key, Value), {Key, Value}).
-
%% Server state.
-record(state, {id = now()}).
-type counter() :: any().
--type contrib() :: any().
-
-%%% ---------------------------------------------------------------------------
-%%% # reg(Pid, Contrib)
-%%%
-%%% Description: Register a process as a contributor of statistics
-%%% associated with a specified term. Statistics can be
-%%% contributed by specifying either Pid or Contrib as
-%%% the second argument to incr/3. Statistics contributed
-%%% by Pid are folded into the corresponding entry for
-%%% Contrib when the process dies.
-%%%
-%%% Contrib can be any term but should not be a pid
-%%% passed as the first argument to reg/2. Subsequent
-%%% registrations for the same Pid overwrite the association
-%%% ---------------------------------------------------------------------------
-
--spec reg(pid(), contrib())
- -> true.
+-type ref() :: any().
+
+%% ---------------------------------------------------------------------------
+%% # reg(Pid, Ref)
+%%
+%% Register a process as a contributor of statistics associated with a
+%% specified term. Statistics can be contributed by specifying either
+%% Pid or Ref as the second argument to incr/3. Statistics contributed
+%% by Pid are folded into the corresponding entry for Ref when the
+%% process dies.
+%% ---------------------------------------------------------------------------
+
+-spec reg(pid(), ref())
+ -> boolean().
-reg(Pid, Contrib)
+reg(Pid, Ref)
when is_pid(Pid) ->
- call({reg, Pid, Contrib}).
+ call({reg, Pid, Ref}).
--spec reg(contrib())
+-spec reg(ref())
-> true.
reg(Ref) ->
reg(self(), Ref).
-%%% ---------------------------------------------------------------------------
-%%% # incr(Counter, Contrib, N)
-%%%
-%%% Description: Increment a counter for the specified contributor.
-%%%
-%%% Contrib will typically be an argument passed to reg/2
-%%% but there's nothing that requires this. In particular,
-%%% if Contrib is a pid that hasn't been registered then
-%%% counters are unaffected by the death of the process.
-%%% ---------------------------------------------------------------------------
-
--spec incr(counter(), contrib(), integer())
- -> integer().
+%% ---------------------------------------------------------------------------
+%% # incr(Counter, Ref, N)
+%%
+%% Increment a counter for the specified contributor.
+%%
+%% Ref will typically be an argument passed to reg/2 but there's
+%% nothing that requires this. Only registered pids can contribute
+%% counters however, otherwise incr/3 is a no-op.
+%% ---------------------------------------------------------------------------
-incr(Ctr, Contrib, N) ->
- update_counter({Ctr, Contrib}, N).
+-spec incr(counter(), ref(), integer())
+ -> integer() | false.
-incr(Ctr, N)
+incr(Ctr, Ref, N)
when is_integer(N) ->
- incr(Ctr, self(), N);
-
-incr(Ctr, Contrib) ->
- incr(Ctr, Contrib, 1).
+ update_counter({Ctr, Ref}, N).
incr(Ctr) ->
incr(Ctr, self(), 1).
-%%% ---------------------------------------------------------------------------
-%%% # read(Contribs)
-%%%
-%%% Description: Retrieve counters for the specified contributors.
-%%% ---------------------------------------------------------------------------
+%% ---------------------------------------------------------------------------
+%% # read(Refs)
+%%
+%% Retrieve counters for the specified contributors.
+%% ---------------------------------------------------------------------------
--spec read([contrib()])
- -> [{contrib(), [{counter(), integer()}]}].
+-spec read([ref()])
+ -> [{ref(), [{counter(), integer()}]}].
-read(Contribs) ->
- lists:foldl(fun(?REC({T,C}, N), D) -> orddict:append(C, {T,N}, D) end,
+read(Refs) ->
+ read(Refs, false).
+
+read(Refs, B) ->
+ MatchSpec = [{{{'_', '$1'}, '_'},
+ [?ORCOND([{'=:=', '$1', {const, R}}
+ || R <- Refs])],
+ ['$_']}],
+ L = ets:select(?TABLE, MatchSpec),
+ B andalso delete(L),
+ lists:foldl(fun({{C,R}, N}, D) -> orddict:append(R, {C,N}, D) end,
orddict:new(),
- ets:select(?TABLE, [{?REC({'_', '$1'}, '_'),
- [?ORCOND([{'=:=', '$1', {const, C}}
- || C <- Contribs])],
- ['$_']}])).
-
-%%% ---------------------------------------------------------------------------
-%%% # flush(Contrib)
-%%%
-%%% Description: Retrieve and delete statistics for the specified
-%%% contributor.
-%%%
-%%% If Contrib is a pid registered with reg/2 then statistics
-%%% for both and its associated contributor are retrieved.
-%%% ---------------------------------------------------------------------------
-
--spec flush(contrib())
- -> [{counter(), integer()}].
+ L).
+
+%% ---------------------------------------------------------------------------
+%% # flush(Refs)
+%%
+%% Retrieve and delete statistics for the specified contributors.
+%% ---------------------------------------------------------------------------
+
+-spec flush([ref()])
+ -> [{ref(), {counter(), integer()}}].
-flush(Contrib) ->
+flush(Refs) ->
try
- call({flush, Contrib})
+ call({flush, Refs})
catch
exit: _ ->
[]
end.
-flush() ->
- flush(self()).
-
-%%% ---------------------------------------------------------
-%%% EXPORTED INTERNAL FUNCTIONS
-%%% ---------------------------------------------------------
+%% ===========================================================================
start_link() ->
ServerName = {local, ?SERVER},
@@ -179,18 +160,16 @@ state() ->
uptime() ->
call(uptime).
-%%% ----------------------------------------------------------
-%%% # init(_)
-%%%
-%%% Output: {ok, State}
-%%% ----------------------------------------------------------
+%% ----------------------------------------------------------
+%% # init/1
+%% ----------------------------------------------------------
init([]) ->
ets:new(?TABLE, [named_table, ordered_set, public]),
{ok, #state{}}.
%% ----------------------------------------------------------
-%% handle_call(Request, From, State)
+%% # handle_call/3
%% ----------------------------------------------------------
handle_call(state, _, State) ->
@@ -199,31 +178,31 @@ handle_call(state, _, State) ->
handle_call(uptime, _, #state{id = Time} = State) ->
{reply, diameter_lib:now_diff(Time), State};
-handle_call({reg, Pid, Contrib}, _From, State) ->
- monitor(not ets:member(?TABLE, Pid), Pid),
- {reply, insert(?REC(Pid, Contrib)), State};
+handle_call({incr, T}, _, State) ->
+ {reply, update_counter(T), State};
-handle_call({flush, Contrib}, _From, State) ->
- {reply, fetch(Contrib), State};
+handle_call({reg, Pid, Ref}, _From, State) ->
+ B = ets:insert_new(?TABLE, {Pid, Ref}),
+ B andalso erlang:monitor(process, Pid),
+ {reply, B, State};
+
+handle_call({flush, Refs}, _From, State) ->
+ {reply, read(Refs, true), State};
handle_call(Req, From, State) ->
?UNEXPECTED([Req, From]),
{reply, nok, State}.
%% ----------------------------------------------------------
-%% handle_cast(Request, State)
+%% # handle_cast/2
%% ----------------------------------------------------------
-handle_cast({incr, Rec}, State) ->
- update_counter(Rec),
- {noreply, State};
-
handle_cast(Msg, State) ->
?UNEXPECTED([Msg]),
{noreply, State}.
%% ----------------------------------------------------------
-%% handle_info(Request, State)
+%% # handle_info/2
%% ----------------------------------------------------------
handle_info({'DOWN', _MRef, process, Pid, _}, State) ->
@@ -235,91 +214,62 @@ handle_info(Info, State) ->
{noreply, State}.
%% ----------------------------------------------------------
-%% terminate(Reason, State)
+%% # terminate/2
%% ----------------------------------------------------------
terminate(_Reason, _State) ->
ok.
%% ----------------------------------------------------------
-%% code_change(OldVsn, State, Extra)
+%% # code_change/3
%% ----------------------------------------------------------
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
-%%% ---------------------------------------------------------
-%%% INTERNAL FUNCTIONS
-%%% ---------------------------------------------------------
-
-%% monitor/2
-
-monitor(true, Pid) ->
- erlang:monitor(process, Pid);
-monitor(false = No, _) ->
- No.
+%% ===========================================================================
%% down/1
down(Pid) ->
- L = ets:match_object(?TABLE, ?REC({'_', Pid}, '_')),
- [?REC(_, Ref) = T] = lookup(Pid),
+ down(lookup(Pid), ets:match_object(?TABLE, {{'_', Pid}, '_'})).
+
+down([{_, Ref} = T], L) ->
fold(Ref, L),
- delete_object(T),
+ delete([T|L]);
+down([], L) -> %% flushed
delete(L).
-%% Fold Pid-based entries into Ref-based ones.
+%% Fold pid-based entries into ref-based ones.
fold(Ref, L) ->
- lists:foreach(fun(?REC({K, _}, V)) -> update_counter({{K, Ref}, V}) end,
- L).
-
-delete(Objs) ->
- lists:foreach(fun delete_object/1, Objs).
-
-%% fetch/1
-
-fetch(X) ->
- MatchSpec = [{?REC({'_', '$1'}, '_'),
- [?ORCOND([{'==', '$1', {const, T}} || T <- [X | ref(X)]])],
- ['$_']}],
- L = ets:select(?TABLE, MatchSpec),
- delete(L),
- D = lists:foldl(fun sum/2, dict:new(), L),
- dict:to_list(D).
-
-sum({{Ctr, _}, N}, Dict) ->
- dict:update(Ctr, fun(V) -> V+N end, N, Dict).
-
-ref(Pid)
- when is_pid(Pid) ->
- ets:select(?TABLE, [{?REC(Pid, '$1'), [], ['$1']}]);
-ref(_) ->
- [].
+ lists:foreach(fun({{K, _}, V}) -> update_counter({{K, Ref}, V}) end, L).
%% update_counter/2
%%
-%% From an arbitrary request process. Cast to the server process to
-%% insert a new element if the counter doesn't exists so that two
-%% processes don't do so simultaneously.
+%% From an arbitrary process. Call to the server process to insert a
+%% new element if the counter doesn't exists so that two processes
+%% don't insert simultaneously.
update_counter(Key, N) ->
try
ets:update_counter(?TABLE, Key, N)
catch
error: badarg ->
- cast({incr, ?REC(Key, N)})
+ call({incr, {Key, N}})
end.
%% update_counter/1
%%
-%% From the server process.
+%% From the server process, when update_counter/2 failed due to a
+%% non-existent entry.
-update_counter(?REC(Key, N) = T) ->
+update_counter({{_Ctr, Ref} = Key, N} = T) ->
try
ets:update_counter(?TABLE, Key, N)
catch
error: badarg ->
- insert(T)
+ (not is_pid(Ref) orelse ets:member(?TABLE, Ref))
+ andalso begin insert(T), N end
end.
insert(T) ->
@@ -328,13 +278,8 @@ insert(T) ->
lookup(Key) ->
ets:lookup(?TABLE, Key).
-delete_object(T) ->
- ets:delete_object(?TABLE, T).
-
-%% cast/1
-
-cast(Msg) ->
- gen_server:cast(?SERVER, Msg).
+delete(Objs) ->
+ lists:foreach(fun({K,_}) -> ets:delete(?TABLE, K) end, Objs).
%% call/1
diff --git a/lib/diameter/src/base/diameter_watchdog.erl b/lib/diameter/src/base/diameter_watchdog.erl
index fb22fd8275..243ad0a986 100644
--- a/lib/diameter/src/base/diameter_watchdog.erl
+++ b/lib/diameter/src/base/diameter_watchdog.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -43,20 +43,24 @@
-include("diameter_internal.hrl").
-define(DEFAULT_TW_INIT, 30000). %% RFC 3539 ch 3.4.1
+-define(NOMASK, {0,32}). %% default sequence mask
-record(watchdog,
{%% PCB - Peer Control Block; see RFC 3539, Appendix A
status = initial :: initial | okay | suspect | down | reopen,
- pending = false :: boolean(),
+ pending = false :: boolean(), %% DWA
tw :: 6000..16#FFFFFFFF | {module(), atom(), list()},
%% {M,F,A} -> integer() >= 0
num_dwa = 0 :: -1 | non_neg_integer(),
%% number of DWAs received during reopen
%% end PCB
- parent = self() :: pid(),
- transport :: pid(),
+ parent = self() :: pid(), %% service process
+ transport :: pid() | undefined, %% peer_fsm process
tref :: reference(), %% reference for current watchdog timer
- message_data}). %% term passed into diameter_service with message
+ message_data, %% term passed into diameter_service with message
+ sequence :: diameter:sequence(), %% mask
+ restrict :: {diameter:restriction(), boolean()},
+ shutdown = false :: boolean()}).
%% start/2
%%
@@ -64,6 +68,13 @@
%% that a failed capabilities exchange produces the desired exit
%% reason.
+-spec start(Type, {RecvData, [Opt], SvcName, #diameter_service{}})
+ -> {reference(), pid()}
+ when Type :: {connect|accept, diameter:transport_ref()},
+ RecvData :: term(),
+ Opt :: diameter:transport_opt(),
+ SvcName :: diameter:service_name().
+
start({_,_} = Type, T) ->
Ref = make_ref(),
{ok, Pid} = diameter_watchdog_sup:start_child({Ref, {Type, self(), T}}),
@@ -102,7 +113,7 @@ i({_, Pid, _} = T) -> %% from old code
erlang:monitor(process, Pid),
make_state(T).
-make_state({T, Pid, {ConnT,
+make_state({T, Pid, {RecvData,
Opts,
SvcName,
#diameter_service{applications = Apps,
@@ -111,12 +122,23 @@ make_state({T, Pid, {ConnT,
random:seed(now()),
putr(restart, {T, Opts, Svc}), %% save seeing it in trace
putr(dwr, dwr(Caps)), %%
+ {_,_} = Mask = call(Pid, sequence),
+ Restrict = call(Pid, restriction),
+ Nodes = restrict_nodes(Restrict),
#watchdog{parent = Pid,
- transport = monitor(diameter_peer_fsm:start(T, Opts, Svc)),
+ transport = monitor(diameter_peer_fsm:start(T,
+ Opts,
+ {Mask, Nodes, Svc})),
tw = proplists:get_value(watchdog_timer,
Opts,
?DEFAULT_TW_INIT),
- message_data = {ConnT, SvcName, Apps}}.
+ message_data = {RecvData, SvcName, Apps, Mask},
+ sequence = Mask,
+ restrict = {Restrict, lists:member(node(), Nodes)}}.
+
+%% Retrieve the sequence mask from the parent from the parent, rather
+%% than having it passed into init/1, for upgrade reasons: the call to
+%% diameter_service:receive_message/3 passes back the mask.
%% handle_call/3
@@ -130,17 +152,46 @@ handle_cast(_, State) ->
%% handle_info/2
-handle_info(T, State) ->
+handle_info(T, #watchdog{} = State) ->
case transition(T, State) of
ok ->
{noreply, State};
- #watchdog{status = X} = S ->
- ?LOGC(X =/= State#watchdog.status, transition, X),
+ #watchdog{} = S ->
+ event(State, S),
{noreply, S};
stop ->
?LOG(stop, T),
+ event(State, State#watchdog{status = down}),
{stop, {shutdown, T}, State}
- end.
+ end;
+
+handle_info(T, S) ->
+ handle_info(T, upgrade(S)).
+
+upgrade(S) ->
+ #watchdog{} = list_to_tuple(tuple_to_list(S)
+ ++ [?NOMASK, {nodes, true}, false]).
+
+event(#watchdog{status = T}, #watchdog{status = T}) ->
+ ok;
+
+event(#watchdog{transport = undefined}, #watchdog{transport = undefined}) ->
+ ok;
+
+event(#watchdog{status = From, transport = F, parent = Pid},
+ #watchdog{status = To, transport = T}) ->
+ E = {tpid(F,T), From, To},
+ notify(Pid, E),
+ ?LOG(transition, {self(), E}).
+
+tpid(_, Pid)
+ when is_pid(Pid) ->
+ Pid;
+tpid(Pid, _) ->
+ Pid.
+
+notify(Pid, E) ->
+ Pid ! {watchdog, self(), E}.
%% terminate/2
@@ -176,9 +227,10 @@ transition({shutdown, Pid}, #watchdog{parent = Pid,
down = S, %% sanity check
stop;
transition({shutdown = T, Pid}, #watchdog{parent = Pid,
- transport = TPid}) ->
+ transport = TPid}
+ = S) ->
TPid ! {T, self()},
- ok;
+ S#watchdog{shutdown = true};
%% Parent process has died,
transition({'DOWN', _, process, Pid, _Reason},
@@ -212,9 +264,10 @@ transition({close, TPid, _Reason}, #watchdog{transport = TPid}) ->
transition({open, TPid, Hosts, T} = Open,
#watchdog{transport = TPid,
status = initial,
- parent = Pid}
+ parent = Pid,
+ restrict = {_, R}}
= S) ->
- case okay(getr(restart), Hosts) of
+ case okay(getr(restart), Hosts, R) of
okay ->
open(Pid, {TPid, T}),
set_watchdog(S#watchdog{status = okay});
@@ -229,12 +282,15 @@ transition({open, TPid, Hosts, T} = Open,
transition({open = P, TPid, _Hosts, T},
#watchdog{transport = TPid,
+ parent = Pid,
status = down}
= S) ->
%% Store the info we need to notify the parent to reopen the
%% connection after the requisite DWA's are received, at which
- %% time we eraser(open).
+ %% time we eraser(open). The reopen message is a later addition,
+ %% to communicate the new capabilities as soon as they're known.
putr(P, {TPid, T}),
+ Pid ! {reopen, self(), {TPid, T}},
set_watchdog(send_watchdog(S#watchdog{status = reopen,
num_dwa = 0}));
@@ -248,11 +304,14 @@ transition({open = P, TPid, _Hosts, T},
transition({'DOWN', _, process, TPid, _},
#watchdog{transport = TPid,
- status = initial}) ->
+ status = S,
+ shutdown = D})
+ when S == initial;
+ D ->
stop;
-transition({'DOWN', _, process, Pid, _},
- #watchdog{transport = Pid}
+transition({'DOWN', _, process, TPid, _},
+ #watchdog{transport = TPid}
= S) ->
failover(S),
close(S),
@@ -283,6 +342,15 @@ transition({state, Pid}, #watchdog{status = S}) ->
%% ===========================================================================
+%% Only call "upwards", to the parent service.
+call(Pid, Req) ->
+ try
+ gen_server:call(Pid, Req, infinity)
+ catch
+ exit: Reason ->
+ exit({shutdown, {Req, Reason}})
+ end.
+
monitor(Pid) ->
erlang:monitor(process, Pid),
Pid.
@@ -296,26 +364,36 @@ getr(Key) ->
eraser(Key) ->
erase({?MODULE, Key}).
-%% encode/1
+%% encode/2
-encode(Msg) ->
- #diameter_packet{bin = Bin} = diameter_codec:encode(?BASE, Msg),
+encode(Msg, Mask) ->
+ Seq = diameter_session:sequence(Mask),
+ Hdr = #diameter_header{version = ?DIAMETER_VERSION,
+ end_to_end_id = Seq,
+ hop_by_hop_id = Seq},
+ Pkt = #diameter_packet{header = Hdr,
+ msg = Msg},
+ #diameter_packet{bin = Bin} = diameter_codec:encode(?BASE, Pkt),
Bin.
-%% okay/2
+%% okay/3
-okay({{accept, Ref}, _, _}, Hosts) ->
+okay({{accept, Ref}, _, _}, Hosts, Restrict) ->
T = {?MODULE, connection, Ref, Hosts},
diameter_reg:add(T),
- okay(diameter_reg:match(T));
+ if Restrict ->
+ okay(diameter_reg:match(T));
+ true ->
+ okay
+ end;
%% Register before matching so that at least one of two registering
-%% processes will match the other. (Which can't happen as long as
-%% diameter_peer_fsm guarantees at most one open connection to the same
-%% peer.)
+%% processes will match the other.
-okay({{connect, _}, _, _}, _) ->
+okay({{connect, _}, _, _}, _, _) ->
okay.
+%% okay/2
+
%% The peer hasn't been connected recently ...
okay([{_,P}]) ->
P = self(), %% assert
@@ -371,9 +449,10 @@ close(#watchdog{parent = Pid}) ->
%% send_watchdog/1
send_watchdog(#watchdog{pending = false,
- transport = TPid}
+ transport = TPid,
+ sequence = Mask}
= S) ->
- TPid ! {send, encode(getr(dwr))},
+ TPid ! {send, encode(getr(dwr), Mask)},
?LOG(send, 'DWR'),
S#watchdog{pending = true}.
@@ -385,7 +464,7 @@ recv(Name, Pkt, S) ->
rcv(Name, Pkt, S),
NS
catch
- throw: {?MODULE, throwaway, #watchdog{} = NS} ->
+ {?MODULE, throwaway, #watchdog{} = NS} ->
NS
end.
@@ -408,6 +487,14 @@ throwaway(S) ->
throw({?MODULE, throwaway, S}).
%% rcv/2
+%%
+%% The lack of Hop-by-Hop and End-to-End Identifiers checks in a
+%% received DWA is intentional. The purpose of the message is to
+%% demonstrate life but a peer that consistently bungles it by sending
+%% the wrong identifiers causes the connection to toggle between OPEN
+%% and SUSPECT, with failover and failback as result, despite there
+%% being no real problem with connectivity. Thus, relax and accept any
+%% incoming DWA as being in response to an outgoing DWR.
%% INITIAL Receive DWA Pending = FALSE
%% Throwaway() INITIAL
@@ -526,7 +613,7 @@ timeout(#watchdog{status = T,
= S)
when T == suspect;
T == reopen, P, N < 0 ->
- exit(TPid, shutdown),
+ exit(TPid, {shutdown, watchdog_timeout}),
close(S),
S#watchdog{status = down};
@@ -571,19 +658,40 @@ restart(#watchdog{transport = undefined} = S) ->
restart(S) ->
S.
+%% restart/2
+%%
%% Only restart the transport in the connecting case. For an accepting
-%% transport, we've registered the peer connection when leaving state
-%% initial and this is used by a new accepting process to realize that
-%% it's actually in state down rather then initial when receiving
-%% notification of an open connection.
-
-restart({{connect, _} = T, Opts, Svc}, #watchdog{parent = Pid} = S) ->
+%% transport, there's no guarantee that an accepted connection in a
+%% restarted transport if from the peer we've lost contact with so
+%% have to be prepared for another watchdog to handle it. This is what
+%% the diameter_reg registration in this module is for: the peer
+%% connection is registered when leaving state initial and this is
+%% used by a new accepting watchdog to realize that it's actually in
+%% state down rather then initial when receiving notification of an
+%% open connection.
+
+restart({{connect, _} = T, Opts, Svc}, #watchdog{parent = Pid,
+ sequence = Mask,
+ restrict = {R,_}}
+ = S) ->
Pid ! {reconnect, self()},
- S#watchdog{transport = monitor(diameter_peer_fsm:start(T, Opts, Svc))};
+ Nodes = restrict_nodes(R),
+ S#watchdog{transport = monitor(diameter_peer_fsm:start(T,
+ Opts,
+ {Mask, Nodes, Svc})),
+ restrict = {R, lists:member(node(), Nodes)}};
+
+%% No restriction on the number of connections to the same peer: just
+%% die. Note that a state machine never enters state REOPEN in this
+%% case.
+restart({{accept, _}, _, _}, #watchdog{restrict = {_, false}}) ->
+ stop;
+
+%% Otherwise hang around until told to die.
restart({{accept, _}, _, _}, S) ->
S.
-%% Don't currently use Opts/Svc in the accept case but having them in
-%% the process dictionary is helpful if the process dies unexpectedly.
+
+%% Don't currently use Opts/Svc in the accept case.
%% dwr/1
@@ -593,3 +701,22 @@ dwr(#diameter_caps{origin_host = OH,
['DWR', {'Origin-Host', OH},
{'Origin-Realm', OR},
{'Origin-State-Id', OSI}].
+
+%% restrict_nodes/1
+
+restrict_nodes(false) ->
+ [];
+
+restrict_nodes(nodes) ->
+ [node() | nodes()];
+
+restrict_nodes(node) ->
+ [node()];
+
+restrict_nodes(Nodes)
+ when [] == Nodes;
+ is_atom(hd(Nodes)) ->
+ Nodes;
+
+restrict_nodes(F) ->
+ diameter_lib:eval(F).
diff --git a/lib/diameter/src/modules.mk b/lib/diameter/src/modules.mk
index 7a700a6d53..25207625be 100644
--- a/lib/diameter/src/modules.mk
+++ b/lib/diameter/src/modules.mk
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2010-2011. All Rights Reserved.
+# Copyright Ericsson AB 2010-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -58,6 +58,7 @@ RT_MODULES = \
transport/diameter_tcp_sup \
transport/diameter_sctp \
transport/diameter_sctp_sup \
+ transport/diameter_transport \
transport/diameter_transport_sup
# Handwritten (compile time) modules not included in the app file.
@@ -102,3 +103,12 @@ EXAMPLES = \
dict/rfc4072_eap.dia \
dict/rfc4590_digest.dia \
dict/rfc4740_sip.dia
+
+# Registered server names.
+
+REGISTERED = \
+ diameter_config \
+ diameter_peer \
+ diameter_reg \
+ diameter_stats \
+ diameter_sync
diff --git a/lib/diameter/src/transport/diameter_etcp.erl b/lib/diameter/src/transport/diameter_etcp.erl
index d925d62545..cd62cf34fa 100644
--- a/lib/diameter/src/transport/diameter_etcp.erl
+++ b/lib/diameter/src/transport/diameter_etcp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -36,7 +36,9 @@
send/2,
close/1,
setopts/2,
- port/1]).
+ sockname/1,
+ peername/1,
+ getstat/1]).
%% child start
-export([start_link/1]).
@@ -113,10 +115,20 @@ close(Pid) ->
setopts(_, _) ->
ok.
-%% port/1
+%% sockname/1
-port(_) ->
- 3868. %% We have no local port: fake it.
+sockname(_) ->
+ {error, ?MODULE}.
+
+%% peername/1
+
+peername(_) ->
+ {error, ?MODULE}.
+
+%% getstat/1
+
+getstat(_) ->
+ {error, ?MODULE}.
%% start_link/1
diff --git a/lib/diameter/src/transport/diameter_sctp.erl b/lib/diameter/src/transport/diameter_sctp.erl
index 68b0342cd5..79b8b851fb 100644
--- a/lib/diameter/src/transport/diameter_sctp.erl
+++ b/lib/diameter/src/transport/diameter_sctp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -37,12 +37,18 @@
code_change/3,
terminate/2]).
+-export([info/1]). %% service_info callback
+
-export([ports/0,
ports/1]).
-include_lib("kernel/include/inet_sctp.hrl").
-include_lib("diameter/include/diameter.hrl").
+%% Keys into process dictionary.
+-define(INFO_KEY, info).
+-define(REF_KEY, ref).
+
-define(ERROR(T), erlang:error({T, ?MODULE, ?LINE})).
%% The default port for a listener.
@@ -62,6 +68,7 @@
-record(transport,
{parent :: pid(),
mode :: {accept, pid()}
+ | accept
| {connect, {list(inet:ip_address()), uint(), list()}}
%% {RAs, RP, Errors}
| connect,
@@ -134,6 +141,24 @@ start_link(T) ->
diameter_lib:spawn_opts(server, [])).
%% ---------------------------------------------------------------------------
+%% # info/1
+%% ---------------------------------------------------------------------------
+
+info({gen_sctp, Sock}) ->
+ lists:flatmap(fun(K) -> info(K, Sock) end,
+ [{socket, sockname},
+ {peer, peername},
+ {statistics, getstat}]).
+
+info({K,F}, Sock) ->
+ case inet:F(Sock) of
+ {ok, V} ->
+ [{K,V}];
+ _ ->
+ []
+ end.
+
+%% ---------------------------------------------------------------------------
%% # init/1
%% ---------------------------------------------------------------------------
@@ -157,7 +182,7 @@ i({connect, Pid, Opts, Addrs, Ref}) ->
RAs = [diameter_lib:ipaddr(A) || {raddr, A} <- As],
[RP] = [P || {rport, P} <- Ps] ++ [P || P <- [?DEFAULT_PORT], [] == Ps],
{LAs, Sock} = open(Addrs, Rest, 0),
- putr(ref, Ref),
+ putr(?REF_KEY, Ref),
proc_lib:init_ack({ok, self(), LAs}),
erlang:monitor(process, Pid),
#transport{parent = Pid,
@@ -169,7 +194,7 @@ i({connect, _, _, _} = T) -> %% from old code
%% An accepting transport spawned by diameter.
i({accept, Pid, LPid, Sock, Ref})
when is_pid(Pid) ->
- putr(ref, Ref),
+ putr(?REF_KEY, Ref),
proc_lib:init_ack({ok, self()}),
erlang:monitor(process, Pid),
erlang:monitor(process, LPid),
@@ -181,7 +206,7 @@ i({accept, _, _, _} = T) -> %% from old code
%% An accepting transport spawned at association establishment.
i({accept, Ref, LPid, Sock, Id}) ->
- putr(ref, Ref),
+ putr(?REF_KEY, Ref),
proc_lib:init_ack({ok, self()}),
MRef = erlang:monitor(process, LPid),
%% Wait for a signal that the transport has been started before
@@ -325,6 +350,11 @@ terminate(_, #transport{assoc_id = undefined}) ->
ok;
terminate(_, #transport{socket = Sock,
+ mode = accept,
+ assoc_id = Id}) ->
+ close(Sock, Id);
+
+terminate(_, #transport{socket = Sock,
mode = {accept, _},
assoc_id = Id}) ->
close(Sock, Id);
@@ -356,13 +386,16 @@ start_timer(S) ->
%% Incoming message from SCTP.
l({sctp, Sock, _RA, _RP, Data} = Msg, #listener{socket = Sock} = S) ->
- setopts(Sock),
- case find(Data, S) of
+ Id = assoc_id(Data),
+
+ try find(Id, Data, S) of
{TPid, NewS} ->
- TPid ! Msg,
+ TPid ! {peeloff, peeloff(Sock, Id, TPid), Msg},
NewS;
false ->
S
+ after
+ setopts(Sock)
end;
%% Transport is asking message to be sent. See send/3 for why the send
@@ -430,15 +463,19 @@ t(T,S) ->
%% transition/2
-%% Incoming message.
-transition({sctp, Sock, _RA, _RP, Data}, #transport{socket = Sock,
- mode = {accept, _}}
- = S) ->
- recv(Data, S);
+%% Listening process is transfering ownership of an association.
+transition({peeloff, Sock, {sctp, LSock, _RA, _RP, _Data} = Msg},
+ #transport{mode = {accept, _},
+ socket = LSock}
+ = S) ->
+ transition(Msg, S#transport{socket = Sock});
-transition({sctp, Sock, _RA, _RP, Data}, #transport{socket = Sock} = S) ->
+%% Incoming message.
+transition({sctp, _Sock, _RA, _RP, Data}, #transport{socket = Sock} = S) ->
setopts(Sock),
recv(Data, S);
+%% Don't match on Sock since in R15B01 it can be the listening socket
+%% in the (peeled-off) accept case, which is likely a bug.
%% Outgoing message.
transition({diameter, {send, Msg}}, S) ->
@@ -456,13 +493,18 @@ transition({diameter, {close, Pid}}, #transport{parent = Pid}) ->
transition({diameter, {tls, _Ref, _Type, _Bool}}, _) ->
stop;
+%% Parent process has died.
+transition({'DOWN', _, process, Pid, _}, #transport{parent = Pid}) ->
+ stop;
+
%% Listener process has died.
transition({'DOWN', _, process, Pid, _}, #transport{mode = {accept, Pid}}) ->
stop;
-%% Parent process has died.
-transition({'DOWN', _, process, Pid, _}, #transport{parent = Pid}) ->
- stop;
+%% Ditto but we have ownership of the association. It might be that
+%% we'll go down anyway though.
+transition({'DOWN', _, process, _Pid, _}, #transport{mode = accept}) ->
+ ok;
%% Request for the local port number.
transition({resolve_port, Pid}, #transport{socket = Sock})
@@ -521,14 +563,6 @@ send(Bin, #transport{streams = {_, OS},
%% send/3
-%% Messages have to be sent from the controlling process, which is
-%% probably a bug. Sending from here causes an inet_reply, Sock,
-%% Status} message to be sent to the controlling process while
-%% gen_sctp:send/4 here hangs.
-send(StreamId, Bin, #transport{assoc_id = AId,
- mode = {accept, LPid}}) ->
- LPid ! {send, AId, StreamId, Bin};
-
send(StreamId, Bin, #transport{socket = Sock,
assoc_id = AId}) ->
send(Sock, AId, StreamId, Bin).
@@ -554,10 +588,9 @@ recv({_, #sctp_assoc_change{state = comm_up,
mode = {T, _},
socket = Sock}
= S) ->
- Ref = getr(ref),
+ Ref = getr(?REF_KEY),
is_reference(Ref) %% started in new code
- andalso
- (true = diameter_reg:add_new({?MODULE, T, {Ref, {Id, Sock}}})),
+ andalso publish(T, Ref, Id, Sock),
up(S#transport{assoc_id = Id,
streams = {IS, OS}});
@@ -599,6 +632,10 @@ recv({_, #sctp_paddr_change{}}, _) ->
recv({_, #sctp_pdapi_event{}}, _) ->
ok.
+publish(T, Ref, Id, Sock) ->
+ true = diameter_reg:add_new({?MODULE, T, {Ref, {Id, Sock}}}),
+ putr(?INFO_KEY, {gen_sctp, Sock}). %% for info/1
+
%% up/1
up(#transport{parent = Pid,
@@ -608,21 +645,15 @@ up(#transport{parent = Pid,
S#transport{mode = C};
up(#transport{parent = Pid,
- mode = {accept, _}}
+ mode = {accept = A, _}}
= S) ->
diameter_peer:up(Pid),
- S.
+ S#transport{mode = A}.
-%% find/2
-
-find({[#sctp_sndrcvinfo{assoc_id = Id}], _}
- = Data,
- #listener{tmap = T}
- = S) ->
- f(ets:lookup(T, Id), Data, S);
+%% find/3
-find({_, Rec} = Data, #listener{tmap = T} = S) ->
- f(ets:lookup(T, assoc_id(Rec)), Data, S).
+find(Id, Data, #listener{tmap = T} = S) ->
+ f(ets:lookup(T, Id), Data, S).
%% New association and a transport waiting for one: use it.
f([],
@@ -663,17 +694,29 @@ f([], _, _) ->
%% assoc_id/1
-assoc_id(#sctp_shutdown_event{assoc_id = Id}) ->
+assoc_id({[#sctp_sndrcvinfo{assoc_id = Id}], _}) ->
Id;
-assoc_id(#sctp_assoc_change{assoc_id = Id}) ->
+assoc_id({_, Rec}) ->
+ id(Rec).
+
+id(#sctp_shutdown_event{assoc_id = Id}) ->
+ Id;
+id(#sctp_assoc_change{assoc_id = Id}) ->
Id;
-assoc_id(#sctp_sndrcvinfo{assoc_id = Id}) ->
+id(#sctp_sndrcvinfo{assoc_id = Id}) ->
Id;
-assoc_id(#sctp_paddr_change{assoc_id = Id}) ->
+id(#sctp_paddr_change{assoc_id = Id}) ->
Id;
-assoc_id(#sctp_adaptation_event{assoc_id = Id}) ->
+id(#sctp_adaptation_event{assoc_id = Id}) ->
Id.
+%% peeloff/3
+
+peeloff(LSock, Id, TPid) ->
+ {ok, Sock} = gen_sctp:peeloff(LSock, Id),
+ ok = gen_sctp:controlling_process(Sock, TPid),
+ Sock.
+
%% connect/4
connect(_, [], _, Reasons) ->
diff --git a/lib/diameter/src/transport/diameter_tcp.erl b/lib/diameter/src/transport/diameter_tcp.erl
index 78dbda6888..f3fbbee609 100644
--- a/lib/diameter/src/transport/diameter_tcp.erl
+++ b/lib/diameter/src/transport/diameter_tcp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -37,11 +37,17 @@
code_change/3,
terminate/2]).
+-export([info/1]). %% service_info callback
+
-export([ports/0,
ports/1]).
-include_lib("diameter/include/diameter.hrl").
+%% Keys into process dictionary.
+-define(INFO_KEY, info).
+-define(REF_KEY, ref).
+
-define(ERROR(T), erlang:error({T, ?MODULE, ?LINE})).
-define(DEFAULT_PORT, 3868). %% RFC 3588, ch 2.1
@@ -74,7 +80,7 @@
%% Accepting/connecting transport process state.
-record(transport,
- {socket :: inet:socket(), %% accept or connect socket
+ {socket :: inet:socket() | ssl:sslsock(), %% accept or connect socket
parent :: pid(), %% of process that started us
module :: module(), %% gen_tcp-like module
frag = <<>> :: binary() | {tref(), frag()}, %% message fragment
@@ -111,6 +117,33 @@ start_link(T) ->
diameter_lib:spawn_opts(server, [])).
%% ---------------------------------------------------------------------------
+%% # info/1
+%% ---------------------------------------------------------------------------
+
+info({Mod, Sock}) ->
+ lists:flatmap(fun(K) -> info(Mod, K, Sock) end,
+ [{socket, fun sockname/2},
+ {peer, fun peername/2},
+ {statistics, fun getstat/2}
+ | ssl_info(Mod, Sock)]).
+
+info(Mod, {K,F}, Sock) ->
+ case F(Mod, Sock) of
+ {ok, V} ->
+ [{K,V}];
+ _ ->
+ []
+ end.
+
+ssl_info(ssl = M, Sock) ->
+ [{M, ssl_info(Sock)}];
+ssl_info(_, _) ->
+ [].
+
+ssl_info(Sock) ->
+ [{peercert, C} || {ok, C} <- [ssl:peercert(Sock)]].
+
+%% ---------------------------------------------------------------------------
%% # init/1
%% ---------------------------------------------------------------------------
@@ -133,7 +166,7 @@ i({T, Ref, Mod, Pid, Opts, Addrs})
MPid ! {stop, self()}, %% tell the monitor to die
M = if SslOpts -> ssl; true -> Mod end,
setopts(M, Sock),
- putr(ref, Ref),
+ putr(?REF_KEY, Ref),
#transport{parent = Pid,
module = M,
socket = Sock,
@@ -191,7 +224,7 @@ i(accept = T, Ref, Mod, Pid, Opts, Addrs) ->
{LAddr, LSock} = listener(Ref, {Mod, Opts, Addrs}),
proc_lib:init_ack({ok, self(), [LAddr]}),
Sock = ok(accept(Mod, LSock)),
- true = diameter_reg:add_new({?MODULE, T, {Ref, Sock}}),
+ publish(Mod, T, Ref, Sock),
diameter_peer:up(Pid),
Sock;
@@ -202,10 +235,14 @@ i(connect = T, Ref, Mod, Pid, Opts, Addrs) ->
RPort = get_port(RP),
proc_lib:init_ack({ok, self(), [LAddr]}),
Sock = ok(connect(Mod, RAddr, RPort, gen_opts(LAddr, Rest))),
- true = diameter_reg:add_new({?MODULE, T, {Ref, Sock}}),
+ publish(Mod, T, Ref, Sock),
diameter_peer:up(Pid, {RAddr, RPort}),
Sock.
+publish(Mod, T, Ref, Sock) ->
+ true = diameter_reg:add_new({?MODULE, T, {Ref, Sock}}),
+ putr(?INFO_KEY, {Mod, Sock}). %% for info/1
+
ok({ok, T}) ->
T;
ok(No) ->
@@ -521,7 +558,7 @@ tls_handshake(Type, true, #transport{socket = Sock,
ssl = Opts}
= S) ->
{ok, SSock} = tls(Type, Sock, [{cb_info, ?TCP_CB(M)} | Opts]),
- Ref = getr(ref),
+ Ref = getr(?REF_KEY),
is_reference(Ref) %% started in new code
andalso
(true = diameter_reg:add_new({?MODULE, Type, {Ref, SSock}})),
@@ -696,12 +733,32 @@ setopts(M, Sock) ->
portnr(gen_tcp, Sock) ->
inet:port(Sock);
-portnr(ssl, Sock) ->
- case ssl:sockname(Sock) of
+portnr(M, Sock) ->
+ case M:sockname(Sock) of
{ok, {_Addr, PortNr}} ->
{ok, PortNr};
{error, _} = No ->
No
- end;
-portnr(M, Sock) ->
- M:port(Sock).
+ end.
+
+%% sockname/2
+
+sockname(gen_tcp, Sock) ->
+ inet:sockname(Sock);
+sockname(M, Sock) ->
+ M:sockname(Sock).
+
+%% peername/2
+
+peername(gen_tcp, Sock) ->
+ inet:peername(Sock);
+peername(M, Sock) ->
+ M:peername(Sock).
+
+%% getstat/2
+
+getstat(gen_tcp, Sock) ->
+ inet:getstat(Sock);
+getstat(M, Sock) ->
+ M:getstat(Sock).
+%% Note that ssl:getstat/1 doesn't yet exist in R15B01.
diff --git a/lib/diameter/src/transport/diameter_transport.erl b/lib/diameter/src/transport/diameter_transport.erl
new file mode 100644
index 0000000000..ff4b6bbc6d
--- /dev/null
+++ b/lib/diameter/src/transport/diameter_transport.erl
@@ -0,0 +1,55 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, 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(diameter_transport).
+
+%%
+%% This module implements a transport start function that
+%% evaluates its config argument.
+%%
+
+%% Transport start functions
+-export([start/3,
+ select/3,
+ eval/3]).
+
+%% start/3
+
+%% Call a start function in this module ...
+start(T, Svc, {F,A}) ->
+ start(T, Svc, {?MODULE, F, [A]});
+
+%% ... or some other.
+start(T, Svc, F) ->
+ diameter_lib:eval([F, T, Svc]).
+
+%% select/3
+%%
+%% A start function that whose config argument is expected to return a
+%% new start function.
+
+select(T, Svc, F) ->
+ start(T, Svc, diameter_lib:eval([F, T, Svc])).
+
+%% eval/3
+%%
+%% A start function that simply evaluates its config argument.
+
+eval(_, _, F) ->
+ diameter_lib:eval(F).
diff --git a/lib/diameter/test/Makefile b/lib/diameter/test/Makefile
index 1659330a91..866d135bd9 100644
--- a/lib/diameter/test/Makefile
+++ b/lib/diameter/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2010-2011. All Rights Reserved.
+# Copyright Ericsson AB 2010-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -16,13 +16,8 @@
#
# %CopyrightEnd%
-ifeq ($(ERL_TOP),)
-include $(DIAMETER_TOP)/make/target.mk
-include $(DIAMETER_TOP)/make/$(TARGET)/rules.mk
-else
include $(ERL_TOP)/make/target.mk
include $(ERL_TOP)/make/$(TARGET)/otp.mk
-endif
# ----------------------------------------------------
# Application version
@@ -67,8 +62,13 @@ ERL_COMPILE_FLAGS += +warn_export_vars \
# Targets
# ----------------------------------------------------
+# Require success ...
all: opt
+# ... or not.
+any: opt
+ $(MAKE) -i $(SUITES)
+
run: $(SUITES)
debug opt: $(TARGET_FILES)
@@ -113,7 +113,7 @@ help:
@echo " Echo some relevant variables."
@echo ========================================
-.PHONY: all run clean debug docs help info opt realclean
+.PHONY: all any run clean debug docs help info opt realclean
# ----------------------------------------------------
# Special Targets
@@ -143,11 +143,7 @@ log:
/%: % force
sed -f release.sed $< > "$(RELSYSDIR)$@"
-ifeq ($(ERL_TOP),)
-include $(DIAMETER_TOP)/make/release_targets.mk
-else
include $(ERL_TOP)/make/otp_release_targets.mk
-endif
release_spec release_docs_spec:
diff --git a/lib/diameter/test/diameter_capx_SUITE.erl b/lib/diameter/test/diameter_capx_SUITE.erl
index 54a161d606..ae128b8203 100644
--- a/lib/diameter/test/diameter_capx_SUITE.erl
+++ b/lib/diameter/test/diameter_capx_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -93,12 +93,12 @@
-define(fail(T), erlang:error({T, process_info(self(), messages)})).
--define(TIMEOUT, 2000).
+-define(TIMEOUT, 10000).
%% ===========================================================================
suite() ->
- [{timetrap, {seconds, 10}}].
+ [{timetrap, {seconds, 60}}].
all() -> [start,
start_services,
diff --git a/lib/diameter/test/diameter_compiler_SUITE.erl b/lib/diameter/test/diameter_compiler_SUITE.erl
index 3b4c9706e0..79bf9d32db 100644
--- a/lib/diameter/test/diameter_compiler_SUITE.erl
+++ b/lib/diameter/test/diameter_compiler_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -31,8 +31,8 @@
%% testcases
-export([format/1, format/2,
replace/1, replace/2,
- generate/1, generate/4, generate/0,
- examples/1, examples/0]).
+ generate/1, generate/4,
+ examples/1]).
-export([dict/0]). %% fake dictionary module
@@ -339,7 +339,7 @@
%% ===========================================================================
suite() ->
- [{timetrap, {seconds, 5}}].
+ [{timetrap, {minutes, 10}}].
all() ->
[format,
@@ -407,9 +407,6 @@ re({RE, Repl}, Bin) ->
%%
%% Ensure success when generating code and compiling.
-generate() ->
- [{timetrap, {seconds, 2*length(?REPLACE)}}].
-
generate(Config) ->
Bin = proplists:get_value(base, Config),
Rs = lists:zip(?REPLACE, lists:seq(1, length(?REPLACE))),
@@ -436,9 +433,6 @@ generate(Mods, Bin, N, Mode) ->
%%
%% Compile dictionaries extracted from various standards.
-examples() ->
- [{timetrap, {seconds, 3*length(?EXAMPLES)}}].
-
examples(_Config) ->
Dir = filename:join([code:lib_dir(diameter, examples), "dict"]),
[D || D <- ?EXAMPLES, _ <- [examples(?S(D), Dir)]].
diff --git a/lib/diameter/test/diameter_ct.erl b/lib/diameter/test/diameter_ct.erl
index f8ee3dc1d7..ded50bf6c5 100644
--- a/lib/diameter/test/diameter_ct.erl
+++ b/lib/diameter/test/diameter_ct.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -25,15 +25,14 @@
-export([run/1]).
-%% ct:run_test/1 is currently documented as returning a list of test
-%% results ... but no. Instead it returns 'ok' regardless of whether
-%% or not the suite in question has failed testcases.
+%% The makefile looks for signs of failure so ignore the ct:run_test/1
+%% return value.
run([Suite]) ->
Start = info(),
- ok = ct:run_test([{suite, Suite},
- {logdir, "./log"},
- {auto_compile, false}]),
+ ct:run_test([{suite, Suite},
+ {logdir, "./log"},
+ {auto_compile, false}]),
info(Start , info()).
info() ->
diff --git a/lib/diameter/test/diameter_dict_SUITE.erl b/lib/diameter/test/diameter_dict_SUITE.erl
index 5cf8506d3f..3cc65c0257 100644
--- a/lib/diameter/test/diameter_dict_SUITE.erl
+++ b/lib/diameter/test/diameter_dict_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -48,7 +48,7 @@
%% ===========================================================================
suite() ->
- [{timetrap, {seconds, 10}}].
+ [{timetrap, {seconds, 60}}].
all() ->
[{group, all},
diff --git a/lib/diameter/test/diameter_dpr_SUITE.erl b/lib/diameter/test/diameter_dpr_SUITE.erl
new file mode 100644
index 0000000000..9252650bf7
--- /dev/null
+++ b/lib/diameter/test/diameter_dpr_SUITE.erl
@@ -0,0 +1,196 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+%% Tests of the disconnect_cb configuration.
+%%
+
+-module(diameter_dpr_SUITE).
+
+-export([suite/0,
+ all/0,
+ groups/0,
+ init_per_group/2,
+ end_per_group/2]).
+
+%% testcases
+-export([start/1,
+ connect/1,
+ remove_transport/1,
+ stop_service/1,
+ check/1,
+ stop/1]).
+
+%% disconnect_cb
+-export([disconnect/5]).
+
+-include("diameter.hrl").
+
+%% ===========================================================================
+
+-define(util, diameter_util).
+
+-define(ADDR, {127,0,0,1}).
+
+-define(CLIENT, "CLIENT").
+-define(SERVER, "SERVER").
+
+-define(DICT_COMMON, ?DIAMETER_DICT_COMMON).
+-define(APP_ID, ?DICT_COMMON:id()).
+
+%% Config for diameter:start_service/2.
+-define(SERVICE(Host),
+ [{'Origin-Host', Host},
+ {'Origin-Realm', "erlang.org"},
+ {'Host-IP-Address', [?ADDR]},
+ {'Vendor-Id', hd(Host)}, %% match this in disconnect/5
+ {'Product-Name', "OTP/diameter"},
+ {'Acct-Application-Id', [?APP_ID]},
+ {restrict_connections, false},
+ {application, [{dictionary, ?DICT_COMMON},
+ {module, #diameter_callback{_ = false}}]}]).
+
+%% Disconnect reasons that diameter passes as the first argument of a
+%% function configured as disconnect_cb.
+-define(REASONS, [transport, service, application]).
+
+%% Valid values for Disconnect-Cause.
+-define(CAUSES, [0, rebooting, 1, busy, 2, goaway]).
+
+%% Establish one client connection for element of this list,
+%% configured with disconnect/5 as disconnect_cb and returning the
+%% specified value.
+-define(RETURNS,
+ [[close, {dpr, [{cause, invalid}]}], [ignore, close], []]
+ ++ [[{dpr, [{timeout, 5000}, {cause, T}]}] || T <- ?CAUSES]).
+
+%% ===========================================================================
+
+suite() ->
+ [{timetrap, {seconds, 60}}].
+
+all() ->
+ [{group, R} || R <- ?REASONS].
+
+%% The group determines how transports are terminated: by remove_transport,
+%% stop_service or application stop.
+groups() ->
+ Ts = tc(),
+ [{R, [], Ts} || R <- ?REASONS].
+
+init_per_group(Name, Config) ->
+ [{group, Name} | Config].
+
+end_per_group(_, _) ->
+ ok.
+
+tc() ->
+ [start, connect, remove_transport, stop_service, check, stop].
+
+%% ===========================================================================
+%% start/stop testcases
+
+start(_Config) ->
+ ok = diameter:start(),
+ ok = diameter:start_service(?SERVER, ?SERVICE(?SERVER)),
+ ok = diameter:start_service(?CLIENT, ?SERVICE(?CLIENT)).
+
+connect(Config) ->
+ Pid = spawn(fun init/0), %% process for disconnect_cb to bang
+ Grp = group(Config),
+ LRef = ?util:listen(?SERVER, tcp),
+ Refs = [?util:connect(?CLIENT, tcp, LRef, opts(RCs, {Grp, Pid}))
+ || RCs <- ?RETURNS],
+ ?util:write_priv(Config, config, [Pid | Refs]).
+
+%% Remove all the client transports only in the transport group.
+remove_transport(Config) ->
+ transport == group(Config)
+ andalso (ok = diameter:remove_transport(?CLIENT, true)).
+
+%% Stop the service only in the service group.
+stop_service(Config) ->
+ service == group(Config)
+ andalso (ok = diameter:stop_service(?CLIENT)).
+
+%% Check for callbacks and stop the service. (Not the other way around
+%% for the timing reason explained below.)
+check(Config) ->
+ Grp = group(Config),
+ [Pid | Refs] = ?util:read_priv(Config, config),
+ Pid ! self(), %% ask for dictionary
+ Dict = receive {Pid, D} -> D end, %% get it
+ check(Refs, ?RETURNS, Grp, Dict). %% check for callbacks
+
+stop(_Config) ->
+ ok = diameter:stop().
+
+%% Whether or not there are callbacks after diameter:stop() depends on
+%% timing as long as the server runs on the same node: a server
+%% transport could close the connection before the client has chance
+%% to apply its callback. Therefore, just check that there haven't
+%% been any callbacks yet.
+check(_, _, application, Dict) ->
+ [] = dict:to_list(Dict);
+
+check([], [], _, _) ->
+ ok;
+
+check([Ref | Refs], CBs, Grp, Dict) ->
+ check1(Ref, hd(CBs), Grp, Dict),
+ check(Refs, tl(CBs), Grp, Dict).
+
+check1(Ref, [ignore | RCs], Reason, Dict) ->
+ check1(Ref, RCs, Reason, Dict);
+
+check1(Ref, [_|_], Reason, Dict) ->
+ {ok, Reason} = dict:find(Ref, Dict); %% callback with expected reason
+
+check1(Ref, [], _, Dict) ->
+ error = dict:find(Ref, Dict). %% no callback
+
+%% ----------------------------------------
+
+group(Config) ->
+ {group, Grp} = lists:keyfind(group, 1, Config),
+ Grp.
+
+%% Configure the callback with the group name (= disconnect reason) as
+%% extra argument.
+opts(RCs, T) ->
+ [{disconnect_cb, {?MODULE, disconnect, [T, RC]}} || RC <- RCs].
+
+%% Match the group name with the disconnect reason to ensure the
+%% callback is being called as expected.
+disconnect(Reason, Ref, Peer, {Reason, Pid}, RC) ->
+ io:format("disconnect: ~p ~p~n", [Ref, Reason]),
+ {_, #diameter_caps{vendor_id = {$C,$S}}} = Peer,
+ Pid ! {Reason, Ref},
+ RC.
+
+init() ->
+ exit(recv(dict:new())).
+
+recv(Dict) ->
+ receive
+ Pid when is_pid(Pid) ->
+ Pid ! {self(), Dict};
+ {Reason, Ref} ->
+ recv(dict:store(Ref, Reason, Dict))
+ end.
diff --git a/lib/diameter/test/diameter_event_SUITE.erl b/lib/diameter/test/diameter_event_SUITE.erl
new file mode 100644
index 0000000000..7c1c76f22a
--- /dev/null
+++ b/lib/diameter/test/diameter_event_SUITE.erl
@@ -0,0 +1,182 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2013. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+%% Tests of events sent as a consequence of diameter:subscribe/1.
+%% Watchdog events are dealt with more extensively in the watchdog
+%% suite.
+%%
+
+-module(diameter_event_SUITE).
+
+-export([suite/0,
+ all/0,
+ init_per_testcase/2,
+ end_per_testcase/2]).
+
+%% testcases
+-export([start/1,
+ start_server/1,
+ up/1,
+ down/1,
+ cea_timeout/1,
+ stop/1]).
+
+-include("diameter.hrl").
+
+%% ===========================================================================
+
+-define(util, diameter_util).
+
+-define(ADDR, {127,0,0,1}).
+-define(REALM, "REALM").
+
+-define(SERVER, "SERVER.SERVER-REALM").
+-define(CLIENT, "CLIENT.CLIENT-REALM").
+
+-define(DICT_COMMON, ?DIAMETER_DICT_COMMON).
+-define(DICT_ACCT, ?DIAMETER_DICT_ACCOUNTING).
+
+-define(SERVER_CAPX_TMO, 6000).
+
+%% Config for diameter:start_service/2.
+-define(SERVICE(Host, Dicts),
+ [{'Origin-Host', Host},
+ {'Origin-Realm', realm(Host)},
+ {'Host-IP-Address', [?ADDR]},
+ {'Vendor-Id', 12345},
+ {'Product-Name', "OTP/diameter"},
+ {'Acct-Application-Id', [D:id() || D <- Dicts]}
+ | [{application, [{dictionary, D},
+ {module, #diameter_callback{}}]}
+ || D <- Dicts]]).
+
+%% Diameter Result-Code's:
+-define(NO_COMMON_APP, 5010).
+
+%% ===========================================================================
+
+suite() ->
+ [{timetrap, {seconds, 60}}].
+
+all() ->
+ [start,
+ start_server,
+ up,
+ down,
+ cea_timeout,
+ stop].
+
+init_per_testcase(Name, Config) ->
+ [{name, Name} | Config].
+
+end_per_testcase(_, _) ->
+ ok.
+
+%% ===========================================================================
+%% start/stop testcases
+
+start(_Config) ->
+ ok = diameter:start().
+
+start_server(Config) ->
+ diameter:subscribe(?SERVER),
+ ok = diameter:start_service(?SERVER, ?SERVICE(?SERVER, [?DICT_COMMON])),
+ LRef = ?util:listen(?SERVER, tcp, [{capabilities_cb, fun capx_cb/2},
+ {capx_timeout, ?SERVER_CAPX_TMO}]),
+ [PortNr] = ?util:lport(tcp, LRef, 20),
+ ?util:write_priv(Config, portnr, PortNr),
+ start = event(?SERVER).
+
+%% Connect with matching capabilities and expect the connection to
+%% come up.
+up(Config) ->
+ {Svc, Ref} = connect(Config, []),
+ start = event(Svc),
+ {up, Ref, {_,_Caps}, _Config, #diameter_packet{}} = event(Svc),
+ {watchdog, Ref, _, {initial, okay}, _} = event(Svc).
+
+%% Connect with non-matching capabilities and expect CEA from the peer
+%% to indicate as much and then for the transport to be restarted
+%% (after reconnect_timer).
+down(Config) ->
+ {Svc, Ref} = connect(Config, [{capabilities, [{'Acct-Application-Id',
+ [?DICT_ACCT:id()]}]},
+ {applications, [?DICT_ACCT]},
+ {reconnect_timer, 5000}]),
+ start = event(Svc),
+ {watchdog, Ref, _, {initial, down}, _} = event(Svc),
+ {closed, Ref, {'CEA', ?NO_COMMON_APP, _, #diameter_packet{}}, _}
+ = event(Svc),
+ {reconnect, Ref, _} = event(Svc).
+
+%% Connect with matching capabilities but have the server delay its
+%% CEA and cause the client to timeout.
+cea_timeout(Config) ->
+ {Svc, Ref} = connect(Config, [{capx_timeout, ?SERVER_CAPX_TMO div 2},
+ {reconnect_timer, 2*?SERVER_CAPX_TMO}]),
+ start = event(Svc),
+ {watchdog, Ref, _, {initial, down}, _} = event(Svc),
+ {closed, Ref, {'CEA', timeout}, _} = event(Svc).
+
+stop(_Config) ->
+ ok = diameter:stop().
+
+%% ----------------------------------------
+
+%% Keep the server from sending CEA until the client has timed out.
+capx_cb(_, #diameter_caps{origin_host = {_, "cea_timeout-" ++ _}}) ->
+ receive after ?SERVER_CAPX_TMO -> ok end;
+
+%% Or not.
+capx_cb(_, _Caps) ->
+ ok.
+
+%% ----------------------------------------
+
+%% Use the testcase name to construct Origin-Host of the client so
+%% that the server can match on it in capx_cb/2.
+connect(Config, Opts) ->
+ Pre = atom_to_list(proplists:get_value(name, Config)),
+ Name = Pre ++ uniq() ++ ?CLIENT,
+ diameter:subscribe(Name),
+ ok = start_service(Name, ?SERVICE(Name, [?DICT_COMMON, ?DICT_ACCT])),
+ {ok, Ref} = diameter:add_transport(Name, opts(Config, Opts)),
+ {Name, Ref}.
+
+uniq() ->
+ {MS,S,US} = now(),
+ lists:flatten(io_lib:format("-~p-~p-~p-", [MS,S,US])).
+
+event(Name) ->
+ receive #diameter_event{service = Name, info = T} -> T end.
+
+start_service(Name, Opts) ->
+ diameter:start_service(Name, [{monitor, self()} | Opts]).
+
+opts(Config, Opts) ->
+ PortNr = ?util:read_priv(Config, portnr),
+
+ {connect, [{transport_module, diameter_tcp},
+ {transport_config, [{ip, ?ADDR}, {port, 0},
+ {raddr, ?ADDR}, {rport, PortNr}]}
+ | Opts]}.
+
+realm(Host) ->
+ tl(lists:dropwhile(fun(C) -> C /= $. end, Host)).
diff --git a/lib/diameter/test/diameter_failover_SUITE.erl b/lib/diameter/test/diameter_failover_SUITE.erl
index 53398dd93e..bb820a8bf2 100644
--- a/lib/diameter/test/diameter_failover_SUITE.erl
+++ b/lib/diameter/test/diameter_failover_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,19 +18,19 @@
%%
%%
-%% Tests of traffic between six Diameter nodes in three realms,
+%% Tests of traffic between seven Diameter nodes in four realms,
%% connected as follows.
%%
-%% ----- SERVER1.REALM2
-%% /
-%% / ----- SERVER2.REALM2
-%% | /
-%% CLIENT.REALM1 ------ SERVER3.REALM2
-%% | \
-%% | \
-%% \ ---- SERVER1.REALM3
-%% \
-%% ----- SERVER2.REALM3
+%% ----- SERVER1.REALM2 -----
+%% / \
+%% / ----- SERVER2.REALM2 ----- \
+%% | / \ |
+%% CLIENT.REALM1 ------ SERVER3.REALM2 ------ CLIENT.REALM4
+%% | \ / |
+%% | \ / |
+%% \ ---- SERVER1.REALM3 ----- /
+%% \ /
+%% ----- SERVER2.REALM3 -----
%%
-module(diameter_failover_SUITE).
@@ -44,12 +44,16 @@
connect/1,
send_ok/1,
send_nok/1,
+ send_discard_1/1,
+ send_discard_2/1,
stop_services/1,
stop/1]).
%% diameter callbacks
-export([pick_peer/4,
prepare_request/3,
+ prepare_retransmit/3,
+ handle_error/4,
handle_answer/4,
handle_request/3]).
@@ -62,14 +66,18 @@
-define(ADDR, {127,0,0,1}).
--define(CLIENT, "CLIENT.REALM1").
+-define(CLIENT1, "CLIENT.REALM1").
+-define(CLIENT2, "CLIENT.REALM4").
-define(SERVER1, "SERVER1.REALM2").
-define(SERVER2, "SERVER2.REALM2").
-define(SERVER3, "SERVER3.REALM2").
-define(SERVER4, "SERVER1.REALM3").
-define(SERVER5, "SERVER2.REALM3").
--define(SERVICES, [?CLIENT, ?SERVER1, ?SERVER2, ?SERVER3, ?SERVER4, ?SERVER5]).
+-define(IS_CLIENT(Svc), Svc == ?CLIENT1; Svc == ?CLIENT2).
+
+-define(CLIENTS, [?CLIENT1, ?CLIENT2]).
+-define(SERVERS, [?SERVER1, ?SERVER2, ?SERVER3, ?SERVER4, ?SERVER5]).
-define(DICT_COMMON, ?DIAMETER_DICT_COMMON).
@@ -77,31 +85,32 @@
-define(APP_ID, ?DICT_COMMON:id()).
%% Config for diameter:start_service/2.
--define(SERVICE(Host, Dict),
+-define(SERVICE(Host),
[{'Origin-Host', Host},
{'Origin-Realm', realm(Host)},
{'Host-IP-Address', [?ADDR]},
{'Vendor-Id', 12345},
{'Product-Name', "OTP/diameter"},
- {'Acct-Application-Id', [Dict:id()]},
+ {'Acct-Application-Id', [?APP_ID]},
{application, [{alias, ?APP_ALIAS},
- {dictionary, Dict},
+ {dictionary, ?DICT_COMMON},
{module, #diameter_callback
{peer_up = false,
peer_down = false,
- handle_error = false,
- prepare_retransmit = false,
default = ?MODULE}},
{answer_errors, callback}]}]).
-define(SUCCESS, 2001).
--define(LOGOUT, ?'DIAMETER_BASE_TERMINATION-CAUSE_DIAMETER_LOGOUT').
+%% Value of Termination-Cause determines client/server behaviour.
+-define(LOGOUT, ?'DIAMETER_BASE_TERMINATION-CAUSE_DIAMETER_LOGOUT').
+-define(MOVED, ?'DIAMETER_BASE_TERMINATION-CAUSE_DIAMETER_USER_MOVED').
+-define(TIMEOUT, ?'DIAMETER_BASE_TERMINATION-CAUSE_DIAMETER_SESSION_TIMEOUT').
%% ===========================================================================
suite() ->
- [{timetrap, {seconds, 10}}].
+ [{timetrap, {seconds, 60}}].
all() ->
[start,
@@ -109,6 +118,8 @@ all() ->
connect,
send_ok,
send_nok,
+ send_discard_1,
+ send_discard_2,
stop_services,
stop].
@@ -119,19 +130,20 @@ start(_Config) ->
ok = diameter:start().
start_services(_Config) ->
- S = [server(N, ?DICT_COMMON) || N <- tl(?SERVICES)],
-
- ok = diameter:start_service(?CLIENT, ?SERVICE(?CLIENT, ?DICT_COMMON)),
+ Servers = [server(N) || N <- ?SERVERS],
+ [] = [T || C <- ?CLIENTS,
+ T <- [diameter:start_service(C, ?SERVICE(C))],
+ T /= ok],
- {save_config, [{?CLIENT, S}]}.
+ {save_config, Servers}.
connect(Config) ->
- {_, Conns} = proplists:get_value(saved_config, Config),
+ {start_services, Servers} = proplists:get_value(saved_config, Config),
- lists:foreach(fun({CN,Ss}) -> connect(CN, Ss) end, Conns).
+ lists:foreach(fun(C) -> connect(C, Servers) end, ?CLIENTS).
stop_services(_Config) ->
- [] = [{H,T} || H <- ?SERVICES,
+ [] = [{H,T} || H <- ?CLIENTS ++ ?SERVERS,
T <- [diameter:stop_service(H)],
T /= ok].
@@ -140,8 +152,8 @@ stop(_Config) ->
%% ----------------------------------------
-server(Name, Dict) ->
- ok = diameter:start_service(Name, ?SERVICE(Name, Dict)),
+server(Name) ->
+ ok = diameter:start_service(Name, ?SERVICE(Name)),
{Name, ?util:listen(Name, tcp)}.
connect(Name, Refs) ->
@@ -153,30 +165,39 @@ connect(Name, Refs) ->
%% Send an STR and expect success after SERVER3 answers after a couple
%% of failovers.
send_ok(_Config) ->
- Req = ['STR', {'Destination-Realm', realm(?SERVER1)},
- {'Termination-Cause', ?LOGOUT},
- {'Auth-Application-Id', ?APP_ID}],
+ Req = #diameter_base_STR{'Destination-Realm' = realm(?SERVER1),
+ 'Termination-Cause' = ?LOGOUT,
+ 'Auth-Application-Id' = ?APP_ID},
#diameter_base_STA{'Result-Code' = ?SUCCESS,
'Origin-Host' = ?SERVER3}
- = call(Req, [{filter, realm}]).
+ = call(?CLIENT1, Req).
%% Send an STR and expect failure when both servers fail.
send_nok(_Config) ->
- Req = ['STR', {'Destination-Realm', realm(?SERVER4)},
- {'Termination-Cause', ?LOGOUT},
- {'Auth-Application-Id', ?APP_ID}],
- {error, failover} = call(Req, [{filter, realm}]).
+ Req = #diameter_base_STR{'Destination-Realm' = realm(?SERVER4),
+ 'Termination-Cause' = ?LOGOUT,
+ 'Auth-Application-Id' = ?APP_ID},
+ {failover, ?LOGOUT} = call(?CLIENT1, Req).
+
+%% Send an STR and have prepare_retransmit discard it.
+send_discard_1(_Config) ->
+ Req = #diameter_base_STR{'Destination-Realm' = realm(?SERVER1),
+ 'Termination-Cause' = ?TIMEOUT,
+ 'Auth-Application-Id' = ?APP_ID},
+ {rejected, ?TIMEOUT} = call(?CLIENT2, Req).
+send_discard_2(_Config) ->
+ Req = #diameter_base_STR{'Destination-Realm' = realm(?SERVER4),
+ 'Termination-Cause' = ?MOVED,
+ 'Auth-Application-Id' = ?APP_ID},
+ {discarded, ?MOVED} = call(?CLIENT2, Req).
%% ===========================================================================
realm(Host) ->
tl(lists:dropwhile(fun(C) -> C /= $. end, Host)).
-call(Req, Opts) ->
- diameter:call(?CLIENT, ?APP_ALIAS, Req, Opts).
-
-set([H|T], Vs) ->
- [H | Vs ++ T].
+call(Svc, Req) ->
+ diameter:call(Svc, ?APP_ALIAS, Req, [{filter, realm}]).
%% ===========================================================================
%% diameter callbacks
@@ -184,7 +205,8 @@ set([H|T], Vs) ->
%% pick_peer/4
%% Choose a server other than SERVER3 or SERVER5 if possible.
-pick_peer(Peers, _, ?CLIENT, _State) ->
+pick_peer(Peers, _, Svc, _State)
+ when ?IS_CLIENT(Svc) ->
case lists:partition(fun({_, #diameter_caps{origin_host = {_, OH}}}) ->
OH /= ?SERVER3 andalso OH /= ?SERVER5
end,
@@ -198,20 +220,41 @@ pick_peer(Peers, _, ?CLIENT, _State) ->
%% prepare_request/3
-prepare_request(Pkt, ?CLIENT, {_Ref, Caps}) ->
+prepare_request(Pkt, Svc, {_Ref, Caps})
+ when ?IS_CLIENT(Svc) ->
{send, prepare(Pkt, Caps)}.
prepare(#diameter_packet{msg = Req}, Caps) ->
#diameter_caps{origin_host = {OH, _},
origin_realm = {OR, _}}
= Caps,
- set(Req, [{'Session-Id', diameter:session_id(OH)},
- {'Origin-Host', OH},
- {'Origin-Realm', OR}]).
+ Req#diameter_base_STR{'Origin-Host' = OH,
+ 'Origin-Realm' = OR,
+ 'Session-Id' = diameter:session_id(OH)}.
+
+%% prepare_retransmit/3
+
+prepare_retransmit(#diameter_packet{header = H} = P, Svc, {_,_})
+ when ?IS_CLIENT(Svc) ->
+ #diameter_header{is_retransmitted = true} = H, %% assert
+ prepare(P).
+
+prepare(#diameter_packet{msg = M} = P) ->
+ case M#diameter_base_STR.'Termination-Cause' of
+ ?LOGOUT -> {send, P};
+ ?MOVED -> discard;
+ ?TIMEOUT -> {discard, rejected}
+ end.
+
+%% handle_error/4
+
+handle_error(Reason, Req, _, _) ->
+ {Reason, Req#diameter_base_STR.'Termination-Cause'}.
%% handle_answer/4
-handle_answer(Pkt, _Req, ?CLIENT, _Peer) ->
+handle_answer(Pkt, _Req, Svc, _Peer)
+ when ?IS_CLIENT(Svc) ->
#diameter_packet{msg = Rec, errors = []} = Pkt,
Rec.
@@ -219,8 +262,8 @@ handle_answer(Pkt, _Req, ?CLIENT, _Peer) ->
%% Only SERVER3 actually answers.
handle_request(Pkt, ?SERVER3, {_, Caps}) ->
- #diameter_packet{msg = #diameter_base_STR{'Session-Id' = SId,
- 'Origin-Host' = ?CLIENT}}
+ #diameter_packet{header = #diameter_header{is_retransmitted = true},
+ msg = #diameter_base_STR{'Session-Id' = SId}}
= Pkt,
#diameter_caps{origin_host = {OH, _},
origin_realm = {OR, _}}
diff --git a/lib/diameter/test/diameter_gen_sctp_SUITE.erl b/lib/diameter/test/diameter_gen_sctp_SUITE.erl
index 7f435a6b7a..2fde7b9fdb 100644
--- a/lib/diameter/test/diameter_gen_sctp_SUITE.erl
+++ b/lib/diameter/test/diameter_gen_sctp_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -175,7 +175,8 @@ send(Sock, Id) ->
send_from_multiple_clients(_) ->
{S, Rs} = T = send_from_multiple_clients(8, 1024),
- {false, [], _} = {?FOREVER < S,
+ Max = ?FOREVER*1000,
+ {false, [], _} = {Max < S,
Rs -- [OI || {O,_} = OI <- Rs, is_integer(O)],
T}.
@@ -223,6 +224,11 @@ send_from_multiple_clients(_) ->
%% {134,100},
%% {117,98},
%% {149,125}]}
+%%
+%% This turns out to have been due to SCTP resends as a consequence of
+%% the listener having an insufficient recbuf. Increasing the size
+%% solves the problem.
+%%
send_from_multiple_clients(N, Sz)
when is_integer(N), 0 < N, is_integer(Sz), 0 < Sz ->
@@ -341,8 +347,15 @@ receive_what_was_sent(_Config) ->
%% open/0
open() ->
- gen_sctp:open([{ip, ?ADDR}, {port, 0}, {active, true}, binary]).
+ open([]).
+
+%% open/1
+open(Opts) ->
+ gen_sctp:open([{ip, ?ADDR}, {port, 0}, {active, true}, binary,
+ {recbuf, 1 bsl 16}, {sndbuf, 1 bsl 16}
+ | Opts]).
+
%% assoc/1
assoc(Sock) ->
diff --git a/lib/diameter/test/diameter_reg_SUITE.erl b/lib/diameter/test/diameter_reg_SUITE.erl
index ec6a0ca731..4939019f7a 100644
--- a/lib/diameter/test/diameter_reg_SUITE.erl
+++ b/lib/diameter/test/diameter_reg_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -43,7 +43,7 @@
%% ===========================================================================
suite() ->
- [{timetrap, {seconds, 10}}].
+ [{timetrap, {seconds, 60}}].
all() ->
[{group, all},
diff --git a/lib/diameter/test/diameter_relay_SUITE.erl b/lib/diameter/test/diameter_relay_SUITE.erl
index 70e1866791..f10d82bdf8 100644
--- a/lib/diameter/test/diameter_relay_SUITE.erl
+++ b/lib/diameter/test/diameter_relay_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -48,6 +48,7 @@
send_loop/1,
send_timeout_1/1,
send_timeout_2/1,
+ info/1,
disconnect/1,
stop_services/1,
stop/1]).
@@ -112,7 +113,7 @@
%% ===========================================================================
suite() ->
- [{timetrap, {seconds, 10}}].
+ [{timetrap, {seconds, 60}}].
all() ->
[start,
@@ -136,7 +137,8 @@ tc() ->
send4,
send_loop,
send_timeout_1,
- send_timeout_2].
+ send_timeout_2,
+ info].
%% ===========================================================================
%% start/stop testcases
@@ -224,6 +226,9 @@ send_timeout(Tmo) ->
{'Re-Auth-Request-Type', ?AUTHORIZE_ONLY}],
call(Req, [{filter, realm}, {timeout, Tmo}]).
+info(_Config) ->
+ [] = ?util:info().
+
%% ===========================================================================
realm(Host) ->
diff --git a/lib/diameter/test/diameter_stats_SUITE.erl b/lib/diameter/test/diameter_stats_SUITE.erl
index e7807fd360..8b7d8cb1b6 100644
--- a/lib/diameter/test/diameter_stats_SUITE.erl
+++ b/lib/diameter/test/diameter_stats_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -30,16 +30,17 @@
end_per_suite/1]).
%% testcases
--export([an/1,
- twa/1]).
+-export([reg/1,
+ incr/1,
+ read/1,
+ flush/1]).
-define(stat, diameter_stats).
--define(util, diameter_util).
%% ===========================================================================
suite() ->
- [{timetrap, {seconds, 10}}].
+ [{timetrap, {seconds, 60}}].
all() ->
[{group, all},
@@ -49,8 +50,10 @@ groups() ->
[{all, [], tc()}].
tc() ->
- [an,
- twa].
+ [reg,
+ incr,
+ read,
+ flush].
init_per_suite(Config) ->
ok = diameter:start(),
@@ -61,25 +64,62 @@ end_per_suite(_Config) ->
%% ===========================================================================
-an(_) ->
- Ref = {'_', make_ref()},
+reg(_) ->
+ Ref = '$1',
true = ?stat:reg(Ref),
- true = ?stat:reg(Ref), %% duplicate
- ok = ?stat:incr(x),
- ok = ?stat:incr(x, Ref),
- ok = ?stat:incr(y, 2),
- ok = ?stat:incr(y, Ref),
- %% Flushing a pid flushes even stats on the registered reference.
- [{x,2},{y,3}] = lists:sort(?stat:flush()),
- [] = ?stat:flush(Ref),
- [] = ?stat:flush().
-
-twa(_) ->
+ false = ?stat:reg(Ref). %% duplicate
+
+incr(_) ->
+ Ref = '_',
+ Ctr = x,
+ false = ?stat:incr(Ctr), %% not registered,
+ 1 = ?stat:incr(Ctr, Ref, 1), %% only pids need register
+ true = ?stat:reg(Ref),
+ spawn(fun() ->
+ true = ?stat:reg(Ref),
+ 2 = ?stat:incr(Ctr, self(), 2)
+ end),
+ ok = fold(Ctr, Ref, 3), %% folded
+ ?stat:flush([self(), Ref]).
+
+read(_) ->
+ Ref = make_ref(),
+ C1 = {a,b},
+ C2 = {b,a},
+ true = ?stat:reg(Ref),
+ 1 = ?stat:incr(C1),
+ 1 = ?stat:incr(C2),
+ 2 = ?stat:incr(C1),
+ 7 = ?stat:incr(C1, Ref, 7),
+ Self = self(),
+ [{Ref, [{C1,7}]}, {Self, [{C1,2}, {C2,1}]}]
+ = lists:sort(?stat:read([self(), Ref, make_ref()])),
+ [] = ?stat:read([]),
+ [] = ?stat:read([make_ref()]),
+ ?stat:flush([self(), Ref, make_ref()]).
+
+flush(_) ->
Ref = make_ref(),
- ok = ?stat:incr(x, 8),
- ok = ?stat:incr(x, Ref, 7),
- %% Flushing a reference doesn't affect registered pids.
- [{x,7}] = ?stat:flush(Ref),
- [] = ?stat:flush(Ref),
- [{x,8}] = ?stat:flush(),
- [] = ?stat:flush().
+ Ctr = '_',
+ true = ?stat:reg(Ref),
+ 1 = ?stat:incr(Ctr),
+ 3 = ?stat:incr(Ctr, self(), 2),
+ 2 = ?stat:incr(Ctr, Ref, 2),
+ Self = self(),
+ [{Self, [{Ctr, 3}]}] = ?stat:flush([self()]),
+ 1 = ?stat:incr(Ctr),
+ [{Ref, [{Ctr, 2}]}] = ?stat:flush([Ref]),
+ [{Self, [{Ctr, 1}]}] = ?stat:flush([self()]),
+ [] = ?stat:flush([self(), Ref]).
+
+%% ===========================================================================
+
+%% Keep incremented until a fold results in the specified value.
+fold(Ctr, Ref, N) ->
+ case ?stat:incr(Ctr, Ref, 0) of
+ N ->
+ ok;
+ M when M < N ->
+ erlang:yield(),
+ fold(Ctr, Ref, N)
+ end.
diff --git a/lib/diameter/test/diameter_sync_SUITE.erl b/lib/diameter/test/diameter_sync_SUITE.erl
index ab629fb1c1..457efab8ae 100644
--- a/lib/diameter/test/diameter_sync_SUITE.erl
+++ b/lib/diameter/test/diameter_sync_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -43,7 +43,7 @@
%% ===========================================================================
suite() ->
- [{timetrap, {seconds, 10}}].
+ [{timetrap, {seconds, 60}}].
all() ->
[{group, all},
diff --git a/lib/diameter/test/diameter_tls_SUITE.erl b/lib/diameter/test/diameter_tls_SUITE.erl
index 85b953dc1a..6cc34b20c5 100644
--- a/lib/diameter/test/diameter_tls_SUITE.erl
+++ b/lib/diameter/test/diameter_tls_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -127,7 +127,7 @@
%% ===========================================================================
suite() ->
- [{timetrap, {seconds, 10}}].
+ [{timetrap, {seconds, 60}}].
all() ->
[start_ssl,
diff --git a/lib/diameter/test/diameter_traffic_SUITE.erl b/lib/diameter/test/diameter_traffic_SUITE.erl
index 6eed8d3b5d..b03a9ce4d1 100644
--- a/lib/diameter/test/diameter_traffic_SUITE.erl
+++ b/lib/diameter/test/diameter_traffic_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -37,6 +37,10 @@
add_transports/1,
result_codes/1,
send_ok/1,
+ send_nok/1,
+ send_eval/1,
+ send_bad_answer/1,
+ send_protocol_error/1,
send_arbitrary/1,
send_unknown/1,
send_unknown_mandatory/1,
@@ -45,6 +49,9 @@
send_unsupported_app/1,
send_error_bit/1,
send_unsupported_version/1,
+ send_invalid_avp_bits/1,
+ send_invalid_avp_length/1,
+ send_invalid_reject/1,
send_long/1,
send_nopeer/1,
send_noapp/1,
@@ -80,15 +87,16 @@
%% diameter callbacks
-export([peer_up/3,
peer_down/3,
- pick_peer/5, pick_peer/6,
- prepare_request/4, prepare_request/5,
- prepare_retransmit/4,
- handle_answer/5, handle_answer/6,
- handle_error/5,
+ pick_peer/6, pick_peer/7,
+ prepare_request/5, prepare_request/6,
+ prepare_retransmit/5,
+ handle_answer/6, handle_answer/7,
+ handle_error/6,
handle_request/3]).
-include("diameter.hrl").
-include("diameter_gen_base_rfc3588.hrl").
+-include("diameter_gen_base_accounting.hrl").
%% ===========================================================================
@@ -101,13 +109,31 @@
-define(REALM, "erlang.org").
-define(HOST(Host, Realm), Host ++ [$.|Realm]).
--define(APP_ALIAS, base).
-define(EXTRA, an_extra_argument).
+
+-define(BASE, ?DIAMETER_DICT_COMMON).
+-define(ACCT, ?DIAMETER_DICT_ACCOUNTING).
+
+%% Sequence mask for End-to-End and Hop-by-Hop identifiers.
+-define(CLIENT_MASK, {1,26}). %% 1 in top 6 bits
+
+%% How to construct messages, as record or list.
-define(ENCODINGS, [list, record]).
--define(DICT, ?DIAMETER_DICT_COMMON).
+%% How to send answers, in a diameter_packet or not.
+-define(CONTAINERS, [pkt, msg]).
+
+%% Send over multiple connections that are mapped onto
+%% [{E,P} || E <- ?ENCODINGS, P <- ?CONTAINERS].
+-define(CONNECTIONS, [c0,c1,c2,c3]).
+
+%% Not really what we should be setting unless the message is sent in
+%% the common application but diameter doesn't care.
-define(APP_ID, ?DIAMETER_APP_ID_COMMON).
+%% An Application-ID the server doesn't support.
+-define(BAD_APP, 42).
+
%% Config for diameter:start_service/2.
-define(SERVICE(Name),
[{'Origin-Host', Name ++ "." ++ ?REALM},
@@ -115,11 +141,13 @@
{'Host-IP-Address', [?ADDR]},
{'Vendor-Id', 12345},
{'Product-Name', "OTP/diameter"},
- {'Acct-Application-Id', [?DIAMETER_APP_ID_COMMON]},
- {application, [{alias, ?APP_ALIAS},
- {dictionary, ?DIAMETER_DICT_COMMON},
- {module, ?MODULE},
- {answer_errors, callback}]}]).
+ {'Auth-Application-Id', [?DIAMETER_APP_ID_COMMON]},
+ {'Acct-Application-Id', [?DIAMETER_APP_ID_ACCOUNTING]},
+ {restrict_connections, false}
+ | [{application, [{dictionary, D},
+ {module, ?MODULE},
+ {answer_errors, callback}]}
+ || D <- [?BASE, ?ACCT]]]).
-define(SUCCESS,
?'DIAMETER_BASE_RESULT-CODE_DIAMETER_SUCCESS').
@@ -131,6 +159,8 @@
?'DIAMETER_BASE_RESULT-CODE_DIAMETER_APPLICATION_UNSUPPORTED').
-define(INVALID_HDR_BITS,
?'DIAMETER_BASE_RESULT-CODE_DIAMETER_INVALID_HDR_BITS').
+-define(INVALID_AVP_BITS,
+ ?'DIAMETER_BASE_RESULT-CODE_DIAMETER_INVALID_AVP_BITS').
-define(AVP_UNSUPPORTED,
?'DIAMETER_BASE_RESULT-CODE_DIAMETER_AVP_UNSUPPORTED').
-define(UNSUPPORTED_VERSION,
@@ -139,6 +169,8 @@
?'DIAMETER_BASE_RESULT-CODE_DIAMETER_REALM_NOT_SERVED').
-define(UNABLE_TO_DELIVER,
?'DIAMETER_BASE_RESULT-CODE_DIAMETER_UNABLE_TO_DELIVER').
+-define(INVALID_AVP_LENGTH,
+ ?'DIAMETER_BASE_RESULT-CODE_DIAMETER_INVALID_AVP_LENGTH').
-define(EVENT_RECORD,
?'DIAMETER_BASE_ACCOUNTING-RECORD-TYPE_EVENT_RECORD').
@@ -151,27 +183,35 @@
?'DIAMETER_BASE_TERMINATION-CAUSE_DIAMETER_LOGOUT').
-define(BAD_ANSWER,
?'DIAMETER_BASE_TERMINATION-CAUSE_DIAMETER_BAD_ANSWER').
+-define(USER_MOVED,
+ ?'DIAMETER_BASE_TERMINATION-CAUSE_DIAMETER_USER_MOVED').
-define(A, list_to_atom).
-define(L, atom_to_list).
--define(P(N), ?A("p_" ++ ?L(N))).
+
+-define(NAME(A,B), ?A(?L(A) ++ "," ++ ?L(B))).
%% ===========================================================================
suite() ->
- [{timetrap, {seconds, 10}}].
+ [{timetrap, {seconds, 60}}].
all() ->
[start, start_services, add_transports, result_codes]
- ++ [{group, E, P} || E <- ?ENCODINGS, P <- [[], [parallel]]]
+ ++ [{group, ?util:name([R,C,A]), P} || R <- ?ENCODINGS,
+ C <- ?CONTAINERS,
+ A <- ?ENCODINGS,
+ P <- [[], [parallel]]]
++ [remove_transports, stop_services, stop].
groups() ->
Ts = tc(),
- [{E, [], Ts} || E <- ?ENCODINGS].
+ [{?util:name([R,C,A]), [], Ts} || R <- ?ENCODINGS,
+ C <- ?CONTAINERS,
+ A <- ?ENCODINGS].
init_per_group(Name, Config) ->
- [{encode, Name} | Config].
+ [{group, Name} | Config].
end_per_group(_, _) ->
ok.
@@ -186,6 +226,10 @@ end_per_testcase(_, _) ->
%% established.
tc() ->
[send_ok,
+ send_nok,
+ send_eval,
+ send_bad_answer,
+ send_protocol_error,
send_arbitrary,
send_unknown,
send_unknown_mandatory,
@@ -194,6 +238,9 @@ tc() ->
send_unsupported_app,
send_error_bit,
send_unsupported_version,
+ send_invalid_avp_bits,
+ send_invalid_avp_length,
+ send_invalid_reject,
send_long,
send_nopeer,
send_noapp,
@@ -231,16 +278,19 @@ start(_Config) ->
start_services(_Config) ->
ok = diameter:start_service(?SERVER, ?SERVICE(?SERVER)),
- ok = diameter:start_service(?CLIENT, ?SERVICE(?CLIENT)).
+ ok = diameter:start_service(?CLIENT, [{sequence, ?CLIENT_MASK}
+ | ?SERVICE(?CLIENT)]).
add_transports(Config) ->
LRef = ?util:listen(?SERVER, tcp, [{capabilities_cb, fun capx/2}]),
- CRef = ?util:connect(?CLIENT, tcp, LRef),
- ?util:write_priv(Config, "transport", {LRef, CRef}).
+ Cs = [?util:connect(?CLIENT, tcp, LRef, [{id, C},
+ {capabilities, [osi(C)]}])
+ || C <- ?CONNECTIONS],
+ ?util:write_priv(Config, "transport", [LRef | Cs]).
remove_transports(Config) ->
- {LRef, CRef} = ?util:read_priv(Config, "transport"),
- ?util:disconnect(?CLIENT, CRef, ?SERVER, LRef).
+ [LRef | Cs] = ?util:read_priv(Config, "transport"),
+ [?util:disconnect(?CLIENT, C, ?SERVER, LRef) || C <- Cs].
stop_services(_Config) ->
ok = diameter:stop_service(?CLIENT),
@@ -253,11 +303,15 @@ capx(_, #diameter_caps{origin_host = {OH,DH}}) ->
io:format("connection: ~p -> ~p~n", [DH,OH]),
ok.
+osi(Id) ->
+ [$c,N] = atom_to_list(Id),
+ {'Origin-State-Id', N - $0}.
+
%% ===========================================================================
%% Ensure that result codes have the expected values.
result_codes(_Config) ->
- {2001, 3001, 3002, 3003, 3004, 3007, 3008, 5001, 5011}
+ {2001, 3001, 3002, 3003, 3004, 3007, 3008, 3009, 5001, 5011, 5014}
= {?SUCCESS,
?COMMAND_UNSUPPORTED,
?UNABLE_TO_DELIVER,
@@ -265,14 +319,50 @@ result_codes(_Config) ->
?TOO_BUSY,
?APPLICATION_UNSUPPORTED,
?INVALID_HDR_BITS,
+ ?INVALID_AVP_BITS,
?AVP_UNSUPPORTED,
- ?UNSUPPORTED_VERSION}.
+ ?UNSUPPORTED_VERSION,
+ ?INVALID_AVP_LENGTH}.
%% Send an ACR and expect success.
send_ok(Config) ->
Req = ['ACR', {'Accounting-Record-Type', ?EVENT_RECORD},
{'Accounting-Record-Number', 1}],
- #diameter_base_ACA{'Result-Code' = ?SUCCESS}
+
+ #diameter_base_accounting_ACA{'Result-Code' = ?SUCCESS}
+ = call(Config, Req).
+
+%% Send an accounting ACR that the server answers badly to.
+send_nok(Config) ->
+ Req = ['ACR', {'Accounting-Record-Type', ?EVENT_RECORD},
+ {'Accounting-Record-Number', 0}],
+
+ #'diameter_base_answer-message'{'Result-Code' = ?INVALID_AVP_BITS}
+ = call(Config, Req).
+
+%% Send an ACR and expect success.
+send_eval(Config) ->
+ Req = ['ACR', {'Accounting-Record-Type', ?EVENT_RECORD},
+ {'Accounting-Record-Number', 3}],
+
+ #diameter_base_accounting_ACA{'Result-Code' = ?SUCCESS}
+ = call(Config, Req).
+
+%% Send an accounting ACR that the server tries to answer with an
+%% inappropriate header, resulting in no answer being sent and the
+%% request timing out.
+send_bad_answer(Config) ->
+ Req = ['ACR', {'Accounting-Record-Type', ?EVENT_RECORD},
+ {'Accounting-Record-Number', 2}],
+ {error, timeout} = call(Config, Req).
+
+%% Send an ACR that the server callback answers explicitly with a
+%% protocol error.
+send_protocol_error(Config) ->
+ Req = ['ACR', {'Accounting-Record-Type', ?EVENT_RECORD},
+ {'Accounting-Record-Number', 4}],
+
+ #'diameter_base_answer-message'{'Result-Code' = ?TOO_BUSY}
= call(Config, Req).
%% Send an ASR with an arbitrary AVP and expect success and the same
@@ -324,7 +414,7 @@ send_unsupported(Config) ->
#'diameter_base_answer-message'{'Result-Code' = ?COMMAND_UNSUPPORTED}
= call(Config, Req).
-%% Send an unsupported command and expect 3007.
+%% Send an unsupported application and expect 3007.
send_unsupported_app(Config) ->
Req = ['STR', {'Termination-Cause', ?BAD_ANSWER}],
#'diameter_base_answer-message'{'Result-Code' = ?APPLICATION_UNSUPPORTED}
@@ -342,6 +432,29 @@ send_unsupported_version(Config) ->
#diameter_base_STA{'Result-Code' = ?UNSUPPORTED_VERSION}
= call(Config, Req).
+%% Send a request containing an incorrect AVP length.
+send_invalid_avp_bits(Config) ->
+ Req = ['STR', {'Termination-Cause', ?LOGOUT}],
+
+ #'diameter_base_answer-message'{'Result-Code' = ?INVALID_AVP_BITS}
+ = call(Config, Req).
+
+%% Send a request containing an AVP length that doesn't match the
+%% AVP's type.
+send_invalid_avp_length(Config) ->
+ Req = ['STR', {'Termination-Cause', ?LOGOUT}],
+
+ #'diameter_base_STA'{'Result-Code' = ?INVALID_AVP_LENGTH}
+ = call(Config, Req).
+
+%% Send a request containing 5xxx errors that the server rejects with
+%% 3xxx.
+send_invalid_reject(Config) ->
+ Req = ['STR', {'Termination-Cause', ?USER_MOVED}],
+
+ #'diameter_base_answer-message'{'Result-Code' = ?TOO_BUSY}
+ = call(Config, Req).
+
%% Send something long that will be fragmented by TCP.
send_long(Config) ->
Req = ['STR', {'Termination-Cause', ?LOGOUT},
@@ -516,32 +629,67 @@ call(Config, Req) ->
call(Config, Req, Opts) ->
Name = proplists:get_value(testcase, Config),
- Enc = proplists:get_value(encode, Config),
+ [Encoding, C, E] = ?util:name(proplists:get_value(group, Config)),
diameter:call(?CLIENT,
- ?APP_ALIAS,
- msg(Req, Enc),
- [{extra, [Name]} | Opts]).
+ dict(Req),
+ msg(Req, Encoding),
+ [{extra, [Name, client(E,C)]} | Opts]).
+
+client(E, C) ->
+ list_to_atom([$c, $0 + 2*codec(E) + container(C)]).
+
+client(N) ->
+ {codec(N bsr 1), container(N rem 2)}.
+
+codec(record) -> 0;
+codec(list) -> 1;
+codec(0) -> record;
+codec(1) -> list.
+
+%% Here we're just mapping booleans but the readable atoms are part of
+%% (constructed) group names, so it's good that they're readable.
-msg([_|_] = L, list) ->
- L;
+container(pkt) -> 0;
+container(msg) -> 1;
+container(0) -> pkt;
+container(1) -> msg.
+
+msg([H|T], record)
+ when H == 'ACR';
+ H == 'ACA' ->
+ ?ACCT:'#new-'(?ACCT:msg2rec(H), T);
msg([H|T], record) ->
- ?DICT:'#new-'(?DICT:msg2rec(H), T);
+ ?BASE:'#new-'(?BASE:msg2rec(H), T);
msg(T, _) ->
T.
+dict(['ACR' | _]) ->
+ ?ACCT;
+dict(#diameter_base_accounting_ACR{}) ->
+ ?ACCT;
+dict(_) ->
+ ?BASE.
+
%% Set only values that aren't already.
set([H|T], Vs) ->
[H | Vs ++ T];
+set(#diameter_base_accounting_ACR{} = Rec, Vs) ->
+ set(Rec, Vs, ?ACCT);
set(Rec, Vs) ->
- lists:foldl(fun({F,_} = FV, A) -> set(?DICT:'#get-'(F, A), FV, A) end,
+ set(Rec, Vs, ?BASE).
+
+set(Rec, Vs, Dict) ->
+ lists:foldl(fun({F,_} = FV, A) ->
+ set(Dict, Dict:'#get-'(F, A), FV, A)
+ end,
Rec,
Vs).
-set(E, FV, Rec)
+set(Dict, E, FV, Rec)
when E == undefined;
E == [] ->
- ?DICT:'#set-'(FV, Rec);
-set(_, _, Rec) ->
+ Dict:'#set-'(FV, Rec);
+set(_, _, _, Rec) ->
Rec.
%% ===========================================================================
@@ -557,42 +705,88 @@ peer_up(_SvcName, _Peer, State) ->
peer_down(_SvcName, _Peer, State) ->
State.
-%% pick_peer/5/6
+%% pick_peer/6-7
-pick_peer([Peer], _, ?CLIENT, _State, Name)
+pick_peer(Peers, _, ?CLIENT, _State, Name, Id)
when Name /= send_detach ->
- {ok, Peer}.
+ find(Id, Peers).
-pick_peer([_Peer], _, ?CLIENT, _State, send_nopeer, ?EXTRA) ->
+pick_peer(_Peers, _, ?CLIENT, _State, send_nopeer, _, ?EXTRA) ->
false;
-pick_peer([Peer], _, ?CLIENT, _State, send_detach, {_,_}) ->
- {ok, Peer}.
+pick_peer(Peers, _, ?CLIENT, _State, send_detach, Id, {_,_}) ->
+ find(Id, Peers).
+
+find(Id, Peers) ->
+ [P] = [P || P <- Peers, id(Id, P)],
+ {ok, P}.
-%% prepare_request/4/5
+id(Id, {Pid, _Caps}) ->
+ [{ref, _}, {type, _}, {options, Opts} | _]
+ = diameter:service_info(?CLIENT, Pid),
+ lists:member({id, Id}, Opts).
-prepare_request(_Pkt, ?CLIENT, {_Ref, _Caps}, send_discard) ->
+%% prepare_request/5-6
+
+prepare_request(_Pkt, ?CLIENT, {_Ref, _Caps}, send_discard, _) ->
{discard, unprepared};
-prepare_request(Pkt, ?CLIENT, {_Ref, Caps}, Name) ->
+prepare_request(Pkt, ?CLIENT, {_Ref, Caps}, Name, _) ->
{send, prepare(Pkt, Caps, Name)}.
-prepare_request(Pkt, ?CLIENT, {_Ref, Caps}, send_detach, _) ->
- {send, prepare(Pkt, Caps)}.
+prepare_request(Pkt, ?CLIENT, {_Ref, Caps}, send_detach, _, _) ->
+ {eval_packet, {send, prepare(Pkt, Caps)}, [fun log/2, detach]}.
+
+log(#diameter_packet{} = P, T) ->
+ io:format("~p: ~p~n", [T,P]).
+
+%% prepare/3
+
+prepare(Pkt, Caps, send_invalid_avp_bits) ->
+ Req = prepare(Pkt, Caps),
+ %% Last AVP in our STR is Termination-Cause of type Unsigned32:
+ %% set its length improperly.
+ #diameter_packet{header = #diameter_header{length = L},
+ bin = B}
+ = E
+ = diameter_codec:encode(?BASE, Pkt#diameter_packet{msg = Req}),
+ Offset = L - 7, %% to AVP Length
+ <<H:Offset/binary, 12:24/integer, T:4/binary>> = B,
+ E#diameter_packet{bin = <<H/binary, 13:24/integer, T/binary>>};
+
+prepare(Pkt, Caps, N)
+ when N == send_invalid_avp_length;
+ N == send_invalid_reject ->
+ Req = prepare(Pkt, Caps),
+ %% Second last AVP in our STR is Auth-Application-Id of type
+ %% Unsigned32: Send a value of length 8.
+ #diameter_packet{header = #diameter_header{length = L},
+ bin = B0}
+ = E
+ = diameter_codec:encode(?BASE, Pkt#diameter_packet{msg = Req}),
+ Offset = L - 7 - 12, %% to AVP Length
+ <<H0:Offset/binary, 12:24/integer, T:16/binary>> = B0,
+ <<V, L:24/integer, H/binary>> = H0, %% assert
+ E#diameter_packet{bin = <<V,
+ (L+4):24/integer,
+ H/binary,
+ 16:24/integer,
+ 0:32/integer,
+ T/binary>>};
prepare(Pkt, Caps, send_unsupported) ->
Req = prepare(Pkt, Caps),
#diameter_packet{bin = <<H:5/binary, _CmdCode:3/binary, T/binary>>}
= E
- = diameter_codec:encode(?DICT, Pkt#diameter_packet{msg = Req}),
+ = diameter_codec:encode(?BASE, Pkt#diameter_packet{msg = Req}),
E#diameter_packet{bin = <<H/binary, 42:24/integer, T/binary>>};
prepare(Pkt, Caps, send_unsupported_app) ->
Req = prepare(Pkt, Caps),
#diameter_packet{bin = <<H:8/binary, _ApplId:4/binary, T/binary>>}
= E
- = diameter_codec:encode(?DICT, Pkt#diameter_packet{msg = Req}),
- E#diameter_packet{bin = <<H/binary, 42:32/integer, T/binary>>};
+ = diameter_codec:encode(?BASE, Pkt#diameter_packet{msg = Req}),
+ E#diameter_packet{bin = <<H/binary, ?BAD_APP:32/integer, T/binary>>};
prepare(Pkt, Caps, send_error_bit) ->
#diameter_packet{header = Hdr} = Pkt,
@@ -611,8 +805,10 @@ prepare(Pkt, Caps, send_anything) ->
prepare(Pkt, Caps, _Name) ->
prepare(Pkt, Caps).
+%% prepare/2
+
prepare(#diameter_packet{msg = Req}, Caps)
- when is_record(Req, diameter_base_ACR);
+ when is_record(Req, diameter_base_accounting_ACR);
'ACR' == hd(Req) ->
#diameter_caps{origin_host = {OH, _},
origin_realm = {OR, DR}}
@@ -661,25 +857,42 @@ prepare(#diameter_packet{msg = Req}, Caps)
{'Destination-Realm', DR},
{'Auth-Application-Id', ?APP_ID}]).
-%% prepare_retransmit/4
+%% prepare_retransmit/5
-prepare_retransmit(_Pkt, false, _Peer, _Name) ->
+prepare_retransmit(_Pkt, false, _Peer, _Name, _Id) ->
discard.
-%% handle_answer/5/6
+%% handle_answer/6-7
-handle_answer(Pkt, Req, ?CLIENT, Peer, Name) ->
+handle_answer(Pkt, Req, ?CLIENT, Peer, Name, _Id) ->
answer(Pkt, Req, Peer, Name).
-handle_answer(Pkt, _Req, ?CLIENT, _Peer, send_detach, {Pid, Ref}) ->
+handle_answer(Pkt, _Req, ?CLIENT, _Peer, send_detach, _Id, {Pid, Ref}) ->
Pid ! {Ref, Pkt}.
-answer(#diameter_packet{msg = Rec, errors = []}, _Req, _Peer, _) ->
+answer(Pkt, Req, _Peer, Name) ->
+ #diameter_packet{header = H, msg = Rec, errors = Es} = Pkt,
+ ApplId = app(Req, Name),
+ #diameter_header{application_id = ApplId} = H, %% assert
+ answer(Rec, Es, Name).
+
+answer(Rec, [_|_], N)
+ when N == send_invalid_avp_bits;
+ N == send_invalid_avp_length;
+ N == send_invalid_reject ->
+ Rec;
+answer(Rec, [], _) ->
Rec.
-%% handle_error/5
+app(_, send_unsupported_app) ->
+ ?BAD_APP;
+app(Req, _) ->
+ Dict = dict(Req),
+ Dict:id().
+
+%% handle_error/6
-handle_error(Reason, _Req, ?CLIENT, _Peer, _Name) ->
+handle_error(Reason, _Req, ?CLIENT, _Peer, _Name, _Id) ->
{error, Reason}.
%% handle_request/3
@@ -687,13 +900,64 @@ handle_error(Reason, _Req, ?CLIENT, _Peer, _Name) ->
%% Note that diameter will set Result-Code and Failed-AVPs if
%% #diameter_packet.errors is non-null.
-handle_request(Pkt, ?SERVER, {_Ref, Caps}) ->
- request(Pkt, Caps).
+handle_request(#diameter_packet{header = H, msg = M}, ?SERVER, {_Ref, Caps}) ->
+ #diameter_header{end_to_end_id = EI,
+ hop_by_hop_id = HI}
+ = H,
+ {V,B} = ?CLIENT_MASK,
+ V = EI bsr B, %% assert
+ V = HI bsr B, %%
+ #diameter_caps{origin_state_id = {_,[N]}} = Caps,
+ answer(client(N), request(M, Caps)).
+
+answer(T, {Tag, Action, Post}) ->
+ {Tag, answer(T, Action), Post};
+answer({E,C}, {reply, Ans}) ->
+ answer(C, {reply, msg(Ans, E)});
+answer(pkt, {reply, Ans}) ->
+ {reply, #diameter_packet{msg = Ans}};
+answer(_, T) ->
+ T.
-request(#diameter_packet{msg
- = #diameter_base_ACR{'Session-Id' = SId,
- 'Accounting-Record-Type' = RT,
- 'Accounting-Record-Number' = RN}},
+%% send_nok
+request(#diameter_base_accounting_ACR{'Accounting-Record-Number' = 0},
+ _) ->
+ {eval_packet, {protocol_error, ?INVALID_AVP_BITS}, [fun log/2, invalid]};
+
+%% send_bad_answer
+request(#diameter_base_accounting_ACR{'Session-Id' = SId,
+ 'Accounting-Record-Type' = RT,
+ 'Accounting-Record-Number' = 2 = RN},
+ #diameter_caps{origin_host = {OH, _},
+ origin_realm = {OR, _}}) ->
+ Ans = ['ACA', {'Result-Code', ?SUCCESS},
+ {'Session-Id', SId},
+ {'Origin-Host', OH},
+ {'Origin-Realm', OR},
+ {'Accounting-Record-Type', RT},
+ {'Accounting-Record-Number', RN}],
+
+ {reply, #diameter_packet{header = #diameter_header{is_error = true},%% NOT
+ msg = Ans}};
+
+%% send_eval
+request(#diameter_base_accounting_ACR{'Session-Id' = SId,
+ 'Accounting-Record-Type' = RT,
+ 'Accounting-Record-Number' = 3 = RN},
+ #diameter_caps{origin_host = {OH, _},
+ origin_realm = {OR, _}}) ->
+ Ans = ['ACA', {'Result-Code', ?SUCCESS},
+ {'Session-Id', SId},
+ {'Origin-Host', OH},
+ {'Origin-Realm', OR},
+ {'Accounting-Record-Type', RT},
+ {'Accounting-Record-Number', RN}],
+ {eval, {reply, Ans}, {erlang, now, []}};
+
+%% send_ok
+request(#diameter_base_accounting_ACR{'Session-Id' = SId,
+ 'Accounting-Record-Type' = RT,
+ 'Accounting-Record-Number' = 1 = RN},
#diameter_caps{origin_host = {OH, _},
origin_realm = {OR, _}}) ->
{reply, ['ACA', {'Result-Code', ?SUCCESS},
@@ -703,39 +967,56 @@ request(#diameter_packet{msg
{'Accounting-Record-Type', RT},
{'Accounting-Record-Number', RN}]};
-request(#diameter_packet{msg = #diameter_base_ASR{'Session-Id' = SId,
- 'AVP' = Avps}},
+%% send_protocol_error
+request(#diameter_base_accounting_ACR{'Accounting-Record-Number' = 4},
#diameter_caps{origin_host = {OH, _},
origin_realm = {OR, _}}) ->
- {reply, #diameter_base_ASA{'Result-Code' = ?SUCCESS,
- 'Session-Id' = SId,
- 'Origin-Host' = OH,
- 'Origin-Realm' = OR,
- 'AVP' = Avps}};
+ Ans = ['answer-message', {'Result-Code', ?TOO_BUSY},
+ {'Origin-Host', OH},
+ {'Origin-Realm', OR}],
+ {reply, Ans};
-request(#diameter_packet{msg = #diameter_base_STR{'Termination-Cause' = T}},
+request(#diameter_base_ASR{'Session-Id' = SId,
+ 'AVP' = Avps},
+ #diameter_caps{origin_host = {OH, _},
+ origin_realm = {OR, _}}) ->
+ {reply, ['ASA', {'Result-Code', ?SUCCESS},
+ {'Session-Id', SId},
+ {'Origin-Host', OH},
+ {'Origin-Realm', OR},
+ {'AVP', Avps}]};
+
+%% send_invalid_reject
+request(#diameter_base_STR{'Termination-Cause' = ?USER_MOVED}, _Caps) ->
+ {protocol_error, ?TOO_BUSY};
+
+%% send_noreply
+request(#diameter_base_STR{'Termination-Cause' = T},
_Caps)
when T /= ?LOGOUT ->
discard;
-request(#diameter_packet{msg = #diameter_base_STR{'Destination-Realm'= R}},
+%% send_destination_5
+request(#diameter_base_STR{'Destination-Realm'= R},
#diameter_caps{origin_realm = {OR, _}})
when R /= undefined, R /= OR ->
{protocol_error, ?REALM_NOT_SERVED};
-request(#diameter_packet{msg = #diameter_base_STR{'Destination-Host'= [H]}},
+%% send_destination_6
+request(#diameter_base_STR{'Destination-Host'= [H]},
#diameter_caps{origin_host = {OH, _}})
when H /= OH ->
{protocol_error, ?UNABLE_TO_DELIVER};
-request(#diameter_packet{msg = #diameter_base_STR{'Session-Id' = SId}},
+request(#diameter_base_STR{'Session-Id' = SId},
#diameter_caps{origin_host = {OH, _},
origin_realm = {OR, _}}) ->
- {reply, #diameter_base_STA{'Result-Code' = ?SUCCESS,
- 'Session-Id' = SId,
- 'Origin-Host' = OH,
- 'Origin-Realm' = OR}};
+ {reply, ['STA', {'Result-Code', ?SUCCESS},
+ {'Session-Id', SId},
+ {'Origin-Host', OH},
+ {'Origin-Realm', OR}]};
-request(#diameter_packet{msg = #diameter_base_RAR{}}, _Caps) ->
+%% send_error
+request(#diameter_base_RAR{}, _Caps) ->
receive after 2000 -> ok end,
{protocol_error, ?TOO_BUSY}.
diff --git a/lib/diameter/test/diameter_util.erl b/lib/diameter/test/diameter_util.erl
index 0c42f955ad..5af4ad9ba5 100644
--- a/lib/diameter/test/diameter_util.erl
+++ b/lib/diameter/test/diameter_util.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -24,7 +24,8 @@
%%
%% generic
--export([consult/2,
+-export([name/1,
+ consult/2,
run/1,
fold/3,
foldl/3,
@@ -35,7 +36,8 @@
lport/3,
listen/2, listen/3,
connect/3, connect/4,
- disconnect/4]).
+ disconnect/4,
+ info/0]).
%% common_test-specific
-export([write_priv/3,
@@ -44,6 +46,21 @@
-define(L, atom_to_list).
+
+%% ---------------------------------------------------------------------------
+%% name/2
+%%
+%% Contruct and deconstruct lists of atoms as atoms to work around
+%% group names in common_test being restricted to atoms.
+
+name(Names)
+ when is_list(Names) ->
+ list_to_atom(string:join([atom_to_list(A) || A <- Names], ","));
+
+name(A)
+ when is_atom(A) ->
+ [list_to_atom(S) || S <- string:tokens(atom_to_list(A), ",")].
+
%% ---------------------------------------------------------------------------
%% consult/2
%%
@@ -262,7 +279,10 @@ listen(SvcName, Prot) ->
listen(SvcName, Prot, []).
listen(SvcName, Prot, Opts) ->
- add_transport(SvcName, {listen, opts(Prot, listen) ++ Opts}).
+ SvcName = diameter:service_info(SvcName, name), %% assert
+ Ref = add_transport(SvcName, {listen, opts(Prot, listen) ++ Opts}),
+ true = transport(SvcName, Ref), %% assert
+ Ref.
%% ---------------------------------------------------------------------------
%% connect/2-3
@@ -275,15 +295,22 @@ connect(Client, Prot, LRef) ->
connect(Client, Prot, LRef, Opts) ->
[PortNr] = lport(Prot, LRef, 20),
- Ref = add_transport(Client, {connect, opts(Prot, PortNr) ++ Opts}),
+ Client = diameter:service_info(Client, name), %% assert
true = diameter:subscribe(Client),
+ Ref = add_transport(Client, {connect, opts(Prot, PortNr) ++ Opts}),
+ true = transport(Client, Ref), %% assert
+
ok = receive
{diameter_event, Client, {up, Ref, _, _, _}} -> ok
- after 2000 ->
+ after 10000 ->
{Client, Prot, PortNr, process_info(self(), messages)}
end,
Ref.
+transport(SvcName, Ref) ->
+ [Ref] == [R || [{ref, R} | _] <- diameter:service_info(SvcName, transport),
+ R == Ref].
+
%% ---------------------------------------------------------------------------
%% disconnect/4
%%
@@ -295,7 +322,7 @@ disconnect(Client, Ref, Server, LRef) ->
ok = diameter:remove_transport(Client, Ref),
ok = receive
{diameter_event, Server, {down, LRef, _, _}} -> ok
- after 2000 ->
+ after 10000 ->
{Client, Ref, Server, LRef, process_info(self(), messages)}
end.
@@ -320,3 +347,17 @@ opts(listen) ->
[];
opts(PortNr) ->
[{raddr, ?ADDR}, {rport, PortNr}].
+
+%% ---------------------------------------------------------------------------
+%% info/0
+
+info() ->
+ [_|_] = Svcs = diameter:services(), %% assert
+ run([[fun info/1, S] || S <- Svcs]).
+
+info(S) ->
+ [_|_] = Keys = diameter:service_info(S, keys),
+ [] = run([[fun info/2, K, S] || K <- Keys]).
+
+info(Key, SvcName) ->
+ [{Key, _}] = diameter:service_info(SvcName, [Key]).
diff --git a/lib/diameter/test/diameter_watchdog_SUITE.erl b/lib/diameter/test/diameter_watchdog_SUITE.erl
index ff40326947..7ce09e93ca 100644
--- a/lib/diameter/test/diameter_watchdog_SUITE.erl
+++ b/lib/diameter/test/diameter_watchdog_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -30,11 +30,21 @@
end_per_suite/1]).
%% testcases
--export([reopen/1, reopen/4]).
+-export([reopen/1, reopen/4, reopen/7]).
--export([start/3, %% diameter_transport callback
- id/1, %% jitter callback
- run/1]).
+-export([id/1, %% jitter callback
+ run1/1]).
+
+%% diameter_app callbacks
+-export([peer_up/3,
+ peer_down/3]).
+
+%% gen_tcp-ish interface
+-export([listen/2,
+ accept/1,
+ connect/3,
+ send/2,
+ setopts/2]).
-include("diameter.hrl").
-include("diameter_ct.hrl").
@@ -43,33 +53,21 @@
-define(util, diameter_util).
--define(BASE, diameter_gen_base_rfc3588).
--define(APPL_ID, diameter_gen_base_rfc3588:id()).
--define(SUCCESS, 2001). %% DIAMETER_SUCCESS
-
-%% Addresses for the local and remote diameter nodes. The values don't
-%% matter since we're faking transport.
--define(LOCALHOST, {127,0,0,1}).
--define(REMOTEHOST, {10,0,0,1}).
-
--define(CAPS, #diameter_caps{origin_host = "node.innan.com",
- origin_realm = "innan.com",
- host_ip_address = [?LOCALHOST],
- vendor_id = 1022,
- product_name = "remote",
- auth_application_id = [?APPL_ID]}).
-
--define(APPL, #diameter_app{alias = ?MODULE,
- dictionary = ?BASE,
- module = [?MODULE],
- init_state = now(),
- id = ?APPL_ID,
- mutable = false}).
-
-%% Service record maintained by our faked service process.
--define(SERVICE, #diameter_service{pid = self(),
- capabilities = ?CAPS,
- applications = [?APPL]}).
+-define(BASE, ?DIAMETER_DICT_COMMON).
+-define(REALM, "erlang.org").
+-define(ADDR, {127,0,0,1}).
+
+%% Config for diameter:start_service/2.
+-define(SERVICE(Name),
+ [{'Origin-Host', Name ++ "." ++ ?REALM},
+ {'Origin-Realm', ?REALM},
+ {'Host-IP-Address', [?ADDR]},
+ {'Vendor-Id', 42},
+ {'Product-Name', "OTP/diameter"},
+ {'Auth-Application-Id', [?DIAMETER_APP_ID_COMMON]},
+ {application, [{alias, Name},
+ {dictionary, ?BASE},
+ {module, ?MODULE}]}]).
%% Watchdog timer as a callback.
-define(WD(T), {?MODULE, id, [T]}).
@@ -82,28 +80,28 @@
F_ <- [fun(T__) -> T__ end,
fun(T__) -> ?WD(T__) end]]]).
-%% Transport types.
--define(TRANSPORTS, [connect, accept]).
+%% Watchdog timer of the misbehaving peer.
+-define(PEER_WD, 10000).
-%% Message over the transport interface.
--define(TMSG(T), {diameter, T}).
-
-%% Receive a message within a specified time.
--define(RECV(T, Timeout),
- receive T -> now()
- after Timeout -> ?ERROR({timeout, Timeout})
+%% Receive a watchdog event within a specified time.
+-define(EVENT(T, Tmo),
+ receive #diameter_event{info = T} -> now()
+ after Tmo -> ?ERROR({timeout, Tmo})
end).
-%% Receive a message in a given number of watchdogs, plus or minus
+%% Receive an event in a given number of watchdogs, plus or minus
%% half. Note that the call to now_diff assumes left to right
%% evaluation order.
--define(RECV(T, N, WdL, WdH),
+-define(EVENT(T, N, WdL, WdH),
[?ERROR({received, _Elapsed_, _LowerBound_, N, WdL})
|| _UpperBound_ <- [(N)*(WdH) + (WdH) div 2],
- _Elapsed_ <- [now_diff(now(), ?RECV(T, _UpperBound_))],
+ _Elapsed_ <- [now_diff(now(), ?EVENT(T, _UpperBound_))],
_LowerBound_ <- [(N)*(WdL) - (WdL) div 2],
_Elapsed_ =< _LowerBound_*1000]).
+-define(EVENT(T, N, Wd),
+ ?EVENT(T, N, Wd, Wd)).
+
%% A timeout that ensures one watchdog. The ensure only one watchdog
%% requires (Wd + 2000) + 1000 < 2*(Wd - 2000) ==> 7000 < Wd for the
%% case with random jitter.
@@ -112,7 +110,7 @@
%% ===========================================================================
suite() ->
- [{timetrap, {minutes, 6}}].%% enough for 11 watchdogs @ 30 sec plus jitter
+ [{timetrap, {minutes, 10}}].%% enough for 17 watchdogs @ 30 sec plus jitter
all() ->
[reopen].
@@ -134,52 +132,97 @@ end_per_suite(_Config) ->
%% implement a transport process that plays the role of the peer
%% Diameter node.
+%reopen(_) ->
+% reopen(connect, ?WD(10000), 1, 'DWR');
+
reopen(_) ->
- [] = ?util:run([{?MODULE, [run, [reopen, Wd, T, N, M]]}
- || Wd <- ?WD_TIMERS,
- T <- ?TRANSPORTS,
- N <- [0,1,2],
- M <- ['DWR', 'DWA', other]]).
+ [] = run([[reopen, T, Wd, N, M]
+ || Wd <- ?WD_TIMERS, %% watchdog_timer value
+ T <- [listen, connect], %% watchdog to test
+ N <- [0,1,2], %% DWR's to answer before ignoring
+ M <- ['DWR', 'DWA', 'RAA']]). %% how to induce failback
-reopen(Wd, Type, N, What) ->
- Ref = make_ref(),
+reopen(Type, Wd, N, M) ->
+ Server = start_service(),
+ Client = start_service(),
- %% The maker of transport processes.
- TPid = start({N, Wd, What, Ref}),
+ %% The peer to the transport whose watchdog is tested is given a
+ %% long watchdog timeout so that it doesn't send DWR of its own.
+ {Node, Peer} = {{[], Wd}, {[{module, ?MODULE}], ?WD(?PEER_WD)}},
- %% Act like diameter_service and start the watchdog process, which
- %% in turn starts a peer_fsm process, which in turn starts a
- %% transport process by way of start/3. Messages received by the
- %% testcase are those sent by diameter_watchdog to the service
- %% process (= process starting the watchdog).
- WPid1 = watchdog(Type, Ref, TPid, Wd),
+ {{LH,LW},{CH,CW}} = case Type of
+ listen -> {Node, Peer};
+ connect -> {Peer, Node}
+ end,
- %% Low/high watchdog timeouts.
- WdL = jitter(Wd, -2000),
- WdH = jitter(Wd, 2000),
+ LO = [{transport_module, diameter_tcp},
+ {transport_config, LH ++ [{ip, ?ADDR}, {port, 0}]},
+ {watchdog_timer, LW}],
+
+ {ok, LRef} = diameter:add_transport(Server, {listen, LO}),
+
+ [LP] = ?util:lport(tcp, LRef, 20),
+
+ CO = [{transport_module, diameter_tcp},
+ {transport_config, CH ++ [{ip, ?ADDR}, {port, 0},
+ {raddr, ?ADDR}, {rport, LP}]},
+ {watchdog_timer, CW}],
+
+ %% Use a temporary process to ensure the connecting transport is
+ %% added only once events from the listening transport are
+ %% subscribed to.
+ Pid = spawn(fun() -> receive _ -> ok end end),
+
+ [] = run([[reopen, Type, T, LRef, Pid, Wd, N, M]
+ || T <- [{listen, Server}, {connect, Client, CO}]]).
+
+%% start_service/1
+
+start_service() ->
+ Name = hostname(),
+ ok = diameter:start_service(Name, [{monitor, self()} | ?SERVICE(Name)]),
+ Name.
+
+%% reopen/7
+reopen(Type, {listen = T, SvcName}, Ref, Pid, Wd, N, M) ->
+ diameter:subscribe(SvcName),
+ Pid ! ok,
+ recv(Type, T, SvcName, Ref, Wd, N, M);
+
+reopen(Type, {connect = T, SvcName, Opts}, _, Pid, Wd, N, M) ->
+ diameter:subscribe(SvcName),
+ MRef = erlang:monitor(process, Pid),
+ receive {'DOWN', MRef, process, _, _} -> ok end,
+ {ok, Ref} = diameter:add_transport(SvcName, {T, Opts}),
+ recv(Type, T, SvcName, Ref, Wd, N, M).
+
+%% recv/7
+
+%% The watchdog to be tested.
+recv(Type, Type, _SvcName, Ref, Wd, N, M) ->
%% Connection should come up immediately as a consequence of
%% starting the watchdog process. In the accepting case this
%% results in a new watchdog on a transport waiting for a new
%% connection.
- ?RECV({connection_up, WPid1, _}, 1000),
- WPid2 = case Type of
- connect ->
- WPid1;
- accept ->
- watchdog(Type, Ref, TPid, Wd)
- end,
+ ?EVENT({watchdog, Ref, _, {initial, okay}, _}, 2000),
+ ?EVENT({up, Ref, _, _, #diameter_packet{}}, 0),
+
+ %% Low/high watchdog timeouts.
+ WdL = jitter(Wd, -2000),
+ WdH = jitter(Wd, 2000),
%% OKAY Timer expires & Failover()
%% Pending SetWatchdog() SUSPECT
%%
- %% Since our transport is replying to N DWR's before becoming
- %% silent, we should go down after N+2 watchdog_timer expirations:
- %% that is, after the first unanswered DWR. Knowing the min/max
- %% watchdog timeout values gives the time interval in which the
- %% down message is expected.
- ?RECV({connection_down, WPid1}, N+2, WdL, WdH),
+ %% The peer replies to N DWR's before becoming silent, we should
+ %% go down after N+2 watchdog_timer expirations: that is, after
+ %% the first unanswered DWR. Knowing the min/max watchdog timeout
+ %% values gives the time interval in which the event is expected.
+
+ ?EVENT({watchdog, Ref, _, {okay, suspect}, _}, N+2, WdL, WdH),
+ ?EVENT({down, Ref, _, _}, 0),
%% SUSPECT Receive DWA Pending = FALSE
%% Failback()
@@ -188,9 +231,11 @@ reopen(Wd, Type, N, What) ->
%% SUSPECT Receive non-DWA Failback()
%% SetWatchdog() OKAY
%%
- %% The transport receives a message before the expiry of another
- %% watchdog to induce failback.
- ?RECV({connection_up, WPid1}, WdH),
+ %% The peer sends a message before the expiry of another watchdog
+ %% to induce failback.
+
+ ?EVENT({watchdog, Ref, _, {suspect, okay}, _}, WdH + 2000),
+ ?EVENT({up, Ref, _, _}, 0),
%% OKAY Timer expires & SendWatchdog()
%% !Pending SetWatchdog()
@@ -199,30 +244,35 @@ reopen(Wd, Type, N, What) ->
%% OKAY Timer expires & Failover()
%% Pending SetWatchdog() SUSPECT
%%
- %% The transport is still not responding to watchdogs so the
- %% connection should go back down after either one or two watchdog
- %% expiries, depending on whether or not DWA restored the connection.
- F = choose(What == 'DWA', 2, 1),
- ?RECV({connection_down, WPid1}, F, WdL, WdH),
+ %% The peer is now ignoring all watchdogs so the connection goes
+ %% back down after either one or two watchdog expiries, depending
+ %% on whether or not DWA restored the connection.
+
+ F = choose(M == 'DWA', 2, 1),
+ ?EVENT({watchdog, Ref, _, {okay, suspect}, _}, F, WdL, WdH),
+ ?EVENT({down, Ref, _, _}, 0),
%% SUSPECT Timer expires CloseConnection()
%% SetWatchdog() DOWN
%%
+ %% Non-response brings the connection down after another timeout.
+
+ ?EVENT({watchdog, Ref, _, {suspect, down}, _}, 1, WdL, WdH),
+
%% DOWN Timer expires AttemptOpen()
%% SetWatchdog() DOWN
%%
- %% Our transport tells us when the fake connection is
- %% reestablished, which should happen after another couple of
- %% watchdog expiries, the first bringing the watchdog to state
- %% DOWN, the second triggering an attempt to reopen the
- %% connection.
- ?RECV({reopen, Ref}, 2, WdL, WdH),
-
%% DOWN Connection up NumDWA = 0
%% SendWatchdog()
%% SetWatchdog()
%% Pending = TRUE REOPEN
%%
+ %% The connection is reestablished after another timeout.
+
+ recv_reopen(Type, Ref, WdL, WdH),
+
+ %% REOPEN Receive non-DWA Throwaway() REOPEN
+ %%
%% REOPEN Receive DWA & Pending = FALSE
%% NumDWA < 2 NumDWA++ REOPEN
%%
@@ -230,312 +280,259 @@ reopen(Wd, Type, N, What) ->
%% NumDWA == 2 NumDWA++
%% Failback() OKAY
%%
- %% Now the watchdog should require three received DWA's before
- %% taking the connection back up. The first DWR is sent directly
- %% after capabilities exchange so it should take no more than two
- %% watchdog expiries.
- ?RECV({connection_up, WPid2, _}, 2, WdL, WdH).
+ %% REOPEN Timer expires & SendWatchdog()
+ %% !Pending SetWatchdog()
+ %% Pending = TRUE REOPEN
+ %%
+ %% An exchange of 3 watchdogs (the first directly after
+ %% capabilities exchange) brings the connection back up.
-%% ===========================================================================
+ ?EVENT({watchdog, Ref, _, {reopen, okay}, _}, 2, WdL, WdH),
+ ?EVENT({up, Ref, _, _, #diameter_packet{}}, 0),
-%% Start the fake transport process. From diameter's point of view
-%% it's started when diameter calls start/3. We start it before this
-%% happens since we use the same fake transport each time diameter
-%% calls start/3. The process lives and dies with the test case.
-start(Config) ->
- Pid = self(),
- spawn(fun() -> loop(init(Pid, Config)) end).
-
-%% Transport start from diameter. This may be called multiple times
-%% depending on the testcase.
-start({Type, _Ref}, #diameter_service{}, Pid) ->
- Ref = make_ref(),
- MRef = erlang:monitor(process, Pid),
- Pid ! {start, self(), Type, Ref},
- {Ref, TPid} = receive
- {Ref, _} = T ->
- T;
- {'DOWN', MRef, process, _, _} = T ->
- T
- end,
- erlang:demonitor(MRef, [flush]),
- {ok, TPid}.
+ %% Non-response brings it down again.
-%% id/1
+ ?EVENT({watchdog, Ref, _, {okay, suspect}, _}, 2, WdL, WdH),
+ ?EVENT({down, Ref, _, _}, 0),
+ ?EVENT({watchdog, Ref, _, {suspect, down}, _}, 1, WdL, WdH),
-id(T) ->
- T.
+ %% Reestablish after another watchdog.
-%% ===========================================================================
+ recv_reopen(Type, Ref, WdL, WdH),
-choose(true, X, _) -> X;
-choose(false, _, X) -> X.
+ %% REOPEN Timer expires & NumDWA = -1
+ %% Pending & SetWatchdog()
+ %% NumDWA >= 0 REOPEN
+ %%
+ %% REOPEN Timer expires & CloseConnection()
+ %% Pending & SetWatchdog()
+ %% NumDWA < 0 DOWN
+ %%
+ %% Peer is now ignoring all watchdogs go down again after 2
+ %% timeouts.
-%% run/1
-%%
-%% A more useful badmatch in case of failure.
+ ?EVENT({watchdog, Ref, _, {reopen, down}, _}, 2, WdL, WdH);
-run([F|A]) ->
- ok = try
- apply(?MODULE, F, A),
- ok
- catch
- E:R ->
- {A, E, R, erlang:get_stacktrace()}
- end.
+%% The misbehaving peer.
+recv(_, Type, SvcName, Ref, Wd, N, M) ->
+ %% First transport process.
+ ?EVENT({watchdog, Ref, _, {initial, okay}, _}, 1000),
+ ?EVENT({up, Ref, _, _, #diameter_packet{}}, 0),
+ reg(Type, Ref, SvcName, {SvcName, {Wd,N,M}}),
+ ?EVENT({watchdog, Ref, _, {okay, down}, _}, infinity),
-%% now_diff/2
+ %% Second transport process.
+ ?EVENT({watchdog, Ref, _, {_, reopen}, _}, infinity),
+ reg(Type, Ref, SvcName, 3),
+ ?EVENT({watchdog, Ref, _, {_, down}, _}, infinity),
-now_diff(T1, T2) ->
- timer:now_diff(T2, T1).
+ %% Third transport process.
+ ?EVENT({watchdog, Ref, _, {_, reopen}, _}, infinity),
+ reg(Type, Ref, SvcName, 0),
+ ?EVENT({watchdog, Ref, _, {_, down}, _}, infinity),
-%% jitter/2
+ ok.
-jitter(?WD(T), _) ->
- T;
-jitter(T,D) ->
- T+D.
+%% recv_reopen/4
-%% watchdog/4
-%%
-%% Fake the call from diameter_service. The watchdog process will send
-%% messages to the calling "service" process so our tests are that the
-%% watchdog responds as expected.
-
-watchdog(Type, Ref, TPid, Wd) ->
- Opts = [{transport_module, ?MODULE},
- {transport_config, TPid},
- {watchdog_timer, Wd}],
- {_MRef, Pid} = diameter_watchdog:start({Type, Ref},
- {false, Opts, false, ?SERVICE}),
- Pid.
+recv_reopen(connect, Ref, WdL, WdH) ->
+ ?EVENT({watchdog, Ref, _, {_, reopen}, _}, 1, WdL, WdH),
+ ?EVENT({reconnect, Ref, _}, 0);
-%% ===========================================================================
+recv_reopen(listen, Ref, _, _) ->
+ ?EVENT({watchdog, Ref, _, {_, reopen}, _}, 1, ?PEER_WD).
-%% Transport process implmentation. Fakes reception of messages by
-%% sending fakes to the parent (peer fsm) process that called start/3.
-
--record(transport,
- {type, %% connect | accept | manager
- parent, %% pid() of peer_fsm/ervice process
- open = false, %% done with capabilities exchange?
- config}).%% testcase-specific config
-
-%% init/2
-
-%% Testcase starting the manager.
-init(SvcPid, {_,_,_,_} = Config) ->
- putr(peer, [{'Origin-Host', hostname() ++ ".utan.com"},
- {'Origin-Realm', "utan.com"}]),
- #transport{type = manager,
- parent = monitor(SvcPid),
- config = Config};
-
-%% Manager starting a transport.
-init(_, {Type, ParentPid, SvcPid, TwinPid, Peer, {N,_,_,_} = Config}) ->
- putr(peer, Peer),
- putr(service, SvcPid),
- putr(count, init(Type, ParentPid, TwinPid, N)),%% number of DWR's to answer
- #transport{type = Type,
- parent = monitor(ParentPid),
- config = Config}.
-
-init(Type, ParentPid, undefined, N) ->
- connected(ParentPid, Type),
- N;
-init(_, _, TPid, _) ->
- monitor(TPid),
- 3.
-
-monitor(Pid) ->
- erlang:monitor(process, Pid),
- Pid.
+%% reg/4
+%%
+%% Lookup the pid of the transport process and publish a term for
+%% send/2 to lookup.
+reg(Type, Ref, SvcName, T) ->
+ TPid = tpid(Type, Ref, diameter:service_info(SvcName, transport)),
+ true = diameter_reg:add_new({?MODULE, TPid, T}).
+
+%% tpid/3
+
+tpid(connect, Ref, [[{ref, Ref},
+ {type, connect},
+ {options, _},
+ {watchdog, _},
+ {peer, _},
+ {apps, _},
+ {caps, _},
+ {port, [{owner, TPid} | _]}
+ | _]]) ->
+ TPid;
+
+tpid(listen, Ref, [[{ref, Ref},
+ {type, listen},
+ {options, _},
+ {accept, As}
+ | _]]) ->
+ [[{watchdog, _},
+ {peer, _},
+ {apps, _},
+ {caps, _},
+ {port, [{owner, TPid} | _]}
+ | _]]
+ = lists:filter(fun([{watchdog, {_,_,S}} | _]) ->
+ S == okay orelse S == reopen
+ end,
+ As),
+ TPid.
-%% Generate a unique hostname for the faked peer.
-hostname() ->
- lists:flatten(io_lib:format("~p-~p-~p", tuple_to_list(now()))).
+%% ===========================================================================
-%% loop/1
-
-loop(S) ->
- loop(msg(receive T -> T end, S)).
-
-msg(T,S) ->
- case transition(T,S) of
- ok ->
- S;
- #transport{} = NS ->
- NS;
- {stop, Reason} ->
- x(Reason)
- end.
-
-x(Reason) ->
- exit(Reason).
-
-%% transition/2
-
-%% Manager is being asked for a new transport process.
-transition({start, Pid, Type, Ref}, #transport{type = manager,
- parent = SvcPid,
- config = Config}) ->
- TPid = start({Type, Pid, SvcPid, getr(transport), getr(peer), Config}),
- Pid ! {Ref, TPid},
- putr(transport, TPid),
+listen(PortNr, Opts) ->
+ gen_tcp:listen(PortNr, Opts).
+
+accept(LSock) ->
+ gen_tcp:accept(LSock).
+
+connect(Addr, Port, Opts) ->
+ gen_tcp:connect(Addr, Port, Opts).
+
+setopts(Sock, Opts) ->
+ inet:setopts(Sock, Opts).
+
+send(Sock, Bin) ->
+ send(getr(config), Sock, Bin).
+
+%% send/3
+
+%% First outgoing message from a new transport process is CER/CEA.
+%% Remaining outgoing messages are either DWR or DWA.
+send(undefined, Sock, Bin) ->
+ putr(config, init),
+ gen_tcp:send(Sock, Bin);
+
+%% Outgoing DWR: fake reception of DWA. Use the fact that AVP values
+%% are ignored. This is to ensure that the peer's watchdog state
+%% transitions are only induced by responses to messages it sends.
+send(_, Sock, <<_:32, 1:1, _:7, 280:24, _:32, EId:32, HId:32, _/binary>>) ->
+ Pkt = #diameter_packet{header = #diameter_header{version = 1,
+ end_to_end_id = EId,
+ hop_by_hop_id = HId},
+ msg = ['DWA', {'Result-Code', 2001},
+ {'Origin-Host', "XXX"},
+ {'Origin-Realm', ?REALM}]},
+ #diameter_packet{bin = Bin} = diameter_codec:encode(?BASE, Pkt),
+ self() ! {tcp, Sock, Bin},
ok;
-%% Peer fsm or testcase process has died.
-transition({'DOWN', _, process, Pid, _} = T, #transport{parent = Pid}) ->
- {stop, T};
-
-%% Twin transport process has gone down. In the connect case, the
-%% transport isn't started until this happens in the first place so
-%% connect immediately. In the accept case, fake the peer reconnecting
-%% only after another watchdog expiry.
-transition({'DOWN', _, process, _, _}, #transport{type = Type,
- config = {_, Wd, _, _}}) ->
- Tmo = case Type of
- connect ->
- 0;
- accept ->
- ?ONE_WD(Wd)
- end,
- erlang:send_after(Tmo, self(), reconnect),
+%% First outgoing DWA.
+send(init, Sock, Bin) ->
+ [{{?MODULE, _, T}, _}] = diameter_reg:wait({?MODULE, self(), '_'}),
+ putr(config, T),
+ send(Sock, Bin);
+
+%% First transport process.
+send({SvcName, {_,_,_} = T}, Sock, Bin) ->
+ [{'Origin-Host', _} = OH, {'Origin-Realm', _} = OR | _]
+ = ?SERVICE(SvcName),
+ putr(origin, [OH, OR]),
+ putr(config, T),
+ send(Sock, Bin);
+
+%% Discard DWA, failback after another timeout in the peer.
+send({Wd, 0 = No, Msg}, Sock, Bin) ->
+ Origin = getr(origin),
+ spawn(fun() -> failback(?ONE_WD(Wd), Msg, Sock, Bin, Origin) end),
+ putr(config, No),
ok;
-transition(reconnect, #transport{type = Type,
- parent = Pid,
- config = {_,_,_,Ref}}) ->
- getr(service) ! {reopen, Ref},
- connected(Pid, Type),
- ok;
+%% Send DWA while we're in the mood (aka 0 < N).
+send({Wd, N, Msg}, Sock, Bin) ->
+ putr(config, {Wd, N-1, Msg}),
+ gen_tcp:send(Sock, Bin);
-%% Peer fsm process is sending CER: fake the peer's CEA.
-transition(?TMSG({send, Bin}), #transport{type = connect,
- open = false,
- parent = Pid}
- = S) ->
- {Code, Flags, _} = ?BASE:msg_header('CER'),
- <<_:32, Flags:8, Code:24, _:96, _/binary>> = Bin,
- Hdr = make_header(Bin),
- recv(Pid, {Hdr, make_cea()}),
- S#transport{open = true};
-
-%% Peer fsm process is sending CEA.
-transition(?TMSG({send, Bin}), #transport{type = accept,
- open = false}
- = S) ->
- {Code, Flags, _} = ?BASE:msg_header('CEA'),
- <<_:32, Flags:8, Code:24, _:96, _/binary>> = Bin,
- S#transport{open = true};
-
-%% Watchdog is sending DWR or DWA.
-transition(?TMSG({send, Bin}), #transport{open = true} = S) ->
- {Code, _, _} = ?BASE:msg_header('DWR'),
- {Code, _, _} = ?BASE:msg_header('DWA'),
- <<_:32, R:1, 0:7, Code:24, _:96, _/binary>> = Bin,
- Hdr = make_header(Bin),
- dwa(1 == R, S, Hdr),
+%% Discard DWA.
+send(0, _Sock, _Bin) ->
ok;
-%% We're telling ourselves to fake a received message.
-transition({recv, Msg}, #transport{parent = Pid}) ->
- recv(Pid, Msg),
- ok;
+%% Send DWA.
+send(N, Sock, <<_:32, 0:1, _:7, 280:24, _/binary>> = Bin) ->
+ putr(config, N-1),
+ gen_tcp:send(Sock, Bin).
-%% We're telling ourselves to receive a message to induce failback.
-transition(failback = T, #transport{parent = Pid}) ->
- recv(Pid, eraser(T)),
- ok.
+failback(Tmo, Msg, Sock, Bin, Origin) ->
+ timer:sleep(Tmo),
+ ok = gen_tcp:send(Sock, msg(Msg, Bin, Origin)).
-make_header(Bin) ->
- #diameter_header{end_to_end_id = E,
- hop_by_hop_id = H}
- = diameter_codec:decode_header(Bin),
- #diameter_header{end_to_end_id = E,
- hop_by_hop_id = H}.
-
-recv(Pid, Msg) ->
- Pid ! ?TMSG({recv, encode(Msg)}).
-
-%% Replace the end-to-end/hop-by-hop identifiers with those from an
-%% incoming request to which we're constructing a reply.
-encode({Hdr, [_|_] = Msg}) ->
- #diameter_header{hop_by_hop_id = HBH,
- end_to_end_id = E2E}
- = Hdr,
- #diameter_packet{bin = Bin} = diameter_codec:encode(?BASE, Msg),
- <<H:12/binary, _:64, T/binary>> = Bin,
- <<H/binary, HBH:32, E2E:32, T/binary>>;
-
-encode([_|_] = Msg) ->
- #diameter_packet{bin = Bin} = diameter_codec:encode(?BASE, Msg),
+%% msg/2
+
+msg('DWA', Bin, _Origin) ->
+ Bin;
+msg(Msg, _Bin, Origin) ->
+ #diameter_packet{bin = Bin}
+ = diameter_codec:encode(?BASE, msg(Msg, Origin)),
Bin.
-connected(Pid, connect) ->
- Pid ! ?TMSG({self(), connected, make_ref()});
-connected(Pid, accept) ->
- Pid ! ?TMSG({self(), connected}),
- recv(Pid, make_cer()).
+msg('DWR' = M, T) ->
+ [M | T];
-make_cer() ->
- ['CER' | getr(peer)] ++ [{'Host-IP-Address', [?REMOTEHOST]},
- {'Vendor-Id', 1028},
- {'Product-Name', "Utan"},
- {'Auth-Application-Id', [?APPL_ID]}].
+msg('RAA', T) ->
+ ['RAA', {'Session-Id', diameter:session_id("abc")},
+ {'Result-Code', 2001}
+ | T].
+%% An unexpected answer is discarded after passing through the
+%% watchdog state machine.
-make_cea() ->
- ['CER' | Rest] = make_cer(),
- ['CEA', {'Result-Code', ?SUCCESS} | Rest].
+%% ===========================================================================
-make_dwr() ->
- ['DWR' | getr(peer)].
+peer_up(_SvcName, _Peer, S) ->
+ S.
-make_dwa() ->
- ['DWR' | Rest] = make_dwr(),
- ['DWA', {'Result-Code', ?SUCCESS} | Rest].
+peer_down(_SvcName, _Peer, S) ->
+ S.
-dwa(false, _, _) -> %% outgoing was DWA ...
- ok;
-dwa(true, S, Hdr) -> %% ... or DWR
- dwa(getr(count), Hdr, S);
-
-%% React to the DWR only after another watchdog expiry. We shouldn't
-%% get another DWR while the answer is pending.
-dwa(0, Hdr, #transport{config = {_, Wd, What, _}}) ->
- erlang:send_after(?ONE_WD(Wd), self(), failback),
- putr(failback, make_msg(What, Hdr)),
- eraser(count);
-
-dwa(undefined, _, _) ->
- undefined = getr(failback), %% ensure this is after failback
- ok;
+%% ===========================================================================
-%% Reply with DWA.
-dwa(N, Hdr, #transport{parent = Pid}) ->
- putr(count, N-1),
- recv(Pid, {Hdr, make_dwa()}).
+choose(true, X, _) -> X;
+choose(false, _, X) -> X.
-%% Answer to received DWR.
-make_msg('DWA', Hdr) ->
- {Hdr, make_dwa()};
+%% id/1
+%%
+%% Jitter callback.
-%% DWR from peer.
-make_msg('DWR', _) ->
- make_dwr();
+id(T) ->
+ T.
-%% An unexpected answer is discarded after passing through the
-%% watchdog state machine.
-make_msg(other, _) ->
- ['RAA', {'Session-Id', diameter:session_id("abc")},
- {'Result-Code', 2001}
- | getr(peer)].
+%% run/1
+%%
+%% A more useful badmatch in case of failure.
+
+run(Fs) ->
+ ?util:run([{?MODULE, [run1, F]} || F <- Fs]).
+
+run1([F|A]) ->
+ ok = try
+ apply(?MODULE, F, A),
+ ok
+ catch
+ E:R ->
+ S = erlang:get_stacktrace(),
+ io:format("~p~n", [{A, E, R, S}]),
+ S
+ end.
+
+%% now_diff/2
+
+now_diff(T1, T2) ->
+ timer:now_diff(T2, T1).
+
+%% jitter/2
+
+jitter(?WD(T), _) ->
+ T;
+jitter(T,D) ->
+ T+D.
+
+%% Generate a unique hostname for the faked peer.
+hostname() ->
+ lists:flatten(io_lib:format("~p-~p-~p", tuple_to_list(now()))).
putr(Key, Val) ->
put({?MODULE, Key}, Val).
getr(Key) ->
get({?MODULE, Key}).
-
-eraser(Key) ->
- erase({?MODULE, Key}).
diff --git a/lib/diameter/test/modules.mk b/lib/diameter/test/modules.mk
index 7f163536fb..80b1769d04 100644
--- a/lib/diameter/test/modules.mk
+++ b/lib/diameter/test/modules.mk
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2010-2011. All Rights Reserved.
+# Copyright Ericsson AB 2010-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -39,7 +39,9 @@ MODULES = \
diameter_traffic_SUITE \
diameter_relay_SUITE \
diameter_tls_SUITE \
- diameter_failover_SUITE
+ diameter_failover_SUITE \
+ diameter_dpr_SUITE \
+ diameter_event_SUITE
HRL_FILES = \
diameter_ct.hrl
diff --git a/lib/diameter/vsn.mk b/lib/diameter/vsn.mk
index f6dc786417..7b2208137b 100644
--- a/lib/diameter/vsn.mk
+++ b/lib/diameter/vsn.mk
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2010-2011. All Rights Reserved.
+# Copyright Ericsson AB 2010-2013. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -18,7 +18,7 @@
# %CopyrightEnd%
APPLICATION = diameter
-DIAMETER_VSN = 1.1
+DIAMETER_VSN = 1.3.1
PRE_VSN =
APP_VSN = "$(APPLICATION)-$(DIAMETER_VSN)$(PRE_VSN)"
diff --git a/lib/edoc/doc/src/notes.xml b/lib/edoc/doc/src/notes.xml
index a74a19bc05..472b3bfc34 100644
--- a/lib/edoc/doc/src/notes.xml
+++ b/lib/edoc/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2007</year><year>2011</year>
+ <year>2007</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -31,6 +31,25 @@
<p>This document describes the changes made to the EDoc
application.</p>
+<section><title>Edoc 0.7.10</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ List behaviour callbacks in Edoc when using -callback
+ attribute. (Thanks to Magnus Henoch.)</p>
+ <p>
+ Added special case for file names under Windows. (Thanks
+ to Beads Land-Trujillo.)</p>
+ <p>
+ Own Id: OTP-10174</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Edoc 0.7.9.1</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/edoc/priv/Makefile b/lib/edoc/priv/Makefile
index 73c42c05eb..9873136201 100644
--- a/lib/edoc/priv/Makefile
+++ b/lib/edoc/priv/Makefile
@@ -27,8 +27,11 @@ GEN_SCRIPT_SRC = edoc_generate.src
GEN_SCRIPT = edoc_generate
PRIV_FILES = stylesheet.css erlang.png edoc.dtd
-debug opt:
- sed -e "s/%EDOC_VSN%/$(EDOC_VSN)/g" \
+debug opt: $(GEN_SCRIPT)
+
+$(GEN_SCRIPT): ../vsn.mk ../../xmerl/vsn.mk ../../syntax_tools/vsn.mk \
+ $(GEN_SCRIPT_SRC)
+ $(vsn_verbose)sed -e "s/%EDOC_VSN%/$(EDOC_VSN)/g" \
-e "s/%XMERL_VSN%/$(XMERL_VSN)/g" \
-e "s/%SYNTAX_TOOLS_VSN%/$(SYNTAX_TOOLS_VSN)/g" \
$(GEN_SCRIPT_SRC) > $(GEN_SCRIPT)
diff --git a/lib/edoc/priv/edoc.dtd b/lib/edoc/priv/edoc.dtd
index 6a332cf22f..ba4ac0db28 100644
--- a/lib/edoc/priv/edoc.dtd
+++ b/lib/edoc/priv/edoc.dtd
@@ -4,7 +4,8 @@
<!ELEMENT overview (title, description?, author*, copyright?, version?,
since?, see*, reference*, todo?, packages, modules)>
<!ATTLIST overview
- root CDATA #IMPLIED>
+ root CDATA #IMPLIED
+ encoding CDATA #IMPLIED>
<!ELEMENT title (#PCDATA)>
@@ -25,6 +26,7 @@
name CDATA #REQUIRED
private (yes | no) #IMPLIED
hidden (yes | no) #IMPLIED
+ encoding CDATA #IMPLIED
root CDATA #IMPLIED>
<!ELEMENT description (briefDescription, fullDescription?)>
diff --git a/lib/edoc/src/Makefile b/lib/edoc/src/Makefile
index 72354ac711..4e5a4182da 100644
--- a/lib/edoc/src/Makefile
+++ b/lib/edoc/src/Makefile
@@ -67,17 +67,17 @@ distclean: clean
realclean: clean
$(EBIN)/%.$(EMULATOR):%.erl
- erlc -W $(ERL_COMPILE_FLAGS) -o$(EBIN) $<
+ $(erlc_verbose)erlc -W $(ERL_COMPILE_FLAGS) -o$(EBIN) $<
# ----------------------------------------------------
# Special Build Targets
# ----------------------------------------------------
$(APP_TARGET): $(APP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
# ----------------------------------------------------
# Release Target
diff --git a/lib/edoc/src/edoc.erl b/lib/edoc/src/edoc.erl
index 544465b14a..599036f380 100644
--- a/lib/edoc/src/edoc.erl
+++ b/lib/edoc/src/edoc.erl
@@ -120,7 +120,8 @@ file(Name, Options) ->
Suffix = proplists:get_value(file_suffix, Options,
?DEFAULT_FILE_SUFFIX),
Dir = proplists:get_value(dir, Options, filename:dirname(Name)),
- edoc_lib:write_file(Text, Dir, BaseName ++ Suffix).
+ Encoding = [{encoding, edoc_lib:read_encoding(Name, [])}],
+ edoc_lib:write_file(Text, Dir, BaseName ++ Suffix, '', Encoding).
%% TODO: better documentation of files/1/2, packages/1/2, application/1/2/3
@@ -455,14 +456,14 @@ expand_sources(Ss, Opts) ->
end,
expand_sources(Ss1, Suffix, sets:new(), [], []).
-expand_sources([{P, F, D} | Fs], Suffix, S, As, Ms) ->
- M = list_to_atom(packages:concat(P, filename:rootname(F, Suffix))),
+expand_sources([{'', F, D} | Fs], Suffix, S, As, Ms) ->
+ M = list_to_atom(filename:rootname(F, Suffix)),
case sets:is_element(M, S) of
true ->
expand_sources(Fs, Suffix, S, As, Ms);
false ->
S1 = sets:add_element(M, S),
- expand_sources(Fs, Suffix, S1, [{M, P, F, D} | As],
+ expand_sources(Fs, Suffix, S1, [{M, '', F, D} | As],
[M | Ms])
end;
expand_sources([], _Suffix, _S, As, Ms) ->
diff --git a/lib/edoc/src/edoc.hrl b/lib/edoc/src/edoc.hrl
index 98debba4ab..44c5d6fef4 100644
--- a/lib/edoc/src/edoc.hrl
+++ b/lib/edoc/src/edoc.hrl
@@ -48,7 +48,8 @@
%% functions = ordset(function_name()),
%% exports = ordset(function_name()),
%% attributes = ordset({atom(), term()}),
-%% records = [{atom(), [{atom(), term()}]}]}
+%% records = [{atom(), [{atom(), term()}]}],
+%% encoding = epp:source_encoding()}
%% ordset(T) = sets:ordset(T)
%% function_name(T) = {atom(), integer()}
@@ -57,7 +58,8 @@
functions = [],
exports = [],
attributes = [],
- records = []
+ records = [],
+ encoding = latin1
}).
%% Environment for generating documentation data
diff --git a/lib/edoc/src/edoc_data.erl b/lib/edoc/src/edoc_data.erl
index aad0b14371..f88ba05f4b 100644
--- a/lib/edoc/src/edoc_data.erl
+++ b/lib/edoc/src/edoc_data.erl
@@ -83,7 +83,8 @@ module(Module, Entries, Env, Opts) ->
AllTags = get_all_tags(Entries),
Functions = function_filter(Entries, Opts),
Out = {module, ([{name, Name},
- {root, Env#env.root}]
+ {root, Env#env.root},
+ {encoding, Module#module.encoding}]
++ case is_private(HeaderTags) of
true -> [{private, "yes"}];
false -> []
@@ -167,7 +168,10 @@ callbacks(Es, Module, Env, Opts) ->
case lists:any(fun (#entry{name = {behaviour_info, 1}}) -> true;
(_) -> false
end,
- Es) of
+ Es)
+ orelse
+ lists:keymember(callback, 1, Module#module.attributes)
+ of
true ->
try (Module#module.name):behaviour_info(callbacks) of
Fs ->
diff --git a/lib/edoc/src/edoc_doclet.erl b/lib/edoc/src/edoc_doclet.erl
index 385d20e9ae..a0c1ae1c0f 100644
--- a/lib/edoc/src/edoc_doclet.erl
+++ b/lib/edoc/src/edoc_doclet.erl
@@ -192,8 +192,9 @@ source({M, P, Name, Path}, Dir, Suffix, Env, Set, Private, Hidden,
andalso ((not is_hidden(Doc)) orelse Hidden) of
true ->
Text = edoc:layout(Doc, Options),
- Name1 = packages:last(M) ++ Suffix,
- edoc_lib:write_file(Text, Dir, Name1, P),
+ Name1 = atom_to_list(M) ++ Suffix,
+ Encoding = [{encoding,encoding(Doc)}],
+ edoc_lib:write_file(Text, Dir, Name1, P, Encoding),
{sets:add_element(Module, Set), Error};
false ->
{Set, Error}
@@ -204,9 +205,9 @@ source({M, P, Name, Path}, Dir, Suffix, Env, Set, Private, Hidden,
end.
check_name(M, M0, P0, File) ->
- P = list_to_atom(packages:strip_last(M)),
- N = packages:last(M),
- N0 = packages:last(M0),
+ P = '',
+ N = M,
+ N0 = M0,
case N of
[$? | _] ->
%% A module name of the form '?...' is assumed to be caused
@@ -359,14 +360,19 @@ xhtml_1(Title, CSS, Body) ->
overview(Dir, Title, Env, Opts) ->
File = proplists:get_value(overview, Opts,
filename:join(Dir, ?OVERVIEW_FILE)),
+ Encoding = edoc_lib:read_encoding(File, [{in_comment_only, false}]),
Tags = read_file(File, overview, Env, Opts),
- Data = edoc_data:overview(Title, Tags, Env, Opts),
+ Data0 = edoc_data:overview(Title, Tags, Env, Opts),
+ EncodingAttribute = #xmlAttribute{name = encoding,
+ value = atom_to_list(Encoding)},
+ #xmlElement{attributes = As} = Data0,
+ Data = Data0#xmlElement{attributes = [EncodingAttribute | As]},
F = fun (M) ->
M:overview(Data, Opts)
end,
Text = edoc_lib:run_layout(F, Opts),
- edoc_lib:write_file(Text, Dir, ?OVERVIEW_SUMMARY).
-
+ EncOpts = [{encoding,Encoding}],
+ edoc_lib:write_file(Text, Dir, ?OVERVIEW_SUMMARY, '', EncOpts).
copy_image(Dir) ->
case code:priv_dir(?EDOC_APP) of
@@ -441,6 +447,12 @@ is_hidden(E) ->
_ -> false
end.
+encoding(E) ->
+ case get_attrval(encoding, E) of
+ "latin1" -> latin1;
+ _ -> utf8
+ end.
+
get_attrval(Name, #xmlElement{attributes = As}) ->
case get_attr(Name, As) of
[#xmlAttribute{value = V}] ->
diff --git a/lib/edoc/src/edoc_extract.erl b/lib/edoc/src/edoc_extract.erl
index 5a79e127f6..67a95e80aa 100644
--- a/lib/edoc/src/edoc_extract.erl
+++ b/lib/edoc/src/edoc_extract.erl
@@ -121,7 +121,7 @@ source1(Tree, File0, Env, Opts, TypeDocs) ->
Module = get_module_info(Tree, File),
{Header, Footer, Entries} = collect(Forms, Module),
Name = Module#module.name,
- Package = list_to_atom(packages:strip_last(Name)),
+ Package = '',
Env1 = Env#env{module = Name,
package = Package,
root = edoc_refs:relative_package_path('', Package)},
@@ -226,7 +226,7 @@ add_macro_defs(Defs0, Opts, Env) ->
%% lines of text before the first tag are ignored. `Env' is an
%% environment created by {@link edoc_lib:get_doc_env/4}. Upon error,
%% `Reason' is an atom returned from the call to {@link
-%% //kernel/file:read_file/1}.
+%% //kernel/file:read_file/1} or the atom 'invalid_unicode'.
%%
%% See {@link text/4} for options.
@@ -235,7 +235,13 @@ add_macro_defs(Defs0, Opts, Env) ->
file(File, Context, Env, Opts) ->
case file:read_file(File) of
{ok, Bin} ->
- {ok, text(binary_to_list(Bin), Context, Env, Opts, File)};
+ Enc = edoc_lib:read_encoding(File,[{in_comment_only, false}]),
+ case catch unicode:characters_to_list(Bin, Enc) of
+ String when is_list(String) ->
+ {ok, text(String, Context, Env, Opts, File)};
+ _ ->
+ {error, invalid_unicode}
+ end;
{error, _} = Error ->
Error
end.
@@ -306,12 +312,14 @@ get_module_info(Forms, File) ->
Exports = ordsets:from_list(get_list_keyval(exports, L)),
Attributes = ordsets:from_list(get_list_keyval(attributes, L)),
Records = get_list_keyval(records, L),
+ Encoding = edoc_lib:read_encoding(File, []),
#module{name = Name,
parameters = Vars,
functions = Functions,
exports = ordsets:intersection(Exports, Functions),
attributes = Attributes,
- records = Records}.
+ records = Records,
+ encoding = Encoding}.
get_list_keyval(Key, L) ->
case lists:keyfind(Key, 1, L) of
diff --git a/lib/edoc/src/edoc_layout.erl b/lib/edoc/src/edoc_layout.erl
index 951cec121c..7bd0615f5c 100644
--- a/lib/edoc/src/edoc_layout.erl
+++ b/lib/edoc/src/edoc_layout.erl
@@ -210,7 +210,8 @@ layout_module(#xmlElement{name = module, content = Es}=E, Opts) ->
++ [hr, ?NL]
++ navigation("bottom")
++ timestamp()),
- xhtml(Title, stylesheet(Opts), Body).
+ Encoding = get_attrval(encoding, E),
+ xhtml(Title, stylesheet(Opts), Body, Encoding).
module_params(Es) ->
As = [{get_text(argName, Es1),
@@ -956,10 +957,17 @@ local_label(R) ->
"#" ++ R.
xhtml(Title, CSS, Body) ->
+ xhtml(Title, CSS, Body, "latin1").
+
+xhtml(Title, CSS, Body, Encoding) ->
+ EncString = case Encoding of
+ "latin1" -> "ISO-8859-1";
+ _ -> "UTF-8"
+ end,
[{html, [?NL,
{head, [?NL,
{meta, [{'http-equiv',"Content-Type"},
- {content, "text/html; charset=ISO-8859-1"}],
+ {content, "text/html; charset="++EncString}],
[]},
?NL,
{title, Title},
@@ -1021,7 +1029,8 @@ overview(E=#xmlElement{name = overview, content = Es}, Options) ->
++ [?NL, hr]
++ navigation("bottom")
++ timestamp()),
- XML = xhtml(Title, stylesheet(Opts), Body),
+ Encoding = get_attrval(encoding, E),
+ XML = xhtml(Title, stylesheet(Opts), Body, Encoding),
xmerl:export_simple(XML, ?HTML_EXPORT, []).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% NYTT
diff --git a/lib/edoc/src/edoc_lib.erl b/lib/edoc/src/edoc_lib.erl
index 7fd8358add..3d193c4bfa 100644
--- a/lib/edoc/src/edoc_lib.erl
+++ b/lib/edoc/src/edoc_lib.erl
@@ -1,3 +1,4 @@
+%% -*- coding: utf-8 -*-
%% =====================================================================
%% This library is free software; you can redistribute it and/or modify
%% it under the terms of the GNU Lesser General Public License as
@@ -30,10 +31,10 @@
parse_contact/2, escape_uri/1, join_uri/2, is_relative_uri/1,
is_name/1, to_label/1, find_doc_dirs/0, find_sources/2,
find_sources/3, find_file/3, try_subdir/2, unique/1,
- write_file/3, write_file/4, write_info_file/4,
+ write_file/3, write_file/4, write_file/5, write_info_file/4,
read_info_file/1, get_doc_env/1, get_doc_env/4, copy_file/2,
uri_get/1, run_doclet/2, run_layout/2,
- simplify_path/1, timestr/1, datestr/1]).
+ simplify_path/1, timestr/1, datestr/1, read_encoding/2]).
-import(edoc_report, [report/2, warning/2]).
@@ -57,6 +58,13 @@ datestr({Y,M,D}) ->
lists:flatten(io_lib:fwrite("~s ~w ~w",[lists:nth(M, Ms),D,Y])).
%% @private
+read_encoding(File, Options) ->
+ case epp:read_encoding(File, Options) of
+ none -> epp:default_encoding();
+ Encoding -> Encoding
+ end.
+
+%% @private
count(X, Xs) ->
count(X, Xs, 0).
@@ -228,13 +236,13 @@ end_of_sentence_1(_, false, _) ->
%% 173 - 176 { - ~ punctuation
%% 177 DEL control
%% 200 - 237 control
-%% 240 - 277 NBSP - � punctuation
-%% 300 - 326 � - � uppercase
-%% 327 � punctuation
-%% 330 - 336 � - � uppercase
-%% 337 - 366 � - � lowercase
-%% 367 � punctuation
-%% 370 - 377 � - � lowercase
+%% 240 - 277 NBSP - ¿ punctuation
+%% 300 - 326 À - Ö uppercase
+%% 327 × punctuation
+%% 330 - 336 Ø - Þ uppercase
+%% 337 - 366 ß - ö lowercase
+%% 367 ÷ punctuation
+%% 370 - 377 ø - ÿ lowercase
%% Names must begin with a lowercase letter and contain only
%% alphanumerics and underscores.
@@ -261,6 +269,10 @@ is_name_1(_) -> false.
to_atom(A) when is_atom(A) -> A;
to_atom(S) when is_list(S) -> list_to_atom(S).
+
+to_list(A) when is_atom(A) -> atom_to_list(A);
+to_list(S) when is_list(S) -> S.
+
%% @private
unique([X | Xs]) -> [X | unique(Xs, X)];
@@ -469,6 +481,10 @@ uri_get("ftp:" ++ Path) ->
uri_get("//" ++ Path) ->
Msg = io_lib:format("cannot access network-path: '//~s'.", [Path]),
{error, Msg};
+uri_get([C, $:, $/ | _]=Path) when C >= $A, C =< $Z; C >= $a, C =< $z ->
+ uri_get_file(Path); % special case for Windows
+uri_get([C, $:, $\ | _]=Path) when C >= $A, C =< $Z; C >= $a, C =< $z ->
+ uri_get_file(Path); % special case for Windows
uri_get(URI) ->
case is_relative_uri(URI) of
true ->
@@ -673,7 +689,6 @@ try_subdir(Dir, Subdir) ->
write_file(Text, Dir, Name) ->
write_file(Text, Dir, Name, '').
-
%% @spec (Text::deep_string(), Dir::edoc:filename(),
%% Name::edoc:filename(), Package::atom()|string()) -> ok
%% @doc Like {@link write_file/3}, but adds path components to the target
@@ -681,10 +696,12 @@ write_file(Text, Dir, Name) ->
%% @private
write_file(Text, Dir, Name, Package) ->
- Dir1 = filename:join([Dir | packages:split(Package)]),
- File = filename:join(Dir1, Name),
+ write_file(Text, Dir, Name, Package, [{encoding,latin1}]).
+
+write_file(Text, Dir, Name, Package, Options) ->
+ File = filename:join([Dir, to_list(Package), Name]),
ok = filelib:ensure_dir(File),
- case file:open(File, [write]) of
+ case file:open(File, [write] ++ Options) of
{ok, FD} ->
io:put_chars(FD, Text),
ok = file:close(FD);
@@ -701,8 +718,9 @@ write_info_file(App, Packages, Modules, Dir) ->
Ts1 = if App =:= ?NO_APP -> Ts;
true -> [{application, App} | Ts]
end,
- S = [io_lib:fwrite("~p.\n", [T]) || T <- Ts1],
- write_file(S, Dir, ?INFO_FILE).
+ S0 = [io_lib:fwrite("~p.\n", [T]) || T <- Ts1],
+ S = ["%% encoding: UTF-8\n" | S0],
+ write_file(S, Dir, ?INFO_FILE, '', [{encoding,unicode}]).
%% @spec (Name::edoc:filename()) -> {ok, string()} | {error, Reason}
%%
@@ -710,7 +728,14 @@ write_info_file(App, Packages, Modules, Dir) ->
read_file(File) ->
case file:read_file(File) of
- {ok, Bin} -> {ok, binary_to_list(Bin)};
+ {ok, Bin} ->
+ Enc = edoc_lib:read_encoding(File, []),
+ case catch unicode:characters_to_list(Bin, Enc) of
+ String when is_list(String) ->
+ {ok, String};
+ _ ->
+ {error, invalid_unicode}
+ end;
{error, Reason} -> {error, Reason}
end.
@@ -814,7 +839,7 @@ find_sources(Path, Pkg, Rec, Ext, Opts) ->
lists:flatten(find_sources_1(Path, to_atom(Pkg), Rec, Ext, Skip)).
find_sources_1([P | Ps], Pkg, Rec, Ext, Skip) ->
- Dir = filename:join(P, filename:join(packages:split(Pkg))),
+ Dir = filename:join(P, atom_to_list(Pkg)),
Fs1 = find_sources_1(Ps, Pkg, Rec, Ext, Skip),
case filelib:is_dir(Dir) of
true ->
@@ -840,9 +865,9 @@ find_sources_2(Dir, Pkg, Rec, Ext, Skip) ->
[]
end.
-find_sources_3(Es, Dir, Pkg, Rec, Ext, Skip) ->
+find_sources_3(Es, Dir, '', Rec, Ext, Skip) ->
[find_sources_2(filename:join(Dir, E),
- to_atom(packages:concat(Pkg, E)), Rec, Ext, Skip)
+ to_atom(E), Rec, Ext, Skip)
|| E <- Es, is_package_dir(E, Dir)].
is_source_file(Name, Ext) ->
@@ -854,16 +879,16 @@ is_package_dir(Name, Dir) ->
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),
+find_file([P | Ps], []=Pkg, Name) ->
+ Pkg = [],
+ File = filename:join(P, Name),
case filelib:is_file(File) of
true ->
File;
false ->
find_file(Ps, Pkg, Name)
end;
-find_file([], _Pkg, _Name) ->
+find_file([], [], _Name) ->
"".
%% @private
diff --git a/lib/edoc/src/edoc_macros.erl b/lib/edoc/src/edoc_macros.erl
index 70fb38bf0a..08686c4fb5 100644
--- a/lib/edoc/src/edoc_macros.erl
+++ b/lib/edoc/src/edoc_macros.erl
@@ -88,13 +88,13 @@ link_macro(S, Line, Env) ->
true -> " target=\"_top\""; % note the initial space
false -> ""
end,
- lists:flatten(io_lib:fwrite("<a href=\"~s\"~s>~s</a>",
+ lists:flatten(io_lib:fwrite("<a href=\"~s\"~s>~ts</a>",
[URI, Target, Txt])).
section_macro(S, _Line, _Env) ->
S1 = lists:reverse(edoc_lib:strip_space(
lists:reverse(edoc_lib:strip_space(S)))),
- lists:flatten(io_lib:format("<a href=\"#~s\">~s</a>",
+ lists:flatten(io_lib:format("<a href=\"#~ts\">~ts</a>",
[edoc_lib:to_label(S1), S1])).
type_macro(S, Line, Env) ->
diff --git a/lib/edoc/src/edoc_parser.yrl b/lib/edoc/src/edoc_parser.yrl
index 4d6428f75b..cf1a2d6b11 100644
--- a/lib/edoc/src/edoc_parser.yrl
+++ b/lib/edoc/src/edoc_parser.yrl
@@ -1,6 +1,7 @@
+%% -*- coding: utf-8 -*-
%% ========================== -*-Erlang-*- =============================
%% EDoc type specification grammar for the Yecc parser generator,
-%% adapted from Sven-Olof Nystr�m's type specification parser.
+%% adapted from Sven-Olof Nyström's type specification parser.
%%
%% Also contains entry points for parsing things like typedefs,
%% references, and throws-declarations.
@@ -100,9 +101,7 @@ ptype -> '[' utype ',' '...' ']' : #t_nonempty_list{type = '$2'}.
ptype -> utype_list:
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'))};
+ #t_paren{type = hd(element(1, '$1'))};
length(element(1, '$1')) == 0 ->
return_error(element(2, '$1'), "syntax error before: ')'");
true ->
@@ -319,10 +318,7 @@ tok_val(T) -> element(3, T).
tok_line(T) -> element(2, T).
-qname([A]) ->
- A; % avoid unnecessary call to packages:concat/1.
-qname(List) ->
- list_to_atom(packages:concat(lists:reverse(List))).
+qname([A]) -> A.
union(Ts) ->
case Ts of
diff --git a/lib/edoc/src/edoc_refs.erl b/lib/edoc/src/edoc_refs.erl
index 1f578a3b83..ea439490ed 100644
--- a/lib/edoc/src/edoc_refs.erl
+++ b/lib/edoc/src/edoc_refs.erl
@@ -126,7 +126,7 @@ abs_uri({package, P}, Env) ->
module_ref(M, Env) ->
case (Env#env.modules)(M) of
"" ->
- File = packages:last(M) ++ Env#env.file_suffix,
+ File = atom_to_list(M) ++ Env#env.file_suffix,
Path = relative_module_path(M, Env#env.package),
join_uri(Path, escape_uri(File));
Base ->
@@ -134,8 +134,7 @@ module_ref(M, Env) ->
end.
module_absref(M, Env) ->
- join_segments(packages:split(M))
- ++ escape_uri(Env#env.file_suffix).
+ escape_uri(atom_to_list(M)) ++ escape_uri(Env#env.file_suffix).
package_ref(P, Env) ->
case (Env#env.packages)(P) of
@@ -147,7 +146,7 @@ package_ref(P, Env) ->
end.
package_absref(P, Env) ->
- join_uri(join_segments(packages:split(P)),
+ join_uri(escape_uri(atom_to_list(P)),
escape_uri(Env#env.package_summary)).
app_ref(A, Env) ->
@@ -179,14 +178,11 @@ join_segments([S | Ss]) ->
%% The empty string is returned if the To module has only one segment,
%% implying a local reference.
-relative_module_path(To, From) ->
- case first(packages:split(To)) of
- [] -> "";
- P -> relative_path(P, packages:split(From))
- end.
+relative_module_path(_To, _From) ->
+ "".
relative_package_path(To, From) ->
- relative_path(packages:split(To), packages:split(From)).
+ relative_path([atom_to_list(To)], [atom_to_list(From)]).
%% This takes two lists of path segments (From, To). Note that an empty
%% string will be returned if the paths are the same. Empty leading
@@ -210,6 +206,3 @@ relative_path_2([], []) ->
"";
relative_path_2([], Ts) ->
join_segments(Ts).
-
-first([H | T]) when T /= [] -> [H | first(T)];
-first(_) -> [].
diff --git a/lib/edoc/src/edoc_wiki.erl b/lib/edoc/src/edoc_wiki.erl
index 5c71658af5..cc0529d2a9 100644
--- a/lib/edoc/src/edoc_wiki.erl
+++ b/lib/edoc/src/edoc_wiki.erl
@@ -80,6 +80,7 @@ parse_xml(Data, Line) ->
parse_xml_1(Text, Line) ->
Text1 = "<doc>" ++ Text ++ "</doc>",
+ %% Any coding except "utf-8".
Opts = [{line, Line}, {encoding, 'iso-8859-1'}],
case catch {ok, xmerl_scan:string(Text1, Opts)} of
{ok, {E, _}} ->
@@ -174,7 +175,7 @@ expand_heading_1(Cs, N, L, As) ->
expand_heading_2(Ts, Cs, N, L, As) ->
H = ?BASE_HEADING + N,
- Ts1 = io_lib:format("<h~w><a name=\"~s\">~s</a></h~w>\n",
+ Ts1 = io_lib:format("<h~w><a name=\"~ts\">~ts</a></h~w>\n",
[H, make_label(Ts), Ts, H]),
expand_new_line(Cs, L + 1, lists:reverse(lists:flatten(Ts1), As)).
diff --git a/lib/edoc/vsn.mk b/lib/edoc/vsn.mk
index b8f33894f1..2f403212c8 100644
--- a/lib/edoc/vsn.mk
+++ b/lib/edoc/vsn.mk
@@ -1 +1 @@
-EDOC_VSN = 0.7.9.1
+EDOC_VSN = 0.7.10
diff --git a/lib/eldap/doc/src/eldap.xml b/lib/eldap/doc/src/eldap.xml
index 04dad2eee7..bd6f00af1f 100644
--- a/lib/eldap/doc/src/eldap.xml
+++ b/lib/eldap/doc/src/eldap.xml
@@ -68,7 +68,7 @@ filter() See present/1, substrings/2,
<fsummary>Open a connection to an LDAP server.</fsummary>
<type>
<v>Handle = handle()</v>
- <v>Option = {port, integer()} | {log, function()} | {timeout, integer()} | {ssl, boolean()}</v>
+ <v>Option = {port, integer()} | {log, function()} | {timeout, integer()} | {ssl, boolean()} | {sslopts, list()}</v>
</type>
<desc>
<p>Setup a connection to an LDAP server, the <c>HOST</c>'s are tried in order.</p>
diff --git a/lib/eldap/src/Makefile b/lib/eldap/src/Makefile
index 39a41d08e2..ad93e1087a 100644
--- a/lib/eldap/src/Makefile
+++ b/lib/eldap/src/Makefile
@@ -75,10 +75,10 @@ clean:
rm -f errs core *~
$(APP_TARGET): $(APP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(ELDAP_VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(ELDAP_VSN);' $< > $@
$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(ELDAP_VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(ELDAP_VSN);' $< > $@
docs:
@@ -88,7 +88,7 @@ $(TARGET_FILES): $(HRL_FILES)
# Special Build Targets
# ----------------------------------------------------
$(ASN1_HRL): ../asn1/$(ASN1_FILES)
- $(ERLC) -o $(EBIN) -bber_bin +optimize +nif $(ERL_COMPILE_FLAGS) ../asn1/ELDAPv3.asn1
+ $(asn_verbose)$(ERLC) -o $(EBIN) -bber $(ERL_COMPILE_FLAGS) ../asn1/ELDAPv3.asn1
# ----------------------------------------------------
# Release Target
diff --git a/lib/eldap/src/eldap.erl b/lib/eldap/src/eldap.erl
index 699dfc8791..d030408770 100644
--- a/lib/eldap/src/eldap.erl
+++ b/lib/eldap/src/eldap.erl
@@ -42,7 +42,8 @@
log, % User provided log function
timeout = infinity, % Request timeout
anon_auth = false, % Allow anonymous authentication
- use_tls = false % LDAP/LDAPS
+ use_tls = false, % LDAP/LDAPS
+ tls_opts = [] % ssl:ssloptsion()
}).
%%% For debug purposes
@@ -320,7 +321,7 @@ present(Attribute) when is_list(Attribute) ->
%%% will match entries containing: 'sn: Tornkvist'
%%%
substrings(Type, SubStr) when is_list(Type), is_list(SubStr) ->
- Ss = {'SubstringFilter_substrings',v_substr(SubStr)},
+ Ss = v_substr(SubStr),
{substrings,#'SubstringFilter'{type = Type,
substrings = Ss}}.
@@ -353,6 +354,10 @@ parse_args([{ssl, true}|T], Cpid, Data) ->
parse_args(T, Cpid, Data#eldap{use_tls = true});
parse_args([{ssl, _}|T], Cpid, Data) ->
parse_args(T, Cpid, Data);
+parse_args([{sslopts, Opts}|T], Cpid, Data) when is_list(Opts) ->
+ parse_args(T, Cpid, Data#eldap{use_tls = true, tls_opts = Opts ++ Data#eldap.tls_opts});
+parse_args([{sslopts, _}|T], Cpid, Data) ->
+ parse_args(T, Cpid, Data);
parse_args([{log, F}|T], Cpid, Data) when is_function(F) ->
parse_args(T, Cpid, Data#eldap{log = F});
parse_args([{log, _}|T], Cpid, Data) ->
@@ -384,8 +389,8 @@ try_connect([],_) ->
do_connect(Host, Data, Opts) when Data#eldap.use_tls == false ->
gen_tcp:connect(Host, Data#eldap.port, Opts, Data#eldap.timeout);
do_connect(Host, Data, Opts) when Data#eldap.use_tls == true ->
- ssl:connect(Host, Data#eldap.port, [{verify,0}|Opts]).
-
+ SslOpts = [{verify,0} | Opts ++ Data#eldap.tls_opts],
+ ssl:connect(Host, Data#eldap.port, SslOpts).
loop(Cpid, Data) ->
receive
@@ -700,20 +705,22 @@ recv_response(S, Data) ->
Error -> throw(Error)
end;
{error,Reason} ->
- throw({gen_tcp_error, Reason});
- Error ->
- throw(Error)
+ throw({gen_tcp_error, Reason})
end.
%%% Sanity check of received packet
check_tag(Data) ->
- case asn1rt_ber_bin:decode_tag(l2b(Data)) of
- {_Tag, Data1, _Rb} ->
- case asn1rt_ber_bin:decode_length(l2b(Data1)) of
- {{_Len, _Data2}, _Rb2} -> ok;
- _ -> throw({error,decoded_tag_length})
- end;
- _ -> throw({error,decoded_tag})
+ try
+ {_Tag, Data1, _Rb} = asn1rt_ber_bin:decode_tag(l2b(Data)),
+ try
+ {{_Len, _Data2}, _Rb2} = asn1rt_ber_bin:decode_length(l2b(Data1)),
+ ok
+ catch
+ _ -> throw({error,decoded_tag_length})
+ end
+ catch
+ _ ->
+ throw({error, decoded_tag})
end.
%%% Check for expected kind of reply
diff --git a/lib/eldap/vsn.mk b/lib/eldap/vsn.mk
index c9d6e4e324..4d05d3d1e3 100644
--- a/lib/eldap/vsn.mk
+++ b/lib/eldap/vsn.mk
@@ -1 +1 @@
-ELDAP_VSN = 1.0
+ELDAP_VSN = 1.0.1
diff --git a/lib/erl_docgen/doc/src/Makefile b/lib/erl_docgen/doc/src/Makefile
index a254c4fb6d..8d8dd0dd55 100644
--- a/lib/erl_docgen/doc/src/Makefile
+++ b/lib/erl_docgen/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2011-2011. All Rights Reserved.
+# Copyright Ericsson AB 2011-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/erl_docgen/doc/src/doc-build.xml b/lib/erl_docgen/doc/src/doc-build.xml
index 08410a1539..ae1b17dff5 100644
--- a/lib/erl_docgen/doc/src/doc-build.xml
+++ b/lib/erl_docgen/doc/src/doc-build.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>1997</year><year>2011</year>
+ <year>1997</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -56,7 +56,8 @@
</p>
<code>
- 1> escript $(ERL_TOP)/lib/erl_docgen/priv/bin/codeline_preprocessing.escript ex1.xmlsrc ex1.xml
+ 1> escript $(ERL_TOP)/lib/erl_docgen/priv/bin/codeline_preprocessing.escript \
+ ex1.xmlsrc ex1.xml
</code>
</section>
</section>
diff --git a/lib/erl_docgen/doc/src/notes.xml b/lib/erl_docgen/doc/src/notes.xml
index 8ca11b1cf5..9775909d76 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>2011</year>
+ <year>2004</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -30,7 +30,38 @@
</header>
<p>This document describes the changes made to the <em>erl_docgen</em> application.</p>
- <section><title>Erl_Docgen 0.3.1</title>
+ <section><title>Erl_Docgen 0.3.3</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p> A possibility to configure erl_docgen so it can
+ generate documentation for other products than
+ Erlang/OTP. </p>
+ <p>
+ Own Id: OTP-9040</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erl_Docgen 0.3.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> A bug regarding spaces in C function prototypes has
+ been fixed. (Thanks to Richard O'Keefe.) </p>
+ <p>
+ Own Id: OTP-10138</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erl_Docgen 0.3.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/erl_docgen/priv/bin/Makefile b/lib/erl_docgen/priv/bin/Makefile
index 449cc6691c..d6d0ad2260 100644
--- a/lib/erl_docgen/priv/bin/Makefile
+++ b/lib/erl_docgen/priv/bin/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2009-2010. All Rights Reserved.
+# Copyright Ericsson AB 2009-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/erl_docgen/priv/bin/xml_from_edoc.escript b/lib/erl_docgen/priv/bin/xml_from_edoc.escript
index 2cb81be1be..65a580dca2 100755
--- a/lib/erl_docgen/priv/bin/xml_from_edoc.escript
+++ b/lib/erl_docgen/priv/bin/xml_from_edoc.escript
@@ -2,7 +2,7 @@
%% -*- erlang -*-
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -100,10 +100,12 @@ module(File, Args) ->
users_guide(File, Args) ->
case filelib:is_regular(File) of
true ->
+ Enc = epp:read_encoding(File, [{in_comment_only, false}]),
+ Encoding = [{encoding, Enc} || Enc =/= none],
Opts = [{def, Args#args.def},
{app_default, "OTPROOT"},
{file_suffix, Args#args.suffix},
- {layout, Args#args.layout}],
+ {layout, Args#args.layout}] ++ Encoding,
Env = edoc_lib:get_doc_env(Opts),
@@ -115,7 +117,7 @@ users_guide(File, Args) ->
Text = edoc_lib:run_layout(F, Opts),
OutFile = "chapter" ++ Args#args.suffix,
- edoc_lib:write_file(Text, ".", OutFile);
+ edoc_lib:write_file(Text, ".", OutFile, '', Encoding);
false ->
io:format("~s: not a regular file\n", [File]),
usage()
diff --git a/lib/erl_docgen/priv/css/Makefile b/lib/erl_docgen/priv/css/Makefile
index 0faca019f3..24abc4fa9c 100644
--- a/lib/erl_docgen/priv/css/Makefile
+++ b/lib/erl_docgen/priv/css/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2009. All Rights Reserved.
+# Copyright Ericsson AB 2009-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/erl_docgen/priv/dtd/Makefile b/lib/erl_docgen/priv/dtd/Makefile
index 314374d34b..4538b7f8e6 100644
--- a/lib/erl_docgen/priv/dtd/Makefile
+++ b/lib/erl_docgen/priv/dtd/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2009-2011. All Rights Reserved.
+# Copyright Ericsson AB 2009-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/erl_docgen/priv/dtd/common.refs.dtd b/lib/erl_docgen/priv/dtd/common.refs.dtd
index c1237766e1..93592607df 100644
--- a/lib/erl_docgen/priv/dtd/common.refs.dtd
+++ b/lib/erl_docgen/priv/dtd/common.refs.dtd
@@ -26,10 +26,12 @@
<!ELEMENT description (%block;|quote|br|marker|warning|note)* >
<!ELEMENT funcs (func)+ >
-<!ELEMENT func (name+,type_desc+,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 type (v,d?)* >
+<!ATTLIST type variable CDATA #IMPLIED
+ name_i CDATA #IMPLIED>
<!ELEMENT v (#PCDATA) >
<!ELEMENT d (#PCDATA|c|em)* >
<!ELEMENT desc (%block;|quote|br|marker|warning|note|anno)* >
@@ -41,3 +43,4 @@
<!ELEMENT datatypes (datatype)+ >
<!ELEMENT datatype (name+,desc?) >
<!ELEMENT type_desc (#PCDATA) >
+<!ATTLIST type_desc variable CDATA #REQUIRED>
diff --git a/lib/erl_docgen/priv/dtd/erlref.dtd b/lib/erl_docgen/priv/dtd/erlref.dtd
index 9905086ff4..0cc5cfa06d 100644
--- a/lib/erl_docgen/priv/dtd/erlref.dtd
+++ b/lib/erl_docgen/priv/dtd/erlref.dtd
@@ -29,3 +29,6 @@
<!-- `name' is used in common.refs.dtd and must therefore
be defined in each *ref. dtd -->
<!ELEMENT name (#PCDATA) >
+<!ATTLIST name name CDATA #IMPLIED
+ arity CDATA #IMPLIED
+ clause_i CDATA #IMPLIED>
diff --git a/lib/erl_docgen/priv/dtd_html_entities/Makefile b/lib/erl_docgen/priv/dtd_html_entities/Makefile
index 9cd3f38c4b..f8a10973b2 100644
--- a/lib/erl_docgen/priv/dtd_html_entities/Makefile
+++ b/lib/erl_docgen/priv/dtd_html_entities/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2009. All Rights Reserved.
+# Copyright Ericsson AB 2009-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/erl_docgen/priv/dtd_man_entities/Makefile b/lib/erl_docgen/priv/dtd_man_entities/Makefile
index b6f29af6b4..da4998dcbc 100644
--- a/lib/erl_docgen/priv/dtd_man_entities/Makefile
+++ b/lib/erl_docgen/priv/dtd_man_entities/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2009. All Rights Reserved.
+# Copyright Ericsson AB 2009-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/erl_docgen/priv/fop.xconf b/lib/erl_docgen/priv/fop.xconf
new file mode 100644
index 0000000000..70ecd608c3
--- /dev/null
+++ b/lib/erl_docgen/priv/fop.xconf
@@ -0,0 +1,45 @@
+<?xml version="1.0"?>
+<!--
+ #
+ # %CopyrightBegin%
+ #
+ # Copyright Ericsson AB 2009-2012. All Rights Reserved.
+ #
+ # The contents of this file are subject to the Erlang Public License,
+ # Version 1.1, (the "License"); you may not use this file except in
+ # compliance with the License. You should have received a copy of the
+ # Erlang Public License along with this software. If not, it can be
+ # retrieved online at http://www.erlang.org/.
+ #
+ # Software distributed under the License is distributed on an "AS IS"
+ # basis, 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 is the version of the configuration -->
+<fop version="1.0">
+ <!-- The substitutions of DejaVu 700 are there because FOP outputs
+ warnings about doing the substitutions otherwise -->
+ <fonts>
+ <substitutions>
+ <substitution>
+ <from font-family="DejaVuSans" font-weight="700"/>
+ <to font-family="DejaVuSans" font-weight="400"/>
+ </substitution>
+ <substitution>
+ <from font-family="DejaVuSansMono" font-weight="700"/>
+ <to font-family="DejaVuSansMono" font-weight="400"/>
+ </substitution>
+ </substitutions>
+ </fonts>
+ <renderers>
+ <renderer mime="application/pdf">
+ <fonts>
+ <auto-detect/>
+ </fonts>
+ </renderer>
+ </renderers>
+</fop>
diff --git a/lib/erl_docgen/priv/images/Makefile b/lib/erl_docgen/priv/images/Makefile
index 389f9f98ae..61016e927c 100644
--- a/lib/erl_docgen/priv/images/Makefile
+++ b/lib/erl_docgen/priv/images/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2009. All Rights Reserved.
+# Copyright Ericsson AB 2009-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/erl_docgen/priv/js/flipmenu/Makefile b/lib/erl_docgen/priv/js/flipmenu/Makefile
index 682d186608..7933752532 100644
--- a/lib/erl_docgen/priv/js/flipmenu/Makefile
+++ b/lib/erl_docgen/priv/js/flipmenu/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2009. All Rights Reserved.
+# Copyright Ericsson AB 2009-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/erl_docgen/priv/xsl/Makefile b/lib/erl_docgen/priv/xsl/Makefile
index ca7a4e8157..58589672b8 100644
--- a/lib/erl_docgen/priv/xsl/Makefile
+++ b/lib/erl_docgen/priv/xsl/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2009. All Rights Reserved.
+# Copyright Ericsson AB 2009-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -42,7 +42,8 @@ XSL_FILES = \
db_pdf_params.xsl \
db_html.xsl \
db_html_params.xsl \
- db_man.xsl
+ db_man.xsl \
+ db_eix.xsl
# ----------------------------------------------------
diff --git a/lib/erl_docgen/priv/xsl/db_html.xsl b/lib/erl_docgen/priv/xsl/db_html.xsl
index 7cf5465f90..ab5f24c406 100644
--- a/lib/erl_docgen/priv/xsl/db_html.xsl
+++ b/lib/erl_docgen/priv/xsl/db_html.xsl
@@ -578,8 +578,22 @@
<xsl:param name="curModule"/>
<html>
<head>
- <link rel="stylesheet" href="{$topdocdir}/otp_doc.css" type="text/css"/>
- <title>Erlang -- <xsl:value-of select="header/title"/></title>
+ <xsl:choose>
+ <xsl:when test="string-length($stylesheet) > 0">
+ <link rel="stylesheet" href="{$topdocdir}/{$stylesheet}" type="text/css"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <link rel="stylesheet" href="{$topdocdir}/otp_doc.css" type="text/css"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:choose>
+ <xsl:when test="string-length($winprefix) > 0">
+ <title><xsl:value-of select="$winprefix"/> -- <xsl:value-of select="header/title"/></title>
+ </xsl:when>
+ <xsl:otherwise>
+ <title>Erlang -- <xsl:value-of select="header/title"/></title>
+ </xsl:otherwise>
+ </xsl:choose>
</head>
<body bgcolor="white" text="#000000" link="#0000ff" vlink="#ff00ff" alink="#ff0000">
@@ -719,7 +733,14 @@
<xsl:template name="menu_top">
- <img alt="Erlang logo" src="{$topdocdir}/erlang-logo.png"/>
+ <xsl:choose>
+ <xsl:when test="string-length($logo) > 0">
+ <img alt="Erlang logo" src="{$topdocdir}/{$logo}"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <img alt="Erlang logo" src="{$topdocdir}/erlang-logo.png"/>
+ </xsl:otherwise>
+ </xsl:choose>
<br/>
<small>
<xsl:if test="boolean(/book/parts/part)">
@@ -731,7 +752,14 @@
<xsl:if test="boolean(/book/releasenotes)">
<a href="release_notes.html">Release Notes</a><br/>
</xsl:if>
- <a href="{$pdfdir}/{$appname}-{$appver}.pdf">PDF</a><br/>
+ <xsl:choose>
+ <xsl:when test="string-length($pdfname) > 0">
+ <a href="{$pdfdir}/{$pdfname}.pdf">PDF</a><br/>
+ </xsl:when>
+ <xsl:otherwise>
+ <a href="{$pdfdir}/{$appname}-{$appver}.pdf">PDF</a><br/>
+ </xsl:otherwise>
+ </xsl:choose>
<a href="{$topdocdir}/index.html">Top</a>
</small>
</xsl:template>
@@ -1817,7 +1845,14 @@
<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/>
+ <a name="{substring-before(nametext, '(')}">
+ <span class="bold_code">
+ <xsl:value-of select="ret"/>
+ <xsl:call-template name="maybe-space-after-ret">
+ <xsl:with-param name="s" select="ret"/>
+ </xsl:call-template>
+ <xsl:value-of select="nametext"/>
+ </span></a><br/>
</xsl:when>
<xsl:when test="ancestor::erlref">
<xsl:variable name="fname">
@@ -1845,6 +1880,18 @@
</xsl:template>
+ <xsl:template name="maybe-space-after-ret">
+ <xsl:param name="s"/>
+ <xsl:variable name="last_char"
+ select="substring($s, string-length($s), 1)"/>
+ <xsl:choose>
+ <xsl:when test="$last_char != '*'">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ </xsl:choose>
+ </xsl:template>
+
+
<!-- Type -->
<xsl:template match="type">
<xsl:param name="partnum"/>
diff --git a/lib/erl_docgen/priv/xsl/db_man.xsl b/lib/erl_docgen/priv/xsl/db_man.xsl
index 5234ba6bd0..33808859c7 100644
--- a/lib/erl_docgen/priv/xsl/db_man.xsl
+++ b/lib/erl_docgen/priv/xsl/db_man.xsl
@@ -3,7 +3,7 @@
#
# %CopyrightBegin%
#
- # Copyright Ericsson AB 2009-2011. All Rights Reserved.
+ # Copyright Ericsson AB 2009-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -758,10 +758,32 @@
<xsl:template name="name">
<xsl:text>&#10;.B&#10;</xsl:text>
- <xsl:apply-templates/>
+ <xsl:choose>
+ <xsl:when test="ancestor::cref">
+ <xsl:value-of select="ret"/>
+ <xsl:call-template name="maybe-space-after-ret">
+ <xsl:with-param name="s" select="ret"/>
+ </xsl:call-template>
+ <xsl:value-of select="nametext"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:apply-templates/>
+ </xsl:otherwise>
+ </xsl:choose>
<xsl:text>&#10;.br</xsl:text>
</xsl:template>
+ <xsl:template name="maybe-space-after-ret">
+ <xsl:param name="s"/>
+ <xsl:variable name="last_char"
+ select="substring($s, string-length($s), 1)"/>
+ <xsl:choose>
+ <xsl:when test="$last_char != '*'">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ </xsl:choose>
+ </xsl:template>
+
<!-- Type -->
<xsl:template match="type">
diff --git a/lib/erl_docgen/priv/xsl/db_pdf.xsl b/lib/erl_docgen/priv/xsl/db_pdf.xsl
index bf17406d84..c846bdbf34 100644
--- a/lib/erl_docgen/priv/xsl/db_pdf.xsl
+++ b/lib/erl_docgen/priv/xsl/db_pdf.xsl
@@ -3,7 +3,7 @@
#
# %CopyrightBegin%
#
- # Copyright Ericsson AB 2009-2011. All Rights Reserved.
+ # Copyright Ericsson AB 2009-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -650,7 +650,7 @@
<fo:flow flow-name="xsl-region-body">
<fo:block xsl:use-attribute-sets="cover.logo">
- <fo:external-graphic src="{$docgen}/priv/images/erlang-logo.gif"/>
+ <fo:external-graphic src="{$logo}"/>
</fo:block>
<fo:block xsl:use-attribute-sets="cover.title" id="cover-page">
<xsl:apply-templates/>
@@ -1102,11 +1102,13 @@
<xsl:template match="taglist/item">
<xsl:param name="partnum"/>
- <fo:block xsl:use-attribute-sets="tagitem">
- <xsl:apply-templates>
- <xsl:with-param name="partnum" select="$partnum"/>
- </xsl:apply-templates>
- </fo:block>
+ <fo:block-container>
+ <fo:block xsl:use-attribute-sets="tagitem">
+ <xsl:apply-templates>
+ <xsl:with-param name="partnum" select="$partnum"/>
+ </xsl:apply-templates>
+ </fo:block>
+ </fo:block-container>
</xsl:template>
@@ -1424,7 +1426,13 @@
<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>
+ <fo:block id="{generate-id(nametext)}">
+ <xsl:value-of select="ret"/>
+ <xsl:call-template name="maybe-space-after-ret">
+ <xsl:with-param name="s" select="ret"/>
+ </xsl:call-template>
+ <xsl:value-of select="nametext"/>
+ </fo:block>
</xsl:when>
<xsl:otherwise>
<fo:block id="{generate-id(.)}"><xsl:value-of select="."/></fo:block>
@@ -1432,6 +1440,16 @@
</xsl:choose>
</xsl:template>
+ <xsl:template name="maybe-space-after-ret">
+ <xsl:param name="s"/>
+ <xsl:variable name="last_char"
+ select="substring($s, string-length($s), 1)"/>
+ <xsl:choose>
+ <xsl:when test="$last_char != '*'">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ </xsl:choose>
+ </xsl:template>
<!-- Type -->
<xsl:template match="type">
diff --git a/lib/erl_docgen/priv/xsl/db_pdf_params.xsl b/lib/erl_docgen/priv/xsl/db_pdf_params.xsl
index 4d9c08d0c3..2e3b22acf4 100644
--- a/lib/erl_docgen/priv/xsl/db_pdf_params.xsl
+++ b/lib/erl_docgen/priv/xsl/db_pdf_params.xsl
@@ -3,7 +3,7 @@
#
# %CopyrightBegin%
#
- # Copyright Ericsson AB 2009-2011. All Rights Reserved.
+ # Copyright Ericsson AB 2009-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -52,7 +52,7 @@
<!-- XSL-FO properties -->
<xsl:attribute-set name="caption">
- <xsl:attribute name="font-family">sans-serif</xsl:attribute>
+ <xsl:attribute name="font-family">DejaVuSans, sans-serif</xsl:attribute>
<xsl:attribute name="font-size">0.8em</xsl:attribute>
<xsl:attribute name="font-weight">bold</xsl:attribute>
<xsl:attribute name="keep-with-previous.within-page">always</xsl:attribute>
@@ -61,7 +61,7 @@
</xsl:attribute-set>
<xsl:attribute-set name="pre">
- <xsl:attribute name="font-family">monospace</xsl:attribute>
+ <xsl:attribute name="font-family">DejaVuSansMono, monospace</xsl:attribute>
<xsl:attribute name="font-size">0.8em</xsl:attribute>
<xsl:attribute name="keep-together.within-page">auto</xsl:attribute>
<xsl:attribute name="linefeed-treatment">preserve</xsl:attribute>
@@ -87,7 +87,7 @@
<xsl:attribute-set name="cover.title">
<xsl:attribute name="border-before-style">solid</xsl:attribute>
<xsl:attribute name="border-before-width">10pt</xsl:attribute>
- <xsl:attribute name="border-color">#960003</xsl:attribute>
+ <xsl:attribute name="border-color"><xsl:value-of select="$pdfcolor"/></xsl:attribute>
<xsl:attribute name="font-size">2.3em</xsl:attribute>
<xsl:attribute name="padding-before">0.5em</xsl:attribute>
<xsl:attribute name="text-align">end</xsl:attribute>
@@ -101,7 +101,7 @@
<xsl:attribute-set name="cover.inner.copyright">
<xsl:attribute name="border-before-style">solid</xsl:attribute>
<xsl:attribute name="border-before-width">1pt</xsl:attribute>
- <xsl:attribute name="border-color">#960003</xsl:attribute>
+ <xsl:attribute name="border-color"><xsl:value-of select="$pdfcolor"/></xsl:attribute>
<xsl:attribute name="font-weight">bold</xsl:attribute>
<xsl:attribute name="padding-before">0.5em</xsl:attribute>
<xsl:attribute name="space-before">200mm</xsl:attribute>
@@ -160,9 +160,9 @@
<xsl:attribute-set name="h1">
<xsl:attribute name="border-after-style">solid</xsl:attribute>
<xsl:attribute name="border-after-width">1pt</xsl:attribute>
- <xsl:attribute name="border-color">#960003</xsl:attribute>
+ <xsl:attribute name="border-color"><xsl:value-of select="$pdfcolor"/></xsl:attribute>
<xsl:attribute name="break-before">page</xsl:attribute>
- <xsl:attribute name="font-family">sans-serif</xsl:attribute>
+ <xsl:attribute name="font-family">DejaVuSans, sans-serif</xsl:attribute>
<xsl:attribute name="font-size">1.83em</xsl:attribute>
<xsl:attribute name="font-weight">normal</xsl:attribute>
<xsl:attribute name="space-after">1em</xsl:attribute>
@@ -171,7 +171,7 @@
</xsl:attribute-set>
<xsl:attribute-set name="h2">
- <xsl:attribute name="font-family">sans-serif</xsl:attribute>
+ <xsl:attribute name="font-family">DejaVuSans, sans-serif</xsl:attribute>
<xsl:attribute name="font-size">1.5em</xsl:attribute>
<xsl:attribute name="font-weight">normal</xsl:attribute>
<xsl:attribute name="keep-with-next.within-page">always</xsl:attribute>
@@ -180,7 +180,7 @@
</xsl:attribute-set>
<xsl:attribute-set name="h3">
- <xsl:attribute name="font-family">sans-serif</xsl:attribute>
+ <xsl:attribute name="font-family">DejaVuSans, sans-serif</xsl:attribute>
<xsl:attribute name="font-size">1.33em</xsl:attribute>
<xsl:attribute name="font-weight">normal</xsl:attribute>
<xsl:attribute name="keep-with-next.within-page">always</xsl:attribute>
@@ -189,7 +189,7 @@
</xsl:attribute-set>
<xsl:attribute-set name="h4">
- <xsl:attribute name="font-family">sans-serif</xsl:attribute>
+ <xsl:attribute name="font-family">DejaVuSans, sans-serif</xsl:attribute>
<xsl:attribute name="font-size">1.17em</xsl:attribute>
<xsl:attribute name="font-weight">normal</xsl:attribute>
<xsl:attribute name="keep-with-next.within-page">always</xsl:attribute>
@@ -198,7 +198,7 @@
</xsl:attribute-set>
<xsl:attribute-set name="h5">
- <xsl:attribute name="font-family">sans-serif</xsl:attribute>
+ <xsl:attribute name="font-family">DejaVuSans, sans-serif</xsl:attribute>
<xsl:attribute name="font-size">1em</xsl:attribute>
<xsl:attribute name="font-weight">bold</xsl:attribute>
<xsl:attribute name="keep-with-next.within-page">always</xsl:attribute>
@@ -207,7 +207,7 @@
</xsl:attribute-set>
<xsl:attribute-set name="h6">
- <xsl:attribute name="font-family">sans-serif</xsl:attribute>
+ <xsl:attribute name="font-family">DejaVuSans, sans-serif</xsl:attribute>
<xsl:attribute name="font-size">0.83em</xsl:attribute>
<xsl:attribute name="font-weight">bold</xsl:attribute>
<xsl:attribute name="keep-with-next.within-page">always</xsl:attribute>
@@ -226,21 +226,21 @@
<xsl:attribute-set name="page-header">
<xsl:attribute name="border-after-style">solid</xsl:attribute>
<xsl:attribute name="border-after-width">2pt</xsl:attribute>
- <xsl:attribute name="border-color">#960003</xsl:attribute>
- <xsl:attribute name="font-family">sans-serif</xsl:attribute>
+ <xsl:attribute name="border-color"><xsl:value-of select="$pdfcolor"/></xsl:attribute>
+ <xsl:attribute name="font-family">DejaVuSans, sans-serif</xsl:attribute>
<xsl:attribute name="font-size">0.9em</xsl:attribute>
<xsl:attribute name="font-weight">bold</xsl:attribute>
</xsl:attribute-set>
<xsl:attribute-set name="page-footer">
- <xsl:attribute name="font-family">sans-serif</xsl:attribute>
+ <xsl:attribute name="font-family">DejaVuSans, sans-serif</xsl:attribute>
<xsl:attribute name="font-size">0.9em</xsl:attribute>
<xsl:attribute name="font-weight">bold</xsl:attribute>
</xsl:attribute-set>
<xsl:attribute-set name="code">
<xsl:attribute name="background-color">#e0e0ff</xsl:attribute>
- <xsl:attribute name="font-family">monospace</xsl:attribute>
+ <xsl:attribute name="font-family">DejaVuSansMono, monospace</xsl:attribute>
<xsl:attribute name="font-size">0.8em</xsl:attribute>
<xsl:attribute name="keep-together.within-page">auto</xsl:attribute>
<xsl:attribute name="linefeed-treatment">preserve</xsl:attribute>
@@ -303,7 +303,7 @@
<xsl:attribute-set name="module-name">
<xsl:attribute name="border-after-style">solid</xsl:attribute>
<xsl:attribute name="border-after-width">1pt</xsl:attribute>
- <xsl:attribute name="font-family">sans-serif</xsl:attribute>
+ <xsl:attribute name="font-family">DejaVuSans, sans-serif</xsl:attribute>
<xsl:attribute name="font-size">1.5em</xsl:attribute>
<xsl:attribute name="font-weight">normal</xsl:attribute>
<xsl:attribute name="keep-with-next.within-page">always</xsl:attribute>
@@ -313,7 +313,7 @@
<xsl:attribute-set name="function-name">
<xsl:attribute name="font-weight">bold</xsl:attribute>
- <xsl:attribute name="font-family">monospace</xsl:attribute>
+ <xsl:attribute name="font-family">DejaVuSansMono, monospace</xsl:attribute>
<!-- xsl:attribute name="font-size">0.8em</xsl:attribute -->
<xsl:attribute name="keep-with-next.within-page">always</xsl:attribute>
<xsl:attribute name="space-after">0.25em</xsl:attribute>
@@ -401,7 +401,7 @@
</xsl:attribute-set>
<xsl:attribute-set name="caption">
- <xsl:attribute name="font-family">sans-serif</xsl:attribute>
+ <xsl:attribute name="font-family">DejaVuSans, sans-serif</xsl:attribute>
<xsl:attribute name="font-size">0.8em</xsl:attribute>
<xsl:attribute name="font-weight">bold</xsl:attribute>
<xsl:attribute name="keep-with-previous.within-page">always</xsl:attribute>
diff --git a/lib/erl_docgen/src/Makefile b/lib/erl_docgen/src/Makefile
index 4d8f43bfed..ef96f5dbf2 100644
--- a/lib/erl_docgen/src/Makefile
+++ b/lib/erl_docgen/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2011. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -78,10 +78,10 @@ docs:
# ----------------------------------------------------
$(APP_TARGET): $(APP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
# ----------------------------------------------------
# Release Target
diff --git a/lib/erl_docgen/src/docgen_edoc_xml_cb.erl b/lib/erl_docgen/src/docgen_edoc_xml_cb.erl
index 20daae8215..e3cc354206 100644
--- a/lib/erl_docgen/src/docgen_edoc_xml_cb.erl
+++ b/lib/erl_docgen/src/docgen_edoc_xml_cb.erl
@@ -1,19 +1,20 @@
-%% ``The contents of this file are subject to the Erlang Public License,
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2001-2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance 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 expressed or implied. See
-%% the Licence for the specific language governing rights and limitations
+%% 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.
-%% Portions created by Ericsson are Copyright 1999-2006, Ericsson AB.
-%% All Rights Reserved.��
-%%
-%% $Id$
+%% %CopyrightEnd%
%%
-module(docgen_edoc_xml_cb).
@@ -39,12 +40,14 @@
module(Element, Opts) ->
SortP = proplists:get_value(sort_functions, Opts, true),
XML = layout_module(Element, SortP),
- xmerl:export_simple([XML], docgen_xmerl_xml_cb, []).
+ RootAttributes = root_attributes(Element, Opts),
+ xmerl:export_simple([XML], docgen_xmerl_xml_cb, RootAttributes).
%% CHAPTER
-overview(Element, _Opts) ->
+overview(Element, Opts) ->
XML = layout_chapter(Element),
- xmerl:export_simple([XML], docgen_xmerl_xml_cb, []).
+ RootAttributes = root_attributes(Element, Opts),
+ xmerl:export_simple([XML], docgen_xmerl_xml_cb, RootAttributes).
%%--Internal functions--------------------------------------------------
@@ -99,6 +102,16 @@ layout_module(#xmlElement{name = module, content = Es}=E, SortP) ->
?NL,Authors]
}.
+root_attributes(Element, Opts) ->
+ Encoding = case get_attrval(encoding, Element) of
+ "" ->
+ DefaultEncoding = epp:default_encoding(),
+ proplists:get_value(encoding, Opts, DefaultEncoding);
+ Enc ->
+ Enc
+ end,
+ [#xmlAttribute{name=encoding, value=Encoding}].
+
layout_chapter(#xmlElement{name=overview, content=Es}) ->
Title = get_text(title, Es),
Header = {header, [
@@ -338,7 +351,7 @@ otp_xmlify_e(#xmlElement{name=code} = E) -> % 4)
end;
otp_xmlify_e(#xmlElement{name=Tag} = E) % 5a
when Tag==h1; Tag==h2; Tag==h3; Tag==h4; Tag==h5 ->
- Content = text_only(E#xmlElement.content),
+ Content = text_and_a_name_only(E#xmlElement.content),
[E#xmlElement{name=b, content=Content}];
otp_xmlify_e(#xmlElement{name=Tag} = E) % 5b-c)
when Tag==center;
@@ -1161,6 +1174,18 @@ get_text(#xmlElement{content=[#xmlText{value=Text}]}) ->
get_text(#xmlElement{content=[E]}) ->
get_text(E).
+%% text_and_name_only(Es) -> Ts
+text_and_a_name_only([#xmlElement{
+ name = a,
+ attributes = [#xmlAttribute{name=name}]} = Name|Es]) ->
+ [Name|text_and_a_name_only(Es)];
+text_and_a_name_only([#xmlElement{content = Content}|Es]) ->
+ text_and_a_name_only(Content) ++ text_and_a_name_only(Es);
+text_and_a_name_only([#xmlText{} = E |Es]) ->
+ [E | text_and_a_name_only(Es)];
+text_and_a_name_only([]) ->
+ [].
+
%% text_only(Es) -> Ts
%% Takes a list of xmlElement and xmlText and return a lists of xmlText.
text_only([#xmlElement{content = Content}|Es]) ->
diff --git a/lib/erl_docgen/src/docgen_xmerl_xml_cb.erl b/lib/erl_docgen/src/docgen_xmerl_xml_cb.erl
index 884932ed12..d713b61c0a 100644
--- a/lib/erl_docgen/src/docgen_xmerl_xml_cb.erl
+++ b/lib/erl_docgen/src/docgen_xmerl_xml_cb.erl
@@ -1,19 +1,20 @@
-%% ``The contents of this file are subject to the Erlang Public License,
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2001-2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance 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 expressed or implied. See
-%% the Licence for the specific language governing rights and limitations
+%% 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.
-%% Portions created by Ericsson are Copyright 1999-2006, Ericsson AB.
-%% All Rights Reserved.��
-%%
-%% $Id$
+%% %CopyrightEnd%
%%
-module(docgen_xmerl_xml_cb).
@@ -35,9 +36,14 @@
'#xml-inheritance#'() ->
[xmerl_xml].
-'#root#'(Data, _Attrs, [], _E) ->
+'#root#'(Data, Attrs, [], _E) ->
+ Encoding =
+ case [E || #xmlAttribute{name = encoding, value = E} <- Attrs] of
+ [E] -> E;
+ _ -> atom_to_list(epp:default_encoding())
+ end,
["<",DTD,">"] = hd(hd(Data)),
- ["<?xml version=\"1.0\" encoding=\"latin1\" ?>\n",
+ ["<?xml version=\"1.0\" encoding=\"",Encoding,"\" ?>\n",
"<!DOCTYPE "++DTD++" SYSTEM \""++DTD++".dtd\">\n",
Data].
diff --git a/lib/erl_docgen/vsn.mk b/lib/erl_docgen/vsn.mk
index bf10591f34..a2262198dc 100644
--- a/lib/erl_docgen/vsn.mk
+++ b/lib/erl_docgen/vsn.mk
@@ -1,2 +1 @@
-ERL_DOCGEN_VSN = 0.3.1
-
+ERL_DOCGEN_VSN = 0.3.4
diff --git a/lib/erl_interface/aclocal.m4 b/lib/erl_interface/aclocal.m4
index 339a15a2bb..918e30a886 100644
--- a/lib/erl_interface/aclocal.m4
+++ b/lib/erl_interface/aclocal.m4
@@ -1,7 +1,7 @@
dnl
dnl %CopyrightBegin%
dnl
-dnl Copyright Ericsson AB 1998-2011. All Rights Reserved.
+dnl Copyright Ericsson AB 1998-2012. All Rights Reserved.
dnl
dnl The contents of this file are subject to the Erlang Public License,
dnl Version 1.1, (the "License"); you may not use this file except in
@@ -59,6 +59,7 @@ AC_ARG_VAR(erl_xcomp_isysroot, [Absolute cross system root include path (only us
dnl Cross compilation variables
AC_ARG_VAR(erl_xcomp_bigendian, [big endian system: yes|no (only used when cross compiling)])
+AC_ARG_VAR(erl_xcomp_double_middle_endian, [double-middle-endian system: yes|no (only used when cross compiling)])
AC_ARG_VAR(erl_xcomp_linux_clock_gettime_correction, [clock_gettime() can be used for time correction: yes|no (only used when cross compiling)])
AC_ARG_VAR(erl_xcomp_linux_nptl, [have Native POSIX Thread Library: yes|no (only used when cross compiling)])
AC_ARG_VAR(erl_xcomp_linux_usable_sigusrx, [SIGUSR1 and SIGUSR2 can be used: yes|no (only used when cross compiling)])
@@ -606,6 +607,103 @@ ifelse([$5], , , [$5
fi
])
+dnl ----------------------------------------------------------------------
+dnl
+dnl AC_DOUBLE_MIDDLE_ENDIAN
+dnl
+dnl Checks whether doubles are represented in "middle-endian" format.
+dnl Sets ac_cv_double_middle_endian={no,yes,unknown} accordingly,
+dnl as well as DOUBLE_MIDDLE_ENDIAN.
+dnl
+dnl
+
+AC_DEFUN([AC_C_DOUBLE_MIDDLE_ENDIAN],
+[AC_CACHE_CHECK(whether double word ordering is middle-endian, ac_cv_c_double_middle_endian,
+[# It does not; compile a test program.
+AC_RUN_IFELSE(
+[AC_LANG_SOURCE([[#include <stdlib.h>
+
+int
+main(void)
+{
+ int i = 0;
+ int zero = 0;
+ int bigendian;
+ int zero_index = 0;
+
+ union
+ {
+ long int l;
+ char c[sizeof (long int)];
+ } u;
+
+ /* we'll use the one with 32-bit words */
+ union
+ {
+ double d;
+ unsigned int c[2];
+ } vint;
+
+ union
+ {
+ double d;
+ unsigned long c[2];
+ } vlong;
+
+ union
+ {
+ double d;
+ unsigned short c[2];
+ } vshort;
+
+
+ /* Are we little or big endian? From Harbison&Steele. */
+ u.l = 1;
+ bigendian = (u.c[sizeof (long int) - 1] == 1);
+
+ zero_index = bigendian ? 1 : 0;
+
+ vint.d = 1.0;
+ vlong.d = 1.0;
+ vshort.d = 1.0;
+
+ if (sizeof(unsigned int) == 4)
+ {
+ if (vint.c[zero_index] != 0)
+ zero = 1;
+ }
+ else if (sizeof(unsigned long) == 4)
+ {
+ if (vlong.c[zero_index] != 0)
+ zero = 1;
+ }
+ else if (sizeof(unsigned short) == 4)
+ {
+ if (vshort.c[zero_index] != 0)
+ zero = 1;
+ }
+
+ exit (zero);
+}
+]])],
+ [ac_cv_c_double_middle_endian=no],
+ [ac_cv_c_double_middle_endian=yes],
+ [ac_cv_c_double_middle=unknown])])
+case $ac_cv_c_double_middle_endian in
+ yes)
+ m4_default([$1],
+ [AC_DEFINE([DOUBLE_MIDDLE_ENDIAN], 1,
+ [Define to 1 if your processor stores the words in a double in
+ middle-endian format (like some ARMs).])]) ;;
+ no)
+ $2 ;;
+ *)
+ m4_default([$3],
+ [AC_MSG_WARN([unknown double endianness
+presetting ac_cv_c_double_middle_endian=no (or yes) will help])]) ;;
+esac
+])# AC_C_DOUBLE_MIDDLE_ENDIAN
+
dnl ----------------------------------------------------------------------
dnl
@@ -642,11 +740,16 @@ dnl Try to find POSIX threads
dnl The usual pthread lib...
AC_CHECK_LIB(pthread, pthread_create, THR_LIBS="-lpthread")
-dnl FreeBSD has pthreads in special c library, c_r...
+dnl Very old versions of FreeBSD have pthreads in special c library, c_r...
if test "x$THR_LIBS" = "x"; then
AC_CHECK_LIB(c_r, pthread_create, THR_LIBS="-lc_r")
fi
+dnl QNX has pthreads in standard C library
+ if test "x$THR_LIBS" = "x"; then
+ AC_CHECK_FUNC(pthread_create, THR_LIBS="none_needed")
+ fi
+
dnl On ofs1 the '-pthread' switch should be used
if test "x$THR_LIBS" = "x"; then
AC_MSG_CHECKING([if the '-pthread' switch can be used])
@@ -667,6 +770,9 @@ dnl On ofs1 the '-pthread' switch should be used
if test "x$THR_LIBS" != "x"; then
THR_DEFS="$THR_DEFS -D_THREAD_SAFE -D_REENTRANT -DPOSIX_THREADS"
THR_LIB_NAME=pthread
+ if test "x$THR_LIBS" = "xnone_needed"; then
+ THR_LIBS=
+ fi
case $host_os in
solaris*)
THR_DEFS="$THR_DEFS -D_POSIX_PTHREAD_SEMANTICS" ;;
@@ -1337,6 +1443,14 @@ if test "$ac_cv_c_bigendian" = "yes"; then
AC_DEFINE(ETHR_BIGENDIAN, 1, [Define if bigendian])
fi
+case X$erl_xcomp_double_middle_endian in
+ X) ;;
+ Xyes|Xno|Xunknown) ac_cv_c_double_middle_endian=$erl_xcomp_double_middle_endian;;
+ *) AC_MSG_ERROR([Bad erl_xcomp_double_middle_endian value: $erl_xcomp_double_middle_endian]);;
+esac
+
+AC_C_DOUBLE_MIDDLE_ENDIAN
+
AC_ARG_ENABLE(native-ethr-impls,
AS_HELP_STRING([--disable-native-ethr-impls],
[disable native ethread implementations]),
@@ -1735,6 +1849,31 @@ case $erl_gethrvtime in
esac
])dnl
+dnl ----------------------------------------------------------------------
+dnl
+dnl LM_TRY_ENABLE_CFLAG
+dnl
+dnl
+dnl Tries a CFLAG and sees if it can be enabled without compiler errors
+dnl $1: textual cflag to add
+dnl $2: variable to store the modified CFLAG in
+dnl Usage example LM_TRY_ENABLE_CFLAG([-Werror=return-type], [CFLAGS])
+dnl
+dnl
+AC_DEFUN([LM_TRY_ENABLE_CFLAG], [
+ AC_MSG_CHECKING([if we can add $1 to $2 (via CFLAGS)])
+ saved_CFLAGS=$CFLAGS;
+ CFLAGS="$1 $$2";
+ AC_TRY_COMPILE([],[return 0;],can_enable_flag=true,can_enable_flag=false)
+ CFLAGS=$saved_CFLAGS;
+ if test "X$can_enable_flag" = "Xtrue"; then
+ AC_MSG_RESULT([yes])
+ AS_VAR_SET($2, "$1 $$2")
+ else
+ AC_MSG_RESULT([no])
+ fi
+])
+
dnl ERL_TRY_LINK_JAVA(CLASSES, FUNCTION-BODY
dnl [ACTION_IF_FOUND [, ACTION-IF-NOT-FOUND]])
dnl Freely inspired by AC_TRY_LINK. (Maybe better to create a
diff --git a/lib/erl_interface/configure.in b/lib/erl_interface/configure.in
index c958f80065..d511f2e240 100644
--- a/lib/erl_interface/configure.in
+++ b/lib/erl_interface/configure.in
@@ -1,7 +1,7 @@
# -*- Autoconf -*-
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2000-2011. All Rights Reserved.
+# Copyright Ericsson AB 2000-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -79,7 +79,7 @@ AC_ARG_ENABLE(threads,
no) threads_disabled=yes ;;
*) threads_disabled=no ;;
esac ],
-[ threads_disabled=no ])
+[ threads_disabled=maybe ])
dnl ----------------------------------------------------------------------
dnl Checks for programs
@@ -237,12 +237,16 @@ AC_SUBST(THR_DEFS)
AC_SUBST(EI_THREADS)
case "$threads_disabled" in
- no)
+ no|maybe)
LM_CHECK_THR_LIB
case "$THR_LIB_NAME" in
"")
EI_THREADS="false"
+ # Fail if --enable-threads given and no threads found
+ if test "x$threads_disabled" = "xno"; then
+ AC_MSG_ERROR(No threads support found)
+ fi
;;
win32_threads)
EI_THREADS="true"
@@ -269,6 +273,8 @@ esac
AC_SUBST(WFLAGS)
if test "x$GCC" = xyes; then
+ # Treat certain GCC warnings as errors
+ LM_TRY_ENABLE_CFLAG([-Werror=return-type], [WERRORFLAGS])
WFLAGS="-Wall -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -Wnested-externs -Winline"
# check which GCC version
GCC_VERSION=`$CC -v 2>&1 | sed -n 's/gcc version //p'`
@@ -283,8 +289,10 @@ if test "x$GCC" = xyes; then
*)
WFLAGS="$WFLAGS -fno-strict-aliasing";;
esac
+ CFLAGS="$WERRORFLAGS $CFLAGS"
else
WFLAGS=""
+ WERRORFLAGS=""
fi
# ---------------------------------------------------------------------------
diff --git a/lib/erl_interface/doc/src/Makefile b/lib/erl_interface/doc/src/Makefile
index 2bad8976b5..b8e638ed24 100644
--- a/lib/erl_interface/doc/src/Makefile
+++ b/lib/erl_interface/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1998-2009. All Rights Reserved.
+# Copyright Ericsson AB 1998-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/erl_interface/doc/src/ei.xml b/lib/erl_interface/doc/src/ei.xml
index 539e16d837..117c787da6 100644
--- a/lib/erl_interface/doc/src/ei.xml
+++ b/lib/erl_interface/doc/src/ei.xml
@@ -82,6 +82,25 @@
function returns the size required (note that for strings an
extra byte is needed for the 0 string terminator).</p>
</description>
+ <section>
+ <title>DATA TYPES</title>
+
+ <taglist>
+ <tag><marker id="erlang_char_encoding"/>enum erlang_char_encoding</tag>
+ <item>
+ <p/>
+ <code type="none">
+enum erlang_char_encoding {
+ ERLANG_ASCII, ERLANG_LATIN1, ERLANG_UTF8
+};
+</code>
+ <p>The character encoding used for atoms. <c>ERLANG_ASCII</c> represents 7-bit ASCII.
+ Latin1 and UTF8 are different extensions of 7-bit ASCII. All 7-bit ASCII characters
+ are valid Latin1 and UTF8 characters. ASCII and Latin1 both represent each character
+ by one byte. A UTF8 character can consist of one to four bytes.</p>
+ </item>
+ </taglist>
+ </section>
<funcs>
<func>
<name><ret>void</ret><nametext>ei_set_compat_rel(release_number)</nametext></name>
@@ -225,12 +244,32 @@
<fsummary>Encode an atom</fsummary>
<desc>
<p>Encodes an atom in the binary format. The <c><![CDATA[p]]></c> parameter
- is the name of the atom. Only upto <c><![CDATA[MAXATOMLEN]]></c> bytes
+ is the name of the atom in latin1 encoding. Only upto <c>MAXATOMLEN-1</c> bytes
are encoded. The name should be zero-terminated, except for
the <c><![CDATA[ei_x_encode_atom_len()]]></c> function.</p>
</desc>
</func>
<func>
+ <name><ret>int</ret><nametext>ei_encode_atom_as(char *buf, int *index, const char *p, enum erlang_char_encoding from_enc, enum erlang_char_encoding to_enc)</nametext></name>
+ <name><ret>int</ret><nametext>ei_encode_atom_len_as(char *buf, int *index, const char *p, int len, enum erlang_char_encoding from_enc, enum erlang_char_encoding to_enc)</nametext></name>
+ <name><ret>int</ret><nametext>ei_x_encode_atom_as(ei_x_buff* x, const char *p, enum erlang_char_encoding from_enc, enum erlang_char_encoding to_enc)</nametext></name>
+ <name><ret>int</ret><nametext>ei_x_encode_atom_len_as(ei_x_buff* x, const char *p, int len, enum erlang_char_encoding from_enc, enum erlang_char_encoding to_enc)</nametext></name>
+ <fsummary>Encode an atom</fsummary>
+ <desc>
+ <p>Encodes an atom in the binary format with character encoding
+ <c><seealso marker="#erlang_char_encoding">to_enc</seealso></c> (latin1 or utf8).
+ The <c>p</c> parameter is the name of the atom with character encoding
+ <c><seealso marker="#erlang_char_encoding">from_enc</seealso></c> (ascii, latin1 or utf8).
+ The name must either be zero-terminated or a function variant with a <c>len</c>
+ parameter must be used.</p>
+ <p>The encoding will fail if <c>p</c> is not a valid string in encoding <c>from_enc</c>,
+ if the string is too long or if it can not be represented with character encoding <c>to_enc</c>.</p>
+ <p>These functions were introduced in R16 release of Erlang/OTP as part of a first step
+ to support UTF8 atoms. Atoms encoded with <c>ERLANG_UTF8</c>
+ can not be decoded by earlier releases than R16.</p>
+ </desc>
+ </func>
+ <func>
<name><ret>int</ret><nametext>ei_encode_binary(char *buf, int *index, const void *p, long len)</nametext></name>
<name><ret>int</ret><nametext>ei_x_encode_binary(ei_x_buff* x, const void *p, long len)</nametext></name>
<fsummary>Encode a binary</fsummary>
@@ -490,11 +529,32 @@ ei_x_encode_empty_list(&amp;x);
<fsummary>Decode an atom</fsummary>
<desc>
<p>This function decodes an atom from the binary format. The
- name of the atom is placed at <c><![CDATA[p]]></c>. There can be at most
+ null terminated name of the atom is placed at <c><![CDATA[p]]></c>. There can be at most
<c><![CDATA[MAXATOMLEN]]></c> bytes placed in the buffer.</p>
</desc>
</func>
<func>
+ <name><ret>int</ret><nametext>ei_decode_atom_as(const char *buf, int *index, char *p, int plen, enum erlang_char_encoding want, enum erlang_char_encoding* was, enum erlang_char_encoding* result)</nametext></name>
+ <fsummary>Decode an atom</fsummary>
+ <desc>
+ <p>This function decodes an atom from the binary format. The
+ null terminated name of the atom is placed in buffer at <c>p</c> of length
+ <c>plen</c> bytes.</p>
+ <p>The wanted string encoding is specified by <c><seealso marker="#erlang_char_encoding">
+ want</seealso></c>. The original encoding used in the
+ binary format (latin1 or utf8) can be obtained from <c>*was</c>. The actual encoding of the resulting string
+ (7-bit ascii, latin1 or utf8) can be obtained from <c>*result</c>. Both <c>was</c> and <c>result</c> can be <c>NULL</c>.
+
+ <c>*result</c> may differ from <c>want</c> if <c>want</c> is a bitwise-or'd combination like
+ <c>ERLANG_LATIN1|ERLANG_UTF8</c> or if <c>*result</c> turn out to be pure 7-bit ascii
+ (compatible with both latin1 and utf8).</p>
+ <p>This function fails if the atom is too long for the buffer
+ or if it can not be represented with encoding <c>want</c>.</p>
+ <p>This function was introduced in R16 release of Erlang/OTP as part of a first step
+ to support UTF8 atoms.</p>
+ </desc>
+ </func>
+ <func>
<name><ret>int</ret><nametext>ei_decode_binary(const char *buf, int *index, void *p, long *len)</nametext></name>
<fsummary>Decode a binary</fsummary>
<desc>
diff --git a/lib/erl_interface/doc/src/erl_eterm.xml b/lib/erl_interface/doc/src/erl_eterm.xml
index f403618c59..c7840d7813 100644
--- a/lib/erl_interface/doc/src/erl_eterm.xml
+++ b/lib/erl_interface/doc/src/erl_eterm.xml
@@ -77,10 +77,12 @@
</p>
<taglist>
<tag><c><![CDATA[char *ERL_ATOM_PTR(t)]]></c></tag>
+ <tag><c><![CDATA[char *ERL_ATOM_PTR_UTF8(t)]]></c></tag>
<item>A string representing atom <c><![CDATA[t]]></c>.
</item>
<tag><c><![CDATA[int ERL_ATOM_SIZE(t)]]></c></tag>
- <item>The length (in characters) of atom t.</item>
+ <tag><c><![CDATA[int ERL_ATOM_SIZE_UTF8(t)]]></c></tag>
+ <item>The length (in bytes) of atom t.</item>
<tag><c><![CDATA[void *ERL_BIN_PTR(t)]]></c></tag>
<item>A pointer to the contents of <c><![CDATA[t]]></c></item>
<tag><c><![CDATA[int ERL_BIN_SIZE(t)]]></c></tag>
@@ -92,6 +94,7 @@
<tag><c><![CDATA[double ERL_FLOAT_VALUE(t)]]></c></tag>
<item>The floating point value of <c><![CDATA[t]]></c>.</item>
<tag><c><![CDATA[ETERM *ERL_PID_NODE(t)]]></c></tag>
+ <tag><c><![CDATA[ETERM *ERL_PID_NODE_UTF8(t)]]></c></tag>
<item>The Node in pid <c><![CDATA[t]]></c>.</item>
<tag><c><![CDATA[int ERL_PID_NUMBER(t)]]></c></tag>
<item>The sequence number in pid <c><![CDATA[t]]></c>.</item>
@@ -104,6 +107,7 @@
<tag><c><![CDATA[int ERL_PORT_CREATION(t)]]></c></tag>
<item>The creation number in port <c><![CDATA[t]]></c>.</item>
<tag><c><![CDATA[ETERM *ERL_PORT_NODE(t)]]></c></tag>
+ <tag><c><![CDATA[ETERM *ERL_PORT_NODE_UTF8(t)]]></c></tag>
<item>The node in port <c><![CDATA[t]]></c>.</item>
<tag><c><![CDATA[int ERL_REF_NUMBER(t)]]></c></tag>
<item>The first part of the reference number in ref <c><![CDATA[t]]></c>. Use
@@ -296,7 +300,7 @@ iohead ::= Binary
<name><ret>ETERM *</ret><nametext>erl_mk_atom(string)</nametext></name>
<fsummary>Creates an atom</fsummary>
<type>
- <v>char *string;</v>
+ <v>const char *string;</v>
</type>
<desc>
<p>Creates an atom.</p>
@@ -305,10 +309,12 @@ iohead ::= Binary
<p>Returns an Erlang term containing an atom. Note that it is
the callers responsibility to make sure that <c><![CDATA[string]]></c>
contains a valid name for an atom.</p>
- <p><c><![CDATA[ERL_ATOM_PTR(atom)]]></c> can be used to retrieve the
- atom name (as a string). Note that the string is not
- 0-terminated in the atom. <c><![CDATA[ERL_ATOM_SIZE(atom)]]></c>returns
- the length of the atom name.</p>
+ <p><c><![CDATA[ERL_ATOM_PTR(atom)]]></c> and <c><![CDATA[ERL_ATOM_PTR_UTF8(atom)]]></c>
+ can be used to retrieve the atom name (as a null terminated string). <c><![CDATA[ERL_ATOM_SIZE(atom)]]></c>
+ and <c><![CDATA[ERL_ATOM_SIZE_UTF8(atom)]]></c> returns the length of the atom name.</p>
+ <note><p>Note that the UTF8 variants were introduced in Erlang/OTP releases R16
+ and the string returned by <c>ERL_ATOM_PTR(atom)</c> was not null terminated on older releases.</p>
+ </note>
</desc>
</func>
<func>
diff --git a/lib/erl_interface/doc/src/notes.xml b/lib/erl_interface/doc/src/notes.xml
index 48ac0be22d..f0a9b336ff 100644
--- a/lib/erl_interface/doc/src/notes.xml
+++ b/lib/erl_interface/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2004</year><year>2011</year>
+ <year>2004</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -30,6 +30,41 @@
</header>
<p>This document describes the changes made to the Erl_interface application.</p>
+<section><title>Erl_Interface 3.7.9</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>Teach lib/erl_interface/configure.in to look for
+ pthreads support in libc (where it can be found on
+ QNX)</p> <p>A minor tweak such that this configure
+ *fails* if you pass --enable-threads and no pthreads
+ support can be found.</p> (Thanks to Per Hedeland)
+ <p>
+ Own Id: OTP-10581</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erl_Interface 3.7.8</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Detect when middle endian doubles are used by a platform
+ and account for it when decoding floats. (Thanks to Mike
+ Sperber)</p>
+ <p>
+ Own Id: OTP-10209</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Erl_Interface 3.7.7</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/erl_interface/include/ei.h b/lib/erl_interface/include/ei.h
index ae815b414a..2278a28adb 100644
--- a/lib/erl_interface/include/ei.h
+++ b/lib/erl_interface/include/ei.h
@@ -115,6 +115,9 @@
#define ERL_FLOAT_EXT 'c'
#define NEW_FLOAT_EXT 'F'
#define ERL_ATOM_EXT 'd'
+#define ERL_SMALL_ATOM_EXT 's'
+#define ERL_ATOM_UTF8_EXT 'v'
+#define ERL_SMALL_ATOM_UTF8_EXT 'w'
#define ERL_REFERENCE_EXT 'e'
#define ERL_NEW_REFERENCE_EXT 'r'
#define ERL_PORT_EXT 'f'
@@ -183,12 +186,21 @@ extern volatile int __erl_errno;
#define EI_MAXHOSTNAMELEN 64
#define EI_MAXALIVELEN 63
#define EI_MAX_COOKIE_SIZE 512
-#define MAXATOMLEN 255
+#define MAXATOMLEN (255 + 1)
+#define MAXATOMLEN_UTF8 (255*4 + 1)
#define MAXNODELEN EI_MAXALIVELEN+1+EI_MAXHOSTNAMELEN
+enum erlang_char_encoding {
+ ERLANG_ASCII = 1,
+ ERLANG_LATIN1 = 2,
+ ERLANG_UTF8 = 4,
+ ERLANG_ANY = ERLANG_ASCII|ERLANG_LATIN1|ERLANG_UTF8
+};
+
/* a pid */
typedef struct {
- char node[MAXATOMLEN+1];
+ char node[MAXATOMLEN_UTF8];
+ enum erlang_char_encoding node_org_enc;
unsigned int num;
unsigned int serial;
unsigned int creation;
@@ -196,14 +208,16 @@ typedef struct {
/* a port */
typedef struct {
- char node[MAXATOMLEN+1];
+ char node[MAXATOMLEN_UTF8];
+ enum erlang_char_encoding node_org_enc;
unsigned int id;
unsigned int creation;
} erlang_port;
/* a ref */
typedef struct {
- char node[MAXATOMLEN+1];
+ char node[MAXATOMLEN_UTF8];
+ enum erlang_char_encoding node_org_enc;
int len;
unsigned int n[3];
unsigned int creation;
@@ -223,15 +237,16 @@ typedef struct {
long msgtype;
erlang_pid from;
erlang_pid to;
- char toname[MAXATOMLEN+1];
- char cookie[MAXATOMLEN+1];
+ char toname[MAXATOMLEN_UTF8];
+ char cookie[MAXATOMLEN_UTF8];
erlang_trace token;
} erlang_msg;
/* a fun */
typedef struct {
long arity;
- char module[MAXATOMLEN+1];
+ char module[MAXATOMLEN_UTF8];
+ enum erlang_char_encoding module_org_enc;
char md5[16];
long index;
long old_index;
@@ -256,7 +271,7 @@ typedef struct {
union {
long i_val;
double d_val;
- char atom_name[MAXATOMLEN+1];
+ char atom_name[MAXATOMLEN_UTF8];
erlang_pid pid;
erlang_port port;
erlang_ref ref;
@@ -425,9 +440,17 @@ int ei_encode_string_len(char *buf, int *index, const char *p, int len);
int ei_x_encode_string(ei_x_buff* x, const char* s);
int ei_x_encode_string_len(ei_x_buff* x, const char* s, int len);
int ei_encode_atom(char *buf, int *index, const char *p);
+int ei_encode_atom_as(char *buf, int *index, const char *p,
+ enum erlang_char_encoding from, enum erlang_char_encoding to);
int ei_encode_atom_len(char *buf, int *index, const char *p, int len);
+int ei_encode_atom_len_as(char *buf, int *index, const char *p, int len,
+ enum erlang_char_encoding from, enum erlang_char_encoding to);
int ei_x_encode_atom(ei_x_buff* x, const char* s);
+int ei_x_encode_atom_as(ei_x_buff* x, const char* s,
+ enum erlang_char_encoding from, enum erlang_char_encoding to);
int ei_x_encode_atom_len(ei_x_buff* x, const char* s, int len);
+int ei_x_encode_atom_len_as(ei_x_buff* x, const char* s, int len,
+ enum erlang_char_encoding from, enum erlang_char_encoding to);
int ei_encode_binary(char *buf, int *index, const void *p, long len);
int ei_x_encode_binary(ei_x_buff* x, const void* s, int len);
int ei_encode_pid(char *buf, int *index, const erlang_pid *p);
@@ -477,6 +500,7 @@ int ei_decode_boolean(const char *buf, int *index, int *p);
int ei_decode_char(const char *buf, int *index, char *p);
int ei_decode_string(const char *buf, int *index, char *p);
int ei_decode_atom(const char *buf, int *index, char *p);
+int ei_decode_atom_as(const char *buf, int *index, char *p, int destlen, enum erlang_char_encoding want, enum erlang_char_encoding* was, enum erlang_char_encoding* result);
int ei_decode_binary(const char *buf, int *index, void *p, long *len);
int ei_decode_fun(const char* buf, int* index, erlang_fun* p);
void free_fun(erlang_fun* f);
diff --git a/lib/erl_interface/include/erl_interface.h b/lib/erl_interface/include/erl_interface.h
index 1c4a94700d..98acc0d71d 100644
--- a/lib/erl_interface/include/erl_interface.h
+++ b/lib/erl_interface/include/erl_interface.h
@@ -95,19 +95,24 @@
#define ERL_FLOAT_VALUE(x) ((x)->uval.fval.f)
-#define ERL_ATOM_PTR(x) ((x)->uval.aval.a)
-#define ERL_ATOM_SIZE(x) ((x)->uval.aval.len)
+#define ERL_ATOM_PTR(x) erl_atom_ptr_latin1((Erl_Atom_data*) &(x)->uval.aval.d)
+#define ERL_ATOM_PTR_UTF8(x) erl_atom_ptr_utf8((Erl_Atom_data*) &(x)->uval.aval.d)
+#define ERL_ATOM_SIZE(x) erl_atom_size_latin1((Erl_Atom_data*) &(x)->uval.aval.d)
+#define ERL_ATOM_SIZE_UTF8(x) erl_atom_size_utf8((Erl_Atom_data*) &(x)->uval.aval.d)
-#define ERL_PID_NODE(x) ((x)->uval.pidval.node)
+#define ERL_PID_NODE(x) erl_atom_ptr_latin1((Erl_Atom_data*) &(x)->uval.pidval.node)
+#define ERL_PID_NODE_UTF8(x) erl_atom_ptr_utf8((Erl_Atom_data*) &(x)->uval.pidval.node)
#define ERL_PID_NUMBER(x) ((x)->uval.pidval.number)
#define ERL_PID_SERIAL(x) ((x)->uval.pidval.serial)
#define ERL_PID_CREATION(x) ((x)->uval.pidval.creation)
-#define ERL_PORT_NODE(x) ((x)->uval.portval.node)
+#define ERL_PORT_NODE(x) erl_atom_ptr_latin1((Erl_Atom_data*) &(x)->uval.portval.node)
+#define ERL_PORT_NODE_UTF8(x) erl_atom_ptr_utf8((Erl_Atom_data*) &(x)->uval.portval.node)
#define ERL_PORT_NUMBER(x) ((x)->uval.portval.number)
#define ERL_PORT_CREATION(x) ((x)->uval.portval.creation)
-#define ERL_REF_NODE(x) ((x)->uval.refval.node)
+#define ERL_REF_NODE(x) erl_atom_ptr_latin1((Erl_Atom_data*) &(x)->uval.refval.node)
+#define ERL_REF_NODE_UTF8(x) erl_atom_ptr_utf8((Erl_Atom_data*) &(x)->uval.refval.node)
#define ERL_REF_NUMBER(x) ((x)->uval.refval.n[0])
#define ERL_REF_NUMBERS(x) ((x)->uval.refval.n)
#define ERL_REF_LEN(x) ((x)->uval.refval.len)
@@ -183,14 +188,26 @@ typedef struct {
} Erl_Float;
typedef struct {
+ char *utf8;
+ int lenU;
+ char *latin1;
+ int lenL;
+} Erl_Atom_data;
+
+char* erl_atom_ptr_latin1(Erl_Atom_data*);
+char* erl_atom_ptr_utf8(Erl_Atom_data*);
+int erl_atom_size_latin1(Erl_Atom_data*);
+int erl_atom_size_utf8(Erl_Atom_data*);
+char* erl_atom_init_latin1(Erl_Atom_data*, const char*);
+
+typedef struct {
Erl_Header h;
- int len;
- char *a;
+ Erl_Atom_data d;
} Erl_Atom;
typedef struct {
Erl_Header h;
- char * node;
+ Erl_Atom_data node;
unsigned int number;
unsigned int serial;
unsigned char creation;
@@ -198,14 +215,14 @@ typedef struct {
typedef struct {
Erl_Header h;
- char * node;
+ Erl_Atom_data node;
unsigned int number;
unsigned char creation;
} Erl_Port;
typedef struct {
Erl_Header h;
- char * node;
+ Erl_Atom_data node;
int len;
unsigned int n[3];
unsigned char creation;
@@ -289,7 +306,7 @@ typedef struct _eterm {
} ETERM;
-#define MAXREGLEN 255 /* max length of registered (atom) name */
+#define MAXREGLEN (255*4) /* max length of registered (atom) name */
typedef struct {
int type; /* one of the message type constants in eiext.h */
@@ -409,6 +426,7 @@ unsigned char erl_ext_type(unsigned char*); /* Note: returned 'char' before R9C
unsigned char *erl_peek_ext(unsigned char*,int);
int erl_term_len(ETERM*);
+int cmp_latin1_vs_utf8(const char* sL, int lenL, const char* sU, int lenU);
/* -------------------------------------------------------------------- */
/* Wrappers around ei functions */
diff --git a/lib/erl_interface/src/Makefile b/lib/erl_interface/src/Makefile
index 5f0367bec1..03e2ce14f0 100644
--- a/lib/erl_interface/src/Makefile
+++ b/lib/erl_interface/src/Makefile
@@ -22,10 +22,11 @@
# FIXME let configure put in this last part TARGET
+include $(ERL_TOP)/make/output.mk
include $(ERL_TOP)/make/target.mk
debug opt shared purify quantify purecov gcov:
- $(MAKE) -f $(TARGET)/Makefile TYPE=$@
+ $(make_verbose)$(MAKE) -f $(TARGET)/Makefile TYPE=$@
clean depend docs release release_docs tests release_tests check:
- $(MAKE) -f $(TARGET)/Makefile $@
+ $(make_verbose)$(MAKE) -f $(TARGET)/Makefile $@
diff --git a/lib/erl_interface/src/Makefile.in b/lib/erl_interface/src/Makefile.in
index 661dbb68ac..ebacc1cee0 100644
--- a/lib/erl_interface/src/Makefile.in
+++ b/lib/erl_interface/src/Makefile.in
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -38,6 +38,8 @@ TARGET = @TARGET@
include ../vsn.mk
include $(TARGET)/eidefs.mk
+include $(ERL_TOP)/make/output.mk
+
USING_MINGW=@MIXED_CYGWIN_MINGW@
USING_MSYS_VC==@MIXED_MSYS_VC@
USING_CYGWIN_VC==@MIXED_MSYS_VC@
@@ -98,6 +100,12 @@ LD = @LD@
AR = @AR@
RANLIB = @RANLIB@
+ifeq ($(V),0)
+AR_FLAGS=rc
+else
+AR_FLAGS=rcv
+endif
+
INCFLAGS = -I. -I../include -Iconnect -Iencode -Idecode -Imisc -Iepmd \
-Iregistry -I$(TARGET)
@@ -552,16 +560,18 @@ distclean: clean
ifeq ($(findstring vxworks,$(TARGET)),vxworks)
$(TARGET)/config.h:
- echo "/* Generated by Makefile */" > $@
- echo "#define HAVE_STRERROR 1" >> $@
- echo "#define HAVE_SOCKLEN_T 1" >> $@
+ $(gen_verbose)
+ $(V_at)echo "/* Generated by Makefile */" > $@
+ $(V_at)echo "#define HAVE_STRERROR 1" >> $@
+ $(V_at)echo "#define HAVE_SOCKLEN_T 1" >> $@
endif
ifeq ($(findstring ose,$(TARGET)),ose)
$(TARGET)/config.h:
- echo "/* Generated by Makefile */" > $@
- echo "#define HAVE_STRERROR 1" >> $@
- echo "#define HAVE_SOCKLEN_T 1" >> $@
+ $(gen_verbose)
+ $(V_at)echo "/* Generated by Makefile */" > $@
+ $(V_at)echo "#define HAVE_STRERROR 1" >> $@
+ $(V_at)echo "#define HAVE_SOCKLEN_T 1" >> $@
endif
###########################################################################
@@ -569,19 +579,19 @@ endif
###########################################################################
$(ST_OBJDIR)/%.o: %.c
- $(CC) $(CFLAGS) -c $< -o $@
+ $(V_CC) $(CFLAGS) -c $< -o $@
$(MT_OBJDIR)/%.o: %.c
- $(CC) $(MTFLAG) $(CFLAGS) $(THR_DEFS) -c $< -o $@
+ $(V_CC) $(MTFLAG) $(CFLAGS) $(THR_DEFS) -c $< -o $@
$(MD_OBJDIR)/%.o: %.c
- $(CC) -MD $(CFLAGS) $(THR_DEFS) -c $< -o $@
+ $(V_CC) -MD $(CFLAGS) $(THR_DEFS) -c $< -o $@
$(MD_OBJDIR)/%.o: %.c
- $(CC) -MD $(CFLAGS) $(THR_DEFS) -c $< -o $@
+ $(V_CC) -MD $(CFLAGS) $(THR_DEFS) -c $< -o $@
$(MDD_OBJDIR)/%.o: %.c
- $(CC) -MDd $(CFLAGS) $(THR_DEFS) -c $< -o $@
+ $(V_CC) -MDd $(CFLAGS) $(THR_DEFS) -c $< -o $@
###########################################################################
# Create directories
@@ -598,67 +608,67 @@ ifeq ($(TARGET),win32)
# Windows archive creation
$(ST_EILIB) : $(ST_EIOBJECTS)
- $(AR) -out:$@ $(ST_EIOBJECTS)
- $(RANLIB) $@
+ $(V_AR) -out:$@ $(ST_EIOBJECTS)
+ $(V_RANLIB) $@
$(ST_ERLLIB) : $(ST_ERLOBJECTS)
- $(AR) -out:$@ $(ST_ERLOBJECTS)
- $(RANLIB) $@
+ $(V_AR) -out:$@ $(ST_ERLOBJECTS)
+ $(V_RANLIB) $@
$(MT_EILIB) : $(MT_EIOBJECTS)
- $(AR) -out:$@ $(MT_EIOBJECTS)
- $(RANLIB) $@
+ $(V_AR) -out:$@ $(MT_EIOBJECTS)
+ $(V_RANLIB) $@
$(MT_ERLLIB) : $(MT_ERLOBJECTS)
- $(AR) -out:$@ $(MT_ERLOBJECTS)
- $(RANLIB) $@
+ $(V_AR) -out:$@ $(MT_ERLOBJECTS)
+ $(V_RANLIB) $@
$(MD_EILIB) : $(MD_EIOBJECTS)
- $(AR) -out:$@ $(MD_EIOBJECTS)
- $(RANLIB) $@
+ $(V_AR) -out:$@ $(MD_EIOBJECTS)
+ $(V_RANLIB) $@
$(MD_ERLLIB) : $(MD_ERLOBJECTS)
- $(AR) -out:$@ $(MD_ERLOBJECTS)
- $(RANLIB) $@
+ $(V_AR) -out:$@ $(MD_ERLOBJECTS)
+ $(V_RANLIB) $@
$(MDD_EILIB) : $(MDD_EIOBJECTS)
- $(AR) -out:$@ $(MDD_EIOBJECTS)
- $(RANLIB) $@
+ $(V_AR) -out:$@ $(MDD_EIOBJECTS)
+ $(V_RANLIB) $@
$(MDD_ERLLIB) : $(MDD_ERLOBJECTS)
- $(AR) -out:$@ $(MDD_ERLOBJECTS)
- $(RANLIB) $@
+ $(V_AR) -out:$@ $(MDD_ERLOBJECTS)
+ $(V_RANLIB) $@
else
# Unix archive creation
$(ST_EILIB) : $(ST_EIOBJECTS)
- rm -f $@
- $(AR) rcv $@ $(ST_EIOBJECTS)
+ $(V_at)rm -f $@
+ $(V_AR) $(AR_FLAGS) $@ $(ST_EIOBJECTS)
ifdef RANLIB
- $(RANLIB) $@
+ $(V_RANLIB) $@
endif
$(ST_ERLLIB) : $(ST_ERLOBJECTS)
- rm -f $@
- $(AR) rcv $@ $(ST_ERLOBJECTS)
+ $(V_at)rm -f $@
+ $(V_AR) $(AR_FLAGS) $@ $(ST_ERLOBJECTS)
ifdef RANLIB
- $(RANLIB) $@
+ $(V_RANLIB) $@
endif
$(MT_EILIB) : $(MT_EIOBJECTS)
- rm -f $@
- $(AR) rcv $@ $(MT_EIOBJECTS)
+ $(V_at)rm -f $@
+ $(V_AR) $(AR_FLAGS) $@ $(MT_EIOBJECTS)
ifdef RANLIB
- $(RANLIB) $@
+ $(V_RANLIB) $@
endif
$(MT_ERLLIB) : $(MT_ERLOBJECTS)
- rm -f $@
- $(AR) rcv $@ $(MT_ERLOBJECTS)
+ $(V_at)rm -f $@
+ $(V_AR) $(AR_FLAGS) $@ $(MT_ERLOBJECTS)
ifdef RANLIB
- $(RANLIB) $@
+ $(V_RANLIB) $@
endif
endif
@@ -669,18 +679,18 @@ endif
ifeq ($(TARGET),win32)
$(ERL_CALL): $(ERLCALL) ../include/ei.h $(MD_EILIB)
- $(PURIFY) $(CC) -MD $(PROG_CFLAGS) $(THR_DEFS) -o $@ $(ERLCALL) \
+ $(ld_verbose)$(PURIFY) $(CC) -MD $(PROG_CFLAGS) $(THR_DEFS) -o $@ $(ERLCALL) \
-L$(OBJDIR) -lei_md $(THR_LIBS) $(LIBS) -lsocket
else
ifeq ($(findstring vxworks,$(TARGET)),vxworks)
$(ERL_CALL): $(ST_OBJDIR)/erl_call.o $(ST_OBJDIR)/erl_start.o ../include/ei.h $(ST_EILIB)
- $(LD) -r -d -o $@ $(ST_OBJDIR)/erl_call.o $(ST_OBJDIR)/erl_start.o -L$(OBJDIR) -lei $(LIBS)
+ $(V_LD) -r -d -o $@ $(ST_OBJDIR)/erl_call.o $(ST_OBJDIR)/erl_start.o -L$(OBJDIR) -lei $(LIBS)
$(ST_OBJDIR)/erl_call.o: prog/erl_call.c
- $(CC) $(CFLAGS) -c $< -o $@
+ $(V_CC) $(CFLAGS) -c $< -o $@
$(ST_OBJDIR)/erl_start.o: prog/erl_start.c
- $(CC) $(CFLAGS) -c $< -o $@
+ $(V_CC) $(CFLAGS) -c $< -o $@
else
ifeq ($(findstring ose,$(TARGET)),ose)
@@ -688,11 +698,11 @@ $(ERL_CALL):
else
ifdef THR_DEFS
$(ERL_CALL): $(ERLCALL) ../include/ei.h $(MT_EILIB)
- $(PURIFY) $(CC) $(PROG_CFLAGS) $(THR_DEFS) $(LDFLAGS) -o $@ $(ERLCALL) \
+ $(ld_verbose)$(PURIFY) $(CC) $(PROG_CFLAGS) $(THR_DEFS) $(LDFLAGS) -o $@ $(ERLCALL) \
-L$(OBJDIR) -lei $(THR_LIBS) $(LIBS)
else
$(ERL_CALL): $(ERLCALL) ../include/ei.h $(ST_EILIB)
- $(PURIFY) $(CC) $(PROG_CFLAGS) $(LDFLAGS) -o $@ $(ERLCALL) \
+ $(ld_verbose)$(PURIFY) $(CC) $(PROG_CFLAGS) $(LDFLAGS) -o $@ $(ERLCALL) \
-L$(OBJDIR) -lei $(LIBS)
endif
endif
@@ -707,36 +717,36 @@ check: $(FAKE_TARGETS)
ifndef THR_DEFS
$(ST_OBJDIR)/erl_fake_prog_st$(EXE): prog/erl_fake_prog.c $(ST_ERLLIB) $(ST_EILIB)
- $(CC) $(PROG_CFLAGS) -o $@ $< -L$(OBJDIR) -lerl_interface -lei \
+ $(V_CC) $(PROG_CFLAGS) -o $@ $< -L$(OBJDIR) -lerl_interface -lei \
$(LIBS)
$(ST_OBJDIR)/ei_fake_prog_st$(EXE): prog/ei_fake_prog.c $(ST_EILIB)
- $(CC) $(PROG_CFLAGS) -o $@ $< -L$(OBJDIR) -lei $(LIBS)
+ $(V_CC) $(PROG_CFLAGS) -o $@ $< -L$(OBJDIR) -lei $(LIBS)
$(ST_OBJDIR)/erl_fake_prog_cxx_st$(EXE): prog/erl_fake_prog.c \
$(ST_ERLLIB) $(ST_EILIB)
- $(CC) $(PROG_CFLAGS) -o $@ -xc++ $< -L$(OBJDIR) \
+ $(V_CC) $(PROG_CFLAGS) -o $@ -xc++ $< -L$(OBJDIR) \
-lerl_interface -lei $(LIBS)
$(ST_OBJDIR)/ei_fake_prog_cxx_st$(EXE): prog/ei_fake_prog.c $(ST_EILIB)
- $(CC) $(PROG_CFLAGS) -o $@ -xc++ $< -L$(OBJDIR) -lei $(LIBS)
+ $(V_CC) $(PROG_CFLAGS) -o $@ -xc++ $< -L$(OBJDIR) -lei $(LIBS)
else
$(ST_OBJDIR)/erl_fake_prog_st$(EXE): prog/erl_fake_prog.c $(ST_ERLLIB) $(ST_EILIB)
- $(CC) $(PROG_CFLAGS) -o $@ $< -L$(OBJDIR) -lerl_interface_st -lei_st \
+ $(V_CC) $(PROG_CFLAGS) -o $@ $< -L$(OBJDIR) -lerl_interface_st -lei_st \
$(LIBS)
$(ST_OBJDIR)/ei_fake_prog_st$(EXE): prog/ei_fake_prog.c $(ST_EILIB)
- $(CC) $(PROG_CFLAGS) -o $@ $< -L$(OBJDIR) -lei_st $(LIBS)
+ $(V_CC) $(PROG_CFLAGS) -o $@ $< -L$(OBJDIR) -lei_st $(LIBS)
$(ST_OBJDIR)/erl_fake_prog_cxx_st$(EXE): prog/erl_fake_prog.c \
$(ST_ERLLIB) $(ST_EILIB)
- $(CC) $(PROG_CFLAGS) -o $@ -xc++ $< -L$(OBJDIR) \
+ $(V_CC) $(PROG_CFLAGS) -o $@ -xc++ $< -L$(OBJDIR) \
-lerl_interface_st -lei_st $(LIBS)
$(ST_OBJDIR)/ei_fake_prog_cxx_st$(EXE): prog/ei_fake_prog.c $(ST_EILIB)
- $(CC) $(PROG_CFLAGS) -o $@ -xc++ $< -L$(OBJDIR) -lei_st $(LIBS)
+ $(V_CC) $(PROG_CFLAGS) -o $@ -xc++ $< -L$(OBJDIR) -lei_st $(LIBS)
endif
@@ -744,63 +754,63 @@ endif
$(MT_OBJDIR)/erl_fake_prog_mt$(EXE): prog/erl_fake_prog.c \
$(MT_ERLLIB) $(MT_EILIB)
- $(CC) $(MTFLAG) $(PROG_CFLAGS) $(THR_DEFS) -o $@ $< -L$(OBJDIR) \
+ $(V_CC) $(MTFLAG) $(PROG_CFLAGS) $(THR_DEFS) -o $@ $< -L$(OBJDIR) \
-lerl_interface -lei $(THR_LIBS) $(LIBS)
$(MT_OBJDIR)/ei_fake_prog_mt$(EXE): prog/ei_fake_prog.c $(MT_EILIB)
- $(CC) $(MTFLAG) $(PROG_CFLAGS) $(THR_DEFS) -o $@ $< \
+ $(V_CC) $(MTFLAG) $(PROG_CFLAGS) $(THR_DEFS) -o $@ $< \
-L$(OBJDIR) -lei $(THR_LIBS) $(LIBS)
$(MT_OBJDIR)/erl_fake_prog_mt_cxx$(EXE): prog/erl_fake_prog.c \
$(MT_ERLLIB) $(MT_EILIB)
- $(CC) $(MTFLAG) $(PROG_CFLAGS) $(THR_DEFS) -o $@ -xc++ $< \
+ $(V_CC) $(MTFLAG) $(PROG_CFLAGS) $(THR_DEFS) -o $@ -xc++ $< \
-L$(OBJDIR) -lerl_interface -lei \
$(THR_LIBS) $(LIBS)
$(MT_OBJDIR)/ei_fake_prog_mt_cxx$(EXE): prog/ei_fake_prog.c $(MT_EILIB)
- $(CC) $(MTFLAG) $(PROG_CFLAGS) $(THR_DEFS) -o $@ -xc++ $< \
+ $(V_CC) $(MTFLAG) $(PROG_CFLAGS) $(THR_DEFS) -o $@ -xc++ $< \
-L$(OBJDIR) -lei $(THR_LIBS) $(LIBS)
####
$(MD_OBJDIR)/erl_fake_prog_md$(EXE): prog/erl_fake_prog.c \
$(MD_ERLLIB) $(MD_EILIB)
- $(CC) -MD $(PROG_CFLAGS) $(THR_DEFS) -o $@ $< -L$(OBJDIR) \
+ $(V_CC) -MD $(PROG_CFLAGS) $(THR_DEFS) -o $@ $< -L$(OBJDIR) \
-lerl_interface_r -lei_r $(THR_LIBS) $(LIBS)
$(MD_OBJDIR)/ei_fake_prog_md$(EXE): prog/ei_fake_prog.c $(MD_EILIB)
- $(CC) -MD $(PROG_CFLAGS) $(THR_DEFS) -o $@ $< \
+ $(V_CC) -MD $(PROG_CFLAGS) $(THR_DEFS) -o $@ $< \
-L$(OBJDIR) -lei_r $(THR_LIBS) $(LIBS)
$(MD_OBJDIR)/erl_fake_prog_md_cxx$(EXE): prog/erl_fake_prog.c \
$(MD_ERLLIB) $(MD_EILIB)
- $(CC) -MD $(PROG_CFLAGS) $(THR_DEFS) -o $@ -xc++ $< \
+ $(V_CC) -MD $(PROG_CFLAGS) $(THR_DEFS) -o $@ -xc++ $< \
-L$(OBJDIR) -lerl_interface_r -lei_r \
$(THR_LIBS) $(LIBS)
$(MD_OBJDIR)/ei_fake_prog_md_cxx$(EXE): prog/ei_fake_prog.c $(MD_EILIB)
- $(CC) -MD $(PROG_CFLAGS) $(THR_DEFS) -o $@ -xc++ $< \
+ $(V_CC) -MD $(PROG_CFLAGS) $(THR_DEFS) -o $@ -xc++ $< \
-L$(OBJDIR) -lei_r $(THR_LIBS) $(LIBS)
####
$(MDD_OBJDIR)/erl_fake_prog_mdd$(EXE): prog/erl_fake_prog.c \
$(MDD_ERLLIB) $(MDD_EILIB)
- $(CC) -MDD $(PROG_CFLAGS) $(THR_DEFS) -o $@ $< -L$(OBJDIR) \
+ $(V_CC) -MDD $(PROG_CFLAGS) $(THR_DEFS) -o $@ $< -L$(OBJDIR) \
-lerl_interface_r -lei_r $(THR_LIBS) $(LIBS)
$(MDD_OBJDIR)/ei_fake_prog_mdd$(EXE): prog/ei_fake_prog.c $(MDD_EILIB)
- $(CC) -MDD $(PROG_CFLAGS) $(THR_DEFS) -o $@ $< \
+ $(V_CC) -MDD $(PROG_CFLAGS) $(THR_DEFS) -o $@ $< \
-L$(OBJDIR) -lei_r $(THR_LIBS) $(LIBS)
$(MDD_OBJDIR)/erl_fake_prog_mdd_cxx$(EXE): prog/erl_fake_prog.c \
$(MDD_ERLLIB) $(MDD_EILIB)
- $(CC) -MDD $(PROG_CFLAGS) $(THR_DEFS) -o $@ -xc++ $< \
+ $(V_CC) -MDD $(PROG_CFLAGS) $(THR_DEFS) -o $@ -xc++ $< \
-L$(OBJDIR) -lerl_interface_r -lei_r \
$(THR_LIBS) $(LIBS)
$(MDD_OBJDIR)/ei_fake_prog_mdd_cxx$(EXE): prog/ei_fake_prog.c $(MDD_EILIB)
- $(CC) -MDD $(PROG_CFLAGS) $(THR_DEFS) -o $@ -xc++ $< \
+ $(V_CC) -MDD $(PROG_CFLAGS) $(THR_DEFS) -o $@ -xc++ $< \
-L$(OBJDIR) -lei_r $(THR_LIBS) $(LIBS)
###########################################################################
@@ -808,9 +818,10 @@ $(MDD_OBJDIR)/ei_fake_prog_mdd_cxx$(EXE): prog/ei_fake_prog.c $(MDD_EILIB)
###########################################################################
depend:
- @echo "Generating dependency file depend.mk..."
+ $(gen_verbose)
+ $(V_colon)@echo "Generating dependency file depend.mk..."
@echo "# Generated dependency rules" > depend.mk; \
- $(CC) $(CFLAGS) -MM $(SOURCES) | \
+ $(V_CC) $(CFLAGS) -MM $(SOURCES) | \
sed 's&$(TARGET)&\$$\(TARGET\)&g' | \
sed 's/^.*:/\$$\(ST_OBJDIR\)\/&/' >> depend.mk; \
echo >> depend.mk; \
diff --git a/lib/erl_interface/src/connect/ei_connect.c b/lib/erl_interface/src/connect/ei_connect.c
index 34362b4b9f..4421bbb7fe 100644
--- a/lib/erl_interface/src/connect/ei_connect.c
+++ b/lib/erl_interface/src/connect/ei_connect.c
@@ -459,6 +459,7 @@ int ei_connect_xinit(ei_cnode* ec, const char *thishostname,
/* memmove(&ec->this_ipaddr, thisipaddr, sizeof(ec->this_ipaddr)); */
strcpy(ec->self.node,thisnodename);
+ ec->self.node_org_enc = ERLANG_LATIN1;
ec->self.num = 0;
ec->self.serial = 0;
ec->self.creation = creation;
@@ -1070,7 +1071,7 @@ int ei_rpc(ei_cnode* ec, int fd, char *mod, char *fun,
int i, index;
ei_term t;
erlang_msg msg;
- char rex[MAXATOMLEN+1];
+ char rex[MAXATOMLEN];
if (ei_rpc_to(ec, fd, mod, fun, inbuf, inbuflen) < 0) {
return -1;
@@ -1332,7 +1333,9 @@ static int send_name_or_challenge(int fd, char *nodename,
| DFLAG_EXTENDED_PIDS_PORTS
| DFLAG_FUN_TAGS
| DFLAG_NEW_FUN_TAGS
- | DFLAG_NEW_FLOATS));
+ | DFLAG_NEW_FLOATS
+ | DFLAG_SMALL_ATOM_TAGS
+ | DFLAG_UTF8_ATOMS));
if (f_chall)
put32be(s, challenge);
memcpy(s, nodename, strlen(nodename));
diff --git a/lib/erl_interface/src/connect/ei_connect_int.h b/lib/erl_interface/src/connect/ei_connect_int.h
index 3c42b49b82..81c384e38d 100644
--- a/lib/erl_interface/src/connect/ei_connect_int.h
+++ b/lib/erl_interface/src/connect/ei_connect_int.h
@@ -102,6 +102,8 @@ extern int h_errno;
#define DFLAG_NEW_FUN_TAGS 0x80
#define DFLAG_EXTENDED_PIDS_PORTS 0x100
#define DFLAG_NEW_FLOATS 0x800
+#define DFLAG_SMALL_ATOM_TAGS 0x4000
+#define DFLAG_UTF8_ATOMS 0x10000
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 ba8f8fbce3..79d259b92d 100644
--- a/lib/erl_interface/src/connect/ei_resolve.c
+++ b/lib/erl_interface/src/connect/ei_resolve.c
@@ -186,11 +186,11 @@ static int verify_dns_configuration(void)
* advance: increment buf by n bytes, reduce len by same amount .
*/
#if defined SIZEOF_VOID_P
-#define ALIGNBYTES (SIZEOF_VOID_P - 1)
+#define EI_ALIGNBYTES (SIZEOF_VOID_P - 1)
#else
-#define ALIGNBYTES (sizeof(void*) - 1)
+#define EI_ALIGNBYTES (sizeof(void*) - 1)
#endif
-#define align_buf(buf,len) for (;(((unsigned)buf) & ALIGNBYTES); (buf)++, len--)
+#define align_buf(buf,len) for (;(((unsigned)buf) & EI_ALIGNBYTES); (buf)++, len--)
#define advance_buf(buf,len,n) ((buf)+=(n),(len)-=(n))
/* "and now the tricky part..." */
@@ -282,6 +282,8 @@ static int copy_hostent(struct hostent *dest, const struct hostent *src, char *b
return 0;
}
+#undef EI_ALIGNBYTES
+
/* This function is a pseudo-reentrant version of gethostbyname(). It
* uses locks to serialize the call to the regular (non-reentrant)
* gethostbyname() and then copies the data into the user-provided
diff --git a/lib/erl_interface/src/connect/eirecv.c b/lib/erl_interface/src/connect/eirecv.c
index 86852f947d..075f78e3d2 100644
--- a/lib/erl_interface/src/connect/eirecv.c
+++ b/lib/erl_interface/src/connect/eirecv.c
@@ -108,7 +108,7 @@ ei_recv_internal (int fd,
switch (msg->msgtype) {
case ERL_SEND: /* { SEND, Cookie, ToPid } */
if (ei_tracelevel >= 4) show_this_msg = 1;
- if (ei_decode_atom(header,&index,msg->cookie)
+ if (ei_decode_atom_as(header,&index,msg->cookie,sizeof(msg->cookie),ERLANG_UTF8,NULL,NULL)
|| ei_decode_pid(header,&index,&msg->to))
{
erl_errno = EIO;
@@ -120,8 +120,8 @@ ei_recv_internal (int fd,
case ERL_REG_SEND: /* { REG_SEND, From, Cookie, ToName } */
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))
+ || ei_decode_atom_as(header,&index,msg->cookie,sizeof(msg->cookie),ERLANG_UTF8,NULL,NULL)
+ || ei_decode_atom_as(header,&index,msg->toname,sizeof(msg->toname),ERLANG_UTF8,NULL,NULL))
{
erl_errno = EIO;
return -1;
@@ -157,7 +157,7 @@ ei_recv_internal (int fd,
case ERL_SEND_TT: /* { SEND_TT, Cookie, ToPid, TraceToken } */
if (ei_tracelevel >= 4) show_this_msg = 1;
- if (ei_decode_atom(header,&index,msg->cookie)
+ if (ei_decode_atom_as(header,&index,msg->cookie,sizeof(msg->cookie),ERLANG_UTF8,NULL,NULL)
|| ei_decode_pid(header,&index,&msg->to)
|| ei_decode_trace(header,&index,&msg->token))
{
@@ -171,8 +171,8 @@ ei_recv_internal (int fd,
case ERL_REG_SEND_TT: /* { REG_SEND_TT, From, Cookie, ToName, TraceToken } */
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)
+ || ei_decode_atom_as(header,&index,msg->cookie,sizeof(msg->cookie),ERLANG_UTF8,NULL,NULL)
+ || ei_decode_atom_as(header,&index,msg->toname,sizeof(msg->toname),ERLANG_UTF8,NULL,NULL)
|| ei_decode_trace(header,&index,&msg->token))
{
erl_errno = EIO;
diff --git a/lib/erl_interface/src/decode/decode_atom.c b/lib/erl_interface/src/decode/decode_atom.c
index c2e6a0426e..9779ad3f35 100644
--- a/lib/erl_interface/src/decode/decode_atom.c
+++ b/lib/erl_interface/src/decode/decode_atom.c
@@ -21,24 +21,155 @@
#include "eiext.h"
#include "putget.h"
+
int ei_decode_atom(const char *buf, int *index, char *p)
{
- const char *s = buf + *index;
- const char *s0 = s;
- int len;
+ return ei_decode_atom_as(buf, index, p, MAXATOMLEN, ERLANG_LATIN1, NULL, NULL);
+}
+
+int ei_decode_atom_as(const char *buf, int *index, char* p, int destlen,
+ enum erlang_char_encoding want_enc,
+ enum erlang_char_encoding* was_encp,
+ enum erlang_char_encoding* res_encp)
+{
+ const char *s = buf + *index;
+ const char *s0 = s;
+ int len;
+ enum erlang_char_encoding got_enc;
+
+ switch (get8(s)) {
+ case ERL_ATOM_EXT:
+ len = get16be(s);
+ got_enc = ERLANG_LATIN1;
+ break;
+ case ERL_SMALL_ATOM_EXT:
+ len = get8(s);
+ got_enc = ERLANG_LATIN1;
+ break;
+ case ERL_ATOM_UTF8_EXT:
+ len = get16be(s);
+ got_enc = ERLANG_UTF8;
+ break;
+ case ERL_SMALL_ATOM_UTF8_EXT:
+ len = get8(s);
+ got_enc = ERLANG_UTF8;
+ break;
+ default:
+ return -1;
+ }
+
+ if ((want_enc & got_enc) || want_enc == ERLANG_ASCII) {
+ int i, found_non_ascii = 0;
+ if (len >= destlen)
+ return -1;
+ for (i=0; i<len; i++) {
+ if (s[i] & 0x80) found_non_ascii = 1;
+ if (p) p[i] = s[i];
+ }
+ if (p) p[len] = 0;
+ if (want_enc == ERLANG_ASCII && found_non_ascii) {
+ return -1;
+ }
+ if (res_encp) {
+ *res_encp = found_non_ascii ? got_enc : ERLANG_ASCII;
+ }
+ }
+ else {
+ int plen = (got_enc == ERLANG_LATIN1) ?
+ latin1_to_utf8(p, s, len, destlen-1, res_encp) :
+ utf8_to_latin1(p, s, len, destlen-1, res_encp);
+ if (plen < 0) return -1;
+ if (p) p[plen] = 0;
+ }
+ if (was_encp) {
+ *was_encp = got_enc;
+ }
+
+ s += len;
+ *index += s-s0;
+ return 0;
+}
- if (get8(s) != ERL_ATOM_EXT) return -1;
- len = get16be(s);
+int utf8_to_latin1(char* dst, const char* src, int slen, int destlen,
+ enum erlang_char_encoding* res_encp)
+{
+ const char* const dst_start = dst;
+ const char* const dst_end = dst + destlen;
+ int found_non_ascii = 0;
+
+ while (slen > 0) {
+ if (dst >= dst_end) return -1;
+ if ((src[0] & 0x80) == 0) {
+ if (dst_start) {
+ *dst = *src;
+ }
+ ++dst;
+ ++src;
+ --slen;
+ }
+ else if (slen > 1 &&
+ (src[0] & 0xFE) == 0xC2 &&
+ (src[1] & 0xC0) == 0x80) {
+ if (dst_start) {
+ *dst = (char) ((src[0] << 6) | (src[1] & 0x3F));
+ }
+ ++dst;
+ src += 2;
+ slen -= 2;
+ found_non_ascii = 1;
+ }
+ else return -1;
+ }
+ if (res_encp) {
+ *res_encp = found_non_ascii ? ERLANG_LATIN1 : ERLANG_ASCII;
+ }
+ return dst - dst_start;
+}
- if (len > MAXATOMLEN) return -1;
+int latin1_to_utf8(char* dst, const char* src, int slen, int destlen,
+ enum erlang_char_encoding* res_encp)
+{
+ const char* const src_end = src + slen;
+ const char* const dst_start = dst;
+ const char* const dst_end = dst + destlen;
+ int found_non_ascii = 0;
- if (p) {
- memmove(p,s,len);
- p[len] = (char)0;
- }
- s += len;
- *index += s-s0;
-
- return 0;
+ while (src < src_end) {
+ if (dst >= dst_end) return -1;
+ if ((src[0] & 0x80) == 0) {
+ if (dst_start) {
+ *dst = *src;
+ }
+ ++dst;
+ }
+ else {
+ if (dst_start) {
+ unsigned char ch = *src;
+ dst[0] = 0xC0 | (ch >> 6);
+ dst[1] = 0x80 | (ch & 0x3F);
+ }
+ dst += 2;
+ found_non_ascii = 1;
+ }
+ ++src;
+ }
+ if (res_encp) {
+ *res_encp = found_non_ascii ? ERLANG_UTF8 : ERLANG_ASCII;
+ }
+ return dst - dst_start;
}
+
+
+
+int ei_internal_get_atom(const char** bufp, char* p,
+ enum erlang_char_encoding* was_encp)
+{
+ int ix = 0;
+ if (ei_decode_atom_as(*bufp, &ix, p, MAXATOMLEN_UTF8, ERLANG_UTF8, was_encp, NULL) < 0)
+ return -1;
+ *bufp += ix;
+ return 0;
+}
+
+
diff --git a/lib/erl_interface/src/decode/decode_boolean.c b/lib/erl_interface/src/decode/decode_boolean.c
index 9fd09c63f1..f20690249b 100644
--- a/lib/erl_interface/src/decode/decode_boolean.c
+++ b/lib/erl_interface/src/decode/decode_boolean.c
@@ -24,34 +24,20 @@
/* c non-zero -> erlang "true" atom, otherwise "false" */
int ei_decode_boolean(const char *buf, int *index, int *p)
{
- const char *s = buf + *index;
- const char *s0 = s;
- int len;
+ char tbuf[6];
int t;
- if (get8(s) != ERL_ATOM_EXT) return -1;
+ if (ei_decode_atom_as(buf, index, tbuf, sizeof(tbuf), ERLANG_ASCII, NULL, NULL) < 0)
+ return -1;
- len = get16be(s);
-
- switch (len) {
- case 4:
- /* typecast makes ansi happy */
- if (strncmp((char*)s,"true",4)) return -1;
- t = 1;
- break;
-
- case 5:
- if (strncmp((char*)s,"false",5)) return -1;
- t = 0;
- break;
-
- default:
- return -1;
- }
-
- s += len;
+ if (memcmp(tbuf, "true", 5) == 0)
+ t = 1;
+ else if (memcmp(tbuf, "false", 6) == 0)
+ t = 0;
+ else
+ return -1;
+
if (p) *p = t;
- *index += s-s0;
-
return 0;
}
+
diff --git a/lib/erl_interface/src/decode/decode_fun.c b/lib/erl_interface/src/decode/decode_fun.c
index 64fb9e86d8..7bbef5db44 100644
--- a/lib/erl_interface/src/decode/decode_fun.c
+++ b/lib/erl_interface/src/decode/decode_fun.c
@@ -42,7 +42,8 @@ int ei_decode_fun(const char *buf, int *index, erlang_fun *p)
if (ei_decode_pid(s, &ix, (p == NULL ? (erlang_pid*)NULL : &p->pid)) < 0)
return -1;
/* then the module (atom) */
- if (ei_decode_atom(s, &ix, (p == NULL ? (char*)NULL : p->module)) < 0)
+ if (ei_decode_atom_as(s, &ix, (p == NULL ? (char*)NULL : p->module),
+ MAXATOMLEN_UTF8, ERLANG_UTF8, &p->module_org_enc, NULL) < 0)
return -1;
/* then the index */
if (ei_decode_long(s, &ix, (p == NULL ? (long*)NULL : &p->index)) < 0)
@@ -84,7 +85,8 @@ int ei_decode_fun(const char *buf, int *index, erlang_fun *p)
if (p != NULL) p->n_free_vars = i;
/* then the module (atom) */
ix = 0;
- if (ei_decode_atom(s, &ix, (p == NULL ? (char*)NULL : p->module)) < 0)
+ if (ei_decode_atom_as(s, &ix, (p == NULL ? (char*)NULL : p->module),
+ MAXATOMLEN_UTF8, ERLANG_UTF8, &p->module_org_enc, NULL) < 0)
return -1;
/* then the old_index */
if (ei_decode_long(s, &ix, (p == NULL ? (long*)NULL : &p->old_index)) < 0)
diff --git a/lib/erl_interface/src/decode/decode_pid.c b/lib/erl_interface/src/decode/decode_pid.c
index 9ed1c36db6..e79952195d 100644
--- a/lib/erl_interface/src/decode/decode_pid.c
+++ b/lib/erl_interface/src/decode/decode_pid.c
@@ -21,26 +21,16 @@
#include "eiext.h"
#include "putget.h"
+
int ei_decode_pid(const char *buf, int *index, erlang_pid *p)
{
const char *s = buf + *index;
const char *s0 = s;
- int len;
if (get8(s) != ERL_PID_EXT) return -1;
/* first 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);
- p->node[len] = (char)0;
- }
- s += len;
+ if (get_atom(&s, p->node, &p->node_org_enc) < 0) return -1;
/* now the numbers: num (4), serial (4), creation (1) */
if (p) {
diff --git a/lib/erl_interface/src/decode/decode_port.c b/lib/erl_interface/src/decode/decode_port.c
index 28abed801a..5fd96b51a4 100644
--- a/lib/erl_interface/src/decode/decode_port.c
+++ b/lib/erl_interface/src/decode/decode_port.c
@@ -25,22 +25,11 @@ int ei_decode_port(const char *buf, int *index, erlang_port *p)
{
const char *s = buf + *index;
const char *s0 = s;
- int len;
if (get8(s) != ERL_PORT_EXT) return -1;
/* first 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);
- p->node[len] = (char)0;
- }
- s += len;
+ if (get_atom(&s, p->node, &p->node_org_enc) < 0) return -1;
/* now the numbers: num (4), creation (1) */
if (p) {
diff --git a/lib/erl_interface/src/decode/decode_ref.c b/lib/erl_interface/src/decode/decode_ref.c
index 7b15808bc5..7294e5d239 100644
--- a/lib/erl_interface/src/decode/decode_ref.c
+++ b/lib/erl_interface/src/decode/decode_ref.c
@@ -21,27 +21,18 @@
#include "eiext.h"
#include "putget.h"
+
int ei_decode_ref(const char *buf, int *index, erlang_ref *p)
{
const char *s = buf + *index;
const char *s0 = s;
- int count, len, i;
+ int count, i;
switch (get8(s)) {
case ERL_REFERENCE_EXT:
- /* first 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);
- p->node[len] = (char)0;
- }
- s += len;
+ /* nodename */
+ if (get_atom(&s, p->node, &p->node_org_enc) < 0) return -1;
/* now the numbers: num (4), creation (1) */
if (p) {
@@ -62,15 +53,7 @@ int ei_decode_ref(const char *buf, int *index, erlang_ref *p)
if (p) p->len = count;
/* 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);
- p->node[len] = (char)0;
- }
- s += len;
+ if (get_atom(&s, p->node, &p->node_org_enc) < 0) return -1;
/* creation */
if (p) {
@@ -95,3 +78,4 @@ int ei_decode_ref(const char *buf, int *index, erlang_ref *p)
return -1;
}
}
+
diff --git a/lib/erl_interface/src/encode/encode_atom.c b/lib/erl_interface/src/encode/encode_atom.c
index 6f41f045e0..044f17cb60 100644
--- a/lib/erl_interface/src/encode/encode_atom.c
+++ b/lib/erl_interface/src/encode/encode_atom.c
@@ -22,29 +22,108 @@
#include "eiext.h"
#include "putget.h"
+
+static int copy_ascii_atom(char* dst, const char* src, int slen);
+static int copy_utf8_atom(char* dst, const char* src, int slen);
+
+
int ei_encode_atom(char *buf, int *index, const char *p)
{
size_t len = strlen(p);
- if (len >= INT_MAX) return -1;
- return ei_encode_atom_len(buf, index, p, len);
+ if (len >= MAXATOMLEN)
+ len = MAXATOMLEN - 1;
+ return ei_encode_atom_len_as(buf, index, p, len, ERLANG_LATIN1, ERLANG_LATIN1);
}
int ei_encode_atom_len(char *buf, int *index, const char *p, int len)
{
+ /* This function is documented to truncate at MAXATOMLEN (256) */
+ if (len >= MAXATOMLEN)
+ len = MAXATOMLEN - 1;
+ return ei_encode_atom_len_as(buf, index, p, len, ERLANG_LATIN1, ERLANG_LATIN1);
+}
+
+int ei_encode_atom_as(char *buf, int *index, const char *p,
+ enum erlang_char_encoding from_enc,
+ enum erlang_char_encoding to_enc)
+{
+ return ei_encode_atom_len_as(buf, index, p, strlen(p), from_enc, to_enc);
+}
+
+int ei_encode_atom_len_as(char *buf, int *index, const char *p, int len,
+ enum erlang_char_encoding from_enc,
+ enum erlang_char_encoding to_enc)
+{
char *s = buf + *index;
char *s0 = s;
+ int offs;
- /* This function is documented to truncate at MAXATOMLEN (256) */
- if (len > MAXATOMLEN)
- len = MAXATOMLEN;
+ if (len >= MAXATOMLEN && (from_enc & (ERLANG_LATIN1|ERLANG_ASCII))) {
+ return -1;
+ }
- if (!buf) s += 3;
- else {
- put8(s,ERL_ATOM_EXT);
- put16be(s,len);
+ switch(to_enc) {
+ case ERLANG_LATIN1:
+ if (buf) {
+ put8(s,ERL_ATOM_EXT);
+ switch (from_enc) {
+ case ERLANG_UTF8:
+ len = utf8_to_latin1(s+2, p, len, MAXATOMLEN-1, NULL);
+ if (len < 0) return -1;
+ break;
+ case ERLANG_ASCII:
+ if (copy_ascii_atom(s+2, p, len) < 0) return -1;
+ break;
+ case ERLANG_LATIN1:
+ memcpy(s+2, p, len);
+ break;
+ default:
+ return -1;
+ }
+ put16be(s,len);
+ }
+ else {
+ s += 3;
+ if (from_enc == ERLANG_UTF8) {
+ len = utf8_to_latin1(NULL, p, len, MAXATOMLEN-1, NULL);
+ if (len < 0) return -1;
+ }
+ }
+ break;
+
+ case ERLANG_UTF8:
+ offs = 1 + 1;
+ switch (from_enc) {
+ case ERLANG_LATIN1:
+ if (len >= 256/2) offs++;
+ len = latin1_to_utf8((buf ? s+offs : NULL), p, len, MAXATOMLEN_UTF8-1, NULL);
+ break;
+ case ERLANG_ASCII:
+ if (buf && copy_ascii_atom(s+offs, p, len) < 0) return -1;
+ break;
+ case ERLANG_UTF8:
+ if (len >= 256) offs++;
+ if (buf && copy_utf8_atom(s+offs, p, len) < 0) return -1;
+ break;
+ default:
+ return -1;
+ }
+ if (buf) {
+ if (offs == 2) {
+ put8(s, ERL_SMALL_ATOM_UTF8_EXT);
+ put8(s, len);
+ }
+ else {
+ put8(s, ERL_ATOM_UTF8_EXT);
+ put16be(s, len);
+ }
+ }
+ else s+= offs;
+ break;
- memmove(s,p,len); /* unterminated string */
+ default:
+ return -1;
}
s += len;
@@ -53,3 +132,58 @@ int ei_encode_atom_len(char *buf, int *index, const char *p, int len)
return 0;
}
+int
+ei_internal_put_atom(char** bufp, const char* p, int slen,
+ enum erlang_char_encoding to_enc)
+{
+ int ix = 0;
+ if (ei_encode_atom_len_as(*bufp, &ix, p, slen, ERLANG_UTF8, to_enc) < 0)
+ return -1;
+ *bufp += ix;
+ return 0;
+}
+
+
+int copy_ascii_atom(char* dst, const char* src, int slen)
+{
+ while (slen > 0) {
+ if ((src[0] & 0x80) != 0) return -1;
+ *dst++ = *src++;
+ slen--;
+ }
+ return 0;
+}
+
+int copy_utf8_atom(char* dst, const char* src, int slen)
+{
+ int num_chars = 0;
+
+ while (slen > 0) {
+ if (++num_chars >= MAXATOMLEN) return -1;
+ if ((src[0] & 0x80) != 0) {
+ if ((src[0] & 0xE0) == 0xC0) {
+ if (slen < 2 || (src[1] & 0xC0) != 0x80) return -1;
+ *dst++ = *src++;
+ slen--;
+ }
+ else if ((src[0] & 0xF0) == 0xE0) {
+ if (slen < 3 || (src[1] & 0xC0) != 0x80 || (src[2] & 0xC0) != 0x80) return -1;
+ *dst++ = *src++;
+ *dst++ = *src++;
+ slen -= 2;
+ }
+ else if ((src[0] & 0xF8) == 0xF0) {
+ if (slen < 4 || (src[1] & 0xC0) != 0x80 || (src[2] & 0xC0) != 0x80 || (src[3] & 0xC0) != 0x80) return -1;
+ *dst++ = *src++;
+ *dst++ = *src++;
+ *dst++ = *src++;
+ slen -= 3;
+ }
+ else return -1;
+ }
+ *dst++ = *src++;
+ slen--;
+ }
+ return 0;
+}
+
diff --git a/lib/erl_interface/src/encode/encode_fun.c b/lib/erl_interface/src/encode/encode_fun.c
index 54ee2083d6..4daee32648 100644
--- a/lib/erl_interface/src/encode/encode_fun.c
+++ b/lib/erl_interface/src/encode/encode_fun.c
@@ -35,7 +35,7 @@ int ei_encode_fun(char *buf, int *index, const erlang_fun *p)
ix += sizeof(char) + 4;
if (ei_encode_pid(buf, &ix, &p->pid) < 0)
return -1;
- if (ei_encode_atom(buf, &ix, p->module) < 0)
+ if (ei_encode_atom_as(buf, &ix, p->module, ERLANG_UTF8, p->module_org_enc) < 0)
return -1;
if (ei_encode_long(buf, &ix, p->index) < 0)
return -1;
@@ -60,7 +60,7 @@ int ei_encode_fun(char *buf, int *index, const erlang_fun *p)
} else
size_p = NULL;
ix += 1 + 4 + 1 + sizeof(p->md5) + 4 + 4;
- if (ei_encode_atom(buf, &ix, p->module) < 0)
+ if (ei_encode_atom_as(buf, &ix, p->module, ERLANG_UTF8, p->module_org_enc) < 0)
return -1;
if (ei_encode_long(buf, &ix, p->old_index) < 0)
return -1;
diff --git a/lib/erl_interface/src/encode/encode_pid.c b/lib/erl_interface/src/encode/encode_pid.c
index ee7f235c17..0cf3ef4efb 100644
--- a/lib/erl_interface/src/encode/encode_pid.c
+++ b/lib/erl_interface/src/encode/encode_pid.c
@@ -24,29 +24,23 @@
int ei_encode_pid(char *buf, int *index, const erlang_pid *p)
{
char *s = buf + *index;
- char *s0 = s;
- int len = strlen(p->node);
-
- if (!buf) s += 13 + len;
- else {
- put8(s,ERL_PID_EXT);
- /* first the nodename */
- put8(s,ERL_ATOM_EXT);
+ ++(*index); /* skip ERL_PID_EXT */
+ if (ei_encode_atom_len_as(buf, index, p->node, strlen(p->node), ERLANG_UTF8, p->node_org_enc) < 0)
+ return -1;
+
+ if (buf) {
+ put8(s,ERL_PID_EXT);
- put16be(s,len);
-
- memmove(s, p->node, len);
- s += len;
+ s = buf + *index;
/* now the integers */
put32be(s,p->num & 0x7fff); /* 15 bits */
put32be(s,p->serial & 0x1fff); /* 13 bits */
put8(s,(p->creation & 0x03)); /* 2 bits */
}
-
- *index += s-s0;
-
+
+ *index += 4 + 4 + 1;
return 0;
}
diff --git a/lib/erl_interface/src/encode/encode_port.c b/lib/erl_interface/src/encode/encode_port.c
index fbbb33182e..2bf9e26d78 100644
--- a/lib/erl_interface/src/encode/encode_port.c
+++ b/lib/erl_interface/src/encode/encode_port.c
@@ -24,28 +24,23 @@
int ei_encode_port(char *buf, int *index, const erlang_port *p)
{
char *s = buf + *index;
- char *s0 = s;
- int len = strlen(p->node);
-
- if (!buf) s += 9 + len;
- else {
- put8(s,ERL_PORT_EXT);
- /* first the nodename */
- put8(s,ERL_ATOM_EXT);
+ ++(*index); /* skip ERL_PORT_EXT */
+ if (ei_encode_atom_len_as(buf, index, p->node, strlen(p->node), ERLANG_UTF8,
+ p->node_org_enc) < 0) {
+ return -1;
+ }
+ if (buf) {
+ put8(s,ERL_PORT_EXT);
- put16be(s,len);
-
- memmove(s, p->node, len);
- s += len;
+ s = buf + *index;
/* now the integers */
put32be(s,p->id & 0x0fffffff /* 28 bits */);
put8(s,(p->creation & 0x03));
}
- *index += s-s0;
-
+ *index += 4 + 1;
return 0;
}
diff --git a/lib/erl_interface/src/encode/encode_ref.c b/lib/erl_interface/src/encode/encode_ref.c
index 292b452864..e8b3173315 100644
--- a/lib/erl_interface/src/encode/encode_ref.c
+++ b/lib/erl_interface/src/encode/encode_ref.c
@@ -24,36 +24,32 @@
int ei_encode_ref(char *buf, int *index, const erlang_ref *p)
{
char *s = buf + *index;
- char *s0 = s;
- int len = strlen(p->node);
int i;
+ (*index) += 1 + 2; /* skip to node atom */
+ if (ei_encode_atom_len_as(buf, index, p->node, strlen(p->node), ERLANG_UTF8,
+ p->node_org_enc) < 0) {
+ return -1;
+ }
+
/* Always encode as an extended reference; all participating parties
are now expected to be able to decode extended references. */
- if (!buf) s += 1 + 2 + (3+len) + p->len*4 + 1;
- else {
+ if (buf) {
put8(s,ERL_NEW_REFERENCE_EXT);
/* first, number of integers */
put16be(s, p->len);
/* then the nodename */
- put8(s,ERL_ATOM_EXT);
-
- put16be(s,len);
-
- memmove(s, p->node, len);
- s += len;
+ s = buf + *index;
/* now the integers */
put8(s,(p->creation & 0x03));
for (i = 0; i < p->len; i++)
put32be(s,p->n[i]);
-
- }
-
- *index += s-s0;
+ }
+ *index += p->len*4 + 1;
return 0;
}
diff --git a/lib/erl_interface/src/legacy/erl_connect.c b/lib/erl_interface/src/legacy/erl_connect.c
index 41d4fa3138..f82704ea8b 100644
--- a/lib/erl_interface/src/legacy/erl_connect.c
+++ b/lib/erl_interface/src/legacy/erl_connect.c
@@ -125,7 +125,7 @@ static ei_cnode erl_if_ec;
int erl_connect_init(int this_node_number, char *cookie, short creation)
{
- char nn[MAXATOMLEN+1];
+ char nn[MAXATOMLEN];
sprintf(nn, "c%d", this_node_number);
@@ -247,9 +247,15 @@ int erl_send(int fd, ETERM *to ,ETERM *msg)
erl_errno = EINVAL;
return -1;
}
-
- strncpy(topid.node, (char *)ERL_PID_NODE(to), sizeof(topid.node));
- topid.node[sizeof(topid.node)-1] = '\0';
+
+ if (to->uval.pidval.node.latin1) {
+ strcpy(topid.node, to->uval.pidval.node.latin1);
+ topid.node_org_enc = ERLANG_LATIN1;
+ }
+ else {
+ strcpy(topid.node, to->uval.pidval.node.utf8);
+ topid.node_org_enc = ERLANG_UTF8;
+ }
topid.num = ERL_PID_NUMBER(to);
topid.serial = ERL_PID_SERIAL(to);
topid.creation = ERL_PID_CREATION(to);
@@ -263,7 +269,7 @@ static int erl_do_receive_msg(int fd, ei_x_buff* x, ErlMessage* emsg)
erlang_msg msg;
int r;
- msg.from.node[0] = msg.to.node[0] = '\0';
+ msg.from.node[0] = msg.to.node[0] = msg.toname[0] = '\0';
r = ei_do_receive_msg(fd, 0, &msg, x, 0);
if (r == ERL_MSG) {
@@ -299,7 +305,7 @@ static int erl_do_receive_msg(int fd, ei_x_buff* x, ErlMessage* emsg)
emsg->to = erl_mk_pid(msg.to.node, msg.to.num, msg.to.serial, msg.to.creation);
else
emsg->to = NULL;
- memcpy(emsg->to_name, msg.toname, MAXATOMLEN+1);
+ strcpy(emsg->to_name, msg.toname);
return r;
}
diff --git a/lib/erl_interface/src/legacy/erl_eterm.c b/lib/erl_interface/src/legacy/erl_eterm.c
index 8d559f0f55..aa0fd5ddcf 100644
--- a/lib/erl_interface/src/legacy/erl_eterm.c
+++ b/lib/erl_interface/src/legacy/erl_eterm.c
@@ -36,6 +36,7 @@
#include "erl_error.h"
#include "erl_internal.h"
#include "ei_internal.h"
+#include "putget.h"
#define ERL_IS_BYTE(x) (ERL_IS_INTEGER(x) && (ERL_INT_VALUE(x) & ~0xFF) == 0)
@@ -142,9 +143,7 @@ ETERM *erl_mk_atom (const char *s)
ep = erl_alloc_eterm(ERL_ATOM);
ERL_COUNT(ep) = 1;
- ERL_ATOM_SIZE(ep) = strlen(s);
- if ((ERL_ATOM_PTR(ep) = strsave(s)) == NULL)
- {
+ if (erl_atom_init_latin1(&ep->uval.aval.d, s) == NULL) {
erl_free_term(ep);
erl_errno = ENOMEM;
return NULL;
@@ -152,6 +151,65 @@ ETERM *erl_mk_atom (const char *s)
return ep;
}
+char* erl_atom_ptr_latin1(Erl_Atom_data* a)
+{
+ if (a->latin1 == NULL) {
+ enum erlang_char_encoding enc;
+ a->lenL = utf8_to_latin1(NULL, a->utf8, a->lenU, a->lenU, &enc);
+ if (a->lenL < 0) {
+ a->lenL = 0;
+ return NULL;
+ }
+ if (enc == ERLANG_ASCII) {
+ a->latin1 = a->utf8;
+ }
+ else {
+ a->latin1 = malloc(a->lenL+1);
+ utf8_to_latin1(a->latin1, a->utf8, a->lenU, a->lenL, NULL);
+ a->latin1[a->lenL] = '\0';
+ }
+ }
+ return a->latin1;
+}
+
+char* erl_atom_ptr_utf8(Erl_Atom_data* a)
+{
+ if (a->utf8 == NULL) {
+ int dlen = a->lenL * 2; /* over estimation */
+ a->utf8 = malloc(dlen + 1);
+ a->lenU = latin1_to_utf8(a->utf8, a->latin1, a->lenL, dlen, NULL);
+ a->utf8[a->lenU] = '\0';
+ }
+ return a->utf8;
+
+}
+int erl_atom_size_latin1(Erl_Atom_data* a)
+{
+ if (a->latin1 == NULL) {
+ erl_atom_ptr_latin1(a);
+ }
+ return a->lenL;
+}
+int erl_atom_size_utf8(Erl_Atom_data* a)
+{
+ if (a->utf8 == NULL) {
+ erl_atom_ptr_utf8(a);
+ }
+ return a->lenU;
+}
+char* erl_atom_init_latin1(Erl_Atom_data* a, const char* s)
+{
+ a->lenL = strlen(s);
+ if ((a->latin1 = strsave(s)) == NULL)
+ {
+ return NULL;
+ }
+ a->utf8 = NULL;
+ a->lenU = 0;
+ return a->latin1;
+}
+
+
/*
* Given a string as input, creates a list.
*/
@@ -208,12 +266,19 @@ ETERM *erl_mk_pid(const char *node,
ep = erl_alloc_eterm(ERL_PID);
ERL_COUNT(ep) = 1;
- if ((ERL_PID_NODE(ep) = strsave(node)) == NULL)
+ if (erl_atom_init_latin1(&ep->uval.pidval.node, node) == NULL)
{
erl_free_term(ep);
erl_errno = ENOMEM;
return NULL;
}
+ erl_mk_pid_helper(ep, number, serial, creation);
+ return ep;
+}
+
+void erl_mk_pid_helper(ETERM *ep, unsigned int number,
+ unsigned int serial, unsigned char creation)
+{
ERL_PID_NUMBER(ep) = number & 0x7fff; /* 15 bits */
if (ei_internal_use_r9_pids_ports()) {
ERL_PID_SERIAL(ep) = serial & 0x07; /* 3 bits */
@@ -222,7 +287,6 @@ ETERM *erl_mk_pid(const char *node,
ERL_PID_SERIAL(ep) = serial & 0x1fff; /* 13 bits */
}
ERL_PID_CREATION(ep) = creation & 0x03; /* 2 bits */
- return ep;
}
/*
@@ -239,12 +303,18 @@ ETERM *erl_mk_port(const char *node,
ep = erl_alloc_eterm(ERL_PORT);
ERL_COUNT(ep) = 1;
- if ((ERL_PORT_NODE(ep) = strsave(node)) == NULL)
+ if (erl_atom_init_latin1(&ep->uval.portval.node, node) == NULL)
{
erl_free_term(ep);
erl_errno = ENOMEM;
return NULL;
}
+ erl_mk_port_helper(ep, number, creation);
+ return ep;
+}
+
+void erl_mk_port_helper(ETERM* ep, unsigned number, unsigned char creation)
+{
if (ei_internal_use_r9_pids_ports()) {
ERL_PORT_NUMBER(ep) = number & 0x3ffff; /* 18 bits */
}
@@ -252,29 +322,29 @@ ETERM *erl_mk_port(const char *node,
ERL_PORT_NUMBER(ep) = number & 0x0fffffff; /* 18 bits */
}
ERL_PORT_CREATION(ep) = creation & 0x03; /* 2 bits */
- return ep;
}
/*
* Create any kind of reference.
*/
-ETERM *__erl_mk_reference (const char *node,
+ETERM *__erl_mk_reference (ETERM* t,
+ const char *node,
size_t len,
unsigned int n[],
unsigned char creation)
{
- ETERM * t;
-
- if (node == NULL) return NULL;
-
- t = erl_alloc_eterm(ERL_REF);
- ERL_COUNT(t) = 1;
-
- if ((ERL_REF_NODE(t) = strsave(node)) == NULL)
- {
- erl_free_term(t);
- erl_errno = ENOMEM;
- return NULL;
+ if (t == NULL) {
+ if (node == NULL) return NULL;
+
+ t = erl_alloc_eterm(ERL_REF);
+ ERL_COUNT(t) = 1;
+
+ if (erl_atom_init_latin1(&t->uval.refval.node, node) == NULL)
+ {
+ erl_free_term(t);
+ erl_errno = ENOMEM;
+ return NULL;
+ }
}
ERL_REF_LEN(t) = len;
ERL_REF_NUMBERS(t)[0] = n[0] & 0x3ffff; /* 18 bits */
@@ -294,7 +364,7 @@ ETERM *erl_mk_ref (const char *node,
{
unsigned int n[3] = {0, 0, 0};
n[0] = number;
- return __erl_mk_reference(node, 1, n, creation);
+ return __erl_mk_reference(NULL, node, 1, n, creation);
}
/*
@@ -307,7 +377,7 @@ erl_mk_long_ref (const char *node,
{
unsigned int n[3] = {0, 0, 0};
n[0] = n3; n[1] = n2; n[2] = n1;
- return __erl_mk_reference(node, 3, n, creation);
+ return __erl_mk_reference(NULL, node, 3, n, creation);
}
/*
@@ -758,6 +828,28 @@ int erl_iolist_length (const ETERM* term)
return -1;
}
+static int erl_atom_copy(Erl_Atom_data* dst, const Erl_Atom_data* src)
+{
+ if (src->latin1 == src->utf8) {
+ dst->latin1 = dst->utf8 = strsave(src->latin1);
+ dst->lenL = dst->lenU = strlen(src->latin1);
+ }
+ else if (src->latin1) {
+ dst->latin1 = strsave(src->latin1);
+ dst->lenL = strlen(src->latin1);
+ dst->utf8 = NULL;
+ dst->lenU = 0;
+ }
+ else {
+ dst->utf8 = strsave(src->utf8);
+ dst->lenU = strlen(src->utf8);
+ dst->latin1 = NULL;
+ dst->lenL = 0;
+ }
+ return (dst->latin1 != NULL || dst->utf8 == NULL);
+}
+
+
/*
* Return a brand NEW COPY of an ETERM.
*/
@@ -796,9 +888,7 @@ ETERM *erl_copy_term(const ETERM *ep)
ERL_FLOAT_VALUE(cp) = ERL_FLOAT_VALUE(ep);
break;
case ERL_ATOM:
- ERL_ATOM_SIZE(cp) = ERL_ATOM_SIZE(ep);
- ERL_ATOM_PTR(cp) = strsave(ERL_ATOM_PTR(ep));
- if (ERL_ATOM_PTR(cp) == NULL)
+ if (!erl_atom_copy(&cp->uval.aval.d, &ep->uval.aval.d))
{
erl_free_term(cp);
erl_errno = ENOMEM;
@@ -810,17 +900,17 @@ ETERM *erl_copy_term(const ETERM *ep)
name and plug in. Somewhat ugly (also done with port and
ref below). */
memcpy(&cp->uval.pidval, &ep->uval.pidval, sizeof(Erl_Pid));
- ERL_PID_NODE(cp) = strsave(ERL_PID_NODE(ep));
+ erl_atom_copy(&cp->uval.pidval.node, &ep->uval.pidval.node);
ERL_COUNT(cp) = 1;
break;
case ERL_PORT:
memcpy(&cp->uval.portval, &ep->uval.portval, sizeof(Erl_Port));
- ERL_PORT_NODE(cp) = strsave(ERL_PORT_NODE(ep));
+ erl_atom_copy(&cp->uval.portval.node, &ep->uval.portval.node);
ERL_COUNT(cp) = 1;
break;
case ERL_REF:
memcpy(&cp->uval.refval, &ep->uval.refval, sizeof(Erl_Ref));
- ERL_REF_NODE(cp) = strsave(ERL_REF_NODE(ep));
+ erl_atom_copy(&cp->uval.refval.node, &ep->uval.refval.node);
ERL_COUNT(cp) = 1;
break;
case ERL_LIST:
@@ -883,29 +973,29 @@ int erl_print_term(FILE *fp, const ETERM *ep)
j = i = doquote = 0;
switch(ERL_TYPE(ep))
{
- case ERL_ATOM:
+ case ERL_ATOM: {
+ char* adata = ERL_ATOM_PTR(ep);
/* FIXME: what if some weird locale is in use? */
- if (!islower((int)ERL_ATOM_PTR(ep)[0]))
+ if (!islower(adata[0]))
doquote = 1;
for (i = 0; !doquote && i < ERL_ATOM_SIZE(ep); i++)
{
- doquote = !(isalnum((int)ERL_ATOM_PTR(ep)[i])
- || (ERL_ATOM_PTR(ep)[i] == '_'));
+ doquote = !(isalnum(adata[i]) || (adata[i] == '_'));
}
if (doquote) {
putc('\'', fp);
ch_written++;
}
- fputs(ERL_ATOM_PTR(ep), fp);
+ fputs(adata, fp);
ch_written += ERL_ATOM_SIZE(ep);
if (doquote) {
putc('\'', fp);
ch_written++;
}
break;
-
+ }
case ERL_VARIABLE:
if (!isupper((int)ERL_VAR_NAME(ep)[0])) {
doquote = 1;
diff --git a/lib/erl_interface/src/legacy/erl_eterm.h b/lib/erl_interface/src/legacy/erl_eterm.h
index 41b008f04f..2e8129d9cd 100644
--- a/lib/erl_interface/src/legacy/erl_eterm.h
+++ b/lib/erl_interface/src/legacy/erl_eterm.h
@@ -55,7 +55,9 @@ typedef struct _heapmark {
} Erl_HeapMark;
-ETERM * __erl_mk_reference(const char *, size_t, unsigned int n[], unsigned char);
+void erl_mk_port_helper(ETERM* ep, unsigned number, unsigned char creation);
+void erl_mk_pid_helper(ETERM*, unsigned,unsigned, unsigned char);
+ETERM * __erl_mk_reference(ETERM*, const char *, size_t, unsigned int n[], unsigned char);
int erl_current_fix_desc(void);
#endif /* _ERL_ETERM_H */
diff --git a/lib/erl_interface/src/legacy/erl_format.c b/lib/erl_interface/src/legacy/erl_format.c
index dc85806c36..533241e396 100644
--- a/lib/erl_interface/src/legacy/erl_format.c
+++ b/lib/erl_interface/src/legacy/erl_format.c
@@ -574,10 +574,22 @@ static int ematch(ETERM *p, ETERM *t)
switch (type_p) {
- case ERL_ATOM:
- return p->uval.aval.len == t->uval.aval.len &&
- memcmp(p->uval.aval.a, t->uval.aval.a, p->uval.aval.len) == 0;
-
+ case ERL_ATOM: {
+ Erl_Atom_data* pa = &p->uval.aval.d;
+ Erl_Atom_data* ta = &t->uval.aval.d;
+ if (pa->utf8 && ta->utf8) {
+ return pa->lenU == ta->lenU && memcmp(pa->utf8, ta->utf8, pa->lenU)==0;
+ }
+ else if (pa->latin1 && ta->latin1) {
+ return pa->lenL == ta->lenL && memcmp(pa->latin1, ta->latin1, pa->lenL)==0;
+ }
+ else if (pa->latin1) {
+ return cmp_latin1_vs_utf8(pa->latin1, pa->lenL, ta->utf8, ta->lenU)==0;
+ }
+ else {
+ return cmp_latin1_vs_utf8(ta->latin1, ta->lenL, pa->utf8, pa->lenU)==0;
+ }
+ }
case ERL_VARIABLE:
if (strcmp(p->uval.vval.name, "_") == 0) /* anon. variable */
return ERL_TRUE;
diff --git a/lib/erl_interface/src/legacy/erl_malloc.c b/lib/erl_interface/src/legacy/erl_malloc.c
index f51a6c69b3..d09239e02d 100644
--- a/lib/erl_interface/src/legacy/erl_malloc.c
+++ b/lib/erl_interface/src/legacy/erl_malloc.c
@@ -112,6 +112,18 @@ do { \
(ptr) = NULL; \
} while (0)
+static void erl_atom_free(Erl_Atom_data* p)
+{
+ erl_free(p->latin1);
+ if (p->utf8 != p->latin1) {
+ erl_free(p->utf8);
+ }
+ p->latin1 = NULL;
+ p->utf8 = NULL;
+ p->lenL = 0;
+ p->lenU = 0;
+}
+
static void _erl_free_term (ETERM *ep, int external, int compound)
{
restart:
@@ -122,7 +134,7 @@ restart:
switch(ERL_TYPE(ep))
{
case ERL_ATOM:
- FREE_AND_CLEAR(ERL_ATOM_PTR(ep));
+ erl_atom_free(&ep->uval.aval.d);
break;
case ERL_VARIABLE:
FREE_AND_CLEAR(ERL_VAR_NAME(ep));
@@ -161,13 +173,13 @@ restart:
FREE_AND_CLEAR(ERL_BIN_PTR(ep));
break;
case ERL_PID:
- FREE_AND_CLEAR(ERL_PID_NODE(ep));
+ erl_atom_free(&ep->uval.pidval.node);
break;
case ERL_PORT:
- FREE_AND_CLEAR(ERL_PORT_NODE(ep));
+ erl_atom_free(&ep->uval.portval.node);
break;
case ERL_REF:
- FREE_AND_CLEAR(ERL_REF_NODE(ep));
+ erl_atom_free(&ep->uval.refval.node);
break;
case ERL_EMPTY_LIST:
case ERL_INTEGER:
diff --git a/lib/erl_interface/src/legacy/erl_marshal.c b/lib/erl_interface/src/legacy/erl_marshal.c
index dad715c762..4c45cebb02 100644
--- a/lib/erl_interface/src/legacy/erl_marshal.c
+++ b/lib/erl_interface/src/legacy/erl_marshal.c
@@ -44,6 +44,9 @@ int erl_fp_compare(unsigned *a, unsigned *b);
static void erl_long_to_fp(long l, unsigned *d);
#endif
+static int cmpbytes(unsigned char* s1,int l1,unsigned char* s2,int l2);
+static int cmpatoms(unsigned char* s1, int l1, unsigned char tag1, unsigned char* s2, int l2, unsigned char tag2);
+
/* Used when comparing two encoded byte arrays */
/* this global data is ok (from threading point of view) since it is
* initialized once and never changed
@@ -51,7 +54,13 @@ static void erl_long_to_fp(long l, unsigned *d);
#define CMP_ARRAY_SIZE 256
/* FIXME problem for threaded ? */
-static char cmp_array[CMP_ARRAY_SIZE];
+
+static enum
+{
+ ERL_NUM_CMP=1, ERL_ATOM_CMP, ERL_REF_CMP, ERL_FUN_CMP, ERL_PORT_CMP,
+ ERL_PID_CMP, ERL_TUPLE_CMP, ERL_NIL_CMP, ERL_LIST_CMP, ERL_BIN_CMP
+}cmp_array[CMP_ARRAY_SIZE];
+
static int init_cmp_array_p=1; /* initialize array, the first time */
#if defined(VXWORKS) && CPU == PPC860
@@ -69,10 +78,8 @@ static int init_cmp_array_p=1; /* initialize array, the first time */
static int cmp_floats(double f1, double f2);
static INLINE double to_float(long l);
-#define ERL_NUM_CMP 1
-#define ERL_REF_CMP 3
-
#define IS_ERL_NUM(t) (cmp_array[t]==ERL_NUM_CMP)
+#define IS_ERL_ATOM(t) (cmp_array[t]==ERL_ATOM_CMP)
#define CMP_NUM_CLASS_SIZE 256
static unsigned char cmp_num_class[CMP_NUM_CLASS_SIZE];
@@ -100,25 +107,28 @@ void erl_init_marshal(void)
{
if (init_cmp_array_p) {
memset(cmp_array, 0, CMP_ARRAY_SIZE);
- 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;
- cmp_array[ERL_REFERENCE_EXT] = 3;
- cmp_array[ERL_NEW_REFERENCE_EXT] = 3;
- cmp_array[ERL_FUN_EXT] = 4;
- cmp_array[ERL_NEW_FUN_EXT] = 4;
- cmp_array[ERL_PORT_EXT] = 5;
- cmp_array[ERL_PID_EXT] = 6;
- cmp_array[ERL_SMALL_TUPLE_EXT] = 7;
- cmp_array[ERL_LARGE_TUPLE_EXT] = 7;
- cmp_array[ERL_NIL_EXT] = 8;
- cmp_array[ERL_STRING_EXT] = 9;
- cmp_array[ERL_LIST_EXT] = 9;
- cmp_array[ERL_BINARY_EXT] = 10;
+ cmp_array[ERL_SMALL_INTEGER_EXT] = ERL_NUM_CMP;
+ cmp_array[ERL_INTEGER_EXT] = ERL_NUM_CMP;
+ cmp_array[ERL_FLOAT_EXT] = ERL_NUM_CMP;
+ cmp_array[NEW_FLOAT_EXT] = ERL_NUM_CMP;
+ cmp_array[ERL_SMALL_BIG_EXT] = ERL_NUM_CMP;
+ cmp_array[ERL_LARGE_BIG_EXT] = ERL_NUM_CMP;
+ cmp_array[ERL_ATOM_EXT] = ERL_ATOM_CMP;
+ cmp_array[ERL_ATOM_UTF8_EXT] = ERL_ATOM_CMP;
+ cmp_array[ERL_SMALL_ATOM_EXT] = ERL_ATOM_CMP;
+ cmp_array[ERL_SMALL_ATOM_UTF8_EXT] = ERL_ATOM_CMP;
+ cmp_array[ERL_REFERENCE_EXT] = ERL_REF_CMP;
+ cmp_array[ERL_NEW_REFERENCE_EXT] = ERL_REF_CMP;
+ cmp_array[ERL_FUN_EXT] = ERL_FUN_CMP;
+ cmp_array[ERL_NEW_FUN_EXT] = ERL_FUN_CMP;
+ cmp_array[ERL_PORT_EXT] = ERL_PORT_CMP;
+ cmp_array[ERL_PID_EXT] = ERL_PID_CMP;
+ cmp_array[ERL_SMALL_TUPLE_EXT] = ERL_TUPLE_CMP;
+ cmp_array[ERL_LARGE_TUPLE_EXT] = ERL_TUPLE_CMP;
+ cmp_array[ERL_NIL_EXT] = ERL_NIL_CMP;
+ cmp_array[ERL_STRING_EXT] = ERL_LIST_CMP;
+ cmp_array[ERL_LIST_EXT] = ERL_LIST_CMP;
+ cmp_array[ERL_BINARY_EXT] = ERL_BIN_CMP;
init_cmp_array_p = 0;
}
if (init_cmp_num_class_p) {
@@ -156,6 +166,21 @@ static int erl_length_x(const ETERM *ep) {
*==============================================================
*/
+static void encode_atom(Erl_Atom_data* a, unsigned char **ext)
+{
+ int ix = 0;
+ if (a->latin1) {
+ ei_encode_atom_len_as((char*)*ext, &ix, a->latin1, a->lenL,
+ ERLANG_LATIN1, ERLANG_LATIN1);
+ }
+ else if (ei_encode_atom_len_as((char*)*ext, &ix, a->utf8, a->lenU,
+ ERLANG_UTF8, ERLANG_LATIN1) < 0) {
+ ei_encode_atom_len_as((char*)*ext, &ix, a->utf8, a->lenU,
+ ERLANG_UTF8, ERLANG_UTF8);
+ }
+ *ext += ix;
+}
+
/*
* The actual ENCODE engine.
* Returns 0 on success, otherwise 1.
@@ -170,12 +195,7 @@ int erl_encode_it(ETERM *ep, unsigned char **ext, int dist)
switch(ERL_TYPE(ep))
{
case ERL_ATOM:
- i = ep->uval.aval.len;
- *(*ext)++ = ERL_ATOM_EXT;
- *(*ext)++ = (i >>8) &0xff;
- *(*ext)++ = i &0xff;
- memcpy((void *) *ext, (const void *) ep->uval.aval.a, i);
- *ext += i;
+ encode_atom(&ep->uval.aval.d, ext);
return 0;
case ERL_INTEGER:
@@ -286,12 +306,7 @@ int erl_encode_it(ETERM *ep, unsigned char **ext, int dist)
case ERL_PID:
*(*ext)++ = ERL_PID_EXT;
/* First poke in node as an atom */
- i = strlen((char *)ERL_PID_NODE(ep));
- *(*ext)++ = ERL_ATOM_EXT;
- *(*ext)++ = (i >>8) &0xff;
- *(*ext)++ = i &0xff;
- memcpy(*ext, ERL_PID_NODE(ep), i);
- *ext += i;
+ encode_atom(&ep->uval.pidval.node, ext);
/* And then fill in the integer fields */
i = ERL_PID_NUMBER(ep);
*(*ext)++ = (i >> 24) &0xff;
@@ -319,11 +334,8 @@ int erl_encode_it(ETERM *ep, unsigned char **ext, int dist)
*(*ext)++ = (len >> 8) &0xff;
*(*ext)++ = len &0xff;
- *(*ext)++ = ERL_ATOM_EXT;
- *(*ext)++ = (i >> 8) &0xff;
- *(*ext)++ = i &0xff;
- memcpy(*ext, ERL_REF_NODE(ep), i);
- *ext += i;
+ encode_atom(&ep->uval.refval.node, ext);
+
*(*ext)++ = ERL_REF_CREATION(ep);
/* Then the integer fields */
for (j = 0; j < ERL_REF_LEN(ep); j++) {
@@ -338,12 +350,7 @@ int erl_encode_it(ETERM *ep, unsigned char **ext, int dist)
case ERL_PORT:
*(*ext)++ = ERL_PORT_EXT;
/* First poke in node as an atom */
- i = strlen((char *)ERL_PORT_NODE(ep));
- *(*ext)++ = ERL_ATOM_EXT;
- *(*ext)++ = (i >>8) &0xff;
- *(*ext)++ = i &0xff;
- memcpy(*ext, ERL_PORT_NODE(ep), i);
- *ext += i;
+ encode_atom(&ep->uval.portval.node, ext);
/* Then the integer fields */
i = ERL_PORT_NUMBER(ep);
*(*ext)++ = (i >> 24) &0xff;
@@ -494,6 +501,16 @@ int erl_term_len(ETERM *ep)
return 1+erl_term_len_helper(ep, 4);
}
+static int atom_len_helper(Erl_Atom_data* a)
+{
+ if (erl_atom_ptr_latin1(a)) {
+ return 1 + 2 + a->lenL; /* ERL_ATOM_EXT */
+ }
+ else {
+ return 1 + 1 + (a->lenU > 255) + a->lenU;
+ }
+}
+
static int erl_term_len_helper(ETERM *ep, int dist)
{
int len = 0;
@@ -505,8 +522,7 @@ static int erl_term_len_helper(ETERM *ep, int dist)
if (ep) {
switch (ERL_TYPE(ep)) {
case ERL_ATOM:
- i = ep->uval.aval.len;
- len = i + 3;
+ len = atom_len_helper(&ep->uval.aval.d);
break;
case ERL_INTEGER:
@@ -538,20 +554,15 @@ static int erl_term_len_helper(ETERM *ep, int dist)
break;
case ERL_PID:
- /* 1 + N + 4 + 4 + 1 where N = 3 + strlen */
- i = strlen((char *)ERL_PID_NODE(ep));
- len = 13 + i;
+ len = 1 + atom_len_helper(&ep->uval.pidval.node) + 4 + 4 + 1;
break;
case ERL_REF:
- i = strlen((char *)ERL_REF_NODE(ep));
- len = 1 + 2 + (i+3) + 1 + ERL_REF_LEN(ep) * 4;
+ len = 1 + 2 + atom_len_helper(&ep->uval.refval.node) + 1 + ERL_REF_LEN(ep) * 4;
break;
case ERL_PORT:
- /* 1 + N + 4 + 1 where N = 3 + strlen */
- i = strlen((char *)ERL_PORT_NODE(ep));
- len = 9 + i;
+ len = 1 + atom_len_helper(&ep->uval.portval.node) + 4 + 1;
break;
case ERL_EMPTY_LIST:
@@ -644,31 +655,36 @@ int erl_encode_buf(ETERM *ep, unsigned char **ext)
} /* erl_encode_buf */
-/*
- * A nice macro to make it look cleaner in the
- * cases of PID's,PORT's and REF's below.
- * It reads the NODE name from a buffer.
- */
-#define READ_THE_NODE(ext,cp,len,i) \
-/* eat first atom, repr. the node */ \
-if (**ext != ERL_ATOM_EXT) \
- return (ETERM *) NULL; \
-*ext += 1; \
-i = (**ext << 8) | (*ext)[1]; \
-cp = (char *) *(ext) + 2; \
-*ext += (i + 2); \
-len = i
-
-#define STATIC_NODE_BUF_SZ 30
-
-#define SET_NODE(node,node_buf,cp,len) \
-if (len >= STATIC_NODE_BUF_SZ) node = erl_malloc(len+1); \
-else node = node_buf; \
-memcpy(node, cp, len); \
-node[len] = '\0'
-
-#define RESET_NODE(node,len) \
-if (len >= STATIC_NODE_BUF_SZ) free(node)
+
+static int read_atom(unsigned char** ext, Erl_Atom_data* a)
+{
+ char buf[MAXATOMLEN_UTF8];
+ int offs = 0;
+ enum erlang_char_encoding enc;
+ int ret = ei_decode_atom_as((char*)*ext, &offs, buf, MAXATOMLEN_UTF8,
+ ERLANG_LATIN1|ERLANG_UTF8, NULL, &enc);
+ *ext += offs;
+
+ if (ret == 0) {
+ int i = strlen(buf);
+ char* clone = erl_malloc(i+1);
+ memcpy(clone, buf, i+1);
+
+ a->latin1 = NULL;
+ a->lenL = 0;
+ a->utf8 = NULL;
+ a->lenU = 0;
+ if (enc & (ERLANG_LATIN1 | ERLANG_ASCII)) {
+ a->latin1 = clone;
+ a->lenL = i;
+ }
+ if (enc & (ERLANG_UTF8 | ERLANG_ASCII)) {
+ a->utf8 = clone;
+ a->lenU = i;
+ }
+ }
+ return ret;
+}
/*
* The actual DECODE engine.
@@ -679,13 +695,13 @@ static ETERM *erl_decode_it(unsigned char **ext)
char *cp;
ETERM *ep,*tp,*np;
unsigned int u,sign;
- int i,j,len,arity;
+ int i,j,arity;
double ff;
/* Assume we are going to decode an integer */
ep = erl_alloc_eterm(ERL_INTEGER);
ERL_COUNT(ep) = 1;
-
+
switch (*(*ext)++)
{
case ERL_INTEGER_EXT:
@@ -774,138 +790,90 @@ static ETERM *erl_decode_it(unsigned char **ext)
return ep;
case ERL_ATOM_EXT:
+ case ERL_SMALL_ATOM_EXT:
+ case ERL_ATOM_UTF8_EXT:
+ case ERL_SMALL_ATOM_UTF8_EXT:
+
ERL_TYPE(ep) = ERL_ATOM;
- i = (**ext << 8) | (*ext)[1];
- cp = (char *) *(ext) + 2;
- *ext += (i + 2);
- ep->uval.aval.len = i;
- ep->uval.aval.a = (char *) erl_malloc(i+1);
- memcpy(ep->uval.aval.a, cp, i);
- ep->uval.aval.a[i]='\0';
+ --(*ext);
+ if (read_atom(ext, &ep->uval.aval.d) < 0) return NULL;
return ep;
case ERL_PID_EXT:
- erl_free_term(ep);
- { /* Why not use the constructors? */
- char *node;
- char node_buf[STATIC_NODE_BUF_SZ];
+ {
unsigned int number, serial;
unsigned char creation;
- ETERM *eterm_p;
- READ_THE_NODE(ext,cp,len,i);
- SET_NODE(node,node_buf,cp,len);
+ ERL_TYPE(ep) = ERL_PID;
+ if (read_atom(ext, &ep->uval.pidval.node) < 0) return NULL;
/* get the integers */
-#if 0
- /* FIXME: Remove code or whatever....
- Ints on the wire are big-endian (== network byte order)
- so use ntoh[sl]. (But some are little-endian! Arrrgh!)
- Also, the libc authors can be expected to optimize them
- heavily. However, the marshalling makes no guarantees
- about alignments -- so it won't work at all. */
- number = ntohl(*((unsigned int *)*ext)++);
- serial = ntohl(*((unsigned int *)*ext)++);
-#else
number = ((*ext)[0] << 24) | ((*ext)[1]) << 16 |
((*ext)[2]) << 8 | ((*ext)[3]);
*ext += 4;
serial = ((*ext)[0] << 24) | ((*ext)[1]) << 16 |
((*ext)[2]) << 8 | ((*ext)[3]);
*ext += 4;
-#endif
creation = *(*ext)++;
- eterm_p = erl_mk_pid(node, number, serial, creation);
- RESET_NODE(node,len);
- return eterm_p;
+ erl_mk_pid_helper(ep, number, serial, creation);
+ return ep;
}
case ERL_REFERENCE_EXT:
- erl_free_term(ep);
{
- char *node;
- char node_buf[STATIC_NODE_BUF_SZ];
- unsigned int number;
+ unsigned int n[3] = {0, 0, 0};
unsigned char creation;
- ETERM *eterm_p;
- READ_THE_NODE(ext,cp,len,i);
- SET_NODE(node,node_buf,cp,len);
+ ERL_TYPE(ep) = ERL_REF;
+ if (read_atom(ext, &ep->uval.refval.node) < 0) return NULL;
/* get the integers */
-#if 0
- number = ntohl(*((unsigned int *)*ext)++);
-#else
- number = ((*ext)[0] << 24) | ((*ext)[1]) << 16 |
+ n[0] = ((*ext)[0] << 24) | ((*ext)[1]) << 16 |
((*ext)[2]) << 8 | ((*ext)[3]);
*ext += 4;
-#endif
creation = *(*ext)++;
- eterm_p = erl_mk_ref(node, number, creation);
- RESET_NODE(node,len);
- return eterm_p;
+ __erl_mk_reference(ep, NULL, 1, n, creation);
+ return ep;
}
case ERL_NEW_REFERENCE_EXT:
- erl_free_term(ep);
{
- char *node;
- char node_buf[STATIC_NODE_BUF_SZ];
size_t cnt, i;
unsigned int n[3];
unsigned char creation;
- ETERM *eterm_p;
-#if 0
- cnt = ntohs(*((unsigned short *)*ext)++);
-#else
+ ERL_TYPE(ep) = ERL_REF;
cnt = ((*ext)[0] << 8) | (*ext)[1];
*ext += 2;
-#endif
- READ_THE_NODE(ext,cp,len,i);
- SET_NODE(node,node_buf,cp,len);
+ if (read_atom(ext, &ep->uval.refval.node) < 0) return NULL;
/* get the integers */
creation = *(*ext)++;
for(i = 0; i < cnt; i++)
{
-#if 0
- n[i] = ntohl(*((unsigned int *)*ext)++);
-#else
n[i] = ((*ext)[0] << 24) | ((*ext)[1]) << 16 |
((*ext)[2]) << 8 | ((*ext)[3]);
*ext += 4;
-#endif
}
- eterm_p = __erl_mk_reference(node, cnt, n, creation);
- RESET_NODE(node,len);
- return eterm_p;
+ __erl_mk_reference(ep, NULL, cnt, n, creation);
+ return ep;
}
case ERL_PORT_EXT:
- erl_free_term(ep);
{
- char *node;
- char node_buf[STATIC_NODE_BUF_SZ];
unsigned int number;
unsigned char creation;
- ETERM *eterm_p;
- READ_THE_NODE(ext,cp,len,i);
- SET_NODE(node,node_buf,cp,len);
+ ERL_TYPE(ep) = ERL_PORT;
+ if (read_atom(ext, &ep->uval.portval.node) < 0) return NULL;
/* get the integers */
-#if 0
- number = ntohl(*((unsigned int *)*ext)++);
-#else
number = ((*ext)[0] << 24) | ((*ext)[1]) << 16 |
((*ext)[2]) << 8 | ((*ext)[3]);
*ext += 4;
-#endif
creation = *(*ext)++;
- eterm_p = erl_mk_port(node, number, creation);
- RESET_NODE(node,len);
- return eterm_p;
+ erl_mk_port_helper(ep, number, creation);
+ return ep;
}
case ERL_NIL_EXT:
@@ -1140,6 +1108,9 @@ unsigned char erl_ext_type(unsigned char *ext)
case ERL_INTEGER_EXT:
return ERL_INTEGER;
case ERL_ATOM_EXT:
+ case ERL_ATOM_UTF8_EXT:
+ case ERL_SMALL_ATOM_EXT:
+ case ERL_SMALL_ATOM_UTF8_EXT:
return ERL_ATOM;
case ERL_PID_EXT:
return ERL_PID;
@@ -1191,6 +1162,9 @@ int erl_ext_size(unsigned char *t)
case ERL_SMALL_INTEGER_EXT:
case ERL_INTEGER_EXT:
case ERL_ATOM_EXT:
+ case ERL_ATOM_UTF8_EXT:
+ case ERL_SMALL_ATOM_EXT:
+ case ERL_SMALL_ATOM_UTF8_EXT:
case ERL_PID_EXT:
case ERL_PORT_EXT:
case ERL_REFERENCE_EXT:
@@ -1229,15 +1203,32 @@ int erl_ext_size(unsigned char *t)
} /* ext_size */
-/*
- * A nice macro that eats up the atom pointed to.
- */
-#define JUMP_ATOM(ext,i) \
-if (**ext != ERL_ATOM_EXT) \
- return 0; \
-*ext += 1; \
-i = (**ext << 8) | (*ext)[1]; \
-*ext += (i + 2)
+
+static int jump_atom(unsigned char** ext)
+{
+ unsigned char* e = *ext;
+ int len;
+
+ switch (*e++) {
+ case ERL_ATOM_EXT:
+ case ERL_ATOM_UTF8_EXT:
+ len = (e[0] << 8) | e[1];
+ e += (len + 2);
+ break;
+
+ case ERL_SMALL_ATOM_EXT:
+ case ERL_SMALL_ATOM_UTF8_EXT:
+ len = e[0];
+ e += (len + 1);
+ break;
+
+ default:
+ return 0;
+ }
+ *ext = e;
+ return 1;
+}
+
/*
* MOVE the POINTER PAST the ENCODED ETERM we
@@ -1259,25 +1250,27 @@ static int jump(unsigned char **ext)
*ext += 1;
break;
case ERL_ATOM_EXT:
- i = (**ext << 8) | (*ext)[1];
- *ext += (i + 2);
+ case ERL_ATOM_UTF8_EXT:
+ case ERL_SMALL_ATOM_EXT:
+ case ERL_SMALL_ATOM_UTF8_EXT:
+ jump_atom(ext);
break;
case ERL_PID_EXT:
/* eat first atom */
- JUMP_ATOM(ext,i);
+ if (!jump_atom(ext)) return 0;
*ext += 9; /* Two int's and the creation field */
break;
case ERL_REFERENCE_EXT:
case ERL_PORT_EXT:
/* first field is an atom */
- JUMP_ATOM(ext,i);
+ if (!jump_atom(ext)) return 0;
*ext += 5; /* One int and the creation field */
break;
case ERL_NEW_REFERENCE_EXT:
n = (**ext << 8) | (*ext)[1];
*ext += 2;
/* first field is an atom */
- JUMP_ATOM(ext,i);
+ if (!jump_atom(ext)) return 0;
*ext += 4*n+1;
break;
case ERL_NIL_EXT:
@@ -1425,6 +1418,58 @@ static int cmpbytes(unsigned char* s1,int l1,unsigned char* s2,int l2)
} /* cmpbytes */
+#define tag2enc(T) ((T)==ERL_ATOM_EXT || (T)==ERL_SMALL_ATOM_EXT ? ERLANG_LATIN1 : ERLANG_UTF8)
+
+static int cmpatoms(unsigned char* s1, int l1, unsigned char tag1,
+ unsigned char* s2, int l2, unsigned char tag2)
+{
+ enum erlang_char_encoding enc1 = tag2enc(tag1);
+ enum erlang_char_encoding enc2 = tag2enc(tag2);
+
+ if (enc1 == enc2) {
+ return cmpbytes(s1, l1,s2,l2);
+ }
+
+ if (enc1 == ERLANG_LATIN1) {
+ return cmp_latin1_vs_utf8((char*)s1, l1, (char*)s2, l2);
+ }
+ else {
+ return -cmp_latin1_vs_utf8((char*)s2, l2, (char*)s1, l1);
+ }
+}
+
+int cmp_latin1_vs_utf8(const char* strL, int lenL, const char* strU, int lenU)
+{
+ unsigned char* sL = (unsigned char*)strL;
+ unsigned char* sU = (unsigned char*)strU;
+ unsigned char* sL_end = sL + lenL;
+ unsigned char* sU_end = sU + lenU;
+
+ while(sL < sL_end && sU < sU_end) {
+ unsigned char UasL;
+ if (*sL >= 0x80) {
+ if (*sU < 0xC4 && (sU+1) < sU_end) {
+ UasL = ((sU[0] & 0x3) << 6) | (sU[1] & 0x3F);
+ }
+ else return -1;
+ }
+ else {
+ UasL = *sU;
+ }
+ if (*sL < UasL) return -1;
+ if (*sL > UasL) return 1;
+
+ sL++;
+ if (*sU < 0x80) sU++;
+ else if (*sU < 0xE0) sU += 2;
+ else if (*sU < 0xF0) sU += 3;
+ else /*if (*sU < 0xF8)*/ sU += 4;
+ }
+
+ return (sU >= sU_end) - (sL >= sL_end); /* -1, 0 or 1 */
+}
+
+
#define CMP_EXT_ERROR_CODE 4711
#define CMP_EXT_INT32_BE(AP, BP) \
@@ -1437,9 +1482,8 @@ do { \
#define CMP_EXT_SKIP_ATOM(EP) \
do { \
- if ((EP)[0] != ERL_ATOM_EXT) \
+ if (!jump_atom(&(EP))) \
return CMP_EXT_ERROR_CODE; \
- (EP) += 3 + ((EP)[1] << 8 | (EP)[2]); \
} while (0)
/*
@@ -1561,6 +1605,7 @@ static int cmp_exe2(unsigned char **e1, unsigned char **e2)
int min, ret,i,j,k;
double ff1, ff2;
unsigned char *tmp1, *tmp2;
+ unsigned char tag1, tag2;
if ( ((*e1)[0] == ERL_STRING_EXT) && ((*e2)[0] == ERL_LIST_EXT) ) {
return cmp_string_list(e1, e2);
@@ -1568,8 +1613,10 @@ static int cmp_exe2(unsigned char **e1, unsigned char **e2)
return -cmp_string_list(e2, e1);
}
- *e2 += 1;
- switch (*(*e1)++)
+ tag1 = *(*e1)++;
+ tag2 = *(*e2)++;
+ i = j = 0;
+ switch (tag1)
{
case ERL_SMALL_INTEGER_EXT:
if (**e1 < **e2) ret = -1;
@@ -1589,11 +1636,17 @@ static int cmp_exe2(unsigned char **e1, unsigned char **e2)
*e1 += 4; *e2 += 4;
return ret;
case ERL_ATOM_EXT:
- i = (**e1 << 8) | (*e1)[1];
- j = (**e2 << 8) | (*e2)[1];
- ret = cmpbytes(*e1 +2, i, *e2 +2, j);
- *e1 += (i + 2);
- *e2 += (j + 2);
+ case ERL_ATOM_UTF8_EXT:
+ i = (**e1) << 8; (*e1)++;
+ j = (**e2) << 8; (*e2)++;
+ /*fall through*/
+ case ERL_SMALL_ATOM_EXT:
+ case ERL_SMALL_ATOM_UTF8_EXT:
+ i |= (**e1); (*e1)++;
+ j |= (**e2); (*e2)++;
+ ret = cmpatoms(*e1, i, tag1, *e2, j, tag2);
+ *e1 += i;
+ *e2 += j;
return ret;
case ERL_PID_EXT: {
unsigned char *n1 = *e1;
@@ -1622,7 +1675,7 @@ static int cmp_exe2(unsigned char **e1, unsigned char **e2)
}
case ERL_PORT_EXT:
/* First compare node names ... */
- if (**e1 != ERL_ATOM_EXT || **e2 != ERL_ATOM_EXT)
+ if (!IS_ERL_ATOM(**e1) || !IS_ERL_ATOM(**e2))
return CMP_EXT_ERROR_CODE;
ret = cmp_exe2(e1, e2);
*e1 += 5; *e2 += 5;
diff --git a/lib/erl_interface/src/legacy/global_whereis.c b/lib/erl_interface/src/legacy/global_whereis.c
index 2afb193504..e6c556d907 100644
--- a/lib/erl_interface/src/legacy/global_whereis.c
+++ b/lib/erl_interface/src/legacy/global_whereis.c
@@ -85,7 +85,16 @@ ETERM *erl_global_whereis(int fd, const char *name, char *node)
opid = erl_decode((unsigned char*)buf);
/* extract the nodename for the caller */
- if (node) strcpy(node,epid.node);
+ if (node) {
+ char* node_str = ERL_PID_NODE(opid);
+ if (node_str) {
+ strcpy(node, node_str);
+ }
+ else {
+ erl_free_term(opid);
+ return NULL;
+ }
+ }
return opid;
}
diff --git a/lib/erl_interface/src/misc/ei_decode_term.c b/lib/erl_interface/src/misc/ei_decode_term.c
index 0b82ef0e35..65afee89cc 100644
--- a/lib/erl_interface/src/misc/ei_decode_term.c
+++ b/lib/erl_interface/src/misc/ei_decode_term.c
@@ -32,7 +32,7 @@
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;
+ int i, n, sign;
char c;
if (term == NULL) return -1;
@@ -48,20 +48,13 @@ int ei_decode_ei_term(const char* buf, int* index, ei_term* term)
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;
- break;
+ case ERL_ATOM_UTF8_EXT:
+ case ERL_SMALL_ATOM_EXT:
+ case ERL_SMALL_ATOM_UTF8_EXT:
+ return ei_decode_atom(buf, index, term->value.atom_name);
case ERL_REFERENCE_EXT:
/* 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;
+ if (get_atom(&s, term->value.ref.node, &term->value.ref.node_org_enc) < 0) return -1;
/* now the numbers: num (4), creation (1) */
term->value.ref.n[0] = get32be(s);
term->value.ref.len = 1;
@@ -71,12 +64,7 @@ int ei_decode_ei_term(const char* buf, int* index, ei_term* term)
/* first the integer count */
term->value.ref.len = get16be(s);
/* 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;
+ if (get_atom(&s, term->value.ref.node, &term->value.ref.node_org_enc) < 0) return -1;
/* creation */
term->value.ref.creation = get8(s) & 0x03;
/* finally the id integers */
@@ -88,22 +76,12 @@ int ei_decode_ei_term(const char* buf, int* index, ei_term* term)
}
break;
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';
+ if (get_atom(&s, term->value.port.node, &term->value.port.node_org_enc) < 0) return -1;
term->value.port.id = get32be(s) & 0x0fffffff; /* 28 bits */;
term->value.port.creation = get8(s) & 0x03;
break;
case ERL_PID_EXT:
- 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;
+ if (get_atom(&s, term->value.pid.node, &term->value.port.node_org_enc) < 0) return -1;
/* now the numbers: num (4), serial (4), creation (1) */
term->value.pid.num = get32be(s) & 0x7fff; /* 15 bits */
term->value.pid.serial = get32be(s) & 0x1fff; /* 13 bits */
diff --git a/lib/erl_interface/src/misc/ei_format.c b/lib/erl_interface/src/misc/ei_format.c
index 281a192535..b5f11e618e 100644
--- a/lib/erl_interface/src/misc/ei_format.c
+++ b/lib/erl_interface/src/misc/ei_format.c
@@ -139,8 +139,8 @@ static int patom(const char** fmt, ei_x_buff* x)
--(*fmt);
len = *fmt - start;
/* FIXME why truncate atom name and not fail?! */
- if (len > MAXATOMLEN)
- len = MAXATOMLEN;
+ if (len >= MAXATOMLEN)
+ len = MAXATOMLEN-1;
return ei_x_encode_atom_len(x, start, len);
}
diff --git a/lib/erl_interface/src/misc/ei_printterm.c b/lib/erl_interface/src/misc/ei_printterm.c
index 5fc6b3542c..f3003a6172 100644
--- a/lib/erl_interface/src/misc/ei_printterm.c
+++ b/lib/erl_interface/src/misc/ei_printterm.c
@@ -115,7 +115,7 @@ static int print_term(FILE* fp, ei_x_buff* x,
const char* buf, int* index)
{
int i, doquote, n, m, ty, r;
- char a[MAXATOMLEN+1], *p;
+ char a[MAXATOMLEN], *p;
int ch_written = 0; /* counter of written chars */
erlang_pid pid;
erlang_port port;
@@ -132,7 +132,10 @@ static int print_term(FILE* fp, ei_x_buff* x,
doquote = 0;
ei_get_type_internal(buf, index, &ty, &n);
switch (ty) {
- case ERL_ATOM_EXT:
+ case ERL_ATOM_EXT:
+ case ERL_ATOM_UTF8_EXT:
+ case ERL_SMALL_ATOM_EXT:
+ case ERL_SMALL_ATOM_UTF8_EXT:
if (ei_decode_atom(buf, index, a) < 0)
goto err;
doquote = !islower((int)a[0]);
diff --git a/lib/erl_interface/src/misc/ei_x_encode.c b/lib/erl_interface/src/misc/ei_x_encode.c
index fa1e26ccbb..44dcff7664 100644
--- a/lib/erl_interface/src/misc/ei_x_encode.c
+++ b/lib/erl_interface/src/misc/ei_x_encode.c
@@ -197,18 +197,33 @@ int ei_x_encode_tuple_header(ei_x_buff* x, long n)
int ei_x_encode_atom(ei_x_buff* x, const char* s)
{
- return ei_x_encode_atom_len(x, s, strlen(s));
+ return ei_x_encode_atom_len_as(x, s, strlen(s), ERLANG_LATIN1, ERLANG_LATIN1);
}
int ei_x_encode_atom_len(ei_x_buff* x, const char* s, int len)
{
+ return ei_x_encode_atom_len_as(x, s, len, ERLANG_LATIN1, ERLANG_LATIN1);
+}
+
+int ei_x_encode_atom_as(ei_x_buff* x, const char* s,
+ enum erlang_char_encoding from_enc,
+ enum erlang_char_encoding to_enc)
+{
+ return ei_x_encode_atom_len_as(x, s, strlen(s), from_enc, to_enc);
+}
+
+int ei_x_encode_atom_len_as(ei_x_buff* x, const char* s, int len,
+ enum erlang_char_encoding from_enc,
+ enum erlang_char_encoding to_enc)
+{
int i = x->index;
- ei_encode_atom_len(NULL, &i, s, len);
+ ei_encode_atom_len_as(NULL, &i, s, len, from_enc, to_enc);
if (!x_fix_buff(x, i))
return -1;
- return ei_encode_atom_len(x->buff, &x->index, s, len);
+ return ei_encode_atom_len_as(x->buff, &x->index, s, len, from_enc, to_enc);
}
+
int ei_x_encode_pid(ei_x_buff* x, const erlang_pid* pid)
{
int i = x->index;
diff --git a/lib/erl_interface/src/misc/get_type.c b/lib/erl_interface/src/misc/get_type.c
index 2a680d0f94..54465196b0 100644
--- a/lib/erl_interface/src/misc/get_type.c
+++ b/lib/erl_interface/src/misc/get_type.c
@@ -33,78 +33,6 @@ int ei_get_type(const char *buf, const int *index, int *type, int *len)
return ei_get_type_internal(buf, index, type, len);
}
-#if 0
-int ei_get_type(const char *buf, const int *index, int *type, int *len)
-{
- const char *s = buf + *index;
- int itype = get8(s); /* Internal type */
-
- *len = 0;
-
- switch (*type) {
-
- case ERL_SMALL_INTEGER_EXT:
- case ERL_INTEGER_EXT:
- case ERL_SMALL_BIG_EXT:
- case ERL_LARGE_BIG_EXT:
- *type = EI_TYPE_INTEGER;
- break;
-
- case ERL_FLOAT_EXT:
- *type = EI_TYPE_FLOAT;
- break;
-
- case ERL_SMALL_TUPLE_EXT:
- *len = get8(s);
- break;
-
- case ERL_ATOM_EXT:
- case ERL_STRING_EXT:
- *len = get16be(s);
- break;
-
- case ERL_LARGE_TUPLE_EXT:
- case ERL_LIST_EXT:
- case ERL_BINARY_EXT:
- *len = get32be(s);
- break;
-
- case ERL_SMALL_BIG_EXT:
- *len = (get8(s)+1)/2; /* big arity */
- break;
-
- case ERL_LARGE_BIG_EXT:
- *len = (get32be(s)+1)/2; /* big arity */
- break;
-
- case ERL_BINARY_EXT:
- *type = EI_TYPE_BINARY;
- break;
-
- case ERL_PID_EXT:
- *type = EI_TYPE_PID;
- break;
-
- case ERL_PORT_EXT:
- *type = EI_TYPE_PORT;
- break;
-
- case ERL_REFERENCE_EXT:
- case ERL_NEW_REFERENCE_EXT:
- *type = EI_TYPE_REF;
- break;
-
- default:
- break;
- }
-
- /* leave index unchanged */
- return 0;
-}
-#endif
-
-
-/* Old definition of function above */
int ei_get_type_internal(const char *buf, const int *index,
int *type, int *len)
@@ -114,10 +42,15 @@ int ei_get_type_internal(const char *buf, const int *index,
*type = get8(s);
switch (*type) {
+ case ERL_SMALL_ATOM_EXT:
+ case ERL_SMALL_ATOM_UTF8_EXT:
+ *type = ERL_ATOM_EXT;
case ERL_SMALL_TUPLE_EXT:
*len = get8(s);
break;
-
+
+ case ERL_ATOM_UTF8_EXT:
+ *type = ERL_ATOM_EXT;
case ERL_ATOM_EXT:
case ERL_STRING_EXT:
*len = get16be(s);
diff --git a/lib/erl_interface/src/misc/putget.h b/lib/erl_interface/src/misc/putget.h
index 7a43de324b..77ae168f8c 100644
--- a/lib/erl_interface/src/misc/putget.h
+++ b/lib/erl_interface/src/misc/putget.h
@@ -105,6 +105,13 @@
((EI_ULONGLONG)((unsigned char *)(s))[-2] << 8) | \
(EI_ULONGLONG)((unsigned char *)(s))[-1]))
+int utf8_to_latin1(char* dst, const char* src, int slen, int destlen, enum erlang_char_encoding* res_encp);
+int latin1_to_utf8(char* dst, const char* src, int slen, int destlen, enum erlang_char_encoding* res_encp);
+int ei_internal_get_atom(const char** bufp, char* p, enum erlang_char_encoding*);
+int ei_internal_put_atom(char** bufp, const char* p, int slen, enum erlang_char_encoding);
+#define get_atom ei_internal_get_atom
+#define put_atom ei_internal_put_atom
+
typedef union float_ext {
double d;
EI_ULONGLONG val;
diff --git a/lib/erl_interface/src/misc/show_msg.c b/lib/erl_interface/src/misc/show_msg.c
index 194296798b..33b09643ca 100644
--- a/lib/erl_interface/src/misc/show_msg.c
+++ b/lib/erl_interface/src/misc/show_msg.c
@@ -132,13 +132,13 @@ int ei_show_sendmsg(FILE *stream, const char *header, const char *msgbuf)
switch (msg.msgtype) {
case ERL_SEND:
- if (ei_decode_atom(header,&index,msg.cookie)
+ if (ei_decode_atom_as(header,&index,msg.cookie,sizeof(msg.cookie),ERLANG_UTF8,NULL,NULL)
|| ei_decode_pid(header,&index,&msg.to)) return -1;
mbuf = msgbuf;
break;
case ERL_SEND_TT:
- if (ei_decode_atom(header,&index,msg.cookie)
+ if (ei_decode_atom_as(header,&index,msg.cookie,sizeof(msg.cookie),ERLANG_UTF8,NULL,NULL)
|| ei_decode_pid(header,&index,&msg.to)
|| ei_decode_trace(header,&index,&msg.token)) return -1;
mbuf = msgbuf;
@@ -146,15 +146,15 @@ int ei_show_sendmsg(FILE *stream, const char *header, const char *msgbuf)
case ERL_REG_SEND:
if (ei_decode_pid(header,&index,&msg.from)
- || ei_decode_atom(header,&index,msg.cookie)
- || ei_decode_atom(header,&index,msg.toname)) return -1;
+ || ei_decode_atom_as(header,&index,msg.cookie,sizeof(msg.cookie),ERLANG_UTF8,NULL,NULL)
+ || ei_decode_atom_as(header,&index,msg.toname,sizeof(msg.toname),ERLANG_UTF8,NULL,NULL)) return -1;
mbuf = msgbuf;
break;
case ERL_REG_SEND_TT:
if (ei_decode_pid(header,&index,&msg.from)
- || ei_decode_atom(header,&index,msg.cookie)
- || ei_decode_atom(header,&index,msg.toname)
+ || ei_decode_atom_as(header,&index,msg.cookie,sizeof(msg.cookie),ERLANG_UTF8,NULL,NULL)
+ || ei_decode_atom_as(header,&index,msg.toname,sizeof(msg.toname),ERLANG_UTF8,NULL,NULL)
|| ei_decode_trace(header,&index,&msg.token)) return -1;
mbuf = msgbuf;
break;
@@ -457,7 +457,7 @@ static void show_term(const char *termbuf, int *index, FILE *stream)
break;
case ERL_FUN_EXT: {
- char atom[MAXATOMLEN+1];
+ char atom[MAXATOMLEN];
long idx;
long uniq;
const char* s = termbuf + *index, * s0 = s;
diff --git a/lib/erl_interface/src/prog/ei_fake_prog.c b/lib/erl_interface/src/prog/ei_fake_prog.c
index 68eb537211..34101a2851 100644
--- a/lib/erl_interface/src/prog/ei_fake_prog.c
+++ b/lib/erl_interface/src/prog/ei_fake_prog.c
@@ -96,6 +96,7 @@ int main(void)
EI_ULONGLONG *ulonglongp = (EI_ULONGLONG*)NULL;
EI_ULONGLONG ulonglongx = 0;
#endif
+ enum erlang_char_encoding enc;
intx = erl_errno;
@@ -148,9 +149,13 @@ int main(void)
ei_x_encode_string(&eix, charp);
ei_x_encode_string_len(&eix, charp, intx);
ei_encode_atom(charp, intp, charp);
+ ei_encode_atom_as(charp, intp, charp, ERLANG_LATIN1, ERLANG_UTF8);
ei_encode_atom_len(charp, intp, charp, intx);
+ ei_encode_atom_len_as(charp, intp, charp, intx, ERLANG_ASCII, ERLANG_LATIN1);
ei_x_encode_atom(&eix, charp);
+ ei_x_encode_atom_as(&eix, charp, ERLANG_LATIN1, ERLANG_UTF8);
ei_x_encode_atom_len(&eix, charp, intx);
+ ei_x_encode_atom_len_as(&eix, charp, intx, ERLANG_LATIN1, ERLANG_UTF8);
ei_encode_binary(charp, intp, (void *)0, longx);
ei_x_encode_binary(&eix, (void*)0, intx);
ei_encode_pid(charp, intp, &epid);
@@ -181,6 +186,7 @@ int main(void)
ei_decode_char(charp, intp, charp);
ei_decode_string(charp, intp, charp);
ei_decode_atom(charp, intp, charp);
+ ei_decode_atom_as(charp, intp, charp, MAXATOMLEN_UTF8, ERLANG_WHATEVER, &enc, &enc);
ei_decode_binary(charp, intp, (void *)0, longp);
ei_decode_fun(charp, intp, &efun);
free_fun(&efun);
diff --git a/lib/erl_interface/test/Makefile b/lib/erl_interface/test/Makefile
index d11a138844..2b85dfc571 100644
--- a/lib/erl_interface/test/Makefile
+++ b/lib/erl_interface/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/erl_interface/test/all_SUITE_data/Makefile.src b/lib/erl_interface/test/all_SUITE_data/Makefile.src
index 39085def2d..d2f57c8149 100644
--- a/lib/erl_interface/test/all_SUITE_data/Makefile.src
+++ b/lib/erl_interface/test/all_SUITE_data/Makefile.src
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2003-2011. All Rights Reserved.
+# Copyright Ericsson AB 2003-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/erl_interface/test/all_SUITE_data/gccifier.c b/lib/erl_interface/test/all_SUITE_data/gccifier.c
index a1019f9a72..7e4ffc7281 100644
--- a/lib/erl_interface/test/all_SUITE_data/gccifier.c
+++ b/lib/erl_interface/test/all_SUITE_data/gccifier.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2004-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2004-2012. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/erl_interface/test/all_SUITE_data/init_tc.erl b/lib/erl_interface/test/all_SUITE_data/init_tc.erl
index 60c965eda3..7e370c566a 100644
--- a/lib/erl_interface/test/all_SUITE_data/init_tc.erl
+++ b/lib/erl_interface/test/all_SUITE_data/init_tc.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/erl_interface/test/ei_decode_SUITE.erl b/lib/erl_interface/test/ei_decode_SUITE.erl
index bb44b78854..2c4b6e5541 100644
--- a/lib/erl_interface/test/ei_decode_SUITE.erl
+++ b/lib/erl_interface/test/ei_decode_SUITE.erl
@@ -1,7 +1,8 @@
+%% -*- coding: utf-8 -*-
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -204,15 +205,15 @@ test_ei_decode_misc(Config) when is_list(Config) ->
?line send_term_as_binary(P,foo),
?line send_term_as_binary(P,''),
- ?line send_term_as_binary(P,'������'),
+ ?line send_term_as_binary(P,'ÅÄÖåäö'),
?line send_term_as_binary(P,"foo"),
?line send_term_as_binary(P,""),
- ?line send_term_as_binary(P,"������"),
+ ?line send_term_as_binary(P,"ÅÄÖåäö"),
?line send_term_as_binary(P,<<"foo">>),
?line send_term_as_binary(P,<<>>),
- ?line send_term_as_binary(P,<<"������">>),
+ ?line send_term_as_binary(P,<<"ÅÄÖåäö">>),
% ?line send_term_as_binary(P,{}),
% ?line send_term_as_binary(P,[]),
diff --git a/lib/erl_interface/test/ei_decode_encode_SUITE.erl b/lib/erl_interface/test/ei_decode_encode_SUITE.erl
index 85cb62239b..0c98b494ec 100644
--- a/lib/erl_interface/test/ei_decode_encode_SUITE.erl
+++ b/lib/erl_interface/test/ei_decode_encode_SUITE.erl
@@ -118,6 +118,13 @@ test_ei_decode_encode(Config) when is_list(Config) ->
?line send_rec(P, OXPort),
?line send_rec(P, OXRef),
+ %% Unicode atoms
+ [begin send_rec(P, Atom),
+ send_rec(P, mk_pid({Atom,1}, 23434, 3434)),
+ send_rec(P, mk_port({Atom,1}, 2343434)),
+ send_rec(P, mk_ref({Atom,1}, [262143, 8723648, 24097245])),
+ void
+ end || Atom <- unicode_atom_data()],
?line runner:recv_eot(P),
ok.
@@ -127,7 +134,7 @@ test_ei_decode_encode(Config) when is_list(Config) ->
% We read two packets for each test, the ei_decode_encode and ei_x_decode_encode version....
send_rec(P, Term) when is_port(P) ->
- ?t:format("Testing: ~p~n", [Term]),
+ %%?t:format("Testing: ~p~n", [Term]),
P ! {self(), {command, term_to_binary(Term)}},
{_B,Term} = get_buf_and_term(P).
@@ -146,7 +153,7 @@ get_buf_and_term(P) ->
_ ->
B1 = list_to_binary([131,B]), % No magic, add
T = binary_to_term(B1),
- io:format("~w\n~w\n(got no magic)\n",[B,T]),
+ %io:format("~w\n~w\n(got no magic)\n",[B,T]),
{B,T}
end.
@@ -160,7 +167,7 @@ get_binary(P) ->
case runner:get_term(P) of
{bytes,L} ->
B = list_to_binary(L),
- io:format("~w\n",[L]),
+ %%io:format("~w\n",[L]),
% For strange reasons <<131>> show up as <>....
% io:format("~w\n",[B]),
B;
@@ -226,38 +233,36 @@ 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) ->
+ <<?VERSION_MAGIC, NodeNameExt/binary>> = term_to_binary(NodeName),
+ mk_pid({NodeNameExt, Creation}, Number, Serial);
+mk_pid({NodeNameExt, Creation}, Number, Serial) ->
case catch binary_to_term(list_to_binary([?VERSION_MAGIC,
?PID_EXT,
- ?ATOM_EXT,
- uint16_be(length(NodeName)),
- NodeName,
+ NodeNameExt,
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]});
+ exit({badarg, mk_pid, [{NodeNameExt, Creation}, Number, Serial]});
Other ->
exit({unexpected_binary_to_term_result, Other})
end.
mk_port({NodeName, Creation}, Number) when is_atom(NodeName) ->
- mk_port({atom_to_list(NodeName), Creation}, Number);
-mk_port({NodeName, Creation}, Number) ->
+ <<?VERSION_MAGIC, NodeNameExt/binary>> = term_to_binary(NodeName),
+ mk_port({NodeNameExt, Creation}, Number);
+mk_port({NodeNameExt, Creation}, Number) ->
case catch binary_to_term(list_to_binary([?VERSION_MAGIC,
?PORT_EXT,
- ?ATOM_EXT,
- uint16_be(length(NodeName)),
- NodeName,
+ NodeNameExt,
uint32_be(Number),
uint8(Creation)])) of
Port when is_port(Port) ->
Port;
{'EXIT', {badarg, _}} ->
- exit({badarg, mk_port, [{NodeName, Creation}, Number]});
+ exit({badarg, mk_port, [{NodeNameExt, Creation}, Number]});
Other ->
exit({unexpected_binary_to_term_result, Other})
end.
@@ -265,33 +270,30 @@ mk_port({NodeName, Creation}, Number) ->
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) ->
+ <<?VERSION_MAGIC, NodeNameExt/binary>> = term_to_binary(NodeName),
+ mk_ref({NodeNameExt, Creation}, Numbers);
+mk_ref({NodeNameExt, Creation}, [Number]) when is_binary(NodeNameExt),
+ 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,
+ NodeNameExt,
uint32_be(Number),
uint8(Creation)])) of
Ref when is_reference(Ref) ->
Ref;
{'EXIT', {badarg, _}} ->
- exit({badarg, mk_ref, [{NodeName, Creation}, [Number]]});
+ exit({badarg, mk_ref, [{NodeNameExt, 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) ->
+mk_ref({NodeNameExt, Creation}, Numbers) when is_binary(NodeNameExt),
+ is_integer(Creation),
+ is_list(Numbers) ->
case catch binary_to_term(list_to_binary([?VERSION_MAGIC,
?NEW_REFERENCE_EXT,
uint16_be(length(Numbers)),
- ?ATOM_EXT,
- uint16_be(length(NodeName)),
- NodeName,
+ NodeNameExt,
uint8(Creation),
lists:map(fun (N) ->
uint32_be(N)
@@ -300,8 +302,67 @@ mk_ref({NodeName, Creation}, Numbers) when is_list(NodeName),
Ref when is_reference(Ref) ->
Ref;
{'EXIT', {badarg, _}} ->
- exit({badarg, mk_ref, [{NodeName, Creation}, Numbers]});
+ exit({badarg, mk_ref, [{NodeNameExt, Creation}, Numbers]});
Other ->
exit({unexpected_binary_to_term_result, Other})
end.
+
+
+unicode_atom_data() ->
+ [uc_atup(lists:seq(16#1f600, 16#1f600+254)),
+ uc_atup(lists:seq(16#1f600, 16#1f600+63)),
+ uc_atup(lists:seq(1, 255)),
+ uc_atup(lists:seq(100, 163)),
+ uc_atup(lists:seq(200, 354)),
+ uc_atup(lists:seq(200, 263)),
+ uc_atup(lists:seq(2000, 2254)),
+ uc_atup(lists:seq(2000, 2063)),
+ uc_atup(lists:seq(65500, 65754)),
+ uc_atup(lists:seq(65500, 65563))
+ | lists:map(fun (N) ->
+ Pow2 = (1 bsl N),
+ uc_atup(lists:seq(Pow2 - 127, Pow2 + 127))
+ end,
+ lists:seq(7, 20))
+ ].
+
+uc_atup(ATxt) ->
+ string_to_atom(ATxt).
+
+string_to_atom(String) ->
+ Utf8List = string_to_utf8_list(String),
+ Len = length(Utf8List),
+ TagLen = case Len < 256 of
+ true -> [119, Len];
+ false -> [118, Len bsr 8, Len band 16#ff]
+ end,
+ binary_to_term(list_to_binary([131, TagLen, Utf8List])).
+
+string_to_utf8_list([]) ->
+ [];
+string_to_utf8_list([CP|CPs]) when is_integer(CP),
+ 0 =< CP,
+ CP =< 16#7F ->
+ [CP | string_to_utf8_list(CPs)];
+string_to_utf8_list([CP|CPs]) when is_integer(CP),
+ 16#80 =< CP,
+ CP =< 16#7FF ->
+ [16#C0 bor (CP bsr 6),
+ 16#80 bor (16#3F band CP)
+ | string_to_utf8_list(CPs)];
+string_to_utf8_list([CP|CPs]) when is_integer(CP),
+ 16#800 =< CP,
+ CP =< 16#FFFF ->
+ [16#E0 bor (CP bsr 12),
+ 16#80 bor (16#3F band (CP bsr 6)),
+ 16#80 bor (16#3F band CP)
+ | string_to_utf8_list(CPs)];
+string_to_utf8_list([CP|CPs]) when is_integer(CP),
+ 16#10000 =< CP,
+ CP =< 16#10FFFF ->
+ [16#F0 bor (CP bsr 18),
+ 16#80 bor (16#3F band (CP bsr 12)),
+ 16#80 bor (16#3F band (CP bsr 6)),
+ 16#80 bor (16#3F band CP)
+ | string_to_utf8_list(CPs)].
diff --git a/lib/erl_interface/test/ei_decode_encode_SUITE_data/ei_decode_encode_test.c b/lib/erl_interface/test/ei_decode_encode_SUITE_data/ei_decode_encode_test.c
index 406f02ecfb..e57663f984 100644
--- a/lib/erl_interface/test/ei_decode_encode_SUITE_data/ei_decode_encode_test.c
+++ b/lib/erl_interface/test/ei_decode_encode_SUITE_data/ei_decode_encode_test.c
@@ -29,171 +29,229 @@
*/
-#define EI_DECODE_ENCODE(FUNC,TYPE) \
- { \
- char *buf; \
- char buf2[1024]; \
- TYPE p; \
- int size1 = 0; \
- int size2 = 0; \
- int size3 = 0; \
- int err; \
- ei_x_buff arg; \
-\
- message("ei_decode_" #FUNC ", arg is type " #TYPE); \
- buf = read_packet(NULL); \
- err = ei_decode_ ## FUNC(buf+1, &size1, &p); \
- if (err != 0) { \
- if (err != -1) { \
- fail("decode returned non zero but not -1"); \
- } else { \
- fail("decode returned non zero"); \
- } \
- return; \
- } \
- if (size1 < 1) { \
- fail("size is < 1"); \
- return; \
- } \
-\
- message("ei_encode_" #FUNC " buf is NULL, arg is type " #TYPE); \
- err = ei_encode_ ## FUNC(NULL, &size2, &p); \
- if (err != 0) { \
- if (err != -1) { \
- fail("size calculation returned non zero but not -1"); \
- return; \
- } else { \
- fail("size calculation returned non zero"); \
- return; \
- } \
- } \
- if (size1 != size2) { \
- message("size1 = %d, size2 = %d\n",size1,size2); \
- fail("decode and encode size differs when buf is NULL"); \
- return; \
- } \
- message("ei_encode_" #FUNC ", arg is type " #TYPE); \
- err = ei_encode_ ## FUNC(buf2, &size3, &p); \
- if (err != 0) { \
- if (err != -1) { \
- fail("returned non zero but not -1"); \
- } else { \
- fail("returned non zero"); \
- } \
- return; \
- } \
- if (size1 != size3) { \
- message("size1 = %d, size2 = %d\n",size1,size3); \
- fail("decode and encode size differs"); \
- return; \
- } \
- send_buffer(buf2, size1); \
-\
- message("ei_x_encode_" #FUNC ", arg is type " #TYPE); \
- ei_x_new(&arg); \
- err = ei_x_encode_ ## FUNC(&arg, &p); \
- if (err != 0) { \
- if (err != -1) { \
- fail("returned non zero but not -1"); \
- } else { \
- fail("returned non zero"); \
- } \
- ei_x_free(&arg); \
- return; \
- } \
- if (arg.index < 1) { \
- fail("size is < 1"); \
- ei_x_free(&arg); \
- return; \
- } \
- send_buffer(arg.buff, arg.index); \
- ei_x_free(&arg); \
- }
-
-#define EI_DECODE_ENCODE_BIG(FUNC,TYPE) \
- { \
- char *buf; \
- char buf2[2048]; \
- TYPE *p; \
- int size1 = 0; \
- int size2 = 0; \
- int size3 = 0; \
- int err, index = 0, len, type; \
- ei_x_buff arg; \
-\
- message("ei_decode_" #FUNC ", arg is type " #TYPE); \
- buf = read_packet(NULL); \
- ei_get_type(buf+1, &index, &type, &len); \
- p = ei_alloc_big(len); \
- err = ei_decode_ ## FUNC(buf+1, &size1, p); \
- if (err != 0) { \
- if (err != -1) { \
- fail("decode returned non zero but not -1"); \
- } else { \
- fail("decode returned non zero"); \
- } \
- return; \
- } \
- if (size1 < 1) { \
- fail("size is < 1"); \
- return; \
- } \
-\
- message("ei_encode_" #FUNC " buf is NULL, arg is type " #TYPE); \
- err = ei_encode_ ## FUNC(NULL, &size2, p); \
- if (err != 0) { \
- if (err != -1) { \
- fail("size calculation returned non zero but not -1"); \
- return; \
- } else { \
- fail("size calculation returned non zero"); \
- return; \
- } \
- } \
- if (size1 != size2) { \
- message("size1 = %d, size2 = %d\n",size1,size2); \
- fail("decode and encode size differs when buf is NULL"); \
- return; \
- } \
- message("ei_encode_" #FUNC ", arg is type " #TYPE); \
- err = ei_encode_ ## FUNC(buf2, &size3, p); \
- if (err != 0) { \
- if (err != -1) { \
- fail("returned non zero but not -1"); \
- } else { \
- fail("returned non zero"); \
- } \
- return; \
- } \
- if (size1 != size3) { \
- message("size1 = %d, size2 = %d\n",size1,size3); \
- fail("decode and encode size differs"); \
- return; \
- } \
- send_buffer(buf2, size1); \
-\
- message("ei_x_encode_" #FUNC ", arg is type " #TYPE); \
- ei_x_new(&arg); \
- err = ei_x_encode_ ## FUNC(&arg, p); \
- if (err != 0) { \
- if (err != -1) { \
- fail("returned non zero but not -1"); \
- } else { \
- fail("returned non zero"); \
- } \
- ei_x_free(&arg); \
- return; \
- } \
- if (arg.index < 1) { \
- fail("size is < 1"); \
- ei_x_free(&arg); \
- return; \
- } \
- send_buffer(arg.buff, arg.index); \
- ei_x_free(&arg); \
- ei_free_big(p); \
- }
+/*#define MESSAGE(FMT,A1,A2) message(FMT,A1,A2)*/
+#define MESSAGE(FMT,A1,A2)
+typedef int decodeFT(const char *buf, int *index, void*);
+typedef int encodeFT(char *buf, int *index, void*);
+typedef int x_encodeFT(ei_x_buff*, void*);
+
+struct Type {
+ char* name;
+ char* type;
+ decodeFT* ei_decode_fp;
+ encodeFT* ei_encode_fp;
+ x_encodeFT* ei_x_encode_fp;
+};
+
+typedef struct
+{
+ char name[MAXATOMLEN_UTF8];
+ enum erlang_char_encoding enc;
+}my_atom;
+
+int ei_decode_my_atom(const char *buf, int *index, my_atom* a)
+{
+ return ei_decode_atom_as(buf, index, a->name, sizeof(a->name), ERLANG_UTF8, &a->enc, NULL);
+}
+int ei_encode_my_atom(char *buf, int *index, my_atom* a)
+{
+ return ei_encode_atom_as(buf, index, a->name, ERLANG_UTF8, a->enc);
+}
+int ei_x_encode_my_atom(ei_x_buff* x, my_atom* a)
+{
+ return ei_x_encode_atom_as(x, a->name, ERLANG_UTF8, a->enc);
+}
+
+#define BUFSZ 2000
+
+void decode_encode(struct Type* t, void* obj)
+{
+ char *buf;
+ char buf2[BUFSZ];
+ int size1 = 0;
+ int size2 = 0;
+ int size3 = 0;
+ int err;
+ ei_x_buff arg;
+
+ MESSAGE("ei_decode_%s, arg is type %s", t->name, t->type);
+ buf = read_packet(NULL);
+ err = t->ei_decode_fp(buf+1, &size1, obj);
+ if (err != 0) {
+ if (err != -1) {
+ fail("decode returned non zero but not -1");
+ } else {
+ fail("decode returned non zero");
+ }
+ return;
+ }
+ if (size1 < 1) {
+ fail("size is < 1");
+ return;
+ }
+
+ if (size1 > BUFSZ) {
+ fail("size is > BUFSZ");
+ return;
+ }
+
+ MESSAGE("ei_encode_%s buf is NULL, arg is type %s", t->name, t->type);
+ err = t->ei_encode_fp(NULL, &size2, obj);
+ if (err != 0) {
+ if (err != -1) {
+ fail("size calculation returned non zero but not -1");
+ return;
+ } else {
+ fail("size calculation returned non zero");
+ return;
+ }
+ }
+ if (size1 != size2) {
+ MESSAGE("size1 = %d, size2 = %d\n",size1,size2);
+ fail("decode and encode size differs when buf is NULL");
+ return;
+ }
+ MESSAGE("ei_encode_%s, arg is type %s", t->name, t->type);
+ err = t->ei_encode_fp(buf2, &size3, obj);
+ if (err != 0) {
+ if (err != -1) {
+ fail("returned non zero but not -1");
+ } else {
+ fail("returned non zero");
+ }
+ return;
+ }
+ if (size1 != size3) {
+ MESSAGE("size1 = %d, size2 = %d\n",size1,size3);
+ fail("decode and encode size differs");
+ return;
+ }
+ send_buffer(buf2, size1);
+
+ MESSAGE("ei_x_encode_%s, arg is type %s", t->name, t->type);
+ ei_x_new(&arg);
+ err = t->ei_x_encode_fp(&arg, obj);
+ if (err != 0) {
+ if (err != -1) {
+ fail("returned non zero but not -1");
+ } else {
+ fail("returned non zero");
+ }
+ ei_x_free(&arg);
+ return;
+ }
+ if (arg.index < 1) {
+ fail("size is < 1");
+ ei_x_free(&arg);
+ return;
+ }
+ send_buffer(arg.buff, arg.index);
+ ei_x_free(&arg);
+}
+
+
+#define EI_DECODE_ENCODE(TYPE, ERLANG_TYPE) { \
+ struct Type type_struct = {#TYPE, #ERLANG_TYPE, \
+ (decodeFT*)ei_decode_##TYPE, \
+ (encodeFT*)ei_encode_##TYPE, \
+ (x_encodeFT*)ei_x_encode_##TYPE }; \
+ ERLANG_TYPE type_obj; \
+ decode_encode(&type_struct, &type_obj); \
+ }
+
+
+void decode_encode_big(struct Type* t)
+{
+ char *buf;
+ char buf2[2048];
+ void *p; /* (TYPE*) */
+ int size1 = 0;
+ int size2 = 0;
+ int size3 = 0;
+ int err, index = 0, len, type;
+ ei_x_buff arg;
+
+ MESSAGE("ei_decode_%s, arg is type %s", t->name, t->type);
+ buf = read_packet(NULL);
+ ei_get_type(buf+1, &index, &type, &len);
+ p = ei_alloc_big(len);
+ err = t->ei_decode_fp(buf+1, &size1, p);
+ if (err != 0) {
+ if (err != -1) {
+ fail("decode returned non zero but not -1");
+ } else {
+ fail("decode returned non zero");
+ }
+ return;
+ }
+ if (size1 < 1) {
+ fail("size is < 1");
+ return;
+ }
+
+ MESSAGE("ei_encode_%s buf is NULL, arg is type %s", t->name, t->type);
+ err = t->ei_encode_fp(NULL, &size2, p);
+ if (err != 0) {
+ if (err != -1) {
+ fail("size calculation returned non zero but not -1");
+ return;
+ } else {
+ fail("size calculation returned non zero");
+ return;
+ }
+ }
+ if (size1 != size2) {
+ MESSAGE("size1 = %d, size2 = %d\n",size1,size2);
+ fail("decode and encode size differs when buf is NULL");
+ return;
+ }
+ MESSAGE("ei_encode_%s, arg is type %s", t->name, t->type);
+ err = t->ei_encode_fp(buf2, &size3, p);
+ if (err != 0) {
+ if (err != -1) {
+ fail("returned non zero but not -1");
+ } else {
+ fail("returned non zero");
+ }
+ return;
+ }
+ if (size1 != size3) {
+ MESSAGE("size1 = %d, size2 = %d\n",size1,size3);
+ fail("decode and encode size differs");
+ return;
+ }
+ send_buffer(buf2, size1);
+
+ MESSAGE("ei_x_encode_%s, arg is type %s", t->name, t->type);
+ ei_x_new(&arg);
+ err = t->ei_x_encode_fp(&arg, p);
+ if (err != 0) {
+ if (err != -1) {
+ fail("returned non zero but not -1");
+ } else {
+ fail("returned non zero");
+ }
+ ei_x_free(&arg);
+ return;
+ }
+ if (arg.index < 1) {
+ fail("size is < 1");
+ ei_x_free(&arg);
+ return;
+ }
+ send_buffer(arg.buff, arg.index);
+ ei_x_free(&arg);
+ ei_free_big(p);
+}
+
+#define EI_DECODE_ENCODE_BIG(TYPE, ERLANG_TYPE) { \
+ struct Type type_struct = {#TYPE, #ERLANG_TYPE, \
+ (decodeFT*)ei_decode_##TYPE, \
+ (encodeFT*)ei_encode_##TYPE, \
+ (x_encodeFT*)ei_x_encode_##TYPE }; \
+ decode_encode_big(&type_struct); \
+ }
@@ -201,6 +259,8 @@
TESTCASE(test_ei_decode_encode)
{
+ int i;
+
EI_DECODE_ENCODE(fun , erlang_fun);
EI_DECODE_ENCODE(pid , erlang_pid);
EI_DECODE_ENCODE(port , erlang_port);
@@ -223,6 +283,14 @@ TESTCASE(test_ei_decode_encode)
EI_DECODE_ENCODE(port , erlang_port);
EI_DECODE_ENCODE(ref , erlang_ref);
+ /* Unicode atoms */
+ for (i=0; i<24; i++) {
+ EI_DECODE_ENCODE(my_atom, my_atom);
+ EI_DECODE_ENCODE(pid, erlang_pid);
+ EI_DECODE_ENCODE(port, erlang_port);
+ EI_DECODE_ENCODE(ref, erlang_ref);
+ }
+
report(1);
}
diff --git a/lib/erl_interface/test/ei_encode_SUITE.erl b/lib/erl_interface/test/ei_encode_SUITE.erl
index cefd33e5f6..537e9cb01c 100644
--- a/lib/erl_interface/test/ei_encode_SUITE.erl
+++ b/lib/erl_interface/test/ei_encode_SUITE.erl
@@ -1,7 +1,8 @@
+%% -*- coding: utf-8 -*-
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -213,19 +214,19 @@ test_ei_encode_misc(Config) when is_list(Config) ->
?line {<<100,0,3,"foo">>,foo} = get_buf_and_term(P),
?line {<<100,0,0,"">>,''} = get_buf_and_term(P),
?line {<<100,0,0,"">>,''} = get_buf_and_term(P),
- ?line {<<100,0,6,"������">>,'������'} = get_buf_and_term(P),
- ?line {<<100,0,6,"������">>,'������'} = get_buf_and_term(P),
+ ?line {<<100,0,6,"ÅÄÖåäö">>,'ÅÄÖåäö'} = get_buf_and_term(P),
+ ?line {<<100,0,6,"ÅÄÖåäö">>,'ÅÄÖåäö'} = get_buf_and_term(P),
?line {<<107,0,3,"foo">>,"foo"} = get_buf_and_term(P),
?line {<<107,0,3,"foo">>,"foo"} = get_buf_and_term(P),
?line {<<106>>,""} = get_buf_and_term(P),
?line {<<106>>,""} = get_buf_and_term(P),
- ?line {<<107,0,6,"������">>,"������"} = get_buf_and_term(P),
- ?line {<<107,0,6,"������">>,"������"} = get_buf_and_term(P),
+ ?line {<<107,0,6,"ÅÄÖåäö">>,"ÅÄÖåäö"} = get_buf_and_term(P),
+ ?line {<<107,0,6,"ÅÄÖåäö">>,"ÅÄÖåäö"} = get_buf_and_term(P),
?line {<<109,0,0,0,3,"foo">>,<<"foo">>} = get_buf_and_term(P),
?line {<<109,0,0,0,0,"">>,<<>>} = get_buf_and_term(P),
- ?line {<<109,0,0,0,6,"������">>,<<"������">>} = get_buf_and_term(P),
+ ?line {<<109,0,0,0,6,"ÅÄÖåäö">>,<<"ÅÄÖåäö">>} = get_buf_and_term(P),
?line {<<104,0>>,{}} = get_buf_and_term(P), % Tuple header for {}
?line {<<106>>,[]} = get_buf_and_term(P), % Empty list []
diff --git a/lib/erl_interface/test/ei_print_SUITE.erl b/lib/erl_interface/test/ei_print_SUITE.erl
index 2a3ed81f53..6305302e28 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-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -136,7 +136,7 @@ lists(Config) when is_list(Config) ->
%% ?line {term, "[{name, 'Madonna'}, {age, 21}, {data, [{addr, "E-street", 42}]}]"} =
%% get_term(P),
- %% kanske regexp i st�llet?
+ %% maybe regexp instead?
?line {term, "[{pi, 3.141500}, {'cos(70)', 0.342020}]"} = get_term(P),
?line {term, "[[pi, 3.141500], ['cos(70)', 0.342020]]"} = get_term(P),
diff --git a/lib/erl_interface/test/ei_tmo_SUITE.erl b/lib/erl_interface/test/ei_tmo_SUITE.erl
index cc22cb7440..c33beecbe9 100644
--- a/lib/erl_interface/test/ei_tmo_SUITE.erl
+++ b/lib/erl_interface/test/ei_tmo_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/erl_interface/test/erl_match_SUITE.erl b/lib/erl_interface/test/erl_match_SUITE.erl
index edbb17a8c1..2cf4fafe82 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-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/erl_interface/vsn.mk b/lib/erl_interface/vsn.mk
index 0e249d2483..1718f38069 100644
--- a/lib/erl_interface/vsn.mk
+++ b/lib/erl_interface/vsn.mk
@@ -1 +1 @@
-EI_VSN = 3.7.7
+EI_VSN = 3.7.9
diff --git a/lib/et/doc/src/Makefile b/lib/et/doc/src/Makefile
index facd335449..249d45fa52 100644
--- a/lib/et/doc/src/Makefile
+++ b/lib/et/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2002-2010. All Rights Reserved.
+# Copyright Ericsson AB 2002-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/et/doc/src/et_intro.xml b/lib/et/doc/src/et_intro.xml
index 60da289721..f90a6d2b5c 100644
--- a/lib/et/doc/src/et_intro.xml
+++ b/lib/et/doc/src/et_intro.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2002</year><year>2010</year>
+ <year>2002</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
diff --git a/lib/et/doc/src/notes.xml b/lib/et/doc/src/notes.xml
index 42790e0cdf..0b1f3eddce 100644
--- a/lib/et/doc/src/notes.xml
+++ b/lib/et/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2002</year><year>2011</year>
+ <year>2002</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -36,6 +36,21 @@
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.4.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix typo in ET doc (Thanks to Ricardo Catalinas Jim�nez)</p>
+ <p>
+ Own Id: OTP-10119</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>ET 1.4.4.1</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/et/examples/Makefile b/lib/et/examples/Makefile
index 190d5e2d6a..3edbebc2e4 100644
--- a/lib/et/examples/Makefile
+++ b/lib/et/examples/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2002-2010. All Rights Reserved.
+# Copyright Ericsson AB 2002-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/et/src/Makefile b/lib/et/src/Makefile
index 7693571c5c..386169fe95 100644
--- a/lib/et/src/Makefile
+++ b/lib/et/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2000-2010. All Rights Reserved.
+# Copyright Ericsson AB 2000-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -92,10 +92,10 @@ docs:
# ----------------------------------------------------
$(APP_TARGET): $(APP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
# ----------------------------------------------------
# Release Target
diff --git a/lib/et/src/et.erl b/lib/et/src/et.erl
index e2cd8564c3..c9ba4f6816 100644
--- a/lib/et/src/et.erl
+++ b/lib/et/src/et.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -102,7 +102,7 @@
%% From = actor()
%% To = actor()
%% FromTo = actor()
-%% Label = atom() | string() |�term()
+%% Label = atom() | string() | term()
%% Contents = [{Key, Value}] | term()
%%
%% actor() = term()
diff --git a/lib/et/src/et_wx_contents_viewer.erl b/lib/et/src/et_wx_contents_viewer.erl
index 86f46f25d0..b559da8807 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-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -245,12 +245,7 @@ handle_event(#wx{id = Id,
Data when is_record(Data, filter) ->
F = Data,
ChildState= S#state{active_filter = F#filter.name},
- case wx_object:start_link(?MODULE, [ChildState], []) of
- {ok, Pid} when S#state.parent_pid =/= self() ->
- unlink(Pid);
- _ ->
- ignore
- end;
+ wx_object:start_link(?MODULE, [ChildState], []);
{hide, Actors} ->
send_viewer_event(S, {delete_actors, Actors});
{show, Actors} ->
@@ -356,12 +351,7 @@ handle_event(#wx{event = #wxKey{rawCode = KeyCode}}, S) ->
case lists:keysearch(?DEFAULT_FILTER_NAME, #filter.name, S#state.filters) of
{value, F} when is_record(F, filter) ->
ChildState= S#state{active_filter = F#filter.name},
- case wx_object:start_link(?MODULE, [ChildState], []) of
- {ok, Pid} when S#state.parent_pid =/= self() ->
- unlink(Pid);
- _ ->
- ignore
- end;
+ wx_object:start_link(?MODULE, [ChildState], []);
false ->
ignore
end,
@@ -370,12 +360,7 @@ handle_event(#wx{event = #wxKey{rawCode = KeyCode}}, S) ->
case catch lists:nth(Int-$0, S#state.filters) of
F when is_record(F, filter) ->
ChildState= S#state{active_filter = F#filter.name},
- case wx_object:start_link(?MODULE, [ChildState], []) of
- {ok, Pid} when S#state.parent_pid =/= self() ->
- unlink(Pid);
- _ ->
- ignore
- end;
+ wx_object:start_link(?MODULE, [ChildState], []);
{'EXIT', _} ->
ignore
end,
diff --git a/lib/et/test/Makefile b/lib/et/test/Makefile
index 7b4db7b0c3..3817079b5f 100644
--- a/lib/et/test/Makefile
+++ b/lib/et/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2009-2011. All Rights Reserved.
+# Copyright Ericsson AB 2009-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/et/vsn.mk b/lib/et/vsn.mk
index 090d43d52f..167c9d9594 100644
--- a/lib/et/vsn.mk
+++ b/lib/et/vsn.mk
@@ -1 +1 @@
-ET_VSN = 1.4.4.1
+ET_VSN = 1.4.4.2
diff --git a/lib/eunit/doc/overview.edoc b/lib/eunit/doc/overview.edoc
index ad449cb6fc..b4af31ae6a 100644
--- a/lib/eunit/doc/overview.edoc
+++ b/lib/eunit/doc/overview.edoc
@@ -723,8 +723,12 @@ A <em>simple test object</em> is one of the following:
```fun some_function/0'''
```fun some_module:some_function/0'''
</li>
- <li>A pair of atoms `{ModuleName, FunctionName}', referring to the
- function `ModuleName:FunctionName/0'</li>
+ <li>A tuple `{test, ModuleName, FunctionName}', where `ModuleName' and
+ `FunctionName' are atoms, referring to the function
+ `ModuleName:FunctionName/0'</li>
+ <li>(Obsolete) A pair of atoms `{ModuleName, FunctionName}', equivalent to
+ `{test, ModuleName, FunctionName}' if nothing else matches first. This
+ might be removed in a future version.</li>
<li>A pair `{LineNumber, SimpleTest}', where `LineNumber' is a
nonnegative integer and `SimpleTest' is another simple test
object. `LineNumber' should indicate the source line of the test.
diff --git a/lib/eunit/doc/src/notes.xml b/lib/eunit/doc/src/notes.xml
index 34e8a47e3d..b797be0ccb 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>2011</year>
+ <year>2012</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -32,6 +32,38 @@
</header>
<p>This document describes the changes made to the EUnit application.</p>
+<section><title>Eunit 2.2.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ New option 'no_tty' to silent the default tty report.</p>
+ <p>
+ Recognize the new stacktrace format introduced in R15,
+ adding location information. (Thanks to Klas Johansson.)</p>
+ <p>
+ Improve layout of error messages, printing the stack
+ trace before the error term.</p>
+ <p>
+ Heuristically detect and report bad return values from
+ generators and instantiators. E.g., "ok" will not be
+ interpreted as a module name, and a warning will be
+ printed.</p>
+ <p>
+ New test representation {test,M,F} for completeness along
+ with {generator,M,F}. Tuples {M,F} are deprecated.</p>
+ <p>
+ Use UTF-8 as encoding in Surefire output files. (Thanks
+ to Lukas Larsson.)</p>
+ <p>
+ Own Id: OTP-10173</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Eunit 2.2.2</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/eunit/include/eunit.hrl b/lib/eunit/include/eunit.hrl
index db68d8ae60..8ebdb6ba16 100644
--- a/lib/eunit/include/eunit.hrl
+++ b/lib/eunit/include/eunit.hrl
@@ -1,3 +1,4 @@
+%% -*- coding: utf-8 -*-
%% 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
@@ -13,7 +14,7 @@
%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
%% USA
%%
-%% Copyright (C) 2004-2006 Micka�l R�mond, Richard Carlsson
+%% Copyright (C) 2004-2006 Mickaël Rémond, Richard Carlsson
%% Including this file turns on testing and defines TEST, unless NOTEST
%% is defined before the file is included. If both NOTEST and TEST are
@@ -25,11 +26,12 @@
%% will become undefined. NODEBUG also implies NOASSERT, unless testing
%% is enabled.
%%
-%% If including this file causes TEST to be defined, then NOASSERT will
-%% be undefined, even if it was previously defined and even if NODEBUG
-%% is defined. If both ASSERT and NOASSERT are defined before the file
-%% is included, then ASSERT takes precedence, and NOASSERT will become
-%% undefined regardless of TEST.
+%% Defining NOASSERT disables asserts. NODEBUG implies NOASSERT unless
+%% testing is enabled. If including this file causes TEST to be defined,
+%% then NOASSERT will be undefined, even if it was previously defined and
+%% even if NODEBUG is defined. If both ASSERT and NOASSERT are defined
+%% before the file is included, then ASSERT takes precedence, and NOASSERT
+%% will become undefined regardless of TEST.
%%
%% After including this file, EUNIT will be defined if and only if TEST
%% is defined.
@@ -123,13 +125,13 @@
-ifndef(UNDER_EUNIT).
-define(UNDER_EUNIT,
(?MATCHES({current_function,{eunit_proc,_,_}},
- .erlang:process_info(.erlang:group_leader(),
- current_function)))).
+ erlang:process_info(erlang:group_leader(),
+ current_function)))).
-endif.
--ifdef(NOASSERT).
%% The plain assert macro should be defined to do nothing if this file
%% is included when debugging/testing is turned off.
+-ifdef(NOASSERT).
-ifndef(assert).
-define(assert(BoolExpr),ok).
-endif.
@@ -141,14 +143,14 @@
((fun () ->
case (BoolExpr) of
true -> ok;
- __V -> .erlang:error({assertion_failed,
- [{module, ?MODULE},
- {line, ?LINE},
- {expression, (??BoolExpr)},
- {expected, true},
- {value, case __V of false -> __V;
- _ -> {not_a_boolean,__V}
- end}]})
+ __V -> erlang:error({assertion_failed,
+ [{module, ?MODULE},
+ {line, ?LINE},
+ {expression, (??BoolExpr)},
+ {expected, true},
+ {value, case __V of false -> __V;
+ _ -> {not_a_boolean,__V}
+ end}]})
end
end)())).
-endif.
@@ -169,12 +171,12 @@
((fun () ->
case (Expr) of
Guard -> ok;
- __V -> .erlang:error({assertMatch_failed,
- [{module, ?MODULE},
- {line, ?LINE},
- {expression, (??Expr)},
- {pattern, (??Guard)},
- {value, __V}]})
+ __V -> erlang:error({assertMatch_failed,
+ [{module, ?MODULE},
+ {line, ?LINE},
+ {expression, (??Expr)},
+ {pattern, (??Guard)},
+ {value, __V}]})
end
end)())).
-endif.
@@ -188,12 +190,12 @@
((fun () ->
__V = (Expr),
case __V of
- Guard -> .erlang:error({assertNotMatch_failed,
- [{module, ?MODULE},
- {line, ?LINE},
- {expression, (??Expr)},
- {pattern, (??Guard)},
- {value, __V}]});
+ Guard -> erlang:error({assertNotMatch_failed,
+ [{module, ?MODULE},
+ {line, ?LINE},
+ {expression, (??Expr)},
+ {pattern, (??Guard)},
+ {value, __V}]});
_ -> ok
end
end)())).
@@ -209,12 +211,12 @@
((fun (__X) ->
case (Expr) of
__X -> ok;
- __V -> .erlang:error({assertEqual_failed,
- [{module, ?MODULE},
- {line, ?LINE},
- {expression, (??Expr)},
- {expected, __X},
- {value, __V}]})
+ __V -> erlang:error({assertEqual_failed,
+ [{module, ?MODULE},
+ {line, ?LINE},
+ {expression, (??Expr)},
+ {expected, __X},
+ {value, __V}]})
end
end)(Expect))).
-endif.
@@ -227,11 +229,11 @@
-define(assertNotEqual(Unexpected, Expr),
((fun (__X) ->
case (Expr) of
- __X -> .erlang:error({assertNotEqual_failed,
- [{module, ?MODULE},
- {line, ?LINE},
- {expression, (??Expr)},
- {value, __X}]});
+ __X -> erlang:error({assertNotEqual_failed,
+ [{module, ?MODULE},
+ {line, ?LINE},
+ {expression, (??Expr)},
+ {value, __X}]});
_ -> ok
end
end)(Unexpected))).
@@ -247,7 +249,7 @@
-define(assertException(Class, Term, Expr),
((fun () ->
try (Expr) of
- __V -> .erlang:error({assertException_failed,
+ __V -> erlang:error({assertException_failed,
[{module, ?MODULE},
{line, ?LINE},
{expression, (??Expr)},
@@ -258,16 +260,16 @@
catch
Class:Term -> ok;
__C:__T ->
- .erlang:error({assertException_failed,
- [{module, ?MODULE},
- {line, ?LINE},
- {expression, (??Expr)},
- {pattern,
- "{ "++(??Class)++" , "++(??Term)
- ++" , [...] }"},
- {unexpected_exception,
- {__C, __T,
- .erlang:get_stacktrace()}}]})
+ erlang:error({assertException_failed,
+ [{module, ?MODULE},
+ {line, ?LINE},
+ {expression, (??Expr)},
+ {pattern,
+ "{ "++(??Class)++" , "++(??Term)
+ ++" , [...] }"},
+ {unexpected_exception,
+ {__C, __T,
+ erlang:get_stacktrace()}}]})
end
end)())).
-endif.
@@ -298,17 +300,17 @@
Class ->
case __T of
Term ->
- .erlang:error({assertNotException_failed,
- [{module, ?MODULE},
- {line, ?LINE},
- {expression, (??Expr)},
- {pattern,
- "{ "++(??Class)++" , "
- ++(??Term)++" , [...] }"},
- {unexpected_exception,
- {__C, __T,
- .erlang:get_stacktrace()
- }}]});
+ erlang:error({assertNotException_failed,
+ [{module, ?MODULE},
+ {line, ?LINE},
+ {expression, (??Expr)},
+ {pattern,
+ "{ "++(??Class)++" , "
+ ++(??Term)++" , [...] }"},
+ {unexpected_exception,
+ {__C, __T,
+ erlang:get_stacktrace()
+ }}]});
_ -> ok
end;
_ -> ok
@@ -323,17 +325,17 @@
%% require EUnit to be present at runtime, or at least eunit_lib.)
%% these can be used for simply running commands in a controlled way
--define(_cmd_(Cmd), (.eunit_lib:command(Cmd))).
+-define(_cmd_(Cmd), (eunit_lib:command(Cmd))).
-define(cmdStatus(N, Cmd),
((fun () ->
case ?_cmd_(Cmd) of
{(N), __Out} -> __Out;
- {__N, _} -> .erlang:error({command_failed,
- [{module, ?MODULE},
- {line, ?LINE},
- {command, (Cmd)},
- {expected_status,(N)},
- {status,__N}]})
+ {__N, _} -> erlang:error({command_failed,
+ [{module, ?MODULE},
+ {line, ?LINE},
+ {command, (Cmd)},
+ {expected_status,(N)},
+ {status,__N}]})
end
end)())).
-define(_cmdStatus(N, Cmd), ?_test(?cmdStatus(N, Cmd))).
@@ -349,12 +351,12 @@
((fun () ->
case ?_cmd_(Cmd) of
{(N), _} -> ok;
- {__N, _} -> .erlang:error({assertCmd_failed,
- [{module, ?MODULE},
- {line, ?LINE},
- {command, (Cmd)},
- {expected_status,(N)},
- {status,__N}]})
+ {__N, _} -> erlang:error({assertCmd_failed,
+ [{module, ?MODULE},
+ {line, ?LINE},
+ {command, (Cmd)},
+ {expected_status,(N)},
+ {status,__N}]})
end
end)())).
-endif.
@@ -367,12 +369,12 @@
((fun () ->
case ?_cmd_(Cmd) of
{_, (T)} -> ok;
- {_, __T} -> .erlang:error({assertCmdOutput_failed,
- [{module, ?MODULE},
- {line, ?LINE},
- {command,(Cmd)},
- {expected_output,(T)},
- {output,__T}]})
+ {_, __T} -> erlang:error({assertCmdOutput_failed,
+ [{module, ?MODULE},
+ {line, ?LINE},
+ {command,(Cmd)},
+ {expected_output,(T)},
+ {output,__T}]})
end
end)())).
-endif.
@@ -393,12 +395,12 @@
-else.
-define(debugMsg(S),
(begin
- .io:fwrite(user, <<"~s:~w:~w: ~s\n">>,
- [?FILE, ?LINE, self(), S]),
+ io:fwrite(user, <<"~s:~w:~w: ~s\n">>,
+ [?FILE, ?LINE, self(), S]),
ok
end)).
-define(debugHere, (?debugMsg("<-"))).
--define(debugFmt(S, As), (?debugMsg(.io_lib:format((S), (As))))).
+-define(debugFmt(S, As), (?debugMsg(io_lib:format((S), (As))))).
-define(debugVal(E),
((fun (__V) ->
?debugFmt(<<"~s = ~P">>, [(??E), __V, 15]),
diff --git a/lib/eunit/src/Makefile b/lib/eunit/src/Makefile
index 0a2e71cf7b..e88e28df83 100644
--- a/lib/eunit/src/Makefile
+++ b/lib/eunit/src/Makefile
@@ -104,10 +104,10 @@ $(OBJECTS): $(PARSE_TRANSFORM_BIN)
# ----------------------------------------------------
$(APP_TARGET): $(APP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
# ----------------------------------------------------
# Release Target
diff --git a/lib/eunit/src/eunit.app.src b/lib/eunit/src/eunit.app.src
index 5e16dfa2ce..431abac98b 100644
--- a/lib/eunit/src/eunit.app.src
+++ b/lib/eunit/src/eunit.app.src
@@ -14,7 +14,6 @@
eunit_striptests,
eunit_surefire,
eunit_test,
- eunit_tests,
eunit_tty]},
{registered,[]},
{applications, [kernel,stdlib]},
diff --git a/lib/eunit/src/eunit.erl b/lib/eunit/src/eunit.erl
index 95857e83c8..5763949519 100644
--- a/lib/eunit/src/eunit.erl
+++ b/lib/eunit/src/eunit.erl
@@ -1,3 +1,4 @@
+%% -*- coding: utf-8 -*-
%% 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
@@ -13,8 +14,8 @@
%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
%% USA
%%
-%% @copyright 2004-2009 Micka�l R�mond, Richard Carlsson
-%% @author Micka�l R�mond <[email protected]>
+%% @copyright 2004-2009 Mickaël Rémond, Richard Carlsson
+%% @author Mickaël Rémond <[email protected]>
%% [http://www.process-one.net/]
%% @author Richard Carlsson <[email protected]>
%% @version {@version}, {@date} {@time}
@@ -139,7 +140,7 @@ test(Tests, Options) ->
%% @private
%% @doc See {@link test/2}.
test(Server, Tests, Options) ->
- Listeners = [eunit_tty:start(Options) | listeners(Options)],
+ Listeners = listeners(Options),
Serial = eunit_serial:start(Listeners),
case eunit_server:start_test(Server, Serial, Tests, Options) of
{ok, Reference} -> test_run(Reference, Listeners);
@@ -194,7 +195,10 @@ submit(Server, T, Options) ->
eunit_server:start_test(Server, Dummy, T, Options).
listeners(Options) ->
- Ps = start_listeners(proplists:get_all_values(report, Options)),
+ %% note that eunit_tty must always run, because it sends the final
+ %% {result,...} message that the test_run() function is waiting for
+ Ls = [{eunit_tty, Options} | proplists:get_all_values(report, Options)],
+ Ps = start_listeners(Ls),
%% the event_log option is for debugging, to view the raw events
case proplists:get_value(event_log, Options) of
undefined ->
diff --git a/lib/eunit/src/eunit_autoexport.erl b/lib/eunit/src/eunit_autoexport.erl
index 099bcb222e..36ae3b71d7 100644
--- a/lib/eunit/src/eunit_autoexport.erl
+++ b/lib/eunit/src/eunit_autoexport.erl
@@ -80,10 +80,9 @@ rewrite([F | Fs], As, Module, Test) ->
rewrite(Fs, [F | As], Module, Test);
rewrite([], As, Module, Test) ->
{if Test ->
- EUnit = {record_field,0,{atom,0,''},{atom,0,eunit}},
[{function,0,test,0,
[{clause,0,[],[],
- [{call,0,{remote,0,EUnit,{atom,0,test}},
+ [{call,0,{remote,0,{atom,0,eunit},{atom,0,test}},
[{atom,0,Module}]}]}]}
| As];
true ->
@@ -92,9 +91,7 @@ rewrite([], As, Module, Test) ->
Test}.
module_decl(Name, M, Fs, Exports) ->
- Module = if is_atom(Name) -> Name;
- true -> list_to_atom(packages:concat(Name))
- end,
+ Module = Name,
{Fs1, Test} = rewrite(Fs, [], Module, true),
Es = if Test -> [{test,0} | Exports];
true -> Exports
diff --git a/lib/eunit/src/eunit_data.erl b/lib/eunit/src/eunit_data.erl
index 392d378a0e..0350f9bf6e 100644
--- a/lib/eunit/src/eunit_data.erl
+++ b/lib/eunit/src/eunit_data.erl
@@ -83,6 +83,7 @@
%% SimpleTest = TestFunction | {Line::integer(), SimpleTest}
%%
%% TestFunction = () -> any()
+%% | {test, M::moduleName(), F::functionName()}
%% | {M::moduleName(), F::functionName()}.
%%
%% AbstractTestFunction = (X::any()) -> any()
@@ -95,7 +96,6 @@
%%
%% @type moduleName() = atom()
%% @type functionName() = atom()
-%% @type arity() = integer()
%% @type appName() = atom()
%% @type fileName() = string()
@@ -156,8 +156,9 @@ iter_prev(#iter{prev = [T | Ts]} = I) ->
%% @spec (tests()) -> none | {testItem(), tests()}
%% @type testItem() = #test{} | #group{}
%% @throws {bad_test, term()}
-%% | {generator_failed, exception()}
-%% | {no_such_function, eunit_lib:mfa()}
+%% | {generator_failed, {{M::atom(),F::atom(),A::integer()},
+%% exception()}}
+%% | {no_such_function, mfa()}
%% | {module_not_found, moduleName()}
%% | {application_not_found, appName()}
%% | {file_read_error, {Reason::atom(), Message::string(),
@@ -221,17 +222,27 @@ parse({foreachx, P, S1, C1, Ps} = T)
[] ->
{data, []}
end;
-parse({generator, F} = T) when is_function(F) ->
+parse({generator, F}) when is_function(F) ->
+ {module, M} = erlang:fun_info(F, module),
+ {name, N} = erlang:fun_info(F, name),
+ {arity, A} = erlang:fun_info(F, arity),
+ parse({generator, F, {M,N,A}});
+parse({generator, F, {M,N,A}} = T)
+ when is_function(F), is_atom(M), is_atom(N), is_integer(A) ->
check_arity(F, 0, T),
%% use run_testfun/1 to handle wrapper exceptions
case eunit_test:run_testfun(F) of
{ok, T1} ->
+ case eunit_lib:is_not_test(T1) of
+ true -> throw({bad_generator, {{M,N,A}, T1}});
+ false -> ok
+ end,
{data, T1};
{error, {Class, Reason, Trace}} ->
- throw({generator_failed, {Class, Reason, Trace}})
+ throw({generator_failed, {{M,N,A}, {Class, Reason, Trace}}})
end;
parse({generator, M, F}) when is_atom(M), is_atom(F) ->
- parse({generator, eunit_test:function_wrapper(M, F)});
+ parse({generator, eunit_test:mf_wrapper(M, F), {M,F,0}});
parse({inorder, T}) ->
group(#group{tests = T, order = inorder});
parse({inparallel, T}) ->
@@ -421,8 +432,11 @@ parse_simple(F) ->
parse_function(F) when is_function(F) ->
check_arity(F, 0, F),
#test{f = F, location = eunit_lib:fun_parent(F)};
-parse_function({M,F}) when is_atom(M), is_atom(F) ->
- #test{f = eunit_test:function_wrapper(M, F), location = {M, F, 0}};
+parse_function({test, M, F}) when is_atom(M), is_atom(F) ->
+ #test{f = eunit_test:mf_wrapper(M, F), location = {M, F, 0}};
+parse_function({M, F}) when is_atom(M), is_atom(F) ->
+ %% {M,F} is now considered obsolete; use {test,M,F} instead
+ parse_function({test, M, F});
parse_function(F) ->
bad_test(F).
@@ -580,7 +594,7 @@ testfuns(Es, M, TestSuffix, GeneratorSuffix) ->
N = atom_to_list(F),
case lists:suffix(TestSuffix, N) of
true ->
- [{M,F} | Fs];
+ [{test, M, F} | Fs];
false ->
case lists:suffix(GeneratorSuffix, N) of
true ->
@@ -723,6 +737,7 @@ data_test_() ->
Tests = [T,T,T],
[?_assertMatch(ok, eunit:test(T)),
?_assertMatch(error, eunit:test(Fail)),
+ ?_assertMatch(ok, eunit:test({test, ?MODULE, trivial_test})),
?_assertMatch(ok, eunit:test({generator, fun () -> Tests end})),
?_assertMatch(ok, eunit:test({generator, fun generator/0})),
?_assertMatch(ok, eunit:test({generator, ?MODULE, generator_exported_})),
@@ -740,6 +755,12 @@ data_test_() ->
%%?_test({foreach, Setup, [T, T, T]})
].
+trivial_test() ->
+ ok.
+
+trivial_generator_test_() ->
+ [?_test(ok)].
+
lazy_test_() ->
{spawn, [?_test(undefined = put(count, 0)),
lazy_gen(7),
diff --git a/lib/eunit/src/eunit_lib.erl b/lib/eunit/src/eunit_lib.erl
index 1c41e229c5..809cb7ab7b 100644
--- a/lib/eunit/src/eunit_lib.erl
+++ b/lib/eunit/src/eunit_lib.erl
@@ -1,3 +1,4 @@
+%% -*- coding: utf-8 -*-
%% 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
@@ -13,8 +14,8 @@
%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
%% USA
%%
-%% @copyright 2004-2007 Micka�l R�mond, Richard Carlsson
-%% @author Micka�l R�mond <[email protected]>
+%% @copyright 2004-2007 Mickaël Rémond, Richard Carlsson
+%% @author Mickaël Rémond <[email protected]>
%% [http://www.process-one.net/]
%% @author Richard Carlsson <[email protected]>
%% @private
@@ -30,7 +31,8 @@
-export([dlist_next/1, uniq/1, fun_parent/1, is_string/1, command/1,
command/2, command/3, trie_new/0, trie_store/2, trie_match/2,
split_node/1, consult_file/1, list_dir/1, format_exit_term/1,
- format_exception/1, format_exception/2, format_error/1]).
+ format_exception/1, format_exception/2, format_error/1,
+ is_not_test/1]).
%% Type definitions for describing exceptions
@@ -39,13 +41,10 @@
%%
%% @type exceptionClass() = error | exit | throw
%%
-%% @type stackTrace() = [{moduleName(), functionName(),
-%% arity() | argList()}]
+%% @type stackTrace() = [{moduleName(), functionName(), arity() | argList()}]
%%
%% @type moduleName() = atom()
%% @type functionName() = atom()
-%% @type arity() = integer()
-%% @type mfa() = {moduleName(), functionName(), arity()}
%% @type argList() = [term()]
%% @type fileName() = string()
@@ -59,8 +58,9 @@ format_exception({Class,Term,Trace}, Depth)
when is_atom(Class), is_list(Trace) ->
case is_stacktrace(Trace) of
true ->
- io_lib:format("~w:~P\n~s",
- [Class, Term, Depth, format_stacktrace(Trace)]);
+ io_lib:format("~s**~w:~s",
+ [format_stacktrace(Trace), Class,
+ format_term(Term, Depth)]);
false ->
format_term(Term, Depth)
end;
@@ -86,6 +86,12 @@ analyze_exit_term(Term) ->
is_stacktrace([]) ->
true;
+is_stacktrace([{M,F,A,L}|Fs])
+ when is_atom(M), is_atom(F), is_integer(A), is_list(L) ->
+ is_stacktrace(Fs);
+is_stacktrace([{M,F,As,L}|Fs])
+ when is_atom(M), is_atom(F), is_list(As), is_list(L) ->
+ is_stacktrace(Fs);
is_stacktrace([{M,F,A}|Fs]) when is_atom(M), is_atom(F), is_integer(A) ->
is_stacktrace(Fs);
is_stacktrace([{M,F,As}|Fs]) when is_atom(M), is_atom(F), is_list(As) ->
@@ -96,10 +102,11 @@ is_stacktrace(_) ->
format_stacktrace(Trace) ->
format_stacktrace(Trace, "in function", "in call from").
-format_stacktrace([{M,F,A}|Fs], Pre, Pre1) when is_integer(A) ->
- [io_lib:fwrite(" ~s ~w:~w/~w\n", [Pre, M, F, A])
+format_stacktrace([{M,F,A,L}|Fs], Pre, Pre1) when is_integer(A) ->
+ [io_lib:fwrite("~s ~w:~w/~w~s\n",
+ [Pre, M, F, A, format_stacktrace_location(L)])
| format_stacktrace(Fs, Pre1, Pre1)];
-format_stacktrace([{M,F,As}|Fs], Pre, Pre1) when is_list(As) ->
+format_stacktrace([{M,F,As,L}|Fs], Pre, Pre1) when is_list(As) ->
A = length(As),
C = case is_op(M,F,A) of
true when A =:= 1 ->
@@ -112,12 +119,23 @@ format_stacktrace([{M,F,As}|Fs], Pre, Pre1) when is_list(As) ->
false ->
io_lib:fwrite("~w(~s)", [F,format_arglist(As)])
end,
- [io_lib:fwrite(" ~s ~w:~w/~w\n called as ~s\n",
- [Pre,M,F,A,C])
+ [io_lib:fwrite("~s ~w:~w/~w~s\n called as ~s\n",
+ [Pre,M,F,A,format_stacktrace_location(L),C])
| format_stacktrace(Fs,Pre1,Pre1)];
+format_stacktrace([{M,F,As}|Fs], Pre, Pre1) ->
+ format_stacktrace([{M,F,As,[]}|Fs], Pre, Pre1);
format_stacktrace([],_Pre,_Pre1) ->
"".
+format_stacktrace_location(Location) ->
+ File = proplists:get_value(file, Location),
+ Line = proplists:get_value(line, Location),
+ if File =/= undefined, Line =/= undefined ->
+ io_lib:format(" (~s, line ~w)", [File, Line]);
+ true ->
+ ""
+ end.
+
format_arg(A) ->
io_lib:format("~P",[A,15]).
@@ -139,9 +157,13 @@ is_op(_M, _F, _A) ->
format_error({bad_test, Term}) ->
error_msg("bad test descriptor", "~P", [Term, 15]);
-format_error({generator_failed, Exception}) ->
- error_msg("test generator failed", "~s",
- [format_exception(Exception)]);
+format_error({bad_generator, {{M,F,A}, Term}}) ->
+ error_msg(io_lib:format("result from generator ~w:~w/~w is not a test",
+ [M,F,A]),
+ "~P", [Term, 15]);
+format_error({generator_failed, {{M,F,A}, Exception}}) ->
+ error_msg(io_lib:format("test generator ~w:~w/~w failed",[M,F,A]),
+ "~s", [format_exception(Exception)]);
format_error({no_such_function, {M,F,A}})
when is_atom(M), is_atom(F), is_integer(A) ->
error_msg(io_lib:format("no such function: ~w:~w/~w", [M,F,A]),
@@ -158,14 +180,55 @@ format_error({setup_failed, Exception}) ->
format_error({cleanup_failed, Exception}) ->
error_msg("context cleanup failed", "~s",
[format_exception(Exception)]);
+format_error({{bad_instantiator, {{M,F,A}, Term}}, _DummyException}) ->
+ error_msg(io_lib:format("result from instantiator ~w:~w/~w is not a test",
+ [M,F,A]),
+ "~P", [Term, 15]);
format_error({instantiation_failed, Exception}) ->
error_msg("instantiation of subtests failed", "~s",
[format_exception(Exception)]).
error_msg(Title, Fmt, Args) ->
- Msg = io_lib:format("::"++Fmt, Args), % gets indentation right
+ Msg = io_lib:format("**"++Fmt, Args), % gets indentation right
io_lib:fwrite("*** ~s ***\n~s\n\n", [Title, Msg]).
+-ifdef(TEST).
+format_exception_test_() ->
+ [?_assertMatch(
+ "\nymmud:rorre"++_,
+ lists:reverse(lists:flatten(
+ format_exception(try erlang:error(dummy)
+ catch C:R -> {C, R, erlang:get_stacktrace()}
+ end)))),
+ ?_assertMatch(
+ "\nymmud:rorre"++_,
+ lists:reverse(lists:flatten(
+ format_exception(try erlang:error(dummy, [a])
+ catch C:R -> {C, R, erlang:get_stacktrace()}
+ end))))].
+-endif.
+
+%% ---------------------------------------------------------------------
+%% detect common return values that are definitely not tests
+
+is_not_test(T) ->
+ case T of
+ ok -> true;
+ error -> true;
+ true -> true;
+ false -> true;
+ undefined -> true;
+ {ok, _} -> true;
+ {error, _} -> true;
+ {'EXIT', _} -> true;
+ N when is_number(N) -> true;
+ [N|_] when is_number(N) -> true;
+ X when is_binary(X) -> true;
+ X when is_pid(X) -> true;
+ X when is_port(X) -> true;
+ X when is_reference(X) -> true;
+ _ -> false
+ end.
%% ---------------------------------------------------------------------
%% Deep list iterator; accepts improper lists/sublists, and also accepts
diff --git a/lib/eunit/src/eunit_surefire.erl b/lib/eunit/src/eunit_surefire.erl
index 2a6cbca14d..cc021625d5 100644
--- a/lib/eunit/src/eunit_surefire.erl
+++ b/lib/eunit/src/eunit_surefire.erl
@@ -1,3 +1,4 @@
+%% -*- coding: utf-8 -*-
%% 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
@@ -13,8 +14,8 @@
%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
%% USA
%%
-%% @author Micka�l R�mond <[email protected]>
-%% @copyright 2009 Micka�l R�mond, Paul Guyot
+%% @author Mickaël Rémond <[email protected]>
+%% @copyright 2009 Mickaël Rémond, Paul Guyot
%% @see eunit
%% @doc Surefire reports for EUnit (Format used by Maven and Atlassian
%% Bamboo for example to integrate test results). Based on initial code
@@ -156,9 +157,33 @@ handle_end(test, Data, St) ->
St#state{testsuites=store_suite(NewTestSuite, TestSuites)}.
%% Cancel group does not give information on the individual cancelled test case
-%% We ignore this event
-handle_cancel(group, _Data, St) ->
- St;
+%% We ignore this event...
+handle_cancel(group, Data, St) ->
+ %% ...except when it tells us that a fixture setup or cleanup failed.
+ case proplists:get_value(reason, Data) of
+ {abort, {SomethingFailed, Exception}}
+ when SomethingFailed =:= setup_failed;
+ SomethingFailed =:= cleanup_failed ->
+ [GroupId|_] = proplists:get_value(id, Data),
+ TestSuites = St#state.testsuites,
+ TestSuite = lookup_suite_by_group_id(GroupId, TestSuites),
+
+ %% We don't have any proper name. Let's give all the
+ %% clues that we have.
+ Name = case SomethingFailed of
+ setup_failed -> "fixture setup ";
+ cleanup_failed -> "fixture cleanup "
+ end
+ ++ io_lib:format("~p", [proplists:get_value(id, Data)]),
+ Desc = format_desc(proplists:get_value(desc, Data)),
+ TestCase = #testcase{
+ name = Name, description = Desc,
+ time = 0, output = <<>>},
+ NewTestSuite = add_testcase_to_testsuite({error, Exception}, TestCase, TestSuite),
+ St#state{testsuites=store_suite(NewTestSuite, TestSuites)};
+ _ ->
+ St
+ end;
handle_cancel(test, Data, St) ->
%% Retrieve existing test suite:
[GroupId|_] = proplists:get_value(id, Data),
@@ -232,7 +257,7 @@ write_reports(TestSuites, XmlDir) ->
write_report(#testsuite{name = Name} = TestSuite, XmlDir) ->
Filename = filename:join(XmlDir, lists:flatten(["TEST-", escape_suitename(Name)], ".xml")),
- case file:open(Filename, [write, raw]) of
+ case file:open(Filename, [write,{encoding,utf8}]) of
{ok, FileDescriptor} ->
try
write_report_to(TestSuite, FileDescriptor)
diff --git a/lib/eunit/src/eunit_test.erl b/lib/eunit/src/eunit_test.erl
index bca49ae626..9cf40a738d 100644
--- a/lib/eunit/src/eunit_test.erl
+++ b/lib/eunit/src/eunit_test.erl
@@ -21,8 +21,7 @@
-module(eunit_test).
--export([run_testfun/1, function_wrapper/2, enter_context/4,
- multi_setup/1]).
+-export([run_testfun/1, mf_wrapper/2, enter_context/4, multi_setup/1]).
-include("eunit.hrl").
@@ -43,8 +42,12 @@ get_stacktrace(Ts) ->
prune_trace([{eunit_data, _, _} | Rest], Tail) ->
prune_trace(Rest, Tail);
+prune_trace([{eunit_data, _, _, _} | Rest], Tail) ->
+ prune_trace(Rest, Tail);
prune_trace([{?MODULE, _, _} | _Rest], Tail) ->
Tail;
+prune_trace([{?MODULE, _, _, _} | _Rest], Tail) ->
+ Tail;
prune_trace([T | Ts], Tail) ->
[T | prune_trace(Ts, Tail)];
prune_trace([], Tail) ->
@@ -258,7 +261,7 @@ macro_test_() ->
%% @type wrapperError() = {no_such_function, mfa()}
%% | {module_not_found, moduleName()}
-function_wrapper(M, F) ->
+mf_wrapper(M, F) ->
fun () ->
try M:F()
catch
@@ -289,12 +292,12 @@ fail(Term) ->
wrapper_test_() ->
{"error handling in function wrapper",
[?_assertException(throw, {module_not_found, eunit_nonexisting},
- run_testfun(function_wrapper(eunit_nonexisting,test))),
+ run_testfun(mf_wrapper(eunit_nonexisting,test))),
?_assertException(throw,
{no_such_function, {?MODULE,nonexisting_test,0}},
- run_testfun(function_wrapper(?MODULE,nonexisting_test))),
+ run_testfun(mf_wrapper(?MODULE,nonexisting_test))),
?_test({error, {error, undef, _T}}
- = run_testfun(function_wrapper(?MODULE,wrapper_test_exported_)))
+ = run_testfun(mf_wrapper(?MODULE,wrapper_test_exported_)))
]}.
%% this must be exported (done automatically by the autoexport transform)
@@ -319,6 +322,17 @@ enter_context(Setup, Cleanup, Instantiate, Callback) ->
R ->
try Instantiate(R) of
T ->
+ case eunit_lib:is_not_test(T) of
+ true ->
+ catch throw(error), % generate a stack trace
+ {module,M} = erlang:fun_info(Instantiate, module),
+ {name,N} = erlang:fun_info(Instantiate, name),
+ {arity,A} = erlang:fun_info(Instantiate, arity),
+ context_error({bad_instantiator, {{M,N,A},T}},
+ error, badarg);
+ false ->
+ ok
+ end,
try Callback(T) %% call back to client code
after
%% Always run cleanup; client may be an idiot
diff --git a/lib/eunit/src/eunit_tty.erl b/lib/eunit/src/eunit_tty.erl
index e3e7b710b2..f21b2da3d3 100644
--- a/lib/eunit/src/eunit_tty.erl
+++ b/lib/eunit/src/eunit_tty.erl
@@ -44,6 +44,7 @@ start(Options) ->
init(Options) ->
St = #state{verbose = proplists:get_bool(verbose, Options)},
+ put(no_tty, proplists:get_bool(no_tty, Options)),
receive
{start, _Reference} ->
if St#state.verbose -> print_header();
@@ -59,30 +60,30 @@ terminate({ok, Data}, St) ->
Cancel = proplists:get_value(cancel, Data, 0),
if Fail =:= 0, Skip =:= 0, Cancel =:= 0 ->
if Pass =:= 0 ->
- io:fwrite(" There were no tests to run.\n");
+ fwrite(" There were no tests to run.\n");
true ->
if St#state.verbose -> print_bar();
true -> ok
end,
if Pass =:= 1 ->
- io:fwrite(" Test passed.\n");
+ fwrite(" Test passed.\n");
true ->
- io:fwrite(" All ~w tests passed.\n", [Pass])
+ fwrite(" All ~w tests passed.\n", [Pass])
end
end,
sync_end(ok);
true ->
print_bar(),
- io:fwrite(" Failed: ~w. Skipped: ~w. Passed: ~w.\n",
- [Fail, Skip, Pass]),
+ fwrite(" Failed: ~w. Skipped: ~w. Passed: ~w.\n",
+ [Fail, Skip, Pass]),
if Cancel =/= 0 ->
- io:fwrite("One or more tests were cancelled.\n");
+ fwrite("One or more tests were cancelled.\n");
true -> ok
end,
sync_end(error)
end;
terminate({error, Reason}, _St) ->
- io:fwrite("Internal error: ~P.\n", [Reason, 25]),
+ fwrite("Internal error: ~P.\n", [Reason, 25]),
sync_end(error).
sync_end(Result) ->
@@ -93,10 +94,10 @@ sync_end(Result) ->
end.
print_header() ->
- io:fwrite("======================== EUnit ========================\n").
+ fwrite("======================== EUnit ========================\n").
print_bar() ->
- io:fwrite("=======================================================\n").
+ fwrite("=======================================================\n").
handle_begin(group, Data, St) ->
@@ -170,18 +171,18 @@ handle_cancel(test, Data, St) ->
indent(N) when is_integer(N), N >= 1 ->
- io:put_chars(lists:duplicate(N * 2, $\s));
+ fwrite(lists:duplicate(N * 2, $\s));
indent(_N) ->
ok.
print_group_start(I, Desc) ->
indent(I),
- io:fwrite("~s\n", [Desc]).
+ fwrite("~s\n", [Desc]).
print_group_end(I, Time) ->
if Time > 0 ->
indent(I),
- io:fwrite("[done in ~.3f s]\n", [Time/1000]);
+ fwrite("[done in ~.3f s]\n", [Time/1000]);
true ->
ok
end.
@@ -198,9 +199,9 @@ print_test_begin(I, Data) ->
end,
case proplists:get_value(source, Data) of
{Module, Name, _Arity} ->
- io:fwrite("~s:~s ~s~s...", [Module, L, Name, D]);
+ fwrite("~s:~s ~s~s...", [Module, L, Name, D]);
_ ->
- io:fwrite("~s~s...", [L, D])
+ fwrite("~s~s...", [L, D])
end.
print_test_end(Data) ->
@@ -208,36 +209,35 @@ print_test_end(Data) ->
T = if Time > 0 -> io_lib:fwrite("[~.3f s] ", [Time/1000]);
true -> ""
end,
- io:fwrite("~sok\n", [T]).
+ fwrite("~sok\n", [T]).
print_test_error({error, Exception}, Data) ->
Output = proplists:get_value(output, Data),
- io:fwrite("*failed*\n::~s",
- [eunit_lib:format_exception(Exception)]),
+ fwrite("*failed*\n~s", [eunit_lib:format_exception(Exception)]),
case Output of
<<>> ->
- io:put_chars("\n\n");
+ fwrite("\n\n");
<<Text:800/binary, _:1/binary, _/binary>> ->
- io:fwrite(" output:<<\"~s\">>...\n\n", [Text]);
+ fwrite(" output:<<\"~s\">>...\n\n", [Text]);
_ ->
- io:fwrite(" output:<<\"~s\">>\n\n", [Output])
+ fwrite(" output:<<\"~s\">>\n\n", [Output])
end;
print_test_error({skipped, Reason}, _) ->
- io:fwrite("*did not run*\n::~s\n", [format_skipped(Reason)]).
+ fwrite("*did not run*\n::~s\n", [format_skipped(Reason)]).
format_skipped({module_not_found, M}) ->
- io_lib:format("missing module: ~w", [M]);
+ io_lib:fwrite("missing module: ~w", [M]);
format_skipped({no_such_function, {M,F,A}}) ->
- io_lib:format("no such function: ~w:~w/~w", [M,F,A]).
+ io_lib:fwrite("no such function: ~w:~w/~w", [M,F,A]).
print_test_cancel(Reason) ->
- io:fwrite(format_cancel(Reason)).
+ fwrite(format_cancel(Reason)).
print_group_cancel(_I, {blame, _}) ->
ok;
print_group_cancel(I, Reason) ->
indent(I),
- io:fwrite(format_cancel(Reason)).
+ fwrite(format_cancel(Reason)).
format_cancel(undefined) ->
"*skipped*\n";
@@ -253,3 +253,12 @@ format_cancel({exit, Reason}) ->
[Reason, 15]);
format_cancel({abort, Reason}) ->
eunit_lib:format_error(Reason).
+
+fwrite(String) ->
+ fwrite(String, []).
+
+fwrite(String, Args) ->
+ case get(no_tty) of
+ false -> io:fwrite(String, Args);
+ true -> ok
+ end.
diff --git a/lib/eunit/test/Makefile b/lib/eunit/test/Makefile
index 1586dd8857..e4ddf4e42c 100644
--- a/lib/eunit/test/Makefile
+++ b/lib/eunit/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2009-2010. All Rights Reserved.
+# Copyright Ericsson AB 2009-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/eunit/vsn.mk b/lib/eunit/vsn.mk
index 445c070e96..174d197117 100644
--- a/lib/eunit/vsn.mk
+++ b/lib/eunit/vsn.mk
@@ -1 +1 @@
-EUNIT_VSN = 2.2.2
+EUNIT_VSN = 2.2.3
diff --git a/lib/gs/contribs/bonk/Makefile b/lib/gs/contribs/bonk/Makefile
index dc92149784..d160ca8b73 100644
--- a/lib/gs/contribs/bonk/Makefile
+++ b/lib/gs/contribs/bonk/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2009. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -81,12 +81,12 @@ clean:
# ----------------------------------------------------
$(EBIN)/$(TOOLNAME).gif: $(TOOLNAME).gif
- rm -f $@
- cp $(TOOLNAME).gif $@
+ $(gen_verbose)rm -f $@
+ $(V_at)cp $(TOOLNAME).gif $@
$(EBIN)/$(TOOLNAME).tool: $(TOOLNAME).tool
- rm -f $@
- cp $(TOOLNAME).tool $@
+ $(gen_verbose)rm -f $@
+ $(V_at)cp $(TOOLNAME).tool $@
# ----------------------------------------------------
# Release Target
diff --git a/lib/gs/contribs/cols/Makefile b/lib/gs/contribs/cols/Makefile
index 4ce4204c92..3036e9565e 100644
--- a/lib/gs/contribs/cols/Makefile
+++ b/lib/gs/contribs/cols/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2009. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -74,16 +74,16 @@ clean:
# ----------------------------------------------------
$(EBIN)/$(TOOLNAME).gif: $(TOOLNAME).gif
- rm -f $@
- cp $(TOOLNAME).gif $@
+ $(gen_verbose)rm -f $@
+ $(V_at)cp $(TOOLNAME).gif $@
$(EBIN)/$(TOOLNAME).tool: $(TOOLNAME).tool
- rm -f $@
- cp $(TOOLNAME).tool $@
+ $(gen_verbose)rm -f $@
+ $(V_at)cp $(TOOLNAME).tool $@
$(EBIN)/help.gif: help.gif
- rm -f $@
- cp help.gif $@
+ $(gen_verbose)rm -f $@
+ $(V_at)cp help.gif $@
# ----------------------------------------------------
# Release Target
diff --git a/lib/gs/contribs/mandel/Makefile b/lib/gs/contribs/mandel/Makefile
index 451ece2c8f..308ba0cbb0 100644
--- a/lib/gs/contribs/mandel/Makefile
+++ b/lib/gs/contribs/mandel/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2009. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -73,16 +73,16 @@ clean:
# ----------------------------------------------------
$(EBIN)/$(TOOLNAME).gif: $(TOOLNAME).gif
- rm -f $@
- cp $(TOOLNAME).gif $@
+ $(gen_verbose)rm -f $@
+ $(V_at)cp $(TOOLNAME).gif $@
$(EBIN)/$(TOOLNAME).tool: $(TOOLNAME).tool
- rm -f $@
- cp $(TOOLNAME).tool $@
+ $(gen_verbose)rm -f $@
+ $(V_at)cp $(TOOLNAME).tool $@
$(EBIN)/help.gif: help.gif
- rm -f $@
- cp help.gif $@
+ $(gen_verbose)rm -f $@
+ $(V_at)cp help.gif $@
# ----------------------------------------------------
# Release Target
diff --git a/lib/gs/contribs/mandel/mandel.erl b/lib/gs/contribs/mandel/mandel.erl
index 8ecd649532..a7a786ce98 100644
--- a/lib/gs/contribs/mandel/mandel.erl
+++ b/lib/gs/contribs/mandel/mandel.erl
@@ -1,3 +1,4 @@
+%% -*- coding: utf-8 -*-
%%
%% %CopyrightBegin%
%%
@@ -34,7 +35,7 @@
%%%-----------------------------------------------------------------
%%% Distributed Mandelbrot program.
%%% Originally written i C++/rpc/lwp/interviews by Klas Eriksson.(1200 lines)
-%%% Rewritten in Erlang by Klas Eriksson and Martin Bj�rklund.
+%%% Rewritten in Erlang by Klas Eriksson and Martin Björklund.
%%%-----------------------------------------------------------------
%% unix>erl -sname foo (all nodes will get the same name)
diff --git a/lib/gs/contribs/othello/Makefile b/lib/gs/contribs/othello/Makefile
index 5f09f2ed13..f9d131c315 100644
--- a/lib/gs/contribs/othello/Makefile
+++ b/lib/gs/contribs/othello/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2009. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -76,12 +76,12 @@ clean:
# ----------------------------------------------------
$(EBIN)/$(TOOLNAME).gif: $(TOOLNAME).gif
- rm -f $@
- cp $(TOOLNAME).gif $@
+ $(gen_verbose)rm -f $@
+ $(V_at)cp $(TOOLNAME).gif $@
$(EBIN)/$(TOOLNAME).tool: $(TOOLNAME).tool
- rm -f $@
- cp $(TOOLNAME).tool $@
+ $(gen_verbose)rm -f $@
+ $(V_at)cp $(TOOLNAME).tool $@
# ----------------------------------------------------
# Release Target
diff --git a/lib/gs/doc/src/Makefile b/lib/gs/doc/src/Makefile
index 14204c52fc..bfc5e4c317 100644
--- a/lib/gs/doc/src/Makefile
+++ b/lib/gs/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2009. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/gs/doc/src/notes.xml b/lib/gs/doc/src/notes.xml
index d1b74ecfb1..69b1e90ba5 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>2011</year>
+ <year>2004</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
diff --git a/lib/gs/examples/Makefile b/lib/gs/examples/Makefile
index d214cd2256..ee77990191 100644
--- a/lib/gs/examples/Makefile
+++ b/lib/gs/examples/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2009. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/gs/src/Makefile b/lib/gs/src/Makefile
index a9904161a4..0a63d5466e 100644
--- a/lib/gs/src/Makefile
+++ b/lib/gs/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2011. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -91,13 +91,13 @@ clean:
# ----------------------------------------------------
gstk_generic.hrl: gs_make.erl ../ebin/gs_make.$(EMULATOR) ../ebin/gs.$(EMULATOR)
- $(ERL) -pa $(EBIN) -s gs_make -s erlang halt -noshell
+ $(gen_verbose)$(ERL) -pa $(EBIN) -s gs_make -s erlang halt -noshell
$(APP_TARGET): $(APP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
$(GSTK_GENERIC_TARGET): gstk_generic.hrl
diff --git a/lib/gs/src/gstk_editor.erl b/lib/gs/src/gstk_editor.erl
index e918d93147..cb422aef95 100644
--- a/lib/gs/src/gstk_editor.erl
+++ b/lib/gs/src/gstk_editor.erl
@@ -90,9 +90,9 @@
%% type
%%
-%.t tag names 2.7 -> red blue (blue �r f�rgen)
-%.t tag add blue 2.1 2.10 tagga text
-%.t tag configure blue -foregr blue skapa tag
+%.t tag names 2.7 -> red blue (blue is the colour)
+%.t tag add blue 2.1 2.10 tag the text
+%.t tag configure blue -foregr blue create tag
% .t index end -> MaxRows.cols
% .t yview moveto (Row-1)/MaxRows
diff --git a/lib/gs/tcl/Makefile.in b/lib/gs/tcl/Makefile.in
index 0bccb60c7b..c22a7037e8 100644
--- a/lib/gs/tcl/Makefile.in
+++ b/lib/gs/tcl/Makefile.in
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2002-2009. All Rights Reserved.
+# Copyright Ericsson AB 2002-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/hipe/Makefile b/lib/hipe/Makefile
index 2294f98158..a9e24f4d17 100644
--- a/lib/hipe/Makefile
+++ b/lib/hipe/Makefile
@@ -57,20 +57,20 @@ edocs:
fi
all-subdirs:
- for dir in $(SUB_DIRECTORIES); do \
+ $(V_at)for dir in $(SUB_DIRECTORIES); do \
(cd $$dir; $(MAKE) $(MAKETARGET) EBIN=$(EBIN); cd ..); \
done
# distclean and realclean should clean the bootstrap files
all-subdirs-x:
- for dir in $(SUB_DIRECTORIES); do \
+ $(V_at)for dir in $(SUB_DIRECTORIES); do \
(cd $$dir; $(MAKE) $(MAKETARGET) EBIN=../boot_ebin; cd ..); \
done
clean:
- $(MAKE) MAKETARGET="clean" all-subdirs all-subdirs-x
+ $(V_at)$(MAKE) MAKETARGET="clean" all-subdirs all-subdirs-x
distclean:
- $(MAKE) MAKETARGET="distclean" all-subdirs all-subdirs-x
+ $(V_at)$(MAKE) MAKETARGET="distclean" all-subdirs all-subdirs-x
realclean:
- $(MAKE) MAKETARGET="realclean" all-subdirs all-subdirs-x
+ $(V_at)$(MAKE) MAKETARGET="realclean" all-subdirs all-subdirs-x
diff --git a/lib/hipe/amd64/hipe_amd64_encode.erl b/lib/hipe/amd64/hipe_amd64_encode.erl
index ee68dfb3b8..cbdab25b25 100644
--- a/lib/hipe/amd64/hipe_amd64_encode.erl
+++ b/lib/hipe/amd64/hipe_amd64_encode.erl
@@ -1,7 +1,7 @@
%%%
%%% %CopyrightBegin%
%%%
-%%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%%% Copyright Ericsson AB 2004-2012. All Rights Reserved.
%%%
%%% The contents of this file are subject to the Erlang Public License,
%%% Version 1.1, (the "License"); you may not use this file except in
@@ -373,16 +373,16 @@ sse2_arith_binop_encode(Prefix, Opcode, {{xmm, XMM64}, {rm64fp, RM64}}) ->
[Prefix, 16#0F, Opcode | encode_rm(RM64, XMM64, [])].
sse2_cvtsi2sd_encode({{xmm,XMM64}, {rm64,RM64}}) ->
- [rex([{w, 1}]), 16#F2, 16#0F, 16#2A�| encode_rm(RM64, XMM64, [])].
+ [rex([{w, 1}]), 16#F2, 16#0F, 16#2A | encode_rm(RM64, XMM64, [])].
sse2_mov_encode(Opnds) ->
case Opnds of
{{xmm, XMM64}, {rm64fp, RM64}} -> % movsd
- [16#F2, 16#0F, 16#10�| encode_rm(RM64, XMM64, [])];
+ [16#F2, 16#0F, 16#10 | encode_rm(RM64, XMM64, [])];
{{rm64fp, RM64}, {xmm, XMM64}} -> % movsd
- [16#F2, 16#0F, 16#11�| encode_rm(RM64, XMM64, [])]
+ [16#F2, 16#0F, 16#11 | encode_rm(RM64, XMM64, [])]
% {{xmm, XMM64}, {rm64, RM64}} -> % cvtsi2sd
-% [rex([{w, 1}]), 16#F2, 16#0F, 16#2A�| encode_rm(RM64, XMM64, [])]
+% [rex([{w, 1}]), 16#F2, 16#0F, 16#2A | encode_rm(RM64, XMM64, [])]
end.
%% arith_binop_sizeof(Opnds) ->
diff --git a/lib/hipe/cerl/erl_bif_types.erl b/lib/hipe/cerl/erl_bif_types.erl
index a5a2773598..d93ad10bd4 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-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -765,38 +765,6 @@ type(erlang, node, 0, _) -> t_node();
%% Guard bif, needs to be here.
type(erlang, node, 1, Xs) ->
strict(arg_types(erlang, node, 1), Xs, fun (_) -> t_node() end);
-type(erlang, nodes, 0, _) -> t_list(t_node());
-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_info, 1, Xs) ->
- strict(arg_types(erlang, port_info, 1), Xs,
- fun (_) -> t_sup(t_atom('undefined'), t_list()) end);
-type(erlang, port_info, 2, Xs) ->
- strict(arg_types(erlang, port_info, 2), Xs,
- fun ([_Port, Item]) ->
- t_sup(t_atom('undefined'),
- case t_atom_vals(Item) of
- ['connected'] -> t_tuple([Item, t_pid()]);
- ['id'] -> t_tuple([Item, t_integer()]);
- ['input'] -> t_tuple([Item, t_integer()]);
- ['links'] -> t_tuple([Item, t_list(t_pid())]);
- ['name'] -> t_tuple([Item, t_string()]);
- ['output'] -> t_tuple([Item, t_integer()]);
- ['os_pid'] -> t_tuple([Item, t_sup(t_non_neg_integer(),t_atom('undefined'))]);
- ['registered_name'] -> t_tuple([Item, t_atom()]);
- List when is_list(List) ->
- t_tuple([t_sup([t_atom(A) || A <- List]),
- t_sup([t_atom(), t_integer(),
- t_pid(), t_list(t_pid()),
- t_string()])]);
- unknown ->
- [_, PosItem] = arg_types(erlang, port_info, 2),
- t_tuple([PosItem,
- t_sup([t_atom(), t_integer(),
- t_pid(), t_list(t_pid()),
- t_string()])])
- end)
- end);
%% Guard bif, needs to be here.
type(erlang, round, 1, Xs) ->
strict(arg_types(erlang, round, 1), Xs, fun (_) -> t_integer() end);
@@ -915,7 +883,6 @@ type(erlang, system_info, 1, Xs) ->
t_list(t_pid());
['os_type'] ->
t_tuple([t_sup([t_atom('unix'),
- t_atom('vxworks'),
t_atom('win32')]),
t_atom()]);
['os_version'] ->
@@ -923,6 +890,14 @@ type(erlang, system_info, 1, Xs) ->
t_non_neg_fixnum(),
t_non_neg_fixnum()]),
t_string());
+ ['otp_release'] ->
+ t_string();
+ ['port_parallelism'] ->
+ t_boolean();
+ ['port_count'] ->
+ t_non_neg_fixnum();
+ ['port_limit'] ->
+ t_non_neg_fixnum();
['process_count'] ->
t_non_neg_fixnum();
['process_limit'] ->
@@ -2274,18 +2249,6 @@ arg_types(erlang, node, 0) ->
%% Guard bif, needs to be here.
arg_types(erlang, node, 1) ->
[t_identifier()];
-arg_types(erlang, nodes, 0) ->
- [];
-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_info, 1) ->
- [t_sup(t_port(), t_atom())];
-arg_types(erlang, port_info, 2) ->
- [t_sup(t_port(), t_atom()),
- t_atoms(['registered_name', 'id', 'connected',
- 'links', 'name', 'input', 'output', 'os_pid'])];
%% Guard bif, needs to be here.
arg_types(erlang, round, 1) ->
[t_number()];
@@ -2418,7 +2381,7 @@ arg_types(hipe_bifs, ref_get, 1) ->
arg_types(hipe_bifs, ref_set, 2) ->
[t_hiperef(), t_immediate()];
arg_types(hipe_bifs, remove_refs_from, 1) ->
- [t_mfa()];
+ [t_sup([t_mfa(), t_atom('all')])];
arg_types(hipe_bifs, set_funinfo_native_address, 3) ->
arg_types(hipe_bifs, set_native_address, 3);
arg_types(hipe_bifs, set_native_address, 3) ->
diff --git a/lib/hipe/cerl/erl_types.erl b/lib/hipe/cerl/erl_types.erl
index 1789fc79fa..ea1e7b1292 100644
--- a/lib/hipe/cerl/erl_types.erl
+++ b/lib/hipe/cerl/erl_types.erl
@@ -275,8 +275,8 @@
-type tag() :: ?atom_tag | ?binary_tag | ?function_tag | ?identifier_tag
| ?list_tag | ?matchstate_tag | ?nil_tag | ?number_tag
- | ?opaque_tag | ?product_tag | ?tuple_tag | ?tuple_set_tag
- | ?union_tag | ?var_tag.
+ | ?opaque_tag | ?product_tag | ?remote_tag
+ | ?tuple_tag | ?tuple_set_tag | ?union_tag | ?var_tag.
-define(float_qual, float).
-define(integer_qual, integer).
@@ -687,8 +687,8 @@ t_solve_remote(?tuple(Types, _Arity, _Tag), ET, R, C) ->
{RL, RR} = list_solve_remote(Types, ET, R, C),
{t_tuple(RL), RR};
t_solve_remote(?tuple_set(Set), ET, R, C) ->
- {NewSet, RR} = tuples_solve_remote(Set, ET, R, C),
- {?tuple_set(NewSet), RR};
+ {NewTuples, RR} = tuples_solve_remote(Set, ET, R, C),
+ {t_sup(NewTuples), RR};
t_solve_remote(?remote(Set), ET, R, C) ->
RemoteList = ordsets:to_list(Set),
{RL, RR} = list_solve_remote_type(RemoteList, ET, R, C),
@@ -788,10 +788,10 @@ opaques_solve_remote([#opaque{struct = Struct} = Remote|Tail], ET, R, C) ->
tuples_solve_remote([], _ET, _R, _C) ->
{[], []};
-tuples_solve_remote([{Sz, Tuples}|Tail], ET, 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}.
+ {RL ++ LSzTpls, RR1 ++ RR2}.
%%-----------------------------------------------------------------------------
%% Unit type. Signals non termination.
@@ -2318,10 +2318,14 @@ t_inf(?product(_), _, _Mode) ->
?none;
t_inf(_, ?product(_), _Mode) ->
?none;
-t_inf(?tuple(?any, ?any, ?any), ?tuple(_, _, _) = T, _Mode) -> T;
-t_inf(?tuple(_, _, _) = T, ?tuple(?any, ?any, ?any), _Mode) -> T;
-t_inf(?tuple(?any, ?any, ?any), ?tuple_set(_) = T, _Mode) -> T;
-t_inf(?tuple_set(_) = T, ?tuple(?any, ?any, ?any), _Mode) -> T;
+t_inf(?tuple(?any, ?any, ?any), ?tuple(_, _, _) = T, _Mode) ->
+ subst_all_vars_to_any(T);
+t_inf(?tuple(_, _, _) = T, ?tuple(?any, ?any, ?any), _Mode) ->
+ subst_all_vars_to_any(T);
+t_inf(?tuple(?any, ?any, ?any), ?tuple_set(_) = T, _Mode) ->
+ subst_all_vars_to_any(T);
+t_inf(?tuple_set(_) = T, ?tuple(?any, ?any, ?any), _Mode) ->
+ subst_all_vars_to_any(T);
t_inf(?tuple(Elements1, Arity, _Tag1), ?tuple(Elements2, Arity, _Tag2), Mode) ->
case t_inf_lists_strict(Elements1, Elements2, Mode) of
bottom -> ?none;
@@ -3428,7 +3432,7 @@ record_field_diffs_to_string(?tuple([_|Fs], Arity, Tag), RecDict) ->
field_diffs([F|Fs], [{FName, DefType}|FDefs], RecDict, Acc) ->
NewAcc =
- case t_is_subtype(F, DefType) of
+ case not t_is_none(t_inf(F, DefType)) of
true -> Acc;
false ->
Str = atom_to_string(FName) ++ "::" ++ t_to_string(DefType, RecDict),
@@ -3562,7 +3566,7 @@ t_from_form({type, _L, function, []}, _TypeNames, _InOpaque, _RecDict,
t_from_form({type, _L, 'fun', []}, _TypeNames, _InOpaque, _RecDict,
_VarDict) ->
{t_fun(), []};
-t_from_form({type, _L, 'fun', [{type, _, any, []}, Range]}, TypeNames,
+t_from_form({type, _L, 'fun', [{type, _, any}, Range]}, TypeNames,
InOpaque, RecDict, VarDict) ->
{T, R} = t_from_form(Range, TypeNames, InOpaque, RecDict, VarDict),
{t_fun(T), R};
@@ -3905,7 +3909,7 @@ t_form_to_string({type, _L, binary, [Base, Unit]} = Type) ->
_ -> io_lib:format("Badly formed bitstr type ~w", [Type])
end;
t_form_to_string({type, _L, 'fun', []}) -> "fun()";
-t_form_to_string({type, _L, 'fun', [{type, _, any, []}, Range]}) ->
+t_form_to_string({type, _L, 'fun', [{type, _, any}, Range]}) ->
"fun(...) -> " ++ t_form_to_string(Range);
t_form_to_string({type, _L, 'fun', [{type, _, product, Domain}, Range]}) ->
"fun((" ++ string:join(t_form_to_string_list(Domain), ",") ++ ") -> "
diff --git a/lib/hipe/doc/src/notes.xml b/lib/hipe/doc/src/notes.xml
index ed8cf0b8d3..cfd22a9d8d 100644
--- a/lib/hipe/doc/src/notes.xml
+++ b/lib/hipe/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2006</year><year>2011</year>
+ <year>2006</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -30,6 +30,66 @@
</header>
<p>This document describes the changes made to HiPE.</p>
+<section><title>Hipe 3.9.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ A faulty spec for process_info/2 could cause false
+ dialyzer warnings. The spec is corrected.</p>
+ <p>
+ Own Id: OTP-10584</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Hipe 3.9.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Correct handling of type names in contracts. Fix
+ crash related to contract checking. Do not rewrite
+ unchanged PLT. </p>
+ <p>
+ Own Id: OTP-10083</p>
+ </item>
+ <item>
+ <p>
+ Changes in comments and minor code cleanups</p>
+ <p>
+ Thanks to Kostis Sagonas.</p>
+ <p>
+ Own Id: OTP-10230</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Remove all code, documentation, options and diagnostic
+ functions which were related to the experimental hybrid
+ heap implementation.</p>
+ <p>
+ Own Id: OTP-10105</p>
+ </item>
+ <item>
+ <p> Bugs in <c>erl_types:t_inf()</c> (HiPE) and in
+ <c>dialyzer_dataflow</c> (Dialyzer) have been fixed. </p>
+ <p>
+ Own Id: OTP-10191</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Hipe 3.9.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/hipe/flow/hipe_dominators.erl b/lib/hipe/flow/hipe_dominators.erl
index 17357461a5..1f2c830eaf 100644
--- a/lib/hipe/flow/hipe_dominators.erl
+++ b/lib/hipe/flow/hipe_dominators.erl
@@ -1,8 +1,8 @@
-%% -*- erlang-indent-level: 2 -*-
+%% -*- coding: utf-8; erlang-indent-level: 2 -*-
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -19,7 +19,7 @@
%%
%%------------------------------------------------------------------------
%% File : hipe_dominators.erl
-%% Author : Christoffer Vikstr�m <[email protected]>
+%% Author : Christoffer Vikström <[email protected]>
%% Daniel Deogun <[email protected]>
%% Jesper Bengtsson <[email protected]>
%% Created : 18 Mar 2002
diff --git a/lib/hipe/icode/hipe_beam_to_icode.erl b/lib/hipe/icode/hipe_beam_to_icode.erl
index 107edf6258..81249c958e 100644
--- a/lib/hipe/icode/hipe_beam_to_icode.erl
+++ b/lib/hipe/icode/hipe_beam_to_icode.erl
@@ -96,12 +96,11 @@ mk_debug_calltrace(MFA, Env, Code) ->
%%-----------------------------------------------------------------------
-%% Exported types
+%% Types
%%-----------------------------------------------------------------------
-type hipe_beam_to_icode_ret() :: [{mfa(),#icode{}}].
-
%%-----------------------------------------------------------------------
%% Internal data structures
%%-----------------------------------------------------------------------
@@ -339,19 +338,19 @@ trans_fun([{call_ext_last,_N,{extfunc,M,F,A},_}|Instructions], Env) ->
%%--- bif0 ---
trans_fun([{bif,BifName,nofail,[],Reg}|Instructions], Env) ->
BifInst = trans_bif0(BifName,Reg),
- [hipe_icode:mk_comment({bif0,BifName}),BifInst|trans_fun(Instructions,Env)];
+ [BifInst|trans_fun(Instructions,Env)];
%%--- bif1 ---
trans_fun([{bif,BifName,{f,Lbl},[_] = Args,Reg}|Instructions], Env) ->
{BifInsts,Env1} = trans_bif(1,BifName,Lbl,Args,Reg,Env),
- [hipe_icode:mk_comment({bif1,BifName})|BifInsts] ++ trans_fun(Instructions,Env1);
+ BifInsts ++ trans_fun(Instructions,Env1);
%%--- bif2 ---
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);
+ 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);
+ BifInsts ++ trans_fun(Instructions,Env1);
%%--- allocate
trans_fun([{allocate,StackSlots,_}|Instructions], Env) ->
trans_allocate(StackSlots) ++ trans_fun(Instructions,Env);
@@ -840,7 +839,7 @@ trans_fun([{test,bs_test_tail2,{f,Lbl},[Ms,Numbits]}| Instructions], Env) ->
trans_op_call({hipe_bs_primop,{bs_test_tail,Numbits}},
Lbl, [MsVar], [], Env, Instructions);
%%--------------------------------------------------------------------
-%% New bit syntax instructions added in February 2004 (R10B).
+%% bit syntax instructions added in February 2004 (R10B).
%%--------------------------------------------------------------------
trans_fun([{bs_init2,{f,Lbl},Size,_Words,_LiveRegs,{field_flags,Flags0},X}|
Instructions], Env) ->
@@ -1055,7 +1054,7 @@ trans_fun([{arithfbif,fnegate,Lab,[SrcR],DestR}|Instructions], Env) ->
trans_fun([{arithbif,'-',Lab,[{float,0.0},SrcR],DestR}|Instructions], Env)
end;
%%--------------------------------------------------------------------
-%% New apply instructions added in April 2004 (R10B).
+%% apply instructions added in April 2004 (R10B).
%%--------------------------------------------------------------------
trans_fun([{apply,Arity}|Instructions], Env) ->
BeamArgs = extract_fun_args(Arity+2), %% +2 is for M and F
@@ -1071,21 +1070,21 @@ trans_fun([{apply_last,Arity,_N}|Instructions], Env) -> % N is StackAdjustment?
hipe_icode:mk_enter_primop(#apply_N{arity=Arity}, [M,F|Args])
| trans_fun(Instructions,Env)];
%%--------------------------------------------------------------------
-%% New test instruction added in April 2004 (R10B).
+%% test for boolean added in April 2004 (R10B).
%%--------------------------------------------------------------------
%%--- is_boolean ---
trans_fun([{test,is_boolean,{f,Lbl},[Arg]}|Instructions], Env) ->
{Code,Env1} = trans_type_test(boolean,Lbl,Arg,Env),
[Code | trans_fun(Instructions,Env1)];
%%--------------------------------------------------------------------
-%% New test instruction added in June 2005 for R11
+%% test for function with specific arity added in June 2005 (R11).
%%--------------------------------------------------------------------
%%--- is_function2 ---
trans_fun([{test,is_function2,{f,Lbl},[Arg,Arity]}|Instructions], Env) ->
{Code,Env1} = trans_type_test2(function2,Lbl,Arg,Arity,Env),
[Code | trans_fun(Instructions,Env1)];
%%--------------------------------------------------------------------
-%% New garbage-collecting BIFs added in January 2006 for R11B.
+%% garbage collecting BIFs added in January 2006 (R11B).
%%--------------------------------------------------------------------
trans_fun([{gc_bif,'-',Fail,_Live,[SrcR],DstR}|Instructions], Env) ->
%% Unary minus. Change this to binary minus.
@@ -1103,21 +1102,21 @@ trans_fun([{gc_bif,Name,Fail,_Live,SrcRs,DstR}|Instructions], Env) ->
trans_fun([{bif,Name,Fail,SrcRs,DstR}|Instructions], Env)
end;
%%--------------------------------------------------------------------
-%% New test instruction added in July 2007 for R12.
+%% test for bitstream added in July 2007 (R12).
%%--------------------------------------------------------------------
%%--- is_bitstr ---
trans_fun([{test,is_bitstr,{f,Lbl},[Arg]}|Instructions], Env) ->
{Code,Env1} = trans_type_test(bitstr, Lbl, Arg, Env),
[Code | trans_fun(Instructions, Env1)];
%%--------------------------------------------------------------------
-%% New stack triming instruction added in October 2007 for R12.
+%% stack triming instruction added in October 2007 (R12).
%%--------------------------------------------------------------------
trans_fun([{trim,N,NY}|Instructions], Env) ->
%% trim away N registers leaving NY registers
Moves = trans_trim(N, NY),
Moves ++ trans_fun(Instructions, Env);
%%--------------------------------------------------------------------
-%% New line/1 instruction in R15.
+%% line instruction added in Fall 2012 (R15).
%%--------------------------------------------------------------------
trans_fun([{line,_}|Instructions], Env) ->
trans_fun(Instructions,Env);
@@ -1321,7 +1320,7 @@ trans_bin([{bs_put_integer,{f,Lbl},Size,Unit,{field_flags,Flags0},Source}|
SrcInstrs ++ trans_bin_call({hipe_bs_primop, Name},
Lbl, [Src|Args], [Offset], Base, Offset, Env2, Instructions);
%%----------------------------------------------------------------
-%% New binary construction instructions added in R12B-5 (Fall 2008).
+%% binary construction instructions added in Fall 2008 (R12B-5).
%%----------------------------------------------------------------
trans_bin([{bs_put_utf8,{f,Lbl},_FF,A3}|Instructions], Base, Offset, Env) ->
Src = trans_arg(A3),
@@ -1372,7 +1371,7 @@ trans_bs_get_or_skip_utf32(Lbl, Ms, Flags0, X, Instructions, Env) ->
Lbl, [Dst,MsVar], [MsVar], Env1, Instructions).
%%-----------------------------------------------------------------------
-%% trans_arith(Op, SrcVars, Des, Lab, Env) -> { Icode, NewEnv }
+%% trans_arith(Op, SrcVars, Des, Lab, Env) -> {Icode, NewEnv}
%% A failure label of type {f,0} means in a body.
%% A failure label of type {f,L} where L>0 means in a guard.
%% Within a guard a failure should branch to the next guard and
@@ -1478,7 +1477,7 @@ clone_dst(Dest) ->
%%-----------------------------------------------------------------------
-%% trans_type_test(Test, Lbl, Arg, Env) -> { Icode, NewEnv }
+%% trans_type_test(Test, Lbl, Arg, Env) -> {Icode, NewEnv}
%% Handles all unary type tests like is_integer etc.
%%-----------------------------------------------------------------------
@@ -1490,7 +1489,7 @@ trans_type_test(Test, Lbl, Arg, Env) ->
{[Move,I,True],Env1}.
%%
-%% This handles binary type tests. Currently, the only such is the new
+%% This handles binary type tests. Currently, the only such is the
%% is_function/2 BIF.
%%
trans_type_test2(function2, Lbl, Arg, Arity, Env) ->
@@ -1503,7 +1502,7 @@ trans_type_test2(function2, Lbl, Arg, Arity, Env) ->
%%-----------------------------------------------------------------------
%% trans_puts(Code, Environment) ->
-%% { Movs, Code, Vars, NewEnv }
+%% {Movs, Code, Vars, NewEnv}
%%-----------------------------------------------------------------------
trans_puts(Code, Env) ->
diff --git a/lib/hipe/icode/hipe_icode.hrl b/lib/hipe/icode/hipe_icode.hrl
index 65deaf6d7c..d76eebf78d 100644
--- a/lib/hipe/icode/hipe_icode.hrl
+++ b/lib/hipe/icode/hipe_icode.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -64,9 +64,9 @@
-type icode_if_op() :: '>' | '<' | '>=' | '=<' | '=:=' | '=/=' | '==' | '/='
| 'fixnum_eq' | 'fixnum_neq' | 'fixnum_lt'
| 'fixnum_le' | 'fixnum_ge' | 'fixnum_gt'
- | 'suspend_msg_timeout'.
+ | 'op_exact_eqeq_2' | 'suspend_msg_timeout'.
--type icode_type_test() :: 'atom' | 'bignum' | 'binary' | 'bitrst' | 'boolean'
+-type icode_type_test() :: 'atom' | 'bignum' | 'binary' | 'bitstr' | 'boolean'
| 'cons' | 'constant' | 'fixnum' | 'float'
| 'function' | 'function2' | 'integer' | 'list' | 'nil'
| 'number' | 'pid' | 'port' | 'reference' | 'tuple'
@@ -88,7 +88,7 @@
-type icode_call_type() :: 'local' | 'primop' | 'remote'.
-type icode_exit_class() :: 'error' | 'exit' | 'rethrow' | 'throw'.
--type icode_comment_text() :: atom() | string() | {atom(), term()}.
+-type icode_comment_text() :: atom() | string().
-type icode_info() :: [{'arg_types', [erl_types:erl_type()]}].
diff --git a/lib/hipe/icode/hipe_icode_mulret.erl b/lib/hipe/icode/hipe_icode_mulret.erl
index a3cae621ab..0bf9f89994 100644
--- a/lib/hipe/icode/hipe_icode_mulret.erl
+++ b/lib/hipe/icode/hipe_icode_mulret.erl
@@ -1,8 +1,8 @@
-%% -*- erlang-indent-level: 2 -*-
+%% -*- coding: utf-8; erlang-indent-level: 2 -*-
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -19,9 +19,9 @@
%%
%%----------------------------------------------------------------------
%% File : hipe_icode_mulret.erl
-%% Author : Christoffer Vikstr�m <[email protected]>
+%% Author : Christoffer Vikström <[email protected]>
%% Purpose :
-%% Created : 23 Jun 2004 by Christoffer Vikstr�m <[email protected]>
+%% Created : 23 Jun 2004 by Christoffer Vikström <[email protected]>
%%----------------------------------------------------------------------
-module(hipe_icode_mulret).
@@ -890,7 +890,7 @@ removeUnElems([I|Code], [OldVar] = OldVars, DstLst, Res, Def, Lab) ->
%% [I|Res], Def, Lab)
%% end;
false ->
- io:format("Borde aldrig kunna hamna h�r!", []),
+ io:format("Borde aldrig kunna hamna här!", []),
removeUnElems(Code, OldVars, DstLst, [I|Res], Def, Lab)
end
end;
@@ -1159,8 +1159,8 @@ printCallList([]) -> io:format("~n").
%% % Purpose :
%% % Arguments :
%% % Return :
-%% % Notes : Fixa s� att funktionen anv�nder defines(I) ist�llet och
-%% % selektorer ist�llet f�r att matcha p� #call{}. L�tt gjort.
+%% % Notes : Fixa så att funktionen använder defines(I) istället och
+%% % selektorer istället för att matcha på #call{}. Lätt gjort.
%% %%>----------------------------------------------------------------------<
%% removeUnElems(List, Var) -> removeUnElems(List, Var, []).
%% removeUnElems([#icode_call{'fun'={unsafe_element,_}, args=Var}|List], Var, Res) ->
diff --git a/lib/hipe/icode/hipe_icode_type.erl b/lib/hipe/icode/hipe_icode_type.erl
index f98d859822..046949d2f2 100644
--- a/lib/hipe/icode/hipe_icode_type.erl
+++ b/lib/hipe/icode/hipe_icode_type.erl
@@ -79,13 +79,13 @@
-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,
+ t_boolean/0, t_cons/0,
t_float/0, t_from_term/1, t_from_range/2,
t_fun/0, t_fun/1, t_fun/2, t_fun_args/1, t_fun_arity/1,
t_inf/2, t_inf_lists/2, t_integer/0,
t_integer/1, t_is_atom/1, t_is_any/1,
t_is_binary/1, t_is_bitstr/1, t_is_bitwidth/1, t_is_boolean/1,
- t_is_fixnum/1, t_is_cons/1, t_is_constant/1,
+ t_is_fixnum/1, t_is_cons/1,
t_is_maybe_improper_list/1, t_is_equal/2, t_is_float/1,
t_is_fun/1, t_is_integer/1, t_is_non_neg_integer/1,
t_is_number/1, t_is_matchstate/1,
@@ -587,7 +587,6 @@ do_type(I, Info, Var) ->
TrueLab = hipe_icode:type_true_label(I),
FalseLab = hipe_icode:type_false_label(I),
None = t_none(),
-
case lookup(Var, Info) of
None ->
[{TrueLab, Info}, {FalseLab, Info}];
@@ -1699,7 +1698,6 @@ lookup_list0([H|T], Info, Acc) ->
lookup_list0([], _, Acc) ->
lists:reverse(Acc).
-
%% safe_lookup treats anything that is neither in the map nor a
%% constant as t_any(). Use this during transformations.
diff --git a/lib/hipe/main/Makefile b/lib/hipe/main/Makefile
index 673431a175..66e4c3e39a 100644
--- a/lib/hipe/main/Makefile
+++ b/lib/hipe/main/Makefile
@@ -76,7 +76,7 @@ ERL_COMPILE_FLAGS += +nowarn_shadow_vars +warn_missing_spec +warn_untyped_record
# ----------------------------------------------------
hipe.hrl: ../vsn.mk hipe.hrl.src
- sed -e "s;%VSN%;$(HIPE_VSN);" ../../hipe/main/hipe.hrl.src > ../../hipe/main/hipe.hrl
+ $(vsn_verbose)sed -e "s;%VSN%;$(HIPE_VSN);" ../../hipe/main/hipe.hrl.src > ../../hipe/main/hipe.hrl
$(EBIN)/hipe.beam: hipe.hrl ../../compiler/src/beam_disasm.hrl
$(EBIN)/hipe_main.beam: hipe.hrl ../icode/hipe_icode.hrl #../rtl/hipe_rtl.hrl
@@ -97,17 +97,17 @@ distclean: clean
realclean: clean
$(DOCS)/%.html:%.erl
- erl -noshell -run edoc_run file '"$<"' '[{dir, "$(DOCS)"}]' -s init stop
+ $(gen_verbose)erl -noshell -run edoc_run file '"$<"' '[{dir, "$(DOCS)"}]' -s init stop
# ----------------------------------------------------
# Special Build Targets
# ----------------------------------------------------
$(APP_TARGET): $(APP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
# ----------------------------------------------------
# Release Target
diff --git a/lib/hipe/misc/Makefile b/lib/hipe/misc/Makefile
index 1204ab7c0b..16166f552c 100644
--- a/lib/hipe/misc/Makefile
+++ b/lib/hipe/misc/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2001-2010. All Rights Reserved.
+# Copyright Ericsson AB 2001-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/hipe/regalloc/hipe_coalescing_regalloc.erl b/lib/hipe/regalloc/hipe_coalescing_regalloc.erl
index 5a4b017c71..7169dd18f3 100644
--- a/lib/hipe/regalloc/hipe_coalescing_regalloc.erl
+++ b/lib/hipe/regalloc/hipe_coalescing_regalloc.erl
@@ -1,8 +1,8 @@
-%% -*- erlang-indent-level: 2 -*-
+%% -*- coding: utf-8; erlang-indent-level: 2 -*-
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -20,8 +20,8 @@
%%-----------------------------------------------------------------------
%% File : hipe_coalescing_regalloc.erl
%% Authors : Andreas Wallin <[email protected]>
-%% Thorild Sel�n <[email protected]>
-%% Ingemar �berg <[email protected]>
+%% Thorild Selén <[email protected]>
+%% Ingemar Åberg <[email protected]>
%% Purpose : Play paintball with registers on a target machine. We win
%% if they are all colored. This is an iterated coalescing
%% register allocator.
diff --git a/lib/hipe/regalloc/hipe_optimistic_regalloc.erl b/lib/hipe/regalloc/hipe_optimistic_regalloc.erl
index 183ec1994c..fc3718cbc0 100644
--- a/lib/hipe/regalloc/hipe_optimistic_regalloc.erl
+++ b/lib/hipe/regalloc/hipe_optimistic_regalloc.erl
@@ -1,8 +1,8 @@
-%% -*- erlang-indent-level: 2 -*-
+%% -*- coding: utf-8; erlang-indent-level: 2 -*-
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -1657,9 +1657,9 @@ findPrimitiveNodes(Node, N, Alias, PrimitiveNodes) ->
% %?debug_msg("Node ~p~n", [Node]),
% NextNode = Node - 1,
% Coalesced_to = hipe_reg_worklists:member_coalesced_to(NextNode, Worklists),
-% ?debug_msg("��-- member coalesced: ~p~n", [Coalesced_to]),
+% ?debug_msg("³³-- member coalesced: ~p~n", [Coalesced_to]),
% {Primitives, Alias1} = undoCoalescing(NextNode, No_temporaries, Alias),
-% ?debug_msg("��-- primitivenodes ~w\n", [Primitives]),
+% ?debug_msg("½½-- primitivenodes ~w\n", [Primitives]),
% case (Coalesced_to) of
% true -> printAlias(Alias1);
% _ -> true
@@ -1683,9 +1683,9 @@ findPrimitiveNodes(Node, N, Alias, PrimitiveNodes) ->
fixAdj(N, SavedAdj, IG, Target) ->
%Saved = hipe_vectors:get(SavedAdj, N),
Saved = hipe_adj_list:edges(N, SavedAdj),
- ?debug_msg("��--adj to ~p: ~p~n", [N, Saved]),
+ ?debug_msg("§§--adj to ~p: ~p~n", [N, Saved]),
Adj = hipe_ig:node_adj_list(N, IG),
- ?debug_msg("��--adj to ~p: ~p~n", [N, Adj]),
+ ?debug_msg("««--adj to ~p: ~p~n", [N, Adj]),
New = findNew(Adj, Saved),
?debug_msg("++--new adj to ~p: ~p~n", [N, New]),
removeAdj(New, N, IG, Target),
diff --git a/lib/hipe/regalloc/hipe_reg_worklists.erl b/lib/hipe/regalloc/hipe_reg_worklists.erl
index 67a5788c7c..e22cc8dc07 100644
--- a/lib/hipe/regalloc/hipe_reg_worklists.erl
+++ b/lib/hipe/regalloc/hipe_reg_worklists.erl
@@ -1,8 +1,8 @@
-%%% -*- erlang-indent-level: 2 -*-
+%%% -*- coding: utf-8; erlang-indent-level: 2 -*-
%%%
%%% %CopyrightBegin%
%%%
-%%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%%% Copyright Ericsson AB 2001-2012. All Rights Reserved.
%%%
%%% The contents of this file are subject to the Erlang Public License,
%%% Version 1.1, (the "License"); you may not use this file except in
@@ -28,7 +28,7 @@
%%%----------------------------------------------------------------------
-module(hipe_reg_worklists).
--author(['Andreas Wallin', 'Thorild Sel�n']).
+-author(['Andreas Wallin', 'Thorild Selén']).
-export([new/5, % only used by optimistic allocator
new/6,
simplify/1,
diff --git a/lib/hipe/rtl/Makefile b/lib/hipe/rtl/Makefile
index 426d1bd3ee..7852a2172b 100644
--- a/lib/hipe/rtl/Makefile
+++ b/lib/hipe/rtl/Makefile
@@ -134,13 +134,13 @@ HIPE_MKLITERALS=$(ERL_TOP)/bin/$(TARGET)/hipe_mkliterals$(TYPE_STR)$(FLAVOR_STR)
hipe_literals.hrl: $(HIPE_MKLITERALS)
- $(HIPE_MKLITERALS) $(MKLIT_FLAGS) -e > hipe_literals.hrl
+ $(gen_verbose)$(HIPE_MKLITERALS) $(MKLIT_FLAGS) -e > hipe_literals.hrl
# Need to generate hipe.hrl from one and only one target in one and only
# one makefile; otherwise, clearmake will force rebuilds of hipe over and
# over again.
../main/hipe.hrl: ../vsn.mk ../main/hipe.hrl.src
- (cd ../main && $(MAKE) hipe.hrl)
+ $(V_at)(cd ../main && $(MAKE) hipe.hrl)
# 2012-02-24. Please keep these dependencies up to date. They tend to rot.
# grep ^-include *.erl says a lot, but you need to dig further, e.g:
diff --git a/lib/hipe/rtl/hipe_rtl_arith.inc b/lib/hipe/rtl/hipe_rtl_arith.inc
index e608506234..7b587e882d 100644
--- a/lib/hipe/rtl/hipe_rtl_arith.inc
+++ b/lib/hipe/rtl/hipe_rtl_arith.inc
@@ -1,9 +1,9 @@
%% -*- Erlang -*-
-%% -*- erlang-indent-level: 2 -*-
+%% -*- coding: utf-8; erlang-indent-level: 2 -*-
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -114,7 +114,7 @@ eval_alu(Op, Arg1, Arg2)
eval_alu(Op, Arg1, Arg2) ->
?EXIT({argument_overflow,Op,Arg1,Arg2}).
-%% Bj�rn & Bjarni:
+%% Björn & Bjarni:
%% We need to be able to do evaluations based only on the bits, since
%% 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)
diff --git a/lib/hipe/rtl/hipe_rtl_mk_switch.erl b/lib/hipe/rtl/hipe_rtl_mk_switch.erl
index e5175217d6..d859c50b7d 100644
--- a/lib/hipe/rtl/hipe_rtl_mk_switch.erl
+++ b/lib/hipe/rtl/hipe_rtl_mk_switch.erl
@@ -1,8 +1,8 @@
-%% -*- erlang-indent-level: 2 -*-
+%% -*- coding: utf-8; erlang-indent-level: 2 -*-
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -31,7 +31,7 @@
%% History : * 2001-02-28 Erik Johansson ([email protected]):
%% Created.
%% * 2001-04-01 Erik Trulsson ([email protected]):
-%% Stefan Lindstr�m ([email protected]):
+%% Stefan Lindström ([email protected]):
%% Added clustering and inlined binary search trees.
%% * 2001-07-30 EJ ([email protected]):
%% Fixed some bugs and started cleanup.
diff --git a/lib/hipe/rtl/hipe_rtl_primops.erl b/lib/hipe/rtl/hipe_rtl_primops.erl
index 53aaa72aa6..d9d08356ce 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-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -738,7 +738,7 @@ gen_mkfun([Dst], {_Mod, _FunId, _Arity} = MFidA, MagicNr, Index, FreeVars) ->
%% Tag the thing and increase the heap_pointer.
%% make_fun(funp);
- WordSize�= hipe_rtl_arch:word_size(),
+ WordSize = hipe_rtl_arch:word_size(),
HeapNeed = (?ERL_FUN_SIZE + NumFree) * WordSize,
TagCode = [hipe_tagscheme:tag_fun(Dst, HP),
%% AdjustHPCode
@@ -829,7 +829,7 @@ load_struct_field(Dest, StructP, Offset, int32) ->
gen_free_vars(Vars, HPReg) ->
HPVar = hipe_rtl:mk_new_var(),
- WordSize�= hipe_rtl_arch:word_size(),
+ WordSize = hipe_rtl_arch:word_size(),
[hipe_rtl:mk_alu(HPVar, HPReg, add, hipe_rtl:mk_imm(?EFT_ENV)) |
gen_free_vars(Vars, HPVar, 0, WordSize, [])].
diff --git a/lib/hipe/rtl/hipe_rtl_ssa_const_prop.erl b/lib/hipe/rtl/hipe_rtl_ssa_const_prop.erl
index 194cf29b64..1c900d767e 100644
--- a/lib/hipe/rtl/hipe_rtl_ssa_const_prop.erl
+++ b/lib/hipe/rtl/hipe_rtl_ssa_const_prop.erl
@@ -1,8 +1,8 @@
-%% -*- erlang-indent-level: 2 -*-
+%% -*- coding: utf-8; erlang-indent-level: 2 -*-
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -190,7 +190,7 @@ set_to(Dst, Val, Env) ->
%% Returns : { FlowWorkList, SSAWorkList, NewEnvironment}
%%-----------------------------------------------------------------------------
-visit_branch(Inst, Env) -> %% Titta ocks� p� exekverbarflagga
+visit_branch(Inst, Env) -> %% Titta också på exekverbarflagga
Val1 = lookup_lattice_value(hipe_rtl:branch_src1(Inst), Env),
Val2 = lookup_lattice_value(hipe_rtl:branch_src2(Inst), Env),
CFGWL = case evaluate_relop(Val1, hipe_rtl:branch_cond(Inst), Val2) of
diff --git a/lib/hipe/rtl/hipe_rtl_ssapre.erl b/lib/hipe/rtl/hipe_rtl_ssapre.erl
index a9e92e5688..34897ba4b7 100644
--- a/lib/hipe/rtl/hipe_rtl_ssapre.erl
+++ b/lib/hipe/rtl/hipe_rtl_ssapre.erl
@@ -1,8 +1,8 @@
-%% -*- erlang-indent-level: 2 -*-
+%% -*- coding: utf-8; erlang-indent-level: 2 -*-
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -19,7 +19,7 @@
%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% File : hipe_rtl_ssapre.erl
-%% Author : He Bingwen and Fr�d�ric Haziza
+%% Author : He Bingwen and Frédéric Haziza
%% Description : Performs Partial Redundancy Elimination on SSA form.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% @doc
@@ -552,7 +552,7 @@ emend_with_processed_xsis(E, [I|Rest], Pred, XsiGraph) ->
true -> %% It's a computation of E!
case xsi_arg(Xsi,Pred) of
undetermined_operand ->
- exit({?MODULE,check_operand_sharing,"######## �h Dear, we trusted Kostis !!!!!!!!! #############"});
+ exit({?MODULE,check_operand_sharing,"######## Ôh Dear, we trusted Kostis !!!!!!!!! #############"});
XsiOp ->
{sharing_operand,XsiOp} %% They share operands
end;
@@ -571,7 +571,7 @@ emend_with_processed_xsis(E, [I|Rest], Pred, XsiGraph) ->
NewE = emend(E,Def,A#eop.var),
emend_with_processed_xsis(NewE,Rest,Pred,XsiGraph);
undetermined_operand ->
- exit({?MODULE,emend_with_processed_xsis,"######## �h Dear, we trusted Kostis, again !!!!!!!!! #############"});
+ exit({?MODULE,emend_with_processed_xsis,"######## Ôh Dear, we trusted Kostis, again !!!!!!!!! #############"});
XsiOp ->
NewE = emend(E,Def,XsiOp),
emend_with_processed_xsis(NewE,Rest,Pred,XsiGraph)
diff --git a/lib/hipe/sparc/hipe_rtl_to_sparc.erl b/lib/hipe/sparc/hipe_rtl_to_sparc.erl
index df5e2b0077..dc001f865e 100644
--- a/lib/hipe/sparc/hipe_rtl_to_sparc.erl
+++ b/lib/hipe/sparc/hipe_rtl_to_sparc.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -19,6 +19,7 @@
%%
-module(hipe_rtl_to_sparc).
+
-export([translate/1]).
-include("../rtl/hipe_rtl.hrl").
@@ -142,8 +143,7 @@ mk_fload_rr(Base1, Base2, Dst) ->
mk_fload_ii(Base1, Base2, Dst) ->
io:format("~w: RTL fload with two immediates\n", [?MODULE]),
Tmp = new_untagged_temp(),
- mk_set(Base1, Tmp,
- mk_fload_ri(Tmp, Base2, Dst)).
+ mk_set(Base1, Tmp, mk_fload_ri(Tmp, Base2, Dst)).
mk_fload_ri(Base, Disp, Dst) ->
hipe_sparc:mk_fload(Base, Disp, Dst, 'new').
@@ -239,8 +239,7 @@ mk_alu_ii(XAluOp, Src1, Src2, Dst) ->
io:format("~w: ALU with two immediates (~w ~w ~w ~w)\n",
[?MODULE, XAluOp, Src1, Src2, Dst]),
Tmp = new_untagged_temp(),
- mk_set(Src1, Tmp,
- mk_alu_ri(XAluOp, Tmp, Src2, Dst)).
+ mk_set(Src1, Tmp, mk_alu_ri(XAluOp, Tmp, Src2, Dst)).
mk_alu_ir(XAluOp, Src1, Src2, Dst) ->
case xaluop_commutes(XAluOp) of
@@ -249,8 +248,7 @@ mk_alu_ir(XAluOp, Src1, Src2, Dst) ->
true};
_ ->
Tmp = new_untagged_temp(),
- {mk_set(Src1, Tmp,
- mk_alu_rs(XAluOp, Tmp, Src2, Dst)),
+ {mk_set(Src1, Tmp, mk_alu_rs(XAluOp, Tmp, Src2, Dst)),
false}
end.
@@ -274,8 +272,7 @@ mk_arith_ri(XAluOp, Src1, Src2, Dst) when is_integer(Src2) ->
mk_alu_rs(XAluOp, Src1, hipe_sparc:mk_simm13(Src2), Dst);
true ->
Tmp = new_untagged_temp(),
- mk_set(Src2, Tmp,
- mk_alu_rs(XAluOp, Src1, Tmp, Dst))
+ mk_set(Src2, Tmp, mk_alu_rs(XAluOp, Src1, Tmp, Dst))
end.
mk_alu_rs(XAluOp, Src1, Src2, Dst) ->
@@ -623,8 +620,7 @@ mk_move(Src, Dst, Tail) ->
conv_return(I, Map, Data) ->
%% TODO: multiple-value returns
{[Arg], Map0} = conv_src_list(hipe_rtl:return_varlist(I), Map),
- I2 = mk_move(Arg, hipe_sparc:mk_rv(),
- [hipe_sparc:mk_pseudo_ret()]),
+ I2 = mk_move(Arg, hipe_sparc:mk_rv(), [hipe_sparc:mk_pseudo_ret()]),
{I2, Map0, Data}.
conv_store(I, Map, Data) ->
@@ -648,8 +644,7 @@ mk_store(StOp, Src, Base1, Base2) ->
mk_store2(StOp, Src, Base1, Base2);
_ ->
Tmp = new_untagged_temp(),
- mk_set(Src, Tmp,
- mk_store2(StOp, Tmp, Base1, Base2))
+ mk_set(Src, Tmp, mk_store2(StOp, Tmp, Base1, Base2))
end.
mk_store2(StOp, Src, Base1, Base2) ->
@@ -674,8 +669,7 @@ conv_switch(I, Map, Data) ->
[] ->
hipe_consttab:insert_block(Data, word, LMap);
SortOrder ->
- hipe_consttab:insert_sorted_block(
- Data, word, LMap, SortOrder)
+ hipe_consttab:insert_sorted_block(Data, word, LMap, SortOrder)
end,
%% no immediates allowed here
{IndexR, Map1} = conv_dst(hipe_rtl:switch_src(I), Map),
@@ -722,7 +716,7 @@ conv_aluop(RtlAluOp) ->
xaluop_commutes(XAluOp) ->
case XAluOp of
- 'cmp' -> true;
+ %% 'cmp' -> true;
'cmpcc' -> true;
'add' -> true;
'addcc' -> true;
@@ -739,16 +733,16 @@ xaluop_commutes(XAluOp) ->
'sll' -> false;
'srl' -> false;
'sra' -> false;
- 'sllx' -> false;
- 'srlx' -> false;
- 'srax' -> false;
+ %% 'sllx' -> false;
+ %% 'srlx' -> false;
+ %% 'srax' -> false;
'ldsb' -> true;
'ldsh' -> true;
- 'ldsw' -> true;
+ %% 'ldsw' -> true;
'ldub' -> true;
'lduh' -> true;
- 'lduw' -> true;
- 'ldx' -> true
+ 'lduw' -> true
+ %% 'ldx' -> true
end.
%%% Check if an extended SPARC AluOp is a shift.
diff --git a/lib/hipe/ssa/hipe_ssa.inc b/lib/hipe/ssa/hipe_ssa.inc
index d15b5ddd56..e766a83c41 100644
--- a/lib/hipe/ssa/hipe_ssa.inc
+++ b/lib/hipe/ssa/hipe_ssa.inc
@@ -1,8 +1,8 @@
-%% -*- erlang-indent-level: 2 -*-
+%% -*- coding: utf-8; erlang-indent-level: 2 -*-
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -19,7 +19,7 @@
%%
%%----------------------------------------------------------------------
%% File : hipe_ssa.inc
-%% Authors : Christoffer Vikstr�m, Daniel Deogun, and Jesper Bengtsson
+%% Authors : Christoffer Vikström, Daniel Deogun, and Jesper Bengtsson
%% Created : March 2002
%% Purpose : Provides code which converts the code of a CFG into SSA
%% (Static Single Assignment) form and back.
diff --git a/lib/hipe/tools/Makefile b/lib/hipe/tools/Makefile
index 6761f11b01..3ce8ad5dd7 100644
--- a/lib/hipe/tools/Makefile
+++ b/lib/hipe/tools/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2002-2011. All Rights Reserved.
+# Copyright Ericsson AB 2002-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/hipe/vsn.mk b/lib/hipe/vsn.mk
index e296997466..f3e2e695b5 100644
--- a/lib/hipe/vsn.mk
+++ b/lib/hipe/vsn.mk
@@ -1 +1 @@
-HIPE_VSN = 3.9.1
+HIPE_VSN = 3.9.3
diff --git a/lib/hipe/x86/hipe_x86_assemble.erl b/lib/hipe/x86/hipe_x86_assemble.erl
index 4e65736db3..7878c7219d 100644
--- a/lib/hipe/x86/hipe_x86_assemble.erl
+++ b/lib/hipe/x86/hipe_x86_assemble.erl
@@ -2,7 +2,7 @@
%%%
%%% %CopyrightBegin%
%%%
-%%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%%% Copyright Ericsson AB 2001-2012. All Rights Reserved.
%%%
%%% The contents of this file are subject to the Erlang Public License,
%%% Version 1.1, (the "License"); you may not use this file except in
@@ -698,7 +698,7 @@ mem_to_ea_common(#x86_mem{base=#x86_temp{reg=Base}, off=#x86_imm{value=Off}}) ->
%% jmp_switch
-ifdef(HIPE_AMD64).
-resolve_jmp_switch_arg(I,�_Context) ->
+resolve_jmp_switch_arg(I, _Context) ->
Base = hipe_x86:temp_reg(hipe_x86:jmp_switch_jtab(I)),
Index = hipe_x86:temp_reg(hipe_x86:jmp_switch_temp(I)),
SINDEX = hipe_amd64_encode:sindex(3, Index),
diff --git a/lib/hipe/x86/hipe_x86_postpass.erl b/lib/hipe/x86/hipe_x86_postpass.erl
index 34e3d7a11b..c0918c4f89 100644
--- a/lib/hipe/x86/hipe_x86_postpass.erl
+++ b/lib/hipe/x86/hipe_x86_postpass.erl
@@ -1,8 +1,8 @@
-%%% -*- erlang-indent-level: 2 -*-
+%%% -*- coding: utf-8; erlang-indent-level: 2 -*-
%%%
%%% %CopyrightBegin%
%%%
-%%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
+%%% Copyright Ericsson AB 2003-2012. All Rights Reserved.
%%%
%%% The contents of this file are subject to the Erlang Public License,
%%% Version 1.1, (the "License"); you may not use this file except in
@@ -19,9 +19,9 @@
%%%
%%%----------------------------------------------------------------------
%%% File : hipe_x86_postpass.erl
-%%% Author : Christoffer Vikstr�m <[email protected]>
+%%% Author : Christoffer Vikström <[email protected]>
%%% Purpose : Contain postpass optimisations for x86-assembler code.
-%%% Created : 5 Aug 2003 by Christoffer Vikstr�m <[email protected]>
+%%% Created : 5 Aug 2003 by Christoffer Vikström <[email protected]>
%%%----------------------------------------------------------------------
-ifndef(HIPE_X86_POSTPASS).
diff --git a/lib/hipe/x86/hipe_x86_ra_postconditions.erl b/lib/hipe/x86/hipe_x86_ra_postconditions.erl
index 0b70764daf..6d7e90df43 100644
--- a/lib/hipe/x86/hipe_x86_ra_postconditions.erl
+++ b/lib/hipe/x86/hipe_x86_ra_postconditions.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -224,7 +224,7 @@ do_byte_move(Src0, Dst0, TempMap, Strategy) ->
do_move64(I, TempMap, Strategy) ->
#move64{dst=Dst} = I,
- case�is_spilled(Dst, TempMap) of
+ case is_spilled(Dst, TempMap) of
false ->
{[I], false};
true ->
diff --git a/lib/ic/c_src/Makefile.in b/lib/ic/c_src/Makefile.in
index d8b8c85a8e..856823b1b3 100644
--- a/lib/ic/c_src/Makefile.in
+++ b/lib/ic/c_src/Makefile.in
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1998-2011. All Rights Reserved.
+# Copyright Ericsson AB 1998-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -67,7 +67,11 @@ ifeq ($(findstring solaris,$(HOST_OS)),solaris)
SKIP_BUILDING_BINARIES := true
endif
else
+ifeq ($(V),0)
+AR_OUT = rc
+else
AR_OUT = rcv
+endif
CC_FLAGS = @DED_CFLAGS@
LIBRARY = $(LIBDIR)/libic.a
SKIP_BUILDING_BINARIES := false
@@ -128,11 +132,13 @@ docs:
_create_dirs := $(shell mkdir -p $(OBJDIR) $(LIBDIR))
$(LIBRARY): $(OBJ_FILES)
- -$(AR) $(AR_OUT) $@ $(OBJ_FILES)
+ $(ar_verbose)
+ -$(AR) $(AR_OUT) $@ $(OBJ_FILES)
+ $(ranlib_verbose)
-$(RANLIB) $@
$(OBJDIR)/%.o: %.c
- $(CC) $(CC_FLAGS) -c -o $@ $(ALL_CFLAGS) $<
+ $(V_CC) $(CC_FLAGS) -c -o $@ $(ALL_CFLAGS) $<
# ----------------------------------------------------
# Release Target
diff --git a/lib/ic/doc/src/Makefile b/lib/ic/doc/src/Makefile
index 0ee242b234..af76cdeaaa 100644
--- a/lib/ic/doc/src/Makefile
+++ b/lib/ic/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1998-2011. All Rights Reserved.
+# Copyright Ericsson AB 1998-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/ic/doc/src/ic_clib.xml b/lib/ic/doc/src/ic_clib.xml
index b557c4b5f6..ebeaabae91 100644
--- a/lib/ic/doc/src/ic_clib.xml
+++ b/lib/ic/doc/src/ic_clib.xml
@@ -4,7 +4,7 @@
<cref>
<header>
<copyright>
- <year>2003</year><year>2009</year>
+ <year>2003</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -41,7 +41,7 @@
</section>
<funcs>
<func>
- <name><ret>CORBA_Environment*</ret><nametext>CORBA_Environment_alloc(int inbufsz, int outbufsz)</nametext></name>
+ <name><ret>CORBA_Environment *</ret><nametext>CORBA_Environment_alloc(int inbufsz, int outbufsz)</nametext></name>
<fsummary>Allocate environment data.</fsummary>
<desc>
<p>This function is used to allocate and initiate the
@@ -79,14 +79,14 @@
</desc>
</func>
<func>
- <name><ret>CORBA_char*</ret><nametext>CORBA_string_alloc(CORBA_unsigned_long len)</nametext></name>
+ <name><ret>CORBA_char *</ret><nametext>CORBA_string_alloc(CORBA_unsigned_long len)</nametext></name>
<fsummary>Allocate a string.</fsummary>
<desc>
<p>Allocates a (simple) CORBA character string of length <c>len + 1</c>.</p>
</desc>
</func>
<func>
- <name><ret>CORBA_wchar*</ret><nametext>CORBA_wstring_alloc(CORBA_unsigned_long len)</nametext></name>
+ <name><ret>CORBA_wchar *</ret><nametext>CORBA_wstring_alloc(CORBA_unsigned_long len)</nametext></name>
<fsummary>Allocate a wide string.</fsummary>
<desc>
<p>Allocates a CORBA wide string of length <c>len + 1</c>.</p>
@@ -101,7 +101,7 @@
</section>
<funcs>
<func>
- <name><ret>CORBA_char*</ret><nametext>CORBA_exception_id(CORBA_Environment *env)</nametext></name>
+ <name><ret>CORBA_char *</ret><nametext>CORBA_exception_id(CORBA_Environment *env)</nametext></name>
<fsummary>Get exception identity.</fsummary>
<desc>
<p>Returns the exception identity if an exception is set, otherwise
@@ -109,7 +109,7 @@
</desc>
</func>
<func>
- <name><ret>void*</ret><nametext>CORBA_exception_value(CORBA_Environment *env)</nametext></name>
+ <name><ret>void *</ret><nametext>CORBA_exception_value(CORBA_Environment *env)</nametext></name>
<fsummary>Get exception value.</fsummary>
<desc>
<p>Returns the exception value, if an exception is set, otherwise
@@ -160,7 +160,7 @@
</desc>
</func>
<func>
- <name><ret>oe_map_t*</ret><nametext>oe_merge_maps(oe_map_t *maps, int size)</nametext></name>
+ <name><ret>oe_map_t *</ret><nametext>oe_merge_maps(oe_map_t *maps, int size)</nametext></name>
<fsummary>Merge an array of server maps to one single map.</fsummary>
<desc>
<p>Merge an array of server maps to one single map.</p>
diff --git a/lib/ic/doc/src/notes.xml b/lib/ic/doc/src/notes.xml
index eac932fc06..3ac35aa0e5 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>2011</year>
+ <year>1998</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -30,7 +30,29 @@
<file>notes.xml</file>
</header>
- <section><title>IC 4.2.30</title>
+ <section><title>IC 4.2.31</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix bug where the ic pre-processor would ignore
+ whitespace quoting.</p>
+ <p>
+ Own Id: OTP-10109</p>
+ </item>
+ <item>
+ <p> A bug regarding spaces in C function prototypes has
+ been fixed. (Thanks to Richard O'Keefe.) </p>
+ <p>
+ Own Id: OTP-10138</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>IC 4.2.30</title>
<section><title>Improvements and New Features</title>
<list>
diff --git a/lib/ic/examples/all-against-all/client.c b/lib/ic/examples/all-against-all/client.c
index e0a52b142d..5dece9cfa6 100644
--- a/lib/ic/examples/all-against-all/client.c
+++ b/lib/ic/examples/all-against-all/client.c
@@ -88,6 +88,7 @@ int main(){
/* Initiating pid*/
strcpy(pid.node,client_node);
+ pid.node_org_enc = ERLANG_LATIN1;
pid.num = 99;
pid.serial = 0;
pid.creation = 0;
diff --git a/lib/ic/examples/c-client/client.c b/lib/ic/examples/c-client/client.c
index 816477cf15..5b11510ce3 100644
--- a/lib/ic/examples/c-client/client.c
+++ b/lib/ic/examples/c-client/client.c
@@ -64,6 +64,7 @@ int main()
/* Initiating pid*/
strcpy(pid.node,CLNODE);
+ pid.node_org_enc = ERLANG_LATIN1;
pid.num = 99;
pid.serial = 0;
pid.creation = 0;
diff --git a/lib/ic/examples/c-server/client.c b/lib/ic/examples/c-server/client.c
index fa570089b5..605e41ddb1 100644
--- a/lib/ic/examples/c-server/client.c
+++ b/lib/ic/examples/c-server/client.c
@@ -58,6 +58,7 @@ int main()
/* Initiating pid*/
strcpy(pid.node, CLNODE);
+ pid.node_org_enc = ERLANG_LATIN1;
pid.num = 99;
pid.serial = 0;
pid.creation = 0;
diff --git a/lib/ic/examples/pre_post_condition/Makefile b/lib/ic/examples/pre_post_condition/Makefile
index 8ae37394ed..53e647e793 100644
--- a/lib/ic/examples/pre_post_condition/Makefile
+++ b/lib/ic/examples/pre_post_condition/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2011. All Rights Reserved.
+# Copyright Ericsson AB 1999-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -109,9 +109,9 @@ test: $(TEST_TARGET_FILES)
IDL-GENERATED: ex.idl
- erlc $(ERL_LOCAL_FLAGS) +'{precond,{tracer,pre}}' \
+ $(gen_verbose)erlc $(ERL_LOCAL_FLAGS) +'{precond,{tracer,pre}}' \
+'{{postcond,"m::i::f"},{tracer,post}}' ex.idl
- >IDL-GENERATED
+ $(V_at)>IDL-GENERATED
$(GEN_ERL_MODULES:%=%.erl) $(GEN_HRL_FILES): IDL-GENERATED
diff --git a/lib/ic/java_src/com/ericsson/otp/ic/Makefile b/lib/ic/java_src/com/ericsson/otp/ic/Makefile
index 1b14d4430f..273614e8d9 100644
--- a/lib/ic/java_src/com/ericsson/otp/ic/Makefile
+++ b/lib/ic/java_src/com/ericsson/otp/ic/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2009. All Rights Reserved.
+# Copyright Ericsson AB 1999-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -85,7 +85,10 @@ JAR= jar
JAVADOCFLAGS=-d $(DOCDIR)
JAVAFLAGS=-d $(JAVA_DEST_ROOT)
-JARFLAGS= -cvf
+JARFLAGS= -cf
+ifneq ($(V),0)
+JARFLAGS= -cfv
+endif
JAVA_OPTIONS =
diff --git a/lib/ic/src/Makefile b/lib/ic/src/Makefile
index c830ebec95..e8769d2335 100644
--- a/lib/ic/src/Makefile
+++ b/lib/ic/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1998-2009. All Rights Reserved.
+# Copyright Ericsson AB 1998-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -175,7 +175,7 @@ clean:
rm -f errs core *~
$(APP_TARGET): $(APP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
docs:
@@ -183,7 +183,7 @@ docs:
# Special Build Targets
# ----------------------------------------------------
../ebin/icparse.beam: icparse.erl
- $(ERLC) $(ERL_COMPILE_FLAGS) +nowarn_unused_vars +nowarn_unused_function -o$(EBIN) +pj $<
+ $(V_ERLC) $(ERL_COMPILE_FLAGS) +nowarn_unused_vars +nowarn_unused_function -o$(EBIN) +pj $<
icparse.erl: icparse.yrl icyeccpre.hrl
diff --git a/lib/ic/src/ic.erl b/lib/ic/src/ic.erl
index bc7ec8ba91..c0742cf7bd 100644
--- a/lib/ic/src/ic.erl
+++ b/lib/ic/src/ic.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/ic/src/ic_pp.erl b/lib/ic/src/ic_pp.erl
index 50eeeab48c..54701f7438 100644
--- a/lib/ic/src/ic_pp.erl
+++ b/lib/ic/src/ic_pp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/ic/test/Makefile b/lib/ic/test/Makefile
index 65003d703b..54ac186c16 100644
--- a/lib/ic/test/Makefile
+++ b/lib/ic/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1998-2011. All Rights Reserved.
+# Copyright Ericsson AB 1998-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/ic/test/java_client_erl_server_SUITE.erl b/lib/ic/test/java_client_erl_server_SUITE.erl
index d4523dc2ad..9e49305c3c 100644
--- a/lib/ic/test/java_client_erl_server_SUITE.erl
+++ b/lib/ic/test/java_client_erl_server_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -62,9 +62,14 @@ 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
+ P ->
+ case filelib:wildcard(filename:join(P, "*.jar")) of
+ [_|_] ->
+ true;
+ [] ->
+ false
+ end
+ end
of
true ->
case find_executable(["java"]) of
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
index c106261efd..a2440adc92 100644
--- a/lib/ic/test/java_client_erl_server_SUITE_data/Makefile.src
+++ b/lib/ic/test/java_client_erl_server_SUITE_data/Makefile.src
@@ -66,8 +66,11 @@ CLASS_FILES = $(JAVA_FILES:.java=.class)
ERL_FILES = $(GEN_ERL_FILES) m_i_impl.erl
EBINS = $(ERL_FILES:.erl=.@EMULATOR@)
-
+@IFEQ@ (@jinterface_classpath@,)
+all:
+@ELSE@
all: $(CLASS_FILES) $(EBINS)
+@ENDIF@
$(GEN_ERL_FILES) $(GEN_HRL_FILES): java_erl_test.built_erl
$(GEN_JAVA_FILES): java_erl_test.built_java
@@ -83,15 +86,15 @@ clean:
java_erl_test.built_erl java_erl_test.built_java
java_erl_test.built_java: java_erl_test.idl
- $(ERLC) -I $(IC_INCLUDE_PATH) "+{be,java}" java_erl_test.idl
- echo done > java_erl_test.built_java
+ $(gen_verbose)$(ERLC) -I $(IC_INCLUDE_PATH) "+{be,java}" java_erl_test.idl
+ $(V_at)echo done > java_erl_test.built_java
$(CLASS_FILES) : $(JAVA_FILES)
- $(JAVAC) -classpath $(CLASSPATH) $(JAVA_FILES)
+ $(V_JAVAC) -classpath $(CLASSPATH) $(JAVA_FILES)
java_erl_test.built_erl: java_erl_test.idl
- $(ERLC) -I $(IC_INCLUDE_PATH) "+{be,erl_genserv}" java_erl_test.idl
- echo done > java_erl_test.built_erl
+ $(gen_verbose)$(ERLC) -I $(IC_INCLUDE_PATH) "+{be,erl_genserv}" java_erl_test.idl
+ $(V_at)echo done > java_erl_test.built_erl
.erl.@EMULATOR@:
- $(ERLC) -I $(IC_INCLUDE_PATH) $<
+ $(V_ERLC) -I $(IC_INCLUDE_PATH) $<
diff --git a/lib/ic/vsn.mk b/lib/ic/vsn.mk
index fc1475974e..0dbf5a1f52 100644
--- a/lib/ic/vsn.mk
+++ b/lib/ic/vsn.mk
@@ -1 +1 @@
-IC_VSN = 4.2.30
+IC_VSN = 4.2.31
diff --git a/lib/inets/doc/src/http_uri.xml b/lib/inets/doc/src/http_uri.xml
index bd31ae42d2..d9e8587bbf 100644
--- a/lib/inets/doc/src/http_uri.xml
+++ b/lib/inets/doc/src/http_uri.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2012</year><year>2012</year>
+ <year>2012</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -55,7 +55,8 @@ string() = list of ASCII characters
<p>For more information about URI, see RFC 3986. </p>
<code type="none"><![CDATA[
-uri() = string() - Syntax according to the URI definition in rfc 3986, ex: "http://www.erlang.org/"
+uri() = string() - Syntax according to the URI definition in rfc 3986,
+ e.g.: "http://www.erlang.org/"
user_info() = string()
scheme() = atom() - Example: http, https
host() = string()
diff --git a/lib/inets/doc/src/httpc.xml b/lib/inets/doc/src/httpc.xml
index 70c845bade..741f2abaef 100644
--- a/lib/inets/doc/src/httpc.xml
+++ b/lib/inets/doc/src/httpc.xml
@@ -43,8 +43,12 @@
cookies and other options that can be applied to more than one
request. </p>
- <p>If the scheme
- https is used the ssl application needs to be started.</p>
+ <p>If the scheme https is used the ssl application needs to be
+ started. When https links needs to go through a proxy the
+ CONNECT method extension to HTTP-1.1 is used to establish a
+ tunnel and then the connection is upgraded to TLS,
+ however "TLS upgrade" according to RFC 2817 is not
+ supported.</p>
<p>Also note that pipelining will only be used if the pipeline
timeout is set, otherwise persistent connections without
@@ -449,7 +453,8 @@ apply(Module, Function, [ReplyInfo | Args])
<type>
<v>Options = [Option]</v>
<v>Option = {proxy, {Proxy, NoProxy}} |
- {max_sessions, MaxSessions} |
+ {https_proxy, {Proxy, NoProxy}} |
+ {max_sessions, MaxSessions} |
{max_keep_alive_length, MaxKeepAlive} |
{keep_alive_timeout, KeepAliveTimeout} |
{max_pipeline_length, MaxPipeline} |
@@ -460,86 +465,94 @@ apply(Module, Function, [ReplyInfo | Args])
{port, Port} |
{socket_opts, socket_opts()} |
{verbose, VerboseMode} </v>
+
<v>Proxy = {Hostname, Port}</v>
<v>Hostname = string() </v>
<d>ex: "localhost" or "foo.bar.se"</d>
<v>Port = integer()</v>
<d>ex: 8080 </d>
- <v>socket_opts() = [socket_opt()]</v>
- <d>The options are appended to the socket options used by the
- client. </d>
- <d>These are the default values when a new request handler
- is started (for the initial connect). They are passed directly
- to the underlying transport (gen_tcp or ssl) <em>without</em>
- verification! </d>
<v>NoProxy = [NoProxyDesc]</v>
<v>NoProxyDesc = DomainDesc | HostName | IPDesc</v>
<v>DomainDesc = "*.Domain"</v>
<d>ex: "*.ericsson.se"</d>
<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>proxy defaults to {undefined, []} e.i. no proxy is configured and https_proxy defaults to
+ the value of proxy.</d>
+
+ <v>MaxSessions = integer() </v>
<d>Default is <c>2</c>.
- Maximum number of persistent connections to a host.</d>
+ Maximum number of persistent connections to a host.</d>
<v>MaxKeepAlive = integer() </v>
- <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 <c>120000</c> (= 2 min).
- If a persistent connection is idle longer than the
- <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>
+ <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 <c>120000</c> (= 2 min).
+ If a persistent connection is idle longer than the
+ <c>keep_alive_timeout</c> in milliseconds,
+ 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 <c>2</c>.
- Maximum number of outstanding requests on a pipelined connection to a host.</d>
- <v>PipelineTimeout = integer() </v>
- <d>Default is <c>0</c>,
- which will result in pipelining not being used.
- If a persistent connection is idle longer than the
- <c>pipeline_timeout</c> the client will close the connection. </d>
+ <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 <c>0</c>,
+ which will result in pipelining not being used.
+ If a persistent connection is idle longer than the
+ <c>pipeline_timeout</c> in milliseconds,
+ the client will close the connection. </d>
<v>CookieMode = enabled | disabled | verify </v>
<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 <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 <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>
+ If Cookies are enabled all valid cookies will automatically be
+ saved in the client manager's cookie database.
+ 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 <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 <seealso marker="kernel:gen_tcp#connect">gen_tcp:connect/3,4</seealso> for more info. </d>
+ <d>If the host has several network interfaces, this option specifies which one to use.
+ 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 <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 <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>
+ <d>Specify which local port number to use.
+ See <seealso marker="kernel:gen_tcp#connect">gen_tcp:connect/3,4</seealso> for more info. </d>
+ <v>socket_opts() = [socket_opt()]</v>
+ <d>The options are appended to the socket options used by the
+ client. </d>
+ <d>These are the default values when a new request handler
+ is started (for the initial connect). They are passed directly
+ to the underlying transport (gen_tcp or ssl) <em>without</em>
+ verification! </d>
+ <v>VerboseMode = false | verbose | debug | trace </v>
+ <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>
<v>Profile = profile() | pid() (when started <c>stand_alone</c>)</v>
</type>
<desc>
- <p>Sets options to be used for subsequent requests.</p>
- <note>
- <p>If possible the client will keep its connections
- 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 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 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 <c>max_sessions</c> option. </p>
- </note>
+ <p>Sets options to be used for subsequent requests.</p>
+ <note>
+ <p>If possible the client will keep its connections
+ 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 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 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 <c>max_sessions</c> option. </p>
+ </note>
<marker id="get_options"></marker>
</desc>
@@ -551,7 +564,8 @@ apply(Module, Function, [ReplyInfo | Args])
<fsummary>Gets the currently used options.</fsummary>
<type>
<v>OptionItems = all | [option_item()]</v>
- <v>option_item() = proxy |
+ <v>option_item() = proxy |
+ https_proxy
max_sessions |
keep_alive_timeout |
max_keep_alive_length |
@@ -648,6 +662,8 @@ apply(Module, Function, [ReplyInfo | Args])
<p>Resets (clears) the cookie database for the specified
<c>Profile</c>. If no profile is specified the default profile
will be used. </p>
+
+ <marker id="which_cookies"></marker>
</desc>
</func>
@@ -667,6 +683,42 @@ apply(Module, Function, [ReplyInfo | Args])
<p>This function produces a list of the entire cookie database.
It is intended for debugging/testing purposes.
If no profile is specified the default profile will be used. </p>
+
+ <marker id="which_sessions"></marker>
+ </desc>
+ </func>
+
+ <func>
+ <name>which_sessions() -> session_info()</name>
+ <name>which_sessions(Profile) -> session_info()</name>
+ <fsummary>Produces a slightly processed dump of the sessions database.</fsummary>
+ <type>
+ <v>Profile = profile() | pid() (when started <c>stand_alone</c>)</v>
+ <v>session_info() = {GoodSessions, BadSessions, NonSessions}</v>
+ <v>GoodSessions = session()</v>
+ <v>BadSessions = tuple()</v>
+ <v>NonSessions = term()</v>
+ </type>
+ <desc>
+ <p>This function produces a slightly processed dump of the session
+ database. It is intended for debugging.
+ If no profile is specified the default profile will be used. </p>
+
+ <marker id="info"></marker>
+ </desc>
+ </func>
+
+ <func>
+ <name>info() -> list()</name>
+ <name>info(Profile) -> list()</name>
+ <fsummary>Produces a list of miscelleneous info</fsummary>
+ <type>
+ <v>Profile = profile() | pid() (when started <c>stand_alone</c>)</v>
+ </type>
+ <desc>
+ <p>This function produces a list of miscelleneous info.
+ It is intended for debugging.
+ If no profile is specified the default profile will be used. </p>
</desc>
</func>
</funcs>
diff --git a/lib/inets/doc/src/httpd.xml b/lib/inets/doc/src/httpd.xml
index 7e21229fcf..3fced5dfcd 100644
--- a/lib/inets/doc/src/httpd.xml
+++ b/lib/inets/doc/src/httpd.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1997</year><year>2011</year>
+ <year>1997</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -178,7 +178,13 @@
<p>Note that this option is only used when the option
<c>socket_type</c> has the value <c>ip_comm</c>. </p>
</item>
-
+ <marker id="prop_minimum_bytes_per_second"></marker>
+ <tag>{minimum_bytes_per_second, integer()}</tag>
+ <item>
+ <p>If given, sets a minimum bytes per second value for connections.</p>
+ <p>If the value is not reached, the socket will close for that connection.</p>
+ <p>The option is good for reducing the risk of "slow dos" attacks.</p>
+ </item>
</taglist>
<marker id="props_api_modules"></marker>
diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml
index dfdeb4016c..e0d6ae3454 100644
--- a/lib/inets/doc/src/notes.xml
+++ b/lib/inets/doc/src/notes.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 Release Notes</title>
@@ -32,16 +32,113 @@
<file>notes.xml</file>
</header>
-
- <section><title>Inets 5.9</title>
+
+ <section><title>Inets 5.9.2</title>
<section><title>Improvements and New Features</title>
-<!--
- <p>-</p>
--->
+ <list>
+ <item>
+ <p>
+ Minimum bytes per second</p>
+ <p>
+ New option to http server, {minimum_bytes_per_second,
+ integer()}, for a connection, if it is not reached the
+ socket will close for that specific connection. Can be
+ used to prevent hanging requests from faulty clients.</p>
+ <p>
+ Own Id: OTP-10392</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+<section>
+ <title>Inets 5.9.1</title>
+
+ <section>
+ <title>Improvements and New Features</title>
+ <!--
+ <p>-</p>
+ -->
+
<list>
<item>
+ <p>Better handling of errorI(s) during update of the session
+ database. </p>
+ <p>Also added and updated some debugging functions
+ <seealso marker="httpc#which_sessions">which_sessions/10,1</seealso>
+ and
+ <seealso marker="httpc#info">info/0</seealso>. </p>
+ <p>Own Id: OTP-10093</p>
+ <p>Aux Id: Seq 12062</p>
+ </item>
+
+ <item>
+ <p>Removed R14B compatible version of (inets-service and
+ tftp) behaviour definition. </p>
+ <p>Own Id: OTP-10095</p>
+ </item>
+
+ <item>
+ <p>[httpc] Documentation of KeepAlive and Pipeline timeout
+ options have been improved. </p>
+ <p>Own Id: OTP-10114</p>
+ </item>
+ </list>
+
+ </section>
+
+ <section>
+ <title>Fixed Bugs and Malfunctions</title>
+ <!--
+ <p>-</p>
+ -->
+
+ <list>
+ <item>
+ <p>[httpc] Cancel request does not work due to incorrect
+ handler table creation (wrong keypos). </p>
+ <p>Vyacheslav Vorobyov</p>
+ <p>Own Id: OTP-10092</p>
+ </item>
+
+ </list>
+
+ </section>
+
+ <section>
+ <title>Incompatibilities</title>
+ <p>-</p>
+
+ <!--
+ <list>
+ <item>
+ <p>[httpc|httpd] The old ssl implementation (based on OpenSSL),
+ has been deprecated. The config option that specified usage of
+ this version of the ssl app, <c>ossl</c>, has been removed. </p>
+ <p>Own Id: OTP-9522</p>
+ </item>
+
+ </list>
+ -->
+
+ </section>
+
+ </section> <!-- 5.9.1 -->
+
+
+ <section>
+ <title>Inets 5.9</title>
+
+ <section>
+ <title>Improvements and New Features</title>
+ <!--
+ <p>-</p>
+ -->
+
+ <list>
+ <item>
<p>[httpd] Make the server header configurable with new config
option
<seealso marker="httpd#prop_server_tokens">server_tokens</seealso>.
@@ -102,11 +199,11 @@
</section>
+<!--
<section>
<title>Incompatibilities</title>
<p>-</p>
-<!--
<list>
<item>
<p>[httpc|httpd] The old ssl implementation (based on OpenSSL),
@@ -116,9 +213,9 @@
</item>
</list>
--->
</section>
+-->
</section> <!-- 5.9 -->
@@ -285,31 +382,6 @@
</section>
- <section>
- <title>Incompatibilities</title>
-<!--
- <p>-</p>
--->
-
- <list>
- <item>
- <p>[httpc] Deprecated interface module <c>http</c> has been removed.
- It has (long) been replaced by http client interface module
- <seealso marker="httpc#">httpc</seealso>. </p>
- <p>Own Id: OTP-9359</p>
- </item>
-
- <item>
- <p>[httpc|httpd] The old ssl implementation (based on OpenSSL),
- has been deprecated. The config option that specified usage of
- this version of the ssl app, <c>ossl</c>, has been removed. </p>
- <p>Own Id: OTP-9522</p>
- </item>
-
- </list>
-
- </section>
-
<section><title>Fixed Bugs and Malfunctions</title>
<!--
<p>-</p>
@@ -332,6 +404,24 @@
</list>
</section>
+<!--
+ <section>
+ <title>Incompatibilities</title>
+ <p>-</p>
+
+ <list>
+ <item>
+ <p>[httpc] Deprecated interface module <c>http</c> has been removed.
+ It has (long) been replaced by http client interface module
+ <seealso marker="httpc#">httpc</seealso>. </p>
+ <p>Own Id: OTP-9359</p>
+ </item>
+
+ </list>
+
+ </section>
+-->
+
</section> <!-- 5.7.2 -->
diff --git a/lib/inets/examples/httpd_load_test/Makefile b/lib/inets/examples/httpd_load_test/Makefile
index de1b2a289f..abd2d8c35d 100644
--- a/lib/inets/examples/httpd_load_test/Makefile
+++ b/lib/inets/examples/httpd_load_test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2010. All Rights Reserved.
+# Copyright Ericsson AB 2010-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/inets/examples/server_root/Makefile b/lib/inets/examples/server_root/Makefile
index 2230a7eafc..7a0ee78ecf 100644
--- a/lib/inets/examples/server_root/Makefile
+++ b/lib/inets/examples/server_root/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2010. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/inets/priv/Makefile b/lib/inets/priv/Makefile
index 4dc521f618..acca20f076 100644
--- a/lib/inets/priv/Makefile
+++ b/lib/inets/priv/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2009. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/inets/src/ftp/Makefile b/lib/inets/src/ftp/Makefile
index 426286f254..2c3d2b6d13 100644
--- a/lib/inets/src/ftp/Makefile
+++ b/lib/inets/src/ftp/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2005-2010. All Rights Reserved.
+# Copyright Ericsson AB 2005-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/inets/src/http_client/Makefile b/lib/inets/src/http_client/Makefile
index 850c790bd1..f0d4ce139e 100644
--- a/lib/inets/src/http_client/Makefile
+++ b/lib/inets/src/http_client/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2005-2011. All Rights Reserved.
+# Copyright Ericsson AB 2005-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/inets/src/http_client/httpc.erl b/lib/inets/src/http_client/httpc.erl
index f4802fb96d..ede649a5a9 100644
--- a/lib/inets/src/http_client/httpc.erl
+++ b/lib/inets/src/http_client/httpc.erl
@@ -39,6 +39,7 @@
cookie_header/1, cookie_header/2, cookie_header/3,
which_cookies/0, which_cookies/1,
reset_cookies/0, reset_cookies/1,
+ which_sessions/0, which_sessions/1,
stream_next/1,
default_profile/0,
profile_name/1, profile_name/2,
@@ -267,6 +268,7 @@ set_option(Key, Value, Profile) ->
%% Reason - term()
%% Description: Retrieves the current options.
%%-------------------------------------------------------------------------
+
get_options() ->
record_info(fields, options).
@@ -373,8 +375,6 @@ cookie_header(Url, Opts, Profile)
{error, {not_started, Profile}}
end.
-
-
%%--------------------------------------------------------------------------
%% which_cookies() -> [cookie()]
@@ -398,6 +398,28 @@ which_cookies(Profile) ->
%%--------------------------------------------------------------------------
+%% which_sessions() -> {GoodSession, BadSessions, NonSessions}
+%% which_sessions(Profile) -> {GoodSession, BadSessions, NonSessions}
+%%
+%% Description: Debug function, dumping the sessions database, sorted
+%% into three groups (Good-, Bad- and Non-sessions).
+%%-------------------------------------------------------------------------
+which_sessions() ->
+ which_sessions(default_profile()).
+
+which_sessions(Profile) ->
+ ?hcrt("which sessions", [{profile, Profile}]),
+ try
+ begin
+ httpc_manager:which_sessions(profile_name(Profile))
+ end
+ catch
+ exit:{noproc, _} ->
+ {[], [], []}
+ end.
+
+
+%%--------------------------------------------------------------------------
%% info() -> list()
%% info(Profile) -> list()
%%
@@ -895,6 +917,10 @@ validate_options([{proxy, Proxy} = Opt| Tail], Acc) ->
validate_proxy(Proxy),
validate_options(Tail, [Opt | Acc]);
+validate_options([{https_proxy, Proxy} = Opt| Tail], Acc) ->
+ validate_https_proxy(Proxy),
+ validate_options(Tail, [Opt | Acc]);
+
validate_options([{max_sessions, Value} = Opt| Tail], Acc) ->
validate_max_sessions(Value),
validate_options(Tail, [Opt | Acc]);
@@ -957,6 +983,14 @@ validate_proxy({{ProxyHost, ProxyPort}, NoProxy} = Proxy)
validate_proxy(BadProxy) ->
bad_option(proxy, BadProxy).
+validate_https_proxy({{ProxyHost, ProxyPort}, NoProxy} = Proxy)
+ when is_list(ProxyHost) andalso
+ is_integer(ProxyPort) andalso
+ is_list(NoProxy) ->
+ Proxy;
+validate_https_proxy(BadProxy) ->
+ bad_option(https_proxy, BadProxy).
+
validate_max_sessions(Value) when is_integer(Value) andalso (Value >= 0) ->
Value;
validate_max_sessions(BadValue) ->
diff --git a/lib/inets/src/http_client/httpc_handler.erl b/lib/inets/src/http_client/httpc_handler.erl
index b8c34bd99b..784a9c0019 100644
--- a/lib/inets/src/http_client/httpc_handler.erl
+++ b/lib/inets/src/http_client/httpc_handler.erl
@@ -29,44 +29,44 @@
%%--------------------------------------------------------------------
%% Internal Application API
-export([
- start_link/4,
- %% connect_and_send/2,
- send/2,
- cancel/3,
- stream/3,
- stream_next/1,
- info/1
- ]).
+ start_link/4,
+ %% connect_and_send/2,
+ send/2,
+ cancel/3,
+ stream/3,
+ stream_next/1,
+ info/1
+ ]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
- terminate/2, code_change/3]).
+ terminate/2, code_change/3]).
-record(timers,
- {
- request_timers = [], % [ref()]
- queue_timer % ref()
- }).
+ {
+ request_timers = [], % [ref()]
+ queue_timer % ref()
+ }).
-record(state,
- {
- request, % #request{}
- session, % #session{}
- status_line, % {Version, StatusCode, ReasonPharse}
- headers, % #http_response_h{}
- body, % binary()
- mfa, % {Moduel, Function, Args}
- pipeline = queue:new(), % queue()
- keep_alive = queue:new(), % queue()
- status, % undefined | new | pipeline | keep_alive | close | ssl_tunnel
- canceled = [], % [RequestId]
- max_header_size = nolimit, % nolimit | integer()
- max_body_size = nolimit, % nolimit | integer()
- options, % #options{}
- timers = #timers{}, % #timers{}
- profile_name, % atom() - id of httpc_manager process.
- once % send | undefined
- }).
+ {
+ request, % #request{}
+ session, % #session{}
+ status_line, % {Version, StatusCode, ReasonPharse}
+ headers, % #http_response_h{}
+ body, % binary()
+ mfa, % {Module, Function, Args}
+ pipeline = queue:new(), % queue()
+ keep_alive = queue:new(), % queue()
+ status, % undefined | new | pipeline | keep_alive | close | {ssl_tunnel, Request}
+ canceled = [], % [RequestId]
+ max_header_size = nolimit, % nolimit | integer()
+ max_body_size = nolimit, % nolimit | integer()
+ options, % #options{}
+ timers = #timers{}, % #timers{}
+ profile_name, % atom() - id of httpc_manager process.
+ once % send | undefined
+ }).
%%====================================================================
@@ -75,8 +75,8 @@
%%--------------------------------------------------------------------
%% Function: start_link(Request, Options, ProfileName) -> {ok, Pid}
%%
-%% Request = #request{}
-%% Options = #options{}
+%% Request = #request{}
+%% Options = #options{}
%% ProfileName = atom() - id of httpc manager process
%%
%% Description: Starts a http-request handler process. Intended to be
@@ -96,11 +96,11 @@
start_link(Parent, Request, Options, ProfileName) ->
{ok, proc_lib:start_link(?MODULE, init, [[Parent, Request, Options,
- ProfileName]])}.
+ ProfileName]])}.
%%--------------------------------------------------------------------
%% Function: send(Request, Pid) -> ok
-%% Request = #request{}
+%% Request = #request{}
%% Pid = pid() - the pid of the http-request handler process.
%%
%% Description: Uses this handlers session to send a request. Intended
@@ -112,7 +112,7 @@ send(Request, Pid) ->
%%--------------------------------------------------------------------
%% Function: cancel(RequestId, Pid) -> ok
-%% RequestId = ref()
+%% RequestId = ref()
%% Pid = pid() - the pid of the http-request handler process.
%%
%% Description: Cancels a request. Intended to be called by the httpc
@@ -142,12 +142,16 @@ stream_next(Pid) ->
%% Used for debugging and testing
%%--------------------------------------------------------------------
info(Pid) ->
- call(info, Pid).
-
+ try
+ call(info, Pid)
+ catch
+ _:_ ->
+ []
+ end.
%%--------------------------------------------------------------------
%% Function: stream(BodyPart, Request, Code) -> _
-%% BodyPart = binary()
+%% BodyPart = binary()
%% Request = #request{}
%% Code = integer()
%%
@@ -167,7 +171,7 @@ stream(BodyPart, #request{stream = Self} = Request, Code)
((Self =:= self) orelse (Self =:= {self, once})) ->
?hcrt("stream - self", [{stream, Self}, {code, Code}]),
httpc_response:send(Request#request.from,
- {Request#request.id, stream, BodyPart}),
+ {Request#request.id, stream, BodyPart}),
{<<>>, Request};
%% Stream to file
@@ -177,11 +181,11 @@ stream(BodyPart, #request{stream = Filename} = Request, Code)
when ((Code =:= 200) orelse (Code =:= 206)) andalso is_list(Filename) ->
?hcrt("stream - filename", [{stream, Filename}, {code, Code}]),
case file:open(Filename, [write, raw, append, delayed_write]) of
- {ok, Fd} ->
- ?hcrt("stream - file open ok", [{fd, Fd}]),
- stream(BodyPart, Request#request{stream = Fd}, 200);
- {error, Reason} ->
- exit({stream_to_file_failed, Reason})
+ {ok, Fd} ->
+ ?hcrt("stream - file open ok", [{fd, Fd}]),
+ stream(BodyPart, Request#request{stream = Fd}, 200);
+ {error, Reason} ->
+ exit({stream_to_file_failed, Reason})
end;
%% Stream to file
@@ -189,10 +193,10 @@ stream(BodyPart, #request{stream = Fd} = Request, Code)
when ((Code =:= 200) orelse (Code =:= 206)) ->
?hcrt("stream to file", [{stream, Fd}, {code, Code}]),
case file:write(Fd, BodyPart) of
- ok ->
- {<<>>, Request};
- {error, Reason} ->
- exit({stream_to_file_failed, Reason})
+ ok ->
+ {<<>>, Request};
+ {error, Reason} ->
+ exit({stream_to_file_failed, Reason})
end;
stream(BodyPart, Request,_) -> % only 200 and 206 responses can be streamed
@@ -208,7 +212,7 @@ stream(BodyPart, Request,_) -> % only 200 and 206 responses can be streamed
%% Function: init([Options, ProfileName]) -> {ok, State} |
%% {ok, State, Timeout} | ignore | {stop, Reason}
%%
-%% Options = #options{}
+%% Options = #options{}
%% ProfileName = atom() - id of httpc manager process
%%
%% Description: Initiates the httpc_handler process
@@ -224,20 +228,19 @@ init([Parent, Request, Options, ProfileName]) ->
%% Do not let initial tcp-connection block the manager-process
proc_lib:init_ack(Parent, self()),
handle_verbose(Options#options.verbose),
- Address = handle_proxy(Request#request.address, Options#options.proxy),
+ ProxyOptions = handle_proxy_options(Request#request.scheme, Options),
+ Address = handle_proxy(Request#request.address, ProxyOptions),
{ok, State} =
- case {Address /= Request#request.address, Request#request.scheme} of
- {true, https} ->
- Error = https_through_proxy_is_not_currently_supported,
- self() ! {init_error,
- Error, httpc_response:error(Request, Error)},
- {ok, #state{request = Request, options = Options,
- status = ssl_tunnel}};
- {_, _} ->
- connect_and_send_first_request(Address, Request,
- #state{options = Options,
- profile_name = ProfileName})
- end,
+ case {Address /= Request#request.address, Request#request.scheme} of
+ {true, https} ->
+ connect_and_send_upgrade_request(Address, Request,
+ #state{options = Options,
+ profile_name = ProfileName});
+ {_, _} ->
+ connect_and_send_first_request(Address, Request,
+ #state{options = Options,
+ profile_name = ProfileName})
+ end,
gen_server:enter_loop(?MODULE, [], State).
%%--------------------------------------------------------------------
@@ -250,139 +253,139 @@ init([Parent, Request, Options, ProfileName]) ->
%% Description: Handling call messages
%%--------------------------------------------------------------------
handle_call(#request{address = Addr} = Request, _,
- #state{status = Status,
- session = #session{type = pipeline} = Session,
- timers = Timers,
- options = #options{proxy = Proxy} = _Options,
- profile_name = ProfileName} = State)
+ #state{status = Status,
+ session = #session{type = pipeline} = Session,
+ timers = Timers,
+ options = #options{proxy = Proxy} = _Options,
+ profile_name = ProfileName} = State)
when Status =/= undefined ->
?hcrv("new request on a pipeline session",
- [{request, Request},
- {profile, ProfileName},
- {status, Status},
- {timers, Timers}]),
+ [{request, Request},
+ {profile, ProfileName},
+ {status, Status},
+ {timers, Timers}]),
Address = handle_proxy(Addr, Proxy),
case httpc_request:send(Address, Session, Request) of
ok ->
- ?hcrd("request sent", []),
+ ?hcrd("request sent", []),
- %% Activate the request time out for the new request
- NewState =
- activate_request_timeout(State#state{request = Request}),
+ %% Activate the request time out for the new request
+ NewState =
+ activate_request_timeout(State#state{request = Request}),
- ClientClose =
- httpc_request:is_client_closing(Request#request.headers),
+ ClientClose =
+ httpc_request:is_client_closing(Request#request.headers),
case State#state.request of
#request{} -> %% Old request not yet finished
- ?hcrd("old request still not finished", []),
- %% Make sure to use the new value of timers in state
- NewTimers = NewState#state.timers,
+ ?hcrd("old request still not finished", []),
+ %% Make sure to use the new value of timers in state
+ NewTimers = NewState#state.timers,
NewPipeline = queue:in(Request, State#state.pipeline),
- NewSession =
- Session#session{queue_length =
- %% Queue + current
- queue:len(NewPipeline) + 1,
- client_close = ClientClose},
- insert_session(NewSession, ProfileName),
- ?hcrd("session updated", []),
+ NewSession =
+ Session#session{queue_length =
+ %% Queue + current
+ queue:len(NewPipeline) + 1,
+ client_close = ClientClose},
+ insert_session(NewSession, ProfileName),
+ ?hcrd("session updated", []),
{reply, ok, State#state{pipeline = NewPipeline,
- session = NewSession,
- timers = NewTimers}};
- undefined ->
- %% Note: tcp-message receiving has already been
- %% activated by handle_pipeline/2.
- ?hcrd("no current request", []),
- cancel_timer(Timers#timers.queue_timer,
- timeout_queue),
- NewSession =
- Session#session{queue_length = 1,
- client_close = ClientClose},
- httpc_manager:insert_session(NewSession, ProfileName),
- Relaxed =
- (Request#request.settings)#http_options.relaxed,
- MFA = {httpc_response, parse,
- [State#state.max_header_size, Relaxed]},
- NewTimers = Timers#timers{queue_timer = undefined},
- ?hcrd("session created", []),
- {reply, ok, NewState#state{request = Request,
- session = NewSession,
- mfa = MFA,
- timers = NewTimers}}
- end;
- {error, Reason} ->
- ?hcri("failed sending request", [{reason, Reason}]),
- {reply, {pipeline_failed, Reason}, State}
+ session = NewSession,
+ timers = NewTimers}};
+ undefined ->
+ %% Note: tcp-message receiving has already been
+ %% activated by handle_pipeline/2.
+ ?hcrd("no current request", []),
+ cancel_timer(Timers#timers.queue_timer,
+ timeout_queue),
+ NewSession =
+ Session#session{queue_length = 1,
+ client_close = ClientClose},
+ httpc_manager:insert_session(NewSession, ProfileName),
+ Relaxed =
+ (Request#request.settings)#http_options.relaxed,
+ MFA = {httpc_response, parse,
+ [State#state.max_header_size, Relaxed]},
+ NewTimers = Timers#timers{queue_timer = undefined},
+ ?hcrd("session created", []),
+ {reply, ok, NewState#state{request = Request,
+ session = NewSession,
+ mfa = MFA,
+ timers = NewTimers}}
+ end;
+ {error, Reason} ->
+ ?hcri("failed sending request", [{reason, Reason}]),
+ {reply, {pipeline_failed, Reason}, State}
end;
handle_call(#request{address = Addr} = Request, _,
- #state{status = Status,
- session = #session{type = keep_alive} = Session,
- timers = Timers,
- options = #options{proxy = Proxy} = _Options,
- profile_name = ProfileName} = State)
+ #state{status = Status,
+ session = #session{type = keep_alive} = Session,
+ timers = Timers,
+ options = #options{proxy = Proxy} = _Options,
+ profile_name = ProfileName} = State)
when Status =/= undefined ->
?hcrv("new request on a keep-alive session",
- [{request, Request},
- {profile, ProfileName},
- {status, Status}]),
+ [{request, Request},
+ {profile, ProfileName},
+ {status, Status}]),
Address = handle_proxy(Addr, Proxy),
case httpc_request:send(Address, Session, Request) of
- ok ->
+ ok ->
- ?hcrd("request sent", []),
+ ?hcrd("request sent", []),
- %% Activate the request time out for the new request
- NewState =
- activate_request_timeout(State#state{request = Request}),
+ %% Activate the request time out for the new request
+ NewState =
+ activate_request_timeout(State#state{request = Request}),
- ClientClose =
- httpc_request:is_client_closing(Request#request.headers),
+ ClientClose =
+ httpc_request:is_client_closing(Request#request.headers),
- case State#state.request of
- #request{} -> %% Old request not yet finished
- %% Make sure to use the new value of timers in state
- ?hcrd("old request still not finished", []),
- NewTimers = NewState#state.timers,
+ case State#state.request of
+ #request{} -> %% Old request not yet finished
+ %% Make sure to use the new value of timers in state
+ ?hcrd("old request still not finished", []),
+ NewTimers = NewState#state.timers,
NewKeepAlive = queue:in(Request, State#state.keep_alive),
- NewSession =
- Session#session{queue_length =
- %% Queue + current
- queue:len(NewKeepAlive) + 1,
- client_close = ClientClose},
- insert_session(NewSession, ProfileName),
- ?hcrd("session updated", []),
+ NewSession =
+ Session#session{queue_length =
+ %% Queue + current
+ queue:len(NewKeepAlive) + 1,
+ client_close = ClientClose},
+ insert_session(NewSession, ProfileName),
+ ?hcrd("session updated", []),
{reply, ok, State#state{keep_alive = NewKeepAlive,
- session = NewSession,
- timers = NewTimers}};
- undefined ->
- %% Note: tcp-message reciving has already been
- %% activated by handle_pipeline/2.
- ?hcrd("no current request", []),
- cancel_timer(Timers#timers.queue_timer,
- timeout_queue),
- NewSession =
- Session#session{queue_length = 1,
- client_close = ClientClose},
- insert_session(NewSession, ProfileName),
- Relaxed =
- (Request#request.settings)#http_options.relaxed,
- MFA = {httpc_response, parse,
- [State#state.max_header_size, Relaxed]},
- {reply, ok, NewState#state{request = Request,
- session = NewSession,
- mfa = MFA}}
- end;
+ session = NewSession,
+ timers = NewTimers}};
+ undefined ->
+ %% Note: tcp-message reciving has already been
+ %% activated by handle_pipeline/2.
+ ?hcrd("no current request", []),
+ cancel_timer(Timers#timers.queue_timer,
+ timeout_queue),
+ NewSession =
+ Session#session{queue_length = 1,
+ client_close = ClientClose},
+ insert_session(NewSession, ProfileName),
+ Relaxed =
+ (Request#request.settings)#http_options.relaxed,
+ MFA = {httpc_response, parse,
+ [State#state.max_header_size, Relaxed]},
+ {reply, ok, NewState#state{request = Request,
+ session = NewSession,
+ mfa = MFA}}
+ end;
- {error, Reason} ->
- ?hcri("failed sending request", [{reason, Reason}]),
- {reply, {request_failed, Reason}, State}
+ {error, Reason} ->
+ ?hcri("failed sending request", [{reason, Reason}]),
+ {reply, {request_failed, Reason}, State}
end;
@@ -411,25 +414,25 @@ handle_call(info, _, State) ->
%% request as if it was never issued as in this case the request will
%% not have been sent.
handle_cast({cancel, RequestId, From},
- #state{request = #request{id = RequestId} = Request,
- profile_name = ProfileName,
- canceled = Canceled} = State) ->
+ #state{request = #request{id = RequestId} = Request,
+ profile_name = ProfileName,
+ canceled = Canceled} = State) ->
?hcrv("cancel current request", [{request_id, RequestId},
- {profile, ProfileName},
- {canceled, Canceled}]),
+ {profile, ProfileName},
+ {canceled, Canceled}]),
httpc_manager:request_canceled(RequestId, ProfileName, From),
?hcrv("canceled", []),
{stop, normal,
State#state{canceled = [RequestId | Canceled],
- request = Request#request{from = answer_sent}}};
+ request = Request#request{from = answer_sent}}};
handle_cast({cancel, RequestId, From},
- #state{profile_name = ProfileName,
- request = #request{id = CurrId},
- canceled = Canceled} = State) ->
+ #state{profile_name = ProfileName,
+ request = #request{id = CurrId},
+ canceled = Canceled} = State) ->
?hcrv("cancel", [{request_id, RequestId},
- {curr_req_id, CurrId},
- {profile, ProfileName},
- {canceled, Canceled}]),
+ {curr_req_id, CurrId},
+ {profile, ProfileName},
+ {canceled, Canceled}]),
httpc_manager:request_canceled(RequestId, ProfileName, From),
?hcrv("canceled", []),
{noreply, State#state{canceled = [RequestId | Canceled]}};
@@ -446,94 +449,94 @@ handle_cast(stream_next, #state{session = Session} = State) ->
%% Description: Handling all non call/cast messages
%%--------------------------------------------------------------------
handle_info({Proto, _Socket, Data},
- #state{mfa = {Module, Function, Args},
- request = #request{method = Method,
- stream = Stream} = Request,
- session = Session,
- status_line = StatusLine} = State)
+ #state{mfa = {Module, Function, Args},
+ request = #request{method = Method,
+ stream = Stream} = Request,
+ session = Session,
+ status_line = StatusLine} = State)
when (Proto =:= tcp) orelse
(Proto =:= ssl) orelse
(Proto =:= httpc_handler) ->
?hcri("received data", [{proto, Proto},
- {module, Module},
- {function, Function},
- {method, Method},
- {stream, Stream},
- {session, Session},
- {status_line, StatusLine}]),
+ {module, Module},
+ {function, Function},
+ {method, Method},
+ {stream, Stream},
+ {session, Session},
+ {status_line, StatusLine}]),
FinalResult =
- try Module:Function([Data | Args]) of
- {ok, Result} ->
- ?hcrd("data processed - ok", []),
- handle_http_msg(Result, State);
- {_, whole_body, _} when Method =:= head ->
- ?hcrd("data processed - whole body", []),
- handle_response(State#state{body = <<>>});
- {Module, whole_body, [Body, Length]} ->
- ?hcrd("data processed - whole body", [{length, Length}]),
- {_, Code, _} = StatusLine,
- {NewBody, NewRequest} = stream(Body, Request, Code),
- %% When we stream we will not keep the already
- %% streamed data, that would be a waste of memory.
- NewLength =
- case Stream of
- none ->
- Length;
- _ ->
- Length - size(Body)
- end,
-
- NewState = next_body_chunk(State),
- NewMFA = {Module, whole_body, [NewBody, NewLength]},
- {noreply, NewState#state{mfa = NewMFA,
- request = NewRequest}};
- NewMFA ->
- ?hcrd("data processed - new mfa", []),
- activate_once(Session),
- {noreply, State#state{mfa = NewMFA}}
- catch
- exit:_Exit ->
- ?hcrd("data processing exit", [{exit, _Exit}]),
- ClientReason = {could_not_parse_as_http, Data},
- ClientErrMsg = httpc_response:error(Request, ClientReason),
- NewState = answer_request(Request, ClientErrMsg, State),
- {stop, normal, NewState};
- error:_Error ->
- ?hcrd("data processing error", [{error, _Error}]),
- ClientReason = {could_not_parse_as_http, Data},
- ClientErrMsg = httpc_response:error(Request, ClientReason),
- NewState = answer_request(Request, ClientErrMsg, State),
- {stop, normal, NewState}
-
- end,
+ try Module:Function([Data | Args]) of
+ {ok, Result} ->
+ ?hcrd("data processed - ok", []),
+ handle_http_msg(Result, State);
+ {_, whole_body, _} when Method =:= head ->
+ ?hcrd("data processed - whole body", []),
+ handle_response(State#state{body = <<>>});
+ {Module, whole_body, [Body, Length]} ->
+ ?hcrd("data processed - whole body", [{length, Length}]),
+ {_, Code, _} = StatusLine,
+ {NewBody, NewRequest} = stream(Body, Request, Code),
+ %% When we stream we will not keep the already
+ %% streamed data, that would be a waste of memory.
+ NewLength =
+ case Stream of
+ none ->
+ Length;
+ _ ->
+ Length - size(Body)
+ end,
+
+ NewState = next_body_chunk(State),
+ NewMFA = {Module, whole_body, [NewBody, NewLength]},
+ {noreply, NewState#state{mfa = NewMFA,
+ request = NewRequest}};
+ NewMFA ->
+ ?hcrd("data processed - new mfa", []),
+ activate_once(Session),
+ {noreply, State#state{mfa = NewMFA}}
+ catch
+ exit:_Exit ->
+ ?hcrd("data processing exit", [{exit, _Exit}]),
+ ClientReason = {could_not_parse_as_http, Data},
+ ClientErrMsg = httpc_response:error(Request, ClientReason),
+ NewState = answer_request(Request, ClientErrMsg, State),
+ {stop, normal, NewState};
+ error:_Error ->
+ ?hcrd("data processing error", [{error, _Error}]),
+ ClientReason = {could_not_parse_as_http, Data},
+ ClientErrMsg = httpc_response:error(Request, ClientReason),
+ NewState = answer_request(Request, ClientErrMsg, State),
+ {stop, normal, NewState}
+
+ end,
?hcri("data processed", [{final_result, FinalResult}]),
FinalResult;
handle_info({Proto, Socket, Data},
- #state{mfa = MFA,
- request = Request,
- session = Session,
- status = Status,
- status_line = StatusLine,
- profile_name = Profile} = State)
+ #state{mfa = MFA,
+ request = Request,
+ session = Session,
+ status = Status,
+ status_line = StatusLine,
+ profile_name = Profile} = State)
when (Proto =:= tcp) orelse
(Proto =:= ssl) orelse
(Proto =:= httpc_handler) ->
error_logger:warning_msg("Received unexpected ~p data on ~p"
- "~n Data: ~p"
- "~n MFA: ~p"
- "~n Request: ~p"
- "~n Session: ~p"
- "~n Status: ~p"
- "~n StatusLine: ~p"
- "~n Profile: ~p"
- "~n",
- [Proto, Socket, Data, MFA,
- Request, Session, Status, StatusLine, Profile]),
+ "~n Data: ~p"
+ "~n MFA: ~p"
+ "~n Request: ~p"
+ "~n Session: ~p"
+ "~n Status: ~p"
+ "~n StatusLine: ~p"
+ "~n Profile: ~p"
+ "~n",
+ [Proto, Socket, Data, MFA,
+ Request, Session, Status, StatusLine, Profile]),
{noreply, State};
@@ -572,45 +575,45 @@ 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,
- profile_name = ProfileName} = 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_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]}};
+ canceled = [RequestId | Canceled]}};
handle_info({timeout, RequestId},
- #state{canceled = Canceled,
- profile_name = ProfileName} = State) ->
+ #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
- httpc_response:send(From,
- httpc_response:error(Request, timeout)),
- httpc_manager:request_done(RequestId, ProfileName),
- ?hcrv("response (timeout) sent", []),
- [Request#request{from = answer_sent}];
- (_) ->
- true
- end,
+ fun(#request{id = Id, from = From} = Request) when Id =:= RequestId ->
+ ?hcrv("found request", [{id, Id}, {from, From}]),
+ %% Notify the owner
+ httpc_response:send(From,
+ httpc_response:error(Request, timeout)),
+ httpc_manager:request_done(RequestId, ProfileName),
+ ?hcrv("response (timeout) sent", []),
+ [Request#request{from = answer_sent}];
+ (_) ->
+ true
+ end,
case State#state.status of
- pipeline ->
- ?hcrd("pipeline", []),
- Pipeline = queue:filter(Filter, State#state.pipeline),
- {noreply, State#state{canceled = [RequestId | Canceled],
- pipeline = Pipeline}};
- keep_alive ->
- ?hcrd("keep_alive", []),
- KeepAlive = queue:filter(Filter, State#state.keep_alive),
- {noreply, State#state{canceled = [RequestId | Canceled],
- keep_alive = KeepAlive}}
+ pipeline ->
+ ?hcrd("pipeline", []),
+ Pipeline = queue:filter(Filter, State#state.pipeline),
+ {noreply, State#state{canceled = [RequestId | Canceled],
+ pipeline = Pipeline}};
+ keep_alive ->
+ ?hcrd("keep_alive", []),
+ KeepAlive = queue:filter(Filter, State#state.keep_alive),
+ {noreply, State#state{canceled = [RequestId | Canceled],
+ keep_alive = KeepAlive}}
end;
handle_info(timeout_queue, State = #state{request = undefined}) ->
@@ -619,11 +622,11 @@ handle_info(timeout_queue, State = #state{request = undefined}) ->
%% Timing was such as the pipeline_timout was not canceled!
handle_info(timeout_queue, #state{timers = Timers} = State) ->
{noreply, State#state{timers =
- Timers#timers{queue_timer = undefined}}};
+ Timers#timers{queue_timer = undefined}}};
%% Setting up the connection to the server somehow failed.
handle_info({init_error, Tag, ClientErrMsg},
- State = #state{request = Request}) ->
+ State = #state{request = Request}) ->
?hcrv("init error", [{tag, Tag}, {client_error, ClientErrMsg}]),
NewState = answer_request(Request, ClientErrMsg, State),
{stop, normal, NewState};
@@ -647,21 +650,21 @@ handle_info({'EXIT', _, _}, State) ->
%% Init error there is no socket to be closed.
terminate(normal,
- #state{request = Request,
- session = {send_failed, AReason} = Reason} = State) ->
+ #state{request = Request,
+ session = {send_failed, AReason} = Reason} = State) ->
?hcrd("terminate", [{send_reason, AReason}, {request, Request}]),
maybe_send_answer(Request,
- httpc_response:error(Request, Reason),
- State),
+ httpc_response:error(Request, Reason),
+ State),
ok;
terminate(normal,
- #state{request = Request,
- session = {connect_failed, AReason} = Reason} = State) ->
+ #state{request = Request,
+ session = {connect_failed, AReason} = Reason} = State) ->
?hcrd("terminate", [{connect_reason, AReason}, {request, Request}]),
maybe_send_answer(Request,
- httpc_response:error(Request, Reason),
- State),
+ httpc_response:error(Request, Reason),
+ State),
ok;
terminate(normal, #state{session = undefined}) ->
@@ -670,21 +673,21 @@ 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{session = #session{id = undefined} = Session}) ->
+ #state{session = #session{id = undefined} = Session}) ->
close_socket(Session);
%% Socket closed remotely
terminate(normal,
- #state{session = #session{socket = {remote_close, Socket},
- socket_type = SocketType,
- id = Id},
- profile_name = ProfileName,
- request = Request,
- timers = Timers,
- pipeline = Pipeline,
- keep_alive = KeepAlive} = State) ->
+ #state{session = #session{socket = {remote_close, Socket},
+ socket_type = SocketType,
+ id = Id},
+ profile_name = ProfileName,
+ request = Request,
+ timers = Timers,
+ pipeline = Pipeline,
+ keep_alive = KeepAlive} = State) ->
?hcrt("terminate(normal) - remote close",
- [{id, Id}, {profile, ProfileName}]),
+ [{id, Id}, {profile, ProfileName}]),
%% Clobber session
(catch httpc_manager:delete_session(Id, ProfileName)),
@@ -702,15 +705,15 @@ terminate(normal,
http_transport:close(SocketType, Socket);
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) ->
+ 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}]),
+ [{id, Id}, {profile, ProfileName}, {reason, Reason}]),
%% Clobber session
(catch httpc_manager:delete_session(Id, ProfileName)),
@@ -728,16 +731,16 @@ terminate(Reason, #state{request = undefined}) ->
terminate(Reason, #state{request = Request} = State) ->
?hcrd("terminate", [{reason, Reason}, {request, Request}]),
NewState = maybe_send_answer(Request,
- httpc_response:error(Request, Reason),
- State),
+ httpc_response:error(Request, Reason),
+ State),
terminate(Reason, NewState#state{request = undefined}).
maybe_retry_queue(Q, State) ->
case queue:is_empty(Q) of
- false ->
- retry_pipeline(queue:to_list(Q), State);
- true ->
- ok
+ false ->
+ retry_pipeline(queue:to_list(Q), State);
+ true ->
+ ok
end.
maybe_send_answer(#request{from = answer_sent}, _Reason, State) ->
@@ -761,44 +764,44 @@ deliver_answer(Request) ->
%%--------------------------------------------------------------------
code_change(_,
- #state{session = OldSession,
- profile_name = ProfileName} = State,
- upgrade_from_pre_5_8_1) ->
+ #state{session = OldSession,
+ profile_name = ProfileName} = State,
+ upgrade_from_pre_5_8_1) ->
case OldSession of
- {session,
- Id, ClientClose, Scheme, Socket, SocketType, QueueLen, Type} ->
- NewSession = #session{id = Id,
- client_close = ClientClose,
- scheme = Scheme,
- socket = Socket,
- socket_type = SocketType,
- queue_length = QueueLen,
- type = Type},
- insert_session(NewSession, ProfileName),
- {ok, State#state{session = NewSession}};
- _ ->
- {ok, State}
+ {session,
+ Id, ClientClose, Scheme, Socket, SocketType, QueueLen, Type} ->
+ NewSession = #session{id = Id,
+ client_close = ClientClose,
+ scheme = Scheme,
+ socket = Socket,
+ socket_type = SocketType,
+ queue_length = QueueLen,
+ type = Type},
+ insert_session(NewSession, ProfileName),
+ {ok, State#state{session = NewSession}};
+ _ ->
+ {ok, State}
end;
code_change(_,
- #state{session = OldSession,
- profile_name = ProfileName} = State,
- downgrade_to_pre_5_8_1) ->
+ #state{session = OldSession,
+ profile_name = ProfileName} = State,
+ downgrade_to_pre_5_8_1) ->
case OldSession of
- #session{id = Id,
- client_close = ClientClose,
- scheme = Scheme,
- socket = Socket,
- socket_type = SocketType,
- queue_length = QueueLen,
- type = Type} ->
- NewSession = {session,
- Id, ClientClose, Scheme, Socket, SocketType,
- QueueLen, Type},
- insert_session(NewSession, ProfileName),
- {ok, State#state{session = NewSession}};
- _ ->
- {ok, State}
+ #session{id = Id,
+ client_close = ClientClose,
+ scheme = Scheme,
+ socket = Socket,
+ socket_type = SocketType,
+ queue_length = QueueLen,
+ type = Type} ->
+ NewSession = {session,
+ Id, ClientClose, Scheme, Socket, SocketType,
+ QueueLen, Type},
+ insert_session(NewSession, ProfileName),
+ {ok, State#state{session = NewSession}};
+ _ ->
+ {ok, State}
end;
code_change(_, State, _) ->
@@ -806,22 +809,22 @@ code_change(_, State, _) ->
%% new_http_options({http_options, TimeOut, AutoRedirect, SslOpts,
-%% Auth, Relaxed}) ->
+%% Auth, Relaxed}) ->
%% {http_options, "HTTP/1.1", TimeOut, AutoRedirect, SslOpts,
%% Auth, Relaxed}.
%% old_http_options({http_options, _, TimeOut, AutoRedirect,
-%% SslOpts, Auth, Relaxed}) ->
+%% 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),
+%% lists:map(fun(Request) ->
+%% Settings =
+%% Fun(Request#request.settings),
+%% Request#request{settings = Settings}
+%% end, List),
%% queue:from_list(NewList).
@@ -830,97 +833,121 @@ code_change(_, State, _) ->
%%%--------------------------------------------------------------------
connect(SocketType, ToAddress,
- #options{ipfamily = IpFamily,
- ip = FromAddress,
- port = FromPort,
- socket_opts = Opts0}, Timeout) ->
+ #options{ipfamily = IpFamily,
+ ip = FromAddress,
+ port = FromPort,
+ socket_opts = Opts0}, Timeout) ->
Opts1 =
- case FromPort of
- default ->
- Opts0;
- _ ->
- [{port, FromPort} | Opts0]
- end,
+ case FromPort of
+ default ->
+ Opts0;
+ _ ->
+ [{port, FromPort} | Opts0]
+ end,
Opts2 =
- case FromAddress of
- default ->
- Opts1;
- _ ->
- [{ip, FromAddress} | Opts1]
- end,
+ case FromAddress of
+ default ->
+ Opts1;
+ _ ->
+ [{ip, FromAddress} | Opts1]
+ end,
case IpFamily of
- inet6fb4 ->
- Opts3 = [inet6 | Opts2],
- case http_transport:connect(SocketType,
- ToAddress, Opts3, Timeout) of
- {error, Reason6} ->
- Opts4 = [inet | Opts2],
- case http_transport:connect(SocketType,
- ToAddress, Opts4, Timeout) of
- {error, Reason4} ->
- {error, {failed_connect,
- [{to_address, ToAddress},
- {inet6, Opts3, Reason6},
- {inet, Opts4, Reason4}]}};
- OK ->
- OK
- end;
- OK ->
- OK
- end;
- _ ->
- Opts3 = [IpFamily | Opts2],
- case http_transport:connect(SocketType, ToAddress, Opts3, Timeout) of
- {error, Reason} ->
- {error, {failed_connect, [{to_address, ToAddress},
- {IpFamily, Opts3, Reason}]}};
- Else ->
- Else
- end
+ inet6fb4 ->
+ Opts3 = [inet6 | Opts2],
+ case http_transport:connect(SocketType,
+ ToAddress, Opts3, Timeout) of
+ {error, Reason6} ->
+ Opts4 = [inet | Opts2],
+ case http_transport:connect(SocketType,
+ ToAddress, Opts4, Timeout) of
+ {error, Reason4} ->
+ {error, {failed_connect,
+ [{to_address, ToAddress},
+ {inet6, Opts3, Reason6},
+ {inet, Opts4, Reason4}]}};
+ OK ->
+ OK
+ end;
+ OK ->
+ OK
+ end;
+ _ ->
+ Opts3 = [IpFamily | Opts2],
+ case http_transport:connect(SocketType, ToAddress, Opts3, Timeout) of
+ {error, Reason} ->
+ {error, {failed_connect, [{to_address, ToAddress},
+ {IpFamily, Opts3, Reason}]}};
+ Else ->
+ Else
+ end
end.
connect_and_send_first_request(Address, Request, #state{options = Options} = State) ->
SocketType = socket_type(Request),
ConnTimeout = (Request#request.settings)#http_options.connect_timeout,
?hcri("connect",
- [{address, Address}, {request, Request}, {options, Options}]),
+ [{address, Address}, {request, Request}, {options, Options}]),
case connect(SocketType, Address, Options, ConnTimeout) of
- {ok, Socket} ->
- ClientClose =
- httpc_request:is_client_closing(
- Request#request.headers),
+ {ok, Socket} ->
+ ClientClose =
+ httpc_request:is_client_closing(
+ Request#request.headers),
+ SessionType = httpc_manager:session_type(Options),
+ SocketType = socket_type(Request),
+ Session = #session{id = {Request#request.address, self()},
+ scheme = Request#request.scheme,
+ socket = Socket,
+ socket_type = SocketType,
+ client_close = ClientClose,
+ type = SessionType},
+ ?hcri("connected - now send first request", [{socket, Socket}]),
+
+ case httpc_request:send(Address, Session, Request) of
+ ok ->
+ ?hcri("first request sent", []),
+ TmpState = State#state{request = Request,
+ session = Session,
+ mfa = init_mfa(Request, State),
+ status_line =
+ init_status_line(Request),
+ headers = undefined,
+ body = undefined,
+ status = new},
+ http_transport:setopts(SocketType,
+ Socket, [{active, once}]),
+ NewState = activate_request_timeout(TmpState),
+ {ok, NewState};
+ {error, Reason} ->
+ self() ! {init_error, error_sending,
+ httpc_response:error(Request, Reason)},
+ {ok, State#state{request = Request,
+ session =
+ #session{socket = Socket}}}
+ end;
+ {error, Reason} ->
+ self() ! {init_error, error_connecting,
+ httpc_response:error(Request, Reason)},
+ {ok, State#state{request = Request}}
+ end.
+
+connect_and_send_upgrade_request(Address, Request, #state{options = Options} = State) ->
+ ConnTimeout = (Request#request.settings)#http_options.connect_timeout,
+ SocketType = ip_comm,
+ case connect(SocketType, Address, Options, ConnTimeout) of
+ {ok, Socket} ->
SessionType = httpc_manager:session_type(Options),
- SocketType = socket_type(Request),
- Session = #session{id = {Request#request.address, self()},
- scheme = Request#request.scheme,
- socket = Socket,
+ Session = #session{socket = Socket,
socket_type = SocketType,
- client_close = ClientClose,
+ id = {Request#request.address, self()},
+ scheme = http,
+ client_close = false,
type = SessionType},
- ?hcri("connected - now send first request", [{socket, Socket}]),
-
- case httpc_request:send(Address, Session, Request) of
- ok ->
- ?hcri("first request sent", []),
- TmpState = State#state{request = Request,
- session = Session,
- mfa = init_mfa(Request, State),
- status_line =
- init_status_line(Request),
- headers = undefined,
- body = undefined,
- status = new},
- http_transport:setopts(SocketType,
- Socket, [{active, once}]),
- NewState = activate_request_timeout(TmpState),
- {ok, NewState};
- {error, Reason} ->
- self() ! {init_error, error_sending,
- httpc_response:error(Request, Reason)},
- {ok, State#state{request = Request,
- session =
- #session{socket = Socket}}}
- end;
+ ErrorHandler =
+ fun(ERequest, EState, EReason) ->
+ self() ! {init_error, error_sending,
+ httpc_response:error(ERequest, EReason)},
+ {ok, EState#state{request = ERequest}} end,
+ tls_tunnel(Address, Request, State#state{session = Session}, ErrorHandler);
{error, Reason} ->
self() ! {init_error, error_connecting,
httpc_response:error(Request, Reason)},
@@ -1024,15 +1051,25 @@ handle_http_msg(Body, #state{status_line = {_,Code, _}} = State) ->
{NewBody, NewRequest} = stream(Body, State#state.request, Code),
handle_response(State#state{body = NewBody, request = NewRequest}).
-handle_http_body(<<>>, State = #state{status_line = {_,304, _}}) ->
+handle_http_body(_, #state{status = {ssl_tunnel, _},
+ status_line = {_,200, _}} = State) ->
+ tls_upgrade(State);
+
+handle_http_body(_, #state{status = {ssl_tunnel, Request},
+ status_line = StatusLine} = State) ->
+ ClientErrMsg = httpc_response:error(Request,{could_no_establish_ssh_tunnel, StatusLine}),
+ NewState = answer_request(Request, ClientErrMsg, State),
+ {stop, normal, NewState};
+
+handle_http_body(<<>>, #state{status_line = {_,304, _}} = State) ->
?hcrt("handle_http_body - 304", []),
handle_response(State#state{body = <<>>});
-handle_http_body(<<>>, State = #state{status_line = {_,204, _}}) ->
+handle_http_body(<<>>, #state{status_line = {_,204, _}} = State) ->
?hcrt("handle_http_body - 204", []),
handle_response(State#state{body = <<>>});
-handle_http_body(<<>>, State = #state{request = #request{method = head}}) ->
+handle_http_body(<<>>, #state{request = #request{method = head}} = State) ->
?hcrt("handle_http_body - head", []),
handle_response(State#state{body = <<>>});
@@ -1119,7 +1156,7 @@ handle_response(#state{request = Request,
{session, Session},
{status_line, StatusLine}]),
- handle_cookies(Headers, Request, Options, ProfileName),
+ handle_cookies(Headers, Request, Options, httpc_manager), %% FOO profile_name
case httpc_response:result({StatusLine, Headers, Body}, Request) of
%% 100-continue
continue ->
@@ -1503,6 +1540,12 @@ retry_pipeline([Request | PipeLine],
end,
retry_pipeline(PipeLine, NewState).
+handle_proxy_options(https, #options{https_proxy = {HttpsProxy, _} = HttpsProxyOpt}) when
+ HttpsProxy =/= undefined ->
+ HttpsProxyOpt;
+handle_proxy_options(_, #options{proxy = Proxy}) ->
+ Proxy.
+
%%% Check to see if the given {Host,Port} tuple is in the NoProxyList
%%% Returns an eventually updated {Host,Port} tuple, with the proxy address
handle_proxy(HostPort = {Host, _Port}, {Proxy, NoProxy}) ->
@@ -1696,6 +1739,96 @@ send_raw(SocketType, Socket, ProcessBody, Acc) ->
end
end.
+tls_tunnel(Address, Request, #state{session = #session{socket = Socket,
+ socket_type = SocketType} = Session} = State,
+ ErrorHandler) ->
+ UpgradeRequest = tls_tunnel_request(Request),
+ case httpc_request:send(Address, Session, UpgradeRequest) of
+ ok ->
+ TmpState = State#state{request = UpgradeRequest,
+ %% session = Session,
+ mfa = init_mfa(UpgradeRequest, State),
+ status_line =
+ init_status_line(UpgradeRequest),
+ headers = undefined,
+ body = undefined},
+ http_transport:setopts(SocketType,
+ Socket, [{active, once}]),
+ NewState = activate_request_timeout(TmpState),
+ {ok, NewState#state{status = {ssl_tunnel, Request}}};
+ {error, Reason} ->
+ ErrorHandler(Request, State, Reason)
+ end.
+
+tls_tunnel_request(#request{headers = Headers,
+ settings = Options,
+ address = {Host, Port}= Adress,
+ ipv6_host_with_brackets = IPV6}) ->
+
+ URI = Host ++":" ++ integer_to_list(Port),
+
+ #request{
+ id = make_ref(),
+ from = self(),
+ scheme = http, %% Use tcp-first and then upgrade!
+ address = Adress,
+ path = URI,
+ pquery = "",
+ method = connect,
+ headers = #http_request_h{host = host_header(Headers, URI),
+ te = "",
+ pragma = "no-cache",
+ other = [{"Proxy-Connection", " Keep-Alive"}]},
+ settings = Options,
+ abs_uri = URI,
+ stream = false,
+ userinfo = "",
+ headers_as_is = [],
+ started = http_util:timestamp(),
+ ipv6_host_with_brackets = IPV6
+ }.
+
+host_header(#http_request_h{host = Host}, _) ->
+ Host;
+
+%% Handles header_as_is
+host_header(_, URI) ->
+ {ok, {_, _, Host, _, _, _}} = http_uri:parse(URI),
+ Host.
+
+tls_upgrade(#state{status =
+ {ssl_tunnel,
+ #request{settings =
+ #http_options{ssl = {_, TLSOptions} = SocketType}} = Request},
+ session = #session{socket = TCPSocket} = Session0,
+ options = Options} = State) ->
+
+ case ssl:connect(TCPSocket, TLSOptions) of
+ {ok, TLSSocket} ->
+ Address = Request#request.address,
+ ClientClose = httpc_request:is_client_closing(Request#request.headers),
+ SessionType = httpc_manager:session_type(Options),
+ Session = Session0#session{
+ scheme = https,
+ socket = TLSSocket,
+ socket_type = SocketType,
+ type = SessionType,
+ client_close = ClientClose},
+ httpc_request:send(Address, Session, Request),
+ http_transport:setopts(SocketType, TLSSocket, [{active, once}]),
+ NewState = State#state{session = Session,
+ request = Request,
+ mfa = init_mfa(Request, State),
+ status_line =
+ init_status_line(Request),
+ headers = undefined,
+ body = undefined,
+ status = new
+ },
+ {noreply, activate_request_timeout(NewState)};
+ {error, _Reason} ->
+ {stop, normal, State#state{request = Request}}
+ end.
%% ---------------------------------------------------------------------
%% Session wrappers
@@ -1713,7 +1846,32 @@ update_session(ProfileName, #session{id = SessionId} = Session, Pos, Value) ->
catch
error:undef -> % This could happen during code upgrade
Session2 = erlang:setelement(Pos, Session, Value),
- insert_session(Session2, ProfileName)
+ insert_session(Session2, ProfileName);
+ T:E ->
+ error_logger:error_msg("Failed updating session: "
+ "~n ProfileName: ~p"
+ "~n SessionId: ~p"
+ "~n Pos: ~p"
+ "~n Value: ~p"
+ "~nwhen"
+ "~n Session (db) info: ~p"
+ "~n Session (db): ~p"
+ "~n Session (record): ~p"
+ "~n T: ~p"
+ "~n E: ~p",
+ [ProfileName, SessionId, Pos, Value,
+ (catch httpc_manager:which_session_info(ProfileName)),
+ Session,
+ (catch httpc_manager:lookup_session(ProfileName, SessionId)),
+ T, E]),
+ exit({failed_updating_session,
+ [{profile, ProfileName},
+ {session_id, SessionId},
+ {pos, Pos},
+ {value, Value},
+ {etype, T},
+ {error, E},
+ {stacktrace, erlang:get_stacktrace()}]})
end.
diff --git a/lib/inets/src/http_client/httpc_internal.hrl b/lib/inets/src/http_client/httpc_internal.hrl
index 8af752546c..30e2742e9d 100644
--- a/lib/inets/src/http_client/httpc_internal.hrl
+++ b/lib/inets/src/http_client/httpc_internal.hrl
@@ -37,6 +37,7 @@
-define(HTTP_MAX_REDIRECTS, 4).
-define(HTTP_KEEP_ALIVE_TIMEOUT, 120000).
-define(HTTP_KEEP_ALIVE_LENGTH, 5).
+-define(TLS_UPGRADE_TOKEN, "TLS/1.0").
%%% HTTP Client per request settings
-record(http_options,
@@ -72,6 +73,7 @@
-record(options,
{
proxy = {undefined, []}, % {{ProxyHost, ProxyPort}, [NoProxy]},
+ https_proxy = {undefined, []}, % {{ProxyHost, ProxyPort}, [NoProxy]}
%% 0 means persistent connections are used without pipelining
pipeline_timeout = ?HTTP_PIPELINE_TIMEOUT,
max_pipeline_length = ?HTTP_PIPELINE_LENGTH,
diff --git a/lib/inets/src/http_client/httpc_manager.erl b/lib/inets/src/http_client/httpc_manager.erl
index b225b43214..c45dcab802 100644
--- a/lib/inets/src/http_client/httpc_manager.erl
+++ b/lib/inets/src/http_client/httpc_manager.erl
@@ -34,8 +34,11 @@
retry_request/2,
redirect_request/2,
insert_session/2,
+ lookup_session/2,
update_session/4,
delete_session/2,
+ which_sessions/1,
+ which_session_info/1,
set_options/2,
get_options/2,
store_cookies/3,
@@ -59,17 +62,9 @@
options = #options{}
}).
--record(handler_info,
- {
- id, % Id of the request: request_id()
- starter, % Pid of the handler starter process (temp): pid()
- handler, % Pid of the handler process: pid()
- from, % From for the request: from()
- state % State of the handler: initiating | started | operational | canceled
- }).
-
-define(DELAY, 500).
+
%%====================================================================
%% Internal Application API
%%====================================================================
@@ -195,13 +190,28 @@ insert_session(Session, ProfileName) ->
%%--------------------------------------------------------------------
+%% Function: lookup_session(SessionId, ProfileName) -> _
+%% SessionId - term()
+%% ProfileName - atom()
+%%
+%% Description: Looks up a session record in the httpc manager
+%% table <ProfileName>__session_db.
+%%--------------------------------------------------------------------
+
+lookup_session(SessionId, ProfileName) ->
+ SessionDbName = session_db_name(ProfileName),
+ ?hcrt("lookup session", [{session_id, SessionId}, {profile, ProfileName}]),
+ ets:lookup(SessionDbName, SessionId).
+
+
+%%--------------------------------------------------------------------
%% Function: update_session(ProfileName, SessionId, Pos, Value) -> _
%% Session - #session{}
%% ProfileName - atom()
%%
%% Description: Update, only one field (Pos) of the session record
%% identified by the SessionId, the session information
-%% of the httpc manager table <ProfileName>_session_db.
+%% of the httpc manager table <ProfileName>__session_db.
%% Intended to be called by the httpc request handler process.
%%--------------------------------------------------------------------
@@ -216,12 +226,12 @@ update_session(ProfileName, SessionId, Pos, Value) ->
%%--------------------------------------------------------------------
-%% Function: delete_session(SessionId, ProfileName) -> _
+%% Function: delete_session(SessionId, ProfileName) -> void()
%% SessionId - {{Host, Port}, HandlerPid}
%% ProfileName - atom()
%%
%% Description: Deletes session information from the httpc manager
-%% table httpc_manager_session_db_<Profile>. Intended to be called by
+%% table <ProfileName>__session_db. Intended to be called by
%% the httpc request handler process.
%%--------------------------------------------------------------------
@@ -232,6 +242,57 @@ delete_session(SessionId, ProfileName) ->
%%--------------------------------------------------------------------
+%% Function: which sessions(ProfileName) -> SessionsInfo
+%% ProfileName - atom()
+%% SessionsInfo - {GoodSessions, BadSessions, NonSessions}
+%% GoodSessions - [#session{}]
+%% BadSessions - [tuple()]
+%% NonSessions - [term()]
+%%
+%% Description: Produces a list of all sessions in the session db.
+%% Used for debugging and since that is the intent, there is some
+%% checking and transforming done, which produces the results.
+%%--------------------------------------------------------------------
+
+which_sessions(ProfileName) ->
+ ?hcrt("which_sessions", [{profile, ProfileName}]),
+ SessionDbName = session_db_name(ProfileName),
+ which_sessions2(SessionDbName).
+
+which_sessions2(SessionDbName) ->
+ Sessions = which_sessions_order(ets:tab2list(SessionDbName)),
+ GoodSessions = [GoodSession || {good_session, GoodSession} <- Sessions],
+ BadSessions = [BadSession || {bad_session, BadSession} <- Sessions],
+ NonSessions = [NonSession || {non_session, NonSession} <- Sessions],
+ {lists:keysort(#session.id, GoodSessions),
+ lists:keysort(#session.id, BadSessions),
+ lists:sort(NonSessions)}.
+
+which_sessions_order([]) ->
+ [];
+which_sessions_order([Session|Sessions]) when is_record(Session, session) ->
+ [{good_session, Session} | which_sessions_order(Sessions)];
+which_sessions_order([BadSession|Sessions])
+ when is_tuple(BadSession) andalso
+ (element(1, BadSession) =:= session) ->
+ [{bad_session, BadSession} | which_sessions_order(Sessions)];
+which_sessions_order([NonSession|Sessions]) ->
+ [{non_session, NonSession} | which_sessions_order(Sessions)].
+
+
+%%--------------------------------------------------------------------
+%% Function: which session_info(ProfileName) -> list()
+%%
+%% Description: Produces a ets table info list of the sessions table
+%%--------------------------------------------------------------------
+
+which_session_info(ProfileName) ->
+ SessionDbName = session_db_name(ProfileName),
+ ?hcrt("which_session_info", [{profile, ProfileName}]),
+ ets:info(SessionDbName).
+
+
+%%--------------------------------------------------------------------
%% Function: set_options(Options, ProfileName) -> ok
%%
%% Options = [Option]
@@ -379,8 +440,7 @@ do_init(ProfileName, CookiesDir) ->
%% Create handler db
?hcrt("create handler/request db", []),
HandlerDbName = handler_db_name(ProfileName),
- ets:new(HandlerDbName,
- [protected, set, named_table, {keypos, #handler_info.id}]),
+ ets:new(HandlerDbName, [protected, set, named_table, {keypos, 1}]),
%% Cookie DB
?hcrt("create cookie db", []),
@@ -414,9 +474,10 @@ handle_call({request, Request}, _, State) ->
{stop, Error, httpc_response:error(Request, Error), State}
end;
-handle_call({cancel_request, RequestId}, From, State) ->
+handle_call({cancel_request, RequestId}, From,
+ #state{handler_db = HandlerDb} = State) ->
?hcri("cancel_request", [{request_id, RequestId}]),
- case ets:lookup(State#state.handler_db, RequestId) of
+ case ets:lookup(HandlerDb, RequestId) of
[] ->
%% The request has allready compleated make sure
%% it is deliverd to the client process queue so
@@ -428,9 +489,9 @@ handle_call({cancel_request, RequestId}, From, State) ->
{noreply, State};
[{_, Pid, _}] ->
httpc_handler:cancel(RequestId, Pid, From),
- {noreply, State#state{cancel =
- [{RequestId, Pid, From} |
- State#state.cancel]}}
+ {noreply,
+ State#state{cancel =
+ [{RequestId, Pid, From} | State#state.cancel]}}
end;
handle_call(reset_cookies, _, #state{cookie_db = CookieDb} = State) ->
@@ -457,8 +518,8 @@ handle_call({which_cookies, Url, Options}, _,
handle_call({get_options, OptionItems}, _, #state{options = Options} = State) ->
?hcrv("get options", [{option_items, OptionItems}]),
- Reply = [{OptionItem, get_option(OptionItem, Options)} || OptionItem <-
- OptionItems],
+ Reply = [{OptionItem, get_option(OptionItem, Options)} ||
+ OptionItem <- OptionItems],
{reply, Reply, State};
handle_call(info, _, State) ->
@@ -516,6 +577,7 @@ handle_cast({set_options, Options}, State = #state{options = OldOptions}) ->
?hcrv("set options", [{options, Options}, {old_options, OldOptions}]),
NewOptions =
#options{proxy = get_proxy(Options, OldOptions),
+ https_proxy = get_https_proxy(Options, OldOptions),
pipeline_timeout = get_pipeline_timeout(Options, OldOptions),
max_pipeline_length = get_max_pipeline_length(Options, OldOptions),
max_keep_alive_length = get_max_keep_alive_length(Options, OldOptions),
@@ -645,7 +707,7 @@ code_change(_,
code_change(_, State, _) ->
{ok, State}.
-%% This function is to catch everything that calls through the cracks...
+%% This function is used to catch everything that falls through the cracks...
update_session_table(SessionDB, Transform) ->
ets:safe_fixtable(SessionDB, true),
update_session_table(SessionDB, ets:first(SessionDB), Transform),
@@ -673,39 +735,42 @@ update_session_table(SessionDB, Key, Transform) ->
%%--------------------------------------------------------------------
get_manager_info(#state{handler_db = HDB,
- cookie_db = CDB} = _State) ->
+ session_db = SDB,
+ cookie_db = CDB,
+ options = Options} = _State) ->
HandlerInfo = get_handler_info(HDB),
+ SessionInfo = which_sessions2(SDB),
+ OptionsInfo =
+ [{Item, get_option(Item, Options)} ||
+ Item <- record_info(fields, options)],
CookieInfo = httpc_cookie:which_cookies(CDB),
- [{handlers, HandlerInfo}, {cookies, CookieInfo}].
+ [{handlers, HandlerInfo},
+ {sessions, SessionInfo},
+ {options, OptionsInfo},
+ {cookies, CookieInfo}].
+
+sort_handlers(Unsorted) ->
+ sort_handlers2(lists:keysort(1, Unsorted)).
+
+sort_handlers2([]) ->
+ [];
+sort_handlers2([{HandlerPid, RequestId}|L]) ->
+ {Handler, Rest} = sort_handlers2(HandlerPid, [RequestId], L),
+ [Handler | sort_handlers2(Rest)].
+
+sort_handlers2(HandlerPid, Reqs, []) ->
+ {{HandlerPid, lists:sort(Reqs)}, []};
+sort_handlers2(HandlerPid, Reqs, [{HandlerPid, ReqId}|Rest]) ->
+ sort_handlers2(HandlerPid, [ReqId|Reqs], Rest);
+sort_handlers2(HandlerPid1, Reqs, [{HandlerPid2, _}|_] = Rest)
+ when HandlerPid1 =/= HandlerPid2 ->
+ {{HandlerPid1, lists:sort(Reqs)}, Rest}.
get_handler_info(Tab) ->
- Pattern = #handler_info{handler = '$1',
- state = '$2',
- _ = '_'},
- Handlers1 = [{Pid, State} || [Pid, State] <- ets:match(Tab, Pattern)],
- F = fun({Pid, State} = Elem, Acc) when State =/= canceled ->
- case lists:keymember(Pid, 1, Acc) of
- true ->
- Acc;
- false ->
- [Elem | Acc]
- end;
- (_, Acc) ->
- Acc
- end,
- Handlers2 = lists:foldl(F, [], Handlers1),
- 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.
+ Pattern = {'$2', '$1', '_'},
+ Handlers1 = [{Pid, Id} || [Pid, Id] <- ets:match(Tab, Pattern)],
+ Handlers2 = sort_handlers(Handlers1),
+ [{Pid, Reqs, httpc_handler:info(Pid)} || {Pid, Reqs} <- Handlers2].
handle_request(#request{settings =
#http_options{version = "HTTP/0.9"}} = Request,
@@ -758,19 +823,21 @@ handle_request(Request, State = #state{options = Options}) ->
{reply, {ok, NewRequest#request.id}, State}.
-start_handler(Request, State) ->
+start_handler(#request{id = Id,
+ from = From} = Request,
+ #state{profile_name = ProfileName,
+ handler_db = HandlerDb,
+ options = Options}) ->
{ok, Pid} =
case is_inets_manager() of
true ->
httpc_handler_sup:start_child([whereis(httpc_handler_sup),
- Request, State#state.options,
- State#state.profile_name]);
+ Request, Options, ProfileName]);
false ->
- httpc_handler:start_link(self(), Request, State#state.options,
- State#state.profile_name)
+ httpc_handler:start_link(self(), Request, Options, ProfileName)
end,
- ets:insert(State#state.handler_db, {Request#request.id,
- Pid, Request#request.from}),
+ HandlerInfo = {Id, Pid, From},
+ ets:insert(HandlerDb, HandlerInfo),
erlang:monitor(process, Pid).
@@ -827,12 +894,14 @@ select_session(Candidates, Max) ->
{ok, HandlerPid}
end.
-pipeline_or_keep_alive(Request, HandlerPid, State) ->
+pipeline_or_keep_alive(#request{id = Id,
+ from = From} = Request,
+ HandlerPid,
+ #state{handler_db = HandlerDb} = State) ->
case (catch httpc_handler:send(Request, HandlerPid)) of
ok ->
- ets:insert(State#state.handler_db, {Request#request.id,
- HandlerPid,
- Request#request.from});
+ HandlerInfo = {Id, HandlerPid, From},
+ ets:insert(HandlerDb, HandlerInfo);
_ -> % timeout pipelining failed
start_handler(Request, State)
end.
@@ -920,6 +989,8 @@ cast(ProfileName, Msg) ->
get_option(proxy, #options{proxy = Proxy}) ->
Proxy;
+get_option(https_proxy, #options{https_proxy = Proxy}) ->
+ Proxy;
get_option(pipeline_timeout, #options{pipeline_timeout = Timeout}) ->
Timeout;
get_option(max_pipeline_length, #options{max_pipeline_length = Length}) ->
@@ -946,6 +1017,9 @@ get_option(socket_opts, #options{socket_opts = SocketOpts}) ->
get_proxy(Opts, #options{proxy = Default}) ->
proplists:get_value(proxy, Opts, Default).
+get_https_proxy(Opts, #options{https_proxy = Default}) ->
+ proplists:get_value(https_proxy, Opts, Default).
+
get_pipeline_timeout(Opts, #options{pipeline_timeout = Default}) ->
proplists:get_value(pipeline_timeout, Opts, Default).
diff --git a/lib/inets/src/http_lib/Makefile b/lib/inets/src/http_lib/Makefile
index 3ec6ce7436..51167b34fa 100644
--- a/lib/inets/src/http_lib/Makefile
+++ b/lib/inets/src/http_lib/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2005-2010. All Rights Reserved.
+# Copyright Ericsson AB 2005-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/inets/src/http_server/httpd_conf.erl b/lib/inets/src/http_server/httpd_conf.erl
index b575d7331b..a97bbd9b25 100644
--- a/lib/inets/src/http_server/httpd_conf.erl
+++ b/lib/inets/src/http_server/httpd_conf.erl
@@ -483,7 +483,7 @@ validate_properties(Properties) ->
case mandatory_properties(Properties) of
ok ->
%% Second, check that property dependency are ok
- {ok, validate_properties2(Properties)};
+ {ok, check_minimum_bytes_per_second(validate_properties2(Properties))};
Error ->
throw(Error)
end.
@@ -522,7 +522,18 @@ validate_properties2(Properties) ->
throw(Error)
end
end.
-
+check_minimum_bytes_per_second(Properties) ->
+ case proplists:get_value(minimum_bytes_per_second, Properties, false) of
+ false ->
+ Properties;
+ Nr ->
+ case is_integer(Nr) of
+ false ->
+ throw({error, {minimum_bytes_per_second, is_not_integer}});
+ _ ->
+ Properties
+ end
+ end.
mandatory_properties(ConfigList) ->
a_must(ConfigList, [server_name, port, server_root, document_root]).
@@ -829,9 +840,7 @@ os_info(Info) ->
{OsFamily, _OsName} when Info =:= partial ->
lists:flatten(io_lib:format("(~w)", [OsFamily]));
{OsFamily, OsName} ->
- lists:flatten(io_lib:format("(~w/~w)", [OsFamily, OsName]));
- OsFamily ->
- lists:flatten(io_lib:format("(~w)", [OsFamily]))
+ lists:flatten(io_lib:format("(~w/~w)", [OsFamily, OsName]))
end.
otp_release() ->
diff --git a/lib/inets/src/http_server/httpd_log.erl b/lib/inets/src/http_server/httpd_log.erl
index 60ab326a20..a34435e0e8 100644
--- a/lib/inets/src/http_server/httpd_log.erl
+++ b/lib/inets/src/http_server/httpd_log.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -36,8 +36,8 @@
AuthUser :: string(),
Date :: string(),
StatusCode :: pos_integer(),
- Size :: pos_integer() | string()) ->
- {Log :: atom() | pid(), Entry :: string()}.
+ Size :: 0 | pos_integer() | string()) ->
+ {Log :: atom() | pid(), Entry :: string()} | term() .
access_entry(Log, NoLog, Info, RFC931, AuthUser, Date, StatusCode, SizeStr)
when is_list(SizeStr) ->
@@ -69,7 +69,7 @@ access_entry(Log, NoLog,
Info :: #mod{},
Date :: string(),
Reason :: term()) ->
- {Log :: atom() | pid(), Entry :: string()}.
+ {Log :: atom() | pid(), Entry :: string()} | term().
error_entry(Log, NoLog,
#mod{config_db = ConfigDB,
@@ -87,7 +87,7 @@ error_entry(Log, NoLog,
ConfigDB :: term(),
Date :: string(),
ErrroStr :: string()) ->
- {Log :: atom() | pid(), Entry :: string()}.
+ {Log :: atom() | pid(), Entry :: string()} | term().
error_report_entry(Log, NoLog, ConfigDb, Date, ErrorStr) ->
MakeEntry = fun() -> io_lib:format("[~s], ~s~n", [Date, ErrorStr]) end,
@@ -99,7 +99,7 @@ error_report_entry(Log, NoLog, ConfigDb, Date, ErrorStr) ->
ConfigDB :: term(),
Date :: string(),
Reason :: term()) ->
- {Log :: atom() | pid(), Entry :: string()}.
+ {Log :: atom() | pid(), Entry :: string()} | term().
security_entry(Log, NoLog, #mod{config_db = ConfigDB}, Date, Reason) ->
MakeEntry = fun() -> io_lib:format("[~s] ~s~n", [Date, Reason]) end,
diff --git a/lib/inets/src/http_server/httpd_request_handler.erl b/lib/inets/src/http_server/httpd_request_handler.erl
index b62c10bbc7..0f47d785ef 100644
--- a/lib/inets/src/http_server/httpd_request_handler.erl
+++ b/lib/inets/src/http_server/httpd_request_handler.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -44,7 +44,9 @@
timeout, %% infinity | integer() > 0
timer, %% ref() - Request timer
headers, %% #http_request_h{}
- body %% binary()
+ body, %% binary()
+ data, %% The total data received in bits, checked after 10s
+ byte_limit %% Bit limit per second before kick out
}).
%%====================================================================
@@ -98,7 +100,6 @@ init([Manager, ConfigDB, AcceptTimeout]) ->
[{socket_type, SocketType}, {socket, Socket}]),
TimeOut = httpd_util:lookup(ConfigDB, keep_alive_timeout, 150000),
-
Then = erlang:now(),
?hdrd("negotiate", []),
@@ -139,12 +140,11 @@ continue_init(Manager, ConfigDB, SocketType, Socket, TimeOut) ->
mfa = MFA},
?hdrt("activate request timeout", []),
- NewState = activate_request_timeout(State),
?hdrt("set socket options (binary, packet & active)", []),
http_transport:setopts(SocketType, Socket,
[binary, {packet, 0}, {active, once}]),
-
+ NewState = data_receive_counter(activate_request_timeout(State), httpd_util:lookup(ConfigDB, minimum_bytes_per_second, false)),
?hdrt("init done", []),
gen_server:enter_loop(?MODULE, [], NewState).
@@ -205,16 +205,25 @@ handle_info({Proto, Socket, Data},
?hdrd("received data",
[{data, Data}, {proto, Proto},
{socket, Socket}, {socket_type, SockType}, {mfa, MFA}]),
-
+
%% case (catch Module:Function([Data | Args])) of
PROCESSED = (catch Module:Function([Data | Args])),
-
+ NewDataSize = case State#state.byte_limit of
+ undefined ->
+ undefined;
+ _ ->
+ State#state.data + byte_size(Data)
+ end,
?hdrt("data processed", [{processing_result, PROCESSED}]),
-
case PROCESSED of
{ok, Result} ->
?hdrd("data processed", [{result, Result}]),
- NewState = cancel_request_timeout(State),
+ NewState = case NewDataSize of
+ undefined ->
+ cancel_request_timeout(State);
+ _ ->
+ set_new_data_size(cancel_request_timeout(State), NewDataSize)
+ end,
handle_http_msg(Result, NewState);
{error, {uri_too_long, MaxSize}, Version} ->
@@ -239,7 +248,12 @@ handle_info({Proto, Socket, Data},
NewMFA ->
?hdrd("data processed - reactivate socket", [{new_mfa, NewMFA}]),
http_transport:setopts(SockType, Socket, [{active, once}]),
- {noreply, State#state{mfa = NewMFA}}
+ case NewDataSize of
+ undefined ->
+ {noreply, State#state{mfa = NewMFA}};
+ _ ->
+ {noreply, State#state{mfa = NewMFA, data = NewDataSize}}
+ end
end;
%% Error cases
@@ -263,7 +277,22 @@ handle_info(timeout, #state{mod = ModData} = State) ->
error_log("The client did not send the whole request before the "
"server side timeout", ModData),
{stop, normal, State#state{response_sent = true}};
-
+handle_info(check_data_first, #state{data = Data, byte_limit = Byte_Limit} = State) ->
+ case Data >= (Byte_Limit*3) of
+ true ->
+ erlang:send_after(1000, self(), check_data),
+ {noreply, State#state{data = 0}};
+ _ ->
+ {stop, normal, State#state{response_sent = true}}
+ end;
+handle_info(check_data, #state{data = Data, byte_limit = Byte_Limit} = State) ->
+ case Data >= Byte_Limit of
+ true ->
+ erlang:send_after(1000, self(), check_data),
+ {noreply, State#state{data = 0}};
+ _ ->
+ {stop, normal, State#state{response_sent = true}}
+ end;
%% Default case
handle_info(Info, #state{mod = ModData} = State) ->
Error = lists:flatten(
@@ -311,6 +340,8 @@ code_change(_OldVsn, State, _Extra) ->
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
+set_new_data_size(State, NewData) ->
+ State#state{data = NewData}.
await_socket_ownership_transfer(AcceptTimeout) ->
receive
{socket_ownership_transfered, SocketType, Socket} ->
@@ -603,7 +634,14 @@ activate_request_timeout(#state{timeout = Time} = State) ->
?hdrt("activate request timeout", [{time, Time}]),
Ref = erlang:send_after(Time, self(), timeout),
State#state{timer = Ref}.
-
+data_receive_counter(State, Byte_limit) ->
+ case Byte_limit of
+ false ->
+ State#state{data = 0};
+ Nr ->
+ erlang:send_after(3000, self(), check_data_first),
+ State#state{data = 0, byte_limit = Nr}
+ end.
cancel_request_timeout(#state{timer = undefined} = State) ->
State;
cancel_request_timeout(#state{timer = Timer} = State) ->
diff --git a/lib/inets/src/inets_app/Makefile b/lib/inets/src/inets_app/Makefile
index b8d923edd8..22426eee79 100644
--- a/lib/inets/src/inets_app/Makefile
+++ b/lib/inets/src/inets_app/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2005-2011. All Rights Reserved.
+# Copyright Ericsson AB 2005-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -99,10 +99,10 @@ docs:
# ----------------------------------------------------
$(APP_TARGET): $(APP_SRC) ../../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
$(APPUP_TARGET): $(APPUP_SRC) ../../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
# ----------------------------------------------------
diff --git a/lib/inets/src/inets_app/inets.appup.src b/lib/inets/src/inets_app/inets.appup.src
index c7029f7b31..ffd0ed622f 100644
--- a/lib/inets/src/inets_app/inets.appup.src
+++ b/lib/inets/src/inets_app/inets.appup.src
@@ -18,14 +18,32 @@
{"%VSN%",
[
+ {"5.9.1",
+ [
+ {load_module, httpd_request_handler, soft_purge, soft_purge, []}
+ ]
+ },
+ {"5.9",
+ [
+ {load_module, httpd_request_handler, soft_purge, soft_purge, []},
+ {load_module, tftp, soft_purge, soft_purge, [inets_service]},
+ {load_module, inets_service, soft_purge, soft_purge, []},
+ {load_module, httpc, soft_purge, soft_purge, [httpc_manager]},
+ {update, httpc_handler, soft, soft_purge, soft_purge, [httpc_manager]},
+ {update, httpc_manager, soft, soft_purge, soft_purge, []}
+ ]
+ },
{"5.8.1",
[
+ {load_module, httpd_request_handler, soft_purge, soft_purge, []},
+ {load_module, tftp, soft_purge, soft_purge, [inets_service]},
+ {load_module, inets_service, soft_purge, soft_purge, []},
+
{load_module, http_uri, soft_purge, soft_purge, []},
{load_module, httpc_response, soft_purge, soft_purge, [http_uri]},
{load_module, httpc, soft_purge, soft_purge,
[http_uri, httpc_manager]},
- {update, httpc_manager, soft, soft_purge, soft_purge, [http_uri]},
{load_module, inets_app, soft_purge, soft_purge, [inets_sup]},
{update, inets_sup, soft, soft_purge, soft_purge, []},
@@ -35,36 +53,15 @@
{load_module, httpd_script_env, soft_purge, soft_purge, []},
{load_module, inets, soft_purge, soft_purge, [inets_trace]},
- {update, httpc_handler, soft, soft_purge, soft_purge, []},
+ {update, httpc_manager, soft, soft_purge, soft_purge, [http_uri]},
+ {update, httpc_handler, soft, soft_purge, soft_purge, [httpc_manager]},
{update, httpd_sup, soft, soft_purge, soft_purge, []},
{add_module, inets_trace}
]
},
- {"5.8",
+ {"5.8",
[
- {load_module, http_uri, soft_purge, soft_purge, []},
- {load_module, httpc_response, soft_purge, soft_purge, [http_uri]},
-
- {load_module, httpc, soft_purge, soft_purge,
- [http_uri, httpc_manager]},
-
- {load_module, inets_app, soft_purge, soft_purge, [inets_sup]},
- {update, inets_sup, soft, soft_purge, soft_purge, []},
-
- {load_module, inets, soft_purge, soft_purge, [inets_trace]},
-
- {load_module, httpd_conf, soft_purge, soft_purge, []},
- {load_module, httpd_response, soft_purge, soft_purge, []},
- {load_module, httpd_script_env, soft_purge, soft_purge, []},
-
- {load_module, ftp, soft_purge, soft_purge, []},
- {update, httpc_handler, {advanced, upgrade_from_pre_5_8_1},
- soft_purge, soft_purge, []},
- {update, httpc_manager, {advanced, upgrade_from_pre_5_8_1},
- soft_purge, soft_purge, [http_uri, httpc_handler]},
- {update, httpd_sup, soft, soft_purge, soft_purge, []},
-
- {add_module, inets_trace}
+ {restart_application, inets}
]
},
{"5.7.2",
@@ -74,14 +71,32 @@
}
],
[
+ {"5.9.1",
+ [
+ {load_module, httpd_request_handler, soft_purge, soft_purge, []}
+ ]
+ },
+ {"5.9",
+ [
+ {load_module, httpd_request_handler, soft_purge, soft_purge, []},
+ {load_module, tftp, soft_purge, soft_purge, [inets_service]},
+ {load_module, inets_service, soft_purge, soft_purge, []},
+ {load_module, httpc, soft_purge, soft_purge, [httpc_manager]},
+ {update, httpc_handler, soft, soft_purge, soft_purge, [httpc_manager]},
+ {update, httpc_manager, soft, soft_purge, soft_purge, []}
+ ]
+ },
{"5.8.1",
[
+ {load_module, httpd_request_handler, soft_purge, soft_purge, []},
+ {load_module, tftp, soft_purge, soft_purge, [inets_service]},
+ {load_module, inets_service, soft_purge, soft_purge, []},
+
{load_module, http_uri, soft_purge, soft_purge, []},
{load_module, httpc_response, soft_purge, soft_purge, [http_uri]},
{load_module, httpc, soft_purge, soft_purge,
[http_uri, httpc_manager]},
- {update, httpc_manager, soft, soft_purge, soft_purge, [http_uri]},
{load_module, inets_app, soft_purge, soft_purge, [inets_sup]},
{update, inets_sup, soft, soft_purge, soft_purge, []},
@@ -91,36 +106,15 @@
{load_module, httpd_script_env, soft_purge, soft_purge, []},
{load_module, inets, soft_purge, soft_purge, []},
- {update, httpc_handler, soft, soft_purge, soft_purge, []},
+ {update, httpc_manager, soft, soft_purge, soft_purge, [http_uri]},
+ {update, httpc_handler, soft, soft_purge, soft_purge, [httpc_manager]},
{update, httpd_sup, soft, soft_purge, soft_purge, []},
{remove, {inets_trace, soft_purge, brutal_purge}}
]
},
{"5.8",
[
- {load_module, http_uri, soft_purge, soft_purge, []},
- {load_module, httpc_response, soft_purge, soft_purge, [http_uri]},
-
- {load_module, httpc, soft_purge, soft_purge,
- [http_uri, httpc_manager]},
-
- {load_module, inets_app, soft_purge, soft_purge, [inets_sup]},
- {update, inets_sup, soft, soft_purge, soft_purge, []},
-
- {load_module, inets, soft_purge, soft_purge, []},
-
- {load_module, httpd_conf, soft_purge, soft_purge, []},
- {load_module, httpd_response, soft_purge, soft_purge, []},
- {load_module, httpd_script_env, soft_purge, soft_purge, []},
-
- {load_module, ftp, soft_purge, soft_purge, []},
- {update, httpc_handler, {advanced, upgrade_from_pre_5_8_1},
- soft_purge, soft_purge, []},
- {update, httpc_manager, {advanced, upgrade_from_pre_5_8_1},
- soft_purge, soft_purge, [http_uri, httpc_handler]},
- {update, httpd_sup, soft, soft_purge, soft_purge, []},
-
- {remove, {inets_trace, soft_purge, brutal_purge}}
+ {restart_application, inets}
]
},
{"5.7.2",
diff --git a/lib/inets/src/inets_app/inets.erl b/lib/inets/src/inets_app/inets.erl
index f33e0abe27..ed8082534f 100644
--- a/lib/inets/src/inets_app/inets.erl
+++ b/lib/inets/src/inets_app/inets.erl
@@ -274,13 +274,8 @@ sys_info() ->
os_info() ->
V = os:version(),
- case os:type() of
- {OsFam, OsName} ->
- [{fam, OsFam}, {name, OsName}, {ver, V}];
- OsFam ->
- [{fam, OsFam}, {ver, V}]
- end.
-
+ {OsFam, OsName} = os:type(),
+ [{fam, OsFam}, {name, OsName}, {ver, V}].
print_mods_info(Versions) ->
case key1search(mod_info, Versions) of
diff --git a/lib/inets/src/inets_app/inets.mk b/lib/inets/src/inets_app/inets.mk
index d24cc0aea3..adef32dc19 100644
--- a/lib/inets/src/inets_app/inets.mk
+++ b/lib/inets/src/inets_app/inets.mk
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2010-2011. All Rights Reserved.
+# Copyright Ericsson AB 2010-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -33,10 +33,6 @@ ifeq ($(WARN_UNUSED_WARS), true)
ERL_COMPILE_FLAGS += +warn_unused_vars
endif
-ifeq ($(shell erl -noshell -eval 'io:format("~4s", [erlang:system_info(otp_release)])' -s init stop), R14B)
-INETS_ERL_COMPILE_FLAGS += -D'OTP-R14B-COMPILER'
-endif
-
INETS_APP_VSN_COMPILE_FLAGS = \
+'{parse_transform,sys_pre_attributes}' \
+'{attribute,insert,app_vsn,$(APP_VSN)}'
diff --git a/lib/inets/src/inets_app/inets_service.erl b/lib/inets/src/inets_app/inets_service.erl
index a057a51e2c..d17fdfe13e 100644
--- a/lib/inets/src/inets_app/inets_service.erl
+++ b/lib/inets/src/inets_app/inets_service.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -20,20 +20,6 @@
-module(inets_service).
--ifdef('OTP-R14B-COMPILER').
-
--export([behaviour_info/1]).
-
-behaviour_info(callbacks) ->
- [{start_standalone, 1},
- {start_service, 1},
- {stop_service, 1},
- {services, 0},
- {service_info, 1}];
-behaviour_info(_) ->
- undefined.
-
--else.
%% Starts service stand-alone
%% start_standalone(Config) -> % {ok, Pid} | {error, Reason}
@@ -83,4 +69,3 @@ behaviour_info(_) ->
-callback service_info(Service :: term()) ->
{ok, [{Property :: term(), Value :: term()}]} | {error, Reason :: term()}.
--endif.
diff --git a/lib/inets/src/tftp/Makefile b/lib/inets/src/tftp/Makefile
index a9df8d0319..b368b12462 100644
--- a/lib/inets/src/tftp/Makefile
+++ b/lib/inets/src/tftp/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2005-2010. All Rights Reserved.
+# Copyright Ericsson AB 2005-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/inets/src/tftp/tftp.erl b/lib/inets/src/tftp/tftp.erl
index 0d7ae1a89e..1621add246 100644
--- a/lib/inets/src/tftp/tftp.erl
+++ b/lib/inets/src/tftp/tftp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -224,20 +224,6 @@
service_info/1
]).
--ifdef('OTP-R14B-COMPILER').
-
--export([behaviour_info/1]).
-
-behaviour_info(callbacks) ->
- [{prepare, 6},
- {open, 6},
- {read, 1},
- {write, 2},
- {abort, 3}];
-behaviour_info(_) ->
- undefined.
-
--else.
-type peer() :: {PeerType :: inet | inet6,
PeerHost :: inet:ip_address(),
@@ -280,8 +266,6 @@ behaviour_info(_) ->
-callback abort(Code :: error_code(), string(), State :: term()) -> 'ok'.
--endif.
-
-include("tftp.hrl").
diff --git a/lib/inets/test/Makefile b/lib/inets/test/Makefile
index 7befa82272..0ca99e8692 100644
--- a/lib/inets/test/Makefile
+++ b/lib/inets/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -149,6 +149,7 @@ INETS_ROOT = ../../inets
MODULES = \
inets_test_lib \
+ erl_make_certs \
ftp_SUITE \
ftp_format_SUITE \
ftp_solaris8_sparc_test \
@@ -169,6 +170,7 @@ MODULES = \
http_format_SUITE \
httpc_SUITE \
httpc_cookie_SUITE \
+ httpc_proxy_SUITE \
httpd_SUITE \
httpd_basic_SUITE \
httpd_mod \
@@ -213,7 +215,7 @@ INETS_FILES = inets.config $(INETS_SPECS)
INETS_DATADIRS = inets_SUITE_data inets_sup_SUITE_data
HTTPD_DATADIRS = httpd_test_data httpd_SUITE_data
-HTTPC_DATADIRS = httpc_SUITE_data
+HTTPC_DATADIRS = httpc_SUITE_data httpc_proxy_SUITE_data
FTP_DATADIRS = ftp_SUITE_data
DATADIRS = $(INETS_DATADIRS) $(HTTPD_DATADIRS) $(HTTPC_DATADIRS) $(FTP_DATADIRS)
diff --git a/lib/inets/test/erl_make_certs.erl b/lib/inets/test/erl_make_certs.erl
new file mode 100644
index 0000000000..d6bdd05d01
--- /dev/null
+++ b/lib/inets/test/erl_make_certs.erl
@@ -0,0 +1,429 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, 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', Der, not_encrypted};
+encode_key(Key = #'DSAPrivateKey'{}) ->
+ {ok, Der} = 'OTP-PUB-KEY':encode('DSAPrivateKey', Key),
+ {'DSAPrivateKey', 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, _}|_]} = 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 -> 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).
+
+user() ->
+ case os:getenv("USER") of
+ false ->
+ "test_user";
+ User ->
+ User
+ end.
+
+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/inets/test/ftp_suite_lib.erl b/lib/inets/test/ftp_suite_lib.erl
index ffb58c91b6..211c9b5bee 100644
--- a/lib/inets/test/ftp_suite_lib.erl
+++ b/lib/inets/test/ftp_suite_lib.erl
@@ -206,7 +206,6 @@ init_per_testcase(Case, Config)
init_per_testcase(Case, Config) ->
put(ftp_testcase, Case),
- inets:enable_trace(max, io, ftpc),
do_init_per_testcase(Case, Config).
do_init_per_testcase(Case, Config)
diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl
index a116edef77..644b01120c 100644
--- a/lib/inets/test/httpc_SUITE.erl
+++ b/lib/inets/test/httpc_SUITE.erl
@@ -34,9 +34,6 @@
-compile(export_all).
%% Test server specific exports
--define(PROXY_URL, "http://www.erlang.org").
--define(PROXY, "www-proxy.ericsson.se").
--define(PROXY_PORT, 8080).
-define(IP_PORT, 8998).
-define(SSL_PORT, 8999).
-define(NOT_IN_USE_PORT, 8997).
@@ -91,7 +88,6 @@ all() ->
options,
headers_as_is,
selecting_session,
- {group, proxy},
{group, ssl},
{group, stream},
{group, ipv6},
@@ -101,18 +97,6 @@ all() ->
groups() ->
[
- {proxy, [], [proxy_options,
- proxy_head,
- proxy_get,
- proxy_trace,
- proxy_post,
- proxy_put,
- proxy_delete,
- proxy_auth,
- proxy_headers,
- proxy_emulate_lower_versions,
- proxy_page_does_not_exist,
- proxy_https_not_supported]},
{ssl, [], [ssl_head,
essl_head,
ssl_get,
@@ -120,13 +104,11 @@ groups() ->
ssl_trace,
essl_trace]},
{stream, [], [http_stream,
- http_stream_once,
- proxy_stream]},
+ http_stream_once]},
{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},
@@ -287,66 +269,6 @@ init_per_testcase(Case, Timeout, Config) ->
init_per_testcase_ssl(essl, PrivDir, SslConfFile,
[{watchdog, Dog} | TmpConfig]);
- "proxy_" ++ Rest ->
- io:format("init_per_testcase -> Rest: ~p~n", [Rest]),
- case Rest of
- "https_not_supported" ->
- tsp("init_per_testcase -> [proxy case] start inets"),
- inets:start(),
- tsp("init_per_testcase -> "
- "[proxy case] start crypto, public_key and ssl"),
- try ?ENSURE_STARTED([crypto, public_key, ssl]) of
- ok ->
- [{watchdog, Dog} | TmpConfig]
- catch
- throw:{error, {failed_starting, App, _}} ->
- SkipString =
- "Could not start " ++ atom_to_list(App),
- skip(SkipString);
- _:X ->
- SkipString =
- lists:flatten(
- io_lib:format("Failed starting apps: ~p", [X])),
- skip(SkipString)
- end;
-
- _ ->
- %% We use erlang.org for the proxy tests
- %% and after the switch to erlang-web, many
- %% of the test cases no longer work (erlang.org
- %% previously run on Apache).
- %% Until we have had time to update inets
- %% (and updated erlang.org to use that inets)
- %% and the test cases, we simply skip the
- %% problematic test cases.
- %% This is not ideal, but I am busy....
- case is_proxy_available(?PROXY, ?PROXY_PORT) of
- true ->
- BadCases =
- [
- "delete",
- "get",
- "head",
- "not_modified_otp_6821",
- "options",
- "page_does_not_exist",
- "post",
- "put",
- "stream"
- ],
- case lists:member(Rest, BadCases) of
- true ->
- [skip("TC and server not compatible") |
- TmpConfig];
- false ->
- inets:start(),
- [{watchdog, Dog} | TmpConfig]
- end;
- false ->
- [skip("proxy not responding") | TmpConfig]
- end
- end;
-
"ipv6_" ++ _Rest ->
%% Ensure needed apps (crypto, public_key and ssl) are started
try ?ENSURE_STARTED([crypto, public_key, ssl]) of
@@ -415,14 +337,6 @@ init_per_testcase(Case, Timeout, Config) ->
%% so this value will be overwritten (see "ipv6_" below).
%% </IPv6>
- %% This will fail for the ipv6_ - cases (but that is ok)
- ProxyExceptions = ["localhost", ?IPV6_LOCAL_HOST],
- tsp("init_per_testcase -> Options before proxy set: ~n~p",
- [httpc:get_options(all)]),
- ok = httpc:set_options([{proxy, {{?PROXY, ?PROXY_PORT}, ProxyExceptions}}]),
- tsp("init_per_testcase -> Options after proxy set: ~n~p",
- [httpc:get_options(all)]),
- inets:enable_trace(max, io, httpc),
%% inets:enable_trace(max, io, all),
%% snmp:set_trace([gen_tcp]),
tsp("init_per_testcase(~w) -> done when"
@@ -466,7 +380,6 @@ end_per_testcase(http_save_to_file = Case, Config) ->
end_per_testcase(Case, Config) ->
io:format(user, "~n~n*** END ~w:~w ***~n~n",
[?MODULE, Case]),
- dbg:stop(), % ?
case atom_to_list(Case) of
"ipv6_" ++ _Rest ->
tsp("end_per_testcase(~w) -> stop ssl", [Case]),
@@ -761,119 +674,161 @@ http_inets_pipe(Config) when is_list(Config) ->
test_pipeline(URL) ->
- p("test_pipeline -> entry with"
- "~n URL: ~p", [URL]),
-
- httpc:set_options([{pipeline_timeout, 50000}]),
-
- p("test_pipeline -> issue (async) request 1"),
- {ok, RequestId1} =
+ p("test_pipeline -> entry with"
+ "~n URL: ~p", [URL]),
+
+ httpc:set_options([{pipeline_timeout, 50000}]),
+
+ p("test_pipeline -> issue (async) request 1"
+ "~n when profile info: ~p", [httpc:info()]),
+ {ok, RequestIdA1} =
httpc:request(get, {URL, []}, [], [{sync, false}]),
- test_server:format("RequestId1: ~p~n", [RequestId1]),
- p("test_pipeline -> RequestId1: ~p", [RequestId1]),
-
- %% Make sure pipeline is initiated
- p("test_pipeline -> sleep some", []),
- test_server:sleep(4000),
-
- p("test_pipeline -> issue (async) request 2"),
- {ok, RequestId2} =
+ tsp("RequestIdA1: ~p", [RequestIdA1]),
+ p("test_pipeline -> RequestIdA1: ~p"
+ "~n when profile info: ~p", [RequestIdA1, httpc:info()]),
+
+ %% Make sure pipeline is initiated
+ p("test_pipeline -> sleep some", []),
+ test_server:sleep(4000),
+
+ p("test_pipeline -> issue (async) request A2, A3 and A4"
+ "~n when profile info: ~p", [httpc:info()]),
+ {ok, RequestIdA2} =
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,_}, [_ | _], [_ | _]}} =
+ {ok, RequestIdA3} =
+ httpc:request(get, {URL, []}, [], [{sync, false}]),
+ {ok, RequestIdA4} =
+ httpc:request(get, {URL, []}, [], [{sync, false}]),
+ tsp("RequestIdAs => A2: ~p, A3: ~p and A4: ~p",
+ [RequestIdA2, RequestIdA3, RequestIdA4]),
+ p("test_pipeline -> RequestIds => A2: ~p, A3: ~p and A4: ~p"
+ "~n when profile info: ~p",
+ [RequestIdA2, RequestIdA3, RequestIdA4, httpc:info()]),
+
+ p("test_pipeline -> issue (sync) request 3"),
+ {ok, {{_,200,_}, [_ | _], [_ | _]}} =
httpc:request(get, {URL, []}, [], []),
+
+ p("test_pipeline -> expect reply for (async) request A1, A2, A3 and A4"
+ "~n when profile info: ~p", [httpc:info()]),
+ pipeline_await_async_reply([{RequestIdA1, a1, 200},
+ {RequestIdA2, a2, 200},
+ {RequestIdA3, a3, 200},
+ {RequestIdA4, a4, 200}], ?MINS(1)),
- p("test_pipeline -> expect reply for (async) request 1 or 2"),
- receive
- {http, {RequestId1, {{_, 200, _}, _, _}}} ->
- p("test_pipeline -> received reply for (async) request 1 - now wait for 2"),
- receive
- {http, {RequestId2, {{_, 200, _}, _, _}}} ->
- p("test_pipeline -> received reply for (async) request 2"),
- ok;
- {http, Msg1} ->
- tsf(Msg1)
- end;
- {http, {RequestId2, {{_, 200, _}, _, _}}} ->
- io:format("test_pipeline -> received reply for (async) request 2 - now wait for 1"),
- receive
- {http, {RequestId1, {{_, 200, _}, _, _}}} ->
- io:format("test_pipeline -> received reply for (async) request 1"),
- ok;
- {http, Msg2} ->
- tsf(Msg2)
- end;
- {http, Msg3} ->
- tsf(Msg3)
- after 60000 ->
- receive Any1 ->
- tsp("received crap after timeout: ~n ~p", [Any1]),
- tsf({error, {timeout, Any1}})
- end
- end,
-
- p("test_pipeline -> sleep some"),
- test_server:sleep(4000),
-
- p("test_pipeline -> issue (async) request 4"),
- {ok, RequestId3} =
+ p("test_pipeline -> sleep some"
+ "~n when profile info: ~p", [httpc:info()]),
+ test_server:sleep(4000),
+
+ p("test_pipeline -> issue (async) request B1, B2, B3 and B4"
+ "~n when profile info: ~p", [httpc:info()]),
+ {ok, RequestIdB1} =
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} =
+ {ok, RequestIdB2} =
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 = httpc:cancel_request(RequestId3),
-
- p("test_pipeline -> expect *no* reply for cancelled (async) request 4 (for 3 secs)"),
- receive
- {http, {RequestId3, _}} ->
- tsf(http_cancel_request_failed)
- after 3000 ->
- ok
- end,
-
- p("test_pipeline -> expect reply for (async) request 4"),
- Body =
- receive
- {http, {RequestId4, {{_, 200, _}, _, BinBody4}}} = Res ->
- p("test_pipeline -> received reply for (async) request 5"),
- tsp("Receive : ~p", [Res]),
- BinBody4;
- {http, Msg4} ->
- tsf(Msg4)
- after 60000 ->
- receive Any2 ->
- tsp("received crap after timeout: ~n ~p", [Any2]),
- tsf({error, {timeout, Any2}})
- end
- end,
+ {ok, RequestIdB3} =
+ httpc:request(get, {URL, []}, [], [{sync, false}]),
+ {ok, RequestIdB4} =
+ httpc:request(get, {URL, []}, [], [{sync, false}]),
+ tsp("RequestIdBs => B1: ~p, B2: ~p, B3: ~p and B4: ~p",
+ [RequestIdB1, RequestIdB2, RequestIdB3, RequestIdB4]),
+ p("test_pipeline -> RequestIdBs => B1: ~p, B2: ~p, B3: ~p and B4: ~p"
+ "~n when profile info: ~p",
+ [RequestIdB1, RequestIdB2, RequestIdB3, RequestIdB4, httpc:info()]),
+
+ p("test_pipeline -> cancel (async) request B2"
+ "~n when profile info: ~p", [httpc:info()]),
+ ok = httpc:cancel_request(RequestIdB2),
+
+ p("test_pipeline -> "
+ "expect *no* reply for cancelled (async) request B2 (for 3 secs)"
+ "~n when profile info: ~p", [httpc:info()]),
+ receive
+ {http, {RequestIdB2, _}} ->
+ tsf(http_cancel_request_failed)
+ after 3000 ->
+ ok
+ end,
+
+ p("test_pipeline -> expect reply for (async) request B1, B3 and B4"
+ "~n when profile info: ~p", [httpc:info()]),
+ Bodies = pipeline_await_async_reply([{RequestIdB1, b1, 200},
+ {RequestIdB3, b3, 200},
+ {RequestIdB4, b4, 200}], ?MINS(1)),
+ [{b1, Body}|_] = Bodies,
- p("test_pipeline -> check reply for (async) request 5"),
+ p("test_pipeline -> check reply for (async) request B1"
+ "~n when profile info: ~p", [httpc:info()]),
inets_test_lib:check_body(binary_to_list(Body)),
-
- p("test_pipeline -> ensure no unexpected incomming"),
+
+ p("test_pipeline -> ensure no unexpected incomming"
+ "~n when profile info: ~p", [httpc:info()]),
receive
{http, Any} ->
tsf({unexpected_message, Any})
after 500 ->
ok
end,
-
- p("test_pipeline -> done"),
+
+ p("test_pipeline -> done"
+ "~n when profile info: ~p", [httpc:info()]),
ok.
+pipeline_await_async_reply(ReqIds, Timeout) ->
+ pipeline_await_async_reply(ReqIds, Timeout, []).
+
+pipeline_await_async_reply([], _, Acc) ->
+ lists:keysort(1, Acc);
+pipeline_await_async_reply(ReqIds, Timeout, Acc) when Timeout > 0 ->
+ T1 = inets_test_lib:timestamp(),
+ p("pipeline_await_async_reply -> await replies"
+ "~n ReqIds: ~p"
+ "~n Timeout: ~p", [ReqIds, Timeout]),
+ receive
+ {http, {RequestId, {{_, Status, _}, _, Body}}} ->
+ p("pipeline_await_async_reply -> received reply for"
+ "~n RequestId: ~p"
+ "~n Status: ~p", [RequestId, Status]),
+ case lists:keysearch(RequestId, 1, ReqIds) of
+ {value, {RequestId, N, Status}} ->
+ p("pipeline_await_async_reply -> "
+ "found expected request ~w", [N]),
+ ReqIds2 = lists:keydelete(RequestId, 1, ReqIds),
+ NewTimeout = Timeout - (inets_test_lib:timestamp()-T1),
+ pipeline_await_async_reply(ReqIds2, NewTimeout,
+ [{N, Body} | Acc]);
+ {value, {RequestId, N, WrongStatus}} ->
+ p("pipeline_await_async_reply -> "
+ "found request ~w with wrong status", [N]),
+ tsf({reply_with_unexpected_status,
+ {RequestId, N, WrongStatus}});
+ false ->
+ tsf({unexpected_reply, {RequestId, Status}})
+ end;
+ {http, Msg} ->
+ tsf({unexpected_reply, Msg})
+ after Timeout ->
+ receive
+ Any ->
+ tsp("pipeline_await_async_reply -> "
+ "received unknown data after timeout: "
+ "~n ~p", [Any]),
+ tsf({timeout, {unknown, Any}})
+ end
+ end;
+pipeline_await_async_reply(ReqIds, _, Acc) ->
+ tsp("pipeline_await_async_reply -> "
+ "timeout: "
+ "~n ~p"
+ "~nwhen"
+ "~n ~p", [ReqIds, Acc]),
+ tsf({timeout, ReqIds, Acc}).
+
+
+
%%-------------------------------------------------------------------------
http_trace(doc) ->
- ["Perform a TRACE request that goes through a proxy."];
+ ["Perform a TRACE request."];
http_trace(suite) ->
[];
http_trace(Config) when is_list(Config) ->
@@ -1512,260 +1467,6 @@ http_cookie(Config) when is_list(Config) ->
ok.
%%-------------------------------------------------------------------------
-proxy_options(doc) ->
- ["Perform a OPTIONS request that goes through a proxy."];
-proxy_options(suite) ->
- [];
-proxy_options(Config) when is_list(Config) ->
- %% As of 2011-03-24, erlang.org (which is used as server)
- %% does no longer run Apache, but instead runs inets, which
- %% do not implement "options".
- case ?config(skip, Config) of
- undefined ->
- case httpc:request(options, {?PROXY_URL, []}, [], []) of
- {ok, {{_,200,_}, Headers, _}} ->
- case lists:keysearch("allow", 1, Headers) of
- {value, {"allow", _}} ->
- ok;
- _ ->
- tsf(http_options_request_failed)
- end;
- Unexpected ->
- tsf({unexpected_result, Unexpected})
- end;
- Reason ->
- skip(Reason)
- end.
-
-
-%%-------------------------------------------------------------------------
-proxy_head(doc) ->
- ["Perform a HEAD request that goes through a proxy."];
-proxy_head(suite) ->
- [];
-proxy_head(Config) when is_list(Config) ->
- %% As of 2011-03-24, erlang.org (which is used as server)
- %% does no longer run Apache, but instead runs inets.
- case ?config(skip, Config) of
- undefined ->
- case httpc:request(head, {?PROXY_URL, []}, [], []) of
- {ok, {{_,200, _}, [_ | _], []}} ->
- ok;
- Unexpected ->
- tsf({unexpected_result, Unexpected})
- end;
- Reason ->
- skip(Reason)
- end.
-
-
-%%-------------------------------------------------------------------------
-proxy_get(doc) ->
- ["Perform a GET request that goes through a proxy."];
-proxy_get(suite) ->
- [];
-proxy_get(Config) when is_list(Config) ->
- case ?config(skip, Config) of
- undefined ->
- case httpc:request(get, {?PROXY_URL, []}, [], []) of
- {ok, {{_,200,_}, [_ | _], Body = [_ | _]}} ->
- inets_test_lib:check_body(Body);
- Unexpected ->
- tsf({unexpected_result, Unexpected})
- end;
- Reason ->
- skip(Reason)
- end.
-
-%%-------------------------------------------------------------------------
-proxy_emulate_lower_versions(doc) ->
- ["Perform requests as 0.9 and 1.0 clients."];
-proxy_emulate_lower_versions(suite) ->
- [];
-proxy_emulate_lower_versions(Config) when is_list(Config) ->
- case ?config(skip, Config) of
- undefined ->
- Result09 = pelv_get("HTTP/0.9"),
- case Result09 of
- {ok, [_| _] = Body0} ->
- inets_test_lib:check_body(Body0),
- ok;
- _ ->
- tsf({unexpected_result, "HTTP/0.9", Result09})
- end,
-
- %% We do not check the version here as many servers
- %% do not behave according to the rfc and send
- %% 1.1 in its response.
- Result10 = pelv_get("HTTP/1.0"),
- case Result10 of
- {ok,{{_, 200, _}, [_ | _], Body1 = [_ | _]}} ->
- inets_test_lib:check_body(Body1),
- ok;
- _ ->
- tsf({unexpected_result, "HTTP/1.0", Result10})
- end,
-
- Result11 = pelv_get("HTTP/1.1"),
- case Result11 of
- {ok, {{"HTTP/1.1", 200, _}, [_ | _], Body2 = [_ | _]}} ->
- inets_test_lib:check_body(Body2);
- _ ->
- tsf({unexpected_result, "HTTP/1.1", Result11})
- end;
-
- Reason ->
- skip(Reason)
- end.
-
-pelv_get(Version) ->
- httpc:request(get, {?PROXY_URL, []}, [{version, Version}], []).
-
-
-%%-------------------------------------------------------------------------
-proxy_trace(doc) ->
- ["Perform a TRACE request that goes through a proxy."];
-proxy_trace(suite) ->
- [];
-proxy_trace(Config) when is_list(Config) ->
- %%{ok, {{_,200,_}, [_ | _], "TRACE " ++ _}} =
- %% httpc:request(trace, {?PROXY_URL, []}, [], []),
- skip("HTTP TRACE is no longer allowed on the ?PROXY_URL server due "
- "to security reasons").
-
-
-%%-------------------------------------------------------------------------
-proxy_post(doc) ->
- ["Perform a POST request that goes through a proxy. Note the server"
- " will reject the request this is a test of the sending of the"
- " request."];
-proxy_post(suite) ->
- [];
-proxy_post(Config) when is_list(Config) ->
- %% As of 2011-03-24, erlang.org (which is used as server)
- %% does no longer run Apache, but instead runs inets.
- case ?config(skip, Config) of
- undefined ->
- case httpc:request(post, {?PROXY_URL, [],
- "text/plain", "foobar"}, [],[]) of
- {ok, {{_,405,_}, [_ | _], [_ | _]}} ->
- ok;
- Unexpected ->
- tsf({unexpected_result, Unexpected})
- end;
- Reason ->
- skip(Reason)
- end.
-
-
-%%-------------------------------------------------------------------------
-proxy_put(doc) ->
- ["Perform a PUT request that goes through a proxy. Note the server"
- " will reject the request this is a test of the sending of the"
- " request."];
-proxy_put(suite) ->
- [];
-proxy_put(Config) when is_list(Config) ->
- %% As of 2011-03-24, erlang.org (which is used as server)
- %% does no longer run Apache, but instead runs inets.
- case ?config(skip, Config) of
- undefined ->
- case httpc:request(put, {"http://www.erlang.org/foobar.html", [],
- "html", "<html> <body><h1> foo </h1>"
- "<p>bar</p> </body></html>"}, [], []) of
- {ok, {{_,405,_}, [_ | _], [_ | _]}} ->
- ok;
- Unexpected ->
- tsf({unexpected_result, Unexpected})
- end;
- Reason ->
- skip(Reason)
- end.
-
-
-%%-------------------------------------------------------------------------
-proxy_delete(doc) ->
- ["Perform a DELETE request that goes through a proxy. Note the server"
- " will reject the request this is a test of the sending of the"
- " request. But as the file does not exist the return code will"
- " be 404 not found."];
-proxy_delete(suite) ->
- [];
-proxy_delete(Config) when is_list(Config) ->
- %% As of 2011-03-24, erlang.org (which is used as server)
- %% does no longer run Apache, but instead runs inets.
- case ?config(skip, Config) of
- undefined ->
- URL = ?PROXY_URL ++ "/foobar.html",
- case httpc:request(delete, {URL, []}, [], []) of
- {ok, {{_,404,_}, [_ | _], [_ | _]}} ->
- ok;
- Unexpected ->
- tsf({unexpected_result, Unexpected})
- end;
- Reason ->
- skip(Reason)
- end.
-
-
-%%-------------------------------------------------------------------------
-proxy_headers(doc) ->
- ["Use as many request headers as possible"];
-proxy_headers(suite) ->
- [];
-proxy_headers(Config) when is_list(Config) ->
- case ?config(skip, Config) of
- undefined ->
- {ok, {{_,200,_}, [_ | _], [_ | _]}}
- = httpc:request(get, {?PROXY_URL,
- [
- {"Accept",
- "text/*, text/html,"
- " text/html;level=1,"
- " */*"},
- {"Accept-Charset",
- "iso-8859-5, unicode-1-1;"
- "q=0.8"},
- {"Accept-Encoding", "*"},
- {"Accept-Language",
- "sv, en-gb;q=0.8,"
- " en;q=0.7"},
- {"User-Agent", "inets"},
- {"Max-Forwards","5"},
- {"Referer",
- "http://otp.ericsson.se:8000"
- "/product/internal"}
- ]}, [], []),
- ok;
- Reason ->
- skip(Reason)
- end.
-
-
-%%-------------------------------------------------------------------------
-proxy_auth(doc) ->
- ["Test the code for sending of proxy authorization."];
-proxy_auth(suite) ->
- [];
-proxy_auth(Config) when is_list(Config) ->
- %% Our proxy seems to ignore the header, however our proxy
- %% does not requirer an auth header, but we want to know
- %% atleast the code for sending the header does not crash!
- case ?config(skip, Config) of
- undefined ->
- case httpc:request(get, {?PROXY_URL, []},
- [{proxy_auth, {"foo", "bar"}}], []) of
- {ok, {{_,200, _}, [_ | _], [_|_]}} ->
- ok;
- Unexpected ->
- tsf({unexpected_result, Unexpected})
- end;
- Reason ->
- skip(Reason)
- end.
-
-
-%%-------------------------------------------------------------------------
http_server_does_not_exist(doc) ->
["Test that we get an error message back when the server "
"does note exist."];
@@ -1793,39 +1494,6 @@ page_does_not_exist(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
-proxy_page_does_not_exist(doc) ->
- ["Test that we get a 404 when the page is not found."];
-proxy_page_does_not_exist(suite) ->
- [];
-proxy_page_does_not_exist(Config) when is_list(Config) ->
- case ?config(skip, Config) of
- undefined ->
- URL = ?PROXY_URL ++ "/doesnotexist.html",
- {ok, {{_,404,_}, [_ | _], [_ | _]}} =
- httpc:request(get, {URL, []}, [], []),
- ok;
- Reason ->
- skip(Reason)
- end.
-
-
-%%-------------------------------------------------------------------------
-
-proxy_https_not_supported(doc) ->
- [];
-proxy_https_not_supported(suite) ->
- [];
-proxy_https_not_supported(Config) when is_list(Config) ->
- Result = httpc:request(get, {"https://login.yahoo.com", []}, [], []),
- case Result of
- {error, https_through_proxy_is_not_currently_supported} ->
- ok;
- _ ->
- tsf({unexpected_reason, Result})
- end.
-
-
-%%-------------------------------------------------------------------------
http_stream(doc) ->
["Test the option stream for asynchrony requests"];
@@ -1926,36 +1594,6 @@ once(URL) ->
%%-------------------------------------------------------------------------
-proxy_stream(doc) ->
- ["Test the option stream for asynchrony requests"];
-proxy_stream(suite) ->
- [];
-proxy_stream(Config) when is_list(Config) ->
- case ?config(skip, Config) of
- undefined ->
- {ok, {{_,200,_}, [_ | _], Body}} =
- httpc:request(get, {?PROXY_URL, []}, [], []),
-
- {ok, RequestId} =
- httpc:request(get, {?PROXY_URL, []}, [],
- [{sync, false}, {stream, self}]),
-
- receive
- {http, {RequestId, stream_start, _Headers}} ->
- ok;
- {http, Msg} ->
- tsf(Msg)
- end,
-
- StreamedBody = receive_streamed_body(RequestId, <<>>),
-
- Body == binary_to_list(StreamedBody);
- Reason ->
- skip(Reason)
- end.
-
-
-%%-------------------------------------------------------------------------
parse_url(doc) ->
["Test that an url is parsed correctly"];
parse_url(suite) ->
@@ -2547,21 +2185,6 @@ transfer_encoding_otp_6807(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
-proxy_not_modified_otp_6821(doc) ->
- ["If unmodified no body should be returned"];
-proxy_not_modified_otp_6821(suite) ->
- [];
-proxy_not_modified_otp_6821(Config) when is_list(Config) ->
- case ?config(skip, Config) of
- undefined ->
- provocate_not_modified_bug(?PROXY_URL);
- Reason ->
- skip(Reason)
- end.
-
-
-%%-------------------------------------------------------------------------
-
empty_response_header_otp_6830(doc) ->
["Test the case that the HTTP server does not send any headers"];
empty_response_header_otp_6830(suite) ->
@@ -3368,15 +2991,6 @@ create_config(FileName, ComType, Port, PrivDir, ServerRoot, DocRoot,
cline(List) ->
lists:flatten([List, "\r\n"]).
-is_proxy_available(Proxy, Port) ->
- case gen_tcp:connect(Proxy, Port, []) of
- {ok, Socket} ->
- gen_tcp:close(Socket),
- true;
- _ ->
- false
- end.
-
receive_streamed_body(RequestId, Body) ->
receive
{http, {RequestId, stream, BinBodyPart}} ->
@@ -3870,42 +3484,6 @@ content_length(["content-length:" ++ Value | _]) ->
content_length([_Head | Tail]) ->
content_length(Tail).
-provocate_not_modified_bug(Url) ->
- Timeout = 15000, %% 15s should be plenty
-
- {ok, {{_, 200, _}, ReplyHeaders, _Body}} =
- httpc:request(get, {Url, []}, [{timeout, Timeout}], []),
- Etag = pick_header(ReplyHeaders, "ETag"),
- Last = pick_header(ReplyHeaders, "last-modified"),
-
- case httpc:request(get, {Url, [{"If-None-Match", Etag},
- {"If-Modified-Since", Last}]},
- [{timeout, 15000}],
- []) of
- {ok, {{_, 304, _}, _, _}} -> %% The expected reply
- page_unchanged;
- {ok, {{_, 200, _}, _, _}} ->
- %% If the page has changed since the
- %% last request we retry to
- %% trigger the bug
- provocate_not_modified_bug(Url);
- {error, timeout} ->
- %% Not what we expected. Tcpdump can be used to
- %% verify that we receive the complete http-reply
- %% but still time out.
- incorrect_result
- end.
-
-pick_header(Headers, Name) ->
- case lists:keysearch(string:to_lower(Name), 1,
- [{string:to_lower(X), Y} || {X, Y} <- Headers]) of
- false ->
- [];
- {value, {_Key, Val}} ->
- Val
- end.
-
-
%% -------------------------------------------------------------------------
simple_request_and_verify(Config,
diff --git a/lib/inets/test/httpc_cookie_SUITE.erl b/lib/inets/test/httpc_cookie_SUITE.erl
index 93dbc270c5..3862bf7a20 100644
--- a/lib/inets/test/httpc_cookie_SUITE.erl
+++ b/lib/inets/test/httpc_cookie_SUITE.erl
@@ -276,8 +276,6 @@ secure_cookie(Config) when is_list(Config) ->
tsp("secure_cookie -> entry with"
"~n Config: ~p", [Config]),
- inets:enable_trace(max, io, httpc),
-
%% httpc:reset_cookies(),
tsp("secure_cookie -> Cookies 1: ~p", [httpc:which_cookies()]),
@@ -309,7 +307,6 @@ secure_cookie(Config) when is_list(Config) ->
tsp("secure_cookie -> Cookies 4: ~p", [httpc:which_cookies()]),
- inets:disable_trace(),
tsp("secure_cookie -> done"),
ok.
diff --git a/lib/inets/test/httpc_proxy_SUITE.erl b/lib/inets/test/httpc_proxy_SUITE.erl
new file mode 100644
index 0000000000..84db39e76b
--- /dev/null
+++ b/lib/inets/test/httpc_proxy_SUITE.erl
@@ -0,0 +1,575 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+
+%%
+%% ts:run(inets, httpc_proxy_SUITE, [batch]).
+%% ct:run("../inets_test", httpc_proxy_SUITE).
+%%
+
+-module(httpc_proxy_SUITE).
+
+-include_lib("common_test/include/ct.hrl").
+
+-include_lib("kernel/include/file.hrl").
+-include("inets_test_lib.hrl").
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-define(LOCAL_PROXY_SCRIPT, "server_proxy.sh").
+-define(p(F, A), % Debug printout
+ begin
+ io:format(
+ "~w ~w: " ++ begin F end,
+ [self(),?MODULE] ++ begin A end)
+ end).
+
+%%--------------------------------------------------------------------
+%% Common Test interface functions -----------------------------------
+%%--------------------------------------------------------------------
+
+suite() ->
+ [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [{group,local_proxy},
+ {group,local_proxy_https}].
+
+groups() ->
+ [{local_proxy,[],
+ [http_emulate_lower_versions
+ |local_proxy_cases()]},
+ {local_proxy_https,[],
+ local_proxy_cases()}].
+
+%% internal functions
+
+local_proxy_cases() ->
+ [http_head,
+ http_get,
+ http_options,
+ http_trace,
+ http_post,
+ http_put,
+ http_delete,
+ http_headers,
+ http_proxy_auth,
+ http_doesnotexist,
+ http_stream,
+ http_not_modified_otp_6821].
+
+%%--------------------------------------------------------------------
+
+init_per_suite(Config0) ->
+ case init_apps([crypto,public_key], Config0) of
+ Config when is_list(Config) ->
+ make_cert_files(dsa, "server-", Config),
+ Config;
+ Other ->
+ Other
+ end.
+
+end_per_suite(_Config) ->
+ [app_stop(App) || App <- r(suite_apps())],
+ ok.
+
+%% internal functions
+
+suite_apps() ->
+ [crypto,public_key].
+
+%%--------------------------------------------------------------------
+
+init_per_group(local_proxy, Config) ->
+ init_local_proxy([{protocol,http}|Config]);
+init_per_group(local_proxy_https, Config) ->
+ init_local_proxy([{protocol,https}|Config]).
+
+end_per_group(Group, Config)
+ when
+ Group =:= local_proxy;
+ Group =:= local_proxy_https ->
+ rcmd_local_proxy(["stop"], Config),
+ Config;
+end_per_group(_, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+
+init_per_testcase(Case, Config0) ->
+ ct:timetrap({seconds,30}),
+ Apps = apps(Case, Config0),
+ case init_apps(Apps, Config0) of
+ Config when is_list(Config) ->
+ case app_start(inets, Config) of
+ ok ->
+ Config;
+ Error ->
+ [app_stop(N) || N <- [inets|r(Apps)]],
+ ct:fail({could_not_init_inets,Error})
+ end;
+ E3 ->
+ E3
+ end.
+
+end_per_testcase(_Case, Config) ->
+ app_stop(inets),
+ Config.
+
+%% internal functions
+
+apps(_Case, Config) ->
+ case ?config(protocol, Config) of
+ https ->
+ [ssl];
+ _ ->
+ []
+ end.
+
+%%--------------------------------------------------------------------
+%% Test Cases --------------------------------------------------------
+%%--------------------------------------------------------------------
+
+http_head(doc) ->
+ ["Test http/https HEAD request."];
+http_head(Config) when is_list(Config) ->
+ Method = head,
+ URL = url("/index.html", Config),
+ Request = {URL,[]},
+ HttpOpts = [],
+ Opts = [],
+ {ok,{{_,200,_},[_|_],[]}} =
+ httpc:request(Method, Request, HttpOpts, Opts),
+ ok.
+
+%%--------------------------------------------------------------------
+
+http_get(doc) ->
+ ["Test http/https GET request."];
+http_get(Config) when is_list(Config) ->
+ Method = get,
+ URL = url("/index.html", Config),
+ Request = {URL,[]},
+ Timeout = timer:seconds(1),
+ ConnTimeout = Timeout + timer:seconds(1),
+
+ HttpOpts1 = [{timeout,Timeout},{connect_timeout,ConnTimeout}],
+ Opts1 = [],
+ {ok,{{_,200,_},[_|_],[_|_]=B1}} =
+ httpc:request(Method, Request, HttpOpts1, Opts1),
+ inets_test_lib:check_body(B1),
+
+ HttpOpts2 = [],
+ Opts2 = [{body_format,binary}],
+ {ok,{{_,200,_},[_|_],B2}} =
+ httpc:request(Method, Request, HttpOpts2, Opts2),
+ inets_test_lib:check_body(binary_to_list(B2)).
+
+%%--------------------------------------------------------------------
+
+http_options(doc) ->
+ ["Perform an OPTIONS request."];
+http_options(Config) when is_list(Config) ->
+ Method = options,
+ URL = url("/index.html", Config),
+ Request = {URL,[]},
+ HttpOpts = [],
+ Opts = [],
+ {ok,{{_,200,_},Headers,_}} =
+ httpc:request(Method, Request, HttpOpts, Opts),
+ {value,_} = lists:keysearch("allow", 1, Headers),
+ ok.
+
+%%--------------------------------------------------------------------
+
+http_trace(doc) ->
+ ["Perform a TRACE request."];
+http_trace(Config) when is_list(Config) ->
+ Method = trace,
+ URL = url("/index.html", Config),
+ Request = {URL,[]},
+ HttpOpts = [],
+ Opts = [],
+ {ok,{{_,200,_},[_|_],"TRACE "++_}} =
+ httpc:request(Method, Request, HttpOpts, Opts),
+ ok.
+
+%%--------------------------------------------------------------------
+
+http_post(doc) ->
+ ["Perform a POST request that goes through a proxy. When the "
+ "request goes to an ordinary file it seems the POST data "
+ "is ignored."];
+http_post(Config) when is_list(Config) ->
+ Method = post,
+ URL = url("/index.html", Config),
+ Request = {URL,[],"text/plain","foobar"},
+ HttpOpts = [],
+ Opts = [],
+ {ok,{{_,200,_},[_|_],[_|_]}} =
+ httpc:request(Method, Request, HttpOpts, Opts),
+ ok.
+
+%%--------------------------------------------------------------------
+
+http_put(doc) ->
+ ["Perform a PUT request. The server will not allow it "
+ "but we only test sending the request."];
+http_put(Config) when is_list(Config) ->
+ Method = put,
+ URL = url("/put.html", Config),
+ Content =
+ "<html><body> <h1>foo</h1> <p>bar</p> </body></html>",
+ Request = {URL,[],"html",Content},
+ HttpOpts = [],
+ Opts = [],
+ {ok,{{_,405,_},[_|_],[_|_]}} =
+ httpc:request(Method, Request, HttpOpts, Opts),
+ ok.
+
+%%--------------------------------------------------------------------
+
+http_delete(doc) ->
+ ["Perform a DELETE request that goes through a proxy. Note the server "
+ "will reject the request with a 405 Method Not Allowed,"
+ "but this is just a test of sending the request."];
+http_delete(Config) when is_list(Config) ->
+ Method = delete,
+ URL = url("/delete.html", Config),
+ Request = {URL,[]},
+ HttpOpts = [],
+ Opts = [],
+ {ok,{{_,405,_},[_|_],[_|_]}} =
+ httpc:request(Method, Request, HttpOpts, Opts),
+ ok.
+
+%%--------------------------------------------------------------------
+
+http_headers(doc) ->
+ ["Use as many request headers as possible"];
+http_headers(Config) when is_list(Config) ->
+ Method = get,
+ URL = url("/index.html", Config),
+ Headers =
+ [{"Accept",
+ "text/*, text/html, text/html;level=1, */*"},
+ {"Accept-Charset",
+ "iso-8859-5, unicode-1-1;q=0.8"},
+ {"Accept-Encoding", "*"},
+ {"Accept-Language",
+ "sv, en-gb;q=0.8, en;q=0.7"},
+ {"User-Agent", "inets"},
+ {"Max-Forwards","5"},
+ {"Referer",
+ "http://otp.ericsson.se:8000/product/internal"}],
+ Request = {URL,Headers},
+ HttpOpts = [],
+ Opts = [],
+ {ok,{{_,200,_},[_|_],[_|_]}} =
+ httpc:request(Method, Request, HttpOpts, Opts),
+ ok.
+
+%%--------------------------------------------------------------------
+
+http_proxy_auth(doc) ->
+ ["Test the code for sending of proxy authorization."];
+http_proxy_auth(Config) when is_list(Config) ->
+ %% Our proxy seems to ignore the header, however our proxy
+ %% does not requirer an auth header, but we want to know
+ %% atleast the code for sending the header does not crash!
+ Method = get,
+ URL = url("/index.html", Config),
+ Request = {URL,[]},
+ HttpOpts = [{proxy_auth,{"foo","bar"}}],
+ Opts = [],
+ {ok,{{_,200,_},[_|_],[_|_]}} =
+ httpc:request(Method, Request, HttpOpts, Opts),
+ ok.
+
+%%--------------------------------------------------------------------
+
+http_doesnotexist(doc) ->
+ ["Test that we get a 404 when the page is not found."];
+http_doesnotexist(Config) when is_list(Config) ->
+ Method = get,
+ URL = url("/doesnotexist.html", Config),
+ Request = {URL,[]},
+ HttpOpts = [{proxy_auth,{"foo","bar"}}],
+ Opts = [],
+ {ok,{{_,404,_},[_|_],[_|_]}} =
+ httpc:request(Method, Request, HttpOpts, Opts),
+ ok.
+
+%%--------------------------------------------------------------------
+
+http_stream(doc) ->
+ ["Test the option stream for asynchronous requests"];
+http_stream(Config) when is_list(Config) ->
+ Method = get,
+ URL = url("/index.html", Config),
+ Request = {URL,[]},
+ HttpOpts = [],
+
+ Opts1 = [{body_format,binary}],
+ {ok,{{_,200,_},[_|_],Body}} =
+ httpc:request(Method, Request, HttpOpts, Opts1),
+
+ Opts2 = [{sync,false},{stream,self}],
+ {ok,RequestId} =
+ httpc:request(Method, Request, HttpOpts, Opts2),
+ receive
+ {http,{RequestId,stream_start,[_|_]}} ->
+ ok
+ end,
+ case http_stream(RequestId, <<>>) of
+ Body -> ok
+ end.
+ %% StreamedBody = http_stream(RequestId, <<>>),
+ %% Body =:= StreamedBody,
+ %% ok.
+
+http_stream(RequestId, Body) ->
+ receive
+ {http,{RequestId,stream,Bin}} ->
+ http_stream(RequestId, <<Body/binary,Bin/binary>>);
+ {http,{RequestId,stream_end,_Headers}} ->
+ Body
+ end.
+
+%%--------------------------------------------------------------------
+
+http_emulate_lower_versions(doc) ->
+ ["Perform requests as 0.9 and 1.0 clients."];
+http_emulate_lower_versions(Config) when is_list(Config) ->
+ Method = get,
+ URL = url("/index.html", Config),
+ Request = {URL,[]},
+ Opts = [],
+
+ HttpOpts1 = [{version,"HTTP/0.9"}],
+ {ok,[_|_]=B1} =
+ httpc:request(Method, Request, HttpOpts1, Opts),
+ inets_test_lib:check_body(B1),
+
+ HttpOpts2 = [{version,"HTTP/1.0"}],
+ {ok,{{_,200,_},[_|_],[_|_]=B2}} =
+ httpc:request(Method, Request, HttpOpts2, Opts),
+ inets_test_lib:check_body(B2),
+
+ HttpOpts3 = [{version,"HTTP/1.1"}],
+ {ok,{{_,200,_},[_|_],[_|_]=B3}} =
+ httpc:request(Method, Request, HttpOpts3, Opts),
+ inets_test_lib:check_body(B3),
+
+ ok.
+
+%%--------------------------------------------------------------------
+http_not_modified_otp_6821(doc) ->
+ ["If unmodified no body should be returned"];
+http_not_modified_otp_6821(Config) when is_list(Config) ->
+ Method = get,
+ URL = url("/index.html", Config),
+ Opts = [],
+
+ Request1 = {URL,[]},
+ HttpOpts1 = [],
+ {ok,{{_,200,_},ReplyHeaders,[_|_]}} =
+ httpc:request(Method, Request1, HttpOpts1, Opts),
+ ETag = header_value("etag", ReplyHeaders),
+ LastModified = header_value("last-modified", ReplyHeaders),
+
+ Request2 =
+ {URL,
+ [{"If-None-Match",ETag},
+ {"If-Modified-Since",LastModified}]},
+ HttpOpts2 = [{timeout,15000}], % Limit wait for bug result
+ {ok,{{_,304,_},_,[]}} = % Page Unchanged
+ httpc:request(Method, Request2, HttpOpts2, Opts),
+
+ ok.
+
+header_value(Name, [{HeaderName,HeaderValue}|Headers]) ->
+ case string:to_lower(HeaderName) of
+ Name ->
+ HeaderValue;
+ _ ->
+ header_value(Name, Headers)
+ end.
+
+%%--------------------------------------------------------------------
+%% Internal Functions ------------------------------------------------
+%%--------------------------------------------------------------------
+
+init_apps([], Config) ->
+ Config;
+init_apps([App|Apps], Config) ->
+ case app_start(App, Config) of
+ ok ->
+ init_apps(Apps, Config);
+ Error ->
+ Msg =
+ lists:flatten(
+ io_lib:format(
+ "Could not start ~p due to ~p.~n",
+ [App, Error])),
+ {skip,Msg}
+ end.
+
+app_start(App, Config) ->
+ try
+ case App of
+ crypto ->
+ crypto:stop(),
+ ok = crypto:start();
+ inets ->
+ application:stop(App),
+ ok = application:start(App),
+ case ?config(proxy, Config) of
+ undefined -> ok;
+ {_,ProxySpec} ->
+ ok = httpc:set_options([{proxy,ProxySpec}])
+ end;
+ _ ->
+ application:stop(App),
+ ok = application:start(App)
+ end
+ catch
+ Class:Reason ->
+ {exception,Class,Reason}
+ end.
+
+app_stop(App) ->
+ application:stop(App).
+
+make_cert_files(Alg, Prefix, Config) ->
+ PrivDir = ?config(priv_dir, Config),
+ CaInfo = {CaCert,_} = erl_make_certs:make_cert([{key,Alg}]),
+ {Cert,CertKey} = erl_make_certs:make_cert([{key,Alg},{issuer,CaInfo}]),
+ CaCertFile = filename:join(PrivDir, Prefix++"cacerts.pem"),
+ CertFile = filename:join(PrivDir, Prefix++"cert.pem"),
+ KeyFile = filename:join(PrivDir, Prefix++"key.pem"),
+ der_to_pem(CaCertFile, [{'Certificate', CaCert, not_encrypted}]),
+ der_to_pem(CertFile, [{'Certificate', Cert, not_encrypted}]),
+ der_to_pem(KeyFile, [CertKey]),
+ ok.
+
+der_to_pem(File, Entries) ->
+ PemBin = public_key:pem_encode(Entries),
+ file:write_file(File, PemBin).
+
+
+
+url(AbsPath, Config) ->
+ Protocol = ?config(protocol, Config),
+ {ServerName,ServerPort} = ?config(Protocol, Config),
+ atom_to_list(Protocol) ++ "://" ++
+ ServerName ++ ":" ++ integer_to_list(ServerPort) ++
+ AbsPath.
+
+%%--------------------------------------------------------------------
+
+init_local_proxy(Config) ->
+ case os:type() of
+ {unix,_} ->
+ case rcmd_local_proxy(["start"], Config) of
+ {0,[":STARTED:"++String]} ->
+ init_local_proxy_string(String, Config);
+ {_,[":SKIP:"++_|_]}=Reason ->
+ {skip,Reason};
+ Error ->
+ rcmd_local_proxy(["stop"], Config),
+ ct:fail({local_proxy_start_failed,Error})
+ end;
+ _ ->
+ {skip,"Platform can not run local proxy start script"}
+ end.
+
+init_local_proxy_string(String, Config) ->
+ {Proxy,Server} = split($|, String),
+ {ProxyName,ProxyPort} = split($:, Proxy),
+ {ServerName,ServerPorts} = split($:, Server),
+ {ServerHttpPort,ServerHttpsPort} = split($:, ServerPorts),
+ [{proxy,{local,{{ProxyName,list_to_integer(ProxyPort)},[]}}},
+ {http,{ServerName,list_to_integer(ServerHttpPort)}},
+ {https,{ServerName,list_to_integer(ServerHttpsPort)}}
+ |Config].
+
+rcmd_local_proxy(Args, Config) ->
+ DataDir = ?config(data_dir, Config),
+ PrivDir = ?config(priv_dir, Config),
+ Script = filename:join(DataDir, ?LOCAL_PROXY_SCRIPT),
+ rcmd(Script, Args, [{cd,PrivDir}]).
+
+rcmd(Cmd, Args, Opts) ->
+ Port =
+ erlang:open_port(
+ {spawn_executable,Cmd},
+ [{args,Args},{line,80},exit_status,eof,hide|Opts]),
+ rcmd_loop(Port, [], [], undefined, false).
+
+rcmd_loop(Port, Lines, Buf, Exit, EOF) ->
+ receive
+ {Port,{data,{Flag,Line}}} ->
+ case Flag of
+ noeol ->
+ rcmd_loop(Port, Lines, r(Line, Buf), Exit, EOF);
+ eol ->
+ rcmd_loop(Port, [r(Buf, Line)|Lines], [], Exit, EOF)
+ end;
+ {Port,{exit_status,Status}} when Exit =:= undefined ->
+ case EOF of
+ true ->
+ rcmd_close(Port, Lines, Buf, Status);
+ false ->
+ rcmd_loop(Port, Lines, Buf, Status, EOF)
+ end;
+ {Port,eof} when EOF =:= false ->
+ case Exit of
+ undefined ->
+ rcmd_loop(Port, Lines, Buf, Exit, true);
+ Status ->
+ rcmd_close(Port, Lines, Buf, Status)
+ end;
+ {Port,_}=Unexpected ->
+ ct:fail({unexpected_from_port,Unexpected})
+ end.
+
+rcmd_close(Port, Lines, Buf, Status) ->
+ catch port_close(Port),
+ case Buf of
+ [] ->
+ {Status,Lines};
+ _ ->
+ {Status,[r(Buf)|Lines]}
+ end.
+
+%%--------------------------------------------------------------------
+
+%% Split on first match of X in Ys, do not include X in neither part
+split(X, Ys) ->
+ split(X, Ys, []).
+%%
+split(X, [X|Ys], Rs) ->
+ {r(Rs),Ys};
+split(X, [Y|Ys], Rs) ->
+ split(X, Ys, [Y|Rs]).
+
+r(L) -> lists:reverse(L).
+r(L, R) -> lists:reverse(L, R).
diff --git a/lib/inets/test/httpc_proxy_SUITE_data/apache2/apache2.conf b/lib/inets/test/httpc_proxy_SUITE_data/apache2/apache2.conf
new file mode 100644
index 0000000000..37af88c510
--- /dev/null
+++ b/lib/inets/test/httpc_proxy_SUITE_data/apache2/apache2.conf
@@ -0,0 +1,87 @@
+## Simple Apache 2 configuration file for daily test very local http server
+##
+## %CopyrightBegin%
+##
+## Copyright Ericsson AB 2012. All Rights Reserved.
+##
+## The contents of this file are subject to the Erlang Public License,
+## Version 1.1, (the "License"); you may not use this file except in
+## compliance with the License. You should have received a copy of the
+## Erlang Public License along with this software. If not, it can be
+## retrieved online at http://www.erlang.org/.
+##
+## Software distributed under the License is distributed on an "AS IS"
+## basis, 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: Raimo Niskanen, Erlang/OTP
+#
+LockFile ${APACHE_LOCK_DIR}/accept.lock
+PidFile ${APACHE_PID_FILE}
+
+Timeout 300
+
+User ${APACHE_RUN_USER}
+Group ${APACHE_RUN_GROUP}
+
+DefaultType text/plain
+HostnameLookups Off
+ErrorLog ${APACHE_LOG_DIR}/error.log
+LogLevel warn
+
+Include ${APACHE_MODS_DIR}/*.load
+Include ${APACHE_MODS_DIR}/*.conf
+
+Listen ${APACHE_HTTP_PORT} http
+
+<IfModule mod_ssl.c>
+ Listen ${APACHE_HTTPS_PORT} https
+ SSLMutex file:${APACHE_LOCK_DIR}/ssl_mutex
+</IfModule>
+
+#<IfModule mod_gnutls.c>
+# Listen 8443
+#</IfModule>
+
+#LogFormat "%v:%p %h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" vhost_combined
+LogFormat "%h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" combined
+#LogFormat "%h %l %u %t \"%r\" %>s %O" common
+#LogFormat "%{Referer}i -> %U" referer
+#LogFormat "%{User-agent}i" agent
+
+CustomLog ${APACHE_LOG_DIR}/access.log combined
+
+<Directory />
+ AllowOverride None
+ Order Deny,Allow
+ Deny from all
+</Directory>
+
+ServerTokens Minimal
+ServerSignature Off
+KeepAlive On
+KeepAliveTimeout 5
+
+ServerName ${APACHE_SERVER_NAME}
+ServerAdmin webmaster@${APACHE_SERVER_NAME}
+DocumentRoot ${APACHE_DOCROOT}
+<Directory ${APACHE_DOCROOT}>
+ Options Indexes FollowSymLinks MultiViews
+ AllowOverride None
+ Order allow,deny
+ Allow from all
+</Directory>
+
+<VirtualHost *:${APACHE_HTTP_PORT}>
+</VirtualHost>
+
+<IfModule mod_ssl.c>
+ <VirtualHost *:${APACHE_HTTPS_PORT}>
+ SSLCertificateFile ${APACHE_CERTS_DIR}/server-cert.pem
+ SSLCertificateKeyFile ${APACHE_CERTS_DIR}/server-key.pem
+ SSLEngine on
+ </VirtualHost>
+</IfModule>
diff --git a/lib/inets/test/httpc_proxy_SUITE_data/apache2/htdocs/index.html b/lib/inets/test/httpc_proxy_SUITE_data/apache2/htdocs/index.html
new file mode 100644
index 0000000000..1c70d95348
--- /dev/null
+++ b/lib/inets/test/httpc_proxy_SUITE_data/apache2/htdocs/index.html
@@ -0,0 +1,4 @@
+<html><body><h1>It works!</h1>
+<p>This is the default web page for this server.</p>
+<p>The web server software is running but no content has been added, yet.</p>
+</body></html>
diff --git a/lib/inets/test/httpc_proxy_SUITE_data/server_proxy.sh b/lib/inets/test/httpc_proxy_SUITE_data/server_proxy.sh
new file mode 100755
index 0000000000..4b05ea63ef
--- /dev/null
+++ b/lib/inets/test/httpc_proxy_SUITE_data/server_proxy.sh
@@ -0,0 +1,198 @@
+#! /bin/sh
+##
+## Command file to handle external webserver and proxy
+## apache2 and tinyproxy.
+##
+## %CopyrightBegin%
+##
+## Copyright Ericsson AB 2012. All Rights Reserved.
+##
+## The contents of this file are subject to the Erlang Public License,
+## Version 1.1, (the "License"); you may not use this file except in
+## compliance with the License. You should have received a copy of the
+## Erlang Public License along with this software. If not, it can be
+## retrieved online at http://www.erlang.org/.
+##
+## Software distributed under the License is distributed on an "AS IS"
+## basis, 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: Raimo Niskanen, Erlang/OTP
+#
+
+PATH=/usr/local/bin:/usr/local/sbin:/bin:/usr/bin:/sbin:/usr/sbin
+SHELL=/bin/sh
+unset CDPATH ENV BASH_ENV
+IFS='
+ '
+
+APACHE_MODS_AVAILABLE_DIR="/etc/apache2/mods-available"
+MODS="authz_host.load mime.conf mime.load ssl.conf ssl.load"
+
+APACHE_HTTP_PORT=8080
+APACHE_HTTPS_PORT=8443
+APACHE_SERVER_NAME=localhost
+export APACHE_HTTP_PORT APACHE_HTTPS_PORT APACHE_SERVER_NAME
+
+PROXY_SERVER_NAME=localhost
+PROXY_PORT=8000
+export PROXY_SERVER_NAME PROXY_PORT
+
+# All stdout goes to the calling erlang port, therefore
+# these helpers push all side info to stderr.
+status () { echo "$@"; }
+info () { echo "$@" 1>&2; }
+die () { REASON="$?"; status "$@"; exit "$REASON"; }
+cmd () { "$@" 1>&2; }
+silent () { "$@" 1>/dev/null 2>&1; }
+
+wait_for_pidfile () {
+ PIDFILE="${1:?Missing argument: PidFile}"
+ for t in 1 1 1 2 2 3 3 3 4; do
+ PID="`head -1 "$1" 2>/dev/null`" && [ :"$PID" != : ] && break
+ sleep $t
+ done
+ [ :"$PID" = : ] && die ":ERROR:No or empty PidFile: $1"
+ info "Started $PIDFILE[$PID]."
+}
+
+kill_and_wait () {
+ PID_FILE="${1:?Missing argument: PidFile}"
+ if [ -f "$PID_FILE" ]; then
+ PID="`head -1 "$PID_FILE" 2>/dev/null`"
+ [ :"$PID" = : ] && \
+ info "Empty Pid file: $1"
+ info "Stopping $1 [$PID]..."
+ shift
+ case :"${1:?Missing argument: kill command}" in
+ :kill)
+ [ :"$PID" = : ] || cmd kill "$PID";;
+ :*)
+ cmd "$@";;
+ esac
+ wait "$PID"
+ for t in 1 1 1 2; do
+ sleep $t
+ [ -e "$PID_FILE" ] || break
+ done
+ silent rm "$PID_FILE"
+ else
+ info "No pid file: $1"
+ fi
+}
+
+
+PRIV_DIR="`pwd`"
+DATA_DIR="`dirname "$0"`"
+DATA_DIR="`cd "$DATA_DIR" && pwd`"
+
+silent type apache2ctl || \
+ die ":SKIP: Can not find apache2ctl."
+silent type tinyproxy || \
+ die ":SKIP: Can not find tinyproxy."
+
+[ -d "$APACHE_MODS_AVAILABLE_DIR" ] || \
+ die ":SKIP:Can not locate modules dir $APACHE_MODS_AVAILABLE_DIR."
+
+silent mkdir apache2 tinyproxy
+cd apache2 || \
+ die ":ERROR:Can not cd to apache2"
+CWD="`pwd`"
+(cd ../tinyproxy) || \
+ die ":ERROR:Can not cd to ../tinyproxy"
+
+unset APACHE_HTTPD APACHE_LYNX APACHE_STATUSURL
+
+## apache2ctl envvars variables
+APACHE_CONFDIR="$DATA_DIR/apache2"
+[ -f "$APACHE_CONFDIR"/apache2.conf ] || \
+ die ":SKIP:No config file: $APACHE_CONFDIR/apache2.conf."
+APACHE_RUN_USER=`id | sed 's/^uid=[0-9]\{1,\}(\([^)]*\)).*/\1/'`
+APACHE_RUN_GROUP=`id | sed 's/.*[ ]gid=[0-9]\{1,\}(\([^)]*\)).*/\1/'`
+APACHE_RUN_DIR="$CWD/run"
+APACHE_PID_FILE="$APACHE_RUN_DIR/pid"
+APACHE_LOCK_DIR="$CWD/lock"
+APACHE_LOG_DIR="$CWD/log"
+export APACHE_CONFDIR APACHE_RUN_USER APACHE_RUN_GROUP
+export APACHE_RUN_DIR APACHE_PID_FILE
+export APACHE_LOCK_DIR APACHE_LOG_DIR
+silent cmd mkdir "$APACHE_CONFDIR"
+silent cmd mkdir "$APACHE_RUN_DIR" "$APACHE_LOCK_DIR" "$APACHE_LOG_DIR"
+
+## Our apache2.conf additional variables
+APACHE_MODS_DIR="$CWD/mods"
+APACHE_DOCROOT="$APACHE_CONFDIR/htdocs"
+APACHE_CERTS_DIR="$PRIV_DIR"
+export APACHE_MODS_DIR APACHE_DOCROOT APACHE_CERTS_DIR
+[ -d "$APACHE_MODS_DIR" ] || {
+ cmd mkdir "$APACHE_MODS_DIR"
+ for MOD in $MODS; do
+ cmd ln -s "$APACHE_MODS_AVAILABLE_DIR/$MOD" "$APACHE_MODS_DIR" || {
+ die ":ERROR:ln of apache 2 module $MOD failed"
+ }
+ done
+}
+
+case :"${1:?}" in
+
+ :start)
+ info "Starting apache2..."
+ cmd apache2ctl start
+ [ $? = 0 ] || \
+ die ":ERROR: apache2 did not start."
+ wait_for_pidfile "$APACHE_PID_FILE"
+
+ info "Starting tinyproxy..."
+ cmd cd ../tinyproxy || \
+ die ":ERROR:Can not cd to `pwd`/../tinyproxy"
+ cat >tinyproxy.conf <<EOF
+Port $PROXY_PORT
+
+Listen 127.0.0.1
+BindSame yes
+Timeout 600
+
+DefaultErrorFile "default.html"
+Logfile "tinyproxy.log"
+PidFile "tinyproxy.pid"
+
+MaxClients 100
+MinSpareServers 2
+MaxSpareServers 8
+StartServers 2
+MaxRequestsPerChild 0
+
+ViaProxyName "tinyproxy"
+
+ConnectPort $APACHE_HTTPS_PORT
+EOF
+ (tinyproxy -d -c tinyproxy.conf 1>/dev/null 2>&1 </dev/null &)&
+ wait_for_pidfile tinyproxy.pid
+
+ status ":STARTED:$PROXY_SERVER_NAME:$PROXY_PORT|\
+$APACHE_SERVER_NAME:$APACHE_HTTP_PORT:$APACHE_HTTPS_PORT"
+ exit 0
+ ;;
+
+ :stop)
+ kill_and_wait ../tinyproxy/tinyproxy.pid kill
+ kill_and_wait "$APACHE_PID_FILE" apache2ctl stop
+
+ status ":STOPPED:"
+ exit 0
+ ;;
+
+ :apache2ctl)
+ shift
+ cmd apache2ctl ${1+"$@"}
+ exit
+ ;;
+
+ :*)
+ (exit 1); die ":ERROR: I do not know of command '$1'."
+ ;;
+
+esac
diff --git a/lib/inets/test/httpd_SUITE.erl b/lib/inets/test/httpd_SUITE.erl
index 41e4188e5f..592469a12f 100644
--- a/lib/inets/test/httpd_SUITE.erl
+++ b/lib/inets/test/httpd_SUITE.erl
@@ -530,24 +530,10 @@ init_per_testcase3(Case, Config) ->
application:stop(inets),
application:stop(ssl),
cleanup_mnesia(),
-
- %% Set trace level
- case lists:reverse(atom_to_list(Case)) of
- "tset_emit" ++ _Rest -> % test-cases ending with time_test
- tsp("init_per_testcase3(~w) -> disabling trace", [Case]),
- inets:disable_trace();
- _ ->
- tsp("init_per_testcase3(~w) -> enabling trace", [Case]),
- %% TraceLevel = 70,
- TraceLevel = max,
- TraceDest = io,
- inets:enable_trace(TraceLevel, TraceDest, httpd)
- end,
-
+
%% Start initialization
tsp("init_per_testcase3(~w) -> start init", [Case]),
-
-
+
Dog = test_server:timetrap(inets_test_lib:minutes(10)),
NewConfig = lists:keydelete(watchdog, 1, Config),
TcTopDir = ?config(tc_top_dir, Config),
@@ -762,14 +748,9 @@ ip_mod_cgi(doc) ->
ip_mod_cgi(suite) ->
[];
ip_mod_cgi(Config) when is_list(Config) ->
- case test_server:os_type() of
- vxworks ->
- {skip, cgi_not_supported_on_vxwoks};
- _ ->
- httpd_mod:cgi(ip_comm, ?IP_PORT,
- ?config(host, Config), ?config(node, Config)),
- ok
- end.
+ httpd_mod:cgi(ip_comm, ?IP_PORT,
+ ?config(host, Config), ?config(node, Config)),
+ ok.
%%-------------------------------------------------------------------------
ip_mod_esi(doc) ->
["Module test: mod_esi"];
@@ -1275,16 +1256,11 @@ 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(Tag,
- ?SSL_PORT,
- ?config(host, Config),
- ?config(node, Config)),
- ok
- end.
+ httpd_mod:cgi(Tag,
+ ?SSL_PORT,
+ ?config(host, Config),
+ ?config(node, Config)),
+ ok.
%%-------------------------------------------------------------------------
@@ -2698,11 +2674,6 @@ 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, ip_comm, 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;
diff --git a/lib/inets/test/httpd_basic_SUITE.erl b/lib/inets/test/httpd_basic_SUITE.erl
index 7a476ea14a..523cf9d38c 100644
--- a/lib/inets/test/httpd_basic_SUITE.erl
+++ b/lib/inets/test/httpd_basic_SUITE.erl
@@ -34,7 +34,8 @@ all() ->
[
uri_too_long_414,
header_too_long_413,
- escaped_url_in_error_body
+ escaped_url_in_error_body,
+ slowdose
].
groups() ->
@@ -278,7 +279,18 @@ escaped_url_in_error_body(Config) when is_list(Config) ->
inets:stop(httpd, Pid),
tsp("escaped_url_in_error_body -> done"),
ok.
-
+slowdose(doc) ->
+ ["Testing minimum bytes per second option"];
+slowdose(Config) when is_list(Config) ->
+ HttpdConf = ?config(httpd_conf, Config),
+ {ok, Pid} = inets:start(httpd, [{port, 0}, {minimum_bytes_per_second, 200}|HttpdConf]),
+ Info = httpd:info(Pid),
+ Port = proplists:get_value(port, Info),
+ {ok, Socket} = gen_tcp:connect("localhost", Port, []),
+ receive
+ after 6000 ->
+ {error, closed} = gen_tcp:send(Socket, "Hey")
+ end.
find_URL_path([]) ->
"";
find_URL_path(["URL", URL | _]) ->
diff --git a/lib/inets/test/httpd_mod.erl b/lib/inets/test/httpd_mod.erl
index cb1214b7fb..387263ce58 100644
--- a/lib/inets/test/httpd_mod.erl
+++ b/lib/inets/test/httpd_mod.erl
@@ -82,19 +82,23 @@ actions(Type, Port, Host, Node) ->
[{statuscode, 200},
{version, "HTTP/1.0"}]).
+
%%-------------------------------------------------------------------------
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 -> register~n", [?MODULE]),
+ tsp("security -> "
+ "entry with"
+ "~n ServerRoot: ~p"
+ "~n Type: ~p"
+ "~n Port: ~p"
+ "~n Host: ~p"
+ "~n Node: ~p", [ServerRoot, Type, Port, Host, Node]),
+
+ tsp("security -> "
+ "register - receive security events"),
global:register_name(mod_security_test, self()), % Receive events
+ tsp("security -> "
+ "sleep"),
test_server:sleep(5000),
OpenDir = filename:join([ServerRoot, "htdocs", "open"]),
@@ -102,133 +106,240 @@ 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]),
+ tsp("security -> "
+ "blocking and unblocking of users - "
+ "remove all existing users"),
remove_users(Node, ServerRoot, Host, Port, "open"),
-%% io:format(user, "~w:security -> auth request~n", [?MODULE]),
+ tsp("security -> "
+ "blocking and unblocking of users - "
+ "auth request for nonex user 'one' - expect 401"),
auth_request(Type, Host, Port, Node, "/open/", "one", "onePassword",
[{statuscode, 401}]),
-%% io:format(user, "~w:security -> await fail security event~n", [?MODULE]),
+
+ tsp("security -> "
+ "blocking and unblocking of users - "
+ "await fail security event"),
receive_security_event({event, auth_fail, Port, OpenDir,
[{user, "one"}, {password, "onePassword"}]},
Node, Port),
-%% io:format(user, "~w:security -> auth request~n", [?MODULE]),
+ tsp("security -> "
+ "blocking and unblocking of users - "
+ "auth request for nonex user 'two' - expect 401"),
auth_request(Type,Host,Port,Node,"/open/", "two", "twoPassword",
[{statuscode, 401}]),
-%% io:format(user, "~w:security -> await fail security event~n", [?MODULE]),
+
+ tsp("security -> "
+ "blocking and unblocking of users - "
+ "await fail security event"),
receive_security_event({event, auth_fail, Port, OpenDir,
[{user, "two"}, {password, "twoPassword"}]},
Node, Port),
-%% io:format(user, "~w:security -> auth request~n", [?MODULE]),
+ tsp("security -> "
+ "blocking and unblocking of users - "
+ "auth request for nonex user 'Alladin' - expect 401"),
auth_request(Type, Host, Port, Node,"/open/", "Aladdin",
"AladdinPassword", [{statuscode, 401}]),
-%% io:format(user, "~w:security -> await fail security event~n", [?MODULE]),
+
+ tsp("security -> "
+ "blocking and unblocking of users - "
+ "await fail security event"),
receive_security_event({event, auth_fail, Port, OpenDir,
[{user, "Aladdin"},
{password, "AladdinPassword"}]},
Node, Port),
-%% io:format(user, "~w:security -> add users~n", [?MODULE]),
+ tsp("security -> "
+ "blocking and unblocking of users - "
+ "add user 'one'"),
add_user(Node, ServerRoot, Port, "open", "one", "onePassword", []),
+
+ tsp("security -> "
+ "blocking and unblocking of users - "
+ "add user 'two'"),
add_user(Node, ServerRoot, Port, "open", "two", "twoPassword", []),
-%% io:format(user, "~w:security -> auth request~n", [?MODULE]),
+ tsp("security -> "
+ "blocking and unblocking of users - "
+ "auth request 1 for user 'one' with wrong password - expect 401"),
auth_request(Type, Host, Port, Node,"/open/", "one", "WrongPassword",
[{statuscode, 401}]),
-%% io:format(user, "~w:security -> await fail security event~n", [?MODULE]),
+
+ tsp("security -> "
+ "blocking and unblocking of users - "
+ "await fail security event"),
receive_security_event({event, auth_fail, Port, OpenDir,
[{user, "one"}, {password, "WrongPassword"}]},
Node, Port),
-%% io:format(user, "~w:security -> auth request~n", [?MODULE]),
+ tsp("security -> "
+ "blocking and unblocking of users - "
+ "auth request 2 for user 'one' with wrong password - expect 401"),
auth_request(Type, Host, Port, Node,"/open/", "one", "WrongPassword",
[{statuscode, 401}]),
-%% io:format(user, "~w:security -> await fail security event~n", [?MODULE]),
+
+ tsp("security -> "
+ "blocking and unblocking of users - "
+ "await fail security event"),
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]),
+ tsp("security -> "
+ "blocking and unblocking of users - "
+ "await block security event (two failed attempts)"),
receive_security_event({event, user_block, Port, OpenDir,
[{user, "one"}]}, Node, Port),
-%% io:format(user, "~w:security -> unregister~n", [?MODULE]),
+ tsp("security -> "
+ "blocking and unblocking of users - "
+ "unregister - no more security events"),
global:unregister_name(mod_security_test), % No more events.
-%% io:format(user, "~w:security -> auth request~n", [?MODULE]),
+ tsp("security -> "
+ "blocking and unblocking of users - "
+ "auth request for user 'one' with wrong password - expect 401"),
auth_request(Type, Host, Port, Node,"/open/", "one", "WrongPassword",
[{statuscode, 401}]),
-%% io:format(user, "~w:security -> auth request~n", [?MODULE]),
+ tsp("security -> "
+ "blocking and unblocking of users - "
+ "auth request for user 'one' with correct password - expect 403"),
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]),
+ tsp("security -> "
+ "blocking and unblocking of users - "
+ "list blocked users - 'one' should be the only one"),
case list_blocked_users(Node, Port) of
[{"one",_, Port, OpenDir,_}] ->
ok;
Blocked ->
- %% io:format(user, "~w:security -> Blocked: ~p"
- %% "~n", [?MODULE, Blocked]),
+ tsp(" *** unexpected blocked users ***"
+ "~n Blocked: ~p", [Blocked]),
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]),
+ tsp("security -> "
+ "blocking and unblocking of users - "
+ "list users blocked for dir '~p' - "
+ "user 'one' should be the only one", [OpenDir]),
+ [{"one",_, Port, OpenDir,_}] = list_blocked_users(Node, Port, OpenDir),
+
+ tsp("security -> "
+ "blocking and unblocking of users - "
+ "unblock user 'one' for dir '~p'", [OpenDir]),
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]),
+ %% User "one" should not be blocked any more.
+
+ tsp("security -> "
+ "blocking and unblocking of users - "
+ "ensure user 'one' is no longer blocked"),
[] = 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]),
+
+
+ tsp("security -> "
+ "blocking and unblocking of users - "
+ "auth request for user 'one' with correct password - expect 200"),
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]),
+
+ tsp("security -> "
+ "list-auth-users and auth-timeout - "
+ "list auth users - expect user 'one'"),
["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]),
+
+ tsp("security -> "
+ "list-auth-users and auth-timeout - "
+ "auth request for user 'two' with wrong password - expect 401"),
auth_request(Type, Host, Port, Node,"/open/", "two", "onePassword",
[{statuscode, 401}]),
-%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]),
+
+ tsp("security -> "
+ "list-auth-users and auth-timeout - "
+ "list auth users - expect user 'one'"),
["one"] = list_auth_users(Node, Port),
-%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]),
+
+ tsp("security -> "
+ "list-auth-users and auth-timeout - "
+ "list auth users for dir '~p' - expect user 'one'", [OpenDir]),
["one"] = list_auth_users(Node, Port, OpenDir),
-%% io:format(user, "~w:security -> auth request~n", [?MODULE]),
+
+ tsp("security -> "
+ "list-auth-users and auth-timeout - "
+ "auth request for user 'two' with correct password - expect 401"),
auth_request(Type, Host, Port, Node,"/open/", "two", "twoPassword",
[{statuscode, 401}]),
-%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]),
+
+ tsp("security -> "
+ "list-auth-users and auth-timeout - "
+ "list auth users - expect user 'one'"),
["one"] = list_auth_users(Node, Port),
-%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]),
+
+ tsp("security -> "
+ "list-auth-users and auth-timeout - "
+ "list auth users for dir '~p' - expect user 'one'", [OpenDir]),
["one"] = list_auth_users(Node, Port, OpenDir),
+
%% Wait for successful auth to timeout.
+ tsp("security -> "
+ "list-auth-users and auth-timeout - "
+ "wait for successful auth to timeout"),
test_server:sleep(?AUTH_TIMEOUT*1001),
-%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]),
+
+ tsp("security -> "
+ "list-auth-users and auth-timeout - "
+ "list auth users - expect none"),
[] = list_auth_users(Node, Port),
-%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]),
+
+ tsp("security -> "
+ "list-auth-users and auth-timeout - "
+ "list auth users for dir '~p'~n - expect none", [OpenDir]),
[] = list_auth_users(Node, Port, OpenDir),
+
%% "two" is blocked.
-%% io:format(user, "~w:security -> unblock user~n", [?MODULE]),
+
+ tsp("security -> "
+ "list-auth-users and auth-timeout - "
+ "unblock user 'two' for dir '~p'", [OpenDir]),
true = unblock_user(Node, "two", Port, OpenDir),
+
+
%% Test explicit blocking. Block user 'two'.
-%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]),
+
+ tsp("security -> "
+ "explicit blocking - list blocked users - should be none"),
[] = list_blocked_users(Node,Port,OpenDir),
-%% io:format(user, "~w:security -> block user~n", [?MODULE]),
+
+ tsp("security -> "
+ "explicit blocking - "
+ "block user 'two' for dir '~p'", [OpenDir]),
true = block_user(Node, "two", Port, OpenDir, 10),
-%% io:format(user, "~w:security -> auth request~n", [?MODULE]),
+
+ tsp("security -> "
+ "explicit blocking - "
+ "auth request for user 'two' with correct password - expect 401"),
auth_request(Type, Host, Port, Node,"/open/", "two", "twoPassword",
- [{statuscode, 401}]).
+ [{statuscode, 401}]),
+ tsp("security -> "
+ "done").
+
%%-------------------------------------------------------------------------
auth(Type, Port, Host, Node) ->
+ tsp("auth -> "
+ "entry with"
+ "~n Type: ~p"
+ "~n Port: ~p"
+ "~n Host: ~p"
+ "~n Node: ~p", [Type, Port, Host, Node]),
+
%% Authentication required!
ok = httpd_test_lib:verify_request(Type,Host,Port,Node,
"GET /open/ HTTP/1.0\r\n\r\n",
@@ -913,13 +1024,11 @@ list_users(Node, Root, _Host, Port, Dir) ->
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]),
+ tsp("receive_security_event -> await ~w event", [element(2, Event)]),
receive
Event ->
+ tsp("receive_security_event -> "
+ "received expected ~w event", [element(2, Event)]),
ok;
{'EXIT', _, _} ->
receive_security_event(Event, Node, Port)
@@ -1027,8 +1136,14 @@ check_lists_members1(L1,L2) ->
{error,{lists_not_equal,L1,L2}}.
-%% tsp(F) ->
-%% inets_test_lib:tsp(F).
+%% p(F) ->
+%% p(F, []).
+
+%% p(F, A) ->
+%% io:format(user, "~w:" ++ F ++ "~n", [?MODULE|A]).
+
+tsp(F) ->
+ inets_test_lib:tsp(F).
tsp(F, A) ->
inets_test_lib:tsp(F, A).
diff --git a/lib/inets/test/inets.spec.vxworks b/lib/inets/test/inets.spec.vxworks
deleted file mode 100644
index 6886299226..0000000000
--- a/lib/inets/test/inets.spec.vxworks
+++ /dev/null
@@ -1,5 +0,0 @@
-{topcase, {dir, "../inets_test"}}.
-{skip, {inets_SUITE, ip_mod_cgi, "Requires processes"}}.
-{skip, {inets_SUITE, ip_mod_all_modules, "Requires processes"}}.
-{skip, {inets_SUITE, ssl, "Requires SSL"}}.
-
diff --git a/lib/inets/test/inets_SUITE.erl b/lib/inets/test/inets_SUITE.erl
index 6fa0f44d77..069c68fa1e 100644
--- a/lib/inets/test/inets_SUITE.erl
+++ b/lib/inets/test/inets_SUITE.erl
@@ -363,8 +363,6 @@ start_ftpc(suite) ->
[];
start_ftpc(Config) when is_list(Config) ->
process_flag(trap_exit, true),
- inets:disable_trace(),
- inets:enable_trace(max, io, ftpc),
ok = inets:start(),
try
begin
@@ -393,16 +391,13 @@ start_ftpc(Config) when is_list(Config) ->
tsf(stand_alone_not_shutdown)
end,
ok = inets:stop(),
- inets:disable_trace(),
ok;
_ ->
- inets:disable_trace(),
{skip, "Unable to reach selected FTP server " ++ FtpdHost}
end
end
catch
throw:{error, not_found} ->
- inets:disable_trace(),
{skip, "No available FTP servers"}
end.
@@ -462,8 +457,6 @@ httpd_reload(Config) when is_list(Config) ->
{document_root, PrivDir},
{bind_address, "localhost"}],
- inets:enable_trace(max, io),
-
i("httpd_reload -> start inets"),
ok = inets:start(),
diff --git a/lib/inets/test/inets_app_test.erl b/lib/inets/test/inets_app_test.erl
index db2218f3b6..eabfa69f7c 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-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -35,6 +35,15 @@
init_per_testcase(undef_funcs, Config) ->
NewConfig = lists:keydelete(watchdog, 1, Config),
Dog = test_server:timetrap(inets_test_lib:minutes(10)),
+
+ %% We need to check if there is a point to run this test.
+ %% On some platforms, crypto will not build, which in turn
+ %% causes ssl to not build (at this time, this will
+ %% change in the future).
+ %% So, we first check if we can start crypto, and if not,
+ %% we skip this test case!
+ ?ENSURE_STARTED(crypto),
+
[{watchdog, Dog}| NewConfig];
init_per_testcase(_, Config) ->
Config.
@@ -240,13 +249,6 @@ undef_funcs(suite) ->
undef_funcs(doc) ->
[];
undef_funcs(Config) when is_list(Config) ->
- %% We need to check if there is a point to run this test.
- %% On some platforms, crypto will not build, which in turn
- %% causes ssl to not build (at this time, this will
- %% change in the future).
- %% So, we first check if we can start crypto, and if not,
- %% we skip this test case!
- ?ENSURE_STARTED(crypto),
App = inets,
AppFile = key1search(app_file, Config),
Mods = key1search(modules, AppFile),
diff --git a/lib/inets/test/inets_sup_SUITE.erl b/lib/inets/test/inets_sup_SUITE.erl
index 1d262a2739..65f0f0e09a 100644
--- a/lib/inets/test/inets_sup_SUITE.erl
+++ b/lib/inets/test/inets_sup_SUITE.erl
@@ -226,8 +226,6 @@ ftpc_worker(doc) ->
ftpc_worker(suite) ->
[];
ftpc_worker(Config) when is_list(Config) ->
- inets:disable_trace(),
- inets:enable_trace(max, io, ftpc),
[] = supervisor:which_children(ftp_sup),
try
begin
@@ -239,20 +237,16 @@ ftpc_worker(Config) when is_list(Config) ->
inets:stop(ftpc, Pid),
test_server:sleep(5000),
[] = supervisor:which_children(ftp_sup),
- inets:disable_trace(),
ok;
Children ->
- inets:disable_trace(),
exit({unexpected_children, Children})
end;
_ ->
- inets:disable_trace(),
{skip, "Unable to reach test FTP server"}
end
end
catch
throw:{error, not_found} ->
- inets:disable_trace(),
{skip, "No available FTP servers"}
end.
diff --git a/lib/inets/test/inets_test_lib.erl b/lib/inets/test/inets_test_lib.erl
index c94be796cd..0f8671b682 100644
--- a/lib/inets/test/inets_test_lib.erl
+++ b/lib/inets/test/inets_test_lib.erl
@@ -31,6 +31,7 @@
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([timestamp/0, formated_timestamp/0]).
-export([tsp/1, tsp/2, tsf/1, tss/1]).
-export([check_body/1]).
-export([millis/0, millis_diff/2, hours/1, minutes/1, seconds/1, sleep/1]).
@@ -530,34 +531,27 @@ connect(ip_comm, Host, Port, Opts, Type) ->
"~n Opts: ~p"
"~n Type: ~p", [Host, Port, Opts, Type]),
- case gen_tcp:connect(Host, Port, Opts) of
+ case gen_tcp:connect(Host, Port, Opts, timer:seconds(10)) of
{ok, Socket} ->
tsp("connect success"),
{ok, Socket};
- {error, nxdomain} when Type =:= inet6 ->
- tsp("connect error nxdomain when"
- "~n Opts: ~p", [Opts]),
- connect(ip_comm, Host, Port, Opts -- [inet6], inet);
- {error, eafnosupport} when Type =:= inet6 ->
- tsp("connect error eafnosupport when"
- "~n Opts: ~p", [Opts]),
- connect(ip_comm, Host, Port, Opts -- [inet6], inet);
- {error, econnreset} when Type =:= inet6 ->
- tsp("connect error econnreset when"
- "~n Opts: ~p", [Opts]),
- connect(ip_comm, Host, Port, Opts -- [inet6], inet);
- {error, enetunreach} when Type =:= inet6 ->
- tsp("connect error eafnosupport when"
- "~n Opts: ~p", [Opts]),
- connect(ip_comm, Host, Port, Opts -- [inet6], inet);
- {error, econnrefused} when Type =:= inet6 ->
- tsp("connect error econnrefused when"
- "~n Opts: ~p", [Opts]),
+ {error, Reason} when ((Type =:= inet6) andalso
+ ((Reason =:= timeout) orelse
+ (Reason =:= nxdomain) orelse
+ (Reason =:= eafnosupport) orelse
+ (Reason =:= econnreset) orelse
+ (Reason =:= enetunreach) orelse
+ (Reason =:= econnrefused) orelse
+ (Reason =:= ehostunreach))) ->
+ tsp("connect(ip_comm) -> Connect error: "
+ "~n Reason: ~p"
+ "~n Type: ~p"
+ "~n Opts: ~p", [Reason, Type, Opts]),
connect(ip_comm, Host, Port, Opts -- [inet6], inet);
Error ->
- tsp("connect(ip_conn) -> Fatal connect error: "
+ tsp("connect(ip_comm) -> Fatal connect error: "
"~n Error: ~p"
"~nwhen"
"~n Host: ~p"
@@ -642,6 +636,9 @@ tsf(Reason) ->
tss(Time) ->
test_server:sleep(Time).
+timestamp() ->
+ http_util:timestamp().
+
formated_timestamp() ->
format_timestamp( os:timestamp() ).
diff --git a/lib/inets/test/rules.mk b/lib/inets/test/rules.mk
index 047c03b267..c4a62a87ed 100644
--- a/lib/inets/test/rules.mk
+++ b/lib/inets/test/rules.mk
@@ -17,17 +17,12 @@ DEFAULT_TARGETS = opt debug instr release release_docs clean docs
# Erlang language section
# ----------------------------------------------------
EMULATOR = beam
-ifeq ($(findstring vxworks,$(TARGET)),vxworks)
-# VxWorks object files should be compressed.
-# Other object files should have debug_info.
-ERL_COMPILE_FLAGS += +compressed
-else
+
ifdef BOOTSTRAP
ERL_COMPILE_FLAGS += +slim
else
ERL_COMPILE_FLAGS += +debug_info
endif
-endif
ERLC_WFLAGS = -W
ERLC = erlc $(ERLC_WFLAGS) $(ERLC_FLAGS)
ERL.beam = erl.beam -boot start_clean
diff --git a/lib/inets/vsn.mk b/lib/inets/vsn.mk
index 488947c3a1..0c7cb5e7c2 100644
--- a/lib/inets/vsn.mk
+++ b/lib/inets/vsn.mk
@@ -18,7 +18,7 @@
# %CopyrightEnd%
APPLICATION = inets
-INETS_VSN = 5.9
+INETS_VSN = 5.9.2
PRE_VSN =
APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)"
diff --git a/lib/jinterface/doc/src/Makefile b/lib/jinterface/doc/src/Makefile
index 858c97f187..4d5713566c 100644
--- a/lib/jinterface/doc/src/Makefile
+++ b/lib/jinterface/doc/src/Makefile
@@ -3,7 +3,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2000-2010. All Rights Reserved.
+# Copyright Ericsson AB 2000-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/jinterface/doc/src/notes.xml b/lib/jinterface/doc/src/notes.xml
index 420e121389..19f25e3681 100644
--- a/lib/jinterface/doc/src/notes.xml
+++ b/lib/jinterface/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2000</year><year>2011</year>
+ <year>2000</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
diff --git a/lib/jinterface/java_src/Makefile b/lib/jinterface/java_src/Makefile
index 19f99831eb..0aa9c09548 100644
--- a/lib/jinterface/java_src/Makefile
+++ b/lib/jinterface/java_src/Makefile
@@ -47,7 +47,7 @@ POM_SRC= $(POM_FILE).src
# ----------------------------------------------------
$(POM_TARGET): $(POM_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
include $(ERL_TOP)/make/otp_subdir.mk
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractNode.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractNode.java
index 16cb544a16..c76fad5e45 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractNode.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractNode.java
@@ -90,6 +90,8 @@ public class AbstractNode {
static final int dFlagExportPtrTag = 0x200; // NOT SUPPORTED
static final int dFlagBitBinaries = 0x400;
static final int dFlagNewFloats = 0x800;
+ static final int dFlagUnicodeIo = 0x1000;
+ static final int dFlagUtf8Atoms = 0x10000;
int ntype = NTYPE_R6;
int proto = 0; // tcp/ip
@@ -98,7 +100,7 @@ public class AbstractNode {
int creation = 0;
int flags = dFlagExtendedReferences | dFlagExtendedPidsPorts
| dFlagBitBinaries | dFlagNewFloats | dFlagFunTags
- | dflagNewFunTags;
+ | dflagNewFunTags | dFlagUtf8Atoms;
/* initialize hostname and default cookie */
static {
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/Makefile b/lib/jinterface/java_src/com/ericsson/otp/erlang/Makefile
index ec817aa39b..f476d4594d 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/Makefile
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/Makefile
@@ -3,7 +3,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2000-2011. All Rights Reserved.
+# Copyright Ericsson AB 2000-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -61,7 +61,10 @@ CLASSPATH = $(JAVA_SRC_ROOT)
JAVADOCFLAGS=-d $(DOCDIR)
JAVAFLAGS=-d $(JAVA_DEST_ROOT)
-JARFLAGS=-cvf
+JARFLAGS=-cf
+ifneq ($(V),0)
+JARFLAGS=-cfv
+endif
JAVA_OPTIONS =
@@ -79,13 +82,13 @@ endif
debug opt: make_dirs $(JAVA_DEST_ROOT)$(JARFILE)
make_dirs:
- if [ ! -d "$(JAVA_DEST_ROOT)" ];then mkdir "$(JAVA_DEST_ROOT)"; fi
+ $(V_at)if [ ! -d "$(JAVA_DEST_ROOT)" ];then mkdir "$(JAVA_DEST_ROOT)"; fi
$(JAVA_DEST_ROOT)$(JARFILE): $(TARGET_FILES)
@(cd $(JAVA_DEST_ROOT) ; $(JAR) $(JARFLAGS) $(JARFILE) $(JAVA_CLASS_SUBDIR))
clean:
- rm -f $(TARGET_FILES) *~
+ $(V_at)rm -f $(TARGET_FILES) *~
docs:
@@ -96,13 +99,13 @@ docs:
# include $(ERL_TOP)/make/otp_release_targets.mk
release release_docs release_tests release_html:
- $(MAKE) $(MFLAGS) RELEASE_PATH="$(RELEASE_PATH)" $(TARGET_MAKEFILE) $@_spec
+ $(V_at)$(MAKE) $(MFLAGS) RELEASE_PATH="$(RELEASE_PATH)" $(TARGET_MAKEFILE) $@_spec
release_spec: opt
- $(INSTALL_DIR) "$(RELSYSDIR)/java_src/com/ericsson/otp/erlang"
- $(INSTALL_DATA) $(JAVA_SRC) "$(RELSYSDIR)/java_src/com/ericsson/otp/erlang"
- $(INSTALL_DIR) "$(RELSYSDIR)/priv"
- $(INSTALL_DATA) $(JAVA_DEST_ROOT)$(JARFILE) "$(RELSYSDIR)/priv"
+ $(V_at)$(INSTALL_DIR) "$(RELSYSDIR)/java_src/com/ericsson/otp/erlang"
+ $(V_at)$(INSTALL_DATA) $(JAVA_SRC) "$(RELSYSDIR)/java_src/com/ericsson/otp/erlang"
+ $(V_at)$(INSTALL_DIR) "$(RELSYSDIR)/priv"
+ $(V_at)$(INSTALL_DATA) $(JAVA_DEST_ROOT)$(JARFILE) "$(RELSYSDIR)/priv"
release_docs_spec:
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 deac528133..b985f8aa50 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpEpmd.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpEpmd.java
@@ -358,7 +358,7 @@ public class OtpEpmd {
}
public static String[] lookupNames() throws IOException {
- return lookupNames(InetAddress.getLocalHost());
+ return lookupNames(InetAddress.getByName(null));
}
public static String[] lookupNames(final InetAddress address)
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangAtom.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangAtom.java
index ced4dbb8c2..2768edc6fa 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangAtom.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangAtom.java
@@ -51,7 +51,7 @@ public class OtpErlangAtom extends OtpErlangObject implements Serializable,
"null string value");
}
- if (atom.length() > maxAtomLength) {
+ if (atom.codePointCount(0, atom.length()) > maxAtomLength) {
throw new java.lang.IllegalArgumentException("Atom may not exceed "
+ maxAtomLength + " characters: " + atom);
}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpExternal.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpExternal.java
index e70b9a786b..2a4cd4fa2d 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpExternal.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpExternal.java
@@ -88,6 +88,12 @@ public class OtpExternal {
/** The tag used for old Funs */
public static final int funTag = 117;
+ /** The tag used for unicode atoms */
+ public static final int atomUtf8Tag = 118;
+
+ /** The tag used for small unicode atoms */
+ public static final int smallAtomUtf8Tag = 119;
+
/** The tag used for compressed terms */
public static final int compressedTag = 80;
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpInputStream.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpInputStream.java
index b9b43481ee..c2a79af841 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpInputStream.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpInputStream.java
@@ -351,26 +351,64 @@ public class OtpInputStream extends ByteArrayInputStream {
*/
public String read_atom() throws OtpErlangDecodeException {
int tag;
- int len;
+ int len = -1;
byte[] strbuf;
String atom;
tag = read1skip_version();
- if (tag != OtpExternal.atomTag) {
- throw new OtpErlangDecodeException(
- "wrong tag encountered, expected " + OtpExternal.atomTag
- + ", got " + tag);
- }
+ switch (tag) {
- len = read2BE();
+ case OtpExternal.atomTag:
+ len = read2BE();
+ strbuf = new byte[len];
+ this.readN(strbuf);
+ try {
+ atom = new String(strbuf, "ISO-8859-1");
+ } catch (final java.io.UnsupportedEncodingException e) {
+ throw new OtpErlangDecodeException(
+ "Failed to decode ISO-8859-1 atom");
+ }
+ if (atom.length() > OtpExternal.maxAtomLength) {
+ /*
+ * Throwing an exception would be better I think,
+ * but truncation seems to be the way it has
+ * been done in other parts of OTP...
+ */
+ atom = atom.substring(0, OtpExternal.maxAtomLength);
+ }
+ break;
- strbuf = new byte[len];
- this.readN(strbuf);
- atom = OtpErlangString.newString(strbuf);
+ case OtpExternal.smallAtomUtf8Tag:
+ len = read1();
+ /* fall through */
+ case OtpExternal.atomUtf8Tag:
+ if (len < 0) {
+ len = read2BE();
+ }
+ strbuf = new byte[len];
+ this.readN(strbuf);
+ try {
+ atom = new String(strbuf, "UTF-8");
+ } catch (final java.io.UnsupportedEncodingException e) {
+ throw new OtpErlangDecodeException(
+ "Failed to decode UTF-8 atom");
+ }
+ if (atom.codePointCount(0, atom.length()) > OtpExternal.maxAtomLength) {
+ /*
+ * Throwing an exception would be better I think,
+ * but truncation seems to be the way it has
+ * been done in other parts of OTP...
+ */
+ final int[] cps = OtpErlangString.stringToCodePoints(atom);
+ atom = new String(cps, 0, OtpExternal.maxAtomLength);
+ }
+ break;
- if (atom.length() > OtpExternal.maxAtomLength) {
- atom = atom.substring(0, OtpExternal.maxAtomLength);
+ default:
+ throw new OtpErlangDecodeException(
+ "wrong tag encountered, expected " + OtpExternal.atomTag
+ + ", or " + OtpExternal.atomUtf8Tag + ", got " + tag);
}
return atom;
@@ -1112,12 +1150,16 @@ public class OtpInputStream extends ByteArrayInputStream {
final int size = read4BE();
final byte[] buf = new byte[size];
final java.util.zip.InflaterInputStream is =
- new java.util.zip.InflaterInputStream(this);
+ new java.util.zip.InflaterInputStream(this, new java.util.zip.Inflater(), size);
+ int curPos = 0;
try {
- final int dsize = is.read(buf, 0, size);
- if (dsize != size) {
+ int curRead;
+ while(curPos < size && (curRead = is.read(buf, curPos, size - curPos)) != -1) {
+ curPos += curRead;
+ }
+ if (curPos != size) {
throw new OtpErlangDecodeException("Decompression gave "
- + dsize + " bytes, not " + size);
+ + curPos + " bytes, not " + size);
}
} catch (final IOException e) {
throw new OtpErlangDecodeException("Cannot read from input stream");
@@ -1148,6 +1190,8 @@ public class OtpInputStream extends ByteArrayInputStream {
return new OtpErlangLong(this);
case OtpExternal.atomTag:
+ case OtpExternal.smallAtomUtf8Tag:
+ case OtpExternal.atomUtf8Tag:
return new OtpErlangAtom(this);
case OtpExternal.floatTag:
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java
index 22ebb4688a..10bdf389cd 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java
@@ -343,9 +343,63 @@ public class OtpOutputStream extends ByteArrayOutputStream {
* the string to write.
*/
public void write_atom(final String atom) {
- write1(OtpExternal.atomTag);
- write2BE(atom.length());
- writeN(atom.getBytes());
+ String enc_atom;
+ byte[] bytes;
+ boolean isLatin1 = true;
+
+ if (atom.codePointCount(0, atom.length()) <= OtpExternal.maxAtomLength) {
+ enc_atom = atom;
+ }
+ else {
+ /*
+ * Throwing an exception would be better I think,
+ * but truncation seems to be the way it has
+ * been done in other parts of OTP...
+ */
+ enc_atom = new String(OtpErlangString.stringToCodePoints(atom),
+ 0, OtpExternal.maxAtomLength);
+ }
+
+ for (int offset = 0; offset < enc_atom.length();) {
+ final int cp = enc_atom.codePointAt(offset);
+ if ((cp & ~0xFF) != 0) {
+ isLatin1 = false;
+ break;
+ }
+ offset += Character.charCount(cp);
+ }
+ try {
+ if (isLatin1) {
+ bytes = enc_atom.getBytes("ISO-8859-1");
+ write1(OtpExternal.atomTag);
+ write2BE(bytes.length);
+ }
+ else {
+ bytes = enc_atom.getBytes("UTF-8");
+ final int length = bytes.length;
+ if (length < 256) {
+ write1(OtpExternal.smallAtomUtf8Tag);
+ write1(length);
+ }
+ else {
+ write1(OtpExternal.atomUtf8Tag);
+ write2BE(length);
+ }
+ }
+ writeN(bytes);
+ } catch (final java.io.UnsupportedEncodingException e) {
+ /*
+ * Sigh, why didn't the API designer add an
+ * OtpErlangEncodeException to these encoding
+ * functions?!? Instead of changing the API we
+ * write an invalid atom and let it fail for
+ * whoever trying to decode this... Sigh,
+ * again...
+ */
+ write1(OtpExternal.smallAtomUtf8Tag);
+ write1(2);
+ write2BE(0xffff); /* Invalid UTF-8 */
+ }
}
/**
diff --git a/lib/jinterface/test/Makefile b/lib/jinterface/test/Makefile
index b70521c08d..d9ff406994 100644
--- a/lib/jinterface/test/Makefile
+++ b/lib/jinterface/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2004-2011. All Rights Reserved.
+# Copyright Ericsson AB 2004-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/jinterface/test/jitu.erl b/lib/jinterface/test/jitu.erl
index c57fb9bfad..571a2dc9c7 100644
--- a/lib/jinterface/test/jitu.erl
+++ b/lib/jinterface/test/jitu.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -89,13 +89,19 @@ classpath(Dir) ->
{win32, _} -> ";";
_ -> ":"
end,
- Dir++PS++
+ es(Dir++PS++
filename:join([code:lib_dir(jinterface),"priv","OtpErlang.jar"])++PS++
case os:getenv("CLASSPATH") of
false -> "";
Classpath -> Classpath
- end.
-
+ end).
+
+es(L) ->
+ lists:flatmap(fun($ ) ->
+ "\\ ";
+ (C) ->
+ [C]
+ end,lists:flatten(L)).
cmd(Cmd) ->
PortOpts = [{line,80},eof,exit_status,stderr_to_stdout],
diff --git a/lib/jinterface/test/nc_SUITE.erl b/lib/jinterface/test/nc_SUITE.erl
index 9c88400c2a..546c600116 100644
--- a/lib/jinterface/test/nc_SUITE.erl
+++ b/lib/jinterface/test/nc_SUITE.erl
@@ -1,7 +1,8 @@
+%% -*- coding: utf-8 -*-
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -22,6 +23,15 @@
-include_lib("common_test/include/ct.hrl").
-include("test_server_line.hrl").
+-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).
+-define(ATOM_UTF8_EXT, 118).
+-define(SMALL_ATOM_UTF8_EXT, 119).
-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2,
init_per_suite/1,
@@ -45,6 +55,10 @@
unicode/1,
unicode_list_to_string/1,
unicode_string_to_list/1,
+ utf8_atom/1,
+ utf8_pid/1,
+ utf8_port/1,
+ utf8_ref/1,
connect/1]).
@@ -58,7 +72,9 @@ all() ->
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].
+ unicode_list_to_string, unicode_string_to_list,
+ utf8_atom, utf8_pid, utf8_port, utf8_ref,
+ connect].
groups() ->
[].
@@ -89,7 +105,7 @@ end_per_suite(Config) ->
init_per_testcase(Case, Config) ->
T = case atom_to_list(Case) of
"unicode"++_ -> 240;
- _ -> 20
+ _ -> 30
end,
WatchDog = test_server:timetrap(test_server:seconds(T)),
[{watchdog, WatchDog}| Config].
@@ -187,10 +203,18 @@ binary_roundtrip(Config) when is_list(Config) ->
decompress_roundtrip(doc) -> [];
decompress_roundtrip(suite) -> [];
decompress_roundtrip(Config) when is_list(Config) ->
+ RandomBin = erlang:term_to_binary(lists:seq(1, 5 * 1024 * 1024)), % roughly 26MB
+ <<RandomBin1k:1024/binary,_/binary>> = RandomBin,
+ <<RandomBin1M:1048576/binary,_/binary>> = RandomBin,
+ <<RandomBin10M:10485760/binary,_/binary>> = RandomBin,
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>>,
+ RandomBin1k,
+ RandomBin1M,
+ RandomBin10M,
+ RandomBin,
make_ref()],
OutTrans =
fun (D) ->
@@ -205,10 +229,18 @@ decompress_roundtrip(Config) when is_list(Config) ->
compress_roundtrip(doc) -> [];
compress_roundtrip(suite) -> [];
compress_roundtrip(Config) when is_list(Config) ->
+ RandomBin = erlang:term_to_binary(lists:seq(1, 5 * 1024 * 1024)), % roughly 26MB
+ <<RandomBin1k:1024/binary,_/binary>> = RandomBin,
+ <<RandomBin1M:1048576/binary,_/binary>> = RandomBin,
+ <<RandomBin10M:10485760/binary,_/binary>> = RandomBin,
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>>,
+ RandomBin1k,
+ RandomBin1M,
+ RandomBin10M,
+ RandomBin,
make_ref()],
OutTrans =
fun (D) ->
@@ -345,8 +377,8 @@ unicode(doc) -> [];
unicode(suite) -> [];
unicode(Config) when is_list(Config) ->
S1 = "plain ascii",
- S2 = "iso-latin ��� �",
- S3 = "Codepoints... ��� \x{1000}",
+ 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],
@@ -432,6 +464,71 @@ unicode_string_to_list(Config) when is_list(Config) ->
end, ["unicode"]).
+evil_smiley() ->
+ <<240,159,152,136>>.
+
+evil_smileys(0) ->
+ [];
+evil_smileys(N) ->
+ [evil_smiley() | evil_smileys(N-1)].
+
+utf8_atom(Config) when is_list(Config) ->
+ ES = evil_smiley(),
+ SmallUA = binary_to_term(list_to_binary([?VERSION_MAGIC,
+ ?SMALL_ATOM_UTF8_EXT,
+ size(ES),
+ ES])),
+ true = is_atom(SmallUA),
+ NoESs = 300 div size(ES),
+ ESs = evil_smileys(NoESs),
+ LargeUA = binary_to_term(list_to_binary([?VERSION_MAGIC,
+ ?ATOM_UTF8_EXT,
+ uint16_be(NoESs*size(ES)),
+ ESs])),
+ true = is_atom(LargeUA),
+ erlang:display({atom, SmallUA, LargeUA}),
+ do_echo([SmallUA, LargeUA], Config).
+
+utf8_nodenames_ext() ->
+ H = "@host",
+ ES = evil_smiley(),
+ SmallUANodeExt = list_to_binary([?SMALL_ATOM_UTF8_EXT,
+ size(ES)+length(H),
+ ES,
+ H]),
+ NoESs = 300 div size(ES),
+ ESs = evil_smileys(NoESs),
+ LargeUANodeExt = list_to_binary([?ATOM_UTF8_EXT,
+ uint16_be(NoESs*size(ES)+length(H)),
+ ESs,
+ H]),
+ {SmallUANodeExt, LargeUANodeExt}.
+
+utf8_pid(Config) when is_list(Config) ->
+ {SmallUANodeExt, LargeUANodeExt} = utf8_nodenames_ext(),
+ SmallPid = mk_pid({SmallUANodeExt, 2}, 4711, 4711),
+ LargePid = mk_pid({LargeUANodeExt, 2}, 4711, 4711),
+ erlang:display({pid, SmallPid, node(SmallPid)}),
+ erlang:display({pid, LargePid, node(LargePid)}),
+ do_echo([SmallPid, LargePid], Config).
+
+utf8_port(Config) when is_list(Config) ->
+ {SmallUANodeExt, LargeUANodeExt} = utf8_nodenames_ext(),
+ SmallPort = mk_port({SmallUANodeExt, 2}, 4711),
+ erlang:display({port, SmallPort, node(SmallPort)}),
+ LargePort = mk_port({LargeUANodeExt, 2}, 4711),
+ erlang:display({port, LargePort, node(LargePort)}),
+ do_echo([SmallPort, LargePort], Config).
+
+utf8_ref(Config) when is_list(Config) ->
+ {SmallUANodeExt, LargeUANodeExt} = utf8_nodenames_ext(),
+ SmallRef = mk_ref({SmallUANodeExt, 2}, [4711, 4711, 4711]),
+ erlang:display({ref, SmallRef, node(SmallRef)}),
+ LargeRef = mk_ref({LargeUANodeExt, 2}, [4711, 4711, 4711]),
+ erlang:display({ref, LargeRef, node(LargeRef)}),
+ do_echo([SmallRef, LargeRef], Config).
+
+
%% Lazy list
cp_gen(N) ->
cp_gen(N, -1, 16#110000).
@@ -630,16 +727,6 @@ make_name() ->
++ "-" ++ 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,
@@ -663,72 +750,70 @@ 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) ->
+ <<?VERSION_MAGIC, NodeNameExt/binary>> = term_to_binary(NodeName),
+ mk_pid({NodeNameExt, Creation}, Number, Serial);
+mk_pid({NodeNameExt, Creation}, Number, Serial) ->
case catch binary_to_term(list_to_binary([?VERSION_MAGIC,
?PID_EXT,
- ?ATOM_EXT,
- uint16_be(length(NodeName)),
- NodeName,
+ NodeNameExt,
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]});
+ exit({badarg, mk_pid, [{NodeNameExt, Creation}, Number, Serial]});
Other ->
exit({unexpected_binary_to_term_result, Other})
end.
mk_port({NodeName, Creation}, Number) when is_atom(NodeName) ->
- mk_port({atom_to_list(NodeName), Creation}, Number);
-mk_port({NodeName, Creation}, Number) ->
+ <<?VERSION_MAGIC, NodeNameExt/binary>> = term_to_binary(NodeName),
+ mk_port({NodeNameExt, Creation}, Number);
+mk_port({NodeNameExt, Creation}, Number) ->
case catch binary_to_term(list_to_binary([?VERSION_MAGIC,
?PORT_EXT,
- ?ATOM_EXT,
- uint16_be(length(NodeName)),
- NodeName,
+ NodeNameExt,
uint32_be(Number),
uint8(Creation)])) of
Port when is_port(Port) ->
Port;
{'EXIT', {badarg, _}} ->
- exit({badarg, mk_port, [{NodeName, Creation}, Number]});
+ exit({badarg, mk_port, [{NodeNameExt, Creation}, Number]});
Other ->
exit({unexpected_binary_to_term_result, Other})
end.
-mk_ref({NodeName, Creation}, Numbers) when is_atom(NodeName),
- is_integer(Creation),
- is_list(Numbers) ->
- mk_ref({atom_to_list(NodeName), Creation}, Numbers);
-mk_ref({NodeName, Creation}, [Number]) when is_list(NodeName),
- is_integer(Creation),
- is_integer(Number) ->
+mk_ref({NodeName, Creation}, [Number] = NL) when is_atom(NodeName),
+ is_integer(Creation),
+ is_integer(Number) ->
+ <<?VERSION_MAGIC, NodeNameExt/binary>> = term_to_binary(NodeName),
+ mk_ref({NodeNameExt, Creation}, NL);
+mk_ref({NodeNameExt, Creation}, [Number]) when 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,
+ NodeNameExt,
uint32_be(Number),
uint8(Creation)])) of
Ref when is_reference(Ref) ->
Ref;
{'EXIT', {badarg, _}} ->
- exit({badarg, mk_ref, [{NodeName, Creation}, [Number]]});
+ exit({badarg, mk_ref, [{NodeNameExt, Creation}, [Number]]});
Other ->
exit({unexpected_binary_to_term_result, Other})
end;
-mk_ref({NodeName, Creation}, Numbers) when is_list(NodeName),
+mk_ref({NodeName, Creation}, Numbers) when is_atom(NodeName),
is_integer(Creation),
is_list(Numbers) ->
+ <<?VERSION_MAGIC, NodeNameExt/binary>> = term_to_binary(NodeName),
+ mk_ref({NodeNameExt, Creation}, Numbers);
+mk_ref({NodeNameExt, Creation}, Numbers) when is_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,
+ NodeNameExt,
uint8(Creation),
lists:map(fun (N) ->
uint32_be(N)
@@ -737,7 +822,7 @@ mk_ref({NodeName, Creation}, Numbers) when is_list(NodeName),
Ref when is_reference(Ref) ->
Ref;
{'EXIT', {badarg, _}} ->
- exit({badarg, mk_ref, [{NodeName, Creation}, Numbers]});
+ exit({badarg, mk_ref, [{NodeNameExt, Creation}, Numbers]});
Other ->
exit({unexpected_binary_to_term_result, Other})
end.
diff --git a/lib/kernel/doc/src/Makefile b/lib/kernel/doc/src/Makefile
index 78e5f7bc26..de3ca1e176 100644
--- a/lib/kernel/doc/src/Makefile
+++ b/lib/kernel/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -58,7 +58,6 @@ XML_REF3_FILES = application.xml \
net_adm.xml \
net_kernel.xml \
os.xml \
- packages.xml \
pg2.xml \
rpc.xml \
seq_trace.xml \
diff --git a/lib/kernel/doc/src/application.xml b/lib/kernel/doc/src/application.xml
index 51a3311ec2..9f19efc793 100644
--- a/lib/kernel/doc/src/application.xml
+++ b/lib/kernel/doc/src/application.xml
@@ -121,6 +121,15 @@
</desc>
</func>
<func>
+ <name name="get_env" arity="3"/>
+ <fsummary>Get the value of a configuration parameter using a default</fsummary>
+ <desc>
+ <p>Works like <seealso marker="#get_env/2">get_env/2</seealso> but returns
+ <c><anno>Def</anno></c> value when configuration parameter
+ <c><anno>Par</anno></c> does not exist.</p>
+ </desc>
+ </func>
+ <func>
<name name="get_key" arity="1"/>
<name name="get_key" arity="2"/>
<fsummary>Get the value of an application specification key</fsummary>
diff --git a/lib/kernel/doc/src/code.xml b/lib/kernel/doc/src/code.xml
index bb111a2242..279c7558bc 100644
--- a/lib/kernel/doc/src/code.xml
+++ b/lib/kernel/doc/src/code.xml
@@ -91,7 +91,7 @@
of the additional library directories will override modules with
the same name in OTP, except for modules in Kernel and
STDLIB.</p>
- <p>The environment variable <c>ERL_LIBS</c> (if defined) shold contain
+ <p>The environment variable <c>ERL_LIBS</c> (if defined) should contain
a colon-separated (for Unix-like systems) or semicolon-separated
(for Windows) list of additional libraries.</p>
<p>Example: On an Unix-like system, <c>ERL_LIBS</c> could be set to
diff --git a/lib/kernel/doc/src/error_handler.xml b/lib/kernel/doc/src/error_handler.xml
index acbf9a2c6e..610b65f0a2 100644
--- a/lib/kernel/doc/src/error_handler.xml
+++ b/lib/kernel/doc/src/error_handler.xml
@@ -43,19 +43,39 @@
A (possibly empty) list of arguments <c>Arg1,..,ArgN</c>
</type_desc>
<desc>
- <p>This function is evaluated if a call is made to
+ <p>This function is called by the run-time system if a call is made to
<c><anno>Module</anno>:<anno>Function</anno>(Arg1,.., ArgN)</c> and
<c><anno>Module</anno>:<anno>Function</anno>/N</c> is undefined. Note that
<c>undefined_function/3</c> is evaluated inside the process
making the original call.</p>
- <p>If <c><anno>Module</anno></c> is interpreted, the interpreter is invoked
- and the return value of the interpreted
- <c><anno>Function</anno>(Arg1,.., ArgN)</c> call is returned.</p>
- <p>Otherwise, it returns, if possible, the value of
- <c>apply(<anno>Module</anno>, <anno>Function</anno>, <anno>Args</anno>)</c> after an attempt has been
- made to autoload <c><anno>Module</anno></c>. If this is not possible, the
- call to <c><anno>Module</anno>:<anno>Function</anno>(Arg1,.., ArgN)</c> fails with
- exit reason <c>undef</c>.</p>
+
+ <p>This function will first attempt to autoload
+ <c><anno>Module</anno></c>. If that is not possible,
+ an <c>undef</c> exception will be raised.</p>
+
+ <p>If it was possible to load <c><anno>Module</anno></c>
+ and the function <c><anno>Function</anno>/N</c> is exported,
+ it will be called.</p>
+
+ <p>Otherwise, if the function <c>'$handle_undefined_function'/2</c>
+ is exported, it will be called as
+ <c>'$handle_undefined_function'(</c><anno>Function</anno>,
+ <anno>Args</anno>).
+ </p>
+ <p>Otherwise an <c>undef</c> exception will be raised.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="raise_undef_exception" arity="3"/>
+ <fsummary>Raise an undef exception</fsummary>
+ <type_desc variable="Args">
+ A (possibly empty) list of arguments <c>Arg1,..,ArgN</c>
+ </type_desc>
+ <desc>
+ <p>Raise an <c>undef</c> exception with a stacktrace indicating
+ that <c><anno>Module</anno>:<anno>Function</anno>/N</c> is
+ undefined.
+ </p>
</desc>
</func>
<func>
diff --git a/lib/kernel/doc/src/file.xml b/lib/kernel/doc/src/file.xml
index b2a259080d..4a9b7d2ceb 100644
--- a/lib/kernel/doc/src/file.xml
+++ b/lib/kernel/doc/src/file.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2012</year>
+ <year>1996</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -100,7 +100,11 @@
<name name="deep_list"/>
</datatype>
<datatype>
- <name name="fd"/>
+ <name><marker id="type-fd">fd()</marker></name>
+ <desc>
+ <p>A file descriptor representing a file opened in <seealso
+ marker="#raw">raw</seealso> mode.</p>
+ </desc>
</datatype>
<datatype>
<name name="filename"/>
@@ -109,8 +113,8 @@
<name name="io_device"/>
<desc>
<p>As returned by
- <seealso marker="#open/2">file:open/2</seealso>,
- a process handling IO protocols.</p>
+ <seealso marker="#open/2">file:open/2</seealso>;
+ <c>pid()</c> is a process handling I/O-protocols.</p>
</desc>
</datatype>
<datatype>
@@ -170,6 +174,18 @@
</desc>
</func>
<func>
+ <name name="allocate" arity="3"/>
+ <fsummary>Allocate file space</fsummary>
+ <desc>
+ <p><c>allocate/3</c> can be used to preallocate space for a file.</p>
+ <p>This function only succeeds in platforms that implement this
+ feature. When it succeeds, space is preallocated for the file but
+ the file size might not be updated. This behaviour depends on the
+ preallocation implementation. To guarantee the file size is updated
+ one must truncate the file to the new size.</p>
+ </desc>
+ </func>
+ <func>
<name name="change_group" arity="2"/>
<fsummary>Change group of a file</fsummary>
<desc>
@@ -261,6 +277,9 @@
{person, "pelle", 30}.</code>
<pre>1> <input>file:consult("f.txt").</input>
{ok,[{person,"kalle",25},{person,"pelle",30}]}</pre>
+ <p>The encoding of of <c><anno>Filename</anno></c> can be set
+ by a comment as described in <seealso
+ marker="stdlib:epp#encoding">epp(3)</seealso>.</p>
</desc>
</func>
<func>
@@ -399,6 +418,9 @@
of the error.</p>
</item>
</taglist>
+ <p>The encoding of of <c><anno>Filename</anno></c> can be set
+ by a comment as described in <seealso
+ marker="stdlib:epp#encoding">epp(3)</seealso>.</p>
</desc>
</func>
<func>
@@ -610,7 +632,7 @@
<name name="open" arity="2"/>
<fsummary>Open a file</fsummary>
<desc>
- <p>Opens the file <c><anno>Filename</anno></c> in the mode determined
+ <p>Opens the file <c><anno>File</anno></c> in the mode determined
by <c><anno>Modes</anno></c>, which may contain one or more of the
following items:</p>
<taglist>
@@ -644,7 +666,8 @@
</item>
<tag><c>raw</c></tag>
<item>
- <p>The <c>raw</c> option allows faster access to a file,
+ <p><marker id="raw"/>
+ The <c>raw</c> option allows faster access to a file,
because no Erlang process is needed to handle the file.
However, a file opened in this way has the following
limitations:</p>
@@ -767,6 +790,10 @@
<p>The Encoding can be changed for a file "on the fly" by using the <seealso marker="stdlib:io#setopts/2">io:setopts/2</seealso> function, why a file can be analyzed in latin1 encoding for i.e. a BOM, positioned beyond the BOM and then be set for the right encoding before further reading.See the <seealso marker="stdlib:unicode">unicode(3)</seealso> module for functions identifying BOM's.</p>
<p>This option is not allowed on <c>raw</c> files.</p>
</item>
+ <tag><c>ram</c></tag>
+ <item>
+ <p><c>File</c> must be <c>iodata()</c>. Returns an <c>fd()</c> which lets the <c>file</c> module operate on the data in-memory as if it is a file.</p>
+ </item>
</taglist>
<p>Returns:</p>
<taglist>
@@ -861,6 +888,9 @@
the error.</p>
</item>
</taglist>
+ <p>The encoding of of <c><anno>Filename</anno></c> can be set
+ by a comment as described in <seealso
+ marker="stdlib:epp#encoding">epp(3)</seealso>.</p>
</desc>
</func>
<func>
@@ -902,6 +932,9 @@
of the error.</p>
</item>
</taglist>
+ <p>The encoding of of <c><anno>Filename</anno></c> can be set
+ by a comment as described in <seealso
+ marker="stdlib:epp#encoding">epp(3)</seealso>.</p>
</desc>
</func>
<func>
@@ -971,7 +1004,10 @@
of the error.</p>
</item>
</taglist>
- </desc>
+ <p>The encoding of of <c><anno>Filename</anno></c> can be set
+ by a comment as described in <seealso
+ marker="stdlib:epp#encoding">epp(3)</seealso>.</p>
+ </desc>
</func>
<func>
<name name="path_script" arity="3"/>
@@ -1220,11 +1256,11 @@
<p>The record <c>file_info</c> contains the following fields.</p>
<taglist>
- <tag><c>size = integer()</c></tag>
+ <tag><c>size = integer() >= 0</c></tag>
<item>
<p>Size of file in bytes.</p>
</item>
- <tag><c>type = device | directory | regular | other</c></tag>
+ <tag><c>type = device | directory | other | regular | symlink</c></tag>
<item>
<p>The type of the file.</p>
</item>
@@ -1232,22 +1268,22 @@
<item>
<p>The current system access to the file.</p>
</item>
- <tag><c>atime = <seealso marker="#type-date_time">date_time()</seealso> | integer() </c></tag>
+ <tag><c>atime = <seealso marker="#type-date_time">date_time()</seealso> | integer() >= 0</c></tag>
<item>
<p>The last time the file was read.</p>
</item>
- <tag><c>mtime = <seealso marker="#type-date_time">date_time()</seealso> | integer() </c></tag>
+ <tag><c>mtime = <seealso marker="#type-date_time">date_time()</seealso> | integer() >= 0</c></tag>
<item>
<p>The last time the file was written.</p>
</item>
- <tag><c>ctime = <seealso marker="#type-date_time">date_time()</seealso> | integer() </c></tag>
+ <tag><c>ctime = <seealso marker="#type-date_time">date_time()</seealso> | integer() >=0</c></tag>
<item>
<p>The interpretation of this time field depends on
the operating system. On Unix, it is the last time
the file or the inode was changed. In Windows, it is
the create time.</p>
</item>
- <tag><c>mode = integer()</c></tag>
+ <tag><c>mode = integer() >= 0</c></tag>
<item>
<p>The file permissions as the sum of the following bit
values:</p>
@@ -1278,33 +1314,33 @@
<p>On Unix platforms, other bits than those listed above
may be set.</p>
</item>
- <tag><c>links = integer()</c></tag>
+ <tag><c>links = integer() >= 0</c></tag>
<item>
<p>Number of links to the file (this will always be 1 for
file systems which have no concept of links).</p>
</item>
- <tag><c>major_device = integer()</c></tag>
+ <tag><c>major_device = integer() >= 0</c></tag>
<item>
<p>Identifies the file system where the file is located.
In Windows, the number indicates a drive as follows:
0 means A:, 1 means B:, and so on.</p>
</item>
- <tag><c>minor_device = integer()</c></tag>
+ <tag><c>minor_device = integer() >= 0</c></tag>
<item>
<p>Only valid for character devices on Unix. In all other
cases, this field is zero.</p>
</item>
- <tag><c>inode = integer()</c></tag>
+ <tag><c>inode = integer() >= 0</c></tag>
<item>
<p>Gives the <c>inode</c> number. On non-Unix file systems,
this field will be zero.</p>
</item>
- <tag><c>uid = integer()</c></tag>
+ <tag><c>uid = integer() >= 0</c></tag>
<item>
<p>Indicates the owner of the file. Will be zero for
non-Unix file systems.</p>
</item>
- <tag><c>gid = integer()</c></tag>
+ <tag><c>gid = integer() >= 0</c></tag>
<item>
<p>Gives the group that the owner of the file belongs to.
Will be zero for non-Unix file systems.</p>
@@ -1502,6 +1538,9 @@
of the error.</p>
</item>
</taglist>
+ <p>The encoding of of <c><anno>Filename</anno></c> can be set
+ by a comment as described in <seealso
+ marker="stdlib:epp#encoding">epp(3)</seealso>.</p>
</desc>
</func>
<func>
@@ -1732,22 +1771,22 @@
<p>The following fields are used from the record, if they are
given.</p>
<taglist>
- <tag><c>atime = <seealso marker="#type-date_time">date_time()</seealso> | integer()</c></tag>
+ <tag><c>atime = <seealso marker="#type-date_time">date_time()</seealso> | integer() >= 0</c></tag>
<item>
<p>The last time the file was read.</p>
</item>
- <tag><c>mtime = <seealso marker="#type-date_time">date_time()</seealso> | integer()</c></tag>
+ <tag><c>mtime = <seealso marker="#type-date_time">date_time()</seealso> | integer() >= 0</c></tag>
<item>
<p>The last time the file was written.</p>
</item>
- <tag><c>ctime = <seealso marker="#type-date_time">date_time()</seealso> | integer()</c></tag>
+ <tag><c>ctime = <seealso marker="#type-date_time">date_time()</seealso> | integer() >= 0</c></tag>
<item>
<p>On Unix, any value give for this field will be ignored
(the "ctime" for the file will be set to the current
time). On Windows, this field is the new creation time to
set for the file.</p>
</item>
- <tag><c>mode = integer()</c></tag>
+ <tag><c>mode = integer() >= 0</c></tag>
<item>
<p>The file permissions as the sum of the following bit
values:</p>
@@ -1778,15 +1817,15 @@
<p>On Unix platforms, other bits than those listed above
may be set.</p>
</item>
- <tag><c>uid = integer()</c></tag>
+ <tag><c>uid = integer() >= 0</c></tag>
<item>
<p>Indicates the owner of the file. Ignored for non-Unix
file systems.</p>
</item>
- <tag><c>gid = integer()</c></tag>
+ <tag><c>gid = integer() >= 0</c></tag>
<item>
<p>Gives the group that the owner of the file belongs to.
- Ignored non-Unix file systems.</p>
+ Ignored for non-Unix file systems.</p>
</item>
</taglist>
<p>Typical error reasons:</p>
diff --git a/lib/kernel/doc/src/gen_sctp.xml b/lib/kernel/doc/src/gen_sctp.xml
index e327a4f907..7ea58fffff 100644
--- a/lib/kernel/doc/src/gen_sctp.xml
+++ b/lib/kernel/doc/src/gen_sctp.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2007</year><year>2011</year>
+ <year>2007</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
diff --git a/lib/kernel/doc/src/gen_udp.xml b/lib/kernel/doc/src/gen_udp.xml
index 726dc30546..4850278a64 100644
--- a/lib/kernel/doc/src/gen_udp.xml
+++ b/lib/kernel/doc/src/gen_udp.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1997</year><year>2011</year>
+ <year>1997</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
diff --git a/lib/kernel/doc/src/global.xml b/lib/kernel/doc/src/global.xml
index 304a9b1d88..9c50049503 100644
--- a/lib/kernel/doc/src/global.xml
+++ b/lib/kernel/doc/src/global.xml
@@ -163,7 +163,8 @@
<fsummary>Globally register a name for a pid</fsummary>
<type name="method"/>
<type_desc name="method">{<c>Module</c>, <c>Function</c>}
- is also allowed
+ is currently also allowed for backward compatibility, but its use is
+ deprecated
</type_desc>
<desc>
<p>Globally associates the name <c><anno>Name</anno></c> with a pid, that is,
@@ -180,6 +181,15 @@
unregistered. This function is called once for each name
clash.</p>
+ <warning>
+ <p>If you plan to change code without restarting your system,
+ you must use an external fun (<c>fun Module:Function/Arity</c>)
+ as the <c><anno>Resolve</anno></c> function; if you use a
+ local fun you can never replace the code for the module that
+ the fun belongs to.
+ </p>
+ </warning>
+
<p>There are three pre-defined resolve functions:
<c>random_exit_name/3</c>, <c>random_notify_name/3</c>, and
<c>notify_all_name/3</c>. If no <c><anno>Resolve</anno></c> function is
diff --git a/lib/kernel/doc/src/heart.xml b/lib/kernel/doc/src/heart.xml
index 26d1e27822..2856d84dcf 100644
--- a/lib/kernel/doc/src/heart.xml
+++ b/lib/kernel/doc/src/heart.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2011</year>
+ <year>1996</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -71,6 +71,39 @@
timeout and try to reboot the system. This can happen, for
example, if the system clock is adjusted automatically by use of
NTP (Network Time Protocol).</p>
+
+ <p> If a crash occurs, an <c><![CDATA[erl_crash.dump]]></c> will <em>not</em> be written
+ unless the environment variable <c><![CDATA[ERL_CRASH_DUMP_SECONDS]]></c> is set.
+ </p>
+
+ <pre>
+% <input>erl -heart -env ERL_CRASH_DUMP_SECONDS 10 ...</input></pre>
+ <p>
+ Furthermore, <c><![CDATA[ERL_CRASH_DUMP_SECONDS]]></c> has the following behaviour on
+ <c>heart</c>:
+ </p>
+ <taglist>
+ <tag><c><![CDATA[ERL_CRASH_DUMP_SECONDS=0]]></c></tag>
+ <item><p>
+ Suppresses the writing a crash dump file entirely,
+ thus rebooting the runtime system immediately.
+ This is the same as not setting the environment variable.
+ </p>
+ </item>
+ <tag><c><![CDATA[ERL_CRASH_DUMP_SECONDS=-1]]></c></tag>
+ <item><p> Setting the environment variable to a negative value will not reboot
+ the runtime system until the crash dump file has been completly written.
+ </p>
+ </item>
+ <tag><c><![CDATA[ERL_CRASH_DUMP_SECONDS=S]]></c></tag>
+ <item><p>
+ Heart will wait for <c>S</c> seconds to let the crash dump file be written.
+ After <c>S</c> seconds <c>heart</c> will reboot the runtime system regardless of
+ the crash dump file has been written or not.
+ </p>
+ </item>
+ </taglist>
+
<p>In the following descriptions, all function fails with reason
<c>badarg</c> if <c>heart</c> is not started.</p>
</description>
diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml
index 32b4a429dd..3d929a772e 100644
--- a/lib/kernel/doc/src/inet.xml
+++ b/lib/kernel/doc/src/inet.xml
@@ -323,8 +323,11 @@ fe80::204:acff:fe17:bf38
<type name="stat_option"/>
<desc>
<p>Gets one or more statistic options for a socket.</p>
+
<p><c>getstat(<anno>Socket</anno>)</c> is equivalent to
- <c>getstat(<anno>Socket</anno>,&nbsp;[recv_avg,&nbsp;recv_cnt,&nbsp;recv_dvi,&nbsp;recv_max,&nbsp;recv_oct,&nbsp;send_avg,&nbsp;send_cnt,&nbsp;send_dvi,&nbsp;send_max,&nbsp;send_oct])</c></p>
+ <c>getstat(<anno>Socket</anno>, [recv_avg, recv_cnt, recv_dvi,
+ recv_max, recv_oct, send_avg, send_cnt, send_dvi, send_max,
+ send_oct])</c>.</p>
<p>The following options are available:</p>
<taglist>
<tag><c>recv_avg</c></tag>
@@ -371,7 +374,51 @@ fe80::204:acff:fe17:bf38
</taglist>
</desc>
</func>
-
+ <func>
+ <name name="parse_ipv4_address" arity="1" />
+ <fsummary>Parse an IPv4 address</fsummary>
+ <desc>
+ <p>Parses an IPv4 address string and returns an <a href="#type-ip4_address">ip4_address()</a>.
+ Accepts a shortened IPv4 shortened address string.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="parse_ipv4strict_address" arity="1" />
+ <fsummary>Parse an IPv4 address strict.</fsummary>
+ <desc>
+ <p>Parses an IPv4 address string containing four fields, i.e <b>not</b> shortened, and returns an <a href="#type-ip4_adress">ip4_address()</a>.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="parse_ipv6_address" arity="1" />
+ <fsummary>Parse an IPv6 address</fsummary>
+ <desc>
+ <p>Parses an IPv6 address string and returns an <a href="#type-ip6_address">ip6_address()</a>.
+ If an IPv4 address string is passed, an IPv4-mapped IPv6 address is returned.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="parse_ipv6strict_address" arity="1" />
+ <fsummary>Parse an IPv6 address strict.</fsummary>
+ <desc>
+ <p>Parses an IPv6 address string and returns an <a href="#type-ip6_address">ip6_address()</a>.
+ Does <b>not</b> accept IPv4 adresses.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="parse_address" arity="1" />
+ <fsummary>Parse an IPv4 or IPv6 address.</fsummary>
+ <desc>
+ <p>Parses an IPv4 or IPv6 address string and returns an <a href="#type-ip4_address">ip4_address()</a> or <a href="#type-ip6_address">ip6_address()</a>. Accepts a shortened IPv4 address string.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="parse_strict_address" arity="1" />
+ <fsummary>Parse an IPv4 or IPv6 address strict.</fsummary>
+ <desc>
+ <p>Parses an IPv4 or IPv6 address string and returns an <a href="#type-ip4_address">ip4_address()</a> or <a href="#type-ip6_adress">ip6_address()</a>. Does <b>not</b> accept a shortened IPv4 address string.</p>
+ </desc>
+ </func>
<func>
<name name="peername" arity="1"/>
<fsummary>Return the address and port for the other end of a connection</fsummary>
@@ -512,13 +559,100 @@ fe80::204:acff:fe17:bf38
<c>[Byte1,Byte2|Binary]</c>.</p>
</item>
- <tag><c>{high_watermark, Size}</c></tag>
- <item> <p>
- Sender is forced busy if sent and enqueued data
- reaches the highwater mark.
- <br /> Default: 8192 kB.
- </p>
- </item>
+ <tag><c>{high_msgq_watermark, Size}</c> (TCP/IP sockets)</tag>
+ <item>
+ <p>The socket message queue will be set into a busy
+ state when the amount of data queued on the message
+ queue reaches this limit. Note that this limit only
+ concerns data that have not yet reached the ERTS internal
+ socket implementation. Default value used is 8 kB.</p>
+ <p>Senders of data to the socket will be suspended if
+ either the socket message queue is busy, or the socket
+ itself is busy.</p>
+ <p>For more information see the <c>low_msgq_watermark</c>,
+ <c>high_watermark</c>, and <c>low_watermark</c> options.</p>
+ <p>Note that distribution sockets will disable the use of
+ <c>high_msgq_watermark</c> and <c>low_msgq_watermark</c>,
+ and will instead use the
+ <seealso marker="erts:erlang#system_info_dist_buf_busy_limit">distribution
+ buffer busy limit</seealso> which is a similar feature.</p>
+ </item>
+
+ <tag><c>{high_watermark, Size}</c> (TCP/IP sockets)</tag>
+ <item>
+ <p>The socket will be set into a busy state when the amount
+ of data queued internally by the ERTS socket implementation
+ reaches this limit. Default value used is 8 kB.</p>
+ <p>Senders of data to the socket will be suspended if
+ either the socket message queue is busy, or the socket
+ itself is busy.</p>
+ <p>For more information see the <c>low_watermark</c>,
+ <c>high_msgq_watermark</c>, and <c>low_msqg_watermark</c>
+ options.</p>
+ </item>
+
+ <tag><c>{ipv6_v6only, Boolean}</c></tag>
+ <item>
+ <p>
+ Restricts the socket to only use IPv6, prohibiting any
+ IPv4 connections. This is only applicable for
+ IPv6 sockets (option <c>inet6</c>).
+ </p>
+ <p>
+ On most platforms this option has to be set on the socket
+ before associating it to an address. Therefore it is only
+ reasonable to give it when creating the socket and not
+ to use it when calling the function
+ (<seealso marker="#setopts/2">setopts/2</seealso>)
+ containing this description.
+ </p>
+ <p>
+ The behaviour of a socket with this socket option set to
+ <c>true</c> is becoming the only portable one. The original
+ idea when IPv6 was new of using IPv6 for all traffic
+ is now not recommended by FreeBSD (you can use
+ <c>{ipv6_v6only,false}</c> to override the recommended
+ system default value),
+ forbidden by OpenBSD (the supported GENERIC kernel)
+ and impossible on Windows (that has separate
+ IPv4 and IPv6 protocol stacks). Most Linux distros
+ still have a system default value of <c>false</c>.
+ This policy shift among operating systems towards
+ separating IPv6 from IPv4 traffic has evolved since
+ it gradually proved hard and complicated to get
+ a dual stack implementation correct and secure.
+ </p>
+ <p>
+ On some platforms the only allowed value for this option
+ is <c>true</c>, e.g. OpenBSD and Windows. Trying to set
+ this option to <c>false</c> when creating the socket
+ will in this case fail.
+ </p>
+ <p>
+ Setting this option on platforms where it does not exist
+ is ignored and getting this option with
+ <seealso marker="#getopts/2">getopts/2</seealso>
+ returns no value i.e the returned list will not contain an
+ <c>{ipv6_v6only,_}</c> tuple. On Windows the option acually
+ does not exist, but it is emulated as being a
+ read-only option with the value <c>true</c>.
+ </p>
+ <p>
+ So it boils down to that setting this option to <c>true</c>
+ when creating a socket will never fail except possibly
+ (at the time of this writing) on a platform where you
+ have customized the kernel to only allow <c>false</c>,
+ which might be doable (but weird) on e.g. OpenBSD.
+ </p>
+ <p>
+ If you read back the option value using
+ <seealso marker="#getopts/2">getopts/2</seealso>
+ and get no value the option does not exist in the host OS
+ and all bets are off regarding the behaviour of both
+ an IPv6 and an IPv4 socket listening on the same port
+ as well as for an IPv6 socket getting IPv4 traffic.
+ </p>
+ </item>
<tag><c>{keepalive, Boolean}</c>(TCP/IP sockets)</tag>
<item>
@@ -540,14 +674,41 @@ fe80::204:acff:fe17:bf38
the flushing time-out in seconds.</p>
</item>
- <tag><c>{low_watermark, Size}</c></tag>
- <item> <p>
- If the port has reached its <c>high_watermark</c> it will
- force busy onto senders. When the port data queue reaches the
- <c>low_watermark</c> callers are no longer forced busy.
- <br /> Default: 4096 kB.
- </p>
- </item>
+ <tag><c>{low_msgq_watermark, Size}</c> (TCP/IP sockets)</tag>
+ <item>
+ <p>If the socket message queue is in a busy state, the
+ socket message queue will be set in a not busy state when
+ the amount of data queued in the message queue falls
+ below this limit. Note that this limit only concerns data
+ that have not yet reached the ERTS internal socket
+ implementation. Default value used is 4 kB.</p>
+ <p>Senders that have been suspended due to either a
+ busy message queue or a busy socket, will be resumed
+ when neither the socket message queue, nor the socket
+ are busy.</p>
+ <p>For more information see the <c>high_msgq_watermark</c>,
+ <c>high_watermark</c>, and <c>low_watermark</c> options.</p>
+ <p>Note that distribution sockets will disable the use of
+ <c>high_msgq_watermark</c> and <c>low_msgq_watermark</c>,
+ and will instead use the
+ <seealso marker="erts:erlang#system_info_dist_buf_busy_limit">distribution
+ buffer busy limit</seealso> which is a similar feature.</p>
+ </item>
+
+ <tag><c>{low_watermark, Size}</c> (TCP/IP sockets)</tag>
+ <item>
+ <p>If the socket is in a busy state, the socket will
+ be set in a not busy state when the amount of data
+ queued internally by the ERTS socket implementation
+ falls below this limit. Default value used is 4 kB.</p>
+ <p>Senders that have been suspended due to either a
+ busy message queue or a busy socket, will be resumed
+ when neither the socket message queue, nor the socket
+ are busy.</p>
+ <p>For more information see the <c>high_watermark</c>,
+ <c>high_msgq_watermark</c>, and <c>low_msgq_watermark</c>
+ options.</p>
+ </item>
<tag><c>{mode, Mode :: binary | list}</c></tag>
<item>
diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml
index 94624bf512..78bc533464 100644
--- a/lib/kernel/doc/src/notes.xml
+++ b/lib/kernel/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2004</year><year>2011</year>
+ <year>2004</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -30,6 +30,135 @@
</header>
<p>This document describes the changes made to the Kernel application.</p>
+<section><title>Kernel 2.15.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Ensure 'erl_crash.dump' when asked for it. This will
+ change erl_crash.dump behaviour.</p>
+ <p>
+ * Not setting ERL_CRASH_DUMP_SECONDS will now terminate
+ beam immediately on a crash without writing a crash dump
+ file.</p>
+ <p>
+ * Setting ERL_CRASH_DUMP_SECONDS to 0 will also terminate
+ beam immediately on a crash without writing a crash dump
+ file, i.e. same as not setting ERL_CRASH_DUMP_SECONDS
+ environment variable.</p>
+ <p>
+ * Setting ERL_CRASH_DUMP_SECONDS to a negative value will
+ let the beam wait indefinitely on the crash dump file
+ being written.</p>
+ <p>
+ * Setting ERL_CRASH_DUMP_SECONDS to a positive value will
+ let the beam wait that many seconds on the crash dump
+ file being written.</p>
+ <p>
+ A positive value will set an alarm/timeout for restart
+ both in beam and in heart if heart is running.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-10422 Aux Id: kunagi-250 [161] </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Kernel 2.15.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixed issue where using controlling_process/2 with self()
+ as the second argument caused the port to leak if self()
+ crashes. (Thanks to Ricardo Catalinas Jim�nez)</p>
+ <p>
+ Own Id: OTP-10094</p>
+ </item>
+ <item>
+ <p>
+ When sending large files using the file:sendfile fallback
+ file:sendfile would crash. This is now fixed.</p>
+ <p>
+ Own Id: OTP-10098</p>
+ </item>
+ <item>
+ <p>
+ Fix rpc:call/5 for local calls with a finite Timeout
+ (Thanks to Tomer Chachamu)</p>
+ <p>
+ Own Id: OTP-10149</p>
+ </item>
+ <item>
+ <p>
+ fix escript/primary archive reloading</p>
+ <p>
+ If the mtime of an escript/primary archive file changes
+ after being added to the code path, correctly reload the
+ archive and update the cache. (Thanks to Tuncer Ayaz)</p>
+ <p>
+ Own Id: OTP-10151</p>
+ </item>
+ <item>
+ <p>
+ Support added for home directories named with non-ASCII
+ characters (codepoints above 127) on a system running in
+ Unicode file mode (e.g. on MacOSX or Linux with startup
+ arguments +fnu or +fna with the right LOCALE). Also
+ environment variables with Unicode content are supported
+ in applicable environments.</p>
+ <p>
+ Own Id: OTP-10160</p>
+ </item>
+ <item>
+ <p>
+ Allow mixed IPv4 and IPv6 addresses to sctp_bindx</p>
+ <p>
+ Also allow mixed address families to bind, since the
+ first address on a multihomed sctp socket must be bound
+ with bind, while the rest are to be bound using
+ sctp_bindx. At least Linux supports adding address of
+ mixing families. Make inet_set_faddress function
+ available also when HAVE_SCTP is not defined, since we
+ use it to find an address for bind to be able to mix ipv4
+ and ipv6 addresses. Thanks to Tomas Abrahamsson</p>
+ <p>
+ Own Id: OTP-10217</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>Document inet options: high_watermark, priority,
+ linger and a some other options that previously was
+ undocumented.</p>
+ <p>
+ Own Id: OTP-10053</p>
+ </item>
+ <item>
+ <p>Remove bit8 option support from inet</p>
+ <p>
+ Own Id: OTP-10056</p>
+ </item>
+ <item>
+ <p> The type of the disk log header has been corrected.
+ (Thanks to Niclas Eklund.) </p>
+ <p>
+ Own Id: OTP-10131</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Kernel 2.15.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/kernel/doc/src/os.xml b/lib/kernel/doc/src/os.xml
index 1bc5b9e464..5e182de41d 100644
--- a/lib/kernel/doc/src/os.xml
+++ b/lib/kernel/doc/src/os.xml
@@ -80,6 +80,10 @@ DirOut = os:cmd("dir"), % on Win32 platform</code>
Each environment variable is given as a single string on
the format <c>"VarName=Value"</c>, where <c>VarName</c> is
the name of the variable and <c>Value</c> its value.</p>
+ <p>If Unicode file name encoding is in effect (see the <seealso
+ marker="erts:erl#file_name_encoding">erl manual
+ page</seealso>), the strings may contain characters with
+ codepoints > 255.</p>
</desc>
</func>
<func>
@@ -89,6 +93,10 @@ DirOut = os:cmd("dir"), % on Win32 platform</code>
<p>Returns the <c><anno>Value</anno></c> of the environment variable
<c><anno>VarName</anno></c>, or <c>false</c> if the environment variable
is undefined.</p>
+ <p>If Unicode file name encoding is in effect (see the <seealso
+ marker="erts:erl#file_name_encoding">erl manual
+ page</seealso>), the strings (both <c><anno>VarName</anno></c> and
+ <c><anno>Value</anno></c>) may contain characters with codepoints > 255.</p>
</desc>
</func>
<func>
@@ -100,8 +108,7 @@ DirOut = os:cmd("dir"), % on Win32 platform</code>
environment. <c><anno>Value</anno></c> is returned as a string containing
the (usually) numerical identifier for a process. On Unix,
this is typically the return value of the <c>getpid()</c>
- system call. On VxWorks, <c><anno>Value</anno></c> contains the task id
- (decimal notation) of the Erlang task. On Windows,
+ system call. On Windows,
the process id as returned by the <c>GetCurrentProcessId()</c>
system call is used.</p>
</desc>
@@ -112,6 +119,13 @@ DirOut = os:cmd("dir"), % on Win32 platform</code>
<desc>
<p>Sets a new <c><anno>Value</anno></c> for the environment variable
<c><anno>VarName</anno></c>.</p>
+ <p>If Unicode filename encoding is in effect (see the <seealso
+ marker="erts:erl#file_name_encoding">erl manual
+ page</seealso>), the strings (both <c><anno>VarName</anno></c> and
+ <c><anno>Value</anno></c>) may contain characters with codepoints > 255.</p>
+ <p>On Unix platforms, the environment will be set using UTF-8 encoding
+ if Unicode file name translation is in effect. On Windows the
+ environment is set using wide character interfaces.</p>
</desc>
</func>
<func>
@@ -154,8 +168,6 @@ format_utc_timestamp() ->
Solaris 1 and 2, it will be <c>sunos</c>.</p>
<p>In Windows, <c><anno>Osname</anno></c> will be either <c>nt</c> (on
Windows NT), or <c>windows</c> (on Windows 95).</p>
- <p>On VxWorks the OS family alone is returned, that is
- <c>vxworks</c>.</p>
<note>
<p>Think twice before using this function. Use the
<c>filename</c> module if you want to inspect or build
diff --git a/lib/kernel/doc/src/packages.xml b/lib/kernel/doc/src/packages.xml
deleted file mode 100644
index 80de2e05fc..0000000000
--- a/lib/kernel/doc/src/packages.xml
+++ /dev/null
@@ -1,214 +0,0 @@
-<?xml version="1.0" encoding="latin1" ?>
-<!DOCTYPE erlref SYSTEM "erlref.dtd">
-
-<erlref>
- <header>
- <copyright>
- <year>2004</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>packages</title>
- <prepared>Kenneth Lundin</prepared>
- <responsible>Kenneth Lundin</responsible>
- <docno>1</docno>
- <approved>Kenneth Lundin</approved>
- <checked></checked>
- <date>2004-09-07</date>
- <rev>A</rev>
- <file>packages.sgml</file>
- </header>
- <module>packages</module>
- <modulesummary>Packages in Erlang</modulesummary>
- <description>
- <warning><p>
- Packages has since it was introduced more than 5 years ago been an
- experimental feature. Use it at your own risk, we do not
- actively maintain and develop this feature. It might however be
- supported some
- day.
- </p>
- <p>
- In spite of this packages work quite well, but there are some
- known issues in tools and other parts where packages don't work well.
- </p>
- </warning>
- <p><em>Introduction</em></p>
- <p>Packages are simply namespaces for modules.
- All old Erlang modules automatically belong to the top level
- ("empty-string") namespace, and do not need any changes.</p>
- <p>The full name of a packaged module is written as e.g.
- "<c>fee.fie.foe.foo</c>",
- i.e., as atoms separated by periods,
- where the package name is the part up to
- but not including the last period;
- in this case "<c>fee.fie.foe</c>".
- A more concrete example is the module <c>erl.lang.term</c>,
- which is in the
- package <c>erl.lang</c>.
- Package names can have any number of segments, as in
- <c>erl.lang.list.sort</c>.
- The atoms in the name can be quoted, as in <c>foo.'Bar'.baz</c>,
- or even the
- whole name, as in <c>'foo.bar.baz'</c> but the concatenation of
- atoms and
- periods must not contain two consecutive period characters or
- end with a period,
- as in <c>'foo..bar'</c>, <c>foo.'.bar'</c>, or <c>foo.'bar.'</c>.
- The periods must not be followed by whitespace.</p>
- <p>The code loader maps module names onto the file system directory
- structure.
- E.g., the module <c>erl.lang.term</c> corresponds to a file
- <c>.../erl/lang/term.beam</c>
- in the search path.
- Note that the name of the actual object file corresponds to
- the last part only of the full module name.
- (Thus, old existing modules such as <c>lists</c>
- simply map to <c>.../lists.beam</c>, exactly as before.)</p>
- <p>A packaged module in a file "<c>foo/bar/fred.erl</c>" is declared
- as:</p>
- <code type="none">
--module(foo.bar.fred).</code>
- <p>This can be compiled and loaded from the Erlang shell using
- <c>c(fred)</c>, if
- your current directory is the same as that of the file.
- The object file will be named <c>fred.beam</c>.</p>
- <p>The Erlang search path works exactly as before,
- except that the package segments will be appended to each
- directory in the path in order to find the
- file. E.g., assume the path is <c>["/usr/lib/erl", "/usr/local/lib/otp/legacy/ebin", "/home/barney/erl"]</c>.
- Then, the code for a module named <c>foo.bar.fred</c> will be
- searched for
- first as <c>"/usr/lib/erl/foo/bar/fred.beam"</c>, then
- <c>"/usr/local/lib/otp/legacy/ebin/foo/bar/fred.beam"</c>
- and lastly <c>"/home/barney/erl/foo/bar/fred.beam"</c>.
- A module
- like <c>lists</c>, which is in the top-level package,
- will be looked for as <c>"/usr/lib/erl/lists.beam"</c>,
- <c>"/usr/local/lib/otp/legacy/ebin/lists.beam"</c> and
- <c>"/home/barney/erl/lists.beam"</c>.</p>
- <p><em>Programming</em></p>
- <p>Normally, if a call is made from one module to another,
- it is assumed that the
- called module belongs to the same package as the source module.
- The compiler
- automatically expands such calls. E.g., in:</p>
- <code type="none">
--module(foo.bar.m1).
--export([f/1]).
-
-f(X) -> m2:g(X).</code>
- <p><c>m2:g(X)</c> becomes a call to <c>foo.bar.m2</c>
- If this is not what was intended, the call can be written
- explicitly, as in</p>
- <code type="none">
--module(foo.bar.m1).
--export([f/1]).
-
-f(X) -> fee.fie.foe.m2:g(X).</code>
- <p>Because the called module is given with an explicit package name,
- no expansion is done in this case.</p>
- <p>If a module from another package is used repeatedly in a module,
- an import declaration can make life easier:</p>
- <code type="none">
--module(foo.bar.m1).
--export([f/1, g/1]).
--import(fee.fie.foe.m2).
-
-f(X) -> m2:g(X).
-g(X) -> m2:h(X).</code>
- <p>will make the calls to <c>m2</c> refer to <c>fee.fie.foe.m2</c>.
- More generally, a declaration <c>-import(Package.Module).</c>
- will cause calls to <c>Module</c>
- to be expanded to <c>Package.Module</c>.</p>
- <p>Old-style function imports work as normal (but full module
- names must be
- used); e.g.:</p>
- <code type="none">
--import(fee.fie.foe.m2, [g/1, h/1]).</code>
- <p>however, it is probably better to avoid this form of import
- altogether in new
- code, since it makes it hard to see what calls are really "remote".</p>
- <p>If it is necessary to call a module in the top-level package
- from within a
- named package, the module name can be written either with an
- initial period as
- in e.g. "<c>.lists</c>", or with an empty initial atom, as in
- "<c>''.lists</c>".
- However, the best way is to use an import declaration -
- this is most obvious to
- the eye, and makes sure we don't forget adding a period somewhere:</p>
- <code type="none">
--module(foo.bar.fred).
--export([f/1]).
--import(lists).
-
-f(X) -> lists:reverse(X).</code>
- <p>The dot-syntax for module names can be used in any expression.
- All segments must
- be constant atoms, and the result must be a well-formed
- package/module name.
- E.g.:</p>
- <code type="none">
-spawn(foo.bar.fred, f, [X])</code>
- <p>is equivalent to <c>spawn('foo.bar.fred', f, [X])</c>.</p>
- <p><em>The Erlang Shell</em></p>
- <p>The shell also automatically expands remote calls,
- however currently no
- expansions are made by default.
- The user can change the behaviour by using the <c>import/1</c>
- shell command (or its abbreviation <c>use/1</c>). E.g.:</p>
- <pre>
-1> <input>import(foo.bar.m).</input>
-ok
-2> <input>m:f().</input></pre>
- <p>will evaluate <c>foo.bar.m:f()</c>.
- If a new import is made of the same name,
- this overrides any previous import.
- (It is likely that in the future, some
- system packages will be pre-imported.)</p>
- <p>In addition, the shell command <c>import_all/1</c>
- (and its alias <c>use_all/1</c>)
- imports all modules currently found in the path for a given
- package name. E.g.,
- assuming the files "<c>.../foo/bar/fred.beam</c>",
- "<c>.../foo/bar/barney.beam</c>"
- and "<c>.../foo/bar/bambam.beam</c>" can be found from our current
- path,</p>
- <pre>
-1> <input>import_all(foo.bar).</input></pre>
- <p>will make <c>fred</c>, <c>barney</c> and <c>bambam</c>
- expand to <c>foo.bar.fred</c>,
- <c>foo.bar.barney</c> and <c>foo.bar.bambam</c>, respectively.</p>
- <p>Note: The compiler does not have an "import all" directive, for the
- reason that Erlang has no compile time type checking.
- E.g. if the wrong search
- path is used at compile time, a call <c>m:f(...)</c>
- could be expanded to <c>foo.bar.m:f(...)</c>
- without any warning, instead of the intended
- <c>frob.ozz.m:f(...)</c>, if
- package <c>foo.bar</c> happens to be found first in the path.
- Explicitly
- declaring each use of a module makes for safe code.</p>
- </description>
- <funcs>
- <func>
- <name>no functions exported</name>
- <fsummary>x</fsummary>
- </func>
- </funcs>
-</erlref>
-
diff --git a/lib/kernel/doc/src/ref_man.xml b/lib/kernel/doc/src/ref_man.xml
index 9ef0959271..67d91ba585 100644
--- a/lib/kernel/doc/src/ref_man.xml
+++ b/lib/kernel/doc/src/ref_man.xml
@@ -64,6 +64,5 @@
<xi:include href="zlib_stub.xml"/>
<xi:include href="app.xml"/>
<xi:include href="config.xml"/>
- <xi:include href="packages.xml"/>
</application>
diff --git a/lib/kernel/examples/uds_dist/c_src/uds_drv.c b/lib/kernel/examples/uds_dist/c_src/uds_drv.c
index 9327ab19dc..9ad6b85a0f 100644
--- a/lib/kernel/examples/uds_dist/c_src/uds_drv.c
+++ b/lib/kernel/examples/uds_dist/c_src/uds_drv.c
@@ -967,7 +967,7 @@ static void *my_malloc(size_t size)
void *ptr;
if ((ptr = driver_alloc(size)) == NULL) {
- erl_exit(1,"Could not allocate %d bytes of memory",(int) size);
+ erl_exit(1,"Could not allocate %lu bytes of memory",(unsigned long) size);
}
return ptr;
}
@@ -977,7 +977,7 @@ static void *my_realloc(void *ptr, size_t size)
void erl_exit(int, char *, ...);
void *nptr;
if ((nptr = driver_realloc(ptr, size)) == NULL) {
- erl_exit(1,"Could not reallocate %d bytes of memory",(int) size);
+ erl_exit(1,"Could not reallocate %lu bytes of memory",(unsigned long) size);
}
return nptr;
}
diff --git a/lib/kernel/include/dist.hrl b/lib/kernel/include/dist.hrl
index 5b52f6f294..91e13d99a9 100644
--- a/lib/kernel/include/dist.hrl
+++ b/lib/kernel/include/dist.hrl
@@ -36,3 +36,4 @@
-define(DFLAG_UNICODE_IO,16#1000).
-define(DFLAG_DIST_HDR_ATOM_CACHE,16#2000).
-define(DFLAG_SMALL_ATOM_TAGS, 16#4000).
+-define(DFLAG_UTF8_ATOMS, 16#10000).
diff --git a/lib/kernel/include/file.hrl b/lib/kernel/include/file.hrl
index bf97173122..69aec1ee36 100644
--- a/lib/kernel/include/file.hrl
+++ b/lib/kernel/include/file.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -22,32 +22,37 @@
%%--------------------------------------------------------------------------
-record(file_info,
- {size :: non_neg_integer(), % Size of file in bytes.
- type :: 'device' | 'directory' | 'other' | 'regular' | 'symlink',
- access :: 'read' | 'write' | 'read_write' | 'none',
- atime :: file:date_time() | integer(), % The local time the file was last read:
- % {{Year, Mon, Day}, {Hour, Min, Sec}}.
- % atime, ctime, mtime may also be unix epochs()
- mtime :: file:date_time() | integer(), % The local time the file was last written.
- ctime :: file:date_time() | integer(), % The interpretation of this time field
- % is dependent on operating system.
- % On Unix it is the last time the file
- % or the inode was changed. On Windows,
- % it is the creation time.
- mode :: integer(), % File permissions. On Windows,
- % the owner permissions will be
- % duplicated for group and user.
- links :: non_neg_integer(), % Number of links to the file (1 if the
- % filesystem doesn't support links).
- major_device :: integer(), % Identifies the file system (Unix),
- % or the drive number (A: = 0, B: = 1)
- % (Windows).
- %% The following are Unix specific.
- %% They are set to zero on other operating systems.
- minor_device :: integer(), % Only valid for devices.
- inode :: integer(), % Inode number for file.
- uid :: integer(), % User id for owner.
- gid :: integer()}). % Group id for owner.
+ {size :: non_neg_integer(), % Size of file in bytes.
+ type :: 'device' | 'directory' | 'other' | 'regular' | 'symlink',
+ access :: 'read' | 'write' | 'read_write' | 'none',
+ atime :: file:date_time() | non_neg_integer(),
+ % The local time the file was last read:
+ % {{Year, Mon, Day}, {Hour, Min, Sec}}.
+ % atime, ctime, mtime may also be unix epochs()
+ mtime :: file:date_time() | non_neg_integer(),
+ % The local time the file was last written.
+ ctime :: file:date_time() | non_neg_integer(),
+ % The interpretation of this time field
+ % is dependent on operating system.
+ % On Unix it is the last time the file
+ % or the inode was changed. On Windows,
+ % it is the creation time.
+ mode :: non_neg_integer(), % File permissions. On Windows,
+ % the owner permissions will be
+ % duplicated for group and user.
+ links :: non_neg_integer(),
+ % Number of links to the file (1 if the
+ % filesystem doesn't support links).
+ major_device :: non_neg_integer(),
+ % Identifies the file system (Unix),
+ % or the drive number (A: = 0, B: = 1)
+ % (Windows).
+ %% The following are Unix specific.
+ %% They are set to zero on other operating systems.
+ minor_device :: non_neg_integer(), % Only valid for devices.
+ inode :: non_neg_integer(), % Inode number for file.
+ uid :: non_neg_integer(), % User id for owner.
+ gid :: non_neg_integer()}). % Group id for owner.
-record(file_descriptor,
diff --git a/lib/kernel/internal_doc/distribution_handshake.txt b/lib/kernel/internal_doc/distribution_handshake.txt
index 6a3ee22ed3..d00c4ceb02 100644
--- a/lib/kernel/internal_doc/distribution_handshake.txt
+++ b/lib/kernel/internal_doc/distribution_handshake.txt
@@ -1,215 +1 @@
-HOW THE DISTRIBUTION HANDSHAKE WORKS
-------------------------------------
-
-This document describes the distribution handshake introduced in
-the R6 release of Erlang/OTP.
-
-GENERAL
--------
-
-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 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
-expects the client (called A) to be the first one to prove that it can
-generate a sufficient digest. The digest is generated with the
-MD5 message digest algorithm and the challenges are expected to be very
-random numbers.
-
-DEFINITIONS
------------
-
-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
-with the cookie (as text)]. Below, the function gen_digest(Challenge, Cookie)
-generates a digest as described above.
-
-An out_cookie is the cookie used in outgoing communication to a certain node,
-so that A's out_cookie for B should correspond with B's in_cookie for A and
-the other way around. A's out_cookie for B and A's in_cookie for B need *NOT*
-be the same. Below the function out_cookie(Node) returns the current
-node's out_cookie for Node.
-
-An in_cookie is the cookie expected to be used by another node when
-communicating with us, so that A's in_cookie for B corresponds with B's
-out_cookie for A. Below the function in_cookie(Node) returns the current
-node's in_cookie for Node.
-
-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 packet headers.
-
-THE HANDSHAKE IN DETAIL
------------------------
-
-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" being one byte and the packet header removed):
-
-+---+--------+--------+-----+-----+-----+-----+-----+-----+-...-+-----+
-|'n'|Version0|Version1|Flag0|Flag1|Flag2|Flag3|Name0|Name1| ... |NameN|
-+---+--------+--------+-----+-----+-----+-----+-----+-----+-... +-----+
-
-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 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).
-
-3) recv_status/send_status: B sends a status message to A, which indicates
-if the connection is allowed. Four different status codes are defined:
-ok: The handshake will continue.
-ok_simultaneous: The handshake will continue, but A is informed that B
- has another ongoing connection attempt that will be
- shut down (simultaneous connect where A's name is
- greater than B's name, compared literally),
-nok: The handshake will not continue, as B already has an ongoing handshake
- which it itself has initiated. (simultaneous connect where B's name is
- greater than A's)
-not_allowed: The connection is disallowed for some (unspecified) security
- reason.
-alive: A connection to the node is already active, which either means
- that node A is confused or that the TCP connection breakdown
- of a previous node with this name has not yet reached node B.
- See 3B below.
-
-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)
-
-3B) send_status/recv_status: If status was 'alive', node A will answer with
-another status message containing either 'true' which means that the
-connection should continue (The old connection from this node is broken), or
-'false', which simply means that the connection should be closed, the
-connection attempt was a mistake.
-
-4) recv_challenge/send_challenge: If the status was 'ok' or 'ok_simultaneous',
-The handshake continues with B sending A another message, the challenge.
-The challenge contains the same type of information as the "name" message
-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 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 its own challenge. Those are sent together in a package
-to B:
-
-+---+-----+-----+-----+-----+-----+-----+-----+-----+-...-+------+
-|'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 ... 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| ... |Dige15|
-+---+-----+-----+-----+-----+-...-+------+
-
-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.
-
-SEMIGRAPHIC VIEW
-----------------
-
-A (initiator) B (acceptor)
-
-TCP connect ----------------------------------------->
- TCP accept
-
-send_name ----------------------------------------->
- recv_name
-
- <---------------------------------------- send_status
-recv_status
-(if status was 'alive'
- send_status - - - - - - - - - - - - - - - - - - - ->
- recv_status)
- ChB = gen_challenge()
- (ChB)
- <---------------------------------------- send_challenge
-recv_challenge
-
-ChA = gen_challenge(),
-OCA = out_cookie(B),
-DiA = gen_digest(ChB,OCA)
- (ChA, DiA)
-send_challenge_reply -------------------------------->
- recv_challenge_reply
- ICB = in_cookie(A),
- check:
- DiA == gen_digest
- (ChB, ICB) ?
- - if OK:
- OCB = out_cookie(A),
- DiB = gen_digest
- (DiB) (ChA, OCB)
- <----------------------------------------- send_challenge_ack
-recv_challenge_ack DONE
-ICA = in_cookie(B), - else
-check: CLOSE
-DiB == gen_digest(ChA,ICA) ?
-- if OK
- DONE
-- else
- CLOSE
-
-
-THE CURRENTLY DEFINED FLAGS
----------------------------
-Currently the following capability flags are defined:
-
-%% The node should be published and part of the global namespace
--define(DFLAG_PUBLISHED,1).
-
-%% The node implements an atom cache
--define(DFLAG_ATOM_CACHE,2).
-
-%% The node implements extended (3 * 32 bits) references
--define(DFLAG_EXTENDED_REFERENCES,4).
-
-%% The node implements distributed process monitoring.
--define(DFLAG_DIST_MONITOR,8).
-
-%% 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
-implements DFLAG_EXTENDED_REFERENCES.
-
-Last modified 1999-11-08 -- Patrik Nyblom, OTP
+This information has been moved to the "Distribution Protocol" chapter of "ERTS User's Guide".
diff --git a/lib/kernel/src/Makefile b/lib/kernel/src/Makefile
index a39864d6b7..eaced4861a 100644
--- a/lib/kernel/src/Makefile
+++ b/lib/kernel/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2011. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -107,7 +107,6 @@ MODULES = \
net_adm \
net_kernel \
os \
- packages \
pg2 \
ram_file \
rpc \
@@ -171,13 +170,13 @@ docs:
# ----------------------------------------------------
../../hipe/main/hipe.hrl: ../../hipe/vsn.mk ../../hipe/main/hipe.hrl.src
- sed -e "s;%VSN%;$(HIPE_VSN);" ../../hipe/main/hipe.hrl.src > ../../hipe/main/hipe.hrl
+ $(vsn_verbose)sed -e "s;%VSN%;$(HIPE_VSN);" ../../hipe/main/hipe.hrl.src > ../../hipe/main/hipe.hrl
$(APP_TARGET): $(APP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(KERNEL_VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(KERNEL_VSN);' $< > $@
$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(KERNEL_VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(KERNEL_VSN);' $< > $@
EPMD_FLAGS = -Depmd_port_no=$(EPMD_PORT_NO) \
@@ -187,10 +186,10 @@ EPMD_FLAGS = -Depmd_port_no=$(EPMD_PORT_NO) \
-Derlang_daemon_port=$(EPMD_PORT_NO)
$(ESRC)/inet_dns_record_adts.hrl: $(ESRC)/inet_dns_record_adts.pl
- LANG=C $(PERL) $< > $@
+ $(gen_verbose)LANG=C $(PERL) $< > $@
$(EBIN)/erl_epmd.beam: $(ESRC)/erl_epmd.erl
- $(ERLC) $(ERL_COMPILE_FLAGS) $(EPMD_FLAGS) -o$(EBIN) $<
+ $(V_ERLC) $(ERL_COMPILE_FLAGS) $(EPMD_FLAGS) -o$(EBIN) $<
# ----------------------------------------------------
# Release Target
diff --git a/lib/kernel/src/application.erl b/lib/kernel/src/application.erl
index c299fb085c..4e65883be7 100644
--- a/lib/kernel/src/application.erl
+++ b/lib/kernel/src/application.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -23,7 +23,7 @@
which_applications/0, which_applications/1,
loaded_applications/0, permit/2]).
-export([set_env/3, set_env/4, unset_env/2, unset_env/3]).
--export([get_env/1, get_env/2, get_all_env/0, get_all_env/1]).
+-export([get_env/1, get_env/2, get_env/3, get_all_env/0, get_all_env/1]).
-export([get_key/1, get_key/2, get_all_key/0, get_all_key/1]).
-export([get_application/0, get_application/1, info/0]).
-export([start_type/0]).
@@ -37,8 +37,7 @@
-type application_opt() :: {'description', Description :: string()}
| {'vsn', Vsn :: string()}
| {'id', Id :: string()}
- | {'modules', [(Module :: module()) |
- {Module :: module(), Version :: term()}]}
+ | {'modules', [Module :: module()]}
| {'registered', Names :: [Name :: atom()]}
| {'applications', [Application :: atom()]}
| {'included_applications', [Application :: atom()]}
@@ -265,6 +264,20 @@ get_env(Key) ->
get_env(Application, Key) ->
application_controller:get_env(Application, Key).
+-spec get_env(Application, Par, Def) -> Val when
+ Application :: atom(),
+ Par :: atom(),
+ Def :: term(),
+ Val :: term().
+
+get_env(Application, Key, Def) ->
+ case get_env(Application, Key) of
+ {ok, Val} ->
+ Val;
+ undefined ->
+ Def
+ end.
+
-spec get_all_env() -> Env when
Env :: [{Par :: atom(), Val :: term()}].
diff --git a/lib/kernel/src/application_controller.erl b/lib/kernel/src/application_controller.erl
index ebfe84463a..75ce852001 100644
--- a/lib/kernel/src/application_controller.erl
+++ b/lib/kernel/src/application_controller.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -162,7 +162,7 @@
%% appl_opt() = {description, string()} |
%% {vsn, string()} |
%% {id, string()}, |
-%% {modules, [Module|{Module,Vsn}]} |
+%% {modules, [Module]} |
%% {registered, [atom()]} |
%% {applications, [atom()]} |
%% {included_applications, [atom()]} |
@@ -172,7 +172,6 @@
%% {maxP, integer()|infinity} |
%% {mod, {Module, term()}}
%% Module = atom()
-%% Vsn = term()
%% Purpose: Starts the application_controller. This process starts all
%% application masters for the applications.
%% The kernel application is the only application that is
@@ -446,7 +445,7 @@ get_application_module(Module) ->
get_application_module(Module, AppModules).
get_application_module(Module, [[AppName, Modules]|AppModules]) ->
- case in_modules(Module, Modules) of
+ case lists:member(Module, Modules) of
true ->
{ok, AppName};
false ->
@@ -455,16 +454,6 @@ get_application_module(Module, [[AppName, Modules]|AppModules]) ->
get_application_module(_Module, []) ->
undefined.
-%% 'modules' key in .app is a list of Module or {Module,Vsn}
-in_modules(Module, [Module|_Modules]) ->
- true;
-in_modules(Module, [{Module, _Vsn}|_Modules]) ->
- true;
-in_modules(Module, [_Module|Modules]) ->
- in_modules(Module, Modules);
-in_modules(_Module, []) ->
- false.
-
permit_application(ApplName, Flag) ->
gen_server:call(?AC,
{permit_application, ApplName, Flag},
@@ -1971,5 +1960,5 @@ to_string(Term) ->
true ->
Term;
false ->
- lists:flatten(io_lib:write(Term))
+ lists:flatten(io_lib:format("~134217728p", [Term]))
end.
diff --git a/lib/kernel/src/code.erl b/lib/kernel/src/code.erl
index a9259817ea..361f2bdf8a 100644
--- a/lib/kernel/src/code.erl
+++ b/lib/kernel/src/code.erl
@@ -63,7 +63,7 @@
which/1,
where_is_file/1,
where_is_file/2,
- set_primary_archive/3,
+ set_primary_archive/4,
clash/0]).
-export_type([load_error_rsn/0, load_ret/0]).
@@ -359,7 +359,6 @@ load_code_server_prerequisites() ->
hipe_unified_loader,
lists,
os,
- packages,
unicode],
[M = M:module_info(module) || M <- Needed],
ok.
@@ -413,7 +412,7 @@ which(Module) when is_atom(Module) ->
end.
which2(Module) ->
- Base = to_path(Module),
+ Base = atom_to_list(Module),
File = filename:basename(Base) ++ objfile_extension(),
Path = get_path(),
which(File, filename:dirname(Base), Path).
@@ -474,13 +473,16 @@ where_is_file(Path, File) when is_list(Path), is_list(File) ->
-spec set_primary_archive(ArchiveFile :: file:filename(),
ArchiveBin :: binary(),
- FileInfo :: file:file_info())
+ FileInfo :: file:file_info(),
+ ParserFun :: fun())
-> 'ok' | {'error', atom()}.
-set_primary_archive(ArchiveFile0, ArchiveBin, #file_info{} = FileInfo)
+set_primary_archive(ArchiveFile0, ArchiveBin, #file_info{} = FileInfo,
+ ParserFun)
when is_list(ArchiveFile0), is_binary(ArchiveBin) ->
ArchiveFile = filename:absname(ArchiveFile0),
- case call({set_primary_archive, ArchiveFile, ArchiveBin, FileInfo}) of
+ case call({set_primary_archive, ArchiveFile, ArchiveBin, FileInfo,
+ ParserFun}) of
{ok, []} ->
ok;
{ok, _Mode, Ebins} ->
@@ -544,9 +546,6 @@ has_ext(Ext, Extlen, File) ->
_ -> false
end.
-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),
diff --git a/lib/kernel/src/code_server.erl b/lib/kernel/src/code_server.erl
index a2db7c9790..b2d2c19f78 100644
--- a/lib/kernel/src/code_server.erl
+++ b/lib/kernel/src/code_server.erl
@@ -394,8 +394,8 @@ handle_call(stop,{_From,_Tag}, S) ->
handle_call({is_cached,_File}, {_From,_Tag}, S=#state{cache=no_cache}) ->
{reply, no, S};
-handle_call({set_primary_archive, File, ArchiveBin, FileInfo}, {_From,_Tag}, S=#state{mode=Mode}) ->
- case erl_prim_loader:set_primary_archive(File, ArchiveBin, FileInfo) of
+handle_call({set_primary_archive, File, ArchiveBin, FileInfo, ParserFun}, {_From,_Tag}, S=#state{mode=Mode}) ->
+ case erl_prim_loader:set_primary_archive(File, ArchiveBin, FileInfo, ParserFun) of
{ok, Files} ->
{reply, {ok, Mode, Files}, S};
{error, _Reason} = Error ->
@@ -1229,7 +1229,7 @@ load_abs(File, Mod0, Caller, St) ->
end.
try_load_module(Mod, Dir, Caller, St) ->
- File = filename:append(Dir, to_path(Mod) ++
+ File = filename:append(Dir, to_list(Mod) ++
objfile_extension()),
case erl_prim_loader:get_file(File) of
error ->
@@ -1347,7 +1347,7 @@ load_file_1(Mod, Caller, #state{cache=Cache}=St0) ->
end.
mod_to_bin([Dir|Tail], Mod) ->
- File = filename:append(Dir, to_path(Mod) ++ objfile_extension()),
+ File = filename:append(Dir, to_list(Mod) ++ objfile_extension()),
case erl_prim_loader:get_file(File) of
error ->
mod_to_bin(Tail, Mod);
@@ -1356,7 +1356,7 @@ mod_to_bin([Dir|Tail], Mod) ->
end;
mod_to_bin([], Mod) ->
%% At last, try also erl_prim_loader's own method
- File = to_path(Mod) ++ objfile_extension(),
+ File = to_list(Mod) ++ objfile_extension(),
case erl_prim_loader:get_file(File) of
error ->
error; % No more alternatives !
@@ -1570,6 +1570,3 @@ to_list(X) when is_atom(X) -> atom_to_list(X).
to_atom(X) when is_atom(X) -> X;
to_atom(X) when is_list(X) -> list_to_atom(X).
-
-to_path(X) ->
- filename:join(packages:split(X)).
diff --git a/lib/kernel/src/disk_log.erl b/lib/kernel/src/disk_log.erl
index 5b1efcd395..1513fdaec0 100644
--- a/lib/kernel/src/disk_log.erl
+++ b/lib/kernel/src/disk_log.erl
@@ -44,6 +44,8 @@
%% To be used for debugging only:
-export([pid2name/1]).
+-export_type([continuation/0]).
+
-type dlog_state_error() :: 'ok' | {'error', term()}.
-record(state, {queue = [],
diff --git a/lib/kernel/src/dist_util.erl b/lib/kernel/src/dist_util.erl
index f0d54a2f3e..bbb212cebe 100644
--- a/lib/kernel/src/dist_util.erl
+++ b/lib/kernel/src/dist_util.erl
@@ -115,7 +115,8 @@ make_this_flags(RequestType, OtherNode) ->
?DFLAG_NEW_FLOATS bor
?DFLAG_UNICODE_IO bor
?DFLAG_DIST_HDR_ATOM_CACHE bor
- ?DFLAG_SMALL_ATOM_TAGS).
+ ?DFLAG_SMALL_ATOM_TAGS bor
+ ?DFLAG_UTF8_ATOMS).
handshake_other_started(#hs_data{request_type=ReqType}=HSData0) ->
{PreOtherFlags,Node,Version} = recv_name(HSData0),
@@ -757,7 +758,8 @@ setup_timer(Pid, Timeout) ->
end.
reset_timer(Timer) ->
- Timer ! {self(), reset}.
+ Timer ! {self(), reset},
+ ok.
cancel_timer(Timer) ->
unlink(Timer),
diff --git a/lib/kernel/src/erl_ddll.erl b/lib/kernel/src/erl_ddll.erl
index f967fcc2ef..e03d280cd8 100644
--- a/lib/kernel/src/erl_ddll.erl
+++ b/lib/kernel/src/erl_ddll.erl
@@ -54,9 +54,7 @@ info(_, _) ->
erlang:nif_error(undef).
-spec format_error_int(ErrSpec) -> string() when
- ErrSpec :: inconsisten | linked_in_driver | permanent
- | not_loaded | not_loaded_by_this_process | not_pending
- | already_loaded | unloading.
+ ErrSpec :: term().
format_error_int(_) ->
erlang:nif_error(undef).
diff --git a/lib/kernel/src/error_handler.erl b/lib/kernel/src/error_handler.erl
index f8bc5f499c..a3aa1f1dcf 100644
--- a/lib/kernel/src/error_handler.erl
+++ b/lib/kernel/src/error_handler.erl
@@ -23,10 +23,12 @@
%% "error_handler: add no_native compiler directive"
-compile(no_native).
-%% A simple error handler.
+%% Callbacks called from the run-time system.
+-export([undefined_function/3,undefined_lambda/3,breakpoint/3]).
--export([undefined_function/3, undefined_lambda/3, stub_function/3,
- breakpoint/3]).
+%% Exported utility functions.
+-export([raise_undef_exception/3]).
+-export([stub_function/3]).
-spec undefined_function(Module, Function, Args) ->
any() when
@@ -41,12 +43,7 @@ undefined_function(Module, Func, Args) ->
true ->
apply(Module, Func, Args);
false ->
- case check_inheritance(Module, Args) of
- {value, Base, Args1} ->
- apply(Base, Func, Args1);
- none ->
- crash(Module, Func, Args)
- end
+ call_undefined_function_handler(Module, Func, Args)
end;
{module, _} ->
crash(Module, Func, Args);
@@ -77,6 +74,14 @@ undefined_lambda(Module, Fun, Args) ->
breakpoint(Module, Func, Args) ->
(int()):eval(Module, Func, Args).
+-spec raise_undef_exception(Module, Function, Args) -> no_return() when
+ Module :: atom(),
+ Function :: atom(),
+ Args :: list().
+
+raise_undef_exception(Module, Func, Args) ->
+ crash({Module,Func,Args,[]}).
+
%% Used to make the call to the 'int' module a "weak" one, to avoid
%% building strong components in xref or dialyzer.
@@ -130,27 +135,11 @@ ensure_loaded(Module) ->
stub_function(Mod, Func, Args) ->
exit({undef,[{Mod,Func,Args,[]}]}).
-check_inheritance(Module, Args) ->
- Attrs = erlang:get_module_info(Module, attributes),
- case lists:keyfind(extends, 1, Attrs) of
- {extends, [Base]} when is_atom(Base), Base =/= Module ->
- %% This is just a heuristic for detecting abstract modules
- %% with inheritance so they can be handled; it would be
- %% much better to do it in the emulator runtime
- case lists:keyfind(abstract, 1, Attrs) of
- {abstract, [true]} ->
- case lists:reverse(Args) of
- [M|Rs] when tuple_size(M) > 1,
- element(1,M) =:= Module,
- tuple_size(element(2,M)) > 0,
- is_atom(element(1,element(2,M))) ->
- {value, Base, lists:reverse(Rs, [element(2,M)])};
- _ ->
- {value, Base, Args}
- end;
- _ ->
- {value, Base, Args}
- end;
- _ ->
- none
+call_undefined_function_handler(Module, Func, Args) ->
+ Handler = '$handle_undefined_function',
+ case erlang:function_exported(Module, Handler, 2) of
+ false ->
+ crash(Module, Func, Args);
+ true ->
+ Module:Handler(Func, Args)
end.
diff --git a/lib/kernel/src/file.erl b/lib/kernel/src/file.erl
index 22af38c598..16f2dde464 100644
--- a/lib/kernel/src/file.erl
+++ b/lib/kernel/src/file.erl
@@ -38,7 +38,7 @@
%% Specialized
-export([ipread_s32bu_p32bu/3]).
%% Generic file contents.
--export([open/2, close/1, advise/4,
+-export([open/2, close/1, advise/4, allocate/3,
read/2, write/2,
pread/2, pread/3, pwrite/2, pwrite/3,
read_line/1,
@@ -397,9 +397,10 @@ raw_write_file_info(Name, #file_info{} = Info) ->
%% Contemporary mode specification - list of options
--spec open(Filename, Modes) -> {ok, IoDevice} | {error, Reason} when
+-spec open(File, Modes) -> {ok, IoDevice} | {error, Reason} when
+ File :: Filename | iodata(),
Filename :: name(),
- Modes :: [mode()],
+ Modes :: [mode() | ram],
IoDevice :: io_device(),
Reason :: posix() | badarg | system_limit.
@@ -489,6 +490,18 @@ advise(#file_descriptor{module = Module} = Handle, Offset, Length, Advise) ->
advise(_, _, _, _) ->
{error, badarg}.
+-spec allocate(File, Offset, Length) ->
+ 'ok' | {'error', posix()} when
+ File :: io_device(),
+ Offset :: non_neg_integer(),
+ Length :: non_neg_integer().
+
+allocate(File, Offset, Length) when is_pid(File) ->
+ R = file_request(File, {allocate, Offset, Length}),
+ wait_file_reply(File, R);
+allocate(#file_descriptor{module = Module} = Handle, Offset, Length) ->
+ Module:allocate(Handle, Offset, Length).
+
-spec read(IoDevice, Number) -> {ok, Data} | eof | {error, Reason} when
IoDevice :: io_device() | atom(),
Number :: non_neg_integer(),
@@ -1314,6 +1327,7 @@ sendfile_send(Sock, Data, Old) ->
%%% Helpers
consult_stream(Fd) ->
+ _ = epp:set_encoding(Fd),
consult_stream(Fd, 1, []).
consult_stream(Fd, Line, Acc) ->
@@ -1327,6 +1341,7 @@ consult_stream(Fd, Line, Acc) ->
end.
eval_stream(Fd, Handling, Bs) ->
+ _ = epp:set_encoding(Fd),
eval_stream(Fd, Handling, 1, undefined, [], Bs).
eval_stream(Fd, H, Line, Last, E, Bs) ->
diff --git a/lib/kernel/src/file_io_server.erl b/lib/kernel/src/file_io_server.erl
index 0bff56cf46..fad2ed7fb3 100644
--- a/lib/kernel/src/file_io_server.erl
+++ b/lib/kernel/src/file_io_server.erl
@@ -40,6 +40,8 @@ format_error({_Line, ?MODULE, Reason}) ->
io_lib:format("~w", [Reason]);
format_error({_Line, Mod, Reason}) ->
Mod:format_error(Reason);
+format_error(invalid_unicode) ->
+ io_lib:format("cannot translate from UTF-8", []);
format_error(ErrorId) ->
erl_posix_msg:message(ErrorId).
@@ -209,6 +211,10 @@ file_request({advise,Offset,Length,Advise},
Reply ->
{reply,Reply,State}
end;
+file_request({allocate, Offset, Length},
+ #state{handle = Handle} = State) ->
+ Reply = ?PRIM_FILE:allocate(Handle, Offset, Length),
+ {reply, Reply, State};
file_request({pread,At,Sz},
#state{handle=Handle,buf=Buf,read_mode=ReadMode}=State) ->
case position(Handle, At, Buf) of
@@ -549,7 +555,7 @@ get_chars_notempty(Mod, Func, XtraArg, S, OutEnc,
<<>> ->
get_chars_apply(Mod, Func, XtraArg, S, OutEnc, State, eof);
_ ->
- {stop,invalid_unicode,{error,invalid_unicode},State}
+ {stop,invalid_unicode,invalid_unicode_error(Mod, Func, XtraArg, S),State}
end;
{error,Reason}=Error ->
{stop,Reason,Error,State}
@@ -616,12 +622,22 @@ get_chars_apply(Mod, Func, XtraArg, S0, OutEnc,
end
catch
exit:ExReason ->
- {stop,ExReason,{error,err_func(Mod, Func, XtraArg)},State};
+ {stop,ExReason,invalid_unicode_error(Mod, Func, XtraArg, S0),State};
error:ErrReason ->
{stop,ErrReason,{error,err_func(Mod, Func, XtraArg)},State}
end.
-
+%% A hack that tries to inform the caller about the position where the
+%% error occured.
+invalid_unicode_error(Mod, Func, XtraArg, S) ->
+ try
+ {erl_scan,tokens,_Args} = XtraArg,
+ Location = erl_scan:continuation_location(S),
+ {error,{Location, ?MODULE, invalid_unicode},Location}
+ catch
+ _:_ ->
+ {error,err_func(Mod, Func, XtraArg)}
+ end.
%% Convert error code to make it look as before
err_func(io_lib, get_until, {_,F,_}) ->
diff --git a/lib/kernel/src/gen_sctp.erl b/lib/kernel/src/gen_sctp.erl
index 8fa963ec78..74ad192802 100644
--- a/lib/kernel/src/gen_sctp.erl
+++ b/lib/kernel/src/gen_sctp.erl
@@ -44,6 +44,7 @@
{priority, non_neg_integer()} |
{recbuf, non_neg_integer()} |
{reuseaddr, boolean()} |
+ {ipv6_v6only, boolean()} |
{sctp_adaptation_layer, #sctp_setadaptation{}} |
{sctp_associnfo, #sctp_assocparams{}} |
{sctp_autoclose, non_neg_integer()} |
@@ -72,6 +73,7 @@
priority |
recbuf |
reuseaddr |
+ ipv6_v6only |
sctp_adaptation_layer |
sctp_associnfo |
sctp_autoclose |
diff --git a/lib/kernel/src/gen_tcp.erl b/lib/kernel/src/gen_tcp.erl
index e6dfdadb03..ec13ab6d2e 100644
--- a/lib/kernel/src/gen_tcp.erl
+++ b/lib/kernel/src/gen_tcp.erl
@@ -37,9 +37,11 @@
{dontroute, boolean()} |
{exit_on_close, boolean()} |
{header, non_neg_integer()} |
+ {high_msgq_watermark, pos_integer()} |
{high_watermark, non_neg_integer()} |
{keepalive, boolean()} |
{linger, {boolean(), non_neg_integer()}} |
+ {low_msgq_watermark, pos_integer()} |
{low_watermark, non_neg_integer()} |
{mode, list | binary} | list | binary |
{nodelay, boolean()} |
@@ -57,7 +59,8 @@
{send_timeout, non_neg_integer() | infinity} |
{send_timeout_close, boolean()} |
{sndbuf, non_neg_integer()} |
- {tos, non_neg_integer()}.
+ {tos, non_neg_integer()} |
+ {ipv6_v6only, boolean()}.
-type option_name() ::
active |
buffer |
@@ -66,9 +69,11 @@
dontroute |
exit_on_close |
header |
+ high_msgq_watermark |
high_watermark |
keepalive |
linger |
+ low_msgq_watermark |
low_watermark |
mode |
nodelay |
@@ -85,7 +90,8 @@
send_timeout |
send_timeout_close |
sndbuf |
- tos.
+ tos |
+ ipv6_v6only.
-type connect_option() ::
{ip, inet:ip_address()} |
{fd, Fd :: non_neg_integer()} |
diff --git a/lib/kernel/src/gen_udp.erl b/lib/kernel/src/gen_udp.erl
index 914854c65c..c5a1173575 100644
--- a/lib/kernel/src/gen_udp.erl
+++ b/lib/kernel/src/gen_udp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -47,7 +47,8 @@
{recbuf, non_neg_integer()} |
{reuseaddr, boolean()} |
{sndbuf, non_neg_integer()} |
- {tos, non_neg_integer()}.
+ {tos, non_neg_integer()} |
+ {ipv6_v6only, boolean()}.
-type option_name() ::
active |
broadcast |
@@ -69,7 +70,8 @@
recbuf |
reuseaddr |
sndbuf |
- tos.
+ tos |
+ ipv6_v6only.
-type socket() :: port().
-export_type([option/0, option_name/0]).
diff --git a/lib/kernel/src/global.erl b/lib/kernel/src/global.erl
index 36cb713ee1..b24a9d5eac 100644
--- a/lib/kernel/src/global.erl
+++ b/lib/kernel/src/global.erl
@@ -232,7 +232,8 @@ register_name(Name, Pid) when is_pid(Pid) ->
Name :: term(),
Pid :: pid(),
Resolve :: method().
-register_name(Name, Pid, Method) when is_pid(Pid) ->
+register_name(Name, Pid, Method0) when is_pid(Pid) ->
+ Method = allow_tuple_fun(Method0),
Fun = fun(Nodes) ->
case (where(Name) =:= undefined) andalso check_dupname(Name, Pid) of
true ->
@@ -290,7 +291,8 @@ re_register_name(Name, Pid) when is_pid(Pid) ->
Name :: term(),
Pid :: pid(),
Resolve :: method().
-re_register_name(Name, Pid, Method) when is_pid(Pid) ->
+re_register_name(Name, Pid, Method0) when is_pid(Pid) ->
+ Method = allow_tuple_fun(Method0),
Fun = fun(Nodes) ->
gen_server:multi_call(Nodes,
global_name_server,
@@ -2218,3 +2220,9 @@ intersection(_, []) ->
[];
intersection(L1, L2) ->
L1 -- (L1 -- L2).
+
+%% Support legacy tuple funs as resolve functions.
+allow_tuple_fun({M, F}) when is_atom(M), is_atom(F) ->
+ fun M:F/3;
+allow_tuple_fun(Fun) when is_function(Fun, 3) ->
+ Fun.
diff --git a/lib/kernel/src/group.erl b/lib/kernel/src/group.erl
index f92c6f7208..c66e823a04 100644
--- a/lib/kernel/src/group.erl
+++ b/lib/kernel/src/group.erl
@@ -424,7 +424,7 @@ get_password_chars(Drv,Buf) ->
end.
get_chars(Prompt, M, F, Xa, Drv, Buf, Encoding) ->
- Pbs = prompt_bytes(Prompt),
+ Pbs = prompt_bytes(Prompt, Encoding),
get_chars_loop(Pbs, M, F, Xa, Drv, Buf, start, Encoding).
get_chars_loop(Pbs, M, F, Xa, Drv, Buf0, State, Encoding) ->
@@ -515,6 +515,27 @@ get_line1({undefined,{_A,Mode,Char},Cs,Cont,Rs}, Drv, Ls0, Encoding)
Drv,
Ls, Encoding)
end;
+%% ^R = backward search, ^S = forward search.
+%% Search is tricky to implement and does a lot of back-and-forth
+%% work with edlin.erl (from stdlib). Edlin takes care of writing
+%% and handling lines and escape characters to get out of search,
+%% whereas this module does the actual searching and appending to lines.
+%% Erlang's shell wasn't exactly meant to traverse the wall between
+%% line and line stack, so we at least restrict it by introducing
+%% new modes: search, search_quit, search_found. These are added to
+%% the regular ones (none, meta_left_sq_bracket) and handle special
+%% cases of history search.
+get_line1({undefined,{_A,Mode,Char},Cs,Cont,Rs}, Drv, Ls, Encoding)
+ when ((Mode =:= none) and (Char =:= $\^R)) ->
+ send_drv_reqs(Drv, Rs),
+ %% drop current line, move to search mode. We store the current
+ %% prompt ('N>') and substitute it with the search prompt.
+ send_drv_reqs(Drv, edlin:erase_line(Cont)),
+ put(search_quit_prompt, edlin:prompt(Cont)),
+ Pbs = prompt_bytes("(search)`': ", Encoding),
+ {more_chars,Ncont,Nrs} = edlin:start(Pbs, search),
+ send_drv_reqs(Drv, Nrs),
+ get_line1(edlin:edit_line1(Cs, Ncont), Drv, Ls, Encoding);
get_line1({expand, Before, Cs0, Cont,Rs}, Drv, Ls0, Encoding) ->
send_drv_reqs(Drv, Rs),
ExpandFun = get(expand_fun),
@@ -535,8 +556,59 @@ get_line1({undefined,_Char,Cs,Cont,Rs}, Drv, Ls, Encoding) ->
send_drv_reqs(Drv, Rs),
send_drv(Drv, beep),
get_line1(edlin:edit_line(Cs, Cont), Drv, Ls, Encoding);
+%% The search item was found and accepted (new line entered on the exact
+%% result found)
+get_line1({_What,Cont={line,_Prompt,_Chars,search_found},Rs}, Drv, Ls0, Encoding) ->
+ Line = edlin:current_line(Cont),
+ %% this may create duplicate entries.
+ Ls = save_line(new_stack(get_lines(Ls0)), Line),
+ get_line1({done, Line, "", Rs}, Drv, Ls, Encoding);
+%% The search mode has been exited, but the user wants to remain in line
+%% editing mode wherever that was, but editing the search result.
+get_line1({What,Cont={line,_Prompt,_Chars,search_quit},Rs}, Drv, Ls, Encoding) ->
+ Line = edlin:current_chars(Cont),
+ %% Load back the old prompt with the correct line number.
+ case get(search_quit_prompt) of
+ undefined -> % should not happen. Fallback.
+ LsFallback = save_line(new_stack(get_lines(Ls)), Line),
+ get_line1({done, "\n", Line, Rs}, Drv, LsFallback, Encoding);
+ Prompt -> % redraw the line and keep going with the same stack position
+ NCont = {line,Prompt,{lists:reverse(Line),[]},none},
+ send_drv_reqs(Drv, Rs),
+ send_drv_reqs(Drv, edlin:erase_line(Cont)),
+ send_drv_reqs(Drv, edlin:redraw_line(NCont)),
+ get_line1({What, NCont ,[]}, Drv, pad_stack(Ls), Encoding)
+ end;
+%% Search mode is entered.
+get_line1({What,{line,Prompt,{RevCmd0,_Aft},search},Rs},
+ Drv, Ls0, Encoding) ->
+ send_drv_reqs(Drv, Rs),
+ %% Figure out search direction. ^S and ^R are returned through edlin
+ %% whenever we received a search while being already in search mode.
+ {Search, Ls1, RevCmd} = case RevCmd0 of
+ [$\^S|RevCmd1] ->
+ {fun search_down_stack/2, Ls0, RevCmd1};
+ [$\^R|RevCmd1] ->
+ {fun search_up_stack/2, Ls0, RevCmd1};
+ _ -> % new search, rewind stack for a proper search.
+ {fun search_up_stack/2, new_stack(get_lines(Ls0)), RevCmd0}
+ end,
+ Cmd = lists:reverse(RevCmd),
+ {Ls, NewStack} = case Search(Ls1, Cmd) of
+ {none, Ls2} ->
+ send_drv(Drv, beep),
+ {Ls2, {RevCmd, "': "}};
+ {Line, Ls2} -> % found. Complete the output edlin couldn't have done.
+ send_drv_reqs(Drv, [{put_chars, Encoding, Line}]),
+ {Ls2, {RevCmd, "': "++Line}}
+ end,
+ Cont = {line,Prompt,NewStack,search},
+ more_data(What, Cont, Drv, Ls, Encoding);
get_line1({What,Cont0,Rs}, Drv, Ls, Encoding) ->
send_drv_reqs(Drv, Rs),
+ more_data(What, Cont0, Drv, Ls, Encoding).
+
+more_data(What, Cont0, Drv, Ls, Encoding) ->
receive
{Drv,{data,Cs}} ->
get_line1(edlin:edit_line(Cs, Cont0), Drv, Ls, Encoding);
@@ -557,7 +629,6 @@ get_line1({What,Cont0,Rs}, Drv, Ls, Encoding) ->
get_line1(edlin:edit_line([], Cont0), Drv, Ls, Encoding)
end.
-
get_line_echo_off(Chars, Pbs, Drv) ->
send_drv_reqs(Drv, [{put_chars, unicode,Pbs}]),
get_line_echo_off1(edit_line(Chars,[]), Drv).
@@ -632,12 +703,46 @@ save_line({stack, U, {}, []}, Line) ->
save_line({stack, U, _L, D}, Line) ->
{stack, U, Line, D}.
-get_lines({stack, U, {}, []}) ->
+get_lines(Ls) -> get_all_lines(Ls).
+%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]}).
+
+%% There's a funny behaviour whenever the line stack doesn't have a "\n"
+%% at its end -- get_lines() seemed to work on the assumption it *will* be
+%% there, but the manipulations done with search history do not require it.
+%%
+%% It is an assumption because the function was built with either the full
+%% stack being on the 'Up' side (we're on the new line) where it isn't
+%% stripped. The only other case when it isn't on the 'Up' side is when
+%% someone has used the up/down arrows (or ^P and ^N) to navigate lines,
+%% in which case, a line with only a \n is stored at the end of the stack
+%% (the \n is returned by edlin:current_line/1).
+%%
+%% get_all_lines works the same as get_lines, but only strips the trailing
+%% character if it's a linebreak. Otherwise it's kept the same. This is
+%% because traversing the stack due to search history will *not* insert
+%% said empty line in the stack at the same time as other commands do,
+%% and thus it should not always be stripped unless we know a new line
+%% is the last entry.
+get_all_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]}).
+get_all_lines({stack, U, {}, D}) ->
+ case lists:reverse(D, U) of
+ ["\n"|Lines] -> Lines;
+ Lines -> Lines
+ end;
+get_all_lines({stack, U, L, D}) ->
+ get_all_lines({stack, U, {}, [L|D]}).
+
+%% For the same reason as above, though, we need to expand the stack
+%% in some cases to make sure we play nice with up/down arrows. We need
+%% to insert newlines, but not always.
+pad_stack({stack, U, L, D}) ->
+ {stack, U, L, D++["\n"]}.
save_line_buffer("\n", Lines) ->
save_line_buffer(Lines);
@@ -649,6 +754,27 @@ save_line_buffer(Line, Lines) ->
save_line_buffer(Lines) ->
put(line_buffer, Lines).
+search_up_stack(Stack, Substr) ->
+ case up_stack(Stack) of
+ {none,NewStack} -> {none,NewStack};
+ {L, NewStack} ->
+ case string:str(L, Substr) of
+ 0 -> search_up_stack(NewStack, Substr);
+ _ -> {string:strip(L,right,$\n), NewStack}
+ end
+ end.
+
+search_down_stack(Stack, Substr) ->
+ case down_stack(Stack) of
+ {none,NewStack} -> {none,NewStack};
+ {L, NewStack} ->
+ case string:str(L, Substr) of
+ 0 -> search_down_stack(NewStack, Substr);
+ _ -> {string:strip(L,right,$\n), NewStack}
+ end
+ end.
+
+
%% This is get_line without line editing (except for backspace) and
%% without echo.
get_password_line(Chars, Drv) ->
@@ -687,10 +813,10 @@ edit_password([$\177|Cs],[_|Chars]) ->%% is backspace enough?
edit_password([Char|Cs],Chars) ->
edit_password(Cs,[Char|Chars]).
-%% prompt_bytes(Prompt)
-%% Return a flat list of bytes for the Prompt.
-prompt_bytes(Prompt) ->
- lists:flatten(io_lib:format_prompt(Prompt)).
+%% prompt_bytes(Prompt, Encoding)
+%% Return a flat list of characters for the Prompt.
+prompt_bytes(Prompt, Encoding) ->
+ lists:flatten(io_lib:format_prompt(Prompt, Encoding)).
cast(L, binary,latin1) when is_list(L) ->
list_to_binary(L);
diff --git a/lib/kernel/src/heart.erl b/lib/kernel/src/heart.erl
index 255ae4e51b..87cb9d7f51 100644
--- a/lib/kernel/src/heart.erl
+++ b/lib/kernel/src/heart.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,6 +18,10 @@
%%
-module(heart).
+-compile(no_native).
+% 'no_native' as part of a crude fix to make init:restart/0 work by clearing
+% all hipe inter-module information (hipe_mfa_info's in hipe_bif0.c).
+
%%%--------------------------------------------------------------------
%%% This is a rewrite of pre_heart from BS.3.
%%%
@@ -38,9 +42,11 @@
-define(CLEAR_CMD, 5).
-define(GET_CMD, 6).
-define(HEART_CMD, 7).
+-define(PREPARING_CRASH, 8). % Used in beam vm
-define(TIMEOUT, 5000).
-define(CYCLE_TIMEOUT, 10000).
+-define(HEART_PORT_NAME, heart_port).
%%---------------------------------------------------------------------
@@ -126,6 +132,8 @@ start_portprogram() ->
Port when is_port(Port) ->
case wait_ack(Port) of
ok ->
+ %% register port so the vm can find it if need be
+ register(?HEART_PORT_NAME, Port),
{ok, Port};
{error, Reason} ->
report_problem({{port_problem, Reason},
@@ -221,6 +229,7 @@ no_reboot_shutdown(Port) ->
end.
do_cycle_port_program(Caller, Parent, Port, Cmd) ->
+ unregister(?HEART_PORT_NAME),
case catch start_portprogram() of
{ok, NewPort} ->
send_shutdown(Port),
diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl
index 41460c948b..9670271b2e 100644
--- a/lib/kernel/src/inet.erl
+++ b/lib/kernel/src/inet.erl
@@ -30,7 +30,9 @@
ifget/3, ifget/2, ifset/3, ifset/2,
getstat/1, getstat/2,
ip/1, stats/0, options/0,
- pushf/3, popf/1, close/1, gethostname/0, gethostname/1]).
+ pushf/3, popf/1, close/1, gethostname/0, gethostname/1,
+ parse_ipv4_address/1, parse_ipv6_address/1, parse_ipv4strict_address/1,
+ parse_ipv6strict_address/1, parse_address/1, parse_strict_address/1]).
-export([connect_options/2, listen_options/2, udp_options/2, sctp_options/2]).
@@ -527,14 +529,57 @@ getservbyname(Name, Protocol) when is_atom(Name) ->
Error -> Error
end.
+-spec parse_ipv4_address(Address) ->
+ {ok, IPv4Address} | {error, einval} when
+ Address :: string(),
+ IPv4Address :: ip_address().
+parse_ipv4_address(Addr) ->
+ inet_parse:ipv4_address(Addr).
+
+-spec parse_ipv6_address(Address) ->
+ {ok, IPv6Address} | {error, einval} when
+ Address :: string(),
+ IPv6Address :: ip_address().
+parse_ipv6_address(Addr) ->
+ inet_parse:ipv6_address(Addr).
+
+-spec parse_ipv4strict_address(Address) ->
+ {ok, IPv4Address} | {error, einval} when
+ Address :: string(),
+ IPv4Address :: ip_address().
+parse_ipv4strict_address(Addr) ->
+ inet_parse:ipv4strict_address(Addr).
+
+-spec parse_ipv6strict_address(Address) ->
+ {ok, IPv6Address} | {error, einval} when
+ Address :: string(),
+ IPv6Address :: ip_address().
+parse_ipv6strict_address(Addr) ->
+ inet_parse:ipv6strict_address(Addr).
+
+-spec parse_address(Address) ->
+ {ok, IPAddress} | {error, einval} when
+ Address :: string(),
+ IPAddress :: ip_address().
+parse_address(Addr) ->
+ inet_parse:address(Addr).
+
+-spec parse_strict_address(Address) ->
+ {ok, IPAddress} | {error, einval} when
+ Address :: string(),
+ IPAddress :: ip_address().
+parse_strict_address(Addr) ->
+ inet_parse:strict_address(Addr).
+
%% Return a list of available options
options() ->
[
tos, priority, reuseaddr, keepalive, dontroute, linger,
- broadcast, sndbuf, recbuf, nodelay,
+ broadcast, sndbuf, recbuf, nodelay, ipv6_v6only,
buffer, header, active, packet, deliver, mode,
multicast_if, multicast_ttl, multicast_loop,
exit_on_close, high_watermark, low_watermark,
+ high_msgq_watermark, low_msgq_watermark,
send_timeout, send_timeout_close
].
@@ -552,8 +597,8 @@ stats() ->
connect_options() ->
[tos, priority, reuseaddr, keepalive, linger, sndbuf, recbuf, nodelay,
header, active, packet, packet_size, buffer, mode, deliver,
- exit_on_close, high_watermark, low_watermark, send_timeout,
- send_timeout_close, delay_send,raw].
+ exit_on_close, high_watermark, low_watermark, high_msgq_watermark,
+ low_msgq_watermark, send_timeout, send_timeout_close, delay_send, raw].
connect_options(Opts, Family) ->
BaseOpts =
@@ -607,9 +652,10 @@ con_add(Name, Val, R, Opts, AllOpts) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
listen_options() ->
[tos, priority, reuseaddr, keepalive, linger, sndbuf, recbuf, nodelay,
- header, active, packet, buffer, mode, deliver, backlog,
- exit_on_close, high_watermark, low_watermark, send_timeout,
- send_timeout_close, delay_send, packet_size,raw].
+ header, active, packet, buffer, mode, deliver, backlog, ipv6_v6only,
+ exit_on_close, high_watermark, low_watermark, high_msgq_watermark,
+ low_msgq_watermark, send_timeout, send_timeout_close, delay_send,
+ packet_size, raw].
listen_options(Opts, Family) ->
BaseOpts =
@@ -664,7 +710,7 @@ list_add(Name, Val, R, Opts, As) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
udp_options() ->
[tos, priority, reuseaddr, sndbuf, recbuf, header, active, buffer, mode,
- deliver,
+ deliver, ipv6_v6only,
broadcast, dontroute, multicast_if, multicast_ttl, multicast_loop,
add_membership, drop_membership, read_packets,raw].
@@ -720,7 +766,7 @@ udp_add(Name, Val, R, Opts, As) ->
sctp_options() ->
[ % The following are generic inet options supported for SCTP sockets:
mode, active, buffer, tos, priority, dontroute, reuseaddr, linger, sndbuf,
- recbuf,
+ recbuf, ipv6_v6only,
% Other options are SCTP-specific (though they may be similar to their
% TCP and UDP counter-parts):
@@ -763,8 +809,12 @@ sctp_opt([Opt|Opts], Mod, R, As) ->
{Name,Val} -> sctp_opt (Opts, Mod, R, As, Name, Val);
_ -> {error,badarg}
end;
-sctp_opt([], _Mod, R, _SockOpts) ->
- {ok, R}.
+sctp_opt([], _Mod, #sctp_opts{ifaddr=IfAddr}=R, _SockOpts) ->
+ if is_list(IfAddr) ->
+ {ok, R#sctp_opts{ifaddr=lists:reverse(IfAddr)}};
+ true ->
+ {ok, R}
+ end.
sctp_opt(Opts, Mod, R, As, Name, Val) ->
case add_opt(Name, Val, R#sctp_opts.opts, As) of
@@ -1015,11 +1065,7 @@ open(Fd, Addr, Port, Opts, Protocol, Family, Type, Module) when Fd < 0 ->
case prim_inet:setopts(S, Opts) of
ok ->
case if is_list(Addr) ->
- prim_inet:bind(S, add,
- [case A of
- {_,_} -> A;
- _ -> {A,Port}
- end || A <- Addr]);
+ bindx(S, Addr, Port);
true ->
prim_inet:bind(S, Addr, Port)
end of
@@ -1040,6 +1086,34 @@ open(Fd, Addr, Port, Opts, Protocol, Family, Type, Module) when Fd < 0 ->
open(Fd, _Addr, _Port, Opts, Protocol, Family, Type, Module) ->
fdopen(Fd, Opts, Protocol, Family, Type, Module).
+bindx(S, [Addr], Port0) ->
+ {IP, Port} = set_bindx_port(Addr, Port0),
+ prim_inet:bind(S, IP, Port);
+bindx(S, Addrs, Port0) ->
+ [{IP, Port} | Rest] = [set_bindx_port(Addr, Port0) || Addr <- Addrs],
+ case prim_inet:bind(S, IP, Port) of
+ {ok, AssignedPort} when Port =:= 0 ->
+ %% On newer Linux kernels, Solaris and FreeBSD, calling
+ %% bindx with port 0 is ok, but on SuSE 10, it results in einval
+ Rest2 = [change_bindx_0_port(Addr, AssignedPort) || Addr <- Rest],
+ prim_inet:bind(S, add, Rest2);
+ {ok, _} ->
+ prim_inet:bind(S, add, Rest);
+ Error ->
+ Error
+ end.
+
+set_bindx_port({_IP, _Port}=Addr, _OtherPort) ->
+ Addr;
+set_bindx_port(IP, Port) ->
+ {IP, Port}.
+
+change_bindx_0_port({IP, 0}, AssignedPort) ->
+ {IP, AssignedPort};
+change_bindx_0_port({_IP, _Port}=Addr, _AssignedPort) ->
+ Addr.
+
+
-spec fdopen(Fd :: non_neg_integer(),
Opts :: [socket_setopt()],
Protocol :: socket_protocol(),
diff --git a/lib/kernel/src/inet_config.erl b/lib/kernel/src/inet_config.erl
index 1ddbdcec25..526baca335 100644
--- a/lib/kernel/src/inet_config.erl
+++ b/lib/kernel/src/inet_config.erl
@@ -197,16 +197,6 @@ do_load_resolv({win32,Type}, longnames) ->
win32_load_from_registry(Type),
inet_db:set_lookup([native]);
-do_load_resolv(vxworks, _) ->
- vxworks_load_hosts(),
- inet_db:set_lookup([file, dns]),
- case os:getenv("ERLRESCONF") of
- false ->
- no_ERLRESCONF;
- Resolv ->
- load_resolv(Resolv, resolv)
- end;
-
do_load_resolv(_, _) ->
inet_db:set_lookup([native]).
@@ -408,55 +398,6 @@ win32_get_strings(Reg, [Name|Rest], Result) ->
win32_get_strings(_, [], Result) ->
lists:reverse(Result).
-%%
-%% Load host data from VxWorks hostShow command
-%%
-
-vxworks_load_hosts() ->
- HostShow = os:cmd("hostShow"),
- case check_hostShow(HostShow) of
- Hosts when is_list(Hosts) ->
- case inet_parse:hosts_vxworks({chars, Hosts}) of
- {ok, Ls} ->
- foreach(
- fun({IP, Name, Aliases}) ->
- inet_db:add_host(IP, [Name|Aliases])
- end,
- Ls);
- {error,Reason} ->
- error("parser error VxWorks hostShow ~s", [Reason])
- end;
- _Error ->
- error("error in VxWorks hostShow~s~n", [HostShow])
- end.
-
-%%
-%% Check if hostShow yields at least two line; the first one
-%% starting with "hostname", the second one starting with
-%% "--------".
-%% Returns: list of hosts in VxWorks notation
-%% rows of 'Name IP [Aliases] \n'
-%% if hostShow yielded these two lines, false otherwise.
-check_hostShow(HostShow) ->
- check_hostShow(["hostname", "--------"], HostShow).
-
-check_hostShow([], HostShow) ->
- HostShow;
-check_hostShow([String_match|Rest], HostShow) ->
- case lists:prefix(String_match, HostShow) of
- true ->
- check_hostShow(Rest, next_line(HostShow));
- false ->
- false
- end.
-
-next_line([]) ->
- [];
-next_line([$\n|Rest]) ->
- Rest;
-next_line([_First|Rest]) ->
- next_line(Rest).
-
read_rc() ->
{RcFile,CfgList} = read_inetrc(),
case extract_cfg_files(CfgList, [], []) of
diff --git a/lib/kernel/src/inet_int.hrl b/lib/kernel/src/inet_int.hrl
index 467c4d5065..000119bc74 100644
--- a/lib/kernel/src/inet_int.hrl
+++ b/lib/kernel/src/inet_int.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -124,6 +124,7 @@
-define(UDP_OPT_MULTICAST_LOOP, 13).
-define(UDP_OPT_ADD_MEMBERSHIP, 14).
-define(UDP_OPT_DROP_MEMBERSHIP, 15).
+-define(INET_OPT_IPV6_V6ONLY, 16).
% "Local" options: codes start from 20:
-define(INET_LOPT_BUFFER, 20).
-define(INET_LOPT_HEADER, 21).
@@ -140,6 +141,8 @@
-define(INET_LOPT_READ_PACKETS, 33).
-define(INET_OPT_RAW, 34).
-define(INET_LOPT_TCP_SEND_TIMEOUT_CLOSE, 35).
+-define(INET_LOPT_TCP_MSGQ_HIWTRMRK, 36).
+-define(INET_LOPT_TCP_MSGQ_LOWTRMRK, 37).
% Specific SCTP options: separate range:
-define(SCTP_OPT_RTOINFO, 100).
-define(SCTP_OPT_ASSOCINFO, 101).
diff --git a/lib/kernel/src/inet_parse.erl b/lib/kernel/src/inet_parse.erl
index 65edddcb46..3551e701b6 100644
--- a/lib/kernel/src/inet_parse.erl
+++ b/lib/kernel/src/inet_parse.erl
@@ -23,7 +23,6 @@
%% 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]).
-export([netmasks/1, netmasks/2]).
-export([networks/1, networks/2]).
@@ -37,7 +36,7 @@
-export([ipv4_address/1, ipv6_address/1]).
-export([ipv4strict_address/1, ipv6strict_address/1]).
--export([address/1]).
+-export([address/1, strict_address/1]).
-export([visible_string/1, domain/1]).
-export([ntoa/1, dots/1]).
-export([split_line/1]).
@@ -107,18 +106,6 @@ hosts(Fname,File) ->
parse_file(Fname, File, Fn).
%% --------------------------------------------------------------------------
-%% Parse hostShow vxworks style
-%% Syntax:
-%% Name IP [Aliases] \n
-%% --------------------------------------------------------------------------
-hosts_vxworks(Hosts) ->
- Fn = fun([Name, Address | Aliases]) ->
- {ok,IP} = address(Address),
- {IP, Name, Aliases}
- end,
- parse_file(Hosts, Fn).
-
-%% --------------------------------------------------------------------------
%% Parse resolv file unix style
%% Syntax:
%% domain Domain \n
@@ -291,9 +278,6 @@ networks(Fname, File) ->
%%
%% --------------------------------------------------------------------------
-parse_file(File, Fn) ->
- parse_file(noname, File, Fn).
-
parse_file(Fname, {fd,Fd}, Fn) ->
parse_fd(Fname,Fd, 1, Fn, []);
parse_file(Fname, {chars,Cs}, Fn) when is_list(Cs) ->
@@ -472,6 +456,17 @@ address(Cs) when is_list(Cs) ->
address(_) ->
{error, einval}.
+%%Parse ipv4 strict address or ipv6 strict address
+strict_address(Cs) when is_list(Cs) ->
+ case ipv4strict_address(Cs) of
+ {ok,IP} ->
+ {ok,IP};
+ _ ->
+ ipv6strict_address(Cs)
+ end;
+strict_address(Cs) ->
+ {error, einval}.
+
%%
%% Parse IPv4 address:
%% d1.d2.d3.d4
diff --git a/lib/kernel/src/kernel.app.src b/lib/kernel/src/kernel.app.src
index 17ab84c177..9a20baf8d0 100644
--- a/lib/kernel/src/kernel.app.src
+++ b/lib/kernel/src/kernel.app.src
@@ -28,7 +28,6 @@
application_starter,
auth,
code,
- packages,
code_server,
dist_util,
erl_boot_server,
diff --git a/lib/kernel/src/os.erl b/lib/kernel/src/os.erl
index b986f3a61e..e20a2434b4 100644
--- a/lib/kernel/src/os.erl
+++ b/lib/kernel/src/os.erl
@@ -60,16 +60,12 @@ timestamp() ->
%%% End of BIFs
--spec type() -> vxworks | {Osfamily, Osname} when
+-spec type() -> {Osfamily, Osname} when
Osfamily :: unix | win32,
Osname :: atom().
type() ->
- case erlang:system_info(os_type) of
- {vxworks, _} ->
- vxworks;
- Else -> Else
- end.
+ erlang:system_info(os_type).
-spec version() -> VersionString | {Major, Minor, Release} when
VersionString :: string(),
@@ -119,25 +115,14 @@ find_executable1(_Name, [], _Extensions) ->
verify_executable(Name0, [Ext|Rest], OrigExtensions) ->
Name1 = Name0 ++ Ext,
- case os:type() of
- vxworks ->
- %% We consider all existing VxWorks files to be executable
- case file:read_file_info(Name1) of
- {ok, _} ->
- {ok, Name1};
- _ ->
- verify_executable(Name0, Rest, OrigExtensions)
- end;
+ case file:read_file_info(Name1) of
+ {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};
_ ->
- case file:read_file_info(Name1) of
- {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, OrigExtensions)
- end
+ verify_executable(Name0, Rest, OrigExtensions)
end;
verify_executable(Name, [], OrigExtensions) when OrigExtensions =/= [""] -> %% Windows
%% Will only happen on windows, hence case insensitivity
@@ -190,8 +175,7 @@ reverse_element(List) ->
extensions() ->
case type() of
{win32, _} -> [".exe",".com",".cmd",".bat"];
- {unix, _} -> [""];
- vxworks -> [""]
+ {unix, _} -> [""]
end.
%% Executes the given command in the default shell for the operating system.
@@ -209,11 +193,6 @@ cmd(Cmd) ->
{Cspec,_} -> lists:concat([Cspec," /c",Cmd])
end,
Port = open_port({spawn, Command}, [stream, in, eof, hide]),
- get_data(Port, []);
- %% VxWorks uses a 'sh -c hook' in 'vxcall.c' to run os:cmd.
- vxworks ->
- Command = lists:concat(["sh -c '", Cmd, "'"]),
- Port = open_port({spawn, Command}, [stream, in, eof]),
get_data(Port, [])
end.
diff --git a/lib/kernel/src/packages.erl b/lib/kernel/src/packages.erl
deleted file mode 100644
index e0b1f36b85..0000000000
--- a/lib/kernel/src/packages.erl
+++ /dev/null
@@ -1,158 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2002-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(packages).
-
--export([to_string/1, concat/1, concat/2, is_valid/1, is_segmented/1,
- split/1, last/1, first/1, strip_last/1, find_modules/1,
- find_modules/2]).
-
-%% A package name (or a package-qualified module name) may be an atom or
-%% a string (list of nonnegative integers) - not a deep list, and not a
-%% list containing atoms. A name may be empty, but may not contain two
-%% consecutive period (`.') characters or end with a period character.
-
--type package_name() :: atom() | string().
-
--spec to_string(package_name()) -> string().
-to_string(Name) when is_atom(Name) ->
- atom_to_list(Name);
-to_string(Name) ->
- Name.
-
-%% `concat' does not insert a leading period if the first segment is
-%% empty. However, the result may contain leading, consecutive or
-%% dangling period characters, if any of the segments after the first
-%% are empty. Use 'is_valid' to check the result if necessary.
-
--spec concat(package_name(), package_name()) -> string().
-concat(A, B) ->
- concat([A, B]).
-
--spec concat([package_name()]) -> string().
-concat([H | T]) when is_atom(H) ->
- concat([atom_to_list(H) | T]);
-concat(["" | T]) ->
- concat_1(T);
-concat(L) ->
- concat_1(L).
-
-concat_1([H | T]) when is_atom(H) ->
- concat_1([atom_to_list(H) | T]);
-concat_1([H]) ->
- H;
-concat_1([H | T]) ->
- H ++ "." ++ concat_1(T);
-concat_1([]) ->
- "";
-concat_1(Name) ->
- erlang:error({badarg, Name}).
-
--spec is_valid(package_name()) -> boolean().
-is_valid(Name) when is_atom(Name) ->
- is_valid_1(atom_to_list(Name));
-is_valid([$. | _]) ->
- false;
-is_valid(Name) ->
- is_valid_1(Name).
-
-is_valid_1([$.]) -> false;
-is_valid_1([$., $. | _]) -> false;
-is_valid_1([H | T]) when is_integer(H), H >= 0 ->
- is_valid_1(T);
-is_valid_1([]) -> true;
-is_valid_1(_) -> false.
-
--spec split(package_name()) -> [string()].
-split(Name) when is_atom(Name) ->
- split_1(atom_to_list(Name), []);
-split(Name) ->
- split_1(Name, []).
-
-split_1([$. | T], Cs) ->
- [lists:reverse(Cs) | split_1(T, [])];
-split_1([H | T], Cs) when is_integer(H), H >= 0 ->
- split_1(T, [H | Cs]);
-split_1([], Cs) ->
- [lists:reverse(Cs)];
-split_1(_, _) ->
- erlang:error(badarg).
-
-%% This is equivalent to testing if `split(Name)' yields a list of
-%% length larger than one (i.e., if the name can be split into two or
-%% more segments), but is cheaper.
-
--spec is_segmented(package_name()) -> boolean().
-is_segmented(Name) when is_atom(Name) ->
- is_segmented_1(atom_to_list(Name));
-is_segmented(Name) ->
- is_segmented_1(Name).
-
-is_segmented_1([$. | _]) -> true;
-is_segmented_1([H | T]) when is_integer(H), H >= 0 ->
- is_segmented_1(T);
-is_segmented_1([]) -> false;
-is_segmented_1(_) ->
- erlang:error(badarg).
-
--spec last(package_name()) -> string().
-last(Name) ->
- last_1(split(Name)).
-
-last_1([H]) -> H;
-last_1([_ | T]) -> last_1(T).
-
--spec first(package_name()) -> [string()].
-first(Name) ->
- first_1(split(Name)).
-
-first_1([H | T]) when T =/= [] -> [H | first_1(T)];
-first_1(_) -> [].
-
--spec strip_last(package_name()) -> string().
-strip_last(Name) ->
- concat(first(Name)).
-
-%% This finds all modules available for a given package, using the
-%% current code server search path. (There is no guarantee that the
-%% modules are loadable; only that the object files exist.)
-
--spec find_modules(package_name()) -> [string()].
-find_modules(P) ->
- find_modules(P, code:get_path()).
-
--spec find_modules(package_name(), [string()]) -> [string()].
-find_modules(P, Paths) ->
- P1 = filename:join(packages:split(P)),
- find_modules(P1, Paths, code:objfile_extension(), sets:new()).
-
-find_modules(P, [Path | Paths], Ext, S0) ->
- case file:list_dir(filename:join(Path, P)) of
- {ok, Fs} ->
- Fs1 = [F || F <- Fs, filename:extension(F) =:= Ext],
- S1 = lists:foldl(fun (F, S) ->
- F1 = filename:rootname(F, Ext),
- sets:add_element(F1, S)
- end,
- S0, Fs1),
- find_modules(P, Paths, Ext, S1);
- _ ->
- find_modules(P, Paths, Ext, S0)
- end;
-find_modules(_P, [], _Ext, S) ->
- sets:to_list(S).
diff --git a/lib/kernel/src/pg2.erl b/lib/kernel/src/pg2.erl
index 0d5838716e..1ff10eb303 100644
--- a/lib/kernel/src/pg2.erl
+++ b/lib/kernel/src/pg2.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -159,7 +159,7 @@ get_closest_pid(Name) ->
-record(state, {}).
--opaque state() :: #state{}.
+-type state() :: #state{}.
-spec init(Arg :: []) -> {'ok', state()}.
diff --git a/lib/kernel/src/ram_file.erl b/lib/kernel/src/ram_file.erl
index 48ea871433..ca881ff8a4 100644
--- a/lib/kernel/src/ram_file.erl
+++ b/lib/kernel/src/ram_file.erl
@@ -29,6 +29,7 @@
%% 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, advise/4]).
+-export([allocate/3]).
-export([open_mode/1]). %% used by ftp-file
@@ -72,6 +73,7 @@
-define(RAM_FILE_UUDECODE, 36).
-define(RAM_FILE_SIZE, 37).
-define(RAM_FILE_ADVISE, 38).
+-define(RAM_FILE_ALLOCATE, 39).
%% Open modes for RAM_FILE_OPEN
-define(RAM_FILE_MODE_READ, 1).
@@ -383,6 +385,11 @@ advise(#file_descriptor{module = ?MODULE, data = Port}, Offset,
advise(#file_descriptor{}, _Offset, _Length, _Advise) ->
{error, enotsup}.
+allocate(#file_descriptor{module = ?MODULE, data = Port}, Offset, Length) ->
+ call_port(Port, <<?RAM_FILE_ALLOCATE, Offset:64/signed, Length:64/signed>>);
+allocate(#file_descriptor{}, _Offset, _Length) ->
+ {error, enotsup}.
+
%%%-----------------------------------------------------------------
diff --git a/lib/kernel/src/rpc.erl b/lib/kernel/src/rpc.erl
index e214ffa404..7c965ca384 100644
--- a/lib/kernel/src/rpc.erl
+++ b/lib/kernel/src/rpc.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -62,6 +62,8 @@
%% Internals
-export([proxy_user_flush/0]).
+-export_type([key/0]).
+
%%------------------------------------------------------------------------
-type state() :: gb_tree().
@@ -286,7 +288,7 @@ call(N,M,F,A) ->
Reason :: term(),
Timeout :: timeout().
-call(N,M,F,A,_Timeout) when node() =:= N -> %% Optimize local call
+call(N,M,F,A,infinity) when node() =:= N -> %% Optimize local call
local_call(M,F,A);
call(N,M,F,A,infinity) ->
do_call(N, {call,M,F,A,group_leader()}, infinity);
diff --git a/lib/kernel/src/user.erl b/lib/kernel/src/user.erl
index 88f32df20b..d6449d9e5e 100644
--- a/lib/kernel/src/user.erl
+++ b/lib/kernel/src/user.erl
@@ -81,7 +81,7 @@ server(PortName,PortSettings) ->
run(P) ->
put(read_mode,list),
- put(unicode,false),
+ put(encoding,latin1),
case init:get_argument(noshell) of
%% non-empty list -> noshell
{ok, [_|_]} ->
@@ -191,39 +191,27 @@ do_io_request(Req, From, ReplyAs, Port, Q0) ->
%% New in R13B
%% Encoding option (unicode/latin1)
io_request({put_chars,unicode,Chars}, Port, Q) -> % Binary new in R9C
- put_chars(wrap_characters_to_binary(Chars,unicode,
- case get(unicode) of
- true -> unicode;
- _ -> latin1
- end), Port, Q);
+ put_chars(wrap_characters_to_binary(Chars,unicode, get(encoding)), Port, Q);
io_request({put_chars,unicode,Mod,Func,Args}, Port, Q) ->
Result = case catch apply(Mod,Func,Args) of
Data when is_list(Data); is_binary(Data) ->
- wrap_characters_to_binary(Data,unicode,
- case get(unicode) of
- true -> unicode;
- _ -> latin1
- end);
+ wrap_characters_to_binary(Data,unicode,get(encoding));
Undef ->
Undef
end,
put_chars(Result, Port, Q);
io_request({put_chars,latin1,Chars}, Port, Q) -> % Binary new in R9C
- Data = case get(unicode) of
- true ->
+ Data = case get(encoding) of
+ unicode ->
unicode:characters_to_binary(Chars,latin1,unicode);
- false ->
+ latin1 ->
erlang:iolist_to_binary(Chars)
end,
put_chars(Data, Port, Q);
io_request({put_chars,latin1,Mod,Func,Args}, Port, Q) ->
Result = case catch apply(Mod,Func,Args) of
Data when is_list(Data); is_binary(Data) ->
- unicode:characters_to_binary(Data,latin1,
- case get(unicode) of
- true -> unicode;
- _ -> latin1
- end);
+ unicode:characters_to_binary(Data,latin1,get(encoding));
Undef ->
Undef
end,
@@ -351,9 +339,9 @@ check_valid_opts(_) ->
do_setopts(Opts, _Port, Q) ->
case proplists:get_value(encoding,Opts) of
Valid when Valid =:= unicode; Valid =:= utf8 ->
- put(unicode,true);
+ put(encoding,unicode);
latin1 ->
- put(unicode,false);
+ put(encoding,latin1);
undefined ->
ok
end,
@@ -370,12 +358,7 @@ do_setopts(Opts, _Port, Q) ->
getopts(_Port,Q) ->
Bin = {binary, get(read_mode) =:= binary},
- Uni = {encoding, case get(unicode) of
- true ->
- unicode;
- _ ->
- latin1
- end},
+ Uni = {encoding, get(encoding)},
{ok,[Bin,Uni],Q}.
@@ -575,31 +558,32 @@ binrev(L, T) ->
%% end
%% end
%% end.
-%% get_chars(Prompt, Module, Function, XtraArg, Port, Queue)
+%% get_chars(Prompt, Module, Function, XtraArg, Port, Queue, Encoding)
%% Gets characters from the input port until the applied function
%% returns {stop,Result,RestBuf}. Does not block output until input
-%% has been received.
+%% has been received. Encoding is the encoding of the data sent to
+%% the client and to Function.
%% Returns:
%% {Status,Result,NewQueue}
%% {exit,Reason}
%% Entry function.
-get_chars(Prompt, M, F, Xa, Port, Q, Fmt) ->
+get_chars(Prompt, M, F, Xa, Port, Q, Enc) ->
prompt(Port, Prompt),
case {get(eof),queue:is_empty(Q)} of
{true,true} ->
{ok,eof,Q};
_ ->
- get_chars(Prompt, M, F, Xa, Port, Q, start, Fmt)
+ get_chars(Prompt, M, F, Xa, Port, Q, start, Enc)
end.
%% First loop. Wait for port data. Respond to output requests.
-get_chars(Prompt, M, F, Xa, Port, Q, State, Fmt) ->
+get_chars(Prompt, M, F, Xa, Port, Q, State, Enc) ->
case queue:is_empty(Q) of
true ->
receive
{Port,{data,Bytes}} ->
- get_chars_bytes(State, M, F, Xa, Port, Q, Bytes, Fmt);
+ get_chars_bytes(State, M, F, Xa, Port, Q, Bytes, Enc);
{Port, eof} ->
put(eof, true),
{ok, eof, []};
@@ -610,41 +594,41 @@ get_chars(Prompt, M, F, Xa, Port, Q, State, Fmt) ->
do_io_request(Req, From, ReplyAs, Port,
queue:new()), %Keep Q over this call
%% No prompt.
- get_chars(Prompt, M, F, Xa, Port, Q, State, Fmt);
+ get_chars(Prompt, M, F, Xa, Port, Q, State, Enc);
{io_request,From,ReplyAs,Request} when is_pid(From) ->
get_chars_req(Prompt, M, F, Xa, Port, Q, State,
- Request, From, ReplyAs, Fmt);
+ Request, From, ReplyAs, Enc);
{'EXIT',From,What} when node(From) =:= node() ->
{exit,What}
end;
false ->
- get_chars_apply(State, M, F, Xa, Port, Q, Fmt)
+ get_chars_apply(State, M, F, Xa, Port, Q, Enc)
end.
get_chars_req(Prompt, M, F, XtraArg, Port, Q, State,
- Req, From, ReplyAs, Fmt) ->
+ Req, From, ReplyAs, Enc) ->
do_io_request(Req, From, ReplyAs, Port, queue:new()), %Keep Q over this call
prompt(Port, Prompt),
- get_chars(Prompt, M, F, XtraArg, Port, Q, State, Fmt).
+ get_chars(Prompt, M, F, XtraArg, Port, Q, State, Enc).
%% Second loop. Pass data to client as long as it wants more.
%% A ^G in data interrupts loop if 'noshell' is not undefined.
-get_chars_bytes(State, M, F, Xa, Port, Q, Bytes, Fmt) ->
+get_chars_bytes(State, M, F, Xa, Port, Q, Bytes, Enc) ->
case get(shell) of
noshell ->
- get_chars_apply(State, M, F, Xa, Port, queue:snoc(Q, Bytes),Fmt);
+ get_chars_apply(State, M, F, Xa, Port, queue:snoc(Q, Bytes),Enc);
_ ->
case contains_ctrl_g_or_ctrl_c(Bytes) of
false ->
get_chars_apply(State, M, F, Xa, Port,
- queue:snoc(Q, Bytes),Fmt);
+ queue:snoc(Q, Bytes),Enc);
_ ->
throw(new_shell)
end
end.
-get_chars_apply(State0, M, F, Xa, Port, Q, Fmt) ->
- case catch M:F(State0, cast(queue:head(Q),Fmt), Fmt, Xa) of
+get_chars_apply(State0, M, F, Xa, Port, Q, Enc) ->
+ case catch M:F(State0, cast(queue:head(Q),Enc), Enc, Xa) of
{stop,Result,<<>>} ->
{ok,Result,queue:tail(Q)};
{stop,Result,[]} ->
@@ -653,32 +637,32 @@ get_chars_apply(State0, M, F, Xa, Port, Q, Fmt) ->
{ok,Result,queue:tail(Q)};
{stop,Result,Buf} ->
{ok,Result,queue:cons(Buf, queue:tail(Q))};
- {'EXIT',_} ->
+ {'EXIT',_Why} ->
{error,{error,err_func(M, F, Xa)},queue:new()};
State1 ->
- get_chars_more(State1, M, F, Xa, Port, queue:tail(Q), Fmt)
+ get_chars_more(State1, M, F, Xa, Port, queue:tail(Q), Enc)
end.
-get_chars_more(State, M, F, Xa, Port, Q, Fmt) ->
+get_chars_more(State, M, F, Xa, Port, Q, Enc) ->
case queue:is_empty(Q) of
true ->
case get(eof) of
undefined ->
receive
{Port,{data,Bytes}} ->
- get_chars_bytes(State, M, F, Xa, Port, Q, Bytes, Fmt);
+ get_chars_bytes(State, M, F, Xa, Port, Q, Bytes, Enc);
{Port,eof} ->
put(eof, true),
get_chars_apply(State, M, F, Xa, Port,
- queue:snoc(Q, eof), Fmt);
+ queue:snoc(Q, eof), Enc);
{'EXIT',From,What} when node(From) =:= node() ->
{exit,What}
end;
_ ->
- get_chars_apply(State, M, F, Xa, Port, queue:snoc(Q, eof), Fmt)
+ get_chars_apply(State, M, F, Xa, Port, queue:snoc(Q, eof), Enc)
end;
false ->
- get_chars_apply(State, M, F, Xa, Port, Q, Fmt)
+ get_chars_apply(State, M, F, Xa, Port, Q, Enc)
end.
@@ -689,11 +673,10 @@ get_chars_more(State, M, F, Xa, Port, Q, Fmt) ->
prompt(_Port, '') -> ok;
prompt(Port, Prompt) ->
- put_port(wrap_characters_to_binary(io_lib:format_prompt(Prompt),unicode,
- case get(unicode) of
- true -> unicode;
- _ -> latin1
- end), Port).
+ Encoding = get(encoding),
+ put_port(wrap_characters_to_binary(io_lib:format_prompt(Prompt, Encoding),
+ unicode, Encoding),
+ Port).
%% Convert error code to make it look as before
err_func(io_lib, get_until, {_,F,_}) ->
@@ -710,56 +693,65 @@ contains_ctrl_g_or_ctrl_c(BinOrList)->
end.
%% Convert a buffer between list and binary
-cast(Data, _Format) when is_atom(Data) ->
+cast(Data, _Encoding) when is_atom(Data) ->
Data;
-cast(Data, Format) ->
- cast(Data, get(read_mode), Format, get(unicode)).
+cast(Data, Encoding) ->
+ IoEncoding = get(encoding),
+ cast(Data, get(read_mode), IoEncoding, Encoding).
-cast(B, binary, latin1, false) when is_binary(B) ->
+cast(B, binary, latin1, latin1) when is_binary(B) ->
B;
-cast(B, binary, latin1, true) when is_binary(B) ->
- unicode:characters_to_binary(B, unicode, latin1);
-cast(L, binary, latin1, false) ->
- erlang:iolist_to_binary(L);
-cast(L, binary, latin1, true) ->
- case unicode:characters_to_binary(
- erlang:iolist_to_binary(L),unicode,latin1) of % may fail
- {error,_,_} -> exit({no_translation, unicode, latin1});
- Else -> Else
+cast(L, binary, latin1, latin1) ->
+ case catch erlang:iolist_to_binary(L) of
+ Bin when is_binary(Bin) -> Bin;
+ _ -> exit({no_translation, latin1, latin1})
+ end;
+cast(Data, binary, unicode, latin1) when is_binary(Data); is_list(Data) ->
+ case catch unicode:characters_to_binary(Data, unicode, latin1) of
+ Bin when is_binary(Bin) -> Bin;
+ _ -> exit({no_translation, unicode, latin1})
+ end;
+cast(Data, binary, latin1, unicode) when is_binary(Data); is_list(Data) ->
+ case catch unicode:characters_to_binary(Data, latin1, unicode) of
+ Bin when is_binary(Bin) -> Bin;
+ _ -> exit({no_translation, latin1, unicode})
end;
-cast(B, binary, unicode, true) when is_binary(B) ->
+cast(B, binary, unicode, unicode) when is_binary(B) ->
B;
-cast(B, binary, unicode, false) when is_binary(B) ->
- unicode:characters_to_binary(B,latin1,unicode);
-cast(L, binary, unicode, true) ->
- % possibly a list containing UTF-8 encoded characters
- unicode:characters_to_binary(erlang:iolist_to_binary(L));
-cast(L, binary, unicode, false) ->
- unicode:characters_to_binary(L, latin1, unicode);
-cast(L, list, latin1, UniTerm) ->
- case UniTerm of
- true -> % Convert input characters to protocol format (i.e latin1)
- case unicode:characters_to_list(
- erlang:iolist_to_binary(L),unicode) of % may fail
- {error,_,_} -> exit({no_translation, unicode, latin1});
- Else -> [ case X of
- High when High > 255 ->
- exit({no_translation, unicode, latin1});
- Low ->
- Low
- end || X <- Else ]
- end;
- _ ->
- binary_to_list(erlang:iolist_to_binary(L))
+cast(L, binary, unicode, unicode) ->
+ case catch unicode:characters_to_binary(L, unicode) of
+ Bin when is_binary(Bin) -> Bin;
+ _ -> exit({no_translation, unicode, unicode})
end;
-cast(L, list, unicode, UniTerm) ->
- unicode:characters_to_list(erlang:iolist_to_binary(L),
- case UniTerm of
- true -> unicode;
- _ -> latin1
- end);
-cast(Other, _, _,_) ->
- Other.
+cast(B, list, latin1, latin1) when is_binary(B) ->
+ binary_to_list(B);
+cast(L, list, latin1, latin1) ->
+ case catch erlang:iolist_to_binary(L) of
+ Bin when is_binary(Bin) -> binary_to_list(Bin);
+ _ -> exit({no_translation, latin1, latin1})
+ end;
+cast(Data, list, unicode, latin1) when is_binary(Data); is_list(Data) ->
+ case catch unicode:characters_to_list(Data, unicode) of
+ Chars when is_list(Chars) ->
+ [ case X of
+ High when High > 255 ->
+ exit({no_translation, unicode, latin1});
+ Low ->
+ Low
+ end || X <- Chars ];
+ _ ->
+ exit({no_translation, unicode, latin1})
+ end;
+cast(Data, list, latin1, unicode) when is_binary(Data); is_list(Data) ->
+ case catch unicode:characters_to_list(Data, latin1) of
+ Chars when is_list(Chars) -> Chars;
+ _ -> exit({no_translation, latin1, unicode})
+ end;
+cast(Data, list, unicode, unicode) when is_binary(Data); is_list(Data) ->
+ case catch unicode:characters_to_list(Data, unicode) of
+ Chars when is_list(Chars) -> Chars;
+ _ -> exit({no_translation, unicode, unicode})
+ end.
wrap_characters_to_binary(Chars,unicode,latin1) ->
case unicode:characters_to_binary(Chars,unicode,latin1) of
diff --git a/lib/kernel/src/wrap_log_reader.erl b/lib/kernel/src/wrap_log_reader.erl
index c41e0091e4..689269fc28 100644
--- a/lib/kernel/src/wrap_log_reader.erl
+++ b/lib/kernel/src/wrap_log_reader.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -30,6 +30,8 @@
-export([open/1, open/2, chunk/1, chunk/2, close/1]).
+-export_type([continuation/0]).
+
-include("disk_log.hrl").
-record(wrap_reader,
diff --git a/lib/kernel/test/Makefile b/lib/kernel/test/Makefile
index 7bb3e3a365..8d2d55777b 100644
--- a/lib/kernel/test/Makefile
+++ b/lib/kernel/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -48,6 +48,7 @@ MODULES= \
erl_distribution_SUITE \
erl_distribution_wb_SUITE \
erl_prim_loader_SUITE \
+ error_handler_SUITE \
error_logger_SUITE \
error_logger_warn_SUITE \
file_SUITE \
@@ -73,6 +74,7 @@ MODULES= \
seq_trace_SUITE \
wrap_log_reader_SUITE \
cleanup \
+ ignore_cores \
zlib_SUITE \
loose_node \
sendfile_SUITE
diff --git a/lib/kernel/test/application_SUITE.erl b/lib/kernel/test/application_SUITE.erl
index f469a0af98..2ca8840e1f 100644
--- a/lib/kernel/test/application_SUITE.erl
+++ b/lib/kernel/test/application_SUITE.erl
@@ -27,7 +27,7 @@
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,
otp_5606/1,
- start_phases/1, get_key/1,
+ start_phases/1, get_key/1, get_env/1,
permit_false_start_local/1, permit_false_start_dist/1, script_start/1,
nodedown_start/1, init2973/0, loop2973/0, loop5606/1]).
@@ -49,7 +49,7 @@ all() ->
[failover, failover_comp, permissions, load,
load_use_cache, {group, reported_bugs}, start_phases,
script_start, nodedown_start, permit_false_start_local,
- permit_false_start_dist, get_key,
+ permit_false_start_dist, get_key, get_env,
{group, distr_changed}, config_change, shutdown_func, shutdown_timeout].
groups() ->
@@ -1503,6 +1503,15 @@ loop5606(Pid) ->
Pid ! {self(), Res}
end.
+get_env(suite) -> [];
+get_env(doc) ->
+ ["Tests get_env/* functions"];
+get_env(Conf) when is_list(Conf) ->
+ {ok, _} = application:get_env(kernel, error_logger),
+ undefined = application:get_env(undefined_app, a),
+ undefined = application:get_env(kernel, error_logger_xyz),
+ default = application:get_env(kernel, error_logger_xyz, default),
+ ok.
%%-----------------------------------------------------------------
%% Should be started in a CC view with:
diff --git a/lib/kernel/test/bif_SUITE.erl b/lib/kernel/test/bif_SUITE.erl
index a2826f34df..c369dca4e1 100644
--- a/lib/kernel/test/bif_SUITE.erl
+++ b/lib/kernel/test/bif_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/kernel/test/code_SUITE.erl b/lib/kernel/test/code_SUITE.erl
index f6d38e6d1f..d7424c0c9a 100644
--- a/lib/kernel/test/code_SUITE.erl
+++ b/lib/kernel/test/code_SUITE.erl
@@ -579,30 +579,25 @@ sticky_compiler(File) ->
pa_pz_option(suite) -> [];
pa_pz_option(doc) -> ["Test that the -pa and -pz options work as expected"];
pa_pz_option(Config) when is_list(Config) ->
- case os:type() of
- vxworks ->
- {comment, "Slave nodes not supported on VxWorks"};
- _ ->
- DDir = ?config(data_dir,Config),
- PaDir = filename:join(DDir,"pa"),
- PzDir = filename:join(DDir,"pz"),
- ?line {ok, Node}=?t:start_node(pa_pz1, slave,
- [{args,
- "-pa " ++ PaDir
- ++ " -pz " ++ PzDir}]),
- ?line Ret=rpc:call(Node, code, get_path, []),
- ?line [PaDir|Paths] = Ret,
- ?line [PzDir|_] = lists:reverse(Paths),
- ?t:stop_node(Node),
- ?line {ok, Node2}=?t:start_node(pa_pz2, slave,
- [{args,
- "-mode embedded " ++ "-pa "
- ++ PaDir ++ " -pz " ++ PzDir}]),
- ?line Ret2=rpc:call(Node2, code, get_path, []),
- ?line [PaDir|Paths2] = Ret2,
- ?line [PzDir|_] = lists:reverse(Paths2),
- ?t:stop_node(Node2)
- end.
+ DDir = ?config(data_dir,Config),
+ PaDir = filename:join(DDir,"pa"),
+ PzDir = filename:join(DDir,"pz"),
+ {ok, Node}=?t:start_node(pa_pz1, slave,
+ [{args,
+ "-pa " ++ PaDir
+ ++ " -pz " ++ PzDir}]),
+ Ret=rpc:call(Node, code, get_path, []),
+ [PaDir|Paths] = Ret,
+ [PzDir|_] = lists:reverse(Paths),
+ ?t:stop_node(Node),
+ {ok, Node2}=?t:start_node(pa_pz2, slave,
+ [{args,
+ "-mode embedded " ++ "-pa "
+ ++ PaDir ++ " -pz " ++ PzDir}]),
+ Ret2=rpc:call(Node2, code, get_path, []),
+ [PaDir|Paths2] = Ret2,
+ [PzDir|_] = lists:reverse(Paths2),
+ ?t:stop_node(Node2).
add_del_path(suite) ->
[];
@@ -689,8 +684,8 @@ ext_mod_dep(Config) when is_list(Config) ->
xref:set_default(s, [{verbose,false},{warnings,false},
{builtins,true},{recurse,true}]),
xref:set_library_path(s, code:get_path()),
- xref:add_directory(s, filename:dirname(code:which(kernel))),
- xref:add_directory(s, filename:dirname(code:which(lists))),
+ xref:add_directory(s, filename:join(code:lib_dir(kernel),"ebin")),
+ xref:add_directory(s, filename:join(code:lib_dir(stdlib),"ebin")),
case catch ext_mod_dep2() of
{'EXIT', Reason} ->
xref:stop(s),
@@ -1594,7 +1589,8 @@ native_early_modules_1(Architecture) ->
true ->
?line true = lists:all(fun code:is_module_native/1,
[ets,file,filename,gb_sets,gb_trees,
- hipe_unified_loader,lists,os,packages]),
+ %%hipe_unified_loader, no_native as workaround
+ lists,os,packages]),
ok
end.
diff --git a/lib/kernel/test/disk_log_SUITE.erl b/lib/kernel/test/disk_log_SUITE.erl
index 0c3f5c3514..0f811b8f73 100644
--- a/lib/kernel/test/disk_log_SUITE.erl
+++ b/lib/kernel/test/disk_log_SUITE.erl
@@ -126,11 +126,6 @@
error, chunk, truncate, many_users, info, change_size,
change_attribute, distribution, evil, otp_6278, otp_10131]).
-%% The following two lists should be mutually exclusive. To skip a case
-%% on VxWorks altogether, use the kernel.spec.vxworks file instead.
-%% PLEASE don't skip out of laziness, the goal is to make every
-%% testcase runnable on VxWorks.
-
%% These test cases should be skipped if the VxWorks card is
%% configured without NFS cache.
-define(SKIP_NO_CACHE,[distribution]).
@@ -5126,33 +5121,8 @@ stop_node(Node) ->
%% If the board is configured without NFS, the port program will fail to load
%% and this will return 0, which may or may not be the wrong thing to do.
-check_nfs(Config) ->
- case (catch check_cache(Config)) of
- N when is_integer(N) ->
- N;
- _ ->
- 0
- end.
-
-check_cache(Config) ->
- ?line Check = filename:join(?datadir(Config), "nfs_check"),
- ?line P = open_port({spawn, Check}, [{line,100}, eof]),
- ?line Size = receive
- {P,{data,{eol,S}}} ->
- list_to_integer(S)
- after 1000 ->
- erlang:display(got_timeout),
- exit(timeout)
- end,
- ?line receive
- {P, eof} ->
- ok
- end,
- ?line P ! {self(), close},
- ?line receive
- {P, closed} -> ok
- end,
- Size.
+check_nfs(_Config) ->
+ 0.
skip_expand([]) ->
[];
@@ -5175,13 +5145,8 @@ skip_list(Config) ->
skip_expand(?SKIP_LARGE_CACHE)
end.
-should_skip(Test,Config) ->
- case os:type() of
- vxworks ->
- lists:member(Test, skip_list(Config));
- _ ->
- false
- end.
+should_skip(_Test,_Config) ->
+ false.
%%-----------------------------------------------------------------
%% The error_logger handler used.
diff --git a/lib/kernel/test/disk_log_SUITE_data/Makefile.src b/lib/kernel/test/disk_log_SUITE_data/Makefile.src
deleted file mode 100644
index cae2f23d29..0000000000
--- a/lib/kernel/test/disk_log_SUITE_data/Makefile.src
+++ /dev/null
@@ -1,15 +0,0 @@
-CC = @CC@
-LD = @LD@
-CFLAGS = @CFLAGS@ -I@erl_include@ @DEFS@
-CROSSLDFLAGS = @CROSSLDFLAGS@
-
-PROGS = nfs_check@exe@
-
-all: $(PROGS)
-
-nfs_check@exe@: nfs_check@obj@
- $(LD) $(CROSSLDFLAGS) -o nfs_check nfs_check@obj@ @LIBS@
-
-nfs_check@obj@: nfs_check.c
- $(CC) -c -o nfs_check@obj@ $(CFLAGS) nfs_check.c
-
diff --git a/lib/kernel/test/disk_log_SUITE_data/nfs_check.c b/lib/kernel/test/disk_log_SUITE_data/nfs_check.c
deleted file mode 100644
index 31e9ba8190..0000000000
--- a/lib/kernel/test/disk_log_SUITE_data/nfs_check.c
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Author: Patrik Nyblom
- * Purpose: A port program to check the NFS cache size on VxWorks (returns 0
- * for other platforms).
- */
-
-#ifdef VXWORKS
-#include <vxWorks.h>
-#include <taskVarLib.h>
-#include <taskLib.h>
-#include <sysLib.h>
-#include <string.h>
-#include <ioLib.h>
-#endif
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-
-#ifdef VXWORKS
-extern unsigned nfsCacheSize;
-#define MAIN(argc, argv) nfs_check(argc, argv)
-#else
-#define MAIN(argc, argv) main(argc, argv)
-#endif
-
-
-MAIN(argc, argv)
-int argc;
-char *argv[];
-{
-#ifdef VXWORKS
- char str[100];
- sprintf(str,"%d\n", nfsCacheSize);
- write(1, str, strlen(str));
-#else
- fprintf(stdout,"0");
- fflush(stdout);
-#endif
- return 0;
-}
-
diff --git a/lib/kernel/test/erl_prim_loader_SUITE.erl b/lib/kernel/test/erl_prim_loader_SUITE.erl
index 6f4f27d594..35502a1d27 100644
--- a/lib/kernel/test/erl_prim_loader_SUITE.erl
+++ b/lib/kernel/test/erl_prim_loader_SUITE.erl
@@ -110,56 +110,46 @@ get_file(Config) when is_list(Config) ->
inet_existing(doc) -> ["Start a node using the 'inet' loading method, ",
"from an already started boot server."];
inet_existing(Config) when is_list(Config) ->
- case os:type() of
- vxworks ->
- {comment, "VxWorks: tested separately"};
- _ ->
- ?line Name = erl_prim_test_inet_existing,
- ?line Host = host(),
- ?line Cookie = atom_to_list(erlang:get_cookie()),
- ?line IpStr = ip_str(Host),
- ?line LFlag = get_loader_flag(os:type()),
- ?line Args = LFlag ++ " -hosts " ++ IpStr ++
- " -setcookie " ++ Cookie,
- ?line {ok, BootPid} = erl_boot_server:start_link([Host]),
- ?line {ok, Node} = start_node(Name, Args),
- ?line {ok,[["inet"]]} = rpc:call(Node, init, get_argument, [loader]),
- ?line stop_node(Node),
- ?line unlink(BootPid),
- ?line exit(BootPid, kill),
- ok
- end.
+ Name = erl_prim_test_inet_existing,
+ Host = host(),
+ Cookie = atom_to_list(erlang:get_cookie()),
+ IpStr = ip_str(Host),
+ LFlag = get_loader_flag(os:type()),
+ Args = LFlag ++ " -hosts " ++ IpStr ++
+ " -setcookie " ++ Cookie,
+ {ok, BootPid} = erl_boot_server:start_link([Host]),
+ {ok, Node} = start_node(Name, Args),
+ {ok,[["inet"]]} = rpc:call(Node, init, get_argument, [loader]),
+ stop_node(Node),
+ unlink(BootPid),
+ exit(BootPid, kill),
+ ok.
inet_coming_up(doc) -> ["Start a node using the 'inet' loading method, ",
"but start the boot server afterwards."];
inet_coming_up(Config) when is_list(Config) ->
- case os:type() of
- vxworks ->
- {comment, "VxWorks: tested separately"};
- _ ->
- ?line Name = erl_prim_test_inet_coming_up,
- ?line Cookie = atom_to_list(erlang:get_cookie()),
- ?line Host = host(),
- ?line IpStr = ip_str(Host),
- ?line LFlag = get_loader_flag(os:type()),
- ?line Args = LFlag ++
- " -hosts " ++ IpStr ++
- " -setcookie " ++ Cookie,
- ?line {ok, Node} = start_node(Name, Args, [{wait, false}]),
-
- %% Wait a while, then start boot server, and wait for node to start.
- ?line test_server:sleep(test_server:seconds(6)),
- io:format("erl_boot_server:start_link([~p]).", [Host]),
- ?line {ok, BootPid} = erl_boot_server:start_link([Host]),
- ?line wait_really_started(Node, 25),
-
- %% Check loader argument, then cleanup.
- ?line {ok,[["inet"]]} = rpc:call(Node, init, get_argument, [loader]),
- ?line stop_node(Node),
- ?line unlink(BootPid),
- ?line exit(BootPid, kill),
- ok
- end.
+ Name = erl_prim_test_inet_coming_up,
+ Cookie = atom_to_list(erlang:get_cookie()),
+ Host = host(),
+ IpStr = ip_str(Host),
+ LFlag = get_loader_flag(os:type()),
+ Args = LFlag ++
+ " -hosts " ++ IpStr ++
+ " -setcookie " ++ Cookie,
+ {ok, Node} = start_node(Name, Args, [{wait, false}]),
+
+ %% Wait a while, then start boot server, and wait for node to start.
+ test_server:sleep(test_server:seconds(6)),
+ io:format("erl_boot_server:start_link([~p]).", [Host]),
+ {ok, BootPid} = erl_boot_server:start_link([Host]),
+ wait_really_started(Node, 25),
+
+ %% Check loader argument, then cleanup.
+ {ok,[["inet"]]} = rpc:call(Node, init, get_argument, [loader]),
+ stop_node(Node),
+ unlink(BootPid),
+ exit(BootPid, kill),
+ ok.
wait_really_started(Node, 0) ->
test_server:fail({not_booted,Node});
@@ -249,8 +239,6 @@ multiple_slaves(doc) ->
"verify that the boot server manages"];
multiple_slaves(Config) when is_list(Config) ->
case os:type() of
- vxworks ->
- {comment, "VxWorks: tested separately"};
{ose,_} ->
{comment, "OSE: multiple nodes not supported"};
_ ->
@@ -426,7 +414,9 @@ primary_archive(Config) when is_list(Config) ->
ExpectedEbins = [Archive, DictDir ++ "/ebin", DummyDir ++ "/ebin"],
io:format("ExpectedEbins: ~p\n", [ExpectedEbins]),
?line {ok, FileInfo} = prim_file:read_file_info(Archive),
- ?line {ok, Ebins} = rpc:call(Node, erl_prim_loader, set_primary_archive, [Archive, ArchiveBin, FileInfo]),
+ ?line {ok, Ebins} = rpc:call(Node, erl_prim_loader, set_primary_archive,
+ [Archive, ArchiveBin, FileInfo,
+ fun escript:parse_file/1]),
?line ExpectedEbins = lists:sort(Ebins), % assert
?line {ok, TopFiles2} = rpc:call(Node, erl_prim_loader, list_dir, [Archive]),
@@ -435,7 +425,9 @@ primary_archive(Config) when is_list(Config) ->
?line ok = test_archive(Node, Archive, DictDir, BeamName),
%% Cleanup
- ?line {ok, []} = rpc:call(Node, erl_prim_loader, set_primary_archive, [undefined, undefined, undefined]),
+ ?line {ok, []} = rpc:call(Node, erl_prim_loader, set_primary_archive,
+ [undefined, undefined, undefined,
+ fun escript:parse_file/1]),
?line stop_node(Node),
?line ok = file:delete(Archive),
ok.
diff --git a/lib/kernel/test/error_handler_SUITE.erl b/lib/kernel/test/error_handler_SUITE.erl
new file mode 100644
index 0000000000..2a86d39b74
--- /dev/null
+++ b/lib/kernel/test/error_handler_SUITE.erl
@@ -0,0 +1,68 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2013. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, 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(error_handler_SUITE).
+
+-export([all/0,suite/0,groups/0,init_per_suite/1,end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ undefined_function_handler/1]).
+
+%% Callback from error_handler.
+-export(['$handle_undefined_function'/2]).
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [undefined_function_handler].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+%%-----------------------------------------------------------------
+
+undefined_function_handler(_) ->
+ 42 = ?MODULE:forty_two(),
+ 42 = (id(?MODULE)):forty_two(),
+ {ok,{a,b,c}} = ?MODULE:one_arg({a,b,c}),
+ {ok,{a,b,c}} = (id(?MODULE)):one_arg({a,b,c}),
+ {'EXIT',{undef,[{?MODULE,undef_and_not_handled,[[1,2,3]],[]}|_]}} =
+ (catch ?MODULE:undef_and_not_handled([1,2,3])),
+ ok.
+
+'$handle_undefined_function'(forty_two, []) ->
+ 42;
+'$handle_undefined_function'(one_arg, [Arg]) ->
+ {ok,Arg};
+'$handle_undefined_function'(Func, Args) ->
+ error_handler:raise_undef_exception(?MODULE, Func, Args).
+
+id(I) ->
+ I.
diff --git a/lib/kernel/test/error_logger_warn_SUITE.erl b/lib/kernel/test/error_logger_warn_SUITE.erl
index 265e1ae4c8..2bf467610e 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-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -310,7 +310,7 @@ nice_stop_node(Name) ->
{nodedown,Name} -> ok
end.
-%rensa rd() f�re varje rapport-test s� man bara f�r en fil...
+%clean out rd() before each report test in order to get only one file...
clean_rd() ->
{ok,L} = file:list_dir(rd()),
lists:foreach(fun(F) ->
diff --git a/lib/kernel/test/file_SUITE.erl b/lib/kernel/test/file_SUITE.erl
index 2b6af7e1fb..f34341f561 100644
--- a/lib/kernel/test/file_SUITE.erl
+++ b/lib/kernel/test/file_SUITE.erl
@@ -60,7 +60,8 @@
-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]).
+ write_compressed/1, compress_errors/1, catenated_gzips/1,
+ compress_async_crash/1]).
-export([ make_link/1, read_link_info_for_non_link/1, symlinks/1]).
@@ -84,6 +85,8 @@
-export([advise/1]).
+-export([allocate/1]).
+
-export([standard_io/1,mini_server/1]).
%% Debug exports
@@ -116,7 +119,7 @@ groups() ->
{files, [],
[{group, open}, {group, pos}, {group, file_info},
{group, consult}, {group, eval}, {group, script},
- truncate, sync, datasync, advise]},
+ truncate, sync, datasync, advise, allocate]},
{open, [],
[open1, old_modes, new_modes, path_open, close, access,
read_write, pread_write, append, open_errors,
@@ -133,7 +136,8 @@ groups() ->
{compression, [],
[read_compressed_cooked, read_compressed_cooked_binary,
read_cooked_tar_problem, read_not_really_compressed,
- write_compressed, compress_errors, catenated_gzips]},
+ write_compressed, compress_errors, catenated_gzips,
+ compress_async_crash]},
{links, [],
[make_link, read_link_info_for_non_link, symlinks]}].
@@ -492,8 +496,6 @@ cur_dir_1(Config) when is_list(Config) ->
?line case os:type() of
{unix, _} ->
?line {error, enotsup} = ?FILE_MODULE:get_cwd("d:");
- vxworks ->
- ?line {error, enotsup} = ?FILE_MODULE:get_cwd("d:");
{win32, _} ->
win_cur_dir_1(Config)
end,
@@ -1038,32 +1040,29 @@ file_info_basic_file(Config) when is_list(Config) ->
file_info_basic_directory(suite) -> [];
file_info_basic_directory(doc) -> [];
file_info_basic_directory(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(5)),
+ Dog = test_server:timetrap(test_server:seconds(5)),
%% Note: filename:join/1 removes any trailing slash,
%% which is essential for ?FILE_MODULE:file_info/1 to work on
%% platforms such as Windows95.
- ?line RootDir = filename:join([?config(priv_dir, Config)]),
+ RootDir = filename:join([?config(priv_dir, Config)]),
%% Test that the RootDir directory has the expected attributes.
- ?line test_directory(RootDir, read_write),
+ test_directory(RootDir, read_write),
%% Note that on Windows file systems,
%% "/" or "c:/" are *NOT* directories.
%% Therefore, test that ?FILE_MODULE:file_info/1 behaves as if they were
%% directories.
- ?line case os:type() of
- {win32, _} ->
- ?line test_directory("/", read_write),
- ?line test_directory("c:/", read_write),
- ?line test_directory("c:\\", read_write);
- {unix, _} ->
- ?line test_directory("/", read);
- vxworks ->
- %% Check is just done for owner
- ?line test_directory("/", read_write)
- end,
- ?line test_server:timetrap_cancel(Dog).
+ case os:type() of
+ {win32, _} ->
+ ?line test_directory("/", read_write),
+ ?line test_directory("c:/", read_write),
+ ?line test_directory("c:\\", read_write);
+ {unix, _} ->
+ ?line test_directory("/", read)
+ end,
+ test_server:timetrap_cancel(Dog).
test_directory(Name, ExpectedAccess) ->
?line {ok,#file_info{size=Size,type=Type,access=Access,
@@ -1622,6 +1621,74 @@ advise(Config) when is_list(Config) ->
?line test_server:timetrap_cancel(Dog),
ok.
+allocate(suite) -> [];
+allocate(doc) -> "Tests that ?FILE_MODULE:allocate/3 at least doesn't crash.";
+allocate(Config) when is_list(Config) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(5)),
+ ?line PrivDir = ?config(priv_dir, Config),
+ ?line Allocate = filename:join(PrivDir,
+ atom_to_list(?MODULE)
+ ++"_allocate.fil"),
+
+ Line1 = "Hello\n",
+ Line2 = "World!\n",
+
+ ?line {ok, Fd} = ?FILE_MODULE:open(Allocate, [write, binary]),
+ allocate_and_assert(Fd, 1, iolist_size([Line1, Line2])),
+ ?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(Allocate, [write, binary]),
+ allocate_and_assert(Fd2, 1, iolist_size(Line1)),
+ ?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(Allocate, [write, binary]),
+ allocate_and_assert(Fd3, 1, iolist_size(Line1) + 1),
+ ?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(Allocate, [write, binary]),
+ allocate_and_assert(Fd4, 1, 4 * iolist_size([Line1, Line2])),
+ ?line ok = io:format(Fd4, "~s", [Line1]),
+ ?line ok = io:format(Fd4, "~s", [Line2]),
+ ?line ok = ?FILE_MODULE:close(Fd4),
+
+ ?line [] = flush(),
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+allocate_and_assert(Fd, Offset, Length) ->
+ % Just verify that calls to ?PRIM_FILE:allocate/3 don't crash or have
+ % any other negative side effect. We can't really asssert against a
+ % specific return value, because support for file space pre-allocation
+ % depends on the OS, OS version and underlying filesystem.
+ %
+ % The Linux kernel added support for fallocate() in version 2.6.23,
+ % which currently works only for the ext4, ocfs2, xfs and btrfs file
+ % systems. posix_fallocate() is available in glibc as of version
+ % 2.1.94, but it was buggy until glibc version 2.7.
+ %
+ % Mac OS X, as of version 10.3, supports the fcntl operation F_PREALLOCATE.
+ %
+ % Solaris supports posix_fallocate() but only for the UFS file system
+ % apparently (not supported for ZFS).
+ %
+ % FreeBSD 9.0 is the first FreeBSD release supporting posix_fallocate().
+ %
+ % For Windows there's apparently no way to pre-allocate file space, at
+ % least with same semantics as posix_fallocate(), fallocate() and
+ % fcntl F_PREALLOCATE.
+ Result = ?FILE_MODULE:allocate(Fd, Offset, Length),
+ case os:type() of
+ {win32, _} ->
+ ?line {error, enotsup} = Result;
+ _ ->
+ ?line _ = Result
+ end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -1784,9 +1851,7 @@ e_delete(Config) when is_list(Config) ->
Base, #file_info {mode=8#600});
{win32, _} ->
%% Remove a character device.
- ?line {error, eacces} = ?FILE_MODULE:delete("nul");
- vxworks ->
- ok
+ ?line {error, eacces} = ?FILE_MODULE:delete("nul")
end,
?line [] = flush(),
@@ -1801,148 +1866,133 @@ e_delete(Config) when is_list(Config) ->
e_rename(suite) -> [];
e_rename(doc) -> [];
e_rename(Config) when is_list(Config) ->
- case os:type() of
- vxworks ->
- {comment, "Windriver: dosFs must be fixed first!"};
- _ ->
- ?line Dog = test_server:timetrap(test_server:seconds(10)),
- ?line RootDir = ?config(priv_dir, Config),
- ?line Base = filename:join(RootDir,
- atom_to_list(?MODULE)++"_e_rename"),
- ?line ok = ?FILE_MODULE:make_dir(Base),
-
- %% Create an empty directory.
- ?line EmptyDir = filename:join(Base, "empty_dir"),
- ?line ok = ?FILE_MODULE:make_dir(EmptyDir),
-
- %% Create a non-empty directory.
- ?line NonEmptyDir = filename:join(Base, "non_empty_dir"),
- ?line ok = ?FILE_MODULE:make_dir(NonEmptyDir),
- ?line ok = ?FILE_MODULE:write_file(
- filename:join(NonEmptyDir, "a_file"),
- "hello\n"),
-
- %% Create another non-empty directory.
- ?line ADirectory = filename:join(Base, "a_directory"),
- ?line ok = ?FILE_MODULE:make_dir(ADirectory),
- ?line ok = ?FILE_MODULE:write_file(
- filename:join(ADirectory, "a_file"),
- "howdy\n\n"),
-
- %% Create a data file.
- ?line File = filename:join(Base, "just_a_file"),
- ?line ok = ?FILE_MODULE:write_file(File, "anything goes\n\n"),
-
- %% Move an existing directory to a non-empty directory.
- ?line {error, eexist} =
- ?FILE_MODULE:rename(ADirectory, NonEmptyDir),
-
- %% Move a root directory.
- ?line {error, einval} = ?FILE_MODULE:rename("/", "arne"),
-
- %% Move Base into Base/new_name.
- ?line {error, einval} =
- ?FILE_MODULE:rename(Base, filename:join(Base, "new_name")),
-
- %% Overwrite a directory with a file.
- ?line expect({error, eexist}, %FreeBSD (?)
- {error, eisdir},
- ?FILE_MODULE:rename(File, EmptyDir)),
- ?line expect({error, eexist}, %FreeBSD (?)
- {error, eisdir},
- ?FILE_MODULE:rename(File, NonEmptyDir)),
-
- %% Move a non-existing file.
- ?line NonExistingFile =
- filename:join(Base, "non_existing_file"),
- ?line {error, enoent} =
- ?FILE_MODULE:rename(NonExistingFile, NonEmptyDir),
-
- %% Overwrite a file with a directory.
- ?line expect({error, eexist}, %FreeBSD (?)
- {error, enotdir},
- ?FILE_MODULE:rename(ADirectory, File)),
-
- %% Move a file to another filesystem.
- %% XXX - This test case is bogus. We cannot be guaranteed that
- %% the source and destination are on
- %% different filesystems.
- %%
- %% XXX - Gross hack!
- ?line Comment =
- case os:type() of
- {unix, _} ->
- OtherFs = "/tmp",
- ?line NameOnOtherFs =
- filename:join(OtherFs, filename:basename(File)),
- ?line {ok, Com} =
- case ?FILE_MODULE:rename(File, NameOnOtherFs) of
- {error, exdev} ->
- %% The file could be in
- %% the same filesystem!
- {ok, ok};
- ok ->
- {ok, {comment,
- "Moving between filesystems "
- "suceeded, files are probably "
- "in the same filesystem!"}};
- {error, eperm} ->
- {ok, {comment, "SBS! You don't "
- "have the permission to do "
- "this test!"}};
- Else ->
- Else
- end,
- Com;
- {win32, _} ->
- %% At least Windows NT can
- %% successfully move a file to
- %% another drive.
- ok
- end,
- ?line [] = flush(),
- ?line test_server:timetrap_cancel(Dog),
- Comment
- end.
+ Dog = test_server:timetrap(test_server:seconds(10)),
+ RootDir = ?config(priv_dir, Config),
+ Base = filename:join(RootDir,
+ atom_to_list(?MODULE)++"_e_rename"),
+ ok = ?FILE_MODULE:make_dir(Base),
+
+ %% Create an empty directory.
+ EmptyDir = filename:join(Base, "empty_dir"),
+ ok = ?FILE_MODULE:make_dir(EmptyDir),
+
+ %% Create a non-empty directory.
+ NonEmptyDir = filename:join(Base, "non_empty_dir"),
+ ok = ?FILE_MODULE:make_dir(NonEmptyDir),
+ ok = ?FILE_MODULE:write_file(
+ filename:join(NonEmptyDir, "a_file"),
+ "hello\n"),
+
+ %% Create another non-empty directory.
+ ADirectory = filename:join(Base, "a_directory"),
+ ok = ?FILE_MODULE:make_dir(ADirectory),
+ ok = ?FILE_MODULE:write_file(
+ filename:join(ADirectory, "a_file"),
+ "howdy\n\n"),
+
+ %% Create a data file.
+ File = filename:join(Base, "just_a_file"),
+ ok = ?FILE_MODULE:write_file(File, "anything goes\n\n"),
+
+ %% Move an existing directory to a non-empty directory.
+ {error, eexist} = ?FILE_MODULE:rename(ADirectory, NonEmptyDir),
+
+ %% Move a root directory.
+ {error, einval} = ?FILE_MODULE:rename("/", "arne"),
+
+ %% Move Base into Base/new_name.
+ {error, einval} =
+ ?FILE_MODULE:rename(Base, filename:join(Base, "new_name")),
+
+ %% Overwrite a directory with a file.
+ expect({error, eexist}, %FreeBSD (?)
+ {error, eisdir},
+ ?FILE_MODULE:rename(File, EmptyDir)),
+ expect({error, eexist}, %FreeBSD (?)
+ {error, eisdir},
+ ?FILE_MODULE:rename(File, NonEmptyDir)),
+
+ %% Move a non-existing file.
+ NonExistingFile = filename:join(Base, "non_existing_file"),
+ {error, enoent} = ?FILE_MODULE:rename(NonExistingFile, NonEmptyDir),
+
+ %% Overwrite a file with a directory.
+ expect({error, eexist}, %FreeBSD (?)
+ {error, enotdir},
+ ?FILE_MODULE:rename(ADirectory, File)),
+
+ %% Move a file to another filesystem.
+ %% XXX - This test case is bogus. We cannot be guaranteed that
+ %% the source and destination are on
+ %% different filesystems.
+ %%
+ %% XXX - Gross hack!
+ Comment = case os:type() of
+ {unix, _} ->
+ OtherFs = "/tmp",
+ NameOnOtherFs = filename:join(OtherFs, filename:basename(File)),
+ {ok, Com} = case ?FILE_MODULE:rename(File, NameOnOtherFs) of
+ {error, exdev} ->
+ %% The file could be in
+ %% the same filesystem!
+ {ok, ok};
+ ok ->
+ {ok, {comment,
+ "Moving between filesystems "
+ "suceeded, files are probably "
+ "in the same filesystem!"}};
+ {error, eperm} ->
+ {ok, {comment, "SBS! You don't "
+ "have the permission to do "
+ "this test!"}};
+ Else ->
+ Else
+ end,
+ Com;
+ {win32, _} ->
+ %% At least Windows NT can
+ %% successfully move a file to
+ %% another drive.
+ ok
+ end,
+ [] = flush(),
+ test_server:timetrap_cancel(Dog),
+ Comment.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
e_make_dir(suite) -> [];
e_make_dir(doc) -> [];
e_make_dir(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(10)),
- ?line RootDir = ?config(priv_dir, Config),
- ?line Base = filename:join(RootDir,
- atom_to_list(?MODULE)++"_e_make_dir"),
- ?line ok = ?FILE_MODULE:make_dir(Base),
+ Dog = test_server:timetrap(test_server:seconds(10)),
+ RootDir = ?config(priv_dir, Config),
+ Base = filename:join(RootDir,
+ atom_to_list(?MODULE)++"_e_make_dir"),
+ ok = ?FILE_MODULE:make_dir(Base),
%% A component of the path does not exist.
- ?line {error, enoent} =
- ?FILE_MODULE:make_dir(filename:join([Base, "a", "b"])),
+ {error, enoent} = ?FILE_MODULE:make_dir(filename:join([Base, "a", "b"])),
%% Use a path-name with a non-directory component.
- ?line Afile = filename:join(Base, "a_directory"),
- ?line ok = ?FILE_MODULE:write_file(Afile, "hello\n"),
- ?line case ?FILE_MODULE:make_dir(
- filename:join(Afile, "another_directory")) of
- {error, enotdir} -> io:format("Result: enotdir");
- {error, enoent} -> io:format("Result: enoent")
- end,
+ Afile = filename:join(Base, "a_directory"),
+ ok = ?FILE_MODULE:write_file(Afile, "hello\n"),
+ case ?FILE_MODULE:make_dir(
+ filename:join(Afile, "another_directory")) of
+ {error, enotdir} -> io:format("Result: enotdir");
+ {error, enoent} -> io:format("Result: enoent")
+ end,
%% No permission (on Unix only).
case os:type() of
{unix, _} ->
- ?line ?FILE_MODULE:write_file_info(Base, #file_info {mode=0}),
- ?line {error, eacces} =
- ?FILE_MODULE:make_dir(filename:join(Base, "xxxx")),
- ?line ?FILE_MODULE:write_file_info(
+ ?FILE_MODULE:write_file_info(Base, #file_info {mode=0}),
+ {error, eacces} = ?FILE_MODULE:make_dir(filename:join(Base, "xxxx")),
+ ?FILE_MODULE:write_file_info(
Base, #file_info {mode=8#600});
{win32, _} ->
- ok;
- vxworks ->
ok
end,
- ?line test_server:timetrap_cancel(Dog),
+ test_server:timetrap_cancel(Dog),
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -1950,57 +2000,50 @@ e_make_dir(Config) when is_list(Config) ->
e_del_dir(suite) -> [];
e_del_dir(doc) -> [];
e_del_dir(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(10)),
- ?line RootDir = ?config(priv_dir, Config),
- ?line Base = test_server:temp_name(filename:join(RootDir, "e_del_dir")),
- ?line io:format("Base: ~p", [Base]),
- ?line ok = ?FILE_MODULE:make_dir(Base),
+ Dog = test_server:timetrap(test_server:seconds(10)),
+ RootDir = ?config(priv_dir, Config),
+ Base = test_server:temp_name(filename:join(RootDir, "e_del_dir")),
+ io:format("Base: ~p", [Base]),
+ ok = ?FILE_MODULE:make_dir(Base),
%% Delete a non-existent directory.
- ?line {error, enoent} =
+ {error, enoent} =
?FILE_MODULE:del_dir(filename:join(Base, "non_existing")),
%% Use a path-name with a non-directory component.
- ?line Afile = filename:join(Base, "a_directory"),
- ?line ok = ?FILE_MODULE:write_file(Afile, "hello\n"),
- ?line {error, E1} =
- expect({error, enotdir}, {error, enoent},
- ?FILE_MODULE:del_dir(
- filename:join(Afile, "another_directory"))),
- ?line io:format("Result: ~p", [E1]),
+ Afile = filename:join(Base, "a_directory"),
+ ok = ?FILE_MODULE:write_file(Afile, "hello\n"),
+ {error, E1} = expect({error, enotdir}, {error, enoent},
+ ?FILE_MODULE:del_dir(
+ filename:join(Afile, "another_directory"))),
+ io:format("Result: ~p", [E1]),
%% Delete a non-empty directory.
- ?line {error, E2} =
- expect({error, enotempty}, {error, eexist}, {error, eacces},
+ {error, E2} = expect({error, enotempty}, {error, eexist}, {error, eacces},
?FILE_MODULE:del_dir(Base)),
- ?line io:format("Result: ~p", [E2]),
+ io:format("Result: ~p", [E2]),
%% Remove the current directory.
- ?line {error, E3} =
- expect({error, einval},
+ {error, E3} = expect({error, einval},
{error, eperm}, % Linux and DUX
{error, eacces},
{error, ebusy},
?FILE_MODULE:del_dir(".")),
- ?line io:format("Result: ~p", [E3]),
+ io:format("Result: ~p", [E3]),
%% No permission.
case os:type() of
{unix, _} ->
- ?line ADirectory = filename:join(Base, "no_perm"),
- ?line ok = ?FILE_MODULE:make_dir(ADirectory),
- ?line ?FILE_MODULE:write_file_info(
- Base, #file_info {mode=0}),
- ?line {error, eacces} = ?FILE_MODULE:del_dir(ADirectory),
- ?line ?FILE_MODULE:write_file_info(
- Base, #file_info {mode=8#600});
+ ADirectory = filename:join(Base, "no_perm"),
+ ok = ?FILE_MODULE:make_dir(ADirectory),
+ ?FILE_MODULE:write_file_info( Base, #file_info {mode=0}),
+ {error, eacces} = ?FILE_MODULE:del_dir(ADirectory),
+ ?FILE_MODULE:write_file_info( Base, #file_info {mode=8#600});
{win32, _} ->
- ok;
- vxworks ->
ok
end,
- ?line [] = flush(),
- ?line test_server:timetrap_cancel(Dog),
+ [] = flush(),
+ test_server:timetrap_cancel(Dog),
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -2271,6 +2314,57 @@ compress_errors(Config) when is_list(Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+compress_async_crash(suite) -> [];
+compress_async_crash(doc) -> [];
+compress_async_crash(Config) when is_list(Config) ->
+ ?line DataDir = ?config(data_dir, Config),
+ ?line Path = filename:join(DataDir, "test.gz"),
+ ExpectedData = <<"qwerty">>,
+
+ ?line _ = ?FILE_MODULE:delete(Path),
+ ?line {ok, Fd} = ?FILE_MODULE:open(Path, [write, binary, compressed]),
+ ?line ok = ?FILE_MODULE:write(Fd, ExpectedData),
+ ?line ok = ?FILE_MODULE:close(Fd),
+
+ % Test that when using async thread pool, the emulator doesn't crash
+ % when the efile port driver is stopped while a compressed file operation
+ % is in progress (being carried by an async thread).
+ ?line ok = compress_async_crash_loop(10000, Path, ExpectedData),
+ ?line ok = ?FILE_MODULE:delete(Path),
+ ok.
+
+compress_async_crash_loop(0, _Path, _ExpectedData) ->
+ ok;
+compress_async_crash_loop(N, Path, ExpectedData) ->
+ Parent = self(),
+ {Pid, Ref} = spawn_monitor(
+ fun() ->
+ ?line {ok, Fd} = ?FILE_MODULE:open(
+ Path, [read, compressed, raw, binary]),
+ Len = byte_size(ExpectedData),
+ Parent ! {self(), continue},
+ ?line {ok, ExpectedData} = ?FILE_MODULE:read(Fd, Len),
+ ?line ok = ?FILE_MODULE:close(Fd),
+ receive foobar -> ok end
+ end),
+ receive
+ {Pid, continue} ->
+ exit(Pid, shutdown),
+ receive
+ {'DOWN', Ref, _, _, Reason} ->
+ ?line shutdown = Reason
+ end;
+ {'DOWN', Ref, _, _, Reason2} ->
+ test_server:fail({worker_exited, Reason2})
+ after 60000 ->
+ exit(Pid, shutdown),
+ erlang:demonitor(Ref, [flush]),
+ test_server:fail(worker_timeout)
+ end,
+ compress_async_crash_loop(N - 1, Path, ExpectedData).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
altname(doc) ->
"Test the file:altname/1 function";
altname(suite) ->
@@ -2358,6 +2452,7 @@ symlinks(doc) -> "Test operations on symbolic links (for Unix).";
symlinks(suite) -> [];
symlinks(Config) when is_list(Config) ->
?line Dog = test_server:timetrap(test_server:seconds(10)),
+ ?line {error, _} = ?FILE_MODULE:read_link(lists:duplicate(10000,$a)),
?line RootDir = ?config(priv_dir, Config),
?line NewDir = filename:join(RootDir,
atom_to_list(?MODULE)
@@ -2563,147 +2658,123 @@ delayed_write(doc) ->
["Tests the file open option {delayed_write, Size, Delay}"];
delayed_write(Config) when is_list(Config) ->
- ?line Dog = ?t:timetrap(?t:seconds(20)),
- %%
- ?line RootDir = ?config(priv_dir, Config),
- ?line File = filename:join(RootDir,
- atom_to_list(?MODULE)++"_delayed_write.txt"),
- ?line Data1 = "asdfghjkl",
- ?line Data2 = "qwertyuio",
- ?line Data3 = "zxcvbnm,.",
- ?line Size = length(Data1),
- ?line Size = length(Data2),
- ?line Size = length(Data3),
- ?line Data1Data1 = Data1++Data1,
- ?line Data1Data1Data1 = Data1Data1++Data1,
- ?line Data1Data1Data1Data1 = Data1Data1++Data1Data1,
+ Dog = ?t:timetrap(?t:seconds(20)),
+
+ RootDir = ?config(priv_dir, Config),
+ File = filename:join(RootDir,
+ atom_to_list(?MODULE)++"_delayed_write.txt"),
+ Data1 = "asdfghjkl",
+ Data2 = "qwertyuio",
+ Data3 = "zxcvbnm,.",
+ Size = length(Data1),
+ Size = length(Data2),
+ Size = length(Data3),
+ Data1Data1 = Data1++Data1,
+ Data1Data1Data1 = Data1Data1++Data1,
+ Data1Data1Data1Data1 = Data1Data1++Data1Data1,
%%
%% Test caching and normal close of non-raw file
- ?line {ok, Fd1} =
+ {ok, Fd1} =
?FILE_MODULE:open(File, [write, {delayed_write, Size+1, 2000}]),
- ?line ok = ?FILE_MODULE:write(Fd1, Data1),
- ?line ?t:sleep(1000), % Just in case the file system is slow
- ?line {ok, Fd2} = ?FILE_MODULE:open(File, [read]),
- ?line case os:type() of
- vxworks ->
- io:format("Line ~p skipped on vxworks", [?LINE]);
- _ ->
- ?line eof = ?FILE_MODULE:read(Fd2, 1)
- end,
- ?line ok = ?FILE_MODULE:write(Fd1, Data1), % Data flush on size
- ?line ?t:sleep(1000), % Just in case the file system is slow
- ?line {ok, Data1Data1} = ?FILE_MODULE:pread(Fd2, bof, 2*Size+1),
- ?line ok = ?FILE_MODULE:write(Fd1, Data1),
- ?line ?t:sleep(3000), % Wait until data flush on timeout
- ?line {ok, Data1Data1Data1} = ?FILE_MODULE:pread(Fd2, bof, 3*Size+1),
- ?line ok = ?FILE_MODULE:write(Fd1, Data1),
- ?line ok = ?FILE_MODULE:close(Fd1), % Data flush on close
- ?line ?t:sleep(1000), % Just in case the file system is slow
- ?line {ok, Data1Data1Data1Data1} = ?FILE_MODULE:pread(Fd2, bof, 4*Size+1),
- ?line ok = ?FILE_MODULE:close(Fd2),
+ ok = ?FILE_MODULE:write(Fd1, Data1),
+ ?t:sleep(1000), % Just in case the file system is slow
+ {ok, Fd2} = ?FILE_MODULE:open(File, [read]),
+ eof = ?FILE_MODULE:read(Fd2, 1),
+ ok = ?FILE_MODULE:write(Fd1, Data1), % Data flush on size
+ ?t:sleep(1000), % Just in case the file system is slow
+ {ok, Data1Data1} = ?FILE_MODULE:pread(Fd2, bof, 2*Size+1),
+ ok = ?FILE_MODULE:write(Fd1, Data1),
+ ?t:sleep(3000), % Wait until data flush on timeout
+ {ok, Data1Data1Data1} = ?FILE_MODULE:pread(Fd2, bof, 3*Size+1),
+ ok = ?FILE_MODULE:write(Fd1, Data1),
+ ok = ?FILE_MODULE:close(Fd1), % Data flush on close
+ ?t:sleep(1000), % Just in case the file system is slow
+ {ok, Data1Data1Data1Data1} = ?FILE_MODULE:pread(Fd2, bof, 4*Size+1),
+ ok = ?FILE_MODULE:close(Fd2),
%%
%% Test implicit close through exit by file owning process,
%% raw file, default parameters.
- ?line Parent = self(),
- ?line Fun =
- fun () ->
- Child = self(),
- Test =
- fun () ->
- ?line {ok, Fd} =
- ?FILE_MODULE:open(File,
- [raw, write,
- delayed_write]),
- ?line ok = ?FILE_MODULE:write(Fd, Data1),
- ?line Parent ! {Child, wrote},
- ?line receive
- {Parent, continue, Reason} ->
- {ok, Reason}
- end
- end,
- case (catch Test()) of
- {ok, Reason} ->
- exit(Reason);
- Unknown ->
- exit({Unknown, get(test_server_loc)})
- end
- end,
- ?line Child1 = spawn(Fun),
- ?line Mref1 = erlang:monitor(process, Child1),
- ?line receive
- {Child1, wrote} ->
- ok;
- {'DOWN', Mref1, _, _, _} = Down1a ->
- ?t:fail(Down1a)
- end,
- ?line ?t:sleep(1000), % Just in case the file system is slow
- ?line {ok, Fd3} = ?FILE_MODULE:open(File, [read]),
- ?line case os:type() of
- vxworks ->
- io:format("Line ~p skipped on vxworks", [?LINE]);
- _ ->
- ?line eof = ?FILE_MODULE:read(Fd3, 1)
- end,
- ?line Child1 ! {Parent, continue, normal},
- ?line receive
- {'DOWN', Mref1, process, Child1, normal} ->
- ok;
- {'DOWN', Mref1, _, _, _} = Down1b ->
- ?t:fail(Down1b)
- end,
- ?line ?t:sleep(1000), % Just in case the file system is slow
- ?line {ok, Data1} = ?FILE_MODULE:pread(Fd3, bof, Size+1),
- ?line ok = ?FILE_MODULE:close(Fd3),
+ Parent = self(),
+ Fun = fun() ->
+ Child = self(),
+ Test =
+ fun () ->
+ {ok, Fd} = ?FILE_MODULE:open(File,
+ [raw, write, delayed_write]),
+ ok = ?FILE_MODULE:write(Fd, Data1),
+ Parent ! {Child, wrote},
+ receive
+ {Parent, continue, Reason} ->
+ {ok, Reason}
+ end
+ end,
+ case (catch Test()) of
+ {ok, Reason} -> exit(Reason);
+ Unknown ->
+ exit({Unknown, get(test_server_loc)})
+ end
+ end,
+ Child1 = spawn(Fun),
+ Mref1 = erlang:monitor(process, Child1),
+ receive
+ {Child1, wrote} ->
+ ok;
+ {'DOWN', Mref1, _, _, _} = Down1a ->
+ ?t:fail(Down1a)
+ end,
+ ?t:sleep(1000), % Just in case the file system is slow
+ {ok, Fd3} = ?FILE_MODULE:open(File, [read]),
+ eof = ?FILE_MODULE:read(Fd3, 1),
+ Child1 ! {Parent, continue, normal},
+ receive
+ {'DOWN', Mref1, process, Child1, normal} ->
+ ok;
+ {'DOWN', Mref1, _, _, _} = Down1b ->
+ ?t:fail(Down1b)
+ end,
+ ?t:sleep(1000), % Just in case the file system is slow
+ {ok, Data1} = ?FILE_MODULE:pread(Fd3, bof, Size+1),
+ ok = ?FILE_MODULE:close(Fd3),
%%
%% The same again, but this time with reason 'kill'.
- ?line Child2 = spawn(Fun),
- ?line Mref2 = erlang:monitor(process, Child2),
- ?line receive
- {Child2, wrote} ->
- ok;
- {'DOWN', Mref2, _, _, _} = Down2a ->
- ?t:fail(Down2a)
- end,
- ?line ?t:sleep(1000), % Just in case the file system is slow
- ?line {ok, Fd4} = ?FILE_MODULE:open(File, [read]),
- ?line case os:type() of
- vxworks ->
- io:format("Line ~p skipped on vxworks", [?LINE]);
- _ ->
- ?line eof = ?FILE_MODULE:read(Fd4, 1)
- end,
- ?line Child2 ! {Parent, continue, kill},
- ?line receive
- {'DOWN', Mref2, process, Child2, kill} ->
- ok;
- {'DOWN', Mref2, _, _, _} = Down2b ->
- ?t:fail(Down2b)
- end,
- ?line ?t:sleep(1000), % Just in case the file system is slow
- ?line eof = ?FILE_MODULE:pread(Fd4, bof, 1),
- ?line ok = ?FILE_MODULE:close(Fd4),
+ Child2 = spawn(Fun),
+ Mref2 = erlang:monitor(process, Child2),
+ receive
+ {Child2, wrote} ->
+ ok;
+ {'DOWN', Mref2, _, _, _} = Down2a ->
+ ?t:fail(Down2a)
+ end,
+ ?t:sleep(1000), % Just in case the file system is slow
+ {ok, Fd4} = ?FILE_MODULE:open(File, [read]),
+ eof = ?FILE_MODULE:read(Fd4, 1),
+ Child2 ! {Parent, continue, kill},
+ receive
+ {'DOWN', Mref2, process, Child2, kill} ->
+ ok;
+ {'DOWN', Mref2, _, _, _} = Down2b ->
+ ?t:fail(Down2b)
+ end,
+ ?t:sleep(1000), % Just in case the file system is slow
+ eof = ?FILE_MODULE:pread(Fd4, bof, 1),
+ ok = ?FILE_MODULE:close(Fd4),
%%
%% Test if file position works with delayed_write
- ?line {ok, Fd5} = ?FILE_MODULE:open(File, [raw, read, write,
- delayed_write]),
- ?line ok = ?FILE_MODULE:truncate(Fd5),
- ?line ok = ?FILE_MODULE:write(Fd5, [Data1|Data2]),
- ?line {ok, 0} = ?FILE_MODULE:position(Fd5, bof),
- ?line ok = ?FILE_MODULE:write(Fd5, [Data3]),
- ?line {ok, Data2} = ?FILE_MODULE:read(Fd5, Size+1),
- ?line {ok, 0} = ?FILE_MODULE:position(Fd5, bof),
- ?line Data3Data2 = Data3++Data2,
- ?line {ok, Data3Data2} = ?FILE_MODULE:read(Fd5, 2*Size+1),
- ?line ok = ?FILE_MODULE:close(Fd5),
+ {ok, Fd5} = ?FILE_MODULE:open(File, [raw, read, write,
+ delayed_write]),
+ ok = ?FILE_MODULE:truncate(Fd5),
+ ok = ?FILE_MODULE:write(Fd5, [Data1|Data2]),
+ {ok, 0} = ?FILE_MODULE:position(Fd5, bof),
+ ok = ?FILE_MODULE:write(Fd5, [Data3]),
+ {ok, Data2} = ?FILE_MODULE:read(Fd5, Size+1),
+ {ok, 0} = ?FILE_MODULE:position(Fd5, bof),
+ Data3Data2 = Data3++Data2,
+ {ok, Data3Data2} = ?FILE_MODULE:read(Fd5, 2*Size+1),
+ ok = ?FILE_MODULE:close(Fd5),
%%
- ?line [] = flush(),
- ?line ?t:timetrap_cancel(Dog),
- ?line case os:type() of
- vxworks ->
- {comment, "Some lines skipped on vxworks"};
- _ ->
- ok
- end.
+ [] = flush(),
+ ?t:timetrap_cancel(Dog),
+ ok.
pid2name(doc) -> "Tests file:pid2name/1.";
diff --git a/lib/kernel/test/file_name_SUITE.erl b/lib/kernel/test/file_name_SUITE.erl
index 53bcb1162d..40bde8a736 100644
--- a/lib/kernel/test/file_name_SUITE.erl
+++ b/lib/kernel/test/file_name_SUITE.erl
@@ -1,8 +1,9 @@
-module(file_name_SUITE).
+%% -*- coding: utf-8 -*-
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -74,7 +75,7 @@
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]).
+-export([normal/1,icky/1,very_icky/1,normalize/1,home_dir/1]).
init_per_testcase(_Func, Config) ->
@@ -88,7 +89,7 @@ end_per_testcase(_Func, Config) ->
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [normal, icky, very_icky, normalize].
+ [normal, icky, very_icky, normalize, home_dir].
groups() ->
[].
@@ -105,6 +106,54 @@ init_per_group(_GroupName, Config) ->
end_per_group(_GroupName, Config) ->
Config.
+home_dir(suite) ->
+ [];
+home_dir(doc) ->
+ ["Check that Erlang can be started with unicode named home directory"];
+home_dir(Config) when is_list(Config) ->
+ try
+ Name=[960,945,964,961,953,954],
+ Priv = ?config(priv_dir, Config),
+ UniMode = file:native_name_encoding() =/= latin1,
+ if
+ not UniMode ->
+ throw(need_unicode_mode);
+ true ->
+ ok
+ end,
+ NewHome=filename:join(Priv,Name),
+ file:make_dir(NewHome),
+ {SaveOldName,SaveOldValue} = case os:type() of
+ {win32,nt} ->
+ HomePath=re:replace(filename:nativename(NewHome),"^[a-zA-Z]:","",[{return,list},unicode]),
+ Save = os:getenv("HOMEPATH"),
+ os:putenv("HOMEPATH",HomePath),
+ {"HOMEPATH",Save};
+ {unix,_} ->
+ Save = os:getenv("HOME"),
+ os:putenv("HOME",NewHome),
+ {"HOME",Save};
+ _ ->
+ rm_rf(prim_file,NewHome),
+ throw(unsupported_os)
+ end,
+ try
+ {ok,Node} = test_server:start_node(test_unicode_homedir,slave,[{args,"-setcookie "++atom_to_list(erlang:get_cookie())}]),
+ test_server:stop_node(Node),
+ ok
+ after
+ os:putenv(SaveOldName,SaveOldValue),
+ rm_rf(prim_file,NewHome)
+ end
+ 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"};
+ throw:unsupported_os ->
+ io:format("Sorry, can only run on Unix/Windows.~n"),
+ {skipped,"Runs only on Unix/Windows"}
+ end.
+
normalize(suite) ->
[];
normalize(doc) ->
@@ -288,7 +337,7 @@ check_normal(Mod) ->
check_icky(Mod) ->
{ok,Dir} = Mod:get_cwd(),
try
- ?line true=(length("���") =:= 3),
+ ?line true=(length("åäö") =:= 3),
?line UniMode = file:native_name_encoding() =/= latin1,
?line make_icky_dir(Mod),
?line {ok, L0} = Mod:list_dir("."),
@@ -309,42 +358,42 @@ check_icky(Mod) ->
|| {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 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 {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"),
+ ?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 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:delete(<<"ååå">>),
?line {ok,[]} = Mod:list_dir("."),
?line ok = Mod:set_cwd(".."),
- ?line rm_r2(Mod,"���_dir");
+ ?line rm_r2(Mod,"åäö_dir");
false ->
ok
end,
- ?line chk_cre_dir(Mod,[{directory,treat_icky(<<"���_dir">>),icky_dir()}]),
+ ?line chk_cre_dir(Mod,[{directory,treat_icky(<<"åäö_dir">>),icky_dir()}]),
if
UniMode and (OS =/= win32) ->
- ?line {error,enoent} = Mod:set_cwd("���_dir");
+ ?line {error,enoent} = Mod:set_cwd("åäö_dir");
true ->
ok
end,
- ?line ok = Mod:set_cwd(treat_icky(<<"���_dir">>)),
+ ?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
@@ -352,22 +401,22 @@ check_icky(Mod) ->
?line true = BeginAt =/= NowAt2,
?line ok = Mod:set_cwd(".."),
?line {ok,BeginAt} = Mod:get_cwd(),
- ?line rm_r2(Mod,conv(treat_icky(<<"���_dir">>))),
+ ?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 = 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�">>));
+ ?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,
@@ -382,42 +431,42 @@ check_icky(Mod) ->
?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","åäö_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">>)),
+ ?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");
+ {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 {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 {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 = 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 {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">>)),
+ ?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),
@@ -427,7 +476,7 @@ check_icky(Mod) ->
check_very_icky(Mod) ->
{ok,Dir} = Mod:get_cwd(),
try
- ?line true=(length("���") =:= 3),
+ ?line true=(length("åäö") =:= 3),
?line UniMode = file:native_name_encoding() =/= latin1,
if
not UniMode ->
@@ -449,7 +498,7 @@ check_very_icky(Mod) ->
?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 {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),
@@ -466,16 +515,16 @@ check_very_icky(Mod) ->
?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 = 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�">>),
+ 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�">>);
+ ?line {error,enoent} = Mod:read_file(<<"nisseö">>),
+ ?line {error,enoent} = Mod:read_link_info(<<"nisseö">>);
false ->
ok
end,
@@ -492,10 +541,10 @@ check_very_icky(Mod) ->
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 {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 {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]),
@@ -526,9 +575,9 @@ check_very_icky(Mod) ->
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",
+ ?line SF3 = unicode:characters_to_binary("åäösubfil3",
file:native_name_encoding()),
- ?line SF2 = case treat_icky(<<"���subfil2">>) of
+ ?line SF2 = case treat_icky(<<"åäösubfil2">>) of
LF2 when is_list(LF2) ->
unicode:characters_to_binary(LF2,
file:native_name_encoding());
@@ -536,7 +585,7 @@ check_very_icky(Mod) ->
BF2
end,
?line Sorted = lists:sort([SF3,SF2]),
- ?line Sorted = lists:sort(filelib:wildcard("*",<<"���subdir2">>)),
+ ?line Sorted = lists:sort(filelib:wildcard("*",<<"åäösubdir2">>)),
ok
catch
throw:need_unicode_mode ->
@@ -696,26 +745,26 @@ hopeless_darwin() ->
icky_dir() ->
[{regular,"fil1","fil1"},
- {regular,"���2","���2"}] ++
+ {regular,"åäö2","åäö2"}] ++
case has_links() of
true ->
- [{regular,"���3","���2"},
- {symlink,"���4","���2"}];
+ [{regular,"åäö3","åäö2"},
+ {symlink,"åäö4","åäö2"}];
false ->
[]
end ++
- [{regular,treat_icky(<<"���5">>),"���5"}] ++
+ [{regular,treat_icky(<<"åäö5">>),"åäö5"}] ++
case has_links() of
true ->
- [{symlink,treat_icky(<<"���6">>),treat_icky(<<"���5">>)}];
+ [{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"}]}].
+ [{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"),
@@ -726,26 +775,26 @@ make_very_icky_dir(Mod) ->
very_icky_dir() ->
[{regular,"fil1","fil1"},
- {regular,[956,965,963,954,959,49],"���2"}] ++
+ {regular,[956,965,963,954,959,49],"åäö2"}] ++
case has_links() of
true ->
- [{regular,[956,965,963,954,959,50],"���2"},
+ [{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"}] ++
+ [{regular,treat_icky(<<"åäö5">>),"åäö5"}] ++
case has_links() of
true ->
- [{symlink,treat_icky(<<"���6">>),treat_icky(<<"���5">>)}];
+ [{symlink,treat_icky(<<"åäö6">>),treat_icky(<<"åäö5">>)}];
false ->
[]
end ++
- [{directory,treat_icky(<<"���subdir2">>),
- [{regular,treat_icky(<<"���subfil2">>),"���subfil12"},
- {regular,"���subfil3","���subfil13"}]},
+ [{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"}]}].
+ [{regular,[956,965,963,954,959]++"subfil1","åäösubfil1"}]}].
%% Some OS'es simply do not allow non UTF8 filenames
treat_icky(Bin) ->
diff --git a/lib/kernel/test/gen_sctp_SUITE.erl b/lib/kernel/test/gen_sctp_SUITE.erl
index 8f490b6643..2a886b2efc 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-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -31,14 +31,24 @@
[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,
- basic_stream/1, xfer_stream_min/1, peeloff/1, buffers/1]).
+ open_multihoming_ipv4_socket/1,
+ open_unihoming_ipv6_socket/1,
+ open_multihoming_ipv6_socket/1,
+ open_multihoming_ipv4_and_ipv6_socket/1,
+ basic_stream/1, xfer_stream_min/1, peeloff_active_once/1,
+ peeloff_active_true/1, buffers/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,
- basic_stream, xfer_stream_min, peeloff, buffers].
+ open_multihoming_ipv4_socket,
+ open_unihoming_ipv6_socket,
+ open_multihoming_ipv6_socket,
+ open_multihoming_ipv4_and_ipv6_socket,
+ basic_stream, xfer_stream_min, peeloff_active_once,
+ peeloff_active_true, buffers].
groups() ->
[].
@@ -915,23 +925,34 @@ do_from_other_process(Fun) ->
end.
+peeloff_active_once(doc) ->
+ "Peel off an SCTP stream socket ({active,once})";
+peeloff_active_once(suite) ->
+ [];
+
+peeloff_active_once(Config) ->
+ peeloff(Config, [{active,once}]).
-peeloff(doc) ->
- "Peel off an SCTP stream socket";
-peeloff(suite) ->
+peeloff_active_true(doc) ->
+ "Peel off an SCTP stream socket ({active,true})";
+peeloff_active_true(suite) ->
[];
-peeloff(Config) when is_list(Config) ->
+
+peeloff_active_true(Config) ->
+ peeloff(Config, [{active,true}]).
+
+peeloff(Config, SockOpts) when is_list(Config) ->
?line Addr = {127,0,0,1},
?line Stream = 0,
?line Timeout = 333,
- ?line S1 = socket_open([{ifaddr,Addr}], Timeout),
+ ?line S1 = socket_open([{ifaddr,Addr}|SockOpts], Timeout),
?line ?LOGVAR(S1),
?line P1 = socket_call(S1, get_port),
?line ?LOGVAR(P1),
?line Socket1 = socket_call(S1, get_socket),
?line ?LOGVAR(Socket1),
?line socket_call(S1, {listen,true}),
- ?line S2 = socket_open([{ifaddr,Addr}], Timeout),
+ ?line S2 = socket_open([{ifaddr,Addr}|SockOpts], Timeout),
?line ?LOGVAR(S2),
?line P2 = socket_call(S2, get_port),
?line ?LOGVAR(P2),
@@ -975,7 +996,7 @@ peeloff(Config) when is_list(Config) ->
socket_bailout([S1,S2])
end,
%%
- ?line S3 = socket_peeloff(Socket1, S1Ai, Timeout),
+ ?line S3 = socket_peeloff(Socket1, S1Ai, SockOpts, Timeout),
?line ?LOGVAR(S3),
?line P3_X = socket_call(S3, get_port),
?line ?LOGVAR(P3_X),
@@ -1105,11 +1126,204 @@ mk_data(N, Bytes, Bin) when N < Bytes ->
mk_data(_, _, Bin) ->
Bin.
+
+
+open_multihoming_ipv4_socket(doc) ->
+ "Test opening a multihoming ipv4 socket";
+open_multihoming_ipv4_socket(suite) ->
+ [];
+open_multihoming_ipv4_socket(Config) when is_list(Config) ->
+ ?line case get_addrs_by_family(inet, 2) of
+ {ok, [Addr1, Addr2]} ->
+ ?line do_open_and_connect([Addr1, Addr2], Addr1);
+ {error, Reason} ->
+ {skip, Reason}
+ end.
+
+open_unihoming_ipv6_socket(doc) ->
+ %% This test is mostly aimed to indicate
+ %% whether host has a non-working ipv6 setup
+ "Test opening a unihoming (non-multihoming) ipv6 socket";
+open_unihoming_ipv6_socket(suite) ->
+ [];
+open_unihoming_ipv6_socket(Config) when is_list(Config) ->
+ ?line case get_addrs_by_family(inet6, 1) of
+ {ok, [Addr]} ->
+ ?line do_open_and_connect([Addr], Addr);
+ {error, Reason} ->
+ {skip, Reason}
+ end.
+
+
+open_multihoming_ipv6_socket(doc) ->
+ "Test opening a multihoming ipv6 socket";
+open_multihoming_ipv6_socket(suite) ->
+ [];
+open_multihoming_ipv6_socket(Config) when is_list(Config) ->
+ ?line case get_addrs_by_family(inet6, 2) of
+ {ok, [Addr1, Addr2]} ->
+ ?line do_open_and_connect([Addr1, Addr2], Addr1);
+ {error, Reason} ->
+ {skip, Reason}
+ end.
+
+open_multihoming_ipv4_and_ipv6_socket(doc) ->
+ "Test opening a multihoming ipv6 socket with ipv4 and ipv6 addresses";
+open_multihoming_ipv4_and_ipv6_socket(suite) ->
+ [];
+open_multihoming_ipv4_and_ipv6_socket(Config) when is_list(Config) ->
+ ?line case get_addrs_by_family(inet_and_inet6, 2) of
+ {ok, [[InetAddr1, InetAddr2], [Inet6Addr1, Inet6Addr2]]} ->
+ %% Connect to the first address to test bind
+ ?line do_open_and_connect([InetAddr1, Inet6Addr1, InetAddr2],
+ InetAddr1),
+ ?line do_open_and_connect([Inet6Addr1, InetAddr1],
+ Inet6Addr1),
+
+ %% Connect an address, not the first,
+ %% to test sctp_bindx
+ ?line do_open_and_connect([Inet6Addr1, Inet6Addr2, InetAddr1],
+ Inet6Addr2),
+ ?line do_open_and_connect([Inet6Addr1, Inet6Addr2, InetAddr1],
+ InetAddr1);
+ {error, Reason} ->
+ {skip, Reason}
+ end.
+
+
+get_addrs_by_family(Family, NumAddrs) ->
+ case os:type() of
+ {unix,linux} ->
+ get_addrs_by_family_aux(Family, NumAddrs);
+ {unix,freebsd} ->
+ get_addrs_by_family_aux(Family, NumAddrs);
+ {unix,sunos} ->
+ case get_addrs_by_family_aux(Family, NumAddrs) of
+ {ok, [InetAddrs, Inet6Addrs]} when Family =:= inet_and_inet6 ->
+ %% Man page for sctp_bindx on Solaris says: "If sock is an
+ %% Internet Protocol Version 6 (IPv6) socket, addrs should
+ %% be an array of sockaddr_in6 structures containing IPv6
+ %% or IPv4-mapped IPv6 addresses."
+ {ok, [ipv4_map_addrs(InetAddrs), Inet6Addrs]};
+ {ok, Addrs} ->
+ {ok, Addrs};
+ {error, Reason} ->
+ {error, Reason}
+ end;
+ Os ->
+ Reason = if Family =:= inet_and_inet6 ->
+ f("Mixing ipv4 and ipv6 addresses for multihoming "
+ " has not been verified on ~p", [Os]);
+ true ->
+ f("Multihoming for ~p has not been verified on ~p",
+ [Family, Os])
+ end,
+ {error, Reason}
+ end.
+
+get_addrs_by_family_aux(Family, NumAddrs) when Family =:= inet;
+ Family =:= inet6 ->
+ ?line
+ case inet:getaddr(localhost, Family) of
+ {error,eafnosupport} ->
+ {skip, f("No support for ~p", Family)};
+ {ok, _} ->
+ ?line IfAddrs = ok(inet:getifaddrs()),
+ ?line case filter_addrs_by_family(IfAddrs, Family) of
+ Addrs when length(Addrs) >= NumAddrs ->
+ {ok, lists:sublist(Addrs, NumAddrs)};
+ [] ->
+ {error, f("Need ~p ~p address(es) found none~n",
+ [NumAddrs, Family])};
+ Addrs ->
+ {error,
+ f("Need ~p ~p address(es) found only ~p: ~p~n",
+ [NumAddrs, Family, length(Addrs), Addrs])}
+ end
+ end;
+get_addrs_by_family_aux(inet_and_inet6, NumAddrs) ->
+ ?line catch {ok, [case get_addrs_by_family_aux(Family, NumAddrs) of
+ {ok, Addrs} -> Addrs;
+ {error, Reason} -> throw({error, Reason})
+ end || Family <- [inet, inet6]]}.
+
+filter_addrs_by_family(IfAddrs, Family) ->
+ lists:flatten([[Addr || {addr, Addr} <- Info,
+ is_good_addr(Addr, Family)]
+ || {_IfName, Info} <- IfAddrs]).
+
+is_good_addr(Addr, inet) when tuple_size(Addr) =:= 4 ->
+ true;
+is_good_addr({0,0,0,0,0,16#ffff,_,_}, inet6) ->
+ false; %% ipv4 mapped
+is_good_addr({16#fe80,_,_,_,_,_,_,_}, inet6) ->
+ false; %% link-local
+is_good_addr(Addr, inet6) when tuple_size(Addr) =:= 8 ->
+ true;
+is_good_addr(_Addr, _Family) ->
+ false.
+
+ipv4_map_addrs(InetAddrs) ->
+ [begin
+ <<AB:16>> = <<A,B>>,
+ <<CD:16>> = <<C,D>>,
+ {0, 0, 0, 0, 0, 16#ffff, AB, CD}
+ end || {A,B,C,D} <- InetAddrs].
+
+f(F, A) ->
+ lists:flatten(io_lib:format(F, A)).
+
+do_open_and_connect(ServerAddresses, AddressToConnectTo) ->
+ ?line ServerFamily = get_family_by_addrs(ServerAddresses),
+ ?line io:format("Serving ~p addresses: ~p~n",
+ [ServerFamily, ServerAddresses]),
+ ?line S1 = ok(gen_sctp:open(0, [{ip,Addr} || Addr <- ServerAddresses] ++
+ [ServerFamily])),
+ ?line ok = gen_sctp:listen(S1, true),
+ ?line P1 = ok(inet:port(S1)),
+ ?line ClientFamily = get_family_by_addr(AddressToConnectTo),
+ ?line io:format("Connecting to ~p ~p~n",
+ [ClientFamily, AddressToConnectTo]),
+ ?line S2 = ok(gen_sctp:open(0, [ClientFamily])),
+ %% Verify client can connect
+ ?line #sctp_assoc_change{state=comm_up} =
+ ok(gen_sctp:connect(S2, AddressToConnectTo, P1, [])),
+ %% verify server side also receives comm_up from client
+ ?line recv_comm_up_eventually(S1),
+ ?line ok = gen_sctp:close(S2),
+ ?line ok = gen_sctp:close(S1).
+
+%% If at least one of the addresses is an ipv6 address, return inet6, else inet.
+get_family_by_addrs(Addresses) ->
+ ?line case lists:usort([get_family_by_addr(Addr) || Addr <- Addresses]) of
+ [inet, inet6] -> inet6;
+ [inet] -> inet;
+ [inet6] -> inet6
+ end.
+
+get_family_by_addr(Addr) when tuple_size(Addr) =:= 4 -> inet;
+get_family_by_addr(Addr) when tuple_size(Addr) =:= 8 -> inet6.
+
+recv_comm_up_eventually(S) ->
+ ?line case ok(gen_sctp:recv(S)) of
+ {_Addr, _Port, _Info, #sctp_assoc_change{state=comm_up}} ->
+ ok;
+ {_Addr, _Port, _Info, _OtherSctpMsg} ->
+ ?line recv_comm_up_eventually(S)
+ end.
+
%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% socket gen_server ultra light
-socket_open(SocketOpts, Timeout) ->
- Opts = [{type,seqpacket},{active,once},binary|SocketOpts],
+socket_open(SockOpts0, Timeout) ->
+ SockOpts =
+ case lists:keyfind(active,1,SockOpts0) of
+ false ->
+ [{active,once}|SockOpts0];
+ _ ->
+ SockOpts0
+ end,
+ Opts = [{type,seqpacket},binary|SockOpts],
Starter =
fun () ->
{ok,Socket} =
@@ -1118,8 +1332,8 @@ socket_open(SocketOpts, Timeout) ->
end,
s_start(Starter, Timeout).
-socket_peeloff(Socket, AssocId, Timeout) ->
- Opts = [{active,once},binary],
+socket_peeloff(Socket, AssocId, SocketOpts, Timeout) ->
+ Opts = [binary|SocketOpts],
Starter =
fun () ->
{ok,NewSocket} =
diff --git a/lib/kernel/test/gen_tcp_echo_SUITE.erl b/lib/kernel/test/gen_tcp_echo_SUITE.erl
index 5bbaeb02ad..94f95798a0 100644
--- a/lib/kernel/test/gen_tcp_echo_SUITE.erl
+++ b/lib/kernel/test/gen_tcp_echo_SUITE.erl
@@ -190,24 +190,19 @@ echo_test_1(SockOpts, EchoFun, Config0) ->
ok.
echo_packet(SockOpts, EchoFun, Opts) ->
- ?line Type =
- case lists:keysearch(type, 1, Opts) of
- {value, {type, T}} ->
- T;
- _ ->
- {value, {packet, T}} = lists:keysearch(packet, 1, SockOpts),
- T
- end,
+ Type = case lists:keysearch(type, 1, Opts) of
+ {value, {type, T}} ->
+ T;
+ _ ->
+ {value, {packet, T}} = lists:keysearch(packet, 1, SockOpts),
+ T
+ end,
%% Connect to the echo server.
- ?line EchoPort = ?config(echo_port, Opts),
- ?line {ok, Echo} = gen_tcp:connect(localhost, EchoPort, SockOpts),
+ EchoPort = ?config(echo_port, Opts),
+ {ok, Echo} = gen_tcp:connect(localhost, EchoPort, SockOpts),
- ?line SlowEcho =
- case os:type() of
- vxworks -> true;
- _ -> lists:member(slow_echo, Opts)
- end,
+ SlowEcho = lists:member(slow_echo, Opts),
case Type of
http ->
diff --git a/lib/kernel/test/gen_tcp_misc_SUITE.erl b/lib/kernel/test/gen_tcp_misc_SUITE.erl
index 1592399996..a72e76f813 100644
--- a/lib/kernel/test/gen_tcp_misc_SUITE.erl
+++ b/lib/kernel/test/gen_tcp_misc_SUITE.erl
@@ -42,12 +42,13 @@
killing_acceptor/1,killing_multi_acceptors/1,killing_multi_acceptors2/1,
several_accepts_in_one_go/1, accept_system_limit/1,
active_once_closed/1, send_timeout/1, send_timeout_active/1,
- otp_7731/1, zombie_sockets/1, otp_7816/1, otp_8102/1,
+ otp_7731/1, zombie_sockets/1, otp_7816/1, otp_8102/1, wrapping_oct/1,
otp_9389/1]).
%% Internal exports.
-export([sender/3, not_owner/1, passive_sockets_server/2, priority_server/1,
- otp_7731_server/1, zombie_server/2]).
+ oct_acceptor/1,
+ otp_7731_server/1, zombie_server/2, do_iter_max_socks/2]).
init_per_testcase(_Func, Config) when is_list(Config) ->
Dog = test_server:timetrap(test_server:seconds(240)),
@@ -75,6 +76,7 @@ all() ->
killing_acceptor, killing_multi_acceptors,
killing_multi_acceptors2, several_accepts_in_one_go, accept_system_limit,
active_once_closed, send_timeout, send_timeout_active, otp_7731,
+ wrapping_oct,
zombie_sockets, otp_7816, otp_8102, otp_9389].
groups() ->
@@ -340,39 +342,23 @@ no_accept(doc) ->
"a tcp_closed message."];
no_accept(suite) -> [];
no_accept(Config) when is_list(Config) ->
- case os:type() of
- vxworks ->
- {skip,"Too tough for vxworks"};
- _ ->
- no_accept2()
+ {ok, L} = gen_tcp:listen(0, []),
+ {ok, {_, Port}} = inet:sockname(L),
+ {ok, Client} = gen_tcp:connect(localhost, Port, []),
+ ok = gen_tcp:close(L),
+ receive
+ {tcp_closed, Client} ->
+ ok
+ after 5000 ->
+ ?line test_server:fail(never_closed)
+
end.
-no_accept2() ->
- ?line {ok, L} = gen_tcp:listen(0, []),
- ?line {ok, {_, Port}} = inet:sockname(L),
- ?line {ok, Client} = gen_tcp:connect(localhost, Port, []),
- ?line ok = gen_tcp:close(L),
- ?line receive
- {tcp_closed, Client} ->
- ok
- after 5000 ->
- ?line test_server:fail(never_closed)
-
- end.
-
close_with_pending_output(doc) ->
["Send several packets to a socket and close it. All packets should arrive ",
"to the other end."];
close_with_pending_output(suite) -> [];
close_with_pending_output(Config) when is_list(Config) ->
- case os:type() of
- vxworks ->
- {skipped,"Too tough for vxworks"};
- _ ->
- close_with_pending_output2()
- end.
-
-close_with_pending_output2() ->
?line {ok, L} = gen_tcp:listen(0, [binary, {active, false}]),
?line {ok, {_, Port}} = inet:sockname(L),
?line Packets = 16,
@@ -423,22 +409,16 @@ otp_3924(doc) ->
otp_3924(suite) -> [];
otp_3924(Config) when is_list(Config) ->
MaxDelay = (case has_superfluous_schedulers() of
- true -> 4;
- false -> 1
- end
- * case {erlang:system_info(debug_compiled),
- erlang:system_info(lock_checking)} of
- {true, _} -> 6;
- {_, true} -> 2;
- _ -> 1
- end * ?OTP_3924_MAX_DELAY),
- case os:type() of
- vxworks ->
-%% {skip,"Too tough for vxworks"};
- otp_3924_1(MaxDelay);
- _ ->
- otp_3924_1(MaxDelay)
- end.
+ true -> 4;
+ false -> 1
+ end
+ * case {erlang:system_info(debug_compiled),
+ erlang:system_info(lock_checking)} of
+ {true, _} -> 6;
+ {_, true} -> 2;
+ _ -> 1
+ end * ?OTP_3924_MAX_DELAY),
+ otp_3924_1(MaxDelay).
otp_3924_1(MaxDelay) ->
Dog = test_server:timetrap(test_server:seconds(240)),
@@ -559,26 +539,18 @@ otp_3924_sender(Receiver, Host, Port, Data) ->
data_before_close(doc) ->
["Tests that a huge amount of data can be received before a close."];
data_before_close(Config) when is_list(Config) ->
- case os:type() of
- vxworks ->
- {skip,"Too tough for vxworks"};
- _ ->
- data_before_close2()
- end.
-
-data_before_close2() ->
- ?line {ok, L} = gen_tcp:listen(0, [binary]),
- ?line {ok, {_, TcpPort}} = inet:sockname(L),
- ?line Bytes = 256*1024,
- ?line spawn_link(fun() -> huge_sender(TcpPort, Bytes) end),
- ?line {ok, A} = gen_tcp:accept(L),
- ?line case count_bytes_recv(A, 0) of
- {Bytes, Result} ->
- io:format("Result: ~p", [Result]);
- {Wrong, Result} ->
- io:format("Result: ~p", [Result]),
- test_server:fail({wrong_count, Wrong})
- end,
+ {ok, L} = gen_tcp:listen(0, [binary]),
+ {ok, {_, TcpPort}} = inet:sockname(L),
+ Bytes = 256*1024,
+ spawn_link(fun() -> huge_sender(TcpPort, Bytes) end),
+ {ok, A} = gen_tcp:accept(L),
+ case count_bytes_recv(A, 0) of
+ {Bytes, Result} ->
+ io:format("Result: ~p", [Result]);
+ {Wrong, Result} ->
+ io:format("Result: ~p", [Result]),
+ test_server:fail({wrong_count, Wrong})
+ end,
ok.
count_bytes_recv(Sock, Total) ->
@@ -611,32 +583,24 @@ get_status(Config) when is_list(Config) ->
?line {ok,{socket,Pid,_,_}} = gen_tcp:listen(5678,[]),
?line {status,Pid,_,_} = sys:get_status(Pid).
+-define(RECOVER_SLEEP, 60000).
+-define(RETRY_SLEEP, 15000).
+
iter_max_socks(doc) ->
["Open as many sockets as possible. Do this several times and check ",
"that we get the same number of sockets every time."];
iter_max_socks(Config) when is_list(Config) ->
- case os:type() of
- vxworks ->
- {skip,"Too tough for vxworks"};
- _ ->
- iter_max_socks2()
- end.
-
--define(RECOVER_SLEEP, 60000).
--define(RETRY_SLEEP, 15000).
+ N = 20,
+ %% Run on a different node in order to limit the effect if this test fails.
+ Dir = filename:dirname(code:which(?MODULE)),
+ {ok,Node} = test_server:start_node(test_iter_max_socks,slave,
+ [{args,"-pa " ++ Dir}]),
+ L = rpc:call(Node,?MODULE,do_iter_max_socks,[N, initalize]),
+ test_server:stop_node(Node),
-iter_max_socks2() ->
- ?line N =
- case os:type() of
- vxworks ->
- 10;
- _ ->
- 20
- end,
- L = do_iter_max_socks(N, initalize),
- ?line io:format("Result: ~p",[L]),
- ?line all_equal(L),
- ?line {comment, "Max sockets: " ++ integer_to_list(hd(L))}.
+ io:format("Result: ~p",[L]),
+ all_equal(L),
+ {comment, "Max sockets: " ++ integer_to_list(hd(L))}.
do_iter_max_socks(0, _) ->
[];
@@ -2619,3 +2583,71 @@ otp_9389_loop(S, OrigLinkHdr, State) ->
3000 ->
?line error({timeout,header})
end.
+
+wrapping_oct(doc) ->
+ "Check that 64bit octet counters work.";
+wrapping_oct(suite) ->
+ [];
+wrapping_oct(Config) when is_list(Config) ->
+ Dog = test_server:timetrap(test_server:seconds(600)),
+ {ok,Sock} = gen_tcp:listen(0,[{active,false},{mode,binary}]),
+ {ok,Port} = inet:port(Sock),
+ spawn_link(?MODULE,oct_acceptor,[Sock]),
+ Res = oct_datapump(Port,16#1FFFFFFFF),
+ gen_tcp:close(Sock),
+ test_server:timetrap_cancel(Dog),
+ ok = Res,
+ ok.
+
+oct_datapump(Port,N) ->
+ {ok,Sock} = gen_tcp:connect("localhost",Port,
+ [{active,false},{mode,binary}]),
+ oct_pump(Sock,N,binary:copy(<<$a:8>>,100000),0).
+
+oct_pump(S,N,_,_) when N =< 0 ->
+ gen_tcp:close(S),
+ ok;
+oct_pump(S,N,Bin,Last) ->
+ case gen_tcp:send(S,Bin) of
+ ok ->
+ {ok,Stat}=inet:getstat(S),
+ {_,R}=lists:keyfind(send_oct,1,Stat),
+ case (R < Last) of
+ true ->
+ io:format("ERROR (output) ~p < ~p~n",[R,Last]),
+ output_counter_error;
+ false ->
+ oct_pump(S,N-byte_size(Bin),Bin,R)
+ end;
+ _ ->
+ input_counter_error
+ end.
+
+
+oct_acceptor(Sock) ->
+ {ok,Data} = gen_tcp:accept(Sock),
+ oct_aloop(Data,0,0).
+
+oct_aloop(S,X,Times) ->
+ case gen_tcp:recv(S,0) of
+ {ok,_} ->
+ {ok,Stat}=inet:getstat(S),
+ {_,R}=lists:keyfind(recv_oct,1,Stat),
+ case (R < X) of
+ true ->
+ io:format("ERROR ~p < ~p~n",[R,X]),
+ gen_tcp:close(S),
+ input_counter_error;
+ false ->
+ case Times rem 16#FFFFF of
+ 0 ->
+ io:format("Read: ~p~n",[R]);
+ _ ->
+ ok
+ end,
+ oct_aloop(S,R,Times+1)
+ end;
+ _ ->
+ gen_tcp:close(S),
+ closed
+ end.
diff --git a/lib/kernel/test/gen_udp_SUITE.erl b/lib/kernel/test/gen_udp_SUITE.erl
index 2354f8accd..cd768813cf 100644
--- a/lib/kernel/test/gen_udp_SUITE.erl
+++ b/lib/kernel/test/gen_udp_SUITE.erl
@@ -1,7 +1,8 @@
+%% -*- coding: utf-8 -*-
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -442,7 +443,7 @@ open_fd(suite) ->
open_fd(doc) ->
["Test that the 'fd' option works"];
open_fd(Config) when is_list(Config) ->
- Msg = "Det g�r ont n�r knoppar brista. Varf�r skulle annars v�ren tveka?",
+ Msg = "Det gör ont när knoppar brista. Varför skulle annars våren tveka?",
Addr = {127,0,0,1},
{ok,S1} = gen_udp:open(0),
{ok,P2} = inet:port(S1),
diff --git a/lib/kernel/test/global_SUITE.erl b/lib/kernel/test/global_SUITE.erl
index 60035b50a0..9428a38660 100644
--- a/lib/kernel/test/global_SUITE.erl
+++ b/lib/kernel/test/global_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -91,27 +91,9 @@ 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.
@@ -135,8 +117,7 @@ end_per_testcase(_Case, Config) ->
?line write_high_level_trace(Config),
?line _ =
gen_server:call(global_name_server, high_level_trace_stop, infinity),
- ?line[global:unregister_name(N) || N <- global:registered_names(),
- N =/= test_server],
+ [global:unregister_name(N) || N <- global:registered_names()],
?line InitRegistered = ?registered,
?line Registered = registered(),
?line [io:format("~s local names: ~p~n", [What, N]) ||
@@ -168,7 +149,7 @@ end_per_testcase(_Case, Config) ->
register_1(suite) -> [];
register_1(Config) when is_list(Config) ->
Timeout = 15,
- ?line Dog = test_server:timetrap({seconds,Timeout}),
+ ct:timetrap({seconds,Timeout}),
init_high_level_trace(Timeout),
?line init_condition(Config),
P = spawn_link(?MODULE, lock_global, [self(), Config]),
@@ -195,7 +176,6 @@ register_1(Config) when is_list(Config) ->
?line _ = global:unregister_name(foo),
write_high_level_trace(Config),
?line init_condition(Config),
- ?line test_server:timetrap_cancel(Dog),
ok.
lock_global(Parent, Config) ->
@@ -238,7 +218,7 @@ lock_global(Parent, Config) ->
both_known_1(suite) -> [];
both_known_1(Config) when is_list(Config) ->
Timeout = 30,
- ?line Dog = test_server:timetrap({seconds,Timeout}),
+ ct:timetrap({seconds,Timeout}),
init_high_level_trace(Timeout),
?line init_condition(Config),
@@ -316,7 +296,6 @@ both_known_1(Config) when is_list(Config) ->
stop_node(Cp3),
?line init_condition(Config),
- ?line test_server:timetrap_cancel(Dog),
ok.
lost_unregister(suite) -> [];
@@ -324,7 +303,7 @@ lost_unregister(doc) ->
["OTP-6428. An unregistered name reappears."];
lost_unregister(Config) when is_list(Config) ->
Timeout = 30,
- ?line Dog = test_server:timetrap({seconds,Timeout}),
+ ct:timetrap({seconds,Timeout}),
init_high_level_trace(Timeout),
?line init_condition(Config),
@@ -361,7 +340,6 @@ lost_unregister(Config) when is_list(Config) ->
stop_node(B),
stop_node(C),
?line init_condition(Config),
- ?line test_server:timetrap_cancel(Dog),
ok.
-define(UNTIL_LOOP, 300).
@@ -448,7 +426,7 @@ lock_global2(Id, Parent) ->
names(suite) -> [];
names(Config) when is_list(Config) ->
Timeout = 30,
- ?line Dog = test_server:timetrap({seconds,Timeout}),
+ ct:timetrap({seconds,Timeout}),
init_high_level_trace(Timeout),
?line init_condition(Config),
?line OrigNames = global:registered_names(),
@@ -532,7 +510,6 @@ names(Config) when is_list(Config) ->
?line ?UNTIL(undefined =:= global:whereis_name(test)),
?line init_condition(Config),
- ?line test_server:timetrap_cancel(Dog),
ok.
names_hidden(suite) -> [];
@@ -541,7 +518,7 @@ names_hidden(doc) ->
"visible nodes."];
names_hidden(Config) when is_list(Config) ->
Timeout = 30,
- ?line Dog = test_server:timetrap({seconds,Timeout}),
+ ct:timetrap({seconds,Timeout}),
init_high_level_trace(Timeout),
?line init_condition(Config),
?line OrigNames = global:registered_names(),
@@ -639,13 +616,12 @@ names_hidden(Config) when is_list(Config) ->
stop_node(Cp3),
?line init_condition(Config),
- ?line test_server:timetrap_cancel(Dog),
ok.
locks(suite) -> [];
locks(Config) when is_list(Config) ->
Timeout = 30,
- ?line Dog = test_server:timetrap({seconds,Timeout}),
+ ct:timetrap({seconds,Timeout}),
init_high_level_trace(Timeout),
?line init_condition(Config),
?line {ok, Cp1} = start_node(cp1, Config),
@@ -750,7 +726,6 @@ locks(Config) when is_list(Config) ->
?line test_server:sleep(10),
?line init_condition(Config),
- ?line test_server:timetrap_cancel(Dog),
ok.
@@ -760,7 +735,7 @@ locks_hidden(doc) ->
"visible nodes."];
locks_hidden(Config) when is_list(Config) ->
Timeout = 30,
- ?line Dog = test_server:timetrap({seconds,Timeout}),
+ ct:timetrap({seconds,Timeout}),
init_high_level_trace(Timeout),
?line init_condition(Config),
?line OrigNodes = nodes(),
@@ -833,14 +808,13 @@ locks_hidden(Config) when is_list(Config) ->
stop_node(Cp3),
?line init_condition(Config),
- ?line test_server:timetrap_cancel(Dog),
ok.
bad_input(suite) -> [];
bad_input(Config) when is_list(Config) ->
Timeout = 15,
- ?line Dog = test_server:timetrap({seconds,Timeout}),
+ ct:timetrap({seconds,Timeout}),
init_high_level_trace(Timeout),
?line init_condition(Config),
Pid = whereis(global_name_server),
@@ -854,13 +828,12 @@ bad_input(Config) when is_list(Config) ->
?line {'EXIT', _} = (catch global:trans({id, self()}, {m,f}, [node()], -1)),
?line Pid = whereis(global_name_server),
?line init_condition(Config),
- ?line test_server:timetrap_cancel(Dog),
ok.
names_and_locks(suite) -> [];
names_and_locks(Config) when is_list(Config) ->
Timeout = 30,
- ?line Dog = test_server:timetrap({seconds,Timeout}),
+ ct:timetrap({seconds,Timeout}),
init_high_level_trace(Timeout),
?line init_condition(Config),
?line OrigNames = global:registered_names(),
@@ -922,7 +895,6 @@ names_and_locks(Config) when is_list(Config) ->
stop_node(Cp3),
?line init_condition(Config),
- ?line test_server:timetrap_cancel(Dog),
ok.
lock_die(suite) -> [];
@@ -930,7 +902,7 @@ lock_die(doc) ->
["OTP-6341. Remove locks using monitors."];
lock_die(Config) when is_list(Config) ->
Timeout = 30,
- ?line Dog = test_server:timetrap({seconds,Timeout}),
+ ct:timetrap({seconds,Timeout}),
init_high_level_trace(Timeout),
?line init_condition(Config),
?line OrigNames = global:registered_names(),
@@ -964,7 +936,6 @@ lock_die(Config) when is_list(Config) ->
stop_node(Cp1),
stop_node(Cp2),
?line init_condition(Config),
- ?line test_server:timetrap_cancel(Dog),
ok.
name_die(suite) -> [];
@@ -972,7 +943,7 @@ name_die(doc) ->
["OTP-6341. Remove names using monitors."];
name_die(Config) when is_list(Config) ->
Timeout = 30,
- ?line Dog = test_server:timetrap({seconds,Timeout}),
+ ct:timetrap({seconds,Timeout}),
init_high_level_trace(Timeout),
?line init_condition(Config),
?line OrigNames = global:registered_names(),
@@ -1027,7 +998,6 @@ name_die(Config) when is_list(Config) ->
write_high_level_trace(Config),
stop_nodes(Cps),
?line init_condition(Config),
- ?line test_server:timetrap_cancel(Dog),
ok.
kill_pid(Pid, File, Config) ->
@@ -1040,7 +1010,7 @@ basic_partition(doc) ->
["Tests that two partitioned networks exchange correct info."];
basic_partition(Config) when is_list(Config) ->
Timeout = 30,
- ?line Dog = test_server:timetrap({seconds,Timeout}),
+ ct:timetrap({seconds,Timeout}),
init_high_level_trace(Timeout),
?line init_condition(Config),
?line OrigNames = global:registered_names(),
@@ -1088,7 +1058,6 @@ basic_partition(Config) when is_list(Config) ->
stop_node(Cp2),
stop_node(Cp3),
?line init_condition(Config),
- ?line test_server:timetrap_cancel(Dog),
ok.
basic_name_partition(suite) ->
@@ -1099,7 +1068,7 @@ basic_name_partition(doc) ->
"during connect phase are handled correctly."];
basic_name_partition(Config) when is_list(Config) ->
Timeout = 60,
- ?line Dog = test_server:timetrap({seconds,Timeout}),
+ ct:timetrap({seconds,Timeout}),
init_high_level_trace(Timeout),
?line init_condition(Config),
?line OrigNames = global:registered_names(),
@@ -1167,7 +1136,6 @@ basic_name_partition(Config) when is_list(Config) ->
stop_node(Cp2),
stop_node(Cp3),
?line init_condition(Config),
- ?line test_server:timetrap_cancel(Dog),
ok.
%Peer nodes cp0 - cp6 are started. Break apart the connections from
@@ -1190,7 +1158,7 @@ advanced_partition(doc) ->
"partitioned networks connect."];
advanced_partition(Config) when is_list(Config) ->
Timeout = 60,
- ?line Dog = test_server:timetrap({seconds,Timeout}),
+ ct:timetrap({seconds,Timeout}),
init_high_level_trace(Timeout),
?line init_condition(Config),
?line OrigNames = global:registered_names(),
@@ -1278,7 +1246,6 @@ advanced_partition(Config) when is_list(Config) ->
stop_node(Cp5),
stop_node(Cp6),
?line init_condition(Config),
- ?line test_server:timetrap_cancel(Dog),
ok.
%Peer nodes cp0 - cp6 are started, and partitioned just like in
@@ -1297,7 +1264,7 @@ stress_partition(doc) ->
"go up/down a bit."];
stress_partition(Config) when is_list(Config) ->
Timeout = 90,
- ?line Dog = test_server:timetrap({seconds,Timeout}),
+ ct:timetrap({seconds,Timeout}),
init_high_level_trace(Timeout),
?line init_condition(Config),
?line OrigNames = global:registered_names(),
@@ -1377,7 +1344,6 @@ stress_partition(Config) when is_list(Config) ->
stop_node(Cp7),
stop_node(Cp8),
?line init_condition(Config),
- ?line test_server:timetrap_cancel(Dog),
ok.
@@ -1408,7 +1374,7 @@ ring(doc) ->
"Make sure that there's just one winner."];
ring(Config) when is_list(Config) ->
Timeout = 60,
- ?line Dog = test_server:timetrap({seconds,Timeout}),
+ ct:timetrap({seconds,Timeout}),
init_high_level_trace(Timeout),
?line init_condition(Config),
?line OrigNames = global:registered_names(),
@@ -1486,7 +1452,6 @@ ring(Config) when is_list(Config) ->
stop_node(Cp7),
stop_node(Cp8),
?line init_condition(Config),
- ?line test_server:timetrap_cancel(Dog),
ok.
simple_ring(suite) ->
@@ -1499,7 +1464,7 @@ simple_ring(doc) ->
"Make sure that there's just one winner."];
simple_ring(Config) when is_list(Config) ->
Timeout = 60,
- ?line Dog = test_server:timetrap({seconds,Timeout}),
+ ct:timetrap({seconds,Timeout}),
init_high_level_trace(Timeout),
?line init_condition(Config),
?line OrigNames = global:registered_names(),
@@ -1565,7 +1530,6 @@ simple_ring(Config) when is_list(Config) ->
stop_node(Cp4),
stop_node(Cp5),
?line init_condition(Config),
- ?line test_server:timetrap_cancel(Dog),
ok.
line(suite) ->
@@ -1576,7 +1540,7 @@ line(doc) ->
"Make sure that there's just one winner."];
line(Config) when is_list(Config) ->
Timeout = 60,
- ?line Dog = test_server:timetrap({seconds,Timeout}),
+ ct:timetrap({seconds,Timeout}),
init_high_level_trace(Timeout),
?line init_condition(Config),
?line OrigNames = global:registered_names(),
@@ -1655,7 +1619,6 @@ line(Config) when is_list(Config) ->
stop_node(Cp7),
stop_node(Cp8),
?line init_condition(Config),
- ?line test_server:timetrap_cancel(Dog),
ok.
@@ -1669,7 +1632,7 @@ simple_line(doc) ->
"Make sure that there's just one winner."];
simple_line(Config) when is_list(Config) ->
Timeout = 60,
- ?line Dog = test_server:timetrap({seconds,Timeout}),
+ ct:timetrap({seconds,Timeout}),
init_high_level_trace(Timeout),
?line init_condition(Config),
?line OrigNames = global:registered_names(),
@@ -1735,7 +1698,6 @@ simple_line(Config) when is_list(Config) ->
stop_node(Cp4),
stop_node(Cp5),
?line init_condition(Config),
- ?line test_server:timetrap_cancel(Dog),
ok.
otp_1849(suite) -> [];
@@ -1743,7 +1705,7 @@ otp_1849(doc) ->
["Test ticket: Global should keep track of all pids that set the same lock."];
otp_1849(Config) when is_list(Config) ->
Timeout = 30,
- ?line Dog = test_server:timetrap({seconds,Timeout}),
+ ct:timetrap({seconds,Timeout}),
init_high_level_trace(Timeout),
?line init_condition(Config),
?line {ok, Cp1} = start_node(cp1, Config),
@@ -1822,7 +1784,6 @@ otp_1849(Config) when is_list(Config) ->
stop_node(Cp2),
stop_node(Cp3),
?line init_condition(Config),
- ?line test_server:timetrap_cancel(Dog),
ok.
@@ -1840,7 +1801,7 @@ otp_3162(Config) when is_list(Config) ->
do_otp_3162(StartFun, Config) ->
Timeout = 30,
- ?line Dog = test_server:timetrap({seconds,Timeout}),
+ ct:timetrap({seconds,Timeout}),
init_high_level_trace(Timeout),
?line init_condition(Config),
?line [Cp1, Cp2, Cp3] = StartFun(),
@@ -1860,16 +1821,16 @@ do_otp_3162(StartFun, Config) ->
?line ?UNTIL
([Cp3] =:= lists:sort(rpc:call(Cp1, erlang, nodes, [])) -- [node()]),
- ?line ?UNTIL([kalle, test_server, vera] =:=
+ ?UNTIL([kalle, vera] =:=
lists:sort(rpc:call(Cp1, global, registered_names, []))),
?line ?UNTIL
([Cp3] =:= lists:sort(rpc:call(Cp2, erlang, nodes, [])) -- [node()]),
- ?line ?UNTIL([stina, test_server, vera] =:=
+ ?UNTIL([stina, vera] =:=
lists:sort(rpc:call(Cp2, global, registered_names, []))),
?line ?UNTIL
([Cp1, Cp2] =:=
lists:sort(rpc:call(Cp3, erlang, nodes, [])) -- [node()]),
- ?line ?UNTIL([kalle, stina, test_server, vera] =:=
+ ?UNTIL([kalle, stina, vera] =:=
lists:sort(rpc:call(Cp3, global, registered_names, []))),
?line pong = rpc:call(Cp2, net_adm, ping, [Cp1]),
@@ -1880,17 +1841,17 @@ do_otp_3162(StartFun, Config) ->
?line
?UNTIL(begin
NN = lists:sort(rpc:call(Cp1, global, registered_names, [])),
- [kalle, stina, test_server, vera] =:= NN
+ [kalle, stina, vera] =:= NN
end),
?line ?UNTIL
([Cp1, Cp3] =:=
lists:sort(rpc:call(Cp2, erlang, nodes, [])) -- [node()]),
- ?line ?UNTIL([kalle, stina, test_server, vera] =:=
+ ?UNTIL([kalle, stina, vera] =:=
lists:sort(rpc:call(Cp2, global, registered_names, []))),
?line ?UNTIL
([Cp1, Cp2] =:=
lists:sort(rpc:call(Cp3, erlang, nodes, [])) -- [node()]),
- ?line ?UNTIL([kalle, stina, test_server, vera] =:=
+ ?UNTIL([kalle, stina, vera] =:=
lists:sort(rpc:call(Cp3, global, registered_names, []))),
write_high_level_trace(Config),
@@ -1898,7 +1859,6 @@ do_otp_3162(StartFun, Config) ->
stop_node(Cp2),
stop_node(Cp3),
?line init_condition(Config),
- ?line test_server:timetrap_cancel(Dog),
ok.
@@ -1907,7 +1867,7 @@ otp_5640(doc) ->
["OTP-5640. 'allow' multiple names for registered processes."];
otp_5640(Config) when is_list(Config) ->
Timeout = 25,
- ?line Dog = test_server:timetrap(test_server:seconds(Timeout)),
+ ct:timetrap({seconds,Timeout}),
init_high_level_trace(Timeout),
init_condition(Config),
?line {ok, B} = start_node(b, Config),
@@ -1965,7 +1925,6 @@ otp_5640(Config) when is_list(Config) ->
write_high_level_trace(Config),
stop_node(B),
?line init_condition(Config),
- ?line test_server:timetrap_cancel(Dog),
ok.
otp_5640_proc(_Parent) ->
@@ -1979,7 +1938,7 @@ otp_5737(doc) ->
["OTP-5737. set_lock/3 and trans/4 accept Retries = 0."];
otp_5737(Config) when is_list(Config) ->
Timeout = 25,
- ?line Dog = test_server:timetrap({seconds,Timeout}),
+ ct:timetrap({seconds,Timeout}),
init_high_level_trace(Timeout),
?line init_condition(Config),
@@ -2000,7 +1959,6 @@ otp_5737(Config) when is_list(Config) ->
write_high_level_trace(Config),
?line init_condition(Config),
- ?line test_server:timetrap_cancel(Dog),
ok.
otp_6931(suite) -> [];
@@ -2025,7 +1983,7 @@ simple_disconnect(suite) -> [];
simple_disconnect(doc) -> ["OTP-5563. Disconnected nodes (not partitions)"];
simple_disconnect(Config) when is_list(Config) ->
Timeout = 30,
- ?line Dog = test_server:timetrap({seconds,Timeout}),
+ ct:timetrap({seconds,Timeout}),
init_high_level_trace(Timeout),
?line init_condition(Config),
?line OrigNames = global:registered_names(),
@@ -2075,7 +2033,6 @@ simple_disconnect(Config) when is_list(Config) ->
write_high_level_trace(Config),
stop_nodes(Cps),
?line init_condition(Config),
- ?line test_server:timetrap_cancel(Dog),
ok.
%% Not used right now.
@@ -2118,7 +2075,7 @@ simple_resolve(suite) -> [];
simple_resolve(doc) -> ["OTP-5563. Partitions and names."];
simple_resolve(Config) when is_list(Config) ->
Timeout = 360,
- ?line Dog = test_server:timetrap({seconds,Timeout}),
+ ct:timetrap({seconds,Timeout}),
init_high_level_trace(Timeout),
?line init_condition(Config),
?line OrigNames = global:registered_names(),
@@ -2245,7 +2202,6 @@ simple_resolve(Config) when is_list(Config) ->
write_high_level_trace(Config),
stop_nodes(Cps),
?line init_condition(Config),
- ?line test_server:timetrap_cancel(Dog),
ok.
simple_resolve2(suite) -> [];
@@ -2255,7 +2211,7 @@ simple_resolve2(Config) when is_list(Config) ->
%% always work to re-start z_2. "Cannot be a global bug."
Timeout = 30,
- ?line Dog = test_server:timetrap({seconds,Timeout}),
+ ct:timetrap({seconds,Timeout}),
init_high_level_trace(Timeout),
?line init_condition(Config),
?line OrigNames = global:registered_names(),
@@ -2283,7 +2239,6 @@ simple_resolve2(Config) when is_list(Config) ->
write_high_level_trace(Config),
stop_nodes(Cps), % Not all nodes may be present, but it works anyway.
?line init_condition(Config),
- ?line test_server:timetrap_cancel(Dog),
ok.
simple_resolve3(suite) -> [];
@@ -2292,7 +2247,7 @@ simple_resolve3(Config) when is_list(Config) ->
%% Continuation of simple_resolve.
Timeout = 30,
- ?line Dog = test_server:timetrap({seconds,Timeout}),
+ ct:timetrap({seconds,Timeout}),
init_high_level_trace(Timeout),
?line init_condition(Config),
?line OrigNames = global:registered_names(),
@@ -2320,7 +2275,6 @@ simple_resolve3(Config) when is_list(Config) ->
write_high_level_trace(Config),
stop_nodes(Cps), % Not all nodes may be present, but it works anyway.
?line init_condition(Config),
- ?line test_server:timetrap_cancel(Dog),
ok.
res({Res,Resolver}, [N1, A2, Z2], Cf) ->
@@ -2504,7 +2458,7 @@ leftover_name(suite) -> [];
leftover_name(doc) -> ["OTP-5563. Bug: nodedown while synching."];
leftover_name(Config) when is_list(Config) ->
Timeout = 30,
- ?line Dog = test_server:timetrap({seconds,Timeout}),
+ ct:timetrap({seconds,Timeout}),
init_high_level_trace(Timeout),
?line init_condition(Config),
?line OrigNames = global:registered_names(),
@@ -2565,7 +2519,6 @@ leftover_name(Config) when is_list(Config) ->
write_high_level_trace(Config),
stop_nodes(Cps),
?line init_condition(Config),
- ?line test_server:timetrap_cancel(Dog),
ok.
%% Runs on n_1
@@ -2604,7 +2557,7 @@ re_register_name(Config) when is_list(Config) ->
%% occupied by links, that's all.
%% Later: now monitors are checked.
Timeout = 15,
- ?line Dog = test_server:timetrap({seconds,Timeout}),
+ ct:timetrap({seconds,Timeout}),
init_high_level_trace(Timeout),
?line init_condition(Config),
Me = self(),
@@ -2618,7 +2571,6 @@ re_register_name(Config) when is_list(Config) ->
receive {Pid2, MonitoredBy2} -> [_] = MonitoredBy2 end,
?line _ = global:unregister_name(name),
?line init_condition(Config),
- ?line test_server:timetrap_cancel(Dog),
ok.
proc(Parent) ->
@@ -2652,7 +2604,7 @@ do_name_exit(StartFun, Version, Config) ->
%% The current release uses monitors so this test is not so relevant.
Timeout = 60,
- ?line Dog = test_server:timetrap({seconds,Timeout}),
+ ct:timetrap({seconds,Timeout}),
init_high_level_trace(Timeout),
?line init_condition(Config),
?line OrigNames = global:registered_names(),
@@ -2692,7 +2644,6 @@ do_name_exit(StartFun, Version, Config) ->
write_high_level_trace(Config),
stop_nodes(Cps),
?line init_condition(Config),
- ?line test_server:timetrap_cancel(Dog),
ok.
long_lock(Parent) ->
@@ -2709,7 +2660,7 @@ external_nodes(suite) -> [];
external_nodes(doc) -> ["OTP-5563. External nodes (cnodes)."];
external_nodes(Config) when is_list(Config) ->
Timeout = 30,
- ?line Dog = test_server:timetrap({seconds,Timeout}),
+ ct:timetrap({seconds,Timeout}),
init_high_level_trace(Timeout),
?line init_condition(Config),
?line OrigNames = global:registered_names(),
@@ -2793,7 +2744,6 @@ external_nodes(Config) when is_list(Config) ->
?line ?UNTIL(length(get_ext_names()) =:= 0),
?line init_condition(Config),
- ?line test_server:timetrap_cancel(Dog),
ok.
get_ext_names() ->
@@ -2845,8 +2795,8 @@ many_nodes(suite) ->
many_nodes(doc) ->
["OTP-5770. Start many nodes. Make them connect at the same time."];
many_nodes(Config) when is_list(Config) ->
- Timeout = 180,
- ?line Dog = test_server:timetrap({seconds,Timeout}),
+ Timeout = 240,
+ ct:timetrap({seconds,Timeout}),
init_high_level_trace(Timeout),
?line init_condition(Config),
?line OrigNames = global:registered_names(),
@@ -2902,7 +2852,6 @@ many_nodes(Config) when is_list(Config) ->
write_high_level_trace(Config),
?line stop_nodes(Cps),
?line init_condition(Config),
- ?line test_server:timetrap_cancel(Dog),
Diff = Time2 - Time,
Return = lists:flatten(io_lib:format("~w nodes took ~w ms",
[N_cps, Diff])),
@@ -2988,7 +2937,7 @@ sync_0(doc) ->
["OTP-5770. sync/0."];
sync_0(Config) when is_list(Config) ->
Timeout = 180,
- ?line Dog = test_server:timetrap({seconds,Timeout}),
+ ct:timetrap({seconds,Timeout}),
init_high_level_trace(Timeout),
?line init_condition(Config),
@@ -3013,7 +2962,6 @@ sync_0(Config) when is_list(Config) ->
stop_nodes(Cps),
?line init_condition(Config),
- ?line test_server:timetrap_cancel(Dog),
ok.
start_and_sync([]) ->
@@ -3031,7 +2979,7 @@ global_groups_change(suite) -> [];
global_groups_change(doc) -> ["Test change of global_groups parameter."];
global_groups_change(Config) ->
Timeout = 90,
- ?line Dog = test_server:timetrap({seconds,Timeout}),
+ ct:timetrap({seconds,Timeout}),
init_high_level_trace(Timeout),
?line init_condition(Config),
?line M = from($@, atom_to_list(node())),
@@ -3376,7 +3324,6 @@ global_groups_change(Config) ->
stop_node(CpE),
?line init_condition(Config),
- ?line test_server:timetrap_cancel(Dog),
ok.
sync_and_wait(Node) ->
@@ -3855,7 +3802,7 @@ start_node_rel(Name0, Rel, Config) ->
Name = node_name(Name0, Config),
{Release, Compat} = case Rel of
this ->
- {[this], "+R8"};
+ {[this], ""};
Rel when is_atom(Rel) ->
{[{release, atom_to_list(Rel)}], ""};
RelList ->
@@ -3919,7 +3866,7 @@ global_lost_nodes(doc) ->
["Tests that locally loaded nodes do not loose contact with other nodes."];
global_lost_nodes(Config) when is_list(Config) ->
Timeout = 60,
- Dog = test_server:timetrap({seconds,Timeout}),
+ ct:timetrap({seconds,Timeout}),
init_high_level_trace(Timeout),
?line init_condition(Config),
@@ -3943,7 +3890,6 @@ global_lost_nodes(Config) when is_list(Config) ->
?line stop_node(Node1),
?line stop_node(Node2),
?line init_condition(Config),
- ?line test_server:timetrap_cancel(Dog),
ok.
global_load(MyName, OtherNode, OtherName) ->
@@ -3994,7 +3940,7 @@ mass_death(doc) ->
["Tests the simultaneous death of many processes with registered names"];
mass_death(Config) when is_list(Config) ->
Timeout = 90,
- ?line Dog = test_server:timetrap({seconds,Timeout}),
+ ct:timetrap({seconds,Timeout}),
init_high_level_trace(Timeout),
?line init_condition(Config),
?line OrigNames = global:registered_names(),
@@ -4023,9 +3969,9 @@ mass_death(Config) when is_list(Config) ->
{H,M,S} = time(),
io:format("Started probing: ~.4.0w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w~n",
[YYYY,MM,DD,H,M,S]),
- wait_mass_death(Dog, Nodes, OrigNames, erlang:now(), Config).
+ wait_mass_death(Nodes, OrigNames, erlang:now(), Config).
-wait_mass_death(Dog, Nodes, OrigNames, Then, Config) ->
+wait_mass_death(Nodes, OrigNames, Then, Config) ->
?line Names = global:registered_names(),
?line
case Names--OrigNames of
@@ -4036,12 +3982,11 @@ wait_mass_death(Dog, Nodes, OrigNames, Then, Config) ->
stop_node(Node)
end, Nodes),
?line init_condition(Config),
- ?line test_server:timetrap_cancel(Dog),
{comment,lists:flatten(io_lib:format("~.3f s~n", [T/1000.0]))};
Ndiff ->
?line io:format("Ndiff: ~p~n", [Ndiff]),
?line test_server:sleep(1000),
- ?line wait_mass_death(Dog, Nodes, OrigNames, Then, Config)
+ ?line wait_mass_death(Nodes, OrigNames, Then, Config)
end.
mass_spawn([]) ->
@@ -4190,7 +4135,7 @@ init_condition(Config) ->
{"Global Locks (ETS)", global_locks},
{"Global Pid Names (ETS)", global_pid_names},
{"Global Pid Ids (ETS)", global_pid_ids}]],
- ?UNTIL([test_server] =:= global:registered_names()),
+ ?UNTIL([] =:= global:registered_names()),
?UNTIL([] =:= nodes()),
?UNTIL([node()] =:= get_known(node())),
ok.
@@ -4213,7 +4158,7 @@ garbage_messages(suite) ->
[];
garbage_messages(Config) when is_list(Config) ->
Timeout = 25,
- ?line Dog = test_server:timetrap({seconds,Timeout}),
+ ct:timetrap({seconds,Timeout}),
init_high_level_trace(Timeout),
?line init_condition(Config),
?line [Slave] = start_nodes([garbage_messages], slave, Config),
@@ -4233,7 +4178,6 @@ garbage_messages(Config) when is_list(Config) ->
write_high_level_trace(Config),
?line stop_node(Slave),
?line init_condition(Config),
- ?line test_server:timetrap_cancel(Dog),
ok.
wait_for_ready_net(Config) ->
diff --git a/lib/kernel/test/heart_SUITE.erl b/lib/kernel/test/heart_SUITE.erl
index 233e438dc9..320b23bea1 100644
--- a/lib/kernel/test/heart_SUITE.erl
+++ b/lib/kernel/test/heart_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -22,7 +22,10 @@
-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, get_cmd/1,
+ reboot/1,
+ node_start_immediately_after_crash/1,
+ node_start_soon_after_crash/1,
+ set_cmd/1, clear_cmd/1, get_cmd/1,
dont_drop/1, kill_pid/1]).
-export([init_per_testcase/2, end_per_testcase/2]).
@@ -38,15 +41,15 @@ init_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),"@"))),
- case NNam of
- heart_test ->
- ?t:format(1, "WARNING: Killed ~p~n", [X]),
- rpc:cast(X, erlang, halt, []);
- _ ->
- ok
- end
- end, Nodes),
+ NNam = list_to_atom(hd(string:tokens(atom_to_list(X),"@"))),
+ case NNam of
+ heart_test ->
+ ?t:format(1, "WARNING: Killed ~p~n", [X]),
+ rpc:cast(X, erlang, halt, []);
+ _ ->
+ ok
+ end
+ end, Nodes),
Dog=?config(watchdog, Config),
test_server:timetrap_cancel(Dog).
@@ -57,8 +60,13 @@ end_per_testcase(_Func, Config) ->
%%-----------------------------------------------------------------
suite() -> [{ct_hooks,[ts_install_cth]}].
-all() ->
- [start, restart, reboot, set_cmd, clear_cmd, get_cmd, kill_pid].
+all() -> [
+ start, restart, reboot,
+ node_start_immediately_after_crash,
+ node_start_soon_after_crash,
+ set_cmd, clear_cmd, get_cmd,
+ kill_pid
+ ].
groups() ->
[].
@@ -75,22 +83,27 @@ init_per_suite(Config) when is_list(Config) ->
{win32, windows} ->
{skipped, "No use to run on Windows 95/98"};
_ ->
- Config
+ ignore_cores:init(Config)
end.
end_per_suite(Config) when is_list(Config) ->
- Config.
+ ignore_cores:fini(Config).
+
start_check(Type, Name) ->
+ start_check(Type, Name, []).
+start_check(Type, Name, Envs) ->
Args = case ?t:os_type() of
- {win32,_} -> "-heart -env HEART_COMMAND no_reboot";
- _ -> "-heart"
- end,
+ {win32,_} ->
+ "-heart " ++ env_encode([{"HEART_COMMAND", no_reboot}|Envs]);
+ _ ->
+ "-heart " ++ env_encode(Envs)
+ end,
{ok, Node} = case Type of
- loose ->
- loose_node:start(Name, Args, ?DEFAULT_TIMEOUT_SECS);
- _ ->
- ?t:start_node(Name, Type, [{args, Args}])
- end,
+ loose ->
+ loose_node:start(Name, Args, ?DEFAULT_TIMEOUT_SECS);
+ _ ->
+ ?t:start_node(Name, Type, [{args, Args}])
+ end,
erlang:monitor_node(Node, true),
case rpc:call(Node, erlang, whereis, [heart]) of
Pid when is_pid(Pid) ->
@@ -103,21 +116,19 @@ start_check(Type, Name) ->
start(doc) -> [];
start(suite) -> {req, [{time, 10}]};
start(Config) when is_list(Config) ->
- ?line {ok, Node} = start_check(slave, heart_test),
- ?line rpc:call(Node, init, reboot, []),
+ {ok, Node} = start_check(slave, heart_test),
+ rpc:call(Node, init, reboot, []),
receive
- {nodedown, Node} ->
- ok
- after 2000 ->
- test_server:fail(node_not_closed)
+ {nodedown, Node} -> ok
+ after 2000 -> test_server:fail(node_not_closed)
end,
test_server:sleep(5000),
- ?line case net_adm:ping(Node) of
- pang ->
- ok;
- _ ->
- test_server:fail(node_rebooted)
- end,
+ case net_adm:ping(Node) of
+ pang ->
+ ok;
+ _ ->
+ test_server:fail(node_rebooted)
+ end,
test_server:stop_node(Node).
%% Also test fixed bug in R1B (it was not possible to
@@ -125,6 +136,10 @@ start(Config) when is_list(Config) ->
%% Slave executes erlang:halt() on master nodedown.
%% Therefore the slave process has to be killed
%% before restart.
+
+%% restart
+%% Purpose:
+%% Check that a node is up and running after a init:restart/0
restart(doc) -> [];
restart(suite) ->
case ?t:os_type() of
@@ -134,8 +149,8 @@ restart(suite) ->
{skip, "Only run on unix and win32"}
end;
restart(Config) when is_list(Config) ->
- ?line {ok, Node} = start_check(loose, heart_test),
- ?line rpc:call(Node, init, restart, []),
+ {ok, Node} = start_check(loose, heart_test),
+ rpc:call(Node, init, restart, []),
receive
{nodedown, Node} ->
ok
@@ -143,32 +158,21 @@ restart(Config) when is_list(Config) ->
test_server:fail(node_not_closed)
end,
test_server:sleep(5000),
-
- ?line case net_adm:ping(Node) of
- pong ->
- erlang:monitor_node(Node, true),
- ?line rpc:call(Node, init, stop, []),
- receive
- {nodedown, Node} ->
- ok
- after 2000 ->
- test_server:fail(node_not_closed2)
- end,
- ok;
- _ ->
- test_server:fail(node_not_restarted)
- end,
+ node_check_up_down(Node, 2000),
loose_node:stop(Node).
+%% reboot
+%% Purpose:
+%% Check that a node is up and running after a init:reboot/0
reboot(doc) -> [];
reboot(suite) -> {req, [{time, 10}]};
reboot(Config) when is_list(Config) ->
{ok, Node} = start_check(slave, heart_test),
- ?line ok = rpc:call(Node, heart, set_cmd,
+ ok = rpc:call(Node, heart, set_cmd,
[atom_to_list(lib:progname()) ++
" -noshell -heart " ++ name(Node) ++ "&"]),
- ?line rpc:call(Node, init, reboot, []),
+ rpc:call(Node, init, reboot, []),
receive
{nodedown, Node} ->
ok
@@ -176,44 +180,142 @@ reboot(Config) when is_list(Config) ->
test_server:fail(node_not_closed)
end,
test_server:sleep(5000),
- ?line case net_adm:ping(Node) of
- pong ->
- erlang:monitor_node(Node, true),
- ?line rpc:call(Node, init, reboot, []),
- receive
- {nodedown, Node} ->
- ok
- after 2000 ->
- test_server:fail(node_not_closed2)
- end,
- ok;
- _ ->
- test_server:fail(node_not_rebooted)
- end,
+ node_check_up_down(Node, 2000),
ok.
+%% node_start_immediately_after_crash
+%% Purpose:
+%% Check that a node is up and running after a crash.
+%% This test exhausts the atom table on the remote node.
+%% ERL_CRASH_DUMP_SECONDS=0 will force beam not to dump an erl_crash.dump.
+%% May currently dump core in beam debug build due to lock-order violation
+%% This should be removed when a non-lockad information retriever is implemented
+%% for crash dumps
+node_start_immediately_after_crash(suite) -> {req, [{time, 10}]};
+node_start_immediately_after_crash(Config) when is_list(Config) ->
+ Config2 = ignore_cores:setup(?MODULE, node_start_immediately_after_crash, Config, true),
+ try
+ node_start_immediately_after_crash_test(Config2)
+ after
+ ignore_cores:restore(Config2)
+ end.
+
+
+node_start_immediately_after_crash_test(Config) when is_list(Config) ->
+ {ok, Node} = start_check(loose, heart_test_imm, [{"ERL_CRASH_DUMP_SECONDS", "0"}]),
+
+ ok = rpc:call(Node, heart, set_cmd,
+ [atom_to_list(lib:progname()) ++
+ " -noshell -heart " ++ name(Node) ++ "&"]),
+
+ Mod = exhaust_atoms,
+
+ Code = generate(Mod, [], [
+ "do() -> "
+ " Set = lists:seq($a,$z), "
+ " [ list_to_atom([A,B,C,D,E]) || "
+ " A <- Set, B <- Set, C <- Set, E <- Set, D <- Set ]."
+ ]),
+
+ %% crash it with atom exhaustion
+ rpc:call(Node, erlang, load_module, [Mod, Code]),
+ rpc:cast(Node, Mod, do, []),
+
+ T0 = now(),
+
+ receive {nodedown, Node} ->
+ test_server:format("Took ~.2f s. for node to go down~n", [timer:now_diff(now(), T0)/1000000]),
+ ok
+ %% timeout is very liberal here. nodedown is received in about 1 s. on linux (palantir)
+ %% and in about 10 s. on solaris (carcharoth)
+ after (15000*test_server:timetrap_scale_factor()) -> test_server:fail(node_not_closed)
+ end,
+ test_server:sleep(3000),
+ node_check_up_down(Node, 2000),
+ loose_node:stop(Node).
+
+%% node_start_soon_after_crash
+%% Purpose:
+%% Check that a node is up and running after a crash.
+%% This test exhausts the atom table on the remote node.
+%% ERL_CRASH_DUMP_SECONDS=10 will force beam
+%% to only dump an erl_crash.dump for 10 seconds.
+%% May currently dump core in beam debug build due to lock-order violation
+%% This should be removed when a non-lockad information retriever is implemented
+%% for crash dumps
+node_start_soon_after_crash(suite) -> {req, [{time, 10}]};
+node_start_soon_after_crash(Config) when is_list(Config) ->
+ Config2 = ignore_cores:setup(?MODULE, node_start_soon_after_crash, Config, true),
+ try
+ node_start_soon_after_crash_test(Config2)
+ after
+ ignore_cores:restore(Config2)
+ end.
+
+node_start_soon_after_crash_test(Config) when is_list(Config) ->
+ {ok, Node} = start_check(loose, heart_test_soon, [{"ERL_CRASH_DUMP_SECONDS", "10"}]),
+
+ ok = rpc:call(Node, heart, set_cmd,
+ [atom_to_list(lib:progname()) ++
+ " -noshell -heart " ++ name(Node) ++ "&"]),
+
+ Mod = exhaust_atoms,
+
+ Code = generate(Mod, [], [
+ "do() -> "
+ " Set = lists:seq($a,$z), "
+ " [ list_to_atom([A,B,C,D,E]) || "
+ " A <- Set, B <- Set, C <- Set, E <- Set, D <- Set ]."
+ ]),
+
+ %% crash it with atom exhaustion
+ rpc:call(Node, erlang, load_module, [Mod, Code]),
+ rpc:cast(Node, Mod, do, []),
+
+ receive {nodedown, Node} -> ok
+ after (15000*test_server:timetrap_scale_factor()) -> test_server:fail(node_not_closed)
+ end,
+ test_server:sleep(20000),
+ node_check_up_down(Node, 15000),
+ loose_node:stop(Node).
+
+
+node_check_up_down(Node, Tmo) ->
+ case net_adm:ping(Node) of
+ pong ->
+ erlang:monitor_node(Node, true),
+ rpc:call(Node, init, reboot, []),
+ receive
+ {nodedown, Node} -> ok
+ after Tmo ->
+ test_server:fail(node_not_closed2)
+ end;
+ _ ->
+ test_server:fail(node_not_rebooted)
+ end.
+
%% Only tests bad command, correct behaviour is tested in reboot/1.
set_cmd(suite) -> [];
set_cmd(Config) when is_list(Config) ->
- ?line {ok, Node} = start_check(slave, heart_test),
+ {ok, Node} = start_check(slave, heart_test),
Cmd = wrong_atom,
- ?line {error, {bad_cmd, Cmd}} = rpc:call(Node, heart, set_cmd, [Cmd]),
+ {error, {bad_cmd, Cmd}} = rpc:call(Node, heart, set_cmd, [Cmd]),
Cmd1 = lists:duplicate(2047, $a),
- ?line {error, {bad_cmd, Cmd1}} = rpc:call(Node, heart, set_cmd, [Cmd1]),
+ {error, {bad_cmd, Cmd1}} = rpc:call(Node, heart, set_cmd, [Cmd1]),
Cmd2 = lists:duplicate(28, $a),
- ?line ok = rpc:call(Node, heart, set_cmd, [Cmd2]),
+ ok = rpc:call(Node, heart, set_cmd, [Cmd2]),
Cmd3 = lists:duplicate(2000, $a),
- ?line ok = rpc:call(Node, heart, set_cmd, [Cmd3]),
+ ok = rpc:call(Node, heart, set_cmd, [Cmd3]),
stop_node(Node),
ok.
clear_cmd(suite) -> {req,[{time,15}]};
clear_cmd(Config) when is_list(Config) ->
- ?line {ok, Node} = start_check(slave, heart_test),
- ?line ok = rpc:call(Node, heart, set_cmd,
+ {ok, Node} = start_check(slave, heart_test),
+ ok = rpc:call(Node, heart, set_cmd,
[atom_to_list(lib:progname()) ++
" -noshell -heart " ++ name(Node) ++ "&"]),
- ?line rpc:call(Node, init, reboot, []),
+ rpc:call(Node, init, reboot, []),
receive
{nodedown, Node} ->
ok
@@ -221,16 +323,16 @@ clear_cmd(Config) when is_list(Config) ->
test_server:fail(node_not_closed)
end,
test_server:sleep(5000),
- ?line case net_adm:ping(Node) of
- pong ->
- erlang:monitor_node(Node, true);
- _ ->
- test_server:fail(node_not_rebooted)
- end,
- ?line ok = rpc:call(Node, heart, set_cmd,
+ case net_adm:ping(Node) of
+ pong ->
+ erlang:monitor_node(Node, true);
+ _ ->
+ test_server:fail(node_not_rebooted)
+ end,
+ ok = rpc:call(Node, heart, set_cmd,
["erl -noshell -heart " ++ name(Node) ++ "&"]),
- ?line ok = rpc:call(Node, heart, clear_cmd, []),
- ?line rpc:call(Node, init, reboot, []),
+ ok = rpc:call(Node, heart, clear_cmd, []),
+ rpc:call(Node, init, reboot, []),
receive
{nodedown, Node} ->
ok
@@ -238,20 +340,20 @@ clear_cmd(Config) when is_list(Config) ->
test_server:fail(node_not_closed)
end,
test_server:sleep(5000),
- ?line case net_adm:ping(Node) of
- pang ->
- ok;
- _ ->
- test_server:fail(node_rebooted)
- end,
+ case net_adm:ping(Node) of
+ pang ->
+ ok;
+ _ ->
+ test_server:fail(node_rebooted)
+ end,
ok.
get_cmd(suite) -> [];
get_cmd(Config) when is_list(Config) ->
- ?line {ok, Node} = start_check(slave, heart_test),
+ {ok, Node} = start_check(slave, heart_test),
Cmd = "test",
- ?line ok = rpc:call(Node, heart, set_cmd, [Cmd]),
- ?line {ok, Cmd} = rpc:call(Node, heart, get_cmd, []),
+ ok = rpc:call(Node, heart, set_cmd, [Cmd]),
+ {ok, Cmd} = rpc:call(Node, heart, get_cmd, []),
stop_node(Node),
ok.
@@ -266,65 +368,56 @@ dont_drop(doc) ->
"set just before halt on very high I/O load."];
dont_drop(Config) when is_list(Config) ->
%%% Have to do it some times to make it happen...
- case os:type() of
- vxworks ->
- {comment, "No use to run with slaves on other nodes..."};
- _ ->
- [ok,ok,ok,ok,ok,ok,ok,ok,ok,ok] = do_dont_drop(Config,10),
- ok
- end.
+ [ok,ok,ok,ok,ok,ok,ok,ok,ok,ok] = do_dont_drop(Config,10),
+ ok.
-do_dont_drop(_,0) ->
- [];
+do_dont_drop(_,0) -> [];
do_dont_drop(Config,N) ->
%% Name of first slave node
- ?line NN1 = atom_to_list(?MODULE) ++ "slave_1",
+ NN1 = atom_to_list(?MODULE) ++ "slave_1",
%% Name of node started by heart on failure
- ?line NN2 = atom_to_list(?MODULE) ++ "slave_2",
+ NN2 = atom_to_list(?MODULE) ++ "slave_2",
%% Name of node started by heart on success
- ?line NN3 = atom_to_list(?MODULE) ++ "slave_3",
- ?line Host = hd(tl(string:tokens(atom_to_list(node()),"@"))),
+ NN3 = atom_to_list(?MODULE) ++ "slave_3",
+ Host = hd(tl(string:tokens(atom_to_list(node()),"@"))),
%% The initial heart command
- ?line FirstCmd = erl() ++ name(NN2 ++ "@" ++ Host),
+ FirstCmd = erl() ++ name(NN2 ++ "@" ++ Host),
%% Separated the parameters to start_node_run for clarity...
- ?line Name = list_to_atom(NN1),
- ?line Env = [{"HEART_COMMAND", FirstCmd}],
- ?line Func = "start_heart_stress",
- ?line Arg = NN3 ++ "@" ++ Host ++ " " ++
+ Name = list_to_atom(NN1),
+ Env = [{"HEART_COMMAND", FirstCmd}],
+ Func = "start_heart_stress",
+ Arg = NN3 ++ "@" ++ Host ++ " " ++
filename:join(?config(data_dir, Config), "simple_echo"),
- ?line start_node_run(Name,Env,Func,Arg),
- ?line case wait_for_any_of(list_to_atom(NN2 ++ "@" ++ Host),
- list_to_atom(NN3 ++ "@" ++ Host)) of
- 2 ->
- ?line [ok | do_dont_drop(Config,N-1)];
- _ ->
- ?line false
- end.
+ start_node_run(Name,Env,Func,Arg),
+ case wait_for_any_of(list_to_atom(NN2 ++ "@" ++ Host),
+ list_to_atom(NN3 ++ "@" ++ Host)) of
+ 2 ->
+ [ok | do_dont_drop(Config,N-1)];
+ _ ->
+ false
+ end.
wait_for_any_of(N1,N2) ->
- ?line wait_for_any_of(N1,N2,45).
+ wait_for_any_of(N1,N2,45).
wait_for_any_of(_N1,_N2,0) ->
- ?line false;
+ false;
wait_for_any_of(N1,N2,Times) ->
- ?line receive
- after 1000 ->
- ?line ok
- end,
- ?line case net_adm:ping(N1) of
- pang ->
- ?line case net_adm:ping(N2) of
- pang ->
- ?line wait_for_any_of(N1,N2,Times - 1);
- pong ->
- ?line rpc:call(N2,init,stop,[]),
- ?line 2
- end;
- pong ->
- ?line rpc:call(N1,init,stop,[]),
- ?line 1
- end.
+ receive after 1000 -> ok end,
+ case net_adm:ping(N1) of
+ pang ->
+ case net_adm:ping(N2) of
+ pang ->
+ wait_for_any_of(N1,N2,Times - 1);
+ pong ->
+ rpc:call(N2,init,stop,[]),
+ 2
+ end;
+ pong ->
+ rpc:call(N1,init,stop,[]),
+ 1
+ end.
kill_pid(suite) ->
@@ -333,13 +426,7 @@ kill_pid(doc) ->
["Tests that heart kills the old erlang node before executing ",
"heart command."];
kill_pid(Config) when is_list(Config) ->
- %%% Have to do it some times to make it happen...
- case os:type() of
- vxworks ->
- {comment, "No use to run with slaves on other nodes..."};
- _ ->
- ok = do_kill_pid(Config)
- end.
+ ok = do_kill_pid(Config).
do_kill_pid(_Config) ->
Name = heart_test,
@@ -347,9 +434,7 @@ do_kill_pid(_Config) ->
{ok,Node} = start_node_run(Name,Env,suicide_by_heart,[]),
ok = wait_for_node(Node,15),
erlang:monitor_node(Node, true),
- receive
- {nodedown,Node} ->
- ok
+ receive {nodedown,Node} -> ok
after 30000 ->
false
end.
@@ -357,23 +442,16 @@ do_kill_pid(_Config) ->
wait_for_node(_,0) ->
false;
wait_for_node(Node,N) ->
- receive
- after 1000 ->
- ok
- end,
+ receive after 1000 -> ok end,
case net_adm:ping(Node) of
- pong ->
- ok;
- pang ->
- wait_for_node(Node,N-1)
+ pong -> ok;
+ pang -> wait_for_node(Node,N-1)
end.
erl() ->
case os:type() of
- {win32,_} ->
- "werl ";
- _ ->
- "erl "
+ {win32,_} -> "werl ";
+ _ -> "erl "
end.
name(Node) when is_list(Node) -> name(Node,[]);
@@ -390,15 +468,13 @@ name([H|T], Name) ->
name(T, [H|Name]).
-atom_conv(A) when is_atom(A) ->
- atom_to_list(A);
-atom_conv(A) when is_list(A) ->
- A.
+enc(A) when is_atom(A) -> atom_to_list(A);
+enc(A) when is_binary(A) -> binary_to_list(A);
+enc(A) when is_list(A) -> A.
-env_conv([]) ->
- [];
-env_conv([{X,Y}|T]) ->
- atom_conv(X) ++ " \"" ++ atom_conv(Y) ++ "\" " ++ env_conv(T).
+env_encode([]) -> [];
+env_encode([{X,Y}|T]) ->
+ "-env " ++ enc(X) ++ " \"" ++ enc(Y) ++ "\" " ++ env_encode(T).
%%%
%%% Starts a node and runs a function in this
@@ -409,12 +485,12 @@ env_conv([{X,Y}|T]) ->
%%% Argument is the argument(s) to send through erl -s
%%%
start_node_run(Name, Env, Function, Argument) ->
- ?line PA = filename:dirname(code:which(?MODULE)),
- ?line Params = "-heart -env " ++ env_conv(Env) ++ " -pa " ++ PA ++
- " -s " ++
- atom_conv(?MODULE) ++ " " ++ atom_conv(Function) ++ " " ++
- atom_conv(Argument),
- ?line start_node(Name, Params).
+ PA = filename:dirname(code:which(?MODULE)),
+ Params = "-heart " ++ env_encode(Env) ++ " -pa " ++ PA ++
+ " -s " ++
+ enc(?MODULE) ++ " " ++ enc(Function) ++ " " ++
+ enc(Argument),
+ start_node(Name, Params).
start_node(Name, Param) ->
test_server:start_node(Name, slave, [{args, Param}]).
@@ -480,3 +556,24 @@ suicide_by_heart() ->
{makaronipudding} ->
sallad
end.
+
+
+%% generate a module from binary
+generate(Module, Attributes, FunStrings) ->
+ FunForms = function_forms(FunStrings),
+ Forms = [
+ {attribute,1,module,Module},
+ {attribute,2,export,[FA || {FA,_} <- FunForms]}
+ ] ++ [{attribute, 3, A, V}|| {A, V} <- Attributes] ++
+ [ Function || {_, Function} <- FunForms],
+ {ok, Module, Bin} = compile:forms(Forms),
+ Bin.
+
+
+function_forms([]) -> [];
+function_forms([S|Ss]) ->
+ {ok, Ts,_} = erl_scan:string(S),
+ {ok, Form} = erl_parse:parse_form(Ts),
+ Fun = element(3, Form),
+ Arity = element(4, Form),
+ [{{Fun,Arity}, Form}|function_forms(Ss)].
diff --git a/lib/kernel/test/heart_SUITE_data/simple_echo.c b/lib/kernel/test/heart_SUITE_data/simple_echo.c
index 0093dbce9b..a92bb8af95 100644
--- a/lib/kernel/test/heart_SUITE_data/simple_echo.c
+++ b/lib/kernel/test/heart_SUITE_data/simple_echo.c
@@ -2,11 +2,7 @@
#include <stdlib.h>
#include <string.h>
-#ifdef VXWORKS
-int simple_echo(void){
-#else
int main(void){
-#endif
int x;
while((x = getchar()) != EOF){
putchar(x);
@@ -14,4 +10,3 @@ int main(void){
}
return 0;
}
-
diff --git a/lib/kernel/test/ignore_cores.erl b/lib/kernel/test/ignore_cores.erl
new file mode 100644
index 0000000000..8b1ac0fe6c
--- /dev/null
+++ b/lib/kernel/test/ignore_cores.erl
@@ -0,0 +1,158 @@
+%%
+%% %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/kernel/test/inet_SUITE.erl b/lib/kernel/test/inet_SUITE.erl
index 7241b093d0..e5e1794514 100644
--- a/lib/kernel/test/inet_SUITE.erl
+++ b/lib/kernel/test/inet_SUITE.erl
@@ -37,7 +37,8 @@
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]).
+ getif_ifr_name_overflow/1,getservbyname_overflow/1, getifaddrs/1,
+ parse_strict_address/1]).
-export([get_hosts/1, get_ipv6_hosts/1, parse_hosts/1, parse_address/1,
kill_gethost/0, parallell_gethost/0]).
@@ -52,7 +53,7 @@ all() ->
t_gethostnative, gethostnative_parallell, cname_loop,
gethostnative_debug_level, gethostnative_soft_restart,
getif, getif_ifr_name_overflow, getservbyname_overflow,
- getifaddrs].
+ getifaddrs, parse_strict_address].
groups() ->
[{parse, [], [parse_hosts, parse_address]}].
@@ -66,7 +67,7 @@ required(v6) ->
{require, test_dummy_ipv6_host}];
required(hosts) ->
case os:type() of
- {OS, _} when OS =:= win32; OS =:= vxworks ->
+ {OS, _} when OS =:= win32 ->
[{require, hardcoded_hosts},
{require, hardcoded_ipv6_hosts}];
_Else ->
@@ -582,16 +583,16 @@ parse_address(Config) when is_list(Config) ->
"fe80::198.168.0.",
"fec0::fFfF:127.0.0.1."],
t_parse_address
- (ipv6_address,
+ (parse_ipv6_address,
V6Strict++V6Sloppy++V6Err++V4Err),
t_parse_address
- (ipv6strict_address,
+ (parse_ipv6strict_address,
V6Strict++V6Err++V4Err++[S || {_,S} <- V6Sloppy]),
t_parse_address
- (ipv4_address,
+ (parse_ipv4_address,
V4Strict++V4Sloppy++V4Err++V6Err++[S || {_,S} <- V6Strict]),
t_parse_address
- (ipv4strict_address,
+ (parse_ipv4strict_address,
V4Strict++V4Err++V6Err++[S || {_,S} <- V4Sloppy++V6Strict]).
t_parse_address(Func, []) ->
@@ -599,14 +600,16 @@ t_parse_address(Func, []) ->
ok;
t_parse_address(Func, [{Addr,String}|L]) ->
io:format("~p = ~p.~n", [Addr,String]),
- {ok,Addr} = inet_parse:Func(String),
+ {ok,Addr} = inet:Func(String),
t_parse_address(Func, L);
t_parse_address(Func, [String|L]) ->
io:format("~p.~n", [String]),
- {error,einval} = inet_parse:Func(String),
+ {error,einval} = inet:Func(String),
t_parse_address(Func, L).
-
+parse_strict_address(Config) when is_list(Config) ->
+ {ok, Ipv4} = inet:parse_strict_address("127.0.0.1"),
+ {ok, Ipv6} = inet:parse_strict_address("c11:0c22:5c33:c440:55c0:c66c:77:0088").
t_gethostnative(suite) ->[];
t_gethostnative(doc) ->[];
@@ -614,17 +617,12 @@ t_gethostnative(Config) when is_list(Config) ->
%% this will result in 26 bytes sent which causes problem in Windows
%% if the port-program has not assured stdin to be read in BINARY mode
%% OTP-2555
- case os:type() of
- vxworks ->
- {skipped, "VxWorks has no native gethostbyname()"};
- _ ->
- ?line case inet_gethost_native:gethostbyname(
- "a23456789012345678901234") of
- {error,notfound} ->
- ?line ok;
- {error,no_data} ->
- ?line ok
- end
+ ?line case inet_gethost_native:gethostbyname(
+ "a23456789012345678901234") of
+ {error,notfound} ->
+ ?line ok;
+ {error,no_data} ->
+ ?line ok
end.
gethostnative_parallell(suite) ->
diff --git a/lib/kernel/test/inet_sockopt_SUITE.erl b/lib/kernel/test/inet_sockopt_SUITE.erl
index 087ae6055b..75496ce745 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-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -53,6 +53,8 @@
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,
+ ipv6_v6only_udp/1, ipv6_v6only_tcp/1, ipv6_v6only_sctp/1,
+ use_ipv6_v6only_udp/1,
type_errors/1]).
-export([init_per_testcase/2, end_per_testcase/2]).
@@ -64,6 +66,8 @@ all() ->
[simple, loop_all, simple_raw, simple_raw_getbin,
doc_examples_raw, doc_examples_raw_getbin, large_raw,
large_raw_getbin, combined, combined_getbin,
+ ipv6_v6only_udp, ipv6_v6only_tcp, ipv6_v6only_sctp,
+ use_ipv6_v6only_udp,
type_errors].
groups() ->
@@ -127,7 +131,7 @@ loop_all(Config) when is_list(Config) ->
io_lib:format("Non mandatory failed:~w",
[Failed]))}
end.
-
+
simple_raw(suite) -> [];
@@ -461,6 +465,153 @@ do_combined(Config,Binary) when is_list(Config) ->
ok
end.
+
+
+ipv6_v6only_udp(suite) -> [];
+ipv6_v6only_udp(doc) -> "Test socket option ipv6_v6only for UDP";
+ipv6_v6only_udp(Config) when is_list(Config) ->
+ ipv6_v6only(Config, gen_udp).
+
+ipv6_v6only_tcp(suite) -> [];
+ipv6_v6only_tcp(doc) -> "Test socket option ipv6_v6only for TCP";
+ipv6_v6only_tcp(Config) when is_list(Config) ->
+ ipv6_v6only(Config, gen_tcp).
+
+ipv6_v6only_sctp(suite) -> [];
+ipv6_v6only_sctp(doc) -> "Test socket option ipv6_v6only for SCTP";
+ipv6_v6only_sctp(Config) when is_list(Config) ->
+ ipv6_v6only(Config, gen_sctp).
+
+ipv6_v6only(Config, Module) when is_list(Config) ->
+ ?line case ipv6_v6only_open(Module, []) of
+ {ok,S1} ->
+ ?line case inet:getopts(S1, [ipv6_v6only]) of
+ {ok,[{ipv6_v6only,Default}]}
+ when is_boolean(Default) ->
+ ?line ok =
+ ipv6_v6only_close(Module, S1),
+ ?line ipv6_v6only(Config, Module, Default);
+ {ok,[]} ->
+ ?line io:format("Not implemented.~n", []),
+ %% This list of OS:es where the option is
+ %% supposed to be not implemented is just
+ %% a guess, and may grow with time.
+ ?line case {os:type(),os:version()} of
+ {{unix,linux},{2,M,_}}
+ when M =< 4 -> ok
+ end,
+ %% At least this should work
+ ?line {ok,S2} =
+ ipv6_v6only_open(
+ Module,
+ [{ipv6_v6only,true}]),
+ ?line ok =
+ ipv6_v6only_close(Module, S2)
+ end;
+ {error,_} ->
+ {skipped,"Socket type not supported"}
+ end.
+
+ipv6_v6only(Config, Module, Default) when is_list(Config) ->
+ ?line io:format("Default ~w.~n", [Default]),
+ ?line {ok,S1} =
+ ipv6_v6only_open(Module, [{ipv6_v6only,Default}]),
+ ?line {ok,[{ipv6_v6only,Default}]} =
+ inet:getopts(S1, [ipv6_v6only]),
+ ?line ok =
+ ipv6_v6only_close(Module, S1),
+ ?line NotDefault = not Default,
+ ?line case ipv6_v6only_open(Module, [{ipv6_v6only,NotDefault}]) of
+ {ok,S2} ->
+ ?line io:format("Read-write.~n", []),
+ ?line {ok,[{ipv6_v6only,NotDefault}]} =
+ inet:getopts(S2, [ipv6_v6only]),
+ ok;
+ {error,einval} ->
+ ?line io:format("Read-only.~n", []),
+ %% This option is known to be read-only and true
+ %% on Windows and OpenBSD
+ ?line case os:type() of
+ {unix,openbsd} when Default =:= true -> ok;
+ {win32,_} when Default =:= true -> ok
+ end
+ end.
+
+ipv6_v6only_open(Module, Opts) ->
+ Module:case Module of
+ gen_tcp -> listen;
+ _ -> open
+ end(0, [inet6|Opts]).
+
+ipv6_v6only_close(Module, Socket) ->
+ Module:close(Socket).
+
+
+use_ipv6_v6only_udp(suite) -> [];
+use_ipv6_v6only_udp(doc) -> "Test using socket option ipv6_v6only for UDP";
+use_ipv6_v6only_udp(Config) when is_list(Config) ->
+ ?line case gen_udp:open(0, [inet6,{ipv6_v6only,true}]) of
+ {ok,S6} ->
+ ?line case inet:getopts(S6, [ipv6_v6only]) of
+ {ok,[{ipv6_v6only,true}]} ->
+ use_ipv6_v6only_udp(Config, S6);
+ {ok,Other} ->
+ {skipped,{getopts,Other}}
+ end;
+ {error,_} ->
+ {skipped,"Socket type not supported"}
+ end.
+
+use_ipv6_v6only_udp(_Config, S6) ->
+ ?line {ok,Port} = inet:port(S6),
+ ?line {ok,S4} = gen_udp:open(Port, [inet]),
+ ?line E6 = " IPv6-echo.",
+ ?line E4 = " IPv4-echo.",
+ ?line Sender =
+ spawn_link(fun () -> use_ipv6_v6only_udp_sender(Port, E6, E4) end),
+ ?line use_ipv6_v6only_udp_listener(
+ S6, S4, E6, E4, monitor(process, Sender)).
+
+use_ipv6_v6only_udp_listener(S6, S4, E6, E4, Mref) ->
+ ?line receive
+ {udp,S6,IP,P,Data} ->
+ ?line ok = gen_udp:send(S6, IP, P, [Data|E6]),
+ ?line use_ipv6_v6only_udp_listener(S6, S4, E6, E4, Mref);
+ {udp,S4,IP,P,Data} ->
+ ?line ok = gen_udp:send(S4, IP, P, [Data|E4]),
+ ?line use_ipv6_v6only_udp_listener(S6, S4, E6, E4, Mref);
+ {'DOWN',Mref,_,_,normal} ->
+ ok;
+ {'DOWN',Mref,_,_,Result} ->
+ %% Since we are linked we will never arrive here
+ Result;
+ Other ->
+ ?line exit({failed,{listener_unexpected,Other}})
+ end.
+
+use_ipv6_v6only_udp_sender(Port, E6, E4) ->
+ D6 = "IPv6-send.",
+ D4 = "IPv4-send.",
+ R6 = D6 ++ E6,
+ R4 = D4 ++ E4,
+ R6 = sndrcv({0,0,0,0,0,0,0,1}, Port, [inet6], D6),
+ R4 = sndrcv({127,0,0,1}, Port, [inet], D4),
+ ok.
+
+sndrcv(Ip, Port, Opts, Data) ->
+ {ok,S} = gen_udp:open(0, Opts),
+ io:format("[~w:~w] ! ~s~n", [Ip,Port,Data]),
+ ok = gen_udp:send(S, Ip, Port, Data),
+ receive
+ {udp,S,Ip,Port,RecData} ->
+ io:format("[~w:~w] : ~s~n", [Ip,Port,RecData]),
+ RecData;
+ Other ->
+ exit({failed,{sndrcv_unexpectec,Other}})
+ end.
+
+
+
type_errors(suite) ->
[];
type_errors(doc) ->
diff --git a/lib/kernel/test/inet_sockopt_SUITE_data/sockopt_helper.c b/lib/kernel/test/inet_sockopt_SUITE_data/sockopt_helper.c
index f24c93edf5..9c8f8eb91a 100644
--- a/lib/kernel/test/inet_sockopt_SUITE_data/sockopt_helper.c
+++ b/lib/kernel/test/inet_sockopt_SUITE_data/sockopt_helper.c
@@ -1,12 +1,3 @@
-#if defined(VXWORKS)
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-int sockopt_helper(void){
- return 0;
-}
-#else
-
#if defined(__WIN32__)
#define WIN32_LEAN_AND_MEAN
#include <winsock2.h>
@@ -215,5 +206,3 @@ int main(void){
} while (x != C_QUIT);
return 0;
}
-#endif
-
diff --git a/lib/kernel/test/init_SUITE.erl b/lib/kernel/test/init_SUITE.erl
index e3fa4642b7..6fe97ed04f 100644
--- a/lib/kernel/test/init_SUITE.erl
+++ b/lib/kernel/test/init_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -256,47 +256,42 @@ get_plain_arguments(Config) when is_list(Config) ->
boot_var(doc) -> [];
boot_var(suite) -> {req, [distribution, {local_slave_nodes, 1}]};
boot_var(Config) when is_list(Config) ->
- case os:type() of
- vxworks ->
- {comment, "Not run on VxWorks"};
+ ?line Dog = ?t:timetrap(?t:seconds(100)),
+
+ {BootScript, TEST_VAR, KernelVsn, StdlibVsn} = create_boot(Config),
+
+ %% Should fail as we have not given -boot_var TEST_VAR
+ ?line {error, timeout} =
+ start_node(init_test, "-boot " ++ BootScript),
+
+ case is_real_system(KernelVsn, StdlibVsn) of
+ true ->
+ %% Now it should work !!
+ ?line {ok, Node} =
+ start_node(init_test,
+ "-boot " ++ BootScript ++
+ " -boot_var TEST_VAR " ++ TEST_VAR),
+ stop_node(Node),
+ Res = ok;
_ ->
- ?line Dog = ?t:timetrap(?t:seconds(100)),
-
- {BootScript, TEST_VAR, KernelVsn, StdlibVsn} = create_boot(Config),
-
- %% Should fail as we have not given -boot_var TEST_VAR
- ?line {error, timeout} =
- start_node(init_test, "-boot " ++ BootScript),
-
- case is_real_system(KernelVsn, StdlibVsn) of
- true ->
- %% Now it should work !!
- ?line {ok, Node} =
- start_node(init_test,
- "-boot " ++ BootScript ++
- " -boot_var TEST_VAR " ++ TEST_VAR),
- stop_node(Node),
- Res = ok;
- _ ->
-%% What we need is not so much version numbers on the directories, but
-%% for the boot var TEST_VAR to appear in the boot script, and it doesn't
-%% if we give the 'local' option to systools:make_script.
- ?t:format(
- "Test case not complete as we are not~n"
- "running in a real system!~n"
- "Probably this test is performed in a "
- "clearcase view or source tree.~n"
- "Need version numbers on the kernel and "
- "stdlib directories!~n",
- []),
- Res = {skip,
- "Test case only partially run since it is run "
- "in a clearcase view or in a source tree. "
- "Need an installed system to complete this test."}
- end,
- ?line ?t:timetrap_cancel(Dog),
- Res
- end.
+ %% What we need is not so much version numbers on the directories, but
+ %% for the boot var TEST_VAR to appear in the boot script, and it doesn't
+ %% if we give the 'local' option to systools:make_script.
+ ?t:format(
+ "Test case not complete as we are not~n"
+ "running in a real system!~n"
+ "Probably this test is performed in a "
+ "clearcase view or source tree.~n"
+ "Need version numbers on the kernel and "
+ "stdlib directories!~n",
+ []),
+ Res = {skip,
+ "Test case only partially run since it is run "
+ "in a clearcase view or in a source tree. "
+ "Need an installed system to complete this test."}
+ end,
+ ?line ?t:timetrap_cancel(Dog),
+ Res.
create_boot(Config) ->
?line {ok, OldDir} = file:get_cwd(),
@@ -579,55 +574,47 @@ script_id(Config) when is_list(Config) ->
boot1(doc) -> [];
boot1(suite) -> {req, [distribution, {local_slave_nodes, 1}, {time, 35}]};
boot1(Config) when is_list(Config) ->
- case os:type() of
- vxworks ->
- {comment, "Not run on VxWorks"};
- _ ->
- ?line Dog = ?t:timetrap(?t:seconds(80)),
- Args = args() ++ " -boot start_sasl",
- ?line {ok, Node} = start_node(init_test, Args),
- ?line stop_node(Node),
-
- %% Try to start with non existing boot file.
- Args1 = args() ++ " -boot dummy_script",
- ?line {error, timeout} = start_node(init_test, Args1),
-
- ?line ?t:timetrap_cancel(Dog),
- ok
- end.
+ ?line Dog = ?t:timetrap(?t:seconds(80)),
+ Args = args() ++ " -boot start_sasl",
+ ?line {ok, Node} = start_node(init_test, Args),
+ ?line stop_node(Node),
+
+ %% Try to start with non existing boot file.
+ Args1 = args() ++ " -boot dummy_script",
+ ?line {error, timeout} = start_node(init_test, Args1),
+
+ ?line ?t:timetrap_cancel(Dog),
+ ok.
boot2(doc) -> [];
boot2(suite) -> {req, [distribution, {local_slave_nodes, 1}, {time, 35}]};
boot2(Config) when is_list(Config) ->
+ Dog = ?t:timetrap(?t:seconds(80)),
+
+ %% Absolute boot file name
+ Boot = filename:join([code:root_dir(), "bin", "start_sasl"]),
+
+ Args = args() ++ " -boot \"" ++ Boot++"\"",
+ {ok, Node} = start_node(init_test, Args),
+ stop_node(Node),
+
case os:type() of
- vxworks ->
- {comment, "Not run on VxWorks"};
+ {win32, _} ->
+ %% Absolute boot file name for Windows -- all slashes are
+ %% converted to backslashes.
+ Win_boot = lists:map(fun
+ ($/) -> $\\;
+ (C) -> C
+ end, Boot),
+ Args2 = args() ++ " -boot \"" ++ Win_boot ++ "\"",
+ {ok, Node2} = start_node(init_test, Args2),
+ stop_node(Node2);
_ ->
- ?line Dog = ?t:timetrap(?t:seconds(80)),
-
- %% Absolute boot file name
- Boot = filename:join([code:root_dir(), "bin", "start_sasl"]),
-
- Args = args() ++ " -boot \"" ++ Boot++"\"",
- ?line {ok, Node} = start_node(init_test, Args),
- ?line stop_node(Node),
-
- case os:type() of
- {win32, _} ->
- %% Absolute boot file name for Windows -- all slashes are
- %% converted to backslashes.
- Win_boot = lists:map(fun($/) -> $\\; (C) -> C end,
- Boot),
- Args2 = args() ++ " -boot \"" ++ Win_boot ++ "\"",
- ?line {ok, Node2} = start_node(init_test, Args2),
- ?line stop_node(Node2);
- _ ->
- ok
- end,
-
- ?line ?t:timetrap_cancel(Dog),
ok
- end.
+ end,
+
+ ?t:timetrap_cancel(Dog),
+ ok.
%% Misc. functions
diff --git a/lib/kernel/test/interactive_shell_SUITE.erl b/lib/kernel/test/interactive_shell_SUITE.erl
index 96e45cc23f..36e13cec26 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-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -29,20 +29,11 @@
-export([toerl_server/3]).
init_per_testcase(_Func, Config) ->
- Dog = test_server:timetrap(test_server:seconds(60)),
- Term = case os:getenv("TERM") of
- List when is_list(List) ->
- List;
- _ ->
- "dumb"
- end,
- os:putenv("TERM","vt100"),
- [{watchdog,Dog},{term,Term}|Config].
+ Dog = test_server:timetrap(test_server:minutes(3)),
+ [{watchdog,Dog}|Config].
end_per_testcase(_Func, Config) ->
Dog = ?config(watchdog, Config),
- Term = ?config(term,Config),
- os:putenv("TERM",Term),
test_server:timetrap_cancel(Dog).
@@ -56,9 +47,19 @@ groups() ->
[].
init_per_suite(Config) ->
- Config.
+ Term = case os:getenv("TERM") of
+ List when is_list(List) ->
+ List;
+ _ ->
+ "dumb"
+ end,
+ os:putenv("TERM","vt100"),
+ DefShell = get_default_shell(),
+ [{default_shell,DefShell},{term,Term}|Config].
-end_per_suite(_Config) ->
+end_per_suite(Config) ->
+ Term = ?config(term,Config),
+ os:putenv("TERM",Term),
ok.
init_per_group(_GroupName, Config) ->
@@ -78,70 +79,118 @@ end_per_group(_GroupName, Config) ->
get_columns_and_rows(suite) -> [];
get_columns_and_rows(doc) -> ["Test that the shell can access columns and rows"];
get_columns_and_rows(Config) when is_list(Config) ->
- ?line rtnode([{putline,""},
- {putline, "2."},
- {getline, "2"},
- {putline,"io:columns()."},
-%% Behaviour change in R12B-5, returns 80
-%% {getline,"{error,enotsup}"},
- {getline,"{ok,80}"},
- {putline,"io:rows()."},
-%% Behaviour change in R12B-5, returns 24
-%% {getline,"{error,enotsup}"}
- {getline,"{ok,24}"}
- ],[]),
- ?line rtnode([{putline,""},
- {putline, "2."},
- {getline, "2"},
- {putline,"io:columns()."},
- {getline,"{ok,90}"},
- {putline,"io:rows()."},
- {getline,"{ok,40}"}],
- [],
- "stty rows 40; stty columns 90; ").
+ case proplists:get_value(default_shell,Config) of
+ old ->
+ %% Old shell tests
+ ?dbg(old_shell),
+ ?line rtnode([{putline,""},
+ {putline, "2."},
+ {getline, "2"},
+ {putline,"io:columns()."},
+ {getline_re,".*{error,enotsup}"},
+ {putline,"io:rows()."},
+ {getline_re,".*{error,enotsup}"}
+
+ ],[]),
+ ?line rtnode([{putline,""},
+ {putline, "2."},
+ {getline, "2"},
+ {putline,"io:columns()."},
+ {getline_re,".*{ok,90}"},
+ {putline,"io:rows()."},
+ {getline_re,".*{ok,40}"}],
+ [],
+ "stty rows 40; stty columns 90; ");
+ new ->
+ % New shell tests
+ ?dbg(new_shell),
+ ?line rtnode([{putline,""},
+ {putline, "2."},
+ {getline, "2"},
+ {putline,"io:columns()."},
+ %% Behaviour change in R12B-5, returns 80
+ %% {getline,"{error,enotsup}"},
+ {getline,"{ok,80}"},
+ {putline,"io:rows()."},
+ %% Behaviour change in R12B-5, returns 24
+ %% {getline,"{error,enotsup}"}
+ {getline,"{ok,24}"}
+ ],[]),
+ ?line rtnode([{putline,""},
+ {putline, "2."},
+ {getline, "2"},
+ {putline,"io:columns()."},
+ {getline,"{ok,90}"},
+ {putline,"io:rows()."},
+ {getline,"{ok,40}"}],
+ [],
+ "stty rows 40; stty columns 90; ")
+ end.
exit_initial(suite) -> [];
exit_initial(doc) -> ["Tests that exit of initial shell restarts shell"];
exit_initial(Config) when is_list(Config) ->
- ?line rtnode([{putline,""},
- {putline, "2."},
- {getline, "2"},
- {putline,"exit()."},
- {getline,""},
- {getline,"Eshell"},
- {putline,""},
- {putline,"35."},
- {getline,"35"}],[]).
+ case proplists:get_value(default_shell,Config) of
+ old ->
+ rtnode([{putline,""},
+ {putline, "2."},
+ {getline_re, ".*2"},
+ {putline,"exit()."},
+ {getline,""},
+ {getline,"Eshell"},
+ {putline,""},
+ {putline,"35."},
+ {getline_re,".*35"}],[]);
+ new ->
+ rtnode([{putline,""},
+ {putline, "2."},
+ {getline, "2"},
+ {putline,"exit()."},
+ {getline,""},
+ {getline,"Eshell"},
+ {putline,""},
+ {putline,"35."},
+ {getline_re,"35"}],[])
+ end.
job_control_local(suite) -> [];
job_control_local(doc) -> [ "Tests that local shell can be "
"started by means of job control" ];
job_control_local(Config) when is_list(Config) ->
- ?line rtnode([{putline,""},
- {putline, "2."},
- {getline, "2"},
- {putline,[7]},
- {sleep,timeout(short)},
- {putline,""},
- {getline," -->"},
- {putline,"s"},
- {putline,"c"},
- {putline_raw,""},
- {getline,"Eshell"},
- {putline_raw,""},
- {getline,"1>"},
- {putline,"35."},
- {getline,"35"}],[]).
+ case proplists:get_value(default_shell,Config) of
+ old ->
+ %% Old shell tests
+ {skip,"No new shell found"};
+ new ->
+ %% New shell tests
+ ?line rtnode([{putline,""},
+ {putline, "2."},
+ {getline, "2"},
+ {putline,[7]},
+ {sleep,timeout(short)},
+ {putline,""},
+ {getline," -->"},
+ {putline,"s"},
+ {putline,"c"},
+ {putline_raw,""},
+ {getline,"Eshell"},
+ {putline_raw,""},
+ {getline,"1>"},
+ {putline,"35."},
+ {getline,"35"}],[])
+ end.
job_control_remote(suite) -> [];
job_control_remote(doc) -> [ "Tests that remote shell can be "
"started by means of job control" ];
job_control_remote(Config) when is_list(Config) ->
- case node() of
- nonode@nohost ->
+ case {node(),proplists:get_value(default_shell,Config)} of
+ {nonode@nohost,_} ->
?line exit(not_distributed);
+ {_,old} ->
+ {skip,"No new shell found"};
_ ->
?line RNode = create_nodename(),
?line MyNode = atom_to_list(node()),
@@ -190,9 +239,11 @@ job_control_remote_noshell(doc) ->
[ "Tests that remote shell can be "
"started by means of job control to -noshell node" ];
job_control_remote_noshell(Config) when is_list(Config) ->
- case node() of
- nonode@nohost ->
+ case {node(),proplists:get_value(default_shell,Config)} of
+ {nonode@nohost,_} ->
?line exit(not_distributed);
+ {_,old} ->
+ {skip,"No new shell found"};
_ ->
?line RNode = create_nodename(),
?line NSNode = start_noshell_node(interactive_shell_noshell),
@@ -351,6 +402,33 @@ get_and_put(CPid, [{getline, Match}|T],N) ->
end
end;
+%% Hey ho copy paste from stdlib/io_proto_SUITE
+get_and_put(CPid, [{getline_re, Match}|T],N) ->
+ ?dbg({getline_re, Match}),
+ CPid ! {self(), {get_line, timeout(normal)}},
+ receive
+ {get_line, timeout} ->
+ error_logger:error_msg("~p: getline_re timeout waiting for \"~s\" "
+ "(command number ~p, skipped: ~p)~n",
+ [?MODULE, Match,N,get(getline_skipped)]),
+ {error, timeout};
+ {get_line, Data} ->
+ ?dbg({data,Data}),
+ case re:run(Data, Match,[{capture,none}]) of
+ match ->
+ erase(getline_skipped),
+ get_and_put(CPid, T,N+1);
+ _ ->
+ case get(getline_skipped) of
+ undefined ->
+ put(getline_skipped,[Data]);
+ List ->
+ put(getline_skipped,List ++ [Data])
+ end,
+ get_and_put(CPid, [{getline_re, Match}|T],N)
+ end
+ end;
+
get_and_put(CPid, [{putline_raw, Line}|T],N) ->
?dbg({putline_raw, Line}),
CPid ! {self(), {send_line, Line}},
@@ -631,6 +709,13 @@ get_data_within(Port, Timeout, Acc) ->
timeout
end.
-
-
-
+get_default_shell() ->
+ try
+ rtnode([{putline,""},
+ {putline, "whereis(user_drv)."},
+ {getline, "undefined"}],[]),
+ old
+ catch E:R ->
+ ?dbg({E,R}),
+ new
+ end.
diff --git a/lib/kernel/test/kernel.cover b/lib/kernel/test/kernel.cover
index f6967ca651..af1dd7eaad 100644
--- a/lib/kernel/test/kernel.cover
+++ b/lib/kernel/test/kernel.cover
@@ -1,3 +1,3 @@
%% -*- erlang -*-
-{incl_mods,[gen_udp,inet6_udp,inet_res,inet_dns]}.
+{incl_app,kernel,details}.
diff --git a/lib/kernel/test/kernel.spec.wxworks b/lib/kernel/test/kernel.spec.wxworks
deleted file mode 100644
index 370e474e64..0000000000
--- a/lib/kernel/test/kernel.spec.wxworks
+++ /dev/null
@@ -1,63 +0,0 @@
-%% -*- 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/os_SUITE.erl b/lib/kernel/test/os_SUITE.erl
index ae3410d13f..3f2195b609 100644
--- a/lib/kernel/test/os_SUITE.erl
+++ b/lib/kernel/test/os_SUITE.erl
@@ -202,8 +202,6 @@ find_executable(Config) when is_list(Config) ->
%% Never return a directory name.
?line false = os:find_executable("unix", [DataDir]),
- ok;
- vxworks ->
ok
end.
diff --git a/lib/kernel/test/pdict_SUITE.erl b/lib/kernel/test/pdict_SUITE.erl
index 8afdfc8a47..60b818cbe3 100644
--- a/lib/kernel/test/pdict_SUITE.erl
+++ b/lib/kernel/test/pdict_SUITE.erl
@@ -152,7 +152,6 @@ heavy(Config) when is_list(Config) ->
time(5000),
?M([],get()),
case {os:type(),?t:is_debug()} of
- {vxworks,_} -> ok;
{_,true} -> ok;
_ ->
time(50000),
diff --git a/lib/kernel/test/prim_file_SUITE.erl b/lib/kernel/test/prim_file_SUITE.erl
index 3e2202922c..4e93a593b3 100644
--- a/lib/kernel/test/prim_file_SUITE.erl
+++ b/lib/kernel/test/prim_file_SUITE.erl
@@ -57,6 +57,8 @@
%% System probe functions that might be handy to check from the shell
-export([unix_free/1]).
+-export([allocate/1]).
+
-include_lib("test_server/include/test_server.hrl").
-include_lib("kernel/include/file.hrl").
@@ -87,7 +89,7 @@ groups() ->
cur_dir_1a, cur_dir_1b]},
{files, [],
[{group, open}, {group, pos}, {group, file_info},
- truncate, sync, datasync, advise, large_write]},
+ truncate, sync, datasync, advise, large_write, allocate]},
{open, [],
[open1, modes, close, access, read_write, pread_write,
append, exclusive]},
@@ -406,9 +408,6 @@ cur_dir_1(Config, Handle) ->
{unix, _} ->
?line {error, enotsup} =
?PRIM_FILE_call(get_cwd, Handle, ["d:"]);
- vxworks ->
- ?line {error, enotsup} =
- ?PRIM_FILE_call(get_cwd, Handle, ["d:"]);
{win32, _} ->
win_cur_dir_1(Config, Handle)
end,
@@ -843,10 +842,7 @@ file_info_basic_directory(Config, Handle) ->
?line test_directory("c:/", read_write, Handle),
?line test_directory("c:\\", read_write, Handle);
{unix, _} ->
- ?line test_directory("/", read, Handle);
- vxworks ->
- %% Check is just done for owner
- ?line test_directory("/", read_write, Handle)
+ ?line test_directory("/", read, Handle)
end,
?line test_server:timetrap_cancel(Dog).
@@ -1365,6 +1361,76 @@ check_large_write(Dog, Fd, _, _, []) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+allocate(suite) -> [];
+allocate(doc) -> "Tests that ?PRIM_FILE:allocate/3 at least doesn't crash.";
+allocate(Config) when is_list(Config) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(5)),
+ ?line PrivDir = ?config(priv_dir, Config),
+ ?line Allocate = filename:join(PrivDir,
+ atom_to_list(?MODULE)
+ ++"_allocate.fil"),
+
+ Line1 = "Hello\n",
+ Line2 = "World!\n",
+
+ ?line {ok, Fd} = ?PRIM_FILE:open(Allocate, [write, binary]),
+ allocate_and_assert(Fd, 1, iolist_size([Line1, Line2])),
+ ?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(Allocate, [write, binary]),
+ allocate_and_assert(Fd2, 1, iolist_size(Line1)),
+ ?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(Allocate, [write, binary]),
+ allocate_and_assert(Fd3, 1, iolist_size(Line1) + 1),
+ ?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(Allocate, [write, binary]),
+ allocate_and_assert(Fd4, 1, 4 * iolist_size([Line1, Line2])),
+ ?line ok = ?PRIM_FILE:write(Fd4, Line1),
+ ?line ok = ?PRIM_FILE:write(Fd4, Line2),
+ ?line ok = ?PRIM_FILE:close(Fd4),
+
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+allocate_and_assert(Fd, Offset, Length) ->
+ % Just verify that calls to ?PRIM_FILE:allocate/3 don't crash or have
+ % any other negative side effect. We can't really asssert against a
+ % specific return value, because support for file space pre-allocation
+ % depends on the OS, OS version and underlying filesystem.
+ %
+ % The Linux kernel added support for fallocate() in version 2.6.23,
+ % which currently works only for the ext4, ocfs2, xfs and btrfs file
+ % systems. posix_fallocate() is available in glibc as of version
+ % 2.1.94, but it was buggy until glibc version 2.7.
+ %
+ % Mac OS X, as of version 10.3, supports the fcntl operation F_PREALLOCATE.
+ %
+ % Solaris supports posix_fallocate() but only for the UFS file system
+ % apparently (not supported for ZFS).
+ %
+ % FreeBSD 9.0 is the first FreeBSD release supporting posix_fallocate().
+ %
+ % For Windows there's apparently no way to pre-allocate file space, at
+ % least with similar API/semantics as posix_fallocate(), fallocate() or
+ % fcntl F_PREALLOCATE.
+ Result = ?PRIM_FILE:allocate(Fd, Offset, Length),
+ case os:type() of
+ {win32, _} ->
+ ?line {error, enotsup} = Result;
+ _ ->
+ ?line _ = Result
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
delete_a(suite) -> [];
delete_a(doc) -> [];
delete_a(Config) when is_list(Config) ->
@@ -1508,9 +1574,7 @@ e_delete(Config) when is_list(Config) ->
Base, #file_info {mode=8#600});
{win32, _} ->
%% Remove a character device.
- ?line {error, eacces} = ?PRIM_FILE:delete("nul");
- vxworks ->
- ok
+ ?line {error, eacces} = ?PRIM_FILE:delete("nul")
end,
?line test_server:timetrap_cancel(Dog),
@@ -1524,110 +1588,105 @@ e_delete(Config) when is_list(Config) ->
e_rename(suite) -> [];
e_rename(doc) -> [];
e_rename(Config) when is_list(Config) ->
- case os:type() of
- vxworks ->
- {comment, "Windriver: dosFs must be fixed first!"};
- _ ->
- ?line Dog = test_server:timetrap(test_server:seconds(10)),
- ?line RootDir = ?config(priv_dir, Config),
- ?line Base = filename:join(RootDir,
- atom_to_list(?MODULE)++"_e_rename"),
- ?line ok = ?PRIM_FILE:make_dir(Base),
-
- %% Create an empty directory.
- ?line EmptyDir = filename:join(Base, "empty_dir"),
- ?line ok = ?PRIM_FILE:make_dir(EmptyDir),
-
- %% Create a non-empty directory.
- ?line NonEmptyDir = filename:join(Base, "non_empty_dir"),
- ?line ok = ?PRIM_FILE:make_dir(NonEmptyDir),
- ?line ok = ?PRIM_FILE:write_file(
- filename:join(NonEmptyDir, "a_file"),
- "hello\n"),
-
- %% Create another non-empty directory.
- ?line ADirectory = filename:join(Base, "a_directory"),
- ?line ok = ?PRIM_FILE:make_dir(ADirectory),
- ?line ok = ?PRIM_FILE:write_file(
- filename:join(ADirectory, "a_file"),
- "howdy\n\n"),
+ ?line Dog = test_server:timetrap(test_server:seconds(10)),
+ ?line RootDir = ?config(priv_dir, Config),
+ ?line Base = filename:join(RootDir,
+ atom_to_list(?MODULE)++"_e_rename"),
+ ?line ok = ?PRIM_FILE:make_dir(Base),
- %% Create a data file.
- ?line File = filename:join(Base, "just_a_file"),
- ?line ok = ?PRIM_FILE:write_file(File, "anything goes\n\n"),
-
- %% Move an existing directory to a non-empty directory.
- ?line {error, eexist} =
- ?PRIM_FILE:rename(ADirectory, NonEmptyDir),
-
- %% Move a root directory.
- ?line {error, einval} = ?PRIM_FILE:rename("/", "arne"),
-
- %% Move Base into Base/new_name.
- ?line {error, einval} =
- ?PRIM_FILE:rename(Base, filename:join(Base, "new_name")),
-
- %% Overwrite a directory with a file.
- ?line expect({error, eexist}, % FreeBSD (?)
- {error, eisdir},
- ?PRIM_FILE:rename(File, EmptyDir)),
- ?line expect({error, eexist}, % FreeBSD (?)
- {error, eisdir},
- ?PRIM_FILE:rename(File, NonEmptyDir)),
-
- %% Move a non-existing file.
- ?line NonExistingFile = filename:join(
- Base, "non_existing_file"),
- ?line {error, enoent} =
- ?PRIM_FILE:rename(NonExistingFile, NonEmptyDir),
-
- %% Overwrite a file with a directory.
- ?line expect({error, eexist}, % FreeBSD (?)
- {error, enotdir},
- ?PRIM_FILE:rename(ADirectory, File)),
-
- %% Move a file to another filesystem.
- %% XXX - This test case is bogus. We cannot be guaranteed that
- %% the source and destination are on
- %% different filesystems.
- %%
- %% XXX - Gross hack!
- ?line Comment =
- case os:type() of
- {unix, _} ->
- OtherFs = "/tmp",
- ?line NameOnOtherFs =
- filename:join(OtherFs,
- filename:basename(File)),
- ?line {ok, Com} =
- case ?PRIM_FILE:rename(
- File, NameOnOtherFs) of
- {error, exdev} ->
- %% The file could be in
- %% the same filesystem!
- {ok, ok};
- ok ->
- {ok, {comment,
- "Moving between filesystems "
- "suceeded, files are probably "
- "in the same filesystem!"}};
- {error, eperm} ->
- {ok, {comment, "SBS! You don't "
- "have the permission to do "
- "this test!"}};
- Else ->
- Else
- end,
- Com;
- {win32, _} ->
- %% At least Windows NT can
- %% successfully move a file to
- %% another drive.
- ok
- end,
- ?line test_server:timetrap_cancel(Dog),
- Comment
- end.
+ %% Create an empty directory.
+ ?line EmptyDir = filename:join(Base, "empty_dir"),
+ ?line ok = ?PRIM_FILE:make_dir(EmptyDir),
+
+ %% Create a non-empty directory.
+ ?line NonEmptyDir = filename:join(Base, "non_empty_dir"),
+ ?line ok = ?PRIM_FILE:make_dir(NonEmptyDir),
+ ?line ok = ?PRIM_FILE:write_file(
+ filename:join(NonEmptyDir, "a_file"),
+ "hello\n"),
+
+ %% Create another non-empty directory.
+ ?line ADirectory = filename:join(Base, "a_directory"),
+ ?line ok = ?PRIM_FILE:make_dir(ADirectory),
+ ?line ok = ?PRIM_FILE:write_file(
+ filename:join(ADirectory, "a_file"),
+ "howdy\n\n"),
+
+ %% Create a data file.
+ ?line File = filename:join(Base, "just_a_file"),
+ ?line ok = ?PRIM_FILE:write_file(File, "anything goes\n\n"),
+
+ %% Move an existing directory to a non-empty directory.
+ ?line {error, eexist} =
+ ?PRIM_FILE:rename(ADirectory, NonEmptyDir),
+
+ %% Move a root directory.
+ ?line {error, einval} = ?PRIM_FILE:rename("/", "arne"),
+
+ %% Move Base into Base/new_name.
+ ?line {error, einval} =
+ ?PRIM_FILE:rename(Base, filename:join(Base, "new_name")),
+
+ %% Overwrite a directory with a file.
+ ?line expect({error, eexist}, % FreeBSD (?)
+ {error, eisdir},
+ ?PRIM_FILE:rename(File, EmptyDir)),
+ ?line expect({error, eexist}, % FreeBSD (?)
+ {error, eisdir},
+ ?PRIM_FILE:rename(File, NonEmptyDir)),
+
+ %% Move a non-existing file.
+ ?line NonExistingFile = filename:join(
+ Base, "non_existing_file"),
+ ?line {error, enoent} =
+ ?PRIM_FILE:rename(NonExistingFile, NonEmptyDir),
+
+ %% Overwrite a file with a directory.
+ ?line expect({error, eexist}, % FreeBSD (?)
+ {error, enotdir},
+ ?PRIM_FILE:rename(ADirectory, File)),
+
+ %% Move a file to another filesystem.
+ %% XXX - This test case is bogus. We cannot be guaranteed that
+ %% the source and destination are on
+ %% different filesystems.
+ %%
+ %% XXX - Gross hack!
+ ?line Comment =
+ case os:type() of
+ {unix, _} ->
+ OtherFs = "/tmp",
+ ?line NameOnOtherFs =
+ filename:join(OtherFs,
+ filename:basename(File)),
+ ?line {ok, Com} =
+ case ?PRIM_FILE:rename(
+ File, NameOnOtherFs) of
+ {error, exdev} ->
+ %% The file could be in
+ %% the same filesystem!
+ {ok, ok};
+ ok ->
+ {ok, {comment,
+ "Moving between filesystems "
+ "suceeded, files are probably "
+ "in the same filesystem!"}};
+ {error, eperm} ->
+ {ok, {comment, "SBS! You don't "
+ "have the permission to do "
+ "this test!"}};
+ Else ->
+ Else
+ end,
+ Com;
+ {win32, _} ->
+ %% At least Windows NT can
+ %% successfully move a file to
+ %% another drive.
+ ok
+ end,
+ ?line test_server:timetrap_cancel(Dog),
+ Comment.
e_make_dir(suite) -> [];
e_make_dir(doc) -> [];
@@ -1660,8 +1719,6 @@ e_make_dir(Config) when is_list(Config) ->
?line
?PRIM_FILE:write_file_info(Base, #file_info {mode=8#600});
{win32, _} ->
- ok;
- vxworks ->
ok
end,
?line test_server:timetrap_cancel(Dog),
@@ -1716,8 +1773,6 @@ e_del_dir(Config) when is_list(Config) ->
?line ?PRIM_FILE:write_file_info(
Base, #file_info {mode=8#600});
{win32, _} ->
- ok;
- vxworks ->
ok
end,
?line test_server:timetrap_cancel(Dog),
diff --git a/lib/kernel/test/ram_file_SUITE.erl b/lib/kernel/test/ram_file_SUITE.erl
index ab95a3ff5f..5c4437d4d3 100644
--- a/lib/kernel/test/ram_file_SUITE.erl
+++ b/lib/kernel/test/ram_file_SUITE.erl
@@ -1,7 +1,8 @@
+%% -*- coding: utf-8 -*-
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -167,7 +168,7 @@ pread_pwrite(suite) ->
pread_pwrite(doc) ->
["Test that pread/2,3 and pwrite/2,3 works."];
pread_pwrite(Config) when is_list(Config) ->
- ?line Str = "Flygande b�ckaziner s�ka hwila p� mjuqa tuvor x",
+ ?line Str = "Flygande bäckaziner söka hwila på mjuqa tuvor x",
?line Bin = list_to_binary(Str),
%%
pread_pwrite_test(?FILE_MODULE, Str, [ram, read, write]),
@@ -206,7 +207,7 @@ position(suite) ->
position(doc) ->
["Test that position/2 works."];
position(Config) when is_list(Config) ->
- ?line Str = "Att vara eller icke vara, det �r fr�gan. ",
+ ?line Str = "Att vara eller icke vara, det är frågan. ",
?line Bin = list_to_binary(Str),
%%
position_test(?FILE_MODULE, Str, [ram, read]),
@@ -287,8 +288,8 @@ truncate(suite) ->
truncate(doc) ->
["Test that truncate/1 works."];
truncate(Config) when is_list(Config) ->
- ?line Str = "M�n �dlare att lida och f�rdraga "
- ++ "ett bittert �des stygn av pilar, ",
+ ?line Str = "Mån ädlare att lida och fördraga "
+ ++ "ett bittert ödes stygn av pilar, ",
?line Bin = list_to_binary(Str),
%%
ok = truncate_test(?FILE_MODULE, Str, [ram, read, write]),
@@ -331,7 +332,7 @@ sync(suite) ->
sync(doc) ->
["Test that sync/1 at least does not crash."];
sync(Config) when is_list(Config) ->
- ?line Str = "�n att ta till vapen mot ett hav av kval. ",
+ ?line Str = "än att ta till vapen mot ett hav av kval. ",
?line Bin = list_to_binary(Str),
%%
sync_test(?FILE_MODULE, Str, [ram, read, write]),
@@ -365,8 +366,8 @@ get_set_file(doc) ->
["Tests get_file/1, set_file/2, get_file_close/1 and get_size/1."];
get_set_file(Config) when is_list(Config) ->
%% These two strings should not be of equal length.
- ?line Str = "N�r h�gan nord blir sn�bet�ckt, ",
- ?line Str2 = "f�r alla harar byta dr�kt. ",
+ ?line Str = "När högan nord blir snöbetäckt, ",
+ ?line Str2 = "får alla harar byta dräkt. ",
?line Bin = list_to_binary(Str),
?line Bin2 = list_to_binary(Str2),
%%
diff --git a/lib/kernel/test/wrap_log_reader_SUITE.erl b/lib/kernel/test/wrap_log_reader_SUITE.erl
index 96dc3e6d33..16b3a7cc1e 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-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -557,7 +557,7 @@ rec(M, Where) ->
M ->
ok;
Else -> ?t:fail({error, {Where, Else}})
- after 1000 -> ?t:fail({error, {Where, time_out}})
+ after 5000 -> ?t:fail({error, {Where, time_out}})
end.
pps() ->
diff --git a/lib/megaco/Makefile b/lib/megaco/Makefile
index fc8f408b0a..3bf502ba1c 100644
--- a/lib/megaco/Makefile
+++ b/lib/megaco/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2011. All Rights Reserved.
+# Copyright Ericsson AB 1999-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/megaco/aclocal.m4 b/lib/megaco/aclocal.m4
new file mode 100644
index 0000000000..918e30a886
--- /dev/null
+++ b/lib/megaco/aclocal.m4
@@ -0,0 +1,1905 @@
+dnl
+dnl %CopyrightBegin%
+dnl
+dnl Copyright Ericsson AB 1998-2012. All Rights Reserved.
+dnl
+dnl The contents of this file are subject to the Erlang Public License,
+dnl Version 1.1, (the "License"); you may not use this file except in
+dnl compliance with the License. You should have received a copy of the
+dnl Erlang Public License along with this software. If not, it can be
+dnl retrieved online at http://www.erlang.org/.
+dnl
+dnl Software distributed under the License is distributed on an "AS IS"
+dnl basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+dnl the License for the specific language governing rights and limitations
+dnl under the License.
+dnl
+dnl %CopyrightEnd%
+dnl
+
+dnl
+dnl aclocal.m4
+dnl
+dnl Local macros used in configure.in. The Local Macros which
+dnl could/should be part of autoconf are prefixed LM_, macros specific
+dnl to the Erlang system are prefixed ERL_.
+dnl
+
+AC_DEFUN(LM_PRECIOUS_VARS,
+[
+
+dnl ERL_TOP
+AC_ARG_VAR(ERL_TOP, [Erlang/OTP top source directory])
+
+dnl Tools
+AC_ARG_VAR(CC, [C compiler])
+AC_ARG_VAR(CFLAGS, [C compiler flags])
+AC_ARG_VAR(STATIC_CFLAGS, [C compiler static flags])
+AC_ARG_VAR(CFLAG_RUNTIME_LIBRARY_PATH, [runtime library path linker flag passed via C compiler])
+AC_ARG_VAR(CPP, [C/C++ preprocessor])
+AC_ARG_VAR(CPPFLAGS, [C/C++ preprocessor flags])
+AC_ARG_VAR(CXX, [C++ compiler])
+AC_ARG_VAR(CXXFLAGS, [C++ compiler flags])
+AC_ARG_VAR(LD, [linker (is often overridden by configure)])
+AC_ARG_VAR(LDFLAGS, [linker flags (can be risky to set since LD may be overriden by configure)])
+AC_ARG_VAR(LIBS, [libraries])
+AC_ARG_VAR(DED_LD, [linker for Dynamic Erlang Drivers (set all DED_LD* variables or none)])
+AC_ARG_VAR(DED_LDFLAGS, [linker flags for Dynamic Erlang Drivers (set all DED_LD* variables or none)])
+AC_ARG_VAR(DED_LD_FLAG_RUNTIME_LIBRARY_PATH, [runtime library path linker flag for Dynamic Erlang Drivers (set all DED_LD* variables or none)])
+AC_ARG_VAR(LFS_CFLAGS, [large file support C compiler flags (set all LFS_* variables or none)])
+AC_ARG_VAR(LFS_LDFLAGS, [large file support linker flags (set all LFS_* variables or none)])
+AC_ARG_VAR(LFS_LIBS, [large file support libraries (set all LFS_* variables or none)])
+AC_ARG_VAR(RANLIB, [ranlib])
+AC_ARG_VAR(AR, [ar])
+AC_ARG_VAR(GETCONF, [getconf])
+
+dnl Cross system root
+AC_ARG_VAR(erl_xcomp_sysroot, [Absolute cross system root path (only used when cross compiling)])
+AC_ARG_VAR(erl_xcomp_isysroot, [Absolute cross system root include path (only used when cross compiling)])
+
+dnl Cross compilation variables
+AC_ARG_VAR(erl_xcomp_bigendian, [big endian system: yes|no (only used when cross compiling)])
+AC_ARG_VAR(erl_xcomp_double_middle_endian, [double-middle-endian system: yes|no (only used when cross compiling)])
+AC_ARG_VAR(erl_xcomp_linux_clock_gettime_correction, [clock_gettime() can be used for time correction: yes|no (only used when cross compiling)])
+AC_ARG_VAR(erl_xcomp_linux_nptl, [have Native POSIX Thread Library: yes|no (only used when cross compiling)])
+AC_ARG_VAR(erl_xcomp_linux_usable_sigusrx, [SIGUSR1 and SIGUSR2 can be used: yes|no (only used when cross compiling)])
+AC_ARG_VAR(erl_xcomp_linux_usable_sigaltstack, [have working sigaltstack(): yes|no (only used when cross compiling)])
+AC_ARG_VAR(erl_xcomp_poll, [have working poll(): yes|no (only used when cross compiling)])
+AC_ARG_VAR(erl_xcomp_kqueue, [have working kqueue(): yes|no (only used when cross compiling)])
+AC_ARG_VAR(erl_xcomp_putenv_copy, [putenv() stores key-value copy: yes|no (only used when cross compiling)])
+AC_ARG_VAR(erl_xcomp_reliable_fpe, [have reliable floating point exceptions: yes|no (only used when cross compiling)])
+AC_ARG_VAR(erl_xcomp_getaddrinfo, [have working getaddrinfo() for both IPv4 and IPv6: yes|no (only used when cross compiling)])
+AC_ARG_VAR(erl_xcomp_gethrvtime_procfs_ioctl, [have working gethrvtime() which can be used with procfs ioctl(): yes|no (only used when cross compiling)])
+AC_ARG_VAR(erl_xcomp_clock_gettime_cpu_time, [clock_gettime() can be used for retrieving process CPU time: yes|no (only used when cross compiling)])
+AC_ARG_VAR(erl_xcomp_after_morecore_hook, [__after_morecore_hook can track malloc()s core memory usage: yes|no (only used when cross compiling)])
+AC_ARG_VAR(erl_xcomp_dlsym_brk_wrappers, [dlsym(RTLD_NEXT, _) brk wrappers can track malloc()s core memory usage: yes|no (only used when cross compiling)])
+
+])
+
+AC_DEFUN(ERL_XCOMP_SYSROOT_INIT,
+[
+erl_xcomp_without_sysroot=no
+if test "$cross_compiling" = "yes"; then
+ test "$erl_xcomp_sysroot" != "" || erl_xcomp_without_sysroot=yes
+ test "$erl_xcomp_isysroot" != "" || erl_xcomp_isysroot="$erl_xcomp_sysroot"
+else
+ erl_xcomp_sysroot=
+ erl_xcomp_isysroot=
+fi
+])
+
+AC_DEFUN(LM_CHECK_GETCONF,
+[
+if test "$cross_compiling" != "yes"; then
+ AC_CHECK_PROG([GETCONF], [getconf], [getconf], [false])
+else
+ dnl First check if we got a `<HOST>-getconf' in $PATH
+ host_getconf="$host_alias-getconf"
+ AC_CHECK_PROG([GETCONF], [$host_getconf], [$host_getconf], [false])
+ if test "$GETCONF" = "false" && test "$erl_xcomp_sysroot" != ""; then
+ dnl We should perhaps give up if we have'nt found it by now, but at
+ dnl least in one Tilera MDE `getconf' under sysroot is a bourne
+ dnl shell script which we can use. We try to find `<HOST>-getconf'
+ dnl or `getconf' under sysconf, but only under sysconf since
+ dnl `getconf' in $PATH is almost guaranteed to be for the build
+ dnl machine.
+ GETCONF=
+ prfx="$erl_xcomp_sysroot"
+ AC_PATH_TOOL([GETCONF], [getconf], [false],
+ ["$prfx/usr/bin:$prfx/bin:$prfx/usr/local/bin"])
+ fi
+fi
+])
+
+dnl ----------------------------------------------------------------------
+dnl
+dnl LM_WINDOWS_ENVIRONMENT
+dnl
+dnl
+dnl Tries to determine thw windows build environment, i.e.
+dnl MIXED_CYGWIN_VC or MIXED_MSYS_VC
+dnl
+
+AC_DEFUN(LM_WINDOWS_ENVIRONMENT,
+[
+MIXED_CYGWIN=no
+MIXED_MSYS=no
+
+AC_MSG_CHECKING(for mixed cygwin or msys and native VC++ environment)
+if test "X$host" = "Xwin32" -a "x$GCC" != "xyes"; then
+ if test -x /usr/bin/cygpath; then
+ CFLAGS="-O2"
+ MIXED_CYGWIN=yes
+ AC_MSG_RESULT([Cygwin and VC])
+ MIXED_CYGWIN_VC=yes
+ CPPFLAGS="$CPPFLAGS -DERTS_MIXED_CYGWIN_VC"
+ elif test -x /usr/bin/msysinfo; then
+ CFLAGS="-O2"
+ MIXED_MSYS=yes
+ AC_MSG_RESULT([MSYS and VC])
+ MIXED_MSYS_VC=yes
+ CPPFLAGS="$CPPFLAGS -DERTS_MIXED_MSYS_VC"
+ else
+ AC_MSG_RESULT([undeterminable])
+ AC_MSG_ERROR(Seems to be mixed windows but not with cygwin, cannot handle this!)
+ fi
+else
+ AC_MSG_RESULT([no])
+ MIXED_CYGWIN_VC=no
+ MIXED_MSYS_VC=no
+fi
+AC_SUBST(MIXED_CYGWIN_VC)
+AC_SUBST(MIXED_MSYS_VC)
+
+MIXED_VC=no
+if test "x$MIXED_MSYS_VC" = "xyes" -o "x$MIXED_CYGWIN_VC" = "xyes" ; then
+ MIXED_VC=yes
+fi
+
+AC_SUBST(MIXED_VC)
+
+if test "x$MIXED_MSYS" != "xyes"; then
+ AC_MSG_CHECKING(for mixed cygwin and native MinGW environment)
+ if test "X$host" = "Xwin32" -a "x$GCC" = x"yes"; then
+ if test -x /usr/bin/cygpath; then
+ CFLAGS="-O2"
+ MIXED_CYGWIN=yes
+ AC_MSG_RESULT([yes])
+ MIXED_CYGWIN_MINGW=yes
+ CPPFLAGS="$CPPFLAGS -DERTS_MIXED_CYGWIN_MINGW"
+ else
+ AC_MSG_RESULT([undeterminable])
+ AC_MSG_ERROR(Seems to be mixed windows but not with cygwin, cannot handle this!)
+ fi
+ else
+ AC_MSG_RESULT([no])
+ MIXED_CYGWIN_MINGW=no
+ fi
+else
+ MIXED_CYGWIN_MINGW=no
+fi
+AC_SUBST(MIXED_CYGWIN_MINGW)
+
+AC_MSG_CHECKING(if we mix cygwin with any native compiler)
+if test "X$MIXED_CYGWIN" = "Xyes"; then
+ AC_MSG_RESULT([yes])
+else
+ AC_MSG_RESULT([no])
+fi
+
+AC_SUBST(MIXED_CYGWIN)
+
+AC_MSG_CHECKING(if we mix msys with another native compiler)
+if test "X$MIXED_MSYS" = "Xyes" ; then
+ AC_MSG_RESULT([yes])
+else
+ AC_MSG_RESULT([no])
+fi
+
+AC_SUBST(MIXED_MSYS)
+])
+
+dnl ----------------------------------------------------------------------
+dnl
+dnl LM_FIND_EMU_CC
+dnl
+dnl
+dnl Tries fairly hard to find a C compiler that can handle jump tables.
+dnl Defines the @EMU_CC@ variable for the makefiles and
+dnl inserts NO_JUMP_TABLE in the header if one cannot be found...
+dnl
+
+AC_DEFUN(LM_FIND_EMU_CC,
+ [AC_CACHE_CHECK(for a compiler that handles jumptables,
+ ac_cv_prog_emu_cc,
+ [
+AC_TRY_COMPILE([],[
+#if defined(__clang_major__) && __clang_major__ >= 3
+ /* clang 3.x or later is fine */
+#elif defined(__llvm__)
+#error "this version of llvm is unable to correctly compile beam_emu.c"
+#endif
+ __label__ lbl1;
+ __label__ lbl2;
+ int x = magic();
+ static void *jtab[2];
+
+ jtab[0] = &&lbl1;
+ jtab[1] = &&lbl2;
+ goto *jtab[x];
+lbl1:
+ return 1;
+lbl2:
+ return 2;
+],ac_cv_prog_emu_cc=$CC,ac_cv_prog_emu_cc=no)
+
+if test $ac_cv_prog_emu_cc = no; then
+ for ac_progname in emu_cc.sh gcc-4.2 gcc; do
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_progname; then
+ ac_cv_prog_emu_cc=$ac_dir/$ac_progname
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+ if test $ac_cv_prog_emu_cc != no; then
+ break
+ fi
+ done
+fi
+
+if test $ac_cv_prog_emu_cc != no; then
+ save_CC=$CC
+ save_CFLAGS=$CFLAGS
+ save_CPPFLAGS=$CPPFLAGS
+ CC=$ac_cv_prog_emu_cc
+ CFLAGS=""
+ CPPFLAGS=""
+ AC_TRY_COMPILE([],[
+#if defined(__clang_major__) && __clang_major__ >= 3
+ /* clang 3.x or later is fine */
+#elif defined(__llvm__)
+#error "this version of llvm is unable to correctly compile beam_emu.c"
+#endif
+ __label__ lbl1;
+ __label__ lbl2;
+ int x = magic();
+ static void *jtab[2];
+
+ jtab[0] = &&lbl1;
+ jtab[1] = &&lbl2;
+ goto *jtab[x];
+ lbl1:
+ return 1;
+ lbl2:
+ return 2;
+ ],ac_cv_prog_emu_cc=$CC,ac_cv_prog_emu_cc=no)
+ CC=$save_CC
+ CFLAGS=$save_CFLAGS
+ CPPFLAGS=$save_CPPFLAGS
+fi
+])
+if test $ac_cv_prog_emu_cc = no; then
+ AC_DEFINE(NO_JUMP_TABLE,[],[Defined if no found C compiler can handle jump tables])
+ EMU_CC=$CC
+else
+ EMU_CC=$ac_cv_prog_emu_cc
+fi
+AC_SUBST(EMU_CC)
+])
+
+
+
+dnl ----------------------------------------------------------------------
+dnl
+dnl LM_PROG_INSTALL_DIR
+dnl
+dnl This macro may be used by any OTP application.
+dnl
+dnl Figure out how to create directories with parents.
+dnl (In my opinion INSTALL_DIR is a bad name, MKSUBDIRS or something is better)
+dnl
+dnl We prefer 'install -d', but use 'mkdir -p' if it exists.
+dnl If none of these methods works, we give up.
+dnl
+
+
+AC_DEFUN(LM_PROG_INSTALL_DIR,
+[AC_CACHE_CHECK(how to create a directory including parents,
+ac_cv_prog_mkdir_p,
+[
+temp_name_base=config.$$
+temp_name=$temp_name_base/x/y/z
+$INSTALL -d $temp_name >/dev/null 2>&1
+ac_cv_prog_mkdir_p=none
+if test -d $temp_name; then
+ ac_cv_prog_mkdir_p="$INSTALL -d"
+else
+ mkdir -p $temp_name >/dev/null 2>&1
+ if test -d $temp_name; then
+ ac_cv_prog_mkdir_p="mkdir -p"
+ fi
+fi
+rm -fr $temp_name_base
+])
+
+case "${ac_cv_prog_mkdir_p}" in
+ none) AC_MSG_ERROR(don't know how create directories with parents) ;;
+ *) INSTALL_DIR="$ac_cv_prog_mkdir_p" AC_SUBST(INSTALL_DIR) ;;
+esac
+])
+
+
+dnl ----------------------------------------------------------------------
+dnl
+dnl LM_PROG_PERL5
+dnl
+dnl Try to find perl version 5. If found set PERL to the absolute path
+dnl of the program, if not found set PERL to false.
+dnl
+dnl On some systems /usr/bin/perl is perl 4 and e.g.
+dnl /usr/local/bin/perl is perl 5. We try to handle this case by
+dnl putting a couple of
+dnl Tries to handle the case that there are two programs called perl
+dnl in the path and one of them is perl 5 and the other isn't.
+dnl
+AC_DEFUN(LM_PROG_PERL5,
+[AC_PATH_PROGS(PERL, perl5 perl, false,
+ /usr/local/bin:/opt/local/bin:/usr/local/gnu/bin:${PATH})
+changequote(, )dnl
+dnl[ That bracket is needed to balance the right bracket below
+if test "$PERL" = "false" || $PERL -e 'exit ($] >= 5)'; then
+changequote([, ])dnl
+ ac_cv_path_PERL=false
+ PERL=false
+dnl AC_MSG_WARN(perl version 5 not found)
+fi
+])dnl
+
+
+dnl ----------------------------------------------------------------------
+dnl
+dnl LM_DECL_SO_BSDCOMPAT
+dnl
+dnl Check if the system has the SO_BSDCOMPAT flag on sockets (linux)
+dnl
+AC_DEFUN(LM_DECL_SO_BSDCOMPAT,
+[AC_CACHE_CHECK([for SO_BSDCOMPAT declaration], ac_cv_decl_so_bsdcompat,
+AC_TRY_COMPILE([#include <sys/socket.h>], [int i = SO_BSDCOMPAT;],
+ ac_cv_decl_so_bsdcompat=yes,
+ ac_cv_decl_so_bsdcompat=no))
+
+case "${ac_cv_decl_so_bsdcompat}" in
+ "yes" ) AC_DEFINE(HAVE_SO_BSDCOMPAT,[],
+ [Define if you have SO_BSDCOMPAT flag on sockets]) ;;
+ * ) ;;
+esac
+])
+
+
+dnl ----------------------------------------------------------------------
+dnl
+dnl LM_DECL_INADDR_LOOPBACK
+dnl
+dnl Try to find declaration of INADDR_LOOPBACK, if nowhere provide a default
+dnl
+
+AC_DEFUN(LM_DECL_INADDR_LOOPBACK,
+[AC_CACHE_CHECK([for INADDR_LOOPBACK in netinet/in.h],
+ ac_cv_decl_inaddr_loopback,
+[AC_TRY_COMPILE([#include <sys/types.h>
+#include <netinet/in.h>], [int i = INADDR_LOOPBACK;],
+ac_cv_decl_inaddr_loopback=yes, ac_cv_decl_inaddr_loopback=no)
+])
+
+if test ${ac_cv_decl_inaddr_loopback} = no; then
+ AC_CACHE_CHECK([for INADDR_LOOPBACK in rpc/types.h],
+ ac_cv_decl_inaddr_loopback_rpc,
+ AC_TRY_COMPILE([#include <rpc/types.h>],
+ [int i = INADDR_LOOPBACK;],
+ ac_cv_decl_inaddr_loopback_rpc=yes,
+ ac_cv_decl_inaddr_loopback_rpc=no))
+
+ case "${ac_cv_decl_inaddr_loopback_rpc}" in
+ "yes" )
+ AC_DEFINE(DEF_INADDR_LOOPBACK_IN_RPC_TYPES_H,[],
+ [Define if you need to include rpc/types.h to get INADDR_LOOPBACK defined]) ;;
+ * )
+ AC_CACHE_CHECK([for INADDR_LOOPBACK in winsock2.h],
+ ac_cv_decl_inaddr_loopback_winsock2,
+ AC_TRY_COMPILE([#define WIN32_LEAN_AND_MEAN
+ #include <winsock2.h>],
+ [int i = INADDR_LOOPBACK;],
+ ac_cv_decl_inaddr_loopback_winsock2=yes,
+ ac_cv_decl_inaddr_loopback_winsock2=no))
+ case "${ac_cv_decl_inaddr_loopback_winsock2}" in
+ "yes" )
+ AC_DEFINE(DEF_INADDR_LOOPBACK_IN_WINSOCK2_H,[],
+ [Define if you need to include winsock2.h to get INADDR_LOOPBACK defined]) ;;
+ * )
+ # couldn't find it anywhere
+ AC_DEFINE(HAVE_NO_INADDR_LOOPBACK,[],
+ [Define if you don't have a definition of INADDR_LOOPBACK]) ;;
+ esac;;
+ esac
+fi
+])
+
+
+dnl ----------------------------------------------------------------------
+dnl
+dnl LM_STRUCT_SOCKADDR_SA_LEN
+dnl
+dnl Check if the sockaddr structure has the field sa_len
+dnl
+
+AC_DEFUN(LM_STRUCT_SOCKADDR_SA_LEN,
+[AC_CACHE_CHECK([whether struct sockaddr has sa_len field],
+ ac_cv_struct_sockaddr_sa_len,
+AC_TRY_COMPILE([#include <sys/types.h>
+#include <sys/socket.h>], [struct sockaddr s; s.sa_len = 10;],
+ ac_cv_struct_sockaddr_sa_len=yes, ac_cv_struct_sockaddr_sa_len=no))
+
+dnl FIXME convbreak
+case ${ac_cv_struct_sockaddr_sa_len} in
+ "no" ) AC_DEFINE(NO_SA_LEN,[1],[Define if you dont have salen]) ;;
+ *) ;;
+esac
+])
+
+dnl ----------------------------------------------------------------------
+dnl
+dnl LM_STRUCT_EXCEPTION
+dnl
+dnl Check to see whether the system supports the matherr function
+dnl and its associated type "struct exception".
+dnl
+
+AC_DEFUN(LM_STRUCT_EXCEPTION,
+[AC_CACHE_CHECK([for struct exception (and matherr function)],
+ ac_cv_struct_exception,
+AC_TRY_COMPILE([#include <math.h>],
+ [struct exception x; x.type = DOMAIN; x.type = SING;],
+ ac_cv_struct_exception=yes, ac_cv_struct_exception=no))
+
+case "${ac_cv_struct_exception}" in
+ "yes" ) AC_DEFINE(USE_MATHERR,[1],[Define if you have matherr() function and struct exception type]) ;;
+ * ) ;;
+esac
+])
+
+
+dnl ----------------------------------------------------------------------
+dnl
+dnl LM_SYS_IPV6
+dnl
+dnl Check for ipv6 support and what the in6_addr structure is called.
+dnl (early linux used in_addr6 insted of in6_addr)
+dnl
+
+AC_DEFUN(LM_SYS_IPV6,
+[AC_MSG_CHECKING(for IP version 6 support)
+AC_CACHE_VAL(ac_cv_sys_ipv6_support,
+[ok_so_far=yes
+ AC_TRY_COMPILE([#include <sys/types.h>
+#ifdef __WIN32__
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#else
+#include <netinet/in.h>
+#endif],
+ [struct in6_addr a6; struct sockaddr_in6 s6;], ok_so_far=yes, ok_so_far=no)
+
+if test $ok_so_far = yes; then
+ ac_cv_sys_ipv6_support=yes
+else
+ AC_TRY_COMPILE([#include <sys/types.h>
+#ifdef __WIN32__
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#else
+#include <netinet/in.h>
+#endif],
+ [struct in_addr6 a6; struct sockaddr_in6 s6;],
+ ac_cv_sys_ipv6_support=in_addr6, ac_cv_sys_ipv6_support=no)
+fi
+])dnl
+
+dnl
+dnl Have to use old style AC_DEFINE due to BC with old autoconf.
+dnl
+
+case ${ac_cv_sys_ipv6_support} in
+ yes)
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(HAVE_IN6,[1],[Define if ipv6 is present])
+ ;;
+ in_addr6)
+ AC_MSG_RESULT([yes (but I am redefining in_addr6 to in6_addr)])
+ AC_DEFINE(HAVE_IN6,[1],[Define if ipv6 is present])
+ AC_DEFINE(HAVE_IN_ADDR6_STRUCT,[],[Early linux used in_addr6 instead of in6_addr, define if you have this])
+ ;;
+ *)
+ AC_MSG_RESULT(no)
+ ;;
+esac
+])
+
+
+dnl ----------------------------------------------------------------------
+dnl
+dnl LM_SYS_MULTICAST
+dnl
+dnl Check for multicast support. Only checks for multicast options in
+dnl setsockopt(), no check is performed that multicasting actually works.
+dnl If options are found defines HAVE_MULTICAST_SUPPORT
+dnl
+
+AC_DEFUN(LM_SYS_MULTICAST,
+[AC_CACHE_CHECK([for multicast support], ac_cv_sys_multicast_support,
+[AC_EGREP_CPP(yes,
+[#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#if defined(IP_MULTICAST_TTL) && defined(IP_MULTICAST_LOOP) && defined(IP_MULTICAST_IF) && defined(IP_ADD_MEMBERSHIP) && defined(IP_DROP_MEMBERSHIP)
+yes
+#endif
+], ac_cv_sys_multicast_support=yes, ac_cv_sys_multicast_support=no)])
+if test $ac_cv_sys_multicast_support = yes; then
+ AC_DEFINE(HAVE_MULTICAST_SUPPORT,[1],
+ [Define if setsockopt() accepts multicast options])
+fi
+])dnl
+
+
+dnl ----------------------------------------------------------------------
+dnl
+dnl LM_DECL_SYS_ERRLIST
+dnl
+dnl Define SYS_ERRLIST_DECLARED if the variable sys_errlist is declared
+dnl in a system header file, stdio.h or errno.h.
+dnl
+
+AC_DEFUN(LM_DECL_SYS_ERRLIST,
+[AC_CACHE_CHECK([for sys_errlist declaration in stdio.h or errno.h],
+ ac_cv_decl_sys_errlist,
+[AC_TRY_COMPILE([#include <stdio.h>
+#include <errno.h>], [char *msg = *(sys_errlist + 1);],
+ ac_cv_decl_sys_errlist=yes, ac_cv_decl_sys_errlist=no)])
+if test $ac_cv_decl_sys_errlist = yes; then
+ AC_DEFINE(SYS_ERRLIST_DECLARED,[],
+ [define if the variable sys_errlist is declared in a system header file])
+fi
+])
+
+
+dnl ----------------------------------------------------------------------
+dnl
+dnl LM_CHECK_FUNC_DECL( funname, declaration [, extra includes
+dnl [, action-if-found [, action-if-not-found]]] )
+dnl
+dnl Checks if the declaration "declaration" of "funname" conflicts
+dnl with the header files idea of how the function should be
+dnl declared. It is useful on systems which lack prototypes and you
+dnl need to provide your own (e.g. when you want to take the address
+dnl of a function). The 4'th argument is expanded if conflicting,
+dnl the 5'th argument otherwise
+dnl
+dnl
+
+AC_DEFUN(LM_CHECK_FUNC_DECL,
+[AC_MSG_CHECKING([for conflicting declaration of $1])
+AC_CACHE_VAL(ac_cv_func_decl_$1,
+[AC_TRY_COMPILE([#include <stdio.h>
+$3],[$2
+char *c = (char *)$1;
+], eval "ac_cv_func_decl_$1=no", eval "ac_cv_func_decl_$1=yes")])
+if eval "test \"`echo '$ac_cv_func_decl_'$1`\" = yes"; then
+ AC_MSG_RESULT(yes)
+ ifelse([$4], , :, [$4])
+else
+ AC_MSG_RESULT(no)
+ifelse([$5], , , [$5
+])dnl
+fi
+])
+
+dnl ----------------------------------------------------------------------
+dnl
+dnl AC_DOUBLE_MIDDLE_ENDIAN
+dnl
+dnl Checks whether doubles are represented in "middle-endian" format.
+dnl Sets ac_cv_double_middle_endian={no,yes,unknown} accordingly,
+dnl as well as DOUBLE_MIDDLE_ENDIAN.
+dnl
+dnl
+
+AC_DEFUN([AC_C_DOUBLE_MIDDLE_ENDIAN],
+[AC_CACHE_CHECK(whether double word ordering is middle-endian, ac_cv_c_double_middle_endian,
+[# It does not; compile a test program.
+AC_RUN_IFELSE(
+[AC_LANG_SOURCE([[#include <stdlib.h>
+
+int
+main(void)
+{
+ int i = 0;
+ int zero = 0;
+ int bigendian;
+ int zero_index = 0;
+
+ union
+ {
+ long int l;
+ char c[sizeof (long int)];
+ } u;
+
+ /* we'll use the one with 32-bit words */
+ union
+ {
+ double d;
+ unsigned int c[2];
+ } vint;
+
+ union
+ {
+ double d;
+ unsigned long c[2];
+ } vlong;
+
+ union
+ {
+ double d;
+ unsigned short c[2];
+ } vshort;
+
+
+ /* Are we little or big endian? From Harbison&Steele. */
+ u.l = 1;
+ bigendian = (u.c[sizeof (long int) - 1] == 1);
+
+ zero_index = bigendian ? 1 : 0;
+
+ vint.d = 1.0;
+ vlong.d = 1.0;
+ vshort.d = 1.0;
+
+ if (sizeof(unsigned int) == 4)
+ {
+ if (vint.c[zero_index] != 0)
+ zero = 1;
+ }
+ else if (sizeof(unsigned long) == 4)
+ {
+ if (vlong.c[zero_index] != 0)
+ zero = 1;
+ }
+ else if (sizeof(unsigned short) == 4)
+ {
+ if (vshort.c[zero_index] != 0)
+ zero = 1;
+ }
+
+ exit (zero);
+}
+]])],
+ [ac_cv_c_double_middle_endian=no],
+ [ac_cv_c_double_middle_endian=yes],
+ [ac_cv_c_double_middle=unknown])])
+case $ac_cv_c_double_middle_endian in
+ yes)
+ m4_default([$1],
+ [AC_DEFINE([DOUBLE_MIDDLE_ENDIAN], 1,
+ [Define to 1 if your processor stores the words in a double in
+ middle-endian format (like some ARMs).])]) ;;
+ no)
+ $2 ;;
+ *)
+ m4_default([$3],
+ [AC_MSG_WARN([unknown double endianness
+presetting ac_cv_c_double_middle_endian=no (or yes) will help])]) ;;
+esac
+])# AC_C_DOUBLE_MIDDLE_ENDIAN
+
+
+dnl ----------------------------------------------------------------------
+dnl
+dnl LM_CHECK_THR_LIB
+dnl
+dnl This macro may be used by any OTP application.
+dnl
+dnl LM_CHECK_THR_LIB sets THR_LIBS, THR_DEFS, and THR_LIB_NAME. It also
+dnl checks for some pthread headers which will appear in DEFS or config.h.
+dnl
+
+AC_DEFUN(LM_CHECK_THR_LIB,
+[
+
+NEED_NPTL_PTHREAD_H=no
+
+dnl win32?
+AC_MSG_CHECKING([for native win32 threads])
+if test "X$host_os" = "Xwin32"; then
+ AC_MSG_RESULT(yes)
+ THR_DEFS="-DWIN32_THREADS"
+ THR_LIBS=
+ THR_LIB_NAME=win32_threads
+ THR_LIB_TYPE=win32_threads
+else
+ AC_MSG_RESULT(no)
+ THR_DEFS=
+ THR_LIBS=
+ THR_LIB_NAME=
+ THR_LIB_TYPE=posix_unknown
+
+dnl Try to find POSIX threads
+
+dnl The usual pthread lib...
+ AC_CHECK_LIB(pthread, pthread_create, THR_LIBS="-lpthread")
+
+dnl Very old versions of FreeBSD have pthreads in special c library, c_r...
+ if test "x$THR_LIBS" = "x"; then
+ AC_CHECK_LIB(c_r, pthread_create, THR_LIBS="-lc_r")
+ fi
+
+dnl QNX has pthreads in standard C library
+ if test "x$THR_LIBS" = "x"; then
+ AC_CHECK_FUNC(pthread_create, THR_LIBS="none_needed")
+ fi
+
+dnl On ofs1 the '-pthread' switch should be used
+ if test "x$THR_LIBS" = "x"; then
+ AC_MSG_CHECKING([if the '-pthread' switch can be used])
+ saved_cflags=$CFLAGS
+ CFLAGS="$CFLAGS -pthread"
+ AC_TRY_LINK([#include <pthread.h>],
+ pthread_create((void*)0,(void*)0,(void*)0,(void*)0);,
+ [THR_DEFS="-pthread"
+ THR_LIBS="-pthread"])
+ CFLAGS=$saved_cflags
+ if test "x$THR_LIBS" != "x"; then
+ AC_MSG_RESULT(yes)
+ else
+ AC_MSG_RESULT(no)
+ fi
+ fi
+
+ if test "x$THR_LIBS" != "x"; then
+ THR_DEFS="$THR_DEFS -D_THREAD_SAFE -D_REENTRANT -DPOSIX_THREADS"
+ THR_LIB_NAME=pthread
+ if test "x$THR_LIBS" = "xnone_needed"; then
+ THR_LIBS=
+ fi
+ case $host_os in
+ solaris*)
+ THR_DEFS="$THR_DEFS -D_POSIX_PTHREAD_SEMANTICS" ;;
+ linux*)
+ THR_DEFS="$THR_DEFS -D_POSIX_THREAD_SAFE_FUNCTIONS"
+
+ LM_CHECK_GETCONF
+ AC_MSG_CHECKING(for Native POSIX Thread Library)
+ libpthr_vsn=`$GETCONF GNU_LIBPTHREAD_VERSION 2>/dev/null`
+ if test $? -eq 0; then
+ case "$libpthr_vsn" in
+ *nptl*|*NPTL*) nptl=yes;;
+ *) nptl=no;;
+ esac
+ elif test "$cross_compiling" = "yes"; then
+ case "$erl_xcomp_linux_nptl" in
+ "") nptl=cross;;
+ yes|no) nptl=$erl_xcomp_linux_nptl;;
+ *) AC_MSG_ERROR([Bad erl_xcomp_linux_nptl value: $erl_xcomp_linux_nptl]);;
+ esac
+ else
+ nptl=no
+ fi
+ AC_MSG_RESULT($nptl)
+ if test $nptl = cross; then
+ nptl=yes
+ AC_MSG_WARN([result yes guessed because of cross compilation])
+ fi
+ if test $nptl = yes; then
+ THR_LIB_TYPE=posix_nptl
+ need_nptl_incldir=no
+ AC_CHECK_HEADER(nptl/pthread.h,
+ [need_nptl_incldir=yes
+ NEED_NPTL_PTHREAD_H=yes])
+ if test $need_nptl_incldir = yes; then
+ # Ahh...
+ nptl_path="$C_INCLUDE_PATH:$CPATH"
+ if test X$cross_compiling != Xyes; then
+ nptl_path="$nptl_path:/usr/local/include:/usr/include"
+ else
+ IROOT="$erl_xcomp_isysroot"
+ test "$IROOT" != "" || IROOT="$erl_xcomp_sysroot"
+ test "$IROOT" != "" || AC_MSG_ERROR([Don't know where to search for includes! Please set erl_xcomp_isysroot])
+ nptl_path="$nptl_path:$IROOT/usr/local/include:$IROOT/usr/include"
+ fi
+ nptl_ws_path=
+ save_ifs="$IFS"; IFS=":"
+ for dir in $nptl_path; do
+ if test "x$dir" != "x"; then
+ nptl_ws_path="$nptl_ws_path $dir"
+ fi
+ done
+ IFS=$save_ifs
+ nptl_incldir=
+ for dir in $nptl_ws_path; do
+ AC_CHECK_HEADER($dir/nptl/pthread.h,
+ nptl_incldir=$dir/nptl)
+ if test "x$nptl_incldir" != "x"; then
+ THR_DEFS="$THR_DEFS -isystem $nptl_incldir"
+ break
+ fi
+ done
+ if test "x$nptl_incldir" = "x"; then
+ AC_MSG_ERROR(Failed to locate nptl system include directory)
+ fi
+ fi
+ fi
+ ;;
+ *) ;;
+ esac
+
+ dnl We sometimes need THR_DEFS in order to find certain headers
+ dnl (at least for pthread.h on osf1).
+ saved_cppflags=$CPPFLAGS
+ CPPFLAGS="$CPPFLAGS $THR_DEFS"
+
+ dnl
+ dnl Check for headers
+ dnl
+
+ AC_CHECK_HEADER(pthread.h,
+ AC_DEFINE(HAVE_PTHREAD_H, 1, \
+[Define if you have the <pthread.h> header file.]))
+
+ dnl Some Linuxes have <pthread/mit/pthread.h> instead of <pthread.h>
+ AC_CHECK_HEADER(pthread/mit/pthread.h, \
+ AC_DEFINE(HAVE_MIT_PTHREAD_H, 1, \
+[Define if the pthread.h header file is in pthread/mit directory.]))
+
+ dnl restore CPPFLAGS
+ CPPFLAGS=$saved_cppflags
+
+ fi
+fi
+
+])
+
+AC_DEFUN(ERL_INTERNAL_LIBS,
+[
+
+ERTS_INTERNAL_X_LIBS=
+
+AC_CHECK_LIB(kstat, kstat_open,
+[AC_DEFINE(HAVE_KSTAT, 1, [Define if you have kstat])
+ERTS_INTERNAL_X_LIBS="$ERTS_INTERNAL_X_LIBS -lkstat"])
+
+AC_SUBST(ERTS_INTERNAL_X_LIBS)
+
+])
+
+AC_DEFUN(ETHR_CHK_SYNC_OP,
+[
+ AC_MSG_CHECKING([for $3-bit $1()])
+ case "$2" in
+ "1") sync_call="$1(&var);";;
+ "2") sync_call="$1(&var, ($4) 0);";;
+ "3") sync_call="$1(&var, ($4) 0, ($4) 0);";;
+ esac
+ have_sync_op=no
+ AC_TRY_LINK([],
+ [
+ $4 res;
+ volatile $4 var;
+ res = $sync_call
+ ],
+ [have_sync_op=yes])
+ test $have_sync_op = yes && $5
+ AC_MSG_RESULT([$have_sync_op])
+])
+
+AC_DEFUN(ETHR_CHK_INTERLOCKED,
+[
+ ilckd="$1"
+ AC_MSG_CHECKING([for ${ilckd}()])
+ case "$2" in
+ "1") ilckd_call="${ilckd}(var);";;
+ "2") ilckd_call="${ilckd}(var, ($3) 0);";;
+ "3") ilckd_call="${ilckd}(var, ($3) 0, ($3) 0);";;
+ "4") ilckd_call="${ilckd}(var, ($3) 0, ($3) 0, arr);";;
+ esac
+ have_interlocked_op=no
+ AC_TRY_LINK(
+ [
+ #define WIN32_LEAN_AND_MEAN
+ #include <windows.h>
+ #include <intrin.h>
+ ],
+ [
+ volatile $3 *var;
+ volatile $3 arr[2];
+
+ $ilckd_call
+ return 0;
+ ],
+ [have_interlocked_op=yes])
+ test $have_interlocked_op = yes && $4
+ AC_MSG_RESULT([$have_interlocked_op])
+])
+
+dnl ----------------------------------------------------------------------
+dnl
+dnl ERL_FIND_ETHR_LIB
+dnl
+dnl NOTE! This macro may be changed at any time! Should *only* be used by
+dnl ERTS!
+dnl
+dnl Find a thread library to use. Sets ETHR_LIBS to libraries to link
+dnl with, ETHR_X_LIBS to extra libraries to link with (same as ETHR_LIBS
+dnl except that the ethread lib itself is not included), ETHR_DEFS to
+dnl defines to compile with, ETHR_THR_LIB_BASE to the name of the
+dnl thread library which the ethread library is based on, and ETHR_LIB_NAME
+dnl to the name of the library where the ethread implementation is located.
+dnl ERL_FIND_ETHR_LIB currently searches for 'pthreads', and
+dnl 'win32_threads'. If no thread library was found ETHR_LIBS, ETHR_X_LIBS,
+dnl ETHR_DEFS, ETHR_THR_LIB_BASE, and ETHR_LIB_NAME are all set to the
+dnl empty string.
+dnl
+
+AC_DEFUN(ERL_FIND_ETHR_LIB,
+[
+
+LM_CHECK_THR_LIB
+ERL_INTERNAL_LIBS
+
+ethr_have_native_atomics=no
+ethr_have_native_spinlock=no
+ETHR_THR_LIB_BASE="$THR_LIB_NAME"
+ETHR_THR_LIB_BASE_TYPE="$THR_LIB_TYPE"
+ETHR_DEFS="$THR_DEFS"
+ETHR_X_LIBS="$THR_LIBS $ERTS_INTERNAL_X_LIBS"
+ETHR_LIBS=
+ETHR_LIB_NAME=
+
+ethr_modified_default_stack_size=
+
+dnl Name of lib where ethread implementation is located
+ethr_lib_name=ethread
+
+case "$THR_LIB_NAME" in
+
+ win32_threads)
+ ETHR_THR_LIB_BASE_DIR=win
+ # * _WIN32_WINNT >= 0x0400 is needed for
+ # TryEnterCriticalSection
+ # * _WIN32_WINNT >= 0x0403 is needed for
+ # InitializeCriticalSectionAndSpinCount
+ # The ethread lib will refuse to build if _WIN32_WINNT < 0x0403.
+ #
+ # -D_WIN32_WINNT should have been defined in $CPPFLAGS; fetch it
+ # and save it in ETHR_DEFS.
+ found_win32_winnt=no
+ for cppflag in $CPPFLAGS; do
+ case $cppflag in
+ -DWINVER*)
+ ETHR_DEFS="$ETHR_DEFS $cppflag"
+ ;;
+ -D_WIN32_WINNT*)
+ ETHR_DEFS="$ETHR_DEFS $cppflag"
+ found_win32_winnt=yes
+ ;;
+ *)
+ ;;
+ esac
+ done
+ if test $found_win32_winnt = no; then
+ AC_MSG_ERROR([-D_WIN32_WINNT missing in CPPFLAGS])
+ fi
+
+ AC_DEFINE(ETHR_WIN32_THREADS, 1, [Define if you have win32 threads])
+
+ ETHR_CHK_INTERLOCKED([_InterlockedDecrement], [1], [long], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDDECREMENT, 1, [Define if you have _InterlockedDecrement()]))
+ ETHR_CHK_INTERLOCKED([_InterlockedDecrement_rel], [1], [long], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDDECREMENT_REL, 1, [Define if you have _InterlockedDecrement_rel()]))
+ ETHR_CHK_INTERLOCKED([_InterlockedIncrement], [1], [long], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDINCREMENT, 1, [Define if you have _InterlockedIncrement()]))
+ ETHR_CHK_INTERLOCKED([_InterlockedIncrement_acq], [1], [long], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDINCREMENT_ACQ, 1, [Define if you have _InterlockedIncrement_acq()]))
+ ETHR_CHK_INTERLOCKED([_InterlockedExchangeAdd], [2], [long], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDEXCHANGEADD, 1, [Define if you have _InterlockedExchangeAdd()]))
+ ETHR_CHK_INTERLOCKED([_InterlockedExchangeAdd_acq], [2], [long], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDEXCHANGEADD_ACQ, 1, [Define if you have _InterlockedExchangeAdd_acq()]))
+ ETHR_CHK_INTERLOCKED([_InterlockedAnd], [2], [long], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDAND, 1, [Define if you have _InterlockedAnd()]))
+ ETHR_CHK_INTERLOCKED([_InterlockedOr], [2], [long], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDOR, 1, [Define if you have _InterlockedOr()]))
+ ETHR_CHK_INTERLOCKED([_InterlockedExchange], [2], [long], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDEXCHANGE, 1, [Define if you have _InterlockedExchange()]))
+ ETHR_CHK_INTERLOCKED([_InterlockedCompareExchange], [3], [long], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDCOMPAREEXCHANGE, 1, [Define if you have _InterlockedCompareExchange()]))
+ test "$have_interlocked_op" = "yes" && ethr_have_native_atomics=yes
+ ETHR_CHK_INTERLOCKED([_InterlockedCompareExchange_acq], [3], [long], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDCOMPAREEXCHANGE_ACQ, 1, [Define if you have _InterlockedCompareExchange_acq()]))
+ test "$have_interlocked_op" = "yes" && ethr_have_native_atomics=yes
+ ETHR_CHK_INTERLOCKED([_InterlockedCompareExchange_rel], [3], [long], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDCOMPAREEXCHANGE_REL, 1, [Define if you have _InterlockedCompareExchange_rel()]))
+ test "$have_interlocked_op" = "yes" && ethr_have_native_atomics=yes
+
+ ETHR_CHK_INTERLOCKED([_InterlockedDecrement64], [1], [__int64], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDDECREMENT64, 1, [Define if you have _InterlockedDecrement64()]))
+ ETHR_CHK_INTERLOCKED([_InterlockedDecrement64_rel], [1], [__int64], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDDECREMENT64_REL, 1, [Define if you have _InterlockedDecrement64_rel()]))
+ ETHR_CHK_INTERLOCKED([_InterlockedIncrement64], [1], [__int64], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDINCREMENT64, 1, [Define if you have _InterlockedIncrement64()]))
+ ETHR_CHK_INTERLOCKED([_InterlockedIncrement64_acq], [1], [__int64], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDINCREMENT64_ACQ, 1, [Define if you have _InterlockedIncrement64_acq()]))
+ ETHR_CHK_INTERLOCKED([_InterlockedExchangeAdd64], [2], [__int64], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDEXCHANGEADD64, 1, [Define if you have _InterlockedExchangeAdd64()]))
+ ETHR_CHK_INTERLOCKED([_InterlockedExchangeAdd64_acq], [2], [__int64], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDEXCHANGEADD64_ACQ, 1, [Define if you have _InterlockedExchangeAdd64_acq()]))
+ ETHR_CHK_INTERLOCKED([_InterlockedAnd64], [2], [__int64], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDAND64, 1, [Define if you have _InterlockedAnd64()]))
+ ETHR_CHK_INTERLOCKED([_InterlockedOr64], [2], [__int64], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDOR64, 1, [Define if you have _InterlockedOr64()]))
+ ETHR_CHK_INTERLOCKED([_InterlockedExchange64], [2], [__int64], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDEXCHANGE64, 1, [Define if you have _InterlockedExchange64()]))
+ ETHR_CHK_INTERLOCKED([_InterlockedCompareExchange64], [3], [__int64], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDCOMPAREEXCHANGE64, 1, [Define if you have _InterlockedCompareExchange64()]))
+ test "$have_interlocked_op" = "yes" && ethr_have_native_atomics=yes
+ ETHR_CHK_INTERLOCKED([_InterlockedCompareExchange64_acq], [3], [__int64], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDCOMPAREEXCHANGE64_ACQ, 1, [Define if you have _InterlockedCompareExchange64_acq()]))
+ test "$have_interlocked_op" = "yes" && ethr_have_native_atomics=yes
+ ETHR_CHK_INTERLOCKED([_InterlockedCompareExchange64_rel], [3], [__int64], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDCOMPAREEXCHANGE64_REL, 1, [Define if you have _InterlockedCompareExchange64_rel()]))
+ test "$have_interlocked_op" = "yes" && ethr_have_native_atomics=yes
+
+ ETHR_CHK_INTERLOCKED([_InterlockedCompareExchange128], [4], [__int64], AC_DEFINE_UNQUOTED(ETHR_HAVE__INTERLOCKEDCOMPAREEXCHANGE128, 1, [Define if you have _InterlockedCompareExchange128()]))
+
+ test "$ethr_have_native_atomics" = "yes" && ethr_have_native_spinlock=yes
+ ;;
+
+ pthread)
+ ETHR_THR_LIB_BASE_DIR=pthread
+ AC_DEFINE(ETHR_PTHREADS, 1, [Define if you have pthreads])
+ case $host_os in
+ openbsd*)
+ # The default stack size is insufficient for our needs
+ # on OpenBSD. We increase it to 256 kilo words.
+ ethr_modified_default_stack_size=256;;
+ linux*)
+ ETHR_DEFS="$ETHR_DEFS -D_GNU_SOURCE"
+
+ if test X$cross_compiling = Xyes; then
+ case X$erl_xcomp_linux_usable_sigusrx in
+ X) usable_sigusrx=cross;;
+ Xyes|Xno) usable_sigusrx=$erl_xcomp_linux_usable_sigusrx;;
+ *) AC_MSG_ERROR([Bad erl_xcomp_linux_usable_sigusrx value: $erl_xcomp_linux_usable_sigusrx]);;
+ esac
+ case X$erl_xcomp_linux_usable_sigaltstack in
+ X) usable_sigaltstack=cross;;
+ Xyes|Xno) usable_sigaltstack=$erl_xcomp_linux_usable_sigaltstack;;
+ *) AC_MSG_ERROR([Bad erl_xcomp_linux_usable_sigaltstack value: $erl_xcomp_linux_usable_sigaltstack]);;
+ esac
+ else
+ # FIXME: Test for actual problems instead of kernel versions
+ linux_kernel_vsn_=`uname -r`
+ case $linux_kernel_vsn_ in
+ [[0-1]].*|2.[[0-1]]|2.[[0-1]].*)
+ usable_sigusrx=no
+ usable_sigaltstack=no;;
+ 2.[[2-3]]|2.[[2-3]].*)
+ usable_sigusrx=yes
+ usable_sigaltstack=no;;
+ *)
+ usable_sigusrx=yes
+ usable_sigaltstack=yes;;
+ esac
+ fi
+
+ AC_MSG_CHECKING(if SIGUSR1 and SIGUSR2 can be used)
+ AC_MSG_RESULT($usable_sigusrx)
+ if test $usable_sigusrx = cross; then
+ usable_sigusrx=yes
+ AC_MSG_WARN([result yes guessed because of cross compilation])
+ fi
+ if test $usable_sigusrx = no; then
+ ETHR_DEFS="$ETHR_DEFS -DETHR_UNUSABLE_SIGUSRX"
+ fi
+
+ AC_MSG_CHECKING(if sigaltstack can be used)
+ AC_MSG_RESULT($usable_sigaltstack)
+ if test $usable_sigaltstack = cross; then
+ usable_sigaltstack=yes
+ AC_MSG_WARN([result yes guessed because of cross compilation])
+ fi
+ if test $usable_sigaltstack = no; then
+ ETHR_DEFS="$ETHR_DEFS -DETHR_UNUSABLE_SIGALTSTACK"
+ fi
+ ;;
+ *) ;;
+ esac
+
+ dnl We sometimes need ETHR_DEFS in order to find certain headers
+ dnl (at least for pthread.h on osf1).
+ saved_cppflags="$CPPFLAGS"
+ CPPFLAGS="$CPPFLAGS $ETHR_DEFS"
+
+ dnl We need the thread library in order to find some functions
+ saved_libs="$LIBS"
+ LIBS="$LIBS $ETHR_X_LIBS"
+
+ dnl
+ dnl Check for headers
+ dnl
+
+ AC_CHECK_HEADER(pthread.h, \
+ AC_DEFINE(ETHR_HAVE_PTHREAD_H, 1, \
+[Define if you have the <pthread.h> header file.]))
+
+ dnl Some Linuxes have <pthread/mit/pthread.h> instead of <pthread.h>
+ AC_CHECK_HEADER(pthread/mit/pthread.h, \
+ AC_DEFINE(ETHR_HAVE_MIT_PTHREAD_H, 1, \
+[Define if the pthread.h header file is in pthread/mit directory.]))
+
+ if test $NEED_NPTL_PTHREAD_H = yes; then
+ AC_DEFINE(ETHR_NEED_NPTL_PTHREAD_H, 1, \
+[Define if you need the <nptl/pthread.h> header file.])
+ fi
+
+ AC_CHECK_HEADER(sched.h, \
+ AC_DEFINE(ETHR_HAVE_SCHED_H, 1, \
+[Define if you have the <sched.h> header file.]))
+
+ AC_CHECK_HEADER(sys/time.h, \
+ AC_DEFINE(ETHR_HAVE_SYS_TIME_H, 1, \
+[Define if you have the <sys/time.h> header file.]))
+
+ AC_TRY_COMPILE([#include <time.h>
+ #include <sys/time.h>],
+ [struct timeval *tv; return 0;],
+ AC_DEFINE(ETHR_TIME_WITH_SYS_TIME, 1, \
+[Define if you can safely include both <sys/time.h> and <time.h>.]))
+
+
+ dnl
+ dnl Check for functions
+ dnl
+
+ AC_CHECK_FUNC(pthread_spin_lock, \
+ [ethr_have_native_spinlock=yes \
+ AC_DEFINE(ETHR_HAVE_PTHREAD_SPIN_LOCK, 1, \
+[Define if you have the pthread_spin_lock function.])])
+
+ have_sched_yield=no
+ have_librt_sched_yield=no
+ AC_CHECK_FUNC(sched_yield, [have_sched_yield=yes])
+ if test $have_sched_yield = no; then
+ AC_CHECK_LIB(rt, sched_yield,
+ [have_librt_sched_yield=yes
+ ETHR_X_LIBS="$ETHR_X_LIBS -lrt"])
+ fi
+ if test $have_sched_yield = yes || test $have_librt_sched_yield = yes; then
+ AC_DEFINE(ETHR_HAVE_SCHED_YIELD, 1, [Define if you have the sched_yield() function.])
+ AC_MSG_CHECKING([whether sched_yield() returns an int])
+ sched_yield_ret_int=no
+ AC_TRY_COMPILE([
+ #ifdef ETHR_HAVE_SCHED_H
+ #include <sched.h>
+ #endif
+ ],
+ [int sched_yield();],
+ [sched_yield_ret_int=yes])
+ AC_MSG_RESULT([$sched_yield_ret_int])
+ if test $sched_yield_ret_int = yes; then
+ AC_DEFINE(ETHR_SCHED_YIELD_RET_INT, 1, [Define if sched_yield() returns an int.])
+ fi
+ fi
+
+ have_pthread_yield=no
+ AC_CHECK_FUNC(pthread_yield, [have_pthread_yield=yes])
+ if test $have_pthread_yield = yes; then
+ AC_DEFINE(ETHR_HAVE_PTHREAD_YIELD, 1, [Define if you have the pthread_yield() function.])
+ AC_MSG_CHECKING([whether pthread_yield() returns an int])
+ pthread_yield_ret_int=no
+ AC_TRY_COMPILE([
+ #if defined(ETHR_NEED_NPTL_PTHREAD_H)
+ #include <nptl/pthread.h>
+ #elif defined(ETHR_HAVE_MIT_PTHREAD_H)
+ #include <pthread/mit/pthread.h>
+ #elif defined(ETHR_HAVE_PTHREAD_H)
+ #include <pthread.h>
+ #endif
+ ],
+ [int pthread_yield();],
+ [pthread_yield_ret_int=yes])
+ AC_MSG_RESULT([$pthread_yield_ret_int])
+ if test $pthread_yield_ret_int = yes; then
+ AC_DEFINE(ETHR_PTHREAD_YIELD_RET_INT, 1, [Define if pthread_yield() returns an int.])
+ fi
+ fi
+
+ have_pthread_rwlock_init=no
+ AC_CHECK_FUNC(pthread_rwlock_init, [have_pthread_rwlock_init=yes])
+ if test $have_pthread_rwlock_init = yes; then
+
+ ethr_have_pthread_rwlockattr_setkind_np=no
+ AC_CHECK_FUNC(pthread_rwlockattr_setkind_np,
+ [ethr_have_pthread_rwlockattr_setkind_np=yes])
+
+ if test $ethr_have_pthread_rwlockattr_setkind_np = yes; then
+ AC_DEFINE(ETHR_HAVE_PTHREAD_RWLOCKATTR_SETKIND_NP, 1, \
+[Define if you have the pthread_rwlockattr_setkind_np() function.])
+
+ AC_MSG_CHECKING([for PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP])
+ ethr_pthread_rwlock_writer_nonrecursive_initializer_np=no
+ AC_TRY_LINK([
+ #if defined(ETHR_NEED_NPTL_PTHREAD_H)
+ #include <nptl/pthread.h>
+ #elif defined(ETHR_HAVE_MIT_PTHREAD_H)
+ #include <pthread/mit/pthread.h>
+ #elif defined(ETHR_HAVE_PTHREAD_H)
+ #include <pthread.h>
+ #endif
+ ],
+ [
+ pthread_rwlockattr_t *attr;
+ return pthread_rwlockattr_setkind_np(attr,
+ PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP);
+ ],
+ [ethr_pthread_rwlock_writer_nonrecursive_initializer_np=yes])
+ AC_MSG_RESULT([$ethr_pthread_rwlock_writer_nonrecursive_initializer_np])
+ if test $ethr_pthread_rwlock_writer_nonrecursive_initializer_np = yes; then
+ AC_DEFINE(ETHR_HAVE_PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP, 1, \
+[Define if you have the PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP rwlock attribute.])
+ fi
+ fi
+ fi
+
+ if test "$force_pthread_rwlocks" = "yes"; then
+
+ AC_DEFINE(ETHR_FORCE_PTHREAD_RWLOCK, 1, \
+[Define if you want to force usage of pthread rwlocks])
+
+ if test $have_pthread_rwlock_init = yes; then
+ AC_MSG_WARN([Forced usage of pthread rwlocks. Note that this implementation may suffer from starvation issues.])
+ else
+ AC_MSG_ERROR([User forced usage of pthread rwlock, but no such implementation was found])
+ fi
+ fi
+
+ AC_CHECK_FUNC(pthread_attr_setguardsize, \
+ AC_DEFINE(ETHR_HAVE_PTHREAD_ATTR_SETGUARDSIZE, 1, \
+[Define if you have the pthread_attr_setguardsize function.]))
+
+ linux_futex=no
+ AC_MSG_CHECKING([for Linux futexes])
+ AC_TRY_LINK([
+ #include <sys/syscall.h>
+ #include <unistd.h>
+ #include <linux/futex.h>
+ #include <sys/time.h>
+ ],
+ [
+ int i = 1;
+ syscall(__NR_futex, (void *) &i, FUTEX_WAKE, 1,
+ (void*)0,(void*)0, 0);
+ syscall(__NR_futex, (void *) &i, FUTEX_WAIT, 0,
+ (void*)0,(void*)0, 0);
+ return 0;
+ ],
+ linux_futex=yes)
+ AC_MSG_RESULT([$linux_futex])
+ test $linux_futex = yes && AC_DEFINE(ETHR_HAVE_LINUX_FUTEX, 1, [Define if you have a linux futex implementation.])
+
+ AC_CHECK_SIZEOF(int)
+ AC_CHECK_SIZEOF(long)
+ AC_CHECK_SIZEOF(long long)
+ AC_CHECK_SIZEOF(__int128_t)
+
+ if test "$ac_cv_sizeof_int" = "4"; then
+ int32="int"
+ elif test "$ac_cv_sizeof_long" = "4"; then
+ int32="long"
+ elif test "$ac_cv_sizeof_long_long" = "4"; then
+ int32="long long"
+ else
+ AC_MSG_ERROR([No 32-bit type found])
+ fi
+
+ if test "$ac_cv_sizeof_int" = "8"; then
+ int64="int"
+ elif test "$ac_cv_sizeof_long" = "8"; then
+ int64="long"
+ elif test "$ac_cv_sizeof_long_long" = "8"; then
+ int64="long long"
+ else
+ AC_MSG_ERROR([No 64-bit type found])
+ fi
+
+ int128=no
+ if test "$ac_cv_sizeof___int128_t" = "16"; then
+ int128="__int128_t"
+ fi
+
+ ETHR_CHK_SYNC_OP([__sync_val_compare_and_swap], [3], [32], [$int32], AC_DEFINE(ETHR_HAVE___SYNC_VAL_COMPARE_AND_SWAP32, 1, [Define if you have __sync_val_compare_and_swap() for 32-bit integers]))
+ test "$have_sync_op" = "yes" && ethr_have_native_atomics=yes
+ ETHR_CHK_SYNC_OP([__sync_add_and_fetch], [2], [32], [$int32], AC_DEFINE(ETHR_HAVE___SYNC_ADD_AND_FETCH32, 1, [Define if you have __sync_add_and_fetch() for 32-bit integers]))
+ ETHR_CHK_SYNC_OP([__sync_fetch_and_and], [2], [32], [$int32], AC_DEFINE(ETHR_HAVE___SYNC_FETCH_AND_AND32, 1, [Define if you have __sync_fetch_and_and() for 32-bit integers]))
+ ETHR_CHK_SYNC_OP([__sync_fetch_and_or], [2], [32], [$int32], AC_DEFINE(ETHR_HAVE___SYNC_FETCH_AND_OR32, 1, [Define if you have __sync_fetch_and_or() for 32-bit integers]))
+
+ ETHR_CHK_SYNC_OP([__sync_val_compare_and_swap], [3], [64], [$int64], AC_DEFINE(ETHR_HAVE___SYNC_VAL_COMPARE_AND_SWAP64, 1, [Define if you have __sync_val_compare_and_swap() for 64-bit integers]))
+ test "$have_sync_op" = "yes" && ethr_have_native_atomics=yes
+ ETHR_CHK_SYNC_OP([__sync_add_and_fetch], [2], [64], [$int64], AC_DEFINE(ETHR_HAVE___SYNC_ADD_AND_FETCH64, 1, [Define if you have __sync_add_and_fetch() for 64-bit integers]))
+ ETHR_CHK_SYNC_OP([__sync_fetch_and_and], [2], [64], [$int64], AC_DEFINE(ETHR_HAVE___SYNC_FETCH_AND_AND64, 1, [Define if you have __sync_fetch_and_and() for 64-bit integers]))
+ ETHR_CHK_SYNC_OP([__sync_fetch_and_or], [2], [64], [$int64], AC_DEFINE(ETHR_HAVE___SYNC_FETCH_AND_OR64, 1, [Define if you have __sync_fetch_and_or() for 64-bit integers]))
+
+ if test $int128 != no; then
+ ETHR_CHK_SYNC_OP([__sync_val_compare_and_swap], [3], [128], [$int128], AC_DEFINE(ETHR_HAVE___SYNC_VAL_COMPARE_AND_SWAP128, 1, [Define if you have __sync_val_compare_and_swap() for 128-bit integers]))
+ fi
+
+ AC_MSG_CHECKING([for a usable libatomic_ops implementation])
+ case "x$with_libatomic_ops" in
+ xno | xyes | x)
+ libatomic_ops_include=
+ ;;
+ *)
+ if test -d "${with_libatomic_ops}/include"; then
+ libatomic_ops_include="-I$with_libatomic_ops/include"
+ CPPFLAGS="$CPPFLAGS $libatomic_ops_include"
+ else
+ AC_MSG_ERROR([libatomic_ops include directory $with_libatomic_ops/include not found])
+ fi;;
+ esac
+ ethr_have_libatomic_ops=no
+ AC_TRY_LINK([#include "atomic_ops.h"],
+ [
+ volatile AO_t x;
+ AO_t y;
+ int z;
+
+ AO_nop_full();
+ AO_store(&x, (AO_t) 0);
+ z = AO_load(&x);
+ z = AO_compare_and_swap_full(&x, (AO_t) 0, (AO_t) 1);
+ ],
+ [ethr_have_native_atomics=yes
+ ethr_have_libatomic_ops=yes])
+ AC_MSG_RESULT([$ethr_have_libatomic_ops])
+ if test $ethr_have_libatomic_ops = yes; then
+ AC_CHECK_SIZEOF(AO_t, ,
+ [
+ #include <stdio.h>
+ #include "atomic_ops.h"
+ ])
+ AC_DEFINE_UNQUOTED(ETHR_SIZEOF_AO_T, $ac_cv_sizeof_AO_t, [Define to the size of AO_t if libatomic_ops is used])
+
+ AC_DEFINE(ETHR_HAVE_LIBATOMIC_OPS, 1, [Define if you have libatomic_ops atomic operations])
+ if test "x$with_libatomic_ops" != "xno" && test "x$with_libatomic_ops" != "x"; then
+ AC_DEFINE(ETHR_PREFER_LIBATOMIC_OPS_NATIVE_IMPLS, 1, [Define if you prefer libatomic_ops native ethread implementations])
+ fi
+ ETHR_DEFS="$ETHR_DEFS $libatomic_ops_include"
+ elif test "x$with_libatomic_ops" != "xno" && test "x$with_libatomic_ops" != "x"; then
+ AC_MSG_ERROR([No usable libatomic_ops implementation found])
+ fi
+
+ case "$host_cpu" in
+ sparc | sun4u | sparc64 | sun4v)
+ case "$with_sparc_memory_order" in
+ "TSO")
+ AC_DEFINE(ETHR_SPARC_TSO, 1, [Define if only run in Sparc TSO mode]);;
+ "PSO")
+ AC_DEFINE(ETHR_SPARC_PSO, 1, [Define if only run in Sparc PSO, or TSO mode]);;
+ "RMO"|"")
+ AC_DEFINE(ETHR_SPARC_RMO, 1, [Define if run in Sparc RMO, PSO, or TSO mode]);;
+ *)
+ AC_MSG_ERROR([Unsupported Sparc memory order: $with_sparc_memory_order]);;
+ esac
+ ethr_have_native_atomics=yes;;
+ i86pc | i*86 | x86_64 | amd64)
+ if test "$enable_x86_out_of_order" = "yes"; then
+ AC_DEFINE(ETHR_X86_OUT_OF_ORDER, 1, [Define if x86/x86_64 out of order instructions should be synchronized])
+ fi
+ ethr_have_native_atomics=yes;;
+ macppc | ppc | "Power Macintosh")
+ ethr_have_native_atomics=yes;;
+ tile)
+ ethr_have_native_atomics=yes;;
+ *)
+ ;;
+ esac
+
+ test ethr_have_native_atomics = "yes" && ethr_have_native_spinlock=yes
+
+ dnl Restore LIBS
+ LIBS=$saved_libs
+ dnl restore CPPFLAGS
+ CPPFLAGS=$saved_cppflags
+
+ ;;
+ *)
+ ;;
+esac
+
+AC_MSG_CHECKING([whether default stack size should be modified])
+if test "x$ethr_modified_default_stack_size" != "x"; then
+ AC_DEFINE_UNQUOTED(ETHR_MODIFIED_DEFAULT_STACK_SIZE, $ethr_modified_default_stack_size, [Define if you want to modify the default stack size])
+ AC_MSG_RESULT([yes; to $ethr_modified_default_stack_size kilo words])
+else
+ AC_MSG_RESULT([no])
+fi
+
+if test "x$ETHR_THR_LIB_BASE" != "x"; then
+ ETHR_DEFS="-DUSE_THREADS $ETHR_DEFS"
+ ETHR_LIBS="-l$ethr_lib_name -lerts_internal_r $ETHR_X_LIBS"
+ ETHR_LIB_NAME=$ethr_lib_name
+fi
+
+AC_CHECK_SIZEOF(void *)
+AC_DEFINE_UNQUOTED(ETHR_SIZEOF_PTR, $ac_cv_sizeof_void_p, [Define to the size of pointers])
+
+AC_CHECK_SIZEOF(int)
+AC_DEFINE_UNQUOTED(ETHR_SIZEOF_INT, $ac_cv_sizeof_int, [Define to the size of int])
+AC_CHECK_SIZEOF(long)
+AC_DEFINE_UNQUOTED(ETHR_SIZEOF_LONG, $ac_cv_sizeof_long, [Define to the size of long])
+AC_CHECK_SIZEOF(long long)
+AC_DEFINE_UNQUOTED(ETHR_SIZEOF_LONG_LONG, $ac_cv_sizeof_long_long, [Define to the size of long long])
+AC_CHECK_SIZEOF(__int64)
+AC_DEFINE_UNQUOTED(ETHR_SIZEOF___INT64, $ac_cv_sizeof___int64, [Define to the size of __int64])
+AC_CHECK_SIZEOF(__int128_t)
+AC_DEFINE_UNQUOTED(ETHR_SIZEOF___INT128_T, $ac_cv_sizeof___int128_t, [Define to the size of __int128_t])
+
+
+case X$erl_xcomp_bigendian in
+ X) ;;
+ Xyes|Xno) ac_cv_c_bigendian=$erl_xcomp_bigendian;;
+ *) AC_MSG_ERROR([Bad erl_xcomp_bigendian value: $erl_xcomp_bigendian]);;
+esac
+
+AC_C_BIGENDIAN
+
+if test "$ac_cv_c_bigendian" = "yes"; then
+ AC_DEFINE(ETHR_BIGENDIAN, 1, [Define if bigendian])
+fi
+
+case X$erl_xcomp_double_middle_endian in
+ X) ;;
+ Xyes|Xno|Xunknown) ac_cv_c_double_middle_endian=$erl_xcomp_double_middle_endian;;
+ *) AC_MSG_ERROR([Bad erl_xcomp_double_middle_endian value: $erl_xcomp_double_middle_endian]);;
+esac
+
+AC_C_DOUBLE_MIDDLE_ENDIAN
+
+AC_ARG_ENABLE(native-ethr-impls,
+ AS_HELP_STRING([--disable-native-ethr-impls],
+ [disable native ethread implementations]),
+[ case "$enableval" in
+ no) disable_native_ethr_impls=yes ;;
+ *) disable_native_ethr_impls=no ;;
+ esac ], disable_native_ethr_impls=no)
+
+AC_ARG_ENABLE(x86-out-of-order,
+ AS_HELP_STRING([--enable-x86-out-of-order],
+ [enable x86/x84_64 out of order support (default disabled)]))
+
+test "X$disable_native_ethr_impls" = "Xyes" &&
+ AC_DEFINE(ETHR_DISABLE_NATIVE_IMPLS, 1, [Define if you want to disable native ethread implementations])
+
+AC_ARG_ENABLE(prefer-gcc-native-ethr-impls,
+ AS_HELP_STRING([--enable-prefer-gcc-native-ethr-impls],
+ [prefer gcc native ethread implementations]),
+[ case "$enableval" in
+ yes) enable_prefer_gcc_native_ethr_impls=yes ;;
+ *) enable_prefer_gcc_native_ethr_impls=no ;;
+ esac ], enable_prefer_gcc_native_ethr_impls=no)
+
+test $enable_prefer_gcc_native_ethr_impls = yes &&
+ AC_DEFINE(ETHR_PREFER_GCC_NATIVE_IMPLS, 1, [Define if you prefer gcc native ethread implementations])
+
+AC_ARG_WITH(libatomic_ops,
+ AS_HELP_STRING([--with-libatomic_ops=PATH],
+ [specify and prefer usage of libatomic_ops in the ethread library]))
+
+AC_ARG_WITH(with_sparc_memory_order,
+ AS_HELP_STRING([--with-sparc-memory-order=TSO|PSO|RMO],
+ [specify sparc memory order (defaults to RMO)]))
+
+ETHR_X86_SSE2_ASM=no
+case "$GCC-$ac_cv_sizeof_void_p-$host_cpu" in
+ yes-4-i86pc | yes-4-i*86 | yes-4-x86_64 | yes-4-amd64)
+ AC_MSG_CHECKING([for gcc sse2 asm support])
+ save_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS -msse2"
+ gcc_sse2_asm=no
+ AC_TRY_COMPILE([],
+ [
+ long long x, *y;
+ __asm__ __volatile__("movq %1, %0\n\t" : "=x"(x) : "m"(*y) : "memory");
+ ],
+ [gcc_sse2_asm=yes])
+ CFLAGS="$save_CFLAGS"
+ AC_MSG_RESULT([$gcc_sse2_asm])
+ if test "$gcc_sse2_asm" = "yes"; then
+ AC_DEFINE(ETHR_GCC_HAVE_SSE2_ASM_SUPPORT, 1, [Define if you use a gcc that supports -msse2 and understand sse2 specific asm statements])
+ ETHR_X86_SSE2_ASM=yes
+ fi
+ ;;
+ *)
+ ;;
+esac
+
+case "$GCC-$host_cpu" in
+ yes-i86pc | yes-i*86 | yes-x86_64 | yes-amd64)
+ gcc_dw_cmpxchg_asm=no
+ AC_MSG_CHECKING([for gcc double word cmpxchg asm support])
+ AC_TRY_COMPILE([],
+ [
+ char xchgd;
+ long new[2], xchg[2], *p;
+ __asm__ __volatile__(
+#if ETHR_SIZEOF_PTR == 4 && defined(__PIC__) && __PIC__
+ "pushl %%ebx\n\t"
+ "movl %8, %%ebx\n\t"
+#endif
+#if ETHR_SIZEOF_PTR == 4
+ "lock; cmpxchg8b %0\n\t"
+#else
+ "lock; cmpxchg16b %0\n\t"
+#endif
+ "setz %3\n\t"
+#if ETHR_SIZEOF_PTR == 4 && defined(__PIC__) && __PIC__
+ "popl %%ebx\n\t"
+#endif
+ : "=m"(*p), "=d"(xchg[1]), "=a"(xchg[0]), "=c"(xchgd)
+ : "m"(*p), "1"(xchg[1]), "2"(xchg[0]), "3"(new[1]),
+#if ETHR_SIZEOF_PTR == 4 && defined(__PIC__) && __PIC__
+ "r"(new[0])
+#else
+ "b"(new[0])
+#endif
+ : "cc", "memory");
+
+ ],
+ [gcc_dw_cmpxchg_asm=yes])
+ if test $gcc_dw_cmpxchg_asm = no && test $ac_cv_sizeof_void_p = 4; then
+ AC_TRY_COMPILE([],
+ [
+ char xchgd;
+ long new[2], xchg[2], *p;
+#if !defined(__PIC__) || !__PIC__
+# error nope
+#endif
+ __asm__ __volatile__(
+ "pushl %%ebx\n\t"
+ "movl (%7), %%ebx\n\t"
+ "movl 4(%7), %%ecx\n\t"
+ "lock; cmpxchg8b %0\n\t"
+ "setz %3\n\t"
+ "popl %%ebx\n\t"
+ : "=m"(*p), "=d"(xchg[1]), "=a"(xchg[0]), "=c"(xchgd)
+ : "m"(*p), "1"(xchg[1]), "2"(xchg[0]), "3"(new)
+ : "cc", "memory");
+
+ ],
+ [gcc_dw_cmpxchg_asm=yes])
+ if test "$gcc_dw_cmpxchg_asm" = "yes"; then
+ AC_DEFINE(ETHR_CMPXCHG8B_REGISTER_SHORTAGE, 1, [Define if you get a register shortage with cmpxchg8b and position independent code])
+ fi
+ fi
+ AC_MSG_RESULT([$gcc_dw_cmpxchg_asm])
+ if test "$gcc_dw_cmpxchg_asm" = "yes"; then
+ AC_DEFINE(ETHR_GCC_HAVE_DW_CMPXCHG_ASM_SUPPORT, 1, [Define if you use a gcc that supports the double word cmpxchg instruction])
+ fi;;
+ *)
+ ;;
+esac
+
+AC_DEFINE(ETHR_HAVE_ETHREAD_DEFINES, 1, \
+[Define if you have all ethread defines])
+
+AC_SUBST(ETHR_X_LIBS)
+AC_SUBST(ETHR_LIBS)
+AC_SUBST(ETHR_LIB_NAME)
+AC_SUBST(ETHR_DEFS)
+AC_SUBST(ETHR_THR_LIB_BASE)
+AC_SUBST(ETHR_THR_LIB_BASE_DIR)
+AC_SUBST(ETHR_X86_SSE2_ASM)
+
+])
+
+
+
+dnl ----------------------------------------------------------------------
+dnl
+dnl ERL_TIME_CORRECTION
+dnl
+dnl In the presence of a high resolution realtime timer Erlang can adapt
+dnl its view of time relative to this timer. On solaris such a timer is
+dnl available with the syscall gethrtime(). On other OS's a fallback
+dnl solution using times() is implemented. (However on e.g. FreeBSD times()
+dnl is implemented using gettimeofday so it doesn't make much sense to
+dnl use it there...) On second thought, it seems to be safer to do it the
+dnl other way around. I.e. only use times() on OS's where we know it will
+dnl work...
+dnl
+
+AC_DEFUN(ERL_TIME_CORRECTION,
+[if test x$ac_cv_func_gethrtime = x; then
+ AC_CHECK_FUNC(gethrtime)
+fi
+if test x$clock_gettime_correction = xunknown; then
+ AC_TRY_COMPILE([#include <time.h>],
+ [struct timespec ts;
+ long long result;
+ clock_gettime(CLOCK_MONOTONIC,&ts);
+ result = ((long long) ts.tv_sec) * 1000000000LL +
+ ((long long) ts.tv_nsec);],
+ clock_gettime_compiles=yes,
+ clock_gettime_compiles=no)
+else
+ clock_gettime_compiles=no
+fi
+
+
+AC_CACHE_CHECK([how to correct for time adjustments], erl_cv_time_correction,
+[
+case $clock_gettime_correction in
+ yes)
+ erl_cv_time_correction=clock_gettime;;
+ no|unknown)
+ case $ac_cv_func_gethrtime in
+ yes)
+ erl_cv_time_correction=hrtime ;;
+ no)
+ case $host_os in
+ linux*)
+ case $clock_gettime_correction in
+ unknown)
+ if test x$clock_gettime_compiles = xyes; then
+ if test X$cross_compiling != Xyes; then
+ linux_kernel_vsn_=`uname -r`
+ case $linux_kernel_vsn_ in
+ [[0-1]].*|2.[[0-5]]|2.[[0-5]].*)
+ erl_cv_time_correction=times ;;
+ *)
+ erl_cv_time_correction=clock_gettime;;
+ esac
+ else
+ case X$erl_xcomp_linux_clock_gettime_correction in
+ X)
+ erl_cv_time_correction=cross;;
+ Xyes|Xno)
+ if test $erl_xcomp_linux_clock_gettime_correction = yes; then
+ erl_cv_time_correction=clock_gettime
+ else
+ erl_cv_time_correction=times
+ fi;;
+ *)
+ AC_MSG_ERROR([Bad erl_xcomp_linux_clock_gettime_correction value: $erl_xcomp_linux_clock_gettime_correction]);;
+ esac
+ fi
+ else
+ erl_cv_time_correction=times
+ fi
+ ;;
+ *)
+ erl_cv_time_correction=times ;;
+ esac
+ ;;
+ *)
+ erl_cv_time_correction=none ;;
+ esac
+ ;;
+ esac
+ ;;
+esac
+])
+
+xrtlib=""
+case $erl_cv_time_correction in
+ times)
+ AC_DEFINE(CORRECT_USING_TIMES,[],
+ [Define if you do not have a high-res. timer & want to use times() instead])
+ ;;
+ clock_gettime|cross)
+ if test $erl_cv_time_correction = cross; then
+ erl_cv_time_correction=clock_gettime
+ AC_MSG_WARN([result clock_gettime guessed because of cross compilation])
+ fi
+ xrtlib="-lrt"
+ AC_DEFINE(GETHRTIME_WITH_CLOCK_GETTIME,[1],
+ [Define if you want to use clock_gettime to simulate gethrtime])
+ ;;
+esac
+dnl
+dnl Check if gethrvtime is working, and if to use procfs ioctl
+dnl or (yet to be written) write to the procfs ctl file.
+dnl
+
+AC_MSG_CHECKING([if gethrvtime works and how to use it])
+AC_TRY_RUN([
+/* gethrvtime procfs ioctl test */
+/* These need to be undef:ed to not break activation of
+ * micro level process accounting on /proc/self
+ */
+#ifdef _LARGEFILE_SOURCE
+# undef _LARGEFILE_SOURCE
+#endif
+#ifdef _FILE_OFFSET_BITS
+# undef _FILE_OFFSET_BITS
+#endif
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/signal.h>
+#include <sys/fault.h>
+#include <sys/syscall.h>
+#include <sys/procfs.h>
+#include <fcntl.h>
+
+int main() {
+ long msacct = PR_MSACCT;
+ int fd;
+ long long start, stop;
+ int i;
+ pid_t pid = getpid();
+ char proc_self[30] = "/proc/";
+
+ sprintf(proc_self+strlen(proc_self), "%lu", (unsigned long) pid);
+ if ( (fd = open(proc_self, O_WRONLY)) == -1)
+ exit(1);
+ if (ioctl(fd, PIOCSET, &msacct) < 0)
+ exit(2);
+ if (close(fd) < 0)
+ exit(3);
+ start = gethrvtime();
+ for (i = 0; i < 100; i++)
+ stop = gethrvtime();
+ if (start == 0)
+ exit(4);
+ if (start == stop)
+ exit(5);
+ exit(0); return 0;
+}
+],
+erl_gethrvtime=procfs_ioctl,
+erl_gethrvtime=false,
+[
+case X$erl_xcomp_gethrvtime_procfs_ioctl in
+ X)
+ erl_gethrvtime=cross;;
+ Xyes|Xno)
+ if test $erl_xcomp_gethrvtime_procfs_ioctl = yes; then
+ erl_gethrvtime=procfs_ioctl
+ else
+ erl_gethrvtime=false
+ fi;;
+ *)
+ AC_MSG_ERROR([Bad erl_xcomp_gethrvtime_procfs_ioctl value: $erl_xcomp_gethrvtime_procfs_ioctl]);;
+esac
+])
+
+case $erl_gethrvtime in
+ procfs_ioctl)
+ AC_DEFINE(HAVE_GETHRVTIME_PROCFS_IOCTL,[1],
+ [define if gethrvtime() works and uses ioctl() to /proc/self])
+ AC_MSG_RESULT(uses ioctl to procfs)
+ ;;
+ *)
+ if test $erl_gethrvtime = cross; then
+ erl_gethrvtime=false
+ AC_MSG_RESULT(cross)
+ AC_MSG_WARN([result 'not working' guessed because of cross compilation])
+ else
+ AC_MSG_RESULT(not working)
+ fi
+
+ dnl
+ dnl Check if clock_gettime (linux) is working
+ dnl
+
+ AC_MSG_CHECKING([if clock_gettime can be used to get process CPU time])
+ save_libs=$LIBS
+ LIBS="-lrt"
+ AC_TRY_RUN([
+ #include <stdlib.h>
+ #include <unistd.h>
+ #include <string.h>
+ #include <stdio.h>
+ #include <time.h>
+ int main() {
+ long long start, stop;
+ int i;
+ struct timespec tp;
+
+ if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &tp) < 0)
+ exit(1);
+ start = ((long long)tp.tv_sec * 1000000000LL) + (long long)tp.tv_nsec;
+ for (i = 0; i < 100; i++)
+ clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &tp);
+ stop = ((long long)tp.tv_sec * 1000000000LL) + (long long)tp.tv_nsec;
+ if (start == 0)
+ exit(4);
+ if (start == stop)
+ exit(5);
+ exit(0); return 0;
+ }
+ ],
+ erl_clock_gettime=yes,
+ erl_clock_gettime=no,
+ [
+ case X$erl_xcomp_clock_gettime_cpu_time in
+ X) erl_clock_gettime=cross;;
+ Xyes|Xno) erl_clock_gettime=$erl_xcomp_clock_gettime_cpu_time;;
+ *) AC_MSG_ERROR([Bad erl_xcomp_clock_gettime_cpu_time value: $erl_xcomp_clock_gettime_cpu_time]);;
+ esac
+ ])
+ LIBS=$save_libs
+ case $host_os in
+ linux*)
+ AC_MSG_RESULT([no; not stable])
+ LIBRT=$xrtlib
+ ;;
+ *)
+ AC_MSG_RESULT($erl_clock_gettime)
+ case $erl_clock_gettime in
+ yes)
+ AC_DEFINE(HAVE_CLOCK_GETTIME,[],
+ [define if clock_gettime() works for getting process time])
+ LIBRT=-lrt
+ ;;
+ cross)
+ erl_clock_gettime=no
+ AC_MSG_WARN([result no guessed because of cross compilation])
+ LIBRT=$xrtlib
+ ;;
+ *)
+ LIBRT=$xrtlib
+ ;;
+ esac
+ ;;
+ esac
+ AC_SUBST(LIBRT)
+ ;;
+esac
+])dnl
+
+dnl ----------------------------------------------------------------------
+dnl
+dnl LM_TRY_ENABLE_CFLAG
+dnl
+dnl
+dnl Tries a CFLAG and sees if it can be enabled without compiler errors
+dnl $1: textual cflag to add
+dnl $2: variable to store the modified CFLAG in
+dnl Usage example LM_TRY_ENABLE_CFLAG([-Werror=return-type], [CFLAGS])
+dnl
+dnl
+AC_DEFUN([LM_TRY_ENABLE_CFLAG], [
+ AC_MSG_CHECKING([if we can add $1 to $2 (via CFLAGS)])
+ saved_CFLAGS=$CFLAGS;
+ CFLAGS="$1 $$2";
+ AC_TRY_COMPILE([],[return 0;],can_enable_flag=true,can_enable_flag=false)
+ CFLAGS=$saved_CFLAGS;
+ if test "X$can_enable_flag" = "Xtrue"; then
+ AC_MSG_RESULT([yes])
+ AS_VAR_SET($2, "$1 $$2")
+ else
+ AC_MSG_RESULT([no])
+ fi
+])
+
+dnl ERL_TRY_LINK_JAVA(CLASSES, FUNCTION-BODY
+dnl [ACTION_IF_FOUND [, ACTION-IF-NOT-FOUND]])
+dnl Freely inspired by AC_TRY_LINK. (Maybe better to create a
+dnl AC_LANG_JAVA instead...)
+AC_DEFUN(ERL_TRY_LINK_JAVA,
+[java_link='$JAVAC conftest.java 1>&AC_FD_CC'
+changequote(, )dnl
+cat > conftest.java <<EOF
+$1
+class conftest { public static void main(String[] args) {
+ $2
+ ; return; }}
+EOF
+changequote([, ])dnl
+if AC_TRY_EVAL(java_link) && test -s conftest.class; then
+ ifelse([$3], , :, [rm -rf conftest*
+ $3])
+else
+ echo "configure: failed program was:" 1>&AC_FD_CC
+ cat conftest.java 1>&AC_FD_CC
+ echo "configure: PATH was $PATH" 1>&AC_FD_CC
+ifelse([$4], , , [ rm -rf conftest*
+ $4
+])dnl
+fi
+rm -f conftest*])
+#define UNSAFE_MASK 0xc0000000 /* Mask for bits that must be constant */
+
+
diff --git a/lib/megaco/configure.in b/lib/megaco/configure.in
index 42c50b8961..643267a154 100644
--- a/lib/megaco/configure.in
+++ b/lib/megaco/configure.in
@@ -145,7 +145,7 @@ AC_ARG_ENABLE(megaco_flex_scanner_lineno,
AC_SUBST(ENABLE_MEGACO_FLEX_SCANNER_LINENO)
-dnl This is the os flavour, should be unix, vxworks or win32
+dnl This is the os flavour, should be unix or win32
if test "X$host" = "Xwin32"; then
ERLANG_OSTYPE=win32
else
@@ -162,6 +162,11 @@ else
fi
AC_SUBST(OTP_EXTRA_FLAGS)
+if test "x$GCC" = xyes; then
+ # Treat certain GCC warnings as errors
+ LM_TRY_ENABLE_CFLAG([-Werror=return-type], [CFLAGS])
+fi
+
dnl
dnl If ${ERL_TOP}/make/otp_ded.mk.in exists and contains DED_MK_VSN > 0,
dnl every thing releted to compiling Dynamic Erlang Drivers can be found
diff --git a/lib/megaco/doc/src/Makefile b/lib/megaco/doc/src/Makefile
index 493304523e..f35413a4fd 100644
--- a/lib/megaco/doc/src/Makefile
+++ b/lib/megaco/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2000-2011. All Rights Reserved.
+# Copyright Ericsson AB 2000-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/megaco/doc/src/definitions/term.defs b/lib/megaco/doc/src/definitions/term.defs
index f3d6f865d2..57379eaa5d 100644
--- a/lib/megaco/doc/src/definitions/term.defs
+++ b/lib/megaco/doc/src/definitions/term.defs
@@ -110,7 +110,6 @@ the module Erlang in the application kernel","kenneth"},
{"Master Agent","Master Agent","The SNMP agent system consists of one Master Agent which terminates the SNMP protocol","mbj"}, {"MIB","Management Information Base (MIB)","An abstract definition of the management information available through a management interface in a system.","mbj"},
{"matching","matching","See pattern matching.","kenneth"},
{"message queue","message queue","The queue of not yet received messages that are in the mailbox of a process.","olin"},
-{"Mnemosyne","Mnemosyne","Mnemosyne was the query language of Mnesia up to the R11B release. Supersed by QLC.","hakan"},
{"Mnesia","Mnesia","Mnesia is a distributed Database Management System, appropriate for telecommunications applications and other applications with need of continuous operation and soft real-time properties.","hakan"},
{"MIBshort","MIB","See Management Information Base.","mbj"},
{"MIME","MIME","Multi-purpose Internet Mail Extensions.","jocke"},
diff --git a/lib/megaco/doc/src/definitions/term.defs.xml b/lib/megaco/doc/src/definitions/term.defs.xml
index 28ac0d6eaf..1c80ee8d80 100644
--- a/lib/megaco/doc/src/definitions/term.defs.xml
+++ b/lib/megaco/doc/src/definitions/term.defs.xml
@@ -794,13 +794,6 @@ The queue of not yet received messages that are in the mailbox of a process.
<resp>olin</resp>
</term>
<term>
- <id>Mnemosyne</id>
- <shortdef>Mnemosyne</shortdef>
- <def>
-Mnemosyne was the query language of Mnesia up to the R11B release. Supersed by QLC.</def>
- <resp>hakan</resp>
- </term>
- <term>
<id>Mnesia</id>
<shortdef>Mnesia</shortdef>
<def>
diff --git a/lib/megaco/doc/src/megaco_encode.xml b/lib/megaco/doc/src/megaco_encode.xml
index 410e4f3b31..4a9d63c2e3 100644
--- a/lib/megaco/doc/src/megaco_encode.xml
+++ b/lib/megaco/doc/src/megaco_encode.xml
@@ -224,22 +224,11 @@
messages.</p>
</item>
<item>
- <p>megaco_ber_bin_encoder - encode/decode ASN.1 BER
- messages. This encoder uses ASN.1 ber_bin which
- has been optimized using the bit syntax.</p>
- </item>
- <item>
<p>megaco_per_encoder - encode/decode ASN.1 PER
messages. N.B. that this format is not included in the
Megaco standard.</p>
</item>
<item>
- <p>megaco_per_bin_encoder - encode/decode ASN.1 PER
- messages. N.B. that this format is not included in the
- Megaco standard. This encoder uses ASN.1 per_bin which
- has been optimized using the bit syntax.</p>
- </item>
- <item>
<p>megaco_erl_dist_encoder - encodes messages into Erlangs
distribution format. It is rather verbose but encoding and
decoding is blinding fast. N.B. that this format is not
@@ -370,18 +359,6 @@
needs to be specified.</p>
<list type="bulleted">
<item>
- <p><c><![CDATA[[driver|_]]]></c> - make use of the asn1 driver for decode
- (ber_bin) and encode (per_bin). This option is only available for
- encoding modules: <c><![CDATA[megaco_binary_encoder]]></c>,
- <c><![CDATA[megaco_ber_bin_encoder]]></c> and <c><![CDATA[megaco_per_bin_encoder]]></c>.</p>
- <p>If this option is present in the encoding config, it <em>must</em>
- to be the <em>first</em>, unless the
- <seealso marker="#handling_versions">version3</seealso> encoding
- config is present, in which case it must come second, after
- the version3 encoding config,
- e.g. <c><![CDATA[[{version3,prev3b},driver]]]></c>.</p>
- </item>
- <item>
<p><c><![CDATA[[native]]]></c> - skips the transformation phase, i.e.
the decoded message(s) will not be transformed into our internal
form.</p>
diff --git a/lib/megaco/doc/src/notes.xml b/lib/megaco/doc/src/notes.xml
index 393064fbb5..928e3bea82 100644
--- a/lib/megaco/doc/src/notes.xml
+++ b/lib/megaco/doc/src/notes.xml
@@ -36,6 +36,78 @@
section is the version number of Megaco.</p>
+ <section><title>Megaco 3.16.0.2</title>
+
+ <p>Version 3.16.0.2 supports code replacement in runtime from/to
+ version 3.16.0.1, 3.16, 3.15.1.1, 3.15.1 and 3.15.</p>
+
+ <section>
+ <title>Improvements and new features</title>
+
+<!--
+ <p>-</p>
+-->
+
+ <list type="bulleted">
+ <item>
+ <p>Allow whitespaces in installation path. </p>
+ <p>It is now possible to give configure and make an
+ installation/release path with whitespaces in it. </p>
+ <p>Own Id: OTP-10107</p>
+ </item>
+
+ <item>
+ <p>Fix parallel make for behaviours. </p>
+ </item>
+
+ <item>
+ <p>Removed use of deprecated system flag,
+ <c>global_haeps_size</c>, in the measurement tool
+ <c>mstone1</c>. </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>
+ </item>
+
+ </list>
+ -->
+
+ </section>
+
+ <section>
+ <title>Incompatibilities</title>
+ <p>-</p>
+
+<!--
+ <list type="bulleted">
+ <item>
+ <p>Due to the change in the flex driver API,
+ we may no longer be able to build and/or use
+ the flex driver without reentrant support. </p>
+ <p>Own Id: OTP-9795</p>
+ </item>
+
+ </list>
+-->
+
+ </section>
+
+ </section> <!-- 3.16.0.2 -->
+
+
<section><title>Megaco 3.16.0.1</title>
<p>Version 3.16.0.1 supports code replacement in runtime from/to
diff --git a/lib/megaco/examples/meas/Makefile.in b/lib/megaco/examples/meas/Makefile.in
index 607d81d440..91e342cd9a 100644
--- a/lib/megaco/examples/meas/Makefile.in
+++ b/lib/megaco/examples/meas/Makefile.in
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2002-2011. All Rights Reserved.
+# Copyright Ericsson AB 2002-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -147,12 +147,12 @@ release_docs_spec:
# ----------------------------------------------------
meas.sh.skel: meas.sh.skel.src
- @echo "transforming $< to $@"
- $(PERL) -p -e 's?%VSN%?$(VSN)? ' < $< > $@
+ $(V_colon)@echo "transforming $< to $@"
+ $(vsn_verbose)$(PERL) -p -e 's?%VSN%?$(VSN)? ' < $< > $@
mstone1.sh.skel: mstone1.sh.skel.src
- @echo "transforming $< to $@"
- $(PERL) -p -e 's?%VSN%?$(VSN)? ' < $< > $@
+ $(V_colon)@echo "transforming $< to $@"
+ $(vsn_verbose)$(PERL) -p -e 's?%VSN%?$(VSN)? ' < $< > $@
megaco_codec_transform.$(EMULATOR): megaco_codec_transform.erl
diff --git a/lib/megaco/examples/meas/megaco_codec_meas.erl b/lib/megaco/examples/meas/megaco_codec_meas.erl
index 51ee396338..65b986ccd1 100644
--- a/lib/megaco/examples/meas/megaco_codec_meas.erl
+++ b/lib/megaco/examples/meas/megaco_codec_meas.erl
@@ -176,7 +176,7 @@ display_megaco_info() ->
io:format("Megaco version: ~s (~s)~n", [Ver, FlexStr]).
display_asn1_info() ->
- AI = megaco_ber_bin_drv_media_gateway_control_v1:info(),
+ AI = megaco_ber_media_gateway_control_v1:info(),
Vsn =
case lists:keysearch(vsn, 1, AI) of
{value, {vsn, V}} when is_atom(V) ->
@@ -281,15 +281,11 @@ expand_codec(Codec) ->
[{Codec, megaco_compact_text_encoder, [flex_scanner], 3000},
{Codec, megaco_compact_text_encoder, [], 1500}];
ber ->
- [{Codec, megaco_ber_bin_encoder, [driver,native], 4000},
- {Codec, megaco_ber_bin_encoder, [native], 3000},
- {Codec, megaco_ber_bin_encoder, [driver], 3000},
- {Codec, megaco_ber_bin_encoder, [], 1000}];
+ [{Codec, megaco_ber_encoder, [native], 3000},
+ {Codec, megaco_ber_encoder, [], 1000}];
per ->
- [{Codec, megaco_per_bin_encoder, [driver,native], 4000},
- {Codec, megaco_per_bin_encoder, [native], 3000},
- {Codec, megaco_per_bin_encoder, [driver], 3000},
- {Codec, megaco_per_bin_encoder, [], 1000}];
+ [{Codec, megaco_per_encoder, [native], 3000},
+ {Codec, megaco_per_encoder, [], 1000}];
erlang ->
[
{Codec, megaco_erl_dist_encoder, [megaco_compressed,compressed], 500},
diff --git a/lib/megaco/examples/meas/megaco_codec_mstone_lib.erl b/lib/megaco/examples/meas/megaco_codec_mstone_lib.erl
index 9af88d9f50..b527ff2e89 100644
--- a/lib/megaco/examples/meas/megaco_codec_mstone_lib.erl
+++ b/lib/megaco/examples/meas/megaco_codec_mstone_lib.erl
@@ -268,7 +268,7 @@ display_megaco_info() ->
io:format("Megaco version: ~s~n", [Ver]).
display_asn1_info() ->
- AI = megaco_ber_bin_drv_media_gateway_control_v1:info(),
+ AI = megaco_ber__media_gateway_control_v1:info(),
Vsn =
case lists:keysearch(vsn, 1, AI) of
{value, {vsn, V}} when is_atom(V) ->
@@ -361,15 +361,11 @@ expand_codec(Codec, only_drv) ->
[{Codec, megaco_compact_text_encoder, [flex_scanner]},
{Codec, megaco_compact_text_encoder, [flex_scanner]}];
ber ->
- [{Codec, megaco_ber_bin_encoder, [driver,native]},
- {Codec, megaco_ber_bin_encoder, [driver]},
- {Codec, megaco_ber_bin_encoder, [driver,native]},
- {Codec, megaco_ber_bin_encoder, [driver]}];
+ [{Codec, megaco_ber_encoder, [native]},
+ {Codec, megaco_ber_encoder, []}];
per ->
- [{Codec, megaco_per_bin_encoder, [driver,native]},
- {Codec, megaco_per_bin_encoder, [native]},
- {Codec, megaco_per_bin_encoder, [driver,native]},
- {Codec, megaco_per_bin_encoder, [native]}];
+ [{Codec, megaco_per_encoder, [native]},
+ {Codec, megaco_per_encoder, []}];
erlang ->
Encoder = megaco_erl_dist_encoder,
[
@@ -390,15 +386,11 @@ expand_codec(Codec, no_drv) ->
[{Codec, megaco_compact_text_encoder, []},
{Codec, megaco_compact_text_encoder, []}];
ber ->
- [{Codec, megaco_ber_bin_encoder, [native]},
- {Codec, megaco_ber_bin_encoder, []},
- {Codec, megaco_ber_bin_encoder, [native]},
- {Codec, megaco_ber_bin_encoder, []}];
+ [{Codec, megaco_ber_encoder, [native]},
+ {Codec, megaco_ber_encoder, []}];
per ->
- [{Codec, megaco_per_bin_encoder, [native]},
- {Codec, megaco_per_bin_encoder, []},
- {Codec, megaco_per_bin_encoder, [native]},
- {Codec, megaco_per_bin_encoder, []}];
+ [{Codec, megaco_per_encoder, [native]},
+ {Codec, megaco_per_encoder, []}];
erlang ->
Encoder = megaco_erl_dist_encoder,
[
@@ -419,15 +411,11 @@ expand_codec(Codec, _) ->
[{Codec, megaco_compact_text_encoder, [flex_scanner]},
{Codec, megaco_compact_text_encoder, []}];
ber ->
- [{Codec, megaco_ber_bin_encoder, [driver,native]},
- {Codec, megaco_ber_bin_encoder, [native]},
- {Codec, megaco_ber_bin_encoder, [driver]},
- {Codec, megaco_ber_bin_encoder, []}];
+ [{Codec, megaco_ber_encoder, [native]},
+ {Codec, megaco_ber_encoder, []}];
per ->
- [{Codec, megaco_per_bin_encoder, [driver,native]},
- {Codec, megaco_per_bin_encoder, [native]},
- {Codec, megaco_per_bin_encoder, [driver]},
- {Codec, megaco_per_bin_encoder, []}];
+ [{Codec, megaco_per_encoder, [native]},
+ {Codec, megaco_per_encoder, []}];
erlang ->
Encoder = megaco_erl_dist_encoder,
[
diff --git a/lib/megaco/examples/meas/megaco_codec_transform.erl b/lib/megaco/examples/meas/megaco_codec_transform.erl
index cfe832ff26..15db165566 100644
--- a/lib/megaco/examples/meas/megaco_codec_transform.erl
+++ b/lib/megaco/examples/meas/megaco_codec_transform.erl
@@ -213,11 +213,11 @@ decode_message(compact, BinMsg) ->
Conf = [{version3,?V3}],
do_decode(Mod, Conf, BinMsg);
decode_message(ber, BinMsg) ->
- Mod = megaco_ber_bin_encoder,
+ Mod = megaco_ber_encoder,
Conf = [{version3,?V3}],
do_decode(Mod, Conf, BinMsg);
decode_message(per, BinMsg) ->
- Mod = megaco_per_bin_encoder,
+ Mod = megaco_per_encoder,
Conf = [{version3,?V3}],
do_decode(Mod, Conf, BinMsg);
decode_message(erlang, BinMsg) ->
diff --git a/lib/megaco/examples/simple/Makefile b/lib/megaco/examples/simple/Makefile
index 261acbb479..8ec9db71db 100644
--- a/lib/megaco/examples/simple/Makefile
+++ b/lib/megaco/examples/simple/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2001-2009. All Rights Reserved.
+# Copyright Ericsson AB 2001-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/megaco/src/app/Makefile b/lib/megaco/src/app/Makefile
index a7b458845d..d18da5326a 100644
--- a/lib/megaco/src/app/Makefile
+++ b/lib/megaco/src/app/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2007-2009. All Rights Reserved.
+# Copyright Ericsson AB 2007-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -94,10 +94,10 @@ info:
# ----------------------------------------------------
$(APP_TARGET): $(APP_SRC) ../../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
$(APPUP_TARGET): $(APPUP_SRC) ../../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
diff --git a/lib/megaco/src/app/megaco.app.src b/lib/megaco/src/app/megaco.app.src
index c0d8218ac8..0ba2a866f9 100644
--- a/lib/megaco/src/app/megaco.app.src
+++ b/lib/megaco/src/app/megaco.app.src
@@ -23,19 +23,6 @@
{modules,
[
megaco,
- megaco_ber_bin_encoder,
- megaco_ber_bin_drv_media_gateway_control_v1,
- megaco_ber_bin_drv_media_gateway_control_v2,
- megaco_ber_bin_drv_media_gateway_control_prev3a,
- megaco_ber_bin_drv_media_gateway_control_prev3b,
- megaco_ber_bin_drv_media_gateway_control_prev3c,
- megaco_ber_bin_drv_media_gateway_control_v3,
- megaco_ber_bin_media_gateway_control_v1,
- megaco_ber_bin_media_gateway_control_v2,
- megaco_ber_bin_media_gateway_control_prev3a,
- megaco_ber_bin_media_gateway_control_prev3b,
- megaco_ber_bin_media_gateway_control_prev3c,
- megaco_ber_bin_media_gateway_control_v3,
megaco_ber_encoder,
megaco_ber_media_gateway_control_v1,
megaco_ber_media_gateway_control_v2,
@@ -87,19 +74,6 @@
megaco_per_media_gateway_control_prev3b,
megaco_per_media_gateway_control_prev3c,
megaco_per_media_gateway_control_v3,
- megaco_per_bin_encoder,
- megaco_per_bin_drv_media_gateway_control_v1,
- megaco_per_bin_drv_media_gateway_control_v2,
- megaco_per_bin_drv_media_gateway_control_prev3a,
- megaco_per_bin_drv_media_gateway_control_prev3b,
- megaco_per_bin_drv_media_gateway_control_prev3c,
- megaco_per_bin_drv_media_gateway_control_v3,
- megaco_per_bin_media_gateway_control_v1,
- megaco_per_bin_media_gateway_control_v2,
- megaco_per_bin_media_gateway_control_prev3a,
- megaco_per_bin_media_gateway_control_prev3b,
- megaco_per_bin_media_gateway_control_prev3c,
- megaco_per_bin_media_gateway_control_v3,
megaco_pretty_text_encoder,
megaco_pretty_text_encoder_v1,
megaco_pretty_text_encoder_v2,
diff --git a/lib/megaco/src/app/megaco.appup.src b/lib/megaco/src/app/megaco.appup.src
index 7f89fa8bc2..a7b38eb107 100644
--- a/lib/megaco/src/app/megaco.appup.src
+++ b/lib/megaco/src/app/megaco.appup.src
@@ -145,10 +145,17 @@
%% |
%% v
%% 3.16.0.1
+%% |
+%% v
+%% 3.16.0.2
%%
%%
{"%VSN%",
[
+ {"3.16.0.1",
+ [
+ ]
+ },
{"3.16",
[
]
@@ -170,6 +177,10 @@
}
],
[
+ {"3.16.0.1",
+ [
+ ]
+ },
{"3.16",
[
]
diff --git a/lib/megaco/src/binary/Makefile b/lib/megaco/src/binary/Makefile
index 77b06c81d3..c1fd66b848 100644
--- a/lib/megaco/src/binary/Makefile
+++ b/lib/megaco/src/binary/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2000-2009. All Rights Reserved.
+# Copyright Ericsson AB 2000-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -50,46 +50,22 @@ ASN1_SPECS = $(ASN1_V1_SPEC) \
ASN1_FILES = $(ASN1_SPECS:%=%.asn)
V1_SPECS = $(BER_ASN1_V1_SPEC) \
- $(BER_BIN_ASN1_V1_SPEC) \
- $(BER_BIN_DRV_ASN1_V1_SPEC) \
- $(PER_ASN1_V1_SPEC) \
- $(PER_BIN_ASN1_V1_SPEC) \
- $(PER_BIN_DRV_ASN1_V1_SPEC)
+ $(PER_ASN1_V1_SPEC)
V2_SPECS = $(BER_ASN1_V2_SPEC) \
- $(BER_BIN_ASN1_V2_SPEC) \
- $(BER_BIN_DRV_ASN1_V2_SPEC) \
- $(PER_ASN1_V2_SPEC) \
- $(PER_BIN_ASN1_V2_SPEC) \
- $(PER_BIN_DRV_ASN1_V2_SPEC)
+ $(PER_ASN1_V2_SPEC)
PREV3A_SPECS = $(BER_ASN1_PREV3A_SPEC) \
- $(BER_BIN_ASN1_PREV3A_SPEC) \
- $(BER_BIN_DRV_ASN1_PREV3A_SPEC) \
- $(PER_ASN1_PREV3A_SPEC) \
- $(PER_BIN_ASN1_PREV3A_SPEC) \
- $(PER_BIN_DRV_ASN1_PREV3A_SPEC)
+ $(PER_ASN1_PREV3A_SPEC)
PREV3B_SPECS = $(BER_ASN1_PREV3B_SPEC) \
- $(BER_BIN_ASN1_PREV3B_SPEC) \
- $(BER_BIN_DRV_ASN1_PREV3B_SPEC) \
- $(PER_ASN1_PREV3B_SPEC) \
- $(PER_BIN_ASN1_PREV3B_SPEC) \
- $(PER_BIN_DRV_ASN1_PREV3B_SPEC)
+ $(PER_ASN1_PREV3B_SPEC)
PREV3C_SPECS = $(BER_ASN1_PREV3C_SPEC) \
- $(BER_BIN_ASN1_PREV3C_SPEC) \
- $(BER_BIN_DRV_ASN1_PREV3C_SPEC) \
- $(PER_ASN1_PREV3C_SPEC) \
- $(PER_BIN_ASN1_PREV3C_SPEC) \
- $(PER_BIN_DRV_ASN1_PREV3C_SPEC)
+ $(PER_ASN1_PREV3C_SPEC)
V3_SPECS = $(BER_ASN1_V3_SPEC) \
- $(BER_BIN_ASN1_V3_SPEC) \
- $(BER_BIN_DRV_ASN1_V3_SPEC) \
$(PER_ASN1_V3_SPEC) \
- $(PER_BIN_ASN1_V3_SPEC) \
- $(PER_BIN_DRV_ASN1_V3_SPEC) \
$(PREV3A_SPECS) $(PREV3B_SPECS) $(PREV3C_SPECS)
SPECS = $(V1_SPECS) $(V2_SPECS) $(V3_SPECS)
@@ -153,12 +129,13 @@ opt: prebuild $(TARGET_FILES)
prebuild: prebuild.skip
prebuild.skip:
- @echo "Building prebuild.skip\c"
+ $(gen_verbose)
+ $(V_colon)@echo "Building prebuild.skip\c"
@touch prebuild.skip
@for a in $(SPEC_ASN1DB); do \
echo $$a >> prebuild.skip; \
done
- @echo ""
+ $(V_colon)@echo ""
v1: $(V2_SPEC_BINS)
diff --git a/lib/megaco/src/binary/depend.mk b/lib/megaco/src/binary/depend.mk
index c9ca34bcf6..2dce45981f 100644
--- a/lib/megaco/src/binary/depend.mk
+++ b/lib/megaco/src/binary/depend.mk
@@ -19,17 +19,8 @@
# Flag description:
#
-# +optimize
-# For ber_bin this means "optimize" (whatever that is),
-# but for per_bin it means that a stage in the encode
-# is done in the asn1 driver.
-#
-# +nif
-# For ber_bin this means that part of the decode is done
-# in the asn1 nif.
-#
# +asn1config
-# This is only used by the ber_bin, and means that
+# This is only used by the ber, and means that
# some partial decode functions will be created
# (as described by the asn1config file).
#
@@ -43,42 +34,18 @@ ifeq ($(MEGACO_INLINE_ASN1_RT),true)
ASN1_CT_OPTS += +inline
endif
-BER_V1_FLAGS = $(ASN1_CT_OPTS)
-BER_BIN_V1_FLAGS = $(ASN1_CT_OPTS) +asn1config +optimize
-BER_BIN_DRV_V1_FLAGS = $(ASN1_CT_OPTS) +asn1config +optimize +nif
-BER_V2_FLAGS = $(ASN1_CT_OPTS)
-BER_BIN_V2_FLAGS = $(ASN1_CT_OPTS) +asn1config +optimize
-BER_BIN_DRV_V2_FLAGS = $(ASN1_CT_OPTS) +asn1config +optimize +nif
-BER_PREV3A_FLAGS = $(ASN1_CT_OPTS)
-BER_BIN_PREV3A_FLAGS = $(ASN1_CT_OPTS) +asn1config +optimize
-BER_BIN_DRV_PREV3A_FLAGS = $(ASN1_CT_OPTS) +asn1config +optimize +nif
-BER_PREV3B_FLAGS = $(ASN1_CT_OPTS)
-BER_BIN_PREV3B_FLAGS = $(ASN1_CT_OPTS) +asn1config +optimize
-BER_BIN_DRV_PREV3B_FLAGS = $(ASN1_CT_OPTS) +asn1config +optimize +nif
-BER_PREV3C_FLAGS = $(ASN1_CT_OPTS)
-BER_BIN_PREV3C_FLAGS = $(ASN1_CT_OPTS) +asn1config +optimize
-BER_BIN_DRV_PREV3C_FLAGS = $(ASN1_CT_OPTS) +asn1config +optimize +nif
-BER_V3_FLAGS = $(ASN1_CT_OPTS)
-BER_BIN_V3_FLAGS = $(ASN1_CT_OPTS) +asn1config +optimize
-BER_BIN_DRV_V3_FLAGS = $(ASN1_CT_OPTS) +asn1config +optimize +nif
+BER_V1_FLAGS = $(ASN1_CT_OPTS) +asn1config
+BER_V2_FLAGS = $(ASN1_CT_OPTS) +asn1config
+BER_PREV3A_FLAGS = $(ASN1_CT_OPTS) +asn1config
+BER_PREV3B_FLAGS = $(ASN1_CT_OPTS) +asn1config
+BER_PREV3C_FLAGS = $(ASN1_CT_OPTS) +asn1config
+BER_V3_FLAGS = $(ASN1_CT_OPTS) +asn1config
PER_V1_FLAGS = $(ASN1_CT_OPTS)
-PER_BIN_V1_FLAGS = $(ASN1_CT_OPTS)
-PER_BIN_DRV_V1_FLAGS = $(ASN1_CT_OPTS) +optimize
PER_V2_FLAGS = $(ASN1_CT_OPTS)
-PER_BIN_V2_FLAGS = $(ASN1_CT_OPTS)
-PER_BIN_DRV_V2_FLAGS = $(ASN1_CT_OPTS) +optimize
PER_PREV3A_FLAGS = $(ASN1_CT_OPTS)
-PER_BIN_PREV3A_FLAGS = $(ASN1_CT_OPTS)
-PER_BIN_DRV_PREV3A_FLAGS = $(ASN1_CT_OPTS) +optimize
PER_PREV3B_FLAGS = $(ASN1_CT_OPTS)
-PER_BIN_PREV3B_FLAGS = $(ASN1_CT_OPTS)
-PER_BIN_DRV_PREV3B_FLAGS = $(ASN1_CT_OPTS) +optimize
PER_PREV3C_FLAGS = $(ASN1_CT_OPTS)
-PER_BIN_PREV3C_FLAGS = $(ASN1_CT_OPTS)
-PER_BIN_DRV_PREV3C_FLAGS = $(ASN1_CT_OPTS) +optimize
PER_V3_FLAGS = $(ASN1_CT_OPTS)
-PER_BIN_V3_FLAGS = $(ASN1_CT_OPTS)
-PER_BIN_DRV_V3_FLAGS = $(ASN1_CT_OPTS) +optimize
# --- Version 1 ---
@@ -86,119 +53,42 @@ PER_BIN_DRV_V3_FLAGS = $(ASN1_CT_OPTS) +optimize
$(BER_ASN1_V1_SPEC).erl: \
$(BER_ASN1_V1_SPEC).set.asn \
$(ASN1_V1_SPEC).asn
- @echo "$(BER_ASN1_V1_SPEC):"
- $(ERLC) -bber $(BER_V1_FLAGS) $(BER_ASN1_V1_SPEC).set.asn
+ $(V_colon)@echo "$(BER_ASN1_V1_SPEC):"
+ $(asn_verbose)$(ERLC) -bber $(BER_V1_FLAGS) $(BER_ASN1_V1_SPEC).set.asn
$(EBIN)/$(BER_ASN1_V1_SPEC).$(EMULATOR): \
$(BER_ASN1_V1_SPEC).erl
-$(BER_BIN_ASN1_V1_SPEC).erl: \
- $(BER_BIN_ASN1_V1_SPEC).set.asn \
- $(BER_BIN_ASN1_V1_SPEC).asn1config \
- $(ASN1_V1_SPEC).asn
- @echo "$(BER_BIN_ASN1_V1_SPEC):"
- $(ERLC) -bber_bin $(BER_BIN_V1_FLAGS) $(BER_BIN_ASN1_V1_SPEC).set.asn
-
-$(EBIN)/$(BER_BIN_ASN1_V1_SPEC).$(EMULATOR): \
- $(BER_BIN_ASN1_V1_SPEC).erl
-
-$(BER_BIN_DRV_ASN1_V1_SPEC).erl: \
- $(BER_BIN_DRV_ASN1_V1_SPEC).set.asn \
- $(BER_BIN_DRV_ASN1_V1_SPEC).asn1config \
- $(ASN1_V1_SPEC).asn
- @echo "$(BER_BIN_DRV_ASN1_V1_SPEC):"
- $(ERLC) -bber_bin $(BER_BIN_DRV_V1_FLAGS) $(BER_BIN_DRV_ASN1_V1_SPEC).set.asn
-
-$(EBIN)/$(BER_BIN_DRV_ASN1_V1_SPEC).$(EMULATOR): \
- $(BER_BIN_DRV_ASN1_V1_SPEC).erl
-
$(PER_ASN1_V1_SPEC).erl: \
$(PER_ASN1_V1_SPEC).set.asn \
$(ASN1_V1_SPEC).asn
- @echo "$(PER_ASN1_V1_SPEC):"
- $(ERLC) -bper $(PER_V1_FLAGS) $(PER_ASN1_V1_SPEC).set.asn
+ $(V_colon)@echo "$(PER_ASN1_V1_SPEC):"
+ $(asn_verbose)$(ERLC) -bper $(PER_V1_FLAGS) $(PER_ASN1_V1_SPEC).set.asn
$(EBIN)/$(PER_ASN1_V1_SPEC).$(EMULATOR): \
$(PER_ASN1_V1_SPEC).erl
-$(PER_BIN_ASN1_V1_SPEC).erl: \
- $(PER_BIN_ASN1_V1_SPEC).set.asn \
- $(ASN1_V1_SPEC).asn
- @echo "$(PER_BIN_ASN1_V1_SPEC):"
- $(ERLC) -bper_bin $(PER_BIN_V1_FLAGS) $(PER_BIN_ASN1_V1_SPEC).set.asn
-
-$(EBIN)/$(PER_BIN_ASN1_V1_SPEC).$(EMULATOR): \
- $(PER_BIN_ASN1_V1_SPEC).erl
-
-$(PER_BIN_DRV_ASN1_V1_SPEC).erl: \
- $(PER_BIN_DRV_ASN1_V1_SPEC).set.asn \
- $(ASN1_V1_SPEC).asn
- @echo "$(PER_BIN_DRV_ASN1_V1_SPEC):"
- $(ERLC) -bper_bin $(PER_BIN_DRV_V1_FLAGS) $(PER_BIN_DRV_ASN1_V1_SPEC).set.asn
-
-$(EBIN)/$(PER_BIN_DRV_ASN1_V1_SPEC).$(EMULATOR): \
- $(PER_BIN_DRV_ASN1_V1_SPEC).erl
-
# --- Version 2 ---
$(BER_ASN1_V2_SPEC).erl: \
$(BER_ASN1_V2_SPEC).set.asn \
$(ASN1_V2_SPEC).asn
- @echo "$(BER_ASN1_V2_SPEC):"
- $(ERLC) -bber $(BER_V2_FLAGS) $(BER_ASN1_V2_SPEC).set.asn
+ $(V_colon)@echo "$(BER_ASN1_V2_SPEC):"
+ $(asn_verbose)$(ERLC) -bber $(BER_V2_FLAGS) $(BER_ASN1_V2_SPEC).set.asn
$(EBIN)/$(BER_ASN1_V2_SPEC).$(EMULATOR): \
$(BER_ASN1_V2_SPEC).erl
-$(BER_BIN_ASN1_V2_SPEC).erl: \
- $(BER_BIN_ASN1_V2_SPEC).set.asn \
- $(BER_BIN_ASN1_V2_SPEC).asn1config \
- $(ASN1_V2_SPEC).asn
- @echo "$(BER_BIN_ASN1_V2_SPEC):"
- $(ERLC) -bber_bin $(BER_BIN_V2_FLAGS) $(BER_BIN_ASN1_V2_SPEC).set.asn
-
-$(EBIN)/$(BER_BIN_ASN1_V2_SPEC).$(EMULATOR): \
- $(BER_BIN_ASN1_V2_SPEC).erl
-
-$(BER_BIN_DRV_ASN1_V2_SPEC).erl: \
- $(BER_BIN_DRV_ASN1_V2_SPEC).set.asn \
- $(BER_BIN_DRV_ASN1_V2_SPEC).asn1config \
- $(ASN1_V2_SPEC).asn
- @echo "$(BER_BIN_DRV_ASN1_V2_SPEC):"
- $(ERLC) -bber_bin $(BER_BIN_DRV_V2_FLAGS) $(BER_BIN_DRV_ASN1_V2_SPEC).set.asn
-
-$(EBIN)/$(BER_BIN_DRV_ASN1_V2_SPEC).$(EMULATOR): \
- $(BER_BIN_DRV_ASN1_V2_SPEC).erl
-
$(PER_ASN1_V2_SPEC).erl: \
$(PER_ASN1_V2_SPEC).set.asn \
$(ASN1_V2_SPEC).asn
- @echo "$(PER_ASN1_V2_SPEC):"
- $(ERLC) -bper $(PER_V2_FLAGS) $(PER_ASN1_V2_SPEC).set.asn
+ $(V_colon)@echo "$(PER_ASN1_V2_SPEC):"
+ $(asn_verbose)$(ERLC) -bper $(PER_V2_FLAGS) $(PER_ASN1_V2_SPEC).set.asn
$(EBIN)/$(PER_ASN1_V2_SPEC).$(EMULATOR): \
$(PER_ASN1_V2_SPEC).erl
-$(PER_BIN_ASN1_V2_SPEC).erl: \
- $(PER_BIN_ASN1_V2_SPEC).set.asn \
- $(ASN1_V2_SPEC).asn
- @echo "$(PER_BIN_ASN1_V2_SPEC):"
- $(ERLC) -bper_bin $(PER_BIN_V2_FLAGS) $(PER_BIN_ASN1_V2_SPEC).set.asn
-
-$(EBIN)/$(PER_BIN_ASN1_V2_SPEC).$(EMULATOR): \
- $(PER_BIN_ASN1_V2_SPEC).erl
-
-$(PER_BIN_DRV_ASN1_V2_SPEC).erl: \
- $(PER_BIN_DRV_ASN1_V2_SPEC).set.asn \
- $(ASN1_V2_SPEC).asn
- @echo "$(PER_BIN_DRV_ASN1_V2_SPEC):"
- $(ERLC) -bper_bin $(PER_BIN_DRV_V2_FLAGS) $(PER_BIN_DRV_ASN1_V2_SPEC).set.asn
-
-$(EBIN)/$(PER_BIN_DRV_ASN1_V2_SPEC).$(EMULATOR): \
- $(PER_BIN_DRV_ASN1_V2_SPEC).erl
-
-
# --- Version 3 ---
# -- (prev3a) --
@@ -206,250 +96,93 @@ $(EBIN)/$(PER_BIN_DRV_ASN1_V2_SPEC).$(EMULATOR): \
$(BER_ASN1_PREV3A_SPEC).erl: \
$(BER_ASN1_PREV3A_SPEC).set.asn \
$(ASN1_PREV3A_SPEC).asn
- @echo "$(BER_ASN1_PREV3A_SPEC):"
- $(ERLC) -bber $(BER_PREV3A_FLAGS) $(BER_ASN1_PREV3A_SPEC).set.asn
+ $(V_colon)@echo "$(BER_ASN1_PREV3A_SPEC):"
+ $(asn_verbose)$(ERLC) -bber $(BER_PREV3A_FLAGS) $(BER_ASN1_PREV3A_SPEC).set.asn
$(EBIN)/$(BER_ASN1_PREV3A_SPEC).$(EMULATOR): \
$(BER_ASN1_PREV3A_SPEC).erl
-$(BER_BIN_ASN1_PREV3A_SPEC).erl: \
- $(BER_BIN_ASN1_PREV3A_SPEC).set.asn \
- $(BER_BIN_ASN1_PREV3A_SPEC).asn1config \
- $(ASN1_PREV3A_SPEC).asn
- @echo "$(BER_BIN_ASN1_PREV3A_SPEC):"
- $(ERLC) -bber_bin $(BER_BIN_PREV3A_FLAGS) $(BER_BIN_ASN1_PREV3A_SPEC).set.asn
-
-$(EBIN)/$(BER_BIN_ASN1_PREV3A_SPEC).$(EMULATOR): \
- $(BER_BIN_ASN1_PREV3A_SPEC).erl
-
-$(BER_BIN_DRV_ASN1_PREV3A_SPEC).erl: \
- $(BER_BIN_DRV_ASN1_PREV3A_SPEC).set.asn \
- $(BER_BIN_DRV_ASN1_PREV3A_SPEC).asn1config \
- $(ASN1_PREV3A_SPEC).asn
- @echo "$(BER_BIN_DRV_ASN1_PREV3A_SPEC):"
- $(ERLC) -bber_bin $(BER_BIN_DRV_PREV3A_FLAGS) $(BER_BIN_DRV_ASN1_PREV3A_SPEC).set.asn
-
-$(EBIN)/$(BER_BIN_DRV_ASN1_PREV3A_SPEC).$(EMULATOR): \
- $(BER_BIN_DRV_ASN1_PREV3A_SPEC).erl
-
$(PER_ASN1_PREV3A_SPEC).erl: \
$(PER_ASN1_PREV3A_SPEC).set.asn \
$(ASN1_PREV3A_SPEC).asn
- @echo "$(PER_ASN1_PREV3A_SPEC):"
- $(ERLC) -bper $(PER_PREV3A_FLAGS) $(PER_ASN1_PREV3A_SPEC).set.asn
+ $(V_colon)@echo "$(PER_ASN1_PREV3A_SPEC):"
+ $(asn_verbose)$(ERLC) -bper $(PER_PREV3A_FLAGS) $(PER_ASN1_PREV3A_SPEC).set.asn
$(EBIN)/$(PER_ASN1_PREV3A_SPEC).$(EMULATOR): \
$(PER_ASN1_PREV3A_SPEC).erl
-$(PER_BIN_ASN1_PREV3A_SPEC).erl: \
- $(PER_BIN_ASN1_PREV3A_SPEC).set.asn \
- $(ASN1_PREV3A_SPEC).asn
- @echo "$(PER_BIN_ASN1_PREV3A_SPEC):"
- $(ERLC) -bper_bin $(PER_BIN_PREV3A_FLAGS) $(PER_BIN_ASN1_PREV3A_SPEC).set.asn
-
-$(EBIN)/$(PER_BIN_ASN1_PREV3A_SPEC).$(EMULATOR): \
- $(PER_BIN_ASN1_PREV3A_SPEC).erl
-
-$(PER_BIN_DRV_ASN1_PREV3A_SPEC).erl: \
- $(PER_BIN_DRV_ASN1_PREV3A_SPEC).set.asn \
- $(ASN1_PREV3A_SPEC).asn
- @echo "$(PER_BIN_DRV_ASN1_PREV3A_SPEC):"
- $(ERLC) -bper_bin $(PER_BIN_DRV_PREV3A_FLAGS) $(PER_BIN_DRV_ASN1_PREV3A_SPEC).set.asn
-
-$(EBIN)/$(PER_BIN_DRV_ASN1_PREV3A_SPEC).$(EMULATOR): \
- $(PER_BIN_DRV_ASN1_PREV3A_SPEC).erl
# -- (prev3b) --
$(BER_ASN1_PREV3B_SPEC).erl: \
$(BER_ASN1_PREV3B_SPEC).set.asn \
$(ASN1_PREV3B_SPEC).asn
- @echo "$(BER_ASN1_PREV3B_SPEC):"
- $(ERLC) -bber $(BER_PREV3B_FLAGS) $(BER_ASN1_PREV3B_SPEC).set.asn
+ $(V_colon)@echo "$(BER_ASN1_PREV3B_SPEC):"
+ $(asn_verbose)$(ERLC) -bber $(BER_PREV3B_FLAGS) $(BER_ASN1_PREV3B_SPEC).set.asn
$(EBIN)/$(BER_ASN1_PREV3B_SPEC).$(EMULATOR): \
$(BER_ASN1_PREV3B_SPEC).erl
-$(BER_BIN_ASN1_PREV3B_SPEC).erl: \
- $(BER_BIN_ASN1_PREV3B_SPEC).set.asn \
- $(BER_BIN_ASN1_PREV3B_SPEC).asn1config \
- $(ASN1_PREV3B_SPEC).asn
- @echo "$(BER_BIN_ASN1_PREV3B_SPEC):"
- $(ERLC) -bber_bin $(BER_BIN_PREV3B_FLAGS) $(BER_BIN_ASN1_PREV3B_SPEC).set.asn
-
-$(EBIN)/$(BER_BIN_ASN1_PREV3B_SPEC).$(EMULATOR): \
- $(BER_BIN_ASN1_PREV3B_SPEC).erl
-
-$(BER_BIN_DRV_ASN1_PREV3B_SPEC).erl: \
- $(BER_BIN_DRV_ASN1_PREV3B_SPEC).set.asn \
- $(BER_BIN_DRV_ASN1_PREV3B_SPEC).asn1config \
- $(ASN1_PREV3B_SPEC).asn
- @echo "$(BER_BIN_DRV_ASN1_PREV3B_SPEC):"
- $(ERLC) -bber_bin $(BER_BIN_DRV_PREV3B_FLAGS) $(BER_BIN_DRV_ASN1_PREV3B_SPEC).set.asn
-
-$(EBIN)/$(BER_BIN_DRV_ASN1_PREV3B_SPEC).$(EMULATOR): \
- $(BER_BIN_DRV_ASN1_PREV3B_SPEC).erl
-
$(PER_ASN1_PREV3B_SPEC).erl: \
$(PER_ASN1_PREV3B_SPEC).set.asn \
$(ASN1_PREV3B_SPEC).asn
- @echo "$(PER_ASN1_PREV3B_SPEC):"
- $(ERLC) -bper $(PER_PREV3B_FLAGS) $(PER_ASN1_PREV3B_SPEC).set.asn
+ $(V_colon)@echo "$(PER_ASN1_PREV3B_SPEC):"
+ $(asn_verbose)$(ERLC) -bper $(PER_PREV3B_FLAGS) $(PER_ASN1_PREV3B_SPEC).set.asn
$(EBIN)/$(PER_ASN1_PREV3B_SPEC).$(EMULATOR): \
$(PER_ASN1_PREV3B_SPEC).erl
-$(PER_BIN_ASN1_PREV3B_SPEC).erl: \
- $(PER_BIN_ASN1_PREV3B_SPEC).set.asn \
- $(ASN1_PREV3B_SPEC).asn
- @echo "$(PER_BIN_ASN1_PREV3B_SPEC):"
- $(ERLC) -bper_bin $(PER_BIN_PREV3B_FLAGS) $(PER_BIN_ASN1_PREV3B_SPEC).set.asn
-
-$(EBIN)/$(PER_BIN_ASN1_PREV3B_SPEC).$(EMULATOR): \
- $(PER_BIN_ASN1_PREV3B_SPEC).erl
-
-$(PER_BIN_DRV_ASN1_PREV3B_SPEC).erl: \
- $(PER_BIN_DRV_ASN1_PREV3B_SPEC).set.asn \
- $(ASN1_PREV3B_SPEC).asn
- @echo "$(PER_BIN_DRV_ASN1_PREV3B_SPEC):"
- $(ERLC) -bper_bin $(PER_BIN_DRV_PREV3B_FLAGS) $(PER_BIN_DRV_ASN1_PREV3B_SPEC).set.asn
-
-$(EBIN)/$(PER_BIN_DRV_ASN1_PREV3B_SPEC).$(EMULATOR): \
- $(PER_BIN_DRV_ASN1_PREV3B_SPEC).erl
-
# -- (prev3c) --
$(BER_ASN1_PREV3C_SPEC).erl: \
$(BER_ASN1_PREV3C_SPEC).set.asn \
$(ASN1_PREV3C_SPEC).asn
- @echo "$(BER_ASN1_PREV3C_SPEC):"
- $(ERLC) -bber $(BER_PREV3C_FLAGS) $(BER_ASN1_PREV3C_SPEC).set.asn
+ $(V_colon)@echo "$(BER_ASN1_PREV3C_SPEC):"
+ $(asn_verbose)$(ERLC) -bber $(BER_PREV3C_FLAGS) $(BER_ASN1_PREV3C_SPEC).set.asn
$(EBIN)/$(BER_ASN1_PREV3C_SPEC).$(EMULATOR): \
$(BER_ASN1_PREV3C_SPEC).erl
-$(BER_BIN_ASN1_PREV3C_SPEC).erl: \
- $(BER_BIN_ASN1_PREV3C_SPEC).set.asn \
- $(BER_BIN_ASN1_PREV3C_SPEC).asn1config \
- $(ASN1_PREV3C_SPEC).asn
- @echo "$(BER_BIN_ASN1_PREV3C_SPEC):"
- $(ERLC) -bber_bin $(BER_BIN_PREV3C_FLAGS) $(BER_BIN_ASN1_PREV3C_SPEC).set.asn
-
-$(EBIN)/$(BER_BIN_ASN1_PREV3C_SPEC).$(EMULATOR): \
- $(BER_BIN_ASN1_PREV3C_SPEC).erl
-
-$(BER_BIN_DRV_ASN1_PREV3C_SPEC).erl: \
- $(BER_BIN_DRV_ASN1_PREV3C_SPEC).set.asn \
- $(BER_BIN_DRV_ASN1_PREV3C_SPEC).asn1config \
- $(ASN1_PREV3C_SPEC).asn
- @echo "$(BER_BIN_DRV_ASN1_PREV3C_SPEC):"
- $(ERLC) -bber_bin $(BER_BIN_DRV_PREV3C_FLAGS) $(BER_BIN_DRV_ASN1_PREV3C_SPEC).set.asn
-
-$(EBIN)/$(BER_BIN_DRV_ASN1_PREV3C_SPEC).$(EMULATOR): \
- $(BER_BIN_DRV_ASN1_PREV3C_SPEC).erl
-
$(PER_ASN1_PREV3C_SPEC).erl: \
$(PER_ASN1_PREV3C_SPEC).set.asn \
$(ASN1_PREV3C_SPEC).asn
- @echo "$(PER_ASN1_PREV3C_SPEC):"
- $(ERLC) -bper $(PER_PREV3C_FLAGS) $(PER_ASN1_PREV3C_SPEC).set.asn
+ $(V_colon)@echo "$(PER_ASN1_PREV3C_SPEC):"
+ $(asn_verbose)$(ERLC) -bper $(PER_PREV3C_FLAGS) $(PER_ASN1_PREV3C_SPEC).set.asn
$(EBIN)/$(PER_ASN1_PREV3C_SPEC).$(EMULATOR): \
$(PER_ASN1_PREV3C_SPEC).erl
-$(PER_BIN_ASN1_PREV3C_SPEC).erl: \
- $(PER_BIN_ASN1_PREV3C_SPEC).set.asn \
- $(ASN1_PREV3C_SPEC).asn
- @echo "$(PER_BIN_ASN1_PREV3C_SPEC):"
- $(ERLC) -bper_bin $(PER_BIN_PREV3C_FLAGS) $(PER_BIN_ASN1_PREV3C_SPEC).set.asn
-
-$(EBIN)/$(PER_BIN_ASN1_PREV3C_SPEC).$(EMULATOR): \
- $(PER_BIN_ASN1_PREV3C_SPEC).erl
-
-$(PER_BIN_DRV_ASN1_PREV3C_SPEC).erl: \
- $(PER_BIN_DRV_ASN1_PREV3C_SPEC).set.asn \
- $(ASN1_PREV3C_SPEC).asn
- @echo "$(PER_BIN_DRV_ASN1_PREV3C_SPEC):"
- $(ERLC) -bper_bin $(PER_BIN_DRV_PREV3C_FLAGS) $(PER_BIN_DRV_ASN1_PREV3C_SPEC).set.asn
-
-$(EBIN)/$(PER_BIN_DRV_ASN1_PREV3C_SPEC).$(EMULATOR): \
- $(PER_BIN_DRV_ASN1_PREV3C_SPEC).erl
-
# -- (v3) --
$(BER_ASN1_V3_SPEC).erl: \
$(BER_ASN1_V3_SPEC).set.asn \
$(ASN1_V3_SPEC).asn
- @echo "$(BER_ASN1_V3_SPEC):"
- $(ERLC) -bber $(BER_V3_FLAGS) $(BER_ASN1_V3_SPEC).set.asn
+ $(V_colon)@echo "$(BER_ASN1_V3_SPEC):"
+ $(asn_verbose)$(ERLC) -bber $(BER_V3_FLAGS) $(BER_ASN1_V3_SPEC).set.asn
$(EBIN)/$(BER_ASN1_V3_SPEC).$(EMULATOR): \
$(BER_ASN1_V3_SPEC).erl
-$(BER_BIN_ASN1_V3_SPEC).erl: \
- $(BER_BIN_ASN1_V3_SPEC).set.asn \
- $(BER_BIN_ASN1_V3_SPEC).asn1config \
- $(ASN1_V3_SPEC).asn
- @echo "$(BER_BIN_ASN1_V3_SPEC):"
- $(ERLC) -bber_bin $(BER_BIN_V3_FLAGS) $(BER_BIN_ASN1_V3_SPEC).set.asn
-
-$(EBIN)/$(BER_BIN_ASN1_V3_SPEC).$(EMULATOR): \
- $(BER_BIN_ASN1_V3_SPEC).erl
-
-$(BER_BIN_DRV_ASN1_V3_SPEC).erl: \
- $(BER_BIN_DRV_ASN1_V3_SPEC).set.asn \
- $(BER_BIN_DRV_ASN1_V3_SPEC).asn1config \
- $(ASN1_V3_SPEC).asn
- @echo "$(BER_BIN_DRV_ASN1_V3_SPEC):"
- $(ERLC) -bber_bin $(BER_BIN_DRV_V3_FLAGS) $(BER_BIN_DRV_ASN1_V3_SPEC).set.asn
-
-$(EBIN)/$(BER_BIN_DRV_ASN1_V3_SPEC).$(EMULATOR): \
- $(BER_BIN_DRV_ASN1_V3_SPEC).erl
-
$(PER_ASN1_V3_SPEC).erl: \
$(PER_ASN1_V3_SPEC).set.asn \
$(ASN1_V3_SPEC).asn
- @echo "$(PER_ASN1_V3_SPEC):"
- $(ERLC) -bper $(PER_V3_FLAGS) $(PER_ASN1_V3_SPEC).set.asn
+ $(V_colon)@echo "$(PER_ASN1_V3_SPEC):"
+ $(asn_verbose)$(ERLC) -bper $(PER_V3_FLAGS) $(PER_ASN1_V3_SPEC).set.asn
$(EBIN)/$(PER_ASN1_V3_SPEC).$(EMULATOR): \
$(PER_ASN1_V3_SPEC).erl
-$(PER_BIN_ASN1_V3_SPEC).erl: \
- $(PER_BIN_ASN1_V3_SPEC).set.asn \
- $(ASN1_V3_SPEC).asn
- @echo "$(PER_BIN_ASN1_V3_SPEC):"
- $(ERLC) -bper_bin $(PER_BIN_V3_FLAGS) $(PER_BIN_ASN1_V3_SPEC).set.asn
-
-$(EBIN)/$(PER_BIN_ASN1_V3_SPEC).$(EMULATOR): \
- $(PER_BIN_ASN1_V3_SPEC).erl
-
-$(PER_BIN_DRV_ASN1_V3_SPEC).erl: \
- $(PER_BIN_DRV_ASN1_V3_SPEC).set.asn \
- $(ASN1_V3_SPEC).asn
- @echo "$(PER_BIN_DRV_ASN1_V3_SPEC):"
- $(ERLC) -bper_bin $(PER_BIN_DRV_V3_FLAGS) $(PER_BIN_DRV_ASN1_V3_SPEC).set.asn
-
-$(EBIN)/$(PER_BIN_DRV_ASN1_V3_SPEC).$(EMULATOR): \
- $(PER_BIN_DRV_ASN1_V3_SPEC).erl
-
# -------------
$(EBIN)/megaco_ber_encoder.$(EMULATOR): megaco_ber_encoder.erl \
$(MEGACO_ENGINEDIR)/megaco_message_internal.hrl
-$(EBIN)/megaco_ber_bin_encoder.$(EMULATOR): megaco_ber_bin_encoder.erl \
- $(MEGACO_ENGINEDIR)/megaco_message_internal.hrl
-
$(EBIN)/megaco_per_encoder.$(EMULATOR): megaco_per_encoder.erl \
$(MEGACO_ENGINEDIR)/megaco_message_internal.hrl
-$(EBIN)/megaco_per_bin_encoder.$(EMULATOR): megaco_per_bin_encoder.erl \
- $(MEGACO_ENGINEDIR)/megaco_message_internal.hrl
-
$(EBIN)/megaco_binary_encoder_lib.$(EMULATOR): megaco_binary_encoder_lib.erl \
$(MEGACO_ENGINEDIR)/megaco_message_internal.hrl
diff --git a/lib/megaco/src/binary/megaco_ber_bin_drv_media_gateway_control_prev3a.asn1config b/lib/megaco/src/binary/megaco_ber_bin_drv_media_gateway_control_prev3a.asn1config
deleted file mode 100644
index 179473717d..0000000000
--- a/lib/megaco/src/binary/megaco_ber_bin_drv_media_gateway_control_prev3a.asn1config
+++ /dev/null
@@ -1,43 +0,0 @@
-{exclusive_decode,
- {'megaco_ber_bin_drv_media_gateway_control_prev3a',
- [
- {decode_message_trans_partial,
- [
- 'MegacoMessage',[{mess,[{messageBody,[{transactions,parts}]}]}]
- ]
- },
- {decode_message_acts_partial,
- ['Transaction',
- [
- {transactionRequest,
- [
- {actions,parts}
- ]
- },
- {transactionReply,
- [
- {transactionResult, [{actionReplies,parts}]}
- ]
- }
- ]
- ]
- },
- {decode_message_version,
- ['MegacoMessage',
- [
- {authHeader,undecoded},
- {mess,[{mId,undecoded},{messageBody,undecoded}]}
- ]
- ]
- },
- {decode_message_mId,
- ['MegacoMessage',
- [
- {authHeader,undecoded},
- {mess,[{messageBody,undecoded}]}
- ]
- ]
- }
- ]
- }
-}.
diff --git a/lib/megaco/src/binary/megaco_ber_bin_drv_media_gateway_control_prev3a.set.asn b/lib/megaco/src/binary/megaco_ber_bin_drv_media_gateway_control_prev3a.set.asn
deleted file mode 100644
index b9ba7ffdb4..0000000000
--- a/lib/megaco/src/binary/megaco_ber_bin_drv_media_gateway_control_prev3a.set.asn
+++ /dev/null
@@ -1 +0,0 @@
-MEDIA-GATEWAY-CONTROL-prev3a.asn
diff --git a/lib/megaco/src/binary/megaco_ber_bin_drv_media_gateway_control_prev3b.asn1config b/lib/megaco/src/binary/megaco_ber_bin_drv_media_gateway_control_prev3b.asn1config
deleted file mode 100644
index ceda97fbd1..0000000000
--- a/lib/megaco/src/binary/megaco_ber_bin_drv_media_gateway_control_prev3b.asn1config
+++ /dev/null
@@ -1,43 +0,0 @@
-{exclusive_decode,
- {'megaco_ber_bin_drv_media_gateway_control_prev3b',
- [
- {decode_message_trans_partial,
- [
- 'MegacoMessage',[{mess,[{messageBody,[{transactions,parts}]}]}]
- ]
- },
- {decode_message_acts_partial,
- ['Transaction',
- [
- {transactionRequest,
- [
- {actions,parts}
- ]
- },
- {transactionReply,
- [
- {transactionResult, [{actionReplies,parts}]}
- ]
- }
- ]
- ]
- },
- {decode_message_version,
- ['MegacoMessage',
- [
- {authHeader,undecoded},
- {mess,[{mId,undecoded},{messageBody,undecoded}]}
- ]
- ]
- },
- {decode_message_mId,
- ['MegacoMessage',
- [
- {authHeader,undecoded},
- {mess,[{messageBody,undecoded}]}
- ]
- ]
- }
- ]
- }
-}.
diff --git a/lib/megaco/src/binary/megaco_ber_bin_drv_media_gateway_control_prev3b.set.asn b/lib/megaco/src/binary/megaco_ber_bin_drv_media_gateway_control_prev3b.set.asn
deleted file mode 100644
index 0437bde310..0000000000
--- a/lib/megaco/src/binary/megaco_ber_bin_drv_media_gateway_control_prev3b.set.asn
+++ /dev/null
@@ -1 +0,0 @@
-MEDIA-GATEWAY-CONTROL-prev3b.asn
diff --git a/lib/megaco/src/binary/megaco_ber_bin_drv_media_gateway_control_prev3c.asn1config b/lib/megaco/src/binary/megaco_ber_bin_drv_media_gateway_control_prev3c.asn1config
deleted file mode 100644
index d181ef44bd..0000000000
--- a/lib/megaco/src/binary/megaco_ber_bin_drv_media_gateway_control_prev3c.asn1config
+++ /dev/null
@@ -1,43 +0,0 @@
-{exclusive_decode,
- {'megaco_ber_bin_drv_media_gateway_control_prev3c',
- [
- {decode_message_trans_partial,
- [
- 'MegacoMessage',[{mess,[{messageBody,[{transactions,parts}]}]}]
- ]
- },
- {decode_message_acts_partial,
- ['Transaction',
- [
- {transactionRequest,
- [
- {actions,parts}
- ]
- },
- {transactionReply,
- [
- {transactionResult, [{actionReplies,parts}]}
- ]
- }
- ]
- ]
- },
- {decode_message_version,
- ['MegacoMessage',
- [
- {authHeader,undecoded},
- {mess,[{mId,undecoded},{messageBody,undecoded}]}
- ]
- ]
- },
- {decode_message_mId,
- ['MegacoMessage',
- [
- {authHeader,undecoded},
- {mess,[{messageBody,undecoded}]}
- ]
- ]
- }
- ]
- }
-}.
diff --git a/lib/megaco/src/binary/megaco_ber_bin_drv_media_gateway_control_prev3c.set.asn b/lib/megaco/src/binary/megaco_ber_bin_drv_media_gateway_control_prev3c.set.asn
deleted file mode 100644
index e78055fbad..0000000000
--- a/lib/megaco/src/binary/megaco_ber_bin_drv_media_gateway_control_prev3c.set.asn
+++ /dev/null
@@ -1 +0,0 @@
-MEDIA-GATEWAY-CONTROL-prev3c.asn
diff --git a/lib/megaco/src/binary/megaco_ber_bin_drv_media_gateway_control_v1.set.asn b/lib/megaco/src/binary/megaco_ber_bin_drv_media_gateway_control_v1.set.asn
deleted file mode 100644
index 0f5a92dba1..0000000000
--- a/lib/megaco/src/binary/megaco_ber_bin_drv_media_gateway_control_v1.set.asn
+++ /dev/null
@@ -1 +0,0 @@
-MEDIA-GATEWAY-CONTROL-v1.asn
diff --git a/lib/megaco/src/binary/megaco_ber_bin_drv_media_gateway_control_v2.asn1config b/lib/megaco/src/binary/megaco_ber_bin_drv_media_gateway_control_v2.asn1config
deleted file mode 100644
index 3d0cb9a019..0000000000
--- a/lib/megaco/src/binary/megaco_ber_bin_drv_media_gateway_control_v2.asn1config
+++ /dev/null
@@ -1,43 +0,0 @@
-{exclusive_decode,
- {'megaco_ber_bin_drv_media_gateway_control_v2',
- [
- {decode_message_trans_partial,
- [
- 'MegacoMessage',[{mess,[{messageBody,[{transactions,parts}]}]}]
- ]
- },
- {decode_message_acts_partial,
- ['Transaction',
- [
- {transactionRequest,
- [
- {actions,parts}
- ]
- },
- {transactionReply,
- [
- {transactionResult, [{actionReplies,parts}]}
- ]
- }
- ]
- ]
- },
- {decode_message_version,
- ['MegacoMessage',
- [
- {authHeader,undecoded},
- {mess,[{mId,undecoded},{messageBody,undecoded}]}
- ]
- ]
- },
- {decode_message_mId,
- ['MegacoMessage',
- [
- {authHeader,undecoded},
- {mess,[{messageBody,undecoded}]}
- ]
- ]
- }
- ]
- }
-}.
diff --git a/lib/megaco/src/binary/megaco_ber_bin_drv_media_gateway_control_v2.set.asn b/lib/megaco/src/binary/megaco_ber_bin_drv_media_gateway_control_v2.set.asn
deleted file mode 100644
index 7fc82b127f..0000000000
--- a/lib/megaco/src/binary/megaco_ber_bin_drv_media_gateway_control_v2.set.asn
+++ /dev/null
@@ -1 +0,0 @@
-MEDIA-GATEWAY-CONTROL-v2.asn
diff --git a/lib/megaco/src/binary/megaco_ber_bin_drv_media_gateway_control_v3.asn1config b/lib/megaco/src/binary/megaco_ber_bin_drv_media_gateway_control_v3.asn1config
deleted file mode 100644
index cc662c0145..0000000000
--- a/lib/megaco/src/binary/megaco_ber_bin_drv_media_gateway_control_v3.asn1config
+++ /dev/null
@@ -1,43 +0,0 @@
-{exclusive_decode,
- {'megaco_ber_bin_drv_media_gateway_control_v3',
- [
- {decode_message_trans_partial,
- [
- 'MegacoMessage',[{mess,[{messageBody,[{transactions,parts}]}]}]
- ]
- },
- {decode_message_acts_partial,
- ['Transaction',
- [
- {transactionRequest,
- [
- {actions,parts}
- ]
- },
- {transactionReply,
- [
- {transactionResult, [{actionReplies,parts}]}
- ]
- }
- ]
- ]
- },
- {decode_message_version,
- ['MegacoMessage',
- [
- {authHeader,undecoded},
- {mess,[{mId,undecoded},{messageBody,undecoded}]}
- ]
- ]
- },
- {decode_message_mId,
- ['MegacoMessage',
- [
- {authHeader,undecoded},
- {mess,[{messageBody,undecoded}]}
- ]
- ]
- }
- ]
- }
-}.
diff --git a/lib/megaco/src/binary/megaco_ber_bin_drv_media_gateway_control_v3.set.asn b/lib/megaco/src/binary/megaco_ber_bin_drv_media_gateway_control_v3.set.asn
deleted file mode 100644
index 1d7950a283..0000000000
--- a/lib/megaco/src/binary/megaco_ber_bin_drv_media_gateway_control_v3.set.asn
+++ /dev/null
@@ -1 +0,0 @@
-MEDIA-GATEWAY-CONTROL-v3.asn
diff --git a/lib/megaco/src/binary/megaco_ber_bin_encoder.erl b/lib/megaco/src/binary/megaco_ber_bin_encoder.erl
deleted file mode 100644
index bf9926c7e5..0000000000
--- a/lib/megaco/src/binary/megaco_ber_bin_encoder.erl
+++ /dev/null
@@ -1,716 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2000-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 : Handle ASN.1 BER encoding of Megaco/H.248
-%%----------------------------------------------------------------------
-
--module(megaco_ber_bin_encoder).
-
--behaviour(megaco_encoder).
-
--export([encode_message/3, decode_message/3,
- decode_mini_message/3,
-
- encode_transaction/3,
- encode_action_requests/3,
- encode_action_request/3,
- encode_action_reply/3,
-
- version_of/2]).
-
-%% Backward compatible functions:
--export([encode_message/2, decode_message/2]).
-
--include_lib("megaco/src/engine/megaco_message_internal.hrl").
-
--define(V1_ASN1_MOD, megaco_ber_bin_media_gateway_control_v1).
--define(V2_ASN1_MOD, megaco_ber_bin_media_gateway_control_v2).
--define(V3_ASN1_MOD, megaco_ber_bin_media_gateway_control_v3).
--define(PREV3A_ASN1_MOD, megaco_ber_bin_media_gateway_control_prev3a).
--define(PREV3B_ASN1_MOD, megaco_ber_bin_media_gateway_control_prev3b).
--define(PREV3C_ASN1_MOD, megaco_ber_bin_media_gateway_control_prev3c).
--define(V1_ASN1_MOD_DRV, megaco_ber_bin_drv_media_gateway_control_v1).
--define(V2_ASN1_MOD_DRV, megaco_ber_bin_drv_media_gateway_control_v2).
--define(V3_ASN1_MOD_DRV, megaco_ber_bin_drv_media_gateway_control_v3).
--define(PREV3A_ASN1_MOD_DRV, megaco_ber_bin_drv_media_gateway_control_prev3a).
--define(PREV3B_ASN1_MOD_DRV, megaco_ber_bin_drv_media_gateway_control_prev3b).
--define(PREV3C_ASN1_MOD_DRV, megaco_ber_bin_drv_media_gateway_control_prev3c).
-
--define(V1_TRANS_MOD, megaco_binary_transformer_v1).
--define(V2_TRANS_MOD, megaco_binary_transformer_v2).
--define(V3_TRANS_MOD, megaco_binary_transformer_v3).
--define(PREV3A_TRANS_MOD, megaco_binary_transformer_prev3a).
--define(PREV3B_TRANS_MOD, megaco_binary_transformer_prev3b).
--define(PREV3C_TRANS_MOD, megaco_binary_transformer_prev3c).
-
--define(BIN_LIB, megaco_binary_encoder_lib).
-
-
-%%----------------------------------------------------------------------
-%% Detect (check/get) message version
-%% Return {ok, Version} | {error, Reason}
-%%----------------------------------------------------------------------
-
-version_of([{version3,v3},driver|EC], Binary) ->
- Decoders = [?V1_ASN1_MOD_DRV, ?V2_ASN1_MOD_DRV, ?V3_ASN1_MOD_DRV],
- ?BIN_LIB:version_of(EC, Binary, dynamic, Decoders);
-version_of([{version3,prev3c},driver|EC], Binary) ->
- Decoders = [?V1_ASN1_MOD_DRV, ?V2_ASN1_MOD_DRV, ?PREV3C_ASN1_MOD_DRV],
- ?BIN_LIB:version_of(EC, Binary, dynamic, Decoders);
-version_of([{version3,prev3b},driver|EC], Binary) ->
- Decoders = [?V1_ASN1_MOD_DRV, ?V2_ASN1_MOD_DRV, ?PREV3B_ASN1_MOD_DRV],
- ?BIN_LIB:version_of(EC, Binary, dynamic, Decoders);
-version_of([{version3,prev3a},driver|EC], Binary) ->
- Decoders = [?V1_ASN1_MOD_DRV, ?V2_ASN1_MOD_DRV, ?PREV3A_ASN1_MOD_DRV],
- ?BIN_LIB:version_of(EC, Binary, dynamic, Decoders);
-version_of([{version3,v3}|EC], Binary) ->
- Decoders = [?V1_ASN1_MOD, ?V2_ASN1_MOD, ?V3_ASN1_MOD],
- ?BIN_LIB:version_of(EC, Binary, dynamic, Decoders);
-version_of([{version3,prev3c}|EC], Binary) ->
- Decoders = [?V1_ASN1_MOD, ?V2_ASN1_MOD, ?PREV3C_ASN1_MOD],
- ?BIN_LIB:version_of(EC, Binary, dynamic, Decoders);
-version_of([{version3,prev3b}|EC], Binary) ->
- Decoders = [?V1_ASN1_MOD, ?V2_ASN1_MOD, ?PREV3B_ASN1_MOD],
- ?BIN_LIB:version_of(EC, Binary, dynamic, Decoders);
-version_of([{version3,prev3a}|EC], Binary) ->
- Decoders = [?V1_ASN1_MOD, ?V2_ASN1_MOD, ?PREV3A_ASN1_MOD],
- ?BIN_LIB:version_of(EC, Binary, dynamic, Decoders);
-version_of([driver|EC], Binary) ->
- Decoders = [?V1_ASN1_MOD_DRV, ?V2_ASN1_MOD_DRV, ?V3_ASN1_MOD_DRV],
- ?BIN_LIB:version_of(EC, Binary, dynamic, Decoders);
-
-%% All values we need to take (special) care of has been delt with,
-%% so just pass the rest on
-version_of(EC, Binary) ->
- Decoders = [?V1_ASN1_MOD, ?V2_ASN1_MOD, ?V3_ASN1_MOD],
- ?BIN_LIB:version_of(EC, Binary, dynamic, Decoders).
-
-
-%%----------------------------------------------------------------------
-%% Convert a 'MegacoMessage' record into a binary
-%% Return {ok, Binary} | {error, Reason}
-%%----------------------------------------------------------------------
-
-
-encode_message(EC,
- #'MegacoMessage'{mess = #'Message'{version = V}} = MegaMsg) ->
- encode_message(EC, V, MegaMsg).
-
-
-%% -- Version 1 --
-
-encode_message([{version3, _},driver|EC], 1, MegaMsg) ->
- AsnMod = ?V1_ASN1_MOD_DRV,
- TransMod = ?V1_TRANS_MOD,
- ?BIN_LIB:encode_message(EC, MegaMsg, AsnMod, TransMod, io_list);
-encode_message([driver|EC], 1, MegaMsg) ->
- AsnMod = ?V1_ASN1_MOD_DRV,
- TransMod = ?V1_TRANS_MOD,
- ?BIN_LIB:encode_message(EC, MegaMsg, AsnMod, TransMod, io_list);
-encode_message([{version3,_}|EC], 1, MegaMsg) ->
- AsnMod = ?V1_ASN1_MOD,
- TransMod = ?V1_TRANS_MOD,
- ?BIN_LIB:encode_message(EC, MegaMsg, AsnMod, TransMod, io_list);
-
-%% All values we need to take (special) care of has been delt with,
-%% so just pass the rest on
-encode_message(EC, 1, MegaMsg) ->
- AsnMod = ?V1_ASN1_MOD,
- TransMod = ?V1_TRANS_MOD,
- ?BIN_LIB:encode_message(EC, MegaMsg, AsnMod, TransMod, io_list);
-
-
-%% -- Version 2 --
-
-encode_message([{version3,_},driver|EC], 2, MegaMsg) ->
- AsnMod = ?V2_ASN1_MOD_DRV,
- TransMod = ?V2_TRANS_MOD,
- ?BIN_LIB:encode_message(EC, MegaMsg, AsnMod, TransMod, io_list);
-encode_message([driver|EC], 2, MegaMsg) ->
- AsnMod = ?V2_ASN1_MOD_DRV,
- TransMod = ?V2_TRANS_MOD,
- ?BIN_LIB:encode_message(EC, MegaMsg, AsnMod, TransMod, io_list);
-encode_message([{version3,_}|EC], 2, MegaMsg) ->
- AsnMod = ?V2_ASN1_MOD,
- TransMod = ?V2_TRANS_MOD,
- ?BIN_LIB:encode_message(EC, MegaMsg, AsnMod, TransMod, io_list);
-
-%% All values we need to take (special) care of has been delt with,
-%% so just pass the rest on
-encode_message(EC, 2, MegaMsg) ->
- AsnMod = ?V2_ASN1_MOD,
- TransMod = ?V2_TRANS_MOD,
- ?BIN_LIB:encode_message(EC, MegaMsg, AsnMod, TransMod, io_list);
-
-
-%% -- Version 3 --
-
-encode_message([{version3,v3},driver|EC], 3, MegaMsg) ->
- AsnMod = ?V3_ASN1_MOD_DRV,
- TransMod = ?V3_TRANS_MOD,
- ?BIN_LIB:encode_message(EC, MegaMsg, AsnMod, TransMod, io_list);
-encode_message([{version3,prev3c},driver|EC], 3, MegaMsg) ->
- AsnMod = ?PREV3C_ASN1_MOD_DRV,
- TransMod = ?PREV3C_TRANS_MOD,
- ?BIN_LIB:encode_message(EC, MegaMsg, AsnMod, TransMod, io_list);
-encode_message([{version3,prev3b},driver|EC], 3, MegaMsg) ->
- AsnMod = ?PREV3B_ASN1_MOD_DRV,
- TransMod = ?PREV3B_TRANS_MOD,
- ?BIN_LIB:encode_message(EC, MegaMsg, AsnMod, TransMod, io_list);
-encode_message([{version3,prev3a},driver|EC], 3, MegaMsg) ->
- AsnMod = ?PREV3A_ASN1_MOD_DRV,
- TransMod = ?PREV3A_TRANS_MOD,
- ?BIN_LIB:encode_message(EC, MegaMsg, AsnMod, TransMod, io_list);
-encode_message([{version3,v3}|EC], 3, MegaMsg) ->
- AsnMod = ?V3_ASN1_MOD,
- TransMod = ?V3_TRANS_MOD,
- ?BIN_LIB:encode_message(EC, MegaMsg, AsnMod, TransMod, io_list);
-encode_message([{version3,prev3c}|EC], 3, MegaMsg) ->
- AsnMod = ?PREV3C_ASN1_MOD,
- TransMod = ?PREV3C_TRANS_MOD,
- ?BIN_LIB:encode_message(EC, MegaMsg, AsnMod, TransMod, io_list);
-encode_message([{version3,prev3b}|EC], 3, MegaMsg) ->
- AsnMod = ?PREV3B_ASN1_MOD,
- TransMod = ?PREV3B_TRANS_MOD,
- ?BIN_LIB:encode_message(EC, MegaMsg, AsnMod, TransMod, io_list);
-encode_message([{version3,prev3a}|EC], 3, MegaMsg) ->
- AsnMod = ?PREV3A_ASN1_MOD,
- TransMod = ?PREV3A_TRANS_MOD,
- ?BIN_LIB:encode_message(EC, MegaMsg, AsnMod, TransMod, io_list);
-encode_message([driver|EC], 3, MegaMsg) ->
- AsnMod = ?V3_ASN1_MOD_DRV,
- TransMod = ?V3_TRANS_MOD,
- ?BIN_LIB:encode_message(EC, MegaMsg, AsnMod, TransMod, io_list);
-
-%% All values we need to take (special) care of has been delt with,
-%% so just pass the rest on
-encode_message(EC, 3, MegaMsg) ->
- AsnMod = ?V3_ASN1_MOD,
- TransMod = ?V3_TRANS_MOD,
- ?BIN_LIB:encode_message(EC, MegaMsg, AsnMod, TransMod, io_list).
-
-
-%%----------------------------------------------------------------------
-%% Convert a transaction (or transactions in the case of ack) record(s)
-%% into a binary
-%% Return {ok, Binary} | {error, Reason}
-%%----------------------------------------------------------------------
-
-%% encode_transaction([] = EC, 1, Trans) ->
-%% AsnMod = ?V1_ASN1_MOD,
-%% TransMod = ?V1_TRANS_MOD,
-%% ?BIN_LIB:encode_transaction(EC, Trans, AsnMod, TransMod,
-%% io_list);
-%% encode_transaction([native] = EC, 1, Trans) ->
-%% AsnMod = ?V1_ASN1_MOD,
-%% TransMod = ?V1_TRANS_MOD,
-%% ?BIN_LIB:encode_transaction(EC, Trans, AsnMod, TransMod,
-%% io_list);
-%% encode_transaction([driver|EC], 1, Trans) ->
-%% AsnMod = ?V1_ASN1_MOD_DRV,
-%% TransMod = ?V1_TRANS_MOD,
-%% ?BIN_LIB:encode_transaction(EC, Trans, AsnMod, TransMod,
-%% io_list);
-%% encode_transaction(_EC, 1, _Trans) ->
-%% AsnMod = ?V1_ASN1_MOD,
-%% TransMod = ?V1_TRANS_MOD,
-%% ?BIN_LIB:encode_transaction(EC, Trans, AsnMod, TransMod,
-%% io_list);
-%% encode_transaction([] = EC, 2, Trans) ->
-%% AsnMod = ?V2_ASN1_MOD,
-%% TransMod = ?V2_TRANS_MOD,
-%% ?BIN_LIB:encode_transaction(EC, Trans, AsnMod, TransMod,
-%% io_list);
-%% encode_transaction([native] = EC, 2, Trans) ->
-%% AsnMod = ?V2_ASN1_MOD,
-%% TransMod = ?V2_TRANS_MOD,
-%% ?BIN_LIB:encode_transaction(EC, Trans, AsnMod, TransMod,
-%% io_list);
-%% encode_transaction([driver|EC], 2, Trans) ->
-%% AsnMod = ?V2_ASN1_MOD_DRV,
-%% TransMod = ?V2_TRANS_MOD,
-%% ?BIN_LIB:encode_transaction(EC, Trans, AsnMod, TransMod,
-%% io_list);
-%%encode_transaction(_EC, 2, _Trans) ->
-%% AsnMod = ?V2_ASN1_MOD,
-%% TransMod = ?V2_TRANS_MOD,
-%% ?BIN_LIB:encode_transaction(EC, Trans, AsnMod, TransMod,
-%% io_list).
-%% encode_transaction([] = EC, 3, Trans) ->
-%% AsnMod = ?V3_ASN1_MOD,
-%% TransMod = ?V3_TRANS_MOD,
-%% ?BIN_LIB:encode_transaction(EC, Trans, AsnMod, TransMod,
-%% io_list);
-%% encode_transaction([native] = EC, 3, Trans) ->
-%% AsnMod = ?V3_ASN1_MOD,
-%% TransMod = ?V3_TRANS_MOD,
-%% ?BIN_LIB:encode_transaction(EC, Trans, AsnMod, TransMod,
-%% io_list);
-%% encode_transaction([driver|EC], 3, Trans) ->
-%% AsnMod = ?V3_ASN1_MOD_DRV,
-%% TransMod = ?V3_TRANS_MOD,
-%% ?BIN_LIB:encode_transaction(EC, Trans, AsnMod, TransMod,
-%% io_list);
-%%encode_transaction(_EC, 3, _Trans) ->
-%% AsnMod = ?V3_ASN1_MOD,
-%% TransMod = ?V3_TRANS_MOD,
-%% ?BIN_LIB:encode_transaction(EC, Trans, AsnMod, TransMod,
-%% io_list).
-encode_transaction(_EC, _V, _Trans) ->
- {error, not_implemented}.
-
-
-%%----------------------------------------------------------------------
-%% Convert a list of ActionRequest record's into a binary
-%% Return {ok, DeepIoList} | {error, Reason}
-%%----------------------------------------------------------------------
-%% encode_action_requests([] = EC, 1, ActReqs) when is_list(ActReqs) ->
-%% AsnMod = ?V1_ASN1_MOD,
-%% TransMod = ?V1_TRANS_MOD,
-%% ?BIN_LIB:encode_action_requests(EC, ActReqs,
-%% AsnMod, TransMod,
-%% io_list);
-%% encode_action_requests([native] = EC, 1, ActReqs) when is_list(ActReqs) ->
-%% AsnMod = ?V1_ASN1_MOD,
-%% TransMod = ?V1_TRANS_MOD,
-%% ?BIN_LIB:encode_action_requests(EC, ActReqs,
-%% AsnMod, TransMod,
-%% io_list);
-%% encode_action_requests([driver|EC], 1, ActReqs) when is_list(ActReqs) ->
-%% AsnMod = ?V1_ASN1_MOD_DRV,
-%% TransMod = ?V1_TRANS_MOD,
-%% ?BIN_LIB:encode_action_requests(EC, ActReqs,
-%% AsnMod, TransMod,
-%% io_list);
-%% encode_action_requests(_EC, 1, ActReqs) when is_list(ActReqs) ->
-%% AsnMod = ?V1_ASN1_MOD,
-%% TransMod = ?V1_TRANS_MOD,
-%% ?BIN_LIB:encode_action_requests(EC, ActReqs,
-%% AsnMod, TransMod,
-%% io_list);
-%% encode_action_requests([] = EC, 2, ActReqs) when is_list(ActReqs) ->
-%% AsnMod = ?V2_ASN1_MOD,
-%% TransMod = ?V2_TRANS_MOD,
-%% ?BIN_LIB:encode_action_requests(EC, ActReqs,
-%% AsnMod, TransMod,
-%% io_list);
-%% encode_action_requests([native] = EC, 2, ActReqs) when is_list(ActReqs) ->
-%% AsnMod = ?V2_ASN1_MOD,
-%% TransMod = ?V2_TRANS_MOD,
-%% ?BIN_LIB:encode_action_requests(EC, ActReqs,
-%% AsnMod, TransMod,
-%% io_list);
-%% encode_action_requests([driver|EC], 2, ActReqs) when is_list(ActReqs) ->
-%% AsnMod = ?V2_ASN1_MOD_DRV,
-%% TransMod = ?V2_TRANS_MOD,
-%% ?BIN_LIB:encode_action_requests(EC, ActReqs,
-%% AsnMod, TransMod,
-%% io_list);
-%% encode_action_requests(_EC, 2, ActReqs) when is_list(ActReqs) ->
-%% AsnMod = ?V2_ASN1_MOD,
-%% TransMod = ?V2_TRANS_MOD,
-%% ?BIN_LIB:encode_action_requests(EC, ActReqs,
-%% AsnMod, TransMod,
-%% io_list);
-%% encode_action_requests([] = EC, 3, ActReqs) when is_list(ActReqs) ->
-%% AsnMod = ?V3_ASN1_MOD,
-%% TransMod = ?V3_TRANS_MOD,
-%% ?BIN_LIB:encode_action_requests(EC, ActReqs,
-%% AsnMod, TransMod,
-%% io_list);
-%% encode_action_requests([native] = EC, 3, ActReqs) when is_list(ActReqs) ->
-%% AsnMod = ?V3_ASN1_MOD,
-%% TransMod = ?V3_TRANS_MOD,
-%% ?BIN_LIB:encode_action_requests(EC, ActReqs,
-%% AsnMod, TransMod,
-%% io_list);
-%% encode_action_requests([driver|EC], 3, ActReqs) when is_list(ActReqs) ->
-%% AsnMod = ?V3_ASN1_MOD_DRV,
-%% TransMod = ?V3_TRANS_MOD,
-%% ?BIN_LIB:encode_action_requests(EC, ActReqs,
-%% AsnMod, TransMod,
-%% io_list);
-%%encode_action_requests(_EC, 3, ActReqs) when is_list(ActReqs) ->
-%% AsnMod = ?V3_ASN1_MOD,
-%% TransMod = ?V3_TRANS_MOD,
-%% ?BIN_LIB:encode_action_requests(EC, ActReqs,
-%% AsnMod, TransMod,
-%% io_list);
-%% encode_action_requests(_EC, V, _ActReqs) ->
-%% {error, {bad_version, V}}.
-encode_action_requests(_EC, _V, _ActReqs) ->
- {error, not_implemented}.
-
-
-%%----------------------------------------------------------------------
-%% Convert a ActionRequest record into a binary
-%% Return {ok, DeepIoList} | {error, Reason}
-%%----------------------------------------------------------------------
-%% encode_action_request([] = EC, 1, ActReq) ->
-%% AsnMod = ?V1_ASN1_MOD,
-%% TransMod = ?V1_TRANS_MOD,
-%% ?BIN_LIB:encode_action_request(EC, ActReq,
-%% AsnMod, TransMod,
-%% io_list);
-%% encode_action_request([native] = EC, 1, ActReq) ->
-%% AsnMod = ?V1_ASN1_MOD,
-%% TransMod = ?V1_TRANS_MOD,
-%% ?BIN_LIB:encode_action_request(EC, ActReq,
-%% AsnMod, TransMod,
-%% io_list);
-%% encode_action_request([driver|EC], 1, ActReq) ->
-%% AsnMod = ?V1_ASN1_MOD_DRV,
-%% TransMod = ?V1_TRANS_MOD,
-%% ?BIN_LIB:encode_action_request(EC, ActReq,
-%% AsnMod, TransMod,
-%% io_list);
-%% encode_action_request(_EC, 1, ActReq) ->
-%% AsnMod = ?V1_ASN1_MOD,
-%% TransMod = ?V1_TRANS_MOD,
-%% ?BIN_LIB:encode_action_request(EC, ActReq,
-%% AsnMod, TransMod,
-%% io_list);
-%% encode_action_request([] = EC, 2, ActReq) ->
-%% AsnMod = ?V2_ASN1_MOD,
-%% TransMod = ?V2_TRANS_MOD,
-%% ?BIN_LIB:encode_action_request(EC, ActReq,
-%% AsnMod, TransMod,
-%% io_list);
-%% encode_action_request([native] = EC, 2, ActReq) ->
-%% AsnMod = ?V2_ASN1_MOD,
-%% TransMod = ?V2_TRANS_MOD,
-%% ?BIN_LIB:encode_action_request(EC, ActReq,
-%% AsnMod, TransMod,
-%% io_list);
-%% encode_action_request([driver|EC], 2, ActReq) ->
-%% AsnMod = ?V2_ASN1_MOD_DRV,
-%% TransMod = ?V2_TRANS_MOD,
-%% ?BIN_LIB:encode_action_request(EC, ActReq,
-%% AsnMod, TransMod,
-%% io_list);
-%% encode_action_request(_EC, 2, ActReq) ->
-%% AsnMod = ?V2_ASN1_MOD,
-%% TransMod = ?V2_TRANS_MOD,
-%% ?BIN_LIB:encode_action_request(EC, ActReq,
-%% AsnMod, TransMod,
-%% io_list);
-%% encode_action_request([] = EC, 3, ActReq) ->
-%% AsnMod = ?V3_ASN1_MOD,
-%% TransMod = ?V3_TRANS_MOD,
-%% ?BIN_LIB:encode_action_request(EC, ActReq,
-%% AsnMod, TransMod,
-%% io_list);
-%% encode_action_request([native] = EC, 3, ActReq) ->
-%% AsnMod = ?V3_ASN1_MOD,
-%% TransMod = ?V3_TRANS_MOD,
-%% ?BIN_LIB:encode_action_request(EC, ActReq,
-%% AsnMod, TransMod,
-%% io_list);
-%% encode_action_request([driver|EC], 3, ActReq) ->
-%% AsnMod = ?V3_ASN1_MOD_DRV,
-%% TransMod = ?V3_TRANS_MOD,
-%% ?BIN_LIB:encode_action_request(EC, ActReq,
-%% AsnMod, TransMod,
-%% io_list);
-%% encode_action_request(_EC, 3, ActReq) ->
-%% AsnMod = ?V3_ASN1_MOD,
-%% TransMod = ?V3_TRANS_MOD,
-%% ?BIN_LIB:encode_action_request(EC, ActReq,
-%% AsnMod, TransMod,
-%% io_list);
-encode_action_request(_EC, _V, _ActReq) ->
- {error, not_implemented}.
-
-
-%%----------------------------------------------------------------------
-%% Convert a action reply into a deep io list
-%% Not yest supported by this binary codec!
-%% Return {ok, DeepIoList} | {error, Reason}
-%%----------------------------------------------------------------------
-
-encode_action_reply(_EC, _V, _AcionReply) ->
- {error, not_implemented}.
-
-
-%%----------------------------------------------------------------------
-%% Convert a binary into a 'MegacoMessage' record
-%% Return {ok, MegacoMessageRecord} | {error, Reason}
-%%----------------------------------------------------------------------
-
-%% Old decode function
-decode_message(EC, Binary) ->
- decode_message(EC, 1, Binary).
-
-%% -- Dynamic version detection --
-
-%% Select from message
-decode_message([{version3,v3},driver|EC], dynamic, Binary) ->
- Mods = [{?V1_ASN1_MOD_DRV, ?V1_TRANS_MOD},
- {?V2_ASN1_MOD_DRV, ?V2_TRANS_MOD},
- {?V3_ASN1_MOD_DRV, ?V3_TRANS_MOD}],
- ?BIN_LIB:decode_message_dynamic(EC, Binary, Mods, binary);
-decode_message([{version3,prev3c},driver|EC], dynamic, Binary) ->
- Mods = [{?V1_ASN1_MOD_DRV, ?V1_TRANS_MOD},
- {?V2_ASN1_MOD_DRV, ?V2_TRANS_MOD},
- {?PREV3C_ASN1_MOD_DRV, ?PREV3C_TRANS_MOD}],
- ?BIN_LIB:decode_message_dynamic(EC, Binary, Mods, binary);
-decode_message([{version3,prev3b},driver|EC], dynamic, Binary) ->
- Mods = [{?V1_ASN1_MOD_DRV, ?V1_TRANS_MOD},
- {?V2_ASN1_MOD_DRV, ?V2_TRANS_MOD},
- {?PREV3B_ASN1_MOD_DRV, ?PREV3B_TRANS_MOD}],
- ?BIN_LIB:decode_message_dynamic(EC, Binary, Mods, binary);
-decode_message([{version3,prev3a},driver|EC], dynamic, Binary) ->
- Mods = [{?V1_ASN1_MOD_DRV, ?V1_TRANS_MOD},
- {?V2_ASN1_MOD_DRV, ?V2_TRANS_MOD},
- {?PREV3A_ASN1_MOD_DRV, ?PREV3A_TRANS_MOD}],
- ?BIN_LIB:decode_message_dynamic(EC, Binary, Mods, binary);
-decode_message([{version3,v3}|EC], dynamic, Binary) ->
- Mods = [{?V1_ASN1_MOD, ?V1_TRANS_MOD},
- {?V2_ASN1_MOD, ?V2_TRANS_MOD},
- {?V3_ASN1_MOD, ?V3_TRANS_MOD}],
- ?BIN_LIB:decode_message_dynamic(EC, Binary, Mods, binary);
-decode_message([{version3,prev3c}|EC], dynamic, Binary) ->
- Mods = [{?V1_ASN1_MOD, ?V1_TRANS_MOD},
- {?V2_ASN1_MOD, ?V2_TRANS_MOD},
- {?PREV3C_ASN1_MOD, ?PREV3C_TRANS_MOD}],
- ?BIN_LIB:decode_message_dynamic(EC, Binary, Mods, binary);
-decode_message([{version3,prev3b}|EC], dynamic, Binary) ->
- Mods = [{?V1_ASN1_MOD, ?V1_TRANS_MOD},
- {?V2_ASN1_MOD, ?V2_TRANS_MOD},
- {?PREV3B_ASN1_MOD, ?PREV3B_TRANS_MOD}],
- ?BIN_LIB:decode_message_dynamic(EC, Binary, Mods, binary);
-decode_message([{version3,prev3a}|EC], dynamic, Binary) ->
- Mods = [{?V1_ASN1_MOD, ?V1_TRANS_MOD},
- {?V2_ASN1_MOD, ?V2_TRANS_MOD},
- {?PREV3A_ASN1_MOD, ?PREV3A_TRANS_MOD}],
- ?BIN_LIB:decode_message_dynamic(EC, Binary, Mods, binary);
-decode_message([driver|EC], dynamic, Binary) ->
- Mods = [{?V1_ASN1_MOD_DRV, ?V1_TRANS_MOD},
- {?V2_ASN1_MOD_DRV, ?V2_TRANS_MOD},
- {?V3_ASN1_MOD_DRV, ?V3_TRANS_MOD}],
- ?BIN_LIB:decode_message_dynamic(EC, Binary, Mods, binary);
-
-%% All values we need to take (special) care of has been delt with,
-%% so just pass the rest on
-decode_message(EC, dynamic, Binary) ->
- Mods = [{?V1_ASN1_MOD, ?V1_TRANS_MOD},
- {?V2_ASN1_MOD, ?V2_TRANS_MOD},
- {?V3_ASN1_MOD, ?V3_TRANS_MOD}],
- ?BIN_LIB:decode_message_dynamic(EC, Binary, Mods, binary);
-
-
-%% -- Version 1 --
-
-decode_message([{version3,_},driver|EC], 1, Binary) ->
- AsnMod = ?V1_ASN1_MOD_DRV,
- TransMod = ?V1_TRANS_MOD,
- ?BIN_LIB:decode_message(EC, Binary, AsnMod, TransMod, binary);
-decode_message([driver|EC], 1, Binary) ->
- AsnMod = ?V1_ASN1_MOD_DRV,
- TransMod = ?V1_TRANS_MOD,
- ?BIN_LIB:decode_message(EC, Binary, AsnMod, TransMod, binary);
-decode_message([{version3,_}|EC], 1, Binary) ->
- AsnMod = ?V1_ASN1_MOD,
- TransMod = ?V1_TRANS_MOD,
- ?BIN_LIB:decode_message(EC, Binary, AsnMod, TransMod, binary);
-
-%% All values we need to take (special) care of has been delt with,
-%% so just pass the rest on
-decode_message(EC, 1, Binary) ->
- AsnMod = ?V1_ASN1_MOD,
- TransMod = ?V1_TRANS_MOD,
- ?BIN_LIB:decode_message(EC, Binary, AsnMod, TransMod, binary);
-
-
-%% -- Version 2 --
-
-decode_message([{version3,_},driver|EC], 2, Binary) ->
- AsnMod = ?V2_ASN1_MOD_DRV,
- TransMod = ?V2_TRANS_MOD,
- ?BIN_LIB:decode_message(EC, Binary, AsnMod, TransMod, binary);
-decode_message([driver|EC], 2, Binary) ->
- AsnMod = ?V2_ASN1_MOD_DRV,
- TransMod = ?V2_TRANS_MOD,
- ?BIN_LIB:decode_message(EC, Binary, AsnMod, TransMod, binary);
-decode_message([{version3,_}|EC], 2, Binary) ->
- AsnMod = ?V2_ASN1_MOD,
- TransMod = ?V2_TRANS_MOD,
- ?BIN_LIB:decode_message(EC, Binary, AsnMod, TransMod, binary);
-
-%% All values we need to take (special) care of has been delt with,
-%% so just pass the rest on
-decode_message(EC, 2, Binary) ->
- AsnMod = ?V2_ASN1_MOD,
- TransMod = ?V2_TRANS_MOD,
- ?BIN_LIB:decode_message(EC, Binary, AsnMod, TransMod, binary);
-
-
-%% -- Version 3 --
-
-decode_message([{version3,v3},driver|EC], 3, Binary) ->
- AsnMod = ?V3_ASN1_MOD_DRV,
- TransMod = ?V3_TRANS_MOD,
- ?BIN_LIB:decode_message(EC, Binary, AsnMod, TransMod, binary);
-decode_message([{version3,prev3c},driver|EC], 3, Binary) ->
- AsnMod = ?PREV3C_ASN1_MOD_DRV,
- TransMod = ?PREV3C_TRANS_MOD,
- ?BIN_LIB:decode_message(EC, Binary, AsnMod, TransMod, binary);
-decode_message([{version3,prev3b},driver|EC], 3, Binary) ->
- AsnMod = ?PREV3B_ASN1_MOD_DRV,
- TransMod = ?PREV3B_TRANS_MOD,
- ?BIN_LIB:decode_message(EC, Binary, AsnMod, TransMod, binary);
-decode_message([{version3,prev3a},driver|EC], 3, Binary) ->
- AsnMod = ?PREV3A_ASN1_MOD_DRV,
- TransMod = ?PREV3A_TRANS_MOD,
- ?BIN_LIB:decode_message(EC, Binary, AsnMod, TransMod, binary);
-decode_message([{version3,v3}|EC], 3, Binary) ->
- AsnMod = ?V3_ASN1_MOD,
- TransMod = ?V3_TRANS_MOD,
- ?BIN_LIB:decode_message(EC, Binary, AsnMod, TransMod, binary);
-decode_message([{version3,prev3c}|EC], 3, Binary) ->
- AsnMod = ?PREV3C_ASN1_MOD,
- TransMod = ?PREV3C_TRANS_MOD,
- ?BIN_LIB:decode_message(EC, Binary, AsnMod, TransMod, binary);
-decode_message([{version3,prev3b}|EC], 3, Binary) ->
- AsnMod = ?PREV3B_ASN1_MOD,
- TransMod = ?PREV3B_TRANS_MOD,
- ?BIN_LIB:decode_message(EC, Binary, AsnMod, TransMod, binary);
-decode_message([{version3,prev3a}|EC], 3, Binary) ->
- AsnMod = ?PREV3A_ASN1_MOD,
- TransMod = ?PREV3A_TRANS_MOD,
- ?BIN_LIB:decode_message(EC, Binary, AsnMod, TransMod, binary);
-decode_message([driver|EC], 3, Binary) ->
- AsnMod = ?V3_ASN1_MOD_DRV,
- TransMod = ?V3_TRANS_MOD,
- ?BIN_LIB:decode_message(EC, Binary, AsnMod, TransMod, binary);
-
-%% All values we need to take (special) care of has been delt with,
-%% so just pass the rest on
-decode_message(EC, 3, Binary) ->
- AsnMod = ?V3_ASN1_MOD,
- TransMod = ?V3_TRANS_MOD,
- ?BIN_LIB:decode_message(EC, Binary, AsnMod, TransMod, binary).
-
-
-decode_mini_message([{version3,v3},driver|EC], dynamic, Bin) ->
- Mods = [?V1_ASN1_MOD_DRV,
- ?V2_ASN1_MOD_DRV,
- ?V3_ASN1_MOD_DRV],
- ?BIN_LIB:decode_mini_message_dynamic(EC, Bin, Mods, binary);
-decode_mini_message([{version3,prev3c},driver|EC], dynamic, Bin) ->
- Mods = [?V1_ASN1_MOD_DRV,
- ?V2_ASN1_MOD_DRV,
- ?PREV3C_ASN1_MOD_DRV],
- ?BIN_LIB:decode_mini_message_dynamic(EC, Bin, Mods, binary);
-decode_mini_message([{version3,prev3b},driver|EC], dynamic, Bin) ->
- Mods = [?V1_ASN1_MOD_DRV,
- ?V2_ASN1_MOD_DRV,
- ?PREV3B_ASN1_MOD_DRV],
- ?BIN_LIB:decode_mini_message_dynamic(EC, Bin, Mods, binary);
-decode_mini_message([{version3,prev3a},driver|EC], dynamic, Bin) ->
- Mods = [?V1_ASN1_MOD_DRV,
- ?V2_ASN1_MOD_DRV,
- ?PREV3A_ASN1_MOD_DRV],
- ?BIN_LIB:decode_mini_message_dynamic(EC, Bin, Mods, binary);
-decode_mini_message([{version3,v3}|EC], dynamic, Bin) ->
- Mods = [?V1_ASN1_MOD,
- ?V2_ASN1_MOD,
- ?V3_ASN1_MOD],
- ?BIN_LIB:decode_mini_message_dynamic(EC, Bin, Mods, binary);
-decode_mini_message([{version3,prev3c}|EC], dynamic, Bin) ->
- Mods = [?V1_ASN1_MOD,
- ?V2_ASN1_MOD,
- ?PREV3C_ASN1_MOD],
- ?BIN_LIB:decode_mini_message_dynamic(EC, Bin, Mods, binary);
-decode_mini_message([{version3,prev3b}|EC], dynamic, Bin) ->
- Mods = [?V1_ASN1_MOD,
- ?V2_ASN1_MOD,
- ?PREV3B_ASN1_MOD],
- ?BIN_LIB:decode_mini_message_dynamic(EC, Bin, Mods, binary);
-decode_mini_message([{version3,prev3a}|EC], dynamic, Bin) ->
- Mods = [?V1_ASN1_MOD,
- ?V2_ASN1_MOD,
- ?PREV3A_ASN1_MOD],
- ?BIN_LIB:decode_mini_message_dynamic(EC, Bin, Mods, binary);
-decode_mini_message([driver|EC], dynamic, Bin) ->
- Mods = [?V1_ASN1_MOD_DRV,
- ?V2_ASN1_MOD_DRV,
- ?V3_ASN1_MOD_DRV],
- ?BIN_LIB:decode_mini_message_dynamic(EC, Bin, Mods, binary);
-decode_mini_message(EC, dynamic, Bin) ->
- Mods = [?V1_ASN1_MOD,
- ?V2_ASN1_MOD,
- ?V3_ASN1_MOD],
- ?BIN_LIB:decode_mini_message_dynamic(EC, Bin, Mods, binary);
-decode_mini_message([{version3,_},driver|EC], 1, Bin) ->
- AsnMod = ?V1_ASN1_MOD_DRV,
- ?BIN_LIB:decode_mini_message(EC, Bin, AsnMod, binary);
-decode_mini_message([{version3,_}|EC], 1, Bin) ->
- AsnMod = ?V1_ASN1_MOD,
- ?BIN_LIB:decode_mini_message(EC, Bin, AsnMod, binary);
-decode_mini_message([driver|EC], 1, Bin) ->
- AsnMod = ?V1_ASN1_MOD_DRV,
- ?BIN_LIB:decode_mini_message(EC, Bin, AsnMod, binary);
-decode_mini_message(EC, 1, Bin) ->
- AsnMod = ?V1_ASN1_MOD,
- ?BIN_LIB:decode_mini_message(EC, Bin, AsnMod, binary);
-decode_mini_message([{version3,_},driver|EC], 2, Bin) ->
- AsnMod = ?V2_ASN1_MOD_DRV,
- ?BIN_LIB:decode_mini_message(EC, Bin, AsnMod, binary);
-decode_mini_message([{version3,_}|EC], 2, Bin) ->
- AsnMod = ?V2_ASN1_MOD,
- ?BIN_LIB:decode_mini_message(EC, Bin, AsnMod, binary);
-decode_mini_message([driver|EC], 2, Bin) ->
- AsnMod = ?V2_ASN1_MOD_DRV,
- ?BIN_LIB:decode_mini_message(EC, Bin, AsnMod, binary);
-decode_mini_message(EC, 2, Bin) ->
- AsnMod = ?V2_ASN1_MOD,
- ?BIN_LIB:decode_mini_message(EC, Bin, AsnMod, binary);
-decode_mini_message([{version3,v3},driver|EC], 3, Bin) ->
- AsnMod = ?V3_ASN1_MOD_DRV,
- ?BIN_LIB:decode_mini_message(EC, Bin, AsnMod, binary);
-decode_mini_message([{version3,prev3c},driver|EC], 3, Bin) ->
- AsnMod = ?PREV3C_ASN1_MOD_DRV,
- ?BIN_LIB:decode_mini_message(EC, Bin, AsnMod, binary);
-decode_mini_message([{version3,prev3b},driver|EC], 3, Bin) ->
- AsnMod = ?PREV3B_ASN1_MOD_DRV,
- ?BIN_LIB:decode_mini_message(EC, Bin, AsnMod, binary);
-decode_mini_message([{version3,prev3a},driver|EC], 3, Bin) ->
- AsnMod = ?PREV3A_ASN1_MOD_DRV,
- ?BIN_LIB:decode_mini_message(EC, Bin, AsnMod, binary);
-decode_mini_message([{version3,v3}|EC], 3, Bin) ->
- AsnMod = ?V3_ASN1_MOD,
- ?BIN_LIB:decode_mini_message(EC, Bin, AsnMod, binary);
-decode_mini_message([{version3,prev3c}|EC], 3, Bin) ->
- AsnMod = ?PREV3C_ASN1_MOD,
- ?BIN_LIB:decode_mini_message(EC, Bin, AsnMod, binary);
-decode_mini_message([{version3,prev3b}|EC], 3, Bin) ->
- AsnMod = ?PREV3B_ASN1_MOD,
- ?BIN_LIB:decode_mini_message(EC, Bin, AsnMod, binary);
-decode_mini_message([{version3,prev3a}|EC], 3, Bin) ->
- AsnMod = ?PREV3A_ASN1_MOD,
- ?BIN_LIB:decode_mini_message(EC, Bin, AsnMod, binary);
-decode_mini_message([driver|EC], 3, Bin) ->
- AsnMod = ?V3_ASN1_MOD_DRV,
- ?BIN_LIB:decode_mini_message(EC, Bin, AsnMod, binary);
-decode_mini_message(EC, 3, Bin) ->
- AsnMod = ?V3_ASN1_MOD,
- ?BIN_LIB:decode_mini_message(EC, Bin, AsnMod, binary).
diff --git a/lib/megaco/src/binary/megaco_ber_bin_media_gateway_control_prev3a.set.asn b/lib/megaco/src/binary/megaco_ber_bin_media_gateway_control_prev3a.set.asn
deleted file mode 100644
index b9ba7ffdb4..0000000000
--- a/lib/megaco/src/binary/megaco_ber_bin_media_gateway_control_prev3a.set.asn
+++ /dev/null
@@ -1 +0,0 @@
-MEDIA-GATEWAY-CONTROL-prev3a.asn
diff --git a/lib/megaco/src/binary/megaco_ber_bin_media_gateway_control_prev3b.set.asn b/lib/megaco/src/binary/megaco_ber_bin_media_gateway_control_prev3b.set.asn
deleted file mode 100644
index 0437bde310..0000000000
--- a/lib/megaco/src/binary/megaco_ber_bin_media_gateway_control_prev3b.set.asn
+++ /dev/null
@@ -1 +0,0 @@
-MEDIA-GATEWAY-CONTROL-prev3b.asn
diff --git a/lib/megaco/src/binary/megaco_ber_bin_media_gateway_control_prev3c.asn1config b/lib/megaco/src/binary/megaco_ber_bin_media_gateway_control_prev3c.asn1config
deleted file mode 100644
index c74422b9a2..0000000000
--- a/lib/megaco/src/binary/megaco_ber_bin_media_gateway_control_prev3c.asn1config
+++ /dev/null
@@ -1,43 +0,0 @@
-{exclusive_decode,
- {'megaco_ber_bin_media_gateway_control_prev3c',
- [
- {decode_message_trans_partial,
- [
- 'MegacoMessage',[{mess,[{messageBody,[{transactions,parts}]}]}]
- ]
- },
- {decode_message_acts_partial,
- ['Transaction',
- [
- {transactionRequest,
- [
- {actions,parts}
- ]
- },
- {transactionReply,
- [
- {transactionResult, [{actionReplies,parts}]}
- ]
- }
- ]
- ]
- },
- {decode_message_version,
- ['MegacoMessage',
- [
- {authHeader,undecoded},
- {mess,[{mId,undecoded},{messageBody,undecoded}]}
- ]
- ]
- },
- {decode_message_mId,
- ['MegacoMessage',
- [
- {authHeader,undecoded},
- {mess,[{messageBody,undecoded}]}
- ]
- ]
- }
- ]
- }
-}.
diff --git a/lib/megaco/src/binary/megaco_ber_bin_media_gateway_control_prev3c.set.asn b/lib/megaco/src/binary/megaco_ber_bin_media_gateway_control_prev3c.set.asn
deleted file mode 100644
index e78055fbad..0000000000
--- a/lib/megaco/src/binary/megaco_ber_bin_media_gateway_control_prev3c.set.asn
+++ /dev/null
@@ -1 +0,0 @@
-MEDIA-GATEWAY-CONTROL-prev3c.asn
diff --git a/lib/megaco/src/binary/megaco_ber_bin_media_gateway_control_v1.asn1config b/lib/megaco/src/binary/megaco_ber_bin_media_gateway_control_v1.asn1config
deleted file mode 100644
index e815e90948..0000000000
--- a/lib/megaco/src/binary/megaco_ber_bin_media_gateway_control_v1.asn1config
+++ /dev/null
@@ -1,44 +0,0 @@
-{exclusive_decode,
- {'megaco_ber_bin_media_gateway_control_v1',
- [
- {decode_message_trans_partial,
- [
- 'MegacoMessage',[{mess,[{messageBody,[{transactions,parts}]}]}]
- ]
- },
- {decode_message_acts_partial,
- ['Transaction',
- [
- {transactionRequest,
- [
- {actions,parts}
- ]
- },
- {transactionReply,
- [
- {transactionResult, [{actionReplies,parts}]}
- ]
- }
- ]
- ]
- },
- {decode_message_version,
- ['MegacoMessage',
- [
- {authHeader,undecoded},
- {mess,[{mId,undecoded},{messageBody,undecoded}]}
- ]
- ]
- },
- {decode_message_mId,
- ['MegacoMessage',
- [
- {authHeader,undecoded},
- {mess,[{messageBody,undecoded}]}
- ]
- ]
- }
-
- ]
- }
-}.
diff --git a/lib/megaco/src/binary/megaco_ber_bin_media_gateway_control_v1.set.asn b/lib/megaco/src/binary/megaco_ber_bin_media_gateway_control_v1.set.asn
deleted file mode 100644
index 0f5a92dba1..0000000000
--- a/lib/megaco/src/binary/megaco_ber_bin_media_gateway_control_v1.set.asn
+++ /dev/null
@@ -1 +0,0 @@
-MEDIA-GATEWAY-CONTROL-v1.asn
diff --git a/lib/megaco/src/binary/megaco_ber_bin_media_gateway_control_v2.set.asn b/lib/megaco/src/binary/megaco_ber_bin_media_gateway_control_v2.set.asn
deleted file mode 100644
index 7fc82b127f..0000000000
--- a/lib/megaco/src/binary/megaco_ber_bin_media_gateway_control_v2.set.asn
+++ /dev/null
@@ -1 +0,0 @@
-MEDIA-GATEWAY-CONTROL-v2.asn
diff --git a/lib/megaco/src/binary/megaco_ber_bin_media_gateway_control_v3.set.asn b/lib/megaco/src/binary/megaco_ber_bin_media_gateway_control_v3.set.asn
deleted file mode 100644
index 1d7950a283..0000000000
--- a/lib/megaco/src/binary/megaco_ber_bin_media_gateway_control_v3.set.asn
+++ /dev/null
@@ -1 +0,0 @@
-MEDIA-GATEWAY-CONTROL-v3.asn
diff --git a/lib/megaco/src/binary/megaco_ber_bin_media_gateway_control_v2.asn1config b/lib/megaco/src/binary/megaco_ber_media_gateway_control_prev3a.asn1config
index cc072b30ee..da67561f1b 100644
--- a/lib/megaco/src/binary/megaco_ber_bin_media_gateway_control_v2.asn1config
+++ b/lib/megaco/src/binary/megaco_ber_media_gateway_control_prev3a.asn1config
@@ -1,5 +1,5 @@
{exclusive_decode,
- {'megaco_ber_bin_media_gateway_control_v2',
+ {'megaco_ber_media_gateway_control_prev3a',
[
{decode_message_trans_partial,
[
@@ -14,7 +14,7 @@
{actions,parts}
]
},
- {transactionReply,
+ {transactionReply,
[
{transactionResult, [{actionReplies,parts}]}
]
diff --git a/lib/megaco/src/binary/megaco_ber_bin_media_gateway_control_v3.asn1config b/lib/megaco/src/binary/megaco_ber_media_gateway_control_prev3b.asn1config
index deeb2b2da9..2f25f03d97 100644
--- a/lib/megaco/src/binary/megaco_ber_bin_media_gateway_control_v3.asn1config
+++ b/lib/megaco/src/binary/megaco_ber_media_gateway_control_prev3b.asn1config
@@ -1,5 +1,5 @@
{exclusive_decode,
- {'megaco_ber_bin_media_gateway_control_v3',
+ {'megaco_ber_media_gateway_control_prev3b',
[
{decode_message_trans_partial,
[
@@ -14,7 +14,7 @@
{actions,parts}
]
},
- {transactionReply,
+ {transactionReply,
[
{transactionResult, [{actionReplies,parts}]}
]
diff --git a/lib/megaco/src/binary/megaco_ber_bin_media_gateway_control_prev3a.asn1config b/lib/megaco/src/binary/megaco_ber_media_gateway_control_prev3c.asn1config
index 456ce750ad..23c343eed0 100644
--- a/lib/megaco/src/binary/megaco_ber_bin_media_gateway_control_prev3a.asn1config
+++ b/lib/megaco/src/binary/megaco_ber_media_gateway_control_prev3c.asn1config
@@ -1,5 +1,5 @@
{exclusive_decode,
- {'megaco_ber_bin_media_gateway_control_prev3a',
+ {'megaco_ber_media_gateway_control_prev3c',
[
{decode_message_trans_partial,
[
@@ -14,7 +14,7 @@
{actions,parts}
]
},
- {transactionReply,
+ {transactionReply,
[
{transactionResult, [{actionReplies,parts}]}
]
diff --git a/lib/megaco/src/binary/megaco_ber_bin_drv_media_gateway_control_v1.asn1config b/lib/megaco/src/binary/megaco_ber_media_gateway_control_v1.asn1config
index ea10a7d527..951825c0aa 100644
--- a/lib/megaco/src/binary/megaco_ber_bin_drv_media_gateway_control_v1.asn1config
+++ b/lib/megaco/src/binary/megaco_ber_media_gateway_control_v1.asn1config
@@ -1,5 +1,5 @@
{exclusive_decode,
- {'megaco_ber_bin_drv_media_gateway_control_v1',
+ {'megaco_ber_media_gateway_control_v1',
[
{decode_message_trans_partial,
[
@@ -14,7 +14,7 @@
{actions,parts}
]
},
- {transactionReply,
+ {transactionReply,
[
{transactionResult, [{actionReplies,parts}]}
]
@@ -38,7 +38,7 @@
]
]
}
+
]
}
}.
-
diff --git a/lib/megaco/src/binary/megaco_ber_bin_media_gateway_control_prev3b.asn1config b/lib/megaco/src/binary/megaco_ber_media_gateway_control_v3.asn1config
index fa5cd80baf..e4b1f9ece9 100644
--- a/lib/megaco/src/binary/megaco_ber_bin_media_gateway_control_prev3b.asn1config
+++ b/lib/megaco/src/binary/megaco_ber_media_gateway_control_v3.asn1config
@@ -1,5 +1,5 @@
{exclusive_decode,
- {'megaco_ber_bin_media_gateway_control_prev3b',
+ {'megaco_ber_media_gateway_control_v3',
[
{decode_message_trans_partial,
[
@@ -14,7 +14,7 @@
{actions,parts}
]
},
- {transactionReply,
+ {transactionReply,
[
{transactionResult, [{actionReplies,parts}]}
]
diff --git a/lib/megaco/src/binary/megaco_binary_encoder.erl b/lib/megaco/src/binary/megaco_binary_encoder.erl
index f825f91a45..51e167590d 100644
--- a/lib/megaco/src/binary/megaco_binary_encoder.erl
+++ b/lib/megaco/src/binary/megaco_binary_encoder.erl
@@ -241,55 +241,30 @@ encode_action_reply(_EC, _V, _AcionReply) ->
%% Return {ok, Version} | {error, Reason}
%%----------------------------------------------------------------------
-version_of([{version3,v3},driver|EC], Binary) ->
- Decoders = [megaco_ber_bin_drv_media_gateway_control_v1,
- megaco_ber_bin_drv_media_gateway_control_v2,
- megaco_ber_bin_drv_media_gateway_control_v3],
- ?BIN_LIB:version_of(EC, Binary, dynamic, Decoders);
-version_of([{version3,prev3c},driver|EC], Binary) ->
- Decoders = [megaco_ber_bin_drv_media_gateway_control_v1,
- megaco_ber_bin_drv_media_gateway_control_v2,
- megaco_ber_bin_drv_media_gateway_control_prev3c],
- ?BIN_LIB:version_of(EC, Binary, dynamic, Decoders);
-version_of([{version3,prev3b},driver|EC], Binary) ->
- Decoders = [megaco_ber_bin_drv_media_gateway_control_v1,
- megaco_ber_bin_drv_media_gateway_control_v2,
- megaco_ber_bin_drv_media_gateway_control_prev3b],
- ?BIN_LIB:version_of(EC, Binary, dynamic, Decoders);
-version_of([{version3,prev3a},driver|EC], Binary) ->
- Decoders = [megaco_ber_bin_drv_media_gateway_control_v1,
- megaco_ber_bin_drv_media_gateway_control_v2,
- megaco_ber_bin_drv_media_gateway_control_prev3a],
- ?BIN_LIB:version_of(EC, Binary, dynamic, Decoders);
-version_of([driver|EC], Binary) ->
- Decoders = [megaco_ber_bin_drv_media_gateway_control_v1,
- megaco_ber_bin_drv_media_gateway_control_v2,
- megaco_ber_bin_drv_media_gateway_control_v3],
- ?BIN_LIB:version_of(EC, Binary, dynamic, Decoders);
version_of([{version3,v3}|EC], Binary) ->
- Decoders = [megaco_ber_bin_media_gateway_control_v1,
- megaco_ber_bin_media_gateway_control_v2,
- megaco_ber_bin_media_gateway_control_v3],
+ Decoders = [megaco_ber_media_gateway_control_v1,
+ megaco_ber_media_gateway_control_v2,
+ megaco_ber_media_gateway_control_v3],
?BIN_LIB:version_of(EC, Binary, dynamic, Decoders);
version_of([{version3,prev3c}|EC], Binary) ->
- Decoders = [megaco_ber_bin_media_gateway_control_v1,
- megaco_ber_bin_media_gateway_control_v2,
- megaco_ber_bin_media_gateway_control_prev3c],
+ Decoders = [megaco_ber_media_gateway_control_v1,
+ megaco_ber_media_gateway_control_v2,
+ megaco_ber_media_gateway_control_prev3c],
?BIN_LIB:version_of(EC, Binary, dynamic, Decoders);
version_of([{version3,prev3b}|EC], Binary) ->
- Decoders = [megaco_ber_bin_media_gateway_control_v1,
- megaco_ber_bin_media_gateway_control_v2,
- megaco_ber_bin_media_gateway_control_prev3b],
+ Decoders = [megaco_ber_media_gateway_control_v1,
+ megaco_ber_media_gateway_control_v2,
+ megaco_ber_media_gateway_control_prev3b],
?BIN_LIB:version_of(EC, Binary, dynamic, Decoders);
version_of([{version3,prev3a}|EC], Binary) ->
- Decoders = [megaco_ber_bin_media_gateway_control_v1,
- megaco_ber_bin_media_gateway_control_v2,
- megaco_ber_bin_media_gateway_control_prev3a],
+ Decoders = [megaco_ber_media_gateway_control_v1,
+ megaco_ber_media_gateway_control_v2,
+ megaco_ber_media_gateway_control_prev3a],
?BIN_LIB:version_of(EC, Binary, dynamic, Decoders);
version_of(EC, Binary) ->
- Decoders = [megaco_ber_bin_media_gateway_control_v1,
- megaco_ber_bin_media_gateway_control_v2,
- megaco_ber_bin_media_gateway_control_v3],
+ Decoders = [megaco_ber_media_gateway_control_v1,
+ megaco_ber_media_gateway_control_v2,
+ megaco_ber_media_gateway_control_v3],
?BIN_LIB:version_of(EC, Binary, dynamic, Decoders).
@@ -301,287 +276,153 @@ version_of(EC, Binary) ->
decode_message(EC, Binary) ->
decode_message(EC, 1, Binary).
-decode_message([{version3,v3},driver|EC], dynamic, Binary) ->
- Decoders = [{megaco_ber_bin_drv_media_gateway_control_v1,
- megaco_binary_transformer_v1},
- {megaco_ber_bin_drv_media_gateway_control_v2,
- megaco_binary_transformer_v2},
- {megaco_ber_bin_drv_media_gateway_control_v3,
- megaco_binary_transformer_v3}],
- ?BIN_LIB:decode_message_dynamic(EC, Binary, Decoders, binary);
-decode_message([{version3,prev3c},driver|EC], dynamic, Binary) ->
- Decoders = [{megaco_ber_bin_drv_media_gateway_control_v1,
- megaco_binary_transformer_v1},
- {megaco_ber_bin_drv_media_gateway_control_v2,
- megaco_binary_transformer_v2},
- {megaco_ber_bin_drv_media_gateway_control_prev3c,
- megaco_binary_transformer_prev3c}],
- ?BIN_LIB:decode_message_dynamic(EC, Binary, Decoders, binary);
-decode_message([{version3,prev3b},driver|EC], dynamic, Binary) ->
- Decoders = [{megaco_ber_bin_drv_media_gateway_control_v1,
- megaco_binary_transformer_v1},
- {megaco_ber_bin_drv_media_gateway_control_v2,
- megaco_binary_transformer_v2},
- {megaco_ber_bin_drv_media_gateway_control_prev3b,
- megaco_binary_transformer_prev3b}],
- ?BIN_LIB:decode_message_dynamic(EC, Binary, Decoders, binary);
-decode_message([{version3,prev3a},driver|EC], dynamic, Binary) ->
- Decoders = [{megaco_ber_bin_drv_media_gateway_control_v1,
- megaco_binary_transformer_v1},
- {megaco_ber_bin_drv_media_gateway_control_v2,
- megaco_binary_transformer_v2},
- {megaco_ber_bin_drv_media_gateway_control_prev3a,
- megaco_binary_transformer_prev3a}],
- ?BIN_LIB:decode_message_dynamic(EC, Binary, Decoders, binary);
-decode_message([driver|EC], dynamic, Binary) ->
- Decoders = [{megaco_ber_bin_drv_media_gateway_control_v1,
- megaco_binary_transformer_v1},
- {megaco_ber_bin_drv_media_gateway_control_v2,
- megaco_binary_transformer_v2},
- {megaco_ber_bin_drv_media_gateway_control_v3,
- megaco_binary_transformer_v3}],
- ?BIN_LIB:decode_message_dynamic(EC, Binary, Decoders, binary);
decode_message([{version3,v3}|EC], dynamic, Binary) ->
- Decoders = [{megaco_ber_bin_media_gateway_control_v1,
+ Decoders = [{megaco_ber_media_gateway_control_v1,
megaco_binary_transformer_v1},
- {megaco_ber_bin_media_gateway_control_v2,
+ {megaco_ber_media_gateway_control_v2,
megaco_binary_transformer_v2},
- {megaco_ber_bin_media_gateway_control_v3,
+ {megaco_ber_media_gateway_control_v3,
megaco_binary_transformer_v3}],
?BIN_LIB:decode_message_dynamic(EC, Binary, Decoders, binary);
decode_message([{version3,prev3c}|EC], dynamic, Binary) ->
- Decoders = [{megaco_ber_bin_media_gateway_control_v1,
+ Decoders = [{megaco_ber_media_gateway_control_v1,
megaco_binary_transformer_v1},
- {megaco_ber_bin_media_gateway_control_v2,
+ {megaco_ber_media_gateway_control_v2,
megaco_binary_transformer_v2},
- {megaco_ber_bin_media_gateway_control_prev3c,
+ {megaco_ber_media_gateway_control_prev3c,
megaco_binary_transformer_prev3c}],
?BIN_LIB:decode_message_dynamic(EC, Binary, Decoders, binary);
decode_message([{version3,prev3b}|EC], dynamic, Binary) ->
- Decoders = [{megaco_ber_bin_media_gateway_control_v1,
+ Decoders = [{megaco_ber_media_gateway_control_v1,
megaco_binary_transformer_v1},
- {megaco_ber_bin_media_gateway_control_v2,
+ {megaco_ber_media_gateway_control_v2,
megaco_binary_transformer_v2},
- {megaco_ber_bin_media_gateway_control_prev3b,
+ {megaco_ber_media_gateway_control_prev3b,
megaco_binary_transformer_prev3b}],
?BIN_LIB:decode_message_dynamic(EC, Binary, Decoders, binary);
decode_message([{version3,prev3a}|EC], dynamic, Binary) ->
- Decoders = [{megaco_ber_bin_media_gateway_control_v1,
+ Decoders = [{megaco_ber_media_gateway_control_v1,
megaco_binary_transformer_v1},
- {megaco_ber_bin_media_gateway_control_v2,
+ {megaco_ber_media_gateway_control_v2,
megaco_binary_transformer_v2},
- {megaco_ber_bin_media_gateway_control_prev3a,
+ {megaco_ber_media_gateway_control_prev3a,
megaco_binary_transformer_prev3a}],
?BIN_LIB:decode_message_dynamic(EC, Binary, Decoders, binary);
decode_message(EC, dynamic, Binary) ->
- Decoders = [{megaco_ber_bin_media_gateway_control_v1,
+ Decoders = [{megaco_ber_media_gateway_control_v1,
megaco_binary_transformer_v1},
- {megaco_ber_bin_media_gateway_control_v2,
+ {megaco_ber_media_gateway_control_v2,
megaco_binary_transformer_v2},
- {megaco_ber_bin_media_gateway_control_v3,
+ {megaco_ber_media_gateway_control_v3,
megaco_binary_transformer_v3}],
?BIN_LIB:decode_message_dynamic(EC, Binary, Decoders, binary);
%% -- Version 1 --
-decode_message([{version3,_},driver|EC], 1, Binary) ->
- AsnMod = megaco_ber_bin_drv_media_gateway_control_v1,
- TransMod = megaco_binary_transformer_v1,
- ?BIN_LIB:decode_message(EC, Binary, AsnMod, TransMod, binary);
-
-decode_message([driver|EC], 1, Binary) ->
- AsnMod = megaco_ber_bin_drv_media_gateway_control_v1,
- TransMod = megaco_binary_transformer_v1,
- ?BIN_LIB:decode_message(EC, Binary, AsnMod, TransMod, binary);
-
decode_message([{version3,_}|EC], 1, Binary) ->
- AsnMod = megaco_ber_bin_media_gateway_control_v1,
+ AsnMod = megaco_ber_media_gateway_control_v1,
TransMod = megaco_binary_transformer_v1,
?BIN_LIB:decode_message(EC, Binary, AsnMod, TransMod, binary);
decode_message(EC, 1, Binary) ->
- AsnMod = megaco_ber_bin_media_gateway_control_v1,
+ AsnMod = megaco_ber_media_gateway_control_v1,
TransMod = megaco_binary_transformer_v1,
?BIN_LIB:decode_message(EC, Binary, AsnMod, TransMod, binary);
%% -- Version 2 --
-decode_message([{version3,_},driver|EC], 2, Binary) ->
- AsnMod = megaco_ber_bin_drv_media_gateway_control_v2,
- TransMod = megaco_binary_transformer_v2,
- ?BIN_LIB:decode_message(EC, Binary, AsnMod, TransMod, binary);
-
-decode_message([driver|EC], 2, Binary) ->
- AsnMod = megaco_ber_bin_drv_media_gateway_control_v2,
- TransMod = megaco_binary_transformer_v2,
- ?BIN_LIB:decode_message(EC, Binary, AsnMod, TransMod, binary);
-
decode_message([{version3,_}|EC], 2, Binary) ->
- AsnMod = megaco_ber_bin_media_gateway_control_v2,
+ AsnMod = megaco_ber_media_gateway_control_v2,
TransMod = megaco_binary_transformer_v2,
?BIN_LIB:decode_message(EC, Binary, AsnMod, TransMod, binary);
decode_message(EC, 2, Binary) ->
- AsnMod = megaco_ber_bin_media_gateway_control_v2,
+ AsnMod = megaco_ber_media_gateway_control_v2,
TransMod = megaco_binary_transformer_v2,
?BIN_LIB:decode_message(EC, Binary, AsnMod, TransMod, binary);
%% -- Version 3 --
-decode_message([{version3,v3},driver|EC], 3, Binary) ->
- AsnMod = megaco_ber_bin_drv_media_gateway_control_v3,
- TransMod = megaco_binary_transformer_v3,
- ?BIN_LIB:decode_message(EC, Binary, AsnMod, TransMod, binary);
-decode_message([{version3,prev3c},driver|EC], 3, Binary) ->
- AsnMod = megaco_ber_bin_drv_media_gateway_control_prev3c,
- TransMod = megaco_binary_transformer_prev3c,
- ?BIN_LIB:decode_message(EC, Binary, AsnMod, TransMod, binary);
-decode_message([{version3,prev3b},driver|EC], 3, Binary) ->
- AsnMod = megaco_ber_bin_drv_media_gateway_control_prev3b,
- TransMod = megaco_binary_transformer_prev3b,
- ?BIN_LIB:decode_message(EC, Binary, AsnMod, TransMod, binary);
-decode_message([{version3,prev3a},driver|EC], 3, Binary) ->
- AsnMod = megaco_ber_bin_drv_media_gateway_control_prev3a,
- TransMod = megaco_binary_transformer_prev3a,
- ?BIN_LIB:decode_message(EC, Binary, AsnMod, TransMod, binary);
-
-decode_message([driver|EC], 3, Binary) ->
- AsnMod = megaco_ber_bin_drv_media_gateway_control_v3,
- TransMod = megaco_binary_transformer_v3,
- ?BIN_LIB:decode_message(EC, Binary, AsnMod, TransMod, binary);
-
decode_message([{version3,v3}|EC], 3, Binary) ->
- AsnMod = megaco_ber_bin_media_gateway_control_v3,
+ AsnMod = megaco_ber_media_gateway_control_v3,
TransMod = megaco_binary_transformer_v3,
?BIN_LIB:decode_message(EC, Binary, AsnMod, TransMod, binary);
decode_message([{version3,prev3c}|EC], 3, Binary) ->
- AsnMod = megaco_ber_bin_media_gateway_control_prev3c,
+ AsnMod = megaco_ber_media_gateway_control_prev3c,
TransMod = megaco_binary_transformer_prev3c,
?BIN_LIB:decode_message(EC, Binary, AsnMod, TransMod, binary);
decode_message([{version3,prev3b}|EC], 3, Binary) ->
- AsnMod = megaco_ber_bin_media_gateway_control_prev3b,
+ AsnMod = megaco_ber_media_gateway_control_prev3b,
TransMod = megaco_binary_transformer_prev3b,
?BIN_LIB:decode_message(EC, Binary, AsnMod, TransMod, binary);
decode_message([{version3,prev3a}|EC], 3, Binary) ->
- AsnMod = megaco_ber_bin_media_gateway_control_prev3a,
+ AsnMod = megaco_ber_media_gateway_control_prev3a,
TransMod = megaco_binary_transformer_prev3a,
?BIN_LIB:decode_message(EC, Binary, AsnMod, TransMod, binary);
decode_message(EC, 3, Binary) ->
- AsnMod = megaco_ber_bin_media_gateway_control_v3,
+ AsnMod = megaco_ber_media_gateway_control_v3,
TransMod = megaco_binary_transformer_v3,
?BIN_LIB:decode_message(EC, Binary, AsnMod, TransMod, binary).
-decode_mini_message([{version3,v3},driver|EC], dynamic, Bin) ->
- Mods = [megaco_ber_bin_drv_media_gateway_control_v1,
- megaco_ber_bin_drv_media_gateway_control_v2,
- megaco_ber_bin_drv_media_gateway_control_v3],
- ?BIN_LIB:decode_mini_message_dynamic(EC, Bin, Mods, binary);
-decode_mini_message([{version3,prev3c},driver|EC], dynamic, Bin) ->
- Mods = [megaco_ber_bin_drv_media_gateway_control_v1,
- megaco_ber_bin_drv_media_gateway_control_v2,
- megaco_ber_bin_drv_media_gateway_control_prev3c],
- ?BIN_LIB:decode_mini_message_dynamic(EC, Bin, Mods, binary);
-decode_mini_message([{version3,prev3b},driver|EC], dynamic, Bin) ->
- Mods = [megaco_ber_bin_drv_media_gateway_control_v1,
- megaco_ber_bin_drv_media_gateway_control_v2,
- megaco_ber_bin_drv_media_gateway_control_prev3b],
- ?BIN_LIB:decode_mini_message_dynamic(EC, Bin, Mods, binary);
-decode_mini_message([{version3,prev3a},driver|EC], dynamic, Bin) ->
- Mods = [megaco_ber_bin_drv_media_gateway_control_v1,
- megaco_ber_bin_drv_media_gateway_control_v2,
- megaco_ber_bin_drv_media_gateway_control_prev3a],
- ?BIN_LIB:decode_mini_message_dynamic(EC, Bin, Mods, binary);
-decode_mini_message([driver|EC], dynamic, Bin) ->
- Mods = [megaco_ber_bin_drv_media_gateway_control_v1,
- megaco_ber_bin_drv_media_gateway_control_v2,
- megaco_ber_bin_drv_media_gateway_control_v3],
- ?BIN_LIB:decode_mini_message_dynamic(EC, Bin, Mods, binary);
decode_mini_message([{version3,v3}|EC], dynamic, Bin) ->
- Mods = [megaco_ber_bin_media_gateway_control_v1,
- megaco_ber_bin_media_gateway_control_v2,
- megaco_ber_bin_media_gateway_control_v3],
+ Mods = [megaco_ber_media_gateway_control_v1,
+ megaco_ber_media_gateway_control_v2,
+ megaco_ber_media_gateway_control_v3],
?BIN_LIB:decode_mini_message_dynamic(EC, Bin, Mods, binary);
decode_mini_message([{version3,prev3c}|EC], dynamic, Bin) ->
- Mods = [megaco_ber_bin_media_gateway_control_v1,
- megaco_ber_bin_media_gateway_control_v2,
- megaco_ber_bin_media_gateway_control_prev3c],
+ Mods = [megaco_ber_media_gateway_control_v1,
+ megaco_ber_media_gateway_control_v2,
+ megaco_ber_media_gateway_control_prev3c],
?BIN_LIB:decode_mini_message_dynamic(EC, Bin, Mods, binary);
decode_mini_message([{version3,prev3b}|EC], dynamic, Bin) ->
- Mods = [megaco_ber_bin_media_gateway_control_v1,
- megaco_ber_bin_media_gateway_control_v2,
- megaco_ber_bin_media_gateway_control_prev3b],
+ Mods = [megaco_ber_media_gateway_control_v1,
+ megaco_ber_media_gateway_control_v2,
+ megaco_ber_media_gateway_control_prev3b],
?BIN_LIB:decode_mini_message_dynamic(EC, Bin, Mods, binary);
decode_mini_message([{version3,prev3a}|EC], dynamic, Bin) ->
- Mods = [megaco_ber_bin_media_gateway_control_v1,
- megaco_ber_bin_media_gateway_control_v2,
- megaco_ber_bin_media_gateway_control_prev3a],
+ Mods = [megaco_ber_media_gateway_control_v1,
+ megaco_ber_media_gateway_control_v2,
+ megaco_ber_media_gateway_control_prev3a],
?BIN_LIB:decode_mini_message_dynamic(EC, Bin, Mods, binary);
decode_mini_message(EC, dynamic, Bin) ->
- Mods = [megaco_ber_bin_media_gateway_control_v1,
- megaco_ber_bin_media_gateway_control_v2,
- megaco_ber_bin_media_gateway_control_v3],
+ Mods = [megaco_ber_media_gateway_control_v1,
+ megaco_ber_media_gateway_control_v2,
+ megaco_ber_media_gateway_control_v3],
?BIN_LIB:decode_mini_message_dynamic(EC, Bin, Mods, binary);
-decode_mini_message([{version3,_},driver|EC], 1, Bin) ->
- AsnMod = megaco_ber_bin_drv_media_gateway_control_v1,
- ?BIN_LIB:decode_mini_message(EC, Bin, AsnMod, binary);
-decode_mini_message([driver|EC], 1, Bin) ->
- AsnMod = megaco_ber_bin_drv_media_gateway_control_v1,
- ?BIN_LIB:decode_mini_message(EC, Bin, AsnMod, binary);
decode_mini_message([{version3,_}|EC], 1, Bin) ->
- AsnMod = megaco_ber_bin_media_gateway_control_v1,
+ AsnMod = megaco_ber_media_gateway_control_v1,
?BIN_LIB:decode_mini_message(EC, Bin, AsnMod, binary);
decode_mini_message(EC, 1, Bin) ->
- AsnMod = megaco_ber_bin_media_gateway_control_v1,
+ AsnMod = megaco_ber_media_gateway_control_v1,
?BIN_LIB:decode_mini_message(EC, Bin, AsnMod, binary);
-decode_mini_message([{version3,_},driver|EC], 2, Bin) ->
- AsnMod = megaco_ber_bin_drv_media_gateway_control_v2,
- ?BIN_LIB:decode_mini_message(EC, Bin, AsnMod, binary);
-decode_mini_message([driver|EC], 2, Bin) ->
- AsnMod = megaco_ber_bin_drv_media_gateway_control_v2,
- ?BIN_LIB:decode_mini_message(EC, Bin, AsnMod, binary);
decode_mini_message([{version3,_}|EC], 2, Bin) ->
- AsnMod = megaco_ber_bin_media_gateway_control_v2,
+ AsnMod = megaco_ber_media_gateway_control_v2,
?BIN_LIB:decode_mini_message(EC, Bin, AsnMod, binary);
decode_mini_message(EC, 2, Bin) ->
- AsnMod = megaco_ber_bin_media_gateway_control_v2,
+ AsnMod = megaco_ber_media_gateway_control_v2,
?BIN_LIB:decode_mini_message(EC, Bin, AsnMod, binary);
-decode_mini_message([{version3,v3},driver|EC], 3, Bin) ->
- AsnMod = megaco_ber_bin_drv_media_gateway_control_v3,
- ?BIN_LIB:decode_mini_message(EC, Bin, AsnMod, binary);
-decode_mini_message([{version3,prev3c},driver|EC], 3, Bin) ->
- AsnMod = megaco_ber_bin_drv_media_gateway_control_prev3c,
- ?BIN_LIB:decode_mini_message(EC, Bin, AsnMod, binary);
-decode_mini_message([{version3,prev3b},driver|EC], 3, Bin) ->
- AsnMod = megaco_ber_bin_drv_media_gateway_control_prev3b,
- ?BIN_LIB:decode_mini_message(EC, Bin, AsnMod, binary);
-decode_mini_message([{version3,prev3a},driver|EC], 3, Bin) ->
- AsnMod = megaco_ber_bin_drv_media_gateway_control_prev3a,
- ?BIN_LIB:decode_mini_message(EC, Bin, AsnMod, binary);
-decode_mini_message([driver|EC], 3, Bin) ->
- AsnMod = megaco_ber_bin_drv_media_gateway_control_v3,
- ?BIN_LIB:decode_mini_message(EC, Bin, AsnMod, binary);
decode_mini_message([{version3,v3}|EC], 3, Bin) ->
- AsnMod = megaco_ber_bin_media_gateway_control_v3,
+ AsnMod = megaco_ber_media_gateway_control_v3,
?BIN_LIB:decode_mini_message(EC, Bin, AsnMod, binary);
decode_mini_message([{version3,prev3c}|EC], 3, Bin) ->
- AsnMod = megaco_ber_bin_media_gateway_control_prev3c,
+ AsnMod = megaco_ber_media_gateway_control_prev3c,
?BIN_LIB:decode_mini_message(EC, Bin, AsnMod, binary);
decode_mini_message([{version3,prev3b}|EC], 3, Bin) ->
- AsnMod = megaco_ber_bin_media_gateway_control_prev3b,
+ AsnMod = megaco_ber_media_gateway_control_prev3b,
?BIN_LIB:decode_mini_message(EC, Bin, AsnMod, binary);
decode_mini_message([{version3,prev3a}|EC], 3, Bin) ->
- AsnMod = megaco_ber_bin_media_gateway_control_prev3a,
+ AsnMod = megaco_ber_media_gateway_control_prev3a,
?BIN_LIB:decode_mini_message(EC, Bin, AsnMod, binary);
decode_mini_message(EC, 3, Bin) ->
- AsnMod = megaco_ber_bin_media_gateway_control_v3,
+ AsnMod = megaco_ber_media_gateway_control_v3,
?BIN_LIB:decode_mini_message(EC, Bin, AsnMod, binary).
diff --git a/lib/megaco/src/binary/megaco_binary_encoder_lib.erl b/lib/megaco/src/binary/megaco_binary_encoder_lib.erl
index 967ee93935..262889db39 100644
--- a/lib/megaco/src/binary/megaco_binary_encoder_lib.erl
+++ b/lib/megaco/src/binary/megaco_binary_encoder_lib.erl
@@ -275,7 +275,7 @@ decode_message_dynamic(_EC, _BadBin, _Mods, _Type) ->
{error, no_binary}.
-decode_message(EC, Bin, AsnMod, TransMod, binary) ->
+decode_message(EC, Bin, AsnMod, TransMod, _) ->
case asn1rt:decode(AsnMod, 'MegacoMessage', Bin) of
{ok, MegaMsg} ->
case EC of
@@ -286,19 +286,6 @@ decode_message(EC, Bin, AsnMod, TransMod, binary) ->
end;
{error, Reason} ->
{error, Reason}
- end;
-decode_message(EC, Bin, AsnMod, TransMod, io_list) ->
- ShallowIoList = erlang:binary_to_list(Bin),
- case asn1rt:decode(AsnMod, 'MegacoMessage', ShallowIoList) of
- {ok, MegaMsg} ->
- case EC of
- [native] ->
- {ok, MegaMsg};
- _ ->
- {ok, TransMod:tr_message(MegaMsg, decode, EC)}
- end;
- {error, Reason} ->
- {error, Reason}
end.
diff --git a/lib/megaco/src/binary/megaco_binary_name_resolver_prev3a.erl b/lib/megaco/src/binary/megaco_binary_name_resolver_prev3a.erl
index 72b3112053..24dc410dbe 100644
--- a/lib/megaco/src/binary/megaco_binary_name_resolver_prev3a.erl
+++ b/lib/megaco/src/binary/megaco_binary_name_resolver_prev3a.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -204,7 +204,7 @@ decode_name(_Config, Scope, Item) ->
%% to command requests from the MGC that audit
%% ObservedEventsDescriptor, and found in the
%% ObservedEventsDescriptor. See 12.2. If there are no parameters
-%% for the ObservedEvents Descriptor, then �none� shall be specified.
+%% for the ObservedEvents Descriptor, then 'none' shall be specified.
%%
%%
%% 12.1.4 Signals
@@ -225,7 +225,7 @@ decode_name(_Config, Scope, Item) ->
%%
%% BR (Brief)
%%
-%% NOTE -�SignalType may be defined such that it is dependent on
+%% NOTE - SignalType may be defined such that it is dependent on
%% the value of one or more parameters. The package MUST specify a
%% default signal type. If the default type is TO, the package MUST
%% specify a default duration which may be provisioned. A default
diff --git a/lib/megaco/src/binary/megaco_binary_name_resolver_prev3b.erl b/lib/megaco/src/binary/megaco_binary_name_resolver_prev3b.erl
index 12e673ac81..d89717c00a 100644
--- a/lib/megaco/src/binary/megaco_binary_name_resolver_prev3b.erl
+++ b/lib/megaco/src/binary/megaco_binary_name_resolver_prev3b.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -204,7 +204,7 @@ decode_name(_Config, Scope, Item) ->
%% to command requests from the MGC that audit
%% ObservedEventsDescriptor, and found in the
%% ObservedEventsDescriptor. See 12.2. If there are no parameters
-%% for the ObservedEvents Descriptor, then �none� shall be specified.
+%% for the ObservedEvents Descriptor, then 'none' shall be specified.
%%
%%
%% 12.1.4 Signals
@@ -225,7 +225,7 @@ decode_name(_Config, Scope, Item) ->
%%
%% BR (Brief)
%%
-%% NOTE -�SignalType may be defined such that it is dependent on
+%% NOTE - SignalType may be defined such that it is dependent on
%% the value of one or more parameters. The package MUST specify a
%% default signal type. If the default type is TO, the package MUST
%% specify a default duration which may be provisioned. A default
diff --git a/lib/megaco/src/binary/megaco_binary_name_resolver_prev3c.erl b/lib/megaco/src/binary/megaco_binary_name_resolver_prev3c.erl
index d08231caac..8b4192ad44 100644
--- a/lib/megaco/src/binary/megaco_binary_name_resolver_prev3c.erl
+++ b/lib/megaco/src/binary/megaco_binary_name_resolver_prev3c.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -204,7 +204,7 @@ decode_name(_Config, Scope, Item) ->
%% to command requests from the MGC that audit
%% ObservedEventsDescriptor, and found in the
%% ObservedEventsDescriptor. See 12.2. If there are no parameters
-%% for the ObservedEvents Descriptor, then �none� shall be specified.
+%% for the ObservedEvents Descriptor, then 'none' shall be specified.
%%
%%
%% 12.1.4 Signals
@@ -225,7 +225,7 @@ decode_name(_Config, Scope, Item) ->
%%
%% BR (Brief)
%%
-%% NOTE -�SignalType may be defined such that it is dependent on
+%% NOTE - SignalType may be defined such that it is dependent on
%% the value of one or more parameters. The package MUST specify a
%% default signal type. If the default type is TO, the package MUST
%% specify a default duration which may be provisioned. A default
diff --git a/lib/megaco/src/binary/megaco_binary_name_resolver_v3.erl b/lib/megaco/src/binary/megaco_binary_name_resolver_v3.erl
index c101aa15bc..20954d4c9d 100644
--- a/lib/megaco/src/binary/megaco_binary_name_resolver_v3.erl
+++ b/lib/megaco/src/binary/megaco_binary_name_resolver_v3.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -204,7 +204,7 @@ decode_name(_Config, Scope, Item) ->
%% to command requests from the MGC that audit
%% ObservedEventsDescriptor, and found in the
%% ObservedEventsDescriptor. See 12.2. If there are no parameters
-%% for the ObservedEvents Descriptor, then �none� shall be specified.
+%% for the ObservedEvents Descriptor, then 'none' shall be specified.
%%
%%
%% 12.1.4 Signals
@@ -225,7 +225,7 @@ decode_name(_Config, Scope, Item) ->
%%
%% BR (Brief)
%%
-%% NOTE -�SignalType may be defined such that it is dependent on
+%% NOTE - SignalType may be defined such that it is dependent on
%% the value of one or more parameters. The package MUST specify a
%% default signal type. If the default type is TO, the package MUST
%% specify a default duration which may be provisioned. A default
diff --git a/lib/megaco/src/binary/megaco_per_bin_drv_media_gateway_control_prev3a.set.asn b/lib/megaco/src/binary/megaco_per_bin_drv_media_gateway_control_prev3a.set.asn
deleted file mode 100644
index b9ba7ffdb4..0000000000
--- a/lib/megaco/src/binary/megaco_per_bin_drv_media_gateway_control_prev3a.set.asn
+++ /dev/null
@@ -1 +0,0 @@
-MEDIA-GATEWAY-CONTROL-prev3a.asn
diff --git a/lib/megaco/src/binary/megaco_per_bin_drv_media_gateway_control_prev3b.set.asn b/lib/megaco/src/binary/megaco_per_bin_drv_media_gateway_control_prev3b.set.asn
deleted file mode 100644
index 0437bde310..0000000000
--- a/lib/megaco/src/binary/megaco_per_bin_drv_media_gateway_control_prev3b.set.asn
+++ /dev/null
@@ -1 +0,0 @@
-MEDIA-GATEWAY-CONTROL-prev3b.asn
diff --git a/lib/megaco/src/binary/megaco_per_bin_drv_media_gateway_control_prev3c.set.asn b/lib/megaco/src/binary/megaco_per_bin_drv_media_gateway_control_prev3c.set.asn
deleted file mode 100644
index e78055fbad..0000000000
--- a/lib/megaco/src/binary/megaco_per_bin_drv_media_gateway_control_prev3c.set.asn
+++ /dev/null
@@ -1 +0,0 @@
-MEDIA-GATEWAY-CONTROL-prev3c.asn
diff --git a/lib/megaco/src/binary/megaco_per_bin_drv_media_gateway_control_v1.set.asn b/lib/megaco/src/binary/megaco_per_bin_drv_media_gateway_control_v1.set.asn
deleted file mode 100644
index 0f5a92dba1..0000000000
--- a/lib/megaco/src/binary/megaco_per_bin_drv_media_gateway_control_v1.set.asn
+++ /dev/null
@@ -1 +0,0 @@
-MEDIA-GATEWAY-CONTROL-v1.asn
diff --git a/lib/megaco/src/binary/megaco_per_bin_drv_media_gateway_control_v2.set.asn b/lib/megaco/src/binary/megaco_per_bin_drv_media_gateway_control_v2.set.asn
deleted file mode 100644
index 7fc82b127f..0000000000
--- a/lib/megaco/src/binary/megaco_per_bin_drv_media_gateway_control_v2.set.asn
+++ /dev/null
@@ -1 +0,0 @@
-MEDIA-GATEWAY-CONTROL-v2.asn
diff --git a/lib/megaco/src/binary/megaco_per_bin_drv_media_gateway_control_v3.set.asn b/lib/megaco/src/binary/megaco_per_bin_drv_media_gateway_control_v3.set.asn
deleted file mode 100644
index 1d7950a283..0000000000
--- a/lib/megaco/src/binary/megaco_per_bin_drv_media_gateway_control_v3.set.asn
+++ /dev/null
@@ -1 +0,0 @@
-MEDIA-GATEWAY-CONTROL-v3.asn
diff --git a/lib/megaco/src/binary/megaco_per_bin_encoder.erl b/lib/megaco/src/binary/megaco_per_bin_encoder.erl
deleted file mode 100644
index f7280f4e04..0000000000
--- a/lib/megaco/src/binary/megaco_per_bin_encoder.erl
+++ /dev/null
@@ -1,447 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2001-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 : Handle ASN.1 PER encoding of Megaco/H.248
-%%----------------------------------------------------------------------
-
--module(megaco_per_bin_encoder).
-
--behaviour(megaco_encoder).
-
--export([encode_message/3, decode_message/3,
- decode_mini_message/3,
-
- encode_transaction/3,
- encode_action_requests/3,
- encode_action_request/3,
- encode_action_reply/3,
-
- version_of/2]).
-
-%% Backward compatible functions:
--export([encode_message/2, decode_message/2]).
-
--include_lib("megaco/src/engine/megaco_message_internal.hrl").
-
--define(V1_ASN1_MOD, megaco_per_bin_media_gateway_control_v1).
--define(V2_ASN1_MOD, megaco_per_bin_media_gateway_control_v2).
--define(V3_ASN1_MOD, megaco_per_bin_media_gateway_control_v3).
--define(PREV3A_ASN1_MOD, megaco_per_bin_media_gateway_control_prev3a).
--define(PREV3B_ASN1_MOD, megaco_per_bin_media_gateway_control_prev3b).
--define(PREV3C_ASN1_MOD, megaco_per_bin_media_gateway_control_prev3c).
--define(V1_ASN1_MOD_DRV, megaco_per_bin_drv_media_gateway_control_v1).
--define(V2_ASN1_MOD_DRV, megaco_per_bin_drv_media_gateway_control_v2).
--define(V3_ASN1_MOD_DRV, megaco_per_bin_drv_media_gateway_control_v3).
--define(PREV3A_ASN1_MOD_DRV, megaco_per_bin_drv_media_gateway_control_prev3a).
--define(PREV3B_ASN1_MOD_DRV, megaco_per_bin_drv_media_gateway_control_prev3b).
--define(PREV3C_ASN1_MOD_DRV, megaco_per_bin_drv_media_gateway_control_prev3c).
-
--define(V1_TRANS_MOD, megaco_binary_transformer_v1).
--define(V2_TRANS_MOD, megaco_binary_transformer_v2).
--define(V3_TRANS_MOD, megaco_binary_transformer_v3).
--define(PREV3A_TRANS_MOD, megaco_binary_transformer_prev3a).
--define(PREV3B_TRANS_MOD, megaco_binary_transformer_prev3b).
--define(PREV3C_TRANS_MOD, megaco_binary_transformer_prev3c).
-
--define(BIN_LIB, megaco_binary_encoder_lib).
-
-
-%%----------------------------------------------------------------------
-%% Detect (check/get) message version
-%% Return {ok, Version} | {error, Reason}
-%%----------------------------------------------------------------------
-
-version_of([{version3,v3},driver|EC], Binary) ->
- Decoders = [?V1_ASN1_MOD_DRV, ?V2_ASN1_MOD_DRV, ?V3_ASN1_MOD_DRV],
- ?BIN_LIB:version_of(EC, Binary, 1, Decoders);
-version_of([{version3,prev3c},driver|EC], Binary) ->
- Decoders = [?V1_ASN1_MOD_DRV, ?V2_ASN1_MOD_DRV, ?PREV3C_ASN1_MOD_DRV],
- ?BIN_LIB:version_of(EC, Binary, 1, Decoders);
-version_of([{version3,prev3b},driver|EC], Binary) ->
- Decoders = [?V1_ASN1_MOD_DRV, ?V2_ASN1_MOD_DRV, ?PREV3B_ASN1_MOD_DRV],
- ?BIN_LIB:version_of(EC, Binary, 1, Decoders);
-version_of([{version3,prev3a},driver|EC], Binary) ->
- Decoders = [?V1_ASN1_MOD_DRV, ?V2_ASN1_MOD_DRV, ?PREV3A_ASN1_MOD_DRV],
- ?BIN_LIB:version_of(EC, Binary, 1, Decoders);
-version_of([driver|EC], Binary) ->
- Decoders = [?V1_ASN1_MOD_DRV, ?V2_ASN1_MOD_DRV, ?V3_ASN1_MOD_DRV],
- ?BIN_LIB:version_of(EC, Binary, 1, Decoders);
-version_of([{version3,v3}|EC], Binary) ->
- Decoders = [?V1_ASN1_MOD, ?V2_ASN1_MOD, ?V3_ASN1_MOD],
- ?BIN_LIB:version_of(EC, Binary, 1, Decoders);
-version_of([{version3,prev3c}|EC], Binary) ->
- Decoders = [?V1_ASN1_MOD, ?V2_ASN1_MOD, ?PREV3C_ASN1_MOD],
- ?BIN_LIB:version_of(EC, Binary, 1, Decoders);
-version_of([{version3,prev3b}|EC], Binary) ->
- Decoders = [?V1_ASN1_MOD, ?V2_ASN1_MOD, ?PREV3B_ASN1_MOD],
- ?BIN_LIB:version_of(EC, Binary, 1, Decoders);
-version_of([{version3,prev3a}|EC], Binary) ->
- Decoders = [?V1_ASN1_MOD, ?V2_ASN1_MOD, ?PREV3A_ASN1_MOD],
- ?BIN_LIB:version_of(EC, Binary, 1, Decoders);
-
-%% All values we need to take (special) care of has been delt with,
-%% so just pass the rest on
-version_of(EC, Binary) ->
- Decoders = [?V1_ASN1_MOD, ?V2_ASN1_MOD, ?V3_ASN1_MOD],
- ?BIN_LIB:version_of(EC, Binary, 1, Decoders).
-
-
-%%----------------------------------------------------------------------
-%% Convert a 'MegacoMessage' record into a binary
-%% Return {ok, Binary} | {error, Reason}
-%%----------------------------------------------------------------------
-
-encode_message(EC,
- #'MegacoMessage'{mess = #'Message'{version = V}} = MegaMsg) ->
- encode_message(EC, V, MegaMsg).
-
-
-%% -- Version 1 --
-
-encode_message([{version3, _},driver|EC], 1, MegaMsg) ->
- AsnMod = ?V1_ASN1_MOD_DRV,
- TransMod = ?V1_TRANS_MOD,
- ?BIN_LIB:encode_message(EC, MegaMsg, AsnMod, TransMod, io_list);
-encode_message([driver|EC], 1, MegaMsg) ->
- AsnMod = ?V1_ASN1_MOD_DRV,
- TransMod = ?V1_TRANS_MOD,
- ?BIN_LIB:encode_message(EC, MegaMsg, AsnMod, TransMod, io_list);
-encode_message([{version3,_}|EC], 1, MegaMsg) ->
- AsnMod = ?V1_ASN1_MOD,
- TransMod = ?V1_TRANS_MOD,
- ?BIN_LIB:encode_message(EC, MegaMsg, AsnMod, TransMod, io_list);
-
-%% All values we need to take (special) care of has been delt with,
-%% so just pass the rest on
-encode_message(EC, 1, MegaMsg) ->
- AsnMod = ?V1_ASN1_MOD,
- TransMod = ?V1_TRANS_MOD,
- ?BIN_LIB:encode_message(EC, MegaMsg, AsnMod, TransMod, io_list);
-
-
-%% -- Version 2 --
-
-encode_message([{version3,_},driver|EC], 2, MegaMsg) ->
- AsnMod = ?V2_ASN1_MOD_DRV,
- TransMod = ?V2_TRANS_MOD,
- ?BIN_LIB:encode_message(EC, MegaMsg, AsnMod, TransMod, io_list);
-encode_message([driver|EC], 2, MegaMsg) ->
- AsnMod = ?V2_ASN1_MOD_DRV,
- TransMod = ?V2_TRANS_MOD,
- ?BIN_LIB:encode_message(EC, MegaMsg, AsnMod, TransMod, io_list);
-encode_message([{version3,_}|EC], 2, MegaMsg) ->
- AsnMod = ?V2_ASN1_MOD,
- TransMod = ?V2_TRANS_MOD,
- ?BIN_LIB:encode_message(EC, MegaMsg, AsnMod, TransMod, io_list);
-
-%% All values we need to take (special) care of has been delt with,
-%% so just pass the rest on
-encode_message(EC, 2, MegaMsg) ->
- AsnMod = ?V2_ASN1_MOD,
- TransMod = ?V2_TRANS_MOD,
- ?BIN_LIB:encode_message(EC, MegaMsg, AsnMod, TransMod, io_list);
-
-
-%% -- Version 3 --
-
-encode_message([{version3,v3},driver|EC], 3, MegaMsg) ->
- AsnMod = ?V3_ASN1_MOD_DRV,
- TransMod = ?V3_TRANS_MOD,
- ?BIN_LIB:encode_message(EC, MegaMsg, AsnMod, TransMod, io_list);
-encode_message([{version3,prev3c},driver|EC], 3, MegaMsg) ->
- AsnMod = ?PREV3C_ASN1_MOD_DRV,
- TransMod = ?PREV3C_TRANS_MOD,
- ?BIN_LIB:encode_message(EC, MegaMsg, AsnMod, TransMod, io_list);
-encode_message([{version3,prev3b},driver|EC], 3, MegaMsg) ->
- AsnMod = ?PREV3B_ASN1_MOD_DRV,
- TransMod = ?PREV3B_TRANS_MOD,
- ?BIN_LIB:encode_message(EC, MegaMsg, AsnMod, TransMod, io_list);
-encode_message([{version3,prev3a},driver|EC], 3, MegaMsg) ->
- AsnMod = ?PREV3A_ASN1_MOD_DRV,
- TransMod = ?PREV3A_TRANS_MOD,
- ?BIN_LIB:encode_message(EC, MegaMsg, AsnMod, TransMod, io_list);
-encode_message([driver|EC], 3, MegaMsg) ->
- AsnMod = ?V3_ASN1_MOD_DRV,
- TransMod = ?V3_TRANS_MOD,
- ?BIN_LIB:encode_message(EC, MegaMsg, AsnMod, TransMod, io_list);
-encode_message([{version3,v3}|EC], 3, MegaMsg) ->
- AsnMod = ?V3_ASN1_MOD,
- TransMod = ?V3_TRANS_MOD,
- ?BIN_LIB:encode_message(EC, MegaMsg, AsnMod, TransMod, io_list);
-encode_message([{version3,prev3c}|EC], 3, MegaMsg) ->
- AsnMod = ?PREV3C_ASN1_MOD,
- TransMod = ?PREV3C_TRANS_MOD,
- ?BIN_LIB:encode_message(EC, MegaMsg, AsnMod, TransMod, io_list);
-encode_message([{version3,prev3b}|EC], 3, MegaMsg) ->
- AsnMod = ?PREV3B_ASN1_MOD,
- TransMod = ?PREV3B_TRANS_MOD,
- ?BIN_LIB:encode_message(EC, MegaMsg, AsnMod, TransMod, io_list);
-encode_message([{version3,prev3a}|EC], 3, MegaMsg) ->
- AsnMod = ?PREV3A_ASN1_MOD,
- TransMod = ?PREV3A_TRANS_MOD,
- ?BIN_LIB:encode_message(EC, MegaMsg, AsnMod, TransMod, io_list);
-
-%% All values we need to take (special) care of has been delt with,
-%% so just pass the rest on
-encode_message(EC, 3, MegaMsg) ->
- AsnMod = ?V3_ASN1_MOD,
- TransMod = ?V3_TRANS_MOD,
- ?BIN_LIB:encode_message(EC, MegaMsg, AsnMod, TransMod, io_list).
-
-
-%%----------------------------------------------------------------------
-%% Convert a transaction (or transactions in the case of ack) record(s)
-%% into a binary
-%% Return {ok, Binary} | {error, Reason}
-%%----------------------------------------------------------------------
-
-%% encode_transaction([] = EC, 1, Trans) ->
-%% AsnMod = ?V1_ASN1_MOD,
-%% TransMod = ?V1_TRANS_MOD,
-%% ?BIN_LIB:encode_transaction(EC, Trans, AsnMod, TransMod,
-%% io_list);
-%% encode_transaction([native] = EC, 1, Trans) ->
-%% AsnMod = ?V1_ASN1_MOD,
-%% TransMod = ?V1_TRANS_MOD,
-%% ?BIN_LIB:encode_transaction(EC, Trans, AsnMod, TransMod,
-%% io_list);
-%% encode_transaction([driver|EC], 1, Trans) ->
-%% AsnMod = ?V1_ASN1_MOD_DRV,
-%% TransMod = ?V1_TRANS_MOD,
-%% ?BIN_LIB:encode_transaction(EC, Trans, AsnMod, TransMod,
-%% io_list);
-encode_transaction(_EC, 1, _Trans) ->
- %% AsnMod = ?V1_ASN1_MOD,
- %% TransMod = ?V1_TRANS_MOD,
- %% ?BIN_LIB:encode_transaction(EC, Trans, AsnMod, TransMod,
- %% io_list);
- {error, not_implemented};
-
-%% encode_transaction([] = EC, 2, Trans) ->
-%% AsnMod = ?V2_ASN1_MOD,
-%% TransMod = ?V2_TRANS_MOD,
-%% ?BIN_LIB:encode_transaction(EC, Trans, AsnMod, TransMod,
-%% io_list);
-%% encode_transaction([native] = EC, 2, Trans) ->
-%% AsnMod = ?V2_ASN1_MOD,
-%% TransMod = ?V2_TRANS_MOD,
-%% ?BIN_LIB:encode_transaction(EC, Trans, AsnMod, TransMod,
-%% io_list);
-%% encode_transaction([driver|EC], 2, Trans) ->
-%% AsnMod = ?V2_ASN1_MOD_DRV,
-%% TransMod = ?V2_TRANS_MOD,
-%% ?BIN_LIB:encode_transaction(EC, Trans, AsnMod, TransMod,
-%% io_list);
-encode_transaction(_EC, 2, _Trans) ->
- %% AsnMod = ?V2_ASN1_MOD,
- %% TransMod = ?V2_TRANS_MOD,
- %% ?BIN_LIB:encode_transaction(EC, Trans, AsnMod, TransMod,
- %% io_list).
- {error, not_implemented};
-
-%% encode_transaction([] = EC, 3, Trans) ->
-%% AsnMod = ?V3_ASN1_MOD,
-%% TransMod = ?V3_TRANS_MOD,
-%% ?BIN_LIB:encode_transaction(EC, Trans, AsnMod, TransMod,
-%% io_list);
-%% encode_transaction([native] = EC, 3, Trans) ->
-%% AsnMod = ?V3_ASN1_MOD,
-%% TransMod = ?V3_TRANS_MOD,
-%% ?BIN_LIB:encode_transaction(EC, Trans, AsnMod, TransMod,
-%% io_list);
-%% encode_transaction([driver|EC], 3, Trans) ->
-%% AsnMod = ?V3_ASN1_MOD_DRV,
-%% TransMod = ?V3_TRANS_MOD,
-%% ?BIN_LIB:encode_transaction(EC, Trans, AsnMod, TransMod,
-%% io_list);
-encode_transaction(_EC, 3, _Trans) ->
- %% AsnMod = ?V3_ASN1_MOD,
- %% TransMod = ?V3_TRANS_MOD,
- %% ?BIN_LIB:encode_transaction(EC, Trans, AsnMod, TransMod, %% io_list).
- {error, not_implemented}.
-
-
-%%----------------------------------------------------------------------
-%% Convert a list of ActionRequest record's into a binary
-%% Return {ok, DeepIoList} | {error, Reason}
-%%----------------------------------------------------------------------
-encode_action_requests(_EC, 1, ActReqs) when is_list(ActReqs) ->
- %% ?BIN_LIB:encode_action_requests(EC, ActReqs,
- %% ?V1_ASN1_MOD,
- %% ?V1_TRANS_MOD,
- %% io_list);
- {error, not_implemented};
-encode_action_requests(_EC, 2, ActReqs) when is_list(ActReqs) ->
- %% ?BIN_LIB:encode_action_requests(EC, ActReqs,
- %% ?V1_ASN1_MOD,
- %% ?V1_TRANS_MOD,
- %% io_list).
- {error, not_implemented};
-encode_action_requests(_EC, 3, ActReqs) when is_list(ActReqs) ->
- %% ?BIN_LIB:encode_action_requests(EC, ActReqs,
- %% ?V1_ASN1_MOD,
- %% ?V1_TRANS_MOD,
- %% io_list).
- {error, not_implemented}.
-
-
-%%----------------------------------------------------------------------
-%% Convert a ActionRequest record into a binary
-%% Return {ok, DeepIoList} | {error, Reason}
-%%----------------------------------------------------------------------
-encode_action_request(_EC, 1, _ActReq) ->
- %% ?BIN_LIB:encode_action_request(EC, ActReq,
- %% ?V1_ASN1_MOD,
- %% ?V1_TRANS_MOD,
- %% io_list);
- {error, not_implemented};
-encode_action_request(_EC, 2, _ActReq) ->
- %% ?BIN_LIB:encode_action_request(EC, ActReq,
- %% ?V1_ASN1_MOD,
- %% ?V1_TRANS_MOD,
- %% io_list).
- {error, not_implemented};
-encode_action_request(_EC, 3, _ActReq) ->
- %% ?BIN_LIB:encode_action_request(EC, ActReq,
- %% ?V1_ASN1_MOD,
- %% ?V1_TRANS_MOD,
- %% io_list).
- {error, not_implemented}.
-
-
-%%----------------------------------------------------------------------
-%% Convert a action reply into a deep io list
-%% Not yest supported by this binary codec!
-%% Return {ok, DeepIoList} | {error, Reason}
-%%----------------------------------------------------------------------
-
-encode_action_reply(_EC, _V, _AcionReply) ->
- {error, not_implemented}.
-
-
-%%----------------------------------------------------------------------
-%% Convert a binary into a 'MegacoMessage' record
-%% Return {ok, MegacoMessageRecord} | {error, Reason}
-%%----------------------------------------------------------------------
-
-decode_message(EC, Binary) ->
- decode_message(EC, 1, Binary).
-
-%% PER does not support partial decode, so this means V1
-decode_message(EC, dynamic, Binary) ->
- decode_message(EC, 1, Binary);
-
-
-%% -- Version 1 --
-
-decode_message([{version3,_},driver|EC], 1, Binary) ->
- AsnMod = ?V1_ASN1_MOD_DRV,
- TransMod = ?V1_TRANS_MOD,
- ?BIN_LIB:decode_message(EC, Binary, AsnMod, TransMod, binary);
-decode_message([driver|EC], 1, Binary) ->
- AsnMod = ?V1_ASN1_MOD_DRV,
- TransMod = ?V1_TRANS_MOD,
- ?BIN_LIB:decode_message(EC, Binary, AsnMod, TransMod, binary);
-decode_message([{version3,_}|EC], 1, Binary) ->
- AsnMod = ?V1_ASN1_MOD,
- TransMod = ?V1_TRANS_MOD,
- ?BIN_LIB:decode_message(EC, Binary, AsnMod, TransMod, binary);
-
-%% All values we need to take (special) care of has been delt with,
-%% so just pass the rest on
-decode_message(EC, 1, Binary) ->
- AsnMod = ?V1_ASN1_MOD,
- TransMod = ?V1_TRANS_MOD,
- ?BIN_LIB:decode_message(EC, Binary, AsnMod, TransMod, binary);
-
-
-%% -- Version 2 --
-
-decode_message([{version3,_},driver|EC], 2, Binary) ->
- AsnMod = ?V2_ASN1_MOD_DRV,
- TransMod = ?V2_TRANS_MOD,
- ?BIN_LIB:decode_message(EC, Binary, AsnMod, TransMod, binary);
-decode_message([driver|EC], 2, Binary) ->
- AsnMod = ?V2_ASN1_MOD_DRV,
- TransMod = ?V2_TRANS_MOD,
- ?BIN_LIB:decode_message(EC, Binary, AsnMod, TransMod, binary);
-decode_message([{version3,_}|EC], 2, Binary) ->
- AsnMod = ?V2_ASN1_MOD,
- TransMod = ?V2_TRANS_MOD,
- ?BIN_LIB:decode_message(EC, Binary, AsnMod, TransMod, binary);
-
-%% All values we need to take (special) care of has been delt with,
-%% so just pass the rest on
-decode_message(EC, 2, Binary) ->
- AsnMod = ?V2_ASN1_MOD,
- TransMod = ?V2_TRANS_MOD,
- ?BIN_LIB:decode_message(EC, Binary, AsnMod, TransMod, binary);
-
-
-%% -- Version 3 --
-
-decode_message([{version3,v3},driver|EC], 3, Binary) ->
- AsnMod = ?V3_ASN1_MOD_DRV,
- TransMod = ?V3_TRANS_MOD,
- ?BIN_LIB:decode_message(EC, Binary, AsnMod, TransMod, binary);
-decode_message([{version3,prev3c},driver|EC], 3, Binary) ->
- AsnMod = ?PREV3C_ASN1_MOD_DRV,
- TransMod = ?PREV3C_TRANS_MOD,
- ?BIN_LIB:decode_message(EC, Binary, AsnMod, TransMod, binary);
-decode_message([{version3,prev3b},driver|EC], 3, Binary) ->
- AsnMod = ?PREV3B_ASN1_MOD_DRV,
- TransMod = ?PREV3B_TRANS_MOD,
- ?BIN_LIB:decode_message(EC, Binary, AsnMod, TransMod, binary);
-decode_message([{version3,prev3a},driver|EC], 3, Binary) ->
- AsnMod = ?PREV3A_ASN1_MOD_DRV,
- TransMod = ?PREV3A_TRANS_MOD,
- ?BIN_LIB:decode_message(EC, Binary, AsnMod, TransMod, binary);
-decode_message([driver|EC], 3, Binary) ->
- AsnMod = ?V3_ASN1_MOD_DRV,
- TransMod = ?V3_TRANS_MOD,
- ?BIN_LIB:decode_message(EC, Binary, AsnMod, TransMod, binary);
-decode_message([{version3,v3}|EC], 3, Binary) ->
- AsnMod = ?V3_ASN1_MOD,
- TransMod = ?V3_TRANS_MOD,
- ?BIN_LIB:decode_message(EC, Binary, AsnMod, TransMod, binary);
-decode_message([{version3,prev3c}|EC], 3, Binary) ->
- AsnMod = ?PREV3C_ASN1_MOD,
- TransMod = ?PREV3C_TRANS_MOD,
- ?BIN_LIB:decode_message(EC, Binary, AsnMod, TransMod, binary);
-decode_message([{version3,prev3b}|EC], 3, Binary) ->
- AsnMod = ?PREV3B_ASN1_MOD,
- TransMod = ?PREV3B_TRANS_MOD,
- ?BIN_LIB:decode_message(EC, Binary, AsnMod, TransMod, binary);
-decode_message([{version3,prev3a}|EC], 3, Binary) ->
- AsnMod = ?PREV3A_ASN1_MOD,
- TransMod = ?PREV3A_TRANS_MOD,
- ?BIN_LIB:decode_message(EC, Binary, AsnMod, TransMod, binary);
-
-%% All values we need to take (special) care of has been delt with,
-%% so just pass the rest on
-decode_message(EC, 3, Binary) ->
- AsnMod = ?V3_ASN1_MOD,
- TransMod = ?V3_TRANS_MOD,
- ?BIN_LIB:decode_message(EC, Binary, AsnMod, TransMod, binary).
-
-
-decode_mini_message(_EC, _Vsn, _Bin) ->
- {error, not_implemented}.
diff --git a/lib/megaco/src/binary/megaco_per_bin_media_gateway_control_prev3a.set.asn b/lib/megaco/src/binary/megaco_per_bin_media_gateway_control_prev3a.set.asn
deleted file mode 100644
index b9ba7ffdb4..0000000000
--- a/lib/megaco/src/binary/megaco_per_bin_media_gateway_control_prev3a.set.asn
+++ /dev/null
@@ -1 +0,0 @@
-MEDIA-GATEWAY-CONTROL-prev3a.asn
diff --git a/lib/megaco/src/binary/megaco_per_bin_media_gateway_control_prev3b.set.asn b/lib/megaco/src/binary/megaco_per_bin_media_gateway_control_prev3b.set.asn
deleted file mode 100644
index 0437bde310..0000000000
--- a/lib/megaco/src/binary/megaco_per_bin_media_gateway_control_prev3b.set.asn
+++ /dev/null
@@ -1 +0,0 @@
-MEDIA-GATEWAY-CONTROL-prev3b.asn
diff --git a/lib/megaco/src/binary/megaco_per_bin_media_gateway_control_prev3c.set.asn b/lib/megaco/src/binary/megaco_per_bin_media_gateway_control_prev3c.set.asn
deleted file mode 100644
index e78055fbad..0000000000
--- a/lib/megaco/src/binary/megaco_per_bin_media_gateway_control_prev3c.set.asn
+++ /dev/null
@@ -1 +0,0 @@
-MEDIA-GATEWAY-CONTROL-prev3c.asn
diff --git a/lib/megaco/src/binary/megaco_per_bin_media_gateway_control_v1.set.asn b/lib/megaco/src/binary/megaco_per_bin_media_gateway_control_v1.set.asn
deleted file mode 100644
index 0f5a92dba1..0000000000
--- a/lib/megaco/src/binary/megaco_per_bin_media_gateway_control_v1.set.asn
+++ /dev/null
@@ -1 +0,0 @@
-MEDIA-GATEWAY-CONTROL-v1.asn
diff --git a/lib/megaco/src/binary/megaco_per_bin_media_gateway_control_v2.set.asn b/lib/megaco/src/binary/megaco_per_bin_media_gateway_control_v2.set.asn
deleted file mode 100644
index 7fc82b127f..0000000000
--- a/lib/megaco/src/binary/megaco_per_bin_media_gateway_control_v2.set.asn
+++ /dev/null
@@ -1 +0,0 @@
-MEDIA-GATEWAY-CONTROL-v2.asn
diff --git a/lib/megaco/src/binary/megaco_per_bin_media_gateway_control_v3.set.asn b/lib/megaco/src/binary/megaco_per_bin_media_gateway_control_v3.set.asn
deleted file mode 100644
index 1d7950a283..0000000000
--- a/lib/megaco/src/binary/megaco_per_bin_media_gateway_control_v3.set.asn
+++ /dev/null
@@ -1 +0,0 @@
-MEDIA-GATEWAY-CONTROL-v3.asn
diff --git a/lib/megaco/src/binary/modules.mk b/lib/megaco/src/binary/modules.mk
index a86ce2aecc..bbaf087ceb 100644
--- a/lib/megaco/src/binary/modules.mk
+++ b/lib/megaco/src/binary/modules.mk
@@ -27,19 +27,6 @@ MODULES = \
megaco_ber_media_gateway_control_prev3b \
megaco_ber_media_gateway_control_prev3c \
megaco_ber_media_gateway_control_v3 \
- megaco_ber_bin_encoder \
- megaco_ber_bin_media_gateway_control_v1 \
- megaco_ber_bin_media_gateway_control_v2 \
- megaco_ber_bin_media_gateway_control_prev3a \
- megaco_ber_bin_media_gateway_control_prev3b \
- megaco_ber_bin_media_gateway_control_prev3c \
- megaco_ber_bin_media_gateway_control_v3 \
- megaco_ber_bin_drv_media_gateway_control_v1 \
- megaco_ber_bin_drv_media_gateway_control_v2 \
- megaco_ber_bin_drv_media_gateway_control_prev3a \
- megaco_ber_bin_drv_media_gateway_control_prev3b \
- megaco_ber_bin_drv_media_gateway_control_prev3c \
- megaco_ber_bin_drv_media_gateway_control_v3 \
megaco_per_encoder \
megaco_per_media_gateway_control_v1 \
megaco_per_media_gateway_control_v2 \
@@ -47,19 +34,6 @@ MODULES = \
megaco_per_media_gateway_control_prev3b \
megaco_per_media_gateway_control_prev3c \
megaco_per_media_gateway_control_v3 \
- megaco_per_bin_encoder \
- megaco_per_bin_media_gateway_control_v1 \
- megaco_per_bin_media_gateway_control_v2 \
- megaco_per_bin_media_gateway_control_prev3a \
- megaco_per_bin_media_gateway_control_prev3b \
- megaco_per_bin_media_gateway_control_prev3c \
- megaco_per_bin_media_gateway_control_v3 \
- megaco_per_bin_drv_media_gateway_control_v1 \
- megaco_per_bin_drv_media_gateway_control_v2 \
- megaco_per_bin_drv_media_gateway_control_prev3a \
- megaco_per_bin_drv_media_gateway_control_prev3b \
- megaco_per_bin_drv_media_gateway_control_prev3c \
- megaco_per_bin_drv_media_gateway_control_v3 \
megaco_binary_name_resolver_v1 \
megaco_binary_name_resolver_v2 \
megaco_binary_name_resolver_prev3a \
@@ -85,44 +59,20 @@ ASN1_PREV3C_SPEC = MEDIA-GATEWAY-CONTROL-prev3c
ASN1_V3_SPEC = MEDIA-GATEWAY-CONTROL-v3
BER_ASN1_V1_SPEC = megaco_ber_media_gateway_control_v1
-BER_BIN_ASN1_V1_SPEC = megaco_ber_bin_media_gateway_control_v1
-BER_BIN_DRV_ASN1_V1_SPEC = megaco_ber_bin_drv_media_gateway_control_v1
PER_ASN1_V1_SPEC = megaco_per_media_gateway_control_v1
-PER_BIN_ASN1_V1_SPEC = megaco_per_bin_media_gateway_control_v1
-PER_BIN_DRV_ASN1_V1_SPEC = megaco_per_bin_drv_media_gateway_control_v1
BER_ASN1_V2_SPEC = megaco_ber_media_gateway_control_v2
-BER_BIN_ASN1_V2_SPEC = megaco_ber_bin_media_gateway_control_v2
-BER_BIN_DRV_ASN1_V2_SPEC = megaco_ber_bin_drv_media_gateway_control_v2
PER_ASN1_V2_SPEC = megaco_per_media_gateway_control_v2
-PER_BIN_ASN1_V2_SPEC = megaco_per_bin_media_gateway_control_v2
-PER_BIN_DRV_ASN1_V2_SPEC = megaco_per_bin_drv_media_gateway_control_v2
BER_ASN1_PREV3A_SPEC = megaco_ber_media_gateway_control_prev3a
-BER_BIN_ASN1_PREV3A_SPEC = megaco_ber_bin_media_gateway_control_prev3a
-BER_BIN_DRV_ASN1_PREV3A_SPEC = megaco_ber_bin_drv_media_gateway_control_prev3a
PER_ASN1_PREV3A_SPEC = megaco_per_media_gateway_control_prev3a
-PER_BIN_ASN1_PREV3A_SPEC = megaco_per_bin_media_gateway_control_prev3a
-PER_BIN_DRV_ASN1_PREV3A_SPEC = megaco_per_bin_drv_media_gateway_control_prev3a
BER_ASN1_PREV3B_SPEC = megaco_ber_media_gateway_control_prev3b
-BER_BIN_ASN1_PREV3B_SPEC = megaco_ber_bin_media_gateway_control_prev3b
-BER_BIN_DRV_ASN1_PREV3B_SPEC = megaco_ber_bin_drv_media_gateway_control_prev3b
PER_ASN1_PREV3B_SPEC = megaco_per_media_gateway_control_prev3b
-PER_BIN_ASN1_PREV3B_SPEC = megaco_per_bin_media_gateway_control_prev3b
-PER_BIN_DRV_ASN1_PREV3B_SPEC = megaco_per_bin_drv_media_gateway_control_prev3b
BER_ASN1_PREV3C_SPEC = megaco_ber_media_gateway_control_prev3c
-BER_BIN_ASN1_PREV3C_SPEC = megaco_ber_bin_media_gateway_control_prev3c
-BER_BIN_DRV_ASN1_PREV3C_SPEC = megaco_ber_bin_drv_media_gateway_control_prev3c
PER_ASN1_PREV3C_SPEC = megaco_per_media_gateway_control_prev3c
-PER_BIN_ASN1_PREV3C_SPEC = megaco_per_bin_media_gateway_control_prev3c
-PER_BIN_DRV_ASN1_PREV3C_SPEC = megaco_per_bin_drv_media_gateway_control_prev3c
BER_ASN1_V3_SPEC = megaco_ber_media_gateway_control_v3
-BER_BIN_ASN1_V3_SPEC = megaco_ber_bin_media_gateway_control_v3
-BER_BIN_DRV_ASN1_V3_SPEC = megaco_ber_bin_drv_media_gateway_control_v3
PER_ASN1_V3_SPEC = megaco_per_media_gateway_control_v3
-PER_BIN_ASN1_V3_SPEC = megaco_per_bin_media_gateway_control_v3
-PER_BIN_DRV_ASN1_V3_SPEC = megaco_per_bin_drv_media_gateway_control_v3
diff --git a/lib/megaco/src/engine/Makefile b/lib/megaco/src/engine/Makefile
index ae2fadb3fc..7cecef1444 100644
--- a/lib/megaco/src/engine/Makefile
+++ b/lib/megaco/src/engine/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2009. All Rights Reserved.
+# Copyright Ericsson AB 1999-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/megaco/src/engine/modules.mk b/lib/megaco/src/engine/modules.mk
index 6e2f9246f0..e6e5e29829 100644
--- a/lib/megaco/src/engine/modules.mk
+++ b/lib/megaco/src/engine/modules.mk
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2001-2011. All Rights Reserved.
+# Copyright Ericsson AB 2001-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/megaco/src/flex/Makefile.in b/lib/megaco/src/flex/Makefile.in
index 976ecaa4f7..6111cf2304 100644
--- a/lib/megaco/src/flex/Makefile.in
+++ b/lib/megaco/src/flex/Makefile.in
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2001-2011. All Rights Reserved.
+# Copyright Ericsson AB 2001-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -104,10 +104,6 @@ ENABLE_MEGACO_FLEX_SCANNER_LINENO = @ENABLE_MEGACO_FLEX_SCANNER_LINENO@
endif
endif
-ifeq ($(findstring vxworks,$(TARGET)),vxworks)
- DED_INCLUDES += -I$(ERL_TOP)/erts/etc/vxworks
-endif
-
PRIVDIR = ../../priv
LIBDIR = $(PRIVDIR)/lib/$(TARGET)
OBJDIR = $(PRIVDIR)/obj/$(TARGET)
@@ -146,15 +142,10 @@ ifeq ($(findstring win32,$(TARGET)), win32)
FLEX_SCANNER_SO =
SOLIBS = $(FLEX_SCANNER_SO)
else
-ifeq ($(findstring vxworks,$(TARGET)),vxworks)
-FLEX_SCANNER_SO =
-SOLIBS = $(FLEX_SCANNER_SO)
-else
FLEX_SCANNER_SO = $(LIBDIR)/$(STD_DRV).$(DED_EXT)
FLEX_SCANNER_MT_SO = $(LIBDIR)/$(MT_DRV).$(DED_EXT)
SOLIBS = $(FLEX_SCANNER_SO) $(FLEX_SCANNER_MT_SO)
endif
-endif
# ----------------------------------------------------
@@ -328,16 +319,16 @@ release_docs_spec:
$(STD_DRV).flex: megaco_flex_scanner_drv.flex.src
ifeq ($(ENABLE_MEGACO_FLEX_SCANNER_LINENO),true)
- @printf "std [flex] scanner - lineno enabled\n"
- $(PERL) -p -e \
+ $(V_colon)@printf "std [flex] scanner - lineno enabled\n"
+ $(gen_verbose)$(PERL) -p -e \
's/%FLEX_VERSION%/$(FLEX_VSN)/ ; \
s/%MEGACO_YY_LINENO_OPTION%/%option yylineno/ ; \
s/%MEGACO_YY_REENTRANT_OPTION%/\/\* %option reentrant \*\// ; \
s/%MEGACO_DUMMY_DECL_YY_LINENO%/\/* static int yylineno = 1; *\//' \
< $< > $@
else
- @printf "std [flex] scanner - lineno disabled\n"
- $(PERL) -p -e \
+ $(V_colon)@printf "std [flex] scanner - lineno disabled\n"
+ $(gen_verbose)$(PERL) -p -e \
's/%FLEX_VERSION%/$(FLEX_VSN)/ ; \
s/%MEGACO_YY_LINENO_OPTION%/\/\* %option yylineno \*\// ; \
s/%MEGACO_YY_REENTRANT_OPTION%/\/\* %option reentrant \*\// ; \
@@ -348,16 +339,16 @@ endif
$(MT_DRV).flex: megaco_flex_scanner_drv.flex.src
ifeq ($(ENABLE_MEGACO_FLEX_SCANNER_LINENO),true)
ifeq ($(ENABLE_REENTRANT_MEGACO_FLEX_SCANNER),true)
- @printf "multi-threaded reentrant [flex] scanner - lineno enabled\n"
- $(PERL) -p -e \
+ $(V_colon)@printf "multi-threaded reentrant [flex] scanner - lineno enabled\n"
+ $(gen_verbose)$(PERL) -p -e \
's/%FLEX_VERSION%/$(FLEX_VSN)/ ; \
s/%MEGACO_YY_LINENO_OPTION%/%option yylineno/ ; \
s/%MEGACO_YY_REENTRANT_OPTION%/%option reentrant/ ; \
s/%MEGACO_DUMMY_DECL_YY_LINENO%/\/* static int yylineno = 1; *\//' \
< $< > $@
else
- @printf "multi-threaded non-reentrant [flex] scanner - lineno enabled\n"
- $(PERL) -p -e \
+ $(V_colon)@printf "multi-threaded non-reentrant [flex] scanner - lineno enabled\n"
+ $(gen_verbose)$(PERL) -p -e \
's/%FLEX_VERSION%/$(FLEX_VSN)/ ; \
s/%MEGACO_YY_LINENO_OPTION%/%option yylineno/ ; \
s/%MEGACO_YY_REENTRANT_OPTION%/\/\* %option reentrant \*\// ; \
@@ -366,16 +357,16 @@ else
endif
else
ifeq ($(ENABLE_REENTRANT_MEGACO_FLEX_SCANNER),true)
- @printf "multi-threaded reentrant [flex] scanner - lineno disabled\n"
- $(PERL) -p -e \
+ $(V_colon)@printf "multi-threaded reentrant [flex] scanner - lineno disabled\n"
+ $(gen_verbose)$(PERL) -p -e \
's/%FLEX_VERSION%/$(FLEX_VSN)/ ; \
s/%MEGACO_YY_LINENO_OPTION%/\/\* %option yylineno \*\// ; \
s/%MEGACO_YY_REENTRANT_OPTION%/%option reentrant/ ; \
s/%MEGACO_DUMMY_DECL_YY_LINENO%/\/* static int yylineno = 1; - REENTRANT SCANNER*\//' \
< $< > $@
else
- @printf "multi-threaded non-reentrant [flex] scanner - lineno disabled\n"
- $(PERL) -p -e \
+ $(V_colon)@printf "multi-threaded non-reentrant [flex] scanner - lineno disabled\n"
+ $(gen_verbose)$(PERL) -p -e \
's/%FLEX_VERSION%/$(FLEX_VSN)/ ; \
s/%MEGACO_YY_LINENO_OPTION%/\/\* %option yylineno \*\// ; \
s/%MEGACO_YY_REENTRANT_OPTION%/\/\* %option reentrant \*\// ; \
@@ -385,31 +376,31 @@ endif
endif
# megaco_flex_scanner_drv.c: megaco_flex_scanner_drv.flex
-# $(LEX) $(LEX_FLAGS) -P$* -o$@ $<
+# $(V_LEX) $(LEX_FLAGS) -P$* -o$@ $<
$(STD_DRV).c: $(STD_DRV).flex
- $(LEX) $(STD_LEX_FLAGS) -P$* -o$@ $<
+ $(V_LEX) $(STD_LEX_FLAGS) -P$* -o$@ $<
$(MT_DRV).c: $(MT_DRV).flex
- $(LEX) $(MT_LEX_FLAGS) -P$* -o$@ $<
+ $(V_LEX) $(MT_LEX_FLAGS) -P$* -o$@ $<
_create_dirs := $(shell mkdir -p $(OBJDIR) $(LIBDIR))
solibs: $(SOLIBS)
$(OBJDIR)/$(STD_DRV).o: $(STD_DRV).c
- @echo "compiling std driver:"
- $(CC) -c $(STD_DRV_NAME) $(CFLAGS) -o $@ $<
+ $(V_colon)@echo "compiling std driver:"
+ $(V_CC) -c $(STD_DRV_NAME) $(CFLAGS) -o $@ $<
$(OBJDIR)/$(MT_DRV).o: $(MT_DRV).c
- @echo "compiling multi-threaded driver:"
- $(CC) -c $(MT_DRV_NAME) $(CFLAGS_MT) -o $@ $<
+ $(V_colon)@echo "compiling multi-threaded driver:"
+ $(V_CC) -c $(MT_DRV_NAME) $(CFLAGS_MT) -o $@ $<
# No need to link with -lfl as we have also defined %option noyywrap -
# and having -lfl doesn't work under Darwin for some reason. - Sean
$(LIBDIR)/$(STD_DRV).$(DED_EXT): $(OBJDIR)/$(STD_DRV).o
- @echo "linking std driver:"
- $(LD) $(LDFLAGS) -o $@ $<
+ $(V_colon)@echo "linking std driver:"
+ $(V_LD) $(LDFLAGS) -o $@ $<
$(LIBDIR)/$(MT_DRV).$(DED_EXT): $(OBJDIR)/$(MT_DRV).o
- @echo "linking multi-threaded driver:"
- $(LD) $(LDFLAGS) -o $@ $<
+ $(V_colon)@echo "linking multi-threaded driver:"
+ $(V_LD) $(LDFLAGS) -o $@ $<
diff --git a/lib/megaco/src/flex/megaco_flex_scanner_drv.flex.src b/lib/megaco/src/flex/megaco_flex_scanner_drv.flex.src
index b8146c345d..5faddb08c5 100644
--- a/lib/megaco/src/flex/megaco_flex_scanner_drv.flex.src
+++ b/lib/megaco/src/flex/megaco_flex_scanner_drv.flex.src
@@ -76,6 +76,7 @@
typedef struct {
ErlDrvPort port;
+ ErlDrvTermData port_id;
char* digit_map_name_ptr;
int digit_map_name_len;
char* digit_map_value_ptr;
@@ -1497,6 +1498,7 @@ static ErlDrvData mfs_start(ErlDrvPort port, char *buf)
DBG( ("mfs_start -> entry\n") );
dataP->port = port;
+ dataP->port_id = driver_mk_port(port);
dataP->digit_map_name_ptr = NULL;
dataP->digit_map_name_len = 0;
dataP->digit_map_value_ptr = NULL;
@@ -1841,10 +1843,10 @@ static ErlDrvSSizeT mfs_control(ErlDrvData handle,
"\n term_spec_size: %d\n",
dataP->term_spec_index, dataP->term_spec_size) );
- driver_send_term(dataP->port,
- driver_caller(dataP->port),
- dataP->term_spec,
- dataP->term_spec_index);
+ erl_drv_send_term(dataP->port_id,
+ driver_caller(dataP->port),
+ dataP->term_spec,
+ dataP->term_spec_index);
if (dataP->text_buf != NULL) FREE(dataP->text_buf);
if (dataP->term_spec != NULL) FREE(dataP->term_spec);
diff --git a/lib/megaco/src/rules.mk b/lib/megaco/src/rules.mk
index 20fbed2a76..a59060032d 100644
--- a/lib/megaco/src/rules.mk
+++ b/lib/megaco/src/rules.mk
@@ -29,10 +29,6 @@ PERL = perl
# Erlang language section
# ----------------------------------------------------
EMULATOR = beam
-ifeq ($(findstring vxworks,$(TARGET)),vxworks)
-# VxWorks jam object files should be compressed
-ERL_COMPILE_FLAGS += +compressed
-endif
ERLC_WFLAGS = -W
ERLC = erlc $(ERLC_WFLAGS) $(ERLC_FLAGS)
ERL.beam = erl.beam -boot start_clean
diff --git a/lib/megaco/src/tcp/Makefile b/lib/megaco/src/tcp/Makefile
index efc76bb2c5..0c30abae2c 100644
--- a/lib/megaco/src/tcp/Makefile
+++ b/lib/megaco/src/tcp/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2000-2009. All Rights Reserved.
+# Copyright Ericsson AB 2000-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/megaco/src/text/Makefile b/lib/megaco/src/text/Makefile
index 2c7703cd33..cab4256e68 100644
--- a/lib/megaco/src/text/Makefile
+++ b/lib/megaco/src/text/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2000-2009. All Rights Reserved.
+# Copyright Ericsson AB 2000-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/megaco/src/udp/Makefile b/lib/megaco/src/udp/Makefile
index db9bb72d82..9486804d38 100644
--- a/lib/megaco/src/udp/Makefile
+++ b/lib/megaco/src/udp/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2000-2009. All Rights Reserved.
+# Copyright Ericsson AB 2000-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/megaco/test/Makefile b/lib/megaco/test/Makefile
index b1bca10ca8..b78f21f608 100644
--- a/lib/megaco/test/Makefile
+++ b/lib/megaco/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2010. All Rights Reserved.
+# Copyright Ericsson AB 1999-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/megaco/test/megaco.spec.vxworks b/lib/megaco/test/megaco.spec.vxworks
deleted file mode 100644
index 2ac250e443..0000000000
--- a/lib/megaco/test/megaco.spec.vxworks
+++ /dev/null
@@ -1,5 +0,0 @@
-{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"}}.
diff --git a/lib/megaco/test/megaco_actions_test.erl b/lib/megaco/test/megaco_actions_test.erl
index 2efb6e834a..6d0e80281d 100644
--- a/lib/megaco/test/megaco_actions_test.erl
+++ b/lib/megaco/test/megaco_actions_test.erl
@@ -80,8 +80,7 @@ end_per_testcase(Case, Config) ->
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].
+ flex_compact_text, erl_dist, erl_dist_mc].
groups() ->
[].
@@ -170,39 +169,6 @@ erl_dist_mc(Config) when is_list(Config) ->
req_and_rep(Config, Codec, Version, EncodingConfig).
-ber_bin(suite) ->
- [];
-ber_bin(doc) ->
- [];
-ber_bin(Config) when is_list(Config) ->
- ?SKIP(currently_not_supported_by_asn1).
-
-
-ber_bin_drv(suite) ->
- [];
-ber_bin_drv(doc) ->
- [];
-ber_bin_drv(Config) when is_list(Config) ->
- ?SKIP(currently_not_supported_by_asn1).
-
-
-ber_bin_native(suite) ->
- [];
-ber_bin_native(doc) ->
- [];
-ber_bin_native(Config) when is_list(Config) ->
- ?SKIP(currently_not_supported_by_asn1).
-
-
-ber_bin_drv_native(suite) ->
- [];
-ber_bin_drv_native(doc) ->
- [];
-ber_bin_drv_native(Config) when is_list(Config) ->
- ?SKIP(currently_not_supported_by_asn1).
-
-
-
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
req_and_rep(Config, Codec, _Version, EC) when is_list(Config) ->
diff --git a/lib/megaco/test/megaco_call_flow_test.erl b/lib/megaco/test/megaco_call_flow_test.erl
index b9d64ca8b2..d5888018cd 100644
--- a/lib/megaco/test/megaco_call_flow_test.erl
+++ b/lib/megaco/test/megaco_call_flow_test.erl
@@ -25,7 +25,6 @@
%% megaco_call_flow_test:compact_text().
%% megaco_call_flow_test:bin().
%% megaco_call_flow_test:asn1_ber().
-%% megaco_call_flow_test:asn1_ber_bin().
%% megaco_call_flow_test:asn1_per().
%% megaco_call_flow_test:erl_dist().
%% megaco_call_flow_test:compressed_erl_dist().
@@ -62,7 +61,7 @@ all() ->
groups() ->
[{text, [], [pretty, compact]},
{flex, [], [pretty_flex, compact_flex]},
- {binary, [], [bin, ber, ber_bin, per]}].
+ {binary, [], [bin, ber, per]}].
init_per_group(_GroupName, Config) ->
Config.
@@ -106,12 +105,6 @@ ber(Config) when is_list(Config) ->
?ACQUIRE_NODES(1, Config),
asn1_ber().
-ber_bin(suite) ->
- [];
-ber_bin(Config) when is_list(Config) ->
- ?ACQUIRE_NODES(1, Config),
- asn1_ber_bin().
-
per(suite) ->
[];
per(Config) when is_list(Config) ->
@@ -1198,8 +1191,7 @@ encoders() ->
{megaco_pretty_text_encoder, [], []},
{megaco_compact_text_encoder, [], []},
{megaco_binary_encoder, [], [native]},
- %% {megaco_ber_encoder, [], [native]},
- %% {megaco_ber_bin_encoder, [], [native]},
+ {megaco_ber_encoder, [], [native]},
{megaco_per_encoder, [], [native]},
{megaco_erl_dist_encoder, [], []},
{megaco_erl_dist_encoder, [compressed], [compressed]}
@@ -1214,7 +1206,6 @@ pretty_mod({Mod, Opt, _Opt2}) ->
megaco_compact_text_encoder -> compact_text;
megaco_binary_encoder -> asn1_ber;
megaco_ber_encoder -> asn1_ber_old;
- megaco_ber_bin_encoder -> asn1_ber_bin;
megaco_per_encoder -> asn1_per;
megaco_erl_dist_encoder when Opt == [] -> standard_erl;
megaco_erl_dist_encoder when Opt == [compressed] -> compressed_erl;
@@ -1263,13 +1254,6 @@ asn1_ber() ->
All = [encode(Slogan, Msg, Encoder) || {Slogan, Msg} <- messages()],
compute_res(All).
-asn1_ber_bin() ->
- Default = [],
- Native = [native],
- Encoder = {megaco_ber_bin_encoder, Default, Native},
- All = [encode(Slogan, Msg, Encoder) || {Slogan, Msg} <- messages()],
- compute_res(All).
-
asn1_per() ->
Default = [],
Native = [native],
@@ -1634,7 +1618,7 @@ gen_ber_header() ->
%% Generate headerfile for asn.1 BER test in C
%%----------------------------------------------------------------------
gen_ber_bin_header() ->
- Encoder = {megaco_ber_bin_encoder, [], []},
+ Encoder = {megaco_ber_encoder, [], []},
L = [{S, gen_byte_msg(Msg, Encoder)} || {S, Msg} <- messages()],
gen_header_file_binary(L).
diff --git a/lib/megaco/test/megaco_codec_prev3a_test.erl b/lib/megaco/test/megaco_codec_prev3a_test.erl
index d50e72aef1..b2316eb509 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-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -63,12 +63,8 @@
ber_test_msgs/1,
- ber_bin_test_msgs/1,
-
per_test_msgs/1,
- per_bin_test_msgs/1,
-
erl_dist_m_test_msgs/1,
tickets/0,
@@ -280,17 +276,14 @@ groups() ->
[{group, pretty}, {group, flex_pretty},
{group, compact}, {group, flex_compact}]},
{binary, [],
- [{group, bin}, {group, ber}, {group, ber_bin},
- {group, per}, {group, per_bin}]},
+ [{group, bin}, {group, ber}, {group, per}]},
{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},
@@ -1106,17 +1099,6 @@ ber_test_msgs(Config) when is_list(Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-ber_bin_test_msgs(suite) ->
- [];
-ber_bin_test_msgs(Config) when is_list(Config) ->
- ?ACQUIRE_NODES(1, Config),
- Msgs = msgs1(binary) ++ msgs4(binary) ++ msgs5(binary) ++ msgs6(binary),
- DynamicDecode = true,
- test_msgs(megaco_ber_bin_encoder, DynamicDecode, ?EC, Msgs).
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
per_test_msgs(suite) ->
[];
per_test_msgs(Config) when is_list(Config) ->
@@ -1128,17 +1110,6 @@ per_test_msgs(Config) when is_list(Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-per_bin_test_msgs(suite) ->
- [];
-per_bin_test_msgs(Config) when is_list(Config) ->
- ?ACQUIRE_NODES(1, Config),
- Msgs = msgs1(binary) ++ msgs4(binary) ++ msgs5(binary) ++ msgs6(binary),
- DynamicDecode = false,
- test_msgs(megaco_per_bin_encoder, DynamicDecode, ?EC, Msgs).
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
erl_dist_m_test_msgs(suite) ->
[];
erl_dist_m_test_msgs(Config) when is_list(Config) ->
@@ -3133,7 +3104,7 @@ pretty_otp5068_msg1() ->
190,
asn1_NOVALUE,
{actionReplies,
- [{'ActionReply', %% Comments: Detta upprepas m�nga g�nger
+ [{'ActionReply', %% Comments: This is repeated many times.
0,
asn1_NOVALUE,
asn1_NOVALUE,
diff --git a/lib/megaco/test/megaco_codec_prev3b_test.erl b/lib/megaco/test/megaco_codec_prev3b_test.erl
index eaab8f37c1..fa24f49372 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-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -63,12 +63,8 @@
ber_test_msgs/1,
- ber_bin_test_msgs/1,
-
per_test_msgs/1,
- per_bin_test_msgs/1,
-
erl_dist_m_test_msgs/1,
tickets/0,
@@ -296,17 +292,14 @@ groups() ->
[{group, pretty}, {group, flex_pretty},
{group, compact}, {group, flex_compact}]},
{binary, [],
- [{group, bin}, {group, ber}, {group, ber_bin},
- {group, per}, {group, per_bin}]},
+ [{group, bin}, {group, ber}, {group, per}]},
{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},
@@ -1171,16 +1164,6 @@ ber_test_msgs(Config) when is_list(Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-ber_bin_test_msgs(suite) ->
- [];
-ber_bin_test_msgs(Config) when is_list(Config) ->
- ?ACQUIRE_NODES(1, Config),
- Msgs = msgs1(binary) ++ msgs4(binary) ++ msgs5(binary) ++ msgs6(binary),
- DynamicDecode = true,
- test_msgs(megaco_ber_bin_encoder, DynamicDecode, ?EC, Msgs).
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
per_test_msgs(suite) ->
[];
@@ -1193,17 +1176,6 @@ per_test_msgs(Config) when is_list(Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-per_bin_test_msgs(suite) ->
- [];
-per_bin_test_msgs(Config) when is_list(Config) ->
- ?ACQUIRE_NODES(1, Config),
- Msgs = msgs1(binary) ++ msgs4(binary) ++ msgs5(binary) ++ msgs6(binary),
- DynamicDecode = false,
- test_msgs(megaco_per_bin_encoder, DynamicDecode, ?EC, Msgs).
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
erl_dist_m_test_msgs(suite) ->
[];
erl_dist_m_test_msgs(Config) when is_list(Config) ->
@@ -3251,7 +3223,7 @@ pretty_otp5068_msg1() ->
190,
asn1_NOVALUE,
{actionReplies,
- [{'ActionReply', %% Comments: Detta upprepas m�nga g�nger
+ [{'ActionReply', %% Comments: This is repeated many times.
0,
asn1_NOVALUE,
asn1_NOVALUE,
diff --git a/lib/megaco/test/megaco_codec_prev3c_test.erl b/lib/megaco/test/megaco_codec_prev3c_test.erl
index 7f9c0fe4e7..7f6d098ed8 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-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -65,12 +65,8 @@
ber_test_msgs/1,
- ber_bin_test_msgs/1,
-
per_test_msgs/1,
- per_bin_test_msgs/1,
-
erl_dist_m_test_msgs/1,
tickets/0,
@@ -301,17 +297,14 @@ groups() ->
[{group, pretty}, {group, flex_pretty},
{group, compact}, {group, flex_compact}]},
{binary, [],
- [{group, bin}, {group, ber}, {group, ber_bin},
- {group, per}, {group, per_bin}]},
+ [{group, bin}, {group, ber}, {group, per}]},
{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},
@@ -823,21 +816,6 @@ ber_test_msgs(Config) when is_list(Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-ber_bin_test_msgs(suite) ->
- [];
-ber_bin_test_msgs(Config) when is_list(Config) ->
- ?ACQUIRE_NODES(1, Config),
- Msgs =
- msgs1a(binary) ++
- msgs5(binary) ++
- msgs6(binary) ++
- msgs7(binary),
- DynamicDecode = true,
- test_msgs(megaco_ber_bin_encoder, DynamicDecode, ?EC, Msgs).
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
per_test_msgs(suite) ->
[];
per_test_msgs(Config) when is_list(Config) ->
@@ -853,21 +831,6 @@ per_test_msgs(Config) when is_list(Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-per_bin_test_msgs(suite) ->
- [];
-per_bin_test_msgs(Config) when is_list(Config) ->
- ?ACQUIRE_NODES(1, Config),
- Msgs =
- msgs1a(binary) ++
- msgs5(binary) ++
- msgs6(binary) ++
- msgs7(binary),
- DynamicDecode = false,
- test_msgs(megaco_per_bin_encoder, DynamicDecode, ?EC, Msgs).
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
erl_dist_m_test_msgs(suite) ->
[];
erl_dist_m_test_msgs(Config) when is_list(Config) ->
@@ -2716,7 +2679,7 @@ pretty_otp5068_msg1() ->
190,
asn1_NOVALUE,
{actionReplies,
- [{'ActionReply', %% Comments: Detta upprepas m�nga g�nger
+ [{'ActionReply', %% Comments: This is repeated many times.
0,
asn1_NOVALUE,
asn1_NOVALUE,
diff --git a/lib/megaco/test/megaco_codec_v1_test.erl b/lib/megaco/test/megaco_codec_v1_test.erl
index e9c19605dd..3be0da3ae4 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-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -66,12 +66,8 @@
ber_test_msgs/1,
- ber_bin_test_msgs/1,
-
per_test_msgs/1,
- per_bin_test_msgs/1,
-
erl_dist_m_test_msgs/1,
tickets/0,
@@ -476,9 +472,7 @@ groups() ->
{group, flex_compact}]},
{binary, [], [{group, bin},
{group, ber},
- {group, ber_bin},
- {group, per},
- {group, per_bin}]},
+ {group, per}]},
{erl_dist, [], [{group, erl_dist_m}]},
{pretty, [], [pretty_test_msgs]},
{compact, [], [compact_test_msgs]},
@@ -486,9 +480,7 @@ groups() ->
{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},
@@ -1266,17 +1258,6 @@ ber_test_msgs(Config) when is_list(Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-ber_bin_test_msgs(suite) ->
- [];
-ber_bin_test_msgs(Config) when is_list(Config) ->
- ?ACQUIRE_NODES(1, Config),
- Msgs = msgs1(),
- DynamicDecode = true,
- test_msgs(megaco_ber_bin_encoder, DynamicDecode, [], Msgs).
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
per_test_msgs(suite) ->
[];
per_test_msgs(Config) when is_list(Config) ->
@@ -1288,17 +1269,6 @@ per_test_msgs(Config) when is_list(Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-per_bin_test_msgs(suite) ->
- [];
-per_bin_test_msgs(Config) when is_list(Config) ->
- ?ACQUIRE_NODES(1, Config),
- Msgs = msgs1(),
- DynamicDecode = false,
- test_msgs(megaco_per_bin_encoder, DynamicDecode, [], Msgs).
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
erl_dist_m_test_msgs(suite) ->
[];
erl_dist_m_test_msgs(Config) when is_list(Config) ->
@@ -3328,7 +3298,7 @@ pretty_otp5068_msg1() ->
190,
asn1_NOVALUE,
{actionReplies,
- [{'ActionReply', %% Comments: Detta upprepas m�nga g�nger
+ [{'ActionReply', %% Comments: This is repeated many times.
0,
asn1_NOVALUE,
asn1_NOVALUE,
diff --git a/lib/megaco/test/megaco_codec_v2_test.erl b/lib/megaco/test/megaco_codec_v2_test.erl
index a44f74166c..1f522504d6 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-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -64,12 +64,8 @@
ber_test_msgs/1,
- ber_bin_test_msgs/1,
-
per_test_msgs/1,
- per_bin_test_msgs/1,
-
erl_dist_m_test_msgs/1,
tickets/0,
@@ -447,17 +443,14 @@ groups() ->
[{group, pretty}, {group, flex_pretty},
{group, compact}, {group, flex_compact}]},
{binary, [],
- [{group, bin}, {group, ber}, {group, ber_bin},
- {group, per}, {group, per_bin}]},
+ [{group, bin}, {group, ber}, {group, per}]},
{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},
@@ -1285,17 +1278,6 @@ ber_test_msgs(Config) when is_list(Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-ber_bin_test_msgs(suite) ->
- [];
-ber_bin_test_msgs(Config) when is_list(Config) ->
- ?ACQUIRE_NODES(1, Config),
- Msgs = msgs1() ++ msgs4(),
- DynamicDecode = true,
- test_msgs(megaco_ber_bin_encoder, DynamicDecode, [], Msgs).
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
per_test_msgs(suite) ->
[];
per_test_msgs(Config) when is_list(Config) ->
@@ -1307,17 +1289,6 @@ per_test_msgs(Config) when is_list(Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-per_bin_test_msgs(suite) ->
- [];
-per_bin_test_msgs(Config) when is_list(Config) ->
- ?ACQUIRE_NODES(1, Config),
- Msgs = msgs1() ++ msgs4(),
- DynamicDecode = false,
- test_msgs(megaco_per_bin_encoder, DynamicDecode, [], Msgs).
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
erl_dist_m_test_msgs(suite) ->
[];
erl_dist_m_test_msgs(Config) when is_list(Config) ->
@@ -3751,7 +3722,7 @@ pretty_otp5068_msg1() ->
190,
asn1_NOVALUE,
{actionReplies,
- [{'ActionReply', %% Comments: Detta upprepas m�nga g�nger
+ [{'ActionReply', %% Comments: This is repeated many times.
0,
asn1_NOVALUE,
asn1_NOVALUE,
diff --git a/lib/megaco/test/megaco_codec_v3_test.erl b/lib/megaco/test/megaco_codec_v3_test.erl
index 2c35ce13b3..9d564a0ae3 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-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -56,9 +56,7 @@
flex_compact_dm_timers8/1,
bin_test_msgs/1,
ber_test_msgs/1,
- ber_bin_test_msgs/1,
per_test_msgs/1,
- per_bin_test_msgs/1,
erl_dist_m_test_msgs/1,
tickets/0,
@@ -288,17 +286,14 @@ groups() ->
[{group, pretty}, {group, flex_pretty},
{group, compact}, {group, flex_compact}]},
{binary, [],
- [{group, bin}, {group, ber}, {group, ber_bin},
- {group, per}, {group, per_bin}]},
+ [{group, bin}, {group, ber}, {group, per}]},
{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},
@@ -823,22 +818,6 @@ ber_test_msgs(Config) when is_list(Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-ber_bin_test_msgs(suite) ->
- [];
-ber_bin_test_msgs(Config) when is_list(Config) ->
- ?ACQUIRE_NODES(1, Config),
- Msgs =
- msgs1a(binary) ++
- msgs5(binary) ++
- msgs6(binary) ++
- msgs7(binary) ++
- msgs8(binary),
- DynamicDecode = true,
- test_msgs(megaco_ber_bin_encoder, DynamicDecode, ?EC, Msgs).
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
per_test_msgs(suite) ->
[];
per_test_msgs(Config) when is_list(Config) ->
@@ -855,22 +834,6 @@ per_test_msgs(Config) when is_list(Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-per_bin_test_msgs(suite) ->
- [];
-per_bin_test_msgs(Config) when is_list(Config) ->
- ?ACQUIRE_NODES(1, Config),
- Msgs =
- msgs1a(binary) ++
- msgs5(binary) ++
- msgs6(binary) ++
- msgs7(binary) ++
- msgs8(binary),
- DynamicDecode = false,
- test_msgs(megaco_per_bin_encoder, DynamicDecode, ?EC, Msgs).
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
erl_dist_m_test_msgs(suite) ->
[];
erl_dist_m_test_msgs(Config) when is_list(Config) ->
@@ -2738,7 +2701,7 @@ pretty_otp5068_msg1() ->
190,
asn1_NOVALUE,
{actionReplies,
- [{'ActionReply', %% Comments: Detta upprepas m�nga g�nger
+ [{'ActionReply', %% Comments: This is repeated many times.
0,
asn1_NOVALUE,
asn1_NOVALUE,
diff --git a/lib/megaco/test/megaco_mess_test.erl b/lib/megaco/test/megaco_mess_test.erl
index 663ac8c329..f8be96c254 100644
--- a/lib/megaco/test/megaco_mess_test.erl
+++ b/lib/megaco/test/megaco_mess_test.erl
@@ -1393,7 +1393,7 @@ rarpaop_mgc_event_sequence(text, tcp) ->
rarpaop_mgc_event_sequence(binary, tcp) ->
Port = 2945,
TranspMod = megaco_tcp,
- EncMod = megaco_ber_bin_encoder,
+ EncMod = megaco_ber_encoder,
EncConf = [],
rarpaop_mgc_event_sequence(Port, TranspMod, EncMod, EncConf).
@@ -1680,7 +1680,7 @@ rarpaop_mg_event_sequence(text, tcp) ->
rarpaop_mg_event_sequence(Port, EncMod, EncConf);
rarpaop_mg_event_sequence(binary, tcp) ->
Port = 2945,
- EncMod = megaco_ber_bin_encoder,
+ EncMod = megaco_ber_encoder,
EncConf = [],
rarpaop_mg_event_sequence(Port, EncMod, EncConf).
diff --git a/lib/megaco/test/megaco_mib_test.erl b/lib/megaco/test/megaco_mib_test.erl
index 52d99d1442..ddc74ab741 100644
--- a/lib/megaco/test/megaco_mib_test.erl
+++ b/lib/megaco/test/megaco_mib_test.erl
@@ -647,13 +647,13 @@ mk_recv_info([{text,udp}|ET], Acc) ->
{port, 2944}],
mk_recv_info(ET, [RI|Acc]);
mk_recv_info([{binary,tcp}|ET], Acc) ->
- RI = [{encoding_module, megaco_ber_bin_encoder},
+ RI = [{encoding_module, megaco_ber_encoder},
{encoding_config, []},
{transport_module, megaco_tcp},
{port, 2945}],
mk_recv_info(ET, [RI|Acc]);
mk_recv_info([{binary,udp}|ET], Acc) ->
- RI = [{encoding_module, megaco_ber_bin_encoder},
+ RI = [{encoding_module, megaco_ber_encoder},
{encoding_config, []},
{transport_module, megaco_udp},
{port, 2945}],
@@ -1013,7 +1013,7 @@ start_mg(Node, Mid, Encoding, Transport, Verbosity) ->
{encoding_config, []},
{port,2944}];
binary ->
- [{encoding_module, megaco_ber_bin_encoder},
+ [{encoding_module, megaco_ber_encoder},
{encoding_config, []},
{port,2945}]
end,
diff --git a/lib/megaco/test/megaco_test_mg.erl b/lib/megaco/test/megaco_test_mg.erl
index ecb3cedc83..947f0eebbb 100644
--- a/lib/megaco/test/megaco_test_mg.erl
+++ b/lib/megaco/test/megaco_test_mg.erl
@@ -158,7 +158,7 @@ select_encoding(pretty_text) ->
select_encoding(compact_text) ->
{megaco_compact_text_encoder, 2944};
select_encoding(binary) ->
- {megaco_ber_bin_encoder, 2945};
+ {megaco_ber_encoder, 2945};
select_encoding(erl_dist) ->
{megaco_erl_dist_encoder, 2946};
select_encoding(Encoding) ->
diff --git a/lib/megaco/test/megaco_test_mgc.erl b/lib/megaco/test/megaco_test_mgc.erl
index 13c1cebe56..a964983861 100644
--- a/lib/megaco/test/megaco_test_mgc.erl
+++ b/lib/megaco/test/megaco_test_mgc.erl
@@ -135,7 +135,7 @@ select_encoding(pretty_text) ->
select_encoding(compact_text) ->
{megaco_compact_text_encoder, 2944};
select_encoding(binary) ->
- {megaco_ber_bin_encoder, 2945};
+ {megaco_ber_encoder, 2945};
select_encoding(erl_dist) ->
{megaco_erl_dist_encoder, 2946};
select_encoding(Encoding) ->
diff --git a/lib/megaco/vsn.mk b/lib/megaco/vsn.mk
index 11a951a23e..5e72ade769 100644
--- a/lib/megaco/vsn.mk
+++ b/lib/megaco/vsn.mk
@@ -18,6 +18,6 @@
# %CopyrightEnd%
APPLICATION = megaco
-MEGACO_VSN = 3.16.0.1
+MEGACO_VSN = 3.16.0.2
PRE_VSN =
APP_VSN = "$(APPLICATION)-$(MEGACO_VSN)$(PRE_VSN)"
diff --git a/lib/mnesia/doc/src/Makefile b/lib/mnesia/doc/src/Makefile
index 173e43ac1f..6a72b98ebc 100644
--- a/lib/mnesia/doc/src/Makefile
+++ b/lib/mnesia/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/mnesia/doc/src/notes.xml b/lib/mnesia/doc/src/notes.xml
index 612ccb5ee8..04b8c106fd 100644
--- a/lib/mnesia/doc/src/notes.xml
+++ b/lib/mnesia/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>1996</year><year>2011</year>
+ <year>1996</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -38,7 +38,37 @@
thus constitutes one section in this document. The title of each
section is the version number of Mnesia.</p>
- <section><title>Mnesia 4.7</title>
+ <section><title>Mnesia 4.7.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Add tests showing that trying to delete non-existing
+ object may corrupt the </p>
+ <p>
+ In case of bag tables, trying to delete a non-existing
+ object leads to the index becoming corrupt. This happens
+ if the non-existing object we try to delete happens to
+ share its key and index field value with a single
+ existing object in the table. Result: The index entry
+ corresponding to the existing object is removed.</p>
+ <p>
+ Prevent index from being corrupted if a nonexistent item
+ is deleted</p>
+ <p>
+ We have to ensure that we actually delete the last object
+ with a given (key, index) pair before removing the index.
+ Thanks to Bartlomiej Puzon</p>
+ <p>
+ Own Id: OTP-10220</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Mnesia 4.7</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/mnesia/examples/Makefile b/lib/mnesia/examples/Makefile
index 5379761c64..d9e6def283 100644
--- a/lib/mnesia/examples/Makefile
+++ b/lib/mnesia/examples/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2009. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/mnesia/examples/mnesia_tpcb.erl b/lib/mnesia/examples/mnesia_tpcb.erl
index 903c53a21c..f36b43a495 100644
--- a/lib/mnesia/examples/mnesia_tpcb.erl
+++ b/lib/mnesia/examples/mnesia_tpcb.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
@@ -99,9 +99,13 @@
replica_test/1,
sticky_replica_test/1,
remote_test/1,
- remote_frag2_test/1
+ remote_frag2_test/1,
+
+ conflict_benchmark/1
]).
+-include_lib("common_test/include/ct_event.hrl").
+
-define(SECOND, 1000000).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -191,8 +195,10 @@
driver_nodes = [node()],
n_drivers_per_node = 1,
use_running_mnesia = false,
+ seed,
stop_after = timer:minutes(15), % Minimum 15 min
report_interval = timer:minutes(1),
+ send_bench_report = false,
use_sticky_locks = false,
spawn_near_branch = false,
activity_type = transaction,
@@ -397,8 +403,30 @@ config(remote_frag2_test, 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_benchmark, ReplicaType) ->
+ Remote = nodes(),
+ Local = node(),
+ Nodes = [Local | Remote],
+ [{seed, {1326,448637,337711}},
+ {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)},
+ {send_bench_report, true},
+ {reuse_history_id, true}
].
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
start(What, ReplicaType) ->
@@ -422,6 +450,9 @@ remote_test(ReplicaType) ->
remote_frag2_test(ReplicaType) ->
start(remote_frag2_test, ReplicaType).
+conflict_benchmark(ReplicaType) ->
+ start(config(conflict_benchmark, 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.
@@ -866,6 +897,7 @@ add_time(Acc, New) ->
show_report(State) ->
Now = now_to_micros(erlang:now()),
Iters = State#reporter_state.n_iters,
+ Cfg = State#reporter_state.run_config,
Time = State#reporter_state.curr,
Max = Time#time.max_time,
N = Time#time.n_trans,
@@ -888,7 +920,17 @@ show_report(State) ->
"duration of longest transaction was ~p milliseconds~n",
[Tps, BruttoTps, Max div 1000])
end,
- State#reporter_state{prev_tps = Tps, prev_micros = Now}.
+ case Cfg#run_config.send_bench_report of
+ true ->
+ ct_event:notify(
+ #event{name = benchmark_data,
+ data = [{suite,"mnesia_tpcb"},
+ {value,Tps}]});
+ _ ->
+ ok
+ end,
+
+ State#reporter_state{prev_tps = Tps, prev_micros = Now}.
signed_diff(Iters, Curr, Prev) ->
case Iters > 1 of
@@ -955,7 +997,13 @@ alloc_local_branches([], Specs, OrphanBranches) ->
{Specs, OrphanBranches}.
driver_init(DS, AllBranches) ->
- Seed = erlang:now(),
+ case (DS#driver_state.run_config)#run_config.seed of
+ undefined ->
+ Seed = erlang:now();
+ Seed ->
+ Seed
+ end,
+
DS2 =
if
DS#driver_state.n_local_branches =:= 0 ->
diff --git a/lib/mnesia/include/Makefile b/lib/mnesia/include/Makefile
index 20f62b5460..eea28045ea 100644
--- a/lib/mnesia/include/Makefile
+++ b/lib/mnesia/include/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1998-2009. All Rights Reserved.
+# Copyright Ericsson AB 1998-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/mnesia/include/mnemosyne.hrl b/lib/mnesia/include/mnemosyne.hrl
deleted file mode 100644
index eb6ec53ae1..0000000000
--- a/lib/mnesia/include/mnemosyne.hrl
+++ /dev/null
@@ -1,18 +0,0 @@
-%% ``The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance 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({parse_transform,mnemosyne}).
diff --git a/lib/mnesia/src/Makefile b/lib/mnesia/src/Makefile
index 1432eabc37..53d327c11b 100644
--- a/lib/mnesia/src/Makefile
+++ b/lib/mnesia/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2011. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -120,10 +120,10 @@ $(TARGET_FILES): $(HRL_FILES)
# ----------------------------------------------------
$(APP_TARGET): $(APP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
# ----------------------------------------------------
diff --git a/lib/mnesia/src/mnesia.appup.src b/lib/mnesia/src/mnesia.appup.src
index 304a15242f..355aafb215 100644
--- a/lib/mnesia/src/mnesia.appup.src
+++ b/lib/mnesia/src/mnesia.appup.src
@@ -1,6 +1,9 @@
%% -*- erlang -*-
{"%VSN%",
[
+ {"4.7.1", [{restart_application, mnesia}]},
+ {"4.7", [{restart_application, mnesia}]},
+ {"4.6", [{restart_application, mnesia}]},
{"4.5.1", [{restart_application, mnesia}]},
{"4.5", [{restart_application, mnesia}]},
{"4.4.19", [{restart_application, mnesia}]},
@@ -9,6 +12,9 @@
{"4.4.16", [{restart_application, mnesia}]}
],
[
+ {"4.7.1", [{restart_application, mnesia}]},
+ {"4.7", [{restart_application, mnesia}]},
+ {"4.6", [{restart_application, mnesia}]},
{"4.5.1", [{restart_application, mnesia}]},
{"4.5", [{restart_application, mnesia}]},
{"4.4.19", [{restart_application, mnesia}]},
diff --git a/lib/mnesia/src/mnesia.erl b/lib/mnesia/src/mnesia.erl
index 3d30debc53..3488d5c99b 100644
--- a/lib/mnesia/src/mnesia.erl
+++ b/lib/mnesia/src/mnesia.erl
@@ -2186,7 +2186,6 @@ system_info2(dump_log_time_threshold) -> mnesia_monitor:get_env(dump_log_time_th
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();
@@ -2224,7 +2223,6 @@ system_info_items(yes) ->
dump_log_time_threshold,
dump_log_update_in_place,
dump_log_write_threshold,
- embedded_mnemosyne,
event_module,
extra_db_nodes,
fallback_activated,
diff --git a/lib/mnesia/src/mnesia_controller.erl b/lib/mnesia/src/mnesia_controller.erl
index d488a33d67..ec67d9ec12 100644
--- a/lib/mnesia/src/mnesia_controller.erl
+++ b/lib/mnesia/src/mnesia_controller.erl
@@ -593,6 +593,12 @@ multicall(Nodes, Msg) ->
{PatchedGood, Bad}. %% Make the replies look like rpc:multicalls..
%% rpc:multicall(Nodes, ?MODULE, call, [Msg]).
+next_async_dump_log() ->
+ Interval = mnesia_monitor:get_env(dump_log_time_threshold),
+ Msg = {next_async_dump_log, time_threshold},
+ Ref = erlang:send_after(Interval, self(), Msg),
+ Ref.
+
%%%----------------------------------------------------------------------
%%% Callback functions from gen_server
%%%----------------------------------------------------------------------
@@ -614,9 +620,7 @@ init([Parent]) ->
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),
+ Ref = next_async_dump_log(),
mnesia_dumper:start_regulator(),
Empty = gb_trees:empty(),
@@ -1121,6 +1125,11 @@ handle_sync_tabs([], _From) ->
%% {stop, Reason, State} (terminate/2 is called)
%%----------------------------------------------------------------------
+handle_info({next_async_dump_log, InitBy}, State) ->
+ async_dump_log(InitBy),
+ Ref = next_async_dump_log(),
+ noreply(State#state{dump_log_timer_ref=Ref});
+
handle_info({async_dump_log, InitBy}, State) ->
Worker = #dump_log{initiated_by = InitBy},
State2 = add_worker(Worker, State),
diff --git a/lib/mnesia/src/mnesia_event.erl b/lib/mnesia/src/mnesia_event.erl
index 8085155fd5..9fd0342d31 100644
--- a/lib/mnesia/src/mnesia_event.erl
+++ b/lib/mnesia/src/mnesia_event.erl
@@ -153,7 +153,7 @@ handle_system_event({mnesia_down, Node}, State) ->
end;
handle_system_event({mnesia_overload, Details}, State) ->
- report_warning("Mnesia is overloaded: ~p~n", [Details]),
+ report_warning("Mnesia is overloaded: ~w~n", [Details]),
{ok, State};
handle_system_event({mnesia_info, Format, Args}, State) ->
diff --git a/lib/mnesia/src/mnesia_index.erl b/lib/mnesia/src/mnesia_index.erl
index 61210d7e55..f9f3ce2ea4 100644
--- a/lib/mnesia/src/mnesia_index.erl
+++ b/lib/mnesia/src/mnesia_index.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -120,9 +120,9 @@ del_object_bag(Tab, Key, Obj, Pos, Ixt, undefined) ->
IxKey = element(Pos, Obj),
Old = [X || X <- mnesia_lib:db_get(Tab, Key), element(Pos, X) =:= IxKey],
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 ->
+%% If Tab type is bag we need remove index identifier if the object being
+%% deleted was the last one
+del_object_bag(_Tab, Key, Obj, Pos, Ixt, Old) when Old =:= [Obj] ->
del_ixes(Ixt, [Obj], Pos, Key);
del_object_bag(_Tab, _Key, _Obj, _Pos, _Ixt, _Old) -> ok.
diff --git a/lib/mnesia/src/mnesia_locker.erl b/lib/mnesia/src/mnesia_locker.erl
index a22c95d454..14011003d3 100644
--- a/lib/mnesia/src/mnesia_locker.erl
+++ b/lib/mnesia/src/mnesia_locker.erl
@@ -1177,9 +1177,9 @@ system_code_change(State, _Module, _OldVsn, _Extra) ->
%% AXD301 patch sort pids according to R9B sort order
%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% Om R9B == true, g�rs j�mf�relsen som i R9B plain.
-%% Om R9B == false, g�rs j�mf�relsen som i alla andra releaser.
-%% cmp_tid(T1, T2) returnerar -1 om T1 < T2, 0 om T1 = T2 och 1 om T1 > T2.
+%% Om R9B == true, the comparison is done as in R9B plain.
+%% Om R9B == false, the comparison is done as in any other release.
+%% cmp_tid(T1, T2) returns -1 if T1 < T2, 0 if T1 = T2 and 1 if T1 > T2.
-define(VERSION_MAGIC, 131).
-define(ATOM_EXT, 100).
diff --git a/lib/mnesia/src/mnesia_monitor.erl b/lib/mnesia/src/mnesia_monitor.erl
index c08bbc879f..d940bd1cb7 100644
--- a/lib/mnesia/src/mnesia_monitor.erl
+++ b/lib/mnesia/src/mnesia_monitor.erl
@@ -673,7 +673,6 @@ env() ->
dump_log_time_threshold,
dump_log_update_in_place,
dump_log_write_threshold,
- embedded_mnemosyne,
event_module,
extra_db_nodes,
ignore_fallback_at_startup,
@@ -706,8 +705,6 @@ 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) ->
@@ -757,7 +754,6 @@ do_check_type(event_module, A) when is_atom(A) -> A;
do_check_type(ignore_fallback_at_startup, B) -> bool(B);
do_check_type(fallback_error_function, {Mod, Func})
when is_atom(Mod), is_atom(Func) -> {Mod, Func};
-do_check_type(embedded_mnemosyne, B) -> bool(B);
do_check_type(extra_db_nodes, L) when is_list(L) ->
Fun = fun(N) when N == node() -> false;
(A) when is_atom(A) -> true
diff --git a/lib/mnesia/src/mnesia_recover.erl b/lib/mnesia/src/mnesia_recover.erl
index 4750291a10..b64f428f15 100644
--- a/lib/mnesia/src/mnesia_recover.erl
+++ b/lib/mnesia/src/mnesia_recover.erl
@@ -45,7 +45,8 @@
note_log_decision/2,
outcome/2,
start/0,
- start_garb/0,
+ next_garb/0,
+ next_check_overload/0,
still_pending/1,
sync_trans_tid_serial/1,
sync/0,
@@ -91,10 +92,38 @@ start() ->
init() ->
call(init).
-start_garb() ->
+next_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).
+ erlang:send_after(timer:minutes(2), Pid, garb_decisions).
+
+next_check_overload() ->
+ Pid = whereis(mnesia_recover),
+ erlang:send_after(timer:seconds(10), Pid, check_overload).
+
+
+do_check_overload(S) ->
+ %% Time to check if mnesia_tm is overloaded
+ case whereis(mnesia_tm) of
+ Pid when is_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}),
+ mnesia_lib:overload_set(mnesia_tm, true),
+ S#state{tm_queue_len = 0};
+ Len > Threshold ->
+ S#state{tm_queue_len = Len};
+ true ->
+ mnesia_lib:overload_set(mnesia_tm, false),
+ S#state{tm_queue_len = 0}
+ end;
+ undefined ->
+ S
+ end.
allow_garb() ->
cast(allow_garb).
@@ -853,34 +882,13 @@ handle_info({connect_nodes, Ns, From}, State) ->
handle_call({connect_nodes,Ns},From,State);
handle_info(check_overload, S) ->
- %% Time to check if mnesia_tm is overloaded
- case whereis(mnesia_tm) of
- Pid when is_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}),
- 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 ->
- {noreply, S}
- end;
+ State2 = do_check_overload(S),
+ next_check_overload(),
+ {noreply, State2};
handle_info(garb_decisions, State) ->
do_garb_decisions(),
+ next_garb(),
{noreply, State};
handle_info({force_decision, Tid}, State) ->
diff --git a/lib/mnesia/src/mnesia_sup.erl b/lib/mnesia/src/mnesia_sup.erl
index 9ee4086f50..ef858d2364 100644
--- a/lib/mnesia/src/mnesia_sup.erl
+++ b/lib/mnesia/src/mnesia_sup.erl
@@ -60,9 +60,8 @@ init() ->
Event = event_procs(),
Kernel = kernel_procs(),
- Mnemosyne = mnemosyne_procs(),
- {ok, {Flags, Event ++ Kernel ++ Mnemosyne}}.
+ {ok, {Flags, Event ++ Kernel}}.
event_procs() ->
KillAfter = timer:seconds(30),
@@ -75,16 +74,6 @@ kernel_procs() ->
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
diff --git a/lib/mnesia/src/mnesia_tm.erl b/lib/mnesia/src/mnesia_tm.erl
index 0af7f55c06..b5b14ac05b 100644
--- a/lib/mnesia/src/mnesia_tm.erl
+++ b/lib/mnesia/src/mnesia_tm.erl
@@ -103,7 +103,8 @@ init(Parent) ->
end,
mnesia_schema:purge_tmp_files(),
- mnesia_recover:start_garb(),
+ mnesia_recover:next_garb(),
+ mnesia_recover:next_check_overload(),
?eval_debug_fun({?MODULE, init}, [{nodes, AllOthers}]),
diff --git a/lib/mnesia/test/Makefile b/lib/mnesia/test/Makefile
index 0451da697d..45ce5b1983 100644
--- a/lib/mnesia/test/Makefile
+++ b/lib/mnesia/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2011. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -26,6 +26,7 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk
MODULES= \
mt \
mnesia_SUITE \
+ mnesia_bench_SUITE \
mnesia_test_lib \
mnesia_install_test \
mnesia_registry_test \
@@ -117,7 +118,7 @@ release_spec: opt
release_tests_spec: opt
$(INSTALL_DIR) "$(RELSYSDIR)"
- $(INSTALL_DATA) mnesia.spec mnesia.cover $(ERL_FILES) $(HRL_FILES) "$(RELSYSDIR)"
+ $(INSTALL_DATA) mnesia.spec mnesia_bench.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 -)
diff --git a/lib/mnesia/test/mnesia.spec b/lib/mnesia/test/mnesia.spec
index 204d1519cb..653e515317 100644
--- a/lib/mnesia/test/mnesia.spec
+++ b/lib/mnesia/test/mnesia.spec
@@ -42,9 +42,6 @@
{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"}.
diff --git a/lib/mnesia/test/mnesia.spec.vxworks b/lib/mnesia/test/mnesia.spec.vxworks
deleted file mode 100644
index 11c01ea3fe..0000000000
--- a/lib/mnesia/test/mnesia.spec.vxworks
+++ /dev/null
@@ -1,362 +0,0 @@
-{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
index 2267a94164..5dbb80d4eb 100644
--- a/lib/mnesia/test/mnesia_SUITE.erl
+++ b/lib/mnesia/test/mnesia_SUITE.erl
@@ -105,7 +105,6 @@ groups() ->
{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},
diff --git a/lib/mnesia/test/mnesia_bench.spec b/lib/mnesia/test/mnesia_bench.spec
new file mode 100644
index 0000000000..7b17cb5c4e
--- /dev/null
+++ b/lib/mnesia/test/mnesia_bench.spec
@@ -0,0 +1 @@
+{suites,"../mnesia_test",[mnesia_bench_SUITE]}. \ No newline at end of file
diff --git a/lib/mnesia/test/mnesia_bench_SUITE.erl b/lib/mnesia/test/mnesia_bench_SUITE.erl
new file mode 100644
index 0000000000..7cbf77f046
--- /dev/null
+++ b/lib/mnesia/test/mnesia_bench_SUITE.erl
@@ -0,0 +1,69 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, 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_bench_SUITE).
+-author('[email protected]').
+-compile(export_all).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+suite() -> [{ct_hooks,[{ts_install_cth,[{nodenames,2}]}]}].
+
+
+all() ->
+ [{group,tpcb}].
+
+groups() ->
+ [{tpcb,[{repeat,2}],[tpcb_conflict_ramcopies,
+ tpcb_conflict_disk_only_copies]}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(Config) ->
+ Config.
+
+init_per_testcase(_Func, Conf) ->
+ Conf.
+
+end_per_testcase(_Func, _Conf) ->
+ ok.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+tpcb_conflict_ramcopies(_Config) ->
+ mnesia_tpcb:conflict_benchmark(ram_copies).
+
+tpcb_conflict_disk_only_copies(_Config) ->
+ mnesia_tpcb:conflict_benchmark(disc_only_copies).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+
+
+
+
diff --git a/lib/mnesia/test/mnesia_config_test.erl b/lib/mnesia/test/mnesia_config_test.erl
index 93510d539c..fd294780da 100644
--- a/lib/mnesia/test/mnesia_config_test.erl
+++ b/lib/mnesia/test/mnesia_config_test.erl
@@ -36,7 +36,6 @@
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,
@@ -104,7 +103,7 @@ 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,
+ dump_log_update_in_place,
event_module, ignore_fallback_at_startup,
inconsistent_database, max_wait_for_decision,
send_compressed, app_test, {group, schema_config},
@@ -610,45 +609,6 @@ dump_log_load_regulation(Config) when is_list(Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-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",
diff --git a/lib/mnesia/test/mnesia_consistency_test.erl b/lib/mnesia/test/mnesia_consistency_test.erl
index bb21723e27..922b89ec2b 100644
--- a/lib/mnesia/test/mnesia_consistency_test.erl
+++ b/lib/mnesia/test/mnesia_consistency_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/mnesia/test/mnesia_dirty_access_test.erl b/lib/mnesia/test/mnesia_dirty_access_test.erl
index abbdab48c0..6017092095 100644
--- a/lib/mnesia/test/mnesia_dirty_access_test.erl
+++ b/lib/mnesia/test/mnesia_dirty_access_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -527,6 +527,9 @@ dirty_index_update_bag(Config, Storage) ->
?match(ok, mnesia:dirty_write(Rec1)),
?match([Rec1], mnesia:dirty_index_read(Tab, 2, ValPos)),
+ ?match(ok, mnesia:dirty_delete_object(Rec5)),
+ ?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)),
diff --git a/lib/mnesia/test/mnesia_measure_test.erl b/lib/mnesia/test/mnesia_measure_test.erl
index e63689d83a..dfed302814 100644
--- a/lib/mnesia/test/mnesia_measure_test.erl
+++ b/lib/mnesia/test/mnesia_measure_test.erl
@@ -72,8 +72,7 @@ groups() ->
resource_consumption_at_full_load]},
{benchmarks, [],
[{group, meter}, cost, dbn_meters,
- measure_all_api_functions, {group, tpcb},
- mnemosyne_vs_mnesia_kernel]},
+ measure_all_api_functions, {group, tpcb}]},
{tpcb, [], [ram_tpcb, disc_tpcb, disc_only_tpcb]},
{meter, [], [ram_meter, disc_meter, disc_only_meter]}].
diff --git a/lib/mnesia/test/mnesia_recovery_test.erl b/lib/mnesia/test/mnesia_recovery_test.erl
index 625e6e824c..c4910a4b11 100644
--- a/lib/mnesia/test/mnesia_recovery_test.erl
+++ b/lib/mnesia/test/mnesia_recovery_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -99,21 +99,21 @@ groups() ->
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]},
+ [asymtrans_part_ask,
+ asymtrans_part_commit_vote,
+ asymtrans_part_pre_commit,
+ asymtrans_part_log_commit,
+ asymtrans_part_do_commit,
+ asymtrans_coord_got_votes,
+ asymtrans_coord_pid_got_votes,
+ asymtrans_coord_log_commit_rec,
+ asymtrans_coord_pid_log_commit_rec,
+ asymtrans_coord_log_commit_dec,
+ asymtrans_coord_pid_log_commit_dec,
+ asymtrans_coord_rec_acc_pre_commit_log_commit,
+ asymtrans_coord_pid_rec_acc_pre_commit_log_commit,
+ asymtrans_coord_rec_acc_pre_commit_done_commit,
+ asymtrans_coord_pid_rec_acc_pre_commit_done_commit]},
{after_corrupt_files, [],
[after_corrupt_files_decision_log_head,
after_corrupt_files_decision_log_tail,
@@ -978,8 +978,8 @@ do_async_dirty([Tab], _Fahter) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-asym_trans_kill_part_ask(suite) -> [];
-asym_trans_kill_part_ask(Config) when is_list(Config) ->
+asymtrans_part_ask(suite) -> [];
+asymtrans_part_ask(Config) when is_list(Config) ->
?is_debug_compiled,
Nodes = ?acquire_nodes(3, Config ++ [{tc_timeout, timer:minutes(2)}]),
[Coord, Part1, Part2] = Nodes,
@@ -989,8 +989,8 @@ asym_trans_kill_part_ask(Config) when is_list(Config) ->
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) ->
+asymtrans_part_commit_vote(suite) -> [];
+asymtrans_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,
@@ -1000,8 +1000,8 @@ asym_trans_kill_part_commit_vote(Config) when is_list(Config) ->
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) ->
+asymtrans_part_pre_commit(suite) -> [];
+asymtrans_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,
@@ -1011,8 +1011,8 @@ asym_trans_kill_part_pre_commit(Config) when is_list(Config) ->
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) ->
+asymtrans_part_log_commit(suite) -> [];
+asymtrans_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,
@@ -1022,8 +1022,8 @@ asym_trans_kill_part_log_commit(Config) when is_list(Config) ->
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) ->
+asymtrans_part_do_commit(suite) -> [];
+asymtrans_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,
@@ -1033,8 +1033,8 @@ asym_trans_kill_part_do_commit(Config) when is_list(Config) ->
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) ->
+asymtrans_coord_got_votes(suite) -> [];
+asymtrans_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,
@@ -1044,8 +1044,8 @@ asym_trans_kill_coord_got_votes(Config) when is_list(Config) ->
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) ->
+asymtrans_coord_pid_got_votes(suite) -> [];
+asymtrans_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,
@@ -1055,8 +1055,8 @@ asym_trans_kill_coord_pid_got_votes(Config) when is_list(Config) ->
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) ->
+asymtrans_coord_log_commit_rec(suite) -> [];
+asymtrans_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,
@@ -1066,8 +1066,8 @@ asym_trans_kill_coord_log_commit_rec(Config) when is_list(Config) ->
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) ->
+asymtrans_coord_pid_log_commit_rec(suite) -> [];
+asymtrans_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,
@@ -1077,8 +1077,8 @@ asym_trans_kill_coord_pid_log_commit_rec(Config) when is_list(Config) ->
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) ->
+asymtrans_coord_log_commit_dec(suite) -> [];
+asymtrans_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,
@@ -1088,8 +1088,8 @@ asym_trans_kill_coord_log_commit_dec(Config) when is_list(Config) ->
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) ->
+asymtrans_coord_pid_log_commit_dec(suite) -> [];
+asymtrans_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,
@@ -1099,8 +1099,8 @@ asym_trans_kill_coord_pid_log_commit_dec(Config) when is_list(Config) ->
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) ->
+asymtrans_coord_rec_acc_pre_commit_log_commit(suite) -> [];
+asymtrans_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,
@@ -1110,8 +1110,8 @@ asym_trans_kill_coord_rec_acc_pre_commit_log_commit(Config) when is_list(Config)
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) ->
+asymtrans_coord_pid_rec_acc_pre_commit_log_commit(suite) -> [];
+asymtrans_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,
@@ -1121,8 +1121,8 @@ asym_trans_kill_coord_pid_rec_acc_pre_commit_log_commit(Config) when is_list(Con
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) ->
+asymtrans_coord_rec_acc_pre_commit_done_commit(suite) -> [];
+asymtrans_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,
@@ -1132,8 +1132,8 @@ asym_trans_kill_coord_rec_acc_pre_commit_done_commit(Config) when is_list(Config
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) ->
+asymtrans_coord_pid_rec_acc_pre_commit_done_commit(suite) -> [];
+asymtrans_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,
diff --git a/lib/mnesia/test/mnesia_test_lib.erl b/lib/mnesia/test/mnesia_test_lib.erl
index ba5bf84e24..57cbc61495 100644
--- a/lib/mnesia/test/mnesia_test_lib.erl
+++ b/lib/mnesia/test/mnesia_test_lib.erl
@@ -272,25 +272,13 @@ slave_start_link(Host, Name, Retries) ->
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:start(Host, Name, Args).
slave_sup() ->
process_flag(trap_exit, true),
receive
{'EXIT', _, _} ->
- case os:type() of
- vxworks ->
- erlang:halt();
- _ ->
- ignore
- end
+ ignore
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/mnesia/test/mnesia_trans_access_test.erl b/lib/mnesia/test/mnesia_trans_access_test.erl
index c040d0ca3f..157e441b27 100644
--- a/lib/mnesia/test/mnesia_trans_access_test.erl
+++ b/lib/mnesia/test/mnesia_trans_access_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -896,6 +896,10 @@ index_update_bag(Config)when is_list(Config) ->
?match({atomic, [Rec1]},
mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end)),
+ ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:delete_object(Rec5) 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),
diff --git a/lib/mnesia/vsn.mk b/lib/mnesia/vsn.mk
index da292cbb43..6d5df448c9 100644
--- a/lib/mnesia/vsn.mk
+++ b/lib/mnesia/vsn.mk
@@ -1 +1 @@
-MNESIA_VSN = 4.7
+MNESIA_VSN = 4.7.1
diff --git a/lib/observer/doc/src/notes.xml b/lib/observer/doc/src/notes.xml
index a9554a08f4..4ec03782a7 100644
--- a/lib/observer/doc/src/notes.xml
+++ b/lib/observer/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2004</year><year>2011</year>
+ <year>2004</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -31,6 +31,75 @@
<p>This document describes the changes made to the Observer
application.</p>
+<section><title>Observer 1.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The module name in the link to the detail page for each
+ loaded module was earlier not URL encoded. If the module
+ name contained e.g. a # this could cause the crashdump
+ viewer to crash when opening the link. This has been
+ corrected.</p>
+ <p>
+ Own Id: OTP-10090 Aux Id: seq12068 </p>
+ </item>
+ <item>
+ <p>
+ Escape control characters in Table Viewer</p>
+ <p>
+ Similar behaviour to old tv. Objects in tables supposed
+ to be printed in a single line and it looks ugly when a
+ [...,10,...] integer list creates a new-line. Fix Table
+ Viewer search crash on new|changed|deleted rows.</p>
+ <p>
+ Fix Table Viewer crash after a 'Found' -&gt; 'Not found'
+ search sequence</p>
+ <p>
+ Start position was lost after a 'Found' -&gt; 'Not found'
+ search sequence leading an undefined position in the next
+ search. Thanks to Peti G�mori</p>
+ <p>
+ Own Id: OTP-10218</p>
+ </item>
+ <item>
+ <p>
+ observer: fix app file (Noticed-by: Motiejus Jakstys)</p>
+ <p>
+ Add missing observer modules to observer.app.src. Thanks
+ to Tuncer Ayaz.</p>
+ <p>
+ Own Id: OTP-10221</p>
+ </item>
+ <item>
+ <p>
+ Make Table Viewer search a bit faster</p>
+ <p>
+ Edit table row in a multiline text dialog. Thanks to Peti
+ Gomori.</p>
+ <p>
+ Own Id: OTP-10225</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>Allow tracing on bifs.</p> <p>Ask epmd for local
+ nodes, and remember users last input in connect.</p>
+ <p>Fix crashes when a table or process information could
+ not be retrieved.</p>
+ <p>
+ Own Id: OTP-10075</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Observer 1.0</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/observer/src/Makefile b/lib/observer/src/Makefile
index 7135a6abd5..877286033e 100644
--- a/lib/observer/src/Makefile
+++ b/lib/observer/src/Makefile
@@ -114,10 +114,10 @@ clean:
rm -f errs core *~
$(APP_TARGET): $(APP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
$(TARGET_FILES): $(INTERNAL_HRL_FILES)
diff --git a/lib/observer/src/observer.app.src b/lib/observer/src/observer.app.src
index 5c65ea5c8f..e881cb3c97 100644
--- a/lib/observer/src/observer.app.src
+++ b/lib/observer/src/observer.app.src
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -25,6 +25,18 @@
etop_gui,
etop_tr,
etop_txt,
+ observer,
+ observer_app_wx,
+ observer_lib,
+ observer_perf_wx,
+ observer_pro_wx,
+ observer_procinfo,
+ observer_sys_wx,
+ observer_trace_wx,
+ observer_traceoptions_wx,
+ observer_tv_table,
+ observer_tv_wx,
+ observer_wx,
ttb,
ttb_et]},
{registered, []},
diff --git a/lib/observer/src/observer_app_wx.erl b/lib/observer/src/observer_app_wx.erl
index f9be11e05a..72bafcc5e0 100644
--- a/lib/observer/src/observer_app_wx.erl
+++ b/lib/observer/src/observer_app_wx.erl
@@ -28,7 +28,7 @@
-include("observer_defs.hrl").
%% Import drawing wrappers
--import(observer_perf_wx, [haveGC/1,
+-import(observer_perf_wx, [haveGC/0,
setPen/2, setFont/3, setBrush/2,
strokeLine/5, strokeLines/2, drawRoundedRectangle/6,
drawText/4, getTextExtent/2]).
@@ -114,9 +114,10 @@ init([Notebook, Parent]) ->
_ -> ok
end,
- UseGC = haveGC(DrawingArea),
+ UseGC = haveGC(),
+ Version28 = ?wxMAJOR_VERSION =:= 2 andalso ?wxMINOR_VERSION =:= 8,
Font = case os:type() of
- {unix,_} when UseGC ->
+ {unix,_} when UseGC, Version28 ->
wxFont:new(12,?wxFONTFAMILY_DECORATIVE,?wxFONTSTYLE_NORMAL,?wxFONTWEIGHT_NORMAL);
_ ->
wxSystemSettings:getFont(?wxSYS_DEFAULT_GUI_FONT)
@@ -267,9 +268,15 @@ handle_call(Event, From, _State) ->
handle_cast(Event, _State) ->
error({unhandled_cast, Event}).
%%%%%%%%%%
-handle_info({active, Node}, State = #state{parent=Parent, current=Curr}) ->
+handle_info({active, Node}, State = #state{parent=Parent, current=Curr, appmon=Appmon}) ->
create_menus(Parent, []),
- {ok, Pid} = appmon_info:start_link(Node, self(), []),
+ Pid = try
+ Node = node(Appmon),
+ Appmon
+ catch _:_ ->
+ {ok, P} = appmon_info:start_link(Node, self(), []),
+ P
+ end,
appmon_info:app_ctrl(Pid, Node, true, []),
(Curr =/= undefined) andalso appmon_info:app(Pid, Curr, true, []),
{noreply, State#state{appmon=Pid}};
diff --git a/lib/observer/src/observer_lib.erl b/lib/observer/src/observer_lib.erl
index 3b924d46cf..4077f8371a 100644
--- a/lib/observer/src/observer_lib.erl
+++ b/lib/observer/src/observer_lib.erl
@@ -19,7 +19,7 @@
-module(observer_lib).
-export([get_wx_parent/1,
- display_info_dialog/1, user_term/3,
+ display_info_dialog/1, user_term/3, user_term_multiline/3,
interval_dialog/4, start_timer/1, stop_timer/1,
display_info/2, fill_info/2, update_info/2, to_str/1,
create_menus/3, create_menu_item/3,
@@ -347,6 +347,58 @@ user_term(Parent, Title, Default) ->
cancel
end.
+user_term_multiline(Parent, Title, Default) ->
+ Dialog = wxDialog:new(Parent, ?wxID_ANY, Title,
+ [{style, ?wxDEFAULT_DIALOG_STYLE bor
+ ?wxRESIZE_BORDER}]),
+ Panel = wxPanel:new(Dialog),
+
+ TextCtrl = wxTextCtrl:new(Panel, ?wxID_ANY,
+ [{value, Default},
+ {style, ?wxDEFAULT bor ?wxTE_MULTILINE}]),
+ Line = wxStaticLine:new(Panel, [{style, ?wxLI_HORIZONTAL}]),
+
+ Buttons = wxDialog:createButtonSizer(Dialog, ?wxOK bor ?wxCANCEL),
+
+ InnerSizer = wxBoxSizer:new(?wxVERTICAL),
+ wxSizer:add(InnerSizer, TextCtrl,
+ [{flag, ?wxEXPAND bor ?wxALL},{proportion, 1},{border, 5}]),
+ wxSizer:add(InnerSizer, Line,
+ [{flag, ?wxEXPAND},{proportion, 0},{border, 5}]),
+ wxPanel:setSizer(Panel, InnerSizer),
+
+ TopSizer = wxBoxSizer:new(?wxVERTICAL),
+ wxSizer:add(TopSizer, Panel,
+ [{flag, ?wxEXPAND bor ?wxALL},{proportion, 1},{border, 5}]),
+ wxSizer:add(TopSizer, Buttons,
+ [{flag, ?wxEXPAND bor ?wxBOTTOM bor ?wxRIGHT},{border, 10}]),
+
+ % calculate the size of TopSizer when the whole user_term
+ % fits in the TextCtrl
+ DC = wxClientDC:new(Panel),
+ W = wxDC:getCharWidth(DC),
+ H = wxDC:getCharHeight(DC),
+ {EW, EH} = wxDC:getMultiLineTextExtent(DC, Default),
+ wxSizer:setItemMinSize(InnerSizer, 0, EW+2*W, EH+H),
+ TopSize = wxSizer:getMinSize(TopSizer),
+ % reset min size of TextCtrl to 40 chararacters * 4 lines
+ wxSizer:setItemMinSize(InnerSizer, 0, 40*W, 4*H),
+
+ wxWindow:setSizerAndFit(Dialog, TopSizer),
+ wxSizer:setSizeHints(TopSizer, Dialog),
+
+ wxWindow:setClientSize(Dialog, TopSize),
+
+ case wxDialog:showModal(Dialog) of
+ ?wxID_OK ->
+ Str = wxTextCtrl:getValue(TextCtrl),
+ wxDialog:destroy(Dialog),
+ parse_string(ensure_last_is_dot(Str));
+ ?wxID_CANCEL ->
+ wxDialog:destroy(Dialog),
+ cancel
+ end.
+
parse_string(Str) ->
try
Tokens = case erl_scan:string(Str) of
diff --git a/lib/observer/src/observer_perf_wx.erl b/lib/observer/src/observer_perf_wx.erl
index fa867e12f6..54c98f3ba3 100644
--- a/lib/observer/src/observer_perf_wx.erl
+++ b/lib/observer/src/observer_perf_wx.erl
@@ -24,7 +24,7 @@
handle_event/2, handle_sync_event/3, handle_cast/2]).
%% Drawing wrappers for DC and GC areas
--export([haveGC/1,
+-export([haveGC/0,
setPen/2, setFont/3, setBrush/2,
strokeLine/5, strokeLines/2, drawRoundedRectangle/6,
drawText/4, getTextExtent/2]).
@@ -90,11 +90,12 @@ init([Notebook, Parent]) ->
_ -> ok
end,
- UseGC = haveGC(Panel),
+ UseGC = haveGC(),
+ Version28 = ?wxMAJOR_VERSION =:= 2 andalso ?wxMINOR_VERSION =:= 8,
{Font, SmallFont}
= case os:type() of
- {unix, _} when UseGC ->
- %% Def font is really small when using Graphics contexts for some reason
+ {unix, _} when UseGC, Version28 ->
+ %% Def font is really small when using Graphics contexts in 2.8
%% Hardcode it
F = wxFont:new(12,?wxFONTFAMILY_DECORATIVE,?wxFONTSTYLE_NORMAL,?wxFONTWEIGHT_BOLD),
SF = wxFont:new(10, ?wxFONTFAMILY_DECORATIVE, ?wxFONTSTYLE_NORMAL, ?wxFONTWEIGHT_NORMAL),
@@ -123,7 +124,7 @@ init([Notebook, Parent]) ->
}}
catch _:Err ->
io:format("~p crashed ~p: ~p~n",[?MODULE, Err, erlang:get_stacktrace()]),
- {error, Err}
+ {stop, Err}
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -524,10 +525,9 @@ colors() ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% wxDC and ?wxGC wrappers
-haveGC(Win) ->
+haveGC() ->
try
- GC = ?wxGC:create(Win),
- ?wxGC:destroy(GC),
+ wxGraphicsRenderer:getDefaultRenderer(),
true
catch _:_ -> false
end.
diff --git a/lib/observer/src/observer_pro_wx.erl b/lib/observer/src/observer_pro_wx.erl
index e2f3ddb02b..9aaf648ea2 100644
--- a/lib/observer/src/observer_pro_wx.erl
+++ b/lib/observer/src/observer_pro_wx.erl
@@ -225,7 +225,7 @@ handle_info({holder_updated, Count}, State0=#state{grid=Grid}) ->
State = update_selection(State0),
wxListCtrl:setItemCount(Grid, Count),
- wxListCtrl:refreshItems(Grid, 0, Count-1),
+ Count > 0 andalso wxListCtrl:refreshItems(Grid, 0, Count-1),
{noreply, State};
@@ -258,8 +258,7 @@ terminate(_Reason, #state{holder=Holder}) ->
ok.
code_change(_, _, State) ->
- {stop, not_yet_implemented, State}.
-
+ {ok, State}.
handle_call(Msg, _From, State) ->
io:format("~p:~p: Unhandled call ~p~n",[?MODULE, ?LINE, Msg]),
diff --git a/lib/observer/src/observer_procinfo.erl b/lib/observer/src/observer_procinfo.erl
index 13e41cfe33..45218c177b 100644
--- a/lib/observer/src/observer_procinfo.erl
+++ b/lib/observer/src/observer_procinfo.erl
@@ -127,17 +127,15 @@ terminate(_Reason, #state{parent=Parent,pid=Pid,frame=Frame}) ->
ok.
code_change(_, _, State) ->
- {stop, not_yet_implemented, State}.
+ {ok, State}.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
init_process_page(Panel, Pid) ->
Fields0 = process_info_fields(Pid),
{FPanel, _, UpFields} = observer_lib:display_info(Panel, Fields0),
- {FPanel, fun() -> case process_info_fields(Pid) of
- Fields when is_list(Fields) ->
- observer_lib:update_info(UpFields, Fields);
- _ -> ok
- end
+ {FPanel, fun() ->
+ Fields = process_info_fields(Pid),
+ observer_lib:update_info(UpFields, Fields)
end}.
init_text_page(Parent) ->
diff --git a/lib/observer/src/observer_sys_wx.erl b/lib/observer/src/observer_sys_wx.erl
index 09602bbd9e..f00a666a35 100644
--- a/lib/observer/src/observer_sys_wx.erl
+++ b/lib/observer/src/observer_sys_wx.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011. All Rights Reserved.
+%% Copyright Ericsson AB 2011-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -147,7 +147,7 @@ terminate(_Reason, _State) ->
ok.
code_change(_, _, State) ->
- {stop, not_yet_implemented, State}.
+ {ok, State}.
handle_call(Msg, _From, State) ->
io:format("~p~p: Unhandled Call ~p~n",[?MODULE, ?LINE, Msg]),
diff --git a/lib/observer/src/observer_trace_wx.erl b/lib/observer/src/observer_trace_wx.erl
index d0b6a1e063..f2a1084f85 100644
--- a/lib/observer/src/observer_trace_wx.erl
+++ b/lib/observer/src/observer_trace_wx.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011. All Rights Reserved.
+%% Copyright Ericsson AB 2011-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -489,7 +489,7 @@ terminate(_Reason, #state{nodes=_Nodes}) ->
ok.
code_change(_, _, State) ->
- {stop, not_yet_implemented, State}.
+ {ok, State}.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
do_add_patterns({Module, NewPs}, State=#state{tpatterns=TPs0, m_view=Mview, f_view=Fview}) ->
diff --git a/lib/observer/src/observer_tv_table.erl b/lib/observer/src/observer_tv_table.erl
index 3930f9ee26..5d1ab2e946 100644
--- a/lib/observer/src/observer_tv_table.erl
+++ b/lib/observer/src/observer_tv_table.erl
@@ -24,6 +24,8 @@
-export([init/1, handle_info/2, terminate/2, code_change/3, handle_call/3,
handle_event/2, handle_sync_event/3, handle_cast/2]).
+-export([format/1]).
+
-include("observer_defs.hrl").
-import(observer_lib, [to_str/1]).
@@ -218,8 +220,8 @@ search_area(Parent) ->
search=TC1,goto=TC2,radio={Nbtn,Pbtn,Cbtn}}.
edit(Index, #state{pid=Pid, frame=Frame}) ->
- Str = get_row(Pid, Index, all),
- case observer_lib:user_term(Frame, "Edit object:", Str) of
+ Str = get_row(Pid, Index, all_multiline),
+ case observer_lib:user_term_multiline(Frame, "Edit object:", Str) of
cancel -> ok;
{ok, Term} -> Pid ! {edit, Index, Term};
Err = {error, _} -> self() ! Err
@@ -265,7 +267,8 @@ handle_event(#wx{id=?ID_DELETE},
wxStatusBar:setStatusText(StatusBar, io_lib:format("Deleted object: ~s",[Str])),
{noreply, State};
-handle_event(#wx{id=?wxID_CLOSE}, State) ->
+handle_event(#wx{id=?wxID_CLOSE}, State = #state{frame=Frame}) ->
+ wxFrame:destroy(Frame),
{stop, normal, State};
handle_event(Help = #wx{id=?wxID_HELP}, State) ->
@@ -321,7 +324,7 @@ handle_event(#wx{id=?SEARCH_ENTRY, event=#wxCommand{type=command_text_enter,cmdS
wxStatusBar:setStatusText(SB, "Not found"),
Pid ! {mark_search_hit, Find#find.start},
wxListCtrl:refreshItem(Grid, Find#find.start),
- {noreply, State#state{search=Search#search{find=#find{found=false}}}};
+ {noreply, State#state{search=Search#search{find=Find#find{found=false}}}};
Row ->
wxListCtrl:ensureVisible(Grid, Row),
wxListCtrl:refreshItem(Grid, Row),
@@ -400,7 +403,7 @@ handle_info({new_cols, New}, State = #state{grid=Grid, columns=Cols0}) ->
{noreply, State#state{columns=Cols}};
handle_info({refresh, Min, Max}, State = #state{grid=Grid}) ->
- wxListCtrl:refreshItems(Grid, Min, Max),
+ Max > 0 andalso wxListCtrl:refreshItems(Grid, Min, Max),
{noreply, State};
handle_info(refresh_interval, State = #state{pid=Pid}) ->
@@ -594,7 +597,7 @@ keysort(Col, Table) ->
lists:sort(Sort, Table).
search([Str, Row, Dir0, CaseSens],
- S=#holder{parent=Parent, table=Table}) ->
+ S=#holder{parent=Parent, table=Table0}) ->
Opt = case CaseSens of
true -> [];
false -> [caseless]
@@ -605,29 +608,35 @@ search([Str, Row, Dir0, CaseSens],
end,
Res = case re:compile(Str, Opt) of
{ok, Re} ->
+ Table =
+ case Dir0 of
+ true ->
+ lists:nthtail(Row, Table0);
+ false ->
+ lists:reverse(lists:sublist(Table0, Row+1))
+ end,
search(Row, Dir, Re, Table);
{error, _} -> false
end,
Parent ! {self(), Res},
S#holder{search=Res}.
-search(Row, Dir, Re, Table) ->
- Res = try lists:nth(Row+1, Table) of
- Term ->
- Str = format(Term),
- re:run(Str, Re)
- catch _:_ -> no_more
- end,
+search(Row, Dir, Re, [ [Term|_] |Table]) ->
+ Str = format(Term),
+ Res = re:run(Str, Re),
case Res of
nomatch -> search(Row+Dir, Dir, Re, Table);
- no_more -> false;
{match,_} -> Row
- end.
+ end;
+search(_, _, _, []) ->
+ false.
get_row(From, Row, Col, Table) ->
case lists:nth(Row+1, Table) of
[Object|_] when Col =:= all ->
From ! {self(), format(Object)};
+ [Object|_] when Col =:= all_multiline ->
+ From ! {self(), io_lib:format("~p", [Object])};
[Object|_] when tuple_size(Object) >= Col ->
From ! {self(), format(element(Col, Object))};
_ ->
@@ -747,6 +756,13 @@ format(List) when is_list(List) ->
format_list(List);
format(Bin) when is_binary(Bin), byte_size(Bin) > 100 ->
io_lib:format("<<#Bin:~w>>", [byte_size(Bin)]);
+format(Bin) when is_binary(Bin) ->
+ try
+ true = printable_list(unicode:characters_to_list(Bin)),
+ io_lib:format("<<\"~ts\">>", [Bin])
+ catch _:_ ->
+ io_lib:format("~w", [Bin])
+ end;
format(Float) when is_float(Float) ->
io_lib:format("~.3g", [Float]);
format(Term) ->
@@ -762,7 +778,7 @@ format_tuple(_Tuple, 1, 0) ->
format_list([]) -> "[]";
format_list(List) ->
case printable_list(List) of
- true -> io_lib:format("\"~ts\"", [List]);
+ true -> io_lib:format("\"~ts\"", [map_printable_list(List)]);
false -> [$[ | make_list(List)]
end.
@@ -771,6 +787,24 @@ make_list([Last]) ->
make_list([Head|Tail]) ->
[format(Head), $,|make_list(Tail)].
+map_printable_list([$\n|Cs]) ->
+ [$\\, $n|map_printable_list(Cs)];
+map_printable_list([$\r|Cs]) ->
+ [$\\, $r|map_printable_list(Cs)];
+map_printable_list([$\t|Cs]) ->
+ [$\\, $t|map_printable_list(Cs)];
+map_printable_list([$\v|Cs]) ->
+ [$\\, $v|map_printable_list(Cs)];
+map_printable_list([$\b|Cs]) ->
+ [$\\, $b|map_printable_list(Cs)];
+map_printable_list([$\f|Cs]) ->
+ [$\\, $f|map_printable_list(Cs)];
+map_printable_list([$\e|Cs]) ->
+ [$\\, $e|map_printable_list(Cs)];
+map_printable_list([]) -> [];
+map_printable_list([C|Cs]) ->
+ [C|map_printable_list(Cs)].
+
%% printable_list([Char]) -> bool()
%% Return true if CharList is a list of printable characters, else
%% false.
diff --git a/lib/observer/src/observer_wx.erl b/lib/observer/src/observer_wx.erl
index ce3f48a05d..47740581f0 100644
--- a/lib/observer/src/observer_wx.erl
+++ b/lib/observer/src/observer_wx.erl
@@ -195,10 +195,13 @@ setup(#state{frame = Frame} = State) ->
%%Callbacks
handle_event(#wx{event=#wxNotebook{type=command_notebook_page_changing}},
#state{active_tab=Previous, node=Node} = State) ->
- Pid = get_active_pid(State),
- Previous ! not_active,
- Pid ! {active, Node},
- {noreply, State#state{active_tab=Pid}};
+ case get_active_pid(State) of
+ Previous -> {noreply, State};
+ Pid ->
+ Previous ! not_active,
+ Pid ! {active, Node},
+ {noreply, State#state{active_tab=Pid}}
+ end;
handle_event(#wx{event = #wxClose{}}, State) ->
{stop, normal, State};
@@ -350,7 +353,7 @@ terminate(_Reason, #state{frame = Frame}) ->
ok.
code_change(_, _, State) ->
- {stop, not_yet_implemented, State}.
+ {ok, State}.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -410,7 +413,9 @@ connect2(NodeName, Opts, Cookie) ->
end.
change_node_view(Node, State) ->
- get_active_pid(State) ! {active, Node},
+ Tab = get_active_pid(State),
+ Tab ! not_active,
+ Tab ! {active, Node},
StatusText = ["Observer - " | atom_to_list(Node)],
wxFrame:setTitle(State#state.frame, StatusText),
wxStatusBar:setStatusText(State#state.status_bar, StatusText),
@@ -460,41 +465,36 @@ create_connect_dialog(ping, #state{frame = Frame, prev_node=Prev}) ->
cancel
end;
create_connect_dialog(connect, #state{frame = Frame}) ->
- Dialog = wxDialog:new(Frame, ?wxID_ANY, "Distribute node "),
+ Dialog = wxDialog:new(Frame, ?wxID_ANY, "Distribute node",
+ [{style, ?wxDEFAULT_FRAME_STYLE bor ?wxRESIZE_BORDER}]),
VSizer = wxBoxSizer:new(?wxVERTICAL),
- RadioBoxSizer = wxBoxSizer:new(?wxHORIZONTAL),
Choices = ["Short name", "Long name"],
- RadioBox = wxRadioBox:new(Dialog, 1, "",
- ?wxDefaultPosition,
- ?wxDefaultSize,
- Choices,
- [{majorDim, 2},
- {style, ?wxHORIZONTAL}]),
+ RadioBox = wxRadioBox:new(Dialog, 1, "", ?wxDefaultPosition, ?wxDefaultSize,
+ Choices, [{majorDim, 2}, {style, ?wxHORIZONTAL}]),
NameText = wxStaticText:new(Dialog, ?wxID_ANY, "Node name: "),
- NameCtrl = wxTextCtrl:new(Dialog, ?wxID_ANY, [{size, {200, 25}}]),
+ NameCtrl = wxTextCtrl:new(Dialog, ?wxID_ANY, [{size, {300,-1}}]),
wxTextCtrl:setValue(NameCtrl, "observer"),
CookieText = wxStaticText:new(Dialog, ?wxID_ANY, "Secret cookie: "),
- CookieCtrl = wxTextCtrl:new(Dialog, ?wxID_ANY,
- [{size, {200, 25}}, {style, ?wxTE_PASSWORD}]),
+ CookieCtrl = wxTextCtrl:new(Dialog, ?wxID_ANY,[{style, ?wxTE_PASSWORD}]),
- BtnSizer = wxDialog:createStdDialogButtonSizer(Dialog, ?wxID_DEFAULT),
- Flags = [{flag, ?wxEXPAND bor ?wxALL}, {border, 5}],
- wxSizer:add(RadioBoxSizer, RadioBox, Flags),
-
- wxSizer:add(VSizer, RadioBoxSizer, Flags),
+ BtnSizer = wxDialog:createButtonSizer(Dialog, ?wxOK bor ?wxCANCEL),
+ Dir = ?wxLEFT bor ?wxRIGHT bor ?wxDOWN,
+ Flags = [{flag, ?wxEXPAND bor Dir bor ?wxALIGN_CENTER_VERTICAL}, {border, 5}],
+ wxSizer:add(VSizer, RadioBox, Flags),
wxSizer:addSpacer(VSizer, 10),
- wxSizer:add(VSizer, NameText),
+ wxSizer:add(VSizer, NameText, [{flag, ?wxLEFT}, {border, 5}]),
wxSizer:add(VSizer, NameCtrl, Flags),
wxSizer:addSpacer(VSizer, 10),
- wxSizer:add(VSizer, CookieText),
+ wxSizer:add(VSizer, CookieText, [{flag, ?wxLEFT}, {border, 5}]),
wxSizer:add(VSizer, CookieCtrl, Flags),
wxSizer:addSpacer(VSizer, 10),
- wxSizer:add(VSizer, BtnSizer, [{flag, ?wxALIGN_LEFT}]),
+ wxSizer:add(VSizer, BtnSizer, [{proportion, 1}, {flag, ?wxEXPAND bor ?wxALL},{border, 5}]),
- wxWindow:setSizer(Dialog, VSizer),
+ wxWindow:setSizerAndFit(Dialog, VSizer),
+ wxSizer:setSizeHints(VSizer, Dialog),
CookiePath = filename:join(os:getenv("HOME"), ".erlang.cookie"),
DefaultCookie = case filelib:is_file(CookiePath) of
true ->
@@ -543,28 +543,36 @@ clean_menus(Menus, MenuBar) ->
remove_menu_items(Menus, MenuBar).
remove_menu_items([{MenuStr = "File", Menus}|Rest], MenuBar) ->
- MenuId = wxMenuBar:findMenu(MenuBar, MenuStr),
- Menu = wxMenuBar:getMenu(MenuBar, MenuId),
- Items = [wxMenu:findItem(Menu, Tag) || #create_menu{text=Tag} <- Menus],
- [wxMenu:delete(Menu, MItem) || MItem <- Items],
- case os:type() =:= {unix, darwin} of
- true ->
- wxMenuBar:remove(MenuBar, MenuId),
- wxMenu:destroy(Menu);
- false ->
- ignore
- end,
- remove_menu_items(Rest, MenuBar);
+ case wxMenuBar:findMenu(MenuBar, MenuStr) of
+ ?wxNOT_FOUND ->
+ remove_menu_items(Rest, MenuBar);
+ MenuId ->
+ Menu = wxMenuBar:getMenu(MenuBar, MenuId),
+ Items = [wxMenu:findItem(Menu, Tag) || #create_menu{text=Tag} <- Menus],
+ [wxMenu:delete(Menu, MItem) || MItem <- Items],
+ case os:type() =:= {unix, darwin} of
+ true ->
+ wxMenuBar:remove(MenuBar, MenuId),
+ wxMenu:destroy(Menu);
+ false ->
+ ignore
+ end,
+ remove_menu_items(Rest, MenuBar)
+ end;
remove_menu_items([{"Nodes", _}|_], _MB) ->
ok;
remove_menu_items([{Tag, _Menus}|Rest], MenuBar) ->
- MenuId = wxMenuBar:findMenu(MenuBar, Tag),
- Menu = wxMenuBar:getMenu(MenuBar, MenuId),
- wxMenuBar:remove(MenuBar, MenuId),
- Items = wxMenu:getMenuItems(Menu),
- [wxMenu:'Destroy'(Menu, Item) || Item <- Items],
- wxMenu:destroy(Menu),
- remove_menu_items(Rest, MenuBar);
+ case wxMenuBar:findMenu(MenuBar, Tag) of
+ ?wxNOT_FOUND ->
+ remove_menu_items(Rest, MenuBar);
+ MenuId ->
+ Menu = wxMenuBar:getMenu(MenuBar, MenuId),
+ wxMenuBar:remove(MenuBar, MenuId),
+ Items = wxMenu:getMenuItems(Menu),
+ [wxMenu:'Destroy'(Menu, Item) || Item <- Items],
+ wxMenu:destroy(Menu),
+ remove_menu_items(Rest, MenuBar)
+ end;
remove_menu_items([], _MB) ->
ok.
@@ -592,15 +600,22 @@ epmd_nodes(Names) ->
update_node_list(State = #state{menubar=MenuBar}) ->
{Nodes, NodesMenuItems} = get_nodes(),
- NodeMenuId = wxMenuBar:findMenu(MenuBar, "Nodes"),
- NodeMenu = wxMenuBar:getMenu(MenuBar, NodeMenuId),
- wx:foreach(fun(Item) -> wxMenu:'Destroy'(NodeMenu, Item) end,
- wxMenu:getMenuItems(NodeMenu)),
-
+ NodeMenu = case wxMenuBar:findMenu(MenuBar, "Nodes") of
+ ?wxNOT_FOUND ->
+ Menu = wxMenu:new(),
+ wxMenuBar:append(MenuBar, Menu, "Nodes"),
+ Menu;
+ NodeMenuId ->
+ Menu = wxMenuBar:getMenu(MenuBar, NodeMenuId),
+ wx:foreach(fun(Item) -> wxMenu:'Destroy'(Menu, Item) end,
+ wxMenu:getMenuItems(Menu)),
+ Menu
+ end,
+
Index = wx:foldl(fun(Record, Index) ->
observer_lib:create_menu_item(Record, NodeMenu, Index)
end, 0, NodesMenuItems),
-
+
Dist = case erlang:is_alive() of
true -> #create_menu{id = ?ID_PING, text = "Connect node"};
false -> #create_menu{id = ?ID_CONNECT, text = "Enable distribution"}
diff --git a/lib/observer/test/crashdump_viewer_SUITE.erl b/lib/observer/test/crashdump_viewer_SUITE.erl
index 6f882d0be9..4c04126d4f 100644
--- a/lib/observer/test/crashdump_viewer_SUITE.erl
+++ b/lib/observer/test/crashdump_viewer_SUITE.erl
@@ -34,6 +34,7 @@
-define(default_timeout, ?t:minutes(30)).
-define(sl_alloc_vsns,[r9b]).
+-define(failed_file,"failed-cases.txt").
init_per_testcase(_Case, Config) ->
DataDir = ?config(data_dir,Config),
@@ -42,9 +43,18 @@ init_per_testcase(_Case, Config) ->
catch crashdump_viewer:stop(),
Dog = ?t:timetrap(?default_timeout),
[{watchdog, Dog}|Config].
-end_per_testcase(_Case, Config) ->
+end_per_testcase(Case, Config) ->
Dog=?config(watchdog, Config),
?t:timetrap_cancel(Dog),
+ case ?config(tc_status,Config) of
+ ok ->
+ ok;
+ _Fail ->
+ File = filename:join(?config(data_dir,Config),?failed_file),
+ {ok,Fd}=file:open(File,[append]),
+ file:write(Fd,io_lib:format("~w.~n",[Case])),
+ file:close(Fd)
+ end,
ok.
suite() -> [{ct_hooks,[ts_install_cth]}].
@@ -67,15 +77,26 @@ 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),
+ delete_saved(Config),
application:start(inets), % will be using the http client later
httpc:set_options([{ipfamily,inet6fb4}]),
DataDir = ?config(data_dir,Config),
- Rels = [R || R <- [r13b,r14b], ?t:is_release_available(R)] ++ [current],
+ Rels = [R || R <- [r14b,r15b], ?t:is_release_available(R)] ++ [current],
io:format("Creating crash dumps for the following releases: ~p", [Rels]),
AllDumps = create_dumps(DataDir,Rels),
?t:timetrap_cancel(Dog),
[{dumps,AllDumps}|Config].
+delete_saved(Config) ->
+ DataDir = ?config(data_dir,Config),
+ file:delete(filename:join(DataDir,?failed_file)),
+ SaveDir = filename:join(DataDir,"save"),
+ Dumps = filelib:wildcard(filename:join(SaveDir,"*")),
+ lists:foreach(fun(F) -> file:delete(F) end, Dumps),
+ file:del_dir(SaveDir),
+ ok.
+
+
translate(suite) ->
[];
translate(doc) ->
@@ -196,6 +217,23 @@ end_per_suite(doc) ->
["Remove generated crashdumps"];
end_per_suite(Config) when is_list(Config) ->
Dumps = ?config(dumps,Config),
+ DataDir = ?config(data_dir,Config),
+ FailedFile = filename:join(DataDir,?failed_file),
+ case filelib:is_file(FailedFile) of
+ true ->
+ SaveDir = filename:join(DataDir,"save"),
+ file:make_dir(SaveDir),
+ file:copy(FailedFile,filename:join(SaveDir,?failed_file)),
+ lists:foreach(
+ fun(CD) ->
+ File = filename:basename(CD),
+ New = filename:join(SaveDir,File),
+ file:copy(CD,New)
+ end, Dumps);
+ false ->
+ ok
+ end,
+ file:delete(FailedFile),
lists:foreach(fun(CD) -> ok = file:delete(CD) end,Dumps),
lists:keydelete(dumps,1,Config).
@@ -388,7 +426,7 @@ special(Port,File) ->
%% I registered a process as aaaaaaaa in the full_dist dumps
%% to make sure it will be the first in the list when sorted
- %% on names. There are some special data here, s� I'll thoroughly
+ %% on names. There are some special data here, so I'll thoroughly
%% read the process details for this process. Other processes
%% are just briefly traversed.
{Pid,Rest1} = get_first_process(AllProcs),
@@ -568,11 +606,14 @@ expand_link(Html) ->
port_details(Port) ->
- Port1 = contents(Port,"port?port=Port<0.1>"),
- "#Port<0.1>" = title(Port1),
-
Port0 = contents(Port,"port?port=Port<0.0>"),
- "Could not find port: #Port<0.0>" = title(Port0).
+ Port1 = contents(Port,"port?port=Port<0.1>"),
+ case title(Port0) of
+ "#Port<0.0>" -> % R16 or later
+ "Could not find port: #Port<0.1>" = title(Port1);
+ "Could not find port: #Port<0.0>" -> % R15 or earlier
+ "#Port<0.1>" = title(Port1)
+ end.
is_truncated(File) ->
case filename:extension(filename:rootname(File)) of
@@ -691,6 +732,12 @@ dump_with_strange_module_name(DataDir,Rel,DumpName) ->
CD.
dump(Node,DataDir,Rel,DumpName) ->
+ case Rel of
+ _ when Rel<r15b, Rel=/=current ->
+ rpc:call(Node,os,putenv,["ERL_CRASH_DUMP_SECONDS","600"]);
+ _ ->
+ ok
+ end,
rpc:call(Node,erlang,halt,[DumpName]),
Crashdump0 = filename:join(filename:dirname(code:which(?t)),
"erl_crash_dump.n1"),
@@ -752,6 +799,7 @@ rel_opt(Rel) ->
r12b -> [{erl,[{release,"r12b_patched"}]}];
r13b -> [{erl,[{release,"r13b_patched"}]}];
r14b -> [{erl,[{release,"r14b_latest"}]}]; %naming convention changed
+ r15b -> [{erl,[{release,"r15b_latest"}]}];
current -> []
end.
@@ -764,7 +812,8 @@ dump_prefix(Rel) ->
r12b -> "r12b_dump.";
r13b -> "r13b_dump.";
r14b -> "r14b_dump.";
- current -> "r15b_dump."
+ r15b -> "r15b_dump.";
+ current -> "r16b_dump."
end.
compat_rel(Rel) ->
@@ -776,5 +825,6 @@ compat_rel(Rel) ->
r12b -> "+R12 ";
r13b -> "+R13 ";
r14b -> "+R14 ";
+ r15b -> "+R15 ";
current -> ""
end.
diff --git a/lib/observer/vsn.mk b/lib/observer/vsn.mk
index 4eb10ae4e8..32e13004b6 100644
--- a/lib/observer/vsn.mk
+++ b/lib/observer/vsn.mk
@@ -1 +1 @@
-OBSERVER_VSN = 1.1
+OBSERVER_VSN = 1.2
diff --git a/lib/odbc/aclocal.m4 b/lib/odbc/aclocal.m4
index 339a15a2bb..918e30a886 100644
--- a/lib/odbc/aclocal.m4
+++ b/lib/odbc/aclocal.m4
@@ -1,7 +1,7 @@
dnl
dnl %CopyrightBegin%
dnl
-dnl Copyright Ericsson AB 1998-2011. All Rights Reserved.
+dnl Copyright Ericsson AB 1998-2012. All Rights Reserved.
dnl
dnl The contents of this file are subject to the Erlang Public License,
dnl Version 1.1, (the "License"); you may not use this file except in
@@ -59,6 +59,7 @@ AC_ARG_VAR(erl_xcomp_isysroot, [Absolute cross system root include path (only us
dnl Cross compilation variables
AC_ARG_VAR(erl_xcomp_bigendian, [big endian system: yes|no (only used when cross compiling)])
+AC_ARG_VAR(erl_xcomp_double_middle_endian, [double-middle-endian system: yes|no (only used when cross compiling)])
AC_ARG_VAR(erl_xcomp_linux_clock_gettime_correction, [clock_gettime() can be used for time correction: yes|no (only used when cross compiling)])
AC_ARG_VAR(erl_xcomp_linux_nptl, [have Native POSIX Thread Library: yes|no (only used when cross compiling)])
AC_ARG_VAR(erl_xcomp_linux_usable_sigusrx, [SIGUSR1 and SIGUSR2 can be used: yes|no (only used when cross compiling)])
@@ -606,6 +607,103 @@ ifelse([$5], , , [$5
fi
])
+dnl ----------------------------------------------------------------------
+dnl
+dnl AC_DOUBLE_MIDDLE_ENDIAN
+dnl
+dnl Checks whether doubles are represented in "middle-endian" format.
+dnl Sets ac_cv_double_middle_endian={no,yes,unknown} accordingly,
+dnl as well as DOUBLE_MIDDLE_ENDIAN.
+dnl
+dnl
+
+AC_DEFUN([AC_C_DOUBLE_MIDDLE_ENDIAN],
+[AC_CACHE_CHECK(whether double word ordering is middle-endian, ac_cv_c_double_middle_endian,
+[# It does not; compile a test program.
+AC_RUN_IFELSE(
+[AC_LANG_SOURCE([[#include <stdlib.h>
+
+int
+main(void)
+{
+ int i = 0;
+ int zero = 0;
+ int bigendian;
+ int zero_index = 0;
+
+ union
+ {
+ long int l;
+ char c[sizeof (long int)];
+ } u;
+
+ /* we'll use the one with 32-bit words */
+ union
+ {
+ double d;
+ unsigned int c[2];
+ } vint;
+
+ union
+ {
+ double d;
+ unsigned long c[2];
+ } vlong;
+
+ union
+ {
+ double d;
+ unsigned short c[2];
+ } vshort;
+
+
+ /* Are we little or big endian? From Harbison&Steele. */
+ u.l = 1;
+ bigendian = (u.c[sizeof (long int) - 1] == 1);
+
+ zero_index = bigendian ? 1 : 0;
+
+ vint.d = 1.0;
+ vlong.d = 1.0;
+ vshort.d = 1.0;
+
+ if (sizeof(unsigned int) == 4)
+ {
+ if (vint.c[zero_index] != 0)
+ zero = 1;
+ }
+ else if (sizeof(unsigned long) == 4)
+ {
+ if (vlong.c[zero_index] != 0)
+ zero = 1;
+ }
+ else if (sizeof(unsigned short) == 4)
+ {
+ if (vshort.c[zero_index] != 0)
+ zero = 1;
+ }
+
+ exit (zero);
+}
+]])],
+ [ac_cv_c_double_middle_endian=no],
+ [ac_cv_c_double_middle_endian=yes],
+ [ac_cv_c_double_middle=unknown])])
+case $ac_cv_c_double_middle_endian in
+ yes)
+ m4_default([$1],
+ [AC_DEFINE([DOUBLE_MIDDLE_ENDIAN], 1,
+ [Define to 1 if your processor stores the words in a double in
+ middle-endian format (like some ARMs).])]) ;;
+ no)
+ $2 ;;
+ *)
+ m4_default([$3],
+ [AC_MSG_WARN([unknown double endianness
+presetting ac_cv_c_double_middle_endian=no (or yes) will help])]) ;;
+esac
+])# AC_C_DOUBLE_MIDDLE_ENDIAN
+
dnl ----------------------------------------------------------------------
dnl
@@ -642,11 +740,16 @@ dnl Try to find POSIX threads
dnl The usual pthread lib...
AC_CHECK_LIB(pthread, pthread_create, THR_LIBS="-lpthread")
-dnl FreeBSD has pthreads in special c library, c_r...
+dnl Very old versions of FreeBSD have pthreads in special c library, c_r...
if test "x$THR_LIBS" = "x"; then
AC_CHECK_LIB(c_r, pthread_create, THR_LIBS="-lc_r")
fi
+dnl QNX has pthreads in standard C library
+ if test "x$THR_LIBS" = "x"; then
+ AC_CHECK_FUNC(pthread_create, THR_LIBS="none_needed")
+ fi
+
dnl On ofs1 the '-pthread' switch should be used
if test "x$THR_LIBS" = "x"; then
AC_MSG_CHECKING([if the '-pthread' switch can be used])
@@ -667,6 +770,9 @@ dnl On ofs1 the '-pthread' switch should be used
if test "x$THR_LIBS" != "x"; then
THR_DEFS="$THR_DEFS -D_THREAD_SAFE -D_REENTRANT -DPOSIX_THREADS"
THR_LIB_NAME=pthread
+ if test "x$THR_LIBS" = "xnone_needed"; then
+ THR_LIBS=
+ fi
case $host_os in
solaris*)
THR_DEFS="$THR_DEFS -D_POSIX_PTHREAD_SEMANTICS" ;;
@@ -1337,6 +1443,14 @@ if test "$ac_cv_c_bigendian" = "yes"; then
AC_DEFINE(ETHR_BIGENDIAN, 1, [Define if bigendian])
fi
+case X$erl_xcomp_double_middle_endian in
+ X) ;;
+ Xyes|Xno|Xunknown) ac_cv_c_double_middle_endian=$erl_xcomp_double_middle_endian;;
+ *) AC_MSG_ERROR([Bad erl_xcomp_double_middle_endian value: $erl_xcomp_double_middle_endian]);;
+esac
+
+AC_C_DOUBLE_MIDDLE_ENDIAN
+
AC_ARG_ENABLE(native-ethr-impls,
AS_HELP_STRING([--disable-native-ethr-impls],
[disable native ethread implementations]),
@@ -1735,6 +1849,31 @@ case $erl_gethrvtime in
esac
])dnl
+dnl ----------------------------------------------------------------------
+dnl
+dnl LM_TRY_ENABLE_CFLAG
+dnl
+dnl
+dnl Tries a CFLAG and sees if it can be enabled without compiler errors
+dnl $1: textual cflag to add
+dnl $2: variable to store the modified CFLAG in
+dnl Usage example LM_TRY_ENABLE_CFLAG([-Werror=return-type], [CFLAGS])
+dnl
+dnl
+AC_DEFUN([LM_TRY_ENABLE_CFLAG], [
+ AC_MSG_CHECKING([if we can add $1 to $2 (via CFLAGS)])
+ saved_CFLAGS=$CFLAGS;
+ CFLAGS="$1 $$2";
+ AC_TRY_COMPILE([],[return 0;],can_enable_flag=true,can_enable_flag=false)
+ CFLAGS=$saved_CFLAGS;
+ if test "X$can_enable_flag" = "Xtrue"; then
+ AC_MSG_RESULT([yes])
+ AS_VAR_SET($2, "$1 $$2")
+ else
+ AC_MSG_RESULT([no])
+ fi
+])
+
dnl ERL_TRY_LINK_JAVA(CLASSES, FUNCTION-BODY
dnl [ACTION_IF_FOUND [, ACTION-IF-NOT-FOUND]])
dnl Freely inspired by AC_TRY_LINK. (Maybe better to create a
diff --git a/lib/odbc/c_src/Makefile.in b/lib/odbc/c_src/Makefile.in
index 5b37b352fb..6572d28ee8 100644
--- a/lib/odbc/c_src/Makefile.in
+++ b/lib/odbc/c_src/Makefile.in
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2011. All Rights Reserved.
+# Copyright Ericsson AB 1999-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -108,17 +108,17 @@ docs:
ifdef UNIX_TARGET
$(UNIX_TARGET): $(OBJ_DIR)/odbcserver.o
- $(CC) $(CFLAGS) -o $@ $(OBJ_DIR)/odbcserver.o $(LDFLAGS) $(LIBS)
+ $(V_CC) $(CFLAGS) -o $@ $(OBJ_DIR)/odbcserver.o $(LDFLAGS) $(LIBS)
endif
ifdef WIN32_TARGET
$(WIN32_TARGET): $(OBJ_DIR)/odbcserver.o
- $(LD) $(LDFLAGS) -o $@ $(OBJ_DIR)/odbcserver.o $(ENTRY_OBJ) \
+ $(V_LD) $(LDFLAGS) -o $@ $(OBJ_DIR)/odbcserver.o $(ENTRY_OBJ) \
$(LIBS) $(ENTRY_LDFLAGS)
endif
$(OBJ_DIR)/odbcserver.o: odbcserver.c
- $(CC) $(CFLAGS) $(INCLUDES) $(TARGET_FLAGS) -o $@ -c odbcserver.c
+ $(V_CC) $(CFLAGS) $(INCLUDES) $(TARGET_FLAGS) -o $@ -c odbcserver.c
# ----------------------------------------------------
# Release Target
diff --git a/lib/odbc/c_src/odbcserver.c b/lib/odbc/c_src/odbcserver.c
index ab2d7fe210..4a7a5224e5 100644
--- a/lib/odbc/c_src/odbcserver.c
+++ b/lib/odbc/c_src/odbcserver.c
@@ -104,6 +104,7 @@
#ifdef UNIX
#include <unistd.h>
+#include <netinet/tcp.h>
#endif
#if defined WIN32
@@ -152,7 +153,7 @@ static db_result_msg db_describe_table(byte *sql, db_state *state);
/* ------------- Encode/decode functions -------- ------------------------*/
static db_result_msg encode_empty_message(void);
-static db_result_msg encode_error_message(char *reason);
+static db_result_msg encode_error_message(char *reason, char *errCode, SQLINTEGER nativeError);
static db_result_msg encode_atom_message(char *atom);
static db_result_msg encode_result(db_state *state);
static db_result_msg encode_result_set(SQLSMALLINT num_of_columns,
@@ -176,7 +177,7 @@ static void encode_column_dyn(db_column column, int column_nr,
static void encode_data_type(SQLSMALLINT sql_type, SQLINTEGER size,
SQLSMALLINT decimal_digits, db_state *state);
static Boolean decode_params(db_state *state, byte *buffer, int *index, param_array **params,
- int i, int j);
+ int i, int j, int num_param_values);
/*------------- Erlang port communication functions ----------------------*/
@@ -201,6 +202,7 @@ static byte *receive_msg(int socket);
static Boolean receive_msg_part(int socket, byte * buffer, size_t msg_len);
static Boolean send_msg_part(int socket, byte * buffer, size_t msg_len);
static void close_socket(int socket);
+static void tcp_nodelay(int sock);
#endif
static void clean_socket_lib(void);
@@ -212,6 +214,7 @@ static db_column * alloc_column_buffer(int n);
static void free_column_buffer(db_column **columns, int n);
static void free_params(param_array **params, int cols);
static void clean_state(db_state *state);
+static SQLLEN* alloc_strlen_indptr(int n, int val);
/* ------------- Init/map/bind/retrive functions -------------------------*/
@@ -227,7 +230,7 @@ static void init_param_statement(int cols,
static void map_dec_num_2_c_column(col_type *type, int precision,
int scale);
-static db_result_msg map_sql_2_c_column(db_column* column);
+static db_result_msg map_sql_2_c_column(db_column* column, db_state *state);
static param_array * bind_parameter_arrays(byte *buffer, int *index,
@@ -243,7 +246,7 @@ static db_result_msg retrive_scrollable_cursor_support_info(db_state
static int num_out_params(int num_of_params, param_array* params);
/* ------------- Error handling functions --------------------------------*/
-static diagnos get_diagnos(SQLSMALLINT handleType, SQLHANDLE handle);
+static diagnos get_diagnos(SQLSMALLINT handleType, SQLHANDLE handle, Boolean extendedErrors);
/* ------------- Boolean functions ---------------------------------------*/
@@ -346,7 +349,7 @@ DWORD WINAPI database_handler(const char *port)
byte *request_buffer = NULL;
db_state state =
{NULL, NULL, NULL, NULL, 0, {NULL, 0, 0},
- FALSE, FALSE, FALSE, FALSE, FALSE, FALSE};
+ FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE};
byte request_id;
#ifdef WIN32
SOCKET socket;
@@ -435,14 +438,16 @@ static db_result_msg db_connect(byte *args, db_state *state)
diagnos diagnos;
byte *connStrIn;
int erl_auto_commit_mode, erl_trace_driver,
- use_srollable_cursors, tuple_row_state, binary_strings;
+ use_srollable_cursors, tuple_row_state, binary_strings,
+ extended_errors;
erl_auto_commit_mode = args[0];
erl_trace_driver = args[1];
use_srollable_cursors = args[2];
tuple_row_state = args[3];
binary_strings = args[4];
- connStrIn = args + 5 * sizeof(byte);
+ extended_errors = args[5];
+ connStrIn = args + 6 * sizeof(byte);
if(tuple_row_state == ON) {
tuple_row(state) = TRUE;
@@ -462,6 +467,12 @@ static db_result_msg db_connect(byte *args, db_state *state)
use_srollable_cursors(state) = FALSE;
}
+ if(extended_errors == ON) {
+ extended_errors(state) = TRUE;
+ } else {
+ extended_errors(state) = FALSE;
+ }
+
init_driver(erl_auto_commit_mode, erl_trace_driver, state);
connlen = (SQLSMALLINT)strlen((const char*)connStrIn);
@@ -472,10 +483,10 @@ static db_result_msg db_connect(byte *args, db_state *state)
&stringlength2ptr, SQL_DRIVER_NOPROMPT);
if (!sql_success(result)) {
- diagnos = get_diagnos(SQL_HANDLE_DBC, connection_handle(state));
+ diagnos = get_diagnos(SQL_HANDLE_DBC, connection_handle(state), extended_errors(state));
strcat((char *)diagnos.error_msg,
" Connection to database failed.");
- msg = encode_error_message(diagnos.error_msg);
+ msg = encode_error_message(diagnos.error_msg, extended_error(state, diagnos.sqlState), diagnos.nativeError );
if(!sql_success(SQLFreeHandle(SQL_HANDLE_DBC,
connection_handle(state))))
@@ -506,8 +517,8 @@ static db_result_msg db_close_connection(db_state *state)
result = SQLDisconnect(connection_handle(state));
if (!sql_success(result)) {
- diagnos = get_diagnos(SQL_HANDLE_DBC, connection_handle(state));
- return encode_error_message(diagnos.error_msg);
+ diagnos = get_diagnos(SQL_HANDLE_DBC, connection_handle(state), extended_errors(state));
+ return encode_error_message(diagnos.error_msg, extended_error(state, diagnos.sqlState), diagnos.nativeError);
}
if(!sql_success(SQLFreeHandle(SQL_HANDLE_DBC,
@@ -533,8 +544,8 @@ static db_result_msg db_end_tran(byte compleationtype, db_state *state)
(SQLSMALLINT)compleationtype);
if (!sql_success(result)) {
- diagnos = get_diagnos(SQL_HANDLE_DBC, connection_handle(state));
- return encode_error_message(diagnos.error_msg);
+ diagnos = get_diagnos(SQL_HANDLE_DBC, connection_handle(state), extended_errors(state));
+ return encode_error_message(diagnos.error_msg, extended_error(state, diagnos.sqlState), diagnos.nativeError);
} else {
return encode_atom_message("ok");
}
@@ -569,7 +580,7 @@ static db_result_msg db_query(byte *sql, db_state *state)
/* SQL_SUCCESS_WITH_INFO at this point may indicate an error in user input. */
if (result != SQL_SUCCESS && result != SQL_NO_DATA_FOUND) {
- diagnos = get_diagnos(SQL_HANDLE_STMT, statement_handle(state));
+ diagnos = get_diagnos(SQL_HANDLE_STMT, statement_handle(state), extended_errors(state));
if(strcmp((char *)diagnos.sqlState, INFO) == 0) {
is_error[0] = 0;
strncat((char *)is_error, (char *)diagnos.error_msg,
@@ -580,12 +591,12 @@ static db_result_msg db_query(byte *sql, db_state *state)
it as we want a nice and clean Erlang API */
if((strcmp((char *)is_error, "error") == 0))
{
- msg = encode_error_message((char *)diagnos.error_msg);
+ msg = encode_error_message((char *)diagnos.error_msg, extended_error(state, diagnos.sqlState), diagnos.nativeError);
clean_state(state);
return msg;
}
} else {
- msg = encode_error_message((char *)diagnos.error_msg);
+ msg = encode_error_message((char *)diagnos.error_msg, extended_error(state, diagnos.sqlState), diagnos.nativeError);
clean_state(state);
return msg;
}
@@ -658,9 +669,9 @@ static db_result_msg db_select_count(byte *sql, db_state *state)
}
if(!sql_success(SQLExecDirect(statement_handle(state), sql, SQL_NTS))) {
- diagnos = get_diagnos(SQL_HANDLE_STMT, statement_handle(state));
+ diagnos = get_diagnos(SQL_HANDLE_STMT, statement_handle(state), extended_errors(state));
clean_state(state);
- return encode_error_message(diagnos.error_msg);
+ return encode_error_message(diagnos.error_msg, extended_error(state, diagnos.sqlState), diagnos.nativeError);
}
if(!sql_success(SQLNumResultCols(statement_handle(state),
@@ -800,13 +811,13 @@ static db_result_msg db_param_query(byte *buffer, db_state *state)
result = SQLExecDirect(statement_handle(state), sql, SQL_NTS);
if (!sql_success(result) || result == SQL_NO_DATA) {
- diagnos = get_diagnos(SQL_HANDLE_STMT, statement_handle(state));
+ diagnos = get_diagnos(SQL_HANDLE_STMT, statement_handle(state), extended_errors(state));
}
/* SQL_NO_DATA and SQLSTATE 00000 indicate success for
updates/deletes that affect no rows */
if(!sql_success(result) &&
!(result == SQL_NO_DATA && !strcmp((char *)diagnos.sqlState, INFO))) {
- msg = encode_error_message(diagnos.error_msg);
+ msg = encode_error_message(diagnos.error_msg, extended_error(state, diagnos.sqlState), diagnos.nativeError);
} else {
for (i = 0; i < param_status.params_processed; i++) {
switch (param_status.param_status_array[i]) {
@@ -820,8 +831,8 @@ static db_result_msg db_param_query(byte *buffer, db_state *state)
break;
default:
diagnos =
- get_diagnos(SQL_HANDLE_STMT, statement_handle(state));
- msg = encode_error_message(diagnos.error_msg);
+ get_diagnos(SQL_HANDLE_STMT, statement_handle(state), extended_errors(state));
+ msg = encode_error_message(diagnos.error_msg, extended_error(state, diagnos.sqlState), diagnos.nativeError);
i = param_status.params_processed;
break;
}
@@ -890,16 +901,16 @@ static db_result_msg db_describe_table(byte *sql, db_state *state)
DO_EXIT(EXIT_ALLOC);
if (!sql_success(SQLPrepare(statement_handle(state), sql, SQL_NTS))){
- diagnos = get_diagnos(SQL_HANDLE_STMT, statement_handle(state));
- msg = encode_error_message(diagnos.error_msg);
+ diagnos = get_diagnos(SQL_HANDLE_STMT, statement_handle(state), extended_errors(state));
+ msg = encode_error_message(diagnos.error_msg, extended_error(state, diagnos.sqlState), diagnos.nativeError);
clean_state(state);
return msg;
}
if(!sql_success(SQLNumResultCols(statement_handle(state),
&num_of_columns))) {
- diagnos = get_diagnos(SQL_HANDLE_STMT, statement_handle(state));
- msg = encode_error_message(diagnos.error_msg);
+ diagnos = get_diagnos(SQL_HANDLE_STMT, statement_handle(state), extended_errors(state));
+ msg = encode_error_message(diagnos.error_msg, extended_error(state, diagnos.sqlState), diagnos.nativeError);
clean_state(state);
return msg;
}
@@ -948,7 +959,7 @@ static db_result_msg encode_empty_message(void)
}
/* Description: Encode an error-message to send back to erlang*/
-static db_result_msg encode_error_message(char *reason)
+static db_result_msg encode_error_message(char *reason, char *errCode, SQLINTEGER nativeError )
{
int index;
db_result_msg msg;
@@ -957,6 +968,12 @@ static db_result_msg encode_error_message(char *reason)
ei_encode_version(NULL, &index);
ei_encode_tuple_header(NULL, &index, 2);
ei_encode_atom(NULL, &index, "error");
+ if (errCode)
+ {
+ ei_encode_tuple_header(NULL, &index, 3);
+ ei_encode_string(NULL, &index, errCode);
+ ei_encode_long(NULL, &index, nativeError);
+ }
ei_encode_string(NULL, &index, reason);
msg.length = index;
@@ -967,6 +984,12 @@ static db_result_msg encode_error_message(char *reason)
ei_encode_version((char *)msg.buffer, &index);
ei_encode_tuple_header((char *)msg.buffer, &index, 2);
ei_encode_atom((char *)msg.buffer, &index, "error");
+ if (errCode)
+ {
+ ei_encode_tuple_header((char *)msg.buffer, &index, 3);
+ ei_encode_string((char *)msg.buffer, &index, errCode);
+ ei_encode_long((char *)msg.buffer, &index, nativeError);
+ }
ei_encode_string((char *)msg.buffer, &index, reason);
return msg;
@@ -1010,8 +1033,8 @@ static db_result_msg encode_result(db_state *state)
if(!sql_success(SQLNumResultCols(statement_handle(state),
&num_of_columns))) {
- diagnos = get_diagnos(SQL_HANDLE_STMT, statement_handle(state));
- msg = encode_error_message(diagnos.error_msg);
+ diagnos = get_diagnos(SQL_HANDLE_STMT, statement_handle(state), extended_errors(state));
+ msg = encode_error_message(diagnos.error_msg, extended_error(state, diagnos.sqlState), diagnos.nativeError);
clean_state(state);
return msg;
}
@@ -1027,8 +1050,8 @@ static db_result_msg encode_result(db_state *state)
}
if(!sql_success(SQLRowCount(statement_handle(state), &RowCountPtr))) {
- diagnos = get_diagnos(SQL_HANDLE_STMT, statement_handle(state));
- msg = encode_error_message(diagnos.error_msg);
+ diagnos = get_diagnos(SQL_HANDLE_STMT, statement_handle(state), extended_errors(state));
+ msg = encode_error_message(diagnos.error_msg, extended_error(state, diagnos.sqlState), diagnos.nativeError);
clean_state(state);
return msg;
}
@@ -1157,7 +1180,7 @@ static db_result_msg encode_out_params(db_state *state,
break;
case SQL_C_BIT:
ei_x_encode_atom(&dynamic_buffer(state),
- ((Boolean*)values)[j]==TRUE?"true":"false");
+ ((byte*)values)[j]==TRUE?"true":"false");
break;
default:
ei_x_encode_atom(&dynamic_buffer(state), "error");
@@ -1236,7 +1259,7 @@ static db_result_msg encode_column_name_list(SQLSMALLINT num_of_columns,
(columns(state)[i]).type.sql = sql_type;
(columns(state)[i]).type.col_size = size;
- msg = map_sql_2_c_column(&columns(state)[i]);
+ msg = map_sql_2_c_column(&columns(state)[i], state);
if (msg.length > 0) {
return msg; /* An error has occurred */
} else {
@@ -1579,37 +1602,48 @@ static void encode_data_type(SQLSMALLINT sql_type, SQLINTEGER size,
}
static Boolean decode_params(db_state *state, byte *buffer, int *index, param_array **params,
- int i, int j)
+ int i, int j, int num_param_values)
{
int erl_type, size;
long bin_size, l64;
long val;
param_array* param;
TIMESTAMP_STRUCT* ts;
+ char atomarray[MAXATOMLEN+1];
ei_get_type(buffer, index, &erl_type, &size);
param = &(*params)[i];
+ if(erl_type == ERL_ATOM_EXT) {
+ ei_decode_atom(buffer, index, atomarray);
+ if(strncmp(atomarray, "null", 4) == 0 ) {
+ param->offset += param->type.len;
+
+ if(!param->type.strlen_or_indptr_array)
+ param->type.strlen_or_indptr_array = alloc_strlen_indptr(num_param_values, param->type.len);
+
+ param->type.strlen_or_indptr_array[j] = SQL_NULL_DATA;
+ return TRUE;
+ }
+ }
+
switch (param->type.c) {
case SQL_C_CHAR:
if (binary_strings(state)) {
ei_decode_binary(buffer, index,
&(param->values.string[param->offset]), &bin_size);
param->offset += param->type.len;
- param->type.strlen_or_indptr_array[j] = SQL_NTS;
} else {
if(erl_type != ERL_STRING_EXT) {
return FALSE;
}
ei_decode_string(buffer, index, &(param->values.string[param->offset]));
param->offset += param->type.len;
- param->type.strlen_or_indptr_array[j] = SQL_NTS;
}
break;
case SQL_C_WCHAR:
ei_decode_binary(buffer, index, &(param->values.string[param->offset]), &bin_size);
param->offset += param->type.len;
- param->type.strlen_or_indptr_array[j] = SQL_NTS;
break;
case SQL_C_TYPE_TIMESTAMP:
ts = (TIMESTAMP_STRUCT*) param->values.string;
@@ -1661,9 +1695,13 @@ static Boolean decode_params(db_state *state, byte *buffer, int *index, param_ar
if((erl_type != ERL_ATOM_EXT)) {
return FALSE;
}
- ei_decode_boolean(buffer, index, &(param->values.bool[j]));
+ if (strncmp((char*)atomarray,"true",4) == 0)
+ param->values.bool[j] = TRUE;
+ else if (strncmp((char*)atomarray,"false",5) == 0)
+ param->values.bool[j] = FALSE;
+ else
+ return -1;
break;
-
default:
return FALSE;
}
@@ -1766,6 +1804,10 @@ static int connect_to_erlang(const char *port)
sin6.sin6_addr = in6addr_loopback;
if (connect(sock, (struct sockaddr*)&sin6, sizeof(sin6)) == 0) {
+ /* Enable TCP_NODELAY to disable Nagel's socket algorithm. (Removes ~40ms delay on Redhat ES 6). */
+ #ifdef UNIX
+ tcp_nodelay(sock);
+ #endif
return sock;
}
close_socket(sock);
@@ -1781,9 +1823,24 @@ static int connect_to_erlang(const char *port)
close_socket(sock);
DO_EXIT(EXIT_SOCKET_CONNECT);
}
+
+ /* Enable TCP_NODELAY to disable Nagel's socket algorithm. (Removes ~40ms delay on Redhat ES 6). */
+ #ifdef UNIX
+ tcp_nodelay(sock);
+ #endif
return sock;
}
+#ifdef UNIX
+static void tcp_nodelay(int sock)
+{
+ int flag = 1;
+ int result = setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int));
+ if (result < 0) {
+ DO_EXIT(EXIT_SOCKET_CONNECT);
+ }
+}
+#endif
#ifdef WIN32
static void close_socket(SOCKET socket)
{
@@ -2014,6 +2071,18 @@ static void clean_state(db_state *state)
nr_of_columns(state) = 0;
}
+/* Allocates and fill with default value StrLen_or_IndPtr array */
+static SQLLEN* alloc_strlen_indptr(int n, int val)
+{
+ int i;
+ SQLLEN* arr = (SQLLEN*)safe_malloc(n * sizeof(SQLLEN));
+
+ for( i=0; i < n; ++i )
+ arr[i] = val;
+
+ return arr;
+}
+
/* ------------- Init/map/bind/retrive functions ------------------------*/
/* Prepare the state for a connection */
@@ -2118,7 +2187,7 @@ static void init_param_column(param_array *params, byte *buffer, int *index,
(double *)safe_malloc(num_param_values * params->type.len);
} else if(params->type.c == SQL_C_CHAR) {
params->type.strlen_or_indptr_array
- = (SQLLEN*)safe_malloc(num_param_values * sizeof(SQLINTEGER));
+ = alloc_strlen_indptr(num_param_values, SQL_NTS);
params->values.string =
(byte *)safe_malloc(num_param_values *
sizeof(byte)* params->type.len);
@@ -2136,8 +2205,8 @@ static void init_param_column(param_array *params, byte *buffer, int *index,
params->type.len = length+1;
params->type.c = SQL_C_CHAR;
params->type.col_size = (SQLUINTEGER)length;
- params->type.strlen_or_indptr_array =
- (SQLLEN*)safe_malloc(num_param_values * sizeof(SQLINTEGER));
+ params->type.strlen_or_indptr_array
+ = alloc_strlen_indptr(num_param_values, SQL_NTS);
params->values.string =
(byte *)safe_malloc(num_param_values *
sizeof(byte)* params->type.len);
@@ -2159,8 +2228,8 @@ static void init_param_column(param_array *params, byte *buffer, int *index,
params->type.len = (length+1)*sizeof(SQLWCHAR);
params->type.c = SQL_C_WCHAR;
params->type.col_size = (SQLUINTEGER)length;
- params->type.strlen_or_indptr_array =
- (SQLLEN*)safe_malloc(num_param_values * sizeof(SQLINTEGER));
+ params->type.strlen_or_indptr_array
+ = alloc_strlen_indptr(num_param_values, SQL_NTS);
params->values.string =
(byte *)safe_malloc(num_param_values * sizeof(byte) * params->type.len);
@@ -2201,10 +2270,10 @@ static void init_param_column(param_array *params, byte *buffer, int *index,
case USER_BOOLEAN:
params->type.sql = SQL_BIT;
params->type.c = SQL_C_BIT;
- params->type.len = sizeof(Boolean);
+ params->type.len = sizeof(byte);
params->type.col_size = params->type.len;
params->values.bool =
- (Boolean *)safe_malloc(num_param_values * params->type.len);
+ (byte *)safe_malloc(num_param_values * params->type.len);
break;
}
params->offset = 0;
@@ -2297,7 +2366,7 @@ static void map_dec_num_2_c_column(col_type *type, int precision, int scale)
/* Description: Transform SQL columntype to C columntype. Returns a dummy
db_result_msg with length 0 on success and an errormessage otherwise.*/
-static db_result_msg map_sql_2_c_column(db_column* column)
+static db_result_msg map_sql_2_c_column(db_column* column, db_state *state)
{
db_result_msg msg;
@@ -2366,10 +2435,10 @@ static db_result_msg map_sql_2_c_column(db_column* column)
column -> type.strlen_or_indptr = (SQLLEN)NULL;
break;
case SQL_UNKNOWN_TYPE:
- msg = encode_error_message("Unknown column type");
+ msg = encode_error_message("Unknown column type", extended_error(state, ""), 0);
break;
default:
- msg = encode_error_message("Column type not supported");
+ msg = encode_error_message("Column type not supported", extended_error(state, ""), 0);
break;
}
return msg;
@@ -2411,7 +2480,7 @@ static param_array * bind_parameter_arrays(byte *buffer, int *index,
}
for (j = 0; j < num_param_values; j++) {
- if(!decode_params(state, buffer, index, &params, i, j)) {
+ if(!decode_params(state, buffer, index, &params, i, j, num_param_values)) {
/* An input parameter was not of the expected type */
free_params(&params, i);
return params;
@@ -2478,7 +2547,7 @@ static db_column retrive_binary_data(db_column column, int column_nr,
while (result == SQL_SUCCESS_WITH_INFO) {
- diagnos = get_diagnos(SQL_HANDLE_STMT, statement_handle(state));
+ diagnos = get_diagnos(SQL_HANDLE_STMT, statement_handle(state), extended_errors(state));
if(strcmp((char *)diagnos.sqlState, TRUNCATED) == 0) {
outputlen = column.type.len - 1;
@@ -2565,10 +2634,10 @@ static db_result_msg more_result_sets(db_state *state)
/* As we found an error we do not care about any potential more result
sets */
exists_more_result_sets(state) = FALSE;
- diagnos = get_diagnos(SQL_HANDLE_STMT, statement_handle(state));
+ diagnos = get_diagnos(SQL_HANDLE_STMT, statement_handle(state), extended_errors(state));
strcat((char *)diagnos.error_msg,
"Failed to create on of the result sets");
- msg = encode_error_message(diagnos.error_msg);
+ msg = encode_error_message(diagnos.error_msg, extended_error(state, diagnos.sqlState), diagnos.nativeError);
return msg;
}
}
@@ -2585,7 +2654,7 @@ static Boolean sql_success(SQLRETURN result)
diagnostic records scaning for error messages and the sqlstate.
If this function is called when no error has ocurred only the sqlState
field may be referenced.*/
-static diagnos get_diagnos(SQLSMALLINT handleType, SQLHANDLE handle)
+static diagnos get_diagnos(SQLSMALLINT handleType, SQLHANDLE handle, Boolean extendedErrors)
{
diagnos diagnos;
SQLINTEGER nativeError;
@@ -2609,17 +2678,16 @@ static diagnos get_diagnos(SQLSMALLINT handleType, SQLHANDLE handle)
result = SQLGetDiagRec(handleType, handle, record_nr, current_sql_state,
&nativeError, current_errmsg_pos,
(SQLSMALLINT)errmsg_buffer_size, &errmsg_size);
- if(result != SQL_SUCCESS && result != SQL_NO_DATA) {
-
-
- break;
- } else {
+ if(result == SQL_SUCCESS) {
/* update the sqlstate in the diagnos record, because the SQLGetDiagRec
call succeeded */
memcpy(diagnos.sqlState, current_sql_state, SQL_STATE_SIZE);
+ diagnos.nativeError = nativeError;
errmsg_buffer_size = errmsg_buffer_size - errmsg_size;
acc_errmsg_size = acc_errmsg_size + errmsg_size;
current_errmsg_pos = current_errmsg_pos + errmsg_size;
+ } else {
+ break;
}
}
@@ -2627,7 +2695,7 @@ static diagnos get_diagnos(SQLSMALLINT handleType, SQLHANDLE handle)
strcat((char *)diagnos.error_msg,
"No SQL-driver information available.");
}
- else {
+ else if (!extendedErrors){
strcat(strcat((char *)diagnos.error_msg, " SQLSTATE IS: "),
(char *)diagnos.sqlState);
}
diff --git a/lib/odbc/c_src/odbcserver.h b/lib/odbc/c_src/odbcserver.h
index 56b6148777..71760189e7 100644
--- a/lib/odbc/c_src/odbcserver.h
+++ b/lib/odbc/c_src/odbcserver.h
@@ -145,6 +145,7 @@ typedef struct {
typedef struct {
SQLCHAR sqlState[SQL_STATE_SIZE];
+ SQLINTEGER nativeError;
byte error_msg[MAX_ERR_MSG];
} diagnos;
@@ -156,7 +157,7 @@ typedef struct {
byte *string;
SQLINTEGER *integer;
double *floating;
- Boolean *bool;
+ byte *bool;
}values;
} param_array;
@@ -179,6 +180,7 @@ typedef struct {
Boolean exists_more_result_sets;
Boolean param_query;
Boolean out_params;
+ Boolean extended_errors;
} db_state;
typedef enum {
@@ -198,3 +200,5 @@ typedef enum {
#define exists_more_result_sets(db_state) (db_state -> exists_more_result_sets)
#define param_query(db_state) (db_state -> param_query)
#define out_params(db_state) (db_state -> out_params)
+#define extended_errors(db_state) (db_state -> extended_errors)
+#define extended_error(db_state, errorcode) ( extended_errors(state) ? errorcode : NULL )
diff --git a/lib/odbc/configure.in b/lib/odbc/configure.in
index bec65c71bf..e6ba9f57f5 100644
--- a/lib/odbc/configure.in
+++ b/lib/odbc/configure.in
@@ -212,4 +212,9 @@ AC_SUBST(ODBC_INCLUDE)
fi dnl "$with_odbc" != "no"
+if test "x$GCC" = xyes; then
+ # Treat certain GCC warnings as errors
+ LM_TRY_ENABLE_CFLAG([-Werror=return-type], [CFLAGS])
+fi
+
AC_OUTPUT(c_src/$host/Makefile:c_src/Makefile.in)
diff --git a/lib/odbc/doc/src/Makefile b/lib/odbc/doc/src/Makefile
index cd9bca0a55..53a855f182 100644
--- a/lib/odbc/doc/src/Makefile
+++ b/lib/odbc/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2011. All Rights Reserved.
+# Copyright Ericsson AB 1999-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/odbc/doc/src/notes.xml b/lib/odbc/doc/src/notes.xml
index 08763163b8..7ba0307a45 100644
--- a/lib/odbc/doc/src/notes.xml
+++ b/lib/odbc/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2004</year><year>2011</year>
+ <year>2004</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -31,7 +31,27 @@
<p>This document describes the changes made to the odbc application.
</p>
- <section><title>ODBC 2.10.12</title>
+ <section><title>ODBC 2.10.13</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Add support for NULL value in odbc:param_query</p>
+ <p>
+ Support atom 'null' in odbc:param_query as database NULL
+ value Fix "ODBC: received unexpected info:{tcp_closed,
+ ...}" when connection is terminated. Fix possible access
+ violation with 64bit ODBC. Thanks to Maxim Zrazhevskiy</p>
+ <p>
+ Own Id: OTP-10206</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>ODBC 2.10.12</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/odbc/doc/src/odbc.xml b/lib/odbc/doc/src/odbc.xml
index 8a58dc2848..a984bf4485 100644
--- a/lib/odbc/doc/src/odbc.xml
+++ b/lib/odbc/doc/src/odbc.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1999</year><year>2011</year>
+ <year>1999</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -74,8 +74,12 @@
<code type="none">
milliseconds() = integer() >= 0 </code>
<code type="none">
- common_reason() = connection_closed | term() - some kind of
- explanation of what went wrong </code>
+ common_reason() = connection_closed | extended_error() | term() - some kind of
+ explanation of what went wrong </code>
+ <code type="none">
+ extended_error() = {string(), integer(), Reason} - extended error type with ODBC
+ and native database error codes, as well as the base reason that would have been
+ returned had extended_errors not been enabled. </code>
<code type="none">
string() = list of ASCII characters </code>
<code type="none">
@@ -101,8 +105,14 @@
odbc_data_type() = sql_integer | sql_smallint | sql_tinyint |
{sql_decimal, precision(), scale()} |
{sql_numeric, precision(), scale()} |
- {sql_char, size()} | {sql_wchar, size()} | {sql_varchar, size()} | {sql_wvarchar, size()}| {sql_float, precision()} |
- {sql_wlongvarchar, size()} | {sql_float, precision()} | sql_real | sql_double | sql_bit | atom()
+ {sql_char, size()} |
+ {sql_wchar, size()} |
+ {sql_varchar, size()} |
+ {sql_wvarchar, size()}|
+ {sql_float, precision()} |
+ {sql_wlongvarchar, size()} |
+ {sql_float, precision()} |
+ sql_real | sql_double | sql_bit | atom()
</code>
<code type="none">
precision() = integer() </code>
@@ -143,7 +153,7 @@
<d>All options has default values. </d>
<v>option() = {auto_commit, on | off} | {timeout, milliseconds()}
| {binary_strings, on | off} | {tuple_row, on | off} | {scrollable_cursors, on | off} |
- {trace_driver, on | off} </v>
+ {trace_driver, on | off} | {extended_errors, on | off} </v>
<v>Ref = connection_reference() - should be used to access the connection. </v>
<v>Reason = port_program_executable_not_found | common_reason()</v>
</type>
@@ -196,6 +206,19 @@
<p>For more information about the <c>ConnectStr</c> see
description of the function SQLDriverConnect in [1].</p>
</note>
+
+ <p>The <c>extended_errors</c> option enables extended ODBC error
+ information when an operation fails. Rather than returning <c>{error, Reason}</c>,
+ the failing function will reutrn <c>{error, {ODBCErrorCode, NativeErrorCode, Reason}}</c>.
+ Note that this information is probably of little use when writing database-independent code,
+ but can be of assistance in providing more sophisticated error handling when dealing with
+ a known underlying database.
+ <list type="bulleted">
+ <item><c>ODBCErrorCode</c> is the ODBC error string returned by the ODBC driver.</item>
+ <item><c>NativeErrorCode</c> is the numberic error code returned by the underlying database. The possible values
+ and their meanings are dependent on the database being used.</item>
+ <item><c>Reason</c> is as per the <c>Reason</c> field when extended errors are not enabled.</item>
+ </list></p>
</desc>
</func>
<func>
@@ -203,7 +226,7 @@
<fsummary>Closes a connection to a database. </fsummary>
<type>
<v>Ref = connection_reference()</v>
- <v>Reason = process_not_owner_of_odbc_connection</v>
+ <v>Reason = process_not_owner_of_odbc_connection | extended_error()</v>
</type>
<desc>
<p>Closes a connection to a database. This will also
diff --git a/lib/odbc/src/Makefile b/lib/odbc/src/Makefile
index 7ca82d784b..bfbda8aaf4 100644
--- a/lib/odbc/src/Makefile
+++ b/lib/odbc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2009. All Rights Reserved.
+# Copyright Ericsson AB 1999-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -95,9 +95,9 @@ clean:
$(APP_TARGET): $(APP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
docs:
diff --git a/lib/odbc/src/odbc.appup.src b/lib/odbc/src/odbc.appup.src
index 853323da09..c7c83ea079 100644
--- a/lib/odbc/src/odbc.appup.src
+++ b/lib/odbc/src/odbc.appup.src
@@ -1,12 +1,8 @@
%% -*- erlang -*-
{"%VSN%",
[
- {"2.10.11", [{restart_application, odbc}]},
- {"2.10.10", [{restart_application, odbc}]},
- {"2.10.9", [{restart_application, odbc}]}
+ {<<"2\\.*">>, [{restart_application, odbc}]}
],
[
- {"2.10.11", [{restart_application, odbc}]},
- {"2.10.10", [{restart_application, odbc}]},
- {"2.10.9", [{restart_application, odbc}]}
+ {<<"2\\.*">>, [{restart_application, odbc}]}
]}.
diff --git a/lib/odbc/src/odbc.erl b/lib/odbc/src/odbc.erl
index 9f7b06dcf1..3eabec9ec3 100644
--- a/lib/odbc/src/odbc.erl
+++ b/lib/odbc/src/odbc.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -755,7 +755,10 @@ handle_info({'DOWN', _Ref, _Type, _Process, shutdown}, State) ->
handle_info({'DOWN', _Ref, _Type, Process, Reason}, State) ->
{stop, {stopped, {'EXIT', Process, Reason}},
State#state{reply_to = undefined}};
-
+
+handle_info({tcp_closed, Socket}, State = #state{odbc_socket=Socket,
+ state = disconnecting}) ->
+ {stop, normal, State};
%---------------------------------------------------------------------------
%% Catch all - throws away unknown messages (This could happen by "accident"
%% so we do not want to crash, but we make a log entry as it is an
@@ -807,10 +810,11 @@ connect(ConnectionReferense, ConnectionStr, Options) ->
{C_TupleRow, _} =
connection_config(tuple_row, Options),
{BinaryStrings, _} = connection_config(binary_strings, Options),
+ {ExtendedErrors, _} = connection_config(extended_errors, Options),
ODBCCmd =
[?OPEN_CONNECTION, C_AutoCommitMode, C_TraceDriver,
- C_SrollableCursors, C_TupleRow, BinaryStrings, ConnectionStr],
+ C_SrollableCursors, C_TupleRow, BinaryStrings, ExtendedErrors, ConnectionStr],
%% Send request, to open a database connection, to the control process.
case call(ConnectionReferense,
@@ -857,6 +861,8 @@ connection_default(trace_driver) ->
connection_default(scrollable_cursors) ->
{?ON, on};
connection_default(binary_strings) ->
+ {?OFF, off};
+connection_default(extended_errors) ->
{?OFF, off}.
%%-------------------------------------------------------------------------
@@ -942,9 +948,11 @@ fix_params({sql_bit, InOut, Values}) ->
fix_params({'sql_timestamp', InOut, Values}) ->
NewValues =
case (catch
- lists:map(fun({{Year,Month,Day},{Hour,Minute,Second}}) ->
- {Year,Month,Day,Hour,Minute,Second}
- end, Values)) of
+ lists:map(
+ fun({{Year,Month,Day},{Hour,Minute,Second}}) ->
+ {Year,Month,Day,Hour,Minute,Second};
+ (null) -> null
+ end, Values)) of
Result ->
Result
end,
@@ -960,15 +968,15 @@ fix_inout(out) ->
fix_inout(inout) ->
?INOUT.
-string_terminate([Value| _ ] = Values) when is_list(Value)->
- case (catch
- lists:map(fun(Str) -> Str ++ [?STR_TERMINATOR] end, Values)) of
- Result ->
- Result
- end;
-string_terminate([Value| _ ] = Values) when is_binary(Value)->
- case (catch
- lists:map(fun(B) -> <<B/binary,0:16>> end, Values)) of
+string_terminate(Values) ->
+ case (catch lists:map(fun string_terminate_value/1, Values)) of
Result ->
Result
end.
+
+string_terminate_value(String) when is_list(String) ->
+ String ++ [?STR_TERMINATOR];
+string_terminate_value(Binary) when is_binary(Binary) ->
+ <<Binary/binary,0:16>>;
+string_terminate_value(null) ->
+ null.
diff --git a/lib/odbc/test/Makefile b/lib/odbc/test/Makefile
index 84551f0eb1..d45073281b 100644
--- a/lib/odbc/test/Makefile
+++ b/lib/odbc/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2011. All Rights Reserved.
+# Copyright Ericsson AB 1999-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/odbc/test/odbc_connect_SUITE.erl b/lib/odbc/test/odbc_connect_SUITE.erl
index a076c4dfff..7d732f20f7 100644
--- a/lib/odbc/test/odbc_connect_SUITE.erl
+++ b/lib/odbc/test/odbc_connect_SUITE.erl
@@ -51,7 +51,7 @@ all() ->
{group, client_dies}, connect_timeout, timeout,
many_timeouts, timeout_reset, disconnect_on_timeout,
connection_closed, disable_scrollable_cursors,
- return_rows_as_lists, api_missuse];
+ return_rows_as_lists, api_missuse, extended_errors];
Other -> {skip, Other}
end.
@@ -838,3 +838,33 @@ transaction_support_str(mysql) ->
"ENGINE = InnoDB";
transaction_support_str(_) ->
"".
+
+
+%%-------------------------------------------------------------------------
+extended_errors(doc)->
+ ["Test the extended errors connection option: When off; the old behaviour of just an error "
+ "string is returned on error. When on, the error string is replaced by a 3 element tuple "
+ "that also exposes underlying ODBC provider error codes."];
+extended_errors(suite) -> [];
+extended_errors(Config) when is_list(Config)->
+ Table = ?config(tableName, Config),
+ {ok, Ref} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()),
+ {updated, _} = odbc:sql_query(Ref, "create table " ++ Table ++" ( id integer, data varchar(10))"),
+
+ % Error case WITHOUT extended errors on...
+ case odbc:sql_query(Ref, "create table " ++ Table ++" ( id integer, data varchar(10))") of
+ {error, ErrorString} when is_list(ErrorString) -> ok
+ end,
+
+ % Now the test case with extended errors on - This should return a tuple, not a list/string now.
+ % The first element is a string that is the ODBC error string; the 2nd element is a native integer error
+ % code passed from the underlying provider driver. The last is the familiar old error string.
+ % We can't check the actual error code; as each different underlying provider will return
+ % a different value - So we just check the return types at least.
+ {ok, RefExtended} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options() ++ [{extended_errors, on}]),
+ case odbc:sql_query(RefExtended, "create table " ++ Table ++" ( id integer, data varchar(10))") of
+ {error, {ODBCCodeString, NativeCodeNum, ShortErrorString}} when is_list(ODBCCodeString), is_number(NativeCodeNum), is_list(ShortErrorString) -> ok
+ end,
+
+ ok = odbc:disconnect(Ref),
+ ok = odbc:disconnect(RefExtended).
diff --git a/lib/odbc/test/odbc_data_type_SUITE.erl b/lib/odbc/test/odbc_data_type_SUITE.erl
index d61a91f973..2d33546622 100644
--- a/lib/odbc/test/odbc_data_type_SUITE.erl
+++ b/lib/odbc/test/odbc_data_type_SUITE.erl
@@ -1,7 +1,8 @@
+%% -*- coding: utf-8 -*-
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -1469,7 +1470,7 @@ utf8(Config) when is_list(Config) ->
odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ "(FIELD text)"),
- Latin1Data = ["���������",
+ Latin1Data = ["ÖÄÅÄÖÅäöå",
"testasdf",
"Row 3",
"Row 4",
@@ -1564,7 +1565,7 @@ timestamp(Config) when is_list(Config) ->
%%------------------------------------------------------------------------
w_char_support(Ref, Table, CharType, Size) ->
- Latin1Data = ["���������",
+ Latin1Data = ["ÖÄÅÄÖÅäöå",
"testasdf",
"Row 3",
"Row 4",
diff --git a/lib/odbc/test/odbc_test_lib.erl b/lib/odbc/test/odbc_test_lib.erl
index a8439d5fb6..e814cd2aca 100644
--- a/lib/odbc/test/odbc_test_lib.erl
+++ b/lib/odbc/test/odbc_test_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -29,6 +29,7 @@
unique_table_name() ->
lists:reverse(lists:foldl(fun($@, Acc) -> [$t, $A |Acc] ;
+ ($-,Acc) -> Acc;
(X, Acc) -> [X |Acc] end,
[], atom_to_list(node()))).
diff --git a/lib/odbc/vsn.mk b/lib/odbc/vsn.mk
index fb6e208a52..585b92b2d2 100644
--- a/lib/odbc/vsn.mk
+++ b/lib/odbc/vsn.mk
@@ -1 +1 @@
-ODBC_VSN = 2.10.12
+ODBC_VSN = 2.10.14
diff --git a/lib/orber/COSS/CosNaming/Makefile b/lib/orber/COSS/CosNaming/Makefile
index a61fcd08a6..814062034c 100644
--- a/lib/orber/COSS/CosNaming/Makefile
+++ b/lib/orber/COSS/CosNaming/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -117,7 +117,7 @@ clean:
rm -f errs core *~
$(APP_TARGET): $(APP_SRC)
- sed -e 's;%VSN%;$(VSN);' $(APP_SRC) > $(APP_TARGET)
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $(APP_SRC) > $(APP_TARGET)
docs:
@@ -125,10 +125,10 @@ docs:
# Special Build Targets
# ----------------------------------------------------
IDL-GENERATED: cos_naming_ext.idl cos_naming.idl
- erlc $(ERL_IDL_FLAGS) +'{this,"CosNaming::NamingContext"}' \
+ $(gen_verbose)erlc $(ERL_IDL_FLAGS) +'{this,"CosNaming::NamingContext"}' \
+'{this,"CosNaming::NamingContextExt"}' cos_naming_ext.idl
- erlc $(ERL_IDL_FLAGS) +'{this,"CosNaming::NamingContext"}' cos_naming.idl
- >IDL-GENERATED
+ $(V_at)erlc $(ERL_IDL_FLAGS) +'{this,"CosNaming::NamingContext"}' cos_naming.idl
+ $(V_at)>IDL-GENERATED
$(GEN_FILES): IDL-GENERATED
diff --git a/lib/orber/c_src/Makefile.in b/lib/orber/c_src/Makefile.in
index 5953d41ff3..126ed8af21 100644
--- a/lib/orber/c_src/Makefile.in
+++ b/lib/orber/c_src/Makefile.in
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2009. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -56,16 +56,10 @@ debug opt: $(OBJDIR) orber
ifeq ($(findstring win32,$(TARGET)),win32)
orber:
- echo "Nothing to build on NT"
+ $(V_colon)echo "Nothing to build on NT"
else
-ifeq ($(findstring vxworks,$(TARGET)),vxworks)
orber:
- echo "Nothing to build for VxWorks"
-
-else
-orber:
- echo "Nothing to build"
-endif
+ $(V_colon)echo "Nothing to build"
endif
clean:
@@ -80,7 +74,7 @@ $(OBJDIR):
-mkdir -p $(OBJDIR)
$(OBJDIR)/%.o: %.c
- $(CC) -c -o $@ $(ALL_CFLAGS) $<
+ $(V_CC) -c -o $@ $(ALL_CFLAGS) $<
# ----------------------------------------------------
# Release Target
@@ -94,20 +88,12 @@ release_spec: opt
$(INSTALL_PROGRAM) $(CC_FILES) "$(RELSYSDIR)/priv/src"
$(INSTALL_PROGRAM) $(HH_FILES) "$(RELSYSDIR)/priv/include"
else
-ifeq ($(findstring vxworks,$(TARGET)),vxworks)
-release_spec:
- $(INSTALL_DIR) "$(RELSYSDIR)/priv/src"
- $(INSTALL_DIR) "$(RELSYSDIR)/priv/include"
- $(INSTALL_PROGRAM) $(CC_FILES) "$(RELSYSDIR)/priv/src"
- $(INSTALL_PROGRAM) $(HH_FILES) "$(RELSYSDIR)/priv/include"
-else
release_spec: opt
$(INSTALL_DIR) "$(RELSYSDIR)/priv/src"
$(INSTALL_DIR) "$(RELSYSDIR)/priv/include"
$(INSTALL_DATA) $(CC_FILES) "$(RELSYSDIR)/priv/src"
$(INSTALL_DATA) $(HH_FILES) "$(RELSYSDIR)/priv/include"
endif
-endif
release_docs_spec:
diff --git a/lib/orber/doc/src/Makefile b/lib/orber/doc/src/Makefile
index c429c1d80b..4cd7c45437 100644
--- a/lib/orber/doc/src/Makefile
+++ b/lib/orber/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
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 964ae3e92d..1fd2f644cb 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>2010</year>
+ <year>1997</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -1142,7 +1142,8 @@ handle_info(_Info, State) ->
module, and <c>lookup</c> in <c>DB_Administrator_impl.erl</c><em>and</em><c>DB_CommonUser_impl.erl</c>. But wait, is that really necessary? Actually,
it is not. We simple use the IC compile option <em>impl</em>:</p>
<pre>
-$ <input>erlc +'{{impl, "DB::CommonUser"}, "DBUser_impl"}' +'{{impl, "DB::Administrator"}, "DBUser_impl"}' DB.idl</input>
+$ <input>erlc +'{{impl, "DB::CommonUser"}, "DBUser_impl"}'\
+ +'{{impl, "DB::Administrator"}, "DBUser_impl"}' DB.idl</input>
$ <input>erlc *.erl</input>
</pre>
<p>Instead of creating, and not the least, maintaining two call-back modules,
diff --git a/lib/orber/doc/src/notes.xml b/lib/orber/doc/src/notes.xml
index 72cd808d20..d561199998 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>2011</year>
+ <year>1997</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
diff --git a/lib/orber/examples/Stack/Makefile b/lib/orber/examples/Stack/Makefile
index b03d7c3b86..1cbb983cd6 100644
--- a/lib/orber/examples/Stack/Makefile
+++ b/lib/orber/examples/Stack/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -104,8 +104,8 @@ docs:
test: $(TEST_TARGET_FILES)
IDL-GENERATED: stack.idl
- erlc $(ERL_IDL_FLAGS) stack.idl
- >IDL-GENERATED
+ $(gen_verbose)erlc $(ERL_IDL_FLAGS) stack.idl
+ $(V_at)>IDL-GENERATED
$(GEN_FILES): IDL-GENERATED
diff --git a/lib/orber/java_src/Orber/Makefile b/lib/orber/java_src/Orber/Makefile
index 4e52ed3635..f7f7336622 100644
--- a/lib/orber/java_src/Orber/Makefile
+++ b/lib/orber/java_src/Orber/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2009. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/orber/priv/Makefile b/lib/orber/priv/Makefile
index 7957f7bbf0..cd361e3f0a 100644
--- a/lib/orber/priv/Makefile
+++ b/lib/orber/priv/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2001-2011. All Rights Reserved.
+# Copyright Ericsson AB 2001-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/orber/src/Makefile b/lib/orber/src/Makefile
index a57d6d948b..1c6781e5fd 100644
--- a/lib/orber/src/Makefile
+++ b/lib/orber/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -200,8 +200,7 @@ ERL_COMPILE_FLAGS += $(ERL_IDL_FLAGS) \
+'{attribute,insert,app_vsn,"orber_$(ORBER_VSN)"}' \
-D'ORBVSN="$(ORBER_VSN)"'
-ASN_FLAGS = -bber_bin +der +compact_bit_string +optimize \
- +nowarn_unused_record
+ASN_FLAGS = -bber +der +compact_bit_string +nowarn_unused_record
# ----------------------------------------------------
# Targets
@@ -216,10 +215,10 @@ clean:
rm -f errs core *~
$(APP_TARGET): $(APP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(ORBER_VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(ORBER_VSN);' $< > $@
$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(ORBER_VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(ORBER_VSN);' $< > $@
docs:
@@ -228,17 +227,17 @@ docs:
# ----------------------------------------------------
IDL-GENERATED: $(ERL_TOP)/lib/ic/include/erlang.idl CORBA.idl OrberIFR.idl
- erlc $(ERL_IDL_FLAGS) $(ERL_TOP)/lib/ic/include/erlang.idl
- erlc $(ERL_IDL_FLAGS) CORBA.idl
- erlc $(ERL_IDL_FLAGS) +'{this,"Orber::IFR"}' OrberIFR.idl
- >IDL-GENERATED
+ $(gen_verbose)erlc $(ERL_IDL_FLAGS) $(ERL_TOP)/lib/ic/include/erlang.idl
+ $(V_at)erlc $(ERL_IDL_FLAGS) CORBA.idl
+ $(V_at)erlc $(ERL_IDL_FLAGS) +'{this,"Orber::IFR"}' OrberIFR.idl
+ $(V_at)>IDL-GENERATED
$(GEN_ERL_FILES): IDL-GENERATED
$(TARGET_FILES): IDL-GENERATED
$(GEN_ASN_ERL) $(GEN_ASN_HRL): OrberCSIv2.asn1 OrberCSIv2.set.asn
- erlc $(ERL_COMPILE_FLAGS) $(ASN_FLAGS) +'{inline,"OrberCSIv2"}' OrberCSIv2.set.asn
- rm -f $(GEN_ASN_ERL:%.erl=%.beam)
+ $(asn_verbose)erlc $(ERL_COMPILE_FLAGS) $(ASN_FLAGS) +'{inline,"OrberCSIv2"}' OrberCSIv2.set.asn
+ $(V_at)rm -f $(GEN_ASN_ERL:%.erl=%.beam)
# erlc $(ERL_COMPILE_FLAGS) $(ASN_FLAGS) OrberCSIv2.asn1 ;\
# erlc $(GEN_ASN_ERL)
diff --git a/lib/orber/src/orber_ifr_exceptiondef.erl b/lib/orber/src/orber_ifr_exceptiondef.erl
index 7665d3d1bc..94c25cc4a5 100644
--- a/lib/orber/src/orber_ifr_exceptiondef.erl
+++ b/lib/orber/src/orber_ifr_exceptiondef.erl
@@ -111,26 +111,6 @@ cleanup_for_destroy({ObjType,ObjID}) ?tcheck(ir_ExceptionDef, ObjType) ->
describe({ObjType, ObjID}) ?tcheck(ir_ExceptionDef, ObjType) ->
orber_ifr_contained:describe({ObjType,ObjID}).
-%%% *** This function should be removed. Use
-%%% orber_ifr_repository:lookup_id/2 instead.
-
-%%lookup_id(SearchId) ->
-%% _F = fun() ->
-%% Q = query [X.ir_Internal_ID || X <- table(ir_ExceptionDef)]
-%% end,
-%% mnemosyne:eval(Q)
-%% end,
-%% case orber_ifr_utils:ifr_transaction_read(_F) of
-%% ?read_check_2() ->
-%% {ok, []};
-%% ?read_check_1(Rep_IDs) ->
-%% ExceptionDefs = lists:map(fun(X) -> {ir_ExceptionDef, X} end,
-%% Rep_IDs),
-%% {ok, lists:filter(fun(X) -> orber_ifr_exceptiondef:'_get_id'(X) ==
-%% SearchId end,
-%% ExceptionDefs)}
-%% end.
-
move({ObjType, ObjID}, New_container, New_name, New_version)
?tcheck(ir_ExceptionDef, ObjType) ->
orber_ifr_contained:move({ObjType,ObjID},New_container,New_name,
diff --git a/lib/orber/src/orber_iiop_net.erl b/lib/orber/src/orber_iiop_net.erl
index 2eed0b538a..33e02e3c04 100644
--- a/lib/orber/src/orber_iiop_net.erl
+++ b/lib/orber/src/orber_iiop_net.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -415,11 +415,10 @@ handle_info({'EXIT', Pid, _Reason}, State) when is_pid(Pid) ->
ref = Ref, options = Options, proxy_options = POpts}] ->
ets:delete(?CONNECTION_DB, Pid),
unlink(Pid),
- NewListen = new_listen_socket(Type, Listen, Port, Options),
- {ok, NewPid} = orber_iiop_socketsup:start_accept(Type, NewListen,
+ {ok, NewPid} = orber_iiop_socketsup:start_accept(Type, Listen,
Ref, POpts),
link(NewPid),
- ets:insert(?CONNECTION_DB, #listen{pid = NewPid, socket = NewListen,
+ ets:insert(?CONNECTION_DB, #listen{pid = NewPid, socket = Listen,
port = Port, type = Type,
ref = Ref, options = Options,
proxy_options = POpts}),
@@ -445,23 +444,6 @@ handle_info({'EXIT', Pid, _Reason}, State) when is_pid(Pid) ->
handle_info(_, State) ->
{noreply, State}.
-new_listen_socket(normal, ListenFd, _Port, _Options) ->
- ListenFd;
-new_listen_socket(ssl, ListenFd, Port, Options) ->
- Generation = orber_env:ssl_generation(),
- if
- Generation > 2 ->
- ListenFd;
- true ->
- case is_process_alive(ssl:pid(ListenFd)) of
- true ->
- ListenFd;
- _ ->
- {ok, Listen, _NP} = orber_socket:listen(ssl, Port, Options, true),
- Listen
- end
- end.
-
from_list(List) ->
from_list(List, queue:new()).
diff --git a/lib/orber/test/Makefile b/lib/orber/test/Makefile
index 8eef1934a4..2f8777c773 100644
--- a/lib/orber/test/Makefile
+++ b/lib/orber/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/orber/test/csiv2_SUITE.erl b/lib/orber/test/csiv2_SUITE.erl
index 60ffa1eb09..b89bf0a56c 100644
--- a/lib/orber/test/csiv2_SUITE.erl
+++ b/lib/orber/test/csiv2_SUITE.erl
@@ -668,67 +668,57 @@ code_OpenSSL509_api(_Config) ->
ssl_server_peercert_api(doc) -> ["Test ssl:peercert (server side)"];
ssl_server_peercert_api(suite) -> [];
ssl_server_peercert_api(_Config) ->
- case os:type() of
- vxworks ->
- {skipped, "No SSL-support for VxWorks."};
- _ ->
- Options = orber_test_lib:get_options(iiop_ssl, server,
- 2, [{iiop_ssl_port, 0}]),
- {ok, ServerNode, ServerHost} =
- ?match({ok,_,_}, orber_test_lib:js_node(Options)),
- ServerPort = orber_test_lib:remote_apply(ServerNode, orber, iiop_ssl_port, []),
- SSLOptions = orber_test_lib:get_options(ssl, client),
- {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, #'Certificate'{}},
- % 'OrberCSIv2':decode('Certificate', PeerCert)),
- destroy_fake_ORB(ssl, Socket),
- ok
- end.
+ Options = orber_test_lib:get_options(iiop_ssl, server,
+ 2, [{iiop_ssl_port, 0}]),
+ {ok, ServerNode, ServerHost} =
+ ?match({ok,_,_}, orber_test_lib:js_node(Options)),
+ ServerPort = orber_test_lib:remote_apply(ServerNode, orber, iiop_ssl_port, []),
+ SSLOptions = orber_test_lib:get_options(ssl, client),
+ {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, #'Certificate'{}},
+ % 'OrberCSIv2':decode('Certificate', PeerCert)),
+ destroy_fake_ORB(ssl, Socket),
+ ok.
ssl_client_peercert_api(doc) -> ["Test ssl:peercert (client side)"];
ssl_client_peercert_api(suite) -> [];
ssl_client_peercert_api(_Config) ->
- case os:type() of
- vxworks ->
- {skipped, "No SSL-support for VxWorks."};
- _ ->
- Options = orber_test_lib:get_options(iiop_ssl, client,
- 2, [{iiop_ssl_port, 0}]),
- {ok, ClientNode, _ClientHost} =
- ?match({ok,_,_}, orber_test_lib:js_node(Options)),
- crypto:start(),
- ssl:start(),
- SSLOptions = orber_test_lib:get_options(ssl, server),
- {ok, LSock} = ?match({ok, _}, ssl:listen(0, SSLOptions)),
- {ok, {_Address, LPort}} = ?match({ok, {_, _}}, ssl:sockname(LSock)),
- IOR = ?match({'IOP_IOR',_,_},
- iop_ior:create_external({1, 2}, "IDL:FAKE:1.0",
- "localhost", 6004, "FAKE",
- [#'IOP_TaggedComponent'
- {tag=?TAG_SSL_SEC_TRANS,
- component_data=#'SSLIOP_SSL'
- {target_supports = 2,
- target_requires = 2,
- port = LPort}}])),
- spawn(orber_test_lib, remote_apply,
- [ClientNode, corba_object, non_existent, [IOR]]),
- {ok, Socket} = ?match({ok, _}, ssl:transport_accept(LSock)),
- ?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, #'Certificate'{}},
- % 'OrberCSIv2':decode('Certificate', PeerCert)),
- ssl:close(Socket),
- ssl:close(LSock),
- ssl:stop(),
- ok
- end.
+ Options = orber_test_lib:get_options(iiop_ssl, client,
+ 2, [{iiop_ssl_port, 0}]),
+ {ok, ClientNode, _ClientHost} =
+ ?match({ok,_,_}, orber_test_lib:js_node(Options)),
+ crypto:start(),
+ ssl:start(),
+ SSLOptions = orber_test_lib:get_options(ssl, server),
+ {ok, LSock} = ?match({ok, _}, ssl:listen(0, SSLOptions)),
+ {ok, {_Address, LPort}} = ?match({ok, {_, _}}, ssl:sockname(LSock)),
+ IOR = ?match({'IOP_IOR',_,_},
+ iop_ior:create_external({1, 2}, "IDL:FAKE:1.0",
+ "localhost", 6004, "FAKE",
+ [#'IOP_TaggedComponent'
+ {tag=?TAG_SSL_SEC_TRANS,
+ component_data=#'SSLIOP_SSL'
+ {target_supports = 2,
+ target_requires = 2,
+ port = LPort}}])),
+ spawn(orber_test_lib, remote_apply,
+ [ClientNode, corba_object, non_existent, [IOR]]),
+ {ok, Socket} = ?match({ok, _}, ssl:transport_accept(LSock)),
+ ?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, #'Certificate'{}},
+ % 'OrberCSIv2':decode('Certificate', PeerCert)),
+ ssl:close(Socket),
+ ssl:close(LSock),
+ ssl:stop(),
+ ok.
%%-----------------------------------------------------------------
%% Local functions.
diff --git a/lib/orber/test/orber_test_lib.erl b/lib/orber/test/orber_test_lib.erl
index 2bfa1ef871..6824d25aef 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-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -278,24 +278,13 @@ check_options(Options) ->
end.
starter(Host, Name, Args) ->
- case os:type() of
- vxworks ->
- test_server:start_node(Name, slave, [{args,Args}]);
- _ ->
- io:format("slave:start_link(~p,~p,~p).~n",[Host,Name,Args]),
- slave:start_link(Host, Name, Args)
- end.
+ io:format("slave:start_link(~p,~p,~p).~n",[Host,Name,Args]),
+ slave:start_link(Host, Name, Args).
slave_sup() ->
process_flag(trap_exit, true),
receive
- {'EXIT', _, _} ->
- case os:type() of
- vxworks ->
- erlang:halt();
- _ ->
- ignore
- end
+ {'EXIT', _, _} -> ignore
end.
start_ssl(true, Node) ->
@@ -419,12 +408,7 @@ destroy_node(Node, Type) ->
stopper(Node, Type).
stopper(Node, _Type) ->
- case os:type() of
- vxworks ->
- test_server:stop_node(Node);
- _ ->
- slave:stop(Node)
- end.
+ slave:stop(Node).
%%------------------------------------------------------------
diff --git a/lib/os_mon/c_src/Makefile.in b/lib/os_mon/c_src/Makefile.in
index 45dc01e993..51569f6ec9 100644
--- a/lib/os_mon/c_src/Makefile.in
+++ b/lib/os_mon/c_src/Makefile.in
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -63,14 +63,10 @@ ENTRY_OBJ=$(ERL_TOP)/erts/obj/$(TARGET)/port_entry.o
PORT_ENTRY_POINT=erl_port_entry
ENTRY_LDFLAGS=-entry:$(PORT_ENTRY_POINT)
else
-ifeq ($(findstring vxworks_simso,$(TARGET)),vxworks_simso)
-PROGRAMS =
-else
PROGRAMS = \
memsup @os_mon_programs@
C_FILES= $(PROGRAMS:%=%.c)
endif
-endif
TARGET_FILES= $(PROGRAMS:%=$(BINDIR)/%)
@@ -97,28 +93,28 @@ docs:
# ----------------------------------------------------
$(BINDIR)/win32sysinfo.exe: $(OBJDIR)/win32sysinfo.o $(ENTRY_OBJ)
- $(LD) $(LDFLAGS) $(ENTRY_LDFLAGS) -o $@ $(OBJDIR)/win32sysinfo.o $(ENTRY_OBJ)
+ $(V_LD) $(LDFLAGS) $(ENTRY_LDFLAGS) -o $@ $(OBJDIR)/win32sysinfo.o $(ENTRY_OBJ)
$(BINDIR)/nteventlog.exe: $(EVLOG_OBJECTS)
- $(LD) $(LDFLAGS) $(ENTRY_LDFLAGS) -o $@ $(EVLOG_OBJECTS) $(ENTRY_OBJ)
+ $(V_LD) $(LDFLAGS) $(ENTRY_LDFLAGS) -o $@ $(EVLOG_OBJECTS) $(ENTRY_OBJ)
$(BINDIR)/ferrule: $(OBJDIR)/ferrule.o
- $(LD) $(LDFLAGS) -o $@ $<
+ $(V_LD) $(LDFLAGS) -o $@ $<
$(BINDIR)/mod_syslog: $(OBJDIR)/mod_syslog.o
- $(LD) $(LDFLAGS) -o $@ $<
+ $(V_LD) $(LDFLAGS) -o $@ $<
$(BINDIR)/memsup: $(OBJDIR)/memsup.o
- $(LD) $(LDFLAGS) -o $@ $<
+ $(V_LD) $(LDFLAGS) -o $@ $<
$(BINDIR)/cpu_sup: $(OBJDIR)/cpu_sup.o
- $(LD) $(LDFLAGS) -o $@ $< $(CPU_SUP_LIBS)
+ $(V_LD) $(LDFLAGS) -o $@ $< $(CPU_SUP_LIBS)
$(OBJDIR)/%.o: %.c
- $(CC) -c -o $@ $(ALL_CFLAGS) $<
+ $(V_CC) -c -o $@ $(ALL_CFLAGS) $<
$(OBJDIR)/%.o: nteventlog/%.c
- $(CC) -c -o $@ $(ALL_CFLAGS) $<
+ $(V_CC) -c -o $@ $(ALL_CFLAGS) $<
$(OBJDIR)/memsup.o: memsup.h
@@ -127,14 +123,10 @@ $(OBJDIR)/memsup.o: memsup.h
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
-ifeq ($(findstring vxworks_simso,$(TARGET)),vxworks_simso)
-release_spec:
-else
release_spec: opt
$(INSTALL_DIR) "$(RELSYSDIR)/src"
$(INSTALL_DATA) $(C_FILES) "$(RELSYSDIR)/src"
$(INSTALL_DIR) "$(RELSYSDIR)/priv/bin"
$(INSTALL_PROGRAM) $(TARGET_FILES) "$(RELSYSDIR)/priv/bin"
-endif
release_docs_spec:
diff --git a/lib/os_mon/c_src/cpu_sup.c b/lib/os_mon/c_src/cpu_sup.c
index 9c5f9a6aa5..7372d5b0e8 100644
--- a/lib/os_mon/c_src/cpu_sup.c
+++ b/lib/os_mon/c_src/cpu_sup.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1997-2011. All Rights Reserved.
+ * Copyright Ericsson AB 1997-2012. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -458,8 +458,18 @@ static void error(char* err_msg) {
* if we get error here we have trouble,
* silence unnecessary warnings
*/
- if(write(FD_ERR, err_msg, strlen(err_msg)));
- if(write(FD_ERR, "\n", 1));
+ char buffer[256] = "[os_mon] cpu supervisor port (cpu_sup): ";
+ int i = strlen(buffer), j = 0;
+ int n = strlen(err_msg);
+
+ while(i < 253 && j < n) {
+ buffer[i++] = err_msg[j++];
+ }
+ buffer[i++] = '\r';
+ buffer[i++] = '\n';
+
+ /* try to use one write only */
+ if(write(FD_ERR, buffer, i));
exit(-1);
}
diff --git a/lib/os_mon/c_src/memsup.c b/lib/os_mon/c_src/memsup.c
index 078f20ff98..3a1a8e9444 100644
--- a/lib/os_mon/c_src/memsup.c
+++ b/lib/os_mon/c_src/memsup.c
@@ -31,7 +31,7 @@
*
* This program is started from Erlang as follows,
*
- * Port = open_port({spawn, 'memsup'}, [{packet,1}]) for UNIX and VxWorks
+ * Port = open_port({spawn, 'memsup'}, [{packet,1}]) for UNIX
*
* Erlang sends one of the request condes defined in memsup.h and this program
* answers in one of two ways:
@@ -75,10 +75,6 @@
* that there is no process at the other end of the connection
* having the connection open for writing (end-of-file).
*
- * COMPILING
- *
- * When the target is VxWorks the identifier VXWORKS must be defined for
- * the preprocessor (usually by a -D option).
*/
#if defined(sgi) || defined(__sgi) || defined(__sgi__)
@@ -90,9 +86,7 @@
#include <stddef.h>
#include <stdlib.h>
-#ifndef VXWORKS
#include <unistd.h>
-#endif
#if (defined(__unix__) || defined(unix)) && !defined(USG)
#include <sys/param.h>
@@ -104,12 +98,6 @@
#include <time.h>
#include <errno.h>
-#ifdef VXWORKS
-#include <vxWorks.h>
-#include <ioLib.h>
-#include <memLib.h>
-#endif
-
#ifdef BSD4_4
#include <sys/types.h>
#include <sys/sysctl.h>
@@ -143,20 +131,8 @@
/* prototypes */
static void print_error(const char *,...);
-#ifdef VXWORKS
-extern int erl_mem_info_get(MEM_PART_STATS *);
-#endif
-
-#ifdef VXWORKS
-#define MAIN memsup
-static MEM_PART_STATS latest;
-static unsigned long latest_system_total; /* does not fit in the struct */
-
-#else
#define MAIN main
-#endif
-
/*
* example, we want procfs information, now give them something equivalent:
@@ -282,16 +258,6 @@ send_tag(int value){
}
}
-
-#ifdef VXWORKS
-static void load_statistics(void){
- if(memPartInfoGet(memSysPartId,&latest) != OK)
- memset(&latest,0,sizeof(latest));
- latest_system_total = latest.numBytesFree + latest.numBytesAlloc;
- erl_mem_info_get(&latest); /* if it fails, latest is untouched */
-}
-#endif
-
#ifdef BSD4_4
static int
get_vmtotal(struct vmtotal *vt) {
@@ -358,19 +324,6 @@ get_mem_procfs(memory_ext *me){
/* arch specific functions */
-#if defined(VXWORKS)
-static int
-get_extended_mem_vxwork(memory_ext *me) {
- load_statistics();
- me->total = (latest.numBytesFree + latest.numBytesAlloc);
- me->free = latest.numBytesFree;
- me->pagesize = 1;
- me->flag = F_MEM_TOTAL | F_MEM_FREE;
- return 1;
-}
-#endif
-
-
#if defined(__linux__) /* ifdef SYSINFO */
/* sysinfo does not include cached memory which is a problem. */
static int
@@ -442,12 +395,8 @@ get_extended_mem_sgi(memory_ext *me) {
static void
get_extended_mem(memory_ext *me) {
-/* vxworks */
-#if defined(VXWORKS)
- if (get_extended_mem_vxworks(me)) return;
-
/* linux */
-#elif defined(__linux__)
+#if defined(__linux__)
if (get_mem_procfs(me)) return;
if (get_extended_mem_sysinfo(me)) return;
@@ -477,12 +426,7 @@ get_extended_mem(memory_ext *me) {
static void
get_basic_mem(unsigned long *tot, unsigned long *used, unsigned long *pagesize){
-#if defined(VXWORKS)
- load_statistics();
- *tot = (latest.numBytesFree + latest.numBytesAlloc);
- *used = latest.numBytesAlloc;
- *pagesize = 1;
-#elif defined(_SC_AVPHYS_PAGES) /* Does this exist on others than Solaris2? */
+#if defined(_SC_AVPHYS_PAGES) /* Does this exist on others than Solaris2? */
unsigned long avPhys, phys, pgSz;
phys = sysconf(_SC_PHYS_PAGES);
@@ -493,7 +437,7 @@ get_basic_mem(unsigned long *tot, unsigned long *used, unsigned long *pagesize){
#elif defined(__linux__) && !defined(_SC_AVPHYS_PAGES)
memory_ext me;
if (get_mem_procfs(&me) < 0) {
- print_error("ProcFS read error.");
+ print_error("ProcFS read error");
exit(1);
}
*tot = me.total;
@@ -557,17 +501,8 @@ extended_show_mem(void){
if (me.flag & F_SWAP_TOTAL) { send_tag(SWAP_TOTAL); send(me.total_swap, ps); }
if (me.flag & F_SWAP_FREE) { send_tag(SWAP_FREE); send(me.free_swap, ps); }
-#ifdef VXWORKS
- send_tag(SM_SYSTEM_TOTAL);
- send(latest_system_total, 1);
- send_tag(SM_LARGEST_FREE);
- send(latest.maxBlockSizeFree, 1);
- send_tag(SM_NUMBER_OF_FREE);
- send(latest.numBlocksFree, 1);
-#else
/* total is system total*/
if (me.flag & F_MEM_TOTAL) { send_tag(MEM_SYSTEM_TOTAL); send(me.total, ps); }
-#endif
send_tag(SHOW_SYSTEM_MEM_END);
}
@@ -582,7 +517,7 @@ message_loop(int erlin_fd)
* Wait for command from Erlang
*/
if ((res = read(erlin_fd, &cmdLen, 1)) < 0) {
- print_error("Error reading from Erlang.");
+ print_error("Error reading from Erlang");
return;
}
@@ -603,19 +538,19 @@ message_loop(int erlin_fd)
break;
case 0:
- print_error("Erlang has closed.");
+ print_error("Erlang has closed");
return;
default:
- print_error("Error reading from Erlang.");
+ print_error("Error reading from Erlang");
return;
} /* switch() */
} else { /* cmdLen != 1 */
- print_error("Invalid command length (%d) received.", cmdLen);
+ print_error("Invalid command length (%d) received", cmdLen);
return;
}
} else { /* Erlang end closed */
- print_error("Erlang has closed.");
+ print_error("Erlang has closed");
return;
}
}
@@ -641,15 +576,12 @@ static void
print_error(const char *format,...)
{
va_list args;
+ char buffer[256];
va_start(args, format);
- fprintf(stderr, "%s: ", program_name);
- vfprintf(stderr, format, args);
+ vsnprintf(buffer, 256, format, args);
va_end(args);
- fprintf(stderr, " \n");
+ /* try to use one write only */
+ fprintf(stderr, "[os_mon] memory supervisor port (memsup): %s\r\n", buffer);
+ fflush(stderr);
}
-
-
-
-
-
diff --git a/lib/os_mon/c_src/win32sysinfo.c b/lib/os_mon/c_src/win32sysinfo.c
index 2a155aae87..f02ae637b2 100644
--- a/lib/os_mon/c_src/win32sysinfo.c
+++ b/lib/os_mon/c_src/win32sysinfo.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1997-2009. All Rights Reserved.
+ * Copyright Ericsson AB 1997-2012. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -89,6 +89,7 @@ typedef BOOL (WINAPI *tfpGetDiskFreeSpaceEx)(LPCTSTR, PULARGE_INTEGER,PULARGE_IN
static tfpGetDiskFreeSpaceEx fpGetDiskFreeSpaceEx;
+static void print_error(const char *msg);
static void
return_answer(char* value)
{
@@ -98,7 +99,7 @@ return_answer(char* value)
res = write(1,(char*) &bytes,1);
if (res != 1) {
- fprintf(stderr,"win32sysinfo:Error writing to pipe");
+ print_error("Error writing to pipe");
exit(1);
}
@@ -107,9 +108,8 @@ return_answer(char* value)
while (left > 0)
{
res = write(1, value+bytes-left, left);
- if (res <= 0)
- {
- fprintf(stderr,"win32sysinfo:Error writing to pipe");
+ if (res <= 0) {
+ print_error("Error writing to pipe");
exit(1);
}
left -= res;
@@ -248,7 +248,6 @@ message_loop()
char cmd[512];
int res;
- fprintf(stderr,"in message_loop\n");
/* Startup ACK. */
return_answer(OK);
while (1)
@@ -257,12 +256,12 @@ message_loop()
* Wait for command from Erlang
*/
if ((res = read(0, &cmdLen, 1)) < 0) {
- fprintf(stderr,"win32sysinfo:Error reading from Erlang.");
+ print_error("Error reading from Erlang");
return;
}
if (res != 1){ /* Exactly one byte read ? */
- fprintf(stderr,"win32sysinfo:Erlang has closed.");
+ print_error("Erlang has closed");
return;
}
if ((res = read(0, &cmd, cmdLen)) == cmdLen){
@@ -291,11 +290,11 @@ message_loop()
return_answer("xEND");
}
else if (res == 0) {
- fprintf(stderr,"win32sysinfo:Erlang has closed.");
+ print_error("Erlang has closed");
return;
}
else {
- fprintf(stderr,"win32sysinfo:Error reading from Erlang.");
+ print_error("Error reading from Erlang");
return;
}
}
@@ -309,10 +308,9 @@ int main(int argc, char ** argv){
message_loop();
return 0;
}
-
-
-
-
-
-
-
+static void
+print_error(const char *msg) {
+ /* try to use one write only */
+ fprintf(stderr, "[os_mon] win32 supervisor port (win32sysinfo): %s\r\n", msg);
+ fflush(stderr);
+}
diff --git a/lib/os_mon/doc/src/Makefile b/lib/os_mon/doc/src/Makefile
index aa8fb4b60c..08fd23ec92 100644
--- a/lib/os_mon/doc/src/Makefile
+++ b/lib/os_mon/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2009. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/os_mon/doc/src/notes.xml b/lib/os_mon/doc/src/notes.xml
index 6674d7b722..061183e51c 100644
--- a/lib/os_mon/doc/src/notes.xml
+++ b/lib/os_mon/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2004</year><year>2011</year>
+ <year>2004</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -30,6 +30,27 @@
</header>
<p>This document describes the changes made to the OS_Mon application.</p>
+<section><title>Os_Mon 2.2.10</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Infinity timeout added to internal calls in disksup to
+ allow it to work properly under very heavy load.</p>
+ <p>
+ Own Id: OTP-10100</p>
+ </item>
+ <item>
+ <p>Clarify error messages from os_mon port programs</p>
+ <p>
+ Own Id: OTP-10161</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Os_Mon 2.2.9</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/os_mon/mibs/Makefile b/lib/os_mon/mibs/Makefile
index a6d8ab5042..19f3dc8367 100644
--- a/lib/os_mon/mibs/Makefile
+++ b/lib/os_mon/mibs/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -67,16 +67,16 @@ OTP_MIBDIR = $(shell if test -d ../../otp_mibs; then echo otp_mibs; \
else echo sasl; fi)
$(SNMP_BIN_TARGET_DIR)/OTP-REG.bin: $(ERL_TOP)/lib/$(OTP_MIBDIR)/mibs/OTP-REG.mib
- $(ERLC) -pa $(SNMP_TOOLKIT)/ebin -I $(SNMP_TOOLKIT)/priv/mibs $(SNMP_FLAGS) -o $(SNMP_BIN_TARGET_DIR) $<
+ $(snmp_verbose)$(ERLC) -pa $(SNMP_TOOLKIT)/ebin -I $(SNMP_TOOLKIT)/priv/mibs $(SNMP_FLAGS) -o $(SNMP_BIN_TARGET_DIR) $<
$(SNMP_BIN_TARGET_DIR)/OTP-TC.bin: $(ERL_TOP)/lib/$(OTP_MIBDIR)/mibs/OTP-TC.mib
- $(ERLC) -pa $(SNMP_TOOLKIT)/ebin -I $(SNMP_TOOLKIT)/priv/mibs $(SNMP_FLAGS) -o $(SNMP_BIN_TARGET_DIR) $<
+ $(snmp_verbose)$(ERLC) -pa $(SNMP_TOOLKIT)/ebin -I $(SNMP_TOOLKIT)/priv/mibs $(SNMP_FLAGS) -o $(SNMP_BIN_TARGET_DIR) $<
$(SNMP_BIN_TARGET_DIR)/OTP-MIB.bin: $(ERL_TOP)/lib/$(OTP_MIBDIR)/mibs/OTP-MIB.mib
- $(ERLC) -pa $(SNMP_TOOLKIT)/ebin -I $(SNMP_TOOLKIT)/priv/mibs $(SNMP_FLAGS) -o $(SNMP_BIN_TARGET_DIR) $<
+ $(snmp_verbose)$(ERLC) -pa $(SNMP_TOOLKIT)/ebin -I $(SNMP_TOOLKIT)/priv/mibs $(SNMP_FLAGS) -o $(SNMP_BIN_TARGET_DIR) $<
v1/%.mib.v1: %.mib
- $(ERL_TOP)/lib/snmp/bin/snmp-v2tov1 -o $@ $<
+ $(gen_verbose)$(ERL_TOP)/lib/snmp/bin/snmp-v2tov1 -o $@ $<
$(SNMP_BIN_TARGET_DIR)/OTP-OS-MON-MIB.bin: \
$(SNMP_BIN_TARGET_DIR)/OTP-REG.bin \
diff --git a/lib/os_mon/src/Makefile b/lib/os_mon/src/Makefile
index a8ad26f6a1..9fc888e552 100644
--- a/lib/os_mon/src/Makefile
+++ b/lib/os_mon/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2009. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -78,10 +78,10 @@ docs:
# ----------------------------------------------------
$(APP_TARGET): $(APP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
#-------------------------------------------------------
# Special dependencies
diff --git a/lib/os_mon/src/disksup.erl b/lib/os_mon/src/disksup.erl
index c7abb7a386..f5f76f1488 100644
--- a/lib/os_mon/src/disksup.erl
+++ b/lib/os_mon/src/disksup.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/os_mon/src/memsup.erl b/lib/os_mon/src/memsup.erl
index 54771b4703..a1b8591c8c 100644
--- a/lib/os_mon/src/memsup.erl
+++ b/lib/os_mon/src/memsup.erl
@@ -185,7 +185,6 @@ init([]) ->
{unix, irix} -> true;
{unix, sunos} -> true;
{win32, _OSname} -> false;
- vxworks -> true;
_ ->
exit({unsupported_os, OS})
end,
@@ -617,8 +616,7 @@ code_change(Vsn, PrevState, "1.8") ->
{unix, openbsd} -> true;
{unix, netbsd} -> true;
{unix, sunos} -> true;
- {win32, _OSname} -> false;
- vxworks -> true
+ {win32, _OSname} -> false
end,
Pid = if
PortMode -> spawn_link(fun() -> port_init() end);
diff --git a/lib/os_mon/src/os_mon.erl b/lib/os_mon/src/os_mon.erl
index 2b6cd7c498..19acae9f0e 100644
--- a/lib/os_mon/src/os_mon.erl
+++ b/lib/os_mon/src/os_mon.erl
@@ -176,11 +176,7 @@ services({unix, sunos}) ->
services({unix, _}) -> % Other unix.
[cpu_sup, disksup, memsup];
services({win32, _}) ->
- [disksup, memsup, os_sup, sysinfo];
-services(vxworks) ->
- [memsup];
-services(_) ->
- [].
+ [disksup, memsup, os_sup, sysinfo].
server_name(cpu_sup) -> cpu_sup;
server_name(disksup) -> disksup;
diff --git a/lib/os_mon/test/Makefile b/lib/os_mon/test/Makefile
index 954cfef7fb..461bebc102 100644
--- a/lib/os_mon/test/Makefile
+++ b/lib/os_mon/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -86,6 +86,7 @@ release_spec:
release_tests_spec: make_emakefile
$(INSTALL_DIR) "$(RELSYSDIR)"
$(INSTALL_DATA) os_mon.spec os_mon.cover $(EMAKEFILE) $(SOURCE) "$(RELSYSDIR)"
+ $(INSTALL_DATA) os_mon_mib_SUITE.cfg "$(RELSYSDIR)"
## tar chf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -)
diff --git a/lib/os_mon/test/os_mon.spec b/lib/os_mon/test/os_mon.spec
index d292b258f3..4b4286b313 100644
--- a/lib/os_mon/test/os_mon.spec
+++ b/lib/os_mon/test/os_mon.spec
@@ -1 +1,2 @@
{suites,"../os_mon_test",all}.
+{config,"os_mon_mib_SUITE.cfg"}. \ No newline at end of file
diff --git a/lib/os_mon/test/os_mon_mib_SUITE.cfg b/lib/os_mon/test/os_mon_mib_SUITE.cfg
new file mode 100644
index 0000000000..a33c23530b
--- /dev/null
+++ b/lib/os_mon/test/os_mon_mib_SUITE.cfg
@@ -0,0 +1,8 @@
+%% -*- erlang -*-
+{snmp, [{start_agent,true},
+ {users,[{os_mon_mib_test,[snmpm_user_default,[]]}]},
+ {managed_agents,[{os_mon_mib_test,
+ [os_mon_mib_test, {127,0,0,1}, 4000, []]}]},
+ {agent_sysname,"Test os_mon_mibs"},
+ {mgr_port,5001}
+ ]}.
diff --git a/lib/os_mon/test/os_mon_mib_SUITE.erl b/lib/os_mon/test/os_mon_mib_SUITE.erl
index a137efc441..08f5532d50 100644
--- a/lib/os_mon/test/os_mon_mib_SUITE.erl
+++ b/lib/os_mon/test/os_mon_mib_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,16 +18,20 @@
%%
-module(os_mon_mib_SUITE).
-%-define(STANDALONE,1).
+%%-----------------------------------------------------------------
+%% This suite can no longer be executed standalone, i.e. it must be
+%% executed with common test. The reason is that ct_snmp is used
+%% instead of the snmp application directly. The suite requires a
+%% config file, os_mon_mib_SUITE.cfg, found in the same directory as
+%% the suite.
+%%
+%% Execute with:
+%% > ct_run -suite os_mon_mib_SUITE -config os_mon_mib_SUITE.cfg
+%%-----------------------------------------------------------------
--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,
@@ -60,15 +64,6 @@
-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].
@@ -78,7 +73,8 @@ end_per_testcase(_Case, Config) when is_list(Config) ->
test_server:timetrap_cancel(Dog),
Config.
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() -> [{ct_hooks,[ts_install_cth]},
+ {require, snmp_mgr_agent, snmp}].
all() ->
[load_unload, get_mem_sys_mark, get_mem_proc_mark,
@@ -104,8 +100,6 @@ end_per_group(_GroupName, Config) ->
Config.
-
--endif.
%%---------------------------------------------------------------------
%%--------------------------------------------------------------------
%% Function: init_per_suite(Config) -> Config
@@ -121,50 +115,13 @@ init_per_suite(Config) ->
?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),
+ ok = ct_snmp:start(Config,snmp_mgr_agent),
%% Load the mibs that should be tested
otp_mib:load(snmp_master_agent),
os_mon_mib:load(snmp_master_agent),
- [{agent_ip, Addr}| Config].
+ Config.
%%--------------------------------------------------------------------
%% Function: end_per_suite(Config) -> _
%% Config - [tuple()]
@@ -197,7 +154,7 @@ end_per_suite(Config) ->
load_unload(doc) ->
["Test to unload and the reload the OTP.mib "];
load_unload(suite) -> [];
-load_unload(Config) when list(Config) ->
+load_unload(Config) when is_list(Config) ->
?line os_mon_mib:unload(snmp_master_agent),
?line os_mon_mib:load(snmp_master_agent),
ok.
@@ -424,7 +381,7 @@ cpu_load(doc) ->
[];
cpu_load(suite) ->
[];
-cpu_load(Config) when list(Config) ->
+cpu_load(Config) when is_list(Config) ->
?line [{[?loadCpuLoad, Len | NodeStr], Load}] =
os_mon_mib:load_table(get_next,[], [?loadCpuLoad]),
?line Len = length(NodeStr),
@@ -640,32 +597,24 @@ disk_capacity(Config) when is_list(Config) ->
%%---------------------------------------------------------------------
real_snmp_request(doc) ->
- ["Starts an snmp manager and sends a real snmp-reques. i.e. "
+ ["Starts an snmp manager and sends a real snmp-request. 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),
-
+real_snmp_request(Config) when is_list(Config) ->
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 ++
+ ?line ok = snmp_get([?loadEntry ++
[?loadLargestErlProcess, Len | NodStr]],
PidStr),
- ?line ok = snmp_get_next(Agent_ip,
- [?loadEntry ++
+ ?line ok = snmp_get_next([?loadEntry ++
[?loadSystemUsedMemory, Len | NodStr]],
?loadEntry ++ [?loadSystemUsedMemory + 1, Len
| NodStr], PidStr),
- ?line ok = snmp_set(Agent_ip, [?loadEntry ++
- [?loadLargestErlProcess, Len | NodStr]],
- s, "<0.101.0>"),
+ ?line ok = snmp_set([?loadEntry ++ [?loadLargestErlProcess, Len | NodStr]],
+ s, "<0.101.0>", Config),
ok.
otp_7441(doc) ->
@@ -674,34 +623,17 @@ otp_7441(doc) ->
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),
+ {noError,0,[#varbind{oid = Oid, variabletype = 'Unsigned32'}]} =
+ ct_snmp:get_values(os_mon_mib_test, Oids, snmp_mgr_agent),
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,
@@ -711,8 +643,6 @@ start_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),
@@ -722,21 +652,22 @@ del_dir(Dir) ->
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),
+snmp_get(Oids = [Oid |_], Result) ->
+ {noError,0,[#varbind{oid = Oid,
+ variabletype = 'OCTET STRING',
+ value = Result}]} =
+ ct_snmp:get_values(os_mon_mib_test, Oids, snmp_mgr_agent),
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),
+snmp_get_next(Oids, NextOid, Result) ->
+ {noError,0,[#varbind{oid = NextOid,
+ variabletype = 'OCTET STRING',
+ value = Result}]} =
+ ct_snmp:get_next_values(os_mon_mib_test, Oids, snmp_mgr_agent),
ok.
-snmp_set(Agent_ip, Oid, ValuType, Value) ->
- ?line {ok, {notWritable, _, _}, _} =
- snmpm:s(os_mon_mib_test,Agent_ip,?AGENT_UDP,[{Oid, ValuType, Value}]),
+snmp_set(Oid, ValuType, Value, Config) ->
+ {notWritable, _, _} =
+ ct_snmp:set_values(os_mon_mib_test, [{Oid, ValuType, Value}],
+ snmp_mgr_agent, Config),
ok.
diff --git a/lib/os_mon/vsn.mk b/lib/os_mon/vsn.mk
index b92bdf3c1a..180906127c 100644
--- a/lib/os_mon/vsn.mk
+++ b/lib/os_mon/vsn.mk
@@ -1 +1 @@
-OS_MON_VSN = 2.2.9
+OS_MON_VSN = 2.2.10
diff --git a/lib/otp_mibs/doc/src/Makefile b/lib/otp_mibs/doc/src/Makefile
index 16a5bb39b4..6b8610bc23 100644
--- a/lib/otp_mibs/doc/src/Makefile
+++ b/lib/otp_mibs/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2003-2009. All Rights Reserved.
+# Copyright Ericsson AB 2003-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/otp_mibs/mibs/Makefile b/lib/otp_mibs/mibs/Makefile
index 7ef8480b3e..7f43ef31a6 100644
--- a/lib/otp_mibs/mibs/Makefile
+++ b/lib/otp_mibs/mibs/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2009. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -68,7 +68,7 @@ docs:
# ----------------------------------------------------
v1/%.mib.v1: %.mib
- $(ERL_TOP)/lib/snmp/bin/snmp-v2tov1 -o $@ $<
+ $(gen_verbose)$(ERL_TOP)/lib/snmp/bin/snmp-v2tov1 -o $@ $<
# ----------------------------------------------------
# Release Target
diff --git a/lib/otp_mibs/src/Makefile b/lib/otp_mibs/src/Makefile
index c9c40fec3a..4f03d0228a 100644
--- a/lib/otp_mibs/src/Makefile
+++ b/lib/otp_mibs/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2003-2009. All Rights Reserved.
+# Copyright Ericsson AB 2003-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -84,10 +84,10 @@ docs:
# ----------------------------------------------------
$(APP_TARGET): $(APP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
# ----------------------------------------------------
# Release Target
diff --git a/lib/parsetools/doc/src/Makefile b/lib/parsetools/doc/src/Makefile
index 464ea66af0..96879084d8 100644
--- a/lib/parsetools/doc/src/Makefile
+++ b/lib/parsetools/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2009. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/parsetools/include/yeccpre.hrl b/lib/parsetools/include/yeccpre.hrl
index 3672394fc5..e4c3ba52be 100644
--- a/lib/parsetools/include/yeccpre.hrl
+++ b/lib/parsetools/include/yeccpre.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -36,7 +36,7 @@ parse_and_scan({M, F, A}) ->
-spec format_error(any()) -> [char() | list()].
format_error(Message) ->
- case io_lib:deep_char_list(Message) of
+ case io_lib:deep_unicode_char_list(Message) of
true ->
Message;
_ ->
@@ -164,7 +164,7 @@ yecctoken_location(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({char,_,C}) -> io_lib:write_unicode_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);
diff --git a/lib/parsetools/src/Makefile b/lib/parsetools/src/Makefile
index e228ee88ef..7b63475231 100644
--- a/lib/parsetools/src/Makefile
+++ b/lib/parsetools/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2009. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -78,10 +78,10 @@ docs:
# ----------------------------------------------------
$(APP_TARGET): $(APP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
# ----------------------------------------------------
# Release Target
diff --git a/lib/parsetools/src/esyntax.yrl b/lib/parsetools/src/esyntax.yrl
deleted file mode 100644
index 1ecb54f0a7..0000000000
--- a/lib/parsetools/src/esyntax.yrl
+++ /dev/null
@@ -1,360 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1996-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%
-%%
-Nonterminals
-add_op attribute basic_type bif_test
-case_expr clause_body
-clause_guard clause_head comp_op cr_clause cr_clauses expr expr_tail
-exprs farity farity_list form formal_parameter_list function
-function_call function_clause guard guard_call guard_expr
-guard_expr_list guard_exprs guard_expr_tail guard_expr_tuple
-guard_parameter_list
-guard_tests guard_test if_clause if_clauses if_expr list match_expr
-mult_op parameter_list pattern patterns pattern_list pattern_tail pattern_tuple
-prefix_op receive_expr send_expr tuple.
-
-Terminals
-'!' '(' ')' '*' '+' ',' '-' '->' '/' '/=' ':' ';' '<' '=' '=/=' '=:='
-'=<' '==' '>' '>=' '[' ']' 'after' 'band' 'begin' 'bnot'
-'bor' 'bsl' 'bsr' 'bxor' 'case' 'catch' 'div' 'end' 'if' 'of'
-'receive' 'rem' 'when' '{' '|' '}' atom float integer string var.
-% 'receive' 'rem' 'true' 'when' '{' '|' '}' atom float integer string var.
-
-Rootsymbol form.
-
-Endsymbol dot.
-
-Unary 0 'catch'.
-Right 200 '='.
-Right 200 '!'.
-Left 300 add_op.
-Left 400 mult_op.
-Unary 500 prefix_op.
-
-
-add_op -> '+' : '$1'.
-add_op -> '-' : '$1'.
-add_op -> 'bor' : '$1'.
-add_op -> 'bxor' : '$1'.
-add_op -> 'bsl' : '$1'.
-add_op -> 'bsr' : '$1'.
-
-comp_op -> '==' : '$1'.
-comp_op -> '/=' : '$1'.
-comp_op -> '=<' : '$1'.
-comp_op -> '<' : '$1'.
-comp_op -> '>=' : '$1'.
-comp_op -> '>' : '$1'.
-comp_op -> '=:=' : '$1'.
-comp_op -> '=/=' : '$1'.
-
-mult_op -> '*' : '$1'.
-mult_op -> '/' : '$1'.
-mult_op -> 'div' : '$1'.
-mult_op -> 'rem' : '$1'.
-mult_op -> 'band' : '$1'.
-
-prefix_op -> '+' : '$1'.
-prefix_op -> '-' : '$1'.
-prefix_op -> 'bnot' : '$1'.
-
-
-basic_type -> atom : '$1'.
-basic_type -> float : '$1'.
-basic_type -> integer : '$1'.
-basic_type -> string : '$1'.
-basic_type -> var : '$1'.
-% basic_type -> 'true' : {atom, element(2, '$1'), 'true'}.
-
-
-pattern -> basic_type : '$1'.
-pattern -> pattern_list : '$1'.
-pattern -> pattern_tuple : '$1'.
-
-pattern_list -> '[' ']' : {nil, element(2, '$1')}.
-pattern_list -> '[' pattern pattern_tail ']' :
- {cons, element(2, '$1'), '$2', '$3'}.
-
-pattern_tail -> '|' pattern : '$2'.
-pattern_tail -> ',' pattern pattern_tail :
- {cons, element(2, '$2'), '$2', '$3'}.
-pattern_tail -> '$empty' : {nil, 0}.
-
-pattern_tuple -> '{' '}' : {tuple, element(2, '$1'), []}.
-pattern_tuple -> '{' patterns '}' : {tuple, element(2, '$1'), '$2'}.
-
-patterns -> pattern : ['$1'].
-patterns -> pattern ',' patterns : ['$1' | '$3'].
-
-
-expr -> basic_type : '$1'.
-expr -> list : '$1'.
-expr -> tuple : '$1'.
-expr -> function_call : '$1'.
-
-expr -> expr add_op expr :
- {Op, Pos} = '$2',
- {arith, Pos, Op, '$1', '$3'}.
-expr -> expr mult_op expr :
- {Op, Pos} = '$2',
- {arith, Pos, Op, '$1', '$3'}.
-expr -> prefix_op expr:
- case '$2' of
- {float, Pos, N} ->
- case '$1' of
- {'-', _} ->
- {float, Pos, -N};
- {'+', _} ->
- {float, Pos, N};
- {Op, Pos1} ->
- {arith, Pos1, Op, {float, Pos, N}}
- end;
- {integer, Pos, N} ->
- case '$1' of
- {'-', _} ->
- {integer, Pos, -N};
- {'+', _} ->
- {integer, Pos, N};
- {Op, Pos1} ->
- {arith, Pos1, Op, {integer, Pos, N}}
- end;
- _ ->
- {Op, Pos} = '$1',
- {arith, Pos, Op, '$2'}
- end.
-
-expr -> '(' expr ')' : '$2'.
-expr -> 'begin' exprs 'end' : {block, element(2, '$1'), '$2'}.
-expr -> 'catch' expr : {'catch', element(2, '$1'), '$2'}.
-
-expr -> case_expr : '$1'.
-expr -> if_expr : '$1'.
-expr -> receive_expr : '$1'.
-expr -> match_expr : '$1'.
-expr -> send_expr : '$1'.
-
-
-list -> '[' ']' : {nil, element(2, '$1')}.
-list -> '[' expr expr_tail ']' : {cons, element(2, '$1'), '$2', '$3'}.
-
-expr_tail -> '|' expr : '$2'.
-expr_tail -> ',' expr expr_tail : {cons, element(2, '$2'), '$2', '$3'}.
-expr_tail -> '$empty' : {nil, 0}.
-
-tuple -> '{' '}' : {tuple, element(2, '$1'), []}.
-tuple -> '{' exprs '}' : {tuple, element(2, '$1'), '$2'}.
-
-
-function_call -> atom '(' parameter_list ')' :
- case erl_parse:erlang_bif(element(3, '$1'), length('$3')) of
- true ->
- {bif, element(2, '$1'), element(3, '$1'), '$3'};
- false ->
- {call, element(2, '$1'), [], element(3, '$1'), '$3'}
- end.
-function_call -> atom ':' atom '(' parameter_list ')' :
- {call, element(2, '$1'), element(3, '$1'), element(3, '$3'), '$5'}.
-
-parameter_list -> exprs : '$1'.
-parameter_list -> '$empty' : [].
-
-
-case_expr -> 'case' expr 'of' cr_clauses 'end' :
- {'case', element(2, '$1'), '$2', '$4'}.
-
-cr_clause -> pattern clause_guard clause_body :
- {clause, element(2, '$1'), ['$1'], '$2', '$3'}.
-
-cr_clauses -> cr_clause : ['$1'].
-cr_clauses -> cr_clause ';' cr_clauses : ['$1' | '$3'].
-
-if_expr -> 'if' if_clauses 'end' : {'if', element(2, '$1'), '$2'}.
-
-if_clause -> guard clause_body : {clause, element(2, hd('$2')), '$1', '$2'}.
-
-if_clauses -> if_clause : ['$1'].
-if_clauses -> if_clause ';' if_clauses : ['$1' | '$3'].
-
-receive_expr -> 'receive' 'after' expr clause_body 'end' :
- {'receive', element(2, '$1'), [], '$3', '$4'}.
-receive_expr -> 'receive' cr_clauses 'end' :
- {'receive', element(2, '$1'), '$2'}.
-receive_expr -> 'receive' cr_clauses 'after' expr clause_body 'end' :
- {'receive', element(2, '$1'), '$2', '$4', '$5'}.
-
-
-match_expr -> expr '=' expr :
- case erl_parse:is_term('$1') of
- true ->
- {match, element(2, '$1'), '$1', '$3'};
- false ->
- throw({error, {element(2, '$1'), yecc, "illegal lhs in match **"}})
- end.
-
-send_expr -> expr '!' expr :
- Pos = element(2, '$1'),
- {send, Pos, '$1', '$3'}.
-
-
-exprs -> expr : ['$1'].
-exprs -> expr ',' exprs : ['$1' | '$3'].
-
-
-guard_expr -> basic_type : '$1'.
-guard_expr -> guard_expr_list : '$1'.
-guard_expr -> guard_expr_tuple : '$1'.
-guard_expr -> guard_call : '$1'.
-guard_expr -> '(' guard_expr ')' : '$2'.
-guard_expr -> guard_expr add_op guard_expr :
- {Op, Pos} = '$2',
- {arith, Pos, Op, '$1', '$3'}.
-guard_expr -> guard_expr mult_op guard_expr :
- {Op, Pos} = '$2',
- {arith, Pos, Op, '$1', '$3'}.
-guard_expr -> prefix_op guard_expr:
- case '$2' of
- {float, Pos, N} ->
- case '$1' of
- {'-', _} ->
- {float, Pos, -N};
- {'+', _} ->
- {float, Pos, N};
- {Op, Pos1} ->
- {arith, Pos1, Op, {float, Pos, N}}
- end;
- {integer, Pos, N} ->
- case '$1' of
- {'-', _} ->
- {integer, Pos, -N};
- {'+', _} ->
- {integer, Pos, N};
- {Op, Pos1} ->
- {arith, Pos1, Op, {integer, Pos, N}}
- end;
- _ ->
- {Op, Pos} = '$1',
- {arith, Pos, Op, '$2'}
- end.
-
-guard_expr_list -> '[' ']' : {nil, element(2, '$1')}.
-guard_expr_list -> '[' guard_expr guard_expr_tail ']' :
- {cons, element(2, '$1'), '$2', '$3'}.
-
-guard_expr_tail -> '|' guard_expr : '$2'.
-guard_expr_tail -> ',' guard_expr guard_expr_tail :
- {cons, element(2, '$2'), '$2', '$3'}.
-guard_expr_tail -> '$empty' : {nil, 0}.
-
-guard_expr_tuple -> '{' '}' : {tuple, element(2, '$1'), []}.
-guard_expr_tuple -> '{' guard_exprs '}' : {tuple, element(2, '$1'), '$2'}.
-
-guard_exprs -> guard_expr : ['$1'].
-guard_exprs -> guard_expr ',' guard_exprs : ['$1' | '$3'].
-
-
-guard_call -> atom '(' guard_parameter_list ')' :
- case erl_parse:erlang_guard_bif(element(3, '$1'), length('$3')) of
- true ->
- {bif, element(2, '$1'), element(3, '$1'), '$3'};
- false ->
- throw({error, {element(2, '$1'), yecc, "illegal test in guard **"}})
- end.
-
-guard_parameter_list -> guard_exprs : '$1'.
-guard_parameter_list -> '$empty' : [].
-
-
-bif_test -> atom '(' guard_parameter_list ')' :
- case erl_parse:erlang_guard_test(element(3, '$1'), length('$3')) of
- true ->
- {test, element(2, '$1'), element(3, '$1'), '$3'};
- false ->
- throw({error, {element(2, '$1'), yecc, "illegal test in guard **"}})
- end.
-
-
-guard_test -> bif_test : '$1'.
-guard_test -> guard_expr comp_op guard_expr :
- {Op, Pos} = '$2',
- {comp, Pos, Op, '$1', '$3'}.
-
-guard_tests -> guard_test : ['$1'].
-guard_tests -> guard_test ',' guard_tests : ['$1' | '$3'].
-
-% guard -> 'true' : [].
-guard -> atom :
- case '$1' of
- {atom, _, true} ->
- [];
- _ ->
- throw({error, {element(2, '$1'), yecc, "illegal test in guard **"}})
- end.
-guard -> guard_tests : '$1'.
-
-
-function_clause -> clause_head clause_guard clause_body :
- {Name, Line, Arity, Parameters} = '$1',
- {function, Line, Name, Arity,
- [{clause, element(2, hd('$3')), Parameters, '$2', '$3'}]}.
-
-clause_head -> atom '(' formal_parameter_list ')' :
- {element(3, '$1'), element(2, '$1'), length('$3'), '$3'}.
-
-formal_parameter_list -> patterns : '$1'.
-formal_parameter_list -> '$empty' : [].
-
-clause_guard -> 'when' guard : '$2'.
-clause_guard -> '$empty' : [].
-
-clause_body -> '->' exprs: '$2'.
-
-
-function -> function_clause : '$1'.
-function -> function_clause ';' function :
- case '$1' of
- {function, Pos1, Name1, Arity1, [Clause]} ->
- case '$3' of
- {function, _, Name1, Arity2, Clauses} ->
- if
- Arity1 /= Arity2 ->
- throw({error, {Pos1, yecc,
- io_lib:format('arity conflict in definition of ~w',
- [Name1])}});
- true ->
- {function, Pos1, Name1, Arity1, [Clause | Clauses]}
- end;
- _ ->
- throw({error, {Pos1, yecc,
- io_lib:format('missing final dot in def of ~w/~w',
- [Name1, Arity1])}})
- end
- end.
-
-
-attribute -> atom : element(3, '$1').
-attribute -> '[' farity_list ']' : '$2'.
-
-farity_list -> farity : ['$1'].
-farity_list -> farity ',' farity_list : ['$1' | '$3'].
-
-farity -> atom '/' integer : {element(3, '$1'), element(3, '$3')}.
-
-
-form -> '-' atom '(' attribute ')' :
- {attribute, element(2, '$2'), element(3, '$2'), '$4'}.
-form -> function : '$1'.
diff --git a/lib/parsetools/src/leex.erl b/lib/parsetools/src/leex.erl
index cdf20461d9..bbef4053b4 100644
--- a/lib/parsetools/src/leex.erl
+++ b/lib/parsetools/src/leex.erl
@@ -58,6 +58,7 @@
gfile=[], % Graph file
module, % Module name
opts=[], % Options
+ encoding=none, % Encoding of Xrl file
% posix=false, % POSIX regular expressions
errors=[],
warnings=[]
@@ -146,7 +147,9 @@ format_error({regexp,E})->
end,
["bad regexp `",Es,"'"];
format_error(ignored_characters) ->
- "ignored characters".
+ "ignored characters";
+format_error(cannot_parse) ->
+ io_lib:fwrite("cannot parse; probably encoding mismatch", []).
%%%
%%% Local functions
@@ -298,10 +301,10 @@ pack_warnings([]) ->
report_errors(St) ->
when_opt(fun () ->
foreach(fun({File,{none,Mod,E}}) ->
- io:fwrite("~s: ~s\n",
+ io:fwrite("~s: ~ts\n",
[File,Mod:format_error(E)]);
({File,{Line,Mod,E}}) ->
- io:fwrite("~s:~w: ~s\n",
+ io:fwrite("~s:~w: ~ts\n",
[File,Line,Mod:format_error(E)])
end, sort(St#leex.errors))
end, report_errors, St#leex.opts).
@@ -316,11 +319,11 @@ report_warnings(St) ->
ShouldReport = member(report_warnings, St#leex.opts) orelse ReportWerror,
when_bool(fun () ->
foreach(fun({File,{none,Mod,W}}) ->
- io:fwrite("~s: ~s~s\n",
+ io:fwrite("~s: ~s~ts\n",
[File,Prefix,
Mod:format_error(W)]);
({File,{Line,Mod,W}}) ->
- io:fwrite("~s:~w: ~s~s\n",
+ io:fwrite("~s:~w: ~s~ts\n",
[File,Line,Prefix,
Mod:format_error(W)])
end, sort(St#leex.warnings))
@@ -396,17 +399,18 @@ verbose_print(St, Format, Args) ->
parse_file(St0) ->
case file:open(St0#leex.xfile, [read]) of
{ok,Xfile} ->
+ St1 = St0#leex{encoding = epp:set_encoding(Xfile)},
try
- verbose_print(St0, "Parsing file ~s, ", [St0#leex.xfile]),
+ verbose_print(St1, "Parsing file ~s, ", [St1#leex.xfile]),
%% We KNOW that errors throw so we can ignore them here.
- {ok,Line1,St1} = parse_head(Xfile, St0),
- {ok,Line2,Macs,St2} = parse_defs(Xfile, Line1, St1),
- {ok,Line3,REAs,Actions,St3} =
- parse_rules(Xfile, Line2, Macs, St2),
- {ok,Code,St4} = parse_code(Xfile, Line3, St3),
- verbose_print(St1, "contained ~w rules.~n", [length(REAs)]),
- {ok,REAs,Actions,Code,St4}
- after file:close(Xfile)
+ {ok,Line1,St2} = parse_head(Xfile, St1),
+ {ok,Line2,Macs,St3} = parse_defs(Xfile, Line1, St2),
+ {ok,Line3,REAs,Actions,St4} =
+ parse_rules(Xfile, Line2, Macs, St3),
+ {ok,Code,St5} = parse_code(Xfile, Line3, St4),
+ verbose_print(St5, "contained ~w rules.~n", [length(REAs)]),
+ {ok,REAs,Actions,Code,St5}
+ after ok = file:close(Xfile)
end;
{error,Error} ->
add_error({none,leex,{file_error,Error}}, St0)
@@ -415,7 +419,7 @@ parse_file(St0) ->
%% parse_head(File, State) -> {ok,NextLine,State}.
%% Parse the head of the file. Skip all comments and blank lines.
-parse_head(Ifile, St) -> {ok,nextline(Ifile, 0),St}.
+parse_head(Ifile, St) -> {ok,nextline(Ifile, 0, St),St}.
%% parse_defs(File, Line, State) -> {ok,NextLine,Macros,State}.
%% Parse the macro definition section of a file. This must exist.
@@ -423,7 +427,7 @@ parse_head(Ifile, St) -> {ok,nextline(Ifile, 0),St}.
parse_defs(Ifile, {ok,?DEFS_HEAD ++ Rest,L}, St) ->
St1 = warn_ignored_chars(L, Rest, St),
- parse_defs(Ifile, nextline(Ifile, L), [], St1);
+ parse_defs(Ifile, nextline(Ifile, L, St), [], St1);
parse_defs(_, {ok,_,L}, St) ->
add_error({L,leex,missing_defs}, St);
parse_defs(_, {eof,L}, St) ->
@@ -435,7 +439,7 @@ parse_defs(Ifile, {ok,Chars,L}=Line, Ms, St) ->
case re:run(Chars, MS, [{capture,all_but_first,list}]) of
{match,[Name,Def]} ->
%%io:fwrite("~p = ~p\n", [Name,Def]),
- parse_defs(Ifile, nextline(Ifile, L), [{Name,Def}|Ms], St);
+ parse_defs(Ifile, nextline(Ifile, L, St), [{Name,Def}|Ms], St);
_ -> {ok,Line,Ms,St} % Anything else
end;
parse_defs(_, Line, Ms, St) ->
@@ -446,7 +450,7 @@ parse_defs(_, Line, Ms, St) ->
parse_rules(Ifile, {ok,?RULE_HEAD ++ Rest,L}, Ms, St) ->
St1 = warn_ignored_chars(L, Rest, St),
- parse_rules(Ifile, nextline(Ifile, L), Ms, [], [], 0, St1);
+ parse_rules(Ifile, nextline(Ifile, L, St), Ms, [], [], 0, St1);
parse_rules(_, {ok,_,L}, _, St) ->
add_error({L,leex,missing_rules}, St);
parse_rules(_, {eof,L}, _, St) ->
@@ -464,7 +468,7 @@ parse_rules(Ifile, NextLine, Ms, REAs, As, N, St) ->
case collect_rule(Ifile, Chars, L0) of
{ok,Re,Atoks,L1} ->
{ok,REA,A,St1} = parse_rule(Re, L0, Atoks, Ms, N, St),
- parse_rules(Ifile, nextline(Ifile, L1), Ms,
+ parse_rules(Ifile, nextline(Ifile, L1, St), Ms,
[REA|REAs], [A|As], N+1, St1);
{error,E} -> add_error(E, St)
end;
@@ -497,8 +501,10 @@ collect_rule(Ifile, Chars, L0) ->
{error,E,_} -> {error,E}
end.
+collect_action(_Ifile, {error, _}, L, _Cont0) ->
+ {error, {L, leex, cannot_parse}, ignored_end_line};
collect_action(Ifile, Chars, L0, Cont0) ->
- case erl_scan:tokens(Cont0, Chars, L0) of
+ case erl_scan:tokens(Cont0, Chars, L0, [unicode]) of
{done,{ok,Toks,_},_} -> {ok,Toks,L0};
{done,{eof,_},_} -> {eof,L0};
{done,{error,E,_},_} -> {error,E,L0};
@@ -560,29 +566,32 @@ parse_code(Ifile, {ok,?CODE_HEAD ++ Rest,CodeL}, St) ->
St1 = warn_ignored_chars(CodeL, Rest, St),
{ok, CodePos} = file:position(Ifile, cur),
%% Just count the lines; copy the code from file to file later.
- NCodeLines = count_lines(Ifile, 0),
+ EndCodeLine = count_lines(Ifile, CodeL, St),
+ NCodeLines = EndCodeLine - CodeL,
{ok,{CodeL,CodePos,NCodeLines},St1};
parse_code(_, {ok,_,L}, St) ->
add_error({L,leex,missing_code}, St);
parse_code(_, {eof,L}, St) ->
add_error({L,leex,missing_code}, St).
-count_lines(File, N) ->
+count_lines(File, N, St) ->
case io:get_line(File, leex) of
eof -> N;
- _Line -> count_lines(File, N+1)
+ {error, _} -> add_error({N+1, leex, cannot_parse}, St);
+ _Line -> count_lines(File, N+1, St)
end.
-%% nextline(InputFile, PrevLineNo) -> {ok,Chars,LineNo} | {eof,LineNo}.
+%% nextline(InputFile, PrevLineNo, State) -> {ok,Chars,LineNo} | {eof,LineNo}.
%% Get the next line skipping comment lines and blank lines.
-nextline(Ifile, L) ->
+nextline(Ifile, L, St) ->
case io:get_line(Ifile, leex) of
eof -> {eof,L};
+ {error, _} -> add_error({L+1, leex, cannot_parse}, St);
Chars ->
case substr(Chars, span(Chars, " \t\n")+1) of
- [$%|_Rest] -> nextline(Ifile, L+1);
- [] -> nextline(Ifile, L+1);
+ [$%|_Rest] -> nextline(Ifile, L+1, St);
+ [] -> nextline(Ifile, L+1, St);
_Other -> {ok,Chars,L+1}
end
end.
@@ -1289,19 +1298,21 @@ out_file(St0, DFA, DF, Actions, Code) ->
try
case file:open(St0#leex.efile, [write]) of
{ok,Ofile} ->
+ set_encoding(St0, Ofile),
try
+ output_encoding_comment(Ofile, St0),
output_file_directive(Ofile, St0#leex.ifile, 0),
out_file(Ifile, Ofile, St0, DFA, DF, Actions,
Code, 1),
verbose_print(St0, "ok~n", []),
St0
- after file:close(Ofile)
+ after ok = file:close(Ofile)
end;
{error,Error} ->
verbose_print(St0, "error~n", []),
add_error({none,leex,{file_error,Error}}, St0)
end
- after file:close(Ifile)
+ after ok = file:close(Ifile)
end;
{{error,Error},Ifile} ->
add_error(Ifile, {none,leex,{file_error,Error}}, St0)
@@ -1310,7 +1321,9 @@ out_file(St0, DFA, DF, Actions, Code) ->
open_inc_file(State) ->
Ifile = State#leex.ifile,
case file:open(Ifile, [read]) of
- {ok,F} -> {ok,F};
+ {ok,F} ->
+ _ = epp:set_encoding(F),
+ {ok,F};
Error -> {Error,Ifile}
end.
@@ -1328,6 +1341,7 @@ inc_file_name(Filename) ->
out_file(Ifile, Ofile, St, DFA, DF, Actions, Code, L) ->
case io:get_line(Ifile, leex) of
eof -> output_file_directive(Ofile, St#leex.ifile, L);
+ {error, _} -> add_error(St#leex.ifile, {L, leex, cannot_parse}, St);
Line ->
case substr(Line, 1, 5) of
"##mod" -> out_module(Ofile, St);
@@ -1347,14 +1361,23 @@ out_erlang_code(File, St, Code, L) ->
output_file_directive(File, St#leex.xfile, CodeL),
{ok,Xfile} = file:open(St#leex.xfile, [read]),
try
+ set_encoding(St, Xfile),
{ok,_} = file:position(Xfile, CodePos),
- {ok,_} = file:copy(Xfile, File)
+ ok = file_copy(Xfile, File)
after
- file:close(Xfile)
+ ok = file:close(Xfile)
end,
io:nl(File),
output_file_directive(File, St#leex.ifile, L).
+file_copy(From, To) ->
+ case io:get_line(From, leex) of
+ eof -> ok;
+ Line when is_list(Line) ->
+ io:fwrite(To, "~ts", [Line]),
+ file_copy(From, To)
+ end.
+
out_dfa(File, St, DFA, Code, DF, L) ->
{_CodeL,_CodePos,NCodeLines} = Code,
%% Three file attributes before this one...
@@ -1569,7 +1592,7 @@ out_dfa_graph(St, DFA, DF) ->
io:fwrite(Gfile, "}~n", []),
verbose_print(St, "ok~n", []),
St
- after file:close(Gfile)
+ after ok = file:close(Gfile)
end;
{error,Error} ->
verbose_print(St, "error~n", []),
@@ -1610,6 +1633,16 @@ dfa_edgelabel(Cranges) ->
(C) -> [quote(C)]
end, Cranges) ++ "]".
+set_encoding(#leex{encoding = none}, File) ->
+ ok = io:setopts(File, [{encoding, epp:default_encoding()}]);
+set_encoding(#leex{encoding = E}, File) ->
+ ok = io:setopts(File, [{encoding, E}]).
+
+output_encoding_comment(_File, #leex{encoding = none}) ->
+ ok;
+output_encoding_comment(File, #leex{encoding = Encoding}) ->
+ io:fwrite(File, <<"%% ~s\n">>, [epp:encoding_to_string(Encoding)]).
+
output_file_directive(File, Filename, Line) ->
io:fwrite(File, <<"-file(~s, ~w).\n">>,
[format_filename(Filename), Line]).
diff --git a/lib/parsetools/src/yecc.erl b/lib/parsetools/src/yecc.erl
index b0792a6ed8..dbb7d025ae 100644
--- a/lib/parsetools/src/yecc.erl
+++ b/lib/parsetools/src/yecc.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -42,6 +42,7 @@
includefile,
includefile_version,
module,
+ encoding = none,
options = [],
verbose = false,
file_attrs = true,
@@ -224,7 +225,11 @@ format_error({unused_nonterminal, Nonterminal}) ->
[format_symbol(Nonterminal)]);
format_error({unused_terminal, Terminal}) ->
io_lib:fwrite("terminal symbol ~s not used",
- [format_symbol(Terminal)]).
+ [format_symbol(Terminal)]);
+format_error({bad_symbol, String}) ->
+ io_lib:fwrite("bad symbol ~ts", [String]);
+format_error(cannot_parse) ->
+ io_lib:fwrite("cannot parse; possibly encoding mismatch", []).
file(File) ->
file(File, [report_errors, report_warnings]).
@@ -257,7 +262,7 @@ yecc(Infile, Outfile, Verbose) ->
yecc(Infile, Outfile, Verbose, []).
yecc(Infilex, Outfilex, Verbose, Includefilex) ->
- statistics(runtime),
+ _ = statistics(runtime),
case file(Infilex, [{parserfile, Outfilex},
{verbose, Verbose},
{report, true},
@@ -407,7 +412,9 @@ infile(Parent, Infilex, Options) ->
St = case file:open(St0#yecc.infile, [read, read_ahead]) of
{ok, Inport} ->
try
- outfile(St0#yecc{inport = Inport})
+ Encoding = epp:set_encoding(Inport),
+ St1 = St0#yecc{inport = Inport, encoding = Encoding},
+ outfile(St1)
after
ok = file:close(Inport)
end;
@@ -428,6 +435,8 @@ outfile(St0) ->
case file:open(St0#yecc.outfile, [write, delayed_write]) of
{ok, Outport} ->
try
+ %% Set the same encoding as infile:
+ set_encoding(St0, Outport),
generate(St0#yecc{outport = Outport, line = 1})
catch
throw: St1 ->
@@ -466,13 +475,14 @@ timeit(Name, Fun, St0) ->
-define(PASS(P), {P, fun P/1}).
generate(St0) ->
+ St1 = output_encoding_comment(St0),
Passes = [?PASS(parse_grammar), ?PASS(check_grammar),
?PASS(states_and_goto_table), ?PASS(parse_actions),
?PASS(action_conflicts), ?PASS(write_file)],
- F = case member(time, St0#yecc.options) of
+ F = case member(time, St1#yecc.options) of
true ->
io:fwrite(<<"Generating parser from grammar in ~s\n">>,
- [format_filename(St0#yecc.infile)]),
+ [format_filename(St1#yecc.infile)]),
fun timeit/3;
false ->
fun(_Name, Fn, St) -> Fn(St) end
@@ -484,13 +494,13 @@ generate(St0) ->
true -> throw(St2)
end
end,
- foldl(Fun, St0, Passes).
+ foldl(Fun, St1, Passes).
parse_grammar(St) ->
parse_grammar(St#yecc.inport, 1, St).
parse_grammar(Inport, Line, St) ->
- {NextLine, Grammar} = read_grammar(Inport, Line),
+ {NextLine, Grammar} = read_grammar(Inport, St, Line),
parse_grammar(Grammar, Inport, NextLine, St).
parse_grammar(eof, _Inport, _NextLine, St) ->
@@ -523,6 +533,8 @@ parse_grammar({rule, Rule, Tokens}, St0) ->
St#yecc{rules_list = [RuleDef | St#yecc.rules_list]};
parse_grammar({prec, Prec}, St) ->
St#yecc{prec = Prec ++ St#yecc.prec};
+parse_grammar({#symbol{}, [{string,Line,String}]}, St) ->
+ add_error(Line, {bad_symbol, String}, St);
parse_grammar({#symbol{line = Line, name = Name}, Symbols}, St) ->
CF = fun(I) ->
case element(I, St) of
@@ -543,12 +555,17 @@ parse_grammar({#symbol{line = Line, name = Name}, Symbols}, St) ->
_ -> add_warning(Line, bad_declaration, St)
end.
-read_grammar(Inport, Line) ->
+read_grammar(Inport, St, Line) ->
case yeccscan:scan(Inport, '', Line) of
{eof, NextLine} ->
{NextLine, eof};
{error, {ErrorLine, Mod, What}, NextLine} ->
{NextLine, {error, ErrorLine, {error, Mod, What}}};
+ {error, terminated} ->
+ throw(St);
+ {error, _} ->
+ File = St#yecc.infile,
+ throw(add_error(File, none, cannot_parse, St));
{ok, Input, NextLine} ->
{NextLine, case yeccparser:parse(Input) of
{error, {ErrorLine, Mod, Message}} ->
@@ -738,9 +755,9 @@ states_and_goto_table(St0) ->
create_precedence_table(St).
parse_actions(St) ->
- erase(), % the pd is used when decoding lookahead sets
+ _ = erase(), % the pd is used when decoding lookahead sets
ParseActions = compute_parse_actions(St#yecc.n_states, St, []),
- erase(),
+ _ = erase(),
St#yecc{parse_actions = ParseActions, state_tab = []}.
action_conflicts(St0) ->
@@ -841,10 +858,10 @@ report_errors(St) ->
case member(report_errors, St#yecc.options) of
true ->
foreach(fun({File,{none,Mod,E}}) ->
- io:fwrite(<<"~s: ~s\n">>,
+ io:fwrite(<<"~s: ~ts\n">>,
[File,Mod:format_error(E)]);
({File,{Line,Mod,E}}) ->
- io:fwrite(<<"~s:~w: ~s\n">>,
+ io:fwrite(<<"~s:~w: ~ts\n">>,
[File,Line,Mod:format_error(E)])
end, sort(St#yecc.errors));
false ->
@@ -861,11 +878,11 @@ report_warnings(St) ->
case member(report_warnings, St#yecc.options) orelse ReportWerror of
true ->
foreach(fun({File,{none,Mod,W}}) ->
- io:fwrite(<<"~s: ~s~s\n">>,
+ io:fwrite(<<"~s: ~s~ts\n">>,
[File,Prefix,
Mod:format_error(W)]);
({File,{Line,Mod,W}}) ->
- io:fwrite(<<"~s:~w: ~s~s\n">>,
+ io:fwrite(<<"~s:~w: ~s~ts\n">>,
[File,Line,Prefix,
Mod:format_error(W)])
end, sort(St#yecc.warnings));
@@ -1024,7 +1041,7 @@ compute_states(St0) ->
rp_info = RulePointerInfo,
goto = GotoTab},
- erase(),
+ _ = erase(),
EndsymCode = code_terminal(StC#yecc.endsymbol, StC#yecc.symbol_tab),
{StateId, State0} = compute_state([{EndsymCode, 1}], Tables),
@@ -1923,9 +1940,10 @@ output_prelude(Outport, Inport, St0) when St0#yecc.includefile =:= [] ->
{St20, 0, no_erlang_code};
Next_line ->
St_10 = output_file_directive(St20, Infile, Next_line-1),
- Nmbr_of_lines = include1([], Inport, Outport),
- {St_10, Nmbr_of_lines,
- {last_erlang_code_line, Next_line+Nmbr_of_lines}}
+ Last_line = include1([], Inport, Outport, Infile,
+ Next_line, St_10),
+ Nmbr_of_lines = Last_line - Next_line,
+ {St_10, Nmbr_of_lines, {last_erlang_code_line, Last_line}}
end,
St30 = nl(St25),
IncludeFile =
@@ -1946,13 +1964,13 @@ output_prelude(Outport, Inport, St0) ->
{St30, N_lines_1, no_erlang_code};
Next_line ->
St = output_file_directive(St30, Infile, Next_line-1),
- Nmbr_of_lines = include1([], Inport, Outport),
- {St, Nmbr_of_lines + N_lines_1,
- {last_erlang_code_line, Next_line+Nmbr_of_lines}}
+ Last_line = include1([], Inport, Outport, Infile, Next_line, St),
+ Nmbr_of_lines = Last_line - Next_line,
+ {St, Nmbr_of_lines + N_lines_1, {last_erlang_code_line, Last_line}}
end.
output_header(St0) ->
- lists:foldl(fun(Str, St) -> fwrite(St, <<"~s\n">>, [Str])
+ lists:foldl(fun(Str, St) -> fwrite(St, <<"~ts\n">>, [Str])
end, St0, St0#yecc.header).
output_goto(St, [{_Nonterminal, []} | Go], StateInfo) ->
@@ -2250,8 +2268,8 @@ output_inlined(St0, FunctionName, Reduce, Infile) ->
[append(["[", tl(A), " | __Stack]"])])
end,
St = St40#yecc{line = St40#yecc.line + NLines},
- fwrite(St, <<" [begin\n ~s\n end | ~s].\n\n">>,
- [pp_tokens(Tokens, Line0), Stack]).
+ fwrite(St, <<" [begin\n ~ts\n end | ~s].\n\n">>,
+ [pp_tokens(Tokens, Line0, St#yecc.encoding), Stack]).
inlined_function_name(State, "Cat") ->
inlined_function_name(State, "");
@@ -2421,24 +2439,24 @@ include(St, File, Outport) ->
{error, Reason} ->
throw(add_error(File, none, {file_error, Reason}, St));
{ok, Inport} ->
+ _ = epp:set_encoding(Inport),
Line = io:get_line(Inport, ''),
- N_lines = include1(Line, Inport, Outport),
- file:close(Inport),
- N_lines
+ try include1(Line, Inport, Outport, File, 1, St) - 1
+ after ok = file:close(Inport)
+ end
end.
-include1(Line, Inport, Outport) ->
- include1(Line, Inport, Outport, 0).
-
-include1(eof, _, _, Nmbr_of_lines) ->
- Nmbr_of_lines;
-include1(Line, Inport, Outport, Nmbr_of_lines) ->
+include1(eof, _, _, _File, L, _St) ->
+ L;
+include1({error, _}=_Error, _Inport, _Outport, File, L, St) ->
+ throw(add_error(File, L, cannot_parse, St));
+include1(Line, Inport, Outport, File, L, St) ->
Incr = case member($\n, Line) of
true -> 1;
false -> 0
end,
io:put_chars(Outport, Line),
- include1(io:get_line(Inport, ''), Inport, Outport, Nmbr_of_lines + Incr).
+ include1(io:get_line(Inport, ''), Inport, Outport, File, L + Incr, St).
includefile_version([]) ->
{1,4};
@@ -2465,18 +2483,22 @@ parse_file(Epp) ->
end.
%% Keeps the line breaks of the original code.
-pp_tokens(Tokens, Line0) ->
- concat(pp_tokens1(Tokens, Line0, [])).
+pp_tokens(Tokens, Line0, Enc) ->
+ concat(pp_tokens1(Tokens, Line0, Enc, [])).
-pp_tokens1([], _Line0, _T0) ->
+pp_tokens1([], _Line0, _Enc, _T0) ->
[];
-pp_tokens1([T | Ts], Line0, T0) ->
+pp_tokens1([T | Ts], Line0, Enc, T0) ->
Line = element(2, T),
- [pp_sep(Line, Line0, T0), pp_symbol(T) | pp_tokens1(Ts, Line, T)].
+ [pp_sep(Line, Line0, T0), pp_symbol(T, Enc)|pp_tokens1(Ts, Line, Enc, T)].
-pp_symbol({var,_,Var}) -> Var;
-pp_symbol({_,_,Symbol}) -> io_lib:fwrite(<<"~p">>, [Symbol]);
-pp_symbol({Symbol, _}) -> Symbol.
+pp_symbol({var,_,Var}, _Enc) -> Var;
+pp_symbol({string,_,String}, latin1) ->
+ io_lib:write_unicode_string_as_latin1(String);
+pp_symbol({string,_,String}, _Enc) -> io_lib:write_unicode_string(String);
+pp_symbol({_,_,Symbol}, latin1) -> io_lib:fwrite(<<"~p">>, [Symbol]);
+pp_symbol({_,_,Symbol}, _Enc) -> io_lib:fwrite(<<"~tp">>, [Symbol]);
+pp_symbol({Symbol, _}, _Enc) -> Symbol.
pp_sep(Line, Line0, T0) when Line > Line0 ->
["\n " | pp_sep(Line - 1, Line0, T0)];
@@ -2485,6 +2507,16 @@ pp_sep(_Line, _Line0, {'.',_}) ->
pp_sep(_Line, _Line0, _T0) ->
" ".
+set_encoding(#yecc{encoding = none}, Port) ->
+ ok = io:setopts(Port, [{encoding, epp:default_encoding()}]);
+set_encoding(#yecc{encoding = E}, Port) ->
+ ok = io:setopts(Port, [{encoding, E}]).
+
+output_encoding_comment(#yecc{encoding = none}=St) ->
+ St;
+output_encoding_comment(#yecc{encoding = Encoding}=St) ->
+ fwrite(St, <<"%% ~s\n">>, [epp:encoding_to_string(Encoding)]).
+
output_file_directive(St, Filename, Line) when St#yecc.file_attrs ->
fwrite(St, <<"-file(~s, ~w).\n">>,
[format_filename(Filename), Line]);
@@ -2529,7 +2561,7 @@ format_assoc(nonassoc) ->
format_symbol(Symbol) ->
String = concat([Symbol]),
- case erl_scan:string(String) of
+ case erl_scan:string(String, 1, [unicode]) of
{ok, [{atom, _, _}], _} ->
io_lib:fwrite(<<"~w">>, [Symbol]);
{ok, [{Word, _}], _} when Word =/= ':', Word =/= '->' ->
diff --git a/lib/parsetools/src/yeccscan.erl b/lib/parsetools/src/yeccscan.erl
index d7ec3ba8d3..9e0e85143a 100644
--- a/lib/parsetools/src/yeccscan.erl
+++ b/lib/parsetools/src/yeccscan.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -24,7 +24,7 @@ scan(Inport) ->
scan(Inport, '', 1).
scan(Inport, Prompt, Line1) ->
- case catch io:scan_erl_form(Inport, Prompt, Line1) of
+ case catch io:scan_erl_form(Inport, Prompt, Line1, [unicode]) of
{eof, Line2} ->
{eof, Line2};
{ok, Tokens, Line2} ->
@@ -34,6 +34,8 @@ scan(Inport, Prompt, Line1) ->
_ ->
{ok, lex(Tokens), Line2}
end;
+ {error, Reason} ->
+ {error, Reason};
{error, Descriptor, Line2} ->
{error, Descriptor, Line2};
{'EXIT', Why} ->
diff --git a/lib/parsetools/test/Makefile b/lib/parsetools/test/Makefile
index 430386bfe5..6455f6ade7 100644
--- a/lib/parsetools/test/Makefile
+++ b/lib/parsetools/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2005-2011. All Rights Reserved.
+# Copyright Ericsson AB 2005-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/parsetools/test/leex_SUITE.erl b/lib/parsetools/test/leex_SUITE.erl
index 1e50aedf07..9d591c0d05 100644
--- a/lib/parsetools/test/leex_SUITE.erl
+++ b/lib/parsetools/test/leex_SUITE.erl
@@ -1,7 +1,8 @@
+%% -*- coding: latin-1 -*-
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -42,7 +43,9 @@
-export([
file/1, compile/1, syntax/1,
- pt/1, man/1, ex/1, ex2/1, not_yet/1]).
+ pt/1, man/1, ex/1, ex2/1, not_yet/1,
+
+ otp_10302/1]).
% Default timetrap timeout (set in init_per_testcase).
-define(default_timeout, ?t:minutes(1)).
@@ -63,7 +66,8 @@ all() ->
groups() ->
[{checks, [], [file, compile, syntax]},
- {examples, [], [pt, man, ex, ex2, not_yet]}].
+ {examples, [], [pt, man, ex, ex2, not_yet]},
+ {tickets, [], [otp_10302]}].
init_per_suite(Config) ->
Config.
@@ -645,7 +649,6 @@ reserved_word('fun') -> true;
reserved_word('if') -> true;
reserved_word('let') -> true;
reserved_word('of') -> true;
-reserved_word('query') -> true;
reserved_word('receive') -> true;
reserved_word('when') -> true;
reserved_word('bnot') -> true;
@@ -875,6 +878,111 @@ not_yet(Config) when is_list(Config) ->
ok.
+otp_10302(doc) ->
+ "OTP-10302. Unicode characters scanner/parser.";
+otp_10302(suite) -> [];
+otp_10302(Config) when is_list(Config) ->
+ Dir = ?privdir,
+ Filename = filename:join(Dir, "file.xrl"),
+ Ret = [return, {report, true}],
+
+ ok = file:write_file(Filename,<<
+ "%% coding: UTF-8\n"
+ "�"
+ >>),
+ {error,[{_,[{2,leex,cannot_parse}]}],[]} =
+ leex:file(Filename, Ret),
+
+ ok = file:write_file(Filename,<<
+ "%% coding: UTF-8\n"
+ "Definitions.\n"
+ "�"
+ >>),
+ {error,[{_,[{3,leex,cannot_parse}]}],[]} = leex:file(Filename, Ret),
+
+ ok = file:write_file(Filename,<<
+ "%% coding: UTF-8\n"
+ "Definitions.\n"
+ "A = a\n"
+ "L = [{A}-{Z}]\n"
+ "Z = z\n"
+ "Rules.\n"
+ "{L}+ : {token,{list_to_atom(TokenChars),H�pp}}.\n"
+ >>),
+ {error,[{_,[{7,leex,cannot_parse}]}],[]} = leex:file(Filename, Ret),
+
+ ok = file:write_file(Filename,<<
+ "%% coding: UTF-8\n"
+ "Definitions.\n"
+ "A = a\n"
+ "L = [{A}-{Z}]\n"
+ "Z = z\n"
+ "Rules.\n"
+ "{L}+ : {token,{list_to_atom(TokenChars)}}.\n"
+ "Erlang code.\n"
+ "-export([t/0]).\n"
+ "t() ->\n"
+ " H�pp\n"
+ >>),
+ {error,[{_,[{11,leex,cannot_parse}]}],[]} = leex:file(Filename, Ret),
+
+ Mini = <<"Definitions.\n"
+ "D = [0-9]\n"
+ "Rules.\n"
+ "{L}+ : {token,{word,TokenLine,TokenChars}}.\n"
+ "Erlang code.\n">>,
+ LeexPre = filename:join(Dir, "leexinc.hrl"),
+ ?line ok = file:write_file(LeexPre, <<"%% coding: UTF-8\n �">>),
+ PreErrors = run_test(Config, Mini, LeexPre),
+ {error,[{IncludeFile,[{2,leex,cannot_parse}]}],[]} = PreErrors,
+ "leexinc.hrl" = filename:basename(IncludeFile),
+
+ Ts = [{uni_1,
+ <<"%% coding: UTF-8\n"
+ "Definitions.\n"
+ "A = a\n"
+ "L = [{A}-{Z}]\n"
+ "Z = z\n"
+ "Rules.\n"
+ "{L}+ : {token,{list_to_atom(TokenChars),\n"
+ "begin Häpp = foo, Häpp end,"
+ " 'Häpp',\"\\x{400}B\",\"örn_Ѐ\"}}.\n"
+ "Erlang code.\n"
+ "-export([t/0]).\n"
+ "t() ->\n"
+ " %% Häpp, 'Häpp',\"\\x{400}B\",\"örn_Ѐ\"\n"
+ " {ok, [R], 1} = string(\"tip\"),\n"
+ " {tip,foo,'Häpp',[1024,66],[246,114,110,95,1024]} = R,\n"
+ " Häpp = foo,\n"
+ " {tip, Häpp, 'Häpp',\"\\x{400}B\",\"örn_Ѐ\"} = R,\n"
+ " ok.\n">>,
+ default,
+ ok},
+ {uni_2,
+ <<"%% coding: Latin-1\n"
+ "Definitions.\n"
+ "A = a\n"
+ "L = [{A}-{Z}]\n"
+ "Z = z\n"
+ "Rules.\n"
+ "{L}+ : {token,{list_to_atom(TokenChars),\n"
+ "begin H�pp = foo, H�pp end,"
+ " 'H�pp',\"\\x{400}B\",\"örn_Ѐ\"}}.\n"
+ "Erlang code.\n"
+ "-export([t/0]).\n"
+ "t() ->\n"
+ " %% H�pp, 'H�pp',\"\\x{400}B\",\"örn_Ѐ\"\n"
+ " {ok, [R], 1} = string(\"tip\"),\n"
+ " {tip,foo,'H�pp',[1024,66],[195,182,114,110,95,208,128]} = R,\n"
+ " H�pp = foo,\n"
+ " {tip, H�pp, 'H�pp',\"\\x{400}B\",\"örn_Ѐ\"} = R,\n"
+ " ok.\n">>,
+ default,
+ ok}],
+ run(Config, Ts),
+
+ ok.
+
unwritable(Fname) ->
{ok, Info} = file:read_file_info(Fname),
Mode = Info#file_info.mode - 8#00200,
diff --git a/lib/parsetools/test/yecc_SUITE.erl b/lib/parsetools/test/yecc_SUITE.erl
index 3d26adf1be..c306dbe833 100644
--- a/lib/parsetools/test/yecc_SUITE.erl
+++ b/lib/parsetools/test/yecc_SUITE.erl
@@ -1,7 +1,8 @@
+%% -*- coding: latin-1 -*-
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -48,7 +49,7 @@
otp_5369/1, otp_6362/1, otp_7945/1, otp_8483/1, otp_8486/1,
- otp_7292/1, otp_7969/1, otp_8919/1]).
+ otp_7292/1, otp_7969/1, otp_8919/1, otp_10302/1]).
% Default timetrap timeout (set in init_per_testcase).
-define(default_timeout, ?t:minutes(1)).
@@ -75,7 +76,7 @@ groups() ->
[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]}].
+ {improvements, [], [otp_7292, otp_7969, otp_8919, otp_10302]}].
init_per_suite(Config) ->
Config.
@@ -1815,6 +1816,153 @@ otp_8919(Config) when is_list(Config) ->
"syntax error before: \"hello\"" = lists:flatten(Mod:format_error(Mess)),
ok.
+otp_10302(doc) ->
+ "OTP-10302. Unicode characters scanner/parser.";
+otp_10302(suite) -> [];
+otp_10302(Config) when is_list(Config) ->
+ Dir = ?privdir,
+ Filename = filename:join(Dir, "OTP-10302.yrl"),
+ Ret = [return, {report, true}],
+ Mini1 = <<"%% coding: utf-8
+ Nonterminals H�pp.
+ nt -> t.">>,
+ ok = file:write_file(Filename, Mini1),
+ %% This could (and should) be refined:
+ {error,[{Filename,[{2,Mod1,Err1}]}],[]} =
+ yecc:file(Filename, Ret),
+ "cannot translate from UTF-8" = Mod1:format_error(Err1),
+
+ Mini2 = <<"%% coding: Utf-8
+ Nonterminals Hopp.
+ Terminals t.
+ Rootsymbol Hopp.
+
+ Hopp -> t.
+
+ Erlang code.
+
+ t() ->
+ H�pp.">>,
+ ok = file:write_file(Filename, Mini2),
+ {error,[{Filename,[{11,Mod2,Err2}]}],[]} =
+ yecc:file(Filename, Ret),
+ "cannot parse; possibly encoding mismatch" = Mod2:format_error(Err2),
+
+ Mini3 = <<"%% coding: latin-1
+ Nonterminals Hopp.
+ Terminals t.
+ Rootsymbol Hopp.
+
+ Hopp -> t.
+
+ Erlang code.
+
+ t() ->
+ H�pp.">>,
+ ok = file:write_file(Filename, Mini3),
+ YeccPre = filename:join(Dir, "yeccpre.hrl"),
+ ok = file:write_file(YeccPre, [<<"%% coding: UTF-8\n �.\n">>]),
+ Inc = [{includefile,YeccPre}],
+ {error,[{_,[{2,yecc,cannot_parse}]}],[]} =
+ yecc:file(Filename, Inc ++ Ret),
+
+ ok = file:write_file(Filename,
+ <<"%% coding: UTF-8
+ Nonterminals Hopp.
+ Terminals t.
+ Rootsymbol \"örn_Ѐ\".
+ Hopp -> t : '$1'.">>),
+ {error,[{Filename,[{4,yecc,{bad_symbol,"�rn_"++[1024]}}]}],[]} =
+ yecc:file(Filename, Ret),
+
+ ok = file:write_file(Filename,
+ <<"%% coding: UTF-8
+ Nonterminals Hopp.
+ Terminals t.
+ Rootsymbol Hopp.
+ Endsymbol \"örn_Ѐ\".
+ Hopp -> t : '$1'.">>),
+ {error,[{Filename,[{5,yecc,{bad_symbol,"�rn_"++[1024]}}]}],[]} =
+ yecc:file(Filename, Ret),
+
+ ok = file:write_file(Filename,
+ <<"%% coding: UTF-8
+ Nonterminals Hopp.
+ Terminals t.
+ Rootsymbol Hopp.
+ Expect \"örn_Ѐ\".
+ Hopp -> t : '$1'.">>),
+ {error,[{Filename,[{5,yecc,{bad_symbol,"�rn_"++[1024]}}]}],[]} =
+ yecc:file(Filename, Ret),
+
+ ok = file:write_file(Filename,
+ <<"%% coding: UTF-8
+ Nonterminals Hopp.
+ Terminals t.
+ Rootsymbol Hopp.
+ States \"örn_Ѐ\".
+ Hopp -> t : '$1'.">>),
+ {error,[{Filename,[{5,yecc,{bad_symbol,"�rn_"++[1024]}}]}],[]} =
+ yecc:file(Filename, Ret),
+
+ Ts = [{otp_10302_1,<<"
+ %% coding: UTF-8
+ Header \"%% örn_Ѐ\" \"%% \\x{400}B\".
+ Nonterminals Häpp list.
+ Terminals element.
+ Rootsymbol Häpp.
+
+ Häpp -> list : '$1'.
+
+ list -> element : '$1'.
+ list -> list element :
+ begin
+ Häpp = foo,
+ {Häpp, 'Häpp',\"\\x{400}B\",\"örn_Ѐ\"}
+ end.
+
+ Erlang code.
+
+ -export([t/0]).
+
+ t() ->
+ L = [{element, 1}, {element,2}],
+ {ok, R} = parse(L),
+ Häpp = foo,
+ {_,_,[1024,66],[246,114,110,95,1024]} = R,
+ {Häpp,'Häpp',\"\\x{400}B\",\"örn_Ѐ\"} = R,
+ ok.
+ ">>,default,ok},
+ {otp_10302_2,<<"
+ %% coding: Latin-1
+ Nonterminals H�pp list.
+ Terminals element.
+ Rootsymbol H�pp.
+
+ H�pp -> list : '$1'.
+
+ list -> element : '$1'.
+ list -> list element :
+ begin
+ H�pp = foo,
+ {H�pp, 'H�pp',\"\\x{400}B\",\"örn_Ѐ\"}
+ end.
+
+ Erlang code.
+
+ -export([t/0]).
+
+ t() ->
+ L = [{element, 1}, {element,2}],
+ {ok, R} = parse(L),
+ H�pp = foo,
+ {_,_,[1024,66],[195,182,114,110,95,208,128]} = R,
+ {H�pp,'H�pp',\"\\x{400}B\",\"örn_Ѐ\"} = R,
+ ok.
+ ">>,default,ok}],
+ run(Config, Ts),
+ ok.
+
yeccpre_size() ->
yeccpre_size(default_yeccpre()).
diff --git a/lib/percept/doc/src/Makefile b/lib/percept/doc/src/Makefile
index e749103783..3182dc2954 100644
--- a/lib/percept/doc/src/Makefile
+++ b/lib/percept/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2007-2010. All Rights Reserved.
+# Copyright Ericsson AB 2007-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/percept/doc/src/notes.xml b/lib/percept/doc/src/notes.xml
index 1ac1a5ab62..dd70cfe994 100644
--- a/lib/percept/doc/src/notes.xml
+++ b/lib/percept/doc/src/notes.xml
@@ -32,6 +32,21 @@
</header>
<p>This document describes the changes made to the Percept application.</p>
+<section><title>Percept 0.8.7</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Add missing modules in app-file</p>
+ <p>
+ Own Id: OTP-10439</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Percept 0.8.6.1</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/percept/priv/Makefile b/lib/percept/priv/Makefile
index 4d7942c49c..b82b51ed4b 100644
--- a/lib/percept/priv/Makefile
+++ b/lib/percept/priv/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2007-2009. All Rights Reserved.
+# Copyright Ericsson AB 2007-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/percept/src/Makefile b/lib/percept/src/Makefile
index 4b9a10cdb8..6bf0af9dc6 100644
--- a/lib/percept/src/Makefile
+++ b/lib/percept/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2007-2009. All Rights Reserved.
+# Copyright Ericsson AB 2007-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -80,10 +80,10 @@ clean:
rm -f errs core *~
$(APP_TARGET): $(APP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
docs:
diff --git a/lib/percept/src/percept.app.src b/lib/percept/src/percept.app.src
index c70fede721..cf4a9fc438 100644
--- a/lib/percept/src/percept.app.src
+++ b/lib/percept/src/percept.app.src
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -17,14 +17,26 @@
%% %CopyrightEnd%
%%
-{application,percept,
- [{description, "PERCEPT Erlang Concurrency Profiling Tool"},
- {vsn, "%VSN%"},
- {modules, [percept,percept_db,percept_html,percept_graph,percept_analyzer]},
- {registered, [percept_db,percept_port]},
- {applications, [kernel,stdlib]},
- {env, []}
- ]}.
-
+{application,percept, [
+ {description, "PERCEPT Erlang Concurrency Profiling Tool"},
+ {vsn, "%VSN%"},
+ {modules, [
+ egd,
+ egd_font,
+ egd_png,
+ egd_primitives,
+ egd_render,
+ percept,
+ percept_analyzer,
+ percept_db,
+ percept_graph,
+ percept_html,
+ percept_image
+ ]},
+ {registered, [percept_db,percept_port]},
+ {applications, [kernel,stdlib]},
+ {env,[]}
+]}.
+%% vim: syntax=erlang
diff --git a/lib/percept/test/Makefile b/lib/percept/test/Makefile
index ee70966b01..156ef5dd0a 100644
--- a/lib/percept/test/Makefile
+++ b/lib/percept/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2007-2011. All Rights Reserved.
+# Copyright Ericsson AB 2007-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/percept/vsn.mk b/lib/percept/vsn.mk
index 9267a41055..f868246f7b 100644
--- a/lib/percept/vsn.mk
+++ b/lib/percept/vsn.mk
@@ -1 +1 @@
-PERCEPT_VSN = 0.8.6.1
+PERCEPT_VSN = 0.8.7
diff --git a/lib/pman/doc/src/Makefile b/lib/pman/doc/src/Makefile
index ec759091ab..d9e0317f05 100644
--- a/lib/pman/doc/src/Makefile
+++ b/lib/pman/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2009. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/pman/src/Makefile b/lib/pman/src/Makefile
index 2cd1c64c49..eb0413bdbc 100644
--- a/lib/pman/src/Makefile
+++ b/lib/pman/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2009. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -85,10 +85,10 @@ clean:
rm -f errs core *~
$(APP_TARGET): $(APP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
docs:
diff --git a/lib/public_key/asn1/AuthenticationFramework.asn1 b/lib/public_key/asn1/AuthenticationFramework.asn1
new file mode 100644
index 0000000000..3754486473
--- /dev/null
+++ b/lib/public_key/asn1/AuthenticationFramework.asn1
@@ -0,0 +1,367 @@
+AuthenticationFramework {joint-iso-itu-t ds(5) module(1)
+ authenticationFramework(7) 6} DEFINITIONS ::=
+BEGIN
+
+-- EXPORTS All
+-- The types and values defined in this module are exported for use in the other ASN.1 modules contained
+-- within the Directory Specifications, and for the use of other applications which will use them to access
+-- Directory services. Other applications may use them for their own purposes, but this will not constrain
+-- extensions and modifications needed to maintain or improve the Directory service.
+IMPORTS
+ id-at, id-nf, id-oc, informationFramework, selectedAttributeTypes,
+ basicAccessControl, certificateExtensions
+ FROM UsefulDefinitions {joint-iso-itu-t ds(5) module(1)
+ usefulDefinitions(0) 6}
+ Name, ATTRIBUTE, OBJECT-CLASS, NAME-FORM, top
+ FROM InformationFramework informationFramework
+ UniqueIdentifier, octetStringMatch, commonName, UnboundedDirectoryString
+ FROM SelectedAttributeTypes selectedAttributeTypes
+ certificateExactMatch, certificatePairExactMatch, certificateListExactMatch,
+ KeyUsage, GeneralNames, CertificatePoliciesSyntax,
+ algorithmIdentifierMatch, CertPolicyId
+ FROM CertificateExtensions certificateExtensions;
+
+-- parameterized types
+ENCRYPTED{ToBeEnciphered} ::=
+ BIT STRING
+ (CONSTRAINED BY {
+ -- shall be the result of applying an encipherment procedure
+ -- to the BER-encoded octets of a value of --ToBeEnciphered})
+
+HASH{ToBeHashed} ::= SEQUENCE {
+ algorithmIdentifier AlgorithmIdentifier{{SupportedAlgorithms}},
+ hashValue
+ BIT STRING
+ (CONSTRAINED BY {
+ -- shall be the result of applying a hashing procedure to the DER-encoded octets
+ -- of a value of -- ToBeHashed})
+}
+
+ENCRYPTED-HASH{ToBeSigned} ::=
+ BIT STRING
+ (CONSTRAINED BY {
+ -- shall be the result of applying a hashing procedure to the DER-encoded (see 6.1) octets
+ -- of a value of --ToBeSigned -- and then applying an encipherment procedure to those octets --})
+
+SIGNATURE{ToBeSigned} ::= SEQUENCE {
+ algorithmIdentifier AlgorithmIdentifier{{SupportedAlgorithms}},
+ encrypted ENCRYPTED-HASH{ToBeSigned}
+}
+
+SIGNED{ToBeSigned} ::= SEQUENCE {
+ toBeSigned ToBeSigned,
+ COMPONENTS OF SIGNATURE{ToBeSigned}
+}
+
+-- public-key certificate definition
+Certificate ::= SIGNED{CertificateContent}
+
+CertificateContent ::= SEQUENCE {
+ version [0] Version DEFAULT v1,
+ serialNumber CertificateSerialNumber,
+ signature AlgorithmIdentifier{{SupportedAlgorithms}},
+ issuer Name,
+ validity Validity,
+ subject Name,
+ subjectPublicKeyInfo SubjectPublicKeyInfo,
+ issuerUniqueIdentifier [1] IMPLICIT UniqueIdentifier OPTIONAL,
+ -- if present, version shall be v2 or v3
+ subjectUniqueIdentifier [2] IMPLICIT UniqueIdentifier OPTIONAL,
+ -- if present, version shall be v2 or v3
+ extensions [3] Extensions OPTIONAL
+ -- If present, version shall be v3
+}
+
+Version ::= INTEGER {v1(0), v2(1), v3(2)}
+
+CertificateSerialNumber ::= INTEGER
+
+AlgorithmIdentifier{ALGORITHM:SupportedAlgorithms} ::= SEQUENCE {
+ algorithm ALGORITHM.&id({SupportedAlgorithms}),
+ parameters ALGORITHM.&Type({SupportedAlgorithms}{@algorithm}) OPTIONAL
+}
+
+-- Definition of the following information object set is deferred, perhaps to standardized
+-- profiles or to protocol implementation conformance statements. The set is required to
+-- specify a table constraint on the parameters component of AlgorithmIdentifier.
+SupportedAlgorithms ALGORITHM ::=
+ {...}
+
+Validity ::= SEQUENCE {notBefore Time,
+ notAfter Time
+}
+
+SubjectPublicKeyInfo ::= SEQUENCE {
+ algorithm AlgorithmIdentifier{{SupportedAlgorithms}},
+ subjectPublicKey BIT STRING
+}
+
+Time ::= CHOICE {utcTime UTCTime,
+ generalizedTime GeneralizedTime
+}
+
+Extensions ::= SEQUENCE OF Extension
+
+-- For those extensions where ordering of individual extensions within the SEQUENCE is significant, the
+-- specification of those individual extensions shall include the rules for the significance of the order therein
+Extension ::= SEQUENCE {
+ extnId EXTENSION.&id({ExtensionSet}),
+ critical BOOLEAN DEFAULT FALSE,
+ extnValue
+ OCTET STRING
+ (CONTAINING EXTENSION.&ExtnType({ExtensionSet}{@extnId})
+ ENCODED BY
+ der)
+}
+
+der OBJECT IDENTIFIER ::=
+ {joint-iso-itu-t asn1(1) ber-derived(2) distinguished-encoding(1)}
+
+ExtensionSet EXTENSION ::=
+ {...}
+
+EXTENSION ::= CLASS {&id OBJECT IDENTIFIER UNIQUE,
+ &ExtnType
+}WITH SYNTAX {SYNTAX &ExtnType
+ IDENTIFIED BY &id
+}
+
+ALGORITHM ::= CLASS {&Type OPTIONAL,
+ &id OBJECT IDENTIFIER UNIQUE
+}WITH SYNTAX {[&Type]
+ IDENTIFIED BY &id
+}
+
+-- other PKI certificate constructs
+Certificates ::= SEQUENCE {
+ userCertificate Certificate,
+ certificationPath ForwardCertificationPath OPTIONAL
+}
+
+CertificationPath ::= SEQUENCE {
+ userCertificate Certificate,
+ theCACertificates SEQUENCE OF CertificatePair OPTIONAL
+}
+
+ForwardCertificationPath ::= SEQUENCE OF CrossCertificates
+
+CrossCertificates ::= SET OF Certificate
+
+PkiPath ::= SEQUENCE OF Certificate
+
+-- certificate revocation list (CRL)
+CertificateList ::=
+ SIGNED{CertificateListContent}
+
+CertificateListContent ::= SEQUENCE {
+ version Version OPTIONAL,
+ -- if present, version shall be v2
+ signature AlgorithmIdentifier{{SupportedAlgorithms}},
+ issuer Name,
+ thisUpdate Time,
+ nextUpdate Time OPTIONAL,
+ revokedCertificates
+ SEQUENCE OF
+ SEQUENCE {serialNumber CertificateSerialNumber,
+ revocationDate Time,
+ crlEntryExtensions Extensions OPTIONAL} OPTIONAL,
+ crlExtensions [0] Extensions OPTIONAL
+}
+
+-- PKI object classes
+pkiUser OBJECT-CLASS ::= {
+ SUBCLASS OF {top}
+ KIND auxiliary
+ MAY CONTAIN {userCertificate}
+ ID id-oc-pkiUser
+}
+
+pkiCA OBJECT-CLASS ::= {
+ SUBCLASS OF {top}
+ KIND auxiliary
+ MAY CONTAIN
+ {cACertificate | certificateRevocationList | authorityRevocationList |
+ crossCertificatePair}
+ ID id-oc-pkiCA
+}
+
+cRLDistributionPoint OBJECT-CLASS ::= {
+ SUBCLASS OF {top}
+ KIND structural
+ MUST CONTAIN {commonName}
+ MAY CONTAIN
+ {certificateRevocationList | authorityRevocationList | deltaRevocationList}
+ ID id-oc-cRLDistributionPoint
+}
+
+cRLDistPtNameForm NAME-FORM ::= {
+ NAMES cRLDistributionPoint
+ WITH ATTRIBUTES {commonName}
+ ID id-nf-cRLDistPtNameForm
+}
+
+deltaCRL OBJECT-CLASS ::= {
+ SUBCLASS OF {top}
+ KIND auxiliary
+ MAY CONTAIN {deltaRevocationList}
+ ID id-oc-deltaCRL
+}
+
+cpCps OBJECT-CLASS ::= {
+ SUBCLASS OF {top}
+ KIND auxiliary
+ MAY CONTAIN {certificatePolicy | certificationPracticeStmt}
+ ID id-oc-cpCps
+}
+
+pkiCertPath OBJECT-CLASS ::= {
+ SUBCLASS OF {top}
+ KIND auxiliary
+ MAY CONTAIN {pkiPath}
+ ID id-oc-pkiCertPath
+}
+
+-- PKI directory attributes
+userCertificate ATTRIBUTE ::= {
+ WITH SYNTAX Certificate
+ EQUALITY MATCHING RULE certificateExactMatch
+ ID id-at-userCertificate
+}
+
+cACertificate ATTRIBUTE ::= {
+ WITH SYNTAX Certificate
+ EQUALITY MATCHING RULE certificateExactMatch
+ ID id-at-cAcertificate
+}
+
+crossCertificatePair ATTRIBUTE ::= {
+ WITH SYNTAX CertificatePair
+ EQUALITY MATCHING RULE certificatePairExactMatch
+ ID id-at-crossCertificatePair
+}
+
+CertificatePair ::= SEQUENCE {
+ forward [0] Certificate OPTIONAL,
+ reverse [1] Certificate OPTIONAL
+ -- at least one of the pair shall be present
+}
+(WITH COMPONENTS {
+ ...,
+ forward PRESENT
+ } | WITH COMPONENTS {
+ ...,
+ reverse PRESENT
+ })
+
+certificateRevocationList ATTRIBUTE ::= {
+ WITH SYNTAX CertificateList
+ EQUALITY MATCHING RULE certificateListExactMatch
+ ID id-at-certificateRevocationList
+}
+
+authorityRevocationList ATTRIBUTE ::= {
+ WITH SYNTAX CertificateList
+ EQUALITY MATCHING RULE certificateListExactMatch
+ ID id-at-authorityRevocationList
+}
+
+deltaRevocationList ATTRIBUTE ::= {
+ WITH SYNTAX CertificateList
+ EQUALITY MATCHING RULE certificateListExactMatch
+ ID id-at-deltaRevocationList
+}
+
+supportedAlgorithms ATTRIBUTE ::= {
+ WITH SYNTAX SupportedAlgorithm
+ EQUALITY MATCHING RULE algorithmIdentifierMatch
+ ID id-at-supportedAlgorithms
+}
+
+SupportedAlgorithm ::= SEQUENCE {
+ algorithmIdentifier AlgorithmIdentifier{{SupportedAlgorithms}},
+ intendedUsage [0] KeyUsage OPTIONAL,
+ intendedCertificatePolicies [1] CertificatePoliciesSyntax OPTIONAL
+}
+
+certificationPracticeStmt ATTRIBUTE ::= {
+ WITH SYNTAX InfoSyntax
+ ID id-at-certificationPracticeStmt
+}
+
+InfoSyntax ::= CHOICE {
+ content UnboundedDirectoryString,
+ pointer SEQUENCE {name GeneralNames,
+ hash HASH{HashedPolicyInfo} OPTIONAL}
+}
+
+POLICY ::= TYPE-IDENTIFIER
+
+HashedPolicyInfo ::= POLICY.&Type({Policies})
+
+Policies POLICY ::=
+ {...} -- Defined by implementors
+
+certificatePolicy ATTRIBUTE ::= {
+ WITH SYNTAX PolicySyntax
+ ID id-at-certificatePolicy
+}
+
+PolicySyntax ::= SEQUENCE {
+ policyIdentifier PolicyID,
+ policySyntax InfoSyntax
+}
+
+PolicyID ::= CertPolicyId
+
+pkiPath ATTRIBUTE ::= {WITH SYNTAX PkiPath
+ ID id-at-pkiPath
+}
+
+userPassword ATTRIBUTE ::= {
+ WITH SYNTAX OCTET STRING(SIZE (0..MAX))
+ EQUALITY MATCHING RULE octetStringMatch
+ ID id-at-userPassword
+}
+
+-- object identifier assignments
+-- object classes
+id-oc-cRLDistributionPoint OBJECT IDENTIFIER ::=
+ {id-oc 19}
+
+id-oc-pkiUser OBJECT IDENTIFIER ::= {id-oc 21}
+
+id-oc-pkiCA OBJECT IDENTIFIER ::= {id-oc 22}
+
+id-oc-deltaCRL OBJECT IDENTIFIER ::= {id-oc 23}
+
+id-oc-cpCps OBJECT IDENTIFIER ::= {id-oc 30}
+
+id-oc-pkiCertPath OBJECT IDENTIFIER ::= {id-oc 31}
+
+-- name forms
+id-nf-cRLDistPtNameForm OBJECT IDENTIFIER ::= {id-nf 14}
+
+-- directory attributes
+id-at-userPassword OBJECT IDENTIFIER ::= {id-at 35}
+
+id-at-userCertificate OBJECT IDENTIFIER ::= {id-at 36}
+
+id-at-cAcertificate OBJECT IDENTIFIER ::= {id-at 37}
+
+id-at-authorityRevocationList OBJECT IDENTIFIER ::= {id-at 38}
+
+id-at-certificateRevocationList OBJECT IDENTIFIER ::= {id-at 39}
+
+id-at-crossCertificatePair OBJECT IDENTIFIER ::= {id-at 40}
+
+id-at-supportedAlgorithms OBJECT IDENTIFIER ::= {id-at 52}
+
+id-at-deltaRevocationList OBJECT IDENTIFIER ::= {id-at 53}
+
+id-at-certificationPracticeStmt OBJECT IDENTIFIER ::= {id-at 68}
+
+id-at-certificatePolicy OBJECT IDENTIFIER ::= {id-at 69}
+
+id-at-pkiPath OBJECT IDENTIFIER ::= {id-at 70}
+
+END -- AuthenticationFramework
diff --git a/lib/public_key/asn1/InformationFramework.asn1 b/lib/public_key/asn1/InformationFramework.asn1
new file mode 100644
index 0000000000..4aed43a39e
--- /dev/null
+++ b/lib/public_key/asn1/InformationFramework.asn1
@@ -0,0 +1,682 @@
+InformationFramework {joint-iso-itu-t ds(5) module(1) informationFramework(1)
+ 6} DEFINITIONS ::=
+BEGIN
+
+-- EXPORTS All
+-- The types and values defined in this module are exported for use in the other ASN.1 modules contained
+-- within the Directory Specifications, and for the use of other applications which will use them to access
+-- Directory services. Other applications may use them for their own purposes, but this will not constrain
+-- extensions and modifications needed to maintain or improve the Directory service.
+IMPORTS
+ -- from ITU-T Rec. X.501 | ISO/IEC 9594-2
+ directoryAbstractService, id-ar, id-at, id-mr, id-nf, id-oa, id-oc,
+ id-sc, selectedAttributeTypes, serviceAdministration
+ FROM UsefulDefinitions {joint-iso-itu-t ds(5) module(1)
+ usefulDefinitions(0) 6}
+ SearchRule
+ FROM ServiceAdministration serviceAdministration
+ -- from ITU-T Rec. X.511 | ISO/IEC 9594-3
+ TypeAndContextAssertion
+ FROM DirectoryAbstractService directoryAbstractService
+ -- from ITU-T Rec. X.520 | ISO/IEC 9594-6
+ booleanMatch, commonName, generalizedTimeMatch, generalizedTimeOrderingMatch,
+ integerFirstComponentMatch, integerMatch, integerOrderingMatch,
+ objectIdentifierFirstComponentMatch, UnboundedDirectoryString
+ FROM SelectedAttributeTypes selectedAttributeTypes;
+
+-- attribute data types
+Attribute{ATTRIBUTE:SupportedAttributes} ::= SEQUENCE {
+ type ATTRIBUTE.&id({SupportedAttributes}),
+ values
+ SET SIZE (0..MAX) OF ATTRIBUTE.&Type({SupportedAttributes}{@type}),
+ valuesWithContext
+ SET SIZE (1..MAX) OF
+ SEQUENCE {value ATTRIBUTE.&Type({SupportedAttributes}{@type}),
+ contextList SET SIZE (1..MAX) OF Context} OPTIONAL
+}
+
+AttributeType ::= ATTRIBUTE.&id
+
+AttributeValue ::= ATTRIBUTE.&Type
+
+Context ::= SEQUENCE {
+ contextType CONTEXT.&id({SupportedContexts}),
+ contextValues
+ SET SIZE (1..MAX) OF CONTEXT.&Type({SupportedContexts}{@contextType}),
+ fallback BOOLEAN DEFAULT FALSE
+}
+
+AttributeValueAssertion ::= SEQUENCE {
+ type ATTRIBUTE.&id({SupportedAttributes}),
+ assertion
+ ATTRIBUTE.&equality-match.&AssertionType
+ ({SupportedAttributes}{@type}),
+ assertedContexts
+ CHOICE {allContexts [0] NULL,
+ selectedContexts [1] SET SIZE (1..MAX) OF ContextAssertion
+ } OPTIONAL
+}
+
+ContextAssertion ::= SEQUENCE {
+ contextType CONTEXT.&id({SupportedContexts}),
+ contextValues
+ SET SIZE (1..MAX) OF
+ CONTEXT.&Assertion({SupportedContexts}{@contextType})
+}
+
+AttributeTypeAssertion ::= SEQUENCE {
+ type ATTRIBUTE.&id({SupportedAttributes}),
+ assertedContexts SEQUENCE SIZE (1..MAX) OF ContextAssertion OPTIONAL
+}
+
+-- Definition of the following information object set is deferred, perhaps to standardized
+-- profiles or to protocol implementation conformance statements. The set is required to
+-- specify a table constraint on the values component of Attribute, the value component
+-- of AttributeTypeAndValue, and the assertion component of AttributeValueAssertion.
+SupportedAttributes ATTRIBUTE ::=
+ {objectClass | aliasedEntryName, ...}
+
+-- Definition of the following information object set is deferred, perhaps to standardized
+-- profiles or to protocol implementation conformance statements. The set is required to
+-- specify a table constraint on the context specifications
+SupportedContexts CONTEXT ::=
+ {...}
+
+-- naming data types
+Name ::= CHOICE { -- only one possibility for now --rdnSequence RDNSequence
+}
+
+RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
+
+DistinguishedName ::= RDNSequence
+
+RelativeDistinguishedName ::=
+ SET SIZE (1..MAX) OF AttributeTypeAndDistinguishedValue
+
+AttributeTypeAndDistinguishedValue ::= SEQUENCE {
+ type ATTRIBUTE.&id({SupportedAttributes}),
+ value ATTRIBUTE.&Type({SupportedAttributes}{@type}),
+ primaryDistinguished BOOLEAN DEFAULT TRUE,
+ valuesWithContext
+ SET SIZE (1..MAX) OF
+ SEQUENCE {distingAttrValue
+ [0] ATTRIBUTE.&Type({SupportedAttributes}{@type})
+ OPTIONAL,
+ contextList SET SIZE (1..MAX) OF Context} OPTIONAL
+}
+
+-- subtree data types
+SubtreeSpecification ::= SEQUENCE {
+ base [0] LocalName DEFAULT {},
+ COMPONENTS OF ChopSpecification,
+ specificationFilter [4] Refinement OPTIONAL
+}
+
+-- empty sequence specifies whole administrative area
+LocalName ::= RDNSequence
+
+ChopSpecification ::= SEQUENCE {
+ specificExclusions
+ [1] SET SIZE (1..MAX) OF
+ CHOICE {chopBefore [0] LocalName,
+ chopAfter [1] LocalName} OPTIONAL,
+ minimum [2] BaseDistance DEFAULT 0,
+ maximum [3] BaseDistance OPTIONAL
+}
+
+BaseDistance ::= INTEGER(0..MAX)
+
+Refinement ::= CHOICE {
+ item [0] OBJECT-CLASS.&id,
+ and [1] SET SIZE (1..MAX) OF Refinement,
+ or [2] SET SIZE (1..MAX) OF Refinement,
+ not [3] Refinement
+}
+
+-- OBJECT-CLASS information object class specification
+OBJECT-CLASS ::= CLASS {
+ &Superclasses OBJECT-CLASS OPTIONAL,
+ &kind ObjectClassKind DEFAULT structural,
+ &MandatoryAttributes ATTRIBUTE OPTIONAL,
+ &OptionalAttributes ATTRIBUTE OPTIONAL,
+ &id OBJECT IDENTIFIER UNIQUE
+}
+WITH SYNTAX {
+ [SUBCLASS OF &Superclasses]
+ [KIND &kind]
+ [MUST CONTAIN &MandatoryAttributes]
+ [MAY CONTAIN &OptionalAttributes]
+ ID &id
+}
+
+ObjectClassKind ::= ENUMERATED {abstract(0), structural(1), auxiliary(2)}
+
+-- object classes
+top OBJECT-CLASS ::= {
+ KIND abstract
+ MUST CONTAIN {objectClass}
+ ID id-oc-top
+}
+
+alias OBJECT-CLASS ::= {
+ SUBCLASS OF {top}
+ MUST CONTAIN {aliasedEntryName}
+ ID id-oc-alias
+}
+
+parent OBJECT-CLASS ::= {KIND abstract
+ ID id-oc-parent
+}
+
+child OBJECT-CLASS ::= {KIND auxiliary
+ ID id-oc-child
+}
+
+-- ATTRIBUTE information object class specification
+ATTRIBUTE ::= CLASS {
+ &derivation ATTRIBUTE OPTIONAL,
+ &Type OPTIONAL, -- either &Type or &derivation required
+ &equality-match MATCHING-RULE OPTIONAL,
+ &ordering-match MATCHING-RULE OPTIONAL,
+ &substrings-match MATCHING-RULE OPTIONAL,
+ &single-valued BOOLEAN DEFAULT FALSE,
+ &collective BOOLEAN DEFAULT FALSE,
+ &dummy BOOLEAN DEFAULT FALSE,
+ -- operational extensions
+ &no-user-modification BOOLEAN DEFAULT FALSE,
+ &usage AttributeUsage DEFAULT userApplications,
+ &id OBJECT IDENTIFIER UNIQUE
+}
+WITH SYNTAX {
+ [SUBTYPE OF &derivation]
+ [WITH SYNTAX &Type]
+ [EQUALITY MATCHING RULE &equality-match]
+ [ORDERING MATCHING RULE &ordering-match]
+ [SUBSTRINGS MATCHING RULE &substrings-match]
+ [SINGLE VALUE &single-valued]
+ [COLLECTIVE &collective]
+ [DUMMY &dummy]
+ [NO USER MODIFICATION &no-user-modification]
+ [USAGE &usage]
+ ID &id
+}
+
+AttributeUsage ::= ENUMERATED {
+ userApplications(0), directoryOperation(1), distributedOperation(2),
+ dSAOperation(3)}
+
+-- attributes
+objectClass ATTRIBUTE ::= {
+ WITH SYNTAX OBJECT IDENTIFIER
+ EQUALITY MATCHING RULE objectIdentifierMatch
+ ID id-at-objectClass
+}
+
+aliasedEntryName ATTRIBUTE ::= {
+ WITH SYNTAX DistinguishedName
+ EQUALITY MATCHING RULE distinguishedNameMatch
+ SINGLE VALUE TRUE
+ ID id-at-aliasedEntryName
+}
+
+-- MATCHING-RULE information object class specification
+MATCHING-RULE ::= CLASS {
+ &ParentMatchingRules MATCHING-RULE OPTIONAL,
+ &AssertionType OPTIONAL,
+ &uniqueMatchIndicator ATTRIBUTE OPTIONAL,
+ &id OBJECT IDENTIFIER UNIQUE
+}
+WITH SYNTAX {
+ [PARENT &ParentMatchingRules]
+ [SYNTAX &AssertionType]
+ [UNIQUE-MATCH-INDICATOR &uniqueMatchIndicator]
+ ID &id
+}
+
+-- matching rules
+objectIdentifierMatch MATCHING-RULE ::= {
+ SYNTAX OBJECT IDENTIFIER
+ ID id-mr-objectIdentifierMatch
+}
+
+distinguishedNameMatch MATCHING-RULE ::= {
+ SYNTAX DistinguishedName
+ ID id-mr-distinguishedNameMatch
+}
+
+MAPPING-BASED-MATCHING{SelectedBy, BOOLEAN:combinable, MappingResult,
+ OBJECT IDENTIFIER:matchingRule} ::= CLASS {
+ &selectBy SelectedBy OPTIONAL,
+ &ApplicableTo ATTRIBUTE,
+ &subtypesIncluded BOOLEAN DEFAULT TRUE,
+ &combinable BOOLEAN(combinable),
+ &mappingResults MappingResult OPTIONAL,
+ &userControl BOOLEAN DEFAULT FALSE,
+ &exclusive BOOLEAN DEFAULT TRUE,
+ &matching-rule MATCHING-RULE.&id(matchingRule),
+ &id OBJECT IDENTIFIER UNIQUE
+}
+WITH SYNTAX {
+ [SELECT BY &selectBy]
+ APPLICABLE TO &ApplicableTo
+ [SUBTYPES INCLUDED &subtypesIncluded]
+ COMBINABLE &combinable
+ [MAPPING RESULTS &mappingResults]
+ [USER CONTROL &userControl]
+ [EXCLUSIVE &exclusive]
+ MATCHING RULE &matching-rule
+ ID &id
+}
+
+-- NAME-FORM information object class specification
+NAME-FORM ::= CLASS {
+ &namedObjectClass OBJECT-CLASS,
+ &MandatoryAttributes ATTRIBUTE,
+ &OptionalAttributes ATTRIBUTE OPTIONAL,
+ &id OBJECT IDENTIFIER UNIQUE
+}
+WITH SYNTAX {
+ NAMES &namedObjectClass
+ WITH ATTRIBUTES &MandatoryAttributes
+ [AND OPTIONALLY &OptionalAttributes]
+ ID &id
+}
+
+-- STRUCTURE-RULE class and DIT structure rule data types
+DITStructureRule ::= SEQUENCE {
+ ruleIdentifier RuleIdentifier,
+ -- shall be unique within the scope of the subschema
+ nameForm NAME-FORM.&id,
+ superiorStructureRules SET SIZE (1..MAX) OF RuleIdentifier OPTIONAL
+}
+
+RuleIdentifier ::= INTEGER
+
+STRUCTURE-RULE ::= CLASS {
+ &nameForm NAME-FORM,
+ &SuperiorStructureRules STRUCTURE-RULE OPTIONAL,
+ &id RuleIdentifier
+}
+WITH SYNTAX {
+ NAME FORM &nameForm
+ [SUPERIOR RULES &SuperiorStructureRules]
+ ID &id
+}
+
+-- DIT content rule data type and CONTENT-RULE class
+DITContentRule ::= SEQUENCE {
+ structuralObjectClass OBJECT-CLASS.&id,
+ auxiliaries SET SIZE (1..MAX) OF OBJECT-CLASS.&id OPTIONAL,
+ mandatory [1] SET SIZE (1..MAX) OF ATTRIBUTE.&id OPTIONAL,
+ optional [2] SET SIZE (1..MAX) OF ATTRIBUTE.&id OPTIONAL,
+ precluded [3] SET SIZE (1..MAX) OF ATTRIBUTE.&id OPTIONAL
+}
+
+CONTENT-RULE ::= CLASS {
+ &structuralClass OBJECT-CLASS.&id UNIQUE,
+ &Auxiliaries OBJECT-CLASS OPTIONAL,
+ &Mandatory ATTRIBUTE OPTIONAL,
+ &Optional ATTRIBUTE OPTIONAL,
+ &Precluded ATTRIBUTE OPTIONAL
+}
+WITH SYNTAX {
+ STRUCTURAL OBJECT-CLASS &structuralClass
+ [AUXILIARY OBJECT-CLASSES &Auxiliaries]
+ [MUST CONTAIN &Mandatory]
+ [MAY CONTAIN &Optional]
+ [MUST-NOT CONTAIN &Precluded]
+}
+
+CONTEXT ::= CLASS {
+ &Type ,
+ &DefaultValue OPTIONAL,
+ &Assertion OPTIONAL,
+ &absentMatch BOOLEAN DEFAULT TRUE,
+ &id OBJECT IDENTIFIER UNIQUE
+}
+WITH SYNTAX {
+ WITH SYNTAX &Type
+ [DEFAULT-VALUE &DefaultValue]
+ [ASSERTED AS &Assertion]
+ [ABSENT-MATCH &absentMatch]
+ ID &id
+}
+
+DITContextUse ::= SEQUENCE {
+ attributeType ATTRIBUTE.&id,
+ mandatoryContexts [1] SET SIZE (1..MAX) OF CONTEXT.&id OPTIONAL,
+ optionalContexts [2] SET SIZE (1..MAX) OF CONTEXT.&id OPTIONAL
+}
+
+DIT-CONTEXT-USE-RULE ::= CLASS {
+ &attributeType ATTRIBUTE.&id UNIQUE,
+ &Mandatory CONTEXT OPTIONAL,
+ &Optional CONTEXT OPTIONAL
+}
+WITH SYNTAX {
+ ATTRIBUTE TYPE &attributeType
+ [MANDATORY CONTEXTS &Mandatory]
+ [OPTIONAL CONTEXTS &Optional]
+}
+
+FRIENDS ::= CLASS {
+ &anchor ATTRIBUTE.&id UNIQUE,
+ &Friends ATTRIBUTE
+}WITH SYNTAX {ANCHOR &anchor
+ FRIENDS &Friends
+}
+
+-- system schema information objects
+-- object classes
+subentry OBJECT-CLASS ::= {
+ SUBCLASS OF {top}
+ KIND structural
+ MUST CONTAIN {commonName | subtreeSpecification}
+ ID id-sc-subentry
+}
+
+subentryNameForm NAME-FORM ::= {
+ NAMES subentry
+ WITH ATTRIBUTES {commonName}
+ ID id-nf-subentryNameForm
+}
+
+subtreeSpecification ATTRIBUTE ::= {
+ WITH SYNTAX SubtreeSpecification
+ USAGE directoryOperation
+ ID id-oa-subtreeSpecification
+}
+
+administrativeRole ATTRIBUTE ::= {
+ WITH SYNTAX OBJECT-CLASS.&id
+ EQUALITY MATCHING RULE objectIdentifierMatch
+ USAGE directoryOperation
+ ID id-oa-administrativeRole
+}
+
+createTimestamp ATTRIBUTE ::= {
+ WITH SYNTAX GeneralizedTime
+ -- as per 46.3 b) or c) of ITU-T Rec. X.680 | ISO/IEC 8824-1
+ EQUALITY MATCHING RULE generalizedTimeMatch
+ ORDERING MATCHING RULE generalizedTimeOrderingMatch
+ SINGLE VALUE TRUE
+ NO USER MODIFICATION TRUE
+ USAGE directoryOperation
+ ID id-oa-createTimestamp
+}
+
+modifyTimestamp ATTRIBUTE ::= {
+ WITH SYNTAX GeneralizedTime
+ -- as per 46.3 b) or c) of ITU-T Rec. X.680 | ISO/IEC 8824-1
+ EQUALITY MATCHING RULE generalizedTimeMatch
+ ORDERING MATCHING RULE generalizedTimeOrderingMatch
+ SINGLE VALUE TRUE
+ NO USER MODIFICATION TRUE
+ USAGE directoryOperation
+ ID id-oa-modifyTimestamp
+}
+
+subschemaTimestamp ATTRIBUTE ::= {
+ WITH SYNTAX GeneralizedTime
+ -- as per 46.3 b) or c) of ITU-T Rec. X.680 | ISO/IEC 8824-1
+ EQUALITY MATCHING RULE generalizedTimeMatch
+ ORDERING MATCHING RULE generalizedTimeOrderingMatch
+ SINGLE VALUE TRUE
+ NO USER MODIFICATION TRUE
+ USAGE directoryOperation
+ ID id-oa-subschemaTimestamp
+}
+
+creatorsName ATTRIBUTE ::= {
+ WITH SYNTAX DistinguishedName
+ EQUALITY MATCHING RULE distinguishedNameMatch
+ SINGLE VALUE TRUE
+ NO USER MODIFICATION TRUE
+ USAGE directoryOperation
+ ID id-oa-creatorsName
+}
+
+modifiersName ATTRIBUTE ::= {
+ WITH SYNTAX DistinguishedName
+ EQUALITY MATCHING RULE distinguishedNameMatch
+ SINGLE VALUE TRUE
+ NO USER MODIFICATION TRUE
+ USAGE directoryOperation
+ ID id-oa-modifiersName
+}
+
+subschemaSubentryList ATTRIBUTE ::= {
+ WITH SYNTAX DistinguishedName
+ EQUALITY MATCHING RULE distinguishedNameMatch
+ SINGLE VALUE TRUE
+ NO USER MODIFICATION TRUE
+ USAGE directoryOperation
+ ID id-oa-subschemaSubentryList
+}
+
+accessControlSubentryList ATTRIBUTE ::= {
+ WITH SYNTAX DistinguishedName
+ EQUALITY MATCHING RULE distinguishedNameMatch
+ NO USER MODIFICATION TRUE
+ USAGE directoryOperation
+ ID id-oa-accessControlSubentryList
+}
+
+collectiveAttributeSubentryList ATTRIBUTE ::= {
+ WITH SYNTAX DistinguishedName
+ EQUALITY MATCHING RULE distinguishedNameMatch
+ NO USER MODIFICATION TRUE
+ USAGE directoryOperation
+ ID id-oa-collectiveAttributeSubentryList
+}
+
+contextDefaultSubentryList ATTRIBUTE ::= {
+ WITH SYNTAX DistinguishedName
+ EQUALITY MATCHING RULE distinguishedNameMatch
+ NO USER MODIFICATION TRUE
+ USAGE directoryOperation
+ ID id-oa-contextDefaultSubentryList
+}
+
+serviceAdminSubentryList ATTRIBUTE ::= {
+ WITH SYNTAX DistinguishedName
+ EQUALITY MATCHING RULE distinguishedNameMatch
+ NO USER MODIFICATION TRUE
+ USAGE directoryOperation
+ ID id-oa-serviceAdminSubentryList
+}
+
+hasSubordinates ATTRIBUTE ::= {
+ WITH SYNTAX BOOLEAN
+ EQUALITY MATCHING RULE booleanMatch
+ SINGLE VALUE TRUE
+ NO USER MODIFICATION TRUE
+ USAGE directoryOperation
+ ID id-oa-hasSubordinates
+}
+
+accessControlSubentry OBJECT-CLASS ::= {
+ KIND auxiliary
+ ID id-sc-accessControlSubentry
+}
+
+collectiveAttributeSubentry OBJECT-CLASS ::= {
+ KIND auxiliary
+ ID id-sc-collectiveAttributeSubentry
+}
+
+collectiveExclusions ATTRIBUTE ::= {
+ WITH SYNTAX OBJECT IDENTIFIER
+ EQUALITY MATCHING RULE objectIdentifierMatch
+ USAGE directoryOperation
+ ID id-oa-collectiveExclusions
+}
+
+contextAssertionSubentry OBJECT-CLASS ::= {
+ KIND auxiliary
+ MUST CONTAIN {contextAssertionDefaults}
+ ID id-sc-contextAssertionSubentry
+}
+
+contextAssertionDefaults ATTRIBUTE ::= {
+ WITH SYNTAX TypeAndContextAssertion
+ EQUALITY MATCHING RULE objectIdentifierFirstComponentMatch
+ USAGE directoryOperation
+ ID id-oa-contextAssertionDefault
+}
+
+serviceAdminSubentry OBJECT-CLASS ::= {
+ KIND auxiliary
+ MUST CONTAIN {searchRules}
+ ID id-sc-serviceAdminSubentry
+}
+
+searchRules ATTRIBUTE ::= {
+ WITH SYNTAX SearchRuleDescription
+ EQUALITY MATCHING RULE integerFirstComponentMatch
+ USAGE directoryOperation
+ ID id-oa-searchRules
+}
+
+SearchRuleDescription ::= SEQUENCE {
+ COMPONENTS OF SearchRule,
+ name [28] SET SIZE (1..MAX) OF UnboundedDirectoryString OPTIONAL,
+ description [29] UnboundedDirectoryString OPTIONAL
+}
+
+hierarchyLevel ATTRIBUTE ::= {
+ WITH SYNTAX HierarchyLevel
+ EQUALITY MATCHING RULE integerMatch
+ ORDERING MATCHING RULE integerOrderingMatch
+ SINGLE VALUE TRUE
+ NO USER MODIFICATION TRUE
+ USAGE directoryOperation
+ ID id-oa-hierarchyLevel
+}
+
+HierarchyLevel ::= INTEGER
+
+hierarchyBelow ATTRIBUTE ::= {
+ WITH SYNTAX HierarchyBelow
+ EQUALITY MATCHING RULE booleanMatch
+ SINGLE VALUE TRUE
+ NO USER MODIFICATION TRUE
+ USAGE directoryOperation
+ ID id-oa-hierarchyBelow
+}
+
+HierarchyBelow ::= BOOLEAN
+
+hierarchyParent ATTRIBUTE ::= {
+ WITH SYNTAX DistinguishedName
+ EQUALITY MATCHING RULE distinguishedNameMatch
+ SINGLE VALUE TRUE
+ USAGE directoryOperation
+ ID id-oa-hierarchyParent
+}
+
+hierarchyTop ATTRIBUTE ::= {
+ WITH SYNTAX DistinguishedName
+ EQUALITY MATCHING RULE distinguishedNameMatch
+ SINGLE VALUE TRUE
+ USAGE directoryOperation
+ ID id-oa-hierarchyTop
+}
+
+-- object identifier assignments
+-- object classes
+id-oc-top OBJECT IDENTIFIER ::=
+ {id-oc 0}
+
+id-oc-alias OBJECT IDENTIFIER ::= {id-oc 1}
+
+id-oc-parent OBJECT IDENTIFIER ::= {id-oc 28}
+
+id-oc-child OBJECT IDENTIFIER ::= {id-oc 29}
+
+-- attributes
+id-at-objectClass OBJECT IDENTIFIER ::= {id-at 0}
+
+id-at-aliasedEntryName OBJECT IDENTIFIER ::= {id-at 1}
+
+-- matching rules
+id-mr-objectIdentifierMatch OBJECT IDENTIFIER ::= {id-mr 0}
+
+id-mr-distinguishedNameMatch OBJECT IDENTIFIER ::= {id-mr 1}
+
+-- operational attributes
+id-oa-excludeAllCollectiveAttributes OBJECT IDENTIFIER ::=
+ {id-oa 0}
+
+id-oa-createTimestamp OBJECT IDENTIFIER ::= {id-oa 1}
+
+id-oa-modifyTimestamp OBJECT IDENTIFIER ::= {id-oa 2}
+
+id-oa-creatorsName OBJECT IDENTIFIER ::= {id-oa 3}
+
+id-oa-modifiersName OBJECT IDENTIFIER ::= {id-oa 4}
+
+id-oa-administrativeRole OBJECT IDENTIFIER ::= {id-oa 5}
+
+id-oa-subtreeSpecification OBJECT IDENTIFIER ::= {id-oa 6}
+
+id-oa-collectiveExclusions OBJECT IDENTIFIER ::= {id-oa 7}
+
+id-oa-subschemaTimestamp OBJECT IDENTIFIER ::= {id-oa 8}
+
+id-oa-hasSubordinates OBJECT IDENTIFIER ::= {id-oa 9}
+
+id-oa-subschemaSubentryList OBJECT IDENTIFIER ::= {id-oa 10}
+
+id-oa-accessControlSubentryList OBJECT IDENTIFIER ::= {id-oa 11}
+
+id-oa-collectiveAttributeSubentryList OBJECT IDENTIFIER ::= {id-oa 12}
+
+id-oa-contextDefaultSubentryList OBJECT IDENTIFIER ::= {id-oa 13}
+
+id-oa-contextAssertionDefault OBJECT IDENTIFIER ::= {id-oa 14}
+
+id-oa-serviceAdminSubentryList OBJECT IDENTIFIER ::= {id-oa 15}
+
+id-oa-searchRules OBJECT IDENTIFIER ::= {id-oa 16}
+
+id-oa-hierarchyLevel OBJECT IDENTIFIER ::= {id-oa 17}
+
+id-oa-hierarchyBelow OBJECT IDENTIFIER ::= {id-oa 18}
+
+id-oa-hierarchyParent OBJECT IDENTIFIER ::= {id-oa 19}
+
+id-oa-hierarchyTop OBJECT IDENTIFIER ::= {id-oa 20}
+
+-- subentry classes
+id-sc-subentry OBJECT IDENTIFIER ::= {id-sc 0}
+
+id-sc-accessControlSubentry OBJECT IDENTIFIER ::= {id-sc 1}
+
+id-sc-collectiveAttributeSubentry OBJECT IDENTIFIER ::= {id-sc 2}
+
+id-sc-contextAssertionSubentry OBJECT IDENTIFIER ::= {id-sc 3}
+
+id-sc-serviceAdminSubentry OBJECT IDENTIFIER ::= {id-sc 4}
+
+-- Name forms
+id-nf-subentryNameForm OBJECT IDENTIFIER ::= {id-nf 16}
+
+-- administrative roles
+id-ar-autonomousArea OBJECT IDENTIFIER ::= {id-ar 1}
+
+id-ar-accessControlSpecificArea OBJECT IDENTIFIER ::= {id-ar 2}
+
+id-ar-accessControlInnerArea OBJECT IDENTIFIER ::= {id-ar 3}
+
+id-ar-subschemaAdminSpecificArea OBJECT IDENTIFIER ::= {id-ar 4}
+
+id-ar-collectiveAttributeSpecificArea OBJECT IDENTIFIER ::= {id-ar 5}
+
+id-ar-collectiveAttributeInnerArea OBJECT IDENTIFIER ::= {id-ar 6}
+
+id-ar-contextDefaultSpecificArea OBJECT IDENTIFIER ::= {id-ar 7}
+
+id-ar-serviceSpecificArea OBJECT IDENTIFIER ::= {id-ar 8}
+
+END -- InformationFramework
diff --git a/lib/public_key/asn1/Makefile b/lib/public_key/asn1/Makefile
index d48f48a5d5..f765906b3b 100644
--- a/lib/public_key/asn1/Makefile
+++ b/lib/public_key/asn1/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2008-2011. All Rights Reserved.
+# Copyright Ericsson AB 2008-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -40,7 +40,8 @@ RELSYSDIR = $(RELEASE_PATH)/lib/public_key-$(VSN)
ASN_TOP = OTP-PUB-KEY PKCS-FRAME
ASN_MODULES = PKIX1Explicit88 PKIX1Implicit88 PKIX1Algorithms88 \
- PKIXAttributeCertificate PKCS-1 PKCS-3 PKCS-8 PKCS5v2-0 OTP-PKIX
+ PKIXAttributeCertificate PKCS-1 PKCS-3 PKCS-7 PKCS-8 PKCS-10 PKCS5v2-0 OTP-PKIX \
+ InformationFramework
ASN_ASNS = $(ASN_MODULES:%=%.asn1)
ASN_ERLS = $(ASN_TOP:%=%.erl)
ASN_HRLS = $(ASN_TOP:%=%.hrl)
@@ -65,7 +66,7 @@ EBIN = ../ebin
EXTRA_ERLC_FLAGS =
ERL_COMPILE_FLAGS += $(EXTRA_ERLC_FLAGS)
-ASN_FLAGS = -bber_bin +der +compact_bit_string +optimize +noobj +asn1config +inline +nif
+ASN_FLAGS = -bber +der +compact_bit_string +noobj +asn1config +inline
# ----------------------------------------------------
# Targets
@@ -80,10 +81,10 @@ clean:
docs:
%.erl %.hrl: %.set.asn
- erlc $(ASN_FLAGS) $<
+ $(asn_verbose)erlc $(ASN_FLAGS) $<
$(INCLUDE)/%.hrl: %.hrl
- cp -p $< $@
+ $(gen_verbose)cp -p $< $@
# ----------------------------------------------------
# Release Target
@@ -112,9 +113,12 @@ OTP-PUB-KEY.asn1db: PKIX1Algorithms88.asn1 \
PKIXAttributeCertificate.asn1 \
PKCS-1.asn1\
PKCS-3.asn1\
+ PKCS-7.asn1\
+ PKCS-10.asn1\
+ InformationFramework.asn1\
OTP-PKIX.asn1
$(EBIN)/PKCS-FRAME.beam: PKCS-FRAME.erl PKCS-FRAME.hrl
-PKCS-FRAME.erl PKCS-FRAME.hrl: PKCS-FRAME.asn1db
-PKCS-FRAME.asn1db: PKCS-8.asn1\
- PKCS5v2-0.asn1 \ No newline at end of file
+PKCS-FRAME.erl PKCS-FRAME.hrl: PKCS-FRAME.asn1db
+PKCS-FRAME.asn1db: PKCS5v2-0.asn1\
+ PKCS-8.asn1\
diff --git a/lib/public_key/asn1/OTP-PKIX.asn1 b/lib/public_key/asn1/OTP-PKIX.asn1
index fbf531df40..4f20208bce 100644
--- a/lib/public_key/asn1/OTP-PKIX.asn1
+++ b/lib/public_key/asn1/OTP-PKIX.asn1
@@ -119,6 +119,7 @@ IMPORTS
md2WithRSAEncryption,
md5WithRSAEncryption,
sha1WithRSAEncryption,
+ sha224WithRSAEncryption,
sha256WithRSAEncryption,
sha384WithRSAEncryption,
sha512WithRSAEncryption
@@ -225,7 +226,17 @@ dnQualifier ATTRIBUTE-TYPE-AND-VALUE-CLASS ::= {
countryName ATTRIBUTE-TYPE-AND-VALUE-CLASS ::= {
ID id-at-countryName
- TYPE X520countryName }
+ TYPE X520countryName } -- this is currently not used when decoding
+ -- The decoding and mapping between ID and Type is done in the code
+ -- in module publickey_cert_records via the function attribute_type
+ -- To be more forgiving and compatible with other SSL implementations
+ -- regarding how to handle and sometimes accept incorrect certificates
+ -- we define and use the type below instead of X520countryName
+
+ OTP-X520countryname ::= CHOICE {
+ printableString PrintableString (SIZE (2)),
+ utf8String UTF8String (SIZE (2))
+}
serialNumber ATTRIBUTE-TYPE-AND-VALUE-CLASS ::= {
ID id-at-serialNumber
@@ -307,6 +318,7 @@ PublicKeyAlgorithm ::= SEQUENCE {
SupportedSignatureAlgorithms SIGNATURE-ALGORITHM-CLASS ::= {
dsa-with-sha1 | md2-with-rsa-encryption |
md5-with-rsa-encryption | sha1-with-rsa-encryption |
+ sha224-with-rsa-encryption |
sha256-with-rsa-encryption |
sha384-with-rsa-encryption |
sha512-with-rsa-encryption |
@@ -355,6 +367,10 @@ SupportedPublicKeyAlgorithms PUBLIC-KEY-ALGORITHM-CLASS ::= {
ID sha1WithRSAEncryption
TYPE NULL }
+ sha224-with-rsa-encryption SIGNATURE-ALGORITHM-CLASS ::= {
+ ID sha224WithRSAEncryption
+ TYPE NULL }
+
sha256-with-rsa-encryption SIGNATURE-ALGORITHM-CLASS ::= {
ID sha256WithRSAEncryption
TYPE NULL }
diff --git a/lib/public_key/asn1/OTP-PUB-KEY.asn1config b/lib/public_key/asn1/OTP-PUB-KEY.asn1config
index 86f4c54748..9ca30564af 100644
--- a/lib/public_key/asn1/OTP-PUB-KEY.asn1config
+++ b/lib/public_key/asn1/OTP-PUB-KEY.asn1config
@@ -1,2 +1,3 @@
{exclusive_decode,{'OTP-PUB-KEY',
- [{decode_TBSCert_exclusive,['Certificate',[{tbsCertificate,undecoded}]]}]}}.
+ [{decode_TBSCert_exclusive,['Certificate',[{tbsCertificate,undecoded}]]},
+ {decode_TBSCertList_exclusive,['CertificateList',[{tbsCertList,undecoded}]]}]}}.
diff --git a/lib/public_key/asn1/OTP-PUB-KEY.set.asn b/lib/public_key/asn1/OTP-PUB-KEY.set.asn
index 5c76d13115..f8fb318c93 100644
--- a/lib/public_key/asn1/OTP-PUB-KEY.set.asn
+++ b/lib/public_key/asn1/OTP-PUB-KEY.set.asn
@@ -6,3 +6,5 @@ PKIX1Algorithms88.asn1
PKCS-1.asn1
PKCS-3.asn1
DSS.asn1
+PKCS-7.asn1
+PKCS-10.asn1
diff --git a/lib/public_key/asn1/PKCS-1.asn1 b/lib/public_key/asn1/PKCS-1.asn1
index b06f5efa9d..c83289e779 100644
--- a/lib/public_key/asn1/PKCS-1.asn1
+++ b/lib/public_key/asn1/PKCS-1.asn1
@@ -33,6 +33,9 @@ sha1WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 5 }
sha256WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 11 }
sha384WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 12 }
sha512WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 13 }
+sha224WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 14 }
+
+
id-sha1 OBJECT IDENTIFIER ::= {
iso(1) identified-organization(3) oiw(14) secsig(3)
diff --git a/lib/public_key/asn1/PKCS-10.asn1 b/lib/public_key/asn1/PKCS-10.asn1
new file mode 100644
index 0000000000..333104d230
--- /dev/null
+++ b/lib/public_key/asn1/PKCS-10.asn1
@@ -0,0 +1,70 @@
+PKCS-10 {iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1)
+pkcs-10(10) modules(1) pkcs-10(1)}
+
+-- $Revision: 1.3 $ --
+
+DEFINITIONS IMPLICIT TAGS ::=
+
+BEGIN
+
+-- EXPORTS All --
+-- All types and values defined in this module are exported for use
+-- in other ASN.1 modules.
+
+IMPORTS
+
+--informationFramework, authenticationFramework
+-- FROM UsefulDefinitions {joint-iso-itu-t(2) ds(5) module(1)
+-- usefulDefinitions(0) 3}
+
+ ATTRIBUTE
+ FROM InformationFramework informationFramework
+
+ Name
+ FROM PKIX1Explicit88 --InformationFramework informationFramework
+
+ ALGORITHM
+ FROM PKCS-7; --AuthenticationFramework authenticationFramework;
+
+-- Certificate requests
+
+CertificationRequestInfo ::= SEQUENCE {
+ version INTEGER { v1(0) } (v1,...),
+ subject Name,
+ subjectPKInfo SubjectPublicKeyInfo-PKCS-10{{ PKInfoAlgorithms }},
+ attributes [0] Attributes{{ CRIAttributes }}
+}
+
+SubjectPublicKeyInfo-PKCS-10 {ALGORITHM: IOSet} ::= SEQUENCE {
+ algorithm AlgorithmIdentifierPKCS-10{{IOSet}},
+ subjectPublicKey BIT STRING
+}
+
+PKInfoAlgorithms ALGORITHM ::= {
+ ... -- add any locally defined algorithms here -- }
+
+Attributes { ATTRIBUTE:IOSet } ::= SET OF AttributePKCS-10{{ IOSet }}
+
+CRIAttributes ATTRIBUTE ::= {
+... -- add any locally defined attributes here -- }
+
+AttributePKCS-10 { ATTRIBUTE:IOSet } ::= SEQUENCE {
+ type ATTRIBUTE.&id({IOSet}),
+ values SET SIZE(1..MAX) OF ATTRIBUTE.&Type({IOSet}{@type})
+}
+
+CertificationRequest ::= SEQUENCE {
+ certificationRequestInfo CertificationRequestInfo,
+ signatureAlgorithm AlgorithmIdentifierPKCS-10{{ SignatureAlgorithms }},
+ signature BIT STRING
+}
+
+AlgorithmIdentifierPKCS-10 {ALGORITHM:IOSet } ::= SEQUENCE {
+ algorithm ALGORITHM.&id({IOSet}),
+ parameters ALGORITHM.&Type({IOSet}{@algorithm}) OPTIONAL
+}
+
+SignatureAlgorithms ALGORITHM ::= {
+ ... -- add any locally defined algorithms here -- }
+
+END
diff --git a/lib/public_key/asn1/PKCS-7.asn1 b/lib/public_key/asn1/PKCS-7.asn1
new file mode 100644
index 0000000000..a6dfd57d80
--- /dev/null
+++ b/lib/public_key/asn1/PKCS-7.asn1
@@ -0,0 +1,387 @@
+PKCS-7 {iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs-7(7)
+ modules(0) pkcs-7(1)}
+
+DEFINITIONS EXPLICIT TAGS ::=
+BEGIN
+
+--
+-- 3. Definitions
+--
+
+-- EXPORTS All;
+
+IMPORTS
+
+informationFramework, authenticationFramework
+ FROM UsefulDefinitions {joint-iso-itu-t ds(5) module(1)
+ usefulDefinitions(0) 3}
+
+ ATTRIBUTE
+ FROM InformationFramework informationFramework
+
+ Name, Certificate, CertificateSerialNumber,
+ CertificateList, Time
+ FROM PKIX1Explicit88; -- AuthenticationFramework authenticationFramework;
+
+-- contentType, messageDigest, signingTime
+-- , counterSignature
+-- FROM PKCS-9 {iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1)
+-- pkcs-9(9) modules(0) pkcs-9(1)};
+--
+-- 6. Useful types
+--
+
+-- inlined from AuthenticationFramework
+
+ALGORITHM ::= CLASS {&Type OPTIONAL,
+ &id OBJECT IDENTIFIER UNIQUE
+}WITH SYNTAX {[&Type]
+ IDENTIFIED BY &id
+}
+
+-- inlined from PKCS-9
+
+pkcs-9 OBJECT IDENTIFIER ::= {iso(1) member-body(2) us(840)
+ rsadsi(113549) pkcs(1) 9}
+
+contentType ATTRIBUTE ::= {
+ WITH SYNTAX ContentType
+-- EQUALITY MATCHING RULE objectIdentifierMatch
+ SINGLE VALUE TRUE
+ ID pkcs-9-at-contentType
+}
+
+pkcs-9-at-contentType OBJECT IDENTIFIER ::= {pkcs-9 3}
+pkcs-9-at-messageDigest OBJECT IDENTIFIER ::= {pkcs-9 4}
+pkcs-9-at-signingTime OBJECT IDENTIFIER ::= {pkcs-9 5}
+pkcs-9-at-counterSignature OBJECT IDENTIFIER ::= {pkcs-9 6}
+
+counterSignature ATTRIBUTE ::= {
+ WITH SYNTAX SignerInfo
+ ID pkcs-9-at-counterSignature
+}
+messageDigest ATTRIBUTE ::= {
+ WITH SYNTAX MessageDigest
+-- EQUALITY MATCHING RULE octetStringMatch
+ SINGLE VALUE TRUE
+ ID pkcs-9-at-messageDigest
+}
+
+MessageDigest ::= OCTET STRING
+
+signingTime ATTRIBUTE ::= {
+ WITH SYNTAX SigningTime
+-- EQUALITY MATCHING RULE signingTimeMatch
+ SINGLE VALUE TRUE
+ ID pkcs-9-at-signingTime
+}
+
+SigningTime ::= Time -- imported from ISO/IEC 9594-8
+
+
+-- Also defined in X.509
+-- Redeclared here as a parameterized type
+AlgorithmIdentifierPKSC-7 {ALGORITHM:IOSet} ::= SEQUENCE {
+ algorithm ALGORITHM.&id({IOSet}),
+ parameters ALGORITHM.&Type({IOSet}{@algorithm}) OPTIONAL
+}
+
+-- Also defined in X.501
+-- Redeclared here as a parameterized type
+AttributePKCS-7 { ATTRIBUTE:IOSet } ::= SEQUENCE {
+ type ATTRIBUTE.&id({IOSet}),
+ values SET SIZE (1..MAX) OF ATTRIBUTE.&Type({IOSet}{@type})
+}
+
+CertificateRevocationLists ::=
+ SET OF CertificateList
+
+Certificates ::=
+ SEQUENCE OF Certificate
+
+CRLSequence ::=
+ SEQUENCE OF CertificateList
+
+ContentEncryptionAlgorithmIdentifier ::=
+ AlgorithmIdentifierPKSC-7 {{ContentEncryptionAlgorithms}}
+
+ContentEncryptionAlgorithms ALGORITHM ::= {
+ ... -- add any application-specific algorithms here
+}
+
+DigestAlgorithmIdentifier ::=
+ AlgorithmIdentifierPKSC-7 {{DigestAlgorithms}}
+
+DigestAlgorithms ALGORITHM ::= {
+ ... -- add any application-specific algorithms here
+}
+
+DigestEncryptionAlgorithmIdentifier ::=
+ AlgorithmIdentifierPKSC-7 {{DigestEncryptionAlgorithms}}
+
+DigestEncryptionAlgorithms ALGORITHM ::= {
+ ... -- add any application-specific algorithms here
+}
+
+ExtendedCertificateOrCertificate ::= CHOICE {
+ certificate Certificate, -- X.509
+ extendedCertificate [0] IMPLICIT ExtendedCertificate -- PKCS#6
+}
+
+ExtendedCertificate ::= Certificate -- cheating
+
+ExtendedCertificatesAndCertificates ::=
+ SET OF ExtendedCertificateOrCertificate
+
+IssuerAndSerialNumber ::= SEQUENCE {
+ issuer Name,
+ serialNumber CertificateSerialNumber
+}
+
+KeyEncryptionAlgorithmIdentifier ::=
+ AlgorithmIdentifierPKSC-7 {{KeyEncryptionAlgorithms}}
+
+KeyEncryptionAlgorithms ALGORITHM ::= {
+ ... -- add any application-specific algorithms here
+}
+
+--
+-- 7. General syntax
+--
+
+ContentInfo ::= SEQUENCE {
+-- contentType ContentType,
+ contentType CONTENTS.&id({Contents}),
+ content [0] EXPLICIT CONTENTS.&Type({Contents}{@contentType})
+OPTIONAL
+}
+
+CONTENTS ::= TYPE-IDENTIFIER
+
+Contents CONTENTS ::= {
+ {Data IDENTIFIED BY data} |
+ {SignedData IDENTIFIED BY signedData} |
+ {EnvelopedData IDENTIFIED BY envelopedData} |
+ {SignedAndEnvelopedData IDENTIFIED BY signedAndEnvelopedData} |
+ {DigestedData IDENTIFIED BY digestedData} |
+ {EncryptedData IDENTIFIED BY encryptedData},
+ ... -- add any application-specific types/contents here
+}
+
+ContentType ::= CONTENTS.&id({Contents})
+
+--
+-- 8. Data content type
+--
+
+Data ::= OCTET STRING
+
+--
+-- 9. Signed-data content type
+--
+
+SignedData ::= SEQUENCE {
+-- version INTEGER {sdVer1(1), sdVer2(2)} (sdVer1 | sdVer2),
+ version INTEGER {sdVer1(1), sdVer2(2)},
+ digestAlgorithms
+ DigestAlgorithmIdentifiers,
+ contentInfo ContentInfo,
+ certificates CHOICE {
+ certSet [0] IMPLICIT ExtendedCertificatesAndCertificates,
+ certSequence [2] IMPLICIT Certificates
+ } OPTIONAL,
+ crls CHOICE {
+ crlSet [1] IMPLICIT CertificateRevocationLists,
+ crlSequence [3] IMPLICIT CRLSequence
+ } OPTIONAL,
+ signerInfos SignerInfos
+} (WITH COMPONENTS { ..., version (sdVer1),
+ digestAlgorithms (WITH COMPONENTS { ..., daSet PRESENT }),
+ certificates (WITH COMPONENTS { ..., certSequence ABSENT }),
+ crls (WITH COMPONENTS { ..., crlSequence ABSENT }),
+ signerInfos (WITH COMPONENTS { ..., siSet PRESENT })
+ } |
+ WITH COMPONENTS { ..., version (sdVer2),
+ digestAlgorithms (WITH COMPONENTS { ..., daSequence PRESENT }),
+ certificates (WITH COMPONENTS { ..., certSet ABSENT }),
+ crls (WITH COMPONENTS { ..., crlSet ABSENT }),
+ signerInfos (WITH COMPONENTS { ..., siSequence PRESENT })
+})
+
+SignerInfos ::= CHOICE {
+ siSet SET OF SignerInfo,
+ siSequence SEQUENCE OF SignerInfo
+}
+
+DigestAlgorithmIdentifiers ::= CHOICE {
+ daSet SET OF DigestAlgorithmIdentifier,
+ daSequence SEQUENCE OF DigestAlgorithmIdentifier
+}
+
+SignerInfo ::= SEQUENCE {
+-- version INTEGER {siVer1(1), siVer2(2)} (siVer1 | siVer2),
+ version INTEGER {siVer1(1), siVer2(2)},
+ issuerAndSerialNumber
+ IssuerAndSerialNumber,
+ digestAlgorithm DigestAlgorithmIdentifier,
+ authenticatedAttributes CHOICE {
+ aaSet [0] IMPLICIT SET OF AttributePKCS-7 {{Authenticated}},
+ aaSequence [2] EXPLICIT SEQUENCE OF AttributePKCS-7 {{Authenticated}}
+ -- Explicit because easier to compute digest on sequence of attributes and then reuse
+ -- encoded sequence in aaSequence.
+ } OPTIONAL,
+ digestEncryptionAlgorithm
+ DigestEncryptionAlgorithmIdentifier,
+ encryptedDigest EncryptedDigest,
+ unauthenticatedAttributes CHOICE {
+ uaSet [1] IMPLICIT SET OF AttributePKCS-7 {{Unauthenticated}},
+ uaSequence [3] IMPLICIT SEQUENCE OF AttributePKCS-7 {{Unauthenticated}}
+ } OPTIONAL
+} (WITH COMPONENTS { ..., version (siVer1),
+ authenticatedAttributes (WITH COMPONENTS { ..., aaSequence ABSENT }),
+ unauthenticatedAttributes (WITH COMPONENTS { ..., uaSequence ABSENT })
+} | WITH COMPONENTS { ..., version (siVer2),
+ authenticatedAttributes (WITH COMPONENTS { ..., aaSet ABSENT }),
+ unauthenticatedAttributes (WITH COMPONENTS { ..., uaSet ABSENT })
+})
+
+Authenticated ATTRIBUTE ::= {
+ contentType |
+ messageDigest,
+ ..., -- add application-specific attributes here
+ signingTime
+}
+
+Unauthenticated ATTRIBUTE ::= {
+ contentType |
+ messageDigest,
+ ..., -- add application-specific attributes here
+ counterSignature
+-- ..., add application-specific attributes here
+-- counterSignature
+}
+
+EncryptedDigest ::= OCTET STRING
+
+DigestInfo ::= SEQUENCE {
+ digestAlgorithm DigestAlgorithmIdentifier,
+ digest Digest
+}
+
+Digest ::= OCTET STRING
+
+--
+-- 10. Enveloped-data content type
+--
+
+EnvelopedData ::= SEQUENCE {
+-- version INTEGER {edVer0(0), edVer1(1)} (edVer0 | edVer1),
+ version INTEGER {edVer0(0), edVer1(1)},
+ recipientInfos RecipientInfos,
+ encryptedContentInfo
+ EncryptedContentInfo
+} (WITH COMPONENTS { ..., version (edVer0),
+ recipientInfos (WITH COMPONENTS { ..., riSet PRESENT })
+} | WITH COMPONENTS { ..., version (edVer1),
+ recipientInfos (WITH COMPONENTS { ..., riSequence PRESENT })
+})
+
+RecipientInfos ::= CHOICE {
+ riSet SET OF RecipientInfo,
+ riSequence SEQUENCE OF RecipientInfo
+}
+
+EncryptedContentInfo ::= SEQUENCE {
+ contentType ContentType,
+ contentEncryptionAlgorithm
+ ContentEncryptionAlgorithmIdentifier,
+ encryptedContent
+ [0] IMPLICIT EncryptedContent OPTIONAL
+}
+
+EncryptedContent ::= OCTET STRING
+
+RecipientInfo ::= SEQUENCE {
+-- version INTEGER {riVer0(0)} (riVer0),
+ version INTEGER {riVer0(0)},
+ issuerAndSerialNumber
+ IssuerAndSerialNumber,
+ keyEncryptionAlgorithm
+ KeyEncryptionAlgorithmIdentifier,
+ encryptedKey EncryptedKey
+}
+
+EncryptedKey ::= OCTET STRING
+
+--
+-- 11. Signed-and-enveloped-data content type
+--
+
+SignedAndEnvelopedData ::= SEQUENCE {
+-- version INTEGER {seVer1(1), seVer2(2)} (seVer1 | seVer2),
+ version INTEGER {seVer1(1), seVer2(2)},
+ recipientInfos RecipientInfos,
+ digestAlgorithms
+ DigestAlgorithmIdentifiers,
+ encryptedContentInfo
+ EncryptedContentInfo,
+ certificates CHOICE {
+ certSet [0] IMPLICIT ExtendedCertificatesAndCertificates,
+ certSequence [2] IMPLICIT Certificates
+ } OPTIONAL,
+ crls CHOICE {
+ crlSet [1] IMPLICIT CertificateRevocationLists,
+ crlSequence [3] IMPLICIT CRLSequence
+ } OPTIONAL,
+ signerInfos SignerInfos
+} (WITH COMPONENTS { ..., version (seVer1),
+ recipientInfos (WITH COMPONENTS { ..., riSet PRESENT }),
+ digestAlgorithms (WITH COMPONENTS { ..., daSet PRESENT }),
+ certificates (WITH COMPONENTS { ..., certSequence ABSENT }),
+ crls (WITH COMPONENTS { ..., crlSequence ABSENT }),
+ signerInfos (WITH COMPONENTS { ..., siSet PRESENT })
+} |
+ WITH COMPONENTS { ..., version (seVer2),
+ recipientInfos (WITH COMPONENTS { ..., riSequence PRESENT }),
+ digestAlgorithms (WITH COMPONENTS { ..., daSequence PRESENT }),
+ certificates (WITH COMPONENTS { ..., certSet ABSENT }),
+ crls (WITH COMPONENTS { ..., crlSet ABSENT }),
+ signerInfos (WITH COMPONENTS { ..., siSequence PRESENT })
+})
+
+--
+-- 12. Digested-data content type
+--pbeWithSHAAnd3-KeyTripleDES-CBC
+
+DigestedData ::= SEQUENCE {
+-- version INTEGER {ddVer0(0)} (ddVer0),
+ version INTEGER {ddVer0(0)},
+ digestAlgorithm DigestAlgorithmIdentifier,
+ contentInfo ContentInfo,
+ digest Digest
+}
+
+--
+-- 13. Encrypted-data content type
+--
+
+EncryptedData ::= SEQUENCE {
+-- version INTEGER {edVer0(0)} (edVer0),
+ version INTEGER {edVer0(0)},
+ encryptedContentInfo EncryptedContentInfo
+}
+
+--
+-- 14. Object Identifiers
+--
+
+pkcs-7 OBJECT IDENTIFIER ::=
+ { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 7 }
+data OBJECT IDENTIFIER ::= { pkcs-7 1 }
+signedData OBJECT IDENTIFIER ::= { pkcs-7 2 }
+envelopedData OBJECT IDENTIFIER ::= { pkcs-7 3 }
+signedAndEnvelopedData OBJECT IDENTIFIER ::= { pkcs-7 4 }
+digestedData OBJECT IDENTIFIER ::= { pkcs-7 5 }
+encryptedData OBJECT IDENTIFIER ::= { pkcs-7 6 }
+
+END
diff --git a/lib/public_key/asn1/PKIX1Explicit88.asn1 b/lib/public_key/asn1/PKIX1Explicit88.asn1
index 03e9da3e05..91758d7269 100644
--- a/lib/public_key/asn1/PKIX1Explicit88.asn1
+++ b/lib/public_key/asn1/PKIX1Explicit88.asn1
@@ -206,13 +206,12 @@ 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))
+-- Legacy attributes
+
-- naming data types --
Name ::= CHOICE { -- only one possibility for now --
diff --git a/lib/public_key/asn1/SelectedAttributeTypes.asn1 b/lib/public_key/asn1/SelectedAttributeTypes.asn1
new file mode 100644
index 0000000000..3ef7077370
--- /dev/null
+++ b/lib/public_key/asn1/SelectedAttributeTypes.asn1
@@ -0,0 +1,1575 @@
+SelectedAttributeTypes {joint-iso-itu-t ds(5) module(1)
+ selectedAttributeTypes(5) 6} DEFINITIONS ::=
+BEGIN
+
+-- EXPORTS All
+-- The types and values defined in this module are exported for use in the other ASN.1 modules contained
+-- within the Directory Specifications, and for the use of other applications which will use them to access
+-- Directory services. Other applications may use them for their own purposes, but this will not constrain
+-- extensions and modifications needed to maintain or improve the Directory service.
+IMPORTS
+ -- from ITU-T Rec. X.501 | ISO/IEC 9594-2
+ directoryAbstractService, id-at, id-avc, id-cat, id-mr, id-not, id-pr,
+ informationFramework, serviceAdministration
+ FROM UsefulDefinitions {joint-iso-itu-t ds(5) module(1)
+ usefulDefinitions(0) 6}
+ Attribute{}, ATTRIBUTE, AttributeType, AttributeValueAssertion, CONTEXT,
+ ContextAssertion, DistinguishedName, distinguishedNameMatch,
+ MAPPING-BASED-MATCHING{}, MATCHING-RULE, OBJECT-CLASS,
+ objectIdentifierMatch, SupportedAttributes
+ FROM InformationFramework informationFramework
+ AttributeCombination, ContextCombination, MRMapping
+ FROM ServiceAdministration serviceAdministration
+ -- from ITU-T Rec. X.511 | ISO/IEC 9594-3
+ FilterItem, HierarchySelections, SearchControlOptions, ServiceControlOptions
+ FROM DirectoryAbstractService directoryAbstractService
+ -- from ITU-T Rec. X.411 | ISO/IEC 10021-4
+ G3FacsimileNonBasicParameters
+ FROM MTSAbstractService {joint-iso-itu-t mhs(6) mts(3) modules(0)
+ mts-abstract-service(1) version-1999(1)};
+
+/*from IETF RFC 3727
+
+The following import is provided for information only (see 7.2.16), it is not referenced by any ASN.1 construct within these Directory Specifications. Note that the ASN.1 module in RFC 3727 imports from the InformationFramework module of edition 4 of ITU-T Rec. X.501 | ISO/IEC 9594-2. A specification importing from both these Directory Specifications and from RFC 3727 should take corrective actions, e.g., by making a copy of the ASN.1 module of
+RFC 3727 and then update the IMPORT statement.
+
+ allComponentsMatch, componentFilterMatch, directoryComponentsMatch, presentMatch, rdnMatch
+ FROM ComponentMatching {iso(1) 2 36 79672281 xed(3) module (0)
+ component-matching(4)} */
+-- Directory string type
+UnboundedDirectoryString ::= CHOICE {
+ teletexString TeletexString(SIZE (1..MAX)),
+ printableString PrintableString(SIZE (1..MAX)),
+ bmpString BMPString(SIZE (1..MAX)),
+ universalString UniversalString(SIZE (1..MAX)),
+ uTF8String UTF8String(SIZE (1..MAX))
+}
+
+DirectoryString{INTEGER:maxSize} ::= CHOICE {
+ teletexString TeletexString(SIZE (1..maxSize)),
+ printableString PrintableString(SIZE (1..maxSize)),
+ bmpString BMPString(SIZE (1..maxSize)),
+ universalString UniversalString(SIZE (1..maxSize)),
+ uTF8String UTF8String(SIZE (1..maxSize))
+}
+
+-- Attribute types
+knowledgeInformation ATTRIBUTE ::= {
+ WITH SYNTAX UnboundedDirectoryString
+ EQUALITY MATCHING RULE caseIgnoreMatch
+ ID id-at-knowledgeInformation
+}
+
+name ATTRIBUTE ::= {
+ WITH SYNTAX UnboundedDirectoryString
+ EQUALITY MATCHING RULE caseIgnoreMatch
+ SUBSTRINGS MATCHING RULE caseIgnoreSubstringsMatch
+ ID id-at-name
+}
+
+commonName ATTRIBUTE ::= {
+ SUBTYPE OF name
+ WITH SYNTAX UnboundedDirectoryString
+ ID id-at-commonName
+}
+
+surname ATTRIBUTE ::= {
+ SUBTYPE OF name
+ WITH SYNTAX UnboundedDirectoryString
+ ID id-at-surname
+}
+
+givenName ATTRIBUTE ::= {
+ SUBTYPE OF name
+ WITH SYNTAX UnboundedDirectoryString
+ ID id-at-givenName
+}
+
+initials ATTRIBUTE ::= {
+ SUBTYPE OF name
+ WITH SYNTAX UnboundedDirectoryString
+ ID id-at-initials
+}
+
+generationQualifier ATTRIBUTE ::= {
+ SUBTYPE OF name
+ WITH SYNTAX UnboundedDirectoryString
+ ID id-at-generationQualifier
+}
+
+uniqueIdentifier ATTRIBUTE ::= {
+ WITH SYNTAX UniqueIdentifier
+ EQUALITY MATCHING RULE bitStringMatch
+ ID id-at-uniqueIdentifier
+}
+
+UniqueIdentifier ::= BIT STRING
+
+dnQualifier ATTRIBUTE ::= {
+ WITH SYNTAX PrintableString
+ EQUALITY MATCHING RULE caseIgnoreMatch
+ ORDERING MATCHING RULE caseIgnoreOrderingMatch
+ SUBSTRINGS MATCHING RULE caseIgnoreSubstringsMatch
+ ID id-at-dnQualifier
+}
+
+serialNumber ATTRIBUTE ::= {
+ WITH SYNTAX PrintableString(SIZE (1..MAX))
+ EQUALITY MATCHING RULE caseIgnoreMatch
+ SUBSTRINGS MATCHING RULE caseIgnoreSubstringsMatch
+ ID id-at-serialNumber
+}
+
+pseudonym ATTRIBUTE ::= {
+ SUBTYPE OF name
+ WITH SYNTAX UnboundedDirectoryString
+ ID id-at-pseudonym
+}
+
+uUIDPair ATTRIBUTE ::= {
+ WITH SYNTAX UUIDPair
+ EQUALITY MATCHING RULE uUIDPairMatch
+ ID id-at-uuidpair
+}
+
+UUIDPair ::= SEQUENCE {issuerUUID UUID,
+ subjectUUID UUID
+}
+
+UUID ::= OCTET STRING(SIZE (16)) -- UUID format only
+
+
+countryName ATTRIBUTE ::= {
+ SUBTYPE OF name
+ WITH SYNTAX CountryName
+ SINGLE VALUE TRUE
+ ID id-at-countryName
+}
+
+CountryName ::= PrintableString(SIZE (2)) -- ISO 3166 codes only
+
+
+localityName ATTRIBUTE ::= {
+ SUBTYPE OF name
+ WITH SYNTAX UnboundedDirectoryString
+ ID id-at-localityName
+}
+
+collectiveLocalityName ATTRIBUTE ::= {
+ SUBTYPE OF localityName
+ COLLECTIVE TRUE
+ ID id-at-collectiveLocalityName
+}
+
+stateOrProvinceName ATTRIBUTE ::= {
+ SUBTYPE OF name
+ WITH SYNTAX UnboundedDirectoryString
+ ID id-at-stateOrProvinceName
+}
+
+collectiveStateOrProvinceName ATTRIBUTE ::= {
+ SUBTYPE OF stateOrProvinceName
+ COLLECTIVE TRUE
+ ID id-at-collectiveStateOrProvinceName
+}
+
+streetAddress ATTRIBUTE ::= {
+ WITH SYNTAX UnboundedDirectoryString
+ EQUALITY MATCHING RULE caseIgnoreMatch
+ SUBSTRINGS MATCHING RULE caseIgnoreSubstringsMatch
+ ID id-at-streetAddress
+}
+
+collectiveStreetAddress ATTRIBUTE ::= {
+ SUBTYPE OF streetAddress
+ COLLECTIVE TRUE
+ ID id-at-collectiveStreetAddress
+}
+
+houseIdentifier ATTRIBUTE ::= {
+ WITH SYNTAX UnboundedDirectoryString
+ EQUALITY MATCHING RULE caseIgnoreMatch
+ SUBSTRINGS MATCHING RULE caseIgnoreSubstringsMatch
+ ID id-at-houseIdentifier
+}
+
+organizationName ATTRIBUTE ::= {
+ SUBTYPE OF name
+ WITH SYNTAX UnboundedDirectoryString
+ ID id-at-organizationName
+}
+
+collectiveOrganizationName ATTRIBUTE ::= {
+ SUBTYPE OF organizationName
+ COLLECTIVE TRUE
+ ID id-at-collectiveOrganizationName
+}
+
+organizationalUnitName ATTRIBUTE ::= {
+ SUBTYPE OF name
+ WITH SYNTAX UnboundedDirectoryString
+ ID id-at-organizationalUnitName
+}
+
+collectiveOrganizationalUnitName ATTRIBUTE ::= {
+ SUBTYPE OF organizationalUnitName
+ COLLECTIVE TRUE
+ ID id-at-collectiveOrganizationalUnitName
+}
+
+title ATTRIBUTE ::= {
+ SUBTYPE OF name
+ WITH SYNTAX UnboundedDirectoryString
+ ID id-at-title
+}
+
+description ATTRIBUTE ::= {
+ WITH SYNTAX UnboundedDirectoryString
+ EQUALITY MATCHING RULE caseIgnoreMatch
+ SUBSTRINGS MATCHING RULE caseIgnoreSubstringsMatch
+ ID id-at-description
+}
+
+searchGuide ATTRIBUTE ::= {WITH SYNTAX Guide
+ ID id-at-searchGuide
+}
+
+Guide ::= SET {
+ objectClass [0] OBJECT-CLASS.&id OPTIONAL,
+ criteria [1] Criteria
+}
+
+Criteria ::= CHOICE {
+ type [0] CriteriaItem,
+ and [1] SET OF Criteria,
+ or [2] SET OF Criteria,
+ not [3] Criteria
+}
+
+CriteriaItem ::= CHOICE {
+ equality [0] AttributeType,
+ substrings [1] AttributeType,
+ greaterOrEqual [2] AttributeType,
+ lessOrEqual [3] AttributeType,
+ approximateMatch [4] AttributeType
+}
+
+enhancedSearchGuide ATTRIBUTE ::= {
+ WITH SYNTAX EnhancedGuide
+ ID id-at-enhancedSearchGuide
+}
+
+EnhancedGuide ::= SEQUENCE {
+ objectClass [0] OBJECT-CLASS.&id,
+ criteria [1] Criteria,
+ subset
+ [2] INTEGER {baseObject(0), oneLevel(1), wholeSubtree(2)} DEFAULT oneLevel
+}
+
+businessCategory ATTRIBUTE ::= {
+ WITH SYNTAX UnboundedDirectoryString
+ EQUALITY MATCHING RULE caseIgnoreMatch
+ SUBSTRINGS MATCHING RULE caseIgnoreSubstringsMatch
+ ID id-at-businessCategory
+}
+
+postalAddress ATTRIBUTE ::= {
+ WITH SYNTAX PostalAddress
+ EQUALITY MATCHING RULE caseIgnoreListMatch
+ SUBSTRINGS MATCHING RULE caseIgnoreListSubstringsMatch
+ ID id-at-postalAddress
+}
+
+PostalAddress ::= SEQUENCE SIZE (1..MAX) OF UnboundedDirectoryString
+
+collectivePostalAddress ATTRIBUTE ::= {
+ SUBTYPE OF postalAddress
+ COLLECTIVE TRUE
+ ID id-at-collectivePostalAddress
+}
+
+postalCode ATTRIBUTE ::= {
+ WITH SYNTAX UnboundedDirectoryString
+ EQUALITY MATCHING RULE caseIgnoreMatch
+ SUBSTRINGS MATCHING RULE caseIgnoreSubstringsMatch
+ ID id-at-postalCode
+}
+
+collectivePostalCode ATTRIBUTE ::= {
+ SUBTYPE OF postalCode
+ COLLECTIVE TRUE
+ ID id-at-collectivePostalCode
+}
+
+postOfficeBox ATTRIBUTE ::= {
+ WITH SYNTAX UnboundedDirectoryString
+ EQUALITY MATCHING RULE caseIgnoreMatch
+ SUBSTRINGS MATCHING RULE caseIgnoreSubstringsMatch
+ ID id-at-postOfficeBox
+}
+
+collectivePostOfficeBox ATTRIBUTE ::= {
+ SUBTYPE OF postOfficeBox
+ COLLECTIVE TRUE
+ ID id-at-collectivePostOfficeBox
+}
+
+physicalDeliveryOfficeName ATTRIBUTE ::= {
+ WITH SYNTAX UnboundedDirectoryString
+ EQUALITY MATCHING RULE caseIgnoreMatch
+ SUBSTRINGS MATCHING RULE caseIgnoreSubstringsMatch
+ ID id-at-physicalDeliveryOfficeName
+}
+
+collectivePhysicalDeliveryOfficeName ATTRIBUTE ::= {
+ SUBTYPE OF physicalDeliveryOfficeName
+ COLLECTIVE TRUE
+ ID id-at-collectivePhysicalDeliveryOfficeName
+}
+
+telephoneNumber ATTRIBUTE ::= {
+ WITH SYNTAX TelephoneNumber
+ EQUALITY MATCHING RULE telephoneNumberMatch
+ SUBSTRINGS MATCHING RULE telephoneNumberSubstringsMatch
+ ID id-at-telephoneNumber
+}
+
+TelephoneNumber ::= PrintableString(SIZE (1..ub-telephone-number))
+
+-- String complying with ITU-T Rec. E.123 only
+ub-telephone-number INTEGER ::=
+ 32
+
+collectiveTelephoneNumber ATTRIBUTE ::= {
+ SUBTYPE OF telephoneNumber
+ COLLECTIVE TRUE
+ ID id-at-collectiveTelephoneNumber
+}
+
+telexNumber ATTRIBUTE ::= {
+ WITH SYNTAX TelexNumber
+ ID id-at-telexNumber
+}
+
+TelexNumber ::= SEQUENCE {
+ telexNumber PrintableString(SIZE (1..ub-telex-number)),
+ countryCode PrintableString(SIZE (1..ub-country-code)),
+ answerback PrintableString(SIZE (1..ub-answerback))
+}
+
+ub-telex-number INTEGER ::= 14
+
+ub-country-code INTEGER ::= 4
+
+ub-answerback INTEGER ::= 8
+
+collectiveTelexNumber ATTRIBUTE ::= {
+ SUBTYPE OF telexNumber
+ COLLECTIVE TRUE
+ ID id-at-collectiveTelexNumber
+}
+
+facsimileTelephoneNumber ATTRIBUTE ::= {
+ WITH SYNTAX FacsimileTelephoneNumber
+ EQUALITY MATCHING RULE facsimileNumberMatch
+ SUBSTRINGS MATCHING RULE facsimileNumberSubstringsMatch
+ ID id-at-facsimileTelephoneNumber
+}
+
+FacsimileTelephoneNumber ::= SEQUENCE {
+ telephoneNumber TelephoneNumber,
+ parameters G3FacsimileNonBasicParameters OPTIONAL
+}
+
+collectiveFacsimileTelephoneNumber ATTRIBUTE ::= {
+ SUBTYPE OF facsimileTelephoneNumber
+ COLLECTIVE TRUE
+ ID id-at-collectiveFacsimileTelephoneNumber
+}
+
+x121Address ATTRIBUTE ::= {
+ WITH SYNTAX X121Address
+ EQUALITY MATCHING RULE numericStringMatch
+ SUBSTRINGS MATCHING RULE numericStringSubstringsMatch
+ ID id-at-x121Address
+}
+
+X121Address ::= NumericString(SIZE (1..ub-x121-address))
+
+-- String as defined by ITU-T Rec. X.121
+ub-x121-address INTEGER ::= 15
+
+internationalISDNNumber ATTRIBUTE ::= {
+ WITH SYNTAX InternationalISDNNumber
+ EQUALITY MATCHING RULE numericStringMatch
+ SUBSTRINGS MATCHING RULE numericStringSubstringsMatch
+ ID id-at-internationalISDNNumber
+}
+
+InternationalISDNNumber ::=
+ NumericString(SIZE (1..ub-international-isdn-number))
+
+-- String complying with ITU-T Rec. E.164 only
+ub-international-isdn-number INTEGER ::=
+ 16
+
+collectiveInternationalISDNNumber ATTRIBUTE ::= {
+ SUBTYPE OF internationalISDNNumber
+ COLLECTIVE TRUE
+ ID id-at-collectiveInternationalISDNNumber
+}
+
+registeredAddress ATTRIBUTE ::= {
+ SUBTYPE OF postalAddress
+ WITH SYNTAX PostalAddress
+ ID id-at-registeredAddress
+}
+
+destinationIndicator ATTRIBUTE ::= {
+ WITH SYNTAX DestinationIndicator
+ EQUALITY MATCHING RULE caseIgnoreMatch
+ SUBSTRINGS MATCHING RULE caseIgnoreSubstringsMatch
+ ID id-at-destinationIndicator
+}
+
+DestinationIndicator ::= PrintableString(SIZE (1..MAX))
+
+-- alphabetical characters only
+communicationsService ATTRIBUTE ::= {
+ WITH SYNTAX CommunicationsService
+ EQUALITY MATCHING RULE objectIdentifierMatch
+ ID id-at-communicationsService
+}
+
+CommunicationsService ::= OBJECT IDENTIFIER
+
+communicationsNetwork ATTRIBUTE ::= {
+ WITH SYNTAX CommunicationsNetwork
+ EQUALITY MATCHING RULE objectIdentifierMatch
+ SINGLE VALUE TRUE
+ ID id-at-communicationsNetwork
+}
+
+CommunicationsNetwork ::= OBJECT IDENTIFIER
+
+preferredDeliveryMethod ATTRIBUTE ::= {
+ WITH SYNTAX PreferredDeliveryMethod
+ SINGLE VALUE TRUE
+ ID id-at-preferredDeliveryMethod
+}
+
+PreferredDeliveryMethod ::=
+ SEQUENCE OF
+ INTEGER {any-delivery-method(0), mhs-delivery(1), physical-delivery(2),
+ telex-delivery(3), teletex-delivery(4), g3-facsimile-delivery(5),
+ g4-facsimile-delivery(6), ia5-terminal-delivery(7),
+ videotex-delivery(8), telephone-delivery(9)}
+
+presentationAddress ATTRIBUTE ::= {
+ WITH SYNTAX PresentationAddress
+ EQUALITY MATCHING RULE presentationAddressMatch
+ SINGLE VALUE TRUE
+ ID id-at-presentationAddress
+}
+
+PresentationAddress ::= SEQUENCE {
+ pSelector [0] OCTET STRING OPTIONAL,
+ sSelector [1] OCTET STRING OPTIONAL,
+ tSelector [2] OCTET STRING OPTIONAL,
+ nAddresses [3] SET SIZE (1..MAX) OF OCTET STRING
+}
+
+supportedApplicationContext ATTRIBUTE ::= {
+ WITH SYNTAX OBJECT IDENTIFIER
+ EQUALITY MATCHING RULE objectIdentifierMatch
+ ID id-at-supportedApplicationContext
+}
+
+protocolInformation ATTRIBUTE ::= {
+ WITH SYNTAX ProtocolInformation
+ EQUALITY MATCHING RULE protocolInformationMatch
+ ID id-at-protocolInformation
+}
+
+ProtocolInformation ::= SEQUENCE {
+ nAddress OCTET STRING,
+ profiles SET OF OBJECT IDENTIFIER
+}
+
+distinguishedName ATTRIBUTE ::= {
+ WITH SYNTAX DistinguishedName
+ EQUALITY MATCHING RULE distinguishedNameMatch
+ ID id-at-distinguishedName
+}
+
+member ATTRIBUTE ::= {SUBTYPE OF distinguishedName
+ ID id-at-member
+}
+
+uniqueMember ATTRIBUTE ::= {
+ WITH SYNTAX NameAndOptionalUID
+ EQUALITY MATCHING RULE uniqueMemberMatch
+ ID id-at-uniqueMember
+}
+
+NameAndOptionalUID ::= SEQUENCE {
+ dn DistinguishedName,
+ uid UniqueIdentifier OPTIONAL
+}
+
+owner ATTRIBUTE ::= {SUBTYPE OF distinguishedName
+ ID id-at-owner
+}
+
+roleOccupant ATTRIBUTE ::= {
+ SUBTYPE OF distinguishedName
+ ID id-at-roleOccupant
+}
+
+seeAlso ATTRIBUTE ::= {SUBTYPE OF distinguishedName
+ ID id-at-seeAlso
+}
+
+dmdName ATTRIBUTE ::= {
+ SUBTYPE OF name
+ WITH SYNTAX UnboundedDirectoryString
+ ID id-at-dmdName
+}
+
+-- Attributes for tag-based identification
+tagOid ATTRIBUTE ::= {
+ WITH SYNTAX OBJECT IDENTIFIER
+ EQUALITY MATCHING RULE objectIdentifierMatch
+ SINGLE VALUE TRUE
+ ID id-at-tagOid
+}
+
+uiiFormat ATTRIBUTE ::= {
+ WITH SYNTAX UnboundedDirectoryString
+ SINGLE VALUE TRUE
+ ID id-at-uiiFormat
+}
+
+uiiInUrn ATTRIBUTE ::= {
+ WITH SYNTAX UTF8String
+ EQUALITY MATCHING RULE caseExactMatch
+ SINGLE VALUE TRUE
+ ID id-at-uiiInUrn
+}
+
+contentUri ATTRIBUTE ::= {
+ WITH SYNTAX UnboundedDirectoryString
+ ID id-at-contentUri
+}
+
+-- Notification attributes
+dSAProblem ATTRIBUTE ::= {
+ WITH SYNTAX OBJECT IDENTIFIER
+ EQUALITY MATCHING RULE objectIdentifierMatch
+ ID id-not-dSAProblem
+}
+
+searchServiceProblem ATTRIBUTE ::= {
+ WITH SYNTAX OBJECT IDENTIFIER
+ EQUALITY MATCHING RULE objectIdentifierMatch
+ SINGLE VALUE TRUE
+ ID id-not-searchServiceProblem
+}
+
+serviceType ATTRIBUTE ::= {
+ WITH SYNTAX OBJECT IDENTIFIER
+ EQUALITY MATCHING RULE objectIdentifierMatch
+ SINGLE VALUE TRUE
+ ID id-not-serviceType
+}
+
+attributeTypeList ATTRIBUTE ::= {
+ WITH SYNTAX OBJECT IDENTIFIER
+ EQUALITY MATCHING RULE objectIdentifierMatch
+ ID id-not-attributeTypeList
+}
+
+matchingRuleList ATTRIBUTE ::= {
+ WITH SYNTAX OBJECT IDENTIFIER
+ EQUALITY MATCHING RULE objectIdentifierMatch
+ ID id-not-matchingRuleList
+}
+
+filterItem ATTRIBUTE ::= {
+ WITH SYNTAX FilterItem
+ ID id-not-filterItem
+}
+
+attributeCombinations ATTRIBUTE ::= {
+ WITH SYNTAX AttributeCombination
+ ID id-not-attributeCombinations
+}
+
+contextTypeList ATTRIBUTE ::= {
+ WITH SYNTAX OBJECT IDENTIFIER
+ EQUALITY MATCHING RULE objectIdentifierMatch
+ ID id-not-contextTypeList
+}
+
+contextList ATTRIBUTE ::= {
+ WITH SYNTAX ContextAssertion
+ ID id-not-contextList
+}
+
+contextCombinations ATTRIBUTE ::= {
+ WITH SYNTAX ContextCombination
+ ID id-not-contextCombinations
+}
+
+hierarchySelectList ATTRIBUTE ::= {
+ WITH SYNTAX HierarchySelections
+ SINGLE VALUE TRUE
+ ID id-not-hierarchySelectList
+}
+
+searchControlOptionsList ATTRIBUTE ::= {
+ WITH SYNTAX SearchControlOptions
+ SINGLE VALUE TRUE
+ ID id-not-searchControlOptionsList
+}
+
+serviceControlOptionsList ATTRIBUTE ::= {
+ WITH SYNTAX ServiceControlOptions
+ SINGLE VALUE TRUE
+ ID id-not-serviceControlOptionsList
+}
+
+multipleMatchingLocalities ATTRIBUTE ::= {
+ WITH SYNTAX MultipleMatchingLocalities
+ ID id-not-multipleMatchingLocalities
+}
+
+MultipleMatchingLocalities ::= SEQUENCE {
+ matchingRuleUsed MATCHING-RULE.&id OPTIONAL,
+ attributeList SEQUENCE OF AttributeValueAssertion
+}
+
+proposedRelaxation ATTRIBUTE ::= {
+ WITH SYNTAX MRMappings
+ ID id-not-proposedRelaxation
+}
+
+MRMappings ::= SEQUENCE OF MRMapping
+
+appliedRelaxation ATTRIBUTE ::= {
+ WITH SYNTAX OBJECT IDENTIFIER
+ EQUALITY MATCHING RULE objectIdentifierMatch
+ ID id-not-appliedRelaxation
+}
+
+-- Matching rules
+caseExactMatch MATCHING-RULE ::= {
+ SYNTAX UnboundedDirectoryString
+ ID id-mr-caseExactMatch
+}
+
+caseIgnoreMatch MATCHING-RULE ::= {
+ SYNTAX UnboundedDirectoryString
+ ID id-mr-caseIgnoreMatch
+}
+
+caseExactOrderingMatch MATCHING-RULE ::= {
+ SYNTAX UnboundedDirectoryString
+ ID id-mr-caseExactOrderingMatch
+}
+
+caseIgnoreOrderingMatch MATCHING-RULE ::= {
+ SYNTAX UnboundedDirectoryString
+ ID id-mr-caseIgnoreOrderingMatch
+}
+
+caseExactSubstringsMatch MATCHING-RULE ::= {
+ SYNTAX SubstringAssertion -- only the PrintableString choice
+ ID id-mr-caseExactSubstringsMatch
+}
+
+caseIgnoreSubstringsMatch MATCHING-RULE ::= {
+ SYNTAX SubstringAssertion
+ ID id-mr-caseIgnoreSubstringsMatch
+}
+
+SubstringAssertion ::=
+ SEQUENCE OF
+ CHOICE {initial [0] UnboundedDirectoryString,
+ any [1] UnboundedDirectoryString,
+ final [2] UnboundedDirectoryString,
+ control Attribute{{SupportedAttributes}}
+ } -- Used to specify interpretation of the following items
+
+-- at most one initial and one final component
+numericStringMatch MATCHING-RULE ::= {
+ SYNTAX NumericString
+ ID id-mr-numericStringMatch
+}
+
+numericStringOrderingMatch MATCHING-RULE ::= {
+ SYNTAX NumericString
+ ID id-mr-numericStringOrderingMatch
+}
+
+numericStringSubstringsMatch MATCHING-RULE ::= {
+ SYNTAX SubstringAssertion
+ ID id-mr-numericStringSubstringsMatch
+}
+
+caseIgnoreListMatch MATCHING-RULE ::= {
+ SYNTAX CaseIgnoreList
+ ID id-mr-caseIgnoreListMatch
+}
+
+CaseIgnoreList ::= SEQUENCE OF UnboundedDirectoryString
+
+caseIgnoreListSubstringsMatch MATCHING-RULE ::= {
+ SYNTAX SubstringAssertion
+ ID id-mr-caseIgnoreListSubstringsMatch
+}
+
+storedPrefixMatch MATCHING-RULE ::= {
+ SYNTAX UnboundedDirectoryString
+ ID id-mr-storedPrefixMatch
+}
+
+booleanMatch MATCHING-RULE ::= {SYNTAX BOOLEAN
+ ID id-mr-booleanMatch
+}
+
+integerMatch MATCHING-RULE ::= {SYNTAX INTEGER
+ ID id-mr-integerMatch
+}
+
+integerOrderingMatch MATCHING-RULE ::= {
+ SYNTAX INTEGER
+ ID id-mr-integerOrderingMatch
+}
+
+bitStringMatch MATCHING-RULE ::= {
+ SYNTAX BIT STRING
+ ID id-mr-bitStringMatch
+}
+
+octetStringMatch MATCHING-RULE ::= {
+ SYNTAX OCTET STRING
+ ID id-mr-octetStringMatch
+}
+
+octetStringOrderingMatch MATCHING-RULE ::= {
+ SYNTAX OCTET STRING
+ ID id-mr-octetStringOrderingMatch
+}
+
+octetStringSubstringsMatch MATCHING-RULE ::= {
+ SYNTAX OctetSubstringAssertion
+ ID id-mr-octetStringSubstringsMatch
+}
+
+OctetSubstringAssertion ::=
+ SEQUENCE OF
+ CHOICE {initial [0] OCTET STRING,
+ any [1] OCTET STRING,
+ final [2] OCTET STRING}
+
+-- at most one initial and one final component
+telephoneNumberMatch MATCHING-RULE ::= {
+ SYNTAX TelephoneNumber
+ ID id-mr-telephoneNumberMatch
+}
+
+telephoneNumberSubstringsMatch MATCHING-RULE ::= {
+ SYNTAX SubstringAssertion
+ ID id-mr-telephoneNumberSubstringsMatch
+}
+
+presentationAddressMatch MATCHING-RULE ::= {
+ SYNTAX PresentationAddress
+ ID id-mr-presentationAddressMatch
+}
+
+uniqueMemberMatch MATCHING-RULE ::= {
+ SYNTAX NameAndOptionalUID
+ ID id-mr-uniqueMemberMatch
+}
+
+protocolInformationMatch MATCHING-RULE ::= {
+ SYNTAX OCTET STRING
+ ID id-mr-protocolInformationMatch
+}
+
+facsimileNumberMatch MATCHING-RULE ::= {
+ SYNTAX TelephoneNumber
+ ID id-mr-facsimileNumberMatch
+}
+
+facsimileNumberSubstringsMatch MATCHING-RULE ::= {
+ SYNTAX SubstringAssertion
+ ID id-mr-facsimileNumberSubstringsMatch
+}
+
+uUIDPairMatch MATCHING-RULE ::= {SYNTAX UUIDPair
+ ID id-mr-uuidpairmatch
+}
+
+uTCTimeMatch MATCHING-RULE ::= {SYNTAX UTCTime
+ ID id-mr-uTCTimeMatch
+}
+
+uTCTimeOrderingMatch MATCHING-RULE ::= {
+ SYNTAX UTCTime
+ ID id-mr-uTCTimeOrderingMatch
+}
+
+generalizedTimeMatch MATCHING-RULE ::= {
+ SYNTAX GeneralizedTime
+ -- as per 46.3 b) or c) of ITU-T Rec. X.680 | ISO/IEC 8824-1
+ ID id-mr-generalizedTimeMatch
+}
+
+generalizedTimeOrderingMatch MATCHING-RULE ::= {
+ SYNTAX GeneralizedTime
+ -- as per 46.3 b) or c) of ITU-T Rec. X.680 | ISO/IEC 8824-1
+ ID id-mr-generalizedTimeOrderingMatch
+}
+
+systemProposedMatch MATCHING-RULE ::= {ID id-mr-systemProposedMatch
+}
+
+integerFirstComponentMatch MATCHING-RULE ::= {
+ SYNTAX INTEGER
+ ID id-mr-integerFirstComponentMatch
+}
+
+objectIdentifierFirstComponentMatch MATCHING-RULE ::= {
+ SYNTAX OBJECT IDENTIFIER
+ ID id-mr-objectIdentifierFirstComponentMatch
+}
+
+directoryStringFirstComponentMatch MATCHING-RULE ::= {
+ SYNTAX UnboundedDirectoryString
+ ID id-mr-directoryStringFirstComponentMatch
+}
+
+wordMatch MATCHING-RULE ::= {
+ SYNTAX UnboundedDirectoryString
+ ID id-mr-wordMatch
+}
+
+keywordMatch MATCHING-RULE ::= {
+ SYNTAX UnboundedDirectoryString
+ ID id-mr-keywordMatch
+}
+
+generalWordMatch MATCHING-RULE ::= {
+ SYNTAX SubstringAssertion
+ ID id-mr-generalWordMatch
+}
+
+sequenceMatchType ATTRIBUTE ::= {
+ WITH SYNTAX SequenceMatchType
+ SINGLE VALUE TRUE
+ ID id-cat-sequenceMatchType
+} -- defaulting to sequenceExact
+
+SequenceMatchType ::= ENUMERATED {
+ sequenceExact(0), sequenceDeletion(1), sequenceRestrictedDeletion(2),
+ sequencePermutation(3), sequencePermutationAndDeletion(4),
+ sequenceProviderDefined(5)}
+
+wordMatchTypes ATTRIBUTE ::= {
+ WITH SYNTAX WordMatchTypes
+ SINGLE VALUE TRUE
+ ID id-cat-wordMatchType
+} -- defaulting to wordExact
+
+WordMatchTypes ::= ENUMERATED {
+ wordExact(0), wordTruncated(1), wordPhonetic(2), wordProviderDefined(3)
+}
+
+characterMatchTypes ATTRIBUTE ::= {
+ WITH SYNTAX CharacterMatchTypes
+ SINGLE VALUE TRUE
+ ID id-cat-characterMatchTypes
+}
+
+CharacterMatchTypes ::= ENUMERATED {
+ characterExact(0), characterCaseIgnore(1), characterMapped(2)}
+
+selectedContexts ATTRIBUTE ::= {
+ WITH SYNTAX ContextAssertion
+ ID id-cat-selectedContexts
+}
+
+approximateStringMatch MATCHING-RULE ::= {ID id-mr-approximateStringMatch
+}
+
+ignoreIfAbsentMatch MATCHING-RULE ::= {ID id-mr-ignoreIfAbsentMatch
+}
+
+nullMatch MATCHING-RULE ::= {ID id-mr-nullMatch
+}
+
+ZONAL-MATCHING ::=
+ MAPPING-BASED-MATCHING{ZonalSelect, TRUE, ZonalResult, zonalMatch.&id}
+
+ZonalSelect ::= SEQUENCE OF AttributeType
+
+ZonalResult ::= ENUMERATED {
+ cannot-select-mapping(0), zero-mappings(2), multiple-mappings(3)}
+
+zonalMatch MATCHING-RULE ::= {
+ UNIQUE-MATCH-INDICATOR multipleMatchingLocalities
+ ID id-mr-zonalMatch
+}
+
+-- Contexts
+languageContext CONTEXT ::= {
+ WITH SYNTAX LanguageContextSyntax
+ ID id-avc-language
+}
+
+LanguageContextSyntax ::= PrintableString(SIZE (2..3)) -- ISO 639-2 codes only
+
+
+temporalContext CONTEXT ::= {
+ WITH SYNTAX TimeSpecification
+ ASSERTED AS TimeAssertion
+ ID id-avc-temporal
+}
+
+TimeSpecification ::= SEQUENCE {
+ time
+ CHOICE {absolute
+ SEQUENCE {startTime [0] GeneralizedTime OPTIONAL,
+ endTime [1] GeneralizedTime OPTIONAL},
+ periodic SET SIZE (1..MAX) OF Period},
+ notThisTime BOOLEAN DEFAULT FALSE,
+ timeZone TimeZone OPTIONAL
+}
+
+Period ::= SEQUENCE {
+ timesOfDay [0] SET SIZE (1..MAX) OF DayTimeBand OPTIONAL,
+ days
+ [1] CHOICE {intDay SET OF INTEGER,
+ bitDay
+ BIT STRING {sunday(0), monday(1), tuesday(2), wednesday(3),
+ thursday(4), friday(5), saturday(6)},
+ dayOf XDayOf} OPTIONAL,
+ weeks
+ [2] CHOICE {allWeeks NULL,
+ intWeek SET OF INTEGER,
+ bitWeek
+ BIT STRING {week1(0), week2(1), week3(2), week4(3), week5(4)}
+ } OPTIONAL,
+ months
+ [3] CHOICE {allMonths NULL,
+ intMonth SET OF INTEGER,
+ bitMonth
+ BIT STRING {january(0), february(1), march(2), april(3),
+ may(4), june(5), july(6), august(7),
+ september(8), october(9), november(10),
+ december(11)}} OPTIONAL,
+ years [4] SET OF INTEGER(1000..MAX) OPTIONAL
+}
+
+XDayOf ::= CHOICE {
+ first [1] NamedDay,
+ second [2] NamedDay,
+ third [3] NamedDay,
+ fourth [4] NamedDay,
+ fifth [5] NamedDay
+}
+
+NamedDay ::= CHOICE {
+ intNamedDays
+ ENUMERATED {sunday(1), monday(2), tuesday(3), wednesday(4), thursday(5),
+ friday(6), saturday(7)},
+ bitNamedDays
+ BIT STRING {sunday(0), monday(1), tuesday(2), wednesday(3), thursday(4),
+ friday(5), saturday(6)}
+}
+
+DayTimeBand ::= SEQUENCE {
+ startDayTime [0] DayTime DEFAULT {hour 0},
+ endDayTime [1] DayTime DEFAULT {hour 23, minute 59, second 59}
+}
+
+DayTime ::= SEQUENCE {
+ hour [0] INTEGER(0..23),
+ minute [1] INTEGER(0..59) DEFAULT 0,
+ second [2] INTEGER(0..59) DEFAULT 0
+}
+
+TimeZone ::= INTEGER(-12..12)
+
+TimeAssertion ::= CHOICE {
+ now NULL,
+ at GeneralizedTime,
+ between
+ SEQUENCE {startTime [0] GeneralizedTime,
+ endTime [1] GeneralizedTime OPTIONAL,
+ entirely BOOLEAN DEFAULT FALSE}
+}
+
+localeContext CONTEXT ::= {
+ WITH SYNTAX LocaleContextSyntax
+ ID id-avc-locale
+}
+
+LocaleContextSyntax ::= CHOICE {
+ localeID1 OBJECT IDENTIFIER,
+ localeID2 UnboundedDirectoryString
+}
+
+ldapAttributeOptionContext CONTEXT ::= {
+ WITH SYNTAX AttributeOptionList
+ ASSERTED AS AttributeOptionList
+ ABSENT-MATCH FALSE
+ ID id-avc-ldapAttributeOption
+}
+
+AttributeOptionList ::= SEQUENCE OF UTF8String
+
+-- Object identifier assignments
+-- object identifiers assigned in other modules are shown in comments
+-- Attributes
+-- id-at-objectClass OBJECT IDENTIFIER ::= {id-at 0}
+-- id-at-aliasedEntryName OBJECT IDENTIFIER ::= {id-at 1}
+-- id-at-encryptedAliasedEntryName OBJECT IDENTIFIER ::= {id-at 1 2}
+id-at-knowledgeInformation OBJECT IDENTIFIER ::=
+ {id-at 2}
+
+id-at-commonName OBJECT IDENTIFIER ::= {id-at 3}
+
+-- id-at-encryptedCommonName OBJECT IDENTIFIER ::= {id-at 3 2}
+id-at-surname OBJECT IDENTIFIER ::=
+ {id-at 4}
+
+-- id-at-encryptedSurname OBJECT IDENTIFIER ::= {id-at 4 2}
+id-at-serialNumber OBJECT IDENTIFIER ::=
+ {id-at 5}
+
+-- id-at-encryptedSerialNumbe r OBJECT IDENTIFIER ::= {id-at 5 2}
+id-at-countryName OBJECT IDENTIFIER ::=
+ {id-at 6}
+
+-- id-at-encryptedCountryName OBJECT IDENTIFIER ::= {id-at 6 2}
+id-at-localityName OBJECT IDENTIFIER ::=
+ {id-at 7}
+
+-- id-at-encryptedLocalityName OBJECT IDENTIFIER ::= {id-at 7 2}
+id-at-collectiveLocalityName OBJECT IDENTIFIER ::=
+ {id-at 7 1}
+
+-- id-at-encryptedCollectiveLocalityName OBJECT IDENTIFIER ::= {id-at 7 1 2}
+id-at-stateOrProvinceName OBJECT IDENTIFIER ::=
+ {id-at 8}
+
+-- id-at-encryptedStateOrProvinceName OBJECT IDENTIFIER ::= {id-at 8 2}
+id-at-collectiveStateOrProvinceName OBJECT IDENTIFIER ::=
+ {id-at 8 1}
+
+-- id-at-encryptedCollectiveStateOrProvinceName OBJECT IDENTIFIER ::= {id-at 8 1 2}
+id-at-streetAddress OBJECT IDENTIFIER ::=
+ {id-at 9}
+
+-- id-at-encryptedStreetAddress OBJECT IDENTIFIER ::= {id-at 9 2}
+id-at-collectiveStreetAddress OBJECT IDENTIFIER ::=
+ {id-at 9 1}
+
+-- id-at-encryptedCollectiveStreetAddress OBJECT IDENTIFIER ::= {id-at 9 1 2}
+id-at-organizationName OBJECT IDENTIFIER ::=
+ {id-at 10}
+
+-- id-at-encryptedOrganizationName OBJECT IDENTIFIER ::= {id-at 10 2}
+id-at-collectiveOrganizationName OBJECT IDENTIFIER ::=
+ {id-at 10 1}
+
+-- id-at-encryptedCollectiveOrganizationName OBJECT IDENTIFIER ::= {id-at 10 1 2}
+id-at-organizationalUnitName OBJECT IDENTIFIER ::=
+ {id-at 11}
+
+-- id-at-encryptedOrganizationalUnitName OBJECT IDENTIFIER ::= {id-at 11 2}
+id-at-collectiveOrganizationalUnitName OBJECT IDENTIFIER ::=
+ {id-at 11 1}
+
+-- id-at-encryptedCollectiveOrganizationalUnitNam OBJECT IDENTIFIER ::= {id-at 11 1 2}
+id-at-title OBJECT IDENTIFIER ::=
+ {id-at 12}
+
+-- id-at-encryptedTitle OBJECT IDENTIFIER ::= {id-at 12 2}
+id-at-description OBJECT IDENTIFIER ::=
+ {id-at 13}
+
+-- id-at-encryptedDescription OBJECT IDENTIFIER ::= {id-at 13 2}
+id-at-searchGuide OBJECT IDENTIFIER ::=
+ {id-at 14}
+
+-- id-at-encryptedSearchGuide OBJECT IDENTIFIER ::= {id-at 14 2}
+id-at-businessCategory OBJECT IDENTIFIER ::=
+ {id-at 15}
+
+-- id-at-encryptedBusinessCategory OBJECT IDENTIFIER ::= {id-at 15 2}
+id-at-postalAddress OBJECT IDENTIFIER ::=
+ {id-at 16}
+
+-- id-at-encryptedPostalAddress OBJECT IDENTIFIER ::= {id-at 16 2}
+id-at-collectivePostalAddress OBJECT IDENTIFIER ::=
+ {id-at 16 1}
+
+-- id-at-encryptedCollectivePostalAddress OBJECT IDENTIFIER ::= {id-at 16 1 2}
+id-at-postalCode OBJECT IDENTIFIER ::=
+ {id-at 17}
+
+-- id-at-encryptedPostalCode OBJECT IDENTIFIER ::= {id-at 17 2}
+id-at-collectivePostalCode OBJECT IDENTIFIER ::=
+ {id-at 17 1}
+
+-- id-at-encryptedCollectivePostalCode OBJECT IDENTIFIER ::= {id-at 17 1 2}
+id-at-postOfficeBox OBJECT IDENTIFIER ::=
+ {id-at 18}
+
+id-at-collectivePostOfficeBox OBJECT IDENTIFIER ::= {id-at 18 1}
+
+-- id-at-encryptedPostOfficeBox OBJECT IDENTIFIER ::= {id-at 18 2}
+-- id-at-encryptedCollectivePostOfficeBox OBJECT IDENTIFIER ::= {id-at 18 1 2}
+id-at-physicalDeliveryOfficeName OBJECT IDENTIFIER ::=
+ {id-at 19}
+
+id-at-collectivePhysicalDeliveryOfficeName OBJECT IDENTIFIER ::= {id-at 19 1}
+
+-- id-at-encryptedPhysicalDeliveryOfficeName OBJECT IDENTIFIER ::= {id-at 19 2}
+-- id-at-encryptedCollectivePhysicalDeliveryOfficeName OBJECT IDENTIFIER ::= {id-at 19 1 2}
+id-at-telephoneNumber OBJECT IDENTIFIER ::=
+ {id-at 20}
+
+-- id-at-encryptedTelephoneNumber OBJECT IDENTIFIER ::= {id-at 20 2}
+id-at-collectiveTelephoneNumber OBJECT IDENTIFIER ::=
+ {id-at 20 1}
+
+-- id-at-encryptedCollectiveTelephoneNumber OBJECT IDENTIFIER ::= {id-at 20 1 2}
+id-at-telexNumber OBJECT IDENTIFIER ::=
+ {id-at 21}
+
+-- id-at-encryptedTelexNumber OBJECT IDENTIFIER ::= {id-at 21 2}
+id-at-collectiveTelexNumber OBJECT IDENTIFIER ::=
+ {id-at 21 1}
+
+-- id-at-encryptedCollectiveTelexNumber OBJECT IDENTIFIER ::= {id-at 21 1 2}
+-- id-at-teletexTerminalIdentifier OBJECT IDENTIFIER ::= {id-at 22}
+-- id-at-encryptedTeletexTerminalIdentifier OBJECT IDENTIFIER ::= {id-at 22 2}
+-- id-at-collectiveTeletexTerminalIdentifier OBJECT IDENTIFIER ::= {id-at 22 1}
+-- id-at-encryptedCollectiveTeletexTerminalIdentifier OBJECT IDENTIFIER ::= {id-at 22 1 2}
+id-at-facsimileTelephoneNumber OBJECT IDENTIFIER ::=
+ {id-at 23}
+
+-- id-at-encryptedFacsimileTelephoneNumber OBJECT IDENTIFIER ::= {id-at 23 2}
+id-at-collectiveFacsimileTelephoneNumber OBJECT IDENTIFIER ::=
+ {id-at 23 1}
+
+-- id-at-encryptedCollectiveFacsimileTelephoneNumber OBJECT IDENTIFIER ::= {id-at 23 1 2}
+id-at-x121Address OBJECT IDENTIFIER ::=
+ {id-at 24}
+
+-- id-at-encryptedX121Address OBJECT IDENTIFIER ::= {id-at 24 2}
+id-at-internationalISDNNumber OBJECT IDENTIFIER ::=
+ {id-at 25}
+
+-- id-at-encryptedInternationalISDNNumber OBJECT IDENTIFIER ::= {id-at 25 2}
+id-at-collectiveInternationalISDNNumber OBJECT IDENTIFIER ::=
+ {id-at 25 1}
+
+-- id-at-encryptedCollectiveInternationalISDNNumber OBJECT IDENTIFIER ::= {id-at 25 1 2}
+id-at-registeredAddress OBJECT IDENTIFIER ::=
+ {id-at 26}
+
+-- id-at-encryptedRegisteredAddress OBJECT IDENTIFIER ::= {id-at 26 2}
+id-at-destinationIndicator OBJECT IDENTIFIER ::=
+ {id-at 27}
+
+-- id-at-encryptedDestinationIndicator OBJECT IDENTIFIER ::= {id-at 27 2}
+id-at-preferredDeliveryMethod OBJECT IDENTIFIER ::=
+ {id-at 28}
+
+-- id-at-encryptedPreferredDeliveryMethod OBJECT IDENTIFIER ::= {id-at 28 2}
+id-at-presentationAddress OBJECT IDENTIFIER ::=
+ {id-at 29}
+
+-- id-at-encryptedPresentationAddress OBJECT IDENTIFIER ::= {id-at 29 2}
+id-at-supportedApplicationContext OBJECT IDENTIFIER ::=
+ {id-at 30}
+
+-- id-at-encryptedSupportedApplicationContext OBJECT IDENTIFIER ::= {id-at 30 2}
+id-at-member OBJECT IDENTIFIER ::=
+ {id-at 31}
+
+-- id-at-encryptedMember OBJECT IDENTIFIER ::= {id-at 31 2}
+id-at-owner OBJECT IDENTIFIER ::=
+ {id-at 32}
+
+-- id-at-encryptedOwner OBJECT IDENTIFIER ::= {id-at 32 2}
+id-at-roleOccupant OBJECT IDENTIFIER ::=
+ {id-at 33}
+
+-- id-at-encryptedRoleOccupant OBJECT IDENTIFIER ::= {id-at 33 2}
+id-at-seeAlso OBJECT IDENTIFIER ::=
+ {id-at 34}
+
+-- id-at-encryptedSeeAlso OBJECT IDENTIFIER ::= {id-at 34 2}
+-- id-at-userPassword OBJECT IDENTIFIER ::= {id-at 35} X.509|Part8
+-- id-at-encryptedUserPassword OBJECT IDENTIFIER ::= {id-at 35 2}
+-- id-at-userCertificate OBJECT IDENTIFIER ::= {id-at 36} X.509|Part8
+-- id-at-encryptedUserCertificate OBJECT IDENTIFIER ::= {id-at 36 2}
+-- id-at-cACertificate OBJECT IDENTIFIER ::= {id-at 37} X.509|Part8
+-- id-at-encryptedCACertificate OBJECT IDENTIFIER ::= {id-at 37 2}
+-- id-at-authorityRevocationList OBJECT IDENTIFIER ::= {id-at 38} X.509|Part8
+-- id-at-encryptedAuthorityRevocationList OBJECT IDENTIFIER ::= {id-at 38 2}
+-- id-at-certificateRevocationList OBJECT IDENTIFIER ::= {id-at 39} X.509|Part8
+-- id-at-encryptedCertificateRevocationList OBJECT IDENTIFIER ::= {id-at 39 2}
+-- id-at-crossCertificatePair OBJECT IDENTIFIER ::= {id-at 40} X.509|Part8
+-- id-at-encryptedCrossCertificatePair OBJECT IDENTIFIER ::= {id-at 40 2}
+id-at-name OBJECT IDENTIFIER ::=
+ {id-at 41}
+
+id-at-givenName OBJECT IDENTIFIER ::= {id-at 42}
+
+-- id-at-encryptedGivenName OBJECT IDENTIFIER ::= {id-at 42 2}
+id-at-initials OBJECT IDENTIFIER ::=
+ {id-at 43}
+
+-- id-at-encryptedInitials OBJECT IDENTIFIER ::= {id-at 43 2}
+id-at-generationQualifier OBJECT IDENTIFIER ::=
+ {id-at 44}
+
+-- id-at-encryptedGenerationQualifier OBJECT IDENTIFIER ::= {id-at 44 2}
+id-at-uniqueIdentifier OBJECT IDENTIFIER ::=
+ {id-at 45}
+
+-- id-at-encryptedUniqueIdentifier OBJECT IDENTIFIER ::= {id-at 45 2}
+id-at-dnQualifier OBJECT IDENTIFIER ::=
+ {id-at 46}
+
+-- id-at-encryptedDnQualifier OBJECT IDENTIFIER ::= {id-at 46 2}
+id-at-enhancedSearchGuide OBJECT IDENTIFIER ::=
+ {id-at 47}
+
+-- id-at-encryptedEnhancedSearchGuide OBJECT IDENTIFIER ::= {id-at 47 2}
+id-at-protocolInformation OBJECT IDENTIFIER ::=
+ {id-at 48}
+
+-- id-at-encryptedProtocolInformation OBJECT IDENTIFIER ::= {id-at 48 2}
+id-at-distinguishedName OBJECT IDENTIFIER ::=
+ {id-at 49}
+
+-- id-at-encryptedDistinguishedName OBJECT IDENTIFIER ::= {id-at 49 2}
+id-at-uniqueMember OBJECT IDENTIFIER ::=
+ {id-at 50}
+
+-- id-at-encryptedUniqueMember OBJECT IDENTIFIER ::= {id-at 50 2}
+id-at-houseIdentifier OBJECT IDENTIFIER ::=
+ {id-at 51}
+
+-- id-at-encryptedHouseIdentifier OBJECT IDENTIFIER ::= {id-at 51 2}
+-- id-at-supportedAlgorithms OBJECT IDENTIFIER ::= {id-at 52} X.509|Part8
+-- id-at-encryptedSupportedAlgorithms OBJECT IDENTIFIER ::= {id-at 52 2}
+-- id-at-deltaRevocationList OBJECT IDENTIFIER ::= {id-at 53} X.509|Part8
+-- id-at-encryptedDeltaRevocationList OBJECT IDENTIFIER ::= {id-at 53 2}
+id-at-dmdName OBJECT IDENTIFIER ::=
+ {id-at 54}
+
+-- id-at-encryptedDmdName OBJECT IDENTIFIER ::= {id-at 54 2}
+-- id-at-clearance OBJECT IDENTIFIER ::= {id-at 55}
+-- id-at-encryptedClearance OBJECT IDENTIFIER ::= {id-at 55 2}
+-- id-at-defaultDirQop OBJECT IDENTIFIER ::= {id-at 56}
+-- id-at-encryptedDefaultDirQop OBJECT IDENTIFIER ::= {id-at 56 2}
+-- id-at-attributeIntegrityInfo OBJECT IDENTIFIER ::= {id-at 57}
+-- id-at-encryptedAttributeIntegrityInfo OBJECT IDENTIFIER ::= {id-at 57 2}
+-- id-at-attributeCertificate OBJECT IDENTIFIER ::= {id-at 58} X.509|Part8
+-- id-at-encryptedAttributeCertificate OBJECT IDENTIFIER ::= {id-at 58 2}
+-- id-at-attributeCertificateRevocationList OBJECT IDENTIFIER ::= {id-at 59} X.509|Part8
+-- id-at-encryptedAttributeCertificateRevocationList OBJECT IDENTIFIER ::= {id-at 59 2}
+-- id-at-confKeyInfo OBJECT IDENTIFIER ::= {id-at 60}
+-- id-at-encryptedConfKeyInfo OBJECT IDENTIFIER ::= {id-at 60 2}
+-- id-at-aACertificate OBJECT IDENTIFIER ::= {id-at 61} X.509|Part8
+-- id-at-attributeDescriptorCertificate OBJECT IDENTIFIER ::= {id-at 62} X.509|Part8
+-- id-at-attributeAuthorityRevocationList OBJECT IDENTIFIER ::= {id-at 63} X.509|Part8
+-- id-at-family-information OBJECT IDENTIFIER ::= {id-at 64}
+id-at-pseudonym OBJECT IDENTIFIER ::=
+ {id-at 65}
+
+id-at-communicationsService OBJECT IDENTIFIER ::= {id-at 66}
+
+id-at-communicationsNetwork OBJECT IDENTIFIER ::= {id-at 67}
+
+-- id-at-certificationPracticeStmt OBJECT IDENTIFIER ::= {id-at 68} X.509|Part8
+-- id-at-certificatePolicy OBJECT IDENTIFIER ::= {id-at 69} X.509|Part8
+-- id-at-pkiPath OBJECT IDENTIFIER ::= {id-at 70} X.509|Part8
+-- id-at-privPolicy OBJECT IDENTIFIER ::= {id-at 71} X.509|Part8
+-- id-at-role OBJECT IDENTIFIER ::= {id-at 72} X.509|Part8
+-- id-at-delegationPath OBJECT IDENTIFIER ::= {id-at 73} X.509|Part8
+-- id-at-protPrivPolicy OBJECT IDENTIFIER ::= {id-at 74} X.509|Part8
+-- id-at-xMLPrivilegeInfo OBJECT IDENTIFIER ::= {id-at 75} X.509|Part8
+-- id-at-xmlPrivPolicy OBJECT IDENTIFIER ::= {id-at 76} X.509|Part8
+id-at-uuidpair OBJECT IDENTIFIER ::=
+ {id-at 77}
+
+id-at-tagOid OBJECT IDENTIFIER ::= {id-at 78}
+
+id-at-uiiFormat OBJECT IDENTIFIER ::= {id-at 79}
+
+id-at-uiiInUrn OBJECT IDENTIFIER ::= {id-at 80}
+
+id-at-contentUri OBJECT IDENTIFIER ::= {id-at 81}
+
+-- id-at-permission OBJECT IDENTIFIER ::= {id-at 82} X.509|Part8
+-- Control attributes
+id-cat-sequenceMatchType OBJECT IDENTIFIER ::=
+ {id-cat 1}
+
+id-cat-wordMatchType OBJECT IDENTIFIER ::= {id-cat 2}
+
+id-cat-characterMatchTypes OBJECT IDENTIFIER ::= {id-cat 3}
+
+id-cat-selectedContexts OBJECT IDENTIFIER ::= {id-cat 4}
+
+-- Notification attributes
+id-not-dSAProblem OBJECT IDENTIFIER ::= {id-not 0}
+
+id-not-searchServiceProblem OBJECT IDENTIFIER ::= {id-not 1}
+
+id-not-serviceType OBJECT IDENTIFIER ::= {id-not 2}
+
+id-not-attributeTypeList OBJECT IDENTIFIER ::= {id-not 3}
+
+id-not-matchingRuleList OBJECT IDENTIFIER ::= {id-not 4}
+
+id-not-filterItem OBJECT IDENTIFIER ::= {id-not 5}
+
+id-not-attributeCombinations OBJECT IDENTIFIER ::= {id-not 6}
+
+id-not-contextTypeList OBJECT IDENTIFIER ::= {id-not 7}
+
+id-not-contextList OBJECT IDENTIFIER ::= {id-not 8}
+
+id-not-contextCombinations OBJECT IDENTIFIER ::= {id-not 9}
+
+id-not-hierarchySelectList OBJECT IDENTIFIER ::= {id-not 10}
+
+id-not-searchControlOptionsList OBJECT IDENTIFIER ::= {id-not 11}
+
+id-not-serviceControlOptionsList OBJECT IDENTIFIER ::= {id-not 12}
+
+id-not-multipleMatchingLocalities OBJECT IDENTIFIER ::= {id-not 13}
+
+id-not-proposedRelaxation OBJECT IDENTIFIER ::= {id-not 14}
+
+id-not-appliedRelaxation OBJECT IDENTIFIER ::= {id-not 15}
+
+-- Problem definitions
+id-pr-targetDsaUnavailable OBJECT IDENTIFIER ::=
+ {id-pr 1}
+
+id-pr-dataSourceUnavailable OBJECT IDENTIFIER ::= {id-pr 2}
+
+id-pr-unidentifiedOperation OBJECT IDENTIFIER ::= {id-pr 3}
+
+id-pr-unavailableOperation OBJECT IDENTIFIER ::= {id-pr 4}
+
+id-pr-searchAttributeViolation OBJECT IDENTIFIER ::= {id-pr 5}
+
+id-pr-searchAttributeCombinationViolation OBJECT IDENTIFIER ::= {id-pr 6}
+
+id-pr-searchValueNotAllowed OBJECT IDENTIFIER ::= {id-pr 7}
+
+id-pr-missingSearchAttribute OBJECT IDENTIFIER ::= {id-pr 8}
+
+id-pr-searchValueViolation OBJECT IDENTIFIER ::= {id-pr 9}
+
+id-pr-attributeNegationViolation OBJECT IDENTIFIER ::= {id-pr 10}
+
+id-pr-searchValueRequired OBJECT IDENTIFIER ::= {id-pr 11}
+
+id-pr-invalidSearchValue OBJECT IDENTIFIER ::= {id-pr 12}
+
+id-pr-searchContextViolation OBJECT IDENTIFIER ::= {id-pr 13}
+
+id-pr-searchContextCombinationViolation OBJECT IDENTIFIER ::= {id-pr 14}
+
+id-pr-missingSearchContext OBJECT IDENTIFIER ::= {id-pr 15}
+
+id-pr-searchContextValueViolation OBJECT IDENTIFIER ::= {id-pr 16}
+
+id-pr-searchContextValueRequired OBJECT IDENTIFIER ::= {id-pr 17}
+
+id-pr-invalidContextSearchValue OBJECT IDENTIFIER ::= {id-pr 18}
+
+id-pr-unsupportedMatchingRule OBJECT IDENTIFIER ::= {id-pr 19}
+
+id-pr-attributeMatchingViolation OBJECT IDENTIFIER ::= {id-pr 20}
+
+id-pr-unsupportedMatchingUse OBJECT IDENTIFIER ::= {id-pr 21}
+
+id-pr-matchingUseViolation OBJECT IDENTIFIER ::= {id-pr 22}
+
+id-pr-hierarchySelectForbidden OBJECT IDENTIFIER ::= {id-pr 23}
+
+id-pr-invalidHierarchySelect OBJECT IDENTIFIER ::= {id-pr 24}
+
+id-pr-unavailableHierarchySelect OBJECT IDENTIFIER ::= {id-pr 25}
+
+id-pr-invalidSearchControlOptions OBJECT IDENTIFIER ::= {id-pr 26}
+
+id-pr-invalidServiceControlOptions OBJECT IDENTIFIER ::= {id-pr 27}
+
+id-pr-searchSubsetViolation OBJECT IDENTIFIER ::= {id-pr 28}
+
+id-pr-unmatchedKeyAttributes OBJECT IDENTIFIER ::= {id-pr 29}
+
+id-pr-ambiguousKeyAttributes OBJECT IDENTIFIER ::= {id-pr 30}
+
+id-pr-unavailableRelaxationLevel OBJECT IDENTIFIER ::= {id-pr 31}
+
+id-pr-emptyHierarchySelection OBJECT IDENTIFIER ::= {id-pr 32}
+
+id-pr-administratorImposedLimit OBJECT IDENTIFIER ::= {id-pr 33}
+
+id-pr-permanentRestriction OBJECT IDENTIFIER ::= {id-pr 34}
+
+id-pr-temporaryRestriction OBJECT IDENTIFIER ::= {id-pr 35}
+
+id-pr-relaxationNotSupported OBJECT IDENTIFIER ::= {id-pr 36}
+
+-- Matching rules
+-- id-mr-objectIdentifierMatch OBJECT IDENTIFIER ::= {id-mr 0} X.501|Part2
+-- id-mr-distinguishedNameMatch OBJECT IDENTIFIER ::= {id-mr 1} X.501|Part2
+id-mr-caseIgnoreMatch OBJECT IDENTIFIER ::=
+ {id-mr 2}
+
+id-mr-caseIgnoreOrderingMatch OBJECT IDENTIFIER ::= {id-mr 3}
+
+id-mr-caseIgnoreSubstringsMatch OBJECT IDENTIFIER ::= {id-mr 4}
+
+id-mr-caseExactMatch OBJECT IDENTIFIER ::= {id-mr 5}
+
+id-mr-caseExactOrderingMatch OBJECT IDENTIFIER ::= {id-mr 6}
+
+id-mr-caseExactSubstringsMatch OBJECT IDENTIFIER ::= {id-mr 7}
+
+id-mr-numericStringMatch OBJECT IDENTIFIER ::= {id-mr 8}
+
+id-mr-numericStringOrderingMatch OBJECT IDENTIFIER ::= {id-mr 9}
+
+id-mr-numericStringSubstringsMatch OBJECT IDENTIFIER ::= {id-mr 10}
+
+id-mr-caseIgnoreListMatch OBJECT IDENTIFIER ::= {id-mr 11}
+
+id-mr-caseIgnoreListSubstringsMatch OBJECT IDENTIFIER ::= {id-mr 12}
+
+id-mr-booleanMatch OBJECT IDENTIFIER ::= {id-mr 13}
+
+id-mr-integerMatch OBJECT IDENTIFIER ::= {id-mr 14}
+
+id-mr-integerOrderingMatch OBJECT IDENTIFIER ::= {id-mr 15}
+
+id-mr-bitStringMatch OBJECT IDENTIFIER ::= {id-mr 16}
+
+id-mr-octetStringMatch OBJECT IDENTIFIER ::= {id-mr 17}
+
+id-mr-octetStringOrderingMatch OBJECT IDENTIFIER ::= {id-mr 18}
+
+id-mr-octetStringSubstringsMatch OBJECT IDENTIFIER ::= {id-mr 19}
+
+id-mr-telephoneNumberMatch OBJECT IDENTIFIER ::= {id-mr 20}
+
+id-mr-telephoneNumberSubstringsMatch OBJECT IDENTIFIER ::= {id-mr 21}
+
+id-mr-presentationAddressMatch OBJECT IDENTIFIER ::= {id-mr 22}
+
+id-mr-uniqueMemberMatch OBJECT IDENTIFIER ::= {id-mr 23}
+
+id-mr-protocolInformationMatch OBJECT IDENTIFIER ::= {id-mr 24}
+
+id-mr-uTCTimeMatch OBJECT IDENTIFIER ::= {id-mr 25}
+
+id-mr-uTCTimeOrderingMatch OBJECT IDENTIFIER ::= {id-mr 26}
+
+id-mr-generalizedTimeMatch OBJECT IDENTIFIER ::= {id-mr 27}
+
+id-mr-generalizedTimeOrderingMatch OBJECT IDENTIFIER ::= {id-mr 28}
+
+id-mr-integerFirstComponentMatch OBJECT IDENTIFIER ::= {id-mr 29}
+
+id-mr-objectIdentifierFirstComponentMatch OBJECT IDENTIFIER ::= {id-mr 30}
+
+id-mr-directoryStringFirstComponentMatch OBJECT IDENTIFIER ::= {id-mr 31}
+
+id-mr-wordMatch OBJECT IDENTIFIER ::= {id-mr 32}
+
+id-mr-keywordMatch OBJECT IDENTIFIER ::= {id-mr 33}
+
+-- id-mr-certificateExactMatch OBJECT IDENTIFIER ::= {id-mr 34} X.509|Part8
+-- id-mr-certificateMatch OBJECT IDENTIFIER ::= {id-mr 35} X.509|Part8
+-- id-mr-certificatePairExactMatch OBJECT IDENTIFIER ::= {id-mr 36} X.509|Part8
+-- id-mr-certificatePairMatch OBJECT IDENTIFIER ::= {id-mr 37} X.509|Part8
+-- id-mr-certificateListExactMatch OBJECT IDENTIFIER ::= {id-mr 38} X.509|Part8
+-- id-mr-certificateListMatch OBJECT IDENTIFIER ::= {id-mr 39} X.509|Part8
+-- id-mr-algorithmIdentifierMatch OBJECT IDENTIFIER ::= {id-mr 40} X.509|Part8
+id-mr-storedPrefixMatch OBJECT IDENTIFIER ::=
+ {id-mr 41}
+
+-- id-mr-attributeCertificateMatch OBJECT IDENTIFIER ::= {id-mr 42} X.509|Part8
+-- id-mr-readerAndKeyIDMatch OBJECT IDENTIFIER ::= {id-mr 43}
+-- id-mr-attributeIntegrityMatch OBJECT IDENTIFIER ::= {id-mr 44}
+-- id-mr-attributeCertificateExactMatch OBJECT IDENTIFIER ::= {id-mr 45} X.509|Part8
+-- id-mr-holderIssuerMatch OBJECT IDENTIFIER ::= {id-mr 46} X.509|Part8
+id-mr-systemProposedMatch OBJECT IDENTIFIER ::=
+ {id-mr 47}
+
+id-mr-generalWordMatch OBJECT IDENTIFIER ::= {id-mr 48}
+
+id-mr-approximateStringMatch OBJECT IDENTIFIER ::= {id-mr 49}
+
+id-mr-ignoreIfAbsentMatch OBJECT IDENTIFIER ::= {id-mr 50}
+
+id-mr-nullMatch OBJECT IDENTIFIER ::= {id-mr 51}
+
+id-mr-zonalMatch OBJECT IDENTIFIER ::= {id-mr 52}
+
+-- id-mr-authAttIdMatch OBJECT IDENTIFIER ::= {id-mr 53} X.509|Part8
+-- id-mr-roleSpecCertIdMatch OBJECT IDENTIFIER ::= {id-mr 54} X.509|Part8
+-- id-mr-basicAttConstraintsMatch OBJECT IDENTIFIER ::= {id-mr 55} X.509|Part8
+-- id-mr-delegatedNameConstraintsMatch OBJECT IDENTIFIER ::= {id-mr 56} X.509|Part8
+-- id-mr-timeSpecMatch OBJECT IDENTIFIER ::= {id-mr 57} X.509|Part8
+-- id-mr-attDescriptorMatch OBJECT IDENTIFIER ::= {id-mr 58} X.509|Part8
+-- id-mr-acceptableCertPoliciesMatch OBJECT IDENTIFIER ::= {id-mr 59} X.509|Part8
+-- id-mr-policyMatch OBJECT IDENTIFIER ::= {id-mr 60} X.509|Part8
+-- id-mr-delegationPathMatch OBJECT IDENTIFIER ::= {id-mr 61} X.509|Part8
+-- id-mr-pkiPathMatch OBJECT IDENTIFIER ::= {id-mr 62} X.509|Part8
+id-mr-facsimileNumberMatch OBJECT IDENTIFIER ::=
+ {id-mr 63}
+
+id-mr-facsimileNumberSubstringsMatch OBJECT IDENTIFIER ::= {id-mr 64}
+
+-- id-mr-enhancedCertificateMatch OBJECT IDENTIFIER ::= {id-mr 65} X.509|Part8
+-- id-mr-sOAIdentifierMatch OBJECT IDENTIFIER ::= {id-mr 66} X.509|Part8
+-- id-mr-extensionPresenceMatch OBJECT IDENTIFIER ::= {id-mr 67} X.509|Part8
+id-mr-uuidpairmatch OBJECT IDENTIFIER ::=
+ {id-mr 68}
+
+-- id-mr-dualStringMatch OBJECT IDENTIFIER ::= {id-mr 69} X.509|Part8
+-- contexts
+id-avc-language OBJECT IDENTIFIER ::=
+ {id-avc 0}
+
+id-avc-temporal OBJECT IDENTIFIER ::= {id-avc 1}
+
+id-avc-locale OBJECT IDENTIFIER ::= {id-avc 2}
+
+-- id-avc-attributeValueSecurityLabelContext OBJECT IDENTIFIER ::= {id-avc 3}
+-- id-avc-attributeValueIntegrityInfoContext OBJECT IDENTIFIER ::= {id-avc 4}
+id-avc-ldapAttributeOption OBJECT IDENTIFIER ::=
+ {id-avc 5}
+
+END -- SelectedAttributeTypes
diff --git a/lib/public_key/asn1/UsefulDefinitions.asn1 b/lib/public_key/asn1/UsefulDefinitions.asn1
new file mode 100644
index 0000000000..a200aac6e2
--- /dev/null
+++ b/lib/public_key/asn1/UsefulDefinitions.asn1
@@ -0,0 +1,234 @@
+UsefulDefinitions {joint-iso-itu-t ds(5) module(1) usefulDefinitions(0) 3}
+DEFINITIONS ::=
+BEGIN
+
+-- EXPORTS All -
+-- The types and values defined in this module are exported for use in the other ASN.1 modules contained
+-- within the Directory Specifications, and for the use of other applications which will use them to access
+-- Directory services. Other applications may use them for their own purposes, but this will not constrain
+-- extensions and modifications needed to maintain or improve the Directory service.
+ID ::= OBJECT IDENTIFIER
+
+ds ID ::= {joint-iso-itu-t ds(5)}
+
+-- categories of information object
+module ID ::= {ds 1}
+
+serviceElement ID ::= {ds 2}
+
+applicationContext ID ::= {ds 3}
+
+attributeType ID ::= {ds 4}
+
+attributeSyntax ID ::= {ds 5}
+
+objectClass ID ::= {ds 6}
+
+-- attributeSet ID ::= {ds 7}
+algorithm ID ::= {ds 8}
+
+abstractSyntax ID ::= {ds 9}
+
+-- object ID ::= {ds 10}
+-- port ID ::= {ds 11}
+dsaOperationalAttribute ID ::=
+ {ds 12}
+
+matchingRule ID ::= {ds 13}
+
+knowledgeMatchingRule ID ::= {ds 14}
+
+nameForm ID ::= {ds 15}
+
+group ID ::= {ds 16}
+
+subentry ID ::= {ds 17}
+
+operationalAttributeType ID ::= {ds 18}
+
+operationalBinding ID ::= {ds 19}
+
+schemaObjectClass ID ::= {ds 20}
+
+schemaOperationalAttribute ID ::= {ds 21}
+
+administrativeRoles ID ::= {ds 23}
+
+accessControlAttribute ID ::= {ds 24}
+
+rosObject ID ::= {ds 25}
+
+contract ID ::= {ds 26}
+
+package ID ::= {ds 27}
+
+accessControlSchemes ID ::= {ds 28}
+
+certificateExtension ID ::= {ds 29}
+
+managementObject ID ::= {ds 30}
+
+attributeValueContext ID ::= {ds 31}
+
+-- securityExchange ID ::= {ds 32}
+idmProtocol ID ::= {ds 33}
+
+problem ID ::= {ds 34}
+
+notification ID ::= {ds 35}
+
+matchingRestriction ID ::=
+ {ds 36} -- None are currently defined by this specification
+
+controlAttributeType ID ::= {ds 37}
+
+-- modules
+usefulDefinitions ID ::= {module usefulDefinitions(0) 3}
+
+informationFramework ID ::= {module informationFramework(1) 3}
+
+directoryAbstractService ID ::= {module directoryAbstractService(2) 3}
+
+distributedOperations ID ::= {module distributedOperations(3) 3}
+
+protocolObjectIdentifiers ID ::= {module protocolObjectIdentifiers(4) 3}
+
+selectedAttributeTypes ID ::= {module selectedAttributeTypes(5) 3}
+
+selectedObjectClasses ID ::= {module selectedObjectClasses(6) 3}
+
+authenticationFramework ID ::= {module authenticationFramework(7) 3}
+
+algorithmObjectIdentifiers ID ::= {module algorithmObjectIdentifiers(8) 3}
+
+directoryObjectIdentifiers ID ::= {module directoryObjectIdentifiers(9) 3}
+
+upperBounds ID ::= {module upperBounds(10) 3}
+
+dap ID ::= {module dap(11) 3}
+
+dsp ID ::= {module dsp(12) 3}
+
+distributedDirectoryOIDs ID ::= {module distributedDirectoryOIDs(13) 3}
+
+directoryShadowOIDs ID ::= {module directoryShadowOIDs(14) 3}
+
+directoryShadowAbstractService ID ::=
+ {module directoryShadowAbstractService(15) 3}
+
+disp ID ::= {module disp(16) 3}
+
+dop ID ::= {module dop(17) 3}
+
+opBindingManagement ID ::= {module opBindingManagement(18) 3}
+
+opBindingOIDs ID ::= {module opBindingOIDs(19) 3}
+
+hierarchicalOperationalBindings ID ::=
+ {module hierarchicalOperationalBindings(20) 3}
+
+dsaOperationalAttributeTypes ID ::= {module dsaOperationalAttributeTypes(22) 3}
+
+schemaAdministration ID ::= {module schemaAdministration(23) 3}
+
+basicAccessControl ID ::= {module basicAccessControl(24) 3}
+
+directoryOperationalBindingTypes ID ::=
+ {module directoryOperationalBindingTypes(25) 3}
+
+certificateExtensions ID ::= {module certificateExtensions(26) 0}
+
+directoryManagement ID ::= {module directoryManagement(27) 1}
+
+enhancedSecurity ID ::= {module enhancedSecurity(28) 1}
+
+iDMProtocolSpecification ID ::= {module iDMProtocolSpecification(30) 4}
+
+directoryIDMProtocols ID ::= {module directoryIDMProtocols(31) 4}
+
+-- directorySecurityExchanges ID ::= {module directorySecurityExchanges (29) 1}
+-- synonyms
+id-oc ID ::=
+ objectClass
+
+id-at ID ::= attributeType
+
+id-as ID ::= abstractSyntax
+
+id-mr ID ::= matchingRule
+
+id-nf ID ::= nameForm
+
+id-sc ID ::= subentry
+
+id-oa ID ::= operationalAttributeType
+
+id-ob ID ::= operationalBinding
+
+id-doa ID ::= dsaOperationalAttribute
+
+id-kmr ID ::= knowledgeMatchingRule
+
+id-soc ID ::= schemaObjectClass
+
+id-soa ID ::= schemaOperationalAttribute
+
+id-ar ID ::= administrativeRoles
+
+id-aca ID ::= accessControlAttribute
+
+id-ac ID ::= applicationContext
+
+id-rosObject ID ::= rosObject
+
+id-contract ID ::= contract
+
+id-package ID ::= package
+
+id-acScheme ID ::= accessControlSchemes
+
+id-ce ID ::= certificateExtension
+
+id-mgt ID ::= managementObject
+
+id-idm ID ::= idmProtocol
+
+id-avc ID ::= attributeValueContext
+
+-- id-se ID ::= securityExchange
+id-pr ID ::= problem
+
+id-not ID ::= notification
+
+id-mre ID ::= matchingRestriction
+
+id-cat ID ::= controlAttributeType
+
+-- obsolete module identifiers
+-- usefulDefinition ID ::= {module 0}
+-- informationFramework ID ::= {module 1}
+-- directoryAbstractService ID ::= {module 2}
+-- distributedOperations ID ::= {module 3}
+-- protocolObjectIdentifiers ID ::= {module 4}
+-- selectedAttributeTypes ID ::= {module 5}
+-- selectedObjectClasses ID ::= {module 6}
+-- authenticationFramework ID ::= {module 7}
+-- algorithmObjectIdentifiers ID ::= {module 8}
+-- directoryObjectIdentifiers ID ::= {module 9}
+-- upperBounds ID ::= {module 10}
+-- dap ID ::= {module 11}
+-- dsp ID ::= {module 12}
+-- distributedDirectoryObjectIdentifiers ID ::= {module 13}
+-- unused module identifiers
+-- directoryShadowOIDs ID ::= {module 14}
+-- directoryShadowAbstractService ID ::= {module 15}
+-- disp ID ::= {module 16}
+-- dop ID ::= {module 17}
+-- opBindingManagement ID ::= {module 18}
+-- opBindingOIDs ID ::= {module 19}
+-- hierarchicalOperationalBindings ID ::= {module 20}
+-- dsaOperationalAttributeTypes ID ::= {module 22}
+-- schemaAdministration ID ::= {module 23}
+-- basicAccessControl ID ::= {module 24}
+-- operationalBindingOIDs ID ::= {module 25}
+END -- UsefulDefinitions
diff --git a/lib/public_key/doc/src/Makefile b/lib/public_key/doc/src/Makefile
index 928aa62c1b..17fb67e95c 100644
--- a/lib/public_key/doc/src/Makefile
+++ b/lib/public_key/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2008-2011. All Rights Reserved.
+# Copyright Ericsson AB 2008-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/public_key/doc/src/cert_records.xml b/lib/public_key/doc/src/cert_records.xml
index ad4f5812cb..f01f7dbaf5 100644
--- a/lib/public_key/doc/src/cert_records.xml
+++ b/lib/public_key/doc/src/cert_records.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2008</year>
- <year>2011</year>
+ <year>2013</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -34,10 +34,12 @@
<file>cert_records.xml</file>
</header>
- <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 5280.
+ <p>This chapter briefly describes erlang records derived from ASN1
+ specifications used to handle <c> X509 certificates</c> and <c>CertificationRequest</c>.
+ The intent is to describe the data types and not to specify the meaning of each
+ component for this we refer you to <url
+ href="http://www.ietf.org/rfc/rfc5280.txt">RFC 5280</url> and
+ <url href="http://www.rsa.com/rsalabs/node.asp?id=2124">PKCS-10</url>.
</p>
<p>Use the following include directive to get access to the
@@ -45,7 +47,7 @@
<code> -include_lib("public_key/include/public_key.hrl"). </code>
- <p>The used asn1 specifications are available <c>asn1</c> subdirectory
+ <p>The used ASN1 specifications are available <c>asn1</c> subdirectory
of the application <c>public_key</c>.
</p>
@@ -59,7 +61,7 @@
follows here.</p>
<p><c>oid() - a tuple of integers
- as generated by the asn1 compiler.</c></p>
+ as generated by the ASN1 compiler.</c></p>
<p><c>time() = uct_time() | general_time()</c></p>
@@ -98,7 +100,7 @@
#'Certificate'{
tbsCertificate, % #'TBSCertificate'{}
signatureAlgorithm, % #'AlgorithmIdentifier'{}
- signature % {0, binary()} - asn1 compact bitstring
+ signature % {0, binary()} - ASN1 compact bitstring
}.
#'TBSCertificate'{
@@ -116,7 +118,7 @@
#'AlgorithmIdentifier'{
algorithm, % oid()
- parameters % asn1_der_encoded()
+ parameters % der_encoded()
}.
</code>
@@ -124,7 +126,7 @@
#'OTPCertificate'{
tbsCertificate, % #'OTPTBSCertificate'{}
signatureAlgorithm, % #'SignatureAlgorithm'
- signature % {0, binary()} - asn1 compact bitstring
+ signature % {0, binary()} - ASN1 compact bitstring
}.
#'OTPTBSCertificate'{
@@ -134,7 +136,7 @@
issuer, % {rdnSequence, [#AttributeTypeAndValue'{}]}
validity, % #'Validity'{}
subject, % {rdnSequence, [#AttributeTypeAndValue'{}]}
- subjectPublicKeyInfo, % #'SubjectPublicKeyInfo'{}
+ subjectPublicKeyInfo, % #'OTPSubjectPublicKeyInfo'{}
issuerUniqueID, % binary() | asn1_novalue
subjectUniqueID, % binary() | asn1_novalue
extensions % [#'Extension'{}]
@@ -287,7 +289,7 @@ oid names see table below. Ex: ?'id-dsa-with-sha1'</p>
#'Extension'{
extnID, % id_extensions() | oid()
critical, % boolean()
- extnValue % asn1_der_encoded()
+ extnValue % der_encoded()
}.
</code>
@@ -369,7 +371,7 @@ oid names see table below. Ex: ?'id-dsa-with-sha1'</p>
<row>
<cell align="left" valign="middle">id-ce-cRLDistributionPoints</cell>
- <cell align="left" valign="middle">#'DistributionPoint'{}</cell>
+ <cell align="left" valign="middle">[#'DistributionPoint'{}]</cell>
</row>
<row>
@@ -458,7 +460,7 @@ oid names see table below. Ex: ?'id-dsa-with-sha1'</p>
#'Attribute'{
type, % oid()
- values % [asn1_der_encoded()]
+ values % [der_encoded()]
}).
#'BasicConstraints'{
@@ -483,9 +485,10 @@ oid names see table below. Ex: ?'id-dsa-with-sha1'</p>
}).
#'DistributionPoint'{
- distributionPoint, % general_name() | [#AttributeTypeAndValue{}]
+ distributionPoint, % {fullName, [general_name()]} | {nameRelativeToCRLIssuer,
+ [#AttributeTypeAndValue{}]}
reasons, % [dist_reason()]
- cRLIssuer % general_name()
+ cRLIssuer % [general_name()]
}).
</code>
@@ -527,7 +530,7 @@ oid names see table below. Ex: ?'id-dsa-with-sha1'</p>
#'CertificateList'{
tbsCertList, % #'TBSCertList{}
signatureAlgorithm, % #'AlgorithmIdentifier'{}
- signature % {0, binary()} - asn1 compact bitstring
+ signature % {0, binary()} - ASN1 compact bitstring
}).
#'TBSCertList'{
@@ -586,7 +589,8 @@ oid names see table below. Ex: ?'id-dsa-with-sha1'</p>
<code>
#'IssuingDistributionPoint'{
- distributionPoint, % general_name() | [#AttributeTypeAndValue'{}]
+ distributionPoint, % {fullName, [general_name()]} | {nameRelativeToCRLIssuer,
+ [#AttributeTypeAndValue'{}]}
onlyContainsUserCerts, % boolean()
onlyContainsCACerts, % boolean()
onlySomeReasons, % [dist_reason()]
@@ -630,6 +634,40 @@ oid names see table below. Ex: ?'id-dsa-with-sha1'</p>
aACompromise
</c></p>
</section>
-
+
+ <section>
+ <marker id="PKCS10"></marker>
+ <title>PKCS#10 Certification Request</title>
+ <code>
+#'CertificationRequest'{
+ certificationRequestInfo #'CertificationRequestInfo'{},
+ signatureAlgorithm #'CertificationRequest_signatureAlgorithm'{}}.
+ signature {0, binary()} - ASN1 compact bitstring
+ }
+
+#'CertificationRequestInfo'{
+ version atom(),
+ subject {rdnSequence, [#AttributeTypeAndValue'{}]} ,
+ subjectPKInfo #'CertificationRequestInfo_subjectPKInfo'{},
+ attributes [#AttributeTypeAndValue'{}]
+ }
+
+#'CertificationRequestInfo_subjectPKInfo'{
+ algorithm #'CertificationRequestInfo_subjectPKInfo_algorithm'{}
+ subjectPublicKey {0, binary()} - ASN1 compact bitstring
+ }
+
+#'CertificationRequestInfo_subjectPKInfo_algorithm'{
+ algorithm = oid(),
+ parameters = der_encoded()
+}
+
+#'CertificationRequest_signatureAlgorithm'{
+ algorithm = oid(),
+ parameters = der_encoded()
+ }
+ </code>
+ </section>
+
</section>
</chapter>
diff --git a/lib/public_key/doc/src/introduction.xml b/lib/public_key/doc/src/introduction.xml
index a21fcf3576..4b59cc2245 100644
--- a/lib/public_key/doc/src/introduction.xml
+++ b/lib/public_key/doc/src/introduction.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>2011</year>
+ <year>2013</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -36,23 +36,26 @@
<section>
<title>Purpose</title>
- <p> This application provides an API to public key infrastructure
- from RFC 3280 (X.509 certificates) and public key formats defined
- by the PKCS-standard.</p>
+ <p> public_key deals with public key related file formats, digital
+ signatures and <url href="http://www.ietf.org/rfc/rfc5280.txt">
+ X-509 certificates</url>. It is a library application that
+ provides encode/decode, sign/verify, encrypt/decrypt and similar
+ functionality, it does not read or write files it expects or returns
+ file contents or partial file contents as binaries.
+ </p>
</section>
<section>
<title>Prerequisites</title>
- <p>It is assumed that the reader is familiar with the Erlang
- programming language, concepts of OTP and has a basic understanding
- of the concepts of using public keys.</p>
+ <p>It is assumed that the reader has a basic understanding
+ of the concepts of using public keys and digital certificates.</p>
</section>
<section>
<title>Performance tips</title>
- <p>The public_key decode and encode functions will try to use the nifs
- which are in the asn1 compilers runtime modules if they can be found.
- So for the best performance you want to have the asn1 application in the
+ <p>The public_key decode and encode functions will try to use the NIFs
+ which are in the ASN1 compilers runtime modules if they can be found.
+ So for the best performance you want to have the ASN1 application in the
path of your system. </p>
</section>
diff --git a/lib/public_key/doc/src/notes.xml b/lib/public_key/doc/src/notes.xml
index 4d3a9856eb..a5e8beedf0 100644
--- a/lib/public_key/doc/src/notes.xml
+++ b/lib/public_key/doc/src/notes.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2008</year>
- <year>2011</year>
+ <year>2012</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -34,6 +34,66 @@
<file>notes.xml</file>
</header>
+<section><title>Public_Key 0.17</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ ssh_decode now handles comments, at the end of the line,
+ containing withe spaces correctly</p>
+ <p>
+ Own Id: OTP-9361</p>
+ </item>
+ <item>
+ <p>
+ Add missing references to sha224 and sha384</p>
+ <p>
+ Own Id: OTP-9362 Aux Id: seq12116 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ public_key now supports PKCS-10 and includes exprimental
+ support for PKCS-7</p>
+ <p>
+ Own Id: OTP-10509 Aux Id: kunagi-291 [202] </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Public_Key 0.16</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Add crypto and public_key support for the hash functions
+ SHA224, SHA256, SHA384 and SHA512 and also hmac and
+ rsa_sign/verify support using these hash functions.
+ Thanks to Andreas Schultz for making a prototype.</p>
+ <p>
+ Own Id: OTP-9908</p>
+ </item>
+ <item>
+ <p>
+ Optimize RSA private key handling in <c>crypto</c> and
+ <c>public_key</c>.</p>
+ <p>
+ Own Id: OTP-10065</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Public_Key 0.15</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 ea3123b5bd..08fa4eec58 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>2011</year>
+ <year>2013</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -32,8 +32,10 @@
</header>
<description>
<p> This application provides an API to public key infrastructure
- from RFC 3280 (X.509 certificates) and some public key formats defined
- by the PKCS-standard. </p>
+ from <url href="http://www.ietf.org/rfc/rfc5280.txt">RFC
+ 5280</url> (X.509 certificates) and public key formats defined by
+ the <url href="http://www.rsa.com/rsalabs/node.asp?id=2124">
+ PKCS-standard</url></p>
</description>
<xi:include href="introduction.xml"/>
<xi:include href="public_key_records.xml"/>
diff --git a/lib/public_key/doc/src/public_key.xml b/lib/public_key/doc/src/public_key.xml
index 0b6673e826..5864de2d57 100644
--- a/lib/public_key/doc/src/public_key.xml
+++ b/lib/public_key/doc/src/public_key.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2008</year>
- <year>2011</year>
+ <year>2013</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -33,12 +33,30 @@
<module>public_key</module>
<modulesummary> API module for public key infrastructure.</modulesummary>
<description>
- <p>This module provides functions to handle public key infrastructure
- from RFC 5280 - X.509 certificates and some parts of the PKCS-standard.
+ <p>This module provides functions to handle public key infrastructure. It can
+ encode/decode different file formats (PEM, openssh), sign and verify digital signatures and validate
+ certificate paths and certificate revocation lists.
</p>
</description>
<section>
+ <title>public_key</title>
+
+ <list type="bulleted">
+ <item>public_key requires the crypto application.</item>
+
+ <item>Supports <url href="http://www.ietf.org/rfc/rfc5280.txt">RFC 5280 </url> -
+ Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile </item>
+ <item>Supports <url href="http://www.rsa.com/rsalabs/node.asp?id=2125"> PKCS-1 </url> - RSA Cryptography Standard </item>
+ <item>Supports <url href="http://csrc.nist.gov/publications/fips/fips186-3/fips_186-3.pdf"> DSA</url>- Digital Signature Algorithm</item>
+ <item>Supports <url href="http://www.rsa.com/rsalabs/node.asp?id=2126"> PKCS-3 </url> - Diffie-Hellman Key Agreement Standard </item>
+ <item>Supports <url href="http://www.rsa.com/rsalabs/node.asp?id=2127"> PKCS-5</url> - Password-Based Cryptography Standard </item>
+ <item>Supports <url href="http://www.rsa.com/rsalabs/node.asp?id=2130"> PKCS-8</url> - Private-Key Information Syntax Standard</item>
+ <item>Supports <url href="http://www.rsa.com/rsalabs/node.asp?id=2132"> PKCS-10</url> - Certification Request Syntax Standard</item>
+ </list>
+ </section>
+
+ <section>
<title>COMMON DATA TYPES </title>
<note><p>All records used in this manual
@@ -58,10 +76,13 @@
<p><code>boolean() = true | false</code></p>
- <p><code>string = [bytes()]</code></p>
+ <p><code>string() = [bytes()]</code></p>
+
+ <p><code>der_encoded() = binary()</code></p>
- <p><code>pki_asn1_type() = 'Certificate' | 'RSAPrivateKey'| 'RSAPublicKey'
- 'DSAPrivateKey' | 'DSAPublicKey' | 'DHParameter' | 'SubjectPublicKeyInfo'| 'PrivateKeyInfo'</code></p>
+ <p><code>pki_asn1_type() = 'Certificate' | 'RSAPrivateKey'| 'RSAPublicKey' |
+ 'DSAPrivateKey' | 'DSAPublicKey' | 'DHParameter' | 'SubjectPublicKeyInfo' |
+ 'PrivateKeyInfo' | 'CertificationRequest'</code></p>
<p><code>pem_entry () = {pki_asn1_type(), binary(), %% DER or encrypted DER
not_encrypted | cipher_info()} </code></p>
@@ -82,9 +103,12 @@
<p><code> rsa_padding() = 'rsa_pkcs1_padding' | 'rsa_pkcs1_oaep_padding'
| 'rsa_no_padding'</code></p>
- <p><code> rsa_digest_type() = 'md5' | 'sha' </code></p>
+ <p><code> rsa_digest_type() = 'md5' | 'sha' | 'sha224' | 'sha256' | 'sha384' | 'sha512' </code></p>
+
+ <p><code> dss_digest_type() = 'sha' </code></p>
- <p><code> dss_digest_type() = 'none' | 'sha' </code></p>
+ <p><code> crl_reason() = unspecified | keyCompromise | cACompromise | affiliationChanged | superseded | cessationOfOperation | certificateHold | privilegeWithdrawn | aACompromise
+ </code></p>
<p><code> ssh_file() = openssh_public_key | rfc4716_public_key |
known_hosts | auth_keys </code></p>
@@ -150,7 +174,7 @@
<func>
<name>der_decode(Asn1type, Der) -> term()</name>
- <fsummary> Decodes a public key asn1 der encoded entity.</fsummary>
+ <fsummary> Decodes a public key ASN.1 DER encoded entity.</fsummary>
<type>
<v>Asn1Type = atom()</v>
<d> ASN.1 type present in the public_key applications
@@ -158,7 +182,7 @@
<v>Der = der_encoded()</v>
</type>
<desc>
- <p> Decodes a public key ASN.1 der encoded entity.</p>
+ <p> Decodes a public key ASN.1 DER encoded entity.</p>
</desc>
</func>
@@ -180,14 +204,14 @@
<func>
<name>pem_decode(PemBin) -> [pem_entry()]</name>
<fsummary>Decode PEM binary data and return
- entries as ASN.1 der encoded entities. </fsummary>
+ entries as ASN.1 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 ASN.1 der encoded entities.</p>
+ entries as ASN.1 DER encoded entities.</p>
</desc>
</func>
@@ -211,8 +235,8 @@
<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
+ <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>
@@ -221,7 +245,7 @@
<func>
<name>pem_entry_encode(Asn1Type, Entity) -> pem_entry()</name>
<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>
+ <fsummary> Creates a PEM entry that can be fed to pem_encode/1.</fsummary>
<type>
<v>Asn1Type = pki_asn1_type()</v>
<v>Entity = term()</v>
@@ -235,7 +259,7 @@
<v>Password = string()</v>
</type>
<desc>
- <p> Creates a pem entry that can be feed to pem_encode/1.</p>
+ <p> Creates a PEM entry that can be feed to pem_encode/1.</p>
</desc>
</func>
@@ -265,12 +289,12 @@
<func>
<name>pkix_decode_cert(Cert, otp|plain) -> #'Certificate'{} | #'OTPCertificate'{}</name>
- <fsummary> Decodes an ASN.1 der encoded pkix x509 certificate.</fsummary>
+ <fsummary> Decodes an ASN.1 DER encoded PKIX x509 certificate.</fsummary>
<type>
<v>Cert = der_encoded()</v>
</type>
<desc>
- <p>Decodes an ASN.1 der encoded pkix certificate. The otp option
+ <p>Decodes an ASN.1 DER encoded PKIX certificate. The otp option
will use the customized ASN.1 specification OTP-PKIX.asn1 for
decoding and also recursively decode most of the standard
parts.</p>
@@ -279,14 +303,15 @@
<func>
<name>pkix_encode(Asn1Type, Entity, otp | plain) -> der_encoded()</name>
- <fsummary>Der encodes a pkix x509 certificate or part of such a
+ <fsummary>DER encodes a PKIX x509 certificate or part of such a
certificate.</fsummary>
<type>
<v>Asn1Type = atom()</v>
<d>The ASN.1 type can be 'Certificate', 'OTPCertificate' or a subtype of either .</d>
+ <v>Entity = #'Certificate'{} | #'OTPCertificate'{} | a valid subtype</v>
</type>
<desc>
- <p>Der encodes a pkix x509 certificate or part of such a
+ <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 in the otp format, whereas for the plain format this
function will directly call der_encode/2. </p>
@@ -356,18 +381,104 @@
</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_path_validation(TrustedCert, CertChain, Options) -> {ok, {PublicKeyInfo, PolicyTree}} | {error, {bad_cert, Reason}} </name>
+ <fsummary> Performs a basic path validation according to RFC 5280.</fsummary>
+ <type>
+ <v> TrustedCert = #'OTPCertificate'{} | der_encode() | unknown_ca | selfsigned_peer </v>
+ <d>Normally a trusted certificate but it can also be one of the path validation
+ errors <c>unknown_ca </c> or <c>selfsigned_peer </c> that can be discovered while
+ constructing the input to this function and that should be run through the <c>verify_fun</c>.</d>
+ <v> CertChain = [der_encode()]</v>
+ <d>A list of DER encoded certificates in trust order ending with the peer certificate.</d>
+ <v> Options = proplists:proplists()</v>
+ <v>PublicKeyInfo = {?'rsaEncryption' | ?'id-dsa',
+ rsa_public_key() | integer(), 'NULL' | 'Dss-Parms'{}}</v>
+ <v> PolicyTree = term() </v>
+ <d>At the moment this will always be an empty list as Policies are not currently supported</d>
+ <v> Reason = cert_expired | invalid_issuer | invalid_signature | unknown_ca |
+ selfsigned_peer | name_not_permitted | missing_basic_constraint | invalid_key_usage | crl_reason()
+ </v>
+ </type>
+ <desc>
+ <p>
+ Performs a basic path validation according to
+ <url href="http://www.ietf.org/rfc/rfc5280.txt">RFC 5280.</url>
+ However CRL validation is done separately by <seealso
+ marker="public_key#pkix_crls_validate-3">pkix_crls_validate/3 </seealso> and should be called
+ from the supplied <c>verify_fun</c>
+ </p>
+
+ <taglist>
+ <p> Available options are: </p>
+
+ <tag>{verify_fun, fun()}</tag>
+ <item>
+ <p>The 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>If the verify callback fun returns {fail, Reason}, the
+ verification process is immediately stopped. If the verify
+ callback fun returns {valid, UserState}, the verification
+ process is continued, this can be used to accept specific path
+ validation errors such as <c>selfsigned_peer</c> as well as
+ verifying application specific extensions. If called with an
+ extension unknown to the user application the return value
+ {unknown, UserState} should be used.</p>
+
+ </item>
+ <tag>{max_path_length, integer()}</tag>
+ <item>
+ The <c>max_path_length</c> is the maximum number of non-self-issued
+ intermediate certificates that may follow the peer certificate
+ in a valid certification path. So if <c>max_path_length</c> is 0 the PEER must
+ be signed by the trusted ROOT-CA directly, if 1 the path can
+ be PEER, CA, ROOT-CA, if it is 2 PEER, CA, CA, ROOT-CA and so
+ on.
+ </item>
+ </taglist>
+ </desc>
+ </func>
+
+ <func>
+ <name>pkix_crls_validate(OTPCertificate, DPAndCRLs, Options) -> CRLStatus()</name>
+ <fsummary> Performs CRL validation.</fsummary>
+ <type>
+ <v> OTPCertificate = #'OTPCertificate'{}</v>
+ <v> DPAndCRLs = [{DP::#'DistributionPoint'{} ,CRL::#'CertificateList'{}}] </v>
+ <v> Options = proplists:proplists()</v>
+ <v> CRLStatus() = valid | {bad_cert, revocation_status_undetermined} |
+ {bad_cert, {revoked, crl_reason()}}</v>
+ </type>
+ <desc>
+ <p> Performs CRL validation. It is intended to be called from
+ the verify fun of <seealso marker="public_key#pkix_path_validation-3"> pkix_path_validation/3
+ </seealso></p>
+ <taglist>
+ <p> Available options are: </p>
+ <tag>{update_crl, fun()}</tag>
+ <item>
+ <p>The fun has the following type spec:</p>
+
+ <code> fun(#'DistributionPoint'{}, #'CertificateList'{}) -> #'CertificateList'{}</code>
+
+ <p>The fun should use the information in the distribution point to acesses
+ the lates possible version of the CRL. If this fun is not specified
+ public_key will use the default implementation:
+ </p>
+ <code> fun(_DP, CRL) -> CRL end</code>
+ </item>
+ </taglist>
+ </desc>
+ </func>
-
<func>
<name>pkix_sign(#'OTPTBSCertificate'{}, Key) -> der_encode()</name>
<fsummary>Signs certificate.</fsummary>
@@ -388,7 +499,7 @@
<v>Key = rsa_public_key() | dsa_public_key()</v>
</type>
<desc>
- <p> Verify pkix x.509 certificate signature.</p>
+ <p> Verify PKIX x.509 certificate signature.</p>
</desc>
</func>
@@ -396,14 +507,14 @@
<name>sign(Msg, DigestType, Key) -> binary()</name>
<fsummary> Create digital signature.</fsummary>
<type>
- <v>Msg = binary()</v>
+ <v>Msg = binary() | {digest,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>
+ signed or it is the hashed value of "plain text" i.e. the
+ digest.</d>
+ <v>DigestType = rsa_digest_type() | dss_digest_type()</v>
<v>Key = rsa_private_key() | dsa_private_key()</v>
- </type>
- <desc>
+ </type>
+ <desc>
<p> Creates a digital signature.</p>
</desc>
</func>
@@ -461,11 +572,10 @@
<name>verify(Msg, DigestType, Signature, Key) -> boolean()</name>
<fsummary>Verifies a digital signature.</fsummary>
<type>
- <v>Msg = binary()</v>
+ <v>Msg = binary() | {digest,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>
+ or it is the hashed value of "plain text" i.e. the digest.</d>
+ <v>DigestType = rsa_digest_type() | dss_digest_type()</v>
<v>Signature = binary()</v>
<v>Key = rsa_public_key() | dsa_public_key()</v>
</type>
diff --git a/lib/public_key/doc/src/public_key_records.xml b/lib/public_key/doc/src/public_key_records.xml
index bb90290266..e39ad0ec64 100644
--- a/lib/public_key/doc/src/public_key_records.xml
+++ b/lib/public_key/doc/src/public_key_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>2011</year>
+ <year>2013</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -34,7 +34,7 @@
<file>public_key_records.xml</file>
</header>
- <p>This chapter briefly describes Erlang records derived from asn1
+ <p>This chapter briefly describes Erlang records derived from ASN1
specifications used to handle public and private keys. The intent
is to describe the data types and not to specify the meaning of
each component for this we refer you to the relevant standards and RFCs.</p>
@@ -67,9 +67,9 @@
}.
#'OtherPrimeInfo'{
- prime, % integer()
- exponent, % integer()
- coefficient % integer()
+ prime, % integer()
+ exponent, % integer()
+ coefficient % integer()
}.
</code>
diff --git a/lib/public_key/doc/src/using_public_key.xml b/lib/public_key/doc/src/using_public_key.xml
index f0eaeb8654..5d9f1536d9 100644
--- a/lib/public_key/doc/src/using_public_key.xml
+++ b/lib/public_key/doc/src/using_public_key.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2011</year><year>2011</year>
+ <year>2011</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -21,7 +21,7 @@
</legalnotice>
- <title>Using the public_key API</title>
+ <title>Getting Started</title>
<file>using_public_key.xml</file>
</header>
@@ -40,7 +40,7 @@
<section>
<title>PEM files</title>
- <p> Pulic key data (keys, certificates etc) may be stored in PEM format. PEM files
+ <p> Public key data (keys, certificates etc) may be stored in PEM format. PEM files
comes from the Private Enhanced Mail Internet standard and has a
structure that looks like this:</p>
@@ -63,7 +63,7 @@
<code>1> {ok, PemBin} = file:read_file("dsa.pem").
{ok,&lt;&lt;"-----BEGIN DSA PRIVATE KEY-----\nMIIBuw"...&gt;&gt;}</code>
- <p>This PEM file only has one entry a private DSA key.</p>
+ <p>This PEM file only has one entry, a private DSA key.</p>
<code>2> [DSAEntry] = public_key:pem_decode(PemBin).
[{'DSAPrivateKey',&lt;&lt;48,130,1,187,2,1,0,2,129,129,0,183,
179,230,217,37,99,144,157,21,228,204,
diff --git a/lib/public_key/include/public_key.hrl b/lib/public_key/include/public_key.hrl
index 2475295974..4d1d510f29 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-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -68,17 +68,32 @@
-record(revoke_state, {
reasons_mask,
cert_status,
- interim_reasons_mask
+ interim_reasons_mask,
+ valid_ext
}).
+
+-define(unspecified, 0).
+-define(keyCompromise, 1).
+-define(cACompromise, 2).
+-define(affiliationChanged, 3).
+-define(superseded, 4).
+-define(cessationOfOperation, 5).
+-define(certificateHold, 6).
+-define(removeFromCRL, 8).
+-define(privilegeWithdrawn, 9).
+-define(aACompromise, 10).
+
-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 der_encoded() :: binary().
+-type decrypt_der() :: binary().
-type pki_asn1_type() :: 'Certificate' | 'RSAPrivateKey' | 'RSAPublicKey'
| 'DSAPrivateKey' | 'DSAPublicKey' | 'DHParameter'
- | 'SubjectPublicKeyInfo'.
+ | 'SubjectPublicKeyInfo' | 'CertificationRequest' | 'CertificateList'.
-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
diff --git a/lib/public_key/src/Makefile b/lib/public_key/src/Makefile
index 8a72da477f..b8ad68ecc7 100644
--- a/lib/public_key/src/Makefile
+++ b/lib/public_key/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2008-2011. All Rights Reserved.
+# Copyright Ericsson AB 2008-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -44,7 +44,8 @@ MODULES = \
pubkey_ssh \
pubkey_pbe \
pubkey_cert \
- pubkey_cert_records
+ pubkey_cert_records \
+ pubkey_crl
HRL_FILES = $(INCLUDE)/public_key.hrl
@@ -91,10 +92,10 @@ clean:
docs:
$(APP_TARGET): $(APP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
# ----------------------------------------------------
# Release Target
diff --git a/lib/public_key/src/pubkey_cert.erl b/lib/public_key/src/pubkey_cert.erl
index b76e32a2a0..f53c94b334 100644
--- a/lib/public_key/src/pubkey_cert.erl
+++ b/lib/public_key/src/pubkey_cert.erl
@@ -26,10 +26,11 @@
-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_extensions/4,
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]).
+ verify_data/1, verify_fun/4, select_extension/2, match_name/3,
+ extensions_list/1, cert_auth_key_id/1, time_str_2_gregorian_sec/1]).
-define(NULL, 0).
@@ -204,17 +205,6 @@ validate_names(OtpCert, Permit, Exclude, Last, UserState, VerifyFun) ->
end.
%%--------------------------------------------------------------------
--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})
- UserState.
-%%--------------------------------------------------------------------
-spec validate_extensions(#'OTPCertificate'{}, #path_validation_state{},
term(), fun())->
{#path_validation_state{}, UserState :: term()}.
@@ -256,8 +246,10 @@ is_self_signed(#'OTPCertificate'{tbsCertificate=
%%
%% Description: Checks if <Issuer> issued <Candidate>.
%%--------------------------------------------------------------------
-is_issuer({rdnSequence, Issuer}, {rdnSequence, Candidate}) ->
- is_dir_name(Issuer, Candidate, true).
+is_issuer({rdnSequence, _} = Issuer, {rdnSequence, _} = Candidate) ->
+ {rdnSequence, IssuerDirName} = normalize_general_name(Issuer),
+ {rdnSequence, CandidateDirName} = normalize_general_name(Candidate),
+ is_dir_name(IssuerDirName, CandidateDirName, true).
%%--------------------------------------------------------------------
-spec issuer_id(#'OTPCertificate'{}, self | other) ->
{ok, {integer(), term()}} | {error, issuer_not_found}.
@@ -307,9 +299,9 @@ verify_fun(Otpcert, Result, UserState0, VerifyFun) ->
{valid,UserState} ->
UserState;
{fail, Reason} ->
- case Result of
+ case Reason of
{bad_cert, _} ->
- throw(Result);
+ throw(Reason);
_ ->
throw({bad_cert, Reason})
end;
@@ -321,6 +313,91 @@ verify_fun(Otpcert, Result, UserState0, VerifyFun) ->
UserState
end
end.
+%%--------------------------------------------------------------------
+-spec select_extension(Oid ::tuple(),[#'Extension'{}]) ->
+ #'Extension'{} | undefined.
+%%
+%% Description: Extracts a specific extension from a list of extensions.
+%%--------------------------------------------------------------------
+select_extension(_, []) ->
+ undefined;
+select_extension(Id, [#'Extension'{extnID = Id} = Extension | _]) ->
+ Extension;
+select_extension(Id, [_ | Extensions]) ->
+ select_extension(Id, Extensions).
+
+%%--------------------------------------------------------------------
+%% TODO:
+%%
+%% Description:
+%%--------------------------------------------------------------------
+match_name(rfc822Name, Name, [PermittedName | Rest]) ->
+ match_name(fun is_valid_host_or_domain/2, Name, PermittedName, Rest);
+
+match_name(directoryName, DirName, [PermittedName | Rest]) ->
+ match_name(fun is_rdnSeq/2, DirName, PermittedName, Rest);
+
+match_name(uniformResourceIdentifier, URI, [PermittedName | Rest]) ->
+ case split_uri(URI) of
+ incomplete ->
+ false;
+ {_, _, Host, _, _} ->
+ match_name(fun is_valid_host_or_domain/2, Host,
+ PermittedName, Rest)
+ end;
+
+match_name(emailAddress, Name, [PermittedName | Rest]) ->
+ Fun = fun(Email, PermittedEmail) ->
+ is_valid_email_address(Email, PermittedEmail,
+ string:tokens(PermittedEmail,"@"))
+ end,
+ match_name(Fun, Name, PermittedName, Rest);
+
+match_name(dNSName, Name, [PermittedName | Rest]) ->
+ Fun = fun(Domain, [$.|Domain]) -> true;
+ (Name1,Name2) ->
+ lists:suffix(string:to_lower(Name2),
+ string:to_lower(Name1))
+ end,
+ match_name(Fun, Name, [$.|PermittedName], Rest);
+
+match_name(x400Address, OrAddress, [PermittedAddr | Rest]) ->
+ match_name(fun is_or_address/2, OrAddress, PermittedAddr, Rest);
+
+match_name(ipAdress, IP, [PermittedIP | Rest]) ->
+ Fun = fun([IP1, IP2, IP3, IP4],
+ [IP5, IP6, IP7, IP8, M1, M2, M3, M4]) ->
+ is_permitted_ip([IP1, IP2, IP3, IP4],
+ [IP5, IP6, IP7, IP8],
+ [M1, M2, M3, M4]);
+ ([IP1, IP2, IP3, IP4, IP5, IP6, IP7, IP8,
+ IP9, IP10, IP11, IP12, IP13, IP14, IP15, IP16],
+ [IP17, IP18, IP19, IP20, IP21, IP22, IP23, IP24,
+ IP25, IP26, IP27, IP28, IP29, IP30, IP31, IP32,
+ M1, M2, M3, M4, M5, M6, M7, M8,
+ M9, M10, M11, M12, M13, M14, M15, M16]) ->
+ is_permitted_ip([IP1, IP2, IP3, IP4, IP5, IP6, IP7, IP8,
+ IP9, IP10, IP11, IP12, IP13,
+ IP14, IP15, IP16],
+ [IP17, IP18, IP19, IP20, IP21, IP22, IP23,
+ IP24,IP25, IP26, IP27, IP28, IP29, IP30,
+ IP31, IP32],
+ [M1, M2, M3, M4, M5, M6, M7, M8, M9, M10,
+ M11, M12, M13, M14, M15, M16]);
+ (_,_) ->
+ false
+ end,
+ match_name(Fun, IP, PermittedIP, Rest).
+
+match_name(Fun, Name, PermittedName, []) ->
+ Fun(Name, PermittedName);
+match_name(Fun, Name, PermittedName, [Head | Tail]) ->
+ case Fun(Name, PermittedName) of
+ true ->
+ true;
+ false ->
+ match_name(Fun, Name, Head, Tail)
+ end.
%%--------------------------------------------------------------------
%%% Internal functions
@@ -332,7 +409,7 @@ do_normalize_general_name(Issuer) ->
(Atter) ->
Atter
end,
- lists:sort(lists:map(Normalize, Issuer)).
+ lists:map(Normalize, Issuer).
%% See rfc3280 4.1.2.6 Subject: regarding emails.
extract_email({rdnSequence, List}) ->
@@ -376,8 +453,12 @@ encoded_tbs_cert(Cert) ->
digest_type(?sha1WithRSAEncryption) ->
sha;
+digest_type(?sha224WithRSAEncryption) ->
+ sha224;
digest_type(?sha256WithRSAEncryption) ->
sha256;
+digest_type(?sha384WithRSAEncryption) ->
+ sha384;
digest_type(?sha512WithRSAEncryption) ->
sha512;
digest_type(?md5WithRSAEncryption) ->
@@ -473,13 +554,6 @@ strip_spaces(String) ->
string:tokens(String, " ")),
string:strip(NewString).
-select_extension(_, []) ->
- undefined;
-select_extension(Id, [#'Extension'{extnID = Id} = Extension | _]) ->
- Extension;
-select_extension(Id, [_ | Extensions]) ->
- select_extension(Id, Extensions).
-
%% No extensions present
validate_extensions(OtpCert, asn1_NOVALUE, ValidationState, ExistBasicCon,
SelfSigned, UserState, VerifyFun) ->
@@ -499,18 +573,16 @@ validate_extensions(OtpCert, [], ValidationState =
true ->
{ValidationState#path_validation_state{max_path_length = Len - 1},
UserState0};
- %% basic_constraint must appear in certs used for digital sign
- %% see 4.2.1.10 in rfc 3280
false ->
- UserState = verify_fun(OtpCert, {bad_cert, missing_basic_constraint},
- UserState0, VerifyFun),
- case SelfSigned of
+ %% basic_constraint must appear in certs used for digital sign
+ %% see 4.2.1.10 in rfc 3280
+ case is_digitally_sign_cert(OtpCert) of
true ->
- {ValidationState, UserState};
- false ->
- {ValidationState#path_validation_state{max_path_length =
- Len - 1},
- UserState}
+ missing_basic_constraints(OtpCert, SelfSigned,
+ ValidationState, VerifyFun,
+ UserState0, Len);
+ false -> %% Example CRL signer only
+ {ValidationState, UserState0}
end
end;
@@ -857,74 +929,6 @@ type_subtree_names(Type, SubTrees) ->
[Name || #'GeneralSubtree'{base = {TreeType, Name}} <- SubTrees,
TreeType =:= Type].
-match_name(rfc822Name, Name, [PermittedName | Rest]) ->
- match_name(fun is_valid_host_or_domain/2, Name, PermittedName, Rest);
-
-match_name(directoryName, DirName, [PermittedName | Rest]) ->
- match_name(fun is_rdnSeq/2, DirName, PermittedName, Rest);
-
-match_name(uniformResourceIdentifier, URI, [PermittedName | Rest]) ->
- case split_uri(URI) of
- incomplete ->
- false;
- {_, _, Host, _, _} ->
- match_name(fun is_valid_host_or_domain/2, Host,
- PermittedName, Rest)
- end;
-
-match_name(emailAddress, Name, [PermittedName | Rest]) ->
- Fun = fun(Email, PermittedEmail) ->
- is_valid_email_address(Email, PermittedEmail,
- string:tokens(PermittedEmail,"@"))
- end,
- match_name(Fun, Name, PermittedName, Rest);
-
-match_name(dNSName, Name, [PermittedName | Rest]) ->
- Fun = fun(Domain, [$.|Domain]) -> true;
- (Name1,Name2) ->
- lists:suffix(string:to_lower(Name2),
- string:to_lower(Name1))
- end,
- match_name(Fun, Name, [$.|PermittedName], Rest);
-
-match_name(x400Address, OrAddress, [PermittedAddr | Rest]) ->
- match_name(fun is_or_address/2, OrAddress, PermittedAddr, Rest);
-
-match_name(ipAdress, IP, [PermittedIP | Rest]) ->
- Fun = fun([IP1, IP2, IP3, IP4],
- [IP5, IP6, IP7, IP8, M1, M2, M3, M4]) ->
- is_permitted_ip([IP1, IP2, IP3, IP4],
- [IP5, IP6, IP7, IP8],
- [M1, M2, M3, M4]);
- ([IP1, IP2, IP3, IP4, IP5, IP6, IP7, IP8,
- IP9, IP10, IP11, IP12, IP13, IP14, IP15, IP16],
- [IP17, IP18, IP19, IP20, IP21, IP22, IP23, IP24,
- IP25, IP26, IP27, IP28, IP29, IP30, IP31, IP32,
- M1, M2, M3, M4, M5, M6, M7, M8,
- M9, M10, M11, M12, M13, M14, M15, M16]) ->
- is_permitted_ip([IP1, IP2, IP3, IP4, IP5, IP6, IP7, IP8,
- IP9, IP10, IP11, IP12, IP13,
- IP14, IP15, IP16],
- [IP17, IP18, IP19, IP20, IP21, IP22, IP23,
- IP24,IP25, IP26, IP27, IP28, IP29, IP30,
- IP31, IP32],
- [M1, M2, M3, M4, M5, M6, M7, M8, M9, M10,
- M11, M12, M13, M14, M15, M16]);
- (_,_) ->
- false
- end,
- match_name(Fun, IP, PermittedIP, Rest).
-
-match_name(Fun, Name, PermittedName, []) ->
- Fun(Name, PermittedName);
-match_name(Fun, Name, PermittedName, [Head | Tail]) ->
- case Fun(Name, PermittedName) of
- true ->
- true;
- false ->
- match_name(Fun, Name, Head, Tail)
- end.
-
is_permitted_ip([], [], []) ->
true;
is_permitted_ip([CandidatIp | CandidatIpRest],
@@ -1033,3 +1037,25 @@ is_dh(?'dhpublicnumber')->
true;
is_dh(_) ->
false.
+
+is_digitally_sign_cert(OtpCert) ->
+ TBSCert = OtpCert#'OTPCertificate'.tbsCertificate,
+ Extensions = extensions_list(TBSCert#'OTPTBSCertificate'.extensions),
+ case pubkey_cert:select_extension(?'id-ce-keyUsage', Extensions) of
+ undefined ->
+ false;
+ #'Extension'{extnValue = KeyUse} ->
+ lists:member(keyCertSign, KeyUse)
+ end.
+
+missing_basic_constraints(OtpCert, SelfSigned, ValidationState, VerifyFun, UserState0,Len) ->
+ UserState = verify_fun(OtpCert, {bad_cert, missing_basic_constraint},
+ UserState0, VerifyFun),
+ case SelfSigned of
+ true ->
+ {ValidationState, UserState};
+ false ->
+ {ValidationState#path_validation_state{max_path_length =
+ Len - 1},
+ UserState}
+ end.
diff --git a/lib/public_key/src/pubkey_cert_records.erl b/lib/public_key/src/pubkey_cert_records.erl
index b86d7a1f0c..98004c71a3 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-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -57,6 +57,15 @@ transform(#'OTPTBSCertificate'{}= TBS, decode) ->
transform(#'AttributeTypeAndValue'{type=Id,value=Value0} = ATAV, Func) ->
{ok, Value} =
case attribute_type(Id) of
+ 'X520countryName'when Func == decode ->
+ %% Workaround that some certificates break the ASN-1 spec
+ %% and encode countryname as utf8
+ case 'OTP-PUB-KEY':Func('OTP-X520countryname', Value0) of
+ {ok, {utf8String, Utf8Value}} ->
+ {ok, unicode:characters_to_list(Utf8Value)};
+ {ok, {printableString, ASCCI}} ->
+ {ok, ASCCI}
+ end;
Type when is_atom(Type) -> 'OTP-PUB-KEY':Func(Type, Value0);
_UnknownType -> {ok, Value0}
end,
@@ -110,7 +119,7 @@ encode_supportedPublicKey(#'OTPSubjectPublicKeyInfo'{algorithm= PA =
subjectPublicKey = SPK0}) ->
Type = supportedPublicKeyAlgorithms(Algo),
{ok, SPK} = 'OTP-PUB-KEY':encode(Type, SPK0),
- #'OTPSubjectPublicKeyInfo'{subjectPublicKey = {0,list_to_binary(SPK)}, algorithm=PA}.
+ #'OTPSubjectPublicKeyInfo'{subjectPublicKey = {0,SPK}, algorithm=PA}.
%%% Extensions
@@ -152,7 +161,7 @@ decode_extensions(Exts) ->
case extension_id(Id) of
undefined -> Ext;
Type ->
- {ok, Value} = 'OTP-PUB-KEY':decode(Type, list_to_binary(Value0)),
+ {ok, Value} = 'OTP-PUB-KEY':decode(Type, iolist_to_binary(Value0)),
Ext#'Extension'{extnValue=transform(Value,decode)}
end
end, Exts).
@@ -167,7 +176,7 @@ encode_extensions(Exts) ->
Type ->
Value1 = transform(Value0,encode),
{ok, Value} = 'OTP-PUB-KEY':encode(Type, Value1),
- Ext#'Extension'{extnValue=list_to_binary(Value)}
+ Ext#'Extension'{extnValue=Value}
end
end, Exts).
diff --git a/lib/public_key/src/pubkey_crl.erl b/lib/public_key/src/pubkey_crl.erl
new file mode 100644
index 0000000000..3e4c3c8b6d
--- /dev/null
+++ b/lib/public_key/src/pubkey_crl.erl
@@ -0,0 +1,701 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, 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_crl).
+
+-include("public_key.hrl").
+
+-export([validate/7, init_revokation_state/0, fresh_crl/3, verify_crl_signature/4,
+ is_delta_crl/1, combines/2, match_one/2]).
+
+-record(userstate, {dpcrls,
+ idp
+ }).
+
+validate(OtpCert, OtherDPCRLs, DP, {DerCRL, CRL}, {DerDeltaCRL, DeltaCRL},
+ Options, RevokedState0) ->
+ RevokedState =
+ case verify_crl(OtpCert, DP, CRL, DerCRL, DeltaCRL,
+ DerDeltaCRL, OtherDPCRLs, Options, RevokedState0) of
+ {valid, Revoked, DeltaRevoked, RevokedState1, IDP} ->
+ TBSCert = OtpCert#'OTPCertificate'.tbsCertificate,
+ SerialNumber = TBSCert#'OTPTBSCertificate'.serialNumber,
+ CertIssuer = TBSCert#'OTPTBSCertificate'.issuer,
+ TBSCRL = CRL#'CertificateList'.tbsCertList,
+ CRLIssuer = TBSCRL#'TBSCertList'.issuer,
+ AltNames = subject_alt_names(TBSCert#'OTPTBSCertificate'.extensions),
+ revoked_status(DP, IDP, {directoryName, CRLIssuer},
+ [ {directoryName, CertIssuer} | AltNames], SerialNumber, Revoked,
+ DeltaRevoked, RevokedState1);
+ {invalid, Revoked} ->
+ Revoked
+ end,
+ crl_status(RevokedState).
+
+init_revokation_state() ->
+ #revoke_state{reasons_mask = sets:new(),
+ interim_reasons_mask = sets:new(),
+ cert_status = unrevoked}.
+
+fresh_crl(_, {undefined, undefined}, _) ->
+ %% Typically happens when there is no delta CRL that covers a CRL
+ no_fresh_crl;
+
+fresh_crl(DP, {_, #'CertificateList'{tbsCertList = TBSCRL}} = CRL, CallBack) ->
+ Now = calendar:datetime_to_gregorian_seconds(calendar:universal_time()),
+ UpdateTime =
+ pubkey_cert:time_str_2_gregorian_sec(TBSCRL#'TBSCertList'.nextUpdate),
+ case Now >= UpdateTime of
+ true ->
+ case CallBack(DP, CRL) of
+ CRL ->
+ no_fresh_crl;
+ NewCRL ->
+ fresh_crl(DP, NewCRL, CallBack)
+ end;
+ false ->
+ {fresh, CRL}
+ end.
+
+is_delta_crl(#'CertificateList'{tbsCertList = TBSCRL}) ->
+ Extensions = TBSCRL#'TBSCertList'.crlExtensions,
+ case pubkey_cert:select_extension(?'id-ce-deltaCRLIndicator',
+ Extensions) of
+ undefined ->
+ false;
+ _ ->
+ true
+ end.
+
+combines(CRL, DeltaCRL) ->
+ check_crl_num(CRL, DeltaCRL) andalso
+ check_delta_issuer_and_scope(CRL, DeltaCRL).
+
+crl_status(State)->
+ %% Fun argument is to enable future implementation of CRL checking
+ %% that does not care about all possible reasons.
+ crl_status(State, fun all_reasons/0).
+
+crl_status({skip, #revoke_state{cert_status = Status} = RevokedState}, _) ->
+ {undetermined, status(Status), RevokedState};
+
+crl_status(#revoke_state{cert_status = unrevoked = Status,
+ valid_ext = false} = RevokedState, _) ->
+ {undetermined, Status, RevokedState};
+
+crl_status(#revoke_state{cert_status = Status,
+ valid_ext = false}, _) ->
+ {finished, status(Status)};
+
+crl_status(#revoke_state{reasons_mask = Mask,
+ cert_status = Status,
+ valid_ext = true} = RevokedState, Fun) ->
+ case is_all_reasons(Mask, Fun) of
+ true ->
+ {finished, status(Status)};
+ false when (Status == unrevoked) ->
+ {undetermined, Status, RevokedState};
+ _ ->
+ {finished ,status(Status)}
+ end.
+
+verify_crl(OtpCert, DP, CRL, DerCRL, DeltaCRL, DerDeltaCRL, OtherDPCRLs,
+ Options, State0) ->
+ #'CertificateList'{tbsCertList =
+ #'TBSCertList'{crlExtensions = Extensions,
+ revokedCertificates = TmpRevoked}
+ } = CRL,
+ Revoked = revoked(TmpRevoked),
+ IDP = issuing_distribution_point(Extensions),
+
+ DeltaRevoked = delta_revoked(DeltaCRL),
+
+ ValidExt = verify_extensions(Extensions) and
+ verify_extensions(Revoked),
+
+ IntMask = compute_interim_reasons_mask(DP, IDP),
+
+ RevokedState =
+ State0#revoke_state{interim_reasons_mask = IntMask,
+ valid_ext = ValidExt},
+
+ {Fun, AdditionalArgs} = IssuerFun = proplists:get_value(issuer_fun, Options),
+
+ try verify_issuer_and_scope(OtpCert, DP, IDP, CRL) of
+ {ok, Issuer} ->
+ case Fun(DP, CRL, Issuer, AdditionalArgs) of
+ {ok, TrustedOtpCert, Path} ->
+ verify_mask_and_signatures(Revoked, DeltaRevoked,
+ RevokedState,
+ CRL, DerCRL, DeltaCRL, DerDeltaCRL,
+ IssuerFun, TrustedOtpCert, Path, OtherDPCRLs, IDP);
+ _ ->
+ {invalid, State0#revoke_state{valid_ext = ValidExt}}
+ end;
+ {error, issuer_not_found} ->
+ case Fun(DP, CRL, issuer_not_found, AdditionalArgs) of
+ {ok, TrustedOtpCert, Path} ->
+ verify_mask_and_signatures(Revoked, DeltaRevoked,
+ RevokedState, CRL, DerCRL, DeltaCRL,
+ DerDeltaCRL, IssuerFun,
+ TrustedOtpCert, Path, OtherDPCRLs, IDP);
+ _ ->
+ {invalid, {skip, State0}}
+ end
+ catch
+ throw:{bad_crl, invalid_issuer} ->
+ {invalid, {skip, State0}};
+ throw:_ ->
+ {invalid, State0#revoke_state{valid_ext = ValidExt}}
+ end.
+
+verify_mask_and_signatures(Revoked, DeltaRevoked, RevokedState, CRL, DerCRL, DeltaCRL, DerDeltaCRL,
+ IssuerFun, TrustedOtpCert, Path, OtherDPCRLs, IDP) ->
+
+ ReasonsMask = sets:union(RevokedState#revoke_state.reasons_mask,
+ RevokedState#revoke_state.interim_reasons_mask),
+ try
+ verify_interim_reasons_mask(RevokedState),
+ true = verify_crl_signatures(CRL, DerCRL, DeltaCRL, DerDeltaCRL,
+ TrustedOtpCert, Path, IssuerFun, OtherDPCRLs, IDP),
+ {valid, Revoked, DeltaRevoked, RevokedState#revoke_state{reasons_mask = ReasonsMask}, IDP}
+ catch
+ throw:_ ->
+ {invalid, RevokedState};
+ error:{badmatch, _} ->
+ {invalid, RevokedState}
+ end.
+
+
+verify_crl_signatures(CRL, DerCRL, DeltaCRL, DerDeltaCRL, TrustedOtpCert, Path,
+ IssuerFun, OtherDPCRLs, IDP) ->
+ try
+ VerifyFunAndState =
+ {fun(_, {bad_cert, _} = Reason, _UserState) ->
+ {fail, Reason};
+ (_,{extension, _}, UserState) ->
+ {unknown, UserState};
+ (_Cert, valid, UserState) ->
+ {valid, UserState};
+ (Cert, valid_peer, UserState) ->
+ case verify_crl_keybit(Cert, cRLSign) of
+ true ->
+ handle_crlsigner(Cert, IssuerFun, UserState);
+ false ->
+ {fail, crl_sign_bit_not_set}
+ end
+ end, #userstate{dpcrls = OtherDPCRLs, idp = IDP}},
+
+ {ok, {{_,Key, KeyParams},_}} =
+ public_key:pkix_path_validation(TrustedOtpCert, Path,
+ [{verify_fun, VerifyFunAndState}]),
+ true = verify_crl_signature(CRL, DerCRL, Key, KeyParams),
+ true = verify_crl_signature(DeltaCRL, DerDeltaCRL, Key, KeyParams)
+ catch
+ error:{badmatch, _} ->
+ false
+ end.
+
+handle_crlsigner(OtpCert, IssuerFun, #userstate{idp = IDP} = UserState) ->
+ case verify_crl_keybit(OtpCert, keyCertSign) of
+ true ->
+ {valid, UserState};
+ false ->
+ case not is_indirect_crl(IDP) andalso not public_key:pkix_is_self_signed(OtpCert) of
+ true ->
+ validate_crl_signing_cert(OtpCert, IssuerFun, UserState);
+ false ->
+ {valid, UserState}
+ end
+ end.
+
+validate_crl_signing_cert(_, _,#userstate{dpcrls = []} = UserState) ->
+ {valid, UserState};
+validate_crl_signing_cert(OtpCert, IssuerFun, #userstate{dpcrls = CRLInfo} = UserState) ->
+ case public_key:pkix_crls_validate(OtpCert, CRLInfo, [{issuer_fun, IssuerFun}]) of
+ valid ->
+ {valid, UserState};
+ Reason ->
+ {fail, Reason}
+ end.
+
+delta_revoked(undefined)->
+ [];
+delta_revoked(#'CertificateList'{tbsCertList =
+ #'TBSCertList'{revokedCertificates
+ = DeltaRevoked}}) ->
+ revoked(DeltaRevoked).
+
+revoked(asn1_NOVALUE) ->
+ [];
+revoked(Revoked) ->
+ Revoked.
+
+revoked_status(DP, IDP, CRLIssuer, Names, SerialNumber, Revoked, DeltaRevoked, RevokedState0) ->
+ DefaultIssuer0 = default_issuer(CRLIssuer, DeltaRevoked),
+ RevokedState1 = check_revoked(DP, IDP, DefaultIssuer0, Names, SerialNumber, DeltaRevoked, RevokedState0),
+ RevokedState = case RevokedState1#revoke_state.cert_status of
+ unrevoked when RevokedState1#revoke_state.cert_status =/= removeFromCRL ->
+ DefaultIssuer = default_issuer(CRLIssuer, Revoked),
+ check_revoked(DP, IDP, DefaultIssuer, Names, SerialNumber,
+ Revoked, RevokedState1);
+ _ ->
+ RevokedState1
+ end,
+ case RevokedState#revoke_state.cert_status of
+ removeFromCRL ->
+ RevokedState#revoke_state{cert_status = unrevoked};
+ _ ->
+ RevokedState
+ end.
+
+default_issuer(_, []) ->
+ undefined;
+default_issuer(Default, [#'TBSCertList_revokedCertificates_SEQOF'{crlEntryExtensions = Extensions}| _]) ->
+ case extension_value(?'id-ce-certificateIssuer', 'GeneralNames', Extensions) of
+ undefined ->
+ [pubkey_cert_records:transform(Default, decode)];
+ GeneralNames ->
+ gen_names(GeneralNames)
+ end.
+
+is_all_reasons(Mask, AllReasonsFun) ->
+ AllReasons = AllReasonsFun(),
+ case sets:is_subset(AllReasons, Mask) of
+ true ->
+ true;
+ false ->
+ %% As the "uspecified" reason should not
+ %% be explicitly used according to RFC 3280
+ %% and the conformance tests have test cases
+ %% that should succed, and that does not specify
+ %% "unspecified", we tolorate that it is not included.
+ sets:is_subset(sets:del_element(unspecified, AllReasons), Mask)
+ end.
+
+all_reasons() ->
+ sets:from_list([unspecified, keyCompromise,
+ cACompromise, affiliationChanged, superseded,
+ cessationOfOperation, certificateHold,
+ privilegeWithdrawn, aACompromise]).
+
+verify_issuer_and_scope(#'OTPCertificate'{tbsCertificate = TBSCert} = Cert,
+ #'DistributionPoint'{cRLIssuer = DPIssuer} = DP, IDP,
+ #'CertificateList'{tbsCertList = TBSCRL} = CRL)
+ when DPIssuer =/= asn1_NOVALUE ->
+ CRLIssuer = pubkey_cert_records:transform(TBSCRL#'TBSCertList'.issuer, decode),
+ Issuer = dp_crlissuer_to_issuer(DPIssuer),
+ case pubkey_cert:is_issuer(Issuer, CRLIssuer) and is_indirect_crl(IDP) of
+ true ->
+ verify_scope(Cert, DP, IDP),
+ issuer_id(Cert, CRL);
+ false ->
+ %% otherwise verify that the CRL issuer matches the certificate issuer
+ verify_issuer_and_scope(Cert, DP#'DistributionPoint'{
+ distributionPoint = [TBSCert#'OTPTBSCertificate'.issuer],
+ cRLIssuer = asn1_NOVALUE},
+ IDP, CRL)
+ end;
+verify_issuer_and_scope(#'OTPCertificate'{tbsCertificate = TBSCert}= Cert,
+ DP, IDP,
+ #'CertificateList'{tbsCertList = TBSCRL}) ->
+ CRLIssuer = pubkey_cert_records:transform(TBSCRL#'TBSCertList'.issuer, decode),
+ CertIssuer = TBSCert#'OTPTBSCertificate'.issuer,
+ case pubkey_cert:is_issuer(CertIssuer, CRLIssuer) of
+ true ->
+ verify_scope(Cert, DP, IDP),
+ issuer_id(Cert);
+ false ->
+ throw({bad_crl, invalid_issuer})
+ end.
+
+dp_crlissuer_to_issuer(DPCRLIssuer) ->
+ [{directoryName, Issuer}] = pubkey_cert_records:transform(DPCRLIssuer, decode),
+ Issuer.
+
+is_indirect_crl(#'IssuingDistributionPoint'{indirectCRL = Value})->
+ Value;
+is_indirect_crl(_) ->
+ false.
+
+verify_scope(_,_, undefined) ->
+ ok;
+verify_scope(#'OTPCertificate'{tbsCertificate = TBSCert}, #'DistributionPoint'{cRLIssuer = DPIssuer} = DP, IDP) ->
+ CertIssuer = TBSCert#'OTPTBSCertificate'.issuer,
+ Names = case gen_names(DPIssuer) of
+ [{directoryName, TNames}] ->
+ TNames;
+ Other ->
+ Other
+ end,
+ DPName = dp_names(DP#'DistributionPoint'.distributionPoint, Names, CertIssuer),
+ IDPName = dp_names(IDP#'IssuingDistributionPoint'.distributionPoint, Names, CertIssuer),
+ verify_scope(DPName, IDPName, Names, TBSCert, IDP).
+
+verify_scope(asn1_NOVALUE, _, asn1_NOVALUE, _, _) ->
+ throw({bad_crl, scope_error1});
+verify_scope(asn1_NOVALUE, IDPName, DPIssuerNames, TBSCert, IDP) ->
+ verify_dp_name(IDPName, DPIssuerNames),
+ verify_dp_bools(TBSCert, IDP);
+
+verify_scope(DPName, IDPName, _, TBSCert, IDP) ->
+ verify_dp_name(IDPName, DPName),
+ verify_dp_bools(TBSCert, IDP).
+
+dp_names(asn1_NOVALUE, _, _) ->
+ asn1_NOVALUE;
+dp_names({fullName, Name}, _, _) ->
+ gen_names(Name);
+dp_names({nameRelativeToCRLIssuer, Fragment}, asn1_NOVALUE, {rdnSequence, RelativeDestinguistNames}) ->
+ [{directoryName, {rdnSequence, RelativeDestinguistNames ++
+ [lists:map(fun(AttrAndValue) ->
+ pubkey_cert_records:transform(AttrAndValue, decode)
+ end, Fragment)]}}];
+dp_names({nameRelativeToCRLIssuer, Fragment},{rdnSequence, RelativeDestinguistNames}, _) ->
+ [{directoryName, {rdnSequence, RelativeDestinguistNames ++
+ [lists:map(fun(AttrAndValue) ->
+ pubkey_cert_records:transform(AttrAndValue, decode)
+ end, Fragment)]}}];
+dp_names([{rdnSequence, _}] = Name0, _,_) ->
+ [Name] = pubkey_cert_records:transform(Name0, decode),
+ [{directoryName, Name}].
+
+gen_names(asn1_NOVALUE) ->
+ asn1_NOVALUE;
+gen_names([]) ->
+ [];
+gen_names([{NameType, Name} | Rest]) ->
+ [ {NameType, pubkey_cert_records:transform(Name, decode)} | gen_names(Rest)].
+
+verify_dp_name(asn1_NOVALUE, _) ->
+ ok;
+
+verify_dp_name(IDPNames, DPorIssuerNames) ->
+ case match_one(DPorIssuerNames, IDPNames) of
+ true ->
+ ok;
+ false ->
+ throw({bad_crl, scope_error})
+ end.
+
+match_one([], _) ->
+ false;
+match_one([{Type, Name} | Names], CandidateNames) ->
+ Candidates = [NameName || {NameType, NameName} <- CandidateNames, NameType == Type],
+ case Candidates of
+ [] ->
+ false;
+ [_|_] -> case pubkey_cert:match_name(Type, Name, Candidates) of
+ true ->
+ true;
+ false ->
+ match_one(Names, CandidateNames)
+ end
+ end.
+
+verify_dp_bools(TBSCert, IDP) ->
+ BasicConstraints =
+ pubkey_cert:select_extension(?'id-ce-basicConstraints',
+ TBSCert#'OTPTBSCertificate'.extensions),
+
+ case verify_onlyContainsUserCerts(BasicConstraints, IDP) andalso
+ verify_onlyContainsCACerts(BasicConstraints, IDP) andalso
+ verify_onlyContainsAttributeCerts(IDP) of
+ true ->
+ ok;
+ _ ->
+ throw({bad_crl, scope_error})
+ end.
+
+verify_onlyContainsUserCerts(
+ #'Extension'{extnValue = #'BasicConstraints'{cA = true}},
+ #'IssuingDistributionPoint'{onlyContainsUserCerts = true}) ->
+ false;
+verify_onlyContainsUserCerts(_,_) ->
+ true.
+
+verify_onlyContainsCACerts(
+ #'Extension'{extnValue = #'BasicConstraints'{cA = true}},
+ #'IssuingDistributionPoint'{onlyContainsCACerts = true}) ->
+ true;
+verify_onlyContainsCACerts(_,#'IssuingDistributionPoint'{onlyContainsCACerts = true}) ->
+ false;
+verify_onlyContainsCACerts(_,_) ->
+ true.
+
+verify_onlyContainsAttributeCerts(
+ #'IssuingDistributionPoint'{onlyContainsAttributeCerts = Bool}) ->
+ not Bool.
+
+check_crl_num(#'CertificateList'{tbsCertList = TBSCRL},
+ #'CertificateList'{tbsCertList = TBSDeltaCRL})->
+ Extensions = TBSCRL#'TBSCertList'.crlExtensions,
+ DeltaExtensions = TBSDeltaCRL#'TBSCertList'.crlExtensions,
+
+ try
+ CRLNum = assert_extension_value(?'id-ce-cRLNumber', 'CRLNumber', Extensions),
+ DeltaBaseNum = assert_extension_value(?'id-ce-deltaCRLIndicator',
+ 'CRLNumber', DeltaExtensions),
+ DeltaCRLNum = assert_extension_value(?'id-ce-cRLNumber', 'CRLNumber', DeltaExtensions),
+ (CRLNum >= DeltaBaseNum) andalso (CRLNum < DeltaCRLNum)
+ catch
+ throw:no_extension_present ->
+ false
+ end;
+check_crl_num(_,_) ->
+ false.
+
+
+extension_value(Extension, ExtType, Extensions) ->
+ case pubkey_cert:select_extension(Extension, Extensions) of
+ #'Extension'{extnValue = Value} ->
+ public_key:der_decode(ExtType, list_to_binary(Value));
+ _ ->
+ undefined
+ end.
+
+
+assert_extension_value(Extension, ExtType, Extensions) ->
+ case extension_value(Extension, ExtType, Extensions) of
+ undefined ->
+ throw(no_extension_present);
+ Value ->
+ Value
+ end.
+
+check_delta_issuer_and_scope(_, undefined) ->
+ true;
+check_delta_issuer_and_scope(#'CertificateList'{tbsCertList = TBSCRL},
+ #'CertificateList'{tbsCertList = TBSDeltaCRL}) ->
+ case pubkey_cert:is_issuer(TBSCRL#'TBSCertList'.issuer,
+ TBSDeltaCRL#'TBSCertList'.issuer) of
+ true ->
+ check_delta_scope(TBSCRL, TBSDeltaCRL);
+ false ->
+ false
+ end.
+
+check_delta_scope(#'TBSCertList'{crlExtensions = Extensions},
+ #'TBSCertList'{crlExtensions = DeltaExtensions})->
+ IDP = issuing_distribution_point(Extensions),
+ DeltaIDP = issuing_distribution_point(DeltaExtensions),
+
+ AuthKey = authority_key_identifier(Extensions),
+ DeltaAuthKey = authority_key_identifier(DeltaExtensions),
+ is_match(IDP, DeltaIDP) andalso is_match(AuthKey, DeltaAuthKey).
+
+is_match(X, X) ->
+ true;
+is_match(_,_) ->
+ false.
+
+compute_interim_reasons_mask(#'DistributionPoint'{reasons = asn1_NOVALUE},
+ #'IssuingDistributionPoint'{onlySomeReasons =
+ asn1_NOVALUE}) ->
+ all_reasons();
+compute_interim_reasons_mask(#'DistributionPoint'{reasons = asn1_NOVALUE},
+ undefined) ->
+ all_reasons();
+
+compute_interim_reasons_mask(#'DistributionPoint'{reasons = asn1_NOVALUE},
+ #'IssuingDistributionPoint'{onlySomeReasons =
+ IDPReasons}) ->
+ sets:from_list(IDPReasons);
+compute_interim_reasons_mask(#'DistributionPoint'{reasons = DPReasons},
+ #'IssuingDistributionPoint'{onlySomeReasons =
+ asn1_NOVALUE}) ->
+ sets:from_list(DPReasons);
+compute_interim_reasons_mask(#'DistributionPoint'{reasons = DPReasons},
+ undefined) ->
+ sets:from_list(DPReasons);
+compute_interim_reasons_mask(#'DistributionPoint'{reasons = DPReasons},
+ #'IssuingDistributionPoint'{onlySomeReasons =
+ IDPReasons}) ->
+ sets:intersection(sets:from_list(DPReasons), sets:from_list(IDPReasons)).
+
+verify_interim_reasons_mask(#revoke_state{reasons_mask = Mask,
+ interim_reasons_mask = IntMask}) ->
+ case sets:fold(fun(Element, Acc) ->
+ case sets:is_element(Element, Mask) of
+ true ->
+ Acc;
+ false ->
+ true
+ end
+ end, false, IntMask) of
+ true ->
+ ok;
+ false ->
+ throw({bad_crl, mask_error})
+ end.
+
+verify_crl_signature(undefined, undefined, _,_) ->
+ true;
+verify_crl_signature(CRL, DerCRL, Key, KeyParams) ->
+ {DigestType, PlainText, Signature} = extract_crl_verify_data(CRL, DerCRL),
+ case Key of
+ #'RSAPublicKey'{} ->
+ public_key:verify(PlainText, DigestType, Signature, Key);
+ _ ->
+ public_key:verify(PlainText, DigestType, Signature,
+ {Key, KeyParams})
+ end.
+extract_crl_verify_data(CRL, DerCRL) ->
+ {0, Signature} = CRL#'CertificateList'.signature,
+ #'AlgorithmIdentifier'{algorithm = SigAlg} =
+ CRL#'CertificateList'.signatureAlgorithm,
+ PlainText = encoded_tbs_crl(DerCRL),
+ DigestType = pubkey_cert:digest_type(SigAlg),
+ {DigestType, PlainText, Signature}.
+
+encoded_tbs_crl(CRL) ->
+ {ok, PKIXCRL} =
+ 'OTP-PUB-KEY':decode_TBSCertList_exclusive(CRL),
+ {'CertificateList',
+ {'CertificateList_tbsCertList', EncodedTBSCertList}, _, _} = PKIXCRL,
+ EncodedTBSCertList.
+
+check_revoked(_,_,_,_,_,[], State) ->
+ State;
+check_revoked(#'DistributionPoint'{cRLIssuer = DPIssuer} = DP, IDP, DefaultIssuer0, Names, SerialNr,
+ [#'TBSCertList_revokedCertificates_SEQOF'{userCertificate =
+ SerialNr,
+ crlEntryExtensions =
+ Extensions}| Rest],
+ State) ->
+ Reason = revoked_reason(Extensions),
+ case (DPIssuer =/= asn1_NOVALUE) and is_indirect_crl(IDP) of
+ true ->
+ handle_indirect_crl_check(DP, IDP, DefaultIssuer0, Names, SerialNr, Extensions, Reason, Rest, State);
+ false ->
+ State#revoke_state{cert_status = Reason}
+ end;
+
+check_revoked(DP, IDP, DefaultIssuer0, Names, SerialNr,
+ [#'TBSCertList_revokedCertificates_SEQOF'{crlEntryExtensions =
+ Extensions}| Rest], State) ->
+ DefaultIssuer = case extension_value(?'id-ce-certificateIssuer', 'GeneralNames', Extensions) of
+ undefined ->
+ DefaultIssuer0;
+ GeneralNames ->
+ gen_names(GeneralNames)
+ end,
+ check_revoked(DP, IDP, DefaultIssuer, Names, SerialNr, Rest, State).
+
+handle_indirect_crl_check(DP, IDP, DefaultIssuer0, Names, SerialNr, Extensions, Reason, Rest, State) ->
+ case check_crl_issuer_extension(Names, Extensions, DefaultIssuer0) of
+ {true, _} ->
+ State#revoke_state{cert_status = Reason};
+ {false, DefaultIssuer} ->
+ check_revoked(DP, IDP, DefaultIssuer, Names, SerialNr, Rest, State)
+ end.
+
+check_crl_issuer_extension(Names, Extensions, Default0) ->
+ case extension_value(?'id-ce-certificateIssuer', 'GeneralNames', Extensions) of
+ undefined ->
+ {match_one(Default0, Names), Default0};
+ GeneralNames ->
+ Default = gen_names(GeneralNames),
+ {match_one(Default, Names), Default}
+ end.
+
+revoked_reason(Extensions) ->
+ case extension_value(?'id-ce-cRLReasons', 'CRLReason', Extensions) of
+ undefined ->
+ unspecified;
+ Value ->
+ Value
+ end.
+
+verify_crl_keybit(#'OTPCertificate'{tbsCertificate = TBS}, Bit) ->
+ case pubkey_cert:select_extension( ?'id-ce-keyUsage',
+ TBS#'OTPTBSCertificate'.extensions) of
+ #'Extension'{extnID = ?'id-ce-keyUsage',
+ extnValue = KeyUse} ->
+ lists:member(Bit, KeyUse);
+ _ ->
+ true
+ end.
+
+issuer_id(Cert, #'CertificateList'{tbsCertList = TBSCRL}) ->
+ Extensions =
+ pubkey_cert:extensions_list(TBSCRL#'TBSCertList'.crlExtensions),
+ case authority_key_identifier(Extensions) of
+ undefined ->
+ issuer_id(Cert);
+ #'AuthorityKeyIdentifier'{authorityCertIssuer = asn1_NOVALUE,
+ authorityCertSerialNumber = asn1_NOVALUE} ->
+ issuer_id(Cert);
+ #'AuthorityKeyIdentifier'{authorityCertIssuer = Issuer,
+ authorityCertSerialNumber = Nr} ->
+ {ok, {Nr, Issuer}}
+ end.
+
+issuer_id(#'OTPCertificate'{} = Cert) ->
+ case public_key:pkix_is_self_signed(Cert) of
+ true ->
+ public_key:pkix_issuer_id(Cert, self);
+ false ->
+ public_key:pkix_issuer_id(Cert, other)
+ end.
+
+status(unrevoked) ->
+ unrevoked;
+status(Reason) ->
+ {revoked, Reason}.
+
+verify_extensions([#'TBSCertList_revokedCertificates_SEQOF'{crlEntryExtensions = Ext} | Rest]) ->
+ verify_extensions(pubkey_cert:extensions_list(Ext)) and verify_extensions(Rest);
+verify_extensions([]) ->
+ true;
+verify_extensions([#'Extension'{critical = true, extnID = Id} | Rest]) ->
+ case lists:member(Id, [?'id-ce-authorityKeyIdentifier',
+ ?'id-ce-issuerAltName',
+ ?'id-ce-cRLNumber',
+ ?'id-ce-certificateIssuer',
+ ?'id-ce-deltaCRLIndicator',
+ ?'id-ce-issuingDistributionPoint',
+ ?'id-ce-freshestCRL']) of
+ true ->
+ verify_extensions(Rest);
+ false ->
+ false
+ end;
+verify_extensions([_Ext | Rest]) ->
+ verify_extensions(Rest).
+
+issuing_distribution_point(Extensions) ->
+ Enc = extension_value(?'id-ce-issuingDistributionPoint',
+ 'IssuingDistributionPoint', Extensions),
+ pubkey_cert_records:transform(Enc, decode).
+
+authority_key_identifier(Extensions) ->
+ Enc = extension_value(?'id-ce-authorityKeyIdentifier',
+ 'AuthorityKeyIdentifier', Extensions),
+ pubkey_cert_records:transform(Enc, decode).
+
+subject_alt_names(Extensions) ->
+ Enc = extension_value(?'id-ce-subjectAltName',
+ 'GeneralNames', Extensions),
+ case Enc of
+ undefined ->
+ [];
+ _ ->
+ pubkey_cert_records:transform(Enc, decode)
+ end.
diff --git a/lib/public_key/src/pubkey_pem.erl b/lib/public_key/src/pubkey_pem.erl
index 910473d629..6bdc35fb79 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-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -167,6 +167,8 @@ split_lines(Bin) ->
%% Ignore white space at end of line
join_entry([<<"-----END ", _/binary>>| Lines], Entry) ->
{lists:reverse(Entry), Lines};
+join_entry([<<"-----END X509 CRL-----", _/binary>>| Lines], Entry) ->
+ {lists:reverse(Entry), Lines};
join_entry([Line | Lines], Entry) ->
join_entry(Lines, [Line | Entry]).
@@ -194,7 +196,14 @@ pem_start('SubjectPublicKeyInfo') ->
pem_start('DSAPrivateKey') ->
<<"-----BEGIN DSA PRIVATE KEY-----">>;
pem_start('DHParameter') ->
- <<"-----BEGIN DH PARAMETERS-----">>.
+ <<"-----BEGIN DH PARAMETERS-----">>;
+pem_start('CertificationRequest') ->
+ <<"-----BEGIN CERTIFICATE REQUEST-----">>;
+pem_start('ContentInfo') ->
+ <<"-----BEGIN PKCS7-----">>;
+pem_start('CertificateList') ->
+ <<"-----BEGIN X509 CRL-----">>.
+
pem_end(<<"-----BEGIN CERTIFICATE-----">>) ->
<<"-----END CERTIFICATE-----">>;
pem_end(<<"-----BEGIN RSA PRIVATE KEY-----">>) ->
@@ -211,6 +220,12 @@ pem_end(<<"-----BEGIN PRIVATE KEY-----">>) ->
<<"-----END PRIVATE KEY-----">>;
pem_end(<<"-----BEGIN ENCRYPTED PRIVATE KEY-----">>) ->
<<"-----END ENCRYPTED PRIVATE KEY-----">>;
+pem_end(<<"-----BEGIN CERTIFICATE REQUEST-----">>) ->
+ <<"-----END CERTIFICATE REQUEST-----">>;
+pem_end(<<"-----BEGIN PKCS7-----">>) ->
+ <<"-----END PKCS7-----">>;
+pem_end(<<"-----BEGIN X509 CRL-----">>) ->
+ <<"-----END X509 CRL-----">>;
pem_end(_) ->
undefined.
@@ -229,7 +244,13 @@ asn1_type(<<"-----BEGIN DH PARAMETERS-----">>) ->
asn1_type(<<"-----BEGIN PRIVATE KEY-----">>) ->
'PrivateKeyInfo';
asn1_type(<<"-----BEGIN ENCRYPTED PRIVATE KEY-----">>) ->
- 'EncryptedPrivateKeyInfo'.
+ 'EncryptedPrivateKeyInfo';
+asn1_type(<<"-----BEGIN CERTIFICATE REQUEST-----">>) ->
+ 'CertificationRequest';
+asn1_type(<<"-----BEGIN PKCS7-----">>) ->
+ 'ContentInfo';
+asn1_type(<<"-----BEGIN X509 CRL-----">>) ->
+ 'CertificateList'.
pem_decrypt() ->
<<"Proc-Type: 4,ENCRYPTED">>.
diff --git a/lib/public_key/src/pubkey_ssh.erl b/lib/public_key/src/pubkey_ssh.erl
index f0c94e29a5..008ea96dd3 100644
--- a/lib/public_key/src/pubkey_ssh.erl
+++ b/lib/public_key/src/pubkey_ssh.erl
@@ -47,7 +47,7 @@ decode(Bin, public_key)->
rfc4716_decode(Bin)
end;
decode(Bin, rfc4716_public_key) ->
- rfc4716_decode(Bin);
+ rfc4716_decode(Bin);
decode(Bin, Type) ->
openssh_decode(Bin, Type).
@@ -58,7 +58,7 @@ decode(Bin, Type) ->
%% Description: Encodes a list of ssh file entries.
%%--------------------------------------------------------------------
encode(Entries, Type) ->
- erlang:iolist_to_binary(lists:map(fun({Key, Attributes}) ->
+ iolist_to_binary(lists:map(fun({Key, Attributes}) ->
do_encode(Type, Key, Attributes)
end, Entries)).
@@ -106,7 +106,7 @@ rfc4716_decode_line(Line, Lines, Acc) ->
_ ->
{Body, Rest} = join_entry([Line | Lines], []),
{lists:reverse(Acc), rfc4716_pubkey_decode(base64:mime_decode(Body)), Rest}
- end.
+ end.
join_entry([<<"---- END SSH2 PUBLIC KEY ----", _/binary>>| Lines], Entry) ->
{lists:reverse(Entry), Lines};
@@ -115,16 +115,16 @@ join_entry([Line | Lines], Entry) ->
rfc4716_pubkey_decode(<<?UINT32(Len), Type:Len/binary,
- ?UINT32(SizeE), E:SizeE/binary,
- ?UINT32(SizeN), N:SizeN/binary>>) when Type == <<"ssh-rsa">> ->
+ ?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">> ->
+ ?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),
@@ -143,94 +143,63 @@ do_openssh_decode(FileType, [<<>> | Lines], Acc) ->
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
- [KeyType, Base64Enc, Comment] ->
+ case decode_auth_keys(Line) of
+ {ssh2, {options, [Options, KeyType, Base64Enc| Comment]}} ->
do_openssh_decode(FileType, Lines,
- [{openssh_pubkey_decode(KeyType, Base64Enc),
- [{comment, string_decode(Comment)}]} | Acc]);
- %% ssh1
- [Options, Bits, Exponent, Modulus, Comment] ->
+ [{openssh_pubkey_decode(KeyType, Base64Enc),
+ decode_comment(Comment) ++ [{options, comma_list_decode(Options)}]} | Acc]);
+ {ssh2, {no_options, [KeyType, Base64Enc| Comment]}} ->
+ do_openssh_decode(FileType, Lines,
+ [{openssh_pubkey_decode(KeyType, Base64Enc),
+ decode_comment(Comment)} | Acc]);
+ {ssh1, {options, [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]);
- [A, B, C, D] ->
- ssh_2_or_1(FileType, Lines, Acc, A,B,C,D)
+ decode_comment(Comment) ++ [{options, comma_list_decode(Options)},
+ {bits, integer_decode(Bits)}]
+ } | Acc]);
+ {ssh1, {no_options, [Bits, Exponent, Modulus | Comment]}} ->
+ do_openssh_decode(FileType, Lines,
+ [{ssh1_rsa_pubkey_decode(Modulus, Exponent),
+ decode_comment(Comment) ++ [{bits, integer_decode(Bits)}]
+ } | Acc])
end;
do_openssh_decode(known_hosts = FileType, [Line | Lines], Acc) ->
- Split = binary:split(Line, <<" ">>, [global]),
- case mend_split(Split, []) of
- %% ssh 2
- [HostNames, KeyType, Base64Enc] ->
+ case decode_known_hosts(Line) of
+ {ssh2, [HostNames, KeyType, Base64Enc| Comment]} ->
do_openssh_decode(FileType, Lines,
- [{openssh_pubkey_decode(KeyType, Base64Enc),
- [{hostnames, comma_list_decode(HostNames)}]}| Acc]);
- [A, B, C, D] ->
- ssh_2_or_1(FileType, Lines, Acc, A, B, C, D);
- %% ssh 1
- [HostNames, Bits, Exponent, Modulus, Comment] ->
+ [{openssh_pubkey_decode(KeyType, Base64Enc),
+ decode_comment(Comment) ++
+ [{hostnames, comma_list_decode(HostNames)}]}| Acc]);
+ {ssh1, [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])
- end;
+ [{ssh1_rsa_pubkey_decode(Modulus, Exponent),
+ decode_comment(Comment) ++
+ [{hostnames, comma_list_decode(HostNames)},
+ {bits, integer_decode(Bits)}]}
+ | Acc])
+ end;
do_openssh_decode(openssh_public_key = FileType, [Line | Lines], Acc) ->
- Split = binary:split(Line, <<" ">>, [global]),
- case mend_split(Split, []) of
- [KeyType, Base64Enc, Comment0] when KeyType == <<"ssh-rsa">>;
- KeyType == <<"ssh-dss">> ->
- Comment = string:strip(binary_to_list(Comment0), right, $\n),
+ case split_n(2, Line, []) of
+ [KeyType, Base64Enc] when KeyType == <<"ssh-rsa">>;
+ KeyType == <<"ssh-dss">> ->
+ do_openssh_decode(FileType, Lines,
+ [{openssh_pubkey_decode(KeyType, Base64Enc),
+ []} | Acc]);
+ [KeyType, Base64Enc | Comment0] when KeyType == <<"ssh-rsa">>;
+ KeyType == <<"ssh-dss">> ->
+ Comment = string:strip(string_decode(iolist_to_binary(Comment0)), right, $\n),
do_openssh_decode(FileType, Lines,
[{openssh_pubkey_decode(KeyType, Base64Enc),
[{comment, Comment}]} | Acc])
end.
-ssh_2_or_1(known_hosts = FileType, Lines, Acc, A, B, C, D) ->
- try integer_decode(B) of
- Int ->
- file_type_decode_ssh1(FileType, Lines, Acc, A, Int, C,D)
- catch
- error:badarg ->
- file_type_decode_ssh2(FileType, Lines, Acc, A,B,C,D)
- end;
-ssh_2_or_1(auth_keys = FileType, Lines, Acc, A, B, C, D) ->
- try integer_decode(A) of
- Int ->
- file_type_decode_ssh1(FileType, Lines, Acc, Int, B, C,D)
- catch
- error:badarg ->
- file_type_decode_ssh2(FileType, Lines, Acc, A,B,C,D)
- end.
-
-file_type_decode_ssh1(known_hosts = FileType, Lines, Acc, HostNames, Bits, Exponent, Modulus) ->
- do_openssh_decode(FileType, Lines,
- [{ssh1_rsa_pubkey_decode(Modulus, Exponent),
- [{comment, []},
- {hostnames, comma_list_decode(HostNames)},
- {bits, Bits}]} | Acc]);
-file_type_decode_ssh1(auth_keys = FileType, Lines, Acc, Bits, Exponent, Modulus, Comment) ->
- do_openssh_decode(FileType, Lines,
- [{ssh1_rsa_pubkey_decode(Modulus, Exponent),
- [{comment, string_decode(Comment)},
- {bits, Bits}]} | Acc]).
-
-file_type_decode_ssh2(known_hosts = FileType, Lines, Acc, HostNames, KeyType, Base64Enc, Comment) ->
- do_openssh_decode(FileType, Lines,
- [{openssh_pubkey_decode(KeyType, Base64Enc),
- [{comment, string_decode(Comment)},
- {hostnames, comma_list_decode(HostNames)}]} | Acc]);
-file_type_decode_ssh2(auth_keys = FileType, Lines, Acc, Options, KeyType, Base64Enc, Comment) ->
- do_openssh_decode(FileType, Lines,
- [{openssh_pubkey_decode(KeyType, Base64Enc),
- [{comment, string_decode(Comment)},
- {options, comma_list_decode(Options)}]}
- | Acc]).
+decode_comment([]) ->
+ [];
+decode_comment(Comment) ->
+ [{comment, string_decode(iolist_to_binary(Comment))}].
openssh_pubkey_decode(<<"ssh-rsa">>, Base64Enc) ->
<<?UINT32(StrLen), _:StrLen/binary,
@@ -267,7 +236,7 @@ integer_decode(BinStr) ->
list_to_integer(binary_to_list(BinStr)).
string_decode(BinStr) ->
- binary_to_list(BinStr).
+ unicode_decode(BinStr).
unicode_decode(BinStr) ->
unicode:characters_to_list(BinStr).
@@ -285,11 +254,11 @@ do_encode(Type, Key, Attributes) ->
openssh_encode(Type, Key, Attributes).
rfc4716_encode(Key, [],[]) ->
- erlang:iolist_to_binary([begin_marker(),"\n",
+ 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",
+ iolist_to_binary([begin_marker(), "\n",
lists:reverse(Acc),
split_lines(base64:encode(ssh2_pubkey_encode(Key))),
"\n", end_marker(), "\n"]);
@@ -319,9 +288,9 @@ rfc4716_encode_value(Value) ->
end.
openssh_encode(openssh_public_key, Key, Attributes) ->
- Comment = proplists:get_value(comment, Attributes),
+ Comment = proplists:get_value(comment, Attributes, ""),
Enc = base64:encode(ssh2_pubkey_encode(Key)),
- erlang:iolist_to_binary([key_type(Key), " ", Enc, " ", Comment, "\n"]);
+ iolist_to_binary([key_type(Key), " ", Enc, " ", Comment, "\n"]);
openssh_encode(auth_keys, Key, Attributes) ->
Comment = proplists:get_value(comment, Attributes, ""),
@@ -345,30 +314,30 @@ openssh_encode(known_hosts, Key, Attributes) ->
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)]);
+ 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, []), " ",
+ 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),
+ 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),
+ 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, []), " ",
+ 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)," ",
+ #'RSAPublicKey'{modulus = N, publicExponent = E},
+ Comment) ->
+ iolist_to_binary([comma_list_encode(Hostnames, [])," ", integer_to_list(Bits)," ",
integer_to_list(E)," ", integer_to_list(N), line_end(Comment)]).
line_end("") ->
@@ -411,24 +380,6 @@ ssh2_pubkey_encode({Y, #'Dss-Parms'{p = P, q = Q, g = G}}) ->
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">>) ->
@@ -456,3 +407,72 @@ split_lines(<<Text:?ENCODED_LINE_LENGTH/binary, Rest/binary>>) ->
[Text, $\n | split_lines(Rest)];
split_lines(Bin) ->
[Bin].
+
+decode_auth_keys(Line) ->
+ [First, Rest] = binary:split(Line, <<" ">>, []),
+ case is_key_field(First) of
+ true ->
+ {ssh2, decode_auth_keys_ssh2(First, Rest)};
+ false ->
+ case is_bits_field(First) of
+ true ->
+ {ssh1, decode_auth_keys_ssh1(First, Rest)};
+ false ->
+ decode_auth_keys(First, Rest)
+ end
+ end.
+
+decode_auth_keys(First, Line) ->
+ [Second, Rest] = binary:split(Line, <<" ">>, []),
+ case is_key_field(Second) of
+ true ->
+ {ssh2, decode_auth_keys_ssh2(First, Second, Rest)};
+ false ->
+ case is_bits_field(Second) of
+ true ->
+ {ssh1, decode_auth_keys_ssh1(First, Second, Rest)};
+ false ->
+ decode_auth_keys(<<First/binary, Second/binary>>, Rest)
+ end
+ end.
+
+decode_auth_keys_ssh2(KeyType, Rest) ->
+ {no_options, [KeyType | split_n(1, Rest, [])]}.
+
+decode_auth_keys_ssh2(Options, Next, Rest) ->
+ {options, [Options, Next | split_n(1, Rest, [])]}.
+
+decode_auth_keys_ssh1(Options, Next, Rest) ->
+ {options, [Options, Next | split_n(2, Rest, [])]}.
+
+decode_auth_keys_ssh1(First, Rest) ->
+ {no_options, [First | split_n(2, Rest, [])]}.
+
+decode_known_hosts(Line) ->
+ [First, Rest] = binary:split(Line, <<" ">>, []),
+ [Second, Rest1] = binary:split(Rest, <<" ">>, []),
+
+ case is_bits_field(Second) of
+ true ->
+ {ssh1, decode_known_hosts_ssh1(First, Second, Rest1)};
+ false ->
+ {ssh2, decode_known_hosts_ssh2(First, Second, Rest1)}
+ end.
+
+decode_known_hosts_ssh1(Hostnames, Bits, Rest) ->
+ [Hostnames, Bits | split_n(2, Rest, [])].
+
+decode_known_hosts_ssh2(Hostnames, KeyType, Rest) ->
+ [Hostnames, KeyType | split_n(1, Rest, [])].
+
+split_n(0, <<>>, Acc) ->
+ lists:reverse(Acc);
+split_n(0, Bin, Acc) ->
+ lists:reverse([Bin | Acc]);
+split_n(N, Bin, Acc) ->
+ case binary:split(Bin, <<" ">>, []) of
+ [First, Rest] ->
+ split_n(N-1, Rest, [First | Acc]);
+ [Last] ->
+ split_n(0, <<>>, [Last | Acc])
+ end.
diff --git a/lib/public_key/src/public_key.app.src b/lib/public_key/src/public_key.app.src
index 4cc81ea573..9f0677606d 100644
--- a/lib/public_key/src/public_key.app.src
+++ b/lib/public_key/src/public_key.app.src
@@ -7,6 +7,7 @@
pubkey_ssh,
pubkey_cert,
pubkey_cert_records,
+ pubkey_crl,
'OTP-PUB-KEY',
'PKCS-FRAME'
]},
diff --git a/lib/public_key/src/public_key.erl b/lib/public_key/src/public_key.erl
index 9f1a0b3af5..9b7d98728f 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-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -42,14 +42,17 @@
pkix_issuer_id/2,
pkix_normalize_name/1,
pkix_path_validation/3,
- ssh_decode/2, ssh_encode/2
+ ssh_decode/2, ssh_encode/2,
+ pkix_crls_validate/3
]).
-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'| 'sha256' | 'sha512'.
--type dss_digest_type() :: 'none' | 'sha'.
+-type rsa_digest_type() :: 'md5' | 'sha'| 'sha224' | 'sha256' | 'sha384' | 'sha512'.
+-type dss_digest_type() :: 'none' | 'sha'. %% None is for backwards compatibility
+-type crl_reason() :: unspecified | keyCompromise | cACompromise | affiliationChanged | superseded
+ | cessationOfOperation | certificateHold | privilegeWithdrawn | aACompromise.
-define(UINT32(X), X:32/unsigned-big-integer).
-define(DER_NULL, <<5, 0>>).
@@ -332,60 +335,61 @@ format_rsa_private_key(#'RSAPrivateKey'{modulus = N, publicExponent = E,
[crypto:mpint(K) || K <- [E, N, D]].
%%--------------------------------------------------------------------
--spec sign(PlainTextOrDigest :: binary(), rsa_digest_type() | dss_digest_type(),
- rsa_private_key() |
+-spec sign(binary() | {digest, binary()}, rsa_digest_type() | dss_digest_type(),
+ rsa_private_key() |
dsa_private_key()) -> Signature :: binary().
-%%
%% Description: Create digital signature.
%%--------------------------------------------------------------------
-sign(PlainText, DigestType,
- #'RSAPrivateKey'{modulus = N, publicExponent = E, privateExponent = D} = Key)
- when is_binary(PlainText),
- (DigestType == md5 orelse DigestType == sha),
- is_integer(N), is_integer(E), is_integer(D) ->
- crypto:rsa_sign(DigestType, sized_binary(PlainText),
- format_rsa_private_key(Key));
-
-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),
+sign({digest,_}=Digest, DigestType, Key = #'RSAPrivateKey'{}) ->
+ crypto:rsa_sign(DigestType, Digest, format_rsa_private_key(Key));
+
+sign(PlainText, DigestType, Key = #'RSAPrivateKey'{}) ->
+ crypto:rsa_sign(DigestType, sized_binary(PlainText), format_rsa_private_key(Key));
+
+sign({digest,_}=Digest, sha, #'DSAPrivateKey'{p = P, q = Q, g = G, x = X}) ->
+ crypto:dss_sign(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)]).
+
+sign(PlainText, sha, #'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)]);
+
+%% Backwards compatible
+sign(Digest, none, #'DSAPrivateKey'{} = Key) ->
+ sign({digest,Digest}, sha, Key).
%%--------------------------------------------------------------------
--spec verify(PlainTextOrDigest :: binary(), rsa_digest_type() | dss_digest_type(),
- Signature :: binary(), rsa_public_key()
+-spec verify(binary() | {digest, 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) and (DigestType == sha orelse
- DigestType == sha256 orelse
- DigestType == sha512 orelse
- DigestType == md5) ->
+verify({digest,_}=Digest, DigestType, Signature,
+ #'RSAPublicKey'{modulus = Mod, publicExponent = Exp}) ->
+ crypto:rsa_verify(DigestType, Digest,
+ sized_binary(Signature),
+ [crypto:mpint(Exp), crypto:mpint(Mod)]);
+
+verify(PlainText, DigestType, Signature,
+ #'RSAPublicKey'{modulus = Mod, publicExponent = Exp}) ->
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),
+verify({digest,_}=Digest, sha, Signature, {Key, #'Dss-Parms'{p = P, q = Q, g = G}})
+ when is_integer(Key), is_binary(Signature) ->
+ crypto:dss_verify(Digest, sized_binary(Signature),
[crypto:mpint(P), crypto:mpint(Q),
crypto:mpint(G), crypto:mpint(Key)]);
-
+%% Backwards compatibility
+verify(Digest, none, Signature, {_, #'Dss-Parms'{}} = Key ) ->
+ verify({digest,Digest}, sha, Signature, Key);
+
verify(PlainText, sha, Signature, {Key, #'Dss-Parms'{p = P, q = Q, g = G}})
- when is_integer(Key), is_binary(PlainText), is_binary(Signature) ->
+ 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),
@@ -427,9 +431,9 @@ pkix_verify(DerCert, #'RSAPublicKey'{} = RSAKey)
verify(PlainText, DigestType, Signature, RSAKey).
%%--------------------------------------------------------------------
--spec pkix_is_issuer(Cert::binary()| #'OTPCertificate'{},
- IssuerCert::binary()|
- #'OTPCertificate'{}) -> boolean().
+-spec pkix_is_issuer(Cert :: der_encoded()| #'OTPCertificate'{} | #'CertificateList'{},
+ IssuerCert :: der_encoded()|
+ #'OTPCertificate'{}) -> boolean().
%%
%% Description: Checks if <IssuerCert> issued <Cert>.
%%--------------------------------------------------------------------
@@ -442,7 +446,11 @@ pkix_is_issuer(Cert, IssuerCert) when is_binary(IssuerCert) ->
pkix_is_issuer(#'OTPCertificate'{tbsCertificate = TBSCert},
#'OTPCertificate'{tbsCertificate = Candidate}) ->
pubkey_cert:is_issuer(TBSCert#'OTPTBSCertificate'.issuer,
- Candidate#'OTPTBSCertificate'.subject).
+ Candidate#'OTPTBSCertificate'.subject);
+pkix_is_issuer(#'CertificateList'{tbsCertList = TBSCRL},
+ #'OTPCertificate'{tbsCertificate = Candidate}) ->
+ pubkey_cert:is_issuer(Candidate#'OTPTBSCertificate'.subject,
+ pubkey_cert_records:transform(TBSCRL#'TBSCertList'.issuer, decode)).
%%--------------------------------------------------------------------
-spec pkix_is_self_signed(Cert::binary()| #'OTPCertificate'{}) -> boolean().
@@ -501,7 +509,7 @@ pkix_normalize_name(Issuer) ->
%%--------------------------------------------------------------------
-spec pkix_path_validation(Cert::binary()| #'OTPCertificate'{} | atom(),
CertChain :: [binary()] ,
- Options :: list()) ->
+ Options :: proplists:proplist()) ->
{ok, {PublicKeyInfo :: term(),
PolicyTree :: term()}} |
{error, {bad_cert, Reason :: term()}}.
@@ -510,7 +518,7 @@ pkix_normalize_name(Issuer) ->
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),
+ Otpcert = otp_cert(Cert),
Reason = {bad_cert, PathErr},
try VerifyFun(Otpcert, Reason, Userstat0) of
{valid, Userstate} ->
@@ -536,6 +544,27 @@ pkix_path_validation(#'OTPCertificate'{} = TrustedCert, CertChain, Options)
Options),
path_validation(CertChain, ValidationState).
+%--------------------------------------------------------------------
+-spec pkix_crls_validate(#'OTPCertificate'{},
+ [{DP::#'DistributionPoint'{} ,CRL::#'CertificateList'{}}],
+ Options :: proplists:proplist()) -> valid | {bad_cert, revocation_status_undetermined}
+ | {bad_cert, {revoked, crl_reason()}}.
+
+%% Description: Performs a basic path validation according to RFC 5280.
+%%--------------------------------------------------------------------
+pkix_crls_validate(OtpCert, [{_,_,_} |_] = DPAndCRLs, Options) ->
+ pkix_crls_validate(OtpCert, DPAndCRLs, DPAndCRLs,
+ Options, pubkey_crl:init_revokation_state());
+
+pkix_crls_validate(OtpCert, DPAndCRLs0, Options) ->
+ CallBack = proplists:get_value(update_crl, Options, fun(_, CurrCRL) ->
+ CurrCRL
+ end),
+ DPAndCRLs = sort_dp_crls(DPAndCRLs0, CallBack),
+ pkix_crls_validate(OtpCert, DPAndCRLs, DPAndCRLs,
+ Options, pubkey_crl:init_revokation_state()).
+
+
%%--------------------------------------------------------------------
-spec ssh_decode(binary(), public_key | ssh_file()) -> [{public_key(), Attributes::list()}].
%%
@@ -610,12 +639,13 @@ path_validation([DerCert | Rest], ValidationState = #path_validation_state{
{error, Reason}
end;
-path_validation([DerCert | _] = Path,
+path_validation([Cert | _] = Path,
#path_validation_state{user_state = UserState0,
verify_fun = VerifyFun} =
ValidationState) ->
Reason = {bad_cert, max_path_length_reached},
- OtpCert = pkix_decode_cert(DerCert, otp),
+ OtpCert = otp_cert(Cert),
+
try VerifyFun(OtpCert, Reason, UserState0) of
{valid, UserState} ->
path_validation(Path,
@@ -629,7 +659,7 @@ path_validation([DerCert | _] = Path,
{error, Reason}
end.
-validate(DerCert, #path_validation_state{working_issuer_name = Issuer,
+validate(Cert, #path_validation_state{working_issuer_name = Issuer,
working_public_key = Key,
working_public_key_parameters =
KeyParams,
@@ -640,31 +670,31 @@ validate(DerCert, #path_validation_state{working_issuer_name = Issuer,
verify_fun = VerifyFun} =
ValidationState0) ->
- OtpCert = pkix_decode_cert(DerCert, otp),
+ OtpCert = otp_cert(Cert),
- UserState1 = pubkey_cert:validate_time(OtpCert, UserState0, VerifyFun),
+ {ValidationState1, UserState1} =
+ pubkey_cert:validate_extensions(OtpCert, ValidationState0, UserState0,
+ VerifyFun),
- UserState2 = pubkey_cert:validate_issuer(OtpCert, Issuer, UserState1, VerifyFun),
+ %% We want the key_usage extension to be checked before we validate
+ %% other things so that CRL validation errors will comply to standard
+ %% test suite description
- UserState3 = pubkey_cert:validate_names(OtpCert, Permit, Exclude, Last,
- UserState2,VerifyFun),
+ UserState2 = pubkey_cert:validate_time(OtpCert, UserState1, VerifyFun),
- UserState4 = pubkey_cert:validate_revoked_status(OtpCert, UserState3, VerifyFun),
-
- {ValidationState1, UserState5} =
- pubkey_cert:validate_extensions(OtpCert, ValidationState0, UserState4,
- VerifyFun),
+ UserState3 = pubkey_cert:validate_issuer(OtpCert, Issuer, UserState2, VerifyFun),
- %% We want the key_usage extension to be checked before we validate
- %% the signature.
- UserState6 = pubkey_cert:validate_signature(OtpCert, DerCert,
- Key, KeyParams, UserState5, VerifyFun),
+ UserState4 = pubkey_cert:validate_names(OtpCert, Permit, Exclude, Last,
+ UserState3, VerifyFun),
+
+ UserState5 = pubkey_cert:validate_signature(OtpCert, der_cert(Cert),
+ Key, KeyParams, UserState4, VerifyFun),
UserState = case Last of
false ->
- pubkey_cert:verify_fun(OtpCert, valid, UserState6, VerifyFun);
+ pubkey_cert:verify_fun(OtpCert, valid, UserState5, VerifyFun);
true ->
pubkey_cert:verify_fun(OtpCert, valid_peer,
- UserState6, VerifyFun)
+ UserState5, VerifyFun)
end,
ValidationState =
@@ -675,3 +705,110 @@ validate(DerCert, #path_validation_state{working_issuer_name = Issuer,
sized_binary(Binary) ->
Size = size(Binary),
<<?UINT32(Size), Binary/binary>>.
+
+otp_cert(Der) when is_binary(Der) ->
+ pkix_decode_cert(Der, otp);
+otp_cert(#'OTPCertificate'{} =Cert) ->
+ Cert.
+
+der_cert(#'OTPCertificate'{} = Cert) ->
+ pkix_encode('OTPCertificate', Cert, otp);
+der_cert(Der) when is_binary(Der) ->
+ Der.
+
+pkix_crls_validate(_, [],_, _, _) ->
+ {bad_cert, revocation_status_undetermined};
+pkix_crls_validate(OtpCert, [{DP, CRL, DeltaCRL} | Rest], All, Options, RevokedState0) ->
+ CallBack = proplists:get_value(update_crl, Options, fun(_, CurrCRL) ->
+ CurrCRL
+ end),
+ case pubkey_crl:fresh_crl(DP, CRL, CallBack) of
+ {fresh, CRL} ->
+ do_pkix_crls_validate(OtpCert, [{DP, CRL, DeltaCRL} | Rest],
+ All, Options, RevokedState0);
+ {fresh, NewCRL} ->
+ NewAll = [{DP, NewCRL, DeltaCRL} | All -- [{DP, CRL, DeltaCRL}]],
+ do_pkix_crls_validate(OtpCert, [{DP, NewCRL, DeltaCRL} | Rest],
+ NewAll, Options, RevokedState0);
+ no_fresh_crl ->
+ pkix_crls_validate(OtpCert, Rest, All, Options, RevokedState0)
+ end.
+
+do_pkix_crls_validate(OtpCert, [{DP, CRL, DeltaCRL} | Rest], All, Options, RevokedState0) ->
+ OtherDPCRLs = All -- [{DP, CRL, DeltaCRL}],
+ case pubkey_crl:validate(OtpCert, OtherDPCRLs, DP, CRL, DeltaCRL, Options, RevokedState0) of
+ {undetermined, _, _} when Rest == []->
+ {bad_cert, revocation_status_undetermined};
+ {undetermined, _, RevokedState} when Rest =/= []->
+ pkix_crls_validate(OtpCert, Rest, All, Options, RevokedState);
+ {finished, unrevoked} ->
+ valid;
+ {finished, Status} ->
+ {bad_cert, Status}
+ end.
+
+sort_dp_crls(DpsAndCrls, FreshCB) ->
+ Sorted = do_sort_dp_crls(DpsAndCrls, dict:new()),
+ sort_crls(Sorted, FreshCB, []).
+
+do_sort_dp_crls([], Dict) ->
+ dict:to_list(Dict);
+do_sort_dp_crls([{DP, CRL} | Rest], Dict0) ->
+ Dict = try dict:fetch(DP, Dict0) of
+ _ ->
+ dict:append(DP, CRL, Dict0)
+ catch _:_ ->
+ dict:store(DP, [CRL], Dict0)
+ end,
+ do_sort_dp_crls(Rest, Dict).
+
+sort_crls([], _, Acc) ->
+ Acc;
+
+sort_crls([{DP, AllCRLs} | Rest], FreshCB, Acc)->
+ {DeltaCRLs, CRLs} = do_sort_crls(AllCRLs),
+ DpsAndCRLs = combine(CRLs, DeltaCRLs, DP, FreshCB, []),
+ sort_crls(Rest, FreshCB, DpsAndCRLs ++ Acc).
+
+do_sort_crls(CRLs) ->
+ lists:partition(fun({_, CRL}) ->
+ pubkey_crl:is_delta_crl(CRL)
+ end, CRLs).
+
+combine([], _,_,_,Acc) ->
+ Acc;
+combine([{_, CRL} = Entry | CRLs], DeltaCRLs, DP, FreshCB, Acc) ->
+ DeltaCRL = combine(CRL, DeltaCRLs),
+ case pubkey_crl:fresh_crl(DP, DeltaCRL, FreshCB) of
+ no_fresh_crl ->
+ combine(CRLs, DeltaCRLs, DP, FreshCB, [{DP, Entry, {undefined, undefined}} | Acc]);
+ {fresh, NewDeltaCRL} ->
+ combine(CRLs, DeltaCRLs, DP, FreshCB, [{DP, Entry, NewDeltaCRL} | Acc])
+ end.
+
+combine(CRL, DeltaCRLs) ->
+ Deltas = lists:filter(fun({_,DeltaCRL}) ->
+ pubkey_crl:combines(CRL, DeltaCRL)
+ end, DeltaCRLs),
+ case Deltas of
+ [] ->
+ {undefined, undefined};
+ [Delta] ->
+ Delta;
+ [_,_|_] ->
+ Fun =
+ fun({_, #'CertificateList'{tbsCertList = FirstTBSCRL}} = CRL1,
+ {_, #'CertificateList'{tbsCertList = SecondTBSCRL}} = CRL2) ->
+ Time1 = pubkey_cert:time_str_2_gregorian_sec(
+ FirstTBSCRL#'TBSCertList'.thisUpdate),
+ Time2 = pubkey_cert:time_str_2_gregorian_sec(
+ SecondTBSCRL#'TBSCertList'.thisUpdate),
+ case Time1 > Time2 of
+ true ->
+ CRL1;
+ false ->
+ CRL2
+ end
+ end,
+ lists:foldl(Fun, hd(Deltas), tl(Deltas))
+ end.
diff --git a/lib/public_key/test/Makefile b/lib/public_key/test/Makefile
index 41d77f103b..929d321d69 100644
--- a/lib/public_key/test/Makefile
+++ b/lib/public_key/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2008-2011. All Rights Reserved.
+# Copyright Ericsson AB 2008-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/public_key/test/erl_make_certs.erl b/lib/public_key/test/erl_make_certs.erl
index 254aa6d2f9..95e288cd71 100644
--- a/lib/public_key/test/erl_make_certs.erl
+++ b/lib/public_key/test/erl_make_certs.erl
@@ -137,10 +137,10 @@ decode_key(PemBin, Pw) ->
encode_key(Key = #'RSAPrivateKey'{}) ->
{ok, Der} = 'OTP-PUB-KEY':encode('RSAPrivateKey', Key),
- {'RSAPrivateKey', list_to_binary(Der), not_encrypted};
+ {'RSAPrivateKey', Der, not_encrypted};
encode_key(Key = #'DSAPrivateKey'{}) ->
{ok, Der} = 'OTP-PUB-KEY':encode('DSAPrivateKey', Key),
- {'DSAPrivateKey', list_to_binary(Der), not_encrypted}.
+ {'DSAPrivateKey', Der, not_encrypted}.
make_tbs(SubjectKey, Opts) ->
Version = list_to_atom("v"++integer_to_list(proplists:get_value(version, Opts, 3))),
@@ -234,7 +234,7 @@ extensions(Opts) ->
end.
default_extensions(Exts) ->
- Def = [{key_usage,undefined},
+ Def = [{key_usage, default},
{subject_altname, undefined},
{issuer_altname, undefined},
{basic_constraints, default},
@@ -265,6 +265,11 @@ extension({basic_constraints, Data}) ->
#'Extension'{extnID = ?'id-ce-basicConstraints',
extnValue = Data}
end;
+
+extension({key_usage, default}) ->
+ #'Extension'{extnID = ?'id-ce-keyUsage',
+ extnValue = [keyCertSign], critical = true};
+
extension({Id, Data, Critical}) ->
#'Extension'{extnID = Id, extnValue = Data, critical = Critical}.
diff --git a/lib/public_key/test/pbe_SUITE.erl b/lib/public_key/test/pbe_SUITE.erl
index 380a67db7b..8fba1e8cd3 100644
--- a/lib/public_key/test/pbe_SUITE.erl
+++ b/lib/public_key/test/pbe_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2011-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -19,72 +19,16 @@
-module(pbe_SUITE).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-include_lib("public_key/include/public_key.hrl").
%% Note: This directive should only be used in test suites.
-compile(export_all).
-%% 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) ->
- try crypto:start() of
- ok ->
- 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) ->
- 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(_TestCase, Config) ->
- 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
+%% Common Test interface functions -----------------------------------
%%--------------------------------------------------------------------
-end_per_testcase(_TestCase, _Config) ->
- ok.
-%%--------------------------------------------------------------------
-%% 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() ->
@@ -96,17 +40,40 @@ all() ->
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).
+
+%%--------------------------------------------------------------------
+
init_per_group(_GroupName, Config) ->
Config.
end_per_group(_GroupName, Config) ->
Config.
+%%--------------------------------------------------------------------
+init_per_testcase(_TestCase, Config) ->
+ Config.
+
+
+end_per_testcase(_TestCase, _Config) ->
+ ok.
-%% Test cases starts here.
%%--------------------------------------------------------------------
-pbdkdf1(doc) ->
- ["Test with PKCS #5 PBKDF1 Test Vectors"];
+%% Test Cases --------------------------------------------------------
+%%--------------------------------------------------------------------
+
+pbdkdf1() ->
+ [{doc,"Test with PKCS #5 PBKDF1 Test Vectors"}].
pbdkdf1(Config) when is_list(Config) ->
%%Password = "password"
%% = (0x)70617373776F7264
@@ -126,8 +93,8 @@ pbdkdf1(Config) when is_list(Config) ->
16#4A, 16#3D, 16#2A, 16#20, _/binary>> =
pubkey_pbe:pbdkdf1(Password, Salt, Count, sha).
-pbdkdf2(doc) ->
- ["Test with PKCS #5 PBKDF2 Test Vectors"];
+pbdkdf2() ->
+ [{doc,"Test with PKCS #5 PBKDF2 Test Vectors"}].
pbdkdf2(Config) when is_list(Config) ->
%% Input:
%% P = "password" (8 octets)
@@ -225,28 +192,28 @@ pbdkdf2(Config) when is_list(Config) ->
= pubkey_pbe:pbdkdf2("pass\0word",
"sa\0lt", 4096, 16, fun crypto:sha_mac/3, 20).
-encrypted_private_key_info(doc) ->
- ["Tests reading a EncryptedPrivateKeyInfo file encrypted with different ciphers"];
+encrypted_private_key_info() ->
+ [{doc,"Tests reading a EncryptedPrivateKeyInfo file encrypted with different ciphers"}].
encrypted_private_key_info(Config) when is_list(Config) ->
Datadir = ?config(data_dir, Config),
{ok, PemDes} = file:read_file(filename:join(Datadir, "des_cbc_enc_key.pem")),
PemDesEntry = public_key:pem_decode(PemDes),
- test_server:format("Pem entry: ~p" , [PemDesEntry]),
+ ct:print("Pem entry: ~p" , [PemDesEntry]),
[{'PrivateKeyInfo', _, {"DES-CBC",_}} = PubEntry0] = PemDesEntry,
KeyInfo = public_key:pem_entry_decode(PubEntry0, "password"),
{ok, Pem3Des} = file:read_file(filename:join(Datadir, "des_ede3_cbc_enc_key.pem")),
Pem3DesEntry = public_key:pem_decode(Pem3Des),
- test_server:format("Pem entry: ~p" , [Pem3DesEntry]),
+ ct:print("Pem entry: ~p" , [Pem3DesEntry]),
[{'PrivateKeyInfo', _, {"DES-EDE3-CBC",_}} = PubEntry1] = Pem3DesEntry,
KeyInfo = public_key:pem_entry_decode(PubEntry1, "password"),
{ok, PemRc2} = file:read_file(filename:join(Datadir, "rc2_cbc_enc_key.pem")),
PemRc2Entry = public_key:pem_decode(PemRc2),
- test_server:format("Pem entry: ~p" , [PemRc2Entry]),
+ ct:print("Pem entry: ~p" , [PemRc2Entry]),
[{'PrivateKeyInfo', _, {"RC2-CBC",_}} = PubEntry2] = PemRc2Entry,
KeyInfo = public_key:pem_entry_decode(PubEntry2, "password"),
diff --git a/lib/public_key/test/pkits_SUITE.erl b/lib/public_key/test/pkits_SUITE.erl
index e59f299399..d901adaadd 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-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -23,16 +23,18 @@
-module(pkits_SUITE).
--compile(export_all).
-
-include_lib("public_key/include/public_key.hrl").
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
-define(error(Format,Args), error(Format,Args,?FILE,?LINE)).
-define(warning(Format,Args), warning(Format,Args,?FILE,?LINE)).
-define(CERTS, "pkits/certs").
-define(MIME, "pkits/smime").
-define(CONV, "pkits/smime-pem").
+-define(CRL, "pkits/crls").
-define(NIST1, "2.16.840.1.101.3.2.1.48.1").
-define(NIST2, "2.16.840.1.101.3.2.1.48.2").
@@ -42,10 +44,13 @@
-define(NIST6, "2.16.840.1.101.3.2.1.48.6").
-record(verify_state, {
- certs_db,
- crl_info,
+ crls,
+ crl_paths,
revoke_state}).
-%%
+%%--------------------------------------------------------------------
+%% Common Test interface functions -----------------------------------
+%%--------------------------------------------------------------------
+
suite() ->
[{ct_hooks,[ts_install_cth]}].
@@ -54,9 +59,9 @@ all() ->
{group, validity_periods},
{group, verifying_name_chaining},
{group, verifying_paths_with_self_issued_certificates},
- %%{group, basic_certificate_revocation_tests},
- %%{group, delta_crls},
- %%{group, distribution_points},
+ {group, basic_certificate_revocation_tests},
+ {group, delta_crls},
+ {group, distribution_points},
{group, verifying_basic_constraints},
{group, key_usage},
{group, name_constraints},
@@ -72,20 +77,22 @@ groups() ->
[invalid_name_chain, whitespace_name_chain, capitalization_name_chain,
uid_name_chain, attrib_name_chain, string_name_chain]},
{verifying_paths_with_self_issued_certificates, [],
- [basic_valid, %%basic_invalid,
- crl_signing_valid, crl_signing_invalid]},
- %% {basic_certificate_revocation_tests, [],
- %% [missing_CRL, revoked_CA, revoked_peer, invalid_CRL_signature,
- %% invalid_CRL_issuer, invalid_CRL, valid_CRL,
- %% unknown_CRL_extension, old_CRL, fresh_CRL, valid_serial,
- %% invalid_serial, valid_seperate_keys, invalid_separate_keys]},
- %% {delta_crls, [], [delta_without_crl, valid_delta_crls, invalid_delta_crls]},
- %% {distribution_points, [], [valid_distribution_points,
- %% valid_distribution_points_no_issuing_distribution_point,
- %% invalid_distribution_points, valid_only_contains,
- %% invalid_only_contains, valid_only_some_reasons,
- %% invalid_only_some_reasons, valid_indirect_crl,
- %% invalid_indirect_crl, valid_crl_issuer, invalid_crl_issuer]},
+ [basic_valid, basic_invalid, crl_signing_valid, crl_signing_invalid]},
+ {basic_certificate_revocation_tests, [],
+ [missing_CRL,
+ revoked_CA,
+ revoked_peer,
+ invalid_CRL_signature,
+ invalid_CRL_issuer, invalid_CRL, valid_CRL,
+ unknown_CRL_extension, old_CRL, fresh_CRL, valid_serial,
+ invalid_serial, valid_seperate_keys, invalid_separate_keys]},
+ {delta_crls, [], [delta_without_crl, valid_delta_crls, invalid_delta_crls]},
+ {distribution_points, [], [valid_distribution_points,
+ valid_distribution_points_no_issuing_distribution_point,
+ invalid_distribution_points, valid_only_contains,
+ invalid_only_contains, valid_only_some_reasons,
+ invalid_only_some_reasons, valid_indirect_crl,
+ invalid_indirect_crl, valid_crl_issuer, invalid_crl_issuer]},
{verifying_basic_constraints,[],
[missing_basic_constraints, valid_basic_constraint, invalid_path_constraints,
valid_path_constraints]},
@@ -102,12 +109,25 @@ groups() ->
[unknown_critical_extension, unknown_not_critical_extension]}
].
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ try crypto:start() of
+ ok ->
+ crypto_support_check(Config)
+ catch _:_ ->
+ {skip, "Crypto did not start"}
+ end.
+
+end_per_suite(_Config) ->
+ application:stop(crypto).
+
+%%--------------------------------------------------------------------
init_per_group(_GroupName, Config) ->
Config.
end_per_group(_GroupName, Config) ->
Config.
-
+%%--------------------------------------------------------------------
init_per_testcase(_Func, Config) ->
Datadir = proplists:get_value(data_dir, Config),
put(datadir, Datadir),
@@ -116,143 +136,105 @@ init_per_testcase(_Func, Config) ->
end_per_testcase(_Func, Config) ->
Config.
-init_per_suite(Config) ->
- try crypto:start() of
- ok ->
- crypto_support_check(Config)
- catch _:_ ->
- {skip, "Crypto did not start"}
- end.
+%%--------------------------------------------------------------------
+%% Test Cases --------------------------------------------------------
+%%--------------------------------------------------------------------
-end_per_suite(_Config) ->
- application:stop(crypto).
-
-%%-----------------------------------------------------------------------------
-valid_rsa_signature(doc) ->
- ["Test rsa signatur verification"];
-valid_rsa_signature(suite) ->
- [];
+%%--------------------------- signature_verification--------------------------------------------------
+valid_rsa_signature() ->
+ [{doc, "Test rsa signatur verification"}].
valid_rsa_signature(Config) when is_list(Config) ->
run([{ "4.1.1", "Valid Certificate Path Test1 EE", ok}]).
-invalid_rsa_signature(doc) ->
- ["Test rsa signatur verification"];
-invalid_rsa_signature(suite) ->
- [];
+invalid_rsa_signature() ->
+ [{doc,"Test rsa signatur verification"}].
invalid_rsa_signature(Config) when is_list(Config) ->
run([{ "4.1.2", "Invalid CA Signature Test2 EE", {bad_cert,invalid_signature}},
{ "4.1.3", "Invalid EE Signature Test3 EE", {bad_cert,invalid_signature}}]).
-valid_dsa_signature(doc) ->
- ["Test dsa signatur verification"];
-valid_dsa_signature(suite) ->
- [];
+valid_dsa_signature() ->
+ [{doc,"Test dsa signatur verification"}].
valid_dsa_signature(Config) when is_list(Config) ->
run([{ "4.1.4", "Valid DSA Signatures Test4 EE", ok},
{ "4.1.5", "Valid DSA Parameter Inheritance Test5 EE", ok}]).
-invalid_dsa_signature(doc) ->
- ["Test dsa signatur verification"];
-invalid_dsa_signature(suite) ->
- [];
+invalid_dsa_signature() ->
+ [{doc,"Test dsa signatur verification"}].
invalid_dsa_signature(Config) when is_list(Config) ->
run([{ "4.1.6", "Invalid DSA Signature Test6 EE",{bad_cert,invalid_signature}}]).
-%%-----------------------------------------------------------------------------
-not_before_invalid(doc) ->
- [""];
-not_before_invalid(suite) ->
- [];
+
+%%-----------------------------validity_periods------------------------------------------------
+not_before_invalid() ->
+ [{doc,"Test valid periods"}].
not_before_invalid(Config) when is_list(Config) ->
run([{ "4.2.1", "Invalid CA notBefore Date Test1 EE",{bad_cert, cert_expired}},
{ "4.2.2", "Invalid EE notBefore Date Test2 EE",{bad_cert, cert_expired}}]).
-not_before_valid(doc) ->
- [""];
-not_before_valid(suite) ->
- [];
+not_before_valid() ->
+ [{doc,"Test valid periods"}].
not_before_valid(Config) when is_list(Config) ->
run([{ "4.2.3", "Valid pre2000 UTC notBefore Date Test3 EE", ok},
{ "4.2.4", "Valid GeneralizedTime notBefore Date Test4 EE", ok}]).
-not_after_invalid(doc) ->
- [""];
-not_after_invalid(suite) ->
- [];
+not_after_invalid() ->
+ [{doc,"Test valid periods"}].
not_after_invalid(Config) when is_list(Config) ->
run([{ "4.2.5", "Invalid CA notAfter Date Test5 EE", {bad_cert, cert_expired}},
{ "4.2.6", "Invalid EE notAfter Date Test6 EE", {bad_cert, cert_expired}},
{ "4.2.7", "Invalid pre2000 UTC EE notAfter Date Test7 EE",{bad_cert, cert_expired}}]).
-not_after_valid(doc) ->
- [""];
-not_after_valid(suite) ->
- [];
+not_after_valid() ->
+ [{doc,"Test valid periods"}].
not_after_valid(Config) when is_list(Config) ->
run([{ "4.2.8", "Valid GeneralizedTime notAfter Date Test8 EE", ok}]).
-%%-----------------------------------------------------------------------------
-invalid_name_chain(doc) ->
- [""];
-invalid_name_chain(suite) ->
- [];
+
+%%----------------------------verifying_name_chaining-------------------------------------------------
+invalid_name_chain() ->
+ [{doc,"Test name chaining"}].
invalid_name_chain(Config) when is_list(Config) ->
run([{ "4.3.1", "Invalid Name Chaining Test1 EE", {bad_cert, invalid_issuer}},
{ "4.3.2", "Invalid Name Chaining Order Test2 EE", {bad_cert, invalid_issuer}}]).
-whitespace_name_chain(doc) ->
- [""];
-whitespace_name_chain(suite) ->
- [];
+whitespace_name_chain() ->
+ [{doc,"Test name chaining"}].
whitespace_name_chain(Config) when is_list(Config) ->
run([{ "4.3.3", "Valid Name Chaining Whitespace Test3 EE", ok},
{ "4.3.4", "Valid Name Chaining Whitespace Test4 EE", ok}]).
-capitalization_name_chain(doc) ->
- [""];
-capitalization_name_chain(suite) ->
- [];
+capitalization_name_chain() ->
+ [{doc,"Test name chaining"}].
capitalization_name_chain(Config) when is_list(Config) ->
run([{ "4.3.5", "Valid Name Chaining Capitalization Test5 EE",ok}]).
-uid_name_chain(doc) ->
- [""];
-uid_name_chain(suite) ->
- [];
+uid_name_chain() ->
+ [{doc,"Test name chaining"}].
uid_name_chain(Config) when is_list(Config) ->
run([{ "4.3.6", "Valid Name UIDs Test6 EE",ok}]).
-attrib_name_chain(doc) ->
- [""];
-attrib_name_chain(suite) ->
- [];
+attrib_name_chain() ->
+ [{doc,"Test name chaining"}].
attrib_name_chain(Config) when is_list(Config) ->
run([{ "4.3.7", "Valid RFC3280 Mandatory Attribute Types Test7 EE", ok},
{ "4.3.8", "Valid RFC3280 Optional Attribute Types Test8 EE", ok}]).
-string_name_chain(doc) ->
- [""];
-string_name_chain(suite) ->
- [];
+string_name_chain() ->
+ [{doc,"Test name chaining"}].
string_name_chain(Config) when is_list(Config) ->
run([{ "4.3.9", "Valid UTF8String Encoded Names Test9 EE", ok},
%%{ "4.3.10", "Valid Rollover from PrintableString to UTF8String Test10 EE", ok},
{ "4.3.11", "Valid UTF8String Case Insensitive Match Test11 EE", ok}]).
-%%-----------------------------------------------------------------------------
-
-basic_valid(doc) ->
- [""];
-basic_valid(suite) ->
- [];
+%%----------------------------verifying_paths_with_self_issued_certificates-------------------------------------------------
+basic_valid() ->
+ [{doc,"Test self issued certificates"}].
basic_valid(Config) when is_list(Config) ->
run([{ "4.5.1", "Valid Basic Self-Issued Old With New Test1 EE", ok},
{ "4.5.3", "Valid Basic Self-Issued New With Old Test3 EE", ok},
{ "4.5.4", "Valid Basic Self-Issued New With Old Test4 EE", ok}
]).
-basic_invalid(doc) ->
- [""];
-basic_invalid(suite) ->
- [];
+basic_invalid() ->
+ [{doc,"Test self issued certificates"}].
basic_invalid(Config) when is_list(Config) ->
run([{"4.5.2", "Invalid Basic Self-Issued Old With New Test2 EE",
{bad_cert, {revoked, keyCompromise}}},
@@ -260,84 +242,63 @@ basic_invalid(Config) when is_list(Config) ->
{bad_cert, {revoked, keyCompromise}}}
]).
-crl_signing_valid(doc) ->
- [""];
-crl_signing_valid(suite) ->
- [];
+crl_signing_valid() ->
+ [{doc,"Test self issued certificates"}].
crl_signing_valid(Config) when is_list(Config) ->
run([{ "4.5.6", "Valid Basic Self-Issued CRL Signing Key Test6 EE", ok}]).
-crl_signing_invalid(doc) ->
- [""];
-crl_signing_invalid(suite) ->
- [];
+crl_signing_invalid() ->
+ [{doc,"Test self issued certificates"}].
crl_signing_invalid(Config) when is_list(Config) ->
- run([%% { "4.5.7", "Invalid Basic Self-Issued CRL Signing Key Test7 EE",
- %% {bad_cert, {revoked, keyCompromise}}},
+ run([{ "4.5.7", "Invalid Basic Self-Issued CRL Signing Key Test7 EE",
+ {bad_cert, {revoked, keyCompromise}}},
{ "4.5.8", "Invalid Basic Self-Issued CRL Signing Key Test8 EE",
{bad_cert, invalid_key_usage}}
]).
-%%-----------------------------------------------------------------------------
-missing_CRL(doc) ->
- [""];
-missing_CRL(suite) ->
- [];
+%%-----------------------------basic_certificate_revocation_tests------------------------------------------------
+missing_CRL() ->
+ [{doc,"Test basic CRL handling"}].
missing_CRL(Config) when is_list(Config) ->
- run([{ "4.4.1", "Missing CRL Test1 EE",{bad_cert,
+ run([{ "4.4.1", "Invalid Missing CRL Test1 EE",{bad_cert,
revocation_status_undetermined}}]).
-revoked_CA(doc) ->
- [""];
-revoked_CA(suite) ->
- [];
+revoked_CA() ->
+ [{doc,"Test basic CRL handling"}].
revoked_CA(Config) when is_list(Config) ->
run([{ "4.4.2", "Invalid Revoked CA Test2 EE", {bad_cert,
{revoked, keyCompromise}}}]).
-revoked_peer(doc) ->
- [""];
-revoked_peer(suite) ->
- [];
+revoked_peer() ->
+ [{doc,"Test basic CRL handling"}].
revoked_peer(Config) when is_list(Config) ->
- run([{ "4.4.3", "Invalid Revoked EE Test3 EE", {bad_cert,
- {revoked, keyCompromise}}}]).
+ run([{ "4.4.3", "Invalid Revoked EE Test3 EE",
+ {bad_cert, {revoked, keyCompromise}}}]).
-invalid_CRL_signature(doc) ->
- [""];
-invalid_CRL_signature(suite) ->
- [];
+invalid_CRL_signature() ->
+ [{doc,"Test basic CRL handling"}].
invalid_CRL_signature(Config) when is_list(Config) ->
run([{ "4.4.4", "Invalid Bad CRL Signature Test4 EE",
- {bad_cert, revocation_status_undetermined}}]).
-
-invalid_CRL_issuer(doc) ->
- [""];
-invalid_CRL_issuer(suite) ->
- [];
+ {bad_cert, revocation_status_undetermined}}]).
+invalid_CRL_issuer() ->
+ [{doc,"Test basic CRL handling"}].
invalid_CRL_issuer(Config) when is_list(Config) ->
run({ "4.4.5", "Invalid Bad CRL Issuer Name Test5 EE",
{bad_cert, revocation_status_undetermined}}).
-invalid_CRL(doc) ->
- [""];
-invalid_CRL(suite) ->
- [];
+invalid_CRL() ->
+ [{doc,"Test basic CRL handling"}].
invalid_CRL(Config) when is_list(Config) ->
run([{ "4.4.6", "Invalid Wrong CRL Test6 EE",
{bad_cert, revocation_status_undetermined}}]).
-valid_CRL(doc) ->
- [""];
-valid_CRL(suite) ->
- [];
+valid_CRL() ->
+ [{doc,"Test basic CRL handling"}].
valid_CRL(Config) when is_list(Config) ->
run([{ "4.4.7", "Valid Two CRLs Test7 EE", ok}]).
-unknown_CRL_extension(doc) ->
- [""];
-unknown_CRL_extension(suite) ->
- [];
+unknown_CRL_extension() ->
+ [{doc,"Test basic CRL handling"}].
unknown_CRL_extension(Config) when is_list(Config) ->
run([{ "4.4.8", "Invalid Unknown CRL Entry Extension Test8 EE",
{bad_cert, {revoked, keyCompromise}}},
@@ -346,27 +307,21 @@ unknown_CRL_extension(Config) when is_list(Config) ->
{ "4.4.10", "Invalid Unknown CRL Extension Test10 EE",
{bad_cert, revocation_status_undetermined}}]).
-old_CRL(doc) ->
- [""];
-old_CRL(suite) ->
- [];
+old_CRL() ->
+ [{doc,"Test basic CRL handling"}].
old_CRL(Config) when is_list(Config) ->
run([{ "4.4.11", "Invalid Old CRL nextUpdate Test11 EE",
{bad_cert, revocation_status_undetermined}},
{ "4.4.12", "Invalid pre2000 CRL nextUpdate Test12 EE",
{bad_cert, revocation_status_undetermined}}]).
-fresh_CRL(doc) ->
- [""];
-fresh_CRL(suite) ->
- [];
+fresh_CRL() ->
+ [{doc,"Test basic CRL handling"}].
fresh_CRL(Config) when is_list(Config) ->
run([{ "4.4.13", "Valid GeneralizedTime CRL nextUpdate Test13 EE", ok}]).
-valid_serial(doc) ->
- [""];
-valid_serial(suite) ->
- [];
+valid_serial() ->
+ [{doc,"Test basic CRL handling"}].
valid_serial(Config) when is_list(Config) ->
run([
{ "4.4.14", "Valid Negative Serial Number Test14 EE",ok},
@@ -374,38 +329,30 @@ valid_serial(Config) when is_list(Config) ->
{ "4.4.17", "Valid Long Serial Number Test17 EE", ok}
]).
-invalid_serial(doc) ->
- [""];
-invalid_serial(suite) ->
- [];
+invalid_serial() ->
+ [{doc,"Test basic CRL handling"}].
invalid_serial(Config) when is_list(Config) ->
run([{ "4.4.15", "Invalid Negative Serial Number Test15 EE",
{bad_cert, {revoked, keyCompromise}}},
{ "4.4.18", "Invalid Long Serial Number Test18 EE",
{bad_cert, {revoked, keyCompromise}}}]).
-valid_seperate_keys(doc) ->
- [""];
-valid_seperate_keys(suite) ->
- [];
+valid_seperate_keys() ->
+ [{doc,"Test basic CRL handling"}].
valid_seperate_keys(Config) when is_list(Config) ->
run([{ "4.4.19", "Valid Separate Certificate and CRL Keys Test19 EE", ok}]).
-invalid_separate_keys(doc) ->
- [""];
-invalid_separate_keys(suite) ->
- [];
+invalid_separate_keys() ->
+ [{doc,"Test basic CRL handling"}].
invalid_separate_keys(Config) when is_list(Config) ->
- run([{ "4.4.20", "Invalid Separate Certificate and CRL Keys Test20",
+ run([{ "4.4.20", "Invalid Separate Certificate and CRL Keys Test20 EE",
{bad_cert, {revoked, keyCompromise}}},
- { "4.4.21", "Invalid Separate Certificate and CRL Keys Test21",
+ { "4.4.21", "Invalid Separate Certificate and CRL Keys Test21 EE",
{bad_cert, revocation_status_undetermined}}
]).
-%%-----------------------------------------------------------------------------
-missing_basic_constraints(doc) ->
- [""];
-missing_basic_constraints(suite) ->
- [];
+%%----------------------------verifying_basic_constraints-------------------------------------------------
+missing_basic_constraints() ->
+ [{doc,"Basic constraint tests"}].
missing_basic_constraints(Config) when is_list(Config) ->
run([{ "4.6.1", "Invalid Missing basicConstraints Test1 EE",
{bad_cert, missing_basic_constraint}},
@@ -414,17 +361,13 @@ missing_basic_constraints(Config) when is_list(Config) ->
{ "4.6.3", "Invalid cA False Test3 EE",
{bad_cert, missing_basic_constraint}}]).
-valid_basic_constraint(doc) ->
- [""];
-valid_basic_constraint(suite) ->
- [];
+valid_basic_constraint() ->
+ [{doc,"Basic constraint tests"}].
valid_basic_constraint(Config) when is_list(Config) ->
run([{"4.6.4", "Valid basicConstraints Not Critical Test4 EE", ok}]).
-invalid_path_constraints(doc) ->
- [""];
-invalid_path_constraints(suite) ->
- [];
+invalid_path_constraints() ->
+ [{doc,"Basic constraint tests"}].
invalid_path_constraints(Config) when is_list(Config) ->
run([{ "4.6.5", "Invalid pathLenConstraint Test5 EE", {bad_cert, max_path_length_reached}},
{ "4.6.6", "Invalid pathLenConstraint Test6 EE", {bad_cert, max_path_length_reached}},
@@ -435,10 +378,8 @@ invalid_path_constraints(Config) when is_list(Config) ->
{ "4.6.16", "Invalid Self-Issued pathLenConstraint Test16 EE",
{bad_cert, max_path_length_reached}}]).
-valid_path_constraints(doc) ->
- [""];
-valid_path_constraints(suite) ->
- [];
+valid_path_constraints() ->
+ [{doc,"Basic constraint tests"}].
valid_path_constraints(Config) when is_list(Config) ->
run([{ "4.6.7", "Valid pathLenConstraint Test7 EE", ok},
{ "4.6.8", "Valid pathLenConstraint Test8 EE", ok},
@@ -447,60 +388,54 @@ valid_path_constraints(Config) when is_list(Config) ->
{ "4.6.15", "Valid Self-Issued pathLenConstraint Test15 EE", ok},
{ "4.6.17", "Valid Self-Issued pathLenConstraint Test17 EE", ok}]).
-%%-----------------------------------------------------------------------------
-invalid_key_usage(doc) ->
- [""];
-invalid_key_usage(suite) ->
- [];
+%%-----------------------------key_usage------------------------------------------------
+invalid_key_usage() ->
+ [{doc,"Key usage tests"}].
invalid_key_usage(Config) when is_list(Config) ->
run([{ "4.7.1", "Invalid keyUsage Critical keyCertSign False Test1 EE",
{bad_cert,invalid_key_usage} },
{ "4.7.2", "Invalid keyUsage Not Critical keyCertSign False Test2 EE",
- {bad_cert,invalid_key_usage}}
- %% { "4.7.4", "Invalid keyUsage Critical cRLSign False Test4 EE",
- %% {bad_cert, revocation_status_undetermined}},
- %% { "4.7.5", "Invalid keyUsage Not Critical cRLSign False Test5 EE",
- %% {bad_cert, revocation_status_undetermined}}
+ {bad_cert,invalid_key_usage}},
+ { "4.7.4", "Invalid keyUsage Critical cRLSign False Test4 EE",
+ {bad_cert, invalid_key_usage}},
+ { "4.7.5", "Invalid keyUsage Not Critical cRLSign False Test5 EE",
+ {bad_cert, invalid_key_usage}}
]).
-valid_key_usage(doc) ->
- [""];
-valid_key_usage(suite) ->
- [];
+valid_key_usage() ->
+ [{doc,"Key usage tests"}].
valid_key_usage(Config) when is_list(Config) ->
run([{ "4.7.3", "Valid keyUsage Not Critical Test3 EE", ok}]).
%%-----------------------------------------------------------------------------
-certificate_policies(doc) -> [""];
-certificate_policies(suite) -> [];
+certificate_policies() ->
+ [{doc,"Not supported yet"}].
certificate_policies(Config) when is_list(Config) ->
- run(certificate_policies()).
+ run(certificate_policies_tests()).
%%-----------------------------------------------------------------------------
-require_explicit_policy(doc) -> [""];
-require_explicit_policy(suite) -> [];
+require_explicit_policy() ->
+ [{doc,"Not supported yet"}].
require_explicit_policy(Config) when is_list(Config) ->
- run(require_explicit_policy()).
+ run(require_explicit_policy_tests()).
%%-----------------------------------------------------------------------------
-policy_mappings(doc) -> [""];
-policy_mappings(suite) -> [];
+policy_mappings() ->
+ [{doc,"Not supported yet"}].
policy_mappings(Config) when is_list(Config) ->
- run(policy_mappings()).
+ run(policy_mappings_tests()).
%%-----------------------------------------------------------------------------
-inhibit_policy_mapping(doc) -> [""];
-inhibit_policy_mapping(suite) -> [];
+inhibit_policy_mapping() ->
+ [{doc,"Not supported yet"}].
inhibit_policy_mapping(Config) when is_list(Config) ->
- run(inhibit_policy_mapping()).
+ run(inhibit_policy_mapping_tests()).
%%-----------------------------------------------------------------------------
-inhibit_any_policy(doc) -> [""];
-inhibit_any_policy(suite) -> [];
+inhibit_any_policy() ->
+ [{doc,"Not supported yet"}].
inhibit_any_policy(Config) when is_list(Config) ->
- run(inhibit_any_policy()).
-%%-----------------------------------------------------------------------------
+ run(inhibit_any_policy_tests()).
+%%-------------------------------name_constraints----------------------------------------------
-valid_DN_name_constraints(doc) ->
- [""];
-valid_DN_name_constraints(suite) ->
- [];
+valid_DN_name_constraints() ->
+ [{doc, "Name constraints tests"}].
valid_DN_name_constraints(Config) when is_list(Config) ->
run([{ "4.13.1", "Valid DN nameConstraints Test1 EE", ok},
{ "4.13.4", "Valid DN nameConstraints Test4 EE", ok},
@@ -511,10 +446,8 @@ valid_DN_name_constraints(Config) when is_list(Config) ->
{ "4.13.18", "Valid DN nameConstraints Test18 EE", ok},
{ "4.13.19", "Valid DN nameConstraints Test19 EE", ok}]).
-invalid_DN_name_constraints(doc) ->
- [""];
-invalid_DN_name_constraints(suite) ->
- [];
+invalid_DN_name_constraints() ->
+ [{doc,"Name constraints tests"}].
invalid_DN_name_constraints(Config) when is_list(Config) ->
run([{ "4.13.2", "Invalid DN nameConstraints Test2 EE", {bad_cert, name_not_permitted}},
{ "4.13.3", "Invalid DN nameConstraints Test3 EE", {bad_cert, name_not_permitted}},
@@ -530,20 +463,15 @@ invalid_DN_name_constraints(Config) when is_list(Config) ->
{ "4.13.20", "Invalid DN nameConstraints Test20 EE",
{bad_cert, name_not_permitted}}]).
-valid_rfc822_name_constraints(doc) ->
- [""];
-valid_rfc822_name_constraints(suite) ->
- [];
+valid_rfc822_name_constraints() ->
+ [{doc,"Name constraints tests"}].
valid_rfc822_name_constraints(Config) when is_list(Config) ->
run([{ "4.13.21", "Valid RFC822 nameConstraints Test21 EE", ok},
{ "4.13.23", "Valid RFC822 nameConstraints Test23 EE", ok},
{ "4.13.25", "Valid RFC822 nameConstraints Test25 EE", ok}]).
-
-invalid_rfc822_name_constraints(doc) ->
- [""];
-invalid_rfc822_name_constraints(suite) ->
- [];
+invalid_rfc822_name_constraints() ->
+ [{doc,"Name constraints tests"}].
invalid_rfc822_name_constraints(Config) when is_list(Config) ->
run([{ "4.13.22", "Invalid RFC822 nameConstraints Test22 EE",
{bad_cert, name_not_permitted}},
@@ -552,71 +480,54 @@ invalid_rfc822_name_constraints(Config) when is_list(Config) ->
{ "4.13.26", "Invalid RFC822 nameConstraints Test26 EE",
{bad_cert, name_not_permitted}}]).
-valid_DN_and_rfc822_name_constraints(doc) ->
- [""];
-valid_DN_and_rfc822_name_constraints(suite) ->
- [];
+valid_DN_and_rfc822_name_constraints() ->
+ [{doc,"Name constraints tests"}].
valid_DN_and_rfc822_name_constraints(Config) when is_list(Config) ->
run([{ "4.13.27", "Valid DN and RFC822 nameConstraints Test27 EE", ok}]).
-invalid_DN_and_rfc822_name_constraints(doc) ->
- [""];
-invalid_DN_and_rfc822_name_constraints(suite) ->
- [];
+invalid_DN_and_rfc822_name_constraints() ->
+ [{doc,"Name constraints tests"}].
invalid_DN_and_rfc822_name_constraints(Config) when is_list(Config) ->
run([{ "4.13.28", "Invalid DN and RFC822 nameConstraints Test28 EE",
{bad_cert, name_not_permitted}},
{ "4.13.29", "Invalid DN and RFC822 nameConstraints Test29 EE",
{bad_cert, name_not_permitted}}]).
-valid_dns_name_constraints(doc) ->
- [""];
-valid_dns_name_constraints(suite) ->
- [];
+valid_dns_name_constraints() ->
+ [{doc,"Name constraints tests"}].
valid_dns_name_constraints(Config) when is_list(Config) ->
run([{ "4.13.30", "Valid DNS nameConstraints Test30 EE", ok},
{ "4.13.32", "Valid DNS nameConstraints Test32 EE", ok}]).
-invalid_dns_name_constraints(doc) ->
- [""];
-invalid_dns_name_constraints(suite) ->
- [];
+invalid_dns_name_constraints() ->
+ [{doc,"Name constraints tests"}].
invalid_dns_name_constraints(Config) when is_list(Config) ->
run([{ "4.13.31", "Invalid DNS nameConstraints Test31 EE", {bad_cert, name_not_permitted}},
{ "4.13.33", "Invalid DNS nameConstraints Test33 EE", {bad_cert, name_not_permitted}},
{ "4.13.38", "Invalid DNS nameConstraints Test38 EE", {bad_cert, name_not_permitted}}]).
-valid_uri_name_constraints(doc) ->
- [""];
-valid_uri_name_constraints(suite) ->
- [];
+valid_uri_name_constraints() ->
+ [{doc,"Name constraints tests"}].
valid_uri_name_constraints(Config) when is_list(Config) ->
run([{ "4.13.34", "Valid URI nameConstraints Test34 EE", ok},
{ "4.13.36", "Valid URI nameConstraints Test36 EE", ok}]).
-invalid_uri_name_constraints(doc) ->
- [""];
-invalid_uri_name_constraints(suite) ->
- [];
+invalid_uri_name_constraints() ->
+ [{doc,"Name constraints tests"}].
invalid_uri_name_constraints(Config) when is_list(Config) ->
run([{ "4.13.35", "Invalid URI nameConstraints Test35 EE",{bad_cert, name_not_permitted}},
{ "4.13.37", "Invalid URI nameConstraints Test37 EE",{bad_cert, name_not_permitted}}]).
-%%-----------------------------------------------------------------------------
-delta_without_crl(doc) ->
- [""];
-delta_without_crl(suite) ->
- [];
+%%------------------------------delta_crls-----------------------------------------------
+delta_without_crl() ->
+ [{doc,"Delta CRL tests"}].
delta_without_crl(Config) when is_list(Config) ->
run([{ "4.15.1", "Invalid deltaCRLIndicator No Base Test1 EE",{bad_cert,
revocation_status_undetermined}},
{"4.15.10", "Invalid delta-CRL Test10 EE", {bad_cert,
revocation_status_undetermined}}]).
-
-valid_delta_crls(doc) ->
- [""];
-valid_delta_crls(suite) ->
- [];
+valid_delta_crls() ->
+ [{doc,"Delta CRL tests"}].
valid_delta_crls(Config) when is_list(Config) ->
run([{ "4.15.2", "Valid delta-CRL Test2 EE", ok},
{ "4.15.5", "Valid delta-CRL Test5 EE", ok},
@@ -624,22 +535,17 @@ valid_delta_crls(Config) when is_list(Config) ->
{ "4.15.8", "Valid delta-CRL Test8 EE", ok}
]).
-invalid_delta_crls(doc) ->
- [""];
-invalid_delta_crls(suite) ->
- [];
+invalid_delta_crls() ->
+ [{doc,"Delta CRL tests"}].
invalid_delta_crls(Config) when is_list(Config) ->
run([{ "4.15.3", "Invalid delta-CRL Test3 EE", {bad_cert,{revoked, keyCompromise}}},
{ "4.15.4", "Invalid delta-CRL Test4 EE", {bad_cert,{revoked, keyCompromise}}},
{ "4.15.6", "Invalid delta-CRL Test6 EE", {bad_cert,{revoked, keyCompromise}}},
{ "4.15.9", "Invalid delta-CRL Test9 EE", {bad_cert,{revoked, keyCompromise}}}]).
-%%-----------------------------------------------------------------------------
-
-valid_distribution_points(doc) ->
- [""];
-valid_distribution_points(suite) ->
- [];
+%%---------------------------distribution_points--------------------------------------------------
+valid_distribution_points() ->
+ [{doc,"CRL Distribution Point tests"}].
valid_distribution_points(Config) when is_list(Config) ->
run([{ "4.14.1", "Valid distributionPoint Test1 EE", ok},
{ "4.14.4", "Valid distributionPoint Test4 EE", ok},
@@ -647,18 +553,14 @@ valid_distribution_points(Config) when is_list(Config) ->
{ "4.14.7", "Valid distributionPoint Test7 EE", ok}
]).
-valid_distribution_points_no_issuing_distribution_point(doc) ->
- [""];
-valid_distribution_points_no_issuing_distribution_point(suite) ->
- [];
+valid_distribution_points_no_issuing_distribution_point() ->
+ [{doc,"CRL Distribution Point tests"}].
valid_distribution_points_no_issuing_distribution_point(Config) when is_list(Config) ->
- run([{ "4.14.10", "Valid No issuingDistributionPoint Test10", ok}
+ run([{ "4.14.10", "Valid No issuingDistributionPoint Test10 EE", ok}
]).
-invalid_distribution_points(doc) ->
- [""];
-invalid_distribution_points(suite) ->
- [];
+invalid_distribution_points() ->
+ [{doc,"CRL Distribution Point tests"}].
invalid_distribution_points(Config) when is_list(Config) ->
run([{ "4.14.2", "Invalid distributionPoint Test2 EE", {bad_cert,{revoked, keyCompromise}}},
{ "4.14.3", "Invalid distributionPoint Test3 EE", {bad_cert,
@@ -670,40 +572,31 @@ invalid_distribution_points(Config) when is_list(Config) ->
revocation_status_undetermined}}
]).
-valid_only_contains(doc) ->
- [""];
-valid_only_contains(suite) ->
- [];
+valid_only_contains() ->
+ [{doc,"CRL Distribution Point tests"}].
valid_only_contains(Config) when is_list(Config) ->
- run([{ "4.14.13", "Valid onlyContainsCACerts CRL Test13 EE", ok}]).
-
+ run([{ "4.14.13", "Valid only Contains CA Certs Test13 EE", ok}]).
-invalid_only_contains(doc) ->
- [""];
-invalid_only_contains(suite) ->
- [];
+invalid_only_contains() ->
+ [{doc,"CRL Distribution Point tests"}].
invalid_only_contains(Config) when is_list(Config) ->
- run([{ "4.14.11", "Invalid onlyContainsUserCerts CRL Test11 EE",
+ run([{ "4.14.11", "Invalid onlyContainsUserCerts Test11 EE",
{bad_cert, revocation_status_undetermined}},
- { "4.14.12", "Invalid onlyContainsCACerts CRL Test12 EE",
+ { "4.14.12", "Invalid onlyContainsCACerts Test12 EE",
{bad_cert, revocation_status_undetermined}},
{ "4.14.14", "Invalid onlyContainsAttributeCerts Test14 EE",
{bad_cert, revocation_status_undetermined}}
]).
-valid_only_some_reasons(doc) ->
- [""];
-valid_only_some_reasons(suite) ->
- [];
+valid_only_some_reasons() ->
+ [{doc,"CRL Distribution Point tests"}].
valid_only_some_reasons(Config) when is_list(Config) ->
run([{ "4.14.18", "Valid onlySomeReasons Test18 EE", ok},
{ "4.14.19", "Valid onlySomeReasons Test19 EE", ok}
]).
-invalid_only_some_reasons(doc) ->
- [""];
-invalid_only_some_reasons(suite) ->
- [];
+invalid_only_some_reasons() ->
+ [{doc,"CRL Distribution Point tests"}].
invalid_only_some_reasons(Config) when is_list(Config) ->
run([{ "4.14.15", "Invalid onlySomeReasons Test15 EE",
{bad_cert,{revoked, keyCompromise}}},
@@ -717,20 +610,16 @@ invalid_only_some_reasons(Config) when is_list(Config) ->
{bad_cert,{revoked, affiliationChanged}}}
]).
-valid_indirect_crl(doc) ->
- [""];
-valid_indirect_crl(suite) ->
- [];
+valid_indirect_crl() ->
+ [{doc,"CRL Distribution Point tests"}].
valid_indirect_crl(Config) when is_list(Config) ->
run([{ "4.14.22", "Valid IDP with indirectCRL Test22 EE", ok},
{ "4.14.24", "Valid IDP with indirectCRL Test24 EE", ok},
{ "4.14.25", "Valid IDP with indirectCRL Test25 EE", ok}
]).
-invalid_indirect_crl(doc) ->
- [""];
-invalid_indirect_crl(suite) ->
- [];
+invalid_indirect_crl() ->
+ [{doc,"CRL Distribution Point tests"}].
invalid_indirect_crl(Config) when is_list(Config) ->
run([{ "4.14.23", "Invalid IDP with indirectCRL Test23 EE",
{bad_cert,{revoked, keyCompromise}}},
@@ -738,20 +627,16 @@ invalid_indirect_crl(Config) when is_list(Config) ->
{bad_cert, revocation_status_undetermined}}
]).
-valid_crl_issuer(doc) ->
- [""];
-valid_crl_issuer(suite) ->
- [];
+valid_crl_issuer() ->
+ [{doc,"CRL Distribution Point tests"}].
valid_crl_issuer(Config) when is_list(Config) ->
- run([{ "4.14.28", "Valid cRLIssuer Test28 EE", ok}%%,
- %%{ "4.14.29", "Valid cRLIssuer Test29 EE", ok},
- %%{ "4.14.33", "Valid cRLIssuer Test33 EE", ok}
+ run([{ "4.14.28", "Valid cRLIssuer Test28 EE", ok},
+ { "4.14.29", "Valid cRLIssuer Test29 EE", ok},
+ { "4.14.33", "Valid cRLIssuer Test33 EE", ok}
]).
-invalid_crl_issuer(doc) ->
- [""];
-invalid_crl_issuer(suite) ->
- [];
+invalid_crl_issuer() ->
+ [{doc,"CRL Distribution Point tests"}].
invalid_crl_issuer(Config) when is_list(Config) ->
run([
{ "4.14.27", "Invalid cRLIssuer Test27 EE", {bad_cert, revocation_status_undetermined}},
@@ -761,44 +646,36 @@ invalid_crl_issuer(Config) when is_list(Config) ->
{ "4.14.35", "Invalid cRLIssuer Test35 EE", {bad_cert, revocation_status_undetermined}}
]).
-
-%%distribution_points() ->
- %%{ "4.14", "Distribution Points" },
-%% [
- %% Although this test is valid it has a circular dependency. As a result
- %% an attempt is made to reursively checks a CRL path and rejected due to
- %% a CRL path validation error. PKITS notes suggest this test does not
- %% need to be run due to this issue.
-%% { "4.14.30", "Valid cRLIssuer Test30", 54 }].
+%% Although this test is valid it has a circular dependency. As a result
+%% an attempt is made to reursively checks a CRL path and rejected due to
+%% a CRL path validation error. PKITS notes suggest this test does not
+%% need to be run due to this issue.
+%% { "4.14.30", "Valid cRLIssuer Test30", 54 }
-%%-----------------------------------------------------------------------------
+%%-------------------------------private_certificate_extensions----------------------------------------------
-unknown_critical_extension(doc) ->
- [""];
-unknown_critical_extension(suite) ->
- [];
+unknown_critical_extension() ->
+ [{doc,"Test that a cert with an unknown critical extension is recjected"}].
unknown_critical_extension(Config) when is_list(Config) ->
run([{ "4.16.2", "Invalid Unknown Critical Certificate Extension Test2 EE",
{bad_cert,unknown_critical_extension}}]).
-unknown_not_critical_extension(doc) ->
- [""];
-unknown_not_critical_extension(suite) ->
- [];
+unknown_not_critical_extension() ->
+ [{doc,"Test that a not critical unknown extension is ignored"}].
unknown_not_critical_extension(Config) when is_list(Config) ->
run([{ "4.16.1", "Valid Unknown Not Critical Certificate Extension Test1 EE", ok}]).
-%%-----------------------------------------------------------------------------
+%%--------------------------------------------------------------------
+%% Internal functions ------------------------------------------------
+%%--------------------------------------------------------------------
+
run(Tests) ->
[TA] = read_certs("Trust Anchor Root Certificate"),
run(Tests, TA).
run({Chap, Test, Result}, TA) ->
CertChain = cas(Chap) ++ read_certs(Test),
- lists:foreach(fun(C) ->
- io:format("CERT: ~p~n", [public_key:pkix_decode_cert(C, otp)])
- end, CertChain),
Options = path_validation_options(TA, Chap,Test),
try public_key:pkix_path_validation(TA, CertChain, Options) of
{Result, _} -> ok;
@@ -825,7 +702,7 @@ run([],_) -> ok.
path_validation_options(TA, Chap, Test) ->
case needs_crl_options(Chap) of
true ->
- crl_options(TA, Test);
+ crl_options(TA, Chap, Test);
false ->
Fun =
fun(_,{bad_cert, _} = Reason, _) ->
@@ -839,6 +716,56 @@ path_validation_options(TA, Chap, Test) ->
[{verify_fun, {Fun, []}}]
end.
+read_certs(Test) ->
+ File = cert_file(Test),
+ Ders = erl_make_certs:pem_to_der(File),
+ [Cert || {'Certificate', Cert, not_encrypted} <- Ders].
+
+read_crls(Test) ->
+ File = crl_file(Test),
+ Ders = erl_make_certs:pem_to_der(File),
+ [CRL || {'CertificateList', CRL, not_encrypted} <- Ders].
+
+cert_file(Test) ->
+ file(?CONV, lists:append(string:tokens(Test, " -")) ++ ".pem").
+
+crl_file(Test) ->
+ file(?CRL, lists:append(string:tokens(Test, " -")) ++ ".pem").
+
+
+file(Sub,File) ->
+ TestDir = case get(datadir) of
+ undefined -> "./pkits_SUITE_data";
+ Dir when is_list(Dir) ->
+ Dir
+ end,
+ AbsFile = filename:join([TestDir,Sub,File]),
+ case filelib:is_file(AbsFile) of
+ true -> ok;
+ false ->
+ ?error("Couldn't read data from ~p ~n",[AbsFile])
+ end,
+ AbsFile.
+
+error(Format, Args, File0, Line) ->
+ File = filename:basename(File0),
+ Pid = group_leader(),
+ Pid ! {failed, File, Line},
+ io:format(Pid, "~s(~p): ERROR"++Format, [File,Line|Args]).
+
+warning(Format, Args, File0, Line) ->
+ File = filename:basename(File0),
+ io:format("~s(~p): Warning "++Format, [File,Line|Args]).
+
+crypto_support_check(Config) ->
+ try crypto:sha256(<<"Test">>) of
+ _ ->
+ Config
+ catch error:notsup ->
+ crypto:stop(),
+ {skip, "To old version of openssl"}
+ end.
+
needs_crl_options("4.4" ++ _) ->
true;
needs_crl_options("4.5" ++ _) ->
@@ -854,54 +781,69 @@ needs_crl_options("4.15" ++ _) ->
needs_crl_options(_) ->
false.
-crl_options(TA, Test) ->
- case read_crls(Test) of
- [] ->
- [];
- CRLs ->
- Fun =
- fun(_,{bad_cert, _} = Reason, _) ->
- {fail, Reason};
- (_,{extension,
- #'Extension'{extnID = ?'id-ce-cRLDistributionPoints',
- extnValue = Value}}, UserState0) ->
- UserState = update_crls(Value, UserState0),
+crl_options(_TA, Chap, _Test) ->
+ CRLNames = crl_names(Chap),
+ CRLs = crls(CRLNames),
+ Paths = lists:map(fun(CRLName) -> crl_path(CRLName) end, CRLNames),
+
+ ct:print("Paths ~p ~n Names ~p ~n", [Paths, CRLNames]),
+ Fun =
+ fun(_,{bad_cert, _} = Reason, _) ->
+ {fail, Reason};
+ (_,{extension,
+ #'Extension'{extnID = ?'id-ce-cRLDistributionPoints',
+ extnValue = Value}}, UserState0) ->
+ UserState = update_crls(Value, UserState0),
+ {valid, UserState};
+ (_,{extension, _}, UserState) ->
+ {unknown, UserState};
+ (OtpCert, Valid, UserState) when Valid == valid;
+ Valid == valid_peer ->
+ DerCRLs = UserState#verify_state.crls,
+ Paths = UserState#verify_state.crl_paths,
+ Crls = [{DerCRL, public_key:der_decode('CertificateList',
+ DerCRL)} || DerCRL <- DerCRLs],
+
+ CRLInfo0 = crl_info(OtpCert, Crls, []),
+ CRLInfo = lists:reverse(CRLInfo0),
+ PathDb = crl_path_db(lists:reverse(Crls), Paths, []),
+
+ Fun = fun(DP, CRLtoValidate, Id, PathDb0) ->
+ trusted_cert_and_path(DP, CRLtoValidate, Id, PathDb0)
+ end,
+
+ case CRLInfo of
+ [] ->
{valid, UserState};
- (_,{extension, _}, UserState) ->
- {unknown, UserState};
- (OtpCert, Valid, UserState) when Valid == valid;
- Valid == valid_peer ->
- {ErlCerts, CRLs} = UserState#verify_state.crl_info,
- CRLInfo0 =
- crl_info(OtpCert,
- ErlCerts,[{DerCRL, public_key:der_decode('CertificateList',
- DerCRL)} || DerCRL <- CRLs],
- []),
- CRLInfo = lists:reverse(CRLInfo0),
- Certs = UserState#verify_state.certs_db,
- Fun = fun(DP, CRLtoValidate, Id, CertsDb) ->
- trusted_cert_and_path(DP, CRLtoValidate, Id, CertsDb)
- end,
- Ignore = ignore_sign_test_when_building_path(Test),
+ [_|_] ->
case public_key:pkix_crls_validate(OtpCert, CRLInfo,
- [{issuer_fun,{Fun, {Ignore, Certs}}}]) of
+ [{issuer_fun,{Fun, PathDb}}]) of
valid ->
{valid, UserState};
Reason ->
{fail, Reason}
end
- end,
+ end
+ end,
- Certs = read_certs(Test),
- ErlCerts = [public_key:pkix_decode_cert(Cert, otp) || Cert <- Certs],
+ [{verify_fun, {Fun, #verify_state{crls = CRLs,
+ crl_paths = Paths}}}].
- [{verify_fun, {Fun, #verify_state{certs_db = [TA| Certs],
- crl_info = {ErlCerts, CRLs}}}}]
- end.
+crl_path_db([], [], Acc) ->
+ Acc;
+crl_path_db([{_, CRL} |CRLs], [Path | Paths], Acc) ->
+ CertPath = lists:flatten(lists:map(fun([]) ->
+ [];
+ (CertFile) ->
+ ct:print("Certfile ~p", [CertFile]),
+ read_certs(CertFile)
+ end, Path)),
+ crl_path_db(CRLs, Paths, [{CRL, CertPath}| Acc]).
-crl_info(_, _, [], Acc) ->
+
+crl_info(_, [], Acc) ->
Acc;
-crl_info(OtpCert, Certs, [{_, #'CertificateList'{tbsCertList =
+crl_info(OtpCert, [{_, #'CertificateList'{tbsCertList =
#'TBSCertList'{issuer = Issuer,
crlExtensions = CRLExtensions}}}
= CRL | Rest], Acc) ->
@@ -910,22 +852,36 @@ crl_info(OtpCert, Certs, [{_, #'CertificateList'{tbsCertList =
ExtList = pubkey_cert:extensions_list(CRLExtensions),
DPs = case pubkey_cert:select_extension(?'id-ce-cRLDistributionPoints', Extensions) of
#'Extension'{extnValue = Value} ->
- lists:map(fun(Point) -> pubkey_cert_records:transform(Point, decode) end, Value);
- _ ->
- case same_issuer(OtpCert, Issuer) of
- true ->
- [make_dp(ExtList, asn1_NOVALUE, Issuer)];
- false ->
- [make_dp(ExtList, Issuer, ignore)]
- end
+ lists:foldl(fun(Point, Acc0) ->
+ Dp = pubkey_cert_records:transform(Point, decode),
+ IDP = pubkey_cert:select_extension(?'id-ce-issuingDistributionPoint',
+ Extensions),
+ case Dp#'DistributionPoint'.cRLIssuer of
+ asn1_NOVALUE ->
+ [Dp | Acc0];
+ DpCRLIssuer ->
+ CRLIssuer = dp_crlissuer_to_issuer(DpCRLIssuer),
+ CertIssuer = OtpTBSCert#'OTPTBSCertificate'.issuer,
+ case pubkey_cert:is_issuer(CRLIssuer, CertIssuer) of
+ true ->
+ [Dp | Acc0];
+ false when (IDP =/= undefined) ->
+ Acc0;
+ false ->
+ [Dp | Acc0]
+ end
+ end
+ end, [], Value);
+ _ ->
+ case same_issuer(OtpCert, Issuer) of
+ true ->
+ [make_dp(ExtList, asn1_NOVALUE, Issuer)];
+ false ->
+ [make_dp(ExtList, Issuer, ignore)]
+ end
end,
DPsCRLs = lists:map(fun(DP) -> {DP, CRL} end, DPs),
- crl_info(OtpCert, Certs, Rest, DPsCRLs ++ Acc).
-
-ignore_sign_test_when_building_path("Invalid Bad CRL Signature Test4") ->
- true;
-ignore_sign_test_when_building_path(_) ->
- false.
+ crl_info(OtpCert, Rest, DPsCRLs ++ Acc).
same_issuer(OTPCert, Issuer) ->
DecIssuer = pubkey_cert_records:transform(Issuer, decode),
@@ -957,200 +913,23 @@ mk_issuer_dp(Issuer, _) ->
update_crls(_, State) ->
State.
-trusted_cert_and_path(DP, CRL, Id, {Ignore, CertsList}) ->
- case crl_issuer(crl_issuer_name(DP), CRL, Id, CertsList, CertsList, Ignore) of
- {ok, IssuerCert, DerIssuerCert} ->
- Certs = [{public_key:pkix_decode_cert(Cert, otp), Cert} || Cert <- CertsList],
- CertChain = build_chain(Certs, Certs, IssuerCert, Ignore, [DerIssuerCert]),
- {ok, public_key:pkix_decode_cert(hd(CertChain), otp), CertChain};
- Other ->
- Other
- end.
-
-crl_issuer_name(#'DistributionPoint'{cRLIssuer = asn1_NOVALUE}) ->
- undefined;
-crl_issuer_name(#'DistributionPoint'{cRLIssuer = [{directoryName, Issuer}]}) ->
- pubkey_cert_records:transform(Issuer, decode).
+trusted_cert_and_path(_, #'CertificateList'{} = CRL, _, PathDb) ->
+ [TrustedDERCert] = read_certs(crl_root_cert()),
+ TrustedCert = public_key:pkix_decode_cert(TrustedDERCert, otp),
-build_chain([],_, _, _,Acc) ->
- Acc;
-
-build_chain([{First, DerFirst}|Certs], All, Cert, Ignore, Acc) ->
- case public_key:pkix_is_self_signed(Cert) andalso is_test_root(Cert) of
- true ->
- Acc;
- false ->
- case public_key:pkix_is_issuer(Cert, First)
- %%andalso check_extension_cert_signer(First)
- andalso is_signer(First, Cert, Ignore)
- of
- true ->
- build_chain(All, All, First, Ignore, [DerFirst | Acc]);
- false ->
- build_chain(Certs, All, Cert, Ignore, Acc)
- end
- end.
-
-is_signer(_,_, true) ->
- true;
-is_signer(Signer, #'OTPCertificate'{} = Cert,_) ->
- TBSCert = Signer#'OTPCertificate'.tbsCertificate,
- PublicKeyInfo = TBSCert#'OTPTBSCertificate'.subjectPublicKeyInfo,
- PublicKey = PublicKeyInfo#'OTPSubjectPublicKeyInfo'.subjectPublicKey,
- AlgInfo = PublicKeyInfo#'OTPSubjectPublicKeyInfo'.algorithm,
- PublicKeyParams = AlgInfo#'PublicKeyAlgorithm'.parameters,
- try pubkey_cert:validate_signature(Cert, public_key:pkix_encode('OTPCertificate',
- Cert, otp),
- PublicKey, PublicKeyParams, true, ?DEFAULT_VERIFYFUN) of
- true ->
- true
- catch
- _:_ ->
- false
- end;
-is_signer(Signer, #'CertificateList'{} = CRL, _) ->
- TBSCert = Signer#'OTPCertificate'.tbsCertificate,
- PublicKeyInfo = TBSCert#'OTPTBSCertificate'.subjectPublicKeyInfo,
- PublicKey = PublicKeyInfo#'OTPSubjectPublicKeyInfo'.subjectPublicKey,
- AlgInfo = PublicKeyInfo#'OTPSubjectPublicKeyInfo'.algorithm,
- PublicKeyParams = AlgInfo#'PublicKeyAlgorithm'.parameters,
- pubkey_crl:verify_crl_signature(CRL, public_key:pkix_encode('CertificateList',
- CRL, plain),
- PublicKey, PublicKeyParams).
-
-is_test_root(OtpCert) ->
- TBSCert = OtpCert#'OTPCertificate'.tbsCertificate,
- {rdnSequence, AtterList} = TBSCert#'OTPTBSCertificate'.issuer,
- lists:member([{'AttributeTypeAndValue',{2,5,4,3},{printableString,"Trust Anchor"}}],
- AtterList).
-
-check_extension_cert_signer(OtpCert) ->
- TBSCert = OtpCert#'OTPCertificate'.tbsCertificate,
- Extensions = TBSCert#'OTPTBSCertificate'.extensions,
- case pubkey_cert:select_extension(?'id-ce-keyUsage', Extensions) of
- #'Extension'{extnValue = KeyUse} ->
- lists:member(keyCertSign, KeyUse);
- _ ->
- true
- end.
-
-check_extension_crl_signer(OtpCert) ->
- TBSCert = OtpCert#'OTPCertificate'.tbsCertificate,
- Extensions = TBSCert#'OTPTBSCertificate'.extensions,
- case pubkey_cert:select_extension(?'id-ce-keyUsage', Extensions) of
- #'Extension'{extnValue = KeyUse} ->
- lists:member(cRLSign, KeyUse);
- _ ->
- true
+ case lists:keysearch(CRL, 1, PathDb) of
+ {_, {CRL, [ _| _] = Path}} ->
+ {ok, TrustedCert, [TrustedDERCert | Path]};
+ {_, {CRL, []}} ->
+ {ok, TrustedCert, [TrustedDERCert]}
end.
-crl_issuer(undefined, CRL, issuer_not_found, _, CertsList, Ignore) ->
- crl_issuer(CRL, CertsList, Ignore);
-
-crl_issuer(IssuerName, CRL, issuer_not_found, CertsList, CertsList, Ignore) ->
- crl_issuer(IssuerName, CRL, IssuerName, CertsList, CertsList, Ignore);
-
-crl_issuer(undefined, CRL, Id, [Cert | Rest], All, false) ->
- ErlCert = public_key:pkix_decode_cert(Cert, otp),
- TBSCertificate = ErlCert#'OTPCertificate'.tbsCertificate,
- SerialNumber = TBSCertificate#'OTPTBSCertificate'.serialNumber,
- Issuer = public_key:pkix_normalize_name(
- TBSCertificate#'OTPTBSCertificate'.subject),
- Bool = is_signer(ErlCert, CRL, false),
- case {SerialNumber, Issuer} of
- Id when Bool == true ->
- {ok, ErlCert, Cert};
- _ ->
- crl_issuer(undefined, CRL, Id, Rest, All, false)
- end;
-
-crl_issuer(IssuerName, CRL, Id, [Cert | Rest], All, false) ->
- ErlCert = public_key:pkix_decode_cert(Cert, otp),
- TBSCertificate = ErlCert#'OTPCertificate'.tbsCertificate,
- SerialNumber = TBSCertificate#'OTPTBSCertificate'.serialNumber,
- %%Issuer = public_key:pkix_normalize_name(
- %% TBSCertificate#'OTPTBSCertificate'.subject),
- Bool = is_signer(ErlCert, CRL, false),
- case {SerialNumber, IssuerName} of
- Id when Bool == true ->
- {ok, ErlCert, Cert};
- {_, IssuerName} when Bool == true ->
- {ok, ErlCert, Cert};
- _ ->
- crl_issuer(IssuerName, CRL, Id, Rest, All, false)
- end;
-
-crl_issuer(undefined, CRL, _, [], CertsList, Ignore) ->
- crl_issuer(CRL, CertsList, Ignore);
-crl_issuer(CRLName, CRL, _, [], CertsList, Ignore) ->
- crl_issuer(CRLName, CRL, CertsList, Ignore).
-
-
-crl_issuer(_, [],_) ->
- {error, issuer_not_found};
-crl_issuer(CRL, [Cert | Rest], Ignore) ->
- ErlCert = public_key:pkix_decode_cert(Cert, otp),
- case public_key:pkix_is_issuer(CRL, ErlCert) andalso
- check_extension_crl_signer(ErlCert) andalso
- is_signer(ErlCert, CRL, Ignore)
- of
- true ->
- {ok, ErlCert,Cert};
- false ->
- crl_issuer(CRL, Rest, Ignore)
- end.
-
-crl_issuer(_,_, [],_) ->
- {error, issuer_not_found};
-crl_issuer(IssuerName, CRL, [Cert | Rest], Ignore) ->
- ErlCert = public_key:pkix_decode_cert(Cert, otp),
- TBSCertificate = ErlCert#'OTPCertificate'.tbsCertificate,
- Issuer = public_key:pkix_normalize_name(
- TBSCertificate#'OTPTBSCertificate'.subject),
-
- case
- public_key:pkix_is_issuer(CRL, ErlCert) andalso
- check_extension_crl_signer(ErlCert) andalso
- is_signer(ErlCert, CRL, Ignore)
- of
- true ->
- case pubkey_cert:is_issuer(Issuer, IssuerName) of
- true ->
- {ok, ErlCert,Cert};
- false ->
- crl_issuer(IssuerName, CRL, Rest, Ignore)
- end;
- false ->
- crl_issuer(IssuerName, CRL, Rest, Ignore)
- end.
-
-read_certs(Test) ->
- File = test_file(Test),
- Ders = erl_make_certs:pem_to_der(File),
- [Cert || {'Certificate', Cert, not_encrypted} <- Ders].
-
-read_crls(Test) ->
- File = test_file(Test),
- Ders = erl_make_certs:pem_to_der(File),
- [CRL || {'CertificateList', CRL, not_encrypted} <- Ders].
-test_file(Test) ->
- io:format("TEST: ~p~n", [Test]),
- file(?CONV, lists:append(string:tokens(Test, " -")) ++ ".pem").
+dp_crlissuer_to_issuer(DPCRLIssuer) ->
+ [{directoryName, Issuer}] = pubkey_cert_records:transform(DPCRLIssuer, decode),
+ Issuer.
-file(Sub,File) ->
- TestDir = case get(datadir) of
- undefined -> "./pkits_SUITE_data";
- Dir when is_list(Dir) ->
- Dir
- end,
- AbsFile = filename:join([TestDir,Sub,File]),
- case filelib:is_file(AbsFile) of
- true -> ok;
- false ->
- ?error("Couldn't read data from ~p ~n",[AbsFile])
- end,
- AbsFile.
+%%%%%%%%%%%%%%% CA mappings %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
cas(Chap) ->
CAS = intermidiate_cas(Chap),
@@ -1242,17 +1021,15 @@ intermidiate_cas(Chap) when Chap == "4.6.2" ->
intermidiate_cas(Chap) when Chap == "4.6.3" ->
["basicConstraints Not Critical cA False CA Cert"];
-intermidiate_cas(Chap) when Chap == "4.5.2";
- Chap == "4.5.5" ->
- ["Basic Self-Issued New Key CA Cert"];
-
-intermidiate_cas(Chap) when Chap == "4.5.1" ->
+intermidiate_cas(Chap) when Chap == "4.5.1";
+ Chap == "4.5.2" ->
["Basic Self-Issued New Key OldWithNew CA Cert", "Basic Self-Issued New Key CA Cert"];
intermidiate_cas(Chap) when Chap == "4.5.3" ->
["Basic Self-Issued Old Key NewWithOld CA Cert", "Basic Self-Issued Old Key CA Cert"];
-intermidiate_cas(Chap) when Chap == "4.5.4" ->
+intermidiate_cas(Chap) when Chap == "4.5.4";
+ Chap == "4.5.5" ->
["Basic Self-Issued Old Key CA Cert"];
intermidiate_cas(Chap) when Chap == "4.13.1";
@@ -1301,9 +1078,6 @@ intermidiate_cas(Chap) when Chap == "4.13.19" ->
["nameConstraints DN1 Self-Issued CA Cert",
"nameConstraints DN1 CA Cert"];
-intermidiate_cas(Chap) when Chap == "4.5.6" ->
- ["Basic Self-Issued CRL Signing Key CA Cert"];
-
intermidiate_cas(Chap) when Chap == "4.7.1";
Chap == "4.7.4" ->
["keyUsage Critical keyCertSign False CA Cert"];
@@ -1387,29 +1161,349 @@ intermidiate_cas(Chap) when Chap == "4.6.16" ->
"pathLenConstraint0 Self-Issued CA Cert",
"pathLenConstraint0 CA Cert"];
-intermidiate_cas(Chap) when Chap == "4.5.7";
- Chap == "4.5.8"
- ->
- ["Basic Self-Issued CRL Signing Key CRL Cert",
- "Basic Self-Issued CRL Signing Key CA Cert"].
+intermidiate_cas(Chap) when Chap == "4.4.1" ->
+ ["No CRL CA Cert"];
-error(Format, Args, File0, Line) ->
- File = filename:basename(File0),
- Pid = group_leader(),
- Pid ! {failed, File, Line},
- io:format(Pid, "~s(~p): ERROR"++Format, [File,Line|Args]).
+intermidiate_cas(Chap) when Chap == "4.4.2" ->
+ ["Revoked subCA Cert", "Good CA Cert"];
-warning(Format, Args, File0, Line) ->
- File = filename:basename(File0),
- io:format("~s(~p): Warning "++Format, [File,Line|Args]).
+intermidiate_cas(Chap) when Chap == "4.4.3" ->
+ ["Good CA Cert"];
+
+intermidiate_cas(Chap) when Chap == "4.4.4" ->
+ ["Bad CRL Signature CA Cert"];
+
+intermidiate_cas(Chap) when Chap == "4.4.5" ->
+ ["Bad CRL Issuer Name CA Cert"];
+
+intermidiate_cas(Chap) when Chap == "4.4.6" ->
+ ["Wrong CRL CA Cert"];
+
+intermidiate_cas(Chap) when Chap == "4.4.7" ->
+ ["Two CRLs CA Cert"];
+
+intermidiate_cas(Chap) when Chap == "4.4.8" ->
+ ["Unknown CRL Entry Extension CA Cert"];
+
+intermidiate_cas(Chap) when Chap == "4.4.9";
+ Chap == "4.4.10" ->
+ ["Unknown CRL Extension CA Cert"];
+
+intermidiate_cas(Chap) when Chap == "4.4.11" ->
+ ["Old CRL nextUpdate CA Cert"];
+
+intermidiate_cas(Chap) when Chap == "4.4.12" ->
+ ["pre2000 CRL nextUpdate CA Cert"];
+
+intermidiate_cas(Chap) when Chap == "4.4.13" ->
+ ["GeneralizedTime CRL nextUpdate CA Cert"];
+
+intermidiate_cas(Chap) when Chap == "4.4.14";
+ Chap == "4.4.15" ->
+ ["Negative Serial Number CA Cert"];
+
+intermidiate_cas(Chap) when Chap == "4.4.16";
+ Chap == "4.4.17";
+ Chap == "4.4.18" ->
+ ["Long Serial Number CA Cert"];
+
+intermidiate_cas(Chap) when Chap == "4.4.19";
+ Chap == "4.4.20" ->
+ ["Separate Certificate and CRL Keys Certificate Signing CA Cert"];
+
+intermidiate_cas(Chap) when Chap == "4.4.21" ->
+ ["Separate Certificate and CRL Keys CA2 Certificate Signing CA Cert"];
+
+intermidiate_cas(Chap) when Chap == "4.14.1";
+ Chap == "4.14.2";
+ Chap == "4.14.3";
+ Chap == "4.14.4" ->
+ ["distributionPoint1 CA Cert"];
+intermidiate_cas(Chap) when Chap == "4.14.5";
+ Chap == "4.14.6";
+ Chap == "4.14.7";
+ Chap == "4.14.8";
+ Chap == "4.14.9" ->
+ ["distributionPoint2 CA Cert"];
+
+intermidiate_cas(Chap) when Chap == "4.14.10" ->
+ ["No issuingDistributionPoint CA Cert"];
+
+intermidiate_cas(Chap) when Chap == "4.14.11" ->
+ ["onlyContainsUserCerts CA Cert"];
+
+intermidiate_cas(Chap) when Chap == "4.14.12";
+ Chap == "4.14.13" ->
+ ["onlyContainsCACerts CA Cert"];
+
+intermidiate_cas(Chap) when Chap == "4.14.14" ->
+ ["onlyContainsAttributeCerts CA Cert"];
+
+intermidiate_cas(Chap) when Chap == "4.14.15";
+ Chap == "4.14.16" ->
+ ["onlySomeReasons CA1 Cert"];
+
+intermidiate_cas(Chap) when Chap == "4.14.17" ->
+ ["onlySomeReasons CA2 Cert"];
+
+intermidiate_cas(Chap) when Chap == "4.14.18" ->
+ ["onlySomeReasons CA3 Cert"];
+
+intermidiate_cas(Chap) when Chap == "4.14.19";
+ Chap == "4.14.20";
+ Chap == "4.14.21" ->
+ ["onlySomeReasons CA4 Cert"];
+intermidiate_cas(Chap) when Chap == "4.14.22";
+ Chap == "4.14.23" ->
+ ["indirectCRL CA1 Cert"];
+
+intermidiate_cas(Chap) when Chap == "4.14.24";
+ Chap == "4.14.25";
+ Chap == "4.14.26" ->
+ ["indirectCRL CA2 Cert"];
+
+intermidiate_cas(Chap) when Chap == "4.14.27" ->
+ ["indirectCRL CA2 Cert"];
+
+intermidiate_cas(Chap) when Chap == "4.14.28";
+ Chap == "4.14.29" ->
+ ["indirectCRL CA3 Cert"];
+
+intermidiate_cas(Chap) when Chap == "4.14.31";
+ Chap == "4.14.32";
+ Chap == "4.14.33" ->
+ ["indirectCRL CA6 Cert"];
+
+intermidiate_cas(Chap) when Chap == "4.14.34";
+ Chap == "4.14.35" ->
+ ["indirectCRL CA5 Cert"];
+
+intermidiate_cas(Chap) when Chap == "4.15.1" ->
+ ["deltaCRLIndicator No Base CA Cert"];
+
+intermidiate_cas(Chap) when Chap == "4.15.2";
+ Chap == "4.15.3";
+ Chap == "4.15.4";
+ Chap == "4.15.5";
+ Chap == "4.15.6";
+ Chap == "4.15.7" ->
+ ["deltaCRL CA1 Cert"];
+
+intermidiate_cas(Chap) when Chap == "4.15.8";
+ Chap == "4.15.9" ->
+ ["deltaCRL CA2 Cert"];
+
+intermidiate_cas(Chap) when Chap == "4.15.10" ->
+ ["deltaCRL CA3 Cert"];
+
+intermidiate_cas(Chap) when Chap == "4.5.6";
+ Chap == "4.5.7" ->
+ ["Basic Self-Issued CRL Signing Key CA Cert"];
+intermidiate_cas(Chap) when Chap == "4.5.8" ->
+ ["Basic Self-Issued CRL Signing Key CRL Cert"].
+
+
+%%%%%%%%%%%%%%% CRL mappings %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+crl_names("4.4.1") ->
+ ["Trust Anchor Root CRL"];
+crl_names("4.4.2") ->
+ ["Trust Anchor Root CRL", "Good CA CRL", "Revoked subCA CRL"];
+crl_names("4.4.3") ->
+ ["Trust Anchor Root CRL", "Good CA CRL", "Revoked subCA CRL"];
+crl_names("4.4.4") ->
+ ["Trust Anchor Root CRL", "Bad CRL Signature CA CRL"];
+crl_names("4.4.5") ->
+ ["Trust Anchor Root CRL", "Bad CRL Issuer Name CA CRL"];
+crl_names("4.4.6") ->
+ ["Trust Anchor Root CRL", "Wrong CRL CA CRL"];
+crl_names("4.4.7") ->
+ ["Trust Anchor Root CRL", "Two CRLs CA Good CRL", "Two CRLs CA Bad CRL"];
+crl_names("4.4.8") ->
+ ["Trust Anchor Root CRL", "Unknown CRL Entry Extension CA CRL"];
+crl_names(Chap) when Chap == "4.4.9";
+ Chap == "4.4.10"->
+ ["Trust Anchor Root CRL", "Unknown CRL Extension CA CRL"];
+crl_names("4.4.11") ->
+ ["Trust Anchor Root CRL", "Old CRL nextUpdate CA CRL"];
+crl_names("4.4.12") ->
+ ["Trust Anchor Root CRL", "pre2000 CRL nextUpdate CA CRL"];
+crl_names("4.4.13") ->
+ ["Trust Anchor Root CRL", "GeneralizedTime CRL nextUpdate CA CRL"];
+crl_names(Chap) when Chap == "4.4.14";
+ Chap == "4.4.15"->
+ ["Trust Anchor Root CRL", "Negative Serial Number CA CRL"];
+crl_names(Chap) when Chap == "4.4.16";
+ Chap == "4.4.17";
+ Chap == "4.4.18" ->
+ ["Trust Anchor Root CRL", "Long Serial Number CA CRL"];
+crl_names(Chap)when Chap == "4.4.19";
+ Chap == "4.4.20" ->
+ ["Trust Anchor Root CRL", "Separate Certificate and CRL Keys CRL"];
+crl_names("4.4.21") ->
+ ["Trust Anchor Root CRL", "Separate Certificate and CRL Keys CA2 CRL"];
+crl_names(Chap) when Chap == "4.5.1";
+ Chap == "4.5.2"->
+ ["Trust Anchor Root CRL", "Basic Self-Issued New Key CA CRL"];
+crl_names(Chap) when Chap == "4.5.3";
+ Chap == "4.5.4";
+ Chap == "4.5.5" ->
+ ["Trust Anchor Root CRL", "Basic Self-Issued Old Key Self-Issued Cert CRL",
+ "Basic Self-Issued Old Key CA CRL"];
+crl_names(Chap) when Chap == "4.5.6";
+ Chap == "4.5.7";
+ Chap == "4.5.8" ->
+ ["Trust Anchor Root CRL", "Basic Self-Issued CRL Signing Key CRL Cert CRL",
+ "Basic Self-Issued CRL Signing Key CA CRL"
+ ];
+crl_names("4.7.4") ->
+ ["Trust Anchor Root CRL", "keyUsage Critical cRLSign False CA CRL"];
+crl_names("4.7.5") ->
+ ["Trust Anchor Root CRL", "keyUsage Not Critical cRLSign False CA CRL"];
+crl_names(Chap) when Chap == "4.14.1";
+ Chap == "4.14.2";
+ Chap == "4.14.3";
+ Chap == "4.14.4" ->
+ ["Trust Anchor Root CRL", "distributionPoint1 CA CRL"];
+crl_names(Chap) when Chap == "4.14.5";
+ Chap == "4.14.6";
+ Chap == "4.14.7";
+ Chap == "4.14.8";
+ Chap == "4.14.9" ->
+ ["Trust Anchor Root CRL", "distributionPoint2 CA CRL"];
+crl_names("4.14.10") ->
+ ["Trust Anchor Root CRL", "No issuingDistributionPoint CA CRL"];
+crl_names("4.14.11") ->
+ ["Trust Anchor Root CRL", "onlyContainsUserCerts CA CRL"];
+crl_names(Chap) when Chap == "4.14.12";
+ Chap == "4.14.13" ->
+ ["Trust Anchor Root CRL", "onlyContainsCACerts CA CRL"];
+crl_names("4.14.14") ->
+ ["Trust Anchor Root CRL", "onlyContainsAttributeCerts CA CRL"];
+crl_names(Chap) when Chap == "4.14.15";
+ Chap == "4.14.16" ->
+ ["Trust Anchor Root CRL", "onlySomeReasons CA1 compromise CRL",
+ "onlySomeReasons CA1 other reasons CRL"];
+crl_names("4.14.17") ->
+ ["Trust Anchor Root CRL",
+ "onlySomeReasons CA2 CRL1", "onlySomeReasons CA2 CRL2"];
+crl_names("4.14.18") ->
+ ["Trust Anchor Root CRL",
+ "onlySomeReasons CA3 compromise CRL", "onlySomeReasons CA3 other reasons CRL"];
+crl_names(Chap) when Chap == "4.14.19";
+ Chap == "4.14.20";
+ Chap == "4.14.21" ->
+ ["Trust Anchor Root CRL", "onlySomeReasons CA4 compromise CRL",
+ "onlySomeReasons CA4 other reasons CRL"];
+crl_names(Chap) when Chap == "4.14.22";
+ Chap == "4.14.23";
+ Chap == "4.14.24";
+ Chap == "4.14.25";
+ Chap == "4.14.26" ->
+ ["Trust Anchor Root CRL", "indirectCRL CA1 CRL"];
+crl_names("4.14.27") ->
+ ["Trust Anchor Root CRL", "Good CA CRL"];
+
+crl_names(Chap) when Chap == "4.14.28";
+ Chap == "4.14.29" ->
+ ["Trust Anchor Root CRL", "indirectCRL CA3 CRL", "indirectCRL CA3 cRLIssuer CRL"];
+crl_names("4.14.30") ->
+ ["Trust Anchor Root CRL", "indirectCRL CA4 cRLIssuer CRL"];
+crl_names(Chap) when Chap == "4.14.31";
+ Chap == "4.14.32";
+ Chap == "4.14.33";
+ Chap == "4.14.34";
+ Chap == "4.14.35" ->
+ ["Trust Anchor Root CRL", "indirectCRL CA5 CRL"];
+crl_names("4.15.1") ->
+ ["Trust Anchor Root CRL", "deltaCRLIndicator No Base CA CRL"];
+crl_names(Chap) when Chap == "4.15.2";
+ Chap == "4.15.3";
+ Chap == "4.15.4";
+ Chap == "4.15.5";
+ Chap == "4.15.6";
+ Chap == "4.15.7" ->
+ ["Trust Anchor Root CRL", "deltaCRL CA1 CRL", "deltaCRL CA1 deltaCRL"];
+crl_names(Chap) when Chap == "4.15.8";
+ Chap == "4.15.9" ->
+ ["Trust Anchor Root CRL", "deltaCRL CA2 CRL", "deltaCRL CA2 deltaCRL"];
+crl_names("4.15.10") ->
+ ["Trust Anchor Root CRL", "deltaCRL CA3 CRL", "deltaCRL CA3 deltaCRL"].
+
+crl_root_cert() ->
+ "Trust Anchor Root Certificate".
+
+crl_path("Trust Anchor Root CRL") ->
+ []; %% Signed directly by crl_root_cert
+crl_path("Revoked subCA CRL") ->
+ ["Good CA Cert", "Revoked subCA Cert"];
+crl_path("indirectCRL CA3 cRLIssuer CRL") ->
+ ["indirectCRL CA3 Cert", "indirectCRL CA3 cRLIssuer Cert"];
+crl_path("Two CRLs CA Good CRL") ->
+ ["Two CRLs CA Cert"];
+crl_path("Two CRLs CA Bad CRL") ->
+ ["Two CRLs CA Cert"];
+crl_path("Separate Certificate and CRL Keys CRL") ->
+ ["Separate Certificate and CRL Keys CRL Signing Cert"];
+crl_path("Separate Certificate and CRL Keys CA2 CRL") ->
+ ["Separate Certificate and CRL Keys CA2 CRL Signing Cert"];
+crl_path("Basic Self-Issued Old Key Self-Issued Cert CRL") ->
+ ["Basic Self-Issued Old Key CA Cert"];
+crl_path("Basic Self-Issued Old Key CA CRL") ->
+ ["Basic Self-Issued Old Key CA Cert", "Basic Self-Issued Old Key NewWithOld CA Cert"];
+
+crl_path("Basic Self-Issued CRL Signing Key CRL Cert CRL") ->
+ ["Basic Self-Issued CRL Signing Key CA Cert"];
+crl_path("Basic Self-Issued CRL Signing Key CA CRL") ->
+ ["Basic Self-Issued CRL Signing Key CA Cert", "Basic Self-Issued CRL Signing Key CRL Cert"];
+
+crl_path("onlySomeReasons CA1 compromise CRL") ->
+ ["onlySomeReasons CA1 Cert"];
+crl_path("onlySomeReasons CA1 other reasons CRL") ->
+ ["onlySomeReasons CA1 Cert"];
+crl_path("onlySomeReasons CA3 other reasons CRL") ->
+ ["onlySomeReasons CA3 Cert"];
+crl_path("onlySomeReasons CA3 compromise CRL") ->
+ ["onlySomeReasons CA3 Cert"];
+crl_path("onlySomeReasons CA4 compromise CRL") ->
+ ["onlySomeReasons CA4 Cert"];
+crl_path("onlySomeReasons CA4 other reasons CRL") ->
+ ["onlySomeReasons CA4 Cert"];
+crl_path("Basic Self-Issued New Key CA CRL") ->
+ ["Basic Self-Issued New Key CA Cert"];
+crl_path("deltaCRL CA1 deltaCRL") ->
+ crl_path("deltaCRL CA2 CRL");
+crl_path("deltaCRL CA2 deltaCRL") ->
+ crl_path("deltaCRL CA2 CRL");
+crl_path("deltaCRL CA3 deltaCRL") ->
+ crl_path("deltaCRL CA3 CRL");
+crl_path(CRL) when CRL == "onlySomeReasons CA2 CRL1";
+ CRL == "onlySomeReasons CA2 CRL2" ->
+ ["onlySomeReasons CA2 Cert"];
+
+crl_path(CRL) ->
+ L = length(CRL),
+ Base = string:sub_string(CRL, 1, L -3),
+ [Base ++ "Cert"].
+
+crls(CRLS) ->
+ lists:foldl(fun([], Acc) ->
+ Acc;
+ (CRLFile, Acc) ->
+ [CRL] = read_crls(CRLFile),
+ [CRL | Acc]
+ end, [], CRLS).
+
+
+%% TODO: If we implement policy support
%% Certificate policy tests need special handling. They can have several
%% sub tests and we need to check the outputs are correct.
-certificate_policies() ->
+certificate_policies_tests() ->
%%{ "4.8", "Certificate Policies" },
[{"4.8.1.1", "All Certificates Same Policy Test1", "-policy anyPolicy -explicit_policy", "True", ?NIST1, ?NIST1, 0},
- {"4.8.1.2", "All Certificates Same Policy Test1", "-policy ?NIST1 -explicit_policy", "True", ?NIST1, ?NIST1, 0},
+ {"4.8.1.2", "All Certificates Same Policy Test1", "-policy ?NIST1BasicSelfIssuedCRLSigningKeyCACert.pem -explicit_policy", "True", ?NIST1, ?NIST1, 0},
{"4.8.1.3", "All Certificates Same Policy Test1", "-policy ?NIST2 -explicit_policy", "True", ?NIST1, "<empty>", 43},
{"4.8.1.4", "All Certificates Same Policy Test1", "-policy ?NIST1 -policy ?NIST2 -explicit_policy", "True", ?NIST1, ?NIST1, 0},
{"4.8.2.1", "All Certificates No Policies Test2", "-policy anyPolicy", "False", "<empty>", "<empty>", 0},
@@ -1443,7 +1537,7 @@ certificate_policies() ->
{"4.8.18.2", "User Notice Qualifier Test18", "-policy ?NIST2", "True", "?NIST1:?NIST2", "?NIST2", 0},
{"4.8.19", "User Notice Qualifier Test19", "-policy anyPolicy", "False", "?NIST1", "?NIST1", 0},
{"4.8.20", "CPS Pointer Qualifier Test20", "-policy anyPolicy -explicit_policy", "True", "?NIST1", "?NIST1", 0}].
-require_explicit_policy() ->
+require_explicit_policy_tests() ->
%%{ "4.9", "Require Explicit Policy" },
[{"4.9.1", "Valid RequireExplicitPolicy Test1", "-policy anyPolicy", "False", "<empty>", "<empty>", 0},
{"4.9.2", "Valid RequireExplicitPolicy Test2", "-policy anyPolicy", "False", "<empty>", "<empty>", 0},
@@ -1453,7 +1547,7 @@ require_explicit_policy() ->
{"4.9.6", "Valid Self-Issued requireExplicitPolicy Test6", "-policy anyPolicy", "False", "<empty>", "<empty>", 0},
{"4.9.7", "Invalid Self-Issued requireExplicitPolicy Test7", "-policy anyPolicy", "True", "<empty>", "<empty>", 43},
{"4.9.8", "Invalid Self-Issued requireExplicitPolicy Test8", "-policy anyPolicy", "True", "<empty>", "<empty>", 43}].
-policy_mappings() ->
+policy_mappings_tests() ->
%%{ "4.10", "Policy Mappings" },
[{"4.10.1.1", "Valid Policy Mapping Test1", "-policy ?NIST1", "True", "?NIST1", "?NIST1", 0},
{"4.10.1.2", "Valid Policy Mapping Test1", "-policy ?NIST2", "True", "?NIST1", "<empty>", 43},
@@ -1483,7 +1577,7 @@ policy_mappings() ->
%% TODO: check notice display
{"4.10.14", "Valid Policy Mapping Test14", "-policy anyPolicy", "True", "?NIST1", "?NIST1", 0}].
-inhibit_policy_mapping() ->
+inhibit_policy_mapping_tests() ->
%%{ "4.11", "Inhibit Policy Mapping" },
[{"4.11.1", "Invalid inhibitPolicyMapping Test1", "-policy anyPolicy", "True", "<empty>", "<empty>", 43},
{"4.11.2", "Valid inhibitPolicyMapping Test2", "-policy anyPolicy", "True", "?NIST1", "?NIST1", 0},
@@ -1496,7 +1590,7 @@ inhibit_policy_mapping() ->
{"4.11.9", "Invalid Self-Issued inhibitPolicyMapping Test9", "-policy anyPolicy", "True", "<empty>", "<empty>", 43},
{"4.11.10", "Invalid Self-Issued inhibitPolicyMapping Test10", "-policy anyPolicy", "True", "<empty>", "<empty>", 43},
{"4.11.11", "Invalid Self-Issued inhibitPolicyMapping Test11", "-policy anyPolicy", "True", "<empty>", "<empty>", 43}].
-inhibit_any_policy() ->
+inhibit_any_policy_tests() ->
%%{ "4.12", "Inhibit Any Policy" },
[{"4.12.1", "Invalid inhibitAnyPolicy Test1", "-policy anyPolicy", "True", "<empty>", "<empty>", 43},
{"4.12.2", "Valid inhibitAnyPolicy Test2", "-policy anyPolicy", "True", "?NIST1", "?NIST1", 0},
@@ -1509,12 +1603,3 @@ inhibit_any_policy() ->
{"4.12.8", "Invalid Self-Issued inhibitAnyPolicy Test8", 43 },
{"4.12.9", "Valid Self-Issued inhibitAnyPolicy Test9", ok},
{"4.12.10", "Invalid Self-Issued inhibitAnyPolicy Test10", 43 }].
-
-crypto_support_check(Config) ->
- try crypto:sha256(<<"Test">>) of
- _ ->
- Config
- catch error:notsup ->
- crypto:stop(),
- {skip, "To old version of openssl"}
- end.
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/BadCRLIssuerNameCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/BadCRLIssuerNameCACRL.pem
new file mode 100644
index 0000000000..a4cf643928
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/BadCRLIssuerNameCACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIBzDCBtQIBATANBgkqhkiG9w0BAQsFADBSMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEiMCAGA1UEAxMZSW5jb3JyZWN0IENS
+TCBJc3N1ZXIgTmFtZRcNMTAwMTAxMDgzMDAwWhcNMzAxMjMxMDgzMDAwWqAvMC0w
+HwYDVR0jBBgwFoAUEXLyNV0E1Q5KIAcHQSj9lHAAHHEwCgYDVR0UBAMCAQEwDQYJ
+KoZIhvcNAQELBQADggEBAIIk/+6+dqID/yKmKYDaVgXh/yLWZO4uV1m6OYRSrdfa
+8aqYcgwChIaIIX465VDCpGO2LMOXV1Z/COerJguUNRGaiSQCSrsIpNGGZTQE+HNN
+C5mabMfPuSVaBgBMzQZTY3ggLNr6x1G7dXlm+Fpo+q/fSznBVsGFHCgtp53FJHBi
+Xcd3LTBFyZLiyhITqm2xvTwyzl/URcDYBp+tWTmn7ZxADdoThK7OkY2U13O0vxKj
+/haiS1NuVbX2QkS+eH399jGQHAHiBOFlRPs8FGdshTWOJxmLMMTIuS7cIEH3Oz8t
+b1Go7j4qHIYr/b2uKHb9RBMFNRKsZfqVKEQ+1J8TPNc=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/BadCRLSignatureCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/BadCRLSignatureCACRL.pem
new file mode 100644
index 0000000000..e17fac1f20
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/BadCRLSignatureCACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIBxzCBsAIBATANBgkqhkiG9w0BAQsFADBNMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEdMBsGA1UEAxMUQmFkIENSTCBTaWdu
+YXR1cmUgQ0EXDTEwMDEwMTA4MzAwMFoXDTMwMTIzMTA4MzAwMFqgLzAtMB8GA1Ud
+IwQYMBaAFDGLNY2eRGEwFN7nCy4UGUgk2/m9MAoGA1UdFAQDAgEBMA0GCSqGSIb3
+DQEBCwUAA4IBAQFsPfAbIcqMI+lzJmY396TIwn2ubAkQgm95U712CIuw00auDjGM
+iebAOLbNLgSktivN23rc5rPxSOqeWsNfgVxwCPKvBv7v4PsLQhzK2OyonV5Cmdl1
+biEymv88bEUcEnibvH3O0Lg3RySMNjPYqZgPfwF9b0iTYeb2JYk+lX+m3WGr5UPg
+M9nuC6sychRxGgF1AQOd/N6spvgSAazEyNqCWhcwdrOeM+HEDPAFOOFAhmCp+jjh
+Fgh2E2Gq1vbxGSzzKlRBDrO1hovT9XhvEa2SjopbWMbfOR/wPTjxEImIQDWw69RL
+KnTL/OdnUXifoSNJIJK3ffPTSG5AEHFUm6j+
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/BadSignedCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/BadSignedCACRL.pem
new file mode 100644
index 0000000000..58c89f81c6
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/BadSignedCACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIBwDCBqQIBATANBgkqhkiG9w0BAQsFADBGMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEWMBQGA1UEAxMNQmFkIFNpZ25lZCBD
+QRcNMTAwMTAxMDgzMDAwWhcNMzAxMjMxMDgzMDAwWqAvMC0wHwYDVR0jBBgwFoAU
+e90QO0rgyN1EhU6IPFqLzZkik68wCgYDVR0UBAMCAQEwDQYJKoZIhvcNAQELBQAD
+ggEBAEBt9Jji+aprAMfoXE8zclIPjs520FFAXSPqsJt77l8vTA50oUbD0sILCDfA
+raU16Pm3lIT3wuEAUmM7luTzG5Kg5qbgyv9cLm3rSJOADZCw6oahFAEbZyqOsgfP
+QYRy9EOeg8TUc6EPrlZhTrZzf2kgmDeL6WxTz2Len415oFf6JsDNMLcHSQkDzmdi
+OxYXDA2w5khPrzX0cxbMpmZVJiQksdKJg/RGGCqNJVcZwTqAZFNzSldJvx7fqQPI
+SkcKznfT/cczC37Q2uCupIyG4cntiRdIztkHiKQFRkiUxIMQA3ky3t+ib3lZDnl2
+VwGLcmmYuu4ScsqL+OP0ZjpzZKM=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/BadnotAfterDateCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/BadnotAfterDateCACRL.pem
new file mode 100644
index 0000000000..3c952437ce
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/BadnotAfterDateCACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIBxzCBsAIBATANBgkqhkiG9w0BAQsFADBNMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEdMBsGA1UEAxMUQmFkIG5vdEFmdGVy
+IERhdGUgQ0EXDTEwMDEwMTA4MzAwMFoXDTMwMTIzMTA4MzAwMFqgLzAtMB8GA1Ud
+IwQYMBaAFCwO/ffuPPOkZuznBZ+Is+LPdEbYMAoGA1UdFAQDAgEBMA0GCSqGSIb3
+DQEBCwUAA4IBAQBcYZ4ucABlH6Z0XtJMAmcrX6txzEawWpikzfzapuRe0YBL6IHm
+LRqW+vpBfR5vSQ2PBLXFJA9eVpJjjBieF9USt58DsvTF1nuPMJCk1WhxfDrE/hWy
+vR3tnLW38c6e4+omJN9fotQilw+sq32j1yQjnR4KU+VxMs6sgs1W2XmmWpTP+fQw
+W9ukAXGzwQNmBb4mbOjLDVv3fXxzVI5gK8bhcxh83cfmHMkI+dp1g4nAxQ5gRTaw
+2ypGWph9170Z1Y/W6eznbNGA40YreXqqZeq8ujpjYlksC6tpQkOp8lBJGwi7fKaI
+IxV4vXSSbExz7A4CFQ1zYx1ciciCGWcyOyqx
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/BadnotBeforeDateCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/BadnotBeforeDateCACRL.pem
new file mode 100644
index 0000000000..fb4bf11a7c
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/BadnotBeforeDateCACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIByDCBsQIBATANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEeMBwGA1UEAxMVQmFkIG5vdEJlZm9y
+ZSBEYXRlIENBFw0xMDAxMDEwODMwMDBaFw0zMDEyMzEwODMwMDBaoC8wLTAfBgNV
+HSMEGDAWgBRjPrwanvuh8lmhL0uV/ubeVriGQDAKBgNVHRQEAwIBATANBgkqhkiG
+9w0BAQsFAAOCAQEAnN6H352YCy4U82Ndz+iFkIxTLVmwiMi00RViqbziNXLSYbbX
+MpJsCPn93wGhEOokDcJBoWq6/2QeHHmEs9rN758+loP1P1R27wgicrcPIEbT0+BT
+st0TYtZExQoXWVe5QtyTa4cNR7Wz88ga1xf3GyngnjFAHmAWrU4IOpxhKvs2FOS1
+zPaxzdgvNZyxvltIUt6zujlAxDf3wJK9qWyZSt7ORob8RVaAJR12UA60qyVbA2HX
+BdAavTzVJatY2AjtnIiFWAmtI3s/jIvLLNX5yUVVDSEDCI3zWY5YR/URyf4uViEk
+qyy8PQ83mVkpTjaWRxjFrcIvwlUvmRlHTvp2NQ==
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/BasicSelfIssuedCRLSigningKeyCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/BasicSelfIssuedCRLSigningKeyCACRL.pem
new file mode 100644
index 0000000000..9a34f8a612
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/BasicSelfIssuedCRLSigningKeyCACRL.pem
@@ -0,0 +1,13 @@
+-----BEGIN X509 CRL-----
+MIIB+zCB5AIBATANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEtMCsGA1UEAxMkQmFzaWMgU2VsZi1J
+c3N1ZWQgQ1JMIFNpZ25pbmcgS2V5IENBFw0xMDAxMDEwODMwMDBaFw0zMDEyMzEw
+ODMwMDBaMCIwIAIBAxcNMTAwMTAxMDgzMDAwWjAMMAoGA1UdFQQDCgEBoC8wLTAf
+BgNVHSMEGDAWgBQkwVVx+p7hIYUq8K1hpxW51U1DFzAKBgNVHRQEAwIBATANBgkq
+hkiG9w0BAQsFAAOCAQEAE4g/85gXzIM9TYVxAXxgc8KwavltVn6wFCluy5zwxGRz
+lpJXjJWctmJ+Z1qPVlL/K8aOW+YW+keL5zcINUYHxipyQLt+W7qeFDivq5ABgK62
+qpZ6tpwTa02IXkUDixs2QVXM7vXLrHlYacLGjIti+LUP3R7y3fLSjg3BMZcvwA17
+0N1YhfYZrNVwfwqv2H58HIdo5+SSCcUGZe9sbkqRINta3/OKnlS5e7Y4KW5I65oT
+2eX4iPmPNR6NqNv6D+mMXxt6uJGw8NRA2NqO8MS8UqkA6bmZldcdkefcJ5ETJu8r
+xewaq22gblqFL2vdcM/JJ61rcuPVZTqChfDyaQu/rg==
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/BasicSelfIssuedCRLSigningKeyCRLCertCRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/BasicSelfIssuedCRLSigningKeyCRLCertCRL.pem
new file mode 100644
index 0000000000..447b7ec511
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/BasicSelfIssuedCRLSigningKeyCRLCertCRL.pem
@@ -0,0 +1,15 @@
+-----BEGIN X509 CRL-----
+MIICZjCCAU4CAQEwDQYJKoZIhvcNAQELBQAwXTELMAkGA1UEBhMCVVMxHzAdBgNV
+BAoTFlRlc3QgQ2VydGlmaWNhdGVzIDIwMTExLTArBgNVBAMTJEJhc2ljIFNlbGYt
+SXNzdWVkIENSTCBTaWduaW5nIEtleSBDQRcNMTAwMTAxMDgzMDAwWhcNMzAxMjMx
+MDgzMDAwWqCBvDCBuTAfBgNVHSMEGDAWgBQpmkUuNpWd7PJeVJwT1dn2RJEsEzCB
+iQYDVR0cAQH/BH8wfaB7oHmkdzB1MQswCQYDVQQGEwJVUzEfMB0GA1UEChMWVGVz
+dCBDZXJ0aWZpY2F0ZXMgMjAxMTFFMEMGA1UEAxM8U2VsZi1Jc3N1ZWQgQ2VydCBE
+UCBmb3IgQmFzaWMgU2VsZi1Jc3N1ZWQgQ1JMIFNpZ25pbmcgS2V5IENBMAoGA1Ud
+FAQDAgEBMA0GCSqGSIb3DQEBCwUAA4IBAQAnpcVORfH/FTHsCl1sjvHIvPQTdrMI
+73pWCshHQB8/eCwDmTg+xFk7n4KCYSRgbN6ingc4wzbnLkb1WPmw80aXbqd4Tg+/
+U9T0ANxSewLBeW0zUFFcW5LjqrxCJVV4IMDXh+D6F+M8uEWE8/UGzpz3qkIBfW/a
+KHQszpVl7i9x0MhjHFAXkJbCuB7ymXntYfbSQNHvMWwbPW+nt0HadbJqjJy4FH4V
+kaSg/tkFa1StClKjm3T+GDhISPpDWthGUmS4n9cmj4QnPSd6E57d+ix0hb5oqQAg
+DYeL9/xaN7fOIzuGknzE3+p19OT8vNhvp7mRoMpZpzPtm0so48EXagMx
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/BasicSelfIssuedNewKeyCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/BasicSelfIssuedNewKeyCACRL.pem
new file mode 100644
index 0000000000..9c3d7d42ab
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/BasicSelfIssuedNewKeyCACRL.pem
@@ -0,0 +1,13 @@
+-----BEGIN X509 CRL-----
+MIIB8zCB3AIBATANBgkqhkiG9w0BAQsFADBVMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTElMCMGA1UEAxMcQmFzaWMgU2VsZi1J
+c3N1ZWQgTmV3IEtleSBDQRcNMTAwMTAxMDgzMDAwWhcNMzAxMjMxMDgzMDAwWjAi
+MCACAQMXDTEwMDEwMTA4MzAwMFowDDAKBgNVHRUEAwoBAaAvMC0wHwYDVR0jBBgw
+FoAUoPzALOtV7pIGbKke6V9fop9iI5UwCgYDVR0UBAMCAQEwDQYJKoZIhvcNAQEL
+BQADggEBAHAue0P1w9Vgt/mvhideeiLl2unmfJC0JKCIBeeWXl0CYW5jcSrshCwZ
+dHRM5uhs8RQCNRnJIrhwuRUvh9SaDsZGFWqAlOdYbkaXdzbX1cyTigQ0tCNTN+aD
+FxGTm+CWxi6awN2a2ZcOzZ3KuB8D7q0b1mVtnMFsvV7JEMyxZRWFm5/8yJAliOKc
+8wrWtDfbpkU6c1P0l2bPzvmDirISAKwj2IwibkbQimbISwt1b/jijPOQgiArom5S
+sHzZh2DI9xdDBXLImkJveUGF1d8zIngbvyn19Iz7bT8mJVANx2K0U6kC3ODgov8b
+ARv87w9CDE+nCZz505ZS1f/pVtswcPg=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/BasicSelfIssuedOldKeyCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/BasicSelfIssuedOldKeyCACRL.pem
new file mode 100644
index 0000000000..3f330c715a
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/BasicSelfIssuedOldKeyCACRL.pem
@@ -0,0 +1,13 @@
+-----BEGIN X509 CRL-----
+MIIB8zCB3AIBATANBgkqhkiG9w0BAQsFADBVMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTElMCMGA1UEAxMcQmFzaWMgU2VsZi1J
+c3N1ZWQgT2xkIEtleSBDQRcNMTAwMTAxMDgzMDAwWhcNMzAxMjMxMDgzMDAwWjAi
+MCACAQQXDTEwMDEwMTA4MzAwMFowDDAKBgNVHRUEAwoBAaAvMC0wHwYDVR0jBBgw
+FoAUiF++PzU5ZprrTcImGyaxKie1CCowCgYDVR0UBAMCAQEwDQYJKoZIhvcNAQEL
+BQADggEBALJPjOBcvrNJea5bTdcDaUR+3rWdWzwAFlIFN29RBQgnnwGyoPnMhBaI
+MBMMxrzPQiqc908w+4E82FSbeVKbutPPjs4izG8wE3jwPk5GmWPKozrZZ3Eliqd1
+9Kx1he6yCDnPpHZ6D4qm6qyy/xmspL7X3W40Dz56ldxu3DECDIrDACjdnu9PERDu
+acqDQPFKbcaZeCY9QX9/vsG+vrWH+HxEkzA/S9tyojYSLfVLvYubBd0OAWhTqqR6
+5h2Gx6z7DH6/1vtctb0HGg2VY7bVjpL0YhwXoTjZZXbWlLH1l7N+fP2cH5eqDwwF
+DRhIRhasjSucbUzOFXoc3dmBrOKwtjM=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/BasicSelfIssuedOldKeySelfIssuedCertCRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/BasicSelfIssuedOldKeySelfIssuedCertCRL.pem
new file mode 100644
index 0000000000..8fa388cd45
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/BasicSelfIssuedOldKeySelfIssuedCertCRL.pem
@@ -0,0 +1,15 @@
+-----BEGIN X509 CRL-----
+MIICVjCCAT4CAQEwDQYJKoZIhvcNAQELBQAwVTELMAkGA1UEBhMCVVMxHzAdBgNV
+BAoTFlRlc3QgQ2VydGlmaWNhdGVzIDIwMTExJTAjBgNVBAMTHEJhc2ljIFNlbGYt
+SXNzdWVkIE9sZCBLZXkgQ0EXDTEwMDEwMTA4MzAwMFoXDTMwMTIzMTA4MzAwMFqg
+gbQwgbEwHwYDVR0jBBgwFoAU3Q11jVNoEsTLFUDAFIYUFjChvq8wgYEGA1UdHAEB
+/wR3MHWgc6BxpG8wbTELMAkGA1UEBhMCVVMxHzAdBgNVBAoTFlRlc3QgQ2VydGlm
+aWNhdGVzIDIwMTExPTA7BgNVBAMTNFNlbGYtSXNzdWVkIENlcnQgRFAgZm9yIEJh
+c2ljIFNlbGYtSXNzdWVkIE9sZCBLZXkgQ0EwCgYDVR0UBAMCAQEwDQYJKoZIhvcN
+AQELBQADggEBAMCggIi2OzR9WO+bNMEVT0PdVnX9lNyjIAmJZj6g/SECKy85ZQp/
+Xs5YY43xxXswtJ+70cQGunzXIXd22a5xTlGlcM0orE2WIbDaoJskD3D5HKPCb6MR
+C0XwhV6KGmB+H/rb77EnHEHTHQ6SwKfSaVSVrVkaGD4HYHAKiOqOzkInultUR04p
+jTJ3watfZyB+L9Xmfj+Bz/GvTCDdYOQfs9uXhWH+C7HQPe8uXD1C1tSDD9+XnUHo
+rScJkZkZpExsGvF8ngtjp+0SUmCTM7VejHtKWD2l4KO+TkjEM9PG0+Hf75iOq9eO
+UdA7iu/ozw7EbowpmABJmeVSiWD9S4/deBc=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/DSACACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/DSACACRL.pem
new file mode 100644
index 0000000000..7d1d13a767
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/DSACACRL.pem
@@ -0,0 +1,7 @@
+-----BEGIN X509 CRL-----
+MIHeMIGeAgEBMAkGByqGSM44BAMwPzELMAkGA1UEBhMCVVMxHzAdBgNVBAoTFlRl
+c3QgQ2VydGlmaWNhdGVzIDIwMTExDzANBgNVBAMTBkRTQSBDQRcNMTAwMTAxMDgz
+MDAwWhcNMzAxMjMxMDgzMDAwWqAvMC0wHwYDVR0jBBgwFoAUj5DGjHToewzIWcd9
+PFtUWWAlC7EwCgYDVR0UBAMCAQEwCQYHKoZIzjgEAwMwADAtAhQCMhWXnJJuf+2W
+pXCHP72o0SdqdAIVAMsLFa667x5ODKAkIlFPI/Ge57Hj
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/DSAParametersInheritedCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/DSAParametersInheritedCACRL.pem
new file mode 100644
index 0000000000..545293a0db
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/DSAParametersInheritedCACRL.pem
@@ -0,0 +1,8 @@
+-----BEGIN X509 CRL-----
+MIHyMIGzAgEBMAkGByqGSM44BAMwVDELMAkGA1UEBhMCVVMxHzAdBgNVBAoTFlRl
+c3QgQ2VydGlmaWNhdGVzIDIwMTExJDAiBgNVBAMTG0RTQSBQYXJhbWV0ZXJzIElu
+aGVyaXRlZCBDQRcNMTAwMTAxMDgzMDAwWhcNMzAxMjMxMDgzMDAwWqAvMC0wHwYD
+VR0jBBgwFoAUZYGfcDqMrfZDHcjnj1WO6Evbh+IwCgYDVR0UBAMCAQEwCQYHKoZI
+zjgEAwMvADAsAhQ80F0tKnVM5ABE7rErqs6hgIc8gAIULN+1q3I9zEFZqSneZHjR
+yXC3FZQ=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/GeneralizedTimeCRLnextUpdateCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/GeneralizedTimeCRLnextUpdateCACRL.pem
new file mode 100644
index 0000000000..cab3080a90
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/GeneralizedTimeCRLnextUpdateCACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIB1DCBvQIBATANBgkqhkiG9w0BAQsFADBYMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEoMCYGA1UEAxMfR2VuZXJpemVkVGlt
+ZSBDUkwgbmV4dFVwZGF0ZSBDQRcNMTAwMTAxMDgzMDAwWhgPMjA1MDAxMDExMjAx
+MDBaoC8wLTAfBgNVHSMEGDAWgBR+KnXvDDbHS+cg2X9hSEeOEoMaLDAKBgNVHRQE
+AwIBATANBgkqhkiG9w0BAQsFAAOCAQEAkJf/F0AFe6ViIe9vaZwlRWs2cge1PnzK
+viLSchMF7AdYTAPq0hfWo34GTEw1e0k3KTIJlnj+fD3wpnhTA3qt3YzyS+wvdSZL
+e2Ogr4CvWKWbHi+5klCk5cJBkT5+SRZQErgATVyQYkcSYHYR8iPZ3izeSuoCPq8z
+KSBdZqgRqdJuPftmcbi4XI30zAJCDmMxuC++D3uTc8NHA6DrtAf4p2Iv0f33jtYo
+CsFBEVKpYd/0At1yxRDJ1MvKsiHZM3Pb2Ygoi5koy2gaNE34l128etEN6U0cZ7iy
+A5OYdxjNKkR1Xvk33eje3ixCOvsidtMSb7446sL6w2zQHQDJITAzcg==
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/GoodCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/GoodCACRL.pem
new file mode 100644
index 0000000000..f482eae237
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/GoodCACRL.pem
@@ -0,0 +1,13 @@
+-----BEGIN X509 CRL-----
+MIICADCB6QIBATANBgkqhkiG9w0BAQsFADBAMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEQMA4GA1UEAxMHR29vZCBDQRcNMTAw
+MTAxMDgzMDAwWhcNMzAxMjMxMDgzMDAwWjBEMCACAQ4XDTEwMDEwMTA4MzAwMFow
+DDAKBgNVHRUEAwoBATAgAgEPFw0xMDAxMDEwODMwMDFaMAwwCgYDVR0VBAMKAQGg
+LzAtMB8GA1UdIwQYMBaAFFgBhCQbvCtSlEo9pRByFFH1rzrJMAoGA1UdFAQDAgEB
+MA0GCSqGSIb3DQEBCwUAA4IBAQA9vPMLiinD8G7FaoTsu8T2jUrTi1OLPHxKnrlB
+rAP/eHa+VQV1HJfY5Gjq1dpNgzZqDIgQM5QHPm0aSgMN7Ultx+XzbxRswLnwgQrZ
+7f76Tlky1I+jz7/p3AEynrNR72v64SZt46UhpSuWBHoF1uEVtgirTZNfOEaGUJTN
+OaTA5U55/iw9BKjHN0e/Vd7OGnrk5h6FsgWOiasGn6/tym9teDt/L2hlOdsZsvX1
+KPc0ExUHVjJIUBYTooqyy/CuTzFHla6RYVYvJuRF5qYCxa0GTZK3ImCtJ3XfsGdf
+LEJDZ7T17xBQHucMvIVLm6vY44WUy7PqQhZJskhJMEvj01ZE
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/GoodsubCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/GoodsubCACRL.pem
new file mode 100644
index 0000000000..04790063da
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/GoodsubCACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIBvTCBpgIBATANBgkqhkiG9w0BAQsFADBDMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTETMBEGA1UEAxMKR29vZCBzdWJDQRcN
+MTAwMTAxMDgzMDAwWhcNMzAxMjMxMDgzMDAwWqAvMC0wHwYDVR0jBBgwFoAUMgcs
+nnRdLV0pu7F6jTsVUrR9QngwCgYDVR0UBAMCAQEwDQYJKoZIhvcNAQELBQADggEB
+AEbJGAG0MzgAYBNHSm7qI/hQO0cVg2FSkzuKdKB4zp0Jl2hvgjJKAEnU1QMkCyW1
+7B1srXRhjyLp8lvBZPWtXtev7QSkNV9ysUiNDdg+FThP9WVsf+zPu2+UuYn57A0U
+0NeWGv0DeoZG1mzRq5DcvLk98o3ErZug8ABzaUlprx8yGvzqw5abyj7AgeXvp5fs
+0Jo+ya4e8aFrBx/8k2gPvGTv5AOaa2+tLFQsVOm4PUQk1ANklm+CIlOz5hvvZgTp
+UXaiY/BrYCSV+N7MFXNH/75HUm4PPLFr5LgI/whOgR6bFC3e/5kTdQpVBwGBNvNy
+OEp8bA7ViIqbseokJI3q29w=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/GoodsubCAPanyPolicyMapping1to2CACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/GoodsubCAPanyPolicyMapping1to2CACRL.pem
new file mode 100644
index 0000000000..ed9ac1744d
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/GoodsubCAPanyPolicyMapping1to2CACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIB1TCBvgIBATANBgkqhkiG9w0BAQsFADBbMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTErMCkGA1UEAxMiR29vZCBzdWJDQSBQ
+YW55UG9saWN5IE1hcHBpbmcgMXRvMhcNMTAwMTAxMDgzMDAwWhcNMzAxMjMxMDgz
+MDAwWqAvMC0wHwYDVR0jBBgwFoAUW3N5meOuBtOKpjNOFHjkoB2x5MkwCgYDVR0U
+BAMCAQEwDQYJKoZIhvcNAQELBQADggEBAKKfTLLrhhr5sQTxYh0/59xGeCAPDiAb
+hZVIWXYrEy3HdaKVI786DjInVHDAHrpvO+pL52/unPqNZAFSpNpk8+4otR2/c9lu
+7EtForVhzkCVgO+bnGnLPqgYKq91tLD/NtB4OdfXlezP1LN4f4j8yHwmkJ9kv5bS
+op2gs30YZo4f5rgRFvDFzL0bYMzHcY/NVLqnHPp+8NqPsNAdyWl/90QjCG6Fj636
+8D7SQQpDY4Qrq0raGpsspyYkMeshUhLiEb8A2rnaen0dhrG8+5W2WP0zNoSQvKH8
+D2JLtZK+l75rRRZm5V9PNrza+ZfjbEABUmJjJ/LOkLiyNna789a2+9g=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/LongSerialNumberCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/LongSerialNumberCACRL.pem
new file mode 100644
index 0000000000..d0d3787427
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/LongSerialNumberCACRL.pem
@@ -0,0 +1,13 @@
+-----BEGIN X509 CRL-----
+MIIB/zCB6AIBATANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEeMBwGA1UEAxMVTG9uZyBTZXJpYWwg
+TnVtYmVyIENBFw0xMDAxMDEwODMwMDBaFw0zMDEyMzEwODMwMDBaMDUwMwIUfwEC
+AwQFBgcICQoLDA0ODxAREhMXDTEwMDEwMTA4MzAwMFowDDAKBgNVHRUEAwoBAaAv
+MC0wHwYDVR0jBBgwFoAUC2O3R67CBzIbf2/jOrjqC//XZKQwCgYDVR0UBAMCAQEw
+DQYJKoZIhvcNAQELBQADggEBAAT3AsJOgrvo4ohLomVgQey2sS6xh4bvK/qZmx4v
+czM1U9QpS7uO1QDgIkdxd7nGEtPqhzhjm5Uyw/M5FJwW0pOu5sRG9I/QvYgaTTbe
+u6CCwpL4fxJ42EjKicKdWvZ8+iQONDhcmHzIsvJJcg4vSkWc0S7CYwcrdbAn/KnW
+ru/Jnh4lGUxoj6GDYrroSCrPQqXk9rCTxq+wH5Ck8WQ3UyXLkVzuxWdBrNQ0Gwag
+dAh4u1o+3e+ta5V/WtQTZaSb71A0GvLqZpynu/pWDCQ8fkguvKbP55CuQLCnnukw
+nFs7zIlDlBQCYOgqvjswdzwZDSRliZD035oLDHPgSIeiWY0=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/Mapping1to2CACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/Mapping1to2CACRL.pem
new file mode 100644
index 0000000000..5aa18e1b89
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/Mapping1to2CACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIBwjCBqwIBATANBgkqhkiG9w0BAQsFADBIMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEYMBYGA1UEAxMPTWFwcGluZyAxdG8y
+IENBFw0xMDAxMDEwODMwMDBaFw0zMDEyMzEwODMwMDBaoC8wLTAfBgNVHSMEGDAW
+gBSZxXhpyz0zdsKZrETlsA7+ufTbxzAKBgNVHRQEAwIBATANBgkqhkiG9w0BAQsF
+AAOCAQEAHDBNYlKeVrvQoU9wdLhmD2iI+pNFkNB0O/BJXY59wp/DFV+gyygyE1sC
+qnwOPMiKR+u9zypXr3+YbG/L0YeZuzzaFbHrm4ZYPcCRzaz0YVm7ZhbI+ea+moVW
+R5BgVfFUkfCiGNYQ5nxKif7WeYcCK+Aiy1eQ7e4munvJ5jzDrRiiZiBPRfY29FPh
+0qkp9TRqPLpY4oYwaGxe9UMKqvVdohBYqUUfdB2tgjeVlDct+MokpYL7wpnrQBTy
+yzgYpZ+GELYzhhn1Oc6PkIrYxc1h3O+tIg85uII/qFOC9qOslH0qPiVqEzwckAhZ
+HX8PPcXxLhO62X4BdaMgJhgHdUrW0g==
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/MappingFromanyPolicyCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/MappingFromanyPolicyCACRL.pem
new file mode 100644
index 0000000000..4d6c9d0aaa
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/MappingFromanyPolicyCACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIBzDCBtQIBATANBgkqhkiG9w0BAQsFADBSMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEiMCAGA1UEAxMZTWFwcGluZyBGcm9t
+IGFueVBvbGljeSBDQRcNMTAwMTAxMDgzMDAwWhcNMzAxMjMxMDgzMDAwWqAvMC0w
+HwYDVR0jBBgwFoAUaHMU4As0z3JA2pSW1hWreqRvLowwCgYDVR0UBAMCAQEwDQYJ
+KoZIhvcNAQELBQADggEBAKZyiDbM1h05WJkyjY/Ri6vJfUyXeHPdkeLm2K+DuxVs
+VOreMcdvbKCDm+VjAud129+ou9CUFZUn2vPwhZ5+W6Z7ohzUoo8QeWGoWkvlY/yU
+/Hf4qR6QOxUATGr/f9u3ptsbVjBKEhD66Q/OvXgiBFNzOdZOqKuuI/x/rnXyOSo4
+pYJ/RecUb1m9WaVjO1eKQCGImMbrHTnIOziAg47/IdCztubxtWwtV8jmd/zi5E+0
+FO/lBnFyyghqNjPnML0LHQUM8Lr1EnFU9Lg2m/QFg2rWghrWadYKoa6lroPKhdMn
+hTL+BnTZkrwlJ7OtCI1wQD1NHIJmiL6lDBFT6TMH1P0=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/MappingToanyPolicyCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/MappingToanyPolicyCACRL.pem
new file mode 100644
index 0000000000..a4a369c179
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/MappingToanyPolicyCACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIByjCBswIBATANBgkqhkiG9w0BAQsFADBQMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEgMB4GA1UEAxMXTWFwcGluZyBUbyBh
+bnlQb2xpY3kgQ0EXDTEwMDEwMTA4MzAwMFoXDTMwMTIzMTA4MzAwMFqgLzAtMB8G
+A1UdIwQYMBaAFBQs7ZPxHhpwFZSLLZO0mNK3BQisMAoGA1UdFAQDAgEBMA0GCSqG
+SIb3DQEBCwUAA4IBAQAMdZWbfYwoDdwrkj0e1LjvvPiIXbBMwxuNhXG1LAaqazMF
+B3SM4/6IQ5kqLD4mXL6VTe1W3Ovu5L8DjbkXHckkq585B2s73Mwl9Ymlx7/MFahd
+taFST1d8AEQqxgBjcQ6imlEPtgJNboJqcbNS3/R/xau8OMXmY/kuSyTiMqeVq0jl
+ePVLDFrrLYaovKT0nG+7mN/afE+kyQxXaRk722ypf+38K4HHUAZwadRKPa8exyKz
+KNS8wFYjlrrua93qbpkDzmZ02gjkUJByCeBTqvkEbabUqLGfFhPJ8w/LFJiubwlI
+CmoYEAKpfR4393Uizsg6T+mHAaJmeIocAsoZBWvR
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/MissingbasicConstraintsCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/MissingbasicConstraintsCACRL.pem
new file mode 100644
index 0000000000..e84c210970
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/MissingbasicConstraintsCACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIBzjCBtwIBATANBgkqhkiG9w0BAQsFADBUMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEkMCIGA1UEAxMbTWlzc2luZyBiYXNp
+Y0NvbnN0cmFpbnRzIENBFw0xMDAxMDEwODMwMDBaFw0zMDEyMzEwODMwMDBaoC8w
+LTAfBgNVHSMEGDAWgBQwVrwVEY1PxibGtZyhcJLS+U8NeTAKBgNVHRQEAwIBATAN
+BgkqhkiG9w0BAQsFAAOCAQEADdJ1BXt1/rZ+Je0fEAOWJOx/zAciiWeBWaa5WMnq
+H4fvy//4+r57ZLVCN87f0fnxBSGk2bs2qZA7tkLMtVyRU12cjYsTXzqIA7w+lMGx
+Tsel+hDHGdJjAL5X7xDMOX3kfX/I5lruE+nKCxaGXWV3peJkaVdfw/qzkR7/3Woz
+aa5B7soNJv8sGyflwJCMUgAfaHZa6LopIRZ2GpmDhDYiXtujZC4NoLhKaq3LgQgF
+Y8gjpJtW7dEs7HQo33OeC4s2u8Q6T0pJkG6Xi7rGArplXTklb0MFjrEN6quGlYNC
+00HSsvG4Pf+2IPKUf4P6I0QW2CAnZqfprIPGok3Sy6tfJw==
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/NameOrderCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/NameOrderCACRL.pem
new file mode 100644
index 0000000000..9aab5ae9dd
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/NameOrderCACRL.pem
@@ -0,0 +1,14 @@
+-----BEGIN X509 CRL-----
+MIICDjCB9wIBATANBgkqhkiG9w0BAQsFADCBkzELMAkGA1UEBhMCVVMxHzAdBgNV
+BAoTFlRlc3QgQ2VydGlmaWNhdGVzIDIwMTExIzAhBgNVBAsTGk9yZ2FuaXphdGlv
+bmFsIFVuaXQgTmFtZSAxMSMwIQYDVQQLExpPcmdhbml6YXRpb25hbCBVbml0IE5h
+bWUgMjEZMBcGA1UEAxMQTmFtZSBPcmRlcmluZyBDQRcNMTAwMTAxMDgzMDAwWhcN
+MzAxMjMxMDgzMDAwWqAvMC0wHwYDVR0jBBgwFoAUv0qLgZtNjBQxjFvpzN0v6HkS
+UVAwCgYDVR0UBAMCAQEwDQYJKoZIhvcNAQELBQADggEBABkyacQyoBQE5YZL2K2Q
+0f637287irENIuJeWpv9MsXXsZZfN//UeOfwIdnhAMQCx1GeEbCdAPAKkBxI3Jn2
+AVtxqytV8F7xqUiUpYRw6g2B0jjsJoaFP4AoY+BGk5/4oLwj2MzNyNcYFb/KPPgc
+Pwq4T0IR7qMz60d2lZzLhWWz50jOmvwvyxFbOcYwh1RmLB7KKcwAaPeSwlCxThQS
+sugsqHuq3K+pi7KTShAV2UWRupDOGlVyPYAuo3n4G2LmwPmwmYmnG3+KS1Azyj8B
+ywYDiAPQyG10rufsDF3GEpJzIOPt7VHv8z2SthTPmVfDFrkwshOQsb+PrltGP9bC
+V/s=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/NegativeSerialNumberCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/NegativeSerialNumberCACRL.pem
new file mode 100644
index 0000000000..62dbbc8db4
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/NegativeSerialNumberCACRL.pem
@@ -0,0 +1,13 @@
+-----BEGIN X509 CRL-----
+MIIB8DCB2QIBATANBgkqhkiG9w0BAQsFADBSMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEiMCAGA1UEAxMZTmVnYXRpdmUgU2Vy
+aWFsIE51bWJlciBDQRcNMTAwMTAxMDgzMDAwWhcNMzAxMjMxMDgzMDAwWjAiMCAC
+Af8XDTEwMDEwMTA4MzAwMFowDDAKBgNVHRUEAwoBAaAvMC0wHwYDVR0jBBgwFoAU
+YuQuNcYPxeiR0AvBjd62r9qI2T8wCgYDVR0UBAMCAQEwDQYJKoZIhvcNAQELBQAD
+ggEBABjkQVYJziJmvShgFK3XecyOCYDlSPShr+vgrGFBQpyi/FePWU0yot93064/
+lRSHgzS8B9yvXPJBygOGT/hbmNG/88Evu2Z1LoEOLTzq+Sbf6LahfqDpkvdf6SjM
+wy4udr0g7dVOjHQg9DM+HDij7tc150o6P0e9++0CgpXuRNQKYKRq3qn4KjBq9Yph
+S/MpUEY70hgZaL+XTbyvti83Y8nPqrVIz3VRcG7GLHAmYb3cGwf/8zNL8lpOhzOU
+MoTwN+TcRWvMlYWYoyfaaGxA+Ak3wcPsIHhHLxTyXucXGQPjrmiGqK9H6BOUZT4q
+HupHV4TbUwMoW+FXISGPVvz3ECA=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/NoPoliciesCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/NoPoliciesCACRL.pem
new file mode 100644
index 0000000000..42ee928471
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/NoPoliciesCACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIBwTCBqgIBATANBgkqhkiG9w0BAQsFADBHMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEXMBUGA1UEAxMOTm8gUG9saWNpZXMg
+Q0EXDTEwMDEwMTA4MzAwMFoXDTMwMTIzMTA4MzAwMFqgLzAtMB8GA1UdIwQYMBaA
+FEIkA+2lS3acl5hcdOoFOhv8NeScMAoGA1UdFAQDAgEBMA0GCSqGSIb3DQEBCwUA
+A4IBAQBuELGhtxMeER+HyzT997rCWtWbXV9HT6zgfU9AUTX2pDoNFn9iGfFkLaJm
+rSkQro17ogzl9xrF2EvdgMSSw6YxRU7MbMMmF2lXC+IbScdtAGgdURnGF/C3Omao
+dGbXXI+3gUM5YSlPqGToDB2j7tBAC+THt8Knxq1NLeRt9cXpztX/UF+B4u62ylHo
+q6jV4KLlWksRGwtEF9w/2iiQv08zz92ySgL4Z/CbBMQfX73iq6SjPFOJm1CpA0BR
+QFN33H1OC8WMHaoV8M7DBGpudcWzHZpuq9ikjLdlCmDuGEyJgceneOe6e7vsupe2
+DGRGvgwmJbJI/XbCbLnwSW3Xdsp2
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/NoissuingDistributionPointCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/NoissuingDistributionPointCACRL.pem
new file mode 100644
index 0000000000..789099d061
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/NoissuingDistributionPointCACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIB0TCBugIBATANBgkqhkiG9w0BAQsFADBXMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEnMCUGA1UECxMeTm8gaXNzdWluZ0Rp
+c3RyaWJ1dGlvblBvaW50IENBFw0xMDAxMDEwODMwMDBaFw0zMDEyMzEwODMwMDBa
+oC8wLTAfBgNVHSMEGDAWgBSzy1S/ap38n8cxDpIMp0drmQCfMTAKBgNVHRQEAwIB
+ATANBgkqhkiG9w0BAQsFAAOCAQEAqnXhjxLsCflfSGLvYk06S6AP9+nEuFtbqXDP
++T4URhv2PZ583f7hg0jEd1/SZqyNxrZk6cNNMR2DYPCX8XsP3c0lKf1wVoielyl1
+bSPCTifm0z8pJr9ORD7XHDZ4F8s/nv08f+GJmYUaE036Nw++wNMqi016tw+TrHa1
+s9jSwUvlzjg/om6n0EMTWnWTnEHngdrGkV63kuo+4yymlsEALlgmIUp41bOiWzFo
+aYxsFd2k+ZAGvv5mB1Qimcp8C1ssInLczcDQ5Un329OAz83nrgdCZcYkFBAR1A46
+p+16zuw55BiQp3gByK1NVTssEcBOkDm/M1DgS+CtE8xkfKnR6g==
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/OldCRLnextUpdateCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/OldCRLnextUpdateCACRL.pem
new file mode 100644
index 0000000000..d9591e5cf1
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/OldCRLnextUpdateCACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIByDCBsQIBATANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEeMBwGA1UEAxMVT2xkIENSTCBuZXh0
+VXBkYXRlIENBFw0xMDAxMDEwODMwMDBaFw0xMDAxMDIwODMwMDBaoC8wLTAfBgNV
+HSMEGDAWgBTO2h/aWsyOl/ogFSlPrJaNKs14EzAKBgNVHRQEAwIBATANBgkqhkiG
+9w0BAQsFAAOCAQEANLTjtSL1Q3Ya29eHHAMhRdlEtcNF5CMpIUxvS3eQjTH0RxIR
+EWfDguJmX5gAeyHgegi0W4E1e5qArzpestM9nXMZypQ0GuOLEqQ8QfV4UTp5B3Ng
+gdgrxRTIdlpkEG43G93gZBmZ7u+HHbBtoBM270gFtluBXTnLbs6cHsZRn3p5jOCc
+Ll/YQBCO42iRjmww2mYKy5dB18geb0YZHlKL3FbZ1JhfZlvmHilBvBSx9uy5qZgZ
+HwlONqnePLkbK+0PyLaooa5NJXmusGVuZVC9x5gzwO+ZHun8fZ8aHIDDO9RtOv5U
+6r2J4kRp8Nw6zM4yWeQOD0GfRFlsq/OM3lHgPg==
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/P12Mapping1to3CACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/P12Mapping1to3CACRL.pem
new file mode 100644
index 0000000000..f590198a12
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/P12Mapping1to3CACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIBxjCBrwIBATANBgkqhkiG9w0BAQsFADBMMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEcMBoGA1UEAxMTUDEyIE1hcHBpbmcg
+MXRvMyBDQRcNMTAwMTAxMDgzMDAwWhcNMzAxMjMxMDgzMDAwWqAvMC0wHwYDVR0j
+BBgwFoAU/PSNYTMygHx9NYfeX1L7afEdwRIwCgYDVR0UBAMCAQEwDQYJKoZIhvcN
+AQELBQADggEBADWFF9y8VCunmgrr/9sJCD19KF6FofJOEV4U6zjPju5N/4b3Txoc
+LiClRWHYyRqHjOlLvAOik0mopqcUhwGqQG3MpU56R8LEKScgQQiacPuSeRSflRRo
+nxGs/90yJrq9s59EN5oZH+AnhJ07R2kq1PFLMGR6MY6B4A4MZony01/BPG4EfpRn
+W270ehb6FEYIc514vHyJ65s8KfUx01BsMSTwS75LJFEmnM6pRLzZzbESQEo/5/BN
+ttRRwrNt7TlwY7zs+oSDjUEpQ4K7tbfuQtIqiGm2VsY1O8kZWYmg+eUTUplcLoEZ
+LuAkicZCYw+YPcorZ+m9nL8FbxnrPm2LjJM=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/P12Mapping1to3subCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/P12Mapping1to3subCACRL.pem
new file mode 100644
index 0000000000..acc5cde185
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/P12Mapping1to3subCACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIByTCBsgIBATANBgkqhkiG9w0BAQsFADBPMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEfMB0GA1UEAxMWUDEyIE1hcHBpbmcg
+MXRvMyBzdWJDQRcNMTAwMTAxMDgzMDAwWhcNMzAxMjMxMDgzMDAwWqAvMC0wHwYD
+VR0jBBgwFoAUvnsTk6Hkm8UnPDBT16XJ5lqWejQwCgYDVR0UBAMCAQEwDQYJKoZI
+hvcNAQELBQADggEBALLR4At4E9ny2pPCIb4rLKvVRs10lszGrH8NVo1jhuugSyMS
+wMYPWuZbUw+rpN/YwFFkM5R256ITwtEttgHkWoa3CetBBRYF6UPIMyZp0KRDQAZC
+SwPU89QvP5H8KHuB3Zg4bg1wrXZTP9vjTjxf65UQhc2S3zj1M+TBv94h5J5cHSY+
+Q9Nz5iRoxEJv55/TWa7UQYBwaDbPxdb+twKEhdocjTZRYub2LhKRlY9nmTTiM/J5
+wE0RYNQsPiUkZuNELh7ZxPCp9Gk5KqbE0qUSO7KYiPEUj5mK4F+j2Mwb6nsoT9gt
+lrN/kqlyXJMhf1wRux12aUXN9Ia92lW6WrI7iH4=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/P12Mapping1to3subsubCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/P12Mapping1to3subsubCACRL.pem
new file mode 100644
index 0000000000..d000a06282
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/P12Mapping1to3subsubCACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIBzDCBtQIBATANBgkqhkiG9w0BAQsFADBSMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEiMCAGA1UEAxMZUDEyIE1hcHBpbmcg
+MXRvMyBzdWJzdWJDQRcNMTAwMTAxMDgzMDAwWhcNMzAxMjMxMDgzMDAwWqAvMC0w
+HwYDVR0jBBgwFoAUAF05Pg/lqipeLfauaCqtM5s9m3MwCgYDVR0UBAMCAQEwDQYJ
+KoZIhvcNAQELBQADggEBABqrLgpbtj/1buUK3kRN35sQe3KtFggWeg1MO7BkQtue
+zRD3ajIwDOBbemqCmEj8w4TdlDzTiEVpaa4k4UqlcTINMdlc8AbSzs4IjZvg2Mxu
+RcGm6+Kr8YfltOSeJk1zdQve5V9ft76TSGOZqK0Q4rTYRuuTipe3EnwJUdvyTR0u
+gmPO5XTMHnyecCJoUAHFmqEy5EAjgANJ9t0DwPzK+BIPDn76PmjKd0zhd5rAhQzm
+FhgGSyUAZMLAnPrpYTJtTW+QM92qJPxOMWvDAMVTJxUrZmfaDSEOFUnoTi6yADaR
+Mu8MWB8W9CRmxNShdUht4CGu5R03u6DGBoT70fkbn2g=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/P1Mapping1to234CACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/P1Mapping1to234CACRL.pem
new file mode 100644
index 0000000000..376de8c6e4
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/P1Mapping1to234CACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIBxzCBsAIBATANBgkqhkiG9w0BAQsFADBNMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEdMBsGA1UEAxMUUDEgTWFwcGluZyAx
+dG8yMzQgQ0EXDTEwMDEwMTA4MzAwMFoXDTMwMTIzMTA4MzAwMFqgLzAtMB8GA1Ud
+IwQYMBaAFJULAalJeKp22n8JDayIFPn190eSMAoGA1UdFAQDAgEBMA0GCSqGSIb3
+DQEBCwUAA4IBAQCqaCGQVcOhNGIvLyAEZ6u7CZSlusbOFE1T7cL10Z0XpNwHIU2e
+dzV4pmt0Y3tAJyR0FIXlqZbxq6fm/S5U4cWoM62jk4PzFGu9HaocJNNOIK0Opfuk
+TwwMOpYN/6IH74iy22lb2Wdgike7G9bCWl7nvMVZw5I7OIGyy/oqkhqjtWFLC9nD
+fYRIwfG3HPD8Kc2GMVc/iTalLDe3OUcsSGTrqCg0OwGckH+CdiXQk5MfV61bwICQ
+RxURNAqzCqYhNSrDH/qcGId/3mN/LzpoBr4SVRMZhMw0MpUWSTGwe1wnarhxaSoD
+VRHuoceqwHtU+s3AzFxHRXDbv/0xHECrkNjX
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/P1Mapping1to234subCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/P1Mapping1to234subCACRL.pem
new file mode 100644
index 0000000000..316b5ba594
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/P1Mapping1to234subCACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIByjCBswIBATANBgkqhkiG9w0BAQsFADBQMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEgMB4GA1UEAxMXUDEgTWFwcGluZyAx
+dG8yMzQgc3ViQ0EXDTEwMDEwMTA4MzAwMFoXDTMwMTIzMTA4MzAwMFqgLzAtMB8G
+A1UdIwQYMBaAFAMX5ZUA/So5eK/LRvZAmGUKAu27MAoGA1UdFAQDAgEBMA0GCSqG
+SIb3DQEBCwUAA4IBAQDCUm9OtS/rVGX56aFpRteKjEE8VKLOCXIecUda5ewyHw6a
+JRpUhGBguzOjY60yM8isBHYefdfjM4usLfZniy6NtfJ2S7yProWY7P4IYUsWgrMn
+2tXBp0LnyyWarx1f2NSeBJbsQKiw8fXLsygMnd8ISb6eA1AGKfZfxmeC4PM2Hl4/
+KbKdvxs7UWT4jD0WolZyOpjr/Ffh/uAKZB/TVks8j7ZENODT2lUc5KxmDbMCHEUp
+pd5GyRrXKJjdMMMUK/VUxOr1K22eW1FgBmx1zUiT9Uhen/bS5WXtfhVGqm1q468F
+jrPVC+/FBoFjuHU5tcxtNup3cLH6Vm4Y/v5+u2u+
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/P1anyPolicyMapping1to2CACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/P1anyPolicyMapping1to2CACRL.pem
new file mode 100644
index 0000000000..709a1368ac
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/P1anyPolicyMapping1to2CACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIBzjCBtwIBATANBgkqhkiG9w0BAQsFADBUMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEkMCIGA1UEAxMbUDFhbnlQb2xpY3kg
+TWFwcGluZyAxdG8yIENBFw0xMDAxMDEwODMwMDBaFw0zMDEyMzEwODMwMDBaoC8w
+LTAfBgNVHSMEGDAWgBQfAigoMo5KhPi4i0HxXXvoJVJrhjAKBgNVHRQEAwIBATAN
+BgkqhkiG9w0BAQsFAAOCAQEABhVsmwrFO9I1+fkjbJM7OfRp8BbNFRh0Mhb/ky6K
+g4U6YuwlEQP6KzM3QyOyQPIh7CAljCBxJpJkXrfm29RJY3OqS4THg+FfEmIcU+MV
+1TrRd8CI6ufBCdoG0SUUsS2W1CZFjbQNEl/xAfMUkuJi2T0lbCCsb1y0lpGrFW1l
+k2HLwq3qbikjZyWhoD/k6ho1yIEg4uT03GyJUm/n7Ij4DThK7mJP5vu9VD8rGwU3
+XXsoD62oMo2duCLrH4Uj5U+k/RbYb3zdbq+6xb4WLkt9MC3zWK3NtiVWIr9Q3XjV
+3uz8fErny04GW1dGGQQn+U50ZLjCA3bG78hbyCxv07zp6g==
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/PanyPolicyMapping1to2CACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/PanyPolicyMapping1to2CACRL.pem
new file mode 100644
index 0000000000..c039b47889
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/PanyPolicyMapping1to2CACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIBzTCBtgIBATANBgkqhkiG9w0BAQsFADBTMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEjMCEGA1UEAxMaUGFueVBvbGljeSBN
+YXBwaW5nIDF0bzIgQ0EXDTEwMDEwMTA4MzAwMFoXDTMwMTIzMTA4MzAwMFqgLzAt
+MB8GA1UdIwQYMBaAFEcDJy9DPcUv2ZKsx9J20DPG+Xe7MAoGA1UdFAQDAgEBMA0G
+CSqGSIb3DQEBCwUAA4IBAQBQVc02PPmQX4EhnPqxh12Tk5NseAryd0o8bmViilep
+uCpk+roAUOXLvKHg43alw0d+OsPA435KTBLKs5hR1lrEjEqE0AW56jqYbdCmei19
+XbaaGc7hrBbraDuFqAeobbZ2jwDxN2MQqIOT5omzfyyVLUzVKNyNsxvOVCoke+ya
+n29eMlOzv61ZesITDJlkrqTEyjwaRHUQtfthv4TB0sPIP5NmLq9ThYqhaVjsEtK5
+4bS6XP6xsVw4uyPrwStEgPPAbrYZyyRVNrNJigAyEKw59Kl5GQPefAm+mAo+x5D9
+aZDC+5MDbHU4CcjPPzbX5iocBgz3rEazHL1CJbIflrlh
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/PoliciesP1234CACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/PoliciesP1234CACRL.pem
new file mode 100644
index 0000000000..ef2c47424e
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/PoliciesP1234CACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIBxDCBrQIBATANBgkqhkiG9w0BAQsFADBKMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEaMBgGA1UEAxMRUG9saWNpZXMgUDEy
+MzQgQ0EXDTEwMDEwMTA4MzAwMFoXDTMwMTIzMTA4MzAwMFqgLzAtMB8GA1UdIwQY
+MBaAFPb9qYwmLLTP1tPr1B6tkmoduyRQMAoGA1UdFAQDAgEBMA0GCSqGSIb3DQEB
+CwUAA4IBAQDVVwwWNj9cO5+4t4+7LgMKvD8IGEKTakoiouH5na732HehNDO/mHzA
+cEKjj5MgEdx2JLghyLAtDsD4LTzdCANAftXnGeZJVcATUbjSecgPg5wFLLp9/mg4
+Fb5mIQeTUkYcrswC6Wa/P3vLJfu7L0vJwg6tPwnkFHw9NqsuzFqmQvrlrRtzLaDQ
+zlvg6BZkDCXSK+7gqQ84F4WJZCV9laVkymfM1ih0wXWby0VWlvAZK8SoWc6W7rep
+6dePGKx1V/lgJkYbxvMROoRKmoXSoHdxPktChC9KL0LievdasDF5/HZ5UgKNcE3n
+65jwDBq9nnOQfhhPm2WeLNlVbzY1Mzjg
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/PoliciesP1234subCAP123CRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/PoliciesP1234subCAP123CRL.pem
new file mode 100644
index 0000000000..5cb4f07fdf
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/PoliciesP1234subCAP123CRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIByzCBtAIBATANBgkqhkiG9w0BAQsFADBRMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEhMB8GA1UEAxMYUG9saWNpZXMgUDEy
+MzQgc3ViQ0FQMTIzFw0xMDAxMDEwODMwMDBaFw0zMDEyMzEwODMwMDBaoC8wLTAf
+BgNVHSMEGDAWgBS5qlCBpjRmUWidQu4piGrsHMh89zAKBgNVHRQEAwIBATANBgkq
+hkiG9w0BAQsFAAOCAQEAfV2uyHCfgiXrwk21BBpCMPiQmJ5erAU2QRfYd/IzoTgx
+sMGttr8QexaLY7rfY8K9Ju7t428gmoFYScIB8AITUCh+Z9l+wx3egQxCC9twjgRt
+zS/1ys8I2nK9CmA26ClY7TAL2ztIjFDcS+S1/tZlJxi26il6EpTjDkb4altMcwYD
+0dy7EW8Y/JAvFGBkziG9FiDThEdYK/hyDtceBEGDcWJP2s4CBOIigzdEkllFJe33
+ugx2AhLvGvHPzpyOzUPxlbKkkkoBjtU5WUl0v1PSiAa/OqXxXjOb1uuMUsHutbdM
+51I6VkQkncKEKN03OTkCgwthFM4GN5rurT02zD0KBw==
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/PoliciesP1234subsubCAP123P12CRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/PoliciesP1234subsubCAP123P12CRL.pem
new file mode 100644
index 0000000000..4f33bf88f8
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/PoliciesP1234subsubCAP123P12CRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIB0TCBugIBATANBgkqhkiG9w0BAQsFADBXMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEnMCUGA1UEAxMeUG9saWNpZXMgUDEy
+MzQgc3Vic3ViQ0FQMTIzUDEyFw0xMDAxMDEwODMwMDBaFw0zMDEyMzEwODMwMDBa
+oC8wLTAfBgNVHSMEGDAWgBRO9F6h+Qgwe2WsksARCyzTtJYHHjAKBgNVHRQEAwIB
+ATANBgkqhkiG9w0BAQsFAAOCAQEAO3mUa/hz4u9id4j6HNSVrunOClJfxtxgVK//
+BhUHRP7DLt9PfridxGqVOENl4KKUp3Is8JDXhsQLfalp+SXUEjUNIW9tK1XGT8RM
+KuyPXfYNvK0ki8RLZBrOObwuz1bwCIr8suJnlbuO2PizZiYkd9JkPpmI4Ql7wvKQ
+qtZG/D9gMSKw8YYU0BBD38C1DBcx9zX8Juq1of2wA1bRO4+1r7d1caiauSNDICLM
+6K+5veVzXPbdi9AiCym0q2DafSijAO/tyVI7viGFriA1XXPyW4IgJTlYwj6C7bNE
+spPxDeHZOYGjSRYwnOVwl5FetLBZeXPnb+oCUueETTdCap53mg==
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/PoliciesP123CACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/PoliciesP123CACRL.pem
new file mode 100644
index 0000000000..9badaf1180
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/PoliciesP123CACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIBwzCBrAIBATANBgkqhkiG9w0BAQsFADBJMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEZMBcGA1UEAxMQUG9saWNpZXMgUDEy
+MyBDQRcNMTAwMTAxMDgzMDAwWhcNMzAxMjMxMDgzMDAwWqAvMC0wHwYDVR0jBBgw
+FoAUjCgK2g0JFGLuPT2WuHGTEonq6GMwCgYDVR0UBAMCAQEwDQYJKoZIhvcNAQEL
+BQADggEBAF+cFlwOZpoC7aoNCsV8ISlb/o86hb45QuGdajlLv/vTDAstyF6123qR
+3dE5/YuJ4FTgjZqNt6iBOMIaauFE9nTeQ/G7WhB6yGOtna9QrTIIWQfi3kyuONC5
+1+YUr/dj9wBJgHXQhO1ugdweVdstePbTe0iRl1ZGG+Hs/xT5r7wji24hhtjyXoAJ
+J9A2NQoLt7wD4OBOKQUHarYY24xpnVcWryWxfS2HGdsiWfyrdVMSTzoZnIqI3kzW
+VvbVj99ShHKFmIQMqBuZZGQCrXlts5G6mknQK9TNCHCJZwX41S5Tw/dd5X+Ujdyz
+knTDjuehdwjtDecV5Dh0q0nORth5X+s=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/PoliciesP123subCAP12CRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/PoliciesP123subCAP12CRL.pem
new file mode 100644
index 0000000000..bfa6e83f6c
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/PoliciesP123subCAP12CRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIByTCBsgIBATANBgkqhkiG9w0BAQsFADBPMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEfMB0GA1UEAxMWUG9saWNpZXMgUDEy
+MyBzdWJDQVAxMhcNMTAwMTAxMDgzMDAwWhcNMzAxMjMxMDgzMDAwWqAvMC0wHwYD
+VR0jBBgwFoAUzgDa/aqTQPjAoHmtwXjOHdcn9p4wCgYDVR0UBAMCAQEwDQYJKoZI
+hvcNAQELBQADggEBAEBeDNTliwQBVeWRQ3NDLEUH3xNSHfAr8itMFr1gMI8YaS7e
+qYMtIZnedQ7o1A10abrH6X4li/8UDL+7o98CTcd02D2Hrvc+QXT++nIJ5oFbt0qJ
+iOVXGQ5ZXVxSaNif729zjcAmgGKcHtVtxPvf9CgmnQ78hA6mx0ugFHs4QSmecsMt
+umd0V1AsscRGzTjvN0YCVBPpZPy/oxLU6pYp6XAmqC1UHPv8Ny1/unMz7cYoam/0
+83KZJHp8I4FSzkdhrFyLjKaaVxwWioLyQUTwrzR/qzaU1Eyk+KipzKVh470kuItM
+cDnV0OXOPuJR1XA3yhdLZU0aMtwd28WZ1Y4myPQ=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/PoliciesP123subsubCAP12P1CRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/PoliciesP123subsubCAP12P1CRL.pem
new file mode 100644
index 0000000000..f8a920a1be
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/PoliciesP123subsubCAP12P1CRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIBzjCBtwIBATANBgkqhkiG9w0BAQsFADBUMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEkMCIGA1UEAxMbUG9saWNpZXMgUDEy
+MyBzdWJzdWJDQVAxMlAxFw0xMDAxMDEwODMwMDBaFw0zMDEyMzEwODMwMDBaoC8w
+LTAfBgNVHSMEGDAWgBTkGz5Gt+bIqdjt0TN/BeHxXRIkwjAKBgNVHRQEAwIBATAN
+BgkqhkiG9w0BAQsFAAOCAQEAZg1qheMvRGkd/jBrdaoWnRb0iEuPZw9Ij5k64ZeI
+JUmMeWjKKFnLBsC0MIrEtR3NPqMU6tfZH3mmeCzJ6pjo0IozkN1OnMYFqwxrfn1T
++d/yM4fYBb12oesd7n1+c36hlHKd7dxi1xUAwCIABnX/17odaOCD9ikA2sq5XfqS
+p4Z7IUPd+i/8dF3Pkfm+cpPJaNdpdd2sxx4mmbsGcL9+2xIwhfiVavawZe0MZYxw
+kPhNVL8zL6kYUCRvB7J2hEwu1MWH3paED9MIkKyS0zVdzfg4DojApNP/nwL3JHIf
+DVscEQo6mY+6hhhOYApDNF6y81BRqasImFItcLp/fgE4tQ==
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/PoliciesP123subsubCAP2P2CRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/PoliciesP123subsubCAP2P2CRL.pem
new file mode 100644
index 0000000000..cbbe9d03b1
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/PoliciesP123subsubCAP2P2CRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIBzjCBtwIBATANBgkqhkiG9w0BAQsFADBUMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEkMCIGA1UEAxMbUG9saWNpZXMgUDEy
+MyBzdWJzdWJDQVAxMlAyFw0xMDAxMDEwODMwMDBaFw0zMDEyMzEwODMwMDBaoC8w
+LTAfBgNVHSMEGDAWgBTp/LZeVhROBh3RMv2IYGsQ+AUbaTAKBgNVHRQEAwIBATAN
+BgkqhkiG9w0BAQsFAAOCAQEAYcCBk7Si8nv8UU/B0/YbctjIyBk8VOtKhIFbRunt
+alE+/Vc/c77r0Y1CfVII/DOAFcB872KtQuWyl5p8uVlVwmDlL1BKmz35mD2PS10i
+NsuofkZTp97xrRZbA1k+zqMshOzmEXReNrR/qliJpTPlH1YMNV5IN02HNYZ8+6mn
+BzAs4e8/xS2cO9ktya0ZwAqwH3ksD+etLL/MIj/3BAL9HRMOC+RcKktWkVZKqYq/
+AAzn3HlvnTUZi2OW81ABtFl2hYSJEyBcKjrSoDuZ4Utk5AuxOsLwa0hH4da+/4nx
+OreMrYjymG9FpLVq+RhVYMdJSzp8c2CU7rgN0Pap7sQY/A==
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/PoliciesP123subsubsubCAP12P2P1CRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/PoliciesP123subsubsubCAP12P2P1CRL.pem
new file mode 100644
index 0000000000..2a6a21b33b
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/PoliciesP123subsubsubCAP12P2P1CRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIB0zCBvAIBATANBgkqhkiG9w0BAQsFADBZMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEpMCcGA1UEAxMgUG9saWNpZXMgUDEy
+MyBzdWJzdWJzdWJDQVAxMlAyUDEXDTEwMDEwMTA4MzAwMFoXDTMwMTIzMTA4MzAw
+MFqgLzAtMB8GA1UdIwQYMBaAFIkgF4T7rLsJ195eXp5o9jlQH0CIMAoGA1UdFAQD
+AgEBMA0GCSqGSIb3DQEBCwUAA4IBAQAVS+qKdu5cTu1Y/7f1Yci/ULGAtX4oYox6
+0LYSSXpOl1BzTNMxUCp30N382djQEYODR8vROlVXDyEaHwMC8Dyn+YreG+1eHODk
+Mq/Hc59ckRW17LDJpyBew1535EVQa6LtC6bD72AQ1Fd+hzH90WNNQCMb4xQ6eOnF
+T1pSoKoopsA2NH3zTBVrtq1n6K7FOojGqKdO7oYqill+bRdMoYSNKQx1lskS9E1b
+lQftEmE8AAlU+Q1zV5IiiSpoEXe8/nbyTwSbYvaUHmmBgwCOdgnpJiG6TvwjcFh/
+zIQiGotG9zQszNrO7GPpvc9wQ+o2uN1Luilv1P0FRw0PBHMwmwUJ
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/PoliciesP12CACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/PoliciesP12CACRL.pem
new file mode 100644
index 0000000000..1af85735f5
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/PoliciesP12CACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIBwjCBqwIBATANBgkqhkiG9w0BAQsFADBIMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEYMBYGA1UEAxMPUG9saWNpZXMgUDEy
+IENBFw0xMDAxMDEwODMwMDBaFw0zMDEyMzEwODMwMDBaoC8wLTAfBgNVHSMEGDAW
+gBTYXzXimsE3KibOg8xzDnAVKjriMTAKBgNVHRQEAwIBATANBgkqhkiG9w0BAQsF
+AAOCAQEAUiAHin1qZsRcAQcRXu9l1qnbsNYeC32w7Y8SR7o7SLMBjrLfpzTt3u31
+UGBH/9CfynTV2n3wVuJoCCFTdkgRn08X+Oc2sK/8g+oKvTwmoLQv0lktEsFvOFC0
+4/5wB94qkPysqGKKPj0ADujY5GBWb64Z9V6ehvxACTzKhPCNc7auvTLZEymtWW2H
+4C6nZGjUuuoZbd1Y20d0u5/18MySt5HCfKyoIfA75dsnuL7RJVFvrjcXxH40cXs8
++xizlf66wdIVeWCAr8RV4bPFmlC7fAF3UjC4MoE1RQqbWuYig79F7RS29s6XNWiq
+A0W7puTYGKoCpIUETmzBSDOsNpcfIg==
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/PoliciesP12subCAP1CRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/PoliciesP12subCAP1CRL.pem
new file mode 100644
index 0000000000..c96f12103b
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/PoliciesP12subCAP1CRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIBxzCBsAIBATANBgkqhkiG9w0BAQsFADBNMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEdMBsGA1UEAxMUUG9saWNpZXMgUDEy
+IHN1YkNBUDEXDTEwMDEwMTA4MzAwMFoXDTMwMTIzMTA4MzAwMFqgLzAtMB8GA1Ud
+IwQYMBaAFCKe1w64SM4JDjpdvtZNWCNWjcvWMAoGA1UdFAQDAgEBMA0GCSqGSIb3
+DQEBCwUAA4IBAQCPniPRTYwvNO1eFZjaR7nnHYFDRh7cAd2EsYgmh9W7kjeafaZt
+vLCnzQv82LN47Ki1z/2KWraLSRduP8qnY4wGOdRsahnrl/ss2wjMB6UiFp+Un6IA
+AKEM5MLgZwvjsadAADqJXonGzB1Eh2xmHGqZloU5V5FDewbjem/scf3yufvahMvr
+ZSjBsSUMCBfIewJq7JRkwSgyip4K/t9UBJBbsfkOOR5/rEgaZyyfI86nGhg0GawS
+FHw1fAPcG6vEKnsyyHU9JhzjtQYUi3JiZO2BamFAXfJkDOhCWfkR4ywhRUP5bqAE
+q4GAriFrKPKYB3Fp4nAhbLi4bhV4kUS+lWoT
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/PoliciesP12subsubCAP1P2CRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/PoliciesP12subsubCAP1P2CRL.pem
new file mode 100644
index 0000000000..4dd0f2fca0
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/PoliciesP12subsubCAP1P2CRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIBzDCBtQIBATANBgkqhkiG9w0BAQsFADBSMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEiMCAGA1UEAxMZUG9saWNpZXMgUDEy
+IHN1YnN1YkNBUDFQMhcNMTAwMTAxMDgzMDAwWhcNMzAxMjMxMDgzMDAwWqAvMC0w
+HwYDVR0jBBgwFoAUx6U3p9D6JOV839vyXWnb7sr2me4wCgYDVR0UBAMCAQEwDQYJ
+KoZIhvcNAQELBQADggEBAKWuXLxMM7H4MkbAsQLa1RgeuDYC5FLy5mzt/UsidYqx
+rSK7vX19l658Lz1NEcq25gXzJlfOG8ZIAUkO/SXwi1IwqOU2QVkn6nJnXYP0J0c8
+QEzxYlYEC/DHDTEUltH2ZoIuOoozIT6NHw8+2/XBBjatS1nL10bhwvzRXirGFKGN
+BtASLRZeHaU0y/n2KkBuusn3HFyErzRskNRLPOTk3Lt0/RB8nbrRxUUZJXAaZJIr
+4Kj4LcatDgdgXCZ2MEC/mCByKwFiwh5RgMQqPPLMhGBRSPfPD5hVKVL2iGu8qSxK
+kkLZkwrGL9U/1mXtcajUpz+r28iLP4uJnCxt9ZBVIcg=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/PoliciesP2subCA2CRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/PoliciesP2subCA2CRL.pem
new file mode 100644
index 0000000000..d2e6b8d3db
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/PoliciesP2subCA2CRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIBxTCBrgIBATANBgkqhkiG9w0BAQsFADBLMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEbMBkGA1UEAxMSUG9saWNpZXMgUDIg
+c3ViQ0EyFw0xMDAxMDEwODMwMDBaFw0zMDEyMzEwODMwMDBaoC8wLTAfBgNVHSME
+GDAWgBQXLOoDuAd3gT1lpb8zH8x60pj8vjAKBgNVHRQEAwIBATANBgkqhkiG9w0B
+AQsFAAOCAQEASUNpb/KK0iBuFV/Y7HRxjqNfJAhSRjOxNrEMtuql/YucrqQKFs2f
+AcbNwEWPPdW7KEd2bhyUBlJJ1g0mWCPu+bHNv9IND3bsioXiStlDblymVm/ShJ0i
+f3VWT47L0Lb6Ppye2r+A486Q468clSbDv2pj+vAPqFqUaSr5iN+Y/WTxUtAs34Dx
+qDRWP9lye1OzlLnMJSf/ZVYHdS/KmnsR9YIIeI7pNqtPf9ilDBe6Va51tpVfHnp3
+OLQersoyyDApH5KCQQv0/+4EPqZjmAcO+ALDg4Q92vfzbULImJW6rMD6dlEtITlu
+iful4K0ey6Jngy/6qFTtI/jJgzNmxsR6Zw==
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/PoliciesP2subCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/PoliciesP2subCACRL.pem
new file mode 100644
index 0000000000..02c8fa43d4
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/PoliciesP2subCACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIBxDCBrQIBATANBgkqhkiG9w0BAQsFADBKMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEaMBgGA1UEAxMRUG9saWNpZXMgUDIg
+c3ViQ0EXDTEwMDEwMTA4MzAwMFoXDTMwMTIzMTA4MzAwMFqgLzAtMB8GA1UdIwQY
+MBaAFF48hHOeMHBycZiugTYZ2yIOfK8DMAoGA1UdFAQDAgEBMA0GCSqGSIb3DQEB
+CwUAA4IBAQAIgjc4iT1LVOUqEO9SLZgx9MpR4Vt3gTa3JciJH+czDui7YgsT688m
+8/dqZXCxTkt9JYPQGABYYhwxsQC2oyjG2jercknnJ+XHk4JV2wN/XO9ItNhI+OvA
+rnKtzk6Rpf7pS3SSjeU3XR5UIOiMM2JoWGvtUhwq7b5RaLk36Z0gGp9x/3LaGDBA
+FTred1LnWgYQCO7jTGWfDT4xIJr0FqA1RNLb+xvkmA1lmhjZ20lw8HXHgFsfvb4L
+ADFkLzFQ6p/zuleXBDe8U5lusCqjls0cTaiNCdpPXuJ1I4hkPYu0raquxjC7VoM8
+LWcIpRna9gUu8sSD7k7N+NDdQSM+Op+P
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/PoliciesP3CACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/PoliciesP3CACRL.pem
new file mode 100644
index 0000000000..0eac63d37a
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/PoliciesP3CACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIBwTCBqgIBATANBgkqhkiG9w0BAQsFADBHMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEXMBUGA1UEAxMOUG9saWNpZXMgUDMg
+Q0EXDTEwMDEwMTA4MzAwMFoXDTMwMTIzMTA4MzAwMFqgLzAtMB8GA1UdIwQYMBaA
+FNgFqyygi8OS3Mataj+/88aY5dz9MAoGA1UdFAQDAgEBMA0GCSqGSIb3DQEBCwUA
+A4IBAQCwxaqJWjJraTqw0xo+dvcx3uD3LYUe9aJqKvZTbNqLT23k7U62qcWUz59N
+Rprnl0q9aemI80Mk/Vk4WAr9weTsOytXrNML4Rgk4Sb+WjU4OSlUZbBka1YS41Y4
+xiZzhQYrL391uyDD/FLsehWJmkO9JqeydLT1A1lAuX9m3f59PJ6waXJMMwySszDI
+T2e56JR6Vt3659YRpNScJmDKWc3wdHagNvmtn4Z/c+TtO+JN11doMwx/92uV+nic
+GVVPJQqKZDvNfK/hLxBo3nKBpXJUrU4AyYCbpe4idc6CxYMAy95UqUlFV72pnndm
+E5RRfI4x2a+BTvs8HNSX3LpruISg
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/RFC3280MandatoryAttributeTypesCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/RFC3280MandatoryAttributeTypesCACRL.pem
new file mode 100644
index 0000000000..49582a3282
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/RFC3280MandatoryAttributeTypesCACRL.pem
@@ -0,0 +1,14 @@
+-----BEGIN X509 CRL-----
+MIICDjCB9wIBATANBgkqhkiG9w0BAQsFADCBkzELMAkGA1UEBhMCVVMxHzAdBgNV
+BAoTFlRlc3QgQ2VydGlmaWNhdGVzIDIwMTExEzARBgoJkiaJk/IsZAEZFgNnb3Yx
+IDAeBgoJkiaJk/IsZAEZFhB0ZXN0Y2VydGlmaWNhdGVzMREwDwYDVQQIEwhNYXJ5
+bGFuZDEMMAoGA1UEBRMDMzQ1MQswCQYDVQQuEwJDQRcNMTAwMTAxMDgzMDAwWhcN
+MzAxMjMxMDgzMDAwWqAvMC0wHwYDVR0jBBgwFoAU8FEYYu/OQce3sGd0awK8Mgoz
+meswCgYDVR0UBAMCAQEwDQYJKoZIhvcNAQELBQADggEBAFqTS/IzHJiqF28DiO+F
+v++AFZ/b8cYN6B5eoSoZYBg2C7pV+5UEL0+hQlper2e6TUIEkMpSU7Y6VWc2okpS
+YR+2bNCsIDX/RB2DYzyEGgqwoBmbeTCE/bIbcxjoSUOP7YlI4wD4XgOISwVOBZoK
+Mc+341k1iHhwI0OFHv3XVHqwegy5RVcgYhWcq/H9ARUvD3+a5HDdBGTCP4r8aCcD
+46zDEsBRYVJNPsyk4cnBsR/8nh8Bufy8PWuArb+s1ZSQ0PMgtwfrvqPCM+wnl/+Z
+gN3UiKvlBG2lSrTxhOelPGvoNZrr6Z8Uw8rl1EXcLvPTJsqR17snRwWCWLR6ewAc
+XbI=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/RFC3280OptionalAttributeTypesCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/RFC3280OptionalAttributeTypesCACRL.pem
new file mode 100644
index 0000000000..106a0215db
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/RFC3280OptionalAttributeTypesCACRL.pem
@@ -0,0 +1,14 @@
+-----BEGIN X509 CRL-----
+MIICGzCCAQMCAQEwDQYJKoZIhvcNAQELBQAwgZ8xCzAJBgNVBAYTAlVTMR8wHQYD
+VQQKExZUZXN0IENlcnRpZmljYXRlcyAyMDExMRUwEwYDVQQHEwxHYWl0aGVyc2J1
+cmcxDTALBgNVBCoTBEpvaG4xCjAIBgNVBCsTAVExEzARBgNVBEETCkZpY3RpdGlv
+dXMxCzAJBgNVBAQTAkNBMQwwCgYDVQQsEwNJSUkxDTALBgNVBAwTBE0uRC4XDTEw
+MDEwMTA4MzAwMFoXDTMwMTIzMTA4MzAwMFqgLzAtMB8GA1UdIwQYMBaAFJtubz+K
+p/TntYwxW86ZS5EcfHy9MAoGA1UdFAQDAgEBMA0GCSqGSIb3DQEBCwUAA4IBAQBB
+32Yrko9n8ahvFVhspRmxscM7BMFLDBPKByEzJomnbztKvfw1bn4lsO39UIuBQgbS
+u+w1ZEnc3FcTvU0KjPNUA8e3eiuRzhct1XtqCPo7OmtFZDP7HQaCC0M7bxLiD56K
+00ww0RoiC8Vx0TI8+A+dZIQH1g19xShQ2WGquQLC26Wcf4FeXCTS/aaPeCGurbKD
+RBpShJHmZ2zFUZy0Wyu/FG+LMQpJf/ypy5dzYO94rHYMOdjfk4q17d9rLbtzyIZl
+GIOuZHdlBJi1uVKKOWFJHc1KIEbf2dc3AOfRAcZnvWZ9cBJpm51VC3FNPjqBtxVm
+OSHxymJkNoLCs4Rr1cCL
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/RevokedsubCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/RevokedsubCACRL.pem
new file mode 100644
index 0000000000..f07d7b88bf
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/RevokedsubCACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIBwDCBqQIBATANBgkqhkiG9w0BAQsFADBGMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEWMBQGA1UEAxMNUmV2b2tlZCBzdWJD
+QRcNMTAwMTAxMDgzMDAwWhcNMzAxMjMxMDgzMDAwWqAvMC0wHwYDVR0jBBgwFoAU
+lm+SmaDpdnS7X9T4+xnZzx0FoO8wCgYDVR0UBAMCAQEwDQYJKoZIhvcNAQELBQAD
+ggEBAB+FPq0gPzR/ldksUu2y1NFGBtliXqr8+2YkuC3znY6aUv+jZG4MgXN/m30I
+hs7lxydqbF2ubdw6Er9PhmvJcnH92BREyAAUHt3zzHbCK8WKmwdofIK/tFrXr0XO
+eGPfmfmm8oHJREcNQ2ftaCvq7eTy61KqVno+p4KNKLPxWvR2uahuhFxgIXZlJVQF
+lSIPCEPGT+t+tSxFQ7RevCv9jl9KFIf8hEWpLxjxY44uucZlQaLFV0oRiiuB7Cq3
+xYM0xa8uZ5/xuBu9MJ1TKg+LWqp6dwd3BRo4BxbBHOwNeUJ+PtaR23AAoiL7s5mt
+bsFxXB0TfszCLEmLmFFfkuB6m6k=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/RolloverfromPrintableStringtoUTF8StringCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/RolloverfromPrintableStringtoUTF8StringCACRL.pem
new file mode 100644
index 0000000000..912b6c8b8f
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/RolloverfromPrintableStringtoUTF8StringCACRL.pem
@@ -0,0 +1,13 @@
+-----BEGIN X509 CRL-----
+MIIB4TCBygIBATANBgkqhkiG9w0BAQsFADBnMQswCQYDVQQGEwJVUzEfMB0GA1UE
+CgwWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTE3MDUGA1UEAwwuUm9sbG92ZXIgZnJv
+bSBQcmludGFibGVTdHJpbmcgdG8gVVRGOFN0cmluZyBDQRcNMTAwMTAxMDgzMDAw
+WhcNMzAxMjMxMDgzMDAwWqAvMC0wHwYDVR0jBBgwFoAUtW1PKD/Hu7GYpKml0Khb
+Xkp0s+cwCgYDVR0UBAMCAQEwDQYJKoZIhvcNAQELBQADggEBAFPDLO8fJtAH7E7z
+iHWXjzFLzoHGbLXSioC2TDR+Fm3hAOwdSphltknYwSxA0SyKeru8c8RVOu0fd7dS
+CaXmFaYL9CE4HREg1zFtuSPsElkhuZgi5N2cijO4oXUgdJsssFe3O/jWTBixJONF
+ZoAu4pjxi1mcNYDK5jn45jB25RNnjvhTm84I0o5/RQOp9vzpT5V60F3npWI8m7PQ
+UswjVucpjvslv2rPZEN5DldNgg+/W/qElgRFa+loYjeJyYRftsknzt8F1g2fzC7i
+k7fJTv0ZgFOk3tdUZhFHC3DNcwb6uscyTEdodNzaPhqoNHgUBdCqbUq0zZQXsIz4
+Sfp0ZJk=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/SeparateCertificateandCRLKeysCA2CRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/SeparateCertificateandCRLKeysCA2CRL.pem
new file mode 100644
index 0000000000..e3b1f68960
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/SeparateCertificateandCRLKeysCA2CRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIB2DCBwQIBATANBgkqhkiG9w0BAQsFADBeMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEuMCwGA1UEAxMlU2VwYXJhdGUgQ2Vy
+dGlmaWNhdGUgYW5kIENSTCBLZXlzIENBMhcNMTAwMTAxMDgzMDAwWhcNMzAxMjMx
+MDgzMDAwWqAvMC0wHwYDVR0jBBgwFoAUE4Q9hI8eqnyBd18x3c1g85eX2bEwCgYD
+VR0UBAMCAQEwDQYJKoZIhvcNAQELBQADggEBAKN55WtVRJBOZvbUAlZXukKilUzr
+4Z8lHcq4fiql8hc6UUhmYAc/7MdfDa3NqgbUoVg7aUngm52yLgXubmnMztA5Vqum
+CXiNSmJ5AVWh7GCs8pdMHFLpFqnh1y4RTpt4AjYFC+xc18gSrsyDKWv7YEYVyQG7
+GHA9JEMm3SpSgh3B872L+neVTTMrO/bHKl54QGGp1tasaLdTkRVB/002yGBraVvE
+zfC7szqOc5v0tl4e3VrUBcNfn4QTDvl5PfrovZdPJP3Ys7139K38+kT/2YwCJ6M4
+EOAhyfzVSTgvuFoB+UlYIt10c5GA7J7pDJr4PCGTzR1K7XesSFuZCbD5Ddo=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/SeparateCertificateandCRLKeysCRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/SeparateCertificateandCRLKeysCRL.pem
new file mode 100644
index 0000000000..9e3ca350cc
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/SeparateCertificateandCRLKeysCRL.pem
@@ -0,0 +1,13 @@
+-----BEGIN X509 CRL-----
+MIIB/DCB5QIBATANBgkqhkiG9w0BAQsFADBeMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEuMCwGA1UEAxMlU2VwYXJhdGUgQ2Vy
+dGlmaWNhdGUgYW5kIENSTCBLZXlzIENBMRcNMTAwMTAxMDgzMDAwWhcNMzAxMjMx
+MDgzMDAwWjAiMCACAQIXDTEwMDEwMTA4MzAwMFowDDAKBgNVHRUEAwoBAaAvMC0w
+HwYDVR0jBBgwFoAUcopDCwRbPT3EVeGDxp/fkMrq914wCgYDVR0UBAMCAQEwDQYJ
+KoZIhvcNAQELBQADggEBADQCnzJmc0jLQFb6EQ2iJDMcxrSuJftSikSCoKvuRTTH
++4cqd+xjh+kkZ1FCCmG6mY/hlnLTlKYqJlrJuu/vhHZP6XYwqkrtyx23ORmAOyq7
+SAuTvPBOPRo9HyvVWWHITgAKA90A0XUK+/zg1MRKgqqslTOaNXCqhu+nvshCtLx6
+MLsCYH5LVs3GimWfsT7wSpF4cqI4QyfZLJDtGz++9M7s03LvH6QNfQ0qrSaOdCfk
+hsdtNEzNFOOy34JejUZIDie83zj7Vgr1v6Gx+c5xkoDemggV/wzLYi7Si45JQBrJ
+trqjdLNOoS7QFadH/Vw7zL06Fahe2uUOOsXac76NQl4=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/TrustAnchorRootCRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/TrustAnchorRootCRL.pem
new file mode 100644
index 0000000000..114e6e1cbf
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/TrustAnchorRootCRL.pem
@@ -0,0 +1,13 @@
+-----BEGIN X509 CRL-----
+MIIB4zCBzAIBATANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEVMBMGA1UEAxMMVHJ1c3QgQW5jaG9y
+Fw0xMDAxMDEwODMwMDBaFw0zMDEyMzEwODMwMDBaMCIwIAIBaBcNMTAwMTAxMDgz
+MDAwWjAMMAoGA1UdFQQDCgEBoC8wLTAfBgNVHSMEGDAWgBTkfV/RXJWGCCwFrr51
+tmWn2V2oZjAKBgNVHRQEAwIBATANBgkqhkiG9w0BAQsFAAOCAQEAqxkdtbsha7bh
+TM3wtzeelTjR1IGQgK4R8Psc2fw2NOydlq8PeSc3qitHV6m4dqHzchQlytYprOK4
+dRitEh+RYY4UKUNu+OQQ5VFLSvuC0Wv3xn2w29VqpQtavBFfJ8Lst9520pece6x8
+6fB9L6VP4YNGIrLc+7hEjEDALJs+ttPoxNNXGMApQQi5xyZEksXQAo60ZdH/r95l
+dVCa7U2OVXO1MCuZlWQRlql0Bi3CzE26cW1jccEdU6yQ0ONKNuROR+6NsXZ2Qm2C
+lHEGWFJAZ/CWB7NjQ9maNkoioZb4IB2AKPKBcb0mT3TYspgT8zcZSP5DLC8iVOrc
+x2SLSvd35g==
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/TwoCRLsCABadCRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/TwoCRLsCABadCRL.pem
new file mode 100644
index 0000000000..526eb1f6e1
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/TwoCRLsCABadCRL.pem
@@ -0,0 +1,13 @@
+-----BEGIN X509 CRL-----
+MIIB7jCB1wIBATANBgkqhkiG9w0BAQsFADBQMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEgMB4GA1UEAxMXQmFkIENSTCBmb3Ig
+VHdvIENSTHMgQ0EXDTEwMDEwMTA4MzAwMFoXDTMwMTIzMTA4MzAwMFowIjAgAgEB
+Fw0xMDAxMDEwODMwMDBaMAwwCgYDVR0VBAMKAQGgLzAtMB8GA1UdIwQYMBaAFBCh
+AdaZnYDjbf3n7ndLX/FJ2TxTMAoGA1UdFAQDAgEBMA0GCSqGSIb3DQEBCwUAA4IB
+AQB+8PsN24d0Zz4YP749N8IiCoLpju5Z7O2yP6C4n3XwSRKExXK5ROwg9adTW556
+L44a0aChbR1b7Mnz/du0vx67O0D6hMRhS7ICuiI+pkpovJsVnT2QdObWBlls2Bic
+e+iSX+eIlXHcURQc0ZkhvKiiDYgxeSo4RvxknjaauHKdIA1DQC1G2IFU+Izjg7j0
+pS5yeLaGtz7azDmrChRrP+XRCDjsSuihtS9dtDRTyUwRYjhU6plkHjjwvXaFh3EW
+ZQWN0LVL7m7M2MtXUPlKYlEG8SpcrfBS25aBg4tRLRo8w/iWY0Z3HcRYYj9HiZhU
+3Yo0R0Z2apWmbVVmxNJQy+jA
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/TwoCRLsCAGoodCRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/TwoCRLsCAGoodCRL.pem
new file mode 100644
index 0000000000..7bffa1d5cd
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/TwoCRLsCAGoodCRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIBvjCBpwIBATANBgkqhkiG9w0BAQsFADBEMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEUMBIGA1UEAxMLVHdvIENSTHMgQ0EX
+DTEwMDEwMTA4MzAwMFoXDTMwMTIzMTA4MzAwMFqgLzAtMB8GA1UdIwQYMBaAFBCh
+AdaZnYDjbf3n7ndLX/FJ2TxTMAoGA1UdFAQDAgEBMA0GCSqGSIb3DQEBCwUAA4IB
+AQAWgigQQEvB+tF9ELpYI007J23N+XwGsg5vz8wIxMsi5Sa3NDkUCZ8ByXClFLRp
+zIM650GcrIDpZCHPhfgT9bU2b2LX/4vVdSy/aI7tC+nd70UPKeH4SU7sVwAJmKQD
+NRnWLJp7VXl79yR51SEMKrwNpgoFmblJHKcLyNr8NrwmLmS/5mpGaeri0bEh53cy
+Zv9dvJI6Nvh30wbYtcEtURtCSHWYlIYs8cQML4IsYcQbWSX8uS4XqSnaUVwYKtFT
+HdUX46XIkuGdKeExJKbWXb9R9JJcCG85Qn+mfX+Z0XatgOxGpmuzJ/JbEqxPS+w0
+25GOIJTaYjoDxoOmVIpJZGcZ
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/UIDCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/UIDCACRL.pem
new file mode 100644
index 0000000000..c94eb68a11
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/UIDCACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIBuTCBogIBATANBgkqhkiG9w0BAQsFADA/MQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEPMA0GA1UEAxMGVUlEIENBFw0xMDAx
+MDEwODMwMDBaFw0zMDEyMzEwODMwMDBaoC8wLTAfBgNVHSMEGDAWgBQQP8UEMPHY
+QzaFeVyMjYud7i8cqTAKBgNVHRQEAwIBATANBgkqhkiG9w0BAQsFAAOCAQEARJT8
+6342NMtFGT+8uvbJJ26LNypeqnke2LqSnfunR258riLofJj2E5r1NS7Ew5YjWdZH
+3BiwowR/IRuFqlj1l7gDB6L9YFVbNuJrC41UxIBzGMa2zHtTXxe610TC987XcbbT
+O4ccPzvQ3MEfiY7odLNkOpKfOHUTgOvPfpo/8X/jofZp0kBTH7q31S4XKH1XHBab
+H2kyudXTJmNkSXOdXlclPdZ1wdtt5hi/i5mYMQYnwVgQoburqiAt1LurPrOi6zgK
+JmfT7ELdKocAa0nRCBMMTZLknEAVubFfI1ViKGMGwolmVIQjlJrI7h6RMh4bUEVI
+eDsOknaIs9a+DRxKNA==
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/UTF8StringCaseInsensitiveMatchCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/UTF8StringCaseInsensitiveMatchCACRL.pem
new file mode 100644
index 0000000000..a6c2055e8e
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/UTF8StringCaseInsensitiveMatchCACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIB1zCBwAIBATANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJVUzEfMB0GA1UE
+CgwWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEtMCsGA1UEAwwkVVRGOFN0cmluZyBD
+YXNlIEluc2Vuc2l0aXZlIE1hdGNoIENBFw0xMDAxMDEwODMwMDBaFw0zMDEyMzEw
+ODMwMDBaoC8wLTAfBgNVHSMEGDAWgBRg3xjRyqlQkhEXIUTSd/Vqraa+eDAKBgNV
+HRQEAwIBATANBgkqhkiG9w0BAQsFAAOCAQEAfnKj08ZVDYA3KZCzOUW4VvkN0rgV
+qYukj5p80KK3oRJtQjiMB7M+BrG/Nu2ReWIrB2vSyryurZD2QeTcJOr68b0I/VLV
+uz4/b1/AlXgvUywrMiaXKrW/dApifr70HDghgvyCwQ1SDST/jFNBmrC//H61A3NG
+PenyRN+GkvTzlOIoJcqZ4PRX7J28D9lesW4m8Z+k7o/mF0c2F2lBMG+yzTh9ZB5W
+eaqvmY63gKTu3GRUHET6/SE2HI1fgioVFPDutr5b1uh9ACA60X+Lkg6MBhPqPGdY
+UQXKBt92Nd1aHg18VydBY9+6yBEvlQfpZyaQ/Sx7TL0SSwzKlcGDwXvVXA==
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/UTF8StringEncodedNamesCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/UTF8StringEncodedNamesCACRL.pem
new file mode 100644
index 0000000000..2bfdb97dcc
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/UTF8StringEncodedNamesCACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIBwDCBqQIBATANBgkqhkiG9w0BAQsFADBGMQswCQYDVQQGEwJVUzEfMB0GA1UE
+CgwWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEWMBQGA1UEAwwNVVRGOFN0cmluZyBD
+QRcNMTAwMTAxMDgzMDAwWhcNMzAxMjMxMDgzMDAwWqAvMC0wHwYDVR0jBBgwFoAU
+O2dbRPINp0h9cymMk5/VJOMSYCYwCgYDVR0UBAMCAQEwDQYJKoZIhvcNAQELBQAD
+ggEBAE2uI86u6pYfkenx+nHcN7B8ymkICJpPJlIqNfjteDVjwz9D2ra89nHKdXRE
+83ftELk0cXxN4qEuylBI+2V/WUNjll1VsTqvRSGDFu6KhjZHEjbdGHu2Z+tT7E9a
+72X3hxYJEDycSTlaR4OvFUWeKksPaKXt6HdhV7AQUHT9KbewgLDekpPKj1O1tjzs
+/NN2mt0mBqbm9dzi4oL2PezZLfEOpa1BXQR9r37HWKmDxO4SixOGNaOd+jplpzhy
+PDqftcMmgRZnKesEvKcXul1GRxreqhaF+dlvluKwzRgC9Z0x8iS8HyoqlCzdGy8x
+OuRJvqqfg0aPDn/waUA0pF7Qfwo=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/UnknownCRLEntryExtensionCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/UnknownCRLEntryExtensionCACRL.pem
new file mode 100644
index 0000000000..dfee3b2056
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/UnknownCRLEntryExtensionCACRL.pem
@@ -0,0 +1,13 @@
+-----BEGIN X509 CRL-----
+MIICCjCB8wIBATANBgkqhkiG9w0BAQsFADBXMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEnMCUGA1UEAxMeVW5rbm93biBDUkwg
+RW50cnkgRXh0ZW5zaW9uIENBFw0xMDAxMDEwODMwMDBaFw0zMDEyMzEwODMwMDBa
+MDcwNQIBARcNMTAwMTAxMDgzMDAwWjAhMAoGA1UdFQQDCgEBMBMGCWCGSAFlAgEM
+AgEB/wQDAgEAoC8wLTAfBgNVHSMEGDAWgBQAphnLoS1NKC8i89JMN8//TDDN6jAK
+BgNVHRQEAwIBATANBgkqhkiG9w0BAQsFAAOCAQEAIGIA0Yful35UcVgnqPFFJiLX
+VB8WXGbleSexbAb9EgxCCpffOaMuaq51McJ+Rjspyjmn0SEnbnpeKmNbmYtnQjn0
+OxNp/Yy5ZwjufitxKb9JgFLBhfpJ2BU9H5MgPEmXA+aZf5ojGPh/2afwJKE/9H22
+wemiZlYlpA5aN7PYqWHfEPE2bd4Ey7RHleuCRpfDHx2EgumLSRYjm9q++THgGOgu
+C59aDH3vvuAItfh9eVHTYxxjRm9kjIC/RCrCvQRclRGN/SoPDBys9RQxv1iklvN2
+rLH+Os+0BB3L67GMsAFQhWXCFU6U9Yubm/mErDVFOwsmuMNNKgojKkuqAhxZzQ==
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/UnknownCRLExtensionCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/UnknownCRLExtensionCACRL.pem
new file mode 100644
index 0000000000..8d7158e411
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/UnknownCRLExtensionCACRL.pem
@@ -0,0 +1,13 @@
+-----BEGIN X509 CRL-----
+MIICBDCB7QIBATANBgkqhkiG9w0BAQsFADBRMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEhMB8GA1UEAxMYVW5rbm93biBDUkwg
+RXh0ZW5zaW9uIENBFw0xMDAxMDEwODMwMDBaFw0zMDEyMzEwODMwMDBaMCIwIAIB
+ARcNMTAwMTAxMDgzMDAwWjAMMAoGA1UdFQQDCgEBoEQwQjAfBgNVHSMEGDAWgBT9
+//4ZTdsMncWi2IJW67DZEGHjMTATBglghkgBZQIBDAIBAf8EAwIBADAKBgNVHRQE
+AwIBATANBgkqhkiG9w0BAQsFAAOCAQEAsguh6rQBEGU98+Fqtl5R79QDu51NuNZF
+H2otyBt8ZPFJkif8J/pquv09GgIMFsMbAtMGkVPNYEjnQ6MHSWYE2SxTYeIfOnQb
+JiED6NtncdUGsoXt2g+9gJTDdVPjl8z9p1j1QEdp6c/XEj2gEVGDkM2iAy/3QImN
+kqrG1n1QtiAOUQ1oZoIk6NnGfAvRQ5/eNZafTa7vm805Q11tE6mT8XwbUX9Cs/lL
+X5h3h7DATdVfDPYwac9NZ8rrof7FQ/jXB4p2P5PM/U2vqEM8YTuRhW2w+jGzBFY6
+ef7bd7LrSoWExjczcmVuIZDqrxYsdAc4TJbQUm8B28FkdM4NzVo2Bg==
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/WrongCRLCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/WrongCRLCACRL.pem
new file mode 100644
index 0000000000..114e6e1cbf
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/WrongCRLCACRL.pem
@@ -0,0 +1,13 @@
+-----BEGIN X509 CRL-----
+MIIB4zCBzAIBATANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEVMBMGA1UEAxMMVHJ1c3QgQW5jaG9y
+Fw0xMDAxMDEwODMwMDBaFw0zMDEyMzEwODMwMDBaMCIwIAIBaBcNMTAwMTAxMDgz
+MDAwWjAMMAoGA1UdFQQDCgEBoC8wLTAfBgNVHSMEGDAWgBTkfV/RXJWGCCwFrr51
+tmWn2V2oZjAKBgNVHRQEAwIBATANBgkqhkiG9w0BAQsFAAOCAQEAqxkdtbsha7bh
+TM3wtzeelTjR1IGQgK4R8Psc2fw2NOydlq8PeSc3qitHV6m4dqHzchQlytYprOK4
+dRitEh+RYY4UKUNu+OQQ5VFLSvuC0Wv3xn2w29VqpQtavBFfJ8Lst9520pece6x8
+6fB9L6VP4YNGIrLc+7hEjEDALJs+ttPoxNNXGMApQQi5xyZEksXQAo60ZdH/r95l
+dVCa7U2OVXO1MCuZlWQRlql0Bi3CzE26cW1jccEdU6yQ0ONKNuROR+6NsXZ2Qm2C
+lHEGWFJAZ/CWB7NjQ9maNkoioZb4IB2AKPKBcb0mT3TYspgT8zcZSP5DLC8iVOrc
+x2SLSvd35g==
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/anyPolicyCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/anyPolicyCACRL.pem
new file mode 100644
index 0000000000..d8cd6550ee
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/anyPolicyCACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIBvzCBqAIBATANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEVMBMGA1UEAxMMYW55UG9saWN5IENB
+Fw0xMDAxMDEwODMwMDBaFw0zMDEyMzEwODMwMDBaoC8wLTAfBgNVHSMEGDAWgBS7
+yd7IHJXnQuKQoo6uA1yrJGB+hTAKBgNVHRQEAwIBATANBgkqhkiG9w0BAQsFAAOC
+AQEAW7i6zdMI6ytGpku8kYKf2WVx9tqIMFh+vvcagsWR/9oPNp1kXgdfv/tfRS9F
+JRQ3RVYaKY2bWDVc8ISsrwZpBMe3RjfDEdqAyuNcy4B/C+O7OAtyNinAvtsYASAU
+gfDoW3s1V53iLbLDfY1uRCq7F78dHwjuuIob+tpHFR9EVUx3MgkPAyq8oLrbDdaV
+FAsq2XTgry00qj82bR0rSY7UdNMOhDy32qeoH4ZEEh0XHdBEM3xuMjwn6x6J2SiV
+96FDpO+UNcTr/VeV9sx0xJAJOqmSU+e8nL8FKCGhm02fI9CIZBV4TOICDgacRgOn
++aSLkS+XcWlyrkZL2FakdOWiXQ==
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/basicConstraintsCriticalcAFalseCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/basicConstraintsCriticalcAFalseCACRL.pem
new file mode 100644
index 0000000000..b5726c158e
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/basicConstraintsCriticalcAFalseCACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIB2DCBwQIBATANBgkqhkiG9w0BAQsFADBeMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEuMCwGA1UEAxMlYmFzaWNDb25zdHJh
+aW50cyBDcml0aWNhbCBjQSBGYWxzZSBDQRcNMTAwMTAxMDgzMDAwWhcNMzAxMjMx
+MDgzMDAwWqAvMC0wHwYDVR0jBBgwFoAUcN9ELwOZHBdzGPI2PBQ00AnR8u0wCgYD
+VR0UBAMCAQEwDQYJKoZIhvcNAQELBQADggEBAFaK4KYhrGo/o9KPU/OnZGglN4vq
+uuAH6a9XjvSUA/de1oHyAxw6rgxrjBo1pgtdLgSqXuYXMeqyVjZV5ew58wd7f0vG
+u06a6f3TOX3ScNZYY9/u1VRsX8/y3kKUg2+EQdr7vYCjhSIc2SLqpAnku150OwB9
+GZG7y/NRjqX9AyecyCJm16G/w9fA4+tndtvBvvDIwYn+LGrse5cgka4iR+CWzsIj
+cA17nCnhg3n88NSXrIsre1LI1YBKEHrIqbTpDglDxtJcdTtF+5/new5L+vftj0Rv
+8bR8NQPo5DBNJ5C9GnCjQgVSyMnQNhD3+LUDDScGs0QVg981FI6tusPFZpE=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/basicConstraintsNotCriticalCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/basicConstraintsNotCriticalCACRL.pem
new file mode 100644
index 0000000000..3dfeb7ca4c
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/basicConstraintsNotCriticalCACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIB0zCBvAIBATANBgkqhkiG9w0BAQsFADBZMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEpMCcGA1UEAxMgYmFzaWNDb25zdHJh
+aW50cyBOb3QgQ3JpdGljYWwgQ0EXDTEwMDEwMTA4MzAwMFoXDTMwMTIzMTA4MzAw
+MFqgLzAtMB8GA1UdIwQYMBaAFAqkuTBDrEPINAITz+9V6L9wn0avMAoGA1UdFAQD
+AgEBMA0GCSqGSIb3DQEBCwUAA4IBAQBY/ZWklRmCU6F7pRzO8mrG3iaF2gp4Qyse
+QLf/yzlYy5XdbwzhwiClQ7EwxA9jaGD8EKopJL9sMh53QBZFivrXUP+O09cHRlMI
+SCyonmbdvnOUDwoky9YY4KrC0YI+mhbOpe6LQJcRgj2ge5C1i+UsQmUZapOUvXaI
+f5rc1TlLg4mlxYSQT9jnLV3aAwt4i6cwitTkpbYPolvjsHqfRQ2/aLLd59SNnIlv
+pdso7IaAW3aoo38dJ8ZyJZBwNAwR3dmi4oIOivHX6FwDgxNdhVIN5X/VsMeNbRu/
+mD1HePYlexWjcMfsVRx9oVsIVoT0i8dAXEnefM7n1XOadWnaW8Di
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/basicConstraintsNotCriticalcAFalseCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/basicConstraintsNotCriticalcAFalseCACRL.pem
new file mode 100644
index 0000000000..36b9df8549
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/basicConstraintsNotCriticalcAFalseCACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIB3DCBxQIBATANBgkqhkiG9w0BAQsFADBiMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEyMDAGA1UEAxMpYmFzaWNDb25zdHJh
+aW50cyBOb3QgQ3JpdGljYWwgY0EgRmFsc2UgQ0EXDTEwMDEwMTA4MzAwMFoXDTMw
+MTIzMTA4MzAwMFqgLzAtMB8GA1UdIwQYMBaAFDnQm7dPKTe+07CKdupqns3vRr5Y
+MAoGA1UdFAQDAgEBMA0GCSqGSIb3DQEBCwUAA4IBAQB5jwS9LsJUqzevwbstTFfJ
+TrsCQBjg8kn30zn2Yi0+ff92y1zfpzK3MSFfq9olKCKSpoKdwgLTZVXiHsO44X4d
+cIzo03knrnXX6wAQH0b/GXlbvQ9fEZaylTxMV7mO38UgIs9cWXdqjlsgAy37qOBT
+L5Dn2xUp2u/+QcbwyLOGP7/shriabGHlepclCQI6BxIor8gJz2zCxtovV90j7WL0
+8nF8wMwVv89hu38b92uORIQDamYDRTigiOYS5FPTirqoReA9lUdQ+hd3KaS1S/YE
+w7fmNEbkdCMg4hHAYnR0xW54Bp9bea2ANRJmdwUCz930q3XqPS8OJt/PZgOAmdYa
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/deltaCRLCA1CRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/deltaCRLCA1CRL.pem
new file mode 100644
index 0000000000..338d64cd37
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/deltaCRLCA1CRL.pem
@@ -0,0 +1,16 @@
+-----BEGIN X509 CRL-----
+MIIChDCCAWwCAQEwDQYJKoZIhvcNAQELBQAwRTELMAkGA1UEBhMCVVMxHzAdBgNV
+BAoTFlRlc3QgQ2VydGlmaWNhdGVzIDIwMTExFTATBgNVBAMTDGRlbHRhQ1JMIENB
+MRcNMTAwMTAxMDgzMDAwWhcNMzAxMjMxMDgzMDAwWjBmMCACAQIXDTEwMDEwMTA4
+MzAwMFowDDAKBgNVHRUEAwoBATAgAgEEFw0xMDAxMDEwODMwMDBaMAwwCgYDVR0V
+BAMKAQYwIAIBBRcNMTAwMTAxMDgzMDAwWjAMMAoGA1UdFQQDCgEGoIGKMIGHMB8G
+A1UdIwQYMBaAFHcYI+V2hMgUlD+C0IHqdLHgpC8zMFgGA1UdLgRRME8wTaBLoEmk
+RzBFMQswCQYDVQQGEwJVUzEfMB0GA1UEChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAx
+MTEVMBMGA1UEAxMMZGVsdGFDUkwgQ0ExMAoGA1UdFAQDAgEBMA0GCSqGSIb3DQEB
+CwUAA4IBAQCEUqY7TzVnZWYlyrtBszCIhhTKhr7tH5UHvoZkNs8lzeBMqtUDZvd6
+o0mB7WSv5R+t8tGleNhn4isudltqLAWZxxZNnht04o3YZTwKS/NRNAinBoQfZnmw
+abMmledsFuF2JrA3XYOZ5NWdlah8/MPUAKRoI0Dw7qxmLL/exsz5OAKh3qjXlzwJ
+Bdm4CYCO3DH58BQk6oJKVlqCY3ZsCuvtvjNHnLpkXAKRQ+t/OsHWssYkEVX5FizE
+WE6G6XMMcQtb2gN0QwzO8o3xyFGYyy1gkEuB+yzmqHqLYTlRZd5IvRxYYoqHbrce
+x7ROIMJEsNFw5k+b7qWYLo703GOsCxD0
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/deltaCRLCA1deltaCRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/deltaCRLCA1deltaCRL.pem
new file mode 100644
index 0000000000..4c306accba
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/deltaCRLCA1deltaCRL.pem
@@ -0,0 +1,15 @@
+-----BEGIN X509 CRL-----
+MIICWjCCAUICAQEwDQYJKoZIhvcNAQELBQAwRTELMAkGA1UEBhMCVVMxHzAdBgNV
+BAoTFlRlc3QgQ2VydGlmaWNhdGVzIDIwMTExFTATBgNVBAMTDGRlbHRhQ1JMIENB
+MRcNMTEwMTAxMDgzMDAwWhcNMzAxMjMxMDgzMDAwWjCBiDAgAgEDFw0xMDA2MDEw
+ODMwMDBaMAwwCgYDVR0VBAMKAQEwIAIBBBcNMTAwNjAxMDgzMDAwWjAMMAoGA1Ud
+FQQDCgEIMCACAQUXDTEwMDEwMTA4MzAwMFowDDAKBgNVHRUEAwoBATAgAgEGFw0x
+MDA2MDEwODMwMDBaMAwwCgYDVR0VBAMKAQigPjA8MB8GA1UdIwQYMBaAFHcYI+V2
+hMgUlD+C0IHqdLHgpC8zMA0GA1UdGwEB/wQDAgEBMAoGA1UdFAQDAgEFMA0GCSqG
+SIb3DQEBCwUAA4IBAQAFVo99MjLWWcc8kdu/qveAAPZgvgHBAQqOygCNbu4J704l
+Ab9wHTJoNvghxK7iGsCGd2O7l1ODdooPWNcY3NVkY2soZmevk5F19MQX7fr4HrLp
+Dtcdfbby1PG2TX0MttfwpGDh8yfO1SFjkclmoP8f1f8mrmulJcy/sXK+H478JHO1
+lycEzMsvgGj2HCZvRziR+g3nH2AVMra4r6u+oo1ctwcWWGRkhfuyeBBJWDMh3T61
+gIbdB+Atjs3QBscvjPdU+F5XyuaBLbwEBYJOoTeSKTxfDVeYnPFghHyenItJsmII
+5IPocdTk/6/RB0wtDlMQwjNUXxe0/3WoHVy8bUQZ
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/deltaCRLCA2CRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/deltaCRLCA2CRL.pem
new file mode 100644
index 0000000000..b4090dfee4
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/deltaCRLCA2CRL.pem
@@ -0,0 +1,15 @@
+-----BEGIN X509 CRL-----
+MIICQDCCASgCAQEwDQYJKoZIhvcNAQELBQAwRTELMAkGA1UEBhMCVVMxHzAdBgNV
+BAoTFlRlc3QgQ2VydGlmaWNhdGVzIDIwMTExFTATBgNVBAMTDGRlbHRhQ1JMIENB
+MhcNMTAwNjAxMDgzMDAwWhcNMzAxMjMxMDgzMDAwWjAiMCACAQIXDTEwMDEwMTA4
+MzAwMFowDDAKBgNVHRUEAwoBAaCBijCBhzAfBgNVHSMEGDAWgBR82Pa+A0zOz7c/
+oRm7M6u11437xDBYBgNVHS4EUTBPME2gS6BJpEcwRTELMAkGA1UEBhMCVVMxHzAd
+BgNVBAoTFlRlc3QgQ2VydGlmaWNhdGVzIDIwMTExFTATBgNVBAMTDGRlbHRhQ1JM
+IENBMjAKBgNVHRQEAwIBAjANBgkqhkiG9w0BAQsFAAOCAQEA57Gy4XXEzOgR+KG2
+Yc4w8HgmX79ERY3s082LNYuSwFrA3D7hoCa+tX8tuXbF6Kgb+wocIFROzk7r+ZIp
+PegSzJQcKWoVCQiuHUfBt3MVZLojeM4n8ThfMbPOnf+Si+V4YgEcBGcgKt/pxEnZ
+d1dJu+rB+TSyUk4QuJ5X2DJn4ne+2Wf6mzoZKakJp61aDD5QxysbN/52R9QR/wdg
+awyL5mtu/8U342t+4sFbLm7X/pyDcPkp9on2ORsKGIBGGr3QoHItIAZn2ghPsT6a
+5YYcJOUm/pZwBeKoZyQ+s9Lhon5HOWs5aE3KwFdS5vo1zpB2Ps9Sv4ud2+O135oG
+tuFQTA==
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/deltaCRLCA2deltaCRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/deltaCRLCA2deltaCRL.pem
new file mode 100644
index 0000000000..d305bae0ab
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/deltaCRLCA2deltaCRL.pem
@@ -0,0 +1,13 @@
+-----BEGIN X509 CRL-----
+MIIB8jCB2wIBATANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEVMBMGA1UEAxMMZGVsdGFDUkwgQ0Ey
+Fw0xMTAxMDEwODMwMDBaFw0zMDEyMzEwODMwMDBaMCIwIAIBAhcNMTAwMTAxMDgz
+MDAwWjAMMAoGA1UdFQQDCgEBoD4wPDAfBgNVHSMEGDAWgBR82Pa+A0zOz7c/oRm7
+M6u11437xDANBgNVHRsBAf8EAwIBATAKBgNVHRQEAwIBAzANBgkqhkiG9w0BAQsF
+AAOCAQEAWX5v6H6qmSiro3ZgYkvIi4WDhAzCpjMNElzQ7rs+mPh/1B/2CSAv+B9Q
+RXHJvMYS0XEwfvTy3N5vAhY6QMNhPKJcepJHVmng0NMOBn3DMJJ7nobJKAA95ls8
+/BP+nNjVHoq7RnLivTxvXOijGGCJACg3st64CByuwcbNcJdEJkI0usFnLEoA8W9Z
+G47VSzeORnf3EmA4IZERhS7NY3+91CDRb8R0CO0u8sVVPPWMltdN0WYSnQA9D4oV
+hOEunai3vsrn7DVSsoTrIWz7zxgIgxk+6sfaWhGIKVLW34lIdA2geMWt446/maWg
+E/O9OWirLi1qY7bp8BcCUe9UL/cfOQ==
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/deltaCRLCA3CRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/deltaCRLCA3CRL.pem
new file mode 100644
index 0000000000..084e49127f
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/deltaCRLCA3CRL.pem
@@ -0,0 +1,14 @@
+-----BEGIN X509 CRL-----
+MIICHDCCAQQCAQEwDQYJKoZIhvcNAQELBQAwRTELMAkGA1UEBhMCVVMxHzAdBgNV
+BAoTFlRlc3QgQ2VydGlmaWNhdGVzIDIwMTExFTATBgNVBAMTDGRlbHRhQ1JMIENB
+MxcNMTAwMTAxMDgzMDAwWhcNMTAwNjAxMDgzMDAwWqCBijCBhzAfBgNVHSMEGDAW
+gBTvY9OoTrH532HiDcMFo5gY0pOZ5zBYBgNVHS4EUTBPME2gS6BJpEcwRTELMAkG
+A1UEBhMCVVMxHzAdBgNVBAoTFlRlc3QgQ2VydGlmaWNhdGVzIDIwMTExFTATBgNV
+BAMTDGRlbHRhQ1JMIENBMzAKBgNVHRQEAwIBATANBgkqhkiG9w0BAQsFAAOCAQEA
+XB3lEAs1DWFKrU3BzflSTtmiCa5KzMZrXpDmqHt7+kWNjEMUS17TsvkrfGWOcEFJ
+9MzmGUqAuD9Z0q5kxVVNRuZhG8K9U+biCmMVFtKzxFGbz/vJkcKaroB/ukiqNQlv
+DTSKebUJJm5g0rc06LVGic+YgK2pcCwm4BsdmcCEYV3mlIhUOUA48Ww0WemFKPTw
+9A1V3URGpOzrMJYJOJff0IOVjdyGwSc+xHCtbJG4xV+/Qe1/9yYn+RqHQXUo0C7D
+GJNSr+OcpXX8urZQp4ZVQRz3OTs4eb2xRK1ZPj9Xhiv98iZwo38GaFGvMhOHv7gv
+C13eDAq4KnlQgz/wpxmHog==
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/deltaCRLCA3deltaCRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/deltaCRLCA3deltaCRL.pem
new file mode 100644
index 0000000000..db02c94369
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/deltaCRLCA3deltaCRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIBzjCBtwIBATANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEVMBMGA1UEAxMMZGVsdGFDUkwgQ0Ez
+Fw0xMDA2MDEwODMwMDBaFw0zMDEyMzEwODMwMDBaoD4wPDAfBgNVHSMEGDAWgBTv
+Y9OoTrH532HiDcMFo5gY0pOZ5zANBgNVHRsBAf8EAwIBAjAKBgNVHRQEAwIBAzAN
+BgkqhkiG9w0BAQsFAAOCAQEAy7ylas55kqoznlqWS9cO6Q/EC7dPiZKF4Szp326L
+qCs4kd16Vbi3mhz7BP2iHSjO0AlaMnmGSYOlnzQEtuD6NlQB1RhuWH1kCWAaQPXU
+4K4nMJduHuGjopkIxnN8BGPfXGbeOJfl0FDQM84vkRCJ4Owq233JvEIDCEhdkYsO
+wQr+dUqPNkR1lz7fGtskqpe3aotkQ3DrS/1wuBRuTmXLKDZy63IdAe+TTp8yugnl
+9QEdYEXvMgLChLe57ZaOFVCE8349rkhikvnFWIfc1st2EsBrOaOwqFUrZReVg6qx
+wxDSf2FoINaLIX7ECkEBBmQh9SyEz8BXwaboI8z08ovRzg==
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/deltaCRLIndicatorNoBaseCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/deltaCRLIndicatorNoBaseCACRL.pem
new file mode 100644
index 0000000000..781db0a18d
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/deltaCRLIndicatorNoBaseCACRL.pem
@@ -0,0 +1,13 @@
+-----BEGIN X509 CRL-----
+MIIB3jCBxwIBATANBgkqhkiG9w0BAQsFADBVMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTElMCMGA1UEAxMcZGVsdGFDUkxJbmRp
+Y2F0b3IgTm8gQmFzZSBDQRcNMTAwNTAxMDgzMDAwWhcNMzAxMjMxMDgzMDAwWqA+
+MDwwHwYDVR0jBBgwFoAU9Dh2Jauk4xzAyHWMjRNrYyO2ioEwDQYDVR0bAQH/BAMC
+AQEwCgYDVR0UBAMCAQUwDQYJKoZIhvcNAQELBQADggEBAJOY+1gmjxVv2D6HNyPt
+vKA7Gk7seNkgnSPoPR24Ao0znff3gjJGgz6r0TY7IIF+ddgjmu4l6BpkS2fN2Uow
+BCFEAD/0lK0RPtuyUCdbFiSHtNQlbpbxEK0iwDAw2YoYQZBP6qobz3IIjxIZjQou
+XlOIRoXE8Lg0QHFHQF58+Bs7Exk5kU8bwCalEL36OcGihMb9wXuuJTvSuXypU5v6
+kDwwrvxx06w9O2WYC8dqriObG8Uz04+Xt6alCmcj/sU2rop6BZMheZ8bKSHXCPWB
+FWIubsW25os4rHlxDz+DmXAcRCyIyrQxz9ERTDjPhKhAUPXz+baG38/oqA3bVMn4
+HKo=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/distributionPoint1CACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/distributionPoint1CACRL.pem
new file mode 100644
index 0000000000..dc27f6b6fd
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/distributionPoint1CACRL.pem
@@ -0,0 +1,16 @@
+-----BEGIN X509 CRL-----
+MIICfTCCAWUCAQEwDQYJKoZIhvcNAQELBQAwTjELMAkGA1UEBhMCVVMxHzAdBgNV
+BAoTFlRlc3QgQ2VydGlmaWNhdGVzIDIwMTExHjAcBgNVBAsTFWRpc3RyaWJ1dGlv
+blBvaW50MSBDQRcNMTAwMTAxMDgzMDAwWhcNMzAxMjMxMDgzMDAwWjAiMCACAQIX
+DTEwMDEwMTA4MzAwMFowDDAKBgNVHRUEAwoBAaCBvjCBuzAfBgNVHSMEGDAWgBQR
+MHO9jXAogtJvz9I37c3rI5Hb7zCBiwYDVR0cAQH/BIGAMH6gfKB6pHgwdjELMAkG
+A1UEBhMCVVMxHzAdBgNVBAoTFlRlc3QgQ2VydGlmaWNhdGVzIDIwMTExHjAcBgNV
+BAsTFWRpc3RyaWJ1dGlvblBvaW50MSBDQTEmMCQGA1UEAxMdQ1JMMSBvZiBkaXN0
+cmlidXRpb25Qb2ludDEgQ0EwCgYDVR0UBAMCAQEwDQYJKoZIhvcNAQELBQADggEB
+ACrxG+l579by89QJva16Yro3XPJ9lXu/l8cgQpoaHZifIG9TnaWxb4WFwSBIbOqp
+rbQW7N580RJBH2SqiiUmCu6Acg2ryKfywGULkFFd0ZYMBrhtHQe7T0SJq0NFtMlZ
+A9Vgia3Tqhiam3c1oJeZnETcn35b5qLAmr+EaRpXDT4Mflqwub0kgwal6p9wmuH5
+vs4dc98l/Ub+J+ve8yVmMVq/0YSGv5Z2IjX2V9iWdjcZPnAC3BaOMvm40zy5cOWJ
+FaXii7M41MgR4d8ZMQn/UQGb2CMypm207QW3yNbuI7+/0y0vxJDo2k84ktw6sAda
+dQBSIAUmYp0Rsdtg9TwZE00=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/distributionPoint2CACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/distributionPoint2CACRL.pem
new file mode 100644
index 0000000000..1ebde75c29
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/distributionPoint2CACRL.pem
@@ -0,0 +1,14 @@
+-----BEGIN X509 CRL-----
+MIICJTCCAQ0CAQEwDQYJKoZIhvcNAQELBQAwTjELMAkGA1UEBhMCVVMxHzAdBgNV
+BAoTFlRlc3QgQ2VydGlmaWNhdGVzIDIwMTExHjAcBgNVBAsTFWRpc3RyaWJ1dGlv
+blBvaW50MiBDQRcNMTAwMTAxMDgzMDAwWhcNMzAxMjMxMDgzMDAwWjAiMCACAQIX
+DTEwMDEwMTA4MzAwMFowDDAKBgNVHRUEAwoBAaBnMGUwHwYDVR0jBBgwFoAURGzu
+229/605Jf3j+zeUYoOy7YGswNgYDVR0cAQH/BCwwKqAooSYwJAYDVQQDEx1DUkwx
+IG9mIGRpc3RyaWJ1dGlvblBvaW50MiBDQTAKBgNVHRQEAwIBATANBgkqhkiG9w0B
+AQsFAAOCAQEAD560RltKzGwM+LefEKN9n+UE6uNAoPwiNSYPGe9Qcu9WXsG11CqZ
+O1saNKTu0+bMx11WU3qRDyGpWZidaW0/x60RcSMR/X/RGoZw2hlixluvHpIBQwYf
+H2V2PE+OjNIeXBF9uEAV5KuBRb/Yre7GlCoXJM1j9WZRCHKwVYZ2xNog9R8STNs5
+fw4kynvydxQ6U8KjUxJeBmb6PiAf64Mcf9AXUCjakwAKJgw2zptKV75QrEEwpnVj
+sv7A5HqoMS7mLwPV2dznH+rHn/0IJp1FuS9YNs4L7oTyKZYlriy7U5pmsHHajGIc
+6g5+w/GiX51Emsi7RXjAvfpu/9MisG29PQ==
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/indirectCRLCA1CRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/indirectCRLCA1CRL.pem
new file mode 100644
index 0000000000..c9de28ecb9
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/indirectCRLCA1CRL.pem
@@ -0,0 +1,13 @@
+-----BEGIN X509 CRL-----
+MIIB9zCB4AIBATANBgkqhkiG9w0BAQsFADBIMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEYMBYGA1UEAxMPaW5kaXJlY3RDUkwg
+Q0ExFw0xMDAxMDEwODMwMDBaFw0zMDEyMzEwODMwMDBaMCIwIAIBAhcNMTAwMTAx
+MDgzMDAwWjAMMAoGA1UdFQQDCgEBoEAwPjAfBgNVHSMEGDAWgBQl+K/8r7apGht5
+S9vLZCyLS7EVzTAPBgNVHRwBAf8EBTADhAH/MAoGA1UdFAQDAgEBMA0GCSqGSIb3
+DQEBCwUAA4IBAQALqiVAlWG4BlkRVLdCVDqUtfpcaK8JLzEqoQUm4fS+Y+mdf9uA
+Q8+l8aQmcGeh/Gkufzp4bwsxkkxL81qTvuvDm4UV5q4TYz3aaWtzE/ewNWzLyLjA
+FQafw0u0ZTdXs5Z7R03DlwrGH27ZmMqjue8uV060HNTVUzWJjnSwxZ2FZ1gY6Rbe
+F0cmOk3ym2/1h61sWgBZRLG5vIGZkJe/An/pJyGL68bKTnyaPeYdKm0knt/yGGbo
+24bdwhzVLbzPIq6n8JCNWhO5r9pcmhR+XdrgddIpS341f9S41j82WGfwLCnc0pBX
+6PxVxMdDQ1qLQAbzCU3OCE98HtTv7IpDCUdi
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/indirectCRLCA3CRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/indirectCRLCA3CRL.pem
new file mode 100644
index 0000000000..607f7bf989
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/indirectCRLCA3CRL.pem
@@ -0,0 +1,14 @@
+-----BEGIN X509 CRL-----
+MIICMjCCARoCAQEwDQYJKoZIhvcNAQELBQAwSDELMAkGA1UEBhMCVVMxHzAdBgNV
+BAoTFlRlc3QgQ2VydGlmaWNhdGVzIDIwMTExGDAWBgNVBAsTD2luZGlyZWN0Q1JM
+IENBMxcNMTAwMTAxMDgzMDAwWhcNMzAxMjMxMDgzMDAwWqCBnTCBmjAfBgNVHSME
+GDAWgBRIk1R9xG0w/y1XRXEk30wFn0oALTBrBgNVHRwBAf8EYTBfoF2gW6RZMFcx
+CzAJBgNVBAYTAlVTMR8wHQYDVQQKExZUZXN0IENlcnRpZmljYXRlcyAyMDExMRgw
+FgYDVQQLEw9pbmRpcmVjdENSTCBDQTMxDTALBgNVBAMTBENSTDEwCgYDVR0UBAMC
+AQEwDQYJKoZIhvcNAQELBQADggEBACvzWPSm982N1bxs5RW+yTNbWoZ1NBGEvAUX
+B80kE0PTfchM1HbkPh4SkkVarj2/alnN4E7cMWzlq1qhc76WR4ApjPZmCW4zlw1e
+a229Eh3s15IX+CZcBEza5mQIHc5mnn2v80MPJCS7W53lvPlrD3T85bv3w3uX8UZi
+AkR4wCC12Rbnm4fEtgkIxuB7FrKb0SWf6ygBn+Tcv27CGSF9YBhVsXEyGAA8qt9N
+1eBJrRGBu1xG6rYEuW5OQgGhZgV3QhxEqUxhatPtYT8bcR07S3PrGx9Y+Mh+o9Mw
+LuHM6OiDiRdd2nRUmWWpZGOgdTKEJ0eFrqOnxHPPF3bhhiJs0hE=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/indirectCRLCA3cRLIssuerCRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/indirectCRLCA3cRLIssuerCRL.pem
new file mode 100644
index 0000000000..a366a243ba
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/indirectCRLCA3cRLIssuerCRL.pem
@@ -0,0 +1,15 @@
+-----BEGIN X509 CRL-----
+MIICajCCAVICAQEwDQYJKoZIhvcNAQELBQAwUjELMAkGA1UEBhMCVVMxHzAdBgNV
+BAoTFlRlc3QgQ2VydGlmaWNhdGVzIDIwMTExIjAgBgNVBAsTGWluZGlyZWN0Q1JM
+IENBMyBjUkxJc3N1ZXIXDTEwMDEwMTA4MzAwMFoXDTMwMTIzMTA4MzAwMFqggcsw
+gcgwHwYDVR0jBBgwFoAUkdE5mMnvT1RlihhSLXwSEgpsd40wgZgGA1UdHAEB/wSB
+jTCBiqCBhKCBgaR/MH0xCzAJBgNVBAYTAlVTMR8wHQYDVQQKExZUZXN0IENlcnRp
+ZmljYXRlcyAyMDExMSIwIAYDVQQLExlpbmRpcmVjdENSTCBDQTMgY1JMSXNzdWVy
+MSkwJwYDVQQDEyBpbmRpcmVjdCBDUkwgZm9yIGluZGlyZWN0Q1JMIENBM4QB/zAK
+BgNVHRQEAwIBATANBgkqhkiG9w0BAQsFAAOCAQEANA7Z476bqcNnf7wWi2LE7Gws
+3Nz3U+G05Yp4xMLc4gYoKj/W+mjvsbXzJ2sjU8D2e3qHu3p0lIsFP+ZXXhvi4CEv
+3lIMQQE8/pPGxtqGDSnKRstdZPq2JvqisZFieChKH469G7yrHNMKD6C4MkQs27gj
+3cybk3G5fEGmpE5xVwHb1m2P/wD3vzkQVU6+T+RD8FPlZbqJRZEdIg/RSBQNXjbY
+ZIvKCkfl6w63f39NgMYSsfUM2g4jiv8PAoAR/RmDHgvylCoc8E2KDWiTFss/502e
+hhNTwMhEGS+Aze381vRThygIpqw29m4My844a8XVX2jZ8y6GGoiwqdby8U4wwg==
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/indirectCRLCA4cRLIssuerCRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/indirectCRLCA4cRLIssuerCRL.pem
new file mode 100644
index 0000000000..596762cdbb
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/indirectCRLCA4cRLIssuerCRL.pem
@@ -0,0 +1,15 @@
+-----BEGIN X509 CRL-----
+MIICajCCAVICAQEwDQYJKoZIhvcNAQELBQAwUjELMAkGA1UEBhMCVVMxHzAdBgNV
+BAoTFlRlc3QgQ2VydGlmaWNhdGVzIDIwMTExIjAgBgNVBAsTGWluZGlyZWN0Q1JM
+IENBNCBjUkxJc3N1ZXIXDTEwMDEwMTA4MzAwMFoXDTMwMTIzMTA4MzAwMFqggcsw
+gcgwHwYDVR0jBBgwFoAU8wjrbbnFoBCk2gRF/696R68RwLkwgZgGA1UdHAEB/wSB
+jTCBiqCBhKCBgaR/MH0xCzAJBgNVBAYTAlVTMR8wHQYDVQQKExZUZXN0IENlcnRp
+ZmljYXRlcyAyMDExMSIwIAYDVQQLExlpbmRpcmVjdENSTCBDQTQgY1JMSXNzdWVy
+MSkwJwYDVQQDEyBpbmRpcmVjdCBDUkwgZm9yIGluZGlyZWN0Q1JMIENBNIQB/zAK
+BgNVHRQEAwIBATANBgkqhkiG9w0BAQsFAAOCAQEAKRPyrIpC9HqbA9TytL4xuspW
+lO0Ihz0TyIs8bWEMPSanD0tdGsO8v1QQrv/XSVeAPiDcPvwFtX47TpbPJxJQBV5/
+AfGEAJ/o5Jl9ah2yuA3Qd1UZkL85V/XJgnqxpqJeLRQoQ3PeT0rlnQUM2fru3LrF
+AZn9lpxyqfqPsNbcDwwf0dbtGOvwUC2KTkXueizW0VEFgxRmRcielTglQXSV+Y1Y
+/MLqly+BHpWOko7BnfR6Ukfod6O8vyPwhUm0VW7AxPNH/qbqcpJjMnwCN8Bzp5LL
+UfQ6pkmCRWESbba14wXPuhoPM3OhzBzpGrBMeRpml96Tf3Q6xho0V/QmOaAs8g==
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/indirectCRLCA5CRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/indirectCRLCA5CRL.pem
new file mode 100644
index 0000000000..fdab0bd2f1
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/indirectCRLCA5CRL.pem
@@ -0,0 +1,35 @@
+-----BEGIN X509 CRL-----
+MIIGJTCCBQ0CAQEwDQYJKoZIhvcNAQELBQAwSDELMAkGA1UEBhMCVVMxHzAdBgNV
+BAoTFlRlc3QgQ2VydGlmaWNhdGVzIDIwMTExGDAWBgNVBAsTD2luZGlyZWN0Q1JM
+IENBNRcNMTAwMTAxMDgzMDAwWhcNMzAxMjMxMDgzMDAwWjCCAt4wIAIBARcNMTAw
+MTAxMDgzMDAwWjAMMAoGA1UdFQQDCgEBMHoCAQIXDTEwMDEwMTA4MzAwMFowZjAK
+BgNVHRUEAwoBATBYBgNVHR0BAf8ETjBMpEowSDELMAkGA1UEBhMCVVMxHzAdBgNV
+BAoTFlRlc3QgQ2VydGlmaWNhdGVzIDIwMTExGDAWBgNVBAMTD2luZGlyZWN0Q1JM
+IENBNjAgAgEDFw0xMDAxMDEwODMwMDBaMAwwCgYDVR0VBAMKAQEwIAIBBBcNMTAw
+MTAxMDgzMDAwWjAMMAoGA1UdFQQDCgEBMHoCAQUXDTEwMDEwMTA4MzAwMFowZjAK
+BgNVHRUEAwoBATBYBgNVHR0BAf8ETjBMpEowSDELMAkGA1UEBhMCVVMxHzAdBgNV
+BAoTFlRlc3QgQ2VydGlmaWNhdGVzIDIwMTExGDAWBgNVBAMTD2luZGlyZWN0Q1JM
+IENBNzAgAgEGFw0xMDAxMDEwODMwMDBaMAwwCgYDVR0VBAMKAQEwIAIBBxcNMTAw
+MTAxMDgzMDAwWjAMMAoGA1UdFQQDCgEBMHoCAQgXDTEwMDEwMTA4MzAwMFowZjAK
+BgNVHRUEAwoBATBYBgNVHR0BAf8ETjBMpEowSDELMAkGA1UEBhMCVVMxHzAdBgNV
+BAoTFlRlc3QgQ2VydGlmaWNhdGVzIDIwMTExGDAWBgNVBAMTD2luZGlyZWN0Q1JM
+IENBNjAgAgEJFw0xMDAxMDEwODMwMDBaMAwwCgYDVR0VBAMKAQEwegIBChcNMTAw
+MTAxMDgzMDAwWjBmMAoGA1UdFQQDCgEBMFgGA1UdHQEB/wROMEykSjBIMQswCQYD
+VQQGEwJVUzEfMB0GA1UEChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEYMBYGA1UE
+AxMPaW5kaXJlY3RDUkwgQ0E1MCACAQsXDTEwMDEwMTA4MzAwMFowDDAKBgNVHRUE
+AwoBAaCCAa0wggGpMB8GA1UdIwQYMBaAFIH3qr1IdVmAsM/fIxid2JNGghazMIIB
+eAYDVR0cAQH/BIIBbDCCAWigggFhoIIBXaR1MHMxCzAJBgNVBAYTAlVTMR8wHQYD
+VQQKExZUZXN0IENlcnRpZmljYXRlcyAyMDExMRgwFgYDVQQLEw9pbmRpcmVjdENS
+TCBDQTUxKTAnBgNVBAMTIGluZGlyZWN0IENSTCBmb3IgaW5kaXJlY3RDUkwgQ0E2
+pHUwczELMAkGA1UEBhMCVVMxHzAdBgNVBAoTFlRlc3QgQ2VydGlmaWNhdGVzIDIw
+MTExGDAWBgNVBAsTD2luZGlyZWN0Q1JMIENBNTEpMCcGA1UEAxMgaW5kaXJlY3Qg
+Q1JMIGZvciBpbmRpcmVjdENSTCBDQTekbTBrMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEYMBYGA1UECxMPaW5kaXJlY3RDUkwg
+Q0E1MSEwHwYDVQQDExhDUkwxIGZvciBpbmRpcmVjdENSTCBDQTWEAf8wCgYDVR0U
+BAMCAQEwDQYJKoZIhvcNAQELBQADggEBAD511WYLf3F/LVZg+HVRImOIHZrYvfUb
+QGXr1qzVVk86bLrZqrPNXo6+dCj/pZJrP35VxvrcapftonBK2FucTiMzWyg5WXI0
+lh3i/7BOlopDezS1m0T/vPhEYJM4ymyhbEY7vjLQWeTWEvcQIW0QGfVRpFzMzkwe
+FcIq6QleFY3hfOWT72oAdZHiweeNPc3/XMPFFkZ/3Tp52Mt1OOq49Xk8HneV/F06
+tZGefG3DrvQbkqBHF3p2qjWrwtNUHlqxJ5uCYf0UKMtwKp3KLTJgphrCOApaPapD
+Dixk3loTgU9P/PO5XAP8s+tTdwjYlYPDan/Uxi1PpwdxUZ/ghuFaW9Q=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitAnyPolicy0CACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitAnyPolicy0CACRL.pem
new file mode 100644
index 0000000000..f6f50228e7
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitAnyPolicy0CACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIBxzCBsAIBATANBgkqhkiG9w0BAQsFADBNMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEdMBsGA1UEAxMUaW5oaWJpdEFueVBv
+bGljeTAgQ0EXDTEwMDEwMTA4MzAwMFoXDTMwMTIzMTA4MzAwMFqgLzAtMB8GA1Ud
+IwQYMBaAFBigoHpq/2qdhYIkzcMmhfi/ijcGMAoGA1UdFAQDAgEBMA0GCSqGSIb3
+DQEBCwUAA4IBAQB+ePMn32XQoMZZJrXrnElaigAgYnqRtRguHjnlJXmaftw3lk++
+PSQPSKUxKtuvbuczbt+jMJhTngohIIfbPfEYrSEe6kolUODSJK4Vdp+ja2jfD9ba
+VrmoHNH5JOLCm4HqaV6Cba3rKH8F+0rlw49q6YTS7otMBaaPgfVPMtketW2okiNK
+YBVOVSdC4XuxuZQZE3rz8u+T9QcOm12eMNJU/pXhFjOaagtqU1xtqEBO3LOsbMym
+uTRGJO8VoWdYNxe/jedXiAMASJlx4BQo0hJDm16wlubz4S0Ac9gHYNrFD6laVDph
+B5eNCGxHDSJkxhNtbW9ztHgQcT18pxOV4mHy
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitAnyPolicy1CACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitAnyPolicy1CACRL.pem
new file mode 100644
index 0000000000..ad1941263c
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitAnyPolicy1CACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIBxzCBsAIBATANBgkqhkiG9w0BAQsFADBNMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEdMBsGA1UEAxMUaW5oaWJpdEFueVBv
+bGljeTEgQ0EXDTEwMDEwMTA4MzAwMFoXDTMwMTIzMTA4MzAwMFqgLzAtMB8GA1Ud
+IwQYMBaAFNimnieXEcOO1Bkh1yC8nO2he/LTMAoGA1UdFAQDAgEBMA0GCSqGSIb3
+DQEBCwUAA4IBAQBM2rRjH0dKX4Ay7xIJONSV1tyQoFaznksb2FKUgatoMTtF/Tbx
+WqANV/RsKCYjHc7wAlyn0LCfxyIvIGSLQTUk3+Dyk0yESy/Cz0R93eCeI2y+MZ7j
+BHz+jCEgOeV4ilL0Y2hTmHOQGBkrFfa/tWA2tAejAZmnozFhgQcfenUMXWc5uU3N
+CMS7n67+hUUE3kQ5qw5L3GtxUytXOdG4CUa96yPcMKJgJ1W4fk7FkAAGt2IM4lDM
++c3gS0+TfewBtGFPfu0kDt+0gLX/SdGGJhBnYCuoAicXhshNmhOjchP3Auo6pZHP
+0uto4/XnOnHKUanX+Cg7D++JqCa1FzShSjUn
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitAnyPolicy1subCA1CRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitAnyPolicy1subCA1CRL.pem
new file mode 100644
index 0000000000..cc49e37c54
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitAnyPolicy1subCA1CRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIByzCBtAIBATANBgkqhkiG9w0BAQsFADBRMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEhMB8GA1UEAxMYaW5oaWJpdEFueVBv
+bGljeTEgc3ViQ0ExFw0xMDAxMDEwODMwMDBaFw0zMDEyMzEwODMwMDBaoC8wLTAf
+BgNVHSMEGDAWgBR0oNVY2StT0iuwzV1xxqG/Q6fIFTAKBgNVHRQEAwIBATANBgkq
+hkiG9w0BAQsFAAOCAQEAcdszKmraUaBp3w473eShNoALBZ1aj31fBfibxZPvtlwG
+BECryi8bmOwXZwKUE8Pr1MeQVtHYgDJhBscq9F8scmBwbmp/kRnBdN9Dgq7fe9Yd
+lke45ULwXOLkX+MdbWcNMX0OILkySpw+zpIDX1HCNeL0QKz+bXwouqgAnpj1IIFL
+yx56Vl/AGQ7S0jkR1emdZJudenGiCKkDqImjORjteSO852f04/83n6iNYDLcDzki
+uxyd+n00PMhqjiG44a5o+rAfOiJBbxv3eDMNQo7mhnTHUZnAq+3bSSQcJqCQBTNC
+zLna0kwa38h1WeQWbekOJGO2kiFd1pTss+mPmvpCBA==
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitAnyPolicy1subCA2CRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitAnyPolicy1subCA2CRL.pem
new file mode 100644
index 0000000000..846500d7af
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitAnyPolicy1subCA2CRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIByzCBtAIBATANBgkqhkiG9w0BAQsFADBRMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEhMB8GA1UEAxMYaW5oaWJpdEFueVBv
+bGljeTEgc3ViQ0EyFw0xMDAxMDEwODMwMDBaFw0zMDEyMzEwODMwMDBaoC8wLTAf
+BgNVHSMEGDAWgBSMBdzffmTbYr7bS1FkjGpm2FyjozAKBgNVHRQEAwIBATANBgkq
+hkiG9w0BAQsFAAOCAQEAsrP3R/bTvBLXn8NE+Qcr0Cr7ETparxT+Grm80Py+H77E
+UR6eT9/tvIJYTNYzpw7zEUB4vYeVqMJCZoPtP26Lkv30BLAkWEQ/kaM9720aG1pH
+sGUCpSwpnZkZeEhUW//IvCb9Ubf/aMAE+NpvLnYHBB2hQdckW8hCrMubc6jfQL2K
+DTEhwuFs8p81I2OXuA//qk9PrQlnI0dsFenmRCbSpZj3CzIjwuPbv4J2JKthNgb9
+xuFT97kzKFYa77JP+Skcicva4o0A94WVkCtcAv8FrHPNNapflzcE6edP5Y5R97SK
+yflBco+Kk8eEzkWleh3wLh04+CEoj9JYMEA+1ICv8w==
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitAnyPolicy1subCAIAP5CRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitAnyPolicy1subCAIAP5CRL.pem
new file mode 100644
index 0000000000..7ebae31c23
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitAnyPolicy1subCAIAP5CRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIBzjCBtwIBATANBgkqhkiG9w0BAQsFADBUMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEkMCIGA1UEAxMbaW5oaWJpdEFueVBv
+bGljeTEgc3ViQ0FJQVA1Fw0xMDAxMDEwODMwMDBaFw0zMDEyMzEwODMwMDBaoC8w
+LTAfBgNVHSMEGDAWgBSJBFR0BmCz9wBuoGGOFfu+UgIGJjAKBgNVHRQEAwIBATAN
+BgkqhkiG9w0BAQsFAAOCAQEAGOaiustpHjlRoLea0ROpIk9v5o+dhhyD281DHP68
+cahcHEEDBsFZ+08KXnho7vLW55E2mnOZpjD+YAw5M1UpuZmKHgHTg14FfZhrZjco
+LjkcUC8hlCOc2ZfIpv23+Sh4dtR/iglXo5KqDiBbii0VPndtZ0M0XgTfLe+3vp1l
+ofMF2I3jkMfQSgyFMihCzguRCgjr2g8TAcX1KxaRyQD5NtelpY3f7juQo7RD3AdF
+hiZdjnCY6qtgQ/9ju/6dkWl2vNOVWQBLJ1aieo2o1UT5PiJXoMlKBbuYZpecyNRa
+bdRFkCoSybqi+qAZ0S39CBZQGuU7Une7xne9cXK+M05J8w==
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitAnyPolicy1subsubCA2CRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitAnyPolicy1subsubCA2CRL.pem
new file mode 100644
index 0000000000..669df318fe
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitAnyPolicy1subsubCA2CRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIBzjCBtwIBATANBgkqhkiG9w0BAQsFADBUMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEkMCIGA1UEAxMbaW5oaWJpdEFueVBv
+bGljeTEgc3Vic3ViQ0EyFw0xMDAxMDEwODMwMDBaFw0zMDEyMzEwODMwMDBaoC8w
+LTAfBgNVHSMEGDAWgBQRfcCcinb5STP3pIFLjjB1lTvoiDAKBgNVHRQEAwIBATAN
+BgkqhkiG9w0BAQsFAAOCAQEArE0ESZ1xjjGSyPpJ2Ru9aEVl6UtjKC1vctLfDCZD
+eLTLipnD0R6vsDscUna4h22okIeP9AEeLEOvpX5Oi0bVNhU0w6O2xP0Tc/Cfuole
+HgH4ZUJcKob6/2DpXLSayUNNL1zY8I/OS+lfsetIqOhp78xLiR0ghyeOagnkH99r
+a9eT9iCCUe/XO/N0gMOjsPkh5qOGC6gg9/XwsECc1BYf3HGzU10IC/C4rrxgrwQx
+YmpA+WDMQpEmDFXxect/9gARsgny4irJ0++2rFI1WdWKFEmMkO/l9B6GRYCjNQJX
+cS0JH8PWhv3DjvUoEeBIFDDJJwquskVZWwP6TbkfEqut+A==
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitAnyPolicy5CACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitAnyPolicy5CACRL.pem
new file mode 100644
index 0000000000..7c053d1c8a
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitAnyPolicy5CACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIBxzCBsAIBATANBgkqhkiG9w0BAQsFADBNMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEdMBsGA1UEAxMUaW5oaWJpdEFueVBv
+bGljeTUgQ0EXDTEwMDEwMTA4MzAwMFoXDTMwMTIzMTA4MzAwMFqgLzAtMB8GA1Ud
+IwQYMBaAFMAmgedp1p188L3VnapTDmX5nMsKMAoGA1UdFAQDAgEBMA0GCSqGSIb3
+DQEBCwUAA4IBAQCXIcfQLBFOcgt5m7Q9laH1FvfjTwZ1s2tC3d3NzBGRJRqo4lFB
+sb3PvwFL96EwopImZofRByNNmtdA1E6FS5dyiaDjOD8u9f02ZnL3PpAkuTTMyA+l
+iGSo7ZEvwYMPjWdtiN0SGLlpoVbTbAcUx4zjHOby1H6TiZcexSrf6ndRtYzR9I/0
+3jzFnqPcSIVQeB/gSrSWMj9j1gYO/UQGf6H8/YXadnYzoh7+C2lbwk27mpb0d8sc
+EkGJPa0iimLbKCmprPG5iHE0y6yUltjSia8fsubGfzNkMaJWsFepd/HMoXWC68hS
+0fI5vvDTIzHqFMAF6cZ8ddHMNbFA4XDD24FW
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitAnyPolicy5subCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitAnyPolicy5subCACRL.pem
new file mode 100644
index 0000000000..5c6e19906c
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitAnyPolicy5subCACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIByjCBswIBATANBgkqhkiG9w0BAQsFADBQMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEgMB4GA1UEAxMXaW5oaWJpdEFueVBv
+bGljeTUgc3ViQ0EXDTEwMDEwMTA4MzAwMFoXDTMwMTIzMTA4MzAwMFqgLzAtMB8G
+A1UdIwQYMBaAFGyZqbYF675wSTZMWJoi6BSIhS/bMAoGA1UdFAQDAgEBMA0GCSqG
+SIb3DQEBCwUAA4IBAQCfcJ8e+tup8Fr+n+Wg9FB/0V86blpBUsRvV6juNBKzuyuz
+b4iY4BZM1flYt9OF043DVHi7xN7BcQ7YGBal/day/v9GisD3Ia8qYdtyRCb0y70V
+6jiVJ1FVolO5New51aMcNAzmdCvmqvsj2KBm5y1zJmBwF+MeKrOk23kL4jOpX06y
+Yjfbs1xCbub01wBmE/t+rL/FZdFamC+uh3XfBUw6vCrDNvdtN31n522EV8I3ifru
+ByIJ7RgtjgeIDfCOND3TZ3i+OTEcn4XrMW+Y3BYiRKhLEpxrHkxNdgOK1ouC0HuI
+QlE/DkaDnw9rj6NnI9wlZTMCbmsSkn28VrkArw7y
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitAnyPolicy5subsubCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitAnyPolicy5subsubCACRL.pem
new file mode 100644
index 0000000000..793cf1291a
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitAnyPolicy5subsubCACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIBzTCBtgIBATANBgkqhkiG9w0BAQsFADBTMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEjMCEGA1UEAxMaaW5oaWJpdEFueVBv
+bGljeTUgc3Vic3ViQ0EXDTEwMDEwMTA4MzAwMFoXDTMwMTIzMTA4MzAwMFqgLzAt
+MB8GA1UdIwQYMBaAFDHhP/xiboBlzal5EAArbola6AXDMAoGA1UdFAQDAgEBMA0G
+CSqGSIb3DQEBCwUAA4IBAQBBNQhxoz5IzMcomKNxeSnmWdgwCCbynAR5YVIt4vqS
+zbN+H6DPt7UbaqhpgJTAedphGYOiVm+LYOehKXMYp6ZF2iHFC7yZ4/tbozh3hqY5
+5G1Zf2c2cXsCI9cI+uXB3jk0h1RgxaBRoAST6PS8HQJvUXM0T3QyApj2ELLjlvHx
++OrJ7XfsmOzliH8pfVdnKGzp2piSiJhdiiV/egX23AXvvQq5k3Tb0oUWo54BAso3
+2eXyypSbPTbxQXVqq4bjsVs/F7h0xgFRlwK4YPbmcpZqM1TXl0f8l/KqnFV+ZOmL
+aXghz7oTvQoFmJodMyzgWtvR4LRWnVj2v53K6AO/Jvrx
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitPolicyMapping0CACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitPolicyMapping0CACRL.pem
new file mode 100644
index 0000000000..c709a56df7
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitPolicyMapping0CACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIByzCBtAIBATANBgkqhkiG9w0BAQsFADBRMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEhMB8GA1UEAxMYaW5oaWJpdFBvbGlj
+eU1hcHBpbmcwIENBFw0xMDAxMDEwODMwMDBaFw0zMDEyMzEwODMwMDBaoC8wLTAf
+BgNVHSMEGDAWgBRYNyYHkYRgrO72QD6lK/z/lx2d2zAKBgNVHRQEAwIBATANBgkq
+hkiG9w0BAQsFAAOCAQEAvS17Fvl0x0lQNKsr9O7mk57ThTYOMkhSLLVLYCrox6vO
+B/wLW2wLJKrJ//n/VFqEGncKHNgwyF0tiBNhm9qEGp+LSbKwN/NT/ZffUmuer74d
+iUIQr7fvQYATGGY8f8a3GlQ451Wnw4LTOpfTfIOdH611ql5bQg9zsZsoamXa0WZ+
+Q9WLA9rT2AAQDWE+6jhD6PfhMZlfrOwfQ0oZJX+EDkiQ0N1BAhURxMxzflrWeedF
+nRHOyZrlIzqZ9rqaIaTcPkn3Q90fxREys2AgX59Fyp76iT3lsYpPWUytnt7e0NpY
+sZTGdW5W8/vh1jYAMhuBJNzTfk0Ilf71Le+qxQVFQA==
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitPolicyMapping0subCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitPolicyMapping0subCACRL.pem
new file mode 100644
index 0000000000..50f92f4a14
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitPolicyMapping0subCACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIBzjCBtwIBATANBgkqhkiG9w0BAQsFADBUMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEkMCIGA1UEAxMbaW5oaWJpdFBvbGlj
+eU1hcHBpbmcwIHN1YkNBFw0xMDAxMDEwODMwMDBaFw0zMDEyMzEwODMwMDBaoC8w
+LTAfBgNVHSMEGDAWgBT/tHNiUo1cljpakK4avLg8eYFjHjAKBgNVHRQEAwIBATAN
+BgkqhkiG9w0BAQsFAAOCAQEArr+kgBiRhrD9A0eGhC60oEuT+oe4x7jNWNet8W+g
+U6eESV36XCGgh37RfZtL6k40uiXm+WNM5Tir3iLaoUVR675FIWTn8deelq410jkk
+RTbEz8ikqYH/MGilEhLEfaYteQu8kqGETlDmFbmcjriRWulMGVOc7yKMIrKGRKWY
+oZXN+MB4zjkepwlJtef6ZtwNa+QkYENfLSu0fNu+MU+aqnwyLqsqNqV6paPViltB
+c47TqfXNtm18NtKDYjXdPgiLfmk0qZ+WSQK9mz2v2NSsHq7G8IeOYJ5BuaXDs3df
+fyZrzQxOFaaRNLor0m5T8HKIM4HtHVuLGioqQRGLyX5zUA==
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitPolicyMapping1P12CACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitPolicyMapping1P12CACRL.pem
new file mode 100644
index 0000000000..ec32a575b2
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitPolicyMapping1P12CACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIBzzCBuAIBATANBgkqhkiG9w0BAQsFADBVMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTElMCMGA1UEAxMcaW5oaWJpdFBvbGlj
+eU1hcHBpbmcxIFAxMiBDQRcNMTAwMTAxMDgzMDAwWhcNMzAxMjMxMDgzMDAwWqAv
+MC0wHwYDVR0jBBgwFoAUTWd+jd05Ga/oJt4OATR4sXUQ2qQwCgYDVR0UBAMCAQEw
+DQYJKoZIhvcNAQELBQADggEBAOTAmNbgjGjQ7urj0qwWnSMCHeyF9To1md3H2IWh
+CNZaepq/e7ALmIDr7viy2FfyNnmBi0SWz4b/3ddI+NhylyuhBX7b+hHIBcAbyuym
+ZprJeuNjk53T2KoUGrOUnRGAoUKY8I1mNrXb2oQ5+keKUevF1G4OYsjoH2i/YU/p
+xf9xMwd4fKjRZuIS62MMJBXOQgWPXu3q1K2U12M1EHJ+Uzm0vgYf54vL35Zczo1T
+42ue5Wkt2tFgiQlN5betiQhuXPSNoEcX54l3oRfo/QS2cFKzaQEYFrS4Khdy590X
+16eGQeXouMpeCARjGbBeUoVWa/qFm+40nOXtBftQOtUuGJc=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitPolicyMapping1P12subCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitPolicyMapping1P12subCACRL.pem
new file mode 100644
index 0000000000..8313d770c7
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitPolicyMapping1P12subCACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIB0jCBuwIBATANBgkqhkiG9w0BAQsFADBYMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEoMCYGA1UEAxMfaW5oaWJpdFBvbGlj
+eU1hcHBpbmcxIFAxMiBzdWJDQRcNMTAwMTAxMDgzMDAwWhcNMzAxMjMxMDgzMDAw
+WqAvMC0wHwYDVR0jBBgwFoAUqiaUHWQPfgW8XWCNB1f8cJVmbOcwCgYDVR0UBAMC
+AQEwDQYJKoZIhvcNAQELBQADggEBAGhzR1y9MeY8/io6ssFZCHWXI8r8OoPogUgA
++Zmv2YJAGwg2FY/ID9nYmyntsT1bsAfzPubQJJGsg6+izEwcjNGaq9S8W2doUJ/5
+LQVpPBYydzpRm9CkW+YlpxTdYxlXLJOlZOxQbAob7dUdViUgbu3bVQq0oMlgjz3z
+uMwHv4yp4xOD45CQbwALHVAoVkayZ6ESltquwQBSOaRATkbTRl7BdNcssrh9TTMS
+YYhcnWHQd4e1KWdJLb8ZSgYpAAoLLV+4QSR3B64O4vy95xwcG71puEs/RLDgW7MY
+pEnbOsL1TDq7ZjYfAHyxFXZKe7XEFknnu7A5cNjoSr65szamIn0=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitPolicyMapping1P12subCAIPM5CRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitPolicyMapping1P12subCAIPM5CRL.pem
new file mode 100644
index 0000000000..3d86355fa7
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitPolicyMapping1P12subCAIPM5CRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIB1jCBvwIBATANBgkqhkiG9w0BAQsFADBcMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEsMCoGA1UEAxMjaW5oaWJpdFBvbGlj
+eU1hcHBpbmcxIFAxMiBzdWJDQUlQTTUXDTEwMDEwMTA4MzAwMFoXDTMwMTIzMTA4
+MzAwMFqgLzAtMB8GA1UdIwQYMBaAFB0Ez3YnB48iO8L0gi7u5t0TgHtTMAoGA1Ud
+FAQDAgEBMA0GCSqGSIb3DQEBCwUAA4IBAQCT97ZZ9/3xeVKS7ylodjmyfxLqZ+7V
+iezI4cQFmH7cWThgfMefgj1664LrHOuiY/4bhvk1LHQj1c5pFbUYLc5hprEiv3dn
+OJejWo7PxTY47hkXIntlElUyYb/mxUPxiklQ7FEbE7RFSOTi5MZNrHgd+JITUa6x
+jPfDI0z/GO/Jo4RPZos2Iu1hVLN++sU8iMVMe7/sIoOrNw/lwudUfo+cZEKWi7xv
++1ICLtnK0XoPBTxNzb/i8cOZrjggiKole9mFEAZrs5P5dfytRTVFje+d4Aj6lYr6
+4smUCABSDWhLcyI2OLIt5taw1BigwfOsGvjMsRVgRuF/3U3LRHf+8o1M
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitPolicyMapping1P12subsubCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitPolicyMapping1P12subsubCACRL.pem
new file mode 100644
index 0000000000..4c8450f3b7
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitPolicyMapping1P12subsubCACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIB1TCBvgIBATANBgkqhkiG9w0BAQsFADBbMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTErMCkGA1UEAxMiaW5oaWJpdFBvbGlj
+eU1hcHBpbmcxIFAxMiBzdWJzdWJDQRcNMTAwMTAxMDgzMDAwWhcNMzAxMjMxMDgz
+MDAwWqAvMC0wHwYDVR0jBBgwFoAU14BcE4uOQXa6CrVzceijQIB0DtEwCgYDVR0U
+BAMCAQEwDQYJKoZIhvcNAQELBQADggEBAB8d26fH9sOi6DPVWkJSNU1UltmljVsq
+Z9FBaQQxT+XSQDuAxW3VxeL55+/62jWO/O8/xvfw4egnAoTjtDfOrHFCEk7AlIgX
+vdGeXe7l4sHgbaq6l/z4EDsQU7SoukhtrjUVUTz7ksLjJwraVbBc8Kun/Umpj2sc
+ZEFrn/PMyibfXkeJ5aWTosLuEyKBag/Ln7hhl4nN6LDnafj+sxvhIlPGKKE1B66S
+h+wbhxZ6QvqOGfdYlcjRwXpdRPiF2/DJApIjF3eV7CkMM8tMo0myD+xQWwjPkNPw
+29n2FmWxfSmGBhbRkJ/nPGid50jCG/1UOrtN9+pQpRS3Tpky/zFtkvo=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitPolicyMapping1P12subsubCAIPM5CRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitPolicyMapping1P12subsubCAIPM5CRL.pem
new file mode 100644
index 0000000000..2ed760a1e5
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitPolicyMapping1P12subsubCAIPM5CRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIB2TCBwgIBATANBgkqhkiG9w0BAQsFADBfMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEvMC0GA1UEAxMmaW5oaWJpdFBvbGlj
+eU1hcHBpbmcxIFAxMiBzdWJzdWJDQUlQTTUXDTEwMDEwMTA4MzAwMFoXDTMwMTIz
+MTA4MzAwMFqgLzAtMB8GA1UdIwQYMBaAFBKHGzVn8LyhoDa6FagpGe0am1twMAoG
+A1UdFAQDAgEBMA0GCSqGSIb3DQEBCwUAA4IBAQBRQeh+mh86ONreRF9oNGE+z0lr
+y1ZYBiri/f8kk7y1GbwNB6LG+M8Fve7pe8pvtawSMfGnEbaHqdOf1jzSeOQXvSBb
+htuZ+DO9kOVDRyysBLNDWTSgA7BC/MI0JwrHnUMq6twutmQ0aPe0hHZu3DdydeRt
+ncUQ6qa+dNiZ2RJCpcXwKcGPLJnafDpICui8VPRJKgj4bcdtpFICoCmnB3iqbLKe
+9s2XyNtHxfJPOW4HSASB82rWcTzNA6Px0FwXm+8TKpkopS1dlZ6cd/nYhJCj1i26
+wxRDAx634CAzhWoGaYtrE2P5epcOEnh4G49ybfPmua8RWxTyi5CvhBhtrtXp
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitPolicyMapping1P1CACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitPolicyMapping1P1CACRL.pem
new file mode 100644
index 0000000000..f231fe784a
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitPolicyMapping1P1CACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIBzjCBtwIBATANBgkqhkiG9w0BAQsFADBUMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEkMCIGA1UEAxMbaW5oaWJpdFBvbGlj
+eU1hcHBpbmcxIFAxIENBFw0xMDAxMDEwODMwMDBaFw0zMDEyMzEwODMwMDBaoC8w
+LTAfBgNVHSMEGDAWgBS+tp29KX8aodmL4aSAaIMorUrwATAKBgNVHRQEAwIBATAN
+BgkqhkiG9w0BAQsFAAOCAQEAUv+vJlKK7TKlyO/T/yrf26BwpHa6dJHReqjLh6e/
+iXSXPSDXvuDrj+uNAIL4o8s8X5y7h5+ivYEDyZi2lnrCsj75+IuJIBfeHnfdeCo5
+rsa2Hml6bhJX0OuZRkMlg1qDMYlN3EEqcpobXWd/TfL3RB+vw/V7WwC/F5vx5UkT
+mH0jpI0glDOOIaQIDhgXerdEMuZ4FpSdAiUtj6icPYmbaGMY+We8X07ztyv2RQsK
+frTxkyAHRM8fC79LlQzzCvTPcV9wkwKteKMY6ZzWsOfp1B9DAyK6wq2Lp+TqbJqh
+pyL/lPhKASzGVegUqAOczdo0rvNDCVIPu0gCk5PgjcpieA==
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitPolicyMapping1P1subCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitPolicyMapping1P1subCACRL.pem
new file mode 100644
index 0000000000..3730e7c738
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitPolicyMapping1P1subCACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIB0TCBugIBATANBgkqhkiG9w0BAQsFADBXMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEnMCUGA1UEAxMeaW5oaWJpdFBvbGlj
+eU1hcHBpbmcxIFAxIHN1YkNBFw0xMDAxMDEwODMwMDBaFw0zMDEyMzEwODMwMDBa
+oC8wLTAfBgNVHSMEGDAWgBTzzQc/gzDTxwJi2ubKbAGlsbaAyzAKBgNVHRQEAwIB
+ATANBgkqhkiG9w0BAQsFAAOCAQEAAuGpcOqakKHpoO5Y+E7QAQnB1fmAm4smN7eu
+RNXIdrZaVd24C1BbFzYAnjdewBVYT4adCdnslaNe4tqpq3Chh/YtmUe152/GTiXF
+MVbo5FRZhjSvK8VTVEzvSZ21b3oiqIv5d4P47XmhVfo0GtTv7Rg+x+ay8dGUgni1
+D153LEBh9beouesqqxn/8uFfxYpqmxFrS8vLdL9kxpeaFX32bM0eSOhA3lg4oCfm
+j28x6yL57beJMHjFZMN5vAXTZHZKeJS1QXak2Dt21G3VKPyq3v7iY0GDgLqQGsIp
+vPsv+nuvwdTjcQPwsMIQjZBrq2woAYvlF5hx8ofqoLNW6/oV1g==
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitPolicyMapping1P1subsubCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitPolicyMapping1P1subsubCACRL.pem
new file mode 100644
index 0000000000..6bb1b2a960
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitPolicyMapping1P1subsubCACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIB1DCBvQIBATANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEqMCgGA1UEAxMhaW5oaWJpdFBvbGlj
+eU1hcHBpbmcxIFAxIHN1YnN1YkNBFw0xMDAxMDEwODMwMDBaFw0zMDEyMzEwODMw
+MDBaoC8wLTAfBgNVHSMEGDAWgBQ+RXSii9LxVoxGAWZ4cCTGIsEDnjAKBgNVHRQE
+AwIBATANBgkqhkiG9w0BAQsFAAOCAQEAdMziQBhDzdFRF40rOtd55OD5MPOXWPRb
+5i6KUQcwxrHZ6xZibEhM/eHGtHW89pL4Jjzrwbp+SXz/OzJ6yU9Cf3PQsP3a7l87
+J/EY+KL++6g5V58wlaXZYOvO8v5WTSFGP6NUvg8Nq2SChq3KqVvKxp/MrFSckP74
+gd2jepabJyTeXajSMManT+wxxTKG8WKMQtiQdXclZmEgaVYgxKVgIJudF46yLEh6
+gVwhKAR8xdIBr/mztrcM+aQKvdxtz3aHuKNwm+lo0tSjxB7UojZvIMdd7fuc0V1E
+PEVJDd2+Eej5s071zlgMeaahFbVeBNgaQy5rURdHxtyMZukFdCW5aQ==
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitPolicyMapping5CACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitPolicyMapping5CACRL.pem
new file mode 100644
index 0000000000..68d33d924f
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitPolicyMapping5CACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIByzCBtAIBATANBgkqhkiG9w0BAQsFADBRMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEhMB8GA1UEAxMYaW5oaWJpdFBvbGlj
+eU1hcHBpbmc1IENBFw0xMDAxMDEwODMwMDBaFw0zMDEyMzEwODMwMDBaoC8wLTAf
+BgNVHSMEGDAWgBTbgAe5YizFw/3zQ+JmUSW72/QczTAKBgNVHRQEAwIBATANBgkq
+hkiG9w0BAQsFAAOCAQEANZI64pJVPbGyUy9hrPD2bO0EkxlY3aJHxDko6PaPdQ86
+dFjmX8tTCD0vmm8Hox4bI8fZHbjMDqhWxLU8NtjNJLSZX5IY7SJaaacDGaRd6laP
+05hB5MTLcEKBxH5FIetoK93e8mf+rMVRG4kCmn/UX4notzBcX0x0qKoWd2v9v2QH
+Gx7Gr6Ouae8wubYiu7aR9fCYRIcllEZVFRQ9f0bB5o4NANAUM4uq7ZrrqSRauqN/
+iNxCEeKSVnItuZnAZXpazNmXwSCdWPiRSl/77tThqoBhGKsgOvNbwJaTKJIuJ8HX
+5HgiQiSaz6Y5GySbA6rFemmW6X6o49/CiJr8cI+fKA==
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitPolicyMapping5subCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitPolicyMapping5subCACRL.pem
new file mode 100644
index 0000000000..7925d0d1f2
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitPolicyMapping5subCACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIBzjCBtwIBATANBgkqhkiG9w0BAQsFADBUMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEkMCIGA1UEAxMbaW5oaWJpdFBvbGlj
+eU1hcHBpbmc1IHN1YkNBFw0xMDAxMDEwODMwMDBaFw0zMDEyMzEwODMwMDBaoC8w
+LTAfBgNVHSMEGDAWgBTY7G2+t2/KE2PKJ8ycW6JpNrbyaDAKBgNVHRQEAwIBATAN
+BgkqhkiG9w0BAQsFAAOCAQEAOYCNoa/mt1m/IQNy9kCVeCItblOzMyiCcng/IMCd
+RQ9q7kAa/nPj/zINqGAM4GUY4yB5wHm5zVKPL/AxC4adae2Xg5o2XqJ9rRI9bSgJ
+s48iHWbFp8mU2H5Wbn/hVKSWWCi6XzP1N9QwsFJa/5nsgRDzp73VHarqIPUBahtA
+mYLPo0bIg77oHPsXYmEI+aufY88+IMtVr4iFIW3ifEQu/XbmfwNjQLopIHBn3E0+
+gyD5XDJeF5f5C3FG9vQ/3DSgrdN2DLzWHr3/dWNIkRWhVxqimffMjYUkuxi4bKQ0
++30q3zclNs0lOr240CPBkg6Y39n0Zo7ihxBoS5INvbTi4g==
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitPolicyMapping5subsubCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitPolicyMapping5subsubCACRL.pem
new file mode 100644
index 0000000000..2fb83ef408
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitPolicyMapping5subsubCACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIB0TCBugIBATANBgkqhkiG9w0BAQsFADBXMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEnMCUGA1UEAxMeaW5oaWJpdFBvbGlj
+eU1hcHBpbmc1IHN1YnN1YkNBFw0xMDAxMDEwODMwMDBaFw0zMDEyMzEwODMwMDBa
+oC8wLTAfBgNVHSMEGDAWgBQ1p9ThS3ROVahxtEJ/Mv4EGskBuDAKBgNVHRQEAwIB
+ATANBgkqhkiG9w0BAQsFAAOCAQEAbayhlH01G0sqNxiiUyC8qPsdhUF3mHVEhoqz
+2SOwTTDzqCTLyqftFnZPAJ9fbG/FN3Denqr2ouh1Rl2mPEG3KmBA/w6gRrTzxmS0
+pC8UsBWdRvRebGZ29WJEjgslUz/r9BbSlwabF1dsAssvmv9xPxFBql4uNrLOEfap
+M9Ldo4y1eiBWd5xNTYp2zu+oBOAgywJggc7xepoUhwcPCTSrcAqmsQNLBoMYcf84
+nzm8rJ89cTqyPvpLxKjjgLdquVevhCcIG9HSCmAbb1z8SKhWaaBjRSoLmAo7845M
+VoDabAd0zNq9DEwb9WHB0zZldJ+u4LJtAAEpdl1nc2KqMADUnA==
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitPolicyMapping5subsubsubCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitPolicyMapping5subsubsubCACRL.pem
new file mode 100644
index 0000000000..ade3f19453
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/inhibitPolicyMapping5subsubsubCACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIB1DCBvQIBATANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEqMCgGA1UEAxMhaW5oaWJpdFBvbGlj
+eU1hcHBpbmc1IHN1YnN1YnN1YkNBFw0xMDAxMDEwODMwMDBaFw0zMDEyMzEwODMw
+MDBaoC8wLTAfBgNVHSMEGDAWgBSuY8vX4sNx4/TObvw19JvSTT7cFzAKBgNVHRQE
+AwIBATANBgkqhkiG9w0BAQsFAAOCAQEAj0qjzBDkME3FgINy8gUWHlUGBpgreUMF
+1IqXG918JUeFy1vfkDdhwmg9ln2GjMquqb6J26L5mgH0EN4qUsfRZtcLHTEnMXt+
+MSKI8xOWJrid//uFlitbPPNdppvovBNSAZroq+uZLd+jDxKfiPOVlrIkbbTQvv44
+YAGrkkdsmrxSxWwrtXdLIURmW/D9J5uRkRqpMVvikD5aMLGQEangg5MO7ZLsGUBL
++mdA7YDq+RMG5F0TVoKOOlhOZhAxcgs+RMCZdJKWxJd6/gu5opHeEuOy5KShVzUW
+ABXNyqZXT4tjAjYW0Z26urTv8FeBoRoJC2Kyozm7+IR4m/H4/GaJMQ==
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/keyUsageCriticalcRLSignFalseCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/keyUsageCriticalcRLSignFalseCACRL.pem
new file mode 100644
index 0000000000..4993db248d
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/keyUsageCriticalcRLSignFalseCACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIB1TCBvgIBATANBgkqhkiG9w0BAQsFADBbMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTErMCkGA1UEAxMia2V5VXNhZ2UgQ3Jp
+dGljYWwgY1JMU2lnbiBGYWxzZSBDQRcNMTAwMTAxMDgzMDAwWhcNMzAxMjMxMDgz
+MDAwWqAvMC0wHwYDVR0jBBgwFoAUwspp9bSvEy30nPIRVcsqMLPSWkkwCgYDVR0U
+BAMCAQEwDQYJKoZIhvcNAQELBQADggEBAJk/UxyWvcm6kbidRgW52BKoSxXcHpfW
+896BFKl4iGUzmYA8GanMvM9EkEoeqjrHdTABcPE1Mv0lREGRYZv9H+gD6iAnKctc
+8G74C8eYkvcdocWAixuvDMyZ+D+TxSCOo04ppe+HraHgEQRy2sgobIYdqEY6XcyS
+X0areuyMQPjqbTdOMFsTrvtSew034Q0zSoKsPZhxfMQ6zcx7JKqna8HzCO9LCZag
+r5+47Bk470ve2Xy5FrPk6sWJQal0/YHOAKKnnXoXitqst2A3cYiWgRAqpDMz2yMH
+zdtJKbBsIieZGgQiedwXDs7aKWzN5o5sh0ALyj17bbHyMrs7gvyiGvA=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/keyUsageCriticalkeyCertSignFalseCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/keyUsageCriticalkeyCertSignFalseCACRL.pem
new file mode 100644
index 0000000000..8a9a50b563
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/keyUsageCriticalkeyCertSignFalseCACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIB2TCBwgIBATANBgkqhkiG9w0BAQsFADBfMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEvMC0GA1UEAxMma2V5VXNhZ2UgQ3Jp
+dGljYWwga2V5Q2VydFNpZ24gRmFsc2UgQ0EXDTEwMDEwMTA4MzAwMFoXDTMwMTIz
+MTA4MzAwMFqgLzAtMB8GA1UdIwQYMBaAFDRVC2f8HLHcwnIKFPBj6dSb8GP5MAoG
+A1UdFAQDAgEBMA0GCSqGSIb3DQEBCwUAA4IBAQB3bfisM1UBugxKYYdxZkYgpRvs
+rh8UEjPVD8z0t29+yeyiPm4bFmTZI64VNvQlcl/Jj0YdcqkWGEKdH7RS79lSQhBs
+piAcYQcdS9WzaZ1tFNG/Fvxo+MkksrQ+Z95rb0vvB+7YuTIVWqbHcJamCgZ4h+Pl
+XTUQgHedNzpPKisiLF9y32JbDJ/nnrec9wEpMn7z2Yhb2vDWhdtQoYMTk4LrMOSk
+74Cqfpuy6JEaymDfdmrIIx4JZOfmo9mQcgeN2WoV/4YYaGGL3T3Sk8FqUaTTdM9Z
+iRiqzBSTOfl4VF4e3S1ZTDv5JH0U/HrJexLhgdi3xEpE4EQIZvXVsZX6I8rn
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/keyUsageNotCriticalCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/keyUsageNotCriticalCACRL.pem
new file mode 100644
index 0000000000..844a745e39
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/keyUsageNotCriticalCACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIByzCBtAIBATANBgkqhkiG9w0BAQsFADBRMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEhMB8GA1UEAxMYa2V5VXNhZ2UgTm90
+IENyaXRpY2FsIENBFw0xMDAxMDEwODMwMDBaFw0zMDEyMzEwODMwMDBaoC8wLTAf
+BgNVHSMEGDAWgBTBkBFK2bQrxXB+zow7Yljlu5crczAKBgNVHRQEAwIBATANBgkq
+hkiG9w0BAQsFAAOCAQEAgK5IxkJ3u7pRpg/BKAlwCJPgj4SrjTrLeu875hsUvEfv
+mMzLN50lGQpwuuH2oaQ15Da+PGpzng6Ao5MhMt3vTqU+Yp5VQEnBIsGED0lTJDHj
+pDyQpS2d4nhdYyPrHQHJWwLoGaTBPglYqc/vASXq862RiNKywdVX4QFj9IXXUrwI
+wQ7d5uKirZUEkInjJCdVH8ryJfo9fLRD4kb3OkbO62e/HHSJO2DstQadNweLoLCM
+hhJlw5KkDXIBfQvq5pPrI76dHY9VatsnqKRkt1CUE4MVk0fOIqci2Nz77LdTsFre
+Pfe0WNoqSJpoGv4SfIr7v8Uw83bmO3Yy/enh3p9J9g==
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/keyUsageNotCriticalcRLSignFalseCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/keyUsageNotCriticalcRLSignFalseCACRL.pem
new file mode 100644
index 0000000000..e0fd7bfd8f
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/keyUsageNotCriticalcRLSignFalseCACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIB2TCBwgIBATANBgkqhkiG9w0BAQsFADBfMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEvMC0GA1UEAxMma2V5VXNhZ2UgTm90
+IENyaXRpY2FsIGNSTFNpZ24gRmFsc2UgQ0EXDTEwMDEwMTA4MzAwMFoXDTMwMTIz
+MTA4MzAwMFqgLzAtMB8GA1UdIwQYMBaAFPl+UqB5ZgQIRAhleRAO3ZDmQ/HYMAoG
+A1UdFAQDAgEBMA0GCSqGSIb3DQEBCwUAA4IBAQCa3FLqxtdS2NeSwWMvuticdJwe
+M+xS0ZRf30G+WsPtmoRnb753WkhlJZZcglilAExrCN9BIMSEpO/UgyXpjDj7GUri
+F9QHfTdIT8QGKcZMbx/nq29ge/fdjOIkhXnBktQy5BfB8lGkYwNs20NuFpL3FaUx
+IvruMRR2AyivlndK+xcz6s2j2v1WYXlvZlFNAAHVmXR8+67zbU7t0WIN7VlkTyTa
+0mxanuASRP5FmlT+Czr6N6OERlBTp1wnvlq9BHeTGDXQp1oXu8B8sTsQHTMbuGWH
+FqGDZpZJL1BsCMuXkBk40b6b57/C5CvIy7SfX9xnvKmJs81C6910qBITMcZI
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/keyUsageNotCriticalkeyCertSignFalseCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/keyUsageNotCriticalkeyCertSignFalseCACRL.pem
new file mode 100644
index 0000000000..d7877a9b68
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/keyUsageNotCriticalkeyCertSignFalseCACRL.pem
@@ -0,0 +1,13 @@
+-----BEGIN X509 CRL-----
+MIIB3TCBxgIBATANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEzMDEGA1UEAxMqa2V5VXNhZ2UgTm90
+IENyaXRpY2FsIGtleUNlcnRTaWduIEZhbHNlIENBFw0xMDAxMDEwODMwMDBaFw0z
+MDEyMzEwODMwMDBaoC8wLTAfBgNVHSMEGDAWgBSyJdIoMNBVaG5MtcJI88qbFfJA
+RTAKBgNVHRQEAwIBATANBgkqhkiG9w0BAQsFAAOCAQEAtIeKFZV9BB4OBq63soXU
+rgYDlsozbj5cYoU86vXpmHddbTowwaGtFlqbfg9deTqT6r9IpdIClfZtJVT3s3Lm
+PFb/areGyQblwYuOD6Mldtdty0FH5A9uMMMCEk1iZD0Z8WApxo1bomtY/EI+ZHAF
+N9+xTE7DZtHIOSZ0nyz64fHMPDYQPDGtkK2QQ6nFNX+ljnPOCRBfwwAsndlumWRP
+SlthF8gU3A8008ytHdpkNEsUhf81ntUy4o2pftmjQOnMJT0C97CXykxZBc8f49A9
+QKsI6ql/dt1XO9hNmVp7qamXA7+yhBwfWAKnhXRQn5D64/ZqVxE9lYL7PvJrr1tM
++w==
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsDN1CACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsDN1CACRL.pem
new file mode 100644
index 0000000000..c999bf8b23
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsDN1CACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIByTCBsgIBATANBgkqhkiG9w0BAQsFADBPMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEfMB0GA1UEAxMWbmFtZUNvbnN0cmFp
+bnRzIEROMSBDQRcNMTAwMTAxMDgzMDAwWhcNMzAxMjMxMDgzMDAwWqAvMC0wHwYD
+VR0jBBgwFoAUQXhCRs1OqILn4Tnf96kWwAr874YwCgYDVR0UBAMCAQEwDQYJKoZI
+hvcNAQELBQADggEBAFtcX7FztYXgApU6J1ska8lB9xinzLv+LkatWBtqZuRZ8yYU
+LLgGDlzgHX8d51lkdXrlTe3iW6aNmKTFznTRqxPGLNA8VikBmBhNLYGgk0u3HTQD
+hZzWtyLOa4FB26lrREAgvEC7P5TB0Xd1p72E4IwKf1ZV7KOiTpw7EmHLsSWYGcDr
+96mwtkUK6g//vxJnIa+k7C9jjQboCmOyH0HSluB0KQYqislhXHO3BSBxiXVhHec5
+3ToVLhmo/v+3fgl8wkEO2RH9MRcx29+JiRrqLr5xYZy3sKKBX/7e5jT0PQsKFOqI
+CKt6oFNRwz2qzEcbHFMyoDR2+5R8zA8OHCDYf54=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsDN1subCA1CRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsDN1subCA1CRL.pem
new file mode 100644
index 0000000000..3884626320
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsDN1subCA1CRL.pem
@@ -0,0 +1,13 @@
+-----BEGIN X509 CRL-----
+MIIB6TCB0gIBATANBgkqhkiG9w0BAQsFADBvMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEaMBgGA1UECxMRcGVybWl0dGVkU3Vi
+dHJlZTExIzAhBgNVBAMTGm5hbWVDb25zdHJhaW50cyBETjEgc3ViQ0ExFw0xMDAx
+MDEwODMwMDBaFw0zMDEyMzEwODMwMDBaoC8wLTAfBgNVHSMEGDAWgBThOA4UGBRD
+XM7nS2LHGsGS9maC6jAKBgNVHRQEAwIBATANBgkqhkiG9w0BAQsFAAOCAQEAL1Mn
+Ok+7QkFamOzoQfdBznGodtPDmg30Cgk3VHY7mAwg0VzRhhTaUHjMrmZzpyKuNNU5
+of9K/maIKludVfLuV9NPmt4pTZWW8l02umkRg/vbAIfPGWpUhev5X3V6f+Bn5bbg
+UQvEiuFQvJzutxukSCkNzijr698Qa5qEu1HzILhO1gQxPhufrfjHqQ5qxAnJiVkm
+uOFSP/OBfuuDg7HS8kCAkzfHrtweRwrrtGf6AYzVZO/+pc7OMOyUW+kh0oXHY60n
+snh7NPi/PNGb4RANJBcQhNKHMVY23eCcwylCnfp7FfuNLSAj55aRvxvG3Pn+WQ+t
+UqzAs79/UBMKcj1IWg==
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsDN1subCA2CRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsDN1subCA2CRL.pem
new file mode 100644
index 0000000000..a39dd31a35
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsDN1subCA2CRL.pem
@@ -0,0 +1,13 @@
+-----BEGIN X509 CRL-----
+MIIB6TCB0gIBATANBgkqhkiG9w0BAQsFADBvMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEaMBgGA1UECxMRcGVybWl0dGVkU3Vi
+dHJlZTExIzAhBgNVBAMTGm5hbWVDb25zdHJhaW50cyBETjEgc3ViQ0EyFw0xMDAx
+MDEwODMwMDBaFw0zMDEyMzEwODMwMDBaoC8wLTAfBgNVHSMEGDAWgBSiL1iDW0yV
+l7fu9oe0lw7gf+CXFTAKBgNVHRQEAwIBATANBgkqhkiG9w0BAQsFAAOCAQEAyDcs
+pkncHaUX9Ebs68OGd6Vbz7PAjZ/cnQXN6ZzCf1HVx8vtldilw0xP7L6LA1z4/sKe
+5AglNKS18TrC9jvMcs5MPI8g9jNPpmzn4tVGJiISlft8SqKPL9dvgUCODtIj+Vqv
+x5ELHuMRwAsj7vXY6FrgTwM4eTwEbRhYCxM1r05VPj8t/fl5XoNR/4vaCmDYR9uf
+JscXoSlBsXJkPrkxHnrreTiSNtP1iAiX6CXOBctSfPzhrZknzWq76UYq4LnQrqNo
+gyZ1pc46+f7RqxgGOgn/Nb6AqBy+N2vpYXabMKmNpIBmpOGq/qonbtPF/zJbj+fc
+Bafk7fSWIrCNZg0pFw==
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsDN1subCA3CRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsDN1subCA3CRL.pem
new file mode 100644
index 0000000000..7dc7ee7fd4
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsDN1subCA3CRL.pem
@@ -0,0 +1,13 @@
+-----BEGIN X509 CRL-----
+MIIB6TCB0gIBATANBgkqhkiG9w0BAQsFADBvMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEaMBgGA1UECxMRcGVybWl0dGVkU3Vi
+dHJlZTExIzAhBgNVBAMTGm5hbWVDb25zdHJhaW50cyBETjEgc3ViQ0EzFw0xMDAx
+MDEwODMwMDBaFw0zMDEyMzEwODMwMDBaoC8wLTAfBgNVHSMEGDAWgBQnSeQE2UX6
+bJiUbPztDcMkUm1VRDAKBgNVHRQEAwIBATANBgkqhkiG9w0BAQsFAAOCAQEAuEeu
+G7nwSvVr9BxS9bMPB12xdegiBpg5U2K0vTD1JSSOR+RQ2LvwpJlkNy2oFt8+sbAi
+oTCOgifKXzi0tszHRH8vYcKy+DJ/RE8mX214KIg5Lj3DEpOlzPE19/z+eUl41Pbw
+sgnQTE+ccm2h4GlUefprR8PSZdOIcqxbDbapLHC9vz1Dy1YdM5iySa7YJ7Xk4uDe
+NtysCgwKtANPKF/qtbYQgcZxGPYArrWprgK2xyMBeux7WTQp+9xaSOU2gUmAKwzj
+bt0iV6KEkoU08FmUwzXTxX1bBsjLyIeRFPm58XbJ0MxgGdJPmMn7m2W4PPeMJpGD
+vH1qhNYwy4NsWosCfQ==
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsDN2CACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsDN2CACRL.pem
new file mode 100644
index 0000000000..d32449cf89
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsDN2CACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIByTCBsgIBATANBgkqhkiG9w0BAQsFADBPMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEfMB0GA1UEAxMWbmFtZUNvbnN0cmFp
+bnRzIEROMiBDQRcNMTAwMTAxMDgzMDAwWhcNMzAxMjMxMDgzMDAwWqAvMC0wHwYD
+VR0jBBgwFoAUo1fZW10Rs2D2AGuJUSuCwwlzqHswCgYDVR0UBAMCAQEwDQYJKoZI
+hvcNAQELBQADggEBAAky6btWTahA//H8vhr8u8hbW8h/OWD8skXuVJHLv0LdS2cI
+zzk3f1u8vwWaohmjsIdNllFU2hd3GH62mJdoXJflLxxVDm47FWdzmmlyUsxFXojL
+HI+tjwBFmSzzzd/t7GeF5mKc11PrUz9oqoSJEZVCcYQX2px2xJIzmqOkc4VWGrGo
+rIvTE8SETRWTYcE/IDOxc1lSs4KyH/MGIwMeN6Mu3p3d/H8ygXKEzf7gYR0riFW7
+ps5wlQRxDC7ICktp4f2bN8l7pw5TwjCfNmzqDbW9IlDFaRr1VUV6hpPwORT9qWVQ
+7ZbDXVLZwaWxGmp8vYh7weNKgrmfibuz/DcHqPg=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsDN3CACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsDN3CACRL.pem
new file mode 100644
index 0000000000..a72350793c
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsDN3CACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIByTCBsgIBATANBgkqhkiG9w0BAQsFADBPMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEfMB0GA1UEAxMWbmFtZUNvbnN0cmFp
+bnRzIEROMyBDQRcNMTAwMTAxMDgzMDAwWhcNMzAxMjMxMDgzMDAwWqAvMC0wHwYD
+VR0jBBgwFoAUBtxbvscSN1mkikB0fAmdRTxKodswCgYDVR0UBAMCAQEwDQYJKoZI
+hvcNAQELBQADggEBAItPJiJOtxXyscdSk4LVnHx2Ge6LLKaoPE6qXi5JRs2t4LjU
+DJAh0bKPehBDlbvqpV6/e8pqaJP3T1DH28vZTr2gGY/Ar4BcVSzrKO0jH7j2PJU1
+Kzq68kP2/tr90XnUOuz5589ORJ9aryewWxJ5g1w5GqD4dC8NV5aDWPWAdnxVw0Dn
+DVvJl11dzlFqaZSwuDIj0IjzssosDmCrPDUjLDLn37f35XRIX7/GB3ghSrUJLsId
+TrU+ROvpQ4iO9zAVMDfQOePSnUeNpO4Sr/as3lHaFptwj0tY1PIeLcyfv8ZNZYN5
+7Q8+aKRQMPg2ZHGeF0d9ADvGLZTZ2vMCK/hHfL4=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsDN3subCA1CRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsDN3subCA1CRL.pem
new file mode 100644
index 0000000000..c179e730e0
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsDN3subCA1CRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIBzTCBtgIBATANBgkqhkiG9w0BAQsFADBTMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEjMCEGA1UEAxMabmFtZUNvbnN0cmFp
+bnRzIEROMyBzdWJDQTEXDTEwMDEwMTA4MzAwMFoXDTMwMTIzMTA4MzAwMFqgLzAt
+MB8GA1UdIwQYMBaAFIC8xy73jhp/8Th79DXr3elYxjxQMAoGA1UdFAQDAgEBMA0G
+CSqGSIb3DQEBCwUAA4IBAQDKBu+BE4WjUUs1+SbDPdTUvtI61Eo5D0f55tSFFzqY
+3mjQN51xcisMQIFvf9dPdD7/MOUjS2GeSG7CgPObXBVkhjsNzNhgUdk7obAAuQNo
+7pj0c+Kp36GKHgSzMmOvzXOpe0OcS0iEJTqOss/+GD+a0kPUQBTQptEFJwPyDdKZ
+yIpuXbmhEtaXJVEq/fIkQLtHDv6VqsOCPDyyca/NP1J2Ed4zEHcP7w4wTcxhCkq1
+duWazm3Bnc3Qu4lIOHUP1VGum+Q4cLzFzNlWCOIaWqCVwQvywAADhy4E/mxHtGAF
+8s55z3PAJfeRKySqkJJtPI5qRgllB+n9b/HxD7DskHy/
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsDN3subCA2CRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsDN3subCA2CRL.pem
new file mode 100644
index 0000000000..4f416b3ab6
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsDN3subCA2CRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIBzTCBtgIBATANBgkqhkiG9w0BAQsFADBTMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEjMCEGA1UEAxMabmFtZUNvbnN0cmFp
+bnRzIEROMyBzdWJDQTIXDTEwMDEwMTA4MzAwMFoXDTMwMTIzMTA4MzAwMFqgLzAt
+MB8GA1UdIwQYMBaAFMwE7WooHX7eZOoAiCrsdRG/pS5nMAoGA1UdFAQDAgEBMA0G
+CSqGSIb3DQEBCwUAA4IBAQBSQba9PCeM3gDcJ5/15bCZXzS1J88gi1mSM00AHLmm
+cvaM3zC1dupZ3+nUGrGuJeSy9gJvKmRvCGQJVVScbWaJSBCLsE2OTLQZosIbktpA
+SMDnJNCgEQMqL3S8I0U71qD4gw/f8DoxBloqVyzuwIynWZl+V9agP5UCWweDpo3n
+zqfqm4zYF2gZYgVz53l9/zYpxr8e0d6o/Y2sqk6ykbzR1e+Yw8Jr5AKs8awilABf
+Lu5pl0DO/pFtT1cX7MEtd1aoxZqQxssOS0JlosEF66VMeOKgUw4uvovkbjP9zfUr
+GfwYbvUJZ4XSDy6p68jsQKRpG7/v6V6Xy4LcZ+4I+ATD
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsDN4CACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsDN4CACRL.pem
new file mode 100644
index 0000000000..bd4472556c
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsDN4CACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIByTCBsgIBATANBgkqhkiG9w0BAQsFADBPMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEfMB0GA1UEAxMWbmFtZUNvbnN0cmFp
+bnRzIERONCBDQRcNMTAwMTAxMDgzMDAwWhcNMzAxMjMxMDgzMDAwWqAvMC0wHwYD
+VR0jBBgwFoAUbEk2rS5YiRI2UUE7VFImJADTynUwCgYDVR0UBAMCAQEwDQYJKoZI
+hvcNAQELBQADggEBAH6NXW4G6VFh6lalxV0HfwwV4W70J+q058P1U/nVsOOg7s46
+v+bhzKwJrIovS+fGE+WmDCMbeeIzvmMnkF7ZSGmZarmGDzNGsxghmiNHW9eP2Wk2
+7QjyePG/cAff8ILOqzCHw1WZMtIgyVM3tQY492xqKPRTBac5P5DN6COtBGzKsk7O
+mtOvhcqtwMt5ejXil9ibRhAKHcgoNtzOoZoaQOvTajOP0vdFYnHHkF+bFDd+lxUE
+IHgX9IOmk1xGFgdEcSpQL+6mvV0aKk9iavdbVBQCeWzul24GRr21EVO2emR3oceI
+vBeEL509yRgDMZoosPcUPzU3MCre4KHexkxNGKg=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsDN5CACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsDN5CACRL.pem
new file mode 100644
index 0000000000..bdb12dbf59
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsDN5CACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIByTCBsgIBATANBgkqhkiG9w0BAQsFADBPMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEfMB0GA1UEAxMWbmFtZUNvbnN0cmFp
+bnRzIERONSBDQRcNMTAwMTAxMDgzMDAwWhcNMzAxMjMxMDgzMDAwWqAvMC0wHwYD
+VR0jBBgwFoAUup8JypA5nE53Wuv7EJWs06dKXScwCgYDVR0UBAMCAQEwDQYJKoZI
+hvcNAQELBQADggEBAITmZi9ly086wq/MHAGjaUYsma31grAeyveTU+oJ7olEb42/
+ALSN0DXoO4UjV1bj/wiIPtErg0EjHWqVW4M9ID8ZdfDuXBLNBQ2NP14gJLjVTk2H
+6iHx4QK9QiB1gVdfKbj5+lTCt+7YWZtOWovr5SIlPtbNqszR1gpTLaLyIkT1FRg0
+txRaU4qTZmOIYCuWGEWeh0F9xHAZ4d30j1KcBSdHzOL28ibTjgDsYQF1680ChYu6
+TMUWY6q/ufziFDezkXkrLrtNWAwYZvS4sRynpbrhKUL+CldFK/u96ZZwL6T9Nkkn
+aY1gGgepUvLKlQt20+jmOu/L8Va7xcXK+AtuQQI=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsDNS1CACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsDNS1CACRL.pem
new file mode 100644
index 0000000000..33e2aa1407
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsDNS1CACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIByjCBswIBATANBgkqhkiG9w0BAQsFADBQMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEgMB4GA1UEAxMXbmFtZUNvbnN0cmFp
+bnRzIEROUzEgQ0EXDTEwMDEwMTA4MzAwMFoXDTMwMTIzMTA4MzAwMFqgLzAtMB8G
+A1UdIwQYMBaAFLGqF/Djz8zSp4mmgwfd/27aB+NJMAoGA1UdFAQDAgEBMA0GCSqG
+SIb3DQEBCwUAA4IBAQA16Gsjfh0eCk00xbqXxGjwPJCTmNcQ5BQRATn1KHZNDm0l
+HSuZNQELk9z/M1jw8rEArioWWkLQEKdVQ8WtjFYYYcj7VTo8aho763/UhY1WBQQF
+ISHqASE4ygCUcwSKus12Ksk+NXvvARlv5EG/UqoSAMx+6t0VLuPteV5JtXaAgABC
+xXScnAJvH0BZJWDXh8x9TCocXxmH9SsZhgXM4WEZxRch1Z3bxLd3NrxztPXe8h7H
+9Lvp/TOEvTdjtbBn0kwomw1DCVeJd9DBNXFO5YvXyDBU3usNmHNMk0wri1roJzUg
+UrnYLYox1O/52yeQdQiSAkQ3grctmcBcJW8WaIWu
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsDNS2CACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsDNS2CACRL.pem
new file mode 100644
index 0000000000..02b8226a6d
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsDNS2CACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIByjCBswIBATANBgkqhkiG9w0BAQsFADBQMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEgMB4GA1UEAxMXbmFtZUNvbnN0cmFp
+bnRzIEROUzIgQ0EXDTEwMDEwMTA4MzAwMFoXDTMwMTIzMTA4MzAwMFqgLzAtMB8G
+A1UdIwQYMBaAFEZInEIJjl1TcNgWHuDByRgVNQoGMAoGA1UdFAQDAgEBMA0GCSqG
+SIb3DQEBCwUAA4IBAQAmhkWLOOzNQKAS8uW774Ig/r2A1dpM2sBujFbCeg9blw2k
+/qRHoInFtwM22vgIBInC6EZW8qPqzPeJpjIluu02T25EjxRFGiameUbHQ02XAejf
+thhUnDw6lWF+N/sIKYXCkN6TuiKC9m4LPvlmI1JC1RqGWmZIsl59ae4zV1G0iyMy
+ol7WhsPhzsQj8pRACFdznwq0ehd3L8UO43BHl8+rXVzS6f++filZ2WZHPFCR5Nq8
+l9KaNhNCE7VeBQv7dElPeknvANPEGomeXFIIPwCx7Ra2mc3mjG5qYwmX1JYjPxe4
+siLRqgKc8s5vwYGwwPqtoInJckVUxfkZ8YAu5vTI
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsRFC822CA1CRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsRFC822CA1CRL.pem
new file mode 100644
index 0000000000..4b594be896
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsRFC822CA1CRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIBzTCBtgIBATANBgkqhkiG9w0BAQsFADBTMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEjMCEGA1UEAxMabmFtZUNvbnN0cmFp
+bnRzIFJGQzgyMiBDQTEXDTEwMDEwMTA4MzAwMFoXDTMwMTIzMTA4MzAwMFqgLzAt
+MB8GA1UdIwQYMBaAFMhqjrEPS6qliLinj5Hb6jNK6NXiMAoGA1UdFAQDAgEBMA0G
+CSqGSIb3DQEBCwUAA4IBAQAEHhqk9qr1vH2Svec33ko0nUKpbgipddl8h6FtsAXi
+ewGnaaUK55/3Eu4FsbORjB+jSKWvYyaXnMPecX8zseOKgDWEiPZY6i+0/16Dz/NI
+2aOEVA3hxBmSToyka2FOki/KzcWfeKpebdihmZ/pyKk2qo5ygfASyV+/3fyroafe
+0FwfIRz0EHXamVhpTBVK/nsmtk2KxccIP0QjPkCu95nAAzeXJVTbzOUAbz9ZDpnM
+puACGthyjzpNfQmx1LHF1juFUdywoQSm/c/FOoPnU2KH2Ae/x0Z+tKQ3O36c72EV
+IU8WWZP82htw8qMDi5U5gymAIdQJjsyKbg53DpbkEwom
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsRFC822CA2CRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsRFC822CA2CRL.pem
new file mode 100644
index 0000000000..6aa93849c0
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsRFC822CA2CRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIBzTCBtgIBATANBgkqhkiG9w0BAQsFADBTMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEjMCEGA1UEAxMabmFtZUNvbnN0cmFp
+bnRzIFJGQzgyMiBDQTIXDTEwMDEwMTA4MzAwMFoXDTMwMTIzMTA4MzAwMFqgLzAt
+MB8GA1UdIwQYMBaAFFGAzfpJckg87Q5OC87OH0BlEnCgMAoGA1UdFAQDAgEBMA0G
+CSqGSIb3DQEBCwUAA4IBAQAw8GlFDmo7lahPoMQ4ha8PN7CTyiVN52wVziP3qO0H
+uFi2H7EZV3VuYHNfJlzeeRtXGjpxBDiYkJJIg/n4ibxdyNtRnCjWTDb0BKbnQuUb
+QxoDRWuzc6G2pJ29H3Dp6DHQ7TLZwCfPfayn3AfOJBtYQpLk6c7ejrgXWv0T1ZSv
+H871UQpCF57JNU3zco9nXYN+I2IVnfgG08JXxGJsw3PMg1xa4v3awSYQ00/8v66G
+lBekAv9no926ub3Hp2DwYwcfE7iLaOdLtnBFPjdFLuZTPIGGJeeIve6NaEAwPqWO
+ZNUKSZipydOKeYsvEVEFUxVApV4TZbcSREvjGXQlYuH9
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsRFC822CA3CRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsRFC822CA3CRL.pem
new file mode 100644
index 0000000000..b2cec3d3fa
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsRFC822CA3CRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIBzTCBtgIBATANBgkqhkiG9w0BAQsFADBTMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEjMCEGA1UEAxMabmFtZUNvbnN0cmFp
+bnRzIFJGQzgyMiBDQTMXDTEwMDEwMTA4MzAwMFoXDTMwMTIzMTA4MzAwMFqgLzAt
+MB8GA1UdIwQYMBaAFJq6OU3aIXWv6kHDPGxR2KhFqX+jMAoGA1UdFAQDAgEBMA0G
+CSqGSIb3DQEBCwUAA4IBAQAAhq4g3DB1Yb6DUVC11zrj0kzA3Lbth5HDupuy0okk
+5Do0LQZeiwCI37JhQceiRGxtD5riXbo3LyL2g+XqV1OCTXAbJrRAe+5sHmwrH8pO
+PZobXV32KSzzqzPbUNn4QmQDxfQLZgGAz/aPtMDuBIm22oI5iP93csW6p0lHcu34
+Z78RF06bdhO5nd8PbU8BQ+weHoWq8h8iykRzkvxL2d+vk0rS9AbEu0Hs/tqnw/pD
+1l+cuy9gmD5MFpfoMDZ9seQAKJOt5VkKW4rphhX9b3rm2cxceIqlvqQgJip+RM/I
+v3o2PYMSOePzWfRA63LYY8B5sY79W0v/miCl08vsjte9
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsURI1CACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsURI1CACRL.pem
new file mode 100644
index 0000000000..67688e4aa4
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsURI1CACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIByjCBswIBATANBgkqhkiG9w0BAQsFADBQMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEgMB4GA1UEAxMXbmFtZUNvbnN0cmFp
+bnRzIFVSSTEgQ0EXDTEwMDEwMTA4MzAwMFoXDTMwMTIzMTA4MzAwMFqgLzAtMB8G
+A1UdIwQYMBaAFPoorUEW3ipoF8gPHCM/JgPeAhQCMAoGA1UdFAQDAgEBMA0GCSqG
+SIb3DQEBCwUAA4IBAQAr3EikSVaRZA2v0/ARpi/8rC3nZprCYF4pOzM5aC96Hooz
+oEfd6+gFBJWGGPyyQuztWCg4AYXJqqotuSL0rGteXeA7S09i58Vi0rMZEYiaw1HV
+EYvt4LDShzMpeTXstjWCfMlhWnBVII+6ga0LwGD1SWtci91T6zgYlXczH33esi9i
+ras/1f4p1SQDAyo1gHulaaBRJvqQrjE/MaL8q3Ts0kQMM57aI6UeqpydIttGUcl3
+pwNujq4V8yChxN9nB1wQauRsi9xCaqTJwLDqxouZIxdUeFWEQvRAIur8NHdhZrE0
+gvLGaebETdYzvDYEkFdVMsYNWPEmd2fZ+F8dJKYB
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsURI2CACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsURI2CACRL.pem
new file mode 100644
index 0000000000..fa5a247617
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/nameConstraintsURI2CACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIByjCBswIBATANBgkqhkiG9w0BAQsFADBQMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEgMB4GA1UEAxMXbmFtZUNvbnN0cmFp
+bnRzIFVSSTIgQ0EXDTEwMDEwMTA4MzAwMFoXDTMwMTIzMTA4MzAwMFqgLzAtMB8G
+A1UdIwQYMBaAFE3riXHf8AQBsvp2OlixumDdjNPDMAoGA1UdFAQDAgEBMA0GCSqG
+SIb3DQEBCwUAA4IBAQBk9OEGWwvZ0lgWzqgJL4OZ9j0+fjAMdi78QuVFSmvPPOi3
+kLfoOrPjMNmwi42SIz4BEyDt0iVxlq+eYqrIN5q/NqFdjl1w0UvNivcuPMjYtkdg
+4XPJXcfGCfnKbZ1voHnuDlmj6+lpUsXRA0YjgRhmGz4T26ReJO1FQy6+DDqO27+V
+CZ3JM83sNDNlcMeZZk9f46HtFq/kwvqFpTHmqWvvti1hLal0aRetSyckf01vmHQa
+14FbJwEqs6hrwxhLN6Fwd/b6lN3+vLbgucxKkFvRL0JUbPuY2VtFlJk8WOllxXMc
+MYhV7lqBnaFyCO+gWQaEIE5dDbKQTNVpmVRqbT+8
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/onlyContainsAttributeCertsCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/onlyContainsAttributeCertsCACRL.pem
new file mode 100644
index 0000000000..0c5aa4a18c
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/onlyContainsAttributeCertsCACRL.pem
@@ -0,0 +1,13 @@
+-----BEGIN X509 CRL-----
+MIIB4TCBygIBATANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEmMCQGA1UEAxMdb25seUNvbnRhaW5z
+QXR0cmlidXRlQ2VydHMgQ0EXDTEwMDEwMTA4MzAwMFoXDTMwMTIzMTA4MzAwMFqg
+QDA+MB8GA1UdIwQYMBaAFE0H/vYtvLUZGlBN35kEem0zcJBOMA8GA1UdHAEB/wQF
+MAOFAf8wCgYDVR0UBAMCAQEwDQYJKoZIhvcNAQELBQADggEBAILLUDuBlm16hFFX
+7mKimuJxRJIqqqi7YTYNnUkkt1ZjNwD+Zb+YdPhJrJJMboHIGRErVjSDGwHJs0HF
+HveDuGTcBDgqyJeG0C55xjRlSkPABS4jtWVVROYX/GkccJOjCGEs8A0vMVWUrsP9
+Otao6UOKDtn4wNkaItZ1Mv8Bbk1han+DsqJIdjCc3T8RV6xZcyQ9o0uO7THeyATG
+mPT6keQhjiAbT7NtvSL7pdd+F3U+68etzFFxjwqFr7/wSJxVWzF4vtrwW6tqPcCF
+SXNG3adV0JgEG4luWVHRC2GODHTh8DeygWMtEkKtkRouQMFoZGv7MvgoqlewMTfB
+1YnOC+M=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/onlyContainsCACertsCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/onlyContainsCACertsCACRL.pem
new file mode 100644
index 0000000000..23ab0ba052
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/onlyContainsCACertsCACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIB2jCBwwIBATANBgkqhkiG9w0BAQsFADBPMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEfMB0GA1UEAxMWb25seUNvbnRhaW5z
+Q0FDZXJ0cyBDQRcNMTAwMTAxMDgzMDAwWhcNMzAxMjMxMDgzMDAwWqBAMD4wHwYD
+VR0jBBgwFoAUJTjDrsotdXpbTdTAA5KIEyLHbFQwDwYDVR0cAQH/BAUwA4IB/zAK
+BgNVHRQEAwIBATANBgkqhkiG9w0BAQsFAAOCAQEAMHmB5cF8VCL97/Gfo/Dpi3SC
+hi1rnJSgDEUQFwICI2Jlyewhfxs6F/EC7F0YYnPPfiA8ajXpGp5ccAvVUIoQUBgX
+iUdjp/RBmalZoyzPw8ZkGQMyy6ehymfyMnH/wKrivmAnxtPm8SGuWVJouu0T5+7n
+8G/vJg4q42RHYQQ7ripuh6wiI+Lf9wNHAzmmGsT6vG0Yrpg0hAcQzhof9xOK88h1
+xyJPReiXKjJx8Uvp/jIRjarGdjh19va1ysYLfMypqeF3C3Lb3J9vuxLS31FdX93s
+wLY919ormQApL+COmVhzilgTKI6ZfgxFhlF3nRNMQDdGc/r3tdezNoMznqBbBA==
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/onlyContainsUserCertsCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/onlyContainsUserCertsCACRL.pem
new file mode 100644
index 0000000000..e99752a506
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/onlyContainsUserCertsCACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIB3DCBxQIBATANBgkqhkiG9w0BAQsFADBRMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEhMB8GA1UEAxMYb25seUNvbnRhaW5z
+VXNlckNlcnRzIENBFw0xMDAxMDEwODMwMDBaFw0zMDEyMzEwODMwMDBaoEAwPjAf
+BgNVHSMEGDAWgBSdvACp3AHN/h2WiH21nk+Z3iTSBTAPBgNVHRwBAf8EBTADgQH/
+MAoGA1UdFAQDAgEBMA0GCSqGSIb3DQEBCwUAA4IBAQCHCFuxTPvpn0fp83f2Az3x
+GKcDG6BSg4w2lISlNPpcuzAOfsNrNNvWBc8jiwdsgKpMr2rFH7HdpvG9k+A5EqT4
+B978vmdJe6d+HJ0a6RINKrDeQ3BWUeZVyzXzKJvJNEtP6+l67BA/PV1O7t93prcA
+sUdgfm074/h81yKVQJKfsE+H9yCXo4XxlvN9/37Du9lNWtlU13tvD1Hc7FjO3pB2
++YJQwmKhlXrt+7K96k9DdPJMXGzJmpICBFxHz8KkC3f0vmYau2h8FzDZV3P0trhO
+azIpQl9N2nqKsYQ57hLeTBRQ3B9tlKm8MH3lzfYUf8Iktufhg/HGuFg35gfeZWUd
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/onlySomeReasonsCA1compromiseCRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/onlySomeReasonsCA1compromiseCRL.pem
new file mode 100644
index 0000000000..8aeb421af8
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/onlySomeReasonsCA1compromiseCRL.pem
@@ -0,0 +1,13 @@
+-----BEGIN X509 CRL-----
+MIIB/DCB5QIBATANBgkqhkiG9w0BAQsFADBMMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEcMBoGA1UEAxMTb25seVNvbWVSZWFz
+b25zIENBMRcNMTAwMTAxMDgzMDAwWhcNMzAxMjMxMDgzMDAwWjAiMCACAQEXDTEw
+MDEwMTA4MzAwMFowDDAKBgNVHRUEAwoBAaBBMD8wHwYDVR0jBBgwFoAUUGjRCUEn
+h+cKTrd4VvsXju4EB3EwEAYDVR0cAQH/BAYwBIMCBWAwCgYDVR0UBAMCAQEwDQYJ
+KoZIhvcNAQELBQADggEBAGGC6QC55Xv0YiF5ILhvWpoLo4gexHND0T8vTNw6fKSa
+g9Re5ty4c1tDwJkRQ3RRY36O82BLImIyx+NltDfnu21/0zLv9XbZxHUraT+5Ch2v
+smfGOTG/zm7a0Ht0QtM6q/br1RJPbxG0c7RUyV4hooP9WDV2UIz/9ba6XfbIf6Ts
+SaDhRmu6P2hbGTuAvKwsMR+DgDRvCQ8OUdc639W2hiVXaf5+kHyQz25BDTDEOptA
+cwgpFmaZiGKgltA63nn4Fl797/VmQbQYg8pmRPsEPeF5kw6n5lbXEo7wLXG4dc4N
+GiqeRO8oi8zU8rI/ddx+zr9jUeJZQldzM9VBsOq2c8c=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/onlySomeReasonsCA1otherreasonsCRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/onlySomeReasonsCA1otherreasonsCRL.pem
new file mode 100644
index 0000000000..49f7456ffa
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/onlySomeReasonsCA1otherreasonsCRL.pem
@@ -0,0 +1,13 @@
+-----BEGIN X509 CRL-----
+MIIB/TCB5gIBATANBgkqhkiG9w0BAQsFADBMMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEcMBoGA1UEAxMTb25seVNvbWVSZWFz
+b25zIENBMRcNMTAwMTAxMDgzMDAxWhcNMzAxMjMxMDgzMDAwWjAiMCACAQIXDTEw
+MDEwMTA4MzAwMFowDDAKBgNVHRUEAwoBBqBCMEAwHwYDVR0jBBgwFoAUUGjRCUEn
+h+cKTrd4VvsXju4EB3EwEQYDVR0cAQH/BAcwBYMDB5+AMAoGA1UdFAQDAgEBMA0G
+CSqGSIb3DQEBCwUAA4IBAQASB9/TxIVbi32C3VcEK5Q9oKUrzWbOmzDBEI3G1S0k
+Y451pdZaFfBVzikVlcBVTw5gikTy4TUK6sEoysiEvRlpTzYuBxqKQgXwacRlVNUG
+mzZqYmHQycTyrlZfv4PyVPrz6E9ToTvz9/DymSkswVyBm/GKZ3Rq31G8niu4xHT2
+EBz0BEW3/GN9sUC/Y4dGPcTNJc3OkcrL7t1YKfz9H+dFdXqpTW9wdt6DkyEI/Ync
+3pxo6Qnj8Wwk8U2aiDvKqQh6z/b8Mlt1CullIhKM3SqGzTqGnv7SntdZwTv/1oeY
+dbpJ6VGFTMqXxUkgDnR4ba+Tn9iMkAUakQvbjnN5/mgW
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/onlySomeReasonsCA2CRL1.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/onlySomeReasonsCA2CRL1.pem
new file mode 100644
index 0000000000..2d585f4780
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/onlySomeReasonsCA2CRL1.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIB2DCBwQIBATANBgkqhkiG9w0BAQsFADBMMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEcMBoGA1UEAxMTb25seVNvbWVSZWFz
+b25zIENBMhcNMTAwMTAxMDgzMDAwWhcNMzAxMjMxMDgzMDAwWqBBMD8wHwYDVR0j
+BBgwFoAUYGPf0iOkKdZBpKzKhnmYpmUBSK4wEAYDVR0cAQH/BAYwBIMCAxgwCgYD
+VR0UBAMCAQEwDQYJKoZIhvcNAQELBQADggEBAE7Yv0ls2R1n277KQK1TR+2NXC1y
+pY4ZorshYnrshNO+Uy23PtVCJzy0390E1CsDzIZdmz+/rXEDsQOSwCYDWrCSo6hK
+BCyTlWBfqZcNTQJnRgYV95e3yHaqY0mEgY6l+az1gDR7hC5UBwFPnFGNnY0s9JSD
+bu4LOdl+bT8QmTw02+hfMA4Q/I+ytJOkWkLs+5pLg6vyQreprf8MjJDdGxG0oKk5
+Q+vPpYQQmsyBosaz7mNSuZ799BAqRAl0ATMmT5FGldFo7whlDhyYwCnT6djdR3nd
+nsBhSdbf3LXqnKk6i79U1H+Yzx0XagnJA7wGGmHf5oIRRVDaMmQOzaf/sl0=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/onlySomeReasonsCA2CRL2.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/onlySomeReasonsCA2CRL2.pem
new file mode 100644
index 0000000000..657f0999fa
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/onlySomeReasonsCA2CRL2.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIB2DCBwQIBATANBgkqhkiG9w0BAQsFADBMMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEcMBoGA1UEAxMTb25seVNvbWVSZWFz
+b25zIENBMhcNMTAwMTAxMDgzMDAxWhcNMzAxMjMxMDgzMDAwWqBBMD8wHwYDVR0j
+BBgwFoAUYGPf0iOkKdZBpKzKhnmYpmUBSK4wEAYDVR0cAQH/BAYwBIMCAQYwCgYD
+VR0UBAMCAQEwDQYJKoZIhvcNAQELBQADggEBAI78t4QP3CIEpYKHa9TIyjr1Z+lq
+HE7UgiKd4HbsKK5gPDAme9A/DK/klDZoufxmSUnjJ+KO5yE5YiQITW9k2CvZL17g
+Wo8miQcf7HFp9dEEnwd6yHE10ZmqqIJrujTaa9P76KhaovCCoWlJUAyCOSF9DDyj
+5fJyJYUKxXo4FwkcViwsJgp4NK3m+YB3BYFRy/p4PQKPFYYrDMI1yx8B/lOp16UH
+rhYzMC5sohfnCpryHxNTgBu1Cy9XzITBvUniXOtMO+hY/axGojkm/IOQSlzYAahL
+AOc1qr6BEK/tkWxUJanBWTpPh5d5Rojto/uu4fOG4XbQLS4PllRZlCKQTyY=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/onlySomeReasonsCA3compromiseCRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/onlySomeReasonsCA3compromiseCRL.pem
new file mode 100644
index 0000000000..acac31a59e
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/onlySomeReasonsCA3compromiseCRL.pem
@@ -0,0 +1,15 @@
+-----BEGIN X509 CRL-----
+MIICPTCCASUCAQEwDQYJKoZIhvcNAQELBQAwTDELMAkGA1UEBhMCVVMxHzAdBgNV
+BAoTFlRlc3QgQ2VydGlmaWNhdGVzIDIwMTExHDAaBgNVBAsTE29ubHlTb21lUmVh
+c29ucyBDQTMXDTEwMDEwMTA4MzAwMFoXDTMwMTIzMTA4MzAwMFqggaQwgaEwHwYD
+VR0jBBgwFoAULSS3l4cs7tocvt6XhBuvoBUWvmswcgYDVR0cAQH/BGgwZqBgoF6k
+XDBaMQswCQYDVQQGEwJVUzEfMB0GA1UEChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAx
+MTEcMBoGA1UECxMTb25seVNvbWVSZWFzb25zIENBMzEMMAoGA1UEAxMDQ1JMgwIF
+YDAKBgNVHRQEAwIBATANBgkqhkiG9w0BAQsFAAOCAQEAdQRa9rY2kA/f5hPXwMzB
+IiCNKdlFB5gAcrElHOAFq8MP3BQ6LtE0dH+RoAudtqh92aydcFza5EsMVAQq99AM
+jBjlm535dj9hxo79uqC2ZReofJBNgLL5H8cktqZPCsnyeGgDWDxHGtSE7PThveN3
+4VD9Kstk04qFpJ/8TK3BPca6zEtTb0k8LkpRojumPy+KGNs936HEkGwRBQaFkn4v
+i0gosO557Q4wXMI4AQfuY5jGCavXjUkXoR1BlS0CrQqMFC69J8hOrpK7tOdDPefa
+NpCf+3pMgASPMhuc3W9/bQIQTQL/dFwdfQZCmJUrxu3e04URw13J+rDXaZKgYXb9
+cg==
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/onlySomeReasonsCA3otherreasonsCRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/onlySomeReasonsCA3otherreasonsCRL.pem
new file mode 100644
index 0000000000..3d20df2931
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/onlySomeReasonsCA3otherreasonsCRL.pem
@@ -0,0 +1,15 @@
+-----BEGIN X509 CRL-----
+MIICPjCCASYCAQEwDQYJKoZIhvcNAQELBQAwTDELMAkGA1UEBhMCVVMxHzAdBgNV
+BAoTFlRlc3QgQ2VydGlmaWNhdGVzIDIwMTExHDAaBgNVBAsTE29ubHlTb21lUmVh
+c29ucyBDQTMXDTEwMDEwMTA4MzAwMVoXDTMwMTIzMTA4MzAwMFqggaUwgaIwHwYD
+VR0jBBgwFoAULSS3l4cs7tocvt6XhBuvoBUWvmswcwYDVR0cAQH/BGkwZ6BgoF6k
+XDBaMQswCQYDVQQGEwJVUzEfMB0GA1UEChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAx
+MTEcMBoGA1UECxMTb25seVNvbWVSZWFzb25zIENBMzEMMAoGA1UEAxMDQ1JMgwMH
+n4AwCgYDVR0UBAMCAQEwDQYJKoZIhvcNAQELBQADggEBAE32bsFUkG2yqjakiO6C
+h4PUu/ZdW+ec4ZPUt/0Aw5pN/yGw2DJp2nBjHbLEn8qoxZbVX7CM4Kl44520B2Mm
+rn/e/dmOMzfKLV5SxreIg2gzl0Y/mg4JqmGza4sTA8nDExWiRyqQbf7wIgK6pQEU
+sEjvc2RsJrzvIt6QryEMYAJaAe9FaMP++SnKSKisBKzcirBgTz0nrPrNkUcI2o6P
+GHW3ZwPfyDsUhx1z2sbtT3y/svye0Ksgh+2wZc3tU5zsvRAKN6fokw4ADhlporgF
+3WZLQJcywJElK3w7mPc8YL9JKieuAiul/E1dbpnm8dA9Sj5oDyooynxUav/o4HCl
+WDw=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/onlySomeReasonsCA4compromiseCRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/onlySomeReasonsCA4compromiseCRL.pem
new file mode 100644
index 0000000000..3b60898d4d
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/onlySomeReasonsCA4compromiseCRL.pem
@@ -0,0 +1,15 @@
+-----BEGIN X509 CRL-----
+MIICYjCCAUoCAQEwDQYJKoZIhvcNAQELBQAwTDELMAkGA1UEBhMCVVMxHzAdBgNV
+BAoTFlRlc3QgQ2VydGlmaWNhdGVzIDIwMTExHDAaBgNVBAsTE29ubHlTb21lUmVh
+c29ucyBDQTQXDTEwMDEwMTA4MzAwMFoXDTMwMTIzMTA4MzAwMFowIjAgAgECFw0x
+MDAxMDEwODMwMDBaMAwwCgYDVR0VBAMKAQGggaUwgaIwHwYDVR0jBBgwFoAUvmbc
+HgwGO/bTiDSRUyaBDWgXbskwcwYDVR0cAQH/BGkwZ6BhoF+kXTBbMQswCQYDVQQG
+EwJVUzEfMB0GA1UEChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEcMBoGA1UECxMT
+b25seVNvbWVSZWFzb25zIENBNDENMAsGA1UEAxMEQ1JMMYMCBWAwCgYDVR0UBAMC
+AQEwDQYJKoZIhvcNAQELBQADggEBAAJIN0tHbsczxcPvHunvZEwHCJR0fVLav9lF
+XrsjWM5Qsaj0eOddNDav/I/ELvHUL2JCVu0UKLtwlNZlAIPgOKi3wEhf6qSV2WY8
+NW+zQ8Zdk+zuIsE/to9477C7VJfd95fqPhxFRzrfo3R1TCwEskmOVhvwIcsuYOfM
+fNl1ObbutpSkauLcUTJaHkdo0/QO6vSMEEVbmVbqXUOVq/Bbv4QFypTl9tEGCAeS
+SXhy9KG1XxdDJRL8nExHIqzvaBxRg0IJAHGi1ERrL3mlUyq/0AkHnT2kqrNaVzxL
+aDF48Wb41H/JPZjU73gRqAm4HHQcEIR/ZMgCQy2ZqMFxubBzw6U=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/onlySomeReasonsCA4otherreasonsCRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/onlySomeReasonsCA4otherreasonsCRL.pem
new file mode 100644
index 0000000000..189002c25d
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/onlySomeReasonsCA4otherreasonsCRL.pem
@@ -0,0 +1,15 @@
+-----BEGIN X509 CRL-----
+MIICYzCCAUsCAQEwDQYJKoZIhvcNAQELBQAwTDELMAkGA1UEBhMCVVMxHzAdBgNV
+BAoTFlRlc3QgQ2VydGlmaWNhdGVzIDIwMTExHDAaBgNVBAsTE29ubHlTb21lUmVh
+c29ucyBDQTQXDTEwMDEwMTA4MzAwMFoXDTMwMTIzMTA4MzAwMFowIjAgAgEDFw0x
+MDAxMDEwODMwMDBaMAwwCgYDVR0VBAMKAQOggaYwgaMwHwYDVR0jBBgwFoAUvmbc
+HgwGO/bTiDSRUyaBDWgXbskwdAYDVR0cAQH/BGowaKBhoF+kXTBbMQswCQYDVQQG
+EwJVUzEfMB0GA1UEChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEcMBoGA1UECxMT
+b25seVNvbWVSZWFzb25zIENBNDENMAsGA1UEAxMEQ1JMMoMDB5+AMAoGA1UdFAQD
+AgEBMA0GCSqGSIb3DQEBCwUAA4IBAQA6VO6zbrPramIZ5tc+d2wb1qV2eX230xjC
+Z+k9zK/PWUvG47ZOYUDccUWhVUgH99nRvoqdhVZrF69HIqdJx8a1+kvi45meJsHN
+faOVtyb2cXnYduYiSrcVZtimnSItAc9LIKt5NvRKVbsD/ZX4Nf+fnCaQEmd1Vx89
+dWq+5Pt3iqORg89D6HzU1GFVE/ES60t7rUbYkxKdfnZhsauX6Mm7ZBIDZK6I14LD
+6qm0UicAFZzpn15jKZWD6h3s3ZGgpFBIR1CkQH5pyTLQK/jPma1mq6ktz3hHjAfR
++BFDWpDxlfjcvBm8TPB8kmPYF9zl7fj8VkpDW/g2jwvDYNwyuYq9
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/pathLenConstraint0CACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/pathLenConstraint0CACRL.pem
new file mode 100644
index 0000000000..c1142fd5f2
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/pathLenConstraint0CACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIByDCBsQIBATANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEeMBwGA1UEAxMVcGF0aExlbkNvbnN0
+cmFpbnQwIENBFw0xMDAxMDEwODMwMDBaFw0zMDEyMzEwODMwMDBaoC8wLTAfBgNV
+HSMEGDAWgBSbK7JKPJDFblABySK9Y84J8Yw9+jAKBgNVHRQEAwIBATANBgkqhkiG
+9w0BAQsFAAOCAQEACbNqHOyNhLoyqsp302Qhh2DQ5xn50BIJt8FHPQoxzjgcSxkV
+vSWc3F7o93x2Gy3t90APobAPMq7lgkvI9rUyIdg3jn+mkAyaGOaZNoocBVIGwEmI
+ITL2DEhUSGag4wZgXgxAqkcvasDMihLpVudYujuqQ3sU4FLxr1jwXtdtwmXrYHSx
+W3CFNk07BfXdrjETCFJVP2fmElf1s3xOJJaw82NqWBAU7OGk/CTI0XQwxPHAqnao
+Pywh9giLL8cjIqi77qOQk3qSmPvygMXfsMQv1LkdL1UpSNta0D4NurcyLXUojN9p
+qwk5NGs5KwY5CNr3lncwUfuSkuySGB+KmoSzQA==
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/pathLenConstraint0subCA2CRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/pathLenConstraint0subCA2CRL.pem
new file mode 100644
index 0000000000..34b51bd784
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/pathLenConstraint0subCA2CRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIBzDCBtQIBATANBgkqhkiG9w0BAQsFADBSMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEiMCAGA1UEAxMZcGF0aExlbkNvbnN0
+cmFpbnQwIHN1YkNBMhcNMTAwMTAxMDgzMDAwWhcNMzAxMjMxMDgzMDAwWqAvMC0w
+HwYDVR0jBBgwFoAUxgkqG/u46T5oYHrHl86zWFF7dt4wCgYDVR0UBAMCAQEwDQYJ
+KoZIhvcNAQELBQADggEBAJqExyZTBoPDB2CCe0jcRtIcGgoQJSMase7HfvYA2gVB
+zQ+2yRT8qyeoK/ybY8kBRGQ8pSfv7R4ARlwK2vvAfx70U512ssjSDWpmAXK3EuDU
+JVvVfrmHO66z6LhZ1BUx3ngzk1tnEZnvfCWqMGkRFh3dXT142l/0uWGdEBpr/lyy
+zhhJZ+AvGuhEWpeuAI5GL4Gw7p/YBaMoioEvVVMJ16Jp9k7dwaFF7hSPLf31GCtn
+Rm/6rENr/P+vdFLRET/FG84eGMBsLtlW4y6q2boQImMqZZ6uC5hIfK1Ke4vLfEZS
+/6Qs392dyBadvH2ifTb/L95kG7zGC7fHAx2e+tECzCo=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/pathLenConstraint0subCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/pathLenConstraint0subCACRL.pem
new file mode 100644
index 0000000000..1c2c371782
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/pathLenConstraint0subCACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIByzCBtAIBATANBgkqhkiG9w0BAQsFADBRMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEhMB8GA1UEAxMYcGF0aExlbkNvbnN0
+cmFpbnQwIHN1YkNBFw0xMDAxMDEwODMwMDBaFw0zMDEyMzEwODMwMDBaoC8wLTAf
+BgNVHSMEGDAWgBQUYmcQfdI3xXIG0N5/tRYdyqNzXjAKBgNVHRQEAwIBATANBgkq
+hkiG9w0BAQsFAAOCAQEAFpwGZEbWR35NljL/Cqg+hxRkUXPsz9aZL5+OrEpCBF/F
+AvtTISr5emDzeNbc+DWdsPAIYIOZ/1kx31aA+0e9Hc5Bac7O246yIwM/kyODPfM+
+36CTCsUCHdIlEm2tDNOhn7gHV9y+tJkT49rRp952zeTwloKyMADiR5amnqhcoOXp
++uY55zW2wQLkADXqukppj4bP13g/R9fIZvhkTFLD1FGArjGt9HH1Hfe3n3MjI0TV
+AaD5TOm4Jogz8RsrFDz3EFMUxCb06jcGz+B3Oe5etjJ0aSpbG8d5ETgGAW3ucXbP
+eVlTs9xMJlm4s34hxYOI3Xnu2ksQ3YiHccWpFcNo8Q==
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/pathLenConstraint1CACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/pathLenConstraint1CACRL.pem
new file mode 100644
index 0000000000..c0dedb88c7
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/pathLenConstraint1CACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIByDCBsQIBATANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEeMBwGA1UEAxMVcGF0aExlbkNvbnN0
+cmFpbnQxIENBFw0xMDAxMDEwODMwMDBaFw0zMDEyMzEwODMwMDBaoC8wLTAfBgNV
+HSMEGDAWgBTz5HFg/xcU3o0mhTN+HPzBR2f6wTAKBgNVHRQEAwIBATANBgkqhkiG
+9w0BAQsFAAOCAQEAYUHA3NQZQRmqjqAh2cWof7Fkl+YK2qY4pF/Yo6L2+ASqcQaE
+6+YCwPVUcjJsNzrF76KKHib7UbodnAH9cBBig1d5a00OFN9n4B81Ha7C0UxKtphU
+gm7wVEBlsHY8KAcV9y+VX11UX9qJ2GEAN1pa/Kio6w42wMcW/oDrAO7UKZtzJv3M
+peqQexGUQYQMbyIh2TTz4TOk2uZovikXa2+1FiUqFPNZfHkZ4eRnQBUQBxFJT1pj
+m0QtotM8+yChkTGrP52ilV/GI3kaLuyIbZVl7adUHiIxUkrEmmkOl7OHhsKwrrTU
+9RkZtL1XF075smmMW8iNRRVEDA3HqJxv2GN0iQ==
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/pathLenConstraint1subCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/pathLenConstraint1subCACRL.pem
new file mode 100644
index 0000000000..fd43b9187b
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/pathLenConstraint1subCACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIByzCBtAIBATANBgkqhkiG9w0BAQsFADBRMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEhMB8GA1UEAxMYcGF0aExlbkNvbnN0
+cmFpbnQxIHN1YkNBFw0xMDAxMDEwODMwMDBaFw0zMDEyMzEwODMwMDBaoC8wLTAf
+BgNVHSMEGDAWgBTlmZa1x31VQq2Bjscl9hjNrJ2QeTAKBgNVHRQEAwIBATANBgkq
+hkiG9w0BAQsFAAOCAQEAE0X1RyeBmz4sK6l9ognWRz3YK1XFln2WRcvR6Rnj5+hD
+paYtOhh2FRMNL11CqqpiG2IC1JPGKA36hx7UcF6LyhcqtDS0YEI9kXz/C5Hk60bc
+wKuIyVOZYgXOnNvsewa2LvGhi8ZUCI4L6DGdV7+OcJ/7PX3Jth+01Np7T15jo/gv
+cCCkFx3fmd9ysx2xcO2Cyd5vtqiEOK7DIoNZ6WguHlYdZb9/kDoRoWf5Qq7rmKXQ
+agJhrU6jWsCfQqY6e1oYlafSRPemgJj4kD1vtTW1D8OZc+iUFy0Fd3EZ6HT8xOAP
+9e0WFDsqL0+K31KZk5tFm3KZOrjJIxvcmLu+a1mELw==
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/pathLenConstraint6CACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/pathLenConstraint6CACRL.pem
new file mode 100644
index 0000000000..a4bb7e3ab2
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/pathLenConstraint6CACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIByDCBsQIBATANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEeMBwGA1UEAxMVcGF0aExlbkNvbnN0
+cmFpbnQ2IENBFw0xMDAxMDEwODMwMDBaFw0zMDEyMzEwODMwMDBaoC8wLTAfBgNV
+HSMEGDAWgBSvvIWu/kyu4Y2XI4jIpbFgC7pO2DAKBgNVHRQEAwIBATANBgkqhkiG
+9w0BAQsFAAOCAQEAmHcSluSxj/t6JAD/aFB0G2KneIGVwM+c+dhxzVgL8/onCq0v
+KLB3BafsI5LQYRb5peAT1mCROTnVIHW0n6bE0RCQIxGty7Zh/kcjRtdL9cv5JX91
+ovjJVm7snc3XR5wUe4+8pRO3HeWurcXj/NvUOmaZg+hEnv8zgICjxndiqtPlQPjq
+/BIIDOT7FSuYwFIB5bcPBvyEjRSR7hi2UDzR9bxjiHBULC8nCbNhk5Qff0815fLK
+TQozMgMeB3D9psl8cDfO2owL77bsQx0um2QjJWW/ByMnYndkzGRt/9SkxRGHKtmV
+2rp5wMZKEQcVOm8v6KmfkdWtMBf5GCXLvjf0RQ==
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/pathLenConstraint6subCA0CRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/pathLenConstraint6subCA0CRL.pem
new file mode 100644
index 0000000000..a06b0839cf
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/pathLenConstraint6subCA0CRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIBzDCBtQIBATANBgkqhkiG9w0BAQsFADBSMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEiMCAGA1UEAxMZcGF0aExlbkNvbnN0
+cmFpbnQ2IHN1YkNBMBcNMTAwMTAxMDgzMDAwWhcNMzAxMjMxMDgzMDAwWqAvMC0w
+HwYDVR0jBBgwFoAUz3Z2g3OQJMeNo21nfOtSwNTU7UgwCgYDVR0UBAMCAQEwDQYJ
+KoZIhvcNAQELBQADggEBAFyoC4W//fvuVDNbyQCjkcD+eWvMUc48a7gacH/9yFzM
+auBjPEdjd4HWqUjEH7AH75ZgRXnf1n1pL9wh9mIJmVV4hLHfnDcryOMH71gfDrft
+d72DZcCBNZv7jXSMuIjCDn4TtlF8DgldJHQ5uA1f/a+qt1nOV7OCgHStr8PsP45+
+Cf/UGN/hqLWdlvaMB7PF7Qbfm639AE4UWs5NyKQC9PaDpFw12fAUJSO6PRXnSEMT
+9xx74LAC4aZIO5CtnUE/MPIvGgJFdUneXVkGDmsg/jsXbCVhKH7fUWun9IPvehE/
+OW6kYzC+6v943J3NelQUzcWzzF0CH8DwMGEY/GcmIwI=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/pathLenConstraint6subCA1CRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/pathLenConstraint6subCA1CRL.pem
new file mode 100644
index 0000000000..5aff3ab9e6
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/pathLenConstraint6subCA1CRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIBzDCBtQIBATANBgkqhkiG9w0BAQsFADBSMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEiMCAGA1UEAxMZcGF0aExlbkNvbnN0
+cmFpbnQ2IHN1YkNBMRcNMTAwMTAxMDgzMDAwWhcNMzAxMjMxMDgzMDAwWqAvMC0w
+HwYDVR0jBBgwFoAUPJqVnpNeVmLpWziQbJo6bpLb9wswCgYDVR0UBAMCAQEwDQYJ
+KoZIhvcNAQELBQADggEBAJEL4fmSZTA+fF23RGynu2YEOWQ+YFNJzfYPg8EdimTL
+8AAXERJlbKKh+51nygXhYhUEonLIVmJyK83n7rIbEaOlrfXu2M4CtSi0zPZyTNL6
+hntIw1oK90JumjJhqxc0T9viI+cCoKz5b1Y0VZ5UNrno5fHmhw784TwN8bKxTig1
+DC4mjrk38QAAEV/5xQdS0i4nXP7Tz1PO81ueqMz8kgdp+QHjFKxDOoadq1gt+7Ly
+GRuY2SlF7pKZ6e4SdGF3rJ/sSzmO0wMLkMU4074RRg0BOo7J8ow/WuMVmwi+alQJ
+l5xRRlX1d4F30oa5AuJWrOSDw87HKTfRwPvCF/U6hPY=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/pathLenConstraint6subCA4CRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/pathLenConstraint6subCA4CRL.pem
new file mode 100644
index 0000000000..1cc5a4506e
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/pathLenConstraint6subCA4CRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIBzDCBtQIBATANBgkqhkiG9w0BAQsFADBSMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEiMCAGA1UEAxMZcGF0aExlbkNvbnN0
+cmFpbnQ2IHN1YkNBNBcNMTAwMTAxMDgzMDAwWhcNMzAxMjMxMDgzMDAwWqAvMC0w
+HwYDVR0jBBgwFoAUSYXbS/sRY9mZAii0C3qeExdaFXcwCgYDVR0UBAMCAQEwDQYJ
+KoZIhvcNAQELBQADggEBALcHlK8kwUx4MZCo/sdlhk02Cf/n4mMvGAzXzjTDFoO1
++Nvo1HL5xmZ9KKCTSYmfBHc/qhMAmH31I6UJAVlOdtUcq2VCRRej8OlOGLImThkt
+oClLD6mmtW1YuB6bJtsuFj+GIPvLZfkpOgKg0fDmcQRVL5DO5ajMN/BH/xJtHuiV
+n5DjNZPhJls11DOd57TzFhGVq17PhOrMvL+8nCMs4Zl7DtNKw3EJuTDGTGAB+OYx
+5FJBmick+Mnkf2ElfidE3yBiOlg0Oj/fXjb5cqCa4uR+sAqYOl9BBjM6RfVbOrhJ
+7FuBGqYEmCr0D/FT8EusGdWFDBqfDsWtXVV7LHxcm6c=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/pathLenConstraint6subsubCA00CRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/pathLenConstraint6subsubCA00CRL.pem
new file mode 100644
index 0000000000..6b19bdf4f3
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/pathLenConstraint6subsubCA00CRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIB0DCBuQIBATANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEmMCQGA1UEAxMdcGF0aExlbkNvbnN0
+cmFpbnQ2IHN1YnN1YkNBMDAXDTEwMDEwMTA4MzAwMFoXDTMwMTIzMTA4MzAwMFqg
+LzAtMB8GA1UdIwQYMBaAFLq54oj31FkliuMp30+gBjjdcXSCMAoGA1UdFAQDAgEB
+MA0GCSqGSIb3DQEBCwUAA4IBAQC9tjk91ybiFjv2eYZsjGMKc+y1kd1xc1xIXSdD
+dM5JR+GglRm79uK4gDZ3gTLIQpDVgYPXV+ereJ9Kg9DOl79O3h/nxtS45azUxvdb
+uk/DW7PpSZSOK4d6hW/VgZG2Giv+ZucWNnUGjsXCs4/Hhi+IZvAI/Fhtcbi5cwHQ
+RG1hqJYGsqzQ1V4iBshQnZeyjp0KFz95dIifHNsL1YJJy7ODiCE6RLGaXs064DII
+g3XyZdz7hZOFbGTR7GwkYtSyIHLm4iD/xJeCU6cA6FNd+Xb5Fs4SSteCUd0lurH1
+JQLnUF/Xjt2IowuQFnfQ7z2jq+8BRrIgkvCQp5lzteSqPE1i
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/pathLenConstraint6subsubCA11CRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/pathLenConstraint6subsubCA11CRL.pem
new file mode 100644
index 0000000000..f85921377f
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/pathLenConstraint6subsubCA11CRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIB0DCBuQIBATANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEmMCQGA1UEAxMdcGF0aExlbkNvbnN0
+cmFpbnQ2IHN1YnN1YkNBMTEXDTEwMDEwMTA4MzAwMFoXDTMwMTIzMTA4MzAwMFqg
+LzAtMB8GA1UdIwQYMBaAFNOmRV4CnWcWfZSAD3O5hMZbtTG+MAoGA1UdFAQDAgEB
+MA0GCSqGSIb3DQEBCwUAA4IBAQChBEJUKVtETDiFUgxBvxOrmaOjI5NxDTsqyM7m
+HrkP330mh+ij8w4DJ/kOhHxC0c4EBgD7m4oUbkClmqY0LVRNaUlFHdNvftGJMg8k
+Z6Vveak9+Z3q5J7QlE8hdLCGsnQXL3gWoOjH28j3I72fW5tabTdyq7qPjNKkt0xh
+6Sv9jLxHbAWimSvj6xQ3JxO4UweFvDl72eQb+tFhrJpouhlUv13eF6eaQoVKf4to
+KRjRXWEErBEkb1fEXzUISEvQH8gbzVi8raKG/CWbUfFCkBWiqd3Ahr+w0xtmVV9W
+wk0SlM+XlKRQZ4chc+93RMQQBCC6LwN2rBmyaLUPAtzUOTA2
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/pathLenConstraint6subsubCA41CRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/pathLenConstraint6subsubCA41CRL.pem
new file mode 100644
index 0000000000..744befe8d2
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/pathLenConstraint6subsubCA41CRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIB0DCBuQIBATANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEmMCQGA1UEAxMdcGF0aExlbkNvbnN0
+cmFpbnQ2IHN1YnN1YkNBNDEXDTEwMDEwMTA4MzAwMFoXDTMwMTIzMTA4MzAwMFqg
+LzAtMB8GA1UdIwQYMBaAFERapgfP9vPIx0bvZKH1W8E/grxXMAoGA1UdFAQDAgEB
+MA0GCSqGSIb3DQEBCwUAA4IBAQBC2N5Gdl473M+niHqbKh49ObjDXcF0Vbam4NyC
+e5V+1D6PZLlZQDNh0ORk/MnIOYWxchUhXpUnpNfcqReFdP/kIhwlc/Nm3xveO86n
+zWuRP3NrN0N8SRz9YmLkoZvDNL5gagKCwLzeGTTi6lMKqhZnmcDH3ko7k3TOlCO7
+I7f6jEp6ECAHUbDE7UyJC93lYu98YpaM5SV4L8i58/GBpCwGsGirSxws7Xhrgr4L
+Q5UUt5v3GDNW5YiRBEuG1kMlBC4T4gX/TNsdu/8G4RT2hO+SIDOtMNfA4D4TIiY0
+OzDtVVto9DYarCyKlQKHzb6RrFL8sYZJc213B/1O4qRNeJCj
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/pathLenConstraint6subsubsubCA11XCRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/pathLenConstraint6subsubsubCA11XCRL.pem
new file mode 100644
index 0000000000..20392a29a6
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/pathLenConstraint6subsubsubCA11XCRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIB1DCBvQIBATANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEqMCgGA1UEAxMhcGF0aExlbkNvbnN0
+cmFpbnQ2IHN1YnN1YnN1YkNBMTFYFw0xMDAxMDEwODMwMDBaFw0zMDEyMzEwODMw
+MDBaoC8wLTAfBgNVHSMEGDAWgBSD2ri1xp3Iiwh8iz/tGnIl4q8b6jAKBgNVHRQE
+AwIBATANBgkqhkiG9w0BAQsFAAOCAQEAmKydWhVC5ytcp4DjzRPCIZ1myP9MGW97
+diaTIZaFdYYz52k2GD6uHBEL1rALDG6e2DiU+YbmfVJHE5RbehxwtimoWsHOtGLO
+NPTMBPwGoGHIUyR2Ex40oozOrprl5tY3y8uticLLyQQ4meUTrquM7aAEaHHYGwU/
+FziX56TGzO9Tm6ci+jlTrOwHy/uj/AVkkQS9RHk1ytgrG54WSwh/diP9qJrNGvBe
+a8Ditg8ijMjGU9J7NeDJn33G0U8/R08ZOfo0kNqD6mG8Np5meREfiCt5UGfwBOV1
+EVMpK/Liv20yc7PcJT2lKGl1eXZmiZ7TLGV0KJWUWRNy1o4cv6a1ng==
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/pathLenConstraint6subsubsubCA41XCRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/pathLenConstraint6subsubsubCA41XCRL.pem
new file mode 100644
index 0000000000..8674072d00
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/pathLenConstraint6subsubsubCA41XCRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIB1DCBvQIBATANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEqMCgGA1UEAxMhcGF0aExlbkNvbnN0
+cmFpbnQ2IHN1YnN1YnN1YkNBNDFYFw0xMDAxMDEwODMwMDBaFw0zMDEyMzEwODMw
+MDBaoC8wLTAfBgNVHSMEGDAWgBSh7aLzNVSln7xj5kdqUyRsSgxyLDAKBgNVHRQE
+AwIBATANBgkqhkiG9w0BAQsFAAOCAQEAB9m9d1A5cqTJSbdKDkv4KfjAJDsKOqyf
+l1QLXj28Mq43YlLbcoaivW0yL+44zumb02ZKr4v1qd7tP5QLPvv+Ew1pQwC3I8cp
+Hq33iO6VX/ZLVT+vD8wguU/Lwq+oZGFDHMatgq2CGtZ2lJbZ8/I11wR/otTBa9Ne
+krQ+n79avPaAagJoyz+1HKSerOyuyFQ9GP/v64sS3YKy5LJZ/VXC9PlpzU523ENu
+ncwldAgVmNTgxa+kPmdIzUkWLHuuggVltFtRbVsiKyJINgNcmooZhDIHCk6LsXYY
+fBMmxX4xFLGzi1p4sobgJgBkY8YBDiB2xEslOFkwKIgpRfX01T4IZQ==
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/pre2000CRLnextUpdateCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/pre2000CRLnextUpdateCACRL.pem
new file mode 100644
index 0000000000..57afdac2b5
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/pre2000CRLnextUpdateCACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIBzDCBtQIBATANBgkqhkiG9w0BAQsFADBSMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEiMCAGA1UEAxMZcHJlMjAwMCBDUkwg
+bmV4dFVwZGF0ZSBDQRcNOTgwMTAxMTIwMTAwWhcNOTkwMTAxMTIwMTAwWqAvMC0w
+HwYDVR0jBBgwFoAUHqhHnGGAaCixQpopjOYoAymSA8wwCgYDVR0UBAMCAQEwDQYJ
+KoZIhvcNAQELBQADggEBAF6aXbwxymxr0Q8xGFXYqmr5vzuVUREiNHYZMX6N6pqI
+noDmGZN1FgX3FSkcKlsiVYWPRp8VygdNPt2sctjy5OV3J8kAKXzoll7/cHVPWj3b
+uGP8B0Y3sk7oCupwlI/lPwu4Yf5hHcUofyGV4yeOCNn0BHGtOzcfStxbO/K2sG10
+ny8KKkH/+horhqSur2p6z02jq1hcHsQLt293tPfaG6skObtvWgrajbE8rPKYLHiS
+mkh+9Lxtzv/+WOR7YeBgjfg7Eu+gLC+2rrA4C/UiQT27baIzEJkaz3qarL8p4qWR
+BctCVbNVbcrgSA7pT3kavp4Te5NHLg1vRfmkHka1ESk=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy0CACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy0CACRL.pem
new file mode 100644
index 0000000000..faecb30bdc
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy0CACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIBzDCBtQIBATANBgkqhkiG9w0BAQsFADBSMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEiMCAGA1UEAxMZcmVxdWlyZUV4cGxp
+Y2l0UG9saWN5MCBDQRcNMTAwMTAxMDgzMDAwWhcNMzAxMjMxMDgzMDAwWqAvMC0w
+HwYDVR0jBBgwFoAUuezfulIiuLi+aveiEtUnINZnBDUwCgYDVR0UBAMCAQEwDQYJ
+KoZIhvcNAQELBQADggEBAErZavnEyTgHh/ez0LB7FgIfWGuiJB+NDDOQBT31adQW
+nPq1ylOQnG5ALE9askbusXrsw8VHIffm8bBd6JRdFXD9p7S2SzFE5skbRHVs71qr
+ytTzQhoQgYMaWRIYBx5Yg9J0+HSp7rip8FPRoPA34LRoPFmtvGyESnFQCZQMj6Ar
+DrRhaj9MFsicWAObMlIWNOsISnNkDd1s4XeRTMbZ6NgvyZ9Oo6K4S9YsPJypmaWD
+rAtkwSqyyH9yT/Ip7jNFdYfin5oE2i+dN4FCWzQXyr+z0l6dWdw/sjiDWRHVQTkA
+G/F4Ro+qR6bVtq9wt/H1qbauMxOqI+OILjfZARtV/mY=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy0subCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy0subCACRL.pem
new file mode 100644
index 0000000000..6aa182da50
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy0subCACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIBzzCBuAIBATANBgkqhkiG9w0BAQsFADBVMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTElMCMGA1UEAxMccmVxdWlyZUV4cGxp
+Y2l0UG9saWN5MCBzdWJDQRcNMTAwMTAxMDgzMDAwWhcNMzAxMjMxMDgzMDAwWqAv
+MC0wHwYDVR0jBBgwFoAUvmJ4/Tu9bpwLM/I7MqpBCPPliVowCgYDVR0UBAMCAQEw
+DQYJKoZIhvcNAQELBQADggEBABmiY4GL9Z4TINu3A3r2ReqPCf6NBrK838gjCy5y
+yomt33rwH0sDWcdU1YtKMk1iAWpBBon3nBrfylsFDAacz9oqvHBnCSYnO/b3WWLv
+rv80IaJoV6IeZC1CF7vXFcUWt0VnUgVxNzhLWOn0GEu/R/W3HIPUUvpg/0+RVG6c
+WSPwCUAidFY88P195evwDEXaKykQw6LzAlnWMvrL0lviPl4Px/GDUOBp+aZaDz+t
+ktuljb5StVBqdQ4XzemwS12ZkAGw//synft8xDg7c1Pq7csftQ1SFuEvkRaobXYp
+mSWUqhhm5pkRQBxPySvpDiYCOiQbsiTyAAnYkydIeAkX1Ps=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy0subsubCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy0subsubCACRL.pem
new file mode 100644
index 0000000000..58a10374de
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy0subsubCACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIB0jCBuwIBATANBgkqhkiG9w0BAQsFADBYMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEoMCYGA1UEAxMfcmVxdWlyZUV4cGxp
+Y2l0UG9saWN5MCBzdWJzdWJDQRcNMTAwMTAxMDgzMDAwWhcNMzAxMjMxMDgzMDAw
+WqAvMC0wHwYDVR0jBBgwFoAU69iXen96IzUZ5M+XJCcizGenVkkwCgYDVR0UBAMC
+AQEwDQYJKoZIhvcNAQELBQADggEBADaIskgZjnU/+X04sIHx4MxEDo7nCtfaRe6U
+sgYqAEr8u9JCuba3tJgEud2meu8XRkQjagttBmG52vxS4rMiu6Xq79DPWLrdKc3x
++1Tx/wF0iACCp7B0aNfFnrF+Cg/XCQqeVlthn8EP51HmFl+Fu/0szwhHS3N4Nh67
+C7QGGL61vSUqK3V9tRSn7ubAJBJp+BFbG0uLvOlEUbs4iil1f+VlWlXr7WFfKw0F
+k9prVeYBcnyV7X4uLEpQZGk26Qo1X63H+aMUhSehqg9v+M5A8/pq5MKCRwD6S3Ej
+h8DoK5dmqjOrjfBGsS8Z1az1WsVDlC7EO/BFgTXM349j406Rpe4=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy0subsubsubCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy0subsubsubCACRL.pem
new file mode 100644
index 0000000000..34265b943c
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy0subsubsubCACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIB1TCBvgIBATANBgkqhkiG9w0BAQsFADBbMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTErMCkGA1UEAxMicmVxdWlyZUV4cGxp
+Y2l0UG9saWN5MCBzdWJzdWJzdWJDQRcNMTAwMTAxMDgzMDAwWhcNMzAxMjMxMDgz
+MDAwWqAvMC0wHwYDVR0jBBgwFoAUtdsE1sggCC9aQcd4o0SJ2s4ua7owCgYDVR0U
+BAMCAQEwDQYJKoZIhvcNAQELBQADggEBAChXnVu5rzda6+f2NZ1nWf5qyFfBR3qb
+IwWmBIAATlpil8e/RfSjzkNP2SrKhGi7KDseMn7F6MsHlGp8McNb7ryQcd0+PSUV
+m9hELQcBosZrU/XTdctfTx2/k5mfixlK+UYqz3k7rFfG7RhdkhUexaCQFSCgBgxQ
+RhHlXyP8xpSytC0T6w8gIXxUQjK5jEBwr8uVYhadcCsQnx4I35MXmNUb52BohF3s
+p3pOdgGiCn5CmgcqBa1ybBK5qtKKHav8H6dBzNLeuG9giZC/ALpFhzB0fgkGXXFr
+52zZxy2Z8dN7OWAMTski7B2c363+TwXI7Y/Vuq3qIYLxdcFWix3zefY=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy10CACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy10CACRL.pem
new file mode 100644
index 0000000000..efb8976e4e
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy10CACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIBzTCBtgIBATANBgkqhkiG9w0BAQsFADBTMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEjMCEGA1UEAxMacmVxdWlyZUV4cGxp
+Y2l0UG9saWN5MTAgQ0EXDTEwMDEwMTA4MzAwMFoXDTMwMTIzMTA4MzAwMFqgLzAt
+MB8GA1UdIwQYMBaAFBnzTATRX9WAR/P4NCwRgeSYz2ufMAoGA1UdFAQDAgEBMA0G
+CSqGSIb3DQEBCwUAA4IBAQALMOPr5blCX12/wmoUiyjhE3KtwudCyjTFeRfWdh1y
+i74p1lKaCtLS2Ccqelr0fuD98PSUvKa37UrWDtj9fXTCgbe0dB2upF/hfkaCEq9b
+vO5HAuBB/hBwR/F1IsQ7qHwDkQA8WhXRfYsrneWfygUtQUzrji/nz0yF5l+GnWWX
+iMID56whZjh/ch6Uvb9qYJO2p4R1KMFJ7G+xKJQm+TsTx6iYIDP2Q8uedSstLasu
++d68KWDFkf8pAXDDgpp4ldUflWbW7Wa7tSHodxE9D9cee4Db7jmXyV7BqQUxYUwF
+9qj1mjdivXtpeTsNKhX0ASsm4jHKmSjKOYvkEALiALq/
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy10subCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy10subCACRL.pem
new file mode 100644
index 0000000000..125c24847c
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy10subCACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIB0DCBuQIBATANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEmMCQGA1UEAxMdcmVxdWlyZUV4cGxp
+Y2l0UG9saWN5MTAgc3ViQ0EXDTEwMDEwMTA4MzAwMFoXDTMwMTIzMTA4MzAwMFqg
+LzAtMB8GA1UdIwQYMBaAFG4YpmEkD2jbLgYRliY3r8l4KVYGMAoGA1UdFAQDAgEB
+MA0GCSqGSIb3DQEBCwUAA4IBAQCZ/4QYxzZaC5BIcXVOv0Pa0KrQdGUx6Ae7knJP
+df/x+IrPcwEHZeD8dQUYnWT7EOvcJBRS3HaI8xD0+n3IFR1+ayaQuV2UCQSu2WbA
+FXx+HRApe5lWOVZsG9vg7V2ccF+8u4qIaLxxTlfOGL3m1FyK8/YXhDfNeOwRukNG
+JxHdfhaqHFtofV3Spa2HcUwKbNAEtu4SAcW55ZV0q/W1tbOBeYwBMNoNMKEVPf8B
+FhJh0ID0xyoFdFIDsj0uNkvNRazAjnUGtz0m7/PZE22qvPH2Bt9gII5UEIbWY4Nv
++Kz6TiXfjZZkl5vR4DHy6qOfQxVDbEuehfRxsBLngKLXzW3r
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy10subsubCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy10subsubCACRL.pem
new file mode 100644
index 0000000000..7754862b2e
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy10subsubCACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIB0zCBvAIBATANBgkqhkiG9w0BAQsFADBZMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEpMCcGA1UEAxMgcmVxdWlyZUV4cGxp
+Y2l0UG9saWN5MTAgc3Vic3ViQ0EXDTEwMDEwMTA4MzAwMFoXDTMwMTIzMTA4MzAw
+MFqgLzAtMB8GA1UdIwQYMBaAFFhQTw7y/nIkpNB3P6CWLHe1JOghMAoGA1UdFAQD
+AgEBMA0GCSqGSIb3DQEBCwUAA4IBAQBc9QUsQJu8eEkhD/bB2k137z1Tw2E+SOGM
+InCgU/5FjqgVS8frk2POJXSyTdZonYpJvj3fEuXLp/eViZ0+V3oe31vnFtzeQ9qv
+//rOvJd0b3yOtLFAB58oF5yms/4Zj2ekeWU38HUrQ8lVrT3p0nj7V49ENlaiXwVD
+vhsaTShUxHnVPb5W4nX/uvl6PUWXZLwzWvr3foaAbf0rgV3eWhpjspPlE0ajF0Tk
+ButtK76M4AJocWeWr1AupATbtGU+vvi+I5D7YrBGn7qxF9BvzMiq65H4YHnNfiiF
+iVtgU+GXZxft7+hmVYgX5eQKcDhSQdKKB05U8r2Q5ZRFq8o+b/Vr
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy10subsubsubCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy10subsubsubCACRL.pem
new file mode 100644
index 0000000000..caac766953
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy10subsubsubCACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIB1jCBvwIBATANBgkqhkiG9w0BAQsFADBcMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEsMCoGA1UEAxMjcmVxdWlyZUV4cGxp
+Y2l0UG9saWN5MTAgc3Vic3Vic3ViQ0EXDTEwMDEwMTA4MzAwMFoXDTMwMTIzMTA4
+MzAwMFqgLzAtMB8GA1UdIwQYMBaAFJaMcfwVqDvO2cT4w9BfaXF86ABLMAoGA1Ud
+FAQDAgEBMA0GCSqGSIb3DQEBCwUAA4IBAQBzlq+27lis3jgVun42HmkOAJ9zzdIt
+iyptjiWBCLV5lLA3gbiuroQbr6cKYWhP/qQ91g7MgSzvq/cVwCudSeqIOCJZUwRu
+Kpu6AKkK77GfWA2/GC4ZtIhsHIyA0P0MTa2ygmtxIn0ufluNzgEognWxF4Kdd0F6
+g1sJL547W8byiNlD6KtZ8Oe9PQOr1tevTWvWgSRj2bitAfcwrEBSsyG1eu/Yyeun
+opopG2H28TuBcitgZA7mjicFqzibabUUA+hqZ/X5cL+JiR+84VWc3o5fPFwpdAWr
+09gYI6qG+5O4q7tIyb8d8dIVO+FcKV+mkbZDpFu+3VWRFY4GkBjq98uJ
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy2CACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy2CACRL.pem
new file mode 100644
index 0000000000..acf0e69c78
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy2CACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIBzDCBtQIBATANBgkqhkiG9w0BAQsFADBSMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEiMCAGA1UEAxMZcmVxdWlyZUV4cGxp
+Y2l0UG9saWN5MiBDQRcNMTAwMTAxMDgzMDAwWhcNMzAxMjMxMDgzMDAwWqAvMC0w
+HwYDVR0jBBgwFoAUNqnZ+6o4L6D3TDvZhZ2aFaMtqccwCgYDVR0UBAMCAQEwDQYJ
+KoZIhvcNAQELBQADggEBAD8YETFOSpFWhTbHdjwH+oNciH7CRTbs6UkICQVsnIN4
+xt8m4Pvho9VeU9KNeIvmcaKaJk8HrxbhKlluWFUS/IMk+U6SQ3QSdnxLtJdvC0JT
+iARR9dKR5YX/G0JHJaAO3ULSu8Gaj093TFfQPs2ALcfg5G6BbQmxJ6qCwp/IZ61T
+x71148Lqbb0k2JVZgnnPkhe1k/7no2rjmvu4Zp0BuFkQIwceN908LGSZmuVf09R9
+6FUkYhY5s6tM0rHLLS0JGgCuYyl6RTeD/OemNh4QfA6z6N+WmvAUwzbRWtm/gDOx
+hQq+8Qcc+HErYcZfSg9cgJqRGb1J8KGGd+92uK1wHmo=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy2subCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy2subCACRL.pem
new file mode 100644
index 0000000000..426415e452
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy2subCACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIBzzCBuAIBATANBgkqhkiG9w0BAQsFADBVMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTElMCMGA1UEAxMccmVxdWlyZUV4cGxp
+Y2l0UG9saWN5MiBzdWJDQRcNMTAwMTAxMDgzMDAwWhcNMzAxMjMxMDgzMDAwWqAv
+MC0wHwYDVR0jBBgwFoAUIHf+TDCN4rNRHY+w9xx/HYOYDkcwCgYDVR0UBAMCAQEw
+DQYJKoZIhvcNAQELBQADggEBAHnk7z+QEkfo+N/q7+bAHDhYCm1ax7Vg1PSs/3WE
+OvoTOp65ghyIX7UdKdmUTpefa89C2PRORNB06xIYzQSeCkCoq7bpTW0hzMfPweY3
+MwlEqnllWldcWgczSOxsomOjJ6os81onh9ZyICGbIZLPL+HZtfPpXKdyw7riGAm3
+h3V0pHznhgcm4XEpe6iHovoFbdmWs73G0vSjLnTyDmSBVjAiEVnZLut10BGyjCRq
+ZQjLcXfXtF1aFymk1p/eN5QLMaNZ59uoGNgsjRQmod7H1Lezp0r53yA1bg9P7yXU
+RsY/pvoiI9j5t3svgZWKC5wuTmJ7nMTwlqaZDS2nWAw3/TE=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy4CACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy4CACRL.pem
new file mode 100644
index 0000000000..c96cbf6314
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy4CACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIBzDCBtQIBATANBgkqhkiG9w0BAQsFADBSMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEiMCAGA1UEAxMZcmVxdWlyZUV4cGxp
+Y2l0UG9saWN5NCBDQRcNMTAwMTAxMDgzMDAwWhcNMzAxMjMxMDgzMDAwWqAvMC0w
+HwYDVR0jBBgwFoAUzdHczNQxYwcsXTaxD42edb5LXmMwCgYDVR0UBAMCAQEwDQYJ
+KoZIhvcNAQELBQADggEBACOdVMcZUqu3gJlw13ZK7/NcOtSAuvEOVHfjChSXRFkp
+gVhiUJqPQYg0fs6eORxMRc/EyKUTNeKQE/08Q0pN69TSShhHJgrbI0qL+z+1l+OA
+615L/Z658Nv7uKLt0nk/RkguMQw2xIlUEKvC1S/MQAcJAiBKPnSA7fCb93ph053o
+ZOVorML758OWTjvXGykpiJMbxgdS1MQpSRrU3FGST+auQhI4Qet5QpMlA9nfHwgB
+l4lUkF73iHwV8LNHI8t/Pnd5LkKd6XWiDrQRAzhrh/UhmcspUJyFlLIxthO2Az6n
+/FIQMAHm0mvyrgmmZSsw8HCseOgTNCnQEEnWLodqabI=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy4subCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy4subCACRL.pem
new file mode 100644
index 0000000000..332b2ec9e5
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy4subCACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIBzzCBuAIBATANBgkqhkiG9w0BAQsFADBVMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTElMCMGA1UEAxMccmVxdWlyZUV4cGxp
+Y2l0UG9saWN5NCBzdWJDQRcNMTAwMTAxMDgzMDAwWhcNMzAxMjMxMDgzMDAwWqAv
+MC0wHwYDVR0jBBgwFoAUfe8OlBe79qeX5tgiSENIrLPuuo0wCgYDVR0UBAMCAQEw
+DQYJKoZIhvcNAQELBQADggEBALIg2yo0C0OaDGoUWWB1PeKkFzO29Zrz7os0w6Ta
+c4W0ufJ/V7W6GTWtNjN2zoVhWe1DJzAivJejwI89ES8kpKJvQ0FIqSmeKk6v7qM6
+gZuhVsvemsw7mb2LVd7t6ismBYCbYTBnoHc/dW+zgHLXNsjHA7aIq3zEzeiJPxgy
+qf4a48BXm8WKQVFLAJv28l/B3nanW2+rYMnEMUBlI7ytGE/gcZ5WB3x8i/NxQAa1
+mBBrHVFyNLJX1pxCmg1myP9dXj1ljlSwRHVgxfYhVcfl5H72cJg/zTQNELpqjlqO
+kM8wXuKpxsFsJpGBOfl3zjeCeFmjOyucmB/CZT3L9h+H9sY=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy4subsubCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy4subsubCACRL.pem
new file mode 100644
index 0000000000..ab40c50e73
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy4subsubCACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIB0jCBuwIBATANBgkqhkiG9w0BAQsFADBYMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEoMCYGA1UEAxMfcmVxdWlyZUV4cGxp
+Y2l0UG9saWN5NCBzdWJzdWJDQRcNMTAwMTAxMDgzMDAwWhcNMzAxMjMxMDgzMDAw
+WqAvMC0wHwYDVR0jBBgwFoAUqerm056wCZev5/4eLhAyoRBnTIYwCgYDVR0UBAMC
+AQEwDQYJKoZIhvcNAQELBQADggEBAD08KCdOiCGTM5PYyFE+HRlYaBd1VnGIFiAb
+fCdTAlEmK+/NxqlrBGqz1zx6O09X4u/XsmvwI5Pi9ujsyxN/aHR16/JBWfUEPKDg
+2a7XOoAyKWO623Z78xWD4DbfQo32vsyDpe2ydCnYiX7qPf3muC7KNROeHMt1QwK0
+/T89l2NJmPBE5+sSCMMZ+8tY1GQgVTeNczO8zzoYcG5XZrKs503sfLvISvcCpre+
+l/LeByAhkJpr+/wlR8+eTrUMpZIhnB8x9uk9hi0YBBbdKmAR8Sqc4U/2yUPFp6ue
+FyF/txO3HXmTNYLFSYo0+G8uEkQCOFQn7BHJOybYH6ertxgKhdQ=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy4subsubsubCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy4subsubsubCACRL.pem
new file mode 100644
index 0000000000..986abff429
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy4subsubsubCACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIB1TCBvgIBATANBgkqhkiG9w0BAQsFADBbMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTErMCkGA1UEAxMicmVxdWlyZUV4cGxp
+Y2l0UG9saWN5NCBzdWJzdWJzdWJDQRcNMTAwMTAxMDgzMDAwWhcNMzAxMjMxMDgz
+MDAwWqAvMC0wHwYDVR0jBBgwFoAUFLvRJvSegTyLDhLP2XsVsizcoyEwCgYDVR0U
+BAMCAQEwDQYJKoZIhvcNAQELBQADggEBAC0JNUPn/JJeMGuu1cv0PUV36H5zZk3z
+KXq6TT6CRatXHsIRl7bwvsJ2wnkkT4QUM0xeVl8Q50Thm/P81vxVb48w3ahA/mm8
+QtxJwkPK9VPV+jkfAW5vcTVzJZlr9EWhuYgfqC0KdUUiE2omtydtinQhGdI0ooey
+U5XP6voQwf8BKCYOgXxQNJtRE1+W96SifSUQr07fN7gXUp2MgZYwp8QPjFZrX5dH
+FqgmhSv6rdjO9knE/guw21FzA0F5rM6HjNuxre3qont7vlzpI9sx7VzrR4J87meb
+DhRujiMvn1Ge6qwNPGgSnd8P/LsA8kltmewk93W94u74GqfwQCtFtxk=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy5CACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy5CACRL.pem
new file mode 100644
index 0000000000..6b45ed261f
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy5CACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIBzDCBtQIBATANBgkqhkiG9w0BAQsFADBSMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEiMCAGA1UEAxMZcmVxdWlyZUV4cGxp
+Y2l0UG9saWN5NSBDQRcNMTAwMTAxMDgzMDAwWhcNMzAxMjMxMDgzMDAwWqAvMC0w
+HwYDVR0jBBgwFoAUu5GDq66u3lzY4PKQPFz6ny7hOWgwCgYDVR0UBAMCAQEwDQYJ
+KoZIhvcNAQELBQADggEBAMz51cm+tJdOQUKRaISFOoTqH2PSV3W1i/VLNoo60huI
+wSIOg0YNxG2wXxFH5UdH+SdRacD3qHlAKKyTsWgvhqZgdgbgieS4Nc1CpGnyxeXk
+FCdGt4OJ6mEToXFqJayWwR25ckoJp8k7gpseERoqgDCTPvKjp+1yrznlX8VWTKya
+dgVr+LgSLzCHY8HYekoKcQRbFWbnMBw5K7VE06CLJJIx9kHgDOU+Zqxjy+cilIIb
+HxMmsrxCwKW+ARA/LBiLpn26m9SC/fHstIopRML+1zkbXCoad2VwB9GyvLwzAoqn
+MwJuDzT0G/MZCB4n/s5J/urXFkYBVCQpivlfX9tNUU0=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy5subCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy5subCACRL.pem
new file mode 100644
index 0000000000..29a2c67261
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy5subCACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIBzzCBuAIBATANBgkqhkiG9w0BAQsFADBVMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTElMCMGA1UEAxMccmVxdWlyZUV4cGxp
+Y2l0UG9saWN5NSBzdWJDQRcNMTAwMTAxMDgzMDAwWhcNMzAxMjMxMDgzMDAwWqAv
+MC0wHwYDVR0jBBgwFoAUN9O/3txQx6/IiuiSsMRIYfA6BAEwCgYDVR0UBAMCAQEw
+DQYJKoZIhvcNAQELBQADggEBANAIgl9rVP+LLEYRL1+oori60bQTlB1+1LBFItkp
+7BfodwaAGRmWO4GV6phJzxjpi1aKVGuCg7vlwzS+/ns0mlAaghPxpelwd/KACsv1
+AnrbnVR7AW9z6/ipRID0K1V+8EJmjUdD05BjXoyDKP5Jg2v1joSB306Lv7EDGqBh
+babIXd9ZVrMJBFhW8Zp9A/c/P72LrfRkDBi5rhxI8lJeDUPTfQVUGn0nh2MHx2LQ
+vDd++QtcVY+4tPwkiNJiVNBu5dec8DBuGwNcxcZ2FnpExVglJuFf9AXlXPlXs1Ri
+6+WLqQ6sFjS8R0NhYyILqm/JtXO9mKzXAwa53nCXPytwtPk=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy5subsubCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy5subsubCACRL.pem
new file mode 100644
index 0000000000..9370cde1bf
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy5subsubCACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIB0jCBuwIBATANBgkqhkiG9w0BAQsFADBYMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEoMCYGA1UEAxMfcmVxdWlyZUV4cGxp
+Y2l0UG9saWN5NSBzdWJzdWJDQRcNMTAwMTAxMDgzMDAwWhcNMzAxMjMxMDgzMDAw
+WqAvMC0wHwYDVR0jBBgwFoAU+IIvef+0fggC21uvMp3kNWG1bBswCgYDVR0UBAMC
+AQEwDQYJKoZIhvcNAQELBQADggEBABWp0q1/e5b14+ebJrBOiLc2iTt8KlT0ycPq
+EjyoPsQAO6krSC8L+PdSOSv4if2Udf9Xrgrk5zvDIBkVrv7+QlR0A/5kBRukZimS
+L0Q5lCJjpBmvgZRu/c5bYm32iIfJtwwEgXN4fNh3QESi+KGB+IopD9+/TxV8nAWn
+bTqJ3YPGM/tlmkpLHQD7lt7PAHe/ofrkJc8L6ircKUzmZ4vce3HQtQ79rlbyOLxW
+bN7O9mfUymz4SN/gsnll4HpJKrSBTeINssUcCUwpZ6p1IJ0/tmZHpF39HZo1W0ts
+ZB6DWqnOL5lWsw3x/QiKVgvfPjKA1mvqsXVhy/xvNFGT3kyNcC8=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy5subsubsubCACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy5subsubsubCACRL.pem
new file mode 100644
index 0000000000..db6c01924b
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy5subsubsubCACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIB1TCBvgIBATANBgkqhkiG9w0BAQsFADBbMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTErMCkGA1UEAxMicmVxdWlyZUV4cGxp
+Y2l0UG9saWN5NSBzdWJzdWJzdWJDQRcNMTAwMTAxMDgzMDAwWhcNMzAxMjMxMDgz
+MDAwWqAvMC0wHwYDVR0jBBgwFoAU+mK6vX5eX98fuge+H3k3gtz8EygwCgYDVR0U
+BAMCAQEwDQYJKoZIhvcNAQELBQADggEBAGQii0uxuUm9ok8RNEseRwlN5+lK2R2A
+VZUJ+vZ/O+4ye3x1RIcZ38oWF8zrGZqp6Afos6JbZLSUAGRrbmYowxXCT3vV3yA5
+SBdIoXsByE2Kh6dyeNbB/jDFRNP5rbKcwJJGqXrfnRbHXOOizHPMmWTBscj9lit6
+HIzQhlURn1bHP2GcQ17uBGSYpSVhVV247dlt4+HgReog9NC8D26xPVdnaodtBYvp
+oknWHAcjehSi5kjt4/DJ0Fym3yHGbwpkKWT0A7RXK6cCRWfvJDnGu5vrIKigQo7q
+z4ZIKE1aO2tXJ98dc8M6Z6u1MZDnn3n/9JUAhZPtZ9Gb4eEUU+qgrRk=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy7CACRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy7CACRL.pem
new file mode 100644
index 0000000000..99d47f5020
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy7CACRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIBzDCBtQIBATANBgkqhkiG9w0BAQsFADBSMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEiMCAGA1UEAxMZcmVxdWlyZUV4cGxp
+Y2l0UG9saWN5NyBDQRcNMTAwMTAxMDgzMDAwWhcNMzAxMjMxMDgzMDAwWqAvMC0w
+HwYDVR0jBBgwFoAUbDGXATUg3ts15QppWFmIZMwhzkowCgYDVR0UBAMCAQEwDQYJ
+KoZIhvcNAQELBQADggEBAHC+EDnf6+cnW0GTLMwWoISAn/jJ++FGGwmSv7O8st49
+YmlngcgSQdE1CmcLFM8Lsv390rQwtm57E89aLHSpSbVXvAS/ZvyS5yRgoxGJLw5r
+ulwKqAmjzrXPB/0A7mjLwXVxLBZTAqJgnIFW+qE679S4SKLWOsc66HRFsdQmFbwe
+BiF1/V5PbM83sNyN+boyBm1w5YrasEIWLajc+s+gF1dbQTKuDziD52azMegWvfIO
+xqixHCdK+CpIFObodAnW2gPHWTicXYH+ZwagtqQbI2EM7SdnbvQQe68frsfqK1k/
+SGXl0LfQl3/GmYynOg46FDf53/oRxz0bwsEa8XBpXpY=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy7subCARE2CRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy7subCARE2CRL.pem
new file mode 100644
index 0000000000..dd8aaf35a7
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy7subCARE2CRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIB0jCBuwIBATANBgkqhkiG9w0BAQsFADBYMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEoMCYGA1UEAxMfcmVxdWlyZUV4cGxp
+Y2l0UG9saWN5NyBzdWJDQVJFMhcNMTAwMTAxMDgzMDAwWhcNMzAxMjMxMDgzMDAw
+WqAvMC0wHwYDVR0jBBgwFoAU51wljn6qTHeDe8PqadbHojThNFkwCgYDVR0UBAMC
+AQEwDQYJKoZIhvcNAQELBQADggEBACgCaW8g9ZHgkL+vcByM70Ao9B8BhlunjYHK
+905EiAQQ3OqjJzEE5YRp0uhrbChY1xoHKAjE45VIhK1aADKyV5Wt+ugBAoFLAPcN
+9FQwvTEBjUwZPCQaV54hyt/PrDBh8GJxnTpLnEb/+gl/hu2P1tDD0Zqb4BPoOxur
+abZ0PpzSCPHJdQq75RX/KZCEkaOsa4wHslFHXJXlLLh5vRqvrM23NDsY9lsNb9+3
+3mSVngQWhqVJNdFsMJisyzq88Iv3cSmgzD4oowE05khNWWhpbgxtdMYGWVqN2qEg
+EDI3oExT5CeehzLWuI7dnhKZVJl7y68n1s7KHKJtMb9vnGsiNZ4=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy7subsubCARE2RE4CRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy7subsubCARE2RE4CRL.pem
new file mode 100644
index 0000000000..cce599a5fb
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy7subsubCARE2RE4CRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIB2DCBwQIBATANBgkqhkiG9w0BAQsFADBeMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEuMCwGA1UEAxMlcmVxdWlyZUV4cGxp
+Y2l0UG9saWN5NyBzdWJzdWJDQVJFMlJFNBcNMTAwMTAxMDgzMDAwWhcNMzAxMjMx
+MDgzMDAwWqAvMC0wHwYDVR0jBBgwFoAUbv+NCItm/pule/tkM2XqA5VIlJgwCgYD
+VR0UBAMCAQEwDQYJKoZIhvcNAQELBQADggEBADsVgmLjUlCjkqyM30ho6gvaBfoZ
+3IEMbL01Z1YWCVL/M7dkcp2H98jCurGWlEQCqlx8gXZRrrO8u7rtpHKu0oUuCcrC
+6bxljOwBRgdv0TnNB3k+FrXcaBT7/SJtGhLwjh0smLC69eI42quLSEXXHhlpmYiJ
+1cOQwPCtd+KHbeDMoKi6j7HbFD3OR7jH+OsbnwMpWdlyhDeZlXXh5vbLXtN/j6VH
+9TWdi/Tt6z049i5YcCL2l5Kq9Mp/Y2DRpLQAycHpYL+/U8yOyUq3A3MYO/fdWo1k
+H0nSPreisWgBkSD/u11jR/hPbEH9SXv2nflBaRg/9kSoAbuDknlvrLGxwbQ=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy7subsubsubCARE2RE4CRL.pem b/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy7subsubsubCARE2RE4CRL.pem
new file mode 100644
index 0000000000..bd1656505c
--- /dev/null
+++ b/lib/public_key/test/pkits_SUITE_data/pkits/crls/requireExplicitPolicy7subsubsubCARE2RE4CRL.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIB2zCBxAIBATANBgkqhkiG9w0BAQsFADBhMQswCQYDVQQGEwJVUzEfMB0GA1UE
+ChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTExMC8GA1UEAxMocmVxdWlyZUV4cGxp
+Y2l0UG9saWN5NyBzdWJzdWJzdWJDQVJFMlJFNBcNMTAwMTAxMDgzMDAwWhcNMzAx
+MjMxMDgzMDAwWqAvMC0wHwYDVR0jBBgwFoAUeyxRYTEVrawsa6m+OzsYGpKqf0Qw
+CgYDVR0UBAMCAQEwDQYJKoZIhvcNAQELBQADggEBAKC0+mcSVMrZw/Yabdg8xvnX
+VJrufKnwa2sdt5WXbKX73DCh2OvTm5gJg61AkjghRN4W1VfHpQqc6lsrWHlFK+6r
+GbsL/og+JwfDgiHr+xLf9hmxlS2Uu+TK1HYUVGw/VNLoLPqre1tq4ag4W7re2Z3g
+BBZ8OyS0aUcMYpI1rp9/+PWOYo/9cEDFK2zlsIazYl0Nk8Jz8xWVzFP4gf5RXb5i
+ejUZI578baWPUfUUnEQMSqiKJmHNxPyY6REVUEFkMDu5dOlCu0GfsLBw61am3hGQ
+XvILnAB1SRdyO5uNlJLkRh9EB5aUDRUC5HydfiaSTzb0gkzdKgCRjUUsUjovcPg=
+-----END X509 CRL-----
diff --git a/lib/public_key/test/public_key_SUITE.erl b/lib/public_key/test/public_key_SUITE.erl
index a91dcfa029..ea48479f0b 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-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -20,85 +20,19 @@
%%
-module(public_key_SUITE).
+-include_lib("common_test/include/ct.hrl").
+-include_lib("public_key/include/public_key.hrl").
+
%% Note: This directive should only be used in test suites.
-compile(export_all).
-%%-include_lib("common_test/include/ct.hrl").
--include_lib("test_server/include/test_server.hrl").
-
--include_lib("public_key/include/public_key.hrl").
-
-define(TIMEOUT, 120000). % 2 min
-%% 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) ->
- try crypto:start() of
- ok ->
- 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) ->
- 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(_TestCase, Config0) ->
- 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
+%% Common Test interface functions -----------------------------------
%%--------------------------------------------------------------------
-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() ->
@@ -107,11 +41,11 @@ all() ->
{group, ssh_public_key_decode_encode},
encrypt_decrypt,
{group, sign_verify},
- pkix, pkix_path_validation].
+ pkix, pkix_countryname, pkix_path_validation].
groups() ->
[{pem_decode_encode, [], [dsa_pem, rsa_pem, encrypted_pem,
- dh_pem, cert_pem]},
+ dh_pem, cert_pem, pkcs10_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,
@@ -119,30 +53,46 @@ groups() ->
ssh_openssh_public_key_long_header]},
{sign_verify, [], [rsa_sign_verify, dsa_sign_verify]}
].
+%%-------------------------------------------------------------------
+init_per_suite(Config) ->
+ try crypto:start() of
+ ok ->
+ Config
+ catch _:_ ->
+ {skip, "Crypto did not start"}
+ end.
+end_per_suite(_Config) ->
+ application:stop(crypto).
+
+%%-------------------------------------------------------------------
init_per_group(_GroupName, Config) ->
Config.
end_per_group(_GroupName, Config) ->
Config.
+%%-------------------------------------------------------------------
+init_per_testcase(_TestCase, Config0) ->
+ Config = lists:keydelete(watchdog, 1, Config0),
+ Dog = ct:timetrap(?TIMEOUT),
+ [{watchdog, Dog} | Config].
-%% Test cases starts here.
+end_per_testcase(_TestCase, _Config) ->
+ ok.
+%%--------------------------------------------------------------------
+%% Test Cases --------------------------------------------------------
%%--------------------------------------------------------------------
-app(doc) ->
- "Test that the public_key app file is ok";
-app(suite) ->
- [];
+app() ->
+ [{doc, "Test that the public_key app file is ok"}].
app(Config) when is_list(Config) ->
- ok = test_server:app_test(public_key).
+ ok = ?t:app_test(public_key).
%%--------------------------------------------------------------------
-dsa_pem(doc) ->
- [""];
-dsa_pem(suite) ->
- [];
+dsa_pem() ->
+ [{doc, "DSA PEM-file decode/encode"}].
dsa_pem(Config) when is_list(Config) ->
Datadir = ?config(data_dir, Config),
@@ -164,10 +114,8 @@ dsa_pem(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
-rsa_pem(doc) ->
- [""];
-rsa_pem(suite) ->
- [];
+rsa_pem() ->
+ [{doc, "RSA PEM-file decode/encode"}].
rsa_pem(Config) when is_list(Config) ->
Datadir = ?config(data_dir, Config),
[{'RSAPrivateKey', DerRSAKey, not_encrypted} = Entry0 ] =
@@ -201,10 +149,8 @@ rsa_pem(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
-encrypted_pem(doc) ->
- [""];
-encrypted_pem(suite) ->
- [];
+encrypted_pem() ->
+ [{doc, "Encrypted PEM-file decode/encode"}].
encrypted_pem(Config) when is_list(Config) ->
Datadir = ?config(data_dir, Config),
@@ -234,10 +180,8 @@ encrypted_pem(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
-dh_pem(doc) ->
- [""];
-dh_pem(suite) ->
- [];
+dh_pem() ->
+ [{doc, "DH parametrs PEM-file decode/encode"}].
dh_pem(Config) when is_list(Config) ->
Datadir = ?config(data_dir, Config),
[{'DHParameter', DerDH, not_encrypted} = Entry] =
@@ -249,12 +193,41 @@ dh_pem(Config) when is_list(Config) ->
DHParameter = public_key:pem_entry_decode(Entry),
Entry = public_key:pem_entry_encode('DHParameter', DHParameter).
-
+
+%%--------------------------------------------------------------------
+
+pkcs10_pem() ->
+ [{doc, "PKCS-10 PEM-file decode/encode"}].
+pkcs10_pem(Config) when is_list(Config) ->
+ Datadir = ?config(data_dir, Config),
+ [{'CertificationRequest', DerPKCS10, not_encrypted} = Entry] =
+ erl_make_certs:pem_to_der(filename:join(Datadir, "req.pem")),
+
+ erl_make_certs:der_to_pem(filename:join(Datadir, "new_req.pem"), [Entry]),
+
+ PKCS10 = public_key:der_decode('CertificationRequest', DerPKCS10),
+ PKCS10 = public_key:pem_entry_decode(Entry),
+
+ Entry = public_key:pem_entry_encode('CertificationRequest', PKCS10).
+
+%%--------------------------------------------------------------------
+pkcs7_pem() ->
+ [{doc, "PKCS-7 PEM-file decode/encode"}].
+pkcs7_pem(Config) when is_list(Config) ->
+ Datadir = ?config(data_dir, Config),
+ [{'ContentInfo', DerPKCS7, not_encrypted} = Entry] =
+ erl_make_certs:pem_to_der(filename:join(Datadir, "pkcs7_cert.pem")),
+
+ erl_make_certs:der_to_pem(filename:join(Datadir, "new_pkcs7_cert.pem"), [Entry]),
+
+ PKCS7 = public_key:der_decode('ContentInfo', DerPKCS7),
+ PKCS7 = public_key:pem_entry_decode(Entry),
+
+ Entry = public_key:pem_entry_encode('ContentInfo', PKCS7).
+
%%--------------------------------------------------------------------
-cert_pem(doc) ->
- [""];
-cert_pem(suite) ->
- [];
+cert_pem() ->
+ [{doc, "Certificate PEM-file decode/encode"}].
cert_pem(Config) when is_list(Config) ->
Datadir = ?config(data_dir, Config),
@@ -280,10 +253,8 @@ cert_pem(Config) when is_list(Config) ->
[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() ->
+ [{doc, "ssh rsa public key decode/encode"}].
ssh_rsa_public_key(Config) when is_list(Config) ->
Datadir = ?config(data_dir, Config),
@@ -308,10 +279,8 @@ ssh_rsa_public_key(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
-ssh_dsa_public_key(doc) ->
- "";
-ssh_dsa_public_key(suite) ->
- [];
+ssh_dsa_public_key() ->
+ [{doc, "ssh dsa public key decode/encode"}].
ssh_dsa_public_key(Config) when is_list(Config) ->
Datadir = ?config(data_dir, Config),
@@ -335,10 +304,8 @@ ssh_dsa_public_key(Config) when is_list(Config) ->
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() ->
+ [{doc, "Test comment header and rsa key"}].
ssh_rfc4716_rsa_comment(Config) when is_list(Config) ->
Datadir = ?config(data_dir, Config),
@@ -353,10 +320,8 @@ ssh_rfc4716_rsa_comment(Config) when is_list(Config) ->
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() ->
+ [{doc, "Test comment header and dsa key"}].
ssh_rfc4716_dsa_comment(Config) when is_list(Config) ->
Datadir = ?config(data_dir, Config),
@@ -375,10 +340,8 @@ ssh_rfc4716_dsa_comment(Config) when is_list(Config) ->
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() ->
+ [{doc, "Test another header value than comment"}].
ssh_rfc4716_rsa_subject(Config) when is_list(Config) ->
Datadir = ?config(data_dir, Config),
@@ -397,17 +360,26 @@ ssh_rfc4716_rsa_subject(Config) when is_list(Config) ->
public_key:ssh_decode(Encoded, public_key).
%%--------------------------------------------------------------------
-ssh_known_hosts(doc) ->
- "";
-ssh_known_hosts(suite) ->
- [];
+ssh_known_hosts() ->
+ [{doc, "ssh known hosts file encode/decode"}].
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 =
+ [{#'RSAPublicKey'{}, Attributes1}, {#'RSAPublicKey'{}, Attributes2},
+ {#'RSAPublicKey'{}, Attributes3}, {#'RSAPublicKey'{}, Attributes4}] = Decoded =
public_key:ssh_decode(SshKnownHosts, known_hosts),
+ Comment1 = undefined,
+ Comment2 = "[email protected]",
+ Comment3 = "Comment with whitespaces",
+ Comment4 = "[email protected] Comment with whitespaces",
+
+ Comment1 = proplists:get_value(comment, Attributes1, undefined),
+ Comment2 = proplists:get_value(comment, Attributes2),
+ Comment3 = proplists:get_value(comment, Attributes3),
+ Comment4 = proplists:get_value(comment, Attributes4),
+
Value1 = proplists:get_value(hostnames, Attributes1, undefined),
Value2 = proplists:get_value(hostnames, Attributes2, undefined),
true = (Value1 =/= undefined) and (Value2 =/= undefined),
@@ -417,66 +389,82 @@ ssh_known_hosts(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
-ssh1_known_hosts(doc) ->
- "";
-ssh1_known_hosts(suite) ->
- [];
+ssh1_known_hosts() ->
+ [{doc, "ssh (ver 1) known hosts file encode/decode"}].
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),
+ [{#'RSAPublicKey'{}, Attributes1}, {#'RSAPublicKey'{}, Attributes2},{#'RSAPublicKey'{}, Attributes3}]
+ = 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),
+ Comment ="dhopson@VMUbuntu-DSH comment with whitespaces",
+ Comment = proplists:get_value(comment, Attributes3),
+
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() ->
+ [{doc, "ssh authorized keys file encode/decode"}].
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 =
+ [{#'RSAPublicKey'{}, Attributes1}, {{_, #'Dss-Parms'{}}, Attributes2},
+ {#'RSAPublicKey'{}, Attributes3}, {{_, #'Dss-Parms'{}}, Attributes4}
+ ] = Decoded =
public_key:ssh_decode(SshAuthKeys, auth_keys),
Value1 = proplists:get_value(options, Attributes1, undefined),
true = Value1 =/= undefined,
+ Comment1 = Comment2 = "dhopson@VMUbuntu-DSH",
+ Comment3 = Comment4 ="dhopson@VMUbuntu-DSH comment with whitespaces",
+
+ Comment1 = proplists:get_value(comment, Attributes1),
+ Comment2 = proplists:get_value(comment, Attributes2),
+ Comment3 = proplists:get_value(comment, Attributes3),
+ Comment4 = proplists:get_value(comment, Attributes4),
+
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() ->
+ [{doc, "ssh (ver 1) authorized keys file encode/decode"}].
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 =
+ [{#'RSAPublicKey'{}, Attributes1},
+ {#'RSAPublicKey'{}, Attributes2}, {#'RSAPublicKey'{}, Attributes3},
+ {#'RSAPublicKey'{}, Attributes4}, {#'RSAPublicKey'{}, Attributes5}] = Decoded =
public_key:ssh_decode(SshAuthKeys, auth_keys),
- Value1 = proplists:get_value(bits, Attributes1, undefined),
- Value2 = proplists:get_value(bits, Attributes2, undefined),
+ Value1 = proplists:get_value(bits, Attributes2, undefined),
+ Value2 = proplists:get_value(bits, Attributes3, undefined),
true = (Value1 =/= undefined) and (Value2 =/= undefined),
+ Comment2 = Comment3 = "dhopson@VMUbuntu-DSH",
+ Comment4 = Comment5 ="dhopson@VMUbuntu-DSH comment with whitespaces",
+
+ undefined = proplists:get_value(comment, Attributes1, undefined),
+ Comment2 = proplists:get_value(comment, Attributes2),
+ Comment3 = proplists:get_value(comment, Attributes3),
+ Comment4 = proplists:get_value(comment, Attributes4),
+ Comment5 = proplists:get_value(comment, Attributes5),
+
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() ->
+ [{doc, "Test that emty lines and lines starting with # are ignored"}].
ssh_openssh_public_key_with_comment(Config) when is_list(Config) ->
Datadir = ?config(data_dir, Config),
@@ -484,10 +472,8 @@ ssh_openssh_public_key_with_comment(Config) when is_list(Config) ->
[{{_, #'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() ->
+ [{doc, "Test that long headers are handled"}].
ssh_openssh_public_key_long_header(Config) when is_list(Config) ->
Datadir = ?config(data_dir, Config),
@@ -498,10 +484,8 @@ ssh_openssh_public_key_long_header(Config) when is_list(Config) ->
Decoded = public_key:ssh_decode(Encoded, rfc4716_public_key).
%%--------------------------------------------------------------------
-encrypt_decrypt(doc) ->
- [""];
-encrypt_decrypt(suite) ->
- [];
+encrypt_decrypt() ->
+ [{doc, "Test public_key:encrypt_private and public_key:decrypt_public"}].
encrypt_decrypt(Config) when is_list(Config) ->
{PrivateKey, _DerKey} = erl_make_certs:gen_rsa(64),
#'RSAPrivateKey'{modulus=Mod, publicExponent=Exp} = PrivateKey,
@@ -518,10 +502,8 @@ encrypt_decrypt(Config) when is_list(Config) ->
ok.
%%--------------------------------------------------------------------
-rsa_sign_verify(doc) ->
- ["Checks that we can sign and verify rsa signatures."];
-rsa_sign_verify(suite) ->
- [];
+rsa_sign_verify() ->
+ [{doc, "Checks that we can sign and verify rsa signatures."}].
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}]),
@@ -541,10 +523,8 @@ rsa_sign_verify(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
-dsa_sign_verify(doc) ->
- ["Checks that we can sign and verify dsa signatures."];
-dsa_sign_verify(suite) ->
- [];
+dsa_sign_verify() ->
+ [{doc, "Checks that we can sign and verify dsa signatures."}].
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}]),
@@ -580,10 +560,8 @@ dsa_sign_verify(Config) when is_list(Config) ->
{DSAPublicKey, DSAParams}).
%%--------------------------------------------------------------------
-pkix(doc) ->
- "Misc pkix tests not covered elsewhere";
-pkix(suite) ->
- [];
+pkix() ->
+ [{doc, "Misc pkix tests not covered elsewhere"}].
pkix(Config) when is_list(Config) ->
Datadir = ?config(data_dir, Config),
Certs0 = erl_make_certs:pem_to_der(filename:join(Datadir, "cacerts.pem")),
@@ -621,16 +599,27 @@ pkix(Config) when is_list(Config) ->
[[{'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"}}]]},
+ [[{'AttributeTypeAndValue', {2,5,4,3},{printableString,"erlangca"}}],
+ [{'AttributeTypeAndValue', {2,5,4,3},{printableString,"erlang ca"}}]]},
VerifyStr = public_key:pkix_normalize_name(TestStr),
ok.
+
%%--------------------------------------------------------------------
-pkix_path_validation(doc) ->
- "Misc pkix tests not covered elsewhere";
-pkix_path_validation(suite) ->
- [];
+pkix_countryname() ->
+ [{doc, "Test workaround for certs that code x509countryname as utf8"}].
+pkix_countryname(Config) when is_list(Config) ->
+ Cert = incorrect_pkix_cert(),
+ OTPCert = public_key:pkix_decode_cert(Cert, otp),
+ TBSCert = OTPCert#'OTPCertificate'.tbsCertificate,
+ Issuer = TBSCert#'OTPTBSCertificate'.issuer,
+ Subj = TBSCert#'OTPTBSCertificate'.subject,
+ check_countryname(Issuer),
+ check_countryname(Subj).
+
+%%--------------------------------------------------------------------
+pkix_path_validation() ->
+ [{doc, "Test PKIX path validation"}].
pkix_path_validation(Config) when is_list(Config) ->
CaK = {Trusted,_} =
erl_make_certs:make_cert([{key, dsa},
@@ -661,7 +650,8 @@ pkix_path_validation(Config) when is_list(Config) ->
CertK3 = {Cert3,_} = erl_make_certs:make_cert([{issuer, CertK1},
{extensions, [{basic_constraints, false}]}]),
- {Cert4,_} = erl_make_certs:make_cert([{issuer, CertK3}]),
+ {Cert4,_} = erl_make_certs:make_cert([{issuer, CertK3}, {extensions, [{key_usage, undefined}]}]),
+
{error, {bad_cert,missing_basic_constraint}} =
public_key:pkix_path_validation(Trusted, [Cert1, Cert3,Cert4], []),
@@ -698,6 +688,21 @@ pkix_path_validation(Config) when is_list(Config) ->
public_key:pkix_path_validation(unknown_ca, [Cert1], [{verify_fun,
VerifyFunAndState1}]),
ok.
+%%--------------------------------------------------------------------
+%% Internal functions ------------------------------------------------
+%%--------------------------------------------------------------------
+check_countryname({rdnSequence,DirName}) ->
+ do_check_countryname(DirName).
+do_check_countryname([]) ->
+ ok;
+do_check_countryname([#'AttributeTypeAndValue'{type = ?'id-at-countryName',
+ value = "US"}|_]) ->
+ ok;
+do_check_countryname([#'AttributeTypeAndValue'{type = ?'id-at-countryName',
+ value = Value}|_]) ->
+ ct:fail({incorrect_cuntry_name, Value});
+do_check_countryname([_| Rest]) ->
+ do_check_countryname(Rest).
check_entry_type(#'DSAPrivateKey'{}, 'DSAPrivateKey') ->
true;
@@ -716,3 +721,6 @@ check_entry_type(_,_) ->
strip_ending_newlines(Bin) ->
string:strip(binary_to_list(Bin), right, 10).
+
+incorrect_pkix_cert() ->
+ <<48,130,5,186,48,130,4,162,160,3,2,1,2,2,7,7,250,61,63,6,140,137,48,13,6,9,42, 134,72,134,247,13,1,1,5,5,0,48,129,220,49,11,48,9,6,3,85,4,6,19,2,85,83,49, 16,48,14,6,3,85,4,8,19,7,65,114,105,122,111,110,97,49,19,48,17,6,3,85,4,7,19, 10,83,99,111,116,116,115,100,97,108,101,49,37,48,35,6,3,85,4,10,19,28,83,116, 97,114,102,105,101,108,100,32,84,101,99,104,110,111,108,111,103,105,101,115, 44,32,73,110,99,46,49,57,48,55,6,3,85,4,11,19,48,104,116,116,112,58,47,47,99, 101,114,116,105,102,105,99,97,116,101,115,46,115,116,97,114,102,105,101,108, 100,116,101,99,104,46,99,111,109,47,114,101,112,111,115,105,116,111,114,121, 49,49,48,47,6,3,85,4,3,19,40,83,116,97,114,102,105,101,108,100,32,83,101,99, 117,114,101,32,67,101,114,116,105,102,105,99,97,116,105,111,110,32,65,117, 116,104,111,114,105,116,121,49,17,48,15,6,3,85,4,5,19,8,49,48,54,56,56,52,51, 53,48,30,23,13,49,48,49,48,50,51,48,49,51,50,48,53,90,23,13,49,50,49,48,50, 51,48,49,51,50,48,53,90,48,122,49,11,48,9,6,3,85,4,6,12,2,85,83,49,11,48,9,6, 3,85,4,8,12,2,65,90,49,19,48,17,6,3,85,4,7,12,10,83,99,111,116,116,115,100, 97,108,101,49,38,48,36,6,3,85,4,10,12,29,83,112,101,99,105,97,108,32,68,111, 109,97,105,110,32,83,101,114,118,105,99,101,115,44,32,73,110,99,46,49,33,48, 31,6,3,85,4,3,12,24,42,46,108,111,103,105,110,46,115,101,99,117,114,101,115, 101,114,118,101,114,46,110,101,116,48,130,1,34,48,13,6,9,42,134,72,134,247, 13,1,1,1,5,0,3,130,1,15,0,48,130,1,10,2,130,1,1,0,185,136,240,80,141,36,124, 245,182,130,73,19,188,74,166,117,72,228,185,209,43,129,244,40,44,193,231,11, 209,12,234,88,43,142,1,162,48,122,17,95,230,105,171,131,12,147,46,204,36,80, 250,171,33,253,35,62,83,22,71,212,186,141,14,198,89,89,121,204,224,122,246, 127,110,188,229,162,67,95,6,74,231,127,99,131,7,240,85,102,203,251,50,58,58, 104,245,103,181,183,134,32,203,121,232,54,32,188,139,136,112,166,126,14,91, 223,153,172,164,14,61,38,163,208,215,186,210,136,213,143,70,147,173,109,217, 250,169,108,31,211,104,238,103,93,182,59,165,43,196,189,218,241,30,148,240, 109,90,69,176,194,52,116,173,151,135,239,10,209,179,129,192,102,75,11,25,168, 223,32,174,84,223,134,70,167,55,172,143,27,130,123,226,226,7,34,142,166,39, 48,246,96,231,150,84,220,106,133,193,55,95,159,227,24,249,64,36,1,142,171,16, 202,55,126,7,156,15,194,22,116,53,113,174,104,239,203,120,45,131,57,87,84, 163,184,27,83,57,199,91,200,34,43,98,61,180,144,76,65,170,177,2,3,1,0,1,163, 130,1,224,48,130,1,220,48,15,6,3,85,29,19,1,1,255,4,5,48,3,1,1,0,48,29,6,3, 85,29,37,4,22,48,20,6,8,43,6,1,5,5,7,3,1,6,8,43,6,1,5,5,7,3,2,48,14,6,3,85, 29,15,1,1,255,4,4,3,2,5,160,48,56,6,3,85,29,31,4,49,48,47,48,45,160,43,160, 41,134,39,104,116,116,112,58,47,47,99,114,108,46,115,116,97,114,102,105,101, 108,100,116,101,99,104,46,99,111,109,47,115,102,115,50,45,48,46,99,114,108, 48,83,6,3,85,29,32,4,76,48,74,48,72,6,11,96,134,72,1,134,253,110,1,7,23,2,48, 57,48,55,6,8,43,6,1,5,5,7,2,1,22,43,104,116,116,112,115,58,47,47,99,101,114, 116,115,46,115,116,97,114,102,105,101,108,100,116,101,99,104,46,99,111,109, 47,114,101,112,111,115,105,116,111,114,121,47,48,129,141,6,8,43,6,1,5,5,7,1, 1,4,129,128,48,126,48,42,6,8,43,6,1,5,5,7,48,1,134,30,104,116,116,112,58,47, 47,111,99,115,112,46,115,116,97,114,102,105,101,108,100,116,101,99,104,46,99, 111,109,47,48,80,6,8,43,6,1,5,5,7,48,2,134,68,104,116,116,112,58,47,47,99, 101,114,116,105,102,105,99,97,116,101,115,46,115,116,97,114,102,105,101,108, 100,116,101,99,104,46,99,111,109,47,114,101,112,111,115,105,116,111,114,121, 47,115,102,95,105,110,116,101,114,109,101,100,105,97,116,101,46,99,114,116, 48,31,6,3,85,29,35,4,24,48,22,128,20,73,75,82,39,209,27,188,242,161,33,106, 98,123,81,66,122,138,215,213,86,48,59,6,3,85,29,17,4,52,48,50,130,24,42,46, 108,111,103,105,110,46,115,101,99,117,114,101,115,101,114,118,101,114,46,110, 101,116,130,22,108,111,103,105,110,46,115,101,99,117,114,101,115,101,114,118, 101,114,46,110,101,116,48,29,6,3,85,29,14,4,22,4,20,138,233,191,208,157,203, 249,85,242,239,20,195,48,10,148,49,144,101,255,116,48,13,6,9,42,134,72,134, 247,13,1,1,5,5,0,3,130,1,1,0,82,31,121,162,49,50,143,26,167,202,143,61,71, 189,201,199,57,81,122,116,90,192,88,24,102,194,174,48,157,74,27,87,210,223, 253,93,3,91,150,109,120,1,110,27,11,200,198,141,222,246,14,200,71,105,41,138, 13,114,122,106,63,17,197,181,234,121,61,89,74,65,41,231,248,219,129,83,176, 219,55,107,55,211,112,98,38,49,69,77,96,221,108,123,152,12,210,159,157,141, 43,226,55,187,129,3,82,49,136,66,81,196,91,234,196,10,82,48,6,80,163,83,71, 127,102,177,93,209,129,26,104,2,84,24,255,248,161,3,244,169,234,92,122,110, 43,4,17,113,185,235,108,219,210,236,132,216,177,227,17,169,58,162,159,182, 162,93,160,229,200,9,163,229,110,121,240,168,232,14,91,214,188,196,109,210, 164,222,0,109,139,132,113,91,16,118,173,178,176,80,132,34,41,199,51,206,250, 224,132,60,115,192,94,107,163,219,212,226,225,65,169,148,108,213,46,174,173, 103,110,189,229,166,149,254,31,51,44,144,108,187,182,11,251,201,206,86,138, 208,59,51,86,132,235,81,225,88,34,190,8,184>>.
diff --git a/lib/public_key/test/public_key_SUITE_data/auth_keys b/lib/public_key/test/public_key_SUITE_data/auth_keys
index 0c4b47edde..8be7357a06 100644
--- a/lib/public_key/test/public_key_SUITE_data/auth_keys
+++ b/lib/public_key/test/public_key_SUITE_data/auth_keys
@@ -1,3 +1,7 @@
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
+
+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 comment with whitespaces
+
+ssh-dss AAAAB3NzaC1kc3MAAACBAPY8ZOHY2yFSJA6XYC9HRwNHxaehvx5wOJ0rzZdzoSOXxbETW6ToHv8D1UJ/z+zHo9Fiko5XybZnDIaBDHtblQ+Yp7StxyltHnXF1YLfKD1G4T6JYrdHYI14Om1eg9e4NnCRleaqoZPF3UGfZia6bXrGTQf3gJq2e7Yisk/gF+1VAAAAFQDb8D5cvwHWTZDPfX0D2s9Rd7NBvQAAAIEAlN92+Bb7D4KLYk3IwRbXblwXdkPggA4pfdtW9vGfJ0/RHd+NjB4eo1D+0dix6tXwYGN7PKS5R/FXPNwxHPapcj9uL1Jn2AWQ2dsknf+i/FAAvioUPkmdMc0zuWoSOEsSNhVDtX3WdvVcGcBq9cetzrtOKWOocJmJ80qadxTRHtUAAACBAN7CY+KKv1gHpRzFwdQm7HK9bb1LAo2KwaoXnadFgeptNBQeSXG1vO+JsvphVMBJc9HSn24VYtYtsMu74qXviYjziVucWKjjKEb11juqnF0GDlB3VVmxHLmxnAz643WK42Z7dLM5sY29ouezv4Xz2PuMch5VGPP+CDqzCM4loWgV dhopson@VMUbuntu-DSH comment with whitespaces
diff --git a/lib/public_key/test/public_key_SUITE_data/known_hosts b/lib/public_key/test/public_key_SUITE_data/known_hosts
index 30fc3b1fe8..3c3af68178 100644
--- a/lib/public_key/test/public_key_SUITE_data/known_hosts
+++ b/lib/public_key/test/public_key_SUITE_data/known_hosts
@@ -1,3 +1,8 @@
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]
+
+hostname.domain.com,192.168.0.1 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEA1XY18+zA8VNK2YkzygOkMqUxHSTfxT1Xxx8CgDZgcQH8HUhPssW5ttvG8nKetlPQZAVk1C4WkWS1y5b3ekBhZTIxocp9Joc6V1+f2EOfO2mSLRwB16RGrdw6q7msrBXTC/dl+hF45kMMzVNzqxnSMVOa0sEPK2zK6Sg3Vi9fCSM= Comment with whitespaces
+
+|1|BWO5qDxk/cFH0wa05JLdHn+j6xQ=|rXQvIxh5cDD3C43k5DPDamawVNA= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEA1XY18+zA8VNK2YkzygOkMqUxHSTfxT1Xxx8CgDZgcQH8HUhPssW5ttvG8nKetlPQZAVk1C4WkWS1y5b3ekBhZTIxocp9Joc6V1+f2EOfO2mSLRwB16RGrdw6q7msrBXTC/dl+hF45kMMzVNzqxnSMVOa0sEPK2zK6Sg3Vi9fCSM= [email protected] Comment with whitespaces
+
diff --git a/lib/public_key/test/public_key_SUITE_data/pkcs7_cert.pem b/lib/public_key/test/public_key_SUITE_data/pkcs7_cert.pem
new file mode 100644
index 0000000000..9b450a22c5
--- /dev/null
+++ b/lib/public_key/test/public_key_SUITE_data/pkcs7_cert.pem
@@ -0,0 +1,23 @@
+-----BEGIN PKCS7-----
+MIID6QYJKoZIhvcNAQcCoIID2jCCA9YCAQExADALBgkqhkiG9w0BBwGgggO8MIID
+uDCCAyGgAwIBAgIBAjANBgkqhkiG9w0BAQUFADCBgzEOMAwGA1UEAxMFb3RwQ0Ex
+EzARBgNVBAsTCkVybGFuZyBPVFAxFDASBgNVBAoTC0VyaWNzc29uIEFCMQswCQYD
+VQQGEwJTRTESMBAGA1UEBxMJU3RvY2tob2xtMSUwIwYJKoZIhvcNAQkBFhZwZXRl
+ckBlcml4LmVyaWNzc29uLnNlMB4XDTA4MDEwOTA4MjkzMFoXDTE3MTExNzA4Mjkz
+MFowgYQxDzANBgNVBAMTBnNlcnZlcjETMBEGA1UECxMKRXJsYW5nIE9UUDEUMBIG
+A1UEChMLRXJpY3Nzb24gQUIxCzAJBgNVBAYTAlNFMRIwEAYDVQQHEwlTdG9ja2hv
+bG0xJTAjBgkqhkiG9w0BCQEWFnBldGVyQGVyaXguZXJpY3Nzb24uc2UwgZ8wDQYJ
+KoZIhvcNAQEBBQADgY0AMIGJAoGBAKR20HPrkDGdiavHUyWwFEQwta2dmtF2eQZZ
+i9Xk68UJYbuU7CikHs2srkrwzj0OPIqbp/xOBNzJ7Kch0o4yO6vcEAiSCJ6AB4uS
+M742hrYW4qXgc18K6PqTwSuKr94sn3qQuo4hF/ymCxLrnSicrNpzGOz9A0Lf2+Vk
+6hV0BtdHAgMBAAGjggE3MIIBMzAJBgNVHRMEAjAAMAsGA1UdDwQEAwIF4DAdBgNV
+HQ4EFgQUi19l/qhEwHP/CUeaEjWy4GhOBRIwgbMGA1UdIwSBqzCBqIAUBquANDqk
+uHayvZ0uKOVtkd59AZuhgYykgYkwgYYxETAPBgNVBAMTCGVybGFuZ0NBMRMwEQYD
+VQQLEwpFcmxhbmcgT1RQMRQwEgYDVQQKEwtFcmljc3NvbiBBQjESMBAGA1UEBxMJ
+U3RvY2tob2xtMQswCQYDVQQGEwJTRTElMCMGCSqGSIb3DQEJARYWcGV0ZXJAZXJp
+eC5lcmljc3Nvbi5zZYIBATAhBgNVHREEGjAYgRZwZXRlckBlcml4LmVyaWNzc29u
+LnNlMCEGA1UdEgQaMBiBFnBldGVyQGVyaXguZXJpY3Nzb24uc2UwDQYJKoZIhvcN
+AQEFBQADgYEAzHGutrGMSeC3Di7Z8d65SM7jZLrkkusmL+D2oPVIOGrfZbVuyfDK
+U/nImm99z+lhC/N3JEEpB6PgAYSskfVdBL3LoxbUTaCn/+G3A/G8NfRVIYyANTBe
+NW6ueNpjnauLzcwpyXpu3vp1VBg8wBePtGTBIbRHRgtwwHRXAddE/WuhADEA
+-----END PKCS7-----
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
index c91f4e4679..ac3d61b4c7 100644
--- a/lib/public_key/test/public_key_SUITE_data/ssh1_auth_keys
+++ b/lib/public_key/test/public_key_SUITE_data/ssh1_auth_keys
@@ -1,3 +1,9 @@
+1024 35 794430685278501116412873221867658581245241426828503388129294124540165981586596106773643485704743298698207838825035605868404742682423919455523383721081589378970796492944950066480951790660582889972423189943567111507801410254720228911513553205592856585541922662924268445466959576882300405064708497308004255650466014242855505233634626075778108365396568863197935915425650388910408127232583533503834009244199384570662092164277923946411149853110048365318587554141774139652307149492021035538341281427025252592933784473453522113124752189378715431529801894015739903371171585194505182320772654217490509848165365152457990491089951560694728469571221819385402117009544812199223715540348068497710535492913376699508575875577554607325905000745578091554027803374110357015655416894607641289462159580964951182385869168785183135763253784745647466464331174922663455073627501620274348748413309761116542324505123795743603781806636788810617169341018091186028310551725315297135354426735951943325476221811539822892501042385411792050504283745898099390893596941969752683246939665141002098430129617772928840718016009187577151479855846883928332010147501182201528575840364152774917950524127063432334646746291719251739989499132767590205934821590545762802261107691663
+
1024 35 794430685278501116412873221867658581245241426828503388129294124540165981586596106773643485704743298698207838825035605868404742682423919455523383721081589378970796492944950066480951790660582889972423189943567111507801410254720228911513553205592856585541922662924268445466959576882300405064708497308004255650466014242855505233634626075778108365396568863197935915425650388910408127232583533503834009244199384570662092164277923946411149853110048365318587554141774139652307149492021035538341281427025252592933784473453522113124752189378715431529801894015739903371171585194505182320772654217490509848165365152457990491089951560694728469571221819385402117009544812199223715540348068497710535492913376699508575875577554607325905000745578091554027803374110357015655416894607641289462159580964951182385869168785183135763253784745647466464331174922663455073627501620274348748413309761116542324505123795743603781806636788810617169341018091186028310551725315297135354426735951943325476221811539822892501042385411792050504283745898099390893596941969752683246939665141002098430129617772928840718016009187577151479855846883928332010147501182201528575840364152774917950524127063432334646746291719251739989499132767590205934821590545762802261107691663 dhopson@VMUbuntu-DSH
command="dump /home",no-pty,no-port-forwarding 1024 35 794430685278501116412873221867658581245241426828503388129294124540165981586596106773643485704743298698207838825035605868404742682423919455523383721081589378970796492944950066480951790660582889972423189943567111507801410254720228911513553205592856585541922662924268445466959576882300405064708497308004255650466014242855505233634626075778108365396568863197935915425650388910408127232583533503834009244199384570662092164277923946411149853110048365318587554141774139652307149492021035538341281427025252592933784473453522113124752189378715431529801894015739903371171585194505182320772654217490509848165365152457990491089951560694728469571221819385402117009544812199223715540348068497710535492913376699508575875577554607325905000745578091554027803374110357015655416894607641289462159580964951182385869168785183135763253784745647466464331174922663455073627501620274348748413309761116542324505123795743603781806636788810617169341018091186028310551725315297135354426735951943325476221811539822892501042385411792050504283745898099390893596941969752683246939665141002098430129617772928840718016009187577151479855846883928332010147501182201528575840364152774917950524127063432334646746291719251739989499132767590205934821590545762802261107691663 dhopson@VMUbuntu-DSH
+
+1024 35 794430685278501116412873221867658581245241426828503388129294124540165981586596106773643485704743298698207838825035605868404742682423919455523383721081589378970796492944950066480951790660582889972423189943567111507801410254720228911513553205592856585541922662924268445466959576882300405064708497308004255650466014242855505233634626075778108365396568863197935915425650388910408127232583533503834009244199384570662092164277923946411149853110048365318587554141774139652307149492021035538341281427025252592933784473453522113124752189378715431529801894015739903371171585194505182320772654217490509848165365152457990491089951560694728469571221819385402117009544812199223715540348068497710535492913376699508575875577554607325905000745578091554027803374110357015655416894607641289462159580964951182385869168785183135763253784745647466464331174922663455073627501620274348748413309761116542324505123795743603781806636788810617169341018091186028310551725315297135354426735951943325476221811539822892501042385411792050504283745898099390893596941969752683246939665141002098430129617772928840718016009187577151479855846883928332010147501182201528575840364152774917950524127063432334646746291719251739989499132767590205934821590545762802261107691663 dhopson@VMUbuntu-DSH comment with whitespaces
+
+command="dump /home",no-pty,no-port-forwarding 1024 35 794430685278501116412873221867658581245241426828503388129294124540165981586596106773643485704743298698207838825035605868404742682423919455523383721081589378970796492944950066480951790660582889972423189943567111507801410254720228911513553205592856585541922662924268445466959576882300405064708497308004255650466014242855505233634626075778108365396568863197935915425650388910408127232583533503834009244199384570662092164277923946411149853110048365318587554141774139652307149492021035538341281427025252592933784473453522113124752189378715431529801894015739903371171585194505182320772654217490509848165365152457990491089951560694728469571221819385402117009544812199223715540348068497710535492913376699508575875577554607325905000745578091554027803374110357015655416894607641289462159580964951182385869168785183135763253784745647466464331174922663455073627501620274348748413309761116542324505123795743603781806636788810617169341018091186028310551725315297135354426735951943325476221811539822892501042385411792050504283745898099390893596941969752683246939665141002098430129617772928840718016009187577151479855846883928332010147501182201528575840364152774917950524127063432334646746291719251739989499132767590205934821590545762802261107691663 dhopson@VMUbuntu-DSH comment with whitespaces
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
index ec668fe05b..835b16ab67 100644
--- a/lib/public_key/test/public_key_SUITE_data/ssh1_known_hosts
+++ b/lib/public_key/test/public_key_SUITE_data/ssh1_known_hosts
@@ -1,2 +1,3 @@
hostname.domain.com,192.168.0.1 1024 35 794430685278501116412873221867658581245241426828503388129294124540165981586596106773643485704743298698207838825035605868404742682423919455523383721081589378970796492944950066480951790660582889972423189943567111507801410254720228911513553205592856585541922662924268445466959576882300405064708497308004255650466014242855505233634626075778108365396568863197935915425650388910408127232583533503834009244199384570662092164277923946411149853110048365318587554141774139652307149492021035538341281427025252592933784473453522113124752189378715431529801894015739903371171585194505182320772654217490509848165365152457990491089951560694728469571221819385402117009544812199223715540348068497710535492913376699508575875577554607325905000745578091554027803374110357015655416894607641289462159580964951182385869168785183135763253784745647466464331174922663455073627501620274348748413309761116542324505123795743603781806636788810617169341018091186028310551725315297135354426735951943325476221811539822892501042385411792050504283745898099390893596941969752683246939665141002098430129617772928840718016009187577151479855846883928332010147501182201528575840364152774917950524127063432334646746291719251739989499132767590205934821590545762802261107691663 dhopson@VMUbuntu-DSH
hostname2.domain.com,192.168.0.2 1024 35 794430685278501116412873221867658581245241426828503388129294124540165981586596106773643485704743298698207838825035605868404742682423919455523383721081589378970796492944950066480951790660582889972423189943567111507801410254720228911513553205592856585541922662924268445466959576882300405064708497308004255650466014242855505233634626075778108365396568863197935915425650388910408127232583533503834009244199384570662092164277923946411149853110048365318587554141774139652307149492021035538341281427025252592933784473453522113124752189378715431529801894015739903371171585194505182320772654217490509848165365152457990491089951560694728469571221819385402117009544812199223715540348068497710535492913376699508575875577554607325905000745578091554027803374110357015655416894607641289462159580964951182385869168785183135763253784745647466464331174922663455073627501620274348748413309761116542324505123795743603781806636788810617169341018091186028310551725315297135354426735951943325476221811539822892501042385411792050504283745898099390893596941969752683246939665141002098430129617772928840718016009187577151479855846883928332010147501182201528575840364152774917950524127063432334646746291719251739989499132767590205934821590545762802261107691663
+hostname3.domain.com,192.168.0.3 1024 35 794430685278501116412873221867658581245241426828503388129294124540165981586596106773643485704743298698207838825035605868404742682423919455523383721081589378970796492944950066480951790660582889972423189943567111507801410254720228911513553205592856585541922662924268445466959576882300405064708497308004255650466014242855505233634626075778108365396568863197935915425650388910408127232583533503834009244199384570662092164277923946411149853110048365318587554141774139652307149492021035538341281427025252592933784473453522113124752189378715431529801894015739903371171585194505182320772654217490509848165365152457990491089951560694728469571221819385402117009544812199223715540348068497710535492913376699508575875577554607325905000745578091554027803374110357015655416894607641289462159580964951182385869168785183135763253784745647466464331174922663455073627501620274348748413309761116542324505123795743603781806636788810617169341018091186028310551725315297135354426735951943325476221811539822892501042385411792050504283745898099390893596941969752683246939665141002098430129617772928840718016009187577151479855846883928332010147501182201528575840364152774917950524127063432334646746291719251739989499132767590205934821590545762802261107691663 dhopson@VMUbuntu-DSH comment with whitespaces
diff --git a/lib/public_key/vsn.mk b/lib/public_key/vsn.mk
index ab4ee8b0ff..bd20a5546b 100644
--- a/lib/public_key/vsn.mk
+++ b/lib/public_key/vsn.mk
@@ -1 +1 @@
-PUBLIC_KEY_VSN = 0.15
+PUBLIC_KEY_VSN = 0.18
diff --git a/lib/reltool/doc/src/Makefile b/lib/reltool/doc/src/Makefile
index 0b85b7e02c..2373b5e507 100644
--- a/lib/reltool/doc/src/Makefile
+++ b/lib/reltool/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2009-2010. All Rights Reserved.
+# Copyright Ericsson AB 2009-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/reltool/doc/src/notes.xml b/lib/reltool/doc/src/notes.xml
index 0aae128c2b..66ec7e9035 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>2011</year>
+ <year>2012</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -37,7 +37,28 @@
thus constitutes one section in this document. The title of each
section is the version number of Reltool.</p>
- <section><title>Reltool 0.6</title>
+ <section><title>Reltool 0.6.1</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ A new sys level configuration parameter
+ <c>{excl_lib,otp_root}</c> is added. When this is set,
+ the target system will not contain anything from
+ <c>$OTP_ROOT</c>. It will only contain a releases
+ directory with rel, script and boot files, and a lib
+ directory with applications found outside of
+ <c>$OTP_ROOT</c> (i.e. "your own" applications).</p>
+ <p>
+ Own Id: OTP-9743</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Reltool 0.6</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/reltool/doc/src/reltool.xml b/lib/reltool/doc/src/reltool.xml
index 9a4e2d130e..8437b7a623 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>2012</year>
+ <year>2013</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -51,8 +51,18 @@
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
+ <c>ERL_LIBS</c>. See the module <c>code</c> for more info.</p>
+
+ <p>An application directory <c>AppDir</c> under a library
+ directory is recognized by the existence of an <c>AppDir/ebin</c>
+ directory. If this does not exist, <c>reltool</c> will not
+ consider <c>AppDir</c> at all when looking for applications.</p>
+
+ <p>It is recommended that application directories are named as the
+ application, possibly followed by a dash and the version
+ number. For example <c>myapp</c> or <c>myapp-1.1</c>.</p>
+
+ <p>Finally single modules and entire applications may be read from
Escripts.</p>
<p>Some configuration parameters control the behavior of Reltool
@@ -144,7 +154,7 @@
value <c>include</c> implies that all applications and
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 do not have any explicit
+ applications and escripts that do not have any explicit
<c>incl_cond</c> setting will be excluded.</p>
</item>
@@ -221,6 +231,53 @@
system.</p>
</item>
+ <tag><c>excl_lib</c></tag>
+ <item>
+ <warning><p>This option is experimental.</p></warning>
+ <p>If the <c>excl_lib</c> option is set to <c>otp_root</c>
+ then reltool will not copy anything from the Erlang/OTP
+ installation ($OTP_ROOT) into the target structure. The goal
+ is to create a "slim" release which can be used together with
+ an existing Erlang/OTP installation. The target structure will
+ therefore only contain a <c>lib</c> directory with the
+ applications that were found outside of $OTP_ROOT (typically
+ your own applications), and a <c>releases</c> directory with
+ the generated <c>.rel,</c> <c>.script</c> and <c>.boot</c>
+ files.</p>
+
+ <p>When starting this release, three things must be specified:</p>
+ <taglist>
+ <tag><b>Which <c>releases</c> directory to use</b></tag>
+ <item>Tell the release handler to use the <c>releases</c>
+ directory in our target structure instead of
+ <c>$OTP_ROOT/releases</c>. This is done by setting the SASL
+ environment variable <c>releases_dir</c>, either from the
+ command line (<c>-sasl releases_dir
+ &lt;target-dir&gt;/releases</c>) or in
+ <c>sys.config</c>.</item>
+
+ <tag><b>Which boot file to use</b></tag>
+ <item>The default boot file is <c>$OTP_ROOT/bin/start</c>,
+ but in this case we need to specify a boot file from our
+ target structure, typically
+ <c>&lt;target-dir&gt;/releases/&lt;vsn&gt;/&lt;RelName&gt;</c>. This
+ is done with the <c>-boot</c> command line option to
+ <c>erl</c></item>
+
+ <tag><b>The location of our applications</b></tag>
+ <item>The generated .script (and .boot) file uses the
+ environment variable <c>$RELTOOL_EXT_LIB</c> as prefix for
+ the paths to all applications. The <c>-boot_var</c> option
+ to <c>erl</c> can be used for specifying the value of this
+ variable, typically <c>-boot_var RELTOOL_EXT_LIB
+ &lt;target-dir&gt;/lib</c>.</item>
+ </taglist>
+
+ <p>Example:</p>
+ <p><code>erl -sasl releases_dir \"mytarget/releases\" -boot mytarget/releases/1.0/myrel\
+ -boot_var RELTOOL_EXT_LIB mytarget/lib</code></p>
+ </item>
+
<tag><c>incl_sys_filters</c></tag>
<item>
<p>This parameter normally contains a list of regular
@@ -326,6 +383,11 @@
<p>This parameter is mutual exclusive with <c>lib_dir</c>. If
<c>vsn</c> and <c>lib_dir</c> are both omitted, the latest version
will be chosen.</p>
+ <p>Note that in order for reltool to sort application versions
+ and thereby be able to select the latest, it is required that
+ the version id for the application consits of integers and
+ dots only, for example <c>1</c>, <c>2.0</c> or
+ <c>3.17.1</c>.</p>
</item>
<tag><c>lib_dir</c></tag>
<item>
@@ -337,6 +399,11 @@
<p>This parameter is mutual exclusive with <c>vsn</c>. If
<c>vsn</c> and <c>lib_dir</c> are both omitted, the latest version
will be chosen.</p>
+ <p>Note that in order for reltool to sort application versions
+ and thereby be able to select the latest, it is required that
+ the version id for the application consits of integers and
+ dots only, for example <c>1</c>, <c>2.0</c> or
+ <c>3.17.1</c>.</p>
</item>
<tag><c>mod</c></tag>
<item>
@@ -400,7 +467,7 @@
<tag><c>incl_cond</c></tag>
<item>
<p>This parameter controls whether the module is included or not. By
- default the <c>mod_incl</c> parameter on application and system level
+ default the <c>mod_cond</c> parameter on application and system level
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
@@ -452,6 +519,7 @@ app() = {vsn, app_vsn()}
| {incl_cond, incl_cond()}
| {debug_info, debug_info()}
| {app_file, app_file()}
+ | {excl_lib, excl_lib()}
| {incl_sys_filters, incl_sys_filters()}
| {excl_sys_filters, excl_sys_filters()}
| {incl_app_filters, incl_app_filters()}
@@ -477,6 +545,7 @@ escript() = {incl_cond, incl_cond()}
escript_file() = file()
excl_app_filters() = regexps()
excl_archive_filters() = regexps()
+excl_lib() = otp_root
excl_sys_filters() = regexps()
file() = string()
incl_app() = app_name()
diff --git a/lib/reltool/doc/src/reltool_usage.xml b/lib/reltool/doc/src/reltool_usage.xml
index d128e80a77..0041e60d8f 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>2011</year>
+ <year>2012</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -257,6 +257,11 @@
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 selecting another version.</p>
+
+ <p>Note that in order for reltool to sort application versions and
+ thereby be able to select the latest, it is required that the
+ version id for the application consits of integers and dots only,
+ for example <c>1</c>, <c>2.0</c> or <c>3.17.1</c>.</p>
<p>By default the <c>Application inclusion policy</c> on system
level is used for all applications. Set the value to
@@ -335,7 +340,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
+ these are listed under <c>Modules using this</c>. If the
module uses other modules, these are listed under <c>Used
modules</c>.</p>
@@ -365,7 +370,7 @@
<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 other
+ under <c>Modules using this</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>
diff --git a/lib/reltool/examples/Makefile b/lib/reltool/examples/Makefile
index afc47e1d12..0b495b14b7 100644
--- a/lib/reltool/examples/Makefile
+++ b/lib/reltool/examples/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2009. All Rights Reserved.
+# Copyright Ericsson AB 2009-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/reltool/src/Makefile b/lib/reltool/src/Makefile
index 4730b4acff..74918f1d67 100644
--- a/lib/reltool/src/Makefile
+++ b/lib/reltool/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2009-2010. All Rights Reserved.
+# Copyright Ericsson AB 2009-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -79,10 +79,10 @@ docs:
# ----------------------------------------------------
$(APP_TARGET): $(APP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
# ----------------------------------------------------
# Dependencies
diff --git a/lib/reltool/src/reltool.hrl b/lib/reltool/src/reltool.hrl
index 26dcd82447..f0d8b38519 100644
--- a/lib/reltool/src/reltool.hrl
+++ b/lib/reltool/src/reltool.hrl
@@ -79,6 +79,7 @@
| {debug_info, debug_info()}
| {app_file, app_file()}
| {profile, profile()}
+ | {excl_lib, excl_lib()}
| {incl_sys_filters, incl_sys_filters()}
| {excl_sys_filters, excl_sys_filters()}
| {incl_app_filters, incl_app_filters()}
@@ -123,6 +124,7 @@
-type incl_defaults() :: boolean().
-type incl_derived() :: boolean().
-type status() :: missing | ok.
+-type excl_lib() :: otp_root.
-record(common,
{
@@ -233,6 +235,7 @@
rels :: [#rel{}],
emu_name :: emu_name(),
profile :: profile(),
+ excl_lib :: excl_lib(),
incl_sys_filters :: [#regexp{}],
excl_sys_filters :: [#regexp{}],
incl_app_filters :: [#regexp{}],
diff --git a/lib/reltool/src/reltool_server.erl b/lib/reltool/src/reltool_server.erl
index 034a42e1e2..c56e29152d 100644
--- a/lib/reltool/src/reltool_server.erl
+++ b/lib/reltool/src/reltool_server.erl
@@ -674,6 +674,8 @@ mod_init_is_included(ModTab, M, ModCond, AppCond, Default, Status) ->
true;
exclude ->
false;
+ derived ->
+ undefined;
undefined ->
%% print(M#mod.name, hipe, "mod_cond -> ~p\n",
%% [ModCond]),
@@ -693,6 +695,8 @@ mod_init_is_included(ModTab, M, ModCond, AppCond, Default, Status) ->
true;
exclude ->
false;
+ derived ->
+ undefined;
undefined ->
Default
end
@@ -783,9 +787,10 @@ mod_mark_is_included(#state{app_tab=AppTab, mod_tab=ModTab, sys=Sys} = S,
M#mod{is_pre_included = true,
is_included = true};
exclude ->
- M#mod{is_pre_included = true,
- is_included = true};
- undefined ->
+ M#mod{is_pre_included = false,
+ is_included = false};
+ ModInclCond when ModInclCond==undefined;
+ ModInclCond==derived ->
M#mod{is_included = true}
end,
ets:insert(ModTab, M2),
@@ -979,7 +984,7 @@ refresh_app(#app{name = AppName,
%% Add info from .app file
Base = get_base(AppName, ActiveDir),
- {_, DefaultVsn} = reltool_utils:split_app_name(Base),
+ DefaultVsn = get_vsn_from_dir(AppName,Base),
Ebin = filename:join([ActiveDir, "ebin"]),
AppFile =
filename:join([Ebin,
@@ -1408,6 +1413,8 @@ decode(#sys{} = Sys, [{Key, Val} | KeyVals]) ->
ExclApp,
Sys#sys.excl_app_filters),
embedded_app_type = AppType};
+ excl_lib when Val =:= otp_root ->
+ Sys#sys{excl_lib=Val};
incl_sys_filters ->
Sys#sys{incl_sys_filters =
dec_re(Key, Val, Sys#sys.incl_sys_filters)};
@@ -1678,8 +1685,7 @@ app_dirs2([Lib | Libs], Acc) ->
EbinDir = filename:join([AppDir, "ebin"]),
case filelib:is_dir(EbinDir, erl_prim_loader) of
true ->
- {Name, _Vsn} =
- reltool_utils:split_app_name(Base),
+ Name = find_app_name(Base,EbinDir),
case Name of
erts -> false;
_ -> {true, {Name, AppDir}}
@@ -1697,17 +1703,74 @@ app_dirs2([Lib | Libs], Acc) ->
app_dirs2([], Acc) ->
lists:sort(lists:append(Acc)).
+find_app_name(Base,EbinDir) ->
+ {ok,EbinFiles} = erl_prim_loader:list_dir(EbinDir),
+ AppFile =
+ case [F || F <- EbinFiles, filename:extension(F)=:=".app"] of
+ [AF] ->
+ AF;
+ _ ->
+ undefined
+ end,
+ find_app_name1(Base,AppFile).
+
+find_app_name1(Base,undefined) ->
+ {Name,_} = reltool_utils:split_app_name(Base),
+ Name;
+find_app_name1(_Base,AppFile) ->
+ list_to_atom(filename:rootname(AppFile)).
+
+get_vsn_from_dir(AppName,Base) ->
+ Prefix = atom_to_list(AppName) ++ "-",
+ case lists:prefix(Prefix,Base) of
+ true ->
+ lists:nthtail(length(Prefix),Base);
+ false ->
+ ""
+ end.
+
+
escripts_to_apps([Escript | Escripts], Apps, Status) ->
{EscriptAppName, _Label} = split_escript_name(Escript),
Ext = code:objfile_extension(),
+
+ %% First find all .app files and associate the app name to the app
+ %% label - this is in order to now which application a module
+ %% belongs to in the next round.
+ AppFun = fun(FullName, _GetInfo, _GetBin, AppFiles) ->
+ Components = filename:split(FullName),
+ case Components of
+ [AppLabel, "ebin", File] ->
+ case filename:extension(File) of
+ ".app" ->
+ [{AppLabel,File}|AppFiles];
+ _ ->
+ AppFiles
+ end;
+ _ ->
+ AppFiles
+ end
+ end,
+ AppFiles =
+ case reltool_utils:escript_foldl(AppFun, [], Escript) of
+ {ok, AF} ->
+ AF;
+ {error, Reason1} ->
+ reltool_utils:throw_error("Illegal escript ~p: ~p",
+ [Escript,Reason1])
+ end,
+
+ %% Next, traverse all files...
Fun = fun(FullName, _GetInfo, GetBin, {FileAcc, StatusAcc}) ->
Components = filename:split(FullName),
case Components of
[AppLabel, "ebin", File] ->
case filename:extension(File) of
".app" ->
- {AppName, DefaultVsn} =
- reltool_utils:split_app_name(AppLabel),
+ AppName =
+ list_to_atom(filename:rootname(File)),
+ DefaultVsn =
+ get_vsn_from_dir(AppName,AppLabel),
AppFileName =
filename:join([Escript, FullName]),
{Info, StatusAcc2} =
@@ -1720,8 +1783,9 @@ escripts_to_apps([Escript | Escripts], Apps, Status) ->
{[{AppName, app, Dir, Info} | FileAcc],
StatusAcc2};
E when E =:= Ext ->
- {AppName, _} =
- reltool_utils:split_app_name(AppLabel),
+ AppFile =
+ proplists:get_value(AppLabel,AppFiles),
+ AppName = find_app_name1(AppLabel,AppFile),
Mod = init_mod(AppName,
File,
{File, GetBin()},
@@ -1758,6 +1822,7 @@ escripts_to_apps([Escript | Escripts], Apps, Status) ->
{FileAcc, StatusAcc}
end
end,
+
case reltool_utils:escript_foldl(Fun, {[], Status}, Escript) of
{ok, {Files, Status2}} ->
EscriptApp =
@@ -1772,8 +1837,9 @@ escripts_to_apps([Escript | Escripts], Apps, Status) ->
Apps,
Status2),
escripts_to_apps(Escripts, Apps2, Status3);
- {error, Reason} ->
- reltool_utils:throw_error("Illegal escript ~p: ~p", [Escript,Reason])
+ {error, Reason2} ->
+ reltool_utils:throw_error("Illegal escript ~p: ~p",
+ [Escript,Reason2])
end;
escripts_to_apps([], Apps, Status) ->
{Apps, Status}.
@@ -1932,7 +1998,7 @@ ensure_app_info(#app{name = Name,
fun(Dir, StatusAcc) ->
Base = get_base(Name, Dir),
Ebin = filename:join([Dir, "ebin"]),
- {_, DefaultVsn} = reltool_utils:split_app_name(Base),
+ DefaultVsn = get_vsn_from_dir(Name,Base),
AppFile = filename:join([Ebin, atom_to_list(Name) ++ ".app"]),
read_app_info(AppFile, AppFile, Name, DefaultVsn, StatusAcc)
end,
diff --git a/lib/reltool/src/reltool_sys_win.erl b/lib/reltool/src/reltool_sys_win.erl
index 0c0b295db1..8e182d02ed 100644
--- a/lib/reltool/src/reltool_sys_win.erl
+++ b/lib/reltool/src/reltool_sys_win.erl
@@ -49,7 +49,6 @@
rel_book,
lib_tree,
status_bar,
- popup_menu,
source,
whitelist,
blacklist,
@@ -136,6 +135,7 @@ init(Options) ->
do_init(Options)
catch
error:Reason ->
+ io:format("~p: ~p~n",[Reason, erlang:get_stacktrace()]),
exit({Reason, erlang:get_stacktrace()})
end.
@@ -403,8 +403,6 @@ create_menubar(Frame) ->
wxEvtHandler:connect(Frame,
command_menu_selected,
[{userData, main_window}]),
- wxEvtHandler:connect(File, menu_close),
- wxEvtHandler:connect(Help, menu_close),
MenuBar.
create_app_page(#state{book = Book} = S) ->
@@ -780,15 +778,12 @@ root_popup(S, Root, Tree, Item) ->
wxMenu:appendSeparator(PopupMenu),
wxMenu:append(PopupMenu, 1, "Edit"),
Choices = [edit],
- wxEvtHandler:connect(PopupMenu, command_menu_selected),
- wxEvtHandler:connect(PopupMenu, menu_close),
+ Popup = #root_popup{dir = Root, choices = Choices,
+ tree = Tree, item = Item},
+ wxEvtHandler:connect(PopupMenu, command_menu_selected, [{userData, {popup, Popup}}]),
wxWindow:popupMenu(S#state.frame, PopupMenu),
- Popup = #root_popup{dir = Root,
- choices = Choices,
- tree = Tree,
- item = Item},
- S#state{popup_menu = Popup}.
+ S.
lib_popup(S, Lib, Tree, Item) ->
PopupMenu = wxMenu:new(),
@@ -804,12 +799,10 @@ lib_popup(S, Lib, Tree, Item) ->
wxMenu:append(PopupMenu, 3, "Delete"),
[add, edit, delete]
end,
- wxEvtHandler:connect(PopupMenu, command_menu_selected),
- wxEvtHandler:connect(PopupMenu, menu_close),
- wxWindow:popupMenu(S#state.frame, PopupMenu),
-
Popup = #lib_popup{dir = Lib, choices = Choices, tree = Tree, item = Item},
- S#state{popup_menu = Popup}.
+ wxEvtHandler:connect(PopupMenu, command_menu_selected, [{userData, {popup, Popup}}]),
+ wxWindow:popupMenu(S#state.frame, PopupMenu),
+ S.
escript_popup(S, File, Tree, Item) ->
PopupMenu = wxMenu:new(),
@@ -825,15 +818,11 @@ escript_popup(S, File, Tree, Item) ->
wxMenu:append(PopupMenu, 3, "Delete"),
[add, edit, delete]
end,
- wxEvtHandler:connect(PopupMenu, command_menu_selected),
- wxEvtHandler:connect(PopupMenu, menu_close),
+ Popup = #escript_popup{file = File, choices = Choices,
+ tree = Tree, item = Item},
+ wxEvtHandler:connect(PopupMenu, command_menu_selected, [{userData, {popup, Popup}}]),
wxWindow:popupMenu(S#state.frame, PopupMenu),
-
- Popup = #escript_popup{file = File,
- choices = Choices,
- tree = Tree,
- item = Item},
- S#state{popup_menu = Popup}.
+ S.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -903,11 +892,13 @@ handle_event(S, #wx{id = Id, obj= ObjRef, userData = UserData, event = Event} =
wxMessageDialog:showModal(MD),
wxMessageDialog:destroy(MD),
S;
- #wxMenu{type = menu_close} ->
- S#state{popup_menu = undefined};
- #wxCommand{type = command_menu_selected = Type, cmdString = Str}
- when S#state.popup_menu =/= undefined ->
- handle_popup_event(S, Type, Id, ObjRef, UserData, Str);
+ #wxCommand{type = command_menu_selected = Type, cmdString = Str} ->
+ case UserData of
+ {popup, Popup} ->
+ handle_popup_event(S, Type, Id, ObjRef, Popup, Str);
+ true ->
+ S
+ end;
#wxMouse{type = enter_window} ->
%% The following is commented out because it raises the
%% main system window on top of popup windows.
@@ -1028,11 +1019,9 @@ warning_popup_position(#state{frame=MF,warning_list=WL},{WFW,WFH}) ->
{X,Y}.
handle_popup_event(S, _Type, 0, _ObjRef, _UserData, _Str) ->
- S#state{popup_menu = undefined};
-handle_popup_event(#state{popup_menu = #root_popup{dir = OldDir,
- choices = Choices},
- sys = Sys} = S,
- _Type, Pos, _ObjRef, _UserData, _Str) ->
+ S;
+handle_popup_event(#state{sys = Sys} = S, _Type, Pos, _ObjRef,
+ #root_popup{dir = OldDir, choices = Choices}, _Str) ->
case lists:nth(Pos, Choices) of
edit ->
Style = ?wxFD_OPEN bor ?wxFD_FILE_MUST_EXIST,
@@ -1042,18 +1031,16 @@ handle_popup_event(#state{popup_menu = #root_popup{dir = OldDir,
Style) of
{ok, NewDir} when NewDir =:= OldDir ->
%% Same dir.Ignore.
- S#state{popup_menu = undefined};
+ S;
{ok, NewDir} ->
Sys2 = Sys#sys{root_dir = NewDir},
- do_set_sys(S#state{popup_menu = undefined, sys = Sys2});
+ do_set_sys(S#state{sys = Sys2});
cancel ->
- S#state{popup_menu = undefined}
+ S
end
end;
-handle_popup_event(#state{popup_menu = #lib_popup{dir = OldDir,
- choices = Choices},
- sys = Sys} = S,
- _Type, Pos, _ObjRef, _UserData, _Str) ->
+handle_popup_event(#state{sys = Sys} = S, _Type, Pos, _ObjRef,
+ #lib_popup{dir = OldDir, choices = Choices}, _Str) ->
case lists:nth(Pos, Choices) of
add ->
{ok, Cwd} = file:get_cwd(),
@@ -1063,15 +1050,14 @@ handle_popup_event(#state{popup_menu = #lib_popup{dir = OldDir,
case lists:member(NewDir, Sys#sys.lib_dirs) of
true ->
%% Ignore duplicate. Keep old.
- S#state{popup_menu = undefined};
+ S;
false ->
LibDirs = Sys#sys.lib_dirs ++ [NewDir],
Sys2 = Sys#sys{lib_dirs = LibDirs},
- do_set_sys(S#state{popup_menu = undefined,
- sys = Sys2})
+ do_set_sys(S#state{sys = Sys2})
end;
cancel ->
- S#state{popup_menu = undefined}
+ S
end;
edit ->
Style = ?wxFD_OPEN bor ?wxFD_FILE_MUST_EXIST,
@@ -1083,28 +1069,25 @@ handle_popup_event(#state{popup_menu = #lib_popup{dir = OldDir,
case lists:member(NewDir, Sys#sys.lib_dirs) of
true ->
%% Ignore duplicate. Keep old.
- S#state{popup_menu = undefined};
+ S;
false ->
Pred = fun(E) -> E =/= OldDir end,
{Before, [_| After]} =
lists:splitwith(Pred, Sys#sys.lib_dirs),
LibDirs2 = Before ++ [NewDir | After],
Sys2 = Sys#sys{lib_dirs = LibDirs2},
- do_set_sys(S#state{popup_menu = undefined,
- sys = Sys2})
+ do_set_sys(S#state{sys = Sys2})
end;
cancel ->
- S#state{popup_menu = undefined}
+ S
end;
delete ->
LibDirs = Sys#sys.lib_dirs -- [OldDir],
Sys2 = Sys#sys{lib_dirs = LibDirs},
- do_set_sys(S#state{popup_menu = undefined, sys = Sys2})
+ do_set_sys(S#state{sys = Sys2})
end;
-handle_popup_event(#state{popup_menu = #escript_popup{file = OldFile,
- choices = Choices},
- sys = Sys} = S,
- _Type, Pos, _ObjRef, _UserData, _Str) ->
+handle_popup_event(#state{sys = Sys} = S, _Type, Pos, _ObjRef,
+ #escript_popup{file = OldFile, choices = Choices}, _Str) ->
case lists:nth(Pos, Choices) of
add ->
OldFile2 =
@@ -1124,14 +1107,14 @@ handle_popup_event(#state{popup_menu = #escript_popup{file = OldFile,
case lists:member(NewFile, Sys#sys.escripts) of
true ->
%% Ignore duplicate. Keep old.
- S#state{popup_menu = undefined};
+ S;
false ->
Escripts = Sys#sys.escripts ++ [NewFile],
Sys2 = Sys#sys{escripts = Escripts},
- do_set_sys(S#state{popup_menu = undefined, sys = Sys2})
+ do_set_sys(S#state{sys = Sys2})
end;
cancel ->
- S#state{popup_menu = undefined}
+ S
end;
edit ->
Style = ?wxFD_OPEN bor ?wxFD_FILE_MUST_EXIST,
@@ -1143,23 +1126,22 @@ handle_popup_event(#state{popup_menu = #escript_popup{file = OldFile,
case lists:member(NewFile, Sys#sys.escripts) of
true ->
%% Ignore duplicate. Keep old.
- S#state{popup_menu = undefined};
+ S;
false ->
Pred = fun(E) -> E =/= OldFile end,
{Before, [_| After]} =
lists:splitwith(Pred, Sys#sys.escripts),
Escripts2 = Before ++ [NewFile | After],
Sys2 = Sys#sys{escripts = Escripts2},
- do_set_sys(S#state{popup_menu = undefined,
- sys = Sys2})
+ do_set_sys(S#state{sys = Sys2})
end;
cancel ->
- S#state{popup_menu = undefined}
+ S
end;
delete ->
Escripts = Sys#sys.escripts -- [OldFile],
Sys2 = Sys#sys{escripts = Escripts},
- do_set_sys(S#state{popup_menu = undefined, sys = Sys2})
+ do_set_sys(S#state{sys = Sys2})
end.
handle_system_event(#state{sys = Sys} = S,
diff --git a/lib/reltool/src/reltool_target.erl b/lib/reltool/src/reltool_target.erl
index e3a7b02143..1f4ce7226a 100644
--- a/lib/reltool/src/reltool_target.erl
+++ b/lib/reltool/src/reltool_target.erl
@@ -333,7 +333,9 @@ merge_apps(#rel{name = RelName,
A#app.name =/= ?MISSING_APP_NAME,
not lists:keymember(A#app.name, #app.name, MergedApps2)],
MergedApps3 = do_merge_apps(RelName, Embedded, Apps, EmbAppType, MergedApps2),
- sort_apps(lists:reverse(MergedApps3)).
+ RevMerged = lists:reverse(MergedApps3),
+ MergedSortedUsedAndIncs = sort_used_and_incl_apps(RevMerged,RevMerged),
+ sort_apps(MergedSortedUsedAndIncs).
do_merge_apps(RelName, [#rel_app{name = Name} = RA | RelApps], Apps, RelAppType, Acc) ->
case is_already_merged(Name, RelApps, Acc) of
@@ -342,9 +344,11 @@ do_merge_apps(RelName, [#rel_app{name = Name} = RA | RelApps], Apps, RelAppType,
false ->
{value, App} = lists:keysearch(Name, #app.name, Apps),
MergedApp = merge_app(RelName, RA, RelAppType, App),
- MoreNames = (MergedApp#app.info)#app_info.applications,
+ ReqNames = (MergedApp#app.info)#app_info.applications,
+ IncNames = (MergedApp#app.info)#app_info.incl_apps,
Acc2 = [MergedApp | Acc],
- do_merge_apps(RelName, MoreNames ++ RelApps, Apps, RelAppType, Acc2)
+ do_merge_apps(RelName, ReqNames ++ IncNames ++ RelApps,
+ Apps, RelAppType, Acc2)
end;
do_merge_apps(RelName, [Name | RelApps], Apps, RelAppType, Acc) ->
case is_already_merged(Name, RelApps, Acc) of
@@ -478,33 +482,62 @@ do_gen_script(#rel{name = RelName, vsn = RelVsn},
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-load_app_mods(#app{mods = Mods} = App, Mand, PathFlag, Variables) ->
+load_app_mods(#app{mods = Mods0} = App, Mand, PathFlag, Variables) ->
Path = cr_path(App, PathFlag, Variables),
- PartNames =
- lists:sort([{packages:split(M),M} ||
- #mod{name = M, is_included=true} <- Mods,
- not lists:member(M, Mand)]),
- SplitMods =
- lists:foldl(
- fun({Parts,M}, [{Last, Acc}|Rest]) ->
- [_|Tail] = lists:reverse(Parts),
- case lists:reverse(Tail) of
- Subs when Subs == Last ->
- [{Last,[M|Acc]}|Rest];
- Subs ->
- [{Subs, [M]}|[{Last,Acc}|Rest]]
- end
- end,
- [{[],
- []}],
- PartNames),
- lists:foldl(
- fun({Subs,Ms}, Cmds) ->
- [{path, [filename:join([Path | Subs])]},
- {primLoad, lists:sort(Ms)} | Cmds]
- end,
- [],
- SplitMods).
+ Mods = [M || #mod{name = M, is_included=true} <- Mods0,
+ not lists:member(M, Mand)],
+ [{path, [filename:join([Path])]},
+ {primLoad, lists:sort(Mods)}].
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% sort_used_and_incl_apps(Apps, OrderedApps) -> Apps
+%% Apps = [#app{}]
+%% OrderedApps = [#app{}]
+%%
+%% OTP-4121, OTP-9984
+%% (Tickets are written for systools, but needs to be implemented here
+%% as well.)
+%% Make sure that used and included applications are given in the same
+%% order as in the release resource file (.rel). Otherwise load and
+%% start instructions in the boot script, and consequently release
+%% upgrade instructions in relup, may end up in the wrong order.
+
+sort_used_and_incl_apps([#app{info=Info} = App|Apps], OrderedApps) ->
+ Incls2 =
+ case Info#app_info.incl_apps of
+ Incls when length(Incls)>1 ->
+ sort_appl_list(Incls, OrderedApps);
+ Incls ->
+ Incls
+ end,
+ Uses2 =
+ case Info#app_info.applications of
+ Uses when length(Uses)>1 ->
+ sort_appl_list(Uses, OrderedApps);
+ Uses ->
+ Uses
+ end,
+ App2 = App#app{info=Info#app_info{incl_apps=Incls2, applications=Uses2}},
+ [App2|sort_used_and_incl_apps(Apps, OrderedApps)];
+sort_used_and_incl_apps([], _OrderedApps) ->
+ [].
+
+sort_appl_list(List, Order) ->
+ IndexedList = find_pos(List, Order),
+ SortedIndexedList = lists:keysort(1, IndexedList),
+ lists:map(fun({_Index,Name}) -> Name end, SortedIndexedList).
+
+find_pos([Name|Incs], OrderedApps) ->
+ [find_pos(1, Name, OrderedApps)|find_pos(Incs, OrderedApps)];
+find_pos([], _OrderedApps) ->
+ [].
+
+find_pos(N, Name, [#app{name=Name}|_OrderedApps]) ->
+ {N, Name};
+find_pos(N, Name, [_OtherAppl|OrderedApps]) ->
+ find_pos(N+1, Name, OrderedApps).
+
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Function: sort_apps(Apps) -> {ok, Apps'} | throw({error, Error})
@@ -687,6 +720,8 @@ strip_name_ebin(Dir, Name, Vsn) ->
case lists:reverse(Dir) of
["ebin", Name | D] -> {ok, lists:reverse(D)};
["ebin", FullName | D] -> {ok, lists:reverse(D)};
+ [Name | D] -> {ok, lists:reverse(D)};
+ [FullName | D] -> {ok, lists:reverse(D)};
_ -> false
end.
@@ -733,8 +768,20 @@ do_spec_rel_files(#rel{name = RelName} = Rel, Sys) ->
BootFile = RelName ++ ".boot",
MergedApps = merge_apps(Rel, Sys),
GenRel = do_gen_rel(Rel, Sys, MergedApps),
+ Variables =
+ case Sys#sys.excl_lib of
+ otp_root ->
+ %% All applications that are fetched from somewhere
+ %% other than $OTP_ROOT/lib will get $RELTOOL_EXT_LIB
+ %% as path prefix in the .script file.
+ [{"RELTOOL_EXT_LIB",LibDir} || LibDir <- Sys#sys.lib_dirs] ++
+ [{"RELTOOL_EXT_LIB",filename:dirname(AppLibDir)} ||
+ #app{active_dir=AppLibDir,use_selected_vsn=dir}
+ <- MergedApps];
+ _ ->
+ []
+ end,
PathFlag = true,
- Variables = [],
{ok, Script} = do_gen_script(Rel, Sys, MergedApps, PathFlag, Variables),
{ok, BootBin} = gen_boot(Script),
Date = date(),
@@ -771,29 +818,34 @@ gen_spec(Sys) ->
end.
do_gen_spec(#sys{root_dir = RootDir,
+ excl_lib = ExclLib,
incl_sys_filters = InclRegexps,
excl_sys_filters = ExclRegexps,
relocatable = Relocatable,
apps = Apps} = Sys) ->
- {create_dir, _, SysFiles} = spec_dir(RootDir),
- {ExclRegexps2, SysFiles2} =
- strip_sys_files(Relocatable, SysFiles, Apps, ExclRegexps),
RelFiles = spec_rel_files(Sys),
- {InclRegexps2, BinFiles} =
- spec_bin_files(Sys, SysFiles, SysFiles2, RelFiles, InclRegexps),
+ {SysFiles, InclRegexps2, ExclRegexps2, Mandatory} =
+ case ExclLib of
+ otp_root ->
+ {[],InclRegexps,ExclRegexps,["lib"]};
+ _ ->
+ {create_dir, _, SF} = spec_dir(RootDir),
+ {ER2, SF2} = strip_sys_files(Relocatable, SF, Apps, ExclRegexps),
+ {IR2, BinFiles} =
+ spec_bin_files(Sys, SF, SF2, RelFiles, InclRegexps),
+ SF3 = [{create_dir, "bin", BinFiles}] ++ SF2,
+ {SF3,IR2,ER2,["bin","erts","lib"]}
+ end,
LibFiles = spec_lib_files(Sys),
{BootVsn, StartFile} = spec_start_file(Sys),
- SysFiles3 =
- [
- {create_dir, "releases",
+ SysFiles2 =
+ [{create_dir, "releases",
[StartFile,
- {create_dir,BootVsn, RelFiles}]},
- {create_dir, "bin", BinFiles}
- ] ++ SysFiles2,
- SysFiles4 = filter_spec(SysFiles3, InclRegexps2, ExclRegexps2),
- SysFiles5 = SysFiles4 ++ [{create_dir, "lib", LibFiles}],
- check_sys(["bin", "erts", "lib"], SysFiles5),
- SysFiles5.
+ {create_dir,BootVsn, RelFiles}]}] ++ SysFiles,
+ SysFiles3 = filter_spec(SysFiles2, InclRegexps2, ExclRegexps2),
+ SysFiles4 = SysFiles3 ++ [{create_dir, "lib", LibFiles}],
+ check_sys(Mandatory, SysFiles4),
+ SysFiles4.
strip_sys_files(Relocatable, SysFiles, Apps, ExclRegexps) ->
ExclRegexps2 =
@@ -967,22 +1019,35 @@ safe_lookup_spec(Prefix, Specs) ->
%% Specify applications
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-spec_lib_files(#sys{apps = Apps} = Sys) ->
+spec_lib_files(#sys{root_dir = RootDir,
+ apps = Apps,
+ excl_lib = ExclLib} = Sys) ->
Filter = fun(#app{is_escript = IsEscript, is_included = IsIncl,
- is_pre_included = IsPre, name = Name}) ->
+ is_pre_included = IsPre, name = Name,
+ active_dir = ActiveDir}) ->
if
Name =:= ?MISSING_APP_NAME ->
false;
IsEscript =/= false ->
false;
IsIncl; IsPre ->
- true;
+ case ExclLib of
+ otp_root ->
+ not lists:prefix(RootDir,ActiveDir);
+ _ ->
+ true
+ end;
true ->
false
end
end,
SelectedApps = lists:filter(Filter, Apps),
- check_apps([kernel, stdlib], SelectedApps),
+ case ExclLib of
+ otp_root ->
+ ok;
+ _ ->
+ check_apps([kernel, stdlib], SelectedApps)
+ end,
lists:flatten([spec_app(App, Sys) || App <- SelectedApps]).
check_apps([Mandatory | Names], Apps) ->
@@ -1388,12 +1453,10 @@ do_install(RelName, TargetDir) ->
BinDir = filename:join([TargetDir2, "bin"]),
case os:type() of
{win32, _} ->
- NativeRootDir = filename:nativename(TargetDir2),
- %% NativeBinDir =
- %% filename:nativename(filename:join([BinDir, "win32"])),
- NativeBinDir = filename:nativename(BinDir),
+ NativeRootDir = nativename(TargetDir2),
+ NativeErtsBinDir = nativename(ErtsBinDir),
IniData = ["[erlang]\r\n",
- "Bindir=", NativeBinDir, "\r\n",
+ "Bindir=", NativeErtsBinDir, "\r\n",
"Progname=erl\r\n",
"Rootdir=", NativeRootDir, "\r\n"],
IniFile = filename:join([BinDir, "erl.ini"]),
@@ -1413,6 +1476,15 @@ do_install(RelName, TargetDir) ->
reltool_utils:throw_error("~s: Illegal data file syntax", [DataFile])
end.
+nativename(Dir) ->
+ escape_backslash(filename:nativename(Dir)).
+escape_backslash([$\\|T]) ->
+ [$\\,$\\|escape_backslash(T)];
+escape_backslash([H|T]) ->
+ [H|escape_backslash(T)];
+escape_backslash([]) ->
+ [].
+
subst_src_scripts(Scripts, SrcDir, DestDir, Vars, Opts) ->
Fun = fun(Script) ->
subst_src_script(Script, SrcDir, DestDir, Vars, Opts)
diff --git a/lib/reltool/test/reltool_server_SUITE.erl b/lib/reltool/test/reltool_server_SUITE.erl
index 8a98dc36cf..8d71865508 100644
--- a/lib/reltool/test/reltool_server_SUITE.erl
+++ b/lib/reltool/test/reltool_server_SUITE.erl
@@ -67,6 +67,7 @@ all() ->
create_standalone_app_clash,
create_multiple_standalone,
create_old_target,
+ create_slim,
eval_target_spec,
otp_9135,
otp_9229_dupl_mod_exclude_app,
@@ -89,8 +90,10 @@ all() ->
gen_rel_files,
save_config,
dependencies,
+ mod_incl_cond_derived,
use_selected_vsn,
- use_selected_vsn_relative_path].
+ use_selected_vsn_relative_path,
+ non_standard_vsn_id].
groups() ->
[].
@@ -105,6 +108,15 @@ end_per_group(_GroupName, Config) ->
%% The test cases
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% A dummy break test case which is NOT in all(), but can be run
+%% directly from the command line with ct_run. It just does a
+%% test_server:break()...
+break(_Config) ->
+ test_server:break(""),
+ ok.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Start a server process and check that it does not crash
start_server(_Config) ->
@@ -297,7 +309,6 @@ create_release(_Config) ->
%% started before the including application.
%% Circular dependencies shall also be detected and cause error.
-create_release_sort(_Config) -> {skip, "Two bugs related to sorting"};
create_release_sort(Config) ->
DataDir = ?config(data_dir,Config),
%% Configure the server
@@ -306,11 +317,12 @@ create_release_sort(Config) ->
RelName3 = "Include-both",
RelName4 = "Include-only-app",
RelName5 = "Include-only-rel",
- RelName6 = "Include-missing-app",
+ RelName6 = "Auto-add-missing-apps",
RelName7 = "Circular",
- RelName8 = "Include-both-missing-app",
- RelName9 = "Include-overwrite",
+ RelName8 = "Include-rel-alter-order",
+ RelName9 = "Include-none-overwrite",
RelName10= "Uses-order-as-rel",
+ RelName11= "Auto-add-dont-overwrite-load",
RelVsn = "1.0",
%% Application z (.app file):
%% includes [tools, mnesia]
@@ -325,11 +337,12 @@ create_release_sort(Config) ->
{rel, RelName3, RelVsn, [stdlib, kernel, {z,[tools]}, tools, mnesia]},
{rel, RelName4, RelVsn, [stdlib, kernel, z, mnesia, tools]},
{rel, RelName5, RelVsn, [stdlib, kernel, {sasl,[tools]}]},
- {rel, RelName6, RelVsn, [stdlib, kernel, z]},
+ {rel, RelName6, RelVsn, [z]},
{rel, RelName7, RelVsn, [stdlib, kernel, mnesia, y, sasl, x]},
- {rel, RelName8, RelVsn, [stdlib, kernel, {z,[tools]}]},
+ {rel, RelName8, RelVsn, [stdlib, kernel, {z,[mnesia,tools]}]},
{rel, RelName9, RelVsn, [stdlib, kernel, {z,[]}]},
{rel, RelName10, RelVsn, [stdlib, kernel, {z,[]}, inets, sasl]},
+ {rel, RelName11, RelVsn, [stdlib, kernel, z, {inets, load}]},
{incl_cond,exclude},
{mod_cond,app},
{app,kernel,[{incl_cond,include}]},
@@ -371,7 +384,6 @@ create_release_sort(Config) ->
{mnesia, _}]}},
reltool:get_rel([{config, Sys}], RelName3)),
- %%! BUG: same as OTP-4121, but for reltool???? Or revert tools and mnesia
?msym({ok, {release, {RelName4, RelVsn},
{erts, _},
[{kernel, _},
@@ -388,13 +400,29 @@ create_release_sort(Config) ->
"in the app file: [tools]"},
reltool:get_rel([{config, Sys}], RelName5)),
- ?m({error, "Undefined applications: [tools,mnesia]"},
+ ?msym({ok, {release, {RelName6, RelVsn},
+ {erts, _},
+ [{kernel, _},
+ {stdlib, _},
+ {sasl, _},
+ {inets, _},
+ {tools, _},
+ {mnesia, _},
+ {z, _}]}},
reltool:get_rel([{config, Sys}], RelName6)),
?m({error,"Circular dependencies: [x,y]"},
reltool:get_rel([{config, Sys}], RelName7)),
- ?m({error,"Undefined applications: [tools]"},
+ ?msym({ok, {release, {RelName8, RelVsn},
+ {erts, _},
+ [{kernel, _},
+ {stdlib, _},
+ {sasl, _},
+ {inets, _},
+ {mnesia, _},
+ {tools, _},
+ {z, _, [mnesia,tools]}]}},
reltool:get_rel([{config, Sys}], RelName8)),
?msym({ok,{release,{RelName9,RelVsn},
@@ -406,7 +434,6 @@ create_release_sort(Config) ->
{z,_,[]}]}},
reltool:get_rel([{config, Sys}], RelName9)),
- %%! BUG: same as OTP-9984, but for reltool???? Or revert inets and sasl?
?msym({ok,{release,{RelName10,RelVsn},
{erts,_},
[{kernel,_},
@@ -416,6 +443,17 @@ create_release_sort(Config) ->
{z,_,[]}]}},
reltool:get_rel([{config, Sys}], RelName10)),
+ ?msym({ok,{release,{RelName11,RelVsn},
+ {erts,_},
+ [{kernel,_},
+ {stdlib,_},
+ {sasl, _},
+ {inets, _, load},
+ {tools, _},
+ {mnesia, _},
+ {z,_}]}},
+ reltool:get_rel([{config, Sys}], RelName11)),
+
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -476,12 +514,16 @@ create_script_sort(Config) ->
RelName3 = "Include-both",
RelName4 = "Include-only-app",
RelName5 = "Include-only-rel",
- RelName6 = "Include-missing-app",
+ RelName6 = "Auto-add-missing-apps",
RelName7 = "Circular",
- RelName8 = "Include-both-missing-app",
- RelName9 = "Include-overwrite",
+ RelName8 = "Include-rel-alter-order",
+ RelName9 = "Include-none-overwrite",
+ RelName10= "Uses-order-as-rel",
RelVsn = "1.0",
LibDir = filename:join(DataDir,"sort_apps"),
+ %% Application z (.app file):
+ %% includes [tools, mnesia]
+ %% uses [kernel, stdlib, sasl, inets]
Sys =
{sys,
[
@@ -492,10 +534,11 @@ create_script_sort(Config) ->
{rel, RelName3, RelVsn, [stdlib, kernel, {z,[tools]}, tools, mnesia]},
{rel, RelName4, RelVsn, [stdlib, kernel, z, mnesia, tools]},
{rel, RelName5, RelVsn, [stdlib, kernel, {sasl,[tools]}]},
- {rel, RelName6, RelVsn, [stdlib, kernel, z]},
+ {rel, RelName6, RelVsn, [z]},
{rel, RelName7, RelVsn, [stdlib, kernel, mnesia, y, sasl, x]},
- {rel, RelName8, RelVsn, [stdlib, kernel, {z,[tools]}]},
+ {rel, RelName8, RelVsn, [stdlib, kernel, {z,[mnesia,tools]}]},
{rel, RelName9, RelVsn, [stdlib, kernel, {z,[]}]},
+ {rel, RelName10, RelVsn, [stdlib, kernel, {z,[]}, inets, sasl]},
{incl_cond,exclude},
{mod_cond,app},
{app,kernel,[{incl_cond,include}]},
@@ -552,8 +595,8 @@ create_script_sort(Config) ->
[{kernel,KernelVsn},
{stdlib,StdlibVsn},
{z,"1.0"},
- {tools,ToolsVsn},
{mnesia,MnesiaVsn},
+ {tools,ToolsVsn},
{sasl,SaslVsn},
{inets,InetsVsn}]},
FullName4 = filename:join(?WORK_DIR,RelName4),
@@ -568,6 +611,10 @@ create_script_sort(Config) ->
Rel6 = {release, {RelName6,RelVsn}, {erts,ErtsVsn},
[{kernel,KernelVsn},
{stdlib,StdlibVsn},
+ {sasl,SaslVsn},
+ {inets,InetsVsn},
+ {tools,ToolsVsn},
+ {mnesia,MnesiaVsn},
{z,"1.0"}]},
FullName6 = filename:join(?WORK_DIR,RelName6),
?m(ok, file:write_file(FullName6 ++ ".rel", io_lib:format("~p.\n", [Rel6]))),
@@ -583,7 +630,11 @@ create_script_sort(Config) ->
Rel8 = {release, {RelName8,RelVsn}, {erts,ErtsVsn},
[{kernel,KernelVsn},
{stdlib,StdlibVsn},
- {z,"1.0",[tools]}]},
+ {z,"1.0",[mnesia,tools]},
+ {sasl,SaslVsn},
+ {inets,InetsVsn},
+ {mnesia,MnesiaVsn},
+ {tools,ToolsVsn}]},
FullName8 = filename:join(?WORK_DIR,RelName8),
?m(ok, file:write_file(FullName8 ++ ".rel", io_lib:format("~p.\n", [Rel8]))),
Rel9 = {release, {RelName9,RelVsn}, {erts,ErtsVsn},
@@ -594,6 +645,14 @@ create_script_sort(Config) ->
{inets,InetsVsn}]},
FullName9 = filename:join(?WORK_DIR,RelName9),
?m(ok, file:write_file(FullName9 ++ ".rel", io_lib:format("~p.\n", [Rel9]))),
+ Rel10 = {release, {RelName10,RelVsn}, {erts,ErtsVsn},
+ [{kernel,KernelVsn},
+ {stdlib,StdlibVsn},
+ {z,"1.0",[]},
+ {inets,InetsVsn},
+ {sasl,SaslVsn}]},
+ FullName10 = filename:join(?WORK_DIR,RelName10),
+ ?m(ok, file:write_file(FullName10 ++ ".rel", io_lib:format("~p.\n", [Rel10]))),
%% Generate script files with systools and reltool and compare
ZPath = filename:join([LibDir,"*",ebin]),
@@ -625,26 +684,31 @@ create_script_sort(Config) ->
"in the app file: [tools]"},
reltool:get_script(Pid, RelName5)),
- ?msym({error,_,{undefined_applications,_}},
- systools_make_script(FullName6,ZPath)),
- ?m({error, "Undefined applications: [tools,mnesia]"},
- reltool:get_script(Pid, RelName6)),
+ ?msym({ok,_,_}, systools_make_script(FullName6,ZPath)),
+ {ok, [SystoolsScript6]} = ?msym({ok,[_]}, file:consult(FullName6++".script")),
+ {ok, Script6} = ?msym({ok, _}, reltool:get_script(Pid, RelName6)),
+ ?m(equal, diff_script(SystoolsScript6, Script6)),
?msym({error,_,{circular_dependencies,_}},
systools_make_script(FullName7,ZPath)),
?m({error,"Circular dependencies: [x,y]"},
reltool:get_script(Pid, RelName7)),
- ?msym({error,_,{undefined_applications,_}},
- systools_make_script(FullName8,ZPath)),
- ?m({error, "Undefined applications: [tools]"},
- reltool:get_script(Pid, RelName8)),
+ ?msym({ok,_,_}, systools_make_script(FullName8,ZPath)),
+ {ok, [SystoolsScript8]} = ?msym({ok,[_]}, file:consult(FullName8++".script")),
+ {ok, Script8} = ?msym({ok, _}, reltool:get_script(Pid, RelName8)),
+ ?m(equal, diff_script(SystoolsScript8, Script8)),
?msym({ok,_,_}, systools_make_script(FullName9,ZPath)),
{ok, [SystoolsScript9]} = ?msym({ok,[_]}, file:consult(FullName9++".script")),
{ok, Script9} = ?msym({ok, _}, reltool:get_script(Pid, RelName9)),
?m(equal, diff_script(SystoolsScript9, Script9)),
+ ?msym({ok,_,_}, systools_make_script(FullName10,ZPath)),
+ {ok, [SystoolsScript10]} = ?msym({ok,[_]}, file:consult(FullName10++".script")),
+ {ok, Script10} = ?msym({ok, _}, reltool:get_script(Pid, RelName10)),
+ ?m(equal, diff_script(SystoolsScript10, Script10)),
+
%% Stop server
?m(ok, reltool:stop(Pid)),
ok.
@@ -950,8 +1014,6 @@ create_multiple_standalone(Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Generate old type of target system
-
-create_old_target(_Config) -> {skip, "Old style of target"};
create_old_target(_Config) ->
%% Configure the server
@@ -974,8 +1036,7 @@ create_old_target(_Config) ->
?m(ok, reltool_utils:recursive_delete(TargetDir)),
?m(ok, file:make_dir(TargetDir)),
ok = ?m(ok, reltool:create_target([{config, Config}], TargetDir)),
-
- %% io:format("Will fail on Windows (should patch erl.ini)\n", []),
+
ok = ?m(ok, reltool:install(RelName2, TargetDir)),
Erl = filename:join([TargetDir, "bin", "erl"]),
@@ -985,6 +1046,61 @@ create_old_target(_Config) ->
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Generate target system
+
+create_slim(Config) ->
+ %% Configure the server
+ RelName = "slim",
+ RelVsn = "1.0",
+
+ DataDir = ?config(data_dir,Config),
+ LibDir = filename:join(DataDir,"slim"),
+
+ Sys =
+ {sys,
+ [
+ {root_dir, code:root_dir()},
+ {lib_dirs, []},
+ {boot_rel, RelName},
+ {rel, RelName, RelVsn, [sasl, stdlib, kernel, a]},
+ {app, sasl, [{incl_cond, include}]},
+ {app, a, [{incl_cond, include},
+ {lib_dir,filename:join(LibDir,"a-1.0")}]},
+ {excl_lib,otp_root}
+ ]},
+
+ %% Generate target file
+ TargetDir = filename:absname(filename:join([?WORK_DIR, "target_slim"])),
+ ?m(ok, reltool_utils:recursive_delete(TargetDir)),
+ ?m(ok, file:make_dir(TargetDir)),
+ ?log("SPEC: ~p\n", [reltool:get_target_spec([{config, Sys}])]),
+ ok = ?m(ok, reltool:create_target([{config, Sys}], TargetDir)),
+
+ TargetLibDir = filename:join(TargetDir,"lib"),
+ TargetRelDir = filename:join(TargetDir,"releases"),
+ TargetRelVsnDir = filename:join(TargetRelDir,RelVsn),
+
+ {ok,["a-1.0.ez"]} = file:list_dir(TargetLibDir),
+
+ RootDir = code:root_dir(),
+ Erl = filename:join([RootDir, "bin", "erl"]),
+ EscapedQuote =
+ case os:type() of
+ {win32,_} -> "\\\"";
+ _ -> "\""
+ end,
+ Args = ["-boot_var", "RELTOOL_EXT_LIB", TargetLibDir,
+ "-boot", filename:join(TargetRelVsnDir,RelName),
+ "-sasl", "releases_dir", EscapedQuote++TargetRelDir++EscapedQuote],
+ {ok, Node} = ?msym({ok, _}, start_node(?NODE_NAME, Erl, Args)),
+ ?msym(RootDir, rpc:call(Node, code, root_dir, [])),
+ ?msym([{RelName,RelVsn,_,permanent}],
+ rpc:call(Node,release_handler,which_releases,[])),
+ ?msym(ok, stop_node(Node)),
+
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Generate target system with eval_target_spec/3
eval_target_spec(_Config) ->
@@ -1891,7 +2007,7 @@ save_config(Config) ->
%%
%% x-1.0: x1.erl x2.erl x3.erl
%% \ / (x2 calls y1, x3 calls y2)
-%% y-1.0: y1.erl y2.erl
+%% y-1.0: y0.erl y1.erl y2.erl
%% \ (y1 calls z1)
%% z-1.0 z1.erl
%%
@@ -2021,6 +2137,47 @@ dependencies(Config) ->
ok.
+%% Test that incl_cond on mod level overwrites mod_cond on app level
+%% Uses same test applications as dependencies/1 above
+mod_incl_cond_derived(Config) ->
+ %% In app y: mod_cond=none means no module shall be included
+ %% but mod_cond is overwritten by incl_cond on mod level
+ Sys = {sys,[{lib_dirs,[filename:join(datadir(Config),"dependencies")]},
+ {incl_cond, exclude},
+ {app,kernel,[{incl_cond,include}]},
+ {app,sasl,[{incl_cond,include}]},
+ {app,stdlib,[{incl_cond,include}]},
+ {app,x,[{incl_cond,include}]},
+ {app,y,[{incl_cond,include},
+ {mod_cond,none},
+ {mod,y0,[{incl_cond,derived}]},
+ {mod,y2,[{incl_cond,derived}]}]}]},
+ {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys}])),
+
+ ?msym({ok,[#app{name=kernel},
+ #app{name=sasl},
+ #app{name=stdlib},
+ #app{name=x,uses_apps=[y]},
+ #app{name=y,uses_apps=[]}]},
+ reltool_server:get_apps(Pid,whitelist)),
+ {ok, Der} = ?msym({ok,_},reltool_server:get_apps(Pid,derived)),
+ ?msym([], rm_missing_app(Der)),
+ ?msym({ok,[]}, reltool_server:get_apps(Pid,source)),
+
+ %% 1. check that y0 is not included since it has
+ %% incl_cond=derived, but is not used by any other module.
+ ?msym({ok,#mod{is_included=undefined}}, reltool_server:get_mod(Pid,y0)),
+
+ %% 2. check that y1 is excluded since it has undefined incl_cond
+ %% on mod level, so mod_cond on app level shall be used.
+ ?msym({ok,#mod{is_included=false}}, reltool_server:get_mod(Pid,y1)),
+
+ %% 3. check that y2 is included since it has incl_cond=derived and
+ %% is used by x3.
+ ?msym({ok,#mod{is_included=true}}, reltool_server:get_mod(Pid,y2)),
+
+ ok.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
use_selected_vsn(Config) ->
LibDir1 = filename:join(datadir(Config),"use_selected_vsn"),
@@ -2145,6 +2302,39 @@ use_selected_vsn_relative_path(Config) ->
ok = file:set_cwd(Cwd),
ok.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Test that reltool recognizes an application with its real name even
+%% though it uses non standard format for its version number (in the
+%% directory name)
+non_standard_vsn_id(Config) ->
+ LibDir = filename:join(datadir(Config),"non_standard_vsn_id"),
+ B1Dir = filename:join(LibDir,"b-first"),
+ B2Dir = filename:join(LibDir,"b-second"),
+
+ %%-----------------------------------------------------------------
+ %% Default vsn of app b
+ Sys1 = {sys,[{lib_dirs,[LibDir]},
+ {incl_cond, exclude},
+ {app,kernel,[{incl_cond,include}]},
+ {app,sasl,[{incl_cond,include}]},
+ {app,stdlib,[{incl_cond,include}]},
+ {app,b,[{incl_cond,include}]}]},
+ {ok, Pid1} = ?msym({ok, _}, reltool:start_server([{config, Sys1}])),
+ ?msym({ok,#app{vsn="first",active_dir=B1Dir,sorted_dirs=[B1Dir,B2Dir]}},
+ reltool_server:get_app(Pid1,b)),
+
+ %%-----------------------------------------------------------------
+ %% Pre-selected vsn of app b
+ Sys2 = {sys,[{lib_dirs,[LibDir]},
+ {incl_cond, exclude},
+ {app,kernel,[{incl_cond,include}]},
+ {app,sasl,[{incl_cond,include}]},
+ {app,stdlib,[{incl_cond,include}]},
+ {app,b,[{incl_cond,include},{vsn,"second"}]}]},
+ {ok, Pid2} = ?msym({ok, _}, reltool:start_server([{config, Sys2}])),
+ ?msym({ok,#app{vsn="second",active_dir=B2Dir,sorted_dirs=[B1Dir,B2Dir]}},
+ reltool_server:get_app(Pid2,b)),
+ ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -2226,10 +2416,14 @@ mod_path(Node,Mod) ->
%% Node handling
start_node(Name, ErlPath) ->
+ start_node(Name, ErlPath, []).
+start_node(Name, ErlPath, Args0) ->
FullName = full_node_name(Name),
- CmdLine = mk_node_cmdline(Name, ErlPath),
- io:format("Starting node ~p: ~s~n", [FullName, CmdLine]),
- case open_port({spawn, CmdLine}, []) of
+ Args = mk_node_args(Name, Args0),
+ io:format("Starting node ~p: ~s~n",
+ [FullName, lists:flatten([[X," "] || X <- [ErlPath|Args]])]),
+ %io:format("open_port({spawn_executable, ~p}, [{args,~p}])~n",[ErlPath,Args]),
+ case open_port({spawn_executable, ErlPath}, [{args,Args}]) of
Port when is_port(Port) ->
unlink(Port),
erlang:port_close(Port),
@@ -2246,29 +2440,21 @@ stop_node(Node) ->
spawn(Node, fun () -> halt() end),
receive {nodedown, Node} -> ok end.
-mk_node_cmdline(Name) ->
- Prog = case catch init:get_argument(progname) of
- {ok,[[P]]} -> P;
- _ -> exit(no_progname_argument_found)
- end,
- mk_node_cmdline(Name, Prog).
-
-mk_node_cmdline(Name, Prog) ->
- Static = "-detached -noinput",
+mk_node_args(Name, Args) ->
Pa = filename:dirname(code:which(?MODULE)),
NameSw = case net_kernel:longnames() of
- false -> "-sname ";
- true -> "-name ";
+ false -> "-sname";
+ true -> "-name";
_ -> exit(not_distributed_node)
end,
{ok, Pwd} = file:get_cwd(),
NameStr = atom_to_list(Name),
- Prog ++ " "
- ++ Static ++ " "
- ++ NameSw ++ " " ++ NameStr ++ " "
- ++ "-pa " ++ Pa ++ " "
- ++ "-env ERL_CRASH_DUMP " ++ Pwd ++ "/erl_crash_dump." ++ NameStr ++ " "
- ++ "-setcookie " ++ atom_to_list(erlang:get_cookie()).
+ ["-detached", "-noinput",
+ NameSw, NameStr,
+ "-pa", Pa,
+ "-env", "ERL_CRASH_DUMP", Pwd ++ "/erl_crash_dump." ++ NameStr,
+ "-setcookie", atom_to_list(erlang:get_cookie())
+ | Args].
full_node_name(PreName) ->
HostSuffix = lists:dropwhile(fun ($@) -> false; (_) -> true end,
diff --git a/lib/reltool/test/reltool_server_SUITE_data/Makefile.src b/lib/reltool/test/reltool_server_SUITE_data/Makefile.src
index 21410ceaa9..02846f42b6 100644
--- a/lib/reltool/test/reltool_server_SUITE_data/Makefile.src
+++ b/lib/reltool/test/reltool_server_SUITE_data/Makefile.src
@@ -22,7 +22,11 @@ SEL_VSN= \
use_selected_vsn/b-3.0/ebin/b.@EMULATOR@ \
use_selected_vsn/lib2/b-2.0/ebin/b.@EMULATOR@
-all: $(OTP9229) $(DEPENDENCIES) $(ESCRIPT) $(SEL_VSN)
+SLIM= \
+ slim/a-1.0/ebin/a_sup.@EMULATOR@ \
+ slim/a-1.0/ebin/a.@EMULATOR@
+
+all: $(OTP9229) $(DEPENDENCIES) $(ESCRIPT) $(SEL_VSN) $(SLIM)
otp_9229/x-1.0/ebin/x.@EMULATOR@: otp_9229/x-1.0/src/x.erl
erlc $(EFLAGS) -ootp_9229/x-1.0/ebin otp_9229/x-1.0/src/x.erl
@@ -55,3 +59,8 @@ use_selected_vsn/b-3.0/ebin/b.@EMULATOR@: use_selected_vsn/b-3.0/src/b.erl
erlc $(EFLAGS) -ouse_selected_vsn/b-3.0/ebin use_selected_vsn/b-3.0/src/b.erl
use_selected_vsn/lib2/b-2.0/ebin/b.@EMULATOR@: use_selected_vsn/lib2/b-2.0/src/b.erl
erlc $(EFLAGS) -ouse_selected_vsn/lib2/b-2.0/ebin use_selected_vsn/lib2/b-2.0/src/b.erl
+
+slim/a-1.0/ebin/a_sup.@EMULATOR@: slim/a-1.0/src/a_sup.erl
+ erlc $(EFLAGS) -oslim/a-1.0/ebin slim/a-1.0/src/a_sup.erl
+slim/a-1.0/ebin/a.@EMULATOR@: slim/a-1.0/src/a.erl
+ erlc $(EFLAGS) -oslim/a-1.0/ebin slim/a-1.0/src/a.erl
diff --git a/lib/reltool/test/reltool_server_SUITE_data/dependencies/y-1.0/ebin/y.app b/lib/reltool/test/reltool_server_SUITE_data/dependencies/y-1.0/ebin/y.app
index d9dac371d7..39fdabeea4 100644
--- a/lib/reltool/test/reltool_server_SUITE_data/dependencies/y-1.0/ebin/y.app
+++ b/lib/reltool/test/reltool_server_SUITE_data/dependencies/y-1.0/ebin/y.app
@@ -2,6 +2,6 @@
{application, y,
[{description, "Library application in reltool dependency test"},
{vsn, "1.0"},
- {modules, [y1,y2]},
+ {modules, [y0,y1,y2]},
{registered, []},
{applications, [kernel, stdlib]}]}.
diff --git a/lib/reltool/test/reltool_server_SUITE_data/dependencies/y-1.0/src/y0.erl b/lib/reltool/test/reltool_server_SUITE_data/dependencies/y-1.0/src/y0.erl
new file mode 100644
index 0000000000..dc188ba7b6
--- /dev/null
+++ b/lib/reltool/test/reltool_server_SUITE_data/dependencies/y-1.0/src/y0.erl
@@ -0,0 +1,5 @@
+-module(y0).
+-compile(export_all).
+
+f() ->
+ ok.
diff --git a/lib/reltool/test/reltool_server_SUITE_data/non_standard_vsn_id/b-first/ebin/b.app b/lib/reltool/test/reltool_server_SUITE_data/non_standard_vsn_id/b-first/ebin/b.app
new file mode 100644
index 0000000000..55550a8190
--- /dev/null
+++ b/lib/reltool/test/reltool_server_SUITE_data/non_standard_vsn_id/b-first/ebin/b.app
@@ -0,0 +1,6 @@
+%% -*- erlang -*-
+{application, b,
+ [{description, "Reltool test app for using selected version of app"},
+ {vsn, "first"},
+ {modules, [b]},
+ {applications, [kernel, stdlib]}]}.
diff --git a/lib/reltool/test/reltool_server_SUITE_data/non_standard_vsn_id/b-first/src/b.erl b/lib/reltool/test/reltool_server_SUITE_data/non_standard_vsn_id/b-first/src/b.erl
new file mode 100644
index 0000000000..a6b4ff1c05
--- /dev/null
+++ b/lib/reltool/test/reltool_server_SUITE_data/non_standard_vsn_id/b-first/src/b.erl
@@ -0,0 +1,4 @@
+-module(b).
+-compile(export_all).
+
+foo() -> ok.
diff --git a/lib/reltool/test/reltool_server_SUITE_data/non_standard_vsn_id/b-second/ebin/b.app b/lib/reltool/test/reltool_server_SUITE_data/non_standard_vsn_id/b-second/ebin/b.app
new file mode 100644
index 0000000000..91e1365df7
--- /dev/null
+++ b/lib/reltool/test/reltool_server_SUITE_data/non_standard_vsn_id/b-second/ebin/b.app
@@ -0,0 +1,6 @@
+%% -*- erlang -*-
+{application, b,
+ [{description, "Reltool test app for using selected version of app"},
+ {vsn, "second"},
+ {modules, [b]},
+ {applications, [kernel, stdlib]}]}.
diff --git a/lib/reltool/test/reltool_server_SUITE_data/non_standard_vsn_id/b-second/src/b.erl b/lib/reltool/test/reltool_server_SUITE_data/non_standard_vsn_id/b-second/src/b.erl
new file mode 100644
index 0000000000..a6b4ff1c05
--- /dev/null
+++ b/lib/reltool/test/reltool_server_SUITE_data/non_standard_vsn_id/b-second/src/b.erl
@@ -0,0 +1,4 @@
+-module(b).
+-compile(export_all).
+
+foo() -> ok.
diff --git a/lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/ebin/a.app b/lib/reltool/test/reltool_server_SUITE_data/slim/a-1.0/ebin/a.app
index 200cfcfe47..3160e00da7 100644
--- a/lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/ebin/a.app
+++ b/lib/reltool/test/reltool_server_SUITE_data/slim/a-1.0/ebin/a.app
@@ -1,8 +1,7 @@
{application, a,
[{description, "A CXC 138 11"},
- {vsn, "2.0"},
- {modules, [{a, 1}, {a_sup,1}]},
+ {vsn, "1.0"},
+ {modules, [a, a_sup]},
{registered, [a_sup]},
{applications, [kernel, stdlib]},
- {env, [{key1, val1}]},
{mod, {a_sup, []}}]}.
diff --git a/lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/src/a.erl b/lib/reltool/test/reltool_server_SUITE_data/slim/a-1.0/src/a.erl
index cfe38b55ce..bb500bed69 100644
--- a/lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/src/a.erl
+++ b/lib/reltool/test/reltool_server_SUITE_data/slim/a-1.0/src/a.erl
@@ -20,6 +20,8 @@
-behaviour(gen_server).
+-vsn(1).
+
%% External exports
-export([start_link/0, a/0]).
%% Internal exports
diff --git a/lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/src/a_sup.erl b/lib/reltool/test/reltool_server_SUITE_data/slim/a-1.0/src/a_sup.erl
index a141c1767b..a141c1767b 100644
--- a/lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/src/a_sup.erl
+++ b/lib/reltool/test/reltool_server_SUITE_data/slim/a-1.0/src/a_sup.erl
diff --git a/lib/reltool/vsn.mk b/lib/reltool/vsn.mk
index 3811d897c7..6872539b8c 100644
--- a/lib/reltool/vsn.mk
+++ b/lib/reltool/vsn.mk
@@ -1 +1 @@
-RELTOOL_VSN = 0.6
+RELTOOL_VSN = 0.6.1
diff --git a/lib/runtime_tools/c_src/Makefile.in b/lib/runtime_tools/c_src/Makefile.in
index 754e6ccd78..d315a90e18 100644
--- a/lib/runtime_tools/c_src/Makefile.in
+++ b/lib/runtime_tools/c_src/Makefile.in
@@ -46,9 +46,6 @@ LDFLAGS += $(DED_LDFLAGS)
DTRACE_LIBNAME = dyntrace
SYSINCLUDE = $(DED_SYS_INCLUDE)
-ifeq ($(findstring vxworks,$(TARGET)),vxworks)
- SYSINCLUDE += -I$(ERL_TOP)/erts/etc/vxworks
-endif
TRACE_DRV_INCLUDES = $(SYSINCLUDE)
@@ -101,12 +98,8 @@ ifeq ($(findstring win32,$(TARGET)), win32)
SOLIBS = $(LIBDIR)/trace_ip_drv.dll $(LIBDIR)/trace_file_drv.dll
LN=cp
else
-ifeq ($(findstring vxworks,$(TARGET)),vxworks)
-SOLIBS = $(LIBDIR)/trace_ip_drv.eld $(LIBDIR)/trace_file_drv.eld
-else
SOLIBS = $(LIBDIR)/trace_ip_drv.so $(LIBDIR)/trace_file_drv.so
endif
-endif
# ----------------------------------------------------
# Targets
# ----------------------------------------------------
@@ -118,11 +111,11 @@ debug opt valgrind: $(SOLIBS) $(OBJDIR) $(LIBDIR) $(NIF_LIB)
ifdef DTRACE_ENABLED
DTRACE_USER_HEADER=$(OBJDIR)/dtrace_user.h
$(OBJDIR)/dtrace_user.h: ./dtrace_user.d
- dtrace -h -C $(INCLUDES) \
+ $(dtrace_verbose)dtrace -h -C $(INCLUDES) \
-s ./dtrace_user.d \
-o ./dtrace_user.tmp
- sed -e '/^#define[ ]*ERLANG_[A-Z0-9_]*(.*)/y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/' ./dtrace_user.tmp > $@
- rm ./dtrace_user.tmp
+ $(V_at)sed -e '/^#define[ ]*ERLANG_[A-Z0-9_]*(.*)/y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/' ./dtrace_user.tmp > $@
+ $(V_at)rm ./dtrace_user.tmp
else
DTRACE_USER_HEADER=
endif
@@ -131,7 +124,7 @@ DTRACE_OBJS =
ifdef DTRACE_ENABLED_2STEP
DTRACE_OBJS += $(OBJDIR)/dtrace_user.o
$(OBJDIR)/dtrace_user.o: $(before_DTrace_OBJS) $(OBJDIR)/dtrace_user.h
- dtrace -G -C \
+ $(dtrace_verbose)dtrace -G -C \
-s ./dtrace_user.d \
-o $@ $(before_DTrace_OBJS)
endif
@@ -145,35 +138,26 @@ $(LIBDIR):
-@mkdir -p $(LIBDIR)
$(OBJDIR)/dyntrace$(TYPEMARKER).o: dyntrace.c $(DTRACE_USER_HEADER)
- $(INSTALL_DIR) $(OBJDIR)
- $(CC) -c -o $@ $(ALL_CFLAGS) $<
+ $(V_at)$(INSTALL_DIR) $(OBJDIR)
+ $(V_CC) -c -o $@ $(ALL_CFLAGS) $<
$(NIF_LIB): $(DYNTRACE_OBJS)
- $(INSTALL_DIR) $(LIBDIR)
- $(LD) $(LDFLAGS) -o $@ $^ $(LDLIBS)
+ $(V_at)$(INSTALL_DIR) $(LIBDIR)
+ $(V_LD) $(LDFLAGS) -o $@ $^ $(LDLIBS)
$(OBJDIR)/%.o: %.c
- $(CC) -c -o $@ $(ALL_CFLAGS) $<
+ $(V_CC) -c -o $@ $(ALL_CFLAGS) $<
$(LIBDIR)/trace_ip_drv.so: $(TRACE_IP_DRV_OBJS)
- $(LD) $(LDFLAGS) -o $@ $^ -lc $(LIBS)
+ $(V_LD) $(LDFLAGS) -o $@ $^ -lc $(LIBS)
$(LIBDIR)/trace_file_drv.so: $(TRACE_FILE_DRV_OBJS)
- $(LD) $(LDFLAGS) -o $@ $^ -lc $(LIBS)
+ $(V_LD) $(LDFLAGS) -o $@ $^ -lc $(LIBS)
$(LIBDIR)/trace_ip_drv.dll: $(TRACE_IP_DRV_OBJS)
- $(LD) $(LDFLAGS) -o $@ $^ $(LIBS)
+ $(V_LD) $(LDFLAGS) -o $@ $^ $(LIBS)
$(LIBDIR)/trace_file_drv.dll: $(TRACE_FILE_DRV_OBJS)
- $(LD) $(LDFLAGS) -o $@ $^ $(LIBS)
-#
-# VxWorks is simply to different from Unix in this sense.
-# Here are the inference rules for VxWorks
-#
-$(LIBDIR)/trace_ip_drv.eld: $(TRACE_IP_DRV_OBJS)
- $(LD) $(LDFLAGS) -o $@ $^
-
-$(LIBDIR)/trace_file_drv.eld: $(TRACE_FILE_DRV_OBJS)
- $(LD) $(LDFLAGS) -o $@ $^
+ $(V_LD) $(LDFLAGS) -o $@ $^ $(LIBS)
clean:
rm -f $(SOLIBS) $(TRACE_IP_DRV_OBJS) $(TRACE_FILE_DRV_OBJS)
diff --git a/lib/runtime_tools/c_src/dtrace_user.d b/lib/runtime_tools/c_src/dtrace_user.d
index 3a80d0f7a3..9e180a3cb2 100644
--- a/lib/runtime_tools/c_src/dtrace_user.d
+++ b/lib/runtime_tools/c_src/dtrace_user.d
@@ -19,31 +19,11 @@
*/
provider erlang {
- /**
- * Send a single string to a probe.
- *
- * @param NUL-terminated string
+ /*
+ * The set of probes for use by Erlang code ... moved from here to
+ * erts/emulator/beam/erlang_dtrace.d until a more portable solution is
+ * found; see erlang_dtrace.d for details.
*/
- probe user_trace__s1(char* message);
-
- /**
- * Multi-purpose probe: up to 4 NUL-terminated strings and 4
- * 64-bit integer arguments.
- *
- * @param proc, the PID (string form) of the sending process
- * @param user_tag, the user tag of the sender
- * @param i1, integer
- * @param i2, integer
- * @param i3, integer
- * @param i4, integer
- * @param s1, string/iolist. D's arg6 is NULL if not given by Erlang
- * @param s2, string/iolist. D's arg7 is NULL if not given by Erlang
- * @param s3, string/iolist. D's arg8 is NULL if not given by Erlang
- * @param s4, string/iolist. D's arg9 is NULL if not given by Erlang
- */
- probe user_trace__i4s4(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
};
#pragma D attributes Evolving/Evolving/Common provider erlang provider
diff --git a/lib/runtime_tools/c_src/dyntrace.c b/lib/runtime_tools/c_src/dyntrace.c
index 96dbebbdfa..eef03afd1c 100644
--- a/lib/runtime_tools/c_src/dyntrace.c
+++ b/lib/runtime_tools/c_src/dyntrace.c
@@ -36,6 +36,10 @@
void dtrace_nifenv_str(ErlNifEnv *env, char *process_buf);
void get_string_maybe(ErlNifEnv *env, const ERL_NIF_TERM term, char **ptr, char *buf, int bufsiz);
+#ifdef HAVE_USE_DTRACE
+ERL_NIF_TERM erl_nif_user_trace_s1(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+ERL_NIF_TERM erl_nif_user_trace_i4s4(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+#endif
#ifdef VALGRIND
# include <valgrind/memcheck.h>
@@ -56,11 +60,13 @@ static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info);
static ERL_NIF_TERM available(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM user_trace_s1(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM user_trace_i4s4(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM user_trace_n(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ErlNifFunc nif_funcs[] = {
{"available", 0, available},
{"user_trace_s1", 1, user_trace_s1},
- {"user_trace_i4s4", 9, user_trace_i4s4}
+ {"user_trace_i4s4", 9, user_trace_i4s4},
+ {"user_trace_n", 10, user_trace_n}
};
ERL_NIF_INIT(dyntrace, nif_funcs, load, NULL, NULL, NULL)
@@ -96,76 +102,25 @@ static ERL_NIF_TERM available(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[
static ERL_NIF_TERM user_trace_s1(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
#ifdef HAVE_USE_DTRACE
- ErlNifBinary message_bin;
- DTRACE_CHARBUF(messagebuf, MESSAGE_BUFSIZ + 1);
-
- if (DTRACE_ENABLED(user_trace_s1)) {
- if (!enif_inspect_iolist_as_binary(env, argv[0], &message_bin) ||
- message_bin.size > MESSAGE_BUFSIZ) {
- return atom_badarg;
- }
- memcpy(messagebuf, (char *) message_bin.data, message_bin.size);
- messagebuf[message_bin.size] = '\0';
- DTRACE1(user_trace_s1, messagebuf);
- return atom_true;
- } else {
- return atom_false;
- }
+ return erl_nif_user_trace_s1(env, argc, argv);
#else
return atom_error;
#endif
}
-void
-get_string_maybe(ErlNifEnv *env,
- const ERL_NIF_TERM term, char **ptr, char *buf, int bufsiz)
+static ERL_NIF_TERM user_trace_i4s4(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
- ErlNifBinary str_bin;
-
- if (!enif_inspect_iolist_as_binary(env, term, &str_bin) ||
- str_bin.size > bufsiz) {
- *ptr = NULL;
- } else {
- memcpy(buf, (char *) str_bin.data, str_bin.size);
- buf[str_bin.size] = '\0';
- *ptr = buf;
- }
+#ifdef HAVE_USE_DTRACE
+ return erl_nif_user_trace_i4s4(env, argc, argv);
+#else
+ return atom_error;
+#endif
}
-static ERL_NIF_TERM user_trace_i4s4(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+static ERL_NIF_TERM user_trace_n(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
#ifdef HAVE_USE_DTRACE
- DTRACE_CHARBUF(procbuf, 32 + 1);
- DTRACE_CHARBUF(user_tagbuf, MESSAGE_BUFSIZ + 1);
- char *utbuf = NULL;
- ErlNifSInt64 i1, i2, i3, i4;
- DTRACE_CHARBUF(messagebuf1, MESSAGE_BUFSIZ + 1);
- DTRACE_CHARBUF(messagebuf2, MESSAGE_BUFSIZ + 1);
- DTRACE_CHARBUF(messagebuf3, MESSAGE_BUFSIZ + 1);
- DTRACE_CHARBUF(messagebuf4, MESSAGE_BUFSIZ + 1);
- char *mbuf1 = NULL, *mbuf2 = NULL, *mbuf3 = NULL, *mbuf4 = NULL;
-
- if (DTRACE_ENABLED(user_trace_i4s4)) {
- dtrace_nifenv_str(env, procbuf);
- get_string_maybe(env, argv[0], &utbuf, user_tagbuf, MESSAGE_BUFSIZ);
- if (! enif_get_int64(env, argv[1], &i1))
- i1 = 0;
- if (! enif_get_int64(env, argv[2], &i2))
- i2 = 0;
- if (! enif_get_int64(env, argv[3], &i3))
- i3 = 0;
- if (! enif_get_int64(env, argv[4], &i4))
- i4 = 0;
- get_string_maybe(env, argv[5], &mbuf1, messagebuf1, MESSAGE_BUFSIZ);
- get_string_maybe(env, argv[6], &mbuf2, messagebuf2, MESSAGE_BUFSIZ);
- get_string_maybe(env, argv[7], &mbuf3, messagebuf3, MESSAGE_BUFSIZ);
- get_string_maybe(env, argv[8], &mbuf4, messagebuf4, MESSAGE_BUFSIZ);
- DTRACE10(user_trace_i4s4, procbuf, utbuf,
- i1, i2, i3, i4, mbuf1, mbuf2, mbuf3, mbuf4);
- return atom_true;
- } else {
- return atom_false;
- }
+ return erl_nif_user_trace_n(env, argc, argv);
#else
return atom_error;
#endif
diff --git a/lib/runtime_tools/c_src/trace_ip_drv.c b/lib/runtime_tools/c_src/trace_ip_drv.c
index 7f7ab8dd9d..a7d132ca6e 100644
--- a/lib/runtime_tools/c_src/trace_ip_drv.c
+++ b/lib/runtime_tools/c_src/trace_ip_drv.c
@@ -34,21 +34,12 @@
#include <stdlib.h>
#include <string.h>
#ifndef __WIN32__
-# ifdef VXWORKS
-# include <sockLib.h>
-# include <sys/times.h>
-# include <iosLib.h>
-# include <taskLib.h>
-# include <selectLib.h>
-# include <ioLib.h>
-# include "reclaim.h"
-# endif
-# include <unistd.h>
-# include <errno.h>
-# include <sys/types.h>
-# include <sys/socket.h>
-# include <netinet/in.h>
-# include <fcntl.h>
+# include <unistd.h>
+# include <errno.h>
+# include <sys/types.h>
+# include <sys/socket.h>
+# include <netinet/in.h>
+# include <fcntl.h>
#endif
#ifdef DEBUG
@@ -590,8 +581,8 @@ static void *my_alloc(size_t size)
void *ret;
if ((ret = driver_alloc(size)) == NULL) {
/* May or may not work... */
- fprintf(stderr, "Could not allocate %d bytes of memory in %s.",
- (int) size, __FILE__);
+ fprintf(stderr, "Could not allocate %lu bytes of memory in %s.",
+ (unsigned long) size, __FILE__);
exit(1);
}
return ret;
@@ -605,8 +596,8 @@ static ErlDrvBinary *my_alloc_binary(int size)
ErlDrvBinary *ret;
if ((ret = driver_alloc_binary(size)) == NULL) {
/* May or may not work... */
- fprintf(stderr, "Could not allocate a binary of %d bytes in %s.",
- (int) size, __FILE__);
+ fprintf(stderr, "Could not allocate a binary of %lu bytes in %s.",
+ (unsigned long) size, __FILE__);
exit(1);
}
return ret;
@@ -910,7 +901,7 @@ static void stop_select(ErlDrvEvent event, void* _)
WSACloseEvent((HANDLE)event);
}
-#else /* UNIX/VXWORKS */
+#else /* UNIX */
static int my_driver_select(TraceIpData *desc, SOCKET fd, int flags, enum MySelectOp op)
{
diff --git a/lib/runtime_tools/doc/src/Makefile b/lib/runtime_tools/doc/src/Makefile
index d240b287c3..51d93df418 100644
--- a/lib/runtime_tools/doc/src/Makefile
+++ b/lib/runtime_tools/doc/src/Makefile
@@ -43,9 +43,11 @@ XML_APPLICATION_FILES = ref_man.xml
XML_REF3_FILES = dbg.xml dyntrace.xml erts_alloc_config.xml
XML_REF6_FILES = runtime_tools_app.xml
-XML_PART_FILES = part_notes.xml part_notes_history.xml
+XML_PART_FILES = part_notes.xml part_notes_history.xml part.xml
XML_CHAPTER_FILES = notes.xml notes_history.xml
+GENERATED_XML_FILES = DTRACE.xml SYSTEMTAP.xml
+
BOOK_FILES = book.xml
XML_FILES = \
@@ -78,6 +80,11 @@ DVIPS_FLAGS +=
# ----------------------------------------------------
# Targets
# ----------------------------------------------------
+$(XML_FILES): $(GENERATED_XML_FILES)
+
+%.xml: $(ERL_TOP)/HOWTO/%.md $(ERL_TOP)/make/emd2exml
+ $(ERL_TOP)/make/emd2exml $< $@
+
$(HTMLDIR)/%.gif: %.gif
$(INSTALL_DATA) $< $@
diff --git a/lib/runtime_tools/doc/src/book.xml b/lib/runtime_tools/doc/src/book.xml
index 3f0dd7d55e..ad7d709644 100644
--- a/lib/runtime_tools/doc/src/book.xml
+++ b/lib/runtime_tools/doc/src/book.xml
@@ -34,6 +34,9 @@
<preamble>
<contents level="2"></contents>
</preamble>
+ <parts lift="no">
+ <xi:include href="part.xml"/>
+ </parts>
<applications>
<xi:include href="ref_man.xml"/>
</applications>
diff --git a/lib/runtime_tools/doc/src/dbg.xml b/lib/runtime_tools/doc/src/dbg.xml
index c7c5cd4ff0..d8c82b2459 100644
--- a/lib/runtime_tools/doc/src/dbg.xml
+++ b/lib/runtime_tools/doc/src/dbg.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2011</year>
+ <year>1996</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -101,7 +101,8 @@
allowed:</p>
<pre>
4> <input>dbg:fun2ms(fun([M,N]) when N > X, is_atomm(M) -> return_trace() end).</input>
-Error: fun containing local erlang function calls ('is_atomm' called in guard) cannot be translated into match_spec
+Error: fun containing local erlang function calls ('is_atomm' called in guard)\
+ cannot be translated into match_spec
{error,transform_error}
5> <input>dbg:fun2ms(fun([M,N]) when N > X, is_atom(M) -> return_trace() end).</input>
[{['$1','$2'],[{'>','$2',{const,3}},{is_atom,'$1'}],[{return_trace}]}]</pre>
diff --git a/lib/runtime_tools/doc/src/dyntrace.xml b/lib/runtime_tools/doc/src/dyntrace.xml
index 5fc5530f25..f0149d0665 100644
--- a/lib/runtime_tools/doc/src/dyntrace.xml
+++ b/lib/runtime_tools/doc/src/dyntrace.xml
@@ -42,7 +42,7 @@
</list>
<p>Both building with dynamic trace probes and using them is experimental and unsupported by Erlang/OTP. It is included as an option for the developer to trace and debug performance issues in their systems.</p>
<p>The original implementation is mostly done by Scott Lystiger Fritchie as an Open Source Contribution and it should be viewed as such even though the source for dynamic tracing as well as this module is included in the main distribution. However, the ability to use dynamic tracing of the virtual machine is a very valuable contribution which OTP has every intention to maintain as a tool for the developer.</p>
- <p>How to write <c>d</c> programs or <c>systemtap</c> scripts can be learned from books and from a lot of pages on the Internet. This manual page does not include any documentation about using the dynamic trace tools of respective platform. The <c>examples</c> directory of the <c>runtime_tools</c> application however contains comprehensive examples of both <c>d</c> and <c>systemtap</c> programs that will help you get started. Another source of information is the <c>README.dtrace(.md)</c> and <c>README.systemtap(.md)</c> files in the Erlang source top directory.</p>
+ <p>How to write <c>d</c> programs or <c>systemtap</c> scripts can be learned from books and from a lot of pages on the Internet. This manual page does not include any documentation about using the dynamic trace tools of respective platform. The <c>examples</c> directory of the <c>runtime_tools</c> application however contains comprehensive examples of both <c>d</c> and <c>systemtap</c> programs that will help you get started. Another source of information is the <seealso marker="DTRACE">dtrace</seealso> and <seealso marker="SYSTEMTAP">systemtap</seealso> chapters in the Runtime Tools Users' Guide.</p>
</description>
<funcs>
<func>
diff --git a/lib/runtime_tools/doc/src/notes.xml b/lib/runtime_tools/doc/src/notes.xml
index 2d4a206e1c..90641719c5 100644
--- a/lib/runtime_tools/doc/src/notes.xml
+++ b/lib/runtime_tools/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2004</year><year>2011</year>
+ <year>2004</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -31,6 +31,52 @@
<p>This document describes the changes made to the Runtime_Tools
application.</p>
+<section><title>Runtime_Tools 1.8.9</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Change the module-level docs to give complete
+ step-by-step instructions for using the `dyntrace:p()`
+ trace function. (Thanks to Scott Lystig Fritchie)</p>
+ <p>
+ Own Id: OTP-10141</p>
+ </item>
+ <item>
+ <p>
+ Add 1024 separate USDT probes to dyntrace.erl and
+ dyntrace.c (Thanks to Scott Lystig Fritchie)</p>
+ <p>
+ Own Id: OTP-10143</p>
+ </item>
+ <item>
+ <p>
+ Relocate bodies of DTrace probes to the statically-linked
+ VM.</p>
+ <p>
+ Due to various operating systems (in both the DTrace and
+ SystemTap worlds) not fully supporting DTrace probes (or
+ SystemTap-compatibility mode probes) in shared libraries,
+ we relocate those probes to the statically-linked virtual
+ machine. This could be seen as pollution of the pristine
+ VM by a (yet) experimental feature. However:</p>
+ <p>
+ 1. This code can be eliminated completely by the C
+ preprocessor. 2. Leaving the probes in the dyntrace NIF
+ shared library simply does not work correctly on too many
+ platforms. *Many* thanks to Macneil Shonle at Basho for
+ assisting when my RSI-injured fingers gave out. (note:
+ Solaris 10 and FreeBSD 9.0-RELEASE can take a long time
+ to compile)</p>
+ <p>
+ Own Id: OTP-10189</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Runtime_Tools 1.8.8</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/runtime_tools/doc/src/part.xml b/lib/runtime_tools/doc/src/part.xml
new file mode 100644
index 0000000000..948d4a8020
--- /dev/null
+++ b/lib/runtime_tools/doc/src/part.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE part SYSTEM "part.dtd">
+
+<part xmlns:xi="http://www.w3.org/2001/XInclude">
+ <header>
+ <copyright>
+ <year>2012</year><year>2012</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>Runtime Tools User's Guide</title>
+ <prepared>Lukas Larsson</prepared>
+ <docno></docno>
+ <date>2012-07-18</date>
+ <rev></rev>
+ <file>part.xml</file>
+ </header>
+
+ <description>
+ <p><em>Runtime Tools</em></p>
+ </description>
+
+ <xi:include href="DTRACE.xml"/>
+ <xi:include href="SYSTEMTAP.xml"/>
+</part>
+
+
+
+
diff --git a/lib/runtime_tools/examples/user-probe-n.d b/lib/runtime_tools/examples/user-probe-n.d
new file mode 100644
index 0000000000..06a3e5c9b9
--- /dev/null
+++ b/lib/runtime_tools/examples/user-probe-n.d
@@ -0,0 +1,44 @@
+/* example usage: dtrace -q -s /path/to/user-probe.d */
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Scott Lystig Fritchie 2011-2012. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+erlang*:::user_trace-n0
+{
+ printf("probe n0: %s %s %d %d %d %d '%s' '%s' '%s' '%s'\n",
+ copyinstr(arg0),
+ arg1 == NULL ? "" : copyinstr(arg1),
+ arg2, arg3, arg4, arg5,
+ arg6 == NULL ? "" : copyinstr(arg6),
+ arg7 == NULL ? "" : copyinstr(arg7),
+ arg8 == NULL ? "" : copyinstr(arg8),
+ arg9 == NULL ? "" : copyinstr(arg9));
+}
+
+erlang*:::user_trace-n1
+{
+ printf("probe n1: %s %s %d %d %d %d '%s' '%s' '%s' '%s'\n",
+ copyinstr(arg0),
+ arg1 == NULL ? "" : copyinstr(arg1),
+ arg2, arg3, arg4, arg5,
+ arg6 == NULL ? "" : copyinstr(arg6),
+ arg7 == NULL ? "" : copyinstr(arg7),
+ arg8 == NULL ? "" : copyinstr(arg8),
+ arg9 == NULL ? "" : copyinstr(arg9));
+}
+
diff --git a/lib/runtime_tools/examples/user-probe-n.systemtap b/lib/runtime_tools/examples/user-probe-n.systemtap
new file mode 100644
index 0000000000..6aa415bb67
--- /dev/null
+++ b/lib/runtime_tools/examples/user-probe-n.systemtap
@@ -0,0 +1,53 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Scott Lystig Fritchie and Andreas Schultz, 2011-2012. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, 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 assumes that you're using the non-SMP-enabled Erlang
+ * virtual machine, "beam". The SMP-enabled VM is called "beam.smp".
+ * Note that other variations of the virtual machine also have
+ * different names, e.g. the debug build of the SMP-enabled VM
+ * is "beam.debug.smp".
+ *
+ * To use a different virtual machine, replace each instance of
+ * "beam" with "beam.smp" or the VM name appropriate to your
+ * environment.
+ */
+
+probe process("beam").mark("user_trace-n0")
+{
+ printf("probe n0: %s %s %d %d %d %d '%s' '%s' '%s' '%s'\n",
+ user_string($arg1),
+ $arg2 == NULL ? "" : user_string($arg2),
+ $arg3, $arg4, $arg5, $arg6,
+ $arg7 == NULL ? "" : user_string($arg7),
+ $arg8 == NULL ? "" : user_string($arg8),
+ $arg9 == NULL ? "" : user_string($arg9),
+ $arg9 == NULL ? "" : user_string($arg9));
+}
+
+probe process("beam").mark("user_trace-n1")
+{
+ printf("probe n1: %s %s %d %d %d %d '%s' '%s' '%s' '%s'\n",
+ user_string($arg1),
+ $arg2 == NULL ? "" : user_string($arg2),
+ $arg3, $arg4, $arg5, $arg6,
+ $arg7 == NULL ? "" : user_string($arg7),
+ $arg8 == NULL ? "" : user_string($arg8),
+ $arg9 == NULL ? "" : user_string($arg9),
+ $arg9 == NULL ? "" : user_string($arg9));
+}
diff --git a/lib/runtime_tools/examples/user-probe.systemtap b/lib/runtime_tools/examples/user-probe.systemtap
index b41d7483ad..0482235324 100644
--- a/lib/runtime_tools/examples/user-probe.systemtap
+++ b/lib/runtime_tools/examples/user-probe.systemtap
@@ -28,12 +28,12 @@
* environment.
*/
-probe process("dyntrace.so").mark("user_trace-s1")
+probe process("beam").mark("user_trace-s1")
{
printf("%s\n", user_string($arg1));
}
-probe process("dyntrace.so").mark("user_trace-i4s4")
+probe process("beam").mark("user_trace-i4s4")
{
printf("%s %s %d %d %d %d '%s' '%s' '%s' '%s'\n",
user_string($arg1),
diff --git a/lib/runtime_tools/src/Makefile b/lib/runtime_tools/src/Makefile
index d6750f3a88..53b9ce34e4 100644
--- a/lib/runtime_tools/src/Makefile
+++ b/lib/runtime_tools/src/Makefile
@@ -35,6 +35,7 @@ RELSYSDIR = $(RELEASE_PATH)/lib/runtime_tools-$(VSN)
# ----------------------------------------------------
MODULES= \
+ appmon_info \
erts_alloc_config \
runtime_tools \
runtime_tools_sup \
@@ -80,10 +81,10 @@ clean:
rm -f errs core *~
$(APP_TARGET): $(APP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
docs:
diff --git a/lib/appmon/src/appmon_info.erl b/lib/runtime_tools/src/appmon_info.erl
index 332140f69d..332140f69d 100644
--- a/lib/appmon/src/appmon_info.erl
+++ b/lib/runtime_tools/src/appmon_info.erl
diff --git a/lib/runtime_tools/src/dbg.erl b/lib/runtime_tools/src/dbg.erl
index 385047ee73..d35c8e781e 100644
--- a/lib/runtime_tools/src/dbg.erl
+++ b/lib/runtime_tools/src/dbg.erl
@@ -431,10 +431,8 @@ trace_port1(file, Filename, Options) ->
fun() ->
Name = filename:absname(Filename),
%% Absname is needed since the driver uses
- %% the supplied name without further investigations,
- %% and if the name is relative the resulting path
- %% might be too long which can cause a bus error
- %% on vxworks instead of a nice error code return.
+ %% the supplied name without further investigations.
+
%% Also, the absname must be found inside the fun,
%% in case the actual node where the port shall be
%% started is on another node (or even another host)
diff --git a/lib/runtime_tools/src/dyntrace.erl b/lib/runtime_tools/src/dyntrace.erl
index 388c7679b9..f7dbef6929 100644
--- a/lib/runtime_tools/src/dyntrace.erl
+++ b/lib/runtime_tools/src/dyntrace.erl
@@ -6,23 +6,22 @@
%%% work on any operating system platform where user-space DTrace/Systemtap
%%% (and in the future LttNG UST) probes are supported.
%%%
-%%% Use the `dyntrace:init()' function to load the NIF shared library and
-%%% to initialize library's private state.
-%%%
%%% It is recommended that you use the `dyntrace:p()' function to add
%%% Dynamic trace probes to your Erlang code. This function can accept up to
%%% four integer arguments and four string arguments; the integer
-%%% argument(s) must come before any string argument. For example:
+%%% argument(s) must come before any string argument.
+%%%
+%%% If using DTrace, enable the dynamic trace probe using the 'dtrace'
+%%% command, for example:
+%%%
+%%% dtrace -s /your/path/to/lib/runtime_tools-1.8.7/examples/user-probe.d
+%%%
+%%% Then, back at the Erlang shell, try this example:
%%% ```
%%% 1> dyntrace:put_tag("GGOOOAAALL!!!!!").
%%% true
-%%% 2> dyntrace:init().
-%%% ok
-%%%
-%%% % % % If using dtrace, enable the Dynamic trace probe using the 'dtrace'
-%%% % % % command.
%%%
-%%% 3> dyntrace:p(7, 8, 9, "one", "four").
+%%% 2> dyntrace:p(7, 8, 9, "one", "four").
%%% true
%%% '''
%%%
@@ -38,15 +37,16 @@
-export([available/0,
user_trace_s1/1, % TODO: unify with pid & tag args like user_trace_i4s4
- p/0, p/1, p/2, p/3, p/4, p/5, p/6, p/7, p/8]).
+ p/0, p/1, p/2, p/3, p/4, p/5, p/6, p/7, p/8,
+ pn/1, pn/2, pn/3, pn/4, pn/5, pn/6, pn/7, pn/8, pn/9]).
-export([put_tag/1, get_tag/0, get_tag_data/0, spread_tag/1, restore_tag/1]).
--export([scaff/0]). % Development only
-export([user_trace_i4s4/9]). % Know what you're doing!
-on_load(on_load/0).
-type probe_arg() :: integer() | iolist().
-type int_p_arg() :: integer() | iolist() | undef.
+-type n_probe_label() :: 0..1023.
%% The *_maybe() types use atom() instead of a stricter 'undef'
%% because user_trace_i4s4/9 is exposed to the outside world, and
@@ -105,7 +105,7 @@ available() ->
user_trace_s1(_Message) ->
erlang:nif_error(nif_not_loaded).
--spec user_trace_i4s4(iolist(),
+-spec user_trace_i4s4(binary() | undefined,
integer_maybe(), integer_maybe(),
integer_maybe(), integer_maybe(),
iolist_maybe(), iolist_maybe(),
@@ -115,6 +115,16 @@ user_trace_s1(_Message) ->
user_trace_i4s4(_, _, _, _, _, _, _, _, _) ->
erlang:nif_error(nif_not_loaded).
+-spec user_trace_n(n_probe_label(), binary() | undefined,
+ integer_maybe(), integer_maybe(),
+ integer_maybe(), integer_maybe(),
+ iolist_maybe(), iolist_maybe(),
+ iolist_maybe(), iolist_maybe()) ->
+ true | false | error | badarg.
+
+user_trace_n(_, _, _, _, _, _, _, _, _, _) ->
+ erlang:nif_error(nif_not_loaded).
+
%%%
%%% Erlang support functions
%%%
@@ -218,6 +228,106 @@ user_trace_int(I1, I2, I3, I4, S1, S2, S3, S4) ->
false
end.
+-spec pn(n_probe_label()) -> true | false | error | badarg.
+
+pn(ProbeLabel) ->
+ user_trace_n_int(ProbeLabel, undef, undef, undef, undef, undef, undef, undef, undef).
+
+-spec pn(n_probe_label(), probe_arg()) -> true | false | error | badarg.
+
+pn(ProbeLabel, I1) when is_integer(I1) ->
+ user_trace_n_int(ProbeLabel, I1, undef, undef, undef, undef, undef, undef, undef);
+pn(ProbeLabel, S1) ->
+ user_trace_n_int(ProbeLabel, undef, undef, undef, undef, S1, undef, undef, undef).
+
+-spec pn(n_probe_label(), probe_arg(), probe_arg()) -> true | false | error | badarg.
+
+pn(ProbeLabel, I1, I2) when is_integer(I1), is_integer(I2) ->
+ user_trace_n_int(ProbeLabel, I1, I2, undef, undef, undef, undef, undef, undef);
+pn(ProbeLabel, I1, S1) when is_integer(I1) ->
+ user_trace_n_int(ProbeLabel, I1, undef, undef, undef, S1, undef, undef, undef);
+pn(ProbeLabel, S1, S2) ->
+ user_trace_n_int(ProbeLabel, undef, undef, undef, undef, S1, S2, undef, undef).
+
+-spec pn(n_probe_label(), probe_arg(), probe_arg(), probe_arg()) -> true | false | error | badarg.
+
+pn(ProbeLabel, I1, I2, I3) when is_integer(I1), is_integer(I2), is_integer(I3) ->
+ user_trace_n_int(ProbeLabel, I1, I2, I3, undef, undef, undef, undef, undef);
+pn(ProbeLabel, I1, I2, S1) when is_integer(I1), is_integer(I2) ->
+ user_trace_n_int(ProbeLabel, I1, I2, undef, undef, S1, undef, undef, undef);
+pn(ProbeLabel, I1, S1, S2) when is_integer(I1) ->
+ user_trace_n_int(ProbeLabel, I1, undef, undef, undef, S1, S2, undef, undef);
+pn(ProbeLabel, S1, S2, S3) ->
+ user_trace_n_int(ProbeLabel, undef, undef, undef, undef, S1, S2, S3, undef).
+
+-spec pn(n_probe_label(), probe_arg(), probe_arg(), probe_arg(), probe_arg()) ->
+ true | false | error | badarg.
+
+pn(ProbeLabel, I1, I2, I3, I4) when is_integer(I1), is_integer(I2), is_integer(I3), is_integer(I4) ->
+ user_trace_n_int(ProbeLabel, I1, I2, I3, I4, undef, undef, undef, undef);
+pn(ProbeLabel, I1, I2, I3, S1) when is_integer(I1), is_integer(I2), is_integer(I3) ->
+ user_trace_n_int(ProbeLabel, I1, I2, I3, undef, S1, undef, undef, undef);
+pn(ProbeLabel, I1, I2, S1, S2) when is_integer(I1), is_integer(I2) ->
+ user_trace_n_int(ProbeLabel, I1, I2, undef, undef, S1, S2, undef, undef);
+pn(ProbeLabel, I1, S1, S2, S3) when is_integer(I1) ->
+ user_trace_n_int(ProbeLabel, I1, undef, undef, undef, S1, S2, S3, undef);
+pn(ProbeLabel, S1, S2, S3, S4) ->
+ user_trace_n_int(ProbeLabel, undef, undef, undef, undef, S1, S2, S3, S4).
+
+-spec pn(n_probe_label(), probe_arg(), probe_arg(), probe_arg(), probe_arg(),
+ probe_arg()) ->
+ true | false | error | badarg.
+
+pn(ProbeLabel, I1, I2, I3, I4, S1) when is_integer(I1), is_integer(I2), is_integer(I3), is_integer(I4) ->
+ user_trace_n_int(ProbeLabel, I1, I2, I3, I4, S1, undef, undef, undef);
+pn(ProbeLabel, I1, I2, I3, S1, S2) when is_integer(I1), is_integer(I2), is_integer(I3) ->
+ user_trace_n_int(ProbeLabel, I1, I2, I3, undef, S1, S2, undef, undef);
+pn(ProbeLabel, I1, I2, S1, S2, S3) when is_integer(I1), is_integer(I2) ->
+ user_trace_n_int(ProbeLabel, I1, I2, undef, undef, S1, S2, S3, undef);
+pn(ProbeLabel, I1, S1, S2, S3, S4) when is_integer(I1) ->
+ user_trace_n_int(ProbeLabel, I1, undef, undef, undef, S1, S2, S3, S4).
+
+-spec pn(n_probe_label(), probe_arg(), probe_arg(), probe_arg(), probe_arg(),
+ probe_arg(), probe_arg()) ->
+ true | false | error | badarg.
+
+pn(ProbeLabel, I1, I2, I3, I4, S1, S2) when is_integer(I1), is_integer(I2), is_integer(I3), is_integer(I4) ->
+ user_trace_n_int(ProbeLabel, I1, I2, I3, I4, S1, S2, undef, undef);
+pn(ProbeLabel, I1, I2, I3, S1, S2, S3) when is_integer(I1), is_integer(I2), is_integer(I3) ->
+ user_trace_n_int(ProbeLabel, I1, I2, I3, undef, S1, S2, S3, undef);
+pn(ProbeLabel, I1, I2, S1, S2, S3, S4) when is_integer(I1), is_integer(I2) ->
+ user_trace_n_int(ProbeLabel, I1, I2, undef, undef, S1, S2, S3, S4).
+
+-spec pn(n_probe_label(), probe_arg(), probe_arg(), probe_arg(), probe_arg(),
+ probe_arg(), probe_arg(), probe_arg()) ->
+ true | false | error | badarg.
+
+pn(ProbeLabel, I1, I2, I3, I4, S1, S2, S3) when is_integer(I1), is_integer(I2), is_integer(I3), is_integer(I4) ->
+ user_trace_n_int(ProbeLabel, I1, I2, I3, I4, S1, S2, S3, undef);
+pn(ProbeLabel, I1, I2, I3, S1, S2, S3, S4) when is_integer(I1), is_integer(I2), is_integer(I3) ->
+ user_trace_n_int(ProbeLabel, I1, I2, I3, undef, S1, S2, S3, S4).
+
+-spec pn(n_probe_label(), probe_arg(), probe_arg(), probe_arg(), probe_arg(),
+ probe_arg(), probe_arg(), probe_arg(), probe_arg()) ->
+ true | false | error | badarg.
+
+pn(ProbeLabel, I1, I2, I3, I4, S1, S2, S3, S4) when is_integer(I1), is_integer(I2), is_integer(I3), is_integer(I4) ->
+ user_trace_n_int(ProbeLabel, I1, I2, I3, I4, S1, S2, S3, S4).
+
+-spec user_trace_n_int(n_probe_label(),
+ int_p_arg(), int_p_arg(), int_p_arg(), int_p_arg(),
+ int_p_arg(), int_p_arg(), int_p_arg(), int_p_arg()) ->
+ true | false | error | badarg.
+
+user_trace_n_int(ProbeLabel, I1, I2, I3, I4, S1, S2, S3, S4) ->
+ UTag = get_tag(),
+ try
+ user_trace_n(ProbeLabel, UTag, I1, I2, I3, I4, S1, S2, S3, S4)
+ catch
+ error:nif_not_loaded ->
+ false
+ end.
+
-spec put_tag(undefined | iodata()) -> binary() | undefined.
put_tag(Data) ->
erlang:dt_put_tag(unicode:characters_to_binary(Data)).
@@ -240,40 +350,3 @@ spread_tag(B) ->
-spec restore_tag(true | {non_neg_integer(), binary() | []}) -> true.
restore_tag(T) ->
erlang:dt_restore_tag(T).
-
-
-%% Scaffolding to write tedious code: quick brute force and not 100% correct.
-
-scaff_int_args(N) ->
- L = lists:sublist(["I1", "I2", "I3", "I4"], N),
- [string:join(L, ", ")].
-
-scaff_int_guards(N) ->
- L = lists:sublist(["is_integer(I1)", "is_integer(I2)", "is_integer(I3)",
- "is_integer(I4)"], N),
- lists:flatten(string:join(L, ", ")).
-
-scaff_char_args(N) ->
- L = lists:sublist(["S1", "S2", "S3", "S4"], N),
- [string:join(L, ", ")].
-
-scaff_fill(N) ->
- [string:join(lists:duplicate(N, "undef"), ", ")].
-
-scaff() ->
- L = [begin
- IntArgs = scaff_int_args(N_int),
- IntGuards = scaff_int_guards(N_int),
- IntFill = scaff_fill(4 - N_int),
- CharArgs = scaff_char_args(N_char),
- CharFill = scaff_fill(4 - N_char),
- InArgs = string:join(IntArgs ++ CharArgs, ", "),
- OutArgs = string:join(IntArgs ++ IntFill ++ CharArgs ++ CharFill,
- ", "),
- {N_int + N_char,
- lists:flatten([io_lib:format("p(~s) when ~s ->\n",
- [InArgs, IntGuards]),
- io_lib:format(" user_trace_int(~s);\n", [OutArgs])
- ])}
- end || N_int <- [0,1,2,3,4], N_char <- [0,1,2,3,4]],
- [io:format("%%~p\n~s", [N, Str]) || {N, Str} <- lists:sort(L)].
diff --git a/lib/runtime_tools/src/observer_backend.erl b/lib/runtime_tools/src/observer_backend.erl
index 01e99f3f5e..9498412505 100644
--- a/lib/runtime_tools/src/observer_backend.erl
+++ b/lib/runtime_tools/src/observer_backend.erl
@@ -83,7 +83,7 @@ get_table2(Parent, Table, Type) ->
ets -> ets:info(Table, size);
mnesia -> mnesia:table_info(Table, size)
end,
- case Size > 0 of
+ case Size =/= undefined andalso Size > 0 of
false ->
Parent ! {self(), '$end_of_table'},
normal;
diff --git a/lib/runtime_tools/src/runtime_tools.app.src b/lib/runtime_tools/src/runtime_tools.app.src
index 84cd6ee10d..602048dc21 100644
--- a/lib/runtime_tools/src/runtime_tools.app.src
+++ b/lib/runtime_tools/src/runtime_tools.app.src
@@ -17,14 +17,13 @@
%% %CopyrightEnd%
%%
{application, runtime_tools,
- [{description, "RUNTIME_TOOLS version 1"},
+ [{description, "RUNTIME_TOOLS"},
{vsn, "%VSN%"},
- {modules, [dbg,observer_backend,percept_profile,
+ {modules, [appmon_info, dbg,observer_backend,percept_profile,
runtime_tools,runtime_tools_sup,erts_alloc_config,
ttb_autostart,dyntrace]},
- {registered, [runtime_tools_sup,inviso_rt,inviso_rt_meta]},
+ {registered, [runtime_tools_sup]},
{applications, [kernel, stdlib]},
-% {env, [{inviso_autostart_mod,your_own_autostart_module}]},
{env, []},
{mod, {runtime_tools, []}}]}.
diff --git a/lib/runtime_tools/src/runtime_tools_sup.erl b/lib/runtime_tools/src/runtime_tools_sup.erl
index 913719c449..264e172a3c 100644
--- a/lib/runtime_tools/src/runtime_tools_sup.erl
+++ b/lib/runtime_tools/src/runtime_tools_sup.erl
@@ -1,7 +1,8 @@
+%% -*- coding: utf-8 -*-
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,7 +19,7 @@
%%
%% ------------------------------------------------------------------------------
%% File : runtime_tools_sup.erl
-%% Author : Lennart �hman <[email protected]>
+%% Author : Lennart Öhman <[email protected]>
-module(runtime_tools_sup).
-behaviour(supervisor).
@@ -31,15 +32,11 @@
%% =============================================================================
%% The runtime tools top most supervisor starts:
-%% -The inviso runtime component. This is the only way to get the runtime component
-%% started automatically (if for instance autostart is wanted).
-%% Note that it is not impossible that the runtime component terminates it self
-%% should it discover that no autostart is configured.
+%% -The ttb_autostart component. This is used for tracing at startup
+%% using observer/ttb.
init(AutoModArgs) ->
Flags = {one_for_one, 0, 3600},
- Children = [{inviso_rt, {inviso_rt, start_link_auto, [AutoModArgs]},
- temporary, 3000, worker, [inviso_rt]},
- {ttb_autostart, {ttb_autostart, start_link, []},
+ Children = [{ttb_autostart, {ttb_autostart, start_link, []},
temporary, 3000, worker, [ttb_autostart]}],
{ok, {Flags, Children}}.
%% -----------------------------------------------------------------------------
diff --git a/lib/runtime_tools/test/dbg_SUITE.erl b/lib/runtime_tools/test/dbg_SUITE.erl
index bd908c1f3a..dfae52ed1d 100644
--- a/lib/runtime_tools/test/dbg_SUITE.erl
+++ b/lib/runtime_tools/test/dbg_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -394,41 +394,36 @@ 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.
+ stop(),
+ {A,B,C} = erlang:now(),
+ FTMP = atom_to_list(?MODULE) ++ integer_to_list(A) ++
+ "-" ++ integer_to_list(B) ++ "-" ++ integer_to_list(C),
+ FName = filename:join([?config(data_dir, Config), FTMP]),
+ %% Ok, lets try with flush and follow_file, not a chance on VxWorks
+ %% with NFS caching...
+ Port2 = dbg:trace_port(file, FName),
+ {ok, _} = dbg:tracer(port, Port2),
+ try
+ {ok, [{matched, _node, 1}]} = dbg:p(self(),call),
+ {ok, _} = dbg:tp(dbg, ltp,[{'_',[],[{message, {self}}]}]),
+ {ok, _} = dbg:tp(dbg, ln, [{'_',[],[{message, hej}]}]),
+ ok = dbg:ltp(),
+ ok = dbg:flush_trace_port(),
+ dbg:trace_client(follow_file, FName,
+ {fun myhandler/2, self()}),
+ S = self(),
+ [{trace,S,call,{dbg,ltp,[]},S}] = flush(),
+ ok = dbg:ln(),
+ ok = dbg:flush_trace_port(),
+ receive after 1000 -> ok end, %% Polls every second...
+ [{trace,S,call,{dbg,ln,[]},hej}] = flush(),
+ stop(),
+ [] = flush()
+ after
+ stop(),
+ file:delete(FName)
+ end,
+ ok.
file_port_schedfix(suite) ->
[];
@@ -519,7 +514,7 @@ file_port_schedfix1(Config) when is_list(Config) ->
%% Cleanup
%%
?line ToBeDeleted = filelib:wildcard(FName++"*"++".wraplog"),
- ?line lists:map({file, delete}, ToBeDeleted),
+ ?line lists:map(fun file:delete/1, ToBeDeleted),
% io:format("ToBeDeleted=~p", [ToBeDeleted]),
%%
%% Present the result
diff --git a/lib/runtime_tools/test/runtime_tools_SUITE.erl b/lib/runtime_tools/test/runtime_tools_SUITE.erl
index b26f3dd881..62497ab527 100644
--- a/lib/runtime_tools/test/runtime_tools_SUITE.erl
+++ b/lib/runtime_tools/test/runtime_tools_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -25,7 +25,7 @@
-export([init_per_testcase/2, end_per_testcase/2]).
%% Test cases
--export([app_file/1]).
+-export([app_file/1, start_stop_app/1]).
%% Default timetrap timeout (set in init_per_testcase)
-define(default_timeout, ?t:minutes(1)).
@@ -42,7 +42,8 @@ end_per_testcase(_Case, Config) ->
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [app_file].
+ [app_file,
+ start_stop_app].
groups() ->
[].
@@ -60,10 +61,14 @@ end_per_group(_GroupName, Config) ->
Config.
-app_file(suite) ->
- [];
-app_file(doc) ->
- ["Testing .app file"];
-app_file(Config) when is_list(Config) ->
+app_file(_Config) ->
?line ok = ?t:app_test(runtime_tools),
ok.
+
+start_stop_app(_Config) ->
+ ok = application:start(runtime_tools),
+ Sup = whereis(runtime_tools_sup),
+ true = is_pid(Sup),
+ Ref = erlang:monitor(process,Sup),
+ ok = application:stop(runtime_tools),
+ receive {'DOWN', Ref, process, Sup, shutdown} -> ok end.
diff --git a/lib/runtime_tools/vsn.mk b/lib/runtime_tools/vsn.mk
index c9b5cc1a41..534c7508d8 100644
--- a/lib/runtime_tools/vsn.mk
+++ b/lib/runtime_tools/vsn.mk
@@ -1 +1 @@
-RUNTIME_TOOLS_VSN = 1.8.8
+RUNTIME_TOOLS_VSN = 1.8.9
diff --git a/lib/sasl/doc/src/notes.xml b/lib/sasl/doc/src/notes.xml
index e4a2c98db7..1333e375b1 100644
--- a/lib/sasl/doc/src/notes.xml
+++ b/lib/sasl/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2004</year><year>2011</year>
+ <year>2004</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
diff --git a/lib/sasl/examples/src/Makefile b/lib/sasl/examples/src/Makefile
index 47c8626205..c0d73aab1d 100644
--- a/lib/sasl/examples/src/Makefile
+++ b/lib/sasl/examples/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2010-2011. All Rights Reserved.
+# Copyright Ericsson AB 2010-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/sasl/src/Makefile b/lib/sasl/src/Makefile
index cae8146ebc..c1ad8ca0bb 100644
--- a/lib/sasl/src/Makefile
+++ b/lib/sasl/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2009. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -80,10 +80,10 @@ docs:
# ----------------------------------------------------
$(APP_TARGET): $(APP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
# ----------------------------------------------------
# Release Target
diff --git a/lib/sasl/src/overload.erl b/lib/sasl/src/overload.erl
index 5a4782efff..97f7bebe00 100644
--- a/lib/sasl/src/overload.erl
+++ b/lib/sasl/src/overload.erl
@@ -1,7 +1,8 @@
+%% -*- coding: utf-8 -*-
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -25,7 +26,7 @@
format_status/2]).
%%%-----------------------------------------------------------------
-%%% This is a rewrite of overload from BS.3, by Peter H�gfeldt.
+%%% This is a rewrite of overload from BS.3, by Peter Högfeldt.
%%%
%%% DESCRIPTION
%%%
diff --git a/lib/sasl/src/release_handler.erl b/lib/sasl/src/release_handler.erl
index 5efd932c92..1ff3eb96eb 100644
--- a/lib/sasl/src/release_handler.erl
+++ b/lib/sasl/src/release_handler.erl
@@ -494,10 +494,10 @@ find_script(App, Dir, OldVsn, UpOrDown) ->
up -> UpFromScripts;
down -> DownToScripts
end,
- case lists:keysearch(OldVsn, 1, Scripts) of
- {value, {_OldVsn, Script}} ->
- {NewVsn, Script};
- false ->
+ case systools_relup:appup_search_for_version(OldVsn,Scripts) of
+ {ok,Script} ->
+ {NewVsn,Script};
+ error ->
throw({version_not_in_appup, OldVsn})
end;
{error, enoent} ->
diff --git a/lib/sasl/src/systools_make.erl b/lib/sasl/src/systools_make.erl
index 61e660e918..9b2e2c809b 100644
--- a/lib/sasl/src/systools_make.erl
+++ b/lib/sasl/src/systools_make.erl
@@ -673,7 +673,7 @@ check_item({_,{registered,Regs}},I) ->
_ -> throw({bad_param, I})
end;
check_item({_,{modules,Mods}},I) ->
- case mod_list_p(Mods) of
+ case a_list_p(Mods) of
true -> Mods;
_ -> throw({bad_param, I})
end;
@@ -900,11 +900,10 @@ find_pos(N, Name, [_OtherAppl|OrderedAppls]) ->
check_modules(Appls, Path, TestP, Machine) ->
%% first check that all the module names are unique
- %% Make a list M1 = [{Mod,Vsn,App,AppVsn,Dir}]
- %% where Vsn = '$$ignore$$' | Specified
- M1 = [{Mod,Vsn,App,Appv,A#application.dir} ||
- {{App,Appv},A} <- Appls,
- {Mod,Vsn} <- get_mod_vsn(A#application.modules)],
+ %% Make a list M1 = [{Mod,App,Dir}]
+ M1 = [{Mod,App,A#application.dir} ||
+ {{App,_Appv},A} <- Appls,
+ Mod <- A#application.modules],
case duplicates(M1) of
[] ->
case check_mods(M1, Appls, Path, TestP, Machine) of
@@ -918,16 +917,8 @@ check_modules(Appls, Path, TestP, Machine) ->
throw({error, {duplicate_modules, Dups}})
end.
-get_mod_vsn([{Mod,Vsn}|Mods]) ->
- [{Mod,Vsn}|get_mod_vsn(Mods)];
-get_mod_vsn([Mod|Mods]) ->
- [{Mod,'$$ignore$$'}|get_mod_vsn(Mods)];
-get_mod_vsn([]) ->
- [].
-
%%______________________________________________________________________
-%% Check that all modules exists and that the specified version
-%% corresponds to the version in the module's source code.
+%% Check that all modules exists.
%% Use the module extension of the running machine as extension for
%% the checked modules.
@@ -952,7 +943,7 @@ 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,
+ {Mod,App,Dir} = ModT,
case check_mod(Mod,App,Dir,Ext,IncPath) of
ok ->
[];
@@ -1076,11 +1067,10 @@ check_mod(Mod,App,Dir,Ext,IncPath) ->
end.
mod_to_filename(Dir, Mod, Ext) ->
- Parts = packages:split(Mod),
- filename:join([Dir | Parts]) ++ Ext.
+ filename:join(Dir, atom_to_list(Mod) ++ Ext).
check_module(Mod, Dir, ObjModTime, IncPath) ->
- {SrcDirs,_IncDirs}= smart_guess(Mod, Dir,IncPath),
+ {SrcDirs,_IncDirs}= smart_guess(Dir,IncPath),
case locate_src(Mod,SrcDirs) of
{ok,_FDir,_File,LastModTime} ->
if
@@ -1094,7 +1084,7 @@ check_module(Mod, Dir, ObjModTime, IncPath) ->
end.
locate_src(Mod,[Dir|Dirs]) ->
- File = filename:join(Dir, mod_to_fname(Mod) ++ ".erl"),
+ File = mod_to_filename(Dir, Mod, ".erl"),
case file:read_file_info(File) of
{ok,FileInfo} ->
LastModTime = FileInfo#file_info.mtime,
@@ -1105,9 +1095,6 @@ locate_src(Mod,[Dir|Dirs]) ->
locate_src(_,[]) ->
false.
-mod_to_fname(Mod) ->
- hd(lists:reverse(packages:split(Mod))).
-
%%______________________________________________________________________
%% smart_guess(Mod, Dir,IncludePath) -> {[Dirs],[IncDirs]}
@@ -1115,17 +1102,12 @@ mod_to_fname(Mod) ->
%% src-dir should be one of .../src or .../src/e_src
%% If dir does not contain .../ebin set dir to the same directory.
-smart_guess(Mod, Dir,IncPath) ->
+smart_guess(Dir,IncPath) ->
case reverse(filename:split(Dir)) of
["ebin"|D] ->
- Subdirs = case packages:split(Mod) of
- [_] -> [];
- [_|_] = Parts ->
- lists:reverse(tl(lists:reverse(Parts)))
- end,
D1 = reverse(D),
- Dirs = [filename:join(D1 ++ ["src" | Subdirs]),
- filename:join(D1 ++ ["src", "e_src" | Subdirs])],
+ Dirs = [filename:join(D1 ++ ["src"]),
+ filename:join(D1 ++ ["src", "e_src"])],
{Dirs,Dirs ++ IncPath};
_ ->
{[Dir],[Dir] ++ IncPath}
@@ -1421,10 +1403,7 @@ create_mandatory_path(Appls, PathFlag, Variables) ->
%% Load all modules, except those in Mandatory_modules.
load_appl_mods([{{Name,Vsn},A}|Appls], Mand, PathFlag, Variables) ->
- Mods = map(fun({Mod,_}) -> Mod;
- (Mod) -> Mod
- end,
- A#application.modules),
+ Mods = A#application.modules,
load_commands(filter(fun(Mod) -> not member(Mod, Mand) end, Mods),
cr_path(Name, Vsn, A, PathFlag, Variables)) ++
load_appl_mods(Appls, Mand, PathFlag, Variables);
@@ -1435,23 +1414,8 @@ load_appl_mods([], _, _, _) ->
[{progress, modules_loaded}].
load_commands(Mods, Path) ->
- SplitMods = lists:foldl(
- fun({Parts,M}, [{Last, Acc}|Rest]) ->
- [_|Tail] = lists:reverse(Parts),
- case lists:reverse(Tail) of
- Subs when Subs == Last ->
- [{Last,[M|Acc]}|Rest];
- Subs ->
- [{Subs, [M]}|[{Last,Acc}|Rest]]
- end
- end, [{[],[]}],
- lists:sort([{packages:split(M),M} || M <- Mods])),
- lists:foldl(
- fun({Subs,Ms}, Cmds) ->
- [{path, [filename:join([Path | Subs])]},
- {primLoad,lists:sort(Ms)} | Cmds]
- end, [], SplitMods).
-
+ [{path, [filename:join([Path])]},
+ {primLoad,lists:sort(Mods)}].
%%______________________________________________________________________
%% Pack an application to an application term.
@@ -1497,7 +1461,8 @@ mandatory_modules() ->
preloaded() ->
%% Sorted
- [erl_prim_loader,erlang,init,otp_ring0,prim_file,prim_inet, prim_zip,zlib].
+ [erl_prim_loader,erlang,erts_internal,init,otp_ring0,prim_file,prim_inet,
+ prim_zip,zlib].
%%______________________________________________________________________
%% Kernel processes; processes that are specially treated by the init
@@ -1784,9 +1749,7 @@ add_appl(Name, Vsn, App, Tar, Variables, Flags, Var) ->
add_to_tar(Tar,
filename:join(AppDir, Name ++ ".app"),
filename:join(BinDir, Name ++ ".app")),
- add_modules(map(fun({Mod,_}) -> to_list(Mod);
- (Mod) -> to_list(Mod)
- end,
+ add_modules(map(fun(Mod) -> to_list(Mod) end,
App#application.modules),
Tar,
AppDir,
@@ -2026,14 +1989,6 @@ t_list_p([{A,_}|T]) when is_atom(A) -> t_list_p(T);
t_list_p([]) -> true;
t_list_p(_) -> false.
-% check if a term is a list of atoms or two-tuples with the first
-% element as an atom.
-
-mod_list_p([{A,_}|T]) when is_atom(A) -> mod_list_p(T);
-mod_list_p([A|T]) when is_atom(A) -> mod_list_p(T);
-mod_list_p([]) -> true;
-mod_list_p(_) -> false.
-
% check if a term is a list of atoms.
a_list_p([A|T]) when is_atom(A) -> a_list_p(T);
diff --git a/lib/sasl/src/systools_rc.erl b/lib/sasl/src/systools_rc.erl
index c16f6aa845..cf5cca7cb3 100644
--- a/lib/sasl/src/systools_rc.erl
+++ b/lib/sasl/src/systools_rc.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -326,8 +326,7 @@ translate_application_instrs(Script, Appls, PreAppls) ->
fun({add_application, Appl, Type}) ->
case lists:keysearch(Appl, #application.name, Appls) of
{value, Application} ->
- Mods =
- remove_vsn(Application#application.modules),
+ Mods = Application#application.modules,
ApplyL = case Type of
none -> [];
load -> [{apply, {application, load, [Appl]}}];
@@ -349,9 +348,11 @@ translate_application_instrs(Script, Appls, PreAppls) ->
end,
case lists:keysearch(Appl, #application.name, PreAppls) of
{value, RemApplication} ->
- Mods = remove_vsn(RemApplication#application.modules),
+ Mods = RemApplication#application.modules,
+
[{apply, {application, stop, [Appl]}}] ++
- [{remove, {M, brutal_purge, brutal_purge}} || M <- Mods] ++
+ [{remove, {M, brutal_purge, brutal_purge}}
+ || M <- Mods] ++
[{purge, Mods},
{apply, {application, unload, [Appl]}}];
false ->
@@ -360,16 +361,14 @@ translate_application_instrs(Script, Appls, PreAppls) ->
({restart_application, Appl}) ->
case lists:keysearch(Appl, #application.name, PreAppls) of
{value, PreApplication} ->
- PreMods =
- remove_vsn(PreApplication#application.modules),
-
+ PreMods = PreApplication#application.modules,
case lists:keysearch(Appl, #application.name, Appls) of
{value, PostApplication} ->
- PostMods =
- remove_vsn(PostApplication#application.modules),
-
+ PostMods = PostApplication#application.modules,
+
[{apply, {application, stop, [Appl]}}] ++
- [{remove, {M, brutal_purge, brutal_purge}} || M <- PreMods] ++
+ [{remove, {M, brutal_purge, brutal_purge}}
+ || M <- PreMods] ++
[{purge, PreMods}] ++
[{add_module, M, []} || M <- PostMods] ++
[{apply, {application, start,
@@ -385,11 +384,6 @@ translate_application_instrs(Script, Appls, PreAppls) ->
end, Script),
lists:flatten(L).
-remove_vsn(Mods) ->
- lists:map(fun({Mod, _Vsn}) -> Mod;
- (Mod) -> Mod
- end, Mods).
-
%%-----------------------------------------------------------------
%% Translates add_module into load_module (high-level transformation)
%%-----------------------------------------------------------------
@@ -654,15 +648,9 @@ translate_dep_to_low(Mode, Instructions, Appls) ->
end.
get_lib(Mod, [#application{name = Name, vsn = Vsn, modules = Modules} | T]) ->
- %% Module = {Mod, Vsn} | Mod
- case lists:keysearch(Mod, 1, Modules) of
- {value, _} ->
- {Name, Vsn};
- false ->
- case lists:member(Mod, Modules) of
- true -> {Name, Vsn};
- false -> get_lib(Mod, T)
- end
+ case lists:member(Mod, Modules) of
+ true -> {Name, Vsn};
+ false -> get_lib(Mod, T)
end;
get_lib(Mod, []) ->
throw({error, {no_such_module, Mod}}).
diff --git a/lib/sasl/src/systools_relup.erl b/lib/sasl/src/systools_relup.erl
index 7fb623bb85..7048184426 100644
--- a/lib/sasl/src/systools_relup.erl
+++ b/lib/sasl/src/systools_relup.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -114,7 +114,8 @@
-define(R15_SASL_VSN,"2.2").
-%% For test purposes only - used by kernel, stdlib and sasl tests
+%% Used by release_handler:find_script/4.
+%% Also used by kernel, stdlib and sasl tests
-export([appup_search_for_version/2]).
%%-----------------------------------------------------------------
diff --git a/lib/sasl/test/Makefile b/lib/sasl/test/Makefile
index bb3f82f411..837abf74d4 100644
--- a/lib/sasl/test/Makefile
+++ b/lib/sasl/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2011. All Rights Reserved.
+# Copyright Ericsson AB 2011-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/sasl/test/installer.erl b/lib/sasl/test/installer.erl
index 6942ec21ea..709269a73c 100644
--- a/lib/sasl/test/installer.erl
+++ b/lib/sasl/test/installer.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011. All Rights Reserved.
+%% Copyright Ericsson AB 2011-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -876,7 +876,9 @@ trace_disallowed_calls(Node) ->
MasterProc = self(),
rpc:call(Node,dbg,tracer,[process,{fun(T,_) -> MasterProc ! T end,[]}]),
rpc:call(Node,dbg,p,[all,call]),
- rpc:call(Node,dbg,tp,[file,[{'_',[],[{message,{caller}}]}]]).
+ rpc:call(Node,dbg,tp,[file,[{'_',[],[{message,{caller}}]}]]),
+ %% File:native_name_encoding/0 is a BIF and OK to use
+ rpc:call(Node,dbg,ctp,[file,native_name_encoding,0]).
check_disallowed_calls(TestNode,Line) ->
receive
diff --git a/lib/sasl/test/rb_SUITE.erl b/lib/sasl/test/rb_SUITE.erl
index 35a4eb7e7b..b0e43be3a2 100644
--- a/lib/sasl/test/rb_SUITE.erl
+++ b/lib/sasl/test/rb_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011. All Rights Reserved.
+%% Copyright Ericsson AB 2011-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -66,23 +66,31 @@ init_per_group(running_error_logger,Config) ->
restart_sasl(),
Config.
-end_per_group(running_error_logger,Config) ->
+end_per_group(running_error_logger,_Config) ->
%% Remove log_mf_h???
ok.
init_per_testcase(_Case,Config) ->
case whereis(?SUP) of
- undefined -> ok;
- Pid -> kill(Pid)
+ undefined ->
+ ok;
+ Sup ->
+ Server = whereis(?MODULE),
+ exit(Sup,kill),
+ wait_for_down([Server,Sup])
end,
empty_error_logs(Config),
Config.
-kill(Pid) ->
+wait_for_down([]) ->
+ ok;
+wait_for_down([undefined|Rest]) ->
+ wait_for_down(Rest);
+wait_for_down([Pid|Rest]) ->
Ref = erlang:monitor(process,Pid),
- exit(Pid,kill),
- receive {'DOWN', Ref, process, Pid, _Info} -> ok end.
+ receive {'DOWN', Ref, process, Pid, _Info} -> ok end,
+ wait_for_down(Rest).
end_per_testcase(Case,Config) ->
try apply(?MODULE,Case,[cleanup,Config])
@@ -96,8 +104,9 @@ end_per_testcase(Case,Config) ->
help(_Config) ->
Help = capture(fun() -> rb:h() end),
- "Report Browser Tool - usage" = hd(Help),
- "rb:stop - stop the rb_server" = lists:last(Help),
+ %% Check that first and last line is there
+ true = lists:member("Report Browser Tool - usage", Help),
+ true = lists:member("rb:stop - stop the rb_server", Help),
ok.
%% Test that all three sasl env vars must be set for a successful start of rb
diff --git a/lib/sasl/test/release_handler_SUITE.erl b/lib/sasl/test/release_handler_SUITE.erl
index a8e3cc3d88..94cffc988d 100644
--- a/lib/sasl/test/release_handler_SUITE.erl
+++ b/lib/sasl/test/release_handler_SUITE.erl
@@ -63,7 +63,8 @@ cases() ->
instructions, eval_appup, eval_appup_with_restart,
supervisor_which_children_timeout,
release_handler_which_releases, install_release_syntax_check,
- upgrade_supervisor, upgrade_supervisor_fail, otp_9864].
+ upgrade_supervisor, upgrade_supervisor_fail, otp_9864,
+ otp_10463_upgrade_script_regexp].
groups() ->
[{release,[],
@@ -1206,12 +1207,25 @@ otp_9395_rm_many_mods(cleanup,_Conf) ->
stop_node(node_name(otp_9395_rm_many_mods)).
otp_9864(Conf) ->
+ case os:type() of
+ {win32,_} ->
+ {skip,"Testing handling of symlinks - skipped on windows"};
+ _ ->
+ do_otp_9864(Conf)
+ end.
+do_otp_9864(Conf) ->
%% Set some paths
PrivDir = priv_dir(Conf),
Dir = filename:join(PrivDir,"otp_9864"),
RelDir = filename:join(?config(data_dir, Conf), "app1_app2"),
- LibDir1 = filename:join(RelDir, "lib1"),
- LibDir2 = filename:join(RelDir, "lib2"),
+
+ %% Copy libs to priv_dir because remove_release will remove some
+ %% of these again, and we don't want to remove anything from
+ %% data_dir
+ copy_tree(Conf,filename:join(RelDir, "lib1"),Dir),
+ copy_tree(Conf,filename:join(RelDir, "lib2"),Dir),
+ LibDir1 = filename:join(Dir, "lib1"),
+ LibDir2 = filename:join(Dir, "lib2"),
%% Create the releases
Rel1 = create_and_install_fake_first_release(Dir,
@@ -1229,10 +1243,6 @@ otp_9864(Conf) ->
{ok, Node} = t_start_node(otp_9864, Rel1, filename:join(Rel1Dir,"sys.config")),
%% Unpack rel2 (make sure it does not work if an AppDir is bad)
- LibDir3 = filename:join(RelDir, "lib3"),
- {error, {no_such_directory, _}} =
- rpc:call(Node, release_handler, set_unpacked,
- [Rel2++".rel", [{app1,"2.0",LibDir2}, {app2,"1.0",LibDir3}]]),
{ok, RelVsn2} =
rpc:call(Node, release_handler, set_unpacked,
[Rel2++".rel", [{app1,"2.0",LibDir2}, {app2,"1.0",LibDir2}]]),
@@ -1243,22 +1253,27 @@ otp_9864(Conf) ->
ok = rpc:call(Node, release_handler, install_file,
[RelVsn2, filename:join(Rel2Dir, "sys.config")]),
- %% Install RelVsn2 without {update_paths, true} option
+ %% Install RelVsn2
{ok, RelVsn1, []} =
rpc:call(Node, release_handler, install_release, [RelVsn2]),
- %% Install RelVsn1 again
+ %% Create a symlink inside release 2
+ Releases2Dir = filename:join([Dir,"releases","2"]),
+ Link = filename:join(Releases2Dir,"foo_symlink_dir"),
+ file:make_symlink(Releases2Dir,Link),
+
+ %% Back down to RelVsn1
{ok, RelVsn1, []} =
rpc:call(Node, release_handler, install_release, [RelVsn1]),
- TempRel2Dir = filename:join(Dir,"releases/2"),
- file:make_symlink(TempRel2Dir, filename:join(TempRel2Dir, "foo_symlink_dir")),
-
%% This will fail if symlinks are not handled
ok = rpc:call(Node, release_handler, remove_release, [RelVsn2]),
ok.
+otp_9864(cleanup,_Conf) ->
+ stop_node(node_name(otp_9864)).
+
upgrade_supervisor(Conf) when is_list(Conf) ->
%% Set some paths
@@ -1646,6 +1661,15 @@ upgrade_gg(cleanup,Config) ->
ok = stop_nodes(NodeNames).
+%%%-----------------------------------------------------------------
+%%% OTP-10463, Bug - release_handler could not handle regexp in appup
+%%% files.
+otp_10463_upgrade_script_regexp(_Config) ->
+ %% Assuming that kernel always has a regexp in it's appup
+ KernelVsn = vsn(kernel,current),
+ {ok,KernelVsn,_} =
+ release_handler:upgrade_script(kernel,code:lib_dir(kernel)),
+ ok.
%%%=================================================================
diff --git a/lib/sasl/test/release_handler_SUITE_data/Makefile.src b/lib/sasl/test/release_handler_SUITE_data/Makefile.src
index 55d20aa8b6..b794aa0e6f 100644
--- a/lib/sasl/test/release_handler_SUITE_data/Makefile.src
+++ b/lib/sasl/test/release_handler_SUITE_data/Makefile.src
@@ -1,9 +1,5 @@
EFLAGS=+debug_info
-P2B= \
- P2B/a-2.0/ebin/a.@EMULATOR@ \
- P2B/a-2.0/ebin/a_sup.@EMULATOR@
-
LIB= \
lib/a-9.1/ebin/a.@EMULATOR@ \
lib/a-9.1/ebin/a_sup.@EMULATOR@ \
@@ -80,13 +76,7 @@ SUP= \
release_handler_timeouts/dummy-0.1/ebin/dummy_sup.@EMULATOR@ \
release_handler_timeouts/dummy-0.1/ebin/dummy_sup_2.@EMULATOR@
-all: $(P2B) $(LIB) $(APP) $(OTP2740) $(C) $(SUP)
-
-P2B/a-2.0/ebin/a.@EMULATOR@: P2B/a-2.0/src/a.erl
- erlc $(EFLAGS) -oP2B/a-2.0/ebin P2B/a-2.0/src/a.erl
-P2B/a-2.0/ebin/a_sup.@EMULATOR@: P2B/a-2.0/src/a_sup.erl
- erlc $(EFLAGS) -oP2B/a-2.0/ebin P2B/a-2.0/src/a_sup.erl
-
+all: $(LIB) $(APP) $(OTP2740) $(C) $(SUP)
lib/a-1.0/ebin/a.@EMULATOR@: lib/a-1.0/src/a.erl
erlc $(EFLAGS) -olib/a-1.0/ebin lib/a-1.0/src/a.erl
diff --git a/lib/sasl/test/release_handler_SUITE_data/c/c.app b/lib/sasl/test/release_handler_SUITE_data/c/c.app
index 908a94cf2d..2f56196918 100644
--- a/lib/sasl/test/release_handler_SUITE_data/c/c.app
+++ b/lib/sasl/test/release_handler_SUITE_data/c/c.app
@@ -1,7 +1,7 @@
{application, c,
[{description, "C CXC 138 11"},
{vsn, "1.0"},
- {modules, [b, {aa, 1}, {c_sup,1}]},
+ {modules, [b, aa, c_sup]},
{registered, [cc,bb,c_sup]},
{applications, [kernel, stdlib]},
{env, [{key1, val1}]},
diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/ebin/a.app b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/ebin/a.app
index e938137f67..7ae189a36a 100644
--- a/lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/ebin/a.app
+++ b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/ebin/a.app
@@ -1,7 +1,7 @@
{application, a,
[{description, "A CXC 138 11"},
{vsn, "1.0"},
- {modules, [{a, 1}, {a_sup,1}]},
+ {modules, [a, a_sup]},
{registered, [a_sup]},
{applications, [kernel, stdlib]},
{env, [{key1, val1}]},
diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/ebin/a.app b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/ebin/a.app
index 1c3053b2fa..f8ab31fab9 100644
--- a/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/ebin/a.app
+++ b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/ebin/a.app
@@ -1,7 +1,7 @@
{application, a,
[{description, "A CXC 138 11"},
{vsn, "1.1"},
- {modules, [{a, 2}, {a_sup,1}]},
+ {modules, [a, a_sup]},
{registered, [a_sup]},
{applications, [kernel, stdlib]},
{env, [{key1, val1}]},
diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/a-1.2/ebin/a.app b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.2/ebin/a.app
index b38722f06d..2393af0493 100644
--- a/lib/sasl/test/release_handler_SUITE_data/lib/a-1.2/ebin/a.app
+++ b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.2/ebin/a.app
@@ -1,7 +1,7 @@
{application, a,
[{description, "A CXC 138 11"},
{vsn, "1.2"},
- {modules, [{a, 2}, {a_sup,1}]},
+ {modules, [a, a_sup]},
{registered, [a_sup]},
{applications, [kernel, stdlib]},
{env, [{key1, val1}]},
diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/b-1.0/ebin/b.app b/lib/sasl/test/release_handler_SUITE_data/lib/b-1.0/ebin/b.app
index 00347b2754..e2eada00a4 100644
--- a/lib/sasl/test/release_handler_SUITE_data/lib/b-1.0/ebin/b.app
+++ b/lib/sasl/test/release_handler_SUITE_data/lib/b-1.0/ebin/b.app
@@ -2,6 +2,6 @@
{application, b,
[{description, "B CXC 138 12"},
{vsn, "1.0"},
- {modules, [{b_server, 1},{b_lib, 1}]},
+ {modules, [b_server,b_lib]},
{registered, [b_server]},
{applications, [kernel, stdlib]}]}.
diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/b-2.0/ebin/b.app b/lib/sasl/test/release_handler_SUITE_data/lib/b-2.0/ebin/b.app
index 73c8e42b32..e4f8369ae5 100644
--- a/lib/sasl/test/release_handler_SUITE_data/lib/b-2.0/ebin/b.app
+++ b/lib/sasl/test/release_handler_SUITE_data/lib/b-2.0/ebin/b.app
@@ -2,6 +2,6 @@
{application, b,
[{description, "B CXC 138 12"},
{vsn, "2.0"},
- {modules, [{b_server, 1}]},
+ {modules, [b_server]},
{registered, [b_server]},
{applications, [kernel, stdlib]}]}.
diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/many_mods-1.0/ebin/many_mods.app b/lib/sasl/test/release_handler_SUITE_data/lib/many_mods-1.0/ebin/many_mods.app
index aa39adfffa..5c56c4cd74 100644
--- a/lib/sasl/test/release_handler_SUITE_data/lib/many_mods-1.0/ebin/many_mods.app
+++ b/lib/sasl/test/release_handler_SUITE_data/lib/many_mods-1.0/ebin/many_mods.app
@@ -2,16 +2,6 @@
{application, many_mods,
[{description, "Application with many modules CXC 138 11"},
{vsn, "1.0"},
- {modules, [{m, 1},
- {m1,1},
- {m2,1},
- {m3,1},
- {m4,1},
- {m5,1},
- {m6,1},
- {m7,1},
- {m8,1},
- {m9,1},
- {m10,1}]},
+ {modules, [m,m1,m2,m3,m4,m5,m6,m7,m8,m9,m10]},
{registered, []},
{applications, [kernel, stdlib]}]}.
diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/many_mods-1.1/ebin/many_mods.app b/lib/sasl/test/release_handler_SUITE_data/lib/many_mods-1.1/ebin/many_mods.app
index 36c50caf2f..87fdfe8442 100644
--- a/lib/sasl/test/release_handler_SUITE_data/lib/many_mods-1.1/ebin/many_mods.app
+++ b/lib/sasl/test/release_handler_SUITE_data/lib/many_mods-1.1/ebin/many_mods.app
@@ -2,16 +2,6 @@
{application, many_mods,
[{description, "Application with many modules CXC 138 11"},
{vsn, "1.1"},
- {modules, [{m, 1},
- {m1,1},
- {m2,1},
- {m3,1},
- {m4,1},
- {m5,1},
- {m6,1},
- {m7,1},
- {m8,1},
- {m9,1},
- {m10,1}]},
+ {modules, [m,m1,m2,m3,m4,m5,m6,m7,m8,m9,m10]},
{registered, []},
{applications, [kernel, stdlib]}]}.
diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/many_mods-2.0/ebin/many_mods.app b/lib/sasl/test/release_handler_SUITE_data/lib/many_mods-2.0/ebin/many_mods.app
index 98f6527750..aba906d667 100644
--- a/lib/sasl/test/release_handler_SUITE_data/lib/many_mods-2.0/ebin/many_mods.app
+++ b/lib/sasl/test/release_handler_SUITE_data/lib/many_mods-2.0/ebin/many_mods.app
@@ -2,6 +2,6 @@
{application, many_mods,
[{description, "Application with many modules CXC 138 11"},
{vsn, "2.0"},
- {modules, [{m, 1}]},
+ {modules, [m]},
{registered, []},
{applications, [kernel, stdlib]}]}.
diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/ebin/db.app b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/ebin/db.app
index d375768b99..0878b80cb5 100644
--- a/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/ebin/db.app
+++ b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/ebin/db.app
@@ -1,7 +1,7 @@
{application, db,
[{description, "ERICSSON NR FOR DB"},
{vsn, "2.0"},
- {modules, [{db1, "1.0"}, {db2, "1.0"}]},
+ {modules, [db1, db2]},
{registered, []},
{applications, []},
{env, []},
diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/ebin/fe.app
index d3bd85cda6..33d44805bd 100644
--- a/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/ebin/fe.app
+++ b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/ebin/fe.app
@@ -1,7 +1,7 @@
{application, fe,
[{description, "ERICSSON NR FOR FE"},
{vsn, "3.1"},
- {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]},
+ {modules, [fe1, fe2, fe3]},
{registered, []},
{applications, []},
{env, []},
diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_appup/lib/fe-2.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_bad_appup/lib/fe-2.1/ebin/fe.app
index 3cb0b0c2cf..d7fae9e092 100644
--- a/lib/sasl/test/systools_SUITE_data/d_bad_appup/lib/fe-2.1/ebin/fe.app
+++ b/lib/sasl/test/systools_SUITE_data/d_bad_appup/lib/fe-2.1/ebin/fe.app
@@ -1,7 +1,7 @@
{application, fe,
[{description, "ERICSSON NR FOR FE"},
{vsn, "2.1"},
- {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]},
+ {modules, [fe1, fe2, fe3]},
{registered, []},
{applications, []},
{mod, {fe1, []}}]}.
diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_appup/lib/fe-3.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_bad_appup/lib/fe-3.1/ebin/fe.app
index 0696e2494c..682d40eb12 100644
--- a/lib/sasl/test/systools_SUITE_data/d_bad_appup/lib/fe-3.1/ebin/fe.app
+++ b/lib/sasl/test/systools_SUITE_data/d_bad_appup/lib/fe-3.1/ebin/fe.app
@@ -1,7 +1,7 @@
{application, fe,
[{description, "ERICSSON NR FOR FE"},
{vsn, "3.1"},
- {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]},
+ {modules, [fe1, fe2, fe3]},
{registered, []},
{applications, []},
{mod, {fe1, []}}]}.
diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/ebin/db.app b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/ebin/db.app
index 191919f8d3..a1025c306a 100644
--- a/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/ebin/db.app
+++ b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/ebin/db.app
@@ -1,7 +1,7 @@
{application, db,
[{description, "ERICSSON NR FOR DB"},
{vsn, "2.1"},
- {modules, [{db1, "1.0"}, {db2, "1.0"}]},
+ {modules, [db1, db2]},
{registered, []},
{applications, []},
{env, []},
diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/ebin/fe.app
index d3bd85cda6..33d44805bd 100644
--- a/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/ebin/fe.app
+++ b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/ebin/fe.app
@@ -1,7 +1,7 @@
{application, fe,
[{description, "ERICSSON NR FOR FE"},
{vsn, "3.1"},
- {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]},
+ {modules, [fe1, fe2, fe3]},
{registered, []},
{applications, []},
{env, []},
diff --git a/lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/ebin/db.app b/lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/ebin/db.app
index 3e0ac3c3c9..28d0f80d34 100644
--- a/lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/ebin/db.app
+++ b/lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/ebin/db.app
@@ -1,7 +1,7 @@
{application, db,
[{description, "ERICSSON NR FOR DB"},
{vsn, "2.1"},
- {modules, [{db1, "1.0"}, {db2, "1.0"}, {db3, "2.0"}]},
+ {modules, [db1, db2, db3]},
{registered, []},
{applications, []},
{env, []},
diff --git a/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/ebin/db.app b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/ebin/db.app
index 191919f8d3..a1025c306a 100644
--- a/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/ebin/db.app
+++ b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/ebin/db.app
@@ -1,7 +1,7 @@
{application, db,
[{description, "ERICSSON NR FOR DB"},
{vsn, "2.1"},
- {modules, [{db1, "1.0"}, {db2, "1.0"}]},
+ {modules, [db1, db2]},
{registered, []},
{applications, []},
{env, []},
diff --git a/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/ebin/fe.app
index d3bd85cda6..33d44805bd 100644
--- a/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/ebin/fe.app
+++ b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/ebin/fe.app
@@ -1,7 +1,7 @@
{application, fe,
[{description, "ERICSSON NR FOR FE"},
{vsn, "3.1"},
- {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]},
+ {modules, [fe1, fe2, fe3]},
{registered, []},
{applications, []},
{env, []},
diff --git a/lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-2.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-2.1/ebin/fe.app
index 47ea248720..717d30cf45 100644
--- a/lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-2.1/ebin/fe.app
+++ b/lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-2.1/ebin/fe.app
@@ -1,7 +1,7 @@
{application, fe,
[{description, "ERICSSON NR FOR FE"},
{vsn, "2.1"},
- {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]},
+ {modules, [fe1, fe2, fe3]},
{registered, []},
{applications, []},
{env, []},
diff --git a/lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-3.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-3.1/ebin/fe.app
index 0696e2494c..682d40eb12 100644
--- a/lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-3.1/ebin/fe.app
+++ b/lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-3.1/ebin/fe.app
@@ -1,7 +1,7 @@
{application, fe,
[{description, "ERICSSON NR FOR FE"},
{vsn, "3.1"},
- {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]},
+ {modules, [fe1, fe2, fe3]},
{registered, []},
{applications, []},
{mod, {fe1, []}}]}.
diff --git a/lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-500.18.7/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-500.18.7/ebin/fe.app
index 3a5c0ddd9b..77b97979cb 100644
--- a/lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-500.18.7/ebin/fe.app
+++ b/lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-500.18.7/ebin/fe.app
@@ -1,7 +1,7 @@
{application, fe,
[{description, "ERICSSON NR FOR FE"},
{vsn, "500.18.7"},
- {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]},
+ {modules, [fe1, fe2, fe3]},
{registered, []},
{applications, []},
{mod, {fe1, []}}]}.
diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.0/ebin/db.app b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.0/ebin/db.app
index 22530ee335..3968ce3c32 100644
--- a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.0/ebin/db.app
+++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.0/ebin/db.app
@@ -1,7 +1,7 @@
{application, db,
[{description, "ERICSSON NR FOR DB"},
{vsn, "1.0"},
- {modules, [{db1, "1.0"}, {db2, "1.0"}]},
+ {modules, [db1, db2]},
{registered, []},
{applications, []},
{env, []},
diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.1/ebin/db.app b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.1/ebin/db.app
index 7243a0a96a..0f0c926fbd 100644
--- a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.1/ebin/db.app
+++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.1/ebin/db.app
@@ -1,7 +1,7 @@
{application, db,
[{description, "ERICSSON NR FOR DB"},
{vsn, "1.1"},
- {modules, [{db1, "1.0"}, {db2, "1.0"}]},
+ {modules, [db1, db2]},
{registered, []},
{applications, []},
{env, []},
diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/ebin/db.app b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/ebin/db.app
index 202d7f1234..6901d225e2 100644
--- a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/ebin/db.app
+++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/ebin/db.app
@@ -1,7 +1,7 @@
{application, db,
[{description, "ERICSSON NR FOR DB"},
{vsn, "2.1"},
- {modules, [{db1, "1.0"}, {db2, "1.0"}]},
+ {modules, [db1, db2]},
{registered, []},
{applications, []},
{mod, {db1, []}}]}.
diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/ebin/fe.app
index c7ba1dfe91..d963a7e7fa 100644
--- a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/ebin/fe.app
+++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/ebin/fe.app
@@ -1,7 +1,7 @@
{application, fe,
[{description, "ERICSSON NR FOR FE"},
{vsn, "2.1.1"},
- {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]},
+ {modules, [fe1, fe2, fe3]},
{registered, []},
{applications, []},
{env, []},
diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/ebin/fe.app
index 47ea248720..717d30cf45 100644
--- a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/ebin/fe.app
+++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/ebin/fe.app
@@ -1,7 +1,7 @@
{application, fe,
[{description, "ERICSSON NR FOR FE"},
{vsn, "2.1"},
- {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]},
+ {modules, [fe1, fe2, fe3]},
{registered, []},
{applications, []},
{env, []},
diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/ebin/fe.app
index 0696e2494c..682d40eb12 100644
--- a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/ebin/fe.app
+++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/ebin/fe.app
@@ -1,7 +1,7 @@
{application, fe,
[{description, "ERICSSON NR FOR FE"},
{vsn, "3.1"},
- {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]},
+ {modules, [fe1, fe2, fe3]},
{registered, []},
{applications, []},
{mod, {fe1, []}}]}.
diff --git a/lib/sasl/test/systools_SUITE_data/d_regexp_appup/lib/fe-2.1.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_regexp_appup/lib/fe-2.1.1/ebin/fe.app
index c7ba1dfe91..d963a7e7fa 100644
--- a/lib/sasl/test/systools_SUITE_data/d_regexp_appup/lib/fe-2.1.1/ebin/fe.app
+++ b/lib/sasl/test/systools_SUITE_data/d_regexp_appup/lib/fe-2.1.1/ebin/fe.app
@@ -1,7 +1,7 @@
{application, fe,
[{description, "ERICSSON NR FOR FE"},
{vsn, "2.1.1"},
- {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]},
+ {modules, [fe1, fe2, fe3]},
{registered, []},
{applications, []},
{env, []},
diff --git a/lib/sasl/test/systools_SUITE_data/d_regexp_appup/lib/fe-2.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_regexp_appup/lib/fe-2.1/ebin/fe.app
index 47ea248720..717d30cf45 100644
--- a/lib/sasl/test/systools_SUITE_data/d_regexp_appup/lib/fe-2.1/ebin/fe.app
+++ b/lib/sasl/test/systools_SUITE_data/d_regexp_appup/lib/fe-2.1/ebin/fe.app
@@ -1,7 +1,7 @@
{application, fe,
[{description, "ERICSSON NR FOR FE"},
{vsn, "2.1"},
- {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]},
+ {modules, [fe1, fe2, fe3]},
{registered, []},
{applications, []},
{env, []},
diff --git a/lib/sasl/test/systools_SUITE_data/d_regexp_appup/lib/fe-3.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_regexp_appup/lib/fe-3.1/ebin/fe.app
index 0696e2494c..682d40eb12 100644
--- a/lib/sasl/test/systools_SUITE_data/d_regexp_appup/lib/fe-3.1/ebin/fe.app
+++ b/lib/sasl/test/systools_SUITE_data/d_regexp_appup/lib/fe-3.1/ebin/fe.app
@@ -1,7 +1,7 @@
{application, fe,
[{description, "ERICSSON NR FOR FE"},
{vsn, "3.1"},
- {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]},
+ {modules, [fe1, fe2, fe3]},
{registered, []},
{applications, []},
{mod, {fe1, []}}]}.
diff --git a/lib/sasl/test/systools_rc_SUITE.erl b/lib/sasl/test/systools_rc_SUITE.erl
index bd4aa9e7a7..0cb6e63cf3 100644
--- a/lib/sasl/test/systools_rc_SUITE.erl
+++ b/lib/sasl/test/systools_rc_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -46,7 +46,7 @@ syntax_check(Config) when is_list(Config) ->
[#application{name = test,
description = "TEST",
vsn = "0.1",
- modules = [{foo,1},{bar,1},{baz,1},{old_mod,1}],
+ modules = [foo,bar,baz,old_mod],
regs = [],
mod = {sasl, []}},
#application{name = snmp,
@@ -59,7 +59,7 @@ syntax_check(Config) when is_list(Config) ->
[#application{name = test,
description = "TEST",
vsn = "1.0",
- modules = [{foo,1},{bar,1},{baz,1},{new_mod,1}],
+ modules = [foo,bar,baz,new_mod],
regs = [],
mod = {sasl, []}}],
S1 = [
@@ -128,8 +128,8 @@ translate(Config) when is_list(Config) ->
[#application{name = test,
description = "TEST",
vsn = "1.0",
- modules = [{foo,1},{bar,1},{baz,1},
- {x,1},{y,1},{z,1}],
+ modules = [foo,bar,baz,
+ x,y,z],
regs = [],
mod = {sasl, []}}],
%% Simple translation (1)
@@ -439,7 +439,7 @@ translate_app(Config) when is_list(Config) ->
[#application{name = test,
description = "TEST",
vsn = "1.0",
- modules = [{foo,1},{bar,1},{baz,1}],
+ modules = [foo,bar,baz],
regs = [],
mod = {sasl, []}},
#application{name = pelle,
@@ -452,7 +452,7 @@ translate_app(Config) when is_list(Config) ->
[#application{name = test,
description = "TEST",
vsn = "1.0",
- modules = [{foo,1},{bar,1},{baz,1}],
+ modules = [foo,bar,baz],
regs = [],
mod = {sasl, []}}],
%% Simple translation (1)
@@ -492,13 +492,13 @@ translate_emulator_restarts(_Config) ->
[#application{name = test,
description = "TEST",
vsn = "1.0",
- modules = [{foo,1},{bar,1},{baz,1}],
+ modules = [foo,bar,baz],
regs = [],
mod = {sasl, []}},
#application{name = test,
description = "TEST2",
vsn = "1.0",
- modules = [{x,1},{y,1},{z,1}],
+ modules = [x,y,z],
regs = [],
mod = {sasl, []}}],
%% restart_new_emulator
diff --git a/lib/snmp/doc/src/Makefile b/lib/snmp/doc/src/Makefile
index 6c58ae994a..6e55498669 100644
--- a/lib/snmp/doc/src/Makefile
+++ b/lib/snmp/doc/src/Makefile
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -118,7 +118,7 @@ clean clean_docs: clean_html clean_man clean_pdf
rm -f errs core *~
$(INDEX_TARGET): $(INDEX_SRC) ../../vsn.mk # Create top make file
- sed -e 's;%VSN%;$(VSN);' $< > $@ # inserting version number
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@ # inserting version number
man: man1 man3 man6 man7
diff --git a/lib/snmp/doc/src/notes.xml b/lib/snmp/doc/src/notes.xml
index 2d045faa0f..5b94dcb051 100644
--- a/lib/snmp/doc/src/notes.xml
+++ b/lib/snmp/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>1996</year><year>2012</year>
+ <year>1996</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -34,6 +34,178 @@
<section>
+ <title>SNMP Development Toolkit 4.23</title>
+<!--
+ <p>Version 4.23 supports code replacement in runtime from/to
+ version 4.22.1,
+ 4.22, 4.21.7 4.21.6 4.21.5, 4.21.4, 4.21.3, 4.21.2, 4.21.1 and 4.21. </p>
+-->
+
+ <section>
+ <title>Improvements and new features</title>
+<!--
+ <p>-</p>
+-->
+
+ <list type="bulleted">
+ <item>
+ <p>[manager] Polish return values of snmpm_user_default according
+ to snmpm_user doc.</p>
+ <p>Luca Favatella</p>
+ <p>Own Id: OTP-10671</p>
+ </item>
+
+ <item>
+ <p>[agent] Remove runtime warning in snmpa_agent because of
+ tuple fun usage. </p>
+ <p>Luca Favatella</p>
+ <p>Own Id: OTP-10672</p>
+ </item>
+
+ <item>
+ <p>[manager] SNMP manager performance optimization. </p>
+ <p>Ivan Dubovik</p>
+ <p>Own Id: OTP-10673</p>
+ </item>
+
+ </list>
+
+ </section>
+
+ <section>
+ <title>Fixed Bugs and Malfunctions</title>
+ <p>-</p>
+
+ <!--
+ <list type="bulleted">
+ <item>
+ <p>[agent] Simultaneous
+ <seealso marker="snmpa#backup">snmpa:backup/1,2</seealso>
+ calls can interfere.
+ The master agent did not check if a backup was already in
+ progress when a backup request was accepted. </p>
+ <p>Own Id: OTP-9884</p>
+ <p>Aux Id: Seq 11995</p>
+ </item>
+
+ </list>
+ -->
+
+ </section>
+
+ <section>
+ <title>Incompatibilities</title>
+<!--
+ <p>-</p>
+-->
+
+ <list type="bulleted">
+ <item>
+ <p>[manager] The old Addr-and-Port based API functions, previously
+ long deprecated and marked for deletion in R16B, has now been
+ removed. </p>
+ <p>Own Id: OTP-10027</p>
+ </item>
+
+ </list>
+ </section>
+
+ </section> <!-- 4.23 -->
+
+
+ <section>
+ <title>SNMP Development Toolkit 4.22.1</title>
+ <p>Version 4.22.1 supports code replacement in runtime from/to
+ version 4.22, 4.21.7 4.21.6 4.21.5, 4.21.4, 4.21.3, 4.21.2, 4.21.1 and
+ 4.21. </p>
+
+ <section>
+ <title>Improvements and new features</title>
+<!--
+ <p>-</p>
+-->
+
+ <list type="bulleted">
+ <item>
+ <p>[agent] Sematic fixes to SNMP-USER-BASED-SM-MIB.
+ The semantics allow the <c>usmUserAuthKeyChange</c> and
+ <c>usmUserPrivKeyChange</c> objects to be written to in the
+ same set requests that also creates and clones the user.
+ This was not possible beforehand, causing test tools checking
+ semantic SNMPv3 behaviour to fail on a lot of test cases. </p>
+ <p>Furthermore, once the user has been cloned by writing to an
+ instance of <c>usmUserCloneFrom</c>, further set-operations to
+ the same object will not return an error, but be no-ops.
+ Especially, it must be avoided to copy security parameters
+ again (possibly even from a different user). </p>
+ <p>Stefan Zegenhagen</p>
+ <p>Own Id: OTP-10166</p>
+ </item>
+
+ <item>
+ <p>[agent] Errors in <c>vacmAccessTable</c> RowStatus handling.
+ There are problems with the handling of vacmAccessTableStatus
+ that cause some SNMP test suites to report errors.
+ Most notably, erroneous set operations frequently cause "genErr"
+ errors to be returned. These "genErr" errors are usually caused
+ by badmatch exceptions coming from
+ <c>{ok, Row} = snmpa_vacm:get_row(RowIndex)</c>
+ if the row does not exist. </p>
+ <p>The semantics of the RowStatus handling in that table has
+ been adjusted to be compliant with the RowStatus
+ textual description of SNPMv2-TC MIB. </p>
+ <p>Stefan Zegenhagen</p>
+ <p>Own Id: OTP-10164</p>
+ </item>
+ </list>
+
+ </section>
+
+ <section>
+ <title>Fixed Bugs and Malfunctions</title>
+<!--
+ <p>-</p>
+-->
+
+ <list type="bulleted">
+ <item>
+ <p>[agent] Fix walk over vacmAccessTable.
+ Fix the get_next implementation of vacmAccessTable to
+ return all table entries. </p>
+ <p>The get_next implementation of vacmAccessTable did not return
+ all available table data. Instead, it only returned the first
+ column for each row, and all columns for the last row available. </p>
+ <p>Stefan Zegenhagen</p>
+ <p>Own Id: OTP-10165</p>
+ </item>
+
+ <item>
+ <p>[manager]
+ <seealso marker="snmpm#log_to_io">snmpm:log_to_io/6</seealso>
+ did not use the LogName argument. </p>
+ <p>Own Id: OTP-10066</p>
+ </item>
+
+ <item>
+ <p>Incorrect TimeTicks decode. Also bad handling of
+ invalid encode (value outside of value range) for both
+ <c>TimeTicks</c> and <c>Unsigned32</c>. </p>
+ <p>Own Id: OTP-10132</p>
+ </item>
+
+ </list>
+
+ </section>
+
+ <section>
+ <title>Incompatibilities</title>
+ <p>-</p>
+ </section>
+
+ </section> <!-- 4.22.1 -->
+
+
+ <section>
<title>SNMP Development Toolkit 4.22</title>
<p>Version 4.22 supports code replacement in runtime from/to
version 4.21.7 4.21.6 4.21.5, 4.21.4, 4.21.3, 4.21.2, 4.21.1 and 4.21. </p>
diff --git a/lib/snmp/doc/src/snmp_config.xml b/lib/snmp/doc/src/snmp_config.xml
index 340f2f1fa7..eec53162a1 100644
--- a/lib/snmp/doc/src/snmp_config.xml
+++ b/lib/snmp/doc/src/snmp_config.xml
@@ -963,7 +963,8 @@ Manager snmp config:
7b. User name? hobbes
7c. Security name? [hobbes]
7d. Authentication protocol (no/sha/md5)? [no] sha
-7e Authentication [sha] key (length 0 or 20)? [""] [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
+7e Authentication [sha] key (length 0 or 20)? [""] [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, \
+ 17,18,19,20]
7d. Priv protocol (no/des/aes)? [no] des
7f Priv [des] key (length 0 or 16)? [""] 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25
7. Configure an usm user handled by this manager (y/n)? [y] n
diff --git a/lib/snmp/doc/src/snmpa_network_interface_filter.xml b/lib/snmp/doc/src/snmpa_network_interface_filter.xml
index 10419517dd..bff0a24879 100644
--- a/lib/snmp/doc/src/snmpa_network_interface_filter.xml
+++ b/lib/snmp/doc/src/snmpa_network_interface_filter.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>2007</year><year>2010</year>
+ <year>2007</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -84,7 +84,9 @@
<title>DATA TYPES</title>
<code type="none">
port() = integer() > 0
-pdu_type() = 'get-request' | 'get-next-request' | 'get-response' | 'set-request' | trap | 'get-bulk-request' | 'inform-request' | report
+pdu_type() = 'get-request' | 'get-next-request' | 'get-response' |
+ 'set-request' | trap | 'get-bulk-request' | 'inform-request' |
+ report
</code>
<marker id="accept_recv"></marker>
</section>
diff --git a/lib/snmp/doc/src/snmpm.xml b/lib/snmp/doc/src/snmpm.xml
index 9bbb6cdbdb..07fdd208ff 100644
--- a/lib/snmp/doc/src/snmpm.xml
+++ b/lib/snmp/doc/src/snmpm.xml
@@ -173,16 +173,16 @@ sec_level() = noAuthNoPriv | authNoPriv | authPriv
</type>
<desc>
<p>Register the manager entity (=user) responsible for specific
- agent(s). </p>
+ agent(s). </p>
<p><c>Module</c> is the callback module (snmpm_user behaviour) which
- will be called whenever something happens (detected
- agent, incoming reply or incoming trap/notification).
- Note that this could have already been done as a
- consequence of the node config. (see users.conf).</p>
+ will be called whenever something happens (detected
+ agent, incoming reply or incoming trap/notification).
+ Note that this could have already been done as a
+ consequence of the node config. (see users.conf).</p>
- <p>The argument <c>DefaultAgentConfig</c> is used as default values when
- this user register agents.</p>
+ <p>The argument <c>DefaultAgentConfig</c> is used as default
+ values when this user register agents.</p>
<p>The type of <c>Val</c> depends on <c>Item</c>: </p>
<code type="none"><![CDATA[
@@ -279,7 +279,6 @@ sec_level = noAuthNoPriv | authNoPriv | authPriv
<fsummary>Register this agent</fsummary>
<type>
<v>UserId = term()</v>
- <v>Addr = ip_address()</v>
<v>TargetName = target_name()</v>
<v>Config = [agent_config()]</v>
<v>agent_config() = {Item, Val}</v>
@@ -367,7 +366,7 @@ sec_level = noAuthNoPriv | authNoPriv | authPriv
<p>Update agent config. The function <c>update_agent_info/3</c>
should be used when several values needs to be updated atomically. </p>
<p>See function
- <seealso marker="#register_agent">register_agent</seealso>)
+ <seealso marker="#register_agent">register_agent</seealso>
for more info about what kind of items are allowed. </p>
<marker id="which_agents"></marker>
diff --git a/lib/snmp/doc/src/snmpm_network_interface_filter.xml b/lib/snmp/doc/src/snmpm_network_interface_filter.xml
index 5f80cec94e..4dc133dd71 100644
--- a/lib/snmp/doc/src/snmpm_network_interface_filter.xml
+++ b/lib/snmp/doc/src/snmpm_network_interface_filter.xml
@@ -82,7 +82,9 @@
<title>DATA TYPES</title>
<code type="none">
port() = integer() > 0
-pdu_type() = 'get-request' | 'get-next-request' | 'get-response' | 'set-request' | trap | 'get-bulk-request' | 'inform-request' | report | trappdu
+pdu_type() = 'get-request' | 'get-next-request' | 'get-response' |
+ 'set-request' | trap | 'get-bulk-request' | 'inform-request' |
+ report | trappdu
</code>
<marker id="accept_recv"></marker>
</section>
diff --git a/lib/snmp/examples/ex1/Makefile b/lib/snmp/examples/ex1/Makefile
index 996b686e0e..8d10476b30 100644
--- a/lib/snmp/examples/ex1/Makefile
+++ b/lib/snmp/examples/ex1/Makefile
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2009. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/snmp/examples/ex2/Makefile b/lib/snmp/examples/ex2/Makefile
index 090b9274d4..a85ff8e4e2 100644
--- a/lib/snmp/examples/ex2/Makefile
+++ b/lib/snmp/examples/ex2/Makefile
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2006-2009. All Rights Reserved.
+# Copyright Ericsson AB 2006-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/snmp/mibs/Makefile.in b/lib/snmp/mibs/Makefile.in
index c74c0b2ac4..e7ca540cc6 100644
--- a/lib/snmp/mibs/Makefile.in
+++ b/lib/snmp/mibs/Makefile.in
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2011. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -141,11 +141,11 @@ OTP_MIBDIR = $(shell if test -d ../../otp_mibs; then echo otp_mibs; \
debug opt: $(TARGET_FILES)
$(ERL_TOP)/lib/snmp/bin/snmp-v2tov1: $(ERL_TOP)/lib/snmp/bin/snmp-v2tov1.src
- $(PERL) -p -e 's?%PERL%?$(PERL)? ' < $< > $@
- chmod 755 $@
+ $(gen_verbose)$(PERL) -p -e 's?%PERL%?$(PERL)? ' < $< > $@
+ $(V_at)chmod 755 $@
$(SNMP_BIN_TARGET_DIR)/OTP-REG.bin: $(ERL_TOP)/lib/$(OTP_MIBDIR)/mibs/OTP-REG.mib
- $(ERLC) -pa $(SNMP_TOOLKIT)/ebin -I $(SNMP_TOOLKIT)/priv/mibs $(SNMP_FLAGS) -o $(SNMP_BIN_TARGET_DIR) $<
+ $(snmp_verbose)$(ERLC) -pa $(SNMP_TOOLKIT)/ebin -I $(SNMP_TOOLKIT)/priv/mibs $(SNMP_FLAGS) -o $(SNMP_BIN_TARGET_DIR) $<
# To support parallel make, we'll need explicit dependencies
# to ensure that an imported MIB has been compiled when it's needed.
@@ -214,7 +214,7 @@ info:
@echo "RELSYSDIR = "$(RELSYSDIR)""
v1/%.mib.v1: %.mib $(ERL_TOP)/lib/snmp/bin/snmp-v2tov1
- $(ERL_TOP)/lib/snmp/bin/snmp-v2tov1 -o $@ $<
+ $(gen_verbose)$(ERL_TOP)/lib/snmp/bin/snmp-v2tov1 -o $@ $<
# ----------------------------------------------------
diff --git a/lib/snmp/priv/conf/agent/Makefile b/lib/snmp/priv/conf/agent/Makefile
index c7921bacc2..3082889dc8 100644
--- a/lib/snmp/priv/conf/agent/Makefile
+++ b/lib/snmp/priv/conf/agent/Makefile
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2009. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/snmp/priv/conf/manager/Makefile b/lib/snmp/priv/conf/manager/Makefile
index ba5a74c3fe..a04c227754 100644
--- a/lib/snmp/priv/conf/manager/Makefile
+++ b/lib/snmp/priv/conf/manager/Makefile
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2004-2009. All Rights Reserved.
+# Copyright Ericsson AB 2004-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/snmp/src/agent/Makefile b/lib/snmp/src/agent/Makefile
index 9a982edf91..beed696648 100644
--- a/lib/snmp/src/agent/Makefile
+++ b/lib/snmp/src/agent/Makefile
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2009. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
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 2e801622e7..3c4ba1af66 100644
--- a/lib/snmp/src/agent/snmp_user_based_sm_mib.erl
+++ b/lib/snmp/src/agent/snmp_user_based_sm_mib.erl
@@ -54,6 +54,7 @@
%% Columns not accessible via SNMP
-define(usmUserAuthKey, 14).
-define(usmUserPrivKey, 15).
+-define(is_cloning, 16).
%%%-----------------------------------------------------------------
@@ -564,7 +565,9 @@ usmUserTable(set, RowIndex, Cols0) ->
{ok, Cols} ->
?vtrace("usmUserTable(set) -> verified"
"~n Cols: ~p", [Cols]),
- NCols = pre_set(RowIndex, Cols),
+ % check whether we're cloning. if so, get cloned params and add a few
+ % defaults that might be needed.
+ NCols = pre_set(RowIndex, validate_clone_from(RowIndex, Cols)),
?vtrace("usmUserTable(set) -> pre-set: "
"~n NCols: ~p", [NCols]),
%% NOTE: The NCols parameter is sent to snmp_generic, but not to
@@ -730,30 +733,40 @@ validate_is_set_ok(Error, _RowIndex, _Cols) ->
Error.
do_validate_is_set_ok(RowIndex, Cols) ->
- validate_clone_from(RowIndex, Cols),
- validate_auth_protocol(RowIndex, Cols),
- validate_auth_key_change(RowIndex, Cols),
- validate_own_auth_key_change(RowIndex, Cols),
- validate_priv_protocol(RowIndex, Cols),
- validate_priv_key_change(RowIndex, Cols),
- validate_own_priv_key_change(RowIndex, Cols),
+ NCols = validate_clone_from(RowIndex, Cols),
+ validate_auth_protocol(RowIndex, NCols),
+ validate_auth_key_change(RowIndex, NCols),
+ validate_own_auth_key_change(RowIndex, NCols),
+ validate_priv_protocol(RowIndex, NCols),
+ validate_priv_key_change(RowIndex, NCols),
+ validate_own_priv_key_change(RowIndex, NCols),
ok.
pre_set(RowIndex, Cols) ->
+ %% Remove the ?is_cloning member again; it must no longer be
+ %% present.
+ Cols0 = key1delete(?is_cloning, Cols),
%% Possibly initialize the usmUserSecurityName and privacy keys
case snmp_generic:table_row_exists(db(usmUserTable), RowIndex) of
- true -> Cols;
+ true -> Cols0;
false ->
SecName = get_user_name(RowIndex),
- [{?usmUserSecurityName, SecName} | Cols] ++
- [{?usmUserAuthKey, ""},
- {?usmUserPrivKey, ""}]
+ Cols1 = [{?usmUserSecurityName, SecName} | Cols0],
+ case proplists:get_value(?is_cloning, Cols) of
+ true ->
+ % the row is just being cloned. the cloned user's
+ % passwords are already present in Cols and must
+ % not be overwritten.
+ Cols1;
+ _ ->
+ Cols1 ++ [{?usmUserAuthKey, ""},
+ {?usmUserPrivKey, ""}]
+ end
end.
validate_set({noError, 0}, RowIndex, Cols) ->
%% Now, all is_set_ok validation steps have been executed. So
%% everything is ready for the set.
- set_clone_from(RowIndex, Cols),
set_auth_key_change(RowIndex, Cols),
set_own_auth_key_change(RowIndex, Cols),
set_priv_key_change(RowIndex, Cols),
@@ -769,7 +782,7 @@ validate_set(Error, _RowIndex, _Cols) ->
%% no further checks.
%%-----------------------------------------------------------------
validate_clone_from(RowIndex, Cols) ->
- case lists:keysearch(?usmUserCloneFrom, 1, Cols) of
+ case key1search(?usmUserCloneFrom, Cols) of
{value, {_Col, RowPointer}} ->
RowIndex2 = extract_row(RowPointer),
OldCloneFrom = snmp_generic:table_get_element(db(usmUserTable),
@@ -778,35 +791,63 @@ validate_clone_from(RowIndex, Cols) ->
case OldCloneFrom of
{value, Val} when Val /= noinit ->
%% This means that the cloning is already done...
- ok;
+ no_cloning(Cols);
_ ->
- %% Otherwise, we must check the CloneFrom value
- case snmp_generic:table_get_element(db(usmUserTable),
- RowIndex2,
- ?usmUserStatus) of
- {value, ?'RowStatus_active'} -> ok;
- _ -> inconsistentName(?usmUserCloneFrom)
- end
+ %% Otherwise, we must check the CloneFrom value. It
+ %% must relate to a usmUserEntry that exists and is active.
+ case snmp_generic:table_get_row(db(usmUserTable), RowIndex2) of
+ CloneFromRow when is_tuple(CloneFromRow) ->
+ case element(?usmUserStatus, CloneFromRow) of
+ ?'RowStatus_active' ->
+ get_cloned_cols(CloneFromRow, Cols);
+ _ ->
+ inconsistentName(?usmUserCloneFrom)
+ end;
+ undefined ->
+ inconsistentName(?usmUserCloneFrom)
+ end
end;
false ->
- ok
+ % no ?usmUserCloneFrom specified, don't modify columns
+ no_cloning(Cols)
end.
+get_cloned_cols(CloneFromRow, Cols) ->
+ % initialize cloned columns with data from CloneFromRow
+ % and overwrite that again with data found in Cols
+ AuthP = element(?usmUserAuthProtocol, CloneFromRow),
+ PrivP = element(?usmUserPrivProtocol, CloneFromRow),
+ AuthK = element(?usmUserAuthKey, CloneFromRow),
+ PrivK = element(?usmUserPrivKey, CloneFromRow),
+ ClonedCols = [{?usmUserAuthProtocol, AuthP},
+ {?usmUserPrivProtocol, PrivP},
+ {?usmUserAuthKey, AuthK},
+ {?usmUserPrivKey, PrivK},
+ {?is_cloning, true}
+ ],
+ Func = fun({Col, _} = Item, NCols) ->
+ key1store(Col, NCols, Item)
+ end,
+ Cols1 = lists:foldl(Func, ClonedCols, Cols),
+ key1sort(Cols1).
+
+no_cloning(Cols0) ->
+ Cols1 = key1delete(?usmUserCloneFrom, Cols0),
+ key1delete(?is_cloning, Cols1).
+
validate_auth_protocol(RowIndex, Cols) ->
- case lists:keysearch(?usmUserAuthProtocol, 1, Cols) of
+ case key1search(?usmUserAuthProtocol, Cols) of
{value, {_Col, AuthProtocol}} ->
- %% Check if the row has been cloned; we can't check the
+ %% Check if the row is being cloned; we can't check the
%% old value of authProtocol, because if the row was
%% createAndWaited, the default value would have been
%% written (usmNoAuthProtocol).
- OldCloneFrom = snmp_generic:table_get_element(db(usmUserTable),
- RowIndex,
- ?usmUserCloneFrom),
- case OldCloneFrom of
- {value, Val} when Val /= noinit ->
- %% This means that the cloning is already done; set is ok
- %% if new protocol is usmNoAuthProtocol
+ IsCloning = proplists:get_value(?is_cloning, Cols, false),
+ if
+ not IsCloning ->
+ %% This means that the row is not being cloned right
+ %% now; set is ok if new protocol is usmNoAuthProtocol
case AuthProtocol of
?usmNoAuthProtocol ->
%% Check that the Priv protocl is noPriv
@@ -821,7 +862,7 @@ validate_auth_protocol(RowIndex, Cols) ->
_ ->
wrongValue(?usmUserAuthProtocol)
end;
- _ ->
+ true ->
%% Otherwise, check that the new protocol is known,
%% and that the system we're running supports the
%% hash function.
@@ -867,7 +908,7 @@ validate_own_priv_key_change(RowIndex, Cols) ->
%% Check that the requesting user is the same as the modified user
validate_requester(RowIndex, Cols, KeyChangeCol) ->
- case lists:keysearch(KeyChangeCol, 1, Cols) of
+ case key1search(KeyChangeCol, Cols) of
{value, _} ->
case get(sec_model) of % Check the securityModel in the request
?SEC_USM -> ok;
@@ -890,17 +931,14 @@ validate_requester(RowIndex, Cols, KeyChangeCol) ->
end.
validate_key_change(RowIndex, Cols, KeyChangeCol, Type) ->
- case lists:keysearch(KeyChangeCol, 1, Cols) of
+ case key1search(KeyChangeCol, Cols) of
{value, {_Col, KeyC}} ->
%% Check if the row has been cloned; or if it is cloned in
%% this set-operation.
OldCloneFrom = snmp_generic:table_get_element(db(usmUserTable),
RowIndex,
?usmUserCloneFrom),
- IsClonePresent = case lists:keysearch(?usmUserCloneFrom, 1, Cols) of
- {value, _} -> true;
- false -> false
- end,
+ IsClonePresent = proplists:get_value(?is_cloning, Cols, false),
%% Set is ok if 1) the user already is created, 2) this is
%% a new user, which has been cloned, or is about to be
%% cloned.
@@ -912,7 +950,7 @@ validate_key_change(RowIndex, Cols, KeyChangeCol, Type) ->
%% The user is cloned in this operation
ok;
_ ->
- %% The user doen't exist, or hasn't been cloned,
+ %% The user doesn't exist, or hasn't been cloned,
%% and is not cloned in this operation.
inconsistentName(KeyChangeCol)
end,
@@ -939,17 +977,15 @@ validate_key_change(RowIndex, Cols, KeyChangeCol, Type) ->
end.
validate_priv_protocol(RowIndex, Cols) ->
- case lists:keysearch(?usmUserPrivProtocol, 1, Cols) of
+ case key1search(?usmUserPrivProtocol, Cols) of
{value, {_Col, PrivProtocol}} ->
%% Check if the row has been cloned; we can't check the
%% old value of privhProtocol, because if the row was
%% createAndWaited, the default value would have been
%% written (usmNoPrivProtocol).
- OldCloneFrom = snmp_generic:table_get_element(db(usmUserTable),
- RowIndex,
- ?usmUserCloneFrom),
- case OldCloneFrom of
- {value, Val} when Val /= noinit ->
+ IsCloning = proplists:get_value(?is_cloning, Cols, false),
+ if
+ not IsCloning ->
%% This means that the cloning is already done; set is ok
%% if new protocol is usmNoPrivProtocol
case PrivProtocol of
@@ -962,7 +998,7 @@ validate_priv_protocol(RowIndex, Cols) ->
_ ->
wrongValue(?usmUserPrivProtocol)
end;
- _ ->
+ true ->
%% Otherwise, check that the new protocol is known,
%% and that the system we're running supports the
%% crypto function.
@@ -1005,31 +1041,6 @@ validate_priv_protocol(RowIndex, Cols) ->
end.
-set_clone_from(RowIndex, Cols) ->
- %% If CloneFrom is modified, do the cloning.
- case lists:keysearch(?usmUserCloneFrom, 1, Cols) of
- {value, {_Col, RowPointer}} ->
- RowIndex2 = extract_row(RowPointer), % won't fail
- CloneRow = snmp_generic:table_get_row(db(usmUserTable), RowIndex2,
- foi(usmUserTable)),
- AuthP = element(?usmUserAuthProtocol, CloneRow),
- PrivP = element(?usmUserPrivProtocol, CloneRow),
- AuthK = element(?usmUserAuthKey, CloneRow),
- PrivK = element(?usmUserPrivKey, CloneRow),
- SCols = [{?usmUserAuthProtocol, AuthP},
- {?usmUserPrivProtocol, PrivP},
- {?usmUserAuthKey, AuthK},
- {?usmUserPrivKey, PrivK}],
- case snmp_generic:table_set_elements(db(usmUserTable),
- RowIndex,
- SCols) of
- true -> ok;
- false -> {commitFailed, ?usmUserCloneFrom}
- end;
- false ->
- ok
- end.
-
set_auth_key_change(RowIndex, Cols) ->
set_key_change(RowIndex, Cols, ?usmUserAuthKeyChange, auth).
@@ -1043,7 +1054,7 @@ set_own_priv_key_change(RowIndex, Cols) ->
set_key_change(RowIndex, Cols, ?usmUserOwnPrivKeyChange, priv).
set_key_change(RowIndex, Cols, KeyChangeCol, Type) ->
- case lists:keysearch(KeyChangeCol, 1, Cols) of
+ case key1search(KeyChangeCol, Cols) of
{value, {_Col, KeyChange}} ->
KeyCol = case Type of
auth -> ?usmUserAuthKey;
@@ -1071,11 +1082,11 @@ extract_row([H | T], [H | T2]) -> extract_row(T, T2);
extract_row([], [?usmUserSecurityName | T]) -> T;
extract_row(_, _) -> wrongValue(?usmUserCloneFrom).
-%% Pre: the user exixt
+%% Pre: the user exists or is being cloned in this operation
get_auth_proto(RowIndex, Cols) ->
- %% The protocol can be chanegd by the request too, otherwise,
+ %% The protocol can be changed by the request too, otherwise,
%% check the stored protocol.
- case lists:keysearch(?usmUserAuthProtocol, 1, Cols) of
+ case key1search(?usmUserAuthProtocol, Cols) of
{value, {_, Protocol}} ->
Protocol;
false ->
@@ -1090,11 +1101,11 @@ get_auth_proto(RowIndex, Cols) ->
end
end.
-%% Pre: the user exixt
+%% Pre: the user exists or is being cloned in this operation
get_priv_proto(RowIndex, Cols) ->
- %% The protocol can be chanegd by the request too, otherwise,
+ %% The protocol can be changed by the request too, otherwise,
%% check the stored protocol.
- case lists:keysearch(?usmUserPrivProtocol, 1, Cols) of
+ case key1search(?usmUserPrivProtocol, Cols) of
{value, {_, Protocol}} ->
Protocol;
false ->
@@ -1232,6 +1243,27 @@ set_sname(_) -> %% Keep it, if already set.
error(Reason) ->
throw({error, Reason}).
+
+%%-----------------------------------------------------------------
+%% lists key-function(s) wrappers
+
+-compile({inline,key1delete/2}).
+key1delete(Key, List) ->
+ lists:keydelete(Key, 1, List).
+
+-compile({inline,key1search/2}).
+key1search(Key, List) ->
+ lists:keysearch(Key, 1, List).
+
+-compile({inline,key1store/3}).
+key1store(Key, List, Elem) ->
+ lists:keystore(Key, 1, List, Elem).
+
+-compile({inline,key1sort/1}).
+key1sort(List) ->
+ lists:keysort(1, List).
+
+
%%-----------------------------------------------------------------
info_msg(F, A) ->
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 479a44795f..ad9540e886 100644
--- a/lib/snmp/src/agent/snmp_view_based_acm_mib.erl
+++ b/lib/snmp/src/agent/snmp_view_based_acm_mib.erl
@@ -496,7 +496,7 @@ verify_vacmSecurityToGroupTable_col(_, Val) ->
%%
%%-----------------------------------------------------------------
vacmAccessTable(print) ->
- %% M�ste jag g�ra om alla entrien till {RowIdx, Row}?
+ %% Do I need to turn all entries into {RowIdx, Row}?
TableInfo = get_table(vacmAccessTable),
PrintRow =
fun(Prefix, Row) ->
@@ -565,45 +565,85 @@ vacmAccessTable(is_set_ok, RowIndex, Cols0) ->
case (catch verify_vacmAccessTable_cols(Cols0, [])) of
{ok, Cols} ->
IsValidKey = is_valid_key(RowIndex),
- case lists:keysearch(?vacmAccessStatus, 1, Cols) of
- %% Ok, if contextMatch is init
- {value, {Col, ?'RowStatus_active'}} ->
- {ok, Row} = snmpa_vacm:get_row(RowIndex),
- case element(?vacmAContextMatch, Row) of
- noinit -> {inconsistentValue, Col};
- _ -> {noError, 0}
+ StatusCol = lists:keyfind(?vacmAccessStatus, 1, Cols),
+ MaybeRow = snmpa_vacm:get_row(RowIndex),
+ case {StatusCol, MaybeRow} of
+ {{Col, ?'RowStatus_active'}, false} ->
+ %% row does not yet exist => inconsistentValue
+ %% (see SNMPv2-TC.mib, RowStatus textual convention)
+ {inconsistentValue, Col};
+ {{Col, ?'RowStatus_active'}, {ok, Row}} ->
+ %% Ok, if contextMatch is init
+ case element(?vacmAContextMatch, Row) of
+ noinit ->
+ %% check whether ContextMatch is being set in
+ %% the same operation
+ case proplists:get_value(?vacmAccessContextMatch,
+ Cols) of
+ undefined ->
+ %% no, we can't make this row active yet
+ {inconsistentValue, Col};
+ _ ->
+ %% ok, activate the row
+ {noError, 0}
+ end;
+ _ ->
+ {noError, 0}
end;
- {value, {Col, ?'RowStatus_notInService'}} -> % Ok, if not notReady
- {ok, Row} = snmpa_vacm:get_row(RowIndex),
+ {{Col, ?'RowStatus_notInService'}, false} ->
+ %% row does not yet exist => inconsistentValue
+ %% (see SNMPv2-TC.mib, RowStatus textual convention)
+ {inconsistentValue, Col};
+ {{Col, ?'RowStatus_notInService'}, {ok, Row}} ->
+ %% Ok, if not notReady
case element(?vacmAStatus, Row) of
- ?'RowStatus_notReady' -> {inconsistentValue, Col};
- _ -> {noError, 0}
+ ?'RowStatus_notReady' ->
+ {inconsistentValue, Col};
+ _ ->
+ {noError, 0}
end;
- {value, {Col, ?'RowStatus_notReady'}} -> % never ok!
+ {{Col, ?'RowStatus_notReady'}, _} ->
+ %% never ok!
{inconsistentValue, Col};
- {value, {Col, ?'RowStatus_createAndGo'}} -> % ok, if it doesn't exist
+ {{Col, ?'RowStatus_createAndGo'}, false} ->
+ %% ok, if it doesn't exist
Res = lists:keysearch(?vacmAccessContextMatch, 1, Cols),
- case snmpa_vacm:get_row(RowIndex) of
- false when (IsValidKey =:= true) andalso
- is_tuple(Res) -> {noError, 0};
- false -> {noCreation, Col}; % Bad RowIndex
- _ -> {inconsistentValue, Col}
+ if
+ IsValidKey =/= true ->
+ %% bad RowIndex => noCreation
+ {noCreation, Col};
+ is_tuple(Res) ->
+ %% required field is present => noError
+ {noError, 0};
+ true ->
+ %% required field is missing => inconsistentValue
+ {inconsistentValue, Col}
end;
- {value, {Col, ?'RowStatus_createAndWait'}} -> % ok, if it doesn't exist
- case snmpa_vacm:get_row(RowIndex) of
- false when (IsValidKey =:= true) -> {noError, 0};
- false -> {noCreation, Col}; % Bad RowIndex
- _ -> {inconsistentValue, Col}
+ {{Col, ?'RowStatus_createAndGo'}, _} ->
+ %% row already exists => inconsistentValue
+ {inconsistentValue, Col};
+ {{Col, ?'RowStatus_createAndWait'}, false} ->
+ %% ok, if it doesn't exist
+ if
+ IsValidKey =:= true ->
+ %% RowIndex is valid => noError
+ {noError, 0};
+ true ->
+ {noCreation, Col}
end;
- {value, {_Col, ?'RowStatus_destroy'}} -> % always ok!
+ {{Col, ?'RowStatus_createAndWait'}, _} ->
+ %% Row already exists => inconsistentValue
+ {inconsistentValue, Col};
+ {value, {_Col, ?'RowStatus_destroy'}} ->
+ %% always ok!
{noError, 0};
- _ -> % otherwise, it's a change; it must exist
- case snmpa_vacm:get_row(RowIndex) of
- {ok, _} ->
- {noError, 0};
- false ->
- {inconsistentName, element(1, hd(Cols))}
- end
+ {_, false} ->
+ %% otherwise, it's a row change;
+ %% row does not exist => inconsistentName
+ {inconsistentName, element(1, hd(Cols))};
+ _ ->
+ %% row change and row exists => noError
+ {noError, 0}
end;
Error ->
Error
@@ -734,10 +774,15 @@ do_vacmAccessTable_set(RowIndex, Cols) ->
%% Cols are sorted, and all columns are > 3.
+do_get_next(_RowIndex, []) ->
+ % Cols can be empty because we're called for each
+ % output of split_cols(); and one of that may well
+ % be empty.
+ [];
do_get_next(RowIndex, Cols) ->
case snmpa_vacm:get_next_row(RowIndex) of
{NextIndex, Row} ->
- F1 = fun(Col) when Col < ?vacmAccessStatus ->
+ F1 = fun(Col) when Col =< ?vacmAccessStatus ->
{[Col | NextIndex], element(Col-3, Row)};
(_) ->
endOfTable
@@ -745,9 +790,9 @@ do_get_next(RowIndex, Cols) ->
lists:map(F1, Cols);
false ->
case snmpa_vacm:get_next_row([]) of
- {_NextIndex, Row} ->
+ {NextIndex2, Row} ->
F2 = fun(Col) when Col < ?vacmAccessStatus ->
- {[Col+1 | RowIndex], element(Col-2, Row)};
+ {[Col+1 | NextIndex2], element(Col-2, Row)};
(_) ->
endOfTable
end,
diff --git a/lib/snmp/src/agent/snmpa_agent.erl b/lib/snmp/src/agent/snmpa_agent.erl
index 9d30e332f1..57846db13b 100644
--- a/lib/snmp/src/agent/snmpa_agent.erl
+++ b/lib/snmp/src/agent/snmpa_agent.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2012. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -1134,7 +1134,7 @@ handle_call({get, Vars, Context}, _From, S) ->
"~n Vars: ~p"
"~n Context: ~p", [Vars, Context]),
put_pdu_data({undefined, undefined, undefined, undefined, Context}),
- case catch mapfoldl({?MODULE, tr_var}, [], 1, Vars) of
+ case catch mapfoldl(fun ?MODULE:tr_var/2, [], 1, Vars) of
{error, Reason} -> {reply, {error, Reason}, S};
{_, Varbinds} ->
?vdebug("Varbinds: ~p",[Varbinds]),
@@ -1155,7 +1155,7 @@ handle_call({get_next, Vars, Context}, _From, S) ->
"~n Vars: ~p"
"~n Context: ~p",[Vars, Context]),
put_pdu_data({undefined, undefined, undefined, undefined, Context}),
- case catch mapfoldl({?MODULE, tr_var}, [], 1, Vars) of
+ case catch mapfoldl(fun ?MODULE:tr_var/2, [], 1, Vars) of
{error, Reason} -> {reply, {error, Reason}, S};
{_, Varbinds} ->
?vdebug("Varbinds: ~p",[Varbinds]),
diff --git a/lib/snmp/src/app/Makefile b/lib/snmp/src/app/Makefile
index 7a16d42c30..716add8b9e 100644
--- a/lib/snmp/src/app/Makefile
+++ b/lib/snmp/src/app/Makefile
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2003-2009. All Rights Reserved.
+# Copyright Ericsson AB 2003-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -116,10 +116,10 @@ info:
# ----------------------------------------------------
$(APP_TARGET): $(APP_SRC) ../../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
$(APPUP_TARGET): $(APPUP_SRC) ../../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
# ----------------------------------------------------
# Release Target
diff --git a/lib/snmp/src/app/snmp.appup.src b/lib/snmp/src/app/snmp.appup.src
index 8360d88c94..a6abf8439a 100644
--- a/lib/snmp/src/app/snmp.appup.src
+++ b/lib/snmp/src/app/snmp.appup.src
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2012. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -22,533 +22,12 @@
%% ----- U p g r a d e -------------------------------------------------------
[
- {"4.21.7",
- [
- {load_module, snmp_config, soft_purge, soft_purge, []},
- {load_module, snmp_conf, soft_purge, soft_purge, []},
- {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_framework_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_notification_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_standard_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_target_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_user_based_sm_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge, [snmp_conf]},
-
- {load_module, snmp, soft_purge, soft_purge, [snmp_log]},
- {load_module, snmpa, soft_purge, soft_purge, [snmp]},
- {load_module, snmpm, soft_purge, soft_purge, [snmp]},
- {load_module, snmp_log, soft_purge, soft_purge, []},
- {load_module, snmp_verbosity, soft_purge, soft_purge, []},
- {load_module, snmpm_mpd, soft_purge, soft_purge, []},
-
- {update, snmpa_local_db, soft, soft_purge, soft_purge, []},
- {update, snmpa_mib, soft, soft_purge, soft_purge, []},
- {update, snmpa_agent, soft, soft_purge, soft_purge, []},
-
- {add_module, snmpm_net_if_mt}
- ]
- },
- {"4.21.6",
- [
- {load_module, snmp_config, soft_purge, soft_purge, []},
- {load_module, snmp_conf, soft_purge, soft_purge, []},
- {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_framework_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_notification_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_standard_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_target_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_user_based_sm_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge, [snmp_conf]},
-
- {load_module, snmp, soft_purge, soft_purge, [snmp_log]},
- {load_module, snmpa, soft_purge, soft_purge, [snmp]},
- {load_module, snmpm, soft_purge, soft_purge, [snmp]},
- {load_module, snmp_log, soft_purge, soft_purge, []},
- {load_module, snmp_verbosity, soft_purge, soft_purge, []},
- {load_module, snmpm_mpd, soft_purge, soft_purge, []},
-
- {update, snmpa_local_db, soft, soft_purge, soft_purge, []},
- {update, snmpa_mib, soft, soft_purge, soft_purge, []},
- {update, snmpa_agent, soft, soft_purge, soft_purge, []},
-
- {add_module, snmpm_net_if_mt}
- ]
- },
- {"4.21.5",
- [
- {load_module, snmp_config, soft_purge, soft_purge, []},
- {load_module, snmp_conf, soft_purge, soft_purge, []},
- {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_framework_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_notification_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_standard_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_user_based_sm_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge, [snmp_conf]},
-
- {load_module, snmp, soft_purge, soft_purge, [snmp_log]},
- {load_module, snmpm, soft_purge, soft_purge, [snmp]},
- {load_module, snmp_log, soft_purge, soft_purge, []},
- {load_module, snmp_verbosity, soft_purge, soft_purge, []},
- {load_module, snmpm_mpd, soft_purge, soft_purge, []},
-
- {load_module, snmpa, soft_purge, soft_purge, [snmp]},
- {load_module, snmp_target_mib, soft_purge, soft_purge,
- [snmp_conf, snmpa_mib_lib]},
- {load_module, snmpa_mib_lib, soft_purge, soft_purge, []},
- {load_module, snmpa_trap, soft_purge, soft_purge, []},
- {load_module, snmpa_vacm, soft_purge, soft_purge, []},
- {update, snmpa_local_db, soft, soft_purge, soft_purge, []},
- {update, snmpa_mib, soft, soft_purge, soft_purge, []},
- {update, snmpa_supervisor, soft, soft_purge, soft_purge, []},
- {update, snmpa_agent, soft, soft_purge, soft_purge, []},
-
- {add_module, snmpm_net_if_mt}
- ]
- },
- {"4.21.4",
- [
- {load_module, snmp_config, soft_purge, soft_purge, []},
- {load_module, snmp_conf, soft_purge, soft_purge, []},
- {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_framework_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_notification_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_standard_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_user_based_sm_mib, soft_purge, soft_purge, [snmp_conf]},
-
- {load_module, snmp, soft_purge, soft_purge, [snmp_log]},
- {load_module, snmpm, soft_purge, soft_purge, [snmp]},
- {load_module, snmp_log, soft_purge, soft_purge, []},
- {load_module, snmp_verbosity, soft_purge, soft_purge, []},
- {load_module, snmpm_mpd, soft_purge, soft_purge, []},
-
- {load_module, snmpa, soft_purge, soft_purge, [snmp]},
- {load_module, snmp_target_mib, soft_purge, soft_purge,
- [snmp_conf, snmpa_mib_lib]},
- {load_module, snmpa_mib_lib, soft_purge, soft_purge, []},
- {load_module, snmpa_trap, soft_purge, soft_purge, []},
- {update, snmpa_supervisor, soft, soft_purge, soft_purge, []},
-
- {load_module, snmp_generic_mnesia, soft_purge, soft_purge, []},
- {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge,
- [snmp_conf]},
- {load_module, snmpa_vacm, soft_purge, soft_purge, []},
- {update, snmpa_local_db, soft, soft_purge, soft_purge, []},
- {update, snmpa_mib, soft, soft_purge, soft_purge, []},
- {update, snmpa_agent, soft, soft_purge, soft_purge, []},
-
- {add_module, snmpm_net_if_mt}
- ]
- },
- {"4.21.3",
- [
- {load_module, snmp_config, soft_purge, soft_purge, []},
- {load_module, snmp_conf, soft_purge, soft_purge, []},
- {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_framework_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_notification_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_standard_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_user_based_sm_mib, soft_purge, soft_purge, [snmp_conf]},
-
- {load_module, snmp, soft_purge, soft_purge, [snmp_log]},
- {load_module, snmpm, soft_purge, soft_purge, [snmp]},
- {load_module, snmp_log, soft_purge, soft_purge, []},
- {load_module, snmp_verbosity, soft_purge, soft_purge, []},
- {load_module, snmpm_mpd, soft_purge, soft_purge, []},
-
- {load_module, snmpa, soft_purge, soft_purge, [snmp]},
- {load_module, snmp_target_mib, soft_purge, soft_purge,
- [snmp_conf, snmpa_mib_lib]},
- {load_module, snmpa_mib_lib, soft_purge, soft_purge, []},
- {load_module, snmpa_trap, soft_purge, soft_purge, []},
- {update, snmpa_supervisor, soft, soft_purge, soft_purge, []},
-
- {load_module, snmp_generic_mnesia, soft_purge, soft_purge, []},
- {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge,
- [snmp_conf]},
- {load_module, snmpa_vacm, soft_purge, soft_purge, []},
- {update, snmpa_local_db, soft, soft_purge, soft_purge, []},
- {update, snmpa_mib, soft, soft_purge, soft_purge, []},
- {update, snmpa_agent, soft, soft_purge, soft_purge, []},
-
- {add_module, snmpm_net_if_mt}
- ]
- },
- {"4.21.2",
- [
- {load_module, snmp_config, soft_purge, soft_purge, []},
- {load_module, snmp_conf, soft_purge, soft_purge, []},
- {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_framework_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_notification_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_standard_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_user_based_sm_mib, soft_purge, soft_purge, [snmp_conf]},
-
- {load_module, snmp, soft_purge, soft_purge, [snmp_log]},
- {load_module, snmpm, soft_purge, soft_purge, [snmp]},
- {load_module, snmp_log, soft_purge, soft_purge, []},
- {load_module, snmp_verbosity, soft_purge, soft_purge, []},
- {load_module, snmpm_mpd, soft_purge, soft_purge, []},
-
- {load_module, snmpa, soft_purge, soft_purge, [snmp]},
- {load_module, snmp_target_mib, soft_purge, soft_purge,
- [snmp_conf, snmpa_mib_lib]},
- {load_module, snmpa_mib_lib, soft_purge, soft_purge, []},
- {update, snmpa_supervisor, soft, soft_purge, soft_purge, []},
-
- {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge,
- [snmp_conf]},
- {load_module, snmpa_vacm, soft_purge, soft_purge, []},
- {load_module, snmpa_mpd, soft_purge, soft_purge, []},
- {load_module, snmpa_set_lib, soft_purge, soft_purge, []},
- {load_module, snmpa_trap, soft_purge, soft_purge, []},
- {load_module, snmp_generic_mnesia, soft_purge, soft_purge, []},
- {update, snmpa_local_db, soft, soft_purge, soft_purge, []},
- {update, snmpa_mib, soft, soft_purge, soft_purge, []},
- {update, snmpa_agent, soft, soft_purge, soft_purge, []},
-
- {add_module, snmpm_net_if_mt}
- ]
- },
- {"4.21.1",
- [
- {load_module, snmp_config, soft_purge, soft_purge, []},
- {load_module, snmp_conf, soft_purge, soft_purge, []},
- {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_framework_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_notification_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_standard_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_user_based_sm_mib, soft_purge, soft_purge, [snmp_conf]},
-
- {load_module, snmp, soft_purge, soft_purge, [snmp_log]},
- {load_module, snmpm, soft_purge, soft_purge, [snmp]},
- {load_module, snmp_log, soft_purge, soft_purge, []},
- {load_module, snmp_verbosity, soft_purge, soft_purge, []},
- {load_module, snmpm_mpd, soft_purge, soft_purge, []},
-
- {load_module, snmpa, soft_purge, soft_purge, [snmp]},
- {load_module, snmp_target_mib, soft_purge, soft_purge,
- [snmp_conf, snmpa_mib_lib]},
- {load_module, snmpa_mib_lib, soft_purge, soft_purge, []},
- {update, snmpa_supervisor, soft, soft_purge, soft_purge, []},
-
- {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge,
- [snmp_conf]},
- {load_module, snmpa_vacm, soft_purge, soft_purge, []},
- {load_module, snmpa_mpd, soft_purge, soft_purge, []},
- {load_module, snmpa_set_lib, soft_purge, soft_purge, []},
- {load_module, snmpa_trap, soft_purge, soft_purge, []},
- {load_module, snmp_generic_mnesia, soft_purge, soft_purge, []},
- {update, snmpa_local_db, soft, soft_purge, soft_purge, []},
- {update, snmpa_mib, soft, soft_purge, soft_purge, []},
- {update, snmpa_agent, soft, soft_purge, soft_purge, []},
- {update, snmp_note_store, soft, soft_purge, soft_purge, []},
-
- {add_module, snmpm_net_if_mt}
- ]
- },
- {"4.21",
- [
- {load_module, snmp_config, soft_purge, soft_purge, []},
- {load_module, snmp_conf, soft_purge, soft_purge, []},
- {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_framework_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_notification_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_standard_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_user_based_sm_mib, soft_purge, soft_purge, [snmp_conf]},
-
- {load_module, snmp, soft_purge, soft_purge, [snmp_log]},
- {load_module, snmpm, soft_purge, soft_purge, [snmp]},
- {load_module, snmp_log, soft_purge, soft_purge, []},
- {load_module, snmp_verbosity, soft_purge, soft_purge, []},
- {load_module, snmpm_mpd, soft_purge, soft_purge, []},
-
- {load_module, snmpa, soft_purge, soft_purge, [snmp]},
- {load_module, snmpa_mib_lib, soft_purge, soft_purge, []},
- {update, snmpa_supervisor, soft, soft_purge, soft_purge, []},
-
- {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge,
- [snmp_conf]},
- {load_module, snmpa_vacm, soft_purge, soft_purge, []},
- {load_module, snmpa_mpd, soft_purge, soft_purge, []},
- {load_module, snmpa_set_lib, soft_purge, soft_purge, []},
- {load_module, snmpa_trap, soft_purge, soft_purge, []},
- {load_module, snmp_target_mib, soft_purge, soft_purge,
- [snmp_conf, snmpa_mib_lib]},
- {load_module, snmp_generic_mnesia, soft_purge, soft_purge, []},
- {update, snmpa_local_db, soft, soft_purge, soft_purge, []},
- {update, snmpa_mib, soft, soft_purge, soft_purge, []},
- {update, snmpa_agent, soft, soft_purge, soft_purge, []},
- {update, snmp_note_store, soft, soft_purge, soft_purge, []},
-
- {add_module, snmpm_net_if_mt}
- ]
- }
],
%% ------D o w n g r a d e ---------------------------------------------------
[
- {"4.21.7",
- [
- {load_module, snmp_config, soft_purge, soft_purge, []},
- {load_module, snmp_conf, soft_purge, soft_purge, []},
- {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_framework_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_notification_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_standard_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_target_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_user_based_sm_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge, [snmp_conf]},
-
- {load_module, snmp, soft_purge, soft_purge, [snmp_log]},
- {load_module, snmpa, soft_purge, soft_purge, [snmp]},
- {load_module, snmpm, soft_purge, soft_purge, [snmp]},
- {load_module, snmp_log, soft_purge, soft_purge, []},
- {load_module, snmp_verbosity, soft_purge, soft_purge, []},
- {load_module, snmpm_mpd, soft_purge, soft_purge, []},
-
- {update, snmpa_local_db, soft, soft_purge, soft_purge, []},
- {update, snmpa_mib, soft, soft_purge, soft_purge, []},
- {update, snmpa_agent, soft, soft_purge, soft_purge, []},
-
- {remove, {snmpm_net_if_mt, soft_purge, soft_purge}}
- ]
- },
- {"4.21.6",
- [
- {load_module, snmp_config, soft_purge, soft_purge, []},
- {load_module, snmp_conf, soft_purge, soft_purge, []},
- {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_framework_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_notification_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_standard_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_target_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_user_based_sm_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge, [snmp_conf]},
-
- {load_module, snmp, soft_purge, soft_purge, [snmp_log]},
- {load_module, snmpa, soft_purge, soft_purge, [snmp]},
- {load_module, snmpm, soft_purge, soft_purge, [snmp]},
- {load_module, snmp_log, soft_purge, soft_purge, []},
- {load_module, snmp_verbosity, soft_purge, soft_purge, []},
- {load_module, snmpm_mpd, soft_purge, soft_purge, []},
-
- {update, snmpa_local_db, soft, soft_purge, soft_purge, []},
- {update, snmpa_mib, soft, soft_purge, soft_purge, []},
- {update, snmpa_agent, soft, soft_purge, soft_purge, []},
-
- {remove, {snmpm_net_if_mt, soft_purge, soft_purge}}
- ]
- },
- {"4.21.5",
- [
- {load_module, snmp_config, soft_purge, soft_purge, []},
- {load_module, snmp_conf, soft_purge, soft_purge, []},
- {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_framework_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_notification_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_standard_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_user_based_sm_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge, [snmp_conf]},
-
- {load_module, snmp, soft_purge, soft_purge, [snmp_log]},
- {load_module, snmpm, soft_purge, soft_purge, [snmp]},
- {load_module, snmp_log, soft_purge, soft_purge, []},
- {load_module, snmp_verbosity, soft_purge, soft_purge, []},
- {load_module, snmpm_mpd, soft_purge, soft_purge, []},
-
- {load_module, snmpa, soft_purge, soft_purge, [snmp]},
- {load_module, snmp_target_mib, soft_purge, soft_purge,
- [snmp_conf, snmpa_mib_lib]},
- {load_module, snmpa_mib_lib, soft_purge, soft_purge, []},
- {load_module, snmpa_trap, soft_purge, soft_purge, []},
- {load_module, snmpa_vacm, soft_purge, soft_purge, []},
- {update, snmpa_supervisor, soft, soft_purge, soft_purge, []},
- {update, snmpa_local_db, soft, soft_purge, soft_purge, []},
- {update, snmpa_mib, soft, soft_purge, soft_purge, []},
- {update, snmpa_agent, soft, soft_purge, soft_purge, []},
-
- {remove, {snmpm_net_if_mt, soft_purge, soft_purge}}
- ]
- },
- {"4.21.4",
- [
- {load_module, snmp_config, soft_purge, soft_purge, []},
- {load_module, snmp_conf, soft_purge, soft_purge, []},
- {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_framework_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_notification_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_standard_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_user_based_sm_mib, soft_purge, soft_purge, [snmp_conf]},
-
- {load_module, snmp, soft_purge, soft_purge, [snmp_log]},
- {load_module, snmpm, soft_purge, soft_purge, [snmp]},
- {load_module, snmp_log, soft_purge, soft_purge, []},
- {load_module, snmp_verbosity, soft_purge, soft_purge, []},
- {load_module, snmpm_mpd, soft_purge, soft_purge, []},
-
- {load_module, snmpa, soft_purge, soft_purge, [snmp]},
- {load_module, snmp_target_mib, soft_purge, soft_purge,
- [snmp_conf, snmpa_mib_lib]},
- {load_module, snmpa_mib_lib, soft_purge, soft_purge, []},
- {load_module, snmpa_trap, soft_purge, soft_purge, []},
- {update, snmpa_supervisor, soft, soft_purge, soft_purge, []},
-
- {load_module, snmp_generic_mnesia, soft_purge, soft_purge, []},
- {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge,
- [snmp_conf]},
- {load_module, snmpa_vacm, soft_purge, soft_purge, []},
- {update, snmpa_local_db, soft, soft_purge, soft_purge, []},
- {update, snmpa_mib, soft, soft_purge, soft_purge, []},
- {update, snmpa_agent, soft, soft_purge, soft_purge, []},
-
- {remove, {snmpm_net_if_mt, soft_purge, soft_purge}}
- ]
- },
- {"4.21.3",
- [
- {load_module, snmp_config, soft_purge, soft_purge, []},
- {load_module, snmp_conf, soft_purge, soft_purge, []},
- {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_framework_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_notification_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_standard_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_user_based_sm_mib, soft_purge, soft_purge, [snmp_conf]},
-
- {load_module, snmp, soft_purge, soft_purge, [snmp_log]},
- {load_module, snmpm, soft_purge, soft_purge, [snmp]},
- {load_module, snmp_log, soft_purge, soft_purge, []},
- {load_module, snmp_verbosity, soft_purge, soft_purge, []},
- {load_module, snmpm_mpd, soft_purge, soft_purge, []},
-
- {load_module, snmpa, soft_purge, soft_purge, [snmp]},
- {load_module, snmp_target_mib, soft_purge, soft_purge,
- [snmp_conf, snmpa_mib_lib]},
- {load_module, snmpa_mib_lib, soft_purge, soft_purge, []},
- {load_module, snmpa_trap, soft_purge, soft_purge, []},
- {update, snmpa_supervisor, soft, soft_purge, soft_purge, []},
-
- {load_module, snmp_generic_mnesia, soft_purge, soft_purge, []},
- {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge,
- [snmp_conf]},
- {load_module, snmpa_vacm, soft_purge, soft_purge, []},
- {update, snmpa_local_db, soft, soft_purge, soft_purge, []},
- {update, snmpa_mib, soft, soft_purge, soft_purge, []},
- {update, snmpa_agent, soft, soft_purge, soft_purge, []},
-
- {remove, {snmpm_net_if_mt, soft_purge, soft_purge}}
- ]
- },
- {"4.21.2",
- [
- {load_module, snmp_config, soft_purge, soft_purge, []},
- {load_module, snmp_conf, soft_purge, soft_purge, []},
- {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_framework_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_notification_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_standard_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_user_based_sm_mib, soft_purge, soft_purge, [snmp_conf]},
-
- {load_module, snmp, soft_purge, soft_purge, [snmp_log]},
- {load_module, snmpm, soft_purge, soft_purge, [snmp]},
- {load_module, snmp_log, soft_purge, soft_purge, []},
- {load_module, snmp_verbosity, soft_purge, soft_purge, []},
- {load_module, snmpm_mpd, soft_purge, soft_purge, []},
-
- {load_module, snmpa, soft_purge, soft_purge, [snmp]},
- {load_module, snmp_target_mib, soft_purge, soft_purge,
- [snmp_conf, snmpa_mib_lib]},
- {load_module, snmpa_mib_lib, soft_purge, soft_purge, []},
- {update, snmpa_supervisor, soft, soft_purge, soft_purge, []},
-
- {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge,
- [snmp_conf]},
- {load_module, snmpa_vacm, soft_purge, soft_purge, []},
- {load_module, snmpa_mpd, soft_purge, soft_purge, []},
- {load_module, snmpa_set_lib, soft_purge, soft_purge, []},
- {load_module, snmpa_trap, soft_purge, soft_purge, []},
- {load_module, snmp_generic_mnesia, soft_purge, soft_purge, []},
- {update, snmpa_local_db, soft, soft_purge, soft_purge, []},
- {update, snmpa_mib, soft, soft_purge, soft_purge, []},
- {update, snmpa_agent, soft, soft_purge, soft_purge, []},
-
- {remove, {snmpm_net_if_mt, soft_purge, soft_purge}}
- ]
- },
- {"4.21.1",
- [
- {load_module, snmp_config, soft_purge, soft_purge, []},
- {load_module, snmp_conf, soft_purge, soft_purge, []},
- {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_framework_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_notification_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_standard_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_user_based_sm_mib, soft_purge, soft_purge, [snmp_conf]},
-
- {load_module, snmp, soft_purge, soft_purge, [snmp_log]},
- {load_module, snmpm, soft_purge, soft_purge, [snmp]},
- {load_module, snmp_log, soft_purge, soft_purge, []},
- {load_module, snmp_verbosity, soft_purge, soft_purge, []},
- {load_module, snmpm_mpd, soft_purge, soft_purge, []},
-
- {load_module, snmpa, soft_purge, soft_purge, [snmp]},
- {load_module, snmp_target_mib, soft_purge, soft_purge,
- [snmp_conf, snmpa_mib_lib]},
- {load_module, snmpa_mib_lib, soft_purge, soft_purge, []},
- {update, snmpa_supervisor, soft, soft_purge, soft_purge, []},
-
- {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge,
- [snmp_conf]},
- {load_module, snmpa_vacm, soft_purge, soft_purge, []},
- {load_module, snmpa_mpd, soft_purge, soft_purge, []},
- {load_module, snmpa_set_lib, soft_purge, soft_purge, []},
- {load_module, snmpa_trap, soft_purge, soft_purge, []},
- {load_module, snmp_generic_mnesia, soft_purge, soft_purge, []},
- {update, snmpa_local_db, soft, soft_purge, soft_purge, []},
- {update, snmpa_mib, soft, soft_purge, soft_purge, []},
- {update, snmpa_agent, soft, soft_purge, soft_purge, []},
- {update, snmp_note_store, soft, soft_purge, soft_purge, []},
-
- {remove, {snmpm_net_if_mt, soft_purge, soft_purge}}
- ]
- },
- {"4.21",
- [
- {load_module, snmp_config, soft_purge, soft_purge, []},
- {load_module, snmp_conf, soft_purge, soft_purge, []},
- {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_framework_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_notification_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_standard_mib, soft_purge, soft_purge, [snmp_conf]},
- {load_module, snmp_user_based_sm_mib, soft_purge, soft_purge, [snmp_conf]},
-
- {load_module, snmp, soft_purge, soft_purge, [snmp_log]},
- {load_module, snmpm, soft_purge, soft_purge, [snmp]},
- {load_module, snmp_log, soft_purge, soft_purge, []},
- {load_module, snmp_verbosity, soft_purge, soft_purge, []},
- {load_module, snmpm_mpd, soft_purge, soft_purge, []},
-
- {load_module, snmpa, soft_purge, soft_purge, [snmp]},
- {load_module, snmpa_mib_lib, soft_purge, soft_purge, []},
- {update, snmpa_supervisor, soft, soft_purge, soft_purge, []},
-
- {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge,
- [snmp_conf]},
- {load_module, snmpa_vacm, soft_purge, soft_purge, []},
- {load_module, snmpa_mpd, soft_purge, soft_purge, []},
- {load_module, snmpa_set_lib, soft_purge, soft_purge, []},
- {load_module, snmpa_trap, soft_purge, soft_purge, []},
- {load_module, snmp_target_mib, soft_purge, soft_purge,
- [snmp_conf, snmpa_mib_lib]},
- {load_module, snmp_generic_mnesia, soft_purge, soft_purge, []},
- {update, snmpa_local_db, soft, soft_purge, soft_purge, []},
- {update, snmpa_mib, soft, soft_purge, soft_purge, []},
- {update, snmpa_agent, soft, soft_purge, soft_purge, []},
- {update, snmp_note_store, soft, soft_purge, soft_purge, []},
-
- {remove, {snmpm_net_if_mt, soft_purge, soft_purge}}
- ]
- }
]
+
}.
diff --git a/lib/snmp/src/compile/Makefile b/lib/snmp/src/compile/Makefile
index 9658ee0005..f63fc18997 100644
--- a/lib/snmp/src/compile/Makefile
+++ b/lib/snmp/src/compile/Makefile
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/snmp/src/compile/depend.mk b/lib/snmp/src/compile/depend.mk
index 3ee8dc4bec..e6ba1ac810 100644
--- a/lib/snmp/src/compile/depend.mk
+++ b/lib/snmp/src/compile/depend.mk
@@ -45,5 +45,5 @@ $(EBIN)/snmpc_mib_gram.$(EMULATOR): \
snmpc_mib_gram.erl
$(BIN)/snmpc: snmpc.src ../../vsn.mk
- $(PERL) -p -e 's?%VSN%?$(VSN)? ' < $< > $@
- chmod 755 $@
+ $(vsn_verbose)$(PERL) -p -e 's?%VSN%?$(VSN)? ' < $< > $@
+ $(V_at)chmod 755 $@
diff --git a/lib/snmp/src/manager/Makefile b/lib/snmp/src/manager/Makefile
index 1f30669f02..c76dbacf90 100644
--- a/lib/snmp/src/manager/Makefile
+++ b/lib/snmp/src/manager/Makefile
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2004-2009. All Rights Reserved.
+# Copyright Ericsson AB 2004-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/snmp/src/manager/snmpm.erl b/lib/snmp/src/manager/snmpm.erl
index 89eaee9f80..6ac0115dad 100644
--- a/lib/snmp/src/manager/snmpm.erl
+++ b/lib/snmp/src/manager/snmpm.erl
@@ -107,63 +107,9 @@
async_get_bulk/5, async_get_bulk/6, async_get_bulk/7, async_get_bulk/8
]).
-%% Backward compatibility exports (API version "1")
--deprecated({agent_info, 3}).
--deprecated({update_agent_info, 5}).
--deprecated({g, 3}).
--deprecated({g, 4}).
--deprecated({g, 5}).
--deprecated({g, 6}).
--deprecated({g, 7}).
--deprecated({ag, 3}).
--deprecated({ag, 4}).
--deprecated({ag, 5}).
--deprecated({ag, 6}).
--deprecated({ag, 7}).
--deprecated({gn, 3}).
--deprecated({gn, 4}).
--deprecated({gn, 5}).
--deprecated({gn, 6}).
--deprecated({gn, 7}).
--deprecated({agn, 3}).
--deprecated({agn, 4}).
--deprecated({agn, 5}).
--deprecated({agn, 6}).
--deprecated({agn, 7}).
--deprecated({gb, 5}).
--deprecated({gb, 6}).
--deprecated({gb, 7}).
--deprecated({gb, 8}).
--deprecated({gb, 9}).
--deprecated({agb, 5}).
--deprecated({agb, 6}).
--deprecated({agb, 7}).
--deprecated({agb, 8}).
--deprecated({agb, 9}).
--deprecated({s, 3}).
--deprecated({s, 4}).
--deprecated({s, 5}).
--deprecated({s, 6}).
--deprecated({s, 7}).
--deprecated({as, 3}).
--deprecated({as, 4}).
--deprecated({as, 5}).
--deprecated({as, 6}).
--deprecated({as, 7}).
--export([
- agent_info/3, update_agent_info/5,
- g/3, g/4, g/5, g/6, g/7,
- ag/3, ag/4, ag/5, ag/6, ag/7,
- gn/3, gn/4, gn/5, gn/6, gn/7,
- agn/3, agn/4, agn/5, agn/6, agn/7,
- gb/5, gb/6, gb/7, gb/8, gb/9,
- agb/5, agb/6, agb/7, agb/8, agb/9,
- s/3, s/4, s/5, s/6, s/7,
- as/3, as/4, as/5, as/6, as/7
- ]).
-
%% Application internal export
-export([start_link/3, snmpm_start_verify/2, snmpm_start_verify/3]).
+-export([target_name/1, target_name/2]).
-include_lib("snmp/src/misc/snmp_debug.hrl").
@@ -439,32 +385,16 @@ unregister_agent(UserId, Addr, Port) ->
Error
end.
+
agent_info(TargetName, Item) ->
snmpm_config:agent_info(TargetName, Item).
-%% Backward compatibility
-agent_info(Addr, Port, Item) ->
- case target_name(Addr, Port) of
- {ok, TargetName} ->
- agent_info(TargetName, Item);
- Error ->
- Error
- end.
-
update_agent_info(UserId, TargetName, Info) when is_list(Info) ->
snmpm_config:update_agent_info(UserId, TargetName, Info).
update_agent_info(UserId, TargetName, Item, Val) ->
update_agent_info(UserId, TargetName, [{Item, Val}]).
-%% Backward compatibility functions
-update_agent_info(UserId, Addr, Port, Item, Val) ->
- case target_name(Addr, Port) of
- {ok, TargetName} ->
- update_agent_info(UserId, TargetName, Item, Val);
- Error ->
- Error
- end.
which_agents() ->
snmpm_config:which_agents().
@@ -552,55 +482,6 @@ sync_get(UserId, TargetName, Context, Oids, Timeout, ExtraInfo) ->
%% </BACKWARD-COMPAT>
-%% <DEPRECATED>
-g(UserId, Addr, Oids) ->
- g(UserId, Addr, ?DEFAULT_AGENT_PORT, Oids).
-
-g(UserId, Addr, CtxName, Oids) when is_list(CtxName) andalso is_list(Oids) ->
- g(UserId, Addr, ?DEFAULT_AGENT_PORT, CtxName, Oids);
-
-g(UserId, Addr, Port, Oids) when is_integer(Port) andalso is_list(Oids) ->
- g(UserId, Addr, Port, ?DEFAULT_CONTEXT, Oids);
-
-g(UserId, Addr, Oids, Timeout)
- when is_list(Oids) andalso is_integer(Timeout) ->
- g(UserId, Addr, ?DEFAULT_AGENT_PORT, Oids, Timeout).
-
-g(UserId, Addr, Port, CtxName, Oids)
- when is_integer(Port) andalso is_list(CtxName) andalso is_list(Oids) ->
- case target_name(Addr, Port) of
- {ok, TargetName} ->
- sync_get(UserId, TargetName, CtxName, Oids);
- Error ->
- Error
- end;
-
-g(UserId, Addr, Port, Oids, Timeout)
- when is_integer(Port) andalso is_list(Oids) andalso is_integer(Timeout) ->
- g(UserId, Addr, Port, ?DEFAULT_CONTEXT, Oids, Timeout);
-
-g(UserId, Addr, CtxName, Oids, Timeout)
- when is_list(CtxName) andalso is_list(Oids) andalso is_integer(Timeout) ->
- g(UserId, Addr, ?DEFAULT_AGENT_PORT, CtxName, Oids, Timeout).
-
-g(UserId, Addr, Port, CtxName, Oids, Timeout) ->
- case target_name(Addr, Port) of
- {ok, TargetName} ->
- sync_get(UserId, TargetName, CtxName, Oids, Timeout);
- Error ->
- Error
- end.
-
-g(UserId, Addr, Port, CtxName, Oids, Timeout, ExtraInfo) ->
- case target_name(Addr, Port) of
- {ok, TargetName} ->
- sync_get(UserId, TargetName, CtxName, Oids, Timeout, ExtraInfo);
- Error ->
- Error
- end.
-%% </DEPRECATED>
-
-
%% --- asynchroneous get-request ---
%%
@@ -637,55 +518,6 @@ async_get(UserId, TargetName, Context, Oids, Expire, ExtraInfo) ->
%% </BACKWARD-COMPAT>
-%% <DEPRECATED>
-ag(UserId, Addr, Oids) ->
- ag(UserId, Addr, ?DEFAULT_AGENT_PORT, Oids).
-
-ag(UserId, Addr, Port, Oids) when is_integer(Port) andalso is_list(Oids) ->
- ag(UserId, Addr, Port, ?DEFAULT_CONTEXT, Oids);
-
-ag(UserId, Addr, CtxName, Oids) when is_list(CtxName) andalso is_list(Oids) ->
- ag(UserId, Addr, ?DEFAULT_AGENT_PORT, CtxName, Oids);
-
-ag(UserId, Addr, Oids, Expire) when is_list(Oids) andalso is_integer(Expire) ->
- ag(UserId, Addr, ?DEFAULT_AGENT_PORT, ?DEFAULT_CONTEXT, Oids, Expire).
-
-ag(UserId, Addr, Port, CtxName, Oids)
- when is_integer(Port) andalso is_list(CtxName) andalso is_list(Oids) ->
- case target_name(Addr, Port) of
- {ok, TargetName} ->
- async_get(UserId, TargetName, CtxName, Oids);
- Error ->
- Error
- end;
-
-ag(UserId, Addr, Port, Oids, Expire)
- when is_integer(Port) andalso is_list(Oids) andalso is_integer(Expire) ->
- ag(UserId, Addr, Port, ?DEFAULT_CONTEXT, Oids, Expire);
-
-ag(UserId, Addr, CtxName, Oids, Expire)
- when is_list(CtxName) andalso is_list(Oids) andalso is_integer(Expire) ->
- ag(UserId, Addr, ?DEFAULT_AGENT_PORT, CtxName, Oids, Expire).
-
-ag(UserId, Addr, Port, CtxName, Oids, Expire) ->
- case target_name(Addr, Port) of
- {ok, TargetName} ->
- async_get(UserId, TargetName, CtxName, Oids, Expire);
- Error ->
- Error
- end.
-
-ag(UserId, Addr, Port, CtxName, Oids, Expire, ExtraInfo) ->
- case target_name(Addr, Port) of
- {ok, TargetName} ->
- async_get(UserId, TargetName, CtxName, Oids, Expire, ExtraInfo);
- Error ->
- Error
- end.
-%% </DEPRECATED>
-
-
-
%% --- synchroneous get_next-request ---
%%
@@ -719,55 +551,6 @@ sync_get_next(UserId, TargetName, Context, Oids, Timeout, ExtraInfo) ->
%% </BACKWARD-COMPAT>
-%% <DEPRECATED>
-gn(UserId, Addr, Oids) ->
- gn(UserId, Addr, ?DEFAULT_AGENT_PORT, Oids).
-
-gn(UserId, Addr, CtxName, Oids) when is_list(CtxName) andalso is_list(Oids) ->
- gn(UserId, Addr, ?DEFAULT_AGENT_PORT, CtxName, Oids);
-
-gn(UserId, Addr, Port, Oids) when is_integer(Port) andalso is_list(Oids) ->
- gn(UserId, Addr, Port, ?DEFAULT_CONTEXT, Oids);
-
-gn(UserId, Addr, Oids, Timeout)
- when is_list(Oids) andalso is_integer(Timeout) ->
- gn(UserId, Addr, ?DEFAULT_AGENT_PORT, Oids, Timeout).
-
-gn(UserId, Addr, Port, CtxName, Oids)
- when is_integer(Port) andalso is_list(CtxName) andalso is_list(Oids) ->
- case target_name(Addr, Port) of
- {ok, TargetName} ->
- sync_get_next(UserId, TargetName, CtxName, Oids);
- Error ->
- Error
- end;
-
-gn(UserId, Addr, Port, Oids, Timeout)
- when is_integer(Port) andalso is_list(Oids) andalso is_integer(Timeout) ->
- gn(UserId, Addr, Port, ?DEFAULT_CONTEXT, Oids, Timeout);
-gn(UserId, Addr, CtxName, Oids, Timeout)
- when is_list(CtxName) andalso is_list(Oids) andalso is_integer(Timeout) ->
- gn(UserId, Addr, ?DEFAULT_AGENT_PORT, CtxName, Oids, Timeout).
-
-gn(UserId, Addr, Port, CtxName, Oids, Timeout) ->
- case target_name(Addr, Port) of
- {ok, TargetName} ->
- sync_get_next(UserId, TargetName, CtxName, Oids, Timeout);
- Error ->
- Error
- end.
-
-gn(UserId, Addr, Port, CtxName, Oids, Timeout, ExtraInfo) ->
- case target_name(Addr, Port) of
- {ok, TargetName} ->
- sync_get_next(UserId, TargetName, CtxName, Oids, Timeout, ExtraInfo);
- Error ->
- Error
- end.
-%% </DEPRECATED>
-
-
-
%% --- asynchroneous get_next-request ---
%%
@@ -801,56 +584,6 @@ async_get_next(UserId, TargetName, Context, Oids, Expire, ExtraInfo) ->
%% </BACKWARD-COMPAT>
-%% <DEPRECATED>
-agn(UserId, Addr, Oids) ->
- agn(UserId, Addr, ?DEFAULT_AGENT_PORT, Oids).
-
-agn(UserId, Addr, CtxName, Oids) when is_list(CtxName) andalso is_list(Oids) ->
- agn(UserId, Addr, ?DEFAULT_AGENT_PORT, CtxName, Oids);
-
-agn(UserId, Addr, Port, Oids) when is_integer(Port) andalso is_list(Oids) ->
- agn(UserId, Addr, Port, ?DEFAULT_CONTEXT, Oids);
-
-agn(UserId, Addr, Oids, Expire)
- when is_list(Oids) andalso is_integer(Expire) ->
- agn(UserId, Addr, ?DEFAULT_AGENT_PORT, Oids, Expire).
-
-agn(UserId, Addr, Port, CtxName, Oids)
- when is_integer(Port) andalso is_list(CtxName) andalso is_list(Oids) ->
- case target_name(Addr, Port) of
- {ok, TargetName} ->
- async_get_next(UserId, TargetName, CtxName, Oids);
- Error ->
- Error
- end;
-
-agn(UserId, Addr, Port, Oids, Expire)
- when is_integer(Port) andalso is_list(Oids) andalso is_integer(Expire) ->
- agn(UserId, Addr, Port, ?DEFAULT_CONTEXT, Oids, Expire);
-agn(UserId, Addr, CtxName, Oids, Expire)
- when is_list(CtxName) andalso is_list(CtxName) andalso is_integer(Expire) ->
- agn(UserId, Addr, ?DEFAULT_AGENT_PORT, CtxName, Oids, Expire).
-
-agn(UserId, Addr, Port, CtxName, Oids, Expire) ->
- case target_name(Addr, Port) of
- {ok, TargetName} ->
- async_get_next(UserId, TargetName, CtxName, Oids, Expire);
- Error ->
- Error
- end.
-
-agn(UserId, Addr, Port, CtxName, Oids, Expire, ExtraInfo) ->
- case target_name(Addr, Port) of
- {ok, TargetName} ->
- async_get_next(UserId, TargetName, CtxName, Oids, Expire,
- ExtraInfo);
- Error ->
- Error
- end.
-%% </DEPRECATED>
-
-
-
%% --- synchroneous set-request ---
%%
@@ -884,64 +617,6 @@ sync_set(UserId, TargetName, Context, VarsAndVals, Timeout, ExtraInfo) ->
%% </BACKWARD-COMPAT>
-%% <DEPRECATED>
-s(UserId, Addr, VarsAndVals) ->
- s(UserId, Addr, ?DEFAULT_AGENT_PORT, VarsAndVals).
-
-s(UserId, Addr, Port, VarsAndVals)
- when is_integer(Port) andalso is_list(VarsAndVals) ->
- s(UserId, Addr, Port, ?DEFAULT_CONTEXT, VarsAndVals);
-
-s(UserId, Addr, CtxName, VarsAndVals)
- when is_list(CtxName) andalso is_list(VarsAndVals) ->
- s(UserId, Addr, ?DEFAULT_AGENT_PORT, CtxName, VarsAndVals);
-
-s(UserId, Addr, VarsAndVals, Timeout)
- when is_list(VarsAndVals) andalso is_integer(Timeout) ->
- s(UserId, Addr, ?DEFAULT_AGENT_PORT, VarsAndVals, Timeout).
-
-s(UserId, Addr, Port, CtxName, VarsAndVals)
- when is_integer(Port) andalso
- is_list(CtxName) andalso
- is_list(VarsAndVals) ->
- case target_name(Addr, Port) of
- {ok, TargetName} ->
- sync_set(UserId, TargetName, CtxName, VarsAndVals);
- Error ->
- Error
- end;
-
-s(UserId, Addr, Port, VarsAndVals, Timeout)
- when is_integer(Port) andalso
- is_list(VarsAndVals) andalso
- is_integer(Timeout) ->
- s(UserId, Addr, Port, ?DEFAULT_CONTEXT, VarsAndVals, Timeout);
-
-s(UserId, Addr, CtxName, VarsAndVals, Timeout)
- when is_list(CtxName) andalso
- is_list(VarsAndVals) andalso
- is_integer(Timeout) ->
- s(UserId, Addr, ?DEFAULT_AGENT_PORT, CtxName, VarsAndVals, Timeout).
-
-s(UserId, Addr, Port, CtxName, VarsAndVals, Timeout) ->
- case target_name(Addr, Port) of
- {ok, TargetName} ->
- sync_set(UserId, TargetName, CtxName, VarsAndVals, Timeout);
- Error ->
- Error
- end.
-
-s(UserId, Addr, Port, CtxName, VarsAndVals, Timeout, ExtraInfo) ->
- case target_name(Addr, Port) of
- {ok, TargetName} ->
- sync_set(UserId, TargetName, CtxName, VarsAndVals, Timeout, ExtraInfo);
- Error ->
- Error
- end.
-%% </DEPRECATED>
-
-
-
%% --- asynchroneous set-request ---
%%
@@ -975,63 +650,6 @@ async_set(UserId, TargetName, Context, VarsAndVals, Expire, ExtraInfo) ->
%% </BACKWARD-COMPAT>
-%% <DEPRECATED>
-as(UserId, Addr, VarsAndVals) ->
- as(UserId, Addr, ?DEFAULT_AGENT_PORT, VarsAndVals).
-
-as(UserId, Addr, Port, VarsAndVals)
- when is_integer(Port) andalso is_list(VarsAndVals) ->
- as(UserId, Addr, Port, ?DEFAULT_CONTEXT, VarsAndVals);
-
-as(UserId, Addr, CtxName, VarsAndVals)
- when is_list(CtxName) andalso is_list(VarsAndVals) ->
- as(UserId, Addr, ?DEFAULT_AGENT_PORT, CtxName, VarsAndVals);
-
-as(UserId, Addr, VarsAndVals, Expire)
- when is_list(VarsAndVals) andalso is_integer(Expire) ->
- as(UserId, Addr, ?DEFAULT_AGENT_PORT, VarsAndVals, Expire).
-
-as(UserId, Addr, Port, CtxName, VarsAndVals)
- when is_integer(Port) andalso
- is_list(CtxName) andalso
- is_list(VarsAndVals) ->
- case target_name(Addr, Port) of
- {ok, TargetName} ->
- async_set(UserId, TargetName, CtxName, VarsAndVals);
- Error ->
- Error
- end;
-
-as(UserId, Addr, Port, VarsAndVals, Expire)
- when is_integer(Port) andalso
- is_list(VarsAndVals) andalso
- is_integer(Expire) ->
- as(UserId, Addr, Port, ?DEFAULT_CONTEXT, VarsAndVals, Expire);
-
-as(UserId, Addr, CtxName, VarsAndVals, Expire)
- when is_list(CtxName) andalso
- is_list(VarsAndVals) andalso
- is_integer(Expire) ->
- as(UserId, Addr, ?DEFAULT_AGENT_PORT, CtxName, VarsAndVals, Expire).
-
-as(UserId, Addr, Port, CtxName, VarsAndVals, Expire) ->
- case target_name(Addr, Port) of
- {ok, TargetName} ->
- async_set(UserId, TargetName, CtxName, VarsAndVals, Expire);
- Error ->
- Error
- end.
-
-as(UserId, Addr, Port, CtxName, VarsAndVals, Expire, ExtraInfo) ->
- case target_name(Addr, Port) of
- {ok, TargetName} ->
- async_set(UserId, TargetName, CtxName, VarsAndVals, Expire, ExtraInfo);
- Error ->
- Error
- end.
-%% </DEPRECATED>
-
-
%% --- synchroneous get-bulk ---
%%
@@ -1091,162 +709,6 @@ sync_get_bulk(UserId, TargetName, NonRep, MaxRep, Context, Oids, Timeout,
%% </BACKWARD-COMPAT>
-%% <DEPRECATED>
-gb(UserId, Addr, NonRep, MaxRep, Oids) ->
- %% p("gb -> entry with"
- %% "~n UserId: ~p"
- %% "~n Addr: ~p"
- %% "~n NonRep: ~p"
- %% "~n MaxRep: ~p"
- %% "~n Oids: ~p",
- %% [UserId, Addr, NonRep, MaxRep, Oids]),
- gb(UserId, Addr, ?DEFAULT_AGENT_PORT, NonRep, MaxRep, Oids).
-
-gb(UserId, Addr, Port, NonRep, MaxRep, Oids)
- when is_integer(Port) andalso
- is_integer(NonRep) andalso
- is_integer(MaxRep) andalso
- is_list(Oids) ->
- %% p("gb -> entry with"
- %% "~n UserId: ~p"
- %% "~n Addr: ~p"
- %% "~n Port: ~p"
- %% "~n NonRep: ~p"
- %% "~n MaxRep: ~p"
- %% "~n Oids: ~p",
- %% [UserId, Addr, Port, NonRep, MaxRep, Oids]),
- gb(UserId, Addr, Port, NonRep, MaxRep, ?DEFAULT_CONTEXT, Oids);
-
-gb(UserId, Addr, NonRep, MaxRep, CtxName, Oids)
- when is_integer(NonRep) andalso
- is_integer(MaxRep) andalso
- is_list(CtxName) andalso
- is_list(Oids) ->
- %% p("gb -> entry with"
- %% "~n UserId: ~p"
- %% "~n Addr: ~p"
- %% "~n NonRep: ~p"
- %% "~n MaxRep: ~p"
- %% "~n CtxName: ~p"
- %% "~n Oids: ~p",
- %% [UserId, Addr, NonRep, MaxRep, CtxName, Oids]),
- gb(UserId, Addr, ?DEFAULT_AGENT_PORT, NonRep, MaxRep, CtxName, Oids);
-
-gb(UserId, Addr, NonRep, MaxRep, Oids, Timeout)
- when is_integer(NonRep) andalso
- is_integer(MaxRep) andalso
- is_list(Oids) andalso
- is_integer(Timeout) ->
- %% p("gb -> entry with"
- %% "~n UserId: ~p"
- %% "~n Addr: ~p"
- %% "~n NonRep: ~p"
- %% "~n MaxRep: ~p"
- %% "~n Oids: ~p"
- %% "~n Timeout: ~p",
- %% [UserId, Addr, NonRep, MaxRep, Oids, Timeout]),
- gb(UserId, Addr, ?DEFAULT_AGENT_PORT, NonRep, MaxRep, Oids, Timeout).
-
-gb(UserId, Addr, Port, NonRep, MaxRep, CtxName, Oids)
- when is_integer(Port) andalso
- is_integer(NonRep) andalso
- is_integer(MaxRep) andalso
- is_list(CtxName) andalso
- is_list(Oids) ->
- %% p("gb -> entry with"
- %% "~n UserId: ~p"
- %% "~n Addr: ~p"
- %% "~n Port: ~p"
- %% "~n NonRep: ~p"
- %% "~n MaxRep: ~p"
- %% "~n CtxName: ~p"
- %% "~n Oids: ~p",
- %% [UserId, Addr, Port, NonRep, MaxRep, CtxName, Oids]),
- case target_name(Addr, Port) of
- {ok, TargetName} ->
- %% p("gb -> TargetName: ~p", [TargetName]),
- sync_get_bulk(UserId, TargetName, NonRep, MaxRep, CtxName, Oids);
- Error ->
- Error
- end;
-
-gb(UserId, Addr, Port, NonRep, MaxRep, Oids, Timeout)
- when is_integer(Port) andalso
- is_integer(NonRep) andalso
- is_integer(MaxRep) andalso
- is_list(Oids) andalso
- is_integer(Timeout) ->
- %% p("gb -> entry with"
- %% "~n UserId: ~p"
- %% "~n Addr: ~p"
- %% "~n Port: ~p"
- %% "~n NonRep: ~p"
- %% "~n MaxRep: ~p"
- %% "~n Oids: ~p"
- %% "~n Timeout: ~p",
- %% [UserId, Addr, Port, NonRep, MaxRep, Oids, Timeout]),
- gb(UserId, Addr, Port, NonRep, MaxRep, ?DEFAULT_CONTEXT, Oids, Timeout);
-
-gb(UserId, Addr, NonRep, MaxRep, CtxName, Oids, Timeout)
- when is_integer(NonRep) andalso
- is_integer(MaxRep) andalso
- is_list(CtxName) andalso
- is_list(Oids) andalso
- is_integer(Timeout) ->
- %% p("gb -> entry with"
- %% "~n UserId: ~p"
- %% "~n Addr: ~p"
- %% "~n NonRep: ~p"
- %% "~n MaxRep: ~p"
- %% "~n CtxName: ~p"
- %% "~n Oids: ~p"
- %% "~n Timeout: ~p",
- %% [UserId, Addr, NonRep, MaxRep, CtxName, Oids, Timeout]),
- gb(UserId, Addr, ?DEFAULT_AGENT_PORT, NonRep, MaxRep, CtxName, Oids,
- Timeout).
-
-gb(UserId, Addr, Port, NonRep, MaxRep, CtxName, Oids, Timeout) ->
- %% p("gb -> entry with"
- %% "~n UserId: ~p"
- %% "~n Addr: ~p"
- %% "~n Port: ~p"
- %% "~n NonRep: ~p"
- %% "~n MaxRep: ~p"
- %% "~n CtxName: ~p"
- %% "~n Oids: ~p"
- %% "~n Timeout: ~p",
- %% [UserId, Addr, Port, NonRep, MaxRep, CtxName, Oids, Timeout]),
- case target_name(Addr, Port) of
- {ok, TargetName} ->
- sync_get_bulk(UserId, TargetName,
- NonRep, MaxRep, CtxName, Oids, Timeout);
- Error ->
- Error
- end.
-
-gb(UserId, Addr, Port, NonRep, MaxRep, CtxName, Oids, Timeout, ExtraInfo) ->
- %% p("gb -> entry with"
- %% "~n UserId: ~p"
- %% "~n Addr: ~p"
- %% "~n Port: ~p"
- %% "~n NonRep: ~p"
- %% "~n MaxRep: ~p"
- %% "~n CtxName: ~p"
- %% "~n Oids: ~p"
- %% "~n Timeout: ~p"
- %% "~n ExtraInfo: ~p",
- %% [UserId, Addr, Port, NonRep, MaxRep, CtxName, Oids, Timeout, ExtraInfo]),
- case target_name(Addr, Port) of
- {ok, TargetName} ->
- sync_get_bulk(UserId, TargetName,
- NonRep, MaxRep, CtxName, Oids, Timeout, ExtraInfo);
- Error ->
- Error
- end.
-%% </DEPRECATED>
-
-
-
%% --- asynchroneous get-bulk ---
%%
@@ -1291,81 +753,6 @@ async_get_bulk(UserId, TargetName, NonRep, MaxRep, Context, Oids, Expire,
%% </BACKWARD-COMPAT>
-%% <DEPRECATED>
-agb(UserId, Addr, NonRep, MaxRep, Oids) ->
- agb(UserId, Addr, ?DEFAULT_AGENT_PORT, NonRep, MaxRep, Oids).
-
-agb(UserId, Addr, Port, NonRep, MaxRep, Oids)
- when is_integer(Port) andalso
- is_integer(NonRep) andalso
- is_integer(MaxRep) andalso
- is_list(Oids) ->
- agb(UserId, Addr, Port, NonRep, MaxRep, ?DEFAULT_CONTEXT, Oids);
-
-agb(UserId, Addr, NonRep, MaxRep, CtxName, Oids)
- when is_integer(NonRep) andalso
- is_integer(MaxRep) andalso
- is_list(CtxName) andalso
- is_list(Oids) ->
- agb(UserId, Addr, ?DEFAULT_AGENT_PORT, NonRep, MaxRep, CtxName, Oids);
-
-agb(UserId, Addr, NonRep, MaxRep, Oids, Expire)
- when is_integer(NonRep) andalso
- is_integer(MaxRep) andalso
- is_list(Oids) andalso
- is_integer(Expire) ->
- agb(UserId, Addr, ?DEFAULT_AGENT_PORT, NonRep, MaxRep, Oids, Expire).
-
-agb(UserId, Addr, Port, NonRep, MaxRep, CtxName, Oids)
- when is_integer(Port) andalso
- is_integer(NonRep) andalso
- is_integer(MaxRep),
- is_list(CtxName) andalso
- is_list(Oids) ->
- case target_name(Addr, Port) of
- {ok, TargetName} ->
- async_get_bulk(UserId, TargetName,
- NonRep, MaxRep, CtxName, Oids);
- Error ->
- Error
- end;
-
-agb(UserId, Addr, Port, NonRep, MaxRep, Oids, Expire)
- when is_integer(Port) andalso
- is_integer(NonRep) andalso
- is_integer(MaxRep) andalso
- is_list(Oids) andalso
- is_integer(Expire) ->
- agb(UserId, Addr, Port, NonRep, MaxRep, ?DEFAULT_CONTEXT, Oids, Expire);
-
-agb(UserId, Addr, NonRep, MaxRep, CtxName, Oids, Expire)
- when is_integer(NonRep) andalso
- is_integer(MaxRep) andalso
- is_list(CtxName) andalso
- is_list(Oids) andalso
- is_integer(Expire) ->
- agb(UserId, Addr, ?DEFAULT_AGENT_PORT, NonRep, MaxRep, CtxName, Oids).
-
-agb(UserId, Addr, Port, NonRep, MaxRep, CtxName, Oids, Expire) ->
- case target_name(Addr, Port) of
- {ok, TargetName} ->
- async_get_bulk(UserId, TargetName,
- NonRep, MaxRep, CtxName, Oids, Expire);
- Error ->
- Error
- end.
-
-agb(UserId, Addr, Port, NonRep, MaxRep, CtxName, Oids, Expire, ExtraInfo) ->
- case target_name(Addr, Port) of
- {ok, TargetName} ->
- async_get_bulk(UserId, TargetName,
- NonRep, MaxRep, CtxName, Oids, Expire,
- ExtraInfo);
- Error ->
- Error
- end.
-%% </DEPRECATED>
-
cancel_async_request(UserId, ReqId) ->
snmpm_server:cancel_async_request(UserId, ReqId).
@@ -1411,7 +798,7 @@ log_to_io(LogDir, Mibs, LogName, LogFile) ->
log_to_io(LogDir, Mibs, LogName, LogFile, Start) ->
snmp:log_to_io(LogDir, Mibs, LogName, LogFile, Start).
log_to_io(LogDir, Mibs, LogName, LogFile, Start, Stop) ->
- snmp:log_to_io(LogDir, Mibs, LogFile, Start, Stop).
+ snmp:log_to_io(LogDir, Mibs, LogName, LogFile, Start, Stop).
change_log_size(NewSize) ->
@@ -1457,7 +844,7 @@ sys_up_time() ->
format_reason(Reason) ->
format_reason("", Reason).
-format_reason(Prefix, Reason) when is_integer(Prefix) and (Prefix >= 0) ->
+format_reason(Prefix, Reason) when is_integer(Prefix) andalso (Prefix >= 0) ->
format_reason(lists:duplicate(Prefix, $ ), Reason);
format_reason(Prefix, Reason) when is_list(Prefix) ->
case (catch do_format_reason(Prefix, Reason)) of
@@ -1691,6 +1078,9 @@ format_vb_value(Prefix, _Type, Val) ->
%% --- Internal utility functions ---
%%
+target_name(Addr) ->
+ target_name(Addr, ?DEFAULT_AGENT_PORT).
+
target_name(Addr, Port) ->
snmpm_config:agent_info(Addr, Port, target_name).
diff --git a/lib/snmp/src/manager/snmpm_conf.erl b/lib/snmp/src/manager/snmpm_conf.erl
index e50508c489..03dbd028f7 100644
--- a/lib/snmp/src/manager/snmpm_conf.erl
+++ b/lib/snmp/src/manager/snmpm_conf.erl
@@ -1,7 +1,8 @@
+%% -*- coding: utf-8 -*-
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -365,7 +366,7 @@ do_write_usm_conf(Fd,
do_write_usm_conf(Fd,
{EngineID, UserName, SecName,
AuthP, AuthKey, PrivP, PrivKey}) ->
- io:format(Fd, "{\"~s\", \"~s\", \"~s\", �~w, ~w, ~w, ~w}.~n",
+ io:format(Fd, "{\"~s\", \"~s\", \"~s\", í~w, ~w, ~w, ~w}.~n",
[EngineID, UserName, SecName, AuthP, AuthKey, PrivP, PrivKey]);
do_write_usm_conf(_Fd, Crap) ->
error({bad_usm_conf, Crap}).
diff --git a/lib/snmp/src/manager/snmpm_mpd.erl b/lib/snmp/src/manager/snmpm_mpd.erl
index d7148bb4a4..953c94ab54 100644
--- a/lib/snmp/src/manager/snmpm_mpd.erl
+++ b/lib/snmp/src/manager/snmpm_mpd.erl
@@ -591,8 +591,8 @@ sec_engine_id(TargetName) ->
%% BMK BMK BMK
-%% Denna verkar v�ldigt lik generate_v1_v2c_response_msg!
-%% Gemensam? Borde det finnas olikheter?
+%% This one looks very similar to lik generate_v1_v2c_response_msg!
+%% Common/shared? Should there be differences?
%%
generate_v1_v2c_msg(Vsn, Pdu, Community, Log) ->
?vdebug("generate_v1_v2c_msg -> encode pdu", []),
diff --git a/lib/snmp/src/manager/snmpm_server.erl b/lib/snmp/src/manager/snmpm_server.erl
index 484954addb..61d22362cc 100644
--- a/lib/snmp/src/manager/snmpm_server.erl
+++ b/lib/snmp/src/manager/snmpm_server.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -3163,17 +3163,16 @@ request_id() ->
%%----------------------------------------------------------------------
agent_data(TargetName, SendOpts) ->
- case snmpm_config:agent_info(TargetName, all) of
- {ok, Info} ->
- Version = agent_data_item(version, Info),
+ case snmpm_config:agent_info(TargetName, version) of
+ {ok, Version} ->
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),
+ DefSecModel = agent_data_item(sec_model, TargetName),
+ DefSecName = agent_data_item(sec_name, TargetName),
+ DefSecLevel = agent_data_item(sec_level, TargetName),
- EngineId = agent_data_item(engine_id, Info),
+ EngineId = agent_data_item(engine_id, TargetName),
CtxName = agent_data_item(context,
SendOpts,
?DEFAULT_CONTEXT),
@@ -3191,8 +3190,8 @@ agent_data(TargetName, SendOpts) ->
{SecModel, SecName, mk_sec_level_flag(SecLevel),
EngineId, CtxName, TargetName};
_ ->
- DefComm = agent_data_item(community, Info),
- DefSecModel = agent_data_item(sec_model, Info),
+ DefComm = agent_data_item(community, TargetName),
+ DefSecModel = agent_data_item(sec_model, TargetName),
Comm = agent_data_item(community,
SendOpts,
@@ -3203,21 +3202,21 @@ agent_data(TargetName, SendOpts) ->
{Comm, SecModel}
end,
- Domain = agent_data_item(tdomain, Info),
- Addr = agent_data_item(address, Info),
- Port = agent_data_item(port, Info),
- RegType = agent_data_item(reg_type, Info),
+ Domain = agent_data_item(tdomain, TargetName),
+ Addr = agent_data_item(address, TargetName),
+ Port = agent_data_item(port, TargetName),
+ RegType = agent_data_item(reg_type, TargetName),
{ok, RegType, Domain, Addr, Port, version(Version), MsgData};
Error ->
Error
end.
-agent_data_item(Item, Info) ->
- case lists:keysearch(Item, 1, Info) of
- {value, {_, Val}} ->
+agent_data_item(Item, TargetName) ->
+ case snmpm_config:agent_info(TargetName, Item) of
+ {ok, Val} ->
Val;
- false ->
- throw({error, {not_found, Item, Info}})
+ {error, not_found} ->
+ throw({error, {not_found, Item, TargetName}})
end.
agent_data_item(Item, Info, Default) ->
diff --git a/lib/snmp/src/manager/snmpm_user_default.erl b/lib/snmp/src/manager/snmpm_user_default.erl
index d90fc3f258..015198cb76 100644
--- a/lib/snmp/src/manager/snmpm_user_default.erl
+++ b/lib/snmp/src/manager/snmpm_user_default.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -62,7 +62,7 @@ handle_trap(TargetName, SnmpTrap, UserData) ->
"~n SnmpTrap: ~p"
"~n UserData: ~p",
[TargetName, SnmpTrap, UserData]),
- ok.
+ ignore.
handle_inform(TargetName, SnmpInform, UserData) ->
@@ -80,7 +80,7 @@ handle_report(TargetName, SnmpReport, UserData) ->
"~n SnmpReport: ~p"
"~n UserData: ~p",
[TargetName, SnmpReport, UserData]),
- ok.
+ ignore.
info(F, A) ->
diff --git a/lib/snmp/src/misc/Makefile b/lib/snmp/src/misc/Makefile
index c6a5064d89..698c341268 100644
--- a/lib/snmp/src/misc/Makefile
+++ b/lib/snmp/src/misc/Makefile
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2003-2009. All Rights Reserved.
+# Copyright Ericsson AB 2003-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/snmp/src/misc/snmp_pdus.erl b/lib/snmp/src/misc/snmp_pdus.erl
index 0788d86b2d..15156f7467 100644
--- a/lib/snmp/src/misc/snmp_pdus.erl
+++ b/lib/snmp/src/misc/snmp_pdus.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -254,68 +254,95 @@ strip(0, _Tail) ->
%%----------------------------------------------------------------------
%% Returns:{Type, Value}
%%----------------------------------------------------------------------
+
+%% OBJECT IDENTIFIER
dec_value([6 | Bytes]) ->
{Value, Rest} = dec_oid_notag(Bytes),
{{'OBJECT IDENTIFIER', Value}, Rest};
dec_value([5,0 | T]) ->
{{'NULL', 'NULL'}, T};
+
+%% INTEGER
dec_value([2 | Bytes]) ->
{Value, Rest} = dec_integer_notag(Bytes),
{{'INTEGER', Value}, Rest};
+
+%% OCTET STRING
dec_value([4 | Bytes]) ->
{Value, Rest} = dec_oct_str_notag(Bytes),
{{'OCTET STRING', Value}, Rest};
+
+%% IpAddress
dec_value([64 | Bytes]) ->
{Value, Rest} = dec_oct_str_notag(Bytes),
{{'IpAddress', Value}, Rest};
+
+%% Counter32
dec_value([65 | Bytes]) ->
%% Counter32 is an unsigned 32 but is actually encoded as
%% a signed integer 32 (INTEGER).
{Value, Rest} = dec_integer_notag(Bytes),
Value2 =
- if
+ if
+ (Value >= 0) andalso (Value =< 16#ffffffff) ->
+ %% We accept value above 16#7fffffff
+ %% in order to be backward bug-compatible
+ Value;
+ (Value < 0) ->
+ 16#ffffffff + Value + 1;
+ true ->
+ exit({error, {bad_counter32, Value}})
+ end,
+ {{'Counter32', Value2}, Rest};
+
+%% Unsigned32
+dec_value([66 | Bytes]) ->
+ {Value, Rest} = dec_integer_notag(Bytes),
+ Value2 =
+ if
(Value >= 0) andalso (Value =< 16#ffffffff) ->
- %% We accept value above 16#7fffffff
- %% in order to be backward bug-compatible
Value;
(Value < 0) ->
16#ffffffff + Value + 1;
true ->
- exit({error, {bad_counter32, Value}})
+ exit({error, {bad_unsigned32, Value}})
end,
- {{'Counter32', Value2}, Rest};
-dec_value([66 | Bytes]) ->
- {Value, Rest} = dec_integer_notag(Bytes),
- if
- (Value >= 0) andalso (Value =< 4294967295) ->
- {{'Unsigned32', Value}, Rest};
- true ->
- exit({error, {bad_unsigned32, Value}})
- end;
+ {{'Unsigned32', Value2}, Rest};
+
+%% TimeTicks
dec_value([67 | Bytes]) ->
{Value, Rest} = dec_integer_notag(Bytes),
- if
- (Value >= 0) andalso (Value =< 4294967295) ->
- {{'TimeTicks', Value}, Rest};
+ Value2 =
+ if
+ (Value >= 0) andalso (Value =< 16#ffffffff) ->
+ Value;
+ (Value < 0) ->
+ 16#ffffffff + Value + 1;
true ->
exit({error, {bad_timeticks, Value}})
- end;
+ end,
+ {{'TimeTicks', Value2}, Rest};
+
+%% Opaque
dec_value([68 | Bytes]) ->
{Value, Rest} = dec_oct_str_notag(Bytes),
{{'Opaque', Value}, Rest};
+
+%% Counter64
dec_value([70 | Bytes]) ->
%% Counter64 is an unsigned 64 but is actually encoded as
%% a signed integer 64.
{Value, Rest} = dec_integer_notag(Bytes),
Value2 =
- if
- (Value >= 0) andalso (Value < 16#8000000000000000) ->
- Value;
- (Value < 0) ->
- 18446744073709551615 + Value + 1;
- true ->
- exit({error, {bad_counter64, Value}}) end,
+ if
+ (Value >= 0) andalso (Value < 16#8000000000000000) ->
+ Value;
+ (Value < 0) ->
+ 16#ffffffffffffffff + Value + 1;
+ true ->
+ exit({error, {bad_counter64, Value}}) end,
{{'Counter64', Value2}, Rest};
+
dec_value([128,0|T]) ->
{{'NULL', noSuchObject}, T};
dec_value([129,0|T]) ->
@@ -629,7 +656,7 @@ enc_VarBind_attributes(#varbind{oid = Oid, variabletype = Type,value = Val}) ->
ValueBytes = enc_value(Type, Val),
lists:append(OidBytes, ValueBytes).
-enc_value('INTEGER',Val) ->
+enc_value('INTEGER', Val) ->
enc_integer_tag(Val);
enc_value('OCTET STRING', Val) ->
enc_oct_str_tag(Val);
@@ -637,7 +664,7 @@ enc_value('BITS', Val) ->
enc_oct_str_tag(bits_to_str(Val));
enc_value('OBJECT IDENTIFIER', Val) ->
enc_oid_tag(Val);
-enc_value('IpAddress',Val) ->
+enc_value('IpAddress', Val) ->
Bytes2 = enc_oct_str_notag(Val),
Len2 = elength(length(Bytes2)),
lists:append([64 | Len2],Bytes2);
@@ -668,6 +695,24 @@ enc_value('Counter32', Val) ->
Bytes2 = enc_integer_notag(Val2),
Len2 = elength(length(Bytes2)),
lists:append([65 | Len2],Bytes2);
+enc_value('Unsigned32', Val) ->
+ if
+ (Val >= 0) andalso (Val =< 4294967295) ->
+ Bytes2 = enc_integer_notag(Val),
+ Len2 = elength(length(Bytes2)),
+ lists:append([66 | Len2], Bytes2);
+ true ->
+ exit({error, {bad_counter32, Val}})
+ end;
+enc_value('TimeTicks', Val) ->
+ if
+ (Val >= 0) andalso (Val =< 4294967295) ->
+ Bytes2 = enc_integer_notag(Val),
+ Len2 = elength(length(Bytes2)),
+ lists:append([67 | Len2], Bytes2);
+ true ->
+ exit({error, {bad_timeticks, Val}})
+ end;
enc_value('Counter64', Val) ->
Val2 =
if
@@ -682,18 +727,7 @@ enc_value('Counter64', Val) ->
end,
Bytes2 = enc_integer_notag(Val2),
Len2 = elength(length(Bytes2)),
- lists:append([70 | Len2],Bytes2);
-enc_value(Type, Val) ->
- Bytes2 = enc_integer_notag(Val),
- Len2 = elength(length(Bytes2)),
- lists:append([enc_val_tag(Type,Val) | Len2],Bytes2).
-
-enc_val_tag('Counter32',Val) when (Val >= 0) andalso (Val =< 4294967295) ->
- 65;
-enc_val_tag('Unsigned32', Val) when (Val >= 0) andalso (Val =< 4294967295) ->
- 66;
-enc_val_tag('TimeTicks', Val) when (Val >= 0) andalso (Val =< 4294967295) ->
- 67.
+ lists:append([70 | Len2],Bytes2).
%%----------------------------------------------------------------------
diff --git a/lib/snmp/test/exp/snmp_agent_bl_test.erl b/lib/snmp/test/exp/snmp_agent_bl_test.erl
index a5a6e8260b..263319aa5d 100644
--- a/lib/snmp/test/exp/snmp_agent_bl_test.erl
+++ b/lib/snmp/test/exp/snmp_agent_bl_test.erl
@@ -95,24 +95,13 @@ end_per_testcase(_Case, Config) when list(Config) ->
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.
+ [
+ app_info,
+ test_v1, test_v2, test_v1_v2, test_v3,
+ test_multi_threaded,
+ mib_storage,
+ tickets
+ ].
%%%-----------------------------------------------------------------
@@ -1187,21 +1176,16 @@ init_v3(Config) when list(Config) ->
%% and we will be stuck with a bunch of mnesia tables for
%% the rest of this suite...
?DBG("start_agent -> start crypto app",[]),
- case os:type() of
- vxworks ->
- no_crypto;
- _ ->
- case ?CRYPTO_START() of
- ok ->
- case ?CRYPTO_SUPPORT() of
- {no, Reason} ->
- ?SKIP({unsupported_encryption, Reason});
- yes ->
- ok
- end;
- {error, Reason} ->
- ?SKIP({failed_starting_crypto, Reason})
- end
+ case ?CRYPTO_START() of
+ ok ->
+ case ?CRYPTO_SUPPORT() of
+ {no, Reason} ->
+ ?SKIP({unsupported_encryption, Reason});
+ yes ->
+ ok
+ end;
+ {error, Reason} ->
+ ?SKIP({failed_starting_crypto, Reason})
end,
SaNode = ?config(snmp_sa, Config),
create_tables(SaNode),
@@ -5071,12 +5055,7 @@ run(F, A, Opts) ->
CtxEngineID = snmp_misc:get_option(context_engine_id, Opts, EngineID),
Community = snmp_misc:get_option(community, Opts, "all-rights"),
?DBG("run -> start crypto app",[]),
- Crypto = case os:type() of
- vxworks ->
- no_crypto;
- _ ->
- ?CRYPTO_START()
- end,
+ Crypto = ?CRYPTO_START(),
?DBG("run -> Crypto: ~p",[Crypto]),
catch snmp_test_mgr:stop(), % If we had a running mgr from a failed case
StdM = filename:join(code:priv_dir(snmp), "mibs") ++ "/",
diff --git a/lib/snmp/test/exp/snmp_agent_ms_test.erl b/lib/snmp/test/exp/snmp_agent_ms_test.erl
index d5eaea55fa..340b95f512 100644
--- a/lib/snmp/test/exp/snmp_agent_ms_test.erl
+++ b/lib/snmp/test/exp/snmp_agent_ms_test.erl
@@ -231,17 +231,13 @@ end_per_testcase(_Case, Config) when list(Config) ->
Config.
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.
+ [
+ app_info,
+ {group, test_v1}, {group, test_v2},
+ {group, test_v1_v2}, {group, test_v3},
+ {group, test_multi_threaded}, {group, mib_storage},
+ {group, tickets}
+ ].
%%%-----------------------------------------------------------------
@@ -1221,21 +1217,16 @@ init_v3(Config) when list(Config) ->
%% and we will be stuck with a bunch of mnesia tables for
%% the rest of this suite...
?DBG("start_agent -> start crypto app",[]),
- case os:type() of
- vxworks ->
- no_crypto;
- _ ->
- case ?CRYPTO_START() of
- ok ->
- case ?CRYPTO_SUPPORT() of
- {no, Reason} ->
- ?SKIP({unsupported_encryption, Reason});
- yes ->
- ok
- end;
- {error, Reason} ->
- ?SKIP({failed_starting_crypto, Reason})
- end
+ case ?CRYPTO_START() of
+ ok ->
+ case ?CRYPTO_SUPPORT() of
+ {no, Reason} ->
+ ?SKIP({unsupported_encryption, Reason});
+ yes ->
+ ok
+ end;
+ {error, Reason} ->
+ ?SKIP({failed_starting_crypto, Reason})
end,
SaNode = ?config(snmp_sa, Config),
create_tables(SaNode),
@@ -5066,12 +5057,7 @@ run(F, A, Opts) ->
CtxEngineID = snmp_misc:get_option(context_engine_id, Opts, EngineID),
Community = snmp_misc:get_option(community, Opts, "all-rights"),
?DBG("run -> start crypto app",[]),
- Crypto = case os:type() of
- vxworks ->
- no_crypto;
- _ ->
- ?CRYPTO_START()
- end,
+ Crypto = ?CRYPTO_START(),
?DBG("run -> Crypto: ~p",[Crypto]),
catch snmp_test_mgr:stop(), % If we had a running mgr from a failed case
StdM = filename:join(code:priv_dir(snmp), "mibs") ++ "/",
diff --git a/lib/snmp/test/exp/snmp_agent_mt_test.erl b/lib/snmp/test/exp/snmp_agent_mt_test.erl
index d62bc6c2e7..33d104305a 100644
--- a/lib/snmp/test/exp/snmp_agent_mt_test.erl
+++ b/lib/snmp/test/exp/snmp_agent_mt_test.erl
@@ -231,17 +231,13 @@ end_per_testcase(_Case, Config) when list(Config) ->
Config.
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.
+ [
+ app_info,
+ {group, test_v1}, {group, test_v2},
+ {group, test_v1_v2}, {group, test_v3},
+ {group, test_multi_threaded}, {group, mib_storage},
+ {group, tickets}
+ ].
%%%-----------------------------------------------------------------
@@ -1221,21 +1217,16 @@ init_v3(Config) when list(Config) ->
%% and we will be stuck with a bunch of mnesia tables for
%% the rest of this suite...
?DBG("start_agent -> start crypto app",[]),
- case os:type() of
- vxworks ->
- no_crypto;
- _ ->
- case ?CRYPTO_START() of
- ok ->
- case ?CRYPTO_SUPPORT() of
- {no, Reason} ->
- ?SKIP({unsupported_encryption, Reason});
- yes ->
- ok
- end;
- {error, Reason} ->
- ?SKIP({failed_starting_crypto, Reason})
- end
+ case ?CRYPTO_START() of
+ ok ->
+ case ?CRYPTO_SUPPORT() of
+ {no, Reason} ->
+ ?SKIP({unsupported_encryption, Reason});
+ yes ->
+ ok
+ end;
+ {error, Reason} ->
+ ?SKIP({failed_starting_crypto, Reason})
end,
SaNode = ?config(snmp_sa, Config),
create_tables(SaNode),
@@ -5066,12 +5057,7 @@ run(F, A, Opts) ->
CtxEngineID = snmp_misc:get_option(context_engine_id, Opts, EngineID),
Community = snmp_misc:get_option(community, Opts, "all-rights"),
?DBG("run -> start crypto app",[]),
- Crypto = case os:type() of
- vxworks ->
- no_crypto;
- _ ->
- ?CRYPTO_START()
- end,
+ Crypto = ?CRYPTO_START(),
?DBG("run -> Crypto: ~p",[Crypto]),
catch snmp_test_mgr:stop(), % If we had a running mgr from a failed case
StdM = filename:join(code:priv_dir(snmp), "mibs") ++ "/",
diff --git a/lib/snmp/test/exp/snmp_agent_v2_test.erl b/lib/snmp/test/exp/snmp_agent_v2_test.erl
index a86449ca72..dc3d2efbb3 100644
--- a/lib/snmp/test/exp/snmp_agent_v2_test.erl
+++ b/lib/snmp/test/exp/snmp_agent_v2_test.erl
@@ -231,17 +231,13 @@ end_per_testcase(_Case, Config) when list(Config) ->
Config.
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.
+ [
+ app_info,
+ {group, test_v1}, {group, test_v2},
+ {group, test_v1_v2}, {group, test_v3},
+ {group, test_multi_threaded}, {group, mib_storage},
+ {group, tickets}
+ ].
%%%-----------------------------------------------------------------
@@ -1221,21 +1217,16 @@ init_v3(Config) when list(Config) ->
%% and we will be stuck with a bunch of mnesia tables for
%% the rest of this suite...
?DBG("start_agent -> start crypto app",[]),
- case os:type() of
- vxworks ->
- no_crypto;
- _ ->
- case ?CRYPTO_START() of
- ok ->
- case ?CRYPTO_SUPPORT() of
- {no, Reason} ->
- ?SKIP({unsupported_encryption, Reason});
- yes ->
- ok
- end;
- {error, Reason} ->
- ?SKIP({failed_starting_crypto, Reason})
- end
+ case ?CRYPTO_START() of
+ ok ->
+ case ?CRYPTO_SUPPORT() of
+ {no, Reason} ->
+ ?SKIP({unsupported_encryption, Reason});
+ yes ->
+ ok
+ end;
+ {error, Reason} ->
+ ?SKIP({failed_starting_crypto, Reason})
end,
SaNode = ?config(snmp_sa, Config),
create_tables(SaNode),
@@ -5066,12 +5057,7 @@ run(F, A, Opts) ->
CtxEngineID = snmp_misc:get_option(context_engine_id, Opts, EngineID),
Community = snmp_misc:get_option(community, Opts, "all-rights"),
?DBG("run -> start crypto app",[]),
- Crypto = case os:type() of
- vxworks ->
- no_crypto;
- _ ->
- ?CRYPTO_START()
- end,
+ Crypto = ?CRYPTO_START(),
?DBG("run -> Crypto: ~p",[Crypto]),
catch snmp_test_mgr:stop(), % If we had a running mgr from a failed case
StdM = filename:join(code:priv_dir(snmp), "mibs") ++ "/",
diff --git a/lib/snmp/test/exp/snmp_agent_v3_test.erl b/lib/snmp/test/exp/snmp_agent_v3_test.erl
index c72d845bf2..b0bc6384e8 100644
--- a/lib/snmp/test/exp/snmp_agent_v3_test.erl
+++ b/lib/snmp/test/exp/snmp_agent_v3_test.erl
@@ -231,17 +231,12 @@ end_per_testcase(_Case, Config) when list(Config) ->
Config.
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.
+ [
+ app_info, {group, test_v1}, {group, test_v2},
+ {group, test_v1_v2}, {group, test_v3},
+ {group, test_multi_threaded}, {group, mib_storage},
+ {group, tickets}
+ ].
%%%-----------------------------------------------------------------
@@ -1221,21 +1216,16 @@ init_v3(Config) when list(Config) ->
%% and we will be stuck with a bunch of mnesia tables for
%% the rest of this suite...
?DBG("start_agent -> start crypto app",[]),
- case os:type() of
- vxworks ->
- no_crypto;
- _ ->
- case ?CRYPTO_START() of
- ok ->
- case ?CRYPTO_SUPPORT() of
- {no, Reason} ->
- ?SKIP({unsupported_encryption, Reason});
- yes ->
- ok
- end;
- {error, Reason} ->
- ?SKIP({failed_starting_crypto, Reason})
- end
+ case ?CRYPTO_START() of
+ ok ->
+ case ?CRYPTO_SUPPORT() of
+ {no, Reason} ->
+ ?SKIP({unsupported_encryption, Reason});
+ yes ->
+ ok
+ end;
+ {error, Reason} ->
+ ?SKIP({failed_starting_crypto, Reason})
end,
SaNode = ?config(snmp_sa, Config),
create_tables(SaNode),
@@ -5066,12 +5056,7 @@ run(F, A, Opts) ->
CtxEngineID = snmp_misc:get_option(context_engine_id, Opts, EngineID),
Community = snmp_misc:get_option(community, Opts, "all-rights"),
?DBG("run -> start crypto app",[]),
- Crypto = case os:type() of
- vxworks ->
- no_crypto;
- _ ->
- ?CRYPTO_START()
- end,
+ Crypto = ?CRYPTO_START(),
?DBG("run -> Crypto: ~p",[Crypto]),
catch snmp_test_mgr:stop(), % If we had a running mgr from a failed case
StdM = filename:join(code:priv_dir(snmp), "mibs") ++ "/",
diff --git a/lib/snmp/test/modules.mk b/lib/snmp/test/modules.mk
index a444cab6d6..3d658bf8e8 100644
--- a/lib/snmp/test/modules.mk
+++ b/lib/snmp/test/modules.mk
@@ -79,5 +79,5 @@ MIB_FILES = \
Test2.mib \
Test3.mib
-SPECS = snmp.spec snmp.spec.vxworks
+SPECS = snmp.spec
diff --git a/lib/snmp/test/snmp.spec.vxworks b/lib/snmp/test/snmp.spec.vxworks
deleted file mode 100644
index 654aa96d8c..0000000000
--- a/lib/snmp/test/snmp.spec.vxworks
+++ /dev/null
@@ -1,4 +0,0 @@
-{topcase, {dir, "../snmp_test"}}.
-{skip, {snmp_SUITE, test_v3, "Requires crypto"}}.
-
-
diff --git a/lib/snmp/test/snmp_agent_test.erl b/lib/snmp/test/snmp_agent_test.erl
index 8ee5dd534d..09e1eb25a9 100644
--- a/lib/snmp/test/snmp_agent_test.erl
+++ b/lib/snmp/test/snmp_agent_test.erl
@@ -1,3 +1,4 @@
+%% -*- coding: utf-8 -*-
%%
%% %CopyrightBegin%
%%
@@ -346,47 +347,72 @@ end_per_group(_GroupName, Config) ->
-init_per_testcase(otp8395 = Case, Config) when is_list(Config) ->
+%% ---- Init Per TestCase ----
+
+init_per_testcase(Case, Config) when is_list(Config) ->
?DBG("init_per_testcase -> entry with"
+ "~n Config: ~p", [Config]),
+
+ p("Agent Info: "
+ "~n ~p", [snmpa:info()]),
+
+ init_per_testcase1(Case, Config).
+
+init_per_testcase1(otp8395 = Case, Config) when is_list(Config) ->
+ ?DBG("init_per_testcase1 -> entry with"
"~n Case: ~p"
"~n Config: ~p", [Case, Config]),
otp8395({init, init_per_testcase2(Case, Config)});
-init_per_testcase(otp9884 = Case, Config) when is_list(Config) ->
- ?DBG("init_per_testcase -> entry with"
+init_per_testcase1(otp9884 = Case, Config) when is_list(Config) ->
+ ?DBG("init_per_testcase1 -> entry with"
"~n Case: ~p"
"~n Config: ~p", [Case, Config]),
otp9884({init, init_per_testcase2(Case, Config)});
-init_per_testcase(otp_7157_test = _Case, Config) when is_list(Config) ->
- ?DBG("init_per_testcase -> entry with"
+init_per_testcase1(otp_7157_test = _Case, Config) when is_list(Config) ->
+ ?DBG("init_per_testcase1 -> entry with"
"~n Case: ~p"
"~n Config: ~p", [_Case, Config]),
Dog = ?WD_START(?MINS(1)),
[{watchdog, Dog} | Config ];
-init_per_testcase(v2_inform_i = _Case, Config) when is_list(Config) ->
- ?DBG("init_per_testcase -> entry with"
+init_per_testcase1(v2_inform_i = _Case, Config) when is_list(Config) ->
+ ?DBG("init_per_testcase1 -> entry with"
"~n Case: ~p"
"~n Config: ~p", [_Case, Config]),
Dog = ?WD_START(?MINS(10)),
[{watchdog, Dog} | Config ];
-init_per_testcase(v3_inform_i = _Case, Config) when is_list(Config) ->
- ?DBG("init_per_testcase -> entry with"
+init_per_testcase1(v3_inform_i = _Case, Config) when is_list(Config) ->
+ ?DBG("init_per_testcase1 -> entry with"
"~n Case: ~p"
"~n Config: ~p", [_Case, Config]),
Dog = ?WD_START(?MINS(10)),
[{watchdog, Dog} | Config ];
-init_per_testcase(_Case, Config) when is_list(Config) ->
+init_per_testcase1(_Case, Config) when is_list(Config) ->
?DBG("init_per_testcase -> entry with"
"~n Case: ~p"
"~n Config: ~p", [_Case, Config]),
Dog = ?WD_START(?MINS(6)),
[{watchdog, Dog}| Config ].
-end_per_testcase(otp8395, Config) when is_list(Config) ->
+
+%% ---- End Per TestCase ----
+
+end_per_testcase(Case, Config) when is_list(Config) ->
+ ?DBG("end_per_testcase -> entry with"
+ "~n Config: ~p", [Config]),
+
+ p("Agent Info: "
+ "~n ~p", [snmpa:info()]),
+
+ display_log(Config),
+
+ end_per_testcase1(Case, Config).
+
+end_per_testcase1(otp8395, Config) when is_list(Config) ->
otp8395({fin, Config});
-end_per_testcase(otp9884, Config) when is_list(Config) ->
+end_per_testcase1(otp9884, Config) when is_list(Config) ->
otp9884({fin, Config});
-end_per_testcase(_Case, Config) when is_list(Config) ->
- ?DBG("end_per_testcase -> entry with"
+end_per_testcase1(_Case, Config) when is_list(Config) ->
+ ?DBG("end_per_testcase1 -> entry with"
"~n Case: ~p"
"~n Config: ~p", [_Case, Config]),
Dog = ?config(watchdog, Config),
@@ -433,29 +459,16 @@ end_per_testcase2(_Case, Config) ->
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.
+ [
+ {group, misc},
+ {group, test_v1},
+ {group, test_v2},
+ {group, test_v1_v2},
+ {group, test_v3},
+ {group, test_multi_threaded},
+ {group, mib_storage},
+ {group, tickets1}
+ ].
%%%-----------------------------------------------------------------
@@ -1280,21 +1293,16 @@ init_v3(Config) when is_list(Config) ->
%% and we will be stuck with a bunch of mnesia tables for
%% the rest of this suite...
?DBG("start_agent -> start crypto app",[]),
- case os:type() of
- vxworks ->
- no_crypto;
- _ ->
- case ?CRYPTO_START() of
- ok ->
- case ?CRYPTO_SUPPORT() of
- {no, Reason} ->
- ?SKIP({unsupported_encryption, Reason});
- yes ->
- ok
- end;
- {error, Reason} ->
- ?SKIP({failed_starting_crypto, Reason})
- end
+ case ?CRYPTO_START() of
+ ok ->
+ case ?CRYPTO_SUPPORT() of
+ {no, Reason} ->
+ ?SKIP({unsupported_encryption, Reason});
+ yes ->
+ ok
+ end;
+ {error, Reason} ->
+ ?SKIP({failed_starting_crypto, Reason})
end,
SaNode = ?config(snmp_sa, Config),
create_tables(SaNode),
@@ -1406,9 +1414,6 @@ simple(Config) when is_list(Config) ->
try_test(simple_standard_test),
- p("Display log"),
- display_log(Config),
-
p("done"),
ok.
@@ -5748,14 +5753,14 @@ otp_4394_config(AgentConfDir, MgrDir, Ip0) ->
?line write_community_conf(AgentConfDir, [C1, C2]),
?line update_vacm(Vsn, AgentConfDir),
Ta1 = {"shelob v1",
- [134,138,177,177], 5000, 1500, 3, %% Anv�nd Ip och modda
+ [134,138,177,177], 5000, 1500, 3, %% Use Ip and modify
"pc1",
"target_v1", "",
%% [255,255,255,255,0,0],
[],
2048},
Ta2 = {"bifur v1",
- [134,138,177,75], 5000, 1500, 3, %% Anv�nd Ip
+ [134,138,177,75], 5000, 1500, 3, %% Use Ip
"pc2",
"target_v1", "",
%% [255,255,255,255,0,0],
diff --git a/lib/snmp/test/snmp_agent_test_lib.erl b/lib/snmp/test/snmp_agent_test_lib.erl
index 238832b7c1..757aebfa9b 100644
--- a/lib/snmp/test/snmp_agent_test_lib.erl
+++ b/lib/snmp/test/snmp_agent_test_lib.erl
@@ -338,12 +338,7 @@ run(Mod, Func, Args, Opts) ->
CtxEngineID = snmp_misc:get_option(context_engine_id, Opts, EngineID),
Community = snmp_misc:get_option(community, Opts, "all-rights"),
?DBG("run -> start crypto app",[]),
- Crypto = case os:type() of
- vxworks ->
- no_crypto;
- _ ->
- ?CRYPTO_START()
- end,
+ Crypto = ?CRYPTO_START(),
?DBG("run -> Crypto: ~p", [Crypto]),
catch snmp_test_mgr:stop(), % If we had a running mgr from a failed case
StdM = filename:join(code:priv_dir(snmp), "mibs") ++ "/",
@@ -729,7 +724,6 @@ expect2(Id, F) ->
get_timeout() ->
get_timeout(os:type()).
-get_timeout(vxworks) -> 7000;
get_timeout(_) -> 3500.
receive_pdu(To) ->
@@ -1540,7 +1534,6 @@ rpc(Node, F, A) ->
%% timeout() ->
%% timeout(os:type()).
%%
-%% timeout(vxworks) -> 7000;
%% timeout(_) -> 3500.
diff --git a/lib/snmp/test/snmp_log_test.erl b/lib/snmp/test/snmp_log_test.erl
index aeac4d1f71..e9345b44cc 100644
--- a/lib/snmp/test/snmp_log_test.erl
+++ b/lib/snmp/test/snmp_log_test.erl
@@ -375,9 +375,9 @@ log_to_io1(Config) when is_list(Config) ->
%%======================================================================
-%% Starta en logger-process som med ett visst intervall loggar
-%% meddelanden. Starta en reader-process som vid ett viss tillf�lle
-%% l�ser fr�n loggen.
+%% Start a logger-process that logs messages with a certain interval.
+%% Start a reader-process that reads messages from the log at a certain
+%% point of time.
log_to_io2(suite) -> [];
log_to_io2(doc) -> "Log to io from a different process than which "
@@ -578,9 +578,9 @@ log_to_txt(Name, SeqNoGen, Config) when is_list(Config) ->
%%======================================================================
-%% Starta en logger-process som med ett visst intervall loggar
-%% meddelanden. Starta en reader-process som vid ett viss tillf�lle
-%% l�ser fr�n loggen.
+%% Start a logger-process that logs messages with a certain interval.
+%% Start a reader-process that reads messages from the log at a certain
+%% point of time.
%%
%% Test: ts:run(snmp, snmp_log_test, log_to_txt2, [batch]).
diff --git a/lib/snmp/test/snmp_manager_test.erl b/lib/snmp/test/snmp_manager_test.erl
index c374a2f0a6..dedbae5ce4 100644
--- a/lib/snmp/test/snmp_manager_test.erl
+++ b/lib/snmp/test/snmp_manager_test.erl
@@ -174,7 +174,26 @@ end_per_suite(Config) when is_list(Config) ->
init_per_testcase(Case, Config) when is_list(Config) ->
io:format(user, "~n~n*** INIT ~w:~w ***~n~n", [?MODULE,Case]),
- init_per_testcase2(Case, Config).
+ %% This version of the API, based on Addr and Port, has been deprecated
+ DeprecatedApiCases =
+ [
+ simple_sync_get1,
+ simple_async_get1,
+ simple_sync_get_next1,
+ simple_async_get_next1,
+ simple_sync_set1,
+ simple_async_set1,
+ simple_sync_get_bulk1,
+ simple_async_get_bulk1,
+ misc_async1
+ ],
+ case lists:member(Case, DeprecatedApiCases) of
+ true ->
+ %% ?SKIP(api_no_longer_supported);
+ {skip, api_no_longer_supported};
+ false ->
+ init_per_testcase2(Case, Config)
+ end.
init_per_testcase2(Case, Config) ->
?DBG("init_per_testcase2 -> ~p", [erlang:nodes()]),
@@ -226,18 +245,6 @@ init_per_testcase2(Case, Config) ->
Conf2.
init_per_testcase3(Case, Config) ->
- ApiCases01 =
- [
- simple_sync_get1,
- simple_async_get1,
- simple_sync_get_next1,
- simple_async_get_next1,
- simple_sync_set1,
- simple_async_set1,
- simple_sync_get_bulk1,
- simple_async_get_bulk1,
- misc_async1
- ],
ApiCases02 =
[
simple_sync_get2,
@@ -273,7 +280,6 @@ init_per_testcase3(Case, Config) ->
inform_swarm,
report
] ++
- ApiCases01 ++
ApiCases02 ++
ApiCases03,
case lists:member(Case, Cases) of
@@ -319,18 +325,6 @@ end_per_testcase(Case, Config) when is_list(Config) ->
Conf2.
end_per_testcase2(Case, Config) ->
- ApiCases01 =
- [
- simple_sync_get1,
- simple_async_get1,
- simple_sync_get_next1,
- simple_async_get_next1,
- simple_sync_set1,
- simple_async_set1,
- simple_sync_get_bulk1,
- simple_async_get_bulk1,
- misc_async1
- ],
ApiCases02 =
[
simple_sync_get2,
@@ -366,7 +360,6 @@ end_per_testcase2(Case, Config) ->
inform_swarm,
report
] ++
- ApiCases01 ++
ApiCases02 ++
ApiCases03,
case lists:member(Case, Cases) of
@@ -1596,12 +1589,14 @@ register_agent3(Config) when is_list(Config) ->
simple_sync_get1(doc) -> ["Simple sync get-request - Old style (Addr & Port)"];
simple_sync_get1(suite) -> [];
simple_sync_get1(Config) when is_list(Config) ->
+ ?SKIP(api_no_longer_supported),
+
process_flag(trap_exit, true),
put(tname, ssg1),
p("starting with Config: ~p~n", [Config]),
Node = ?config(manager_node, Config),
- Addr = ?config(ip, Config),
+ Addr = ?config(manager_agent_target_name, Config),
Port = ?AGENT_PORT,
p("issue get-request without loading the mib"),
@@ -1757,6 +1752,8 @@ simple_async_get1(doc) ->
["Simple (async) get-request - Old style (Addr & Port)"];
simple_async_get1(suite) -> [];
simple_async_get1(Config) when is_list(Config) ->
+ ?SKIP(api_no_longer_supported),
+
process_flag(trap_exit, true),
put(tname, sag1),
p("starting with Config: ~p~n", [Config]),
@@ -1972,6 +1969,8 @@ simple_sync_get_next1(doc) -> ["Simple (sync) get_next-request - "
"Old style (Addr & Port)"];
simple_sync_get_next1(suite) -> [];
simple_sync_get_next1(Config) when is_list(Config) ->
+ ?SKIP(api_no_longer_supported),
+
process_flag(trap_exit, true),
put(tname, ssgn1),
p("starting with Config: ~p~n", [Config]),
@@ -2264,6 +2263,8 @@ simple_async_get_next1(doc) -> ["Simple (async) get_next-request - "
"Old style (Addr & Port)"];
simple_async_get_next1(suite) -> [];
simple_async_get_next1(Config) when is_list(Config) ->
+ ?SKIP(api_no_longer_supported),
+
process_flag(trap_exit, true),
put(tname, ssgn1),
p("starting with Config: ~p~n", [Config]),
@@ -2516,6 +2517,8 @@ simple_sync_set1(doc) -> ["Simple (sync) set-request - "
"Old style (Addr & Port)"];
simple_sync_set1(suite) -> [];
simple_sync_set1(Config) when is_list(Config) ->
+ ?SKIP(api_no_longer_supported),
+
process_flag(trap_exit, true),
put(tname, sss1),
p("starting with Config: ~p~n", [Config]),
@@ -2686,6 +2689,8 @@ simple_async_set1(doc) -> ["Simple (async) set-request - "
"Old style (Addr & Port)"];
simple_async_set1(suite) -> [];
simple_async_set1(Config) when is_list(Config) ->
+ ?SKIP(api_no_longer_supported),
+
process_flag(trap_exit, true),
put(tname, sas1),
p("starting with Config: ~p~n", [Config]),
@@ -2896,6 +2901,8 @@ simple_sync_get_bulk1(doc) -> ["Simple (sync) get_bulk-request - "
"Old style (Addr & Port)"];
simple_sync_get_bulk1(suite) -> [];
simple_sync_get_bulk1(Config) when is_list(Config) ->
+ ?SKIP(api_no_longer_supported),
+
process_flag(trap_exit, true),
put(tname, ssgb1),
p("starting with Config: ~p~n", [Config]),
@@ -3261,6 +3268,8 @@ simple_async_get_bulk1(doc) -> ["Simple (async) get_bulk-request - "
"Old style (Addr & Port)"];
simple_async_get_bulk1(suite) -> [];
simple_async_get_bulk1(Config) when is_list(Config) ->
+ ?SKIP(api_no_longer_supported),
+
process_flag(trap_exit, true),
put(tname, sagb1),
p("starting with Config: ~p~n", [Config]),
@@ -3605,6 +3614,8 @@ misc_async1(doc) -> ["Misc (async) request(s) - "
"Old style (Addr & Port)"];
misc_async1(suite) -> [];
misc_async1(Config) when is_list(Config) ->
+ ?SKIP(api_no_longer_supported),
+
process_flag(trap_exit, true),
put(tname, ms1),
p("starting with Config: ~p~n", [Config]),
@@ -5591,29 +5602,35 @@ fin_mgr_user(Conf) ->
init_mgr_user_data1(Conf) ->
Node = ?config(manager_node, Conf),
- Addr = ?config(ip, Conf),
- Port = ?AGENT_PORT,
- ?line ok = mgr_user_register_agent(Node, Addr, Port),
+ TargetName = ?config(manager_agent_target_name, Conf),
+ Addr = ?config(ip, Conf),
+ Port = ?AGENT_PORT,
+ ?line ok = mgr_user_register_agent(Node, TargetName,
+ [{address, Addr},
+ {port, Port},
+ {engine_id, "agentEngine"}]),
Agents = mgr_user_which_own_agents(Node),
?DBG("Own agents: ~p", [Agents]),
- ?line {ok, DefAgentConf} = mgr_user_agent_info(Node, Addr, Port, all),
+ ?line {ok, DefAgentConf} = mgr_user_agent_info(Node, TargetName, all),
?DBG("Default agent config: ~n~p", [DefAgentConf]),
- ?line ok = mgr_user_update_agent_info(Node, Addr, Port,
+ ?line ok = mgr_user_update_agent_info(Node, TargetName,
community, "all-rights"),
- ?line ok = mgr_user_update_agent_info(Node, Addr, Port,
+ ?line ok = mgr_user_update_agent_info(Node, TargetName,
sec_name, "all-rights"),
- ?line ok = mgr_user_update_agent_info(Node, Addr, Port,
+ ?line ok = mgr_user_update_agent_info(Node, TargetName,
engine_id, "agentEngine"),
- ?line ok = mgr_user_update_agent_info(Node, Addr, Port,
+ ?line ok = mgr_user_update_agent_info(Node, TargetName,
max_message_size, 1024),
- ?line {ok, AgentConf} = mgr_user_agent_info(Node, Addr, Port, all),
+ ?line {ok, AgentConf} = mgr_user_agent_info(Node, TargetName, all),
?DBG("Updated agent config: ~n~p", [AgentConf]),
Conf.
init_mgr_user_data2(Conf) ->
+ ?DBG("init_mgr_user_data2 -> entry with"
+ "~n Conf: ~p", [Conf]),
Node = ?config(manager_node, Conf),
TargetName = ?config(manager_agent_target_name, Conf),
Addr = ?config(ip, Conf),
@@ -5641,9 +5658,8 @@ init_mgr_user_data2(Conf) ->
fin_mgr_user_data1(Conf) ->
Node = ?config(manager_node, Conf),
- Addr = ?config(ip, Conf),
- Port = ?AGENT_PORT,
- mgr_user_unregister_agent(Node, Addr, Port),
+ TargetName = ?config(manager_agent_target_name, Conf),
+ mgr_user_unregister_agent(Node, TargetName),
mgr_user_which_own_agents(Node),
Conf.
@@ -5670,33 +5686,41 @@ mgr_user_stop(Node) ->
%% mgr_user_register_agent(Node, Addr, ?AGENT_PORT, []).
mgr_user_register_agent(Node, TargetName, Conf)
when is_list(TargetName) andalso is_list(Conf) ->
- rcall(Node, snmp_manager_user, register_agent, [TargetName, Conf]);
-mgr_user_register_agent(Node, Addr, Port) ->
- mgr_user_register_agent(Node, Addr, Port, []).
-mgr_user_register_agent(Node, Addr, Port, Conf) ->
- rcall(Node, snmp_manager_user, register_agent, [Addr, Port, Conf]).
+ rcall(Node, snmp_manager_user, register_agent, [TargetName, Conf]).
+%% <REMOVED-IN-R16B>
+%% mgr_user_register_agent(Node, Addr, Port) ->
+%% mgr_user_register_agent(Node, Addr, Port, []).
+%% mgr_user_register_agent(Node, Addr, Port, Conf) ->
+%% rcall(Node, snmp_manager_user, register_agent, [Addr, Port, Conf]).
+%% </REMOVED-IN-R16B>
%% mgr_user_unregister_agent(Node) ->
%% mgr_user_unregister_agent(Node, ?LOCALHOST(), ?AGENT_PORT).
-mgr_user_unregister_agent(Node, Addr_or_TargetName) ->
- rcall(Node, snmp_manager_user, unregister_agent, [Addr_or_TargetName]).
-mgr_user_unregister_agent(Node, Addr, Port) ->
- rcall(Node, snmp_manager_user, unregister_agent, [Addr, Port]).
-
-mgr_user_agent_info(Node, Addr_or_TargetName, Item) when is_atom(Item) ->
- rcall(Node, snmp_manager_user, agent_info, [Addr_or_TargetName, Item]).
-mgr_user_agent_info(Node, Addr, Port, Item) when is_atom(Item) ->
- rcall(Node, snmp_manager_user, agent_info, [Addr, Port, Item]).
+mgr_user_unregister_agent(Node, TargetName) when is_list(TargetName) ->
+ rcall(Node, snmp_manager_user, unregister_agent, [TargetName]).
+%% <REMOVED-IN-R16B>
+%% mgr_user_unregister_agent(Node, Addr, Port) ->
+%% rcall(Node, snmp_manager_user, unregister_agent, [Addr, Port]).
+%% </REMOVED-IN-R16B>
+
+mgr_user_agent_info(Node, TargetName, Item)
+ when is_list(TargetName) andalso is_atom(Item) ->
+ rcall(Node, snmp_manager_user, agent_info, [TargetName, Item]).
+%% <REMOVED-IN-R16B>
+%% mgr_user_agent_info(Node, Addr, Port, Item) when is_atom(Item) ->
+%% rcall(Node, snmp_manager_user, agent_info, [Addr, Port, Item]).
+%% </REMOVED-IN-R16B>
%% mgr_user_update_agent_info(Node, Item, Val) when atom(Item) ->
%% mgr_user_update_agent_info(Node, ?LOCALHOST(), ?AGENT_PORT, Item, Val).
-mgr_user_update_agent_info(Node, Addr_or_TargetName, Item, Val)
- when is_atom(Item) ->
- rcall(Node, snmp_manager_user, update_agent_info,
- [Addr_or_TargetName, Item, Val]).
-mgr_user_update_agent_info(Node, Addr, Port, Item, Val) when is_atom(Item) ->
- rcall(Node, snmp_manager_user, update_agent_info,
- [Addr, Port, Item, Val]).
+mgr_user_update_agent_info(Node, TargetName, Item, Val)
+ when is_list(TargetName) andalso is_atom(Item) ->
+ rcall(Node, snmp_manager_user, update_agent_info, [TargetName, Item, Val]).
+%% <REMOVED-IN-R16B>
+%% mgr_user_update_agent_info(Node, Addr, Port, Item, Val) when is_atom(Item) ->
+%% rcall(Node, snmp_manager_user, update_agent_info,
+%% [Addr, Port, Item, Val]).
+%% </REMOVED-IN-R16B>
%% mgr_user_which_all_agents(Node) ->
%% rcall(Node, snmp_manager_user, which_all_agents, []).
@@ -5709,89 +5733,112 @@ mgr_user_load_mib(Node, Mib) ->
%% mgr_user_sync_get(Node, Oids) ->
%% mgr_user_sync_get(Node, ?LOCALHOST(), ?AGENT_PORT, Oids).
-mgr_user_sync_get(Node, Addr_or_TargetName, Oids) ->
- rcall(Node, snmp_manager_user, sync_get, [Addr_or_TargetName, Oids]).
+mgr_user_sync_get(Node, TargetName, Oids) when is_list(TargetName) ->
+ rcall(Node, snmp_manager_user, sync_get, [TargetName, Oids]).
+%% <REMOVED-IN-R16B>
mgr_user_sync_get(Node, Addr, Port, Oids) ->
rcall(Node, snmp_manager_user, sync_get, [Addr, Port, Oids]).
+%% </REMOVED-IN-R16B>
-mgr_user_sync_get2(Node, TargetName, Oids, SendOpts) ->
+mgr_user_sync_get2(Node, TargetName, Oids, SendOpts) when is_list(TargetName) ->
rcall(Node, snmp_manager_user, sync_get2, [TargetName, Oids, SendOpts]).
%% mgr_user_async_get(Node, Oids) ->
%% mgr_user_async_get(Node, ?LOCALHOST(), ?AGENT_PORT, Oids).
-mgr_user_async_get(Node, Addr_or_TargetName, Oids) ->
- rcall(Node, snmp_manager_user, async_get, [Addr_or_TargetName, Oids]).
+mgr_user_async_get(Node, TargetName, Oids) when is_list(TargetName) ->
+ rcall(Node, snmp_manager_user, async_get, [TargetName, Oids]).
+%% <REMOVED-IN-R16B>
mgr_user_async_get(Node, Addr, Port, Oids) ->
rcall(Node, snmp_manager_user, async_get, [Addr, Port, Oids]).
+%% </REMOVED-IN-R16B>
-mgr_user_async_get2(Node, TargetName, Oids, SendOpts) ->
+mgr_user_async_get2(Node, TargetName, Oids, SendOpts)
+ when is_list(TargetName) ->
rcall(Node, snmp_manager_user, async_get2, [TargetName, Oids, SendOpts]).
%% mgr_user_sync_get_next(Node, Oids) ->
%% mgr_user_sync_get_next(Node, ?LOCALHOST(), ?AGENT_PORT, Oids).
-mgr_user_sync_get_next(Node, Addr_or_TargetName, Oids) ->
- rcall(Node, snmp_manager_user, sync_get_next, [Addr_or_TargetName, Oids]).
+mgr_user_sync_get_next(Node, TargetName, Oids) when is_list(TargetName) ->
+ rcall(Node, snmp_manager_user, sync_get_next, [TargetName, Oids]).
+%% <REMOVED-IN-R16B>
mgr_user_sync_get_next(Node, Addr, Port, Oids) ->
rcall(Node, snmp_manager_user, sync_get_next, [Addr, Port, Oids]).
+%% </REMOVED-IN-R16B>
-mgr_user_sync_get_next2(Node, TargetName, Oids, SendOpts) ->
+mgr_user_sync_get_next2(Node, TargetName, Oids, SendOpts)
+ when is_list(TargetName) ->
rcall(Node, snmp_manager_user, sync_get_next2, [TargetName, Oids, SendOpts]).
%% mgr_user_async_get_next(Node, Oids) ->
%% mgr_user_async_get_next(Node, ?LOCALHOST(), ?AGENT_PORT, Oids).
-mgr_user_async_get_next(Node, Addr_or_TargetName, Oids) ->
- rcall(Node, snmp_manager_user, async_get_next, [Addr_or_TargetName, Oids]).
+mgr_user_async_get_next(Node, TargetName, Oids) when is_list(TargetName) ->
+ rcall(Node, snmp_manager_user, async_get_next, [TargetName, Oids]).
+%% <REMOVED-IN-R16B>
mgr_user_async_get_next(Node, Addr, Port, Oids) ->
rcall(Node, snmp_manager_user, async_get_next, [Addr, Port, Oids]).
+%% </REMOVED-IN-R16B>
-mgr_user_async_get_next2(Node, TargetName, Oids, SendOpts) ->
+mgr_user_async_get_next2(Node, TargetName, Oids, SendOpts)
+ when is_list(TargetName) ->
rcall(Node, snmp_manager_user, async_get_next2, [TargetName, Oids, SendOpts]).
%% mgr_user_sync_set(Node, VAV) ->
%% mgr_user_sync_set(Node, ?LOCALHOST(), ?AGENT_PORT, VAV).
-mgr_user_sync_set(Node, Addr_or_TargetName, VAV) ->
- rcall(Node, snmp_manager_user, sync_set, [Addr_or_TargetName, VAV]).
+mgr_user_sync_set(Node, TargetName, VAV) when is_list(TargetName) ->
+ rcall(Node, snmp_manager_user, sync_set, [TargetName, VAV]).
+%% <REMOVED-IN-R16B>
mgr_user_sync_set(Node, Addr, Port, VAV) ->
rcall(Node, snmp_manager_user, sync_set, [Addr, Port, VAV]).
+%% </REMOVED-IN-R16B>
-mgr_user_sync_set2(Node, TargetName, VAV, SendOpts) ->
+mgr_user_sync_set2(Node, TargetName, VAV, SendOpts) when is_list(TargetName) ->
rcall(Node, snmp_manager_user, sync_set2, [TargetName, VAV, SendOpts]).
%% mgr_user_async_set(Node, VAV) ->
%% mgr_user_async_set(Node, ?LOCALHOST(), ?AGENT_PORT, VAV).
-mgr_user_async_set(Node, Addr_or_TargetName, VAV) ->
- rcall(Node, snmp_manager_user, async_set, [Addr_or_TargetName, VAV]).
+mgr_user_async_set(Node, TargetName, VAV) when is_list(TargetName) ->
+ rcall(Node, snmp_manager_user, async_set, [TargetName, VAV]).
+%% <REMOVED-IN-R16B>
mgr_user_async_set(Node, Addr, Port, VAV) ->
rcall(Node, snmp_manager_user, async_set, [Addr, Port, VAV]).
+%% </REMOVED-IN-R16B>
-mgr_user_async_set2(Node, TargetName, VAV, SendOpts) ->
+mgr_user_async_set2(Node, TargetName, VAV, SendOpts) when is_list(TargetName) ->
rcall(Node, snmp_manager_user, async_set2, [TargetName, VAV, SendOpts]).
%% mgr_user_sync_get_bulk(Node, NonRep, MaxRep, Oids) ->
%% mgr_user_sync_get_bulk(Node, ?LOCALHOST(), ?AGENT_PORT,
%% NonRep, MaxRep, Oids).
-mgr_user_sync_get_bulk(Node, Addr_or_TargetName, NonRep, MaxRep, Oids) ->
+mgr_user_sync_get_bulk(Node, TargetName, NonRep, MaxRep, Oids)
+ when is_list(TargetName) ->
rcall(Node, snmp_manager_user, sync_get_bulk,
- [Addr_or_TargetName, NonRep, MaxRep, Oids]).
+ [TargetName, NonRep, MaxRep, Oids]).
+%% <REMOVED-IN-R16B>
mgr_user_sync_get_bulk(Node, Addr, Port, NonRep, MaxRep, Oids) ->
rcall(Node, snmp_manager_user, sync_get_bulk,
[Addr, Port, NonRep, MaxRep, Oids]).
+%% </REMOVED-IN-R16B>
-mgr_user_sync_get_bulk2(Node, TargetName, NonRep, MaxRep, Oids, SendOpts) ->
+mgr_user_sync_get_bulk2(Node, TargetName, NonRep, MaxRep, Oids, SendOpts)
+ when is_list(TargetName) ->
rcall(Node, snmp_manager_user, sync_get_bulk2,
[TargetName, NonRep, MaxRep, Oids, SendOpts]).
%% mgr_user_async_get_bulk(Node, NonRep, MaxRep, Oids) ->
%% mgr_user_async_get_bulk(Node, ?LOCALHOST(), ?AGENT_PORT,
%% NonRep, MaxRep, Oids).
-mgr_user_async_get_bulk(Node, Addr_or_TargetName, NonRep, MaxRep, Oids) ->
+mgr_user_async_get_bulk(Node, TargetName, NonRep, MaxRep, Oids)
+ when is_list(TargetName) ->
rcall(Node, snmp_manager_user, async_get_bulk,
- [Addr_or_TargetName, NonRep, MaxRep, Oids]).
+ [TargetName, NonRep, MaxRep, Oids]).
+%% <REMOVED-IN-R16B>
mgr_user_async_get_bulk(Node, Addr, Port, NonRep, MaxRep, Oids) ->
rcall(Node, snmp_manager_user, async_get_bulk,
[Addr, Port, NonRep, MaxRep, Oids]).
+%% </REMOVED-IN-R16B>
-mgr_user_async_get_bulk2(Node, TargetName, NonRep, MaxRep, Oids, SendOpts) ->
+mgr_user_async_get_bulk2(Node, TargetName, NonRep, MaxRep, Oids, SendOpts)
+ when is_list(TargetName) ->
rcall(Node, snmp_manager_user, async_get_bulk2,
[TargetName, NonRep, MaxRep, Oids, SendOpts]).
diff --git a/lib/snmp/test/snmp_manager_user.erl b/lib/snmp/test/snmp_manager_user.erl
index 1b62b04960..4e789bbaec 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-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -44,20 +44,20 @@
info/0,
system_info/0,
simulate_crash/1,
- register_agent/2, register_agent/3,
- unregister_agent/1, unregister_agent/2,
- agent_info/2, agent_info/3,
- update_agent_info/3, update_agent_info/4,
+ register_agent/2,
+ unregister_agent/1,
+ agent_info/2,
+ update_agent_info/3,
which_all_agents/0, which_own_agents/0,
load_mib/1, unload_mib/1,
- sync_get/1, sync_get/2, sync_get/3, sync_get2/3,
- async_get/1, async_get/2, async_get/3, async_get2/3,
- sync_get_next/1, sync_get_next/2, sync_get_next/3, sync_get_next2/3,
- async_get_next/1, async_get_next/2, async_get_next/3, async_get_next2/3,
- sync_set/1, sync_set/2, sync_set/3, sync_set2/3,
- async_set/1, async_set/2, async_set/3, async_set2/3,
- sync_get_bulk/3, sync_get_bulk/4, sync_get_bulk/5, sync_get_bulk2/5,
- async_get_bulk/3, async_get_bulk/4, async_get_bulk/5, async_get_bulk2/5,
+ sync_get/1, sync_get/2, sync_get2/3,
+ async_get/1, async_get/2, async_get2/3,
+ sync_get_next/1, sync_get_next/2, sync_get_next2/3,
+ async_get_next/1, async_get_next/2, async_get_next2/3,
+ sync_set/1, sync_set/2, sync_set2/3,
+ async_set/1, async_set/2, async_set2/3,
+ sync_get_bulk/3, sync_get_bulk/4, sync_get_bulk2/5,
+ async_get_bulk/3, async_get_bulk/4, async_get_bulk2/5,
name_to_oid/1, oid_to_name/1,
purify_oid/1
]).
@@ -73,15 +73,11 @@
-export([
handle_error/3,
- handle_agent/4,
+ handle_agent/5,
handle_pdu/4,
handle_trap/3,
handle_inform/3,
- handle_report/3,
- handle_pdu/5, % For backwards compatibillity
- handle_trap/4, % For backwards compatibillity
- handle_inform/4, % For backwards compatibillity
- handle_report/4 % For backwards compatibillity
+ handle_report/3
]).
@@ -126,27 +122,16 @@ simulate_crash(Reason) ->
register_agent(TargetName, Config)
when is_list(TargetName) andalso is_list(Config) ->
- call({register_agent, TargetName, Config});
-register_agent(Addr, Port) ->
- register_agent(Addr, Port, []).
-
-register_agent(Addr, Port, Conf) ->
- call({register_agent, Addr, Port, Conf}).
+ call({register_agent, TargetName, Config}).
unregister_agent(TargetName) ->
call({unregister_agent, TargetName}).
-unregister_agent(Addr, Port) ->
- call({unregister_agent, Addr, Port}).
agent_info(TargetName, Item) ->
call({agent_info, TargetName, Item}).
-agent_info(Addr, Port, Item) ->
- call({agent_info, Addr, Port, Item}).
update_agent_info(TargetName, Item, Val) ->
call({update_agent_info, TargetName, Item, Val}).
-update_agent_info(Addr, Port, Item, Val) ->
- call({update_agent_info, Addr, Port, Item, Val}).
which_all_agents() ->
call(which_all_agents).
@@ -165,11 +150,8 @@ unload_mib(Mib) ->
sync_get(Oids) ->
call({sync_get, Oids}).
-sync_get(Addr_or_TargetName, Oids) ->
- call({sync_get, Addr_or_TargetName, Oids}).
-
-sync_get(Addr, Port, Oids) ->
- call({sync_get, Addr, Port, Oids}).
+sync_get(TargetName, Oids) ->
+ call({sync_get, TargetName, Oids}).
sync_get2(TargetName, Oids, SendOpts) ->
call({sync_get2, TargetName, Oids, SendOpts}).
@@ -180,11 +162,8 @@ sync_get2(TargetName, Oids, SendOpts) ->
async_get(Oids) ->
call({async_get, Oids}).
-async_get(Addr_or_TargetName, Oids) ->
- call({async_get, Addr_or_TargetName, Oids}).
-
-async_get(Addr, Port, Oids) ->
- call({async_get, Addr, Port, Oids}).
+async_get(TargetName, Oids) ->
+ call({async_get, TargetName, Oids}).
async_get2(TargetName, Oids, SendOpts) ->
call({async_get2, TargetName, Oids, SendOpts}).
@@ -194,11 +173,8 @@ async_get2(TargetName, Oids, SendOpts) ->
sync_get_next(Oids) ->
call({sync_get_next, Oids}).
-sync_get_next(Addr_or_TargetName, Oids) ->
- call({sync_get_next, Addr_or_TargetName, Oids}).
-
-sync_get_next(Addr, Port, Oids) ->
- call({sync_get_next, Addr, Port, Oids}).
+sync_get_next(TargetName, Oids) ->
+ call({sync_get_next, TargetName, Oids}).
sync_get_next2(TargetName, Oids, SendOpts) ->
call({sync_get_next2, TargetName, Oids, SendOpts}).
@@ -208,11 +184,8 @@ sync_get_next2(TargetName, Oids, SendOpts) ->
async_get_next(Oids) ->
call({async_get_next, Oids}).
-async_get_next(Addr_or_TargetName, Oids) ->
- call({async_get_next, Addr_or_TargetName, Oids}).
-
-async_get_next(Addr, Port, Oids) ->
- call({async_get_next, Addr, Port, Oids}).
+async_get_next(TargetName, Oids) ->
+ call({async_get_next, TargetName, Oids}).
async_get_next2(TargetName, Oids, SendOpts) ->
call({async_get_next2, TargetName, Oids, SendOpts}).
@@ -222,11 +195,8 @@ async_get_next2(TargetName, Oids, SendOpts) ->
sync_set(VAV) ->
call({sync_set, VAV}).
-sync_set(Addr_or_TargetName, VAV) ->
- call({sync_set, Addr_or_TargetName, VAV}).
-
-sync_set(Addr, Port, VAV) ->
- call({sync_set, Addr, Port, VAV}).
+sync_set(TargetName, VAV) ->
+ call({sync_set, TargetName, VAV}).
sync_set2(TargetName, VAV, SendOpts) ->
call({sync_set2, TargetName, VAV, SendOpts}).
@@ -236,11 +206,8 @@ sync_set2(TargetName, VAV, SendOpts) ->
async_set(VAV) ->
call({async_set, VAV}).
-async_set(Addr_or_TargetName, VAV) ->
- call({async_set, Addr_or_TargetName, VAV}).
-
-async_set(Addr, Port, VAV) ->
- call({async_set, Addr, Port, VAV}).
+async_set(TargetName, VAV) ->
+ call({async_set, TargetName, VAV}).
async_set2(TargetName, VAV, SendOpts) ->
call({async_set2, TargetName, VAV, SendOpts}).
@@ -250,11 +217,8 @@ async_set2(TargetName, VAV, SendOpts) ->
sync_get_bulk(NonRep, MaxRep, Oids) ->
call({sync_get_bulk, NonRep, MaxRep, Oids}).
-sync_get_bulk(Addr_or_TargetName, NonRep, MaxRep, Oids) ->
- call({sync_get_bulk, Addr_or_TargetName, NonRep, MaxRep, Oids}).
-
-sync_get_bulk(Addr, Port, NonRep, MaxRep, Oids) ->
- call({sync_get_bulk, Addr, Port, NonRep, MaxRep, Oids}).
+sync_get_bulk(TargetName, NonRep, MaxRep, Oids) ->
+ call({sync_get_bulk, TargetName, NonRep, MaxRep, Oids}).
sync_get_bulk2(TargetName, NonRep, MaxRep, Oids, SendOpts) ->
call({sync_get_bulk2, TargetName, NonRep, MaxRep, Oids, SendOpts}).
@@ -264,11 +228,8 @@ sync_get_bulk2(TargetName, NonRep, MaxRep, Oids, SendOpts) ->
async_get_bulk(NonRep, MaxRep, Oids) ->
call({async_get_bulk, NonRep, MaxRep, Oids}).
-async_get_bulk(Addr_or_TargetName, NonRep, MaxRep, Oids) ->
- call({async_get_bulk, Addr_or_TargetName, NonRep, MaxRep, Oids}).
-
-async_get_bulk(Addr, Port, NonRep, MaxRep, Oids) ->
- call({async_get_bulk, Addr, Port, NonRep, MaxRep, Oids}).
+async_get_bulk(TargetName, NonRep, MaxRep, Oids) ->
+ call({async_get_bulk, TargetName, NonRep, MaxRep, Oids}).
async_get_bulk2(TargetName, NonRep, MaxRep, Oids, SendOpts) ->
call({async_get_bulk2, TargetName, NonRep, MaxRep, Oids, SendOpts}).
@@ -340,24 +301,12 @@ loop(#state{parent = Parent, id = Id} = S) ->
reply(From, Res, Ref),
loop(S);
- {{register_agent, Addr, Port, Conf}, From, Ref} ->
- d("loop -> received register_agent request"),
- Res = snmpm:register_agent(Id, Addr, Port, Conf),
- reply(From, Res, Ref),
- loop(S);
-
{{unregister_agent, TargetName}, From, Ref} ->
d("loop -> received unregister_agent request"),
Res = snmpm:unregister_agent(Id, TargetName),
reply(From, Res, Ref),
loop(S);
- {{unregister_agent, Addr, Port}, From, Ref} ->
- d("loop -> received unregister_agent request"),
- Res = snmpm:unregister_agent(Id, Addr, Port),
- reply(From, Res, Ref),
- loop(S);
-
{{agent_info, TargetName, Item}, From, Ref} ->
d("loop -> received agent_info request with"
"~n TargetName: ~p"
@@ -368,15 +317,6 @@ loop(#state{parent = Parent, id = Id} = S) ->
reply(From, Res, Ref),
loop(S);
- {{agent_info, Addr, Port, Item}, From, Ref} ->
- d("loop -> received agent_info request with"
- "~n Addr: ~p"
- "~n Port: ~p"
- "~n Item: ~p", [Addr, Port, Item]),
- Res = snmpm:agent_info(Addr, Port, Item),
- reply(From, Res, Ref),
- loop(S);
-
{{update_agent_info, TargetName, Item, Val}, From, Ref} ->
d("loop -> received update_agent_info request with"
"~n TargetName: ~p"
@@ -386,16 +326,6 @@ loop(#state{parent = Parent, id = Id} = S) ->
reply(From, Res, Ref),
loop(S);
- {{update_agent_info, Addr, Port, Item, Val}, From, Ref} ->
- d("loop -> received update_agent_info request with"
- "~n Addr: ~p"
- "~n Port: ~p"
- "~n Item: ~p"
- "~n Val: ~p", [Addr, Port, Item, Val]),
- Res = snmpm:update_agent_info(Id, Addr, Port, Item, Val),
- reply(From, Res, Ref),
- loop(S);
-
{which_all_agents, From, Ref} ->
d("loop -> received which_all_agents request"),
Res = snmpm:which_agents(),
@@ -452,23 +382,6 @@ loop(#state{parent = Parent, id = Id} = S) ->
reply(From, Res, Ref),
loop(S);
- {{sync_get, Addr, Oids}, From, Ref} ->
- d("loop -> received sync_get request with"
- "~n Addr: ~p"
- "~n Oids: ~p", [Addr, Oids]),
- Res = snmpm:g(Id, Addr, Oids),
- reply(From, Res, Ref),
- loop(S);
-
- {{sync_get, Addr, Port, Oids}, From, Ref} ->
- d("loop -> received sync_get request with"
- "~n Addr: ~p"
- "~n Port: ~p"
- "~n Oids: ~p", [Addr, Port, Oids]),
- Res = snmpm:g(Id, Addr, Port, Oids),
- reply(From, Res, Ref),
- loop(S);
-
%%
%% -- (async) get-request --
@@ -500,18 +413,6 @@ loop(#state{parent = Parent, id = Id} = S) ->
reply(From, Res, Ref),
loop(S);
- {{async_get, Addr, Oids}, From, Ref} ->
- d("loop -> received async_get request"),
- Res = snmpm:ag(Id, Addr, Oids),
- reply(From, Res, Ref),
- loop(S);
-
- {{async_get, Addr, Port, Oids}, From, Ref} ->
- d("loop -> received async_get request"),
- Res = snmpm:ag(Id, Addr, Port, Oids),
- reply(From, Res, Ref),
- loop(S);
-
%%
%% -- (sync) get_next-request --
@@ -543,18 +444,6 @@ loop(#state{parent = Parent, id = Id} = S) ->
reply(From, Res, Ref),
loop(S);
- {{sync_get_next, Addr, Oids}, From, Ref} ->
- d("loop -> received sync_get_next request"),
- Res = snmpm:gn(Id, Addr, Oids),
- reply(From, Res, Ref),
- loop(S);
-
- {{sync_get_next, Addr, Port, Oids}, From, Ref} ->
- d("loop -> received sync_get_next request"),
- Res = snmpm:gn(Id, Addr, Port, Oids),
- reply(From, Res, Ref),
- loop(S);
-
%%
%% -- (async) get_next-request --
@@ -586,18 +475,6 @@ loop(#state{parent = Parent, id = Id} = S) ->
reply(From, Res, Ref),
loop(S);
- {{async_get_next, Addr, Oids}, From, Ref} ->
- d("loop -> received async_get_next request"),
- Res = snmpm:agn(Id, Addr, Oids),
- reply(From, Res, Ref),
- loop(S);
-
- {{async_get_next, Addr, Port, Oids}, From, Ref} ->
- d("loop -> received async_get_next request"),
- Res = snmpm:agn(Id, Addr, Port, Oids),
- reply(From, Res, Ref),
- loop(S);
-
%%
%% -- (sync) set-request --
@@ -626,18 +503,6 @@ loop(#state{parent = Parent, id = Id} = S) ->
reply(From, Res, Ref),
loop(S);
- {{sync_set, Addr, VAV}, From, Ref} ->
- d("loop -> received sync_set request"),
- Res = snmpm:s(Id, Addr, VAV),
- reply(From, Res, Ref),
- loop(S);
-
- {{sync_set, Addr, Port, VAV}, From, Ref} ->
- d("loop -> received sync_set request"),
- Res = snmpm:s(Id, Addr, Port, VAV),
- reply(From, Res, Ref),
- loop(S);
-
%%
%% -- (async) set-request --
@@ -666,18 +531,6 @@ loop(#state{parent = Parent, id = Id} = S) ->
reply(From, Res, Ref),
loop(S);
- {{async_set, Addr, VAV}, From, Ref} ->
- d("loop -> received async_set request"),
- Res = snmpm:as(Id, Addr, VAV),
- reply(From, Res, Ref),
- loop(S);
-
- {{async_set, Addr, Port, VAV}, From, Ref} ->
- d("loop -> received async_set request"),
- Res = snmpm:as(Id, Addr, Port, VAV),
- reply(From, Res, Ref),
- loop(S);
-
%%
%% -- (sync) get-bulk-request --
@@ -718,27 +571,6 @@ loop(#state{parent = Parent, id = Id} = S) ->
reply(From, Res, Ref),
loop(S);
- {{sync_get_bulk, Addr, NonRep, MaxRep, Oids}, From, Ref} ->
- d("loop -> received sync_get_bulk request with"
- "~n Addr: ~p"
- "~n NonRep: ~w"
- "~n MaxRep: ~w"
- "~n Oids: ~p", [Addr, NonRep, MaxRep, Oids]),
- Res = snmpm:gb(Id, Addr, NonRep, MaxRep, Oids),
- reply(From, Res, Ref),
- loop(S);
-
- {{sync_get_bulk, Addr, Port, NonRep, MaxRep, Oids}, From, Ref} ->
- d("loop -> received sync_get_bulk request with"
- "~n Addr: ~p"
- "~n Port: ~w"
- "~n NonRep: ~w"
- "~n MaxRep: ~w"
- "~n Oids: ~p", [Addr, Port, NonRep, MaxRep, Oids]),
- Res = snmpm:gb(Id, Addr, Port, NonRep, MaxRep, Oids),
- reply(From, Res, Ref),
- loop(S);
-
%%
%% -- (async) get-bulk-request --
@@ -779,27 +611,6 @@ loop(#state{parent = Parent, id = Id} = S) ->
reply(From, Res, Ref),
loop(S);
- {{async_get_bulk, Addr, NonRep, MaxRep, Oids}, From, Ref} ->
- d("loop -> received async_get_bulk request with"
- "~n Addr: ~p"
- "~n NonRep: ~w"
- "~n MaxRep: ~w"
- "~n Oids: ~p", [Addr, NonRep, MaxRep, Oids]),
- Res = snmpm:agb(Id, Addr, NonRep, MaxRep, Oids),
- reply(From, Res, Ref),
- loop(S);
-
- {{async_get_bulk, Addr, Port, NonRep, MaxRep, Oids}, From, Ref} ->
- d("loop -> received async_get_bulk request with"
- "~n Addr: ~p"
- "~n Port: ~w"
- "~n NonRep: ~w"
- "~n MaxRep: ~w"
- "~n Oids: ~p", [Addr, Port, NonRep, MaxRep, Oids]),
- Res = snmpm:agb(Id, Addr, Port, NonRep, MaxRep, Oids),
- reply(From, Res, Ref),
- loop(S);
-
%%
%% -- logical name translation --
@@ -846,12 +657,6 @@ loop(#state{parent = Parent, id = Id} = S) ->
Parent ! {async_event, ReqId, {pdu, SnmpResponse}},
loop(S);
- %% For backwards compatibillity
- {handle_pdu, _Pid, _Addr, _Port, ReqId, SnmpResponse} ->
- d("loop -> received pdu callback from manager for ~w", [ReqId]),
- Parent ! {async_event, ReqId, {pdu, SnmpResponse}},
- loop(S);
-
{handle_trap, _Pid, TargetName, SnmpTrap} ->
d("loop -> received trap callback from manager for "
"~n ~p",
@@ -860,15 +665,6 @@ loop(#state{parent = Parent, id = Id} = S) ->
Parent ! {async_event, TargetName, {trap, SnmpTrap}},
loop(S);
- %% For backwards compatibillity
- {handle_trap, _Pid, Addr, Port, SnmpTrap} ->
- d("loop -> received trap callback from manager for "
- "~n ~p:~w",
- "~n ~p",
- [Addr, Port, SnmpTrap]),
- Parent ! {async_event, {Addr, Port}, {trap, SnmpTrap}},
- loop(S);
-
{handle_inform, Pid, TargetName, SnmpInform} ->
d("loop -> received inform callback from manager for "
"~n ~p",
@@ -877,15 +673,6 @@ loop(#state{parent = Parent, id = Id} = S) ->
Parent ! {async_event, TargetName, {inform, Pid, SnmpInform}},
loop(S);
- %% For backwards compatibillity
- {handle_inform, Pid, Addr, Port, SnmpInform} ->
- d("loop -> received inform callback from manager for "
- "~n ~p:~w",
- "~n ~p",
- [Addr, Port, SnmpInform]),
- Parent ! {async_event, {Addr, Port}, {inform, Pid, SnmpInform}},
- loop(S);
-
{handle_report, _Pid, TargetName, SnmpReport} ->
d("loop -> received report callback from manager for "
"~n ~p",
@@ -894,15 +681,6 @@ loop(#state{parent = Parent, id = Id} = S) ->
Parent ! {async_event, TargetName, {report, SnmpReport}},
loop(S);
- %% For backwards compatibillity
- {handle_report, _Pid, Addr, Port, SnmpReport} ->
- d("loop -> received report callback from manager for "
- "~n ~p:~w",
- "~n ~p",
- [Addr, Port, SnmpReport]),
- Parent ! {async_event, {Addr, Port}, {report, SnmpReport}},
- loop(S);
-
{'EXIT', Parent, Reason} ->
d("received exit signal from parent: ~n~p", [Reason]),
info("received exit signal from parent: ~n~p", [Reason]),
@@ -981,8 +759,8 @@ handle_error(ReqId, Reason, UserPid) ->
ignore.
-handle_agent(Addr, Port, SnmpInfo, UserPid) ->
- UserPid ! {handle_agent, self(), Addr, Port, SnmpInfo},
+handle_agent(Addr, Port, SnmpInfo, UserPid, UserData) ->
+ UserPid ! {handle_agent, self(), Addr, Port, SnmpInfo, UserData},
ignore.
@@ -990,22 +768,10 @@ handle_pdu(TargetName, ReqId, SnmpResponse, UserPid) ->
UserPid ! {handle_pdu, self(), TargetName, ReqId, SnmpResponse},
ignore.
-%% For backwards compatibillity
-handle_pdu(Addr, Port, ReqId, SnmpResponse, UserPid) ->
- UserPid ! {handle_pdu, self(), Addr, Port, ReqId, SnmpResponse},
- ignore.
-
-
handle_trap(TargetName, SnmpTrap, UserPid) ->
UserPid ! {handle_trap, self(), TargetName, SnmpTrap},
ok.
-%% For backwards compatibillity
-handle_trap(Addr, Port, SnmpTrap, UserPid) ->
- UserPid ! {handle_trap, self(), Addr, Port, SnmpTrap},
- ok.
-
-
handle_inform(TargetName, SnmpInform, UserPid) ->
UserPid ! {handle_inform, self(), TargetName, SnmpInform},
receive
@@ -1015,26 +781,10 @@ handle_inform(TargetName, SnmpInform, UserPid) ->
ok
end.
-%% For backwards compatibillity
-handle_inform(Addr, Port, SnmpInform, UserPid) ->
- UserPid ! {handle_inform, self(), Addr, Port, SnmpInform},
- receive
- {handle_inform_no_response, {Addr, Port}} ->
- no_reply;
- {handle_inform_response, {Addr, Port}} ->
- ok
- end.
-
-
handle_report(TargetName, SnmpReport, UserPid) ->
UserPid ! {handle_report, self(), TargetName, SnmpReport},
ok.
-%% For backwards compatibillity
-handle_report(Addr, Port, SnmpReport, UserPid) ->
- UserPid ! {handle_report, self(), Addr, Port, SnmpReport},
- ok.
-
%%----------------------------------------------------------------------
%% Debug
diff --git a/lib/snmp/test/snmp_manager_user_old.erl b/lib/snmp/test/snmp_manager_user_old.erl
index 6280cef51f..9f951bf64d 100644
--- a/lib/snmp/test/snmp_manager_user_old.erl
+++ b/lib/snmp/test/snmp_manager_user_old.erl
@@ -107,10 +107,20 @@ unregister_agent(Addr, Port) ->
snmpm:unregister_agent(?USER_ID, Addr, Port).
agent_info(Addr, Port, Item) ->
- snmpm:agent_info(?USER_ID, Addr, Port, Item).
+ case snmpm:target_name(Addr, Port) of
+ {ok, TargetName} ->
+ snmpm:agent_info(TargetName, Item);
+ Error ->
+ Error
+ end.
update_agent_info(Addr, Port, Item, Val) ->
- snmpm:update_agent_info(?USER_ID, Addr, Port, Item, Val).
+ case snmpm:target_name(Addr, Port) of
+ {ok, TargetName} ->
+ snmpm:update_agent_info(?USER_ID, TargetName, Item, Val);
+ Error ->
+ Error
+ end.
which_agents() ->
snmpm:which_agents().
@@ -128,73 +138,153 @@ unload_mib(Mib) ->
%% --
sync_get(Addr, Oids) ->
- snmpm:g(?USER_ID, Addr, Oids).
+ case snmpm:target_name(Addr) of
+ {ok, TargetName} ->
+ snmpm:sync_get2(?USER_ID, TargetName, Oids);
+ Error ->
+ Error
+ end.
sync_get(Addr, Port, Oids) ->
- snmpm:g(?USER_ID, Addr, Port, Oids).
+ case snmpm:target_name(Addr, Port) of
+ {ok, TargetName} ->
+ snmpm:sync_get2(?USER_ID, TargetName, Oids);
+ Error ->
+ Error
+ end.
%% --
async_get(Addr, Oids) ->
- snmpm:ag(?USER_ID, Addr, Oids).
+ case snmpm:target_name(Addr) of
+ {ok, TargetName} ->
+ snmpm:async_get2(?USER_ID, TargetName, Oids);
+ Error ->
+ Error
+ end.
async_get(Addr, Port, Oids) ->
- snmpm:ag(?USER_ID, Addr, Port, Oids).
+ case snmpm:target_name(Addr, Port) of
+ {ok, TargetName} ->
+ snmpm:async_get2(?USER_ID, TargetName, Oids);
+ Error ->
+ Error
+ end.
%% --
sync_get_next(Addr, Oids) ->
- snmpm:gn(?USER_ID, Addr, Oids).
+ case snmpm:target_name(Addr) of
+ {ok, TargetName} ->
+ snmpm:sync_get_next2(?USER_ID, TargetName, Oids);
+ Error ->
+ Error
+ end.
sync_get_next(Addr, Port, Oids) ->
- snmpm:gn(?USER_ID, Addr, Port, Oids).
+ case snmpm:target_name(Addr, Port) of
+ {ok, TargetName} ->
+ snmpm:sync_get_next2(?USER_ID, TargetName, Oids);
+ Error ->
+ Error
+ end.
%% --
async_get_next(Addr, Oids) ->
- snmpm:agn(?USER_ID, Addr, Oids).
+ case snmpm:target_name(Addr) of
+ {ok, TargetName} ->
+ snmpm:async_get_next2(?USER_ID, TargetName, Oids);
+ Error ->
+ Error
+ end.
async_get_next(Addr, Port, Oids) ->
- snmpm:agn(?USER_ID, Addr, Port, Oids).
+ case snmpm:target_name(Addr, Port) of
+ {ok, TargetName} ->
+ snmpm:async_get_next2(?USER_ID, TargetName, Oids);
+ Error ->
+ Error
+ end.
%% --
sync_set(Addr, VAV) ->
- snmpm:s(?USER_ID, Addr, VAV).
+ case snmpm:target_name(Addr) of
+ {ok, TargetName} ->
+ snmpm:sync_set2(?USER_ID, TargetName, VAV);
+ Error ->
+ Error
+ end.
sync_set(Addr, Port, VAV) ->
- snmpm:s(?USER_ID, Addr, Port, VAV).
+ case snmpm:target_name(Addr, Port) of
+ {ok, TargetName} ->
+ snmpm:sync_set2(?USER_ID, TargetName, VAV);
+ Error ->
+ Error
+ end.
%% --
async_set(Addr, VAV) ->
- snmpm:as(?USER_ID, Addr, VAV).
+ case snmpm:target_name(Addr) of
+ {ok, TargetName} ->
+ snmpm:async_set2(?USER_ID, TargetName, VAV);
+ Error ->
+ Error
+ end.
async_set(Addr, Port, VAV) ->
- snmpm:as(?USER_ID, Addr, Port, VAV).
+ case snmpm:target_name(Addr, Port) of
+ {ok, TargetName} ->
+ snmpm:async_set2(?USER_ID, TargetName, VAV);
+ Error ->
+ Error
+ end.
%% --
sync_get_bulk(Addr, NonRep, MaxRep, Oids) ->
- snmpm:gb(?USER_ID, Addr, NonRep, MaxRep, Oids).
+ case snmpm:target_name(Addr) of
+ {ok, TargetName} ->
+ snmpm:sync_get_bulk2(?USER_ID, TargetName, NonRep, MaxRep, Oids);
+ Error ->
+ Error
+ end.
sync_get_bulk(Addr, Port, NonRep, MaxRep, Oids) ->
- snmpm:gb(?USER_ID, Addr, Port, NonRep, MaxRep, Oids).
+ case snmpm:target_name(Addr, Port) of
+ {ok, TargetName} ->
+ snmpm:sync_get_bulk2(?USER_ID, TargetName, NonRep, MaxRep, Oids);
+ Error ->
+ Error
+ end.
%% --
async_get_bulk(Addr, NonRep, MaxRep, Oids) ->
- snmpm:agb(?USER_ID, Addr, NonRep, MaxRep, Oids).
+ case snmpm:target_name(Addr) of
+ {ok, TargetName} ->
+ snmpm:async_get_bulk2(?USER_ID, TargetName, NonRep, MaxRep, Oids);
+ Error ->
+ Error
+ end.
async_get_bulk(Addr, Port, NonRep, MaxRep, Oids) ->
- snmpm:agb(?USER_ID, Addr, Port, NonRep, MaxRep, Oids).
+ case snmpm:target_name(Addr, Port) of
+ {ok, TargetName} ->
+ snmpm:async_get_bulk2(?USER_ID, TargetName, NonRep, MaxRep, Oids);
+ Error ->
+ Error
+ end.
%% --
diff --git a/lib/snmp/test/snmp_manager_user_test.erl b/lib/snmp/test/snmp_manager_user_test.erl
index fefa1ad713..41d5c50b19 100644
--- a/lib/snmp/test/snmp_manager_user_test.erl
+++ b/lib/snmp/test/snmp_manager_user_test.erl
@@ -36,7 +36,7 @@
%% -compile(export_all).
-export([
-all/0,groups/0,init_per_group/2,end_per_group/2,
+ all/0,groups/0,init_per_group/2,end_per_group/2,
init_per_testcase/2, end_per_testcase/2,
diff --git a/lib/snmp/test/snmp_pdus_test.erl b/lib/snmp/test/snmp_pdus_test.erl
index 07b6d6b657..7d649b1dad 100644
--- a/lib/snmp/test/snmp_pdus_test.erl
+++ b/lib/snmp/test/snmp_pdus_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -39,6 +39,7 @@
otp7575/1,
otp8563/1,
otp9022/1,
+ otp10132/1,
init_per_testcase/2, end_per_testcase/2
]).
@@ -74,16 +75,16 @@ end_per_testcase(_Case, Config) when is_list(Config) ->
%% Test case definitions
%%======================================================================
all() ->
-[{group, tickets}].
+ [{group, tickets}].
groups() ->
- [{tickets, [], [otp7575, otp8563, otp9022]}].
+ [{tickets, [], [otp7575, otp8563, otp9022, otp10132]}].
init_per_group(_GroupName, Config) ->
- Config.
+ Config.
end_per_group(_GroupName, Config) ->
- Config.
+ Config.
@@ -94,7 +95,7 @@ end_per_group(_GroupName, Config) ->
%%======================================================================
otp7575(suite) -> [];
-otp7575(doc) -> ["OTP-7575"];
+otp7575(doc) -> ["OTP-7575 - Message version"];
otp7575(Config) when is_list(Config) ->
io:format("attempt to decode message with valid version~n", []),
MsgWithOkVersion = <<48,39,2,1,0,4,6,112,117,98,108,105,99,160,26,2,2,1,49,2,1,0,2,1,0,48,14,48,12,6,8,43,6,1,2,1,1,5,0,5,0>>,
@@ -127,48 +128,55 @@ otp7575(Config) when is_list(Config) ->
otp8563(suite) -> [];
-otp8563(doc) -> ["OTP-8563"];
+otp8563(doc) -> ["OTP-8563 - Counter64"];
otp8563(Config) when is_list(Config) ->
Val1 = 16#7fffffffffffffff,
- io:format("try encode and decode ~w~n", [Val1]),
+ io:format("try encode and decode value 1: ~w (0x~.16b)~n", [Val1, Val1]),
Enc1 = snmp_pdus:enc_value('Counter64', Val1),
+ io:format(" => ~w~n", [Enc1]),
{{'Counter64', Val1}, []} = snmp_pdus:dec_value(Enc1),
Val2 = Val1 + 1,
- io:format("try encode and decode ~w~n", [Val2]),
+ io:format("try encode and decode value 2: ~w (0x~.16b)~n", [Val2, Val2]),
Enc2 = snmp_pdus:enc_value('Counter64', Val2),
+ io:format(" => ~w~n", [Enc2]),
{{'Counter64', Val2}, []} = snmp_pdus:dec_value(Enc2),
Val3 = Val2 + 1,
- io:format("try encode and decode ~w~n", [Val3]),
+ io:format("try encode and decode valule 3: ~w (0x~.16b)~n", [Val3, Val3]),
Enc3 = snmp_pdus:enc_value('Counter64', Val3),
+ io:format(" => ~w~n", [Enc3]),
{{'Counter64', Val3}, []} = snmp_pdus:dec_value(Enc3),
Val4 = 16#fffffffffffffffe,
- io:format("try encode and decode ~w~n", [Val4]),
+ io:format("try encode and decode value 4: ~w (0x~.16b)~n", [Val4, Val4]),
Enc4 = snmp_pdus:enc_value('Counter64', Val4),
+ io:format(" => ~w~n", [Enc4]),
{{'Counter64', Val4}, []} = snmp_pdus:dec_value(Enc4),
Val5 = Val4 + 1,
- io:format("try encode and decode ~w~n", [Val5]),
+ io:format("try encode and decode value 5: ~w (0x~.16b)~n", [Val5, Val5]),
Enc5 = snmp_pdus:enc_value('Counter64', Val5),
+ io:format(" => ~w~n", [Enc5]),
{{'Counter64', Val5}, []} = snmp_pdus:dec_value(Enc5),
Val6 = 16#ffffffffffffffff + 1,
- io:format("try and fail to encode ~w~n", [Val6]),
+ io:format("try and fail to encode value 6: ~w (0x~.16b)~n", [Val6, Val6]),
case (catch snmp_pdus:enc_value('Counter64', Val6)) of
{'EXIT', {error, {bad_counter64, Val6}}} ->
ok;
Unexpected6 ->
+ io:format(" => ~w~n", [Unexpected6]),
exit({unexpected_encode_result, Unexpected6, Val6})
end,
Val7 = -1,
- io:format("try and fail to encode ~w~n", [Val7]),
+ io:format("try and fail to encode value 7: ~w~n", [Val7]),
case (catch snmp_pdus:enc_value('Counter64', Val7)) of
{'EXIT', {error, {bad_counter64, Val7}}} ->
ok;
Unexpected7 ->
+ io:format(" => ~w~n", [Unexpected7]),
exit({unexpected_encode_result, Unexpected7, Val7})
end,
@@ -176,51 +184,151 @@ otp8563(Config) when is_list(Config) ->
otp9022(suite) -> [];
-otp9022(doc) -> ["OTP-9022"];
+otp9022(doc) -> ["OTP-9022 - Counter32"];
otp9022(Config) when is_list(Config) ->
- Val1 = 16#7fffffff,
- io:format("try encode and decode ~w~n", [Val1]),
+ Val0 = 2908389204,
+ io:format("try encode and decode value 0: ~w (0x~.16b)~n", [Val0, Val0]),
+ Enc0 = snmp_pdus:enc_value('Counter32', Val0),
+ io:format(" => ~w~n", [Enc0]),
+ {{'Counter32', Val0}, []} = snmp_pdus:dec_value(Enc0),
+
+ Val1 = 0,
+ io:format("try encode and decode value 1: ~w (0x~.16b)~n", [Val1, Val1]),
Enc1 = snmp_pdus:enc_value('Counter32', Val1),
+ io:format(" => ~w~n", [Enc1]),
{{'Counter32', Val1}, []} = snmp_pdus:dec_value(Enc1),
Val2 = Val1 + 1,
- io:format("try encode and decode ~w~n", [Val2]),
+ io:format("try encode and decode value 2: ~w (0x~.16b)~n", [Val2, Val2]),
Enc2 = snmp_pdus:enc_value('Counter32', Val2),
+ io:format(" => ~w~n", [Enc2]),
{{'Counter32', Val2}, []} = snmp_pdus:dec_value(Enc2),
- Val3 = Val2 + 1,
- io:format("try encode and decode ~w~n", [Val3]),
+ Val3 = 16#7ffffffe,
+ io:format("try encode and decode value 3: ~w (0x~.16b)~n", [Val3, Val3]),
Enc3 = snmp_pdus:enc_value('Counter32', Val3),
+ io:format(" => ~w~n", [Enc3]),
{{'Counter32', Val3}, []} = snmp_pdus:dec_value(Enc3),
- Val4 = 16#fffffffe,
- io:format("try encode and decode ~w~n", [Val4]),
+ Val4 = Val3 + 1,
+ io:format("try encode and decode value 4: ~w (0x~.16b)~n", [Val4, Val4]),
Enc4 = snmp_pdus:enc_value('Counter32', Val4),
+ io:format(" => ~w~n", [Enc4]),
{{'Counter32', Val4}, []} = snmp_pdus:dec_value(Enc4),
Val5 = Val4 + 1,
- io:format("try encode and decode ~w~n", [Val5]),
+ io:format("try encode and decode value 5: ~w (0x~.16b)~n", [Val5, Val5]),
Enc5 = snmp_pdus:enc_value('Counter32', Val5),
+ io:format(" => ~w~n", [Enc5]),
{{'Counter32', Val5}, []} = snmp_pdus:dec_value(Enc5),
- Val6 = 16#ffffffff + 1,
- io:format("try and fail to encode ~w~n", [Val6]),
- case (catch snmp_pdus:enc_value('Counter32', Val6)) of
- {'EXIT', {error, {bad_counter32, Val6}}} ->
+ Val6 = 16#fffffffe,
+ io:format("try encode and decode value 6: ~w (0x~.16b)~n", [Val6, Val6]),
+ Enc6 = snmp_pdus:enc_value('Counter32', Val6),
+ io:format(" => ~w~n", [Enc6]),
+ {{'Counter32', Val6}, []} = snmp_pdus:dec_value(Enc6),
+
+ Val7 = Val6 + 1,
+ io:format("try encode and decode value 7: ~w (0x~.16b)~n", [Val7, Val7]),
+ Enc7 = snmp_pdus:enc_value('Counter32', Val7),
+ io:format(" => ~w~n", [Enc7]),
+ {{'Counter32', Val7}, []} = snmp_pdus:dec_value(Enc7),
+
+ Val8 = 16#ffffffff + 1,
+ io:format("try and fail to encode value 8: ~w (0x~.16b)~n", [Val8, Val8]),
+ case (catch snmp_pdus:enc_value('Counter32', Val8)) of
+ {'EXIT', {error, {bad_counter32, Val8}}} ->
ok;
- Unexpected6 ->
- exit({unexpected_encode_result, Unexpected6, Val6})
+ Unexpected8 ->
+ io:format(" => ~w~n", [Unexpected8]),
+ exit({unexpected_encode_result, Unexpected8, Val8})
end,
- Val7 = -1,
- io:format("try and fail to encode ~w~n", [Val7]),
- case (catch snmp_pdus:enc_value('Counter32', Val7)) of
- {'EXIT', {error, {bad_counter32, Val7}}} ->
+ Val9 = -1,
+ io:format("try and fail to encode value 9: ~w~n", [Val9]),
+ case (catch snmp_pdus:enc_value('Counter32', Val9)) of
+ {'EXIT', {error, {bad_counter32, Val9}}} ->
ok;
- Unexpected7 ->
- exit({unexpected_encode_result, Unexpected7, Val7})
+ Unexpected9 ->
+ io:format(" => ~w~n", [Unexpected9]),
+ exit({unexpected_encode_result, Unexpected9, Val9})
+ end,
+
+ ok.
+
+
+otp10132(suite) -> [];
+otp10132(doc) -> ["OTP-10132 - TimeTicks"];
+otp10132(Config) when is_list(Config) ->
+ Val0 = 2159001034,
+ io:format("try encode and decode value 0: ~w (0x~.16b)~n", [Val0, Val0]),
+ Enc0 = snmp_pdus:enc_value('TimeTicks', Val0),
+ io:format(" => ~w~n", [Enc0]),
+ {{'TimeTicks', Val0}, []} = snmp_pdus:dec_value(Enc0),
+
+ Val1 = 0,
+ io:format("try encode and decode value 1: ~w (0x~.16b)~n", [Val1, Val1]),
+ Enc1 = snmp_pdus:enc_value('TimeTicks', Val1),
+ io:format(" => ~w~n", [Enc1]),
+ {{'TimeTicks', Val1}, []} = snmp_pdus:dec_value(Enc1),
+
+ Val2 = Val1 + 1,
+ io:format("try encode and decode value 2: ~w (0x~.16b)~n", [Val2, Val2]),
+ Enc2 = snmp_pdus:enc_value('TimeTicks', Val2),
+ io:format(" => ~w~n", [Enc2]),
+ {{'TimeTicks', Val2}, []} = snmp_pdus:dec_value(Enc2),
+
+ Val3 = 16#7ffffffe,
+ io:format("try encode and decode value 3: ~w (0x~.16b)~n", [Val3, Val3]),
+ Enc3 = snmp_pdus:enc_value('TimeTicks', Val3),
+ io:format(" => ~w~n", [Enc3]),
+ {{'TimeTicks', Val3}, []} = snmp_pdus:dec_value(Enc3),
+
+ Val4 = Val3 + 1,
+ io:format("try encode and decode value 4: ~w (0x~.16b)~n", [Val4, Val4]),
+ Enc4 = snmp_pdus:enc_value('TimeTicks', Val4),
+ io:format(" => ~w~n", [Enc4]),
+ {{'TimeTicks', Val4}, []} = snmp_pdus:dec_value(Enc4),
+
+ Val5 = Val4 + 1,
+ io:format("try encode and decode value 5: ~w (0x~.16b)~n", [Val5, Val5]),
+ Enc5 = snmp_pdus:enc_value('TimeTicks', Val5),
+ io:format(" => ~w~n", [Enc5]),
+ {{'TimeTicks', Val5}, []} = snmp_pdus:dec_value(Enc5),
+
+ Val6 = 16#fffffffe,
+ io:format("try encode and decode value 6: ~w (0x~.16b)~n", [Val6, Val6]),
+ Enc6 = snmp_pdus:enc_value('TimeTicks', Val6),
+ io:format(" => ~w~n", [Enc6]),
+ {{'TimeTicks', Val6}, []} = snmp_pdus:dec_value(Enc6),
+
+ Val7 = Val6 + 1,
+ io:format("try encode and decode value 7: ~w (0x~.16b)~n", [Val7, Val7]),
+ Enc7 = snmp_pdus:enc_value('TimeTicks', Val7),
+ io:format(" => ~w~n", [Enc7]),
+ {{'TimeTicks', Val7}, []} = snmp_pdus:dec_value(Enc7),
+
+ Val8 = Val7 + 1,
+ io:format("try and fail to encode value 8: ~w (0x~.16b)~n", [Val8, Val8]),
+ case (catch snmp_pdus:enc_value('TimeTicks', Val8)) of
+ {'EXIT', {error, {bad_timeticks, Val8}}} ->
+ ok;
+ Unexpected8 ->
+ io:format(" => ~w~n", [Unexpected8]),
+ exit({unexpected_encode_result, Unexpected8, Val8})
end,
+ Val9 = -1,
+ io:format("try and fail to encode value 9: ~w~n", [Val9]),
+ case (catch snmp_pdus:enc_value('TimeTicks', Val9)) of
+ {'EXIT', {error, {bad_timeticks, Val9}}} ->
+ ok;
+ Unexpected9 ->
+ io:format(" => ~w~n", [Unexpected9]),
+ exit({unexpected_encode_result, Unexpected9, Val9})
+ end,
+
+ io:format("done~n", []),
ok.
diff --git a/lib/snmp/test/snmp_test_lib.erl b/lib/snmp/test/snmp_test_lib.erl
index 26115a0c74..f0abae73e8 100644
--- a/lib/snmp/test/snmp_test_lib.erl
+++ b/lib/snmp/test/snmp_test_lib.erl
@@ -324,7 +324,7 @@ fail(Reason, Mod, Line) ->
skip(Reason, Module, Line) ->
String = lists:flatten(io_lib:format("Skipping ~p(~p): ~p~n",
[Module, Line, Reason])),
- exit({skipped, String}).
+ exit({skip, String}).
%% ----------------------------------------------------------------
@@ -516,8 +516,6 @@ warning_msg(F, A) ->
timeout(T) ->
trunc(timeout(T, os:type())).
-timeout(T, vxworks) ->
- 5 * T * timetrap_scale_factor();
timeout(T, _) ->
T * timetrap_scale_factor().
diff --git a/lib/snmp/test/snmp_test_manager.erl b/lib/snmp/test/snmp_test_manager.erl
index 4cc6d36acc..0df2350d58 100644
--- a/lib/snmp/test/snmp_test_manager.erl
+++ b/lib/snmp/test/snmp_test_manager.erl
@@ -43,7 +43,7 @@
%% Manager callback API:
-export([
handle_error/3,
- handle_agent/4,
+ handle_agent/5,
handle_pdu/4,
handle_trap/3,
handle_inform/3,
@@ -239,7 +239,7 @@ handle_info({snmp_error, ReqId, Reason},
P ! {snmp_error, ReqId, Reason},
{noreply, State};
-handle_info({snmp_agent, Addr, Port, Info, Pid},
+handle_info({snmp_agent, Addr, Port, Info, Pid, _UserData},
#state{parent = P} = State) ->
error_msg("detected new agent: "
"~n Addr: ~w"
@@ -326,8 +326,8 @@ handle_error(ReqId, Reason, Pid) ->
ignore.
-handle_agent(Addr, Port, SnmpInfo, Pid) ->
- Pid ! {snmp_agent, Addr, Port, SnmpInfo, self()},
+handle_agent(Addr, Port, SnmpInfo, Pid, UserData) ->
+ Pid ! {snmp_agent, Addr, Port, SnmpInfo, self(), UserData},
receive
{snmp_agent_reply, Reply, Pid} ->
Reply
diff --git a/lib/snmp/test/snmp_test_mgr.erl b/lib/snmp/test/snmp_test_mgr.erl
index 499cf7abcf..40fcbce8f1 100644
--- a/lib/snmp/test/snmp_test_mgr.erl
+++ b/lib/snmp/test/snmp_test_mgr.erl
@@ -161,7 +161,6 @@ get_timeout() ->
get_timeout(os:type())
end.
-get_timeout(vxworks) -> 7000;
get_timeout(_) -> 3500.
%%----------------------------------------------------------------------
diff --git a/lib/snmp/vsn.mk b/lib/snmp/vsn.mk
index 36b9764bc8..8145e415f3 100644
--- a/lib/snmp/vsn.mk
+++ b/lib/snmp/vsn.mk
@@ -18,6 +18,6 @@
# %CopyrightEnd%
APPLICATION = snmp
-SNMP_VSN = 4.22
+SNMP_VSN = 4.23
PRE_VSN =
APP_VSN = "$(APPLICATION)-$(SNMP_VSN)$(PRE_VSN)"
diff --git a/lib/ssh/doc/html/SSH_protocols.png b/lib/ssh/doc/html/SSH_protocols.png
new file mode 100644
index 0000000000..145c96c4cd
--- /dev/null
+++ b/lib/ssh/doc/html/SSH_protocols.png
Binary files differ
diff --git a/lib/ssh/doc/man6/.gitignore b/lib/ssh/doc/man6/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/ssh/doc/man6/.gitignore
diff --git a/lib/ssh/doc/src/Makefile b/lib/ssh/doc/src/Makefile
index 38782a3b00..0e79d9979f 100644
--- a/lib/ssh/doc/src/Makefile
+++ b/lib/ssh/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2004-2011. All Rights Reserved.
+# Copyright Ericsson AB 2004-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -37,21 +37,30 @@ RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
# Target Specs
# ----------------------------------------------------
XML_APPLICATION_FILES = ref_man.xml
-XML_REF3_FILES = \
- ssh.xml \
+XML_REF3_FILES = ssh.xml \
ssh_channel.xml \
- ssh_connection.xml\
+ ssh_connection.xml \
+ ssh_client_key_api.xml \
+ ssh_server_key_api.xml \
ssh_sftp.xml \
ssh_sftpd.xml \
-XML_PART_FILES = part_notes.xml
-XML_CHAPTER_FILES = notes.xml
+XML_REF6_FILES = ssh_app.xml
+
+XML_PART_FILES = part_notes.xml \
+ usersguide.xml
+XML_CHAPTER_FILES = notes.xml \
+ introduction.xml \
+ ssh_protocol.xml \
+ using_ssh.xml
BOOK_FILES = book.xml
-XML_FILES = $(BOOK_FILES) $(XML_APPLICATION_FILES) $(XML_REF3_FILES) \
+XML_FILES = $(BOOK_FILES) $(XML_APPLICATION_FILES) $(XML_REF3_FILES) $(XML_REF6_FILES)\
$(XML_PART_FILES) $(XML_CHAPTER_FILES)
+IMAGE_FILES = SSH_protocols.png
+
# ----------------------------------------------------
HTML_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \
@@ -62,10 +71,12 @@ EXTRA_FILES = \
$(DEFAULT_GIF_FILES) \
$(DEFAULT_HTML_FILES) \
$(XML_REF3_FILES:%.xml=$(HTMLDIR)/%.html) \
+ $(XML_REF6_FILES:%.xml=$(HTMLDIR)/%.html) \
$(XML_CHAPTER_FILES:%.xml=$(HTMLDIR)/%.html)
MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3)
+MAN6_FILES = $(XML_REF6_FILES:%_app.xml=$(MAN6DIR)/%.6)
HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
@@ -80,16 +91,19 @@ DVIPS_FLAGS +=
# ----------------------------------------------------
# Targets
# ----------------------------------------------------
-$(HTMLDIR)/%.gif: %.gif
+$(HTMLDIR)/%.png: %.png
$(INSTALL_DATA) $< $@
docs: pdf html man
$(TOP_PDF_FILE): $(XML_FILES)
+images: $(IMAGE_FILES:%=$(HTMLDIR)/%)
+
pdf: $(TOP_PDF_FILE)
-html: $(HTML_REF_MAN_FILE)
+html: images $(HTML_REF_MAN_FILE)
+
clean clean_docs:
rm -rf $(HTMLDIR)/*
@@ -97,7 +111,7 @@ clean clean_docs:
rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
rm -f errs core *~
-man: $(MAN3_FILES)
+man: $(MAN3_FILES) $(MAN6_FILES)
debug opt:
@@ -117,5 +131,8 @@ release_docs_spec: docs
$(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
$(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
$(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3"
+ $(INSTALL_DIR) "$(RELEASE_PATH)/man/man6"
+ $(INSTALL_DATA) $(MAN6_FILES) "$(RELEASE_PATH)/man/man6"
+
release_spec:
diff --git a/lib/ssh/doc/src/SSH_protocols.png b/lib/ssh/doc/src/SSH_protocols.png
new file mode 100644
index 0000000000..145c96c4cd
--- /dev/null
+++ b/lib/ssh/doc/src/SSH_protocols.png
Binary files differ
diff --git a/lib/ssh/doc/src/book.xml b/lib/ssh/doc/src/book.xml
index fcec1d6f70..3c2375f96d 100644
--- a/lib/ssh/doc/src/book.xml
+++ b/lib/ssh/doc/src/book.xml
@@ -1,10 +1,10 @@
-<?xml version="1.0" encoding="latin1" ?>
+<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE book SYSTEM "book.dtd">
<book xmlns:xi="http://www.w3.org/2001/XInclude">
<header titlestyle="normal">
<copyright>
- <year>2005</year><year>2010</year>
+ <year>2005</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -34,6 +34,9 @@
<preamble>
<contents level="2"></contents>
</preamble>
+ <parts lift="yes">
+ <xi:include href="usersguide.xml"/>
+ </parts>
<applications>
<xi:include href="ref_man.xml"/>
</applications>
diff --git a/lib/ssh/doc/src/fascicules.xml b/lib/ssh/doc/src/fascicules.xml
index 43090b4aed..069d9002e0 100644
--- a/lib/ssh/doc/src/fascicules.xml
+++ b/lib/ssh/doc/src/fascicules.xml
@@ -1,7 +1,10 @@
-<?xml version="1.0" encoding="latin1" ?>
+<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE fascicules SYSTEM "fascicules.dtd">
<fascicules>
+ <fascicule file="usersguide" href="usersguide_frame.html" entry="no">
+ User's Guide
+ </fascicule>
<fascicule file="ref_man" href="ref_man_frame.html" entry="yes">
Reference Manual
</fascicule>
diff --git a/lib/ssh/doc/src/introduction.xml b/lib/ssh/doc/src/introduction.xml
new file mode 100644
index 0000000000..aac8de0f76
--- /dev/null
+++ b/lib/ssh/doc/src/introduction.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="iso-8859-1" ?>
+<!DOCTYPE chapter SYSTEM "chapter.dtd">
+
+<chapter>
+ <header>
+ <copyright>
+ <year>2012</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.
+
+ The Initial Developer of the Original Code is Ericsson AB.
+ </legalnotice>
+
+ <title>Introduction</title>
+ <prepared>OTP team</prepared>
+ <file>introduction.xml</file>
+ </header>
+
+ <section>
+ <title>Purpose</title>
+
+ <p>Secure Shell (SSH) is a protocol for secure remote login and
+ other secure network services over an insecure network. SSH
+ provides a single, full-duplex, byte-oriented connection between
+ client and server. The protocol also provides privacy, integrity,
+ server authentication and man-in-the-middle protection.</p>
+
+ <p>The Erlang SSH application is an implementation of the SSH
+ protocol in Erlang which offers API functions to write customized
+ SSH clients and servers as well as making the Erlang shell
+ available via SSH. Also included in the SSH application are an
+ SFTP (SSH File Transfer Protocol) client <seealso
+ marker="ssh_sftp">ssh_sftp</seealso> and server <seealso
+ marker="ssh_sftp">ssh_sftpd</seealso>.</p>
+ </section>
+
+ <section>
+ <title>Prerequisites</title>
+ <p>It is assumed that the reader is familiar with the concepts of <seealso marker="doc/design_principals:users_guide">OTP</seealso>
+ and has a basic understanding of <url href="http://en.wikipedia.org/wiki/Public-key_cryptography">public keys</url>.</p>
+ </section>
+
+</chapter>
diff --git a/lib/ssh/doc/src/notes.xml b/lib/ssh/doc/src/notes.xml
index a85cada732..e58d4e2c36 100644
--- a/lib/ssh/doc/src/notes.xml
+++ b/lib/ssh/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>2004</year><year>2011</year>
+ <year>2004</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -29,6 +29,120 @@
<file>notes.xml</file>
</header>
+<section><title>Ssh 2.1.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ SSH quiet mode</p>
+ <p>
+ A new option to ssh:connect/3,4, quiet_mode. If true, the
+ client will not print out anything on authorization.</p>
+ <p>
+ Own Id: OTP-10429 Aux Id: kunagi-273 [184] </p>
+ </item>
+ <item>
+ <p>
+ Restrict which key algorithms to use</p>
+ <p>
+ A new option to ssh:connect/3,4 is introduced,
+ public_key_algs, where you can restrict which key
+ algorithms to use and in which order to try them.</p>
+ <p>
+ Own Id: OTP-10498 Aux Id: kunagi-289 [200] </p>
+ </item>
+ <item>
+ <p>
+ Confidentiality of client password</p>
+ <p>
+ Unsets clients password after authentication.</p>
+ <p>
+ Own Id: OTP-10511 Aux Id: kunagi-292 [203] </p>
+ </item>
+ <item>
+ <p>
+ Fixed user interaction for SSH</p>
+ <p>
+ It's now available to accept hosts and input password</p>
+ <p>
+ Own Id: OTP-10513 Aux Id: kunagi-293 [204] </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Ssh 2.1.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Ssh now only sends one channel close message under all
+ circumstances, before it would sometimes incorrectly send
+ two.</p>
+ <p>
+ Own Id: OTP-10060</p>
+ </item>
+ <item>
+ <p>
+ The options check mistreated the ip_v6_disable-option,
+ and did not handle some, at the moment, undocumented
+ options correctly.</p>
+ <p>
+ Own Id: OTP-10061</p>
+ </item>
+ <item>
+ <p>
+ The channel id in a channel failure message, sent to the
+ peer, is now in all cases the remote channel id</p>
+ <p>
+ Own Id: OTP-10062</p>
+ </item>
+ <item>
+ <p>
+ Improved handling of multiple closes to avoid occasional
+ crashes when a channel is closed more than once.</p>
+ <p>
+ Own Id: OTP-10112</p>
+ </item>
+ <item>
+ <p>
+ Fix lib/src/test/ssh_basic_SUITE.erl to fix IPv6 option
+ typos</p>
+ <p>
+ Fixed incorrect option "ipv6_disable" to "ipv6_disabled"
+ as documented in the ssh manual.</p>
+ <p>
+ Own Id: OTP-10219</p>
+ </item>
+ <item>
+ <p>
+ SSH: Make "auth_methods" server option re-usable</p>
+ <p>
+ The 'auth_methods' option is used by the server side of
+ the SSH code to tell a connecting SSH client about the
+ authentication methods that are supported by the server.
+ The code still extracts and handles the 'auth_methods'
+ option from Opts in appropriate places, but the Opts
+ checking code in ssh.erl didn't allow that option to be
+ specified.</p>
+ <p>
+ Own Id: OTP-10224</p>
+ </item>
+ <item>
+ <p>
+ Use the correct channel id when adjusting the channel
+ window</p>
+ <p>
+ Own Id: OTP-10232</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Ssh 2.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
@@ -666,7 +780,7 @@
<list>
<item>
<p>
- Ssh timeouts will now behave as expected e.i. defaults to
+ Ssh timeouts will now behave as expected i.e. defaults to
infinity only the user of the ssh application can know of
a reasonable timeout value for their application.</p>
<p>
@@ -719,7 +833,7 @@
caused the ssh daemon to close the connections to all
currently logged in users if one user logged out. Another
problem related to the supervision tree caused the closing
- down of clients to leak processes e.i. all processes was
+ down of clients to leak processes i.e. all processes was
not shutdown correctly.</p>
<p>
Own Id: OTP-7676</p>
@@ -811,7 +925,7 @@
slightly changes the options to the API function
ssh:daemon/[1,2,3] deprecating all no longer documented
options. Note that the new API enforces the "logical way"
- of using the old API e.i. making the subsystem process
+ of using the old API i.e. making the subsystem process
part of the ssh applications supervisor tree, so missuses
of the old API are not compatible with the new API.</p>
<p>
diff --git a/lib/ssh/doc/src/ref_man.xml b/lib/ssh/doc/src/ref_man.xml
index 9ab56b28ec..88203b5034 100644
--- a/lib/ssh/doc/src/ref_man.xml
+++ b/lib/ssh/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>2004</year><year>2010</year>
+ <year>2004</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -22,19 +22,22 @@
</legalnotice>
<title>SSH Reference Manual</title>
- <prepared>Jakob Cederlund</prepared>
- <docno></docno>
+ <prepared>OTP</prepared>
<date>2007-10-06</date>
<rev>%VSN%</rev>
- <file>application.sgml</file>
+ <file>ref_man.xml</file>
</header>
<description>
<p>The SSH application is an erlang implementation of the
- secure shell protocol.</p>
+ secure shell protocol (SSH) as defined by RFC 4250 - 4254</p>
+
</description>
+ <xi:include href="ssh_app.xml"/>
<xi:include href="ssh.xml"/>
<xi:include href="ssh_channel.xml"/>
<xi:include href="ssh_connection.xml"/>
+ <xi:include href="ssh_client_key_api.xml"/>
+ <xi:include href="ssh_server_key_api.xml"/>
<xi:include href="ssh_sftp.xml"/>
<xi:include href="ssh_sftpd.xml"/>
</application>
diff --git a/lib/ssh/doc/src/ssh.xml b/lib/ssh/doc/src/ssh.xml
index e30c6f1ccc..7f7d887d5e 100644
--- a/lib/ssh/doc/src/ssh.xml
+++ b/lib/ssh/doc/src/ssh.xml
@@ -22,13 +22,7 @@
</legalnotice>
<title>ssh</title>
- <prepared>Ingela Anderton Andin</prepared>
- <responsible>H&aring;kan Mattsson</responsible>
- <docno></docno>
- <approved>H&aring;kan Mattsson</approved>
- <checked></checked>
<date>2007-10-06</date>
- <rev>PA1</rev>
</header>
<module>ssh</module>
<modulesummary>Main API of the SSH application</modulesummary>
@@ -40,80 +34,85 @@
<title>SSH</title>
<list type="bulleted">
- <item>ssh requires the crypto and public_key applications.</item>
- <item>Supported SSH-version is 2.0 </item>
- <item>Currently supports only a minimum of mac and encryption algorithms i.e.
- hmac-sha1, and aes128-cb and 3des-cbc.</item>
+ <item>SSH requires the crypto and public_key applications.</item>
+ <item>Supported SSH version is 2.0 </item>
+ <item>Supported MAC algorithms: hmac-sha1</item>
+ <item>Supported encryption algorithms: aes128-cb and 3des-cbc</item>
</list>
</section>
<section>
- <title>COMMON DATA TYPES </title>
+ <title>DATA TYPES </title>
<p>Type definitions that are used more than once in
- this module:</p>
+ this module and/or abstractions to indicate the intended use of the data
+ type:</p>
<p><c>boolean() = true | false </c></p>
- <p><c>string() = list of ASCII characters</c></p>
+ <p><c>string() = [byte()]</c></p>
<p><c>ssh_daemon_ref() - opaque to the user
returned by ssh:daemon/[1,2,3]</c></p>
<p><c>ssh_connection_ref() - opaque to the user
returned by ssh:connect/3</c></p>
<p><c>ip_address() - {N1,N2,N3,N4} % IPv4 |
{K1,K2,K3,K4,K5,K6,K7,K8} % IPv6</c></p>
- <p><c>subsystem_spec() = {subsystem_name(), {channel_callback(), channel_init_args()}} </c></p>
+ <p><c>subsystem_spec() = {subsystem_name(),
+ {channel_callback(), channel_init_args()}} </c></p>
<p><c>subsystem_name() = string() </c></p>
<p><c>channel_callback() = atom() - Name of the erlang module
implementing the subsystem using the ssh_channel behavior see</c>
<seealso marker="ssh_channel">ssh_channel(3)</seealso></p>
<p><c>channel_init_args() = list()</c></p>
</section>
-
+
<funcs>
<func>
<name>close(ConnectionRef) -> ok </name>
- <fsummary>Closes a ssh connection</fsummary>
+ <fsummary>Closes an SSH connection</fsummary>
<type>
<v>ConnectionRef = ssh_connection_ref()</v>
</type>
- <desc><p>Closes a ssh connection.</p>
+ <desc><p>Closes an SSH connection.</p>
</desc>
</func>
<func>
<name>connect(Host, Port, Options) -> </name>
- <name>connect(Host, Port, Options, Timeout) -> {ok, ssh_connection_ref()}
- | {error, Reason}</name>
+ <name>connect(Host, Port, Options, Timeout) -> {ok,
+ ssh_connection_ref()} | {error, Reason}</name>
<fsummary>Connect to an ssh server.</fsummary>
<type>
<v>Host = string()</v>
<v>Port = integer()</v>
- <d>The default is <c><![CDATA[22]]></c>, the registered port for SSH.</d>
+ <d>The default is <c><![CDATA[22]]></c>, the assigned well known port
+ number for SSH.</d>
<v>Options = [{Option, Value}]</v>
<v>Timeout = infinity | integer(milliseconds)</v>
</type>
<desc>
- <p>Connects to an SSH server. No channel is started this is done
+ <p>Connects to an SSH server. No channel is started. This is done
by calling ssh_connect:session_channel/2.</p>
<p>Options are:</p>
<taglist>
<tag><c><![CDATA[{user_dir, string()}]]></c></tag>
<item>
- <p>Sets the user directory e.i. the directory containing
+ <p>Sets the user directory i.e. the directory containing
ssh configuration files for the user such as
- <c><![CDATA[known_hosts]]></c>, <c><![CDATA[id_rsa, id_dsa]]></c> and
- <c><![CDATA[authorized_key]]></c>. Defaults to the directory normally
- referred to as <c><![CDATA[~/.ssh]]></c> </p>
+ <c><![CDATA[known_hosts]]></c>, <c><![CDATA[id_rsa,
+ id_dsa]]></c> and
+ <c><![CDATA[authorized_key]]></c>. Defaults to the
+ directory normally referred to as
+ <c><![CDATA[~/.ssh]]></c> </p>
</item>
<tag><c><![CDATA[{dsa_pass_phrase, string()}]]></c></tag>
<item>
- <p>If the user dsa key is protected by a pass phrase it can be
+ <p>If the user dsa key is protected by a passphrase it can be
supplied with this option.
</p>
</item>
<tag><c><![CDATA[{rsa_pass_phrase, string()}]]></c></tag>
<item>
- <p>If the user rsa key is protected by a pass phrase it can be
+ <p>If the user rsa key is protected by a passphrase it can be
supplied with this option.
</p>
</item>
@@ -135,20 +134,26 @@
password. Do note that it may not always be desirable to use
those options from a security point of view.</p>
</item>
- <tag><c><![CDATA[{public_key_alg, ssh_rsa | ssh_dsa}]]></c></tag>
+ <tag><c><![CDATA[{public_key_alg, 'ssh-rsa' | 'ssh-dss'}]]></c></tag>
<item>
<p>Sets the preferred public key algorithm to use for user
- authentication. If the the preferred algorithm fails of
+ authentication. If the the preferred algorithm fails for
some reason, the other algorithm is tried. The default is
to try <c><![CDATA[ssh_rsa]]></c> first.</p>
</item>
+ <tag><c><![CDATA[{pref_public_key_algs, list()}]]></c></tag>
+ <item>
+ <p>List of public key algorithms to try to use, 'ssh-rsa' and 'ssh-dss' available.
+ Will override <c><![CDATA[{public_key_alg, 'ssh-rsa' | 'ssh-dss'}]]></c></p>
+ </item>
<tag><c><![CDATA[{connect_timeout, timeout()}]]></c></tag>
<item>
- <p>Sets a timeout on the transport layer connection. Defaults to infinity.</p>
+ <p>Sets a timeout on the transport layer
+ connection. Defaults to <c>infinity</c>.</p>
</item>
- <tag><c><![CDATA[{user, String}]]></c></tag>
+ <tag><c><![CDATA[{user, string()}]]></c></tag>
<item>
- <p>Provide a user name. If this option is not given, ssh
+ <p>Provides a user name. If this option is not given, ssh
reads from the environment (<c><![CDATA[LOGNAME]]></c> or
<c><![CDATA[USER]]></c> on unix,
<c><![CDATA[USERNAME]]></c> on Windows).</p>
@@ -160,37 +165,39 @@
password if the password authentication method is
attempted.</p>
</item>
- <tag><c><![CDATA[{user_auth, Fun/3}]]></c></tag>
- <item>
- <p>Provide a fun for password authentication. The fun
- will be called as <c><![CDATA[fun(User, Password, Opts)]]></c> and
- should return <c><![CDATA[true]]></c> or <c><![CDATA[false]]></c>.</p>
+ <tag><c><![CDATA[{key_cb, atom()}]]></c></tag>
+ <item>
+ <p>Module implementing the behaviour <seealso marker="ssh_client_key_api">ssh_client_key_api</seealso>.
+ Can be used to customize the handling of public keys.
+ </p>
</item>
- <tag><c><![CDATA[{key_cb, atom() = KeyCallbackModule}]]></c></tag>
+ <tag><c><![CDATA[{quiet_mode, atom() = boolean()}]]></c></tag>
<item>
- <p>Provide a special call-back module for key handling.
- The call-back module should be modeled after the
- <c><![CDATA[ssh_file]]></c> module. The functions that must
- be exported are:
- <c><![CDATA[private_host_rsa_key/2]]></c>,
- <c><![CDATA[private_host_dsa_key/2]]></c>,
- <c><![CDATA[lookup_host_key/3]]></c> and
- <c><![CDATA[add_host_key/3]]></c>. This is considered
- somewhat experimental and will be better documented later on.</p>
+ <p>If true, the client will not print out anything on authorization.</p>
</item>
<tag><c><![CDATA[{fd, file_descriptor()}]]></c></tag>
<item>
- <p>Allow an existing file-descriptor to be used
+ <p>Allow an existing file descriptor to be used
(simply passed on to the transport protocol).</p></item>
- <tag><c><![CDATA[{ip_v6_disabled, boolean()}]]></c></tag>
+ <tag><c><![CDATA[{ipv6_disabled, boolean()}]]></c></tag>
+ <item>
+ <p>Determines if SSH shall use IPv6 or not.</p>
+ </item>
+ <tag><c><![CDATA[{rekey_limit, integer()}]]></c></tag>
<item>
- <p>Determines if SSH shall use IPv6 or not.</p></item>
+ <p>Provide, in bytes, when rekeying should be initiated,
+ defaults to one time each GB and one time per hour.</p>
+ </item>
+ <tag><c><![CDATA[{idle_time, integer()}]]></c></tag>
+ <item>
+ <p>Sets a timeout on connection when no channels are active, default is infinity</p></item>
</taglist>
</desc>
</func>
<func>
- <name>connection_info(ConnectionRef, [Option]) ->[{Option, Value}] </name>
+ <name>connection_info(ConnectionRef, [Option]) ->[{Option,
+ Value}] </name>
<fsummary> Retrieves information about a connection. </fsummary>
<type>
<v>Option = client_version | server_version | peer</v>
@@ -205,7 +212,8 @@
<func>
<name>daemon(Port) -> </name>
<name>daemon(Port, Options) -> </name>
- <name>daemon(HostAddress, Port, Options) -> ssh_daemon_ref()</name>
+ <name>daemon(HostAddress, Port, Options) -> {ok,
+ ssh_daemon_ref()} | {error, atom()}</name>
<fsummary>Starts a server listening for SSH connections
on the given port.</fsummary>
<type>
@@ -216,30 +224,32 @@
<v>Value = term()</v>
</type>
<desc>
- <p>Starts a server listening for SSH connections on the given port.</p>
-
- <p>Options are:</p>
+ <p>Starts a server listening for SSH connections on the given
+ port.</p>
+ <p>Options are:</p>
<taglist>
<tag><c><![CDATA[{subsystems, [subsystem_spec()]]]></c></tag>
<item>
- Provides specifications for handling of subsystems. The
- "sftp" subsystem-spec can be retrieved by calling
- ssh_sftpd:subsystem_spec/1. If the subsystems option in not present
- the value of <c>[ssh_sftpd:subsystem_spec([])]</c> will be used.
- It is of course possible to set the option to the empty list
- if you do not want the daemon to run any subsystems at all.
+ Provides specifications for handling of subsystems. The
+ "sftp" subsystem spec can be retrieved by calling
+ ssh_sftpd:subsystem_spec/1. If the subsystems option in
+ not present the value of
+ <c>[ssh_sftpd:subsystem_spec([])]</c> will be used. It is
+ of course possible to set the option to the empty list if
+ you do not want the daemon to run any subsystems at all.
</item>
- <tag><c><![CDATA[{shell, {Module, Function, Args} | fun(string() = User) - > pid() |
- fun(string() = User, ip_address() = PeerAddr) -> pid()}]]></c></tag>
+ <tag><c><![CDATA[{shell, {Module, Function, Args} |
+ fun(string() = User) - > pid() | fun(string() = User,
+ ip_address() = PeerAddr) -> pid()}]]></c></tag>
<item>
- Defines the read-eval-print loop used when a shell is requested
- by the client. Example use the
- erlang shell: <c><![CDATA[{shell, start, []}]]></c> which is
- the default behavior.
+ Defines the read-eval-print loop used when a shell is
+ requested by the client. Default is to use the erlang shell:
+ <c><![CDATA[{shell, start, []}]]></c>
</item>
- <tag><c><![CDATA[{ssh_cli,{channel_callback(), channel_init_args()}}]]></c></tag>
+ <tag><c><![CDATA[{ssh_cli,{channel_callback(),
+ channel_init_args()}}]]></c></tag>
<item>
- Provide your own cli implementation, e.i. a channel callback
+ Provides your own cli implementation, i.e. a channel callback
module that implements a shell and command execution. Note
that you may customize the shell read-eval-print loop using the
option <c>shell</c> which is much less work than implementing
@@ -247,23 +257,32 @@
</item>
<tag><c><![CDATA[{user_dir, String}]]></c></tag>
<item>
- <p>Sets the user directory e.i. the directory containing
+ <p>Sets the user directory i.e. the directory containing
ssh configuration files for the user such as
- <c><![CDATA[known_hosts]]></c>, <c><![CDATA[id_rsa, id_dsa]]></c> and
- <c><![CDATA[authorized_key]]></c>. Defaults to the directory normally
- referred to as <c><![CDATA[~/.ssh]]></c> </p>
+ <c><![CDATA[known_hosts]]></c>, <c><![CDATA[id_rsa,
+ id_dsa]]></c> and
+ <c><![CDATA[authorized_key]]></c>. Defaults to the
+ directory normally referred to as
+ <c><![CDATA[~/.ssh]]></c> </p>
</item>
<tag><c><![CDATA[{system_dir, string()}]]></c></tag>
<item>
- <p>Sets the system directory, containing the host files
- that identifies the host for ssh. The default is
- <c><![CDATA[/etc/ssh]]></c>, note that SSH normally
- requires the host files there to be readable only by
- root.</p>
+ <p>Sets the system directory, containing the host key files
+ that identifies the host keys for ssh. The default is
+ <c><![CDATA[/etc/ssh]]></c>, note that for security reasons
+ this directory is normally only accessible by the root user.</p>
+ </item>
+ <tag><c><![CDATA[{auth_methods, string()}]]></c></tag>
+ <item>
+ <p>Comma separated string that determines which
+ authentication methodes that the server should support and
+ in what order they will be tried. Defaults to
+ <c><![CDATA["publickey,keyboard-interactive,password"]]></c></p>
</item>
- <tag><c><![CDATA[{user_passwords, [{string() = User, string() = Password}]}]]></c></tag>
- <item>
- <p>Provide passwords for password authentication.They will
+ <tag><c><![CDATA[{user_passwords, [{string() = User,
+ string() = Password}]}]]></c></tag>
+ <item>
+ <p>Provide passwords for password authentication.They will
be used when someone tries to connect to the server and
public key user authentication fails. The option provides
a list of valid user names and the corresponding password.
@@ -275,13 +294,19 @@
user. From a security perspective this option makes
the server very vulnerable.</p>
</item>
- <tag><c><![CDATA[{pwdfun, fun/2}]]></c></tag>
+ <tag><c><![CDATA[{pwdfun, fun(User::string(), password::string() -> boolean()}]]></c></tag>
<item>
<p>Provide a function for password validation. This is called
with user and password as strings, and should return
<c><![CDATA[true]]></c> if the password is valid and
<c><![CDATA[false]]></c> otherwise.</p>
</item>
+ <tag><c><![CDATA[{key_cb, atom()}]]></c></tag>
+ <item>
+ <p>Module implementing the behaviour <seealso marker="ssh_server_key_api">ssh_server_key_api</seealso>.
+ Can be used to customize the handling of public keys.
+ </p>
+ </item>
<tag><c><![CDATA[{fd, file_descriptor()}]]></c></tag>
<item>
<p>Allow an existing file-descriptor to be used
@@ -290,6 +315,18 @@
<item>
<p>Determines if SSH shall use IPv6 or not (only used when
HostAddress is set to any).</p></item>
+ <tag><c><![CDATA[{failfun, fun()}]]></c></tag>
+ <item>
+ <p>Provide a fun() to implement your own logging when a user fails to authenticate.</p>
+ </item>
+ <tag><c><![CDATA[{connectfun, fun()}]]></c></tag>
+ <item>
+ <p>Provide a fun() to implement your own logging when a user authenticates to the server.</p>
+ </item>
+ <tag><c><![CDATA[{disconnectfun, fun()}]]></c></tag>
+ <item>
+ <p>Provide a fun() to implement your own logging when a user disconnects from the server.</p>
+ </item>
</taglist>
</desc>
</func>
@@ -305,9 +342,9 @@
<v> Options - see ssh:connect/3</v>
</type>
<desc>
- <p>Starts an interactive shell to an SSH server on the
+ <p>Starts an interactive shell via an SSH server on the
given <c>Host</c>. The function waits for user input,
- and will not return until the remote shell is ended (e.g. on
+ and will not return until the remote shell is ended (i.e.
exit from the shell).
</p>
</desc>
@@ -316,25 +353,24 @@
<func>
<name>start() -> </name>
<name>start(Type) -> ok | {error, Reason}</name>
- <fsummary>Starts the Ssh application. </fsummary>
+ <fsummary>Starts the SSH application. </fsummary>
<type>
<v>Type = permanent | transient | temporary</v>
<v>Reason = term() </v>
</type>
<desc>
- <p>Starts the Ssh application. Default type
- is temporary. See also
- <seealso marker="kernel:application">application(3)</seealso>
- Requires that the crypto application has been started.
+ <p>Utility function that starts crypto, public_key and the SSH
+ application. Defult type is temporary.
+ See also <seealso marker="kernel:application">application(3)</seealso>
</p>
</desc>
</func>
<func>
<name>stop() -> ok </name>
- <fsummary>Stops the Ssh application.</fsummary>
+ <fsummary>Stops the SSH application.</fsummary>
<desc>
- <p>Stops the Ssh application. See also
+ <p>Stops the SSH application. See also
<seealso marker="kernel:application">application(3)</seealso></p>
</desc>
</func>
diff --git a/lib/ssh/doc/src/ssh_app.xml b/lib/ssh/doc/src/ssh_app.xml
new file mode 100644
index 0000000000..c01f44936a
--- /dev/null
+++ b/lib/ssh/doc/src/ssh_app.xml
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="iso-8859-1" ?>
+<!DOCTYPE appref SYSTEM "appref.dtd">
+
+<appref>
+ <header>
+ <copyright>
+ <year>2012</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>SSH</title>
+ <file>ssh_app.xml</file>
+ </header>
+ <app>SSH</app>
+ <appsummary>The ssh application implements the SSH (Secure Shell) protocol and
+ provides an SFTP (SSH File Transfer Protocol) client and server. </appsummary>
+
+ <section>
+ <title>DEPENDENCIES</title>
+ <p>The ssh application uses the Erlang applications public_key and
+ crypto to handle public keys and encryption, hence these
+ applications needs to be loaded for the ssh application to work. In
+ an embedded environment that means they need to be started with
+ application:start/[1,2] before the ssh application is started.
+ </p>
+ </section>
+
+ <section>
+ <title>CONFIGURATION</title>
+
+ <p>The ssh application does not currently have an application
+ specific configuration file as described in application(3),
+ however it will by default use the following configuration files
+ from openssh: known_hosts, authorized_keys, authorized_keys2,
+ id_dsa and id_rsa, ssh_host_dsa_key and ssh_host_rsa_key. By
+ default Erlang SSH will look for id_dsa, id_rsa, known_hosts
+ and authorized_keys in ~/.ssh, and the host key files in /etc/ssh
+ . These locations may be changed by the options user_dir and
+ system_dir. Public key handling may also be customized by
+ providing a callback module implementing the behaviors
+ <seealso marker="ssh_client_key_api">ssh_client_key_api</seealso> and
+ <seealso marker="ssh_server_key_api">ssh_server_key_api</seealso>.
+ </p>
+
+ <section>
+ <title>PUBLIC KEYS</title>
+ <p>
+ id_dsa and id_rsa are the users private key files, note that
+ the public key is part of the private key so the ssh
+ application will not use the id_&lt;*>.pub files. These are
+ for the users convenience when he/she needs to convey their
+ public key.
+ </p>
+ </section>
+
+ <section>
+ <title>KNOW HOSTS</title>
+ <p>The known_hosts file contains a list of approved servers and
+ their public keys. Once a server is listed, it can be verified
+ without user interaction.
+ </p>
+ </section>
+
+ <section>
+ <title>AUTHORIZED KEYS</title>
+ <p>The authorized key file keeps track of the user's authorized
+ public keys. The most common use of this file is to let users
+ log in without entering their password which is supported by the
+ Erlang SSH daemon.
+ </p>
+ </section>
+
+ <section>
+ <title>HOST KEYS</title>
+ <p>Currently rsa and dsa host keys are supported and are
+ expected to be found in files named ssh_host_rsa_key and
+ ssh_host_dsa_key.
+ </p>
+ </section>
+ </section>
+
+ <section>
+ <title>SEE ALSO</title>
+ <p>application(3)</p>
+ </section>
+
+</appref>
diff --git a/lib/ssh/doc/src/ssh_channel.xml b/lib/ssh/doc/src/ssh_channel.xml
index c2b7aa94a5..f0083ae8d1 100644
--- a/lib/ssh/doc/src/ssh_channel.xml
+++ b/lib/ssh/doc/src/ssh_channel.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>2009</year>
- <year>2009</year>
+ <year>2012</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -22,31 +22,35 @@
The Initial Developer of the Original Code is Ericsson AB.
</legalnotice>
-
<title>ssh_channel</title>
- <prepared>Ingela Anderton Andin</prepared>
- <responsible></responsible>
- <docno></docno>
- <approved></approved>
- <checked></checked>
- <date></date>
- <rev></rev>
</header>
<module>ssh_channel</module>
- <modulesummary>Generic Ssh Channel Behavior
+ <modulesummary>-behaviour(ssh_channel).
</modulesummary>
<description>
- <p>Ssh services are implemented as channels that are multiplexed
- over an ssh connection and communicates via the ssh connection
- protocol. This module provides a callback API that takes care of
- generic channel aspects such as flow control and close messages
- and lets the callback functions take care of the service specific
- parts.
+ <p>SSH services (clients and servers) are implemented as channels
+ that are multiplexed over an SSH connection and communicates via
+ the <url href="http://www.ietf.org/rfc/rfc4254.txt"> SSH
+ Connection Protocol </url>. This module provides a callback API
+ that takes care of generic channel aspects such as flow control
+ and close messages and lets the callback functions take care of
+ the service (application) specific parts. This behavior also ensures
+ that the channel process honors the principal of an OTP-process so
+ that it can be part of a supervisor tree. This is a requirement of
+ channel processes implementing a subsystem that will be added to
+ the SSH applications supervisor tree.
</p>
+
+ <note> When implementing a SSH subsystem use the
+ <c>-behaviour(ssh_subsystem).</c> instead of <c>-behaviour(ssh_channel).</c>
+ as the only relevant callback functions for subsystems are
+ init/1, handle_ssh_msg/2, handle_msg/2 and terminate/2, so the ssh_subsystem
+ behaviour is limited version of the ssh_channel behaviour.
+ </note>
</description>
<section>
- <title>COMMON DATA TYPES </title>
+ <title>DATA TYPES </title>
<p>Type definitions that are used more than once in this module
and/or abstractions to indicate the intended use of the data
@@ -56,10 +60,10 @@
<p><c>string() = list of ASCII characters</c></p>
<p><c>timeout() = infinity | integer() - in milliseconds.</c></p>
<p><c>ssh_connection_ref() - opaque to the user returned by
- ssh:connect/3 or sent to a ssh channel process</c></p>
+ ssh:connect/3 or sent to an SSH channel process</c></p>
<p><c>ssh_channel_id() = integer() </c></p>
<p><c>ssh_data_type_code() = 1 ("stderr") | 0 ("normal") are
- currently valid values see RFC 4254 section 5.2.</c></p>
+ currently valid values see <url href="http://www.ietf.org/rfc/rfc4254.txt">RFC 4254 </url> section 5.2.</c></p>
</section>
<funcs>
@@ -74,13 +78,15 @@
<v>Timeout = timeout() </v>
<v>Reply = term() </v>
<v>Reason = closed | timeout </v>
+
</type>
<desc>
<p>Makes a synchronous call to the channel process by sending
a message and waiting until a reply arrives or a timeout
- occurs. The channel will call
- <c>CallbackModule:handle_call/3</c> to handle the message.
- If the channel process does not exist <c>{error, closed}</c> is returned.
+ occurs. The channel will call <seealso mark =
+ "#Module:handle_call-3">Module:handle_call/3</seealso>
+ to handle the message. If the channel process does not exist
+ <c>{error, closed}</c> is returned.
</p>
</desc>
</func>
@@ -98,26 +104,27 @@
<p>Sends an asynchronous message to the channel process and
returns ok immediately, ignoring if the destination node or
channel process does not exist. The channel will call
- <c>CallbackModule:handle_cast/2</c> to handle the message.
+ <seealso mark = "#Module:handle_cast-3">Module:handle_cast/2</seealso>
+ to handle the message.
</p>
</desc>
</func>
<func>
<name>enter_loop(State) -> _ </name>
- <fsummary> Makes an existing process into a ssh_channel process. </fsummary>
+ <fsummary> Makes an existing process an ssh_channel process. </fsummary>
<type>
- <v> State = term() - as returned by ssh_channel:init/1</v>
+ <v> State = term() - as returned by <seealso mark = "#init-1">ssh_channel:init/1</seealso></v>
</type>
<desc>
- <p> Makes an existing process into a <c>ssh_channel</c>
+ <p> Makes an existing process an <c>ssh_channel</c>
process. Does not return, instead the calling process will
- enter the <c>ssh_channel</c> process receive loop and become a
+ enter the <c>ssh_channel</c> process receive loop and become an
<c>ssh_channel process.</c> The process must have been started using
one of the start functions in proc_lib, see <seealso
marker="stdlib:proc_lib">proc_lib(3)</seealso>. The
user is responsible for any initialization of the process
- and needs to call ssh_channel:init/1.
+ and needs to call <seealso mark = "#init-1">ssh_channel:init/1</seealso>
</p>
</desc>
</func>
@@ -126,7 +133,10 @@
<name>init(Options) -> {ok, State} | {ok, State, Timeout} | {stop, Reason} </name>
<fsummary> Initiates a ssh_channel process.</fsummary>
<type>
- <v> Options = [{Option, Value}]</v>
+ <v>Options = [{Option, Value}]</v>
+ <v>State = term()</v>
+ <v>Timeout = timeout() </v>
+ <v>Reason = term() </v>
</type>
<desc>
<p>
@@ -134,24 +144,27 @@
</p>
<taglist>
<tag><c><![CDATA[{channel_cb, atom()}]]></c></tag>
- <item>The module that implements the channel behavior.</item>
+ <item>The module that implements the channel behaviour.</item>
<tag><c><![CDATA[{init_args(), list()}]]></c></tag>
- <item> The list of arguments to the callback modules
+ <item> The list of arguments to the callback module's
init function.</item>
<tag><c><![CDATA[{cm, connection_ref()}]]></c></tag>
- <item> Reference to the ssh connection.</item>
+ <item> Reference to the ssh connection as returned by <seealso
+ marker="ssh#connect-3">ssh:connect/3</seealso></item>
<tag><c><![CDATA[{channel_id, channel_id()}]]></c></tag>
<item> Id of the ssh channel.</item>
</taglist>
- <note><p>This function is normally not called by the user, it is
- only needed if for some reason the channel process needs
- to be started with help of <c>proc_lib</c> instead calling
- <c>ssh_channel:start/4</c> or <c>ssh_channel:start_link/4</c> </p>
+ <note><p>This function is normally not called by the
+ user. The user only needs to call if for some reason the
+ channel process needs to be started with help of
+ <c>proc_lib</c> instead of calling
+ <c>ssh_channel:start/4</c> or
+ <c>ssh_channel:start_link/4</c> </p>
</note>
</desc>
</func>
@@ -167,12 +180,12 @@
<p>This function can be used by a channel to explicitly send a
reply to a client that called <c>call/[2,3]</c> when the reply
cannot be defined in the return value of
- <c>CallbackModule:handle_call/3</c>.</p>
+ <seealso marker ="#Module:handle_call-3">Module:handle_call/3</seealso>.</p>
<p><c>Client</c> must be the <c>From</c> argument provided to
the callback function <c>handle_call/3</c>.
<c>Reply</c> is an arbitrary term,
- which will be given back to the client as the return value of
- <c>ssh_channel:call/[2,3].</c></p>
+ which will be given back to the client as the return value of
+ <seealso marker="#call-2">ssh_channel:call/[2,3].</seealso>></p>
</desc>
</func>
@@ -180,11 +193,12 @@
<name>start(SshConnection, ChannelId, ChannelCb, CbInitArgs) -> </name>
<name>start_link(SshConnection, ChannelId, ChannelCb, CbInitArgs) ->
{ok, ChannelRef} | {error, Reason}</name>
- <fsummary> Starts a processes that handles a ssh channel. </fsummary>
+ <fsummary> Starts a processes that handles a SSH channel. </fsummary>
<type>
<v>SshConnection = ssh_connection_ref()</v>
<v>ChannelId = ssh_channel_id() </v>
- <d> As returned by ssh_connection:session_channel/[2,4]</d>
+ <d> As returned by cannot be defined in the return value of
+ <seealso marker ="ssh_connection#session_channel/2">ssh_connection:session_channel/[2,4]</seealso></d>
<v>ChannelCb = atom()</v>
<d> The name of the module implementing the service specific parts
of the channel.</d>
@@ -193,10 +207,10 @@
<v>ChannelRef = pid()</v>
</type>
<desc>
- <p>Starts a processes that handles a ssh channel. Will be
- called internally by the ssh daemon or explicitly by the ssh
- client implementations. A channel process traps exit signals
- by default.
+ <p>Starts a processes that handles an SSH channel. It will be
+ called internally by the SSH daemon or explicitly by the SSH
+ client implementations. The behavior will set the
+ <c>trap_exit</c> flag to true.
</p>
</desc>
</func>
@@ -204,72 +218,61 @@
</funcs>
<section>
- <title>CALLBACK FUNCTIONS</title>
-
- <p>The functions init/1, terminate/2, handle_ssh_msg/2 and
- handle_msg/2 are the functions that are required to provide the
- implementation for a server side channel, such as a ssh subsystem
- channel that can be plugged into the erlang ssh daemon see
- <seealso marker="ssh">ssh:daemon/[2, 3]</seealso>. The
- handle_call/3, handle_cast/2 code_change/3 and enter_loop/1
- functions are only relevant when implementing a client side
- channel.</p>
- </section>
-
- <section>
<marker id="cb_timeouts"></marker>
<title> CALLBACK TIMEOUTS</title>
- <p> If an integer timeout value is provided in a return value of
- one of the callback functions, a timeout will occur unless a
- message is received within <c>Timeout</c> milliseconds. A timeout
- is represented by the atom <c>timeout</c> which should be handled
- by the <seealso marker="#handle_msg">handle_msg/2</seealso>
- callback function. The atom infinity can be used to wait
- indefinitely, this is the default value. </p>
+
+ <p>The timeout values that may be returned by the callback functions
+ has the same semantics as in a <seealso marker="stdlib#gen_server">gen_server</seealso>
+ If the timeout occurs <seealso marker="#handle_msg">handle_msg/2</seealso>
+ will be called as <c>handle_msg(timeout, State). </c></p>
</section>
<funcs>
<func>
- <name>CallbackModule:code_change(OldVsn, State, Extra) -> {ok,
+ <name>Module:code_change(OldVsn, State, Extra) -> {ok,
NewState}</name>
<fsummary> Converts process state when code is changed.</fsummary>
<type>
- <v> Converts process state when code is changed.</v>
+ <v>OldVsn = term()</v>
+ <d>In the case of an upgrade, <c>OldVsn</c> is <c>Vsn</c>, and
+ in the case of a downgrade, <c>OldVsn</c> is
+ <c>{down,Vsn}</c>. <c>Vsn</c> is defined by the <c>vsn</c>
+ attribute(s) of the old version of the callback module
+ <c>Module</c>. If no such attribute is defined, the version is
+ the checksum of the BEAM file.</d>
+ <v>State = term()</v>
+ <d>The internal state of the channel.</d>
+ <v>Extra = term()</v>
+ <d>Passed as-is from the <c>{advanced,Extra}</c>
+ part of the update instruction.</d>
</type>
<desc>
- <p>This function is called by a client side channel when it
- should update its internal state during a release
- upgrade/downgrade, i.e. when the instruction
- <c>{update,Module,Change,...}</c> where
- <c>Change={advanced,Extra}</c> is given in the <c>appup</c>
- file. See <seealso
- marker="doc/design_principles:release_handling#instr">OTP
- Design Principles</seealso> for more information. Any new
- connection will benefit from a server side upgrade but
- already started connections on the server side will not be
- affected.
- </p>
+ <p> Converts process state when code is changed.</p>
+
+ <p>This function is called by a client side channel when it
+ should update its internal state during a release
+ upgrade/downgrade, i.e. when the instruction
+ <c>{update,Module,Change,...}</c> where
+ <c>Change={advanced,Extra}</c> is given in the <c>appup</c>
+ file. See <seealso marker="doc/design_principles:release_handling#instr">OTP
+ Design Principles</seealso> for more information.
+ </p>
- <note><p>If there are long lived ssh connections and more
- than one upgrade in a short time this may cause the old
- connections to fail as only two versions of the code may
- be loaded simultaneously.</p></note>
+ <note><p>Soft upgrade according to the OTP release concept
+ is not straight forward for the server side, as subsystem
+ channel processes are spawned by the SSH application and
+ hence added to its supervisor tree. It could be possible to
+ upgrade the subsystem channels, when upgrading the user
+ application, if the callback functions can handle two
+ versions of the state, but this function can not be used in
+ the normal way.</p>
+ </note>
- <p>In the case of an upgrade, <c>OldVsn</c> is <c>Vsn</c>, and
- in the case of a downgrade, <c>OldVsn</c> is
- <c>{down,Vsn}</c>. <c>Vsn</c> is defined by the <c>vsn</c>
- attribute(s) of the old version of the callback module
- <c>Module</c>. If no such attribute is defined, the version
- is the checksum of the BEAM file.</p>
- <p><c>State</c> is the internal state of the channel.</p>
- <p><c>Extra</c> is passed as-is from the <c>{advanced,Extra}</c>
- part of the update instruction.</p>
- <p>The function should return the updated internal state.</p>
</desc>
</func>
<func>
- <name>CallbackModule:init(Args) -> {ok, State} | {ok, State, Timeout} |
+ <name>Module:init(Args) -> {ok, State} | {ok, State, timeout()} |
{stop, Reason}</name>
<fsummary> Makes necessary initializations and returns the
initial channel state if the initializations succeed.</fsummary>
@@ -277,7 +280,6 @@
<v> Args = term() </v>
<d> Last argument to ssh_channel:start_link/4.</d>
<v> State = term() </v>
- <v>Timeout = timeout() </v>
<v> Reason = term() </v>
</type>
<desc>
@@ -290,7 +292,7 @@
</func>
<func>
- <name>CallbackModule:handle_call(Msg, From, State) -> Result</name>
+ <name>Module:handle_call(Msg, From, State) -> Result</name>
<fsummary> Handles messages sent by calling
<c>ssh_channel:call/[2,3]</c></fsummary>
<type>
@@ -298,17 +300,16 @@
<v>From = opaque to the user should be used as argument to
ssh_channel:reply/2</v>
<v>State = term()</v>
- <v>Result = {reply, Reply, NewState} | {reply, Reply, NewState, Timeout}
- | {noreply, NewState} | {noreply , NewState, Timeout}
+ <v>Result = {reply, Reply, NewState} | {reply, Reply, NewState, timeout()}
+ | {noreply, NewState} | {noreply , NewState, timeout()}
| {stop, Reason, Reply, NewState} | {stop, Reason, NewState} </v>
<v>Reply = term() - will be the return value of ssh_channel:call/[2,3]</v>
- <v>Timeout = timeout() </v>
- <v>NewState = term() - a possible updated version of State</v>
+ <v>NewState = term()</v>
<v>Reason = term()</v>
</type>
<desc>
<p>Handles messages sent by calling
- <c>ssh_channel:call/[2,3]</c>
+ <seealso marker="#call-2">ssh_channel:call/[2,3]</seealso>
</p>
<p>For more detailed information on timeouts see the section
<seealso marker="#cb_timeouts">CALLBACK TIMEOUTS</seealso>. </p>
@@ -316,16 +317,15 @@
</func>
<func>
- <name>CallbackModule:handle_cast(Msg, State) -> Result</name>
+ <name>Module:handle_cast(Msg, State) -> Result</name>
<fsummary> Handles messages sent by calling
<c>ssh_channel:cact/2</c></fsummary>
<type>
<v>Msg = term()</v>
<v>State = term()</v>
- <v>Result = {noreply, NewState} | {noreply, NewState, Timeout}
+ <v>Result = {noreply, NewState} | {noreply, NewState, timeout()}
| {stop, Reason, NewState}</v>
- <v>NewState = term() - a possible updated version of State</v>
- <v>Timeout = timeout() </v>
+ <v>NewState = term() </v>
<v>Reason = term()</v>
</type>
<desc>
@@ -339,7 +339,7 @@
</func>
<func>
- <name>CallbackModule:handle_msg(Msg, State) -> {ok, State} |
+ <name>Module:handle_msg(Msg, State) -> {ok, State} |
{stop, ChannelId, State}</name>
<fsummary> Handle other messages than ssh connection protocol,
@@ -359,136 +359,37 @@
<taglist>
<tag><c><![CDATA[{ssh_channel_up, ssh_channel_id(),
ssh_connection_ref()}]]></c></tag>
- <item>This is the first messages that will be received
- by the channel, it is sent just before
- the ssh_channel:init/1 function returns successfully.
- This is especially useful if the server wants
- to send a message to the client without first receiving
- a message from the client. If the message is not useful
- for your particular problem just ignore it by immediately
- returning {ok, State}.
+ <item>This is the first messages that will be received by
+ the channel, it is sent just before the <seealso
+ marker="#init-2">ssh_channel:init/1</seealso> function
+ returns successfully. This is especially useful if the
+ server wants to send a message to the client without first
+ receiving a message from it. If the message is not
+ useful for your particular scenario just ignore it by
+ immediately returning {ok, State}.
</item>
</taglist>
</desc>
</func>
<func>
- <name>CallbackModule:handle_ssh_msg(Msg, State) -> {ok, State} | {stop,
+ <name>Module:handle_ssh_msg(Msg, State) -> {ok, State} | {stop,
ssh_channel_id(), State}</name>
<fsummary> Handles ssh connection protocol messages. </fsummary>
<type>
- <v>Msg = {ssh_cm, ssh_connection_ref(), SshMsg}</v>
- <v> SshMsg = tuple() - see message list below</v>
+ <v>Msg = <seealso marker="ssh_connection"> ssh_connection:event() </seealso> </v>
<v>State = term()</v>
</type>
<desc>
<p> Handles ssh connection protocol messages that may need
service specific attention.
</p>
-
- <p> All channels should handle the following messages. For
- channels implementing subsystems the handle_ssh_msg-callback
- will not be called for any other messages. </p>
-
- <taglist>
- <tag><c><![CDATA[{ssh_cm, ssh_connection_ref(), {data, ssh_channel_id(),
- ssh_data_type_code(), binary() = Data}}]]></c></tag>
- <item> Data has arrived on the channel. When the callback
- for this message returns the channel behavior will adjust
- the ssh flow control window.</item>
-
- <tag><c><![CDATA[{ssh_cm, ssh_connection_ref(), {eof,
- ssh_channel_id()}}]]></c></tag>
- <item>Indicteas that the other side will not send any more
- data.</item>
-
- <tag><c><![CDATA[{ssh_cm, ssh_connection_ref(), {signal,
- ssh_channel_id(), ssh_signal()}} ]]></c></tag>
- <item>A signal can be delivered to the remote
- process/service using the following message. Some systems
- may not implement signals, in which case they should ignore
- this message.</item>
-
- <tag><c><![CDATA[{ssh_cm, ssh_connection_ref(),
- {exit_signal, ssh_channel_id(), string() = exit_signal,
- string() = ErrorMsg, string() =
- LanguageString}}]]></c></tag>
- <item>A remote execution may terminate violently due to a
- signal then this message may be received. For details on valid string
- values see RFC 4254 section 6.10</item>
-
- <tag><c><![CDATA[{ssh_cm, ssh_connection_ref(), {exit_status,
- ssh_channel_id(), integer() = ExitStatus}}]]></c></tag>
- <item> When the command running at the other end terminates,
- the following message can be sent to return the exit status
- of the command. A zero 'exit_status' usually means that the
- command terminated successfully.</item>
- </taglist>
-
- <p> Channels implementing a shell and command execution on the server side
- should also handle the following messages. </p>
-
- <taglist>
- <tag><c><![CDATA[{ssh_cm, ssh_connection_ref(), {env, ssh_channel_id(),
- boolean() = WantReply, string() = Var, string() = Value}}]]></c></tag>
- <item> Environment variables may be passed to the
- shell/command to be started later. Note that before the
- callback returns it should call the function
- ssh_connection:reply_request/4 with the boolean value of <c>
- WantReply</c> as the second argument.
- </item>
-
- <tag><c><![CDATA[{ssh_cm, ConnectionRef, {exec, ssh_channel_id(),
- boolean() = WantReply, string() = Cmd}}]]></c></tag>
- <item> This message will request that the server start the
- execution of the given command. Note that before the
- callback returns it should call the function
- ssh_connection:reply_request/4 with the boolean value of <c>
- WantReply</c> as the second argument.</item>
-
- <tag><c><![CDATA[{ssh_cm, ssh_connection_ref(), {pty, ssh_channel_id(),
- boolean() = WantReply, {string() = Terminal, integer() = CharWidth,
- integer() = RowHeight, integer() = PixelWidth, integer() = PixelHight,
- [{atom() | integer() = Opcode,
- integer() = Value}] = TerminalModes}}}]]></c></tag>
- <item>A pseudo-terminal has been requested for the
- session. Terminal is the value of the TERM environment
- variable value (e.g., vt100). Zero dimension parameters must
- be ignored. The character/row dimensions override the pixel
- dimensions (when nonzero). Pixel dimensions refer to the
- drawable area of the window. The <c>Opcode</c> in the
- <c>TerminalModes</c> list is the mnemonic name, represented
- as an lowercase erlang atom, defined in RFC 4254 section 8,
- or the opcode if the mnemonic name is not listed in the
- RFC. Example <c>OP code: 53, mnemonic name ECHO erlang atom:
- echo</c>. Note that before the callback returns it should
- call the function ssh_connection:reply_request/4 with the
- boolean value of <c> WantReply</c> as the second
- argument.</item>
-
- <tag><c><![CDATA[{ssh_cm, ConnectionRef, {shell, boolean() =
- WantReply}}]]></c></tag>
- <item> This message will request that the user's default
- shell be started at the other end. Note that before the
- callback returns it should call the function
- ssh_connection:reply_request/4 with the value of <c>
- WantReply</c> as the second argument.
- </item>
-
- <tag><c><![CDATA[ {ssh_cm, ssh_connection_ref(), {window_change,
- ssh_channel_id(), integer() = CharWidth, integer() = RowHeight,
- integer() = PixWidth, integer() = PixHeight}}]]></c></tag>
- <item> When the window (terminal) size changes on the client
- side, it MAY send a message to the other side to inform it
- of the new dimensions.</item>
- </taglist>
<p> The following message is completely taken care of by the
ssh channel behavior</p>
<taglist>
- <tag><c><![CDATA[{ssh_cm, ssh_connection_ref(), {closed,
- ssh_channel_id()}}]]></c></tag>
+ <tag><c><![CDATA[{closed, ssh_channel_id()}]]></c></tag>
<item> The channel behavior will send a close message to the
other side if such a message has not already been sent and
then terminate the channel with reason normal.</item>
@@ -497,7 +398,7 @@
</func>
<func>
- <name>CallbackModule:terminate(Reason, State) -> _</name>
+ <name>Module:terminate(Reason, State) -> _</name>
<fsummary> </fsummary>
<type>
<v>Reason = term()</v>
@@ -505,12 +406,12 @@
</type>
<desc>
<p>This function is called by a channel process when it is
- about to terminate. Before this function is called ssh_connection:close/2
- will be called if it has not been called earlier.
- This function should be the opposite of <c>CallbackModule:init/1</c>
- and do any necessary cleaning up. When it returns, the
- channel process terminates with reason <c>Reason</c>. The return value is
- ignored.
+ about to terminate. Before this function is called <seealso
+ marker="ssh_connection#close-2"> ssh_connection:close/2
+ </seealso> will be called if it has not been called earlier.
+ This function should do any necessary cleaning
+ up. When it returns, the channel process terminates with
+ reason <c>Reason</c>. The return value is ignored.
</p>
</desc>
</func>
diff --git a/lib/ssh/doc/src/ssh_client_key_api.xml b/lib/ssh/doc/src/ssh_client_key_api.xml
new file mode 100644
index 0000000000..abc1070e78
--- /dev/null
+++ b/lib/ssh/doc/src/ssh_client_key_api.xml
@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="iso-8859-1" ?>
+<!DOCTYPE erlref SYSTEM "erlref.dtd">
+
+<erlref>
+ <header>
+ <copyright>
+ <year>2012</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.
+
+ The Initial Developer of the Original Code is Ericsson AB.
+ </legalnotice>
+ <title>ssh_client_key_api</title>
+ </header>
+ <module>ssh_client_key_api</module>
+ <modulesummary>
+ -behaviour(ssh_client_key_api).
+ </modulesummary>
+ <description>
+ <p> Behavior describing the API for an SSH client's public key handling.
+ By implementing the callbacks defined.
+ in this behavior it is possible to customize the SSH client's public key
+ handling. By default the SSH application implements this behavior
+ with help of the standard openssh files, see <seealso marker="SSH_app"> ssh(6)</seealso>. </p>
+ </description>
+
+ <section>
+ <title>DATA TYPES </title>
+
+ <p>Type definitions that are used more than once in this module
+ and/or abstractions to indicate the intended use of the data
+ type:</p>
+
+ <p> boolean() = true | false</p>
+ <p> string() = [byte()] </p>
+ <p> public_key() = #'RSAPublicKey'{}| {integer(), #'Dss-Parms'{}}| term()</p>
+ <p> private_key() = #'RSAPublicKey'{}| {integer(), #'Dss-Parms'{}}| term()</p>
+ <p> public_key_algorithm() = 'ssh-rsa'| 'ssh-dss' | atom()</p>
+
+ </section>
+
+ <funcs>
+ <func>
+ <name>Module:add_host_key(HostNames, Key, ConnectOptions) -> ok | {error, Reason}</name>
+ <fsummary>Adds a host key to the set of trusted host keys</fsummary>
+ <type>
+ <v>HostNames = string()</v>
+ <d>Description of the host that owns the <c>PublicKey</c></d>
+
+ <v>Key = public_key() </v>
+ <d> Normally an RSA or DSA public key but handling of other public keys can be added</d>
+
+ <v>ConnectOptions = proplists:proplist() </v>
+ <d>Options provided to <seealso marker="ssh#daemon">ssh:connect/[3,4]</seealso></d>
+ <v>Reason = term() </v>
+ </type>
+ <desc>
+ <p> Adds a host key to the set of trusted host keys</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>Module:is_host_key(Key, Host, Algorithm, ConnectOptions) -> Result</name>
+ <fsummary>Checks if a host key is trusted</fsummary>
+ <type>
+ <v>Key = public_key() </v>
+ <d> Normally an RSA or DSA public key but handling of other public keys can be added</d>
+
+ <v>Host = string()</v>
+ <d>Description of the host</d>
+
+ <v>Algorithm = public_key_algorithm()</v>
+ <d> Host key algorithm. Should support 'ssh-rsa'| 'ssh-dss' but additional algorithms
+ can be handled.</d>
+
+ <v> ConnectOptions = proplists:proplist() </v>
+ <d>Options provided to <seealso marker="ssh#daemon">ssh:connect/[3,4]</seealso></d>
+
+ <v> Result = boolean()</v>
+ </type>
+ <desc>
+ <p>Checks if a host key is trusted</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>Module:user_key(Algorithm, ConnectOptions) ->
+ {ok, PrivateKey} | {error, Reason}</name>
+ <fsummary>Fetches the users "public key" matching the <c>Algorithm</c>.</fsummary>
+ <type>
+ <v>Algorithm = public_key_algorithm()</v>
+ <d> Host key algorithm. Should support 'ssh-rsa'| 'ssh-dss' but additional algorithms
+ can be handled.</d>
+
+ <v> ConnectOptions = proplists:proplist() </v>
+ <d>Options provided to <seealso marker="ssh#daemon">ssh:connect/[3,4]</seealso></d>
+
+ <v> PrivateKey = private_key()</v>
+ <d> The private key of the user matching the <c>Algorithm</c></d>
+
+ <v>Reason = term() </v>
+ </type>
+
+ <desc>
+ <p>Fetches the users "public key" matching the <c>Algorithm</c>.
+ <note>The private key contains the public key</note>
+ </p>
+ </desc>
+ </func>
+
+ </funcs>
+
+</erlref>
diff --git a/lib/ssh/doc/src/ssh_connection.xml b/lib/ssh/doc/src/ssh_connection.xml
index 9942306b93..c66622307f 100644
--- a/lib/ssh/doc/src/ssh_connection.xml
+++ b/lib/ssh/doc/src/ssh_connection.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>2011</year>
+ <year>2012</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -24,69 +24,178 @@
</legalnotice>
<title>ssh_connection</title>
- <prepared>Ingela Anderton Andin</prepared>
- <responsible></responsible>
- <docno></docno>
- <approved></approved>
- <checked></checked>
<date></date>
- <rev></rev>
</header>
<module>ssh_connection</module>
- <modulesummary>This module provides an API to the ssh connection protocol.
+ <modulesummary>This module provides API functions to send <url href="http://www.ietf.org/rfc/rfc4254.txt"> SSH Connection Protocol </url>
+ events to the other side of an SSH channel.
</modulesummary>
+
<description>
- <p>This module provides an API to the ssh connection protocol.
- Not all features of the connection protocol are officially supported yet.
- Only the ones supported are documented here.</p>
+ <p>The SSH Connection Protocol is used by clients and servers
+ (i.e. SSH channels) to communicate over the SSH connection. The
+ API functions in this module sends SSH Connection Protocol events
+ that are received as messages by the remote channel.
+ In the case that the receiving channel is an Erlang process the
+ message will be on the following format
+ <c><![CDATA[{ssh_cm, ssh_connection_ref(), ssh_event_msg()}]]></c>. If the <seealso
+ marker="ssh_channel">ssh_channel</seealso> behavior is used to
+ implement the channel process these will be handled by
+ <seealso
+ marker="ssh_channel#CallbackModule:handled_ssh_msg-2">handle_ssh_msg/2 </seealso>.</p>
</description>
- <section>
- <title>COMMON DATA TYPES </title>
+ <section>
+ <title>DATA TYPES </title>
+
<p>Type definitions that are used more than once in this module and/or
abstractions to indicate the intended use of the data type:</p>
-
+
<p><c>boolean() = true | false </c></p>
<p><c>string() = list of ASCII characters</c></p>
<p><c>timeout() = infinity | integer() - in milliseconds.</c></p>
<p><c>ssh_connection_ref() - opaque to the user returned by
- ssh:connect/3 or sent to a ssh channel processes</c></p>
+ ssh:connect/3 or sent to an SSH channel processes</c></p>
<p><c>ssh_channel_id() = integer() </c></p>
<p><c>ssh_data_type_code() = 1 ("stderr") | 0 ("normal") are
- currently valid values see RFC 4254 section 5.2.</c></p>
+ currently valid values see</c> <url href="http://www.ietf.org/rfc/rfc4254.txt">RFC 4254 </url> section 5.2.</p>
<p><c>ssh_request_status() = success | failure</c></p>
- </section>
+ <p><c>event() = {ssh_cm, ssh_connection_ref(), ssh_event_msg()} </c></p>
+ <p><c>ssh_event_msg() = data_events() | status_events() | terminal_events() </c></p>
+
+ <taglist>
+ <tag><b>data_events()</b></tag>
+ <item>
+ <taglist>
+ <tag><c><![CDATA[{data, ssh_channel_id(), ssh_data_type_code(), binary() = Data}]]></c></tag>
+ <item> Data has arrived on the channel. This event is sent as
+ result of calling <seealso marker="#send-3"> ssh_connection:send/[3,4,5] </seealso></item>
+
+ <tag><c><![CDATA[{eof, ssh_channel_id()}]]></c></tag>
+ <item>Indicates that the other side will not send any more
+ data. This event is sent as result of calling <seealso
+ marker="#send_eof-2"> ssh_connection:send_eof/2</seealso>
+ </item>
+ </taglist>
+ </item>
+
+ <tag><b>status_events()</b></tag>
+ <item>
+
+ <taglist>
+ <tag><c><![CDATA[{signal, ssh_channel_id(), ssh_signal()}]]></c></tag>
+ <item>A signal can be delivered to the remote process/service
+ using the following message. Some systems will not support
+ signals, in which case they should ignore this message. There is
+ currently no funtion to generate this event as the signals
+ refered to are on OS-level and not something generated by an
+ Erlang program.</item>
+
+ <tag><c><![CDATA[{exit_signal, ssh_channel_id(), string() = ExitSignal, string() = ErrorMsg,
+ string() = LanguageString}]]></c></tag>
+
+ <item>A remote execution may terminate violently due to a signal
+ then this message may be received. For details on valid string
+ values see <url href="http://www.ietf.org/rfc/rfc4254.txt">RFC 4254</url> section 6.10. Special case of the signals
+ mentioned above.</item>
+
+ <tag><c><![CDATA[{exit_status, ssh_channel_id(), integer() = ExitStatus}]]></c></tag>
+ <item> When the command running at the other end terminates, the
+ following message can be sent to return the exit status of the
+ command. A zero 'exit_status' usually means that the command
+ terminated successfully. This event is sent as result of calling
+ <seealso marker="#exit_status">
+ ssh_connection:exit_status/3</seealso></item>
+
+ <tag><c><![CDATA[{closed, ssh_channel_id()}]]></c></tag>
+ <item> This event is sent as result of calling
+ <seealso marker="#close">ssh_connection:close/2</seealso> Both the handling of this
+ event and sending of it will be taken care of by the
+ <seealso marker="ssh_channel">ssh_channel</seealso> behavior.</item>
+
+ </taglist>
+ </item>
- <section>
- <title>MESSAGES SENT TO CHANNEL PROCESSES</title>
+ <tag><b>terminal_events()</b></tag>
+
+ <item>
+ <p> Channels implementing a shell and command execution on the
+ server side should handle the following messages that may be sent by client channel processes. </p>
+
+ <p><note>Events that includes a <c> WantReply</c> expects the event handling
+ process to call <seealso marker="#reply_request">ssh_connection:reply_request/4</seealso>
+ with the boolean value of <c> WantReply</c> as the second
+ argument. </note> </p>
+
+ <taglist>
+ <tag><c><![CDATA[{env, ssh_channel_id(), boolean() = WantReply,
+ string() = Var, string() = Value}]]></c></tag>
+ <item> Environment variables may be passed to the shell/command
+ to be started later. This event is sent as result of calling <seealso
+ marker="#setenv"> ssh_connection:setenv/5</seealso>
+ </item>
+
+ <tag><c><![CDATA[{pty, ssh_channel_id(),
+ boolean() = WantReply, {string() = Terminal, integer() = CharWidth,
+ integer() = RowHeight, integer() = PixelWidth, integer() = PixelHight,
+ [{atom() | integer() = Opcode,
+ integer() = Value}] = TerminalModes}}]]></c></tag>
+ <item>A pseudo-terminal has been requested for the
+ session. Terminal is the value of the TERM environment
+ variable value (e.g., vt100). Zero dimension parameters must
+ be ignored. The character/row dimensions override the pixel
+ dimensions (when nonzero). Pixel dimensions refer to the
+ drawable area of the window. The <c>Opcode</c> in the
+ <c>TerminalModes</c> list is the mnemonic name, represented
+ as an lowercase erlang atom, defined in
+ <url href="http://www.ietf.org/rfc/rfc4254.txt">RFC 4254 </url> section 8,
+ or the opcode if the mnemonic name is not listed in the
+ RFC. Example <c>OP code: 53, mnemonic name ECHO erlang atom:
+ echo</c>. There is currently no API function to generate this
+ event.</item>
+
+ <tag><c><![CDATA[{shell, boolean() = WantReply}]]></c></tag>
+ <item> This message will request that the user's default shell
+ be started at the other end. This event is sent as result of calling <seealso
+ marker="#shell"> ssh_connection:shell/2</seealso>
+ </item>
+
+ <tag><c><![CDATA[{window_change, ssh_channel_id(), integer() = CharWidth,
+ integer() = RowHeight, integer() = PixWidth, integer() = PixHeight}]]></c></tag>
+ <item> When the window (terminal) size changes on the client
+ side, it MAY send a message to the server side to inform it of
+ the new dimensions. There is currently no API function to generate this
+ event.</item>
- <p>As a result of the ssh connection protocol messages on the form
- <c><![CDATA[{ssh_cm, ssh_connection_ref(), term()}]]></c>
- will be sent to a channel process. The term will contain
- information regarding the ssh connection protocol event,
- for details see the ssh channel behavior callback <seealso
- marker="ssh_channel">handle_ssh_msg/2 </seealso> </p>
- </section>
-
- <funcs>
+ <tag><c><![CDATA[{exec, ssh_channel_id(),
+ boolean() = WantReply, string() = Cmd}]]></c></tag>
+ <item> This message will request that the server starts
+ execution of the given command. This event is sent as result of calling <seealso
+ marker="#exec">ssh_connection:exec/4 </seealso>
+ </item>
+ </taglist>
+ </item>
+ </taglist>
+ </section>
+
+ <funcs>
<func>
<name>adjust_window(ConnectionRef, ChannelId, NumOfBytes) -> ok</name>
- <fsummary>Adjusts the ssh flowcontrol window. </fsummary>
+ <fsummary>Adjusts the SSH flowcontrol window. </fsummary>
<type>
<v> ConnectionRef = ssh_connection_ref() </v>
<v> ChannelId = ssh_channel_id() </v>
<v> NumOfBytes = integer()</v>
</type>
<desc>
- <p>Adjusts the ssh flowcontrol window. </p>
+ <p>Adjusts the SSH flowcontrol window. This shall be done by both client and server side channel processes.</p>
- <note><p>This will be taken care of by the ssh_channel
- behavior when the callback <seealso marker="ssh_channel">
- handle_ssh_msg/2 </seealso> has returned after processing a
- {ssh_cm, ssh_connection_ref(), {data, ssh_channel_id(),
- ssh_data_type_code(), binary()}}
- message, and should normally not be called explicitly.</p></note>
+ <note><p>Channels implemented with the <seealso marker="ssh_channel"> ssh_channel
+ behavior</seealso> will normaly not need to call this function as flow control
+ will be handled by the behavior. The behavior will adjust the window every time
+ the callback <seealso marker="ssh_channel#handled_ssh_msg-2">
+ handle_ssh_msg/2 </seealso> has returned after processing channel data</p> </note>
</desc>
</func>
@@ -98,20 +207,19 @@
<v> ChannelId = ssh_channel_id()</v>
</type>
<desc>
- <p>Sends a close message on the channel <c>ChannelId</c>
+ <p>A server or client channel process can choose to close their session by sending a close event.
</p>
- <note><p>This function will be called by the ssh channel
+ <note><p>This function will be called by the ssh_channel
behavior when the channel is terminated see <seealso
- marker="ssh_channel"> ssh_channel(3) </seealso> and should
- normally not be called explicitly.</p></note>
+ marker="ssh_channel"> ssh_channel(3) </seealso> so channels implemented with the
+ behavior should not call this function explicitly.</p></note>
</desc>
</func>
<func>
<name>exec(ConnectionRef, ChannelId, Command, TimeOut) -> ssh_request_status() </name>
- <fsummary>Will request that the server start the
- execution of the given command. </fsummary>
+ <fsummary>Request that the server start the execution of the given command. </fsummary>
<type>
<v> ConnectionRef = ssh_connection_ref() </v>
<v> ChannelId = ssh_channel_id()</v>
@@ -119,44 +227,39 @@
<v>Timeout = timeout() </v>
</type>
<desc>
- <p>Will request that the server start the execution of the
- given command, the result will be received as:</p>
+ <p>Should be called by a client channel process to request that the server starts execution of the
+ given command, the result will be several messages according to the following pattern. Note
+ that the last message will be a channel close message, as the exec request is a one time
+ execution that closes the channel when it is done.</p>
<taglist>
- <tag><c> N X {ssh_cm,
- ssh_connection_ref(), {data, ssh_channel_id(), ssh_data_type_code(),
- binary() = Data}} </c></tag>
+ <tag><c> N x {ssh_cm, ssh_connection_ref(),
+ {data, ssh_channel_id(), ssh_data_type_code(), binary() = Data}} </c></tag>
<item>The result of executing the command may be only one line
or thousands of lines depending on the command.</item>
- <tag><c> 1 X {ssh_cm, ssh_connection_ref(), {eof, ssh_channel_id()}}</c></tag>
+ <tag><c>0 or 1 x {ssh_cm, ssh_connection_ref(), {eof, ssh_channel_id()}}</c></tag>
<item>Indicates that no more data will be sent.</item>
- <tag><c>0 or 1 X {ssh_cm,
+ <tag><c>0 or 1 x {ssh_cm,
ssh_connection_ref(), {exit_signal,
ssh_channel_id(), string() = ExitSignal, string() = ErrorMsg, string() = LanguageString}}</c></tag>
<item>Not all systems send signals. For details on valid string
values see RFC 4254 section 6.10 </item>
- <tag><c>0 or 1 X {ssh_cm, ssh_connection_ref(), {exit_status,
+ <tag><c>0 or 1 x {ssh_cm, ssh_connection_ref(), {exit_status,
ssh_channel_id(), integer() = ExitStatus}}</c></tag>
<item>It is recommended by the <c>ssh connection protocol</c> that this
message shall be sent, but that may not always be the case.</item>
- <tag><c> 1 X {ssh_cm, ssh_connection_ref(),
+ <tag><c> 1 x {ssh_cm, ssh_connection_ref(),
{closed, ssh_channel_id()}}</c></tag>
<item>Indicates that the ssh channel started for the
execution of the command has now been shutdown.</item>
</taglist>
-
- <p> These message should be handled by the
- client. The <seealso marker="ssh_channel">ssh channel
- behavior</seealso> can be used when writing a client.
- </p>
</desc>
</func>
-
<func>
<name>exit_status(ConnectionRef, ChannelId, Status) -> ok</name>
<fsummary>Sends the exit status of a command to the client.</fsummary>
@@ -166,12 +269,12 @@
<v> Status = integer()</v>
</type>
<desc>
- <p>Sends the exit status of a command to the client.</p>
+ <p>Should be called by a server channel process to sends the exit status of a command to the client.</p>
</desc>
- </func>
+ </func>
<func>
- <name>reply_request(ConnectionRef, WantReply, Status, CannelId) -> ok</name>
+ <name>reply_request(ConnectionRef, WantReply, Status, ChannelId) -> ok</name>
<fsummary>Send status replies to requests that want such replies. </fsummary>
<type>
<v> ConnectionRef = ssh_connection_ref() </v>
@@ -183,10 +286,9 @@
<p>Sends status replies to requests where the requester has
stated that they want a status report e.i .<c> WantReply = true</c>,
if <c> WantReply</c> is false calling this function will be a
- "noop". Should be called after handling an ssh connection
+ "noop". Should be called while handling an ssh connection
protocol message containing a <c>WantReply</c> boolean
- value. See the ssh_channel behavior callback <seealso
- marker="ssh_channel"> handle_ssh_msg/2 </seealso>
+ value.
</p>
</desc>
</func>
@@ -196,7 +298,7 @@
<name>send(ConnectionRef, ChannelId, Data, Timeout) -></name>
<name>send(ConnectionRef, ChannelId, Type, Data) -></name>
<name>send(ConnectionRef, ChannelId, Type, Data, TimeOut) ->
- ok | {error, timeout}</name>
+ ok | {error, timeout} | {error, closed}</name>
<fsummary>Sends channel data </fsummary>
<type>
<v> ConnectionRef = ssh_connection_ref() </v>
@@ -206,13 +308,13 @@
<v> Timeout = timeout()</v>
</type>
<desc>
- <p>Sends channel data.
+ <p>Should be called by client- and server channel processes to send data to each other.
</p>
</desc>
</func>
<func>
- <name>send_eof(ConnectionRef, ChannelId) -> ok </name>
+ <name>send_eof(ConnectionRef, ChannelId) -> ok | {error, closed}</name>
<fsummary>Sends eof on the channel <c>ChannelId</c>. </fsummary>
<type>
<v> ConnectionRef = ssh_connection_ref() </v>
@@ -228,9 +330,7 @@
<name>session_channel(ConnectionRef, Timeout) -> </name>
<name>session_channel(ConnectionRef, InitialWindowSize,
MaxPacketSize, Timeout) -> {ok, ssh_channel_id()} | {error, Reason}</name>
- <fsummary>Opens a channel for a ssh session. A session is a
- remote execution of a program. The program may be a shell, an
- application, a system command, or some built-in subsystem. </fsummary>
+ <fsummary>Opens a channel for a ssh session. </fsummary>
<type>
<v> ConnectionRef = ssh_connection_ref()</v>
<v> InitialWindowSize = integer() </v>
@@ -239,9 +339,8 @@
<v> Reason = term() </v>
</type>
<desc>
- <p>Opens a channel for a ssh session. A session is a
- remote execution of a program. The program may be a shell, an
- application, a system command, or some built-in subsystem.
+ <p>Opens a channel for an SSH session. The channel id returned from this function
+ is the id used as input to the other funtions in this module.
</p>
</desc>
</func>
@@ -258,8 +357,8 @@
<v> Timeout = timeout()</v>
</type>
<desc>
- <p> Environment variables may be passed to the shell/command to be
- started later.
+ <p> Environment variables may be passed before starting the
+ shell/command. Should be called by a client channel processes.
</p>
</desc>
</func>
@@ -267,17 +366,16 @@
<func>
<name>shell(ConnectionRef, ChannelId) -> ssh_request_status()
</name>
- <fsummary> Will request that the user's default shell (typically
- defined in /etc/passwd in UNIX systems) be started at the other
+ <fsummary> Requests that the user's default shell (typically
+ defined in /etc/passwd in UNIX systems) shall be executed at the server
end. </fsummary>
<type>
<v> ConnectionRef = ssh_connection_ref() </v>
<v> ChannelId = ssh_channel_id()</v>
</type>
<desc>
- <p> Will request that the user's default shell (typically
- defined in /etc/passwd in UNIX systems) be started at the
- other end.
+ <p> Should be called by a client channel process to request that the user's default shell (typically
+ defined in /etc/passwd in UNIX systems) shall be executed at the server end.
</p>
</desc>
</func>
@@ -292,7 +390,7 @@
<v> Timeout = timeout()</v>
</type>
<desc>
- <p> Sends a request to execute a predefined subsystem.
+ <p> Should be called by a client channel process for requesting to execute a predefined subsystem on the server.
</p>
</desc>
</func>
diff --git a/lib/ssh/doc/src/ssh_protocol.xml b/lib/ssh/doc/src/ssh_protocol.xml
new file mode 100644
index 0000000000..28f42f5707
--- /dev/null
+++ b/lib/ssh/doc/src/ssh_protocol.xml
@@ -0,0 +1,149 @@
+<?xml version="1.0" encoding="iso-8859-1" ?>
+<!DOCTYPE chapter SYSTEM "chapter.dtd">
+<!-- %EricssonCopyright% -->
+<chapter>
+ <header>
+ <copyright>
+ <year>2013</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ The program may be used and/or copied only with the written permission from
+ Ericsson AB, or in accordance with the terms and conditions stipulated in
+ the agreement/contract under which the program has been supplied.
+ </legalnotice>
+ <title>Secure Shell (SSH)</title>
+ <prepared>OTP</prepared>
+ <date></date>
+ <rev>%VSN%</rev>
+ <file>ssh_protocol.xml</file>
+ </header>
+
+ <section>
+ <title>SSH Protocol Overview</title>
+
+ <p> Conceptually the SSH protocol can be partitioned into four
+ layers:</p>
+
+ <image file="SSH_protocols.png">
+ <icaption>SSH Protocol Architecture</icaption>
+ </image>
+
+ <section>
+ <title>Transport Protocol</title>
+
+ <p> The SSH Transport Protocol is a secure, low level transport.
+ It provides strong encryption, cryptographic host
+ authentication and integrity protection. Currently, only a
+ minimum of MAC- (message authentication code, a short piece of
+ information used to authenticate a message) and encryption
+ algorithms are supported see <seealso marker="ssh">ssh(3)</seealso>
+ </p>
+ </section>
+
+ <section>
+ <title>Authentication Protocol</title>
+
+ <p>The SSH authentication protocol is a general-purpose user
+ authentication protocol run over the SSH transport
+ protocol. Erlang SSH supports user authentication using public
+ key technology (RSA and DSA, X509-certificates are currently not
+ supported). It is also possible to use a so called keyboard
+ interactive authentication. This method is suitable for
+ interactive authentication methods that do not need any special
+ software support on the client side. Instead, all authentication
+ data should be entered via the keyboard. It is also possible
+ to use a pure password based authentication scheme, note that in
+ this case the the plain text password will be encrypted before sent
+ over the network. There are several configuration options for
+ authentication handling available in
+ <seealso marker="ssh#connect-3">ssh:connect/[3,4]</seealso>
+ and <seealso marker="ssh#daemon-2">ssh:daemon/[2,3]</seealso>
+ It is also possible to customize the public key handling
+ by implementing the behaviours <seealso
+ marker="ssh_client_key_api">ssh_client_key_api</seealso> and
+ <seealso
+ marker="ssh_server_key_api">ssh_server_key_api</seealso>
+ </p>
+ </section>
+
+ <section>
+ <title>Connection Protocol</title>
+
+ <p>The SSH Connection Protocol provides application-support
+ services over the transport pipe, such as channel multiplexing,
+ flow control, remote program execution, signal propagation,
+ connection forwarding, etc. Functions for handling the SSH
+ Connection Protocol can be found in the module <seealso
+ marker="ssh_connection">ssh_connection</seealso>.
+ </p>
+ </section>
+
+ <section>
+ <title>Channels</title>
+
+ <p>All terminal sessions, forwarded connections etc., are
+ channels. Multiple channels are multiplexed into a single
+ connection, and all channels are flow-controlled. Typically an
+ SSH client will open a channel, send data/commands, receive
+ data/"control information" and when it is done close the
+ channel. The <seealso
+ marker="ssh_channel">ssh_channel</seealso> behaviour makes it easy to
+ write your own SSH client/server processes that use flow
+ control. It handles generic parts of SSH channel management and
+ lets you focus on the application logic.
+ </p>
+
+ <p>Channels comes in three flavors</p>
+
+ <list type="bulleted">
+ <item><em>Subsystem</em> - named services that can be run as
+ part of an SSH server such as SFTP <seealso
+ marker="ssh_sftpd">ssh_sftpd</seealso>, that is built in to the
+ SSH daemon (server) by default but may be disabled. The Erlang SSH
+ daemon may be configured to run any Erlang
+ implemented SSH subsystem.
+ </item>
+ <item><em>Shell</em> - interactive shell. By default the
+ Erlang daemon will run the Erlang shell. It is
+ possible to customize the shell by providing your own
+ read-eval-print loop. It is also possible, but much more work,
+ to provide your own CLI (Command Line Interface) implementation.
+ </item>
+ <item><em>Exec</em> - one-time remote execution of commands. See <seealso
+ marker="ssh_connection#exec-4">ssh_connection:exec/4</seealso></item>
+ </list>
+ </section>
+
+ <p>Channels are flow controlled. No data may be sent to a channel
+ peer until a message is received to indicate that window space is
+ available. The 'initial window size' specifies how many bytes of
+ channel data that can be sent to the channel peer without adjusting the
+ window.
+ </p>
+
+ <p>
+ For more detailed information about the SSH protocol, see the
+ following RFCs:
+ </p>
+
+ <list type="bulleted">
+ <item><url href="http://www.ietf.org/rfc/rfc4250.txt">RFC 4250</url> -
+ Protocol Assigned Numbers.</item>
+ <item><url href="http://www.ietf.org/rfc/rfc4251.txt">RFC 4251</url> -
+ Protocol Architecture.</item>
+ <item><url href="http://www.ietf.org/rfc/rfc4252.txt">RFC 4252</url> -
+ Authentication Protocol.</item>
+ <item><url href="http://www.ietf.org/rfc/rfc4253.txt">RFC 4253</url> -
+ Transport Layer Protocol.</item>
+ <item><url href="http://www.ietf.org/rfc/rfc4254.txt">RFC 4254</url> -
+ Connection Protocol.</item>
+ <item><url href="http://www.ietf.org/rfc/rfc4255.txt">RFC 4255</url> -
+ Key Fingerprints.</item>
+ <item><url href="http://www.ietf.org/rfc/rfc4344.txt">RFC 4344</url> -
+ Transport Layer Encryption Modes.</item>
+ <item><url href="http://www.ietf.org/rfc/rfc4716.txt">RFC 4716</url> -
+ Public Key File Format.</item>
+ </list>
+ </section>
+</chapter>
diff --git a/lib/ssh/doc/src/ssh_server_key_api.xml b/lib/ssh/doc/src/ssh_server_key_api.xml
new file mode 100644
index 0000000000..78ff105387
--- /dev/null
+++ b/lib/ssh/doc/src/ssh_server_key_api.xml
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="iso-8859-1" ?>
+<!DOCTYPE erlref SYSTEM "erlref.dtd">
+
+<erlref>
+ <header>
+ <copyright>
+ <year>2012</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.
+
+ The Initial Developer of the Original Code is Ericsson AB.
+ </legalnotice>
+ <title>ssh_server_key_api</title>
+ </header>
+ <module>ssh_server_key_api</module>
+ <modulesummary>
+ -behaviour(ssh_server_key_api).
+ </modulesummary>
+ <description>
+ <p> Behaviour describing the API for an SSH server's public key handling.By implementing the callbacks defined
+ in this behavior it is possible to customize the SSH server's public key
+ handling. By default the SSH application implements this behavior
+ with help of the standard openssh files, see <seealso marker="SSH_app"> ssh(6)</seealso>.</p>
+ </description>
+
+ <section>
+ <title>DATA TYPES </title>
+
+ <p>Type definitions that are used more than once in this module
+ and/or abstractions to indicate the intended use of the data
+ type:</p>
+
+ <p> boolean() = true | false</p>
+ <p> string() = [byte()]</p>
+ <p> public_key() = #'RSAPublicKey'{}| {integer(), #'Dss-Parms'{}}| term()</p>
+ <p> private_key() = #'RSAPublicKey'{}| {integer(), #'Dss-Parms'{}}| term()</p>
+ <p> public_key_algorithm() = 'ssh-rsa'| 'ssh-dss' | atom()</p>
+ </section>
+
+ <funcs>
+ <func>
+ <name>Module:host_key(Algorithm, DaemonOptions) ->
+ {ok, Key} | {error, Reason}</name>
+ <fsummary>Fetches the hosts private key </fsummary>
+ <type>
+ <v>Algorithm = public_key_algorithm()</v>
+ <d> Host key algorithm. Should support 'ssh-rsa'| 'ssh-dss' but additional algorithms
+ can be handled.</d>
+ <v> DaemonOptions = proplists:proplist() </v>
+ <d>Options provided to <seealso marker="ssh#daemon">ssh:daemon/[2,3]</seealso></d>
+ <v> Key = private_key()</v>
+ <d> The private key of the host matching the <c>Algorithm</c></d>
+ <v>Reason = term() </v>
+ </type>
+ <desc>
+ <p>Fetches the hosts private key</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>Module:is_auth_key(Key, User, DaemonOptions) -> Result</name>
+ <fsummary> Checks if the user key is authorized</fsummary>
+ <type>
+ <v> Key = public_key() </v>
+ <d> Normally an RSA or DSA public key but handling of other public keys can be added</d>
+ <v> User = string()</v>
+ <d> The user owning the public key</d>
+ <v> DaemonOptions = proplists:proplist() </v>
+ <d> Options provided to <seealso marker="ssh#daemon">ssh:daemon/[2,3]</seealso></d>
+ <v> Result = boolean()</v>
+ </type>
+ <desc>
+ <p> Checks if the user key is authorized </p>
+ </desc>
+ </func>
+
+ </funcs>
+
+</erlref>
diff --git a/lib/ssh/doc/src/ssh_sftp.xml b/lib/ssh/doc/src/ssh_sftp.xml
index c1f75461b1..0d61e57edb 100644
--- a/lib/ssh/doc/src/ssh_sftp.xml
+++ b/lib/ssh/doc/src/ssh_sftp.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>2005</year><year>2010</year>
+ <year>2005</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -22,25 +22,20 @@
</legalnotice>
<title>ssh_sftp</title>
- <prepared>Jakob Cederlund</prepared>
- <responsible></responsible>
- <docno>1</docno>
- <approved></approved>
- <checked></checked>
+ <prepared>OTP</prepared>
<date>2005-09-22</date>
- <rev>PA1</rev>
<file>ssh_sftp.sgml</file>
</header>
<module>ssh_sftp</module>
<modulesummary>SFTP client.</modulesummary>
<description>
<p>This module implements an SFTP (SSH FTP) client. SFTP is a
- secure, encrypted file transfer service available for
- SSH.</p>
+ secure, encrypted file transfer service available for
+ SSH.</p>
</description>
<section>
- <title>COMMON DATA TYPES </title>
+ <title>DATA TYPES </title>
<p>Type definitions that are used more than once in this module
and/or abstractions to indicate the intended use of the data type:
</p>
@@ -51,8 +46,8 @@
<section>
<title>TIMEOUTS </title>
- <p>If the request functions for the sftp channel return {error, timeout}
- it does not mean that the request did not reach the server and was
+ <p>If the request functions for the SFTP channel return {error, timeout}
+ it does not guarantee that the request did not reach the server and was
not performed, it only means that we did not receive an answer from the
server within the time that was expected.</p>
</section>
@@ -64,7 +59,7 @@
<name>start_channel(Host, Options) -></name>
<name>start_channel(Host, Port, Options) -> {ok, Pid} | {ok, Pid, ConnectionRef} |
{error, Reason}</name>
- <fsummary>Starts a sftp client</fsummary>
+ <fsummary>Starts a SFTP client</fsummary>
<type>
<v>Host = string()</v>
<v>ConnectionRef = ssh_connection_ref()</v>
@@ -73,11 +68,11 @@
<v>Reason = term()</v>
</type>
<desc>
- <p>If not provided, setups a ssh connection in this case a
- connection reference will be returned too. A ssh channel
- process is started to handle the communication with the SFTP
- server, the returned pid for this process should be used as
- input to all other API functions in this module.</p>
+ <p>If no connection reference is provided, a connection is set
+ up and the new connection is returned. An SSH channel process
+ is started to handle the communication with the SFTP server.
+ The returned pid for this process should be used as input to
+ all other API functions in this module.</p>
<p>Options are:</p>
<taglist>
@@ -95,13 +90,13 @@
<func>
<name>stop_channel(ChannelPid) -> ok</name>
- <fsummary>Stops the sftp client channel.</fsummary>
+ <fsummary>Stops the SFTP client channel.</fsummary>
<type>
<v>ChannelPid = pid()</v>
</type>
<desc>
- <p>Stops a sftp channel. If the ssh connection should be closed
- call <seealso marker="ssh">ssh:close/1</seealso>.</p>
+ <p>Stops an SFTP channel. Does not close the SSH connetion.
+ Use <seealso marker="ssh">ssh:close/1</seealso> to close it.</p>
</desc>
</func>
@@ -133,8 +128,9 @@
<v>Reason = term()</v>
</type>
<desc>
- <p>Writes a file to the server, like <c><![CDATA[file:write_file/2]]></c>.
- The file is created if it's not there.</p>
+ <p>Writes a file to the server, like
+ <c><![CDATA[file:write_file/2]]></c>. The file is created if
+ it does not exist or is owerwritten if it does.</p>
</desc>
</func>
<func>
@@ -169,7 +165,7 @@
</type>
<desc>
<p>Opens a file on the server, and returns a handle that
- is used for reading or writing.</p>
+ can be used for reading or writing.</p>
</desc>
</func>
<func>
@@ -184,7 +180,7 @@
</type>
<desc>
<p>Opens a handle to a directory on the server, the handle
- is used for reading directory contents.</p>
+ can be used for reading directory contents.</p>
</desc>
</func>
<func>
@@ -218,7 +214,7 @@
</type>
<desc>
<p>Reads <c><![CDATA[Len]]></c> bytes from the file referenced by
- <c><![CDATA[Handle]]></c>. Returns <c><![CDATA[{ok, Data}]]></c>, or <c><![CDATA[eof]]></c>, or
+ <c><![CDATA[Handle]]></c>. Returns <c><![CDATA[{ok, Data}]]></c>, <c><![CDATA[eof]]></c>, or
<c><![CDATA[{error, Reason}]]></c>. If the file is opened with <c><![CDATA[binary]]></c>,
<c><![CDATA[Data]]></c> is a binary, otherwise it is a string.</p>
<p>If the file is read past eof, only the remaining bytes
@@ -267,9 +263,9 @@
<v>Reason = term()</v>
</type>
<desc>
- <p>Write <c><![CDATA[data]]></c> to the file referenced by <c><![CDATA[Handle]]></c>.
+ <p>Writes<c><![CDATA[data]]></c> to the file referenced by <c><![CDATA[Handle]]></c>.
The file should be opened with <c><![CDATA[write]]></c> or <c><![CDATA[append]]></c>
- flag. Returns <c><![CDATA[ok]]></c> if successful and <c><![CDATA[{error, Reason}]]></c>
+ flag. Returns <c><![CDATA[ok]]></c> if successful or S<c><![CDATA[{error, Reason}]]></c>
otherwise.</p>
<p>Typical error reasons are:</p>
<taglist>
@@ -317,14 +313,14 @@
<v>ChannelPid = pid()</v>
<v>Handle = term()</v>
<v>Location = Offset | {bof, Offset} | {cur, Offset} | {eof, Offset} | bof | cur | eof</v>
- <v>Offset = int()</v>
+ <v>Offset = integer()</v>
<v>Timeout = timeout()</v>
<v>NewPosition = integer()</v>
<v>Reason = term()</v>
</type>
<desc>
<p>Sets the file position of the file referenced by <c><![CDATA[Handle]]></c>.
- Returns <c><![CDATA[{ok, NewPosition]]></c> (as an absolute offset) if
+ Returns <c><![CDATA[{ok, NewPosition}]]></c> (as an absolute offset) if
successful, otherwise <c><![CDATA[{error, Reason}]]></c>. <c><![CDATA[Location]]></c> is
one of the following:</p>
<taglist>
@@ -413,7 +409,7 @@
<v>Reason = term()</v>
</type>
<desc>
- <p>Read the link target from the symbolic link specified
+ <p>Reads the link target from the symbolic link specified
by <c><![CDATA[name]]></c>, like <c><![CDATA[file:read_link/1]]></c>.</p>
</desc>
</func>
@@ -490,8 +486,9 @@
<v>Reason = term()</v>
</type>
<desc>
- <p>Deletes a directory specified by <c><![CDATA[Name]]></c>. The directory
- should be empty.</p>
+ <p>Deletes a directory specified by <c><![CDATA[Name]]></c>.
+ Note that the directory must be empty before it can be successfully deleted
+ </p>
</desc>
</func>
diff --git a/lib/ssh/doc/src/ssh_sftpd.xml b/lib/ssh/doc/src/ssh_sftpd.xml
index b3d64e72b4..3666bc7692 100644
--- a/lib/ssh/doc/src/ssh_sftpd.xml
+++ b/lib/ssh/doc/src/ssh_sftpd.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>2005</year><year>2010</year>
+ <year>2005</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -22,23 +22,17 @@
</legalnotice>
<title>ssh_sftpd</title>
- <prepared>Ingela Anderton Andin</prepared>
- <responsible></responsible>
- <docno>1</docno>
- <approved></approved>
- <checked></checked>
<date>2005-09-22</date>
- <rev>PA1</rev>
<file>ssh_sftpd.sgml</file>
</header>
<module>ssh_sftpd</module>
- <modulesummary>Specifies a channel process to handle a sftp subsystem.</modulesummary>
+ <modulesummary>Specifies the channel process to handle an sftp subsystem.</modulesummary>
<description>
<p>Specifies a channel process to handle a sftp subsystem.</p>
</description>
<section>
- <title>COMMON DATA TYPES </title>
+ <title>DATA TYPES </title>
<p><c>subsystem_spec() = {subsystem_name(), {channel_callback(), channel_init_args()}} </c></p>
<p><c>subsystem_name() = "sftp"</c></p>
<p><c>channel_callback() = atom()</c> - Name of the erlang module implementing the
@@ -65,27 +59,32 @@
</item>
<tag><c><![CDATA[{file_handler, CallbackModule}]]></c></tag>
<item>
- <p>Determines which module to call for communicating with
- the file server. Default value is <c>ssh_sftpd_file</c> that uses the
- file and filelib API:s to access the standard OTP file
- server. This option may be used to plug in the use of
- other file servers.</p>
- </item>
- <tag><c><![CDATA[{max_files, Integer}]]></c></tag>
- <item>
- <p>The default value is <c>0</c>, which means that there is no upper limit.
- If supplied, the number of filenames returned to the sftp client per <c>READDIR</c>
- request, is limited to at most the given value.</p>
- </item>
+ <p>Determines which module to call for accessing
+ the file server. The default value is <c>ssh_sftpd_file</c> that uses the
+ <seealso marker="kernel#file">file</seealso> and <seealso marker="kernel#filelib">filelib</seealso> API:s to access the standard OTP file
+ server. This option may be used to plug in
+ other file servers.</p>
+ </item>
+ <tag><c><![CDATA[{max_files, Integer}]]></c></tag>
+ <item>
+ <p>The default value is <c>0</c>, which means that there is no upper limit.
+ If supplied, the number of filenames returned to the sftp client per <c>READDIR</c>
+ request is limited to at most the given value.</p>
+ </item>
<tag><c><![CDATA[{root, String}]]></c></tag>
<item>
<p>Sets the sftp root directory. The user will then not be
- able to see any files above this root. If for instance
- the root is set to <c>/tmp</c> the user will see this
- directory as <c>/</c> and if the user does cd <c>/etc</c>
- the user will end up in <c>/tmp/etc</c>.
+ able to see any files above this root. If for instance
+ the root is set to <c>/tmp</c> the user will see this
+ directory as <c>/</c> and if the user does cd <c>/etc</c>
+ the user will end up in <c>/tmp/etc</c>.
</p>
</item>
+ <tag><c><![CDATA[{sftpd_vsn, integer()}]]></c></tag>
+ <item>
+ <p>Sets the sftp version to use, defaults to 5. Version 6 is under
+ development and limited.</p>
+ </item>
</taglist>
</desc>
</func>
diff --git a/lib/ssh/doc/src/user_guide.gif b/lib/ssh/doc/src/user_guide.gif
deleted file mode 100644
index e6275a803d..0000000000
--- a/lib/ssh/doc/src/user_guide.gif
+++ /dev/null
Binary files differ
diff --git a/lib/ssh/doc/src/usersguide.xml b/lib/ssh/doc/src/usersguide.xml
new file mode 100644
index 0000000000..c818003090
--- /dev/null
+++ b/lib/ssh/doc/src/usersguide.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="iso-8859-1" ?>
+<!DOCTYPE part SYSTEM "part.dtd">
+
+<part xmlns:xi="http://www.w3.org/2001/XInclude">
+ <header>
+ <copyright>
+ <year>2012</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>SSH User's Guide</title>
+ <prepared>OTP Team</prepared>
+ <date>2012-10-11</date>
+ <file>usersguide.xml</file>
+ </header>
+ <description>
+ <p>The <em>SSH</em> application implements the SSH (Secure Shell) protocol and
+ provides an SFTP (Secret File Transfer Protocol) client and server.
+ </p>
+ </description>
+ <xi:include href="introduction.xml"/>
+ <xi:include href="ssh_protocol.xml"/>
+ <xi:include href="using_ssh.xml"/>
+</part>
diff --git a/lib/ssh/doc/src/using_ssh.xml b/lib/ssh/doc/src/using_ssh.xml
new file mode 100644
index 0000000000..87b811d591
--- /dev/null
+++ b/lib/ssh/doc/src/using_ssh.xml
@@ -0,0 +1,298 @@
+<?xml version="1.0" encoding="iso-8859-1" ?>
+<!DOCTYPE chapter SYSTEM "chapter.dtd">
+
+<chapter>
+ <header>
+ <copyright>
+ <year>2012</year>
+ <year>2013</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>Getting started</title>
+ <file>using_ssh.xml</file>
+ </header>
+
+ <section>
+ <title> General information</title>
+ <p>The examples in the following sections use the utility function
+ <seealso marker="ssh#start"> ssh:start/0 </seealso> that starts
+ all needed applications (crypto, public_key and ssh). All examples
+ are run in an Erlang shell, or in a bash shell using openssh to
+ illustrate how the erlang ssh application can be used. The
+ exampels are run as the user otptest on a local network where the
+ user is authorized to login in over ssh to the host "tarlop". If
+ nothing else is stated it is persumed that the otptest user has an
+ entry in tarlop's authorized_keys file (may log in via ssh without
+ entering a password). Also tarlop is a known host in the user
+ otptest's known_hosts file so that host verification can be done
+ without user interaction.
+ </p>
+ </section>
+
+ <section>
+ <title>Using the Erlang SSH Terminal Client</title>
+
+ <p>The user otptest, that has bash as default shell, uses the
+ ssh:shell/1 client to connect to the openssh daemon running on a
+ host called tarlop. Note that currently this client is very simple
+ and you should not be expected to be as fancy as the openssh
+ client.</p>
+
+ <code type="erl" >
+ 1> ssh:start().
+ ok
+ 2> {ok, S} = ssh:shell("tarlop").
+ >pwd
+ /home/otptest
+ >exit
+ logout
+ 3>
+ </code>
+ </section>
+
+ <section>
+ <title>Running an Erlang SSH Daemon </title>
+
+ <p> The option system_dir must be a directory containing a host
+ key file and it defaults to /etc/ssh. For details see section
+ Configuration Files in <seealso
+ marker="ssh_app">ssh(6)</seealso>.
+ </p>
+
+ <note><p>Normally the /etc/ssh directory is only readable by root. </p>
+ </note>
+
+ <p> The option user_dir defaults to the users ~/.ssh directory</p>
+
+ <p>In the following example we generate new keys and host keys as
+ to be able to run the example without having root privilages</p>
+
+ <code>
+ $bash> ssh-keygen -t rsa -f /tmp/ssh_daemon/ssh_host_rsa_key
+ [...]
+ $bash> ssh-keygen -t rsa -f /tmp/otptest_user/.ssh/id_rsa
+ [...]
+ </code>
+
+ <p>Create the file /tmp/otptest_user/.ssh/authrized_keys and add the content
+ of /tmp/otptest_user/.ssh/id_rsa.pub Now we can do</p>
+
+ <code type="erl">
+ 1> ssh:start().
+ ok
+ 2> {ok, Sshd} = ssh:daemon(8989, [{system_dir, "/tmp/ssh_daemon"},
+ {user_dir, "/tmp/otptest_user/.ssh"}]).
+ {ok,&lt;0.54.0>}
+ 3>
+ </code>
+
+ <p>Use the openssh client from a shell to connect to the Erlang ssh daemon.</p>
+
+ <code>
+ $bash> ssh tarlop -p 8989 -i /tmp/otptest_user/.ssh/id_rsa\
+ -o UserKnownHostsFile=/tmp/otptest_user/.ssh/known_hosts
+ The authenticity of host 'tarlop' can't be established.
+ RSA key fingerprint is 14:81:80:50:b1:1f:57:dd:93:a8:2d:2f:dd:90:ae:a8.
+ Are you sure you want to continue connecting (yes/no)? yes
+ Warning: Permanently added 'tarlop' (RSA) to the list of known hosts.
+ Eshell V5.10 (abort with ^G)
+ 1>
+ </code>
+
+ <p>There are two ways of shutting down an SSH daemon</p>
+
+ <p>1: Stops the listener, but leaves existing connections started by the listener up and running.</p>
+
+ <code type="erl">
+ 3> ssh:stop_listener(Sshd).
+ ok
+ 4>
+ </code>
+
+ <p>2: Stops the listener and all connections started by the listener.</p>
+
+ <code type="erl">
+ 3> ssh:stop_daemon(Sshd)
+ ok
+ 4>
+ </code>
+
+ </section>
+
+ <section>
+ <title>One Time Execution</title>
+
+ <p>In the following example the Erlang shell is the client process
+ that receives the channel replies. <note> If you run this example
+ in your environment you may get fewer or more messages back as
+ this depends on the OS and shell on the machine running the ssh
+ daemon. See also <seealso marker="ssh_connection#exec-4">ssh_connection:exec/4</seealso>
+ </note>
+ </p>
+
+ <code type="erl" >
+ 1> ssh:start().
+ ok
+ 2> {ok, ConnectionRef} = ssh:connect("tarlop", 22, []).
+ {ok,&lt;0.57.0>}
+ 3>{ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity).
+ {ok,0}
+ 4> success = ssh_connection:exec(ConnectionRef, ChannelId, "pwd", infinity).
+ 5> flush().
+ Shell got {ssh_cm,&lt;0.57.0>,{data,0,0,&lt;&lt;"/home/otptest\n">>}}
+ Shell got {ssh_cm,&lt;0.57.0>,{eof,0}}
+ Shell got {ssh_cm,&lt;0.57.0>,{exit_status,0,0}}
+ Shell got {ssh_cm,&lt;0.57.0>,{closed,0}}
+ ok
+ 6>
+ </code>
+
+ <p>Note only the channel is closed the connection is still up and can handle other channels</p>
+
+ <code type="erl" >
+ 6> {ok, NewChannelId} = ssh_connection:session_channel(ConnectionRef, infinity).
+ {ok,1}
+ ...
+ </code>
+ </section>
+
+ <section>
+ <title>SFTP (SSH File Transport Protocol) server</title>
+
+ <code type="erl" >
+ 1> ssh:start().
+ ok
+ 2> ssh:daemon(8989, [{system_dir, "/tmp/ssh_daemon"},
+ {user_dir, "/tmp/otptest_user/.ssh"},
+ {subsystems, [ssh_sftpd:subsystem_spec([{cwd, "/tmp/sftp/example"}])]}]).
+ {ok,&lt;0.54.0>}
+ 3>
+ </code>
+
+ <p> Run the openssh sftp client</p>
+
+ <code type="erl">
+ $bash> sftp -oPort=8989 -o IdentityFile=/tmp/otptest_user/.ssh/id_rsa\
+ -o UserKnownHostsFile=/tmp/otptest_user/.ssh/known_hosts tarlop
+ Connecting to tarlop...
+ sftp> pwd
+ Remote working directory: /tmp/sftp/example
+ sftp>
+ </code>
+ </section>
+
+ <section>
+ <title>SFTP (SSH File Transport Protocol) client</title>
+
+ <code type="erl" >
+ 1> ssh:start().
+ ok
+ 2> {ok, ChannelPid, Connection} = ssh_sftp:start_channel("tarlop", []).
+ {ok,&lt;0.57.0>,&lt;0.51.0>}
+ 3> ssh_sftp:read_file(ChannelPid, "/home/otptest/test.txt").
+ {ok,&lt;&lt;"This is a test file\n">>}
+ </code>
+ </section>
+
+ <section>
+ <title>Creating a subsystem</title>
+
+ <p>A very small SSH subsystem that echos N bytes could be implemented like this.
+ See also <seealso marker="ssh_channel"> ssh_channel(3)</seealso> </p>
+
+ <code type="erl" >
+-module(ssh_echo_server).
+-behaviour(ssh_subsystem).
+-record(state, {
+ n,
+ id,
+ cm
+ }).
+-export([init/1, handle_msg/2, handle_ssh_msg/2, terminate/2]).
+
+init([N]) ->
+ {ok, #state{n = N}}.
+
+handle_msg({ssh_channel_up, ChannelId, ConnectionManager}, State) ->
+ {ok, State#state{id = ChannelId,
+ cm = ConnectionManager}}.
+
+handle_ssh_msg({ssh_cm, CM, {data, ChannelId, 0, Data}}, #state{n = N} = State) ->
+ M = N - size(Data),
+ case M > 0 of
+ true ->
+ ssh_connection:send(CM, ChannelId, Data),
+ {ok, State#state{n = M}};
+ false ->
+ &lt;&lt;SendData:N/binary, _/binary>> = Data,
+ ssh_connection:send(CM, ChannelId, SendData),
+ ssh_connection:send_eof(CM, ChannelId),
+ {stop, ChannelId, State}
+ end;
+handle_ssh_msg({ssh_cm, _ConnectionManager,
+ {data, _ChannelId, 1, Data}}, State) ->
+ error_logger:format(standard_error, " ~p~n", [binary_to_list(Data)]),
+ {ok, State};
+
+handle_ssh_msg({ssh_cm, _ConnectionManager, {eof, _ChannelId}}, State) ->
+ {ok, State};
+
+handle_ssh_msg({ssh_cm, _, {signal, _, _}}, State) ->
+ %% Ignore signals according to RFC 4254 section 6.9.
+ {ok, State};
+
+handle_ssh_msg({ssh_cm, _, {exit_signal, ChannelId, _, _Error, _}},
+ State) ->
+ {stop, ChannelId, State};
+
+handle_ssh_msg({ssh_cm, _, {exit_status, ChannelId, _Status}}, State) ->
+ {stop, ChannelId, State}.
+
+terminate(_Reason, _State) ->
+ ok.
+ </code>
+
+ <p>And run like this on the host tarlop with the keys generated in section 3.3</p>
+
+ <code type="erl" >
+ 1> ssh:start().
+ ok
+ 2> ssh:daemon(8989, [{system_dir, "/tmp/ssh_daemon"},
+ {user_dir, "/tmp/otptest_user/.ssh"}
+ {subsystems, [{"echo_n", {ssh_echo_server, [10]}}]}]).
+ {ok,&lt;0.54.0>}
+ 3>
+ </code>
+
+ <code type="erl" >
+ 1> ssh:start().
+ ok
+ 2>{ok, ConnectionRef} = ssh:connect("tarlop", 8989, [{user_dir, "/tmp/otptest_user/.ssh"}]).
+ {ok,&lt;0.57.0>}
+ 3>{ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity).
+ 4> success = ssh_connection:subsystem(ConnectionRef, ChannelId, "echo_n", infinity).
+ 5> ok = ssh_connection:send(ConnectionRef, ChannelId, "0123456789", infinity).
+ 6> flush().
+ {ssh_msg, &lt;0.57.0>, {data, 0, 1, "0123456789"}}
+ {ssh_msg, &lt;0.57.0>, {eof, 0}}
+ {ssh_msg, &lt;0.57.0>, {closed, 0}}
+ 7> {error, closed} = ssh_connection:send(ConnectionRef, ChannelId, "10", infinity).
+ </code>
+
+</section>
+
+</chapter>
diff --git a/lib/ssh/examples/Makefile b/lib/ssh/examples/Makefile
index 84c7edac8c..de019f75b5 100644
--- a/lib/ssh/examples/Makefile
+++ b/lib/ssh/examples/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2005-2010. All Rights Reserved.
+# Copyright Ericsson AB 2005-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/ssh/src/Makefile b/lib/ssh/src/Makefile
index b8eecd3fa2..27e43a88ed 100644
--- a/lib/ssh/src/Makefile
+++ b/lib/ssh/src/Makefile
@@ -41,7 +41,9 @@ RELSYSDIR = $(RELEASE_PATH)/lib/ssh-$(VSN)
BEHAVIOUR_MODULES= \
ssh_sftpd_file_api \
ssh_channel \
- ssh_key_api
+ ssh_subsystem \
+ ssh_client_key_api \
+ ssh_server_key_api
MODULES= \
ssh \
@@ -118,10 +120,10 @@ clean:
rm -f errs core *~
$(APP_TARGET): $(APP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
docs:
diff --git a/lib/ssh/src/ssh.app.src b/lib/ssh/src/ssh.app.src
index 316c09eb06..a0ba7cf7d9 100644
--- a/lib/ssh/src/ssh.app.src
+++ b/lib/ssh/src/ssh.app.src
@@ -10,6 +10,7 @@
ssh_auth,
ssh_bits,
ssh_cli,
+ ssh_client_key_api,
ssh_channel,
ssh_channel_sup,
ssh_connection,
@@ -21,13 +22,14 @@
sshd_sup,
ssh_file,
ssh_io,
- ssh_key_api,
ssh_math,
ssh_no_io,
+ ssh_server_key_api,
ssh_sftp,
ssh_sftpd,
ssh_sftpd_file,
ssh_sftpd_file_api,
+ ssh_subsystem,
ssh_subsystem_sup,
ssh_sup,
ssh_system_sup,
@@ -35,7 +37,7 @@
ssh_userreg,
ssh_xfer]},
{registered, []},
- {applications, [kernel, stdlib, crypto]},
+ {applications, [kernel, stdlib, crypto, public_key]},
{env, []},
{mod, {ssh_app, []}}]}.
diff --git a/lib/ssh/src/ssh.appup.src b/lib/ssh/src/ssh.appup.src
index 0542054596..cbd8166bb9 100644
--- a/lib/ssh/src/ssh.appup.src
+++ b/lib/ssh/src/ssh.appup.src
@@ -18,12 +18,18 @@
%%
{"%VSN%",
- [
- {<<"2\\.*">>, [{restart_application, ssh}]},
- {<<"1\\.*">>, [{restart_application, ssh}]}
+ [
+ {<<"2.1.2">>, [{restart_application, ssh}]},
+ {<<"2.1.1">>, [{restart_application, ssh}]},
+ {<<"2.1">>, [{restart_application, ssh}]},
+ {<<"2.0\\.*">>, [{restart_application, ssh}]},
+ {<<"1\\.*">>, [{restart_application, ssh}]}
],
[
- {<<"2\\.*">>, [{restart_application, ssh}]},
- {<<"1\\.*">>, [{restart_application, ssh}]}
+ {<<"2.1.2">>, [{restart_application, ssh}]},
+ {<<"2.1.1">>, [{restart_application, ssh}]},
+ {<<"2.1">>,[{restart_application, ssh}]},
+ {<<"2.0\\.*">>, [{restart_application, ssh}]},
+ {<<"1\\.*">>, [{restart_application, ssh}]}
]
}.
diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl
index f4a40c81a4..193f877b98 100644
--- a/lib/ssh/src/ssh.erl
+++ b/lib/ssh/src/ssh.erl
@@ -31,29 +31,28 @@
stop_listener/1, stop_listener/2, stop_daemon/1, stop_daemon/2,
shell/1, shell/2, shell/3]).
--deprecated({sign_data, 2, next_major_release}).
--deprecated({verify_data, 3, next_major_release}).
-
--export([sign_data/2, verify_data/3]).
-
%%--------------------------------------------------------------------
%% Function: start([, Type]) -> ok
%%
%% Type = permanent | transient | temporary
%%
-%% Description: Starts the inets application. Default type
+%% Description: Starts the ssh application. Default type
%% is temporary. see application(3)
%%--------------------------------------------------------------------
start() ->
+ application:start(crypto),
+ application:start(public_key),
application:start(ssh).
start(Type) ->
+ application:start(crypto, Type),
+ application:start(public_key, Type),
application:start(ssh, Type).
%%--------------------------------------------------------------------
%% Function: stop() -> ok
%%
-%% Description: Stops the inets application.
+%% Description: Stops the ssh application.
%%--------------------------------------------------------------------
stop() ->
application:stop(ssh).
@@ -76,10 +75,10 @@ connect(Host, Port, Options, Timeout) ->
{error, _Reason} = Error ->
Error;
{SocketOptions, SshOptions} ->
- DisableIpv6 = proplists:get_value(ip_v6_disabled, SshOptions, false),
+ DisableIpv6 = proplists:get_value(ipv6_disabled, SshOptions, false),
Inet = inetopt(DisableIpv6),
do_connect(Host, Port, [Inet | SocketOptions],
- [{host, Host} | SshOptions], Timeout, DisableIpv6)
+ [{user_pid, self()}, {host, Host} | fix_idle_time(SshOptions)], Timeout, DisableIpv6)
end.
do_connect(Host, Port, SocketOptions, SshOptions, Timeout, DisableIpv6) ->
@@ -91,59 +90,39 @@ do_connect(Host, Port, SocketOptions, SshOptions, Timeout, DisableIpv6) ->
{ok, ConnectionSup} ->
{ok, Manager} =
ssh_connection_sup:connection_manager(ConnectionSup),
- MRef = erlang:monitor(process, Manager),
- receive
- {Manager, is_connected} ->
- do_demonitor(MRef, Manager),
- {ok, Manager};
- %% When the connection fails
- %% ssh_connection_sup:connection_manager
- %% might return undefined as the connection manager
- %% could allready have terminated, so we will not
- %% match the Manager in this case
- {_, not_connected, {error, econnrefused}} when DisableIpv6 == false ->
- do_demonitor(MRef, Manager),
- do_connect(Host, Port, proplists:delete(inet6, SocketOptions),
- SshOptions, Timeout, true);
- {_, not_connected, {error, Reason}} ->
- do_demonitor(MRef, Manager),
- {error, Reason};
- {_, not_connected, Other} ->
- 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, channel_closed}
- after 0 ->
- {error, channel_closed}
- end
- after Timeout ->
- do_demonitor(MRef, Manager),
- ssh_connection_manager:stop(Manager),
- {error, timeout}
- end
+ msg_loop(Manager, DisableIpv6, Host, Port, SocketOptions, SshOptions, Timeout)
catch
exit:{noproc, _} ->
{error, ssh_not_started}
end.
-
-do_demonitor(MRef, Manager) ->
- erlang:demonitor(MRef),
+msg_loop(Manager, DisableIpv6, Host, Port, SocketOptions, SshOptions, Timeout) ->
receive
- {'DOWN', MRef, _, Manager, _} ->
- ok
- after 0 ->
- ok
+ {Manager, is_connected} ->
+ {ok, Manager};
+ %% When the connection fails
+ %% ssh_connection_sup:connection_manager
+ %% might return undefined as the connection manager
+ %% could allready have terminated, so we will not
+ %% match the Manager in this case
+ {_, not_connected, {error, econnrefused}} when DisableIpv6 == false ->
+ do_connect(Host, Port, proplists:delete(inet6, SocketOptions),
+ SshOptions, Timeout, true);
+ {_, not_connected, {error, Reason}} ->
+ {error, Reason};
+ {_, not_connected, Other} ->
+ {error, Other};
+ {From, user_password} ->
+ Pass = io:get_password(),
+ From ! Pass,
+ msg_loop(Manager, DisableIpv6, Host, Port, SocketOptions, SshOptions, Timeout);
+ {From, question} ->
+ Answer = io:get_line(""),
+ From ! Answer,
+ msg_loop(Manager, DisableIpv6, Host, Port, SocketOptions, SshOptions, Timeout)
+ after Timeout ->
+ ssh_connection_manager:stop(Manager),
+ {error, timeout}
end.
-
-
%%--------------------------------------------------------------------
%% Function: close(ConnectionRef) -> ok
%%
@@ -189,7 +168,7 @@ daemon(HostAddr, Port, Options0) ->
_ ->
Options0
end,
- DisableIpv6 = proplists:get_value(ip_v6_disabled, Options0, false),
+ DisableIpv6 = proplists:get_value(ipv6_disabled, Options0, false),
{Host, Inet, Options} = case HostAddr of
any ->
{ok, Host0} = inet:gethostname(),
@@ -266,6 +245,13 @@ shell(Host, Port, Options) ->
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
+fix_idle_time(SshOptions) ->
+ case proplists:get_value(idle_time, SshOptions) of
+ undefined ->
+ [{idle_time, infinity}|SshOptions];
+ _ ->
+ SshOptions
+ end.
start_daemon(Host, Port, Options, Inet) ->
case handle_options(Options) of
{error, _Reason} = Error ->
@@ -277,7 +263,7 @@ start_daemon(Host, Port, Options, Inet) ->
do_start_daemon(Host, Port, Options, SocketOptions) ->
case ssh_system_sup:system_supervisor(Host, Port) of
undefined ->
- %% TODO: It would proably make more sense to call the
+ %% It would proably make more sense to call the
%% address option host but that is a too big change at the
%% monent. The name is a legacy name!
try sshd_sup:start_child([{address, Host},
@@ -287,7 +273,9 @@ do_start_daemon(Host, Port, Options, SocketOptions) ->
{ok, SysSup} ->
{ok, SysSup};
{error, {already_started, _}} ->
- {error, eaddrinuse}
+ {error, eaddrinuse};
+ {error, R} ->
+ {error, R}
catch
exit:{noproc, _} ->
{error, ssh_not_started}
@@ -338,8 +326,6 @@ handle_option([{user_passwords, _} = Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
handle_option([{pwdfun, _} = Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
-handle_option([{user_auth, _} = Opt | Rest],SocketOptions, SshOptions ) ->
- handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
handle_option([{key_cb, _} = Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
handle_option([{role, _} = Opt | Rest], SocketOptions, SshOptions) ->
@@ -357,7 +343,10 @@ handle_option([{disconnectfun, _} = Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
handle_option([{failfun, _} = Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
-handle_option([{ip_v6_disabled, _} = Opt | Rest], SocketOptions, SshOptions) ->
+%%Backwards compatibility should not be underscore between ip and v6 in API
+handle_option([{ip_v6_disabled, Value} | Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option({ipv6_disabled, Value}) | SshOptions]);
+handle_option([{ipv6_disabled, _} = Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
handle_option([{transport, _} = Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
@@ -369,6 +358,16 @@ handle_option([{shell, _} = Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
handle_option([{exec, _} = Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([{auth_methods, _} = Opt | Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([{pref_public_key_algs, _} = Opt | Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([{quiet_mode, _} = Opt|Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([{idle_time, _} = Opt | Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([{rekey_limit, _} = Opt|Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
handle_option([Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, [handle_inet_option(Opt) | SocketOptions], SshOptions).
@@ -382,8 +381,19 @@ handle_ssh_option({silently_accept_hosts, Value} = Opt) when Value == true; Valu
Opt;
handle_ssh_option({user_interaction, Value} = Opt) when Value == true; Value == false ->
Opt;
-handle_ssh_option({public_key_alg, Value} = Opt) when Value == ssh_rsa; Value == ssh_dsa ->
+handle_ssh_option({public_key_alg, ssh_dsa}) ->
+ {public_key_alg, 'ssh-dss'};
+handle_ssh_option({public_key_alg, ssh_rsa}) ->
+ {public_key_alg, 'ssh-rsa'};
+handle_ssh_option({public_key_alg, Value} = Opt) when Value == 'ssh-rsa'; Value == 'ssh-dss' ->
Opt;
+handle_ssh_option({pref_public_key_algs, Value} = Opt) when is_list(Value), length(Value) >= 1 ->
+ case handle_pref_algs(Value, []) of
+ {true, NewOpts} ->
+ NewOpts;
+ _ ->
+ throw({error, {eoptions, Opt}})
+ end;
handle_ssh_option({connect_timeout, Value} = Opt) when is_integer(Value); Value == infinity ->
Opt;
handle_ssh_option({user, Value} = Opt) when is_list(Value) ->
@@ -398,8 +408,6 @@ handle_ssh_option({user_passwords, Value} = Opt) when is_list(Value)->
Opt;
handle_ssh_option({pwdfun, Value} = Opt) when is_function(Value) ->
Opt;
-handle_ssh_option({user_auth, Value} = Opt) when is_function(Value) ->
- Opt;
handle_ssh_option({key_cb, Value} = Opt) when is_atom(Value) ->
Opt;
handle_ssh_option({compression, Value} = Opt) when is_atom(Value) ->
@@ -408,6 +416,8 @@ handle_ssh_option({exec, {Module, Function, _}} = Opt) when is_atom(Module),
is_atom(Function) ->
Opt;
+handle_ssh_option({auth_methods, Value} = Opt) when is_list(Value) ->
+ Opt;
handle_ssh_option({infofun, Value} = Opt) when is_function(Value) ->
Opt;
handle_ssh_option({connectfun, Value} = Opt) when is_function(Value) ->
@@ -416,7 +426,8 @@ handle_ssh_option({disconnectfun , Value} = Opt) when is_function(Value) ->
Opt;
handle_ssh_option({failfun, Value} = Opt) when is_function(Value) ->
Opt;
-handle_ssh_option({ip_v6_disabled, Value} = Opt) when Value == true;
+
+handle_ssh_option({ipv6_disabled, Value} = Opt) when Value == true;
Value == false ->
Opt;
handle_ssh_option({transport, {Protocol, Cb, ClosTag}} = Opt) when is_atom(Protocol),
@@ -432,6 +443,13 @@ handle_ssh_option({shell, {Module, Function, _}} = Opt) when is_atom(Module),
Opt;
handle_ssh_option({shell, Value} = Opt) when is_function(Value) ->
Opt;
+handle_ssh_option({quiet_mode, Value} = Opt) when Value == true;
+ Value == false ->
+ Opt;
+handle_ssh_option({idle_time, Value} = Opt) when is_integer(Value), Value > 0 ->
+ Opt;
+handle_ssh_option({rekey_limit, Value} = Opt) when is_integer(Value) ->
+ Opt;
handle_ssh_option(Opt) ->
throw({error, {eoptions, Opt}}).
@@ -440,7 +458,7 @@ handle_inet_option({active, _} = Opt) ->
"and activ is handled internaly user is not allowd"
"to specify this option"}});
handle_inet_option({inet, _} = Opt) ->
- throw({error, {{eoptions, Opt},"Is set internaly use ip_v6_disabled to"
+ throw({error, {{eoptions, Opt},"Is set internaly use ipv6_disabled to"
" enforce iv4 in the server, client will fallback to ipv4 if"
" it can not use ipv6"}});
handle_inet_option({reuseaddr, _} = Opt) ->
@@ -449,12 +467,27 @@ handle_inet_option({reuseaddr, _} = Opt) ->
%% Option verified by inet
handle_inet_option(Opt) ->
Opt.
-
+%% Check preferred algs
+handle_pref_algs([], Acc) ->
+ {true, lists:reverse(Acc)};
+handle_pref_algs([H|T], Acc) ->
+ case H of
+ ssh_dsa ->
+ handle_pref_algs(T, ['ssh-dss'| Acc]);
+ ssh_rsa ->
+ handle_pref_algs(T, ['ssh-rsa'| Acc]);
+ 'ssh-dss' ->
+ handle_pref_algs(T, ['ssh-dss'| Acc]);
+ 'ssh-rsa' ->
+ handle_pref_algs(T, ['ssh-rsa'| Acc]);
+ _ ->
+ false
+ end.
%% Has IPv6 been disabled?
inetopt(true) ->
inet;
inetopt(false) ->
- case gen_tcp:listen(0, [inet6, {ip, loopback}]) of
+ case gen_tcp:listen(0, [inet6]) of
{ok, Dummyport} ->
gen_tcp:close(Dummyport),
inet6;
@@ -465,38 +498,3 @@ inetopt(false) ->
%%%
%% Deprecated
%%%
-
-%%--------------------------------------------------------------------
-%% 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:user_key(Algorithm,[]) of
- {ok, Key} when Algorithm == "ssh-rsa" ->
- public_key:sign(Data, sha, Key);
- 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:user_key(Algorithm, []) of
- {ok, #'RSAPrivateKey'{publicExponent = E, modulus = N}} when Algorithm == "ssh-rsa" ->
- public_key:verify(Data, sha, Signature, #'RSAPublicKey'{publicExponent = E, modulus = N});
- Error ->
- Error
- end.
diff --git a/lib/ssh/src/ssh_auth.erl b/lib/ssh/src/ssh_auth.erl
index aa452a8e09..cb0c7751f0 100644
--- a/lib/ssh/src/ssh_auth.erl
+++ b/lib/ssh/src/ssh_auth.erl
@@ -48,17 +48,18 @@ publickey_msg([Alg, #ssh{user = User,
case KeyCb:user_key(Alg, Opts) of
{ok, Key} ->
+ StrAlgo = algorithm_string(Alg),
PubKeyBlob = encode_public_key(Key),
SigData = build_sig_data(SessionId,
- User, Service, PubKeyBlob, Alg),
+ User, Service, PubKeyBlob, StrAlgo),
Sig = ssh_transport:sign(SigData, Hash, Key),
- SigBlob = list_to_binary([?string(Alg), ?binary(Sig)]),
+ SigBlob = list_to_binary([?string(StrAlgo), ?binary(Sig)]),
ssh_transport:ssh_packet(
#ssh_msg_userauth_request{user = User,
service = Service,
method = "publickey",
data = [?TRUE,
- ?string(Alg),
+ ?string(StrAlgo),
?binary(PubKeyBlob),
?binary(SigBlob)]},
Ssh);
@@ -71,7 +72,7 @@ password_msg([#ssh{opts = Opts, io_cb = IoCb,
ssh_bits:install_messages(userauth_passwd_messages()),
Password = case proplists:get_value(password, Opts) of
undefined ->
- user_interaction(IoCb);
+ user_interaction(IoCb, Ssh);
PW ->
PW
end,
@@ -89,10 +90,10 @@ password_msg([#ssh{opts = Opts, io_cb = IoCb,
Ssh)
end.
-user_interaction(ssh_no_io) ->
+user_interaction(ssh_no_io, _) ->
not_ok;
-user_interaction(IoCb) ->
- IoCb:read_password("ssh password: ").
+user_interaction(IoCb, Ssh) ->
+ IoCb:read_password("ssh password: ", Ssh).
%% See RFC 4256 for info on keyboard-interactive
@@ -118,15 +119,36 @@ init_userauth_request_msg(#ssh{opts = Opts} = Ssh) ->
service = "ssh-connection",
method = "none",
data = <<>>},
- FirstAlg = algorithm(proplists:get_value(public_key_alg, Opts,
- ?PREFERRED_PK_ALG)),
- SecondAlg = other_alg(FirstAlg),
- AllowUserInt = proplists:get_value(user_interaction, Opts, true),
- Prefs = method_preference(FirstAlg, SecondAlg, AllowUserInt),
- ssh_transport:ssh_packet(Msg, Ssh#ssh{user = User,
- userauth_preference = Prefs,
- userauth_methods = none,
- service = "ssh-connection"});
+ case proplists:get_value(pref_public_key_algs, Opts, false) of
+ false ->
+ FirstAlg = proplists:get_value(public_key_alg, Opts, ?PREFERRED_PK_ALG),
+ SecondAlg = other_alg(FirstAlg),
+ AllowUserInt = proplists:get_value(user_interaction, Opts, true),
+ Prefs = method_preference(FirstAlg, SecondAlg, AllowUserInt),
+ ssh_transport:ssh_packet(Msg, Ssh#ssh{user = User,
+ userauth_preference = Prefs,
+ userauth_methods = none,
+ service = "ssh-connection"});
+ Algs ->
+ FirstAlg = lists:nth(1, Algs),
+ case length(Algs) =:= 2 of
+ true ->
+ SecondAlg = other_alg(FirstAlg),
+ AllowUserInt = proplists:get_value(user_interaction, Opts, true),
+ Prefs = method_preference(FirstAlg, SecondAlg, AllowUserInt),
+ ssh_transport:ssh_packet(Msg, Ssh#ssh{user = User,
+ userauth_preference = Prefs,
+ userauth_methods = none,
+ service = "ssh-connection"});
+ _ ->
+ AllowUserInt = proplists:get_value(user_interaction, Opts, true),
+ Prefs = method_preference(FirstAlg, AllowUserInt),
+ ssh_transport:ssh_packet(Msg, Ssh#ssh{user = User,
+ userauth_preference = Prefs,
+ userauth_methods = none,
+ service = "ssh-connection"})
+ end
+ end;
{error, no_user} ->
ErrStr = "Could not determine the users name",
throw(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_ILLEGAL_USER_NAME,
@@ -287,6 +309,15 @@ method_preference(Alg1, Alg2, false) ->
{"publickey", ?MODULE, publickey_msg,[Alg2]},
{"password", ?MODULE, password_msg, []}
].
+method_preference(Alg1, true) ->
+ [{"publickey", ?MODULE, publickey_msg, [Alg1]},
+ {"password", ?MODULE, password_msg, []},
+ {"keyboard-interactive", ?MODULE, keyboard_interactive_msg, []}
+ ];
+method_preference(Alg1, false) ->
+ [{"publickey", ?MODULE, publickey_msg, [Alg1]},
+ {"password", ?MODULE, password_msg, []}
+ ].
user_name(Opts) ->
Env = case os:type() of
@@ -327,7 +358,7 @@ verify_sig(SessionId, User, Service, Alg, KeyBlob, SigWLen, Opts) ->
{ok, Key} = decode_public_key_v2(KeyBlob, Alg),
KeyCb = proplists:get_value(key_cb, Opts, ssh_file),
- case KeyCb:is_auth_key(Key, User, Alg, Opts) of
+ case KeyCb:is_auth_key(Key, User, Opts) of
true ->
PlainText = build_sig_data(SessionId, User,
Service, KeyBlob, Alg),
@@ -350,9 +381,9 @@ build_sig_data(SessionId, User, Service, KeyBlob, Alg) ->
?binary(KeyBlob)],
list_to_binary(Sig).
-algorithm(ssh_rsa) ->
+algorithm_string('ssh-rsa') ->
"ssh-rsa";
-algorithm(ssh_dsa) ->
+algorithm_string('ssh-dss') ->
"ssh-dss".
decode_keyboard_interactive_prompts(NumPrompts, Data) ->
@@ -370,11 +401,11 @@ keyboard_interact_get_responses(IoCb, Opts, Name, Instr, PromptInfos) ->
%% Special case/fallback for just one prompt
%% (assumed to be the password prompt)
case proplists:get_value(password, Opts) of
- undefined -> keyboard_interact(IoCb, Name, Instr, PromptInfos);
+ undefined -> keyboard_interact(IoCb, Name, Instr, PromptInfos, Opts);
PW -> [PW]
end;
undefined ->
- keyboard_interact(IoCb, Name, Instr, PromptInfos);
+ keyboard_interact(IoCb, Name, Instr, PromptInfos, Opts);
KbdInteractFun ->
Prompts = lists:map(fun({Prompt, _Echo}) -> Prompt end,
PromptInfos),
@@ -388,15 +419,15 @@ keyboard_interact_get_responses(IoCb, Opts, Name, Instr, PromptInfos) ->
end
end.
-keyboard_interact(IoCb, Name, Instr, Prompts) ->
+keyboard_interact(IoCb, Name, Instr, Prompts, Opts) ->
if Name /= "" -> IoCb:format("~s", [Name]);
true -> ok
end,
if Instr /= "" -> IoCb:format("~s", [Instr]);
true -> ok
end,
- lists:map(fun({Prompt, true}) -> IoCb:read_line(Prompt);
- ({Prompt, false}) -> IoCb:read_password(Prompt)
+ lists:map(fun({Prompt, true}) -> IoCb:read_line(Prompt, Opts);
+ ({Prompt, false}) -> IoCb:read_password(Prompt, Opts)
end,
Prompts).
@@ -426,10 +457,10 @@ userauth_pk_messages() ->
binary]} % key blob
].
-other_alg("ssh-rsa") ->
- "ssh-dss";
-other_alg("ssh-dss") ->
- "ssh-rsa".
+other_alg('ssh-rsa') ->
+ 'ssh-dss';
+other_alg('ssh-dss') ->
+ 'ssh-rsa'.
decode_public_key_v2(K_S, "ssh-rsa") ->
case ssh_bits:decode(K_S,[string,mpint,mpint]) of
["ssh-rsa", E, N] ->
diff --git a/lib/ssh/src/ssh_auth.hrl b/lib/ssh/src/ssh_auth.hrl
index 7d7bad4436..6cd8e6bf14 100644
--- a/lib/ssh/src/ssh_auth.hrl
+++ b/lib/ssh/src/ssh_auth.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -21,9 +21,9 @@
%%% Description: Ssh User Authentication Protocol
--define(SUPPORTED_AUTH_METHODS, "publickey,keyboard_interactive,password").
+-define(SUPPORTED_AUTH_METHODS, "publickey,keyboard-interactive,password").
--define(PREFERRED_PK_ALG, ssh_rsa).
+-define(PREFERRED_PK_ALG, 'ssh-rsa').
-define(SSH_MSG_USERAUTH_REQUEST, 50).
-define(SSH_MSG_USERAUTH_FAILURE, 51).
diff --git a/lib/ssh/src/ssh_channel.erl b/lib/ssh/src/ssh_channel.erl
index 1938858420..4e8f8538c2 100644
--- a/lib/ssh/src/ssh_channel.erl
+++ b/lib/ssh/src/ssh_channel.erl
@@ -23,14 +23,35 @@
-include("ssh_connect.hrl").
-%%% Optional callbacks handle_call/3, handle_cast/2, handle_msg/2,
-%%% code_change/3
-%% Should be further specified later
--callback init(Options::list()) ->
- {ok, State::term()} | {ok, State::term(), Timeout::timeout()} |
- {stop, Reason ::term()}.
-
--callback terminate(term(), term()) -> term().
+-callback init(Args :: term()) ->
+ {ok, State :: term()} | {ok, State :: term(), timeout() | hibernate} |
+ {stop, Reason :: term()} | ignore.
+-callback handle_call(Request :: term(), From :: {pid(), Tag :: term()},
+ State :: term()) ->
+ {reply, Reply :: term(), NewState :: term()} |
+ {reply, Reply :: term(), NewState :: term(), timeout() | hibernate} |
+ {noreply, NewState :: term()} |
+ {noreply, NewState :: term(), timeout() | hibernate} |
+ {stop, Reason :: term(), Reply :: term(), NewState :: term()} |
+ {stop, Reason :: term(), NewState :: term()}.
+-callback handle_cast(Request :: term(), State :: term()) ->
+ {noreply, NewState :: term()} |
+ {noreply, NewState :: term(), timeout() | hibernate} |
+ {stop, Reason :: term(), NewState :: term()}.
+
+-callback terminate(Reason :: (normal | shutdown | {shutdown, term()} |
+ term()),
+ State :: term()) ->
+ term().
+-callback code_change(OldVsn :: (term() | {down, term()}), State :: term(),
+ Extra :: term()) ->
+ {ok, NewState :: term()} | {error, Reason :: term()}.
+
+-callback handle_msg(Msg ::term(), State :: term()) ->
+ {noreply, NewState :: term()} |
+ {noreply, NewState :: term(), timeout() | hibernate} |
+ {stop, Reason :: term(), NewState :: term()}.
+
-callback handle_ssh_msg({ssh_cm, ConnectionRef::term(), SshMsg::term()},
State::term()) -> {ok, State::term()} |
diff --git a/lib/ssh/src/ssh_cli.erl b/lib/ssh/src/ssh_cli.erl
index 781e01b9d1..c8c610f8ef 100644
--- a/lib/ssh/src/ssh_cli.erl
+++ b/lib/ssh/src/ssh_cli.erl
@@ -81,7 +81,8 @@ handle_ssh_msg({ssh_cm, ConnectionManager,
height = not_zero(Height, 24),
pixel_width = PixWidth,
pixel_height = PixHeight,
- modes = Modes}},
+ modes = Modes},
+ buf = empty_buf()},
set_echo(State),
ssh_connection:reply_request(ConnectionManager, WantReply,
success, ChannelId),
diff --git a/lib/ssh/src/ssh_client_key.erl b/lib/ssh/src/ssh_client_key.erl
new file mode 100644
index 0000000000..2c48884dc2
--- /dev/null
+++ b/lib/ssh/src/ssh_client_key.erl
@@ -0,0 +1,34 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2011-2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, 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(ssh_client_key).
+
+-include_lib("public_key/include/public_key.hrl").
+-include("ssh.hrl").
+
+-callback is_host_key(Key :: public_key(), Host :: string(),
+ Algorithm :: 'ssh-rsa'| 'ssh-dsa'| atom(), Options :: proplists:proplist()) ->
+ boolean().
+
+-callback user_key(Algorithm :: 'ssh-rsa'| 'ssh-dsa'| atom(), Options :: list()) ->
+ {ok, PrivateKey :: term()} | {error, string()}.
+
+
+-callback add_host_key(Host :: string(), PublicKey :: term(), Options :: list()) ->
+ ok | {error, Error::term()}.
diff --git a/lib/ssh/src/ssh_client_key_api.erl b/lib/ssh/src/ssh_client_key_api.erl
new file mode 100644
index 0000000000..58054a9fc5
--- /dev/null
+++ b/lib/ssh/src/ssh_client_key_api.erl
@@ -0,0 +1,35 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2011-2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, 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(ssh_client_key_api).
+
+-include_lib("public_key/include/public_key.hrl").
+-include("ssh.hrl").
+
+-callback is_host_key(PublicKey :: #'RSAPublicKey'{}| {integer(), #'Dss-Parms'{}}| term() , Host :: string(),
+ Algorithm :: 'ssh-rsa'| 'ssh-dss'| atom(), ConnectOptions :: proplists:proplist()) ->
+ boolean().
+
+-callback user_key(Algorithm :: 'ssh-rsa'| 'ssh-dss'| atom(), ConnectOptions :: proplists:proplist()) ->
+ {ok, PrivateKey :: #'RSAPrivateKey'{}| #'DSAPrivateKey'{} | term()} | {error, string()}.
+
+
+-callback add_host_key(Host :: string(), PublicKey :: #'RSAPublicKey'{}| {integer(), #'Dss-Parms'{}}| term(),
+ Options :: list()) ->
+ ok | {error, Error::term()}.
diff --git a/lib/ssh/src/ssh_connection.erl b/lib/ssh/src/ssh_connection.erl
index c46f799b6d..9424cdd423 100644
--- a/lib/ssh/src/ssh_connection.erl
+++ b/lib/ssh/src/ssh_connection.erl
@@ -177,7 +177,7 @@ close(ConnectionManager, ChannelId) ->
%% Description: Send status replies to requests that want such replies.
%%--------------------------------------------------------------------
reply_request(ConnectionManager, true, Status, ChannelId) ->
- ConnectionManager ! {ssh_cm, self(), {Status, ChannelId}},
+ ssh_connection_manager:reply_request(ConnectionManager, Status, ChannelId),
ok;
reply_request(_,false, _, _) ->
ok.
@@ -318,21 +318,22 @@ channel_data(ChannelId, DataType, Data,
From) ->
case ssh_channel:cache_lookup(Cache, ChannelId) of
- #channel{remote_id = Id} = Channel0 ->
- {SendList, Channel} = update_send_window(Channel0, DataType,
+ #channel{remote_id = Id, sent_close = false} = Channel0 ->
+ {SendList, Channel} = update_send_window(Channel0#channel{flow_control = From}, DataType,
Data, Connection),
Replies =
lists:map(fun({SendDataType, SendData}) ->
- {connection_reply, ConnectionPid,
- channel_data_msg(Id,
- SendDataType,
- SendData)}
+ {connection_reply, ConnectionPid,
+ channel_data_msg(Id,
+ SendDataType,
+ SendData)}
end, SendList),
FlowCtrlMsgs = flow_control(Replies,
- Channel#channel{flow_control = From},
+ Channel,
Cache),
{{replies, Replies ++ FlowCtrlMsgs}, Connection};
- undefined ->
+ _ ->
+ gen_server:reply(From, {error, closed}),
{noreply, Connection}
end.
@@ -386,20 +387,30 @@ handle_msg(#ssh_msg_channel_close{recipient_channel = ChannelId},
ConnectionPid, _) ->
case ssh_channel:cache_lookup(Cache, ChannelId) of
- #channel{sent_close = Closed, remote_id = RemoteId} = Channel ->
+ #channel{sent_close = Closed, remote_id = RemoteId, flow_control = FlowControl} = Channel ->
ssh_channel:cache_delete(Cache, ChannelId),
{CloseMsg, Connection} =
reply_msg(Channel, Connection0, {closed, ChannelId}),
+
+ ConnReplyMsgs =
case Closed of
- true ->
- {{replies, [CloseMsg]}, Connection};
+ true -> [];
false ->
RemoteCloseMsg = channel_close_msg(RemoteId),
- {{replies,
- [{connection_reply,
- ConnectionPid, RemoteCloseMsg},
- CloseMsg]}, Connection}
- end;
+ [{connection_reply, ConnectionPid, RemoteCloseMsg}]
+ end,
+
+ %% if there was a send() in progress, make it fail
+ SendReplyMsgs =
+ case FlowControl of
+ undefined -> [];
+ From ->
+ [{flow_control, From, {error, closed}}]
+ end,
+
+ Replies = ConnReplyMsgs ++ [CloseMsg] ++ SendReplyMsgs,
+ {{replies, Replies}, Connection};
+
undefined ->
{{replies, []}, Connection0}
end;
@@ -436,32 +447,32 @@ handle_msg(#ssh_msg_channel_window_adjust{recipient_channel = ChannelId,
#connection{channel_cache = Cache} = Connection,
ConnectionPid, _) ->
- #channel{send_window_size = Size} =
+ #channel{send_window_size = Size, remote_id = RemoteId} =
Channel0 = ssh_channel:cache_lookup(Cache, ChannelId),
-
+
{SendList, Channel} = %% TODO: Datatype 0 ?
update_send_window(Channel0#channel{send_window_size = Size + Add},
- 0, <<>>, Connection),
+ 0, undefined, Connection),
Replies = lists:map(fun({Type, Data}) ->
{connection_reply, ConnectionPid,
- channel_data_msg(ChannelId, Type, Data)}
+ channel_data_msg(RemoteId, Type, Data)}
end, SendList),
FlowCtrlMsgs = flow_control(Channel, Cache),
{{replies, Replies ++ FlowCtrlMsgs}, Connection};
handle_msg(#ssh_msg_channel_open{channel_type = "session" = Type,
- sender_channel = ChannelId,
+ sender_channel = RemoteId,
initial_window_size = WindowSz,
maximum_packet_size = PacketSz}, Connection0,
ConnectionPid, server) ->
- try setup_session(Connection0, ConnectionPid, ChannelId,
+ try setup_session(Connection0, ConnectionPid, RemoteId,
Type, WindowSz, PacketSz) of
Result ->
Result
catch _:_ ->
- FailMsg = channel_open_failure_msg(ChannelId,
+ FailMsg = channel_open_failure_msg(RemoteId,
?SSH_OPEN_CONNECT_FAILED,
"Connection refused", "en"),
{{replies, [{connection_reply, ConnectionPid, FailMsg}]},
@@ -532,9 +543,9 @@ handle_msg(#ssh_msg_channel_open{channel_type = "forwarded-tcpip",
{{replies, [{connection_reply, ConnectionPid, FailMsg}]}, Connection};
-handle_msg(#ssh_msg_channel_open{sender_channel = ChannelId}, Connection,
+handle_msg(#ssh_msg_channel_open{sender_channel = RemoteId}, Connection,
ConnectionPid, _) ->
- FailMsg = channel_open_failure_msg(ChannelId,
+ FailMsg = channel_open_failure_msg(RemoteId,
?SSH_OPEN_ADMINISTRATIVELY_PROHIBITED,
"Not allowed", "en"),
{{replies, [{connection_reply, ConnectionPid, FailMsg}]}, Connection};
@@ -1073,14 +1084,15 @@ request_reply_or_data(#channel{local_id = ChannelId, user = ChannelPid},
false ->
{{channel_data, ChannelPid, Reply}, Connection}
end.
+update_send_window(Channel, _, undefined,
+ #connection{channel_cache = Cache}) ->
+ do_update_send_window(Channel, Channel#channel.send_buf, Cache);
-update_send_window(Channel0, DataType, Data,
- #connection{channel_cache = Cache}) ->
- Buf0 = if Data == <<>> ->
- Channel0#channel.send_buf;
- true ->
- Channel0#channel.send_buf ++ [{DataType, Data}]
- end,
+update_send_window(Channel, DataType, Data,
+ #connection{channel_cache = Cache}) ->
+ do_update_send_window(Channel, Channel#channel.send_buf ++ [{DataType, Data}], Cache).
+
+do_update_send_window(Channel0, Buf0, Cache) ->
{Buf1, NewSz, Buf2} = get_window(Buf0,
Channel0#channel.send_packet_size,
Channel0#channel.send_window_size),
@@ -1125,13 +1137,13 @@ flow_control(Channel, Cache) ->
flow_control([], Channel, Cache) ->
ssh_channel:cache_update(Cache, Channel),
[];
-flow_control([_|_], #channel{flow_control = From} = Channel, Cache) ->
- case From of
- undefined ->
- [];
- _ ->
- [{flow_control, Cache, Channel, From, ok}]
- end.
+
+flow_control([_|_], #channel{flow_control = From,
+ send_buf = []} = Channel, Cache) when From =/= undefined ->
+ [{flow_control, Cache, Channel, From, ok}];
+flow_control(_,_,_) ->
+ [].
+
encode_pty_opts(Opts) ->
Bin = list_to_binary(encode_pty_opts2(Opts)),
diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl
index 5b3d1b8a1b..9378686242 100644
--- a/lib/ssh/src/ssh_connection_handler.erl
+++ b/lib/ssh/src/ssh_connection_handler.erl
@@ -35,7 +35,8 @@
-export([start_link/4, send/2, renegotiate/1, send_event/2,
connection_info/3,
- peer_address/1]).
+ peer_address/1,
+ renegotiate_data/1]).
%% gen_fsm callbacks
-export([hello/2, kexinit/2, key_exchange/2, new_keys/2,
@@ -85,6 +86,8 @@ send(ConnectionHandler, Data) ->
renegotiate(ConnectionHandler) ->
send_all_state_event(ConnectionHandler, renegotiate).
+renegotiate_data(ConnectionHandler) ->
+ send_all_state_event(ConnectionHandler, data_size).
connection_info(ConnectionHandler, From, Options) ->
send_all_state_event(ConnectionHandler, {info, From, Options}).
@@ -419,11 +422,15 @@ userauth(#ssh_msg_userauth_failure{authentications = Methodes},
#state{ssh_params = #ssh{role = client,
userauth_methods = none} = Ssh0} = State) ->
AuthMethods = string:tokens(Methodes, ","),
- {Msg, Ssh} = ssh_auth:userauth_request_msg(
- Ssh0#ssh{userauth_methods = AuthMethods}),
- send_msg(Msg, State),
- {next_state, userauth, next_packet(State#state{ssh_params = Ssh})};
-
+ Ssh1 = Ssh0#ssh{userauth_methods = AuthMethods},
+ case ssh_auth:userauth_request_msg(Ssh1) of
+ {disconnect, DisconnectMsg, {Msg, Ssh}} ->
+ send_msg(Msg, State),
+ handle_disconnect(DisconnectMsg, State#state{ssh_params = Ssh});
+ {Msg, Ssh} ->
+ send_msg(Msg, State),
+ {next_state, userauth, next_packet(State#state{ssh_params = Ssh})}
+ end;
%% The prefered authentication method failed try next method
userauth(#ssh_msg_userauth_failure{},
#state{ssh_params = #ssh{role = client} = Ssh0} = State) ->
@@ -500,7 +507,22 @@ handle_event(renegotiate, StateName, State) ->
handle_event({info, From, Options}, StateName, #state{ssh_params = Ssh} = State) ->
spawn(?MODULE, ssh_info_handler, [Options, Ssh, From]),
{next_state, StateName, State};
-
+handle_event(data_size, connected, #state{ssh_params = Ssh0} = State) ->
+ Sent = inet:getstat(State#state.socket, [send_oct]),
+ MaxSent = proplists:get_value(rekey_limit, State#state.opts, 1024000000),
+ case Sent >= MaxSent of
+ true ->
+ {KeyInitMsg, SshPacket, Ssh} = ssh_transport:key_exchange_init_msg(Ssh0),
+ send_msg(SshPacket, State),
+ {next_state, connected,
+ next_packet(State#state{ssh_params = Ssh,
+ key_exchange_init_msg = KeyInitMsg,
+ renegotiate = true})};
+ _ ->
+ {next_state, connected, next_packet(State)}
+ end;
+handle_event(data_size, StateName, State) ->
+ {next_state, StateName, State};
handle_event({unknown, Data}, StateName, State) ->
Msg = #ssh_msg_unimplemented{sequence = Data},
send_msg(Msg, State),
@@ -718,8 +740,18 @@ init_ssh(server = Role, Vsn, Version, Options, Socket) ->
available_host_keys = supported_host_keys(Role, KeyCb, Options)
}.
-supported_host_keys(client, _, _) ->
- ["ssh-rsa", "ssh-dss"];
+supported_host_keys(client, _, Options) ->
+ try
+ case extract_algs(proplists:get_value(pref_public_key_algs, Options, false), []) of
+ false ->
+ ["ssh-rsa", "ssh-dss"];
+ Algs ->
+ Algs
+ end
+ catch
+ exit:Reason ->
+ {stop, {shutdown, Reason}}
+ end;
supported_host_keys(server, KeyCb, Options) ->
lists:foldl(fun(Type, Acc) ->
case available_host_key(KeyCb, Type, Options) of
@@ -731,7 +763,19 @@ supported_host_keys(server, KeyCb, Options) ->
end, [],
%% Prefered alg last so no need to reverse
["ssh-dss", "ssh-rsa"]).
-
+extract_algs(false, _) ->
+ false;
+extract_algs([],[]) ->
+ false;
+extract_algs([], NewList) ->
+ lists:reverse(NewList);
+extract_algs([H|T], NewList) ->
+ case H of
+ 'ssh-dss' ->
+ extract_algs(T, ["ssh-dss"|NewList]);
+ 'ssh-rsa' ->
+ extract_algs(T, ["ssh-rsa"|NewList])
+ end.
available_host_key(KeyCb, "ssh-dss"= Alg, Opts) ->
case KeyCb:host_key('ssh-dss', Opts) of
{ok, _} ->
diff --git a/lib/ssh/src/ssh_connection_manager.erl b/lib/ssh/src/ssh_connection_manager.erl
index e53cd4f4f7..94a9ed505f 100644
--- a/lib/ssh/src/ssh_connection_manager.erl
+++ b/lib/ssh/src/ssh_connection_manager.erl
@@ -40,7 +40,7 @@
close/2, stop/1, send/5,
send_eof/2]).
--export([open_channel/6, request/6, request/7, global_request/4, event/2,
+-export([open_channel/6, reply_request/3, request/6, request/7, global_request/4, event/2,
cast/2]).
%% Internal application API and spawn
@@ -62,6 +62,7 @@
latest_channel_id = 0,
opts,
channel_args,
+ idle_timer_ref, % timerref
connected
}).
@@ -95,6 +96,9 @@ request(ConnectionManager, ChannelId, Type, true, Data, Timeout) ->
request(ConnectionManager, ChannelId, Type, false, Data, _) ->
cast(ConnectionManager, {request, ChannelId, Type, Data}).
+reply_request(ConnectionManager, Status, ChannelId) ->
+ cast(ConnectionManager, {reply_request, Status, ChannelId}).
+
global_request(ConnectionManager, Type, true = Reply, Data) ->
case call(ConnectionManager,
{global_request, self(), Type, Reply, Data}) of
@@ -121,7 +125,8 @@ info(ConnectionManager, ChannelProcess) ->
%% or amount of data sent counter!
renegotiate(ConnectionManager) ->
cast(ConnectionManager, renegotiate).
-
+renegotiate_data(ConnectionManager) ->
+ cast(ConnectionManager, renegotiate_data).
connection_info(ConnectionManager, Options) ->
call(ConnectionManager, {connection_info, Options}).
@@ -163,7 +168,7 @@ send(ConnectionManager, ChannelId, Type, Data, Timeout) ->
call(ConnectionManager, {data, ChannelId, Type, Data}, Timeout).
send_eof(ConnectionManager, ChannelId) ->
- cast(ConnectionManager, {eof, ChannelId}).
+ call(ConnectionManager, {eof, ChannelId}).
%%====================================================================
%% gen_server callbacks
@@ -200,6 +205,8 @@ init([client, Opts]) ->
ChannelPid = proplists:get_value(channel_pid, Opts),
self() !
{start_connection, client, [Parent, Address, Port, SocketOpts, Options]},
+ TimerRef = get_idle_time(Options),
+
{ok, #state{role = client,
client = ChannelPid,
connection_state = #connection{channel_cache = Cache,
@@ -208,6 +215,7 @@ init([client, Opts]) ->
connection_supervisor = Parent,
requests = []},
opts = Opts,
+ idle_timer_ref = TimerRef,
connected = false}}.
%%--------------------------------------------------------------------
@@ -227,6 +235,13 @@ handle_call({request, ChannelPid, ChannelId, Type, Data}, From, State0) ->
%% channel is sent later when reply arrives from the connection
%% handler.
lists:foreach(fun send_msg/1, Replies),
+ SshOpts = proplists:get_value(ssh_opts, State0#state.opts),
+ case proplists:get_value(idle_time, SshOpts) of
+ infinity ->
+ ok;
+ _IdleTime ->
+ erlang:send_after(5000, self(), {check_cache, [], []})
+ end,
{noreply, State};
handle_call({request, ChannelId, Type, Data}, From, State0) ->
@@ -295,6 +310,18 @@ handle_call({data, ChannelId, Type, Data}, From,
channel_data(ChannelId, Type, Data, Connection0, ConnectionPid, From,
State);
+handle_call({eof, ChannelId}, _From,
+ #state{connection = Pid, connection_state =
+ #connection{channel_cache = Cache}} = State) ->
+ case ssh_channel:cache_lookup(Cache, ChannelId) of
+ #channel{remote_id = Id, sent_close = false} ->
+ send_msg({connection_reply, Pid,
+ ssh_connection:channel_eof_msg(Id)}),
+ {reply, ok, State};
+ _ ->
+ {reply, {error,closed}, State}
+ end;
+
handle_call({connection_info, Options}, From,
#state{connection = Connection} = State) ->
ssh_connection_handler:connection_info(Connection, From, Options),
@@ -343,7 +370,7 @@ handle_call({open, ChannelPid, Type, InitialWindowSize, MaxPacketSize, Data},
recv_packet_size = MaxPacketSize},
ssh_channel:cache_update(Cache, Channel),
State = add_request(true, ChannelId, From, State1),
- {noreply, State};
+ {noreply, remove_timer_ref(State)};
handle_call({send_window, ChannelId}, _From,
#state{connection_state =
@@ -388,6 +415,13 @@ handle_call({close, ChannelId}, _,
send_msg({connection_reply, Pid,
ssh_connection:channel_close_msg(Id)}),
ssh_channel:cache_update(Cache, Channel#channel{sent_close = true}),
+ SshOpts = proplists:get_value(ssh_opts, State#state.opts),
+ case proplists:get_value(idle_time, SshOpts) of
+ infinity ->
+ ok;
+ _IdleTime ->
+ erlang:send_after(5000, self(), {check_cache, [], []})
+ end,
{reply, ok, State};
undefined ->
{reply, ok, State}
@@ -431,6 +465,16 @@ handle_cast({request, ChannelId, Type, Data}, State0) ->
lists:foreach(fun send_msg/1, Replies),
{noreply, State};
+handle_cast({reply_request, Status, ChannelId}, #state{connection_state =
+ #connection{channel_cache = Cache}} = State0) ->
+ State = case ssh_channel:cache_lookup(Cache, ChannelId) of
+ #channel{remote_id = RemoteId} ->
+ cm_message({Status, RemoteId}, State0);
+ undefined ->
+ State0
+ end,
+ {noreply, State};
+
handle_cast({global_request, _, _, _, _} = Request, State0) ->
State = handle_global_request(Request, State0),
{noreply, State};
@@ -438,7 +482,9 @@ handle_cast({global_request, _, _, _, _} = Request, State0) ->
handle_cast(renegotiate, #state{connection = Pid} = State) ->
ssh_connection_handler:renegotiate(Pid),
{noreply, State};
-
+handle_cast(renegotiate_data, #state{connection = Pid} = State) ->
+ ssh_connection_handler:renegotiate_data(Pid),
+ {noreply, State};
handle_cast({adjust_window, ChannelId, Bytes},
#state{connection = Pid, connection_state =
#connection{channel_cache = Cache}} = State) ->
@@ -453,18 +499,6 @@ handle_cast({adjust_window, ChannelId, Bytes},
end,
{noreply, State};
-handle_cast({eof, ChannelId},
- #state{connection = Pid, connection_state =
- #connection{channel_cache = Cache}} = State) ->
- case ssh_channel:cache_lookup(Cache, ChannelId) of
- #channel{remote_id = Id} ->
- send_msg({connection_reply, Pid,
- ssh_connection:channel_eof_msg(Id)}),
- {noreply, State};
- undefined ->
- {noreply, State}
- end;
-
handle_cast({success, ChannelId}, #state{connection = Pid} = State) ->
Msg = ssh_connection:channel_success_msg(ChannelId),
send_msg({connection_reply, Pid, Msg}),
@@ -489,6 +523,8 @@ handle_info({start_connection, server,
Exec = proplists:get_value(exec, Options),
CliSpec = proplists:get_value(ssh_cli, Options, {ssh_cli, [Shell]}),
ssh_connection_handler:send_event(Connection, socket_control),
+ erlang:send_after(3600000, self(), rekey),
+ erlang:send_after(60000, self(), rekey_data),
{noreply, State#state{connection = Connection,
connection_state =
CState#connection{address = Address,
@@ -505,12 +541,17 @@ handle_info({start_connection, client,
case (catch ssh_transport:connect(Parent, Address,
Port, SocketOpts, Options)) of
{ok, Connection} ->
+ erlang:send_after(60000, self(), rekey_data),
+ erlang:send_after(3600000, self(), rekey),
{noreply, State#state{connection = Connection}};
Reason ->
Pid ! {self(), not_connected, Reason},
{stop, {shutdown, normal}, State}
end;
-
+handle_info({check_cache, _ , _},
+ #state{connection_state =
+ #connection{channel_cache = Cache}} = State) ->
+ {noreply, check_cache(State, Cache)};
handle_info({ssh_cm, _Sender, Msg}, State0) ->
%% Backwards compatibility!
State = cm_message(Msg, State0),
@@ -523,7 +564,7 @@ handle_info({same_user, _}, State) ->
handle_info(ssh_connected, #state{role = client, client = Pid}
= State) ->
Pid ! {self(), is_connected},
- {noreply, State#state{connected = true}};
+ {noreply, State#state{connected = true, opts = handle_password(State#state.opts)}};
handle_info(ssh_connected, #state{role = server} = State) ->
{noreply, State#state{connected = true}};
@@ -534,8 +575,56 @@ handle_info({'DOWN', _Ref, process, ChannelPid, _Reason}, State) ->
%%% So that terminate will be run when supervisor is shutdown
handle_info({'EXIT', _Sup, Reason}, State) ->
- {stop, Reason, State}.
-
+ {stop, Reason, State};
+handle_info(rekey, State) ->
+ renegotiate(self()),
+ erlang:send_after(3600000, self(), rekey),
+ {noreply, State};
+handle_info(rekey_data, State) ->
+ renegotiate_data(self()),
+ erlang:send_after(60000, self(), rekey_data),
+ {noreply, State}.
+handle_password(Opts) ->
+ handle_rsa_password(handle_dsa_password(handle_normal_password(Opts))).
+handle_normal_password(Opts) ->
+ case proplists:get_value(ssh_opts, Opts, false) of
+ false ->
+ Opts;
+ SshOpts ->
+ case proplists:get_value(password, SshOpts, false) of
+ false ->
+ Opts;
+ _Password ->
+ NewOpts = [{password, undefined}|lists:keydelete(password, 1, SshOpts)],
+ [{ssh_opts, NewOpts}|lists:keydelete(ssh_opts, 1, Opts)]
+ end
+ end.
+handle_dsa_password(Opts) ->
+ case proplists:get_value(ssh_opts, Opts, false) of
+ false ->
+ Opts;
+ SshOpts ->
+ case proplists:get_value(dsa_pass_phrase, SshOpts, false) of
+ false ->
+ Opts;
+ _Password ->
+ NewOpts = [{dsa_pass_phrase, undefined}|lists:keydelete(dsa_pass_phrase, 1, SshOpts)],
+ [{ssh_opts, NewOpts}|lists:keydelete(ssh_opts, 1, Opts)]
+ end
+ end.
+handle_rsa_password(Opts) ->
+ case proplists:get_value(ssh_opts, Opts, false) of
+ false ->
+ Opts;
+ SshOpts ->
+ case proplists:get_value(rsa_pass_phrase, SshOpts, false) of
+ false ->
+ Opts;
+ _Password ->
+ NewOpts = [{rsa_pass_phrase, undefined}|lists:keydelete(rsa_pass_phrase, 1, SshOpts)],
+ [{ssh_opts, NewOpts}|lists:keydelete(ssh_opts, 1, Opts)]
+ end
+ end.
%%--------------------------------------------------------------------
%% Function: terminate(Reason, State) -> void()
%% Description: This function is called by a gen_server when it is about to
@@ -567,6 +656,45 @@ code_change(_OldVsn, State, _Extra) ->
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
+get_idle_time(SshOptions) ->
+ case proplists:get_value(idle_time, SshOptions) of
+ infinity ->
+ infinity;
+ _IdleTime -> %% We dont want to set the timeout on first connect
+ undefined
+ end.
+check_cache(State, Cache) ->
+ %% Check the number of entries in Cache
+ case proplists:get_value(size, ets:info(Cache)) of
+ 0 ->
+ Opts = proplists:get_value(ssh_opts, State#state.opts),
+ case proplists:get_value(idle_time, Opts) of
+ infinity ->
+ State;
+ undefined ->
+ State;
+ Time ->
+ case State#state.idle_timer_ref of
+ undefined ->
+ TimerRef = erlang:send_after(Time, self(), {'EXIT', [], "Timeout"}),
+ State#state{idle_timer_ref=TimerRef};
+ _ ->
+ State
+ end
+ end;
+ _ ->
+ State
+ end.
+remove_timer_ref(State) ->
+ case State#state.idle_timer_ref of
+ infinity -> %% If the timer is not activated
+ State;
+ undefined -> %% If we already has cancelled the timer
+ State;
+ TimerRef -> %% Timer is active
+ erlang:cancel_timer(TimerRef),
+ State#state{idle_timer_ref = undefined}
+ end.
channel_data(Id, Type, Data, Connection0, ConnectionPid, From, State) ->
case ssh_connection:channel_data(Id, Type, Data, Connection0,
ConnectionPid, From) of
@@ -614,6 +742,8 @@ do_send_msg({connection_reply, Pid, Data}) ->
ssh_connection_handler:send(Pid, Msg);
do_send_msg({flow_control, Cache, Channel, From, Msg}) ->
ssh_channel:cache_update(Cache, Channel#channel{flow_control = undefined}),
+ gen_server:reply(From, Msg);
+do_send_msg({flow_control, From, Msg}) ->
gen_server:reply(From, Msg).
handle_request(ChannelPid, ChannelId, Type, Data, WantReply, From,
@@ -662,7 +792,7 @@ handle_channel_down(ChannelPid, #state{connection_state =
(_,Acc) ->
Acc
end, [], Cache),
- {{replies, []}, State}.
+ {{replies, []}, check_cache(State, Cache)}.
update_sys(Cache, Channel, Type, ChannelPid) ->
ssh_channel:cache_update(Cache,
diff --git a/lib/ssh/src/ssh_connection_sup.erl b/lib/ssh/src/ssh_connection_sup.erl
index e3544af1c6..b620056310 100644
--- a/lib/ssh/src/ssh_connection_sup.erl
+++ b/lib/ssh/src/ssh_connection_sup.erl
@@ -48,8 +48,12 @@ start_manager_child(Sup, Args) ->
supervisor:start_child(Sup, Spec).
connection_manager(SupPid) ->
- Children = supervisor:which_children(SupPid),
- {ok, ssh_connection_manager(Children)}.
+ try supervisor:which_children(SupPid) of
+ Children ->
+ {ok, ssh_connection_manager(Children)}
+ catch exit:{noproc,_} ->
+ {ok, undefined}
+ end.
%%%=========================================================================
%%% Supervisor callback
@@ -107,6 +111,8 @@ handler_spec([Role, Socket, Opts]) ->
Type = worker,
{Name, StartFunc, Restart, Shutdown, Type, Modules}.
+ssh_connection_manager([]) ->
+ undefined;
ssh_connection_manager([{_, Child, _, [ssh_connection_manager]} | _]) ->
Child;
ssh_connection_manager([_ | Rest]) ->
diff --git a/lib/ssh/src/ssh_file.erl b/lib/ssh/src/ssh_file.erl
index d05fa8e09a..f115a32710 100644
--- a/lib/ssh/src/ssh_file.erl
+++ b/lib/ssh/src/ssh_file.erl
@@ -23,7 +23,8 @@
-module(ssh_file).
--behaviour(ssh_key_api).
+-behaviour(ssh_server_key_api).
+-behaviour(ssh_client_key_api).
-include_lib("public_key/include/public_key.hrl").
-include_lib("kernel/include/file.hrl").
@@ -34,7 +35,7 @@
user_key/2,
is_host_key/4,
add_host_key/3,
- is_auth_key/4]).
+ is_auth_key/3]).
-define(PERM_700, 8#700).
@@ -53,8 +54,8 @@ host_key(Algorithm, Opts) ->
decode(File, Password).
-is_auth_key(Key, User, Alg, Opts) ->
- case lookup_user_key(Key, User, Alg, Opts) of
+is_auth_key(Key, User,Opts) ->
+ case lookup_user_key(Key, User, Opts) of
{ok, Key} ->
true;
_ ->
@@ -138,13 +139,13 @@ add_host_key(Host, Key, Opts) ->
Error
end.
-lookup_user_key(Key, User, Alg, Opts) ->
+lookup_user_key(Key, User, Opts) ->
SshDir = ssh_dir({remoteuser,User}, Opts),
- case lookup_user_key_f(Key, User, SshDir, Alg, "authorized_keys", Opts) of
+ case lookup_user_key_f(Key, User, SshDir, "authorized_keys", Opts) of
{ok, Key} ->
{ok, Key};
_ ->
- lookup_user_key_f(Key, User, SshDir, Alg, "authorized_keys2", Opts)
+ lookup_user_key_f(Key, User, SshDir, "authorized_keys2", Opts)
end.
@@ -213,9 +214,9 @@ do_lookup_host_key(Host, Alg, Opts) ->
Error -> Error
end.
-identity_key_filename("ssh-dss") ->
+identity_key_filename('ssh-dss') ->
"id_dsa";
-identity_key_filename("ssh-rsa") ->
+identity_key_filename('ssh-rsa') ->
"id_rsa".
identity_pass_phrase("ssh-dss") ->
@@ -232,7 +233,7 @@ lookup_host_key_fd(Fd, Host, KeyType) ->
eof ->
{error, not_found};
Line ->
- case public_key:ssh_decode(Line, known_hosts) of
+ case ssh_decode_line(Line, known_hosts) of
[{Key, Attributes}] ->
handle_host(Fd, Host, proplists:get_value(hostnames, Attributes), Key, KeyType);
[] ->
@@ -240,6 +241,13 @@ lookup_host_key_fd(Fd, Host, KeyType) ->
end
end.
+ssh_decode_line(Line, Type) ->
+ try
+ public_key:ssh_decode(Line, Type)
+ catch _:_ ->
+ []
+ end.
+
handle_host(Fd, Host, HostList, Key, KeyType) ->
Host1 = host_name(Host),
case lists:member(Host1, HostList) and key_match(Key, KeyType) of
@@ -254,9 +262,9 @@ host_name(Atom) when is_atom(Atom) ->
host_name(List) ->
List.
-key_match(#'RSAPublicKey'{}, "ssh-rsa") ->
+key_match(#'RSAPublicKey'{}, 'ssh-rsa') ->
true;
-key_match({_, #'Dss-Parms'{}}, "ssh-dss") ->
+key_match({_, #'Dss-Parms'{}}, 'ssh-dss') ->
true;
key_match(_, _) ->
false.
@@ -265,11 +273,11 @@ add_key_fd(Fd, Host,Key) ->
SshBin = public_key:ssh_encode([{Key, [{hostnames, [Host]}]}], known_hosts),
file:write(Fd, SshBin).
-lookup_user_key_f(_, _User, [], _Alg, _F, _Opts) ->
+lookup_user_key_f(_, _User, [], _F, _Opts) ->
{error, nouserdir};
-lookup_user_key_f(_, _User, nouserdir, _Alg, _F, _Opts) ->
+lookup_user_key_f(_, _User, nouserdir, _F, _Opts) ->
{error, nouserdir};
-lookup_user_key_f(Key, _User, Dir, _Alg, F, _Opts) ->
+lookup_user_key_f(Key, _User, Dir, F, _Opts) ->
FileName = filename:join(Dir, F),
case file:open(FileName, [read, binary]) of
{ok, Fd} ->
@@ -285,7 +293,7 @@ lookup_user_key_fd(Fd, Key) ->
eof ->
{error, not_found};
Line ->
- case public_key:ssh_decode(Line, auth_keys) of
+ case ssh_decode_line(Line, auth_keys) of
[{AuthKey, _}] ->
case is_auth_key(Key, AuthKey) of
true ->
diff --git a/lib/ssh/src/ssh_io.erl b/lib/ssh/src/ssh_io.erl
index 1dbd097423..01fc713569 100644
--- a/lib/ssh/src/ssh_io.erl
+++ b/lib/ssh/src/ssh_io.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -23,37 +23,52 @@
-module(ssh_io).
--export([yes_no/1, read_password/1, read_line/1, format/2]).
+-export([yes_no/2, read_password/2, read_line/2, format/2]).
-import(lists, [reverse/1]).
+-include("ssh.hrl").
+read_line(Prompt, Ssh) ->
+ format("~s", [listify(Prompt)]),
+ proplists:get_value(user_pid, Ssh) ! {self(), question},
+ receive
+ Answer ->
+ Answer
+ end.
-read_line(Prompt) when is_list(Prompt) ->
- io:get_line(list_to_atom(Prompt));
-read_line(Prompt) when is_atom(Prompt) ->
- io:get_line(Prompt).
-
-read_ln(Prompt) ->
- trim(read_line(Prompt)).
-
-yes_no(Prompt) ->
+yes_no(Prompt, Ssh) ->
io:format("~s [y/n]?", [Prompt]),
- case read_ln('') of
- "y" -> yes;
- "n" -> no;
- "Y" -> yes;
- "N" -> no;
- _ ->
- io:format("please answer y or n\n"),
- yes_no(Prompt)
+ proplists:get_value(user_pid, Ssh#ssh.opts) ! {self(), question},
+ receive
+ Answer ->
+ case trim(Answer) of
+ "y" -> yes;
+ "n" -> no;
+ "Y" -> yes;
+ "N" -> no;
+ y -> yes;
+ n -> no;
+ _ ->
+ io:format("please answer y or n\n"),
+ yes_no(Prompt, Ssh)
+ end
end.
-read_password(Prompt) ->
+read_password(Prompt, Ssh) ->
format("~s", [listify(Prompt)]),
- case io:get_password() of
- "" ->
- read_password(Prompt);
- Pass -> Pass
+ case is_list(Ssh) of
+ false ->
+ proplists:get_value(user_pid, Ssh#ssh.opts) ! {self(), user_password};
+ _ ->
+ proplists:get_value(user_pid, Ssh) ! {self(), user_password}
+ end,
+ receive
+ Answer ->
+ case Answer of
+ "" ->
+ read_password(Prompt, Ssh);
+ Pass -> Pass
+ end
end.
listify(A) when is_atom(A) ->
diff --git a/lib/ssh/src/ssh_key_api.erl b/lib/ssh/src/ssh_key_api.erl
deleted file mode 100644
index 8085c12e21..0000000000
--- a/lib/ssh/src/ssh_key_api.erl
+++ /dev/null
@@ -1,45 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2011-2012. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, 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(ssh_key_api).
-
--include_lib("public_key/include/public_key.hrl").
--include("ssh.hrl").
-
--type ssh_algorithm() :: string().
--type file_error() :: file:posix() | badarg | system_limit | terminated.
-
--callback host_key(Algorithm :: ssh_algorithm(), Options :: list()) ->
- {ok, [{public_key(), Attributes::list()}]} | public_key()
- | {error, string()}.
-
--callback user_key(Algorithm :: ssh_algorithm(), Options :: list()) ->
- {ok, [{public_key(), Attributes::list()}]} | public_key()
- | {error, string()}.
-
--callback is_host_key(Key :: public_key(), PeerName :: string(),
- Algorithm :: ssh_algorithm(), Options :: list()) ->
- boolean().
-
--callback add_host_key(Host :: string(), Key :: public_key(), Options :: list()) ->
- ok | {error, file_error()}.
-
--callback is_auth_key(Key :: public_key(), User :: string(),
- Algorithm :: ssh_algorithm(), Options :: list()) ->
- boolean().
diff --git a/lib/test_server/test/test_server_line_SUITE_data/parse_transform_test.erl b/lib/ssh/src/ssh_server_key.erl
index 8f3477d3ac..8140114990 100644
--- a/lib/test_server/test/test_server_line_SUITE_data/parse_transform_test.erl
+++ b/lib/ssh/src/ssh_server_key.erl
@@ -1,59 +1,33 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2011-2012. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, 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(parse_transform_test).
-
--include("test_server_line.hrl").
--no_lines([{excluded,0}]).
-
--export([excluded/0, func/0]).
-
-excluded() ->
- line1,
- line2,
- ok.
+-module(ssh_server_key).
+-include_lib("public_key/include/public_key.hrl").
+-include("ssh.hrl").
-func() ->
- hello,
- func1(),
- case func2() of
- ok ->
- helloagain,
- case func3() of
- ok ->
- ok;
- error ->
- error
- end;
- error ->
- error
- end,
- excluded(),
- func4().
+-type ssh_algorithm() :: string().
-func1() ->
- ok.
-func2() ->
- ok.
-func3() ->
- error.
-func4() ->
- ok.
+-callback host_key(Algorithm :: ssh_algorithm(), Options :: list()) ->
+ {ok, [{public_key(), Attributes::list()}]} | public_key()
+ | {error, string()}.
+-callback is_auth_key(Key :: public_key(), User :: string(),
+ Algorithm :: ssh_algorithm(), Options :: list()) ->
+ boolean().
diff --git a/lib/ssh/src/ssh_server_key_api.erl b/lib/ssh/src/ssh_server_key_api.erl
new file mode 100644
index 0000000000..4fd660ecb5
--- /dev/null
+++ b/lib/ssh/src/ssh_server_key_api.erl
@@ -0,0 +1,30 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2011-2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, 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(ssh_server_key_api).
+
+-include_lib("public_key/include/public_key.hrl").
+-include("ssh.hrl").
+
+-callback host_key(Algorithm :: 'ssh-rsa'| 'ssh-dss'| atom(), DaemonOptions :: proplists:proplist()) ->
+ {ok, PrivateKey :: #'RSAPrivateKey'{}| #'DSAPrivateKey'{} | term()} | {error, string()}.
+
+-callback is_auth_key(PublicKey :: #'RSAPublicKey'{}| {integer(), #'Dss-Parms'{}}| term(),
+ User :: string(), DaemonOptions :: proplists:proplist()) ->
+ boolean().
diff --git a/lib/ssh/src/ssh_sftpd.erl b/lib/ssh/src/ssh_sftpd.erl
index ec7b76b0b3..c7e8373840 100644
--- a/lib/ssh/src/ssh_sftpd.erl
+++ b/lib/ssh/src/ssh_sftpd.erl
@@ -24,7 +24,7 @@
-module(ssh_sftpd).
%%-behaviour(gen_server).
--behaviour(ssh_channel).
+-behaviour(ssh_subsystem).
-include_lib("kernel/include/file.hrl").
@@ -36,7 +36,7 @@
-export([subsystem_spec/1,
listen/1, listen/2, listen/3, stop/1]).
--export([init/1, handle_ssh_msg/2, handle_msg/2, terminate/2, code_change/3]).
+-export([init/1, handle_ssh_msg/2, handle_msg/2, terminate/2]).
-record(state, {
xf, % [{channel,ssh_xfer states}...]
@@ -119,23 +119,13 @@ init(Options) ->
{Root0, State0}
end,
MaxLength = proplists:get_value(max_files, Options, 0),
-
- Vsn = proplists:get_value(vsn, Options, 5),
-
+ Vsn = proplists:get_value(sftpd_vsn, Options, 5),
{ok, State#state{cwd = CWD, root = Root, max_files = MaxLength,
handles = [], pending = <<>>,
xf = #ssh_xfer{vsn = Vsn, ext = []}}}.
%%--------------------------------------------------------------------
-%% Function: code_change(OldVsn, State, Extra) -> {ok, NewState}
-%% Description:
-%%--------------------------------------------------------------------
-code_change(_OldVsn, State, _Extra) ->
- {ok, State}.
-
-
-%%--------------------------------------------------------------------
%% Function: handle_ssh_msg(Args) -> {ok, State} | {stop, ChannelId, State}
%%
%% Description: Handles channel messages
@@ -369,17 +359,21 @@ handle_op(?SSH_FXP_FSETSTAT, ReqId, <<?UINT32(HLen), BinHandle:HLen/binary,
State0
end;
handle_op(?SSH_FXP_REMOVE, ReqId, <<?UINT32(PLen), BPath:PLen/binary>>,
- State0 = #state{file_handler = FileMod, file_state = FS0}) ->
+ State0 = #state{file_handler = FileMod, file_state = FS0, xf = #ssh_xfer{vsn = Vsn}}) ->
Path = relate_file_name(BPath, State0),
- %% case FileMod:is_dir(Path) of %% This version 6 we still have ver 5
- %% true ->
- %% ssh_xfer:xf_send_status(State#state.xf, ReqId,
- %% ?SSH_FX_FILE_IS_A_DIRECTORY);
- %% false ->
- {Status, FS1} = FileMod:delete(Path, FS0),
- State1 = State0#state{file_state = FS1},
- send_status(Status, ReqId, State1);
- %%end;
+ {IsDir, _FS1} = FileMod:is_dir(Path, FS0),
+ case IsDir of %% This version 6 we still have ver 5
+ true when Vsn > 5 ->
+ ssh_xfer:xf_send_status(State0#state.xf, ReqId,
+ ?SSH_FX_FILE_IS_A_DIRECTORY, "File is a directory");
+ true ->
+ ssh_xfer:xf_send_status(State0#state.xf, ReqId,
+ ?SSH_FX_FAILURE, "File is a directory");
+ false ->
+ {Status, FS1} = FileMod:delete(Path, FS0),
+ State1 = State0#state{file_state = FS1},
+ send_status(Status, ReqId, State1)
+ end;
handle_op(?SSH_FXP_RMDIR, ReqId, <<?UINT32(PLen), BPath:PLen/binary>>,
State0 = #state{file_handler = FileMod, file_state = FS0}) ->
Path = relate_file_name(BPath, State0),
@@ -637,31 +631,34 @@ open(Vsn, ReqId, Data, State) when Vsn >= 4 ->
do_open(ReqId, State, Path, Flags).
do_open(ReqId, State0, Path, Flags) ->
- #state{file_handler = FileMod, file_state = FS0, root = Root} = State0,
+ #state{file_handler = FileMod, file_state = FS0, root = Root, xf = #ssh_xfer{vsn = Vsn}} = State0,
XF = State0#state.xf,
F = [binary | Flags],
- %% case FileMod:is_dir(Path) of %% This is version 6 we still have 5
- %% true ->
- %% ssh_xfer:xf_send_status(State#state.xf, ReqId,
- %% ?SSH_FX_FILE_IS_A_DIRECTORY);
- %% false ->
-
- AbsPath = case Root of
- "" ->
- Path;
- _ ->
- relate_file_name(Path, State0)
- end,
-
- {Res, FS1} = FileMod:open(AbsPath, F, FS0),
- State1 = State0#state{file_state = FS1},
- case Res of
- {ok, IoDevice} ->
- add_handle(State1, XF, ReqId, file, {Path,IoDevice});
- {error, Error} ->
- ssh_xfer:xf_send_status(State1#state.xf, ReqId,
- ssh_xfer:encode_erlang_status(Error)),
- State1
+ {IsDir, _FS1} = FileMod:is_dir(Path, FS0),
+ case IsDir of
+ true when Vsn > 5 ->
+ ssh_xfer:xf_send_status(State0#state.xf, ReqId,
+ ?SSH_FX_FILE_IS_A_DIRECTORY, "File is a directory");
+ true ->
+ ssh_xfer:xf_send_status(State0#state.xf, ReqId,
+ ?SSH_FX_FAILURE, "File is a directory");
+ false ->
+ AbsPath = case Root of
+ "" ->
+ Path;
+ _ ->
+ relate_file_name(Path, State0)
+ end,
+ {Res, FS1} = FileMod:open(AbsPath, F, FS0),
+ State1 = State0#state{file_state = FS1},
+ case Res of
+ {ok, IoDevice} ->
+ add_handle(State1, XF, ReqId, file, {Path,IoDevice});
+ {error, Error} ->
+ ssh_xfer:xf_send_status(State1#state.xf, ReqId,
+ ssh_xfer:encode_erlang_status(Error)),
+ State1
+ end
end.
%% resolve all symlinks in a path
diff --git a/lib/ssh/src/ssh_sftpd_file_api.erl b/lib/ssh/src/ssh_sftpd_file_api.erl
index 38371f809d..83d90907f5 100644
--- a/lib/ssh/src/ssh_sftpd_file_api.erl
+++ b/lib/ssh/src/ssh_sftpd_file_api.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -22,40 +22,40 @@
-module(ssh_sftpd_file_api).
%% To be further specified later
--callback close(IoDevice::term(), State::term()) ->
- ok | {error, Reason::term()}.
--callback delete(Path::term(), State::term()) ->
- ok | {error, Reason::term()}.
--callback del_dir(Path::term(), State::term()) ->
- ok | {error, Reason::term()}.
+-callback close(file:io_device(), State::term()) ->
+ {ok, State::term()} | {{error, Reason::term()}, State::term()}.
+-callback delete(file:name(), State::term()) ->
+ {ok, State::term()} | {{error, Reason::term()}, State::term()}.
+-callback del_dir(file:name(), State::term()) ->
+ {ok, State::term()} | {{error, Reason::term()}, State::term()}.
-callback get_cwd(State::term()) ->
- {ok, Dir::term()} | {error, Reason::term()}.
--callback is_dir(AbsPath::term(), State::term()) ->
- boolean().
--callback list_dir(AbsPath::term(), State::term()) ->
- {ok, Filenames::term()} | {error, Reason::term()}.
+ {{ok, Dir::term()}, State::term()} | {{error, Reason::term()}, State::term()}.
+-callback is_dir(file:name(), State::term()) ->
+ {boolean(), State::term()}.
+-callback list_dir(file:name(), State::term()) ->
+ {{ok, Filenames::term()}, State::term()} | {{error, Reason::term()}, State::term()}.
-callback make_dir(Dir::term(), State::term()) ->
- ok | {error, Reason::term()}.
+ {{ok, State::term()},State::term()} | {{error, Reason::term()}, State::term()}.
-callback make_symlink(Path2::term(), Path::term(), State::term()) ->
- ok | {error, Reason::term()}.
+ {ok, State::term()} | {{error, Reason::term()}, State::term()}.
-callback open(Path::term(), Flags::term(), State::term()) ->
- {ok, IoDevice::term()} | {error, Reason::term()}.
--callback position(IoDevice::term(), Offs::term(), State::term()) ->
- {ok, NewPosition::term()} | {error, Reason::term()}.
--callback read(IoDevice::term(), Len::term(), State::term()) ->
- {ok, Data::term()} | eof | {error, Reason::term()}.
--callback read_link(Path::term(), State::term()) ->
- {ok, FileName::term()} | {error, Reason::term()}.
--callback read_link_info(Path::term(), State::term()) ->
- {ok, FileInfo::term()} | {error, Reason::term()}.
--callback read_file_info(Path::term(), State::term()) ->
- {ok, FileInfo::term()} | {error, Reason::term()}.
--callback rename(Path::term(), Path2::term(), State::term()) ->
- ok | {error, Reason::term()}.
--callback write(IoDevice::term(), Data::term(), State::term()) ->
- ok | {error, Reason::term()}.
--callback write_file_info(Path::term(),Info::term(), State::term()) ->
- ok | {error, Reason::term()}.
+ {{ok, IoDevice::term()}, State::term()} | {{error, Reason::term()}, State::term()}.
+-callback position(file:io_device(), Offs::term(), State::term()) ->
+ {{ok, NewPosition::term()}, State::term()} | {{error, Reason::term()}, State::term()}.
+-callback read(file:io_device(), Len::term(), State::term()) ->
+ {{ok, Data::term()},State::term()} | {eof, State::term()} | {{error, Reason::term()}, State::term()}.
+-callback read_link(file:name(), State::term()) ->
+ {{ok, FileName::term()}, State::term()} | {{error, Reason::term()}, State::term()}.
+-callback read_link_info(file:name(), State::term()) ->
+ {{ok, FileInfo::term()}, State::term()} | {{error, Reason::term()}, State::term()}.
+-callback read_file_info(file:name(), State::term()) ->
+ {{ok, FileInfo::term()}, State::term()} | {{error, Reason::term()},State::term()}.
+-callback rename(file:name(), file:name(), State::term()) ->
+ {ok, State::term()} | {{error, Reason::term()}, State::term()}.
+-callback write(file:io_device(), Data::term(), State::term()) ->
+ {ok, State::term()} | {{error, Reason::term()}, State::term()}.
+-callback write_file_info(file:name(),Info::term(), State::term()) ->
+ {ok, State::term()} | {{error, Reason::term()}, State::term()}.
diff --git a/lib/ssh/src/ssh_subsystem.erl b/lib/ssh/src/ssh_subsystem.erl
new file mode 100644
index 0000000000..5a9fa32668
--- /dev/null
+++ b/lib/ssh/src/ssh_subsystem.erl
@@ -0,0 +1,47 @@
+-module(ssh_subsystem).
+
+%% API to special server side channel that can be pluged into the erlang ssh daemeon
+-callback init(Args :: term()) ->
+ {ok, State :: term()} | {ok, State :: term(), timeout() | hibernate} |
+ {stop, Reason :: term()} | ignore.
+
+-callback terminate(Reason :: (normal | shutdown | {shutdown, term()} |
+ term()),
+ State :: term()) ->
+ term().
+
+-callback handle_msg(Msg ::term(), State :: term()) ->
+ {noreply, NewState :: term()} |
+ {noreply, NewState :: term(), timeout() | hibernate} |
+ {stop, Reason :: term(), NewState :: term()}.
+
+-callback handle_ssh_msg({ssh_cm, ConnectionRef::term(), SshMsg::term()},
+ State::term()) -> {ok, State::term()} |
+ {stop, ChannelId::integer(),
+ State::term()}.
+
+%%% API
+-export([start/4, start/5, start_link/4, start_link/5, enter_loop/1]).
+
+%% gen_server callbacks
+-export([init/1, terminate/2]).
+
+start(ConnectionManager, ChannelId, CallBack, CbInitArgs) ->
+ ssh_channel:start(ConnectionManager, ChannelId, CallBack, CbInitArgs, undefined).
+
+start(ConnectionManager, ChannelId, CallBack, CbInitArgs, Exec) ->
+ ssh_channel:start(ConnectionManager, ChannelId, CallBack, CbInitArgs, Exec).
+
+start_link(ConnectionManager, ChannelId, CallBack, CbInitArgs) ->
+ ssh_channel:start_link(ConnectionManager, ChannelId, CallBack, CbInitArgs, undefined).
+
+start_link(ConnectionManager, ChannelId, CallBack, CbInitArgs, Exec) ->
+ ssh_channel:start_link(ConnectionManager, ChannelId, CallBack, CbInitArgs, Exec).
+
+enter_loop(State) ->
+ ssh_channel:enter_loop(State).
+
+init(Args) ->
+ ssh_channel:init(Args).
+terminate(Reason, State) ->
+ ssh_channel:terminate(Reason, State).
diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl
index 1f912c9bdf..1abb69921d 100644
--- a/lib/ssh/src/ssh_transport.erl
+++ b/lib/ssh/src/ssh_transport.erl
@@ -133,7 +133,7 @@ kex_dh_gex_messages() ->
].
yes_no(Ssh, Prompt) ->
- (Ssh#ssh.io_cb):yes_no(Prompt).
+ (Ssh#ssh.io_cb):yes_no(Prompt, Ssh).
connect(ConnectionSup, Address, Port, SocketOpts, Opts) ->
Timeout = proplists:get_value(connect_timeout, Opts, infinity),
@@ -449,7 +449,7 @@ verify_host_key_rsa(SSH, K_S, H, H_SIG) ->
false ->
{error, bad_signature};
true ->
- known_host_key(SSH, Public, "ssh-rsa")
+ known_host_key(SSH, Public, 'ssh-rsa')
end;
_ ->
{error, bad_format}
@@ -464,7 +464,7 @@ verify_host_key_dss(SSH, K_S, H, H_SIG) ->
false ->
{error, bad_signature};
true ->
- known_host_key(SSH, Public, "ssh-dss")
+ known_host_key(SSH, Public, 'ssh-dss')
end;
_ ->
{error, bad_host_key_format}
diff --git a/lib/ssh/src/ssh_xfer.erl b/lib/ssh/src/ssh_xfer.erl
index d5b6dd03d1..4dfd9ed8b0 100644
--- a/lib/ssh/src/ssh_xfer.erl
+++ b/lib/ssh/src/ssh_xfer.erl
@@ -383,6 +383,8 @@ decode_status(Status) ->
?SSH_FX_UNKNOWN_PRINCIPLE -> unknown_principle;
?SSH_FX_LOCK_CONFlICT -> lock_conflict;
?SSH_FX_NOT_A_DIRECTORY -> not_a_directory;
+ ?SSH_FX_FILE_IS_A_DIRECTORY -> file_is_a_directory;
+ ?SSH_FX_CANNOT_DELETE -> cannot_delete;
_ -> {error,Status}
end.
@@ -392,6 +394,9 @@ encode_erlang_status(Status) ->
eof -> ?SSH_FX_EOF;
enoent -> ?SSH_FX_NO_SUCH_FILE;
eacces -> ?SSH_FX_PERMISSION_DENIED;
+ eisdir -> ?SSH_FX_FILE_IS_A_DIRECTORY;
+ eperm -> ?SSH_FX_CANNOT_DELETE;
+ eexist -> ?SSH_FX_FILE_ALREADY_EXISTS;
_ -> ?SSH_FX_FAILURE
end.
diff --git a/lib/ssh/src/ssh_xfer.hrl b/lib/ssh/src/ssh_xfer.hrl
index c13950eb6e..0d85cf2094 100644
--- a/lib/ssh/src/ssh_xfer.hrl
+++ b/lib/ssh/src/ssh_xfer.hrl
@@ -58,7 +58,6 @@
%%% # SSH_FX_xxx
%%% Description: Response packet types for file transfer protocol.
%%%----------------------------------------------------------------------
-
-define(SSH_FX_OK, 0).
-define(SSH_FX_EOF, 1).
-define(SSH_FX_NO_SUCH_FILE, 2).
@@ -79,7 +78,18 @@
-define(SSH_FX_LOCK_CONFlICT, 17).
-define(SSH_FX_DIR_NOT_EMPTY, 18).
-define(SSH_FX_NOT_A_DIRECTORY, 19).
+-define(SSH_FX_INVALID_FILENAME, 20).
+-define(SSH_FX_LINK_LOOP, 21).
+-define(SSH_FX_CANNOT_DELETE, 22).
+-define(SSH_FX_INVALID_PARAMETER, 23).
-define(SSH_FX_FILE_IS_A_DIRECTORY, 24).
+-define(SSH_FX_BYTE_RANGE_LOCK_CONFLICT,25).
+-define(SSH_FX_BYTE_RANGE_LOCK_REFUSED, 26).
+-define(SSH_FX_DELETE_PENDING, 27).
+-define(SSH_FX_FILE_CORRUPT, 28).
+-define(SSH_FX_OWNER_INVALID, 29).
+-define(SSH_FX_GROUP_INVALID, 30).
+-define(SSH_FX_NO_MATCHING_BYTE_RANGE_LOCK,31).
%%%----------------------------------------------------------------------
%%% # SSH_FILEXFER_xxx
diff --git a/lib/ssh/test/Makefile b/lib/ssh/test/Makefile
index 25072688ad..f5db31baee 100644
--- a/lib/ssh/test/Makefile
+++ b/lib/ssh/test/Makefile
@@ -36,7 +36,9 @@ MODULES= \
ssh_to_openssh_SUITE \
ssh_sftp_SUITE \
ssh_sftpd_SUITE \
- ssh_sftpd_erlclient_SUITE
+ ssh_sftpd_erlclient_SUITE \
+ ssh_connection_SUITE \
+ ssh_echo_server
HRL_FILES_NEEDED_IN_TEST= \
$(ERL_TOP)/lib/ssh/src/ssh.hrl \
diff --git a/lib/ssh/test/ssh.spec.vxworks b/lib/ssh/test/ssh.spec.vxworks
deleted file mode 100644
index 81f665283c..0000000000
--- a/lib/ssh/test/ssh.spec.vxworks
+++ /dev/null
@@ -1,3 +0,0 @@
-{topcase, {dir, "../ssh_test"}}.
-{require_nodenames, 1}.
-%{skip, {M, F, "Not yet implemented"}}.
diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl
index c5019425cd..efcb11f88f 100644
--- a/lib/ssh/test/ssh_basic_SUITE.erl
+++ b/lib/ssh/test/ssh_basic_SUITE.erl
@@ -22,7 +22,6 @@
-module(ssh_basic_SUITE).
-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).
@@ -30,78 +29,12 @@
-define(NEWLINE, <<"\r\n">>).
%%--------------------------------------------------------------------
-%% 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.
+%% Common Test interface functions -----------------------------------
%%--------------------------------------------------------------------
-init_per_suite(Config) ->
- case catch crypto:start() of
- ok ->
- Config;
- _Else ->
- {skip, "Crypto could not be started!"}
- 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) ->
- ssh:stop(),
- crypto:stop(),
- ok.
+suite() ->
+ [{ct_hooks,[ts_install_cth]}].
-%%--------------------------------------------------------------------
-%% 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, Config) ->
- ssh:start(),
- 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) when TestCase == server_password_option;
- TestCase == server_userpassword_option ->
- UserDir = filename:join(?config(priv_dir, Config), nopubkey),
- ssh_test_lib:del_dirs(UserDir),
- end_per_testcase(Config);
-end_per_testcase(_TestCase, Config) ->
- end_per_testcase(Config).
-end_per_testcase(_Config) ->
- ssh:stop(),
- ok.
-
-%%--------------------------------------------------------------------
-%% 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
-%%--------------------------------------------------------------------
all() ->
[app_test,
{group, dsa_key},
@@ -110,17 +43,29 @@ all() ->
{group, rsa_pass_key},
{group, internal_error},
daemon_already_started,
- server_password_option, server_userpassword_option,
+ server_password_option,
+ server_userpassword_option,
close].
groups() ->
- [{dsa_key, [], [exec, exec_compressed, shell, known_hosts]},
- {rsa_key, [], [exec, exec_compressed, shell, known_hosts]},
+ [{dsa_key, [], [send, exec, exec_compressed, shell, known_hosts, idle_time, rekey]},
+ {rsa_key, [], [send, exec, exec_compressed, shell, known_hosts, idle_time, rekey]},
{dsa_pass_key, [], [pass_phrase]},
{rsa_pass_key, [], [pass_phrase]},
{internal_error, [], [internal_error]}
].
-
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ case catch crypto:start() of
+ ok ->
+ Config;
+ _Else ->
+ {skip, "Crypto could not be started!"}
+ end.
+end_per_suite(_Config) ->
+ ssh:stop(),
+ crypto:stop().
+%%--------------------------------------------------------------------
init_per_group(dsa_key, Config) ->
DataDir = ?config(data_dir, Config),
PrivDir = ?config(priv_dir, Config),
@@ -173,11 +118,25 @@ end_per_group(internal_error, Config) ->
end_per_group(_, Config) ->
Config.
+%%--------------------------------------------------------------------
+init_per_testcase(_TestCase, Config) ->
+ ssh:start(),
+ Config.
+
+end_per_testcase(TestCase, Config) when TestCase == server_password_option;
+ TestCase == server_userpassword_option ->
+ UserDir = filename:join(?config(priv_dir, Config), nopubkey),
+ ssh_test_lib:del_dirs(UserDir),
+ end_per_testcase(Config);
+end_per_testcase(_TestCase, Config) ->
+ end_per_testcase(Config).
+end_per_testcase(_Config) ->
+ ssh:stop(),
+ ok.
-%% Test cases starts here.
%%--------------------------------------------------------------------
-app_test(suite) ->
- [];
+%% Test Cases --------------------------------------------------------
+%%--------------------------------------------------------------------
app_test(doc) ->
["Application consistency test."];
app_test(Config) when is_list(Config) ->
@@ -188,16 +147,14 @@ misc_ssh_options(doc) ->
["Test that we can set some misc options not tested elsewhere, "
"some options not yet present are not decided if we should support or "
"if they need thier own test case."];
-misc_ssh_options(suite) ->
- [];
misc_ssh_options(Config) when is_list(Config) ->
SystemDir = filename:join(?config(priv_dir, Config), system),
UserDir = ?config(priv_dir, Config),
- CMiscOpt0 = [{connecect_timeout, 1000}, {ip_v6_disable, false}, {user_dir, UserDir}],
- CMiscOpt1 = [{connecect_timeout, infinity}, {ip_v6_disable, true}, {user_dir, UserDir}],
- SMiscOpt0 = [{ip_v6_disable, false}, {user_dir, UserDir}, {system_dir, SystemDir}],
- SMiscOpt1 = [{ip_v6_disable, true}, {user_dir, UserDir}, {system_dir, SystemDir}],
+ CMiscOpt0 = [{connecect_timeout, 1000}, {ip_v6_disabled, false}, {user_dir, UserDir}],
+ CMiscOpt1 = [{connecect_timeout, infinity}, {ip_v6_disabled, true}, {user_dir, UserDir}],
+ SMiscOpt0 = [{ip_v6_disabled, false}, {user_dir, UserDir}, {system_dir, SystemDir}],
+ SMiscOpt1 = [{ip_v6_disabled, true}, {user_dir, UserDir}, {system_dir, SystemDir}],
ClientOpts = ?config(client_opts, Config),
ServerOpts = ?config(server_opts, Config),
@@ -208,10 +165,6 @@ misc_ssh_options(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
exec(doc) ->
["Test api function ssh_connection:exec"];
-
-exec(suite) ->
- [];
-
exec(Config) when is_list(Config) ->
process_flag(trap_exit, true),
SystemDir = filename:join(?config(priv_dir, Config), system),
@@ -232,7 +185,7 @@ exec(Config) when is_list(Config) ->
expected ->
ok;
Other0 ->
- test_server:fail(Other0)
+ ct:fail(Other0)
end,
ssh_test_lib:receive_exec_end(ConnectionRef, ChannelId0),
@@ -246,7 +199,7 @@ exec(Config) when is_list(Config) ->
expected ->
ok;
Other1 ->
- test_server:fail(Other1)
+ ct:fail(Other1)
end,
ssh_test_lib:receive_exec_end(ConnectionRef, ChannelId1),
ssh:stop_daemon(Pid).
@@ -254,10 +207,6 @@ exec(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
exec_compressed(doc) ->
["Test that compression option works"];
-
-exec_compressed(suite) ->
- [];
-
exec_compressed(Config) when is_list(Config) ->
process_flag(trap_exit, true),
SystemDir = filename:join(?config(priv_dir, Config), system),
@@ -279,19 +228,58 @@ exec_compressed(Config) when is_list(Config) ->
expected ->
ok;
Other ->
- test_server:fail(Other)
+ ct:fail(Other)
end,
ssh_test_lib:receive_exec_end(ConnectionRef, ChannelId),
ssh:stop_daemon(Pid).
%%--------------------------------------------------------------------
+idle_time(doc) ->
+ ["Idle timeout test"];
+idle_time(Config) ->
+ SystemDir = filename:join(?config(priv_dir, Config), system),
+ UserDir = ?config(priv_dir, Config),
+
+ {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
+ {user_dir, UserDir},
+ {failfun, fun ssh_test_lib:failfun/2}]),
+ ConnectionRef =
+ ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
+ {user_dir, UserDir},
+ {user_interaction, false},
+ {idle_time, 2000}]),
+ {ok, Id} = ssh_connection:session_channel(ConnectionRef, 1000),
+ ssh_connection:close(ConnectionRef, Id),
+ receive
+ after 10000 ->
+ {error,channel_closed} = ssh_connection:session_channel(ConnectionRef, 1000)
+ end,
+ ssh:stop_daemon(Pid).
+%%--------------------------------------------------------------------
+rekey(doc) ->
+ ["Idle timeout test"];
+rekey(Config) ->
+ SystemDir = filename:join(?config(priv_dir, Config), system),
+ UserDir = ?config(priv_dir, Config),
+ {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
+ {user_dir, UserDir},
+ {failfun, fun ssh_test_lib:failfun/2},
+ {rekey_limit, 0}]),
+ ConnectionRef =
+ ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
+ {user_dir, UserDir},
+ {user_interaction, false},
+ {rekey_limit, 0}]),
+ receive
+ after 15000 ->
+ %%By this time rekeying would have been done
+ ssh:close(ConnectionRef),
+ ssh:stop_daemon(Pid)
+ end.
+%%--------------------------------------------------------------------
shell(doc) ->
["Test that ssh:shell/2 works"];
-
-shell(suite) ->
- [];
-
shell(Config) when is_list(Config) ->
process_flag(trap_exit, true),
SystemDir = filename:join(?config(priv_dir, Config), system),
@@ -299,76 +287,22 @@ shell(Config) when is_list(Config) ->
{_Pid, _Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},{user_dir, UserDir},
{failfun, fun ssh_test_lib:failfun/2}]),
- test_server:sleep(500),
+ ct:sleep(500),
IO = ssh_test_lib:start_io_server(),
Shell = ssh_test_lib:start_shell(Port, IO, UserDir),
receive
{'EXIT', _, _} ->
- test_server:fail(no_ssh_connection);
+ ct:fail(no_ssh_connection);
ErlShellStart ->
- test_server:format("Erlang shell start: ~p~n", [ErlShellStart]),
+ ct:pal("Erlang shell start: ~p~n", [ErlShellStart]),
do_shell(IO, Shell)
end.
-do_shell(IO, Shell) ->
- receive
- ErlPrompt0 ->
- test_server:format("Erlang prompt: ~p~n", [ErlPrompt0])
- end,
- IO ! {input, self(), "1+1.\r\n"},
- receive
- Echo0 ->
- test_server:format("Echo: ~p ~n", [Echo0])
- end,
- receive
- ?NEWLINE ->
- ok
- end,
- receive
- Result0 = <<"2">> ->
- test_server:format("Result: ~p~n", [Result0])
- end,
- receive
- ?NEWLINE ->
- ok
- end,
- receive
- ErlPrompt1 ->
- test_server:format("Erlang prompt: ~p~n", [ErlPrompt1])
- end,
- exit(Shell, kill),
- %% Does not seem to work in the testserver!
- %% IO ! {input, self(), "q().\r\n"},
- %% receive
- %% ?NEWLINE ->
- %% ok
- %% end,
- %% receive
- %% Echo1 ->
- %% test_server:format("Echo: ~p ~n", [Echo1])
- %% end,
- %% receive
- %% ?NEWLINE ->
- %% ok
- %% end,
- %% receive
- %% Result1 ->
- %% test_server:format("Result: ~p~n", [Result1])
- %% end,
- receive
- {'EXIT', Shell, killed} ->
- ok
- end.
-
%%--------------------------------------------------------------------
daemon_already_started(doc) ->
["Test that get correct error message if you try to start a daemon",
"on an adress that already runs a daemon see also seq10667" ];
-
-daemon_already_started(suite) ->
- [];
-
daemon_already_started(Config) when is_list(Config) ->
SystemDir = ?config(data_dir, Config),
UserDir = ?config(priv_dir, Config),
@@ -385,8 +319,6 @@ daemon_already_started(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
server_password_option(doc) ->
["validate to server that uses the 'password' option"];
-server_password_option(suite) ->
- [];
server_password_option(Config) when is_list(Config) ->
PrivDir = ?config(priv_dir, Config),
UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
@@ -412,7 +344,7 @@ server_password_option(Config) when is_list(Config) ->
{user_interaction, false},
{user_dir, UserDir}]),
- test_server:format("Test of wrong password: Error msg: ~p ~n", [Reason]),
+ ct:pal("Test of wrong password: Error msg: ~p ~n", [Reason]),
ssh:close(ConnectionRef),
ssh:stop_daemon(Pid).
@@ -421,8 +353,6 @@ server_password_option(Config) when is_list(Config) ->
server_userpassword_option(doc) ->
["validate to server that uses the 'password' option"];
-server_userpassword_option(suite) ->
- [];
server_userpassword_option(Config) when is_list(Config) ->
PrivDir = ?config(priv_dir, Config),
UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
@@ -459,8 +389,6 @@ server_userpassword_option(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
known_hosts(doc) ->
["check that known_hosts is updated correctly"];
-known_hosts(suite) ->
- [];
known_hosts(Config) when is_list(Config) ->
SystemDir = ?config(data_dir, Config),
PrivDir = ?config(priv_dir, Config),
@@ -488,10 +416,6 @@ known_hosts(Config) when is_list(Config) ->
pass_phrase(doc) ->
["Test that we can use keyes protected by pass phrases"];
-
-pass_phrase(suite) ->
- [];
-
pass_phrase(Config) when is_list(Config) ->
process_flag(trap_exit, true),
SystemDir = filename:join(?config(priv_dir, Config), system),
@@ -513,10 +437,6 @@ pass_phrase(Config) when is_list(Config) ->
internal_error(doc) ->
["Test that client does not hang if disconnects due to internal error"];
-
-internal_error(suite) ->
- [];
-
internal_error(Config) when is_list(Config) ->
process_flag(trap_exit, true),
SystemDir = filename:join(?config(priv_dir, Config), system),
@@ -532,12 +452,29 @@ internal_error(Config) when is_list(Config) ->
ssh:stop_daemon(Pid).
%%--------------------------------------------------------------------
-close(doc) ->
- ["Simulate that we try to close an already closed connection"];
+send(doc) ->
+ ["Test ssh_connection:send/3"];
+send(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ SystemDir = filename:join(?config(priv_dir, Config), system),
+ UserDir = ?config(priv_dir, Config),
+
+ {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
+ {user_dir, UserDir},
+ {failfun, fun ssh_test_lib:failfun/2}]),
+ ConnectionRef =
+ ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
+ {user_dir, UserDir},
+ {user_interaction, false}]),
+ {ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity),
+ ok = ssh_connection:send(ConnectionRef, ChannelId, <<"Data">>),
+ ok = ssh_connection:send(ConnectionRef, ChannelId, << >>),
+ ssh:stop_daemon(Pid).
-close(suite) ->
- [];
+%%--------------------------------------------------------------------
+close(doc) ->
+ ["Simulate that we try to close an already closed connection"];
close(Config) when is_list(Config) ->
SystemDir = ?config(data_dir, Config),
PrivDir = ?config(priv_dir, Config),
@@ -557,10 +494,8 @@ close(Config) when is_list(Config) ->
exit(CM, {shutdown, normal}),
ok = ssh:close(CM).
-
-
%%--------------------------------------------------------------------
-%% Internal functions
+%% Internal functions ------------------------------------------------
%%--------------------------------------------------------------------
basic_test(Config) ->
@@ -571,3 +506,53 @@ basic_test(Config) ->
{ok, CM} = ssh:connect(Host, Port, ClientOpts),
ok = ssh:close(CM),
ssh:stop_daemon(Pid).
+
+do_shell(IO, Shell) ->
+ receive
+ ErlPrompt0 ->
+ ct:pal("Erlang prompt: ~p~n", [ErlPrompt0])
+ end,
+ IO ! {input, self(), "1+1.\r\n"},
+ receive
+ Echo0 ->
+ ct:pal("Echo: ~p ~n", [Echo0])
+ end,
+ receive
+ ?NEWLINE ->
+ ok
+ end,
+ receive
+ Result0 = <<"2">> ->
+ ct:pal("Result: ~p~n", [Result0])
+ end,
+ receive
+ ?NEWLINE ->
+ ok
+ end,
+ receive
+ ErlPrompt1 ->
+ ct:pal("Erlang prompt: ~p~n", [ErlPrompt1])
+ end,
+ exit(Shell, kill).
+ %%Does not seem to work in the testserver!
+ %% IO ! {input, self(), "q().\r\n"},
+ %% receive
+ %% ?NEWLINE ->
+ %% ok
+ %% end,
+ %% receive
+ %% Echo1 ->
+ %% ct:pal("Echo: ~p ~n", [Echo1])
+ %% end,
+ %% receive
+ %% ?NEWLINE ->
+ %% ok
+ %% end,
+ %% receive
+ %% Result1 ->
+ %% ct:pal("Result: ~p~n", [Result1])
+ %% end,
+ %% receive
+ %% {'EXIT', Shell, killed} ->
+ %% ok
+ %% end.
diff --git a/lib/ssh/test/ssh_connection_SUITE.erl b/lib/ssh/test/ssh_connection_SUITE.erl
new file mode 100644
index 0000000000..acaf3d6eeb
--- /dev/null
+++ b/lib/ssh/test/ssh_connection_SUITE.erl
@@ -0,0 +1,313 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2008-2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, 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(ssh_connection_SUITE).
+
+-include_lib("common_test/include/ct.hrl").
+
+-compile(export_all).
+
+-define(SSH_DEFAULT_PORT, 22).
+-define(EXEC_TIMEOUT, 10000).
+
+%%--------------------------------------------------------------------
+%% Common Test interface functions -----------------------------------
+%%--------------------------------------------------------------------
+
+suite() ->
+ [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [
+ {group, openssh_payload},
+ interrupted_send
+ ].
+groups() ->
+ [{openssh_payload, [], [simple_exec,
+ small_cat,
+ big_cat,
+ send_after_exit
+ ]}].
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ case catch crypto:start() of
+ ok ->
+ Config;
+ _Else ->
+ {skip, "Crypto could not be started!"}
+ end.
+
+end_per_suite(_Config) ->
+ crypto:stop().
+
+%%--------------------------------------------------------------------
+init_per_group(openssh_payload, _Config) ->
+ case gen_tcp:connect("localhost", 22, []) of
+ {error,econnrefused} ->
+ {skip,"No openssh deamon"};
+ {ok, Socket} ->
+ gen_tcp:close(Socket)
+ end;
+init_per_group(_, Config) ->
+ Config.
+
+end_per_group(_, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+init_per_testcase(_TestCase, Config) ->
+ ssh:start(),
+ Config.
+
+end_per_testcase(_Config) ->
+ ssh:stop().
+
+%%--------------------------------------------------------------------
+%% Test Cases --------------------------------------------------------
+%%--------------------------------------------------------------------
+simple_exec(doc) ->
+ ["Simple openssh connectivity test for ssh_connection:exec"];
+
+simple_exec(Config) when is_list(Config) ->
+ ConnectionRef = ssh_test_lib:connect(?SSH_DEFAULT_PORT, [{silently_accept_hosts, true},
+ {user_interaction, false}]),
+ {ok, ChannelId0} = ssh_connection:session_channel(ConnectionRef, infinity),
+ success = ssh_connection:exec(ConnectionRef, ChannelId0,
+ "echo testing", infinity),
+
+ %% receive response to input
+ receive
+ {ssh_cm, ConnectionRef, {data, ChannelId0, 0, <<"testing\n">>}} ->
+ ok
+ end,
+
+ %% receive close messages
+ receive
+ {ssh_cm, ConnectionRef, {eof, ChannelId0}} ->
+ ok
+ end,
+ receive
+ {ssh_cm, ConnectionRef, {exit_status, ChannelId0, 0}} ->
+ ok
+ end,
+ receive
+ {ssh_cm, ConnectionRef,{closed, ChannelId0}} ->
+ ok
+ end.
+
+%%--------------------------------------------------------------------
+small_cat(doc) ->
+ ["Use 'cat' to echo small data block back to us."];
+
+small_cat(Config) when is_list(Config) ->
+ ConnectionRef = ssh_test_lib:connect(?SSH_DEFAULT_PORT, [{silently_accept_hosts, true},
+ {user_interaction, false}]),
+ {ok, ChannelId0} = ssh_connection:session_channel(ConnectionRef, infinity),
+ success = ssh_connection:exec(ConnectionRef, ChannelId0,
+ "cat", infinity),
+
+ Data = <<"I like spaghetti squash">>,
+ ok = ssh_connection:send(ConnectionRef, ChannelId0, Data),
+ ok = ssh_connection:send_eof(ConnectionRef, ChannelId0),
+
+ %% receive response to input
+ receive
+ {ssh_cm, ConnectionRef, {data, ChannelId0, 0, Data}} ->
+ ok
+ end,
+
+ %% receive close messages
+ receive
+ {ssh_cm, ConnectionRef, {eof, ChannelId0}} ->
+ ok
+ end,
+ receive
+ {ssh_cm, ConnectionRef, {exit_status, ChannelId0, 0}} ->
+ ok
+ end,
+ receive
+ {ssh_cm, ConnectionRef,{closed, ChannelId0}} ->
+ ok
+ end.
+
+%%--------------------------------------------------------------------
+big_cat(doc) ->
+ ["Use 'cat' to echo large data block back to us."];
+
+big_cat(Config) when is_list(Config) ->
+ ConnectionRef = ssh_test_lib:connect(?SSH_DEFAULT_PORT, [{silently_accept_hosts, true},
+ {user_interaction, false}]),
+ {ok, ChannelId0} = ssh_connection:session_channel(ConnectionRef, infinity),
+ success = ssh_connection:exec(ConnectionRef, ChannelId0,
+ "cat", infinity),
+
+ %% build 10MB binary
+ Data = << <<X:32>> || X <- lists:seq(1,2500000)>>,
+
+ %% pre-adjust receive window so the other end doesn't block
+ ssh_connection:adjust_window(ConnectionRef, ChannelId0, size(Data)),
+
+ ct:pal("sending ~p byte binary~n",[size(Data)]),
+ ok = ssh_connection:send(ConnectionRef, ChannelId0, Data, 10000),
+ ok = ssh_connection:send_eof(ConnectionRef, ChannelId0),
+
+ %% collect echoed data until eof
+ case big_cat_rx(ConnectionRef, ChannelId0) of
+ {ok, Data} ->
+ ok;
+ {ok, Other} ->
+ case size(Data) =:= size(Other) of
+ true ->
+ ct:pal("received and sent data are same"
+ "size but do not match~n",[]);
+ false ->
+ ct:pal("sent ~p but only received ~p~n",
+ [size(Data), size(Other)])
+ end,
+ ct:fail(receive_data_mismatch);
+ Else ->
+ ct:fail(Else)
+ end,
+
+ %% receive close messages (eof already consumed)
+ receive
+ {ssh_cm, ConnectionRef, {exit_status, ChannelId0, 0}} ->
+ ok
+ end,
+ receive
+ {ssh_cm, ConnectionRef,{closed, ChannelId0}} ->
+ ok
+ end.
+
+%%--------------------------------------------------------------------
+send_after_exit(doc) ->
+ ["Send channel data after the channel has been closed."];
+
+send_after_exit(Config) when is_list(Config) ->
+ ConnectionRef = ssh_test_lib:connect(?SSH_DEFAULT_PORT, [{silently_accept_hosts, true},
+ {user_interaction, false}]),
+ {ok, ChannelId0} = ssh_connection:session_channel(ConnectionRef, infinity),
+
+ %% Shell command "false" will exit immediately
+ success = ssh_connection:exec(ConnectionRef, ChannelId0,
+ "false", infinity),
+
+ timer:sleep(2000), %% Allow incoming eof/close/exit_status ssh messages to be processed
+
+ Data = <<"I like spaghetti squash">>,
+ case ssh_connection:send(ConnectionRef, ChannelId0, Data, 2000) of
+ {error, closed} -> ok;
+ ok ->
+ ct:fail({expected,{error,closed}});
+ {error, timeout} ->
+ ct:fail({expected,{error,closed}});
+ Else ->
+ ct:fail(Else)
+ end,
+
+ %% receive close messages
+ receive
+ {ssh_cm, ConnectionRef, {eof, ChannelId0}} ->
+ ok
+ end,
+ receive
+ {ssh_cm, ConnectionRef, {exit_status, ChannelId0, _}} ->
+ ok
+ end,
+ receive
+ {ssh_cm, ConnectionRef,{closed, ChannelId0}} ->
+ ok
+ end.
+%%--------------------------------------------------------------------
+interrupted_send(doc) ->
+ ["Use a subsystem that echos n char and then sends eof to cause a channel exit partway through a large send."];
+
+interrupted_send(Config) when is_list(Config) ->
+ PrivDir = ?config(priv_dir, Config),
+ UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
+ file:make_dir(UserDir),
+ SysDir = ?config(data_dir, Config),
+ {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
+ {user_dir, UserDir},
+ {password, "morot"},
+ {subsystems, [{"echo_n", {ssh_echo_server, [4000000]}}]}]),
+
+ ConnectionRef = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
+ {user, "foo"},
+ {password, "morot"},
+ {user_interaction, false},
+ {user_dir, UserDir}]),
+
+ {ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity),
+
+ success = ssh_connection:subsystem(ConnectionRef, ChannelId, "echo_n", infinity),
+
+ %% build 10MB binary
+ Data = << <<X:32>> || X <- lists:seq(1,2500000)>>,
+
+ %% expect remote end to send us 4MB back
+ <<ExpectedData:4000000/binary, _/binary>> = Data,
+
+ %% pre-adjust receive window so the other end doesn't block
+ ssh_connection:adjust_window(ConnectionRef, ChannelId, size(ExpectedData) + 1),
+
+ case ssh_connection:send(ConnectionRef, ChannelId, Data, 10000) of
+ {error, closed} ->
+ ok;
+ Msg ->
+ ct:fail({expected,{error,closed}, got, Msg})
+ end,
+ receive_data(ExpectedData, ConnectionRef, ChannelId),
+ ssh:close(ConnectionRef),
+ ssh:stop_daemon(Pid).
+
+%%--------------------------------------------------------------------
+%% Internal functions ------------------------------------------------
+%%--------------------------------------------------------------------
+big_cat_rx(ConnectionRef, ChannelId) ->
+ big_cat_rx(ConnectionRef, ChannelId, []).
+
+big_cat_rx(ConnectionRef, ChannelId, Acc) ->
+ receive
+ {ssh_cm, ConnectionRef, {data, ChannelId, 0, Data}} ->
+ %% ssh_connection:adjust_window(ConnectionRef, ChannelId, size(Data)),
+ %% window was pre-adjusted, don't adjust again here
+ big_cat_rx(ConnectionRef, ChannelId, [Data | Acc]);
+ {ssh_cm, ConnectionRef, {eof, ChannelId}} ->
+ {ok, iolist_to_binary(lists:reverse(Acc))}
+ after ?EXEC_TIMEOUT ->
+ timeout
+ end.
+
+receive_data(ExpectedData, ConnectionRef, ChannelId) ->
+ ExpectedData = collect_data(ConnectionRef, ChannelId).
+
+collect_data(ConnectionRef, ChannelId) ->
+ collect_data(ConnectionRef, ChannelId, []).
+
+collect_data(ConnectionRef, ChannelId, Acc) ->
+ receive
+ {ssh_cm, ConnectionRef, {data, ChannelId, 0, Data}} ->
+ collect_data(ConnectionRef, ChannelId, [Data | Acc]);
+ {ssh_cm, ConnectionRef, {eof, ChannelId}} ->
+ iolist_to_binary(lists:reverse(Acc))
+ after 5000 ->
+ timeout
+ end.
diff --git a/lib/ssh/test/ssh_connection_SUITE_data/ssh_host_rsa_key b/lib/ssh/test/ssh_connection_SUITE_data/ssh_host_rsa_key
new file mode 100644
index 0000000000..6ae7ee023d
--- /dev/null
+++ b/lib/ssh/test/ssh_connection_SUITE_data/ssh_host_rsa_key
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQDCZX+4FBDwZIh9y/Uxee1VJnEXlowpz2yDKwj8semM4q843337
+zbNfxHmladB1lpz2NqyxI175xMIJuDxogyZdsOxGnFAzAnthR4dqL/RWRWzjaxSB
+6IAO9SPYVVlrpZ+1hsjLW79fwXK/yc8VdhRuWTeQiRgYY2ek8+OKbOqz4QIDAQAB
+AoGANmvJzJO5hkLuvyDZHKfAnGTtpifcR1wtSa9DjdKUyn8vhKF0mIimnbnYQEmW
+NUUb3gXCZLi9PvkpRSVRrASDOZwcjoU/Kvww163vBUVb2cOZfFhyn6o2Sk88Tt++
+udH3hdjpf9i7jTtUkUe+QYPsia+wgvvrmn4QrahLAH86+kECQQDx5gFeXTME3cnW
+WMpFz3PPumduzjqgqMMWEccX4FtQkMX/gyGa5UC7OHFyh0N/gSWvPbRHa8A6YgIt
+n8DO+fh5AkEAzbqX4DOn8NY6xJIi42q7l/2jIA0RkB6P7YugW5NblhqBZ0XDnpA5
+sMt+rz+K07u9XZtxgh1xi7mNfwY6lEAMqQJBAJBEauCKmRj35Z6OyeQku59SPsnY
++SJEREVvSNw2lH9SOKQQ4wPsYlTGbvKtNVZgAcen91L5MmYfeckYE/fdIZECQQCt
+64zxsTnM1I8iFxj/gP/OYlJBikrKt8udWmjaghzvLMEw+T2DExJyb9ZNeT53+UMB
+m6O+B/4xzU/djvp+0hbhAkAemIt+rA5kTmYlFndhpvzkSSM8a2EXsO4XIPgGWCTT
+tQKS/tTly0ADMjN/TVy11+9d6zcqadNVuHXHGtR4W0GR
+-----END RSA PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_echo_server.erl b/lib/ssh/test/ssh_echo_server.erl
new file mode 100644
index 0000000000..007b00c373
--- /dev/null
+++ b/lib/ssh/test/ssh_echo_server.erl
@@ -0,0 +1,71 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2005-2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, 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: Example ssh server
+-module(ssh_echo_server).
+-behaviour(ssh_subsytem).
+-record(state, {
+ n,
+ id,
+ cm
+ }).
+-export([init/1, handle_msg/2, handle_ssh_msg/2, terminate/2]).
+
+init([N]) ->
+ {ok, #state{n = N}}.
+
+handle_msg({ssh_channel_up, ChannelId, ConnectionManager}, State) ->
+ {ok, State#state{id = ChannelId,
+ cm = ConnectionManager}}.
+
+handle_ssh_msg({ssh_cm, CM, {data, ChannelId, 0, Data}}, #state{n = N} = State) ->
+ M = N - size(Data),
+ case M > 0 of
+ true ->
+ ssh_connection:send(CM, ChannelId, Data),
+ {ok, State#state{n = M}};
+ false ->
+ <<SendData:N/binary, _/binary>> = Data,
+ ssh_connection:send(CM, ChannelId, SendData),
+ ssh_connection:send_eof(CM, ChannelId),
+ {stop, ChannelId, State}
+ end;
+handle_ssh_msg({ssh_cm, _ConnectionManager,
+ {data, _ChannelId, 1, Data}}, State) ->
+ error_logger:format(standard_error, " ~p~n", [binary_to_list(Data)]),
+ {ok, State};
+
+handle_ssh_msg({ssh_cm, _ConnectionManager, {eof, _ChannelId}}, State) ->
+ {ok, State};
+
+handle_ssh_msg({ssh_cm, _, {signal, _, _}}, State) ->
+ %% Ignore signals according to RFC 4254 section 6.9.
+ {ok, State};
+
+handle_ssh_msg({ssh_cm, _, {exit_signal, ChannelId, _, _Error, _}},
+ State) ->
+ {stop, ChannelId, State};
+
+handle_ssh_msg({ssh_cm, _, {exit_status, ChannelId, _Status}}, State) ->
+ {stop, ChannelId, State}.
+
+terminate(_Reason, _State) ->
+ ok.
diff --git a/lib/ssh/test/ssh_sftp_SUITE.erl b/lib/ssh/test/ssh_sftp_SUITE.erl
index d40b1d544d..232161d029 100644
--- a/lib/ssh/test/ssh_sftp_SUITE.erl
+++ b/lib/ssh/test/ssh_sftp_SUITE.erl
@@ -24,7 +24,6 @@
-compile(export_all).
-include_lib("common_test/include/ct.hrl").
-
-include_lib("kernel/include/file.hrl").
% Default timetrap timeout
@@ -33,16 +32,18 @@
-define(USER, "Alladin").
-define(PASSWD, "Sesame").
-%% Test server callback functions
%%--------------------------------------------------------------------
-%% 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.
+%% Common Test interface functions -----------------------------------
%%--------------------------------------------------------------------
+
+suite() ->
+ [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [{group, erlang_server},
+ {group, openssh_server}].
+
+
init_per_suite(Config) ->
case (catch crypto:start()) of
ok ->
@@ -52,35 +53,58 @@ init_per_suite(Config) ->
{skip,"Could not start crypto!"}
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) ->
ssh:stop(),
crypto:stop(),
Config.
%%--------------------------------------------------------------------
-%% 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: 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.
-%% Description: Initiation before each test case
+groups() ->
+ [{erlang_server, [], [open_close_file, open_close_dir, read_file, read_dir,
+ write_file, rename_file, mk_rm_dir, remove_file, links,
+ retrieve_attributes, set_attributes, async_read,
+ async_write, position, pos_read, pos_write]},
+ {openssh_server, [], [open_close_file, open_close_dir, read_file, read_dir,
+ write_file, rename_file, mk_rm_dir, remove_file, links,
+ retrieve_attributes, set_attributes, async_read,
+ async_write, position, pos_read, pos_write]}].
+
+init_per_group(erlang_server, Config) ->
+ PrivDir = ?config(priv_dir, Config),
+ SysDir = ?config(data_dir, Config),
+ Sftpd =
+ ssh_test_lib:daemon([{system_dir, SysDir},
+ {user_dir, PrivDir},
+ {user_passwords,
+ [{?USER, ?PASSWD}]},
+ {failfun,
+ fun ssh_test_lib:failfun/2}]),
+ [{group, erlang_server}, {sftpd, Sftpd} | Config];
+
+init_per_group(openssh_server, Config) ->
+ Host = ssh_test_lib:hostname(),
+ case (catch ssh_sftp:start_channel(Host,
+ [{user_interaction, false},
+ {silently_accept_hosts, true}])) of
+ {ok, _ChannelPid, Connection} ->
+ ssh:close(Connection),
+ [{group, openssh_server} | Config];
+ _ ->
+ {skip, "No openssh server"}
+ end.
+
+end_per_group(erlang_server, Config) ->
+ Config;
+end_per_group(_, Config) ->
+ Config.
+
%%--------------------------------------------------------------------
+
init_per_testcase(Case, Config) ->
prep(Config),
TmpConfig0 = lists:keydelete(watchdog, 1, Config),
TmpConfig = lists:keydelete(sftp, 1, TmpConfig0),
- Dog = test_server:timetrap(?default_timeout),
+ Dog = ct:timetrap(?default_timeout),
case ?config(group, Config) of
erlang_server ->
@@ -105,14 +129,6 @@ init_per_testcase(Case, Config) ->
[{sftp, Sftp}, {watchdog, Dog} | TmpConfig]
end.
-%%--------------------------------------------------------------------
-%% 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(rename_file, Config) ->
PrivDir = ?config(priv_dir, Config),
NewFileName = filename:join(PrivDir, "test.txt"),
@@ -124,69 +140,13 @@ end_per_testcase(_, Config) ->
end_per_testcase(Config) ->
{Sftp, Connection} = ?config(sftp, Config),
ssh_sftp:stop_channel(Sftp),
- ssh:close(Connection),
- Dog = ?config(watchdog, Config),
- test_server:timetrap_cancel(Dog),
- ok.
+ ssh:close(Connection).
%%--------------------------------------------------------------------
-%% 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
-%%--------------------------------------------------------------------
-all() ->
- [{group, erlang_server},
- {group, openssh_server}].
-
-groups() ->
- [{erlang_server, [], [open_close_file, open_close_dir, read_file, read_dir,
- write_file, rename_file, mk_rm_dir, remove_file, links,
- retrieve_attributes, set_attributes, async_read,
- async_write, position, pos_read, pos_write]},
- {openssh_server, [], [open_close_file, open_close_dir, read_file, read_dir,
- write_file, rename_file, mk_rm_dir, remove_file, links,
- retrieve_attributes, set_attributes, async_read,
- async_write, position, pos_read, pos_write]}].
-
-init_per_group(erlang_server, Config) ->
- PrivDir = ?config(priv_dir, Config),
- SysDir = ?config(data_dir, Config),
- Sftpd =
- ssh_test_lib:daemon([{system_dir, SysDir},
- {user_dir, PrivDir},
- {user_passwords,
- [{?USER, ?PASSWD}]},
- {failfun,
- fun ssh_test_lib:failfun/2}]),
- [{group, erlang_server}, {sftpd, Sftpd} | Config];
-
-init_per_group(openssh_server, Config) ->
- Host = ssh_test_lib:hostname(),
- case (catch ssh_sftp:start_channel(Host,
- [{user_interaction, false},
- {silently_accept_hosts, true}])) of
- {ok, _ChannelPid, Connection} ->
- ssh:close(Connection),
- [{group, openssh_server} | Config];
- _ ->
- {skip, "No openssh server"}
- end.
-
-end_per_group(erlang_server, Config) ->
- Config;
-end_per_group(_, Config) ->
- Config.
-
-
-%% Test cases starts here.
+%% Test Cases --------------------------------------------------------
%%--------------------------------------------------------------------
open_close_file(doc) ->
["Test API functions open/3 and close/2"];
-open_close_file(suite) ->
- [];
open_close_file(Config) when is_list(Config) ->
PrivDir = ?config(priv_dir, Config),
FileName = filename:join(PrivDir, "sftp.txt"),
@@ -198,21 +158,15 @@ open_close_file(Config) when is_list(Config) ->
ok = open_close_file(Sftp, FileName, [write, creat]),
ok = open_close_file(Sftp, FileName, [write, trunc]),
ok = open_close_file(Sftp, FileName, [append]),
- ok = open_close_file(Sftp, FileName, [read, binary]),
-
- ok.
+ ok = open_close_file(Sftp, FileName, [read, binary]).
open_close_file(Server, File, Mode) ->
{ok, Handle} = ssh_sftp:open(Server, File, Mode),
- ok = ssh_sftp:close(Server, Handle),
- ok.
-
+ ok = ssh_sftp:close(Server, Handle).
%%--------------------------------------------------------------------
open_close_dir(doc) ->
["Test API functions opendir/2 and close/2"];
-open_close_dir(suite) ->
- [];
open_close_dir(Config) when is_list(Config) ->
PrivDir = ?config(priv_dir, Config),
{Sftp, _} = ?config(sftp, Config),
@@ -220,138 +174,92 @@ open_close_dir(Config) when is_list(Config) ->
{ok, Handle} = ssh_sftp:opendir(Sftp, PrivDir),
ok = ssh_sftp:close(Sftp, Handle),
- {error, _} = ssh_sftp:opendir(Sftp, FileName),
+ {error, _} = ssh_sftp:opendir(Sftp, FileName).
- ok.
%%--------------------------------------------------------------------
read_file(doc) ->
["Test API funtion read_file/2"];
-read_file(suite) ->
- [];
read_file(Config) when is_list(Config) ->
PrivDir = ?config(priv_dir, Config),
FileName = filename:join(PrivDir, "sftp.txt"),
-
{Sftp, _} = ?config(sftp, Config),
-
{ok, Data} = ssh_sftp:read_file(Sftp, FileName),
+ {ok, Data} = file:read_file(FileName).
- {ok, Data} = file:read_file(FileName),
-
- ok.
%%--------------------------------------------------------------------
read_dir(doc) ->
["Test API function list_dir/2"];
-read_dir(suite) ->
- [];
read_dir(Config) when is_list(Config) ->
PrivDir = ?config(priv_dir, Config),
{Sftp, _} = ?config(sftp, Config),
{ok, Files} = ssh_sftp:list_dir(Sftp, PrivDir),
- test_server:format("sftp list dir: ~p~n", [Files]),
- ok.
+ ct:pal("sftp list dir: ~p~n", [Files]).
%%--------------------------------------------------------------------
write_file(doc) ->
["Test API function write_file/2"];
-write_file(suite) ->
- [];
write_file(Config) when is_list(Config) ->
PrivDir = ?config(priv_dir, Config),
FileName = filename:join(PrivDir, "sftp.txt"),
-
{Sftp, _} = ?config(sftp, Config),
Data = list_to_binary("Hej hopp!"),
-
ssh_sftp:write_file(Sftp, FileName, [Data]),
-
- {ok, Data} = file:read_file(FileName),
-
- ok.
+ {ok, Data} = file:read_file(FileName).
%%--------------------------------------------------------------------
remove_file(doc) ->
["Test API function delete/2"];
-remove_file(suite) ->
- [];
remove_file(Config) when is_list(Config) ->
PrivDir = ?config(priv_dir, Config),
FileName = filename:join(PrivDir, "sftp.txt"),
-
{Sftp, _} = ?config(sftp, Config),
{ok, Files} = ssh_sftp:list_dir(Sftp, PrivDir),
-
true = lists:member(filename:basename(FileName), Files),
-
ok = ssh_sftp:delete(Sftp, FileName),
-
{ok, NewFiles} = ssh_sftp:list_dir(Sftp, PrivDir),
-
false = lists:member(filename:basename(FileName), NewFiles),
-
- {error, _} = ssh_sftp:delete(Sftp, FileName),
-
- ok.
-
+ {error, _} = ssh_sftp:delete(Sftp, FileName).
%%--------------------------------------------------------------------
rename_file(doc) ->
["Test API function rename_file/2"];
-rename_file(suite) ->
- [];
rename_file(Config) when is_list(Config) ->
PrivDir = ?config(priv_dir, Config),
FileName = filename:join(PrivDir, "sftp.txt"),
NewFileName = filename:join(PrivDir, "test.txt"),
{Sftp, _} = ?config(sftp, Config),
-
{ok, Files} = ssh_sftp:list_dir(Sftp, PrivDir),
-
- test_server:format("FileName: ~p, Files: ~p~n", [FileName, Files]),
-
+ ct:pal("FileName: ~p, Files: ~p~n", [FileName, Files]),
true = lists:member(filename:basename(FileName), Files),
false = lists:member(filename:basename(NewFileName), Files),
-
ok = ssh_sftp:rename(Sftp, FileName, NewFileName),
-
{ok, NewFiles} = ssh_sftp:list_dir(Sftp, PrivDir),
-
- test_server:format("FileName: ~p, Files: ~p~n", [FileName, NewFiles]),
+ ct:pal("FileName: ~p, Files: ~p~n", [FileName, NewFiles]),
false = lists:member(filename:basename(FileName), NewFiles),
- true = lists:member(filename:basename(NewFileName), NewFiles),
-
- ok.
+ true = lists:member(filename:basename(NewFileName), NewFiles).
%%--------------------------------------------------------------------
mk_rm_dir(doc) ->
["Test API functions make_dir/2, del_dir/2"];
-mk_rm_dir(suite) ->
- [];
mk_rm_dir(Config) when is_list(Config) ->
PrivDir = ?config(priv_dir, Config),
{Sftp, _} = ?config(sftp, Config),
+
DirName = filename:join(PrivDir, "test"),
-
ok = ssh_sftp:make_dir(Sftp, DirName),
ok = ssh_sftp:del_dir(Sftp, DirName),
-
NewDirName = filename:join(PrivDir, "foo/bar"),
-
{error, _} = ssh_sftp:make_dir(Sftp, NewDirName),
- {error, _} = ssh_sftp:del_dir(Sftp, PrivDir),
-
- ok.
+ {error, _} = ssh_sftp:del_dir(Sftp, PrivDir).
%%--------------------------------------------------------------------
links(doc) ->
["Tests API function make_symlink/3"];
-links(suite) ->
- [];
links(Config) when is_list(Config) ->
- case test_server:os_type() of
+ case os:type() of
{win32, _} ->
{skip, "Links are not fully supported by windows"};
_ ->
@@ -361,74 +269,60 @@ links(Config) when is_list(Config) ->
LinkFileName = filename:join(PrivDir, "link_test.txt"),
ok = ssh_sftp:make_symlink(Sftp, LinkFileName, FileName),
- {ok, FileName} = ssh_sftp:read_link(Sftp, LinkFileName),
- ok
+ {ok, FileName} = ssh_sftp:read_link(Sftp, LinkFileName)
end.
%%--------------------------------------------------------------------
retrieve_attributes(doc) ->
["Test API function read_file_info/3"];
-retrieve_attributes(suite) ->
- [];
retrieve_attributes(Config) when is_list(Config) ->
PrivDir = ?config(priv_dir, Config),
FileName = filename:join(PrivDir, "sftp.txt"),
- {Sftp, _} = ?config(sftp, Config),
+ {Sftp, _} = ?config(sftp, Config),
{ok, FileInfo} = ssh_sftp:read_file_info(Sftp, FileName),
-
{ok, NewFileInfo} = file:read_file_info(FileName),
%% TODO comparison. There are some differences now is that ok?
- test_server:format("SFTP: ~p FILE: ~p~n", [FileInfo, NewFileInfo]),
- ok.
+ ct:pal("SFTP: ~p FILE: ~p~n", [FileInfo, NewFileInfo]).
%%--------------------------------------------------------------------
set_attributes(doc) ->
["Test API function write_file_info/3"];
-set_attributes(suite) ->
- [];
set_attributes(Config) when is_list(Config) ->
PrivDir = ?config(priv_dir, Config),
FileName = filename:join(PrivDir, "test.txt"),
- {Sftp, _} = ?config(sftp, Config),
+ {Sftp, _} = ?config(sftp, Config),
{ok,Fd} = file:open(FileName, write),
io:put_chars(Fd,"foo"),
-
ok = ssh_sftp:write_file_info(Sftp, FileName, #file_info{mode=8#400}),
{error, eacces} = file:write_file(FileName, "hello again"),
ssh_sftp:write_file_info(Sftp, FileName, #file_info{mode=8#600}),
- ok = file:write_file(FileName, "hello again"),
-
- ok.
+ ok = file:write_file(FileName, "hello again").
%%--------------------------------------------------------------------
async_read(doc) ->
["Test API aread/3"];
-async_read(suite) ->
- [];
async_read(Config) when is_list(Config) ->
{Sftp, _} = ?config(sftp, Config),
PrivDir = ?config(priv_dir, Config),
+
FileName = filename:join(PrivDir, "sftp.txt"),
{ok, Handle} = ssh_sftp:open(Sftp, FileName, [read]),
{async, Ref} = ssh_sftp:aread(Sftp, Handle, 20),
receive
{async_reply, Ref, {ok, Data}} ->
- test_server:format("Data: ~p~n", [Data]),
+ ct:pal("Data: ~p~n", [Data]),
ok;
Msg ->
- test_server:fail(Msg)
- end,
- ok.
+ ct:fail(Msg)
+ end.
%%--------------------------------------------------------------------
async_write(doc) ->
["Test API awrite/3"];
-async_write(suite) ->
- [];
async_write(Config) when is_list(Config) ->
{Sftp, _} = ?config(sftp, Config),
PrivDir = ?config(priv_dir, Config),
@@ -441,16 +335,13 @@ async_write(Config) when is_list(Config) ->
{async_reply, Ref, ok} ->
{ok, Data} = file:read_file(FileName);
Msg ->
- test_server:fail(Msg)
- end,
- ok.
+ ct:fail(Msg)
+ end.
%%--------------------------------------------------------------------
position(doc) ->
["Test API functions position/3"];
-position(suite) ->
- [];
position(Config) when is_list(Config) ->
PrivDir = ?config(priv_dir, Config),
FileName = filename:join(PrivDir, "test.txt"),
@@ -458,7 +349,6 @@ position(Config) when is_list(Config) ->
Data = list_to_binary("1234567890"),
ssh_sftp:write_file(Sftp, FileName, [Data]),
-
{ok, Handle} = ssh_sftp:open(Sftp, FileName, [read]),
{ok, 3} = ssh_sftp:position(Sftp, Handle, {bof, 3}),
@@ -477,15 +367,11 @@ position(Config) when is_list(Config) ->
{ok, "1"} = ssh_sftp:read(Sftp, Handle, 1),
{ok, 1} = ssh_sftp:position(Sftp, Handle, cur),
- {ok, "2"} = ssh_sftp:read(Sftp, Handle, 1),
-
- ok.
+ {ok, "2"} = ssh_sftp:read(Sftp, Handle, 1).
%%--------------------------------------------------------------------
pos_read(doc) ->
["Test API functions pread/3 and apread/3"];
-pos_read(suite) ->
- [];
pos_read(Config) when is_list(Config) ->
PrivDir = ?config(priv_dir, Config),
FileName = filename:join(PrivDir, "test.txt"),
@@ -494,7 +380,6 @@ pos_read(Config) when is_list(Config) ->
ssh_sftp:write_file(Sftp, FileName, [Data]),
{ok, Handle} = ssh_sftp:open(Sftp, FileName, [read]),
-
{async, Ref} = ssh_sftp:apread(Sftp, Handle, {bof, 5}, 4),
NewData = "opp!",
@@ -503,21 +388,17 @@ pos_read(Config) when is_list(Config) ->
{async_reply, Ref, {ok, NewData}} ->
ok;
Msg ->
- test_server:fail(Msg)
+ ct:fail(Msg)
end,
NewData1 = "hopp",
- {ok, NewData1} = ssh_sftp:pread(Sftp, Handle, {bof, 4}, 4),
+ {ok, NewData1} = ssh_sftp:pread(Sftp, Handle, {bof, 4}, 4).
- ok.
%%--------------------------------------------------------------------
pos_write(doc) ->
["Test API functions pwrite/4 and apwrite/4"];
-pos_write(suite) ->
- [];
pos_write(Config) when is_list(Config) ->
-
PrivDir = ?config(priv_dir, Config),
FileName = filename:join(PrivDir, "test.txt"),
{Sftp, _} = ?config(sftp, Config),
@@ -533,17 +414,16 @@ pos_write(Config) when is_list(Config) ->
{async_reply, Ref, ok} ->
ok;
Msg ->
- test_server:fail(Msg)
+ ct:fail(Msg)
end,
ok = ssh_sftp:pwrite(Sftp, Handle, eof, list_to_binary("!")),
NewData1 = list_to_binary("Bye, see you tomorrow!"),
- {ok, NewData1} = ssh_sftp:read_file(Sftp, FileName),
+ {ok, NewData1} = ssh_sftp:read_file(Sftp, FileName).
- ok.
-
-%% Internal functions
+%%--------------------------------------------------------------------
+%% Internal functions ------------------------------------------------
%%--------------------------------------------------------------------
prep(Config) ->
PrivDir = ?config(priv_dir, Config),
diff --git a/lib/ssh/test/ssh_sftpd_SUITE.erl b/lib/ssh/test/ssh_sftpd_SUITE.erl
index 695a7caa7d..5aa46872ee 100644
--- a/lib/ssh/test/ssh_sftpd_SUITE.erl
+++ b/lib/ssh/test/ssh_sftpd_SUITE.erl
@@ -24,12 +24,10 @@
-compile(export_all).
-include_lib("common_test/include/ct.hrl").
--include("test_server_line.hrl").
+-include_lib("kernel/include/file.hrl").
-include("ssh_xfer.hrl").
-include("ssh.hrl").
--include_lib("kernel/include/file.hrl").
-
-define(USER, "Alladin").
-define(PASSWD, "Sesame").
-define(XFER_PACKET_SIZE, 32768).
@@ -41,16 +39,33 @@
-define(is_set(F, Bits),
((F) band (Bits)) == (F)).
-%% Test server callback functions
%%--------------------------------------------------------------------
-%% 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.
+%% Common Test interface functions -----------------------------------
+%%--------------------------------------------------------------------
+
+all() ->
+ [open_close_file,
+ open_close_dir,
+ read_file,
+ read_dir,
+ write_file,
+ rename_file,
+ mk_rm_dir,
+ remove_file,
+ real_path,
+ retrieve_attributes,
+ set_attributes,
+ links,
+ ver3_rename,
+ relpath,
+ sshd_read_file,
+ ver6_basic].
+
+groups() ->
+ [].
+
%%--------------------------------------------------------------------
+
init_per_suite(Config) ->
case (catch crypto:start()) of
ok ->
@@ -66,34 +81,24 @@ init_per_suite(Config) ->
{skip,"Could not start crypto!"}
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) ->
SysDir = ?config(priv_dir, Config),
ssh_test_lib:clean_dsa(SysDir),
UserDir = filename:join(?config(priv_dir, Config), nopubkey),
file:del_dir(UserDir),
ssh:stop(),
- crypto:stop(),
- ok.
+ crypto:stop().
%%--------------------------------------------------------------------
-%% 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: 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.
-%% Description: Initiation before each test case
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
%%--------------------------------------------------------------------
+
init_per_testcase(TestCase, Config) ->
ssh:start(),
prep(Config),
@@ -102,12 +107,18 @@ init_per_testcase(TestCase, Config) ->
SystemDir = filename:join(?config(priv_dir, Config), system),
Port = ssh_test_lib:inet_port(node()),
-
- {ok, Sftpd} =
- ssh_sftpd:listen(Port, [{system_dir, SystemDir},
- {user_dir, PrivDir},
- {user_passwords,[{?USER, ?PASSWD}]},
- {pwdfun, fun(_,_) -> true end}]),
+ Options = [{system_dir, SystemDir},
+ {user_dir, PrivDir},
+ {user_passwords,[{?USER, ?PASSWD}]},
+ {pwdfun, fun(_,_) -> true end}],
+ {ok, Sftpd} = case TestCase of
+ ver6_basic ->
+ SubSystems = [ssh_sftpd:subsystem_spec([{sftpd_vsn, 6}])],
+ ssh:daemon(Port, [{subsystems, SubSystems}|Options]);
+ _ ->
+ SubSystems = [ssh_sftpd:subsystem_spec([])],
+ ssh:daemon(Port, [{subsystems, SubSystems}|Options])
+ end,
Cm = ssh_test_lib:connect(Port,
[{user_dir, ClientUserDir},
@@ -138,56 +149,22 @@ init_per_testcase(TestCase, Config) ->
{ok, <<?SSH_FXP_VERSION, ?UINT32(Version), _Ext/binary>>, _}
= reply(Cm, Channel),
- test_server:format("Client: ~p Server ~p~n", [ProtocolVer, Version]),
+ ct:pal("Client: ~p Server ~p~n", [ProtocolVer, Version]),
[{sftp, {Cm, Channel}}, {sftpd, Sftpd }| 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) ->
ssh_sftpd:stop(?config(sftpd, Config)),
{Cm, Channel} = ?config(sftp, Config),
ssh_connection:close(Cm, Channel),
ssh:close(Cm),
- ssh:stop(),
- ok.
+ ssh:stop().
%%--------------------------------------------------------------------
-%% 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
-%%--------------------------------------------------------------------
-all() ->
- [open_close_file, open_close_dir, read_file, read_dir,
- write_file, rename_file, mk_rm_dir, remove_file,
- real_path, retrieve_attributes, set_attributes, links,
- ver3_rename_OTP_6352, seq10670, sshd_read_file].
-
-groups() ->
- [].
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-%% Test cases starts here.
+%% Test Cases --------------------------------------------------------
%%--------------------------------------------------------------------
open_close_file(doc) ->
["Test SSH_FXP_OPEN and SSH_FXP_CLOSE commands"];
-open_close_file(suite) ->
- [];
open_close_file(Config) when is_list(Config) ->
PrivDir = ?config(priv_dir, Config),
FileName = filename:join(PrivDir, "test.txt"),
@@ -214,15 +191,11 @@ open_close_file(Config) when is_list(Config) ->
?UINT32(?SSH_FX_FAILURE), _/binary>>, _} =
open_file(PrivDir, Cm, Channel, NewReqId1,
?ACE4_READ_DATA bor ?ACE4_READ_ATTRIBUTES,
- ?SSH_FXF_OPEN_EXISTING),
-
- ok.
+ ?SSH_FXF_OPEN_EXISTING).
%%--------------------------------------------------------------------
open_close_dir(doc) ->
["Test SSH_FXP_OPENDIR and SSH_FXP_CLOSE commands"];
-open_close_dir(suite) ->
- [];
open_close_dir(Config) when is_list(Config) ->
PrivDir = ?config(priv_dir, Config),
{Cm, Channel} = ?config(sftp, Config),
@@ -250,8 +223,6 @@ open_close_dir(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
read_file(doc) ->
["Test SSH_FXP_READ command"];
-read_file(suite) ->
- [];
read_file(Config) when is_list(Config) ->
PrivDir = ?config(priv_dir, Config),
FileName = filename:join(PrivDir, "test.txt"),
@@ -270,28 +241,22 @@ read_file(Config) when is_list(Config) ->
Data/binary>>, _} =
read_file(Handle, 100, 0, Cm, Channel, NewReqId),
- {ok, Data} = file:read_file(FileName),
+ {ok, Data} = file:read_file(FileName).
- ok.
%%--------------------------------------------------------------------
read_dir(doc) ->
["Test SSH_FXP_READDIR command"];
-read_dir(suite) ->
- [];
read_dir(Config) when is_list(Config) ->
PrivDir = ?config(priv_dir, Config),
{Cm, Channel} = ?config(sftp, Config),
ReqId = 0,
{ok, <<?SSH_FXP_HANDLE, ?UINT32(ReqId), Handle/binary>>, _} =
open_dir(PrivDir, Cm, Channel, ReqId),
- ok = read_dir(Handle, Cm, Channel, ReqId),
- ok.
+ ok = read_dir(Handle, Cm, Channel, ReqId).
%%--------------------------------------------------------------------
write_file(doc) ->
["Test SSH_FXP_WRITE command"];
-write_file(suite) ->
- [];
write_file(Config) when is_list(Config) ->
PrivDir = ?config(priv_dir, Config),
FileName = filename:join(PrivDir, "test.txt"),
@@ -311,15 +276,11 @@ write_file(Config) when is_list(Config) ->
_/binary>>, _}
= write_file(Handle, Data, 0, Cm, Channel, NewReqId),
- {ok, Data} = file:read_file(FileName),
-
- ok.
+ {ok, Data} = file:read_file(FileName).
%%--------------------------------------------------------------------
remove_file(doc) ->
["Test SSH_FXP_REMOVE command"];
-remove_file(suite) ->
- [];
remove_file(Config) when is_list(Config) ->
PrivDir = ?config(priv_dir, Config),
FileName = filename:join(PrivDir, "test.txt"),
@@ -336,15 +297,11 @@ remove_file(Config) when is_list(Config) ->
{ok, <<?SSH_FXP_STATUS, ?UINT32(NewReqId),
?UINT32(?SSH_FX_FAILURE), _/binary>>, _} =
- remove(PrivDir, Cm, Channel, NewReqId),
-
- ok.
+ remove(PrivDir, Cm, Channel, NewReqId).
%%--------------------------------------------------------------------
rename_file(doc) ->
["Test SSH_FXP_RENAME command"];
-rename_file(suite) ->
- [];
rename_file(Config) when is_list(Config) ->
PrivDir = ?config(priv_dir, Config),
FileName = filename:join(PrivDir, "test.txt"),
@@ -377,15 +334,11 @@ rename_file(Config) when is_list(Config) ->
{ok, <<?SSH_FXP_STATUS, ?UINT32(NewReqId2),
?UINT32(?SSH_FX_OP_UNSUPPORTED), _/binary>>, _} =
rename(FileName, NewFileName, Cm, Channel, NewReqId2, 6,
- ?SSH_FXP_RENAME_ATOMIC),
-
- ok.
+ ?SSH_FXP_RENAME_ATOMIC).
%%--------------------------------------------------------------------
mk_rm_dir(doc) ->
["Test SSH_FXP_MKDIR and SSH_FXP_RMDIR command"];
-mk_rm_dir(suite) ->
- [];
mk_rm_dir(Config) when is_list(Config) ->
PrivDir = ?config(priv_dir, Config),
{Cm, Channel} = ?config(sftp, Config),
@@ -395,7 +348,7 @@ mk_rm_dir(Config) when is_list(Config) ->
_/binary>>, _} = mkdir(DirName, Cm, Channel, ReqId),
NewReqId = 1,
- {ok, <<?SSH_FXP_STATUS, ?UINT32(NewReqId), ?UINT32(?SSH_FX_FAILURE),
+ {ok, <<?SSH_FXP_STATUS, ?UINT32(NewReqId), ?UINT32(?SSH_FX_FILE_ALREADY_EXISTS),
_/binary>>, _} = mkdir(DirName, Cm, Channel, NewReqId),
NewReqId1 = 2,
@@ -404,16 +357,13 @@ mk_rm_dir(Config) when is_list(Config) ->
NewReqId2 = 3,
{ok, <<?SSH_FXP_STATUS, ?UINT32(NewReqId2), ?UINT32(?SSH_FX_NO_SUCH_FILE),
- _/binary>>, _} = rmdir(DirName, Cm, Channel, NewReqId2),
+ _/binary>>, _} = rmdir(DirName, Cm, Channel, NewReqId2).
- ok.
%%--------------------------------------------------------------------
real_path(doc) ->
["Test SSH_FXP_REALPATH command"];
-real_path(suite) ->
- [];
real_path(Config) when is_list(Config) ->
- case test_server:os_type() of
+ case os:type() of
{win32, _} ->
{skip, "Not a relevant test on windows"};
_ ->
@@ -432,20 +382,16 @@ real_path(Config) when is_list(Config) ->
RealPath = filename:absname(binary_to_list(Path)),
AbsPrivDir = filename:absname(PrivDir),
- test_server:format("Path: ~p PrivDir: ~p~n", [RealPath, AbsPrivDir]),
-
- true = RealPath == AbsPrivDir,
+ ct:pal("Path: ~p PrivDir: ~p~n", [RealPath, AbsPrivDir]),
- ok
+ true = RealPath == AbsPrivDir
end.
%%--------------------------------------------------------------------
links(doc) ->
[];
-links(suite) ->
- [];
links(Config) when is_list(Config) ->
- case test_server:os_type() of
+ case os:type() of
{win32, _} ->
{skip, "Links are not fully supported by windows"};
_ ->
@@ -467,15 +413,12 @@ links(Config) when is_list(Config) ->
true = binary_to_list(Path) == FileName,
- test_server:format("Path: ~p~n", [binary_to_list(Path)]),
- ok
+ ct:pal("Path: ~p~n", [binary_to_list(Path)])
end.
%%--------------------------------------------------------------------
retrieve_attributes(doc) ->
["Test SSH_FXP_STAT, SSH_FXP_LSTAT AND SSH_FXP_FSTAT commands"];
-retrieve_attributes(suite) ->
- [];
retrieve_attributes(Config) when is_list(Config) ->
PrivDir = ?config(priv_dir, Config),
FileName = filename:join(PrivDir, "test.txt"),
@@ -536,16 +479,13 @@ retrieve_attributes(Config) when is_list(Config) ->
Owner = list_to_integer(binary_to_list(BinOwner)),
Group = list_to_integer(binary_to_list(BinGroup))
- end, AttrValues),
+ end, AttrValues).
- ok.
%%--------------------------------------------------------------------
set_attributes(doc) ->
["Test SSH_FXP_SETSTAT AND SSH_FXP_FSETSTAT commands"];
-set_attributes(suite) ->
- [];
set_attributes(Config) when is_list(Config) ->
- case test_server:os_type() of
+ case os:type() of
{win32, _} ->
{skip, "Known error bug in erts file:read_file_info"};
_ ->
@@ -574,10 +514,10 @@ set_attributes(Config) when is_list(Config) ->
%% Can not test that NewPermissions = Permissions as
%% on Unix platforms, other bits than those listed in the
%% API may be set.
- test_server:format("Org: ~p New: ~p~n", [OrigPermissions, NewPermissions]),
+ ct:pal("Org: ~p New: ~p~n", [OrigPermissions, NewPermissions]),
true = OrigPermissions =/= NewPermissions,
- test_server:format("Try to open the file"),
+ ct:pal("Try to open the file"),
NewReqId = 2,
{ok, <<?SSH_FXP_HANDLE, ?UINT32(NewReqId), Handle/binary>>, _} =
open_file(FileName, Cm, Channel, NewReqId,
@@ -589,25 +529,20 @@ set_attributes(Config) when is_list(Config) ->
NewReqId1 = 3,
- test_server:format("Set original permissions on the now open file"),
+ ct:pal("Set original permissions on the now open file"),
{ok, <<?SSH_FXP_STATUS, ?UINT32(NewReqId1),
?UINT32(?SSH_FX_OK), _/binary>>, _} =
set_attributes_open_file(Handle, NewAtters, Cm, Channel, NewReqId1),
{ok, NewFileInfo1} = file:read_file_info(FileName),
- OrigPermissions = NewFileInfo1#file_info.mode,
- ok
+ OrigPermissions = NewFileInfo1#file_info.mode
end.
%%--------------------------------------------------------------------
-ver3_rename_OTP_6352(doc) ->
- ["Test that ver3 rename message is handled"];
-
-ver3_rename_OTP_6352(suite) ->
- [];
-
-ver3_rename_OTP_6352(Config) when is_list(Config) ->
+ver3_rename(doc) ->
+ ["Test that ver3 rename message is handled OTP 6352"];
+ver3_rename(Config) when is_list(Config) ->
PrivDir = ?config(priv_dir, Config),
FileName = filename:join(PrivDir, "test.txt"),
NewFileName = filename:join(PrivDir, "test1.txt"),
@@ -616,22 +551,16 @@ ver3_rename_OTP_6352(Config) when is_list(Config) ->
{ok, <<?SSH_FXP_STATUS, ?UINT32(ReqId),
?UINT32(?SSH_FX_OK), _/binary>>, _} =
- rename(FileName, NewFileName, Cm, Channel, ReqId, 3, 0),
-
- ok.
+ rename(FileName, NewFileName, Cm, Channel, ReqId, 3, 0).
%%--------------------------------------------------------------------
-seq10670(doc) ->
- ["Check that realpath works ok"];
-
-seq10670(suite) ->
- [];
-
-seq10670(Config) when is_list(Config) ->
+relpath(doc) ->
+ ["Check that realpath works ok seq10670"];
+relpath(Config) when is_list(Config) ->
ReqId = 0,
{Cm, Channel} = ?config(sftp, Config),
- case test_server:os_type() of
+ case os:type() of
{win32, _} ->
{skip, "Not a relevant test on windows"};
_ ->
@@ -644,11 +573,45 @@ seq10670(Config) when is_list(Config) ->
{ok, <<?SSH_FXP_NAME, ?UINT32(ReqId), ?UINT32(_), ?UINT32(Len),
Path:Len/binary, _/binary>>, _}
= real_path("/usr/bin/../..", Cm, Channel, ReqId),
-
Root = Path
end.
-%% Internal functions
+%%--------------------------------------------------------------------
+sshd_read_file(doc) ->
+ ["Test SSH_FXP_READ command, using sshd-server"];
+sshd_read_file(Config) when is_list(Config) ->
+ PrivDir = ?config(priv_dir, Config),
+ FileName = filename:join(PrivDir, "test.txt"),
+
+ ReqId = 0,
+ {Cm, Channel} = ?config(sftp, Config),
+
+ {ok, <<?SSH_FXP_HANDLE, ?UINT32(ReqId), Handle/binary>>, _} =
+ open_file(FileName, Cm, Channel, ReqId,
+ ?ACE4_READ_DATA bor ?ACE4_READ_ATTRIBUTES,
+ ?SSH_FXF_OPEN_EXISTING),
+
+ NewReqId = 1,
+
+ {ok, <<?SSH_FXP_DATA, ?UINT32(NewReqId), ?UINT32(_Length),
+ Data/binary>>, _} =
+ read_file(Handle, 100, 0, Cm, Channel, NewReqId),
+
+ {ok, Data} = file:read_file(FileName).
+ver6_basic(doc) ->
+ ["Test SFTP Version 6"];
+ver6_basic(Config) when is_list(Config) ->
+ PrivDir = ?config(priv_dir, Config),
+ %FileName = filename:join(PrivDir, "test.txt"),
+ {Cm, Channel} = ?config(sftp, Config),
+ ReqId = 0,
+ {ok, <<?SSH_FXP_STATUS, ?UINT32(ReqId), % Ver 6 we have 5
+ ?UINT32(?SSH_FX_FILE_IS_A_DIRECTORY), _/binary>>, _} =
+ open_file(PrivDir, Cm, Channel, ReqId,
+ ?ACE4_READ_DATA bor ?ACE4_READ_ATTRIBUTES,
+ ?SSH_FXF_OPEN_EXISTING).
+%%--------------------------------------------------------------------
+%% Internal functions ------------------------------------------------
%%--------------------------------------------------------------------
prep(Config) ->
PrivDir = ?config(priv_dir, Config),
@@ -684,7 +647,7 @@ reply(Cm, Channel, RBuf) ->
{ssh_cm, Cm, {closed, Channel}} ->
closed;
{ssh_cm, Cm, Msg} ->
- test_server:fail(Msg)
+ ct:fail(Msg)
end.
@@ -778,7 +741,7 @@ read_dir(Handle, Cm, Channel, ReqId) ->
case reply(Cm, Channel) of
{ok, <<?SSH_FXP_NAME, ?UINT32(ReqId), ?UINT32(Count),
?UINT32(Len), Listing:Len/binary, _/binary>>, _} ->
- test_server:format("Count: ~p Listing: ~p~n",
+ ct:pal("Count: ~p Listing: ~p~n",
[Count, binary_to_list(Listing)]),
read_dir(Handle, Cm, Channel, ReqId);
{ok, <<?SSH_FXP_STATUS, ?UINT32(ReqId),
@@ -921,32 +884,5 @@ encode_file_type(Type) ->
undefined -> ?SSH_FILEXFER_TYPE_UNKNOWN
end.
-%%--------------------------------------------------------------------
-sshd_read_file(doc) ->
- ["Test SSH_FXP_READ command, using sshd-server"];
-sshd_read_file(suite) ->
- [];
-sshd_read_file(Config) when is_list(Config) ->
- PrivDir = ?config(priv_dir, Config),
- FileName = filename:join(PrivDir, "test.txt"),
-
- ReqId = 0,
- {Cm, Channel} = ?config(sftp, Config),
-
- {ok, <<?SSH_FXP_HANDLE, ?UINT32(ReqId), Handle/binary>>, _} =
- open_file(FileName, Cm, Channel, ReqId,
- ?ACE4_READ_DATA bor ?ACE4_READ_ATTRIBUTES,
- ?SSH_FXF_OPEN_EXISTING),
-
- NewReqId = 1,
-
- {ok, <<?SSH_FXP_DATA, ?UINT32(NewReqId), ?UINT32(_Length),
- Data/binary>>, _} =
- read_file(Handle, 100, 0, Cm, Channel, NewReqId),
-
- {ok, Data} = file:read_file(FileName),
-
- ok.
-
not_default_permissions() ->
8#600. %% User read-write-only
diff --git a/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl b/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl
index 4c469ed5f7..8f722941d4 100644
--- a/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl
+++ b/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl
@@ -24,24 +24,32 @@
-compile(export_all).
-include_lib("common_test/include/ct.hrl").
--include("test_server_line.hrl").
-
-include_lib("kernel/include/file.hrl").
-define(USER, "Alladin").
-define(PASSWD, "Sesame").
-define(SSH_MAX_PACKET_SIZE, 32768).
-%% Test server callback functions
%%--------------------------------------------------------------------
-%% 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.
+%% Common Test interface functions -----------------------------------
+%%--------------------------------------------------------------------
+
+suite() ->
+ [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [close_file,
+ quit,
+ file_cb,
+ root_dir,
+ list_dir_limited,
+ ver6_basic].
+
+groups() ->
+ [].
+
%%--------------------------------------------------------------------
+
init_per_suite(Config) ->
catch ssh:stop(),
case catch crypto:start() of
@@ -60,12 +68,6 @@ init_per_suite(Config) ->
{skip,"Could not start ssh!"}
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) ->
UserDir = filename:join(?config(priv_dir, Config), nopubkey),
file:del_dir(UserDir),
@@ -75,18 +77,14 @@ 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: 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.
-%% Description: Initiation before each test case
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
%%--------------------------------------------------------------------
+
init_per_testcase(TestCase, Config) ->
ssh:start(),
PrivDir = ?config(priv_dir, Config),
@@ -115,7 +113,12 @@ init_per_testcase(TestCase, Config) ->
[{system_dir, SystemDir},
{user_dir, PrivDir},
{subsystems, [Spec]}];
-
+ "ver6_basic" ->
+ Spec =
+ ssh_sftpd:subsystem_spec([{sftpd_vsn, 6}]),
+ [{system_dir, SystemDir},
+ {user_dir, PrivDir},
+ {subsystems, [Spec]}];
_ ->
[{user_dir, PrivDir},
{system_dir, SystemDir}]
@@ -132,53 +135,21 @@ init_per_testcase(TestCase, Config) ->
NewConfig = lists:keydelete(sftpd, 1, TmpConfig),
[{port, Port}, {sftp, {ChannelPid, Connection}}, {sftpd, Sftpd} | NewConfig].
-%%--------------------------------------------------------------------
-%% 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) ->
catch ssh_sftpd:stop(?config(sftpd, Config)),
{Sftp, Connection} = ?config(sftp, Config),
catch ssh_sftp:stop_channel(Sftp),
catch ssh:close(Connection),
- ssh:stop(),
- ok.
+ ssh:stop().
%%--------------------------------------------------------------------
-%% 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
+%% Test cases starts here. -------------------------------------------
%%--------------------------------------------------------------------
-all() ->
- [close_file_OTP_6350, quit_OTP_6349, file_cb_OTP_6356,
- root_dir, list_dir_limited].
-
-groups() ->
- [].
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-%% Test cases starts here.
-%%--------------------------------------------------------------------
-close_file_OTP_6350(doc) ->
+close_file(doc) ->
["Test that sftpd closes its fildescriptors after compleating the "
- "transfer"];
-
-close_file_OTP_6350(suite) ->
- [];
+ "transfer OTP-6350"];
-close_file_OTP_6350(Config) when is_list(Config) ->
+close_file(Config) when is_list(Config) ->
DataDir = ?config(data_dir, Config),
FileName = filename:join(DataDir, "test.txt"),
@@ -186,28 +157,20 @@ close_file_OTP_6350(Config) when is_list(Config) ->
NumOfPorts = length(erlang:ports()),
- test_server:format("Number of open ports: ~p~n", [NumOfPorts]),
+ ct:pal("Number of open ports: ~p~n", [NumOfPorts]),
{ok, <<_/binary>>} = ssh_sftp:read_file(Sftp, FileName),
- NumOfPorts = length(erlang:ports()),
-
- test_server:format("Number of open ports: ~p~n",
- [length(erlang:ports())]),
-
- ok.
+ NumOfPorts = length(erlang:ports()).
%%--------------------------------------------------------------------
-quit_OTP_6349(doc) ->
+quit(doc) ->
[" When the sftp client ends the session the "
"server will now behave correctly and not leave the "
- "client hanging."];
-
-quit_OTP_6349(suite) ->
- [];
+ "client hanging. OTP-6349"];
-quit_OTP_6349(Config) when is_list(Config) ->
+quit(Config) when is_list(Config) ->
DataDir = ?config(data_dir, Config),
FileName = filename:join(DataDir, "test.txt"),
UserDir = ?config(priv_dir, Config),
@@ -230,19 +193,15 @@ quit_OTP_6349(Config) when is_list(Config) ->
{ok, <<_/binary>>} = ssh_sftp:read_file(NewSftp, FileName),
- ok = ssh_sftp:stop_channel(NewSftp),
- ok.
+ ok = ssh_sftp:stop_channel(NewSftp).
%%--------------------------------------------------------------------
-file_cb_OTP_6356(doc) ->
+file_cb(doc) ->
["Test that it is possible to change the callback module for"
- " the sftpds filehandling."];
-
-file_cb_OTP_6356(suite) ->
- [];
+ " the sftpds filehandling. OTP-6356"];
-file_cb_OTP_6356(Config) when is_list(Config) ->
+file_cb(Config) when is_list(Config) ->
DataDir = ?config(data_dir, Config),
PrivDir = ?config(priv_dir, Config),
FileName = filename:join(DataDir, "test.txt"),
@@ -279,17 +238,15 @@ file_cb_OTP_6356(Config) when is_list(Config) ->
NewDir = filename:join(PrivDir, "testdir"),
ok = ssh_sftp:make_dir(Sftp, NewDir),
alt_file_handler_check(alt_make_dir),
-
+
ok = ssh_sftp:del_dir(Sftp, NewDir),
alt_file_handler_check(alt_read_link_info),
alt_file_handler_check(alt_write_file_info),
- alt_file_handler_check(alt_del_dir),
- ok.
+ alt_file_handler_check(alt_del_dir).
+%%--------------------------------------------------------------------
root_dir(doc) ->
[""];
-root_dir(suite) ->
- [];
root_dir(Config) when is_list(Config) ->
{Sftp, _} = ?config(sftp, Config),
FileName = "test.txt",
@@ -298,26 +255,36 @@ root_dir(Config) when is_list(Config) ->
{ok, Bin} = ssh_sftp:read_file(Sftp, FileName),
{ok, Listing} =
ssh_sftp:list_dir(Sftp, "."),
- test_server:format("Listing: ~p~n", [Listing]),
- ok.
+ ct:pal("Listing: ~p~n", [Listing]).
+%%--------------------------------------------------------------------
list_dir_limited(doc) ->
[""];
-list_dir_limited(suite) ->
- [];
list_dir_limited(Config) when is_list(Config) ->
{Sftp, _} = ?config(sftp, Config),
{ok, Listing} =
ssh_sftp:list_dir(Sftp, "."),
- test_server:format("Listing: ~p~n", [Listing]),
- ok.
+ ct:pal("Listing: ~p~n", [Listing]).
+ver6_basic(doc) ->
+ ["Test some version 6 features"];
+ver6_basic(Config) when is_list(Config) ->
+ PrivDir = ?config(priv_dir, Config),
+ NewDir = filename:join(PrivDir, "testdir2"),
+ {Sftp, _} = ?config(sftp, Config),
+ ok = ssh_sftp:make_dir(Sftp, NewDir),
+ %%Test file_is_a_directory
+ {error, file_is_a_directory} = ssh_sftp:delete(Sftp, NewDir).
+%%--------------------------------------------------------------------
+%% Internal functions ------------------------------------------------
+%%--------------------------------------------------------------------
+
alt_file_handler_check(Msg) ->
receive
Msg ->
ok;
Other ->
- test_server:fail({Msg, Other})
+ ct:fail({Msg, Other})
after 10000 ->
- test_server:fail("Not alt file handler")
+ ct:fail("Not alt file handler")
end.
diff --git a/lib/ssh/test/ssh_sftpd_erlclient_SUITE_data/ssh_sftpd_file_alt.erl b/lib/ssh/test/ssh_sftpd_erlclient_SUITE_data/ssh_sftpd_file_alt.erl
index 9e119c4929..9f8a7c496c 100644
--- a/lib/ssh/test/ssh_sftpd_erlclient_SUITE_data/ssh_sftpd_file_alt.erl
+++ b/lib/ssh/test/ssh_sftpd_erlclient_SUITE_data/ssh_sftpd_file_alt.erl
@@ -48,7 +48,7 @@ get_cwd(State) ->
{file:get_cwd(), State}.
is_dir(AbsPath, State) ->
- sftpd_file_alt_tester ! alt_is_dir,
+ %sftpd_file_alt_tester ! alt_is_dir,
{filelib:is_dir(AbsPath), State}.
list_dir(AbsPath, State) ->
diff --git a/lib/ssh/test/ssh_test_lib.erl b/lib/ssh/test/ssh_test_lib.erl
index 609663c87a..6ed3dfa68c 100644
--- a/lib/ssh/test/ssh_test_lib.erl
+++ b/lib/ssh/test/ssh_test_lib.erl
@@ -25,8 +25,7 @@
-compile(export_all).
-include_lib("public_key/include/public_key.hrl").
--include("test_server.hrl").
--include("test_server_line.hrl").
+-include_lib("common_test/include/ct.hrl").
-define(TIMEOUT, 50000).
@@ -129,16 +128,16 @@ reply(TestCase, Result) ->
TestCase ! Result.
receive_exec_result(Msg) ->
- test_server:format("Expect data! ~p", [Msg]),
+ ct:pal("Expect data! ~p", [Msg]),
receive
{ssh_cm,_,{data,_,1, Data}} ->
- test_server:format("StdErr: ~p~n", [Data]),
+ ct:pal("StdErr: ~p~n", [Data]),
receive_exec_result(Msg);
Msg ->
- test_server:format("1: Collected data ~p", [Msg]),
+ ct:pal("1: Collected data ~p", [Msg]),
expected;
Other ->
- test_server:format("Other ~p", [Other]),
+ ct:pal("Other ~p", [Other]),
{unexpected_msg, Other}
end.
@@ -150,19 +149,19 @@ receive_exec_end(ConnectionRef, ChannelId) ->
case receive_exec_result(ExitStatus) of
{unexpected_msg, Eof} -> %% Open ssh seems to not allways send these messages
%% in the same order!
- test_server:format("2: Collected data ~p", [Eof]),
+ ct:pal("2: Collected data ~p", [Eof]),
case receive_exec_result(ExitStatus) of
expected ->
expected = receive_exec_result(Closed);
{unexpected_msg, Closed} ->
- test_server:format("3: Collected data ~p", [Closed])
+ ct:pal("3: Collected data ~p", [Closed])
end;
expected ->
- test_server:format("4: Collected data ~p", [ExitStatus]),
+ ct:pal("4: Collected data ~p", [ExitStatus]),
expected = receive_exec_result(Eof),
expected = receive_exec_result(Closed);
Other ->
- test_server:fail({unexpected_msg, Other})
+ ct:fail({unexpected_msg, Other})
end.
receive_exec_result(Data, ConnectionRef, ChannelId) ->
diff --git a/lib/ssh/test/ssh_to_openssh_SUITE.erl b/lib/ssh/test/ssh_to_openssh_SUITE.erl
index c337617ee4..99dc76e12d 100644
--- a/lib/ssh/test/ssh_to_openssh_SUITE.erl
+++ b/lib/ssh/test/ssh_to_openssh_SUITE.erl
@@ -21,7 +21,6 @@
-module(ssh_to_openssh_SUITE).
-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).
@@ -29,76 +28,10 @@
-define(TIMEOUT, 50000).
-define(SSH_DEFAULT_PORT, 22).
-%% 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 catch crypto:start() of
- ok ->
- case gen_tcp:connect("localhost", 22, []) of
- {error,econnrefused} ->
- {skip,"No openssh deamon"};
- _ ->
- Config
- end;
- _Else ->
- {skip,"Could not start crypto!"}
- 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) ->
- crypto:stop(),
- 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, Config) ->
- ssh:start(),
- 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
+%% Common Test interface functions -----------------------------------
%%--------------------------------------------------------------------
-end_per_testcase(_TestCase, _Config) ->
- ssh:stop(),
- ok.
-%%--------------------------------------------------------------------
-%% 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
-%%--------------------------------------------------------------------
all() ->
case os:find_executable("ssh") of
false ->
@@ -122,6 +55,23 @@ groups() ->
erlang_server_openssh_client_pulic_key_dsa]}
].
+init_per_suite(Config) ->
+ case catch crypto:start() of
+ ok ->
+ case gen_tcp:connect("localhost", 22, []) of
+ {error,econnrefused} ->
+ {skip,"No openssh deamon"};
+ _ ->
+ Config
+ end;
+ _Else ->
+ {skip,"Could not start crypto!"}
+ end.
+
+end_per_suite(_Config) ->
+ crypto:stop(),
+ ok.
+
init_per_group(erlang_server, Config) ->
DataDir = ?config(data_dir, Config),
UserDir = ?config(priv_dir, Config),
@@ -137,14 +87,21 @@ end_per_group(erlang_server, Config) ->
end_per_group(_, Config) ->
Config.
-%% TEST cases starts here.
+init_per_testcase(_TestCase, Config) ->
+ ssh:start(),
+ Config.
+
+end_per_testcase(_TestCase, _Config) ->
+ ssh:stop(),
+ ok.
+
+%%--------------------------------------------------------------------
+%% Test Cases --------------------------------------------------------
%%--------------------------------------------------------------------
+
erlang_shell_client_openssh_server(doc) ->
["Test that ssh:shell/2 works"];
-erlang_shell_client_openssh_server(suite) ->
- [];
-
erlang_shell_client_openssh_server(Config) when is_list(Config) ->
process_flag(trap_exit, true),
IO = ssh_test_lib:start_io_server(),
@@ -159,22 +116,19 @@ erlang_shell_client_openssh_server(Config) when is_list(Config) ->
ok
end;
Other0 ->
- test_server:fail({unexpected_msg, Other0})
+ ct:fail({unexpected_msg, Other0})
end,
receive
{'EXIT', Shell, normal} ->
ok;
Other1 ->
- test_server:fail({unexpected_msg, Other1})
+ ct:fail({unexpected_msg, Other1})
end.
%--------------------------------------------------------------------
erlang_client_openssh_server_exec(doc) ->
["Test api function ssh_connection:exec"];
-erlang_client_openssh_server_exec(suite) ->
- [];
-
erlang_client_openssh_server_exec(Config) when is_list(Config) ->
ConnectionRef = ssh_test_lib:connect(?SSH_DEFAULT_PORT, [{silently_accept_hosts, true},
{user_interaction, false}]),
@@ -187,11 +141,11 @@ erlang_client_openssh_server_exec(Config) when is_list(Config) ->
ssh_test_lib:receive_exec_end(ConnectionRef, ChannelId0);
{unexpected_msg,{ssh_cm, ConnectionRef, {exit_status, ChannelId0, 0}}
= ExitStatus0} ->
- test_server:format("0: Collected data ~p", [ExitStatus0]),
+ ct:pal("0: Collected data ~p", [ExitStatus0]),
ssh_test_lib:receive_exec_result(Data0,
ConnectionRef, ChannelId0);
Other0 ->
- test_server:fail(Other0)
+ ct:fail(Other0)
end,
{ok, ChannelId1} = ssh_connection:session_channel(ConnectionRef, infinity),
@@ -203,20 +157,17 @@ erlang_client_openssh_server_exec(Config) when is_list(Config) ->
ssh_test_lib:receive_exec_end(ConnectionRef, ChannelId1);
{unexpected_msg,{ssh_cm, ConnectionRef, {exit_status, ChannelId1, 0}}
= ExitStatus1} ->
- test_server:format("0: Collected data ~p", [ExitStatus1]),
+ ct:pal("0: Collected data ~p", [ExitStatus1]),
ssh_test_lib:receive_exec_result(Data1,
ConnectionRef, ChannelId1);
Other1 ->
- test_server:fail(Other1)
+ ct:fail(Other1)
end.
%%--------------------------------------------------------------------
erlang_client_openssh_server_exec_compressed(doc) ->
["Test that compression option works"];
-erlang_client_openssh_server_exec_compressed(suite) ->
- [];
-
erlang_client_openssh_server_exec_compressed(Config) when is_list(Config) ->
ConnectionRef = ssh_test_lib:connect(?SSH_DEFAULT_PORT, [{silently_accept_hosts, true},
{user_interaction, false},
@@ -230,19 +181,16 @@ erlang_client_openssh_server_exec_compressed(Config) when is_list(Config) ->
ssh_test_lib:receive_exec_end(ConnectionRef, ChannelId);
{unexpected_msg,{ssh_cm, ConnectionRef,
{exit_status, ChannelId, 0}} = ExitStatus} ->
- test_server:format("0: Collected data ~p", [ExitStatus]),
+ ct:pal("0: Collected data ~p", [ExitStatus]),
ssh_test_lib:receive_exec_result(Data, ConnectionRef, ChannelId);
Other ->
- test_server:fail(Other)
+ ct:fail(Other)
end.
%%--------------------------------------------------------------------
erlang_server_openssh_client_exec(doc) ->
["Test that exec command works."];
-erlang_server_openssh_client_exec(suite) ->
- [];
-
erlang_server_openssh_client_exec(Config) when is_list(Config) ->
SystemDir = ?config(data_dir, Config),
PrivDir = ?config(priv_dir, Config),
@@ -252,12 +200,12 @@ erlang_server_openssh_client_exec(Config) when is_list(Config) ->
{failfun, fun ssh_test_lib:failfun/2}]),
- test_server:sleep(500),
+ ct:sleep(500),
Cmd = "ssh -p " ++ integer_to_list(Port) ++
" -o UserKnownHostsFile=" ++ KnownHosts ++ " " ++ Host ++ " 1+1.",
- test_server:format("Cmd: ~p~n", [Cmd]),
+ ct:pal("Cmd: ~p~n", [Cmd]),
SshPort = open_port({spawn, Cmd}, [binary]),
@@ -265,7 +213,7 @@ erlang_server_openssh_client_exec(Config) when is_list(Config) ->
{SshPort,{data, <<"2\n">>}} ->
ok
after ?TIMEOUT ->
- test_server:fail("Did not receive answer")
+ ct:fail("Did not receive answer")
end,
ssh:stop_daemon(Pid).
@@ -274,9 +222,6 @@ erlang_server_openssh_client_exec(Config) when is_list(Config) ->
erlang_server_openssh_client_exec_compressed(doc) ->
["Test that exec command works."];
-erlang_server_openssh_client_exec_compressed(suite) ->
- [];
-
erlang_server_openssh_client_exec_compressed(Config) when is_list(Config) ->
SystemDir = ?config(data_dir, Config),
PrivDir = ?config(priv_dir, Config),
@@ -286,7 +231,7 @@ erlang_server_openssh_client_exec_compressed(Config) when is_list(Config) ->
{compression, zlib},
{failfun, fun ssh_test_lib:failfun/2}]),
- test_server:sleep(500),
+ ct:sleep(500),
Cmd = "ssh -p " ++ integer_to_list(Port) ++
" -o UserKnownHostsFile=" ++ KnownHosts ++ " -C "++ Host ++ " 1+1.",
@@ -296,7 +241,7 @@ erlang_server_openssh_client_exec_compressed(Config) when is_list(Config) ->
{SshPort,{data, <<"2\n">>}} ->
ok
after ?TIMEOUT ->
- test_server:fail("Did not receive answer")
+ ct:fail("Did not receive answer")
end,
ssh:stop_daemon(Pid).
@@ -305,9 +250,6 @@ erlang_server_openssh_client_exec_compressed(Config) when is_list(Config) ->
erlang_client_openssh_server_setenv(doc) ->
["Test api function ssh_connection:setenv"];
-erlang_client_openssh_server_setenv(suite) ->
- [];
-
erlang_client_openssh_server_setenv(Config) when is_list(Config) ->
ConnectionRef =
ssh_test_lib:connect(?SSH_DEFAULT_PORT, [{silently_accept_hosts, true},
@@ -332,15 +274,15 @@ erlang_client_openssh_server_setenv(Config) when is_list(Config) ->
{data,0,1, UnxpectedData}}} ->
%% Some os may return things as
%% ENV_TEST: Undefined variable.\n"
- test_server:format("UnxpectedData: ~p", [UnxpectedData]),
+ ct:pal("UnxpectedData: ~p", [UnxpectedData]),
ssh_test_lib:receive_exec_end(ConnectionRef, ChannelId);
{unexpected_msg,{ssh_cm, ConnectionRef, {exit_status, ChannelId, 0}}
= ExitStatus} ->
- test_server:format("0: Collected data ~p", [ExitStatus]),
+ ct:pal("0: Collected data ~p", [ExitStatus]),
ssh_test_lib:receive_exec_result(Data,
ConnectionRef, ChannelId);
Other ->
- test_server:fail(Other)
+ ct:fail(Other)
end.
%%--------------------------------------------------------------------
@@ -350,8 +292,6 @@ erlang_client_openssh_server_setenv(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
erlang_client_openssh_server_publickey_rsa(doc) ->
["Validate using rsa publickey."];
-erlang_client_openssh_server_publickey_rsa(suite) ->
- [];
erlang_client_openssh_server_publickey_rsa(Config) when is_list(Config) ->
{ok,[[Home]]} = init:get_argument(home),
KeyFile = filename:join(Home, ".ssh/id_rsa"),
@@ -379,8 +319,6 @@ erlang_client_openssh_server_publickey_rsa(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
erlang_client_openssh_server_publickey_dsa(doc) ->
["Validate using dsa publickey."];
-erlang_client_openssh_server_publickey_dsa(suite) ->
- [];
erlang_client_openssh_server_publickey_dsa(Config) when is_list(Config) ->
{ok,[[Home]]} = init:get_argument(home),
KeyFile = filename:join(Home, ".ssh/id_dsa"),
@@ -406,10 +344,6 @@ erlang_client_openssh_server_publickey_dsa(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
erlang_server_openssh_client_pulic_key_dsa(doc) ->
["Validate using dsa publickey."];
-
-erlang_server_openssh_client_pulic_key_dsa(suite) ->
- [];
-
erlang_server_openssh_client_pulic_key_dsa(Config) when is_list(Config) ->
SystemDir = ?config(data_dir, Config),
PrivDir = ?config(priv_dir, Config),
@@ -419,7 +353,7 @@ erlang_server_openssh_client_pulic_key_dsa(Config) when is_list(Config) ->
{public_key_alg, ssh_dsa},
{failfun, fun ssh_test_lib:failfun/2}]),
- test_server:sleep(500),
+ ct:sleep(500),
Cmd = "ssh -p " ++ integer_to_list(Port) ++
" -o UserKnownHostsFile=" ++ KnownHosts ++
@@ -430,17 +364,13 @@ erlang_server_openssh_client_pulic_key_dsa(Config) when is_list(Config) ->
{SshPort,{data, <<"2\n">>}} ->
ok
after ?TIMEOUT ->
- test_server:fail("Did not receive answer")
+ ct:fail("Did not receive answer")
end,
ssh:stop_daemon(Pid).
%%--------------------------------------------------------------------
erlang_client_openssh_server_password(doc) ->
["Test client password option"];
-
-erlang_client_openssh_server_password(suite) ->
- [];
-
erlang_client_openssh_server_password(Config) when is_list(Config) ->
%% to make sure we don't public-key-auth
UserDir = ?config(data_dir, Config),
@@ -451,7 +381,7 @@ erlang_client_openssh_server_password(Config) when is_list(Config) ->
{user_interaction, false},
{user_dir, UserDir}]),
- test_server:format("Test of user foo that does not exist. "
+ ct:pal("Test of user foo that does not exist. "
"Error msg: ~p~n", [Reason0]),
User = string:strip(os:cmd("whoami"), right, $\n),
@@ -465,10 +395,10 @@ erlang_client_openssh_server_password(Config) when is_list(Config) ->
{password, "foo"},
{user_interaction, false},
{user_dir, UserDir}]),
- test_server:format("Test of wrong Pasword. "
+ ct:pal("Test of wrong Pasword. "
"Error msg: ~p~n", [Reason1]);
_ ->
- test_server:format("Whoami failed reason: ~n", [])
+ ct:pal("Whoami failed reason: ~n", [])
end.
%%--------------------------------------------------------------------
@@ -477,13 +407,13 @@ erlang_client_openssh_server_password(Config) when is_list(Config) ->
%%
%%--------------------------------------------------------------------
%%--------------------------------------------------------------------
-%%% Internal functions
+%%% Internal functions -----------------------------------------------
%%--------------------------------------------------------------------
receive_hej() ->
receive
<<"Hej\n">> = Hej->
- test_server:format("Expected result: ~p~n", [Hej]);
+ ct:pal("Expected result: ~p~n", [Hej]);
Info ->
- test_server:format("Extra info: ~p~n", [Info]),
+ ct:pal("Extra info: ~p~n", [Info]),
receive_hej()
end.
diff --git a/lib/ssh/vsn.mk b/lib/ssh/vsn.mk
index bff73a1b40..71666a3179 100644
--- a/lib/ssh/vsn.mk
+++ b/lib/ssh/vsn.mk
@@ -1,5 +1,5 @@
#-*-makefile-*- ; force emacs to enter makefile-mode
-SSH_VSN = 2.1
+SSH_VSN = 2.1.3
APP_VSN = "ssh-$(SSH_VSN)"
diff --git a/lib/ssl/doc/src/Makefile b/lib/ssl/doc/src/Makefile
index 8736913c72..fb12499ef7 100644
--- a/lib/ssl/doc/src/Makefile
+++ b/lib/ssl/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2011. All Rights Reserved.
+# Copyright Ericsson AB 1999-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml
index a4da939d3e..73cda03b2f 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>2011</year>
+ <year>1999</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -22,15 +22,121 @@
</legalnotice>
<title>SSL Release Notes</title>
- <prepared>Peter H&ouml;gfeldt</prepared>
- <docno></docno>
- <date>2003-08-03</date>
- <rev>G</rev>
<file>notes.xml</file>
</header>
<p>This document describes the changes made to the SSL application.</p>
- <section><title>SSL 5.0.1</title>
+ <section><title>SSL 5.1.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ ssl:ssl_accept/2 timeout is no longer ignored</p>
+ <p>
+ Own Id: OTP-10600</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>SSL 5.1.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ ssl:recv/3 could "loose" data when the timeout occurs. If
+ the timout in ssl:connect or ssl:ssl_accept expired the
+ ssl connection process was not terminated as it should,
+ this due to gen_fsm:send_all_state_event timout is a
+ client side time out. These timouts are now handled by
+ the gen_fsm-procss instead.</p>
+ <p>
+ Own Id: OTP-10569</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Better termination handling that avoids hanging.</p>
+ <p>
+ Own Id: OTP-10574</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>SSL 5.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Sometimes the client process could receive an extra
+ {error, closed} message after ssl:recv had returned
+ {error, closed}.</p>
+ <p>
+ Own Id: OTP-10118</p>
+ </item>
+ <item>
+ <p>
+ ssl v3 alert number 41 (no_certificate_RESERVED) is now
+ recognized</p>
+ <p>
+ Own Id: OTP-10196</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Experimental support for TLS 1.1 is now available, will
+ be officially supported from OTP-R16. Thanks to Andreas
+ Schultz for implementing the first version.</p>
+ <p>
+ Own Id: OTP-8871</p>
+ </item>
+ <item>
+ <p>
+ Experimental support for TLS 1.2 is now available, will
+ be officially supported from OTP-R16. Thanks to Andreas
+ Schultz for implementing the first version.</p>
+ <p>
+ Own Id: OTP-8872</p>
+ </item>
+ <item>
+ <p>
+ Removed some bottlenecks increasing the applications
+ parallelism especially for the client side.</p>
+ <p>
+ Own Id: OTP-10113</p>
+ </item>
+ <item>
+ <p>
+ Workaround for handling certificates that wrongly encode
+ X509countryname in utf-8 when the actual value is a valid
+ ASCCI value of length 2. Such certificates are accepted
+ by many browsers such as Chrome and Fierfox so for
+ interoperability reasons we will too.</p>
+ <p>
+ Own Id: OTP-10222</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>SSL 5.0.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
@@ -495,1285 +601,7 @@
</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>
- <list>
- <item>
- <p>
- Fixes handling of the option fail_if_no_peer_cert and
- some undocumented options. Thanks to Rory Byrne.</p>
- <p>
- Own Id: OTP-8557</p>
- </item>
- </list>
- </section>
-
- <section><title>Improvements and New Features</title>
- <list>
- <item>
- <p>
- Support for Diffie-Hellman. ssl-3.11 requires
- public_key-0.6.</p>
- <p>
- Own Id: OTP-7046</p>
- </item>
- <item>
- <p>
- New ssl now properly handles ssl renegotiation, and
- initiates a renegotiation if ssl/ltls-sequence numbers
- comes close to the max value. However RFC-5746 is not yet
- supported, but will be in an upcoming release.</p>
- <p>
- Own Id: OTP-8517</p>
- </item>
- <item>
- <p>
- When gen_tcp is configured with the {packet,http} option,
- it automatically switches to expect HTTP Headers after a
- HTTP Request/Response line has been received. This update
- fixes ssl to behave in the same way. Thanks to Rory
- Byrne.</p>
- <p>
- Own Id: OTP-8545</p>
- </item>
- <item>
- <p>
- Ssl now correctly verifies the extended_key_usage
- extension and also allows the user to verify application
- specific extensions by supplying an appropriate fun.</p>
- <p>
- Own Id: OTP-8554 Aux Id: OTP-8553 </p>
- </item>
- <item>
- <p>
- Fixed ssl:transport_accept/2 to return properly when
- socket is closed. Thanks to Rory Byrne.</p>
- <p>
- Own Id: OTP-8560</p>
- </item>
- </list>
- </section>
-
-</section>
-
-<section><title>SSL 3.10.9</title>
-
- <section><title>Fixed Bugs and Malfunctions</title>
- <list>
- <item>
- <p>
- Fixed a crash in the certificate certification part.</p>
- <p>
- Own Id: OTP-8510 Aux Id: seq11525 </p>
- </item>
- </list>
- </section>
-
-</section>
-
-<section><title>SSL 3.10.8</title>
-
- <section><title>Fixed Bugs and Malfunctions</title>
- <list>
- <item>
- <p><c>ssl:send/2</c> ignored packet option, fix provided
- by YAMASHINA Hio.</p>
- <p>Fixed a file cache bug which caused problems when the
- same file was used for both cert and cacert.</p>
- <p>Allow <c>ssl:listen/2</c> to be called with option
- {ssl_imp, old}.</p>
- <p> Fixed ssl:setopts(Socket, binary) which didn't work
- for 'new' ssl.</p>.
- <p>
- Own Id: OTP-8441</p>
- </item>
- <item>
- <p>
- Do a controlled shutdown if a non ssl packet arrives as
- the first packet.</p>
- <p>
- Own Id: OTP-8459 Aux Id: seq11505 </p>
- </item>
- </list>
- </section>
-
-
- <section><title>Improvements and New Features</title>
- <list>
- <item>
- <p>Fixed session reuse (in new_ssl), thanks Wil Tan.</p>
- <p>Send CA list during Certificate Request (in new_ssl) ,
- thanks Wil Tan.</p> <p><c>NOTE</c>: SSL (new_ssl)
- requires public_key-0.5.</p>
- <p>
- Own Id: OTP-8372</p>
- </item>
- </list>
- </section>
-
-</section>
-
-<section><title>SSL 3.10.7</title>
-
- <section><title>Fixed Bugs and Malfunctions</title>
- <list>
- <item>
- <p>
- A ticker process could potentially be blocked
- indefinitely trying to send a tick to a node not
- responding. If this happened, the connection would not be
- brought down as it should.</p>
- <p> This requires erts-5.7.4 and kernel-2.13.4 or later
- to be able to get the erlang distribution over ssl to work.</p>
- <p>
- Own Id: OTP-8218</p>
- </item>
- </list>
- </section>
-
-
- <section><title>Improvements and New Features</title>
- <list>
- <item>
- <p>
- The documentation is now built with open source tools
- (xsltproc and fop) that exists on most platforms. One
- visible change is that the frames are removed.</p>
- <p>
- Own Id: OTP-8250</p>
- </item>
- <item>
- <p>
- Code cleanup from Kostis.</p>
- <p>
- Own Id: OTP-8260</p>
- </item>
- </list>
- </section>
-
-</section>
-
-<section><title>SSL 3.10.6</title>
-
- <section><title>Fixed Bugs and Malfunctions</title>
- <list>
- <item>
- <p>
- The ssl:ssl_accept/3 issue was not properly fixed in the
- previous patch, see OTP-8244.</p>
- <p>
- Own Id: OTP-8275 Aux Id: seq11451 </p>
- </item>
- </list>
</section>
-
-</section>
-
-<section><title>SSL 3.10.5</title>
-
- <section><title>Fixed Bugs and Malfunctions</title>
- <list>
- <item>
- <p>
- Allow clients to not send certificates if option
- <c>fail_if_no_peer_cert</c> was not set.</p>
- <p>
- Own Id: OTP-8224</p>
- </item>
- <item>
- <p>An ssl:ssl_accept/3 could crash a connection if the
- timing was wrong.</p> <p>Removed info message if the
- socket closed without a proper disconnect from the ssl
- layer. </p> <p>ssl:send/2 is now blocking until the
- message is sent.</p>
- <p>
- Own Id: OTP-8244 Aux Id: seq11420 </p>
- </item>
- </list>
- </section>
-
-</section>
-
-<section><title>SSL 3.10.4</title>
-
- <section><title>Fixed Bugs and Malfunctions</title>
- <list>
- <item>
- <p>
- A client could avoid a certificate check if the client
- code didn't send the requested certificate.</p>
- <p>
- Own Id: OTP-8137</p>
- </item>
- </list>
- </section>
-
-</section>
-
-<section><title>SSL 3.10.3</title>
-
- <section><title>Improvements and New Features</title>
- <list>
- <item>
- <p>Packet handling was not implemented correctly.</p>
- <p>Inet option handling support have been improved.</p>
- <p>The <c>verify_fun</c> is now invoked even if
- verify_peer is used, that implies that by default
- {bad_cert,unknown_ca} is an accepted fault during the
- client connection phase. The check can still be done by
- suppling another verify_fun.</p>
- <p>
- Own Id: OTP-8011 Aux Id: seq11287 </p>
- </item>
- </list>
- </section>
-
-</section>
-
-
-<section><title>SSL 3.10.2</title>
-
- <section><title>Fixed Bugs and Malfunctions</title>
- <list>
- <item>
- <p>
- A "new_ssl" socket was not closed if the controlling
- process died without calling ssl:close/1.</p>
- <p>
- Own Id: OTP-7963 Aux Id: seq11276 </p>
- </item>
- </list>
- </section>
-
-</section>
-
-<section><title>SSL 3.10.1</title>
-
- <section><title>Fixed Bugs and Malfunctions</title>
- <list>
- <item>
- <p>
- Fixed bug that caused the ssl handshake finished message
- to be calculated wrongly under the circumstances that the
- server did not send the trusted cert and that the
- previous cert did not have the extension telling us the
- trusted certs name. This manifested it self as
- bad_record_mac alert from the server.</p>
- <p>
- Own Id: OTP-7878</p>
- </item>
- </list>
- </section>
-
-
- <section><title>Improvements and New Features</title>
- <list>
- <item>
- <p>
- The cacertsfile option is now optional for ssl servers.</p>
- <p>
- Own Id: OTP-7656</p>
- </item>
- <item>
- <p>
- For the ssl client the options cacertfile, certfile and
- keyfile are now optional as they are not always needed
- depending on configuration of the client itself and the
- configuration of the server. Also as PEM-files may
- contain more than one entry the keyfile option will
- default to the same file as given by the certfile option.</p>
- <p>
- Own Id: OTP-7870</p>
- </item>
- <item>
- <p>
- Added new ssl client option verify_fun.</p>
- <p>
- Own Id: OTP-7871</p>
- </item>
- </list>
- </section>
-
-</section>
-
- <section><title>SSL 3.10</title>
-
- <section><title>Fixed Bugs and Malfunctions</title>
- <list>
- <item>
- <p>
- Error log entries are now formatted correctly.</p>
- <p>
- Own Id: OTP-7258</p>
- </item>
- </list>
- </section>
-
-
- <section><title>Improvements and New Features</title>
- <list>
- <item>
- <p>
- All handling of X509-certificates and public keys have
- been moved to the new application public_key.</p>
- <p>
- Own Id: OTP-6894</p>
- </item>
- <item>
- <p>
- New ssl now supports SSL-3.0 and TLS-1.0</p>
- <p>
- Own Id: OTP-7037</p>
- </item>
- <item>
- <p>
- New ssl now supports all inet-packet types.</p>
- <p>
- Own Id: OTP-7039</p>
- </item>
- <item>
- <p>
- The new ssl-server is now able to send a certificate
- request to the client. However new options may be
- introduced later to fully support all features regarding
- certificate requests.</p>
- <p>
- Own Id: OTP-7150</p>
- </item>
- </list>
- </section>
-
-
- <section><title>Known Bugs and Problems</title>
- <list>
- <item>
- <p>
- Running erlang distribution over ssl don't work as
- described in the documentation.</p>
- <p>
- Own Id: OTP-7536</p>
- </item>
- </list>
- </section>
-
- </section>
-
-
- <section><title>SSL 3.9</title>
-
- <section><title>Fixed Bugs and Malfunctions</title>
- <list>
- <item>
- <p>
- ssl_prim.erl was passing an FD rather than an #sslsocket
- to ssl_broker:ssl_accept_prim. This could cause problems
- in the deprecated accept function, this will not cause
- any more problems however this function is deprecated!</p>
- <p>
- Own Id: OTP-6926</p>
- </item>
- <item>
- <p>
- Erlang distribution over ssl was broken after R11B-0,
- this has now been fixed.</p>
- <p>
- Own Id: OTP-7004</p>
- </item>
- </list>
- </section>
-
-
- <section><title>Improvements and New Features</title>
- <list>
- <item>
- <p>
- All inet options are available in the new ssl
- implementation that is released as a alfa in ssl-3.9 and
- will replace the old implementation in ssl-4.0. This will
- not be fixed in the old implementation.</p>
- <p>
- Own Id: OTP-4677</p>
- </item>
- <item>
- <p>
- The new ssl implementation released as a alfa in this
- version supports upgrading of a tcp connection to an ssl
- connection so that http client and servers may implement
- RFC 2817.</p>
- <p>
- Own Id: OTP-5510</p>
- </item>
- <item>
- <p>A new implementation of ssl is released as a alfa
- version in ssl-3.9 it will later replace the old
- implementation in ssl-4.0. 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 an ssl connection. The alfa version has a
- few limitations that will be removed before the ssl-4.0
- release. Main differences and limitations in the alfa 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>Diffie-Hellman keyexchange is
- not supported.</item> <item>Not all inet packet types are
- supported.</item> <item>CRL and policy certificate
- extensions are not supported.</item> <item>In this alfa
- only sslv3 is enabled, although tlsv1 and tlsv1.1
- versions are implemented and will be supported in future
- versions.</item> <item>For security reasons sslv2 is not
- supported.</item> </list>
- <p>
- Own Id: OTP-6619</p>
- </item>
- <item>
- <p>
- New ssl implementation, released as alfa in ssl-3.9,
- supports ipv6. It will not be supported in the old
- implementation.</p>
- <p>
- Own Id: OTP-6637 Aux Id: OTP-6636 </p>
- </item>
- </list>
- </section>
-
- </section>
-
- <section>
- <title>SSL 3.1.1.1</title>
-
- <section>
- <title>Minor Makefile changes</title>
- <list type="bulleted">
- <item>
- <p>Removed use of <c>erl_flags</c> from Makefile.</p>
- <p>Own Id: OTP-6689</p>
- </item>
- </list>
- </section>
- </section>
-
- <section>
- <title>SSL 3.1.1</title>
-
- <section>
- <title>Crash on error in ssl_accept</title>
- <list type="bulleted">
- <item>
- <p>A bug in ssl_accept could cause all ssl
- connections to hang when a connection
- attempt was closed by the client while
- the server was in <c>ssl_accept</c>.</p>
- <p>Own Id: OTP-6612 Aux Id: seq10599</p>
- </item>
- </list>
- </section>
- </section>
-
- <section>
- <title>SSL 3.1</title>
-
- <section>
- <title>Fixed Bugs and Malfunctions</title>
- <list type="bulleted">
- <item>
- <p>SSL now uses a two-phase accept, with a separate accept
- calls for the socket and the ssl protocol. This avoids
- timeouts when a client doesn't initiate ssl handshake.</p>
- <p>With the old implementation of accept, the server
- was locked by a client, if the client didn't do
- proper ssl handshake.</p>
- <p>Own Id: OTP-6418 Aux Id: seq10105</p>
- </item>
- </list>
- </section>
- </section>
-
- <section>
- <title>SSL 3.0.12</title>
-
- <section>
- <title>Fixed Bugs and Malfunctions</title>
- <list type="bulleted">
- <item>
- <p>An integer array pointing to a struct pollfd array, is
- now reset before file descriptors are collected to be
- included in a call to poll(). This is to prevent file
- descriptors to be mixed up.</p>
- <p>Own Id: OTP-6084</p>
- </item>
- <item>
- <p>The generation of the module ssl_pkix_oid contained
- multiple identifiers, which made the mapping between
- atoms and identifiers not one-to-one.</p>
- <p>Own Id: OTP-6085</p>
- </item>
- </list>
- </section>
- </section>
-
- <section>
- <title>SSL 3.0.11</title>
-
- <section>
- <title>Fixed Bugs and Malfunctions</title>
- <list type="bulleted">
- <item>
- <p>The state of a connection in active mode could be in a
- restrictive state, so that an internal tcp_closed message
- was incorrectly considered illegal, resulting in a
- premature termination of the connection process.</p>
- <p>Own Id: OTP-5972 Aux Id: seq10188 </p>
- </item>
- </list>
- </section>
- </section>
-
- <section>
- <title>SSL 3.0.10</title>
-
- <section>
- <title>Fixed Bugs and Malfunctions</title>
- <list type="bulleted">
- <item>
- <p>Erlang distribution over SSL was broken. Corrected.
- (Thanks to Fredrik Thulin.)</p>
- <p>Own Id: OTP-5863</p>
- </item>
- </list>
- </section>
- </section>
-
- <section>
- <title>SSL 3.0.9</title>
-
- <section>
- <title>Fixed Bugs and Malfunctions</title>
- <list type="bulleted">
- <item>
- <p>The port program for the ssl application could waste huge
- amounts of CPU time if a write could not be completed
- directly and was put in the write queue. (Only on platforms
- where poll() is used, such as Solaris and Linux.)</p>
- <p>Own Id: OTP-5784</p>
- </item>
- </list>
- </section>
- </section>
-
- <section>
- <title>SSL 3.0.8</title>
-
- <section>
- <title>Fixed Bugs and Malfunctions</title>
- <list type="bulleted">
- <item>
- <p>A process reading only a portion of a sufficiently large
- amount of data from an accepted socket, and then quering
- the ssl library (e.g. ssl:getpeername()), would cause a
- global deadlock in the esock port program.</p>
- <p>Own Id: OTP-5702</p>
- </item>
- <item>
- <p>A spelling error in the module <c>ssl_pkix</c> caused the
- call to <c>ssl:peercert/2</c> to fail when the option
- <c>subject</c> was used.</p>
- <p>Own Id: OTP-5708</p>
- </item>
- <item>
- <p>Because fopen() on Solaris 8 can't handle file
- descriptor numbers above 255, reading of certificate
- files would fail if all file descriptors below 256 were
- in use (typically, if many connections were open). This
- problem has been worked around.</p>
- <p>The ssl application's port program used to use
- select(), which meant that it could not handle more than
- FD_SETSIZE file descriptors (usually 1024). To eliminate
- that limitation, poll() is now used on all platforms that
- support it.</p>
- <p>Solaris/Sparc, 64-bit emulator: The SO_REUSEADDR
- option was not set for listen sockets, which essentially
- made the ssl application unusable. Corrected.</p>
- <p>The default listen queue size for ssl port program was
- changed to 128 (from 5).</p>
- <p>Own Id: OTP-5755 Aux Id: seq10068 </p>
- </item>
- </list>
- </section>
- </section>
-
- <section>
- <title>Ssl 3.0.7</title>
-
- <section>
- <title>Fixed Bugs and Malfunctions</title>
- <list type="bulleted">
- <item>
- <p>The R/W buffer length i esock.c was too small. It has
- been increased from 4k to 32k.</p>
- <p>Own Id: OTP-5620</p>
- </item>
- </list>
- </section>
- </section>
-
- <section>
- <title>Ssl 3.0.6</title>
-
- <section>
- <title>Improvements and New Features</title>
- <list type="bulleted">
- <item>
- <p>A configuration option for choosing protocol versions has
- been added (<c>sslv2</c>, <c>sslv3</c>, and
- <c>tlsv1</c>).</p>
- <p>Own Id: OTP-5429 Aux Id: seq9755 </p>
- </item>
- </list>
- </section>
- </section>
-
- <section>
- <title>Ssl 3.0.5</title>
-
- <section>
- <title>Fixed Bugs and Malfunctions</title>
- <list type="bulleted">
- <item>
- <p>Linked in drivers in the crypto, and asn1 applications
- are now compiled with the -D_THREAD_SAFE and -D_REENTRANT
- switches on unix when the emulator has thread support
- enabled.</p>
- <p>Linked in drivers on MacOSX are not compiled with the
- undocumented -lbundle1.o switch anymore. Thanks to Sean
- Hinde who sent us a patch.</p>
- <p>Linked in driver in crypto, and port programs in ssl, now
- compiles on OSF1.</p>
- <p>Minor makefile improvements in runtime_tools.</p>
- <p>Own Id: OTP-5346</p>
- </item>
- </list>
- </section>
- </section>
-
- <section>
- <title>Ssl 3.0.4</title>
-
- <section>
- <title>Fixed Bugs and Malfunctions</title>
- <list type="bulleted">
- <item>
- <p><c>ssl:recv/3</c> with finite timeout value, closed the
- connection at timeout.</p>
- <p>Own Id: OTP-4882</p>
- </item>
- </list>
- </section>
- </section>
-
- <section>
- <title>Ssl 3.0.3</title>
-
- <section>
- <title>Fixed Bugs and Malfunctions</title>
- <list type="bulleted">
- <item>
- <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>
- </item>
- <item>
- <p>When the number of open file descriptors reached
- FD_SETSIZE, the SSL port program entered a busy loop.</p>
- <p>Own Id: OTP-5094 Aux Id: seq8806 </p>
- </item>
- </list>
- </section>
-
- <section>
- <title>Improvements and New Features</title>
- <list type="bulleted">
- <item>
- <p>The SSL application now supports SSL sessions for
- servers, which typically speeds up HTTP requests from
- browsers.</p>
- <p>Own Id: OTP-5095</p>
- </item>
- </list>
- </section>
- </section>
-
- <section>
- <title>SSL 3.0.2</title>
-
- <section>
- <title>Fixed Bugs and Malfunctions</title>
- <list type="bulleted">
- <item>
- <p>The UTF8String type is now defined in asn1-1.4.4.2 and
- later. Therefore the definitions of UTF8String has been
- removed from the ASN.1 modules PKIX1Explicit88.asn1 and
- PKIXAttributeCertificate.asn1. The SSL application can now
- only be built using asn-1.4.4.2 or later.</p>
- <p>OwnId: OTP-4971.</p>
- </item>
- </list>
- </section>
-
- <section>
- <title>Known Bugs and Problems</title>
- <p>See SSL-3.0.
- </p>
- </section>
- </section>
-
- <section>
- <title>SSL 3.0.1</title>
-
- <section>
- <title>Fixed Bugs and Malfunctions</title>
- <list type="bulleted">
- <item>
- <p>An unexpected object identifier would crash <c>ssl:peercert</c>. </p>
- <p>OwnId: OTP-4771.</p>
- </item>
- </list>
- </section>
-
- <section>
- <title>Known Bugs and Problems</title>
- <p>See SSL-3.0.
- </p>
- </section>
- </section>
-
- <section>
- <title>SSL 3.0</title>
-
- <section>
- <title>Improvements and New Features</title>
- <list type="bulleted">
- <item>
- <p>The <c>cache_timout</c> option was silently ignored. It had
- to do with SSL sessions, where multiple connections can occur.
- Since the Erlang SSL application does not support sessions the
- option is still ignored, and consequently the documentation
- about it has been removed.</p>
- <p>OwnId: OTP-3146</p>
- </item>
- <item>
- <p>The Erlang SSL application is now based on OpenSSL version
- 0.9.7a. OpenSSL 0.9.6 should also work.</p>
- <p>OwnId: OTP-4002</p>
- </item>
- <item>
- <p>When connecting it is now possible to bind to a local address
- and local port. </p>
- <p>OwnId: OTP-4675</p>
- </item>
- <item>
- <p>The <c>ssl_esock</c> port program is now part of the
- distribution and thus does not have to be created
- explicitly. It is dynamically linked to OpenSSL
- libraries in a "standard" location (typically
- <c>/usr/local/lib</c> on UNIX; in the path on Win32).</p>
- <p>OwnId:
- OTP-4676</p>
- </item>
- <item>
- <p>The new functions <c>ssl:peercert/1/2</c> provide information
- from the certificate of a peer of a connection.</p>
- <p>OwnId: OTP-4680
- <br></br>
-Aux Id: seq7688</p>
- </item>
- <item>
- <p>The function <c>ssl:port/1</c> has been removed from the
- documentation, but not from the <c>ssl</c> interface module.
- The recommendation is to use <c>ssl:peername/1</c>
- instead, which provides both address and port of the peer.</p>
- <p>OwnId: OTP-4681 </p>
- </item>
- <item>
- <p>New User's Guide documentation has been added.</p>
- <p>OwnId: OTP-4682 </p>
- </item>
- <item>
- <p>The old <c>ssl_socket</c> interface has been removed and also
- the documentation of it. </p>
- <p>OwnId: OTP-4683 </p>
- </item>
- <item>
- <p>The use of ephemeral RSA keys is now supported. It is
- a global configuration option (see the ssl(6) manual page).</p>
- <p>OwnId: OTP-4691.</p>
- </item>
- </list>
- </section>
-
- <section>
- <title>Fixed Bugs and Malfunctions</title>
- <list type="bulleted">
- <item>
- <p>The option <c>cacertfile</c> is now in effect, and can
- therefore no longer be set with the OS environment
- variable SSL_CERT_FILE (which did set the same value for
- all connections). </p>
- <p>OwnId: OTP-3146</p>
- </item>
- <item>
- <p>There was a synchronization error at closing of an SSL
- connection. </p>
- <p>OwnId: OTP-4435
- <br></br>
-Aux Id: seq7534</p>
- </item>
- <item>
- <p>C macros in <c>debuglog.c</c> were not ANSI C compliant.</p>
- <p>OwnId: OTP-4674</p>
- </item>
- <item>
- <p>The <c>binary</c> option was not properly handled.</p>
- <p>OwnId: OTP-4678</p>
- </item>
- <item>
- <p>The <c>ssl:format_error/1</c> did not consider <c>inet</c>
- error codes, nor did it have a catch all for unknown error
- codes.</p>
- <p>OwnId: OTP-4679</p>
- </item>
- </list>
- </section>
-
- <section>
- <title>Known Bugs and Problems</title>
- <list type="bulleted">
- <item>
- <p>Change of controlling process in not OTP compliant. </p>
- <p>OwnId; OTP-4712</p>
- </item>
- <item>
- <p>There is still no way to restrict the cipher sizes. </p>
- <p>OwnId: OTP-4712</p>
- </item>
- <item>
- <p>The <c>keep_alive</c> and <c>reuse_addr</c> options will be
- added in a future release. </p>
- <p>OwnId: OTP-4677</p>
- </item>
- <item>
- <p>There is currently no way to restrict the SSL/TLS
- protocol versions to use. In a future release this will be
- supported as a configuration option, and as an option for
- each connection as well. </p>
- <p>OwnId: OTP-4711.</p>
- </item>
- </list>
- </section>
- </section>
-
- <section>
- <title>SSL 2.3.6</title>
-
- <section>
- <title>Fixed Bugs and Malfunctions</title>
- <list type="bulleted">
- <item>
- <p>There was a synchronization error at closing, which could
- result in that an SSL socket was removed prematurely, resulting
- in that a user process referring to it received an unexpected
- exit.</p>
- <p>OwnId: OTP-4435
- <br></br>
-Aux Id: seq7600</p>
- </item>
- </list>
- </section>
-
- <section>
- <title>Known Bugs and Problems</title>
- <p>See SSL 2.2 . </p>
- </section>
- </section>
-
- <section>
- <title>SSL 2.3.5</title>
-
- <section>
- <title>Fixed Bugs and Malfunctions</title>
- <list type="bulleted">
- <item>
- <p>Setting of the option `nodelay' caused the SSL port program
- to dump core.</p>
- <p>OwnId: OTP-4380
- <br></br>
-Aux Id: -</p>
- </item>
- <item>
- <p>Setting of the option '{active, once}' in <c>setopts</c> was
- wrong, causing a correct socket message to be regarded as
- erroneous. </p>
- <p>OwnId: OTP-4380
- <br></br>
-Aux Id: -</p>
- </item>
- <item>
- <p>A self-signed peer certificate was always rejected with the
- error `eselfsignedcert', irrespective of the `depth' value. </p>
- <p>OwnId: OTP-4374
- <br></br>
-Aux Id: seq7417</p>
- </item>
- </list>
- </section>
-
- <section>
- <title>Known Bugs and Problems</title>
- <p>See SSL 2.2 . </p>
- </section>
- </section>
-
- <section>
- <title>SSL 2.3.4</title>
-
- <section>
- <title>Improvements and New Features</title>
- <list type="bulleted">
- <item>
- <p>All TCP options allowed in gen_tcp, are now also allowed in
- SSL, except the option <c>{reuseaddr, Boolean}</c>. A new
- function <c>getopts</c> has been added to the SSL interface
- module <c>ssl</c>. </p>
- <p>OwnId: OTP-4305, OTP-4159</p>
- </item>
- </list>
- </section>
- </section>
-
- <section>
- <title>SSL 2.3.3</title>
-
- <section>
- <title>Fixed Bugs and Malfunctions</title>
- <list type="bulleted">
- <item>
- <p>The roles of the SSLeay and OpenSSL packages has been
- clarified in the ssl(6) application manual page. Also
- the URLs from which to download SSLeay has been updated.</p>
- <p>OwnId: OTP-4002
- <br></br>
-Aux Id: seq5269</p>
- </item>
- <item>
- <p>A call to <c>ssl:listen(Port, Options)</c> with
- <c>Options = []</c> resulted in the cryptic <c>{error, ebadf}</c> return value. The return value has been changed
- to <c>{error, enooptions}</c>, and the behaviour has been
- documented in the <c>listen/2</c> function.</p>
- <p>OwnId: OTP-4016
- <br></br>
-Aux Id: seq7006</p>
- </item>
- <item>
- <p>Use of the option <c>{nodelay, boolean()}</c> crashed
- the <c>ssl_server</c>.</p>
- <p>OwnId: OTP-4070
- <br></br>
-Aux Id:</p>
- </item>
- <item>
- <p>A bug caused the Erlang distribution over ssl to fail.
- This bug has now been fixed.</p>
- <p>OwnId: OTP-4072
- <br></br>
-Aux Id:</p>
- </item>
- <item>
- <p>On Windows when the SSL port program encountered an
- error code not anticipated it crashed. </p>
- <p>OwnId: OTP-4132
- <br></br>
-Aux Id:</p>
- </item>
- </list>
- </section>
- </section>
-
- <section>
- <title>SSL 2.3.2</title>
-
- <section>
- <title>Fixed Bugs and Malfunctions</title>
- <list type="bulleted">
- <item>
- <p>The <c>ssl:accept/1-2</c> function sometimes returned
- <c>{error, {What, Where}}</c> instead of <c>{error, What}</c>, where <c>What</c> is an atom. </p>
- <p>OwnId: OTP-3775
- <br></br>
-Aux Id: seq4991</p>
- </item>
- </list>
- </section>
- </section>
-
- <section>
- <title>SSL 2.3.1</title>
-
- <section>
- <title>Fixed Bugs and Malfunctions</title>
- <list type="bulleted">
- <item>
- <p>Sometimes the SSL portprogram would loop in an accept
- loop, without terminating even when the SSL application
- was stopped.. </p>
- <p>OwnId: OTP-3691</p>
- </item>
- </list>
- </section>
- </section>
-
- <section>
- <title>SSL 2.3</title>
- <p>Functions have been added to SSL to experimentally support
- Erlang distribution.
- </p>
- </section>
-
- <section>
- <title>SSL 2.2.1</title>
- <p>The 2.2.1 version of SSL provides code replacement in runtime
- by upgrading from, or downgrading to, versions 2.1 and 2.2.
- </p>
- </section>
-
- <section>
- <title>SSL 2.2</title>
-
- <section>
- <title>Improvements and New Features</title>
- <list type="bulleted">
- <item>
- <p>The restriction that only the creator of an SSL socket can
- read from and write to the socket has been lifted.</p>
- <p>OwnId: OTP-3301</p>
- </item>
- <item>
- <p>The option <c>{packet, cdr}</c> for SSL sockets has been added,
- which means that SSL sockets also supports CDR encoded packets.</p>
- <p>OwnId: OTP-3302</p>
- </item>
- </list>
- </section>
-
- <section>
- <title>Known Bugs and Problems</title>
- <list type="bulleted">
- <item>
- <p>Setting of a CA certificate file with the <c>cacertfile</c>
- option (in calls to <c>ssl:accept/1/2</c> or
- <c>ssl:connect/3/4</c>) does not work due to weaknesses
- in the SSLeay package. </p>
- <p>A work-around is to set the OS environment variable
- <c>SSL_CERT_FILE</c> before SSL is started. However, then
- the CA certificate file will be global for all connections.</p>
- <p>OwnId: OTP-3146</p>
- </item>
- <item>
- <p>When changing controlling process of an SSL socket, a
- temporary process is started, which is not gen_server
- compliant.</p>
- <p>OwnId: OTP-3146</p>
- </item>
- <item>
- <p>Although there is a <c>cache</c> timeout option, it is
- silently ignored.</p>
- <p>OwnId: OTP-3146</p>
- </item>
- <item>
- <p>There is currently no way to restrict the cipher sizes.</p>
- <p>OwnId: OTP-3146</p>
- </item>
- </list>
- </section>
- </section>
-
- <section>
- <title>SSL 2.1</title>
-
- <section>
- <title>Improvements and New Features</title>
- <list type="bulleted">
- <item>
- <p>The set of possible error reasons has been extended to
- contain diagnostics on erroneous certificates and failures
- to verify certificates.</p>
- <p>OwnId: OTP-3145</p>
- </item>
- <item>
- <p>The maximum number of simultaneous SSL connections on
- Windows has been increased from 31 to 127.</p>
- <p>OwnId: OTP-3145</p>
- </item>
- </list>
- </section>
-
- <section>
- <title>Fixed Bugs and Malfunctions</title>
- <list type="bulleted">
- <item>
- <p>A dead-lock occurring when write queues are not empty has
- been removed. </p>
- <p>OwnId: OTP-3145</p>
- </item>
- <item>
- <p>Error reasons have been unified and changed.</p>
- <p>(** POTENTIAL INCOMPATIBILITY **)</p>
- <p>OwnId: OTP-3145</p>
- </item>
- <item>
- <p>On Windows a check of the existence of the environment
- variable <c>ERLSRV_SERVICE_NAME</c> has been added. If
- that variable is defined, the port program of the SSL
- application will not terminated when a user logs off.</p>
- <p>OwnId: OTP-3145</p>
- </item>
- <item>
- <p>An error in the setting of the <c>nodelay</c> option
- has been corrected.</p>
- <p>OwnId: OTP-3145</p>
- </item>
- <item>
- <p>The confounded notions of verify mode and verify depth has
- been corrected. The option <c>verifydepth</c> has been
- removed, and the two separate options <c>verify</c> and
- <c>depth</c> has been added.</p>
- <p>(** POTENTIAL INCOMPATIBILITY **)</p>
- <p>OwnId: OTP-3145</p>
- </item>
- </list>
- </section>
-
- <section>
- <title>Known Bugs and Problems</title>
- <list type="bulleted">
- <item>
- <p>Setting of a CA certificate file with the <c>cacertfile</c>
- option (in calls to <c>ssl:accept/1/2</c> or
- <c>ssl:connect/3/4</c>) does not work due to weaknesses
- in the SSLeay package. </p>
- <p>A work-around is to set the OS environment variable
- <c>SSL_CERT_FILE</c> before SSL is started. However, then
- the CA certificate file will be global for all connections.</p>
- <p>OwnId: OTP-3146</p>
- </item>
- <item>
- <p>When changing controlling process of an SSL socket, a
- temporary process is started, which is not gen_server
- compliant.</p>
- <p>OwnId: OTP-3146</p>
- </item>
- <item>
- <p>Although there is a <c>cache</c> timeout option, it is
- silently ignored.</p>
- <p>OwnId: OTP-3146</p>
- </item>
- <item>
- <p>There is currently no way to restrict the cipher sizes.</p>
- <p>OwnId: OTP-3146</p>
- </item>
- </list>
- </section>
- </section>
-
- <section>
- <title>SSL 2.0</title>
- <p>A complete new version of SSL with separate I/O channels
- for all connections with non-blocking I/O multiplexing.</p>
- </section>
</chapter>
diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml
index 28bf82b406..e45a4c774f 100644
--- a/lib/ssl/doc/src/ssl.xml
+++ b/lib/ssl/doc/src/ssl.xml
@@ -36,12 +36,16 @@
<list type="bulleted">
<item>ssl requires the crypto and public_key applications.</item>
- <item>Supported SSL/TLS-versions are SSL-3.0 and TLS-1.0 </item>
+ <item>Supported SSL/TLS-versions are SSL-3.0, TLS-1.0,
+ TLS-1.1 and TLS-1.2 (no support for elliptic curve cipher suites yet).</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>IDEA cipher suites are not supported as they have
+ become deprecated by the latest TLS spec so there is not any
+ real motivation to implement them.</item>
<item>CRL and policy certificate
extensions are not supported yet. </item>
</list>
@@ -75,7 +79,9 @@
{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()}
+ {ssl_imp, ssl_imp()} | {reuse_sessions, boolean()} | {reuse_session, fun()}
+ {next_protocols_advertised, list(binary()} |
+ {client_preferred_next_protocols, binary(), client | server, list(binary())}
</c></p>
<p><c>transportoption() = {CallbackModule, DataTag, ClosedTag}
@@ -106,7 +112,7 @@
<p><c>sslsocket() - opaque to the user. </c></p>
- <p><c>protocol() = sslv3 | tlsv1 </c></p>
+ <p><c>protocol() = sslv3 | tlsv1 | 'tlsv1.1' | 'tlsv1.2' </c></p>
<p><c>ciphers() = [ciphersuite()] | string() (according to old API)</c></p>
@@ -297,8 +303,29 @@ fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom()} |
when possible.
</item>
+ <tag>{client_preferred_next_protocols, Precedence:: server | client, ClientPrefs::[binary()]}
+ {client_preferred_next_protocols, Precedence:: server | client, ClientPrefs::[binary()] , Default :: binary()}}</tag>
+
+ <item> <p>Indicates the client will try to perform Next Protocol
+ Negotiation.</p>
+
+ <p>If precedence is server the negaotiated protocol will be the
+ first protocol that appears on the server advertised list that is
+ also on the clients preference list.</p>
+
+ <p>If the precedence is client the negaotiated protocol will be the
+ first protocol that appears on the clients preference list that is
+ also on the server advertised list.</p>
+
+ <p> If the client does not support any of the servers advertised
+ protocols or the server does not advertise any protocols the
+ client will fallback to the first protocol in its list or if a
+ default is supplied it will fallback to that instead. If the
+ server does not support next protocol renegotiation the
+ connection will be aborted if no default protocol is supplied.</p>
+ </item>
</taglist>
- </section>
+ </section>
<section>
<title>SSL OPTION DESCRIPTIONS - SERVER SIDE</title>
@@ -349,6 +376,14 @@ fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom()} |
SuggestedSessionId is a binary(), PeerCert is a DER encoded
certificate, Compression is an enumeration integer
and CipherSuite is of type ciphersuite().
+ </item>
+
+ <tag>{next_protocols_advertised, Protocols :: list(binary())}</tag>
+ <item>The list of protocols to send to the client if the client indicates
+ it supports the Next Protocol extension. The client may select a protocol
+ that is not on this list. The list of protocols must not contain an empty
+ binary. If the server negotiates a Next Protocol it can be accessed
+ using <c>negotiated_next_protocol/1</c> method.
</item>
</taglist>
@@ -762,8 +797,23 @@ fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom()} |
ssl application.</p>
</desc>
</func>
+ <func>
+ <name>negotiated_next_protocol(Socket) -> {ok, Protocol} | {error, next_protocol_not_negotiated}</name>
+ <fsummary>Returns the Next Protocol negotiated.</fsummary>
+ <type>
+ <v>Socket = sslsocket()</v>
+ <v>Protocol = binary()</v>
+ </type>
+ <desc>
+ <p>
+ Returns the Next Protocol negotiated.
+ </p>
+ </desc>
+ </func>
+
+
</funcs>
-
+
<section>
<title>SEE ALSO</title>
<p><seealso marker="kernel:inet">inet(3) </seealso> and
diff --git a/lib/ssl/doc/src/ssl_app.xml b/lib/ssl/doc/src/ssl_app.xml
index 2ba6f48611..178bbcaebb 100644
--- a/lib/ssl/doc/src/ssl_app.xml
+++ b/lib/ssl/doc/src/ssl_app.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="latin1" ?>
+<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE appref SYSTEM "appref.dtd">
<appref>
@@ -29,7 +29,17 @@
sockets.</appsummary>
<section>
- <title>Environment</title>
+ <title>DEPENDENCIES</title>
+ <p>The ssl application uses the Erlang applications public_key and
+ crypto to handle public keys and encryption, hence these
+ applications needs to be loaded for the ssl application to work. In
+ an embedded environment that means they need to be started with
+ application:start/[1,2] before the ssl application is started.
+ </p>
+ </section>
+
+ <section>
+ <title>ENVIRONMENT</title>
<p>The following application environment configuration parameters
are defined for the SSL application. Refer to application(3) for
more information about configuration parameters.
diff --git a/lib/ssl/doc/src/ssl_protocol.xml b/lib/ssl/doc/src/ssl_protocol.xml
index 17268a634d..f540dc999b 100644
--- a/lib/ssl/doc/src/ssl_protocol.xml
+++ b/lib/ssl/doc/src/ssl_protocol.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2003</year><year>2011</year>
+ <year>2003</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -25,9 +25,8 @@
<file>ssl_protocol.xml</file>
</header>
- <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>The erlang SSL application currently implements the protocol SSL/TLS
+ for currently supported versions see <seealso marker="ssl">ssl(3)</seealso>
</p>
<p>By default erlang SSL is run over the TCP/IP protocol even
diff --git a/lib/ssl/examples/certs/Makefile b/lib/ssl/examples/certs/Makefile
index 549f11d709..be2546fe82 100644
--- a/lib/ssl/examples/certs/Makefile
+++ b/lib/ssl/examples/certs/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2003-2010. All Rights Reserved.
+# Copyright Ericsson AB 2003-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/ssl/examples/src/Makefile b/lib/ssl/examples/src/Makefile
index 420634c97e..3e033c08d9 100644
--- a/lib/ssl/examples/src/Makefile
+++ b/lib/ssl/examples/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2003-2011. All Rights Reserved.
+# Copyright Ericsson AB 2003-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/ssl/examples/src/client_server.erl b/lib/ssl/examples/src/client_server.erl
index baf5a9185e..133a1764bc 100644
--- a/lib/ssl/examples/src/client_server.erl
+++ b/lib/ssl/examples/src/client_server.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -21,18 +21,14 @@
-module(client_server).
--export([start/0, start/1, init_connect/1]).
+-export([start/0, init_connect/1]).
start() ->
- start([ssl, subject]).
-
-start(CertOpts) ->
%% Start ssl application
+ application:start(crypto),
+ application:start(public_key),
application:start(ssl),
- %% Always seed
- ssl:seed("ellynatefttidppohjeh"),
-
%% Let the current process be the server that listens and accepts
%% Listen
{ok, LSock} = ssl:listen(0, mk_opts(listen)),
@@ -40,14 +36,14 @@ start(CertOpts) ->
io:fwrite("Listen: port = ~w.~n", [LPort]),
%% Spawn the client process that connects to the server
- spawn(?MODULE, init_connect, [{LPort, CertOpts}]),
+ spawn(?MODULE, init_connect, [LPort]),
%% Accept
{ok, ASock} = ssl:transport_accept(LSock),
ok = ssl:ssl_accept(ASock),
io:fwrite("Accept: accepted.~n"),
- {ok, Cert} = ssl:peercert(ASock, CertOpts),
- io:fwrite("Accept: peer cert:~n~p~n", [Cert]),
+ {ok, Cert} = ssl:peercert(ASock),
+ io:fwrite("Accept: peer cert:~n~p~n", [public_key:pkix_decode_cert(Cert, otp)]),
io:fwrite("Accept: sending \"hello\".~n"),
ssl:send(ASock, "hello"),
{error, closed} = ssl:recv(ASock, 0),
@@ -59,12 +55,12 @@ start(CertOpts) ->
%% Client connect
-init_connect({LPort, CertOpts}) ->
+init_connect(LPort) ->
{ok, Host} = inet:gethostname(),
{ok, CSock} = ssl:connect(Host, LPort, mk_opts(connect)),
io:fwrite("Connect: connected.~n"),
- {ok, Cert} = ssl:peercert(CSock, CertOpts),
- io:fwrite("Connect: peer cert:~n~p~n", [Cert]),
+ {ok, Cert} = ssl:peercert(CSock),
+ io:fwrite("Connect: peer cert:~n~p~n", [public_key:pkix_decode_cert(Cert, otp)]),
{ok, Data} = ssl:recv(CSock, 0),
io:fwrite("Connect: got data: ~p~n", [Data]),
io:fwrite("Connect: closing and terminating.~n"),
diff --git a/lib/ssl/src/Makefile b/lib/ssl/src/Makefile
index a08444b8dd..043645be41 100644
--- a/lib/ssl/src/Makefile
+++ b/lib/ssl/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2011. All Rights Reserved.
+# Copyright Ericsson AB 1999-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -108,10 +108,10 @@ clean:
rm -f errs core *~
$(APP_TARGET): $(APP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
docs:
@@ -130,3 +130,23 @@ release_spec: opt
release_docs_spec:
+# ----------------------------------------------------
+# Dependencies
+# ----------------------------------------------------
+$(EBIN)/inet_tls_dist.$(EMULATOR): ../../kernel/include/net_address.hrl ../../kernel/include/dist.hrl ../../kernel/include/dist_util.hrl
+$(EBIN)/ssl.$(EMULATOR): ssl_internal.hrl ssl_record.hrl ssl_cipher.hrl ssl_handshake.hrl ../../public_key/include/public_key.hrl
+$(EBIN)/ssl_alert.$(EMULATOR): ssl_alert.hrl ssl_record.hrl
+$(EBIN)/ssl_certificate.$(EMULATOR): ssl_internal.hrl ssl_alert.hrl ssl_handshake.hrl ../../public_key/include/public_key.hrl
+$(EBIN)/ssl_certificate_db.$(EMULATOR): ssl_internal.hrl ../../public_key/include/public_key.hrl ../../kernel/include/file.hrl
+$(EBIN)/ssl_cipher.$(EMULATOR): ssl_internal.hrl ssl_record.hrl ssl_cipher.hrl ssl_handshake.hrl ssl_alert.hrl ../../public_key/include/public_key.hrl
+$(EBIN)/ssl_connection.$(EMULATOR): ssl_internal.hrl ssl_record.hrl ssl_cipher.hrl ssl_handshake.hrl ssl_alert.hrl ../../public_key/include/public_key.hrl
+$(EBIN)/ssl_handshake.$(EMULATOR): ssl_internal.hrl ssl_record.hrl ssl_cipher.hrl ssl_handshake.hrl ssl_alert.hrl ../../public_key/include/public_key.hrl
+$(EBIN)/ssl_manager.$(EMULATOR): ssl_internal.hrl ssl_handshake.hrl ../../kernel/include/file.hrl
+$(EBIN)/ssl_record.$(EMULATOR): ssl_internal.hrl ssl_record.hrl ssl_cipher.hrl ssl_handshake.hrl ssl_alert.hrl
+$(EBIN)/ssl_session.$(EMULATOR): ssl_internal.hrl ssl_handshake.hrl
+$(EBIN)/ssl_session_cache.$(EMULATOR): ssl_internal.hrl ssl_handshake.hrl
+$(EBIN)/ssl_session_cache_api.$(EMULATOR): ssl_internal.hrl ssl_handshake.hrl
+$(EBIN)/ssl_ssl3.$(EMULATOR): ssl_internal.hrl ssl_record.hrl ssl_cipher.hrl
+$(EBIN)/ssl_tls1.$(EMULATOR): ssl_internal.hrl ssl_record.hrl ssl_cipher.hrl
+
+
diff --git a/lib/ssl/src/ssl.appup.src b/lib/ssl/src/ssl.appup.src
index e346b1e9e6..76e14860ec 100644
--- a/lib/ssl/src/ssl.appup.src
+++ b/lib/ssl/src/ssl.appup.src
@@ -1,12 +1,14 @@
%% -*- erlang -*-
{"%VSN%",
[
- {"5.0", [{restart_application, ssl}]},
+ {<<"5.1\\*">>, [{restart_application, ssl}]},
+ {<<"5.0\\*">>, [{restart_application, ssl}]},
{<<"4\\.*">>, [{restart_application, ssl}]},
{<<"3\\.*">>, [{restart_application, ssl}]}
],
[
- {"5.0", [{restart_application, ssl}]},
+ {<<"5.1\\*">>, [{restart_application, ssl}]},
+ {<<"5.0\\*">>, [{restart_application, ssl}]},
{<<"4\\.*">>, [{restart_application, ssl}]},
{<<"3\\.*">>, [{restart_application, ssl}]}
]}.
diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl
index 5e3ced144a..09f2819ca8 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -25,18 +25,18 @@
-export([start/0, start/1, stop/0, transport_accept/1,
transport_accept/2, ssl_accept/1, ssl_accept/2, ssl_accept/3,
- cipher_suites/0, cipher_suites/1, close/1, shutdown/2,
+ cipher_suites/0, cipher_suites/1, suite_definition/1,
+ close/1, shutdown/2,
connect/3, connect/2, connect/4, connection_info/1,
- controlling_process/2, listen/2, pid/1, peername/1, peercert/1,
+ controlling_process/2, listen/2, peername/1, peercert/1,
recv/2, recv/3, send/2, getopts/2, setopts/2, sockname/1,
versions/0, session_info/1, format_error/1,
- renegotiate/1, prf/5, clear_pem_cache/0]).
-
--deprecated({pid, 1, next_major_release}).
+ renegotiate/1, prf/5, clear_pem_cache/0, random_bytes/1, negotiated_next_protocol/1]).
-include("ssl_internal.hrl").
-include("ssl_record.hrl").
-include("ssl_cipher.hrl").
+-include("ssl_handshake.hrl").
-include_lib("public_key/include/public_key.hrl").
@@ -44,7 +44,7 @@
-export_type([connect_option/0, listen_option/0, ssl_option/0, transport_option/0,
erl_cipher_suite/0, %% From ssl_cipher.hrl
tls_atom_version/0, %% From ssl_internal.hrl
- prf_random/0]).
+ prf_random/0, sslsocket/0]).
-record(config, {ssl, %% SSL parameters
inet_user, %% User set inet options
@@ -52,6 +52,8 @@
inet_ssl, %% inet options for internal ssl socket
cb %% Callback info
}).
+
+-type sslsocket() :: #sslsocket{}.
-type connect_option() :: socket_connect_option() | ssl_option() | transport_option().
-type socket_connect_option() :: gen_tcp:connect_option().
-type listen_option() :: socket_listen_option() | ssl_option() | transport_option().
@@ -64,7 +66,9 @@
{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}.
+ {reuse_session, fun()} | {hibernate_after, integer()|undefined} |
+ {next_protocols_advertised, list(binary())} |
+ {client_preferred_next_protocols, binary(), client | server, list(binary())}.
-type verify_type() :: verify_none | verify_peer.
-type path() :: string().
@@ -160,7 +164,7 @@ listen(Port, Options0) ->
#config{cb={CbModule, _, _, _},inet_user=Options} = Config,
case CbModule:listen(Port, Options) of
{ok, ListenSocket} ->
- {ok, #sslsocket{pid = {ListenSocket, Config}, fd = new_ssl}};
+ {ok, #sslsocket{pid = {ListenSocket, Config}}};
Err = {error, _} ->
Err
end
@@ -240,18 +244,20 @@ ssl_accept(Socket, SslOptions, Timeout) when is_port(Socket) ->
%%
%% Description: Close an ssl connection
%%--------------------------------------------------------------------
+close(#sslsocket{pid = Pid}) when is_pid(Pid) ->
+ ssl_connection:close(Pid);
close(#sslsocket{pid = {ListenSocket, #config{cb={CbMod,_, _, _}}}}) ->
- CbMod:close(ListenSocket);
-close(#sslsocket{pid = Pid}) ->
- ssl_connection:close(Pid).
+ CbMod:close(ListenSocket).
%%--------------------------------------------------------------------
-spec send(#sslsocket{}, iodata()) -> ok | {error, reason()}.
%%
%% Description: Sends data over the ssl connection
%%--------------------------------------------------------------------
-send(#sslsocket{pid = Pid}, Data) ->
- ssl_connection:send(Pid, Data).
+send(#sslsocket{pid = Pid}, Data) when is_pid(Pid) ->
+ ssl_connection:send(Pid, Data);
+send(#sslsocket{pid = {ListenSocket, #config{cb={CbModule, _, _, _}}}}, Data) ->
+ CbModule:send(ListenSocket, Data). %% {error,enotconn}
%%--------------------------------------------------------------------
-spec recv(#sslsocket{}, integer()) -> {ok, binary()| list()} | {error, reason()}.
@@ -261,8 +267,10 @@ send(#sslsocket{pid = Pid}, Data) ->
%%--------------------------------------------------------------------
recv(Socket, Length) ->
recv(Socket, Length, infinity).
-recv(#sslsocket{pid = Pid, fd = new_ssl}, Length, Timeout) ->
- ssl_connection:recv(Pid, Length, Timeout).
+recv(#sslsocket{pid = Pid}, Length, Timeout) when is_pid(Pid) ->
+ ssl_connection:recv(Pid, Length, Timeout);
+recv(#sslsocket{pid = {Listen, #config{cb={CbModule, _, _, _}}}}, _,_) when is_port(Listen)->
+ CbModule:recv(Listen, 0). %% {error,enotconn}
%%--------------------------------------------------------------------
-spec controlling_process(#sslsocket{}, pid()) -> ok | {error, reason()}.
@@ -270,8 +278,12 @@ recv(#sslsocket{pid = Pid, fd = new_ssl}, Length, Timeout) ->
%% Description: Changes process that receives the messages when active = true
%% or once.
%%--------------------------------------------------------------------
-controlling_process(#sslsocket{pid = Pid}, NewOwner) when is_pid(Pid) ->
- ssl_connection:new_user(Pid, NewOwner).
+controlling_process(#sslsocket{pid = Pid}, NewOwner) when is_pid(Pid), is_pid(NewOwner) ->
+ ssl_connection:new_user(Pid, NewOwner);
+controlling_process(#sslsocket{pid = {Listen,
+ #config{cb={CbModule, _, _, _}}}}, NewOwner) when is_port(Listen),
+ is_pid(NewOwner) ->
+ CbModule:controlling_process(Listen, NewOwner).
%%--------------------------------------------------------------------
-spec connection_info(#sslsocket{}) -> {ok, {tls_atom_version(), erl_cipher_suite()}} |
@@ -279,31 +291,54 @@ controlling_process(#sslsocket{pid = Pid}, NewOwner) when is_pid(Pid) ->
%%
%% Description: Returns ssl protocol and cipher used for the connection
%%--------------------------------------------------------------------
-connection_info(#sslsocket{pid = Pid}) ->
- ssl_connection:info(Pid).
+connection_info(#sslsocket{pid = Pid}) when is_pid(Pid) ->
+ ssl_connection:info(Pid);
+connection_info(#sslsocket{pid = {Listen, _}}) when is_port(Listen) ->
+ {error, enotconn}.
%%--------------------------------------------------------------------
-spec peername(#sslsocket{}) -> {ok, {inet:ip_address(), inet:port_number()}} | {error, reason()}.
%%
%% Description: same as inet:peername/1.
%%--------------------------------------------------------------------
-peername(#sslsocket{pid = Pid}) ->
- ssl_connection:peername(Pid).
+peername(#sslsocket{pid = Pid, fd = Socket}) when is_pid(Pid)->
+ inet:peername(Socket);
+peername(#sslsocket{pid = {ListenSocket, _}}) ->
+ inet:peername(ListenSocket). %% Will return {error, enotconn}
%%--------------------------------------------------------------------
-spec peercert(#sslsocket{}) ->{ok, DerCert::binary()} | {error, reason()}.
%%
%% Description: Returns the peercert.
%%--------------------------------------------------------------------
-peercert(#sslsocket{pid = Pid}) ->
+peercert(#sslsocket{pid = Pid}) when is_pid(Pid) ->
case ssl_connection:peer_certificate(Pid) of
{ok, undefined} ->
{error, no_peercert};
Result ->
Result
- end.
+ end;
+peercert(#sslsocket{pid = {Listen, _}}) when is_port(Listen) ->
+ {error, enotconn}.
+
+%%--------------------------------------------------------------------
+-spec suite_definition(cipher_suite()) -> erl_cipher_suite().
+%%
+%% Description: Return erlang cipher suite definition.
+%%--------------------------------------------------------------------
+suite_definition(S) ->
+ {KeyExchange, Cipher, Hash, _} = ssl_cipher:suite_definition(S),
+ {KeyExchange, Cipher, Hash}.
%%--------------------------------------------------------------------
+-spec negotiated_next_protocol(#sslsocket{}) -> {ok, binary()} | {error, reason()}.
+%%
+%% Description: Returns the next protocol that has been negotiated. If no
+%% protocol has been negotiated will return {error, next_protocol_not_negotiated}
+%%--------------------------------------------------------------------
+negotiated_next_protocol(#sslsocket{pid = Pid}) ->
+ ssl_connection:negotiated_next_protocol(Pid).
+
-spec cipher_suites() -> [erl_cipher_suite()].
-spec cipher_suites(erlang | openssl) -> [erl_cipher_suite()] | [string()].
@@ -314,7 +349,7 @@ cipher_suites() ->
cipher_suites(erlang) ->
Version = ssl_record:highest_protocol_version([]),
- [ssl_cipher:suite_definition(S) || S <- ssl_cipher:suites(Version)];
+ [suite_definition(S) || S <- ssl_cipher:suites(Version)];
cipher_suites(openssl) ->
Version = ssl_record:highest_protocol_version([]),
@@ -374,8 +409,9 @@ setopts(#sslsocket{}, Options) ->
%%
%% Description: Same as gen_tcp:shutdown/2
%%--------------------------------------------------------------------
-shutdown(#sslsocket{pid = {ListenSocket, #config{cb={CbMod,_, _, _}}}}, How) ->
- CbMod:shutdown(ListenSocket, How);
+shutdown(#sslsocket{pid = {Listen, #config{cb={CbMod,_, _, _}}}},
+ How) when is_port(Listen) ->
+ CbMod:shutdown(Listen, How);
shutdown(#sslsocket{pid = Pid}, How) ->
ssl_connection:shutdown(Pid, How).
@@ -384,11 +420,11 @@ shutdown(#sslsocket{pid = Pid}, How) ->
%%
%% Description: Same as inet:sockname/1
%%--------------------------------------------------------------------
-sockname(#sslsocket{pid = {ListenSocket, _}}) ->
- inet:sockname(ListenSocket);
+sockname(#sslsocket{pid = {Listen, _}}) when is_port(Listen) ->
+ inet:sockname(Listen);
-sockname(#sslsocket{pid = Pid}) ->
- ssl_connection:sockname(Pid).
+sockname(#sslsocket{pid = Pid, fd = Socket}) when is_pid(Pid) ->
+ inet:sockname(Socket).
%%---------------------------------------------------------------
-spec session_info(#sslsocket{}) -> {ok, list()} | {error, reason()}.
@@ -396,19 +432,21 @@ sockname(#sslsocket{pid = Pid}) ->
%% 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).
+session_info(#sslsocket{pid = Pid}) when is_pid(Pid) ->
+ ssl_connection:session_info(Pid);
+session_info(#sslsocket{pid = {Listen,_}}) when is_port(Listen) ->
+ {error, enotconn}.
%%---------------------------------------------------------------
-spec versions() -> [{ssl_app, string()} | {supported, [tls_atom_version()]} |
- {available, [tls_atom_version()]}].
+ {available, [tls_atom_version()]}].
%%
%% Description: Returns a list of relevant versions.
%%--------------------------------------------------------------------
versions() ->
Vsns = ssl_record:supported_protocol_versions(),
SupportedVsns = [ssl_record:protocol_version(Vsn) || Vsn <- Vsns],
- AvailableVsns = ?DEFAULT_SUPPORTED_VERSIONS,
+ AvailableVsns = ?ALL_SUPPORTED_VERSIONS,
[{ssl_app, ?VSN}, {supported, SupportedVsns}, {available, AvailableVsns}].
@@ -417,8 +455,10 @@ versions() ->
%%
%% Description: Initiates a renegotiation.
%%--------------------------------------------------------------------
-renegotiate(#sslsocket{pid = Pid, fd = new_ssl}) ->
- ssl_connection:renegotiation(Pid).
+renegotiate(#sslsocket{pid = Pid}) when is_pid(Pid) ->
+ ssl_connection:renegotiation(Pid);
+renegotiate(#sslsocket{pid = {Listen,_}}) when is_port(Listen) ->
+ {error, enotconn}.
%%--------------------------------------------------------------------
-spec prf(#sslsocket{}, binary() | 'master_secret', binary(),
@@ -427,10 +467,11 @@ renegotiate(#sslsocket{pid = Pid, fd = new_ssl}) ->
%%
%% Description: use a ssl sessions TLS PRF to generate key material
%%--------------------------------------------------------------------
-prf(#sslsocket{pid = Pid, fd = new_ssl},
- Secret, Label, Seed, WantedLength) ->
- ssl_connection:prf(Pid, Secret, Label, Seed, WantedLength).
-
+prf(#sslsocket{pid = Pid},
+ Secret, Label, Seed, WantedLength) when is_pid(Pid) ->
+ ssl_connection:prf(Pid, Secret, Label, Seed, WantedLength);
+prf(#sslsocket{pid = {Listen,_}}, _,_,_,_) when is_port(Listen) ->
+ {error, enotconn}.
%%--------------------------------------------------------------------
-spec clear_pem_cache() -> ok.
@@ -474,6 +515,23 @@ format_error(Error) ->
Other
end.
+%%--------------------------------------------------------------------
+-spec random_bytes(integer()) -> binary().
+
+%%
+%% Description: Generates cryptographically secure random sequence if possible
+%% fallbacks on pseudo random function
+%%--------------------------------------------------------------------
+random_bytes(N) ->
+ try crypto:strong_rand_bytes(N) of
+ RandBytes ->
+ RandBytes
+ catch
+ error:low_entropy ->
+ crypto:rand_bytes(N)
+ end.
+
+
%%%--------------------------------------------------------------
%%% Internal functions
%%%--------------------------------------------------------------------
@@ -567,7 +625,9 @@ handle_options(Opts0, _Role) ->
renegotiate_at = handle_option(renegotiate_at, Opts, ?DEFAULT_RENEGOTIATE_AT),
debug = handle_option(debug, Opts, []),
hibernate_after = handle_option(hibernate_after, Opts, undefined),
- erl_dist = handle_option(erl_dist, Opts, false)
+ erl_dist = handle_option(erl_dist, Opts, false),
+ next_protocols_advertised = handle_option(next_protocols_advertised, Opts, undefined),
+ next_protocol_selector = make_next_protocol_selector(handle_option(client_preferred_next_protocols, Opts, undefined))
},
CbInfo = proplists:get_value(cb_info, Opts, {gen_tcp, tcp, tcp_closed, tcp_error}),
@@ -576,7 +636,8 @@ handle_options(Opts0, _Role) ->
depth, cert, certfile, key, keyfile,
password, cacerts, cacertfile, dh, dhfile, ciphers,
debug, reuse_session, reuse_sessions, ssl_imp,
- cb_info, renegotiate_at, secure_renegotiate, hibernate_after, erl_dist],
+ cb_info, renegotiate_at, secure_renegotiate, hibernate_after, erl_dist, next_protocols_advertised,
+ client_preferred_next_protocols],
SockOpts = lists:foldl(fun(Key, PropList) ->
proplists:delete(Key, PropList)
@@ -701,15 +762,68 @@ validate_option(hibernate_after, undefined) ->
undefined;
validate_option(hibernate_after, Value) when is_integer(Value), Value >= 0 ->
Value;
-validate_option(erl_dist,Value) when Value == true;
+validate_option(erl_dist,Value) when Value == true;
Value == false ->
Value;
+validate_option(client_preferred_next_protocols = Opt, {Precedence, PreferredProtocols} = Value)
+ when is_list(PreferredProtocols) ->
+ case ssl_record:highest_protocol_version([]) of
+ {3,0} ->
+ throw({error, {eoptions, {not_supported_in_sslv3, {Opt, Value}}}});
+ _ ->
+ validate_binary_list(client_preferred_next_protocols, PreferredProtocols),
+ validate_npn_ordering(Precedence),
+ {Precedence, PreferredProtocols, ?NO_PROTOCOL}
+ end;
+validate_option(client_preferred_next_protocols = Opt, {Precedence, PreferredProtocols, Default} = Value)
+ when is_list(PreferredProtocols), is_binary(Default),
+ byte_size(Default) > 0, byte_size(Default) < 256 ->
+ case ssl_record:highest_protocol_version([]) of
+ {3,0} ->
+ throw({error, {eoptions, {not_supported_in_sslv3, {Opt, Value}}}});
+ _ ->
+ validate_binary_list(client_preferred_next_protocols, PreferredProtocols),
+ validate_npn_ordering(Precedence),
+ Value
+ end;
+
+validate_option(client_preferred_next_protocols, undefined) ->
+ undefined;
+validate_option(next_protocols_advertised = Opt, Value) when is_list(Value) ->
+ case ssl_record:highest_protocol_version([]) of
+ {3,0} ->
+ throw({error, {eoptions, {not_supported_in_sslv3, {Opt, Value}}}});
+ _ ->
+ validate_binary_list(next_protocols_advertised, Value),
+ Value
+ end;
+
+validate_option(next_protocols_advertised, undefined) ->
+ undefined;
validate_option(Opt, Value) ->
throw({error, {eoptions, {Opt, Value}}}).
-
+
+validate_npn_ordering(client) ->
+ ok;
+validate_npn_ordering(server) ->
+ ok;
+validate_npn_ordering(Value) ->
+ throw({error, {eoptions, {client_preferred_next_protocols, {invalid_precedence, Value}}}}).
+
+validate_binary_list(Opt, List) ->
+ lists:foreach(
+ fun(Bin) when is_binary(Bin),
+ byte_size(Bin) > 0,
+ byte_size(Bin) < 256 ->
+ ok;
+ (Bin) ->
+ throw({error, {eoptions, {Opt, {invalid_protocol, Bin}}}})
+ end, List).
+
validate_versions([], Versions) ->
Versions;
-validate_versions([Version | Rest], Versions) when Version == 'tlsv1.1';
+validate_versions([Version | Rest], Versions) when Version == 'tlsv1.2';
+ Version == 'tlsv1.1';
Version == tlsv1;
Version == sslv3 ->
validate_versions(Rest, Versions);
@@ -754,10 +868,10 @@ internal_inet_values() ->
socket_options(InetValues) ->
#socket_options{
- mode = proplists:get_value(mode, InetValues),
- header = proplists:get_value(header, InetValues),
- active = proplists:get_value(active, InetValues),
- packet = proplists:get_value(packet, InetValues),
+ mode = proplists:get_value(mode, InetValues, lists),
+ header = proplists:get_value(header, InetValues, 0),
+ active = proplists:get_value(active, InetValues, active),
+ packet = proplists:get_value(packet, InetValues, 0),
packet_size = proplists:get_value(packet_size, InetValues)
}.
@@ -811,14 +925,31 @@ cipher_suites(Version, Ciphers0) ->
no_format(Error) ->
lists:flatten(io_lib:format("No format string for error: \"~p\" available.", [Error])).
-
-%% Only used to remove exit messages from old ssl
-%% First is a nonsense clause to provide some
-%% backward compatibility for orber that uses this
-%% function in a none recommended way, but will
-%% work correctly if a valid pid is returned.
-%% Deprcated to be removed in r16
-pid(#sslsocket{fd = new_ssl}) ->
- whereis(ssl_connection_sup);
-pid(#sslsocket{pid = Pid}) ->
- Pid.
+
+detect(_Pred, []) ->
+ undefined;
+detect(Pred, [H|T]) ->
+ case Pred(H) of
+ true ->
+ H;
+ _ ->
+ detect(Pred, T)
+ end.
+
+make_next_protocol_selector(undefined) ->
+ undefined;
+make_next_protocol_selector({client, AllProtocols, DefaultProtocol}) ->
+ fun(AdvertisedProtocols) ->
+ case detect(fun(PreferredProtocol) -> lists:member(PreferredProtocol, AdvertisedProtocols) end, AllProtocols) of
+ undefined -> DefaultProtocol;
+ PreferredProtocol -> PreferredProtocol
+ end
+ end;
+
+make_next_protocol_selector({server, AllProtocols, DefaultProtocol}) ->
+ fun(AdvertisedProtocols) ->
+ case detect(fun(PreferredProtocol) -> lists:member(PreferredProtocol, AllProtocols) end, AdvertisedProtocols) of
+ undefined -> DefaultProtocol;
+ PreferredProtocol -> PreferredProtocol
+ end
+ end.
diff --git a/lib/ssl/src/ssl_alert.erl b/lib/ssl/src/ssl_alert.erl
index eb1228afa4..f94a1136a0 100644
--- a/lib/ssl/src/ssl_alert.erl
+++ b/lib/ssl/src/ssl_alert.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -36,8 +36,7 @@
%% Internal application API
%%====================================================================
%%--------------------------------------------------------------------
--spec reason_code(#alert{}, client | server) -> closed | esslconnect |
- esslaccept | string().
+-spec reason_code(#alert{}, client | server) -> closed | {essl, string()}.
%%
%% Description: Returns the error reason that will be returned to the
%% user.
@@ -45,12 +44,8 @@
reason_code(#alert{description = ?CLOSE_NOTIFY}, _) ->
closed;
-reason_code(#alert{description = ?HANDSHAKE_FAILURE}, client) ->
- esslconnect;
-reason_code(#alert{description = ?HANDSHAKE_FAILURE}, server) ->
- esslaccept;
reason_code(#alert{description = Description}, _) ->
- description_txt(Description).
+ {essl, description_txt(Description)}.
%%--------------------------------------------------------------------
-spec alert_txt(#alert{}) -> string().
@@ -84,6 +79,8 @@ description_txt(?DECOMPRESSION_FAILURE) ->
"decompression failure";
description_txt(?HANDSHAKE_FAILURE) ->
"handshake failure";
+description_txt(?NO_CERTIFICATE_RESERVED) ->
+ "No certificate reserved";
description_txt(?BAD_CERTIFICATE) ->
"bad certificate";
description_txt(?UNSUPPORTED_CERTIFICATE) ->
diff --git a/lib/ssl/src/ssl_alert.hrl b/lib/ssl/src/ssl_alert.hrl
index 6470b82d50..92548edab7 100644
--- a/lib/ssl/src/ssl_alert.hrl
+++ b/lib/ssl/src/ssl_alert.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -43,6 +43,7 @@
%% record_overflow(22),
%% decompression_failure(30),
%% handshake_failure(40),
+%% no_certificate_RESERVED(41), %% Only sslv3
%% bad_certificate(42),
%% unsupported_certificate(43),
%% certificate_revoked(44),
@@ -69,6 +70,7 @@
-define(RECORD_OVERFLOW, 22).
-define(DECOMPRESSION_FAILURE, 30).
-define(HANDSHAKE_FAILURE, 40).
+-define(NO_CERTIFICATE_RESERVED, 41).
-define(BAD_CERTIFICATE, 42).
-define(UNSUPPORTED_CERTIFICATE, 43).
-define(CERTIFICATE_REVOKED, 44).
diff --git a/lib/ssl/src/ssl_certificate.erl b/lib/ssl/src/ssl_certificate.erl
index 0931b86782..86f5617b54 100644
--- a/lib/ssl/src/ssl_certificate.erl
+++ b/lib/ssl/src/ssl_certificate.erl
@@ -103,7 +103,7 @@ certificate_chain(OwnCert, CertDbHandle, CertsDbRef) ->
ErlCert = public_key:pkix_decode_cert(OwnCert, otp),
certificate_chain(ErlCert, OwnCert, CertDbHandle, CertsDbRef, [OwnCert]).
%%--------------------------------------------------------------------
--spec file_to_certificats(string(), term()) -> [der_cert()].
+-spec file_to_certificats(binary(), term()) -> [der_cert()].
%%
%% Description: Return list of DER encoded certificates.
%%--------------------------------------------------------------------
@@ -172,7 +172,12 @@ extensions_list(Extensions) ->
%% Description:
%%--------------------------------------------------------------------
signature_type(RSA) when RSA == ?sha1WithRSAEncryption;
- RSA == ?md5WithRSAEncryption ->
+ RSA == ?md5WithRSAEncryption;
+ RSA == ?sha224WithRSAEncryption;
+ RSA == ?sha256WithRSAEncryption;
+ RSA == ?sha384WithRSAEncryption;
+ RSA == ?sha512WithRSAEncryption
+ ->
rsa;
signature_type(?'id-dsa-with-sha1') ->
dsa.
diff --git a/lib/ssl/src/ssl_certificate_db.erl b/lib/ssl/src/ssl_certificate_db.erl
index 01ddf056c9..67d00f0da7 100644
--- a/lib/ssl/src/ssl_certificate_db.erl
+++ b/lib/ssl/src/ssl_certificate_db.erl
@@ -106,7 +106,7 @@ add_trusted_certs(_Pid, File, [CertsDb, RefDb, PemChache] = Db) ->
{ok, Ref};
[Content] ->
Ref = make_ref(),
- insert(Ref, [], 1, RefDb),
+ update_counter(Ref, 1, RefDb),
insert(MD5, {Content, Ref}, PemChache),
add_certs_from_pem(Content, Ref, CertsDb),
{ok, Ref};
@@ -114,8 +114,8 @@ add_trusted_certs(_Pid, File, [CertsDb, RefDb, PemChache] = Db) ->
new_trusted_cert_entry({MD5, File}, Db)
end.
%%--------------------------------------------------------------------
--spec cache_pem_file(string(), [db_handle()]) -> term().
--spec cache_pem_file(reference(), string(), [db_handle()]) -> term().
+-spec cache_pem_file({binary(), binary()}, [db_handle()]) -> term().
+-spec cache_pem_file(reference(), {binary(), binary()}, [db_handle()]) -> term().
%%
%% Description: Cache file as binary in DB
%%--------------------------------------------------------------------
@@ -204,10 +204,8 @@ insert(Key, Data, Db) ->
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
-insert(Key, [], Count, Db) ->
- true = ets:insert(Db, {Key, Count});
-insert(Key, Data, Count, Db) ->
- true = ets:insert(Db, {Key, Count, Data}).
+update_counter(Key, Count, Db) ->
+ true = ets:insert(Db, {Key, Count}).
remove_certs(Ref, CertsDb) ->
ets:match_delete(CertsDb, {{Ref, '_', '_'}, '_'}).
@@ -236,7 +234,7 @@ add_certs(Cert, Ref, CertsDb) ->
new_trusted_cert_entry(FileRef, [CertsDb, RefDb, _] = Db) ->
Ref = make_ref(),
- insert(Ref, [], 1, RefDb),
+ update_counter(Ref, 1, RefDb),
{ok, Content} = cache_pem_file(Ref, FileRef, Db),
add_certs_from_pem(Content, Ref, CertsDb),
{ok, Ref}.
diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl
index d43d312be8..567690a413 100644
--- a/lib/ssl/src/ssl_cipher.erl
+++ b/lib/ssl/src/ssl_cipher.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -28,25 +28,27 @@
-include("ssl_internal.hrl").
-include("ssl_record.hrl").
-include("ssl_cipher.hrl").
+-include("ssl_handshake.hrl").
-include("ssl_alert.hrl").
-include_lib("public_key/include/public_key.hrl").
--export([security_parameters/2, suite_definition/1,
- decipher/5, cipher/4,
+-export([security_parameters/3, suite_definition/1,
+ decipher/5, cipher/5,
suite/1, suites/1, anonymous_suites/0,
- openssl_suite/1, openssl_suite_name/1, filter/2]).
+ openssl_suite/1, openssl_suite_name/1, filter/2,
+ hash_algorithm/1, sign_algorithm/1]).
-compile(inline).
%%--------------------------------------------------------------------
--spec security_parameters(cipher_suite(), #security_parameters{}) ->
+-spec security_parameters(tls_version(), 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} = suite_definition(CipherSuite),
+security_parameters(Version, CipherSuite, SecParams) ->
+ { _, Cipher, Hash, PrfHashAlg} = suite_definition(CipherSuite),
SecParams#security_parameters{
cipher_suite = CipherSuite,
bulk_cipher_algorithm = bulk_cipher_algorithm(Cipher),
@@ -55,20 +57,21 @@ security_parameters(CipherSuite, SecParams) ->
expanded_key_material_length = expanded_key_material(Cipher),
key_material_length = key_material(Cipher),
iv_size = iv_size(Cipher),
- mac_algorithm = mac_algorithm(Hash),
+ mac_algorithm = hash_algorithm(Hash),
+ prf_algorithm = prf_algorithm(PrfHashAlg, Version),
hash_size = hash_size(Hash)}.
%%--------------------------------------------------------------------
--spec cipher(cipher_enum(), #cipher_state{}, binary(), binary()) ->
+-spec cipher(cipher_enum(), #cipher_state{}, binary(), binary(), tls_version()) ->
{binary(), #cipher_state{}}.
%%
%% Description: Encrypts the data and the MAC using chipher described
%% by cipher_enum() and updating the cipher state
%%-------------------------------------------------------------------
-cipher(?NULL, CipherState, <<>>, Fragment) ->
+cipher(?NULL, CipherState, <<>>, Fragment, _Version) ->
GenStreamCipherList = [Fragment, <<>>],
{GenStreamCipherList, CipherState};
-cipher(?RC4, CipherState, Mac, Fragment) ->
+cipher(?RC4, CipherState, Mac, Fragment, _Version) ->
State0 = case CipherState#cipher_state.state of
undefined -> crypto:rc4_set_key(CipherState#cipher_state.key);
S -> S
@@ -76,32 +79,41 @@ cipher(?RC4, CipherState, Mac, Fragment) ->
GenStreamCipherList = [Fragment, Mac],
{State1, T} = crypto:rc4_encrypt_with_state(State0, GenStreamCipherList),
{T, CipherState#cipher_state{state = State1}};
-cipher(?DES, CipherState, Mac, Fragment) ->
+cipher(?DES, CipherState, Mac, Fragment, Version) ->
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) ->
+ end, block_size(des_cbc), CipherState, Mac, Fragment, Version);
+cipher(?'3DES', CipherState, Mac, Fragment, Version) ->
block_cipher(fun(<<K1:8/binary, K2:8/binary, K3:8/binary>>, IV, T) ->
crypto:des3_cbc_encrypt(K1, K2, K3, IV, T)
- end, block_size(des_cbc), CipherState, Mac, Fragment);
-cipher(?AES, CipherState, Mac, Fragment) ->
+ end, block_size(des_cbc), CipherState, Mac, Fragment, Version);
+cipher(?AES, CipherState, Mac, Fragment, Version) ->
block_cipher(fun(Key, IV, T) when byte_size(Key) =:= 16 ->
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).
-%% 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);
-
-block_cipher(Fun, BlockSz, #cipher_state{key=Key, iv=IV} = CS0,
- Mac, Fragment) ->
+ end, block_size(aes_128_cbc), CipherState, Mac, Fragment, Version).
+
+build_cipher_block(BlockSz, Mac, Fragment) ->
TotSz = byte_size(Mac) + erlang:iolist_size(Fragment) + 1,
{PaddingLength, Padding} = get_padding(TotSz, BlockSz),
- L = [Fragment, Mac, PaddingLength, Padding],
+ [Fragment, Mac, PaddingLength, Padding].
+
+block_cipher(Fun, BlockSz, #cipher_state{key=Key, iv=IV} = CS0,
+ Mac, Fragment, {3, N})
+ when N == 0; N == 1 ->
+ L = build_cipher_block(BlockSz, Mac, Fragment),
T = Fun(Key, IV, L),
NextIV = next_iv(T, IV),
+ {T, CS0#cipher_state{iv=NextIV}};
+
+block_cipher(Fun, BlockSz, #cipher_state{key=Key, iv=IV} = CS0,
+ Mac, Fragment, {3, N})
+ when N == 2; N == 3 ->
+ NextIV = random_iv(IV),
+ L0 = build_cipher_block(BlockSz, Mac, Fragment),
+ L = [NextIV|L0],
+ T = Fun(Key, IV, L),
{T, CS0#cipher_state{iv=NextIV}}.
%%--------------------------------------------------------------------
@@ -147,19 +159,16 @@ decipher(?AES, HashSz, CipherState, Fragment, Version) ->
(Key, IV, T) when byte_size(Key) =:= 32 ->
crypto:aes_cbc_256_decrypt(Key, IV, T)
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, Version);
block_decipher(Fun, #cipher_state{key=Key, iv=IV} = CipherState0,
HashSz, Fragment, Version) ->
try
Text = Fun(Key, IV, Fragment),
- GBC = generic_block_cipher_from_bin(Text, HashSz),
+ NextIV = next_iv(Fragment, IV),
+ GBC = generic_block_cipher_from_bin(Version, Text, NextIV, HashSz),
Content = GBC#generic_block_cipher.content,
Mac = GBC#generic_block_cipher.mac,
- CipherState1 = CipherState0#cipher_state{iv=next_iv(Fragment, IV)},
+ CipherState1 = CipherState0#cipher_state{iv=GBC#generic_block_cipher.next_iv},
case is_correct_padding(GBC, Version) of
true ->
{Content, Mac, CipherState1};
@@ -187,8 +196,8 @@ block_decipher(Fun, #cipher_state{key=Key, iv=IV} = CipherState0,
%%--------------------------------------------------------------------
suites({3, 0}) ->
ssl_ssl3:suites();
-suites({3, N}) when N == 1; N == 2 ->
- ssl_tls1:suites().
+suites({3, N}) ->
+ ssl_tls1:suites(N).
%%--------------------------------------------------------------------
-spec anonymous_suites() -> [cipher_suite()].
@@ -201,10 +210,12 @@ anonymous_suites() ->
?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].
+ ?TLS_DH_anon_WITH_AES_256_CBC_SHA,
+ ?TLS_DH_anon_WITH_AES_128_CBC_SHA256,
+ ?TLS_DH_anon_WITH_AES_256_CBC_SHA256].
%%--------------------------------------------------------------------
--spec suite_definition(cipher_suite()) -> erl_cipher_suite().
+-spec suite_definition(cipher_suite()) -> int_cipher_suite().
%%
%% Description: Return erlang cipher suite definition.
%% Note: Currently not supported suites are commented away.
@@ -212,56 +223,81 @@ anonymous_suites() ->
%%-------------------------------------------------------------------
%% TLS v1.1 suites
suite_definition(?TLS_NULL_WITH_NULL_NULL) ->
- {null, null, null};
+ {null, null, null, null};
%% suite_definition(?TLS_RSA_WITH_NULL_MD5) ->
-%% {rsa, null, md5};
+%% {rsa, null, md5, default_prf};
%% suite_definition(?TLS_RSA_WITH_NULL_SHA) ->
-%% {rsa, null, sha};
+%% {rsa, null, sha, default_prf};
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};
+ {rsa, rc4_128, md5, default_prf};
+suite_definition(?TLS_RSA_WITH_RC4_128_SHA) ->
+ {rsa, rc4_128, sha, default_prf};
+suite_definition(?TLS_RSA_WITH_DES_CBC_SHA) ->
+ {rsa, des_cbc, sha, default_prf};
suite_definition(?TLS_RSA_WITH_3DES_EDE_CBC_SHA) ->
- {rsa, '3des_ede_cbc', sha};
+ {rsa, '3des_ede_cbc', sha, default_prf};
suite_definition(?TLS_DHE_DSS_WITH_DES_CBC_SHA) ->
- {dhe_dss, des_cbc, sha};
+ {dhe_dss, des_cbc, sha, default_prf};
suite_definition(?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA) ->
- {dhe_dss, '3des_ede_cbc', sha};
+ {dhe_dss, '3des_ede_cbc', sha, default_prf};
suite_definition(?TLS_DHE_RSA_WITH_DES_CBC_SHA) ->
- {dhe_rsa, des_cbc, sha};
+ {dhe_rsa, des_cbc, sha, default_prf};
suite_definition(?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA) ->
- {dhe_rsa, '3des_ede_cbc', sha};
+ {dhe_rsa, '3des_ede_cbc', sha, default_prf};
%%% TSL V1.1 AES suites
suite_definition(?TLS_RSA_WITH_AES_128_CBC_SHA) ->
- {rsa, aes_128_cbc, sha};
+ {rsa, aes_128_cbc, sha, default_prf};
suite_definition(?TLS_DHE_DSS_WITH_AES_128_CBC_SHA) ->
- {dhe_dss, aes_128_cbc, sha};
+ {dhe_dss, aes_128_cbc, sha, default_prf};
suite_definition(?TLS_DHE_RSA_WITH_AES_128_CBC_SHA) ->
- {dhe_rsa, aes_128_cbc, sha};
+ {dhe_rsa, aes_128_cbc, sha, default_prf};
suite_definition(?TLS_RSA_WITH_AES_256_CBC_SHA) ->
- {rsa, aes_256_cbc, sha};
+ {rsa, aes_256_cbc, sha, default_prf};
suite_definition(?TLS_DHE_DSS_WITH_AES_256_CBC_SHA) ->
- {dhe_dss, aes_256_cbc, sha};
+ {dhe_dss, aes_256_cbc, sha, default_prf};
suite_definition(?TLS_DHE_RSA_WITH_AES_256_CBC_SHA) ->
- {dhe_rsa, aes_256_cbc, sha};
+ {dhe_rsa, aes_256_cbc, sha, default_prf};
+
+%% TLS v1.2 suites
+
+%% suite_definition(?TLS_RSA_WITH_NULL_SHA) ->
+%% {rsa, null, sha, default_prf};
+suite_definition(?TLS_RSA_WITH_AES_128_CBC_SHA256) ->
+ {rsa, aes_128_cbc, sha256, default_prf};
+suite_definition(?TLS_RSA_WITH_AES_256_CBC_SHA256) ->
+ {rsa, aes_256_cbc, sha256, default_prf};
+suite_definition(?TLS_DHE_DSS_WITH_AES_128_CBC_SHA256) ->
+ {dhe_dss, aes_128_cbc, sha256, default_prf};
+suite_definition(?TLS_DHE_RSA_WITH_AES_128_CBC_SHA256) ->
+ {dhe_rsa, aes_128_cbc, sha256, default_prf};
+suite_definition(?TLS_DHE_DSS_WITH_AES_256_CBC_SHA256) ->
+ {dhe_dss, aes_256_cbc, sha256, default_prf};
+suite_definition(?TLS_DHE_RSA_WITH_AES_256_CBC_SHA256) ->
+ {dhe_rsa, aes_256_cbc, sha256, default_prf};
+
+%% not defined YET:
+%% TLS_DH_DSS_WITH_AES_128_CBC_SHA256 DH_DSS AES_128_CBC SHA256
+%% TLS_DH_RSA_WITH_AES_128_CBC_SHA256 DH_RSA AES_128_CBC SHA256
+%% TLS_DH_DSS_WITH_AES_256_CBC_SHA256 DH_DSS AES_256_CBC SHA256
+%% TLS_DH_RSA_WITH_AES_256_CBC_SHA256 DH_RSA AES_256_CBC SHA256
%%% 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};
+ {dh_anon, rc4_128, md5, default_prf};
suite_definition(?TLS_DH_anon_WITH_DES_CBC_SHA) ->
- {dh_anon, des_cbc, sha};
+ {dh_anon, des_cbc, sha, default_prf};
suite_definition(?TLS_DH_anon_WITH_3DES_EDE_CBC_SHA) ->
- {dh_anon, '3des_ede_cbc', sha};
+ {dh_anon, '3des_ede_cbc', sha, default_prf};
suite_definition(?TLS_DH_anon_WITH_AES_128_CBC_SHA) ->
- {dh_anon, aes_128_cbc, sha};
+ {dh_anon, aes_128_cbc, sha, default_prf};
suite_definition(?TLS_DH_anon_WITH_AES_256_CBC_SHA) ->
- {dh_anon, aes_256_cbc, sha}.
+ {dh_anon, aes_256_cbc, sha, default_prf};
+suite_definition(?TLS_DH_anon_WITH_AES_128_CBC_SHA256) ->
+ {dh_anon, aes_128_cbc, sha256, default_prf};
+suite_definition(?TLS_DH_anon_WITH_AES_256_CBC_SHA256) ->
+ {dh_anon, aes_256_cbc, sha256, default_prf}.
%%--------------------------------------------------------------------
-spec suite(erl_cipher_suite()) -> cipher_suite().
@@ -278,8 +314,6 @@ suite({rsa, rc4_128, md5}) ->
?TLS_RSA_WITH_RC4_128_MD5;
suite({rsa, rc4_128, sha}) ->
?TLS_RSA_WITH_RC4_128_SHA;
-%% suite({rsa, idea_cbc, sha}) ->
-%% ?TLS_RSA_WITH_IDEA_CBC_SHA;
suite({rsa, des_cbc, sha}) ->
?TLS_RSA_WITH_DES_CBC_SHA;
suite({rsa, '3des_ede_cbc', sha}) ->
@@ -315,7 +349,28 @@ suite({dhe_dss, aes_256_cbc, sha}) ->
suite({dhe_rsa, aes_256_cbc, sha}) ->
?TLS_DHE_RSA_WITH_AES_256_CBC_SHA;
suite({dh_anon, aes_256_cbc, sha}) ->
- ?TLS_DH_anon_WITH_AES_256_CBC_SHA.
+ ?TLS_DH_anon_WITH_AES_256_CBC_SHA;
+
+%% TLS v1.2 suites
+
+%% suite_definition(?TLS_RSA_WITH_NULL_SHA) ->
+%% {rsa, null, sha, sha256};
+suite({rsa, aes_128_cbc, sha256}) ->
+ ?TLS_RSA_WITH_AES_128_CBC_SHA256;
+suite({rsa, aes_256_cbc, sha256}) ->
+ ?TLS_RSA_WITH_AES_256_CBC_SHA256;
+suite({dhe_dss, aes_128_cbc, sha256}) ->
+ ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA256;
+suite({dhe_rsa, aes_128_cbc, sha256}) ->
+ ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA256;
+suite({dhe_dss, aes_256_cbc, sha256}) ->
+ ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA256;
+suite({dhe_rsa, aes_256_cbc, sha256}) ->
+ ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA256;
+suite({dh_anon, aes_128_cbc, sha256}) ->
+ ?TLS_DH_anon_WITH_AES_128_CBC_SHA256;
+suite({dh_anon, aes_256_cbc, sha256}) ->
+ ?TLS_DH_anon_WITH_AES_256_CBC_SHA256.
%%--------------------------------------------------------------------
-spec openssl_suite(openssl_cipher_suite()) -> cipher_suite().
@@ -323,6 +378,18 @@ suite({dh_anon, aes_256_cbc, sha}) ->
%% Description: Return TLS cipher suite definition.
%%--------------------------------------------------------------------
%% translate constants <-> openssl-strings
+openssl_suite("DHE-RSA-AES256-SHA256") ->
+ ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA256;
+openssl_suite("DHE-DSS-AES256-SHA256") ->
+ ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA256;
+openssl_suite("AES256-SHA256") ->
+ ?TLS_RSA_WITH_AES_256_CBC_SHA256;
+openssl_suite("DHE-RSA-AES128-SHA256") ->
+ ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA256;
+openssl_suite("DHE-DSS-AES128-SHA256") ->
+ ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA256;
+openssl_suite("AES128-SHA256") ->
+ ?TLS_RSA_WITH_AES_128_CBC_SHA256;
openssl_suite("DHE-RSA-AES256-SHA") ->
?TLS_DHE_RSA_WITH_AES_256_CBC_SHA;
openssl_suite("DHE-DSS-AES256-SHA") ->
@@ -341,8 +408,6 @@ 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;
-%%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") ->
@@ -374,8 +439,6 @@ openssl_suite_name(?TLS_DHE_DSS_WITH_AES_128_CBC_SHA) ->
"DHE-DSS-AES128-SHA";
openssl_suite_name(?TLS_RSA_WITH_AES_128_CBC_SHA) ->
"AES128-SHA";
-%% openssl_suite_name(?TLS_RSA_WITH_IDEA_CBC_SHA) ->
-%% "IDEA-CBC-SHA";
openssl_suite_name(?TLS_RSA_WITH_RC4_128_SHA) ->
"RC4-SHA";
openssl_suite_name(?TLS_RSA_WITH_RC4_128_MD5) ->
@@ -384,6 +447,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_RSA_WITH_NULL_SHA256) ->
+ "NULL-SHA256";
+openssl_suite_name(?TLS_RSA_WITH_AES_128_CBC_SHA256) ->
+ "AES128-SHA256";
+openssl_suite_name(?TLS_RSA_WITH_AES_256_CBC_SHA256) ->
+ "AES256-SHA256";
+openssl_suite_name(?TLS_DH_DSS_WITH_AES_128_CBC_SHA256) ->
+ "DH-DSS-AES128-SHA256";
+openssl_suite_name(?TLS_DH_RSA_WITH_AES_128_CBC_SHA256) ->
+ "DH-RSA-AES128-SHA256";
+openssl_suite_name(?TLS_DHE_DSS_WITH_AES_128_CBC_SHA256) ->
+ "DHE-DSS-AES128-SHA256";
+openssl_suite_name(?TLS_DHE_RSA_WITH_AES_128_CBC_SHA256) ->
+ "DHE-RSA-AES128-SHA256";
+openssl_suite_name(?TLS_DH_DSS_WITH_AES_256_CBC_SHA256) ->
+ "DH-DSS-AES256-SHA256";
+openssl_suite_name(?TLS_DH_RSA_WITH_AES_256_CBC_SHA256) ->
+ "DH-RSA-AES256-SHA256";
+openssl_suite_name(?TLS_DHE_DSS_WITH_AES_256_CBC_SHA256) ->
+ "DHE-DSS-AES256-SHA256";
+openssl_suite_name(?TLS_DHE_RSA_WITH_AES_256_CBC_SHA256) ->
+ "DHE-RSA-AES256-SHA256";
%% No oppenssl name
openssl_suite_name(Cipher) ->
suite_definition(Cipher).
@@ -411,9 +496,6 @@ filter(DerCert, Ciphers) ->
bulk_cipher_algorithm(null) ->
?NULL;
-%% Not supported yet
-%% bulk_cipher_algorithm(idea_cbc) ->
-%% ?IDEA;
bulk_cipher_algorithm(rc4_128) ->
?RC4;
bulk_cipher_algorithm(des_cbc) ->
@@ -428,8 +510,7 @@ type(Cipher) when Cipher == null;
Cipher == rc4_128 ->
?STREAM;
-type(Cipher) when Cipher == idea_cbc;
- Cipher == des_cbc;
+type(Cipher) when Cipher == des_cbc;
Cipher == '3des_ede_cbc';
Cipher == aes_128_cbc;
Cipher == aes_256_cbc ->
@@ -437,8 +518,7 @@ type(Cipher) when Cipher == idea_cbc;
key_material(null) ->
0;
-key_material(Cipher) when Cipher == idea_cbc;
- Cipher == rc4_128 ->
+key_material(rc4_128) ->
16;
key_material(des_cbc) ->
8;
@@ -451,8 +531,7 @@ key_material(aes_256_cbc) ->
expanded_key_material(null) ->
0;
-expanded_key_material(Cipher) when Cipher == idea_cbc;
- Cipher == rc4_128 ->
+expanded_key_material(rc4_128) ->
16;
expanded_key_material(Cipher) when Cipher == des_cbc ->
8;
@@ -467,8 +546,7 @@ effective_key_bits(null) ->
0;
effective_key_bits(des_cbc) ->
56;
-effective_key_bits(Cipher) when Cipher == idea_cbc;
- Cipher == rc4_128;
+effective_key_bits(Cipher) when Cipher == rc4_128;
Cipher == aes_128_cbc ->
128;
effective_key_bits('3des_ede_cbc') ->
@@ -482,8 +560,7 @@ iv_size(Cipher) when Cipher == null;
iv_size(Cipher) ->
block_size(Cipher).
-block_size(Cipher) when Cipher == idea_cbc;
- Cipher == des_cbc;
+block_size(Cipher) when Cipher == des_cbc;
Cipher == '3des_ede_cbc' ->
8;
@@ -491,19 +568,51 @@ block_size(Cipher) when Cipher == aes_128_cbc;
Cipher == aes_256_cbc ->
16.
-mac_algorithm(null) ->
- ?NULL;
-mac_algorithm(md5) ->
- ?MD5;
-mac_algorithm(sha) ->
- ?SHA.
+prf_algorithm(default_prf, {3, N}) when N >= 3 ->
+ ?SHA256;
+prf_algorithm(default_prf, {3, _}) ->
+ ?MD5SHA;
+prf_algorithm(Algo, _) ->
+ hash_algorithm(Algo).
+
+hash_algorithm(null) -> ?NULL;
+hash_algorithm(md5) -> ?MD5;
+hash_algorithm(sha) -> ?SHA; %% Only sha always refers to "SHA-1"
+hash_algorithm(sha224) -> ?SHA224;
+hash_algorithm(sha256) -> ?SHA256;
+hash_algorithm(sha384) -> ?SHA384;
+hash_algorithm(sha512) -> ?SHA512;
+hash_algorithm(?NULL) -> null;
+hash_algorithm(?MD5) -> md5;
+hash_algorithm(?SHA) -> sha;
+hash_algorithm(?SHA224) -> sha224;
+hash_algorithm(?SHA256) -> sha256;
+hash_algorithm(?SHA384) -> sha384;
+hash_algorithm(?SHA512) -> sha512.
+
+sign_algorithm(anon) -> ?ANON;
+sign_algorithm(rsa) -> ?RSA;
+sign_algorithm(dsa) -> ?DSA;
+sign_algorithm(ecdsa) -> ?ECDSA;
+sign_algorithm(?ANON) -> anon;
+sign_algorithm(?RSA) -> rsa;
+sign_algorithm(?DSA) -> dsa;
+sign_algorithm(?ECDSA) -> ecdsa.
hash_size(null) ->
0;
hash_size(md5) ->
16;
hash_size(sha) ->
- 20.
+ 20;
+hash_size(sha256) ->
+ 32.
+%% Currently no supported cipher suites defaults to sha384 or sha512
+%% so these clauses are not needed at the moment.
+%% hash_size(sha384) ->
+%% 48;
+%% hash_size(sha512) ->
+%% 64.
%% RFC 5246: 6.2.3.2. CBC Block Cipher
%%
@@ -525,7 +634,8 @@ hash_size(sha) ->
%% We return the original (possibly invalid) PadLength in any case.
%% An invalid PadLength will be caught by is_correct_padding/2
%%
-generic_block_cipher_from_bin(T, HashSize) ->
+generic_block_cipher_from_bin({3, N}, T, IV, HashSize)
+ when N == 0; N == 1 ->
Sz1 = byte_size(T) - 1,
<<_:Sz1/binary, ?BYTE(PadLength0)>> = T,
PadLength = if
@@ -536,7 +646,20 @@ generic_block_cipher_from_bin(T, HashSize) ->
<<Content:CompressedLength/binary, Mac:HashSize/binary,
Padding:PadLength/binary, ?BYTE(PadLength0)>> = T,
#generic_block_cipher{content=Content, mac=Mac,
- padding=Padding, padding_length=PadLength0}.
+ padding=Padding, padding_length=PadLength0,
+ next_iv = IV};
+
+generic_block_cipher_from_bin({3, N}, T, IV, HashSize)
+ when N == 2; N == 3 ->
+ Sz1 = byte_size(T) - 1,
+ <<_:Sz1/binary, ?BYTE(PadLength)>> = T,
+ IVLength = byte_size(IV),
+ CompressedLength = byte_size(T) - IVLength - PadLength - 1 - HashSize,
+ <<NextIV:IVLength/binary, Content:CompressedLength/binary, Mac:HashSize/binary,
+ Padding:PadLength/binary, ?BYTE(PadLength)>> = T,
+ #generic_block_cipher{content=Content, mac=Mac,
+ padding=Padding, padding_length=PadLength,
+ next_iv = NextIV}.
generic_stream_cipher_from_bin(T, HashSz) ->
Sz = byte_size(T),
@@ -567,6 +690,10 @@ get_padding_aux(BlockSize, PadLength) ->
N = BlockSize - PadLength,
{N, list_to_binary(lists:duplicate(N, N))}.
+random_iv(IV) ->
+ IVSz = byte_size(IV),
+ ssl:random_bytes(IVSz).
+
next_iv(Bin, IV) ->
BinSz = byte_size(Bin),
IVSz = byte_size(IV),
@@ -578,16 +705,19 @@ rsa_signed_suites() ->
dhe_rsa_suites() ++ rsa_suites().
dhe_rsa_suites() ->
- [?TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
+ [?TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,
+ ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA,
+ ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,
?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_AES_256_CBC_SHA256,
+ ?TLS_RSA_WITH_AES_256_CBC_SHA,
?TLS_RSA_WITH_3DES_EDE_CBC_SHA,
+ ?TLS_RSA_WITH_AES_128_CBC_SHA256,
?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].
@@ -596,8 +726,10 @@ dsa_signed_suites() ->
dhe_dss_suites().
dhe_dss_suites() ->
- [?TLS_DHE_DSS_WITH_AES_256_CBC_SHA,
+ [?TLS_DHE_DSS_WITH_AES_256_CBC_SHA256,
+ ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA,
?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA,
+ ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA256,
?TLS_DHE_DSS_WITH_AES_128_CBC_SHA,
?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA].
diff --git a/lib/ssl/src/ssl_cipher.hrl b/lib/ssl/src/ssl_cipher.hrl
index 8bd68cc190..0f439f8ed5 100644
--- a/lib/ssl/src/ssl_cipher.hrl
+++ b/lib/ssl/src/ssl_cipher.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -28,8 +28,9 @@
-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 hash() :: null | sha | md5 | sha256 | sha384 | sha512.
-type erl_cipher_suite() :: {key_algo(), cipher(), hash()}.
+-type int_cipher_suite() :: {key_algo(), cipher(), hash(), hash()}.
-type cipher_suite() :: binary().
-type cipher_enum() :: integer().
-type openssl_cipher_suite() :: string().
@@ -177,6 +178,47 @@
%% TLS_DH_anon_WITH_AES_256_CBC_SHA = { 0x00, 0x3A };
-define(TLS_DH_anon_WITH_AES_256_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#3A)>>).
+%%% TLS 1.2 Cipher Suites RFC 5246
+
+%% TLS_RSA_WITH_NULL_SHA256 = { 0x00,0x3B };
+-define(TLS_RSA_WITH_NULL_SHA256, <<?BYTE(16#00), ?BYTE(16#3B)>>).
+
+%% TLS_RSA_WITH_AES_128_CBC_SHA256 = { 0x00,0x3C };
+-define(TLS_RSA_WITH_AES_128_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#3C)>>).
+
+%% TLS_RSA_WITH_AES_256_CBC_SHA256 = { 0x00,0x3D };
+-define(TLS_RSA_WITH_AES_256_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#3D)>>).
+
+%% TLS_DH_DSS_WITH_AES_128_CBC_SHA256 = { 0x00,0x3E };
+-define(TLS_DH_DSS_WITH_AES_128_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#3E)>>).
+
+%% TLS_DH_RSA_WITH_AES_128_CBC_SHA256 = { 0x00,0x3F };
+-define(TLS_DH_RSA_WITH_AES_128_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#3F)>>).
+
+%% TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 = { 0x00,0x40 };
+-define(TLS_DHE_DSS_WITH_AES_128_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#40)>>).
+
+%% TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 = { 0x00,0x67 };
+-define(TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#67)>>).
+
+%% TLS_DH_DSS_WITH_AES_256_CBC_SHA256 = { 0x00,0x68 };
+-define(TLS_DH_DSS_WITH_AES_256_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#68)>>).
+
+%% TLS_DH_RSA_WITH_AES_256_CBC_SHA256 = { 0x00,0x69 };
+-define(TLS_DH_RSA_WITH_AES_256_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#69)>>).
+
+%% TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 = { 0x00,0x6A };
+-define(TLS_DHE_DSS_WITH_AES_256_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#6A)>>).
+
+%% TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 = { 0x00,0x6B };
+-define(TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#6B)>>).
+
+%% TLS_DH_anon_WITH_AES_128_CBC_SHA256 = { 0x00,0x6C };
+-define(TLS_DH_anon_WITH_AES_128_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#6C)>>).
+
+%% TLS_DH_anon_WITH_AES_256_CBC_SHA256 = { 0x00,0x6D };
+-define(TLS_DH_anon_WITH_AES_256_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#6D)>>).
+
%%% Kerberos Cipher Suites
%% TLS_KRB5_WITH_DES_CBC_SHA = { 0x00,0x1E };
diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl
index c57930e821..68f6a4d4c1 100644
--- a/lib/ssl/src/ssl_connection.erl
+++ b/lib/ssl/src/ssl_connection.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -40,8 +40,7 @@
-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,
- prf/5]).
+ peer_certificate/1, renegotiation/1, negotiated_next_protocol/1, prf/5]).
%% Called by ssl_connection_sup
-export([start_link/7]).
@@ -67,8 +66,7 @@
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_handshake_history, % tls_handshake_history()
tls_cipher_texts, % list() received but not deciphered yet
cert_db, %
session, % #session{} from ssl_handshake.hrl
@@ -78,6 +76,7 @@
supported_protocol_versions, % [atom()]
client_certificate_requested = false,
key_algorithm, % atom as defined by cipher_suite
+ hashsign_algorithm, % atom as defined by cipher_suite
public_key_info, % PKIX: {Algorithm, PublicKey, PublicKeyParams}
private_key, % PKIX: #'RSAPrivateKey'{}
diffie_hellman_params, % PKIX: #'DHParameter'{} relevant for server side
@@ -90,9 +89,12 @@
log_alert, % boolean()
renegotiation, % {boolean(), From | internal | peer}
start_or_recv_from, % "gen_fsm From"
+ timer, % start_or_recv_timer
send_queue, % queue()
terminated = false, %
- allow_renegotiate = true
+ allow_renegotiate = true,
+ expecting_next_protocol_negotiation = false :: boolean(),
+ next_protocol = undefined :: undefined | binary()
}).
-define(DEFAULT_DIFFIE_HELLMAN_PARAMS,
@@ -118,7 +120,7 @@ send(Pid, Data) ->
sync_send_all_state_event(Pid, {application_data,
%% iolist_to_binary should really
%% be called iodata_to_binary()
- erlang:iolist_to_binary(Data)}, infinity).
+ erlang:iolist_to_binary(Data)}).
%%--------------------------------------------------------------------
-spec recv(pid(), integer(), timeout()) ->
@@ -127,7 +129,7 @@ send(Pid, Data) ->
%% Description: Receives data when active = false
%%--------------------------------------------------------------------
recv(Pid, Length, Timeout) ->
- sync_send_all_state_event(Pid, {recv, Length}, Timeout).
+ sync_send_all_state_event(Pid, {recv, Length, Timeout}).
%%--------------------------------------------------------------------
-spec connect(host(), inet:port_number(), port(), {#ssl_options{}, #socket_options{}},
pid(), tuple(), timeout()) ->
@@ -164,7 +166,7 @@ ssl_accept(Port, Socket, Opts, User, CbInfo, Timeout) ->
%% Description: Starts ssl handshake.
%%--------------------------------------------------------------------
handshake(#sslsocket{pid = Pid}, Timeout) ->
- case sync_send_all_state_event(Pid, start, Timeout) of
+ case sync_send_all_state_event(Pid, {start, Timeout}) of
connected ->
ok;
Error ->
@@ -179,7 +181,7 @@ handshake(#sslsocket{pid = Pid}, Timeout) ->
socket_control(Socket, Pid, CbModule) ->
case CbModule:controlling_process(Socket, Pid) of
ok ->
- {ok, sslsocket(Pid)};
+ {ok, sslsocket(Pid, Socket)};
{error, Reason} ->
{error, Reason}
end.
@@ -213,20 +215,15 @@ shutdown(ConnectionPid, How) ->
%%--------------------------------------------------------------------
new_user(ConnectionPid, User) ->
sync_send_all_state_event(ConnectionPid, {new_user, User}).
+
%%--------------------------------------------------------------------
--spec sockname(pid()) -> {ok, {inet:ip_address(), inet:port_number()}} | {error, reason()}.
-%%
-%% Description: Same as inet:sockname/1
-%%--------------------------------------------------------------------
-sockname(ConnectionPid) ->
- sync_send_all_state_event(ConnectionPid, sockname).
-%%--------------------------------------------------------------------
--spec peername(pid()) -> {ok, {inet:ip_address(), inet:port_number()}} | {error, reason()}.
+-spec negotiated_next_protocol(pid()) -> {ok, binary()} | {error, reason()}.
%%
-%% Description: Same as inet:peername/1
+%% Description: Returns the negotiated protocol
%%--------------------------------------------------------------------
-peername(ConnectionPid) ->
- sync_send_all_state_event(ConnectionPid, peername).
+negotiated_next_protocol(ConnectionPid) ->
+ sync_send_all_state_event(ConnectionPid, negotiated_next_protocol).
+
%%--------------------------------------------------------------------
-spec get_opts(pid(), list()) -> {ok, list()} | {error, reason()}.
%%
@@ -301,12 +298,13 @@ start_link(Role, Host, Port, Socket, Options, User, CbInfo) ->
init([Role, Host, Port, Socket, {SSLOpts0, _} = Options, User, CbInfo]) ->
State0 = initial_state(Role, Host, Port, Socket, Options, User, CbInfo),
- Hashes0 = ssl_handshake:init_hashes(),
+ Handshake = ssl_handshake:init_handshake_history(),
TimeStamp = calendar:datetime_to_gregorian_seconds({date(), time()}),
try ssl_init(SSLOpts0, Role) of
{ok, Ref, CertDbHandle, FileRefHandle, CacheHandle, OwnCert, Key, DHParams} ->
Session = State0#state.session,
- State = State0#state{tls_handshake_hashes = Hashes0,
+ State = State0#state{
+ tls_handshake_history = Handshake,
session = Session#session{own_certificate = OwnCert,
time_stamp = TimeStamp},
file_ref_db = FileRefHandle,
@@ -334,25 +332,25 @@ init([Role, Host, Port, Socket, {SSLOpts0, _} = Options, User, CbInfo]) ->
#state{}) -> gen_fsm_state_return().
%%--------------------------------------------------------------------
hello(start, #state{host = Host, port = Port, role = client,
- ssl_options = SslOpts,
- session = #session{own_certificate = Cert} = Session0,
- session_cache = Cache, session_cache_cb = CacheCb,
- transport_cb = Transport, socket = Socket,
- connection_states = ConnectionStates0,
- renegotiation = {Renegotiation, _}} = State0) ->
+ ssl_options = SslOpts,
+ session = #session{own_certificate = Cert} = Session0,
+ session_cache = Cache, session_cache_cb = CacheCb,
+ transport_cb = Transport, socket = Socket,
+ connection_states = ConnectionStates0,
+ renegotiation = {Renegotiation, _}} = State0) ->
Hello = ssl_handshake:client_hello(Host, Port, ConnectionStates0, SslOpts,
Cache, CacheCb, Renegotiation, Cert),
-
+
Version = Hello#client_hello.client_version,
- Hashes0 = ssl_handshake:init_hashes(),
- {BinMsg, ConnectionStates, Hashes} =
- encode_handshake(Hello, Version, ConnectionStates0, Hashes0),
+ Handshake0 = ssl_handshake:init_handshake_history(),
+ {BinMsg, ConnectionStates, Handshake} =
+ encode_handshake(Hello, Version, ConnectionStates0, Handshake0),
Transport:send(Socket, BinMsg),
State1 = State0#state{connection_states = ConnectionStates,
- negotiated_version = Version, %% Requested version at this point
+ negotiated_version = Version, %% Requested version
session =
Session0#session{session_id = Hello#client_hello.session_id},
- tls_handshake_hashes = Hashes},
+ tls_handshake_history = Handshake},
{Record, State} = next_record(State1),
next_state(hello, hello, Record, State);
@@ -373,16 +371,29 @@ hello(#server_hello{cipher_suite = CipherSuite,
renegotiation = {Renegotiation, _},
ssl_options = SslOptions} = State0) ->
case ssl_handshake:hello(Hello, SslOptions, ConnectionStates0, Renegotiation) of
- {Version, NewId, ConnectionStates} ->
- {KeyAlgorithm, _, _} =
+ #alert{} = Alert ->
+ handle_own_alert(Alert, ReqVersion, hello, State0),
+ {stop, {shutdown, own_alert}, State0};
+ {Version, NewId, ConnectionStates, NextProtocol} ->
+ {KeyAlgorithm, _, _, _} =
ssl_cipher:suite_definition(CipherSuite),
-
+
PremasterSecret = make_premaster_secret(ReqVersion, KeyAlgorithm),
+ NewNextProtocol = case NextProtocol of
+ undefined ->
+ State0#state.next_protocol;
+ _ ->
+ NextProtocol
+ end,
+
State = State0#state{key_algorithm = KeyAlgorithm,
+ hashsign_algorithm = default_hashsign(Version, KeyAlgorithm),
negotiated_version = Version,
connection_states = ConnectionStates,
- premaster_secret = PremasterSecret},
+ premaster_secret = PremasterSecret,
+ expecting_next_protocol_negotiation = NextProtocol =/= undefined,
+ next_protocol = NewNextProtocol},
case ssl_session:is_new(OldId, NewId) of
true ->
@@ -390,13 +401,10 @@ hello(#server_hello{cipher_suite = CipherSuite,
State#state{connection_states = ConnectionStates});
false ->
handle_resumed_session(NewId, State#state{connection_states = ConnectionStates})
- end;
- #alert{} = Alert ->
- handle_own_alert(Alert, ReqVersion, hello, State0),
- {stop, normal, State0}
+ end
end;
-hello(Hello = #client_hello{client_version = ClientVersion},
+hello(Hello = #client_hello{client_version = ClientVersion},
State = #state{connection_states = ConnectionStates0,
port = Port, session = #session{own_certificate = Cert} = Session0,
renegotiation = {Renegotiation, _},
@@ -405,14 +413,13 @@ hello(Hello = #client_hello{client_version = ClientVersion},
ssl_options = SslOpts}) ->
case ssl_handshake:hello(Hello, SslOpts, {Port, Session0, Cache, CacheCb,
ConnectionStates0, Cert}, Renegotiation) of
- {Version, {Type, Session}, ConnectionStates} ->
- do_server_hello(Type, State#state{connection_states =
+ {Version, {Type, Session}, ConnectionStates, ProtocolsToAdvertise} ->
+ do_server_hello(Type, ProtocolsToAdvertise, State#state{connection_states =
ConnectionStates,
negotiated_version = Version,
session = Session});
#alert{} = Alert ->
- handle_own_alert(Alert, ClientVersion, hello, State),
- {stop, normal, State}
+ handle_own_alert(Alert, ClientVersion, hello, State)
end;
hello(timeout, State) ->
@@ -431,39 +438,39 @@ abbreviated(#hello_request{}, State0) ->
abbreviated(#finished{verify_data = Data} = Finished,
#state{role = server,
negotiated_version = Version,
- tls_handshake_hashes = Hashes,
+ tls_handshake_history = Handshake,
session = #session{master_secret = MasterSecret},
connection_states = ConnectionStates0} =
State) ->
case ssl_handshake:verify_connection(Version, Finished, client,
- MasterSecret, Hashes) of
+ get_current_connection_state_prf(ConnectionStates0, write),
+ MasterSecret, Handshake) of
verified ->
ConnectionStates = ssl_record:set_client_verify_data(current_both, Data, ConnectionStates0),
next_state_connection(abbreviated,
ack_connection(State#state{connection_states = ConnectionStates}));
#alert{} = Alert ->
- handle_own_alert(Alert, Version, abbreviated, State),
- {stop, normal, State}
+ handle_own_alert(Alert, Version, abbreviated, State)
end;
abbreviated(#finished{verify_data = Data} = Finished,
- #state{role = client, tls_handshake_hashes = Hashes0,
+ #state{role = client, tls_handshake_history = Handshake0,
session = #session{master_secret = MasterSecret},
negotiated_version = Version,
connection_states = ConnectionStates0} = State) ->
case ssl_handshake:verify_connection(Version, Finished, server,
- MasterSecret, Hashes0) of
+ get_pending_connection_state_prf(ConnectionStates0, write),
+ MasterSecret, Handshake0) of
verified ->
ConnectionStates1 = ssl_record:set_server_verify_data(current_read, Data, ConnectionStates0),
- {ConnectionStates, Hashes} =
+ {ConnectionStates, Handshake} =
finalize_handshake(State#state{connection_states = ConnectionStates1}, abbreviated),
next_state_connection(abbreviated,
- ack_connection(State#state{tls_handshake_hashes = Hashes,
+ ack_connection(State#state{tls_handshake_history = Handshake,
connection_states =
ConnectionStates}));
#alert{} = Alert ->
- handle_own_alert(Alert, Version, abbreviated, State),
- {stop, normal, State}
+ handle_own_alert(Alert, Version, abbreviated, State)
end;
abbreviated(timeout, State) ->
@@ -487,8 +494,7 @@ certify(#certificate{asn1_certificates = []},
fail_if_no_peer_cert = true}} =
State) ->
Alert = ?ALERT_REC(?FATAL,?HANDSHAKE_FAILURE),
- handle_own_alert(Alert, Version, certify, State),
- {stop, normal, State};
+ handle_own_alert(Alert, Version, certify, State);
certify(#certificate{asn1_certificates = []},
#state{role = server,
@@ -511,8 +517,7 @@ certify(#certificate{} = Cert,
handle_peer_cert(PeerCert, PublicKeyInfo,
State#state{client_certificate_requested = false});
#alert{} = Alert ->
- handle_own_alert(Alert, Version, certify, State),
- {stop, normal, State}
+ handle_own_alert(Alert, Version, certify, State)
end;
certify(#server_key_exchange{} = KeyExchangeMsg,
@@ -524,8 +529,7 @@ certify(#server_key_exchange{} = KeyExchangeMsg,
{Record, State} = next_record(State1),
next_state(certify, certify, Record, State);
#alert{} = Alert ->
- handle_own_alert(Alert, Version, certify, State0),
- {stop, normal, State0}
+ handle_own_alert(Alert, Version, certify, State0)
end;
certify(#server_key_exchange{} = Msg,
@@ -549,8 +553,7 @@ certify(#server_hello_done{},
State = State0#state{connection_states = ConnectionStates},
client_certify_and_key_exchange(State);
#alert{} = Alert ->
- handle_own_alert(Alert, Version, certify, State0),
- {stop, normal, State0}
+ handle_own_alert(Alert, Version, certify, State0)
end;
%% Master secret is calculated from premaster_secret
@@ -568,8 +571,7 @@ certify(#server_hello_done{},
session = Session},
client_certify_and_key_exchange(State);
#alert{} = Alert ->
- handle_own_alert(Alert, Version, certify, State0),
- {stop, normal, State0}
+ handle_own_alert(Alert, Version, certify, State0)
end;
certify(#client_key_exchange{} = Msg,
@@ -585,10 +587,10 @@ certify(#client_key_exchange{exchange_keys = Keys},
certify_client_key_exchange(ssl_handshake:decode_client_key(Keys, KeyAlg, Version), State)
catch
#alert{} = Alert ->
- handle_own_alert(Alert, Version, certify, State),
- {stop, normal, State}
+ handle_own_alert(Alert, Version, certify, State)
end;
+
certify(timeout, State) ->
{ next_state, certify, State, hibernate };
@@ -610,8 +612,7 @@ certify_client_key_exchange(#encrypted_premaster_secret{premaster_secret= EncPMS
{Record, State} = next_record(State1),
next_state(certify, cipher, Record, State);
#alert{} = Alert ->
- handle_own_alert(Alert, Version, certify, State0),
- {stop, normal, State0}
+ handle_own_alert(Alert, Version, certify, State0)
end;
certify_client_key_exchange(#client_diffie_hellman_public{dh_public = ClientPublicDhKey},
@@ -624,8 +625,7 @@ certify_client_key_exchange(#client_diffie_hellman_public{dh_public = ClientPubl
{Record, State} = next_record(State1),
next_state(certify, cipher, Record, State);
#alert{} = Alert ->
- handle_own_alert(Alert, Version, certify, State0),
- {stop, normal, State0}
+ handle_own_alert(Alert, Version, certify, State0)
end.
%%--------------------------------------------------------------------
@@ -636,23 +636,33 @@ cipher(#hello_request{}, State0) ->
{Record, State} = next_record(State0),
next_state(cipher, hello, Record, State);
-cipher(#certificate_verify{signature = Signature},
+cipher(#certificate_verify{signature = Signature, hashsign_algorithm = CertHashSign},
#state{role = server,
public_key_info = PublicKeyInfo,
negotiated_version = Version,
session = #session{master_secret = MasterSecret},
- tls_handshake_hashes = Hashes
+ hashsign_algorithm = ConnectionHashSign,
+ tls_handshake_history = Handshake
} = State0) ->
+ HashSign = case CertHashSign of
+ {_, _} -> CertHashSign;
+ _ -> ConnectionHashSign
+ end,
case ssl_handshake:certificate_verify(Signature, PublicKeyInfo,
- Version, MasterSecret, Hashes) of
+ Version, HashSign, MasterSecret, Handshake) of
valid ->
{Record, State} = next_record(State0),
next_state(cipher, cipher, Record, State);
#alert{} = Alert ->
- handle_own_alert(Alert, Version, cipher, State0),
- {stop, normal, State0}
+ handle_own_alert(Alert, Version, cipher, State0)
end;
+% client must send a next protocol message if we are expecting it
+cipher(#finished{}, #state{role = server, expecting_next_protocol_negotiation = true,
+ next_protocol = undefined, negotiated_version = Version} = State0) ->
+ handle_own_alert(?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE), Version, cipher, State0),
+ {stop, normal, State0};
+
cipher(#finished{verify_data = Data} = Finished,
#state{negotiated_version = Version,
host = Host,
@@ -660,18 +670,26 @@ cipher(#finished{verify_data = Data} = Finished,
role = Role,
session = #session{master_secret = MasterSecret}
= Session0,
- tls_handshake_hashes = Hashes0} = State) ->
+ connection_states = ConnectionStates0,
+ tls_handshake_history = Handshake0} = State) ->
case ssl_handshake:verify_connection(Version, Finished,
opposite_role(Role),
- MasterSecret, Hashes0) of
+ get_current_connection_state_prf(ConnectionStates0, read),
+ MasterSecret, Handshake0) of
verified ->
Session = register_session(Role, Host, Port, Session0),
cipher_role(Role, Data, Session, State);
#alert{} = Alert ->
- handle_own_alert(Alert, Version, cipher, State),
- {stop, normal, State}
+ handle_own_alert(Alert, Version, cipher, State)
end;
+% only allowed to send next_protocol message after change cipher spec
+% & before finished message and it is not allowed during renegotiation
+cipher(#next_protocol{selected_protocol = SelectedProtocol},
+ #state{role = server, expecting_next_protocol_negotiation = true} = State0) ->
+ {Record, State} = next_record(State0#state{next_protocol = SelectedProtocol}),
+ next_state(cipher, cipher, Record, State);
+
cipher(timeout, State) ->
{ next_state, cipher, State, hibernate };
@@ -691,17 +709,17 @@ connection(#hello_request{}, #state{host = Host, port = Port,
transport_cb = Transport,
connection_states = ConnectionStates0,
renegotiation = {Renegotiation, _},
- tls_handshake_hashes = Hashes0} = State0) ->
+ tls_handshake_history = Handshake0} = State0) ->
Hello = ssl_handshake:client_hello(Host, Port, ConnectionStates0, SslOpts,
Cache, CacheCb, Renegotiation, Cert),
- {BinMsg, ConnectionStates, Hashes} =
- encode_handshake(Hello, Version, ConnectionStates0, Hashes0),
+ {BinMsg, ConnectionStates, Handshake} =
+ encode_handshake(Hello, Version, ConnectionStates0, Handshake0),
Transport:send(Socket, BinMsg),
{Record, State} = next_record(State0#state{connection_states =
- ConnectionStates,
+ ConnectionStates,
session = Session0#session{session_id = Hello#client_hello.session_id},
- tls_handshake_hashes = Hashes}),
+ tls_handshake_history = Handshake}),
next_state(connection, hello, Record, State);
connection(#client_hello{} = Hello, #state{role = server, allow_renegotiate = true} = State) ->
%% Mitigate Computational DoS attack
@@ -757,8 +775,10 @@ handle_sync_event({application_data, Data}, From, StateName,
State#state{send_queue = queue:in({From, Data}, Queue)},
get_timeout(State)};
-handle_sync_event(start, StartFrom, hello, State) ->
- hello(start, State#state{start_or_recv_from = StartFrom});
+handle_sync_event({start, Timeout}, StartFrom, hello, State) ->
+ Timer = start_or_recv_cancel_timer(Timeout, StartFrom),
+ hello(start, State#state{start_or_recv_from = StartFrom,
+ timer = Timer});
%% The two clauses below could happen if a server upgrades a socket in
%% active mode. Note that in this case we are lucky that
@@ -767,13 +787,16 @@ handle_sync_event(start, StartFrom, hello, State) ->
%% mode before telling the client that it is willing to upgrade
%% and before calling ssl:ssl_accept/2. These clauses are
%% here to make sure it is the users problem and not owers if
-%% they upgrade a active socket.
-handle_sync_event(start, _, connection, State) ->
+%% they upgrade an active socket.
+handle_sync_event({start,_}, _, connection, State) ->
{reply, connected, connection, State, get_timeout(State)};
-handle_sync_event(start, _From, error, {Error, State = #state{}}) ->
+handle_sync_event({start,_}, _From, error, {Error, State = #state{}}) ->
{stop, {shutdown, Error}, {error, Error}, State};
-handle_sync_event(start, StartFrom, StateName, State) ->
- {next_state, StateName, State#state{start_or_recv_from = StartFrom}, get_timeout(State)};
+
+handle_sync_event({start, Timeout}, StartFrom, StateName, State) ->
+ Timer = start_or_recv_cancel_timer(Timeout, StartFrom),
+ {next_state, StateName, State#state{start_or_recv_from = StartFrom,
+ timer = Timer}, get_timeout(State)};
handle_sync_event(close, _, StateName, State) ->
%% Run terminate before returning
@@ -804,13 +827,17 @@ handle_sync_event({shutdown, How0}, _, StateName,
{stop, normal, Error, State}
end;
-handle_sync_event({recv, N}, RecvFrom, connection = StateName, State0) ->
- passive_receive(State0#state{bytes_to_read = N, start_or_recv_from = RecvFrom}, StateName);
+handle_sync_event({recv, N, Timeout}, RecvFrom, connection = StateName, State0) ->
+ Timer = start_or_recv_cancel_timer(Timeout, RecvFrom),
+ passive_receive(State0#state{bytes_to_read = N,
+ start_or_recv_from = RecvFrom, timer = Timer}, StateName);
%% Doing renegotiate wait with handling request until renegotiate is
%% finished. Will be handled by next_state_is_connection/2.
-handle_sync_event({recv, N}, RecvFrom, StateName, State) ->
- {next_state, StateName, State#state{bytes_to_read = N, start_or_recv_from = RecvFrom},
+handle_sync_event({recv, N, Timeout}, RecvFrom, StateName, State) ->
+ Timer = start_or_recv_cancel_timer(Timeout, RecvFrom),
+ {next_state, StateName, State#state{bytes_to_read = N, start_or_recv_from = RecvFrom,
+ timer = Timer},
get_timeout(State)};
handle_sync_event({new_user, User}, _From, StateName,
@@ -826,15 +853,10 @@ handle_sync_event({get_opts, OptTags}, _From, StateName,
OptsReply = get_socket_opts(Socket, OptTags, SockOpts, []),
{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, get_timeout(State)};
-
-handle_sync_event(peername, _From, StateName,
- #state{socket = Socket} = State) ->
- PeerNameReply = inet:peername(Socket),
- {reply, PeerNameReply, StateName, State, get_timeout(State)};
+handle_sync_event(negotiated_next_protocol, _From, StateName, #state{next_protocol = undefined} = State) ->
+ {reply, {error, next_protocol_not_negotiated}, StateName, State, get_timeout(State)};
+handle_sync_event(negotiated_next_protocol, _From, StateName, #state{next_protocol = NextProtocol} = State) ->
+ {reply, {ok, NextProtocol}, StateName, State, get_timeout(State)};
handle_sync_event({set_opts, Opts0}, _From, StateName,
#state{socket_options = Opts1,
@@ -908,14 +930,14 @@ handle_sync_event(info, _, StateName,
session = #session{cipher_suite = Suite}} = State) ->
AtomVersion = ssl_record:protocol_version(Version),
- {reply, {ok, {AtomVersion, ssl_cipher:suite_definition(Suite)}},
+ {reply, {ok, {AtomVersion, ssl:suite_definition(Suite)}},
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)}],
+ {cipher_suite, ssl:suite_definition(Suite)}],
StateName, State, get_timeout(State)};
handle_sync_event(peer_certificate, _, StateName,
@@ -937,7 +959,7 @@ handle_info({Protocol, _, Data}, StateName,
next_state(StateName, StateName, Record, State);
#alert{} = Alert ->
handle_normal_shutdown(Alert, StateName, State0),
- {stop, normal, State0}
+ {stop, {shutdown, own_alert}, State0}
end;
handle_info({CloseTag, Socket}, StateName,
@@ -958,12 +980,12 @@ handle_info({CloseTag, Socket}, StateName,
ok
end,
handle_normal_shutdown(?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), StateName, State),
- {stop, normal, State};
+ {stop, {shutdown, transport_closed}, State};
handle_info({ErrorTag, Socket, econnaborted}, StateName,
#state{socket = Socket, start_or_recv_from = StartFrom, role = Role,
error_tag = ErrorTag} = State) when StateName =/= connection ->
- alert_user(StartFrom, ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE), Role),
+ alert_user(Socket, StartFrom, ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE), Role),
{stop, normal, State};
handle_info({ErrorTag, Socket, Reason}, StateName, #state{socket = Socket,
@@ -979,7 +1001,21 @@ handle_info({'DOWN', MonitorRef, _, _, _}, _,
handle_info(allow_renegotiate, StateName, State) ->
{next_state, StateName, State#state{allow_renegotiate = true}, get_timeout(State)};
-
+
+handle_info({cancel_start_or_recv, StartFrom}, StateName,
+ #state{renegotiation = {false, first}} = State) when StateName =/= connection ->
+ gen_fsm:reply(StartFrom, {error, timeout}),
+ {stop, {shutdown, user_timeout}, State#state{timer = undefined}};
+
+handle_info({cancel_start_or_recv, RecvFrom}, StateName, #state{start_or_recv_from = RecvFrom} = State) ->
+ gen_fsm:reply(RecvFrom, {error, timeout}),
+ {next_state, StateName, State#state{start_or_recv_from = undefined,
+ bytes_to_read = undefined,
+ timer = undefined}, get_timeout(State)};
+
+handle_info({cancel_start_or_recv, _RecvFrom}, StateName, State) ->
+ {next_state, StateName, State#state{timer = undefined}, get_timeout(State)};
+
handle_info(Msg, StateName, State) ->
Report = io_lib:format("SSL: Got unexpected info: ~p ~n", [Msg]),
error_logger:info_report(Report),
@@ -996,6 +1032,20 @@ terminate(_, _, #state{terminated = true}) ->
%% we want to guarantee that Transport:close has been called
%% when ssl:close/1 returns.
ok;
+
+terminate({shutdown, transport_closed}, StateName, #state{send_queue = SendQueue,
+ renegotiation = Renegotiate} = State) ->
+ handle_unrecv_data(StateName, State),
+ handle_trusted_certs_db(State),
+ notify_senders(SendQueue),
+ notify_renegotiater(Renegotiate);
+
+terminate({shutdown, own_alert}, _StateName, #state{send_queue = SendQueue,
+ renegotiation = Renegotiate} = State) ->
+ handle_trusted_certs_db(State),
+ notify_senders(SendQueue),
+ notify_renegotiater(Renegotiate);
+
terminate(Reason, connection, #state{negotiated_version = Version,
connection_states = ConnectionStates,
transport_cb = Transport,
@@ -1006,16 +1056,14 @@ terminate(Reason, connection, #state{negotiated_version = Version,
notify_renegotiater(Renegotiate),
BinAlert = terminate_alert(Reason, Version, ConnectionStates),
Transport:send(Socket, BinAlert),
- workaround_transport_delivery_problems(Socket, Transport, Reason),
- Transport:close(Socket);
+ workaround_transport_delivery_problems(Socket, Transport);
-terminate(Reason, _StateName, #state{transport_cb = Transport,
+terminate(_Reason, _StateName, #state{transport_cb = Transport,
socket = Socket, send_queue = SendQueue,
renegotiation = Renegotiate} = State) ->
handle_trusted_certs_db(State),
notify_senders(SendQueue),
notify_renegotiater(Renegotiate),
- workaround_transport_delivery_problems(Socket, Transport, Reason),
Transport:close(Socket).
%%--------------------------------------------------------------------
@@ -1087,7 +1135,7 @@ init_certificates(#ssl_options{cacerts = CaCerts,
{ok, _, _, _, _, _} = ssl_manager:connection_init(Certs, Role)
catch
Error:Reason ->
- handle_file_error(?LINE, Error, Reason, CACertFile, ecacertfile,
+ handle_file_error(?LINE, Error, Reason, CACertFile, {ecacertfile, Reason},
erlang:get_stacktrace())
end,
init_certificates(Cert, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheHandle, CertFile, Role).
@@ -1109,7 +1157,7 @@ init_certificates(undefined, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHan
{ok, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheRef, OwnCert}
catch
Error:Reason ->
- handle_file_error(?LINE, Error, Reason, CertFile, ecertfile,
+ handle_file_error(?LINE, Error, Reason, CertFile, {ecertfile, Reason},
erlang:get_stacktrace())
end;
init_certificates(Cert, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheRef, _, _) ->
@@ -1128,7 +1176,7 @@ init_private_key(DbHandle, undefined, KeyFile, Password, _) ->
private_key(public_key:pem_entry_decode(PemEntry, Password))
catch
Error:Reason ->
- handle_file_error(?LINE, Error, Reason, KeyFile, ekeyfile,
+ handle_file_error(?LINE, Error, Reason, KeyFile, {ekeyfile, Reason},
erlang:get_stacktrace())
end;
@@ -1186,22 +1234,17 @@ init_diffie_hellman(DbHandle,_, DHParamFile, server) ->
catch
Error:Reason ->
handle_file_error(?LINE, Error, Reason,
- DHParamFile, edhfile, erlang:get_stacktrace())
+ DHParamFile, {edhfile, Reason}, erlang:get_stacktrace())
end.
sync_send_all_state_event(FsmPid, Event) ->
- sync_send_all_state_event(FsmPid, Event, infinity).
-
-sync_send_all_state_event(FsmPid, Event, Timeout) ->
- try gen_fsm:sync_send_all_state_event(FsmPid, Event, Timeout)
+ try gen_fsm:sync_send_all_state_event(FsmPid, Event, infinity)
catch
exit:{noproc, _} ->
{error, closed};
- exit:{timeout, _} ->
- {error, timeout};
exit:{normal, _} ->
{error, closed};
- exit:{shutdown, _} ->
+ exit:{{shutdown, _},_} ->
{error, closed}
end.
@@ -1224,13 +1267,13 @@ certify_client(#state{client_certificate_requested = true, role = client,
cert_db_ref = CertDbRef,
session = #session{own_certificate = OwnCert},
socket = Socket,
- tls_handshake_hashes = Hashes0} = State) ->
+ tls_handshake_history = Handshake0} = State) ->
Certificate = ssl_handshake:certificate(OwnCert, CertDbHandle, CertDbRef, client),
- {BinCert, ConnectionStates, Hashes} =
- encode_handshake(Certificate, Version, ConnectionStates0, Hashes0),
+ {BinCert, ConnectionStates, Handshake} =
+ encode_handshake(Certificate, Version, ConnectionStates0, Handshake0),
Transport:send(Socket, BinCert),
State#state{connection_states = ConnectionStates,
- tls_handshake_hashes = Hashes};
+ tls_handshake_history = Handshake};
certify_client(#state{client_certificate_requested = false} = State) ->
State.
@@ -1242,17 +1285,19 @@ verify_client_cert(#state{client_certificate_requested = true, role = client,
private_key = PrivateKey,
session = #session{master_secret = MasterSecret,
own_certificate = OwnCert},
- tls_handshake_hashes = Hashes0} = State) ->
+ hashsign_algorithm = HashSign,
+ tls_handshake_history = Handshake0} = State) ->
+ %%TODO: for TLS 1.2 we can choose a different/stronger HashSign combination for this.
case ssl_handshake:client_certificate_verify(OwnCert, MasterSecret,
- Version, PrivateKey, Hashes0) of
+ Version, HashSign, PrivateKey, Handshake0) of
#certificate_verify{} = Verified ->
- {BinVerified, ConnectionStates, Hashes} =
+ {BinVerified, ConnectionStates, Handshake} =
encode_handshake(Verified, Version,
- ConnectionStates0, Hashes0),
+ ConnectionStates0, Handshake0),
Transport:send(Socket, BinVerified),
State#state{connection_states = ConnectionStates,
- tls_handshake_hashes = Hashes};
+ tls_handshake_history = Handshake};
ignore ->
State;
#alert{} = Alert ->
@@ -1261,17 +1306,18 @@ verify_client_cert(#state{client_certificate_requested = true, role = client,
verify_client_cert(#state{client_certificate_requested = false} = State) ->
State.
-do_server_hello(Type, #state{negotiated_version = Version,
- session = #session{session_id = SessId},
- connection_states = ConnectionStates0,
- renegotiation = {Renegotiation, _}}
- = State0) when is_atom(Type) ->
+do_server_hello(Type, NextProtocolsToSend, #state{negotiated_version = Version,
+ session = #session{session_id = SessId},
+ connection_states = ConnectionStates0,
+ renegotiation = {Renegotiation, _}}
+ = State0) when is_atom(Type) ->
ServerHello =
ssl_handshake:server_hello(SessId, Version,
- ConnectionStates0, Renegotiation),
- State = server_hello(ServerHello, State0),
-
+ ConnectionStates0, Renegotiation, NextProtocolsToSend),
+ State = server_hello(ServerHello,
+ State0#state{expecting_next_protocol_negotiation =
+ NextProtocolsToSend =/= undefined}),
case Type of
new ->
new_server_hello(ServerHello, State);
@@ -1295,8 +1341,7 @@ new_server_hello(#server_hello{cipher_suite = CipherSuite,
next_state(hello, certify, Record, State)
catch
#alert{} = Alert ->
- handle_own_alert(Alert, Version, hello, State0),
- {stop, normal, State0}
+ handle_own_alert(Alert, Version, hello, State0)
end.
resumed_server_hello(#state{session = Session,
@@ -1308,16 +1353,15 @@ resumed_server_hello(#state{session = Session,
{_, ConnectionStates1} ->
State1 = State0#state{connection_states = ConnectionStates1,
session = Session},
- {ConnectionStates, Hashes} =
+ {ConnectionStates, Handshake} =
finalize_handshake(State1, abbreviated),
State2 = State1#state{connection_states =
ConnectionStates,
- tls_handshake_hashes = Hashes},
+ tls_handshake_history = Handshake},
{Record, State} = next_record(State2),
next_state(hello, abbreviated, Record, State);
#alert{} = Alert ->
- handle_own_alert(Alert, Version, hello, State0),
- {stop, normal, State0}
+ handle_own_alert(Alert, Version, hello, State0)
end.
handle_new_session(NewId, CipherSuite, Compression, #state{session = Session0} = State0) ->
@@ -1342,8 +1386,7 @@ handle_resumed_session(SessId, #state{connection_states = ConnectionStates0,
session = Session}),
next_state(hello, abbreviated, Record, State);
#alert{} = Alert ->
- handle_own_alert(Alert, Version, hello, State0),
- {stop, normal, State0}
+ handle_own_alert(Alert, Version, hello, State0)
end.
@@ -1351,17 +1394,16 @@ client_certify_and_key_exchange(#state{negotiated_version = Version} =
State0) ->
try do_client_certify_and_key_exchange(State0) of
State1 = #state{} ->
- {ConnectionStates, Hashes} = finalize_handshake(State1, certify),
+ {ConnectionStates, Handshake} = finalize_handshake(State1, certify),
State2 = State1#state{connection_states = ConnectionStates,
%% Reinitialize
client_certificate_requested = false,
- tls_handshake_hashes = Hashes},
+ tls_handshake_history = Handshake},
{Record, State} = next_record(State2),
next_state(certify, cipher, Record, State)
catch
throw:#alert{} = Alert ->
- handle_own_alert(Alert, Version, certify, State0),
- {stop, normal, State0}
+ handle_own_alert(Alert, Version, certify, State0)
end.
do_client_certify_and_key_exchange(State0) ->
@@ -1378,29 +1420,30 @@ server_hello(ServerHello, #state{transport_cb = Transport,
socket = Socket,
negotiated_version = Version,
connection_states = ConnectionStates0,
- tls_handshake_hashes = Hashes0} = State) ->
+ tls_handshake_history = Handshake0} = State) ->
CipherSuite = ServerHello#server_hello.cipher_suite,
- {KeyAlgorithm, _, _} = ssl_cipher:suite_definition(CipherSuite),
- {BinMsg, ConnectionStates1, Hashes1} =
- encode_handshake(ServerHello, Version, ConnectionStates0, Hashes0),
+ {KeyAlgorithm, _, _, _} = ssl_cipher:suite_definition(CipherSuite),
+ {BinMsg, ConnectionStates1, Handshake1} =
+ encode_handshake(ServerHello, Version, ConnectionStates0, Handshake0),
Transport:send(Socket, BinMsg),
State#state{connection_states = ConnectionStates1,
- tls_handshake_hashes = Hashes1,
- key_algorithm = KeyAlgorithm}.
+ tls_handshake_history = Handshake1,
+ key_algorithm = KeyAlgorithm,
+ hashsign_algorithm = default_hashsign(Version, KeyAlgorithm)}.
server_hello_done(#state{transport_cb = Transport,
socket = Socket,
negotiated_version = Version,
connection_states = ConnectionStates0,
- tls_handshake_hashes = Hashes0} = State) ->
+ tls_handshake_history = Handshake0} = State) ->
HelloDone = ssl_handshake:server_hello_done(),
- {BinHelloDone, ConnectionStates, Hashes} =
- encode_handshake(HelloDone, Version, ConnectionStates0, Hashes0),
+ {BinHelloDone, ConnectionStates, Handshake} =
+ encode_handshake(HelloDone, Version, ConnectionStates0, Handshake0),
Transport:send(Socket, BinHelloDone),
State#state{connection_states = ConnectionStates,
- tls_handshake_hashes = Hashes}.
+ tls_handshake_history = Handshake}.
certify_server(#state{key_algorithm = dh_anon} = State) ->
State;
@@ -1409,17 +1452,17 @@ certify_server(#state{transport_cb = Transport,
socket = Socket,
negotiated_version = Version,
connection_states = ConnectionStates0,
- tls_handshake_hashes = Hashes0,
+ tls_handshake_history = Handshake0,
cert_db = CertDbHandle,
cert_db_ref = CertDbRef,
session = #session{own_certificate = OwnCert}} = State) ->
case ssl_handshake:certificate(OwnCert, CertDbHandle, CertDbRef, server) of
CertMsg = #certificate{} ->
- {BinCertMsg, ConnectionStates, Hashes} =
- encode_handshake(CertMsg, Version, ConnectionStates0, Hashes0),
+ {BinCertMsg, ConnectionStates, Handshake} =
+ encode_handshake(CertMsg, Version, ConnectionStates0, Handshake0),
Transport:send(Socket, BinCertMsg),
State#state{connection_states = ConnectionStates,
- tls_handshake_hashes = Hashes
+ tls_handshake_history = Handshake
};
Alert = #alert{} ->
throw(Alert)
@@ -1428,11 +1471,12 @@ certify_server(#state{transport_cb = Transport,
key_exchange(#state{role = server, key_algorithm = rsa} = State) ->
State;
key_exchange(#state{role = server, key_algorithm = Algo,
+ hashsign_algorithm = HashSignAlgo,
diffie_hellman_params = #'DHParameter'{prime = P, base = G} = Params,
private_key = PrivateKey,
connection_states = ConnectionStates0,
negotiated_version = Version,
- tls_handshake_hashes = Hashes0,
+ tls_handshake_history = Handshake0,
socket = Socket,
transport_cb = Transport
} = State)
@@ -1445,16 +1489,16 @@ key_exchange(#state{role = server, key_algorithm = Algo,
SecParams = ConnectionState#connection_state.security_parameters,
#security_parameters{client_random = ClientRandom,
server_random = ServerRandom} = SecParams,
- Msg = ssl_handshake:key_exchange(server, {dh, Keys, Params,
- Algo, ClientRandom,
+ Msg = ssl_handshake:key_exchange(server, Version, {dh, Keys, Params,
+ HashSignAlgo, ClientRandom,
ServerRandom,
PrivateKey}),
- {BinMsg, ConnectionStates, Hashes} =
- encode_handshake(Msg, Version, ConnectionStates0, Hashes0),
+ {BinMsg, ConnectionStates, Handshake} =
+ encode_handshake(Msg, Version, ConnectionStates0, Handshake0),
Transport:send(Socket, BinMsg),
State#state{connection_states = ConnectionStates,
diffie_hellman_keys = Keys,
- tls_handshake_hashes = Hashes};
+ tls_handshake_history = Handshake};
key_exchange(#state{role = client,
connection_states = ConnectionStates0,
@@ -1463,67 +1507,93 @@ key_exchange(#state{role = client,
negotiated_version = Version,
premaster_secret = PremasterSecret,
socket = Socket, transport_cb = Transport,
- tls_handshake_hashes = Hashes0} = State) ->
- Msg = rsa_key_exchange(PremasterSecret, PublicKeyInfo),
- {BinMsg, ConnectionStates, Hashes} =
- encode_handshake(Msg, Version, ConnectionStates0, Hashes0),
+ tls_handshake_history = Handshake0} = State) ->
+ Msg = rsa_key_exchange(Version, PremasterSecret, PublicKeyInfo),
+ {BinMsg, ConnectionStates, Handshake} =
+ encode_handshake(Msg, Version, ConnectionStates0, Handshake0),
Transport:send(Socket, BinMsg),
State#state{connection_states = ConnectionStates,
- tls_handshake_hashes = Hashes};
+ tls_handshake_history = Handshake};
key_exchange(#state{role = client,
connection_states = ConnectionStates0,
key_algorithm = Algorithm,
negotiated_version = Version,
diffie_hellman_keys = {DhPubKey, _},
socket = Socket, transport_cb = Transport,
- tls_handshake_hashes = Hashes0} = State)
+ tls_handshake_history = Handshake0} = State)
when Algorithm == dhe_dss;
Algorithm == dhe_rsa;
Algorithm == dh_anon ->
- Msg = ssl_handshake:key_exchange(client, {dh, DhPubKey}),
- {BinMsg, ConnectionStates, Hashes} =
- encode_handshake(Msg, Version, ConnectionStates0, Hashes0),
+ Msg = ssl_handshake:key_exchange(client, Version, {dh, DhPubKey}),
+ {BinMsg, ConnectionStates, Handshake} =
+ encode_handshake(Msg, Version, ConnectionStates0, Handshake0),
Transport:send(Socket, BinMsg),
State#state{connection_states = ConnectionStates,
- tls_handshake_hashes = Hashes}.
+ tls_handshake_history = Handshake}.
-rsa_key_exchange(PremasterSecret, PublicKeyInfo = {Algorithm, _, _})
+rsa_key_exchange(Version, PremasterSecret, PublicKeyInfo = {Algorithm, _, _})
when Algorithm == ?rsaEncryption;
Algorithm == ?md2WithRSAEncryption;
Algorithm == ?md5WithRSAEncryption;
- Algorithm == ?sha1WithRSAEncryption ->
- ssl_handshake:key_exchange(client,
+ Algorithm == ?sha1WithRSAEncryption;
+ Algorithm == ?sha224WithRSAEncryption;
+ Algorithm == ?sha256WithRSAEncryption;
+ Algorithm == ?sha384WithRSAEncryption;
+ Algorithm == ?sha512WithRSAEncryption
+ ->
+ ssl_handshake:key_exchange(client, Version,
{premaster_secret, PremasterSecret,
PublicKeyInfo});
-rsa_key_exchange(_, _) ->
+rsa_key_exchange(_, _, _) ->
throw (?ALERT_REC(?FATAL,?HANDSHAKE_FAILURE)).
request_client_cert(#state{ssl_options = #ssl_options{verify = verify_peer},
connection_states = ConnectionStates0,
cert_db = CertDbHandle,
cert_db_ref = CertDbRef,
- tls_handshake_hashes = Hashes0,
+ tls_handshake_history = Handshake0,
negotiated_version = Version,
socket = Socket,
transport_cb = Transport} = State) ->
Msg = ssl_handshake:certificate_request(ConnectionStates0, CertDbHandle, CertDbRef),
- {BinMsg, ConnectionStates, Hashes} =
- encode_handshake(Msg, Version, ConnectionStates0, Hashes0),
+ {BinMsg, ConnectionStates, Handshake} =
+ encode_handshake(Msg, Version, ConnectionStates0, Handshake0),
Transport:send(Socket, BinMsg),
State#state{client_certificate_requested = true,
connection_states = ConnectionStates,
- tls_handshake_hashes = Hashes};
+ tls_handshake_history = Handshake};
request_client_cert(#state{ssl_options = #ssl_options{verify = verify_none}} =
State) ->
State.
finalize_handshake(State, StateName) ->
- ConnectionStates0 = cipher_protocol(State),
+ ConnectionStates0 = cipher_protocol(State),
+
ConnectionStates =
ssl_record:activate_pending_connection_state(ConnectionStates0,
write),
- finished(State#state{connection_states = ConnectionStates}, StateName).
-
+
+ State1 = State#state{connection_states = ConnectionStates},
+ State2 = next_protocol(State1),
+ finished(State2, StateName).
+
+next_protocol(#state{role = server} = State) ->
+ State;
+next_protocol(#state{next_protocol = undefined} = State) ->
+ State;
+next_protocol(#state{expecting_next_protocol_negotiation = false} = State) ->
+ State;
+next_protocol(#state{transport_cb = Transport, socket = Socket,
+ negotiated_version = Version,
+ next_protocol = NextProtocol,
+ connection_states = ConnectionStates0,
+ tls_handshake_history = Handshake0} = State) ->
+ NextProtocolMessage = ssl_handshake:next_protocol(NextProtocol),
+ {BinMsg, ConnectionStates, Handshake} = encode_handshake(NextProtocolMessage, Version, ConnectionStates0, Handshake0),
+ Transport:send(Socket, BinMsg),
+ State#state{connection_states = ConnectionStates,
+ tls_handshake_history = Handshake}.
+
cipher_protocol(#state{connection_states = ConnectionStates0,
socket = Socket,
negotiated_version = Version,
@@ -1538,14 +1608,16 @@ finished(#state{role = Role, socket = Socket, negotiated_version = Version,
transport_cb = Transport,
session = Session,
connection_states = ConnectionStates0,
- tls_handshake_hashes = Hashes0}, StateName) ->
+ tls_handshake_history = Handshake0}, StateName) ->
MasterSecret = Session#session.master_secret,
- Finished = ssl_handshake:finished(Version, Role, MasterSecret, Hashes0),
+ Finished = ssl_handshake:finished(Version, Role,
+ get_current_connection_state_prf(ConnectionStates0, write),
+ MasterSecret, Handshake0),
ConnectionStates1 = save_verify_data(Role, Finished, ConnectionStates0, StateName),
- {BinFinished, ConnectionStates, Hashes} =
- encode_handshake(Finished, Version, ConnectionStates1, Hashes0),
+ {BinFinished, ConnectionStates, Handshake} =
+ encode_handshake(Finished, Version, ConnectionStates1, Handshake0),
Transport:send(Socket, BinFinished),
- {ConnectionStates, Hashes}.
+ {ConnectionStates, Handshake}.
save_verify_data(client, #finished{verify_data = Data}, ConnectionStates, certify) ->
ssl_record:set_client_verify_data(current_write, Data, ConnectionStates);
@@ -1556,73 +1628,49 @@ 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{public_key_info = PubKeyInfo,
- key_algorithm = KeyAlgo,
- connection_states = ConnectionStates} = State) ->
-
- PLen = size(P),
- GLen = size(G),
- YLen = size(ServerPublicDhKey),
+handle_server_key(#server_key_exchange{exchange_keys = Keys},
+ #state{key_algorithm = KeyAlg,
+ negotiated_version = Version} = State) ->
+ Params = ssl_handshake:decode_server_key(Keys, KeyAlg, Version),
+ HashSign = connection_hashsign(Params#server_key_params.hashsign, State),
+ case HashSign of
+ {_, anon} ->
+ server_master_secret(Params#server_key_params.params, State);
+ _ ->
+ verify_server_key(Params, HashSign, State)
+ end.
- ConnectionState =
+verify_server_key(#server_key_params{params = Params,
+ params_bin = EncParams,
+ signature = Signature},
+ HashSign = {HashAlgo, _},
+ #state{negotiated_version = Version,
+ public_key_info = PubKeyInfo,
+ connection_states = ConnectionStates} = State) ->
+ ConnectionState =
ssl_record:pending_connection_state(ConnectionStates, read),
SecParams = ConnectionState#connection_state.security_parameters,
#security_parameters{client_random = ClientRandom,
server_random = ServerRandom} = SecParams,
- Hash = ssl_handshake:server_key_exchange_hash(KeyAlgo,
- <<ClientRandom/binary,
- ServerRandom/binary,
- ?UINT16(PLen), P/binary,
- ?UINT16(GLen), G/binary,
- ?UINT16(YLen),
- ServerPublicDhKey/binary>>),
-
- case verify_dh_params(Signed, Hash, PubKeyInfo) of
+ Hash = ssl_handshake:server_key_exchange_hash(HashAlgo,
+ <<ClientRandom/binary,
+ ServerRandom/binary,
+ EncParams/binary>>),
+ case ssl_handshake:verify_signature(Version, Hash, HashSign, Signature, PubKeyInfo) of
true ->
- dh_master_secret(P, G, ServerPublicDhKey, undefined, State);
+ server_master_secret(Params, State);
false ->
?ALERT_REC(?FATAL, ?DECRYPT_ERROR)
end.
-verify_dh_params(Signed, Hashes, {?rsaEncryption, PubKey, _PubKeyParams}) ->
- case public_key:decrypt_public(Signed, PubKey,
- [{rsa_pad, rsa_pkcs1_padding}]) of
- 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});
+server_master_secret(#server_dh_params{dh_p = P, dh_g = G, dh_y = ServerPublicDhKey},
+ State) ->
+ dh_master_secret(P, G, ServerPublicDhKey, undefined, State).
-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]),
+master_from_premaster_secret(PremasterSecret,
+ #state{session = Session,
+ negotiated_version = Version, role = Role,
+ connection_states = ConnectionStates0} = State) ->
case ssl_handshake:master_secret(Version, PremasterSecret,
ConnectionStates0, Role) of
{MasterSecret, ConnectionStates} ->
@@ -1634,6 +1682,19 @@ dh_master_secret(PMpint, GMpint, PublicDhKey, PrivateDhKey,
Alert
end.
+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) ->
+ PremasterSecret =
+ crypto:dh_compute_key(mpint_binary(PublicDhKey), PrivateDhKey,
+ [PMpint, GMpint]),
+ master_from_premaster_secret(PremasterSecret, State).
+
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,
@@ -1641,26 +1702,26 @@ cipher_role(client, Data, Session, #state{connection_states = ConnectionStates0}
cipher_role(server, Data, Session, #state{connection_states = ConnectionStates0} = State) ->
ConnectionStates1 = ssl_record:set_client_verify_data(current_read, Data, ConnectionStates0),
- {ConnectionStates, Hashes} =
+ {ConnectionStates, Handshake} =
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})).
+ tls_handshake_history =
+ Handshake})).
encode_alert(#alert{} = Alert, Version, ConnectionStates) ->
ssl_record:encode_alert_record(Alert, Version, ConnectionStates).
encode_change_cipher(#change_cipher_spec{}, Version, ConnectionStates) ->
ssl_record:encode_change_cipher_spec(Version, ConnectionStates).
-encode_handshake(HandshakeRec, Version, ConnectionStates0, Hashes0) ->
+encode_handshake(HandshakeRec, Version, ConnectionStates0, Handshake0) ->
Frag = ssl_handshake:encode_handshake(HandshakeRec, Version),
- Hashes1 = ssl_handshake:update_hashes(Hashes0, Frag),
+ Handshake1 = ssl_handshake:update_handshake_history(Handshake0, Frag),
{E, ConnectionStates1} =
ssl_record:encode_handshake(Frag, Version, ConnectionStates0),
- {E, ConnectionStates1, Hashes1}.
+ {E, ConnectionStates1, Handshake1}.
encode_packet(Data, #socket_options{packet=Packet}) ->
case Packet of
@@ -1701,10 +1762,12 @@ passive_receive(State0 = #state{user_data_buffer = Buffer}, StateName) ->
end.
read_application_data(Data, #state{user_application = {_Mon, Pid},
- socket_options = SOpts,
- bytes_to_read = BytesToRead,
- start_or_recv_from = RecvFrom,
- user_data_buffer = Buffer0} = State0) ->
+ socket = Socket,
+ socket_options = SOpts,
+ bytes_to_read = BytesToRead,
+ start_or_recv_from = RecvFrom,
+ timer = Timer,
+ user_data_buffer = Buffer0} = State0) ->
Buffer1 = if
Buffer0 =:= <<>> -> Data;
Data =:= <<>> -> Buffer0;
@@ -1712,10 +1775,12 @@ read_application_data(Data, #state{user_application = {_Mon, Pid},
end,
case get_data(SOpts, BytesToRead, Buffer1) of
{ok, ClientData, Buffer} -> % Send data
- SocketOpt = deliver_app_data(SOpts, ClientData, Pid, RecvFrom),
+ SocketOpt = deliver_app_data(Socket, SOpts, ClientData, Pid, RecvFrom),
+ cancel_timer(Timer),
State = State0#state{user_data_buffer = Buffer,
start_or_recv_from = undefined,
- bytes_to_read = 0,
+ timer = undefined,
+ bytes_to_read = undefined,
socket_options = SocketOpt
},
if
@@ -1728,8 +1793,10 @@ read_application_data(Data, #state{user_application = {_Mon, Pid},
end;
{more, Buffer} -> % no reply, we need more data
next_record(State0#state{user_data_buffer = Buffer});
+ {passive, Buffer} ->
+ next_record_if_active(State0#state{user_data_buffer = Buffer});
{error,_Reason} -> %% Invalid packet in packet mode
- deliver_packet_error(SOpts, Buffer1, Pid, RecvFrom),
+ deliver_packet_error(Socket, SOpts, Buffer1, Pid, RecvFrom),
{stop, normal, State0}
end.
@@ -1769,6 +1836,9 @@ is_time_to_renegotiate(_,_) ->
%% Picks ClientData
get_data(_, _, <<>>) ->
{more, <<>>};
+%% Recv timed out save buffer data until next recv
+get_data(#socket_options{active=false}, undefined, Buffer) ->
+ {passive, Buffer};
get_data(#socket_options{active=Active, packet=Raw}, BytesToRead, Buffer)
when Raw =:= raw; Raw =:= 0 -> %% Raw Mode
if
@@ -1808,9 +1878,9 @@ decode_packet(Type, Buffer, PacketOpts) ->
%% Note that if the user has explicitly configured the socket to expect
%% HTTP headers using the {packet, httph} option, we don't do any automatic
%% switching of states.
-deliver_app_data(SOpts = #socket_options{active=Active, packet=Type},
- Data, Pid, From) ->
- send_or_reply(Active, Pid, From, format_reply(SOpts, Data)),
+deliver_app_data(Socket, SOpts = #socket_options{active=Active, packet=Type},
+ Data, Pid, From) ->
+ send_or_reply(Active, Pid, From, format_reply(Socket, SOpts, Data)),
SO = case Data of
{P, _, _, _} when ((P =:= http_request) or (P =:= http_response)),
((Type =:= http) or (Type =:= http_bin)) ->
@@ -1829,35 +1899,35 @@ deliver_app_data(SOpts = #socket_options{active=Active, packet=Type},
SO
end.
-format_reply(#socket_options{active = false, mode = Mode, packet = Packet,
+format_reply(_,#socket_options{active = false, mode = Mode, packet = Packet,
header = Header}, Data) ->
- {ok, format_reply(Mode, Packet, Header, Data)};
-format_reply(#socket_options{active = _, mode = Mode, packet = Packet,
+ {ok, do_format_reply(Mode, Packet, Header, Data)};
+format_reply(Socket, #socket_options{active = _, mode = Mode, packet = Packet,
header = Header}, Data) ->
- {ssl, sslsocket(), format_reply(Mode, Packet, Header, Data)}.
+ {ssl, sslsocket(self(), Socket), do_format_reply(Mode, Packet, Header, Data)}.
-deliver_packet_error(SO= #socket_options{active = Active}, Data, Pid, From) ->
- send_or_reply(Active, Pid, From, format_packet_error(SO, Data)).
+deliver_packet_error(Socket, SO= #socket_options{active = Active}, Data, Pid, From) ->
+ send_or_reply(Active, Pid, From, format_packet_error(Socket, SO, Data)).
-format_packet_error(#socket_options{active = false, mode = Mode}, Data) ->
- {error, {invalid_packet, format_reply(Mode, raw, 0, Data)}};
-format_packet_error(#socket_options{active = _, mode = Mode}, Data) ->
- {ssl_error, sslsocket(), {invalid_packet, format_reply(Mode, raw, 0, Data)}}.
+format_packet_error(_,#socket_options{active = false, mode = Mode}, Data) ->
+ {error, {invalid_packet, do_format_reply(Mode, raw, 0, Data)}};
+format_packet_error(Socket, #socket_options{active = _, mode = Mode}, Data) ->
+ {ssl_error, sslsocket(self(), Socket), {invalid_packet, do_format_reply(Mode, raw, 0, Data)}}.
-format_reply(binary, _, N, Data) when N > 0 -> % Header mode
+do_format_reply(binary, _, N, Data) when N > 0 -> % Header mode
header(N, Data);
-format_reply(binary, _, _, Data) ->
+do_format_reply(binary, _, _, Data) ->
Data;
-format_reply(list, Packet, _, Data)
+do_format_reply(list, Packet, _, Data)
when Packet == http; Packet == {http, headers};
Packet == http_bin; Packet == {http_bin, headers};
Packet == httph; Packet == httph_bin ->
Data;
-format_reply(list, _,_, Data) ->
+do_format_reply(list, _,_, Data) ->
binary_to_list(Data).
header(0, <<>>) ->
- <<>>;
+ [];
header(_, <<>>) ->
[];
header(0, Binary) ->
@@ -1897,8 +1967,7 @@ handle_tls_handshake(Handle, StateName, #state{tls_packets = [Packet | Packets]}
end.
next_state(Current,_, #alert{} = Alert, #state{negotiated_version = Version} = State) ->
- handle_own_alert(Alert, Version, Current, State),
- {stop, normal, State};
+ handle_own_alert(Alert, Version, Current, State);
next_state(_,Next, no_record, State) ->
{next_state, Next, State, get_timeout(State)};
@@ -1913,30 +1982,30 @@ next_state(Current, Next, #ssl_tls{type = ?HANDSHAKE, fragment = Data},
fun({#hello_request{} = Packet, _}, {next_state, connection = SName, State}) ->
%% This message should not be included in handshake
%% message hashes. Starts new handshake (renegotiation)
- Hs0 = ssl_handshake:init_hashes(),
- ?MODULE:SName(Packet, State#state{tls_handshake_hashes=Hs0,
+ Hs0 = ssl_handshake:init_handshake_history(),
+ ?MODULE:SName(Packet, State#state{tls_handshake_history=Hs0,
renegotiation = {true, peer}});
({#hello_request{} = Packet, _}, {next_state, SName, State}) ->
%% This message should not be included in handshake
%% message hashes. Already in negotiation so it will be ignored!
?MODULE:SName(Packet, State);
({#client_hello{} = Packet, Raw}, {next_state, connection = SName, State}) ->
- Hs0 = ssl_handshake:init_hashes(),
- Hs1 = ssl_handshake:update_hashes(Hs0, Raw),
- ?MODULE:SName(Packet, State#state{tls_handshake_hashes=Hs1,
+ Version = Packet#client_hello.client_version,
+ Hs0 = ssl_handshake:init_handshake_history(),
+ Hs1 = ssl_handshake:update_handshake_history(Hs0, Raw),
+ ?MODULE:SName(Packet, State#state{tls_handshake_history=Hs1,
renegotiation = {true, peer}});
- ({Packet, Raw}, {next_state, SName, State = #state{tls_handshake_hashes=Hs0}}) ->
- Hs1 = ssl_handshake:update_hashes(Hs0, Raw),
- ?MODULE:SName(Packet, State#state{tls_handshake_hashes=Hs1});
+ ({Packet, Raw}, {next_state, SName, State = #state{tls_handshake_history=Hs0}}) ->
+ Hs1 = ssl_handshake:update_handshake_history(Hs0, Raw),
+ ?MODULE:SName(Packet, State#state{tls_handshake_history=Hs1});
(_, StopState) -> StopState
end,
try
- {Packets, Buf} = ssl_handshake:get_tls_handshake(Data,Buf0),
+ {Packets, Buf} = ssl_handshake:get_tls_handshake(Version,Data,Buf0),
State = State0#state{tls_packets = Packets, tls_handshake_buffer = Buf},
handle_tls_handshake(Handle, Next, State)
catch throw:#alert{} = Alert ->
- handle_own_alert(Alert, Version, Current, State0),
- {stop, normal, State0}
+ handle_own_alert(Alert, Version, Current, State0)
end;
next_state(_, StateName, #ssl_tls{type = ?APPLICATION_DATA, fragment = Data}, State0) ->
@@ -2011,7 +2080,7 @@ next_state_connection(StateName, #state{send_queue = Queue0,
next_state_is_connection(StateName, State)
end.
-%% In next_state_is_connection/1: clear tls_handshake_hashes,
+%% In next_state_is_connection/1: clear tls_handshake,
%% premaster_secret and public_key_info (only needed during handshake)
%% to reduce memory foot print of a connection.
next_state_is_connection(_, State =
@@ -2020,13 +2089,13 @@ next_state_is_connection(_, State =
#socket_options{active = false}}) when RecvFrom =/= undefined ->
passive_receive(State#state{premaster_secret = undefined,
public_key_info = undefined,
- tls_handshake_hashes = {<<>>, <<>>}}, connection);
+ tls_handshake_history = ssl_handshake:init_handshake_history()}, connection);
next_state_is_connection(StateName, State0) ->
{Record, State} = next_record_if_active(State0),
next_state(StateName, connection, Record, State#state{premaster_secret = undefined,
- public_key_info = undefined,
- tls_handshake_hashes = {<<>>, <<>>}}).
+ public_key_info = undefined,
+ tls_handshake_history = ssl_handshake:init_handshake_history()}).
register_session(client, Host, Port, #session{is_resumable = new} = Session0) ->
Session = Session0#session{is_resumable = true},
@@ -2075,7 +2144,6 @@ initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions}, User,
tls_record_buffer = <<>>,
tls_cipher_texts = [],
user_application = {Monitor, User},
- bytes_to_read = 0,
user_data_buffer = <<>>,
log_alert = true,
session_cache_cb = SessionCacheCb,
@@ -2084,11 +2152,8 @@ initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions}, User,
send_queue = queue:new()
}.
-sslsocket(Pid) ->
- #sslsocket{pid = Pid, fd = new_ssl}.
-
-sslsocket() ->
- sslsocket(self()).
+sslsocket(Pid, Socket) ->
+ #sslsocket{pid = Pid, fd = Socket}.
get_socket_opts(_,[], _, Acc) ->
{ok, Acc};
@@ -2184,24 +2249,24 @@ handle_alerts([Alert | Alerts], {next_state, StateName, State, _Timeout}) ->
handle_alerts(Alerts, handle_alert(Alert, StateName, State)).
handle_alert(#alert{level = ?FATAL} = Alert, StateName,
- #state{start_or_recv_from = From, host = Host, port = Port, session = Session,
- user_application = {_Mon, Pid},
+ #state{socket = Socket, start_or_recv_from = From, host = Host,
+ port = Port, session = Session, 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(StateName, Opts, Pid, From, Alert, Role),
+ alert_user(Socket, StateName, Opts, Pid, From, Alert, Role),
{stop, normal, State};
handle_alert(#alert{level = ?WARNING, description = ?CLOSE_NOTIFY} = Alert,
StateName, State) ->
handle_normal_shutdown(Alert, StateName, State),
- {stop, normal, State};
+ {stop, {shutdown, peer_close}, State};
handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert, StateName,
#state{log_alert = Log, renegotiation = {true, internal}} = State) ->
log_alert(Log, StateName, Alert),
handle_normal_shutdown(Alert, StateName, State),
- {stop, normal, State};
+ {stop, {shutdown, peer_close}, State};
handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert, StateName,
#state{log_alert = Log, renegotiation = {true, From}} = State0) ->
@@ -2216,28 +2281,28 @@ handle_alert(#alert{level = ?WARNING, description = ?USER_CANCELED} = Alert, Sta
{Record, State} = next_record(State0),
next_state(StateName, 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(Socket, connection, Opts, Pid, From, Alert, Role) ->
+ alert_user(Socket, Opts#socket_options.active, Pid, From, Alert, Role);
+alert_user(Socket,_, _, _, From, Alert, Role) ->
+ alert_user(Socket, From, Alert, Role).
-alert_user(From, Alert, Role) ->
- alert_user(false, no_pid, From, Alert, Role).
+alert_user(Socket, From, Alert, Role) ->
+ alert_user(Socket, false, no_pid, From, Alert, Role).
-alert_user(false = Active, Pid, From, Alert, Role) ->
+alert_user(_Socket, false = Active, Pid, From, Alert, Role) ->
%% If there is an outstanding ssl_accept | recv
%% From will be defined and send_or_reply will
%% send the appropriate error message.
ReasonCode = ssl_alert:reason_code(Alert, Role),
send_or_reply(Active, Pid, From, {error, ReasonCode});
-alert_user(Active, Pid, From, Alert, Role) ->
+alert_user(Socket, Active, Pid, From, Alert, Role) ->
case ssl_alert:reason_code(Alert, Role) of
closed ->
send_or_reply(Active, Pid, From,
- {ssl_closed, sslsocket()});
+ {ssl_closed, sslsocket(self(), Socket)});
ReasonCode ->
send_or_reply(Active, Pid, From,
- {ssl_error, sslsocket(), ReasonCode})
+ {ssl_error, sslsocket(self(), Socket), ReasonCode})
end.
log_alert(true, Info, Alert) ->
@@ -2254,8 +2319,8 @@ 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)
+ Transport:send(Socket, BinMsg),
+ workaround_transport_delivery_problems(Socket, Transport)
catch _:_ -> %% Can crash if we are in a uninitialized state
ignore
end,
@@ -2264,23 +2329,26 @@ handle_own_alert(Alert, Version, StateName,
handle_normal_shutdown(Alert,StateName, State)
catch _:_ ->
ok
- end.
+ end,
+ {stop, {shutdown, own_alert}, State}.
-handle_normal_shutdown(Alert, _, #state{start_or_recv_from = StartFrom, role = Role, renegotiation = {false, first}}) ->
- alert_user(StartFrom, Alert, Role);
+handle_normal_shutdown(Alert, _, #state{socket = Socket,
+ start_or_recv_from = StartFrom,
+ role = Role, renegotiation = {false, first}}) ->
+ alert_user(Socket, StartFrom, Alert, Role);
-handle_normal_shutdown(Alert, StateName, #state{socket_options = Opts,
+handle_normal_shutdown(Alert, StateName, #state{socket = Socket,
+ socket_options = Opts,
user_application = {_Mon, Pid},
start_or_recv_from = RecvFrom, role = Role}) ->
- alert_user(StateName, Opts, Pid, RecvFrom, Alert, Role).
+ alert_user(Socket, StateName, Opts, Pid, RecvFrom, Alert, Role).
handle_unexpected_message(Msg, Info, #state{negotiated_version = Version} = State) ->
Alert = ?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE),
- handle_own_alert(Alert, Version, {Info, Msg}, State),
- {stop, normal, State}.
+ handle_own_alert(Alert, Version, {Info, Msg}, State).
make_premaster_secret({MajVer, MinVer}, rsa) ->
- Rand = crypto:rand_bytes(?NUM_OF_PREMASTERSECRET_BYTES-2),
+ Rand = ssl:random_bytes(?NUM_OF_PREMASTERSECRET_BYTES-2),
<<?BYTE(MajVer), ?BYTE(MinVer), Rand/binary>>;
make_premaster_secret(_, _) ->
undefined.
@@ -2298,17 +2366,19 @@ ack_connection(#state{renegotiation = {true, From}} = State) ->
gen_fsm:reply(From, ok),
State#state{renegotiation = undefined};
ack_connection(#state{renegotiation = {false, first},
- start_or_recv_from = StartFrom} = State) when StartFrom =/= undefined ->
+ start_or_recv_from = StartFrom,
+ timer = Timer} = State) when StartFrom =/= undefined ->
gen_fsm:reply(StartFrom, connected),
- State#state{renegotiation = undefined, start_or_recv_from = undefined};
+ cancel_timer(Timer),
+ State#state{renegotiation = undefined, start_or_recv_from = undefined, timer = undefined};
ack_connection(State) ->
State.
renegotiate(#state{role = client} = State) ->
%% Handle same way as if server requested
%% the renegotiation
- Hs0 = ssl_handshake:init_hashes(),
- connection(#hello_request{}, State#state{tls_handshake_hashes = Hs0});
+ Hs0 = ssl_handshake:init_handshake_history(),
+ connection(#hello_request{}, State#state{tls_handshake_history = Hs0});
renegotiate(#state{role = server,
socket = Socket,
transport_cb = Transport,
@@ -2316,13 +2386,13 @@ renegotiate(#state{role = server,
connection_states = ConnectionStates0} = State0) ->
HelloRequest = ssl_handshake:hello_request(),
Frag = ssl_handshake:encode_handshake(HelloRequest, Version),
- Hs0 = ssl_handshake:init_hashes(),
+ Hs0 = ssl_handshake:init_handshake_history(),
{BinMsg, ConnectionStates} =
ssl_record:encode_handshake(Frag, Version, ConnectionStates0),
Transport:send(Socket, BinMsg),
{Record, State} = next_record(State0#state{connection_states =
ConnectionStates,
- tls_handshake_hashes = Hs0}),
+ tls_handshake_history = Hs0}),
next_state(connection, hello, Record, State#state{allow_renegotiate = true}).
notify_senders(SendQueue) ->
@@ -2335,36 +2405,35 @@ notify_renegotiater({true, From}) when not is_atom(From) ->
notify_renegotiater(_) ->
ok.
-terminate_alert(Reason, Version, ConnectionStates) when Reason == normal; Reason == shutdown;
+terminate_alert(Reason, Version, ConnectionStates) when Reason == normal;
Reason == user_close ->
{BinAlert, _} = encode_alert(?ALERT_REC(?WARNING, ?CLOSE_NOTIFY),
Version, ConnectionStates),
BinAlert;
+terminate_alert({shutdown, _}, Version, ConnectionStates) ->
+ {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, _) ->
+workaround_transport_delivery_problems(Socket, gen_tcp = 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.
+ %% data sent to the tcp port is really delivered to the
+ %% peer application before tcp port is closed so that the peer will
+ %% get the correct TLS alert message and not only a transport close.
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.
+ %% Will return when other side has closed or after 30 s
+ %% e.g. we do not want to hang if something goes wrong
+ %% with the network but we want to maximise the odds that
+ %% peer application gets all data sent on the tcp connection.
+ Transport:recv(Socket, 0, 30000);
+workaround_transport_delivery_problems(Socket, Transport) ->
+ Transport:close(Socket).
get_timeout(#state{ssl_options=#ssl_options{hibernate_after = undefined}}) ->
infinity;
@@ -2392,3 +2461,76 @@ handle_trusted_certs_db(#state{cert_db_ref = Ref,
_ ->
ok
end.
+
+get_current_connection_state_prf(CStates, Direction) ->
+ CS = ssl_record:current_connection_state(CStates, Direction),
+ CS#connection_state.security_parameters#security_parameters.prf_algorithm.
+get_pending_connection_state_prf(CStates, Direction) ->
+ CS = ssl_record:pending_connection_state(CStates, Direction),
+ CS#connection_state.security_parameters#security_parameters.prf_algorithm.
+
+connection_hashsign(HashSign = {_, _}, _State) ->
+ HashSign;
+connection_hashsign(_, #state{hashsign_algorithm = HashSign}) ->
+ HashSign.
+
+%% RFC 5246, Sect. 7.4.1.4.1. Signature Algorithms
+%% If the client does not send the signature_algorithms extension, the
+%% server MUST do the following:
+%%
+%% - If the negotiated key exchange algorithm is one of (RSA, DHE_RSA,
+%% DH_RSA, RSA_PSK, ECDH_RSA, ECDHE_RSA), behave as if client had
+%% sent the value {sha1,rsa}.
+%%
+%% - If the negotiated key exchange algorithm is one of (DHE_DSS,
+%% DH_DSS), behave as if the client had sent the value {sha1,dsa}.
+%%
+%% - If the negotiated key exchange algorithm is one of (ECDH_ECDSA,
+%% ECDHE_ECDSA), behave as if the client had sent value {sha1,ecdsa}.
+
+default_hashsign(_Version = {Major, Minor}, KeyExchange)
+ when Major == 3 andalso Minor >= 3 andalso
+ (KeyExchange == rsa orelse
+ KeyExchange == dhe_rsa orelse
+ KeyExchange == dh_rsa) ->
+ {sha, rsa};
+default_hashsign(_Version, KeyExchange)
+ when KeyExchange == rsa;
+ KeyExchange == dhe_rsa;
+ KeyExchange == dh_rsa ->
+ {md5sha, rsa};
+default_hashsign(_Version, KeyExchange)
+ when KeyExchange == dhe_dss;
+ KeyExchange == dh_dss ->
+ {sha, dsa};
+default_hashsign(_Version, KeyExchange)
+ when KeyExchange == dh_anon ->
+ {null, anon}.
+
+start_or_recv_cancel_timer(infinity, _RecvFrom) ->
+ undefined;
+start_or_recv_cancel_timer(Timeout, RecvFrom) ->
+ erlang:send_after(Timeout, self(), {cancel_start_or_recv, RecvFrom}).
+
+cancel_timer(undefined) ->
+ ok;
+cancel_timer(Timer) ->
+ erlang:cancel_timer(Timer).
+
+handle_unrecv_data(StateName, #state{socket = Socket, transport_cb = Transport} = State) ->
+ inet:setopts(Socket, [{active, false}]),
+ case Transport:recv(Socket, 0, 0) of
+ {error, closed} ->
+ ok;
+ {ok, Data} ->
+ handle_close_alert(Data, StateName, State)
+ end.
+
+handle_close_alert(Data, StateName, State0) ->
+ case next_tls_record(Data, State0) of
+ {#ssl_tls{type = ?ALERT, fragment = EncAlerts}, State} ->
+ [Alert|_] = decode_alerts(EncAlerts),
+ handle_normal_shutdown(Alert, StateName, State);
+ _ ->
+ ok
+ end.
diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl
index 06d45966c1..1929370991 100644
--- a/lib/ssl/src/ssl_handshake.erl
+++ b/lib/ssl/src/ssl_handshake.erl
@@ -30,21 +30,21 @@
-include("ssl_internal.hrl").
-include_lib("public_key/include/public_key.hrl").
--export([master_secret/4, client_hello/8, server_hello/4, hello/4,
+-export([master_secret/4, client_hello/8, server_hello/5, hello/4,
hello_request/0, certify/7, certificate/4,
- client_certificate_verify/5, certificate_verify/5,
- certificate_request/3, 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, prf/5]).
+ client_certificate_verify/6, certificate_verify/6, verify_signature/5,
+ certificate_request/3, key_exchange/3, server_key_exchange_hash/2,
+ finished/5, verify_connection/6, get_tls_handshake/3,
+ decode_client_key/3, decode_server_key/3, server_hello_done/0,
+ encode_handshake/2, init_handshake_history/0, update_handshake_history/2,
+ decrypt_premaster_secret/2, prf/5, next_protocol/1]).
-export([dec_hello_extensions/2]).
-type tls_handshake() :: #client_hello{} | #server_hello{} |
#server_hello_done{} | #certificate{} | #certificate_request{} |
#client_key_exchange{} | #finished{} | #certificate_verify{} |
- #hello_request{}.
+ #hello_request{} | #next_protocol{}.
%%====================================================================
%% Internal application API
@@ -77,17 +77,31 @@ client_hello(Host, Port, ConnectionStates,
cipher_suites = cipher_suites(Ciphers, Renegotiation),
compression_methods = ssl_record:compressions(),
random = SecParams#security_parameters.client_random,
+
renegotiation_info =
- renegotiation_info(client, ConnectionStates, Renegotiation)
+ renegotiation_info(client, ConnectionStates, Renegotiation),
+ hash_signs = default_hash_signs(),
+ next_protocol_negotiation =
+ encode_client_protocol_negotiation(SslOpts#ssl_options.next_protocol_selector, Renegotiation)
}.
+encode_protocol(Protocol, Acc) ->
+ Len = byte_size(Protocol),
+ <<Acc/binary, ?BYTE(Len), Protocol/binary>>.
+
+encode_protocols_advertised_on_server(undefined) ->
+ undefined;
+
+encode_protocols_advertised_on_server(Protocols) ->
+ #next_protocol_negotiation{extension_data = lists:foldl(fun encode_protocol/2, <<>>, Protocols)}.
+
%%--------------------------------------------------------------------
-spec server_hello(session_id(), tls_version(), #connection_states{},
- boolean()) -> #server_hello{}.
+ boolean(), [binary()] | undefined) -> #server_hello{}.
%%
%% Description: Creates a server hello message.
%%--------------------------------------------------------------------
-server_hello(SessionId, Version, ConnectionStates, Renegotiation) ->
+server_hello(SessionId, Version, ConnectionStates, Renegotiation, ProtocolsAdvertisedOnServer) ->
Pending = ssl_record:pending_connection_state(ConnectionStates, read),
SecParams = Pending#connection_state.security_parameters,
#server_hello{server_version = Version,
@@ -97,7 +111,8 @@ server_hello(SessionId, Version, ConnectionStates, Renegotiation) ->
random = SecParams#security_parameters.server_random,
session_id = SessionId,
renegotiation_info =
- renegotiation_info(server, ConnectionStates, Renegotiation)
+ renegotiation_info(server, ConnectionStates, Renegotiation),
+ next_protocol_negotiation = encode_protocols_advertised_on_server(ProtocolsAdvertisedOnServer)
}.
%%--------------------------------------------------------------------
@@ -112,28 +127,35 @@ hello_request() ->
%%--------------------------------------------------------------------
-spec hello(#server_hello{} | #client_hello{}, #ssl_options{},
#connection_states{} | {inet:port_number(), #session{}, db_handle(),
- atom(), #connection_states{}, binary()},
- boolean()) -> {tls_version(), session_id(), #connection_states{}}|
- {tls_version(), {resumed | new, #session{}},
- #connection_states{}} | #alert{}.
+ atom(), #connection_states{}, binary()},
+ boolean()) ->
+ {tls_version(), session_id(), #connection_states{}, binary() | undefined}|
+ {tls_version(), {resumed | new, #session{}}, #connection_states{}, list(binary()) | undefined} |
+ #alert{}.
%%
%% Description: Handles a recieved hello message
%%--------------------------------------------------------------------
hello(#server_hello{cipher_suite = CipherSuite, server_version = Version,
compression_method = Compression, random = Random,
- session_id = SessionId, renegotiation_info = Info},
- #ssl_options{secure_renegotiate = SecureRenegotation},
+ session_id = SessionId, renegotiation_info = Info,
+ hash_signs = _HashSigns} = Hello,
+ #ssl_options{secure_renegotiate = SecureRenegotation, next_protocol_selector = NextProtocolSelector},
ConnectionStates0, Renegotiation) ->
-
+ %%TODO: select hash and signature algorigthm
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,
+ hello_pending_connection_states(client, Version, CipherSuite, Random,
Compression, ConnectionStates1),
- {Version, SessionId, ConnectionStates};
+ case handle_next_protocol(Hello, NextProtocolSelector, Renegotiation) of
+ #alert{} = Alert ->
+ Alert;
+ Protocol ->
+ {Version, SessionId, ConnectionStates, Protocol}
+ end;
#alert{} = Alert ->
Alert
end;
@@ -144,9 +166,10 @@ hello(#server_hello{cipher_suite = CipherSuite, server_version = Version,
hello(#client_hello{client_version = ClientVersion, random = Random,
cipher_suites = CipherSuites,
renegotiation_info = Info} = Hello,
- #ssl_options{versions = Versions,
+ #ssl_options{versions = Versions,
secure_renegotiate = SecureRenegotation} = SslOpts,
{Port, Session0, Cache, CacheCb, ConnectionStates0, Cert}, Renegotiation) ->
+%% TODO: select hash and signature algorithm
Version = select_version(ClientVersion, Versions),
case ssl_record:is_acceptable_version(Version) of
true ->
@@ -164,11 +187,17 @@ hello(#client_hello{client_version = ClientVersion, random = Random,
{ok, ConnectionStates1} ->
ConnectionStates =
hello_pending_connection_states(server,
+ Version,
CipherSuite,
Random,
Compression,
ConnectionStates1),
- {Version, {Type, Session}, ConnectionStates};
+ case handle_next_protocol_on_server(Hello, Renegotiation, SslOpts) of
+ #alert{} = Alert ->
+ Alert;
+ ProtocolsToAdvertise ->
+ {Version, {Type, Session}, ConnectionStates, ProtocolsToAdvertise}
+ end;
#alert{} = Alert ->
Alert
end
@@ -215,18 +244,23 @@ certify(#certificate{asn1_certificates = ASN1Certs}, CertDbHandle, CertDbRef,
end, {Role, UserState0}}
end,
- {TrustedErlCert, CertPath} =
- ssl_certificate:trusted_cert_and_path(ASN1Certs, CertDbHandle, 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)
+ try
+ {TrustedErlCert, CertPath} =
+ ssl_certificate:trusted_cert_and_path(ASN1Certs, CertDbHandle, 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
+ catch
+ error:_ ->
+ %% ASN-1 decode of certificate somehow failed
+ ?ALERT_REC(?FATAL, ?CERTIFICATE_UNKNOWN)
end.
%%--------------------------------------------------------------------
@@ -257,57 +291,65 @@ certificate(OwnCert, CertDbHandle, CertDbRef, server) ->
%%--------------------------------------------------------------------
-spec client_certificate_verify(undefined | der_cert(), binary(),
- tls_version(), private_key(),
- {{binary(), binary()},{binary(), binary()}}) ->
+ tls_version(), term(), private_key(),
+ tls_handshake_history()) ->
#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,
- PrivateKey, {Hashes0, _}) ->
+ {HashAlgo, SignAlgo},
+ PrivateKey, {Handshake, _}) ->
case public_key:pkix_is_fixed_dh_cert(OwnCert) of
true ->
?ALERT_REC(?FATAL, ?UNSUPPORTED_CERTIFICATE);
- false ->
- Hashes =
- calc_certificate_verify(Version, MasterSecret,
- alg_oid(PrivateKey), Hashes0),
- Signed = digitally_signed(Hashes, PrivateKey),
- #certificate_verify{signature = Signed}
+ false ->
+ Hashes =
+ calc_certificate_verify(Version, HashAlgo, MasterSecret, Handshake),
+ Signed = digitally_signed(Version, Hashes, HashAlgo, PrivateKey),
+ #certificate_verify{signature = Signed, hashsign_algorithm = {HashAlgo, SignAlgo}}
end.
%%--------------------------------------------------------------------
--spec certificate_verify(binary(), public_key_info(), tls_version(),
- binary(), {_, {binary(), binary()}}) -> valid | #alert{}.
+-spec certificate_verify(binary(), public_key_info(), tls_version(), term(),
+ binary(), tls_handshake_history()) -> valid | #alert{}.
%%
%% Description: Checks that the certificate_verify message is valid.
%%--------------------------------------------------------------------
-certificate_verify(Signature, {?'rsaEncryption'= Algorithm, PublicKey, _}, Version,
- MasterSecret, {_, Hashes0}) ->
- Hashes = calc_certificate_verify(Version, MasterSecret,
- Algorithm, Hashes0),
- case public_key:decrypt_public(Signature, PublicKey,
- [{rsa_pad, rsa_pkcs1_padding}]) of
- Hashes ->
+certificate_verify(Signature, PublicKeyInfo, Version,
+ HashSign = {HashAlgo, _}, MasterSecret, {_, Handshake}) ->
+ Hash = calc_certificate_verify(Version, HashAlgo, MasterSecret, Handshake),
+ case verify_signature(Version, Hash, HashSign, Signature, PublicKeyInfo) of
+ true ->
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.
+%%--------------------------------------------------------------------
+-spec verify_signature(tls_version(), binary(), {term(), term()}, binary(),
+ public_key_info()) -> true | false.
+%%
+%% Description: Checks that a public_key signature is valid.
+%%--------------------------------------------------------------------
+verify_signature(_Version, _Hash, {_HashAlgo, anon}, _Signature, _) ->
+ true;
+verify_signature({3, Minor}, Hash, {HashAlgo, rsa}, Signature, {?rsaEncryption, PubKey, _PubKeyParams})
+ when Minor >= 3 ->
+ public_key:verify({digest, Hash}, HashAlgo, Signature, PubKey);
+verify_signature(_Version, Hash, _HashAlgo, Signature, {?rsaEncryption, PubKey, _PubKeyParams}) ->
+ case public_key:decrypt_public(Signature, PubKey,
+ [{rsa_pad, rsa_pkcs1_padding}]) of
+ Hash -> true;
+ _ -> false
+ end;
+verify_signature(_Version, Hash, {HashAlgo, dsa}, Signature, {?'id-dsa', PublicKey, PublicKeyParams}) ->
+ public_key:verify({digest, Hash}, HashAlgo, Signature, {PublicKey, PublicKeyParams}).
+
%%--------------------------------------------------------------------
-spec certificate_request(#connection_states{}, db_handle(), certdb_ref()) ->
@@ -320,58 +362,64 @@ certificate_request(ConnectionStates, CertDbHandle, CertDbRef) ->
#security_parameters{cipher_suite = CipherSuite}} =
ssl_record:pending_connection_state(ConnectionStates, read),
Types = certificate_types(CipherSuite),
+ HashSigns = default_hash_signs(),
Authorities = certificate_authorities(CertDbHandle, CertDbRef),
#certificate_request{
certificate_types = Types,
+ hashsign_algorithms = HashSigns,
certificate_authorities = Authorities
}.
%%--------------------------------------------------------------------
--spec key_exchange(client | server,
+-spec key_exchange(client | server, tls_version(),
{premaster_secret, binary(), public_key_info()} |
{dh, binary()} |
- {dh, {binary(), binary()}, #'DHParameter'{}, key_algo(),
+ {dh, {binary(), binary()}, #'DHParameter'{}, {HashAlgo::atom(), SignAlgo::atom()},
binary(), binary(), private_key()}) ->
#client_key_exchange{} | #server_key_exchange{}.
%%
%% Description: Creates a keyexchange message.
%%--------------------------------------------------------------------
-key_exchange(client, {premaster_secret, Secret, {_, PublicKey, _}}) ->
+key_exchange(client, _Version, {premaster_secret, Secret, {_, PublicKey, _}}) ->
EncPremasterSecret =
encrypted_premaster_secret(Secret, PublicKey),
#client_key_exchange{exchange_keys = EncPremasterSecret};
-key_exchange(client, {dh, <<?UINT32(Len), PublicKey:Len/binary>>}) ->
+key_exchange(client, _Version, {dh, <<?UINT32(Len), PublicKey:Len/binary>>}) ->
#client_key_exchange{
exchange_keys = #client_diffie_hellman_public{
dh_public = PublicKey}
};
-key_exchange(server, {dh, {<<?UINT32(Len), PublicKey:Len/binary>>, _},
+key_exchange(server, Version, {dh, {<<?UINT32(Len), PublicKey:Len/binary>>, _},
#'DHParameter'{prime = P, base = G},
- KeyAlgo, ClientRandom, ServerRandom, PrivateKey}) ->
+ HashSign, ClientRandom, ServerRandom, PrivateKey}) ->
<<?UINT32(_), PBin/binary>> = crypto:mpint(P),
<<?UINT32(_), GBin/binary>> = crypto:mpint(G),
- PLen = byte_size(PBin),
- GLen = byte_size(GBin),
- YLen = byte_size(PublicKey),
ServerDHParams = #server_dh_params{dh_p = PBin,
dh_g = GBin, dh_y = PublicKey},
-
- case KeyAlgo of
- dh_anon ->
- #server_key_exchange{params = ServerDHParams,
- signed_params = <<>>};
+ enc_server_key_exchange(Version, ServerDHParams, HashSign,
+ ClientRandom, ServerRandom, PrivateKey).
+
+enc_server_key_exchange(Version, Params, {HashAlgo, SignAlgo},
+ ClientRandom, ServerRandom, PrivateKey) ->
+ EncParams = enc_server_key(Params),
+ case HashAlgo of
+ null ->
+ #server_key_params{params = Params,
+ params_bin = EncParams,
+ hashsign = {null, anon},
+ signature = <<>>};
_ ->
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}
+ server_key_exchange_hash(HashAlgo, <<ClientRandom/binary,
+ ServerRandom/binary,
+ EncParams/binary>>),
+ Signature = digitally_signed(Version, Hash, HashAlgo, PrivateKey),
+ #server_key_params{params = Params,
+ params_bin = EncParams,
+ hashsign = {HashAlgo, SignAlgo},
+ signature = Signature}
end.
%%--------------------------------------------------------------------
@@ -401,10 +449,11 @@ master_secret(Version, PremasterSecret, ConnectionStates, Role) ->
ConnectionState =
ssl_record:pending_connection_state(ConnectionStates, read),
SecParams = ConnectionState#connection_state.security_parameters,
- #security_parameters{client_random = ClientRandom,
+ #security_parameters{prf_algorithm = PrfAlgo,
+ client_random = ClientRandom,
server_random = ServerRandom} = SecParams,
try master_secret(Version,
- calc_master_secret(Version,PremasterSecret,
+ calc_master_secret(Version,PrfAlgo,PremasterSecret,
ClientRandom, ServerRandom),
SecParams, ConnectionStates, Role)
catch
@@ -415,27 +464,32 @@ master_secret(Version, PremasterSecret, ConnectionStates, Role) ->
?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE)
end.
+-spec next_protocol(binary()) -> #next_protocol{}.
+
+next_protocol(SelectedProtocol) ->
+ #next_protocol{selected_protocol = SelectedProtocol}.
+
%%--------------------------------------------------------------------
--spec finished(tls_version(), client | server, binary(), {{binary(), binary()},_}) ->
+-spec finished(tls_version(), client | server, integer(), binary(), tls_handshake_history()) ->
#finished{}.
%%
%% Description: Creates a handshake finished message
%%-------------------------------------------------------------------
-finished(Version, Role, MasterSecret, {Hashes, _}) -> % use the current hashes
+finished(Version, Role, PrfAlgo, MasterSecret, {Handshake, _}) -> % use the current handshake
#finished{verify_data =
- calc_finished(Version, Role, MasterSecret, Hashes)}.
+ calc_finished(Version, Role, PrfAlgo, MasterSecret, Handshake)}.
%%--------------------------------------------------------------------
--spec verify_connection(tls_version(), #finished{}, client | server, binary(),
- {_, {binary(), binary()}}) -> verified | #alert{}.
+-spec verify_connection(tls_version(), #finished{}, client | server, integer(), binary(),
+ tls_handshake_history()) -> verified | #alert{}.
%%
%% Description: Checks the ssl handshake finished message to verify
%% the connection.
%%-------------------------------------------------------------------
verify_connection(Version, #finished{verify_data = Data},
- Role, MasterSecret, {_, {MD5, SHA}}) ->
+ Role, PrfAlgo, MasterSecret, {_, Handshake}) ->
%% use the previous hashes
- case calc_finished(Version, Role, MasterSecret, {MD5, SHA}) of
+ case calc_finished(Version, Role, PrfAlgo, MasterSecret, Handshake) of
Data ->
verified;
_ ->
@@ -460,17 +514,17 @@ encode_handshake(Package, Version) ->
[MsgType, ?uint24(Len), Bin].
%%--------------------------------------------------------------------
--spec get_tls_handshake(binary(), binary() | iolist()) ->
+-spec get_tls_handshake(tls_version(), binary(), binary() | iolist()) ->
{[tls_handshake()], binary()}.
%%
%% Description: Given buffered and new data from ssl_record, collects
%% and returns it as a list of handshake messages, also returns leftover
%% data.
%%--------------------------------------------------------------------
-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(Version, Data, <<>>) ->
+ get_tls_handshake_aux(Version, Data, []);
+get_tls_handshake(Version, Data, Buffer) ->
+ get_tls_handshake_aux(Version, list_to_binary([Buffer, Data]), []).
%%--------------------------------------------------------------------
-spec decode_client_key(binary(), key_algo(), tls_version()) ->
@@ -482,39 +536,43 @@ decode_client_key(ClientKey, Type, Version) ->
dec_client_key(ClientKey, key_exchange_alg(Type), Version).
%%--------------------------------------------------------------------
--spec init_hashes() ->{{binary(), binary()}, {binary(), binary()}}.
+-spec decode_server_key(binary(), key_algo(), tls_version()) ->
+ #server_key_params{}.
+%%
+%% Description: Decode server_key data and return appropriate type
+%%--------------------------------------------------------------------
+decode_server_key(ServerKey, Type, Version) ->
+ dec_server_key(ServerKey, key_exchange_alg(Type), Version).
+
+%%--------------------------------------------------------------------
+-spec init_handshake_history() -> tls_handshake_history().
%%
-%% Description: Calls crypto hash (md5 and sha) init functions to
-%% initalize the hash context.
+%% Description: Initialize the empty handshake history buffer.
%%--------------------------------------------------------------------
-init_hashes() ->
- T = {crypto:md5_init(), crypto:sha_init()},
- {T, T}.
+init_handshake_history() ->
+ {[], []}.
%%--------------------------------------------------------------------
--spec update_hashes({{binary(), binary()}, {binary(), binary()}}, Data ::term()) ->
- {{binary(), binary()}, {binary(), binary()}}.
+-spec update_handshake_history(tls_handshake_history(), Data ::term()) ->
+ tls_handshake_history().
%%
-%% Description: Calls crypto hash (md5 and sha) update functions to
-%% update the hash context with Data.
+%% Description: Update the handshake history buffer 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}}.
+update_handshake_history(Handshake, % 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_handshake_history(Handshake,
+ <<?CLIENT_HELLO, ?BYTE(Major), ?BYTE(Minor),
+ ?UINT16(CSLength), ?UINT16(0),
+ ?UINT16(CDLength),
+ CipherSuites:CSLength/binary,
+ ChallengeData:CDLength/binary>>);
+update_handshake_history({Handshake0, _Prev}, Data) ->
+ {[Data|Handshake0], Handshake0}.
%%--------------------------------------------------------------------
-spec decrypt_premaster_secret(binary(), #'RSAPrivateKey'{}) -> binary().
@@ -527,23 +585,22 @@ decrypt_premaster_secret(Secret, RSAPrivateKey) ->
[{rsa_pad, rsa_pkcs1_padding}])
catch
_:_ ->
+ io:format("decrypt_premaster_secret error"),
throw(?ALERT_REC(?FATAL, ?DECRYPT_ERROR))
end.
%%--------------------------------------------------------------------
--spec server_key_exchange_hash(rsa | dhe_rsa| dhe_dss | dh_anon, binary()) -> binary().
-
+-spec server_key_exchange_hash(md5sha | md5 | sha | sha224 |sha256 | sha384 | sha512, binary()) -> binary().
%%
%% Description: Calculate server key exchange hash
%%--------------------------------------------------------------------
-server_key_exchange_hash(Algorithm, Value) when Algorithm == rsa;
- Algorithm == dhe_rsa ->
+server_key_exchange_hash(md5sha, Value) ->
MD5 = crypto:md5(Value),
- SHA = crypto:sha(Value),
+ SHA = crypto:sha(Value),
<<MD5/binary, SHA/binary>>;
-server_key_exchange_hash(dhe_dss, Value) ->
- crypto:sha(Value).
+server_key_exchange_hash(Hash, Value) ->
+ crypto:hash(Hash, Value).
%%--------------------------------------------------------------------
-spec prf(tls_version(), binary(), binary(), [binary()], non_neg_integer()) ->
@@ -553,19 +610,20 @@ server_key_exchange_hash(dhe_dss, Value) ->
%%--------------------------------------------------------------------
prf({3,0}, _, _, _, _) ->
{error, undefined};
-prf({3,N}, Secret, Label, Seed, WantedLength)
- when N == 1; N == 2 ->
- {ok, ssl_tls1:prf(Secret, Label, Seed, WantedLength)}.
+prf({3,1}, Secret, Label, Seed, WantedLength) ->
+ {ok, ssl_tls1:prf(?MD5SHA, Secret, Label, Seed, WantedLength)};
+prf({3,_N}, Secret, Label, Seed, WantedLength) ->
+ {ok, ssl_tls1:prf(?SHA256, Secret, Label, Seed, WantedLength)}.
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
-get_tls_handshake_aux(<<?BYTE(Type), ?UINT24(Length),
+get_tls_handshake_aux(Version, <<?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) ->
+ H = dec_hs(Version, Type, Body),
+ get_tls_handshake_aux(Version, Rest, [{H,Raw} | Acc]);
+get_tls_handshake_aux(_Version, Data, Acc) ->
{lists:reverse(Acc), Data}.
path_validation_alert({bad_cert, cert_expired}) ->
@@ -653,6 +711,57 @@ renegotiation_info(server, ConnectionStates, true) ->
#renegotiation_info{renegotiated_connection = undefined}
end.
+decode_next_protocols({next_protocol_negotiation, Protocols}) ->
+ decode_next_protocols(Protocols, []).
+decode_next_protocols(<<>>, Acc) ->
+ lists:reverse(Acc);
+decode_next_protocols(<<?BYTE(Len), Protocol:Len/binary, Rest/binary>>, Acc) ->
+ case Len of
+ 0 ->
+ {error, invalid_next_protocols};
+ _ ->
+ decode_next_protocols(Rest, [Protocol|Acc])
+ end;
+decode_next_protocols(_Bytes, _Acc) ->
+ {error, invalid_next_protocols}.
+
+next_protocol_extension_allowed(NextProtocolSelector, Renegotiating) ->
+ NextProtocolSelector =/= undefined andalso not Renegotiating.
+
+handle_next_protocol_on_server(#client_hello{next_protocol_negotiation = undefined}, _Renegotiation, _SslOpts) ->
+ undefined;
+
+handle_next_protocol_on_server(#client_hello{next_protocol_negotiation = {next_protocol_negotiation, <<>>}},
+ false, #ssl_options{next_protocols_advertised = Protocols}) ->
+ Protocols;
+
+handle_next_protocol_on_server(_Hello, _Renegotiation, _SSLOpts) ->
+ ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE). % unexpected next protocol extension
+
+handle_next_protocol(#server_hello{next_protocol_negotiation = undefined},
+ _NextProtocolSelector, _Renegotiating) ->
+ undefined;
+
+handle_next_protocol(#server_hello{next_protocol_negotiation = Protocols},
+ NextProtocolSelector, Renegotiating) ->
+
+ case next_protocol_extension_allowed(NextProtocolSelector, Renegotiating) of
+ true ->
+ select_next_protocol(decode_next_protocols(Protocols), NextProtocolSelector);
+ false ->
+ ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE) % unexpected next protocol extension
+ end.
+
+select_next_protocol({error, _Reason}, _NextProtocolSelector) ->
+ ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE);
+select_next_protocol(Protocols, NextProtocolSelector) ->
+ case NextProtocolSelector(Protocols) of
+ ?NO_PROTOCOL ->
+ ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE);
+ Protocol when is_binary(Protocol) ->
+ Protocol
+ end.
+
handle_renegotiation_info(_, #renegotiation_info{renegotiated_connection = ?byte(0)},
ConnectionStates, false, _, _) ->
{ok, ssl_record:set_renegotiation_flag(true, ConnectionStates)};
@@ -722,7 +831,7 @@ handle_renegotiation_info(ConnectionStates, SecureRenegotation) ->
%% hello messages
%% NOTE : Role is the role of the receiver of the hello message
%% currently being processed.
-hello_pending_connection_states(Role, CipherSuite, Random, Compression,
+hello_pending_connection_states(Role, Version, CipherSuite, Random, Compression,
ConnectionStates) ->
ReadState =
ssl_record:pending_connection_state(ConnectionStates, read),
@@ -730,30 +839,30 @@ hello_pending_connection_states(Role, CipherSuite, Random, Compression,
ssl_record:pending_connection_state(ConnectionStates, write),
NewReadSecParams =
- hello_security_parameters(Role, ReadState, CipherSuite,
+ hello_security_parameters(Role, Version, ReadState, CipherSuite,
Random, Compression),
NewWriteSecParams =
- hello_security_parameters(Role, WriteState, CipherSuite,
+ hello_security_parameters(Role, Version, WriteState, CipherSuite,
Random, Compression),
ssl_record:update_security_params(NewReadSecParams,
NewWriteSecParams,
ConnectionStates).
-hello_security_parameters(client, ConnectionState, CipherSuite, Random,
+hello_security_parameters(client, Version, ConnectionState, CipherSuite, Random,
Compression) ->
SecParams = ConnectionState#connection_state.security_parameters,
- NewSecParams = ssl_cipher:security_parameters(CipherSuite, SecParams),
+ NewSecParams = ssl_cipher:security_parameters(Version, CipherSuite, SecParams),
NewSecParams#security_parameters{
server_random = Random,
compression_algorithm = Compression
};
-hello_security_parameters(server, ConnectionState, CipherSuite, Random,
+hello_security_parameters(server, Version, ConnectionState, CipherSuite, Random,
Compression) ->
SecParams = ConnectionState#connection_state.security_parameters,
- NewSecParams = ssl_cipher:security_parameters(CipherSuite, SecParams),
+ NewSecParams = ssl_cipher:security_parameters(Version, CipherSuite, SecParams),
NewSecParams#security_parameters{
client_random = Random,
compression_algorithm = Compression
@@ -787,13 +896,14 @@ master_secret(Version, MasterSecret, #security_parameters{
client_random = ClientRandom,
server_random = ServerRandom,
hash_size = HashSize,
+ prf_algorithm = PrfAlgo,
key_material_length = KML,
expanded_key_material_length = EKML,
iv_size = IVS},
ConnectionStates, Role) ->
{ClientWriteMacSecret, ServerWriteMacSecret, ClientWriteKey,
ServerWriteKey, ClientIV, ServerIV} =
- setup_keys(Version, MasterSecret, ServerRandom,
+ setup_keys(Version, PrfAlgo, MasterSecret, ServerRandom,
ClientRandom, HashSize, KML, EKML, IVS),
ConnStates1 = ssl_record:set_master_secret(MasterSecret, ConnectionStates),
@@ -808,17 +918,21 @@ master_secret(Version, MasterSecret, #security_parameters{
ServerCipherState, Role)}.
-dec_hs(?HELLO_REQUEST, <<>>) ->
+dec_hs(_, ?NEXT_PROTOCOL, <<?BYTE(SelectedProtocolLength), SelectedProtocol:SelectedProtocolLength/binary,
+ ?BYTE(PaddingLength), _Padding:PaddingLength/binary>>) ->
+ #next_protocol{selected_protocol = SelectedProtocol};
+
+dec_hs(_, ?HELLO_REQUEST, <<>>) ->
#hello_request{};
%% Client hello v2.
%% The server must be able to receive such messages, from clients that
%% are willing to use ssl v3 or higher, but have ssl v2 compatibility.
-dec_hs(?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor),
- ?UINT16(CSLength), ?UINT16(0),
- ?UINT16(CDLength),
- CipherSuites:CSLength/binary,
- ChallengeData:CDLength/binary>>) ->
+dec_hs(_Version, ?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor),
+ ?UINT16(CSLength), ?UINT16(0),
+ ?UINT16(CDLength),
+ CipherSuites:CSLength/binary,
+ ChallengeData:CDLength/binary>>) ->
#client_hello{client_version = {Major, Minor},
random = ssl_ssl2:client_random(ChallengeData, CDLength),
session_id = 0,
@@ -826,24 +940,29 @@ dec_hs(?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor),
compression_methods = [?NULL],
renegotiation_info = undefined
};
-dec_hs(?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
+dec_hs(_Version, ?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>>) ->
-
- RenegotiationInfo = proplists:get_value(renegotiation_info, dec_hello_extensions(Extensions),
- undefined),
- #client_hello{
- client_version = {Major,Minor},
- random = Random,
- session_id = Session_ID,
- cipher_suites = from_2bytes(CipherSuites),
- compression_methods = Comp_methods,
- renegotiation_info = RenegotiationInfo
- };
-dec_hs(?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
+ DecodedExtensions = dec_hello_extensions(Extensions),
+ RenegotiationInfo = proplists:get_value(renegotiation_info, DecodedExtensions, undefined),
+ HashSigns = proplists:get_value(hash_signs, DecodedExtensions, undefined),
+ NextProtocolNegotiation = proplists:get_value(next_protocol_negotiation, DecodedExtensions, undefined),
+
+ #client_hello{
+ client_version = {Major,Minor},
+ random = Random,
+ session_id = Session_ID,
+ cipher_suites = from_2bytes(CipherSuites),
+ compression_methods = Comp_methods,
+ renegotiation_info = RenegotiationInfo,
+ hash_signs = HashSigns,
+ next_protocol_negotiation = NextProtocolNegotiation
+ };
+
+dec_hs(_Version, ?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
?BYTE(SID_length), Session_ID:SID_length/binary,
Cipher_suite:2/binary, ?BYTE(Comp_method)>>) ->
#server_hello{
@@ -852,53 +971,61 @@ dec_hs(?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
session_id = Session_ID,
cipher_suite = Cipher_suite,
compression_method = Comp_method,
- renegotiation_info = undefined};
+ renegotiation_info = undefined,
+ hash_signs = undefined};
-dec_hs(?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
+dec_hs(_Version, ?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),
?UINT16(ExtLen), Extensions:ExtLen/binary>>) ->
- RenegotiationInfo = proplists:get_value(renegotiation_info, dec_hello_extensions(Extensions, []),
- undefined),
+ HelloExtensions = dec_hello_extensions(Extensions, []),
+ RenegotiationInfo = proplists:get_value(renegotiation_info, HelloExtensions,
+ undefined),
+ HashSigns = proplists:get_value(hash_signs, HelloExtensions,
+ undefined),
+ NextProtocolNegotiation = proplists:get_value(next_protocol_negotiation, HelloExtensions, undefined),
+
#server_hello{
server_version = {Major,Minor},
random = Random,
session_id = Session_ID,
cipher_suite = Cipher_suite,
compression_method = Comp_method,
- renegotiation_info = RenegotiationInfo};
-dec_hs(?CERTIFICATE, <<?UINT24(ACLen), ASN1Certs:ACLen/binary>>) ->
+ renegotiation_info = RenegotiationInfo,
+ hash_signs = HashSigns,
+ next_protocol_negotiation = NextProtocolNegotiation};
+dec_hs(_Version, ?CERTIFICATE, <<?UINT24(ACLen), ASN1Certs:ACLen/binary>>) ->
#certificate{asn1_certificates = certs_to_list(ASN1Certs)};
-
-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(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,
+dec_hs(_Version, ?SERVER_KEY_EXCHANGE, Keys) ->
+ #server_key_exchange{exchange_keys = Keys};
+dec_hs({Major, Minor}, ?CERTIFICATE_REQUEST,
+ <<?BYTE(CertTypesLen), CertTypes:CertTypesLen/binary,
+ ?UINT16(HashSignsLen), HashSigns:HashSignsLen/binary,
+ ?UINT16(CertAuthsLen), CertAuths:CertAuthsLen/binary>>)
+ when Major == 3, Minor >= 3 ->
+ HashSignAlgos = [{ssl_cipher:hash_algorithm(Hash), ssl_cipher:sign_algorithm(Sign)} ||
+ <<?BYTE(Hash), ?BYTE(Sign)>> <= HashSigns],
+ #certificate_request{certificate_types = CertTypes,
+ hashsign_algorithms = #hash_sign_algos{hash_sign_algos = HashSignAlgos},
+ certificate_authorities = CertAuths};
+dec_hs(_Version, ?CERTIFICATE_REQUEST,
<<?BYTE(CertTypesLen), CertTypes:CertTypesLen/binary,
?UINT16(CertAuthsLen), CertAuths:CertAuthsLen/binary>>) ->
#certificate_request{certificate_types = CertTypes,
certificate_authorities = CertAuths};
-dec_hs(?SERVER_HELLO_DONE, <<>>) ->
+dec_hs(_Version, ?SERVER_HELLO_DONE, <<>>) ->
#server_hello_done{};
-dec_hs(?CERTIFICATE_VERIFY,<<?UINT16(_), Signature/binary>>)->
+dec_hs({Major, Minor}, ?CERTIFICATE_VERIFY,<<HashSign:2/binary, ?UINT16(SignLen), Signature:SignLen/binary>>)
+ when Major == 3, Minor >= 3 ->
+ #certificate_verify{hashsign_algorithm = hashsign_dec(HashSign), signature = Signature};
+dec_hs(_Version, ?CERTIFICATE_VERIFY,<<?UINT16(SignLen), Signature:SignLen/binary>>)->
#certificate_verify{signature = Signature};
-dec_hs(?CLIENT_KEY_EXCHANGE, PKEPMS) ->
+dec_hs(_Version, ?CLIENT_KEY_EXCHANGE, PKEPMS) ->
#client_key_exchange{exchange_keys = PKEPMS};
-dec_hs(?FINISHED, VerifyData) ->
+dec_hs(_Version, ?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}) ->
@@ -911,6 +1038,42 @@ dec_client_key(<<?UINT16(DH_YLen), DH_Y:DH_YLen/binary>>,
?KEY_EXCHANGE_DIFFIE_HELLMAN, _) ->
#client_diffie_hellman_public{dh_public = DH_Y}.
+dec_ske_params(Len, Keys, Version) ->
+ <<Params:Len/bytes, Signature/binary>> = Keys,
+ dec_ske_signature(Params, Signature, Version).
+
+dec_ske_signature(Params, <<?BYTE(HashAlgo), ?BYTE(SignAlgo),
+ ?UINT16(0)>>, {Major, Minor})
+ when Major == 3, Minor >= 3 ->
+ HashSign = {ssl_cipher:hash_algorithm(HashAlgo), ssl_cipher:sign_algorithm(SignAlgo)},
+ {Params, HashSign, <<>>};
+dec_ske_signature(Params, <<?BYTE(HashAlgo), ?BYTE(SignAlgo),
+ ?UINT16(Len), Signature:Len/binary>>, {Major, Minor})
+ when Major == 3, Minor >= 3 ->
+ HashSign = {ssl_cipher:hash_algorithm(HashAlgo), ssl_cipher:sign_algorithm(SignAlgo)},
+ {Params, HashSign, Signature};
+dec_ske_signature(Params, <<>>, _) ->
+ {Params, {null, anon}, <<>>};
+dec_ske_signature(Params, <<?UINT16(0)>>, _) ->
+ {Params, {null, anon}, <<>>};
+dec_ske_signature(Params, <<?UINT16(Len), Signature:Len/binary>>, _) ->
+ {Params, undefined, Signature};
+dec_ske_signature(_, _, _) ->
+ throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE)).
+
+dec_server_key(<<?UINT16(PLen), P:PLen/binary,
+ ?UINT16(GLen), G:GLen/binary,
+ ?UINT16(YLen), Y:YLen/binary, _/binary>> = KeyStruct,
+ ?KEY_EXCHANGE_DIFFIE_HELLMAN, Version) ->
+ Params = #server_dh_params{dh_p = P, dh_g = G, dh_y = Y},
+ {BinMsg, HashSign, Signature} = dec_ske_params(PLen + GLen + YLen + 6, KeyStruct, Version),
+ #server_key_params{params = Params,
+ params_bin = BinMsg,
+ hashsign = HashSign,
+ signature = Signature};
+dec_server_key(_, _, _) ->
+ throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE)).
+
dec_hello_extensions(<<>>) ->
[];
dec_hello_extensions(<<?UINT16(ExtLen), Extensions:ExtLen/binary>>) ->
@@ -920,6 +1083,9 @@ dec_hello_extensions(_) ->
dec_hello_extensions(<<>>, Acc) ->
Acc;
+dec_hello_extensions(<<?UINT16(?NEXTPROTONEG_EXT), ?UINT16(Len), ExtensionData:Len/binary, Rest/binary>>, Acc) ->
+ Prop = {next_protocol_negotiation, #next_protocol_negotiation{extension_data = ExtensionData}},
+ dec_hello_extensions(Rest, [Prop | Acc]);
dec_hello_extensions(<<?UINT16(?RENEGOTIATION_EXT), ?UINT16(Len), Info:Len/binary, Rest/binary>>, Acc) ->
RenegotiateInfo = case Len of
1 -> % Initial handshake
@@ -932,8 +1098,18 @@ dec_hello_extensions(<<?UINT16(?RENEGOTIATION_EXT), ?UINT16(Len), Info:Len/binar
dec_hello_extensions(Rest, [{renegotiation_info,
#renegotiation_info{renegotiated_connection = RenegotiateInfo}} | Acc]);
+dec_hello_extensions(<<?UINT16(?SIGNATURE_ALGORITHMS_EXT), ?UINT16(Len),
+ ExtData:Len/binary, Rest/binary>>, Acc) ->
+ SignAlgoListLen = Len - 2,
+ <<?UINT16(SignAlgoListLen), SignAlgoList/binary>> = ExtData,
+ HashSignAlgos = [{ssl_cipher:hash_algorithm(Hash), ssl_cipher:sign_algorithm(Sign)} ||
+ <<?BYTE(Hash), ?BYTE(Sign)>> <= SignAlgoList],
+ dec_hello_extensions(Rest, [{hash_signs,
+ #hash_sign_algos{hash_sign_algos = HashSignAlgos}} | Acc]);
+
%% Ignore data following the ClientHello (i.e.,
%% extensions) if not understood.
+
dec_hello_extensions(<<?UINT16(_), ?UINT16(Len), _Unknown:Len/binary, Rest/binary>>, Acc) ->
dec_hello_extensions(Rest, Acc);
%% This theoretically should not happen if the protocol is followed, but if it does it is ignored.
@@ -966,6 +1142,11 @@ certs_from_list(ACList) ->
<<?UINT24(CertLen), Cert/binary>>
end || Cert <- ACList]).
+enc_hs(#next_protocol{selected_protocol = SelectedProtocol}, _Version) ->
+ PaddingLength = 32 - ((byte_size(SelectedProtocol) + 2) rem 32),
+
+ {?NEXT_PROTOCOL, <<?BYTE((byte_size(SelectedProtocol))), SelectedProtocol/binary,
+ ?BYTE(PaddingLength), 0:(PaddingLength * 8)>>};
enc_hs(#hello_request{}, _Version) ->
{?HELLO_REQUEST, <<>>};
enc_hs(#client_hello{client_version = {Major, Minor},
@@ -973,15 +1154,22 @@ enc_hs(#client_hello{client_version = {Major, Minor},
session_id = SessionID,
cipher_suites = CipherSuites,
compression_methods = CompMethods,
- renegotiation_info = RenegotiationInfo}, _Version) ->
+ renegotiation_info = RenegotiationInfo,
+ hash_signs = HashSigns,
+ next_protocol_negotiation = NextProtocolNegotiation}, _Version) ->
SIDLength = byte_size(SessionID),
BinCompMethods = list_to_binary(CompMethods),
CmLength = byte_size(BinCompMethods),
BinCipherSuites = list_to_binary(CipherSuites),
CsLength = byte_size(BinCipherSuites),
- Extensions = hello_extensions(RenegotiationInfo),
- ExtensionsBin = enc_hello_extensions(Extensions),
- {?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
+ Extensions0 = hello_extensions(RenegotiationInfo, NextProtocolNegotiation),
+ Extensions1 = if
+ Major == 3, Minor >=3 -> Extensions0 ++ hello_extensions(HashSigns);
+ true -> Extensions0
+ end,
+ ExtensionsBin = enc_hello_extensions(Extensions1),
+
+ {?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
?BYTE(SIDLength), SessionID/binary,
?UINT16(CsLength), BinCipherSuites/binary,
?BYTE(CmLength), BinCompMethods/binary, ExtensionsBin/binary>>};
@@ -991,9 +1179,10 @@ 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,
+ next_protocol_negotiation = NextProtocolNegotiation}, _Version) ->
SID_length = byte_size(Session_ID),
- Extensions = hello_extensions(RenegotiationInfo),
+ Extensions = hello_extensions(RenegotiationInfo, NextProtocolNegotiation),
ExtensionsBin = enc_hello_extensions(Extensions),
{?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
?BYTE(SID_length), Session_ID/binary,
@@ -1002,17 +1191,26 @@ enc_hs(#certificate{asn1_certificates = ASN1CertList}, _Version) ->
ASN1Certs = certs_from_list(ASN1CertList),
ACLen = erlang:iolist_size(ASN1Certs),
{?CERTIFICATE, <<?UINT24(ACLen), ASN1Certs:ACLen/binary>>};
-enc_hs(#server_key_exchange{params = #server_dh_params{
- dh_p = P, dh_g = G, dh_y = Y},
- signed_params = SignedParams}, _Version) ->
- PLen = byte_size(P),
- GLen = byte_size(G),
- YLen = byte_size(Y),
- SignedLen = byte_size(SignedParams),
- {?SERVER_KEY_EXCHANGE, <<?UINT16(PLen), P/binary,
- ?UINT16(GLen), G/binary,
- ?UINT16(YLen), Y/binary,
- ?UINT16(SignedLen), SignedParams/binary>>
+enc_hs(#server_key_exchange{exchange_keys = Keys}, _Version) ->
+ {?SERVER_KEY_EXCHANGE, Keys};
+enc_hs(#server_key_params{params_bin = Keys, hashsign = HashSign,
+ signature = Signature}, Version) ->
+ EncSign = enc_sign(HashSign, Signature, Version),
+ {?SERVER_KEY_EXCHANGE, <<Keys/binary, EncSign/binary>>};
+enc_hs(#certificate_request{certificate_types = CertTypes,
+ hashsign_algorithms = #hash_sign_algos{hash_sign_algos = HashSignAlgos},
+ certificate_authorities = CertAuths},
+ {Major, Minor})
+ when Major == 3, Minor >= 3 ->
+ HashSigns= << <<(ssl_cipher:hash_algorithm(Hash)):8, (ssl_cipher:sign_algorithm(Sign)):8>> ||
+ {Hash, Sign} <- HashSignAlgos >>,
+ CertTypesLen = byte_size(CertTypes),
+ HashSignsLen = byte_size(HashSigns),
+ CertAuthsLen = byte_size(CertAuths),
+ {?CERTIFICATE_REQUEST,
+ <<?BYTE(CertTypesLen), CertTypes/binary,
+ ?UINT16(HashSignsLen), HashSigns/binary,
+ ?UINT16(CertAuthsLen), CertAuths/binary>>
};
enc_hs(#certificate_request{certificate_types = CertTypes,
certificate_authorities = CertAuths},
@@ -1027,8 +1225,8 @@ enc_hs(#server_hello_done{}, _Version) ->
{?SERVER_HELLO_DONE, <<>>};
enc_hs(#client_key_exchange{exchange_keys = ExchangeKeys}, Version) ->
{?CLIENT_KEY_EXCHANGE, enc_cke(ExchangeKeys, Version)};
-enc_hs(#certificate_verify{signature = BinSig}, _) ->
- EncSig = enc_bin_sig(BinSig),
+enc_hs(#certificate_verify{signature = BinSig, hashsign_algorithm = HashSign}, Version) ->
+ EncSig = enc_sign(HashSign, BinSig, Version),
{?CERTIFICATE_VERIFY, EncSig};
enc_hs(#finished{verify_data = VerifyData}, _Version) ->
{?FINISHED, VerifyData}.
@@ -1042,14 +1240,37 @@ enc_cke(#client_diffie_hellman_public{dh_public = DHPublic}, _) ->
Len = byte_size(DHPublic),
<<?UINT16(Len), DHPublic/binary>>.
-enc_bin_sig(BinSig) ->
- Size = byte_size(BinSig),
- <<?UINT16(Size), BinSig/binary>>.
+enc_server_key(#server_dh_params{dh_p = P, dh_g = G, dh_y = Y}) ->
+ PLen = byte_size(P),
+ GLen = byte_size(G),
+ YLen = byte_size(Y),
+ <<?UINT16(PLen), P/binary, ?UINT16(GLen), G/binary, ?UINT16(YLen), Y/binary>>.
-%% Renegotiation info, only current extension
+enc_sign({_, anon}, _Sign, _Version) ->
+ <<>>;
+enc_sign({HashAlg, SignAlg}, Signature, _Version = {Major, Minor})
+ when Major == 3, Minor >= 3->
+ SignLen = byte_size(Signature),
+ HashSign = hashsign_enc(HashAlg, SignAlg),
+ <<HashSign/binary, ?UINT16(SignLen), Signature/binary>>;
+enc_sign(_HashSign, Sign, _Version) ->
+ SignLen = byte_size(Sign),
+ <<?UINT16(SignLen), Sign/binary>>.
+
+hello_extensions(RenegotiationInfo, NextProtocolNegotiation) ->
+ hello_extensions(RenegotiationInfo) ++ next_protocol_extension(NextProtocolNegotiation).
+
+%% Renegotiation info
hello_extensions(#renegotiation_info{renegotiated_connection = undefined}) ->
[];
hello_extensions(#renegotiation_info{} = Info) ->
+ [Info];
+hello_extensions(#hash_sign_algos{} = Info) ->
+ [Info].
+
+next_protocol_extension(undefined) ->
+ [];
+next_protocol_extension(#next_protocol_negotiation{} = Info) ->
[Info].
enc_hello_extensions(Extensions) ->
@@ -1060,6 +1281,9 @@ enc_hello_extensions([], Acc) ->
Size = byte_size(Acc),
<<?UINT16(Size), Acc/binary>>;
+enc_hello_extensions([#next_protocol_negotiation{extension_data = ExtensionData} | Rest], Acc) ->
+ Len = byte_size(ExtensionData),
+ enc_hello_extensions(Rest, <<?UINT16(?NEXTPROTONEG_EXT), ?UINT16(Len), ExtensionData/binary, Acc/binary>>);
enc_hello_extensions([#renegotiation_info{renegotiated_connection = ?byte(0) = Info} | Rest], Acc) ->
Len = byte_size(Info),
enc_hello_extensions(Rest, <<?UINT16(?RENEGOTIATION_EXT), ?UINT16(Len), Info/binary, Acc/binary>>);
@@ -1067,8 +1291,22 @@ enc_hello_extensions([#renegotiation_info{renegotiated_connection = ?byte(0) = I
enc_hello_extensions([#renegotiation_info{renegotiated_connection = Info} | Rest], Acc) ->
InfoLen = byte_size(Info),
Len = InfoLen +1,
- enc_hello_extensions(Rest, <<?UINT16(?RENEGOTIATION_EXT), ?UINT16(Len), ?BYTE(InfoLen), Info/binary, Acc/binary>>).
-
+ enc_hello_extensions(Rest, <<?UINT16(?RENEGOTIATION_EXT), ?UINT16(Len), ?BYTE(InfoLen), Info/binary, Acc/binary>>);
+
+enc_hello_extensions([#hash_sign_algos{hash_sign_algos = HashSignAlgos} | Rest], Acc) ->
+ SignAlgoList = << <<(ssl_cipher:hash_algorithm(Hash)):8, (ssl_cipher:sign_algorithm(Sign)):8>> ||
+ {Hash, Sign} <- HashSignAlgos >>,
+ ListLen = byte_size(SignAlgoList),
+ Len = ListLen + 2,
+ enc_hello_extensions(Rest, <<?UINT16(?SIGNATURE_ALGORITHMS_EXT),
+ ?UINT16(Len), ?UINT16(ListLen), SignAlgoList/binary, Acc/binary>>).
+
+encode_client_protocol_negotiation(undefined, _) ->
+ undefined;
+encode_client_protocol_negotiation(_, false) ->
+ #next_protocol_negotiation{extension_data = <<>>};
+encode_client_protocol_negotiation(_, _) ->
+ undefined.
from_3bytes(Bin3) ->
from_3bytes(Bin3, []).
@@ -1095,6 +1333,14 @@ certificate_types({KeyExchange, _, _, _})
certificate_types(_) ->
<<?BYTE(?RSA_SIGN)>>.
+hashsign_dec(<<?BYTE(HashAlgo), ?BYTE(SignAlgo)>>) ->
+ {ssl_cipher:hash_algorithm(HashAlgo), ssl_cipher:sign_algorithm(SignAlgo)}.
+
+hashsign_enc(HashAlgo, SignAlgo) ->
+ Hash = ssl_cipher:hash_algorithm(HashAlgo),
+ Sign = ssl_cipher:sign_algorithm(SignAlgo),
+ <<?BYTE(Hash), ?BYTE(Sign)>>.
+
certificate_authorities(CertDbHandle, CertDbRef) ->
Authorities = certificate_authorities_from_db(CertDbHandle, CertDbRef),
Enc = fun(#'OTPCertificate'{tbsCertificate=TBSCert}) ->
@@ -1113,43 +1359,43 @@ certificate_authorities_from_db(CertDbHandle, CertDbRef) ->
[Cert | Acc];
(_, Acc) ->
Acc
- end,
+ end,
ssl_certificate_db:foldl(ConnectionCerts, [], CertDbHandle).
-digitally_signed(Hash, #'RSAPrivateKey'{} = Key) ->
+
+digitally_signed({3, Minor}, Hash, HashAlgo, Key) when Minor >= 3 ->
+ public_key:sign({digest, Hash}, HashAlgo, Key);
+digitally_signed(_Version, Hash, HashAlgo, #'DSAPrivateKey'{} = Key) ->
+ public_key:sign({digest, Hash}, HashAlgo, Key);
+digitally_signed(_Version, Hash, _HashAlgo, #'RSAPrivateKey'{} = Key) ->
public_key:encrypt_private(Hash, Key,
- [{rsa_pad, rsa_pkcs1_padding}]);
-digitally_signed(Hash, #'DSAPrivateKey'{} = Key) ->
- public_key:sign(Hash, none, Key).
-
-calc_master_secret({3,0}, PremasterSecret, ClientRandom, ServerRandom) ->
+ [{rsa_pad, rsa_pkcs1_padding}]).
+
+calc_master_secret({3,0}, _PrfAlgo, PremasterSecret, ClientRandom, ServerRandom) ->
ssl_ssl3:master_secret(PremasterSecret, ClientRandom, ServerRandom);
-calc_master_secret({3,N},PremasterSecret, ClientRandom, ServerRandom)
- when N == 1; N == 2 ->
- ssl_tls1:master_secret(PremasterSecret, ClientRandom, ServerRandom).
+calc_master_secret({3,_}, PrfAlgo, PremasterSecret, ClientRandom, ServerRandom) ->
+ ssl_tls1:master_secret(PrfAlgo, PremasterSecret, ClientRandom, ServerRandom).
-setup_keys({3,0}, MasterSecret,
+setup_keys({3,0}, _PrfAlgo, MasterSecret,
ServerRandom, ClientRandom, HashSize, KML, EKML, IVS) ->
- ssl_ssl3:setup_keys(MasterSecret, ServerRandom,
+ ssl_ssl3:setup_keys(MasterSecret, ServerRandom,
ClientRandom, HashSize, KML, EKML, IVS);
-setup_keys({3,1}, MasterSecret,
+setup_keys({3,N}, PrfAlgo, MasterSecret,
ServerRandom, ClientRandom, HashSize, KML, _EKML, IVS) ->
- ssl_tls1:setup_keys(MasterSecret, ServerRandom, ClientRandom, HashSize,
+ ssl_tls1:setup_keys(N, PrfAlgo, MasterSecret, ServerRandom, ClientRandom, HashSize,
KML, IVS).
-calc_finished({3, 0}, Role, MasterSecret, Hashes) ->
- ssl_ssl3:finished(Role, MasterSecret, Hashes);
-calc_finished({3, N}, Role, MasterSecret, Hashes)
- when N == 1; N == 2 ->
- ssl_tls1:finished(Role, MasterSecret, Hashes).
+calc_finished({3, 0}, Role, _PrfAlgo, MasterSecret, Handshake) ->
+ ssl_ssl3:finished(Role, MasterSecret, lists:reverse(Handshake));
+calc_finished({3, N}, Role, PrfAlgo, MasterSecret, Handshake) ->
+ ssl_tls1:finished(Role, N, PrfAlgo, MasterSecret, lists:reverse(Handshake)).
-calc_certificate_verify({3, 0}, MasterSecret, Algorithm, Hashes) ->
- ssl_ssl3:certificate_verify(Algorithm, MasterSecret, Hashes);
-calc_certificate_verify({3, N}, _, Algorithm, Hashes)
- when N == 1; N == 2 ->
- ssl_tls1:certificate_verify(Algorithm, Hashes).
+calc_certificate_verify({3, 0}, HashAlgo, MasterSecret, Handshake) ->
+ ssl_ssl3:certificate_verify(HashAlgo, MasterSecret, lists:reverse(Handshake));
+calc_certificate_verify({3, N}, HashAlgo, _MasterSecret, Handshake) ->
+ ssl_tls1:certificate_verify(HashAlgo, N, lists:reverse(Handshake)).
key_exchange_alg(rsa) ->
?KEY_EXCHANGE_RSA;
@@ -1169,7 +1415,17 @@ apply_user_fun(Fun, OtpCert, ExtensionOrError, UserState0, SslState) ->
{unknown, {SslState, UserState}}
end.
-alg_oid(#'RSAPrivateKey'{}) ->
- ?'rsaEncryption';
-alg_oid(#'DSAPrivateKey'{}) ->
- ?'id-dsa'.
+-define(TLSEXT_SIGALG_RSA(MD), {MD, rsa}).
+-define(TLSEXT_SIGALG_DSA(MD), {MD, dsa}).
+
+-define(TLSEXT_SIGALG(MD), ?TLSEXT_SIGALG_RSA(MD)).
+
+default_hash_signs() ->
+ #hash_sign_algos{hash_sign_algos =
+ [?TLSEXT_SIGALG(sha512),
+ ?TLSEXT_SIGALG(sha384),
+ ?TLSEXT_SIGALG(sha256),
+ ?TLSEXT_SIGALG(sha224),
+ ?TLSEXT_SIGALG(sha),
+ ?TLSEXT_SIGALG_DSA(sha),
+ ?TLSEXT_SIGALG_RSA(md5)]}.
diff --git a/lib/ssl/src/ssl_handshake.hrl b/lib/ssl/src/ssl_handshake.hrl
index fb0ebac7d1..2414d5b666 100644
--- a/lib/ssl/src/ssl_handshake.hrl
+++ b/lib/ssl/src/ssl_handshake.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -31,6 +31,15 @@
-type algo_oid() :: ?'rsaEncryption' | ?'id-dsa'.
-type public_key_params() :: #'Dss-Parms'{} | term().
-type public_key_info() :: {algo_oid(), #'RSAPublicKey'{} | integer() , public_key_params()}.
+-type tls_handshake_history() :: {[binary()], [binary()]}.
+
+-define(NO_PROTOCOL, <<>>).
+
+%% Signature algorithms
+-define(ANON, 0).
+-define(RSA, 1).
+-define(DSA, 2).
+-define(ECDSA, 3).
-record(session, {
session_id,
@@ -89,7 +98,9 @@
session_id, % opaque SessionID<0..32>
cipher_suites, % cipher_suites<2..2^16-1>
compression_methods, % compression_methods<1..2^8-1>,
- renegotiation_info
+ renegotiation_info,
+ hash_signs, % supported combinations of hashes/signature algos
+ next_protocol_negotiation = undefined % [binary()]
}).
-record(server_hello, {
@@ -98,7 +109,9 @@
session_id, % opaque SessionID<0..32>
cipher_suite, % cipher_suites
compression_method, % compression_method
- renegotiation_info
+ renegotiation_info,
+ hash_signs, % supported combinations of hashes/signature algos
+ next_protocol_negotiation = undefined % [binary()]
}).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -128,8 +141,14 @@
}).
-record(server_key_exchange, {
+ exchange_keys
+ }).
+
+-record(server_key_params, {
params, %% #server_rsa_params{} | #server_dh_params{}
- signed_params %% #signature{}
+ params_bin,
+ hashsign, %% term(atom(), atom())
+ signature %% #signature{}
}).
%% enum { anonymous, rsa, dsa } SignatureAlgorithm;
@@ -159,6 +178,7 @@
-record(certificate_request, {
certificate_types, %ClientCertificateType <1..2^8-1>
+ hashsign_algorithms, %%SignatureAndHashAlgorithm <2^16-1>;
certificate_authorities %DistinguishedName <0..2^16-1>
}).
@@ -193,6 +213,7 @@
%%% Certificate verify - RFC 4346 section 7.4.8
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-record(certificate_verify, {
+ hashsign_algorithm,
signature % binary()
}).
@@ -213,6 +234,27 @@
renegotiated_connection
}).
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Signature Algorithms RFC 5746 section 7.4.1.4.1.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+-define(SIGNATURE_ALGORITHMS_EXT, 13).
+
+-record(hash_sign_algos, {
+ hash_sign_algos
+ }).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Next Protocol Negotiation
+%% (http://tools.ietf.org/html/draft-agl-tls-nextprotoneg-02)
+%% (http://technotes.googlecode.com/git/nextprotoneg.html)
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-define(NEXTPROTONEG_EXT, 13172).
+-define(NEXT_PROTOCOL, 67).
+-record(next_protocol_negotiation, {extension_data}).
+
+-record(next_protocol, {selected_protocol}).
+
-endif. % -ifdef(ssl_handshake).
diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl
index 18cfcdcd68..ed0dc34adf 100644
--- a/lib/ssl/src/ssl_internal.hrl
+++ b/lib/ssl/src/ssl_internal.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -34,7 +34,7 @@
-type host() :: inet:ip_address() | inet:hostname().
-type session_id() :: 0 | binary().
-type tls_version() :: {integer(), integer()}.
--type tls_atom_version() :: sslv3 | tlsv1.
+-type tls_atom_version() :: sslv3 | tlsv1 | 'tlsv1.1' | 'tlsv1.2'.
-type certdb_ref() :: reference().
-type db_handle() :: term().
-type key_algo() :: null | rsa | dhe_rsa | dhe_dss | dh_anon.
@@ -69,11 +69,11 @@
-define(TRUE, 0).
-define(FALSE, 1).
--define(DEFAULT_SUPPORTED_VERSIONS, [tlsv1, sslv3]). % TODO: This is temporary
-%-define(DEFAULT_SUPPORTED_VERSIONS, ['tlsv1.1', tlsv1, sslv3]).
+-define(ALL_SUPPORTED_VERSIONS, ['tlsv1.2', 'tlsv1.1', tlsv1, sslv3]).
+-define(MIN_SUPPORTED_VERSIONS, ['tlsv1.1', tlsv1, sslv3]).
-record(ssl_options, {
- versions, % 'tlsv1.1' | tlsv1 | sslv3
+ versions, % 'tlsv1.2' | 'tlsv1.1' | tlsv1 | sslv3
verify, % verify_none | verify_peer
verify_fun, % fun(CertVerifyErrors) -> boolean()
fail_if_no_peer_cert, % boolean()
@@ -106,7 +106,9 @@
% after which ssl_connection will
% go into hibernation
%% This option should only be set to true by inet_tls_dist
- erl_dist = false
+ erl_dist = false,
+ next_protocols_advertised = undefined, %% [binary()],
+ next_protocol_selector = undefined %% fun([binary()]) -> binary())
}).
-record(socket_options,
diff --git a/lib/ssl/src/ssl_manager.erl b/lib/ssl/src/ssl_manager.erl
index 3e947af2c9..14fba72d86 100644
--- a/lib/ssl/src/ssl_manager.erl
+++ b/lib/ssl/src/ssl_manager.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -24,8 +24,6 @@
-module(ssl_manager).
-behaviour(gen_server).
--include("ssl_internal.hrl").
-
%% Internal application API
-export([start_link/1, start_link_dist/1,
connection_init/2, cache_pem_file/2,
@@ -86,7 +84,7 @@ start_link_dist(Opts) ->
%%--------------------------------------------------------------------
-spec connection_init(binary()| {der, list()}, client | server) ->
- {ok, certdb_ref(), db_handle(), db_handle()}.
+ {ok, certdb_ref(), db_handle(), db_handle(), db_handle(), db_handle()}.
%%
%% Description: Do necessary initializations for a new connection.
%%--------------------------------------------------------------------
@@ -144,8 +142,14 @@ lookup_trusted_cert(DbHandle, Ref, SerialNumber, Issuer) ->
new_session_id(Port) ->
call({new_session_id, Port}).
+%%--------------------------------------------------------------------
+-spec clean_cert_db(reference(), binary()) -> term().
+%%
+%% Description: Send clean request of cert db to ssl_manager process should
+%% be called by ssl-connection processes.
+%%--------------------------------------------------------------------
clean_cert_db(Ref, File) ->
- erlang:send_after(?CLEAN_CERT_DB, self(), {clean_cert_db, Ref, File}).
+ erlang:send_after(?CLEAN_CERT_DB, get(ssl_manager), {clean_cert_db, Ref, File}).
%%--------------------------------------------------------------------
-spec register_session(inet:port_number(), #session{}) -> ok.
@@ -191,7 +195,7 @@ init([Name, Opts]) ->
proplists:get_value(session_lifetime, Opts, ?'24H_in_sec'),
CertDb = ssl_certificate_db:create(),
SessionCache = CacheCb:init(proplists:get_value(session_cb_init_args, Opts, [])),
- Timer = erlang:send_after(SessionLifeTime * 1000,
+ Timer = erlang:send_after(SessionLifeTime * 1000 + 5000,
self(), validate_sessions),
erlang:send_after(?CLEAR_PEM_CACHE, self(), clear_pem_cache),
{ok, #state{certificate_db = CertDb,
@@ -322,19 +326,12 @@ handle_info(clear_pem_cache, #state{certificate_db = [_,_,PemChace]} = State) ->
handle_info({clean_cert_db, Ref, File},
#state{certificate_db = [CertDb,RefDb, PemCache]} = State) ->
- case ssl_certificate_db:ref_count(Ref, RefDb, 0) of
- 0 ->
- MD5 = crypto:md5(File),
- case ssl_certificate_db:lookup_cached_pem(MD5, PemCache) of
- [{Content, Ref}] ->
- ssl_certificate_db:insert(MD5, Content, PemCache);
- undefined ->
- ok
- end,
- ssl_certificate_db:remove(Ref, RefDb),
- ssl_certificate_db:remove_trusted_certs(Ref, CertDb);
+
+ case ssl_certificate_db:lookup(Ref, RefDb) of
+ undefined -> %% Alredy cleaned
+ ok;
_ ->
- ok
+ clean_cert_db(Ref, CertDb, RefDb, PemCache, File)
end,
{noreply, State};
@@ -466,3 +463,19 @@ new_id(Port, Tries, Cache, CacheCb) ->
_ ->
new_id(Port, Tries - 1, Cache, CacheCb)
end.
+
+clean_cert_db(Ref, CertDb, RefDb, PemCache, File) ->
+ case ssl_certificate_db:ref_count(Ref, RefDb, 0) of
+ 0 ->
+ MD5 = crypto:md5(File),
+ case ssl_certificate_db:lookup_cached_pem(PemCache, MD5) of
+ [{Content, Ref}] ->
+ ssl_certificate_db:insert(MD5, Content, PemCache);
+ _ ->
+ ok
+ end,
+ ssl_certificate_db:remove(Ref, RefDb),
+ ssl_certificate_db:remove_trusted_certs(Ref, CertDb);
+ _ ->
+ ok
+ end.
diff --git a/lib/ssl/src/ssl_record.erl b/lib/ssl/src/ssl_record.erl
index 830026c825..173b9611c6 100644
--- a/lib/ssl/src/ssl_record.erl
+++ b/lib/ssl/src/ssl_record.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -383,6 +383,8 @@ get_tls_records_aux(Data, Acc) ->
%% Description: Creates a protocol version record from a version atom
%% or vice versa.
%%--------------------------------------------------------------------
+protocol_version('tlsv1.2') ->
+ {3, 3};
protocol_version('tlsv1.1') ->
{3, 2};
protocol_version(tlsv1) ->
@@ -391,6 +393,8 @@ protocol_version(sslv3) ->
{3, 0};
protocol_version(sslv2) -> %% Backwards compatibility
{2, 0};
+protocol_version({3, 3}) ->
+ 'tlsv1.2';
protocol_version({3, 2}) ->
'tlsv1.1';
protocol_version({3, 1}) ->
@@ -445,9 +449,9 @@ supported_protocol_versions() ->
end,
case application:get_env(ssl, protocol_version) of
undefined ->
- lists:map(Fun, ?DEFAULT_SUPPORTED_VERSIONS);
+ lists:map(Fun, supported_protocol_versions([]));
{ok, []} ->
- lists:map(Fun, ?DEFAULT_SUPPORTED_VERSIONS);
+ lists:map(Fun, supported_protocol_versions([]));
{ok, Vsns} when is_list(Vsns) ->
Versions = lists:filter(fun is_acceptable_version/1, lists:map(Fun, Vsns)),
supported_protocol_versions(Versions);
@@ -457,7 +461,15 @@ supported_protocol_versions() ->
end.
supported_protocol_versions([]) ->
- ?DEFAULT_SUPPORTED_VERSIONS;
+ Vsns = case sufficient_tlsv1_2_crypto_support() of
+ true ->
+ ?ALL_SUPPORTED_VERSIONS;
+ false ->
+ ?MIN_SUPPORTED_VERSIONS
+ end,
+ application:set_env(ssl, protocol_version, Vsns),
+ Vsns;
+
supported_protocol_versions([_|_] = Vsns) ->
Vsns.
@@ -561,14 +573,14 @@ highest_protocol_version() ->
initial_connection_state(ConnectionEnd) ->
#connection_state{security_parameters =
- initial_security_params(ConnectionEnd),
+ initial_security_params(ConnectionEnd),
sequence_number = 0
}.
initial_security_params(ConnectionEnd) ->
SecParams = #security_parameters{connection_end = ConnectionEnd,
compression_algorithm = ?NULL},
- ssl_cipher:security_parameters(?TLS_NULL_WITH_NULL_NULL,
+ ssl_cipher:security_parameters(highest_protocol_version(), ?TLS_NULL_WITH_NULL_NULL,
SecParams).
empty_connection_state(ConnectionEnd) ->
@@ -633,7 +645,7 @@ cipher(Type, Version, Fragment, CS0) ->
BCA}
}} =
hash_and_bump_seqno(CS0, Type, Version, Length, Fragment),
- {Ciphered, CipherS1} = ssl_cipher:cipher(BCA, CipherS0, MacHash, Fragment),
+ {Ciphered, CipherS1} = ssl_cipher:cipher(BCA, CipherS0, MacHash, Fragment, Version),
CS2 = CS1#connection_state{cipher_state=CipherS1},
{Ciphered, CS2}.
@@ -687,6 +699,17 @@ mac_hash({_,_}, ?NULL, _MacSecret, _SeqNo, _Type,
mac_hash({3, 0}, MacAlg, MacSecret, SeqNo, Type, Length, Fragment) ->
ssl_ssl3:mac_hash(MacAlg, MacSecret, SeqNo, Type, Length, Fragment);
mac_hash({3, N} = Version, MacAlg, MacSecret, SeqNo, Type, Length, Fragment)
- when N =:= 1; N =:= 2 ->
+ when N =:= 1; N =:= 2; N =:= 3 ->
ssl_tls1:mac_hash(MacAlg, MacSecret, SeqNo, Type, Version,
Length, Fragment).
+
+sufficient_tlsv1_2_crypto_support() ->
+ Data = "Sampl",
+ Data2 = "e #1",
+ Key = <<0,1,2,3,16,17,18,19,32,33,34,35,48,49,50,51,4,5,6,7,20,21,22,23,36,37,38,39,
+ 52,53,54,55,8,9,10,11,24,25,26,27,40,41,42,43,56,57,58,59>>,
+ try
+ crypto:sha256_mac(Key, lists:flatten([Data, Data2])),
+ true
+ catch _:_ -> false
+ end.
diff --git a/lib/ssl/src/ssl_record.hrl b/lib/ssl/src/ssl_record.hrl
index 282d642138..f73da92a52 100644
--- a/lib/ssl/src/ssl_record.hrl
+++ b/lib/ssl/src/ssl_record.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -47,6 +47,7 @@
key_material_length, % unit 8
expanded_key_material_length, % unit 8
mac_algorithm, % unit 8
+ prf_algorithm, % unit 8
hash_size, % unit 8
compression_algorithm, % unit 8
master_secret, % opaque 48
@@ -97,10 +98,15 @@
%-define(TRUE, 0). %% Already defined by ssl_internal.hrl
%-define(FALSE, 1). %% Already defined by ssl_internal.hrl
-%% MACAlgorithm
+%% MAC and PRF Algorithms
%-define(NULL, 0). %% Already defined by ssl_internal.hrl
-define(MD5, 1).
-define(SHA, 2).
+-define(MD5SHA, 4711). %% Not defined in protocol used to represent old prf
+-define(SHA224, 3).
+-define(SHA256, 4).
+-define(SHA384, 5).
+-define(SHA512, 6).
%% CompressionMethod
% -define(NULL, 0). %% Already defined by ssl_internal.hrl
@@ -176,7 +182,8 @@
content, % opaque content[TLSCompressed.length];
mac, % opaque MAC[CipherSpec.hash_size];
padding, % unit 8 padding[GenericBlockCipher.padding_length];
- padding_length % uint8 padding_length;
+ padding_length, % uint8 padding_length;
+ next_iv % opaque IV[SecurityParameters.record_iv_length];
}).
-endif. % -ifdef(ssl_record).
diff --git a/lib/ssl/src/ssl_session.erl b/lib/ssl/src/ssl_session.erl
index 2ad422fc03..a24b2d9444 100644
--- a/lib/ssl/src/ssl_session.erl
+++ b/lib/ssl/src/ssl_session.erl
@@ -72,15 +72,12 @@ valid_session(#session{time_stamp = TimeStamp}, LifeTime) ->
server_id(Port, <<>>, _SslOpts, _Cert, _, _) ->
{ssl_manager:new_session_id(Port), undefined};
-server_id(Port, SuggestedId,
- #ssl_options{reuse_sessions = ReuseEnabled,
- reuse_session = ReuseFun},
- Cert, Cache, CacheCb) ->
+server_id(Port, SuggestedId, Options, Cert, Cache, CacheCb) ->
LifeTime = case application:get_env(ssl, session_lifetime) of
{ok, Time} when is_integer(Time) -> Time;
_ -> ?'24H_in_sec'
end,
- case is_resumable(SuggestedId, Port, ReuseEnabled,ReuseFun,
+ case is_resumable(SuggestedId, Port, Options,
Cache, CacheCb, LifeTime, Cert)
of
{true, Resumed} ->
@@ -112,9 +109,9 @@ select_session(Sessions, #ssl_options{ciphers = Ciphers}, OwnCert) ->
[[Id, _]|_] -> Id
end.
-is_resumable(_, _, false, _, _, _, _, _) ->
+is_resumable(_, _, #ssl_options{reuse_sessions = false}, _, _, _, _) ->
{false, undefined};
-is_resumable(SuggestedSessionId, Port, true, ReuseFun, Cache,
+is_resumable(SuggestedSessionId, Port, #ssl_options{reuse_session = ReuseFun} = Options, Cache,
CacheCb, SecondLifeTime, OwnCert) ->
case CacheCb:lookup(Cache, {Port, SuggestedSessionId}) of
#session{cipher_suite = CipherSuite,
@@ -125,6 +122,7 @@ is_resumable(SuggestedSessionId, Port, true, ReuseFun, Cache,
case resumable(IsResumable)
andalso (OwnCert == SessionOwnCert)
andalso valid_session(Session, SecondLifeTime)
+ andalso reusable_options(Options, Session)
andalso ReuseFun(SuggestedSessionId, PeerCert,
Compression, CipherSuite)
of
@@ -139,3 +137,9 @@ resumable(new) ->
false;
resumable(IsResumable) ->
IsResumable.
+
+reusable_options(#ssl_options{fail_if_no_peer_cert = true,
+ verify = verify_peer}, Session) ->
+ (Session#session.peer_certificate =/= undefined);
+reusable_options(_,_) ->
+ true.
diff --git a/lib/ssl/src/ssl_ssl3.erl b/lib/ssl/src/ssl_ssl3.erl
index f2926b2d2f..a11c5b8c0c 100644
--- a/lib/ssl/src/ssl_ssl3.erl
+++ b/lib/ssl/src/ssl_ssl3.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -54,9 +54,9 @@ master_secret(PremasterSecret, ClientRandom, ServerRandom) ->
Block = generate_keyblock(PremasterSecret, ClientRandom, ServerRandom, 48),
Block.
--spec finished(client | server, binary(), {binary(), binary()}) -> binary().
+-spec finished(client | server, binary(), [binary()]) -> binary().
-finished(Role, MasterSecret, {MD5Hash, SHAHash}) ->
+finished(Role, MasterSecret, Handshake) ->
%% draft-ietf-tls-ssl-version3-00 - 5.6.9 Finished
%% struct {
%% opaque md5_hash[16];
@@ -70,13 +70,13 @@ finished(Role, MasterSecret, {MD5Hash, SHAHash}) ->
%% SHA(handshake_messages + Sender +
%% master_secret + pad1));
Sender = get_sender(Role),
- MD5 = handshake_hash(?MD5, MasterSecret, Sender, MD5Hash),
- SHA = handshake_hash(?SHA, MasterSecret, Sender, SHAHash),
+ MD5 = handshake_hash(?MD5, MasterSecret, Sender, Handshake),
+ SHA = handshake_hash(?SHA, MasterSecret, Sender, Handshake),
<<MD5/binary, SHA/binary>>.
--spec certificate_verify(OID::tuple(), binary(), {binary(), binary()}) -> binary().
+-spec certificate_verify(md5sha | sha, binary(), [binary()]) -> binary().
-certificate_verify(?'rsaEncryption', MasterSecret, {MD5Hash, SHAHash}) ->
+certificate_verify(md5sha, MasterSecret, Handshake) ->
%% md5_hash
%% MD5(master_secret + pad_2 +
%% MD5(handshake_messages + master_secret + pad_1));
@@ -84,15 +84,16 @@ certificate_verify(?'rsaEncryption', MasterSecret, {MD5Hash, SHAHash}) ->
%% SHA(master_secret + pad_2 +
%% SHA(handshake_messages + master_secret + pad_1));
- MD5 = handshake_hash(?MD5, MasterSecret, undefined, MD5Hash),
- SHA = handshake_hash(?SHA, MasterSecret, undefined, SHAHash),
+ MD5 = handshake_hash(?MD5, MasterSecret, undefined, Handshake),
+ SHA = handshake_hash(?SHA, MasterSecret, undefined, Handshake),
<<MD5/binary, SHA/binary>>;
-certificate_verify(?'id-dsa', MasterSecret, {_, SHAHash}) ->
+certificate_verify(sha, MasterSecret, Handshake) ->
%% sha_hash
%% SHA(master_secret + pad_2 +
%% SHA(handshake_messages + master_secret + pad_1));
- handshake_hash(?SHA, MasterSecret, undefined, SHAHash).
+
+ handshake_hash(?SHA, MasterSecret, undefined, Handshake).
-spec mac_hash(integer(), binary(), integer(), integer(), integer(), binary()) -> binary().
@@ -152,28 +153,17 @@ suites() ->
%%% Internal functions
%%--------------------------------------------------------------------
-hash(?MD5, Data) ->
+hash(?MD5, Data) ->
crypto:md5(Data);
-hash(?SHA, Data) ->
+hash(?SHA, Data) ->
crypto:sha(Data).
-hash_update(?MD5, Context, Data) ->
- crypto:md5_update(Context, Data);
-hash_update(?SHA, Context, Data) ->
- crypto:sha_update(Context, Data).
-
-hash_final(?MD5, Context) ->
- crypto:md5_final(Context);
-hash_final(?SHA, Context) ->
- crypto:sha_final(Context).
-
%%pad_1(?NULL) ->
%% "";
pad_1(?MD5) ->
<<"666666666666666666666666666666666666666666666666">>;
pad_1(?SHA) ->
<<"6666666666666666666666666666666666666666">>.
-
%%pad_2(?NULL) ->
%% "";
pad_2(?MD5) ->
@@ -189,19 +179,11 @@ mac_hash(Method, Secret, Data) ->
InnerHash = hash(Method, [Secret, pad_1(Method), Data]),
hash(Method, [Secret, pad_2(Method), InnerHash]).
-handshake_hash(Method, HandshakeHash, Extra) ->
- HSH = hash_update(Method, HandshakeHash, Extra),
- hash_final(Method, HSH).
-
-handshake_hash(Method, MasterSecret, undefined, HandshakeHash) ->
- InnerHash =
- handshake_hash(Method, HandshakeHash,
- [MasterSecret, pad_1(Method)]),
+handshake_hash(Method, MasterSecret, undefined, Handshake) ->
+ InnerHash = hash(Method, [Handshake, MasterSecret, pad_1(Method)]),
hash(Method, [MasterSecret, pad_2(Method), InnerHash]);
-handshake_hash(Method, MasterSecret, Sender, HandshakeHash) ->
- InnerHash =
- handshake_hash(Method, HandshakeHash,
- [Sender, MasterSecret, pad_1(Method)]),
+handshake_hash(Method, MasterSecret, Sender, Handshake) ->
+ InnerHash = hash(Method, [Handshake, Sender, MasterSecret, pad_1(Method)]),
hash(Method, [MasterSecret, pad_2(Method), InnerHash]).
get_sender(client) -> "CLNT";
diff --git a/lib/ssl/src/ssl_tls1.erl b/lib/ssl/src/ssl_tls1.erl
index c8aae34892..41dc1bf0dc 100644
--- a/lib/ssl/src/ssl_tls1.erl
+++ b/lib/ssl/src/ssl_tls1.erl
@@ -26,27 +26,29 @@
-include("ssl_cipher.hrl").
-include("ssl_internal.hrl").
--include("ssl_record.hrl").
+-include("ssl_record.hrl").
--export([master_secret/3, finished/3, certificate_verify/2, mac_hash/7,
- setup_keys/6, suites/0, prf/4]).
+-export([master_secret/4, finished/5, certificate_verify/3, mac_hash/7,
+ setup_keys/8, suites/1, prf/5]).
%%====================================================================
%% Internal application API
%%====================================================================
--spec master_secret(binary(), binary(), binary()) -> binary().
+-spec master_secret(integer(), binary(), binary(), binary()) -> binary().
-master_secret(PreMasterSecret, ClientRandom, ServerRandom) ->
- %% RFC 2246 & 4346 - 8.1 %% master_secret = PRF(pre_master_secret,
- %% "master secret", ClientHello.random +
- %% ServerHello.random)[0..47];
- prf(PreMasterSecret, <<"master secret">>,
+master_secret(PrfAlgo, PreMasterSecret, ClientRandom, ServerRandom) ->
+ %% RFC 2246 & 4346 && RFC 5246 - 8.1 %% master_secret = PRF(pre_master_secret,
+ %% "master secret", ClientHello.random +
+ %% ServerHello.random)[0..47];
+
+ prf(PrfAlgo, PreMasterSecret, <<"master secret">>,
[ClientRandom, ServerRandom], 48).
--spec finished(client | server, binary(), {binary(), binary()}) -> binary().
+-spec finished(client | server, integer(), integer(), binary(), [binary()]) -> binary().
-finished(Role, MasterSecret, {MD5Hash, SHAHash}) ->
+finished(Role, Version, PrfAlgo, MasterSecret, Handshake)
+ when Version == 1; Version == 2; PrfAlgo == ?MD5SHA ->
%% RFC 2246 & 4346 - 7.4.9. Finished
%% struct {
%% opaque verify_data[12];
@@ -55,26 +57,39 @@ finished(Role, MasterSecret, {MD5Hash, SHAHash}) ->
%% verify_data
%% PRF(master_secret, finished_label, MD5(handshake_messages) +
%% SHA-1(handshake_messages)) [0..11];
- MD5 = hash_final(?MD5, MD5Hash),
- SHA = hash_final(?SHA, SHAHash),
- prf(MasterSecret, finished_label(Role), [MD5, SHA], 12).
+ MD5 = crypto:md5(Handshake),
+ SHA = crypto:sha(Handshake),
+ prf(?MD5SHA, MasterSecret, finished_label(Role), [MD5, SHA], 12);
+
+finished(Role, Version, PrfAlgo, MasterSecret, Handshake)
+ when Version == 3 ->
+ %% RFC 5246 - 7.4.9. Finished
+ %% struct {
+ %% opaque verify_data[12];
+ %% } Finished;
+ %%
+ %% verify_data
+ %% PRF(master_secret, finished_label, Hash(handshake_messages)) [0..11];
+ Hash = crypto:hash(mac_algo(PrfAlgo), Handshake),
+ prf(PrfAlgo, MasterSecret, finished_label(Role), Hash, 12).
--spec certificate_verify(OID::tuple(), {binary(), binary()}) -> binary().
+-spec certificate_verify(md5sha | sha, integer(), [binary()]) -> binary().
-certificate_verify(?'rsaEncryption', {MD5Hash, SHAHash}) ->
- MD5 = hash_final(?MD5, MD5Hash),
- SHA = hash_final(?SHA, SHAHash),
+certificate_verify(md5sha, _Version, Handshake) ->
+ MD5 = crypto:md5(Handshake),
+ SHA = crypto:sha(Handshake),
<<MD5/binary, SHA/binary>>;
-certificate_verify(?'id-dsa', {_, SHAHash}) ->
- hash_final(?SHA, SHAHash).
+certificate_verify(HashAlgo, _Version, Handshake) ->
+ crypto:hash(HashAlgo, Handshake).
--spec setup_keys(binary(), binary(), binary(), integer(),
- integer(), integer()) -> {binary(), binary(), binary(),
+-spec setup_keys(integer(), integer(), binary(), binary(), binary(), integer(),
+ integer(), integer()) -> {binary(), binary(), binary(),
binary(), binary(), binary()}.
-setup_keys(MasterSecret, ServerRandom, ClientRandom, HashSize,
- KeyMatLen, IVSize) ->
+setup_keys(Version, _PrfAlgo, MasterSecret, ServerRandom, ClientRandom, HashSize,
+ KeyMatLen, IVSize)
+ when Version == 1 ->
%% RFC 2246 - 6.3. Key calculation
%% key_block = PRF(SecurityParameters.master_secret,
%% "key expansion",
@@ -88,36 +103,67 @@ setup_keys(MasterSecret, ServerRandom, ClientRandom, HashSize,
%% client_write_IV[SecurityParameters.IV_size]
%% server_write_IV[SecurityParameters.IV_size]
WantedLength = 2 * (HashSize + KeyMatLen + IVSize),
- KeyBlock = prf(MasterSecret, "key expansion",
+ KeyBlock = prf(?MD5SHA, MasterSecret, "key expansion",
[ServerRandom, ClientRandom], WantedLength),
<<ClientWriteMacSecret:HashSize/binary,
ServerWriteMacSecret:HashSize/binary,
ClientWriteKey:KeyMatLen/binary, ServerWriteKey:KeyMatLen/binary,
ClientIV:IVSize/binary, ServerIV:IVSize/binary>> = KeyBlock,
{ClientWriteMacSecret, ServerWriteMacSecret, ClientWriteKey,
- ServerWriteKey, ClientIV, ServerIV}.
+ ServerWriteKey, ClientIV, ServerIV};
+
+%% TLS v1.1
+setup_keys(Version, _PrfAlgo, MasterSecret, ServerRandom, ClientRandom, HashSize,
+ KeyMatLen, IVSize)
+ when Version == 2 ->
+ %% 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]
+ %%
+ %% RFC 4346 is incomplete, the client and server IVs have to
+ %% be generated just like for TLS 1.0
+ WantedLength = 2 * (HashSize + KeyMatLen + IVSize),
+ KeyBlock = prf(?MD5SHA, MasterSecret, "key expansion",
+ [ServerRandom, ClientRandom], WantedLength),
+ <<ClientWriteMacSecret:HashSize/binary,
+ ServerWriteMacSecret:HashSize/binary,
+ ClientWriteKey:KeyMatLen/binary, ServerWriteKey:KeyMatLen/binary,
+ ClientIV:IVSize/binary, ServerIV:IVSize/binary>> = KeyBlock,
+ {ClientWriteMacSecret, ServerWriteMacSecret, ClientWriteKey,
+ ServerWriteKey, ClientIV, ServerIV};
-%% 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}.
+%% TLS v1.2
+setup_keys(Version, PrfAlgo, MasterSecret, ServerRandom, ClientRandom, HashSize,
+ KeyMatLen, IVSize)
+ when Version == 3 ->
+ %% RFC 5246 - 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]
+ %% client_write_IV[SecurityParameters.fixed_iv_length]
+ %% server_write_IV[SecurityParameters.fixed_iv_length]
+ WantedLength = 2 * (HashSize + KeyMatLen + IVSize),
+ KeyBlock = prf(PrfAlgo, MasterSecret, "key expansion",
+ [ServerRandom, ClientRandom], WantedLength),
+ <<ClientWriteMacSecret:HashSize/binary,
+ ServerWriteMacSecret:HashSize/binary,
+ ClientWriteKey:KeyMatLen/binary, ServerWriteKey:KeyMatLen/binary,
+ ClientIV:IVSize/binary, ServerIV:IVSize/binary>> = KeyBlock,
+ {ClientWriteMacSecret, ServerWriteMacSecret, ClientWriteKey,
+ ServerWriteKey, ClientIV, ServerIV}.
-spec mac_hash(integer(), binary(), integer(), integer(), tls_version(),
integer(), binary()) -> binary().
@@ -134,9 +180,9 @@ mac_hash(Method, Mac_write_secret, Seq_num, Type, {Major, Minor},
Fragment]),
Mac.
--spec suites() -> [cipher_suite()].
+-spec suites(1|2|3) -> [cipher_suite()].
-suites() ->
+suites(Minor) when Minor == 1; Minor == 2->
[
?TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
?TLS_DHE_DSS_WITH_AES_256_CBC_SHA,
@@ -152,7 +198,19 @@ suites() ->
?TLS_RSA_WITH_RC4_128_MD5,
?TLS_DHE_RSA_WITH_DES_CBC_SHA,
?TLS_RSA_WITH_DES_CBC_SHA
- ].
+ ];
+
+suites(Minor) when Minor == 3 ->
+ [
+ ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,
+ ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA256,
+ ?TLS_RSA_WITH_AES_256_CBC_SHA256,
+ ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,
+ ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA256,
+ ?TLS_RSA_WITH_AES_128_CBC_SHA256
+ %% ?TLS_DH_anon_WITH_AES_128_CBC_SHA256,
+ %% ?TLS_DH_anon_WITH_AES_256_CBC_SHA256
+ ] ++ suites(2).
%%--------------------------------------------------------------------
%%% Internal functions
@@ -163,7 +221,19 @@ hmac_hash(?NULL, _, _) ->
hmac_hash(?MD5, Key, Value) ->
crypto:md5_mac(Key, Value);
hmac_hash(?SHA, Key, Value) ->
- crypto:sha_mac(Key, Value).
+ crypto:sha_mac(Key, Value);
+hmac_hash(?SHA256, Key, Value) ->
+ crypto:sha256_mac(Key, Value);
+hmac_hash(?SHA384, Key, Value) ->
+ crypto:sha384_mac(Key, Value);
+hmac_hash(?SHA512, Key, Value) ->
+ crypto:sha512_mac(Key, Value).
+
+mac_algo(?MD5) -> md5;
+mac_algo(?SHA) -> sha;
+mac_algo(?SHA256) -> sha256;
+mac_algo(?SHA384) -> sha384;
+mac_algo(?SHA512) -> sha512.
% First, we define a data expansion function, P_hash(secret, data) that
% uses a single hash function to expand a secret and seed into an
@@ -182,7 +252,7 @@ p_hash(_Secret, _Seed, WantedLength, _Method, _N, [Last | Acc])
when WantedLength =< 0 ->
Keep = byte_size(Last) + WantedLength,
<<B:Keep/binary, _/binary>> = Last,
- lists:reverse(Acc, [B]);
+ list_to_binary(lists:reverse(Acc, [B]));
p_hash(Secret, Seed, WantedLength, Method, N, Acc) ->
N1 = N+1,
Bin = hmac_hash(Method, Secret, [a(N1, Secret, Seed, Method), Seed]),
@@ -214,13 +284,18 @@ split_secret(BinSecret) ->
<<_:Div/binary, Secret2:EvenLength/binary>> = BinSecret,
{Secret1, Secret2}.
-prf(Secret, Label, Seed, WantedLength) ->
+prf(?MD5SHA, Secret, Label, Seed, WantedLength) ->
%% PRF(secret, label, seed) = P_MD5(S1, label + seed) XOR
%% P_SHA-1(S2, label + seed);
{S1, S2} = split_secret(Secret),
LS = list_to_binary([Label, Seed]),
crypto:exor(p_hash(S1, LS, WantedLength, ?MD5),
- p_hash(S2, LS, WantedLength, ?SHA)).
+ p_hash(S2, LS, WantedLength, ?SHA));
+
+prf(MAC, Secret, Label, Seed, WantedLength) ->
+ %% PRF(secret, label, seed) = P_SHA256(secret, label + seed);
+ LS = list_to_binary([Label, Seed]),
+ p_hash(Secret, LS, WantedLength, MAC).
%%%% Misc help functions %%%%
@@ -228,8 +303,3 @@ finished_label(client) ->
<<"client finished">>;
finished_label(server) ->
<<"server finished">>.
-
-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 a0f54c0359..847907cde8 100644
--- a/lib/ssl/test/Makefile
+++ b/lib/ssl/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2011. All Rights Reserved.
+# Copyright Ericsson AB 1999-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -37,13 +37,16 @@ VSN=$(GS_VSN)
MODULES = \
ssl_test_lib \
ssl_basic_SUITE \
+ ssl_cipher_SUITE \
+ ssl_certificate_verify_SUITE\
+ ssl_dist_SUITE \
ssl_handshake_SUITE \
+ ssl_npn_hello_SUITE \
+ ssl_npn_handshake_SUITE \
ssl_packet_SUITE \
- ssl_cipher_SUITE \
ssl_payload_SUITE \
- ssl_to_openssl_SUITE \
ssl_session_cache_SUITE \
- ssl_dist_SUITE \
+ ssl_to_openssl_SUITE \
make_certs\
erl_make_certs
diff --git a/lib/ssl/test/erl_make_certs.erl b/lib/ssl/test/erl_make_certs.erl
index 254aa6d2f9..d6bdd05d01 100644
--- a/lib/ssl/test/erl_make_certs.erl
+++ b/lib/ssl/test/erl_make_certs.erl
@@ -137,10 +137,10 @@ decode_key(PemBin, Pw) ->
encode_key(Key = #'RSAPrivateKey'{}) ->
{ok, Der} = 'OTP-PUB-KEY':encode('RSAPrivateKey', Key),
- {'RSAPrivateKey', list_to_binary(Der), not_encrypted};
+ {'RSAPrivateKey', Der, not_encrypted};
encode_key(Key = #'DSAPrivateKey'{}) ->
{ok, Der} = 'OTP-PUB-KEY':encode('DSAPrivateKey', Key),
- {'DSAPrivateKey', list_to_binary(Der), not_encrypted}.
+ {'DSAPrivateKey', Der, not_encrypted}.
make_tbs(SubjectKey, Opts) ->
Version = list_to_atom("v"++integer_to_list(proplists:get_value(version, Opts, 3))),
diff --git a/lib/ssl/test/make_certs.erl b/lib/ssl/test/make_certs.erl
index 693289990c..4603a9f846 100644
--- a/lib/ssl/test/make_certs.erl
+++ b/lib/ssl/test/make_certs.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -121,7 +121,19 @@ create_self_signed_cert(Root, OpenSSLCmd, CAName, Cnf) ->
" -keyout ", KeyFile,
" -out ", CertFile],
Env = [{"ROOTDIR", Root}],
- cmd(Cmd, Env).
+ cmd(Cmd, Env),
+ fix_key_file(OpenSSLCmd, KeyFile).
+
+% openssl 1.0 generates key files in pkcs8 format by default and we don't handle this format
+fix_key_file(OpenSSLCmd, KeyFile) ->
+ KeyFileTmp = KeyFile ++ ".tmp",
+ Cmd = [OpenSSLCmd, " rsa",
+ " -in ",
+ KeyFile,
+ " -out ",
+ KeyFileTmp],
+ cmd(Cmd, []),
+ ok = file:rename(KeyFileTmp, KeyFile).
create_ca_dir(Root, CAName, Cnf) ->
CARoot = filename:join([Root, CAName]),
@@ -139,7 +151,8 @@ create_req(Root, OpenSSLCmd, CnfFile, KeyFile, ReqFile) ->
" -keyout ", KeyFile,
" -out ", ReqFile],
Env = [{"ROOTDIR", Root}],
- cmd(Cmd, Env).
+ cmd(Cmd, Env),
+ fix_key_file(OpenSSLCmd, KeyFile).
sign_req(Root, OpenSSLCmd, CA, CertType, ReqFile, CertFile) ->
CACnfFile = filename:join([Root, CA, "ca.cnf"]),
diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl
index 5a52917d6c..df84acacdc 100644
--- a/lib/ssl/test/ssl_basic_SUITE.erl
+++ b/lib/ssl/test/ssl_basic_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -27,9 +27,11 @@
-include_lib("common_test/include/ct.hrl").
-include_lib("public_key/include/public_key.hrl").
+-include("ssl_internal.hrl").
-include("ssl_alert.hrl").
-include("ssl_internal.hrl").
-include("ssl_record.hrl").
+-include("ssl_handshake.hrl").
-define('24H_in_sec', 86400).
-define(TIMEOUT, 60000).
@@ -37,30 +39,148 @@
-define(EXPIRE, 10).
-define(SLEEP, 500).
-define(RENEGOTIATION_DISABLE_TIME, 12000).
+-define(CLEAN_SESSION_DB, 60000).
-%% 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.
+%% Common Test interface functions -----------------------------------
+%%--------------------------------------------------------------------
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [
+ {group, basic},
+ {group, options},
+ {group, session},
+ {group, 'tlsv1.2'},
+ {group, 'tlsv1.1'},
+ {group, 'tlsv1'},
+ {group, 'sslv3'}
+ ].
+
+groups() ->
+ [{basic, [], basic_tests()},
+ {options, [], options_tests()},
+ {'tlsv1.2', [], all_versions_groups()},
+ {'tlsv1.1', [], all_versions_groups()},
+ {'tlsv1', [], all_versions_groups() ++ rizzo_tests()},
+ {'sslv3', [], all_versions_groups() ++ rizzo_tests()},
+ {api,[], api_tests()},
+ {session, [], session_tests()},
+ {renegotiate, [], renegotiate_tests()},
+ {ciphers, [], cipher_tests()},
+ {error_handling_tests, [], error_handling_tests()}
+ ].
+
+all_versions_groups ()->
+ [{group, api},
+ {group, renegotiate},
+ {group, ciphers},
+ {group, error_handling_tests}].
+
+
+basic_tests() ->
+ [app,
+ alerts,
+ send_close,
+ connect_twice,
+ connect_dist,
+ clear_pem_cache
+ ].
+
+options_tests() ->
+ [der_input,
+ misc_ssl_options,
+ socket_options,
+ invalid_inet_get_option,
+ invalid_inet_get_option_not_list,
+ invalid_inet_get_option_improper_list,
+ invalid_inet_set_option,
+ invalid_inet_set_option_not_list,
+ invalid_inet_set_option_improper_list,
+ dh_params,
+ ecertfile,
+ ecacertfile,
+ ekeyfile,
+ eoptions,
+ protocol_versions,
+ empty_protocol_versions,
+ ipv6,
+ reuseaddr,
+ tcp_reuseaddr].
+
+api_tests() ->
+ [connection_info,
+ peername,
+ peercert,
+ peercert_with_client_cert,
+ sockname,
+ versions,
+ controlling_process,
+ upgrade,
+ upgrade_with_timeout,
+ shutdown,
+ shutdown_write,
+ shutdown_both,
+ shutdown_error,
+ hibernate,
+ listen_socket,
+ ssl_accept_timeout,
+ ssl_recv_timeout
+ ].
+
+session_tests() ->
+ [reuse_session,
+ reuse_session_expired,
+ server_does_not_want_to_reuse_session,
+ no_reuses_session_server_restart_new_cert,
+ no_reuses_session_server_restart_new_cert_file].
+
+renegotiate_tests() ->
+ [client_renegotiate,
+ server_renegotiate,
+ client_renegotiate_reused_session,
+ server_renegotiate_reused_session,
+ client_no_wrap_sequence_number,
+ server_no_wrap_sequence_number,
+ renegotiate_dos_mitigate_active,
+ renegotiate_dos_mitigate_passive].
+
+cipher_tests() ->
+ [cipher_suites,
+ ciphers_rsa_signed_certs,
+ ciphers_rsa_signed_certs_openssl_names,
+ ciphers_dsa_signed_certs,
+ ciphers_dsa_signed_certs_openssl_names,
+ anonymous_cipher_suites,
+ default_reject_anonymous].
+
+error_handling_tests()->
+ [controller_dies,
+ client_closes_socket,
+ tcp_error_propagation_in_active_mode,
+ tcp_connect,
+ tcp_connect_big,
+ close_transport_accept
+ ].
+
+rizzo_tests() ->
+ [rizzo,
+ no_rizzo_rc4].
+
%%--------------------------------------------------------------------
init_per_suite(Config0) ->
- Dog = ssl_test_lib:timetrap(?LONG_TIMEOUT *2),
+ Dog = ct:timetrap(?LONG_TIMEOUT *2),
catch crypto:stop(),
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]),
+ ct:print("Make certs ~p~n", [Result]),
Config1 = ssl_test_lib:make_dsa_cert(Config0),
Config = ssl_test_lib:cert_options(Config1),
@@ -68,67 +188,52 @@ init_per_suite(Config0) ->
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_group(GroupName, Config) ->
+ case ssl_test_lib:is_tls_version(GroupName) of
+ true ->
+ case ssl_test_lib:sufficient_crypto_support(GroupName) of
+ true ->
+ ssl_test_lib:init_tls_version(GroupName),
+ Config;
+ false ->
+ {skip, "Missing crypto support"}
+ end;
+ _ ->
+ ssl:start(),
+ Config
+ end.
-init_per_testcase(session_cache_process_mnesia, Config) ->
- mnesia:start(),
- init_customized_session_cache(mnesia, Config);
-init_per_testcase(reuse_session_expired, 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];
+end_per_group(_GroupName, Config) ->
+ Config.
+%%--------------------------------------------------------------------
init_per_testcase(no_authority_key_identifier, Config) ->
%% Clear cach so that root cert will not
%% be found.
- ssl:stop(),
- ssl:start(),
+ ssl:clear_pem_cache(),
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 ->
+init_per_testcase(protocol_versions, Config) ->
ssl:stop(),
application:load(ssl),
- application:set_env(ssl, protocol_version, sslv3),
+ %% For backwards compatibility sslv2 should be filtered out.
+ application:set_env(ssl, protocol_version, [sslv2, sslv3, tlsv1]),
ssl:start(),
Config;
-init_per_testcase(protocol_versions, Config) ->
+init_per_testcase(reuse_session_expired, Config0) ->
+ Config = lists:keydelete(watchdog, 1, Config0),
ssl:stop(),
application:load(ssl),
- %% For backwards compatibility sslv2 should be filtered out.
- application:set_env(ssl, protocol_version, [sslv2, sslv3, tlsv1]),
+ application:set_env(ssl, session_lifetime, ?EXPIRE),
+ application:set_env(ssl, session_delay_cleanup_time, 500),
ssl:start(),
Config;
@@ -139,155 +244,33 @@ init_per_testcase(empty_protocol_versions, Config) ->
ssl:start(),
Config;
-init_per_testcase(different_ca_peer_sign, Config0) ->
- ssl_test_lib:make_mix_cert(Config0);
+%% init_per_testcase(different_ca_peer_sign, Config0) ->
+%% ssl_test_lib:make_mix_cert(Config0);
init_per_testcase(_TestCase, Config0) ->
+ ct:print("TLS/SSL version ~p~n ", [ssl_record:supported_protocol_versions()]),
Config = lists:keydelete(watchdog, 1, Config0),
- Dog = test_server:timetrap(?TIMEOUT),
+ Dog = ct: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:stop(),
- ssl:stop(),
- ssl:start(),
- end_per_testcase(default_action, Config);
end_per_testcase(reuse_session_expired, Config) ->
application:unset_env(ssl, session_lifetime),
+ application:unset_env(ssl, session_delay_cleanup_time),
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
- 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() ->
- [app, alerts, connection_info, protocol_versions,
- empty_protocol_versions, controlling_process,
- controller_dies, client_closes_socket,
- connect_dist, peername, peercert, sockname, socket_options,
- invalid_inet_get_option, invalid_inet_get_option_not_list,
- invalid_inet_get_option_improper_list,
- invalid_inet_set_option, invalid_inet_set_option_not_list,
- invalid_inet_set_option_improper_list,
- misc_ssl_options, versions, cipher_suites, upgrade,
- upgrade_with_timeout, tcp_connect, tcp_connect_big, 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,
- 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_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,
- verify_fun_always_run_client,
- verify_fun_always_run_server,
- unknown_server_ca_fail, der_input,
- unknown_server_ca_accept_verify_none,
- unknown_server_ca_accept_verify_peer,
- unknown_server_ca_accept_backwardscompatibility,
- %%different_ca_peer_sign,
- no_reuses_session_server_restart_new_cert,
- no_reuses_session_server_restart_new_cert_file, reuseaddr,
- hibernate, connect_twice, renegotiate_dos_mitigate_active,
- renegotiate_dos_mitigate_passive,
- tcp_error_propagation_in_active_mode, rizzo, no_rizzo_rc4,
- recv_error_handling
- ].
-groups() ->
- [].
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
+end_per_testcase(_TestCase, Config) ->
Config.
-%% Test cases starts here.
%%--------------------------------------------------------------------
-app(doc) ->
- "Test that the ssl app file is ok";
-app(suite) ->
- [];
+%% Test Cases --------------------------------------------------------
+%%--------------------------------------------------------------------
+app() ->
+ [{doc, "Test that the ssl app file is ok"}].
app(Config) when is_list(Config) ->
- ok = test_server:app_test(ssl).
+ ok = ?t:app_test(ssl).
%%--------------------------------------------------------------------
-alerts(doc) ->
- "Test ssl_alert:alert_txt/1";
-alerts(suite) ->
- [];
+alerts() ->
+ [{doc, "Test ssl_alert:alert_txt/1"}].
alerts(Config) when is_list(Config) ->
Descriptions = [?CLOSE_NOTIFY, ?UNEXPECTED_MESSAGE, ?BAD_RECORD_MAC,
?DECRYPTION_FAILED, ?RECORD_OVERFLOW, ?DECOMPRESSION_FAILURE,
@@ -304,14 +287,12 @@ alerts(Config) when is_list(Config) ->
Txt when is_list(Txt) ->
ok;
Other ->
- test_server:fail({unexpected, Other})
+ ct:fail({unexpected, Other})
end
end, Alerts).
%%--------------------------------------------------------------------
-connection_info(doc) ->
- ["Test the API function ssl:connection_info/1"];
-connection_info(suite) ->
- [];
+connection_info() ->
+ [{doc,"Test the API function ssl:connection_info/1"}].
connection_info(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
ServerOpts = ?config(server_opts, Config),
@@ -330,7 +311,7 @@ connection_info(Config) when is_list(Config) ->
[{ciphers,[{rsa,rc4_128,sha,no_export}]} |
ClientOpts]}]),
- test_server:format("Testcase ~p, Client ~p Server ~p ~n",
+ ct:print("Testcase ~p, Client ~p Server ~p ~n",
[self(), Client, Server]),
Version =
@@ -343,58 +324,23 @@ connection_info(Config) when is_list(Config) ->
ssl_test_lib:close(Server),
ssl_test_lib:close(Client).
-connection_info_result(Socket) ->
- ssl:connection_info(Socket).
-
%%--------------------------------------------------------------------
-
-protocol_versions(doc) ->
- ["Test to set a list of protocol versions in app environment."];
-
-protocol_versions(suite) ->
- [];
+protocol_versions() ->
+ [{doc,"Test to set a list of protocol versions in app environment."}].
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() ->
+ [{doc,"Test to set an empty list of protocol versions in app environment."}].
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"];
-
-controlling_process(suite) ->
- [];
+controlling_process() ->
+ [{doc,"Test API function controlling_process/2"}].
controlling_process(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
@@ -418,7 +364,7 @@ controlling_process(Config) when is_list(Config) ->
ClientMsg]}},
{options, ClientOpts}]),
- test_server:format("Testcase ~p, Client ~p Server ~p ~n",
+ ct:print("Testcase ~p, Client ~p Server ~p ~n",
[self(), Client, Server]),
receive
@@ -437,46 +383,15 @@ controlling_process(Config) when is_list(Config) ->
ok
end;
Unexpected ->
- test_server:fail(Unexpected)
+ ct:fail(Unexpected)
end,
ssl_test_lib:close(Server),
ssl_test_lib:close(Client).
-controlling_process_result(Socket, Pid, Msg) ->
- ok = ssl:controlling_process(Socket, Pid),
- %% Make sure other side has evaluated controlling_process
- %% before message is sent
- test_server:sleep(?SLEEP),
- ssl:send(Socket, Msg),
- no_result_msg.
-
-receive_s_rizzo_duong_beast() ->
- receive
- {ssl, _, "erver hello"} ->
- receive
- {ssl, _, "C"} ->
- receive
- {ssl, _, "lient hello"} ->
- ok
- end
- end
- end.
-receive_c_rizzo_duong_beast() ->
- receive
- {ssl, _, "lient hello"} ->
- receive
- {ssl, _, "S"} ->
- receive
- {ssl, _, "erver hello"} ->
- ok
- end
- end
- end.
%%--------------------------------------------------------------------
-controller_dies(doc) ->
- ["Test that the socket is closed after controlling process dies"];
-controller_dies(suite) -> [];
+controller_dies() ->
+ [{doc,"Test that the socket is closed after controlling process dies"}].
controller_dies(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
ServerOpts = ?config(server_opts, Config),
@@ -499,8 +414,8 @@ controller_dies(Config) when is_list(Config) ->
ClientMsg]}},
{options, ClientOpts}]),
- test_server:format("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]),
- test_server:sleep(?SLEEP), %% so that they are connected
+ ct:print("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]),
+ ct:sleep(?SLEEP), %% so that they are connected
process_flag(trap_exit, true),
@@ -517,7 +432,7 @@ controller_dies(Config) when is_list(Config) ->
%% Make sure server finishes and verification
%% and is in coonection state before
%% killing client
- test_server:sleep(?SLEEP),
+ ct:sleep(?SLEEP),
Pid ! {self(), connected, Socket},
receive die_nice -> normal end
end,
@@ -537,13 +452,13 @@ controller_dies(Config) when is_list(Config) ->
Client3 ! die_nice
end,
- test_server:format("Wating on exit ~p~n",[Client3]),
+ ct:print("Wating on exit ~p~n",[Client3]),
receive {'EXIT', Client3, normal} -> ok end,
receive %% Client3 is dead but that doesn't matter, socket should not be closed.
Unexpected ->
- test_server:format("Unexpected ~p~n",[Unexpected]),
- test_server:fail({line, ?LINE-1})
+ ct:print("Unexpected ~p~n",[Unexpected]),
+ ct:fail({line, ?LINE-1})
after 1000 ->
ok
end,
@@ -559,39 +474,17 @@ controller_dies(Config) when is_list(Config) ->
controller_dies_result, [self(),
ClientMsg]}},
{options, [{reuseaddr,true}|ClientOpts]}]),
- test_server:sleep(?SLEEP), %% so that they are connected
+ ct:sleep(?SLEEP), %% so that they are connected
exit(Server, killed),
get_close(Server, ?LINE),
process_flag(trap_exit, false),
ssl_test_lib:close(LastClient).
-controller_dies_result(_Socket, _Pid, _Msg) ->
- receive Result -> Result end.
-
-get_close(Pid, Where) ->
- receive
- {'EXIT', Pid, _Reason} ->
- receive
- {_, {ssl_closed, Socket}} ->
- test_server:format("Socket closed ~p~n",[Socket]);
- Unexpected ->
- test_server:format("Unexpected ~p~n",[Unexpected]),
- test_server:fail({line, ?LINE-1})
- after 5000 ->
- test_server:fail({timeout, {line, ?LINE, Where}})
- end;
- Unexpected ->
- test_server:format("Unexpected ~p~n",[Unexpected]),
- test_server:fail({line, ?LINE-1})
- after 5000 ->
- test_server:fail({timeout, {line, ?LINE, Where}})
- end.
-
%%--------------------------------------------------------------------
-client_closes_socket(doc) ->
- ["Test what happens when client closes socket before handshake is compleated"];
-client_closes_socket(suite) -> [];
+client_closes_socket() ->
+ [{doc,"Test what happens when client closes socket before handshake is compleated"}].
+
client_closes_socket(Config) when is_list(Config) ->
ServerOpts = ?config(server_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
@@ -608,7 +501,7 @@ client_closes_socket(Config) when is_list(Config) ->
[Hostname, Port, TcpOpts]),
%% Make sure that ssl_accept is called before
%% client process ends and closes socket.
- test_server:sleep(?SLEEP)
+ ct:sleep(?SLEEP)
end,
_Client = spawn_link(Connect),
@@ -616,11 +509,8 @@ client_closes_socket(Config) when is_list(Config) ->
ssl_test_lib:check_result(Server, {error,closed}).
%%--------------------------------------------------------------------
-connect_dist(doc) ->
- ["Test a simple connect as is used by distribution"];
-
-connect_dist(suite) ->
- [];
+connect_dist() ->
+ [{doc,"Test a simple connect as is used by distribution"}].
connect_dist(Config) when is_list(Config) ->
ClientOpts0 = ?config(client_kc_opts, Config),
@@ -646,22 +536,36 @@ connect_dist(Config) when is_list(Config) ->
ssl_test_lib:close(Server),
ssl_test_lib:close(Client).
-connect_dist_s(S) ->
- Msg = term_to_binary({erlang,term}),
- ok = ssl:send(S, Msg).
-
-connect_dist_c(S) ->
- Test = binary_to_list(term_to_binary({erlang,term})),
- {ok, Test} = ssl:recv(S, 0, 10000),
- ok.
+%%--------------------------------------------------------------------
+clear_pem_cache() ->
+ [{doc,"Test that internal reference tabel is cleaned properly even when "
+ " the PEM cache is cleared" }].
+clear_pem_cache(Config) when is_list(Config) ->
+ {status, _, _, StatusInfo} = sys:get_status(whereis(ssl_manager)),
+ [_, _,_, _, Prop] = StatusInfo,
+ State = ssl_test_lib:state(Prop),
+ [_,FilRefDb, _] = element(5, State),
+ {Server, Client} = basic_verify_test_no_close(Config),
+ 2 = ets:info(FilRefDb, size),
+ ssl:clear_pem_cache(),
+ _ = sys:get_status(whereis(ssl_manager)),
+ {Server1, Client1} = basic_verify_test_no_close(Config),
+ 4 = ets:info(FilRefDb, size),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client),
+ ct:sleep(5000),
+ _ = sys:get_status(whereis(ssl_manager)),
+ 2 = ets:info(FilRefDb, size),
+ ssl_test_lib:close(Server1),
+ ssl_test_lib:close(Client1),
+ ct:sleep(5000),
+ _ = sys:get_status(whereis(ssl_manager)),
+ 0 = ets:info(FilRefDb, size).
%%--------------------------------------------------------------------
-peername(doc) ->
- ["Test API function peername/1"];
-
-peername(suite) ->
- [];
+peername() ->
+ [{doc,"Test API function peername/1"}].
peername(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
@@ -684,7 +588,7 @@ peername(Config) when is_list(Config) ->
ServerMsg = {ok, {ClientIp, ClientPort}},
ClientMsg = {ok, {ServerIp, Port}},
- test_server:format("Testcase ~p, Client ~p Server ~p ~n",
+ ct:print("Testcase ~p, Client ~p Server ~p ~n",
[self(), Client, Server]),
ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg),
@@ -692,14 +596,9 @@ peername(Config) when is_list(Config) ->
ssl_test_lib:close(Server),
ssl_test_lib:close(Client).
-peername_result(S) ->
- ssl:peername(S).
-
%%--------------------------------------------------------------------
-peercert(doc) ->
- [""];
-peercert(suite) ->
- [];
+peercert() ->
+ [{doc,"Test API function peercert/1"}].
peercert(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
ServerOpts = ?config(server_opts, Config),
@@ -722,7 +621,7 @@ peercert(Config) when is_list(Config) ->
ServerMsg = {error, no_peercert},
ClientMsg = {ok, BinCert},
- test_server:format("Testcase ~p, Client ~p Server ~p ~n",
+ ct:print("Testcase ~p, Client ~p Server ~p ~n",
[self(), Client, Server]),
ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg),
@@ -732,14 +631,45 @@ peercert(Config) when is_list(Config) ->
peercert_result(Socket) ->
ssl:peercert(Socket).
-
%%--------------------------------------------------------------------
-sockname(doc) ->
- ["Test API function sockname/1"];
-sockname(suite) ->
- [];
+peercert_with_client_cert() ->
+ [{doc,"Test API function peercert/1"}].
+peercert_with_client_cert(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_dsa_opts, Config),
+ ServerOpts = ?config(server_dsa_verify_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, peercert_result, []}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, peercert_result, []}},
+ {options, ClientOpts}]),
+
+ ServerCertFile = proplists:get_value(certfile, ServerOpts),
+ [{'Certificate', ServerBinCert, _}]= ssl_test_lib:pem_to_der(ServerCertFile),
+ ClientCertFile = proplists:get_value(certfile, ClientOpts),
+ [{'Certificate', ClientBinCert, _}]= ssl_test_lib:pem_to_der(ClientCertFile),
+
+ ServerMsg = {ok, ClientBinCert},
+ ClientMsg = {ok, ServerBinCert},
+
+ ct:print("Testcase ~p, Client ~p Server ~p ~n",
+ [self(), Client, Server]),
+
+ ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+sockname() ->
+ [{doc,"Test API function sockname/1"}].
sockname(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
ServerOpts = ?config(server_opts, Config),
@@ -761,7 +691,7 @@ sockname(Config) when is_list(Config) ->
ServerMsg = {ok, {ServerIp, Port}},
ClientMsg = {ok, {ClientIp, ClientPort}},
- test_server:format("Testcase ~p, Client ~p Server ~p ~n",
+ ct:print("Testcase ~p, Client ~p Server ~p ~n",
[self(), Client, Server]),
ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg),
@@ -773,11 +703,8 @@ sockname_result(S) ->
ssl:sockname(S).
%%--------------------------------------------------------------------
-cipher_suites(doc) ->
- ["Test API function cipher_suites/0"];
-
-cipher_suites(suite) ->
- [];
+cipher_suites() ->
+ [{doc,"Test API function cipher_suites/0"}].
cipher_suites(Config) when is_list(Config) ->
MandatoryCipherSuite = {rsa,'3des_ede_cbc',sha},
@@ -787,11 +714,8 @@ cipher_suites(Config) when is_list(Config) ->
[_|_] =ssl:cipher_suites(openssl).
%%--------------------------------------------------------------------
-socket_options(doc) ->
- ["Test API function getopts/2 and setopts/2"];
-
-socket_options(suite) ->
- [];
+socket_options() ->
+ [{doc,"Test API function getopts/2 and setopts/2"}].
socket_options(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
@@ -840,16 +764,13 @@ socket_options_result(Socket, Options, DefaultValues, NewOptions, NewValues) ->
ssl:setopts(Socket, [{nodelay, true}]),
{ok,[{nodelay, true}]} = ssl:getopts(Socket, [nodelay]),
{ok, All} = ssl:getopts(Socket, []),
- test_server:format("All opts ~p~n", [All]),
+ ct:print("All opts ~p~n", [All]),
ok.
%%--------------------------------------------------------------------
-invalid_inet_get_option(doc) ->
- ["Test handling of invalid inet options in getopts"];
-
-invalid_inet_get_option(suite) ->
- [];
+invalid_inet_get_option() ->
+ [{doc,"Test handling of invalid inet options in getopts"}].
invalid_inet_get_option(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
@@ -866,24 +787,16 @@ invalid_inet_get_option(Config) when is_list(Config) ->
{mfa, {ssl_test_lib, no_result, []}},
{options, ClientOpts}]),
- test_server:format("Testcase ~p, Client ~p Server ~p ~n",
+ ct:print("Testcase ~p, Client ~p Server ~p ~n",
[self(), Client, Server]),
ssl_test_lib:check_result(Server, ok),
ssl_test_lib:close(Server),
ssl_test_lib:close(Client).
-
-get_invalid_inet_option(Socket) ->
- {error, {eoptions, {inet_option, foo, _}}} = ssl:getopts(Socket, [foo]),
- ok.
-
%%--------------------------------------------------------------------
-invalid_inet_get_option_not_list(doc) ->
- ["Test handling of invalid type in getopts"];
-
-invalid_inet_get_option_not_list(suite) ->
- [];
+invalid_inet_get_option_not_list() ->
+ [{doc,"Test handling of invalid type in getopts"}].
invalid_inet_get_option_not_list(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
@@ -900,7 +813,7 @@ invalid_inet_get_option_not_list(Config) when is_list(Config) ->
{mfa, {ssl_test_lib, no_result, []}},
{options, ClientOpts}]),
- test_server:format("Testcase ~p, Client ~p Server ~p ~n",
+ ct:print("Testcase ~p, Client ~p Server ~p ~n",
[self(), Client, Server]),
ssl_test_lib:check_result(Server, ok),
@@ -914,11 +827,8 @@ get_invalid_inet_option_not_list(Socket) ->
ok.
%%--------------------------------------------------------------------
-invalid_inet_get_option_improper_list(doc) ->
- ["Test handling of invalid type in getopts"];
-
-invalid_inet_get_option_improper_list(suite) ->
- [];
+invalid_inet_get_option_improper_list() ->
+ [{doc,"Test handling of invalid type in getopts"}].
invalid_inet_get_option_improper_list(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
@@ -935,7 +845,7 @@ invalid_inet_get_option_improper_list(Config) when is_list(Config) ->
{mfa, {ssl_test_lib, no_result, []}},
{options, ClientOpts}]),
- test_server:format("Testcase ~p, Client ~p Server ~p ~n",
+ ct:print("Testcase ~p, Client ~p Server ~p ~n",
[self(), Client, Server]),
ssl_test_lib:check_result(Server, ok),
@@ -948,11 +858,8 @@ get_invalid_inet_option_improper_list(Socket) ->
ok.
%%--------------------------------------------------------------------
-invalid_inet_set_option(doc) ->
- ["Test handling of invalid inet options in setopts"];
-
-invalid_inet_set_option(suite) ->
- [];
+invalid_inet_set_option() ->
+ [{doc,"Test handling of invalid inet options in setopts"}].
invalid_inet_set_option(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
@@ -969,7 +876,7 @@ invalid_inet_set_option(Config) when is_list(Config) ->
{mfa, {ssl_test_lib, no_result, []}},
{options, ClientOpts}]),
- test_server:format("Testcase ~p, Client ~p Server ~p ~n",
+ ct:print("Testcase ~p, Client ~p Server ~p ~n",
[self(), Client, Server]),
ssl_test_lib:check_result(Server, ok),
@@ -983,11 +890,8 @@ set_invalid_inet_option(Socket) ->
{error, {eoptions, {inet_opt, {mode, foo}}}} = ssl:setopts(Socket, [{mode, foo}]),
ok.
%%--------------------------------------------------------------------
-invalid_inet_set_option_not_list(doc) ->
- ["Test handling of invalid type in setopts"];
-
-invalid_inet_set_option_not_list(suite) ->
- [];
+invalid_inet_set_option_not_list() ->
+ [{doc,"Test handling of invalid type in setopts"}].
invalid_inet_set_option_not_list(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
@@ -1004,7 +908,7 @@ invalid_inet_set_option_not_list(Config) when is_list(Config) ->
{mfa, {ssl_test_lib, no_result, []}},
{options, ClientOpts}]),
- test_server:format("Testcase ~p, Client ~p Server ~p ~n",
+ ct:print("Testcase ~p, Client ~p Server ~p ~n",
[self(), Client, Server]),
ssl_test_lib:check_result(Server, ok),
@@ -1018,11 +922,8 @@ set_invalid_inet_option_not_list(Socket) ->
ok.
%%--------------------------------------------------------------------
-invalid_inet_set_option_improper_list(doc) ->
- ["Test handling of invalid tye in setopts"];
-
-invalid_inet_set_option_improper_list(suite) ->
- [];
+invalid_inet_set_option_improper_list() ->
+ [{doc,"Test handling of invalid tye in setopts"}].
invalid_inet_set_option_improper_list(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
@@ -1039,7 +940,7 @@ invalid_inet_set_option_improper_list(Config) when is_list(Config) ->
{mfa, {ssl_test_lib, no_result, []}},
{options, ClientOpts}]),
- test_server:format("Testcase ~p, Client ~p Server ~p ~n",
+ ct:print("Testcase ~p, Client ~p Server ~p ~n",
[self(), Client, Server]),
ssl_test_lib:check_result(Server, ok),
@@ -1052,11 +953,8 @@ set_invalid_inet_option_improper_list(Socket) ->
ok.
%%--------------------------------------------------------------------
-misc_ssl_options(doc) ->
- ["Test what happens when we give valid options"];
-
-misc_ssl_options(suite) ->
- [];
+misc_ssl_options() ->
+ [{doc,"Test what happens when we give valid options"}].
misc_ssl_options(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
@@ -1074,17 +972,17 @@ misc_ssl_options(Config) when is_list(Config) ->
Server =
ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
{from, self()},
- {mfa, {?MODULE, send_recv_result_active, []}},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}},
{options, TestOpts ++ 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, []}},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}},
{options, TestOpts ++ ClientOpts}]),
- test_server:format("Testcase ~p, Client ~p Server ~p ~n",
+ ct:print("Testcase ~p, Client ~p Server ~p ~n",
[self(), Client, Server]),
ssl_test_lib:check_result(Server, ok, Client, ok),
@@ -1092,23 +990,16 @@ misc_ssl_options(Config) when is_list(Config) ->
ssl_test_lib:close(Client).
%%--------------------------------------------------------------------
-versions(doc) ->
- ["Test API function versions/0"];
-
-versions(suite) ->
- [];
+versions() ->
+ [{doc,"Test API function versions/0"}].
versions(Config) when is_list(Config) ->
[_|_] = Versions = ssl:versions(),
- test_server:format("~p~n", [Versions]).
+ ct:print("~p~n", [Versions]).
%%--------------------------------------------------------------------
-send_recv(doc) ->
- [""];
-
-send_recv(suite) ->
- [];
-
+send_recv() ->
+ [{doc,""}].
send_recv(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
ServerOpts = ?config(server_opts, Config),
@@ -1116,17 +1007,17 @@ send_recv(Config) when is_list(Config) ->
Server =
ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
{from, self()},
- {mfa, {?MODULE, send_recv_result, []}},
+ {mfa, {ssl_test_lib, 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, []}},
+ {mfa, {ssl_test_lib, send_recv_result, []}},
{options, [{active, false} | ClientOpts]}]),
- test_server:format("Testcase ~p, Client ~p Server ~p ~n",
+ ct:print("Testcase ~p, Client ~p Server ~p ~n",
[self(), Client, Server]),
ssl_test_lib:check_result(Server, ok, Client, ok),
@@ -1135,12 +1026,8 @@ send_recv(Config) when is_list(Config) ->
ssl_test_lib:close(Client).
%%--------------------------------------------------------------------
-send_close(doc) ->
- [""];
-
-send_close(suite) ->
- [];
-
+send_close() ->
+ [{doc,""}].
send_close(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
ServerOpts = ?config(server_opts, Config),
@@ -1148,7 +1035,7 @@ send_close(Config) when is_list(Config) ->
Server =
ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
{from, self()},
- {mfa, {?MODULE, send_recv_result, []}},
+ {mfa, {ssl_test_lib, send_recv_result, []}},
{options, [{active, false} | ServerOpts]}]),
Port = ssl_test_lib:inet_port(Server),
{ok, TcpS} = rpc:call(ClientNode, gen_tcp, connect,
@@ -1156,7 +1043,7 @@ send_close(Config) when is_list(Config) ->
{ok, SslS} = rpc:call(ClientNode, ssl, connect,
[TcpS,[{active, false}|ClientOpts]]),
- test_server:format("Testcase ~p, Client ~p Server ~p ~n",
+ ct:print("Testcase ~p, Client ~p Server ~p ~n",
[self(), self(), Server]),
ok = ssl:send(SslS, "Hello world"),
{ok,<<"Hello world">>} = ssl:recv(SslS, 11),
@@ -1164,11 +1051,8 @@ send_close(Config) when is_list(Config) ->
{error, _} = ssl:send(SslS, "Hello world").
%%--------------------------------------------------------------------
-close_transport_accept(doc) ->
- ["Tests closing ssl socket when waiting on ssl:transport_accept/1"];
-
-close_transport_accept(suite) ->
- [];
+close_transport_accept() ->
+ [{doc,"Tests closing ssl socket when waiting on ssl:transport_accept/1"}].
close_transport_accept(Config) when is_list(Config) ->
ServerOpts = ?config(server_opts, Config),
@@ -1178,7 +1062,7 @@ close_transport_accept(Config) when is_list(Config) ->
Opts = [{active, false} | ServerOpts],
{ok, ListenSocket} = rpc:call(ServerNode, ssl, listen, [Port, Opts]),
spawn_link(fun() ->
- test_server:sleep(?SLEEP),
+ ct:sleep(?SLEEP),
rpc:call(ServerNode, ssl, close, [ListenSocket])
end),
case rpc:call(ServerNode, ssl, transport_accept, [ListenSocket]) of
@@ -1189,11 +1073,8 @@ close_transport_accept(Config) when is_list(Config) ->
end.
%%--------------------------------------------------------------------
-dh_params(doc) ->
- ["Test to specify DH-params file in server."];
-
-dh_params(suite) ->
- [];
+dh_params() ->
+ [{doc,"Test to specify DH-params file in server."}].
dh_params(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
@@ -1205,13 +1086,13 @@ dh_params(Config) when is_list(Config) ->
Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
{from, self()},
- {mfa, {?MODULE, send_recv_result_active, []}},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}},
{options, [{dhfile, DHParamFile} | 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, []}},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}},
{options,
[{ciphers,[{dhe_rsa,aes_256_cbc,sha,ignore}]} |
ClientOpts]}]),
@@ -1222,11 +1103,8 @@ dh_params(Config) when is_list(Config) ->
ssl_test_lib:close(Client).
%%--------------------------------------------------------------------
-upgrade(doc) ->
- ["Test that you can upgrade an tcp connection to an ssl connection"];
-
-upgrade(suite) ->
- [];
+upgrade() ->
+ [{doc,"Test that you can upgrade an tcp connection to an ssl connection"}].
upgrade(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
@@ -1250,7 +1128,7 @@ upgrade(Config) when is_list(Config) ->
{tcp_options, TcpOpts},
{ssl_options, ClientOpts}]),
- test_server:format("Testcase ~p, Client ~p Server ~p ~n",
+ ct:print("Testcase ~p, Client ~p Server ~p ~n",
[self(), Client, Server]),
ssl_test_lib:check_result(Server, ok, Client, ok),
@@ -1274,11 +1152,8 @@ upgrade_result(Socket) ->
end.
%%--------------------------------------------------------------------
-upgrade_with_timeout(doc) ->
- ["Test ssl_accept/3"];
-
-upgrade_with_timeout(suite) ->
- [];
+upgrade_with_timeout() ->
+ [{doc,"Test ssl_accept/3"}].
upgrade_with_timeout(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
@@ -1303,7 +1178,7 @@ upgrade_with_timeout(Config) when is_list(Config) ->
{tcp_options, TcpOpts},
{ssl_options, ClientOpts}]),
- test_server:format("Testcase ~p, Client ~p Server ~p ~n",
+ ct:print("Testcase ~p, Client ~p Server ~p ~n",
[self(), Client, Server]),
ssl_test_lib:check_result(Server, ok, Client, ok),
@@ -1312,11 +1187,8 @@ upgrade_with_timeout(Config) when is_list(Config) ->
ssl_test_lib:close(Client).
%%--------------------------------------------------------------------
-tcp_connect(doc) ->
- ["Test what happens when a tcp tries to connect, i,e. a bad (ssl) packet is sent first"];
-
-tcp_connect(suite) ->
- [];
+tcp_connect() ->
+ [{doc,"Test what happens when a tcp tries to connect, i,e. a bad (ssl) packet is sent first"}].
tcp_connect(Config) when is_list(Config) ->
ServerOpts = ?config(server_opts, Config),
@@ -1332,22 +1204,19 @@ tcp_connect(Config) when is_list(Config) ->
Port = ssl_test_lib:inet_port(Server),
{ok, Socket} = gen_tcp:connect(Hostname, Port, [binary, {packet, 0}]),
- test_server:format("Testcase ~p connected to Server ~p ~n", [self(), Server]),
+ ct:print("Testcase ~p connected to Server ~p ~n", [self(), Server]),
gen_tcp:send(Socket, "<SOME GARBLED NON SSL MESSAGE>"),
receive
{tcp_closed, Socket} ->
receive
{Server, {error, Error}} ->
- test_server:format("Error ~p", [Error])
+ ct:print("Error ~p", [Error])
end
end.
-
-tcp_connect_big(doc) ->
- ["Test what happens when a tcp tries to connect, i,e. a bad big (ssl) packet is sent first"];
-
-tcp_connect_big(suite) ->
- [];
+%%--------------------------------------------------------------------
+tcp_connect_big() ->
+ [{doc,"Test what happens when a tcp tries to connect, i,e. a bad big (ssl) packet is sent first"}].
tcp_connect_big(Config) when is_list(Config) ->
ServerOpts = ?config(server_opts, Config),
@@ -1363,7 +1232,7 @@ tcp_connect_big(Config) when is_list(Config) ->
Port = ssl_test_lib:inet_port(Server),
{ok, Socket} = gen_tcp:connect(Hostname, Port, [binary, {packet, 0}]),
- test_server:format("Testcase ~p connected to Server ~p ~n", [self(), Server]),
+ ct:print("Testcase ~p connected to Server ~p ~n", [self(), Server]),
Rand = crypto:rand_bytes(?MAX_CIPHER_TEXT_LENGTH+1),
gen_tcp:send(Socket, <<?BYTE(0),
@@ -1373,24 +1242,16 @@ tcp_connect_big(Config) when is_list(Config) ->
{tcp_closed, Socket} ->
receive
{Server, {error, timeout}} ->
- test_server:fail("hangs");
+ ct:fail("hangs");
{Server, {error, Error}} ->
- test_server:format("Error ~p", [Error])
+ ct:print("Error ~p", [Error])
end
end.
-dummy(_Socket) ->
- %% Should not happen as the ssl connection will not be established
- %% due to fatal handshake failiure
- exit(kill).
-
%%--------------------------------------------------------------------
ipv6() ->
- [{require, ipv6_hosts}].
-ipv6(doc) ->
- ["Test ipv6."];
-ipv6(suite) ->
- [];
+ [{require, ipv6_hosts},
+ {doc,"Test ipv6."}].
ipv6(Config) when is_list(Config) ->
{ok, Hostname0} = inet:gethostname(),
@@ -1402,18 +1263,18 @@ ipv6(Config) when is_list(Config) ->
ssl_test_lib:run_where(Config, ipv6),
Server = ssl_test_lib:start_server([{node, ServerNode},
{port, 0}, {from, self()},
- {mfa, {?MODULE, send_recv_result, []}},
+ {mfa, {ssl_test_lib, send_recv_result, []}},
{options,
[inet6, {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, []}},
+ {mfa, {ssl_test_lib, send_recv_result, []}},
{options,
[inet6, {active, false} | ClientOpts]}]),
- test_server:format("Testcase ~p, Client ~p Server ~p ~n",
+ ct:print("Testcase ~p, Client ~p Server ~p ~n",
[self(), Client, Server]),
ssl_test_lib:check_result(Server, ok, Client, ok),
@@ -1426,12 +1287,8 @@ ipv6(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
-ekeyfile(doc) ->
- ["Test what happens with an invalid key file"];
-
-ekeyfile(suite) ->
- [];
-
+ekeyfile() ->
+ [{doc,"Test what happens with an invalid key file"}].
ekeyfile(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
BadOpts = ?config(server_bad_key, Config),
@@ -1454,11 +1311,8 @@ ekeyfile(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
-ecertfile(doc) ->
- ["Test what happens with an invalid cert file"];
-
-ecertfile(suite) ->
- [];
+ecertfile() ->
+ [{doc,"Test what happens with an invalid cert file"}].
ecertfile(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
@@ -1483,11 +1337,8 @@ ecertfile(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
-ecacertfile(doc) ->
- ["Test what happens with an invalid cacert file"];
-
-ecacertfile(suite) ->
- [];
+ecacertfile() ->
+ [{doc,"Test what happens with an invalid cacert file"}].
ecacertfile(Config) when is_list(Config) ->
ClientOpts = [{reuseaddr, true}|?config(client_opts, Config)],
@@ -1535,12 +1386,9 @@ ecacertfile(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
-eoptions(doc) ->
- ["Test what happens when we give invalid options"];
+eoptions() ->
+ [{doc,"Test what happens when we give invalid options"}].
-eoptions(suite) ->
- [];
-
eoptions(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
ServerOpts = ?config(server_opts, Config),
@@ -1597,12 +1445,8 @@ eoptions(Config) when is_list(Config) ->
ok.
%%--------------------------------------------------------------------
-shutdown(doc) ->
- [""];
-
-shutdown(suite) ->
- [];
-
+shutdown() ->
+ [{doc,"Test API function ssl:shutdown/2"}].
shutdown(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
ServerOpts = ?config(server_opts, Config),
@@ -1627,25 +1471,9 @@ shutdown(Config) when is_list(Config) ->
ssl_test_lib:close(Server),
ssl_test_lib:close(Client).
-shutdown_result(Socket, server) ->
- ssl:send(Socket, "Hej"),
- ssl:shutdown(Socket, write),
- {ok, "Hej hopp"} = ssl:recv(Socket, 8),
- ok;
-
-shutdown_result(Socket, client) ->
- {ok, "Hej"} = ssl:recv(Socket, 3),
- ssl:send(Socket, "Hej hopp"),
- ssl:shutdown(Socket, write),
- ok.
-
%%--------------------------------------------------------------------
-shutdown_write(doc) ->
- [""];
-
-shutdown_write(suite) ->
- [];
-
+shutdown_write() ->
+ [{doc,"Test API function ssl:shutdown/2 with option write."}].
shutdown_write(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
ServerOpts = ?config(server_opts, Config),
@@ -1662,20 +1490,10 @@ shutdown_write(Config) when is_list(Config) ->
{options, [{active, false} | ClientOpts]}]),
ssl_test_lib:check_result(Server, ok, Client, {error, closed}).
-
-shutdown_write_result(Socket, server) ->
- test_server:sleep(?SLEEP),
- ssl:shutdown(Socket, write);
-shutdown_write_result(Socket, client) ->
- ssl:recv(Socket, 0).
%%--------------------------------------------------------------------
-shutdown_both(doc) ->
- [""];
-
-shutdown_both(suite) ->
- [];
-
+shutdown_both() ->
+ [{doc,"Test API function ssl:shutdown/2 with option both."}].
shutdown_both(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
ServerOpts = ?config(server_opts, Config),
@@ -1693,19 +1511,9 @@ shutdown_both(Config) when is_list(Config) ->
ssl_test_lib:check_result(Server, ok, Client, {error, closed}).
-shutdown_both_result(Socket, server) ->
- test_server:sleep(?SLEEP),
- ssl:shutdown(Socket, read_write);
-shutdown_both_result(Socket, client) ->
- ssl:recv(Socket, 0).
-
%%--------------------------------------------------------------------
-shutdown_error(doc) ->
- [""];
-
-shutdown_error(suite) ->
- [];
-
+shutdown_error() ->
+ [{doc,"Test ssl:shutdown/2 error handling"}].
shutdown_error(Config) when is_list(Config) ->
ServerOpts = ?config(server_opts, Config),
Port = ssl_test_lib:inet_port(node()),
@@ -1715,194 +1523,60 @@ shutdown_error(Config) when is_list(Config) ->
{error, closed} = ssl:shutdown(Listen, read_write).
%%-------------------------------------------------------------------
-ciphers_rsa_signed_certs(doc) ->
- ["Test all rsa ssl cipher suites in highest support ssl/tls version"];
+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]),
+ ct:print("~p erlang cipher suites ~p~n", [Version, 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_rsa_signed_certs_openssl_names() ->
+ [{doc,"Test all rsa ssl cipher suites in highest support ssl/tls version"}].
-ciphers_rsa_signed_certs_openssl_names(suite) ->
- [];
-
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_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(),
+ ct:print("tls1 openssl cipher suites ~p~n", [Ciphers]),
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() ->
+ [{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]),
+ ct:print("~p erlang cipher suites ~p~n", [Version, 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() ->
+ [{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]),
+ ct:print("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()->
+ [{doc,"Test the anonymous ciphersuites"}].
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, ClientOpts, ServerOpts) end,
- Ciphers),
- case lists:flatten(Result) of
- [] ->
- ok;
- Error ->
- test_server:format("Cipher suite errors: ~p~n", [Error]),
- test_server:fail(cipher_suite_failed_see_test_case_log)
- end.
-
-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]),
- {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, {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, {ssl_test_lib, cipher_result, [ConnectionInfo]}},
- {options,
- [{ciphers,[CipherSuite]} |
- ClientOpts]}]),
-
- Result = ssl_test_lib:wait_for_result(Server, ok, Client, ok),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client),
-
- case Result of
- ok ->
- [];
- Error ->
- [{ErlangCipherSuite, Error}]
- end.
-
%%--------------------------------------------------------------------
-default_reject_anonymous(doc)->
- ["Test that by default anonymous cipher suites are rejected "];
-default_reject_anonymous(suite) ->
- [];
+default_reject_anonymous()->
+ [{doc,"Test that by default anonymous cipher suites are rejected "}].
default_reject_anonymous(Config) when is_list(Config) ->
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
ClientOpts = ?config(client_opts, Config),
@@ -1921,16 +1595,12 @@ default_reject_anonymous(Config) when is_list(Config) ->
[{ciphers,[Cipher]} |
ClientOpts]}]),
- ssl_test_lib:check_result(Server, {error, "insufficient security"},
- Client, {error, "insufficient security"}).
+ ssl_test_lib:check_result(Server, {error, {essl, "insufficient security"}},
+ Client, {error, {essl, "insufficient security"}}).
%%--------------------------------------------------------------------
-reuse_session(doc) ->
- ["Test reuse of sessions (short handshake)"];
-
-reuse_session(suite) ->
- [];
-
+reuse_session() ->
+ [{doc,"Test reuse of sessions (short handshake)"}].
reuse_session(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
ServerOpts = ?config(server_opts, Config),
@@ -1956,7 +1626,7 @@ reuse_session(Config) when is_list(Config) ->
Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}},
%% Make sure session is registered
- test_server:sleep(?SLEEP),
+ ct:sleep(?SLEEP),
Client1 =
ssl_test_lib:start_client([{node, ClientNode},
@@ -1967,9 +1637,9 @@ reuse_session(Config) when is_list(Config) ->
{Client1, SessionInfo} ->
ok;
{Client1, Other} ->
- test_server:format("Expected: ~p, Unexpected: ~p~n",
+ ct:print("Expected: ~p, Unexpected: ~p~n",
[SessionInfo, Other]),
- test_server:fail(session_not_reused)
+ ct:fail(session_not_reused)
end,
Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}},
@@ -1982,7 +1652,7 @@ reuse_session(Config) when is_list(Config) ->
| ClientOpts]}]),
receive
{Client2, SessionInfo} ->
- test_server:fail(
+ ct:fail(
session_reused_when_session_reuse_disabled_by_client);
{Client2, _} ->
ok
@@ -2012,7 +1682,7 @@ reuse_session(Config) when is_list(Config) ->
Server1 ! {listen, {mfa, {ssl_test_lib, no_result, []}}},
%% Make sure session is registered
- test_server:sleep(?SLEEP),
+ ct:sleep(?SLEEP),
Client4 =
ssl_test_lib:start_client([{node, ClientNode},
@@ -2022,10 +1692,10 @@ reuse_session(Config) when is_list(Config) ->
receive
{Client4, SessionInfo1} ->
- test_server:fail(
+ ct:fail(
session_reused_when_session_reuse_disabled_by_server);
{Client4, _Other} ->
- test_server:format("OTHER: ~p ~n", [_Other]),
+ ct:print("OTHER: ~p ~n", [_Other]),
ok
end,
@@ -2037,12 +1707,8 @@ reuse_session(Config) when is_list(Config) ->
ssl_test_lib:close(Client4).
%%--------------------------------------------------------------------
-reuse_session_expired(doc) ->
- ["Test sessions is not reused when it has expired"];
-
-reuse_session_expired(suite) ->
- [];
-
+reuse_session_expired() ->
+ [{doc,"Test sessions is not reused when it has expired"}].
reuse_session_expired(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
ServerOpts = ?config(server_opts, Config),
@@ -2068,7 +1734,7 @@ reuse_session_expired(Config) when is_list(Config) ->
Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}},
%% Make sure session is registered
- test_server:sleep(?SLEEP),
+ ct:sleep(?SLEEP),
Client1 =
ssl_test_lib:start_client([{node, ClientNode},
@@ -2079,24 +1745,27 @@ reuse_session_expired(Config) when is_list(Config) ->
{Client1, SessionInfo} ->
ok;
{Client1, Other} ->
- test_server:format("Expected: ~p, Unexpected: ~p~n",
+ ct:print("Expected: ~p, Unexpected: ~p~n",
[SessionInfo, Other]),
- test_server:fail(session_not_reused)
+ ct:fail(session_not_reused)
end,
Server ! listen,
%% Make sure session is unregistered due to expiration
- test_server:sleep((?EXPIRE+1) * 1000),
+ ct:sleep((?EXPIRE+1)),
+ [{session_id, Id} |_] = SessionInfo,
+
+ make_sure_expired(Hostname, Port, Id),
Client2 =
ssl_test_lib:start_client([{node, ClientNode},
- {port, Port}, {host, Hostname},
+ {port, Port}, {host, Hostname},
{mfa, {ssl_test_lib, session_info_result, []}},
- {from, self()}, {options, ClientOpts}]),
+ {from, self()}, {options, ClientOpts}]),
receive
{Client2, SessionInfo} ->
- test_server:fail(session_reused_when_session_expired);
+ ct:fail(session_reused_when_session_expired);
{Client2, _} ->
ok
end,
@@ -2106,13 +1775,25 @@ reuse_session_expired(Config) when is_list(Config) ->
ssl_test_lib:close(Client1),
ssl_test_lib:close(Client2).
-%%--------------------------------------------------------------------
-server_does_not_want_to_reuse_session(doc) ->
- ["Test reuse of sessions (short handshake)"];
+make_sure_expired(Host, Port, Id) ->
+ {status, _, _, StatusInfo} = sys:get_status(whereis(ssl_manager)),
+ [_, _,_, _, Prop] = StatusInfo,
+ State = ssl_test_lib:state(Prop),
+ Cache = element(2, State),
-server_does_not_want_to_reuse_session(suite) ->
- [];
+ case ssl_session_cache:lookup(Cache, {{Host, Port}, Id}) of
+ undefined ->
+ ok;
+ #session{is_resumable = false} ->
+ ok;
+ _ ->
+ ct:sleep(?SLEEP),
+ make_sure_expired(Host, Port, Id)
+ end.
+%%--------------------------------------------------------------------
+server_does_not_want_to_reuse_session() ->
+ [{doc,"Test reuse of sessions (short handshake)"}].
server_does_not_want_to_reuse_session(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
ServerOpts = ?config(server_opts, Config),
@@ -2141,7 +1822,7 @@ server_does_not_want_to_reuse_session(Config) when is_list(Config) ->
Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}},
%% Make sure session is registered
- test_server:sleep(?SLEEP),
+ ct:sleep(?SLEEP),
ssl_test_lib:close(Client0),
Client1 =
@@ -2151,7 +1832,7 @@ server_does_not_want_to_reuse_session(Config) when is_list(Config) ->
{from, self()}, {options, ClientOpts}]),
receive
{Client1, SessionInfo} ->
- test_server:fail(session_reused_when_server_does_not_want_to);
+ ct:fail(session_reused_when_server_does_not_want_to);
{Client1, _Other} ->
ok
end,
@@ -2160,512 +1841,55 @@ server_does_not_want_to_reuse_session(Config) when is_list(Config) ->
ssl_test_lib:close(Client1).
%%--------------------------------------------------------------------
-
-server_verify_peer_passive(doc) ->
- ["Test server option verify_peer"];
-
-server_verify_peer_passive(suite) ->
- [];
-
-server_verify_peer_passive(Config) when is_list(Config) ->
- ClientOpts = ?config(client_verification_opts, 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, []}},
- {options, [{active, false}, {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, []}},
- {options, [{active, false} | ClientOpts]}]),
-
- ssl_test_lib:check_result(Server, ok, Client, ok),
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-%%--------------------------------------------------------------------
-
-server_verify_peer_active(doc) ->
- ["Test server option verify_peer"];
-
-server_verify_peer_active(suite) ->
- [];
-
-server_verify_peer_active(Config) when is_list(Config) ->
- ClientOpts = ?config(client_verification_opts, 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}, {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, []}},
- {options, [{active, true} | ClientOpts]}]),
-
- ssl_test_lib:check_result(Server, ok, Client, ok),
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-%%--------------------------------------------------------------------
-server_verify_peer_active_once(doc) ->
- ["Test server option verify_peer"];
-
-server_verify_peer_active_once(suite) ->
- [];
-
-server_verify_peer_active_once(Config) when is_list(Config) ->
- ClientOpts = ?config(client_verification_opts, 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_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} | ClientOpts]}]),
-
- ssl_test_lib:check_result(Server, ok, Client, ok),
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-%%--------------------------------------------------------------------
-
-server_verify_none_passive(doc) ->
- ["Test server option verify_none"];
-
-server_verify_none_passive(suite) ->
- [];
-
-server_verify_none_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),
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, send_recv_result, []}},
- {options, [{active, false}, {verify, verify_none}
- | 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).
-
-%%--------------------------------------------------------------------
-
-server_verify_none_active(doc) ->
- ["Test server option verify_none"];
-
-server_verify_none_active(suite) ->
- [];
-
-server_verify_none_active(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, [{active, true}, {verify, verify_none} |
- 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).
-
-%%--------------------------------------------------------------------
-server_verify_none_active_once(doc) ->
- ["Test server option verify_none"];
-
-server_verify_none_active_once(suite) ->
- [];
-
-server_verify_none_active_once(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_once, []}},
- {options, [{active, once}, {verify, verify_none}
- | 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} | ClientOpts]}]),
-
- 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) ->
- ["Test server option verify_client_once"];
-
-server_verify_client_once_passive(suite) ->
- [];
-
-server_verify_client_once_passive(Config) when is_list(Config) ->
- ClientOpts = ?config(client_opts, 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, []}},
- {options, [{active, false}, {verify, verify_peer},
- {verify_client_once, true}
- | ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
- Client0 = 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, Client0, ok),
- Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}},
- ssl_test_lib:close(Client0),
- Client1 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE, result_ok, []}},
- {options, [{active, false} | ClientOpts]}]),
-
- ssl_test_lib:check_result(Client1, ok),
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client1).
-
-%%--------------------------------------------------------------------
-
-server_verify_client_once_active(doc) ->
- ["Test server option verify_client_once"];
-
-server_verify_client_once_active(suite) ->
- [];
-
-server_verify_client_once_active(Config) when is_list(Config) ->
- ClientOpts = ?config(client_opts, 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}, {verify, verify_peer},
- {verify_client_once, true}
- | ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
- Client0 = 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, Client0, ok),
- Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}},
- ssl_test_lib:close(Client0),
- Client1 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE, result_ok, []}},
- {options, [{active, true} | ClientOpts]}]),
-
- ssl_test_lib:check_result(Client1, ok),
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client1).
-
-%%--------------------------------------------------------------------
-
-server_verify_client_once_active_once(doc) ->
- ["Test server option verify_client_once"];
-
-server_verify_client_once_active_once(suite) ->
- [];
-
-server_verify_client_once_active_once(Config) when is_list(Config) ->
- ClientOpts = ?config(client_opts, 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_once, []}},
- {options, [{active, once}, {verify, verify_peer},
- {verify_client_once, true}
- | ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
- Client0 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE, send_recv_result_active_once, []}},
- {options, [{active, once} | ClientOpts]}]),
-
- ssl_test_lib:check_result(Server, ok, Client0, ok),
- Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}},
- ssl_test_lib:close(Client0),
- Client1 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {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) ->
- ["Test server must have cacerts if it wants to verify client"];
-
-server_verify_no_cacerts(suite) ->
- [];
-server_verify_no_cacerts(Config) when is_list(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()},
- {options, [{verify, verify_peer}
- | ServerOpts]}]),
-
- ssl_test_lib:check_result(Server, {error, {eoptions, {cacertfile, ""}}}).
-
-%%--------------------------------------------------------------------
-
-server_require_peer_cert_ok(doc) ->
- ["Test server option fail_if_no_peer_cert when peer sends cert"];
-
-server_require_peer_cert_ok(suite) ->
- [];
-
-server_require_peer_cert_ok(Config) when is_list(Config) ->
- ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true}
- | ?config(server_verification_opts, Config)],
- ClientOpts = ?config(client_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, []}},
- {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).
-
-%%--------------------------------------------------------------------
-
-server_require_peer_cert_fail(doc) ->
- ["Test server option fail_if_no_peer_cert when peer doesn't send cert"];
-
-server_require_peer_cert_fail(suite) ->
- [];
-
-server_require_peer_cert_fail(Config) when is_list(Config) ->
- ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true}
- | ?config(server_verification_opts, Config)],
- BadClientOpts = ?config(client_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
- {from, self()},
- {options, [{active, false} | ServerOpts]}]),
-
- Port = ssl_test_lib:inet_port(Server),
-
- Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {options, [{active, false} | BadClientOpts]}]),
-
- ssl_test_lib:check_result(Server, {error, esslaccept},
- Client, {error, esslconnect}).
-
-%%--------------------------------------------------------------------
-
-client_verify_none_passive(doc) ->
- ["Test client option verify_none"];
-
-client_verify_none_passive(suite) ->
- [];
-
-client_verify_none_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),
- 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},
- {verify, verify_none}
- | ClientOpts]}]),
-
- ssl_test_lib:check_result(Server, ok, Client, ok),
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-%%--------------------------------------------------------------------
-
-client_verify_none_active(doc) ->
- ["Test client option verify_none"];
-
-client_verify_none_active(suite) ->
- [];
-
-client_verify_none_active(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, [{active, true}
- | 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},
- {verify, verify_none}
- | ClientOpts]}]),
-
- ssl_test_lib:check_result(Server, ok, Client, ok),
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-%%--------------------------------------------------------------------
-client_verify_none_active_once(doc) ->
- ["Test client option verify_none"];
-
-client_verify_none_active_once(suite) ->
- [];
-
-client_verify_none_active_once(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_once, []}},
- {options, [{active, once} | 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_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."];
-
-client_renegotiate(suite) ->
- [];
-
+client_renegotiate() ->
+ [{doc,"Test ssl:renegotiate/1 on client."}].
client_renegotiate(Config) when is_list(Config) ->
- ServerOpts = ?config(server_opts, Config),
- ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ ClientOpts = ?config(client_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
+
Data = "From erlang to erlang",
- Server =
- ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ 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),
-
+
Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
{host, Hostname},
- {from, self()},
- {mfa, {?MODULE,
+ {from, self()},
+ {mfa, {?MODULE,
renegotiate, [Data]}},
{options, [{reuse_sessions, false} | ClientOpts]}]),
- ssl_test_lib:check_result(Client, ok, Server, ok),
+ ssl_test_lib:check_result(Client, ok, Server, ok),
ssl_test_lib:close(Server),
ssl_test_lib:close(Client).
%%--------------------------------------------------------------------
-server_renegotiate(doc) ->
- ["Test ssl:renegotiate/1 on server."];
-
-server_renegotiate(suite) ->
- [];
-
+server_renegotiate() ->
+ [{doc,"Test ssl:renegotiate/1 on server."}].
server_renegotiate(Config) when is_list(Config) ->
- ServerOpts = ?config(server_opts, Config),
- ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ ClientOpts = ?config(client_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
+
Data = "From erlang to erlang",
Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
{from, self()},
- {mfa, {?MODULE,
+ {mfa, {?MODULE,
renegotiate, [Data]}},
{options, ServerOpts}]),
Port = ssl_test_lib:inet_port(Server),
-
+
Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
{host, Hostname},
- {from, self()},
+ {from, self()},
{mfa, {?MODULE, erlang_ssl_receive, [Data]}},
{options, [{reuse_sessions, false} | ClientOpts]}]),
@@ -2674,805 +1898,133 @@ server_renegotiate(Config) when is_list(Config) ->
ssl_test_lib:close(Client).
%%--------------------------------------------------------------------
-client_renegotiate_reused_session(doc) ->
- ["Test ssl:renegotiate/1 on client when the ssl session will be reused."];
-
-client_renegotiate_reused_session(suite) ->
- [];
-
+client_renegotiate_reused_session() ->
+ [{doc,"Test ssl:renegotiate/1 on client when the ssl session will be reused."}].
client_renegotiate_reused_session(Config) when is_list(Config) ->
- ServerOpts = ?config(server_opts, Config),
- ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ ClientOpts = ?config(client_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
+
Data = "From erlang to erlang",
- Server =
- ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ 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),
-
+
Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
{host, Hostname},
- {from, self()},
- {mfa, {?MODULE,
+ {from, self()},
+ {mfa, {?MODULE,
renegotiate_reuse_session, [Data]}},
{options, [{reuse_sessions, true} | ClientOpts]}]),
- ssl_test_lib:check_result(Client, ok, Server, ok),
+ ssl_test_lib:check_result(Client, ok, Server, ok),
ssl_test_lib:close(Server),
ssl_test_lib:close(Client).
%%--------------------------------------------------------------------
-server_renegotiate_reused_session(doc) ->
- ["Test ssl:renegotiate/1 on server when the ssl session will be reused."];
-
-server_renegotiate_reused_session(suite) ->
- [];
-
+server_renegotiate_reused_session() ->
+ [{doc,"Test ssl:renegotiate/1 on server when the ssl session will be reused."}].
server_renegotiate_reused_session(Config) when is_list(Config) ->
- ServerOpts = ?config(server_opts, Config),
- ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ ClientOpts = ?config(client_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
Data = "From erlang to erlang",
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE,
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE,
renegotiate_reuse_session, [Data]}},
{options, ServerOpts}]),
Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
{host, Hostname},
- {from, self()},
+ {from, self()},
{mfa, {?MODULE, erlang_ssl_receive, [Data]}},
{options, [{reuse_sessions, true} | ClientOpts]}]),
-
+
ssl_test_lib:check_result(Server, ok, Client, ok),
ssl_test_lib:close(Server),
ssl_test_lib:close(Client).
%%--------------------------------------------------------------------
-client_no_wrap_sequence_number(doc) ->
- ["Test that erlang client will renegotiate session when",
+client_no_wrap_sequence_number() ->
+ [{doc,"Test that erlang client will renegotiate session when",
"max sequence number celing is about to be reached. Although"
- "in the testcase we use the test option renegotiate_at"
- " to lower treashold substantially."];
-
-client_no_wrap_sequence_number(suite) ->
- [];
+ "in the testcase we use the test option renegotiate_at"
+ " to lower treashold substantially."}].
client_no_wrap_sequence_number(Config) when is_list(Config) ->
- ServerOpts = ?config(server_opts, Config),
- ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ ClientOpts = ?config(client_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
+
ErlData = "From erlang to erlang",
N = 10,
- Server =
- ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ Server =
+ ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
{from, self()},
{mfa, {ssl_test_lib, no_result, []}},
{options, ServerOpts}]),
Port = ssl_test_lib:inet_port(Server),
-
+
Version = ssl_record:highest_protocol_version(ssl_record:supported_protocol_versions()),
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
{host, Hostname},
- {from, self()},
- {mfa, {ssl_test_lib,
+ {from, self()},
+ {mfa, {ssl_test_lib,
trigger_renegotiate, [[ErlData, treashold(N, Version)]]}},
{options, [{reuse_sessions, false},
{renegotiate_at, N} | ClientOpts]}]),
-
- ssl_test_lib:check_result(Client, ok),
+
+ ssl_test_lib:check_result(Client, ok),
ssl_test_lib:close(Server),
ssl_test_lib:close(Client).
- %% First two clauses handles 1/n-1 splitting countermeasure Rizzo/Duong-Beast
-treashold(N, {3,0}) ->
- (N div 2) + 1;
-treashold(N, {3,1}) ->
- (N div 2) + 1;
-treashold(N, _) ->
- N + 1.
-
%%--------------------------------------------------------------------
-server_no_wrap_sequence_number(doc) ->
- ["Test that erlang server will renegotiate session when",
+server_no_wrap_sequence_number() ->
+ [{doc, "Test that erlang server will renegotiate session when",
"max sequence number celing is about to be reached. Although"
- "in the testcase we use the test option renegotiate_at"
- " to lower treashold substantially."];
-
-server_no_wrap_sequence_number(suite) ->
- [];
+ "in the testcase we use the test option renegotiate_at"
+ " to lower treashold substantially."}].
server_no_wrap_sequence_number(Config) when is_list(Config) ->
- ServerOpts = ?config(server_opts, Config),
- ClientOpts = ?config(client_opts, Config),
-
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Data = "From erlang to erlang",
- N = 10,
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib,
- trigger_renegotiate, [[Data, N+2]]}},
- {options, [{renegotiate_at, N} | 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, [{reuse_sessions, false} | ClientOpts]}]),
-
- ssl_test_lib:check_result(Server, ok),
- ssl_test_lib:close(Server),
- 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 = ssl_test_lib:public_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_verify_none(doc) ->
- ["Test cert that has a critical extended_key_usage extension in verify_none mode"];
-
-extended_key_usage_verify_none(suite) ->
- [];
-
-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 = ssl_test_lib:public_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),
-
- KeyFile = filename:join(PrivDir, "otpCA/private/key.pem"),
- [KeyEntry] = ssl_test_lib:pem_to_der(KeyFile),
- Key = ssl_test_lib:public_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},
-
- test_server:format("Extensions ~p~n, NewExtensions: ~p~n", [Extensions, NewExtensions]),
-
- 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),
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, send_recv_result_active, []}},
- {options, 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} | 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]).
-
-%%--------------------------------------------------------------------
-
-invalid_signature_server(doc) ->
- ["Test server with invalid signature"];
-
-invalid_signature_server(suite) ->
- [];
-
-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 = ssl_test_lib:public_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),
-
- 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]}]),
-
- tcp_delivery_workaround(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 = ssl_test_lib:public_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_error([{node, ServerNode}, {port, 0},
- {from, self()},
- {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}]),
-
- tcp_delivery_workaround(Server, {error, "bad certificate"},
- Client, {error,"bad certificate"}).
-
-tcp_delivery_workaround(Server, ServerMsg, Client, ClientMsg) ->
- receive
- {Server, ServerMsg} ->
- client_msg(Client, ClientMsg);
- {Client, ClientMsg} ->
- server_msg(Server, ServerMsg);
- {Client, {error,closed}} ->
- server_msg(Server, ServerMsg);
- {Server, {error,closed}} ->
- client_msg(Client, ClientMsg);
- {Client, {error, esslconnect}} ->
- server_msg(Server, ServerMsg);
- {Server, {error, esslaccept}} ->
- client_msg(Client, ClientMsg)
- end.
-
-client_msg(Client, ClientMsg) ->
- receive
- {Client, ClientMsg} ->
- ok;
- {Client, {error,closed}} ->
- test_server:format("client got close"),
- ok;
- {Client, {error, esslconnect}} ->
- test_server:format("client got econnaborted"),
- ok;
- Unexpected ->
- test_server:fail(Unexpected)
- end.
-
-server_msg(Server, ServerMsg) ->
- receive
- {Server, ServerMsg} ->
- ok;
- {Server, {error,closed}} ->
- test_server:format("server got close"),
- ok;
- {Server, {error, esslaccept}} ->
- test_server:format("server got econnaborted"),
- ok;
- 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 = ssl_test_lib:public_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)],
-
- {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).
-
-%%--------------------------------------------------------------------
-verify_fun_always_run_client(doc) ->
- ["Verify that user verify_fun is always run (for valid and valid_peer not only unknown_extension)"];
-verify_fun_always_run_client(suite) ->
- [];
-verify_fun_always_run_client(Config) when is_list(Config) ->
- ClientOpts = ?config(client_verification_opts, Config),
- ServerOpts = ?config(server_verification_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),
-
- %% If user verify fun is called correctly we fail the connection.
- %% otherwise we can not tell this case apart form where we miss
- %% to call users verify fun
- FunAndState = {fun(_,{extension, _}, UserState) ->
- {unknown, UserState};
- (_, valid, [ChainLen]) ->
- {valid, [ChainLen + 1]};
- (_, valid_peer, [2]) ->
- {fail, "verify_fun_was_always_run"};
- (_, valid_peer, UserState) ->
- {valid, UserState}
- end, [0]},
-
- 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]}]),
- %% Server error may be esslaccept or closed depending on timing
- %% this is not a bug it is a circumstance of how tcp works!
- receive
- {Server, ServerError} ->
- test_server:format("Server Error ~p~n", [ServerError])
- end,
-
- ssl_test_lib:check_result(Client, {error, esslconnect}).
-
-%%--------------------------------------------------------------------
-verify_fun_always_run_server(doc) ->
- ["Verify that user verify_fun is always run (for valid and valid_peer not only unknown_extension)"];
-verify_fun_always_run_server(suite) ->
- [];
-verify_fun_always_run_server(Config) when is_list(Config) ->
- ClientOpts = ?config(client_verification_opts, Config),
- ServerOpts = ?config(server_verification_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- %% If user verify fun is called correctly we fail the connection.
- %% otherwise we can not tell this case apart form where we miss
- %% to call users verify fun
- FunAndState = {fun(_,{extension, _}, UserState) ->
- {unknown, UserState};
- (_, valid, [ChainLen]) ->
- {valid, [ChainLen + 1]};
- (_, valid_peer, [2]) ->
- {fail, "verify_fun_was_always_run"};
- (_, valid_peer, UserState) ->
- {valid, UserState}
- end, [0]},
-
- Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib,
- no_result, []}},
- {options,
- [{verify, verify_peer},
- {verify_fun, FunAndState} |
- ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
-
- 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}
- | ClientOpts]}]),
-
- %% Client error may be esslconnect or closed depending on timing
- %% this is not a bug it is a circumstance of how tcp works!
- receive
- {Client, ClientError} ->
- test_server:format("Client Error ~p~n", [ClientError])
- end,
-
- ssl_test_lib:check_result(Server, {error, esslaccept}).
-
-%%--------------------------------------------------------------------
-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]}]),
+ ClientOpts = ?config(client_opts, Config),
- 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).
+ Data = "From erlang to erlang",
+ N = 10,
-%%--------------------------------------------------------------------
-unknown_server_ca_accept_backwardscompatibility(doc) ->
- ["Test that old style verify_funs will work"];
-unknown_server_ca_accept_backwardscompatibility(suite) ->
- [];
-unknown_server_ca_accept_backwardscompatibility(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,
+ {mfa, {ssl_test_lib,
+ trigger_renegotiate, [[Data, N+2]]}},
+ {options, [{renegotiate_at, N} | 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_peer},
- {verify_fun, VerifyFun}| ClientOpts]}]),
+ {mfa, {ssl_test_lib, no_result, []}},
+ {options, [{reuse_sessions, false} | ClientOpts]}]),
- ssl_test_lib:check_result(Server, ok, Client, ok),
+ ssl_test_lib:check_result(Server, 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() ->
+ [{doc,"Test to input certs and key as der"}].
der_input(Config) when is_list(Config) ->
DataDir = ?config(data_dir, Config),
@@ -3493,19 +2045,19 @@ der_input(Config) when is_list(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, []}},
+ {mfa, {ssl_test_lib, 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, []}},
+ {mfa, {ssl_test_lib, 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),
@@ -3522,12 +2074,9 @@ der_input_opts(Opts) ->
{Cert, {Asn1Type, Key}, CaCerts, DHParams}.
%%--------------------------------------------------------------------
-%% different_ca_peer_sign(doc) ->
+%% different_ca_peer_sign() ->
%% ["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),
@@ -3535,7 +2084,7 @@ der_input_opts(Opts) ->
%% {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, []}},
+%% {mfa, {ssl_test_lib, send_recv_result_active_once, []}},
%% {options, [{active, once},
%% {verify, verify_peer} | ServerOpts]}]),
%% Port = ssl_test_lib:inet_port(Server),
@@ -3543,7 +2092,7 @@ der_input_opts(Opts) ->
%% Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
%% {host, Hostname},
%% {from, self()},
-%% {mfa, {?MODULE,
+%% {mfa, {ssl_test_lib,
%% send_recv_result_active_once,
%% []}},
%% {options, [{active, once},
@@ -3556,12 +2105,8 @@ der_input_opts(Opts) ->
%%--------------------------------------------------------------------
-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() ->
+ [{doc,"Check that a session is not reused if the server is restarted with a new cert."}].
no_reuses_session_server_restart_new_cert(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
@@ -3587,10 +2132,15 @@ no_reuses_session_server_restart_new_cert(Config) when is_list(Config) ->
end,
%% Make sure session is registered
- test_server:sleep(?SLEEP),
+ ct:sleep(?SLEEP),
+ Monitor = erlang:monitor(process, Server),
ssl_test_lib:close(Server),
ssl_test_lib:close(Client0),
-
+ receive
+ {'DOWN', Monitor, _, _, _} ->
+ ok
+ end,
+
Server1 =
ssl_test_lib:start_server([{node, ServerNode}, {port, Port},
{from, self()},
@@ -3604,7 +2154,7 @@ no_reuses_session_server_restart_new_cert(Config) when is_list(Config) ->
{from, self()}, {options, ClientOpts}]),
receive
{Client1, SessionInfo} ->
- test_server:fail(session_reused_when_server_has_new_cert);
+ ct:fail(session_reused_when_server_has_new_cert);
{Client1, _Other} ->
ok
end,
@@ -3612,12 +2162,9 @@ no_reuses_session_server_restart_new_cert(Config) when is_list(Config) ->
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() ->
+ [{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(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
@@ -3647,7 +2194,7 @@ no_reuses_session_server_restart_new_cert_file(Config) when is_list(Config) ->
%% Make sure session is registered and we get
%% new file time stamp when calling new_config!
- test_server:sleep(?SLEEP* 2),
+ ct:sleep(?SLEEP* 2),
ssl_test_lib:close(Server),
ssl_test_lib:close(Client0),
@@ -3667,7 +2214,7 @@ no_reuses_session_server_restart_new_cert_file(Config) when is_list(Config) ->
{from, self()}, {options, ClientOpts}]),
receive
{Client1, SessionInfo} ->
- test_server:fail(session_reused_when_server_has_new_cert);
+ ct:fail(session_reused_when_server_has_new_cert);
{Client1, _Other} ->
ok
end,
@@ -3675,11 +2222,8 @@ no_reuses_session_server_restart_new_cert_file(Config) when is_list(Config) ->
ssl_test_lib:close(Client1).
%%--------------------------------------------------------------------
-reuseaddr(doc) ->
- [""];
-
-reuseaddr(suite) ->
- [];
+reuseaddr() ->
+ [{doc,"Test reuseaddr option"}].
reuseaddr(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
@@ -3697,20 +2241,19 @@ reuseaddr(Config) when is_list(Config) ->
{from, self()},
{mfa, {ssl_test_lib, no_result, []}},
{options, [{active, false} | ClientOpts]}]),
- test_server:sleep(?SLEEP),
ssl_test_lib:close(Server),
ssl_test_lib:close(Client),
-
+
Server1 =
ssl_test_lib:start_server([{node, ServerNode}, {port, Port},
{from, self()},
- {mfa, {?MODULE, send_recv_result, []}},
+ {mfa, {ssl_test_lib, 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, []}},
+ {mfa, {ssl_test_lib, send_recv_result, []}},
{options, [{active, false} | ClientOpts]}]),
ssl_test_lib:check_result(Server1, ok, Client1, ok),
@@ -3718,14 +2261,51 @@ reuseaddr(Config) when is_list(Config) ->
ssl_test_lib:close(Client1).
%%--------------------------------------------------------------------
+tcp_reuseaddr() ->
+ [{doc, "Reference test case."}].
+tcp_reuseaddr(Config) when is_list(Config) ->
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server =
+ ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {transport, gen_tcp},
+ {mfa, {ssl_test_lib, no_result, []}},
+ {options, [{active, false}, {reuseaddr, true}]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client =
+ ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {transport, gen_tcp},
+ {from, self()},
+ {mfa, {ssl_test_lib, no_result, []}},
+ {options, [{active, false}]}]),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client),
+
+ Server1 =
+ ssl_test_lib:start_server([{node, ServerNode}, {port, Port},
+ {from, self()},
+ {transport, gen_tcp},
+ {mfa, {?MODULE, tcp_send_recv_result, []}},
+ {options, [{active, false}, {reuseaddr, true}]}]),
+ Client1 =
+ ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {transport, gen_tcp},
+ {mfa, {?MODULE, tcp_send_recv_result, []}},
+ {options, [{active, false}]}]),
+
+ 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() ->
+ [{doc,"Check that an SSL connection that is started with option "
+ "{hibernate_after, 1000} indeed hibernates after 1000ms of "
+ "inactivity"}].
hibernate(Config) ->
ClientOpts = ?config(client_opts, Config),
@@ -3735,14 +2315,14 @@ hibernate(Config) ->
Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
{from, self()},
- {mfa, {?MODULE, send_recv_result_active, []}},
+ {mfa, {ssl_test_lib, 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, []}},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}},
{options, [{hibernate_after, 1000}|ClientOpts]}]),
{current_function, _} =
process_info(Pid, current_function),
@@ -3756,11 +2336,87 @@ hibernate(Config) ->
ssl_test_lib:close(Client).
%%--------------------------------------------------------------------
+listen_socket() ->
+ [{doc,"Check error handling and inet compliance when calling API functions with listen sockets."}].
+
+listen_socket(Config) ->
+ ServerOpts = ?config(server_opts, Config),
+ {ok, ListenSocket} = ssl:listen(0, ServerOpts),
+
+ %% This can be a valid thing to do as
+ %% options are inherited by the accept socket
+ ok = ssl:controlling_process(ListenSocket, self()),
+
+ {ok, _} = ssl:sockname(ListenSocket),
+
+ {error, enotconn} = ssl:send(ListenSocket, <<"data">>),
+ {error, enotconn} = ssl:recv(ListenSocket, 0),
+ {error, enotconn} = ssl:connection_info(ListenSocket),
+ {error, enotconn} = ssl:peername(ListenSocket),
+ {error, enotconn} = ssl:peercert(ListenSocket),
+ {error, enotconn} = ssl:session_info(ListenSocket),
+ {error, enotconn} = ssl:renegotiate(ListenSocket),
+ {error, enotconn} = ssl:prf(ListenSocket, 'master_secret', <<"Label">>, client_random, 256),
+ {error, enotconn} = ssl:shutdown(ListenSocket, read_write),
+
+ ok = ssl:close(ListenSocket).
+%%--------------------------------------------------------------------
+ssl_accept_timeout() ->
+ [{doc,"Test ssl:ssl_accept timeout"}].
+
+ssl_accept_timeout(Config) ->
+ process_flag(trap_exit, true),
+ ServerOpts = ?config(server_opts, Config),
+ {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {timeout, 5000},
+ {mfa, {ssl_test_lib,
+ no_result_msg, []}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ {ok, CSocket} = gen_tcp:connect(Hostname, Port, [binary, {active, true}]),
+
+ receive
+ {tcp_closed, CSocket} ->
+ ssl_test_lib:check_result(Server, {error, timeout}),
+ receive
+ {'EXIT', Server, _} ->
+ [] = supervisor:which_children(ssl_connection_sup)
+ end
+ end.
+
+%%--------------------------------------------------------------------
+ssl_recv_timeout() ->
+ [{doc,"Test ssl:ssl_accept timeout"}].
+
+ssl_recv_timeout(Config) ->
+ ServerOpts = ?config(server_opts, Config),
+ ClientOpts = ?config(client_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_timeout_server, []}},
+ {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_timeout_client, []}},
+ {options, [{active, false} | ClientOpts]}]),
-connect_twice(doc) ->
- [""];
-connect_twice(suite) ->
- [];
+ ssl_test_lib:check_result(Client, ok, Server, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+connect_twice() ->
+ [{doc,""}].
connect_twice(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
ServerOpts = ?config(server_opts, Config),
@@ -3770,7 +2426,7 @@ connect_twice(Config) when is_list(Config) ->
Server =
ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
{from, self()},
- {mfa, {?MODULE, send_recv_result, []}},
+ {mfa, {ssl_test_lib, send_recv_result, []}},
{options, [{keepalive, true},{active, false}
| ServerOpts]}]),
Port = ssl_test_lib:inet_port(Server),
@@ -3778,7 +2434,7 @@ connect_twice(Config) when is_list(Config) ->
ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
{host, Hostname},
{from, self()},
- {mfa, {?MODULE, send_recv_result, []}},
+ {mfa, {ssl_test_lib, send_recv_result, []}},
{options, [{keepalive, true},{active, false}
| ClientOpts]}]),
Server ! listen,
@@ -3788,11 +2444,11 @@ connect_twice(Config) when is_list(Config) ->
{node, ClientNode}, {port, Port},
{host, Hostname},
{from, self()},
- {mfa, {?MODULE, send_recv_result, []}},
+ {mfa, {ssl_test_lib, send_recv_result, []}},
{options, [{keepalive, true},{active, false}
| ClientOpts]}]),
- test_server:format("Testcase ~p, Client ~p Server ~p ~n",
+ ct:print("Testcase ~p, Client ~p Server ~p ~n",
[self(), Client, Server]),
ssl_test_lib:check_result(Server, ok, Client, ok),
@@ -3803,13 +2459,9 @@ connect_twice(Config) when is_list(Config) ->
ssl_test_lib:close(Client1).
%%--------------------------------------------------------------------
-renegotiate_dos_mitigate_active(doc) ->
- ["Mitigate DOS computational attack by not allowing client to renegotiate many times in a row",
- "immediately after each other"];
-
-renegotiate_dos_mitigate_active(suite) ->
- [];
-
+renegotiate_dos_mitigate_active() ->
+ [{doc, "Mitigate DOS computational attack by not allowing client to renegotiate many times in a row",
+ "immediately after each other"}].
renegotiate_dos_mitigate_active(Config) when is_list(Config) ->
ServerOpts = ?config(server_opts, Config),
ClientOpts = ?config(client_opts, Config),
@@ -3819,7 +2471,7 @@ renegotiate_dos_mitigate_active(Config) when is_list(Config) ->
Server =
ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
{from, self()},
- {mfa, {?MODULE, send_recv_result_active, []}},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}},
{options, [ServerOpts]}]),
Port = ssl_test_lib:inet_port(Server),
@@ -3835,13 +2487,9 @@ renegotiate_dos_mitigate_active(Config) when is_list(Config) ->
ssl_test_lib:close(Client).
%%--------------------------------------------------------------------
-renegotiate_dos_mitigate_passive(doc) ->
- ["Mitigate DOS computational attack by not allowing client to renegotiate many times in a row",
- "immediately after each other"];
-
-renegotiate_dos_mitigate_passive(suite) ->
- [];
-
+renegotiate_dos_mitigate_passive() ->
+ [{doc, "Mitigate DOS computational attack by not allowing client to renegotiate many times in a row",
+ "immediately after each other"}].
renegotiate_dos_mitigate_passive(Config) when is_list(Config) ->
ServerOpts = ?config(server_opts, Config),
ClientOpts = ?config(client_opts, Config),
@@ -3851,7 +2499,7 @@ renegotiate_dos_mitigate_passive(Config) when is_list(Config) ->
Server =
ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
{from, self()},
- {mfa, {?MODULE, send_recv_result, []}},
+ {mfa, {ssl_test_lib, send_recv_result, []}},
{options, [{active, false} | ServerOpts]}]),
Port = ssl_test_lib:inet_port(Server),
@@ -3867,8 +2515,8 @@ renegotiate_dos_mitigate_passive(Config) when is_list(Config) ->
ssl_test_lib:close(Client).
%%--------------------------------------------------------------------
-tcp_error_propagation_in_active_mode(doc) ->
- ["Test that process recives {ssl_error, Socket, closed} when tcp error ocurres"];
+tcp_error_propagation_in_active_mode() ->
+ [{doc,"Test that process recives {ssl_error, Socket, closed} when tcp error ocurres"}].
tcp_error_propagation_in_active_mode(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
ServerOpts = ?config(server_opts, Config),
@@ -3897,11 +2545,9 @@ tcp_error_propagation_in_active_mode(Config) when is_list(Config) ->
ssl_test_lib:check_result(Client, {ssl_closed, SslSocket}).
-
%%--------------------------------------------------------------------
-
-recv_error_handling(doc) ->
- ["Special case of call error handling"];
+recv_error_handling() ->
+ [{doc,"Special case of call error handling"}].
recv_error_handling(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
ServerOpts = ?config(server_opts, Config),
@@ -3912,7 +2558,7 @@ recv_error_handling(Config) when is_list(Config) ->
{mfa, {?MODULE, recv_close, []}},
{options, [{active, false} | ServerOpts]}]),
Port = ssl_test_lib:inet_port(Server),
- {Client, #sslsocket{pid=Pid} = SslSocket} = ssl_test_lib:start_client([return_socket,
+ {_Client, #sslsocket{} = SslSocket} = ssl_test_lib:start_client([return_socket,
{node, ClientNode}, {port, Port},
{host, Hostname},
{from, self()},
@@ -3921,82 +2567,165 @@ recv_error_handling(Config) when is_list(Config) ->
ssl:close(SslSocket),
ssl_test_lib:check_result(Server, ok).
-
%%--------------------------------------------------------------------
-rizzo(doc) -> ["Test that there is a 1/n-1-split for non RC4 in 'TLS < 1.1' as it is
- vunrable to Rizzo/Dungon attack"];
+rizzo() ->
+ [{doc, "Test that there is a 1/n-1-split for non RC4 in 'TLS < 1.1' as it is
+ vunrable to Rizzo/Dungon attack"}].
rizzo(Config) when is_list(Config) ->
Ciphers = [X || X ={_,Y,_} <- ssl:cipher_suites(), Y =/= rc4_128],
- run_send_recv_rizzo(Ciphers, Config, sslv3,
- {?MODULE, send_recv_result_active_rizzo, []}),
- run_send_recv_rizzo(Ciphers, Config, tlsv1,
+ Prop = ?config(tc_group_properties, Config),
+ Version = proplists:get_value(name, Prop),
+ run_send_recv_rizzo(Ciphers, Config, Version,
{?MODULE, send_recv_result_active_rizzo, []}).
%%--------------------------------------------------------------------
-no_rizzo_rc4(doc) ->
- ["Test that there is no 1/n-1-split for RC4 as it is not vunrable to Rizzo/Dungon attack"];
+no_rizzo_rc4() ->
+ [{doc,"Test that there is no 1/n-1-split for RC4 as it is not vunrable to Rizzo/Dungon attack"}].
no_rizzo_rc4(Config) when is_list(Config) ->
Ciphers = [X || X ={_,Y,_} <- ssl:cipher_suites(),Y == rc4_128],
- run_send_recv_rizzo(Ciphers, Config, sslv3,
- {?MODULE, send_recv_result_active_no_rizzo, []}),
- run_send_recv_rizzo(Ciphers, Config, tlsv1,
+ Prop = ?config(tc_group_properties, Config),
+ Version = proplists:get_value(name, Prop),
+ run_send_recv_rizzo(Ciphers, Config, Version,
{?MODULE, send_recv_result_active_no_rizzo, []}).
%%--------------------------------------------------------------------
-run_send_recv_rizzo(Ciphers, Config, Version, Mfa) ->
- Result = lists:map(fun(Cipher) ->
- rizzo_test(Cipher, Config, Version, Mfa) end,
- Ciphers),
- case lists:flatten(Result) of
- [] ->
- ok;
- Error ->
- test_server:format("Cipher suite errors: ~p~n", [Error]),
- test_server:fail(cipher_suite_failed_see_test_case_log)
- end.
+new_server_wants_peer_cert() ->
+ [{doc, "Test that server configured to do client certification does"
+ " not reuse session without a client certificate."}].
+new_server_wants_peer_cert(Config) when is_list(Config) ->
+ ServerOpts = ?config(server_opts, Config),
+ VServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true}
+ | ?config(server_verification_opts, Config)],
+ ClientOpts = ?config(client_verification_opts, Config),
-rizzo_test(Cipher, Config, Version, Mfa) ->
- {ClientOpts, ServerOpts} = client_server_opts(Cipher, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, Mfa},
- {options, [{active, true}, {ciphers, [Cipher]},
- {versions, [Version]}
- | ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, Mfa},
- {options, [{active, true} | ClientOpts]}]),
-
- Result = ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ Server =
+ ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, peercert_result, []}},
+ {options, [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, ClientOpts}]),
+
+ Monitor = erlang:monitor(process, Server),
ssl_test_lib:close(Server),
ssl_test_lib:close(Client),
- case Result of
- ok ->
- [];
- Error ->
- [{Cipher, Error}]
- end.
+ receive
+ {'DOWN', Monitor, _, _, _} ->
+ ok
+ end,
+
+ Server1 = ssl_test_lib:start_server([{node, ServerNode}, {port, Port},
+ {from, self()},
+ {mfa, {?MODULE, peercert_result, []}},
+ {options, VServerOpts}]),
+ Client1 =
+ ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, no_result, []}},
+ {options, [ClientOpts]}]),
-client_server_opts({KeyAlgo,_,_}, Config) when KeyAlgo == rsa orelse KeyAlgo == dhe_rsa ->
- {?config(client_opts, Config),
- ?config(server_opts, Config)};
-client_server_opts({KeyAlgo,_,_}, Config) when KeyAlgo == dss orelse KeyAlgo == dhe_dss ->
- {?config(client_dsa_opts, Config),
- ?config(server_dsa_opts, Config)}.
+ CertFile = proplists:get_value(certfile, ClientOpts),
+ [{'Certificate', BinCert, _}]= ssl_test_lib:pem_to_der(CertFile),
+
+ ServerMsg = {error, no_peercert},
+ Sever1Msg = {ok, BinCert},
+
+ ssl_test_lib:check_result(Server, ServerMsg, Server1, Sever1Msg),
+
+ ssl_test_lib:close(Server1),
+ ssl_test_lib:close(Client),
+ ssl_test_lib:close(Client1).
%%--------------------------------------------------------------------
-%%% Internal functions
+session_cache_process_list() ->
+ [{doc,"Test reuse of sessions (short handshake)"}].
+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(Config) when is_list(Config) ->
+ session_cache_process(mnesia,Config).
+
+%%--------------------------------------------------------------------
+%% Internal functions ------------------------------------------------
%%--------------------------------------------------------------------
send_recv_result(Socket) ->
ssl:send(Socket, "Hello world"),
{ok,"Hello world"} = ssl:recv(Socket, 11),
ok.
+tcp_send_recv_result(Socket) ->
+ gen_tcp:send(Socket, "Hello world"),
+ {ok,"Hello world"} = gen_tcp:recv(Socket, 11),
+ ok.
+
+basic_verify_test_no_close(Config) ->
+ ClientOpts = ?config(client_verification_opts, 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, {ssl_test_lib, 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, {ssl_test_lib, send_recv_result_active, []}},
+ {options, ClientOpts}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+ {Server, Client}.
+
+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, {ssl_test_lib, 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, {ssl_test_lib, 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).
+
+send_recv_result_timeout_client(Socket) ->
+ {error, timeout} = ssl:recv(Socket, 11, 500),
+ ssl:send(Socket, "Hello world"),
+ receive
+ Msg ->
+ io:format("Msg ~p~n",[Msg])
+ after 500 ->
+ ok
+ end,
+ {ok, "Hello world"} = ssl:recv(Socket, 11, 500),
+ ok.
+send_recv_result_timeout_server(Socket) ->
+ ssl:send(Socket, "Hello"),
+ {ok, "Hello world"} = ssl:recv(Socket, 11),
+ ssl:send(Socket, " world"),
+ ok.
recv_close(Socket) ->
{error, closed} = ssl:recv(Socket, 11),
@@ -4007,17 +2736,6 @@ recv_close(Socket) ->
ok
end.
-send_recv_result_active(Socket) ->
- ssl:send(Socket, "Hello world"),
- receive
- {ssl, Socket, "H"} ->
- receive
- {ssl, Socket, "ello world"} ->
- ok
- end;
- {ssl, Socket, "Hello world"} ->
- ok
- end.
send_recv_result_active_rizzo(Socket) ->
ssl:send(Socket, "Hello world"),
@@ -4036,26 +2754,13 @@ send_recv_result_active_no_rizzo(Socket) ->
ok
end.
-send_recv_result_active_once(Socket) ->
- ssl:send(Socket, "Hello world"),
- receive
- {ssl, Socket, "H"} ->
- ssl:setopts(Socket, [{active, once}]),
- receive
- {ssl, Socket, "ello world"} ->
- ok
- end;
- {ssl, Socket, "Hello world"} ->
- ok
- end.
-
result_ok(_Socket) ->
ok.
renegotiate(Socket, Data) ->
- test_server:format("Renegotiating ~n", []),
+ ct:print("Renegotiating ~n", []),
Result = ssl:renegotiate(Socket),
- test_server:format("Result ~p~n", [Result]),
+ ct:print("Result ~p~n", [Result]),
ssl:send(Socket, Data),
case Result of
ok ->
@@ -4066,7 +2771,7 @@ renegotiate(Socket, Data) ->
renegotiate_reuse_session(Socket, Data) ->
%% Make sure session is registered
- test_server:sleep(?SLEEP),
+ ct:sleep(?SLEEP),
renegotiate(Socket, Data).
renegotiate_immediately(Socket) ->
@@ -4082,9 +2787,9 @@ renegotiate_immediately(Socket) ->
end,
ok = ssl:renegotiate(Socket),
{error, renegotiation_rejected} = ssl:renegotiate(Socket),
- test_server:sleep(?RENEGOTIATION_DISABLE_TIME +1),
+ ct:sleep(?RENEGOTIATION_DISABLE_TIME +1),
ok = ssl:renegotiate(Socket),
- test_server:format("Renegotiated again"),
+ ct:print("Renegotiated again"),
ssl:send(Socket, "Hello world"),
ok.
@@ -4103,27 +2808,11 @@ new_config(PrivDir, ServerOpts0) ->
ServerOpts = proplists:delete(keyfile, ServerOpts2),
{ok, PEM} = file:read_file(NewCaCertFile),
- test_server:format("CA file content: ~p~n", [public_key:pem_decode(PEM)]),
+ ct:print("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)"];
-
-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_process(_Type,Config) when is_list(Config) ->
reuse_session(Config).
@@ -4265,9 +2954,9 @@ erlang_ssl_receive(Socket, Data) ->
io:format("Received ~p~n",[Byte]),
erlang_ssl_receive(Socket, tl(Data));
Other ->
- test_server:fail({unexpected_message, Other})
+ ct:fail({unexpected_message, Other})
after ?SLEEP * 3 ->
- test_server:fail({did_not_get, Data})
+ ct:fail({did_not_get, Data})
end.
receive_msg(_) ->
@@ -4275,3 +2964,222 @@ receive_msg(_) ->
Msg ->
Msg
end.
+
+controlling_process_result(Socket, Pid, Msg) ->
+ ok = ssl:controlling_process(Socket, Pid),
+ %% Make sure other side has evaluated controlling_process
+ %% before message is sent
+ ct:sleep(?SLEEP),
+ ssl:send(Socket, Msg),
+ no_result_msg.
+
+receive_s_rizzo_duong_beast() ->
+ receive
+ {ssl, _, "erver hello"} ->
+ receive
+ {ssl, _, "C"} ->
+ receive
+ {ssl, _, "lient hello"} ->
+ ok
+ end
+ end
+ end.
+receive_c_rizzo_duong_beast() ->
+ receive
+ {ssl, _, "lient hello"} ->
+ receive
+ {ssl, _, "S"} ->
+ receive
+ {ssl, _, "erver hello"} ->
+ ok
+ end
+ end
+ end.
+
+controller_dies_result(_Socket, _Pid, _Msg) ->
+ receive Result -> Result end.
+
+get_close(Pid, Where) ->
+ receive
+ {'EXIT', Pid, _Reason} ->
+ receive
+ {_, {ssl_closed, Socket}} ->
+ ct:print("Socket closed ~p~n",[Socket]);
+ Unexpected ->
+ ct:print("Unexpected ~p~n",[Unexpected]),
+ ct:fail({line, ?LINE-1})
+ after 5000 ->
+ ct:fail({timeout, {line, ?LINE, Where}})
+ end;
+ Unexpected ->
+ ct:print("Unexpected ~p~n",[Unexpected]),
+ ct:fail({line, ?LINE-1})
+ after 5000 ->
+ ct:fail({timeout, {line, ?LINE, Where}})
+ end.
+
+run_send_recv_rizzo(Ciphers, Config, Version, Mfa) ->
+ Result = lists:map(fun(Cipher) ->
+ rizzo_test(Cipher, Config, Version, Mfa) end,
+ Ciphers),
+ case lists:flatten(Result) of
+ [] ->
+ ok;
+ Error ->
+ ct:print("Cipher suite errors: ~p~n", [Error]),
+ ct:fail(cipher_suite_failed_see_test_case_log)
+ end.
+
+rizzo_test(Cipher, Config, Version, Mfa) ->
+ {ClientOpts, ServerOpts} = client_server_opts(Cipher, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, Mfa},
+ {options, [{active, true}, {ciphers, [Cipher]},
+ {versions, [Version]}
+ | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, Mfa},
+ {options, [{active, true} | ClientOpts]}]),
+
+ Result = ssl_test_lib:check_result(Server, ok, Client, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client),
+ case Result of
+ ok ->
+ [];
+ Error ->
+ [{Cipher, Error}]
+ end.
+
+client_server_opts({KeyAlgo,_,_}, Config) when KeyAlgo == rsa orelse KeyAlgo == dhe_rsa ->
+ {?config(client_opts, Config),
+ ?config(server_opts, Config)};
+client_server_opts({KeyAlgo,_,_}, Config) when KeyAlgo == dss orelse KeyAlgo == dhe_dss ->
+ {?config(client_dsa_opts, Config),
+ ?config(server_dsa_opts, Config)}.
+
+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, ClientOpts, ServerOpts) end,
+ Ciphers),
+ case lists:flatten(Result) of
+ [] ->
+ ok;
+ Error ->
+ ct:print("Cipher suite errors: ~p~n", [Error]),
+ ct:fail(cipher_suite_failed_see_test_case_log)
+ end.
+
+erlang_cipher_suite(Suite) when is_list(Suite)->
+ ssl:suite_definition(ssl_cipher:openssl_suite(Suite));
+erlang_cipher_suite(Suite) ->
+ Suite.
+
+cipher(CipherSuite, Version, Config, ClientOpts, ServerOpts) ->
+ %% process_flag(trap_exit, true),
+ ct:print("Testing CipherSuite ~p~n", [CipherSuite]),
+ {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, {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, {ssl_test_lib, cipher_result, [ConnectionInfo]}},
+ {options,
+ [{ciphers,[CipherSuite]} |
+ ClientOpts]}]),
+
+ Result = ssl_test_lib:wait_for_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client),
+
+ case Result of
+ ok ->
+ [];
+ Error ->
+ [{ErlangCipherSuite, Error}]
+ end.
+
+connection_info_result(Socket) ->
+ ssl:connection_info(Socket).
+
+connect_dist_s(S) ->
+ Msg = term_to_binary({erlang,term}),
+ ok = ssl:send(S, Msg).
+
+connect_dist_c(S) ->
+ Test = binary_to_list(term_to_binary({erlang,term})),
+ {ok, Test} = ssl:recv(S, 0, 10000),
+ ok.
+
+ %% First two clauses handles 1/n-1 splitting countermeasure Rizzo/Duong-Beast
+treashold(N, {3,0}) ->
+ (N div 2) + 1;
+treashold(N, {3,1}) ->
+ (N div 2) + 1;
+treashold(N, _) ->
+ N + 1.
+
+get_invalid_inet_option(Socket) ->
+ {error, {eoptions, {inet_option, foo, _}}} = ssl:getopts(Socket, [foo]),
+ ok.
+
+shutdown_result(Socket, server) ->
+ ssl:send(Socket, "Hej"),
+ ssl:shutdown(Socket, write),
+ {ok, "Hej hopp"} = ssl:recv(Socket, 8),
+ ok;
+
+shutdown_result(Socket, client) ->
+ {ok, "Hej"} = ssl:recv(Socket, 3),
+ ssl:send(Socket, "Hej hopp"),
+ ssl:shutdown(Socket, write),
+ ok.
+
+shutdown_write_result(Socket, server) ->
+ ct:sleep(?SLEEP),
+ ssl:shutdown(Socket, write);
+shutdown_write_result(Socket, client) ->
+ ssl:recv(Socket, 0).
+
+dummy(_Socket) ->
+ %% Should not happen as the ssl connection will not be established
+ %% due to fatal handshake failiure
+ exit(kill).
+
+shutdown_both_result(Socket, server) ->
+ ct:sleep(?SLEEP),
+ ssl:shutdown(Socket, read_write);
+shutdown_both_result(Socket, client) ->
+ ssl:recv(Socket, 0).
+
+peername_result(S) ->
+ ssl:peername(S).
diff --git a/lib/ssl/test/ssl_certificate_verify_SUITE.erl b/lib/ssl/test/ssl_certificate_verify_SUITE.erl
new file mode 100644
index 0000000000..86e1d47be7
--- /dev/null
+++ b/lib/ssl/test/ssl_certificate_verify_SUITE.erl
@@ -0,0 +1,978 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012-2013. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have 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_certificate_verify_SUITE).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("public_key/include/public_key.hrl").
+
+-include("ssl_internal.hrl").
+-include("ssl_alert.hrl").
+-include("ssl_internal.hrl").
+-include("ssl_record.hrl").
+-include("ssl_handshake.hrl").
+
+-define(LONG_TIMEOUT, 600000).
+
+%%--------------------------------------------------------------------
+%% Common Test interface functions -----------------------------------
+%%--------------------------------------------------------------------
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [{group, active},
+ {group, passive},
+ {group, active_once},
+ {group, error_handling}].
+
+
+groups() ->
+ [{active, [], tests()},
+ {active_once, [], tests()},
+ {passive, [], tests()},
+ {error_handling, [],error_handling_tests()}].
+
+tests() ->
+ [server_verify_peer,
+ server_verify_none,
+ server_require_peer_cert_ok,
+ server_require_peer_cert_fail,
+ verify_fun_always_run_client,
+ verify_fun_always_run_server,
+ cert_expired,
+ invalid_signature_client,
+ invalid_signature_server,
+ extended_key_usage_verify_peer,
+ extended_key_usage_verify_none].
+
+error_handling_tests()->
+ [client_with_cert_cipher_suites_handshake,
+ server_verify_no_cacerts,
+ unknown_server_ca_fail,
+ unknown_server_ca_accept_verify_none,
+ unknown_server_ca_accept_verify_peer,
+ unknown_server_ca_accept_backwardscompatibility,
+ no_authority_key_identifier].
+
+init_per_suite(Config0) ->
+ Dog = ct:timetrap(?LONG_TIMEOUT *2),
+ catch crypto:stop(),
+ try crypto:start() of
+ ok ->
+ application:start(public_key),
+ application:start(ssl),
+ %% make rsa certs using oppenssl
+ Result =
+ (catch make_certs:all(?config(data_dir, Config0),
+ ?config(priv_dir, Config0))),
+ ct:print("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_per_suite(_Config) ->
+ ssl:stop(),
+ application:stop(crypto).
+
+init_per_group(active, Config) ->
+ [{active, true}, {receive_function, send_recv_result_active} | Config];
+init_per_group(active_once, Config) ->
+ [{active, once}, {receive_function, send_recv_result_active_once} | Config];
+init_per_group(passive, Config) ->
+ [{active, false}, {receive_function, send_recv_result} | Config];
+init_per_group(_, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% Test Cases --------------------------------------------------------
+%%--------------------------------------------------------------------
+
+server_verify_peer() ->
+ [{doc,"Test server option verify_peer"}].
+server_verify_peer(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_verification_opts, Config),
+ ServerOpts = ?config(server_verification_opts, Config),
+ Active = ?config(active, Config),
+ ReceiveFunction = ?config(receive_function, 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, ReceiveFunction, []}},
+ {options, [{active, Active}, {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, {ssl_test_lib, ReceiveFunction, []}},
+ {options, [{active, Active} | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+server_verify_none() ->
+ [{doc,"Test server option verify_none"}].
+
+server_verify_none(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ Active = ?config(active, Config),
+ ReceiveFunction = ?config(receive_function, 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, ReceiveFunction, []}},
+ {options, [{active, Active}, {verify, verify_none}
+ | 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, ReceiveFunction, []}},
+ {options, [{active, Active} | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+
+server_verify_client_once() ->
+ [{doc,"Test server option verify_client_once"}].
+
+server_verify_client_once(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_verification_opts, Config),
+ Active = ?config(active, Config),
+ ReceiveFunction = ?config(receive_function, 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, ReceiveFunction, []}},
+ {options, [{active, Active}, {verify, verify_peer},
+ {verify_client_once, true}
+ | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client0 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, ReceiveFunction, []}},
+ {options, [{active, Active} | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client0, ok),
+ Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}},
+ ssl_test_lib:close(Client0),
+ Client1 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, result_ok, []}},
+ {options, [{active, Active} | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Client1, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client1).
+
+%%--------------------------------------------------------------------
+
+server_require_peer_cert_ok() ->
+ [{doc,"Test server option fail_if_no_peer_cert when peer sends cert"}].
+
+server_require_peer_cert_ok(Config) when is_list(Config) ->
+ ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true}
+ | ?config(server_verification_opts, Config)],
+ ClientOpts = ?config(client_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, {ssl_test_lib,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, {ssl_test_lib, 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).
+
+%%--------------------------------------------------------------------
+
+server_require_peer_cert_fail() ->
+ [{doc,"Test server option fail_if_no_peer_cert when peer doesn't send cert"}].
+
+server_require_peer_cert_fail(Config) when is_list(Config) ->
+ ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true}
+ | ?config(server_verification_opts, Config)],
+ BadClientOpts = ?config(client_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {options, [{active, false} | ServerOpts]}]),
+
+ Port = ssl_test_lib:inet_port(Server),
+
+ Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {options, [{active, false} | BadClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, {error, {essl, "handshake failure"}},
+ Client, {error, {essl, "handshake failure"}}).
+
+
+%%--------------------------------------------------------------------
+verify_fun_always_run_client() ->
+ [{doc,"Verify that user verify_fun is always run (for valid and valid_peer not only unknown_extension)"}].
+
+verify_fun_always_run_client(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_verification_opts, Config),
+ ServerOpts = ?config(server_verification_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),
+
+ %% If user verify fun is called correctly we fail the connection.
+ %% otherwise we can not tell this case apart form where we miss
+ %% to call users verify fun
+ FunAndState = {fun(_,{extension, _}, UserState) ->
+ {unknown, UserState};
+ (_, valid, [ChainLen]) ->
+ {valid, [ChainLen + 1]};
+ (_, valid_peer, [2]) ->
+ {fail, "verify_fun_was_always_run"};
+ (_, valid_peer, UserState) ->
+ {valid, UserState}
+ end, [0]},
+
+ 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]}]),
+ %% Server error may be {essl,"handshake failure"} or closed depending on timing
+ %% this is not a bug it is a circumstance of how tcp works!
+ receive
+ {Server, ServerError} ->
+ ct:print("Server Error ~p~n", [ServerError])
+ end,
+
+ ssl_test_lib:check_result(Client, {error, {essl, "handshake failure"}}).
+
+%%--------------------------------------------------------------------
+verify_fun_always_run_server() ->
+ [{doc,"Verify that user verify_fun is always run (for valid and valid_peer not only unknown_extension)"}].
+verify_fun_always_run_server(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_verification_opts, Config),
+ ServerOpts = ?config(server_verification_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ %% If user verify fun is called correctly we fail the connection.
+ %% otherwise we can not tell this case apart form where we miss
+ %% to call users verify fun
+ FunAndState = {fun(_,{extension, _}, UserState) ->
+ {unknown, UserState};
+ (_, valid, [ChainLen]) ->
+ {valid, [ChainLen + 1]};
+ (_, valid_peer, [2]) ->
+ {fail, "verify_fun_was_always_run"};
+ (_, valid_peer, UserState) ->
+ {valid, UserState}
+ end, [0]},
+
+ Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib,
+ no_result, []}},
+ {options,
+ [{verify, verify_peer},
+ {verify_fun, FunAndState} |
+ ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ 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}
+ | ClientOpts]}]),
+
+ %% Client error may be {essl, "handshake failure" } or closed depending on timing
+ %% this is not a bug it is a circumstance of how tcp works!
+ receive
+ {Client, ClientError} ->
+ ct:print("Client Error ~p~n", [ClientError])
+ end,
+
+ ssl_test_lib:check_result(Server, {error, {essl, "handshake failure"}}).
+
+%%--------------------------------------------------------------------
+
+client_verify_none_passive() ->
+ [{doc,"Test client option verify_none"}].
+
+client_verify_none_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),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, 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, {ssl_test_lib, send_recv_result, []}},
+ {options, [{active, false},
+ {verify, verify_none}
+ | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+%%--------------------------------------------------------------------
+cert_expired() ->
+ [{doc,"Test server with expired certificate"}].
+
+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 = ssl_test_lib:public_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}},
+
+ ct:print("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)],
+
+ {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, {essl, "certificate expired"}},
+ Client, {error, {essl, "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_verify_none_active() ->
+ [{doc,"Test client option verify_none"}].
+
+client_verify_none_active(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,
+ send_recv_result_active, []}},
+ {options, [{active, true}
+ | 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,
+ send_recv_result_active, []}},
+ {options, [{active, true},
+ {verify, verify_none}
+ | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+client_verify_none_active_once() ->
+ [{doc,"Test client option verify_none"}].
+
+client_verify_none_active_once(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, send_recv_result_active, []}},
+ {options, [{active, once} | 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,
+ send_recv_result_active_once,
+ []}},
+ {options, [{active, once},
+ {verify, verify_none}
+ | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+ ssl_test_lib:close(Server),
+ 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(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_verification_opts, Config),
+ ServerOpts = ?config(server_verification_opts, Config),
+ PrivDir = ?config(priv_dir, Config),
+ Active = ?config(active, Config),
+ ReceiveFunction = ?config(receive_function, Config),
+
+ KeyFile = filename:join(PrivDir, "otpCA/private/key.pem"),
+ [KeyEntry] = ssl_test_lib:pem_to_der(KeyFile),
+ Key = ssl_test_lib:public_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, {ssl_test_lib, ReceiveFunction, []}},
+ {options, [{verify, verify_peer}, {active, Active} | NewServerOpts]}]),
+ 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, ReceiveFunction, []}},
+ {options, [{verify, verify_peer}, {active, Active} |
+ NewClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+extended_key_usage_verify_none() ->
+ [{doc,"Test cert that has a critical extended_key_usage extension in verify_none mode"}].
+
+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),
+ Active = ?config(active, Config),
+ ReceiveFunction = ?config(receive_function, Config),
+
+ KeyFile = filename:join(PrivDir, "otpCA/private/key.pem"),
+ [KeyEntry] = ssl_test_lib:pem_to_der(KeyFile),
+ Key = ssl_test_lib:public_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, {ssl_test_lib, ReceiveFunction, []}},
+ {options, [{verify, verify_none}, {active, Active} | NewServerOpts]}]),
+ 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, ReceiveFunction, []}},
+ {options, [{verify, verify_none}, {active, Active} | 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(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_verification_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ PrivDir = ?config(priv_dir, Config),
+
+ KeyFile = filename:join(PrivDir, "otpCA/private/key.pem"),
+ [KeyEntry] = ssl_test_lib:pem_to_der(KeyFile),
+ Key = ssl_test_lib:public_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},
+
+ ct:print("Extensions ~p~n, NewExtensions: ~p~n", [Extensions, NewExtensions]),
+
+ 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),
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}},
+ {options, NewServerOpts}]),
+ 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, send_recv_result_active, []}},
+ {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]).
+
+%%--------------------------------------------------------------------
+
+invalid_signature_server() ->
+ [{doc,"Test client with invalid signature"}].
+
+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 = ssl_test_lib:public_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),
+
+ 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]}]),
+
+ tcp_delivery_workaround(Server, {error, {essl, "bad certificate"}},
+ Client, {error, {essl, "bad certificate"}}).
+
+%%--------------------------------------------------------------------
+
+invalid_signature_client() ->
+ [{doc,"Test server with invalid signature"}].
+
+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 = ssl_test_lib:public_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_error([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {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}]),
+
+ tcp_delivery_workaround(Server, {error, {essl, "bad certificate"}},
+ Client, {error, {essl, "bad certificate"}}).
+
+
+%%--------------------------------------------------------------------
+
+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(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, {ssl_test_lib,
+ 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, {ssl_test_lib,
+ 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).
+
+%%--------------------------------------------------------------------
+
+server_verify_no_cacerts() ->
+ [{doc,"Test server must have cacerts if it wants to verify client"}].
+server_verify_no_cacerts(Config) when is_list(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()},
+ {options, [{verify, verify_peer}
+ | ServerOpts]}]),
+
+ ssl_test_lib:check_result(Server, {error, {eoptions, {cacertfile, ""}}}).
+
+
+%%--------------------------------------------------------------------
+unknown_server_ca_fail() ->
+ [{doc,"Test that the client fails if the ca is unknown in verify_peer mode"}].
+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, {essl, "unknown ca"}},
+ Client, {error, {essl, "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(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,
+ 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, {ssl_test_lib,
+ 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(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,
+ 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, {ssl_test_lib,
+ 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_backwardscompatibility() ->
+ [{doc,"Test that old style verify_funs will work"}].
+unknown_server_ca_accept_backwardscompatibility(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,
+ 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, {ssl_test_lib,
+ 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).
+
+%%--------------------------------------------------------------------
+%% Internal functions ------------------------------------------------
+%%--------------------------------------------------------------------
+
+tcp_delivery_workaround(Server, ServerMsg, Client, ClientMsg) ->
+ receive
+ {Server, ServerMsg} ->
+ client_msg(Client, ClientMsg);
+ {Client, ClientMsg} ->
+ server_msg(Server, ServerMsg);
+ {Client, {error,closed}} ->
+ server_msg(Server, ServerMsg);
+ {Server, {error,closed}} ->
+ client_msg(Client, ClientMsg)
+ end.
+
+client_msg(Client, ClientMsg) ->
+ receive
+ {Client, ClientMsg} ->
+ ok;
+ {Client, {error,closed}} ->
+ ct:print("client got close"),
+ ok;
+ {Client, {error, Reason}} ->
+ ct:print("client got econnaborted: ~p", [Reason]),
+ ok;
+ Unexpected ->
+ ct:fail(Unexpected)
+ end.
+server_msg(Server, ServerMsg) ->
+ receive
+ {Server, ServerMsg} ->
+ ok;
+ {Server, {error,closed}} ->
+ ct:print("server got close"),
+ ok;
+ {Server, {error, Reason}} ->
+ ct:print("server got econnaborted: ~p", [Reason]),
+ ok;
+ Unexpected ->
+ ct:fail(Unexpected)
+ end.
diff --git a/lib/ssl/test/ssl_cipher_SUITE.erl b/lib/ssl/test/ssl_cipher_SUITE.erl
index 99bc21e820..9869812e6e 100644
--- a/lib/ssl/test/ssl_cipher_SUITE.erl
+++ b/lib/ssl/test/ssl_cipher_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -27,19 +27,22 @@
-include("ssl_internal.hrl").
-include("ssl_record.hrl").
-include("ssl_cipher.hrl").
+-include("ssl_alert.hrl").
-define(TIMEOUT, 600000).
-%% 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.
+%% Common Test interface functions -----------------------------------
%%--------------------------------------------------------------------
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [aes_decipher_good, aes_decipher_good_tls11, aes_decipher_fail, aes_decipher_fail_tls11].
+
+groups() ->
+ [].
+
init_per_suite(Config) ->
try crypto:start() of
ok ->
@@ -47,83 +50,55 @@ init_per_suite(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_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
init_per_testcase(_TestCase, Config0) ->
Config = lists:keydelete(watchdog, 1, Config0),
- Dog = ssl_test_lib:timetrap(?TIMEOUT),
+ Dog = ct: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.
+ Config.
%%--------------------------------------------------------------------
-%% 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
+%% Test Cases --------------------------------------------------------
%%--------------------------------------------------------------------
-suite() -> [{ct_hooks,[ts_install_cth]}].
-
-all() ->
- [aes_decipher_good, aes_decipher_fail].
-
-groups() ->
- [].
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
+aes_decipher_good() ->
+ [{doc,"Decipher a known cryptotext."}].
+aes_decipher_good(Config) when is_list(Config) ->
+ HashSz = 32,
+ CipherState = #cipher_state{iv = <<59,201,85,117,188,206,224,136,5,109,46,70,104,79,4,9>>,
+ key = <<72,196,247,97,62,213,222,109,210,204,217,186,172,184,197,148>>},
+ Fragment = <<220,193,179,139,171,33,143,245,202,47,123,251,13,232,114,8,
+ 190,162,74,31,186,227,119,155,94,74,119,79,169,193,240,160,
+ 198,181,81,19,98,162,213,228,74,224,253,168,156,59,195,122,
+ 108,101,107,242,20,15,169,150,163,107,101,94,93,104,241,165>>,
+ Content = <<183,139,16,132,10,209,67,86,168,100,61,217,145,57,36,56, "HELLO\n">>,
+ Mac = <<71,136,212,107,223,200,70,232,127,116,148,205,232,35,158,113,237,174,15,217,192,168,35,8,6,107,107,233,25,174,90,111>>,
+ Version = {3,0},
+ {Content, Mac, _} = ssl_cipher:decipher(?AES, HashSz, CipherState, Fragment, Version),
+ Version1 = {3,1},
+ {Content, Mac, _} = ssl_cipher:decipher(?AES, HashSz, CipherState, Fragment, Version1),
+ ok.
-%% Test cases starts here.
%%--------------------------------------------------------------------
-aes_decipher_good(doc) ->
- ["Decipher a known cryptotext."];
-aes_decipher_good(suite) ->
- [];
+aes_decipher_good_tls11() ->
+ [{doc,"Decipher a known TLS 1.1 cryptotext."}].
-aes_decipher_good(Config) when is_list(Config) ->
+%% the fragment is actuall a TLS 1.1 record, with
+%% Version = TLS 1.1, we get the correct NextIV in #cipher_state
+aes_decipher_good_tls11(Config) when is_list(Config) ->
HashSz = 32,
CipherState = #cipher_state{iv = <<59,201,85,117,188,206,224,136,5,109,46,70,104,79,4,9>>,
key = <<72,196,247,97,62,213,222,109,210,204,217,186,172,184,197,148>>},
@@ -131,19 +106,19 @@ aes_decipher_good(Config) when is_list(Config) ->
190,162,74,31,186,227,119,155,94,74,119,79,169,193,240,160,
198,181,81,19,98,162,213,228,74,224,253,168,156,59,195,122,
108,101,107,242,20,15,169,150,163,107,101,94,93,104,241,165>>,
- Version = {3,3},
- Content = <<183,139,16,132,10,209,67,86,168,100,61,217,145,57,36,56,72,69,76,76,79,10>>,
+ Content = <<"HELLO\n">>,
+ NextIV = <<183,139,16,132,10,209,67,86,168,100,61,217,145,57,36,56>>,
Mac = <<71,136,212,107,223,200,70,232,127,116,148,205,232,35,158,113,237,174,15,217,192,168,35,8,6,107,107,233,25,174,90,111>>,
- {Content, Mac, _} = ssl_cipher:decipher(?AES, HashSz, CipherState, Fragment, Version),
+ Version = {3,2},
+ {Content, Mac, #cipher_state{iv = NextIV}} = ssl_cipher:decipher(?AES, HashSz, CipherState, Fragment, Version),
+ Version1 = {3,2},
+ {Content, Mac, #cipher_state{iv = NextIV}} = ssl_cipher:decipher(?AES, HashSz, CipherState, Fragment, Version1),
ok.
%%--------------------------------------------------------------------
-aes_decipher_fail(doc) ->
- ["Decipher a known cryptotext."];
-
-aes_decipher_fail(suite) ->
- [];
+aes_decipher_fail() ->
+ [{doc,"Decipher a known cryptotext."}].
%% same as above, last byte of key replaced
aes_decipher_fail(Config) when is_list(Config) ->
@@ -154,10 +129,37 @@ aes_decipher_fail(Config) when is_list(Config) ->
190,162,74,31,186,227,119,155,94,74,119,79,169,193,240,160,
198,181,81,19,98,162,213,228,74,224,253,168,156,59,195,122,
108,101,107,242,20,15,169,150,163,107,101,94,93,104,241,165>>,
- Version = {3,3},
+ Version = {3,0},
{Content, Mac, _} = ssl_cipher:decipher(?AES, HashSz, CipherState, Fragment, Version),
32 = byte_size(Content),
32 = byte_size(Mac),
+ Version1 = {3,1},
+ {Content1, Mac1, _} = ssl_cipher:decipher(?AES, HashSz, CipherState, Fragment, Version1),
+ 32 = byte_size(Content1),
+ 32 = byte_size(Mac1),
+ ok.
+
+%%--------------------------------------------------------------------
+
+aes_decipher_fail_tls11() ->
+ [{doc,"Decipher a known TLS 1.1 cryptotext."}].
+
+%% same as above, last byte of key replaced
+%% stricter padding checks in TLS 1.1 mean we get an alert instead
+aes_decipher_fail_tls11(Config) when is_list(Config) ->
+ HashSz = 32,
+ CipherState = #cipher_state{iv = <<59,201,85,117,188,206,224,136,5,109,46,70,104,79,4,9>>,
+ key = <<72,196,247,97,62,213,222,109,210,204,217,186,172,184,197,254>>},
+ Fragment = <<220,193,179,139,171,33,143,245,202,47,123,251,13,232,114,8,
+ 190,162,74,31,186,227,119,155,94,74,119,79,169,193,240,160,
+ 198,181,81,19,98,162,213,228,74,224,253,168,156,59,195,122,
+ 108,101,107,242,20,15,169,150,163,107,101,94,93,104,241,165>>,
+ Version = {3,2},
+ #alert{level = ?FATAL, description = ?BAD_RECORD_MAC} =
+ ssl_cipher:decipher(?AES, HashSz, CipherState, Fragment, Version),
+ Version1 = {3,3},
+ #alert{level = ?FATAL, description = ?BAD_RECORD_MAC} =
+ ssl_cipher:decipher(?AES, HashSz, CipherState, Fragment, Version1),
ok.
%%--------------------------------------------------------------------
diff --git a/lib/ssl/test/ssl_dist_SUITE.erl b/lib/ssl/test/ssl_dist_SUITE.erl
index 818f7f1897..7bfd678f4b 100644
--- a/lib/ssl/test/ssl_dist_SUITE.erl
+++ b/lib/ssl/test/ssl_dist_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -19,7 +19,7 @@
-module(ssl_dist_SUITE).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
%% Note: This directive should only be used in test suites.
-compile(export_all).
@@ -35,7 +35,10 @@
nodename}
).
-%% Test server callback functions
+%%--------------------------------------------------------------------
+%% Common Test interface functions -----------------------------------
+%%--------------------------------------------------------------------
+
suite() ->
[{ct_hooks,[ts_install_cth]}].
@@ -54,6 +57,7 @@ end_per_group(_GroupName, Config) ->
init_per_suite(Config0) ->
try crypto:start() of
ok ->
+ %% Currently no ct function avilable for is_cover!
case test_server:is_cover() of
false ->
Config = add_ssl_opts_config(Config0),
@@ -98,11 +102,13 @@ common_end(_, Config) ->
Dog = ?config(watchdog, Config),
?t:timetrap_cancel(Dog),
ok.
+
%%--------------------------------------------------------------------
-%% Test cases starts here.
+%% Test Cases --------------------------------------------------------
%%--------------------------------------------------------------------
-basic(doc) ->
- ["Test that two nodes can connect via ssl distribution"];
+
+basic() ->
+ [{doc,"Test that two nodes can connect via ssl distribution"}].
basic(Config) when is_list(Config) ->
NH1 = start_ssl_node(Config),
Node1 = NH1#node_handle.nodename,
@@ -162,8 +168,8 @@ basic(Config) when is_list(Config) ->
success(Config).
%%--------------------------------------------------------------------
-payload(doc) ->
- ["Test that send a lot of data between the ssl distributed noes"];
+payload() ->
+ [{doc,"Test that send a lot of data between the ssl distributed noes"}].
payload(Config) when is_list(Config) ->
NH1 = start_ssl_node(Config),
Node1 = NH1#node_handle.nodename,
@@ -204,8 +210,8 @@ payload(Config) when is_list(Config) ->
stop_ssl_node(NH2),
success(Config).
%%--------------------------------------------------------------------
-plain_options(doc) ->
- ["Test specifying additional options"];
+plain_options() ->
+ [{doc,"Test specifying additional options"}].
plain_options(Config) when is_list(Config) ->
DistOpts = "-ssl_dist_opt server_secure_renegotiate true "
"client_secure_renegotiate true "
@@ -228,8 +234,8 @@ plain_options(Config) when is_list(Config) ->
stop_ssl_node(NH2),
success(Config).
%%--------------------------------------------------------------------
-plain_verify_options(doc) ->
- ["Test specifying additional options"];
+plain_verify_options() ->
+ [{doc,"Test specifying additional options"}].
plain_verify_options(Config) when is_list(Config) ->
DistOpts = "-ssl_dist_opt server_secure_renegotiate true "
"client_secure_renegotiate true "
@@ -251,7 +257,7 @@ plain_verify_options(Config) when is_list(Config) ->
success(Config).
%%--------------------------------------------------------------------
-%%% Internal functions
+%%% Internal functions -----------------------------------------------
%%--------------------------------------------------------------------
%% ssl_node side api
diff --git a/lib/ssl/test/ssl_handshake_SUITE.erl b/lib/ssl/test/ssl_handshake_SUITE.erl
index 08c23b2d47..aff0e0fbbc 100644
--- a/lib/ssl/test/ssl_handshake_SUITE.erl
+++ b/lib/ssl/test/ssl_handshake_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -27,6 +27,9 @@
-include("ssl_internal.hrl").
-include("ssl_handshake.hrl").
+%%--------------------------------------------------------------------
+%% Common Test interface functions -----------------------------------
+%%--------------------------------------------------------------------
suite() -> [{ct_hooks,[ts_install_cth]}].
all() -> [
@@ -34,6 +37,9 @@ all() -> [
decode_single_hello_extension_correctly,
decode_unknown_hello_extension_correctly].
+%%--------------------------------------------------------------------
+%% Test Cases --------------------------------------------------------
+%%--------------------------------------------------------------------
decode_hello_handshake(_Config) ->
HelloPacket = <<16#02, 16#00, 16#00,
16#44, 16#03, 16#03, 16#4e, 16#7f, 16#c1, 16#03, 16#35,
@@ -48,7 +54,8 @@ decode_hello_handshake(_Config) ->
16#00, 16#00, 16#33, 16#74, 16#00, 16#07, 16#06, 16#73,
16#70, 16#64, 16#79, 16#2f, 16#32>>,
- {Records, _Buffer} = ssl_handshake:get_tls_handshake(HelloPacket, <<>>),
+ Version = {3, 0},
+ {Records, _Buffer} = ssl_handshake:get_tls_handshake(Version, HelloPacket, <<>>),
{Hello, _Data} = hd(Records),
#renegotiation_info{renegotiated_connection = <<0>>} = Hello#server_hello.renegotiation_info.
diff --git a/lib/ssl/test/ssl_npn_handshake_SUITE.erl b/lib/ssl/test/ssl_npn_handshake_SUITE.erl
new file mode 100644
index 0000000000..4e848095a5
--- /dev/null
+++ b/lib/ssl/test/ssl_npn_handshake_SUITE.erl
@@ -0,0 +1,312 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, 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_npn_handshake_SUITE).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+-include_lib("common_test/include/ct.hrl").
+
+%%--------------------------------------------------------------------
+%% Common Test interface functions -----------------------------------
+%%--------------------------------------------------------------------
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [{group, 'tlsv1.2'},
+ {group, 'tlsv1.1'},
+ {group, 'tlsv1'},
+ {group, 'sslv3'}].
+
+groups() ->
+ [
+ {'tlsv1.2', [], next_protocol_tests()},
+ {'tlsv1.1', [], next_protocol_tests()},
+ {'tlsv1', [], next_protocol_tests()},
+ {'sslv3', [], next_protocol_not_supported()}
+ ].
+
+next_protocol_tests() ->
+ [validate_empty_protocols_are_not_allowed,
+ validate_empty_advertisement_list_is_allowed,
+ validate_advertisement_must_be_a_binary_list,
+ validate_client_protocols_must_be_a_tuple,
+ normal_npn_handshake_server_preference,
+ normal_npn_handshake_client_preference,
+ fallback_npn_handshake,
+ fallback_npn_handshake_server_preference,
+ client_negotiate_server_does_not_support,
+ no_client_negotiate_but_server_supports_npn,
+ renegotiate_from_client_after_npn_handshake
+ ].
+
+next_protocol_not_supported() ->
+ [npn_not_supported_client,
+ npn_not_supported_server
+ ].
+
+init_per_suite(Config) ->
+ catch crypto:stop(),
+ try crypto:start() of
+ ok ->
+ application:start(public_key),
+ ssl:start(),
+ Result =
+ (catch make_certs:all(?config(data_dir, Config),
+ ?config(priv_dir, Config))),
+ ct:print("Make certs ~p~n", [Result]),
+ ssl_test_lib:cert_options(Config)
+ catch _:_ ->
+ {skip, "Crypto did not start"}
+ end.
+
+end_per_suite(_Config) ->
+ ssl:stop(),
+ application:stop(crypto).
+
+
+init_per_group(GroupName, Config) ->
+ case ssl_test_lib:is_tls_version(GroupName) of
+ true ->
+ case ssl_test_lib:sufficient_crypto_support(GroupName) of
+ true ->
+ ssl_test_lib:init_tls_version(GroupName),
+ Config;
+ false ->
+ {skip, "Missing crypto support"}
+ end;
+ _ ->
+ ssl:start(),
+ Config
+ end.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% Test Cases --------------------------------------------------------
+%%--------------------------------------------------------------------
+
+validate_empty_protocols_are_not_allowed(Config) when is_list(Config) ->
+ {error, {eoptions, {next_protocols_advertised, {invalid_protocol, <<>>}}}}
+ = (catch ssl:listen(9443,
+ [{next_protocols_advertised, [<<"foo/1">>, <<"">>]}])),
+ {error, {eoptions, {client_preferred_next_protocols, {invalid_protocol, <<>>}}}}
+ = (catch ssl:connect({127,0,0,1}, 9443,
+ [{client_preferred_next_protocols,
+ {client, [<<"foo/1">>, <<"">>], <<"foox/1">>}}], infinity)),
+ Option = {client_preferred_next_protocols, {invalid_protocol, <<"">>}},
+ {error, {eoptions, Option}} = (catch ssl:connect({127,0,0,1}, 9443, [Option], infinity)).
+
+%--------------------------------------------------------------------------------
+
+validate_empty_advertisement_list_is_allowed(Config) when is_list(Config) ->
+ Option = {next_protocols_advertised, []},
+ {ok, Socket} = ssl:listen(0, [Option]),
+ ssl:close(Socket).
+%--------------------------------------------------------------------------------
+
+validate_advertisement_must_be_a_binary_list(Config) when is_list(Config) ->
+ Option = {next_protocols_advertised, blah},
+ {error, {eoptions, Option}} = (catch ssl:listen(9443, [Option])).
+%--------------------------------------------------------------------------------
+
+validate_client_protocols_must_be_a_tuple(Config) when is_list(Config) ->
+ Option = {client_preferred_next_protocols, [<<"foo/1">>]},
+ {error, {eoptions, Option}} = (catch ssl:connect({127,0,0,1}, 9443, [Option])).
+
+%--------------------------------------------------------------------------------
+
+normal_npn_handshake_server_preference(Config) when is_list(Config) ->
+ run_npn_handshake(Config,
+ [{client_preferred_next_protocols,
+ {server, [<<"http/1.0">>, <<"http/1.1">>], <<"http/1.1">>}}],
+ [{next_protocols_advertised, [<<"spdy/2">>, <<"http/1.1">>, <<"http/1.0">>]}],
+ {ok, <<"http/1.1">>}).
+%--------------------------------------------------------------------------------
+
+normal_npn_handshake_client_preference(Config) when is_list(Config) ->
+ run_npn_handshake(Config,
+ [{client_preferred_next_protocols,
+ {client, [<<"http/1.0">>, <<"http/1.1">>], <<"http/1.1">>}}],
+ [{next_protocols_advertised, [<<"spdy/2">>, <<"http/1.1">>, <<"http/1.0">>]}],
+ {ok, <<"http/1.0">>}).
+
+%--------------------------------------------------------------------------------
+
+fallback_npn_handshake(Config) when is_list(Config) ->
+ run_npn_handshake(Config,
+ [{client_preferred_next_protocols, {client, [<<"spdy/2">>], <<"http/1.1">>}}],
+ [{next_protocols_advertised, [<<"spdy/1">>, <<"http/1.1">>, <<"http/1.0">>]}],
+ {ok, <<"http/1.1">>}).
+%--------------------------------------------------------------------------------
+
+fallback_npn_handshake_server_preference(Config) when is_list(Config) ->
+ run_npn_handshake(Config,
+ [{client_preferred_next_protocols, {server, [<<"spdy/2">>], <<"http/1.1">>}}],
+ [{next_protocols_advertised, [<<"spdy/1">>, <<"http/1.1">>, <<"http/1.0">>]}],
+ {ok, <<"http/1.1">>}).
+
+%--------------------------------------------------------------------------------
+
+no_client_negotiate_but_server_supports_npn(Config) when is_list(Config) ->
+ run_npn_handshake(Config,
+ [],
+ [{next_protocols_advertised, [<<"spdy/1">>, <<"http/1.1">>, <<"http/1.0">>]}],
+ {error, next_protocol_not_negotiated}).
+%--------------------------------------------------------------------------------
+
+
+client_negotiate_server_does_not_support(Config) when is_list(Config) ->
+ run_npn_handshake(Config,
+ [{client_preferred_next_protocols, {client, [<<"spdy/2">>], <<"http/1.1">>}}],
+ [],
+ {error, next_protocol_not_negotiated}).
+
+%--------------------------------------------------------------------------------
+renegotiate_from_client_after_npn_handshake(Config) when is_list(Config) ->
+ Data = "hello world",
+
+ ClientOpts0 = ?config(client_opts, Config),
+ ClientOpts = [{client_preferred_next_protocols,
+ {client, [<<"http/1.0">>], <<"http/1.1">>}}] ++ ClientOpts0,
+ ServerOpts0 = ?config(server_opts, Config),
+ ServerOpts = [{next_protocols_advertised,
+ [<<"spdy/2">>, <<"http/1.1">>, <<"http/1.0">>]}] ++ ServerOpts0,
+ ExpectedProtocol = {ok, <<"http/1.0">>},
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, ssl_receive_and_assert_npn, [ExpectedProtocol, Data]}},
+ {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, assert_npn_and_renegotiate_and_send_data, [ExpectedProtocol, Data]}},
+ {options, ClientOpts}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok).
+
+%--------------------------------------------------------------------------------
+npn_not_supported_client(Config) when is_list(Config) ->
+ ClientOpts0 = ?config(client_opts, Config),
+ PrefProtocols = {client_preferred_next_protocols,
+ {client, [<<"http/1.0">>], <<"http/1.1">>}},
+ ClientOpts = [PrefProtocols] ++ ClientOpts0,
+ {ClientNode, _ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Client = ssl_test_lib:start_client_error([{node, ClientNode},
+ {port, 8888}, {host, Hostname},
+ {from, self()}, {options, ClientOpts}]),
+
+ ssl_test_lib:check_result(Client, {error,
+ {eoptions,
+ {not_supported_in_sslv3, PrefProtocols}}}).
+
+%--------------------------------------------------------------------------------
+npn_not_supported_server(Config) when is_list(Config)->
+ ServerOpts0 = ?config(server_opts, Config),
+ AdvProtocols = {next_protocols_advertised, [<<"spdy/2">>, <<"http/1.1">>, <<"http/1.0">>]},
+ ServerOpts = [AdvProtocols] ++ ServerOpts0,
+
+ {error, {eoptions, {not_supported_in_sslv3, AdvProtocols}}} = ssl:listen(0, ServerOpts).
+
+%%--------------------------------------------------------------------
+%% Internal functions ------------------------------------------------
+%%--------------------------------------------------------------------
+run_npn_handshake(Config, ClientExtraOpts, ServerExtraOpts, ExpectedProtocol) ->
+ Data = "hello world",
+
+ ClientOpts0 = ?config(client_opts, Config),
+ ClientOpts = ClientExtraOpts ++ ClientOpts0,
+ ServerOpts0 = ?config(server_opts, Config),
+ ServerOpts = ServerExtraOpts ++ ServerOpts0,
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, ssl_receive_and_assert_npn, [ExpectedProtocol, Data]}},
+ {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, ssl_send_and_assert_npn, [ExpectedProtocol, Data]}},
+ {options, ClientOpts}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok).
+
+
+assert_npn(Socket, Protocol) ->
+ ct:print("Negotiated Protocol ~p, Expecting: ~p ~n",
+ [ssl:negotiated_next_protocol(Socket), Protocol]),
+ Protocol = ssl:negotiated_next_protocol(Socket).
+
+assert_npn_and_renegotiate_and_send_data(Socket, Protocol, Data) ->
+ assert_npn(Socket, Protocol),
+ ct:print("Renegotiating ~n", []),
+ ok = ssl:renegotiate(Socket),
+ ssl:send(Socket, Data),
+ assert_npn(Socket, Protocol),
+ ok.
+
+ssl_send_and_assert_npn(Socket, Protocol, Data) ->
+ assert_npn(Socket, Protocol),
+ ssl_send(Socket, Data).
+
+ssl_receive_and_assert_npn(Socket, Protocol, Data) ->
+ assert_npn(Socket, Protocol),
+ ssl_receive(Socket, Data).
+
+ssl_send(Socket, Data) ->
+ ct:print("Connection info: ~p~n",
+ [ssl:connection_info(Socket)]),
+ ssl:send(Socket, Data).
+
+ssl_receive(Socket, Data) ->
+ ssl_receive(Socket, Data, []).
+
+ssl_receive(Socket, Data, Buffer) ->
+ ct:print("Connection info: ~p~n",
+ [ssl:connection_info(Socket)]),
+ receive
+ {ssl, Socket, MoreData} ->
+ ct:print("Received ~p~n",[MoreData]),
+ NewBuffer = Buffer ++ MoreData,
+ case NewBuffer of
+ Data ->
+ ssl:send(Socket, "Got it"),
+ ok;
+ _ ->
+ ssl_receive(Socket, Data, NewBuffer)
+ end;
+ Other ->
+ ct:fail({unexpected_message, Other})
+ after 4000 ->
+ ct:fail({did_not_get, Data})
+ end.
+
+
+connection_info_result(Socket) ->
+ ssl:connection_info(Socket).
diff --git a/lib/ssl/test/ssl_npn_hello_SUITE.erl b/lib/ssl/test/ssl_npn_hello_SUITE.erl
new file mode 100644
index 0000000000..72768bcb55
--- /dev/null
+++ b/lib/ssl/test/ssl_npn_hello_SUITE.erl
@@ -0,0 +1,129 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, 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_npn_hello_SUITE).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+-include("ssl_handshake.hrl").
+-include("ssl_record.hrl").
+-include_lib("common_test/include/ct.hrl").
+
+%%--------------------------------------------------------------------
+%% Common Test interface functions -----------------------------------
+%%--------------------------------------------------------------------
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [encode_and_decode_npn_client_hello_test,
+ encode_and_decode_npn_server_hello_test,
+ encode_and_decode_client_hello_test,
+ encode_and_decode_server_hello_test,
+ create_server_hello_with_advertised_protocols_test,
+ create_server_hello_with_no_advertised_protocols_test].
+
+%%--------------------------------------------------------------------
+%% Test Cases --------------------------------------------------------
+%%--------------------------------------------------------------------
+
+encode_and_decode_client_hello_test(_Config) ->
+ HandShakeData = create_client_handshake(undefined),
+ Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+ {[{DecodedHandshakeMessage, _Raw}], _} =
+ ssl_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>),
+ NextProtocolNegotiation = DecodedHandshakeMessage#client_hello.next_protocol_negotiation,
+ NextProtocolNegotiation = undefined.
+%%--------------------------------------------------------------------
+encode_and_decode_npn_client_hello_test(_Config) ->
+ HandShakeData = create_client_handshake(#next_protocol_negotiation{extension_data = <<>>}),
+ Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+ {[{DecodedHandshakeMessage, _Raw}], _} =
+ ssl_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>),
+ NextProtocolNegotiation = DecodedHandshakeMessage#client_hello.next_protocol_negotiation,
+ NextProtocolNegotiation = #next_protocol_negotiation{extension_data = <<>>}.
+%%--------------------------------------------------------------------
+encode_and_decode_server_hello_test(_Config) ->
+ HandShakeData = create_server_handshake(undefined),
+ Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+ {[{DecodedHandshakeMessage, _Raw}], _} =
+ ssl_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>),
+ NextProtocolNegotiation = DecodedHandshakeMessage#server_hello.next_protocol_negotiation,
+ NextProtocolNegotiation = undefined.
+%%--------------------------------------------------------------------
+encode_and_decode_npn_server_hello_test(_Config) ->
+ HandShakeData = create_server_handshake(#next_protocol_negotiation{extension_data = <<6, "spdy/2">>}),
+ Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+ {[{DecodedHandshakeMessage, _Raw}], _} =
+ ssl_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>),
+ NextProtocolNegotiation = DecodedHandshakeMessage#server_hello.next_protocol_negotiation,
+ ct:print("~p ~n", [NextProtocolNegotiation]),
+ NextProtocolNegotiation = #next_protocol_negotiation{extension_data = <<6, "spdy/2">>}.
+
+%%--------------------------------------------------------------------
+create_server_hello_with_no_advertised_protocols_test(_Config) ->
+ Hello = ssl_handshake:server_hello(<<>>, {3, 0}, create_connection_states(), false, undefined),
+ undefined = Hello#server_hello.next_protocol_negotiation.
+%%--------------------------------------------------------------------
+create_server_hello_with_advertised_protocols_test(_Config) ->
+ Hello = ssl_handshake:server_hello(<<>>, {3, 0}, create_connection_states(),
+ false, [<<"spdy/1">>, <<"http/1.0">>, <<"http/1.1">>]),
+ #next_protocol_negotiation{extension_data = <<6, "spdy/1", 8, "http/1.0", 8, "http/1.1">>} =
+ Hello#server_hello.next_protocol_negotiation.
+%%--------------------------------------------------------------------
+%% Internal functions ------------------------------------------------
+%%--------------------------------------------------------------------
+create_client_handshake(Npn) ->
+ ssl_handshake:encode_handshake(#client_hello{
+ client_version = {1, 2},
+ random = <<1:256>>,
+ session_id = <<>>,
+ cipher_suites = "",
+ compression_methods = "",
+ next_protocol_negotiation = Npn,
+ renegotiation_info = #renegotiation_info{}
+ }, vsn).
+
+create_server_handshake(Npn) ->
+ ssl_handshake:encode_handshake(#server_hello{
+ server_version = {1, 2},
+ random = <<1:256>>,
+ session_id = <<>>,
+ cipher_suite = <<1,2>>,
+ compression_method = 1,
+ next_protocol_negotiation = Npn,
+ renegotiation_info = #renegotiation_info{}
+ }, vsn).
+
+create_connection_states() ->
+ #connection_states{
+ pending_read = #connection_state{
+ security_parameters = #security_parameters{
+ server_random = <<1:256>>,
+ compression_algorithm = 1,
+ cipher_suite = <<1, 2>>
+ }
+ },
+
+ current_read = #connection_state {
+ secure_renegotiation = false
+ }
+ }.
diff --git a/lib/ssl/test/ssl_packet_SUITE.erl b/lib/ssl/test/ssl_packet_SUITE.erl
index 593b1fda5e..158c40e372 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-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -41,96 +41,57 @@
-define(MANY, 1000).
-define(SOME, 50).
-
-%% 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) ->
- catch crypto:stop(),
- 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()]
-%% 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(_TestCase, Config0) ->
- Config = lists:keydelete(watchdog, 1, Config0),
- Dog = ssl_test_lib: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
+%% Common Test interface functions -----------------------------------
%%--------------------------------------------------------------------
-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() ->
+ [
+ {group, 'tlsv1.2'},
+ {group, 'tlsv1.1'},
+ {group, 'tlsv1'},
+ {group, 'sslv3'}
+ ].
+
+packet_tests() ->
+ active_packet_tests() ++ active_once_packet_tests() ++ passive_packet_tests() ++
+ [packet_send_to_large,
+ packet_cdr_decode, packet_cdr_decode_list,
+ packet_http_decode, packet_http_decode_list,
+ packet_http_bin_decode_multi,
+ 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].
+
+passive_packet_tests() ->
[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_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_httph_passive,
+ packet_httph_bin_passive,
+ packet_http_error_passive,
+ packet_wait_passive,
+ packet_size_passive,
+ packet_baddata_passive,
+ %% inet header option should be deprecated!
+ header_decode_one_byte_passive,
+ header_decode_two_bytes_passive,
+ header_decode_two_bytes_two_sent_passive,
+ header_decode_two_bytes_one_sent_passive
+ ].
+
+active_once_packet_tests() ->
+ [packet_raw_active_once_many_small,
packet_0_active_once_many_small,
packet_1_active_once_many_small,
packet_2_active_once_many_small,
@@ -140,1074 +101,327 @@ all() ->
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_wait_passive, packet_wait_active,
- packet_baddata_passive, packet_baddata_active,
- packet_size_passive, packet_size_active,
- packet_cdr_decode, packet_cdr_decode_list,
- packet_http_decode, packet_http_decode_list,
- packet_http_bin_decode_multi, packet_http_error_passive,
- packet_httph_active, packet_httph_bin_active,
- packet_httph_active_once, packet_httph_bin_active_once,
- packet_httph_passive, packet_httph_bin_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,
- {group, header}
+ packet_httph_active_once,
+ packet_httph_bin_active_once
].
-groups() ->
- [{header, [], [ header_decode_one_byte,
- header_decode_two_bytes,
- header_decode_two_bytes_one_sent,
- header_decode_two_bytes_two_sent]}].
+active_packet_tests() ->
+ [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_httph_active,
+ packet_httph_bin_active,
+ packet_wait_active,
+ packet_baddata_active,
+ packet_size_active,
+ %% inet header option should be deprecated!
+ header_decode_one_byte_active,
+ header_decode_two_bytes_active,
+ header_decode_two_bytes_two_sent_active,
+ header_decode_two_bytes_one_sent_active
+ ].
-init_per_group(header, Config) ->
- case ssl_record:highest_protocol_version(ssl_record:supported_protocol_versions()) of
- {3, N} when N < 2 ->
- {skip, ""};
+init_per_suite(Config) ->
+ catch crypto:stop(),
+ try crypto:start() of
+ ok ->
+ application:start(public_key),
+ ssl:start(),
+ Result =
+ (catch make_certs:all(?config(data_dir, Config),
+ ?config(priv_dir, Config))),
+ ct:print("Make certs ~p~n", [Result]),
+ ssl_test_lib:cert_options(Config)
+ catch _:_ ->
+ {skip, "Crypto did not start"}
+ end.
+
+end_per_suite(_Config) ->
+ ssl:stop(),
+ application:stop(crypto).
+
+init_per_group(GroupName, Config) ->
+ case ssl_test_lib:is_tls_version(GroupName) of
+ true ->
+ case ssl_test_lib:sufficient_crypto_support(GroupName) of
+ true ->
+ ssl_test_lib:init_tls_version(GroupName),
+ Config;
+ false ->
+ {skip, "Missing crypto support"}
+ end;
_ ->
+ ssl:start(),
Config
- end;
+ end.
-init_per_group(_, Config) ->
- Config.
end_per_group(_GroupName, Config) ->
Config.
+init_per_testcase(_TestCase, Config0) ->
+ Config = lists:keydelete(watchdog, 1, Config0),
+ Dog = ct:timetrap(?TIMEOUT),
+ [{watchdog, Dog} | Config].
-%% Test cases starts here.
-%%--------------------------------------------------------------------
-packet_raw_passive_many_small(doc) ->
- ["Test packet option {packet, raw} in passive mode."];
-
-packet_raw_passive_many_small(suite) ->
- [];
-
-packet_raw_passive_many_small(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 = "Packet option is {packet, raw}",
+end_per_testcase(_TestCase, Config) ->
+ Config.
- Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, send_raw ,[Data, ?MANY]}},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE, passive_raw, [Data, ?MANY]}},
- {options,
- [{active, false},
- {packet, raw} |
- ClientOpts]}]),
+%%--------------------------------------------------------------------
+%% Test Cases --------------------------------------------------------
+%%--------------------------------------------------------------------
- ssl_test_lib:check_result(Client, ok),
+packet_raw_passive_many_small() ->
+ [{doc,"Test packet option {packet, raw} in passive mode."}].
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
+packet_raw_passive_many_small(Config) when is_list(Config) ->
+ Data = "Packet option is {packet, raw}",
+ packet(Config, Data, send, passive_recv_packet, ?MANY, raw, false).
%%--------------------------------------------------------------------
-packet_raw_passive_some_big(doc) ->
- ["Test packet option {packet, raw} in passive mode."];
-
-packet_raw_passive_some_big(suite) ->
- [];
-
-packet_raw_passive_some_big(Config) when is_list(Config) ->
- ClientOpts = ?config(client_opts, Config),
- ServerOpts = ?config(server_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+packet_raw_passive_some_big() ->
+ [{doc,"Test packet option {packet, raw} in passive mode."}].
+packet_raw_passive_some_big(Config) when is_list(Config) ->
Data = lists:append(lists:duplicate(100, "1234567890")),
-
- Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, send_raw ,[Data, ?SOME]}},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE, passive_raw, [Data, ?SOME]}},
- {options,
- [{active, false},
- {packet, raw} |
- ClientOpts]}]),
-
- ssl_test_lib:check_result(Client, ok),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
+ packet(Config, Data, send, passive_recv_packet, ?SOME, raw, false).
%%--------------------------------------------------------------------
-packet_0_passive_many_small(doc) ->
- ["Test packet option {packet, 0} in passive mode."];
-
-packet_0_passive_many_small(suite) ->
- [];
-
-packet_0_passive_many_small(Config) when is_list(Config) ->
- ClientOpts = ?config(client_opts, Config),
- ServerOpts = ?config(server_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+packet_0_passive_many_small() ->
+ [{doc,"Test packet option {packet, 0} in passive mode."}].
+packet_0_passive_many_small(Config) when is_list(Config) ->
Data = "Packet option is {packet, 0}, equivalent to packet raw.",
-
- Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, send_raw ,[Data, ?MANY]}},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE, passive_raw, [Data, ?MANY]}},
- {options, [{active, false},
- {packet, 0} |
- ClientOpts]}]),
-
- ssl_test_lib:check_result(Client, ok),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
+ packet(Config, Data, send, passive_recv_packet, ?MANY, 0, false).
%%--------------------------------------------------------------------
-packet_0_passive_some_big(doc) ->
- ["Test packet option {packet, 0} in passive mode."];
-
-packet_0_passive_some_big(suite) ->
- [];
-
-packet_0_passive_some_big(Config) when is_list(Config) ->
- ClientOpts = ?config(client_opts, Config),
- ServerOpts = ?config(server_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+packet_0_passive_some_big() ->
+ [{doc,"Test packet option {packet, 0} in passive mode."}].
+packet_0_passive_some_big(Config) when is_list(Config) ->
Data = lists:append(lists:duplicate(100, "1234567890")),
-
- Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, send_raw ,[Data, ?SOME]}},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE, passive_raw, [Data, ?SOME]}},
- {options, [{active, false},
- {packet, 0} |
- ClientOpts]}]),
-
- ssl_test_lib:check_result(Client, ok),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
+ packet(Config, Data, send, passive_recv_packet, ?SOME, 0, false).
%%--------------------------------------------------------------------
-packet_1_passive_many_small(doc) ->
- ["Test packet option {packet, 1} in passive mode."];
-
-packet_1_passive_many_small(suite) ->
- [];
-
-packet_1_passive_many_small(Config) when is_list(Config) ->
- ClientOpts = ?config(client_opts, Config),
- ServerOpts = ?config(server_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+packet_1_passive_many_small() ->
+ [{doc,"Test packet option {packet, 1} in passive mode."}].
+packet_1_passive_many_small(Config) when is_list(Config) ->
Data = "Packet option is {packet, 1}",
-
- Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, send, [Data, ?MANY]}},
- {options, [{packet, 1}|ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE, passive_recv_packet,
- [Data, ?MANY]}},
- {options, [{active, false},
- {packet, 1} |
- ClientOpts]}]),
-
- ssl_test_lib:check_result(Client, ok),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
+ packet(Config, Data, send, passive_recv_packet, ?MANY, 1, false).
%%--------------------------------------------------------------------
-packet_1_passive_some_big(doc) ->
- ["Test packet option {packet, 1} in passive mode."];
-
-packet_1_passive_some_big(suite) ->
- [];
-
-packet_1_passive_some_big(Config) when is_list(Config) ->
- ClientOpts = ?config(client_opts, Config),
- ServerOpts = ?config(server_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+packet_1_passive_some_big() ->
+ [{doc,"Test packet option {packet, 1} in passive mode."}].
+packet_1_passive_some_big(Config) when is_list(Config) ->
Data = lists:append(lists:duplicate(255, "1")),
-
- Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, send, [Data, ?SOME]}},
- {options, [{packet, 1}|ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE, passive_recv_packet,
- [Data, ?SOME]}},
- {options, [{active, false},
- {packet, 1} |
- ClientOpts]}]),
-
- ssl_test_lib:check_result(Client, ok),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
+ packet(Config, Data, send, passive_recv_packet, ?SOME, 1, false).
%%--------------------------------------------------------------------
-packet_2_passive_many_small(doc) ->
- ["Test packet option {packet, 2} in passive mode"];
-
-packet_2_passive_many_small(suite) ->
- [];
-
-packet_2_passive_many_small(Config) when is_list(Config) ->
- ClientOpts = ?config(client_opts, Config),
- ServerOpts = ?config(server_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+packet_2_passive_many_small() ->
+ [{doc,"Test packet option {packet, 2} in passive mode"}].
+packet_2_passive_many_small(Config) when is_list(Config) ->
Data = "Packet option is {packet, 2}",
-
- Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, send, [Data, ?MANY]}},
- {options, [{packet, 2}|ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE, passive_recv_packet,
- [Data, ?MANY]}},
- {options, [{active, false},
- {packet, 2} |
- ClientOpts]}]),
-
- ssl_test_lib:check_result(Client, ok),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
+ packet(Config, Data, send, passive_recv_packet, ?MANY, 2, false).
%%--------------------------------------------------------------------
-packet_2_passive_some_big(doc) ->
- ["Test packet option {packet, 2} in passive mode"];
-
-packet_2_passive_some_big(suite) ->
- [];
-
-packet_2_passive_some_big(Config) when is_list(Config) ->
- ClientOpts = ?config(client_opts, Config),
- ServerOpts = ?config(server_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+packet_2_passive_some_big() ->
+ [{doc,"Test packet option {packet, 2} in passive mode"}].
+packet_2_passive_some_big(Config) when is_list(Config) ->
Data = lists:append(lists:duplicate(100, "1234567890")),
-
- Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, send, [Data, ?SOME]}},
- {options, [{packet, 2}|ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE, passive_recv_packet,
- [Data, ?SOME]}},
- {options, [{active, false},
- {packet, 2} |
- ClientOpts]}]),
-
- ssl_test_lib:check_result(Client, ok),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
+ packet(Config, Data, send, passive_recv_packet, ?SOME, 2, false).
%%--------------------------------------------------------------------
-packet_4_passive_many_small(doc) ->
- ["Test packet option {packet, 4} in passive mode"];
-
-packet_4_passive_many_small(suite) ->
- [];
-
-packet_4_passive_many_small(Config) when is_list(Config) ->
- ClientOpts = ?config(client_opts, Config),
- ServerOpts = ?config(server_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+packet_4_passive_many_small() ->
+ [{doc,"Test packet option {packet, 4} in passive mode"}].
+packet_4_passive_many_small(Config) when is_list(Config) ->
Data = "Packet option is {packet, 4}",
-
- Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
- {from, self()},
- {mfa,
- {?MODULE, send, [Data, ?MANY]}},
- {options, [{packet, 4}|ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE, passive_recv_packet,
- [Data, ?MANY]}},
- {options, [{active, false},
- {packet, 4} |
- ClientOpts]}]),
-
- ssl_test_lib:check_result(Client, ok),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
+ packet(Config, Data, send, passive_recv_packet, ?MANY, 4, false).
%%--------------------------------------------------------------------
-packet_4_passive_some_big(doc) ->
- ["Test packet option {packet, 4} in passive mode"];
-
-packet_4_passive_some_big(suite) ->
- [];
-
-packet_4_passive_some_big(Config) when is_list(Config) ->
- ClientOpts = ?config(client_opts, Config),
- ServerOpts = ?config(server_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+packet_4_passive_some_big() ->
+ [{doc,"Test packet option {packet, 4} in passive mode"}].
+packet_4_passive_some_big(Config) when is_list(Config) ->
Data = lists:append(lists:duplicate(100, "1234567890")),
-
- Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, send, [Data, ?SOME]}},
- {options, [{packet, 4}|ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE, passive_recv_packet,
- [Data, ?SOME]}},
- {options, [{active, false},
- {packet, 4} |
- ClientOpts]}]),
-
- ssl_test_lib:check_result(Client, ok),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
+ packet(Config, Data, send, passive_recv_packet, ?SOME, 4, false).
%%--------------------------------------------------------------------
-packet_raw_active_once_many_small(doc) ->
- ["Test packet option {packet, raw} in active once mode."];
-
-packet_raw_active_once_many_small(suite) ->
- [];
-
-packet_raw_active_once_many_small(Config) when is_list(Config) ->
- ClientOpts = ?config(client_opts, Config),
- ServerOpts = ?config(server_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+packet_raw_active_once_many_small() ->
+ [{doc,"Test packet option {packet, raw} in active once mode."}].
+packet_raw_active_once_many_small(Config) when is_list(Config) ->
Data = "Packet option is {packet, raw}",
-
- Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, send_raw ,[Data, ?MANY]}},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE, active_once_raw,
- [Data, ?MANY]}},
- {options, [{active, once},
- {packet, raw} |
- ClientOpts]}]),
-
- ssl_test_lib:check_result(Client, ok),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
+ packet(Config, Data, send_raw, active_once_raw, ?MANY, raw, once).
%%--------------------------------------------------------------------
-packet_raw_active_once_some_big(doc) ->
- ["Test packet option {packet, raw} in active once mode."];
-
-packet_raw_active_once_some_big(suite) ->
- [];
-
-packet_raw_active_once_some_big(Config) when is_list(Config) ->
- ClientOpts = ?config(client_opts, Config),
- ServerOpts = ?config(server_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+packet_raw_active_once_some_big() ->
+ [{doc,"Test packet option {packet, raw} in active once mode."}].
+packet_raw_active_once_some_big(Config) when is_list(Config) ->
Data = lists:append(lists:duplicate(100, "1234567890")),
-
- Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, send_raw ,[Data, ?SOME]}},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE, active_once_raw,
- [Data, ?SOME]}},
- {options, [{active, once},
- {packet, raw} |
- ClientOpts]}]),
-
- ssl_test_lib:check_result(Client, ok),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
+ packet(Config, Data, send_raw, active_once_raw, ?SOME, raw, once).
%%--------------------------------------------------------------------
-packet_0_active_once_many_small(doc) ->
- ["Test packet option {packet, 0} in active once mode."];
-
-packet_0_active_once_many_small(suite) ->
- [];
-
-packet_0_active_once_many_small(Config) when is_list(Config) ->
- ClientOpts = ?config(client_opts, Config),
- ServerOpts = ?config(server_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+packet_0_active_once_many_small() ->
+ [{doc,"Test packet option {packet, 0} in active once mode."}].
+packet_0_active_once_many_small(Config) when is_list(Config) ->
Data = "Packet option is {packet, 0}",
-
- Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, send_raw ,[Data, ?MANY]}},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa,
- {?MODULE, active_once_raw,
- [Data, ?MANY]}},
- {options, [{active, once},
- {packet, 0} |
- ClientOpts]}]),
-
- ssl_test_lib:check_result(Client, ok),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
+ packet(Config, Data, send_raw, active_once_raw, ?MANY, 0, once).
%%--------------------------------------------------------------------
-packet_0_active_once_some_big(doc) ->
- ["Test packet option {packet, 0} in active once mode."];
-
-packet_0_active_once_some_big(suite) ->
- [];
-
-packet_0_active_once_some_big(Config) when is_list(Config) ->
- ClientOpts = ?config(client_opts, Config),
- ServerOpts = ?config(server_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+packet_0_active_once_some_big() ->
+ [{doc,"Test packet option {packet, 0} in active once mode."}].
+packet_0_active_once_some_big(Config) when is_list(Config) ->
Data = lists:append(lists:duplicate(100, "1234567890")),
-
- Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, send_raw ,
- [Data, ?SOME]}},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa,
- {?MODULE, active_once_raw,
- [Data, ?SOME]}},
- {options, [{active, once},
- {packet, 0} |
- ClientOpts]}]),
-
- ssl_test_lib:check_result(Client, ok),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
+ packet(Config, Data, send_raw, active_once_raw, ?SOME, 0, once).
%%--------------------------------------------------------------------
-packet_1_active_once_many_small(doc) ->
- ["Test packet option {packet, 1} in active once mode."];
-
-packet_1_active_once_many_small(suite) ->
- [];
-
-packet_1_active_once_many_small(Config) when is_list(Config) ->
- ClientOpts = ?config(client_opts, Config),
- ServerOpts = ?config(server_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+packet_1_active_once_many_small() ->
+ [{doc,"Test packet option {packet, 1} in active once mode."}].
+packet_1_active_once_many_small(Config) when is_list(Config) ->
Data = "Packet option is {packet, 1}",
-
- Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, send, [Data, ?MANY]}},
- {options, [{packet, 1}|ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa,
- {?MODULE,
- active_once_packet,
- [Data, ?MANY]}},
- {options, [{active, once},
- {packet, 1} |
- ClientOpts]}]),
-
- ssl_test_lib:check_result(Client, ok),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
+ packet(Config, Data, send_raw, active_once_raw, ?MANY, 1, once).
%%--------------------------------------------------------------------
-packet_1_active_once_some_big(doc) ->
- ["Test packet option {packet, 1} in active once mode."];
-
-packet_1_active_once_some_big(suite) ->
- [];
-
-packet_1_active_once_some_big(Config) when is_list(Config) ->
- ClientOpts = ?config(client_opts, Config),
- ServerOpts = ?config(server_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+packet_1_active_once_some_big() ->
+ [{doc,"Test packet option {packet, 1} in active once mode."}].
+packet_1_active_once_some_big(Config) when is_list(Config) ->
Data = lists:append(lists:duplicate(255, "1")),
+ packet(Config, Data, send_raw, active_once_raw, ?SOME, 1, once).
- Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, send, [Data, ?SOME]}},
- {options, [{packet, 1}|ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa,
- {?MODULE,
- active_once_packet,
- [Data, ?SOME]}},
- {options, [{active, once},
- {packet, 1} |
- ClientOpts]}]),
-
- ssl_test_lib:check_result(Client, ok),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
%%--------------------------------------------------------------------
-packet_2_active_once_many_small(doc) ->
- ["Test packet option {packet, 2} in active once mode"];
-
-packet_2_active_once_many_small(suite) ->
- [];
-
-packet_2_active_once_many_small(Config) when is_list(Config) ->
- ClientOpts = ?config(client_opts, Config),
- ServerOpts = ?config(server_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+packet_2_active_once_many_small() ->
+ [{doc,"Test packet option {packet, 2} in active once mode"}].
+packet_2_active_once_many_small(Config) when is_list(Config) ->
Data = "Packet option is {packet, 2}",
-
- Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, send, [Data, ?MANY]}},
- {options, [{packet, 2}|ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa,
- {?MODULE,
- active_once_packet,
- [Data, ?MANY]}},
- {options, [{active, once},
- {packet, 2} |
- ClientOpts]}]),
-
- ssl_test_lib:check_result(Client, ok),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
+ packet(Config, Data, send_raw, active_once_raw, ?MANY, 2, once).
%%--------------------------------------------------------------------
-packet_2_active_once_some_big(doc) ->
- ["Test packet option {packet, 2} in active once mode"];
-
-packet_2_active_once_some_big(suite) ->
- [];
-
-packet_2_active_once_some_big(Config) when is_list(Config) ->
- ClientOpts = ?config(client_opts, Config),
- ServerOpts = ?config(server_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+packet_2_active_once_some_big() ->
+ [{doc,"Test packet option {packet, 2} in active once mode"}].
+packet_2_active_once_some_big(Config) when is_list(Config) ->
Data = lists:append(lists:duplicate(100, "1234567890")),
-
- Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, send, [Data, ?SOME]}},
- {options, [{packet, 2}|ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa,
- {?MODULE,
- active_once_packet,
- [Data, ?SOME]}},
- {options, [{active, once},
- {packet, 2} |
- ClientOpts]}]),
-
- ssl_test_lib:check_result(Client, ok),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
+ packet(Config, Data, send_raw, active_once_raw, ?SOME, 2, once).
%%--------------------------------------------------------------------
-packet_4_active_once_many_small(doc) ->
- ["Test packet option {packet, 4} in active once mode"];
-
-packet_4_active_once_many_small(suite) ->
- [];
-
-packet_4_active_once_many_small(Config) when is_list(Config) ->
- ClientOpts = ?config(client_opts, Config),
- ServerOpts = ?config(server_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+packet_4_active_once_many_small() ->
+ [{doc,"Test packet option {packet, 4} in active once mode"}].
+packet_4_active_once_many_small(Config) when is_list(Config) ->
Data = "Packet option is {packet, 4}",
-
- Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, send, [Data, ?MANY]}},
- {options, [{packet, 4}|ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa,
- {?MODULE,
- active_once_packet,
- [Data, ?MANY]}},
- {options, [{active, once},
- {packet, 4} |
- ClientOpts]}]),
-
- ssl_test_lib:check_result(Client, ok),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
+ packet(Config, Data, send_raw, active_once_raw, ?MANY, 4, once).
%%--------------------------------------------------------------------
-packet_4_active_once_some_big(doc) ->
- ["Test packet option {packet, 4} in active once mode"];
-
-packet_4_active_once_some_big(suite) ->
- [];
-
-packet_4_active_once_some_big(Config) when is_list(Config) ->
- ClientOpts = ?config(client_opts, Config),
- ServerOpts = ?config(server_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+packet_4_active_once_some_big() ->
+ [{doc,"Test packet option {packet, 4} in active once mode"}].
+packet_4_active_once_some_big(Config) when is_list(Config) ->
Data = lists:append(lists:duplicate(100, "1234567890")),
-
- Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, send, [Data, ?SOME]}},
- {options, [{packet, 4}|ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa,
- {?MODULE,
- active_once_packet,
- [Data, ?SOME]}},
- {options, [{active, once},
- {packet, 4} |
- ClientOpts]}]),
-
- ssl_test_lib:check_result(Client, ok),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
+ packet(Config, Data, send_raw, active_once_raw, ?SOME, 4, once).
%%--------------------------------------------------------------------
-packet_raw_active_many_small(doc) ->
- ["Test packet option {packet, raw} in active mode."];
-
-packet_raw_active_many_small(suite) ->
- [];
+packet_raw_active_many_small() ->
+ [{doc,"Test packet option {packet, raw} in active mode."}].
packet_raw_active_many_small(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 = "Packet option is {packet, raw}",
-
- Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, send_raw ,[Data, ?MANY]}},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE, active_raw,
- [Data, ?MANY]}},
- {options, [{active, true},
- {packet, raw} |
- ClientOpts]}]),
-
- ssl_test_lib:check_result(Client, ok),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
+ packet(Config, Data, send_raw, active_raw, ?MANY, raw, active).
%%--------------------------------------------------------------------
-packet_raw_active_some_big(doc) ->
- ["Test packet option {packet, raw} in active mode."];
-
-packet_raw_active_some_big(suite) ->
- [];
+packet_raw_active_some_big() ->
+ [{doc,"Test packet option {packet, raw} in active mode."}].
packet_raw_active_some_big(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:append(lists:duplicate(100, "1234567890")),
-
- Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, send_raw ,[Data, ?SOME]}},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE, active_raw, [Data, ?SOME]}},
- {options, [{active, true},
- {packet, raw} |
- ClientOpts]}]),
-
- ssl_test_lib:check_result(Client, ok),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
+ packet(Config, Data, send_raw, active_raw, ?SOME, raw, active).
%%--------------------------------------------------------------------
-packet_0_active_many_small(doc) ->
- ["Test packet option {packet, 0} in active mode."];
-
-packet_0_active_many_small(suite) ->
- [];
-
-packet_0_active_many_small(Config) when is_list(Config) ->
- ClientOpts = ?config(client_opts, Config),
- ServerOpts = ?config(server_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+packet_0_active_many_small() ->
+ [{doc,"Test packet option {packet, 0} in active mode."}].
+packet_0_active_many_small(Config) when is_list(Config) ->
Data = "Packet option is {packet, 0}",
-
- Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, send_raw ,[Data, ?MANY]}},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa,
- {?MODULE, active_raw,
- [Data, ?MANY]}},
- {options, [{active, true},
- {packet, 0} |
- ClientOpts]}]),
-
- ssl_test_lib:check_result(Client, ok),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
+ packet(Config, Data, send_raw, active_raw, ?MANY, 0, active).
%%--------------------------------------------------------------------
-packet_0_active_some_big(doc) ->
- ["Test packet option {packet, 0} in active mode."];
-
-packet_0_active_some_big(suite) ->
- [];
-
-packet_0_active_some_big(Config) when is_list(Config) ->
- ClientOpts = ?config(client_opts, Config),
- ServerOpts = ?config(server_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+packet_0_active_some_big() ->
+ [{doc,"Test packet option {packet, 0} in active mode."}].
+packet_0_active_some_big(Config) when is_list(Config) ->
Data = lists:append(lists:duplicate(100, "1234567890")),
-
- Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, send_raw ,[Data, ?SOME]}},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa,
- {?MODULE, active_raw,
- [Data, ?SOME]}},
- {options, [{active, true},
- {packet, 0} |
- ClientOpts]}]),
-
- ssl_test_lib:check_result(Client, ok),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
+ packet(Config, Data, send_raw, active_raw, ?SOME, 0, active).
%%--------------------------------------------------------------------
-packet_1_active_many_small(doc) ->
- ["Test packet option {packet, 1} in active mode."];
-
-packet_1_active_many_small(suite) ->
- [];
-
-packet_1_active_many_small(Config) when is_list(Config) ->
- ClientOpts = ?config(client_opts, Config),
- ServerOpts = ?config(server_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+packet_1_active_many_small() ->
+ [{doc,"Test packet option {packet, 1} in active mode."}].
+packet_1_active_many_small(Config) when is_list(Config) ->
Data = "Packet option is {packet, 1}",
-
- Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, send, [Data, ?MANY]}},
- {options, [{packet, 1}|ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa,
- {?MODULE,
- active_packet, [Data, ?MANY]}},
- {options, [{active, true},
- {packet, 1} |
- ClientOpts]}]),
-
- ssl_test_lib:check_result(Client, ok),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
+ packet(Config, Data, send_raw, active_raw, ?MANY, 1, active).
%%--------------------------------------------------------------------
-packet_1_active_some_big(doc) ->
- ["Test packet option {packet, 1} in active mode."];
-
-packet_1_active_some_big(suite) ->
- [];
-
-packet_1_active_some_big(Config) when is_list(Config) ->
- ClientOpts = ?config(client_opts, Config),
- ServerOpts = ?config(server_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+packet_1_active_some_big() ->
+ [{doc,"Test packet option {packet, 1} in active mode."}].
+packet_1_active_some_big(Config) when is_list(Config) ->
Data = lists:append(lists:duplicate(255, "1")),
-
- Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, send, [Data, ?SOME]}},
- {options, [{packet, 1}|ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa,
- {?MODULE,
- active_packet, [Data, ?SOME]}},
- {options, [{active, true},
- {packet, 1} |
- ClientOpts]}]),
-
- ssl_test_lib:check_result(Client, ok),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
+ packet(Config, Data, send_raw, active_raw, ?SOME, 1, active).
%%--------------------------------------------------------------------
-packet_2_active_many_small(doc) ->
- ["Test packet option {packet, 2} in active mode"];
-
-packet_2_active_many_small(suite) ->
- [];
-
-packet_2_active_many_small(Config) when is_list(Config) ->
- ClientOpts = ?config(client_opts, Config),
- ServerOpts = ?config(server_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+packet_2_active_many_small() ->
+ [{doc,"Test packet option {packet, 2} in active mode"}].
+packet_2_active_many_small(Config) when is_list(Config) ->
Data = "Packet option is {packet, 2}",
-
- Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, send, [Data, ?MANY]}},
- {options, [{packet, 2}|ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa,
- {?MODULE,
- active_packet, [Data, ?MANY]}},
- {options, [{active, true},
- {packet, 2} |
- ClientOpts]}]),
-
- ssl_test_lib:check_result(Client, ok),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
+ packet(Config, Data, send_raw, active_raw, ?MANY, 2, active).
%%--------------------------------------------------------------------
-packet_2_active_some_big(doc) ->
- ["Test packet option {packet, 2} in active mode"];
-
-packet_2_active_some_big(suite) ->
- [];
-
-packet_2_active_some_big(Config) when is_list(Config) ->
- ClientOpts = ?config(client_opts, Config),
- ServerOpts = ?config(server_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+packet_2_active_some_big() ->
+ [{doc,"Test packet option {packet, 2} in active mode"}].
+packet_2_active_some_big(Config) when is_list(Config) ->
Data = lists:append(lists:duplicate(100, "1234567890")),
-
- Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, send, [Data, ?SOME]}},
- {options, [{packet, 2}|ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa,
- {?MODULE,
- active_packet, [Data, ?SOME]}},
- {options, [{active, true},
- {packet, 2} |
- ClientOpts]}]),
-
- ssl_test_lib:check_result(Client, ok),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
+ packet(Config, Data, send_raw, active_raw, ?SOME, 2, active).
%%--------------------------------------------------------------------
-packet_4_active_many_small(doc) ->
- ["Test packet option {packet, 4} in active mode"];
-
-packet_4_active_many_small(suite) ->
- [];
-
-packet_4_active_many_small(Config) when is_list(Config) ->
- ClientOpts = ?config(client_opts, Config),
- ServerOpts = ?config(server_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+packet_4_active_many_small() ->
+ [{doc,"Test packet option {packet, 4} in active mode"}].
+packet_4_active_many_small(Config) when is_list(Config) ->
Data = "Packet option is {packet, 4}",
-
- Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, send, [Data, ?MANY]}},
- {options, [{packet, 4}|ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa,
- {?MODULE,
- active_packet, [Data, ?MANY]}},
- {options, [{active, true},
- {packet, 4} |
- ClientOpts]}]),
-
- ssl_test_lib:check_result(Client, ok),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
+ packet(Config, Data, send_raw, active_raw, ?MANY, 4, active).
%%--------------------------------------------------------------------
-packet_4_active_some_big(doc) ->
- ["Test packet option {packet, 4} in active mode"];
-
-packet_4_active_some_big(suite) ->
- [];
-
-packet_4_active_some_big(Config) when is_list(Config) ->
- ClientOpts = ?config(client_opts, Config),
- ServerOpts = ?config(server_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+packet_4_active_some_big() ->
+ [{doc,"Test packet option {packet, 4} in active mode"}].
+packet_4_active_some_big(Config) when is_list(Config) ->
Data = lists:append(lists:duplicate(100, "1234567890")),
-
- Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, send, [Data, ?SOME]}},
- {options, [{packet, 4} | ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa,
- {?MODULE,
- active_packet, [Data, ?SOME]}},
- {options, [{active, true},
- {packet, 4} |
- ClientOpts]}]),
-
- ssl_test_lib:check_result(Client, ok),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
+ packet(Config, Data, send_raw, active_raw, ?SOME, 4, active).
%%--------------------------------------------------------------------
-packet_send_to_large(doc) ->
- ["Test setting the packet option {packet, 2} on the send side"];
-
-packet_send_to_large(suite) -> [];
+packet_send_to_large() ->
+ [{doc,"Test setting the packet option {packet, 2} on the send side"}].
packet_send_to_large(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
@@ -1233,16 +447,9 @@ packet_send_to_large(Config) when is_list(Config) ->
ssl_test_lib:close(Server),
ssl_test_lib:close(Client).
-
-
-
-
%%--------------------------------------------------------------------
-packet_wait_active(doc) ->
- ["Test waiting when complete packages have not arrived"];
-
-packet_wait_active(suite) ->
- [];
+packet_wait_active() ->
+ [{doc,"Test waiting when complete packages have not arrived"}].
packet_wait_active(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
@@ -1274,11 +481,8 @@ packet_wait_active(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
-packet_wait_passive(doc) ->
- ["Test waiting when complete packages have not arrived"];
-
-packet_wait_passive(suite) ->
- [];
+packet_wait_passive() ->
+ [{doc,"Test waiting when complete packages have not arrived"}].
packet_wait_passive(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
@@ -1307,10 +511,8 @@ packet_wait_passive(Config) when is_list(Config) ->
ssl_test_lib:close(Server),
ssl_test_lib:close(Client).
%%--------------------------------------------------------------------
-packet_baddata_active(doc) ->
- ["Test that if a bad packet arrives error msg is sent and socket is closed"];
-packet_baddata_active(suite) ->
- [];
+packet_baddata_active() ->
+ [{doc,"Test that if a bad packet arrives error msg is sent and socket is closed"}].
packet_baddata_active(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
@@ -1335,18 +537,15 @@ packet_baddata_active(Config) when is_list(Config) ->
{Client, {other, {ssl_error, _Socket,
{invalid_packet, _}},{error,closed},1}} -> ok;
Unexpected ->
- test_server:fail({unexpected, Unexpected})
+ ct:fail({unexpected, Unexpected})
end,
ssl_test_lib:close(Server),
ssl_test_lib:close(Client).
%%--------------------------------------------------------------------
-packet_baddata_passive(doc) ->
- ["Test that if a bad packet arrives error msg is sent and socket is closed"];
-
-packet_baddata_passive(suite) ->
- [];
+packet_baddata_passive() ->
+ [{doc,"Test that if a bad packet arrives error msg is sent and socket is closed"}].
packet_baddata_passive(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
@@ -1372,19 +571,16 @@ packet_baddata_passive(Config) when is_list(Config) ->
receive
{Client, {other, {error, {invalid_packet, _}},{error,closed}, 1}} -> ok;
Unexpected ->
- test_server:fail({unexpected, Unexpected})
+ ct:fail({unexpected, Unexpected})
end,
ssl_test_lib:close(Server),
ssl_test_lib:close(Client).
%%--------------------------------------------------------------------
-packet_size_active(doc) ->
- ["Test that if a packet of size larger than
- packet_size arrives error msg is sent and socket is closed"];
-
-packet_size_active(suite) ->
- [];
+packet_size_active() ->
+ [{doc,"Test that if a packet of size larger than
+ packet_size arrives error msg is sent and socket is closed"}].
packet_size_active(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
@@ -1409,17 +605,16 @@ packet_size_active(Config) when is_list(Config) ->
{Client, {other, {ssl_error, _Socket,
{invalid_packet, _}},{error,closed},1}} -> ok;
Unexpected ->
- test_server:fail({unexpected, Unexpected})
+ ct:fail({unexpected, Unexpected})
end,
ssl_test_lib:close(Server),
ssl_test_lib:close(Client).
%%--------------------------------------------------------------------
-packet_size_passive(doc) ->
- ["Test that if a packet of size larger
- than packet_size arrives error msg is sent and socket is closed"];
-packet_size_passive(suite) -> [];
+packet_size_passive() ->
+ [{doc, "Test that if a packet of size larger
+ than packet_size arrives error msg is sent and socket is closed"}].
packet_size_passive(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
@@ -1444,17 +639,15 @@ packet_size_passive(Config) when is_list(Config) ->
receive
{Client, {other, {error, {invalid_packet, _}},{error,closed},1}} -> ok;
Unexpected ->
- test_server:fail({unexpected, Unexpected})
+ ct:fail({unexpected, Unexpected})
end,
ssl_test_lib:close(Server),
ssl_test_lib:close(Client).
%%--------------------------------------------------------------------
-packet_cdr_decode(doc) ->
- ["Test setting the packet option {packet, cdr}, {mode, binary}"];
-packet_cdr_decode(suite) ->
- [];
+packet_cdr_decode() ->
+ [{doc,"Test setting the packet option {packet, cdr}, {mode, binary}"}].
packet_cdr_decode(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
ServerOpts = ?config(server_opts, Config),
@@ -1486,10 +679,8 @@ 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() ->
+ [{doc,"Test setting the packet option {packet, cdr} {mode, list}"}].
packet_cdr_decode_list(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
ServerOpts = ?config(server_opts, Config),
@@ -1521,11 +712,9 @@ packet_cdr_decode_list(Config) when is_list(Config) ->
ssl_test_lib:close(Client).
%%--------------------------------------------------------------------
-packet_http_decode(doc) ->
- ["Test setting the packet option {packet, http} {mode, binary} "
- "(Body will be binary http strings are lists)"];
-packet_http_decode(suite) ->
- [];
+packet_http_decode() ->
+ [{doc, "Test setting the packet option {packet, http} {mode, binary} "
+ "(Body will be binary http strings are lists)"}].
packet_http_decode(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
@@ -1606,11 +795,9 @@ client_http_decode(Socket, HttpRequest) ->
ok.
%%--------------------------------------------------------------------
-packet_http_decode_list(doc) ->
- ["Test setting the packet option {packet, http}, {mode, list}"
- "(Body will be list too)"];
-packet_http_decode_list(suite) ->
- [];
+packet_http_decode_list() ->
+ [{doc, "Test setting the packet option {packet, http}, {mode, list}"
+ "(Body will be list too)"}].
packet_http_decode_list(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
ServerOpts = ?config(server_opts, Config),
@@ -1666,11 +853,8 @@ client_http_decode_list(Socket, HttpRequest) ->
ok.
%%--------------------------------------------------------------------
-packet_http_bin_decode_multi(doc) ->
- ["Test setting the packet option {packet, http_bin} with multiple requests"];
-packet_http_bin_decode_multi(suite) ->
- [];
-
+packet_http_bin_decode_multi() ->
+ [{doc,"Test setting the packet option {packet, http_bin} with multiple requests"}].
packet_http_bin_decode_multi(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
ServerOpts = ?config(server_opts, Config),
@@ -1757,11 +941,10 @@ 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() ->
+ [{doc,"Test setting the packet option {packet, http}, {active, false}"
+ " with a incorrect http header."}].
+
packet_http_error_passive(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
ServerOpts = ?config(server_opts, Config),
@@ -1819,10 +1002,9 @@ server_http_decode_error(Socket, HttpResponse) ->
ok = ssl:send(Socket, HttpResponse),
ok.
%%--------------------------------------------------------------------
-packet_httph_active(doc) ->
- ["Test setting the packet option {packet, httph}"];
-packet_httph_active(suite) ->
- [];
+packet_httph_active() ->
+ [{doc,"Test setting the packet option {packet, httph}"}].
+
packet_httph_active(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
ServerOpts = ?config(server_opts, Config),
@@ -1876,10 +1058,8 @@ client_http_decode_trailer_active(Socket) ->
ok.
%%--------------------------------------------------------------------
-packet_httph_bin_active(doc) ->
- ["Test setting the packet option {packet, httph_bin}"];
-packet_httph_bin_active(suite) ->
- [];
+packet_httph_bin_active() ->
+ [{doc,"Test setting the packet option {packet, httph_bin}"}].
packet_httph_bin_active(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
ServerOpts = ?config(server_opts, Config),
@@ -1927,10 +1107,9 @@ client_http_decode_trailer_bin_active(Socket) ->
end,
ok.
%%--------------------------------------------------------------------
-packet_httph_active_once(doc) ->
- ["Test setting the packet option {packet, httph}"];
-packet_httph_active_once(suite) ->
- [];
+packet_httph_active_once() ->
+ [{doc,"Test setting the packet option {packet, httph}"}].
+
packet_httph_active_once(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
ServerOpts = ?config(server_opts, Config),
@@ -1981,10 +1160,9 @@ client_http_decode_trailer_active_once(Socket) ->
end,
ok.
%%--------------------------------------------------------------------
-packet_httph_bin_active_once(doc) ->
- ["Test setting the packet option {packet, httph_bin}"];
-packet_httph_bin_active_once(suite) ->
- [];
+packet_httph_bin_active_once() ->
+ [{doc,"Test setting the packet option {packet, httph_bin}"}].
+
packet_httph_bin_active_once(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
ServerOpts = ?config(server_opts, Config),
@@ -2036,10 +1214,9 @@ client_http_decode_trailer_bin_active_once(Socket) ->
%%--------------------------------------------------------------------
-packet_httph_passive(doc) ->
- ["Test setting the packet option {packet, httph}"];
-packet_httph_passive(suite) ->
- [];
+packet_httph_passive() ->
+ [{doc,"Test setting the packet option {packet, httph}"}].
+
packet_httph_passive(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
ServerOpts = ?config(server_opts, Config),
@@ -2077,10 +1254,9 @@ client_http_decode_trailer_passive(Socket) ->
ok.
%%--------------------------------------------------------------------
-packet_httph_bin_passive(doc) ->
- ["Test setting the packet option {packet, httph_bin}"];
-packet_httph_bin_passive(suite) ->
- [];
+packet_httph_bin_passive() ->
+ [{doc,"Test setting the packet option {packet, httph_bin}"}].
+
packet_httph_bin_passive(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
ServerOpts = ?config(server_opts, Config),
@@ -2118,10 +1294,9 @@ client_http_decode_trailer_bin_passive(Socket) ->
ok.
%%--------------------------------------------------------------------
-packet_line_decode(doc) ->
- ["Test setting the packet option {packet, line}, {mode, binary}"];
-packet_line_decode(suite) ->
- [];
+packet_line_decode() ->
+ [{doc,"Test setting the packet option {packet, line}, {mode, binary}"}].
+
packet_line_decode(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
ServerOpts = ?config(server_opts, Config),
@@ -2155,10 +1330,9 @@ packet_line_decode(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
-packet_line_decode_list(doc) ->
- ["Test setting the packet option {packet, line}, {mode, list}"];
-packet_line_decode_list(suite) ->
- [];
+packet_line_decode_list() ->
+ [{doc,"Test setting the packet option {packet, line}, {mode, list}"}].
+
packet_line_decode_list(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
ServerOpts = ?config(server_opts, Config),
@@ -2194,10 +1368,9 @@ packet_line_decode_list(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
-packet_asn1_decode(doc) ->
- ["Test setting the packet option {packet, asn1}"];
-packet_asn1_decode(suite) ->
- [];
+packet_asn1_decode() ->
+ [{doc,"Test setting the packet option {packet, asn1}"}].
+
packet_asn1_decode(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
ServerOpts = ?config(server_opts, Config),
@@ -2230,10 +1403,9 @@ 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() ->
+ [{doc,"Test setting the packet option {packet, asn1}"}].
+
packet_asn1_decode_list(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
ServerOpts = ?config(server_opts, Config),
@@ -2268,10 +1440,9 @@ packet_asn1_decode_list(Config) when is_list(Config) ->
ssl_test_lib:close(Client).
%%--------------------------------------------------------------------
-packet_tpkt_decode(doc) ->
- ["Test setting the packet option {packet, tpkt}"];
-packet_tpkt_decode(suite) ->
- [];
+packet_tpkt_decode() ->
+ [{doc,"Test setting the packet option {packet, tpkt}"}].
+
packet_tpkt_decode(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
ServerOpts = ?config(server_opts, Config),
@@ -2301,10 +1472,9 @@ 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() ->
+ [{doc,"Test setting the packet option {packet, tpkt}"}].
+
packet_tpkt_decode_list(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
ServerOpts = ?config(server_opts, Config),
@@ -2335,10 +1505,9 @@ packet_tpkt_decode_list(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
-%% packet_fcgi_decode(doc) ->
-%% ["Test setting the packet option {packet, fcgi}"];
-%% packet_fcgi_decode(suite) ->
-%% [];
+%% packet_fcgi_decode() ->
+%% [{doc,"Test setting the packet option {packet, fcgi}"}].
+
%% packet_fcgi_decode(Config) when is_list(Config) ->
%% ClientOpts = ?config(client_opts, Config),
%% ServerOpts = ?config(server_opts, Config),
@@ -2370,10 +1539,8 @@ packet_tpkt_decode_list(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
-packet_sunrm_decode(doc) ->
- ["Test setting the packet option {packet, sunrm}"];
-packet_sunrm_decode(suite) ->
- [];
+packet_sunrm_decode() ->
+ [{doc,"Test setting the packet option {packet, sunrm}"}].
packet_sunrm_decode(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
ServerOpts = ?config(server_opts, Config),
@@ -2403,10 +1570,9 @@ packet_sunrm_decode(Config) when is_list(Config) ->
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() ->
+ [{doc,"Test setting the packet option {packet, sunrm}"}].
+
packet_sunrm_decode_list(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
ServerOpts = ?config(server_opts, Config),
@@ -2436,11 +1602,10 @@ packet_sunrm_decode_list(Config) when is_list(Config) ->
ssl_test_lib:close(Client).
%%--------------------------------------------------------------------
-header_decode_one_byte(doc) ->
- ["Test setting the packet option {header, 1}"];
-header_decode_one_byte(suite) ->
- [];
-header_decode_one_byte(Config) when is_list(Config) ->
+header_decode_one_byte_active() ->
+ [{doc,"Test setting the packet option {header, 1}"}].
+
+header_decode_one_byte_active(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
ServerOpts = ?config(server_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
@@ -2449,7 +1614,7 @@ header_decode_one_byte(Config) when is_list(Config) ->
Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
{from, self()},
- {mfa, {?MODULE, server_header_decode,
+ {mfa, {?MODULE, server_header_decode_active,
[Data, [11 | <<"Hello world">>]]}},
{options, [{active, true}, binary,
{header,1}|ServerOpts]}]),
@@ -2458,7 +1623,7 @@ header_decode_one_byte(Config) when is_list(Config) ->
Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
{host, Hostname},
{from, self()},
- {mfa, {?MODULE, client_header_decode,
+ {mfa, {?MODULE, client_header_decode_active,
[Data, [11 | <<"Hello world">> ]]}},
{options, [{active, true}, {header, 1},
binary | ClientOpts]}]),
@@ -2470,11 +1635,10 @@ header_decode_one_byte(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
-header_decode_two_bytes(doc) ->
- ["Test setting the packet option {header, 2}"];
-header_decode_two_bytes(suite) ->
- [];
-header_decode_two_bytes(Config) when is_list(Config) ->
+header_decode_two_bytes_active() ->
+ [{doc,"Test setting the packet option {header, 2}"}].
+
+header_decode_two_bytes_active(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
ServerOpts = ?config(server_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
@@ -2483,7 +1647,7 @@ header_decode_two_bytes(Config) when is_list(Config) ->
Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
{from, self()},
- {mfa, {?MODULE, server_header_decode,
+ {mfa, {?MODULE, server_header_decode_active,
[Data, [11, $H | <<"ello world">> ]]}},
{options, [{active, true}, binary,
{header,2}|ServerOpts]}]),
@@ -2492,7 +1656,7 @@ header_decode_two_bytes(Config) when is_list(Config) ->
Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
{host, Hostname},
{from, self()},
- {mfa, {?MODULE, client_header_decode,
+ {mfa, {?MODULE, client_header_decode_active,
[Data, [11, $H | <<"ello world">> ]]}},
{options, [{active, true}, {header, 2},
binary | ClientOpts]}]),
@@ -2505,11 +1669,10 @@ header_decode_two_bytes(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
-header_decode_two_bytes_two_sent(doc) ->
- ["Test setting the packet option {header, 2} and sending on byte"];
-header_decode_two_bytes_two_sent(suite) ->
- [];
-header_decode_two_bytes_two_sent(Config) when is_list(Config) ->
+header_decode_two_bytes_two_sent_active() ->
+ [{doc,"Test setting the packet option {header, 2} and sending two byte"}].
+
+header_decode_two_bytes_two_sent_active(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
ServerOpts = ?config(server_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
@@ -2518,8 +1681,8 @@ header_decode_two_bytes_two_sent(Config) when is_list(Config) ->
Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
{from, self()},
- {mfa, {?MODULE, server_header_decode,
- [Data, [$H, $e | <<>> ]]}},
+ {mfa, {?MODULE, server_header_decode_active,
+ [Data, [$H, $e]]}},
{options, [{active, true}, binary,
{header,2}|ServerOpts]}]),
@@ -2527,8 +1690,8 @@ header_decode_two_bytes_two_sent(Config) when is_list(Config) ->
Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
{host, Hostname},
{from, self()},
- {mfa, {?MODULE, client_header_decode,
- [Data, [$H, $e | <<>> ]]}},
+ {mfa, {?MODULE, client_header_decode_active,
+ [Data, [$H, $e]]}},
{options, [{active, true}, {header, 2},
binary | ClientOpts]}]),
@@ -2540,11 +1703,10 @@ header_decode_two_bytes_two_sent(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
-header_decode_two_bytes_one_sent(doc) ->
- ["Test setting the packet option {header, 2} and sending on byte"];
-header_decode_two_bytes_one_sent(suite) ->
- [];
-header_decode_two_bytes_one_sent(Config) when is_list(Config) ->
+header_decode_two_bytes_one_sent_active() ->
+ [{doc,"Test setting the packet option {header, 2} and sending one byte"}].
+
+header_decode_two_bytes_one_sent_active(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
ServerOpts = ?config(server_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
@@ -2553,7 +1715,7 @@ header_decode_two_bytes_one_sent(Config) when is_list(Config) ->
Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
{from, self()},
- {mfa, {?MODULE, server_header_decode,
+ {mfa, {?MODULE, server_header_decode_active,
[Data, "H"]}},
{options, [{active, true}, binary,
{header,2}|ServerOpts]}]),
@@ -2562,7 +1724,7 @@ header_decode_two_bytes_one_sent(Config) when is_list(Config) ->
Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
{host, Hostname},
{from, self()},
- {mfa, {?MODULE, client_header_decode,
+ {mfa, {?MODULE, client_header_decode_active,
[Data, "H"]}},
{options, [{active, true}, {header, 2},
binary | ClientOpts]}]),
@@ -2572,9 +1734,165 @@ header_decode_two_bytes_one_sent(Config) when is_list(Config) ->
ssl_test_lib:close(Server),
ssl_test_lib:close(Client).
+%%--------------------------------------------------------------------
+
+header_decode_one_byte_passive() ->
+ [{doc,"Test setting the packet option {header, 1}"}].
+
+header_decode_one_byte_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),
+
+ Data = <<11:8, "Hello world">>,
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, server_header_decode_passive,
+ [Data, [11 | <<"Hello world">>]]}},
+ {options, [{active, false}, binary,
+ {header,1}|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_header_decode_passive,
+ [Data, [11 | <<"Hello world">> ]]}},
+ {options, [{active, false}, {header, 1},
+ binary | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
%%--------------------------------------------------------------------
-%% Internal functions
+
+header_decode_two_bytes_passive() ->
+ [{doc,"Test setting the packet option {header, 2}"}].
+
+header_decode_two_bytes_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),
+
+ Data = <<11:8, "Hello world">>,
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, server_header_decode_passive,
+ [Data, [11, $H | <<"ello world">> ]]}},
+ {options, [{active, false}, binary,
+ {header,2}|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_header_decode_passive,
+ [Data, [11, $H | <<"ello world">> ]]}},
+ {options, [{active, false}, {header, 2},
+ binary | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+
+%%--------------------------------------------------------------------
+
+header_decode_two_bytes_two_sent_passive() ->
+ [{doc,"Test setting the packet option {header, 2} and sending two byte"}].
+
+header_decode_two_bytes_two_sent_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),
+
+ Data = <<"He">>,
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, server_header_decode_passive,
+ [Data, [$H, $e]]}},
+ {options, [{active, false}, binary,
+ {header,2}|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_header_decode_passive,
+ [Data, [$H, $e]]}},
+ {options, [{active, false}, {header, 2},
+ binary | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+
+%%--------------------------------------------------------------------
+
+header_decode_two_bytes_one_sent_passive() ->
+ [{doc,"Test setting the packet option {header, 2} and sending one byte"}].
+
+header_decode_two_bytes_one_sent_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),
+
+ Data = <<"H">>,
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, server_header_decode_passive,
+ [Data, "H"]}},
+ {options, [{active, false}, binary,
+ {header,2}|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_header_decode_passive,
+ [Data, "H"]}},
+ {options, [{active, false}, {header, 2},
+ binary | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+%% Internal functions ------------------------------------------------
+%%--------------------------------------------------------------------
+packet(Config, Data, Send, Recv, Quantity, Packet, Active) ->
+ 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, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, Send ,[Data, Quantity]}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, Recv, [Data, Quantity]}},
+ {options, [{active, Active},
+ {packet, Packet} |
+ ClientOpts]}]),
+
+ ssl_test_lib:check_result(Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
send_raw(Socket,_, 0) ->
ssl:send(Socket, <<>>),
@@ -2745,7 +2063,7 @@ 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]),
+ ct:print("Packet: ~p ~n", [Packet]),
ok = ssl:send(Socket, P1),
ok = ssl:send(Socket, P2),
receive
@@ -2758,29 +2076,52 @@ client_packet_decode(Socket, P1, P2, Packet) ->
Other2 -> exit({?LINE, Other2})
end.
-server_header_decode(Socket, Packet, Result) ->
+server_header_decode_active(Socket, Packet, Result) ->
receive
- {ssl, Socket, Result} -> ok;
- Other1 -> exit({?LINE, Other1})
- end,
- ok = ssl:send(Socket, Packet),
- receive
- {ssl, Socket, Result} -> ok;
- Other2 -> exit({?LINE, Other2})
+ {ssl, Socket, Result} ->
+ ok;
+ {ssl, Socket, Other1} ->
+ check_header_result(Result, Other1)
end,
ok = ssl:send(Socket, Packet).
-client_header_decode(Socket, Packet, Result) ->
+client_header_decode_active(Socket, Packet, Result) ->
ok = ssl:send(Socket, Packet),
receive
- {ssl, Socket, Result} -> ok;
- Other1 -> exit({?LINE, Other1})
+ {ssl, Socket, Result} ->
+ ok;
+ {ssl, Socket, Other1} ->
+ check_header_result(Result, Other1)
+ end.
+
+server_header_decode_passive(Socket, Packet, Result) ->
+ case ssl:recv(Socket, 0) of
+ {ok, Result} ->
+ ok;
+ {ok, Other} ->
+ check_header_result(Result, Other)
end,
+ ok = ssl:send(Socket, Packet).
+
+client_header_decode_passive(Socket, Packet, Result) ->
ok = ssl:send(Socket, Packet),
- receive
- {ssl, Socket, Result} -> ok;
- Other2 -> exit({?LINE, Other2})
+
+ case ssl:recv(Socket, 0) of
+ {ok, Result} ->
+ ok;
+ {ok, Other} ->
+ check_header_result(Result, Other)
end.
+
+%% The inet header option is a broken option as it does not buffer until it gets enough data.
+%% This check only checks that it has the same behavior as inet, but it is a quite useless
+%% option and the bitsynax makes it obsolete!
+check_header_result([Byte1 | _], [Byte1]) ->
+ ok;
+check_header_result([Byte1, Byte2 | _], [Byte1, Byte2]) ->
+ ok;
+check_header_result(_,Got) ->
+ exit({?LINE, Got}).
server_line_packet_decode(Socket, Packet) when is_binary(Packet) ->
[L1, L2] = string:tokens(binary_to_list(Packet), "\n"),
diff --git a/lib/ssl/test/ssl_payload_SUITE.erl b/lib/ssl/test/ssl_payload_SUITE.erl
index 02b5516e35..77ad546420 100644
--- a/lib/ssl/test/ssl_payload_SUITE.erl
+++ b/lib/ssl/test/ssl_payload_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -26,16 +26,47 @@
-define(TIMEOUT, 600000).
-%% 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.
+%% Common Test interface functions -----------------------------------
%%--------------------------------------------------------------------
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [
+ {group, 'tlsv1.2'},
+ {group, 'tlsv1.1'},
+ {group, 'tlsv1'},
+ {group, 'sslv3'}
+ ].
+
+groups() ->
+ [
+ {'tlsv1.2', [], payload_tests()},
+ {'tlsv1.1', [], payload_tests()},
+ {'tlsv1', [], payload_tests()},
+ {'sslv3', [], payload_tests()}
+ ].
+
+payload_tests() ->
+ [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].
+
init_per_suite(Config) ->
catch crypto:stop(),
try crypto:start() of
@@ -47,92 +78,43 @@ init_per_suite(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(_TestCase, Config0) ->
- Config = lists:keydelete(watchdog, 1, Config0),
- Dog = ssl_test_lib: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;
+init_per_group(GroupName, Config) ->
+ case ssl_test_lib:is_tls_version(GroupName) of
+ true ->
+ case ssl_test_lib:sufficient_crypto_support(GroupName) of
+ true ->
+ ssl_test_lib:init_tls_version(GroupName),
+ Config;
+ false ->
+ {skip, "Missing crypto support"}
+ end;
_ ->
- test_server:timetrap_cancel(Dog)
+ ssl:start(),
+ Config
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() ->
- [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.
+init_per_testcase(_TestCase, Config0) ->
+ Config = lists:keydelete(watchdog, 1, Config0),
+ Dog = ct:timetrap(?TIMEOUT),
+ [{watchdog, Dog} | Config].
-%% Test cases starts here.
+end_per_testcase(_TestCase, Config) ->
+ Config.
+%%--------------------------------------------------------------------
+%% Test Cases --------------------------------------------------------
%%--------------------------------------------------------------------
-server_echos_passive_small(doc) ->
- ["Client sends 1000 bytes in passive mode to server, that receives them, "
- "sends them back, and closes."];
-server_echos_passive_small(suite) ->
- [];
+server_echos_passive_small() ->
+ [{doc, "Client sends 1000 bytes in passive mode to server, that receives them, "
+ "sends them back, and closes."}].
server_echos_passive_small(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
@@ -146,12 +128,9 @@ server_echos_passive_small(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
-server_echos_active_once_small(doc) ->
- ["Client sends 1000 bytes in active once mode to server, that receives "
- " them, sends them back, and closes."];
-
-server_echos_active_once_small(suite) ->
- [];
+server_echos_active_once_small() ->
+ [{doc, "Client sends 1000 bytes in active once mode to server, that receives "
+ " them, sends them back, and closes."}].
server_echos_active_once_small(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
@@ -165,12 +144,9 @@ server_echos_active_once_small(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
-server_echos_active_small(doc) ->
- ["Client sends 1000 bytes in active mode to server, that receives them, "
- "sends them back, and closes."];
-
-server_echos_active_small(suite) ->
- [];
+server_echos_active_small() ->
+ [{doc, "Client sends 1000 bytes in active mode to server, that receives them, "
+ "sends them back, and closes."}].
server_echos_active_small(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
@@ -183,12 +159,9 @@ server_echos_active_small(Config) when is_list(Config) ->
ClientNode, ServerNode, Hostname).
%%--------------------------------------------------------------------
-client_echos_passive_small(doc) ->
- ["Server sends 1000 bytes in passive mode to client, that receives them, "
- "sends them back, and closes."];
-
-client_echos_passive_small(suite) ->
- [];
+client_echos_passive_small() ->
+ [{doc, "Server sends 1000 bytes in passive mode to client, that receives them, "
+ "sends them back, and closes."}].
client_echos_passive_small(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
@@ -201,12 +174,9 @@ client_echos_passive_small(Config) when is_list(Config) ->
ServerNode, Hostname).
%%--------------------------------------------------------------------
-client_echos_active_once_small(doc) ->
+client_echos_active_once_small() ->
["Server sends 1000 bytes in active once mode to client, that receives "
- "them, sends them back, and closes."];
-
-client_echos_active_once_small(suite) ->
- [];
+ "them, sends them back, and closes."].
client_echos_active_once_small(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
@@ -219,15 +189,12 @@ client_echos_active_once_small(Config) when is_list(Config) ->
ServerNode, Hostname).
%%--------------------------------------------------------------------
-client_echos_active_small(doc) ->
- ["Server sends 1000 bytes in active mode to client, that receives them, "
- "sends them back, and closes."];
-
-client_echos_active_small(suite) ->
- [];
+client_echos_active_small() ->
+ [{doc, "Server sends 1000 bytes in active mode to client, that receives them, "
+ "sends them back, and closes."}].
client_echos_active_small(Config) when is_list(Config) ->
- ClientOpts = ?config(client_opts, Config),
+ ClientOpts = ?config(client_opts, Config),
ServerOpts = ?config(server_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
@@ -238,12 +205,9 @@ client_echos_active_small(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
-server_echos_passive_big(doc) ->
- ["Client sends 50000 bytes to server in passive mode, that receives them, "
- "sends them back, and closes."];
-
-server_echos_passive_big(suite) ->
- [];
+server_echos_passive_big() ->
+ [{doc, "Client sends 50000 bytes to server in passive mode, that receives them, "
+ "sends them back, and closes."}].
server_echos_passive_big(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
@@ -257,15 +221,12 @@ server_echos_passive_big(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
-server_echos_active_once_big(doc) ->
- ["Client sends 50000 bytes to server in active once mode, that receives "
- "them, sends them back, and closes."];
-
-server_echos_active_once_big(suite) ->
- [];
+server_echos_active_once_big() ->
+ [{doc,"Client sends 50000 bytes to server in active once mode, that receives "
+ "them, sends them back, and closes."}].
server_echos_active_once_big(Config) when is_list(Config) ->
- ClientOpts = ?config(client_opts, Config),
+ ClientOpts = ?config(client_opts, Config),
ServerOpts = ?config(server_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
@@ -276,12 +237,9 @@ server_echos_active_once_big(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
-server_echos_active_big(doc) ->
- ["Client sends 50000 bytes to server in active once mode, that receives "
- " them, sends them back, and closes."];
-
-server_echos_active_big(suite) ->
- [];
+server_echos_active_big() ->
+ [{doc, "Client sends 50000 bytes to server in active once mode, that receives "
+ " them, sends them back, and closes."}].
server_echos_active_big(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
@@ -294,12 +252,9 @@ server_echos_active_big(Config) when is_list(Config) ->
ServerNode, Hostname).
%%--------------------------------------------------------------------
-client_echos_passive_big(doc) ->
- ["Server sends 50000 bytes to client in passive mode, that receives them, "
- "sends them back, and closes."];
-
-client_echos_passive_big(suite) ->
- [];
+client_echos_passive_big() ->
+ [{doc, "Server sends 50000 bytes to client in passive mode, that receives them, "
+ "sends them back, and closes."}].
client_echos_passive_big(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
@@ -312,12 +267,9 @@ client_echos_passive_big(Config) when is_list(Config) ->
ServerNode, Hostname).
%%--------------------------------------------------------------------
-client_echos_active_once_big(doc) ->
- ["Server sends 50000 bytes to client in active once mode, that receives"
- " them, sends them back, and closes."];
-
-client_echos_active_once_big(suite) ->
- [];
+client_echos_active_once_big() ->
+ [{doc, "Server sends 50000 bytes to client in active once mode, that receives"
+ " them, sends them back, and closes."}].
client_echos_active_once_big(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
@@ -330,12 +282,9 @@ client_echos_active_once_big(Config) when is_list(Config) ->
ServerNode, Hostname).
%%--------------------------------------------------------------------
-client_echos_active_big(doc) ->
- ["Server sends 50000 bytes to client in active mode, that receives them, "
- "sends them back, and closes."];
-
-client_echos_active_big(suite) ->
- [];
+client_echos_active_big() ->
+ [{doc, "Server sends 50000 bytes to client in active mode, that receives them, "
+ "sends them back, and closes."}].
client_echos_active_big(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
@@ -348,12 +297,9 @@ client_echos_active_big(Config) when is_list(Config) ->
ServerNode, Hostname).
%%--------------------------------------------------------------------
-server_echos_passive_huge(doc) ->
- ["Client sends 500000 bytes to server in passive mode, that receives "
- " them, sends them back, and closes."];
-
-server_echos_passive_huge(suite) ->
- [];
+server_echos_passive_huge() ->
+ [{doc, "Client sends 500000 bytes to server in passive mode, that receives "
+ " them, sends them back, and closes."}].
server_echos_passive_huge(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
@@ -366,12 +312,9 @@ server_echos_passive_huge(Config) when is_list(Config) ->
ServerNode, Hostname).
%%--------------------------------------------------------------------
-server_echos_active_once_huge(doc) ->
- ["Client sends 500000 bytes to server in active once mode, that receives "
- "them, sends them back, and closes."];
-
-server_echos_active_once_huge(suite) ->
- [];
+server_echos_active_once_huge() ->
+ [{doc, "Client sends 500000 bytes to server in active once mode, that receives "
+ "them, sends them back, and closes."}].
server_echos_active_once_huge(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
@@ -384,12 +327,9 @@ server_echos_active_once_huge(Config) when is_list(Config) ->
ServerNode, Hostname).
%%--------------------------------------------------------------------
-server_echos_active_huge(doc) ->
- ["Client sends 500000 bytes to server in active mode, that receives them, "
- "sends them back, and closes."];
-
-server_echos_active_huge(suite) ->
- [];
+server_echos_active_huge() ->
+ [{doc, "Client sends 500000 bytes to server in active mode, that receives them, "
+ "sends them back, and closes."}].
server_echos_active_huge(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
@@ -402,12 +342,9 @@ server_echos_active_huge(Config) when is_list(Config) ->
ServerNode, Hostname).
%%--------------------------------------------------------------------
-client_echos_passive_huge(doc) ->
- ["Server sends 500000 bytes to client in passive mode, that receives "
- "them, sends them back, and closes."];
-
-client_echos_passive_huge(suite) ->
- [];
+client_echos_passive_huge() ->
+ [{doc, "Server sends 500000 bytes to client in passive mode, that receives "
+ "them, sends them back, and closes."}].
client_echos_passive_huge(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
@@ -419,12 +356,9 @@ client_echos_passive_huge(Config) when is_list(Config) ->
ServerNode, Hostname).
%%--------------------------------------------------------------------
-client_echos_active_once_huge(doc) ->
- ["Server sends 500000 bytes to client in active once mode, that receives "
- "them, sends them back, and closes."];
-
-client_echos_active_once_huge(suite) ->
- [];
+client_echos_active_once_huge() ->
+ [{doc, "Server sends 500000 bytes to client in active once mode, that receives "
+ "them, sends them back, and closes."}].
client_echos_active_once_huge(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
@@ -436,12 +370,9 @@ client_echos_active_once_huge(Config) when is_list(Config) ->
ServerNode, Hostname).
%%--------------------------------------------------------------------
-client_echos_active_huge(doc) ->
- ["Server sends 500000 bytes to client in active mode, that receives them, "
- "sends them back, and closes."];
-
-client_echos_active_huge(suite) ->
- [];
+client_echos_active_huge() ->
+ [{doc, "Server sends 500000 bytes to client in active mode, that receives them, "
+ "sends them back, and closes."}].
client_echos_active_huge(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
@@ -453,6 +384,8 @@ client_echos_active_huge(Config) when is_list(Config) ->
ServerNode, Hostname).
%%--------------------------------------------------------------------
+%% Internal functions ------------------------------------------------
+%%--------------------------------------------------------------------
server_echos_passive(Data, Length, ClientOpts, ServerOpts,
ClientNode, ServerNode, Hostname) ->
@@ -623,33 +556,33 @@ send(Socket, Data, Size, Repeate,F) ->
sender(Socket, Data, Size) ->
ok = send(Socket, Data, Size, 100, fun() -> do_recv(Socket, Data, Size, <<>>, false) end),
- test_server:format("Sender recv: ~p~n", [ssl:getopts(Socket, [active])]),
+ ct:print("Sender recv: ~p~n", [ssl:getopts(Socket, [active])]),
ok.
sender_once(Socket, Data, Size) ->
send(Socket, Data, Size, 100,
fun() -> do_active_once(Socket, Data, Size, <<>>, false) end),
- test_server:format("Sender active once: ~p~n",
+ ct:print("Sender active once: ~p~n",
[ssl:getopts(Socket, [active])]),
ok.
sender_active(Socket, Data, Size) ->
F = fun() -> do_active(Socket, Data, Size, <<>>, false) end,
send(Socket, Data, Size, 100, F),
- test_server:format("Sender active: ~p~n", [ssl:getopts(Socket, [active])]),
+ ct:print("Sender active: ~p~n", [ssl:getopts(Socket, [active])]),
ok.
echoer(Socket, Data, Size) ->
- test_server:format("Echoer recv: ~p~n", [ssl:getopts(Socket, [active])]),
+ ct:print("Echoer recv: ~p~n", [ssl:getopts(Socket, [active])]),
echo(fun() -> do_recv(Socket, Data, Size, <<>>, true) end, 100).
echoer_once(Socket, Data, Size) ->
- test_server:format("Echoer active once: ~p ~n",
+ ct:print("Echoer active once: ~p ~n",
[ssl:getopts(Socket, [active])]),
echo(fun() -> do_active_once(Socket, Data, Size, <<>>, true) end, 100).
echoer_active(Socket, Data, Size) ->
- test_server:format("Echoer active: ~p~n", [ssl:getopts(Socket, [active])]),
+ ct:print("Echoer active: ~p~n", [ssl:getopts(Socket, [active])]),
echo(fun() -> do_active(Socket, Data, Size, <<>>, true) end, 100).
echo(_Fun, 0) -> ok;
diff --git a/lib/ssl/test/ssl_session_cache_SUITE.erl b/lib/ssl/test/ssl_session_cache_SUITE.erl
index 6d758ecb01..fd9a0a594c 100644
--- a/lib/ssl/test/ssl_session_cache_SUITE.erl
+++ b/lib/ssl/test/ssl_session_cache_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -37,18 +37,22 @@
-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.
+%% Common Test interface functions -----------------------------------
%%--------------------------------------------------------------------
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [session_cleanup,
+ session_cache_process_list,
+ session_cache_process_mnesia].
+
+groups() ->
+ [].
+
init_per_suite(Config0) ->
- Dog = ssl_test_lib:timetrap(?LONG_TIMEOUT *2),
+ Dog = ct:timetrap(?LONG_TIMEOUT *2),
catch crypto:stop(),
try crypto:start() of
ok ->
@@ -59,7 +63,7 @@ init_per_suite(Config0) ->
Result =
(catch make_certs:all(?config(data_dir, Config0),
?config(priv_dir, Config0))),
- test_server:format("Make certs ~p~n", [Result]),
+ ct:print("Make certs ~p~n", [Result]),
Config1 = ssl_test_lib:make_dsa_cert(Config0),
Config = ssl_test_lib:cert_options(Config1),
@@ -68,29 +72,16 @@ init_per_suite(Config0) ->
{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_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
init_per_testcase(session_cache_process_list, Config) ->
init_customized_session_cache(list, Config);
@@ -100,7 +91,7 @@ init_per_testcase(session_cache_process_mnesia, Config) ->
init_per_testcase(session_cleanup, Config0) ->
Config = lists:keydelete(watchdog, 1, Config0),
- Dog = test_server:timetrap(?TIMEOUT),
+ Dog = ct:timetrap(?TIMEOUT),
ssl:stop(),
application:load(ssl),
application:set_env(ssl, session_lifetime, 5),
@@ -110,12 +101,12 @@ init_per_testcase(session_cleanup, Config0) ->
init_per_testcase(_TestCase, Config0) ->
Config = lists:keydelete(watchdog, 1, Config0),
- Dog = test_server:timetrap(?TIMEOUT),
+ Dog = ct:timetrap(?TIMEOUT),
[{watchdog, Dog} | Config].
init_customized_session_cache(Type, Config0) ->
Config = lists:keydelete(watchdog, 1, Config0),
- Dog = test_server:timetrap(?TIMEOUT),
+ Dog = ct:timetrap(?TIMEOUT),
ssl:stop(),
application:load(ssl),
application:set_env(ssl, session_cb, ?MODULE),
@@ -123,14 +114,6 @@ init_customized_session_cache(Type, Config0) ->
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);
@@ -146,43 +129,14 @@ end_per_testcase(session_cleanup, Config) ->
application:unset_env(ssl, session_lifetime),
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_cleanup,
- session_cache_process_list,
- session_cache_process_mnesia].
-
-groups() ->
- [].
-
-init_per_group(_GroupName, Config) ->
Config.
-end_per_group(_GroupName, Config) ->
- Config.
%%--------------------------------------------------------------------
-session_cleanup(doc) ->
- ["Test that sessions are cleand up eventually, so that the session table "
- "does not grow and grow ..."];
-session_cleanup(suite) ->
- [];
+%% Test Cases --------------------------------------------------------
+%%--------------------------------------------------------------------
+session_cleanup() ->
+ [{doc, "Test that sessions are cleand up eventually, so that the session table "
+ "does not grow and grow ..."}].
session_cleanup(Config)when is_list(Config) ->
process_flag(trap_exit, true),
ClientOpts = ?config(client_opts, Config),
@@ -207,7 +161,7 @@ session_cleanup(Config)when is_list(Config) ->
end,
%% Make sure session is registered
- test_server:sleep(?SLEEP),
+ ct:sleep(?SLEEP),
{status, _, _, StatusInfo} = sys:get_status(whereis(ssl_manager)),
[_, _,_, _, Prop] = StatusInfo,
@@ -224,14 +178,14 @@ session_cleanup(Config)when is_list(Config) ->
%% Make sure session has expired and been cleaned up
check_timer(SessionTimer),
- test_server:sleep(?DELAY *2), %% Delay time + some extra time
+ ct:sleep(?DELAY *2), %% Delay time + some extra time
{ServerDelayTimer, ClientDelayTimer} = get_delay_timers(),
check_timer(ServerDelayTimer),
check_timer(ClientDelayTimer),
- test_server:sleep(?SLEEP), %% Make sure clean has had time to run
+ ct:sleep(?SLEEP), %% Make sure clean has had time to run
undefined = ssl_session_cache:lookup(Cache, {{Hostname, Port}, Id}),
undefined = ssl_session_cache:lookup(Cache, {Port, Id}),
@@ -248,7 +202,7 @@ check_timer(Timer) ->
{status, _, _, _} = sys:get_status(whereis(ssl_manager)),
ok;
Int ->
- test_server:sleep(Int),
+ ct:sleep(Int),
check_timer(Timer)
end.
@@ -258,31 +212,25 @@ get_delay_timers() ->
State = ssl_test_lib:state(Prop),
case element(7, State) of
{undefined, undefined} ->
- test_server:sleep(?SLEEP),
+ ct:sleep(?SLEEP),
get_delay_timers();
{undefined, _} ->
- test_server:sleep(?SLEEP),
+ ct:sleep(?SLEEP),
get_delay_timers();
{_, undefined} ->
- test_server:sleep(?SLEEP),
+ ct:sleep(?SLEEP),
get_delay_timers();
DelayTimers ->
DelayTimers
end.
%%--------------------------------------------------------------------
-session_cache_process_list(doc) ->
- ["Test reuse of sessions (short handshake)"];
-
-session_cache_process_list(suite) ->
- [];
+session_cache_process_list() ->
+ [{doc,"Test reuse of sessions (short handshake)"}].
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() ->
+ [{doc,"Test reuse of sessions (short handshake)"}].
session_cache_process_mnesia(Config) when is_list(Config) ->
session_cache_process(mnesia,Config).
diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl
index fa8a1826f2..8d96a70a6e 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-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -20,8 +20,7 @@
%%
-module(ssl_test_lib).
--include("test_server.hrl").
--include("test_server_line.hrl").
+-include_lib("common_test/include/ct.hrl").
-include_lib("public_key/include/public_key.hrl").
%% Note: This directive should only be used in test suites.
@@ -29,12 +28,6 @@
-record(sslsocket, { fd = nil, pid = nil}).
-timetrap(Time) ->
- Mul = try
- test_server:timetrap_scale_factor()
- catch _:_ -> 1 end,
- test_server:timetrap(1000+Time*Mul).
-
%% For now always run locally
run_where(_) ->
ClientNode = node(),
@@ -65,24 +58,32 @@ run_server(Opts) ->
Port = proplists:get_value(port, Opts),
Options = proplists:get_value(options, Opts),
Pid = proplists:get_value(from, Opts),
- test_server:format("ssl:listen(~p, ~p)~n", [Port, Options]),
- {ok, ListenSocket} = rpc:call(Node, ssl, listen, [Port, Options]),
+ Transport = proplists:get_value(transport, Opts, ssl),
+ ct:print("ssl:listen(~p, ~p)~n", [Port, Options]),
+ {ok, ListenSocket} = rpc:call(Node, Transport, listen, [Port, Options]),
Pid ! {listen, up},
send_selected_port(Pid, Port, ListenSocket),
run_server(ListenSocket, Opts).
run_server(ListenSocket, Opts) ->
- AcceptSocket = connect(ListenSocket, Opts),
+ do_run_server(ListenSocket, connect(ListenSocket, Opts), Opts).
+
+do_run_server(_, {error, timeout} = Result, Opts) ->
+ Pid = proplists:get_value(from, Opts),
+ Pid ! {self(), Result};
+
+do_run_server(ListenSocket, AcceptSocket, Opts) ->
Node = proplists:get_value(node, Opts),
Pid = proplists:get_value(from, Opts),
+ Transport = proplists:get_value(transport, Opts, ssl),
{Module, Function, Args} = proplists:get_value(mfa, Opts),
- test_server:format("Server: apply(~p,~p,~p)~n",
+ ct:print("Server: apply(~p,~p,~p)~n",
[Module, Function, [AcceptSocket | Args]]),
case rpc:call(Node, Module, Function, [AcceptSocket | Args]) of
no_result_msg ->
ok;
Msg ->
- test_server:format("Server Msg: ~p ~n", [Msg]),
+ ct:print("Server Msg: ~p ~n", [Msg]),
Pid ! {self(), Msg}
end,
receive
@@ -91,35 +92,49 @@ run_server(ListenSocket, Opts) ->
{listen, MFA} ->
run_server(ListenSocket, [MFA | proplists:delete(mfa, Opts)]);
close ->
- test_server:format("Server closing ~p ~n", [self()]),
- Result = rpc:call(Node, ssl, close, [AcceptSocket], 500),
- test_server:format("Result ~p ~n", [Result]);
+ ct:print("Server closing ~p ~n", [self()]),
+ Result = rpc:call(Node, Transport, close, [AcceptSocket], 500),
+ Result1 = rpc:call(Node, Transport, close, [ListenSocket], 500),
+ ct:print("Result ~p : ~p ~n", [Result, Result1]);
{ssl_closed, _} ->
ok
end.
%%% To enable to test with s_client -reconnect
-connect(ListenSocket, Opts) ->
+connect(#sslsocket{} = ListenSocket, Opts) ->
Node = proplists:get_value(node, Opts),
ReconnectTimes = proplists:get_value(reconnect_times, Opts, 0),
- AcceptSocket = connect(ListenSocket, Node, 1 + ReconnectTimes, dummy),
+ Timeout = proplists:get_value(timeout, Opts, infinity),
+ AcceptSocket = connect(ListenSocket, Node, 1 + ReconnectTimes, dummy, Timeout),
case ReconnectTimes of
0 ->
AcceptSocket;
_ ->
remove_close_msg(ReconnectTimes),
AcceptSocket
- end.
-
-connect(_, _, 0, AcceptSocket) ->
+ end;
+connect(ListenSocket, Opts) ->
+ Node = proplists:get_value(node, Opts),
+ ct:print("gen_tcp:accept(~p)~n", [ListenSocket]),
+ {ok, AcceptSocket} = rpc:call(Node, gen_tcp, accept,
+ [ListenSocket]),
+ AcceptSocket.
+
+connect(_, _, 0, AcceptSocket, _) ->
AcceptSocket;
-connect(ListenSocket, Node, N, _) ->
- test_server:format("ssl:transport_accept(~p)~n", [ListenSocket]),
+connect(ListenSocket, Node, N, _, Timeout) ->
+ ct:print("ssl:transport_accept(~p)~n", [ListenSocket]),
{ok, AcceptSocket} = rpc:call(Node, ssl, transport_accept,
[ListenSocket]),
- test_server:format("ssl:ssl_accept(~p)~n", [AcceptSocket]),
- ok = rpc:call(Node, ssl, ssl_accept, [AcceptSocket]),
- connect(ListenSocket, Node, N-1, AcceptSocket).
+ ct:print("ssl:ssl_accept(~p, ~p)~n", [AcceptSocket, Timeout]),
+
+ case rpc:call(Node, ssl, ssl_accept, [AcceptSocket, Timeout]) of
+ ok ->
+ connect(ListenSocket, Node, N-1, AcceptSocket, Timeout);
+ Result ->
+ Result
+ end.
+
remove_close_msg(0) ->
ok;
@@ -144,47 +159,111 @@ run_client(Opts) ->
Host = proplists:get_value(host, Opts),
Port = proplists:get_value(port, Opts),
Pid = proplists:get_value(from, Opts),
+ Transport = proplists:get_value(transport, Opts, ssl),
Options = proplists:get_value(options, Opts),
- test_server:format("ssl:connect(~p, ~p, ~p)~n", [Host, Port, Options]),
- case rpc:call(Node, ssl, connect, [Host, Port, Options]) of
+ ct:print("ssl:connect(~p, ~p, ~p)~n", [Host, Port, Options]),
+ case rpc:call(Node, Transport, connect, [Host, Port, Options]) of
{ok, Socket} ->
Pid ! { connected, Socket },
- test_server:format("Client: connected~n", []),
- %% In specail cases we want to know the client port, it will
+ ct:print("Client: connected~n", []),
+ %% In special cases we want to know the client port, it will
%% be indicated by sending {port, 0} in options list!
send_selected_port(Pid, proplists:get_value(port, Options), Socket),
{Module, Function, Args} = proplists:get_value(mfa, Opts),
- test_server:format("Client: apply(~p,~p,~p)~n",
+ ct:print("Client: apply(~p,~p,~p)~n",
[Module, Function, [Socket | Args]]),
case rpc:call(Node, Module, Function, [Socket | Args]) of
no_result_msg ->
ok;
Msg ->
- test_server:format("Client Msg: ~p ~n", [Msg]),
+ ct:print("Client Msg: ~p ~n", [Msg]),
Pid ! {self(), Msg}
end,
receive
close ->
- test_server:format("Client closing~n", []),
- rpc:call(Node, ssl, close, [Socket]);
+ ct:print("Client closing~n", []),
+ rpc:call(Node, Transport, close, [Socket]);
{ssl_closed, Socket} ->
+ ok;
+ {gen_tcp, closed} ->
ok
end;
{error, Reason} ->
- test_server:format("Client: connection failed: ~p ~n", [Reason]),
+ ct:print("Client: connection failed: ~p ~n", [Reason]),
Pid ! {self(), {error, Reason}}
end.
close(Pid) ->
- test_server:format("Close ~p ~n", [Pid]),
+ ct:print("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])
+ ct:print("Pid: ~p down due to:~p ~n", [Pid, Reason])
end.
+
+check_result(Server, {error, SReason} = ServerMsg, Client, {error, closed} = ClientMsg) ->
+ receive
+ {Server, {error, {SReason, _}}} ->
+ receive
+ {Client, ClientMsg} ->
+ ok;
+ Unexpected ->
+ Reason = {{expected, {Client, ClientMsg}},
+ {got, Unexpected}},
+ ct:fail(Reason)
+ end;
+ {Client, ClientMsg} ->
+ receive
+ {Server, {error, {SReason, _}}} ->
+ ok;
+ Unexpected ->
+ Reason = {{expected, {Server,{error, {SReason, 'term()'}}},
+ {got, Unexpected}}},
+ ct:fail(Reason)
+ end;
+ {Port, {data,Debug}} when is_port(Port) ->
+ io:format("openssl ~s~n",[Debug]),
+ check_result(Server, ServerMsg, Client, ClientMsg);
+
+ Unexpected ->
+ Reason = {{expected, {Client, ClientMsg}},
+ {expected, {Server, {error, {SReason, 'term()'}}}, {got, Unexpected}}},
+ ct:fail(Reason)
+ end;
+
+check_result(Server, {error, closed} = ServerMsg, Client, {error, CReson} = ClientMsg) ->
+ receive
+ {Server, ServerMsg} ->
+ receive
+ {Client, {error, {CReson, _}}} ->
+ ok;
+ Unexpected ->
+ Reason = {{expected, {Client, {error, {CReson, 'term()'}}},
+ {got, Unexpected}}},
+ ct:fail(Reason)
+ end;
+ {Client, {error, {CReson, _}}} ->
+ receive
+ {Server, ServerMsg} ->
+ ok;
+ Unexpected ->
+ Reason = {{expected, {Server, ServerMsg}},
+ {got, Unexpected}},
+ ct:fail(Reason)
+ end;
+ {Port, {data,Debug}} when is_port(Port) ->
+ io:format("openssl ~s~n",[Debug]),
+ check_result(Server, ServerMsg, Client, ClientMsg);
+
+ Unexpected ->
+ Reason = {{expected, {Client, {error, {CReson, 'term()'}}},
+ {expected, {Server, ServerMsg}}, {got, Unexpected}}},
+ ct:fail(Reason)
+ end;
+
check_result(Server, ServerMsg, Client, ClientMsg) ->
receive
{Server, ServerMsg} ->
@@ -194,7 +273,7 @@ check_result(Server, ServerMsg, Client, ClientMsg) ->
Unexpected ->
Reason = {{expected, {Client, ClientMsg}},
{got, Unexpected}},
- test_server:fail(Reason)
+ ct:fail(Reason)
end;
{Client, ClientMsg} ->
receive
@@ -203,7 +282,7 @@ check_result(Server, ServerMsg, Client, ClientMsg) ->
Unexpected ->
Reason = {{expected, {Server, ClientMsg}},
{got, Unexpected}},
- test_server:fail(Reason)
+ ct:fail(Reason)
end;
{Port, {data,Debug}} when is_port(Port) ->
io:format("openssl ~s~n",[Debug]),
@@ -212,9 +291,25 @@ check_result(Server, ServerMsg, Client, ClientMsg) ->
Unexpected ->
Reason = {{expected, {Client, ClientMsg}},
{expected, {Server, ServerMsg}}, {got, Unexpected}},
- test_server:fail(Reason)
+ ct:fail(Reason)
end.
+check_result(Pid, {error, Reason} = Err) when Reason == ecertfile;
+ Reason == ecacertfile;
+ Reason == ekeyfile;
+ Reason == edhfile ->
+ receive
+ {Pid, {error, {Reason, Str}}} when is_list(Str) ->
+ ok;
+ {Port, {data,Debug}} when is_port(Port) ->
+ io:format("openssl ~s~n",[Debug]),
+ check_result(Pid, Err);
+ Unexpected ->
+ Reason = {{expected, {Pid, {error, {Reason, "'appropriate error string'"}}}},
+ {got, Unexpected}},
+ ct:fail(Reason)
+ end;
+
check_result(Pid, Msg) ->
receive
{Pid, Msg} ->
@@ -225,7 +320,7 @@ check_result(Pid, Msg) ->
Unexpected ->
Reason = {{expected, {Pid, Msg}},
{got, Unexpected}},
- test_server:fail(Reason)
+ ct:fail(Reason)
end.
wait_for_result(Server, ServerMsg, Client, ClientMsg) ->
@@ -392,33 +487,33 @@ run_upgrade_server(Opts) ->
SslOptions = proplists:get_value(ssl_options, Opts),
Pid = proplists:get_value(from, Opts),
- test_server:format("gen_tcp:listen(~p, ~p)~n", [Port, TcpOptions]),
+ ct:print("gen_tcp:listen(~p, ~p)~n", [Port, TcpOptions]),
{ok, ListenSocket} = rpc:call(Node, gen_tcp, listen, [Port, TcpOptions]),
Pid ! {listen, up},
send_selected_port(Pid, Port, ListenSocket),
- test_server:format("gen_tcp:accept(~p)~n", [ListenSocket]),
+ ct:print("gen_tcp:accept(~p)~n", [ListenSocket]),
{ok, AcceptSocket} = rpc:call(Node, gen_tcp, accept, [ListenSocket]),
try
{ok, SslAcceptSocket} = case TimeOut of
infinity ->
- test_server:format("ssl:ssl_accept(~p, ~p)~n",
+ ct:print("ssl:ssl_accept(~p, ~p)~n",
[AcceptSocket, SslOptions]),
rpc:call(Node, ssl, ssl_accept,
[AcceptSocket, SslOptions]);
_ ->
- test_server:format("ssl:ssl_accept(~p, ~p, ~p)~n",
+ ct:print("ssl:ssl_accept(~p, ~p, ~p)~n",
[AcceptSocket, SslOptions, TimeOut]),
rpc:call(Node, ssl, ssl_accept,
[AcceptSocket, SslOptions, TimeOut])
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]),
+ ct:print("Upgrade Server Msg: ~p ~n", [Msg]),
Pid ! {self(), Msg},
receive
close ->
- test_server:format("Upgrade Server closing~n", []),
+ ct:print("Upgrade Server closing~n", []),
rpc:call(Node, ssl, close, [SslAcceptSocket])
end
catch error:{badmatch, Error} ->
@@ -436,24 +531,24 @@ run_upgrade_client(Opts) ->
TcpOptions = proplists:get_value(tcp_options, Opts),
SslOptions = proplists:get_value(ssl_options, Opts),
- test_server:format("gen_tcp:connect(~p, ~p, ~p)~n",
+ ct:print("gen_tcp:connect(~p, ~p, ~p)~n",
[Host, Port, TcpOptions]),
{ok, Socket} = rpc:call(Node, gen_tcp, connect, [Host, Port, TcpOptions]),
send_selected_port(Pid, Port, Socket),
- test_server:format("ssl:connect(~p, ~p)~n", [Socket, SslOptions]),
+ ct:print("ssl:connect(~p, ~p)~n", [Socket, SslOptions]),
{ok, SslSocket} = rpc:call(Node, ssl, connect, [Socket, SslOptions]),
{Module, Function, Args} = proplists:get_value(mfa, Opts),
- test_server:format("apply(~p, ~p, ~p)~n",
+ ct:print("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]),
+ ct:print("Upgrade Client Msg: ~p ~n", [Msg]),
Pid ! {self(), Msg},
receive
close ->
- test_server:format("Upgrade Client closing~n", []),
+ ct:print("Upgrade Client closing~n", []),
rpc:call(Node, ssl, close, [SslSocket])
end.
@@ -472,20 +567,20 @@ run_upgrade_server_error(Opts) ->
SslOptions = proplists:get_value(ssl_options, Opts),
Pid = proplists:get_value(from, Opts),
- test_server:format("gen_tcp:listen(~p, ~p)~n", [Port, TcpOptions]),
+ ct:print("gen_tcp:listen(~p, ~p)~n", [Port, TcpOptions]),
{ok, ListenSocket} = rpc:call(Node, gen_tcp, listen, [Port, TcpOptions]),
Pid ! {listen, up},
send_selected_port(Pid, Port, ListenSocket),
- test_server:format("gen_tcp:accept(~p)~n", [ListenSocket]),
+ ct:print("gen_tcp:accept(~p)~n", [ListenSocket]),
{ok, AcceptSocket} = rpc:call(Node, gen_tcp, accept, [ListenSocket]),
Error = case TimeOut of
infinity ->
- test_server:format("ssl:ssl_accept(~p, ~p)~n",
+ ct:print("ssl:ssl_accept(~p, ~p)~n",
[AcceptSocket, SslOptions]),
rpc:call(Node, ssl, ssl_accept,
[AcceptSocket, SslOptions]);
_ ->
- test_server:format("ssl:ssl_accept(~p, ~p, ~p)~n",
+ ct:print("ssl:ssl_accept(~p, ~p, ~p)~n",
[AcceptSocket, SslOptions, TimeOut]),
rpc:call(Node, ssl, ssl_accept,
[AcceptSocket, SslOptions, TimeOut])
@@ -504,22 +599,31 @@ run_server_error(Opts) ->
Port = proplists:get_value(port, Opts),
Options = proplists:get_value(options, Opts),
Pid = proplists:get_value(from, Opts),
- test_server:format("ssl:listen(~p, ~p)~n", [Port, Options]),
- case rpc:call(Node, ssl, listen, [Port, Options]) of
- {ok, ListenSocket} ->
+ Transport = proplists:get_value(transport, Opts, ssl),
+ ct:print("ssl:listen(~p, ~p)~n", [Port, Options]),
+ case rpc:call(Node, Transport, listen, [Port, Options]) of
+ {ok, #sslsocket{} = ListenSocket} ->
%% To make sure error_client will
%% get {error, closed} and not {error, connection_refused}
Pid ! {listen, up},
send_selected_port(Pid, Port, ListenSocket),
- test_server:format("ssl:transport_accept(~p)~n", [ListenSocket]),
- case rpc:call(Node, ssl, transport_accept, [ListenSocket]) of
+ ct:print("ssl:transport_accept(~p)~n", [ListenSocket]),
+ case rpc:call(Node, Transport, transport_accept, [ListenSocket]) of
{error, _} = Error ->
Pid ! {self(), Error};
{ok, AcceptSocket} ->
- test_server:format("ssl:ssl_accept(~p)~n", [AcceptSocket]),
+ ct:print("ssl:ssl_accept(~p)~n", [AcceptSocket]),
Error = rpc:call(Node, ssl, ssl_accept, [AcceptSocket]),
Pid ! {self(), Error}
end;
+ {ok, ListenSocket} ->
+ Pid ! {listen, up},
+ send_selected_port(Pid, Port, ListenSocket),
+ ct:print("~p:accept(~p)~n", [Transport, ListenSocket]),
+ case rpc:call(Node, Transport, accept, [ListenSocket]) of
+ {error, _} = Error ->
+ Pid ! {self(), Error}
+ end;
Error ->
%% Not really true but as this is an error test
%% this is what we want.
@@ -535,9 +639,10 @@ run_client_error(Opts) ->
Host = proplists:get_value(host, Opts),
Port = proplists:get_value(port, Opts),
Pid = proplists:get_value(from, Opts),
+ Transport = proplists:get_value(transport, Opts, ssl),
Options = proplists:get_value(options, Opts),
- test_server:format("ssl:connect(~p, ~p, ~p)~n", [Host, Port, Options]),
- Error = rpc:call(Node, ssl, connect, [Host, Port, Options]),
+ ct:print("ssl:connect(~p, ~p, ~p)~n", [Host, Port, Options]),
+ Error = rpc:call(Node, Transport, connect, [Host, Port, Options]),
Pid ! {self(), Error}.
inet_port(Pid) when is_pid(Pid)->
@@ -564,7 +669,7 @@ trigger_renegotiate(Socket, [ErlData, N]) ->
trigger_renegotiate(Socket, ErlData, N, Id).
trigger_renegotiate(Socket, _, 0, Id) ->
- test_server:sleep(1000),
+ ct:sleep(1000),
case ssl:session_info(Socket) of
[{session_id, Id} | _ ] ->
fail_session_not_renegotiated;
@@ -657,7 +762,7 @@ der_to_pem(File, Entries) ->
cipher_result(Socket, Result) ->
Result = ssl:connection_info(Socket),
- test_server:format("Successfull connect: ~p~n", [Result]),
+ ct:print("Successfull connect: ~p~n", [Result]),
%% Importante to send two packets here
%% to properly test "cipher state" handling
ssl:send(Socket, "Hello\n"),
@@ -708,3 +813,63 @@ state([{data,[{"StateData", State}]} | _]) ->
State;
state([_ | Rest]) ->
state(Rest).
+
+is_tls_version('tlsv1.2') ->
+ true;
+is_tls_version('tlsv1.1') ->
+ true;
+is_tls_version('tlsv1') ->
+ true;
+is_tls_version('sslv3') ->
+ true;
+is_tls_version(_) ->
+ false.
+
+init_tls_version(Version) ->
+ ssl:stop(),
+ application:load(ssl),
+ application:set_env(ssl, protocol_version, Version),
+ ssl:start().
+
+sufficient_crypto_support('tlsv1.2') ->
+ Data = "Sampl",
+ Data2 = "e #1",
+ Key = <<0,1,2,3,16,17,18,19,32,33,34,35,48,49,50,51,4,5,6,7,20,21,22,23,36,37,38,39,
+ 52,53,54,55,8,9,10,11,24,25,26,27,40,41,42,43,56,57,58,59>>,
+ try
+ crypto:sha256_mac(Key, lists:flatten([Data, Data2])),
+ true
+ catch _:_ -> false
+ end;
+sufficient_crypto_support(_) ->
+ true.
+
+send_recv_result_active(Socket) ->
+ ssl:send(Socket, "Hello world"),
+ receive
+ {ssl, Socket, "H"} ->
+ receive
+ {ssl, Socket, "ello world"} ->
+ ok
+ end;
+ {ssl, Socket, "Hello world"} ->
+ ok
+ end.
+
+send_recv_result(Socket) ->
+ ssl:send(Socket, "Hello world"),
+ {ok,"Hello world"} = ssl:recv(Socket, 11),
+ ok.
+
+send_recv_result_active_once(Socket) ->
+ ssl:send(Socket, "Hello world"),
+ receive
+ {ssl, Socket, "H"} ->
+ ssl:setopts(Socket, [{active, once}]),
+ receive
+ {ssl, Socket, "ello world"} ->
+ ok
+ end;
+ {ssl, Socket, "Hello world"} ->
+ ok
+ end.
diff --git a/lib/ssl/test/ssl_to_openssl_SUITE.erl b/lib/ssl/test/ssl_to_openssl_SUITE.erl
index f593c1c552..7c0c00bf36 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-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -16,7 +16,6 @@
%%
%% %CopyrightEnd%
%%
-
%%
-module(ssl_to_openssl_SUITE).
@@ -29,23 +28,70 @@
-define(TIMEOUT, 120000).
-define(LONG_TIMEOUT, 600000).
-define(SLEEP, 1000).
--define(OPENSSL_RENEGOTIATE, "r\n").
+-define(OPENSSL_RENEGOTIATE, "R\n").
-define(OPENSSL_QUIT, "Q\n").
-define(OPENSSL_GARBAGE, "P\n").
-define(EXPIRE, 10).
-%% 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.
+%% Common Test interface functions -----------------------------------
%%--------------------------------------------------------------------
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [
+ {group, basic},
+ {group, 'tlsv1.2'},
+ {group, 'tlsv1.1'},
+ {group, 'tlsv1'},
+ {group, 'sslv3'}
+ ].
+
+groups() ->
+ [{basic, [], basic_tests()},
+ {'tlsv1.2', [], all_versions_tests() ++ npn_tests()},
+ {'tlsv1.1', [], all_versions_tests() ++ npn_tests()},
+ {'tlsv1', [], all_versions_tests()++ npn_tests()},
+ {'sslv3', [], all_versions_tests()}].
+
+basic_tests() ->
+ [basic_erlang_client_openssl_server,
+ basic_erlang_server_openssl_client,
+ expired_session].
+
+all_versions_tests() ->
+ [
+ erlang_client_openssl_server,
+ erlang_server_openssl_client,
+ erlang_client_openssl_server_dsa_cert,
+ erlang_server_openssl_client_dsa_cert,
+ erlang_server_openssl_client_reuse_session,
+ erlang_client_openssl_server_renegotiate,
+ erlang_client_openssl_server_nowrap_seqnum,
+ erlang_server_openssl_client_nowrap_seqnum,
+ erlang_client_openssl_server_no_server_ca_cert,
+ erlang_client_openssl_server_client_cert,
+ erlang_server_openssl_client_client_cert,
+ ciphers_rsa_signed_certs,
+ ciphers_dsa_signed_certs,
+ erlang_client_bad_openssl_server,
+ expired_session,
+ ssl2_erlang_server_openssl_client].
+
+npn_tests() ->
+ [erlang_client_openssl_server_npn,
+ erlang_server_openssl_client_npn,
+ erlang_server_openssl_client_npn_renegotiate,
+ erlang_client_openssl_server_npn_renegotiate,
+ erlang_server_openssl_client_npn_only_client,
+ erlang_server_openssl_client_npn_only_server,
+ erlang_client_openssl_server_npn_only_client,
+ erlang_client_openssl_server_npn_only_server].
+
+
init_per_suite(Config0) ->
- Dog = ssl_test_lib:timetrap(?LONG_TIMEOUT *2),
+ Dog = ct:timetrap(?LONG_TIMEOUT *2),
case os:find_executable("openssl") of
false ->
{skip, "Openssl not found"};
@@ -58,7 +104,7 @@ init_per_suite(Config0) ->
Result =
(catch make_certs:all(?config(data_dir, Config0),
?config(priv_dir, Config0))),
- test_server:format("Make certs ~p~n", [Result]),
+ ct:print("Make certs ~p~n", [Result]),
Config1 = ssl_test_lib:make_dsa_cert(Config0),
Config = ssl_test_lib:cert_options(Config1),
[{watchdog, Dog} | Config]
@@ -67,32 +113,31 @@ init_per_suite(Config0) ->
end
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_group(GroupName, Config) ->
+ case ssl_test_lib:is_tls_version(GroupName) of
+ true ->
+ case check_sane_openssl_version(GroupName) of
+ true ->
+ ssl_test_lib:init_tls_version(GroupName),
+ Config;
+ false ->
+ {skip, openssl_does_not_support_version}
+ end;
+ _ ->
+ ssl:start(),
+ Config
+ end.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
init_per_testcase(expired_session, Config0) ->
Config = lists:keydelete(watchdog, 1, Config0),
- Dog = ssl_test_lib:timetrap(?EXPIRE * 1000 * 5),
+ Dog = ct:timetrap(?EXPIRE * 1000 * 5),
ssl:stop(),
application:load(ssl),
application:set_env(ssl, session_lifetime, ?EXPIRE),
@@ -101,102 +146,45 @@ init_per_testcase(expired_session, Config0) ->
init_per_testcase(TestCase, Config0) ->
Config = lists:keydelete(watchdog, 1, Config0),
- Dog = ssl_test_lib:timetrap(?TIMEOUT),
+ Dog = ct:timetrap(?TIMEOUT),
special_init(TestCase, [{watchdog, Dog} | Config]).
-special_init(TestCase, Config)
+special_init(TestCase, Config)
when TestCase == erlang_client_openssl_server_renegotiate;
- TestCase == erlang_client_openssl_server_no_wrap_sequence_number;
- TestCase == erlang_server_openssl_client_no_wrap_sequence_number ->
+ TestCase == erlang_client_openssl_server_nowrap_seqnum;
+ TestCase == erlang_server_openssl_client_nowrap_seqnum
+ ->
check_sane_openssl_renegotaite(Config);
special_init(ssl2_erlang_server_openssl_client, Config) ->
check_sane_openssl_sslv2(Config);
-special_init(ciphers_dsa_signed_certs, Config) ->
- check_sane_openssl_dsa(Config);
+special_init(TestCase, Config)
+ when TestCase == erlang_client_openssl_server_npn;
+ TestCase == erlang_server_openssl_client_npn;
+ TestCase == erlang_server_openssl_client_npn_renegotiate;
+ TestCase == erlang_client_openssl_server_npn_renegotiate;
+ TestCase == erlang_server_openssl_client_npn_only_server;
+ TestCase == erlang_server_openssl_client_npn_only_client;
+ TestCase == erlang_client_openssl_server_npn_only_client;
+ TestCase == erlang_client_openssl_server_npn_only_server ->
+ check_openssl_npn_support(Config);
special_init(_, Config) ->
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(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;
+ Config;
end_per_testcase(_, Config) ->
- end_per_testcase(default_action, Config).
-
-%%--------------------------------------------------------------------
-%% 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() ->
- [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_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_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_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.
%%--------------------------------------------------------------------
-
-erlang_client_openssl_server(doc) ->
- ["Test erlang client with openssl server"];
-erlang_client_openssl_server(suite) ->
- [];
-erlang_client_openssl_server(Config) when is_list(Config) ->
+%% Test Cases --------------------------------------------------------
+%%--------------------------------------------------------------------
+basic_erlang_client_openssl_server() ->
+ [{doc,"Test erlang client with openssl server"}].
+basic_erlang_client_openssl_server(Config) when is_list(Config) ->
process_flag(trap_exit, true),
ServerOpts = ?config(server_opts, Config),
ClientOpts = ?config(client_opts, Config),
@@ -208,11 +196,11 @@ erlang_client_openssl_server(Config) when is_list(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) ++
+
+ Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++
" -cert " ++ CertFile ++ " -key " ++ KeyFile,
- test_server:format("openssl cmd: ~p~n", [Cmd]),
+ ct:print("openssl cmd: ~p~n", [Cmd]),
OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
@@ -231,16 +219,12 @@ erlang_client_openssl_server(Config) when is_list(Config) ->
%% Clean close down! Server needs to be closed first !!
close_port(OpensslPort),
ssl_test_lib:close(Client),
- process_flag(trap_exit, false),
- ok.
-
+ process_flag(trap_exit, false).
%%--------------------------------------------------------------------
-erlang_server_openssl_client(doc) ->
- ["Test erlang server with openssl client"];
-erlang_server_openssl_client(suite) ->
- [];
-erlang_server_openssl_client(Config) when is_list(Config) ->
+basic_erlang_server_openssl_client() ->
+ [{doc,"Test erlang server with openssl client"}].
+basic_erlang_server_openssl_client(Config) when is_list(Config) ->
process_flag(trap_exit, true),
ServerOpts = ?config(server_opts, Config),
@@ -253,11 +237,11 @@ erlang_server_openssl_client(Config) when is_list(Config) ->
{mfa, {?MODULE, erlang_ssl_receive, [Data]}},
{options, ServerOpts}]),
Port = ssl_test_lib:inet_port(Server),
-
- Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++
+
+ Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++
" -host localhost",
- test_server:format("openssl cmd: ~p~n", [Cmd]),
+ ct:print("openssl cmd: ~p~n", [Cmd]),
OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
port_command(OpenSslPort, Data),
@@ -269,32 +253,26 @@ erlang_server_openssl_client(Config) when is_list(Config) ->
close_port(OpenSslPort),
process_flag(trap_exit, false),
ok.
-
-%%--------------------------------------------------------------------
-
-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) ->
+%%--------------------------------------------------------------------
+erlang_client_openssl_server() ->
+ [{doc,"Test erlang client with openssl server"}].
+erlang_client_openssl_server(Config) when is_list(Config) ->
process_flag(trap_exit, true),
- ClientOpts = ?config(client_dsa_opts, Config),
- ServerOpts = ?config(server_dsa_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ ClientOpts = ?config(client_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",
+ Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+ Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ version_flag(Version) ++
+ " -cert " ++ CertFile ++ " -key " ++ KeyFile,
- test_server:format("openssl cmd: ~p~n", [Cmd]),
+ ct:print("openssl cmd: ~p~n", [Cmd]),
OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
@@ -306,65 +284,53 @@ tls1_erlang_client_openssl_server_dsa_cert(Config) when is_list(Config) ->
{mfa, {?MODULE,
erlang_ssl_receive, [Data]}},
{options, ClientOpts}]),
-
port_command(OpensslPort, Data),
- ssl_test_lib:check_result(Client, ok),
-
+ 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.
-
-%%--------------------------------------------------------------------
+ process_flag(trap_exit, false).
-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) ->
+%%--------------------------------------------------------------------
+erlang_server_openssl_client() ->
+ [{doc,"Test erlang server with openssl client"}].
+erlang_server_openssl_client(Config) when is_list(Config) ->
process_flag(trap_exit, true),
- ClientOpts = ?config(client_dsa_opts, Config),
- ServerOpts = ?config(server_dsa_verify_opts, Config),
+ ServerOpts = ?config(server_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",
+ Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
- test_server:format("openssl cmd: ~p~n", [Cmd]),
+ Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ version_flag(Version) ++
+ " -host localhost",
+
+ ct:print("openssl cmd: ~p~n", [Cmd]),
OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
port_command(OpenSslPort, Data),
ssl_test_lib:check_result(Server, ok),
-
+
%% Clean close down! Server needs to be closed first !!
ssl_test_lib:close(Server),
close_port(OpenSslPort),
- process_flag(trap_exit, false),
- ok.
+ process_flag(trap_exit, false).
%%--------------------------------------------------------------------
-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) ->
+erlang_client_openssl_server_dsa_cert() ->
+ [{doc,"Test erlang server with openssl client"}].
+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),
@@ -377,12 +343,13 @@ ssl3_erlang_client_openssl_server_dsa_cert(Config) when is_list(Config) ->
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) ++
+ Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+
+ Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ version_flag(Version) ++
" -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile
- ++ " -key " ++ KeyFile ++ " -Verify 2 -ssl3 -msg",
+ ++ " -key " ++ KeyFile ++ " -Verify 2 -msg",
- test_server:format("openssl cmd: ~p~n", [Cmd]),
+ ct:print("openssl cmd: ~p~n", [Cmd]),
OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
@@ -404,56 +371,48 @@ ssl3_erlang_client_openssl_server_dsa_cert(Config) when is_list(Config) ->
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) ->
+%%--------------------------------------------------------------------
+erlang_server_openssl_client_dsa_cert() ->
+ [{doc,"Test erlang server with openssl client"}].
+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),
+ 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}]),
+
+ 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",
+ Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+ Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ version_flag(Version) ++
+ " -host localhost " ++ " -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile
+ ++ " -key " ++ KeyFile ++ " -msg",
- test_server:format("openssl cmd: ~p~n", [Cmd]),
-
- OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
+ ct:print("openssl cmd: ~p~n", [Cmd]),
+
+ OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
port_command(OpenSslPort, Data),
-
+
ssl_test_lib:check_result(Server, ok),
-
+
%% Clean close down! Server needs to be closed first !!
ssl_test_lib:close(Server),
close_port(OpenSslPort),
- process_flag(trap_exit, false),
- ok.
-
+ process_flag(trap_exit, false).
%%--------------------------------------------------------------------
-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."];
-erlang_server_openssl_client_reuse_session(suite) ->
- [];
+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."}].
erlang_server_openssl_client_reuse_session(Config) when is_list(Config) ->
process_flag(trap_exit, true),
ServerOpts = ?config(server_opts, Config),
@@ -468,11 +427,11 @@ erlang_server_openssl_client_reuse_session(Config) when is_list(Config) ->
{reconnect_times, 5},
{options, ServerOpts}]),
Port = ssl_test_lib:inet_port(Server),
-
- Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++
+ Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+ Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ version_flag(Version) ++
" -host localhost -reconnect",
- test_server:format("openssl cmd: ~p~n", [Cmd]),
+ ct:print("openssl cmd: ~p~n", [Cmd]),
OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
@@ -488,10 +447,8 @@ erlang_server_openssl_client_reuse_session(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
-erlang_client_openssl_server_renegotiate(doc) ->
- ["Test erlang client when openssl server issuses a renegotiate"];
-erlang_client_openssl_server_renegotiate(suite) ->
- [];
+erlang_client_openssl_server_renegotiate() ->
+ [{doc,"Test erlang client when openssl server issuses a renegotiate"}].
erlang_client_openssl_server_renegotiate(Config) when is_list(Config) ->
process_flag(trap_exit, true),
ServerOpts = ?config(server_opts, Config),
@@ -505,11 +462,12 @@ erlang_client_openssl_server_renegotiate(Config) when is_list(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) ++
+ Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+
+ Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ version_flag(Version) ++
" -cert " ++ CertFile ++ " -key " ++ KeyFile ++ " -msg",
- test_server:format("openssl cmd: ~p~n", [Cmd]),
+ ct:print("openssl cmd: ~p~n", [Cmd]),
OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
@@ -523,7 +481,7 @@ erlang_client_openssl_server_renegotiate(Config) when is_list(Config) ->
{options, ClientOpts}]),
port_command(OpensslPort, ?OPENSSL_RENEGOTIATE),
- test_server:sleep(?SLEEP),
+ ct:sleep(?SLEEP),
port_command(OpensslPort, OpenSslData),
ssl_test_lib:check_result(Client, ok),
@@ -536,14 +494,12 @@ erlang_client_openssl_server_renegotiate(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
-erlang_client_openssl_server_no_wrap_sequence_number(doc) ->
- ["Test that erlang client will renegotiate session when",
+erlang_client_openssl_server_nowrap_seqnum() ->
+ [{doc, "Test that erlang client will renegotiate session when",
"max sequence number celing is about to be reached. Although"
"in the testcase we use the test option renegotiate_at"
- " to lower treashold substantially."];
-erlang_client_openssl_server_no_wrap_sequence_number(suite) ->
- [];
-erlang_client_openssl_server_no_wrap_sequence_number(Config) when is_list(Config) ->
+ " to lower treashold substantially."}].
+erlang_client_openssl_server_nowrap_seqnum(Config) when is_list(Config) ->
process_flag(trap_exit, true),
ServerOpts = ?config(server_opts, Config),
ClientOpts = ?config(client_opts, Config),
@@ -556,11 +512,11 @@ erlang_client_openssl_server_no_wrap_sequence_number(Config) when is_list(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) ++
+ Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+ Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ version_flag(Version) ++
" -cert " ++ CertFile ++ " -key " ++ KeyFile ++ " -msg",
- test_server:format("openssl cmd: ~p~n", [Cmd]),
+ ct:print("openssl cmd: ~p~n", [Cmd]),
OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
@@ -579,18 +535,14 @@ erlang_client_openssl_server_no_wrap_sequence_number(Config) when is_list(Config
%% Clean close down! Server needs to be closed first !!
close_port(OpensslPort),
ssl_test_lib:close(Client),
- process_flag(trap_exit, false),
- ok.
+ process_flag(trap_exit, false).
%%--------------------------------------------------------------------
-erlang_server_openssl_client_no_wrap_sequence_number(doc) ->
- ["Test that erlang client will renegotiate session when",
+erlang_server_openssl_client_nowrap_seqnum() ->
+ [{doc, "Test that erlang client will renegotiate session when",
"max sequence number celing is about to be reached. Although"
"in the testcase we use the test option renegotiate_at"
- " to lower treashold substantially."];
-
-erlang_server_openssl_client_no_wrap_sequence_number(suite) ->
- [];
-erlang_server_openssl_client_no_wrap_sequence_number(Config) when is_list(Config) ->
+ " to lower treashold substantially."}].
+erlang_server_openssl_client_nowrap_seqnum(Config) when is_list(Config) ->
process_flag(trap_exit, true),
ServerOpts = ?config(server_opts, Config),
@@ -606,11 +558,11 @@ erlang_server_openssl_client_no_wrap_sequence_number(Config) when is_list(Config
trigger_renegotiate, [[Data, N+2]]}},
{options, [{renegotiate_at, N}, {reuse_sessions, false} | ServerOpts]}]),
Port = ssl_test_lib:inet_port(Server),
-
- Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++
+ Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+ Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ version_flag(Version) ++
" -host localhost -msg",
- test_server:format("openssl cmd: ~p~n", [Cmd]),
+ ct:print("openssl cmd: ~p~n", [Cmd]),
OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
@@ -621,16 +573,14 @@ erlang_server_openssl_client_no_wrap_sequence_number(Config) when is_list(Config
%% Clean close down! Server needs to be closed first !!
ssl_test_lib:close(Server),
close_port(OpenSslPort),
- process_flag(trap_exit, false),
- ok.
+ process_flag(trap_exit, false).
+
%%--------------------------------------------------------------------
-erlang_client_openssl_server_no_server_ca_cert(doc) ->
- ["Test erlang client when openssl server sends a cert chain not"
+erlang_client_openssl_server_no_server_ca_cert() ->
+ [{doc, "Test erlang client when openssl server sends a cert chain not"
"including the ca cert. Explicitly test this even if it is"
- "implicitly tested eleswhere."];
-erlang_client_openssl_server_no_server_ca_cert(suite) ->
- [];
+ "implicitly tested eleswhere."}].
erlang_client_openssl_server_no_server_ca_cert(Config) when is_list(Config) ->
process_flag(trap_exit, true),
ServerOpts = ?config(server_opts, Config),
@@ -643,11 +593,11 @@ erlang_client_openssl_server_no_server_ca_cert(Config) when is_list(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) ++
+ Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+ Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ version_flag(Version) ++
" -cert " ++ CertFile ++ " -key " ++ KeyFile ++ " -msg",
- test_server:format("openssl cmd: ~p~n", [Cmd]),
+ ct:print("openssl cmd: ~p~n", [Cmd]),
OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
@@ -667,89 +617,12 @@ erlang_client_openssl_server_no_server_ca_cert(Config) when is_list(Config) ->
%% 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_client_openssl_server(doc) ->
- ["Test erlang client with openssl server"];
-ssl3_erlang_client_openssl_server(suite) ->
- [];
-ssl3_erlang_client_openssl_server(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- ServerOpts = ?config(server_opts, Config),
- ClientOpts = ?config(client_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 ++ " -ssl3",
-
- 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,
- connection_info, [sslv3]}},
- {options,
- [{versions, [sslv3]} | ClientOpts]}]),
- 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(doc) ->
- ["Test erlang server with openssl client"];
-ssl3_erlang_server_openssl_client(suite) ->
- [];
-ssl3_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),
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa,
- {?MODULE, connection_info, [sslv3]}},
- {options,
- [{versions, [sslv3]} | ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
-
- Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++
- " -host localhost -ssl3",
-
- test_server:format("openssl cmd: ~p~n", [Cmd]),
-
- OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
-
- ssl_test_lib:check_result(Server, ok),
- %% Clean close down! Server needs to be closed first !!
- ssl_test_lib:close(Server),
- close_port(OpenSslPort),
- process_flag(trap_exit, false),
- ok.
+ process_flag(trap_exit, false).
%%--------------------------------------------------------------------
-ssl3_erlang_client_openssl_server_client_cert(doc) ->
- ["Test erlang client with openssl server when client sends cert"];
-ssl3_erlang_client_openssl_server_client_cert(suite) ->
- [];
-ssl3_erlang_client_openssl_server_client_cert(Config) when is_list(Config) ->
+erlang_client_openssl_server_client_cert() ->
+ [{doc,"Test erlang client with openssl server when client sends cert"}].
+erlang_client_openssl_server_client_cert(Config) when is_list(Config) ->
process_flag(trap_exit, true),
ServerOpts = ?config(server_verification_opts, Config),
ClientOpts = ?config(client_verification_opts, Config),
@@ -762,12 +635,12 @@ ssl3_erlang_client_openssl_server_client_cert(Config) when is_list(Config) ->
CertFile = proplists:get_value(certfile, ServerOpts),
CaCertFile = proplists:get_value(cacertfile, ServerOpts),
KeyFile = proplists:get_value(keyfile, ServerOpts),
-
- Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++
+ Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+ Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ version_flag(Version) ++
" -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile
- ++ " -key " ++ KeyFile ++ " -Verify 2 -ssl3",
+ ++ " -key " ++ KeyFile ++ " -Verify 2",
- test_server:format("openssl cmd: ~p~n", [Cmd]),
+ ct:print("openssl cmd: ~p~n", [Cmd]),
OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
@@ -786,16 +659,13 @@ ssl3_erlang_client_openssl_server_client_cert(Config) when is_list(Config) ->
%% Clean close down! Server needs to be closed first !!
close_port(OpensslPort),
ssl_test_lib:close(Client),
- process_flag(trap_exit, false),
- ok.
+ process_flag(trap_exit, false).
%%--------------------------------------------------------------------
-ssl3_erlang_server_openssl_client_client_cert(doc) ->
- ["Test erlang server with openssl client when client sends cert"];
-ssl3_erlang_server_openssl_client_client_cert(suite) ->
- [];
-ssl3_erlang_server_openssl_client_client_cert(Config) when is_list(Config) ->
+erlang_server_openssl_client_client_cert() ->
+ [{doc,"Test erlang server with openssl client when client sends cert"}].
+erlang_server_openssl_client_client_cert(Config) when is_list(Config) ->
process_flag(trap_exit, true),
ServerOpts = ?config(server_verification_opts, Config),
ClientOpts = ?config(client_verification_opts, Config),
@@ -816,12 +686,12 @@ ssl3_erlang_server_openssl_client_client_cert(Config) when is_list(Config) ->
CaCertFile = proplists:get_value(cacertfile, ClientOpts),
CertFile = proplists:get_value(certfile, ClientOpts),
KeyFile = proplists:get_value(keyfile, ClientOpts),
-
+ Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
Cmd = "openssl s_client -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile
- ++ " -key " ++ KeyFile ++ " -port " ++ integer_to_list(Port) ++
- " -host localhost -ssl3",
+ ++ " -key " ++ KeyFile ++ " -port " ++ integer_to_list(Port) ++ version_flag(Version) ++
+ " -host localhost",
- test_server:format("openssl cmd: ~p~n", [Cmd]),
+ ct:print("openssl cmd: ~p~n", [Cmd]),
OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
port_command(OpenSslPort, Data),
@@ -831,21 +701,17 @@ ssl3_erlang_server_openssl_client_client_cert(Config) when is_list(Config) ->
%% Clean close down! Server needs to be closed first !!
close_port(OpenSslPort),
ssl_test_lib:close(Server),
- process_flag(trap_exit, false),
- ok.
-
+ process_flag(trap_exit, false).
%%--------------------------------------------------------------------
-ssl3_erlang_server_erlang_client_client_cert(doc) ->
- ["Test erlang server with erlang client when client sends cert"];
-ssl3_erlang_server_erlang_client_client_cert(suite) ->
- [];
-ssl3_erlang_server_erlang_client_client_cert(Config) when is_list(Config) ->
+erlang_server_erlang_client_client_cert() ->
+ [{doc,"Test erlang server with erlang client when client sends cert"}].
+erlang_server_erlang_client_client_cert(Config) when is_list(Config) ->
process_flag(trap_exit, true),
ServerOpts = ?config(server_verification_opts, Config),
ClientOpts = ?config(client_verification_opts, Config),
-
+ Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
Data = "From erlang to erlang",
@@ -867,253 +733,278 @@ ssl3_erlang_server_erlang_client_client_cert(Config) when is_list(Config) ->
%% Due to 1/n-1 splitting countermeasure Rizzo/Duong-Beast
{mfa, {ssl, send, [Data]}},
{options,
- [{versions, [sslv3]} | ClientOpts]}]),
+ [{versions, [Version]} | ClientOpts]}]),
ssl_test_lib:check_result(Server, ok, Client, ok),
ssl_test_lib:close(Server),
ssl_test_lib:close(Client),
- process_flag(trap_exit, false),
- ok.
+ process_flag(trap_exit, false).
+
+%%--------------------------------------------------------------------
+ciphers_rsa_signed_certs() ->
+ [{doc,"Test cipher suites that uses rsa certs"}].
+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).
%%--------------------------------------------------------------------
-tls1_erlang_client_openssl_server(doc) ->
- ["Test erlang client with openssl server"];
-tls1_erlang_client_openssl_server(suite) ->
- [];
-tls1_erlang_client_openssl_server(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- ServerOpts = ?config(server_opts, Config),
- ClientOpts = ?config(client_opts, Config),
+ciphers_dsa_signed_certs() ->
+ [{doc,"Test cipher suites that uses dsa certs"}].
+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(),
+ run_suites(Ciphers, Version, Config, dsa).
- test_server:format("Server Opts", [ServerOpts]),
+%%--------------------------------------------------------------------
+erlang_client_bad_openssl_server() ->
+ [{doc,"Test what happens if openssl server sends garbage to erlang ssl client"}].
+erlang_client_bad_openssl_server(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ ServerOpts = ?config(server_verification_opts, Config),
+ ClientOpts = ?config(client_verification_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),
+ Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+ Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ version_flag(Version) ++
+ " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ "",
- Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++
- " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ " -tls1",
-
- test_server:format("openssl cmd: ~p~n", [Cmd]),
+ ct:print("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,
- connection_info, [tlsv1]}},
- {options,
- [{versions, [tlsv1]} | ClientOpts]}]),
+ Client0 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, server_sent_garbage, []}},
+ {options,
+ [{versions, [Version]} | ClientOpts]}]),
- 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.
-
-%%--------------------------------------------------------------------
+ %% Send garbage
+ port_command(OpensslPort, ?OPENSSL_GARBAGE),
-tls1_erlang_server_openssl_client(doc) ->
- ["Test erlang server with openssl client"];
-tls1_erlang_server_openssl_client(suite) ->
- [];
-tls1_erlang_server_openssl_client(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- ServerOpts = ?config(server_opts, Config),
+ ct:sleep(?SLEEP),
- {_, ServerNode, _} = ssl_test_lib:run_where(Config),
+ Client0 ! server_sent_garbage,
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa,
- {?MODULE, connection_info, [tlsv1]}},
- {options,
- [{versions, [tlsv1]} | ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
-
- Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++
- " -host localhost -tls1",
-
- test_server:format("openssl cmd: ~p~n", [Cmd]),
+ ssl_test_lib:check_result(Client0, true),
- OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
-
- ssl_test_lib:check_result(Server, ok),
+ ssl_test_lib:close(Client0),
+ %% Make sure openssl does not hang and leave zombie process
+ Client1 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, no_result_msg, []}},
+ {options,
+ [{versions, [Version]} | ClientOpts]}]),
+
%% Clean close down! Server needs to be closed first !!
- ssl_test_lib:close(Server),
- close_port(OpenSslPort),
+ close_port(OpensslPort),
+ ssl_test_lib:close(Client1),
process_flag(trap_exit, false),
ok.
%%--------------------------------------------------------------------
-tls1_erlang_client_openssl_server_client_cert(doc) ->
- ["Test erlang client with openssl server when client sends cert"];
-tls1_erlang_client_openssl_server_client_cert(suite) ->
- [];
-tls1_erlang_client_openssl_server_client_cert(Config) when is_list(Config) ->
+expired_session() ->
+ [{doc, "Test our ssl client handling of expired sessions. Will make"
+ "better code coverage of the ssl_manager module"}].
+expired_session(Config) when is_list(Config) ->
process_flag(trap_exit, true),
- ServerOpts = ?config(server_verification_opts, Config),
- ClientOpts = ?config(client_verification_opts, Config),
-
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_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",
+
+ Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++
+ " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ "",
- test_server:format("openssl cmd: ~p~n", [Cmd]),
+ ct:print("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),
- 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),
+ %% Make sure session is registered
+ ct: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:check_result(Client, ok),
-
+ ssl_test_lib:close(Client1),
+ %% Make sure session is unregistered due to expiration
+ ct: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}]),
+
%% Clean close down! Server needs to be closed first !!
close_port(OpensslPort),
- ssl_test_lib:close(Client),
- process_flag(trap_exit, false),
- ok.
+ ssl_test_lib:close(Client2),
+ process_flag(trap_exit, false).
-%%--------------------------------------------------------------------
+%%--------------------------------------------------------------------
+ssl2_erlang_server_openssl_client() ->
+ [{doc,"Test that ssl v2 clients are rejected"}].
-tls1_erlang_server_openssl_client_client_cert(doc) ->
- ["Test erlang server with openssl client when client sends cert"];
-tls1_erlang_server_openssl_client_client_cert(suite) ->
- [];
-tls1_erlang_server_openssl_client_client_cert(Config) when is_list(Config) ->
+ssl2_erlang_server_openssl_client(Config) when is_list(Config) ->
process_flag(trap_exit, true),
- ServerOpts = ?config(server_verification_opts, Config),
- ClientOpts = ?config(client_verification_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
{_, ServerNode, _} = ssl_test_lib:run_where(Config),
Data = "From openssl to erlang",
- 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,
- erlang_ssl_receive, [Data]}},
- {options,
- [{verify , verify_peer}
- | ServerOpts]}]),
+ {options, ServerOpts}]),
Port = ssl_test_lib:inet_port(Server),
- CaCertFile = proplists:get_value(cacertfile, ClientOpts),
- CertFile = proplists:get_value(certfile, ClientOpts),
- KeyFile = proplists:get_value(keyfile, ClientOpts),
-
- Cmd = "openssl s_client -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile
- ++ " -key " ++ KeyFile ++ " -port " ++ integer_to_list(Port) ++
- " -host localhost -tls1",
+ Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++
+ " -host localhost -ssl2 -msg",
- test_server:format("openssl cmd: ~p~n", [Cmd]),
+ ct:print("openssl cmd: ~p~n", [Cmd]),
OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
port_command(OpenSslPort, Data),
-
- ssl_test_lib:check_result(Server, ok),
-
- %% Clean close down! Server needs to be closed first !!
- ssl_test_lib:close(Server),
- close_port(OpenSslPort),
- process_flag(trap_exit, false),
- ok.
+ receive
+ {'EXIT', OpenSslPort, _} ->
+ ok
+
+ end,
+ ssl_test_lib:check_result(Server, {error, {essl, "protocol version"}}),
+ process_flag(trap_exit, false).
%%--------------------------------------------------------------------
-tls1_erlang_server_erlang_client_client_cert(doc) ->
- ["Test erlang server with erlang client when client sends cert"];
-tls1_erlang_server_erlang_client_client_cert(suite) ->
- [];
-tls1_erlang_server_erlang_client_client_cert(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- ServerOpts = ?config(server_verification_opts, Config),
- ClientOpts = ?config(client_verification_opts, Config),
+erlang_client_openssl_server_npn() ->
+ [{doc,"Test erlang client with openssl server doing npn negotiation"}].
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Data = "From erlang to erlang",
+erlang_client_openssl_server_npn(Config) when is_list(Config) ->
+ Data = "From openssl to erlang",
+ start_erlang_client_and_openssl_server_for_npn_negotiation(Config, Data, fun(Client, OpensslPort) ->
+ port_command(OpensslPort, Data),
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE,
- erlang_ssl_receive, [Data]}},
- {options,
- [{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, {ssl, send, [Data]}},
- {options,
- [{versions, [tlsv1]} | ClientOpts]}]),
-
- ssl_test_lib:check_result(Server, ok, Client, ok),
- ssl_test_lib:close(Server),
- process_flag(trap_exit, false),
+ ssl_test_lib:check_result(Client, ok)
+ end),
ok.
+
%%--------------------------------------------------------------------
+erlang_client_openssl_server_npn_renegotiate() ->
+ [{doc,"Test erlang client with openssl server doing npn negotiation and renegotiate"}].
-ciphers_rsa_signed_certs(doc) ->
- ["Test cipher suites that uses rsa certs"];
-
-ciphers_rsa_signed_certs(suite) ->
- [];
+erlang_client_openssl_server_npn_renegotiate(Config) when is_list(Config) ->
+ Data = "From openssl to erlang",
+ start_erlang_client_and_openssl_server_for_npn_negotiation(Config, Data, fun(Client, OpensslPort) ->
+ port_command(OpensslPort, ?OPENSSL_RENEGOTIATE),
+ ct:sleep(?SLEEP),
+ port_command(OpensslPort, Data),
+ ssl_test_lib:check_result(Client, ok)
+ end),
+ ok.
+%%--------------------------------------------------------------------------
+erlang_server_openssl_client_npn() ->
+ [{doc,"Test erlang server with openssl client and npn negotiation"}].
-ciphers_rsa_signed_certs(Config) when is_list(Config) ->
- Version =
- ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+erlang_server_openssl_client_npn(Config) when is_list(Config) ->
- Ciphers = ssl_test_lib:rsa_suites(),
- run_suites(Ciphers, Version, Config, rsa).
+ Data = "From openssl to erlang",
+ start_erlang_server_and_openssl_client_for_npn_negotiation(Config, Data, fun(Server, OpensslPort) ->
+ port_command(OpensslPort, Data),
+ ssl_test_lib:check_result(Server, ok)
+ end),
+ ok.
+%%--------------------------------------------------------------------------
+erlang_server_openssl_client_npn_renegotiate() ->
+ [{doc,"Test erlang server with openssl client and npn negotiation with renegotiation"}].
-ciphers_dsa_signed_certs(doc) ->
- ["Test cipher suites that uses dsa certs"];
-
-ciphers_dsa_signed_certs(suite) ->
- [];
+erlang_server_openssl_client_npn_renegotiate(Config) when is_list(Config) ->
+ Data = "From openssl to erlang",
+ start_erlang_server_and_openssl_client_for_npn_negotiation(Config, Data, fun(Server, OpensslPort) ->
+ port_command(OpensslPort, ?OPENSSL_RENEGOTIATE),
+ ct:sleep(?SLEEP),
+ port_command(OpensslPort, Data),
+ ssl_test_lib:check_result(Server, ok)
+ end),
+ ok.
+%%--------------------------------------------------------------------------
+erlang_client_openssl_server_npn_only_server(Config) when is_list(Config) ->
+ Data = "From openssl to erlang",
+ start_erlang_client_and_openssl_server_with_opts(Config, [],
+ "-nextprotoneg spdy/2", Data, fun(Server, OpensslPort) ->
+ port_command(OpensslPort, Data),
+ ssl_test_lib:check_result(Server, ok)
+ end),
+ ok.
-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(),
- run_suites(Ciphers, Version, Config, dsa).
+erlang_client_openssl_server_npn_only_client(Config) when is_list(Config) ->
+ Data = "From openssl to erlang",
+ start_erlang_client_and_openssl_server_with_opts(Config,
+ [{client_preferred_next_protocols,
+ {client, [<<"spdy/2">>], <<"http/1.1">>}}], "",
+ Data, fun(Server, OpensslPort) ->
+ port_command(OpensslPort, Data),
+ ssl_test_lib:check_result(Server, ok)
+ end),
+ ok.
+
+%%--------------------------------------------------------------------------
+erlang_server_openssl_client_npn_only_server(Config) when is_list(Config) ->
+ Data = "From openssl to erlang",
+ start_erlang_server_and_openssl_client_with_opts(Config, [{next_protocols_advertised, [<<"spdy/2">>]}], "",
+ Data, fun(Server, OpensslPort) ->
+ port_command(OpensslPort, Data),
+ ssl_test_lib:check_result(Server, ok)
+ end),
+ ok.
+erlang_server_openssl_client_npn_only_client(Config) when is_list(Config) ->
+ Data = "From openssl to erlang",
+ start_erlang_server_and_openssl_client_with_opts(Config, [], "-nextprotoneg spdy/2",
+ Data, fun(Server, OpensslPort) ->
+ port_command(OpensslPort, Data),
+ ssl_test_lib:check_result(Server, ok)
+ end),
+ ok.
+
+%%--------------------------------------------------------------------
+%% Internal functions ------------------------------------------------
+%%--------------------------------------------------------------------
run_suites(Ciphers, Version, Config, Type) ->
{ClientOpts, ServerOpts} =
- case Type of
+ case Type of
rsa ->
{?config(client_opts, Config),
?config(server_opts, Config)};
@@ -1121,39 +1012,39 @@ run_suites(Ciphers, Version, Config, Type) ->
{?config(client_opts, Config),
?config(server_dsa_opts, Config)}
end,
-
- Result = lists:map(fun(Cipher) ->
+
+ Result = lists:map(fun(Cipher) ->
cipher(Cipher, Version, Config, ClientOpts, ServerOpts) end,
Ciphers),
case lists:flatten(Result) of
[] ->
ok;
Error ->
- test_server:format("Cipher suite errors: ~p~n", [Error]),
- test_server:fail(cipher_suite_failed_see_test_case_log)
+ ct:print("Cipher suite errors: ~p~n", [Error]),
+ ct:fail(cipher_suite_failed_see_test_case_log)
end.
-cipher(CipherSuite, Version, Config, ClientOpts, ServerOpts) ->
+cipher(CipherSuite, Version, Config, ClientOpts, ServerOpts) ->
process_flag(trap_exit, true),
- test_server:format("Testing CipherSuite ~p~n", [CipherSuite]),
+ ct:print("Testing CipherSuite ~p~n", [CipherSuite]),
{ClientNode, _ServerNode, 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) ++ version_flag(Version) ++
" -cert " ++ CertFile ++ " -key " ++ KeyFile ++ "",
- test_server:format("openssl cmd: ~p~n", [Cmd]),
-
- OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
-
+ ct:print("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},
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
{host, Hostname},
{from, self()},
{mfa, {ssl_test_lib, cipher_result, [ConnectionInfo]}},
@@ -1162,25 +1053,25 @@ cipher(CipherSuite, Version, Config, ClientOpts, ServerOpts) ->
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"
+ ct:print("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),
+
+ Result = ssl_test_lib:wait_for_result(Client, ok),
%% Clean close down! Server needs to be closed first !!
close_port(OpenSslPort),
ssl_test_lib:close(Client),
-
+
Return = case Result of
ok ->
[];
@@ -1190,167 +1081,151 @@ cipher(CipherSuite, Version, Config, ClientOpts, ServerOpts) ->
process_flag(trap_exit, false),
Return.
-
-version_flag(tlsv1) ->
- " -tls1 ";
-version_flag(sslv3) ->
- " -ssl3 ".
-
-%%--------------------------------------------------------------------
-erlang_client_bad_openssl_server(doc) ->
- [""];
-erlang_client_bad_openssl_server(suite) ->
- [];
-erlang_client_bad_openssl_server(Config) when is_list(Config) ->
+start_erlang_client_and_openssl_server_with_opts(Config, ErlangClientOpts, OpensslServerOpts, Data, Callback) ->
process_flag(trap_exit, true),
- ServerOpts = ?config(server_verification_opts, Config),
- ClientOpts = ?config(client_verification_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ ClientOpts0 = ?config(client_opts, Config),
+ ClientOpts = ErlangClientOpts ++ ClientOpts0,
{ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
-
+
+ Data = "From openssl to erlang",
+
Port = ssl_test_lib:inet_port(node()),
CertFile = proplists:get_value(certfile, ServerOpts),
KeyFile = proplists:get_value(keyfile, ServerOpts),
+ Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
- Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++
- " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ "",
+ Cmd = "openssl s_server " ++ OpensslServerOpts ++ " -accept " ++
+ integer_to_list(Port) ++ version_flag(Version) ++
+ " -cert " ++ CertFile ++ " -key " ++ KeyFile,
- test_server:format("openssl cmd: ~p~n", [Cmd]),
+ ct:print("openssl cmd: ~p~n", [Cmd]),
+
+ OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
- OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
-
wait_for_openssl_server(),
-
- Client0 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE, server_sent_garbage, []}},
- {options,
- [{versions, [tlsv1]} | ClientOpts]}]),
-
- %% Send garbage
- port_command(OpensslPort, ?OPENSSL_GARBAGE),
- test_server:sleep(?SLEEP),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE,
+ erlang_ssl_receive, [Data]}},
+ {options, ClientOpts}]),
- Client0 ! server_sent_garbage,
-
- ssl_test_lib:check_result(Client0, true),
-
- ssl_test_lib:close(Client0),
-
- %% Make sure openssl does not hang and leave zombie process
- Client1 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {ssl_test_lib, no_result_msg, []}},
- {options,
- [{versions, [tlsv1]} | ClientOpts]}]),
+ Callback(Client, OpensslPort),
%% Clean close down! Server needs to be closed first !!
close_port(OpensslPort),
- ssl_test_lib:close(Client1),
- 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) ->
- [];
+ ssl_test_lib:close(Client),
+ process_flag(trap_exit, false).
-expired_session(Config) when is_list(Config) ->
+start_erlang_client_and_openssl_server_for_npn_negotiation(Config, Data, Callback) ->
process_flag(trap_exit, true),
- ClientOpts = ?config(client_opts, Config),
ServerOpts = ?config(server_opts, Config),
+ ClientOpts0 = ?config(client_opts, Config),
+ ClientOpts = [{client_preferred_next_protocols, {client, [<<"spdy/2">>], <<"http/1.1">>}} | ClientOpts0],
+
{ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
+ Data = "From openssl to erlang",
+
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]),
+ Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
- OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
+ Cmd = "openssl s_server -msg -nextprotoneg http/1.1,spdy/2 -accept " ++ integer_to_list(Port) ++ version_flag(Version) ++
+ " -cert " ++ CertFile ++ " -key " ++ KeyFile,
+
+ ct:print("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),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE,
+ erlang_ssl_receive_and_assert_npn, [<<"spdy/2">>, Data]}},
+ {options, ClientOpts}]),
- 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}]),
+ Callback(Client, OpensslPort),
%% Clean close down! Server needs to be closed first !!
close_port(OpensslPort),
- ssl_test_lib:close(Client2),
+
+ ssl_test_lib:close(Client),
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) ->
+start_erlang_server_and_openssl_client_for_npn_negotiation(Config, Data, Callback) ->
process_flag(trap_exit, true),
- ServerOpts = ?config(server_opts, Config),
+ ServerOpts0 = ?config(server_opts, Config),
+ ServerOpts = [{next_protocols_advertised, [<<"spdy/2">>]}, ServerOpts0],
{_, 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}]),
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, erlang_ssl_receive_and_assert_npn, [<<"spdy/2">>, Data]}},
+ {options, ServerOpts}]),
Port = ssl_test_lib:inet_port(Server),
-
- Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++
- " -host localhost -ssl2 -msg",
+ Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+ Cmd = "openssl s_client -nextprotoneg http/1.0,spdy/2 -msg -port " ++ integer_to_list(Port) ++ version_flag(Version) ++
+ " -host localhost",
+
+ ct:print("openssl cmd: ~p~n", [Cmd]),
+
+ OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
+
+ Callback(Server, OpenSslPort),
- 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"}),
-
- %% Clean close down! Server needs to be closed first !!
ssl_test_lib:close(Server),
+
close_port(OpenSslPort),
- process_flag(trap_exit, false),
- ok.
+ process_flag(trap_exit, false).
-%%--------------------------------------------------------------------
+
+start_erlang_server_and_openssl_client_with_opts(Config, ErlangServerOpts, OpenSSLClientOpts, Data, Callback) ->
+ process_flag(trap_exit, true),
+ ServerOpts0 = ?config(server_opts, Config),
+ ServerOpts = ErlangServerOpts ++ ServerOpts0,
+
+ {_, ServerNode, _} = ssl_test_lib:run_where(Config),
+
+
+ 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 " ++ OpenSSLClientOpts ++ " -msg -port " ++ integer_to_list(Port) ++
+ " -host localhost",
+
+ ct:print("openssl cmd: ~p~n", [Cmd]),
+
+ OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
+
+ Callback(Server, OpenSslPort),
+
+ ssl_test_lib:close(Server),
+
+ close_port(OpenSslPort),
+ process_flag(trap_exit, false).
+
+
+erlang_ssl_receive_and_assert_npn(Socket, Protocol, Data) ->
+ {ok, Protocol} = ssl:negotiated_next_protocol(Socket),
+ erlang_ssl_receive(Socket, Data),
+ {ok, Protocol} = ssl:negotiated_next_protocol(Socket),
+ ok.
erlang_ssl_receive(Socket, Data) ->
- test_server:format("Connection info: ~p~n",
+ ct:print("Connection info: ~p~n",
[ssl:connection_info(Socket)]),
receive
{ssl, Socket, Data} ->
@@ -1364,15 +1239,15 @@ erlang_ssl_receive(Socket, Data) ->
io:format("openssl ~s~n",[Debug]),
erlang_ssl_receive(Socket,Data);
Other ->
- test_server:fail({unexpected_message, Other})
+ ct:fail({unexpected_message, Other})
after 4000 ->
- test_server:fail({did_not_get, Data})
+ ct:fail({did_not_get, Data})
end.
connection_info(Socket, Version) ->
case ssl:connection_info(Socket) of
{ok, {Version, _} = Info} ->
- test_server:format("Connection info: ~p~n", [Info]),
+ ct:print("Connection info: ~p~n", [Info]),
ok;
{ok, {OtherVersion, _}} ->
{wrong_version, OtherVersion}
@@ -1383,7 +1258,7 @@ connection_info_result(Socket) ->
delayed_send(Socket, [ErlData, OpenSslData]) ->
- test_server:sleep(?SLEEP),
+ ct:sleep(?SLEEP),
ssl:send(Socket, ErlData),
erlang_ssl_receive(Socket, OpenSslData).
@@ -1435,33 +1310,73 @@ wait_for_openssl_server() ->
%% it will be in accept. Parsing
%% output is too error prone. (Even
%% more so than sleep!)
- test_server:sleep(?SLEEP)
+ ct:sleep(?SLEEP)
end.
-
+
+version_flag(tlsv1) ->
+ " -tls1 ";
+version_flag('tlsv1.1') ->
+ " -tls1_1 ";
+version_flag('tlsv1.2') ->
+ " -tls1_2 ";
+version_flag(sslv3) ->
+ " -ssl3 ".
+
+check_openssl_npn_support(Config) ->
+ HelpText = os:cmd("openssl s_client --help"),
+ case string:str(HelpText, "nextprotoneg") of
+ 0 ->
+ {skip, "Openssl not compiled with nextprotoneg support"};
+ _ ->
+ Config
+ end.
+
check_sane_openssl_renegotaite(Config) ->
case os:cmd("openssl version") of
"OpenSSL 0.9.8" ++ _ ->
- {skip, "Known renegotiation bug in OppenSSL"};
+ {skip, "Known renegotiation bug in OpenSSL"};
"OpenSSL 0.9.7" ++ _ ->
- {skip, "Known renegotiation bug in OppenSSL"};
- "OpenSSL 1.0.1c" ++ _ ->
- {skip, "Known renegotiation bug in OppenSSL"};
+ {skip, "Known renegotiation bug in OpenSSL"};
_ ->
Config
end.
check_sane_openssl_sslv2(Config) ->
- case os:cmd("openssl version") of
- "OpenSSL 1." ++ _ ->
- {skip, "sslv2 by default turned of in 1.*"};
- _ ->
- Config
+ Port = open_port({spawn, "openssl s_client -ssl2 "}, [stderr_to_stdout]),
+ case supports_sslv2(Port) of
+ true ->
+ Config;
+ false ->
+ {skip, "sslv2 not supported by openssl"}
end.
-check_sane_openssl_dsa(Config) ->
- case os:cmd("openssl version") of
- "OpenSSL 1.0.1" ++ _ ->
- {skip, "known dsa bug in openssl"};
- _ ->
- Config
+supports_sslv2(Port) ->
+ receive
+ {Port, {data, "unknown option -ssl2" ++ _}} ->
+ false;
+ {Port, {data, Data}} ->
+ case lists:member("error", string:tokens(Data, ":")) of
+ true ->
+ false;
+ false ->
+ supports_sslv2(Port)
+ end
+ after 500 ->
+ true
+ end.
+
+check_sane_openssl_version(Version) ->
+ case {Version, os:cmd("openssl version")} of
+ {_, "OpenSSL 1.0.1" ++ _} ->
+ true;
+ {'tlsv1.2', "OpenSSL 1.0" ++ _} ->
+ false;
+ {'tlsv1.1', "OpenSSL 1.0" ++ _} ->
+ false;
+ {'tlsv1.2', "OpenSSL 0" ++ _} ->
+ false;
+ {'tlsv1.1', "OpenSSL 0" ++ _} ->
+ false;
+ {_, _} ->
+ true
end.
diff --git a/lib/ssl/vsn.mk b/lib/ssl/vsn.mk
index 0fccbfe908..cb73e86ede 100644
--- a/lib/ssl/vsn.mk
+++ b/lib/ssl/vsn.mk
@@ -1 +1 @@
-SSL_VSN = 5.0.1
+SSL_VSN = 5.2
diff --git a/lib/stdlib/doc/src/Makefile b/lib/stdlib/doc/src/Makefile
index c3138c4d9f..6f1e61e70c 100644
--- a/lib/stdlib/doc/src/Makefile
+++ b/lib/stdlib/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -101,7 +101,6 @@ XML_REF6_FILES = stdlib_app.xml
XML_PART_FILES = part.xml part_notes.xml part_notes_history.xml
XML_CHAPTER_FILES = io_protocol.xml unicode_usage.xml notes.xml notes_history.xml
-GIF_FILES = ushell1.gif ushell2.gif ushell3.gif
BOOK_FILES = book.xml
@@ -112,8 +111,7 @@ XML_FILES = \
# ----------------------------------------------------
HTML_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \
- $(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html) \
- $(GIF_FILES:%.gif=$(HTMLDIR)/%.gif)
+ $(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html)
INFO_FILE = ../../info
@@ -138,21 +136,16 @@ SPECS_FLAGS = -I../../include -I../../../kernel/include
# ----------------------------------------------------
# Targets
# ----------------------------------------------------
-$(HTMLDIR)/%.gif: %.gif
- $(INSTALL_DATA) $< $@
-
docs: man pdf html
$(TOP_PDF_FILE): $(XML_FILES)
pdf: $(TOP_PDF_FILE)
-html: gifs $(HTML_REF_MAN_FILE)
+html: $(HTML_REF_MAN_FILE)
man: $(MAN3_FILES) $(MAN6_FILES)
-gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
-
debug opt:
clean clean_docs:
diff --git a/lib/stdlib/doc/src/epp.xml b/lib/stdlib/doc/src/epp.xml
index 488499581f..3e8aba2e5f 100644
--- a/lib/stdlib/doc/src/epp.xml
+++ b/lib/stdlib/doc/src/epp.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2011</year>
+ <year>1996</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -37,6 +37,18 @@
<p>The Erlang code preprocessor includes functions which are used
by <c>compile</c> to preprocess macros and include files before
the actual parsing takes place.</p>
+ <p>The Erlang source file <marker
+ id="encoding"><em>encoding</em></marker> is selected by a
+ comment in one of the first two lines of the source file. The
+ first string that matches the regular expression
+ <c>coding\s*[:=]\s*([-a-zA-Z0-9])+</c> selects the encoding. If
+ the matching string is not a valid encoding it is ignored. The
+ valid encodings are <c>Latin-1</c> and <c>UTF-8</c> where the
+ case of the characters can be chosen freely. Examples:</p>
+ <pre>
+%% coding: utf-8
+%% For this file we have chosen encoding = Latin-1
+%% -*- coding: latin-1 -*-</pre>
</description>
<datatypes>
<datatype>
@@ -46,6 +58,9 @@
<name name="epp_handle"></name>
<desc><p>Handle to the epp server.</p></desc>
</datatype>
+ <datatype>
+ <name name="source_encoding"></name>
+ </datatype>
</datatypes>
<funcs>
<func>
@@ -83,6 +98,50 @@
</desc>
</func>
<func>
+ <name name="default_encoding" arity="0"/>
+ <fsummary>Return the default encoding of Erlang source files</fsummary>
+ <desc>
+ <p>Returns the default encoding of Erlang source files.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="encoding_to_string" arity="1"/>
+ <fsummary>Return a string representation of an encoding</fsummary>
+ <desc>
+ <p>Returns a string representation of an encoding. The string
+ is recognized by <c>read_encoding/1,2</c> and
+ <c>set_encoding/1</c> as a valid encoding.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="read_encoding" arity="1"/>
+ <name name="read_encoding" arity="2"/>
+ <fsummary>Read the encoding from a file</fsummary>
+ <desc>
+ <p>Read the <seealso marker="#encoding">encoding</seealso> from
+ a file. Returns the read encoding, or <c>none</c> if no
+ valid encoding was found.</p>
+ <p>The option <c>in_comment_only</c> is <c>true</c> by
+ default, which is correct for Erlang source files. If set to
+ <c>false</c> the encoding string does not necessarily have to
+ occur in a comment.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="set_encoding" arity="1"/>
+ <fsummary>Read and set the encoding of an IO device</fsummary>
+ <desc>
+ <p>Reads the <seealso marker="#encoding">encoding</seealso> from
+ an IO device and sets the encoding of the device
+ accordingly. The position of the IO device referenced by
+ <c><anno>File</anno></c> is not affected. If no valid
+ encoding can be read from the IO device the encoding of the
+ IO device is set to the default encoding.</p>
+ <p>Returns the read encoding, or <c>none</c> if no valid
+ encoding was found.</p>
+ </desc>
+ </func>
+ <func>
<name name="format_error" arity="1"/>
<fsummary>Format an error descriptor</fsummary>
<desc>
diff --git a/lib/stdlib/doc/src/erl_pp.xml b/lib/stdlib/doc/src/erl_pp.xml
index 57b5828bcd..9ae4f3d91f 100644
--- a/lib/stdlib/doc/src/erl_pp.xml
+++ b/lib/stdlib/doc/src/erl_pp.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1996</year>
- <year>2011</year>
+ <year>2012</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -63,6 +63,12 @@
breaks and only a space is used as a separator.</p>
</desc>
</datatype>
+ <datatype>
+ <name name="option"/>
+ </datatype>
+ <datatype>
+ <name name="options"/>
+ </datatype>
</datatypes>
<funcs>
<func>
diff --git a/lib/stdlib/doc/src/ets.xml b/lib/stdlib/doc/src/ets.xml
index 7880bf8fbb..abaf64fb91 100644
--- a/lib/stdlib/doc/src/ets.xml
+++ b/lib/stdlib/doc/src/ets.xml
@@ -146,6 +146,10 @@
<desc><p>A match specification, see above.</p></desc>
</datatype>
<datatype>
+ <name name="comp_match_spec"/>
+ <desc><p>A compiled match specification.</p></desc>
+ </datatype>
+ <datatype>
<name name="match_pattern"/>
</datatype>
<datatype>
@@ -766,8 +770,6 @@ ets:is_compiled_ms(Broken).</code>
</func>
<func>
<name name="match_spec_compile" arity="1"/>
- <type name="comp_match_spec"/>
- <type_desc name="comp_match_spec">A compiled match specification.</type_desc>
<fsummary>Compiles a match specification into its internal representation</fsummary>
<desc>
<p>This function transforms a
@@ -791,8 +793,6 @@ ets:is_compiled_ms(Broken).</code>
</func>
<func>
<name name="match_spec_run" arity="2"/>
- <type name="comp_match_spec"/>
- <type_desc name="comp_match_spec">A compiled match specification.</type_desc>
<fsummary>Performs matching, using a compiled match_spec, on a list of tuples</fsummary>
<desc>
<p>This function executes the matching specified in a
diff --git a/lib/stdlib/doc/src/filelib.xml b/lib/stdlib/doc/src/filelib.xml
index f3079c7337..cec20aee8e 100644
--- a/lib/stdlib/doc/src/filelib.xml
+++ b/lib/stdlib/doc/src/filelib.xml
@@ -150,6 +150,11 @@
<p>Matches any number of characters up to the end of
the filename, the next dot, or the next slash.</p>
</item>
+ <tag>**</tag>
+ <item>
+ <p>Two adjacent <c>*</c>'s used as a single pattern will
+ match all files and zero or more directories and subdirectories.</p>
+ </item>
<tag>[Character1,Character2,...]</tag>
<item>
<p>Matches any of the characters listed. Two characters
@@ -192,6 +197,10 @@
<c>src</c> or <c>include</c> directories, use:</p>
<code type="none">
filelib:wildcard("lib/*/{src,include}/*.{erl,hrl}") </code>
+ <p>To find all <c>.erl</c> or <c>.hrl</c> files in any
+ subdirectory, use:</p>
+ <code type="none">
+ filelib:wildcard("lib/**/*.{erl,hrl}") </code>
</desc>
</func>
<func>
diff --git a/lib/stdlib/doc/src/io.xml b/lib/stdlib/doc/src/io.xml
index e6d262466c..22cd45a482 100644
--- a/lib/stdlib/doc/src/io.xml
+++ b/lib/stdlib/doc/src/io.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2011</year>
+ <year>1996</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -28,9 +28,9 @@
<rev></rev>
</header>
<module>io</module>
- <modulesummary>Standard IO Server Interface Functions</modulesummary>
+ <modulesummary>Standard I/O Server Interface Functions</modulesummary>
<description>
- <p>This module provides an interface to standard Erlang IO servers.
+ <p>This module provides an interface to standard Erlang I/O servers.
The output functions all return <c>ok</c> if they are successful,
or exit if they are not.</p>
<p>In the following description, all functions have an optional
@@ -38,17 +38,16 @@
process which handles the IO protocols. Normally, it is the
<c>IoDevice</c> returned by
<seealso marker="kernel:file#open/2">file:open/2</seealso>.</p>
- <p>For a description of the IO protocols refer to the STDLIB Users Guide.</p>
+ <p>For a description of the IO protocols refer to the <seealso marker="io_protocol">STDLIB User's Guide</seealso>.</p>
<warning>
<p>As of R13A, data supplied to the <seealso
marker="#put_chars/2">put_chars</seealso> function should be in the
<seealso marker="unicode#type-chardata"><c>unicode:chardata()</c></seealso> format. This means that programs
supplying binaries to this function need to convert them to UTF-8
- before trying to output the data on an
- <c>io_device()</c>.</p>
+ before trying to output the data on an IO device.</p>
- <p>If an io_device() is set in binary mode, the functions <seealso
+ <p>If an IO device is set in binary mode, the functions <seealso
marker="#get_chars/3">get_chars</seealso> and <seealso
marker="#get_line/2">get_line</seealso> may return binaries
instead of lists. The binaries will, as of R13A, be encoded in
@@ -68,9 +67,9 @@
<datatype>
<name name="device"/>
<desc>
- <p>Either <c>standard_io</c>, <c>standard_error</c>, a
+ <p>An IO device. Either <c>standard_io</c>, <c>standard_error</c>, a
registered name, or a pid handling IO protocols (returned from
- <seealso marker="kernel:file#open/2">file:open/2</seealso>).</p>
+ <seealso marker="kernel:file#open/2">file:open/2</seealso>).</p>
</desc>
</datatype>
<datatype>
@@ -89,17 +88,14 @@
<name name="format"/>
</datatype>
<datatype>
- <name name="line"/>
+ <name name="location"/>
</datatype>
<datatype>
<name name="prompt"/>
</datatype>
<datatype>
- <name name="request_error"/>
- </datatype>
- <datatype>
- <name name="error_description"/>
- <desc><p>Whatever the I/O-server sends.</p></desc>
+ <name name="server_no_data"/>
+ <desc><p>What the I/O-server sends when there is no data.</p></desc>
</datatype>
</datatypes>
@@ -107,11 +103,11 @@
<func>
<name name="columns" arity="0"/>
<name name="columns" arity="1"/>
- <fsummary>Get the number of columns of a device</fsummary>
+ <fsummary>Get the number of columns of an IO device</fsummary>
<desc>
<p>Retrieves the number of columns of the
<c><anno>IoDevice</anno></c> (i.e. the width of a terminal). The function
- only succeeds for terminal devices, for all other devices
+ only succeeds for terminal devices, for all other IO devices
the function returns <c>{error, enotsup}</c></p>
</desc>
</func>
@@ -120,7 +116,7 @@
<name name="put_chars" arity="2"/>
<fsummary>Write a list of characters</fsummary>
<desc>
- <p>Writes the characters of <c><anno>CharData</anno></c> to the io_server()
+ <p>Writes the characters of <c><anno>CharData</anno></c> to the I/O server
(<c><anno>IoDevice</anno></c>).</p>
</desc>
</func>
@@ -135,6 +131,7 @@
<func>
<name name="get_chars" arity="2"/>
<name name="get_chars" arity="3"/>
+ <type name="server_no_data"/>
<fsummary>Read a specified number of characters</fsummary>
<desc>
<p>Reads <c><anno>Count</anno></c> characters from standard input
@@ -143,19 +140,19 @@
<taglist>
<tag><c><anno>Data</anno></c></tag>
<item>
- <p>The input characters. If the device supports Unicode,
+ <p>The input characters. If the IO device supports Unicode,
the data may represent codepoints larger than 255 (the
- latin1 range). If the io_server() is set to deliver
+ latin1 range). If the I/O server is set to deliver
binaries, they will be encoded in UTF-8 (regardless of if
- the device actually supports Unicode or not).</p>
+ the IO device actually supports Unicode or not).</p>
</item>
<tag><c>eof</c></tag>
<item>
<p>End of file was encountered.</p>
</item>
- <tag><c>{error,<anno>Reason</anno>}</c></tag>
+ <tag><c>{error, <anno>ErrorDescription</anno>}</c></tag>
<item>
- <p>Other (rare) error condition, for instance <c>{error,estale}</c>
+ <p>Other (rare) error condition, for instance <c>{error, estale}</c>
if reading from an NFS file system.</p>
</item>
</taglist>
@@ -164,6 +161,7 @@
<func>
<name name="get_line" arity="1"/>
<name name="get_line" arity="2"/>
+ <type name="server_no_data"/>
<fsummary>Read a line</fsummary>
<desc>
<p>Reads a line from the standard input (<c><anno>IoDevice</anno></c>),
@@ -172,19 +170,19 @@
<tag><c><anno>Data</anno></c></tag>
<item>
<p>The characters in the line terminated by a LF (or end of
- file). If the device supports Unicode,
+ file). If the IO device supports Unicode,
the data may represent codepoints larger than 255 (the
- latin1 range). If the io_server() is set to deliver
+ latin1 range). If the I/O server is set to deliver
binaries, they will be encoded in UTF-8 (regardless of if
- the device actually supports Unicode or not).</p>
+ the IO device actually supports Unicode or not).</p>
</item>
<tag><c>eof</c></tag>
<item>
<p>End of file was encountered.</p>
</item>
- <tag><c>{error,<anno>Reason</anno>}</c></tag>
+ <tag><c>{error, <anno>ErrorDescription</anno>}</c></tag>
<item>
- <p>Other (rare) error condition, for instance <c>{error,estale}</c>
+ <p>Other (rare) error condition, for instance <c>{error, estale}</c>
if reading from an NFS file system.</p>
</item>
</taglist>
@@ -195,7 +193,7 @@
<name name="getopts" arity="1"/>
<fsummary>Get the supported options and values from an I/O-server</fsummary>
<desc>
- <p>This function requests all available options and their current values for a specific io_device(). Example:</p>
+ <p>This function requests all available options and their current values for a specific IO device. Example:</p>
<pre>
1> <input>{ok,F} = file:open("/dev/null",[read]).</input>
{ok,&lt;0.42.0&gt;}
@@ -217,30 +215,30 @@
<name name="setopts" arity="2"/>
<fsummary>Set options</fsummary>
<desc>
- <p>Set options for the io_device() (<c><anno>IoDevice</anno></c>).</p>
+ <p>Set options for the standard IO device (<c><anno>IoDevice</anno></c>).</p>
<p>Possible options and values vary depending on the actual
- io_device(). For a list of supported options and their current values
- on a specific device, use the <seealso
+ IO device. For a list of supported options and their current values
+ on a specific IO device, use the <seealso
marker="#getopts/1">getopts/1</seealso> function.</p>
- <p>The options and values supported by the current OTP io_devices are:</p>
+ <p>The options and values supported by the current OTP IO devices are:</p>
<taglist>
<tag><c>binary, list or {binary, boolean()}</c></tag>
<item>
- <p>If set in binary mode (binary or {binary,true}), the io_server() sends binary data (encoded in UTF-8) as answers to the get_line, get_chars and, if possible, get_until requests (see the I/O protocol description in STDLIB User's Guide for details). The immediate effect is that <c>get_chars/2,3</c> and <c>get_line/1,2</c> return UTF-8 binaries instead of lists of chars for the affected device.</p>
- <p>By default, all io_devices in OTP are set in list mode, but the io functions can handle any of these modes and so should other, user written, modules behaving as clients to I/O-servers.</p>
- <p>This option is supported by the standard shell (group.erl), the 'oldshell' (user.erl) and the file I/O servers.</p>
+ <p>If set in binary mode (<c>binary</c> or <c>{binary, true}</c>), the I/O server sends binary data (encoded in UTF-8) as answers to the <c>get_line</c>, <c>get_chars</c> and, if possible, <c>get_until</c> requests (see the I/O protocol description in <seealso marker="io_protocol">STDLIB User's Guide</seealso> for details). The immediate effect is that <c>get_chars/2,3</c> and <c>get_line/1,2</c> return UTF-8 binaries instead of lists of chars for the affected IO device.</p>
+ <p>By default, all IO devices in OTP are set in list mode, but the I/O functions can handle any of these modes and so should other, user written, modules behaving as clients to I/O-servers.</p>
+ <p>This option is supported by the standard shell (<c>group.erl</c>), the 'oldshell' (<c>user.erl</c>) and the file I/O servers.</p>
</item>
<tag><c>{echo, boolean()}</c></tag>
<item>
- <p>Denotes if the terminal should echo input. Only supported for the standard shell I/O-server (group.erl)</p>
+ <p>Denotes if the terminal should echo input. Only supported for the standard shell I/O-server (<c>group.erl</c>)</p>
</item>
<tag><c>{expand_fun, expand_fun()}</c></tag>
<item>
<p>Provide a function for tab-completion (expansion)
- like the erlang shell. This function is called
- when the user presses the Tab key. The expansion is
+ like the Erlang shell. This function is called
+ when the user presses the TAB key. The expansion is
active when calling line-reading functions such as
<c>get_line/1,2</c>.</p>
<p>The function is called with the current line, upto
@@ -253,25 +251,25 @@
will be printed and the current input line will be written
once again.</p>
<p>Trivial example (beep on anything except empty line, which
- is expanded to "quit"):</p>
+ is expanded to <c>"quit"</c>):</p>
<code type="none">
fun("") -> {yes, "quit", []};
(_) -> {no, "", ["quit"]} end</code>
- <p>This option is supported by the standard shell only (group.erl).</p>
+ <p>This option is supported by the standard shell only (<c>group.erl</c>).</p>
</item>
<tag><c>{encoding, latin1 | unicode}</c></tag>
<item>
- <p>Specifies how characters are input or output from or to the actual device, implying that i.e. a terminal is set to handle Unicode input and output or a file is set to handle UTF-8 data encoding.</p>
- <p>The option <em>does not</em> affect how data is returned from the io-functions or how it is sent in the I/O-protocol, it only affects how the io_device() is to handle Unicode characters towards the &quot;physical&quot; device.</p>
- <p>The standard shell will be set for either unicode or latin1 encoding when the system is started. The actual encoding is set with the help of the "LANG" or "LC_CTYPE" environment variables on Unix-like system or by other means on other systems. The bottom line is that the user can input Unicode characters and the device will be in {encoding, unicode} mode if the device supports it. The mode can be changed, if the assumption of the runtime system is wrong, by setting this option.</p>
- <p>The io_device() used when Erlang is started with the "-oldshell" or "-noshell" flags is by default set to latin1 encoding, meaning that any characters beyond codepoint 255 will be escaped and that input is expected to be plain 8-bit ISO-latin-1. If the encoding is changed to Unicode, input and output from the standard file descriptors will be in UTF-8 (regardless of operating system).</p>
- <p>Files can also be set in {encoding, unicode}, meaning that data is written and read as UTF-8. More encodings are possible for files, see below.</p>
- <p>{encoding, unicode | latin1} is supported by both the standard shell (group.erl including werl on windows), the 'oldshell' (user.erl) and the file I/O servers.</p>
+ <p>Specifies how characters are input or output from or to the actual IO device, implying that i.e. a terminal is set to handle Unicode input and output or a file is set to handle UTF-8 data encoding.</p>
+ <p>The option <em>does not</em> affect how data is returned from the I/O functions or how it is sent in the I/O-protocol, it only affects how the IO device is to handle Unicode characters towards the &quot;physical&quot; device.</p>
+ <p>The standard shell will be set for either Unicode or latin1 encoding when the system is started. The actual encoding is set with the help of the <c>LANG</c> or <c>LC_CTYPE</c> environment variables on Unix-like system or by other means on other systems. The bottom line is that the user can input Unicode characters and the IO device will be in <c>{encoding, unicode}</c> mode if the IO device supports it. The mode can be changed, if the assumption of the runtime system is wrong, by setting this option.</p>
+ <p>The IO device used when Erlang is started with the "-oldshell" or "-noshell" flags is by default set to latin1 encoding, meaning that any characters beyond codepoint 255 will be escaped and that input is expected to be plain 8-bit ISO-latin-1. If the encoding is changed to Unicode, input and output from the standard file descriptors will be in UTF-8 (regardless of operating system).</p>
+ <p>Files can also be set in <c>{encoding, unicode}</c>, meaning that data is written and read as UTF-8. More encodings are possible for files, see below.</p>
+ <p><c>{encoding, unicode | latin1}</c> is supported by both the standard shell (<c>group.erl</c> including <c>werl</c> on Windows&reg;), the 'oldshell' (<c>user.erl</c>) and the file I/O servers.</p>
</item>
<tag><c>{encoding, utf8 | utf16 | utf32 | {utf16,big} | {utf16,little} | {utf32,big} | {utf32,little}}</c></tag>
<item>
<p>For disk files, the encoding can be set to various UTF variants. This will have the effect that data is expected to be read as the specified encoding from the file and the data will be written in the specified encoding to the disk file.</p>
- <p>{encoding, utf8} will have the same effect as {encoding,unicode} on files.</p>
+ <p><c>{encoding, utf8}</c> will have the same effect as <c>{encoding, unicode}</c> on files.</p>
<p>The extended encodings are only supported on disk files (opened by the <seealso marker="kernel:file#open/2">file:open/2</seealso> function)</p>
</item>
</taglist>
@@ -289,6 +287,7 @@
<func>
<name name="read" arity="1"/>
<name name="read" arity="2"/>
+ <type name="server_no_data"/>
<fsummary>Read a term</fsummary>
<desc>
<p>Reads a term <c><anno>Term</anno></c> from the standard input
@@ -312,21 +311,25 @@
</func>
<func>
<name name="read" arity="3"/>
+ <name name="read" arity="4"/>
+ <type name="server_no_data"/>
<fsummary>Read a term</fsummary>
<desc>
<p>Reads a term <c><anno>Term</anno></c> from <c><anno>IoDevice</anno></c>, prompting it
- with <c><anno>Prompt</anno></c>. Reading starts at line number
- <c><anno>StartLine</anno></c>. It returns:</p>
+ with <c><anno>Prompt</anno></c>. Reading starts at location
+ <c><anno>StartLocation</anno></c>. The argument
+ <c><anno>Options</anno></c> is passed on as the <c>Options</c>
+ argument of the <c>erl_scan:tokens/4</c> function. It returns:</p>
<taglist>
- <tag><c>{ok, Term, <anno>EndLine</anno>}</c></tag>
+ <tag><c>{ok, Term, <anno>EndLocation</anno>}</c></tag>
<item>
<p>The parsing was successful.</p>
</item>
- <tag><c>{eof, <anno>EndLine</anno>}</c></tag>
+ <tag><c>{eof, <anno>EndLocation</anno>}</c></tag>
<item>
<p>End of file was encountered.</p>
</item>
- <tag><c>{error, <anno>ErrorInfo</anno>, <anno>ErrorLine</anno>}</c></tag>
+ <tag><c>{error, <anno>ErrorInfo</anno>, <anno>ErrorLocation</anno>}</c></tag>
<item>
<p>The parsing failed.</p>
</item>
@@ -377,7 +380,7 @@ ok</pre>
applicable, it is used for both the field width and precision.
The default padding character is <c>' '</c> (space).</p>
<p><c>Mod</c> is the control sequence modifier. It is either a
- single character (currently only 't', for unicode translation,
+ single character (currently only <c>t</c>, for Unicode translation,
is supported) that changes the interpretation of Data.</p>
<p>The following control sequences are available:</p>
@@ -397,9 +400,9 @@ ok</pre>
2> <input>io:fwrite("|~10.5c|~-10.5c|~5c|~n", [$a, $b, $c]).</input>
| aaaaa|bbbbb |ccccc|
ok</pre>
- <p>If the Unicode translation modifier ('t') is in effect,
+ <p>If the Unicode translation modifier (<c>t</c>) is in effect,
the integer argument can be any number representing a
- valid unicode codepoint, otherwise it should be an integer
+ valid Unicode codepoint, otherwise it should be an integer
less than or equal to 255, otherwise it is masked with 16#FF:</p>
<pre>
1> <input>io:fwrite("~tc~n",[1024]).</input>
@@ -439,7 +442,7 @@ ok</pre>
<item>
<p>Prints the argument with the <c>string</c> syntax. The
argument is, if no Unicode translation modifier is present, an
- iolist(), a binary, or an atom. If the Unicode translation modifier ('t') is in effect, the argument is unicode:chardata(), meaning that binaries are in UTF-8. The characters
+ iolist(), a binary, or an atom. If the Unicode translation modifier (<c>t</c>) is in effect, the argument is unicode:chardata(), meaning that binaries are in UTF-8. The characters
are printed without quotes. The string is first truncated
by the given precision and then padded and justified
to the given field width. The default precision is the field width.</p>
@@ -476,8 +479,10 @@ ok
<p>Writes the data with standard syntax in the same way as
<c>~w</c>, but breaks terms whose printed representation
is longer than one line into many lines and indents each
- line sensibly. It also tries to detect lists of printable
- characters and to output these as strings. For example:</p>
+ line sensibly. It also tries to detect lists of
+ printable characters and to output these as strings. The
+ Unicode translation modifier is used for determining
+ what characters are printable. For example:</p>
<pre>
5> <input>T = [{attributes,[[{id,age,1.50000},{mode,explicit},</input>
<input>{typename,"INTEGER"}], [{id,cho},{mode,explicit},{typename,'Cho'}]]},</input>
@@ -516,6 +521,19 @@ Here T = [{attributes,[[{id,age,1.5},
{tag,{'PRIVATE',3}},
{mode,implicit}]
ok</pre>
+ <p>Binaries that look like UTF-8 encoded strings will be
+ output with the string syntax if the Unicode translation
+ modifier is given:</p>
+ <pre>
+9> <input>io:fwrite("~p~n",[[1024]]).</input>
+[1024]
+10> <input>io:fwrite("~tp~n",[[1024]]).</input>
+"\x{400}"
+11> <input>io:fwrite("~tp~n", [&lt;&lt;128,128&gt;&gt;]).</input>
+&lt;&lt;128,128&gt;&gt;
+12> <input>io:fwrite("~tp~n", [&lt;&lt;208,128&gt;&gt;]).</input>
+&lt;&lt;"\x{400}"/utf8&gt;&gt;
+ok</pre>
</item>
<tag><c>W</c></tag>
<item>
@@ -583,7 +601,7 @@ ok</pre>
<tag><c>#</c></tag>
<item>
<p>Like <c>B</c>, but prints the number with an Erlang style
- '#'-separated base prefix.</p>
+ <c>#</c>-separated base prefix.</p>
<pre>
16> <input>io:fwrite("~.10#~n", [31]).</input>
10#31
@@ -633,13 +651,14 @@ ok
{shell,eval_loop,3}]}
in function io:o_request/2</pre>
<p>In this example, an attempt was made to output the single
- character '65' with the aid of the string formatting directive
+ character 65 with the aid of the string formatting directive
"~s".</p>
</desc>
</func>
<func>
<name name="fread" arity="2"/>
<name name="fread" arity="3"/>
+ <type name="server_no_data"/>
<fsummary>Read formatted input</fsummary>
<desc>
<p>Reads characters from the standard input (<c><anno>IoDevice</anno></c>),
@@ -664,7 +683,7 @@ ok
return suppression character. It provides a method to
specify a field which is to be omitted. <c>F</c> is the
<c>field width</c> of the input field, <c>M</c> is an optional
- translation modifier (of which 't' is the only currently
+ translation modifier (of which <c>t</c> is the only currently
supported, meaning Unicode translation) and <c>C</c>
determines the type of control sequence.</p>
@@ -690,8 +709,8 @@ ok
<tag><c>-</c></tag>
<item>
<p>An optional sign character is expected. A sign
- character '-' gives the return value <c>-1</c>. Sign
- character '+' or none gives <c>1</c>. The field width
+ character <c>-</c> gives the return value <c>-1</c>. Sign
+ character <c>+</c> or none gives <c>1</c>. The field width
parameter is ignored. Leading white-space characters
are not skipped.</p>
</item>
@@ -713,7 +732,7 @@ ok
characters are stripped. An Erlang string (list of
characters) is returned.</p>
- <p>If Unicode translation is in effect (~ts),
+ <p>If Unicode translation is in effect (<c>~ts</c>),
characters larger than 255 are accepted, otherwise
not. With the translation modifier, the list
returned may as a consequence also contain
@@ -769,10 +788,15 @@ Prompt> <input>&lt;Character beyond latin1 range not printable in this medium&gt
<item>
<p>End of file was encountered.</p>
</item>
- <tag><c>{error, <anno>What</anno>}</c></tag>
+ <tag><c>{error, <anno>FreadError</anno>}</c></tag>
+ <item>
+ <p>The reading failed and <c>FreadError</c> gives a
+ hint about the error.</p>
+ </item>
+ <tag><c>{error, <anno>ErrorDescription</anno>}</c></tag>
<item>
<p>The read operation failed and the parameter
- <c><anno>What</anno></c> gives a hint about the error.</p>
+ <c><anno>ErrorDescription</anno></c> gives a hint about the error.</p>
</item>
</taglist>
</item>
@@ -793,11 +817,11 @@ enter><input>:</input> <input>alan</input> <input>:</input> <input>joe</in
<func>
<name name="rows" arity="0"/>
<name name="rows" arity="1"/>
- <fsummary>Get the number of rows of a device</fsummary>
+ <fsummary>Get the number of rows of an IO device</fsummary>
<desc>
<p>Retrieves the number of rows of the
<c><anno>IoDevice</anno></c> (i.e. the height of a terminal). The function
- only succeeds for terminal devices, for all other devices
+ only succeeds for terminal devices, for all other IO devices
the function returns <c>{error, enotsup}</c></p>
</desc>
</func>
@@ -805,23 +829,28 @@ enter><input>:</input> <input>alan</input> <input>:</input> <input>joe</in
<name name="scan_erl_exprs" arity="1"/>
<name name="scan_erl_exprs" arity="2"/>
<name name="scan_erl_exprs" arity="3"/>
+ <name name="scan_erl_exprs" arity="4"/>
+ <type name="server_no_data"/>
<fsummary>Read and tokenize Erlang expressions</fsummary>
<desc>
<p>Reads data from the standard input (<c>IoDevice</c>),
- prompting it with <c>Prompt</c>. Reading starts at line number
- <c>StartLine</c> (1). The data is tokenized as if it were a
- sequence of Erlang expressions until a final <c>'.'</c> is
+ prompting it with <c>Prompt</c>. Reading starts at location
+ <c>StartLocation</c> (<c>1</c>). The argument <c><anno>Options</anno></c>
+ is passed on as the <c>Options</c> argument of the
+ <c>erl_scan:tokens/4</c> function. The data is tokenized as if
+ it were a
+ sequence of Erlang expressions until a final dot (<c>.</c>) is
reached. This token is also returned. It returns:</p>
<taglist>
- <tag><c>{ok, Tokens, EndLine}</c></tag>
+ <tag><c>{ok, Tokens, EndLocation}</c></tag>
<item>
<p>The tokenization succeeded.</p>
</item>
- <tag><c>{eof, EndLine}</c></tag>
+ <tag><c>{eof, EndLocation}</c></tag>
<item>
<p>End of file was encountered.</p>
</item>
- <tag><c>{error, ErrorInfo, ErrorLine}</c></tag>
+ <tag><c>{error, ErrorInfo, ErrorLocation}</c></tag>
<item>
<p>An error occurred.</p>
</item>
@@ -840,13 +869,18 @@ enter><input>1.0er.</input>
<name name="scan_erl_form" arity="1"/>
<name name="scan_erl_form" arity="2"/>
<name name="scan_erl_form" arity="3"/>
+ <name name="scan_erl_form" arity="4"/>
+ <type name="server_no_data"/>
<fsummary>Read and tokenize an Erlang form</fsummary>
<desc>
<p>Reads data from the standard input (<c><anno>IoDevice</anno></c>),
- prompting it with <c><anno>Prompt</anno></c>. Starts reading at line number
- <c><anno>StartLine</anno></c> (1). The data is tokenized as if it were an
+ prompting it with <c><anno>Prompt</anno></c>. Starts reading
+ at location <c><anno>StartLocation</anno></c> (<c>1</c>). The
+ argument <c><anno>Options</anno></c> is passed on as the
+ <c>Options</c> argument of the <c>erl_scan:tokens/4</c>
+ function. The data is tokenized as if it were an
Erlang form - one of the valid Erlang expressions in an
- Erlang source file - until a final <c>'.'</c> is reached.
+ Erlang source file - until a final dot (<c>.</c>) is reached.
This last token is also returned. The return values are the
same as for <c>scan_erl_exprs/1,2,3</c> above.</p>
</desc>
@@ -855,24 +889,30 @@ enter><input>1.0er.</input>
<name name="parse_erl_exprs" arity="1"/>
<name name="parse_erl_exprs" arity="2"/>
<name name="parse_erl_exprs" arity="3"/>
+ <name name="parse_erl_exprs" arity="4"/>
<type name="parse_ret"/>
+ <type name="server_no_data"/>
<fsummary>Read, tokenize and parse Erlang expressions</fsummary>
<desc>
- <p>Reads data from the standard input (<c><anno>IoDevice</anno></c>),
- prompting it with <c><anno>Prompt</anno></c>. Starts reading at line number
- <c><anno>StartLine</anno></c> (1). The data is tokenized and parsed as if
- it were a sequence of Erlang expressions until a final '.' is
- reached. It returns:</p>
+ <p>Reads data from the standard input
+ (<c><anno>IoDevice</anno></c>), prompting it with
+ <c><anno>Prompt</anno></c>. Starts reading at location
+ <c><anno>StartLocation</anno></c> (<c>1</c>). The argument
+ <c><anno>Options</anno></c> is passed on as the
+ <c>Options</c> argument of the <c>erl_scan:tokens/4</c>
+ function. The data is tokenized and parsed as if it were a
+ sequence of Erlang expressions until a final dot (<c>.</c>) is reached.
+ It returns:</p>
<taglist>
- <tag><c>{ok, ExprList, EndLine}</c></tag>
+ <tag><c>{ok, ExprList, EndLocation}</c></tag>
<item>
<p>The parsing was successful.</p>
</item>
- <tag><c>{eof, EndLine}</c></tag>
+ <tag><c>{eof, EndLocation}</c></tag>
<item>
<p>End of file was encountered.</p>
</item>
- <tag><c>{error, ErrorInfo, ErrorLine}</c></tag>
+ <tag><c>{error, ErrorInfo, ErrorLocation}</c></tag>
<item>
<p>An error occurred.</p>
</item>
@@ -891,25 +931,30 @@ enter><input>abc("hey".</input>
<name name="parse_erl_form" arity="1"/>
<name name="parse_erl_form" arity="2"/>
<name name="parse_erl_form" arity="3"/>
+ <name name="parse_erl_form" arity="4"/>
<type name="parse_form_ret"/>
+ <type name="server_no_data"/>
<fsummary>Read, tokenize and parse an Erlang form</fsummary>
<desc>
<p>Reads data from the standard input (<c><anno>IoDevice</anno></c>),
- prompting it with <c><anno>Prompt</anno></c>. Starts reading at line number
- <c><anno>StartLine</anno></c> (1). The data is tokenized and parsed as if
+ prompting it with <c><anno>Prompt</anno></c>. Starts reading at
+ location <c><anno>StartLocation</anno></c> (<c>1</c>). The argument
+ <c><anno>Options</anno></c> is passed on as the
+ <c>Options</c> argument of the <c>erl_scan:tokens/4</c>
+ function. The data is tokenized and parsed as if
it were an Erlang form - one of the valid Erlang expressions
- in an Erlang source file - until a final '.' is reached. It
+ in an Erlang source file - until a final dot (<c>.</c>) is reached. It
returns:</p>
<taglist>
- <tag><c>{ok, AbsForm, EndLine}</c></tag>
+ <tag><c>{ok, AbsForm, EndLocation}</c></tag>
<item>
<p>The parsing was successful.</p>
</item>
- <tag><c>{eof, EndLine}</c></tag>
+ <tag><c>{eof, EndLocation}</c></tag>
<item>
<p>End of file was encountered.</p>
</item>
- <tag><c>{error, ErrorInfo, ErrorLine}</c></tag>
+ <tag><c>{error, ErrorInfo, ErrorLocation}</c></tag>
<item>
<p>An error occurred.</p>
</item>
@@ -940,7 +985,7 @@ enter><input>bar.</input>
</section>
<section>
<title>Standard Error</title>
- <p>In certain situations, especially when the standard output is redirected, access to an io_server() specific for error messages might be convenient. The io_device 'standard_error' can be used to direct output to whatever the current operating system considers a suitable device for error output. Example on a Unix-like operating system:</p>
+ <p>In certain situations, especially when the standard output is redirected, access to an I/O-server specific for error messages might be convenient. The IO device <c>standard_error</c> can be used to direct output to whatever the current operating system considers a suitable IO device for error output. Example on a Unix-like operating system:</p>
<pre>
$ <input>erl -noshell -noinput -eval 'io:format(standard_error,"Error: ~s~n",["error 11"]),'\</input>
<input>'init:stop().' > /dev/null</input>
@@ -956,7 +1001,7 @@ Error: error 11</pre>
<c>ErrorInfo</c> structure which is returned from all IO modules.
It has the format:</p>
<code type="none">
-{ErrorLine, Module, ErrorDescriptor}</code>
+{ErrorLocation, Module, ErrorDescriptor}</code>
<p>A string which describes the error is obtained with the following
call:</p>
<code type="none">
diff --git a/lib/stdlib/doc/src/io_lib.xml b/lib/stdlib/doc/src/io_lib.xml
index 506c1792f1..617a6b74fc 100644
--- a/lib/stdlib/doc/src/io_lib.xml
+++ b/lib/stdlib/doc/src/io_lib.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2011</year>
+ <year>1996</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -43,6 +43,12 @@
<name name="chars"/>
</datatype>
<datatype>
+ <name name="unicode_chars"/>
+ </datatype>
+ <datatype>
+ <name name="unicode_string"/>
+ </datatype>
+ <datatype>
<name name="continuation"/>
<desc><p>A continuation as returned by <seealso marker="#fread/3"><c>fread/3</c></seealso>.</p>
</desc>
@@ -50,6 +56,9 @@
<datatype>
<name name="depth"/>
</datatype>
+ <datatype>
+ <name name="fread_error"/>
+ </datatype>
</datatypes>
<funcs>
<func>
@@ -209,6 +218,23 @@
</desc>
</func>
<func>
+ <name name="write_unicode_string" arity="1"/>
+ <fsummary>Write a Unicode string</fsummary>
+ <desc>
+ <p>Returns the list of characters needed to print
+ <c><anno>UnicodeString</anno></c> as a string.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="write_unicode_string_as_latin1" arity="1"/>
+ <fsummary>Write a Unicode string</fsummary>
+ <desc>
+ <p>Returns the list of characters needed to print
+ <c><anno>UnicodeString</anno></c> as a string. Non-Latin-1
+ characters are escaped.</p>
+ </desc>
+ </func>
+ <func>
<name name="write_char" arity="1"/>
<fsummary>Write a character</fsummary>
<desc>
@@ -217,6 +243,23 @@
</desc>
</func>
<func>
+ <name name="write_unicode_char" arity="1"/>
+ <fsummary>Write a Unicode character</fsummary>
+ <desc>
+ <p>Returns the list of characters needed to print a character
+ constant in the Unicode character set.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="write_unicode_char_as_latin1" arity="1"/>
+ <fsummary>Write a Unicode character</fsummary>
+ <desc>
+ <p>Returns the list of characters needed to print a character
+ constant in the Unicode character set. Non-Latin-1 characters
+ are escaped.</p>
+ </desc>
+ </func>
+ <func>
<name name="indentation" arity="2"/>
<fsummary>Indentation after printing string</fsummary>
<desc>
@@ -233,6 +276,14 @@
</desc>
</func>
<func>
+ <name name="unicode_char_list" arity="1"/>
+ <fsummary>Test for a list of Unicode characters</fsummary>
+ <desc>
+ <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a flat list of
+ characters in the Unicode range, otherwise it returns <c>false</c>.</p>
+ </desc>
+ </func>
+ <func>
<name name="deep_char_list" arity="1"/>
<fsummary>Test for a deep list of characters</fsummary>
<desc>
@@ -241,6 +292,14 @@
</desc>
</func>
<func>
+ <name name="deep_unicode_char_list" arity="1"/>
+ <fsummary>Test for a deep list of Unicode characters</fsummary>
+ <desc>
+ <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a, possibly deep, list
+ of characters in the Unicode range, otherwise it returns <c>false</c>.</p>
+ </desc>
+ </func>
+ <func>
<name name="printable_list" arity="1"/>
<fsummary>Test for a list of printable ISO-latin-1 characters</fsummary>
<desc>
@@ -248,6 +307,14 @@
printable ISO-latin-1 characters, otherwise it returns <c>false</c>.</p>
</desc>
</func>
+ <func>
+ <name name="printable_unicode_list" arity="1"/>
+ <fsummary>Test for a list of printable Unicode characters</fsummary>
+ <desc>
+ <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a flat list of
+ printable Unicode characters, otherwise it returns <c>false</c>.</p>
+ </desc>
+ </func>
</funcs>
</erlref>
diff --git a/lib/stdlib/doc/src/io_protocol.xml b/lib/stdlib/doc/src/io_protocol.xml
index 0ff3d5c1ee..d36bf2042f 100644
--- a/lib/stdlib/doc/src/io_protocol.xml
+++ b/lib/stdlib/doc/src/io_protocol.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1999</year>
- <year>2011</year>
+ <year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -35,10 +35,10 @@
<p>The I/O-protocol in Erlang specifies a way for a client to communicate
-with an io_server and vice versa. The io_server is a process handling the
-requests and that performs the requested task on i.e. a device. The
+with an I/O server and vice versa. The I/O server is a process that handles
+the requests and performs the requested task on e.g. an IO device. The
client is any Erlang process wishing to read or write data from/to the
-device.</p>
+IO device.</p>
<p>The common I/O-protocol has been present in OTP since the
beginning, but has been fairly undocumented and has also somewhat
@@ -53,85 +53,85 @@ implement than the original. It can certainly be argumented that the
current protocol is too complex, but this text describes how it looks
today, not how it should have looked.</p>
-<p>The basic ideas from the original protocol still hold. The io_server
+<p>The basic ideas from the original protocol still hold. The I/O server
and client communicate with one single, rather simplistic protocol and
-no server state is ever present in the client. Any io_server can be
+no server state is ever present in the client. Any I/O server can be
used together with any client code and client code need not be aware
-of the actual device the io_server communicates with.</p>
+of the actual IO device the I/O server communicates with.</p>
<section>
-<title>Protocol basics</title>
+<title>Protocol Basics</title>
-<p>As described in Robert's paper, servers and clients communicate using
-io_request/io_reply tuples as follows:</p>
+<p>As described in Robert's paper, I/O servers and clients communicate using
+<c>io_request</c>/<c>io_reply</c> tuples as follows:</p>
<p><em>{io_request, From, ReplyAs, Request}</em><br/>
<em>{io_reply, ReplyAs, Reply}</em></p>
-<p>The client sends an io_request to the io_server and the server
-eventually sends a corresponding reply.</p>
+<p>The client sends an <c>io_request</c> tuple to the I/O server and
+the server eventually sends a corresponding <c>io_reply</c> tuple.</p>
<list type="bulleted">
-<item>From is the pid() of the client, the process which the io_server
-sends the reply to.</item>
-
-<item>ReplyAs can be any datum and is simply returned in the corresponding
-io_reply. The io-module in the Erlang standard library simply uses the pid()
-of the io_server as the ReplyAs datum, but a more complicated client
-could have several outstanding io-requests to the same server and
-would then use i.e. a reference() or something else to differentiate among
-the incoming io_reply's. The ReplyAs element should be considered
-opaque by the io_server. Note that the pid() of the server is not
-explicitly present in the io_reply. The reply can be sent from any
-process, not necessarily the actual io_server. The ReplyAs element is
-the only thing that connects one io_request with an io_reply.</item>
-
-<item>Request and Reply are described below.</item>
+<item><c>From</c> is the <c>pid()</c> of the client, the process which
+the I/O server sends the IO reply to.</item>
+
+<item><c>ReplyAs</c> can be any datum and is returned in the corresponding
+<c>io_reply</c>. The <seealso marker="stdlib:io">io</seealso> module simply uses the pid()
+of the I/O server as the <c>ReplyAs</c> datum, but a more complicated client
+could have several outstanding I/O requests to the same I/O server and
+would then use i.e. a <c>reference()</c> or something else to differentiate among
+the incoming IO replies. The <c>ReplyAs</c> element should be considered
+opaque by the I/O server. Note that the <c>pid()</c> of the I/O server is not
+explicitly present in the <c>io_reply</c> tuple. The reply can be sent from any
+process, not necessarily the actual I/O server. The <c>ReplyAs</c> element is
+the only thing that connects one I/O request with an I/O-reply.</item>
+
+<item><c>Request</c> and <c>Reply</c> are described below.</item>
</list>
-<p>When an io_server receives an io_request, it acts upon the actual
-Request part and eventually sends an io_reply with the corresponding
-Reply part.</p>
+<p>When an I/O server receives an <c>io_request</c> tuple, it acts upon the actual
+<c>Request</c> part and eventually sends an <c>io_reply</c> tuple with the corresponding
+<c>Reply</c> part.</p>
</section>
<section>
-<title>Output requests</title>
+<title>Output Requests</title>
-<p>To output characters on a device, the following Requests exist:</p>
+<p>To output characters on an IO device, the following <c>Request</c>s exist:</p>
<p>
<em>{put_chars, Encoding, Characters}</em><br/>
<em>{put_chars, Encoding, Module, Function, Args}</em>
</p>
<list type="bulleted">
-<item>Encoding is either 'unicode' or 'latin1', meaning that the
+<item><c>Encoding</c> is either <c>unicode</c> or <c>latin1</c>, meaning that the
characters are (in case of binaries) encoded as either UTF-8 or
- iso-latin-1 (pure bytes). A well behaved io_server should also
- return error if list elements contain integers > 255 when the
- Encoding is set to latin1. Note that this does not in any way tell
- how characters should be put on the actual device or how the
- io_server should handle them. Different io_servers may handle the
- characters however they want, this simply tells the io_server which
- format the data is expected to have. In the Module/Function/argument
- case, the Encoding tells which format the designated function
- produces. Note that byte-oriented data is simplest sent using latin1
- Encoding</item>
-
-<item>Characters are the data to be put on the device. If Encoding is
- latin1, this is an iolist(). If Encoding is unicode, this is an
- Erlang standard mixed unicode list (one integer in a list per
+ ISO-latin-1 (pure bytes). A well behaved I/O server should also
+ return error if list elements contain integers > 255 when
+ <c>Encoding</c> is set to <c>latin1</c>. Note that this does not in any way tell
+ how characters should be put on the actual IO device or how the
+ I/O server should handle them. Different I/O servers may handle the
+ characters however they want, this simply tells the I/O server which
+ format the data is expected to have. In the <c>Module</c>/<c>Function</c>/<c>Args</c>
+ case, <c>Encoding</c> tells which format the designated function
+ produces. Note that byte-oriented data is simplest sent using the ISO-latin-1
+ encoding.</item>
+
+<item>Characters are the data to be put on the IO device. If <c>Encoding</c> is
+ <c>latin1</c>, this is an <c>iolist()</c>. If <c>Encoding</c> is <c>unicode</c>, this is an
+ Erlang standard mixed Unicode list (one integer in a list per
character, characters in binaries represented as UTF-8).</item>
-<item>Module, Function, Args denotes a function which will be called to
- produce the data (like io_lib:format). Args is a list of arguments
+<item><c>Module</c>, <c>Function</c>, and <c>Args</c> denote a function which will be called to
+ produce the data (like <c>io_lib:format/2</c>). <c>Args</c> is a list of arguments
to the function. The function should produce data in the given
- Encoding. The io_server should call the function as apply(Mod, Func,
- Args) and will put the returned data on the device as if it was sent
- in a {put_chars, Encoding, Characters} request. If the function
+ <c>Encoding</c>. The I/O server should call the function as
+ <c>apply(Mod, Func, Args)</c> and will put the returned data on the IO device as if it was sent
+ in a <c>{put_chars, Encoding, Characters}</c> request. If the function
returns anything else than a binary or list or throws an exception,
an error should be sent back to the client.</item>
</list>
-<p>The server replies to the client with an io_reply where the Reply
+<p>The I/O server replies to the client with an <c>io_reply</c> tuple where the <c>Reply</c>
element is one of:</p>
<p>
<em>ok</em><br/>
@@ -139,49 +139,50 @@ element is one of:</p>
</p>
<list type="bulleted">
-<item>Error describes the error to the client, which may do whatever it
- wants with it. The Erlang io-module typically returns it as is.</item>
+<item><c>Error</c> describes the error to the client, which may do whatever
+ it wants with it. The Erlang <seealso marker="stdlib:io">io</seealso>
+ module typically returns it as is.</item>
</list>
-<p>For backward compatibility the following Requests should also be
-handled by an io_server (these messages should not be present after
+<p>For backward compatibility the following <c>Request</c>s should also be
+handled by an I/O server (these requests should not be present after
R15B of OTP):</p>
<p>
<em>{put_chars, Characters}</em><br/>
<em>{put_chars, Module, Function, Args}</em>
</p>
-<p>These should behave as {put_chars, latin1, Characters} and {put_chars,
-latin1, Module, Function, Args} respectively. </p>
+<p>These should behave as <c>{put_chars, latin1, Characters}</c> and
+<c>{put_chars, latin1, Module, Function, Args}</c> respectively. </p>
</section>
<section>
<title>Input Requests</title>
-<p>To read characters from a device, the following Requests exist:</p>
+<p>To read characters from an IO device, the following <c>Request</c>s exist:</p>
<p><em>{get_until, Encoding, Prompt, Module, Function, ExtraArgs}</em></p>
<list type="bulleted">
-<item>Encoding denotes how data is to be sent back to the client and
+<item><c>Encoding</c> denotes how data is to be sent back to the client and
what data is sent to the function denoted by
- Module/Function/ExtraArgs. If the function supplied returns data as a
+ <c>Module</c>/<c>Function</c>/<c>ExtraArgs</c>. If the function supplied returns data as a
list, the data is converted to this encoding. If however the
function supplied returns data in some other format, no conversion
- can be done and it's up to the client supplied function to return
- data in a proper way. If Encoding is latin1, lists of integers
+ can be done and it is up to the client supplied function to return
+ data in a proper way. If <c>Encoding</c> is <c>latin1</c>, lists of integers
0..255 or binaries containing plain bytes are sent back to the
- client when possible, if Encoding is unicode, lists with integers in
- the whole unicode range or binaries encoded in UTF-8 are sent to the
+ client when possible; if <c>Encoding</c> is <c>unicode</c>, lists with integers in
+ the whole Unicode range or binaries encoded in UTF-8 are sent to the
client. The user supplied function will always see lists of integers, never
- binaries, but the list may contain numbers > 255 if the Encoding is
- 'unicode'.</item>
+ binaries, but the list may contain numbers > 255 if the <c>Encoding</c> is
+ <c>unicode</c>.</item>
-<item>Prompt is a list of characters (not mixed, no binaries) or an atom()
- to be output as a prompt for input on the device. The Prompt is
- often ignored by the io_server and a Prompt set to '' should always
- be ignored (and result in nothing being written to the device).</item>
+<item><c>Prompt</c> is a list of characters (not mixed, no binaries) or an atom
+ to be output as a prompt for input on the IO device. <c>Prompt</c> is
+ often ignored by the I/O server and if set to <c>''</c> it should always
+ be ignored (and result in nothing being written to the IO device).</item>
-<item><p>Module, Function, ExtraArgs denotes a function and arguments to
+<item><p><c>Module</c>, <c>Function</c>, and <c>ExtraArgs</c> denote a function and arguments to
determine when enough data is written. The function should take two
additional arguments, the last state, and a list of characters. The
function should return one of:</p>
@@ -189,23 +190,23 @@ latin1, Module, Function, Args} respectively. </p>
<em>{done, Result, RestChars}</em><br/>
<em>{more, Continuation}</em>
</p>
- <p>The Result can be any Erlang term, but if it is a list(), the
- io_server may convert it to a binary() of appropriate format before
- returning it to the client, if the server is set in binary mode (see
+ <p>The <c>Result</c> can be any Erlang term, but if it is a <c>list()</c>, the
+ I/O server may convert it to a <c>binary()</c> of appropriate format before
+ returning it to the client, if the I/O server is set in binary mode (see
below).</p>
- <p>The function will be called with the data the io_server finds on
- its device, returning {done, Result, RestChars} when enough data is
- read (in which case Result is sent to the client and RestChars are
- kept in the io_server as a buffer for subsequent input) or {more,
- Continuation}, indicating that more characters are needed to
- complete the request. The Continuation will be sent as the state in
+ <p>The function will be called with the data the I/O server finds on
+ its IO device, returning <c>{done, Result, RestChars}</c> when enough data is
+ read (in which case <c>Result</c> is sent to the client and <c>RestChars</c> is
+ kept in the I/O server as a buffer for subsequent input) or
+ <c>{more, Continuation}</c>, indicating that more characters are needed to
+ complete the request. The <c>Continuation</c> will be sent as the state in
subsequent calls to the function when more characters are
available. When no more characters are available, the function
- shall return {done,eof,Rest}.
+ shall return <c>{done, eof, Rest}</c>.
The initial state is the empty list and the data when an
- end of file is reached on the device is the atom 'eof'. An emulation
- of the get_line request could be (inefficiently) implemented using
+ end of file is reached on the IO device is the atom <c>eof</c>. An emulation
+ of the <c>get_line</c> request could be (inefficiently) implemented using
the following functions:</p>
<code>
-module(demo).
@@ -214,7 +215,9 @@ latin1, Module, Function, Args} respectively. </p>
until_newline(_ThisFar,eof,_MyStopCharacter) -&gt;
{done,eof,[]};
until_newline(ThisFar,CharList,MyStopCharacter) -&gt;
- case lists:splitwith(fun(X) -&gt; X =/= MyStopCharacter end, CharList) of
+ case
+ lists:splitwith(fun(X) -&gt; X =/= MyStopCharacter end, CharList)
+ of
{L,[]} -&gt;
{more,ThisFar++L};
{L2,[MyStopCharacter|Rest]} -&gt;
@@ -222,45 +225,47 @@ until_newline(ThisFar,CharList,MyStopCharacter) -&gt;
end.
get_line(IoServer) -&gt;
- IoServer ! {io_request, self(), IoServer, {get_until, unicode, '',
- ?MODULE, until_newline, [$\n]}},
+ IoServer ! {io_request,
+ self(),
+ IoServer,
+ {get_until, unicode, '', ?MODULE, until_newline, [$\n]}},
receive
{io_reply, IoServer, Data} -&gt;
Data
end.
</code>
- <p>Note especially that the last element in the Request tuple ([$\n])
+ <p>Note especially that the last element in the <c>Request</c> tuple (<c>[$\n]</c>)
is appended to the argument list when the function is called. The
function should be called like
- apply(Module, Function, [ State, Data | ExtraArgs ]) by the io_server</p>
+ <c>apply(Module, Function, [ State, Data | ExtraArgs ])</c> by the I/O server</p>
</item>
</list>
-<p>A defined number of characters is requested using this Request:</p>
+<p>A fixed number of characters is requested using this <c>Request</c>:</p>
<p>
<em>{get_chars, Encoding, Prompt, N}</em>
</p>
<list type="bulleted">
-<item>Encoding and Prompt as for get_until.</item>
+<item><c>Encoding</c> and <c>Prompt</c> as for <c>get_until</c>.</item>
-<item>N is the number of characters to be read from the device.</item>
+<item><c>N</c> is the number of characters to be read from the IO device.</item>
</list>
-<p>A single line (like in the example above) is requested with this Request:</p>
+<p>A single line (like in the example above) is requested with this <c>Request</c>:</p>
<p>
<em>{get_line, Encoding, Prompt}</em>
</p>
<list type="bulleted">
-<item>Encoding and prompt as above.</item>
+<item><c>Encoding</c> and <c>Prompt</c> as above.</item>
</list>
-<p>Obviously, get_chars and get_line could be implemented with the
-get_until request (and indeed was originally), but demands for
+<p>Obviously, the <c>get_chars</c> and <c>get_line</c> could be implemented with the
+<c>get_until</c> request (and indeed they were originally), but demands for
efficiency has made these additions necessary.</p>
-<p>The server replies to the client with an io_reply where the Reply
+<p>The I/O server replies to the client with an <c>io_reply</c> tuple where the <c>Reply</c>
element is one of:</p>
<p>
<em>Data</em><br/>
@@ -269,16 +274,17 @@ element is one of:</p>
</p>
<list type="bulleted">
-<item>Data is the characters read, in either list or binary form
- (depending on the io_server mode, see below).</item>
-<item>Error describes the error to the client, which may do whatever it
- wants with it. The Erlang io-module typically returns it as is.</item>
-<item>eof is returned when input end is reached and no more data is
+<item><c>Data</c> is the characters read, in either list or binary form
+ (depending on the I/O server mode, see below).</item>
+<item><c>Error</c> describes the error to the client, which may do whatever it
+ wants with it. The Erlang <seealso marker="stdlib:io">io</seealso>
+ module typically returns it as is.</item>
+<item><c>eof</c> is returned when input end is reached and no more data is
available to the client process.</item>
</list>
-<p>For backward compatibility the following Requests should also be
-handled by an io_server (these messages should not be present after
+<p>For backward compatibility the following <c>Request</c>s should also be
+handled by an I/O server (these reqeusts should not be present after
R15B of OTP):</p>
<p>
@@ -287,30 +293,30 @@ R15B of OTP):</p>
<em>{get_line, Prompt}</em><br/>
</p>
-<p>These should behave as {get_until, latin1, Prompt, Module, Function,
-ExtraArgs}, {get_chars, latin1, Prompt, N} and {get_line, latin1,
-Prompt} respectively.</p>
+<p>These should behave as <c>{get_until, latin1, Prompt, Module, Function,
+ExtraArgs}</c>, <c>{get_chars, latin1, Prompt, N}</c> and <c>{get_line, latin1,
+Prompt}</c> respectively.</p>
</section>
<section>
-<title>I/O-server modes</title>
-
-<p>Demands for efficiency when reading data from an io_server has not
-only lead to the addition of the get_line and get_chars requests, but
-has also added the concept of io_server options. No options are
-mandatory to implement, but all io_servers in the Erlang standard
-libraries honor the 'binary' option, which allows the Data in the
-io_reply to be binary instead of in list form <em>when possible</em>.
+<title>I/O-server Modes</title>
+
+<p>Demands for efficiency when reading data from an I/O server has not
+only lead to the addition of the <c>get_line</c> and <c>get_chars</c> requests, but
+has also added the concept of I/O server options. No options are
+mandatory to implement, but all I/O servers in the Erlang standard
+libraries honor the <c>binary</c> option, which allows the <c>Data</c> element of the
+<c>io_reply</c> tuple to be a binary instead of a list <em>when possible</em>.
If the data is sent as a binary, Unicode data will be sent in the
-standard Erlang unicode
-format, i.e. UTF-8 (note that the function in get_until still gets
-list data regardless of the io_server mode).</p>
+standard Erlang Unicode
+format, i.e. UTF-8 (note that the function of the <c>get_until</c> request still gets
+list data regardless of the I/O server mode).</p>
-<p>Note that i.e. the <c>get_until</c> request allows for a function with the data specified as always being a list. Also the return value data from such a function can be of any type (as is indeed the case when an io:fread request is sent to an io_server). The client has to be prepared for data received as answers to those requests to be in a variety of forms, but the server should convert the results to binaries whenever possible (i.e. when the function supplied to get_until actually returns a list). The example shown later in this text does just that.</p>
+<p>Note that i.e. the <c>get_until</c> request allows for a function with the data specified as always being a list. Also the return value data from such a function can be of any type (as is indeed the case when an <c>io:fread</c> request is sent to an I/O server). The client has to be prepared for data received as answers to those requests to be in a variety of forms, but the I/O server should convert the results to binaries whenever possible (i.e. when the function supplied to <c>get_until</c> actually returns a list). The example shown later in this text does just that.</p>
<p>An I/O-server in binary mode will affect the data sent to the client,
so that it has to be able to handle binary data. For convenience, it
-is possible to set and retrieve the modes of an io_server using the
-following I/O-requests:</p>
+is possible to set and retrieve the modes of an I/O server using the
+following I/O requests:</p>
<p>
<em>{setopts, Opts}</em>
@@ -318,72 +324,72 @@ following I/O-requests:</p>
<list type="bulleted">
-<item>Opts is a list of options in the format recognized by proplists (and
- of course by the io_server itself).</item>
+<item><c>Opts</c> is a list of options in the format recognized by <seealso marker="stdlib:proplists">proplists</seealso> (and
+ of course by the I/O server itself).</item>
</list>
-<p>As an example, the io_server for the interactive shell (in group.erl)
+<p>As an example, the I/O server for the interactive shell (in <c>group.erl</c>)
understands the following options:</p>
<p>
-<em>{binary, bool()} (or 'binary'/'list')</em><br/>
-<em>{echo, bool()}</em><br/>
+<em>{binary, boolean()}</em> (or <em>binary</em>/<em>list</em>)<br/>
+<em>{echo, boolean()}</em><br/>
<em>{expand_fun, fun()}</em><br/>
-<em>{encoding, 'unicode'/'latin1'} (or 'unicode'/'latin1')</em>
+<em>{encoding, unicode/latin1}</em> (or <em>unicode</em>/<em>latin1</em>)
</p>
-<p>- of which the 'binary' and 'encoding' options are common for all
-io_servers in OTP, while 'echo' and 'expand' is valid only for this
-io_server. It's worth noting that the 'unicode' option notifies how
-characters are actually put on the physical device, i.e. if the
-terminal per se is unicode aware, it does not affect how characters
+<p>- of which the <c>binary</c> and <c>encoding</c> options are common for all
+I/O servers in OTP, while <c>echo</c> and <c>expand</c> are valid only for this
+I/O server. It is worth noting that the <c>unicode</c> option notifies how
+characters are actually put on the physical IO device, i.e. if the
+terminal per se is Unicode aware, it does not affect how characters
are sent in the I/O-protocol, where each request contains encoding
information for the provided or returned data.</p>
-<p>The server should send one of the following as Reply:</p>
+<p>The I/O server should send one of the following as <c>Reply</c>:</p>
<p>
<em>ok</em><br/>
<em>{error, Error}</em>
</p>
-<p>An error (preferably enotsup) is to be expected if the option is
-not supported by the io_server (like if an 'echo' option is sent in a
-setopt Request to a plain file).</p>
+<p>An error (preferably <c>enotsup</c>) is to be expected if the option is
+not supported by the I/O server (like if an <c>echo</c> option is sent in a
+<c>setopts</c> request to a plain file).</p>
-<p>To retrieve options, this message is used:</p>
+<p>To retrieve options, this request is used:</p>
<p>
<em>getopts</em>
</p>
-<p>The 'getopts' message requests a complete list of all options
-supported by the io_server as well as their current values.</p>
+<p>The <c>getopts</c> request asks for a complete list of all options
+supported by the I/O server as well as their current values.</p>
-<p>The server replies:</p>
+<p>The I/O server replies:</p>
<p>
<em>OptList</em><br/>
-<em>{error,Error}</em>
+<em>{error, Error}</em>
</p>
<list type="bulleted">
-<item>OptList is a list of tuples {Option, Value} where Option is always
+<item><c>OptList</c> is a list of tuples <c>{Option, Value}</c> where <c>Option</c> is always
an atom.</item>
</list>
</section>
<section>
-<title>Multiple I/O requests</title>
+<title>Multiple I/O Requests</title>
-<p>The Request element can in itself contain several Requests by using
+<p>The <c>Request</c> element can in itself contain several <c>Request</c>s by using
the following format:</p>
<p>
<em>{requests, Requests}</em>
</p>
<list type="bulleted">
-<item>Requests is a list of valid Request tuples for the protocol, they
+<item><c>Requests</c> is a list of valid <c>io_request</c> tuples for the protocol, they
shall be executed in the order in which they appear in the list and
the execution should continue until one of the requests result in an
error or the list is consumed. The result of the last request is
sent back to the client.</item>
</list>
-<p>The server can for a list of requests send any of the valid results in
+<p>The I/O server can for a list of requests send any of the valid results in
the reply:</p>
<p>
@@ -395,7 +401,7 @@ the reply:</p>
<p>- depending on the actual requests in the list.</p>
</section>
<section>
-<title>Optional I/O-requests</title>
+<title>Optional I/O Requests</title>
<p>The following I/O request is optional to implement and a client
should be prepared for an error return:</p>
@@ -403,47 +409,47 @@ should be prepared for an error return:</p>
<em>{get_geometry, Geometry}</em>
</p>
<list type="bulleted">
-<item>Geometry is either the atom 'rows' or the atom 'columns'.</item>
+<item><c>Geometry</c> is either the atom <c>rows</c> or the atom <c>columns</c>.</item>
</list>
-<p>The server should send the Reply as:</p>
+<p>The I/O server should send the <c>Reply</c> as:</p>
<p>
<em>{ok, N}</em><br/>
<em>{error, Error}</em>
</p>
<list type="bulleted">
-<item>N is the number of character rows or columns the device has, if
- applicable to the device the io_server handles, otherwise {error,
- enotsup} is a good answer.</item>
+<item><c>N</c> is the number of character rows or columns the IO device has, if
+ applicable to the IO device the I/O server handles, otherwise <c>{error,
+ enotsup}</c> is a good answer.</item>
</list>
</section>
<section>
-<title>Unimplemented request types:</title>
+<title>Unimplemented Request Types</title>
-<p>If an io_server encounters a request it does not recognize (i.e. the
-io_request tuple is in the expected format, but the actual Request is
-unknown), the server should send a valid reply with the error tuple:</p>
+<p>If an I/O server encounters a request it does not recognize (i.e. the
+<c>io_request</c> tuple is in the expected format, but the actual <c>Request</c> is
+unknown), the I/O server should send a valid reply with the error tuple:</p>
<p>
<em>{error, request}</em>
</p>
-<p>This makes it possible to extend the protocol with optional messages
+<p>This makes it possible to extend the protocol with optional requests
and for the clients to be somewhat backwards compatible.</p>
</section>
<section>
-<title>An annotated and working example io_server:</title>
+<title>An Annotated and Working Example I/O Server</title>
-<p>An io_server is any process capable of handling the protocol. There is
-no generic io_server behavior, but could well be. The framework is
+<p>An I/O server is any process capable of handling the I/O protocol. There is
+no generic I/O server behavior, but could well be. The framework is
simple enough, a process handling incoming requests, usually both
-io_requests and other device-specific requests (for i.e. positioning ,
+I/O-requests and other IO device-specific requests (for i.e. positioning,
closing etc.).</p>
-<p>Our example io_server stores characters in an ets table, making up a
+<p>Our example I/O server stores characters in an ETS table, making up a
fairly crude ram-file (it is probably not useful, but working).</p>
<p>The module begins with the usual directives, a function to start the
-server and a main loop handling the requests:</p>
+I/O server and a main loop handling the requests:</p>
<code>
-module(ets_io_server).
@@ -486,18 +492,19 @@ loop(State) -&gt;
</code>
<p>The main loop receives messages from the client (which might be using
-the io-module to send requests). For each request the function
-request/2 is called and a reply is eventually sent using the reply/3
+the <seealso marker="stdlib:io">io</seealso> module to send requests).
+For each request the function
+<c>request/2</c> is called and a reply is eventually sent using the <c>reply/3</c>
function.</p>
-<p>The &quot;private&quot; message {From, rewind} results in the
+<p>The &quot;private&quot; message <c>{From, rewind}</c> results in the
current position in the pseudo-file to be reset to 0 (the beginning of
-the &quot;file&quot;). This is a typical example of device-specific
+the &quot;file&quot;). This is a typical example of IO device-specific
messages not being part of the I/O-protocol. It is usually a bad idea
-to embed such private messages in io_request tuples, as that might be
+to embed such private messages in <c>io_request</c> tuples, as that might be
confusing to the reader.</p>
-<p>Let's look at the reply function first...</p>
+<p>Let us look at the reply function first...</p>
<code>
@@ -506,8 +513,8 @@ reply(From, ReplyAs, Reply) -&gt;
</code>
-<p>Simple enough, it sends the io_reply tuple back to the client,
-providing the ReplyAs element received in the request along with the
+<p>Simple enough, it sends the <c>io_reply</c> tuple back to the client,
+providing the <c>ReplyAs</c> element received in the request along with the
result of the request, as described above.</p>
<p>Now look at the different requests we need to handle. First the
@@ -525,18 +532,18 @@ request({put_chars, Encoding, Module, Function, Args}, State) -&gt;
end;
</code>
-<p>The Encoding tells us how the characters in the message are
+<p>The <c>Encoding</c> tells us how the characters in the request are
represented. We want to store the characters as lists in the
-ets-table, so we convert them to lists using the
-unicode:characters_to_list/2 function. The conversion function
-conveniently accepts the encoding types unicode or latin1, so we can
-use the Encoding parameter directly.</p>
+ETS table, so we convert them to lists using the
+<seealso marker="stdlib:unicode#characters_to_list/2"><c>unicode:characters_to_list/2</c></seealso> function. The conversion function
+conveniently accepts the encoding types <c>unicode</c> or <c>latin1</c>, so we can
+use <c>Encoding</c> directly.</p>
-<p>When Module, Function and Arguments are provided, we simply apply it
+<p>When <c>Module</c>, <c>Function</c> and <c>Arguments</c> are provided, we simply apply it
and do the same thing with the result as if the data was provided
directly.</p>
-<p>Let's handle the requests for retrieving data too:</p>
+<p>Let us handle the requests for retrieving data too:</p>
<code>
request({get_until, Encoding, _Prompt, M, F, As}, State) -&gt;
@@ -550,11 +557,11 @@ request({get_line, Encoding, _Prompt}, State) -&gt;
</code>
<p>Here we have cheated a little by more or less only implementing
-get_until and using internal helpers to implement get_chars and
-get_line. In production code, this might be to inefficient, but that
+<c>get_until</c> and using internal helpers to implement <c>get_chars</c> and
+<c>get_line</c>. In production code, this might be too inefficient, but that
of course depends on the frequency of the different requests. Before
-we start actually implementing the functions put_chars/2 and
-get_until/5, lets look into the few remaining requests:</p>
+we start actually implementing the functions <c>put_chars/2</c> and
+<c>get_until/5</c>, let us look into the few remaining requests:</p>
<code>
request({get_geometry,_}, State) -&gt;
@@ -567,18 +574,18 @@ request({requests, Reqs}, State) -&gt;
multi_request(Reqs, {ok, ok, State});
</code>
-<p>The get_geometry request has no meaning for this io_server, so the
-reply will be {error, enotsup}. The only option we handle is the
-binary/list option, which is done in separate functions.</p>
+<p>The <c>get_geometry</c> request has no meaning for this I/O server, so the
+reply will be <c>{error, enotsup}</c>. The only option we handle is the
+<c>binary</c>/<c>list</c> option, which is done in separate functions.</p>
-<p>The multi-request tag (requests) is handled in a separate loop
+<p>The multi-request tag (<c>requests</c>) is handled in a separate loop
function applying the requests in the list one after another,
returning the last result.</p>
-<p>What's left is to handle backward compatibility and the file-module
+<p>What is left is to handle backward compatibility and the <seealso marker="kernel:file">file</seealso> module
(which uses the old requests until backward compatibility with pre-R13
-nodes is no longer needed). Note that the io_server will not work with
-a simple file:write if these are not added:</p>
+nodes is no longer needed). Note that the I/O server will not work with
+a simple <c>file:write/2</c> if these are not added:</p>
<code>
request({put_chars,Chars}, State) -&gt;
@@ -593,7 +600,7 @@ request({get_until, Prompt,M,F,As}, State) -&gt;
request({get_until,latin1,Prompt,M,F,As}, State);
</code>
-<p>Ok, what's left now is to return {error, request} if the request is
+<p>OK, what is left now is to return <c>{error, request}</c> if the request is
not recognized:</p>
<code>
@@ -601,7 +608,7 @@ request(_Other, State) -&gt;
{error, {error, request}, State}.
</code>
-<p>Let's move further and actually handle the different requests, first
+<p>Let us move further and actually handle the different requests, first
the fairly generic multi-request type:</p>
<code>
@@ -615,10 +622,10 @@ multi_request([], Result) -&gt;
<p>We loop through the requests one at the time, stopping when we either
encounter an error or the list is exhausted. The last return value is
-sent back to the client (it's first returned to the main loop and then
-sent back by the function io_reply).</p>
+sent back to the client (it is first returned to the main loop and then
+sent back by the function <c>io_reply</c>).</p>
-<p>The getopt and setopt requests are also simple to handle, we just
+<p>The <c>getopts</c> and <c>setopts</c> requests are also simple to handle, we just
change or read our state record:</p>
<code>
@@ -656,24 +663,24 @@ getopts(#state{mode=M} = S) -&gt;
end}],S}.
</code>
-<p>As a convention, all io_servers handle both {setopts, [binary]},
-{setopts, [list]} and {setopts,[{binary, bool()}]}, hence the trick
-with proplists:substitute_negations/2 and proplists:unfold/1. If
-invalid options are sent to us, we send {error,enotsup} back to the
+<p>As a convention, all I/O servers handle both <c>{setopts, [binary]}</c>,
+<c>{setopts, [list]}</c> and <c>{setopts,[{binary, boolean()}]}</c>, hence the trick
+with <c>proplists:substitute_negations/2</c> and <c>proplists:unfold/1</c>. If
+invalid options are sent to us, we send <c>{error, enotsup}</c> back to the
client.</p>
-<p>The getopts request should return a list of {Option, Value} tuples,
+<p>The <c>getopts</c> request should return a list of <c>{Option, Value}</c> tuples,
which has the twofold function of providing both the current values
-and the available options of this io_server. We have only one option,
+and the available options of this I/O server. We have only one option,
and hence return that.</p>
-<p>So far our io_server has been fairly generic (except for the rewind
-request handled in the main loop and the creation of an ets table).
-Most io_servers contain code similar to what's above.</p>
+<p>So far our I/O server has been fairly generic (except for the <c>rewind</c>
+request handled in the main loop and the creation of an ETS table).
+Most I/O servers contain code similar to the one above.</p>
<p>To make the example runnable, we now start implementing the actual
-reading and writing of the data to/from the ets-table. First the
-put_chars function:</p>
+reading and writing of the data to/from the ETS table. First the
+<c>put_chars/3</c> function:</p>
<code>
put_chars(Chars, #state{table = T, position = P} = State) -&gt;
@@ -686,10 +693,10 @@ put_chars(Chars, #state{table = T, position = P} = State) -&gt;
<p>We already have the data as (Unicode) lists and therefore just split
the list in runs of a predefined size and put each run in the
table at the current position (and forward). The functions
-split_data/3 and apply_update/2 are implemented below.</p>
+<c>split_data/3</c> and <c>apply_update/2</c> are implemented below.</p>
-<p>Now we want to read data from the table. The get_until function reads
-data and applies the function until it says it's done. The result is
+<p>Now we want to read data from the table. The <c>get_until/5</c> function reads
+data and applies the function until it says it is done. The result is
sent back to the client:</p>
<code>
@@ -700,11 +707,12 @@ get_until(Encoding, Mod, Func, As,
if
M =:= binary -&gt;
{ok,
- unicode:characters_to_binary(Data,unicode,Encoding),
+ unicode:characters_to_binary(Data, unicode, Encoding),
State#state{position = NewP}};
true -&gt;
case check(Encoding,
- unicode:characters_to_list(Data, unicode)) of
+ unicode:characters_to_list(Data, unicode))
+ of
{error, _} = E -&gt;
{error, E, State};
List -&gt;
@@ -730,24 +738,24 @@ get_loop(M,F,A,T,P,C) -&gt;
end.
</code>
-<p>Here we also handle the mode (binary or list) that can be set by
-the setopts request. By default, all OTP io_servers send data back to
-the client as lists, but switching mode to binary might increase
-efficiency if the server handles it in an appropriate way. The
-implementation of get_until is hard to get efficient as the supplied
-function is defined to take lists as arguments, but get_chars and
-get_line can be optimized for binary mode. This example does not
+<p>Here we also handle the mode (<c>binary</c> or <c>list</c>) that can be set by
+the <c>setopts</c> request. By default, all OTP I/O servers send data back to
+the client as lists, but switching mode to <c>binary</c> might increase
+efficiency if the I/O server handles it in an appropriate way. The
+implementation of <c>get_until</c> is hard to get efficient as the supplied
+function is defined to take lists as arguments, but <c>get_chars</c> and
+<c>get_line</c> can be optimized for binary mode. This example does not
optimize anything however. It is important though that the returned
data is of the right type depending on the options set, so we convert
the lists to binaries in the correct encoding <em>if possible</em>
-before returning. The function supplied in the get_until request may,
+before returning. The function supplied in the <c>get_until</c> request tuple may,
as its final result return anything, so only functions actually
returning lists can get them converted to binaries. If the request
-contained the encoding tag unicode, the lists can contain all unicode
+contained the encoding tag <c>unicode</c>, the lists can contain all Unicode
codepoints and the binaries should be in UTF-8, if the encoding tag
-was latin1, the client should only get characters in the range
-0..255. The function check/2 takes care of not returning arbitrary
-unicode codepoints in lists if the encoding was given as latin1. If
+was <c>latin1</c>, the client should only get characters in the range
+0..255. The function <c>check/2</c> takes care of not returning arbitrary
+Unicode codepoints in lists if the encoding was given as <c>latin1</c>. If
the function did not return a list, the check cannot be performed and
the result will be that of the supplied function untouched.</p>
@@ -768,13 +776,13 @@ check(latin1, List) -&gt;
end.
</code>
-<p>The function check takes care of providing an error tuple if unicode
+<p>The function check takes care of providing an error tuple if Unicode
codepoints above 255 is to be returned if the client requested
latin1.</p>
-<p>The two functions until_newline/3 and until_enough/3 are helpers used
-together with the get_until function to implement get_chars and
-get_line (inefficiently):</p>
+<p>The two functions <c>until_newline/3</c> and <c>until_enough/3</c> are helpers used
+together with the <c>get_until/5</c> function to implement <c>get_chars</c> and
+<c>get_line</c> (inefficiently):</p>
<code>
until_newline([],eof,_MyStopCharacter) -&gt;
@@ -782,7 +790,9 @@ until_newline([],eof,_MyStopCharacter) -&gt;
until_newline(ThisFar,eof,_MyStopCharacter) -&gt;
{done,ThisFar,[]};
until_newline(ThisFar,CharList,MyStopCharacter) -&gt;
- case lists:splitwith(fun(X) -&gt; X =/= MyStopCharacter end, CharList) of
+ case
+ lists:splitwith(fun(X) -&gt; X =/= MyStopCharacter end, CharList)
+ of
{L,[]} -&gt;
{more,ThisFar++L};
{L2,[MyStopCharacter|Rest]} -&gt;
@@ -802,10 +812,10 @@ until_enough(ThisFar,CharList,_N) -&gt;
</code>
<p>As can be seen, the functions above are just the type of functions
-that should be provided in get_until requests.</p>
+that should be provided in <c>get_until</c> requests.</p>
<p>Now we only need to read and write the table in an appropriate way to
-complete the server:</p>
+complete the I/O server:</p>
<code>
get(P,Tab) -&gt;
@@ -847,13 +857,13 @@ apply_update(Table, {Row, Col, List}) -&gt;
end.
</code>
-<p>The table is read or written in chunks of ?CHARS_PER_REC, overwriting
+<p>The table is read or written in chunks of <c>?CHARS_PER_REC</c>, overwriting
when necessary. The implementation is obviously not efficient, it is
just working.</p>
<p>This concludes the example. It is fully runnable and you can read or
-write to the io_server by using i.e. the io_module or even the file
-module. It's as simple as that to implement a fully fledged io_server
+write to the I/O server by using i.e. the <seealso marker="stdlib:io">io</seealso> module or even the <seealso marker="kernel:file">file</seealso>
+module. It is as simple as that to implement a fully fledged I/O server
in Erlang.</p>
</section>
</chapter>
diff --git a/lib/stdlib/doc/src/notes.xml b/lib/stdlib/doc/src/notes.xml
index 4834426d5c..2a308cbe09 100644
--- a/lib/stdlib/doc/src/notes.xml
+++ b/lib/stdlib/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2004</year><year>2011</year>
+ <year>2004</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -30,6 +30,99 @@
</header>
<p>This document describes the changes made to the STDLIB application.</p>
+<section><title>STDLIB 1.18.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Minor test updates</p>
+ <p>
+ Own Id: OTP-10591</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>STDLIB 1.18.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixed bug where if given an invalid drive letter on
+ windows ensure dir would go into an infinite loop.</p>
+ <p>
+ Own Id: OTP-10104</p>
+ </item>
+ <item>
+ <p>
+ Calls to gen_server:enter_loop/4 where ServerName has a
+ global scope and no timeout is given now works correctly.</p>
+ <p>
+ Thanks to Sam Bobroff for reporting the issue.</p>
+ <p>
+ Own Id: OTP-10130</p>
+ </item>
+ <item>
+ <p>
+ fix escript/primary archive reloading</p>
+ <p>
+ If the mtime of an escript/primary archive file changes
+ after being added to the code path, correctly reload the
+ archive and update the cache. (Thanks to Tuncer Ayaz)</p>
+ <p>
+ Own Id: OTP-10151</p>
+ </item>
+ <item>
+ <p>
+ Fix bug that in some cases could cause corrupted binaries
+ in ETS tables with <c>compressed</c> option.</p>
+ <p>
+ Own Id: OTP-10182</p>
+ </item>
+ <item>
+ <p>
+ Fix filename:nativename/1 on Win32</p>
+ <p>
+ Don't choke on paths given as binary argument on Win32.
+ Thanks to Jan Kl�tzke</p>
+ <p>
+ Own Id: OTP-10188</p>
+ </item>
+ <item>
+ <p>
+ Fix bug in <c>ets:test_ms/2</c> that could cause emulator
+ crash when using <c>'$_'</c> in match spec.</p>
+ <p>
+ Own Id: OTP-10190</p>
+ </item>
+ <item>
+ <p>
+ Fix bug where zip archives wrongly have a first disk
+ number set to 1</p>
+ <p>
+ Own Id: OTP-10223</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p> The message printed by the Erlang shell as an
+ explanation of the <c>badarith</c> error has been
+ corrected. (Thanks to Matthias Lang.) </p>
+ <p>
+ Own Id: OTP-10054</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>STDLIB 1.18.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/stdlib/doc/src/proplists.xml b/lib/stdlib/doc/src/proplists.xml
index 225c5e97eb..8d64319344 100644
--- a/lib/stdlib/doc/src/proplists.xml
+++ b/lib/stdlib/doc/src/proplists.xml
@@ -70,7 +70,7 @@
<fsummary></fsummary>
<desc>
<p>Minimizes the representation of all entries in the list. This is
- equivalent to <c><![CDATA[[property(P) || P <- List]]]></c>.</p>
+ equivalent to <c><![CDATA[[property(P) || P <- ListIn]]]></c>.</p>
<p>See also: <c>property/1</c>, <c>unfold/1</c>.</p>
</desc>
</func>
@@ -88,11 +88,11 @@
<desc>
<p>Expands particular properties to corresponding sets of
properties (or other terms). For each pair <c>{<anno>Property</anno>, <anno>Expansion</anno>}</c> in <c><anno>Expansions</anno></c>, if <c>E</c> is
- the first entry in <c><anno>List</anno></c> with the same key as
+ the first entry in <c><anno>ListIn</anno></c> with the same key as
<c><anno>Property</anno></c>, and <c>E</c> and <c><anno>Property</anno></c>
have equivalent normal forms, then <c>E</c> is replaced with
the terms in <c><anno>Expansion</anno></c>, and any following entries with
- the same key are deleted from <c><anno>List</anno></c>.</p>
+ the same key are deleted from <c><anno>ListIn</anno></c>.</p>
<p>For example, the following expressions all return <c>[fie, bar, baz, fum]</c>:</p>
<code type="none">
expand([{foo, [bar, baz]}],
@@ -198,7 +198,7 @@
<name name="normalize" arity="2"/>
<fsummary></fsummary>
<desc>
- <p>Passes <c><anno>List</anno></c> through a sequence of
+ <p>Passes <c><anno>ListIn</anno></c> through a sequence of
substitution/expansion stages. For an <c>aliases</c> operation,
the function <c>substitute_aliases/2</c> is applied using the
given list of aliases; for a <c>negations</c> operation,
@@ -221,9 +221,9 @@
<fsummary></fsummary>
<desc>
<p>Creates a normal form (minimal) representation of a property. If
- <c><anno>Property</anno></c> is <c>{Key, true}</c> where <c>Key</c> is
- an atom, this returns <c>Key</c>, otherwise the whole term
- <c><anno>Property</anno></c> is returned.</p>
+ <c><anno>PropertyIn</anno></c> is <c>{Key, true}</c> where
+ <c>Key</c> is an atom, this returns <c>Key</c>, otherwise
+ the whole term <c><anno>PropertyIn</anno></c> is returned.</p>
<p>See also: <c>property/2</c>.</p>
</desc>
</func>
@@ -260,7 +260,7 @@
<fsummary></fsummary>
<desc>
<p>Substitutes keys of properties. For each entry in
- <c><anno>List</anno></c>, if it is associated with some key <c>K1</c>
+ <c><anno>ListIn</anno></c>, if it is associated with some key <c>K1</c>
such that <c>{K1, K2}</c> occurs in <c><anno>Aliases</anno></c>, the
key of the entry is changed to <c>K2</c>. If the same
<c>K1</c> occurs more than once in <c><anno>Aliases</anno></c>, only
@@ -278,13 +278,13 @@
<desc>
<p>Substitutes keys of boolean-valued properties and
simultaneously negates their values. For each entry in
- <c><anno>List</anno></c>, if it is associated with some key <c>K1</c>
+ <c><anno>ListIn</anno></c>, if it is associated with some key <c>K1</c>
such that <c>{K1, K2}</c> occurs in <c><anno>Negations</anno></c>, then
if the entry was <c>{K1, true}</c> it will be replaced with
<c>{K2, false}</c>, otherwise it will be replaced with
<c>{K2, true}</c>, thus changing the name of the option and
simultaneously negating the value given by
- <c>get_bool(List)</c>. If the same <c>K1</c> occurs more
+ <c>get_bool(ListIn)</c>. If the same <c>K1</c> occurs more
than once in <c><anno>Negations</anno></c>, only the first occurrence is
used.</p>
<p>Example: <c>substitute_negations([{no_foo, foo}], L)</c>
@@ -300,7 +300,7 @@
<name name="unfold" arity="1"/>
<fsummary></fsummary>
<desc>
- <p>Unfolds all occurrences of atoms in <c><anno>List</anno></c> to tuples
+ <p>Unfolds all occurrences of atoms in <c><anno>ListIn</anno></c> to tuples
<c>{Atom, true}</c>.</p>
</desc>
</func>
diff --git a/lib/stdlib/doc/src/re.xml b/lib/stdlib/doc/src/re.xml
index c6f45fb1e1..71a6e34513 100644
--- a/lib/stdlib/doc/src/re.xml
+++ b/lib/stdlib/doc/src/re.xml
@@ -96,12 +96,12 @@
subjects during the program's lifetime. Compiling once and
executing many times is far more efficient than compiling each
time one wants to match.</p>
- <p>When the unicode option is given, the regular expression should be given as a valid unicode <c>charlist()</c>, otherwise as any valid <c>iodata()</c>.</p>
+ <p>When the unicode option is given, the regular expression should be given as a valid Unicode <c>charlist()</c>, otherwise as any valid <c>iodata()</c>.</p>
<p><marker id="compile_options"/>The options have the following meanings:</p>
<taglist>
<tag><c>unicode</c></tag>
- <item>The regular expression is given as a unicode <c>charlist()</c> and the resulting regular expression code is to be run against a valid unicode <c>charlist()</c> subject.</item>
+ <item>The regular expression is given as a Unicode <c>charlist()</c> and the resulting regular expression code is to be run against a valid Unicode <c>charlist()</c> subject.</item>
<tag><c>anchored</c></tag>
<item>The pattern is forced to be "anchored", that is, it is constrained to match only at the first matching point in the string that is being searched (the "subject string"). This effect can also be achieved by appropriate constructs in the pattern itself.</item>
<tag><c>caseless</c></tag>
@@ -478,7 +478,7 @@ This option makes it possible to include comments inside complicated patterns. N
<p>Replaces the matched part of the <c><anno>Subject</anno></c> string with the contents of <c><anno>Replacement</anno></c>.</p>
<p>The permissible options are the same as for <c>re:run/3</c>, except that the <c>capture</c> option is not allowed.
Instead a <c>{return, <anno>ReturnType</anno>}</c> is present. The default return type is <c>iodata</c>, constructed in a
- way to minimize copying. The <c>iodata</c> result can be used directly in many i/o-operations. If a flat <c>list()</c> is
+ way to minimize copying. The <c>iodata</c> result can be used directly in many I/O-operations. If a flat <c>list()</c> is
desired, specify <c>{return, list}</c> and if a binary is preferred, specify <c>{return, binary}</c>.</p>
<p>As in the <c>re:run/3</c> function, an <c>mp()</c> compiled
@@ -490,8 +490,8 @@ This option makes it possible to include comments inside complicated patterns. N
<p>The replacement string can contain the special character
<c>&amp;</c>, which inserts the whole matching expression in the
- result, and the special sequence <c>\</c>N (where N is an
- integer &gt; 0), resulting in the subexpression number N will be
+ result, and the special sequence <c>\</c>N (where N is an integer &gt; 0),
+ <c>\g</c>N or <c>\g{</c>N<c>}</c> resulting in the subexpression number N will be
inserted in the result. If no subexpression with that number is
generated by the regular expression, nothing is inserted.</p>
<p>To insert an <c>&amp;</c> or <c>\</c> in the result, precede it
diff --git a/lib/stdlib/doc/src/supervisor.xml b/lib/stdlib/doc/src/supervisor.xml
index f9a5e245b4..9021d02ade 100644
--- a/lib/stdlib/doc/src/supervisor.xml
+++ b/lib/stdlib/doc/src/supervisor.xml
@@ -294,10 +294,10 @@ child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules}
is a term with information about the error, and the supervisor
terminates with reason <c>Term</c>.</p>
<p>If any child process start function fails or returns an error
- tuple or an erroneous value, the function returns
- <c>{error,shutdown}</c> and the supervisor terminates all
- started child processes and then itself with reason
- <c>shutdown</c>.</p>
+ tuple or an erroneous value, the supervisor will first terminate
+ all already started child processes with reason <c>shutdown</c>
+ and then terminate itself and return
+ <c>{error, {shutdown, Reason}}</c>.</p>
</desc>
</func>
<func>
diff --git a/lib/stdlib/doc/src/unicode.xml b/lib/stdlib/doc/src/unicode.xml
index 1f6cbaccd7..d235f3e180 100644
--- a/lib/stdlib/doc/src/unicode.xml
+++ b/lib/stdlib/doc/src/unicode.xml
@@ -32,9 +32,9 @@
<module>unicode</module>
<modulesummary>Functions for converting Unicode characters</modulesummary>
<description>
- <p>This module contains functions for converting between different character representations. Basically it converts between iso-latin-1 characters and Unicode ditto, but it can also convert between different Unicode encodings (like UTF-8, UTF-16 and UTF-32).</p>
+ <p>This module contains functions for converting between different character representations. Basically it converts between ISO-latin-1 characters and Unicode ditto, but it can also convert between different Unicode encodings (like UTF-8, UTF-16 and UTF-32).</p>
<p>The default Unicode encoding in Erlang is in binaries UTF-8, which is also the format in which built in functions and libraries in OTP expect to find binary Unicode data. In lists, Unicode data is encoded as integers, each integer representing one character and encoded simply as the Unicode codepoint for the character.</p>
- <p>Other Unicode encodings than integers representing codepoints or UTF-8 in binaries are referred to as &quot;external encodings&quot;. The iso-latin-1 encoding is in binaries and lists referred to as latin1-encoding.</p>
+ <p>Other Unicode encodings than integers representing codepoints or UTF-8 in binaries are referred to as &quot;external encodings&quot;. The ISO-latin-1 encoding is in binaries and lists referred to as latin1-encoding.</p>
<p>It is recommended to only use external encodings for communication with external entities where this is required. When working inside the Erlang/OTP environment, it is recommended to keep binaries in UTF-8 when representing Unicode characters. Latin1 encoding is supported both for backward compatibility and for communication with external entities not supporting Unicode character sets.</p>
</description>
@@ -48,13 +48,13 @@
<datatype>
<name name="unicode_binary"/>
<desc>
- <p>A binary() with characters encoded in the UTF-8 coding standard.</p>
+ <p>A <c>binary()</c> with characters encoded in the UTF-8 coding standard.</p>
</desc>
</datatype>
<datatype>
<name name="unicode_char"/>
<desc>
- <p>An integer() representing a valid unicode codepoint.</p>
+ <p>An <c>integer()</c> representing a valid Unicode codepoint.</p>
</desc>
</datatype>
<datatype>
@@ -63,7 +63,7 @@
<datatype>
<name name="charlist"/>
<desc>
- <p>A unicode_binary is allowed as the tail of the list.</p>
+ <p>A <c>unicode_binary()</c> is allowed as the tail of the list.</p>
</desc>
</datatype>
<datatype>
@@ -85,7 +85,7 @@
</datatype>
<datatype>
<name name="latin1_binary"/>
- <desc><p>A <c>binary()</c> with characters coded in iso-latin-1.</p>
+ <desc><p>A <c>binary()</c> with characters coded in ISO-latin-1.</p>
</desc>
</datatype>
<datatype>
@@ -110,7 +110,9 @@
<name name="bom_to_encoding" arity="1"/>
<fsummary>Identify UTF byte order marks in a binary.</fsummary>
<type name="endian"/>
- <type_desc variable="Bin">A binary() of byte_size 4 or more.</type_desc>
+ <type_desc variable="Bin">
+ A <c>binary()</c> such that <c>byte_size(<anno>Bin</anno>) >= 4</c>.
+ </type_desc>
<desc>
<p>Check for a UTF byte order mark (BOM) in the beginning of a
@@ -126,7 +128,7 @@
<name name="characters_to_list" arity="1"/>
<fsummary>Convert a collection of characters to list of Unicode characters</fsummary>
<desc>
- <p>Same as characters_to_list(<anno>Data</anno>,unicode).</p>
+ <p>Same as <c>characters_to_list(<anno>Data</anno>, unicode)</c>.</p>
</desc>
</func>
<func>
@@ -134,8 +136,8 @@
<fsummary>Convert a collection of characters to list of Unicode characters</fsummary>
<desc>
- <p>This function converts a possibly deep list of integers and
- binaries into a list of integers representing unicode
+ <p>Converts a possibly deep list of integers and
+ binaries into a list of integers representing Unicode
characters. The binaries in the input may have characters
encoded as latin1 (0 - 255, one character per byte), in which
case the <c><anno>InEncoding</anno></c> parameter should be given as
@@ -148,18 +150,18 @@
<p>If <c><anno>InEncoding</anno></c> is <c>latin1</c>, the <c><anno>Data</anno></c> parameter
corresponds to the <c>iodata()</c> type, but for <c>unicode</c>,
the <c><anno>Data</anno></c> parameter can contain integers greater than 255
- (unicode characters beyond the iso-latin-1 range), which would
+ (Unicode characters beyond the ISO-latin-1 range), which would
make it invalid as <c>iodata()</c>.</p>
<p>The purpose of the function is mainly to be able to convert
- combinations of unicode characters into a pure unicode
+ combinations of Unicode characters into a pure Unicode
string in list representation for further processing. For
writing the data to an external entity, the reverse function
<seealso
- marker="#characters_to_binary/3">characters_to_binary/3</seealso>
+ marker="#characters_to_binary/3"><c>characters_to_binary/3</c></seealso>
comes in handy.</p>
- <p>The option <c>unicode</c> is an alias for <c>utf8</c>, as this is the
+ <p>The option <c>unicode</c> is an alias for <c>utf8</c>, as this is the
preferred encoding for Unicode characters in
binaries. <c>utf16</c> is an alias for <c>{utf16,big}</c> and
<c>utf32</c> is an alias for <c>{utf32,big}</c>. The <c>big</c>
@@ -167,7 +169,7 @@
encoding.</p>
<p>If for some reason, the data cannot be converted, either
- because of illegal unicode/latin1 characters in the list, or
+ because of illegal Unicode/latin1 characters in the list, or
because of invalid UTF encoding in any binaries, an error
tuple is returned. The error tuple contains the tag
<c>error</c>, a list representing the characters that could be
@@ -176,7 +178,7 @@
last part is mostly for debugging as it still constitutes a
possibly deep and/or mixed list, not necessarily of the same
depth as the original data. The error occurs when traversing the
- list and whatever's left to decode is simply returned as is.</p>
+ list and whatever is left to decode is simply returned as is.</p>
<p>However, if the input <c><anno>Data</anno></c> is a pure binary, the third
part of the error tuple is guaranteed to be a binary as
@@ -191,7 +193,7 @@
of a Unicode type, an error occurs whenever an integer
<list type="bulleted">
<item>greater than <c>16#10FFFF</c>
- (the maximum unicode character),</item>
+ (the maximum Unicode character),</item>
<item>in the range <c>16#D800</c> to <c>16#DFFF</c>
(invalid range reserved for UTF-16 surrogate pairs)</item>
</list>
@@ -205,14 +207,14 @@
(like the upper
bits of the bytes being wrong), the bytes are decoded to a
too large number, the bytes are decoded to a code-point in the
- invalid unicode
- range or encoding is &quot;overlong&quot;, meaning that a
+ invalid Unicode
+ range, or encoding is &quot;overlong&quot;, meaning that a
number should have been encoded in fewer bytes. The
case of a truncated UTF is handled specially, see the
paragraph about incomplete binaries below. If
<c><anno>InEncoding</anno></c> is <c>latin1</c>, binaries are always valid
as long as they contain whole bytes,
- as each byte falls into the valid iso-latin-1 range.</item>
+ as each byte falls into the valid ISO-latin-1 range.</item>
</list>
@@ -250,7 +252,7 @@
ever be decoded.</p>
<p>If any parameters are of the wrong type, the list structure
- is invalid (a number as tail) or the binaries does not contain
+ is invalid (a number as tail) or the binaries do not contain
whole bytes (bit-strings), a <c>badarg</c> exception is
thrown.</p>
@@ -258,28 +260,27 @@
</func>
<func>
<name name="characters_to_binary" arity="1"/>
- <fsummary>Convert a collection of characters to an UTF-8 binary</fsummary>
+ <fsummary>Convert a collection of characters to a UTF-8 binary</fsummary>
<desc>
- <p>Same as characters_to_binary(Data, unicode, unicode).</p>
+ <p>Same as <c>characters_to_binary(<anno>Data</anno>, unicode, unicode)</c>.</p>
</desc>
</func>
<func>
<name name="characters_to_binary" arity="2"/>
- <fsummary>Convert a collection of characters to an UTF-8 binary</fsummary>
+ <fsummary>Convert a collection of characters to a UTF-8 binary</fsummary>
<desc>
- <p>Same as characters_to_binary(<anno>Data</anno>, <anno>InEncoding</anno>, unicode).</p>
+ <p>Same as <c>characters_to_binary(<anno>Data</anno>, <anno>InEncoding</anno>, unicode)</c>.</p>
</desc>
</func>
<func>
<name name="characters_to_binary" arity="3"/>
- <fsummary>Convert a collection of characters to an UTF-8 binary</fsummary>
+ <fsummary>Convert a collection of characters to a UTF-8 binary</fsummary>
<desc>
- <p>This function behaves as <seealso
- marker="#characters_to_list/2">
- characters_to_list/2</seealso>, but produces an binary
- instead of a unicode list. The
+ <p>Behaves as <seealso marker="#characters_to_list/2">
+ <c>characters_to_list/2</c></seealso>, but produces an binary
+ instead of a Unicode list. The
<c><anno>InEncoding</anno></c> defines how input is to be interpreted if
binaries are present in the <c>Data</c>, while
<c><anno>OutEncoding</anno></c> defines in what format output is to be
@@ -294,7 +295,7 @@
<p>Errors and exceptions occur as in <seealso
marker="#characters_to_list/2">
- characters_to_list/2</seealso>, but the second element
+ <c>characters_to_list/2</c></seealso>, but the second element
in the <c>error</c> or
<c>incomplete</c> tuple will be a <c>binary()</c> and not a
<c>list()</c>.</p>
@@ -304,16 +305,18 @@
<func>
<name name="encoding_to_bom" arity="1"/>
<fsummary>Create a binary UTF byte order mark from encoding.</fsummary>
- <type_desc variable="Bin">A binary() of byte_size 4 or more.</type_desc>
+ <type_desc variable="Bin">
+ A <c>binary()</c> such that <c>byte_size(<anno>Bin</anno>) >= 4</c>.
+ </type_desc>
<desc>
- <p>Create an UTF byte order mark (BOM) as a binary from the
+ <p>Create a UTF byte order mark (BOM) as a binary from the
supplied <c><anno>InEncoding</anno></c>. The BOM is, if supported at all,
expected to be placed first in UTF encoded files or
messages.</p>
<p>The function returns <c>&lt;&lt;&gt;&gt;</c> for the
- <c>latin1</c> encoding, there is no BOM for ISO-latin-1.</p>
+ <c>latin1</c> encoding as there is no BOM for ISO-latin-1.</p>
<p>It can be noted that the BOM for UTF-8 is seldom used, and it
is really not a <em>byte order</em> mark. There are obviously no
diff --git a/lib/stdlib/doc/src/unicode_usage.xml b/lib/stdlib/doc/src/unicode_usage.xml
index a7e010a05f..320b5b2e84 100644
--- a/lib/stdlib/doc/src/unicode_usage.xml
+++ b/lib/stdlib/doc/src/unicode_usage.xml
@@ -1,11 +1,11 @@
-<?xml version="1.0" encoding="latin1" ?>
+<?xml version="1.0" encoding="utf8" ?>
<!DOCTYPE chapter SYSTEM "chapter.dtd">
<chapter>
<header>
<copyright>
<year>1999</year>
- <year>2011</year>
+ <year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -33,14 +33,14 @@
<file>unicode_usage.xml</file>
</header>
<p>Implementing support for Unicode character sets is an ongoing process. The Erlang Enhancement Proposal (EEP) 10 outlines the basics of Unicode support and also specifies a default encoding in binaries that all Unicode-aware modules should handle in the future.</p>
-<p>The functionality described in EEP10 is implemented in Erlang/OTP as of R13A, but that's by no means the end of it. More functionality will be needed in the future and more OTP-libraries might need updating to cope with Unicode data. One example of future development is obvious when reading this manual, our documentation format is limited to the ISO-latin-1 character range, why no Unicode characters beyond that range will occur in this document.</p>
+<p>The functionality described in EEP10 is implemented in Erlang/OTP as of R13A, but that is by no means the end of it. More functionality will be needed in the future and more OTP-libraries might need updating to cope with Unicode data.</p>
<p>This guide outlines the current Unicode support and gives a couple of recipes for working with Unicode data.</p>
<section>
-<title>What Unicode is</title>
+<title>What Unicode Is</title>
<p>Unicode is a standard defining codepoints (numbers) for all known, living or dead, scripts. In principle, every known symbol used in any language has a Unicode codepoint.</p>
<p>Unicode codepoints are defined and published by the <em>Unicode Consortium</em>, which is a non profit organization.</p>
<p>Support for Unicode is increasing throughout the world of computing, as the benefits of one common character set are overwhelming when programs are used in a global environment.</p>
-<p>Along with the base of the standard, the codepoints for all the scripts, there are a couple of encoding standards available. Different operating systems and tools support different encodings. For example Linux and MacOS X has chosen the UTF-8 encoding, which is backwards compatible with 7-bit ASCII and therefore affects programs written in plain English the least. Windows&reg; on the other hand supports a limited version of UTF-16, namely all the code planes where the characters can be stored in one single 16-bit entity, which includes most living languages.</p>
+<p>Along with the base of the standard, the codepoints for all the scripts, there are a couple of encoding standards available. Different operating systems and tools support different encodings. For example Linux and MacOSX has chosen the UTF-8 encoding, which is backwards compatible with 7-bit ASCII and therefore affects programs written in plain English the least. Windows&reg; on the other hand supports a limited version of UTF-16, namely all the code planes where the characters can be stored in one single 16-bit entity, which includes most living languages.</p>
<p>The most widely spread encodings are:</p>
<taglist>
<tag>UTF-8</tag>
@@ -53,41 +53,50 @@
<item>Basically the same as UTF-32, but without some Unicode semantics, defined by IEEE and has little use as a separate encoding standard. For all normal (and possibly abnormal) usages, UTF-32 and UCS-4 are interchangeable.</item>
</taglist>
<p>Certain ranges of characters are left unused and certain ranges are even deemed invalid. The most notable invalid range is 16#D800 - 16#DFFF, as the UTF-16 encoding does not allow for encoding of these numbers. It can be speculated that the UTF-16 encoding standard was, from the beginning, expected to be able to hold all Unicode characters in one 16-bit entity, but then had to be extended, leaving a hole in the Unicode range to cope with backward compatibility.</p>
-<p>Additionally, the codepoint 16#FEFF is used for byte order marks (BOM's) and use of that character is not encouraged in other contexts than that. It actually is valid though, as the character "ZWNBS" (Zero Width Non Breaking Space). BOM's are used to identify encodings and byte order for programs where such parameters are not known in advance. Byte order marks are more seldom used than one could expect, put their use is becoming more widely spread as they provide the means for programs to make educated guesses about the Unicode format of a certain file.</p>
+<p>Additionally, the codepoint 16#FEFF is used for byte order marks (BOM's) and use of that character is not encouraged in other contexts than that. It actually is valid though, as the character "ZWNBS" (Zero Width Non Breaking Space). BOM's are used to identify encodings and byte order for programs where such parameters are not known in advance. Byte order marks are more seldom used than one could expect, but their use is becoming more widely spread as they provide the means for programs to make educated guesses about the Unicode format of a certain file.</p>
</section>
<section>
-<title>Standard Unicode representation in Erlang</title>
+<title>Standard Unicode Representation in Erlang</title>
<p>In Erlang, strings are actually lists of integers. A string is defined to be encoded in the ISO-latin-1 (ISO8859-1) character set, which is, codepoint by codepoint, a sub-range of the Unicode character set.</p>
<p>The standard list encoding for strings is therefore easily extendible to cope with the whole Unicode range: A Unicode string in Erlang is simply a list containing integers, each integer being a valid Unicode codepoint and representing one character in the Unicode character set.</p>
-<p>Regular Erlang strings in ISO-latin-1 are a subset of their Unicode strings.</p>
+<p>Regular Erlang strings in ISO-latin-1 are a subset of their Unicode
+strings.</p>
-<p>Binaries on the other hand are more troublesome. For performance reasons, programs often store textual data in binaries instead of lists, mainly because they are more compact (one byte per character instead of two words per character, as is the case with lists). Using erlang:list_to_binary/1, an regular Erlang string can be converted into a binary, effectively using the ISO-latin-1 encoding in the binary - one byte per character. This is very convenient for those regular Erlang strings, but cannot be done for Unicode lists.</p>
+<p>Binaries on the other hand are more troublesome. For performance reasons, programs often store textual data in binaries instead of lists, mainly because they are more compact (one byte per character instead of two words per character, as is the case with lists). Using <c>erlang:list_to_binary/1</c>, a regular Erlang string can be converted into a binary, effectively using the ISO-latin-1 encoding in the binary - one byte per character. This is very convenient for those regular Erlang strings, but cannot be done for Unicode lists.</p>
<p>As the UTF-8 encoding is widely spread and provides the most compact storage, it is selected as the standard encoding of Unicode characters in binaries for Erlang.</p>
<p>The standard binary encoding is used whenever a library function in Erlang should cope with Unicode data in binaries, but is of course not enforced when communicating externally. Functions and bit-syntax exist to encode and decode both UTF-8, UTF-16 and UTF-32 in binaries. Library functions dealing with binaries and Unicode in general, however, only deal with the default encoding.</p>
-<p>Character data may be combined from several sources, sometimes available in a mix of strings and binaries. Erlang has for long had the concept of iodata or iolists, where binaries and lists can be combined to represent a sequence of bytes. In the same way, the Unicode aware modules often allow for combinations of binaries and lists where the binaries have characters encoded in UTF-8 and the lists contain such binaries or numbers representing Unicode codepoints:</p>
+<p>Character data may be combined from several sources, sometimes available in a mix of strings and binaries. Erlang has for long had the concept of <c>iodata</c> or <c>iolists</c>, where binaries and lists can be combined to represent a sequence of bytes. In the same way, the Unicode aware modules often allow for combinations of binaries and lists where the binaries have characters encoded in UTF-8 and the lists contain such binaries or numbers representing Unicode codepoints:</p>
<code type="none">
unicode_binary() = binary() with characters encoded in UTF-8 coding standard
-unicode_char() = integer() representing valid unicode codepoint
+unicode_char() = integer() >= 0 representing valid Unicode codepoint
chardata() = charlist() | unicode_binary()
charlist() = [unicode_char() | unicode_binary() | charlist()]
a unicode_binary is allowed as the tail of the list</code>
-<p>The module <c>unicode</c> in stdlib even supports similar mixes with binaries containing other encodings than UTF-8, but that is a special case to allow for conversions to and from external data:</p>
+<p>The module <c>unicode</c> in STDLIB even supports similar mixes with binaries containing other encodings than UTF-8, but that is a special case to allow for conversions to and from external data:</p>
<code type="none">
-external_unicode_binary() = binary() with characters coded in a user specified Unicode
- encoding other than UTF-8 (UTF-16 or UTF-32)
+external_unicode_binary() = binary() with characters coded in
+ a user specified Unicode encoding other than UTF-8 (UTF-16 or UTF-32)
external_chardata() = external_charlist() | external_unicode_binary()
-external_charlist() = [unicode_char() | external_unicode_binary() | external_charlist()]
- an external_unicode_binary is allowed as the tail of the list</code>
+external_charlist() = [unicode_char() |
+ external_unicode_binary() |
+ external_charlist()]
+ an external_unicode_binary() is allowed as the tail of the list</code>
</section>
<section>
-<title>Basic language support for Unicode</title>
-<p>First of all, Erlang is still defined to be written in the ISO-latin-1 character set. Functions have to be named in that character set, atoms are restricted to ISO-latin-1 and regular strings are still lists of characters 0..255 in the ISO-latin-1 encoding. This has not (yet) changed, but the language has been slightly extended to cope with Unicode characters and encodings.</p>
-
+<title>Basic Language Support for Unicode</title>
+<p><marker id="unicode_in_erlang"/>As of Erlang/OTP R16 Erlang can be
+written in ISO-latin-1 or Unicode (UTF-8). The details on how to state
+the encoding of an Erlang source file can be found in <seealso
+marker="stdlib:epp#encoding">epp(3)</seealso>. Strings and comments
+can be written using Unicode, but functions still have to be named in
+ISO-latin-1 and atoms are restricted to ISO-latin-1. Erlang/OTP R18 is
+expected to handle functions named in Unicode as well as Unicode
+atoms.</p>
<section>
<title>Bit-syntax</title>
<p>The bit-syntax contains types for coping with binary data in the three main encodings. The types are named <c>utf8</c>, <c>utf16</c> and <c>utf32</c> respectively. The <c>utf16</c> and <c>utf32</c> types can be in a big- or little-endian variant:</p>
@@ -101,74 +110,79 @@ Bin3 = &lt;&lt;$H/utf32-little, $e/utf32-little, $l/utf32-little, $l/utf32-littl
Bin4 = &lt;&lt;"Hello"/utf16&gt;&gt;,</code>
</section>
<section>
-<title>String- and character-literals</title>
-<warning>
-<p>The literal syntax described here may be subject to change in R13B, it has not yet passed the usual process for language changes approval.</p>
-</warning>
-<p>It is convenient to be able to write a list of Unicode characters in the string syntax. However, the language specifies strings as being in the ISO-latin-1 character set which the compiler tool chain as well as many other tools expect.</p>
-<p>Also the source code is (for now) still expected to be written using the ISO-latin-1 character set, why Unicode characters beyond that range cannot be entered in string literals.</p>
-<p>To make it easier to enter Unicode characters in the shell, it allows strings with Unicode characters on input, immediately converting them to regular lists of integers. They will, by the evaluator etc be viewed as if they were input using the regular list syntax, which is - in the end - how the language actually treats them. They will in the same way not be output as strings by i.e <c>io:write/2</c> or <c>io:format/3</c> unless the format string supplied to <c>io:format</c> uses the Unicode translation modifier (which we will talk about later).</p>
-<p>For source code, there is an extension to the \OOO (backslash followed by three octal numbers) and \xHH (backslash followed by 'x', followed by two hexadecimal characters) syntax, namely \x{H ...} (a backslash followed by an 'x', followed by left curly bracket, any number of hexadecimal digits and a terminating right curly bracket). This allows for entering characters of any codepoint literally in a string. The string is immediately converted into a list by the scanner however, which is obvious when calling it directly:</p>
-<pre>
-1> <input>erl_scan:string("\"X\".").</input>
-{ok,[{string,1,"X"},{dot,1}],1}
-2> <input>erl_scan:string("\"\x{400}\".").</input>
-{ok,[{'[',1},{integer,1,1024},{']',1},{dot,1}],1}</pre>
-<p>Character literals, or rather integers representing Unicode codepoints can be expressed in a similar way using $\x{H ...}:</p>
-<pre>
-4> <input>$\x{400}.</input>
-1024</pre>
-<p>This also is a translation by the scanner:</p>
-<pre>
-5> <input>erl_scan:string("$Y.").</input>
-{ok,[{char,1,89},{dot,1}],1}
-6> <input>erl_scan:string("$\x{400}.").</input>
-{ok,[{integer,1,1024},{dot,1}],1}</pre>
-<p>In the shell, if using a Unicode input device, '$' can be followed directly by a Unicode character producing an integer. In the following example, let's imagine the character 'c' is actually a Cyrillic 's' (looking fairly similar):</p>
+<title>String- and Character-literals</title>
+<p>For source code, there is an extension to the <c>\</c>OOO (backslash
+followed by three octal numbers) and <c>\x</c>HH (backslash followed by <c>x</c>,
+followed by two hexadecimal characters) syntax, namely <c>\x{</c>H ...<c>}</c> (a
+backslash followed by an <c>x</c>, followed by left curly bracket, any
+number of hexadecimal digits and a terminating right curly bracket).
+This allows for entering characters of any codepoint literally in a
+string even when the encoding is ISO-latin-1.</p>
+</section>
+<p>In the shell, if using a Unicode input device, <c>$</c> can be followed directly by a Unicode character producing an integer. In the following example the codepoint of a Cyrillic <c>s</c> is output:</p>
<pre>
-7> <input>$c.</input>
+7> <input>$с.</input>
1089</pre>
</section>
-<p>The literal syntax allowing Unicode characters is to be viewed as "syntactic sugar", but is, as such, fairly useful.</p>
-</section>
<section>
-<title>The interactive shell</title>
+<title>The Interactive Shell</title>
<p>The interactive Erlang shell, when started towards a terminal or started using the <c>werl</c> command on windows, can support Unicode input and output.</p>
-<p>On Windows&reg;, proper operation requires that a suitable font is installed and selected for the Erlang application to use. If no suitable font is available on your system, try installing the DejaVu fonts (dejavu-fonts.org), which are freely available and then select that font in the Erlang shell application.</p>
-<p>On Unix&reg;-like operating systems, the terminal should be able to handle UTF-8 on input and output (modern versions of XTerm, KDE konsole and the Gnome terminal do for example) and your locale settings have to be proper. As an example, my LANG environment variable is set as this:</p>
+<p>On Windows&reg;, proper operation requires that a suitable font is installed and selected for the Erlang application to use. If no suitable font is available on your system, try installing the DejaVu fonts (<c>dejavu-fonts.org</c>), which are freely available and then select that font in the Erlang shell application.</p>
+<p>On Unix&reg;-like operating systems, the terminal should be able to handle UTF-8 on input and output (modern versions of XTerm, KDE konsole and the Gnome terminal do for example) and your locale settings have to be proper. As an example, my <c>LANG</c> environment variable is set as this:</p>
<pre>
$ <input>echo $LANG</input>
en_US.UTF-8</pre>
-<p>Actually, most systems handle the LC_CTYPE variable before LANG, so if that is set, it has to be set to UTF-8:</p>
+<p>Actually, most systems handle the <c>LC_CTYPE</c> variable before <c>LANG</c>, so if that is set, it has to be set to <c>UTF-8</c>:</p>
<pre>
$ echo <input>$LC_CTYPE</input>
en_US.UTF-8</pre>
-<p>The LANG or LC_CTYPE setting should be consistent with what the terminal is capable of, there is no portable way for Erlang to ask the actual terminal about its UTF-8 capacity, we have to rely on the language and character type settings.</p>
+<p>The <c>LANG</c> or <c>LC_CTYPE</c> setting should be consistent with what the terminal is capable of, there is no portable way for Erlang to ask the actual terminal about its UTF-8 capacity, we have to rely on the language and character type settings.</p>
<p>To investigate what Erlang thinks about the terminal, the <c>io:getopts()</c> call can be used when the shell is started:</p>
<pre>
$ <input>LC_CTYPE=en_US.ISO-8859-1 erl</input>
-Erlang R13A (erts-5.7) [source] [64-bit] [smp:4:4] [rq:4] [async-threads:0] [kernel-poll:false]
+Erlang R16B (erts-5.10) [source] [async-threads:0] [hipe] [kernel-poll:false]
-Eshell V5.7 (abort with ^G)
-1> <input>lists:keyfind(encoding,1,io:getopts()).</input>
+Eshell V5.10 (abort with ^G)
+1> <input>lists:keyfind(encoding, 1, io:getopts()).</input>
{encoding,latin1}
2> <input>q().</input>
ok
$ <input>LC_CTYPE=en_US.UTF-8 erl</input>
-Erlang R13A (erts-5.7) [source] [64-bit] [smp:4:4] [rq:4] [async-threads:0] [kernel-poll:false]
+Erlang R16B (erts-5.10) [source] [async-threads:0] [hipe] [kernel-poll:false]
-Eshell V5.7 (abort with ^G)
-1> <input>lists:keyfind(encoding,1,io:getopts()).</input>
+Eshell V5.10 (abort with ^G)
+1> <input>lists:keyfind(encoding, 1, io:getopts()).</input>
{encoding,unicode}
2></pre>
<p>When (finally?) everything is in order with the locale settings, fonts and the terminal emulator, you probably also have discovered a way to input characters in the script you desire. For testing, the simplest way is to add some keyboard mappings for other languages, usually done with some applet in your desktop environment. In my KDE environment, I start the KDE Control Center (Personal Settings), select "Regional and Accessibility" and then "Keyboard Layout". On Windows XP&reg;, I start Control Panel->Regional and Language Options, select the Language tab and click the Details... button in the square named "Text services and input Languages". Your environment probably provides similar means of changing the keyboard layout. Make sure you have a way to easily switch back and forth between keyboards if you are not used to this, entering commands using a Cyrillic character set is, as an example, not easily done in the Erlang shell.</p>
<p>Now you are set up for some Unicode input and output. The simplest thing to do is of course to enter a string in the shell:</p>
-<image file="ushell1.gif"><icaption>Cyrillic characters in an Erlang shell</icaption></image>
+<pre>
+$ <input>erl</input>
+Erlang R16B (erts-5.10) [source] [async-threads:0] [hipe] [kernel-poll:false]
+
+Eshell V5.10 (abort with ^G)
+1> <input>lists:keyfind(encoding, 1, io:getopts()).</input>
+{encoding,unicode}
+2> <input>"уницоде"</input>
+"уницоде"
+3> <input>io:format("~ts~n", [v(2)]).</input>
+уницоде
+ok
+4> </pre>
<p>While strings can be input as Unicode characters, the language elements are still limited to the ISO-latin-1 character set. Only character constants and strings are allowed to be beyond that range:</p>
-<image file="ushell2.gif"><icaption>Unicode characters in allowed and disallowed context</icaption></image>
+<pre>
+$ <input>erl</input>
+Erlang R16B (erts-5.10) [source] [async-threads:0] [hipe] [kernel-poll:false]
+
+Eshell V5.10 (abort with ^G)
+1> <input>$ξ</input>
+958
+2> <input>уницоде.</input>
+* 1: illegal character
+2> </pre>
</section>
<section>
-<title>Unicode file names</title>
+<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>
@@ -183,7 +197,7 @@ Eshell V5.7 (abort with ^G)
<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>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 is supported by the underlying OS and file system). The Unicode character list is used to denote file or directory names and if the file system content is listed, you will also 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>
@@ -193,25 +207,32 @@ Eshell V5.7 (abort with ^G)
<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>
+<note><p>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.</p></note>
<section>
-<title>Notes about raw file names and automatic file name conversion</title>
-<p>Raw file names is introduced together with Unicode file name support in erts-5.8.2 (OTP R14B01). The reason &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>
+<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>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 <seealso marker="erts:erl"><c>erl(1)</c></seealso> 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>
+<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>
+<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>
+<title>Unicode in Environment Variables and Parameters</title>
+<p>Environment variables and their interpretation is handled much in the same way as file names. If Unicode file names are enabled, environment variables as well as parameters to the Erlang VM are expected to be in Unicode.</p>
+<p>If Unicode file names are enabled, the calls to <seealso marker="kernel:os#getenv/0"><c>os:getenv/0</c></seealso>, <seealso marker="kernel:os#getenv/1"><c>os:getenv/1</c></seealso> and <seealso marker="kernel:os#putenv/2"><c>os:putenv/2</c></seealso> will handle Unicode strings. On Unix-like platforms, the built-in functions will translate environment variables in UTF-8 to/from Unicode strings, possibly with codepoints > 255. On Windows the Unicode versions of the environment system API will be used, also allowing for codepoints > 255.</p>
+<p>On Unix-like operating systems, parameters are expected to be UTF-8 without translation if Unicode file names are enabled.</p>
+</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 should not have. Typically they handle non-textual or byte-oriented data (like <c>gen_tcp</c> etc).</p>
<p>Modules that actually handle textual data (like <c>io_lib</c>, <c>string</c> etc) are sometimes subject to conversion or extension to be able to handle Unicode characters.</p>
<p>Fortunately, most textual data has been stored in lists and range checking has been sparse, why modules like <c>string</c> works well for Unicode lists with little need for conversion or extension.</p>
<p>Some modules are however changed to be explicitly Unicode-aware. These modules include:</p>
@@ -224,7 +245,7 @@ Eshell V5.7 (abort with ^G)
<item>
<p>The <seealso marker="stdlib:io">io</seealso> module has been extended along with the actual I/O-protocol to handle Unicode data. This means that several functions require binaries to be in UTF-8 and there are modifiers to formatting control sequences to allow for outputting of Unicode strings.</p>
</item>
-<tag><c>file</c>, <c>group</c> and <c>user</c></tag>
+<tag><c>file</c>, <c>group</c>, <c>user</c></tag>
<item>
<p>I/O-servers throughout the system are able both to handle Unicode data and has options for converting data upon actual output or input to/from the device. As shown earlier, the <seealso marker="stdlib:shell">shell</seealso> has support for Unicode terminals and the <seealso marker="kernel:file">file</seealso> module allows for translation to and from various Unicode formats on disk.</p>
<p>The actual reading and writing of files with Unicode data is however not best done with the <c>file</c> module as its interface is byte oriented. A file opened with a Unicode encoding (like UTF-8), is then best read or written using the <seealso marker="stdlib:io">io</seealso> module.</p>
@@ -241,10 +262,10 @@ Eshell V5.7 (abort with ^G)
<p>The module <seealso marker="stdlib:string">string</seealso> works perfect for Unicode strings as well as for ISO-latin-1 strings with the exception of the language-dependent <seealso marker="stdlib:string#to_upper/1">to_upper</seealso> and <seealso marker="stdlib:string#to_lower/1">to_lower</seealso> functions, which are only correct for the ISO-latin-1 character set. Actually they can never function correctly for Unicode characters in their current form, there are language and locale issues as well as multi-character mappings to consider when conversion text between cases. Converting case in an international environment is a big subject not yet addressed in OTP.</p>
</section>
<section>
-<title>Unicode recipes</title>
+<title>Unicode Recipes</title>
<p>When starting with Unicode, one often stumbles over some common issues. I try to outline some methods of dealing with Unicode data in this section.</p>
<section>
-<title>Byte order marks</title>
+<title>Byte Order Marks</title>
<p>A common method of identifying encoding in text-files is to put a byte order mark (BOM) first in the file. The BOM is the codepoint 16#FEFF encoded in the same way as the rest of the file. If such a file is to be read, the first few bytes (depending on encoding) is not part of the actual text. This code outlines how to open a file which is believed to have a BOM and set the files encoding and position for further sequential reading (preferably using the <seealso marker="stdlib:io">io</seealso> module). Note that error handling is omitted from the code:</p>
<code>
open_bom_file_for_reading(File) -&gt;
@@ -255,7 +276,7 @@ open_bom_file_for_reading(File) -&gt;
io:setopts(F,[{encoding,Type}]),
{ok,F}.
</code>
-<p>The <c>unicode:bom_to_encoding/1</c> function identifies the encoding from a binary of at least four bytes. It returns, along with an term suitable for setting the encoding of the file, the actual length of the BOM, so that the file position can be set accordingly. Note that <c>file:position</c> always works on byte-offsets, so that the actual byte-length of the BOM is needed.</p>
+<p>The <c>unicode:bom_to_encoding/1</c> function identifies the encoding from a binary of at least four bytes. It returns, along with an term suitable for setting the encoding of the file, the actual length of the BOM, so that the file position can be set accordingly. Note that <c>file:position/2</c> always works on byte-offsets, so that the actual byte-length of the BOM is needed.</p>
<p>To open a file for writing and putting the BOM first is even simpler:</p>
<code>
open_bom_file_for_writing(File,Encoding) -&gt;
@@ -267,24 +288,33 @@ open_bom_file_for_writing(File,Encoding) -&gt;
<p>In both cases the file is then best processed using the <c>io</c> module, as the functions in <c>io</c> can handle codepoints beyond the ISO-latin-1 range.</p>
</section>
<section>
-<title>Formatted input and output</title>
-<p>When reading and writing to Unicode-aware entities, like the User or a file opened for Unicode translation, you will probably want to format text strings using the functions in <seealso marker="stdlib:io">io</seealso> or <seealso marker="stdlib:io_lib">io_lib</seealso>. For backward compatibility reasons, these functions don't accept just any list as a string, but require e special "translation modifier" when working with Unicode texts. The modifier is "t". When applied to the "s" control character in a formatting string, it accepts all Unicode codepoints and expect binaries to be in UTF-8:</p>
+<title>Formatted Input and Output</title>
+<p>When reading and writing to Unicode-aware entities, like the User or a file opened for Unicode translation, you will probably want to format text strings using the functions in <seealso marker="stdlib:io">io</seealso> or <seealso marker="stdlib:io_lib">io_lib</seealso>. For backward compatibility reasons, these functions do not accept just any list as a string, but require a special <em>translation modifier</em> when working with Unicode texts. The modifier is <c>t</c>. When applied to the <c>s</c> control character in a formatting string, it accepts all Unicode codepoints and expect binaries to be in UTF-8:</p>
<pre>
-1> <input>io:format("~ts~n",[&lt;&lt;"���"/utf8&gt;&gt;]).</input>
-���
-ok
-2> <input>io:format("~s~n",[&lt;&lt;"���"/utf8&gt;&gt;]).</input>
+1> <input>io:format("~ts~n",[&lt;&lt;"åäö"/utf8&gt;&gt;]).</input>
åäö
+ok
+2> <input>io:format("~s~n",[&lt;&lt;"åäö"/utf8&gt;&gt;]).</input>
+åäö
+ok</pre>
+<p>Obviously the second <c>io:format/2</c> gives undesired output because the UTF-8 binary is not in latin1. Because ISO-latin-1 is still the defined character set of Erlang, the non prefixed <c>s</c> control character expects ISO-latin-1 in binaries as well as lists.</p>
+<p>As long as the data is always lists, the <c>t</c> modifier can be used for any string, but when binary data is involved, care must be taken to make the right choice of formatting characters.</p>
+<p>The function <c>format/2</c> in <c>io_lib</c> behaves similarly. This function is defined to return a deep list of characters and the output could easily be converted to binary data for outputting on a device of any kind by a simple <c>erlang:list_to_binary/1</c>. When the translation modifier is used, the list can however contain characters that cannot be stored in one byte. The call to <c>erlang:list_to_binary/1</c> will in that case fail. However, if the I/O server you want to communicate with is Unicode-aware, the list returned can still be used directly:</p>
+<pre>
+$ <input>erl</input>
+Erlang R16B (erts-5.10) [source] [async-threads:0] [hipe] [kernel-poll:false]
+
+Eshell V5.10 (abort with ^G)
+1> <input>io_lib:format("~ts~n", ["θνιψοδε"]).</input>
+["θνιψοδε","\n"]
+2> <input>io:put_chars(io_lib:format("~ts~n", ["θνιψοδε"])).</input>
+θνιψοδε
ok</pre>
-<p>Obviously the second <c>io:format</c> gives undesired output because the UTF-8 binary is not in latin1. Because ISO-latin-1 is still the defined character set of Erlang, the non prefixed "s" control character expects ISO-latin-1 in binaries as well as lists.</p>
-<p>As long as the data is always lists, the "t" modifier can be used for any string, but when binary data is involved, care must be taken to make the tight choice of formatting characters.</p>
-<p>The function <c>format</c> in <c>io_lib</c> behaves similarly. This function is defined to return a deep list of characters and the output could easily be converted to binary data for outputting on a device of any kind by a simple <c>erlang:list_to_binary</c>. When the translation modifier is used, the list can however contain characters that cannot be stored in one byte. The call to <c>erlang:list_to_binary</c> will in that case fail. However, if the io_server you want to communicate with is Unicode-aware, the list returned can still be used directly:</p>
-<image file="ushell3.gif"><icaption>io_lib:format with Unicode translation</icaption></image>
-<p>The Unicode string is returned as a Unicode list, why the return value of <c>io_lib:format</c> no longer qualifies as a regular Erlang string (the function <seealso marker="stdlib:io_lib#deep_char_list/1">io_lib:deep_char_list</seealso> will, as an example, return <c>false</c>). The Unicode list is however valid input to the <seealso marker="stdlib:io#put_chars/2">io:put_chars</seealso> function, so data can be output on any Unicode capable device anyway. If the device is a terminal, characters will be output in the \x{H ...} format if encoding is <c>latin1</c> otherwise in UTF-8 (for the non-interactive terminal - "oldshell" or "noshell") or whatever is suitable to show the character properly (for an interactive terminal - the regular shell). The bottom line is that you can always send Unicode data to the <c>standard_io</c> device. Files will however only accept Unicode codepoints beyond ISO-latin-1 if <c>encoding</c> is set to something else than <c>latin1</c>.</p>
+<p>The Unicode string is returned as a Unicode list, which is recognized as such since the Erlang shell uses the Unicode encoding. The Unicode list is valid input to the <seealso marker="stdlib:io#put_chars/2">io:put_chars/2</seealso> function, so data can be output on any Unicode capable device. If the device is a terminal, characters will be output in the <c>\x{</c>H ...<c>}</c> format if encoding is <c>latin1</c> otherwise in UTF-8 (for the non-interactive terminal - "oldshell" or "noshell") or whatever is suitable to show the character properly (for an interactive terminal - the regular shell). The bottom line is that you can always send Unicode data to the <c>standard_io</c> device. Files will however only accept Unicode codepoints beyond ISO-latin-1 if <c>encoding</c> is set to something else than <c>latin1</c>.</p>
</section>
<section>
-<title>Heuristic identification of UTF-8</title>
-<p>While it's strongly encouraged that the actual encoding of characters in binary data is known prior to processing, that is not always possible. On a typical Linux&reg; system, there is a mix of UTF-8 and ISO-latin-1 text files and there are seldom any BOM's in the files to identify them.</p>
+<title>Heuristic Identification of UTF-8</title>
+<p>While it iss strongly encouraged that the actual encoding of characters in binary data is known prior to processing, that is not always possible. On a typical Linux&reg; system, there is a mix of UTF-8 and ISO-latin-1 text files and there are seldom any BOM's in the files to identify them.</p>
<p>UTF-8 is designed in such a way that ISO-latin-1 characters with numbers beyond the 7-bit ASCII range are seldom considered valid when decoded as UTF-8. Therefore one can usually use heuristics to determine if a file is in UTF-8 or if it is encoded in ISO-latin-1 (one byte per character) encoding. The <c>unicode</c> module can be used to determine if data can be interpreted as UTF-8:</p>
<code>
heuristic_encoding_bin(Bin) when is_binary(Bin) -&gt;
diff --git a/lib/stdlib/doc/src/ushell1.gif b/lib/stdlib/doc/src/ushell1.gif
deleted file mode 100644
index 7c46464fd2..0000000000
--- a/lib/stdlib/doc/src/ushell1.gif
+++ /dev/null
Binary files differ
diff --git a/lib/stdlib/doc/src/ushell1.ps b/lib/stdlib/doc/src/ushell1.ps
deleted file mode 100644
index 95bfebf194..0000000000
--- a/lib/stdlib/doc/src/ushell1.ps
+++ /dev/null
@@ -1,1196 +0,0 @@
-%!PS-Adobe-3.0
-%%Creator: GIMP PostScript file plugin V 1.17 by Peter Kirchgessner
-%%Title: ushell1.ps
-%%CreationDate: Mon Mar 16 09:53:27 2009
-%%DocumentData: Clean7Bit
-%%LanguageLevel: 2
-%%Pages: 1
-%%BoundingBox: 14 14 468 142
-%%EndComments
-%%BeginProlog
-% Use own dictionary to avoid conflicts
-10 dict begin
-%%EndProlog
-%%Page: 1 1
-% Translate for offset
-14.173228346456694 14.173228346456694 translate
-% Translate to begin of first scanline
-0 127.55905511811024 translate
-453.54330708661422 -127.55905511811024 scale
-% Image geometry
-640 180 8
-% Transformation matrix
-[ 640 0 0 180 0 0 ]
-% Strings to hold RGB-samples per scanline
-/rstr 640 string def
-/gstr 640 string def
-/bstr 640 string def
-{currentfile /ASCII85Decode filter /RunLengthDecode filter rstr readstring pop}
-{currentfile /ASCII85Decode filter /RunLengthDecode filter gstr readstring pop}
-{currentfile /ASCII85Decode filter /RunLengthDecode filter bstr readstring pop}
-true 3
-%%BeginData: 67571 ASCII Bytes
-colorimage
-JP1PeJP1PeJP1L~>
-JP1PeJP1PeJP1L~>
-JP1PeJP1PeJP1L~>
-!!e'9JNA?CJNABDJ,~>
-!!e'9JNA?CJNABDJ,~>
-!!e'9JNA?CJNABDJ,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$2%<!Lr-2rr]6=Dt\b^JcC<$JcD\KJ,~>
-!$2%<!Lr-2rr]6=Dt\b^JcC<$JcD\KJ,~>
-!$2%<!Lr-2rr]6=Dt\b^JcC<$JcD\KJ,~>
-#9EjEY>Q6)=6BMbc`J;XJcC<$JcC<$W;hA~>
-#9EjEY>Q6)=6BMbc`J;XJcC<$JcC<$W;hA~>
-#9EjEY>Q6)=6BMbc`J;XJcC<$JcC<$W;hA~>
-#T`rUH-/@H`Dbmu!A3bks+13$s+13Ks*t~>
-#T`rUH-/@H`Dbmu!A3bks+13$s+13Ks*t~>
-#T`rUH-/@H`Dbmu!A3bks+13$s+13Ks*t~>
-#T`rEG5Y4]rp0@Z!W@cP*=4P!s-oDP9H?1krVlmMmt(Lis+13$s/>sJ~>
-#T`rEG5Y4]rp0@Z!W@cP*=4P!s-oDP9H?1krVlmMmt(Lis+13$s/>sJ~>
-#T`rEG5Y4]rp0@Z!W@cP*=4P!s-oDP9H?1krVlmMmt(Lis+13$s/>sJ~>
-#9EjER>jY@r:g3lQA5D=$0F2Hs)Ak%s4op<rrF_?JcC<$JcC<$W;hA~>
-#9EjER>jY@r:g3lQA5D=$0F2Hs)Ak%s4op<rrF_?JcC<$JcC<$W;hA~>
-#9EjER>jY@r:g3lQA5D=$0F2Hs)Ak%s4op<rrF_?JcC<$JcC<$W;hA~>
-!$2(="-F9Q\G-"(Q=^'2$*?/ds)G.?s5e.srrF_?JcC<$JcC<$W;hA~>
-!$2(="-F9Q\G-"(Q=^'2$*?/ds)G.?s5e.srrF_?JcC<$JcC<$W;hA~>
-!$2(="-F9Q\G-"(Q=^'2$*?/ds)G.?s5e.srrF_?JcC<$JcC<$W;hA~>
-!$2%<"&I0WZ2">"Q8&8B"FpIOENK!9!A3bks+13$s+13Ks*t~>
-!$2%<"&I0WZ2">"Q8&8B"FpIOENK!9!A3bks+13$s+13Ks*t~>
-!$2%<"&I0WZ2">"Q8&8B"FpIOENK!9!A3bks+13$s+13Ks*t~>
-#T`rq^Ah!RpEo\8!LAK:rrI,@q>UIImt(Lis+13$s/>sJ~>
-#T`rq^Ah!RpEo\8!LAK:rrI,@q>UIImt(Lis+13$s/>sJ~>
-#T`rq^Ah!RpEo\8!LAK:rrI,@q>UIImt(Lis+13$s/>sJ~>
-#T`s"JSH?.CSgh+!RuStC&iMXrrI,@qu6esC(k*/JcC<$JcC<$WW.J~>
-#T`s"JSH?.CSgh+!RuStC&iMXrrI,@qu6esC(k*/JcC<$JcC<$WW.J~>
-#T`s"JSH?.CSgh+!RuStC&iMXrrI,@qu6esC(k*/JcC<$JcC<$WW.J~>
-#9EjEgp/o#[J'V#fD`&U])M^1eE6Z.!7LkP!5F*bJcC<$JcD_LJ,~>
-#9EjEgp/o#[J'V#fD`&U])M^1eE6Z.!7LkP!5F*bJcC<$JcD_LJ,~>
-#9EjEgp/o#[J'V#fD`&U])M^1eE6Z.!7LkP!5F*bJcC<$JcD_LJ,~>
-!$2%<!T*O$s+13$s+13+s*t~>
-!$2%<!T*O$s+13$s+13+s*t~>
-!$2%<!T*O$s+13$s+13+s*t~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$.X1!VbZgrrMcToD\g]rn%5Fci4"4rn%5Kp&>-Vf\#!)rr_95f\lE%"5DJ\kK!T&r;P=KmIgJZ
-p&<SDo\]ZKoD[ABp@\Fcm/GW;rJ1CTgXt0Ap&>$hrn%5=kPp&~>
-!$.X1!VbZgrrMcToD\g]rn%5Fci4"4rn%5Kp&>-Vf\#!)rr_95f\lE%"5DJ\kK!T&r;P=KmIgJZ
-p&<SDo\]ZKoD[ABp@\Fcm/GW;rJ1CTgXt0Ap&>$hrn%5=kPp&~>
-!$.X1!VbZgrrMcToD\g]rn%5Fci4"4rn%5Kp&>-Vf\#!)rr_95f\lE%"5DJ\kK!T&r;P=KmIgJZ
-p&<SDo\]ZKoD[ABp@\Fcm/GW;rJ1CTgXt0Ap&>$hrn%5=kPp&~>
-!Zh=)r4;sJp\t?M]=[dnrrC^M])^+RrrBt8rrCOG])^O`rrN,;oD\mfUOUkp!5JHD!:K[_!<2l-
-"nM]nb];P3rrf;C6q#_Srrf>i6k8JSrrZO_6rs9d"4Ffn`;BQ8r5SL2!SZc5rrL6To`##66p3I#
-rrWs'7#p%%s5ESL#i+SBs6i?D-]@Zt"M5R?7*O]k#gN&Bs2.5m(WQ.J"Fqsi7/+e&!SceorrM<;
-mf3=!!7LiG"&[email protected])jpjdIXT/=<lMlA~>
-!Zh=)r4;sJp\t?M]=[dnrrC^M])^+RrrBt8rrCOG])^O`rrN,;oD\mfUOUkp!5JHD!:K[_!<2l-
-"nM]nb];P3rrf;C6q#_Srrf>i6k8JSrrZO_6rs9d"4Ffn`;BQ8r5SL2!SZc5rrL6To`##66p3I#
-rrWs'7#p%%s5ESL#i+SBs6i?D-]@Zt"M5R?7*O]k#gN&Bs2.5m(WQ.J"Fqsi7/+e&!SceorrM<;
-mf3=!!7LiG"&[email protected])jpjdIXT/=<lMlA~>
-!Zh=)r4;sJp\t?M]=[dnrrC^M])^+RrrBt8rrCOG])^O`rrN,;oD\mfUOUkp!5JHD!:K[_!<2l-
-"nM]nb];P3rrf;C6q#_Srrf>i6k8JSrrZO_6rs9d"4Ffn`;BQ8r5SL2!SZc5rrL6To`##66p3I#
-rrWs'7#p%%s5ESL#i+SBs6i?D-]@Zt"M5R?7*O]k#gN&Bs2.5m(WQ.J"Fqsi7/+e&!SceorrM<;
-mf3=!!7LiG"&[email protected])jpjdIXT/=<lMlA~>
-"!.E>/H&uWGk_4?XC2t&hZ!Um6iLKpEPqeU!Nm==rrAJc@0&Z7rr3%sBt![6!U2E,rrMo!m/I&"
-r*]TWq>UHor*]UHci3qFSUgA4!NC.^rrFq?p&>%u_#438a`V$#Fo21>SLO=5!Hk=9rrIX^qu6]"
-qYL3mju2l3!$0bm!oI+@o`#!G9q_Fa!Ki<5rrK`?iVrtr.,4\"!ER55rrIY@bPqUQnGW@fGGa6s
-!Mju/rrf?1@<TfkrrKf@p&>'T2Z3RT*U<ZT~>
-"!.E>/H&uWGk_4?XC2t&hZ!Um6iLKpEPqeU!Nm==rrAJc@0&Z7rr3%sBt![6!U2E,rrMo!m/I&"
-r*]TWq>UHor*]UHci3qFSUgA4!NC.^rrFq?p&>%u_#438a`V$#Fo21>SLO=5!Hk=9rrIX^qu6]"
-qYL3mju2l3!$0bm!oI+@o`#!G9q_Fa!Ki<5rrK`?iVrtr.,4\"!ER55rrIY@bPqUQnGW@fGGa6s
-!Mju/rrf?1@<TfkrrKf@p&>'T2Z3RT*U<ZT~>
-"!.E>/H&uWGk_4?XC2t&hZ!Um6iLKpEPqeU!Nm==rrAJc@0&Z7rr3%sBt![6!U2E,rrMo!m/I&"
-r*]TWq>UHor*]UHci3qFSUgA4!NC.^rrFq?p&>%u_#438a`V$#Fo21>SLO=5!Hk=9rrIX^qu6]"
-qYL3mju2l3!$0bm!oI+@o`#!G9q_Fa!Ki<5rrK`?iVrtr.,4\"!ER55rrIY@bPqUQnGW@fGGa6s
-!Mju/rrf?1@<TfkrrKf@p&>'T2Z3RT*U<ZT~>
-"!.E>Fn#D352P/r!Go">rs$)Ds8SmD(]"(;jFF`>s."Z-J$8PF!JQp-rrML@m/I&+mf*?sJ*6h2
-!mQ8Ap&>&lF34F_1[4T4!F<M>rrU8keG9+GR!:(SrrI8?nc&V>o(r@eju2l3!$0en"6=u%O7iMT
-gjhP\r;Qe[MtR)N]4'_!"297-g\h'P<lXh4!J$`arrGO?rVlnBVV_=RUj2D/#+pAEs3lJ>rVlo3
[email protected]#ju3/;!$1A)J,~>
-"!.E>Fn#D352P/r!Go">rs$)Ds8SmD(]"(;jFF`>s."Z-J$8PF!JQp-rrML@m/I&+mf*?sJ*6h2
-!mQ8Ap&>&lF34F_1[4T4!F<M>rrU8keG9+GR!:(SrrI8?nc&V>o(r@eju2l3!$0en"6=u%O7iMT
-gjhP\r;Qe[MtR)N]4'_!"297-g\h'P<lXh4!J$`arrGO?rVlnBVV_=RUj2D/#+pAEs3lJ>rVlo3
[email protected]#ju3/;!$1A)J,~>
-"!.E>Fn#D352P/r!Go">rs$)Ds8SmD(]"(;jFF`>s."Z-J$8PF!JQp-rrML@m/I&+mf*?sJ*6h2
-!mQ8Ap&>&lF34F_1[4T4!F<M>rrU8keG9+GR!:(SrrI8?nc&V>o(r@eju2l3!$0en"6=u%O7iMT
-gjhP\r;Qe[MtR)N]4'_!"297-g\h'P<lXh4!J$`arrGO?rVlnBVV_=RUj2D/#+pAEs3lJ>rVlo3
[email protected]#ju3/;!$1A)J,~>
-"!.E>Fo21B`NfH(A8hDG!C#B=rrB%tA-dJ\s0]Y*A7U/grro!)A9-Unq#:A2Yl=Y,]4(`5N:.e`
-rsS:7]`8"tO7rV9LTgFO!$1t:!-S9O&?Pp3K(J/9A?l17I9dLsA@MR<!+u4@!5/(+!?L;$A,sX@
-rrHo@r;QeISb<!`WH8";!8%0["6KR_df!\YlMob-rr3C]n,M+2s.s7:o`+s/rF?$+s8Th3A,uW,
-rrFq?p&>%u_#=96O,`u8"-Sf5.JNiGFf1%+A;L6b!gR;1rVm%pA.`0ZP5P=\ju2l3!$1t:!.4]U%
-&a.*Lh(\`A?>h2KC/YK!3uM&!omgerVm#hE4h0krVlrPA?Pn2"QaaBIA$N<!Ki<5rrK`?rVm2rJ
-cEJqAD7(`h>Updc%Y]errTT=df'1K_1:6*g\h'P<lXh4!J$a=rrBe3A-;i2s1/,>"DIi8`ioCA#
-^67J_6fbDA97bM!36$/!-n8<'sV9g<Fg^]s*(>sA7UJqs/40BEb(..rrA/[A,qbkrr@uVA,qqpr
-s4?iA;[1-s8RZLA,r>%rrVe&PlC[`45p2=!K3->rrKf@p&>'T2Z3RT*W?!?Us]5;rF?8Ss8R9Bc
-X^S/s8RT~>
-"!.E>Fo21B`NfH(A8hDG!C#B=rrB%tA-dJ\s0]Y*A7U/grro!)A9-Unq#:A2Yl=Y,]4(`5N:.e`
-rsS:7]`8"tO7rV9LTgFO!$1t:!-S9O&?Pp3K(J/9A?l17I9dLsA@MR<!+u4@!5/(+!?L;$A,sX@
-rrHo@r;QeISb<!`WH8";!8%0["6KR_df!\YlMob-rr3C]n,M+2s.s7:o`+s/rF?$+s8Th3A,uW,
-rrFq?p&>%u_#=96O,`u8"-Sf5.JNiGFf1%+A;L6b!gR;1rVm%pA.`0ZP5P=\ju2l3!$1t:!.4]U%
-&a.*Lh(\`A?>h2KC/YK!3uM&!omgerVm#hE4h0krVlrPA?Pn2"QaaBIA$N<!Ki<5rrK`?rVm2rJ
-cEJqAD7(`h>Updc%Y]errTT=df'1K_1:6*g\h'P<lXh4!J$a=rrBe3A-;i2s1/,>"DIi8`ioCA#
-^67J_6fbDA97bM!36$/!-n8<'sV9g<Fg^]s*(>sA7UJqs/40BEb(..rrA/[A,qbkrr@uVA,qqpr
-s4?iA;[1-s8RZLA,r>%rrVe&PlC[`45p2=!K3->rrKf@p&>'T2Z3RT*W?!?Us]5;rF?8Ss8R9Bc
-X^S/s8RT~>
-"!.E>Fo21B`NfH(A8hDG!C#B=rrB%tA-dJ\s0]Y*A7U/grro!)A9-Unq#:A2Yl=Y,]4(`5N:.e`
-rsS:7]`8"tO7rV9LTgFO!$1t:!-S9O&?Pp3K(J/9A?l17I9dLsA@MR<!+u4@!5/(+!?L;$A,sX@
-rrHo@r;QeISb<!`WH8";!8%0["6KR_df!\YlMob-rr3C]n,M+2s.s7:o`+s/rF?$+s8Th3A,uW,
-rrFq?p&>%u_#=96O,`u8"-Sf5.JNiGFf1%+A;L6b!gR;1rVm%pA.`0ZP5P=\ju2l3!$1t:!.4]U%
-&a.*Lh(\`A?>h2KC/YK!3uM&!omgerVm#hE4h0krVlrPA?Pn2"QaaBIA$N<!Ki<5rrK`?rVm2rJ
-cEJqAD7(`h>Updc%Y]errTT=df'1K_1:6*g\h'P<lXh4!J$a=rrBe3A-;i2s1/,>"DIi8`ioCA#
-^67J_6fbDA97bM!36$/!-n8<'sV9g<Fg^]s*(>sA7UJqs/40BEb(..rrA/[A,qbkrr@uVA,qqpr
-s4?iA;[1-s8RZLA,r>%rrVe&PlC[`45p2=!K3->rrKf@p&>'T2Z3RT*W?!?Us]5;rF?8Ss8R9Bc
-X^S/s8RT~>
-"!.E>+oY"=nGiOLJ4Q':Y\F(9!C#B=rrC[M[h5T^s+Mqh\$r2\s.PF]\#s&_q#:A21B'fKA^g\.
-li/"%rrth%K`D'Nb5VDAU3cP5!$2";!HG1>[hOJ#s2@f;\$pmts21i\\$t2ns'E/5[fL`lq#:@.
->Q2)4ER=CY!i_%@r;QeISb<!`WH8%<!Tdkj[f]k-he;tt"'>BB;Z?\)39C2.O@Y5;[oi[TF8`QM
->5uWerO2c3r;QdRl1P&W?,6F="cnXr84[+erreOOo(+=)rsNs&T!u2#L]@D"[ho#C"k:"&Y.)XQ
-rrM7?o`"n3r;QeBVuF.6Lo^P:UT.b3KX(P47fKjtJ@GOX!mgp]rVm"#a8_-[rVlr7!.+YE"nc'C
-s*^R<rrJ7?p&>')@K$34qP-T)\$ol2lsTh%!c/D1rr3%Q!4Dh+"j;#Cs$>E;rrH0?p&>&CSc&Kf
-iVg4f>lVWdrjN$<@K3$Jrr3BIs8SH4NjlL"D?$enrjMp-F85bQgUAo,\&JCIG;#Mr\"8?Sen[gZ
-\!r?VOdQ-W"IG;Os2k8L%$R(YKV,`_S1]'dI\ZnO!K<iRrrVKd:]C@p45p2=!K3->rrKf@p&>'T
-2Z3R\*WQ/Q8;ZX3=oGf:EmOcj88>WCDUnc;~>
-"!.E>+oY"=nGiOLJ4Q':Y\F(9!C#B=rrC[M[h5T^s+Mqh\$r2\s.PF]\#s&_q#:A21B'fKA^g\.
-li/"%rrth%K`D'Nb5VDAU3cP5!$2";!HG1>[hOJ#s2@f;\$pmts21i\\$t2ns'E/5[fL`lq#:@.
->Q2)4ER=CY!i_%@r;QeISb<!`WH8%<!Tdkj[f]k-he;tt"'>BB;Z?\)39C2.O@Y5;[oi[TF8`QM
->5uWerO2c3r;QdRl1P&W?,6F="cnXr84[+erreOOo(+=)rsNs&T!u2#L]@D"[ho#C"k:"&Y.)XQ
-rrM7?o`"n3r;QeBVuF.6Lo^P:UT.b3KX(P47fKjtJ@GOX!mgp]rVm"#a8_-[rVlr7!.+YE"nc'C
-s*^R<rrJ7?p&>')@K$34qP-T)\$ol2lsTh%!c/D1rr3%Q!4Dh+"j;#Cs$>E;rrH0?p&>&CSc&Kf
-iVg4f>lVWdrjN$<@K3$Jrr3BIs8SH4NjlL"D?$enrjMp-F85bQgUAo,\&JCIG;#Mr\"8?Sen[gZ
-\!r?VOdQ-W"IG;Os2k8L%$R(YKV,`_S1]'dI\ZnO!K<iRrrVKd:]C@p45p2=!K3->rrKf@p&>'T
-2Z3R\*WQ/Q8;ZX3=oGf:EmOcj88>WCDUnc;~>
-"!.E>+oY"=nGiOLJ4Q':Y\F(9!C#B=rrC[M[h5T^s+Mqh\$r2\s.PF]\#s&_q#:A21B'fKA^g\.
-li/"%rrth%K`D'Nb5VDAU3cP5!$2";!HG1>[hOJ#s2@f;\$pmts21i\\$t2ns'E/5[fL`lq#:@.
->Q2)4ER=CY!i_%@r;QeISb<!`WH8%<!Tdkj[f]k-he;tt"'>BB;Z?\)39C2.O@Y5;[oi[TF8`QM
->5uWerO2c3r;QdRl1P&W?,6F="cnXr84[+erreOOo(+=)rsNs&T!u2#L]@D"[ho#C"k:"&Y.)XQ
-rrM7?o`"n3r;QeBVuF.6Lo^P:UT.b3KX(P47fKjtJ@GOX!mgp]rVm"#a8_-[rVlr7!.+YE"nc'C
-s*^R<rrJ7?p&>')@K$34qP-T)\$ol2lsTh%!c/D1rr3%Q!4Dh+"j;#Cs$>E;rrH0?p&>&CSc&Kf
-iVg4f>lVWdrjN$<@K3$Jrr3BIs8SH4NjlL"D?$enrjMp-F85bQgUAo,\&JCIG;#Mr\"8?Sen[gZ
-\!r?VOdQ-W"IG;Os2k8L%$R(YKV,`_S1]'dI\ZnO!K<iRrrVKd:]C@p45p2=!K3->rrKf@p&>'T
-2Z3R\*WQ/Q8;ZX3=oGf:EmOcj88>WCDUnc;~>
-"!.E>@K+aVr;Q`rJHPNDmOnJ<!C#B9rt+!Ns+Ppms8UV?s*^O>s62?6rrm5&e/6]nr;Qa;qYpcM
-e=;Er;p,+>!MXo6rr=);rrG.?rr3F`PlLb'g].;(SH&WV0`:qO+T;<>"6]4S55Zo5_Z/6Crr3"*
-^@hL,Lm7f:!Ip[5rrK*?rVloQ62gfch#5\orVlsEq8uV7rsUmKs8Sm*mJm2,s1)Y<rr^sSZ#'C=
-!$2";!Aj!5rrHE@rVmFi(nB+*^0^i/Nq35A.KBF4r$r%hs)j7ms8U&>rVlj<qu6[Ho(r@eju2l3
-!$2";!CPT?rs\;\s#T/u49(2%s"_RmrrI\?rr3&oeE6c1"\.)Cs,E*<rrViBl2L\d6/2G>IA$N<
-!Ki<5rrK`?r;R&L5j&+H4l>?\rr3&Z/Ed$4$gQ75s8UhXjo>?Hg\h'P<lXh4!J$a8rrX;AWH8(=
-"TI-TTmQe=$m#BJQu;Bms6)??OGs2="8^pTS,=c:AcD]17+hJ<&U0-*s8U)>s8Prqqu=?:s(8b>
-rrKN?qu7)+E;rqZs8VbKGlLIarr3#elMgebr7'^)rrG4?rr3"WP5YC]]jUO5!Tl<<rs0Y*.qmH"
-s8O,<rsE/Hs6;)ns8Q3>s*t~>
-"!.E>@K+aVr;Q`rJHPNDmOnJ<!C#B9rt+!Ns+Ppms8UV?s*^O>s62?6rrm5&e/6]nr;Qa;qYpcM
-e=;Er;p,+>!MXo6rr=);rrG.?rr3F`PlLb'g].;(SH&WV0`:qO+T;<>"6]4S55Zo5_Z/6Crr3"*
-^@hL,Lm7f:!Ip[5rrK*?rVloQ62gfch#5\orVlsEq8uV7rsUmKs8Sm*mJm2,s1)Y<rr^sSZ#'C=
-!$2";!Aj!5rrHE@rVmFi(nB+*^0^i/Nq35A.KBF4r$r%hs)j7ms8U&>rVlj<qu6[Ho(r@eju2l3
-!$2";!CPT?rs\;\s#T/u49(2%s"_RmrrI\?rr3&oeE6c1"\.)Cs,E*<rrViBl2L\d6/2G>IA$N<
-!Ki<5rrK`?r;R&L5j&+H4l>?\rr3&Z/Ed$4$gQ75s8UhXjo>?Hg\h'P<lXh4!J$a8rrX;AWH8(=
-"TI-TTmQe=$m#BJQu;Bms6)??OGs2="8^pTS,=c:AcD]17+hJ<&U0-*s8U)>s8Prqqu=?:s(8b>
-rrKN?qu7)+E;rqZs8VbKGlLIarr3#elMgebr7'^)rrG4?rr3"WP5YC]]jUO5!Tl<<rs0Y*.qmH"
-s8O,<rsE/Hs6;)ns8Q3>s*t~>
-"!.E>@K+aVr;Q`rJHPNDmOnJ<!C#B9rt+!Ns+Ppms8UV?s*^O>s62?6rrm5&e/6]nr;Qa;qYpcM
-e=;Er;p,+>!MXo6rr=);rrG.?rr3F`PlLb'g].;(SH&WV0`:qO+T;<>"6]4S55Zo5_Z/6Crr3"*
-^@hL,Lm7f:!Ip[5rrK*?rVloQ62gfch#5\orVlsEq8uV7rsUmKs8Sm*mJm2,s1)Y<rr^sSZ#'C=
-!$2";!Aj!5rrHE@rVmFi(nB+*^0^i/Nq35A.KBF4r$r%hs)j7ms8U&>rVlj<qu6[Ho(r@eju2l3
-!$2";!CPT?rs\;\s#T/u49(2%s"_RmrrI\?rr3&oeE6c1"\.)Cs,E*<rrViBl2L\d6/2G>IA$N<
-!Ki<5rrK`?r;R&L5j&+H4l>?\rr3&Z/Ed$4$gQ75s8UhXjo>?Hg\h'P<lXh4!J$a8rrX;AWH8(=
-"TI-TTmQe=$m#BJQu;Bms6)??OGs2="8^pTS,=c:AcD]17+hJ<&U0-*s8U)>s8Prqqu=?:s(8b>
-rrKN?qu7)+E;rqZs8VbKGlLIarr3#elMgebr7'^)rrG4?rr3"WP5YC]]jUO5!Tl<<rs0Y*.qmH"
-s8O,<rsE/Hs6;)ns8Q3>s*t~>
-"!.E>Fo)+AJXc]>rq-0h!C#B>rrMn@rZ)+Z5Q?G(rr38S8H4C6nF56up\tD5YlF#Po_e^h*VfX;
-WcJ,<(]GEU&pj9O!$2";!BD(t*<[.Hs8O,=rrr2os8VI?r;Qfh+9!8_PktFNr:'daqu6\'^@qR.
-Wd+@:rrIV?p&>&lF8c+>rO`"K"3gbn9)\bl,PfJ[rr3-]jo>@VGlI^FoDc@2qu6]%%/h1H.fB;I
-1[4T4!F<M>rt(6GqZ$TP56$WZ*??+'>Q=KrnH8IaFf56=!QA.=rr=):rrFV?qYpTY2Y@"L*W5p<
-ofW3o%!VLH3o]*[s,*$?1@+r>!J-a8rr=\N*WHZN[eTk&+9!8^%MHbZrrJ7?p&>')@Jp-+cqXN>
-"82WS3;rjX2<Xf8!S.q`*<HH`q>L<o<lXh4!J$a>rrMt`r>btZs8%lW*<[SWs.Of=rraABs-SK=
-rrhOCs,`3:rrDlmnGr7]rrGO?rVlnBVZ-T(``E->;9](?qtC&%(&f3V)0#WK;Z7[>'QF(PaSu2B
-Uj2q47/e2-Dts,-!B]9>rrJ%@[email protected]#ju3/;!ua,Xm/?qa')`gR'Ysb61B.:TpAFr@~>
-"!.E>Fo)+AJXc]>rq-0h!C#B>rrMn@rZ)+Z5Q?G(rr38S8H4C6nF56up\tD5YlF#Po_e^h*VfX;
-WcJ,<(]GEU&pj9O!$2";!BD(t*<[.Hs8O,=rrr2os8VI?r;Qfh+9!8_PktFNr:'daqu6\'^@qR.
-Wd+@:rrIV?p&>&lF8c+>rO`"K"3gbn9)\bl,PfJ[rr3-]jo>@VGlI^FoDc@2qu6]%%/h1H.fB;I
-1[4T4!F<M>rt(6GqZ$TP56$WZ*??+'>Q=KrnH8IaFf56=!QA.=rr=):rrFV?qYpTY2Y@"L*W5p<
-ofW3o%!VLH3o]*[s,*$?1@+r>!J-a8rr=\N*WHZN[eTk&+9!8^%MHbZrrJ7?p&>')@Jp-+cqXN>
-"82WS3;rjX2<Xf8!S.q`*<HH`q>L<o<lXh4!J$a>rrMt`r>btZs8%lW*<[SWs.Of=rraABs-SK=
-rrhOCs,`3:rrDlmnGr7]rrGO?rVlnBVZ-T(``E->;9](?qtC&%(&f3V)0#WK;Z7[>'QF(PaSu2B
-Uj2q47/e2-Dts,-!B]9>rrJ%@[email protected]#ju3/;!ua,Xm/?qa')`gR'Ysb61B.:TpAFr@~>
-"!.E>Fo)+AJXc]>rq-0h!C#B>rrMn@rZ)+Z5Q?G(rr38S8H4C6nF56up\tD5YlF#Po_e^h*VfX;
-WcJ,<(]GEU&pj9O!$2";!BD(t*<[.Hs8O,=rrr2os8VI?r;Qfh+9!8_PktFNr:'daqu6\'^@qR.
-Wd+@:rrIV?p&>&lF8c+>rO`"K"3gbn9)\bl,PfJ[rr3-]jo>@VGlI^FoDc@2qu6]%%/h1H.fB;I
-1[4T4!F<M>rt(6GqZ$TP56$WZ*??+'>Q=KrnH8IaFf56=!QA.=rr=):rrFV?qYpTY2Y@"L*W5p<
-ofW3o%!VLH3o]*[s,*$?1@+r>!J-a8rr=\N*WHZN[eTk&+9!8^%MHbZrrJ7?p&>')@Jp-+cqXN>
-"82WS3;rjX2<Xf8!S.q`*<HH`q>L<o<lXh4!J$a>rrMt`r>btZs8%lW*<[SWs.Of=rraABs-SK=
-rrhOCs,`3:rrDlmnGr7]rrGO?rVlnBVZ-T(``E->;9](?qtC&%(&f3V)0#WK;Z7[>'QF(PaSu2B
-Uj2q47/e2-Dts,-!B]9>rrJ%@[email protected]#ju3/;!ua,Xm/?qa')`gR'Ysb61B.:TpAFr@~>
-"!.E>Fo)+=JXcK8!C#B>s8S,ZrrrD25Q?G(rr35R8H7pi/1a!Yrs!;Ds8UtYjSf)Y*W?!=j7N?N
-"KHMB;p,+>!MXo6rrGC?rVlmYj8/cU*W#d@m4eS?kjSQ)rVln:Xn_nrf_tgN?G?F=!qFb*rVlrl
-R#h.E!q(Q@p&>&lF8c+>q;2)M""Woj9)\bl,PfJ[rr3-]jo>@VGl7RB\RYU<"5*XYD"mr11[4T4
-!F<M>rrJ1?rr3#U55bE]o-sG6#'Ggrs8U&>rVlj<qu6dKo)J:BrVlo\2Y@"L*W5p<htd9O%$HMJ
-3o]*[s,*$?1;s1l!J-a>rrVrDjneuXNK=&<!qat*qYpSET`"fjOc/o4!P;e<rrLJ@r;QfZ3<&pZ
-i@O0krrVK7o(r@e6/2>;!ER55rrIY@rVlo(C]=>:fH("]kPkJiq'?!6HiO-#)uor*K`:uSkV`C%
-N;ihXqVLrG#=R5EpYc'qVZ-T!``E->;9\t<!G8h<rt/POs8VeRE;rqZs8V_IGlQ^rrr3"hJc>ZN
-r6sX(rrucDs8VSDV>^Dp]jUO5!Tl<<rs0Y+/83N"s8O,9rrMC?qu;0~>
-"!.E>Fo)+=JXcK8!C#B>s8S,ZrrrD25Q?G(rr35R8H7pi/1a!Yrs!;Ds8UtYjSf)Y*W?!=j7N?N
-"KHMB;p,+>!MXo6rrGC?rVlmYj8/cU*W#d@m4eS?kjSQ)rVln:Xn_nrf_tgN?G?F=!qFb*rVlrl
-R#h.E!q(Q@p&>&lF8c+>q;2)M""Woj9)\bl,PfJ[rr3-]jo>@VGl7RB\RYU<"5*XYD"mr11[4T4
-!F<M>rrJ1?rr3#U55bE]o-sG6#'Ggrs8U&>rVlj<qu6dKo)J:BrVlo\2Y@"L*W5p<htd9O%$HMJ
-3o]*[s,*$?1;s1l!J-a>rrVrDjneuXNK=&<!qat*qYpSET`"fjOc/o4!P;e<rrLJ@r;QfZ3<&pZ
-i@O0krrVK7o(r@e6/2>;!ER55rrIY@rVlo(C]=>:fH("]kPkJiq'?!6HiO-#)uor*K`:uSkV`C%
-N;ihXqVLrG#=R5EpYc'qVZ-T!``E->;9\t<!G8h<rt/POs8VeRE;rqZs8V_IGlQ^rrr3"hJc>ZN
-r6sX(rrucDs8VSDV>^Dp]jUO5!Tl<<rs0Y+/83N"s8O,9rrMC?qu;0~>
-"!.E>Fo)+=JXcK8!C#B>s8S,ZrrrD25Q?G(rr35R8H7pi/1a!Yrs!;Ds8UtYjSf)Y*W?!=j7N?N
-"KHMB;p,+>!MXo6rrGC?rVlmYj8/cU*W#d@m4eS?kjSQ)rVln:Xn_nrf_tgN?G?F=!qFb*rVlrl
-R#h.E!q(Q@p&>&lF8c+>q;2)M""Woj9)\bl,PfJ[rr3-]jo>@VGl7RB\RYU<"5*XYD"mr11[4T4
-!F<M>rrJ1?rr3#U55bE]o-sG6#'Ggrs8U&>rVlj<qu6dKo)J:BrVlo\2Y@"L*W5p<htd9O%$HMJ
-3o]*[s,*$?1;s1l!J-a>rrVrDjneuXNK=&<!qat*qYpSET`"fjOc/o4!P;e<rrLJ@r;QfZ3<&pZ
-i@O0krrVK7o(r@e6/2>;!ER55rrIY@rVlo(C]=>:fH("]kPkJiq'?!6HiO-#)uor*K`:uSkV`C%
-N;ihXqVLrG#=R5EpYc'qVZ-T!``E->;9\t<!G8h<rt/POs8VeRE;rqZs8V_IGlQ^rrr3"hJc>ZN
-r6sX(rrucDs8VSDV>^Dp]jUO5!Tl<<rs0Y+/83N"s8O,9rrMC?qu;0~>
-"!.E><;j6._>jOdS,<3sfsWK(^&S,8L:4Ot5<o1%Qi@!feO]_7NW-?d`V9B5Cp<p=%(fsJe$c\"
-[^O]cMOa[S"Ho5R;p,+>!MXo6rrM@?rVln=WrBF,f`(mN*W#dAnm/]4I)#\h[JmT8GfBIX!*]?0
-!HHNdrrT>'N;`bW_HQg9!M+c5rrK*?rVlo[Ac9%>>P6lerO)f1qq5fb[KU*@s8Sm>r;QfBF8`NL
->Q;`frO2V'!Aj!5rrHE@rVlo%L&SL]W-/%<!V7c7rsa*)S[PttL]@D![M?6mrVm"5Z*os^rVlo\
-2Y@"L*W5p<Gc(JK%#:qa3o]FGs,*$?1.V>P!J%]ZrrUOIC&7i1NK=&<!l+e^qYpSET`"fjOc/o4
-!P;e<rrLJ@r;Qfa@K*\:C_,_.rrRiR[JKn(6/2>;!ER55rrIY@rVlo>GQ,#R<ZM.VHN(>]ZXWsI
-I^Z[i'EA*"K`:uSkV`CEL&SL]WH@k6#C<5TTn35fVZ-T!``E->;9\t<!Ki`J[M6pbs,3AT[\#9n
-s+R&Q[[]!qs*pdB[K2>`rr3&c!)NRn"CAOFOdu@L!PMn6rrM7?r;R$Cs8Tc(M<Y%DrO)jhs8V@>
-qu;0~>
-"!.E><;j6._>jOdS,<3sfsWK(^&S,8L:4Ot5<o1%Qi@!feO]_7NW-?d`V9B5Cp<p=%(fsJe$c\"
-[^O]cMOa[S"Ho5R;p,+>!MXo6rrM@?rVln=WrBF,f`(mN*W#dAnm/]4I)#\h[JmT8GfBIX!*]?0
-!HHNdrrT>'N;`bW_HQg9!M+c5rrK*?rVlo[Ac9%>>P6lerO)f1qq5fb[KU*@s8Sm>r;QfBF8`NL
->Q;`frO2V'!Aj!5rrHE@rVlo%L&SL]W-/%<!V7c7rsa*)S[PttL]@D![M?6mrVm"5Z*os^rVlo\
-2Y@"L*W5p<Gc(JK%#:qa3o]FGs,*$?1.V>P!J%]ZrrUOIC&7i1NK=&<!l+e^qYpSET`"fjOc/o4
-!P;e<rrLJ@r;Qfa@K*\:C_,_.rrRiR[JKn(6/2>;!ER55rrIY@rVlo>GQ,#R<ZM.VHN(>]ZXWsI
-I^Z[i'EA*"K`:uSkV`CEL&SL]WH@k6#C<5TTn35fVZ-T!``E->;9\t<!Ki`J[M6pbs,3AT[\#9n
-s+R&Q[[]!qs*pdB[K2>`rr3&c!)NRn"CAOFOdu@L!PMn6rrM7?r;R$Cs8Tc(M<Y%DrO)jhs8V@>
-qu;0~>
-"!.E><;j6._>jOdS,<3sfsWK(^&S,8L:4Ot5<o1%Qi@!feO]_7NW-?d`V9B5Cp<p=%(fsJe$c\"
-[^O]cMOa[S"Ho5R;p,+>!MXo6rrM@?rVln=WrBF,f`(mN*W#dAnm/]4I)#\h[JmT8GfBIX!*]?0
-!HHNdrrT>'N;`bW_HQg9!M+c5rrK*?rVlo[Ac9%>>P6lerO)f1qq5fb[KU*@s8Sm>r;QfBF8`NL
->Q;`frO2V'!Aj!5rrHE@rVlo%L&SL]W-/%<!V7c7rsa*)S[PttL]@D![M?6mrVm"5Z*os^rVlo\
-2Y@"L*W5p<Gc(JK%#:qa3o]FGs,*$?1.V>P!J%]ZrrUOIC&7i1NK=&<!l+e^qYpSET`"fjOc/o4
-!P;e<rrLJ@r;Qfa@K*\:C_,_.rrRiR[JKn(6/2>;!ER55rrIY@rVlo>GQ,#R<ZM.VHN(>]ZXWsI
-I^Z[i'EA*"K`:uSkV`CEL&SL]WH@k6#C<5TTn35fVZ-T!``E->;9\t<!Ki`J[M6pbs,3AT[\#9n
-s+R&Q[[]!qs*pdB[K2>`rr3&c!)NRn"CAOFOdu@L!PMn6rrM7?r;R$Cs8Tc(M<Y%DrO)jhs8V@>
-qu;0~>
-!Zh<`r+6(Zs8TIDqu6Z!ral.Qrr38'AnGcCN;p?%rr3,`OoNUMral1M\bQ1*VpPGC"4j.FUASU*
-V>pRRral;$s8SGCrr3#>YP.ttno2/<rr@6AAcSt4rr@9=rr_7mB".d>!,):C!5/(+!;?A'!65!;
-!mYDhrVloQSGW<fj+Xf2rs,MlR@3@?s4I9^"6KR_e,<k\lMpn0ral>jGQ7]bY5A5!_u40Lq>^K/
-rFQ<6s8UK7R31\drrZPJRA9c]!36$1!."[email protected];Qh8As<5o"1">P
-2Y@"O(n$f.rr2tGral;2s8RrDrr3,2^AftMral/8rVlrZAu5A(!P?#CrrUkcYPS8)Z,ZhDp6h=M
-MtR)U]/uFKj8]/>Pl(I\hYq*gc8FearrTT?e,'(MNTpKChjKlh`qB?:J6nY3qu?]2ral<"GQ7]S
-ral.Frr383AnL-Fs8TpCrr3,kL&_1Rral.Uo`#,DAqU-`Xi^SB"l5XIs-DU?rrA2\AcS"nrt(-$
-AqnR0s8RjdArFa5s8R]MAcSS(rrVe(Q2L[^AcS:urr]!`EFAJ>#Nd.sRF;-8GQ%ODV:,D=rFQ2O
-s8VYCqu;0~>
-!Zh<`r+6(Zs8TIDqu6Z!ral.Qrr38'AnGcCN;p?%rr3,`OoNUMral1M\bQ1*VpPGC"4j.FUASU*
-V>pRRral;$s8SGCrr3#>YP.ttno2/<rr@6AAcSt4rr@9=rr_7mB".d>!,):C!5/(+!;?A'!65!;
-!mYDhrVloQSGW<fj+Xf2rs,MlR@3@?s4I9^"6KR_e,<k\lMpn0ral>jGQ7]bY5A5!_u40Lq>^K/
-rFQ<6s8UK7R31\drrZPJRA9c]!36$1!."[email protected];Qh8As<5o"1">P
-2Y@"O(n$f.rr2tGral;2s8RrDrr3,2^AftMral/8rVlrZAu5A(!P?#CrrUkcYPS8)Z,ZhDp6h=M
-MtR)U]/uFKj8]/>Pl(I\hYq*gc8FearrTT?e,'(MNTpKChjKlh`qB?:J6nY3qu?]2ral<"GQ7]S
-ral.Frr383AnL-Fs8TpCrr3,kL&_1Rral.Uo`#,DAqU-`Xi^SB"l5XIs-DU?rrA2\AcS"nrt(-$
-AqnR0s8RjdArFa5s8R]MAcSS(rrVe(Q2L[^AcS:urr]!`EFAJ>#Nd.sRF;-8GQ%ODV:,D=rFQ2O
-s8VYCqu;0~>
-!Zh<`r+6(Zs8TIDqu6Z!ral.Qrr38'AnGcCN;p?%rr3,`OoNUMral1M\bQ1*VpPGC"4j.FUASU*
-V>pRRral;$s8SGCrr3#>YP.ttno2/<rr@6AAcSt4rr@9=rr_7mB".d>!,):C!5/(+!;?A'!65!;
-!mYDhrVloQSGW<fj+Xf2rs,MlR@3@?s4I9^"6KR_e,<k\lMpn0ral>jGQ7]bY5A5!_u40Lq>^K/
-rFQ<6s8UK7R31\drrZPJRA9c]!36$1!."[email protected];Qh8As<5o"1">P
-2Y@"O(n$f.rr2tGral;2s8RrDrr3,2^AftMral/8rVlrZAu5A(!P?#CrrUkcYPS8)Z,ZhDp6h=M
-MtR)U]/uFKj8]/>Pl(I\hYq*gc8FearrTT?e,'(MNTpKChjKlh`qB?:J6nY3qu?]2ral<"GQ7]S
-ral.Frr383AnL-Fs8TpCrr3,kL&_1Rral.Uo`#,DAqU-`Xi^SB"l5XIs-DU?rrA2\AcS"nrt(-$
-AqnR0s8RjdArFa5s8R]MAcSS(rrVe(Q2L[^AcS:urr]!`EFAJ>#Nd.sRF;-8GQ%ODV:,D=rFQ2O
-s8VYCqu;0~>
-!$0_l!I^U>rrL>?c2Rh,WkJE5h6Z_Q!7h($!9VW-!65"j!;,sa"0dE1O3[b-Tn@ugo`##OK7gQ"
-rrFn@g]%9Grdt3jp&>$Krdt4,o)A_JkOAKOfD^C&j7WEP_>]&eqXFLccb08W!$.[2"-%qcZ1\+s
-oDX@BaQNSR~>
-!$0_l!I^U>rrL>?c2Rh,WkJE5h6Z_Q!7h($!9VW-!65"j!;,sa"0dE1O3[b-Tn@ugo`##OK7gQ"
-rrFn@g]%9Grdt3jp&>$Krdt4,o)A_JkOAKOfD^C&j7WEP_>]&eqXFLccb08W!$.[2"-%qcZ1\+s
-oDX@BaQNSR~>
-!$0_l!I^U>rrL>?c2Rh,WkJE5h6Z_Q!7h($!9VW-!65"j!;,sa"0dE1O3[b-Tn@ugo`##OK7gQ"
-rrFn@g]%9Grdt3jp&>$Krdt4,o)A_JkOAKOfD^C&j7WEP_>]&eqXFLccb08W!$.[2"-%qcZ1\+s
-oDX@BaQNSR~>
-!$0_l!R>og?NFuWJcC<$dJj5&laHfo2<W*]!Si8*?N?dNs6ou<~>
-!$0_l!R>og?NFuWJcC<$dJj5&laHfo2<W*]!Si8*?N?dNs6ou<~>
-!$0_l!R>og?NFuWJcC<$dJj5&laHfo2<W*]!Si8*?N?dNs6ou<~>
-!$0\k!7h(]!6TlmJcF*s!Qk2HrrL:<aSu7trk&7.JcG3=J,~>
-!$0\k!7h(]!6TlmJcF*s!Qk2HrrL:<aSu7trk&7.JcG3=J,~>
-!$0\k!7h(]!6TlmJcF*s!Qk2HrrL:<aSu7trk&7.JcG3=J,~>
-!$..#"#_JQH%H!Hs+13$s7lVE~>
-!$..#"#_JQH%H!Hs+13$s7lVE~>
-!$..#"#_JQH%H!Hs+13$s7lVE~>
-!$1P.",Is-hrXk>C)m`\rrQ[N'D)5,m2m?Wo_8@e73")Lrr`#hZsnUdJcC<$JcGNFJ,~>
-!$1P.",Is-hrXk>C)m`\rrQ[N'D)5,m2m?Wo_8@e73")Lrr`#hZsnUdJcC<$JcGNFJ,~>
-!$1P.",Is-hrXk>C)m`\rrQ[N'D)5,m2m?Wo_8@e73")Lrr`#hZsnUdJcC<$JcGNFJ,~>
-!$1J,!C#B#rr=)9rr=)2rraJBs/L,5rrMX?lMgms@Y+Q1s+13$s7lVE~>
-!$1J,!C#B#rr=)9rr=)2rraJBs/L,5rrMX?lMgms@Y+Q1s+13$s7lVE~>
-!$1J,!C#B#rr=)9rr=)2rraJBs/L,5rrMX?lMgms@Y+Q1s+13$s7lVE~>
-#9Ej*e^C_-[/U(*g&A5V[f$.+52Q#5"kqkTZ*D%BrrC@DYlMW<rr=)9rr=)9rrKOBr;R!Er;X^+
-s8Tq7YlN)JrrMX?r;Qc0rilIPrr2u0rilIRr;Qf0@Y+Q1s+13$s7lVE~>
-#9Ej*e^C_-[/U(*g&A5V[f$.+52Q#5"kqkTZ*D%BrrC@DYlMW<rr=)9rr=)9rrKOBr;R!Er;X^+
-s8Tq7YlN)JrrMX?r;Qc0rilIPrr2u0rilIRr;Qf0@Y+Q1s+13$s7lVE~>
-#9Ej*e^C_-[/U(*g&A5V[f$.+52Q#5"kqkTZ*D%BrrC@DYlMW<rr=)9rr=)9rrKOBr;R!Er;X^+
-s8Tq7YlN)JrrMX?r;Qc0rilIPrr2u0rilIRr;Qf0@Y+Q1s+13$s7lVE~>
-$Q]8F7!rccCo%*_JGs<bDQ*O6!C#B6rs=B]GACu7ZN&$mrbDOU[f-4+*W#d9*W#d:$nqPY!?h=<
-rr@rUCB8b%rr3#h/,fJKY]9YX"F\VrXDe)R!IiJqrrK`@JcC<$JcC<$q#>j~>
-$Q]8F7!rccCo%*_JGs<bDQ*O6!C#B6rs=B]GACu7ZN&$mrbDOU[f-4+*W#d9*W#d:$nqPY!?h=<
-rr@rUCB8b%rr3#h/,fJKY]9YX"F\VrXDe)R!IiJqrrK`@JcC<$JcC<$q#>j~>
-$Q]8F7!rccCo%*_JGs<bDQ*O6!C#B6rs=B]GACu7ZN&$mrbDOU[f-4+*W#d9*W#d:$nqPY!?h=<
-rr@rUCB8b%rr3#h/,fJKY]9YX"F\VrXDe)R!IiJqrrK`@JcC<$JcC<$q#>j~>
-$Q]8F50X',pEop4IfB?JmOnJ<!C#B>rrBt7G70i=Ki$S)s4'[?I@pN=!R+C=rr=)9rr=)9rrJ+N
-rr3,"G7Sk^q>UJiHN*pFnLOS<!CGQ?rrgQfs#K-=rrIq?rVlo1@Y+Q1s+13$s7lVE~>
-$Q]8F50X',pEop4IfB?JmOnJ<!C#B>rrBt7G70i=Ki$S)s4'[?I@pN=!R+C=rr=)9rr=)9rrJ+N
-rr3,"G7Sk^q>UJiHN*pFnLOS<!CGQ?rrgQfs#K-=rrIq?rVlo1@Y+Q1s+13$s7lVE~>
-$Q]8F50X',pEop4IfB?JmOnJ<!C#B>rrBt7G70i=Ki$S)s4'[?I@pN=!R+C=rr=)9rr=)9rrJ+N
-rr3,"G7Sk^q>UJiHN*pFnLOS<!CGQ?rrgQfs#K-=rrIq?rVlo1@Y+Q1s+13$s7lVE~>
-"!.E>FoMCDpEop4/cJoS<%e.L!C#B>rrC[KV$"@0KpVf="P$'CI@pN=!R+C=rr=)9rr=)4rrg<;
-'r/;;rr@KH=ogX0rr3#h/,fJK]Oh(G"JPkq3DocZ!AKc:rrK`@JcC<$JcC<$q#>j~>
-"!.E>FoMCDpEop4/cJoS<%e.L!C#B>rrC[KV$"@0KpVf="P$'CI@pN=!R+C=rr=)9rr=)4rrg<;
-'r/;;rr@KH=ogX0rr3#h/,fJK]Oh(G"JPkq3DocZ!AKc:rrK`@JcC<$JcC<$q#>j~>
-"!.E>FoMCDpEop4/cJoS<%e.L!C#B>rrC[KV$"@0KpVf="P$'CI@pN=!R+C=rr=)9rr=)4rrg<;
-'r/;;rr@KH=ogX0rr3#h/,fJK]Oh(G"JPkq3DocZ!AKc:rrK`@JcC<$JcC<$q#>j~>
-"!.E>FoMCDpEop4?i@e@c2IYC52Q#5!JQm>rrgkCs*^O=rrL>?rVlj<qYpO9oD\h6r;HWrI&?nZ
-!IK.lrrMX?r;Qc>rkS_oVuJcYrP8KqrVlo1@Y+Q1s+13$s7lVE~>
-"!.E>FoMCDpEop4?i@e@c2IYC52Q#5!JQm>rrgkCs*^O=rrL>?rVlj<qYpO9oD\h6r;HWrI&?nZ
-!IK.lrrMX?r;Qc>rkS_oVuJcYrP8KqrVlo1@Y+Q1s+13$s7lVE~>
-"!.E>FoMCDpEop4?i@e@c2IYC52Q#5!JQm>rrgkCs*^O=rrL>?rVlj<qYpO9oD\h6r;HWrI&?nZ
-!IK.lrrMX?r;Qc>rkS_oVuJcYrP8KqrVlo1@Y+Q1s+13$s7lVE~>
-"!.E>FoMCDpEop4Ie`pD52Q#5$&'&,s8UV?s*^O=rrL>?rVlj<qYpO9qYpRH9)S\i+T23<##i\E
-s2Pk#rr3#h/,fJK>2T>Z"HeWB3TKo7!P;fls+13$s+14Fs*t~>
-"!.E>FoMCDpEop4Ie`pD52Q#5$&'&,s8UV?s*^O=rrL>?rVlj<qYpO9qYpRH9)S\i+T23<##i\E
-s2Pk#rr3#h/,fJK>2T>Z"HeWB3TKo7!P;fls+13$s+14Fs*t~>
-"!.E>FoMCDpEop4Ie`pD52Q#5$&'&,s8UV?s*^O=rrL>?rVlj<qYpO9qYpRH9)S\i+T23<##i\E
-s2Pk#rr3#h/,fJK>2T>Z"HeWB3TKo7!P;fls+13$s+14Fs*t~>
-"!.EGKDtlRpH/ESNrC%!/H5YPL`IBR1\^nUKp>sb*CB](rG_`V#YFsos(WPm*Duh9"CiGj*Ei=?
-!@1&1rrG%UrVmK-9-#$QWJCNR73*9eGQ7^@4oQH)IJs3D2?"TrLAq2TkiSgQJcC<$JcC<$q#>j~>
-"!.EGKDtlRpH/ESNrC%!/H5YPL`IBR1\^nUKp>sb*CB](rG_`V#YFsos(WPm*Duh9"CiGj*Ei=?
-!@1&1rrG%UrVmK-9-#$QWJCNR73*9eGQ7^@4oQH)IJs3D2?"TrLAq2TkiSgQJcC<$JcC<$q#>j~>
-"!.EGKDtlRpH/ESNrC%!/H5YPL`IBR1\^nUKp>sb*CB](rG_`V#YFsos(WPm*Duh9"CiGj*Ei=?
-!@1&1rrG%UrVmK-9-#$QWJCNR73*9eGQ7^@4oQH)IJs3D2?"TrLAq2TkiSgQJcC<$JcC<$q#>j~>
-!$1"t!JQlGrrY\J2M6S\JcC<$JcGNFJ,~>
-!$1"t!JQlGrrY\J2M6S\JcC<$JcGNFJ,~>
-!$1"t!JQlGrrY\J2M6S\JcC<$JcGNFJ,~>
-!$1"t!JQkks+13$s+13Hs*t~>
-!$1"t!JQkks+13$s+13Hs*t~>
-!$1"t!JQkks+13$s+13Hs*t~>
-!$1"t!P?=%s+13$s+13Hs*t~>
-!$1"t!P?=%s+13$s+13Hs*t~>
-!$1"t!P?=%s+13$s+13Hs*t~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$.@)!V"X)rrM*>JcC<$JcFU,J,~>
-!$.@)!V"X)rrM*>JcC<$JcFU,J,~>
-!$.@)!V"X)rrM*>JcC<$JcFU,J,~>
-!Zh<ur1X1qq#:BJe+3M@aJ,F)rVlu=U8%SZrrL7$rr3)_`W*sUU&`:0rrBk4U&`O-rrVhUQM1=Z
-Z2;uL!p66)pAY0d\+'Cuce\R"!9*mN!;6?k!jdU@JcC<$JcFX-J,~>
-!Zh<ur1X1qq#:BJe+3M@aJ,F)rVlu=U8%SZrrL7$rr3)_`W*sUU&`:0rrBk4U&`O-rrVhUQM1=Z
-Z2;uL!p66)pAY0d\+'Cuce\R"!9*mN!;6?k!jdU@JcC<$JcFX-J,~>
-!Zh<ur1X1qq#:BJe+3M@aJ,F)rVlu=U8%SZrrL7$rr3)_`W*sUU&`:0rrBk4U&`O-rrVhUQM1=Z
-Z2;uL!p66)pAY0d\+'Cuce\R"!9*mN!;6?k!jdU@JcC<$JcFX-J,~>
-"!.E>3;n.'NqiVTQA4u1"0d(en,<7gXEkNRpAY/0Yl=Y*]4(_L=T*OGZ2">!V>Y]@9mZ7-!T6*5
-rrFP?lMgnMW9aHbgNpR2rrLi`rVloY3V!+Sc@:E's8V55rH\HtrVln5Z@W%,s+14-s*t~>
-"!.E>3;n.'NqiVTQA4u1"0d(en,<7gXEkNRpAY/0Yl=Y*]4(_L=T*OGZ2">!V>Y]@9mZ7-!T6*5
-rrFP?lMgnMW9aHbgNpR2rrLi`rVloY3V!+Sc@:E's8V55rH\HtrVln5Z@W%,s+14-s*t~>
-"!.E>3;n.'NqiVTQA4u1"0d(en,<7gXEkNRpAY/0Yl=Y*]4(_L=T*OGZ2">!V>Y]@9mZ7-!T6*5
-rrFP?lMgnMW9aHbgNpR2rrLi`rVloY3V!+Sc@:E's8V55rH\HtrVln5Z@W%,s+14-s*t~>
-"!.E>Fn>V6QA4o/!A3d;rrFG?pAY/0Yl=Y*]4(_L\aTP"fm1^.rrW1;YP%nr..lg)!RjX#rrKB?
-rVloY3V*1UdsK?QFR&nK;Z6UqU](2o_KOsjs+13$s5<p-~>
-"!.E>Fn>V6QA4o/!A3d;rrFG?pAY/0Yl=Y*]4(_L\aTP"fm1^.rrW1;YP%nr..lg)!RjX#rrKB?
-rVloY3V*1UdsK?QFR&nK;Z6UqU](2o_KOsjs+13$s5<p-~>
-"!.E>Fn>V6QA4o/!A3d;rrFG?pAY/0Yl=Y*]4(_L\aTP"fm1^.rrW1;YP%nr..lg)!RjX#rrKB?
-rVloY3V*1UdsK?QFR&nK;Z6UqU](2o_KOsjs+13$s5<p-~>
-"!.E>Fo)+<VuB<p=9&;dL5\bu?2jj(S,Q%\A,Q?-/arT:!@@L6rsdIoh#HGJd/RUdBM2!LoD\j+
-?1.^nrZD%;!*T:o"Ju.u.-LS&!3?,!!)<Gc&@)98@/nYJ9=Os$<^%9l9>1-#!WHO+rs")2s5QaF
-V>gK%nk1e^92!h8j#P!U9*!]@rs@Uudf9?f>5QH?p\t7gd"24Js+14.s*t~>
-"!.E>Fo)+<VuB<p=9&;dL5\bu?2jj(S,Q%\A,Q?-/arT:!@@L6rsdIoh#HGJd/RUdBM2!LoD\j+
-?1.^nrZD%;!*T:o"Ju.u.-LS&!3?,!!)<Gc&@)98@/nYJ9=Os$<^%9l9>1-#!WHO+rs")2s5QaF
-V>gK%nk1e^92!h8j#P!U9*!]@rs@Uudf9?f>5QH?p\t7gd"24Js+14.s*t~>
-"!.E>Fo)+<VuB<p=9&;dL5\bu?2jj(S,Q%\A,Q?-/arT:!@@L6rsdIoh#HGJd/RUdBM2!LoD\j+
-?1.^nrZD%;!*T:o"Ju.u.-LS&!3?,!!)<Gc&@)98@/nYJ9=Os$<^%9l9>1-#!WHO+rs")2s5QaF
-V>gK%nk1e^92!h8j#P!U9*!]@rs@Uudf9?f>5QH?p\t7gd"24Js+14.s*t~>
-"!.E>(&ffgmf3<kIf@_'``2u((m`Rs^g$i5M>km']41a=!A3d;rrFG?p&>IscMuQcs8QRQ\'`Tr
-L%YHIj&b4-rrN*@qu6ZGrm:k!Y5]n0rm:jp[JreDrm;:"]`6A3=4,E7_uJ2f4OMREq#:Bn+8u3D
-:!`k7d=?cHrsJ\OO]TrXo`*qYAcC'X:[RuX!VbILrr^mPb#8!6!DUpls+13$s5<p-~>
-"!.E>(&ffgmf3<kIf@_'``2u((m`Rs^g$i5M>km']41a=!A3d;rrFG?p&>IscMuQcs8QRQ\'`Tr
-L%YHIj&b4-rrN*@qu6ZGrm:k!Y5]n0rm:jp[JreDrm;:"]`6A3=4,E7_uJ2f4OMREq#:Bn+8u3D
-:!`k7d=?cHrsJ\OO]TrXo`*qYAcC'X:[RuX!VbILrr^mPb#8!6!DUpls+13$s5<p-~>
-"!.E>(&ffgmf3<kIf@_'``2u((m`Rs^g$i5M>km']41a=!A3d;rrFG?p&>IscMuQcs8QRQ\'`Tr
-L%YHIj&b4-rrN*@qu6ZGrm:k!Y5]n0rm:jp[JreDrm;:"]`6A3=4,E7_uJ2f4OMREq#:Bn+8u3D
-:!`k7d=?cHrsJ\OO]TrXo`*qYAcC'X:[RuX!VbILrr^mPb#8!6!DUpls+13$s5<p-~>
-"!.E>D>ro*rr<"nIfAsJoC;jHIJNpCju<=#M#R#Idm*g2!A3d;rrFG?p&>J%a8XI[s8V`Yr;Zf'
-C%_K,e4B!,!WF2<rrD`koE9K1s!Zt-rrdSCruh:>rs`nKs7mi/s8QrHs8UP>p&>9q+9/Bms%Ui=
-rrJ[@r;Qf&C]487j#-H-rrFt?nc&g9;ZHc1*>SMP!DUpls+13$s5<p-~>
-"!.E>D>ro*rr<"nIfAsJoC;jHIJNpCju<=#M#R#Idm*g2!A3d;rrFG?p&>J%a8XI[s8V`Yr;Zf'
-C%_K,e4B!,!WF2<rrD`koE9K1s!Zt-rrdSCruh:>rs`nKs7mi/s8QrHs8UP>p&>9q+9/Bms%Ui=
-rrJ[@r;Qf&C]487j#-H-rrFt?nc&g9;ZHc1*>SMP!DUpls+13$s5<p-~>
-"!.E>D>ro*rr<"nIfAsJoC;jHIJNpCju<=#M#R#Idm*g2!A3d;rrFG?p&>J%a8XI[s8V`Yr;Zf'
-C%_K,e4B!,!WF2<rrD`koE9K1s!Zt-rrdSCruh:>rs`nKs7mi/s8QrHs8UP>p&>9q+9/Bms%Ui=
-rrJ[@r;Qf&C]487j#-H-rrFt?nc&g9;ZHc1*>SMP!DUpls+13$s5<p-~>
-"!.E>Fo)+<Q2W071uA7uLAq2Uju<=#(B#W]?2ad(/arT:!@@L4rrOq7-MdZBZYB.5!W"&-rrN*@
-r;QfS2?#!,'V,1Oo`"jnGbtE_rVlg"Dls'8,PqE@dn064#Q5bEV0Dr6ci3qFSUgY<!O6G=rrM.?
-rVlmTkjeZRb#83<!$2";!DUpls+13$s5<p-~>
-"!.E>Fo)+<Q2W071uA7uLAq2Uju<=#(B#W]?2ad(/arT:!@@L4rrOq7-MdZBZYB.5!W"&-rrN*@
-r;QfS2?#!,'V,1Oo`"jnGbtE_rVlg"Dls'8,PqE@dn064#Q5bEV0Dr6ci3qFSUgY<!O6G=rrM.?
-rVlmTkjeZRb#83<!$2";!DUpls+13$s5<p-~>
-"!.E>Fo)+<Q2W071uA7uLAq2Uju<=#(B#W]?2ad(/arT:!@@L4rrOq7-MdZBZYB.5!W"&-rrN*@
-r;QfS2?#!,'V,1Oo`"jnGbtE_rVlg"Dls'8,PqE@dn064#Q5bEV0Dr6ci3qFSUgY<!O6G=rrM.?
-rVlmTkjeZRb#83<!$2";!DUpls+13$s5<p-~>
-"!.E>Fo21>jkTk8"R[oBQA5D="QhZCNfNo7!A3d;rrFG?o`"tIi[4[)!S-Q9rrKH?rVlo1ao)/>
-^0^1+!rc-@rVm0Ym/R+I?(CpC\,QC1GbtE_rVlg"Dls'8,PqEDdn0T>e*Zu2#Q5b5OaQ._ci3qF
-SUgY<#I/(Es2t)r3W8sY2!FK0!Qn==rr=)<rrUech1>TWs+14.s*t~>
-"!.E>Fo21>jkTk8"R[oBQA5D="QhZCNfNo7!A3d;rrFG?o`"tIi[4[)!S-Q9rrKH?rVlo1ao)/>
-^0^1+!rc-@rVm0Ym/R+I?(CpC\,QC1GbtE_rVlg"Dls'8,PqEDdn0T>e*Zu2#Q5b5OaQ._ci3qF
-SUgY<#I/(Es2t)r3W8sY2!FK0!Qn==rr=)<rrUech1>TWs+14.s*t~>
-"!.E>Fo21>jkTk8"R[oBQA5D="QhZCNfNo7!A3d;rrFG?o`"tIi[4[)!S-Q9rrKH?rVlo1ao)/>
-^0^1+!rc-@rVm0Ym/R+I?(CpC\,QC1GbtE_rVlg"Dls'8,PqEDdn0T>e*Zu2#Q5b5OaQ._ci3qF
-SUgY<#I/(Es2t)r3W8sY2!FK0!Qn==rr=)<rrUech1>TWs+14.s*t~>
-"!.E>8H#(]XT-4grga1[IfG^grr3,`2ugF@rga%hrr3,?SK*iqrr3,<SJRZup&>)DQ?rQ1!M>AN
-SH4YDrVlm#2uN[U*U<Y*i&pu<$%SG7SVAkhs!V@USHOD^s)sq3SHO>bs7mo9rrqJ)SXk#Wq>Us(
-Boe[jKQQ2Qn=<r_S`TkN#L@afST*rU3W8sY2!FK0!T%ttSH*F'rrHl?JcC<$JcFX-J,~>
-"!.E>8H#(]XT-4grga1[IfG^grr3,`2ugF@rga%hrr3,?SK*iqrr3,<SJRZup&>)DQ?rQ1!M>AN
-SH4YDrVlm#2uN[U*U<Y*i&pu<$%SG7SVAkhs!V@USHOD^s)sq3SHO>bs7mo9rrqJ)SXk#Wq>Us(
-Boe[jKQQ2Qn=<r_S`TkN#L@afST*rU3W8sY2!FK0!T%ttSH*F'rrHl?JcC<$JcFX-J,~>
-"!.E>8H#(]XT-4grga1[IfG^grr3,`2ugF@rga%hrr3,?SK*iqrr3,<SJRZup&>)DQ?rQ1!M>AN
-SH4YDrVlm#2uN[U*U<Y*i&pu<$%SG7SVAkhs!V@USHOD^s)sq3SHO>bs7mo9rrqJ)SXk#Wq>Us(
-Boe[jKQQ2Qn=<r_S`TkN#L@afST*rU3W8sY2!FK0!T%ttSH*F'rrHl?JcC<$JcFX-J,~>
-!Zh<ir-ng3s8Tn6IftQ,s2r4Xrrhn]s8TS-IfPT0rrBD)IfP`4rrB8%IfPl.rrA)WrrAemIfQ>C
-rrIY>r;QbWlMgqTJ$&\L#`4%\YeSH_POSR$!5ng9!.b&u"NUQBr/pgT"586Sd.dPGn"'LY[+PEY
-lhu;5h#76Waa\g!s7)TWrrJP[nG`L>rI4h<rr3&;J(]DQJcC<$huA3~>
-!Zh<ir-ng3s8Tn6IftQ,s2r4Xrrhn]s8TS-IfPT0rrBD)IfP`4rrB8%IfPl.rrA)WrrAemIfQ>C
-rrIY>r;QbWlMgqTJ$&\L#`4%\YeSH_POSR$!5ng9!.b&u"NUQBr/pgT"586Sd.dPGn"'LY[+PEY
-lhu;5h#76Waa\g!s7)TWrrJP[nG`L>rI4h<rr3&;J(]DQJcC<$huA3~>
-!Zh<ir-ng3s8Tn6IftQ,s2r4Xrrhn]s8TS-IfPT0rrBD)IfP`4rrB8%IfPl.rrA)WrrAemIfQ>C
-rrIY>r;QbWlMgqTJ$&\L#`4%\YeSH_POSR$!5ng9!.b&u"NUQBr/pgT"586Sd.dPGn"'LY[+PEY
-lhu;5h#76Waa\g!s7)TWrrJP[nG`L>rI4h<rr3&;J(]DQJcC<$huA3~>
-!$.@)!U.7_rrLKtJcC<$JcFU,J,~>
-!$.@)!U.7_rrLKtJcC<$JcFU,J,~>
-!$.@)!U.7_rrLKtJcC<$JcFU,J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$2%<!VH<grrMlioD\pdn*g5QrrVf\rndYUo_[nQ"9.cXpAP!ln+6&K!VZ?grrW(rKA$8/oDJXg
-qX=1=rrMiio`#!en,'Hk!THTHrrG4Kqu6[UnU^^ks2P(h~>
-!$2%<!VH<grrMlioD\pdn*g5QrrVf\rndYUo_[nQ"9.cXpAP!ln+6&K!VZ?grrW(rKA$8/oDJXg
-qX=1=rrMiio`#!en,'Hk!THTHrrG4Kqu6[UnU^^ks2P(h~>
-!$2%<!VH<grrMlioD\pdn*g5QrrVf\rndYUo_[nQ"9.cXpAP!ln+6&K!VZ?grrW(rKA$8/oDJXg
-qX=1=rrMiio`#!en,'Hk!THTHrrG4Kqu6[UnU^^ks2P(h~>
-!$2(=!p)_mrVlqdOn//E",\Z;k5>5\CG#,LrrLpOmJd2k_<Lt+`@WZ`lMpn`/RS#I!I1I?rrL/?
-f`).EfDklc/Y1u&!pa0mo`"uX/]ZZH!TE_;rrLpOo)A\1qYpO9qu6]o,(]cFs2P(h~>
-!$2(=!p)_mrVlqdOn//E",\Z;k5>5\CG#,LrrLpOmJd2k_<Lt+`@WZ`lMpn`/RS#I!I1I?rrL/?
-f`).EfDklc/Y1u&!pa0mo`"uX/]ZZH!TE_;rrLpOo)A\1qYpO9qu6]o,(]cFs2P(h~>
-!$2(=!p)_mrVlqdOn//E",\Z;k5>5\CG#,LrrLpOmJd2k_<Lt+`@WZ`lMpn`/RS#I!I1I?rrL/?
-f`).EfDklc/Y1u&!pa0mo`"uX/]ZZH!TE_;rrLpOo)A\1qYpO9qu6]o,(]cFs2P(h~>
-"s*aDh09a\r;Qh\Qh0hK!B0*,rrG1?mJd2k_<Lt&*WQ/%MsLBIGbtH?pRR)lrrGR?i;WoBL*<S@
-rrH*?li-uIiUd'M_-?d9!Ed>=rrW+#a+=8As2Y.i~>
-"s*aDh09a\r;Qh\Qh0hK!B0*,rrG1?mJd2k_<Lt&*WQ/%MsLBIGbtH?pRR)lrrGR?i;WoBL*<S@
-rrH*?li-uIiUd'M_-?d9!Ed>=rrW+#a+=8As2Y.i~>
-"s*aDh09a\r;Qh\Qh0hK!B0*,rrG1?mJd2k_<Lt&*WQ/%MsLBIGbtH?pRR)lrrGR?i;WoBL*<S@
-rrH*?li-uIiUd'M_-?d9!Ed>=rrW+#a+=8As2Y.i~>
-"s*a!J\Fq)qu6_[OS\VL!B0*=rr^-*R/$X[!5JN##d+.,a-\'+R'?Si!4`#q!42V'!M-:jrrH??
-rr3,W`rH(Arg3u*s8TOps8V)prVm0Es8TdDs8Sj]Yl4S&XRlFY!6bBA$(RBM_g&$Xs5Z0;rrE#r
-Qiu"?s.o&]Qil%As7ZDY"5NqVo)4pXi;`iGrg3rB7G%S*R$c5!rrVhrh#<ZClMpnGrg3cHWqlJj
-"R:fBEiSg4"4rp-jSf)YeGYd4rrT]tpAY'lc2O(5o;_ijao7Y5UAt8AQnilQV>gJp_Z#o6WW3"A
-fX$s4XT/=@Qm7?QYl=Y&\GhiqZi0n).e<H9!Tl<;rrLq?JcC<$a8^Y~>
-"s*a!J\Fq)qu6_[OS\VL!B0*=rr^-*R/$X[!5JN##d+.,a-\'+R'?Si!4`#q!42V'!M-:jrrH??
-rr3,W`rH(Arg3u*s8TOps8V)prVm0Es8TdDs8Sj]Yl4S&XRlFY!6bBA$(RBM_g&$Xs5Z0;rrE#r
-Qiu"?s.o&]Qil%As7ZDY"5NqVo)4pXi;`iGrg3rB7G%S*R$c5!rrVhrh#<ZClMpnGrg3cHWqlJj
-"R:fBEiSg4"4rp-jSf)YeGYd4rrT]tpAY'lc2O(5o;_ijao7Y5UAt8AQnilQV>gJp_Z#o6WW3"A
-fX$s4XT/=@Qm7?QYl=Y&\GhiqZi0n).e<H9!Tl<;rrLq?JcC<$a8^Y~>
-"s*a!J\Fq)qu6_[OS\VL!B0*=rr^-*R/$X[!5JN##d+.,a-\'+R'?Si!4`#q!42V'!M-:jrrH??
-rr3,W`rH(Arg3u*s8TOps8V)prVm0Es8TdDs8Sj]Yl4S&XRlFY!6bBA$(RBM_g&$Xs5Z0;rrE#r
-Qiu"?s.o&]Qil%As7ZDY"5NqVo)4pXi;`iGrg3rB7G%S*R$c5!rrVhrh#<ZClMpnGrg3cHWqlJj
-"R:fBEiSg4"4rp-jSf)YeGYd4rrT]tpAY'lc2O(5o;_ijao7Y5UAt8AQnilQV>gJp_Z#o6WW3"A
-fX$s4XT/=@Qm7?QYl=Y&\GhiqZi0n).e<H9!Tl<;rrLq?JcC<$a8^Y~>
-"s*`o]`1dPqYpS`K_59F2Wju<"1Ek"n,E=f]Rg'8%"b>U]S%>KKVAGGZ[r+/!J7HPrrEN]rr3:/
-_>j(QI_5WWK)UE/J\(kRdf9?VB)V`0*W,j<O`W_arrFipre(B(bl<e(KEcrfV>pSV3;idVL])l/
-J(ai;IK"m%Ibk$RrIb9%ir4?(KEH\cqL&9q#C[iJs6,,1M#RDUiu<IHKEHYom!\kd!e,arq>UTZ
-XT*=@p&>-?KJL1=rrL`$rIb0.rVm'u!9sO`c@>hH"FQ[<aajAD%"joHab23XKTuN:_1;N<'S<"X
-IS>?HKS$$"]n@GLKV8AFZ[r+/!J7HQrrFV?qYpTY2Z*LTh*6JjJcEdjJ,~>
-"s*`o]`1dPqYpS`K_59F2Wju<"1Ek"n,E=f]Rg'8%"b>U]S%>KKVAGGZ[r+/!J7HPrrEN]rr3:/
-_>j(QI_5WWK)UE/J\(kRdf9?VB)V`0*W,j<O`W_arrFipre(B(bl<e(KEcrfV>pSV3;idVL])l/
-J(ai;IK"m%Ibk$RrIb9%ir4?(KEH\cqL&9q#C[iJs6,,1M#RDUiu<IHKEHYom!\kd!e,arq>UTZ
-XT*=@p&>-?KJL1=rrL`$rIb0.rVm'u!9sO`c@>hH"FQ[<aajAD%"joHab23XKTuN:_1;N<'S<"X
-IS>?HKS$$"]n@GLKV8AFZ[r+/!J7HQrrFV?qYpTY2Z*LTh*6JjJcEdjJ,~>
-"s*`o]`1dPqYpS`K_59F2Wju<"1Ek"n,E=f]Rg'8%"b>U]S%>KKVAGGZ[r+/!J7HPrrEN]rr3:/
-_>j(QI_5WWK)UE/J\(kRdf9?VB)V`0*W,j<O`W_arrFipre(B(bl<e(KEcrfV>pSV3;idVL])l/
-J(ai;IK"m%Ibk$RrIb9%ir4?(KEH\cqL&9q#C[iJs6,,1M#RDUiu<IHKEHYom!\kd!e,arq>UTZ
-XT*=@p&>-?KJL1=rrL`$rIb0.rVm'u!9sO`c@>hH"FQ[<aajAD%"joHab23XKTuN:_1;N<'S<"X
-IS>?HKS$$"]n@GLKV8AFZ[r+/!J7HQrrFV?qYpTY2Z*LTh*6JjJcEdjJ,~>
-!$2%<!Dgu9rr=)3rrG%?qu6[Kn,E=fI@pN=!URQ"rrG1?rVln7Yl=Y'k/I<!!N!+$rs;oGF(M&\
-s8Q$?rr3M'HN-Ucs8TQ?s8Rm]$XStnrrM%?rVlmQR/[*pJ=QWfo`+sD9;V[gj>d);!$2%<"D>1C
-(pX)?"(&_A*W?!?_!:k?rVlsoch&XbrrU.ifDbdNP_f>=!okQBrr3'WkhAE&rrG^@p&>&5Wq65k
-@CuO=!O6J>rr=)<rs$32p](8dIK'6L*WQ/+L&V)Qk;N>>!E@/=rrIk?rr3;U8,n$Kk5YJ+:]C@p
-45p/<!H#%>rrM9#rr3!Ko(r@eju3,:!Sotks+13js*t~>
-!$2%<!Dgu9rr=)3rrG%?qu6[Kn,E=fI@pN=!URQ"rrG1?rVln7Yl=Y'k/I<!!N!+$rs;oGF(M&\
-s8Q$?rr3M'HN-Ucs8TQ?s8Rm]$XStnrrM%?rVlmQR/[*pJ=QWfo`+sD9;V[gj>d);!$2%<"D>1C
-(pX)?"(&_A*W?!?_!:k?rVlsoch&XbrrU.ifDbdNP_f>=!okQBrr3'WkhAE&rrG^@p&>&5Wq65k
-@CuO=!O6J>rr=)<rs$32p](8dIK'6L*WQ/+L&V)Qk;N>>!E@/=rrIk?rr3;U8,n$Kk5YJ+:]C@p
-45p/<!H#%>rrM9#rr3!Ko(r@eju3,:!Sotks+13js*t~>
-!$2%<!Dgu9rr=)3rrG%?qu6[Kn,E=fI@pN=!URQ"rrG1?rVln7Yl=Y'k/I<!!N!+$rs;oGF(M&\
-s8Q$?rr3M'HN-Ucs8TQ?s8Rm]$XStnrrM%?rVlmQR/[*pJ=QWfo`+sD9;V[gj>d);!$2%<"D>1C
-(pX)?"(&_A*W?!?_!:k?rVlsoch&XbrrU.ifDbdNP_f>=!okQBrr3'WkhAE&rrG^@p&>&5Wq65k
-@CuO=!O6J>rr=)<rs$32p](8dIK'6L*WQ/+L&V)Qk;N>>!E@/=rrIk?rr3;U8,n$Kk5YJ+:]C@p
-45p/<!H#%>rrM9#rr3!Ko(r@eju3,:!Sotks+13js*t~>
-!$2%<!Dgu:rrHN?o`"qMk55/Z/ar]=!T%ep5lbQrrrG1?rVloOB`:9tFSGe;>;EA"rVlms2#]cO
-.=2"ddf9?VB)hnV^)"H2r;QfU4T59\1$no>"G!$B..mN="aHmDs5Z0;rr<r85m&%6ruM(<"(&_A
-*W,j;r?)"<"&7,6./j/H7G%P=!L/E>rrM.?rVlsVkhAE&rrG^@p&>&5Wq65k@CuO=!O6J>rr=)7
-rrJd@rr3*As8SE0r]pQH2ZE^W<65%<!JZp>rrgnCs*gR=rrLA?rr3!\iVicWg1^IO!-A,=!@m[:
-rrM7?qu6]Q5Crics2Y.i~>
-!$2%<!Dgu:rrHN?o`"qMk55/Z/ar]=!T%ep5lbQrrrG1?rVloOB`:9tFSGe;>;EA"rVlms2#]cO
-.=2"ddf9?VB)hnV^)"H2r;QfU4T59\1$no>"G!$B..mN="aHmDs5Z0;rr<r85m&%6ruM(<"(&_A
-*W,j;r?)"<"&7,6./j/H7G%P=!L/E>rrM.?rVlsVkhAE&rrG^@p&>&5Wq65k@CuO=!O6J>rr=)7
-rrJd@rr3*As8SE0r]pQH2ZE^W<65%<!JZp>rrgnCs*gR=rrLA?rr3!\iVicWg1^IO!-A,=!@m[:
-rrM7?qu6]Q5Crics2Y.i~>
-!$2%<!Dgu:rrHN?o`"qMk55/Z/ar]=!T%ep5lbQrrrG1?rVloOB`:9tFSGe;>;EA"rVlms2#]cO
-.=2"ddf9?VB)hnV^)"H2r;QfU4T59\1$no>"G!$B..mN="aHmDs5Z0;rr<r85m&%6ruM(<"(&_A
-*W,j;r?)"<"&7,6./j/H7G%P=!L/E>rrM.?rVlsVkhAE&rrG^@p&>&5Wq65k@CuO=!O6J>rr=)7
-rrJd@rr3*As8SE0r]pQH2ZE^W<65%<!JZp>rrgnCs*gR=rrLA?rr3!\iVicWg1^IO!-A,=!@m[:
-rrM7?qu6]Q5Crics2Y.i~>
-!$2%<!Dgu;rrQWBrUg*j2Wjo:!A3d=rrD9^gB"`srr3![ir&fVk5O*9Wd+=="'_Wb48o0[;m$#Q
-#OMI_df9?VB)V`0*W#d:i&pu<!AWs?rrdkBs![O=rrmYDs8V.>r;Qa:r7_;GruM(<"(&_A*W,j;
-r?)"<"&7,6./j/H7G%P=!L/E>rrM.?rVlsVkk_C[,QXh=p&>&5Wq65k@CuO=!O6J>rr=)7rrMYA
-rZhWqs8SF+rS%>3rr3!uaSl,>L6hi="P-*CI\-Q=!R4F>rrG4?r;[email protected]<H9!Tl<;
-rrLq?JcC<$a8^Y~>
-!$2%<!Dgu;rrQWBrUg*j2Wjo:!A3d=rrD9^gB"`srr3![ir&fVk5O*9Wd+=="'_Wb48o0[;m$#Q
-#OMI_df9?VB)V`0*W#d:i&pu<!AWs?rrdkBs![O=rrmYDs8V.>r;Qa:r7_;GruM(<"(&_A*W,j;
-r?)"<"&7,6./j/H7G%P=!L/E>rrM.?rVlsVkk_C[,QXh=p&>&5Wq65k@CuO=!O6J>rr=)7rrMYA
-rZhWqs8SF+rS%>3rr3!uaSl,>L6hi="P-*CI\-Q=!R4F>rrG4?r;[email protected]<H9!Tl<;
-rrLq?JcC<$a8^Y~>
-!$2%<!Dgu;rrQWBrUg*j2Wjo:!A3d=rrD9^gB"`srr3![ir&fVk5O*9Wd+=="'_Wb48o0[;m$#Q
-#OMI_df9?VB)V`0*W#d:i&pu<!AWs?rrdkBs![O=rrmYDs8V.>r;Qa:r7_;GruM(<"(&_A*W,j;
-r?)"<"&7,6./j/H7G%P=!L/E>rrM.?rVlsVkk_C[,QXh=p&>&5Wq65k@CuO=!O6J>rr=)7rrMYA
-rZhWqs8SF+rS%>3rr3!uaSl,>L6hi="P-*CI\-Q=!R4F>rrG4?r;[email protected]<H9!Tl<;
-rrLq?JcC<$a8^Y~>
-%NYTElKUsRlL"WOoPM5>p&>0dl7pcSrr38pl71QUs8S%KrosO!?iL'19%*"9C]Ag_rosNhDZ0S9
-'/fdh$!Y7@i>l@us'`V?l3Qq[s&m;*`t`4brr=):rs7u:2sU&8s"Wm>rsjRLs#T-(lBDdTs8W)9
->5eI$0`C8=pAb.5rVlt+^Afhal2tM?r@e-7"'3nD2u`(NC^AYmqs&uclMCP@3W8s[2!Egmro*tA
-qu-O&IgEmjs82*HBq4JRrVlq:#grYO#l!UeX6T]a[r;61!&4BO#$)#ps8T-Jro+(Hs8SaFrosLM
-rVm+4]^!gBs,`ECl3YL3s*bTflKZ/krr33rdH\>Ys*h!Il2e2-rr3#p,l7NA,Ph9;!rFA@r;Qii
-#YfmMJcF'rJ,~>
-%NYTElKUsRlL"WOoPM5>p&>0dl7pcSrr38pl71QUs8S%KrosO!?iL'19%*"9C]Ag_rosNhDZ0S9
-'/fdh$!Y7@i>l@us'`V?l3Qq[s&m;*`t`4brr=):rs7u:2sU&8s"Wm>rsjRLs#T-(lBDdTs8W)9
->5eI$0`C8=pAb.5rVlt+^Afhal2tM?r@e-7"'3nD2u`(NC^AYmqs&uclMCP@3W8s[2!Egmro*tA
-qu-O&IgEmjs82*HBq4JRrVlq:#grYO#l!UeX6T]a[r;61!&4BO#$)#ps8T-Jro+(Hs8SaFrosLM
-rVm+4]^!gBs,`ECl3YL3s*bTflKZ/krr33rdH\>Ys*h!Il2e2-rr3#p,l7NA,Ph9;!rFA@r;Qii
-#YfmMJcF'rJ,~>
-%NYTElKUsRlL"WOoPM5>p&>0dl7pcSrr38pl71QUs8S%KrosO!?iL'19%*"9C]Ag_rosNhDZ0S9
-'/fdh$!Y7@i>l@us'`V?l3Qq[s&m;*`t`4brr=):rs7u:2sU&8s"Wm>rsjRLs#T-(lBDdTs8W)9
->5eI$0`C8=pAb.5rVlt+^Afhal2tM?r@e-7"'3nD2u`(NC^AYmqs&uclMCP@3W8s[2!Egmro*tA
-qu-O&IgEmjs82*HBq4JRrVlq:#grYO#l!UeX6T]a[r;61!&4BO#$)#ps8T-Jro+(Hs8SaFrosLM
-rVm+4]^!gBs,`ECl3YL3s*bTflKZ/krr33rdH\>Ys*h!Il2e2-rr3#p,l7NA,Ph9;!rFA@r;Qii
-#YfmMJcF'rJ,~>
-!Zh<Nr%\CPs8SOkoD\fMr\=IJrr2tQr\=IQrr2tJr\=IXr;Qgi11L7_!-.un!,;B2!BaK`rrIJk
-rr3,5NrT,br\=J%rr3+%10(eqrVljiqu6Xfr\=V=s8Q6jrr3:mYlF_b1,=WJ\,QC/dUqP=rrDoo
-1'=ZYs%<7i"+JGnmJSdB])VfmrA"Jks8V!U1'FgqhuD@-1'>l&l!XJi"'k6gV>W.MZMa_%"dK_8
-s8UXI1&s31rrRrdd/O%FYPg3Yp](8mrA"BPrVm(C1@>,Am",-k"?b98s-3L<!(Qnd!a[WVrr2tR
-r\=aXs8RP>BJM>Hr;Qgj11C1^!-8&o!,2<1!CkZ<rrLS?qu6]C:&FqloeL<6s+13rs*t~>
-!Zh<Nr%\CPs8SOkoD\fMr\=IJrr2tQr\=IQrr2tJr\=IXr;Qgi11L7_!-.un!,;B2!BaK`rrIJk
-rr3,5NrT,br\=J%rr3+%10(eqrVljiqu6Xfr\=V=s8Q6jrr3:mYlF_b1,=WJ\,QC/dUqP=rrDoo
-1'=ZYs%<7i"+JGnmJSdB])VfmrA"Jks8V!U1'FgqhuD@-1'>l&l!XJi"'k6gV>W.MZMa_%"dK_8
-s8UXI1&s31rrRrdd/O%FYPg3Yp](8mrA"BPrVm(C1@>,Am",-k"?b98s-3L<!(Qnd!a[WVrr2tR
-r\=aXs8RP>BJM>Hr;Qgj11C1^!-8&o!,2<1!CkZ<rrLS?qu6]C:&FqloeL<6s+13rs*t~>
-!Zh<Nr%\CPs8SOkoD\fMr\=IJrr2tQr\=IQrr2tJr\=IXr;Qgi11L7_!-.un!,;B2!BaK`rrIJk
-rr3,5NrT,br\=J%rr3+%10(eqrVljiqu6Xfr\=V=s8Q6jrr3:mYlF_b1,=WJ\,QC/dUqP=rrDoo
-1'=ZYs%<7i"+JGnmJSdB])VfmrA"Jks8V!U1'FgqhuD@-1'>l&l!XJi"'k6gV>W.MZMa_%"dK_8
-s8UXI1&s31rrRrdd/O%FYPg3Yp](8mrA"BPrVm(C1@>,Am",-k"?b98s-3L<!(Qnd!a[WVrr2tR
-r\=aXs8RP>BJM>Hr;Qgj11C1^!-8&o!,2<1!CkZ<rrLS?qu6]C:&FqloeL<6s+13rs*t~>
-!$/$<"ip05s0lUqrrL'0a8Z1p62gfa/+NT<!F<J3rrGj@kPkRUIK'6Imk*c'!IgX(rrMb1r;Qe0
-r;6Ko@tFZ2s2G"g~>
-!$/$<"ip05s0lUqrrL'0a8Z1p62gfa/+NT<!F<J3rrGj@kPkRUIK'6Imk*c'!IgX(rrMb1r;Qe0
-r;6Ko@tFZ2s2G"g~>
-!$/$<"ip05s0lUqrrL'0a8Z1p62gfa/+NT<!F<J3rrGj@kPkRUIK'6Imk*c'!IgX(rrMb1r;Qe0
-r;6Ko@tFZ2s2G"g~>
-!$/$<"db13PDH-0rrMD.rK@8'p\t0oa*QJ3rrTu\jQHODaFF2Q!Ki-#rrIS?JcC<$SH"*~>
-!$/$<"db13PDH-0rrMD.rK@8'p\t0oa*QJ3rrTu\jQHODaFF2Q!Ki-#rrIS?JcC<$SH"*~>
-!$/$<"db13PDH-0rrMD.rK@8'p\t0oa*QJ3rrTu\jQHODaFF2Q!Ki-#rrIS?JcC<$SH"*~>
-!$/!;".k@+Wh04jiVeT5li$ha_US2W!P7(JrrC%;M#`Y#rrL$cJcC<$SH"*~>
-!$/!;".k@+Wh04jiVeT5li$ha_US2W!P7(JrrC%;M#`Y#rrL$cJcC<$SH"*~>
-!$/!;".k@+Wh04jiVeT5li$ha_US2W!P7(JrrC%;M#`Y#rrL$cJcC<$SH"*~>
-!$2";!d\!iJc>iPBN]t=s+13$s,R,0~>
-!$2";!d\!iJc>iPBN]t=s+13$s,R,0~>
-!$2";!d\!iJc>iPBN]t=s+13$s,R,0~>
-!$2%<"*pi_bhN-ufQmJr!GFXFrrGGskl1[N](l:-aMX^)JcC<$JcCf2J,~>
-!$2%<"*pi_bhN-ufQmJr!GFXFrrGGskl1[N](l:-aMX^)JcC<$JcCf2J,~>
-!$2%<"*pi_bhN-ufQmJr!GFXFrrGGskl1[N](l:-aMX^)JcC<$JcCf2J,~>
-!$2%<!DgtnrrL>?rr3#^i7%],i9'8'!I1I7rrKi?JcC<$JcCf2J,~>
-!$2%<!DgtnrrL>?rr3#^i7%],i9'8'!I1I7rrKi?JcC<$JcCf2J,~>
-!$2%<!DgtnrrL>?rr3#^i7%],i9'8'!I1I7rrKi?JcC<$JcCf2J,~>
-!$2%<!b/kArr2uhroFFJs8V`^mcEQnrr2ufroF.Drr2ueroF.Err3>ojlP[L;#gR`jlQI@"n;9O
-jlPk.rrqcNjluO/q#:fjq>^Kimf3=RqW?o$n,E=gkiM+-rrD9^jT+iMrrD6]jT+lNrs.]Jjm[Mk
-s8W&Z!;-9j!rK6?JcC<$JcCf2J,~>
-!$2%<!b/kArr2uhroFFJs8V`^mcEQnrr2ufroF.Drr2ueroF.Err3>ojlP[L;#gR`jlQI@"n;9O
-jlPk.rrqcNjluO/q#:fjq>^Kimf3=RqW?o$n,E=gkiM+-rrD9^jT+iMrrD6]jT+lNrs.]Jjm[Mk
-s8W&Z!;-9j!rK6?JcC<$JcCf2J,~>
-!$2%<!b/kArr2uhroFFJs8V`^mcEQnrr2ufroF.Drr2ueroF.Err3>ojlP[L;#gR`jlQI@"n;9O
-jlPk.rrqcNjluO/q#:fjq>^Kimf3=RqW?o$n,E=gkiM+-rrD9^jT+iMrrD6]jT+lNrs.]Jjm[Mk
-s8W&Z!;-9j!rK6?JcC<$JcCf2J,~>
-!$2(=#N*U%n,NFQJc7S:6.5e!BkoXd7b%J#GQ'N(9%Et'ErJ!-:Y5X,D)XC@TMY[gHoD</rsik&
-;`?XAh>c;<2`FX*MYR2b;p,.?^Kpm>8u4d32f[pYs&:a0rVloJ8,bFMHKbCW6N/nPJ*R'\4Z><@
-?D[\J3W:rBM=(?Cj'9[bJcC<$JcCi3J,~>
-!$2(=#N*U%n,NFQJc7S:6.5e!BkoXd7b%J#GQ'N(9%Et'ErJ!-:Y5X,D)XC@TMY[gHoD</rsik&
-;`?XAh>c;<2`FX*MYR2b;p,.?^Kpm>8u4d32f[pYs&:a0rVloJ8,bFMHKbCW6N/nPJ*R'\4Z><@
-?D[\J3W:rBM=(?Cj'9[bJcC<$JcCi3J,~>
-!$2(=#N*U%n,NFQJc7S:6.5e!BkoXd7b%J#GQ'N(9%Et'ErJ!-:Y5X,D)XC@TMY[gHoD</rsik&
-;`?XAh>c;<2`FX*MYR2b;p,.?^Kpm>8u4d32f[pYs&:a0rVloJ8,bFMHKbCW6N/nPJ*R'\4Z><@
-?D[\J3W:rBM=(?Cj'9[bJcC<$JcCi3J,~>
-!$2(=!c7q^rr3"kIfB?UmOnO*/AhGeju<=#NrK%]k^NPcQi@!keO]^gTE"r]6W!a]!$2%<%;\)^
-s8T]>s'rV>s3OI6rshuMs8Ti>s8P\[p&G&[KDtlOkqi;<!BK3>rrfR8s"Wm>rsXFJs![O>s0sGQ
-ruh:>rrI&?rr3&6!.0:sJcC<$OT0h~>
-!$2(=!c7q^rr3"kIfB?UmOnO*/AhGeju<=#NrK%]k^NPcQi@!keO]^gTE"r]6W!a]!$2%<%;\)^
-s8T]>s'rV>s3OI6rshuMs8Ti>s8P\[p&G&[KDtlOkqi;<!BK3>rrfR8s"Wm>rsXFJs![O>s0sGQ
-ruh:>rrI&?rr3&6!.0:sJcC<$OT0h~>
-!$2(=!c7q^rr3"kIfB?UmOnO*/AhGeju<=#NrK%]k^NPcQi@!keO]^gTE"r]6W!a]!$2%<%;\)^
-s8T]>s'rV>s3OI6rshuMs8Ti>s8P\[p&G&[KDtlOkqi;<!BK3>rrfR8s"Wm>rsXFJs![O>s0sGQ
-ruh:>rrI&?rr3&6!.0:sJcC<$OT0h~>
-!$2(=#Hi'&pAb/b7fJDNKeECkLAq2Uju<=#Nr/hWKpVf="P$'CI@pN=!R+C=rr=)<rrHr?rr389
-@K0iJs8UG>p\tOub5_LV?2spsdf07IR=kM=!U2E=rrG.?qu6[Om/I"fJ=QWfo`"jnGbtE_NW+qC
-5-=kbmtYnkJcC<$JcCi3J,~>
-!$2(=#Hi'&pAb/b7fJDNKeECkLAq2Uju<=#Nr/hWKpVf="P$'CI@pN=!R+C=rr=)<rrHr?rr389
-@K0iJs8UG>p\tOub5_LV?2spsdf07IR=kM=!U2E=rrG.?qu6[Om/I"fJ=QWfo`"jnGbtE_NW+qC
-5-=kbmtYnkJcC<$JcCi3J,~>
-!$2(=#Hi'&pAb/b7fJDNKeECkLAq2Uju<=#Nr/hWKpVf="P$'CI@pN=!R+C=rr=)<rrHr?rr389
-@K0iJs8UG>p\tOub5_LV?2spsdf07IR=kM=!U2E=rrG.?qu6[Om/I"fJ=QWfo`"jnGbtE_NW+qC
-5-=kbmtYnkJcC<$JcCi3J,~>
-!$2%<#+^SDs8S]\rJguSs-AE=rrhICs,N-:rrIh?rr3,O8H4+1rr3#C;#UCo*W?!=Cp<p=#eOOF
-\p8:8G-L`@#uf"Hs1_k>s%:`=rrJO?rr3#`1]@=S3TL#:!AWs?rrdkBs![O=rrdSCrud="NWn2;
-s6k`>JcC<$JcCf2J,~>
-!$2%<#+^SDs8S]\rJguSs-AE=rrhICs,N-:rrIh?rr3,O8H4+1rr3#C;#UCo*W?!=Cp<p=#eOOF
-\p8:8G-L`@#uf"Hs1_k>s%:`=rrJO?rr3#`1]@=S3TL#:!AWs?rrdkBs![O=rrdSCrud="NWn2;
-s6k`>JcC<$JcCf2J,~>
-!$2%<#+^SDs8S]\rJguSs-AE=rrhICs,N-:rrIh?rr3,O8H4+1rr3#C;#UCo*W?!=Cp<p=#eOOF
-\p8:8G-L`@#uf"Hs1_k>s%:`=rrJO?rr3#`1]@=S3TL#:!AWs?rrdkBs![O=rrdSCrud="NWn2;
-s6k`>JcC<$JcCf2J,~>
-!$2%<!Dgu>rrJa@qu6\_LAq2Uju<=#NrK%]k^NPcQi@!keO]^gTE"r]6W!a]!$2%<!Go">rs-/E
-s-JqMX1J3.!EOLFrs;WHs8Ti>s8P^>rr3"eKDtlOkqi;<!BK3>rrfR8s"Wm>rsXFJs![O>s0sGQ
-ruh:9rrKi?JcC<$JcCf2J,~>
-!$2%<!Dgu>rrJa@qu6\_LAq2Uju<=#NrK%]k^NPcQi@!keO]^gTE"r]6W!a]!$2%<!Go">rs-/E
-s-JqMX1J3.!EOLFrs;WHs8Ti>s8P^>rr3"eKDtlOkqi;<!BK3>rrfR8s"Wm>rsXFJs![O>s0sGQ
-ruh:9rrKi?JcC<$JcCf2J,~>
-!$2%<!Dgu>rrJa@qu6\_LAq2Uju<=#NrK%]k^NPcQi@!keO]^gTE"r]6W!a]!$2%<!Go">rs-/E
-s-JqMX1J3.!EOLFrs;WHs8Ti>s8P^>rr3"eKDtlOkqi;<!BK3>rrfR8s"Wm>rsXFJs![O>s0sGQ
-ruh:9rrKi?JcC<$JcCf2J,~>
-!$2%<!Dgu>rrMD9r\jsKs8SC>rr3,`2ui#ar\jsPec4`Or\kNefDjlJ2`F*s;#gQC2[23Qs8Qo>
-rr3,[email protected]\jgur;Qd"2u`g`g/U'j>EbER9%*_=%%2bJs&:`]2i[k;e49Ks"FB;3dR*pl%"IRB
-coj<HF'b^CcT1t`!3#kr!PVlks+13$s,[21~>
-!$2%<!Dgu>rrMD9r\jsKs8SC>rr3,`2ui#ar\jsPec4`Or\kNefDjlJ2`F*s;#gQC2[23Qs8Qo>
-rr3,[email protected]\jgur;Qd"2u`g`g/U'j>EbER9%*_=%%2bJs&:`]2i[k;e49Ks"FB;3dR*pl%"IRB
-coj<HF'b^CcT1t`!3#kr!PVlks+13$s,[21~>
-!$2%<!Dgu>rrMD9r\jsKs8SC>rr3,`2ui#ar\jsPec4`Or\kNefDjlJ2`F*s;#gQC2[23Qs8Qo>
-rr3,[email protected]\jgur;Qd"2u`g`g/U'j>EbER9%*_=%%2bJs&:`]2i[k;e49Ks"FB;3dR*pl%"IRB
-coj<HF'b^CcT1t`!3#kr!PVlks+13$s,[21~>
-!$2%<#44f!56(ZRroF:Fs8V`^rr3-!lMpnRroF.Drr2ueroF.Err38mjlP\%mJm4SroF:Ks8VT_
-rr3,rn,IL3roF0ZH2[aDc8Y_%#jUO5lh0fJm/$_]"SD9bs60ID!:^!f!9jFD!:g'g!9a@C!:p-h
-#Nk.0nF?5Ps8D$`o`+qH*Dc*Ss+13$s,[21~>
-!$2%<#44f!56(ZRroF:Fs8V`^rr3-!lMpnRroF.Drr2ueroF.Err38mjlP\%mJm4SroF:Ks8VT_
-rr3,rn,IL3roF0ZH2[aDc8Y_%#jUO5lh0fJm/$_]"SD9bs60ID!:^!f!9jFD!:g'g!9a@C!:p-h
-#Nk.0nF?5Ps8D$`o`+qH*Dc*Ss+13$s,[21~>
-!$2%<#44f!56(ZRroF:Fs8V`^rr3-!lMpnRroF.Drr2ueroF.Err38mjlP\%mJm4SroF:Ks8VT_
-rr3,rn,IL3roF0ZH2[aDc8Y_%#jUO5lh0fJm/$_]"SD9bs60ID!:^!f!9jFD!:g'g!9a@C!:p-h
-#Nk.0nF?5Ps8D$`o`+qH*Dc*Ss+13$s,[21~>
-!$/lT!I(UDhuT^&rVloB:kAXts+13$s3Udr~>
-!$/lT!I(UDhuT^&rVloB:kAXts+13$s3Udr~>
-!$/lT!I(UDhuT^&rVloB:kAXts+13$s3Udr~>
-!$/iS!-8'$!-\;?!)nIKJcC<$JcF'rJ,~>
-!$/iS!-8'$!-\;?!)nIKJcC<$JcF'rJ,~>
-!$/iS!-8'$!-\;?!)nIKJcC<$JcF'rJ,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-"!.FAao7+sNW/qY`R48V"0^kcRDo1.QN,gOJcC<$JcC<$iW"E~>
-"!.FAao7+sNW/qY`R48V"0^kcRDo1.QN,gOJcC<$JcC<$iW"E~>
-"!.FAao7+sNW/qY`R48V"0^kcRDo1.QN,gOJcC<$JcC<$iW"E~>
-"!.EcIf>N8P(N`6b'V_2rrZ3AqBEoY"!.ENAqBu5s+13$s5F!.~>
-"!.EcIf>N8P(N`6b'V_2rrZ3AqBEoY"!.ENAqBu5s+13$s5F!.~>
-"!.EcIf>N8P(N`6b'V_2rrZ3AqBEoY"!.ENAqBu5s+13$s5F!.~>
-"!.E>FoMCApEon>!mbW@o`#"land4#rrX;A[q:3kJcC<$JcF^/J,~>
-"!.E>FoMCApEon>!mbW@o`#"land4#rrX;A[q:3kJcC<$JcF^/J,~>
-"!.E>FoMCApEon>!mbW@o`#"land4#rrX;A[q:3kJcC<$JcF^/J,~>
-"!.F0jSo/[8C[V<!o$B@p&>9Qo`!>>s.-4irrh.os-Btirrgnos,aejrs6qrs,"Pks57Vjrr?j6
-1&u7krrYqM13WZs!+#RZ!.FhG"4R;?iIV#[s+13$s5F!.~>
-"!.F0jSo/[8C[V<!o$B@p&>9Qo`!>>s.-4irrh.os-Btirrgnos,aejrs6qrs,"Pks57Vjrr?j6
-1&u7krrYqM13WZs!+#RZ!.FhG"4R;?iIV#[s+13$s5F!.~>
-"!.F0jSo/[8C[V<!o$B@p&>9Qo`!>>s.-4irrh.os-Btirrgnos,aejrs6qrs,"Pks57Vjrr?j6
-1&u7krrYqM13WZs!+#RZ!.FhG"4R;?iIV#[s+13$s5F!.~>
-!$2%<!r>IAqu6]a0CSoAKpVf="P$'CI@pN='[0DRFK#6>hA08oCp<s>fgPl?E3K9+!NL>Brs'\0
-[oNJ.@D2[*!M"jps+13$s+14(s*t~>
-!$2%<!r>IAqu6]a0CSoAKpVf="P$'CI@pN='[0DRFK#6>hA08oCp<s>fgPl?E3K9+!NL>Brs'\0
-[oNJ.@D2[*!M"jps+13$s+14(s*t~>
-!$2%<!r>IAqu6]a0CSoAKpVf="P$'CI@pN='[0DRFK#6>hA08oCp<s>fgPl?E3K9+!NL>Brs'\0
-[oNJ.@D2[*!M"jps+13$s+14(s*t~>
-!$2(=!pX(@qYpTT4R`:NKpVf="P$'CI>RsX'X(@5FK#5r5/^V(Cp<s>fgPl?A%DU=!OHM>rs'\E
-aAr9?;m$&R!K28Ts+13$s+14(s*t~>
-!$2(=!pX(@qYpTT4R`:NKpVf="P$'CI>RsX'X(@5FK#5r5/^V(Cp<s>fgPl?A%DU=!OHM>rs'\E
-aAr9?;m$&R!K28Ts+13$s+14(s*t~>
-!$2(=!pX(@qYpTT4R`:NKpVf="P$'CI>RsX'X(@5FK#5r5/^V(Cp<s>fgPl?A%DU=!OHM>rs'\E
-aAr9?;m$&R!K28Ts+13$s+14(s*t~>
-"WdXCiHE09rrUPJp@&"^KpVf="P$'CI4>.]'JrYdFK!D[s28(?Cp<s>fgPl?A%DU=%'s[Js34F>
-aAr9?;`+G=!1\W?JcC<$JcFI(J,~>
-"WdXCiHE09rrUPJp@&"^KpVf="P$'CI4>.]'JrYdFK!D[s28(?Cp<s>fgPl?A%DU=%'s[Js34F>
-aAr9?;`+G=!1\W?JcC<$JcFI(J,~>
-"WdXCiHE09rrUPJp@&"^KpVf="P$'CI4>.]'JrYdFK!D[s28(?Cp<s>fgPl?A%DU=%'s[Js34F>
-aAr9?;`+G=!1\W?JcC<$JcFI(J,~>
-"<IO"K"LmZ!lf6Amf*TIQiI*^L.M>mTDnj)bY\=]C2NA<`**+VYlFaV70!9s\c2U;ZYBI>52ZC]
-<WE(tb45K5pS#?Qs+13$s+146s*t~>
-"<IO"K"LmZ!lf6Amf*TIQiI*^L.M>mTDnj)bY\=]C2NA<`**+VYlFaV70!9s\c2U;ZYBI>52ZC]
-<WE(tb45K5pS#?Qs+13$s+146s*t~>
-"<IO"K"LmZ!lf6Amf*TIQiI*^L.M>mTDnj)bY\=]C2NA<`**+VYlFaV70!9s\c2U;ZYBI>52ZC]
-<WE(tb45K5pS#?Qs+13$s+146s*t~>
-"!.E>4oL$;QN.!%J(j;i$,D(EKS*o.s*^O=rtN[Rs)_,ss8U#?s(knmKQ&0Bs0%"QKFEF;s0*Ve
-KP)jJs/(DIKE/:MrrVEb;1\aus+13$s60K5~>
-"!.E>4oL$;QN.!%J(j;i$,D(EKS*o.s*^O=rtN[Rs)_,ss8U#?s(knmKQ&0Bs0%"QKFEF;s0*Ve
-KP)jJs/(DIKE/:MrrVEb;1\aus+13$s60K5~>
-"!.E>4oL$;QN.!%J(j;i$,D(EKS*o.s*^O=rtN[Rs)_,ss8U#?s(knmKQ&0Bs0%"QKFEF;s0*Ve
-KP)jJs/(DIKE/:MrrVEb;1\aus+13$s60K5~>
-!Zh<rr0RVbs8U<pli.7-R$aGp8H6ibrr3,d\GsV)rr3)`]`5qKQiqA:s8TJ*Qisnus''RDQiq#A
-s8T8$QiOknrrVn]\Ujd3s+13$s60K5~>
-!Zh<rr0RVbs8U<pli.7-R$aGp8H6ibrr3,d\GsV)rr3)`]`5qKQiqA:s8TJ*Qisnus''RDQiq#A
-s8T8$QiOknrrVn]\Ujd3s+13$s60K5~>
-!Zh<rr0RVbs8U<pli.7-R$aGp8H6ibrr3,d\GsV)rr3)`]`5qKQiqA:s8TJ*Qisnus''RDQiq#A
-s8T8$QiOknrrVn]\Ujd3s+13$s60K5~>
-!$1"t!NEL+rrLY@li.!t@JKj'cgC`3!Tqsas+13$s+13us*t~>
-!$1"t!NEL+rrLY@li.!t@JKj'cgC`3!Tqsas+13$s+13us*t~>
-!$1"t!NEL+rrLY@li.!t@JKj'cgC`3!Tqsas+13$s+13us*t~>
-!$1"t!U4S&/HJH"li."Wj+75]s+13$s2+ed~>
-!$1"t!U4S&/HJH"li."Wj+75]s+13$s2+ed~>
-!$1"t!U4S&/HJH"li."Wj+75]s+13$s2+ed~>
-!$2(="8MHXoB-&Qp&=C[nU^_$rr`#do(.G4JcDABJ,~>
-!$2(="8MHXoB-&Qp&=C[nU^_$rr`#do(.G4JcDABJ,~>
-!$2(="8MHXoB-&Qp&=C[nU^_$rr`#do(.G4JcDABJ,~>
-!$2(=")T&-1&V%PiV<?N"6A%arr2otkl0-5g\h'Ph>HmE!8@>M!oDM`r;QiRf%p?*!9F(/!9j.V
-!WCjOrrUg'j8ArWec=e0rr`5Nf&cQ(!VYROrr`)Jf'<87!<)lI!:]mc!U0(HrrMTRr;Qlif%0?h
-rr_fBf(/e>!:^!=!;Q6e!U0(Ors%i@f(]4Eo_d8?g&D!Ol2K9+rrM*Rr;Qu_f%0j#s6]a8!oX+E
-r72)2s8RBo.2IL7JcDABJ,~>
-!$2(=")T&-1&V%PiV<?N"6A%arr2otkl0-5g\h'Ph>HmE!8@>M!oDM`r;QiRf%p?*!9F(/!9j.V
-!WCjOrrUg'j8ArWec=e0rr`5Nf&cQ(!VYROrr`)Jf'<87!<)lI!:]mc!U0(HrrMTRr;Qlif%0?h
-rr_fBf(/e>!:^!=!;Q6e!U0(Ors%i@f(]4Eo_d8?g&D!Ol2K9+rrM*Rr;Qu_f%0j#s6]a8!oX+E
-r72)2s8RBo.2IL7JcDABJ,~>
-!$2(=")T&-1&V%PiV<?N"6A%arr2otkl0-5g\h'Ph>HmE!8@>M!oDM`r;QiRf%p?*!9F(/!9j.V
-!WCjOrrUg'j8ArWec=e0rr`5Nf&cQ(!VYROrr`)Jf'<87!<)lI!:]mc!U0(HrrMTRr;Qlif%0?h
-rr_fBf(/e>!:^!=!;Q6e!U0(Ors%i@f(]4Eo_d8?g&D!Ol2K9+rrM*Rr;Qu_f%0j#s6]a8!oX+E
-r72)2s8RBo.2IL7JcDABJ,~>
-!$2(=!H#(<rrU\+ec#LRg2BGM^An5kH2^86<m(FA!mMnWo`"s6(&\(7c<Nh@bPqMIbZRD>cN!oK
-2>ouERJ-X]ZRbtR"N*i$CY/Rc"MdJrDV>$h"M.&lEnpBg!r.jWrVm0%WCB@+j8].frC-gJmJ[%d
-i)?WKrrVP,JGoK[ns2a7ORE/Kmug.0Pk"eRm>h08!9!SN!oR"WrVm,bK18>*qZ"e;7KaM,s5A>(
-7KEG]rrU_-eG]CPg2KMN^&S,8rC-lP56%S[7KGS@r;Qa;JcC<$TDsE~>
-!$2(=!H#(<rrU\+ec#LRg2BGM^An5kH2^86<m(FA!mMnWo`"s6(&\(7c<Nh@bPqMIbZRD>cN!oK
-2>ouERJ-X]ZRbtR"N*i$CY/Rc"MdJrDV>$h"M.&lEnpBg!r.jWrVm0%WCB@+j8].frC-gJmJ[%d
-i)?WKrrVP,JGoK[ns2a7ORE/Kmug.0Pk"eRm>h08!9!SN!oR"WrVm,bK18>*qZ"e;7KaM,s5A>(
-7KEG]rrU_-eG]CPg2KMN^&S,8rC-lP56%S[7KGS@r;Qa;JcC<$TDsE~>
-!$2(=!H#(<rrU\+ec#LRg2BGM^An5kH2^86<m(FA!mMnWo`"s6(&\(7c<Nh@bPqMIbZRD>cN!oK
-2>ouERJ-X]ZRbtR"N*i$CY/Rc"MdJrDV>$h"M.&lEnpBg!r.jWrVm0%WCB@+j8].frC-gJmJ[%d
-i)?WKrrVP,JGoK[ns2a7ORE/Kmug.0Pk"eRm>h08!9!SN!oR"WrVm,bK18>*qZ"e;7KaM,s5A>(
-7KEG]rrU_-eG]CPg2KMN^&S,8rC-lP56%S[7KGS@r;Qa;JcC<$TDsE~>
-!$2(=!H#(=rr^Pl*7b&g$.aRGs8Ql>s,N-=rs.@Es8U#Z'(>Mn!krU^rVmJLFlEAaEl.jnF6<M^
-FN+8a_=IU-X^NaYrt`"Xqu=rhdf6Ufs8U/hec2gjs8Tudg%YLJQVCKYrs5BFs8TH[ir:%trrHH?
-rr3)XK.X(srr__K4b3P3')7TQs8SOJoCS*2s8S@Gp@4-5oD\pMK.O&%rs%YBXT/<NSGW<iX)\0u
-KX^RQ"4)95eGfIPf7(a>s(nt<rrW1aM>[ATqNHs;rr='js+13Bs*t~>
-!$2(=!H#(=rr^Pl*7b&g$.aRGs8Ql>s,N-=rs.@Es8U#Z'(>Mn!krU^rVmJLFlEAaEl.jnF6<M^
-FN+8a_=IU-X^NaYrt`"Xqu=rhdf6Ufs8U/hec2gjs8Tudg%YLJQVCKYrs5BFs8TH[ir:%trrHH?
-rr3)XK.X(srr__K4b3P3')7TQs8SOJoCS*2s8S@Gp@4-5oD\pMK.O&%rs%YBXT/<NSGW<iX)\0u
-KX^RQ"4)95eGfIPf7(a>s(nt<rrW1aM>[ATqNHs;rr='js+13Bs*t~>
-!$2(=!H#(=rr^Pl*7b&g$.aRGs8Ql>s,N-=rs.@Es8U#Z'(>Mn!krU^rVmJLFlEAaEl.jnF6<M^
-FN+8a_=IU-X^NaYrt`"Xqu=rhdf6Ufs8U/hec2gjs8Tudg%YLJQVCKYrs5BFs8TH[ir:%trrHH?
-rr3)XK.X(srr__K4b3P3')7TQs8SOJoCS*2s8S@Gp@4-5oD\pMK.O&%rs%YBXT/<NSGW<iX)\0u
-KX^RQ"4)95eGfIPf7(a>s(nt<rrW1aM>[ATqNHs;rr='js+13Bs*t~>
-!$2(=!H#(>rrgYtbt$=`rrJF?rr3,`2uenurr35Z5QATNg+Dderr]f^iZ8!t!Go">rt)eNs0@.>
-s6"m>s'*=#It+Hlrr]'Il7;i5!CGQ?rsY$Js.+]?s3cD>s"Wm>rrIY?q#:GWNQ5&trr=)<rrZoB
-ruM(<#[Y7Fs5ea>F/es7"T!VCSUg_>!UMN=rsLsInURY>s/:J?gHkH2"QY'BEiT-=!Q&(>rr=):
-rra_Bs06D3rrgYtbt-@`rrJI?rr3#^2Z3RUq]Yk:!V7c;rr='js+13Bs*t~>
-!$2(=!H#(>rrgYtbt$=`rrJF?rr3,`2uenurr35Z5QATNg+Dderr]f^iZ8!t!Go">rt)eNs0@.>
-s6"m>s'*=#It+Hlrr]'Il7;i5!CGQ?rsY$Js.+]?s3cD>s"Wm>rrIY?q#:GWNQ5&trr=)<rrZoB
-ruM(<#[Y7Fs5ea>F/es7"T!VCSUg_>!UMN=rsLsInURY>s/:J?gHkH2"QY'BEiT-=!Q&(>rr=):
-rra_Bs06D3rrgYtbt-@`rrJI?rr3#^2Z3RUq]Yk:!V7c;rr='js+13Bs*t~>
-!$2(=!H#(>rrgYtbt$=`rrJF?rr3,`2uenurr35Z5QATNg+Dderr]f^iZ8!t!Go">rt)eNs0@.>
-s6"m>s'*=#It+Hlrr]'Il7;i5!CGQ?rsY$Js.+]?s3cD>s"Wm>rrIY?q#:GWNQ5&trr=)<rrZoB
-ruM(<#[Y7Fs5ea>F/es7"T!VCSUg_>!UMN=rsLsInURY>s/:J?gHkH2"QY'BEiT-=!Q&(>rr=):
-rra_Bs06D3rrgYtbt-@`rrJI?rr3#^2Z3RUq]Yk:!V7c;rr='js+13Bs*t~>
-!$2(=!H#(>rrh'#s$tW=rrJF?rr3Mk2uenus8W!s5QB*^s"3^5rr^F#ruM(<!Go">rt2kOs8TTE
-@Vr^Vs'&*]SXkV9q#:H,mdVh+rrGI@rr3ChMuWgT@UaQ)s8Oh?rr3"LSG<*dWW'q<rVlj<rVlt4
-[f7BIrs>hR^Am_@]`3&Pq#:KoXT+iArr3#c0`D"[4Q-8(M.0qOli5^*`Vs<HjS8`Xlb3==WrE#!
-_H[!=!$2";#)nfDs06C@raGm,q#:KNc2U>arr3"cL&V)Qk;N8<!HY:<rrI&?qu6X:JcC<$TDsE~>
-!$2(=!H#(>rrh'#s$tW=rrJF?rr3Mk2uenus8W!s5QB*^s"3^5rr^F#ruM(<!Go">rt2kOs8TTE
-@Vr^Vs'&*]SXkV9q#:H,mdVh+rrGI@rr3ChMuWgT@UaQ)s8Oh?rr3"LSG<*dWW'q<rVlj<rVlt4
-[f7BIrs>hR^Am_@]`3&Pq#:KoXT+iArr3#c0`D"[4Q-8(M.0qOli5^*`Vs<HjS8`Xlb3==WrE#!
-_H[!=!$2";#)nfDs06C@raGm,q#:KNc2U>arr3"cL&V)Qk;N8<!HY:<rrI&?qu6X:JcC<$TDsE~>
-!$2(=!H#(>rrh'#s$tW=rrJF?rr3Mk2uenus8W!s5QB*^s"3^5rr^F#ruM(<!Go">rt2kOs8TTE
-@Vr^Vs'&*]SXkV9q#:H,mdVh+rrGI@rr3ChMuWgT@UaQ)s8Oh?rr3"LSG<*dWW'q<rVlj<rVlt4
-[f7BIrs>hR^Am_@]`3&Pq#:KoXT+iArr3#c0`D"[4Q-8(M.0qOli5^*`Vs<HjS8`Xlb3==WrE#!
-_H[!=!$2";#)nfDs06C@raGm,q#:KNc2U>arr3"cL&V)Qk;N8<!HY:<rrI&?qu6X:JcC<$TDsE~>
-!$2(=!H#(;rrGX?rr3"bLAq2Zju<=uGst.lTg/MU!A3d3rr=)<rrHr?rr3SB@K6@PVn/[Ps8UQZ
-pAb/mEq]M6nLOS<!CGQ?rsY$Js8Q96\riK^s"Wm>rrIY?pAY06<W2pt*W?!?B=@iLr^@-E7DAe$
-aT)9]WV-8lSUg_>!UMN=rsV$Js42%u\n]pugCeK)\cGt,pAY/6WrE#!_H[!=!$2";#"7,Ds03St
-rji)5p&>%`eGfIKQ\GG=!TuB=rr=)9rr=)9rr='js+13Bs*t~>
-!$2(=!H#(;rrGX?rr3"bLAq2Zju<=uGst.lTg/MU!A3d3rr=)<rrHr?rr3SB@K6@PVn/[Ps8UQZ
-pAb/mEq]M6nLOS<!CGQ?rsY$Js8Q96\riK^s"Wm>rrIY?pAY06<W2pt*W?!?B=@iLr^@-E7DAe$
-aT)9]WV-8lSUg_>!UMN=rsV$Js42%u\n]pugCeK)\cGt,pAY/6WrE#!_H[!=!$2";#"7,Ds03St
-rji)5p&>%`eGfIKQ\GG=!TuB=rr=)9rr=)9rr='js+13Bs*t~>
-!$2(=!H#(;rrGX?rr3"bLAq2Zju<=uGst.lTg/MU!A3d3rr=)<rrHr?rr3SB@K6@PVn/[Ps8UQZ
-pAb/mEq]M6nLOS<!CGQ?rsY$Js8Q96\riK^s"Wm>rrIY?pAY06<W2pt*W?!?B=@iLr^@-E7DAe$
-aT)9]WV-8lSUg_>!UMN=rsV$Js42%u\n]pugCeK)\cGt,pAY/6WrE#!_H[!=!$2";#"7,Ds03St
-rji)5p&>%`eGfIKQ\GG=!TuB=rr=)9rr=)9rr='js+13Bs*t~>
-!$2(=!H#(;rrGX?rr3"bLAq2Zju<>?l.=P>h*:l<!A3d3rr=)<rrHr?rr3;:@K1_Qq>^5bJc#HJ
-WcIh5!V%]=rrGI@rr3ChMuQnts8VVDV>i::rr3"LSG)s`a&W*<!$2%<")#(BrVkCOls@F+$N;FT
-&f]0jrrJ[@rr3#c0`D"[4Q,F!jT#8@9^LLmk5PA]/+N?5!HP4>rrKu@rVlj<rVm+KW;6JnZ!6So
-rr=)4rrG[?rr3"cL&V)Qk;N;=!E[;<rrGp?qYpO9JcC<$TDsE~>
-!$2(=!H#(;rrGX?rr3"bLAq2Zju<>?l.=P>h*:l<!A3d3rr=)<rrHr?rr3;:@K1_Qq>^5bJc#HJ
-WcIh5!V%]=rrGI@rr3ChMuQnts8VVDV>i::rr3"LSG)s`a&W*<!$2%<")#(BrVkCOls@F+$N;FT
-&f]0jrrJ[@rr3#c0`D"[4Q,F!jT#8@9^LLmk5PA]/+N?5!HP4>rrKu@rVlj<rVm+KW;6JnZ!6So
-rr=)4rrG[?rr3"cL&V)Qk;N;=!E[;<rrGp?qYpO9JcC<$TDsE~>
-!$2(=!H#(;rrGX?rr3"bLAq2Zju<>?l.=P>h*:l<!A3d3rr=)<rrHr?rr3;:@K1_Qq>^5bJc#HJ
-WcIh5!V%]=rrGI@rr3ChMuQnts8VVDV>i::rr3"LSG)s`a&W*<!$2%<")#(BrVkCOls@F+$N;FT
-&f]0jrrJ[@rr3#c0`D"[4Q,F!jT#8@9^LLmk5PA]/+N?5!HP4>rrKu@rVlj<rVm+KW;6JnZ!6So
-rr=)4rrG[?rr3"cL&V)Qk;N;=!E[;<rrGp?qYpO9JcC<$TDsE~>
-!$2(=!H#(;rrGX?rr31nL&(cKfL5W:!ROO=rrF_?qu6]dn,*+b*W?!MG,kK?r3L5>DR'->r2b#?
-o_\Xf!NL2=rrMNXr;Qff/,fJ`:X9"?q2^a>8(IY>q1kI>5McA>pjf.=rrW,cp&+gja&W*<"t]9D
-s7^9?r;QlmA&J?FoE&p)r;Q]tq<dtTrrJ[@rr3St47iLPoL@j%6hC?Xo02Ho62gfa/+NW=!qu$Y
-r;Qe<WrE#&`aJN?s7%Z>rrVn+_#F?7Z#'C=!$2%<!qYgXr;QdgeGfIPSqQq>s4Kd=rrVdXkPY>]
-o/c@:rr='js+13Bs*t~>
-!$2(=!H#(;rrGX?rr31nL&(cKfL5W:!ROO=rrF_?qu6]dn,*+b*W?!MG,kK?r3L5>DR'->r2b#?
-o_\Xf!NL2=rrMNXr;Qff/,fJ`:X9"?q2^a>8(IY>q1kI>5McA>pjf.=rrW,cp&+gja&W*<"t]9D
-s7^9?r;QlmA&J?FoE&p)r;Q]tq<dtTrrJ[@rr3St47iLPoL@j%6hC?Xo02Ho62gfa/+NW=!qu$Y
-r;Qe<WrE#&`aJN?s7%Z>rrVn+_#F?7Z#'C=!$2%<!qYgXr;QdgeGfIPSqQq>s4Kd=rrVdXkPY>]
-o/c@:rr='js+13Bs*t~>
-!$2(=!H#(;rrGX?rr31nL&(cKfL5W:!ROO=rrF_?qu6]dn,*+b*W?!MG,kK?r3L5>DR'->r2b#?
-o_\Xf!NL2=rrMNXr;Qff/,fJ`:X9"?q2^a>8(IY>q1kI>5McA>pjf.=rrW,cp&+gja&W*<"t]9D
-s7^9?r;QlmA&J?FoE&p)r;Q]tq<dtTrrJ[@rr3St47iLPoL@j%6hC?Xo02Ho62gfa/+NW=!qu$Y
-r;Qe<WrE#&`aJN?s7%Z>rrVn+_#F?7Z#'C=!$2%<!qYgXr;QdgeGfIPSqQq>s4Kd=rrVdXkPY>]
-o/c@:rr='js+13Bs*t~>
-!$2(=!H#(>rseACd5nGXg&M)7QI4)Lrr3GhdF$63s8V?2d3Z]XhYmHT,o6L[%Hc'c(t$ais8R#B
-dC<ffrs3;IdBd]is*:UCd/f_qrr3&r!'pP`%GoLO,LOp&s8Pd:d@H*mrrbg=d?p!orrbd<d?9go
-rrULHCB"5Ef$U6^dF%I`qF/fZJueqOr;P(EIBrbM!HY7=ruS!:PlLd^dF!JXdF%ahf3m"RB?pPW
-d:L_Q@b(M<=T-VJ9(W&^'70_os8Vc>d9V[XdJs6SHdU58nG`FgJ"HW=!P`^Sd/ZZ7rt8-.l2UeO
-dEt%YdFnR"P*XMC7fE>f<64t:!DLl9rr='js+13Bs*t~>
-!$2(=!H#(>rseACd5nGXg&M)7QI4)Lrr3GhdF$63s8V?2d3Z]XhYmHT,o6L[%Hc'c(t$ais8R#B
-dC<ffrs3;IdBd]is*:UCd/f_qrr3&r!'pP`%GoLO,LOp&s8Pd:d@H*mrrbg=d?p!orrbd<d?9go
-rrULHCB"5Ef$U6^dF%I`qF/fZJueqOr;P(EIBrbM!HY7=ruS!:PlLd^dF!JXdF%ahf3m"RB?pPW
-d:L_Q@b(M<=T-VJ9(W&^'70_os8Vc>d9V[XdJs6SHdU58nG`FgJ"HW=!P`^Sd/ZZ7rt8-.l2UeO
-dEt%YdFnR"P*XMC7fE>f<64t:!DLl9rr='js+13Bs*t~>
-!$2(=!H#(>rseACd5nGXg&M)7QI4)Lrr3GhdF$63s8V?2d3Z]XhYmHT,o6L[%Hc'c(t$ais8R#B
-dC<ffrs3;IdBd]is*:UCd/f_qrr3&r!'pP`%GoLO,LOp&s8Pd:d@H*mrrbg=d?p!orrbd<d?9go
-rrULHCB"5Ef$U6^dF%I`qF/fZJueqOr;P(EIBrbM!HY7=ruS!:PlLd^dF!JXdF%ahf3m"RB?pPW
-d:L_Q@b(M<=T-VJ9(W&^'70_os8Vc>d9V[XdJs6SHdU58nG`FgJ"HW=!P`^Sd/ZZ7rt8-.l2UeO
-dEt%YdFnR"P*XMC7fE>f<64t:!DLl9rr='js+13Bs*t~>
-!$2(=#&Sap[f?B>r(@$+rr3(Z92#3^rr\``92GQe!3,lh!.OnH!G*,IrrAVf9)skKrrR(2B)MZ1
-BM31?rr@9B9)sbGrrW'`5lUc`JG`%?W;ceu:eQK?rr`6n98`]J"8GM^OSo+[h)/sHrr?O+9*WB8
-s5lsIUAk/mp&7SaW;?MpSAY=*"i)ONs8Vlk9*XGVs2[i+_#=98_FmPArr2uGrC[.KrVm%q*5DOR
-gAFSD:&b.pX%Q+BrVlnp]);R.Zi*Msrr)j#KFd>Is1J8&!,MT6"-#rHr;HWrLY2M(!J0,*rr^WT
-Zl=SrJcDABJ,~>
-!$2(=#&Sap[f?B>r(@$+rr3(Z92#3^rr\``92GQe!3,lh!.OnH!G*,IrrAVf9)skKrrR(2B)MZ1
-BM31?rr@9B9)sbGrrW'`5lUc`JG`%?W;ceu:eQK?rr`6n98`]J"8GM^OSo+[h)/sHrr?O+9*WB8
-s5lsIUAk/mp&7SaW;?MpSAY=*"i)ONs8Vlk9*XGVs2[i+_#=98_FmPArr2uGrC[.KrVm%q*5DOR
-gAFSD:&b.pX%Q+BrVlnp]);R.Zi*Msrr)j#KFd>Is1J8&!,MT6"-#rHr;HWrLY2M(!J0,*rr^WT
-Zl=SrJcDABJ,~>
-!$2(=#&Sap[f?B>r(@$+rr3(Z92#3^rr\``92GQe!3,lh!.OnH!G*,IrrAVf9)skKrrR(2B)MZ1
-BM31?rr@9B9)sbGrrW'`5lUc`JG`%?W;ceu:eQK?rr`6n98`]J"8GM^OSo+[h)/sHrr?O+9*WB8
-s5lsIUAk/mp&7SaW;?MpSAY=*"i)ONs8Vlk9*XGVs2[i+_#=98_FmPArr2uGrC[.KrVm%q*5DOR
-gAFSD:&b.pX%Q+BrVlnp]);R.Zi*Msrr)j#KFd>Is1J8&!,MT6"-#rHr;HWrLY2M(!J0,*rr^WT
-Zl=SrJcDABJ,~>
-!$2(="/omHD;5'j*S^Spgd'Wo!O?IprrIq?g]%:[^YAbh1[3cr"/0CAFFjICs.B=A~>
-!$2(="/omHD;5'j*S^Spgd'Wo!O?IprrIq?g]%:[^YAbh1[3cr"/0CAFFjICs.B=A~>
-!$2(="/omHD;5'j*S^Spgd'Wo!O?IprrIq?g]%:[^YAbh1[3cr"/0CAFFjICs.B=A~>
-!$0Vi!KE/qrrW+FUtktOk'abprrUPsbhW4"\q/eprrT!Kon!-os+14Fs*t~>
-!$0Vi!KE/qrrW+FUtktOk'abprrUPsbhW4"\q/eprrT!Kon!-os+14Fs*t~>
-!$0Vi!KE/qrrW+FUtktOk'abprrUPsbhW4"\q/eprrT!Kon!-os+14Fs*t~>
-!$0Vi!O2LWrrN)%g]%<B^>&YghTjmU!RKTVrrKn$JcC<$JcGKEJ,~>
-!$0Vi!O2LWrrN)%g]%<B^>&YghTjmU!RKTVrrKn$JcC<$JcGKEJ,~>
-!$0Vi!O2LWrrN)%g]%<B^>&YghTjmU!RKTVrrKn$JcC<$JcGKEJ,~>
-!$.X1!SDh6rrC7A5QK%#rrJ>#p&>&uU\t,l_>Z\"ec#LKV6GONJcC<$hZ&*~>
-!$.X1!SDh6rrC7A5QK%#rrJ>#p&>&uU\t,l_>Z\"ec#LKV6GONJcC<$hZ&*~>
-!$.X1!SDh6rrC7A5QK%#rrJ>#p&>&uU\t,l_>Z\"ec#LKV6GONJcC<$hZ&*~>
-!Zh<Ir$)>4s8S:^o)Ac,,O4Up!^%].ec,^;8E]sO"TCUbZAJS\"Z/[Js,sDOrs&<E>Q=_N_"[j3
-dV8`9[J'V'\lIP@p@\FeS;d+>rrC+<,66HsrrV_<Y5SA'o\A4na8Q#>mVojjs+13$s53j,~>
-!Zh<Ir$)>4s8S:^o)Ac,,O4Up!^%].ec,^;8E]sO"TCUbZAJS\"Z/[Js,sDOrs&<E>Q=_N_"[j3
-dV8`9[J'V'\lIP@p@\FeS;d+>rrC+<,66HsrrV_<Y5SA'o\A4na8Q#>mVojjs+13$s53j,~>
-!Zh<Ir$)>4s8S:^o)Ac,,O4Up!^%].ec,^;8E]sO"TCUbZAJS\"Z/[Js,sDOrs&<E>Q=_N_"[j3
-dV8`9[J'V'\lIP@p@\FeS;d+>rrC+<,66HsrrV_<Y5SA'o\A4na8Q#>mVojjs+13$s53j,~>
-!Zh=>rV6]S3WK-WF0PQ6rrW)nrp9Xf5Ml4O?/GS],PqE@f1,]<&cNmPVK`(?2WiUik^]N0s6V]9
-rrs_D_.`%J]_DF/b>J:\WV6>m\mk:2!r%S@rr3#S7/co^0_,/B!f!3?qu6[sa8Gr=GH(Ijs+13$
-s5<p-~>
-!Zh=>rV6]S3WK-WF0PQ6rrW)nrp9Xf5Ml4O?/GS],PqE@f1,]<&cNmPVK`(?2WiUik^]N0s6V]9
-rrs_D_.`%J]_DF/b>J:\WV6>m\mk:2!r%S@rr3#S7/co^0_,/B!f!3?qu6[sa8Gr=GH(Ijs+13$
-s5<p-~>
-!Zh=>rV6]S3WK-WF0PQ6rrW)nrp9Xf5Ml4O?/GS],PqE@f1,]<&cNmPVK`(?2WiUik^]N0s6V]9
-rrs_D_.`%J]_DF/b>J:\WV6>m\mk:2!r%S@rr3#S7/co^0_,/B!f!3?qu6[sa8Gr=GH(Ijs+13$
-s5<p-~>
-!$2";!S0d=rrR#CrV$6nilfOArVllVrlY;kr;QfHd/<nK3o^1dHN4$"rlYl+s8V$Is3AgBs8Us!
-bPB?;rr2uLrlYT(s8Ufr(X("[rVlj<qu7B09`MVcs8Q9ls2[$bs8UT^-HjTqrr2uBrlY`7s8Pal
-s0OVbs8UL?rlY<.rr3,OFoRN7pAY0)@f?<-qpkZF"k`YNs/C)<rrM+mrr3&R:%\Da!F<J;rrH-?
-qu6[kd"24Js+14.s*t~>
-!$2";!S0d=rrR#CrV$6nilfOArVllVrlY;kr;QfHd/<nK3o^1dHN4$"rlYl+s8V$Is3AgBs8Us!
-bPB?;rr2uLrlYT(s8Ufr(X("[rVlj<qu7B09`MVcs8Q9ls2[$bs8UT^-HjTqrr2uBrlY`7s8Pal
-s0OVbs8UL?rlY<.rr3,OFoRN7pAY0)@f?<-qpkZF"k`YNs/C)<rrM+mrr3&R:%\Da!F<J;rrH-?
-qu6[kd"24Js+14.s*t~>
-!$2";!S0d=rrR#CrV$6nilfOArVllVrlY;kr;QfHd/<nK3o^1dHN4$"rlYl+s8V$Is3AgBs8Us!
-bPB?;rr2uLrlYT(s8Ufr(X("[rVlj<qu7B09`MVcs8Q9ls2[$bs8UT^-HjTqrr2uBrlY`7s8Pal
-s0OVbs8UL?rlY<.rr3,OFoRN7pAY0)@f?<-qpkZF"k`YNs/C)<rrM+mrr3&R:%\Da!F<J;rrH-?
-qu6[kd"24Js+14.s*t~>
-!$2%<!luR\qu6\6YP.tuSPTp\rr3#PGQ(D>@)`*E!@8NZrrG1?rVloGD>m?GC!H[BQ*41aD:&;e
-4]%qNE7=jeFoG2CF4L;C:^KrpQMpg`*UNe4CeG:8:p'ct\P`?&!J]_2rrF"/r_NWSlgOiS\mkX<
-!UMN=rrkOCs8T'>q>UNBCZ53k!F<J;rrH-?qu6[kd"24Js+14.s*t~>
-!$2%<!luR\qu6\6YP.tuSPTp\rr3#PGQ(D>@)`*E!@8NZrrG1?rVloGD>m?GC!H[BQ*41aD:&;e
-4]%qNE7=jeFoG2CF4L;C:^KrpQMpg`*UNe4CeG:8:p'ct\P`?&!J]_2rrF"/r_NWSlgOiS\mkX<
-!UMN=rrkOCs8T'>q>UNBCZ53k!F<J;rrH-?qu6[kd"24Js+14.s*t~>
-!$2%<!luR\qu6\6YP.tuSPTp\rr3#PGQ(D>@)`*E!@8NZrrG1?rVloGD>m?GC!H[BQ*41aD:&;e
-4]%qNE7=jeFoG2CF4L;C:^KrpQMpg`*UNe4CeG:8:p'ct\P`?&!J]_2rrF"/r_NWSlgOiS\mkX<
-!UMN=rrkOCs8T'>q>UNBCZ53k!F<J;rrH-?qu6[kd"24Js+14.s*t~>
-!$2%<".O.FUAOrj*VB@42Wk#=!JQm>rrLY@rVlnMHN*pI`0)9pTDnikCp<p=&\DKOs$g8gs0HG>
->J^=[s/L,:rrJj?rr2s=qYpO9li."Q0`:qO+T;<>!Nj`DrrEjhrr3"*^@2(&\mkX<$hSPIs8TTP
-mJm3cF8Gn<b'_b:rrHE?qu6[sa8>l;:!eIkJcC<$i;\<~>
-!$2%<".O.FUAOrj*VB@42Wk#=!JQm>rrLY@rVlnMHN*pI`0)9pTDnikCp<p=&\DKOs$g8gs0HG>
->J^=[s/L,:rrJj?rr2s=qYpO9li."Q0`:qO+T;<>!Nj`DrrEjhrr3"*^@2(&\mkX<$hSPIs8TTP
-mJm3cF8Gn<b'_b:rrHE?qu6[sa8>l;:!eIkJcC<$i;\<~>
-!$2%<".O.FUAOrj*VB@42Wk#=!JQm>rrLY@rVlnMHN*pI`0)9pTDnikCp<p=&\DKOs$g8gs0HG>
->J^=[s/L,:rrJj?rr2s=qYpO9li."Q0`:qO+T;<>!Nj`DrrEjhrr3"*^@2(&\mkX<$hSPIs8TTP
-mJm3cF8Gn<b'_b:rrHE?qu6[sa8>l;:!eIkJcC<$i;\<~>
-!$1t:!V[r=rrJ%?o`"qMk5PA]KpVf=!S'a9rrg)o++'CSrrHr?rr3\E@K6?sf)Pd*])Q!NruM->
-ErZ0&rcJ66HN*pE*W#d9*UE_+m4eJ<!1Ee.!5nR2!$2%<!FEM/rrK]?r;QrH:B1>t_uBZ:WH7t:
-!mH/@qYpS%^\e$3<QG":!DUpls+13$s5<p-~>
-!$1t:!V[r=rrJ%?o`"qMk5PA]KpVf=!S'a9rrg)o++'CSrrHr?rr3\E@K6?sf)Pd*])Q!NruM->
-ErZ0&rcJ66HN*pE*W#d9*UE_+m4eJ<!1Ee.!5nR2!$2%<!FEM/rrK]?r;QrH:B1>t_uBZ:WH7t:
-!mH/@qYpS%^\e$3<QG":!DUpls+13$s5<p-~>
-!$1t:!V[r=rrJ%?o`"qMk5PA]KpVf=!S'a9rrg)o++'CSrrHr?rr3\E@K6?sf)Pd*])Q!NruM->
-ErZ0&rcJ66HN*pE*W#d9*UE_+m4eJ<!1Ee.!5nR2!$2%<!FEM/rrK]?r;QrH:B1>t_uBZ:WH7t:
-!mH/@qYpS%^\e$3<QG":!DUpls+13$s5<p-~>
-!$1t:!V[r>rrRqDqt0mh2Wk#=!JQm>[email protected]/lXCp<p="hS4Cs$kT;rs;oGruM->ErV/d
-rhfd3HN*pE*W#d9*UE_+m4eG;!3#mp!IDl_rr=)<rrHH?n,EF"@f660mZ3s:MsC<A!NC/<rrU\m
-deWnD?,-::!EI2;rrGj@JcC<$JcF[.J,~>
-!$1t:!V[r>rrRqDqt0mh2Wk#=!JQm>[email protected]/lXCp<p="hS4Cs$kT;rs;oGruM->ErV/d
-rhfd3HN*pE*W#d9*UE_+m4eG;!3#mp!IDl_rr=)<rrHH?n,EF"@f660mZ3s:MsC<A!NC/<rrU\m
-deWnD?,-::!EI2;rrGj@JcC<$JcF[.J,~>
-!$1t:!V[r>rrRqDqt0mh2Wk#=!JQm>[email protected]/lXCp<p="hS4Cs$kT;rs;oGruM->ErV/d
-rhfd3HN*pE*W#d9*UE_+m4eG;!3#mp!IDl_rr=)<rrHH?n,EF"@f660mZ3s:MsC<A!NC/<rrU\m
-deWnD?,-::!EI2;rrGj@JcC<$JcF[.J,~>
-"!.ESQ2^dapEon>!f3H@oD\hLk5PA]KpVf=!S'a>rrH1pr;QdYir/lXCp<p="hS4Cs$kT;rsi8L
-ruM->ErT(\s8U@jHN*pI*WQ/ET`4rl\mt+,#jj>Gs-L=s;uQ^q!H5+8rr=)<rrHH?pAY3*40AJ]
-!P2b;rr]NAGGY9<!pbT@rr3&[?J>5T!h#5@qu6[sa8Gr=N19J;rrRp:iIV#[s+146s*t~>
-"!.ESQ2^dapEon>!f3H@oD\hLk5PA]KpVf=!S'a>rrH1pr;QdYir/lXCp<p="hS4Cs$kT;rsi8L
-ruM->ErT(\s8U@jHN*pI*WQ/ET`4rl\mt+,#jj>Gs-L=s;uQ^q!H5+8rr=)<rrHH?pAY3*40AJ]
-!P2b;rr]NAGGY9<!pbT@rr3&[?J>5T!h#5@qu6[sa8Gr=N19J;rrRp:iIV#[s+146s*t~>
-"!.ESQ2^dapEon>!f3H@oD\hLk5PA]KpVf=!S'a>rrH1pr;QdYir/lXCp<p="hS4Cs$kT;rsi8L
-ruM->ErT(\s8U@jHN*pI*WQ/ET`4rl\mt+,#jj>Gs-L=s;uQ^q!H5+8rr=)<rrHH?pAY3*40AJ]
-!P2b;rr]NAGGY9<!pbT@rr3&[?J>5T!h#5@qu6[sa8Gr=N19J;rrRp:iIV#[s+146s*t~>
-"!.F7L]/>!-0>1,OHfM3rs4/s"rf])s7("+*W\IhrVlmE(B"153o^,<!Ua.j*X,I.s8PR>r;R72
-_>iTrWcJ.*7ii^IO*^g*"P=b:D".H(!U2E,rs8S9*Zg(.s5bC&*W_5mq#:=7rVln)^@qR.V#ZbY
-rrK]?qu6clO,!<)rrJ(@rr3#R#lG_DoD\ajH_UB:!EI2<rrHl?qu6_+!8)l&JcC<$kl6/~>
-"!.F7L]/>!-0>1,OHfM3rs4/s"rf])s7("+*W\IhrVlmE(B"153o^,<!Ua.j*X,I.s8PR>r;R72
-_>iTrWcJ.*7ii^IO*^g*"P=b:D".H(!U2E,rs8S9*Zg(.s5bC&*W_5mq#:=7rVln)^@qR.V#ZbY
-rrK]?qu6clO,!<)rrJ(@rr3#R#lG_DoD\ajH_UB:!EI2<rrHl?qu6_+!8)l&JcC<$kl6/~>
-"!.F7L]/>!-0>1,OHfM3rs4/s"rf])s7("+*W\IhrVlmE(B"153o^,<!Ua.j*X,I.s8PR>r;R72
-_>iTrWcJ.*7ii^IO*^g*"P=b:D".H(!U2E,rs8S9*Zg(.s5bC&*W_5mq#:=7rVln)^@qR.V#ZbY
-rrK]?qu6clO,!<)rrJ(@rr3#R#lG_DoD\ajH_UB:!EI2<rrHl?qu6_+!8)l&JcC<$kl6/~>
-!Zh=@r;Q]q!<2Qhr;Q]qr;QWos8Mrr!<2lqr;Q]q!<2lq!<2or!ri6"rVcitrr)lrrqucsct2PC
-rr2osr;QHj!<2rs!<2Wj!Jcp<rrg.X*?E#qs8W)qrrW1K\,QF)rr2ouUltZU"L]<k*6nH^!L9>S
-s8W(Ls+13$s6'E4~>
-!Zh=@r;Q]q!<2Qhr;Q]qr;QWos8Mrr!<2lqr;Q]q!<2lq!<2or!ri6"rVcitrr)lrrqucsct2PC
-rr2osr;QHj!<2rs!<2Wj!Jcp<rrg.X*?E#qs8W)qrrW1K\,QF)rr2ouUltZU"L]<k*6nH^!L9>S
-s8W(Ls+13$s6'E4~>
-!Zh=@r;Q]q!<2Qhr;Q]qr;QWos8Mrr!<2lqr;Q]q!<2lq!<2or!ri6"rVcitrr)lrrqucsct2PC
-rr2osr;QHj!<2rs!<2Wj!Jcp<rrg.X*?E#qs8W)qrrW1K\,QF)rr2ouUltZU"L]<k*6nH^!L9>S
-s8W(Ls+13$s6'E4~>
-!$-XjhZ!ZCKWKb)JcC<$W;hA~>
-!$-XjhZ!ZCKWKb)JcC<$W;hA~>
-!$-XjhZ!ZCKWKb)JcC<$W;hA~>
-!$-XjhZ!VoV1JYts+13Js*t~>
-!$-XjhZ!VoV1JYts+13Js*t~>
-!$-XjhZ!VoV1JYts+13Js*t~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-"!.EGKDtlRpH/DRMZ3VYn3d5LP5bIij\#Q@RfEEb3<0$WGlA]a6N$i_#?;`a8,rVfC&T+R;>l(G
-JcC<$JcE=]J,~>
-"!.EGKDtlRpH/DRMZ3VYn3d5LP5bIij\#Q@RfEEb3<0$WGlA]a6N$i_#?;`a8,rVfC&T+R;>l(G
-JcC<$JcE=]J,~>
-"!.EGKDtlRpH/DRMZ3VYn3d5LP5bIij\#Q@RfEEb3<0$WGlA]a6N$i_#?;`a8,rVfC&T+R;>l(G
-JcC<$JcE=]J,~>
-"!.E>FoMCDpEop4IfB?]mOnO*LB%;6+?0=\NrT.U,6.[sQi@!ceO]]>#=@//1]RJsW;cet`*%Zk
-JcC<$JcE=]J,~>
-"!.E>FoMCDpEop4IfB?]mOnO*LB%;6+?0=\NrT.U,6.[sQi@!ceO]]>#=@//1]RJsW;cet`*%Zk
-JcC<$JcE=]J,~>
-"!.E>FoMCDpEop4IfB?]mOnO*LB%;6+?0=\NrT.U,6.[sQi@!ceO]]>#=@//1]RJsW;cet`*%Zk
-JcC<$JcE=]J,~>
-"!.E>FoMCDpEop4?iIkU[4_KFLB#Xsj#@!uNrT.U,6.[sQi@!ceO]]>#=@//1]RJsJ,[7bPZ`S;
-JcC<$JcE=]J,~>
-"!.E>FoMCDpEop4?iIkU[4_KFLB#Xsj#@!uNrT.U,6.[sQi@!ceO]]>#=@//1]RJsJ,[7bPZ`S;
-JcC<$JcE=]J,~>
-"!.E>FoMCDpEop4?iIkU[4_KFLB#Xsj#@!uNrT.U,6.[sQi@!ceO]]>#=@//1]RJsJ,[7bPZ`S;
-JcC<$JcE=]J,~>
-"!.E>FoMCDpEop4/cJof<%e3:L;e#pju<=#NrT.U,6.[sQi@!neO]`?`bsu(1]RJs4T/F`P_&jc
-s+13$s185\~>
-"!.E>FoMCDpEop4/cJof<%e3:L;e#pju<=#NrT.U,6.[sQi@!neO]`?`bsu(1]RJs4T/F`P_&jc
-s+13$s185\~>
-"!.E>FoMCDpEop4/cJof<%e3:L;e#pju<=#NrT.U,6.[sQi@!neO]`?`bsu(1]RJs4T/F`P_&jc
-s+13$s185\~>
-"!.E>FoMCDN@+[tIfB?]mOnO*7t'7oju<=#NrT.U,6.[sQi@!neO]`?@(lU)1]RJsW.Fu"s+13$
-s0_lW~>
-"!.E>FoMCDN@+[tIfB?]mOnO*7t'7oju<=#NrT.U,6.[sQi@!neO]`?@(lU)1]RJsW.Fu"s+13$
-s0_lW~>
-"!.E>FoMCDN@+[tIfB?]mOnO*7t'7oju<=#NrT.U,6.[sQi@!neO]`?@(lU)1]RJsW.Fu"s+13$
-s0_lW~>
-"!.EqJc9EfN$eRsIfB?]mOnO*:>,[Iju<=#4DS_H%ZgY6I/[miEjG_O-r4Vf'q,*@H2_RZNIh+\
-s+13$s1//[~>
-"!.EqJc9EfN$eRsIfB?]mOnO*:>,[Iju<=#4DS_H%ZgY6I/[miEjG_O-r4Vf'q,*@H2_RZNIh+\
-s+13$s1//[~>
-"!.EqJc9EfN$eRsIfB?]mOnO*:>,[Iju<=#4DS_H%ZgY6I/[miEjG_O-r4Vf'q,*@H2_RZNIh+\
-s+13$s1//[~>
-"!.FAg]"G\n0\1pfDbdQqS3'fgA_*SpVQscrNQKo5QCc2rilU?s8RLcrilTd;#gR>rilI@JcC<$
-JcC<$\c70~>
-"!.FAg]"G\n0\1pfDbdQqS3'fgA_*SpVQscrNQKo5QCc2rilU?s8RLcrilTd;#gR>rilI@JcC<$
-JcC<$\c70~>
-"!.FAg]"G\n0\1pfDbdQqS3'fgA_*SpVQscrNQKo5QCc2rilU?s8RLcrilTd;#gR>rilI@JcC<$
-JcC<$\c70~>
-"!.EOOT,7\pEo5+!Sp!8rrM9Lrr3#lh1>TWs+13$s0DZT~>
-"!.EOOT,7\pEo5+!Sp!8rrM9Lrr3#lh1>TWs+13$s0DZT~>
-"!.EOOT,7\pEo5+!Sp!8rrM9Lrr3#lh1>TWs+13$s0DZT~>
-"!.F:N;agu,jt!u!W;G<s+13$s+13Es*t~>
-"!.F:N;agu,jt!u!W;G<s+13$s+13Es*t~>
-"!.F:N;agu,jt!u!W;G<s+13$s+13Es*t~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$1k7!Tr3hs+13$s+130s*t~>
-!$1k7!Tr3hs+13$s+130s*t~>
-!$1k7!Tr3hs+13$s+130s*t~>
-!$1k7!M=gls+13$s+130s*t~>
-!$1k7!M=gls+13$s+130s*t~>
-!$1k7!M=gls+13$s+130s*t~>
-!$1k7!M=gls+13$s+130s*t~>
-!$1k7!M=gls+13$s+130s*t~>
-!$1k7!M=gls+13$s+130s*t~>
-"!.FA\c-1DC]FF,IfB?Jon%bpJcC<$JcCo5J,~>
-"!.FA\c-1DC]FF,IfB?Jon%bpJcC<$JcCo5J,~>
-"!.FA\c-1DC]FF,IfB?Jon%bpJcC<$JcCo5J,~>
-"!.EWHiCG[ZXj*.IfKF\:r@kaJcC<$JcCo5J,~>
-"!.EWHiCG[ZXj*.IfKF\:r@kaJcC<$JcCo5J,~>
-"!.EWHiCG[ZXj*.IfKF\:r@kaJcC<$JcCo5J,~>
-"!.E>FoMCHpEop4IX`oYeq*jPs+13$s,m>3~>
-"!.E>FoMCHpEop4IX`oYeq*jPs+13$s,m>3~>
-"!.E>FoMCHpEop4IX`oYeq*jPs+13$s,m>3~>
-"!.E>FoMCGpEop4%[H&MJcC<$JcC<$OT0h~>
-"!.E>FoMCGpEop4%[H&MJcC<$JcC<$OT0h~>
-"!.E>FoMCGpEop4%[H&MJcC<$JcC<$OT0h~>
-"!.E>FoMCHpEop4IXWfXf7EsQs+13$s,m>3~>
-"!.E>FoMCHpEop4IXWfXf7EsQs+13$s,m>3~>
-"!.E>FoMCHpEop4IXWfXf7EsQs+13$s,m>3~>
-"!.EWHiCJ\Zt'-.IfKF[:W.haJcC<$JcCo5J,~>
-"!.EWHiCJ\Zt'-.IfKF[:W.haJcC<$JcCo5J,~>
-"!.EWHiCJ\Zt'-.IfKF[:W.haJcC<$JcCo5J,~>
-"!.FA\Gg"ACB+=QZ2Xb(omhVnJcC<$JcCo5J,~>
-"!.FA\Gg"ACB+=QZ2Xb(omhVnJcC<$JcCo5J,~>
-"!.FA\Gg"ACB+=QZ2Xb(omhVnJcC<$JcCo5J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$1>(!5\M"!/Q4+JcC<$JcDMFJ,~>
-!$1>(!5\M"!/Q4+JcC<$JcDMFJ,~>
-!$1>(!5\M"!/Q4+JcC<$JcDMFJ,~>
-!$1>(!NTW+fE%aPJcC<$JcC<$U]5i~>
-!$1>(!NTW+fE%aPJcC<$JcC<$U]5i~>
-!$1>(!NTW+fE%aPJcC<$JcC<$U]5i~>
-!$2";!UqE7rrS:OqY'piX)\(;!Phrks+13$s+13Fs*t~>
-!$2";!UqE7rrS:OqY'piX)\(;!Phrks+13$s+13Fs*t~>
-!$2";!UqE7rrS:OqY'piX)\(;!Phrks+13$s+13Fs*t~>
-!$2%<!o?]]rVlqMU\FN_!NU5<rrKo?JcC<$JcC<$U]5i~>
-!$2%<!o?]]rVlqMU\FN_!NU5<rrKo?JcC<$JcC<$U]5i~>
-!$2%<!o?]]rVlqMU\FN_!NU5<rrKo?JcC<$JcC<$U]5i~>
-!$2(="4M)A*W5p=I\Q`7rrK0?r;Qf5>_2p+s+13$s.fUE~>
-!$2(="4M)A*W5p=I\Q`7rrK0?r;Qf5>_2p+s+13$s.fUE~>
-!$2(="4M)A*W5p=I\Q`7rrK0?r;Qf5>_2p+s+13$s.fUE~>
-#9EjEc[=+>*W,j;JsuK7!NU5<rrKo?JcC<$JcC<$U]5i~>
-#9EjEc[=+>*W,j;JsuK7!NU5<rrKo?JcC<$JcC<$U]5i~>
-#9EjEc[=+>*W,j;JsuK7!NU5<rrKo?JcC<$JcC<$U]5i~>
-#9EilJ'@rm*W#d9*VfX8X)\(;!Phrks+13$s+13Fs*t~>
-#9EilJ'@rm*W#d9*VfX8X)\(;!Phrks+13$s+13Fs*t~>
-#9EilJ'@rm*W#d9*VfX8X)\(;!Phrks+13$s+13Fs*t~>
-"!.E>.fNZR$Zu=H!Ht@8rrK0?r;Qf5>_2p+s+13$s.fUE~>
-"!.E>.fNZR$Zu=H!Ht@8rrK0?r;Qf5>_2p+s+13$s.fUE~>
-"!.E>.fNZR$Zu=H!Ht@8rrK0?r;Qf5>_2p+s+13$s.fUE~>
-!Zh=*rP/FLao25@FK>?7rrK0?r;Qf5>_2p+s+13$s.fUE~>
-!Zh=*rP/FLao25@FK>?7rrK0?r;Qf5>_2p+s+13$s.fUE~>
-!Zh=*rP/FLao25@FK>?7rrK0?r;Qf5>_2p+s+13$s.fUE~>
-!$1t:!$2%<!dUd@p\t8pEW#h;^g)HjJcC<$JcDMFJ,~>
-!$1t:!$2%<!dUd@p\t8pEW#h;^g)HjJcC<$JcDMFJ,~>
-!$1t:!$2%<!dUd@p\t8pEW#h;^g)HjJcC<$JcDMFJ,~>
-!$1t:!&FQR!g'2VpAY/oEW#h;^g)HjJcC<$JcDMFJ,~>
-!$1t:!&FQR!g'2VpAY/oEW#h;^g)HjJcC<$JcDMFJ,~>
-!$1t:!&FQR!g'2VpAY/oEW#h;^g)HjJcC<$JcDMFJ,~>
-!$1>(!NU5<rrKo?JcC<$JcC<$U]5i~>
-!$1>(!NU5<rrKo?JcC<$JcC<$U]5i~>
-!$1>(!NU5<rrKo?JcC<$JcC<$U]5i~>
-!$1>(!NSrmXT=#YJcC<$JcC<$U]5i~>
-!$1>(!NSrmXT=#YJcC<$JcC<$U]5i~>
-!$1>(!NSrmXT=#YJcC<$JcC<$U]5i~>
-!$1>(!71L[!2G,FJcC<$JcDMFJ,~>
-!$1>(!71L[!2G,FJcC<$JcDMFJ,~>
-!$1>(!71L[!2G,FJcC<$JcDMFJ,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-!$-XjJcC<$JcC?%J,~>
-%%EndData
-showpage
-%%Trailer
-end
-%%EOF
diff --git a/lib/stdlib/doc/src/ushell2.gif b/lib/stdlib/doc/src/ushell2.gif
deleted file mode 100644
index 273cf2078a..0000000000
--- a/lib/stdlib/doc/src/ushell2.gif
+++ /dev/null
Binary files differ
diff --git a/lib/stdlib/doc/src/ushell2.ps b/lib/stdlib/doc/src/ushell2.ps
deleted file mode 100644
index e6db3c2be2..0000000000
--- a/lib/stdlib/doc/src/ushell2.ps
+++ /dev/null
@@ -1,404 +0,0 @@
-%!PS-Adobe-3.0
-%%Creator: GIMP PostScript file plugin V 1.17 by Peter Kirchgessner
-%%Title: ushell2.ps
-%%CreationDate: Mon Mar 16 09:52:14 2009
-%%DocumentData: Clean7Bit
-%%LanguageLevel: 2
-%%Pages: 1
-%%BoundingBox: 14 14 468 74
-%%EndComments
-%%BeginProlog
-% Use own dictionary to avoid conflicts
-10 dict begin
-%%EndProlog
-%%Page: 1 1
-% Translate for offset
-14.173228346456694 14.173228346456694 translate
-% Translate to begin of first scanline
-0 59.527559055118118 translate
-453.54330708661422 -59.527559055118118 scale
-% Image geometry
-640 84 8
-% Transformation matrix
-[ 640 0 0 84 0 0 ]
-% Strings to hold RGB-samples per scanline
-/rstr 640 string def
-/gstr 640 string def
-/bstr 640 string def
-{currentfile /ASCII85Decode filter /RunLengthDecode filter rstr readstring pop}
-{currentfile /ASCII85Decode filter /RunLengthDecode filter gstr readstring pop}
-{currentfile /ASCII85Decode filter /RunLengthDecode filter bstr readstring pop}
-true 3
-%%BeginData: 18704 ASCII Bytes
-colorimage
-J`MCCJ`MCCJ`M=~>
-J`MCCJ`MCCJ`M=~>
-J`MCCJ`MCCJ`M=~>
-!)nG7JO+iQJO+lRJ,~>
-!)nG7JO+iQJO+lRJ,~>
-!)nG7JO+iQJO+lRJ,~>
-!D=/Y=b0_,=b0_.=b$~>
-!D=/Y=b0_,=b0_.=b$~>
-!D=/Y=b0_,=b0_.=b$~>
-!D>M*s+13$s+13&s*t~>
-!D>M*s+13$s+13&s*t~>
-!D>M*s+13$s+13&s*t~>
-!D>M>rrKOJQ2^i3JcC<$JcFU,J,~>
-!D>M>rrKOJQ2^i3JcC<$JcFU,J,~>
-!D>M>rrKOJQ2^i3JcC<$JcFU,J,~>
-"%t`UXo(ooJ,'$ES](+!!dROKr;Qh<:2'>#!I!f.rr]@2s).a0!581-!+,Ru!6ss3!kNp@p\t9X
-H-uWnOA=s3rrK.0nG`Jtr;QbGr_<HIrVlo/@tFZ2s+14-s*t~>
-"%t`UXo(ooJ,'$ES](+!!dROKr;Qh<:2'>#!I!f.rr]@2s).a0!581-!+,Ru!6ss3!kNp@p\t9X
-H-uWnOA=s3rrK.0nG`Jtr;QbGr_<HIrVlo/@tFZ2s+14-s*t~>
-"%t`UXo(ooJ,'$ES](+!!dROKr;Qh<:2'>#!I!f.rr]@2s).a0!581-!+,Ru!6ss3!kNp@p\t9X
-H-uWnOA=s3rrK.0nG`Jtr;QbGr_<HIrVlo/@tFZ2s+14-s*t~>
-"A:iVM0T!F!8RAL!HP42rrUp#*W5p=fZ5E4rrG1@rr3+VQ2`JQrQbK2q#:?CrQbLB\*j7sS:L>5
-!T-**rrJOjli.%FchRG<!HlihrrIe?nc&Yn]MJP,!J%!FcN0Vnrr3#u++aHCs+14-s*t~>
-"A:iVM0T!F!8RAL!HP42rrUp#*W5p=fZ5E4rrG1@rr3+VQ2`JQrQbK2q#:?CrQbLB\*j7sS:L>5
-!T-**rrJOjli.%FchRG<!HlihrrIe?nc&Yn]MJP,!J%!FcN0Vnrr3#u++aHCs+14-s*t~>
-"A:iVM0T!F!8RAL!HP42rrUp#*W5p=fZ5E4rrG1@rr3+VQ2`JQrQbK2q#:?CrQbLB\*j7sS:L>5
-!T-**rrJOjli.%FchRG<!HlihrrIe?nc&Yn]MJP,!J%!FcN0Vnrr3#u++aHCs+14-s*t~>
-"A:iVM3Rl:!;QQc#kn;uEiSm+o_8@b!;HKb!;HEk!$;%;!V[r6rs;$Hs8VtORf>#.rq$3eoD\lN
-QMU(M!l&CArVllrrU^6ks5-$;rq$<ks8W#qoFLs$rV6Ego^r._r:GB$o^qnX!W2]krs&2ss8)B[
-rVlg+qXdY#o_/=bKUDN+o_/%Z$2J#IrQSC<s'rY>rrMrfrVloH97d+os+14.s*t~>
-"A:iVM3Rl:!;QQc#kn;uEiSm+o_8@b!;HKb!;HEk!$;%;!V[r6rs;$Hs8VtORf>#.rq$3eoD\lN
-QMU(M!l&CArVllrrU^6ks5-$;rq$<ks8W#qoFLs$rV6Ego^r._r:GB$o^qnX!W2]krs&2ss8)B[
-rVlg+qXdY#o_/=bKUDN+o_/%Z$2J#IrQSC<s'rY>rrMrfrVloH97d+os+14.s*t~>
-"A:iVM3Rl:!;QQc#kn;uEiSm+o_8@b!;HKb!;HEk!$;%;!V[r6rs;$Hs8VtORf>#.rq$3eoD\lN
-QMU(M!l&CArVllrrU^6ks5-$;rq$<ks8W#qoFLs$rV6Ego^r._r:GB$o^qnX!W2]krs&2ss8)B[
-rVlg+qXdY#o_/=bKUDN+o_/%Z$2J#IrQSC<s'rY>rrMrfrVloH97d+os+14.s*t~>
-#>7/YM1+"-g&D!PlY?DI$r#a^Efa$E-rf=.k?@j5!F+acrr=,;rrMj@o`#1<s8Sp?s8O^er[7f!
-nFce_n:I\.rrK]@r;QcHr?qfQqqj5tr[7lYrV35f-kO2nd:1^L-n*1:a!_W=-n+farrK!?rr35q
-.KBF2-jn]-rsk_N%3R&fs8Rb&=stdhj7rWTKtI?a"NGcfA%MI8!T6+ls+13$s5<p-~>
-#>7/YM1+"-g&D!PlY?DI$r#a^Efa$E-rf=.k?@j5!F+acrr=,;rrMj@o`#1<s8Sp?s8O^er[7f!
-nFce_n:I\.rrK]@r;QcHr?qfQqqj5tr[7lYrV35f-kO2nd:1^L-n*1:a!_W=-n+farrK!?rr35q
-.KBF2-jn]-rsk_N%3R&fs8Rb&=stdhj7rWTKtI?a"NGcfA%MI8!T6+ls+13$s5<p-~>
-#>7/YM1+"-g&D!PlY?DI$r#a^Efa$E-rf=.k?@j5!F+acrr=,;rrMj@o`#1<s8Sp?s8O^er[7f!
-nFce_n:I\.rrK]@r;QcHr?qfQqqj5tr[7lYrV35f-kO2nd:1^L-n*1:a!_W=-n+farrK!?rr35q
-.KBF2-jn]-rsk_N%3R&fs8Rb&=stdhj7rWTKtI?a"NGcfA%MI8!T6+ls+13$s5<p-~>
-#>7/YM'th_:B(7oI@pN=%,$43E\H&Es1hq?B=@g>!OQP=rr=,;rrMj@o`#1<s8Sp?s8Qb.rr3"H
-U%SEdTmZ8-!P2e8rrb=Bh[t\DrrXkBdn0N<#U90GWZSDDruV1>!J6g6rs#?Ds3C3-.K08I6JDA;
-!EI5>rs"-+b5_Li9CVrcA%M^:\[g>jrrM%@JcC<$JcF[.J,~>
-#>7/YM'th_:B(7oI@pN=%,$43E\H&Es1hq?B=@g>!OQP=rr=,;rrMj@o`#1<s8Sp?s8Qb.rr3"H
-U%SEdTmZ8-!P2e8rrb=Bh[t\DrrXkBdn0N<#U90GWZSDDruV1>!J6g6rs#?Ds3C3-.K08I6JDA;
-!EI5>rs"-+b5_Li9CVrcA%M^:\[g>jrrM%@JcC<$JcF[.J,~>
-#>7/YM'th_:B(7oI@pN=%,$43E\H&Es1hq?B=@g>!OQP=rr=,;rrMj@o`#1<s8Sp?s8Qb.rr3"H
-U%SEdTmZ8-!P2e8rrb=Bh[t\DrrXkBdn0N<#U90GWZSDDruV1>!J6g6rs#?Ds3C3-.K08I6JDA;
-!EI5>rs"-+b5_Li9CVrcA%M^:\[g>jrrM%@JcC<$JcF[.J,~>
-"A:iVM3Ro;!O.@YS-6(rs)P.=rrg,Cs(?9LS,mM@rVlj=qu6]k-M7<@Ff=JZXSVqtI%g96!p59A
-mJd3uA,Q?,li!=U,37WFrVlsOnC'u-rraPCs/L,=rr@3@rrI_@p&>5nG5k:_oI9b=!CGN<rrH-@
-rVlnOR/[*ddS'*0#%P7Eo4(4*rVloV4b<Was+14.s*t~>
-"A:iVM3Ro;!O.@YS-6(rs)P.=rrg,Cs(?9LS,mM@rVlj=qu6]k-M7<@Ff=JZXSVqtI%g96!p59A
-mJd3uA,Q?,li!=U,37WFrVlsOnC'u-rraPCs/L,=rr@3@rrI_@p&>5nG5k:_oI9b=!CGN<rrH-@
-rVlnOR/[*ddS'*0#%P7Eo4(4*rVloV4b<Was+14.s*t~>
-"A:iVM3Ro;!O.@YS-6(rs)P.=rrg,Cs(?9LS,mM@rVlj=qu6]k-M7<@Ff=JZXSVqtI%g96!p59A
-mJd3uA,Q?,li!=U,37WFrVlsOnC'u-rraPCs/L,=rr@3@rrI_@p&>5nG5k:_oI9b=!CGN<rrH-@
-rVlnOR/[*ddS'*0#%P7Eo4(4*rVloV4b<Was+14.s*t~>
-"A:iVM3Rl:!3lHR"G!EMEiT-="Me=CB3bCd!4;_)!$;%;!V[r5rr`6B_HQd8!IUU7rrKi?m/I*t
-A,ZE.oRHgj"!$CZ55kK`/b%4\rVm!Equ<[:qYpSJS+ZdcVKVu6ch&[brrGI?r;QdtaSl,>KUDc=
-!RaX1rrHW@rr3#'D#OA7i&uYkJcC<$i;\<~>
-"A:iVM3Rl:!3lHR"G!EMEiT-="Me=CB3bCd!4;_)!$;%;!V[r5rr`6B_HQd8!IUU7rrKi?m/I*t
-A,ZE.oRHgj"!$CZ55kK`/b%4\rVm!Equ<[:qYpSJS+ZdcVKVu6ch&[brrGI?r;QdtaSl,>KUDc=
-!RaX1rrHW@rr3#'D#OA7i&uYkJcC<$i;\<~>
-"A:iVM3Rl:!3lHR"G!EMEiT-="Me=CB3bCd!4;_)!$;%;!V[r5rr`6B_HQd8!IUU7rrKi?m/I*t
-A,ZE.oRHgj"!$CZ55kK`/b%4\rVm!Equ<[:qYpSJS+ZdcVKVu6ch&[brrGI?r;QdtaSl,>KUDc=
-!RaX1rrHW@rr3#'D#OA7i&uYkJcC<$i;\<~>
-"A:iVM3Ro;!O'H@rrgMCs)P.=rrg,Cs(Ae8rr=,;rrMj@o`#$m+2V4\rrITArr3"HU&P&nhI3>\
-rrVG(_X.:(jbC#[email protected]($+6gRf<<h/b%4\rVm!Equ<[:qYp\MS,`M0q#:PqG,("4kUHK1
-!CGN<rs)QFs6>F?KUDc=!RaX1rrHW@rr3#'D#XG9rb/]js+13$s5<p-~>
-"A:iVM3Ro;!O'H@rrgMCs)P.=rrg,Cs(Ae8rr=,;rrMj@o`#$m+2V4\rrITArr3"HU&P&nhI3>\
-rrVG(_X.:(jbC#[email protected]($+6gRf<<h/b%4\rVm!Equ<[:qYp\MS,`M0q#:PqG,("4kUHK1
-!CGN<rs)QFs6>F?KUDc=!RaX1rrHW@rr3#'D#XG9rb/]js+13$s5<p-~>
-"A:iVM3Ro;!O'H@rrgMCs)P.=rrg,Cs(Ae8rr=,;rrMj@o`#$m+2V4\rrITArr3"HU&P&nhI3>\
-rrVG(_X.:(jbC#[email protected]($+6gRf<<h/b%4\rVm!Equ<[:qYp\MS,`M0q#:PqG,("4kUHK1
-!CGN<rs)QFs6>F?KUDc=!RaX1rrHW@rr3#'D#XG9rb/]js+13$s5<p-~>
-"A:iVM'r6h"*ae$E;i3->g`RlWrE#$^g6ulB)Y-tHN*pIEC1"!K)YcQBL)tmMtR)OX^KZXrrL26
-r^-^;k5PA^aT->ZrrLn@lMgmTJH#QLq5"!W"d:Fsh_(&j6Na7`oU5YF"K_V&WcIt9"Q"/d6bE-n%
-HrJ%lcE#Es8S]Y&NNhhrs-tX6W;80KUDc=!RaX1rrL\]r^-^"f`(mOrZ?^kJcC<$huA3~>
-"A:iVM'r6h"*ae$E;i3->g`RlWrE#$^g6ulB)Y-tHN*pIEC1"!K)YcQBL)tmMtR)OX^KZXrrL26
-r^-^;k5PA^aT->ZrrLn@lMgmTJH#QLq5"!W"d:Fsh_(&j6Na7`oU5YF"K_V&WcIt9"Q"/d6bE-n%
-HrJ%lcE#Es8S]Y&NNhhrs-tX6W;80KUDc=!RaX1rrL\]r^-^"f`(mOrZ?^kJcC<$huA3~>
-"A:iVM'r6h"*ae$E;i3->g`RlWrE#$^g6ulB)Y-tHN*pIEC1"!K)YcQBL)tmMtR)OX^KZXrrL26
-r^-^;k5PA^aT->ZrrLn@lMgmTJH#QLq5"!W"d:Fsh_(&j6Na7`oU5YF"K_V&WcIt9"Q"/d6bE-n%
-HrJ%lcE#Es8S]Y&NNhhrs-tX6W;80KUDc=!RaX1rrL\]r^-^"f`(mOrZ?^kJcC<$huA3~>
-"%t`Umek`?jT#8Drn.G5s8VBUrr3,pkPtSCrn.;4rr2uYrn.;6rr2uWrn.;8p&>'hir&fVh#>t,
-mJ[%dpY"j1rrMuVlMgqTJ#rYL!;QQH"nL[MqW%/Gf`V$Ls7H9C"T&/uoBQ/O"6eFkrV-<pmdL2U
-hZ!NTm/GZ<h>I9Win<2gs6]=TrrMoUn,ECKrn.;5rVlm_hLY]Xs+14-s*t~>
-"%t`Umek`?jT#8Drn.G5s8VBUrr3,pkPtSCrn.;4rr2uYrn.;6rr2uWrn.;8p&>'hir&fVh#>t,
-mJ[%dpY"j1rrMuVlMgqTJ#rYL!;QQH"nL[MqW%/Gf`V$Ls7H9C"T&/uoBQ/O"6eFkrV-<pmdL2U
-hZ!NTm/GZ<h>I9Win<2gs6]=TrrMoUn,ECKrn.;5rVlm_hLY]Xs+14-s*t~>
-"%t`Umek`?jT#8Drn.G5s8VBUrr3,pkPtSCrn.;4rr2uYrn.;6rr2uWrn.;8p&>'hir&fVh#>t,
-mJ[%dpY"j1rrMuVlMgqTJ#rYL!;QQH"nL[MqW%/Gf`V$Ls7H9C"T&/uoBQ/O"6eFkrV-<pmdL2U
-hZ!NTm/GZ<h>I9Win<2gs6]=TrrMoUn,ECKrn.;5rVlm_hLY]Xs+14-s*t~>
-!D>M>rrN#pQ2^jZJcC<$JcFU,J,~>
-!D>M>rrN#pQ2^jZJcC<$JcFU,J,~>
-!D>M>rrN#pQ2^jZJcC<$JcFU,J,~>
-!D>M*s+13$s+13&s*t~>
-!D>M*s+13$s+13&s*t~>
-!D>M*s+13$s+13&s*t~>
-!D>M*s+13$s+13&s*t~>
-!D>M*s+13$s+13&s*t~>
-!D>M*s+13$s+13&s*t~>
-!D>NQrrK"krVlo0ipm$KT)JZh\GVHh_h%i=s+13$s/Z0M~>
-!D>NQrrK"krVlo0ipm$KT)JZh\GVHh_h%i=s+13$s/Z0M~>
-!D>NQrrK"krVlo0ipm$KT)JZh\GVHh_h%i=s+13$s/Z0M~>
-!D>NRrrSc-mJ[%d[sIB4rsbK>%<J'>s0[duMN!M0JcC<$JcC<$X8d\~>
-!D>NRrrSc-mJ[%d[sIB4rsbK>%<J'>s0[duMN!M0JcC<$JcC<$X8d\~>
-!D>NRrrSc-mJ[%d[sIB4rsbK>%<J'>s0[duMN!M0JcC<$JcC<$X8d\~>
-#>7/Ys.,5omJQtc^LI'5rsP9XW>)=oS,`M<TDjEAJcC<$JcD\KJ,~>
-#>7/Ys.,5omJQtc^LI'5rsP9XW>)=oS,`M<TDjEAJcC<$JcD\KJ,~>
-#>7/Ys.,5omJQtc^LI'5rsP9XW>)=oS,`M<TDjEAJcC<$JcD\KJ,~>
-#>7/YUPnOomJHnacVF36$>6gI*rkZbs8P:?JcC<$JcC<$VuM8~>
-#>7/YUPnOomJHnacVF36$>6gI*rkZbs8P:?JcC<$JcC<$VuM8~>
-#>7/YUPnOomJHnacVF36$>6gI*rkZbs8P:?JcC<$JcC<$VuM8~>
-#>7/YoCW&:mJ?h`dn0<6"RfFC*W>s:"6h]b:P&Oss+13$s/H$K~>
-#>7/YoCW&:mJ?h`dn0<6"RfFC*W>s:"6h]b:P&Oss+13$s/H$K~>
-#>7/YoCW&:mJ?h`dn0<6"RfFC*W>s:"6h]b:P&Oss+13$s/H$K~>
-!D>NQrrFh@qYpTB:A+Vm7Kc'As8V@[j+75]s+13$s/5mI~>
-!D>NQrrFh@qYpTB:A+Vm7Kc'As8V@[j+75]s+13$s/5mI~>
-!D>NQrrFh@qYpTB:A+Vm7Kc'As8V@[j+75]s+13$s/5mI~>
-!D>NQrrFh@qu6]>;Y0nm*o1A]s'3Bks+13$s+13Is*t~>
-!D>NQrrFh@qu6]>;Y0nm*o1A]s'3Bks+13$s+13Is*t~>
-!D>NQrrFh@qu6]>;Y0nm*o1A]s'3Bks+13$s+13Is*t~>
-!D>NQrrFh@r;Qi1B);6$$-D</*riT\s'3D9rrW+o]Rg*6s+13$s0;TS~>
-!D>NQrrFh@r;Qi1B);6$$-D</*riT\s'3D9rrW+o]Rg*6s+13$s0;TS~>
-!D>NQrrFh@r;Qi1B);6$$-D</*riT\s'3D9rrW+o]Rg*6s+13$s0;TS~>
-%SJn`a+f<dI>4^#s0R1?p&>?%JqahkK!>9SKDpT*YPnJ&pAdU4s+13$s+13Ts*t~>
-%SJn`a+f<dI>4^#s0R1?p&>?%JqahkK!>9SKDpT*YPnJ&pAdU4s+13$s+13Ts*t~>
-%SJn`a+f<dI>4^#s0R1?p&>?%JqahkK!>9SKDpT*YPnJ&pAdU4s+13$s+13Ts*t~>
-"%t`UcMWt2ZiC'>jR`BS[$D;i[Jp1+Yl9phLTURU!rS@iJcC<$JcC<$Z2]=~>
-"%t`UcMWt2ZiC'>jR`BS[$D;i[Jp1+Yl9phLTURU!rS@iJcC<$JcC<$Z2]=~>
-"%t`UcMWt2ZiC'>jR`BS[$D;i[Jp1+Yl9phLTURU!rS@iJcC<$JcC<$Z2]=~>
-!D>N9rrBb-rrK*@JcC<$JcC<$X8d\~>
-!D>N9rrBb-rrK*@JcC<$JcC<$X8d\~>
-!D>N9rrBb-rrK*@JcC<$JcC<$X8d\~>
-!D>N1rr_L<A`eRDJcC<$JcDeNJ,~>
-!D>N1rr_L<A`eRDJcC<$JcDeNJ,~>
-!D>N1rr_L<A`eRDJcC<$JcDeNJ,~>
-!D>N1rrW/foR[$ns+13$s/Q*L~>
-!D>N1rrW/foR[$ns+13$s/Q*L~>
-!D>N1rrW/foR[$ns+13$s/Q*L~>
-"A:iVs5s=1"5EkUl29$2ir8rZh:1/0s+13$s+13<s*t~>
-"A:iVs5s=1"5EkUl29$2ir8rZh:1/0s+13$s+13<s*t~>
-"A:iVs5s=1"5EkUl29$2ir8rZh:1/0s+13$s+13<s*t~>
-"A:iVh08ih"Bs"KI4bCg#D<'VcX9:FaasJCs+13$s-it<~>
-"A:iVh08ih"Bs"KI4bCg#D<'VcX9:FaasJCs+13$s-it<~>
-"A:iVh08ih"Bs"KI4bCg#D<'VcX9:FaasJCs+13$s-it<~>
-"A:iVM3S#>"P-'BI@pE:#01rEs6Fa?JcC<$JcC<$S,\!~>
-"A:iVM3S#>"P-'BI@pE:#01rEs6Fa?JcC<$JcC<$S,\!~>
-"A:iVM3S#>"P-'BI@pE:#01rEs6Fa?JcC<$JcC<$S,\!~>
-"A:iVM3S#>&_9GOI@m*XH^=^3]7/f?mX;?ks+13$s+13>s*t~>
-"A:iVM3S#>&_9GOI@m*XH^=^3]7/f?mX;?ks+13$s+13>s*t~>
-"A:iVM3S#>&_9GOI@m*XH^=^3]7/f?mX;?ks+13$s+13>s*t~>
-(eZsjM3S&?peOBZI63OaTp(Z>s1iGWB=NNmJcC<$JcD2=J,~>
-(eZsjM3S&?peOBZI63OaTp(Z>s1iGWB=NNmJcC<$JcD2=J,~>
-(eZsjM3S&?peOBZI63OaTp(Z>s1iGWB=NNmJcC<$JcD2=J,~>
-(eZsjiH7CN7\E6NhS8LYs3"=?s+$lZY&JslJcC<$JcD2=J,~>
-(eZsjiH7CN7\E6NhS8LYs3"=?s+$lZY&JslJcC<$JcD2=J,~>
-(eZsjiH7CN7\E6NhS8LYs3"=?s+$lZY&JslJcC<$JcD2=J,~>
-#tmA[s6JIqhUY0nrs?kHs+R*>s8&m>JcC<$JcC<$S,\!~>
-#tmA[s6JIqhUY0nrs?kHs+R*>s8&m>JcC<$JcC<$S,\!~>
-#tmA[s6JIqhUY0nrs?kHs+R*>s8&m>JcC<$JcC<$S,\!~>
-!D>NPrrrDA=TA!drr3;J;ZD6@qZ$K"Ck;V;s+13$s-s%=~>
-!D>NPrrrDA=TA!drr3;J;ZD6@qZ$K"Ck;V;s+13$s-s%=~>
-!D>NPrrrDA=TA!drr3;J;ZD6@qZ$K"Ck;V;s+13$s-s%=~>
-$qi\^s5;#@_cHg;R?IQk#ci.Ks*1Qc[V16lJcC<$JcD2=J,~>
-$qi\^s5;#@_cHg;R?IQk#ci.Ks*1Qc[V16lJcC<$JcD2=J,~>
-$qi\^s5;#@_cHg;R?IQk#ci.Ks*1Qc[V16lJcC<$JcD2=J,~>
-#>7/Ys-lqc>Q+R%Pl=S]G5_FBGZ/@.s+13$s+13<s*t~>
-#>7/Ys-lqc>Q+R%Pl=S]G5_FBGZ/@.s+13$s+13<s*t~>
-#>7/Ys-lqc>Q+R%Pl=S]G5_FBGZ/@.s+13$s+13<s*t~>
-!D>M*s+13$s+13&s*t~>
-!D>M*s+13$s+13&s*t~>
-!D>M*s+13$s+13&s*t~>
-!D>M*s+13$s+13&s*t~>
-!D>M*s+13$s+13&s*t~>
-!D>M*s+13$s+13&s*t~>
-!D>M*s+13$s+13&s*t~>
-!D>M*s+13$s+13&s*t~>
-!D>M*s+13$s+13&s*t~>
-!D>M*s+13$s+13&s*t~>
-!D>M*s+13$s+13&s*t~>
-!D>M*s+13$s+13&s*t~>
-"A:iVrHeGa#!r.ds)PpSp&>3n>ok%TqqqDSq^h^f!K[21s+13$s+13ns*t~>
-"A:iVrHeGa#!r.ds)PpSp&>3n>ok%TqqqDSq^h^f!K[21s+13$s+13ns*t~>
-"A:iVrHeGa#!r.ds)PpSp&>3n>ok%TqqqDSq^h^f!K[21s+13$s+13ns*t~>
-"A:iVM3S#>!S0a>rrQHBrq??qB=@j?dS&Kt!DCl?rrJ._JcC<$JcC<$bQ!(~>
-"A:iVM3S#>!S0a>rrQHBrq??qB=@j?dS&Kt!DCl?rrJ._JcC<$JcC<$bQ!(~>
-"A:iVM3S#>!S0a>rrQHBrq??qB=@j?dS&Kt!DCl?rrJ._JcC<$JcC<$bQ!(~>
-#tmA[Vm$.#r2Oo<rrHB@pAYG3[f?BU9E5%Cn,E=fl.l:<!64s:$./AAbQ$Y^s8U+<^B!*hrr<u:
-^B!3krrBk6^B!;Fs+13$s+13us*t~>
-#tmA[Vm$.#r2Oo<rrHB@pAYG3[f?BU9E5%Cn,E=fl.l:<!64s:$./AAbQ$Y^s8U+<^B!*hrr<u:
-^B!3krrBk6^B!;Fs+13$s+13us*t~>
-#tmA[Vm$.#r2Oo<rrHB@pAYG3[f?BU9E5%Cn,E=fl.l:<!64s:$./AAbQ$Y^s8U+<^B!*hrr<u:
-^B!3krrBk6^B!;Fs+13$s+13us*t~>
-!D>NPrrJs@qu6\1[.jS,B60c?KhMIG>f$F>!NC2?rr=,<rs;-HlS8F'MZ:+rra#_Uf`/6dra#_X
-h#FNara#VXi.:oZs+13$s3q!u~>
-!D>NPrrJs@qu6\1[.jS,B60c?KhMIG>f$F>!NC2?rr=,<rs;-HlS8F'MZ:+rra#_Uf`/6dra#_X
-h#FNara#VXi.:oZs+13$s3q!u~>
-!D>NPrrJs@qu6\1[.jS,B60c?KhMIG>f$F>!NC2?rr=,<rs;-HlS8F'MZ:+rra#_Uf`/6dra#_X
-h#FNara#VXi.:oZs+13$s3q!u~>
-!D>NQrrJU@qYpT[2#%"[B39M-AkW1(X+Kg?jFOf>rr=,<rs;-HlS8F'MZ5`trr3+VQ2`K)rr3+K
-U&QA5rr3#-e:IXNs+13$s3q!u~>
-!D>NQrrJU@qYpT[2#%"[B39M-AkW1(X+Kg?jFOf>rr=,<rs;-HlS8F'MZ5`trr3+VQ2`K)rr3+K
-U&QA5rr3#-e:IXNs+13$s3q!u~>
-!D>NQrrJU@qYpT[2#%"[B39M-AkW1(X+Kg?jFOf>rr=,<rs;-HlS8F'MZ5`trr3+VQ2`K)rr3+K
-U&QA5rr3#-e:IXNs+13$s3q!u~>
-!D>NRrrSFGq"k!kDm&j7"_Y:Ds3aR>rrbOCs2S1=rr=,<rs;-HlS8F'MZ5`trr3+VQ2`K)rr34N
-U&V$'AnJ&os+13$s+13ts*t~>
-!D>NRrrSFGq"k!kDm&j7"_Y:Ds3aR>rrbOCs2S1=rr=,<rs;-HlS8F'MZ5`trr3+VQ2`K)rr34N
-U&V$'AnJ&os+13$s+13ts*t~>
-!D>NRrrSFGq"k!kDm&j7"_Y:Ds3aR>rrbOCs2S1=rr=,<rs;-HlS8F'MZ5`trr3+VQ2`K)rr34N
-U&V$'AnJ&os+13$s+13ts*t~>
-#"q&Xs-8l>qYpS+](Z.-B=@j?dS'T>"NXX/@F+oP!$;(<#t<M41&mGps#T3>rre+Bs"<a=rs*qF
-s'i@E\(?32JcC<$JcF-tJ,~>
-#"q&Xs-8l>qYpS+](Z.-B=@j?dS'T>"NXX/@F+oP!$;(<#t<M41&mGps#T3>rre+Bs"<a=rs*qF
-s'i@E\(?32JcC<$JcF-tJ,~>
-#"q&Xs-8l>qYpS+](Z.-B=@j?dS'T>"NXX/@F+oP!$;(<#t<M41&mGps#T3>rre+Bs"<a=rs*qF
-s'i@E\(?32JcC<$JcF-tJ,~>
-"\UrWSW34:rrQZBrq??qB=@j?dS'Q=![-G]r;R9Ks2TNes$bT+1&mGps#T3>rre+Bs"<a=rrd_C
-s!.@=rrItfrr3&A/!>J`JcC<$JcFF'J,~>
-"\UrWSW34:rrQZBrq??qB=@j?dS'Q=![-G]r;R9Ks2TNes$bT+1&mGps#T3>rre+Bs"<a=rrd_C
-s!.@=rrItfrr3&A/!>J`JcC<$JcFF'J,~>
-"\UrWSW34:rrQZBrq??qB=@j?dS'Q=![-G]r;R9Ks2TNes$bT+1&mGps#T3>rre+Bs"<a=rrd_C
-s!.@=rrItfrr3&A/!>J`JcC<$JcFF'J,~>
-"A:iVO;Rp;"^M*-EkD;Crrr.#*Zd]BrVlrj2YI"J&*t?<p](9d4rX\IGkqC42?"X"Jbf?</H-[n
-MYdAE,5rVaPl(I[!l>M%JcC<$JcC<$g&HR~>
-"A:iVO;Rp;"^M*-EkD;Crrr.#*Zd]BrVlrj2YI"J&*t?<p](9d4rX\IGkqC42?"X"Jbf?</H-[n
-MYdAE,5rVaPl(I[!l>M%JcC<$JcC<$g&HR~>
-"A:iVO;Rp;"^M*-EkD;Crrr.#*Zd]BrVlrj2YI"J&*t?<p](9d4rX\IGkqC42?"X"Jbf?</H-[n
-MYdAE,5rVaPl(I[!l>M%JcC<$JcC<$g&HR~>
-!D>N"rrMF?JcC<$JcC<$\Gq'~>
-!D>N"rrMF?JcC<$JcC<$\Gq'~>
-!D>N"rrMF?JcC<$JcC<$\Gq'~>
-!D>N"rrMF?JcC<$JcC<$\Gq'~>
-!D>N"rrMF?JcC<$JcC<$\Gq'~>
-!D>N"rrMF?JcC<$JcC<$\Gq'~>
-!D>N"rrM_GJcC<$JcC<$\Gq'~>
-!D>N"rrM_GJcC<$JcC<$\Gq'~>
-!D>N"rrM_GJcC<$JcC<$\Gq'~>
-!D>M*s+13$s+13&s*t~>
-!D>M*s+13$s+13&s*t~>
-!D>M*s+13$s+13&s*t~>
-!D>NQrrIH;oD\f.li.!"FSu.?F^'4jrrR%AK'!"7oNPOlmf*=XI"D<Ks+13$s7QDB~>
-!D>NQrrIH;oD\f.li.!"FSu.?F^'4jrrR%AK'!"7oNPOlmf*=XI"D<Ks+13$s7QDB~>
-!D>NQrrIH;oD\f.li.!"FSu.?F^'4jrrR%AK'!"7oNPOlmf*=XI"D<Ks+13$s7QDB~>
-#tmA[V69hPmGN'ZrrHa>li."*b5D8@bH`l#rrU9A4lu\Gqn\)'mf*=Q1YMd/Qb7nLJcC<$TDsE~>
-#tmA[V69hPmGN'ZrrHa>li."*b5D8@bH`l#rrU9A4lu\Gqn\)'mf*=Q1YMd/Qb7nLJcC<$TDsE~>
-#tmA[V69hPmGN'ZrrHa>li."*b5D8@bH`l#rrU9A4lu\Gqn\)'mf*=Q1YMd/Qb7nLJcC<$TDsE~>
-#tmA[n8Punj$2c%rrQrD)WUhulS8;:!T6-$rrKH@mf*=Q1YMd/G,BijJcC<$TDsE~>
-#tmA[n8Punj$2c%rrQrD)WUhulS8;:!T6-$rrKH@mf*=Q1YMd/G,BijJcC<$TDsE~>
-#tmA[n8Punj$2c%rrQrD)WUhulS8;:!T6-$rrKH@mf*=Q1YMd/G,BijJcC<$TDsE~>
-#YR8Zs6a"o,j+k$"+miX*r>m;&MNhW!_OI]qu6]_1&LtOi'%&=!U2-6*XdYos6(XJ*d)V6s8)[8
-!L<EYrrKH@pAY0m`Vgh\YP[T:VZ%V=\GZBJr>l?_rRb34I3'HPs8TJ)*WsmkpmV.&$f1io*Y&i)
-*rl96M#JG%-KtK-K&fcM0C",gJcC<$ZN#F~>
-#YR8Zs6a"o,j+k$"+miX*r>m;&MNhW!_OI]qu6]_1&LtOi'%&=!U2-6*XdYos6(XJ*d)V6s8)[8
-!L<EYrrKH@pAY0m`Vgh\YP[T:VZ%V=\GZBJr>l?_rRb34I3'HPs8TJ)*WsmkpmV.&$f1io*Y&i)
-*rl96M#JG%-KtK-K&fcM0C",gJcC<$ZN#F~>
-#YR8Zs6a"o,j+k$"+miX*r>m;&MNhW!_OI]qu6]_1&LtOi'%&=!U2-6*XdYos6(XJ*d)V6s8)[8
-!L<EYrrKH@pAY0m`Vgh\YP[T:VZ%V=\GZBJr>l?_rRb34I3'HPs8TJ)*WsmkpmV.&$f1io*Y&i)
-*rl96M#JG%-KtK-K&fcM0C",gJcC<$ZN#F~>
-#tmA[j`JBojA4A"rr=,:rrGJko`"sd-N!fDlS8;:!T6->rrFb?rr37OU&QA5s8S+>qYpS-\,QC.
-ZYK46!VIi=rrd2tl3hh8rrG+?qu6sSnGfp5T`>$9qtpBq*rio]rVljnrr3"BVZ$MqVKVt=$1o\H
-s*Z-8s6):js+13$s0DZT~>
-#tmA[j`JBojA4A"rr=,:rrGJko`"sd-N!fDlS8;:!T6->rrFb?rr37OU&QA5s8S+>qYpS-\,QC.
-ZYK46!VIi=rrd2tl3hh8rrG+?qu6sSnGfp5T`>$9qtpBq*rio]rVljnrr3"BVZ$MqVKVt=$1o\H
-s*Z-8s6):js+13$s0DZT~>
-#tmA[j`JBojA4A"rr=,:rrGJko`"sd-N!fDlS8;:!T6->rrFb?rr37OU&QA5s8S+>qYpS-\,QC.
-ZYK46!VIi=rrd2tl3hh8rrG+?qu6sSnGfp5T`>$9qtpBq*rio]rVljnrr3"BVZ$MqVKVt=$1o\H
-s*Z-8s6):js+13$s0DZT~>
-#tmA[Z+0ffmGruorr=,+rrMj@qu6]_1&LtOi'%&=!A9uCW=)Uts!.@>s,N->s8Dnq!CO?qrrKH@
-pAY0d-i<oEl8/D="$#BAlM[[b*q93<BE%o5D#F=nrM]l7s1Mh9rrI;?rVlnq:B%4!U+--BI@pQ>
-n9BNaJcC<$ZN#F~>
-#tmA[Z+0ffmGruorr=,+rrMj@qu6]_1&LtOi'%&=!A9uCW=)Uts!.@>s,N->s8Dnq!CO?qrrKH@
-pAY0d-i<oEl8/D="$#BAlM[[b*q93<BE%o5D#F=nrM]l7s1Mh9rrI;?rVlnq:B%4!U+--BI@pQ>
-n9BNaJcC<$ZN#F~>
-#tmA[Z+0ffmGruorr=,+rrMj@qu6]_1&LtOi'%&=!A9uCW=)Uts!.@>s,N->s8Dnq!CO?qrrKH@
-pAY0d-i<oEl8/D="$#BAlM[[b*q93<BE%o5D#F=nrM]l7s1Mh9rrI;?rVlnq:B%4!U+--BI@pQ>
-n9BNaJcC<$ZN#F~>
-!D>NQrrJ/QoD\e3li."[-N!fDlS8;:!T6->rrF`grGr=hs+cMkN.JhFs-EV)!A:k\rrKH@pAY0d
--i<oEl8/D="$#B2LAc/r'_).2BDhc1k(N\S!tkRH@/9g'G,G6<!N%dREs.C#s*^Mjs+13$s/uBP~>
-!D>NQrrJ/QoD\e3li."[-N!fDlS8;:!T6->rrF`grGr=hs+cMkN.JhFs-EV)!A:k\rrKH@pAY0d
--i<oEl8/D="$#B2LAc/r'_).2BDhc1k(N\S!tkRH@/9g'G,G6<!N%dREs.C#s*^Mjs+13$s/uBP~>
-!D>NQrrJ/QoD\e3li."[-N!fDlS8;:!T6->rrF`grGr=hs+cMkN.JhFs-EV)!A:k\rrKH@pAY0d
--i<oEl8/D="$#B2LAc/r'_).2BDhc1k(N\S!tkRH@/9g'G,G6<!N%dREs.C#s*^Mjs+13$s/uBP~>
-!D>NArr=,:rrI;!o`"sd-N!fDlS8;:!T6->rrFb?qu6eNOHG[Brr2s>rr3&1/@YWY!OHP7rrMd?
-rVltal0:)'rrY7Ah`h&>"dftms0cS<rrL2@rr3+[&c]OPrVlkIrr34HVZ6[Gs/'u9rrIP?JcC<$
-JcDnQJ,~>
-!D>NArr=,:rrI;!o`"sd-N!fDlS8;:!T6->rrFb?qu6eNOHG[Brr2s>rr3&1/@YWY!OHP7rrMd?
-rVltal0:)'rrY7Ah`h&>"dftms0cS<rrL2@rr3+[&c]OPrVlkIrr34HVZ6[Gs/'u9rrIP?JcC<$
-JcDnQJ,~>
-!D>NArr=,:rrI;!o`"sd-N!fDlS8;:!T6->rrFb?qu6eNOHG[Brr2s>rr3&1/@YWY!OHP7rrMd?
-rVltal0:)'rrY7Ah`h&>"dftms0cS<rrL2@rr3+[&c]OPrVlkIrr34HVZ6[Gs/'u9rrIP?JcC<$
-JcDnQJ,~>
-!D>NDrs#)m;$g)sOT#1[%P@AR"ER<H;3V"Z"Dg[A;4IRb#\6F=;54*j]iY21"KMM%\l8T*"1%t,
-[/No/IRUaGo2.Lk;8N&-!W8Vh;$3-Ul8/D="$#B7R/TqdP99;o[V,O<!Uc$J;$<@(s6>O@;#mj"
-rs.%n;,Ok'j+I>.!*K7#!I^Sks+13$s/uBP~>
-!D>NDrs#)m;$g)sOT#1[%P@AR"ER<H;3V"Z"Dg[A;4IRb#\6F=;54*j]iY21"KMM%\l8T*"1%t,
-[/No/IRUaGo2.Lk;8N&-!W8Vh;$3-Ul8/D="$#B7R/TqdP99;o[V,O<!Uc$J;$<@(s6>O@;#mj"
-rs.%n;,Ok'j+I>.!*K7#!I^Sks+13$s/uBP~>
-!D>NDrs#)m;$g)sOT#1[%P@AR"ER<H;3V"Z"Dg[A;4IRb#\6F=;54*j]iY21"KMM%\l8T*"1%t,
-[/No/IRUaGo2.Lk;8N&-!W8Vh;$3-Ul8/D="$#B7R/TqdP99;o[V,O<!Uc$J;$<@(s6>O@;#mj"
-rs.%n;,Ok'j+I>.!*K7#!I^Sks+13$s/uBP~>
-!D>NDrrD*WbQ-Q!rrLHrp&>$CrlbB"rr2uJrlbB%rr2uHrlbB'rr2uErlbN-s8Pm:rlbIg^&S*2
-bQR%cnc/OcbQ.&)rrDflbQID8qpt`G"5!DLoDZr;nC@I:nDX9E!:Kj1"SL4Cs6T^.!<)lr!oD/F
-rr2u]rlbAfrr3#\m",1fs+13Qs*t~>
-!D>NDrrD*WbQ-Q!rrLHrp&>$CrlbB"rr2uJrlbB%rr2uHrlbB'rr2uErlbN-s8Pm:rlbIg^&S*2
-bQR%cnc/OcbQ.&)rrDflbQID8qpt`G"5!DLoDZr;nC@I:nDX9E!:Kj1"SL4Cs6T^.!<)lr!oD/F
-rr2u]rlbAfrr3#\m",1fs+13Qs*t~>
-!D>NDrrD*WbQ-Q!rrLHrp&>$CrlbB"rr2uJrlbB%rr2uHrlbB'rr2uErlbN-s8Pm:rlbIg^&S*2
-bQR%cnc/OcbQ.&)rrDflbQID8qpt`G"5!DLoDZr;nC@I:nDX9E!:Kj1"SL4Cs6T^.!<)lr!oD/F
-rr2u]rlbAfrr3#\m",1fs+13Qs*t~>
-!D>MarrFV?rq?G6YCZ_)s+13$s3q!u~>
-!D>MarrFV?rq?G6YCZ_)s+13$s3q!u~>
-!D>MarrFV?rq?G6YCZ_)s+13$s3q!u~>
-!D>M`rr=PJ-30Zhs+13$s+13us*t~>
-!D>M`rr=PJ-30Zhs+13$s+13us*t~>
-!D>M`rr=PJ-30Zhs+13$s+13us*t~>
-!D>M*s+13$s+13&s*t~>
-!D>M*s+13$s+13&s*t~>
-!D>M*s+13$s+13&s*t~>
-"A:iVs2Y,1!3Z>$!Q4&Ns+13$s+132s*t~>
-"A:iVs2Y,1!3Z>$!Q4&Ns+13$s+132s*t~>
-"A:iVs2Y,1!3Z>$!Q4&Ns+13$s+132s*t~>
-"A:iV`..c8#(NHKs0$t?JcC<$JcC<$OT0h~>
-"A:iV`..c8#(NHKs0$t?JcC<$JcC<$OT0h~>
-"A:iV`..c8#(NHKs0$t?JcC<$JcC<$OT0h~>
-"A:iVM3S#>!S0a>rrTTDqgncus+13$s,m>3~>
-"A:iVM3S#>!S0a>rrTTDqgncus+13$s,m>3~>
-"A:iVM3S#>!S0a>rrTTDqgncus+13$s,m>3~>
-#tmA[hr=\9pK5Z<rrL#?JcC<$JcC<$OoKq~>
-#tmA[hr=\9pK5Z<rrL#?JcC<$JcC<$OoKq~>
-#tmA[hr=\9pK5Z<rrL#?JcC<$JcC<$OoKq~>
-!D>NPrrG@?qu6]<<.Y(#s+13$s-!D4~>
-!D>NPrrG@?qu6]<<.Y(#s+13$s-!D4~>
-!D>NPrrG@?qu6]<<.Y(#s+13$s-!D4~>
-!D>NRrrVaZjneuXg-^GkJcC<$JcCo5J,~>
-!D>NRrrVaZjneuXg-^GkJcC<$JcCo5J,~>
-!D>NRrrVaZjneuXg-^GkJcC<$JcCo5J,~>
-#"q&Xs5RM>qYpTI7tL\ks+13$s,m>3~>
-#"q&Xs5RM>qYpTI7tL\ks+13$s,m>3~>
-#"q&Xs5RM>qYpTI7tL\ks+13$s,m>3~>
-"\UrWgN^j:rrU/EqLSZts+13$s,m>3~>
-"\UrWgN^j:rrU/EqLSZts+13$s,m>3~>
-"\UrWgN^j:rrU/EqLSZts+13$s,m>3~>
-"A:iVM,sS>"gS+-]QWRks+13$s+133s*t~>
-"A:iVM,sS>"gS+-]QWRks+13$s+133s*t~>
-"A:iVM,sS>"gS+-]QWRks+13$s+133s*t~>
-"%t`UaS^ktWrN+,i.:oZs+13$s,[21~>
-"%t`UaS^ktWrN+,i.:oZs+13$s,[21~>
-"%t`UaS^ktWrN+,i.:oZs+13$s,[21~>
-!D>M*s+13$s+13&s*t~>
-!D>M*s+13$s+13&s*t~>
-!D>M*s+13$s+13&s*t~>
-!D>M*s+13$s+13&s*t~>
-!D>M*s+13$s+13&s*t~>
-!D>M*s+13$s+13&s*t~>
-!D>M*s+13$s+13&s*t~>
-!D>M*s+13$s+13&s*t~>
-!D>M*s+13$s+13&s*t~>
-%%EndData
-showpage
-%%Trailer
-end
-%%EOF
diff --git a/lib/stdlib/doc/src/ushell3.gif b/lib/stdlib/doc/src/ushell3.gif
deleted file mode 100644
index 2141268b91..0000000000
--- a/lib/stdlib/doc/src/ushell3.gif
+++ /dev/null
Binary files differ
diff --git a/lib/stdlib/doc/src/ushell3.ps b/lib/stdlib/doc/src/ushell3.ps
deleted file mode 100644
index dd64eeab5c..0000000000
--- a/lib/stdlib/doc/src/ushell3.ps
+++ /dev/null
@@ -1,662 +0,0 @@
-%!PS-Adobe-3.0
-%%Creator: GIMP PostScript file plugin V 1.17 by Peter Kirchgessner
-%%Title: ushell3.ps
-%%CreationDate: Mon Mar 16 12:15:17 2009
-%%DocumentData: Clean7Bit
-%%LanguageLevel: 2
-%%Pages: 1
-%%BoundingBox: 14 14 468 78
-%%EndComments
-%%BeginProlog
-% Use own dictionary to avoid conflicts
-10 dict begin
-%%EndProlog
-%%Page: 1 1
-% Translate for offset
-14.173228346456694 14.173228346456694 translate
-% Translate to begin of first scanline
-0 63.070866141732289 translate
-453.54330708661422 -63.070866141732289 scale
-% Image geometry
-640 89 8
-% Transformation matrix
-[ 640 0 0 89 0 0 ]
-% Strings to hold RGB-samples per scanline
-/rstr 640 string def
-/gstr 640 string def
-/bstr 640 string def
-{currentfile /ASCII85Decode filter /RunLengthDecode filter rstr readstring pop}
-{currentfile /ASCII85Decode filter /RunLengthDecode filter gstr readstring pop}
-{currentfile /ASCII85Decode filter /RunLengthDecode filter bstr readstring pop}
-true 3
-%%BeginData: 37475 ASCII Bytes
-colorimage
-!1\W%J`VIEJ`VLFJ,~>
-!1\W%J`VIEJ`VLFJ,~>
-!1\W%J`VIEJ`VLFJ,~>
-!T[+/6@hIS6@hIU6@]~>
-!T[+/6@hIS6@hIU6@]~>
-!T[+/6@hIS6@hIU6@]~>
-!ouWfJQ.2"JQ.2"KN*I~>
-!ouWfJQ.2"JQ.2"KN*I~>
-!ouWfJQ.2"JQ.2"KN*I~>
-!ouXLJcC<$JcC<$K`?Q~>
-!ouXLJcC<$JcC<$K`?Q~>
-!ouXLJcC<$JcC<$K`?Q~>
-!ouXLQ2^m!`0L?'S`PG&JcC<$i;\<~>
-!ouXLQ2^m!`0L?'S`PG&JcC<$i;\<~>
-!ouXLQ2^m!`0L?'S`PG&JcC<$i;\<~>
-"QVjNs.9-i!1*E[!L)+*rrR=EF8Z%>Dc_2VrrI?7rr3)%^&N-N<rg/5rr?^0<rgP6rrU,eg\:^K
-o6/O]!ioDNpAY05U[\9aJa!(4!2BHo!,VW6!lAR@JcC<$JcF^/J,~>
-"QVjNs.9-i!1*E[!L)+*rrR=EF8Z%>Dc_2VrrI?7rr3)%^&N-N<rg/5rr?^0<rgP6rrU,eg\:^K
-o6/O]!ioDNpAY05U[\9aJa!(4!2BHo!,VW6!lAR@JcC<$JcF^/J,~>
-"QVjNs.9-i!1*E[!L)+*rrR=EF8Z%>Dc_2VrrI?7rr3)%^&N-N<rg/5rr?^0<rgP6rrU,eg\:^K
-o6/O]!ioDNpAY05U[\9aJa!(4!2BHo!,VW6!lAR@JcC<$JcF^/J,~>
-"lqsOs)!Pi_uSNdrrHB@o)Ad;\gmXX!mS).p&>%Ili-neJXl`h]D]YJlM1AZb5K6Z:po`k!N()7
-rrML@l2LdgT^2UZil(<`rrJFsrVlntF7K88K;)-<s8T'RrkniDCAn/50C=>jJcC<$iW"E~>
-"lqsOs)!Pi_uSNdrrHB@o)Ad;\gmXX!mS).p&>%Ili-neJXl`h]D]YJlM1AZb5K6Z:po`k!N()7
-rrML@l2LdgT^2UZil(<`rrJFsrVlntF7K88K;)-<s8T'RrkniDCAn/50C=>jJcC<$iW"E~>
-"lqsOs)!Pi_uSNdrrHB@o)Ad;\gmXX!mS).p&>%Ili-neJXl`h]D]YJlM1AZb5K6Z:po`k!N()7
-rrML@l2LdgT^2UZil(<`rrJFsrVlntF7K88K;)-<s8T'RrkniDCAn/50C=>jJcC<$iW"E~>
-"lqsOs)#"3rrHB@nc&X\.f95Hkqi#4!AWp>rrdnCs!dR.rrSCIp@&"_b$si5rrML@l2LdPK]E(5
-GGY9<!NC/2rs4ONpZ4C7s,`6>rrN#\rVlo\2M(mZs+14/s*t~>
-"lqsOs)#"3rrHB@nc&X\.f95Hkqi#4!AWp>rrdnCs!dR.rrSCIp@&"_b$si5rrML@l2LdPK]E(5
-GGY9<!NC/2rs4ONpZ4C7s,`6>rrN#\rVlo\2M(mZs+14/s*t~>
-"lqsOs)#"3rrHB@nc&X\.f95Hkqi#4!AWp>rrdnCs!dR.rrSCIp@&"_b$si5rrML@l2LdPK]E(5
-GGY9<!NC/2rs4ONpZ4C7s,`6>rrN#\rVlo\2M(mZs+14/s*t~>
-#in9Rs)"@hjludE!Us(f*"5s=s'2Za*#r;Ts6`DV*!'XArr3#i.f95Hkqi#4$2ktGs.=c>s!c4l
-*!(ffp&>*eKVIo7!Q/(<rrCjQ*!EJBm4518*!<VHr42bG&A7u":Z[6L*5hd.*"`Z#*8gPk!Q/1@
-rrsVGs/-hF]Dhg?\f;.l*$"qWWG$>m*$OSArs>H^rr<#^9E1*5p\t46JcC<$JcF^/J,~>
-#in9Rs)"@hjludE!Us(f*"5s=s'2Za*#r;Ts6`DV*!'XArr3#i.f95Hkqi#4$2ktGs.=c>s!c4l
-*!(ffp&>*eKVIo7!Q/(<rrCjQ*!EJBm4518*!<VHr42bG&A7u":Z[6L*5hd.*"`Z#*8gPk!Q/1@
-rrsVGs/-hF]Dhg?\f;.l*$"qWWG$>m*$OSArs>H^rr<#^9E1*5p\t46JcC<$JcF^/J,~>
-#in9Rs)"@hjludE!Us(f*"5s=s'2Za*#r;Ts6`DV*!'XArr3#i.f95Hkqi#4$2ktGs.=c>s!c4l
-*!(ffp&>*eKVIo7!Q/(<rrCjQ*!EJBm4518*!<VHr42bG&A7u":Z[6L*5hd.*"`Z#*8gPk!Q/1@
-rrsVGs/-hF]Dhg?\f;.l*$"qWWG$>m*$OSArs>H^rr<#^9E1*5p\t46JcC<$JcF^/J,~>
-#in9Rs(s&?2H0VT!G&_>rsZf$s'-u6s8T->s&7&=rrJm@rr3#i.f95Hkqhu3#T`sFSpp_><^Zld
-!I1F5rrJj?mJd4)=nhq!7G$o6ErQ(@4l><[rVm<akl8@2QiI(:nGiNVK_>?L_HQutn,FF,rrHT?
-r;QeAV>^DuW>MT6s7dl/rs"REs8UEef)5OJ*J+6As+14/s*t~>
-#in9Rs(s&?2H0VT!G&_>rsZf$s'-u6s8T->s&7&=rrJm@rr3#i.f95Hkqhu3#T`sFSpp_><^Zld
-!I1F5rrJj?mJd4)=nhq!7G$o6ErQ(@4l><[rVm<akl8@2QiI(:nGiNVK_>?L_HQutn,FF,rrHT?
-r;QeAV>^DuW>MT6s7dl/rs"REs8UEef)5OJ*J+6As+14/s*t~>
-#in9Rs(s&?2H0VT!G&_>rsZf$s'-u6s8T->s&7&=rrJm@rr3#i.f95Hkqhu3#T`sFSpp_><^Zld
-!I1F5rrJj?mJd4)=nhq!7G$o6ErQ(@4l><[rVm<akl8@2QiI(:nGiNVK_>?L_HQutn,FF,rrHT?
-r;QeAV>^DuW>MT6s7dl/rs"REs8UEef)5OJ*J+6As+14/s*t~>
-"lqsOs)#";rrJFMriH=Cs8Q??rr3,%EW8soriH3>HN*pFngaP:!U2E4rrciCl&)D8rrIA?pAY3[
-N2>qA!Q/(<rrDWgXTL6.m4eM="$PQ&3;rj[2<b(S?N0s.E:s82R=t85#/XRDCU3s\rVln-]);R/
-GGY9<!NC/>rrMm?nG`]SNW9#h7m6eM!$-XjJcC<$iW"E~>
-"lqsOs)#";rrJFMriH=Cs8Q??rr3,%EW8soriH3>HN*pFngaP:!U2E4rrciCl&)D8rrIA?pAY3[
-N2>qA!Q/(<rrDWgXTL6.m4eM="$PQ&3;rj[2<b(S?N0s.E:s82R=t85#/XRDCU3s\rVln-]);R/
-GGY9<!NC/>rrMm?nG`]SNW9#h7m6eM!$-XjJcC<$iW"E~>
-"lqsOs)#";rrJFMriH=Cs8Q??rr3,%EW8soriH3>HN*pFngaP:!U2E4rrciCl&)D8rrIA?pAY3[
-N2>qA!Q/(<rrDWgXTL6.m4eM="$PQ&3;rj[2<b(S?N0s.E:s82R=t85#/XRDCU3s\rVln-]);R/
-GGY9<!NC/>rrMm?nG`]SNW9#h7m6eM!$-XjJcC<$iW"E~>
-"lqsOs)#":rrAAaD?P3us'3D>rrfBBs&2tsD?.$BrrM[?qu6]]1\C\Lp*RC[q>UJ?V"Xfh^Kp4+
-!Q/(=rrN"UrGD]YfBk9jrrYFAj>d,<"?#EC^0^[9!L\W6rs$>Ds(eq?*W?!=@_2L;!I(C=rrK*?
-rr3#o,k1g7OH'8>!T-'<rr='js+13$s5F!.~>
-"lqsOs)#":rrAAaD?P3us'3D>rrfBBs&2tsD?.$BrrM[?qu6]]1\C\Lp*RC[q>UJ?V"Xfh^Kp4+
-!Q/(=rrN"UrGD]YfBk9jrrYFAj>d,<"?#EC^0^[9!L\W6rs$>Ds(eq?*W?!=@_2L;!I(C=rrK*?
-rr3#o,k1g7OH'8>!T-'<rr='js+13$s5F!.~>
-"lqsOs)#":rrAAaD?P3us'3D>rrfBBs&2tsD?.$BrrM[?qu6]]1\C\Lp*RC[q>UJ?V"Xfh^Kp4+
-!Q/(=rrN"UrGD]YfBk9jrrYFAj>d,<"?#EC^0^[9!L\W6rs$>Ds(eq?*W?!=@_2L;!I(C=rrK*?
-rr3#o,k1g7OH'8>!T-'<rr='js+13$s5F!.~>
-"lqsOs)#";rrK1Rrr3,.C&_GSrr3,%EW8tZq>UKd.f95Hkqhu3"84(R@K-9-Mu!AP!I1F>rrV/%
-Zi0n*m!?)+rrV55[Jp1,odBb="fictm1u>nrrYFAj>d,<"?#EC^0^[9"dt&Ds,$[Lrs$>D^O^an
-*W?!=@_2L;#C!$Es+UKPF8l1?pa#A/!K`<?rrM"?rVlnZNIh+\s+14/s*t~>
-"lqsOs)#";rrK1Rrr3,.C&_GSrr3,%EW8tZq>UKd.f95Hkqhu3"84(R@K-9-Mu!AP!I1F>rrV/%
-Zi0n*m!?)+rrV55[Jp1,odBb="fictm1u>nrrYFAj>d,<"?#EC^0^[9"dt&Ds,$[Lrs$>D^O^an
-*W?!=@_2L;#C!$Es+UKPF8l1?pa#A/!K`<?rrM"?rVlnZNIh+\s+14/s*t~>
-"lqsOs)#";rrK1Rrr3,.C&_GSrr3,%EW8tZq>UKd.f95Hkqhu3"84(R@K-9-Mu!AP!I1F>rrV/%
-Zi0n*m!?)+rrV55[Jp1,odBb="fictm1u>nrrYFAj>d,<"?#EC^0^[9"dt&Ds,$[Lrs$>D^O^an
-*W?!=@_2L;#C!$Es+UKPF8l1?pa#A/!K`<?rrM"?rVlnZNIh+\s+14/s*t~>
-"lqsOs(spt=Tb&ka`%/]"E;N`>f$F>"KQPB_eK*Q!1!Q`"F!iV=e#Ej"E@<P=ePKg"8nU*aSu2?
-ZsEZ6!J95.rrU7AErH"=gd(0)!N()?rrN%dr`KD\/_BA3O8`8aPP"R6rE08hp]%s6qYp`RIT]gC
-rV?I'lAL)E_.]YEs1_\\6!=!^rs-kl=]qs.WH8(=!Vdr0rrLc"r`K83]D_a10C=>jJcC<$iW"E~>
-"lqsOs(spt=Tb&ka`%/]"E;N`>f$F>"KQPB_eK*Q!1!Q`"F!iV=e#Ej"E@<P=ePKg"8nU*aSu2?
-ZsEZ6!J95.rrU7AErH"=gd(0)!N()?rrN%dr`KD\/_BA3O8`8aPP"R6rE08hp]%s6qYp`RIT]gC
-rV?I'lAL)E_.]YEs1_\\6!=!^rs-kl=]qs.WH8(=!Vdr0rrLc"r`K83]D_a10C=>jJcC<$iW"E~>
-"lqsOs(spt=Tb&ka`%/]"E;N`>f$F>"KQPB_eK*Q!1!Q`"F!iV=e#Ej"E@<P=ePKg"8nU*aSu2?
-ZsEZ6!J95.rrU7AErH"=gd(0)!N()?rrN%dr`KD\/_BA3O8`8aPP"R6rE08hp]%s6qYp`RIT]gC
-rV?I'lAL)E_.]YEs1_\\6!=!^rs-kl=]qs.WH8(=!Vdr0rrLc"r`K83]D_a10C=>jJcC<$iW"E~>
-"QVjNs4mOh"53_Se,I2eeGoR$nG`FjleVU@ci1c]f`(mNc2PQ[gA_*PbPo?Yh"C[Jp<rm=!6+rS
-!93tW!qO4arVlomdH^`5l@c>>rrDcl_?K,Np&!#$rk\d,s8VZg_?BH0s7"\:rr_/q_Y3a(#jL4G
-s3:H@s6'?t!<)lr#1p`/s8VB?rr3#tb4#?1h>Y7kb5M>AGH(Ijs+13$s5F!.~>
-"QVjNs4mOh"53_Se,I2eeGoR$nG`FjleVU@ci1c]f`(mNc2PQ[gA_*PbPo?Yh"C[Jp<rm=!6+rS
-!93tW!qO4arVlomdH^`5l@c>>rrDcl_?K,Np&!#$rk\d,s8VZg_?BH0s7"\:rr_/q_Y3a(#jL4G
-s3:H@s6'?t!<)lr#1p`/s8VB?rr3#tb4#?1h>Y7kb5M>AGH(Ijs+13$s5F!.~>
-"QVjNs4mOh"53_Se,I2eeGoR$nG`FjleVU@ci1c]f`(mNc2PQ[gA_*PbPo?Yh"C[Jp<rm=!6+rS
-!93tW!qO4arVlomdH^`5l@c>>rrDcl_?K,Np&!#$rk\d,s8VZg_?BH0s7"\:rr_/q_Y3a(#jL4G
-s3:H@s6'?t!<)lr#1p`/s8VB?rr3#tb4#?1h>Y7kb5M>AGH(Ijs+13$s5F!.~>
-!ouXLQ2^mSnWj+TkkTf0JcC<$i;\<~>
-!ouXLQ2^mSnWj+TkkTf0JcC<$i;\<~>
-!ouXLQ2^mSnWj+TkkTf0JcC<$i;\<~>
-!ouXLJcC<$JcC<$K`?Q~>
-!ouXLJcC<$JcC<$K`?Q~>
-!ouXLJcC<$JcC<$K`?Q~>
-!ouXLJcG]K!TNR]rr_0&bgEm!"4YQ=h#.0PdXhFLs/5mI~>
-!ouXLJcG]K!TNR]rr_0&bgEm!"4YQ=h#.0PdXhFLs/5mI~>
-!ouXLJcG]K!TNR]rr_0&bgEm!"4YQ=h#.0PdXhFLs/5mI~>
-!ouXLrVll.r;Qf;o()e]\@V&,!kGPPr;QirYf6S@!PSC#rr_SkYi""2!o&"@rVm>df)Ne\s8V;e
-g&M)arUBgji3;2?s1.k'"4H?3\b5t)>rthhrVluBmf'fsrr^ZQYd<0c!6"l@!6Y!7"1eHrci3qH
-Bhpuir;Qe$_L_`<s/>sJ~>
-!ouXLrVll.r;Qf;o()e]\@V&,!kGPPr;QirYf6S@!PSC#rr_SkYi""2!o&"@rVm>df)Ne\s8V;e
-g&M)arUBgji3;2?s1.k'"4H?3\b5t)>rthhrVluBmf'fsrr^ZQYd<0c!6"l@!6Y!7"1eHrci3qH
-Bhpuir;Qe$_L_`<s/>sJ~>
-!ouXLrVll.r;Qf;o()e]\@V&,!kGPPr;QirYf6S@!PSC#rr_SkYi""2!o&"@rVm>df)Ne\s8V;e
-g&M)arUBgji3;2?s1.k'"4H?3\b5t)>rthhrVluBmf'fsrr^ZQYd<0c!6"l@!6Y!7"1eHrci3qH
-Bhpuir;Qe$_L_`<s/>sJ~>
-!ouXLrr3"u(&\(5P`>P2rrICpm/I-7?T\2c!r?\qrVlmIoCDnbn;;!sKAbYe!T1]hrrJ(?r;RA&
-HiH[ds6YVXJCjfHq>^KAch7;Bie2*#ir:%mrr[/AruLe4!F<J;rsMiIr#l%>c$X;BD5H.i!P<LR
-CB8[tpAY48s0uV;rrLA?r;QfW3e@<^s/>sJ~>
-!ouXLrr3"u(&\(5P`>P2rrICpm/I-7?T\2c!r?\qrVlmIoCDnbn;;!sKAbYe!T1]hrrJ(?r;RA&
-HiH[ds6YVXJCjfHq>^KAch7;Bie2*#ir:%mrr[/AruLe4!F<J;rsMiIr#l%>c$X;BD5H.i!P<LR
-CB8[tpAY48s0uV;rrLA?r;QfW3e@<^s/>sJ~>
-!ouXLrr3"u(&\(5P`>P2rrICpm/I-7?T\2c!r?\qrVlmIoCDnbn;;!sKAbYe!T1]hrrJ(?r;RA&
-HiH[ds6YVXJCjfHq>^KAch7;Bie2*#ir:%mrr[/AruLe4!F<J;rsMiIr#l%>c$X;BD5H.i!P<LR
-CB8[tpAY48s0uV;rrLA?r;QfW3e@<^s/>sJ~>
-#NS0Qs8SgH'Dqe1RtBDp!T?-5rrFS?o)AmK5l^js[GUubZ>0::!n)SAr;RA&HiH[ds3jR>_1$W_
-r;ZeYN:m2T\mk]II!C_Grr[/AruLe4!F<J;rsMiIr#l%>LR%o>oI8\t#.&^Es8U`apAY48s0uV;
-rrLA?r;QigKVj>#JcD_LJ,~>
-#NS0Qs8SgH'Dqe1RtBDp!T?-5rrFS?o)AmK5l^js[GUubZ>0::!n)SAr;RA&HiH[ds3jR>_1$W_
-r;ZeYN:m2T\mk]II!C_Grr[/AruLe4!F<J;rsMiIr#l%>LR%o>oI8\t#.&^Es8U`apAY48s0uV;
-rrLA?r;QigKVj>#JcD_LJ,~>
-#NS0Qs8SgH'Dqe1RtBDp!T?-5rrFS?o)AmK5l^js[GUubZ>0::!n)SAr;RA&HiH[ds3jR>_1$W_
-r;ZeYN:m2T\mk]II!C_Grr[/AruLe4!F<J;rsMiIr#l%>LR%o>oI8\t#.&^Es8U`apAY48s0uV;
-rrLA?r;QigKVj>#JcD_LJ,~>
-#NS0Qs.PG>*W#d:UNuP4!e+Bor;QbBr`fGnp&>'O48f*[Bj?GlrrFS4r`fH/rVlrS>c%E!#M_TE
-s2a:$pAJ50_Z0XSs3"YPaT)6E>a;X#c2[h+rEKWWs79J\-<sg.rr3#!F8PtRh9c29n,Mjis8U'5
-o)IQO1br<@mf3=+rEKd&s5qB%s1G-(s3748r`fI&rr3)Dir?1SrrHE?qu72Dli+*fs+cm>s7@c?
-s/j-:rrM&<rr3"Nm/?qjU<NXUs4*P;s,-e\!/pjV"@#[e>`o$c!.XuQ!1*T`"0V[da8Gr<btn6:
-!QA,ks+13Ls*t~>
-#NS0Qs.PG>*W#d:UNuP4!e+Bor;QbBr`fGnp&>'O48f*[Bj?GlrrFS4r`fH/rVlrS>c%E!#M_TE
-s2a:$pAJ50_Z0XSs3"YPaT)6E>a;X#c2[h+rEKWWs79J\-<sg.rr3#!F8PtRh9c29n,Mjis8U'5
-o)IQO1br<@mf3=+rEKd&s5qB%s1G-(s3748r`fI&rr3)Dir?1SrrHE?qu72Dli+*fs+cm>s7@c?
-s/j-:rrM&<rr3"Nm/?qjU<NXUs4*P;s,-e\!/pjV"@#[e>`o$c!.XuQ!1*T`"0V[da8Gr<btn6:
-!QA,ks+13Ls*t~>
-#NS0Qs.PG>*W#d:UNuP4!e+Bor;QbBr`fGnp&>'O48f*[Bj?GlrrFS4r`fH/rVlrS>c%E!#M_TE
-s2a:$pAJ50_Z0XSs3"YPaT)6E>a;X#c2[h+rEKWWs79J\-<sg.rr3#!F8PtRh9c29n,Mjis8U'5
-o)IQO1br<@mf3=+rEKd&s5qB%s1G-(s3748r`fI&rr3)Dir?1SrrHE?qu72Dli+*fs+cm>s7@c?
-s/j-:rrM&<rr3"Nm/?qjU<NXUs4*P;s,-e\!/pjV"@#[e>`o$c!.XuQ!1*T`"0V[da8Gr<btn6:
-!QA,ks+13Ls*t~>
-#NS0Qs4Zf=*W#d;ql"c5rrUHX,5qNBGcC\W!KEfHrrM(?r;Qi:U."t[!@^M;^B&_orr3&@!-8&<
-!Sfs<rr>sq^CtP2s0b1+^V=R6rYN>5HbX4Is7Q?*#\)ch^P5S"^[V7&!NL5.rsA82Ch^Z!q#AcZ
-rP&>0q#:E#)he4*!*/Ie!F<J5rs=H,5X7I]s8RS?rr3#E:B(7o4Q68=$#ZsH*WNf[s,<HH^BC!c
-s+R-F^BBjes*pjD^B'Ldp&>';:](.m`E.WjJcD_LJ,~>
-#NS0Qs4Zf=*W#d;ql"c5rrUHX,5qNBGcC\W!KEfHrrM(?r;Qi:U."t[!@^M;^B&_orr3&@!-8&<
-!Sfs<rr>sq^CtP2s0b1+^V=R6rYN>5HbX4Is7Q?*#\)ch^P5S"^[V7&!NL5.rsA82Ch^Z!q#AcZ
-rP&>0q#:E#)he4*!*/Ie!F<J5rs=H,5X7I]s8RS?rr3#E:B(7o4Q68=$#ZsH*WNf[s,<HH^BC!c
-s+R-F^BBjes*pjD^B'Ldp&>';:](.m`E.WjJcD_LJ,~>
-#NS0Qs4Zf=*W#d;ql"c5rrUHX,5qNBGcC\W!KEfHrrM(?r;Qi:U."t[!@^M;^B&_orr3&@!-8&<
-!Sfs<rr>sq^CtP2s0b1+^V=R6rYN>5HbX4Is7Q?*#\)ch^P5S"^[V7&!NL5.rsA82Ch^Z!q#AcZ
-rP&>0q#:E#)he4*!*/Ie!F<J5rs=H,5X7I]s8RS?rr3#E:B(7o4Q68=$#ZsH*WNf[s,<HH^BC!c
-s+R-F^BBjes*pjD^B'Ldp&>';:](.m`E.WjJcD_LJ,~>
-!ouXLrVlj<q>UKC9D/;cqBGn<!DCl?rrJR?pAY0P48]$YfgPf=!@cOtrrIA?rr3&og["k="?7J0
-,`Vg'!$2%<&RW6Okq)#ts&R/=+9/3^s%^l:rrkjCs8TB>qu6\sF6ii,OGs/<!Q&%=rrCsOrrTH1
-mf*4d*U`q.?,-(4%YoShgX7PNs6XR>s6`D>rr3!]iVic_DQj'\s1Me>A[h[="LVqC?,6I>"KZSB
-<65(=!V5UMrrLA?qu6]9=+UC&s/H$K~>
-!ouXLrVlj<q>UKC9D/;cqBGn<!DCl?rrJR?pAY0P48]$YfgPf=!@cOtrrIA?rr3&og["k="?7J0
-,`Vg'!$2%<&RW6Okq)#ts&R/=+9/3^s%^l:rrkjCs8TB>qu6\sF6ii,OGs/<!Q&%=rrCsOrrTH1
-mf*4d*U`q.?,-(4%YoShgX7PNs6XR>s6`D>rr3!]iVic_DQj'\s1Me>A[h[="LVqC?,6I>"KZSB
-<65(=!V5UMrrLA?qu6]9=+UC&s/H$K~>
-!ouXLrVlj<q>UKC9D/;cqBGn<!DCl?rrJR?pAY0P48]$YfgPf=!@cOtrrIA?rr3&og["k="?7J0
-,`Vg'!$2%<&RW6Okq)#ts&R/=+9/3^s%^l:rrkjCs8TB>qu6\sF6ii,OGs/<!Q&%=rrCsOrrTH1
-mf*4d*U`q.?,-(4%YoShgX7PNs6XR>s6`D>rr3!]iVic_DQj'\s1Me>A[h[="LVqC?,6I>"KZSB
-<65(=!V5UMrrLA?qu6]9=+UC&s/H$K~>
-!ouXLrVlj<qYpWR7/HTV!W"#=rrGd@rr3"fK(f3FiB-r:!SKm>rrFS?rr3"DV"jrmpsqY'qu6Tq
-*W?!@?bQL(1]IC^qYgC8s/(#?:<rj^r>YtHfDklVCA\#3WcRM+!K`9=rrN(ur>Ygmq#:B"C]485
-*U`q.?,-(4"c&BCs7@c>rrd5Er7aR:rrG7@rVm1AYQ#XC]jLeN\,QC1[:oSG_#F?=XDn4+7iWLI
-oD\j9:](.m`E.WjJcD_LJ,~>
-!ouXLrVlj<qYpWR7/HTV!W"#=rrGd@rr3"fK(f3FiB-r:!SKm>rrFS?rr3"DV"jrmpsqY'qu6Tq
-*W?!@?bQL(1]IC^qYgC8s/(#?:<rj^r>YtHfDklVCA\#3WcRM+!K`9=rrN(ur>Ygmq#:B"C]485
-*U`q.?,-(4"c&BCs7@c>rrd5Er7aR:rrG7@rVm1AYQ#XC]jLeN\,QC1[:oSG_#F?=XDn4+7iWLI
-oD\j9:](.m`E.WjJcD_LJ,~>
-!ouXLrVlj<qYpWR7/HTV!W"#=rrGd@rr3"fK(f3FiB-r:!SKm>rrFS?rr3"DV"jrmpsqY'qu6Tq
-*W?!@?bQL(1]IC^qYgC8s/(#?:<rj^r>YtHfDklVCA\#3WcRM+!K`9=rrN(ur>Ygmq#:B"C]485
-*U`q.?,-(4"c&BCs7@c>rrd5Er7aR:rrG7@rVm1AYQ#XC]jLeN\,QC1[:oSG_#F?=XDn4+7iWLI
-oD\j9:](.m`E.WjJcD_LJ,~>
-!ouXLrVlj<qu6]J7e?W\qBGn<!DCl?rrJR?pAY0P48]$YfgPf=!@c+hrrIA?rr3&lcfG')!Sfs<
-rr=)<rrc]Bs62?;rsAZHs/(#?:<rM[rr3Mf37n31Z>0F>gA1dK\p3N,rs+XEs8UUKoAKTI!$1k7
-!O6G=rr=)4rrV!%r;HWr?,-(4"c&BCs7@c=rrPU@*W5pJ4Q6.is8Qu?ruM-Q?iO]Hrr3,/B`DAR
-rr3,&E;rnYrr3#ejn8WSbtn9;!rAp@r;Qincf'HTJcE"TJ,~>
-!ouXLrVlj<qu6]J7e?W\qBGn<!DCl?rrJR?pAY0P48]$YfgPf=!@c+hrrIA?rr3&lcfG')!Sfs<
-rr=)<rrc]Bs62?;rsAZHs/(#?:<rM[rr3Mf37n31Z>0F>gA1dK\p3N,rs+XEs8UUKoAKTI!$1k7
-!O6G=rr=)4rrV!%r;HWr?,-(4"c&BCs7@c=rrPU@*W5pJ4Q6.is8Qu?ruM-Q?iO]Hrr3,/B`DAR
-rr3,&E;rnYrr3#ejn8WSbtn9;!rAp@r;Qincf'HTJcE"TJ,~>
-!ouXLrVlj<qu6]J7e?W\qBGn<!DCl?rrJR?pAY0P48]$YfgPf=!@c+hrrIA?rr3&lcfG')!Sfs<
-rr=)<rrc]Bs62?;rsAZHs/(#?:<rM[rr3Mf37n31Z>0F>gA1dK\p3N,rs+XEs8UUKoAKTI!$1k7
-!O6G=rr=)4rrV!%r;HWr?,-(4"c&BCs7@c=rrPU@*W5pJ4Q6.is8Qu?ruM-Q?iO]Hrr3,/B`DAR
-rr3,&E;rnYrr3#ejn8WSbtn9;!rAp@r;Qincf'HTJcE"TJ,~>
-&*-#Ys3n9Z'XG$js8U/Gp@S@j_ljo_cN!oqT`24tMQ$:L"M:6SZI]$T#e?<UZIo3W.8^#L!I_`^
-rrU7AErH"=gd(f;!+#Q/"EMoikqi8;$2u%H_4Ua&cM1ZDZ3k[gfDklmGI"Mcl2L\`N0*E+#I/ug
-Z<@4XE;d'B>PS4!Z"s=<!$1b4!_EC^rVln(^\.U1X,+'eY%dk7!dpbLr;R:NVPeS:s-]%U'XF*e
-s-&cJZN$0ks,EHGZ36;Zs+m3EZ2ouZp&>';:]14ni]?u:!nmW^JcC<$Z2]=~>
-&*-#Ys3n9Z'XG$js8U/Gp@S@j_ljo_cN!oqT`24tMQ$:L"M:6SZI]$T#e?<UZIo3W.8^#L!I_`^
-rrU7AErH"=gd(f;!+#Q/"EMoikqi8;$2u%H_4Ua&cM1ZDZ3k[gfDklmGI"Mcl2L\`N0*E+#I/ug
-Z<@4XE;d'B>PS4!Z"s=<!$1b4!_EC^rVln(^\.U1X,+'eY%dk7!dpbLr;R:NVPeS:s-]%U'XF*e
-s-&cJZN$0ks,EHGZ36;Zs+m3EZ2ouZp&>';:]14ni]?u:!nmW^JcC<$Z2]=~>
-&*-#Ys3n9Z'XG$js8U/Gp@S@j_ljo_cN!oqT`24tMQ$:L"M:6SZI]$T#e?<UZIo3W.8^#L!I_`^
-rrU7AErH"=gd(f;!+#Q/"EMoikqi8;$2u%H_4Ua&cM1ZDZ3k[gfDklmGI"Mcl2L\`N0*E+#I/ug
-Z<@4XE;d'B>PS4!Z"s=<!$1b4!_EC^rVln(^\.U1X,+'eY%dk7!dpbLr;R:NVPeS:s-]%U'XF*e
-s-&cJZN$0ks,EHGZ36;Zs+m3EZ2ouZp&>';:]14ni]?u:!nmW^JcC<$Z2]=~>
-"QVjNs/Gp1"JPkqP32B9!/UVg!2'8j!/(8b!i5k1qj%?1s8RBDB`P=8rr@0?B`tdBs+0V;B`P[A
-rrV%kXSr/"l]1oC!;HG+"NUQBnq[/C!WIHErr\VIs60Gr!l^>crr3)9Bkc?#rrTfah<b.GXD)D;
-s8U7?B`Rf#rrLNGrVlkDp&>)W'(Pr#"'oo4T_ABfVeKj`qu6\Hq>:0mH?oJh"INm3Boi8n!0dCr
-!0mK_!07%m!1Eid!/^\h!1roa"/_B0:]14nOc0,:!pOEmJcC<$Z2]=~>
-"QVjNs/Gp1"JPkqP32B9!/UVg!2'8j!/(8b!i5k1qj%?1s8RBDB`P=8rr@0?B`tdBs+0V;B`P[A
-rrV%kXSr/"l]1oC!;HG+"NUQBnq[/C!WIHErr\VIs60Gr!l^>crr3)9Bkc?#rrTfah<b.GXD)D;
-s8U7?B`Rf#rrLNGrVlkDp&>)W'(Pr#"'oo4T_ABfVeKj`qu6\Hq>:0mH?oJh"INm3Boi8n!0dCr
-!0mK_!07%m!1Eid!/^\h!1roa"/_B0:]14nOc0,:!pOEmJcC<$Z2]=~>
-"QVjNs/Gp1"JPkqP32B9!/UVg!2'8j!/(8b!i5k1qj%?1s8RBDB`P=8rr@0?B`tdBs+0V;B`P[A
-rrV%kXSr/"l]1oC!;HG+"NUQBnq[/C!WIHErr\VIs60Gr!l^>crr3)9Bkc?#rrTfah<b.GXD)D;
-s8U7?B`Rf#rrLNGrVlkDp&>)W'(Pr#"'oo4T_ABfVeKj`qu6\Hq>:0mH?oJh"INm3Boi8n!0dCr
-!0mK_!07%m!1Eid!/^\h!1roa"/_B0:]14nOc0,:!pOEmJcC<$Z2]=~>
-!ouXLg&D&Xqh5$jXoAF5fYd^i/+NT<"0mQ6OPKj9*SgYsQ\C-or;Qb\JcC<$VuM8~>
-!ouXLg&D&Xqh5$jXoAF5fYd^i/+NT<"0mQ6OPKj9*SgYsQ\C-or;Qb\JcC<$VuM8~>
-!ouXLg&D&Xqh5$jXoAF5fYd^i/+NT<"0mQ6OPKj9*SgYsQ\C-or;Qb\JcC<$VuM8~>
-!ouXLJcEIa!fWE@eGfLhJcC<$JcGNFJ,~>
-!ouXLJcEIa!fWE@eGfLhJcC<$JcGNFJ,~>
-!ouXLJcEIa!fWE@eGfLhJcC<$JcGNFJ,~>
-!ouXLJcEIa!RUJfrrBugs+13$s7lVE~>
-!ouXLJcEIa!RUJfrrBugs+13$s7lVE~>
-!ouXLJcEIa!RUJfrrBugs+13$s7lVE~>
-!ouXLrr3't-73*urrYk?->%i/VuHj>-71q,rrYb<->A&2JcDGDJ,~>
-!ouXLrr3't-73*urrYk?->%i/VuHj>-71q,rrYb<->A&2JcDGDJ,~>
-!ouXLrr3't-73*urrYk?->%i/VuHj>-71q,rrYb<->A&2JcDGDJ,~>
-!ouXLrr3'UiUusDrs(%<p%\ReoM>H+"C_!'=r@2X",R!J62hi)Dt`u+!V%uF*!C]as#M+t*!3#;
-5lDZ''<(^+!:'M&".8rb)Z1QV`W%.\*!$Vlq>UKpc2AUb[/Bt%Y5TCHaoDD<V]6\VqtU0lr3?2?
-"47(m&H*RN*rl,4r>Ygpq#:BkQMhd'mem(cnKn27"SQdS*96en!V^p>*!%tfr;Qr]1&q:ID#PCZ
-7.^HYq"=;#o`#1Qo$gS;s)5IHo)Ae=s1<%Frr_uf`D;'bJcDGDJ,~>
-!ouXLrr3'UiUusDrs(%<p%\ReoM>H+"C_!'=r@2X",R!J62hi)Dt`u+!V%uF*!C]as#M+t*!3#;
-5lDZ''<(^+!:'M&".8rb)Z1QV`W%.\*!$Vlq>UKpc2AUb[/Bt%Y5TCHaoDD<V]6\VqtU0lr3?2?
-"47(m&H*RN*rl,4r>Ygpq#:BkQMhd'mem(cnKn27"SQdS*96en!V^p>*!%tfr;Qr]1&q:ID#PCZ
-7.^HYq"=;#o`#1Qo$gS;s)5IHo)Ae=s1<%Frr_uf`D;'bJcDGDJ,~>
-!ouXLrr3'UiUusDrs(%<p%\ReoM>H+"C_!'=r@2X",R!J62hi)Dt`u+!V%uF*!C]as#M+t*!3#;
-5lDZ''<(^+!:'M&".8rb)Z1QV`W%.\*!$Vlq>UKpc2AUb[/Bt%Y5TCHaoDD<V]6\VqtU0lr3?2?
-"47(m&H*RN*rl,4r>Ygpq#:BkQMhd'mem(cnKn27"SQdS*96en!V^p>*!%tfr;Qr]1&q:ID#PCZ
-7.^HYq"=;#o`#1Qo$gS;s)5IHo)Ae=s1<%Frr_uf`D;'bJcDGDJ,~>
-!ouXLrr3!SlMLS^.eEW=!F3J?rrfBBs&7&:rrGd@rr3"fK)#?H3o^/="Gr?B1$eT6!pk9@q#:=7
-rVlt4[Jq9@rrV=od/!\BodB_<"@^r?ZYfX?#PtQEs8R)Bqt^6mdn0N<"!m]c;>^@o_HQp<!$1k7
-!O6G=rr=)<rsRa\$ig7pJueqO6eV87!K`<?rrM"?rVm)U2_"e,J"HZ>!R=I<rr=)3rrXeBdRsN=
-!C,E2rrX;A\7GO;!R4Dks+13Ds*t~>
-!ouXLrr3!SlMLS^.eEW=!F3J?rrfBBs&7&:rrGd@rr3"fK)#?H3o^/="Gr?B1$eT6!pk9@q#:=7
-rVlt4[Jq9@rrV=od/!\BodB_<"@^r?ZYfX?#PtQEs8R)Bqt^6mdn0N<"!m]c;>^@o_HQp<!$1k7
-!O6G=rr=)<rsRa\$ig7pJueqO6eV87!K`<?rrM"?rVm)U2_"e,J"HZ>!R=I<rr=)3rrXeBdRsN=
-!C,E2rrX;A\7GO;!R4Dks+13Ds*t~>
-!ouXLrr3!SlMLS^.eEW=!F3J?rrfBBs&7&:rrGd@rr3"fK)#?H3o^/="Gr?B1$eT6!pk9@q#:=7
-rVlt4[Jq9@rrV=od/!\BodB_<"@^r?ZYfX?#PtQEs8R)Bqt^6mdn0N<"!m]c;>^@o_HQp<!$1k7
-!O6G=rr=)<rsRa\$ig7pJueqO6eV87!K`<?rrM"?rVm)U2_"e,J"HZ>!R=I<rr=)3rrXeBdRsN=
-!C,E2rrX;A\7GO;!R4Dks+13Ds*t~>
-!ouXLrr3!SlMLS^.eEW=!F3J?rt2;Os&7%P[C+B]s*rr+s6>N>q#:@Uir8r\M3Irokl.sigACmN
-QA>57!$2%<"D>.B*V99<!9="W!oJ@Aq#:Bf-iO&I7G$o\qu6of@a>#Rc!]u8rrLS?rVm!Gq9)Xm
-rj;k&s2%t<rr=)7rrKB?rVlj<rr3DWCt]p0s1Eg>s8TE?q#:AVNW/qYh`^u=#KpH:.KBEtT)S`j
-c;+<;!$1_3").AbJ,]HLGGkH=rrp[9]sY8prVlt,s36](rrLA?JcC<$U&TW~>
-!ouXLrr3!SlMLS^.eEW=!F3J?rt2;Os&7%P[C+B]s*rr+s6>N>q#:@Uir8r\M3Irokl.sigACmN
-QA>57!$2%<"D>.B*V99<!9="W!oJ@Aq#:Bf-iO&I7G$o\qu6of@a>#Rc!]u8rrLS?rVm!Gq9)Xm
-rj;k&s2%t<rr=)7rrKB?rVlj<rr3DWCt]p0s1Eg>s8TE?q#:AVNW/qYh`^u=#KpH:.KBEtT)S`j
-c;+<;!$1_3").AbJ,]HLGGkH=rrp[9]sY8prVlt,s36](rrLA?JcC<$U&TW~>
-!ouXLrr3!SlMLS^.eEW=!F3J?rt2;Os&7%P[C+B]s*rr+s6>N>q#:@Uir8r\M3Irokl.sigACmN
-QA>57!$2%<"D>.B*V99<!9="W!oJ@Aq#:Bf-iO&I7G$o\qu6of@a>#Rc!]u8rrLS?rVm!Gq9)Xm
-rj;k&s2%t<rr=)7rrKB?rVlj<rr3DWCt]p0s1Eg>s8TE?q#:AVNW/qYh`^u=#KpH:.KBEtT)S`j
-c;+<;!$1_3").AbJ,]HLGGkH=rrp[9]sY8prVlt,s36](rrLA?JcC<$U&TW~>
-!ouXLrr3!SlMLS^.eEW=%U?jMs6409s&42YAnH;1rVlreMkg%H#s.)Gs4gM9s"TH2Ac[D5rVlrX
-KX^^U!$2(="jT<>rtpjsAc[YJrr3)X=&duMrrMd?rr3/<.bF&Ai;T,MkPtS<GF=kBht[3Pdn0Q=
-"g\s>b;"YKAcn:t_HQs=!N[(3rrKB?rr38#*WQ/eI*Va4rr3,/Kpe?Lp\u"jNW9%XL->S:_fb#3
-.KBEtT)\ibHqsV>!$1G+!ILR>rruGICM%1'an>Z7btiojJcDGDJ,~>
-!ouXLrr3!SlMLS^.eEW=%U?jMs6409s&42YAnH;1rVlreMkg%H#s.)Gs4gM9s"TH2Ac[D5rVlrX
-KX^^U!$2(="jT<>rtpjsAc[YJrr3)X=&duMrrMd?rr3/<.bF&Ai;T,MkPtS<GF=kBht[3Pdn0Q=
-"g\s>b;"YKAcn:t_HQs=!N[(3rrKB?rr38#*WQ/eI*Va4rr3,/Kpe?Lp\u"jNW9%XL->S:_fb#3
-.KBEtT)\ibHqsV>!$1G+!ILR>rruGICM%1'an>Z7btiojJcDGDJ,~>
-!ouXLrr3!SlMLS^.eEW=%U?jMs6409s&42YAnH;1rVlreMkg%H#s.)Gs4gM9s"TH2Ac[D5rVlrX
-KX^^U!$2(="jT<>rtpjsAc[YJrr3)X=&duMrrMd?rr3/<.bF&Ai;T,MkPtS<GF=kBht[3Pdn0Q=
-"g\s>b;"YKAcn:t_HQs=!N[(3rrKB?rr38#*WQ/eI*Va4rr3,/Kpe?Lp\u"jNW9%XL->S:_fb#3
-.KBEtT)\ibHqsV>!$1G+!ILR>rruGICM%1'an>Z7btiojJcDGDJ,~>
-!ouXLrr3!SlMLS^.eEW=%^1>0I=>7;s/UbCs8Sj?rr3&pI\lc<$(cToI=F_Ms-e`BrrI\@rVlo)
-C%hQ,NrFG1Il4Y6MO4>B!FNP>rrgr*Jq)eUrrMtIrd=s#,M2<!If=p)Idd<rItE9%jS8`Ul@Jq_
-"b@<^jat#ArrX;AiILoU!J(s[rrLf'rd>''*<4K;aoDA]rr3,:J:`B,p\t98J,Xj)E]sH>JD'tp
-.KBF`J,XisBhnU+!$1D*!$2%<##o4*s8Sm?p&>';:P&Oss.TIC~>
-!ouXLrr3!SlMLS^.eEW=%^1>0I=>7;s/UbCs8Sj?rr3&pI\lc<$(cToI=F_Ms-e`BrrI\@rVlo)
-C%hQ,NrFG1Il4Y6MO4>B!FNP>rrgr*Jq)eUrrMtIrd=s#,M2<!If=p)Idd<rItE9%jS8`Ul@Jq_
-"b@<^jat#ArrX;AiILoU!J(s[rrLf'rd>''*<4K;aoDA]rr3,:J:`B,p\t98J,Xj)E]sH>JD'tp
-.KBF`J,XisBhnU+!$1D*!$2%<##o4*s8Sm?p&>';:P&Oss.TIC~>
-!ouXLrr3!SlMLS^.eEW=%^1>0I=>7;s/UbCs8Sj?rr3&pI\lc<$(cToI=F_Ms-e`BrrI\@rVlo)
-C%hQ,NrFG1Il4Y6MO4>B!FNP>rrgr*Jq)eUrrMtIrd=s#,M2<!If=p)Idd<rItE9%jS8`Ul@Jq_
-"b@<^jat#ArrX;AiILoU!J(s[rrLf'rd>''*<4K;aoDA]rr3,:J:`B,p\t98J,Xj)E]sH>JD'tp
-.KBF`J,XisBhnU+!$1D*!$2%<##o4*s8Sm?p&>';:P&Oss.TIC~>
-!ouXLrr3!SlMLS^.eET<"h("oZ`A*CrrJm@rr3":Y4V_tWLf]sM3Ii:!J-d?rrV2$`qKE4rViAi
-c"FH]rrHK?qu6[t`q]Q6nG]!_fgXN[*kVFO#sI26DSQ5O]l*B8rrD*YSc\%"q>:0o*WQ/qrgj/X
-*V]R6f)D6Do,[k1/H0&a$[;WhJ(Xf$TR?M6(!<,>TAMg3LH/dO>n;nos21G"VTqs7rr=)*rrJR?
-rr3!uaSu2?Uj;Y5!R4Dks+13Ds*t~>
-!ouXLrr3!SlMLS^.eET<"h("oZ`A*CrrJm@rr3":Y4V_tWLf]sM3Ii:!J-d?rrV2$`qKE4rViAi
-c"FH]rrHK?qu6[t`q]Q6nG]!_fgXN[*kVFO#sI26DSQ5O]l*B8rrD*YSc\%"q>:0o*WQ/qrgj/X
-*V]R6f)D6Do,[k1/H0&a$[;WhJ(Xf$TR?M6(!<,>TAMg3LH/dO>n;nos21G"VTqs7rr=)*rrJR?
-rr3!uaSu2?Uj;Y5!R4Dks+13Ds*t~>
-!ouXLrr3!SlMLS^.eET<"h("oZ`A*CrrJm@rr3":Y4V_tWLf]sM3Ii:!J-d?rrV2$`qKE4rViAi
-c"FH]rrHK?qu6[t`q]Q6nG]!_fgXN[*kVFO#sI26DSQ5O]l*B8rrD*YSc\%"q>:0o*WQ/qrgj/X
-*V]R6f)D6Do,[k1/H0&a$[;WhJ(Xf$TR?M6(!<,>TAMg3LH/dO>n;nos21G"VTqs7rr=)*rrJR?
-rr3!uaSu2?Uj;Y5!R4Dks+13Ds*t~>
-!ouXLrr3!SlMLS^.eEH8!NU5;rro0Ds8Q]>n,EEFPPb@\JXl`>!ROO0rrHc?qu6\(^&.g1<lXb2
-"%Ci/0`D"R4l><[rVlmUkk"fT,l.?;!$1q9!$1Y1!Zh=)rON+H`;cKXrr3#g/G&lDh`_"srji0:
-*mOT^!R=I<rr=)*rs&7lJcGaLaSu2?Uj;Y5!R4Dks+13Ds*t~>
-!ouXLrr3!SlMLS^.eEH8!NU5;rro0Ds8Q]>n,EEFPPb@\JXl`>!ROO0rrHc?qu6\(^&.g1<lXb2
-"%Ci/0`D"R4l><[rVlmUkk"fT,l.?;!$1q9!$1Y1!Zh=)rON+H`;cKXrr3#g/G&lDh`_"srji0:
-*mOT^!R=I<rr=)*rs&7lJcGaLaSu2?Uj;Y5!R4Dks+13Ds*t~>
-!ouXLrr3!SlMLS^.eEH8!NU5;rro0Ds8Q]>n,EEFPPb@\JXl`>!ROO0rrHc?qu6\(^&.g1<lXb2
-"%Ci/0`D"R4l><[rVlmUkk"fT,l.?;!$1q9!$1Y1!Zh=)rON+H`;cKXrr3#g/G&lDh`_"srji0:
-*mOT^!R=I<rr=)*rs&7lJcGaLaSu2?Uj;Y5!R4Dks+13Ds*t~>
-!ouXLrr3!SlMLS^.eEK9"jpfCs)6?brro0Ds(Jn>qYpWa.;\k\"grXDs&/:ars"%Es8VR[iqr`W
-cmaeIrre^NoD_,TrrZWAs$?V`!ER5>rrTPVV>C2pI&Hf-0`D"X4l?.@E<#rUZMXY$!gkFQqYp\*
-^AcSurVlj<qu6[obPhGBHR[hJrrGL?qu6sCs8VS?WW2u_h>R?T?RbjKrrW-`i;ETUoI9\;!q&\@
-r;Qa;qu6[Y8Flcaa]&6><65(=!Mk#6rrLA?JcC<$U&TW~>
-!ouXLrr3!SlMLS^.eEK9"jpfCs)6?brro0Ds(Jn>qYpWa.;\k\"grXDs&/:ars"%Es8VR[iqr`W
-cmaeIrre^NoD_,TrrZWAs$?V`!ER5>rrTPVV>C2pI&Hf-0`D"X4l?.@E<#rUZMXY$!gkFQqYp\*
-^AcSurVlj<qu6[obPhGBHR[hJrrGL?qu6sCs8VS?WW2u_h>R?T?RbjKrrW-`i;ETUoI9\;!q&\@
-r;Qa;qu6[Y8Flcaa]&6><65(=!Mk#6rrLA?JcC<$U&TW~>
-!ouXLrr3!SlMLS^.eEK9"jpfCs)6?brro0Ds(Jn>qYpWa.;\k\"grXDs&/:ars"%Es8VR[iqr`W
-cmaeIrre^NoD_,TrrZWAs$?V`!ER5>rrTPVV>C2pI&Hf-0`D"X4l?.@E<#rUZMXY$!gkFQqYp\*
-^AcSurVlj<qu6[obPhGBHR[hJrrGL?qu6sCs8VS?WW2u_h>R?T?RbjKrrW-`i;ETUoI9\;!q&\@
-r;Qa;qu6[Y8Flcaa]&6><65(=!Mk#6rrLA?JcC<$U&TW~>
-!ouXLrr3'Uh!jt7rrXb3nFZ_[#ZMc`9(r;bp.>6'"D[`3<Z:oV!0I3[!p^7krVm-\*#rScs8VaD
-rYu)rqZ$T`48SsYb6i:hrrDop*!Cojs8V9\*!;u6s5<hq!NQ(srrT5,PlC[_h#88rTD\`gb5E:c
-^]"31WuN+^r;HWsM[$WhrrBe4*!EqQs89%u*!>!rs0)DA!QtE?rrQs=fDbdMT`,o2f_YUJ/,oPO
-q/RGpkPY>]9EP%hrrdon*$4bLrrMaLrr3+B*#pR'rr3)nnF.IqrrF>jo)AmkXapFYbPqMBWIagD
-"7krt:P&Oss.TIC~>
-!ouXLrr3'Uh!jt7rrXb3nFZ_[#ZMc`9(r;bp.>6'"D[`3<Z:oV!0I3[!p^7krVm-\*#rScs8VaD
-rYu)rqZ$T`48SsYb6i:hrrDop*!Cojs8V9\*!;u6s5<hq!NQ(srrT5,PlC[_h#88rTD\`gb5E:c
-^]"31WuN+^r;HWsM[$WhrrBe4*!EqQs89%u*!>!rs0)DA!QtE?rrQs=fDbdMT`,o2f_YUJ/,oPO
-q/RGpkPY>]9EP%hrrdon*$4bLrrMaLrr3+B*#pR'rr3)nnF.IqrrF>jo)AmkXapFYbPqMBWIagD
-"7krt:P&Oss.TIC~>
-!ouXLrr3'Uh!jt7rrXb3nFZ_[#ZMc`9(r;bp.>6'"D[`3<Z:oV!0I3[!p^7krVm-\*#rScs8VaD
-rYu)rqZ$T`48SsYb6i:hrrDop*!Cojs8V9\*!;u6s5<hq!NQ(srrT5,PlC[_h#88rTD\`gb5E:c
-^]"31WuN+^r;HWsM[$WhrrBe4*!EqQs89%u*!>!rs0)DA!QtE?rrQs=fDbdMT`,o2f_YUJ/,oPO
-q/RGpkPY>]9EP%hrrdon*$4bLrrMaLrr3+B*#pR'rr3)nnF.IqrrF>jo)AmkXapFYbPqMBWIagD
-"7krt:P&Oss.TIC~>
-!ouXLrr3(#.k>-.rrZ"H.r+/F!R+C#rrK-?j8T.9PiMcEB=?k#!CbW#rrFA?k5PO+.k=!arrMm@
-h>[RM.k>Ifs+13Ds*t~>
-!ouXLrr3(#.k>-.rrZ"H.r+/F!R+C#rrK-?j8T.9PiMcEB=?k#!CbW#rrFA?k5PO+.k=!arrMm@
-h>[RM.k>Ifs+13Ds*t~>
-!ouXLrr3(#.k>-.rrZ"H.r+/F!R+C#rrK-?j8T.9PiMcEB=?k#!CbW#rrFA?k5PO+.k=!arrMm@
-h>[RM.k>Ifs+13Ds*t~>
-!ouXLf)GgITn2;)!q2;?jSo;DK<jS<!o8"AjSo;1B&htP!m4UAh#@Dd@Y+Q1s+14Gs*t~>
-!ouXLf)GgITn2;)!q2;?jSo;DK<jS<!o8"AjSo;1B&htP!m4UAh#@Dd@Y+Q1s+14Gs*t~>
-!ouXLf)GgITn2;)!q2;?jSo;DK<jS<!o8"AjSo;1B&htP!m4UAh#@Dd@Y+Q1s+14Gs*t~>
-!ouXLf)GdAK&ck3h3[1%!QhG'rrKLBj8T.Pe`6Z1MsB's!-NkmJcC<$q#>j~>
-!ouXLf)GdAK&ck3h3[1%!QhG'rrKLBj8T.Pe`6Z1MsB's!-NkmJcC<$q#>j~>
-!ouXLf)GdAK&ck3h3[1%!QhG'rrKLBj8T.Pe`6Z1MsB's!-NkmJcC<$q#>j~>
-!ouXLL&V1cdZa\2NkPGmp\oXB_RKOFg\u[&hYmHTaMn&V!Q<*2s-Nb9~>
-!ouXLL&V1cdZa\2NkPGmp\oXB_RKOFg\u[&hYmHTaMn&V!Q<*2s-Nb9~>
-!ouXLL&V1cdZa\2NkPGmp\oXB_RKOFg\u[&hYmHTaMn&V!Q<*2s-Nb9~>
-"lqsOs8S>_@fVJerrJ2Co)A`3GfB[_Fl<9$^4G)<rrUAR]C#V#c!t8,r;Qi,A'k5T!S*h3rrSHq
-B[m%_G,G3;",$1/N;ih\O(7qad\H:2"b:Cgs2))8rr[EDb,b79"R[8"SC%98#OmHI]"7qAr;L1+
-Xks'Xj8EHfg%PFR`if?(j8]/>S=DXRrVlrKIEMKf!muA@JcD,;J,~>
-"lqsOs8S>_@fVJerrJ2Co)A`3GfB[_Fl<9$^4G)<rrUAR]C#V#c!t8,r;Qi,A'k5T!S*h3rrSHq
-B[m%_G,G3;",$1/N;ih\O(7qad\H:2"b:Cgs2))8rr[EDb,b79"R[8"SC%98#OmHI]"7qAr;L1+
-Xks'Xj8EHfg%PFR`if?(j8]/>S=DXRrVlrKIEMKf!muA@JcD,;J,~>
-"lqsOs8S>_@fVJerrJ2Co)A`3GfB[_Fl<9$^4G)<rrUAR]C#V#c!t8,r;Qi,A'k5T!S*h3rrSHq
-B[m%_G,G3;",$1/N;ih\O(7qad\H:2"b:Cgs2))8rr[EDb,b79"R[8"SC%98#OmHI]"7qAr;L1+
-Xks'Xj8EHfg%PFR`if?(j8]/>S=DXRrVlrKIEMKf!muA@JcD,;J,~>
-"lqsOs-ArL\-AC]s8U8Gp@J:b^VS_*!V0skrr=(trrJC?r;Qi]\DZcS"6d6nWVlbthRMhLrrK]?
-nc&`YPdn,0hZ!V$j8/cV*rc*;$o@/%6N@(IR(-<![:oR>!FYftrs!qIZ=X'*JbK*G*WN'\pAY0]
-0)PYRju37"NrT,`rjVu\[,Crbo3D7(!Ft<errf'Bs%:`;rrHK?r;Qe>W;HSqCpAQkR/_[~>
-"lqsOs-ArL\-AC]s8U8Gp@J:b^VS_*!V0skrr=(trrJC?r;Qi]\DZcS"6d6nWVlbthRMhLrrK]?
-nc&`YPdn,0hZ!V$j8/cV*rc*;$o@/%6N@(IR(-<![:oR>!FYftrs!qIZ=X'*JbK*G*WN'\pAY0]
-0)PYRju37"NrT,`rjVu\[,Crbo3D7(!Ft<errf'Bs%:`;rrHK?r;Qe>W;HSqCpAQkR/_[~>
-"lqsOs-ArL\-AC]s8U8Gp@J:b^VS_*!V0skrr=(trrJC?r;Qi]\DZcS"6d6nWVlbthRMhLrrK]?
-nc&`YPdn,0hZ!V$j8/cV*rc*;$o@/%6N@(IR(-<![:oR>!FYftrs!qIZ=X'*JbK*G*WN'\pAY0]
-0)PYRju37"NrT,`rjVu\[,Crbo3D7(!Ft<errf'Bs%:`;rrHK?r;Qe>W;HSqCpAQkR/_[~>
-"lqsOs)#">rrKc?rr3#R6HoH1m4[i+!$1%u!l&a@j8T.$WV6>m\mk40"E(CBpa"_r!CYT;rrHE@
-qu7'\l.SK(s(/\>b:?iZrr3!CqXsjm9[NaJ.rFSFrrX;AW-.h6!U_T;rs.[Es,N->ruM+=!JZs!
-rrW,+CB"56hYHpG"JU5B9%*Y;!FNP<rrV@pci!eEk$Q\js-`n;~>
-"lqsOs)#">rrKc?rr3#R6HoH1m4[i+!$1%u!l&a@j8T.$WV6>m\mk40"E(CBpa"_r!CYT;rrHE@
-qu7'\l.SK(s(/\>b:?iZrr3!CqXsjm9[NaJ.rFSFrrX;AW-.h6!U_T;rs.[Es,N->ruM+=!JZs!
-rrW,+CB"56hYHpG"JU5B9%*Y;!FNP<rrV@pci!eEk$Q\js-`n;~>
-"lqsOs)#">rrKc?rr3#R6HoH1m4[i+!$1%u!l&a@j8T.$WV6>m\mk40"E(CBpa"_r!CYT;rrHE@
-qu7'\l.SK(s(/\>b:?iZrr3!CqXsjm9[NaJ.rFSFrrX;AW-.h6!U_T;rs.[Es,N->ruM+=!JZs!
-rrW,+CB"56hYHpG"JU5B9%*Y;!FNP<rrV@pci!eEk$Q\js-`n;~>
-$KOKTs7Z<hs3js?rVlrY6h^6R!_aRerVlol6N.r*EV]V6!p^A$rr3!gf_uj%J,91/m/R*]LB%9=
-)?h*#PkY1Xn,44/S,<4+cMedcU\t/Xr>Z3Fr;JVFV]6\]rVt:D*!NkNs8TB>r;QlD*#=n?rrN)2
-r>Yj_rq??mF/f-<"0W[Z[f6:.\m:*j*!#='rrQ^Air/l^DQa$>o)JQDrYuV$o`(\EnkpG]p&C<n
-*5aNjp&G&<rYu2-pA^*l#8eFOrVlm\iVWWkoD\^\s8Vfis8MN[s8R!W"<8Cns8VdbrYuMaq#C!a
-s8DH\s8PmA,9.\Iq>UBrnc/I[pAY0]0)PYRrU^'aq#C@7rpg2AQiI(9rVm%8_#OG]>5\C+/,u]"
-s'!eLdet-g\,H<I&cNaP^A\&Lr>Z*^rr<#op]'m`r;Qe)^&.g1=2t.;!D^pks-`n;~>
-$KOKTs7Z<hs3js?rVlrY6h^6R!_aRerVlol6N.r*EV]V6!p^A$rr3!gf_uj%J,91/m/R*]LB%9=
-)?h*#PkY1Xn,44/S,<4+cMedcU\t/Xr>Z3Fr;JVFV]6\]rVt:D*!NkNs8TB>r;QlD*#=n?rrN)2
-r>Yj_rq??mF/f-<"0W[Z[f6:.\m:*j*!#='rrQ^Air/l^DQa$>o)JQDrYuV$o`(\EnkpG]p&C<n
-*5aNjp&G&<rYu2-pA^*l#8eFOrVlm\iVWWkoD\^\s8Vfis8MN[s8R!W"<8Cns8VdbrYuMaq#C!a
-s8DH\s8PmA,9.\Iq>UBrnc/I[pAY0]0)PYRrU^'aq#C@7rpg2AQiI(9rVm%8_#OG]>5\C+/,u]"
-s'!eLdet-g\,H<I&cNaP^A\&Lr>Z*^rr<#op]'m`r;Qe)^&.g1=2t.;!D^pks-`n;~>
-$KOKTs7Z<hs3js?rVlrY6h^6R!_aRerVlol6N.r*EV]V6!p^A$rr3!gf_uj%J,91/m/R*]LB%9=
-)?h*#PkY1Xn,44/S,<4+cMedcU\t/Xr>Z3Fr;JVFV]6\]rVt:D*!NkNs8TB>r;QlD*#=n?rrN)2
-r>Yj_rq??mF/f-<"0W[Z[f6:.\m:*j*!#='rrQ^Air/l^DQa$>o)JQDrYuV$o`(\EnkpG]p&C<n
-*5aNjp&G&<rYu2-pA^*l#8eFOrVlm\iVWWkoD\^\s8Vfis8MN[s8R!W"<8Cns8VdbrYuMaq#C!a
-s8DH\s8PmA,9.\Iq>UBrnc/I[pAY0]0)PYRrU^'aq#C@7rpg2AQiI(9rVm%8_#OG]>5\C+/,u]"
-s'!eLdet-g\,H<I&cNaP^A\&Lr>Z*^rr<#op]'m`r;Qe)^&.g1=2t.;!D^pks-`n;~>
-!ouXLrVloJ8c&Gfd7a04!W"#=rrGd@rr3"fK)YcOm3ulNrrG/,rr37\PQ*B's8S[>rr3#d0D,8E
-*W?!@JAM6u:&b.n?bQ@:$Wb:Ii!OT+s%^l4-iO&KA)71bZ>079!K<->rrLn@rVlmLnFlk_F/f':
-!Gf"?rrTbCPlC[_*W?!JF=H>Os7+ZGP1KO1s.Fc=rt4`Os8REaaoCN]s,`6?39B$\qu6]J7fNDg
-7+hD:!Bf?,rrF>?rVlmtaSu2?ZB"_[#"'X1s8S:?n,EFV0(f/D)ZD/q'pnt#5McA>l>"B>s34C<
-rs0\GVfi#8c1WL_rrYdBmP"P="$YT'2uWaW<pTGY!FNP;rrH3@qu6[lc@Q"`s*t~>
-!ouXLrVloJ8c&Gfd7a04!W"#=rrGd@rr3"fK)YcOm3ulNrrG/,rr37\PQ*B's8S[>rr3#d0D,8E
-*W?!@JAM6u:&b.n?bQ@:$Wb:Ii!OT+s%^l4-iO&KA)71bZ>079!K<->rrLn@rVlmLnFlk_F/f':
-!Gf"?rrTbCPlC[_*W?!JF=H>Os7+ZGP1KO1s.Fc=rt4`Os8REaaoCN]s,`6?39B$\qu6]J7fNDg
-7+hD:!Bf?,rrF>?rVlmtaSu2?ZB"_[#"'X1s8S:?n,EFV0(f/D)ZD/q'pnt#5McA>l>"B>s34C<
-rs0\GVfi#8c1WL_rrYdBmP"P="$YT'2uWaW<pTGY!FNP;rrH3@qu6[lc@Q"`s*t~>
-!ouXLrVloJ8c&Gfd7a04!W"#=rrGd@rr3"fK)YcOm3ulNrrG/,rr37\PQ*B's8S[>rr3#d0D,8E
-*W?!@JAM6u:&b.n?bQ@:$Wb:Ii!OT+s%^l4-iO&KA)71bZ>079!K<->rrLn@rVlmLnFlk_F/f':
-!Gf"?rrTbCPlC[_*W?!JF=H>Os7+ZGP1KO1s.Fc=rt4`Os8REaaoCN]s,`6?39B$\qu6]J7fNDg
-7+hD:!Bf?,rrF>?rVlmtaSu2?ZB"_[#"'X1s8S:?n,EFV0(f/D)ZD/q'pnt#5McA>l>"B>s34C<
-rs0\GVfi#8c1WL_rrYdBmP"P="$YT'2uWaW<pTGY!FNP;rrH3@qu6[lc@Q"`s*t~>
-!ouXLrr3#?<Vl^sqPAT4rrMs?rVlmkdf07IRY(>7!BT6>rs4IFs"Wj>s.4]=rrML?o`"n3qYpO9
-rVlt,^&Rp,\cb7;s56$=rr[`)pfIF*!:9^b!O?J:rrJ(?rr3#S62gfa/F`B5!HY7;rrHo@rr3#1
-@f?<,*VfX@^dT:t7IU9VTR?b='_![Rs*LL?s6a\&OH'9"jki6$s4@7]!M)^ZrrGO?qu6[ZiTpLE
-,5V9<!J7$E\cC4RrrGL?rr3"_MXUQGmOn/3!$2(=$&4fHs7.Z>s*^O=rrLA@r;R$Ds/1#>:X/S[
-rVlsif'Y3irrYIAlsB\&!9s+T!FNP;rrH3@qu6[lc@Q"`s*t~>
-!ouXLrr3#?<Vl^sqPAT4rrMs?rVlmkdf07IRY(>7!BT6>rs4IFs"Wj>s.4]=rrML?o`"n3qYpO9
-rVlt,^&Rp,\cb7;s56$=rr[`)pfIF*!:9^b!O?J:rrJ(?rr3#S62gfa/F`B5!HY7;rrHo@rr3#1
-@f?<,*VfX@^dT:t7IU9VTR?b='_![Rs*LL?s6a\&OH'9"jki6$s4@7]!M)^ZrrGO?qu6[ZiTpLE
-,5V9<!J7$E\cC4RrrGL?rr3"_MXUQGmOn/3!$2(=$&4fHs7.Z>s*^O=rrLA@r;R$Ds/1#>:X/S[
-rVlsif'Y3irrYIAlsB\&!9s+T!FNP;rrH3@qu6[lc@Q"`s*t~>
-!ouXLrr3#?<Vl^sqPAT4rrMs?rVlmkdf07IRY(>7!BT6>rs4IFs"Wj>s.4]=rrML?o`"n3qYpO9
-rVlt,^&Rp,\cb7;s56$=rr[`)pfIF*!:9^b!O?J:rrJ(?rr3#S62gfa/F`B5!HY7;rrHo@rr3#1
-@f?<,*VfX@^dT:t7IU9VTR?b='_![Rs*LL?s6a\&OH'9"jki6$s4@7]!M)^ZrrGO?qu6[ZiTpLE
-,5V9<!J7$E\cC4RrrGL?rr3"_MXUQGmOn/3!$2(=$&4fHs7.Z>s*^O=rrLA@r;R$Ds/1#>:X/S[
-rVlsif'Y3irrYIAlsB\&!9s+T!FNP;rrH3@qu6[lc@Q"`s*t~>
-#NS0Qs8T`Ko(r@eT76G4!W"#=rrGd@rr3"fK)#?H3o^/=#`4cF1$el>Spp\=!UVQ4rr=)9rr=)<
-rrZWAs-rsq"<kebi&po:!9a;h"c`$Os0?D9rrJ(?rr3#S62gfa/F`B5!HY7;rrHo@rr3#1@f?<,
-*VTL6DQ`s<!MFi>rrqmCs8RG?r;R(bNW2!"h`_"eHN%=R;+CQb!CYT;rrG7@li-u0qY^?nKDo9[
-GKfj^!CPQ>rrJ=@n,EFV0(f/D*WH'FL6qr?pM7=mJ&M?d!R4I=rs0\GVfi#8c1WL_rrYdBmP"P=
-"$YT7OoAbhbk(i8?bQ@:!E[;<rrGm?JcD/<J,~>
-#NS0Qs8T`Ko(r@eT76G4!W"#=rrGd@rr3"fK)#?H3o^/=#`4cF1$el>Spp\=!UVQ4rr=)9rr=)<
-rrZWAs-rsq"<kebi&po:!9a;h"c`$Os0?D9rrJ(?rr3#S62gfa/F`B5!HY7;rrHo@rr3#1@f?<,
-*VTL6DQ`s<!MFi>rrqmCs8RG?r;R(bNW2!"h`_"eHN%=R;+CQb!CYT;rrG7@li-u0qY^?nKDo9[
-GKfj^!CPQ>rrJ=@n,EFV0(f/D*WH'FL6qr?pM7=mJ&M?d!R4I=rs0\GVfi#8c1WL_rrYdBmP"P=
-"$YT7OoAbhbk(i8?bQ@:!E[;<rrGm?JcD/<J,~>
-#NS0Qs8T`Ko(r@eT76G4!W"#=rrGd@rr3"fK)#?H3o^/=#`4cF1$el>Spp\=!UVQ4rr=)9rr=)<
-rrZWAs-rsq"<kebi&po:!9a;h"c`$Os0?D9rrJ(?rr3#S62gfa/F`B5!HY7;rrHo@rr3#1@f?<,
-*VTL6DQ`s<!MFi>rrqmCs8RG?r;R(bNW2!"h`_"eHN%=R;+CQb!CYT;rrG7@li-u0qY^?nKDo9[
-GKfj^!CPQ>rrJ=@n,EFV0(f/D*WH'FL6qr?pM7=mJ&M?d!R4I=rs0\GVfi#8c1WL_rrYdBmP"P=
-"$YT7OoAbhbk(i8?bQ@:!E[;<rrGm?JcD/<J,~>
-#38'Ps0mL>qYpS^L@bEGqBGn<!DCl?rrJR?rr3&lJuSbL!BP?%rs4IFs"Wj>s.4]=rrqdCs8Td\
-q#:=7rVlu-g]&;mrrZWAruM+="hQ>*s56$;rrMt]rVm'jfDkm'J&M6a!K<->rrLn@rVlmLnFlk_
-F/f':!Gf"?rrTbe_>aH7*W?!>Y(H0ArrI#?rVlnkI/a-Ln1=V>H_UH<%Zl\M39B$\s+cm>s7pM$
-rr3*hf`1-5rr3%hJc>$9#9s$Ef"(g]jSo/[Uj;b8!CPQ>rrJ=@pAY3]K!G:S!U_T4rr=)=rrIk@
-rr3)N8",&.rt!@Ns1?e\ruV3<FoP7^p*Tb<"%Ur10E(nQ52PB[rVlngoC`+a?bQC;!kWsAr;Qi-
-J*-\/!jdLEJcDGDJ,~>
-#38'Ps0mL>qYpS^L@bEGqBGn<!DCl?rrJR?rr3&lJuSbL!BP?%rs4IFs"Wj>s.4]=rrqdCs8Td\
-q#:=7rVlu-g]&;mrrZWAruM+="hQ>*s56$;rrMt]rVm'jfDkm'J&M6a!K<->rrLn@rVlmLnFlk_
-F/f':!Gf"?rrTbe_>aH7*W?!>Y(H0ArrI#?rVlnkI/a-Ln1=V>H_UH<%Zl\M39B$\s+cm>s7pM$
-rr3*hf`1-5rr3%hJc>$9#9s$Ef"(g]jSo/[Uj;b8!CPQ>rrJ=@pAY3]K!G:S!U_T4rr=)=rrIk@
-rr3)N8",&.rt!@Ns1?e\ruV3<FoP7^p*Tb<"%Ur10E(nQ52PB[rVlngoC`+a?bQC;!kWsAr;Qi-
-J*-\/!jdLEJcDGDJ,~>
-#38'Ps0mL>qYpS^L@bEGqBGn<!DCl?rrJR?rr3&lJuSbL!BP?%rs4IFs"Wj>s.4]=rrqdCs8Td\
-q#:=7rVlu-g]&;mrrZWAruM+="hQ>*s56$;rrMt]rVm'jfDkm'J&M6a!K<->rrLn@rVlmLnFlk_
-F/f':!Gf"?rrTbe_>aH7*W?!>Y(H0ArrI#?rVlnkI/a-Ln1=V>H_UH<%Zl\M39B$\s+cm>s7pM$
-rr3*hf`1-5rr3%hJc>$9#9s$Ef"(g]jSo/[Uj;b8!CPQ>rrJ=@pAY3]K!G:S!U_T4rr=)=rrIk@
-rr3)N8",&.rt!@Ns1?e\ruV3<FoP7^p*Tb<"%Ur10E(nQ52PB[rVlngoC`+a?bQC;!kWsAr;Qi-
-J*-\/!jdLEJcDGDJ,~>
-"lqsOs(t7(Ad+k-s,iH?o`#2OA.E4Bs8TQHral1Wc2R_Ekl=QZrrG0:ralakeGlXcAnH==_uKc3
-OCi*Ug\CdKUA\[+K]2qOrVlt,^&OGuAd*cC`rFsZr;QfpOo8kmMspZF!L8H=rs7Fm3'([/s7)'H
-Ac\"hq#:rOAhHJ`n,NF0Ah-A`o`)KaJ,X$[TDeck=odLZrrI#?rVloOJGs-aBrh:6H_UH<%Zl\M
-YOp^8s3H%(AnZ`brr3,.DJ!jtrVlm>rp9XiVe9Uc`rEYjral1UanYl:6eVJ=!L&E7rrUmS@/^*+
-mOn/3!2BI)!IiSurr_\IHHlEg&*o'aK&$D+P@d08Ar5jNOo8koMsgA%rFQ.ko(A%AAc[qbp&>&"
-^&7m2FK#*:!Go%<rrQ[1eq*jps*t~>
-"lqsOs(t7(Ad+k-s,iH?o`#2OA.E4Bs8TQHral1Wc2R_Ekl=QZrrG0:ralakeGlXcAnH==_uKc3
-OCi*Ug\CdKUA\[+K]2qOrVlt,^&OGuAd*cC`rFsZr;QfpOo8kmMspZF!L8H=rs7Fm3'([/s7)'H
-Ac\"hq#:rOAhHJ`n,NF0Ah-A`o`)KaJ,X$[TDeck=odLZrrI#?rVloOJGs-aBrh:6H_UH<%Zl\M
-YOp^8s3H%(AnZ`brr3,.DJ!jtrVlm>rp9XiVe9Uc`rEYjral1UanYl:6eVJ=!L&E7rrUmS@/^*+
-mOn/3!2BI)!IiSurr_\IHHlEg&*o'aK&$D+P@d08Ar5jNOo8koMsgA%rFQ.ko(A%AAc[qbp&>&"
-^&7m2FK#*:!Go%<rrQ[1eq*jps*t~>
-"lqsOs(t7(Ad+k-s,iH?o`#2OA.E4Bs8TQHral1Wc2R_Ekl=QZrrG0:ralakeGlXcAnH==_uKc3
-OCi*Ug\CdKUA\[+K]2qOrVlt,^&OGuAd*cC`rFsZr;QfpOo8kmMspZF!L8H=rs7Fm3'([/s7)'H
-Ac\"hq#:rOAhHJ`n,NF0Ah-A`o`)KaJ,X$[TDeck=odLZrrI#?rVloOJGs-aBrh:6H_UH<%Zl\M
-YOp^8s3H%(AnZ`brr3,.DJ!jtrVlm>rp9XiVe9Uc`rEYjral1UanYl:6eVJ=!L&E7rrUmS@/^*+
-mOn/3!2BI)!IiSurr_\IHHlEg&*o'aK&$D+P@d08Ar5jNOo8koMsgA%rFQ.ko(A%AAc[qbp&>&"
-^&7m2FK#*:!Go%<rrQ[1eq*jps*t~>
-"QVjNs4.%T"O[8Lb4G6)!6+rF!7:`F!5e`C!7LiG!r97Jrr3![ir6=cfDbdR]=#&og>`,3"nB".
-h#DQp8cls2rVa,+iW&qlrVluIm/Qn\[0>C3mf3"#qu6ZgrNuXkrVlrWI)#[\!:Tlo"7Z?jm/=<m
-o(DiO!rDr[rNub%s8V3Z[06@+ldFMd[/g.'rrUNSpAP!le`Zl1!8IL\!4i+/!SQQ3rrM$6rr3;u
-a8c1h[C*O9ao25@^pV)XrrKi?li.$p[E\^N!6+rF!7:K?!Qk!5rrM'6pAY3dL1'u["RZ^k8u_Rb
-!<2u*!8.5L!UA,1rrVDlh>[E[p9f'B[HRYjo_l0"li7"TrNuaps8VQd[/f[irrgP<8fYPCrrTHY
-k5>5\X,-!:rrUWVo7?q8s*t~>
-"QVjNs4.%T"O[8Lb4G6)!6+rF!7:`F!5e`C!7LiG!r97Jrr3![ir6=cfDbdR]=#&og>`,3"nB".
-h#DQp8cls2rVa,+iW&qlrVluIm/Qn\[0>C3mf3"#qu6ZgrNuXkrVlrWI)#[\!:Tlo"7Z?jm/=<m
-o(DiO!rDr[rNub%s8V3Z[06@+ldFMd[/g.'rrUNSpAP!le`Zl1!8IL\!4i+/!SQQ3rrM$6rr3;u
-a8c1h[C*O9ao25@^pV)XrrKi?li.$p[E\^N!6+rF!7:K?!Qk!5rrM'6pAY3dL1'u["RZ^k8u_Rb
-!<2u*!8.5L!UA,1rrVDlh>[E[p9f'B[HRYjo_l0"li7"TrNuaps8VQd[/f[irrgP<8fYPCrrTHY
-k5>5\X,-!:rrUWVo7?q8s*t~>
-"QVjNs4.%T"O[8Lb4G6)!6+rF!7:`F!5e`C!7LiG!r97Jrr3![ir6=cfDbdR]=#&og>`,3"nB".
-h#DQp8cls2rVa,+iW&qlrVluIm/Qn\[0>C3mf3"#qu6ZgrNuXkrVlrWI)#[\!:Tlo"7Z?jm/=<m
-o(DiO!rDr[rNub%s8V3Z[06@+ldFMd[/g.'rrUNSpAP!le`Zl1!8IL\!4i+/!SQQ3rrM$6rr3;u
-a8c1h[C*O9ao25@^pV)XrrKi?li.$p[E\^N!6+rF!7:K?!Qk!5rrM'6pAY3dL1'u["RZ^k8u_Rb
-!<2u*!8.5L!UA,1rrVDlh>[E[p9f'B[HRYjo_l0"li7"TrNuaps8VQd[/f[irrgP<8fYPCrrTHY
-k5>5\X,-!:rrUWVo7?q8s*t~>
-!ouXLd/O,-ip?[Fh##J!nC@O>kO7p?!;u]@!<0,#!7o'f!PDh=rrDurd/`FerrK$?h>[KGrm:`;
-rVloalMLS^lKj*%QiDR~>
-!ouXLd/O,-ip?[Fh##J!nC@O>kO7p?!;u]@!<0,#!7o'f!PDh=rrDurd/`FerrK$?h>[KGrm:`;
-rVloalMLS^lKj*%QiDR~>
-!ouXLd/O,-ip?[Fh##J!nC@O>kO7p?!;u]@!<0,#!7o'f!PDh=rrDurd/`FerrK$?h>[KGrm:`;
-rVloalMLS^lKj*%QiDR~>
-!ouXLd/O,5jFR>grrV%Uo[NmAWdB<sJcF[.J,~>
-!ouXLd/O,5jFR>grrV%Uo[NmAWdB<sJcF[.J,~>
-!ouXLd/O,5jFR>grrV%Uo[NmAWdB<sJcF[.J,~>
-!ouXLJcC<$JcC<$K`?Q~>
-!ouXLJcC<$JcC<$K`?Q~>
-!ouXLJcC<$JcC<$K`?Q~>
-#in9Rs8UK[]uSt/!65#O!82r'JcC<$JcE(VJ,~>
-#in9Rs8UK[]uSt/!65#O!82r'JcC<$JcE(VJ,~>
-#in9Rs8UK[]uSt/!65#O!82r'JcC<$JcE(VJ,~>
-$04BSs2f=g?>-n,rrKZDra#VPe:IXNs+13$s0VfV~>
-$04BSs2f=g?>-n,rrKZDra#VPe:IXNs+13$s0VfV~>
-$04BSs2f=g?>-n,rrKZDra#VPe:IXNs+13$s0VfV~>
-$04BSs)#"?s4Kcsrs#$Hqu?]9_L_`<s+13$s0VfV~>
-$04BSs)#"?s4Kcsrs#$Hqu?]9_L_`<s+13$s0VfV~>
-$04BSs)#"?s4Kcsrs#$Hqu?]9_L_`<s+13$s0VfV~>
-%HKfWs)#"?s4Kd>s.n3ErrLQFrr3"HrVca&Rc"$ks2_GEs+C;b!2TVo"=7VnBrV+3!."QX!3UnQ
-JcC<$JcE@^J,~>
-%HKfWs)#"?s4Kd>s.n3ErrLQFrr3"HrVca&Rc"$ks2_GEs+C;b!2TVo"=7VnBrV+3!."QX!3UnQ
-JcC<$JcE@^J,~>
-%HKfWs)#"?s4Kd>s.n3ErrLQFrr3"HrVca&Rc"$ks2_GEs+C;b!2TVo"=7VnBrV+3!."QX!3UnQ
-JcC<$JcE@^J,~>
-%HKfWs(sVe9L2&Gs(&Y=rrKK@rr3!Bqu-O$;p"k[s.ao?JY<"K"H'/XH_gYI"Ga/[Ff55F!J@_0
-s+13$s+13_s*t~>
-%HKfWs(sVe9L2&Gs(&Y=rrKK@rr3!Bqu-O$;p"k[s.ao?JY<"K"H'/XH_gYI"Ga/[Ff55F!J@_0
-s+13$s+13_s*t~>
-%HKfWs(sVe9L2&Gs(&Y=rrKK@rr3!Bqu-O$;p"k[s.ao?JY<"K"H'/XH_gYI"Ga/[Ff55F!J@_0
-s+13$s+13_s*t~>
-'')>\s)!eBc`fe:s5I;>s4Ui>rr3!Bqu-O$;p"k[s.ao?9@Eh>"IarB6JDG="Hn]C3o^/=!U8m#
-s+13$s+13_s*t~>
-'')>\s)!eBc`fe:s5I;>s4Ui>rr3!Bqu-O$;p"k[s.ao?9@Eh>"IarB6JDG="Hn]C3o^/=!U8m#
-s+13$s+13_s*t~>
-'')>\s)!eBc`fe:s5I;>s4Ui>rr3!Bqu-O$;p"k[s.ao?9@Eh>"IarB6JDG="Hn]C3o^/=!U8m#
-s+13$s+13_s*t~>
-$04BSs)#"?s4Kd=rrc$Brle7<rrF;?rVm1&and4]UO)r5df07LRY(Q+gA_*WP)KA)1)q9LJcC<$
-JcC<$])R9~>
-$04BSs)#"?s4Kd=rrc$Brle7<rrF;?rVm1&and4]UO)r5df07LRY(Q+gA_*WP)KA)1)q9LJcC<$
-JcC<$])R9~>
-$04BSs)#"?s4Kd=rrc$Brle7<rrF;?rVm1&and4]UO)r5df07LRY(Q+gA_*WP)KA)1)q9LJcC<$
-JcC<$])R9~>
-$04BSs)#"?s4Kd<rrO\00)Y_[+oD#ss8Q$>qBGs7HN-Xdrr3+iK)\0krr3+aMuPitrr3#fn:CUj
-s+13$s1JA^~>
-$04BSs)#"?s4Kd<rrO\00)Y_[+oD#ss8Q$>qBGs7HN-Xdrr3+iK)\0krr3+aMuPitrr3#fn:CUj
-s+13$s1JA^~>
-$04BSs)#"?s4Kd<rrO\00)Y_[+oD#ss8Q$>qBGs7HN-Xdrr3+iK)\0krr3+aMuPitrr3#fn:CUj
-s+13$s1JA^~>
-$04BSs,i\`^T;JSrrQQ4@f66:=hUV\s8RPD]G\JHS,[`4rkASqU&T,7rkASmW;gY<rkAJeY(?V(
-s+13$s1JA^~>
-$04BSs,i\`^T;JSrrQQ4@f66:=hUV\s8RPD]G\JHS,[`4rkASqU&T,7rkASmW;gY<rkAJeY(?V(
-s+13$s1JA^~>
-$04BSs,i\`^T;JSrrQQ4@f66:=hUV\s8RPD]G\JHS,[`4rkASqU&T,7rkASmW;gY<rkAJeY(?V(
-s+13$s1JA^~>
-#in9Rs8S-b>]flC!,q`6!FC9SrrddS$s].urr@?D>QC;nrr@0?>QCMtrr?s9>QC[Qs+13$s+13^
-s*t~>
-#in9Rs8S-b>]flC!,q`6!FC9SrrddS$s].urr@?D>QC;nrr@0?>QCMtrr?s9>QC[Qs+13$s+13^
-s*t~>
-#in9Rs8S-b>]flC!,q`6!FC9SrrddS$s].urr@?D>QC;nrr@0?>QCMtrr?s9>QC[Qs+13$s+13^
-s*t~>
-!ouXLk5PJY,(]cFs+13$s.TIC~>
-!ouXLk5PJY,(]cFs+13$s.TIC~>
-!ouXLk5PJY,(]cFs+13$s.TIC~>
-!ouXLk5PJY,(]cFs+13$s.TIC~>
-!ouXLk5PJY,(]cFs+13$s.TIC~>
-!ouXLk5PJY,(]cFs+13$s.TIC~>
-!ouXLk5PJ]\q0m4s+13$s.TIC~>
-!ouXLk5PJ]\q0m4s+13$s.TIC~>
-!ouXLk5PJ]\q0m4s+13$s.TIC~>
-!ouXLJcC<$JcC<$K`?Q~>
-!ouXLJcC<$JcC<$K`?Q~>
-!ouXLJcC<$JcC<$K`?Q~>
-!ouXLq#:A8^4H<8s+13$s,[21~>
-!ouXLq#:A8^4H<8s+13$s,[21~>
-!ouXLq#:A8^4H<8s+13$s,[21~>
-!ouXLq#:A*\:O[2s+13$s,[21~>
-!ouXLq#:A*\:O[2s+13$s,[21~>
-!ouXLq#:A*\:O[2s+13$s,[21~>
-"lqsOs8UXK_?A<es(&Y=rrMNAJcC<$JcC<$PlH7~>
-"lqsOs8UXK_?A<es(&Y=rrMNAJcC<$JcC<$PlH7~>
-"lqsOs8UXK_?A<es(&Y=rrMNAJcC<$JcC<$PlH7~>
-"lqsOs3,K^=UArcs(&Y>q504DJcC<$JcC<$PlH7~>
-"lqsOs3,K^=UArcs(&Y>q504DJcC<$JcC<$PlH7~>
-"lqsOs3,K^=UArcs(&Y>q504DJcC<$JcC<$PlH7~>
-"lqsOs)#">rs68Fs(&X1Lio;?s+13$s+136s*t~>
-"lqsOs)#">rs68Fs(&X1Lio;?s+13$s+136s*t~>
-"lqsOs)#">rs68Fs(&X1Lio;?s+13$s+136s*t~>
-"lqsOs)#">rs$,Ds("oaR=YBhs+13$s,m>3~>
-"lqsOs)#">rs$,Ds("oaR=YBhs+13$s,m>3~>
-"lqsOs)#">rs$,Ds("oaR=YBhs+13$s,m>3~>
-"lqsOs)#">rs$,Ds($Od?%N$,s+13$s,m>3~>
-"lqsOs)#">rs$,Ds($Od?%N$,s+13$s,m>3~>
-"lqsOs)#">rs$,Ds($Od?%N$,s+13$s,m>3~>
-"lqsOs)#">rs68Fs(&Xf^c$1`s+13$s+136s*t~>
-"lqsOs)#">rs68Fs(&Xf^c$1`s+13$s+136s*t~>
-"lqsOs)#">rs68Fs(&Xf^c$1`s+13$s+136s*t~>
-"lqsOs6s7l*!oL2s(8hArT1&)JcC<$JcC<$PlH7~>
-"lqsOs6s7l*!oL2s(8hArT1&)JcC<$JcC<$PlH7~>
-"lqsOs6s7l*!oL2s(8hArT1&)JcC<$JcC<$PlH7~>
-!ouXLJcC<$JcC<$K`?Q~>
-!ouXLJcC<$JcC<$K`?Q~>
-!ouXLJcC<$JcC<$K`?Q~>
-!ouXLJcC<$JcC<$K`?Q~>
-!ouXLJcC<$JcC<$K`?Q~>
-!ouXLJcC<$JcC<$K`?Q~>
-!ouXLJcC<$JcC<$K`?Q~>
-!ouXLJcC<$JcC<$K`?Q~>
-!ouXLJcC<$JcC<$K`?Q~>
-!ouXLJcC<$JcC<$K`?Q~>
-!ouXLJcC<$JcC<$K`?Q~>
-!ouXLJcC<$JcC<$K`?Q~>
-"QVjNs/l3<"K)5!R-9,$JcC<$JcCi3J,~>
-"QVjNs/l3<"K)5!R-9,$JcC<$JcCi3J,~>
-"QVjNs/l3<"K)5!R-9,$JcC<$JcCi3J,~>
-"QVjNs3LYE#(Bt[s2&7>JcC<$JcC<$OoKq~>
-"QVjNs3LYE#(Bt[s2&7>JcC<$JcC<$OoKq~>
-"QVjNs3LYE#(Bt[s2&7>JcC<$JcC<$OoKq~>
-!ouXLr;QiX?JPP[!ROMks+13$s+134s*t~>
-!ouXLr;QiX?JPP[!ROMks+13$s+134s*t~>
-!ouXLr;QiX?JPP[!ROMks+13$s+134s*t~>
-!ouXLrVlrn3U6PB!S9bks+13$s+135s*t~>
-!ouXLrVlrn3U6PB!S9bks+13$s+135s*t~>
-!ouXLrVlrn3U6PB!S9bks+13$s+135s*t~>
-%%EndData
-showpage
-%%Trailer
-end
-%%EOF
diff --git a/lib/stdlib/doc/src/zip.xml b/lib/stdlib/doc/src/zip.xml
index cf0d581352..61f49f5940 100644
--- a/lib/stdlib/doc/src/zip.xml
+++ b/lib/stdlib/doc/src/zip.xml
@@ -217,7 +217,7 @@
<tag><c>{uncompress, <anno>What</anno>}</c></tag>
<item>
<p>Controls what types of files will be uncompressed. It is by
- default set to <c>[".Z",".zip",".zoo",".arc",".lzh",".arj"]</c>.
+ default set to <c>[".Z", ".zip", ".zoo", ".arc", ".lzh", ".arj"]</c>.
The following values of <c>What</c> are allowed:</p>
<taglist>
<tag><c>all</c></tag>
@@ -355,7 +355,7 @@
{ok,{"dummy.zip",
&lt;&lt;80,75,3,4,20,0,0,0,0,0,74,152,97,60,171,39,212,26,3,0,
0,0,3,0,0,...&gt;&gt;}}
-&gt; <input>catch zip:foldl(fun("foo", _, B, _) -> throw(B()); (_, _, _, Acc) -> Acc end, [], {Name, Bin}). </input>
+&gt; <input>catch zip:foldl(fun("foo", _, B, _) -> throw(B()); (_,_,_,Acc) -> Acc end, [], {Name, Bin}). </input>
&lt;&lt;"FOO"&gt;&gt;
</pre>
</desc>
diff --git a/lib/stdlib/examples/erl_id_trans.erl b/lib/stdlib/examples/erl_id_trans.erl
index 72e41d6473..51def8c8e1 100644
--- a/lib/stdlib/examples/erl_id_trans.erl
+++ b/lib/stdlib/examples/erl_id_trans.erl
@@ -153,7 +153,6 @@ pattern({record,Line,Name,Pfs0}) ->
pattern({record_index,Line,Name,Field0}) ->
Field1 = pattern(Field0),
{record_index,Line,Name,Field1};
-%% record_field occurs in query expressions
pattern({record_field,Line,Rec0,Name,Field0}) ->
Rec1 = expr(Rec0),
Field1 = expr(Field0),
@@ -283,15 +282,6 @@ gexpr({call,Line,{remote,La,{atom,Lb,erlang},{atom,Lc,F}},As0}) ->
true -> As1 = gexpr_list(As0),
{call,Line,{remote,La,{atom,Lb,erlang},{atom,Lc,F}},As1}
end;
-% Unfortunately, writing calls as {M,F}(...) is also allowed.
-gexpr({call,Line,{tuple,La,[{atom,Lb,erlang},{atom,Lc,F}]},As0}) ->
- case erl_internal:guard_bif(F, length(As0)) or
- erl_internal:arith_op(F, length(As0)) or
- erl_internal:comp_op(F, length(As0)) or
- erl_internal:bool_op(F, length(As0)) of
- true -> As1 = gexpr_list(As0),
- {call,Line,{tuple,La,[{atom,Lb,erlang},{atom,Lc,F}]},As1}
- end;
gexpr({bin,Line,Fs}) ->
Fs2 = pattern_grp(Fs),
{bin,Line,Fs2};
@@ -440,10 +430,6 @@ expr({'catch',Line,E0}) ->
%% No new variables added.
E1 = expr(E0),
{'catch',Line,E1};
-expr({'query', Line, E0}) ->
- %% lc expression
- E = expr(E0),
- {'query', Line, E};
expr({match,Line,P0,E0}) ->
E1 = expr(E0),
P1 = pattern(P0),
diff --git a/lib/stdlib/src/Makefile b/lib/stdlib/src/Makefile
index 8bdaae57fd..2bab46b72a 100644
--- a/lib/stdlib/src/Makefile
+++ b/lib/stdlib/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2011. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -167,30 +167,33 @@ docs:
# This is a trick so that the preloaded files will get the correct type
# specifications.
primary_bootstrap_compiler: \
+ $(BOOTSTRAP_COMPILER)/ebin/epp.beam \
$(BOOTSTRAP_COMPILER)/ebin/erl_scan.beam \
$(BOOTSTRAP_COMPILER)/ebin/erl_parse.beam \
$(BOOTSTRAP_COMPILER)/ebin/erl_lint.beam \
+ $(BOOTSTRAP_COMPILER)/ebin/io.beam \
$(BOOTSTRAP_COMPILER)/ebin/otp_internal.beam
$(BOOTSTRAP_COMPILER)/ebin/erl_parse.beam: erl_parse.yrl
- $(ERLC) -o $(BOOTSTRAP_COMPILER)/egen erl_parse.yrl
- $(ERLC) -o $(BOOTSTRAP_COMPILER)/ebin $(BOOTSTRAP_COMPILER)/egen/erl_parse.erl
+ $(gen_verbose)
+ $(V_at)$(ERLC) -o $(BOOTSTRAP_COMPILER)/egen erl_parse.yrl
+ $(V_at)$(ERLC) -o $(BOOTSTRAP_COMPILER)/ebin $(BOOTSTRAP_COMPILER)/egen/erl_parse.erl
$(BOOTSTRAP_TOP)/lib/stdlib/egen/erl_parse.erl: erl_parse.yrl
- $(ERLC) $(YRL_FLAGS) -o$(BOOTSTRAP_TOP)/lib/stdlib/egen erl_parse.yrl
+ $(yecc_verbose)$(ERLC) $(YRL_FLAGS) -o$(BOOTSTRAP_TOP)/lib/stdlib/egen erl_parse.yrl
$(BOOTSTRAP_COMPILER)/ebin/%.beam: %.erl
- $(ERLC) -o $(BOOTSTRAP_COMPILER)/ebin $<
+ $(V_ERLC) -o $(BOOTSTRAP_COMPILER)/ebin $<
# ----------------------------------------------------
# Special Build Targets
# ----------------------------------------------------
$(APP_TARGET): $(APP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
# ----------------------------------------------------
# Release Target
diff --git a/lib/stdlib/src/base64.erl b/lib/stdlib/src/base64.erl
index 5d800e87b8..0068d82d43 100644
--- a/lib/stdlib/src/base64.erl
+++ b/lib/stdlib/src/base64.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -28,7 +28,7 @@
%% of (some) functions of this module.
%%-------------------------------------------------------------------------
--type ascii_string() :: [1..255].
+-type ascii_string() :: [1..127].
%%-------------------------------------------------------------------------
%% encode_to_string(ASCII) -> Base64String
diff --git a/lib/stdlib/src/binary.erl b/lib/stdlib/src/binary.erl
index 0e95372a76..41b6ab1d5f 100644
--- a/lib/stdlib/src/binary.erl
+++ b/lib/stdlib/src/binary.erl
@@ -21,7 +21,9 @@
%% Implemented in this module:
-export([split/2,split/3,replace/3,replace/4]).
--opaque cp() :: tuple().
+-export_type([cp/0]).
+
+-opaque cp() :: {'am' | 'bm', binary()}.
-type part() :: {Start :: non_neg_integer(), Length :: integer()}.
%%% BIFs.
diff --git a/lib/stdlib/src/c.erl b/lib/stdlib/src/c.erl
index a920921a5e..4c1c0f904b 100644
--- a/lib/stdlib/src/c.erl
+++ b/lib/stdlib/src/c.erl
@@ -116,7 +116,7 @@ machine_load(Mod, File, Opts) ->
File2 = filename:join(Dir, filename:basename(File, ".erl")),
case compile:output_generated(Opts) of
true ->
- Base = packages:last(Mod),
+ Base = atom_to_list(Mod),
case filename:basename(File, ".erl") of
Base ->
code:purge(Mod),
diff --git a/lib/stdlib/src/dets.erl b/lib/stdlib/src/dets.erl
index c0f9ce34b0..845fae4bf4 100644
--- a/lib/stdlib/src/dets.erl
+++ b/lib/stdlib/src/dets.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -88,7 +88,8 @@
%% Not documented, or not ready for publication.
-export([lookup_keys/2]).
--export_type([tab_name/0]).
+-export_type([bindings_cont/0, cont/0, object_cont/0, select_cont/0,
+ tab_name/0]).
-compile({inline, [{einval,2},{badarg,2},{undefined,1},
{badarg_exit,2},{lookup_reply,2}]}).
@@ -319,7 +320,7 @@ foldr(Fun, Acc, Tab) ->
foldl(Fun, Acc, Tab) ->
Ref = make_ref(),
- do_traverse(Fun, Acc, Tab, Ref).
+ badarg(do_traverse(Fun, Acc, Tab, Ref), [Fun, Acc, Tab]).
-spec from_ets(Name, EtsTab) -> 'ok' | {'error', Reason} when
Name :: tab_name(),
@@ -515,7 +516,7 @@ match(Tab, Pat) ->
Reason :: term().
match(Tab, Pat, N) ->
- badarg(init_chunk_match(Tab, Pat, bindings, N), [Tab, Pat, N]).
+ badarg(init_chunk_match(Tab, Pat, bindings, N, no_safe), [Tab, Pat, N]).
-spec match(Continuation) ->
{[Match], Continuation2} | '$end_of_table' | {'error', Reason} when
@@ -525,7 +526,7 @@ match(Tab, Pat, N) ->
Reason :: term().
match(State) when State#dets_cont.what =:= bindings ->
- badarg(chunk_match(State), [State]);
+ badarg(chunk_match(State, no_safe), [State]);
match(Term) ->
erlang:error(badarg, [Term]).
@@ -538,26 +539,26 @@ match_delete(Tab, Pat) ->
badarg(match_delete(Tab, Pat, delete), [Tab, Pat]).
match_delete(Tab, Pat, What) ->
- safe_fixtable(Tab, true),
case compile_match_spec(What, Pat) of
{Spec, MP} ->
- Proc = dets_server:get_pid(Tab),
- R = req(Proc, {match_delete_init, MP, Spec}),
- do_match_delete(Tab, Proc, R, What, 0);
+ case catch dets_server:get_pid(Tab) of
+ {'EXIT', _Reason} ->
+ badarg;
+ Proc ->
+ R = req(Proc, {match_delete_init, MP, Spec}),
+ do_match_delete(Proc, R, What, 0)
+ end;
badarg ->
badarg
end.
-do_match_delete(Tab, _Proc, {done, N1}, select, N) ->
- safe_fixtable(Tab, false),
+do_match_delete(_Proc, {done, N1}, select, N) ->
N + N1;
-do_match_delete(Tab, _Proc, {done, _N1}, _What, _N) ->
- safe_fixtable(Tab, false),
+do_match_delete(_Proc, {done, _N1}, _What, _N) ->
ok;
-do_match_delete(Tab, Proc, {cont, State, N1}, What, N) ->
- do_match_delete(Tab, Proc, req(Proc, {match_delete, State}), What, N+N1);
-do_match_delete(Tab, _Proc, Error, _What, _N) ->
- safe_fixtable(Tab, false),
+do_match_delete(Proc, {cont, State, N1}, What, N) ->
+ do_match_delete(Proc, req(Proc, {match_delete, State}), What, N+N1);
+do_match_delete(_Proc, Error, _What, _N) ->
Error.
-spec match_object(Name, Pattern) -> Objects | {'error', Reason} when
@@ -579,7 +580,7 @@ match_object(Tab, Pat) ->
Reason :: term().
match_object(Tab, Pat, N) ->
- badarg(init_chunk_match(Tab, Pat, object, N), [Tab, Pat, N]).
+ badarg(init_chunk_match(Tab, Pat, object, N, no_safe), [Tab, Pat, N]).
-spec match_object(Continuation) ->
{Objects, Continuation2} | '$end_of_table' | {'error', Reason} when
@@ -589,7 +590,7 @@ match_object(Tab, Pat, N) ->
Reason :: term().
match_object(State) when State#dets_cont.what =:= object ->
- badarg(chunk_match(State), [State]);
+ badarg(chunk_match(State, no_safe), [State]);
match_object(Term) ->
erlang:error(badarg, [Term]).
@@ -712,7 +713,7 @@ select(Tab, Pat) ->
Reason :: term().
select(Tab, Pat, N) ->
- badarg(init_chunk_match(Tab, Pat, select, N), [Tab, Pat, N]).
+ badarg(init_chunk_match(Tab, Pat, select, N, no_safe), [Tab, Pat, N]).
-spec select(Continuation) ->
{Selection, Continuation2} | '$end_of_table' | {'error', Reason} when
@@ -722,7 +723,7 @@ select(Tab, Pat, N) ->
Reason :: term().
select(State) when State#dets_cont.what =:= select ->
- badarg(chunk_match(State), [State]);
+ badarg(chunk_match(State, no_safe), [State]);
select(Term) ->
erlang:error(badarg, [Term]).
@@ -898,7 +899,7 @@ traverse(Tab, Fun) ->
throw({Ref, Other})
end
end,
- do_traverse(TFun, [], Tab, Ref).
+ badarg(do_traverse(TFun, [], Tab, Ref), [Tab, Fun]).
-spec update_counter(Name, Key, Increment) -> Result when
Name :: tab_name(),
@@ -929,20 +930,21 @@ where(Tab, Object) ->
badarg(treq(Tab, {where, Object}), [Tab, Object]).
do_traverse(Fun, Acc, Tab, Ref) ->
- safe_fixtable(Tab, true),
- Proc = dets_server:get_pid(Tab),
- try
- do_trav(Proc, Acc, Fun)
- catch {Ref, Result} ->
- Result
- after
- safe_fixtable(Tab, false)
+ case catch dets_server:get_pid(Tab) of
+ {'EXIT', _Reason} ->
+ badarg;
+ Proc ->
+ try
+ do_trav(Proc, Acc, Fun)
+ catch {Ref, Result} ->
+ Result
+ end
end.
do_trav(Proc, Acc, Fun) ->
{Spec, MP} = compile_match_spec(object, '_'),
%% MP not used
- case req(Proc, {match, MP, Spec, default}) of
+ case req(Proc, {match, MP, Spec, default, safe}) of
{cont, State} ->
do_trav(State, Proc, Acc, Fun);
Error ->
@@ -952,7 +954,7 @@ do_trav(Proc, Acc, Fun) ->
do_trav(#dets_cont{bin = eof}, _Proc, Acc, _Fun) ->
Acc;
do_trav(State, Proc, Acc, Fun) ->
- case req(Proc, {match_init, State}) of
+ case req(Proc, {match_init, State, safe}) of
{cont, {Bins, NewState}} ->
do_trav_bins(NewState, Proc, Acc, Fun, lists:reverse(Bins));
Error ->
@@ -972,44 +974,47 @@ do_trav_bins(State, Proc, Acc, Fun, [Bin | Bins]) ->
end.
safe_match(Tab, Pat, What) ->
- safe_fixtable(Tab, true),
- R = do_safe_match(init_chunk_match(Tab, Pat, What, default), []),
- safe_fixtable(Tab, false),
- R.
+ do_safe_match(init_chunk_match(Tab, Pat, What, default, safe), []).
do_safe_match({error, Error}, _L) ->
{error, Error};
do_safe_match({L, C}, LL) ->
- do_safe_match(chunk_match(C), L++LL);
+ do_safe_match(chunk_match(C, safe), L++LL);
do_safe_match('$end_of_table', L) ->
L;
do_safe_match(badarg, _L) ->
badarg.
%% What = object | bindings | select
-init_chunk_match(Tab, Pat, What, N) when is_integer(N), N >= 0;
- N =:= default ->
+init_chunk_match(Tab, Pat, What, N, Safe) when is_integer(N), N >= 0;
+ N =:= default ->
case compile_match_spec(What, Pat) of
{Spec, MP} ->
- Proc = dets_server:get_pid(Tab),
- case req(Proc, {match, MP, Spec, N}) of
- {done, L} ->
- {L, #dets_cont{tab = Tab, proc = Proc, what = What,
- bin = eof}};
- {cont, State} ->
- chunk_match(State#dets_cont{what = What, tab = Tab,
- proc = Proc});
- Error ->
- Error
+ case catch dets_server:get_pid(Tab) of
+ {'EXIT', _Reason} ->
+ badarg;
+ Proc ->
+ case req(Proc, {match, MP, Spec, N, Safe}) of
+ {done, L} ->
+ {L, #dets_cont{tab = Tab, proc = Proc,
+ what = What, bin = eof}};
+ {cont, State} ->
+ chunk_match(State#dets_cont{what = What,
+ tab = Tab,
+ proc = Proc},
+ Safe);
+ Error ->
+ Error
+ end
end;
badarg ->
badarg
end;
-init_chunk_match(_Tab, _Pat, _What, _) ->
+init_chunk_match(_Tab, _Pat, _What, _N, _Safe) ->
badarg.
-chunk_match(#dets_cont{proc = Proc}=State) ->
- case req(Proc, {match_init, State}) of
+chunk_match(#dets_cont{proc = Proc}=State, Safe) ->
+ case req(Proc, {match_init, State, Safe}) of
'$end_of_table'=Reply ->
Reply;
{cont, {Bins, NewState}} ->
@@ -1024,7 +1029,7 @@ chunk_match(#dets_cont{proc = Proc}=State) ->
badarg
end;
[] ->
- chunk_match(NewState);
+ chunk_match(NewState, Safe);
Terms ->
{Terms, NewState}
end;
@@ -1301,7 +1306,7 @@ open_file_loop(Head, N) ->
%% - wait 1 ms after each update.
%% next is normally followed by lookup, but since lookup is also
%% used when not traversing the table, it is not prioritized.
- ?DETS_CALL(From, {match_init, _State} = Op) ->
+ ?DETS_CALL(From, {match_init, _State, _Safe} = Op) ->
do_apply_op(Op, From, Head, N);
?DETS_CALL(From, {bchunk, _State} = Op) ->
do_apply_op(Op, From, Head, N);
@@ -1558,12 +1563,17 @@ apply_op(Op, From, Head, N) ->
H2;
{lookup_keys, _Keys} ->
stream_op(Op, From, [], Head, N);
- {match_init, State} ->
- {H2, Res} = fmatch_init(Head, State),
+ {match_init, State, Safe} ->
+ {H1, Res} = fmatch_init(Head, State),
+ H2 = case Res of
+ {cont,_} -> H1;
+ _ when Safe =:= no_safe-> H1;
+ _ when Safe =:= safe -> do_safe_fixtable(H1, From, false)
+ end,
From ! {self(), Res},
H2;
- {match, MP, Spec, NObjs} ->
- {H2, Res} = fmatch(Head, MP, Spec, NObjs),
+ {match, MP, Spec, NObjs, Safe} ->
+ {H2, Res} = fmatch(Head, MP, Spec, NObjs, Safe, From),
From ! {self(), Res},
H2;
{member, Key} when Head#head.version =:= 8 ->
@@ -1577,11 +1587,15 @@ apply_op(Op, From, Head, N) ->
From ! {self(), Res},
H2;
{match_delete, State} when Head#head.update_mode =:= dirty ->
- {H2, Res} = fmatch_delete(Head, State),
+ {H1, Res} = fmatch_delete(Head, State),
+ H2 = case Res of
+ {cont,_S,_N} -> H1;
+ _ -> do_safe_fixtable(H1, From, false)
+ end,
From ! {self(), Res},
{N + 1, H2};
{match_delete_init, MP, Spec} when Head#head.update_mode =:= dirty ->
- {H2, Res} = fmatch_delete_init(Head, MP, Spec),
+ {H2, Res} = fmatch_delete_init(Head, MP, Spec, From),
From ! {self(), Res},
{N + 1, H2};
{safe_fixtable, Bool} ->
@@ -2229,13 +2243,18 @@ fmatch_init(Head, C) ->
end.
%% -> {NewHead, Result}
-fmatch(Head, MP, Spec, N) ->
+fmatch(Head, MP, Spec, N, Safe, From) ->
KeyPos = Head#head.keypos,
case find_all_keys(Spec, KeyPos, []) of
[] ->
%% Complete match
case catch write_cache(Head) of
- {NewHead, []} ->
+ {Head1, []} ->
+ NewHead =
+ case Safe of
+ safe -> do_safe_fixtable(Head1, From, true);
+ no_safe -> Head1
+ end,
C0 = init_scan(NewHead, N),
{NewHead, {cont, C0#dets_cont{match_program = MP}}};
{NewHead, _} = HeadError when is_record(NewHead, head) ->
@@ -2300,12 +2319,12 @@ contains_variable(_) ->
false.
%% -> {NewHead, Res}
-fmatch_delete_init(Head, MP, Spec) ->
+fmatch_delete_init(Head, MP, Spec, From) ->
KeyPos = Head#head.keypos,
case catch
case find_all_keys(Spec, KeyPos, []) of
[] ->
- do_fmatch_delete_var_keys(Head, MP, Spec);
+ do_fmatch_delete_var_keys(Head, MP, Spec, From);
List ->
Keys = lists:usort(List),
do_fmatch_constant_keys(Head, Keys, MP)
@@ -2336,7 +2355,7 @@ fmatch_delete(Head, C) ->
end
end.
-do_fmatch_delete_var_keys(Head, _MP, ?PATTERN_TO_TRUE_MATCH_SPEC('_'))
+do_fmatch_delete_var_keys(Head, _MP, ?PATTERN_TO_TRUE_MATCH_SPEC('_'), _From)
when Head#head.fixed =:= false ->
%% Handle the case where the file is emptied efficiently.
%% Empty the cache just to get the number of objects right.
@@ -2348,8 +2367,9 @@ do_fmatch_delete_var_keys(Head, _MP, ?PATTERN_TO_TRUE_MATCH_SPEC('_'))
Reply ->
Reply
end;
-do_fmatch_delete_var_keys(Head, MP, _Spec) ->
- {NewHead, []} = write_cache(Head),
+do_fmatch_delete_var_keys(Head, MP, _Spec, From) ->
+ Head1 = do_safe_fixtable(Head, From, true),
+ {NewHead, []} = write_cache(Head1),
C0 = init_scan(NewHead, default),
{NewHead, {cont, C0#dets_cont{match_program = MP}, 0}}.
diff --git a/lib/stdlib/src/dict.erl b/lib/stdlib/src/dict.erl
index 2e9eba4bfa..4f8d45dc8d 100644
--- a/lib/stdlib/src/dict.erl
+++ b/lib/stdlib/src/dict.erl
@@ -1,7 +1,8 @@
+%% -*- coding: utf-8 -*-
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -17,7 +18,7 @@
%% %CopyrightEnd%
%%
-%% We use the dynamic hashing techniques by Per-�ke Larsson as
+%% We use the dynamic hashing techniques by Per-Åke Larsson as
%% described in "The Design and Implementation of Dynamic Hashing for
%% Sets and Tables in Icon" by Griswold and Townsend. Much of the
%% terminology comes from that paper as well.
diff --git a/lib/stdlib/src/edlin.erl b/lib/stdlib/src/edlin.erl
index 026bd9038f..3192879f09 100644
--- a/lib/stdlib/src/edlin.erl
+++ b/lib/stdlib/src/edlin.erl
@@ -1,7 +1,8 @@
+%% -*- coding: utf-8 -*-
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -21,10 +22,10 @@
%% A simple Emacs-like line editor.
%% About Latin-1 characters: see the beginning of erl_scan.erl.
--export([init/0,start/1,edit_line/2,prefix_arg/1]).
+-export([init/0,start/1,start/2,edit_line/2,prefix_arg/1]).
-export([erase_line/1,erase_inp/1,redraw_line/1]).
-export([length_before/1,length_after/1,prompt/1]).
--export([current_line/1]).
+-export([current_line/1, current_chars/1]).
%%-export([expand/1]).
-export([edit_line1/2]).
@@ -53,7 +54,12 @@ init() ->
%% {undefined,Char,Rest,Cont,Requests}
start(Pbs) ->
- {more_chars,{line,Pbs,{[],[]},none},[{put_chars,unicode,Pbs}]}.
+ start(Pbs, none).
+
+%% Only two modes used: 'none' and 'search'. Other modes can be
+%% handled inline through specific character handling.
+start(Pbs, Mode) ->
+ {more_chars,{line,Pbs,{[],[]},Mode},[{put_chars,unicode,Pbs}]}.
edit_line(Cs, {line,P,L,{blink,N}}) ->
edit(Cs, P, L, none, [{move_rel,N}]);
@@ -75,6 +81,10 @@ edit([C|Cs], P, {Bef,Aft}, Prefix, Rs0) ->
edit(Cs, P, {Bef,Aft}, meta, Rs0);
meta_left_sq_bracket ->
edit(Cs, P, {Bef,Aft}, meta_left_sq_bracket, Rs0);
+ search_meta ->
+ edit(Cs, P, {Bef,Aft}, search_meta, Rs0);
+ search_meta_left_sq_bracket ->
+ edit(Cs, P, {Bef,Aft}, search_meta_left_sq_bracket, Rs0);
ctlx ->
edit(Cs, P, {Bef,Aft}, ctlx, Rs0);
new_line ->
@@ -114,6 +124,8 @@ edit([C|Cs], P, {Bef,Aft}, Prefix, Rs0) ->
case do_op(Op, Bef, Aft, Rs0) of
{blink,N,Line,Rs} ->
edit(Cs, P, Line, {blink,N}, Rs);
+ {Line, Rs, Mode} -> % allow custom modes from do_op
+ edit(Cs, P, Line, Mode, Rs);
{Line,Rs} ->
edit(Cs, P, Line, none, Rs)
end
@@ -167,9 +179,15 @@ key_map($\^], none) -> auto_blink;
key_map($\^X, none) -> ctlx;
key_map($\^Y, none) -> yank;
key_map($\e, none) -> meta;
-key_map($), Prefix) when Prefix =/= meta -> {blink,$),$(};
-key_map($}, Prefix) when Prefix =/= meta -> {blink,$},${};
-key_map($], Prefix) when Prefix =/= meta -> {blink,$],$[};
+key_map($), Prefix) when Prefix =/= meta,
+ Prefix =/= search,
+ Prefix =/= search_meta -> {blink,$),$(};
+key_map($}, Prefix) when Prefix =/= meta,
+ Prefix =/= search,
+ Prefix =/= search_meta -> {blink,$},${};
+key_map($], Prefix) when Prefix =/= meta,
+ Prefix =/= search,
+ Prefix =/= search_meta -> {blink,$],$[};
key_map($B, meta) -> backward_word;
key_map($D, meta) -> kill_word;
key_map($F, meta) -> forward_word;
@@ -187,6 +205,32 @@ key_map($D, meta_left_sq_bracket) -> backward_char;
key_map($C, meta_left_sq_bracket) -> forward_char;
key_map(C, none) when C >= $\s ->
{insert,C};
+%% for search, we need smarter line handling and so
+%% we cheat a bit on the dispatching, and allow to
+%% return a mode.
+key_map($\^H, search) -> {search, backward_delete_char};
+key_map($\177, search) -> {search, backward_delete_char};
+key_map($\^R, search) -> {search, skip_up};
+key_map($\^S, search) -> {search, skip_down};
+key_map($\n, search) -> {search, search_found};
+key_map($\r, search) -> {search, search_found};
+key_map($\^A, search) -> {search, search_quit};
+key_map($\^B, search) -> {search, search_quit};
+key_map($\^D, search) -> {search, search_quit};
+key_map($\^E, search) -> {search, search_quit};
+key_map($\^F, search) -> {search, search_quit};
+key_map($\t, search) -> {search, search_quit};
+key_map($\^L, search) -> {search, search_quit};
+key_map($\^T, search) -> {search, search_quit};
+key_map($\^U, search) -> {search, search_quit};
+key_map($\^], search) -> {search, search_quit};
+key_map($\^X, search) -> {search, search_quit};
+key_map($\^Y, search) -> {search, search_quit};
+key_map($\e, search) -> search_meta;
+key_map($[, search_meta) -> search_meta_left_sq_bracket;
+key_map(_, search_meta) -> {search, search_quit};
+key_map(_C, search_meta_left_sq_bracket) -> {search, search_quit};
+key_map(C, search) -> {insert_search,C};
key_map(C, _) -> {undefined,C}.
%% do_op(Action, Before, After, Requests)
@@ -195,6 +239,57 @@ do_op({insert,C}, Bef, [], Rs) ->
{{[C|Bef],[]},[{put_chars, unicode,[C]}|Rs]};
do_op({insert,C}, Bef, Aft, Rs) ->
{{[C|Bef],Aft},[{insert_chars, unicode, [C]}|Rs]};
+%% Search mode prompt always looks like (search)`$TERMS': $RESULT.
+%% the {insert_search, _} handlings allow to share this implementation
+%% correctly with group.erl. This module provides $TERMS, and group.erl
+%% is in charge of providing $RESULT.
+%% This require a bit of trickery. Because search disables moving around
+%% on the line (left/right arrow keys and other shortcuts that just exit
+%% search mode), we can use the Bef and Aft variables to hold each
+%% part of the line. Bef takes charge of "(search)`$TERMS" and Aft
+%% takes charge of "': $RESULT".
+do_op({insert_search, C}, Bef, [], Rs) ->
+ Aft="': ",
+ {{[C|Bef],Aft},
+ [{insert_chars, unicode, [C]++Aft}, {delete_chars,-3} | Rs],
+ search};
+do_op({insert_search, C}, Bef, Aft, Rs) ->
+ Offset= length(Aft),
+ NAft = "': ",
+ {{[C|Bef],NAft},
+ [{insert_chars, unicode, [C]++NAft}, {delete_chars,-Offset} | Rs],
+ search};
+do_op({search, backward_delete_char}, [_|Bef], Aft, Rs) ->
+ Offset= length(Aft)+1,
+ NAft = "': ",
+ {{Bef,NAft},
+ [{insert_chars, unicode, NAft}, {delete_chars,-Offset}|Rs],
+ search};
+do_op({search, backward_delete_char}, [], _Aft, Rs) ->
+ Aft="': ",
+ {{[],Aft}, Rs, search};
+do_op({search, skip_up}, Bef, Aft, Rs) ->
+ Offset= length(Aft),
+ NAft = "': ",
+ {{[$\^R|Bef],NAft}, % we insert ^R as a flag to whoever called us
+ [{insert_chars, unicode, NAft}, {delete_chars,-Offset}|Rs],
+ search};
+do_op({search, skip_down}, Bef, Aft, Rs) ->
+ Offset= length(Aft),
+ NAft = "': ",
+ {{[$\^S|Bef],NAft}, % we insert ^S as a flag to whoever called us
+ [{insert_chars, unicode, NAft}, {delete_chars,-Offset}|Rs],
+ search};
+do_op({search, search_found}, _Bef, Aft, Rs) ->
+ "': "++NAft = Aft,
+ {{[],NAft},
+ [{put_chars, unicode, "\n"}, {move_rel,-length(Aft)} | Rs],
+ search_found};
+do_op({search, search_quit}, _Bef, Aft, Rs) ->
+ "': "++NAft = Aft,
+ {{[],NAft},
+ [{put_chars, unicode, "\n"}, {move_rel,-length(Aft)} | Rs],
+ search_quit};
%% do blink after $$
do_op({blink,C,M}, Bef=[$$,$$|_], Aft, Rs) ->
N = over_paren(Bef, C, M),
@@ -317,9 +412,9 @@ over_non_word([], Stack, N) ->
{[],Stack,N}.
word_char(C) when C >= $A, C =< $Z -> true;
-word_char(C) when C >= $�, C =< $�, C =/= $� -> true;
+word_char(C) when C >= $À, C =< $Þ, C =/= $× -> true;
word_char(C) when C >= $a, C =< $z -> true;
-word_char(C) when C >= $�, C =< $�, C =/= $� -> true;
+word_char(C) when C >= $ß, C =< $ÿ, C =/= $÷ -> true;
word_char(C) when C >= $0, C =< $9 -> true;
word_char(C) when C =:= $_ -> true;
word_char(C) when C =:= $. -> true; % accept dot-separated names
@@ -452,6 +547,9 @@ prompt({line,Pbs,_,_}) ->
current_line({line,_,{Bef, Aft},_}) ->
reverse(Bef, Aft ++ "\n").
+current_chars({line,_,{Bef,Aft},_}) ->
+ reverse(Bef, Aft).
+
%% %% expand(CurrentBefore) ->
%% %% {yes,Expansion} | no
%% %% Try to expand the word before as either a module name or a function
diff --git a/lib/stdlib/src/epp.erl b/lib/stdlib/src/epp.erl
index ccc14610d7..ebabf8d700 100644
--- a/lib/stdlib/src/epp.erl
+++ b/lib/stdlib/src/epp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -23,13 +23,18 @@
-export([open/2,open/3,open/5,close/1,format_error/1]).
-export([scan_erl_form/1,parse_erl_form/1,macro_defs/1]).
-export([parse_file/1, parse_file/3]).
+-export([default_encoding/0, encoding_to_string/1,
+ read_encoding/1, read_encoding/2, set_encoding/1]).
-export([interpret_file_attribute/1]).
-export([normalize_typed_record_fields/1,restore_typed_record_fields/1]).
%%------------------------------------------------------------------------
+-export_type([source_encoding/0]).
+
-type macros() :: [{atom(), term()}].
-type epp_handle() :: pid().
+-type source_encoding() :: latin1 | utf8.
%% Epp state record.
-record(epp, {file, %Current file
@@ -213,6 +218,173 @@ parse_file(Epp) ->
[{eof,Location}]
end.
+-define(DEFAULT_ENCODING, latin1).
+
+-spec default_encoding() -> source_encoding().
+
+default_encoding() ->
+ ?DEFAULT_ENCODING.
+
+-spec encoding_to_string(Encoding) -> string() when
+ Encoding :: source_encoding().
+
+encoding_to_string(latin1) -> "coding: latin-1";
+encoding_to_string(utf8) -> "coding: utf-8".
+
+-spec read_encoding(FileName) -> source_encoding() | none when
+ FileName :: file:name().
+
+read_encoding(Name) ->
+ read_encoding(Name, []).
+
+-spec read_encoding(FileName, Options) -> source_encoding() | none when
+ FileName :: file:name(),
+ Options :: [Option],
+ Option :: {in_comment_only, boolean()}.
+
+read_encoding(Name, Options) ->
+ InComment = proplists:get_value(in_comment_only, Options, true),
+ case file:open(Name, [read]) of
+ {ok,File} ->
+ try read_encoding_from_file(File, InComment)
+ after ok = file:close(File)
+ end;
+ _Error ->
+ none
+ end.
+
+-spec set_encoding(File) -> source_encoding() | none when
+ File :: io:device(). % pid(); raw files don't work
+
+set_encoding(File) ->
+ Encoding = read_encoding_from_file(File, true),
+ Enc = case Encoding of
+ none -> default_encoding();
+ Encoding -> Encoding
+ end,
+ ok = io:setopts(File, [{encoding, Enc}]),
+ Encoding.
+
+-spec read_encoding_from_file(File, InComment) -> source_encoding() | none when
+ File :: io:device(),
+ InComment :: boolean().
+
+-define(ENC_CHUNK, 32).
+-define(N_ENC_CHUNK, 16). % a total of 512 bytes
+
+read_encoding_from_file(File, InComment) ->
+ {ok, Pos0} = file:position(File, cur),
+ Opts = io:getopts(File),
+ Encoding0 = lists:keyfind(encoding, 1, Opts),
+ Binary0 = lists:keyfind(binary, 1, Opts),
+ ok = io:setopts(File, [binary, {encoding, latin1}]),
+ try
+ {B, Fun} = (reader(File, 0))(),
+ com_nl(B, Fun, 0, InComment)
+ catch
+ throw:no ->
+ none
+ after
+ {ok, Pos0} = file:position(File, Pos0),
+ ok = io:setopts(File, [Binary0, Encoding0])
+ end.
+
+reader(Fd, N) ->
+ fun() when N =:= ?N_ENC_CHUNK ->
+ throw(no);
+ () ->
+ case file:read(Fd, ?ENC_CHUNK) of
+ eof ->
+ {<<>>, reader(Fd, N+1)};
+ {ok, Bin} ->
+ {Bin, reader(Fd, N+1)};
+ {error, _} ->
+ throw(no) % ignore errors
+ end
+ end.
+
+com_nl(_, _, 2, _) ->
+ throw(no);
+com_nl(B, Fun, N, false=Com) ->
+ com_c(B, Fun, N, Com);
+com_nl(B, Fun, N, true=Com) ->
+ com(B, Fun, N, Com).
+
+com(<<"\n",B/binary>>, Fun, N, Com) ->
+ com_nl(B, Fun, N+1, Com);
+com(<<"%", B/binary>>, Fun, N, Com) ->
+ com_c(B, Fun, N, Com);
+com(<<_:1/unit:8,B/binary>>, Fun, N, Com) ->
+ com(B, Fun, N, Com);
+com(<<>>, Fun, N, Com) ->
+ {B, Fun1} = Fun(),
+ com(B, Fun1, N, Com).
+
+com_c(<<"c",B/binary>>, Fun, N, Com) ->
+ com_oding(B, Fun, N, Com);
+com_c(<<"\n",B/binary>>, Fun, N, Com) ->
+ com_nl(B, Fun, N+1, Com);
+com_c(<<_:1/unit:8,B/binary>>, Fun, N, Com) ->
+ com_c(B, Fun, N, Com);
+com_c(<<>>, Fun, N, Com) ->
+ {B, Fun1} = Fun(),
+ com_c(B, Fun1, N, Com).
+
+com_oding(<<"oding",B/binary>>, Fun, N, Com) ->
+ com_sep(B, Fun, N, Com);
+com_oding(B, Fun, N, Com) when byte_size(B) >= length("oding") ->
+ com_c(B, Fun, N, Com);
+com_oding(B, Fun, N, Com) ->
+ {B1, Fun1} = Fun(),
+ com_oding(list_to_binary([B, B1]), Fun1, N, Com).
+
+com_sep(<<":",B/binary>>, Fun, N, Com) ->
+ com_space(B, Fun, N, Com);
+com_sep(<<"=",B/binary>>, Fun, N, Com) ->
+ com_space(B, Fun, N, Com);
+com_sep(<<"\s",B/binary>>, Fun, N, Com) ->
+ com_sep(B, Fun, N, Com);
+com_sep(<<>>, Fun, N, Com) ->
+ {B, Fun1} = Fun(),
+ com_sep(B, Fun1, N, Com);
+com_sep(B, Fun, N, Com) ->
+ com_c(B, Fun, N, Com).
+
+com_space(<<"\s",B/binary>>, Fun, N, Com) ->
+ com_space(B, Fun, N, Com);
+com_space(<<>>, Fun, N, Com) ->
+ {B, Fun1} = Fun(),
+ com_space(B, Fun1, N, Com);
+com_space(B, Fun, N, _Com) ->
+ com_enc(B, Fun, N, [], []).
+
+com_enc(<<C:1/unit:8,B/binary>>, Fun, N, L, Ps) when C >= $a, C =< $z;
+ C >= $A, C =< $Z;
+ C >= $0, C =< $9 ->
+ com_enc(B, Fun, N, [C | L], Ps);
+com_enc(<<>>, Fun, N, L, Ps) ->
+ case Fun() of
+ {<<>>, _} ->
+ com_enc_end([L | Ps]);
+ {B, Fun1} ->
+ com_enc(B, Fun1, N, L, Ps)
+ end;
+com_enc(<<"-",B/binary>>, Fun, N, L, Ps) ->
+ com_enc(B, Fun, N, [], [L | Ps]);
+com_enc(_B, _Fun, _N, L, Ps) ->
+ com_enc_end([L | Ps]).
+
+com_enc_end(Ps0) ->
+ Ps = lists:reverse([lists:reverse(string:to_lower(P)) || P <- Ps0]),
+ com_encoding(Ps).
+
+com_encoding(["latin","1"|_]) ->
+ latin1;
+com_encoding(["utf","8"|_]) ->
+ utf8;
+com_encoding(_) ->
+ throw(no). % Don't try any further
+
normalize_typed_record_fields([]) ->
{typed, []};
normalize_typed_record_fields(Fields) ->
@@ -266,14 +438,17 @@ init_server(Pid, Name, File, AtLocation, Path, Pdm, Pre) ->
Ms0 = predef_macros(Name),
case user_predef(Pdm, Ms0) of
{ok,Ms1} ->
- epp_reply(Pid, {ok,self()}),
- %% ensure directory of current source file is first in path
+ _ = set_encoding(File),
+ epp_reply(Pid, {ok,self()}),
+ %% ensure directory of current source file is
+ %% first in path
Path1 = [filename:dirname(Name) | Path],
- St = #epp{file=File, location=AtLocation, delta=0, name=Name,
- name2=Name, path=Path1, macs=Ms1, pre_opened = Pre},
- From = wait_request(St),
- enter_file_reply(From, Name, AtLocation, AtLocation),
- wait_req_scan(St);
+ St = #epp{file=File, location=AtLocation, delta=0,
+ name=Name, name2=Name, path=Path1, macs=Ms1,
+ pre_opened = Pre},
+ From = wait_request(St),
+ enter_file_reply(From, Name, AtLocation, AtLocation),
+ wait_req_scan(St);
{error,E} ->
epp_reply(Pid, {error,E})
end.
@@ -385,19 +560,20 @@ enter_file(NewName, Inc, From, St) ->
%% enter_file2(File, FullName, From, EppState, AtLocation) -> EppState.
%% Set epp to use this file and "enter" it.
-enter_file2(NewF, Pname, From, St, AtLocation) ->
+enter_file2(NewF, Pname, From, St0, AtLocation) ->
Loc = start_loc(AtLocation),
enter_file_reply(From, Pname, Loc, AtLocation),
- Ms = dict:store({atom,'FILE'}, {none,[{string,Loc,Pname}]}, St#epp.macs),
+ Ms = dict:store({atom,'FILE'}, {none,[{string,Loc,Pname}]}, St0#epp.macs),
%% update the head of the include path to be the directory of the new
%% source file, so that an included file can always include other files
%% relative to its current location (this is also how C does it); note
%% that the directory of the parent source file (the previous head of
%% the path) must be dropped, otherwise the path used within the current
%% file will depend on the order of file inclusions in the parent files
- Path = [filename:dirname(Pname) | tl(St#epp.path)],
+ Path = [filename:dirname(Pname) | tl(St0#epp.path)],
+ _ = set_encoding(NewF),
#epp{file=NewF,location=Loc,name=Pname,delta=0,
- sstk=[St|St#epp.sstk],path=Path,macs=Ms}.
+ sstk=[St0|St0#epp.sstk],path=Path,macs=Ms}.
enter_file_reply(From, Name, Location, AtLocation) ->
Attr = loc_attr(AtLocation),
@@ -456,7 +632,7 @@ leave_file(From, St) ->
%% scan_toks(Tokens, From, EppState)
scan_toks(From, St) ->
- case io:scan_erl_form(St#epp.file, '', St#epp.location) of
+ case io:scan_erl_form(St#epp.file, '', St#epp.location, [unicode]) of
{ok,Toks,Cl} ->
scan_toks(Toks, From, St#epp{location=Cl});
{error,E,Cl} ->
@@ -830,7 +1006,7 @@ new_location(Ln, {Le,_}, {Lf,_}) ->
%% nested conditionals and repeated 'else's.
skip_toks(From, St, [I|Sis]) ->
- case io:scan_erl_form(St#epp.file, '', St#epp.location) of
+ case io:scan_erl_form(St#epp.file, '', St#epp.location, [unicode]) of
{ok,[{'-',_Lh},{atom,_Li,ifdef}|_Toks],Cl} ->
skip_toks(From, St#epp{location=Cl}, [ifdef,I|Sis]);
{ok,[{'-',_Lh},{atom,_Li,ifndef}|_Toks],Cl} ->
@@ -1048,8 +1224,6 @@ macro_arg([{'try',Lr}|Toks], E, Arg) ->
macro_arg(Toks, ['end'|E], [{'try',Lr}|Arg]);
macro_arg([{'cond',Lr}|Toks], E, Arg) ->
macro_arg(Toks, ['end'|E], [{'cond',Lr}|Arg]);
-macro_arg([{'query',Lr}|Toks], E, Arg) ->
- macro_arg(Toks, ['end'|E], [{'query',Lr}|Arg]);
macro_arg([{Rb,Lrb}|Toks], [Rb|E], Arg) -> %Found matching close
macro_arg(Toks, E, [{Rb,Lrb}|Arg]);
macro_arg([T|Toks], E, Arg) ->
@@ -1094,6 +1268,7 @@ expand_arg([], Ts, L, Rest, Bs) ->
%%% tokenized would yield the token list Ts.
%% erl_scan:token_info(T, text) is not backward compatible with this.
+%% Note that escaped characters will be replaced by themselves.
token_src({dot, _}) ->
".";
token_src({X, _}) when is_atom(X) ->
@@ -1101,16 +1276,16 @@ token_src({X, _}) when is_atom(X) ->
token_src({var, _, X}) ->
atom_to_list(X);
token_src({char,_,C}) ->
- io_lib:write_char(C);
+ io_lib:write_unicode_char(C);
token_src({string, _, X}) ->
- lists:flatten(io_lib:format("~p", [X]));
+ io_lib:write_unicode_string(X);
token_src({_, _, X}) ->
- lists:flatten(io_lib:format("~w", [X])).
+ io_lib:format("~w", [X]).
stringify1([]) ->
[];
stringify1([T | Tokens]) ->
- [io_lib:format(" ~s", [token_src(T)]) | stringify1(Tokens)].
+ [io_lib:format(" ~ts", [token_src(T)]) | stringify1(Tokens)].
stringify(Ts, L) ->
[$\s | S] = lists:flatten(stringify1(Ts)),
diff --git a/lib/stdlib/src/erl_eval.erl b/lib/stdlib/src/erl_eval.erl
index 95ba6b1096..8471ae6b64 100644
--- a/lib/stdlib/src/erl_eval.erl
+++ b/lib/stdlib/src/erl_eval.erl
@@ -227,13 +227,6 @@ expr({bc,_,E,Qs}, Bs, Lf, Ef, RBs) ->
expr({tuple,_,Es}, Bs0, Lf, Ef, RBs) ->
{Vs,Bs} = expr_list(Es, Bs0, Lf, Ef),
ret_expr(list_to_tuple(Vs), Bs, RBs);
-expr({record_field,_,_,_}=Mod, Bs, _Lf, _Ef, RBs) ->
- case expand_module_name(Mod, Bs) of
- {atom,_,A} ->
- ret_expr(A, Bs, RBs); %% This is the "x.y" syntax
- _ ->
- erlang:raise(error, {badexpr, '.'}, stacktrace())
- end;
expr({record_field,_,_,Name,_}, _Bs, _Lf, _Ef, _RBs) ->
erlang:raise(error, {undef_record,Name}, stacktrace());
expr({record_index,_,Name,_}, _Bs, _Lf, _Ef, _RBs) ->
@@ -332,8 +325,7 @@ expr({call,L1,{remote,L2,{record_field,_,{atom,_,''},{atom,_,qlc}=Mod},
Bs, Lf, Ef, RBs) when length(As0) =< 1 ->
expr({call,L1,{remote,L2,Mod,Func},As}, Bs, Lf, Ef, RBs);
expr({call,_,{remote,_,Mod,Func},As0}, Bs0, Lf, Ef, RBs) ->
- Mod1 = expand_module_name(Mod, Bs0),
- {value,M,Bs1} = expr(Mod1, Bs0, Lf, Ef, none),
+ {value,M,Bs1} = expr(Mod, Bs0, Lf, Ef, none),
{value,F,Bs2} = expr(Func, Bs0, Lf, Ef, none),
{As,Bs3} = expr_list(As0, merge_bindings(Bs1, Bs2), Lf, Ef),
%% M could be a parameterized module (not an atom).
@@ -1210,41 +1202,6 @@ ret_expr(_Old, New) ->
line(Expr) -> element(2, Expr).
-%% In syntax trees, module/package names are atoms or lists of atoms.
-
-expand_module_name({atom,L,A} = M, Bs) ->
- case binding({module,A}, Bs) of
- {value, A1} ->
- {atom,L,A1};
- unbound ->
- case packages:is_segmented(A) of
- true ->
- M;
- false ->
-%%% P = case binding({module,'$package'}, Bs) of
-%%% {value, P1} -> P1;
-%%% unbound -> ""
-%%% end,
-%%% A1 = list_to_atom(packages:concat(P, A)),
-%%% {atom,L,list_to_atom(A1)}
- {atom,L,A}
- end
- end;
-expand_module_name(M, _) ->
- case erl_parse:package_segments(M) of
- error ->
- M;
- M1 ->
- L = element(2,M),
- Mod = packages:concat(M1),
- case packages:is_valid(Mod) of
- true ->
- {atom,L,list_to_atom(Mod)};
- false ->
- erlang:raise(error, {bad_module_name, Mod}, stacktrace())
- end
- end.
-
%% {?MODULE,expr,3} is still the stacktrace, despite the
%% fact that expr() now takes two, three or four arguments...
stacktrace() -> [{?MODULE,expr,3}].
diff --git a/lib/stdlib/src/erl_expand_records.erl b/lib/stdlib/src/erl_expand_records.erl
index 9759a8f001..d05f630d8e 100644
--- a/lib/stdlib/src/erl_expand_records.erl
+++ b/lib/stdlib/src/erl_expand_records.erl
@@ -135,8 +135,6 @@ pattern({tuple,Line,Ps}, St0) ->
%%pattern({struct,Line,Tag,Ps}, St0) ->
%% {TPs,TPsvs,St1} = pattern_list(Ps, St0),
%% {{struct,Line,Tag,TPs},TPsvs,St1};
-pattern({record_field,_,_,_}=M, St) ->
- {M,St}; % must be a package name
pattern({record_index,Line,Name,Field}, St) ->
{index_expr(Line, Field, Name, record_fields(Name, St)),St};
pattern({record,Line,Name,Pfs}, St0) ->
@@ -306,8 +304,6 @@ expr({tuple,Line,Es0}, St0) ->
%%expr({struct,Line,Tag,Es0}, Vs, St0) ->
%% {Es1,Esvs,Esus,St1} = expr_list(Es0, Vs, St0),
%% {{struct,Line,Tag,Es1},Esvs,Esus,St1};
-expr({record_field,_,_,_}=M, St) ->
- {M,St}; % must be a package name
expr({record_index,Line,Name,F}, St) ->
I = index_expr(Line, F, Name, record_fields(Name, St)),
expr(I, St);
@@ -375,15 +371,9 @@ expr({call,Line,{atom,_La,N}=Atom,As0}, St0) ->
end
end
end;
-expr({call,Line,{record_field,_,_,_}=M,As0}, St0) ->
- {As,St1} = expr_list(As0, St0),
- {{call,Line,M,As},St1};
expr({call,Line,{remote,Lr,M,F},As0}, St0) ->
{[M1,F1 | As1],St1} = expr_list([M,F | As0], St0),
{{call,Line,{remote,Lr,M1,F1},As1},St1};
-expr({call,Line,{tuple,Lt,[{atom,_,_}=M,{atom,_,_}=F]},As0}, St0) ->
- {As,St1} = expr_list(As0, St0),
- {{call,Line,{tuple,Lt,[M,F]},As},St1};
expr({call,Line,F,As0}, St0) ->
{[Fun1 | As1],St1} = expr_list([F | As0], St0),
{{call,Line,Fun1,As1},St1};
diff --git a/lib/stdlib/src/erl_internal.erl b/lib/stdlib/src/erl_internal.erl
index 3063881890..bf2fffbd97 100644
--- a/lib/stdlib/src/erl_internal.erl
+++ b/lib/stdlib/src/erl_internal.erl
@@ -278,6 +278,7 @@ bif(exit, 1) -> true;
bif(exit, 2) -> true;
bif(float, 1) -> true;
bif(float_to_list, 1) -> true;
+bif(float_to_list, 2) -> true;
bif(garbage_collect, 0) -> true;
bif(garbage_collect, 1) -> true;
bif(get, 0) -> true;
diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl
index abab81d31f..642d972582 100644
--- a/lib/stdlib/src/erl_lint.erl
+++ b/lib/stdlib/src/erl_lint.erl
@@ -94,12 +94,9 @@ value_option(Flag, Default, On, OnVal, Off, OffVal, Opts) ->
%% the other function collections contain {Function, Arity}.
-record(lint, {state=start :: 'start' | 'attribute' | 'function',
module=[], %Module
- package="", %Module package
- extends=[], %Extends
behaviour=[], %Behaviour
exports=gb_sets:empty() :: gb_set(), %Exports
imports=[], %Imports
- mod_imports=dict:new() :: dict(), %Module Imports
compile=[], %Compile flags
records=dict:new() :: dict(), %Record definitions
locals=gb_sets:empty() :: gb_set(), %All defined functions (prescanned)
@@ -114,7 +111,6 @@ value_option(Flag, Default, On, OnVal, Off, OffVal, Opts) ->
enabled_warnings=[], %All enabled warnings (ordset).
errors=[], %Current errors
warnings=[], %Current warnings
- global_vt=[], %The global VarTable
file = "" :: string(), %From last file attribute
recdef_top=false :: boolean(), %true in record initialisation
%outside any fun or lc
@@ -144,10 +140,8 @@ format_error({bad_module_name, M}) ->
io_lib:format("bad module name '~s'", [M]);
format_error(redefine_module) ->
"redefining module";
-format_error(redefine_extends) ->
- "redefining extends attribute";
-format_error(extends_self) ->
- "cannot extend from self";
+format_error(pmod_unsupported) ->
+ "parameterized modules are no longer supported";
%% format_error({redefine_mod_import, M, P}) ->
%% io_lib:format("module '~s' already imported from package '~s'", [M, P]);
@@ -168,10 +162,6 @@ format_error({bad_inline,{F,A}}) ->
io_lib:format("inlined function ~w/~w undefined", [F,A]);
format_error({invalid_deprecated,D}) ->
io_lib:format("badly formed deprecated attribute ~w", [D]);
-format_error(invalid_extends) ->
- "badly formed extends attribute";
-format_error(define_instance) ->
- "defining instance function not allowed in abstract module";
format_error({bad_deprecated,{F,A}}) ->
io_lib:format("deprecated function ~w/~w undefined or not exported", [F,A]);
format_error({bad_nowarn_unused_function,{F,A}}) ->
@@ -248,6 +238,8 @@ format_error({illegal_guard_local_call, {F,A}}) ->
io_lib:format("call to local/imported function ~w/~w is illegal in guard",
[F,A]);
format_error(illegal_guard_expr) -> "illegal guard expression";
+format_error(deprecated_tuple_fun) ->
+ "tuple funs are deprecated and will be removed in R16";
%% --- exports ---
format_error({explicit_export,F,A}) ->
io_lib:format("in this release, the call to ~w/~w must be written "
@@ -363,6 +355,12 @@ format_error(callback_wrong_arity) ->
format_error({imported_predefined_type, Name}) ->
io_lib:format("referring to built-in type ~w as a remote type; "
"please take out the module name", [Name]);
+format_error({not_exported_opaque, {TypeName, Arity}}) ->
+ io_lib:format("opaque type ~w~s is not exported",
+ [TypeName, gen_type_paren(Arity)]);
+format_error({underspecified_opaque, {TypeName, Arity}}) ->
+ io_lib:format("opaque type ~w~s is underspecified and therefore meaningless",
+ [TypeName, gen_type_paren(Arity)]);
%% --- obsolete? unused? ---
format_error({format_error, {Fmt, Args}}) ->
io_lib:format(Fmt, Args);
@@ -531,7 +529,6 @@ start(File, Opts) ->
end,
#lint{state = start,
exports = gb_sets:from_list([{module_info,0},{module_info,1}]),
- mod_imports = dict:from_list([{erlang,erlang}]),
compile = Opts,
%% Internal pseudo-functions must appear as defined/reached.
defined = gb_sets:from_list(pseudolocals()),
@@ -617,8 +614,6 @@ forms(Forms0, St0) ->
pre_scan([{function,_L,new,_A,_Cs} | Fs], St) ->
pre_scan(Fs, St#lint{new=true});
-pre_scan([{attribute,_L,extends,M} | Fs], St) when is_atom(M) ->
- pre_scan(Fs, St#lint{extends=true});
pre_scan([{attribute,L,compile,C} | Fs], St) ->
case is_warn_enabled(export_all, St) andalso
member(export_all, lists:flatten([C])) of
@@ -673,51 +668,15 @@ form(Form, #lint{state=State}=St) ->
%% start_state(Form, State) -> State'
-start_state({attribute,L,module,{M,Ps}}, St) ->
- St1 = set_module(M, L, St),
- Arity = length(Ps),
- Ps1 = if is_atom(St1#lint.extends) ->
- ['BASE', 'THIS' | Ps];
- true ->
- ['THIS' | Ps]
- end,
- Vt = orddict:from_list([{V, {bound, used, []}} || V <- Ps1]),
- St2 = add_instance(Arity, St1),
- St3 = ensure_new(Arity, St2),
- St3#lint{state=attribute, extends=[], global_vt=Vt};
-start_state({attribute,L,module,M}, St) ->
- St1 = set_module(M, L, St),
- St1#lint{state=attribute, extends=[]};
+start_state({attribute,Line,module,{_,_}}=Form, St0) ->
+ St1 = add_error(Line, pmod_unsupported, St0),
+ attribute_state(Form, St1#lint{state=attribute});
+start_state({attribute,_,module,M}, St0) ->
+ St1 = St0#lint{module=M},
+ St1#lint{state=attribute};
start_state(Form, St) ->
St1 = add_error(element(2, Form), undefined_module, St),
- attribute_state(Form, St1#lint{state=attribute, extends=[]}).
-
-set_module(M, L, St) ->
- M1 = package_to_string(M),
- case packages:is_valid(M1) of
- true ->
- St#lint{module=list_to_atom(M1),
- package=packages:strip_last(M1)};
- false ->
- add_error(L, {bad_module_name, M1}, St)
- end.
-
-ensure_new(Arity, St) ->
- case St#lint.new of
- true ->
- St;
- false ->
- add_func(new, Arity, St)
- end.
-
-add_instance(Arity, St) ->
- A = Arity + (if is_atom(St#lint.extends) -> 1; true -> 0 end),
- add_func(instance, A, St).
-
-add_func(Name, Arity, St) ->
- F = {Name, Arity},
- St#lint{exports = gb_sets:add_element(F, St#lint.exports),
- defined = gb_sets:add_element(F, St#lint.defined)}.
+ attribute_state(Form, St1#lint{state=attribute}).
%% attribute_state(Form, State) ->
%% State'
@@ -726,15 +685,6 @@ attribute_state({attribute,_L,module,_M}, #lint{module=[]}=St) ->
St;
attribute_state({attribute,L,module,_M}, St) ->
add_error(L, redefine_module, St);
-attribute_state({attribute,L,extends,M}, #lint{module=M}=St) when is_atom(M) ->
- add_error(L, extends_self, St);
-attribute_state({attribute,_L,extends,M}, #lint{extends=[]}=St)
- when is_atom(M) ->
- St#lint{extends=M};
-attribute_state({attribute,L,extends,M}, St) when is_atom(M) ->
- add_error(L, redefine_extends, St);
-attribute_state({attribute,L,extends,_M}, St) ->
- add_error(L, invalid_extends, St);
attribute_state({attribute,L,export,Es}, St) ->
export(L, Es, St);
attribute_state({attribute,L,export_type,Es}, St) ->
@@ -849,7 +799,8 @@ post_traversal_check(Forms, St0) ->
StC = check_untyped_records(Forms, StB),
StD = check_on_load(StC),
StE = check_unused_records(Forms, StD),
- check_callback_information(StE).
+ StF = check_local_opaque_types(StE),
+ check_callback_information(StF).
%% check_behaviour(State0) -> State
%% Check that the behaviour attribute is valid.
@@ -998,9 +949,9 @@ check_imports(Forms, St0) ->
true ->
Usage = St0#lint.usage,
Unused = ordsets:subtract(St0#lint.imports, Usage#usage.imported),
- Imports = [{{FA,list_to_atom(package_to_string(Mod))},L}
- || {attribute,L,import,{Mod,Fs}} <- Forms,
- FA <- lists:usort(Fs)],
+ Imports = [{{FA,Mod},L} ||
+ {attribute,L,import,{Mod,Fs}} <- Forms,
+ FA <- lists:usort(Fs)],
Bad = [{FM,L} || FM <- Unused, {FM2,L} <- Imports, FM =:= FM2],
func_line_warning(unused_import, Bad, St0)
end.
@@ -1213,73 +1164,46 @@ export_type(Line, ETs, #lint{usage = Usage, exp_types = ETs0} = St0) ->
-spec import(line(), import(), lint_state()) -> lint_state().
import(Line, {Mod,Fs}, St) ->
- Mod1 = package_to_string(Mod),
- case packages:is_valid(Mod1) of
- true ->
- Mfs = ordsets:from_list(Fs),
- case check_imports(Line, Mfs, St#lint.imports) of
- [] ->
- St#lint{imports=add_imports(list_to_atom(Mod1), Mfs,
- St#lint.imports)};
- Efs ->
- {Err, St1} =
- foldl(fun ({bif,{F,A},_}, {Err,St0}) ->
- %% BifClash - import directive
- Warn = is_warn_enabled(bif_clash, St0)
- and (not bif_clash_specifically_disabled(St0,{F,A})),
- AutoImpSup = is_autoimport_suppressed(St0#lint.no_auto,{F,A}),
- OldBif = erl_internal:old_bif(F,A),
- {Err,if
- Warn and (not AutoImpSup) and OldBif ->
- add_error
- (Line,
- {redefine_old_bif_import, {F,A}},
- St0);
- Warn and (not AutoImpSup) ->
- add_warning
- (Line,
- {redefine_bif_import, {F,A}},
- St0);
- true ->
- St0
- end};
- (Ef, {_Err,St0}) ->
- {true,add_error(Line,
- {redefine_import,Ef},
- St0)}
- end,
- {false,St}, Efs),
- if
- not Err ->
- St1#lint{imports=
- add_imports(list_to_atom(Mod1), Mfs,
+ Mfs = ordsets:from_list(Fs),
+ case check_imports(Line, Mfs, St#lint.imports) of
+ [] ->
+ St#lint{imports=add_imports(Mod, Mfs,
+ St#lint.imports)};
+ Efs ->
+ {Err, St1} =
+ foldl(fun ({bif,{F,A},_}, {Err,St0}) ->
+ %% BifClash - import directive
+ Warn = is_warn_enabled(bif_clash, St0) andalso
+ (not bif_clash_specifically_disabled(St0,{F,A})),
+ AutoImpSup = is_autoimport_suppressed(St0#lint.no_auto,{F,A}),
+ OldBif = erl_internal:old_bif(F,A),
+ {Err,if
+ Warn and (not AutoImpSup) and OldBif ->
+ add_error
+ (Line,
+ {redefine_old_bif_import, {F,A}},
+ St0);
+ Warn and (not AutoImpSup) ->
+ add_warning
+ (Line,
+ {redefine_bif_import, {F,A}},
+ St0);
+ true ->
+ St0
+ end};
+ (Ef, {_Err,St0}) ->
+ {true,add_error(Line,
+ {redefine_import,Ef},
+ St0)}
+ end,
+ {false,St}, Efs),
+ if
+ not Err ->
+ St1#lint{imports=add_imports(Mod, Mfs,
St#lint.imports)};
- true ->
- St1
- end
- end;
- false ->
- add_error(Line, {bad_module_name, Mod1}, St)
- end;
-import(Line, Mod, St) ->
- Mod1 = package_to_string(Mod),
- case packages:is_valid(Mod1) of
- true ->
- Key = list_to_atom(packages:last(Mod1)),
- Imps = St#lint.mod_imports,
-%%% case dict:is_key(Key, Imps) of
-%%% true ->
-%%% M = packages:last(Mod1),
-%%% P = packages:strip_last(Mod1),
-%%% add_error(Line, {redefine_mod_import, M, P}, St);
-%%% false ->
-%%% St#lint{mod_imports =
-%%% dict:store(Key, list_to_atom(Mod1), Imps)}
-%%% end;
- St#lint{mod_imports = dict:store(Key, list_to_atom(Mod1),
- Imps)};
- false ->
- add_error(Line, {bad_module_name, Mod1}, St)
+ true ->
+ St1
+ end
end.
check_imports(_Line, Fs, Is) ->
@@ -1353,11 +1277,9 @@ call_function(Line, F, A, #lint{usage=Usage0,called=Cd,func=Func}=St) ->
%% function(Line, Name, Arity, Clauses, State) -> State.
-function(Line, instance, _Arity, _Cs, St) when St#lint.global_vt =/= [] ->
- add_error(Line, define_instance, St);
function(Line, Name, Arity, Cs, St0) ->
St1 = define_function(Line, Name, Arity, St0#lint{func={Name,Arity}}),
- clauses(Cs, St1#lint.global_vt, St1).
+ clauses(Cs, St1).
-spec define_function(line(), atom(), arity(), lint_state()) -> lint_state().
@@ -1380,15 +1302,16 @@ function_check_max_args(Line, Arity, St) when Arity > ?MAX_ARGUMENTS ->
add_error(Line, {too_many_arguments,Arity}, St);
function_check_max_args(_, _, St) -> St.
-%% clauses([Clause], VarTable, State) -> {VarTable, State}.
+%% clauses([Clause], State) -> {VarTable, State}.
-clauses(Cs, Vt, St) ->
+clauses(Cs, St) ->
foldl(fun (C, St0) ->
- {_,St1} = clause(C, Vt, St0),
+ {_,St1} = clause(C, St0),
St1
end, St, Cs).
-clause({clause,_Line,H,G,B}, Vt0, St0) ->
+clause({clause,_Line,H,G,B}, St0) ->
+ Vt0 = [],
{Hvt,Binvt,St1} = head(H, Vt0, St0),
%% Cannot ignore BinVt since "binsize variables" may have been used.
Vt1 = vtupdate(Hvt, vtupdate(Binvt, Vt0)),
@@ -1454,13 +1377,6 @@ pattern({record_index,Line,Name,Field}, _Vt, _Old, _Bvt, St) ->
pattern_field(Field, Name, Dfs, St1)
end),
{Vt1,[],St1};
-pattern({record_field,Line,_,_}=M, _Vt, _Old, _Bvt, St0) ->
- case expand_package(M, St0) of
- {error, St1} ->
- {[],[],add_error(Line, illegal_expr, St1)};
- {_, St1} ->
- {[],[],St1}
- end;
pattern({record,Line,Name,Pfs}, Vt, Old, Bvt, St) ->
case dict:find(Name, St#lint.records) of
{ok,{_Line,Fields}} ->
@@ -1842,13 +1758,6 @@ gexpr({tuple,_Line,Es}, Vt, St) ->
gexpr({record_index,Line,Name,Field}, _Vt, St) ->
check_record(Line, Name, St,
fun (Dfs, St1) -> record_field(Field, Name, Dfs, St1) end );
-gexpr({record_field,Line,_,_}=M, _Vt, St0) ->
- case expand_package(M, St0) of
- {error, St1} ->
- {[],add_error(Line, illegal_expr, St1)};
- {_, St1} ->
- {[], St1}
- end;
gexpr({record_field,Line,Rec,Name,Field}, Vt, St0) ->
{Rvt,St1} = gexpr(Rec, Vt, St0),
{Fvt,St2} = check_record(Line, Name, St1,
@@ -1914,8 +1823,6 @@ gexpr({call,Line,{remote,_Lr,{atom,_Lm,erlang},{atom,_Lf,F}},As}, Vt, St0) ->
true -> {Asvt,St1};
false -> {Asvt,add_error(Line, illegal_guard_expr, St1)}
end;
-gexpr({call,L,{tuple,Lt,[{atom,Lm,erlang},{atom,Lf,F}]},As}, Vt, St) ->
- gexpr({call,L,{remote,Lt,{atom,Lm,erlang},{atom,Lf,F}},As}, Vt, St);
gexpr({op,Line,Op,A}, Vt, St0) ->
{Avt,St1} = gexpr(A, Vt, St0),
case is_gexpr_op(Op, 1) of
@@ -1989,8 +1896,6 @@ is_gexpr({tuple,_L,Es}, RDs) -> is_gexpr_list(Es, RDs);
%% is_gexpr_list(Es, RDs);
is_gexpr({record_index,_L,_Name,Field}, RDs) ->
is_gexpr(Field, RDs);
-is_gexpr({record_field,_L,_,_}=M, _RDs) ->
- erl_parse:package_segments(M) =/= error;
is_gexpr({record_field,_L,Rec,_Name,Field}, RDs) ->
is_gexpr_list([Rec,Field], RDs);
is_gexpr({record,L,Name,Inits}, RDs) ->
@@ -2079,13 +1984,6 @@ expr({record,Line,Name,Inits}, Vt, St) ->
fun (Dfs, St1) ->
init_fields(Inits, Line, Name, Dfs, Vt, St1)
end);
-expr({record_field,Line,_,_}=M, _Vt, St0) ->
- case expand_package(M, St0) of
- {error, St1} ->
- {[],add_error(Line, illegal_expr, St1)};
- {_, St1} ->
- {[], St1}
- end;
expr({record_field,Line,Rec,Name,Field}, Vt, St0) ->
{Rvt,St1} = record_expr(Line, Rec, Vt, St0),
{Fvt,St2} = check_record(Line, Name, St1,
@@ -2156,20 +2054,14 @@ expr({call,Line,{remote,_Lr,{atom,_Lm,erlang},{atom,Lf,is_record}},[E,A]},
expr({call,Line,{atom,Lf,is_record},[E,A]}, Vt, St0);
expr({call,L,{tuple,Lt,[{atom,Lm,erlang},{atom,Lf,is_record}]},As}, Vt, St) ->
expr({call,L,{remote,Lt,{atom,Lm,erlang},{atom,Lf,is_record}},As}, Vt, St);
+expr({call,Line,{remote,_Lr,{atom,_Lm,M},{atom,Lf,F}},As}, Vt, St0) ->
+ St1 = keyword_warning(Lf, F, St0),
+ St2 = check_remote_function(Line, M, F, As, St1),
+ expr_list(As, Vt, St2);
expr({call,Line,{remote,_Lr,M,F},As}, Vt, St0) ->
- case expand_package(M, St0) of
- {error, _} ->
- expr_list([M,F|As], Vt, St0);
- {{atom,_La,M1}, St1} ->
- case F of
- {atom,Lf,F1} ->
- St2 = keyword_warning(Lf, F1, St1),
- St3 = check_remote_function(Line, M1, F1, As, St2),
- expr_list(As, Vt, St3);
- _ ->
- expr_list([F|As], Vt, St1)
- end
- end;
+ St1 = keyword_warning(Line, M, St0),
+ St2 = keyword_warning(Line, F, St1),
+ expr_list([M,F|As], Vt, St2);
expr({call,Line,{atom,La,F},As}, Vt, St0) ->
St1 = keyword_warning(La, F, St0),
{Asvt,St2} = expr_list(As, Vt, St1),
@@ -2225,13 +2117,6 @@ expr({call,Line,{atom,La,F},As}, Vt, St0) ->
end
end}
end;
-expr({call,Line,{record_field,_,_,_}=F,As}, Vt, St0) ->
- case expand_package(F, St0) of
- {error, _} ->
- expr_list([F|As], Vt, St0);
- {A, St1} ->
- expr({call,Line,A,As}, Vt, St1)
- end;
expr({call,Line,F,As}, Vt, St0) ->
St = warn_invalid_call(Line,F,St0),
expr_list([F|As], Vt, St); %They see the same variables
@@ -2274,9 +2159,7 @@ expr({op,_Line,_Op,L,R}, Vt, St) ->
expr_list([L,R], Vt, St); %They see the same variables
%% The following are not allowed to occur anywhere!
expr({remote,Line,_M,_F}, _Vt, St) ->
- {[],add_error(Line, illegal_expr, St)};
-expr({'query',Line,_Q}, _Vt, St) ->
- {[],add_error(Line, {mnemosyne,"query"}, St)}.
+ {[],add_error(Line, illegal_expr, St)}.
%% expr_list(Expressions, Variables, State) ->
%% {UsedVarTable,State}
@@ -2554,15 +2437,24 @@ find_field(_F, []) -> error.
%% Attr :: 'type' | 'opaque'
%% Checks that a type definition is valid.
+-record(typeinfo, {attr, line}).
+
type_def(_Attr, _Line, {record, _RecName}, Fields, [], St0) ->
%% The record field names and such are checked in the record format.
%% We only need to check the types.
Types = [T || {typed_record_field, _, T} <- Fields],
check_type({type, -1, product, Types}, St0);
-type_def(_Attr, Line, TypeName, ProtoType, Args, St0) ->
+type_def(Attr, Line, TypeName, ProtoType, Args, St0) ->
TypeDefs = St0#lint.types,
Arity = length(Args),
TypePair = {TypeName, Arity},
+ Info = #typeinfo{attr = Attr, line = Line},
+ StoreType =
+ fun(St) ->
+ NewDefs = dict:store(TypePair, Info, TypeDefs),
+ CheckType = {type, -1, product, [ProtoType|Args]},
+ check_type(CheckType, St#lint{types=NewDefs})
+ end,
case (dict:is_key(TypePair, TypeDefs) orelse is_var_arity_type(TypeName)) of
true ->
case dict:is_key(TypePair, default_types()) of
@@ -2572,20 +2464,29 @@ type_def(_Attr, Line, TypeName, ProtoType, Args, St0) ->
true ->
Warn = {new_builtin_type, TypePair},
St1 = add_warning(Line, Warn, St0),
- NewDefs = dict:store(TypePair, Line, TypeDefs),
- CheckType = {type, -1, product, [ProtoType|Args]},
- check_type(CheckType, St1#lint{types=NewDefs});
+ StoreType(St1);
false ->
add_error(Line, {builtin_type, TypePair}, St0)
end;
false -> add_error(Line, {redefine_type, TypePair}, St0)
end;
false ->
- NewDefs = dict:store(TypePair, Line, TypeDefs),
- CheckType = {type, -1, product, [ProtoType|Args]},
- check_type(CheckType, St0#lint{types=NewDefs})
+ St1 = case
+ Attr =:= opaque andalso
+ is_underspecified(ProtoType, Arity)
+ of
+ true ->
+ Warn = {underspecified_opaque, TypePair},
+ add_warning(Line, Warn, St0);
+ false -> St0
+ end,
+ StoreType(St1)
end.
+is_underspecified({type,_,term,[]}, 0) -> true;
+is_underspecified({type,_,any,[]}, 0) -> true;
+is_underspecified(_ProtType, _Arity) -> false.
+
check_type(Types, St) ->
{SeenVars, St1} = check_type(Types, dict:new(), St),
dict:fold(fun(Var, {seen_once, Line}, AccSt) ->
@@ -2895,7 +2796,7 @@ check_unused_types(Forms, #lint{usage=Usage, types=Ts, exp_types=ExpTs}=St) ->
fun(_Type, -1, AccSt) ->
%% Default type
AccSt;
- (Type, FileLine, AccSt) ->
+ (Type, #typeinfo{line = FileLine}, AccSt) ->
case loc(FileLine) of
{FirstFile, _} ->
case gb_sets:is_member(Type, UsedTypes) of
@@ -2914,6 +2815,24 @@ check_unused_types(Forms, #lint{usage=Usage, types=Ts, exp_types=ExpTs}=St) ->
St
end.
+check_local_opaque_types(St) ->
+ #lint{types=Ts, exp_types=ExpTs} = St,
+ FoldFun =
+ fun(_Type, -1, AccSt) ->
+ %% Default type
+ AccSt;
+ (_Type, #typeinfo{attr = type}, AccSt) ->
+ AccSt;
+ (Type, #typeinfo{attr = opaque, line = FileLine}, AccSt) ->
+ case gb_sets:is_element(Type, ExpTs) of
+ true -> AccSt;
+ false ->
+ Warn = {not_exported_opaque,Type},
+ add_warning(FileLine, Warn, AccSt)
+ end
+ end,
+ dict:fold(FoldFun, St, Ts).
+
%% icrt_clauses(Clauses, In, ImportVarTable, State) ->
%% {NewVts,State}.
@@ -3575,6 +3494,10 @@ extract_sequence(4, [$t, $c | Fmt], Need) ->
extract_sequence(5, [$c|Fmt], Need);
extract_sequence(4, [$t, $s | Fmt], Need) ->
extract_sequence(5, [$s|Fmt], Need);
+extract_sequence(4, [$t, $p | Fmt], Need) ->
+ extract_sequence(5, [$p|Fmt], Need);
+extract_sequence(4, [$t, $P | Fmt], Need) ->
+ extract_sequence(5, [$P|Fmt], Need);
extract_sequence(4, [$t, C | _Fmt], _Need) ->
{error,"invalid control ~t" ++ [C]};
extract_sequence(4, Fmt, Need) ->
@@ -3611,49 +3534,6 @@ control_type($n, Need) -> Need;
control_type($i, Need) -> [term|Need];
control_type(_C, _Need) -> error.
-%% In syntax trees, module/package names are atoms or lists of atoms.
-
-package_to_string(A) when is_atom(A) -> atom_to_list(A);
-package_to_string(L) when is_list(L) -> packages:concat(L).
-
-expand_package({atom,L,A} = M, St0) ->
- St1 = keyword_warning(L, A, St0),
- case dict:find(A, St1#lint.mod_imports) of
- {ok, A1} ->
- {{atom,L,A1}, St1};
- error ->
- Name = atom_to_list(A),
- case packages:is_valid(Name) of
- true ->
- case packages:is_segmented(Name) of
- true ->
- {M, St1};
- false ->
- M1 = packages:concat(St1#lint.package,
- Name),
- {{atom,L,list_to_atom(M1)}, St1}
- end;
- false ->
- St2 = add_error(L, {bad_module_name, Name}, St1),
- {error, St2}
- end
- end;
-expand_package(M, St0) ->
- L = element(2, M),
- case erl_parse:package_segments(M) of
- error ->
- {error, St0};
- M1 ->
- Name = package_to_string(M1),
- case packages:is_valid(Name) of
- true ->
- {{atom,L,list_to_atom(Name)}, St0};
- false ->
- St1 = add_error(L, {bad_module_name, Name}, St0),
- {error, St1}
- end
- end.
-
%% Prebuild set of local functions (to override auto-import)
local_functions(Forms) ->
diff --git a/lib/stdlib/src/erl_parse.yrl b/lib/stdlib/src/erl_parse.yrl
index 928c10f7f2..8316462989 100644
--- a/lib/stdlib/src/erl_parse.yrl
+++ b/lib/stdlib/src/erl_parse.yrl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -26,7 +26,7 @@ attribute attr_val
function function_clauses function_clause
clause_args clause_guard clause_body
expr expr_100 expr_150 expr_160 expr_200 expr_300 expr_400 expr_500
-expr_600 expr_700 expr_800 expr_900
+expr_600 expr_700 expr_800
expr_max
list tail
list_comprehension lc_expr lc_exprs
@@ -36,7 +36,7 @@ tuple
record_expr record_tuple record_field record_fields
if_expr if_clause if_clauses case_expr cr_clause cr_clauses receive_expr
fun_expr fun_clause fun_clauses atom_or_var integer_or_var
-try_expr try_catch try_clause try_clauses query_expr
+try_expr try_catch try_clause try_clauses
function_call argument_list
exprs guard
atomic strings
@@ -54,7 +54,7 @@ char integer float atom string var
'(' ')' ',' '->' ':-' '{' '}' '[' ']' '|' '||' '<-' ';' ':' '#' '.'
'after' 'begin' 'case' 'try' 'catch' 'end' 'fun' 'if' 'of' 'receive' 'when'
-'andalso' 'orelse' 'query'
+'andalso' 'orelse'
'bnot' 'not'
'*' '/' 'div' 'rem' 'band' 'and'
'+' '-' 'bor' 'bxor' 'bsl' 'bsr' 'or' 'xor'
@@ -253,15 +253,9 @@ expr_700 -> function_call : '$1'.
expr_700 -> record_expr : '$1'.
expr_700 -> expr_800 : '$1'.
-expr_800 -> expr_900 ':' expr_max :
+expr_800 -> expr_max ':' expr_max :
{remote,?line('$2'),'$1','$3'}.
-expr_800 -> expr_900 : '$1'.
-
-expr_900 -> '.' atom :
- {record_field,?line('$1'),{atom,?line('$1'),''},'$2'}.
-expr_900 -> expr_900 '.' atom :
- {record_field,?line('$2'),'$1','$3'}.
-expr_900 -> expr_max : '$1'.
+expr_800 -> expr_max : '$1'.
expr_max -> var : '$1'.
expr_max -> atomic : '$1'.
@@ -278,7 +272,6 @@ expr_max -> case_expr : '$1'.
expr_max -> receive_expr : '$1'.
expr_max -> fun_expr : '$1'.
expr_max -> try_expr : '$1'.
-expr_max -> query_expr : '$1'.
list -> '[' ']' : {nil,?line('$1')}.
@@ -438,9 +431,6 @@ try_clause -> var ':' expr clause_guard clause_body :
L = ?line('$1'),
{clause,L,[{tuple,L,['$1','$3',{var,L,'_'}]}],'$4','$5'}.
-query_expr -> 'query' list_comprehension 'end' :
- {'query',?line('$1'),'$2'}.
-
argument_list -> '(' ')' : {[],?line('$1')}.
argument_list -> '(' exprs ')' : {'$2',?line('$1')}.
@@ -510,7 +500,7 @@ Erlang code.
-export([parse_form/1,parse_exprs/1,parse_term/1]).
-export([normalise/1,abstract/1,tokens/1,tokens/2]).
--export([abstract/2, package_segments/1]).
+-export([abstract/2]).
-export([inop_prec/1,preop_prec/1,func_prec/0,max_prec/0]).
-export([set_line/2,get_attribute/2,get_attributes/1]).
@@ -679,20 +669,6 @@ build_attribute({atom,La,module}, Val) ->
{attribute,La,module,Module};
[{atom,_Lm,Module},ExpList] ->
{attribute,La,module,{Module,var_list(ExpList)}};
- [Name] ->
- case package_segments(Name) of
- error ->
- error_bad_decl(La, module);
- Module ->
- {attribute,La,module,Module}
- end;
- [Name,ExpList] ->
- case package_segments(Name) of
- error ->
- error_bad_decl(La, module);
- Module ->
- {attribute,La,module,{Module,var_list(ExpList)}}
- end;
_Other ->
error_bad_decl(La, module)
end;
@@ -704,22 +680,8 @@ build_attribute({atom,La,export}, Val) ->
end;
build_attribute({atom,La,import}, Val) ->
case Val of
- [Name] ->
- case package_segments(Name) of
- error ->
- error_bad_decl(La, import);
- Module ->
- {attribute,La,import,Module}
- end;
[{atom,_Lm,Mod},ImpList] ->
{attribute,La,import,{Mod,farity_list(ImpList)}};
- [Name, ImpList] ->
- case package_segments(Name) of
- error ->
- error_bad_decl(La, import);
- Module ->
- {attribute,La,import,{Module,farity_list(ImpList)}}
- end;
_Other -> error_bad_decl(La, import)
end;
build_attribute({atom,La,record}, Val) ->
@@ -820,18 +782,6 @@ term(Expr) ->
catch _:_R -> ret_err(?line(Expr), "bad attribute")
end.
-package_segments(Name) ->
- package_segments(Name, [], []).
-
-package_segments({record_field, _, F1, F2}, Fs, As) ->
- package_segments(F1, [F2 | Fs], As);
-package_segments({atom, _, A}, [F | Fs], As) ->
- package_segments(F, Fs, [A | As]);
-package_segments({atom, _, A}, [], As) ->
- lists:reverse([A | As]);
-package_segments(_, _, _) ->
- error.
-
%% build_function([Clause]) -> {function,Line,Name,Arity,[Clause]}
build_function(Cs) ->
@@ -900,12 +850,6 @@ normalise({cons,_,Head,Tail}) ->
[normalise(Head)|normalise(Tail)];
normalise({tuple,_,Args}) ->
list_to_tuple(normalise_list(Args));
-%% Atom dot-notation, as in 'foo.bar.baz'
-normalise({record_field,_,_,_}=A) ->
- case package_segments(A) of
- error -> erlang:error({badarg, A});
- As -> list_to_atom(packages:concat(As))
- end;
%% Special case for unary +/-.
normalise({op,_,'+',{char,_,I}}) -> I;
normalise({op,_,'+',{integer,_,I}}) -> I;
@@ -923,73 +867,77 @@ normalise_list([]) ->
-spec abstract(Data) -> AbsTerm when
Data :: term(),
AbsTerm :: abstract_expr().
-abstract(T) when is_integer(T) -> {integer,0,T};
-abstract(T) when is_float(T) -> {float,0,T};
-abstract(T) when is_atom(T) -> {atom,0,T};
-abstract([]) -> {nil,0};
-abstract(B) when is_bitstring(B) ->
- {bin, 0, [abstract_byte(Byte, 0) || Byte <- bitstring_to_list(B)]};
-abstract([C|T]) when is_integer(C), 0 =< C, C < 256 ->
- abstract_string(T, [C]);
-abstract([H|T]) ->
- {cons,0,abstract(H),abstract(T)};
-abstract(Tuple) when is_tuple(Tuple) ->
- {tuple,0,abstract_list(tuple_to_list(Tuple))}.
-
-abstract_string([C|T], String) when is_integer(C), 0 =< C, C < 256 ->
- abstract_string(T, [C|String]);
-abstract_string([], String) ->
- {string, 0, lists:reverse(String)};
-abstract_string(T, String) ->
- not_string(String, abstract(T)).
-
-not_string([C|T], Result) ->
- not_string(T, {cons, 0, {integer, 0, C}, Result});
-not_string([], Result) ->
+abstract(T) ->
+ abstract(T, 0, epp:default_encoding()).
+
+%%% abstract/2 takes line and encoding options
+-spec abstract(Data, Options) -> AbsTerm when
+ Data :: term(),
+ Options :: Line | [Option],
+ Option :: {line, Line} | {encoding, Encoding},
+ Encoding :: latin1 | unicode | utf8,
+ Line :: erl_scan:line(),
+ AbsTerm :: abstract_expr().
+
+abstract(T, Line) when is_integer(Line) ->
+ abstract(T, Line, epp:default_encoding());
+abstract(T, Options) when is_list(Options) ->
+ Line = proplists:get_value(line, Options, 0),
+ Encoding = proplists:get_value(encoding, Options,epp:default_encoding()),
+ abstract(T, Line, Encoding).
+
+-define(UNICODE(C),
+ (C >= 0 andalso C < 16#D800 orelse
+ C > 16#DFFF andalso C < 16#FFFE orelse
+ C > 16#FFFF andalso C =< 16#10FFFF)).
+
+abstract(T, L, _E) when is_integer(T) -> {integer,L,T};
+abstract(T, L, _E) when is_float(T) -> {float,L,T};
+abstract(T, L, _E) when is_atom(T) -> {atom,L,T};
+abstract([], L, _E) -> {nil,L};
+abstract(B, L, _E) when is_bitstring(B) ->
+ {bin, L, [abstract_byte(Byte, L) || Byte <- bitstring_to_list(B)]};
+abstract([C|T], L, unicode=E) when ?UNICODE(C) ->
+ abstract_unicode_string(T, [C], L, E);
+abstract([C|T], L, utf8=E) when ?UNICODE(C) ->
+ abstract_unicode_string(T, [C], L, E);
+abstract([C|T], L, latin1=E) when is_integer(C), 0 =< C, C < 256 ->
+ abstract_string(T, [C], L, E);
+abstract([H|T], L, E) ->
+ {cons,L,abstract(H, L, E),abstract(T, L, E)};
+abstract(Tuple, L, E) when is_tuple(Tuple) ->
+ {tuple,L,abstract_list(tuple_to_list(Tuple), L, E)}.
+
+abstract_string([C|T], String, L, E) when is_integer(C), 0 =< C, C < 256 ->
+ abstract_string(T, [C|String], L, E);
+abstract_string([], String, L, _E) ->
+ {string, L, lists:reverse(String)};
+abstract_string(T, String, L, E) ->
+ not_string(String, abstract(T, L, E), L, E).
+
+abstract_unicode_string([C|T], String, L, E) when ?UNICODE(C) ->
+ abstract_unicode_string(T, [C|String], L, E);
+abstract_unicode_string([], String, L, _E) ->
+ {string, L, lists:reverse(String)};
+abstract_unicode_string(T, String, L, E) ->
+ not_string(String, abstract(T, L, E), L, E).
+
+not_string([C|T], Result, L, E) ->
+ not_string(T, {cons, L, {integer, L, C}, Result}, L, E);
+not_string([], Result, _L, _E) ->
Result.
-abstract_list([H|T]) ->
- [abstract(H)|abstract_list(T)];
-abstract_list([]) ->
+abstract_list([H|T], L, E) ->
+ [abstract(H, L, E)|abstract_list(T, L, E)];
+abstract_list([], _L, _E) ->
[].
-abstract_byte(Byte, Line) when is_integer(Byte) ->
- {bin_element, Line, {integer, Line, Byte}, default, default};
-abstract_byte(Bits, Line) ->
+abstract_byte(Byte, L) when is_integer(Byte) ->
+ {bin_element, L, {integer, L, Byte}, default, default};
+abstract_byte(Bits, L) ->
Sz = bit_size(Bits),
<<Val:Sz>> = Bits,
- {bin_element, Line, {integer, Line, Val}, {integer, Line, Sz}, default}.
-
-%%% abstract/2 keeps the line number
-abstract(T, Line) when is_integer(T) -> {integer,Line,T};
-abstract(T, Line) when is_float(T) -> {float,Line,T};
-abstract(T, Line) when is_atom(T) -> {atom,Line,T};
-abstract([], Line) -> {nil,Line};
-abstract(B, Line) when is_bitstring(B) ->
- {bin, Line, [abstract_byte(Byte, Line) || Byte <- bitstring_to_list(B)]};
-abstract([C|T], Line) when is_integer(C), 0 =< C, C < 256 ->
- abstract_string(T, [C], Line);
-abstract([H|T], Line) ->
- {cons,Line,abstract(H, Line),abstract(T, Line)};
-abstract(Tuple, Line) when is_tuple(Tuple) ->
- {tuple,Line,abstract_list(tuple_to_list(Tuple), Line)}.
-
-abstract_string([C|T], String, Line) when is_integer(C), 0 =< C, C < 256 ->
- abstract_string(T, [C|String], Line);
-abstract_string([], String, Line) ->
- {string, Line, lists:reverse(String)};
-abstract_string(T, String, Line) ->
- not_string(String, abstract(T, Line), Line).
-
-not_string([C|T], Result, Line) ->
- not_string(T, {cons, Line, {integer, Line, C}, Result}, Line);
-not_string([], Result, _Line) ->
- Result.
-
-abstract_list([H|T], Line) ->
- [abstract(H, Line)|abstract_list(T, Line)];
-abstract_list([], _Line) ->
- [].
+ {bin_element, L, {integer, L, Val}, {integer, L, Sz}, default}.
%% Generate a list of tokens representing the abstract term.
@@ -1079,9 +1027,9 @@ preop_prec('#') -> {700,800}.
func_prec() -> {800,700}.
--spec max_prec() -> 1000.
+-spec max_prec() -> 900.
-max_prec() -> 1000.
+max_prec() -> 900.
%%% [Experimental]. The parser just copies the attributes of the
%%% scanner tokens to the abstract format. This design decision has
diff --git a/lib/stdlib/src/erl_pp.erl b/lib/stdlib/src/erl_pp.erl
index 6b5aa951cf..0e1156075a 100644
--- a/lib/stdlib/src/erl_pp.erl
+++ b/lib/stdlib/src/erl_pp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -26,7 +26,7 @@
guard/1,guard/2,exprs/1,exprs/2,exprs/3,expr/1,expr/2,expr/3,expr/4]).
-import(lists, [append/1,foldr/3,mapfoldl/3,reverse/1,reverse/2]).
--import(io_lib, [write/1,format/2,write_char/1,write_string/1]).
+-import(io_lib, [write/1,format/2]).
-import(erl_parse, [inop_prec/1,preop_prec/1,func_prec/0,max_prec/0]).
-define(MAXLINE, 72).
@@ -36,7 +36,15 @@
CurrentIndentation :: integer(),
CurrentPrecedence :: non_neg_integer(),
HookFunction :: hook_function()) ->
- io_lib:chars())).
+ io_lib:chars())).
+
+-type(option() :: {hook, hook_function()}
+ | {encoding, latin1 | unicode | utf8}).
+-type(options() :: hook_function() | [option()]).
+
+-record(pp, {string_fun, char_fun}).
+
+-record(options, {hook, encoding, opts}).
%%%
%%% Exported functions
@@ -48,12 +56,12 @@
form(Thing) ->
form(Thing, none).
--spec(form(Form, HookFunction) -> io_lib:chars() when
+-spec(form(Form, Options) -> io_lib:chars() when
Form :: erl_parse:abstract_form(),
- HookFunction :: hook_function()).
+ Options :: options()).
-form(Thing, Hook) ->
- frmt(lform(Thing, Hook)).
+form(Thing, Options) ->
+ frmt(lform(Thing, options(Options)), state(Options)).
-spec(attribute(Attribute) -> io_lib:chars() when
Attribute :: erl_parse:abstract_form()).
@@ -61,12 +69,12 @@ form(Thing, Hook) ->
attribute(Thing) ->
attribute(Thing, none).
--spec(attribute(Attribute, HookFunction) -> io_lib:chars() when
+-spec(attribute(Attribute, Options) -> io_lib:chars() when
Attribute :: erl_parse:abstract_form(),
- HookFunction :: hook_function()).
+ Options :: options()).
-attribute(Thing, Hook) ->
- frmt(lattribute(Thing, Hook)).
+attribute(Thing, Options) ->
+ frmt(lattribute(Thing, options(Options)), state(Options)).
-spec(function(Function) -> io_lib:chars() when
Function :: erl_parse:abstract_form()).
@@ -74,18 +82,18 @@ attribute(Thing, Hook) ->
function(F) ->
function(F, none).
--spec(function(Function, HookFunction) -> io_lib:chars() when
+-spec(function(Function, Options) -> io_lib:chars() when
Function :: erl_parse:abstract_form(),
- HookFunction :: hook_function()).
+ Options :: options()).
-function(F, Hook) ->
- frmt(lfunction(F, Hook)).
+function(F, Options) ->
+ frmt(lfunction(F, options(Options)), state(Options)).
rule(R) ->
rule(R, none).
-rule(R, Hook) ->
- frmt(lrule(R, Hook)).
+rule(R, Options) ->
+ frmt(lrule(R, options(Options)), state(Options)).
-spec(guard(Guard) -> io_lib:chars() when
Guard :: [erl_parse:abstract_expr()]).
@@ -93,12 +101,12 @@ rule(R, Hook) ->
guard(Gs) ->
guard(Gs, none).
--spec(guard(Guard, HookFunction) -> io_lib:chars() when
+-spec(guard(Guard, Options) -> io_lib:chars() when
Guard :: [erl_parse:abstract_expr()],
- HookFunction :: hook_function()).
+ Options :: options()).
-guard(Gs, Hook) ->
- frmt(lguard(Gs, Hook)).
+guard(Gs, Options) ->
+ frmt(lguard(Gs, options(Options)), state(Options)).
-spec(exprs(Expressions) -> io_lib:chars() when
Expressions :: [erl_parse:abstract_expr()]).
@@ -106,99 +114,129 @@ guard(Gs, Hook) ->
exprs(Es) ->
exprs(Es, 0, none).
--spec(exprs(Expressions, HookFunction) -> io_lib:chars() when
+-spec(exprs(Expressions, Options) -> io_lib:chars() when
Expressions :: [erl_parse:abstract_expr()],
- HookFunction :: hook_function()).
+ Options :: options()).
-exprs(Es, Hook) ->
- exprs(Es, 0, Hook).
+exprs(Es, Options) ->
+ exprs(Es, 0, Options).
--spec(exprs(Expressions, Indent, HookFunction) -> io_lib:chars() when
+-spec(exprs(Expressions, Indent, Options) -> io_lib:chars() when
Expressions :: [erl_parse:abstract_expr()],
Indent :: integer(),
- HookFunction :: hook_function()).
+ Options :: options()).
-exprs(Es, I, Hook) ->
- frmt({seq,[],[],[$,],lexprs(Es, Hook)}, I).
+exprs(Es, I, Options) ->
+ frmt({seq,[],[],[$,],lexprs(Es, options(Options))}, I, state(Options)).
-spec(expr(Expression) -> io_lib:chars() when
Expression :: erl_parse:abstract_expr()).
expr(E) ->
- frmt(lexpr(E, 0, none)).
+ frmt(lexpr(E, 0, options(none)), state(none)).
--spec(expr(Expression, HookFunction) -> io_lib:chars() when
+-spec(expr(Expression, Options) -> io_lib:chars() when
Expression :: erl_parse:abstract_expr(),
- HookFunction :: hook_function()).
+ Options :: options()).
-expr(E, Hook) ->
- frmt(lexpr(E, 0, Hook)).
+expr(E, Options) ->
+ frmt(lexpr(E, 0, options(Options)), state(Options)).
--spec(expr(Expression, Indent, HookFunction) -> io_lib:chars() when
+-spec(expr(Expression, Indent, Options) -> io_lib:chars() when
Expression :: erl_parse:abstract_expr(),
Indent :: integer(),
- HookFunction :: hook_function()).
+ Options :: options()).
-expr(E, I, Hook) ->
- frmt(lexpr(E, 0, Hook), I).
+expr(E, I, Options) ->
+ frmt(lexpr(E, 0, options(Options)), I, state(Options)).
--spec(expr(Expression, Indent, Precedence, HookFunction) -> io_lib:chars() when
+-spec(expr(Expression, Indent, Precedence, Options) -> io_lib:chars() when
Expression :: erl_parse:abstract_expr(),
Indent :: integer(),
Precedence :: non_neg_integer(),
- HookFunction :: hook_function()).
+ Options :: options()).
-expr(E, I, P, Hook) ->
- frmt(lexpr(E, P, Hook), I).
+expr(E, I, P, Options) ->
+ frmt(lexpr(E, P, options(Options)), I, state(Options)).
%%%
%%% Local functions
%%%
-lform({attribute,Line,Name,Arg}, Hook) ->
- lattribute({attribute,Line,Name,Arg}, Hook);
-lform({function,Line,Name,Arity,Clauses}, Hook) ->
- lfunction({function,Line,Name,Arity,Clauses}, Hook);
-lform({rule,Line,Name,Arity,Clauses}, Hook) ->
- lrule({rule,Line,Name,Arity,Clauses}, Hook);
+options(Options) when is_list(Options) ->
+ Hook = proplists:get_value(hook, Options, none),
+ Encoding = encoding(Options),
+ #options{hook = Hook, encoding = Encoding, opts = Options};
+options(Hook) ->
+ #options{hook = Hook, encoding = encoding([]), opts = Hook}.
+
+state(Options) when is_list(Options) ->
+ case encoding(Options) of
+ latin1 -> state();
+ unicode -> unicode_state()
+ end;
+state(_Hook) ->
+ state().
+
+state() ->
+ #pp{string_fun = fun io_lib:write_unicode_string_as_latin1/1,
+ char_fun = fun io_lib:write_unicode_char_as_latin1/1}.
+
+unicode_state() ->
+ #pp{string_fun = fun io_lib:write_unicode_string/1,
+ char_fun = fun io_lib:write_unicode_char/1}.
+
+encoding(Options) ->
+ case proplists:get_value(encoding, Options, epp:default_encoding()) of
+ latin1 -> latin1;
+ utf8 -> unicode;
+ unicode -> unicode
+ end.
+
+lform({attribute,Line,Name,Arg}, Opts) ->
+ lattribute({attribute,Line,Name,Arg}, Opts);
+lform({function,Line,Name,Arity,Clauses}, Opts) ->
+ lfunction({function,Line,Name,Arity,Clauses}, Opts);
+lform({rule,Line,Name,Arity,Clauses}, Opts) ->
+ lrule({rule,Line,Name,Arity,Clauses}, Opts);
%% These are specials to make it easier for the compiler.
-lform({error,E}, _Hook) ->
+lform({error,E}, _Opts) ->
leaf(format("~p\n", [{error,E}]));
-lform({warning,W}, _Hook) ->
+lform({warning,W}, _Opts) ->
leaf(format("~p\n", [{warning,W}]));
-lform({eof,_Line}, _Hook) ->
+lform({eof,_Line}, _Opts) ->
$\n.
-lattribute({attribute,_Line,type,Type}, Hook) ->
- [typeattr(type, Type, Hook),leaf(".\n")];
-lattribute({attribute,_Line,opaque,Type}, Hook) ->
- [typeattr(opaque, Type, Hook),leaf(".\n")];
-lattribute({attribute,_Line,spec,Arg}, _Hook) ->
+lattribute({attribute,_Line,type,Type}, Opts) ->
+ [typeattr(type, Type, Opts),leaf(".\n")];
+lattribute({attribute,_Line,opaque,Type}, Opts) ->
+ [typeattr(opaque, Type, Opts),leaf(".\n")];
+lattribute({attribute,_Line,spec,Arg}, _Opts) ->
[specattr(Arg),leaf(".\n")];
-lattribute({attribute,_Line,Name,Arg}, Hook) ->
- [lattribute(Name, Arg, Hook),leaf(".\n")].
+lattribute({attribute,_Line,Name,Arg}, Opts) ->
+ [lattribute(Name, Arg, Opts),leaf(".\n")].
-lattribute(module, {M,Vs}, _Hook) ->
+lattribute(module, {M,Vs}, _Opts) ->
attr("module",[{var,0,pname(M)},
foldr(fun(V, C) -> {cons,0,{var,0,V},C}
end, {nil,0}, Vs)]);
-lattribute(module, M, _Hook) ->
+lattribute(module, M, _Opts) ->
attr("module", [{var,0,pname(M)}]);
-lattribute(export, Falist, _Hook) ->
+lattribute(export, Falist, _Opts) ->
call({var,0,"-export"}, [falist(Falist)], 0, none);
-lattribute(import, Name, _Hook) when is_list(Name) ->
+lattribute(import, Name, _Opts) when is_list(Name) ->
attr("import", [{var,0,pname(Name)}]);
-lattribute(import, {From,Falist}, _Hook) ->
+lattribute(import, {From,Falist}, _Opts) ->
attr("import",[{var,0,pname(From)},falist(Falist)]);
-lattribute(file, {Name,Line}, _Hook) ->
+lattribute(file, {Name,Line}, _Opts) ->
attr("file", [{var,0,format("~p", [Name])},{integer,0,Line}]);
-lattribute(record, {Name,Is}, Hook) ->
+lattribute(record, {Name,Is}, Opts) ->
Nl = leaf(format("-record(~w,", [Name])),
- [{first,Nl,record_fields(Is, Hook)},$)];
-lattribute(Name, Arg, _Hook) ->
- attr(write(Name), [erl_parse:abstract(Arg)]).
+ [{first,Nl,record_fields(Is, Opts)},$)];
+lattribute(Name, Arg, #options{encoding = Encoding}) ->
+ attr(write(Name), [erl_parse:abstract(Arg, [{encoding,Encoding}])]).
-typeattr(Tag, {TypeName,Type,Args}, _Hook) ->
+typeattr(Tag, {TypeName,Type,Args}, _Opts) ->
{first,leaf("-"++atom_to_list(Tag)++" "),
typed(call({atom,0,TypeName}, Args, 0, none), Type)}.
@@ -293,7 +331,7 @@ guard_type(Before, Gs) ->
Gl = {list,[{step,'when',expr_list(Gs, [$,], fun constraint/2, none)}]},
{list,[{step,Before,Gl}]}.
-constraint({type,_Line,constraint,[Tag,As]}, _Hook) ->
+constraint({type,_Line,constraint,[Tag,As]}, _Opts) ->
simple_type(Tag, As).
fun_type(Before, {type,_,'fun',[FType,Ret]}) ->
@@ -333,231 +371,230 @@ falist([]) ->
falist([{Name,Arity}|Falist]) ->
{cons,0,{var,0,format("~w/~w", [Name,Arity])},falist(Falist)}.
-lfunction({function,_Line,Name,_Arity,Cs}, Hook) ->
- Cll = nl_clauses(fun (C, H) -> func_clause(Name, C, H) end, $;, Hook, Cs),
+lfunction({function,_Line,Name,_Arity,Cs}, Opts) ->
+ Cll = nl_clauses(fun (C, H) -> func_clause(Name, C, H) end, $;, Opts, Cs),
[Cll,leaf(".\n")].
-func_clause(Name, {clause,Line,Head,Guard,Body}, Hook) ->
- Hl = call({atom,Line,Name}, Head, 0, Hook),
- Gl = guard_when(Hl, Guard, Hook),
- Bl = body(Body, Hook),
+func_clause(Name, {clause,Line,Head,Guard,Body}, Opts) ->
+ Hl = call({atom,Line,Name}, Head, 0, Opts),
+ Gl = guard_when(Hl, Guard, Opts),
+ Bl = body(Body, Opts),
{step,Gl,Bl}.
-lrule({rule,_Line,Name,_Arity,Cs}, Hook) ->
- Cll = nl_clauses(fun (C, H) -> rule_clause(Name, C, H) end, $;, Hook, Cs),
+lrule({rule,_Line,Name,_Arity,Cs}, Opts) ->
+ Cll = nl_clauses(fun (C, H) -> rule_clause(Name, C, H) end, $;, Opts, Cs),
[Cll,leaf(".\n")].
-rule_clause(Name, {clause,Line,Head,Guard,Body}, Hook) ->
- Hl = call({atom,Line,Name}, Head, 0, Hook),
- Gl = guard_when(Hl, Guard, Hook, leaf(" :-")),
- Bl = rule_body(Body, Hook),
+rule_clause(Name, {clause,Line,Head,Guard,Body}, Opts) ->
+ Hl = call({atom,Line,Name}, Head, 0, Opts),
+ Gl = guard_when(Hl, Guard, Opts, leaf(" :-")),
+ Bl = rule_body(Body, Opts),
{step,Gl,Bl}.
-rule_body(Es, Hook) ->
- lc_quals(Es, Hook).
+rule_body(Es, Opts) ->
+ lc_quals(Es, Opts).
-guard_when(Before, Guard, Hook) ->
- guard_when(Before, Guard, Hook, ' ->').
+guard_when(Before, Guard, Opts) ->
+ guard_when(Before, Guard, Opts, ' ->').
-guard_when(Before, Guard, Hook, After) ->
- Gl = lguard(Guard, Hook),
+guard_when(Before, Guard, Opts, After) ->
+ Gl = lguard(Guard, Opts),
[{list,[{step,Before,Gl}]},After].
-lguard([E|Es], Hook) when is_list(E) ->
- {list,[{step,'when',expr_list([E|Es], [$;], fun guard0/2, Hook)}]};
-lguard([E|Es], Hook) -> % before R6
- lguard([[E|Es]], Hook);
+lguard([E|Es], Opts) when is_list(E) ->
+ {list,[{step,'when',expr_list([E|Es], [$;], fun guard0/2, Opts)}]};
+lguard([E|Es], Opts) -> % before R6
+ lguard([[E|Es]], Opts);
lguard([], _) ->
[].
-guard0(Es, Hook) ->
- expr_list(Es, [$,], fun lexpr/2, Hook).
+guard0(Es, Opts) ->
+ expr_list(Es, [$,], fun lexpr/2, Opts).
-%% body(Before, Es, Hook) -> [Char].
+%% body(Before, Es, Opts) -> [Char].
-body([E], Hook) ->
- lexpr(E, Hook);
-body(Es, Hook) ->
- {prefer_nl,[$,],lexprs(Es, Hook)}.
+body([E], Opts) ->
+ lexpr(E, Opts);
+body(Es, Opts) ->
+ {prefer_nl,[$,],lexprs(Es, Opts)}.
-lexpr(E, Hook) ->
- lexpr(E, 0, Hook).
+lexpr(E, Opts) ->
+ lexpr(E, 0, Opts).
lexpr({var,_,V}, _, _) when is_integer(V) -> %Special hack for Robert
leaf(format("_~w", [V]));
lexpr({var,_,V}, _, _) -> leaf(format("~s", [V]));
-lexpr({char,_,C}, _, _) -> leaf(write_char(C));
+lexpr({char,_,C}, _, _) -> {char,C};
lexpr({integer,_,N}, _, _) -> leaf(write(N));
lexpr({float,_,F}, _, _) -> leaf(write(F));
lexpr({atom,_,A}, _, _) -> leaf(write(A));
lexpr({string,_,S}, _, _) -> {string,S};
lexpr({nil,_}, _, _) -> '[]';
-lexpr({cons,_,H,T}, _, Hook) ->
- list(T, [H], Hook);
-lexpr({lc,_,E,Qs}, _Prec, Hook) ->
- Lcl = {list,[{step,[lexpr(E, Hook),leaf(" ||")],lc_quals(Qs, Hook)}]},
+lexpr({cons,_,H,T}, _, Opts) ->
+ list(T, [H], Opts);
+lexpr({lc,_,E,Qs}, _Prec, Opts) ->
+ Lcl = {list,[{step,[lexpr(E, Opts),leaf(" ||")],lc_quals(Qs, Opts)}]},
{list,[{seq,$[,[],[[]],[{force_nl,leaf(" "),[Lcl]}]},$]]};
%% {list,[{step,$[,Lcl},$]]};
-lexpr({bc,_,E,Qs}, _Prec, Hook) ->
- Lcl = {list,[{step,[lexpr(E, Hook),leaf(" ||")],lc_quals(Qs, Hook)}]},
+lexpr({bc,_,E,Qs}, _Prec, Opts) ->
+ Lcl = {list,[{step,[lexpr(E, Opts),leaf(" ||")],lc_quals(Qs, Opts)}]},
{list,[{seq,'<<',[],[[]],[{force_nl,leaf(" "),[Lcl]}]},'>>']};
%% {list,[{step,'<<',Lcl},'>>']};
-lexpr({tuple,_,Elts}, _, Hook) ->
- tuple(Elts, Hook);
-%%lexpr({struct,_,Tag,Elts}, _, Hook) ->
-%% {first,format("~w", [Tag]),tuple(Elts, Hook)};
-lexpr({record_index, _, Name, F}, Prec, Hook) ->
+lexpr({tuple,_,Elts}, _, Opts) ->
+ tuple(Elts, Opts);
+%%lexpr({struct,_,Tag,Elts}, _, Opts) ->
+%% {first,format("~w", [Tag]),tuple(Elts, Opts)};
+lexpr({record_index, _, Name, F}, Prec, Opts) ->
{P,R} = preop_prec('#'),
Nl = record_name(Name),
- El = [Nl,$.,lexpr(F, R, Hook)],
+ El = [Nl,$.,lexpr(F, R, Opts)],
maybe_paren(P, Prec, El);
-lexpr({record, _, Name, Fs}, Prec, Hook) ->
+lexpr({record, _, Name, Fs}, Prec, Opts) ->
{P,_R} = preop_prec('#'),
Nl = record_name(Name),
- El = {first,Nl,record_fields(Fs, Hook)},
+ El = {first,Nl,record_fields(Fs, Opts)},
maybe_paren(P, Prec, El);
-lexpr({record_field, _, Rec, Name, F}, Prec, Hook) ->
+lexpr({record_field, _, Rec, Name, F}, Prec, Opts) ->
{L,P,R} = inop_prec('#'),
- Rl = lexpr(Rec, L, Hook),
+ Rl = lexpr(Rec, L, Opts),
Nl = leaf(format("#~w.", [Name])),
- El = [Rl,Nl,lexpr(F, R, Hook)],
+ El = [Rl,Nl,lexpr(F, R, Opts)],
maybe_paren(P, Prec, El);
-lexpr({record, _, Rec, Name, Fs}, Prec, Hook) ->
+lexpr({record, _, Rec, Name, Fs}, Prec, Opts) ->
{L,P,_R} = inop_prec('#'),
- Rl = lexpr(Rec, L, Hook),
+ Rl = lexpr(Rec, L, Opts),
Nl = record_name(Name),
- El = {first,[Rl,Nl],record_fields(Fs, Hook)},
+ El = {first,[Rl,Nl],record_fields(Fs, Opts)},
maybe_paren(P, Prec, El);
-lexpr({record_field, _, {atom,_,''}, F}, Prec, Hook) ->
+lexpr({record_field, _, {atom,_,''}, F}, Prec, Opts) ->
{_L,P,R} = inop_prec('.'),
- El = [$.,lexpr(F, R, Hook)],
+ El = [$.,lexpr(F, R, Opts)],
maybe_paren(P, Prec, El);
-lexpr({record_field, _, Rec, F}, Prec, Hook) ->
+lexpr({record_field, _, Rec, F}, Prec, Opts) ->
{L,P,R} = inop_prec('.'),
- El = [lexpr(Rec, L, Hook),$.,lexpr(F, R, Hook)],
+ El = [lexpr(Rec, L, Opts),$.,lexpr(F, R, Opts)],
maybe_paren(P, Prec, El);
-lexpr({block,_,Es}, _, Hook) ->
- {list,[{step,'begin',body(Es, Hook)},'end']};
-lexpr({'if',_,Cs}, _, Hook) ->
- {list,[{step,'if',if_clauses(Cs, Hook)},'end']};
-lexpr({'case',_,Expr,Cs}, _, Hook) ->
- {list,[{step,{list,[{step,'case',lexpr(Expr, Hook)},'of']},
- cr_clauses(Cs, Hook)},
+lexpr({block,_,Es}, _, Opts) ->
+ {list,[{step,'begin',body(Es, Opts)},'end']};
+lexpr({'if',_,Cs}, _, Opts) ->
+ {list,[{step,'if',if_clauses(Cs, Opts)},'end']};
+lexpr({'case',_,Expr,Cs}, _, Opts) ->
+ {list,[{step,{list,[{step,'case',lexpr(Expr, Opts)},'of']},
+ cr_clauses(Cs, Opts)},
'end']};
-lexpr({'cond',_,Cs}, _, Hook) ->
- {list,[{step,leaf("cond"),cond_clauses(Cs, Hook)},'end']};
-lexpr({'receive',_,Cs}, _, Hook) ->
- {list,[{step,'receive',cr_clauses(Cs, Hook)},'end']};
-lexpr({'receive',_,Cs,To,ToOpt}, _, Hook) ->
- Al = {list,[{step,[lexpr(To, Hook),' ->'],body(ToOpt, Hook)}]},
- {list,[{step,'receive',cr_clauses(Cs, Hook)},
+lexpr({'cond',_,Cs}, _, Opts) ->
+ {list,[{step,leaf("cond"),cond_clauses(Cs, Opts)},'end']};
+lexpr({'receive',_,Cs}, _, Opts) ->
+ {list,[{step,'receive',cr_clauses(Cs, Opts)},'end']};
+lexpr({'receive',_,Cs,To,ToOpt}, _, Opts) ->
+ Al = {list,[{step,[lexpr(To, Opts),' ->'],body(ToOpt, Opts)}]},
+ {list,[{step,'receive',cr_clauses(Cs, Opts)},
{step,'after',Al},
'end']};
-lexpr({'fun',_,{function,F,A}}, _Prec, _Hook) ->
+lexpr({'fun',_,{function,F,A}}, _Prec, _Opts) ->
leaf(format("fun ~w/~w", [F,A]));
-lexpr({'fun',_,{function,F,A},Extra}, _Prec, _Hook) ->
+lexpr({'fun',_,{function,F,A},Extra}, _Prec, _Opts) ->
{force_nl,fun_info(Extra),leaf(format("fun ~w/~w", [F,A]))};
-lexpr({'fun',_,{function,M,F,A}}, _Prec, _Hook)
+lexpr({'fun',_,{function,M,F,A}}, _Prec, _Opts)
when is_atom(M), is_atom(F), is_integer(A) ->
%% For backward compatibility with pre-R15 abstract format.
leaf(format("fun ~w:~w/~w", [M,F,A]));
-lexpr({'fun',_,{function,M,F,A}}, _Prec, Hook) ->
+lexpr({'fun',_,{function,M,F,A}}, _Prec, Opts) ->
%% New format in R15.
- NameItem = lexpr(M, Hook),
- CallItem = lexpr(F, Hook),
- ArityItem = lexpr(A, Hook),
+ NameItem = lexpr(M, Opts),
+ CallItem = lexpr(F, Opts),
+ ArityItem = lexpr(A, Opts),
["fun ",NameItem,$:,CallItem,$/,ArityItem];
-lexpr({'fun',_,{clauses,Cs}}, _Prec, Hook) ->
- {list,[{first,'fun',fun_clauses(Cs, Hook)},'end']};
-lexpr({'fun',_,{clauses,Cs},Extra}, _Prec, Hook) ->
+lexpr({'fun',_,{clauses,Cs}}, _Prec, Opts) ->
+ {list,[{first,'fun',fun_clauses(Cs, Opts)},'end']};
+lexpr({'fun',_,{clauses,Cs},Extra}, _Prec, Opts) ->
{force_nl,fun_info(Extra),
- {list,[{first,'fun',fun_clauses(Cs, Hook)},'end']}};
-lexpr({'query',_,Lc}, _Prec, Hook) ->
- {list,[{step,leaf("query"),lexpr(Lc, 0, Hook)},'end']};
-lexpr({call,_,{remote,_,{atom,_,M},{atom,_,F}=N}=Name,Args}, Prec, Hook) ->
+ {list,[{first,'fun',fun_clauses(Cs, Opts)},'end']}};
+lexpr({call,_,{remote,_,{atom,_,M},{atom,_,F}=N}=Name,Args}, Prec, Opts) ->
case erl_internal:bif(M, F, length(Args)) of
true ->
- call(N, Args, Prec, Hook);
+ call(N, Args, Prec, Opts);
false ->
- call(Name, Args, Prec, Hook)
+ call(Name, Args, Prec, Opts)
end;
-lexpr({call,_,Name,Args}, Prec, Hook) ->
- call(Name, Args, Prec, Hook);
-lexpr({'try',_,Es,Scs,Ccs,As}, _, Hook) ->
+lexpr({call,_,Name,Args}, Prec, Opts) ->
+ call(Name, Args, Prec, Opts);
+lexpr({'try',_,Es,Scs,Ccs,As}, _, Opts) ->
{list,[if
Scs =:= [] ->
- {step,'try',body(Es, Hook)};
+ {step,'try',body(Es, Opts)};
true ->
- {step,{list,[{step,'try',body(Es, Hook)},'of']},
- cr_clauses(Scs, Hook)}
+ {step,{list,[{step,'try',body(Es, Opts)},'of']},
+ cr_clauses(Scs, Opts)}
end,
if
Ccs =:= [] ->
[];
true ->
- {step,'catch',try_clauses(Ccs, Hook)}
+ {step,'catch',try_clauses(Ccs, Opts)}
end,
if
As =:= [] ->
[];
true ->
- {step,'after',body(As, Hook)}
+ {step,'after',body(As, Opts)}
end,
'end']};
-lexpr({'catch',_,Expr}, Prec, Hook) ->
+lexpr({'catch',_,Expr}, Prec, Opts) ->
{P,R} = preop_prec('catch'),
- El = {list,[{step,'catch',lexpr(Expr, R, Hook)}]},
+ El = {list,[{step,'catch',lexpr(Expr, R, Opts)}]},
maybe_paren(P, Prec, El);
-lexpr({match,_,Lhs,Rhs}, Prec, Hook) ->
+lexpr({match,_,Lhs,Rhs}, Prec, Opts) ->
{L,P,R} = inop_prec('='),
- Pl = lexpr(Lhs, L, Hook),
- Rl = lexpr(Rhs, R, Hook),
+ Pl = lexpr(Lhs, L, Opts),
+ Rl = lexpr(Rhs, R, Opts),
El = {list,[{cstep,[Pl,' ='],Rl}]},
maybe_paren(P, Prec, El);
-lexpr({op,_,Op,Arg}, Prec, Hook) ->
+lexpr({op,_,Op,Arg}, Prec, Opts) ->
{P,R} = preop_prec(Op),
Ol = leaf(format("~s ", [Op])),
- El = [Ol,lexpr(Arg, R, Hook)],
+ El = [Ol,lexpr(Arg, R, Opts)],
maybe_paren(P, Prec, El);
-lexpr({op,_,Op,Larg,Rarg}, Prec, Hook) when Op =:= 'orelse';
+lexpr({op,_,Op,Larg,Rarg}, Prec, Opts) when Op =:= 'orelse';
Op =:= 'andalso' ->
%% Breaks lines since R12B.
{L,P,R} = inop_prec(Op),
- Ll = lexpr(Larg, L, Hook),
+ Ll = lexpr(Larg, L, Opts),
Ol = leaf(format("~s", [Op])),
- Lr = lexpr(Rarg, R, Hook),
+ Lr = lexpr(Rarg, R, Opts),
El = {prefer_nl,[[]],[Ll,Ol,Lr]},
maybe_paren(P, Prec, El);
-lexpr({op,_,Op,Larg,Rarg}, Prec, Hook) ->
+lexpr({op,_,Op,Larg,Rarg}, Prec, Opts) ->
{L,P,R} = inop_prec(Op),
- Ll = lexpr(Larg, L, Hook),
+ Ll = lexpr(Larg, L, Opts),
Ol = leaf(format("~s", [Op])),
- Lr = lexpr(Rarg, R, Hook),
+ Lr = lexpr(Rarg, R, Opts),
El = {list,[Ll,Ol,Lr]},
maybe_paren(P, Prec, El);
%% Special expressions which are not really legal everywhere.
-lexpr({remote,_,M,F}, Prec, Hook) ->
+lexpr({remote,_,M,F}, Prec, Opts) ->
{L,P,R} = inop_prec(':'),
- NameItem = lexpr(M, L, Hook),
- CallItem = lexpr(F, R, Hook),
+ NameItem = lexpr(M, L, Opts),
+ CallItem = lexpr(F, R, Opts),
maybe_paren(P, Prec, [NameItem,$:,CallItem]);
%% BIT SYNTAX:
-lexpr({bin,_,Fs}, _, Hook) ->
- bit_grp(Fs, Hook);
+lexpr({bin,_,Fs}, _, Opts) ->
+ bit_grp(Fs, Opts);
%% Special case for straight values.
lexpr({value,_,Val}, _,_) ->
leaf(write(Val));
%% Now do the hook.
-lexpr(Other, _Precedence, none) ->
+lexpr(Other, _Precedence, #options{hook = none}) ->
leaf(format("INVALID-FORM:~w:",[Other]));
-lexpr(HookExpr, Precedence, {Mod,Func,Eas}) when Mod =/= 'fun' ->
+lexpr(HookExpr, Precedence, #options{hook = {Mod,Func,Eas}})
+ when Mod =/= 'fun' ->
{ehook,HookExpr,Precedence,{Mod,Func,Eas}};
-lexpr(HookExpr, Precedence, Func) ->
- {hook,HookExpr,Precedence,Func}.
+lexpr(HookExpr, Precedence, #options{hook = Func, opts = Options}) ->
+ {hook,HookExpr,Precedence,Func,Options}.
-call(Name, Args, Prec, Hook) ->
+call(Name, Args, Prec, Opts) ->
{F,P} = func_prec(),
- Item = {first,lexpr(Name, F, Hook),args(Args, Hook)},
+ Item = {first,lexpr(Name, F, Opts),args(Args, Opts)},
maybe_paren(P, Prec, Item).
fun_info(Extra) ->
@@ -565,32 +602,18 @@ fun_info(Extra) ->
%% BITS:
-bit_grp(Fs, Hook) ->
- append([['<<'],
- [try
- true = Fs =/= [],
- S = bin_string(Fs),
- true = io_lib:printable_list(S),
- {string,S}
- catch _:_ ->
- bit_elems(Fs, Hook)
- end],
- ['>>']]).
-
-bin_string([]) ->
- [];
-bin_string([{bin_element,_,{char,_,C},_,_}|Bin]) ->
- [C | bin_string(Bin)].
+bit_grp(Fs, Opts) ->
+ append([['<<'], [bit_elems(Fs, Opts)], ['>>']]).
-bit_elems(Es, Hook) ->
- expr_list(Es, $,, fun bit_elem/2, Hook).
+bit_elems(Es, Opts) ->
+ expr_list(Es, $,, fun bit_elem/2, Opts).
-bit_elem({bin_element,_,Expr,Sz,Types}, Hook) ->
+bit_elem({bin_element,_,Expr,Sz,Types}, Opts) ->
P = max_prec(),
- VChars = lexpr(Expr, P, Hook),
+ VChars = lexpr(Expr, P, Opts),
SChars = if
Sz =/= default ->
- [VChars,$:,lexpr(Sz, P, Hook)];
+ [VChars,$:,lexpr(Sz, P, Opts)];
true ->
VChars
end,
@@ -618,157 +641,157 @@ bit_elem_type(T) ->
record_name(Name) ->
leaf(format("#~w", [Name])).
-record_fields(Fs, Hook) ->
- tuple(Fs, fun record_field/2, Hook).
+record_fields(Fs, Opts) ->
+ tuple(Fs, fun record_field/2, Opts).
-record_field({record_field,_,F,Val}, Hook) ->
+record_field({record_field,_,F,Val}, Opts) ->
{L,_P,R} = inop_prec('='),
- Fl = lexpr(F, L, Hook),
- Vl = lexpr(Val, R, Hook),
+ Fl = lexpr(F, L, Opts),
+ Vl = lexpr(Val, R, Opts),
{list,[{cstep,[Fl,' ='],Vl}]};
-record_field({typed_record_field,{record_field,_,F,Val},Type}, Hook) ->
+record_field({typed_record_field,{record_field,_,F,Val},Type}, Opts) ->
{L,_P,R} = inop_prec('='),
- Fl = lexpr(F, L, Hook),
- Vl = typed(lexpr(Val, R, Hook), Type),
+ Fl = lexpr(F, L, Opts),
+ Vl = typed(lexpr(Val, R, Opts), Type),
{list,[{cstep,[Fl,' ='],Vl}]};
-record_field({typed_record_field,Field,Type}, Hook) ->
- typed(record_field(Field, Hook), Type);
-record_field({record_field,_,F}, Hook) ->
- lexpr(F, 0, Hook).
-
-list({cons,_,H,T}, Es, Hook) ->
- list(T, [H|Es], Hook);
-list({nil,_}, Es, Hook) ->
- proper_list(reverse(Es), Hook);
-list(Other, Es, Hook) ->
- improper_list(reverse(Es, [Other]), Hook).
-
-%% if_clauses(Clauses, Hook) -> [Char].
+record_field({typed_record_field,Field,Type}, Opts) ->
+ typed(record_field(Field, Opts), Type);
+record_field({record_field,_,F}, Opts) ->
+ lexpr(F, 0, Opts).
+
+list({cons,_,H,T}, Es, Opts) ->
+ list(T, [H|Es], Opts);
+list({nil,_}, Es, Opts) ->
+ proper_list(reverse(Es), Opts);
+list(Other, Es, Opts) ->
+ improper_list(reverse(Es, [Other]), Opts).
+
+%% if_clauses(Clauses, Opts) -> [Char].
%% Print 'if' clauses.
-if_clauses(Cs, Hook) ->
- clauses(fun if_clause/2, Hook, Cs).
+if_clauses(Cs, Opts) ->
+ clauses(fun if_clause/2, Opts, Cs).
-if_clause({clause,_,[],G,B}, Hook) ->
- Gl = [guard_no_when(G, Hook),' ->'],
- {step,Gl,body(B, Hook)}.
+if_clause({clause,_,[],G,B}, Opts) ->
+ Gl = [guard_no_when(G, Opts),' ->'],
+ {step,Gl,body(B, Opts)}.
-guard_no_when([E|Es], Hook) when is_list(E) ->
- expr_list([E|Es], $;, fun guard0/2, Hook);
-guard_no_when([E|Es], Hook) -> % before R6
- guard_no_when([[E|Es]], Hook);
+guard_no_when([E|Es], Opts) when is_list(E) ->
+ expr_list([E|Es], $;, fun guard0/2, Opts);
+guard_no_when([E|Es], Opts) -> % before R6
+ guard_no_when([[E|Es]], Opts);
guard_no_when([], _) -> % cannot happen
leaf("true").
-%% cr_clauses(Clauses, Hook) -> [Char].
+%% cr_clauses(Clauses, Opts) -> [Char].
%% Print 'case'/'receive' clauses.
-cr_clauses(Cs, Hook) ->
- clauses(fun cr_clause/2, Hook, Cs).
+cr_clauses(Cs, Opts) ->
+ clauses(fun cr_clause/2, Opts, Cs).
-cr_clause({clause,_,[T],G,B}, Hook) ->
- El = lexpr(T, 0, Hook),
- Gl = guard_when(El, G, Hook),
- Bl = body(B, Hook),
+cr_clause({clause,_,[T],G,B}, Opts) ->
+ El = lexpr(T, 0, Opts),
+ Gl = guard_when(El, G, Opts),
+ Bl = body(B, Opts),
{step,Gl,Bl}.
-%% try_clauses(Clauses, Hook) -> [Char].
+%% try_clauses(Clauses, Opts) -> [Char].
%% Print 'try' clauses.
-try_clauses(Cs, Hook) ->
- clauses(fun try_clause/2, Hook, Cs).
+try_clauses(Cs, Opts) ->
+ clauses(fun try_clause/2, Opts, Cs).
-try_clause({clause,_,[{tuple,_,[{atom,_,throw},V,S]}],G,B}, Hook) ->
- El = lexpr(V, 0, Hook),
- Sl = stack_backtrace(S, [El], Hook),
- Gl = guard_when(Sl, G, Hook),
- Bl = body(B, Hook),
+try_clause({clause,_,[{tuple,_,[{atom,_,throw},V,S]}],G,B}, Opts) ->
+ El = lexpr(V, 0, Opts),
+ Sl = stack_backtrace(S, [El], Opts),
+ Gl = guard_when(Sl, G, Opts),
+ Bl = body(B, Opts),
{step,Gl,Bl};
-try_clause({clause,_,[{tuple,_,[C,V,S]}],G,B}, Hook) ->
- Cs = lexpr(C, 0, Hook),
- El = lexpr(V, 0, Hook),
+try_clause({clause,_,[{tuple,_,[C,V,S]}],G,B}, Opts) ->
+ Cs = lexpr(C, 0, Opts),
+ El = lexpr(V, 0, Opts),
CsEl = [Cs,$:,El],
- Sl = stack_backtrace(S, CsEl, Hook),
- Gl = guard_when(Sl, G, Hook),
- Bl = body(B, Hook),
+ Sl = stack_backtrace(S, CsEl, Opts),
+ Gl = guard_when(Sl, G, Opts),
+ Bl = body(B, Opts),
{step,Gl,Bl}.
-stack_backtrace({var,_,'_'}, El, _Hook) ->
+stack_backtrace({var,_,'_'}, El, _Opts) ->
El;
-stack_backtrace(S, El, Hook) ->
- El++[$:,lexpr(S, 0, Hook)].
+stack_backtrace(S, El, Opts) ->
+ El++[$:,lexpr(S, 0, Opts)].
-%% fun_clauses(Clauses, Hook) -> [Char].
+%% fun_clauses(Clauses, Opts) -> [Char].
%% Print 'fun' clauses.
-fun_clauses(Cs, Hook) ->
- nl_clauses(fun fun_clause/2, [$;], Hook, Cs).
+fun_clauses(Cs, Opts) ->
+ nl_clauses(fun fun_clause/2, [$;], Opts, Cs).
-fun_clause({clause,_,A,G,B}, Hook) ->
- El = args(A, Hook),
- Gl = guard_when(El, G, Hook),
- Bl = body(B, Hook),
+fun_clause({clause,_,A,G,B}, Opts) ->
+ El = args(A, Opts),
+ Gl = guard_when(El, G, Opts),
+ Bl = body(B, Opts),
{step,Gl,Bl}.
-%% cond_clauses(Clauses, Hook) -> [Char].
+%% cond_clauses(Clauses, Opts) -> [Char].
%% Print 'cond' clauses.
-cond_clauses(Cs, Hook) ->
- clauses(fun cond_clause/2, Hook, Cs).
+cond_clauses(Cs, Opts) ->
+ clauses(fun cond_clause/2, Opts, Cs).
-cond_clause({clause,_,[],[[E]],B}, Hook) ->
- {step,[lexpr(E, Hook),' ->'],body(B, Hook)}.
+cond_clause({clause,_,[],[[E]],B}, Opts) ->
+ {step,[lexpr(E, Opts),' ->'],body(B, Opts)}.
-%% nl_clauses(Type, Hook, Clauses) -> [Char].
+%% nl_clauses(Type, Opts, Clauses) -> [Char].
%% Generic clause printing function (always breaks lines).
-nl_clauses(Type, Sep, Hook, Cs) ->
- {prefer_nl,Sep,lexprs(Cs, Type, Hook)}.
+nl_clauses(Type, Sep, Opts, Cs) ->
+ {prefer_nl,Sep,lexprs(Cs, Type, Opts)}.
-%% clauses(Type, Hook, Clauses) -> [Char].
+%% clauses(Type, Opts, Clauses) -> [Char].
%% Generic clause printing function (breaks lines since R12B).
-clauses(Type, Hook, Cs) ->
- {prefer_nl,[$;],lexprs(Cs, Type, Hook)}.
+clauses(Type, Opts, Cs) ->
+ {prefer_nl,[$;],lexprs(Cs, Type, Opts)}.
-%% lc_quals(Qualifiers, After, Hook)
+%% lc_quals(Qualifiers, After, Opts)
%% List comprehension qualifiers (breaks lines since R12B).
-lc_quals(Qs, Hook) ->
- {prefer_nl,[$,],lexprs(Qs, fun lc_qual/2, Hook)}.
+lc_quals(Qs, Opts) ->
+ {prefer_nl,[$,],lexprs(Qs, fun lc_qual/2, Opts)}.
-lc_qual({b_generate,_,Pat,E}, Hook) ->
- Pl = lexpr(Pat, 0, Hook),
- {list,[{step,[Pl,leaf(" <=")],lexpr(E, 0, Hook)}]};
-lc_qual({generate,_,Pat,E}, Hook) ->
- Pl = lexpr(Pat, 0, Hook),
- {list,[{step,[Pl,leaf(" <-")],lexpr(E, 0, Hook)}]};
-lc_qual(Q, Hook) ->
- lexpr(Q, 0, Hook).
+lc_qual({b_generate,_,Pat,E}, Opts) ->
+ Pl = lexpr(Pat, 0, Opts),
+ {list,[{step,[Pl,leaf(" <=")],lexpr(E, 0, Opts)}]};
+lc_qual({generate,_,Pat,E}, Opts) ->
+ Pl = lexpr(Pat, 0, Opts),
+ {list,[{step,[Pl,leaf(" <-")],lexpr(E, 0, Opts)}]};
+lc_qual(Q, Opts) ->
+ lexpr(Q, 0, Opts).
-proper_list(Es, Hook) ->
- {seq,$[,$],$,,lexprs(Es, Hook)}.
+proper_list(Es, Opts) ->
+ {seq,$[,$],$,,lexprs(Es, Opts)}.
-improper_list(Es, Hook) ->
- {seq,$[,$],{$,,$|},lexprs(Es, Hook)}.
+improper_list(Es, Opts) ->
+ {seq,$[,$],{$,,$|},lexprs(Es, Opts)}.
-tuple(L, Hook) ->
- tuple(L, fun lexpr/2, Hook).
+tuple(L, Opts) ->
+ tuple(L, fun lexpr/2, Opts).
-tuple(Es, F, Hook) ->
- {seq,${,$},$,,lexprs(Es, F, Hook)}.
+tuple(Es, F, Opts) ->
+ {seq,${,$},$,,lexprs(Es, F, Opts)}.
-args(As, Hook) ->
- {seq,$(,$),[$,],lexprs(As, Hook)}.
+args(As, Opts) ->
+ {seq,$(,$),[$,],lexprs(As, Opts)}.
-expr_list(Es, Sep, F, Hook) ->
- {seq,[],[],Sep,lexprs(Es, F, Hook)}.
+expr_list(Es, Sep, F, Opts) ->
+ {seq,[],[],Sep,lexprs(Es, F, Opts)}.
-lexprs(Es, Hook) ->
- lexprs(Es, fun lexpr/2, Hook).
+lexprs(Es, Opts) ->
+ lexprs(Es, fun lexpr/2, Opts).
-lexprs(Es, F, Hook) ->
- [F(E, Hook) || E <- Es].
+lexprs(Es, F, Opts) ->
+ [F(E, Opts) || E <- Es].
maybe_paren(P, Prec, Expr) when P < Prec ->
[$(,Expr,$)];
@@ -781,13 +804,13 @@ leaf(S) ->
%%% Do the formatting. Currently nothing fancy. Could probably have
%%% done it in one single pass.
-frmt(Item) ->
- frmt(Item, 0).
+frmt(Item, PP) ->
+ frmt(Item, 0, PP).
-frmt(Item, I) ->
+frmt(Item, I, PP) ->
ST = spacetab(),
WT = wordtable(),
- {Chars,_Length} = f(Item, I, ST, WT),
+ {Chars,_Length} = f(Item, I, ST, WT, PP),
[Chars].
%%% What the tags mean:
@@ -803,6 +826,7 @@ frmt(Item, I) ->
%%% - {force_nl,ExtraInfo,I}: fun-info (a comment) forces linebreak before I.
%%% - {prefer_nl,Sep,IPs}: forces linebreak between Is unlesss negative
%%% indentation.
+%%% - {char,C}: a character
%%% - {string,S}: a string.
%%% - {hook,...}, {ehook,...}: hook expressions.
%%%
@@ -812,22 +836,22 @@ frmt(Item, I) ->
%%% cstep works similarly, but no linebreak if the width of I1 is less
%%% than the indentation (this is for "A = <expression over several lines>).
-f([]=Nil, _I0, _ST, _WT) ->
+f([]=Nil, _I0, _ST, _WT, _PP) ->
{Nil,0};
-f(C, _I0, _ST, _WT) when is_integer(C) ->
+f(C, _I0, _ST, _WT, _PP) when is_integer(C) ->
{C,1};
-f({leaf,Length,Chars}, _I0, _ST, _WT) ->
+f({leaf,Length,Chars}, _I0, _ST, _WT, _PP) ->
{Chars,Length};
-f([Item|Items], I0, ST, WT) ->
- consecutive(Items, f(Item, I0, ST, WT), I0, ST, WT);
-f({list,Items}, I0, ST, WT) ->
- f({seq,[],[],[[]],Items}, I0, ST, WT);
-f({first,E,Item}, I0, ST, WT) ->
- f({seq,E,[],[[]],[Item]}, I0, ST, WT);
-f({seq,Before,After,Sep,LItems}, I0, ST, WT) ->
- BCharsSize = f(Before, I0, ST, WT),
+f([Item|Items], I0, ST, WT, PP) ->
+ consecutive(Items, f(Item, I0, ST, WT, PP), I0, ST, WT, PP);
+f({list,Items}, I0, ST, WT, PP) ->
+ f({seq,[],[],[[]],Items}, I0, ST, WT, PP);
+f({first,E,Item}, I0, ST, WT, PP) ->
+ f({seq,E,[],[[]],[Item]}, I0, ST, WT, PP);
+f({seq,Before,After,Sep,LItems}, I0, ST, WT, PP) ->
+ BCharsSize = f(Before, I0, ST, WT, PP),
I = indent(BCharsSize, I0),
- CharsSizeL = fl(LItems, Sep, I, After, ST, WT),
+ CharsSizeL = fl(LItems, Sep, I, After, ST, WT, PP),
{CharsL,SizeL} = unz(CharsSizeL),
{BCharsL,BSizeL} = unz1([BCharsSize]),
Sizes = BSizeL ++ SizeL,
@@ -848,15 +872,15 @@ f({seq,Before,After,Sep,LItems}, I0, ST, WT) ->
{BCharsL++insert_newlines(CharsSizeL, I, ST),
nsz(lists:last(Sizes), I0)}
end;
-f({force_nl,_ExtraInfoItem,Item}, I, ST, WT) when I < 0 ->
+f({force_nl,_ExtraInfoItem,Item}, I, ST, WT, PP) when I < 0 ->
%% Extra info is a comment; cannot have that on the same line
- f(Item, I, ST, WT);
-f({force_nl,ExtraInfoItem,Item}, I, ST, WT) ->
- f({prefer_nl,[],[ExtraInfoItem,Item]}, I, ST, WT);
-f({prefer_nl,Sep,LItems}, I, ST, WT) when I < 0 ->
- f({seq,[],[],Sep,LItems}, I, ST, WT);
-f({prefer_nl,Sep,LItems}, I0, ST, WT) ->
- CharsSize2L = fl(LItems, Sep, I0, [], ST, WT),
+ f(Item, I, ST, WT, PP);
+f({force_nl,ExtraInfoItem,Item}, I, ST, WT, PP) ->
+ f({prefer_nl,[],[ExtraInfoItem,Item]}, I, ST, WT, PP);
+f({prefer_nl,Sep,LItems}, I, ST, WT, PP) when I < 0 ->
+ f({seq,[],[],Sep,LItems}, I, ST, WT, PP);
+f({prefer_nl,Sep,LItems}, I0, ST, WT, PP) ->
+ CharsSize2L = fl(LItems, Sep, I0, [], ST, WT, PP),
{_CharsL,Sizes} = unz(CharsSize2L),
if
Sizes =:= [] ->
@@ -864,37 +888,40 @@ f({prefer_nl,Sep,LItems}, I0, ST, WT) ->
true ->
{insert_newlines(CharsSize2L, I0, ST),nsz(lists:last(Sizes), I0)}
end;
-f({string,S}, I, ST, WT) ->
- f(write_a_string(S, I), I, ST, WT);
-f({hook,HookExpr,Precedence,Func}, I, _ST, _WT) ->
- Chars = Func(HookExpr, I, Precedence, Func),
+f({char,C}, I, ST, WT, PP) ->
+ f(write_a_char(C, PP), I, ST, WT, PP);
+f({string,S}, I, ST, WT, PP) ->
+ f(write_a_string(S, I, PP), I, ST, WT, PP);
+f({hook,HookExpr,Precedence,Func,Options}, I, _ST, _WT, _PP) ->
+ Chars = Func(HookExpr, I, Precedence, Options),
{Chars,indentation(Chars, I)};
-f({ehook,HookExpr,Precedence,{Mod,Func,Eas}=ModFuncEas}, I, _ST, _WT) ->
+f({ehook,HookExpr,Precedence,{Mod,Func,Eas}=ModFuncEas}, I, _ST, _WT, _PP) ->
Chars = apply(Mod, Func, [HookExpr,I,Precedence,ModFuncEas|Eas]),
{Chars,indentation(Chars, I)};
-f(WordName, _I, _ST, WT) -> % when is_atom(WordName)
+f(WordName, _I, _ST, WT, _PP) -> % when is_atom(WordName)
word(WordName, WT).
-define(IND, 4).
%% fl(ListItems, I0, ST, WT) -> [[CharsSize1,CharsSize2]]
%% ListItems = [{Item,Items}|Item]
-fl([], _Sep, I0, After, ST, WT) ->
- [[f(After, I0, ST, WT),{[],0}]];
-fl(CItems, Sep0, I0, After, ST, WT) ->
+fl([], _Sep, I0, After, ST, WT, PP) ->
+ [[f(After, I0, ST, WT, PP),{[],0}]];
+fl(CItems, Sep0, I0, After, ST, WT, PP) ->
F = fun({step,Item1,Item2}, S) ->
- [f(Item1, I0, ST, WT),f([Item2,S], incr(I0, ?IND), ST, WT)];
+ [f(Item1, I0, ST, WT, PP),
+ f([Item2,S], incr(I0, ?IND), ST, WT, PP)];
({cstep,Item1,Item2}, S) ->
- {_,Sz1} = CharSize1 = f(Item1, I0, ST, WT),
+ {_,Sz1} = CharSize1 = f(Item1, I0, ST, WT, PP),
if
is_integer(Sz1), Sz1 < ?IND ->
Item2p = [leaf("\s"),Item2,S],
- [consecutive(Item2p, CharSize1, I0, ST, WT),{[],0}];
+ [consecutive(Item2p, CharSize1, I0, ST, WT, PP),{[],0}];
true ->
- [CharSize1,f([Item2,S], incr(I0, ?IND), ST, WT)]
+ [CharSize1,f([Item2,S], incr(I0, ?IND), ST, WT, PP)]
end;
(Item, S) ->
- [f([Item,S], I0, ST, WT),{[],0}]
+ [f([Item,S], I0, ST, WT, PP),{[],0}]
end,
{Sep,LastSep} = case Sep0 of {_,_} -> Sep0; _ -> {Sep0,Sep0} end,
fl1(CItems, F, Sep, LastSep, After).
@@ -906,10 +933,10 @@ fl1([CItem1,CItem2], F, _Sep, LastSep, After) ->
fl1([CItem|CItems], F, Sep, LastSep, After) ->
[F(CItem, Sep)|fl1(CItems, F, Sep, LastSep, After)].
-consecutive(Items, CharSize1, I0, ST, WT) ->
+consecutive(Items, CharSize1, I0, ST, WT, PP) ->
{CharsSizes,_Length} =
mapfoldl(fun(Item, Len) ->
- CharsSize = f(Item, Len, ST, WT),
+ CharsSize = f(Item, Len, ST, WT, PP),
{CharsSize,indent(CharsSize, Len)}
end, indent(CharSize1, I0), Items),
{CharsL,SizeL} = unz1([CharSize1|CharsSizes]),
@@ -999,26 +1026,40 @@ has_nl([C|Cs]) ->
has_nl([]) ->
false.
+write_a_char(C, PP) ->
+ flat_leaf(write_char(C, PP)).
+
-define(MIN_SUBSTRING, 5).
-write_a_string(S, I) when I < 0; S =:= [] ->
- leaf(write_string(S));
-write_a_string(S, I) ->
+write_a_string(S, I, PP) when I < 0; S =:= [] ->
+ flat_leaf(write_string(S, PP));
+write_a_string(S, I, PP) ->
Len = erlang:max(?MAXLINE-I, ?MIN_SUBSTRING),
- {list,write_a_string(S, Len, Len)}.
+ {list,write_a_string(S, Len, Len, PP)}.
-write_a_string([], _N, _Len) ->
+write_a_string([], _N, _Len, _PP) ->
[];
-write_a_string(S, N, Len) ->
+write_a_string(S, N, Len, PP) ->
SS = string:sub_string(S, 1, N),
- Sl = write_string(SS),
- case (iolist_size(Sl) > Len) and (N > ?MIN_SUBSTRING) of
+ Sl = write_string(SS, PP),
+ case (length(Sl) > Len) and (N > ?MIN_SUBSTRING) of
true ->
- write_a_string(S, N-1, Len);
+ write_a_string(S, N-1, Len, PP);
false ->
- [leaf(Sl)|write_a_string(lists:nthtail(length(SS), S), Len, Len)]
+ [flat_leaf(Sl) |
+ write_a_string(lists:nthtail(length(SS), S), Len, Len, PP)]
end.
+flat_leaf(S) ->
+ L = lists:flatten(S),
+ {leaf,length(L),L}.
+
+write_string(S, PP) ->
+ lists:flatten((PP#pp.string_fun)(S)).
+
+write_char(C, PP) ->
+ lists:flatten((PP#pp.char_fun)(C)).
+
%%
%% Utilities
%%
diff --git a/lib/stdlib/src/erl_scan.erl b/lib/stdlib/src/erl_scan.erl
index 10b2ed2e49..bc0eaf015d 100644
--- a/lib/stdlib/src/erl_scan.erl
+++ b/lib/stdlib/src/erl_scan.erl
@@ -1,7 +1,8 @@
+%% -*- coding: utf-8 -*-
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -32,19 +33,19 @@
%% 173 - 176 { - ~ punctuation
%% 177 DEL control
%% 200 - 237 control
-%% 240 - 277 NBSP - � punctuation
-%% 300 - 326 � - � uppercase
-%% 327 � punctuation
-%% 330 - 336 � - � uppercase
-%% 337 - 366 � - � lowercase
-%% 367 � punctuation
-%% 370 - 377 � - � lowercase
+%% 240 - 277 NBSP - ¿ punctuation
+%% 300 - 326 À - Ö uppercase
+%% 327 × punctuation
+%% 330 - 336 Ø - Þ uppercase
+%% 337 - 366 ß - ö lowercase
+%% 367 ÷ punctuation
+%% 370 - 377 ø - ÿ lowercase
%%
%% Many punctuation characters have special meaning:
%% $\s, $_, $", $$, $%, $', $.
%% DEL is a punctuation.
%%
-%% Must watch using � \327, very close to x \170.
+%% Must watch using × \327, very close to x \170.
-module(erl_scan).
@@ -55,7 +56,15 @@
token_info/1,token_info/2,
attributes_info/1,attributes_info/2,set_attribute/3]).
--export_type([error_info/0, line/0, tokens_result/0]).
+%%% Private
+-export([continuation_location/1]).
+
+-export_type([error_info/0,
+ line/0,
+ location/0,
+ options/0,
+ return_cont/0,
+ tokens_result/0]).
%%%
%%% Defines and type definitions
@@ -74,7 +83,8 @@
-type location() :: line() | {line(),column()}.
-type resword_fun() :: fun((atom()) -> boolean()).
-type option() :: 'return' | 'return_white_spaces' | 'return_comments'
- | 'text' | {'reserved_word_fun', resword_fun()}.
+ | 'text' | {'reserved_word_fun', resword_fun()}
+ | 'unicode'.
-type options() :: option() | [option()].
-type symbol() :: atom() | float() | integer() | string().
-type info_line() :: integer() | term().
@@ -95,7 +105,8 @@
{resword_fun = fun reserved_word/1 :: resword_fun(),
ws = false :: boolean(),
comment = false :: boolean(),
- text = false :: boolean()}).
+ text = false :: boolean(),
+ unicode = false :: boolean()}).
%%----------------------------------------------------------------------------
@@ -183,6 +194,11 @@ tokens({erl_scan_continuation,Cs,Col,Toks,Line,St,Any,Fun},
CharSpec, _Loc, _Opts) ->
tokens1(Cs++CharSpec, St, Line, Col, Toks, Fun, Any).
+continuation_location({erl_scan_continuation,_,no_col,_,Line,_,_,_}) ->
+ Line;
+continuation_location({erl_scan_continuation,_,Col,_,Line,_,_,_}) ->
+ {Line,Col}.
+
-type attribute_item() :: 'column' | 'length' | 'line'
| 'location' | 'text'.
-type info_location() :: location() | term().
@@ -201,15 +217,14 @@ token_info(Token) ->
Items = [category,column,length,line,symbol,text], % undefined order
token_info(Token, Items).
--spec token_info(Token, TokenItem) -> TokenInfo | 'undefined' when
- Token :: token(),
- TokenItem :: token_item(),
- TokenInfo :: TokenInfoTuple :: token_info();
- (Token, TokenItems) -> [TokenInfo] when
- Token :: token(),
- TokenItems :: [TokenItem],
- TokenItem :: token_item(),
- TokenInfo :: [TokenInfoTuple :: token_info()].
+-spec token_info(Token, TokenItem) -> TokenInfoTuple | 'undefined' when
+ Token :: token(),
+ TokenItem :: token_item(),
+ TokenInfoTuple :: token_info();
+ (Token, TokenItems) -> TokenInfo when
+ Token :: token(),
+ TokenItems :: [TokenItem :: token_item()],
+ TokenInfo :: [TokenInfoTuple :: token_info()].
token_info(_Token, []) ->
[];
token_info(Token, [Item|Items]) when is_atom(Item) ->
@@ -239,16 +254,15 @@ attributes_info(Attributes) ->
Items = [column,length,line,text], % undefined order
attributes_info(Attributes, Items).
--spec attributes_info(Attributes, AttributeItem) ->
- AttributeInfo | 'undefined' when
- Attributes :: attributes(),
- AttributeItem :: attribute_item(),
- AttributeInfo :: AttributeInfoTuple :: attribute_info();
- (Attributes, AttributeItems) -> [AttributeInfo] when
- Attributes :: attributes(),
- AttributeItems :: [AttributeItem],
- AttributeItem :: attribute_item(),
- AttributeInfo :: [AttributeInfoTuple :: attribute_info()].
+-spec attributes_info
+ (Attributes, AttributeItem) -> AttributeInfoTuple | 'undefined' when
+ Attributes :: attributes(),
+ AttributeItem :: attribute_item(),
+ AttributeInfoTuple :: attribute_info();
+ (Attributes, AttributeItems) -> AttributeInfo when
+ Attributes :: attributes(),
+ AttributeItems :: [AttributeItem :: attribute_item()],
+ AttributeInfo :: [AttributeInfoTuple :: attribute_info()].
attributes_info(_Attrs, []) ->
[];
attributes_info(Attrs, [A|As]) when is_atom(A) ->
@@ -324,13 +338,20 @@ string_thing(_) -> "string".
(C >= $\000 andalso C =< $\s orelse C >= $\200 andalso C =< $\240)).
-define(DIGIT(C), C >= $0, C =< $9).
-define(CHAR(C), is_integer(C), C >= 0).
-
-%% A workaround: Unicode strings are not returned as strings, but as
-%% lists of integers. For instance, "b\x{aaa}c" => [98,2730,99]. This
-%% is to protect the system from character codes greater than 255. To
-%% be removed. Search for UNI to find workaround code.
+-define(UNICODE(C),
+ (C >= 0 andalso C < 16#D800 orelse
+ C > 16#DFFF andalso C < 16#FFFE orelse
+ C > 16#FFFF andalso C =< 16#10FFFF)).
+
+%% When the option 'unicode' is false: return Unicode strings as lists
+%% of integers and Unicode characters as integers. For instance,
+%% erl_scan:string("\"b\x{aaa}c\".") is equivalent to
+%% erl_scan:string("[98,2730,99]."). This is to protect the caller
+%% from character codes greater than 255. Search for UNI to find code
+%% implementing this "feature". The 'unicode' option is undocumented
+%% and will probably be removed later.
-define(NO_UNICODE, 0).
--define(UNI255(C), (C) =< 16#ff).
+-define(UNI255(C), (C =< 16#ff)).
options(Opts0) when is_list(Opts0) ->
Opts = lists:foldr(fun expand_opt/2, [], Opts0),
@@ -344,10 +365,12 @@ options(Opts0) when is_list(Opts0) ->
Comment = proplists:get_bool(return_comments, Opts),
WS = proplists:get_bool(return_white_spaces, Opts),
Txt = proplists:get_bool(text, Opts),
+ Unicode = proplists:get_bool(unicode, Opts),
#erl_scan{resword_fun = RW_fun,
comment = Comment,
ws = WS,
- text = Txt};
+ text = Txt,
+ unicode = Unicode};
options(Opt) ->
options([Opt]).
@@ -513,9 +536,9 @@ scan1([$$|Cs], St, Line, Col, Toks) ->
scan_char(Cs, St, Line, Col, Toks);
scan1([$\r|Cs], St, Line, Col, Toks) when St#erl_scan.ws ->
white_space_end(Cs, St, Line, Col, Toks, 1, "\r");
-scan1([C|Cs], St, Line, Col, Toks) when C >= $�, C =< $�, C =/= $� ->
+scan1([C|Cs], St, Line, Col, Toks) when C >= $ß, C =< $ÿ, C =/= $÷ ->
scan_atom(Cs, St, Line, Col, Toks, [C]);
-scan1([C|Cs], St, Line, Col, Toks) when C >= $�, C =< $�, C /= $� ->
+scan1([C|Cs], St, Line, Col, Toks) when C >= $À, C =< $Þ, C /= $× ->
scan_variable(Cs, St, Line, Col, Toks, [C]);
scan1([$\t|Cs], St, Line, Col, Toks) when St#erl_scan.ws ->
scan_tabs(Cs, St, Line, Col, Toks, 1);
@@ -628,15 +651,12 @@ scan1([$~|Cs], St, Line, Col, Toks) ->
scan1([$&|Cs], St, Line, Col, Toks) ->
tok2(Cs, St, Line, Col, Toks, "&", '&', 1);
%% End of optimization.
-scan1([C|Cs], St, Line, Col, Toks) when ?CHAR(C) ->
+scan1([C|Cs], St, Line, Col, Toks) when ?CHAR(C), ?UNI255(C) ->
Str = [C],
- case catch list_to_atom(Str) of
- Sym when is_atom(Sym) ->
- tok2(Cs, St, Line, Col, Toks, Str, Sym, 1);
- _ ->
- Ncol = incr_column(Col, 1),
- scan_error({illegal,character}, Line, Col, Line, Ncol, Cs)
- end;
+ tok2(Cs, St, Line, Col, Toks, Str, list_to_atom(Str), 1);
+scan1([C|Cs], _St, Line, Col, _Toks) when ?CHAR(C) ->
+ Ncol = incr_column(Col, 1),
+ scan_error({illegal,character}, Line, Col, Line, Ncol, Cs);
scan1([]=Cs, _St, Line, Col, Toks) ->
{more,{Cs,Col,Toks,Line,[],fun scan/6}};
scan1(eof=Cs, _St, Line, Col, Toks) ->
@@ -685,9 +705,9 @@ scan_name([C|Cs], Ncs) when ?DIGIT(C) ->
scan_name(Cs, [C|Ncs]);
scan_name([$@=C|Cs], Ncs) ->
scan_name(Cs, [C|Ncs]);
-scan_name([C|Cs], Ncs) when C >= $�, C =< $�, C =/= $� ->
+scan_name([C|Cs], Ncs) when C >= $ß, C =< $ÿ, C =/= $÷ ->
scan_name(Cs, [C|Ncs]);
-scan_name([C|Cs], Ncs) when C >= $�, C =< $�, C =/= $� ->
+scan_name([C|Cs], Ncs) when C >= $À, C =< $Þ, C =/= $× ->
scan_name(Cs, [C|Ncs]);
scan_name([], Ncs) ->
{more,Ncs};
@@ -834,32 +854,44 @@ scan_char([$\\|Cs]=Cs0, St, Line, Col, Toks) ->
{eof,Ncol} ->
scan_error(char, Line, Col, Line, Ncol, eof);
{nl,Val,Str,Ncs,Ncol} ->
- Attrs = attributes(Line, Col, St, "$\\"++Str),
+ Attrs = attributes(Line, Col, St, "$\\"++Str), %"
Ntoks = [{char,Attrs,Val}|Toks],
scan1(Ncs, St, Line+1, Ncol, Ntoks);
{unicode,Val,Str,Ncs,Ncol} ->
- Attrs = attributes(Line, Col, St, "$\\"++Str),
- Ntoks = [{integer,Attrs,Val}|Toks], % UNI
+ Attrs = attributes(Line, Col, St, "$\\"++Str), %"
+ Tag = char_tag(Val, St), % UNI
+ Ntoks = [{Tag,Attrs,Val}|Toks],
scan1(Ncs, St, Line, Ncol, Ntoks);
{Val,Str,Ncs,Ncol} ->
- Attrs = attributes(Line, Col, St, "$\\"++Str),
+ Attrs = attributes(Line, Col, St, "$\\"++Str), %"
Ntoks = [{char,Attrs,Val}|Toks],
scan1(Ncs, St, Line, Ncol, Ntoks)
end;
scan_char([$\n=C|Cs], St, Line, Col, Toks) ->
Attrs = attributes(Line, Col, St, [$$,C]),
scan1(Cs, St, Line+1, new_column(Col, 1), [{char,Attrs,C}|Toks]);
-scan_char([C|Cs], St, Line, Col, Toks) when ?CHAR(C) ->
- Tag = if ?UNI255(C) -> char; true -> integer end, % UNI
+scan_char([C|Cs], St, Line, Col, Toks) when ?UNICODE(C) ->
+ Tag = char_tag(C, St), % UNI
Attrs = attributes(Line, Col, St, [$$,C]),
scan1(Cs, St, Line, incr_column(Col, 2), [{Tag,Attrs,C}|Toks]);
+scan_char([C|_Cs], _St, Line, Col, _Toks) when ?CHAR(C) ->
+ scan_error({illegal,character}, Line, Col, Line, incr_column(Col, 1), eof);
scan_char([], _St, Line, Col, Toks) ->
{more,{[$$],Col,Toks,Line,[],fun scan/6}};
scan_char(eof, _St, Line, Col, _Toks) ->
scan_error(char, Line, Col, Line, incr_column(Col, 1), eof).
+-compile({inline,[char_tag/2]}).
+
+char_tag(C, _St) when ?UNI255(C) ->
+ char;
+char_tag(_C, #erl_scan{unicode = true}) ->
+ char;
+char_tag(_C, _St) ->
+ integer.
+
scan_string(Cs, St, Line, Col, Toks, {Wcs,Str,Line0,Col0,Uni0}) ->
- case scan_string0(Cs, St, Line, Col, $\", Str, Wcs, Uni0) of
+ case scan_string0(Cs, St, Line, Col, $\", true, Str, Wcs, Uni0) of %"
{more,Ncs,Nline,Ncol,Nstr,Nwcs,Uni} ->
State = {Nwcs,Nstr,Line0,Col0,Uni},
{more,{Ncs,Ncol,Toks,Nline,State,fun scan_string/6}};
@@ -867,8 +899,9 @@ scan_string(Cs, St, Line, Col, Toks, {Wcs,Str,Line0,Col0,Uni0}) ->
scan_error(Error, Nline, Ncol, Nline, EndCol, Ncs);
{error,Nline,Ncol,Nwcs,Ncs} ->
Estr = string:substr(Nwcs, 1, 16), % Expanded escape chars.
- scan_error({string,$\",Estr}, Line0, Col0, Nline, Ncol, Ncs);
- {Ncs,Nline,Ncol,Nstr,Nwcs,?NO_UNICODE} ->
+ scan_error({string,$\",Estr}, Line0, Col0, Nline, Ncol, Ncs); %"
+ {Ncs,Nline,Ncol,Nstr,Nwcs,Uni} when Uni =:= ?NO_UNICODE;
+ St#erl_scan.unicode ->
Attrs = attributes(Line0, Col0, St, Nstr),
scan1(Ncs, St, Nline, Ncol, [{string,Attrs,Nwcs}|Toks]);
{Ncs,Nline,Ncol,Nstr,_Nwcs,_Uni} ->
@@ -920,7 +953,8 @@ unicode_tokens(Line, Col, Str, Val, St, Toks, Cs, Cline, Ccol) ->
[{',',attributes(Cline, Ccol, St, "")} || Cs =/= "\""] ++ [Token|Toks].
scan_qatom(Cs, St, Line, Col, Toks, {Wcs,Str,Line0,Col0,Uni0}) ->
- case scan_string0(Cs, St, Line, Col, $\', Str, Wcs, Uni0) of
+ AllowUni = St#erl_scan.unicode,
+ case scan_string0(Cs, St, Line, Col, $\', AllowUni, Str, Wcs, Uni0) of %'
{more,Ncs,Nline,Ncol,Nstr,Nwcs,Uni} ->
State = {Nwcs,Nstr,Line0,Col0,Uni},
{more,{Ncs,Ncol,Toks,Nline,State,fun scan_qatom/6}};
@@ -928,8 +962,9 @@ scan_qatom(Cs, St, Line, Col, Toks, {Wcs,Str,Line0,Col0,Uni0}) ->
scan_error(Error, Nline, Ncol, Nline, EndCol, Ncs);
{error,Nline,Ncol,Nwcs,Ncs} ->
Estr = string:substr(Nwcs, 1, 16), % Expanded escape chars.
- scan_error({string,$\',Estr}, Line0, Col0, Nline, Ncol, Ncs);
- {Ncs,Nline,Ncol,Nstr,Nwcs,?NO_UNICODE} ->
+ scan_error({string,$\',Estr}, Line0, Col0, Nline, Ncol, Ncs); %'
+ {Ncs,Nline,Ncol,Nstr,Nwcs,Uni} ->
+ true = Uni =:= ?NO_UNICODE orelse AllowUni,
case catch list_to_atom(Nwcs) of
A when is_atom(A) ->
Attrs = attributes(Line0, Col0, St, Nstr),
@@ -939,38 +974,40 @@ scan_qatom(Cs, St, Line, Col, Toks, {Wcs,Str,Line0,Col0,Uni0}) ->
end
end.
-scan_string0(Cs, #erl_scan{text=false}, Line, no_col=Col, Q, [], Wcs, Uni) ->
- scan_string_no_col(Cs, Line, Col, Q, Wcs, Uni);
-scan_string0(Cs, #erl_scan{text=true}, Line, no_col=Col, Q, Str, Wcs, Uni) ->
- scan_string1(Cs, Line, Col, Q, Str, Wcs, Uni);
-scan_string0(Cs, _St, Line, Col, Q, [], Wcs, Uni) ->
- scan_string_col(Cs, Line, Col, Q, Wcs, Uni);
-scan_string0(Cs, _St, Line, Col, Q, Str, Wcs, Uni) ->
- scan_string1(Cs, Line, Col, Q, Str, Wcs, Uni).
+scan_string0(Cs, #erl_scan{text=false}, Line, no_col=Col, Q, U, [], Wcs, Uni) ->
+ scan_string_no_col(Cs, Line, Col, Q, U, Wcs, Uni);
+scan_string0(Cs, #erl_scan{text=true}, Line, no_col=Col, Q, U, Str, Wcs, Uni) ->
+ scan_string1(Cs, Line, Col, Q, U, Str, Wcs, Uni);
+scan_string0(Cs, _St, Line, Col, Q, U, [], Wcs, Uni) ->
+ scan_string_col(Cs, Line, Col, Q, U, Wcs, Uni);
+scan_string0(Cs, _St, Line, Col, Q, U, Str, Wcs, Uni) ->
+ scan_string1(Cs, Line, Col, Q, U, Str, Wcs, Uni).
%% Optimization. Col =:= no_col.
-scan_string_no_col([Q|Cs], Line, Col, Q, Wcs, Uni) ->
+scan_string_no_col([Q|Cs], Line, Col, Q, _U, Wcs, Uni) ->
{Cs,Line,Col,_DontCare=[],lists:reverse(Wcs),Uni};
-scan_string_no_col([$\n=C|Cs], Line, Col, Q, Wcs, Uni) ->
- scan_string_no_col(Cs, Line+1, Col, Q, [C|Wcs], Uni);
-scan_string_no_col([C|Cs], Line, Col, Q, Wcs, Uni) when C =/= $\\,
- ?CHAR(C), ?UNI255(C) ->
- scan_string_no_col(Cs, Line, Col, Q, [C|Wcs], Uni);
-scan_string_no_col(Cs, Line, Col, Q, Wcs, Uni) ->
- scan_string1(Cs, Line, Col, Q, Wcs, Wcs, Uni).
+scan_string_no_col([$\n=C|Cs], Line, Col, Q, U, Wcs, Uni) ->
+ scan_string_no_col(Cs, Line+1, Col, Q, U, [C|Wcs], Uni);
+scan_string_no_col([C|Cs], Line, Col, Q, U, Wcs, Uni) when C =/= $\\,
+ ?CHAR(C),
+ ?UNI255(C) ->
+ scan_string_no_col(Cs, Line, Col, Q, U, [C|Wcs], Uni);
+scan_string_no_col(Cs, Line, Col, Q, U, Wcs, Uni) ->
+ scan_string1(Cs, Line, Col, Q, U, Wcs, Wcs, Uni).
%% Optimization. Col =/= no_col.
-scan_string_col([Q|Cs], Line, Col, Q, Wcs0, Uni) ->
+scan_string_col([Q|Cs], Line, Col, Q, _U, Wcs0, Uni) ->
Wcs = lists:reverse(Wcs0),
Str = [Q|Wcs++[Q]],
{Cs,Line,Col+1,Str,Wcs,Uni};
-scan_string_col([$\n=C|Cs], Line, _xCol, Q, Wcs, Uni) ->
- scan_string_col(Cs, Line+1, 1, Q, [C|Wcs], Uni);
-scan_string_col([C|Cs], Line, Col, Q, Wcs, Uni) when C =/= $\\,
- ?CHAR(C), ?UNI255(C) ->
- scan_string_col(Cs, Line, Col+1, Q, [C|Wcs], Uni);
-scan_string_col(Cs, Line, Col, Q, Wcs, Uni) ->
- scan_string1(Cs, Line, Col, Q, Wcs, Wcs, Uni).
+scan_string_col([$\n=C|Cs], Line, _xCol, Q, U, Wcs, Uni) ->
+ scan_string_col(Cs, Line+1, 1, Q, U, [C|Wcs], Uni);
+scan_string_col([C|Cs], Line, Col, Q, U, Wcs, Uni) when C =/= $\\,
+ ?CHAR(C),
+ ?UNI255(C) ->
+ scan_string_col(Cs, Line, Col+1, Q, U, [C|Wcs], Uni);
+scan_string_col(Cs, Line, Col, Q, U, Wcs, Uni) ->
+ scan_string1(Cs, Line, Col, Q, U, Wcs, Wcs, Uni).
%% UNI_STR is to be replaced by STR when the Unicode-string-to-list
%% workaround is eventually removed.
@@ -981,14 +1018,14 @@ scan_string_col(Cs, Line, Col, Q, Wcs, Uni) ->
%% but then the end location of the error tuple would not correspond
%% to the start location of the returned Rest string. (Maybe the end
%% location could be modified, but that too is ugly.)
-scan_string1([Q|Cs], Line, Col, Q, Str0, Wcs0, Uni) ->
+scan_string1([Q|Cs], Line, Col, Q, _U, Str0, Wcs0, Uni) ->
Wcs = lists:reverse(Wcs0),
Str = ?UNI_STR(Col, [Q|lists:reverse(Str0, [Q])]),
{Cs,Line,incr_column(Col, 1),Str,Wcs,Uni};
-scan_string1([$\n=C|Cs], Line, Col, Q, Str, Wcs, Uni) ->
+scan_string1([$\n=C|Cs], Line, Col, Q, U, Str, Wcs, Uni) ->
Ncol = new_column(Col, 1),
- scan_string1(Cs, Line+1, Ncol, Q, ?UNI_STR(Col, [C|Str]), [C|Wcs], Uni);
-scan_string1([$\\|Cs]=Cs0, Line, Col, Q, Str, Wcs, Uni) ->
+ scan_string1(Cs, Line+1, Ncol, Q, U, ?UNI_STR(Col, [C|Str]), [C|Wcs], Uni);
+scan_string1([$\\|Cs]=Cs0, Line, Col, Q, U, Str, Wcs, Uni) ->
case scan_escape(Cs, Col) of
more ->
{more,Cs0,Line,Col,Str,Wcs,Uni};
@@ -999,31 +1036,33 @@ scan_string1([$\\|Cs]=Cs0, Line, Col, Q, Str, Wcs, Uni) ->
{nl,Val,ValStr,Ncs,Ncol} ->
Nstr = ?UNI_STR(Ncol, lists:reverse(ValStr, [$\\|Str])),
Nwcs = [Val|Wcs],
- scan_string1(Ncs, Line+1, Ncol, Q, Nstr, Nwcs, Uni);
- {unicode,_Val,_ValStr,Ncs,Ncol} when Q =:= $' -> %' Emacs
+ scan_string1(Ncs, Line+1, Ncol, Q, U, Nstr, Nwcs, Uni);
+ {unicode,_Val,_ValStr,Ncs,Ncol} when not U -> %' Emacs
{char_error,Ncs,{illegal,character},Line,Col,incr_column(Ncol, 1)};
{unicode,Val,ValStr,Ncs,Ncol} -> % UNI. Uni is set to Val.
Nstr = ?UNI_STR(Ncol, lists:reverse(ValStr, [$\\|Str])),
Nwcs = [Val|Wcs], % not used
- scan_string1(Ncs, Line, incr_column(Ncol, 1), Q, Nstr, Nwcs, Val);
+ scan_string1(Ncs, Line, incr_column(Ncol, 1), Q, U, Nstr, Nwcs, Val);
{Val,ValStr,Ncs,Ncol} ->
Nstr = ?UNI_STR(Ncol, lists:reverse(ValStr, [$\\|Str])),
Nwcs = [Val|Wcs],
- scan_string1(Ncs, Line, incr_column(Ncol, 1), Q, Nstr, Nwcs, Uni)
+ scan_string1(Ncs, Line, incr_column(Ncol, 1), Q, U, Nstr, Nwcs, Uni)
end;
-scan_string1([C|Cs], Line, no_col=Col, Q, Str, Wcs, Uni) when ?CHAR(C),
- ?UNI255(C) ->
- %% scan_string1(Cs, Line, Col, Q, Str, [C|Wcs], Uni);
- scan_string1(Cs, Line, Col, Q, [C|Str], [C|Wcs], Uni); % UNI
-scan_string1([C|Cs], Line, Col, Q, Str, Wcs, Uni) when ?CHAR(C), ?UNI255(C) ->
- scan_string1(Cs, Line, Col+1, Q, [C|Str], [C|Wcs], Uni);
-scan_string1([C|Cs], Line, Col, $', _Str, _Wcs, _Uni) when ?CHAR(C) -> %' UNI
+scan_string1([C|Cs], Line, no_col=Col, Q, U, Str, Wcs, Uni) when ?CHAR(C),
+ ?UNI255(C) ->
+ %% scan_string1(Cs, Line, Col, Q, U, Str, [C|Wcs], Uni);
+ scan_string1(Cs, Line, Col, Q, U, [C|Str], [C|Wcs], Uni); % UNI
+scan_string1([C|Cs], Line, Col, Q, U, Str, Wcs, Uni) when ?CHAR(C), ?UNI255(C) ->
+ scan_string1(Cs, Line, Col+1, Q, U, [C|Str], [C|Wcs], Uni);
+scan_string1([C|Cs], Line, Col, _Q, false, _Str, _Wcs, _Uni) when ?CHAR(C) -> %' UNI
+ {char_error,Cs,{illegal,character},Line,Col,incr_column(Col, 1)};
+scan_string1([C|Cs], Line, Col, Q, U, Str, Wcs, _Uni) when ?UNICODE(C) ->
+ scan_string1(Cs, Line, incr_column(Col, 1), Q, U, [C|Str], [C|Wcs], C);
+scan_string1([C|Cs], Line, Col, _Q, _U, _Str, _Wcs, _Uni) when ?CHAR(C) -> % UNI
{char_error,Cs,{illegal,character},Line,Col,incr_column(Col, 1)};
-scan_string1([C|Cs], Line, Col, Q, Str, Wcs, _Uni) when ?CHAR(C) -> % UNI
- scan_string1(Cs, Line, incr_column(Col, 1), Q, [C|Str], [C|Wcs], C);
-scan_string1([]=Cs, Line, Col, _Q, Str, Wcs, Uni) ->
+scan_string1([]=Cs, Line, Col, _Q, _U, Str, Wcs, Uni) ->
{more,Cs,Line,Col,Str,Wcs,Uni};
-scan_string1(eof, Line, Col, _Q, _Str, Wcs, _Uni) ->
+scan_string1(eof, Line, Col, _Q, _U, _Str, Wcs, _Uni) ->
{error,Line,Col,lists:reverse(Wcs),eof}.
-define(OCT(C), C >= $0, C =< $7).
@@ -1074,8 +1113,10 @@ scan_escape([$\n=C|Cs], Col) ->
scan_escape([C0|Cs], Col) when ?CHAR(C0), ?UNI255(C0) ->
C = escape_char(C0),
{C,?UNI_STR(Col, [C0]),Cs,incr_column(Col, 1)};
-scan_escape([C|Cs], Col) when ?CHAR(C) -> % UNI
+scan_escape([C|Cs], Col) when ?UNICODE(C) ->
{unicode,C,?UNI_STR(Col, [C]),Cs,incr_column(Col, 1)};
+scan_escape([C|Cs], Col) when ?CHAR(C) -> % UNI
+ {error,Cs,{illegal,character},incr_column(Col, 1)};
scan_escape([], _Col) ->
more;
scan_escape(eof, Col) ->
@@ -1093,7 +1134,7 @@ scan_esc_end([$}|Cs], Col, Wcs0, B, Str0) ->
case catch erlang:list_to_integer(Wcs, B) of
Val when Val =< 16#FF ->
{Val,?UNI_STR(Col, Str0++Wcs++[$}]),Cs,incr_column(Col, 1)};
- Val when Val =< 16#10FFFF ->
+ Val when ?UNICODE(Val) ->
{unicode,Val,?UNI_STR(Col, Str0++Wcs++[$}]),Cs,incr_column(Col,1)};
_ ->
{error,Cs,{illegal,character},incr_column(Col, 1)}
@@ -1199,18 +1240,36 @@ float_end(Cs, St, Line, Col, Toks, Ncs0) ->
scan_error({illegal,float}, Line, Col, Line, Ncol, Cs)
end.
-skip_comment([C|Cs], St, Line, Col, Toks, N) when C =/= $\n, ?CHAR(C) ->
- skip_comment(Cs, St, Line, Col, Toks, N+1);
-skip_comment([]=Cs, _St, Line, Col, Toks, N) ->
- {more,{Cs,Col,Toks,Line,N,fun skip_comment/6}};
skip_comment(Cs, St, Line, Col, Toks, N) ->
+ skip_comment(Cs, St, Line, Col, Toks, N, St#erl_scan.unicode).
+
+skip_comment([C|Cs], St, Line, Col, Toks, N, U) when C =/= $\n, ?CHAR(C) ->
+ case ?UNI255(C) orelse U andalso ?UNICODE(C) of
+ true ->
+ skip_comment(Cs, St, Line, Col, Toks, N+1, U);
+ false ->
+ Ncol = incr_column(Col, N+1),
+ scan_error({illegal,character}, Line, Col, Line, Ncol, Cs)
+ end;
+skip_comment([]=Cs, _St, Line, Col, Toks, N, _U) ->
+ {more,{Cs,Col,Toks,Line,N,fun skip_comment/6}};
+skip_comment(Cs, St, Line, Col, Toks, N, _U) ->
scan1(Cs, St, Line, incr_column(Col, N), Toks).
-scan_comment([C|Cs], St, Line, Col, Toks, Ncs) when C =/= $\n, ?CHAR(C) ->
- scan_comment(Cs, St, Line, Col, Toks, [C|Ncs]);
-scan_comment([]=Cs, _St, Line, Col, Toks, Ncs) ->
+scan_comment(Cs, St, Line, Col, Toks, Ncs) ->
+ scan_comment(Cs, St, Line, Col, Toks, Ncs, St#erl_scan.unicode).
+
+scan_comment([C|Cs], St, Line, Col, Toks, Ncs, U) when C =/= $\n, ?CHAR(C) ->
+ case ?UNI255(C) orelse U andalso ?UNICODE(C) of
+ true ->
+ scan_comment(Cs, St, Line, Col, Toks, [C|Ncs], U);
+ false ->
+ Ncol = incr_column(Col, length(Ncs)+1),
+ scan_error({illegal,character}, Line, Col, Line, Ncol, Cs)
+ end;
+scan_comment([]=Cs, _St, Line, Col, Toks, Ncs, _U) ->
{more,{Cs,Col,Toks,Line,Ncs,fun scan_comment/6}};
-scan_comment(Cs, St, Line, Col, Toks, Ncs0) ->
+scan_comment(Cs, St, Line, Col, Toks, Ncs0, _U) ->
Ncs = lists:reverse(Ncs0),
tok3(Cs, St, Line, Col, Toks, comment, Ncs, Ncs).
@@ -1345,7 +1404,6 @@ reserved_word('fun') -> true;
reserved_word('if') -> true;
reserved_word('let') -> true;
reserved_word('of') -> true;
-reserved_word('query') -> true;
reserved_word('receive') -> true;
reserved_word('when') -> true;
reserved_word('bnot') -> true;
diff --git a/lib/stdlib/src/escript.erl b/lib/stdlib/src/escript.erl
index 27e70ac4d4..99a9d138ac 100644
--- a/lib/stdlib/src/escript.erl
+++ b/lib/stdlib/src/escript.erl
@@ -22,7 +22,7 @@
-export([script_name/0, create/2, extract/2]).
%% Internal API.
--export([start/0, start/1]).
+-export([start/0, start/1, parse_file/1]).
%%-----------------------------------------------------------------------
@@ -346,7 +346,8 @@ parse_and_run(File, Args, Options) ->
case Source of
archive ->
{ok, FileInfo} = file:read_file_info(File),
- case code:set_primary_archive(File, FormsOrBin, FileInfo) of
+ case code:set_primary_archive(File, FormsOrBin, FileInfo,
+ fun escript:parse_file/1) of
ok when CheckOnly ->
case code:load_file(Module) of
{module, _} ->
@@ -396,6 +397,19 @@ parse_and_run(File, Args, Options) ->
%% Parse script
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Only used as callback by erl_prim_loader
+parse_file(File) ->
+ try parse_file(File, false) of
+ {_Source, _Module, FormsOrBin, _HasRecs, _Mode}
+ when is_binary(FormsOrBin) ->
+ {ok, FormsOrBin};
+ _ ->
+ {error, no_archive_bin}
+ catch
+ throw:Reason ->
+ {error, Reason}
+ end.
+
parse_file(File, CheckOnly) ->
{HeaderSz, NextLineNo, Fd, Sections} =
parse_header(File, false),
@@ -696,7 +710,7 @@ epp_parse_file2(Epp, S, Forms, Parsed) ->
epp_parse_file(Epp, S, [Form | Forms])
end;
{error,{Ln,Mod,Args}} = Form ->
- io:format("~s:~w: ~s\n",
+ io:format("~s:~w: ~ts\n",
[S#state.file,Ln,Mod:format_error(Args)]),
epp_parse_file(Epp, S#state{n_errors = S#state.n_errors + 1}, [Form | Forms]);
{eof, _LastLine} = Eof ->
@@ -766,10 +780,10 @@ report_errors(Errors) ->
Errors).
list_errors(F, [{Line,Mod,E}|Es]) ->
- io:fwrite("~s:~w: ~s\n", [F,Line,Mod:format_error(E)]),
+ io:fwrite("~s:~w: ~ts\n", [F,Line,Mod:format_error(E)]),
list_errors(F, Es);
list_errors(F, [{Mod,E}|Es]) ->
- io:fwrite("~s: ~s\n", [F,Mod:format_error(E)]),
+ io:fwrite("~s: ~ts\n", [F,Mod:format_error(E)]),
list_errors(F, Es);
list_errors(_F, []) -> ok.
@@ -781,10 +795,10 @@ report_warnings(Ws0) ->
lists:foreach(fun({_,Str}) -> io:put_chars(Str) end, Ws).
format_message(F, [{Line,Mod,E}|Es]) ->
- M = {{F,Line},io_lib:format("~s:~w: Warning: ~s\n", [F,Line,Mod:format_error(E)])},
+ M = {{F,Line},io_lib:format("~s:~w: Warning: ~ts\n", [F,Line,Mod:format_error(E)])},
[M|format_message(F, Es)];
format_message(F, [{Mod,E}|Es]) ->
- M = {none,io_lib:format("~s: Warning: ~s\n", [F,Mod:format_error(E)])},
+ M = {none,io_lib:format("~s: Warning: ~ts\n", [F,Mod:format_error(E)])},
[M|format_message(F, Es)];
format_message(_, []) -> [].
@@ -837,12 +851,27 @@ eval_exprs([E|Es], Bs0, Lf, Ef, RBs) ->
eval_exprs(Es, Bs, Lf, Ef, RBs).
format_exception(Class, Reason) ->
+ Enc = encoding(),
+ P = case Enc of
+ latin1 -> "P";
+ _ -> "tP"
+ end,
PF = fun(Term, I) ->
- io_lib:format("~." ++ integer_to_list(I) ++ "P", [Term, 50])
+ io_lib:format("~." ++ integer_to_list(I) ++ P, [Term, 50])
end,
StackTrace = erlang:get_stacktrace(),
StackFun = fun(M, _F, _A) -> (M =:= erl_eval) or (M =:= ?MODULE) end,
- lib:format_exception(1, Class, Reason, StackTrace, StackFun, PF).
+ lib:format_exception(1, Class, Reason, StackTrace, StackFun, PF, Enc).
+
+encoding() ->
+ [{encoding, Encoding}] = enc(),
+ Encoding.
+
+enc() ->
+ case lists:keyfind(encoding, 1, io:getopts()) of
+ false -> [{encoding,latin1}]; % should never happen
+ Enc -> [Enc]
+ end.
fatal(Str) ->
throw(Str).
diff --git a/lib/stdlib/src/ets.erl b/lib/stdlib/src/ets.erl
index 817b397cc4..61bb038737 100644
--- a/lib/stdlib/src/ets.erl
+++ b/lib/stdlib/src/ets.erl
@@ -42,7 +42,7 @@
-export([i/0, i/1, i/2, i/3]).
--export_type([tab/0, tid/0, match_spec/0]).
+-export_type([tab/0, tid/0, match_spec/0, comp_match_spec/0, match_pattern/0]).
%%-----------------------------------------------------------------------------
@@ -445,7 +445,7 @@ update_element(_, _, _) ->
%%% End of BIFs
--opaque comp_match_spec() :: any(). %% this one is REALLY opaque
+-opaque comp_match_spec() :: binary(). %% this one is REALLY opaque
-spec match_spec_run(List, CompiledMatchSpec) -> list() when
List :: [tuple()],
diff --git a/lib/stdlib/src/filelib.erl b/lib/stdlib/src/filelib.erl
index b098d4cb91..318f3b87b8 100644
--- a/lib/stdlib/src/filelib.erl
+++ b/lib/stdlib/src/filelib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -132,6 +132,8 @@ do_wildcard_comp({compiled_wildcard,{exists,File}}, Mod) ->
{ok,_} -> [File];
_ -> []
end;
+do_wildcard_comp({compiled_wildcard,[cwd,Base|Rest]}, Mod) ->
+ do_wildcard_1([Base], Rest, Mod);
do_wildcard_comp({compiled_wildcard,[Base|Rest]}, Mod) ->
do_wildcard_1([Base], Rest, Mod).
@@ -143,7 +145,11 @@ do_wildcard_comp({compiled_wildcard,{exists,File}}, Cwd, Mod) ->
{ok,_} -> [File];
_ -> []
end;
-do_wildcard_comp({compiled_wildcard,[current|Rest]}, Cwd0, Mod) ->
+do_wildcard_comp({compiled_wildcard,[cwd|Rest0]}, Cwd0, Mod) ->
+ case Rest0 of
+ [current|Rest] -> ok;
+ Rest -> ok
+ end,
{Cwd,PrefixLen} = case filename:join([Cwd0]) of
Bin when is_binary(Bin) -> {Bin,byte_size(Bin)+1};
Other -> {Other,length(Other)+1}
@@ -295,6 +301,8 @@ do_wildcard_2([File|Rest], Pattern, Result, Mod) ->
do_wildcard_2([], _, Result, _Mod) ->
Result.
+do_wildcard_3(Base, [[double_star]|Rest], Result, Mod) ->
+ lists:sort(do_double_star(current, [Base], Rest, Result, Mod, true));
do_wildcard_3(Base, [Pattern|Rest], Result, Mod) ->
case do_list_dir(Base, Mod) of
{ok, Files0} ->
@@ -328,6 +336,8 @@ wildcard_5([question|Rest1], [_|Rest2]) ->
wildcard_5(Rest1, Rest2);
wildcard_5([accept], _) ->
true;
+wildcard_5([double_star], _) ->
+ true;
wildcard_5([star|Rest], File) ->
do_star(Rest, File);
wildcard_5([{one_of, Ordset}|Rest], [C|File]) ->
@@ -348,6 +358,21 @@ wildcard_5([], [_|_]) ->
wildcard_5([_|_], []) ->
false.
+do_double_star(Base, [H|T], Rest, Result, Mod, Root) ->
+ Full = join(Base, H),
+ Result1 = case do_list_dir(Full, Mod) of
+ {ok, Files} ->
+ do_double_star(Full, Files, Rest, Result, Mod, false);
+ _ -> Result
+ end,
+ Result2 = case Root andalso Rest == [] of
+ true -> Result1;
+ false -> do_wildcard_3(Full, Rest, Result1, Mod)
+ end,
+ do_double_star(Base, T, Rest, Result2, Mod, Root);
+do_double_star(_Base, [], _Rest, Result, _Mod, _Root) ->
+ Result.
+
do_star(Pattern, [X|Rest]) ->
case wildcard_5(Pattern, [X|Rest]) of
true -> true;
@@ -383,7 +408,10 @@ compile_wildcard_1(Pattern) ->
[Root|Rest] = filename:split(Pattern),
case filename:pathtype(Root) of
relative ->
- compile_wildcard_2([Root|Rest], current);
+ case compile_wildcard_2([Root|Rest], current) of
+ {exists,_}=Wc -> Wc;
+ [_|_]=Wc -> [cwd|Wc]
+ end;
_ ->
compile_wildcard_2(Rest, [Root])
end.
@@ -416,6 +444,10 @@ compile_part([$}|Rest], true, Result) ->
{ok, $}, lists:reverse(Result), Rest};
compile_part([$?|Rest], Upto, Result) ->
compile_part(Rest, Upto, [question|Result]);
+compile_part([$*,$*], Upto, Result) ->
+ compile_part([], Upto, [double_star|Result]);
+compile_part([$*,$*|Rest], Upto, Result) ->
+ compile_part(Rest, Upto, [star|Result]);
compile_part([$*], Upto, Result) ->
compile_part([], Upto, [accept|Result]);
compile_part([$*|Rest], Upto, Result) ->
diff --git a/lib/stdlib/src/filename.erl b/lib/stdlib/src/filename.erl
index dbfcbea4f7..0c50eb34e6 100644
--- a/lib/stdlib/src/filename.erl
+++ b/lib/stdlib/src/filename.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -69,7 +69,7 @@ absname(Name) ->
-spec absname(Filename, Dir) -> file:filename() when
Filename :: file:name(),
- Dir :: file:filename().
+ Dir :: file:name().
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) ->
@@ -123,7 +123,7 @@ absname_vr([[X, $:]|Name], _, _AbsBase) ->
%% AbsBase must be absolute and Name must be relative.
-spec absname_join(Dir, Filename) -> file:filename() when
- Dir :: file:filename(),
+ Dir :: file:name(),
Filename :: file:name().
absname_join(AbsBase, Name) ->
join(AbsBase, flatten(Name)).
@@ -388,7 +388,7 @@ extension([], Result, _OsType) ->
%% Joins a list of filenames with directory separators.
-spec join(Components) -> file:filename() when
- Components :: [file:filename()].
+ Components :: [file:name()].
join([Name1, Name2|Rest]) ->
join([join(Name1, Name2)|Rest]);
join([Name]) when is_list(Name) ->
@@ -401,8 +401,8 @@ join([Name]) when is_atom(Name) ->
%% Joins two filenames with directory separators.
-spec join(Name1, Name2) -> file:filename() when
- Name1 :: file:filename(),
- Name2 :: file:filename().
+ Name1 :: file:name(),
+ Name2 :: file:name().
join(Name1, Name2) when is_list(Name1), is_list(Name2) ->
OsType = major_os_type(),
case pathtype(Name2) of
@@ -624,7 +624,7 @@ rootname2([Char|Rest], Ext, Result) when is_integer(Char) ->
-spec split(Filename) -> Components when
Filename :: file:name(),
- Components :: [file:filename()].
+ Components :: [file:name()].
split(Name) when is_binary(Name) ->
case os:type() of
{win32, _} -> win32_splitb(Name);
@@ -718,7 +718,7 @@ split([], Comp, Components, OsType) ->
%% name will be normalized as done by join/1.
-spec nativename(Path) -> file:filename() when
- Path :: file:filename().
+ Path :: file:name().
nativename(Name0) ->
Name = join([Name0]), %Normalize.
case os:type() of
@@ -726,6 +726,8 @@ nativename(Name0) ->
_ -> Name
end.
+win32_nativename(Name) when is_binary(Name) ->
+ binary:replace(Name, <<"/">>, <<"\\">>, [global]);
win32_nativename([$/|Rest]) ->
[$\\|win32_nativename(Rest)];
win32_nativename([C|Rest]) ->
@@ -876,7 +878,7 @@ filter_options(_Base, [], Result) ->
%% Gets the source file given path of object code and module name.
get_source_file(Obj, Mod, Rules) ->
- source_by_rules(dirname(Obj), packages:last(Mod), Rules).
+ source_by_rules(dirname(Obj), atom_to_list(Mod), Rules).
source_by_rules(Dir, Base, [{From, To}|Rest]) ->
case try_rule(Dir, Base, From, To) of
@@ -913,10 +915,8 @@ make_abs_path(BasePath, Path) ->
join(BasePath, Path).
major_os_type() ->
- case os:type() of
- {OsT, _} -> OsT;
- OsT -> OsT
- end.
+ {OsT, _} = os:type(),
+ OsT.
%% flatten(List)
%% Flatten a list, also accepting atoms.
diff --git a/lib/stdlib/src/gb_sets.erl b/lib/stdlib/src/gb_sets.erl
index 91d21d869c..ba35a7170a 100644
--- a/lib/stdlib/src/gb_sets.erl
+++ b/lib/stdlib/src/gb_sets.erl
@@ -1,7 +1,8 @@
+%% -*- coding: utf-8 -*-
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -165,7 +166,7 @@
-export([new/0, is_element/2, add_element/2, del_element/2,
subtract/2]).
-%% GB-trees adapted from Sven-Olof Nystr�m's implementation for
+%% GB-trees adapted from Sven-Olof Nyström's implementation for
%% representation of sets.
%%
%% Data structures:
@@ -196,6 +197,8 @@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Some types.
+-export_type([iter/0]).
+
-type gb_set_node() :: 'nil' | {term(), _, _}.
-opaque iter() :: [gb_set_node()].
diff --git a/lib/stdlib/src/gb_trees.erl b/lib/stdlib/src/gb_trees.erl
index 6ad861ff5b..de0c239e26 100644
--- a/lib/stdlib/src/gb_trees.erl
+++ b/lib/stdlib/src/gb_trees.erl
@@ -1,7 +1,8 @@
+%% -*- coding: utf-8 -*-
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -19,7 +20,7 @@
%% =====================================================================
%% General Balanced Trees - highly efficient dictionaries.
%%
-%% Copyright (C) 1999-2001 Sven-Olof Nystr�m, Richard Carlsson
+%% Copyright (C) 1999-2001 Sven-Olof Nyström, Richard Carlsson
%%
%% An efficient implementation of Prof. Arne Andersson's General
%% Balanced Trees. These have no storage overhead compared to plain
@@ -152,6 +153,8 @@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Some types.
+-export_type([iter/0]).
+
-type gb_tree_node() :: 'nil' | {_, _, _, _}.
-opaque iter() :: [gb_tree_node()].
diff --git a/lib/stdlib/src/gen_server.erl b/lib/stdlib/src/gen_server.erl
index 59c6d240ba..04308a51b7 100644
--- a/lib/stdlib/src/gen_server.erl
+++ b/lib/stdlib/src/gen_server.erl
@@ -270,7 +270,7 @@ enter_loop(Mod, Options, State) ->
enter_loop(Mod, Options, State, self(), infinity).
enter_loop(Mod, Options, State, ServerName = {Scope, _})
- when Scope == local; Scope == local ->
+ when Scope == local; Scope == global ->
enter_loop(Mod, Options, State, ServerName, infinity);
enter_loop(Mod, Options, State, ServerName = {via, _, _}) ->
diff --git a/lib/stdlib/src/io.erl b/lib/stdlib/src/io.erl
index 9f65bbfa3a..ecf2aeb375 100644
--- a/lib/stdlib/src/io.erl
+++ b/lib/stdlib/src/io.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -22,28 +22,28 @@
get_chars/2,get_chars/3,get_line/1,get_line/2,
get_password/0, get_password/1,
setopts/1, setopts/2, getopts/0, getopts/1]).
--export([write/1,write/2,read/1,read/2,read/3]).
+-export([write/1,write/2,read/1,read/2,read/3,read/4]).
-export([columns/0,columns/1,rows/0,rows/1]).
-export([fwrite/1,fwrite/2,fwrite/3,fread/2,fread/3,
format/1,format/2,format/3]).
--export([scan_erl_exprs/1,scan_erl_exprs/2,scan_erl_exprs/3,
- scan_erl_form/1,scan_erl_form/2,scan_erl_form/3,
+-export([scan_erl_exprs/1,scan_erl_exprs/2,scan_erl_exprs/3,scan_erl_exprs/4,
+ scan_erl_form/1,scan_erl_form/2,scan_erl_form/3,scan_erl_form/4,
parse_erl_exprs/1,parse_erl_exprs/2,parse_erl_exprs/3,
- parse_erl_form/1,parse_erl_form/2,parse_erl_form/3]).
+ parse_erl_exprs/4,parse_erl_form/1,parse_erl_form/2,
+ parse_erl_form/3,parse_erl_form/4]).
-export([request/1,request/2,requests/1,requests/2]).
--export_type([device/0, format/0]).
+-export_type([device/0, format/0, server_no_data/0]).
%%-------------------------------------------------------------------------
-type device() :: atom() | pid().
-type prompt() :: atom() | string().
--type error_description() :: term(). % Whatever the io-server sends.
--type request_error() :: {'error',error_description()}.
+%% ErrorDescription is whatever the I/O-server sends.
+-type server_no_data() :: {'error', ErrorDescription :: term()} | 'eof'.
-%% XXX: Some uses of line() in this file may need to read erl_scan:location()
--type line() :: pos_integer().
+-type location() :: erl_scan:location().
%%-------------------------------------------------------------------------
@@ -73,9 +73,9 @@ o_request(Io, Request, Func) ->
put_chars(Chars) ->
put_chars(default_output(), Chars).
--spec put_chars(IoDevice, IoData) -> 'ok' when
+-spec put_chars(IoDevice, CharData) -> 'ok' when
IoDevice :: device(),
- IoData :: unicode:chardata().
+ CharData :: unicode:chardata().
put_chars(Io, Chars) ->
o_request(Io, {put_chars,unicode,Chars}, put_chars).
@@ -124,7 +124,7 @@ rows(Io) ->
{error,enotsup}
end.
--spec get_chars(Prompt, Count) -> Data | 'eof' when
+-spec get_chars(Prompt, Count) -> Data | server_no_data() when
Prompt :: prompt(),
Count :: non_neg_integer(),
Data :: [unicode:unicode_char()] | unicode:unicode_binary().
@@ -132,25 +132,23 @@ rows(Io) ->
get_chars(Prompt, N) ->
get_chars(default_input(), Prompt, N).
--spec get_chars(IoDevice, Prompt, Count) -> Data | 'eof' | {error, Reason} when
+-spec get_chars(IoDevice, Prompt, Count) -> Data | server_no_data() when
IoDevice :: device(),
Prompt :: prompt(),
Count :: non_neg_integer(),
- Reason :: term(),
Data :: [unicode:unicode_char()] | unicode:unicode_binary().
get_chars(Io, Prompt, N) when is_integer(N), N >= 0 ->
request(Io, {get_chars,unicode,Prompt,N}).
--spec get_line(Prompt) -> Data | 'eof' | {'error', Reason} when
+-spec get_line(Prompt) -> Data | server_no_data() when
Prompt :: prompt(),
- Reason :: term(),
Data :: [unicode:unicode_char()] | unicode:unicode_binary().
get_line(Prompt) ->
get_line(default_input(), Prompt).
--spec get_line(IoDevice, Prompt) -> Data | 'eof' | {'error', term()} when
+-spec get_line(IoDevice, Prompt) -> Data | server_no_data() when
IoDevice :: device(),
Prompt :: prompt(),
Data :: [unicode:unicode_char()] | unicode:unicode_binary().
@@ -219,8 +217,9 @@ write(Io, Term) ->
-spec read(Prompt) -> Result when
Prompt :: prompt(),
Result :: {'ok', Term :: term()}
- | 'eof'
- | {'error', ErrorInfo :: erl_scan:error_info()}.
+ | server_no_data()
+ | {'error', ErrorInfo},
+ ErrorInfo :: erl_scan:error_info() | erl_parse:error_info().
% Read does not use get_until as erl_scan does not work with unicode
% XXX:PaN fixme?
@@ -231,8 +230,9 @@ read(Prompt) ->
IoDevice :: device(),
Prompt :: prompt(),
Result :: {'ok', Term :: term()}
- | 'eof'
- | {'error', ErrorInfo :: erl_scan:error_info()}.
+ | server_no_data()
+ | {'error', ErrorInfo},
+ ErrorInfo :: erl_scan:error_info() | erl_parse:error_info().
read(Io, Prompt) ->
case request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[1]}) of
@@ -248,24 +248,41 @@ read(Io, Prompt) ->
Other
end.
--spec read(IoDevice, Prompt, StartLine) -> Result when
+-spec read(IoDevice, Prompt, StartLocation) -> Result when
+ IoDevice :: device(),
+ Prompt :: prompt(),
+ StartLocation :: location(),
+ Result :: {'ok', Term :: term(), EndLocation :: location()}
+ | {'eof', EndLocation :: location()}
+ | server_no_data()
+ | {'error', ErrorInfo, ErrorLocation :: location()},
+ ErrorInfo :: erl_scan:error_info() | erl_parse:error_info().
+
+read(Io, Prompt, Pos0) ->
+ read(Io, Prompt, Pos0, []).
+
+-spec read(IoDevice, Prompt, StartLocation, Options) -> Result when
IoDevice :: device(),
Prompt :: prompt(),
- StartLine :: line(),
- Result :: {'ok', Term :: term(), EndLine :: line()}
- | {'eof', EndLine :: line()}
- | {'error', ErrorInfo :: erl_scan:error_info(), ErrorLine :: line()}.
-
-read(Io, Prompt, StartLine) when is_integer(StartLine) ->
- case request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[StartLine]}) of
- {ok,Toks,EndLine} ->
+ StartLocation :: location(),
+ Options :: erl_scan:options(),
+ Result :: {'ok', Term :: term(), EndLocation :: location()}
+ | {'eof', EndLocation :: location()}
+ | server_no_data()
+ | {'error', ErrorInfo, ErrorLocation :: location()},
+ ErrorInfo :: erl_scan:error_info() | erl_parse:error_info().
+
+read(Io, Prompt, Pos0, Options) ->
+ Args = [Pos0,Options],
+ case request(Io, {get_until,unicode,Prompt,erl_scan,tokens,Args}) of
+ {ok,Toks,EndLocation} ->
case erl_parse:parse_term(Toks) of
- {ok,Term} -> {ok,Term,EndLine};
- {error,ErrorInfo} -> {error,ErrorInfo,EndLine}
+ {ok,Term} -> {ok,Term,EndLocation};
+ {error,ErrorInfo} -> {error,ErrorInfo,EndLocation}
end;
- {error,_E,_EndLine} = Error ->
+ {error,_E,_EndLocation} = Error ->
Error;
- {eof,_EndLine} = Eof ->
+ {eof,_EndLocation} = Eof ->
Eof;
Other ->
Other
@@ -313,7 +330,9 @@ fread(Prompt, Format) ->
IoDevice :: device(),
Prompt :: prompt(),
Format :: format(),
- Result :: {'ok', Terms :: [term()]} | 'eof' | {'error', What :: term()}.
+ Result :: {'ok', Terms :: [term()]}
+ | {'error', FreadError :: io_lib:fread_error()}
+ | server_no_data().
fread(Io, Prompt, Format) ->
case request(Io, {fread,Prompt,Format}) of
@@ -348,7 +367,7 @@ format(Io, Format, Args) ->
-spec scan_erl_exprs(Prompt) -> Result when
Prompt :: prompt(),
- Result :: erl_scan:tokens_result() | request_error().
+ Result :: erl_scan:tokens_result() | server_no_data().
scan_erl_exprs(Prompt) ->
scan_erl_exprs(default_input(), Prompt, 1).
@@ -356,23 +375,33 @@ scan_erl_exprs(Prompt) ->
-spec scan_erl_exprs(Device, Prompt) -> Result when
Device :: device(),
Prompt :: prompt(),
- Result :: erl_scan:tokens_result() | request_error().
+ Result :: erl_scan:tokens_result() | server_no_data().
scan_erl_exprs(Io, Prompt) ->
scan_erl_exprs(Io, Prompt, 1).
--spec scan_erl_exprs(Device, Prompt, StartLine) -> Result when
+-spec scan_erl_exprs(Device, Prompt, StartLocation) -> Result when
Device :: device(),
Prompt :: prompt(),
- StartLine :: line(),
- Result :: erl_scan:tokens_result() | request_error().
+ StartLocation :: location(),
+ Result :: erl_scan:tokens_result() | server_no_data().
scan_erl_exprs(Io, Prompt, Pos0) ->
- request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[Pos0]}).
+ scan_erl_exprs(Io, Prompt, Pos0, []).
+
+-spec scan_erl_exprs(Device, Prompt, StartLocation, Options) -> Result when
+ Device :: device(),
+ Prompt :: prompt(),
+ StartLocation :: location(),
+ Options :: erl_scan:options(),
+ Result :: erl_scan:tokens_result() | server_no_data().
+
+scan_erl_exprs(Io, Prompt, Pos0, Options) ->
+ request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[Pos0,Options]}).
-spec scan_erl_form(Prompt) -> Result when
Prompt :: prompt(),
- Result :: erl_scan:tokens_result() | request_error().
+ Result :: erl_scan:tokens_result() | server_no_data().
scan_erl_form(Prompt) ->
scan_erl_form(default_input(), Prompt, 1).
@@ -380,26 +409,41 @@ scan_erl_form(Prompt) ->
-spec scan_erl_form(IoDevice, Prompt) -> Result when
IoDevice :: device(),
Prompt :: prompt(),
- Result :: erl_scan:tokens_result() | request_error().
+ Result :: erl_scan:tokens_result() | server_no_data().
scan_erl_form(Io, Prompt) ->
scan_erl_form(Io, Prompt, 1).
--spec scan_erl_form(IoDevice, Prompt, StartLine) -> Result when
+-spec scan_erl_form(IoDevice, Prompt, StartLocation) -> Result when
IoDevice :: device(),
Prompt :: prompt(),
- StartLine :: line(),
- Result :: erl_scan:tokens_result() | request_error().
+ StartLocation :: location(),
+ Result :: erl_scan:tokens_result() | server_no_data().
scan_erl_form(Io, Prompt, Pos0) ->
- request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[Pos0]}).
+ scan_erl_form(Io, Prompt, Pos0, []).
+
+-spec scan_erl_form(IoDevice, Prompt, StartLocation, Options) -> Result when
+ IoDevice :: device(),
+ Prompt :: prompt(),
+ StartLocation :: location(),
+ Options :: erl_scan:options(),
+ Result :: erl_scan:tokens_result() | server_no_data().
+
+scan_erl_form(Io, Prompt, Pos0, Options) ->
+ request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[Pos0,Options]}).
%% Parsing Erlang code.
--type parse_ret() :: {'ok', ExprList :: erl_parse:abstract_expr(), EndLine :: line()}
- | {'eof', EndLine :: line()}
- | {'error', ErrorInfo :: erl_scan:error_info(), ErrorLine :: line()}
- | request_error().
+-type parse_ret() :: {'ok',
+ ExprList :: erl_parse:abstract_expr(),
+ EndLocation :: location()}
+ | {'eof', EndLocation :: location()}
+ | {'error',
+ ErrorInfo :: erl_scan:error_info()
+ | erl_parse:error_info(),
+ ErrorLocation :: location()}
+ | server_no_data().
-spec parse_erl_exprs(Prompt) -> Result when
Prompt :: prompt(),
@@ -416,14 +460,24 @@ parse_erl_exprs(Prompt) ->
parse_erl_exprs(Io, Prompt) ->
parse_erl_exprs(Io, Prompt, 1).
--spec parse_erl_exprs(IoDevice, Prompt, StartLine) -> Result when
+-spec parse_erl_exprs(IoDevice, Prompt, StartLocation) -> Result when
IoDevice :: device(),
Prompt :: prompt(),
- StartLine :: line(),
+ StartLocation :: location(),
Result :: parse_ret().
parse_erl_exprs(Io, Prompt, Pos0) ->
- case request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[Pos0]}) of
+ parse_erl_exprs(Io, Prompt, Pos0, []).
+
+-spec parse_erl_exprs(IoDevice, Prompt, StartLocation, Options) -> Result when
+ IoDevice :: device(),
+ Prompt :: prompt(),
+ StartLocation :: location(),
+ Options :: erl_scan:options(),
+ Result :: parse_ret().
+
+parse_erl_exprs(Io, Prompt, Pos0, Options) ->
+ case request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[Pos0,Options]}) of
{ok,Toks,EndPos} ->
case erl_parse:parse_exprs(Toks) of
{ok,Exprs} -> {ok,Exprs,EndPos};
@@ -433,10 +487,15 @@ parse_erl_exprs(Io, Prompt, Pos0) ->
Other
end.
--type parse_form_ret() :: {'ok', AbsForm :: erl_parse:abstract_form(), EndLine :: line()}
- | {'eof', EndLine :: line()}
- | {'error', ErrorInfo :: erl_scan:error_info(), ErrorLine :: line()}
- | request_error().
+-type parse_form_ret() :: {'ok',
+ AbsForm :: erl_parse:abstract_form(),
+ EndLocation :: location()}
+ | {'eof', EndLocation :: location()}
+ | {'error',
+ ErrorInfo :: erl_scan:error_info()
+ | erl_parse:error_info(),
+ ErrorLocation :: location()}
+ | server_no_data().
-spec parse_erl_form(Prompt) -> Result when
Prompt :: prompt(),
@@ -453,14 +512,25 @@ parse_erl_form(Prompt) ->
parse_erl_form(Io, Prompt) ->
parse_erl_form(Io, Prompt, 1).
--spec parse_erl_form(IoDevice, Prompt, StartLine) -> Result when
+-spec parse_erl_form(IoDevice, Prompt, StartLocation) -> Result when
IoDevice :: device(),
Prompt :: prompt(),
- StartLine :: line(),
+ StartLocation :: location(),
Result :: parse_form_ret().
parse_erl_form(Io, Prompt, Pos0) ->
- case request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[Pos0]}) of
+ parse_erl_form(Io, Prompt, Pos0, []).
+
+-spec parse_erl_form(IoDevice, Prompt, StartLocation, Options) -> Result when
+ IoDevice :: device(),
+ Prompt :: prompt(),
+ StartLocation :: location(),
+ Options :: erl_scan:options(),
+ Result :: parse_form_ret().
+
+parse_erl_form(Io, Prompt, Pos0, Options) ->
+ Args = [Pos0, Options],
+ case request(Io, {get_until,unicode,Prompt,erl_scan,tokens,Args}) of
{ok,Toks,EndPos} ->
case erl_parse:parse_form(Toks) of
{ok,Exprs} -> {ok,Exprs,EndPos};
diff --git a/lib/stdlib/src/io_lib.erl b/lib/stdlib/src/io_lib.erl
index 0252cdf742..5ad505f683 100644
--- a/lib/stdlib/src/io_lib.erl
+++ b/lib/stdlib/src/io_lib.erl
@@ -1,7 +1,8 @@
+%% -*- coding: utf-8 -*-
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -46,25 +47,28 @@
%% 173 - 176 { - ~ punctuation
%% 177 DEL control
%% 200 - 237 control
-%% 240 - 277 NBSP - � punctuation
-%% 300 - 326 � - � uppercase
-%% 327 � punctuation
-%% 330 - 336 � - � uppercase
-%% 337 - 366 � - � lowercase
-%% 367 � punctuation
-%% 370 - 377 � - � lowercase
+%% 240 - 277 NBSP - ¿ punctuation
+%% 300 - 326 À - Ö uppercase
+%% 327 × punctuation
+%% 330 - 336 Ø - Þ uppercase
+%% 337 - 366 ß - ö lowercase
+%% 367 ÷ punctuation
+%% 370 - 377 ø - ÿ lowercase
%%
%% Many punctuation characters region have special meaning. Must
-%% watch using � \327, very close to x \170
+%% watch using × \327, very close to x \170
-module(io_lib).
-export([fwrite/2,fread/2,fread/3,format/2]).
-export([print/1,print/4,indentation/2]).
--export([write/1,write/2,write/3,nl/0,format_prompt/1]).
+-export([write/1,write/2,write/3,nl/0,format_prompt/1,format_prompt/2]).
-export([write_atom/1,write_string/1,write_string/2,write_unicode_string/1,
- write_unicode_string/2, write_char/1, write_unicode_char/1]).
+ write_unicode_string/2, write_char/1, write_unicode_char/1]).
+
+-export([write_unicode_string_as_latin1/1, write_unicode_string_as_latin1/2,
+ write_unicode_char_as_latin1/1]).
-export([quote_atom/2, char_list/1, unicode_char_list/1,
deep_char_list/1, deep_unicode_char_list/1,
@@ -75,14 +79,30 @@
collect_line/2, collect_line/3, collect_line/4,
get_until/3, get_until/4]).
--export_type([chars/0, continuation/0]).
+-export_type([chars/0, unicode_chars/0, unicode_string/0, continuation/0,
+ fread_error/0]).
%%----------------------------------------------------------------------
-type chars() :: [char() | chars()].
+-type unicode_chars() :: [unicode:unicode_char() | unicode_chars()].
+-type unicode_string() :: [unicode:unicode_char()].
-type depth() :: -1 | non_neg_integer().
--opaque continuation() :: {_, _, _, _}. % XXX: refine
+-opaque continuation() :: {Format :: string(),
+ Stack :: chars(),
+ Nchars :: non_neg_integer(),
+ Results :: [term()]}.
+
+-type fread_error() :: 'atom'
+ | 'based'
+ | 'character'
+ | 'float'
+ | 'format'
+ | 'input'
+ | 'integer'
+ | 'string'
+ | 'unsigned'.
%%----------------------------------------------------------------------
@@ -104,7 +124,7 @@ fwrite(Format, Args) ->
| {'more', RestFormat :: string(),
Nchars :: non_neg_integer(),
InputStack :: chars()}
- | {'error', What :: term()}.
+ | {'error', What :: fread_error()}.
fread(Chars, Format) ->
io_lib_fread:fread(Chars, Format).
@@ -117,7 +137,7 @@ fread(Chars, Format) ->
| {'done', Result, LeftOverChars :: string()},
Result :: {'ok', InputList :: [term()]}
| 'eof'
- | {'error', What :: term()}.
+ | {'error', What :: fread_error()}.
fread(Cont, Chars, Format) ->
io_lib_fread:fread(Cont, Chars, Format).
@@ -159,27 +179,34 @@ indentation(Chars, Current) ->
%% Format an IO-request prompt (handles formatting errors safely).
-%% Atoms, binaries, and iolists can be used as-is, and will be
-%% printed without any additional quotes.
-%% Note that the output is a deep string, and not an iolist (i.e.,
-%% it may be deep, but never contains binaries, due to the "~s").
+%% Atoms, binaries, and iolists (or unicode:charlist()) can be used
+%% as-is, and will be printed without any additional quotes.
-spec format_prompt(term()) -> chars().
-format_prompt({format,Format,Args}) ->
- format_prompt(Format,Args);
-format_prompt(Prompt)
- when is_list(Prompt); is_atom(Prompt); is_binary(Prompt) ->
- format_prompt("~ts", [Prompt]);
format_prompt(Prompt) ->
- format_prompt("~tp", [Prompt]).
+ format_prompt(Prompt, latin1).
-format_prompt(Format, Args) ->
+-spec format_prompt(term(), atom()) -> chars().
+
+format_prompt({format,Format,Args}, _Encoding) ->
+ do_format_prompt(Format, Args);
+format_prompt(Prompt, Encoding)
+ when is_list(Prompt); is_atom(Prompt); is_binary(Prompt) ->
+ do_format_prompt(add_modifier(Encoding, "s"), [Prompt]);
+format_prompt(Prompt, Encoding) ->
+ do_format_prompt(add_modifier(Encoding, "p"), [Prompt]).
+
+do_format_prompt(Format, Args) ->
case catch io_lib:format(Format, Args) of
{'EXIT',_} -> "???";
List -> List
end.
+add_modifier(latin1, C) ->
+ "~"++C;
+add_modifier(_, C) ->
+ "~t"++C.
%% write(Term)
%% write(Term, Depth)
@@ -250,10 +277,10 @@ write_ref(Ref) ->
write_binary(B, D) when is_integer(D) ->
[$<,$<,write_binary_body(B, D),$>,$>].
-write_binary_body(_B, 1) ->
- "...";
write_binary_body(<<>>, _D) ->
"";
+write_binary_body(_B, 1) ->
+ "...";
write_binary_body(<<X:8>>, _D) ->
[integer_to_list(X)];
write_binary_body(<<X:8,Rest/bitstring>>, D) ->
@@ -291,7 +318,7 @@ quote_atom(Atom, Cs0) ->
case Cs0 of
[C|Cs] when C >= $a, C =< $z ->
not name_chars(Cs);
- [C|Cs] when C >= $�, C =< $�, C =/= $� ->
+ [C|Cs] when C >= $ß, C =< $ÿ, C =/= $÷ ->
not name_chars(Cs);
_ -> true
end
@@ -305,9 +332,9 @@ name_chars([C|Cs]) ->
name_chars([]) -> true.
name_char(C) when C >= $a, C =< $z -> true;
-name_char(C) when C >= $�, C =< $�, C =/= $� -> true;
+name_char(C) when C >= $ß, C =< $ÿ, C =/= $÷ -> true;
name_char(C) when C >= $A, C =< $Z -> true;
-name_char(C) when C >= $�, C =< $�, C =/= $� -> true;
+name_char(C) when C >= $À, C =< $Þ, C =/= $× -> true;
name_char(C) when C >= $0, C =< $9 -> true;
name_char($_) -> true;
name_char($@) -> true;
@@ -327,11 +354,32 @@ write_string(S) ->
write_string(S, Q) ->
[Q|write_string1(latin1, S, Q)].
+%%% There are two functions to write Unicode strings:
+%%% - they both escape control characters < 160;
+%%% - write_unicode_string() never escapes characters >= 160;
+%%% - write_unicode_string_as_latin1() also escapes characters >= 255.
+
+-spec write_unicode_string(UnicodeString) -> unicode_string() when
+ UnicodeString :: unicode_string().
+
write_unicode_string(S) ->
write_unicode_string(S, $"). %"
+-spec write_unicode_string(unicode_string(), char()) -> unicode_string().
+
write_unicode_string(S, Q) ->
- [Q|write_string1(unicode, S, Q)].
+ [Q|write_string1(unicode_as_unicode, S, Q)].
+
+-spec write_unicode_string_as_latin1(UnicodeString) -> string() when
+ UnicodeString :: unicode_string().
+
+write_unicode_string_as_latin1(S) ->
+ write_unicode_string_as_latin1(S, $"). %"
+
+-spec write_unicode_string_as_latin1(unicode_string(), char()) -> string().
+
+write_unicode_string_as_latin1(S, Q) ->
+ [Q|write_string1(unicode_as_latin1, S, Q)].
write_string1(_,[], Q) ->
[Q];
@@ -344,7 +392,11 @@ string_char(_,C, _, Tail) when C >= $\s, C =< $~ ->
[C|Tail];
string_char(latin1,C, _, Tail) when C >= $\240, C =< $\377 ->
[C|Tail];
-string_char(unicode,C, _, Tail) when C >= $\240 ->
+string_char(unicode_as_unicode,C, _, Tail) when C >= $\240 ->
+ [C|Tail];
+string_char(unicode_as_latin1,C, _, Tail) when C >= $\240, C =< $\377 ->
+ [C|Tail];
+string_char(unicode_as_latin1,C, _, Tail) when C >= $\377 ->
"\\x{"++erlang:integer_to_list(C, 16)++"}"++Tail;
string_char(_,$\n, _, Tail) -> [$\\,$n|Tail]; %\n = LF
string_char(_,$\r, _, Tail) -> [$\\,$r|Tail]; %\r = CR
@@ -371,10 +423,22 @@ write_char($\s) -> "$\\s"; %Must special case this.
write_char(C) when is_integer(C), C >= $\000, C =< $\377 ->
[$$|string_char(latin1,C, -1, [])].
-write_unicode_char(Ch) when Ch =< 255 ->
- write_char(Ch);
-write_unicode_char(Uni) ->
- [$$|string_char(unicode,Uni, -1, [])].
+%%% There are two functions to write a Unicode character:
+%%% - they both escape control characters < 160;
+%%% - write_unicode_char() never escapes characters >= 160;
+%%% - write_unicode_char_as_latin1() also escapes characters >= 255.
+
+-spec write_unicode_char(UnicodeChar) -> unicode_string() when
+ UnicodeChar :: unicode:unicode_char().
+
+write_unicode_char(Uni) when is_integer(Uni), Uni >= $\000 ->
+ [$$|string_char(unicode_as_unicode,Uni, -1, [])].
+
+-spec write_unicode_char_as_latin1(UnicodeChar) -> string() when
+ UnicodeChar :: unicode:unicode_char().
+
+write_unicode_char_as_latin1(Uni) when is_integer(Uni), Uni >= $\000 ->
+ [$$|string_char(unicode_as_latin1,Uni, -1, [])].
%% char_list(CharList)
%% deep_char_list(CharList)
@@ -389,7 +453,8 @@ char_list([C|Cs]) when is_integer(C), C >= $\000, C =< $\377 ->
char_list([]) -> true;
char_list(_) -> false. %Everything else is false
--spec unicode_char_list(term()) -> boolean().
+-spec unicode_char_list(Term) -> boolean() when
+ Term :: term().
unicode_char_list([C|Cs]) when is_integer(C), C >= 0, C < 16#D800;
is_integer(C), C > 16#DFFF, C < 16#FFFE;
@@ -414,7 +479,8 @@ deep_char_list([], []) -> true;
deep_char_list(_, _More) -> %Everything else is false
false.
--spec deep_unicode_char_list(term()) -> boolean().
+-spec deep_unicode_char_list(Term) -> boolean() when
+ Term :: term().
deep_unicode_char_list(Cs) ->
deep_unicode_char_list(Cs, []).
@@ -459,7 +525,8 @@ printable_list(_) -> false. %Everything else is false
%% Everything that is not a control character and not invalid unicode
%% will be considered printable.
--spec printable_unicode_list(term()) -> boolean().
+-spec printable_unicode_list(Term) -> boolean() when
+ Term :: term().
printable_unicode_list([C|Cs]) when is_integer(C), C >= $\040, C =< $\176 ->
printable_unicode_list(Cs);
diff --git a/lib/stdlib/src/io_lib_format.erl b/lib/stdlib/src/io_lib_format.erl
index 49a00a4ec7..5680f83ab6 100644
--- a/lib/stdlib/src/io_lib_format.erl
+++ b/lib/stdlib/src/io_lib_format.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -22,7 +22,7 @@
-export([fwrite/2,fwrite_g/1,indentation/2]).
-%% fwrite(Format, ArgList) -> [Char].
+%% fwrite(Format, ArgList) -> [unicode:unicode:char()].
%% Format the arguments in ArgList after string Format. Just generate
%% an error if there is an error in the arguments.
%%
@@ -133,7 +133,7 @@ pcount([{$P,_As,_F,_Ad,_P,_Pad,_Enc}|Cs], Acc) -> pcount(Cs, Acc+1);
pcount([_|Cs], Acc) -> pcount(Cs, Acc);
pcount([], Acc) -> Acc.
-%% build([Control], Pc, Indentation) -> [Char].
+%% build([Control], Pc, Indentation) -> [unicode:unicode_char()].
%% Interpret the control structures. Count the number of print
%% remaining and only calculate indentation when necessary. Must also
%% be smart when calculating indentation for characters in format.
@@ -154,7 +154,7 @@ decr_pc($p, Pc) -> Pc - 1;
decr_pc($P, Pc) -> Pc - 1;
decr_pc(_, Pc) -> Pc.
-%% indentation([Char], Indentation) -> Indentation.
+%% indentation([unicode:unicode_char()], Indentation) -> Indentation.
%% Calculate the indentation of the end of a string given its start
%% indentation. We assume tabs at 8 cols.
@@ -167,19 +167,19 @@ indentation([C|Cs], I) ->
indentation([], I) -> I.
%% control(FormatChar, [Argument], FieldWidth, Adjust, Precision, PadChar,
-%% Indentation) ->
-%% [Char]
+%% Encoding, Indentation) ->
+%% [unicode:unicode_char()]
%% This is the main dispatch function for the various formatting commands.
%% Field widths and precisions have already been calculated.
control($w, [A], F, Adj, P, Pad, _Enc,_I) ->
term(io_lib:write(A, -1), F, Adj, P, Pad);
-control($p, [A], F, Adj, P, Pad, _Enc, I) ->
- print(A, -1, F, Adj, P, Pad, I);
+control($p, [A], F, Adj, P, Pad, Enc, I) ->
+ print(A, -1, F, Adj, P, Pad, Enc, I);
control($W, [A,Depth], F, Adj, P, Pad, _Enc, _I) when is_integer(Depth) ->
term(io_lib:write(A, Depth), F, Adj, P, Pad);
-control($P, [A,Depth], F, Adj, P, Pad, _Enc, I) when is_integer(Depth) ->
- print(A, Depth, F, Adj, P, Pad, I);
+control($P, [A,Depth], F, Adj, P, Pad, Enc, I) when is_integer(Depth) ->
+ print(A, Depth, F, Adj, P, Pad, Enc, I);
control($s, [A], F, Adj, P, Pad, _Enc, _I) when is_atom(A) ->
string(atom_to_list(A), F, Adj, P, Pad);
control($s, [L0], F, Adj, P, Pad, latin1, _I) ->
@@ -187,6 +187,7 @@ control($s, [L0], F, Adj, P, Pad, latin1, _I) ->
string(L, F, Adj, P, Pad);
control($s, [L0], F, Adj, P, Pad, unicode, _I) ->
L = unicode:characters_to_list(L0),
+ true = is_list(L),
uniconv(string(L, F, Adj, P, Pad));
control($e, [A], F, Adj, P, Pad, _Enc, _I) when is_float(A) ->
fwrite_e(A, F, Adj, P, Pad);
@@ -256,13 +257,17 @@ term(T, F, Adj, P0, Pad) ->
adjust(T, chars(Pad, F-L), Adj)
end.
-%% print(Term, Depth, Field, Adjust, Precision, PadChar, Indentation)
+%% print(Term, Depth, Field, Adjust, Precision, PadChar, Encoding,
+%% Indentation)
%% Print a term.
-print(T, D, none, Adj, P, Pad, I) -> print(T, D, 80, Adj, P, Pad, I);
-print(T, D, F, Adj, none, Pad, I) -> print(T, D, F, Adj, I+1, Pad, I);
-print(T, D, F, right, P, _Pad, _I) ->
- io_lib_pretty:print(T, P, F, D).
+print(T, D, none, Adj, P, Pad, E, I) -> print(T, D, 80, Adj, P, Pad, E, I);
+print(T, D, F, Adj, none, Pad, E, I) -> print(T, D, F, Adj, I+1, Pad, E, I);
+print(T, D, F, right, P, _Pad, latin1, _I) ->
+ io_lib_pretty:print(T, P, F, D);
+print(T, D, F, right, P, _Pad, Enc, _I) ->
+ Options = [{column, P}, {line_length, F}, {depth, D}, {encoding, Enc}],
+ io_lib_pretty:print(T, Options).
%% fwrite_e(Float, Field, Adjust, Precision, PadChar)
@@ -608,7 +613,7 @@ prefixed_integer(Int, F, Adj, Base, Pad, Prefix, Lowercase)
term([Prefix|S], F, Adj, none, Pad)
end.
-%% char(Char, Field, Adjust, Precision, PadChar) -> [Char].
+%% char(Char, Field, Adjust, Precision, PadChar) -> [unicode:unicode_char()].
char(C, none, _Adj, none, _Pad) -> [C];
char(C, F, _Adj, none, _Pad) -> chars(C, F);
diff --git a/lib/stdlib/src/io_lib_fread.erl b/lib/stdlib/src/io_lib_fread.erl
index ded1346097..84d4b8bba0 100644
--- a/lib/stdlib/src/io_lib_fread.erl
+++ b/lib/stdlib/src/io_lib_fread.erl
@@ -43,7 +43,7 @@
| {'done', Result, LeftOverChars :: string()},
Result :: {'ok', InputList :: io_lib:chars()}
| 'eof'
- | {'error', What :: term()}.
+ | {'error', What :: io_lib:fread_error()}.
fread([], Chars, Format) ->
%%io:format("FREAD: ~w `~s'~n", [Format,Chars]),
diff --git a/lib/stdlib/src/io_lib_pretty.erl b/lib/stdlib/src/io_lib_pretty.erl
index 169410796b..99ad281a9b 100644
--- a/lib/stdlib/src/io_lib_pretty.erl
+++ b/lib/stdlib/src/io_lib_pretty.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -33,43 +33,76 @@
%% print(Term, Column, LineLength, Depth) -> [Chars]
%% Depth = -1 gives unlimited print depth. Use io_lib:write for atomic terms.
+-spec print(term()) -> io_lib:chars().
+
print(Term) ->
print(Term, 1, 80, -1).
%% print(Term, RecDefFun) -> [Chars]
%% print(Term, Depth, RecDefFun) -> [Chars]
%% RecDefFun = fun(Tag, NoFields) -> [FieldTag] | no
-%% Used by the shell for printing records.
+%% Used by the shell for printing records and for Unicode.
+
+-type rec_print_fun() :: fun((Tag :: atom(), NFields :: non_neg_integer()) ->
+ no | [FieldName :: atom()]).
+-type column() :: integer().
+-type line_length() :: pos_integer().
+-type depth() :: integer().
+-type max_chars() :: integer().
+
+-type chars() :: io_lib:chars().
+-type unicode_chars() :: io_lib:unicode_chars().
+-type option() :: {column, column()}
+ | {line_length, line_length()}
+ | {depth, depth()}
+ | {max_chars, max_chars()}
+ | {record_print_fun, rec_print_fun()}
+ | {encoding, latin1 | utf8 | unicode}.
+-type options() :: [option()].
+
+-spec print(term(), rec_print_fun()) -> chars() | unicode_chars();
+ (term(), options()) -> chars() | unicode_chars().
+
+print(Term, Options) when is_list(Options) ->
+ Col = proplists:get_value(column, Options, 1),
+ Ll = proplists:get_value(line_length, Options, 80),
+ D = proplists:get_value(depth, Options, -1),
+ M = proplists:get_value(max_chars, Options, -1),
+ RecDefFun = proplists:get_value(record_print_fun, Options, no_fun),
+ Encoding = proplists:get_value(encoding, Options, epp:default_encoding()),
+ print(Term, Col, Ll, D, M, RecDefFun, Encoding);
print(Term, RecDefFun) ->
print(Term, -1, RecDefFun).
+-spec print(term(), depth(), rec_print_fun()) -> chars() | unicode_chars().
+
print(Term, Depth, RecDefFun) ->
print(Term, 1, 80, Depth, RecDefFun).
+-spec print(term(), column(), line_length(), depth()) ->
+ chars() | unicode_chars().
+
print(Term, Col, Ll, D) ->
- print(Term, Col, Ll, D, _M=-1, no_fun).
+ print(Term, Col, Ll, D, _M=-1, no_fun, latin1).
+-spec print(term(), column(), line_length(), depth(), rec_print_fun()) ->
+ chars() | unicode_chars().
print(Term, Col, Ll, D, RecDefFun) ->
print(Term, Col, Ll, D, _M=-1, RecDefFun).
-print(_, _, _, 0, _M, _RF) -> "...";
-print(Term, Col, Ll, D, M, RecDefFun) when Col =< 0 ->
- print(Term, 1, Ll, D, M, RecDefFun);
-print(Term, Col, Ll, D, M0, RecDefFun) when is_tuple(Term);
- is_list(Term) ->
- If = {_S, Len} = print_length(Term, D, RecDefFun),
- M = max_cs(M0, Len),
- if
- Len < Ll - Col, Len =< M ->
- write(If);
- true ->
- TInd = while_fail([-1, 4],
- fun(I) -> cind(If, Col, Ll, M, I, 0, 0) end,
- 1),
- pp(If, Col, Ll, M, TInd, indent(Col), 0, 0)
- end;
-print(<<_/bitstring>>=Term, Col, Ll, D, M0, RecDefFun) ->
- If = {_S, Len} = print_length(Term, D, RecDefFun),
+-spec print(term(), column(), line_length(), depth(), max_chars(),
+ rec_print_fun()) -> chars() | unicode_chars().
+
+print(Term, Col, Ll, D, M, RecDefFun) ->
+ print(Term, Col, Ll, D, M, RecDefFun, latin1).
+
+print(_, _, _, 0, _M, _RF, _Enc) -> "...";
+print(Term, Col, Ll, D, M, RecDefFun, Enc) when Col =< 0 ->
+ print(Term, 1, Ll, D, M, RecDefFun, Enc);
+print(Term, Col, Ll, D, M0, RecDefFun, Enc) when is_tuple(Term);
+ is_list(Term);
+ is_bitstring(Term) ->
+ If = {_S, Len} = print_length(Term, D, RecDefFun, Enc),
M = max_cs(M0, Len),
if
Len < Ll - Col, Len =< M ->
@@ -80,7 +113,7 @@ print(<<_/bitstring>>=Term, Col, Ll, D, M0, RecDefFun) ->
1),
pp(If, Col, Ll, M, TInd, indent(Col), 0, 0)
end;
-print(Term, _Col, _Ll, _D, _M, _RF) ->
+print(Term, _Col, _Ll, _D, _M, _RF, _Enc) ->
io_lib:write(Term).
%%%
@@ -294,50 +327,56 @@ write_tail(E, S) ->
%% counted but need to be added later.
%% D =/= 0
-print_length([], _D, _RF) ->
+print_length([], _D, _RF, _Enc) ->
{"[]", 2};
-print_length({}, _D, _RF) ->
+print_length({}, _D, _RF, _Enc) ->
{"{}", 2};
-print_length(List, D, RF) when is_list(List) ->
- case printable_list(List, D) of
+print_length(List, D, RF, Enc) when is_list(List) ->
+ case printable_list(List, D, Enc) of
true ->
- S = io_lib:write_string(List, $"), %"
+ S = write_string(List, Enc),
{S, length(S)};
%% Truncated lists could break some existing code.
% {true, Prefix} ->
- % S = io_lib:write_string(Prefix, $"), %"
+ % S = write_string(Prefix, Enc),
% {[S | "..."], 3 + length(S)};
false ->
- print_length_list(List, D, RF)
+ print_length_list(List, D, RF, Enc)
end;
-print_length(Fun, _D, _RF) when is_function(Fun) ->
+print_length(Fun, _D, _RF, _Enc) when is_function(Fun) ->
S = io_lib:write(Fun),
{S, iolist_size(S)};
-print_length(R, D, RF) when is_atom(element(1, R)),
- is_function(RF) ->
+print_length(R, D, RF, Enc) when is_atom(element(1, R)),
+ is_function(RF) ->
case RF(element(1, R), tuple_size(R) - 1) of
no ->
- print_length_tuple(R, D, RF);
+ print_length_tuple(R, D, RF, Enc);
RDefs ->
- print_length_record(R, D, RF, RDefs)
+ print_length_record(R, D, RF, RDefs, Enc)
end;
-print_length(Tuple, D, RF) when is_tuple(Tuple) ->
- print_length_tuple(Tuple, D, RF);
-print_length(<<>>, _D, _RF) ->
+print_length(Tuple, D, RF, Enc) when is_tuple(Tuple) ->
+ print_length_tuple(Tuple, D, RF, Enc);
+print_length(<<>>, _D, _RF, _Enc) ->
{"<<>>", 4};
-print_length(<<_/bitstring>>, 1, _RF) ->
+print_length(<<_/bitstring>>, 1, _RF, _Enc) ->
{"<<...>>", 7};
-print_length(<<_/bitstring>>=Bin, D, _RF) ->
+print_length(<<_/bitstring>>=Bin, D, _RF, Enc) ->
case bit_size(Bin) rem 8 of
0 ->
D1 = D - 1,
- case printable_bin(Bin, D1) of
- List when is_list(List) ->
- S = io_lib:write_string(List, $"),
+ case printable_bin(Bin, D1, Enc) of
+ {true, List} when is_list(List) ->
+ S = io_lib:write_string(List, $"), %"
{[$<,$<,S,$>,$>], 4 + length(S)};
- {true, Prefix} ->
- S = io_lib:write_string(Prefix, $"),
- {[$<,$<, S | "...>>"], 4 + length(S)};
+ {false, List} when is_list(List) ->
+ S = io_lib:write_unicode_string(List, $"), %"
+ {[$<,$<,S,"/utf8>>"], 9 + length(S)};
+ {true, true, Prefix} ->
+ S = io_lib:write_string(Prefix, $"), %"
+ {[$<,$<, S | "...>>"], 7 + length(S)};
+ {false, true, Prefix} ->
+ S = io_lib:write_unicode_string(Prefix, $"), %"
+ {[$<,$<, S | "/utf8...>>"], 12 + length(S)};
false ->
S = io_lib:write(Bin, D),
{{bin,S}, iolist_size(S)}
@@ -346,51 +385,51 @@ print_length(<<_/bitstring>>=Bin, D, _RF) ->
S = io_lib:write(Bin, D),
{{bin,S}, iolist_size(S)}
end;
-print_length(Term, _D, _RF) ->
+print_length(Term, _D, _RF, _Enc) ->
S = io_lib:write(Term),
{S, iolist_size(S)}.
-print_length_tuple(_Tuple, 1, _RF) ->
+print_length_tuple(_Tuple, 1, _RF, _Enc) ->
{"{...}", 5};
-print_length_tuple(Tuple, D, RF) ->
- L = print_length_list1(tuple_to_list(Tuple), D, RF),
+print_length_tuple(Tuple, D, RF, Enc) ->
+ L = print_length_list1(tuple_to_list(Tuple), D, RF, Enc),
IsTagged = is_atom(element(1, Tuple)) and (tuple_size(Tuple) > 1),
{{tuple,IsTagged,L}, list_length(L, 2)}.
-print_length_record(_Tuple, 1, _RF, _RDefs) ->
+print_length_record(_Tuple, 1, _RF, _RDefs, _Enc) ->
{"{...}", 5};
-print_length_record(Tuple, D, RF, RDefs) ->
+print_length_record(Tuple, D, RF, RDefs, Enc) ->
Name = [$# | io_lib:write_atom(element(1, Tuple))],
NameL = length(Name),
- L = print_length_fields(RDefs, D - 1, tl(tuple_to_list(Tuple)), RF),
+ L = print_length_fields(RDefs, D - 1, tl(tuple_to_list(Tuple)), RF, Enc),
{{record, [{Name,NameL} | L]}, list_length(L, NameL + 2)}.
-print_length_fields([], _D, [], _RF) ->
+print_length_fields([], _D, [], _RF, _Enc) ->
[];
-print_length_fields(_, 1, _, _RF) ->
+print_length_fields(_, 1, _, _RF, _Enc) ->
{dots, 3};
-print_length_fields([Def | Defs], D, [E | Es], RF) ->
- [print_length_field(Def, D - 1, E, RF) |
- print_length_fields(Defs, D - 1, Es, RF)].
+print_length_fields([Def | Defs], D, [E | Es], RF, Enc) ->
+ [print_length_field(Def, D - 1, E, RF, Enc) |
+ print_length_fields(Defs, D - 1, Es, RF, Enc)].
-print_length_field(Def, D, E, RF) ->
+print_length_field(Def, D, E, RF, Enc) ->
Name = io_lib:write_atom(Def),
- {S, L} = print_length(E, D, RF),
+ {S, L} = print_length(E, D, RF, Enc),
NameL = length(Name) + 3,
{{field, Name, NameL, {S, L}}, NameL + L}.
-print_length_list(List, D, RF) ->
- L = print_length_list1(List, D, RF),
+print_length_list(List, D, RF, Enc) ->
+ L = print_length_list1(List, D, RF, Enc),
{{list, L}, list_length(L, 2)}.
-print_length_list1([], _D, _RF) ->
+print_length_list1([], _D, _RF, _Enc) ->
[];
-print_length_list1(_, 1, _RF) ->
+print_length_list1(_, 1, _RF, _Enc) ->
{dots, 3};
-print_length_list1([E | Es], D, RF) ->
- [print_length(E, D - 1, RF) | print_length_list1(Es, D - 1, RF)];
-print_length_list1(E, D, RF) ->
- print_length(E, D - 1, RF).
+print_length_list1([E | Es], D, RF, Enc) ->
+ [print_length(E, D - 1, RF, Enc) | print_length_list1(Es, D - 1, RF, Enc)];
+print_length_list1(E, D, RF, Enc) ->
+ print_length(E, D - 1, RF, Enc).
list_length([], Acc) ->
Acc;
@@ -409,16 +448,16 @@ list_length_tail({_, Len}, Acc) ->
%% ?CHARS printable characters has depth 1.
-define(CHARS, 4).
-printable_list(L, D) when D < 0 ->
- io_lib:printable_list(L);
-printable_list(_L, 1) ->
+printable_list(_L, 1, _Enc) ->
false;
-printable_list(L, _D) ->
- io_lib:printable_list(L).
+printable_list(L, _D, latin1) ->
+ io_lib:printable_list(L);
+printable_list(L, _D, _Uni) ->
+ io_lib:printable_unicode_list(L).
%% Truncated lists could break some existing code.
-% printable_list(L, D) ->
+% printable_list(L, D, Enc) when D >= 0 ->
% Len = ?CHARS * (D - 1),
-% case printable_list1(L, Len) of
+% case printable_list1(L, Len, Enc) of
% all ->
% true;
% N when is_integer(N), Len - N >= D - 1 ->
@@ -428,32 +467,41 @@ printable_list(L, _D) ->
% false
% end.
-printable_bin(Bin, D) when D >= 0, ?CHARS * D =< byte_size(Bin) ->
- printable_bin(Bin, erlang:min(?CHARS * D, byte_size(Bin)), D);
-printable_bin(Bin, D) ->
- printable_bin(Bin, byte_size(Bin), D).
+printable_bin(Bin, D, Enc) when D >= 0, ?CHARS * D =< byte_size(Bin) ->
+ printable_bin(Bin, erlang:min(?CHARS * D, byte_size(Bin)), D, Enc);
+printable_bin(Bin, D, Enc) ->
+ printable_bin(Bin, byte_size(Bin), D, Enc).
-printable_bin(Bin, Len, D) ->
+printable_bin(Bin, Len, D, latin1) ->
N = erlang:min(20, Len),
L = binary_to_list(Bin, 1, N),
case printable_list1(L, N) of
all when N =:= byte_size(Bin) ->
- L;
- all when N =:= Len -> % N < byte_size(Bin)
{true, L};
+ all when N =:= Len -> % N < byte_size(Bin)
+ {true, true, L};
all ->
case printable_bin1(Bin, 1 + N, Len - N) of
0 when byte_size(Bin) =:= Len ->
- binary_to_list(Bin);
+ {true, binary_to_list(Bin)};
NC when D > 0, Len - NC >= D ->
- {true, binary_to_list(Bin, 1, Len - NC)};
+ {true, true, binary_to_list(Bin, 1, Len - NC)};
NC when is_integer(NC) ->
false
end;
NC when is_integer(NC), D > 0, N - NC >= D ->
- {true, binary_to_list(Bin, 1, N - NC)};
+ {true, true, binary_to_list(Bin, 1, N - NC)};
NC when is_integer(NC) ->
false
+ end;
+printable_bin(Bin, Len, D, _Uni) ->
+ case printable_unicode(Bin, Len, []) of
+ {_, <<>>, L} ->
+ {byte_size(Bin) =:= length(L), L};
+ {NC, Bin1, L} when D > 0, Len - NC >= D ->
+ {byte_size(Bin)-byte_size(Bin1) =:= length(L), true, L};
+ {_NC, _Bin, _L} ->
+ false
end.
printable_bin1(_Bin, _Start, 0) ->
@@ -484,6 +532,16 @@ printable_list1([$\e | Cs], N) -> printable_list1(Cs, N - 1);
printable_list1([], _) -> all;
printable_list1(_, N) -> N.
+printable_unicode(<<C/utf8, R/binary>>, I, L) when I > 0 ->
+ printable_unicode(R, I - 1, [C | L]);
+printable_unicode(Bin, I, L) ->
+ {I, Bin, lists:reverse(L)}.
+
+write_string(S, latin1) ->
+ io_lib:write_string(S, $"); %"
+write_string(S, _Uni) ->
+ io_lib:write_unicode_string(S, $"). %"
+
%% Throw 'no_good' if the indentation exceeds half the line length
%% unless there is room for M characters on the line.
diff --git a/lib/stdlib/src/lib.erl b/lib/stdlib/src/lib.erl
index cf4b87d7eb..b2ce2a5a8f 100644
--- a/lib/stdlib/src/lib.erl
+++ b/lib/stdlib/src/lib.erl
@@ -21,8 +21,9 @@
-export([flush_receive/0, error_message/2, progname/0, nonl/1, send/2,
sendw/2, eval_str/1]).
--export([format_exception/6, format_stacktrace/4,
- format_call/4, format_fun/1]).
+-export([format_exception/6, format_exception/7,
+ format_stacktrace/4, format_stacktrace/5,
+ format_call/4, format_call/5, format_fun/1]).
-spec flush_receive() -> 'ok'.
@@ -128,32 +129,49 @@ all_white(_) -> false.
%% as indentation whenever newline has been inserted);
%% Class, Reason and StackTrace are the exception;
%% FormatFun = fun(Term, I) -> iolist() formats terms;
-%% StackFun = fun(Mod, Fun, Arity) -> bool() is used for trimming the
+%% StackFun = fun(Mod, Fun, Arity) -> boolean() is used for trimming the
%% end of the stack (typically calls to erl_eval are skipped).
-format_exception(I, Class, Reason, StackTrace, StackFun, FormatFun)
+format_exception(I, Class, Reason, StackTrace, StackFun, FormatFun) ->
+ format_exception(I, Class, Reason, StackTrace, StackFun, FormatFun,
+ latin1).
+
+%% -> iolist() | unicode:charlist() (no \n at end)
+%% FormatFun = fun(Term, I) -> iolist() | unicode:charlist().
+format_exception(I, Class, Reason, StackTrace, StackFun, FormatFun, Encoding)
when is_integer(I), I >= 1, is_function(StackFun, 3),
is_function(FormatFun, 2) ->
S = n_spaces(I-1),
{Term,Trace1,Trace} = analyze_exception(Class, Reason, StackTrace),
- Expl0 = explain_reason(Term, Class, Trace1, FormatFun, S),
- Expl = io_lib:fwrite(<<"~s~s">>, [exited(Class), Expl0]),
- case format_stacktrace1(S, Trace, FormatFun, StackFun) of
+ Expl0 = explain_reason(Term, Class, Trace1, FormatFun, S, Encoding),
+ FormatString = case Encoding of
+ latin1 -> "~s~s";
+ _ -> "~s~ts"
+ end,
+ Expl = io_lib:fwrite(FormatString, [exited(Class), Expl0]),
+ case format_stacktrace1(S, Trace, FormatFun, StackFun, Encoding) of
[] -> Expl;
Stack -> [Expl, $\n, Stack]
end.
%% -> iolist() (no \n at end)
-format_stacktrace(I, StackTrace, StackFun, FormatFun)
+format_stacktrace(I, StackTrace, StackFun, FormatFun) ->
+ format_stacktrace(I, StackTrace, StackFun, FormatFun, latin1).
+
+%% -> iolist() | unicode:charlist() (no \n at end)
+format_stacktrace(I, StackTrace, StackFun, FormatFun, Encoding)
when is_integer(I), I >= 1, is_function(StackFun, 3),
is_function(FormatFun, 2) ->
S = n_spaces(I-1),
- format_stacktrace1(S, StackTrace, FormatFun, StackFun).
+ format_stacktrace1(S, StackTrace, FormatFun, StackFun, Encoding).
%% -> iolist() (no \n at end)
-format_call(I, ForMForFun, As, FormatFun) when is_integer(I), I >= 1,
- is_list(As),
- is_function(FormatFun, 2) ->
- format_call("", n_spaces(I-1), ForMForFun, As, FormatFun).
+format_call(I, ForMForFun, As, FormatFun) ->
+ format_call(I, ForMForFun, As, FormatFun, latin1).
+
+%% -> iolist() | unicode:charlist() (no \n at end)
+format_call(I, ForMForFun, As, FormatFun, Enc)
+ when is_integer(I), I >= 1, is_list(As), is_function(FormatFun, 2) ->
+ format_call("", n_spaces(I-1), ForMForFun, As, FormatFun, Enc).
%% -> iolist() (no \n at end)
format_fun(Fun) when is_function(Fun) ->
@@ -204,79 +222,80 @@ is_stacktrace(_) ->
false.
%% ERTS exit codes (some of them are also returned by erl_eval):
-explain_reason(badarg, error, [], _PF, _S) ->
+explain_reason(badarg, error, [], _PF, _S, _Enc) ->
<<"bad argument">>;
-explain_reason({badarg,V}, error=Cl, [], PF, S) -> % orelse, andalso
+explain_reason({badarg,V}, error=Cl, [], PF, S, _Enc) -> % orelse, andalso
format_value(V, <<"bad argument: ">>, Cl, PF, S);
-explain_reason(badarith, error, [], _PF, _S) ->
+explain_reason(badarith, error, [], _PF, _S, _Enc) ->
<<"an error occurred when evaluating an arithmetic expression">>;
-explain_reason({badarity,{Fun,As}}, error, [], _PF, _S)
+explain_reason({badarity,{Fun,As}}, error, [], _PF, _S, _Enc)
when is_function(Fun) ->
%% Only the arity is displayed, not the arguments As.
io_lib:fwrite(<<"~s called with ~s">>,
[format_fun(Fun), argss(length(As))]);
-explain_reason({badfun,Term}, error=Cl, [], PF, S) ->
+explain_reason({badfun,Term}, error=Cl, [], PF, S, _Enc) ->
format_value(Term, <<"bad function ">>, Cl, PF, S);
-explain_reason({badmatch,Term}, error=Cl, [], PF, S) ->
- format_value(Term, <<"no match of right hand side value ">>, Cl, PF, S);
-explain_reason({case_clause,V}, error=Cl, [], PF, S) ->
+explain_reason({badmatch,Term}, error=Cl, [], PF, S, _Enc) ->
+ Str = <<"no match of right hand side value ">>,
+ format_value(Term, Str, Cl, PF, S);
+explain_reason({case_clause,V}, error=Cl, [], PF, S, _Enc) ->
%% "there is no case clause with a true guard sequence and a
%% pattern matching..."
format_value(V, <<"no case clause matching ">>, Cl, PF, S);
-explain_reason(function_clause, error, [{F,A}], _PF, _S) ->
+explain_reason(function_clause, error, [{F,A}], _PF, _S, _Enc) ->
%% Shell commands
FAs = io_lib:fwrite(<<"~w/~w">>, [F, A]),
[<<"no function clause matching call to ">> | FAs];
-explain_reason(function_clause, error=Cl, [{M,F,As,Loc}], PF, S) ->
+explain_reason(function_clause, error=Cl, [{M,F,As,Loc}], PF, S, Enc) ->
Str = <<"no function clause matching ">>,
- [format_errstr_call(Str, Cl, {M,F}, As, PF, S),$\s|location(Loc)];
-explain_reason(if_clause, error, [], _PF, _S) ->
+ [format_errstr_call(Str, Cl, {M,F}, As, PF, S, Enc),$\s|location(Loc)];
+explain_reason(if_clause, error, [], _PF, _S, _Enc) ->
<<"no true branch found when evaluating an if expression">>;
-explain_reason(noproc, error, [], _PF, _S) ->
+explain_reason(noproc, error, [], _PF, _S, _Enc) ->
<<"no such process or port">>;
-explain_reason(notalive, error, [], _PF, _S) ->
+explain_reason(notalive, error, [], _PF, _S, _Enc) ->
<<"the node cannot be part of a distributed system">>;
-explain_reason(system_limit, error, [], _PF, _S) ->
+explain_reason(system_limit, error, [], _PF, _S, _Enc) ->
<<"a system limit has been reached">>;
-explain_reason(timeout_value, error, [], _PF, _S) ->
+explain_reason(timeout_value, error, [], _PF, _S, _Enc) ->
<<"bad receive timeout value">>;
-explain_reason({try_clause,V}, error=Cl, [], PF, S) ->
+explain_reason({try_clause,V}, error=Cl, [], PF, S, _Enc) ->
%% "there is no try clause with a true guard sequence and a
%% pattern matching..."
format_value(V, <<"no try clause matching ">>, Cl, PF, S);
-explain_reason(undef, error, [{M,F,A,_}], _PF, _S) ->
+explain_reason(undef, error, [{M,F,A,_}], _PF, _S, _Enc) ->
%% Only the arity is displayed, not the arguments, if there are any.
io_lib:fwrite(<<"undefined function ~s">>,
[mfa_to_string(M, F, n_args(A))]);
-explain_reason({shell_undef,F,A,_}, error, [], _PF, _S) ->
+explain_reason({shell_undef,F,A,_}, error, [], _PF, _S, _Enc) ->
%% Give nicer reports for undefined shell functions
%% (but not when the user actively calls shell_default:F(...)).
io_lib:fwrite(<<"undefined shell command ~s/~w">>, [F, n_args(A)]);
%% Exit codes returned by erl_eval only:
-explain_reason({argument_limit,_Fun}, error, [], _PF, _S) ->
+explain_reason({argument_limit,_Fun}, error, [], _PF, _S, _Enc) ->
io_lib:fwrite(<<"limit of number of arguments to interpreted function"
" exceeded">>, []);
-explain_reason({bad_filter,V}, error=Cl, [], PF, S) ->
+explain_reason({bad_filter,V}, error=Cl, [], PF, S, _Enc) ->
format_value(V, <<"bad filter ">>, Cl, PF, S);
-explain_reason({bad_generator,V}, error=Cl, [], PF, S) ->
+explain_reason({bad_generator,V}, error=Cl, [], PF, S, _Enc) ->
format_value(V, <<"bad generator ">>, Cl, PF, S);
-explain_reason({unbound,V}, error, [], _PF, _S) ->
+explain_reason({unbound,V}, error, [], _PF, _S, _Enc) ->
io_lib:fwrite(<<"variable ~w is unbound">>, [V]);
%% Exit codes local to the shell module (restricted shell):
-explain_reason({restricted_shell_bad_return, V}, exit=Cl, [], PF, S) ->
+explain_reason({restricted_shell_bad_return, V}, exit=Cl, [], PF, S, _Enc) ->
Str = <<"restricted shell module returned bad value ">>,
format_value(V, Str, Cl, PF, S);
explain_reason({restricted_shell_disallowed,{ForMF,As}},
- exit=Cl, [], PF, S) ->
+ exit=Cl, [], PF, S, Enc) ->
%% ForMF can be a fun, but not a shell fun.
Str = <<"restricted shell does not allow ">>,
- format_errstr_call(Str, Cl, ForMF, As, PF, S);
-explain_reason(restricted_shell_started, exit, [], _PF, _S) ->
+ format_errstr_call(Str, Cl, ForMF, As, PF, S, Enc);
+explain_reason(restricted_shell_started, exit, [], _PF, _S, _Enc) ->
<<"restricted shell starts now">>;
-explain_reason(restricted_shell_stopped, exit, [], _PF, _S) ->
+explain_reason(restricted_shell_stopped, exit, [], _PF, _S, _Enc) ->
<<"restricted shell stopped">>;
%% Other exit code:
-explain_reason(Reason, Class, [], PF, S) ->
+explain_reason(Reason, Class, [], PF, S, _Enc) ->
PF(Reason, (iolist_size(S)+1) + exited_size(Class)).
n_args(A) when is_integer(A) ->
@@ -293,28 +312,28 @@ argss(2) ->
argss(I) ->
io_lib:fwrite(<<"~w arguments">>, [I]).
-format_stacktrace1(S0, Stack0, PF, SF) ->
+format_stacktrace1(S0, Stack0, PF, SF, Enc) ->
Stack1 = lists:dropwhile(fun({M,F,A,_}) -> SF(M, F, A)
end, lists:reverse(Stack0)),
S = [" " | S0],
Stack = lists:reverse(Stack1),
- format_stacktrace2(S, Stack, 1, PF).
+ format_stacktrace2(S, Stack, 1, PF, Enc).
-format_stacktrace2(S, [{M,F,A,L}|Fs], N, PF) when is_integer(A) ->
+format_stacktrace2(S, [{M,F,A,L}|Fs], N, PF, Enc) when is_integer(A) ->
[io_lib:fwrite(<<"~s~s ~s ~s">>,
[sep(N, S), origin(N, M, F, A),
mfa_to_string(M, F, A),
location(L)])
- | format_stacktrace2(S, Fs, N + 1, PF)];
-format_stacktrace2(S, [{M,F,As,_}|Fs], N, PF) when is_list(As) ->
+ | format_stacktrace2(S, Fs, N + 1, PF, Enc)];
+format_stacktrace2(S, [{M,F,As,_}|Fs], N, PF, Enc) when is_list(As) ->
A = length(As),
CalledAs = [S,<<" called as ">>],
- C = format_call("", CalledAs, {M,F}, As, PF),
- [io_lib:fwrite(<<"~s~s ~s\n~s~s">>,
+ C = format_call("", CalledAs, {M,F}, As, PF, Enc),
+ [io_lib:fwrite(<<"~s~s ~s\n~s~ts">>,
[sep(N, S), origin(N, M, F, A), mfa_to_string(M, F, A),
CalledAs, C])
- | format_stacktrace2(S, Fs, N + 1, PF)];
-format_stacktrace2(_S, [], _N, _PF) ->
+ | format_stacktrace2(S, Fs, N + 1, PF, Enc)];
+format_stacktrace2(_S, [], _N, _PF, _Enc) ->
"".
location(L) ->
@@ -338,22 +357,22 @@ origin(1, M, F, A) ->
origin(_N, _M, _F, _A) ->
<<"in call from">>.
-format_errstr_call(ErrStr, Class, ForMForFun, As, PF, Pre0) ->
+format_errstr_call(ErrStr, Class, ForMForFun, As, PF, Pre0, Enc) ->
Pre1 = [Pre0 | n_spaces(exited_size(Class))],
- format_call(ErrStr, Pre1, ForMForFun, As, PF).
+ format_call(ErrStr, Pre1, ForMForFun, As, PF, Enc).
-format_call(ErrStr, Pre1, ForMForFun, As, PF) ->
+format_call(ErrStr, Pre1, ForMForFun, As, PF, Enc) ->
Arity = length(As),
[ErrStr |
case is_op(ForMForFun, Arity) of
{yes,Op} ->
- format_op(ErrStr, Pre1, Op, As, PF);
+ format_op(ErrStr, Pre1, Op, As, PF, Enc);
no ->
MFs = mf_to_string(ForMForFun, Arity),
I1 = iolist_size([Pre1,ErrStr|MFs]),
- S1 = pp_arguments(PF, As, I1),
- S2 = pp_arguments(PF, As, iolist_size([Pre1|MFs])),
- Long = count_nl(pp_arguments(PF, [a2345,b2345], I1)) > 0,
+ S1 = pp_arguments(PF, As, I1, Enc),
+ S2 = pp_arguments(PF, As, iolist_size([Pre1|MFs]), Enc),
+ Long = count_nl(pp_arguments(PF, [a2345,b2345], I1, Enc)) > 0,
case Long or (count_nl(S2) < count_nl(S1)) of
true ->
[$\n, Pre1, MFs, S2];
@@ -362,11 +381,11 @@ format_call(ErrStr, Pre1, ForMForFun, As, PF) ->
end
end].
-format_op(ErrStr, Pre, Op, [A1], PF) ->
+format_op(ErrStr, Pre, Op, [A1], PF, _Enc) ->
OpS = io_lib:fwrite(<<"~s ">>, [Op]),
I1 = iolist_size([ErrStr,Pre,OpS]),
[OpS | PF(A1, I1+1)];
-format_op(ErrStr, Pre, Op, [A1, A2], PF) ->
+format_op(ErrStr, Pre, Op, [A1, A2], PF, Enc) ->
I1 = iolist_size([ErrStr,Pre]),
S1 = PF(A1, I1+1),
S2 = PF(A2, I1+1),
@@ -377,33 +396,40 @@ format_op(ErrStr, Pre, Op, [A1, A2], PF) ->
[S1,Pre1,OpS,Pre1|S2];
false ->
OpS2 = io_lib:fwrite(<<" ~s ">>, [Op]),
- S2_2 = PF(A2, iolist_size([ErrStr,Pre,S1|OpS2])+1),
+ Size1 = iolist_size([ErrStr,Pre|OpS2]),
+ {Size2,S1_2} = size(Enc, S1),
+ S2_2 = PF(A2, Size1+Size2+1),
case count_nl(S2) < count_nl(S2_2) of
true ->
- [S1,Pre1,OpS,Pre1|S2];
+ [S1_2,Pre1,OpS,Pre1|S2];
false ->
- [S1,OpS2|S2_2]
+ [S1_2,OpS2|S2_2]
end
end.
-pp_arguments(PF, As, I) ->
- case {As, io_lib:printable_list(As)} of
+pp_arguments(PF, As, I, Enc) ->
+ case {As, printable_list(Enc, As)} of
{[Int | T], true} ->
L = integer_to_list(Int),
Ll = length(L),
A = list_to_atom(lists:duplicate(Ll, $a)),
- S0 = binary_to_list(iolist_to_binary(PF([A | T], I+1))),
- brackets_to_parens([$[,L,string:sub_string(S0, 2+Ll)]);
+ S0 = unicode:characters_to_list(PF([A | T], I+1), Enc),
+ brackets_to_parens([$[,L,string:sub_string(S0, 2+Ll)], Enc);
_ ->
- brackets_to_parens(PF(As, I+1))
+ brackets_to_parens(PF(As, I+1), Enc)
end.
-brackets_to_parens(S) ->
- B = iolist_to_binary(S),
+brackets_to_parens(S, Enc) ->
+ B = unicode:characters_to_binary(S, Enc),
Sz = byte_size(B) - 2,
<<$[,R:Sz/binary,$]>> = B,
[$(,R,$)].
+printable_list(latin1, As) ->
+ io_lib:printable_list(As);
+printable_list(_, As) ->
+ io_lib:printable_unicode_list(As).
+
mfa_to_string(M, F, A) ->
io_lib:fwrite(<<"~s/~w">>, [mf_to_string({M, F}, A), A]).
@@ -472,3 +498,10 @@ exited(exit) ->
<<"exception exit: ">>;
exited(throw) ->
<<"exception throw: ">>.
+
+size(latin1, S) ->
+ {iolist_size(S),S};
+size(_, S0) ->
+ S = unicode:characters_to_list(S0, unicode),
+ true = is_list(S),
+ {length(S),S}.
diff --git a/lib/stdlib/src/lists.erl b/lib/stdlib/src/lists.erl
index eb527471d5..961c060019 100644
--- a/lib/stdlib/src/lists.erl
+++ b/lib/stdlib/src/lists.erl
@@ -1178,8 +1178,7 @@ rumerge(T1, [H2 | T2]) ->
%% takewhile(Predicate, List)
%% dropwhile(Predicate, List)
%% splitwith(Predicate, List)
-%% for list programming. Function here is a 'fun'. For backward compatibility,
-%% {Module,Function} is still accepted.
+%% for list programming. Function here is a 'fun'.
%%
%% The name zf is a joke!
%%
diff --git a/lib/stdlib/src/log_mf_h.erl b/lib/stdlib/src/log_mf_h.erl
index f7f128dac7..19b555a48c 100644
--- a/lib/stdlib/src/log_mf_h.erl
+++ b/lib/stdlib/src/log_mf_h.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -25,6 +25,8 @@
-export([init/1, handle_event/2, handle_info/2, terminate/2]).
-export([handle_call/2, code_change/3]).
+-export_type([args/0]).
+
%%-----------------------------------------------------------------
-type b() :: non_neg_integer().
diff --git a/lib/stdlib/src/otp_internal.erl b/lib/stdlib/src/otp_internal.erl
index cddf345c76..9257953071 100644
--- a/lib/stdlib/src/otp_internal.erl
+++ b/lib/stdlib/src/otp_internal.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2012. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -78,89 +78,89 @@ obsolete_1(snmp, N, A) ->
end;
obsolete_1(snmpm, agent_info, 3) ->
- {deprecated, {snmpm, agent_info, 2}, "R16B"};
+ {removed, {snmpm, agent_info, 2}, "R16B"};
obsolete_1(snmpm, update_agent_info, 5) ->
- {deprecated, {snmpm, update_agent_info, 4}, "R16B"};
+ {removed, {snmpm, update_agent_info, 4}, "R16B"};
obsolete_1(snmpm, g, 3) ->
- {deprecated, {snmpm, sync_get, 3}, "R16B"};
+ {removed, {snmpm, sync_get, 3}, "R16B"};
obsolete_1(snmpm, g, 4) ->
- {deprecated, {snmpm, sync_get, [3,4]}, "R16B"};
+ {removed, {snmpm, sync_get, [3,4]}, "R16B"};
obsolete_1(snmpm, g, 5) ->
- {deprecated, {snmpm, sync_get, [4,5]}, "R16B"};
+ {removed, {snmpm, sync_get, [4,5]}, "R16B"};
obsolete_1(snmpm, g, 6) ->
- {deprecated, {snmpm, sync_get, [5,6]}, "R16B"};
+ {removed, {snmpm, sync_get, [5,6]}, "R16B"};
obsolete_1(snmpm, g, 7) ->
- {deprecated, {snmpm, sync_get, 6}, "R16B"};
+ {removed, {snmpm, sync_get, 6}, "R16B"};
obsolete_1(snmpm, ag, 3) ->
- {deprecated, {snmpm, async_get, 3}, "R16B"};
+ {removed, {snmpm, async_get, 3}, "R16B"};
obsolete_1(snmpm, ag, 4) ->
- {deprecated, {snmpm, async_get, [3,4]}, "R16B"};
+ {removed, {snmpm, async_get, [3,4]}, "R16B"};
obsolete_1(snmpm, ag, 5) ->
- {deprecated, {snmpm, async_get, [4,5]}, "R16B"};
+ {removed, {snmpm, async_get, [4,5]}, "R16B"};
obsolete_1(snmpm, ag, 6) ->
- {deprecated, {snmpm, async_get, [5,6]}, "R16B"};
+ {removed, {snmpm, async_get, [5,6]}, "R16B"};
obsolete_1(snmpm, ag, 7) ->
- {deprecated, {snmpm, async_get, 6}, "R16B"};
+ {removed, {snmpm, async_get, 6}, "R16B"};
obsolete_1(snmpm, gn, 3) ->
- {deprecated, {snmpm, sync_get_next, 3}, "R16B"};
+ {removed, {snmpm, sync_get_next, 3}, "R16B"};
obsolete_1(snmpm, gn, 4) ->
- {deprecated, {snmpm, sync_get_next, [3,4]}, "R16B"};
+ {removed, {snmpm, sync_get_next, [3,4]}, "R16B"};
obsolete_1(snmpm, gn, 5) ->
- {deprecated, {snmpm, sync_get_next, [4,5]}, "R16B"};
+ {removed, {snmpm, sync_get_next, [4,5]}, "R16B"};
obsolete_1(snmpm, gn, 6) ->
- {deprecated, {snmpm, sync_get_next, [5,6]}, "R16B"};
+ {removed, {snmpm, sync_get_next, [5,6]}, "R16B"};
obsolete_1(snmpm, gn, 7) ->
- {deprecated, {snmpm, sync_get_next, 6}, "R16B"};
+ {removed, {snmpm, sync_get_next, 6}, "R16B"};
obsolete_1(snmpm, agn, 3) ->
- {deprecated, {snmpm, async_get_next, 3}, "R16B"};
+ {removed, {snmpm, async_get_next, 3}, "R16B"};
obsolete_1(snmpm, agn, 4) ->
- {deprecated, {snmpm, async_get_next, [3,4]}, "R16B"};
+ {removed, {snmpm, async_get_next, [3,4]}, "R16B"};
obsolete_1(snmpm, agn, 5) ->
- {deprecated, {snmpm, async_get_next, [4,5]}, "R16B"};
+ {removed, {snmpm, async_get_next, [4,5]}, "R16B"};
obsolete_1(snmpm, agn, 6) ->
- {deprecated, {snmpm, async_get_next, [5,6]}, "R16B"};
+ {removed, {snmpm, async_get_next, [5,6]}, "R16B"};
obsolete_1(snmpm, agn, 7) ->
- {deprecated, {snmpm, async_get_next, 6}, "R16B"};
+ {removed, {snmpm, async_get_next, 6}, "R16B"};
obsolete_1(snmpm, s, 3) ->
- {deprecated, {snmpm, sync_set, 3}, "R16B"};
+ {removed, {snmpm, sync_set, 3}, "R16B"};
obsolete_1(snmpm, s, 4) ->
- {deprecated, {snmpm, sync_set, [3,4]}, "R16B"};
+ {removed, {snmpm, sync_set, [3,4]}, "R16B"};
obsolete_1(snmpm, s, 5) ->
- {deprecated, {snmpm, sync_set, [4,5]}, "R16B"};
+ {removed, {snmpm, sync_set, [4,5]}, "R16B"};
obsolete_1(snmpm, s, 6) ->
- {deprecated, {snmpm, sync_set, [5,6]}, "R16B"};
+ {removed, {snmpm, sync_set, [5,6]}, "R16B"};
obsolete_1(snmpm, s, 7) ->
- {deprecated, {snmpm, sync_set, 6}, "R16B"};
+ {removed, {snmpm, sync_set, 6}, "R16B"};
obsolete_1(snmpm, as, 3) ->
- {deprecated, {snmpm, async_set, 3}, "R16B"};
+ {removed, {snmpm, async_set, 3}, "R16B"};
obsolete_1(snmpm, as, 4) ->
- {deprecated, {snmpm, async_set, [3,4]}, "R16B"};
+ {removed, {snmpm, async_set, [3,4]}, "R16B"};
obsolete_1(snmpm, as, 5) ->
- {deprecated, {snmpm, async_set, [4,5]}, "R16B"};
+ {removed, {snmpm, async_set, [4,5]}, "R16B"};
obsolete_1(snmpm, as, 6) ->
- {deprecated, {snmpm, async_set, [5,6]}, "R16B"};
+ {removed, {snmpm, async_set, [5,6]}, "R16B"};
obsolete_1(snmpm, as, 7) ->
- {deprecated, {snmpm, async_set, 6}, "R16B"};
+ {removed, {snmpm, async_set, 6}, "R16B"};
obsolete_1(snmpm, gb, 5) ->
- {deprecated, {snmpm, sync_get_bulk, 5}, "R16B"};
+ {removed, {snmpm, sync_get_bulk, 5}, "R16B"};
obsolete_1(snmpm, gb, 6) ->
- {deprecated, {snmpm, sync_get_bulk, [5,6]}, "R16B"};
+ {removed, {snmpm, sync_get_bulk, [5,6]}, "R16B"};
obsolete_1(snmpm, gb, 7) ->
- {deprecated, {snmpm, sync_get_bulk, [6,7]}, "R16B"};
+ {removed, {snmpm, sync_get_bulk, [6,7]}, "R16B"};
obsolete_1(snmpm, gb, 8) ->
- {deprecated, {snmpm, sync_get_bulk, [7,8]}, "R16B"};
+ {removed, {snmpm, sync_get_bulk, [7,8]}, "R16B"};
obsolete_1(snmpm, gb, 9) ->
- {deprecated, {snmpm, sync_get_bulk, 8}, "R16B"};
+ {removed, {snmpm, sync_get_bulk, 8}, "R16B"};
obsolete_1(snmpm, agb, 5) ->
- {deprecated, {snmpm, async_get_bulk, 5}, "R16B"};
+ {removed, {snmpm, async_get_bulk, 5}, "R16B"};
obsolete_1(snmpm, agb, 6) ->
- {deprecated, {snmpm, async_get_bulk, [5,6]}, "R16B"};
+ {removed, {snmpm, async_get_bulk, [5,6]}, "R16B"};
obsolete_1(snmpm, agb, 7) ->
- {deprecated, {snmpm, async_get_bulk, [6,7]}, "R16B"};
+ {removed, {snmpm, async_get_bulk, [6,7]}, "R16B"};
obsolete_1(snmpm, agb, 8) ->
- {deprecated, {snmpm, async_get_bulk, [7,8]}, "R16B"};
+ {removed, {snmpm, async_get_bulk, [7,8]}, "R16B"};
obsolete_1(snmpm, agb, 9) ->
- {deprecated, {snmpm, async_get_bulk, 8}, "R16B"};
+ {removed, {snmpm, async_get_bulk, 8}, "R16B"};
%% *** MEGACO ***
@@ -347,7 +347,7 @@ obsolete_1(docb_xml_check, _, _) ->
obsolete_1(asn1rt, F, _) when F == load_driver; F == unload_driver ->
{deprecated,"deprecated (will be removed in R16A); has no effect as drivers are no longer used."};
obsolete_1(ssl, pid, 1) ->
- {deprecated,"deprecated (will be removed in R17); is no longer needed"};
+ {removed,"was removed in R16; is no longer needed"};
obsolete_1(inviso, _, _) ->
{removed,"the inviso application was removed in R16"};
@@ -359,6 +359,45 @@ obsolete_1(ssh, sign_data, 2) ->
"and public_key:sign/3 instead"};
obsolete_1(ssh, verify_data, 3) ->
{deprecated,"deprecated (will be removed in R16A); use public_key:ssh_decode/1, and public_key:verify/4 instead"};
+
+%% Added in R16
+obsolete_1(wxCalendarCtrl, enableYearChange, _) -> %% wx bug documented?
+ {deprecated,"deprecated function not available in wxWidgets-2.9 and later"};
+obsolete_1(wxDC, computeScaleAndOrigin, 1) ->
+ {deprecated,"deprecated function not available in wxWidgets-2.9 and later"};
+obsolete_1(wxClientDC, new, 0) ->
+ {deprecated,"deprecated function not available in wxWidgets-2.9 and later"};
+obsolete_1(wxPaintDC, new, 0) ->
+ {deprecated,"deprecated function not available in wxWidgets-2.9 and later"};
+obsolete_1(wxWindowDC, new, 0) ->
+ {deprecated,"deprecated function not available in wxWidgets-2.9 and later"};
+obsolete_1(wxGraphicsContext, createLinearGradientBrush, 7) ->
+ {deprecated,"deprecated function not available in wxWidgets-2.9 and later"};
+obsolete_1(wxGraphicsContext, createRadialGradientBrush, 8) ->
+ {deprecated,"deprecated function not available in wxWidgets-2.9 and later"};
+obsolete_1(wxGraphicsRenderer, createLinearGradientBrush, 7) ->
+ {deprecated,"deprecated function not available in wxWidgets-2.9 and later"};
+obsolete_1(wxGraphicsRenderer, createRadialGradientBrush, 8) ->
+ {deprecated,"deprecated function not available in wxWidgets-2.9 and later"};
+obsolete_1(wxGridCellEditor, endEdit, 4) ->
+ {deprecated,"deprecated function not available in wxWidgets-2.9 and later"};
+obsolete_1(wxGridCellEditor, paintBackground, 3) ->
+ {deprecated,"deprecated function not available in wxWidgets-2.9 and later"};
+obsolete_1(wxIdleEvent, canSend, 1) ->
+ {deprecated,"deprecated function not available in wxWidgets-2.9 and later"};
+obsolete_1(wxMDIClientWindow, new, 1) ->
+ {deprecated,"deprecated function not available in wxWidgets-2.9 and later"};
+obsolete_1(wxMDIClientWindow, new, 2) ->
+ {deprecated,"deprecated function not available in wxWidgets-2.9 and later"};
+obsolete_1(wxPostScriptDC, getResolution, 0) ->
+ {deprecated,"deprecated function not available in wxWidgets-2.9 and later"};
+obsolete_1(wxPostScriptDC, setResolution, 1) ->
+ {deprecated,"deprecated function not available in wxWidgets-2.9 and later"};
+obsolete_1(wxCursor, new, 3) ->
+ {deprecated,"deprecated function not available in wxWidgets-2.9 and later"};
+obsolete_1(wxCursor, new, 4) ->
+ {deprecated,"deprecated function not available in wxWidgets-2.9 and later"};
+
obsolete_1(_, _, _) ->
no.
diff --git a/lib/stdlib/src/proc_lib.erl b/lib/stdlib/src/proc_lib.erl
index 02bcbb5a60..4bca4c1e6d 100644
--- a/lib/stdlib/src/proc_lib.erl
+++ b/lib/stdlib/src/proc_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -184,6 +184,17 @@ check_for_monitor(SpawnOpts) ->
false
end.
+spawn_mon(M,F,A) ->
+ Parent = get_my_name(),
+ Ancestors = get_ancestors(),
+ erlang:spawn_monitor(?MODULE, init_p, [Parent,Ancestors,M,F,A]).
+
+spawn_opt_mon(M, F, A, Opts) when is_atom(M), is_atom(F), is_list(A) ->
+ Parent = get_my_name(),
+ Ancestors = get_ancestors(),
+ check_for_monitor(Opts),
+ erlang:spawn_opt(?MODULE, init_p, [Parent,Ancestors,M,F,A], [monitor|Opts]).
+
-spec hibernate(Module, Function, Args) -> no_return() when
Module :: module(),
Function :: atom(),
@@ -270,8 +281,8 @@ start(M, F, A) when is_atom(M), is_atom(F), is_list(A) ->
Ret :: term() | {error, Reason :: term()}.
start(M, F, A, Timeout) when is_atom(M), is_atom(F), is_list(A) ->
- Pid = ?MODULE:spawn(M, F, A),
- sync_wait(Pid, Timeout).
+ PidRef = spawn_mon(M, F, A),
+ sync_wait_mon(PidRef, Timeout).
-spec start(Module, Function, Args, Time, SpawnOpts) -> Ret when
Module :: module(),
@@ -282,8 +293,8 @@ start(M, F, A, Timeout) when is_atom(M), is_atom(F), is_list(A) ->
Ret :: term() | {error, Reason :: term()}.
start(M, F, A, Timeout, SpawnOpts) when is_atom(M), is_atom(F), is_list(A) ->
- Pid = ?MODULE:spawn_opt(M, F, A, SpawnOpts),
- sync_wait(Pid, Timeout).
+ PidRef = spawn_opt_mon(M, F, A, SpawnOpts),
+ sync_wait_mon(PidRef, Timeout).
-spec start_link(Module, Function, Args) -> Ret when
Module :: module(),
@@ -330,6 +341,23 @@ sync_wait(Pid, Timeout) ->
{error, timeout}
end.
+sync_wait_mon({Pid, Ref}, Timeout) ->
+ receive
+ {ack, Pid, Return} ->
+ erlang:demonitor(Ref, [flush]),
+ Return;
+ {'DOWN', Ref, _Type, Pid, Reason} ->
+ {error, Reason};
+ {'EXIT', Pid, Reason} -> %% link as spawn_opt?
+ erlang:demonitor(Ref, [flush]),
+ {error, Reason}
+ after Timeout ->
+ erlang:demonitor(Ref, [flush]),
+ exit(Pid, kill),
+ flush(Pid),
+ {error, timeout}
+ end.
+
-spec flush(pid()) -> 'true'.
flush(Pid) ->
diff --git a/lib/stdlib/src/proplists.erl b/lib/stdlib/src/proplists.erl
index e3eda5d932..204f8e128c 100644
--- a/lib/stdlib/src/proplists.erl
+++ b/lib/stdlib/src/proplists.erl
@@ -51,20 +51,21 @@
-export_type([property/0, proplist/0]).
--type property() :: atom() | tuple().
+-type property() :: atom() | tuple().
-type proplist() :: [property()].
%% ---------------------------------------------------------------------
%% @doc Creates a normal form (minimal) representation of a property. If
-%% <code>P</code> is <code>{Key, true}</code> where <code>Key</code> is
-%% an atom, this returns <code>Key</code>, otherwise the whole term
-%% <code>P</code> is returned.
+%% <code>PropertyIn</code> is <code>{Key, true}</code> where
+%% <code>Key</code> is an atom, this returns <code>Key</code>, otherwise
+%% the whole term <code>PropertyIn</code> is returned.
%%
%% @see property/2
--spec property(Property) -> Property when
- Property :: property().
+-spec property(PropertyIn) -> PropertyOut when
+ PropertyIn :: property(),
+ PropertyOut :: property().
property({Key, true}) when is_atom(Key) ->
Key;
@@ -92,13 +93,14 @@ property(Key, Value) ->
%% ---------------------------------------------------------------------
-%% @doc Unfolds all occurences of atoms in <code>List</code> to tuples
+%% @doc Unfolds all occurences of atoms in <code>ListIn</code> to tuples
%% <code>{Atom, true}</code>.
%%
%% @see compact/1
--spec unfold(List) -> List when
- List :: [term()].
+-spec unfold(ListIn) -> ListOut when
+ ListIn :: [term()],
+ ListOut :: [term()].
unfold([P | Ps]) ->
if is_atom(P) ->
@@ -110,16 +112,17 @@ unfold([]) ->
[].
%% @doc Minimizes the representation of all entries in the list. This is
-%% equivalent to <code>[property(P) || P &lt;- List]</code>.
+%% equivalent to <code>[property(P) || P &lt;- ListIn]</code>.
%%
%% @see unfold/1
%% @see property/1
--spec compact(List) -> List when
- List :: [property()].
+-spec compact(ListIn) -> ListOut when
+ ListIn :: [property()],
+ ListOut :: [property()].
-compact(List) ->
- [property(P) || P <- List].
+compact(ListIn) ->
+ [property(P) || P <- ListIn].
%% ---------------------------------------------------------------------
@@ -199,7 +202,7 @@ is_defined(_Key, []) ->
-spec get_value(Key, List) -> term() when
Key :: term(),
- List :: List::[term()].
+ List :: [term()].
get_value(Key, List) ->
get_value(Key, List, undefined).
@@ -272,9 +275,10 @@ get_all_values(_Key, []) ->
%%
%% @see get_all_values/2
--spec append_values(Key, List) -> List when
+-spec append_values(Key, ListIn) -> ListOut when
Key :: term(),
- List :: [term()].
+ ListIn :: [term()],
+ ListOut :: [term()].
append_values(Key, [P | Ps]) ->
if is_atom(P), P =:= Key ->
@@ -357,7 +361,7 @@ get_keys([], Keys) ->
-spec delete(Key, List) -> List when
Key :: term(),
- List::[term()].
+ List :: [term()].
delete(Key, [P | Ps]) ->
if is_atom(P), P =:= Key ->
@@ -374,7 +378,7 @@ delete(_, []) ->
%% ---------------------------------------------------------------------
%% @doc Substitutes keys of properties. For each entry in
-%% <code>List</code>, if it is associated with some key <code>K1</code>
+%% <code>ListIn</code>, if it is associated with some key <code>K1</code>
%% such that <code>{K1, K2}</code> occurs in <code>Aliases</code>, the
%% key of the entry is changed to <code>Key2</code>. If the same
%% <code>K1</code> occurs more than once in <code>Aliases</code>, only
@@ -388,10 +392,11 @@ delete(_, []) ->
%% @see substitute_negations/2
%% @see normalize/2
--spec substitute_aliases(Aliases, List) -> List when
+-spec substitute_aliases(Aliases, ListIn) -> ListOut when
Aliases :: [{Key, Key}],
Key :: term(),
- List::[term()].
+ ListIn :: [term()],
+ ListOut :: [term()].
substitute_aliases(As, Props) ->
[substitute_aliases_1(As, P) || P <- Props].
@@ -411,13 +416,13 @@ substitute_aliases_1([], P) ->
%% ---------------------------------------------------------------------
%% @doc Substitutes keys of boolean-valued properties and simultaneously
-%% negates their values. For each entry in <code>List</code>, if it is
+%% negates their values. For each entry in <code>ListIn</code>, if it is
%% associated with some key <code>K1</code> such that <code>{K1,
%% K2}</code> occurs in <code>Negations</code>, then if the entry was
%% <code>{K1, true}</code> it will be replaced with <code>{K2,
%% false}</code>, otherwise it will be replaced with <code>{K2,
%% true}</code>, thus changing the name of the option and simultaneously
-%% negating the value given by <code>get_bool(List)</code>. If the same
+%% negating the value given by <code>get_bool(ListIn)</code>. If the same
%% <code>K1</code> occurs more than once in <code>Negations</code>, only
%% the first occurrence is used.
%%
@@ -431,10 +436,11 @@ substitute_aliases_1([], P) ->
%% @see substitute_aliases/2
%% @see normalize/2
--spec substitute_negations(Negations, List) -> List when
+-spec substitute_negations(Negations, ListIn) -> ListOut when
Negations :: [{Key, Key}],
Key :: term(),
- List :: [term()].
+ ListIn :: [term()],
+ ListOut :: [term()].
substitute_negations(As, Props) ->
[substitute_negations_1(As, P) || P <- Props].
@@ -466,11 +472,11 @@ substitute_negations_1([], P) ->
%% @doc Expands particular properties to corresponding sets of
%% properties (or other terms). For each pair <code>{Property,
%% Expansion}</code> in <code>Expansions</code>, if <code>E</code> is
-%% the first entry in <code>List</code> with the same key as
+%% the first entry in <code>ListIn</code> with the same key as
%% <code>Property</code>, and <code>E</code> and <code>Property</code>
%% have equivalent normal forms, then <code>E</code> is replaced with
%% the terms in <code>Expansion</code>, and any following entries with
-%% the same key are deleted from <code>List</code>.
+%% the same key are deleted from <code>ListIn</code>.
%%
%% <p>For example, the following expressions all return <code>[fie, bar,
%% baz, fum]</code>:
@@ -497,9 +503,10 @@ substitute_negations_1([], P) ->
%%
%% @see normalize/2
--spec expand(Expansions, List) -> List when
+-spec expand(Expansions, ListIn) -> ListOut when
Expansions :: [{Property :: property(), Expansion :: [term()]}],
- List :: [term()].
+ ListIn :: [term()],
+ ListOut :: [term()].
expand(Es, Ps) when is_list(Ps) ->
Es1 = [{property(P), V} || {P, V} <- Es],
@@ -599,15 +606,16 @@ flatten([]) ->
%% @see expand/2
%% @see compact/1
--spec normalize(List, Stages) -> List when
- List :: [term()],
+-spec normalize(ListIn, Stages) -> ListOut when
+ ListIn :: [term()],
Stages :: [Operation],
Operation :: {'aliases', Aliases}
| {'negations', Negations}
| {'expand', Expansions},
Aliases :: [{Key, Key}],
Negations :: [{Key, Key}],
- Expansions :: [{Property :: property(), Expansion :: [term()]}].
+ Expansions :: [{Property :: property(), Expansion :: [term()]}],
+ ListOut :: [term()].
normalize(L, [{aliases, As} | Xs]) ->
normalize(substitute_aliases(As, L), Xs);
diff --git a/lib/stdlib/src/qlc.erl b/lib/stdlib/src/qlc.erl
index 2b691e6abf..9b71d0edb8 100644
--- a/lib/stdlib/src/qlc.erl
+++ b/lib/stdlib/src/qlc.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -125,7 +125,7 @@
-define(THROWN_ERROR, {?MODULE, throw_error, _, _}).
--export_type([query_handle/0]).
+-export_type([query_cursor/0, query_handle/0]).
%%% A query handle is a tuple {qlc_handle, Handle} where Handle is one
%%% of #qlc_append, #qlc_table, #qlc_sort, and #qlc_lc.
diff --git a/lib/stdlib/src/qlc_pt.erl b/lib/stdlib/src/qlc_pt.erl
index ad25fd559c..d441f38e44 100644
--- a/lib/stdlib/src/qlc_pt.erl
+++ b/lib/stdlib/src/qlc_pt.erl
@@ -31,9 +31,6 @@
%% Also in qlc.erl.
-define(QLC_Q(L1, L2, L3, L4, LC, Os),
{call,L1,{remote,L2,{atom,L3,?APIMOD},{atom,L4,?Q}},[LC | Os]}).
--define(QLC_QQ(L1, L2, L3, L4, L5, L6, LC, Os), % packages...
- {call,L1,{remote,L2,{record_field,L3,{atom,L4,''},
- {atom,L5,?APIMOD}},{atom,L6,?Q}},[LC | Os]}).
-define(IMP_Q(L1, L2, LC, Os), {call,L,{atom,L2,?Q},[LC | Os]}).
%% Also in qlc.erl.
@@ -2475,13 +2472,6 @@ qlcmf(?QLC_Q(L1, L2, L3, L4, LC0, Os0), F, Imp, A0, No0) when length(Os0) < 2 ->
NL = make_lcid(L1, No),
{T, A} = F(NL, LC, A2),
{?QLC_Q(L1, L2, L3, L4, T, Os), A, No + 1};
-qlcmf(?QLC_QQ(L1, L2, L3, L4, L5, L6, LC0, Os0),
- F, Imp, A0, No0) when length(Os0) < 2 ->
- {Os, A1, No1} = qlcmf(Os0, F, Imp, A0, No0),
- {LC, A2, No} = qlcmf(LC0, F, Imp, A1, No1), % nested...
- NL = make_lcid(L1, No),
- {T, A} = F(NL, LC, A2),
- {?QLC_QQ(L1, L2, L3, L4, L5, L6, T, Os), A, No + 1};
qlcmf(?IMP_Q(L1, L2, LC0, Os0), F, Imp=true, A0, No0) when length(Os0) < 2 ->
{Os, A1, No1} = qlcmf(Os0, F, Imp, A0, No0),
{LC, A2, No} = qlcmf(LC0, F, Imp, A1, No1), % nested...
diff --git a/lib/stdlib/src/queue.erl b/lib/stdlib/src/queue.erl
index afe917b151..bc1bd06534 100644
--- a/lib/stdlib/src/queue.erl
+++ b/lib/stdlib/src/queue.erl
@@ -472,22 +472,24 @@ init(Q) -> drop_r(Q).
-compile({inline, [{r2f,1},{f2r,1}]}).
-%% Move all but two from R to F, if there are at least three
+%% Move half of elements from R to F, if there are at least three
r2f([]) ->
{[],[]};
r2f([_]=R) ->
{[],R};
r2f([X,Y]) ->
{[X],[Y]};
-r2f([X,Y|R]) ->
- {[X,Y],lists:reverse(R, [])}.
+r2f(List) ->
+ {FF,RR} = lists:split(length(List) div 2 + 1, List),
+ {FF,lists:reverse(RR, [])}.
-%% Move all but two from F to R, if there are enough
+%% Move half of elements from F to R, if there are enough
f2r([]) ->
{[],[]};
f2r([_]=F) ->
{F,[]};
f2r([X,Y]) ->
{[Y],[X]};
-f2r([X,Y|F]) ->
- {lists:reverse(F, []),[X,Y]}.
+f2r(List) ->
+ {FF,RR} = lists:split(length(List) div 2 + 1, List),
+ {lists:reverse(RR, []),FF}.
diff --git a/lib/stdlib/src/re.erl b/lib/stdlib/src/re.erl
index 359afc8c14..c5109ec455 100644
--- a/lib/stdlib/src/re.erl
+++ b/lib/stdlib/src/re.erl
@@ -409,6 +409,12 @@ apply_mlist(Subject,Replacement,Mlist) ->
precomp_repl(<<>>) ->
[];
+precomp_repl(<<$\\,$g,${,Rest/binary>>) when byte_size(Rest) > 0 ->
+ {NS, <<$},NRest/binary>>} = pick_int(Rest),
+ [list_to_integer(NS) | precomp_repl(NRest)];
+precomp_repl(<<$\\,$g,Rest/binary>>) when byte_size(Rest) > 0 ->
+ {NS,NRest} = pick_int(Rest),
+ [list_to_integer(NS) | precomp_repl(NRest)];
precomp_repl(<<$\\,X,Rest/binary>>) when X < $1 ; X > $9 ->
%% Escaped character
case precomp_repl(Rest) of
diff --git a/lib/stdlib/src/sets.erl b/lib/stdlib/src/sets.erl
index 3fd6c81e5f..e6f05b71d4 100644
--- a/lib/stdlib/src/sets.erl
+++ b/lib/stdlib/src/sets.erl
@@ -1,7 +1,8 @@
+%% -*- coding: utf-8 -*-
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -17,7 +18,7 @@
%% %CopyrightEnd%
%%
-%% We use the dynamic hashing techniques by Per-�ke Larsson as
+%% We use the dynamic hashing techniques by Per-Åke Larsson as
%% described in "The Design and Implementation of Dynamic Hashing for
%% Sets and Tables in Icon" by Griswold and Townsend. Much of the
%% terminology comes from that paper as well.
diff --git a/lib/stdlib/src/shell.erl b/lib/stdlib/src/shell.erl
index dc450f0ee6..5c929d2f51 100644
--- a/lib/stdlib/src/shell.erl
+++ b/lib/stdlib/src/shell.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -128,7 +128,7 @@ start_restricted(RShMod) when is_atom(RShMod) ->
error_logger:error_report(
lists:flatten(
io_lib:fwrite(
- <<"Restricted shell module ~w not found: ~p\n">>,
+ "Restricted shell module ~w not found: ~"++cs_p() ++"\n",
[RShMod,What]))),
Error
end.
@@ -139,16 +139,6 @@ stop_restricted() ->
application:unset_env(stdlib, restricted_shell),
exit(restricted_shell_stopped).
-default_packages() ->
- [].
-%%% ['erl','erl.lang'].
-
-default_modules() ->
- [].
-%%% [{pdict, 'erl.lang.proc.pdict'},
-%%% {keylist, 'erl.lang.list.keylist'},
-%%% {debug, 'erl.system.debug'}].
-
-spec server(boolean(), boolean()) -> 'terminated'.
server(NoCtrlG, StartSync) ->
@@ -183,16 +173,7 @@ server(StartSync) ->
end
end,
%% Our spawner has fixed the process groups.
- Bs0 = erl_eval:new_bindings(),
- Bs = lists:foldl(fun ({K, V}, D) ->
- erl_eval:add_binding({module,K}, V, D)
- end,
- lists:foldl(fun (P, D) ->
- import_all(P, D)
- end,
- Bs0, default_packages()),
- default_modules()),
- %% io:fwrite("Imported modules: ~p.\n", [erl_eval:bindings(Bs)]),
+ Bs = erl_eval:new_bindings(),
%% Use an Ets table for record definitions. It takes too long to
%% send a huge term to and from the evaluator. Ets makes it
@@ -230,9 +211,10 @@ server(StartSync) ->
ok;
{RShMod2,What2} ->
io:fwrite(
- <<"Warning! Restricted shell module ~w not found: ~p.\n"
- "Only the commands q() and init:stop() will be allowed!\n">>,
- [RShMod2,What2]),
+ ("Warning! Restricted shell module ~w not found: ~"
+ ++cs_p()++".\n"
+ "Only the commands q() and init:stop() will be allowed!\n"),
+ [RShMod2,What2]),
application:set_env(stdlib, restricted_shell, ?MODULE)
end,
@@ -244,7 +226,7 @@ server_loop(N0, Eval_0, Bs00, RT, Ds00, History0, Results0) ->
{Eval_1,Bs0,Ds0,Prompt} = prompt(N, Eval_0, Bs00, RT, Ds00),
{Res,Eval0} = get_command(Prompt, Eval_1, Bs0, RT, Ds0),
case Res of
- {ok,Es0,_EndLine} ->
+ {ok,Es0} ->
case expand_hist(Es0, N) of
{ok,Es} ->
{V,Eval,Bs,Ds} = shell_cmd(Es, Eval0, Bs0, RT, Ds0, cmd),
@@ -263,11 +245,11 @@ server_loop(N0, Eval_0, Bs00, RT, Ds00, History0, Results0) ->
end,
server_loop(N, Eval, Bs, RT, Ds, History, Results);
{error,E} ->
- fwrite_severity(benign, <<"~s">>, [E]),
+ fwrite_severity(benign, <<"~ts">>, [E]),
server_loop(N0, Eval0, Bs0, RT, Ds0, History0, Results0)
end;
- {error,{Line,Mod,What},_EndLine} ->
- fwrite_severity(benign, <<"~w: ~s">>,
+ {error,{Line,Mod,What}} ->
+ fwrite_severity(benign, <<"~w: ~ts">>,
[Line, Mod:format_error(What)]),
server_loop(N0, Eval0, Bs0, RT, Ds0, History0, Results0);
{error,terminated} -> %Io process terminated
@@ -277,20 +259,35 @@ server_loop(N0, Eval_0, Bs00, RT, Ds00, History0, Results0) ->
exit(Eval0, kill),
{_,Eval,_,_} = shell_rep(Eval0, Bs0, RT, Ds0),
server_loop(N0, Eval, Bs0, RT, Ds0, History0, Results0);
- {error,tokens} -> %Most probably unicode > 255
+ {error,tokens} -> %Most probably character > 255
fwrite_severity(benign, <<"~w: Invalid tokens.">>,
[N]),
server_loop(N0, Eval0, Bs0, RT, Ds0, History0, Results0);
- {eof,_EndLine} ->
- fwrite_severity(fatal, <<"Terminating erlang (~w)">>, [node()]),
- halt();
eof ->
fwrite_severity(fatal, <<"Terminating erlang (~w)">>, [node()]),
halt()
end.
get_command(Prompt, Eval, Bs, RT, Ds) ->
- Parse = fun() -> exit(io:parse_erl_exprs(Prompt)) end,
+ Parse =
+ fun() ->
+ exit(
+ case
+ io:scan_erl_exprs(group_leader(), Prompt, 1, [unicode])
+ of
+ {ok,Toks,_EndPos} ->
+ erl_parse:parse_exprs(Toks);
+ {eof,_EndPos} ->
+ eof;
+ {error,ErrorInfo,_EndPos} ->
+ %% Skip the rest of the line:
+ _ = io:get_line(''),
+ {error,ErrorInfo};
+ Else ->
+ Else
+ end
+ )
+ end,
Pid = spawn_link(Parse),
get_command1(Pid, Eval, Bs, RT, Ds).
@@ -337,7 +334,7 @@ get_prompt_func() ->
end.
bad_prompt_func(M) ->
- fwrite_severity(benign, <<"Bad prompt function: ~p">>, [M]).
+ fwrite_severity(benign, "Bad prompt function: ~"++cs_p(), [M]).
default_prompt(N) ->
%% Don't bother flattening the list irrespective of what the
@@ -453,7 +450,8 @@ expand_bin_elements([{bin_element,L,E,Sz,Ts}|Fs], C) ->
no_command(N) ->
throw({error,
- io_lib:fwrite(<<"~s: command not found">>, [erl_pp:expr(N)])}).
+ io_lib:fwrite(<<"~ts: command not found">>,
+ [erl_pp:expr(N, enc())])}).
%% add_cmd(Number, Expressions, Value)
%% get_cmd(Number, CurrentCommand)
@@ -518,7 +516,7 @@ shell_rep(Ev, Bs0, RT, Ds0) ->
{shell_rep,Ev,{value,V,Bs,Ds}} ->
{V,Ev,Bs,Ds};
{shell_rep,Ev,{command_error,{Line,M,Error}}} ->
- fwrite_severity(benign, <<"~w: ~s">>,
+ fwrite_severity(benign, <<"~w: ~ts">>,
[Line, M:format_error(Error)]),
{{'EXIT',Error},Ev,Bs0,Ds0};
{shell_req,Ev,get_cmd} ->
@@ -570,9 +568,10 @@ report_exception(Class, Severity, {Reason,Stacktrace}, RT) ->
I = iolist_size(Tag) + 1,
PF = fun(Term, I1) -> pp(Term, I1, RT) end,
SF = fun(M, _F, _A) -> (M =:= erl_eval) or (M =:= ?MODULE) end,
- io:requests([{put_chars, Tag},
- {put_chars,
- lib:format_exception(I, Class, Reason, Stacktrace, SF, PF)},
+ Enc = encoding(),
+ Str = lib:format_exception(I, Class, Reason, Stacktrace, SF, PF, Enc),
+ io:requests([{put_chars, latin1, Tag},
+ {put_chars, unicode, Str},
nl]).
start_eval(Bs, RT, Ds) ->
@@ -671,7 +670,8 @@ exprs([E0|Es], Bs1, RT, Lf, Ef, Bs0, W) ->
if
Es =:= [] ->
VS = pp(V0, 1, RT),
- [io:requests([{put_chars, VS}, nl]) || W =:= cmd],
+ [io:requests([{put_chars, unicode, VS}, nl]) ||
+ W =:= cmd],
%% Don't send the result back if it will be
%% discarded anyway.
V = if
@@ -753,7 +753,7 @@ used_records(E) ->
{expr, E}.
fwrite_severity(Severity, S, As) ->
- io:fwrite(<<"~s\n">>, [format_severity(Severity, S, As)]).
+ io:fwrite(<<"~ts\n">>, [format_severity(Severity, S, As)]).
format_severity(Severity, S, As) ->
add_severity(Severity, io_lib:fwrite(S, As)).
@@ -958,13 +958,13 @@ local_func(rd, [{atom,_,RecName},RecDef0], Bs, _Shell, RT, _Lf, _Ef) ->
RecDef = expand_value(RecDef0),
RDs = lists:flatten(erl_pp:expr(RecDef)),
Attr = lists:concat(["-record('", RecName, "',", RDs, ")."]),
- {ok, Tokens, _} = erl_scan:string(Attr),
+ {ok, Tokens, _} = erl_scan:string(Attr, 1, [unicode]),
case erl_parse:parse_form(Tokens) of
{ok,AttrForm} ->
[RN] = add_records([AttrForm], Bs, RT),
{value,RN,Bs};
{error,{_Line,M,ErrDesc}} ->
- ErrStr = io_lib:fwrite(<<"~s">>, [M:format_error(ErrDesc)]),
+ ErrStr = io_lib:fwrite(<<"~ts">>, [M:format_error(ErrDesc)]),
exit(lists:flatten(ErrStr))
end;
local_func(rd, [_,_], _Bs, _Shell, _RT, _Lf, _Ef) ->
@@ -988,11 +988,13 @@ local_func(rl, [A], Bs0, _Shell, RT, Lf, Ef) ->
{value,list_records(record_defs(RT, listify(Recs))),Bs};
local_func(rp, [A], Bs0, _Shell, RT, Lf, Ef) ->
{[V],Bs} = expr_list([A], Bs0, Lf, Ef),
- W = columns(),
- io:requests([{put_chars,
- io_lib_pretty:print(V, 1, W, -1, ?CHAR_MAX,
- record_print_fun(RT))},
- nl]),
+ Cs = io_lib_pretty:print(V, ([{column, 1},
+ {line_length, columns()},
+ {depth, -1},
+ {max_chars, ?CHAR_MAX},
+ {record_print_fun, record_print_fun(RT)}]
+ ++ enc())),
+ io:requests([{put_chars, unicode, Cs}, nl]),
{value,ok,Bs};
local_func(rr, [A], Bs0, _Shell, RT, Lf, Ef) ->
{[File],Bs} = expr_list([A], Bs0, Lf, Ef),
@@ -1012,38 +1014,6 @@ local_func(which, [{atom,_,M}], Bs, _Shell, _RT, _Lf, _Ef) ->
end;
local_func(which, [_Other], _Bs, _Shell, _RT, _Lf, _Ef) ->
erlang:raise(error, function_clause, [{shell,which,1}]);
-local_func(import, [M], Bs, _Shell, _RT, _Lf, _Ef) ->
- case erl_parse:package_segments(M) of
- error -> erlang:raise(error, function_clause, [{shell,import,1}]);
- M1 ->
- Mod = packages:concat(M1),
- case packages:is_valid(Mod) of
- true ->
- Key = list_to_atom(packages:last(Mod)),
- Mod1 = list_to_atom(Mod),
- {value,ok,erl_eval:add_binding({module,Key}, Mod1, Bs)};
- false ->
- exit({{bad_module_name, Mod}, [{shell,import,1}]})
- end
- end;
-local_func(import_all, [P], Bs0, _Shell, _RT, _Lf, _Ef) ->
- case erl_parse:package_segments(P) of
- error -> erlang:raise(error, function_clause, [{shell,import_all,1}]);
- P1 ->
- Name = packages:concat(P1),
- case packages:is_valid(Name) of
- true ->
- Bs1 = import_all(Name, Bs0),
- {value,ok,Bs1};
- false ->
- exit({{bad_package_name, Name},
- [{shell,import_all,1}]})
- end
- end;
-local_func(use, [M], Bs, Shell, RT, Lf, Ef) ->
- local_func(import, [M], Bs, Shell, RT, Lf, Ef);
-local_func(use_all, [M], Bs, Shell, RT, Lf, Ef) ->
- local_func(import_all, [M], Bs, Shell, RT, Lf, Ef);
local_func(history, [{integer,_,N}], Bs, _Shell, _RT, _Lf, _Ef) ->
{value,history(N),Bs};
local_func(history, [_Other], _Bs, _Shell, _RT, _Lf, _Ef) ->
@@ -1166,7 +1136,7 @@ add_records(RAs, Bs0, RT) ->
case check_command([], Bs1) of
{error,{_Line,M,ErrDesc}} ->
%% A source file that has not been compiled.
- ErrStr = io_lib:fwrite(<<"~s">>, [M:format_error(ErrDesc)]),
+ ErrStr = io_lib:fwrite(<<"~ts">>, [M:format_error(ErrDesc)]),
exit(lists:flatten(ErrStr));
ok ->
true = ets:insert(RT, Recs),
@@ -1323,15 +1293,6 @@ record_attrs(Forms) ->
%%% End of reading record information from file(s)
-import_all(P, Bs0) ->
- Ms = packages:find_modules(P),
- lists:foldl(fun (M, Bs) ->
- Key = list_to_atom(M),
- M1 = list_to_atom(packages:concat(P, M)),
- erl_eval:add_binding({module,Key}, M1, Bs)
- end,
- Bs0, Ms).
-
shell_req(Shell, Req) ->
Shell ! {shell_req,self(),Req},
receive
@@ -1343,25 +1304,25 @@ list_commands([{{N,command},Es0}, {{N,result}, V} |Ds], RT) ->
VS = pp(V, 4, RT),
Ns = io_lib:fwrite(<<"~w: ">>, [N]),
I = iolist_size(Ns),
- io:requests([{put_chars, Ns},
- {format,<<"~s\n">>,[erl_pp:exprs(Es, I, none)]},
+ io:requests([{put_chars, latin1, Ns},
+ {format,<<"~ts\n">>,[erl_pp:exprs(Es, I, enc())]},
{format,<<"-> ">>,[]},
- {put_chars, VS},
+ {put_chars, unicode, VS},
nl]),
list_commands(Ds, RT);
list_commands([{{N,command},Es0} |Ds], RT) ->
Es = prep_list_commands(Es0),
Ns = io_lib:fwrite(<<"~w: ">>, [N]),
I = iolist_size(Ns),
- io:requests([{put_chars, Ns},
- {format,<<"~s\n">>,[erl_pp:exprs(Es, I, none)]}]),
+ io:requests([{put_chars, latin1, Ns},
+ {format,<<"~ts\n">>,[erl_pp:exprs(Es, I, enc())]}]),
list_commands(Ds, RT);
list_commands([_D|Ds], RT) ->
list_commands(Ds, RT);
list_commands([], _RT) -> ok.
list_bindings([{{module,M},Val}|Bs], RT) ->
- io:fwrite(<<"~p is ~p\n">>, [M,Val]),
+ io:fwrite(<<"~w is ~w\n">>, [M,Val]),
list_bindings(Bs, RT);
list_bindings([{Name,Val}|Bs], RT) ->
case erl_eval:fun_data(Val) of
@@ -1369,13 +1330,13 @@ list_bindings([{Name,Val}|Bs], RT) ->
FCs = expand_value(FCs0), % looks nicer
F = {'fun',0,{clauses,FCs}},
M = {match,0,{var,0,Name},F},
- io:fwrite(<<"~s\n">>, [erl_pp:expr(M)]);
+ io:fwrite(<<"~ts\n">>, [erl_pp:expr(M, enc())]);
false ->
Namel = io_lib:fwrite(<<"~s = ">>, [Name]),
Nl = iolist_size(Namel)+1,
ValS = pp(Val, Nl, RT),
- io:requests([{put_chars, Namel},
- {put_chars, ValS},
+ io:requests([{put_chars, latin1, Namel},
+ {put_chars, unicode, ValS},
nl])
end,
list_bindings(Bs, RT);
@@ -1384,7 +1345,7 @@ list_bindings([], _RT) ->
list_records(Records) ->
lists:foreach(fun({_Name,Attr}) ->
- io:fwrite(<<"~s">>, [erl_pp:attribute(Attr)])
+ io:fwrite(<<"~ts">>, [erl_pp:attribute(Attr, enc())])
end, Records).
record_defs(RT, Names) ->
@@ -1427,8 +1388,20 @@ get_history_and_results() ->
{History, erlang:min(Results, History)}.
pp(V, I, RT) ->
- io_lib_pretty:print(V, I, columns(), ?LINEMAX, ?CHAR_MAX,
- record_print_fun(RT)).
+ pp(V, I, RT, enc()).
+
+pp(V, I, RT, Enc) ->
+ io_lib_pretty:print(V, ([{column, I}, {line_length, columns()},
+ {depth, ?LINEMAX}, {max_chars, ?CHAR_MAX},
+ {record_print_fun, record_print_fun(RT)}]
+ ++ Enc)).
+
+%% Control sequence 'p' possibly with Unicode translation modifier
+cs_p() ->
+ case encoding() of
+ latin1 -> "p";
+ unicode -> "tp"
+ end.
columns() ->
case io:columns() of
@@ -1436,9 +1409,20 @@ columns() ->
_ -> 80
end.
+encoding() ->
+ [{encoding, Encoding}] = enc(),
+ Encoding.
+
+enc() ->
+ case lists:keyfind(encoding, 1, io:getopts()) of
+ false -> [{encoding,latin1}]; % should never happen
+ Enc -> [Enc]
+ end.
+
garb(Shell) ->
erlang:garbage_collect(Shell),
catch erlang:garbage_collect(whereis(user)),
+ catch erlang:garbage_collect(whereis(group)),
catch erlang:garbage_collect(group_leader()),
erlang:garbage_collect().
@@ -1458,7 +1442,8 @@ check_env(V) ->
ok;
{ok, Val} ->
Txt = io_lib:fwrite(
- <<"Invalid value of STDLIB configuration parameter ~p: ~p\n">>,
+ ("Invalid value of STDLIB configuration parameter ~w: ~"
+ ++cs_p()++"\n"),
[V, Val]),
error_logger:info_report(lists:flatten(Txt))
end.
diff --git a/lib/stdlib/src/string.erl b/lib/stdlib/src/string.erl
index fc029a582f..03f0a19f14 100644
--- a/lib/stdlib/src/string.erl
+++ b/lib/stdlib/src/string.erl
@@ -1,3 +1,4 @@
+%% -*- coding: utf-8 -*-
%%
%% %CopyrightBegin%
%%
@@ -257,7 +258,7 @@ chars(C, N, Tail) when N > 0 ->
chars(C, 0, Tail) when is_integer(C) ->
Tail.
-%% Torbj�rn's bit.
+%% Torbjörn's bit.
%%% COPIES %%%
diff --git a/lib/stdlib/src/supervisor.erl b/lib/stdlib/src/supervisor.erl
index 7d3c5a0e21..9f93747c3e 100644
--- a/lib/stdlib/src/supervisor.erl
+++ b/lib/stdlib/src/supervisor.erl
@@ -104,7 +104,9 @@
%%% SupName = {local, atom()} | {global, atom()}.
%%% ---------------------------------------------------
--type startlink_err() :: {'already_started', pid()} | 'shutdown' | term().
+-type startlink_err() :: {'already_started', pid()}
+ | {'shutdown', term()}
+ | term().
-type startlink_ret() :: {'ok', pid()} | 'ignore' | {'error', startlink_err()}.
-spec start_link(Module, Args) -> startlink_ret() when
@@ -221,8 +223,10 @@ cast(Supervisor, Req) ->
-type init_sup_name() :: sup_name() | 'self'.
--type stop_rsn() :: 'shutdown' | {'bad_return', {module(),'init', term()}}
- | {'bad_start_spec', term()} | {'start_spec', term()}
+-type stop_rsn() :: {'shutdown', term()}
+ | {'bad_return', {module(),'init', term()}}
+ | {'bad_start_spec', term()}
+ | {'start_spec', term()}
| {'supervisor_data', term()}.
-spec init({init_sup_name(), module(), [term()]}) ->
@@ -253,9 +257,9 @@ init_children(State, StartSpec) ->
case start_children(Children, SupName) of
{ok, NChildren} ->
{ok, State#state{children = NChildren}};
- {error, NChildren} ->
+ {error, NChildren, Reason} ->
terminate_children(NChildren, SupName),
- {stop, shutdown}
+ {stop, {shutdown, Reason}}
end;
Error ->
{stop, {start_spec, Error}}
@@ -275,9 +279,9 @@ init_dynamic(_State, StartSpec) ->
%% Func: start_children/2
%% Args: Children = [child_rec()] in start order
%% SupName = {local, atom()} | {global, atom()} | {pid(), Mod}
-%% Purpose: Start all children. The new list contains #child's
+%% Purpose: Start all children. The new list contains #child's
%% with pids.
-%% Returns: {ok, NChildren} | {error, NChildren}
+%% Returns: {ok, NChildren} | {error, NChildren, Reason}
%% NChildren = [child_rec()] in termination order (reversed
%% start order)
%%-----------------------------------------------------------------
@@ -293,7 +297,8 @@ start_children([Child|Chs], NChildren, SupName) ->
start_children(Chs, [Child#child{pid = Pid}|NChildren], SupName);
{error, Reason} ->
report_error(start_error, Reason, Child, SupName),
- {error, lists:reverse(Chs) ++ [Child | NChildren]}
+ {error, lists:reverse(Chs) ++ [Child | NChildren],
+ {failed_to_start_child,Child#child.name,Reason}}
end;
start_children([], NChildren, _SupName) ->
{ok, NChildren}.
@@ -793,7 +798,7 @@ restart(rest_for_one, Child, State) ->
case start_children(ChAfter2, State#state.name) of
{ok, ChAfter3} ->
{ok, State#state{children = ChAfter3 ++ ChBefore}};
- {error, ChAfter3} ->
+ {error, ChAfter3, _Reason} ->
NChild = Child#child{pid=restarting(Child#child.pid)},
NState = State#state{children = ChAfter3 ++ ChBefore},
{try_again, replace_child(NChild,NState)}
@@ -804,7 +809,7 @@ restart(one_for_all, Child, State) ->
case start_children(Children2, State#state.name) of
{ok, NChs} ->
{ok, State#state{children = NChs}};
- {error, NChs} ->
+ {error, NChs, _Reason} ->
NChild = Child#child{pid=restarting(Child#child.pid)},
NState = State#state{children = NChs},
{try_again, replace_child(NChild,NState)}
diff --git a/lib/stdlib/src/sys.erl b/lib/stdlib/src/sys.erl
index f34201604c..2d6287814e 100644
--- a/lib/stdlib/src/sys.erl
+++ b/lib/stdlib/src/sys.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -32,12 +32,25 @@
%% Types
%%-----------------------------------------------------------------
+-export_type([dbg_opt/0]).
+
-type name() :: pid() | atom() | {'global', atom()}.
-type system_event() :: {'in', Msg :: _}
| {'in', Msg :: _, From :: _}
| {'out', Msg :: _, To :: _}
| term().
--opaque dbg_opt() :: list().
+-opaque dbg_opt() :: {'trace', 'true'}
+ | {'log',
+ {N :: non_neg_integer(),
+ [{Event :: system_event(),
+ FuncState :: _,
+ FormFunc :: dbg_fun()}]}}
+ | {'statistics', {file:date_time(),
+ {'reductions', non_neg_integer()},
+ MessagesIn :: non_neg_integer(),
+ MessagesOut :: non_neg_integer()}}
+ | {'log_to_file', file:io_device()}
+ | {Func :: dbg_fun(), FuncState :: term()}.
-type dbg_fun() :: fun((FuncState :: _,
Event :: system_event(),
ProcState :: _) -> 'done' | (NewFuncState :: _)).
@@ -45,24 +58,22 @@
%%-----------------------------------------------------------------
%% System messages
%%-----------------------------------------------------------------
--spec suspend(Name) -> Void when
- Name :: name(),
- Void :: term().
+-spec suspend(Name) -> 'ok' when
+ Name :: name().
suspend(Name) -> send_system_msg(Name, suspend).
--spec suspend(Name, Timeout) -> Void when
+
+-spec suspend(Name, Timeout) -> 'ok' when
Name :: name(),
- Timeout :: timeout(),
- Void :: term().
+ Timeout :: timeout().
suspend(Name, Timeout) -> send_system_msg(Name, suspend, Timeout).
--spec resume(Name) -> Void when
- Name :: name(),
- Void :: term().
+-spec resume(Name) -> 'ok' when
+ Name :: name().
resume(Name) -> send_system_msg(Name, resume).
--spec resume(Name, Timeout) -> Void when
+
+-spec resume(Name, Timeout) -> 'ok' when
Name :: name(),
- Timeout :: timeout(),
- Void :: term().
+ Timeout :: timeout().
resume(Name, Timeout) -> send_system_msg(Name, resume, Timeout).
-spec get_status(Name) -> Status when
@@ -71,9 +82,10 @@ resume(Name, Timeout) -> send_system_msg(Name, resume, Timeout).
SItem :: (PDict :: [{Key :: term(), Value :: term()}])
| (SysState :: 'running' | 'suspended')
| (Parent :: pid())
- | (Dbg :: dbg_opt())
+ | (Dbg :: [dbg_opt()])
| (Misc :: term()).
get_status(Name) -> send_system_msg(Name, get_status).
+
-spec get_status(Name, Timeout) -> Status when
Name :: name(),
Timeout :: timeout(),
@@ -81,7 +93,7 @@ get_status(Name) -> send_system_msg(Name, get_status).
SItem :: (PDict :: [{Key :: term(), Value :: term()}])
| (SysState :: 'running' | 'suspended')
| (Parent :: pid())
- | (Dbg :: dbg_opt())
+ | (Dbg :: [dbg_opt()])
| (Misc :: term()).
get_status(Name, Timeout) -> send_system_msg(Name, get_status, Timeout).
@@ -93,6 +105,7 @@ get_status(Name, Timeout) -> send_system_msg(Name, get_status, Timeout).
Reason :: term().
change_code(Name, Mod, Vsn, Extra) ->
send_system_msg(Name, {change_code, Mod, Vsn, Extra}).
+
-spec change_code(Name, Module, OldVsn, Extra, Timeout) ->
'ok' | {error, Reason} when
Name :: name(),
@@ -189,35 +202,33 @@ no_debug(Name) -> send_system_msg(Name, {debug, no_debug}).
Timeout :: timeout().
no_debug(Name, Timeout) -> send_system_msg(Name, {debug, no_debug}, Timeout).
--spec install(Name, FuncSpec) -> Void when
+-spec install(Name, FuncSpec) -> 'ok' when
Name :: name(),
FuncSpec :: {Func, FuncState},
Func :: dbg_fun(),
- FuncState :: term(),
- Void :: term().
+ FuncState :: term().
install(Name, {Func, FuncState}) ->
send_system_msg(Name, {debug, {install, {Func, FuncState}}}).
--spec install(Name, FuncSpec, Timeout) -> Void when
+
+-spec install(Name, FuncSpec, Timeout) -> 'ok' when
Name :: name(),
FuncSpec :: {Func, FuncState},
Func :: dbg_fun(),
FuncState :: term(),
- Timeout :: timeout(),
- Void :: term().
+ Timeout :: timeout().
install(Name, {Func, FuncState}, Timeout) ->
send_system_msg(Name, {debug, {install, {Func, FuncState}}}, Timeout).
--spec remove(Name, Func) -> Void when
+-spec remove(Name, Func) -> 'ok' when
Name :: name(),
- Func :: dbg_fun(),
- Void :: term().
+ Func :: dbg_fun().
remove(Name, Func) ->
send_system_msg(Name, {debug, {remove, Func}}).
--spec remove(Name, Func, Timeout) -> Void when
+
+-spec remove(Name, Func, Timeout) -> 'ok' when
Name :: name(),
Func :: dbg_fun(),
- Timeout :: timeout(),
- Void :: term().
+ Timeout :: timeout().
remove(Name, Func, Timeout) ->
send_system_msg(Name, {debug, {remove, Func}}, Timeout).
@@ -243,18 +254,13 @@ mfa(Name, {change_code, Mod, Vsn, Extra}) ->
{sys, change_code, [Name, Mod, Vsn, Extra]};
mfa(Name, Atom) ->
{sys, Atom, [Name]}.
+
mfa(Name, Req, Timeout) ->
{M, F, A} = mfa(Name, Req),
{M, F, A ++ [Timeout]}.
%%-----------------------------------------------------------------
%% Func: handle_system_msg/6
-%% Args: Msg ::= term()
-%% From ::= {pid(),Ref} but don't count on that
-%% Parent ::= pid()
-%% Module ::= atom()
-%% Debug ::= [debug_opts()]
-%% Misc ::= term()
%% Purpose: Used by a process module that wishes to take care of
%% system messages. The process receives a {system, From,
%% Msg} message, and passes the Msg to this function.
@@ -266,14 +272,14 @@ mfa(Name, Req, Timeout) ->
%% The Module must export system_continue/3, system_terminate/4
%% and format_status/2 for status information.
%%-----------------------------------------------------------------
--spec handle_system_msg(Msg, From, Parent, Module, Debug, Misc) -> Void when
+-spec handle_system_msg(Msg, From, Parent, Module, Debug, Misc) ->
+ no_return() when
Msg :: term(),
From :: {pid(), Tag :: _},
Parent :: pid(),
Module :: module(),
Debug :: [dbg_opt()],
- Misc :: term(),
- Void :: term().
+ Misc :: term().
handle_system_msg(Msg, From, Parent, Module, Debug, Misc) ->
handle_system_msg(running, Msg, From, Parent, Module, Debug, Misc, false).
@@ -292,10 +298,6 @@ handle_system_msg(SysState, Msg, From, Parent, Mod, Debug, Misc, Hib) ->
%%-----------------------------------------------------------------
%% Func: handle_debug/4
-%% Args: Debug ::= [debug_opts()]
-%% Func ::= {M,F} | fun() arity 3
-%% State ::= term()
-%% Event ::= {in, Msg} | {in, Msg, From} | {out, Msg, To} | term()
%% Purpose: Called by a process that wishes to debug an event.
%% Func is a formatting function, called as Func(Device, Event).
%% Returns: [debug_opts()]
@@ -451,6 +453,7 @@ print_event(Dev, {Event, State, FormFunc}) ->
FormFunc(Dev, Event, State).
init_stat() -> {erlang:localtime(), process_info(self(), reductions), 0, 0}.
+
get_stat({Time, {reductions, Reds}, In, Out}) ->
{reductions, Reds2} = process_info(self(), reductions),
[{start_time, Time}, {current_time, erlang:localtime()},
@@ -490,9 +493,8 @@ get_debug2(Item, Debug, Default) ->
_ -> Default
end.
--spec print_log(Debug) -> Void when
- Debug :: [dbg_opt()],
- Void :: term().
+-spec print_log(Debug) -> 'ok' when
+ Debug :: [dbg_opt()].
print_log(Debug) ->
{_N, Logs} = get_debug(log, Debug, {0, []}),
lists:foreach(fun print_event/1,
@@ -509,8 +511,6 @@ close_log_file(Debug) ->
%%-----------------------------------------------------------------
%% Func: debug_options/1
-%% Args: [trace|log|{log,N}|statistics|{log_to_file, FileName}|
-%% {install, {Func, FuncState}}]
%% Purpose: Initiate a debug structure. Called by a process that
%% wishes to initiate the debug structure without the
%% system messages.
@@ -519,7 +519,11 @@ close_log_file(Debug) ->
-spec debug_options(Options) -> [dbg_opt()] when
Options :: [Opt],
- Opt :: 'trace' | 'log' | 'statistics' | {'log_to_file', FileName}
+ Opt :: 'trace'
+ | 'log'
+ | {'log', pos_integer()}
+ | 'statistics'
+ | {'log_to_file', FileName}
| {'install', FuncSpec},
FileName :: file:name(),
FuncSpec :: {Func, FuncState},
@@ -527,6 +531,7 @@ close_log_file(Debug) ->
FuncState :: term().
debug_options(Options) ->
debug_options(Options, []).
+
debug_options([trace | T], Debug) ->
debug_options(T, install_debug(trace, true, Debug));
debug_options([log | T], Debug) ->
diff --git a/lib/stdlib/src/win32reg.erl b/lib/stdlib/src/win32reg.erl
index 598e77ffdc..48a7e262be 100644
--- a/lib/stdlib/src/win32reg.erl
+++ b/lib/stdlib/src/win32reg.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -25,6 +25,8 @@
expand/1,
format_error/1]).
+-export_type([reg_handle/0]).
+
%% Key handles (always open).
-define(hkey_classes_root, 16#80000000).
-define(hkey_current_user, 16#80000001).
diff --git a/lib/stdlib/src/zip.erl b/lib/stdlib/src/zip.erl
index c82c8159b6..c383540db7 100644
--- a/lib/stdlib/src/zip.erl
+++ b/lib/stdlib/src/zip.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -1017,7 +1017,7 @@ cd_file_header_from_lh_and_pos(LH, Pos) ->
file_name_length = FileNameLength,
extra_field_length = ExtraFieldLength,
file_comment_length = 0, % FileCommentLength,
- disk_num_start = 1, % DiskNumStart,
+ disk_num_start = 0, % DiskNumStart,
internal_attr = 0, % InternalAttr,
external_attr = 0, % ExternalAttr,
local_header_offset = Pos}.
diff --git a/lib/stdlib/test/Makefile b/lib/stdlib/test/Makefile
index 29b8e28d3a..6aa09d7bd0 100644
--- a/lib/stdlib/test/Makefile
+++ b/lib/stdlib/test/Makefile
@@ -23,7 +23,6 @@ MODULES= \
dummy_via \
edlin_expand_SUITE \
epp_SUITE \
- erl_eval_helper \
erl_eval_SUITE \
erl_expand_records_SUITE \
erl_internal_SUITE \
diff --git a/lib/stdlib/test/base64_SUITE.erl b/lib/stdlib/test/base64_SUITE.erl
index c64a961ffa..b28df94221 100644
--- a/lib/stdlib/test/base64_SUITE.erl
+++ b/lib/stdlib/test/base64_SUITE.erl
@@ -1,7 +1,8 @@
+%% -*- coding: utf-8 -*-
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -20,7 +21,6 @@
-module(base64_SUITE).
-include_lib("common_test/include/ct.hrl").
--include("test_server_line.hrl").
%% Test server specific exports
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
@@ -33,7 +33,7 @@
mime_decode_to_string/1, roundtrip/1]).
init_per_testcase(_, Config) ->
- Dog = test_server:timetrap(?t:minutes(2)),
+ Dog = test_server:timetrap(?t:minutes(4)),
NewConfig = lists:keydelete(watchdog, 1, Config),
[{watchdog, Dog} | NewConfig].
@@ -180,7 +180,7 @@ mime_decode(Config) when is_list(Config) ->
<<"o">> = base64:mime_decode(<<"b=w=====">>),
%% Test misc white space and illegals with embedded padding
<<"one">> = base64:mime_decode(<<" b~2=\r\n5()l===">>),
- <<"on">> = base64:mime_decode(<<"\tb =2\"�4=�= ==">>),
+ <<"on">> = base64:mime_decode(<<"\tb =2\"¤4=¤= ==">>),
<<"o">> = base64:mime_decode(<<"\nb=w=====">>),
%% Two pads
<<"Aladdin:open sesame">> =
@@ -189,7 +189,7 @@ mime_decode(Config) when is_list(Config) ->
<<"Hello World!!">> = base64:mime_decode(<<"SGVsb)(G8gV29ybGQ=h IQ= =">>),
%% No pad
<<"Aladdin:open sesam">> =
- base64:mime_decode("QWxhZGRpbjpvcG�\")(VuIHNlc2Ft"),
+ base64:mime_decode("QWxhZGRpbjpvcG¤\")(VuIHNlc2Ft"),
%% Encoded base 64 strings may be divided by non base 64 chars.
%% In this cases whitespaces.
<<"0123456789!@#0^&*();:<>,. []{}">> =
@@ -223,7 +223,7 @@ mime_decode_to_string(Config) when is_list(Config) ->
"o" = base64:mime_decode_to_string(<<"b=w=====">>),
%% Test misc white space and illegals with embedded padding
"one" = base64:mime_decode_to_string(<<" b~2=\r\n5()l===">>),
- "on" = base64:mime_decode_to_string(<<"\tb =2\"�4=�= ==">>),
+ "on" = base64:mime_decode_to_string(<<"\tb =2\"¤4=¤= ==">>),
"o" = base64:mime_decode_to_string(<<"\nb=w=====">>),
%% Two pads
"Aladdin:open sesame" =
@@ -232,7 +232,7 @@ mime_decode_to_string(Config) when is_list(Config) ->
"Hello World!!" = base64:mime_decode_to_string(<<"SGVsb)(G8gV29ybGQ=h IQ= =">>),
%% No pad
"Aladdin:open sesam" =
- base64:mime_decode_to_string("QWxhZGRpbjpvcG�\")(VuIHNlc2Ft"),
+ base64:mime_decode_to_string("QWxhZGRpbjpvcG¤\")(VuIHNlc2Ft"),
%% Encoded base 64 strings may be divided by non base 64 chars.
%% In this cases whitespaces.
"0123456789!@#0^&*();:<>,. []{}" =
diff --git a/lib/stdlib/test/binary_module_SUITE.erl b/lib/stdlib/test/binary_module_SUITE.erl
index bac59a3107..9b6f628aa9 100644
--- a/lib/stdlib/test/binary_module_SUITE.erl
+++ b/lib/stdlib/test/binary_module_SUITE.erl
@@ -1,7 +1,8 @@
+%% -*- coding: utf-8 -*-
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -1331,9 +1332,9 @@ one_random_number(N) ->
one_random(N) ->
M = ((N - 1) rem 68) + 1,
element(M,{$a,$b,$c,$d,$e,$f,$g,$h,$i,$j,$k,$l,$m,$n,$o,$p,$q,$r,$s,$t,
- $u,$v,$w,$x,$y,$z,$�,$�,$�,$A,$B,$C,$D,$E,$F,$G,$H,
- $I,$J,$K,$L,$M,$N,$O,$P,$Q,$R,$S,$T,$U,$V,$W,$X,$Y,$Z,$�,
- $�,$�,$0,$1,$2,$3,$4,$5,$6,$7,$8,$9}).
+ $u,$v,$w,$x,$y,$z,$å,$ä,$ö,$A,$B,$C,$D,$E,$F,$G,$H,
+ $I,$J,$K,$L,$M,$N,$O,$P,$Q,$R,$S,$T,$U,$V,$W,$X,$Y,$Z,$Å,
+ $Ä,$Ö,$0,$1,$2,$3,$4,$5,$6,$7,$8,$9}).
random_number({Min,Max}) -> % Min and Max are *length* of number in
% decimal positions
diff --git a/lib/stdlib/test/dets_SUITE.erl b/lib/stdlib/test/dets_SUITE.erl
index 6f77cff2b9..66799f4d05 100644
--- a/lib/stdlib/test/dets_SUITE.erl
+++ b/lib/stdlib/test/dets_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -38,7 +38,7 @@
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2,
- not_run/1, newly_started/1, basic_v8/1, basic_v9/1,
+ newly_started/1, basic_v8/1, basic_v9/1,
open_v8/1, open_v9/1, sets_v8/1, sets_v9/1, bags_v8/1,
bags_v9/1, duplicate_bags_v8/1, duplicate_bags_v9/1,
access_v8/1, access_v9/1, dirty_mark/1, dirty_mark2/1,
@@ -95,27 +95,25 @@ end_per_testcase(_Case, _Config) ->
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- case os:type() of
- vxworks -> [not_run];
- _ ->
- [basic_v8, basic_v9, open_v8, open_v9, sets_v8, sets_v9,
- bags_v8, bags_v9, duplicate_bags_v8, duplicate_bags_v9,
- newly_started, open_file_v8, open_file_v9,
- init_table_v8, init_table_v9, repair_v8, repair_v9,
- access_v8, access_v9, oldbugs_v8, oldbugs_v9,
- unsafe_assumptions, truncated_segment_array_v8,
- truncated_segment_array_v9, dirty_mark, dirty_mark2,
- bag_next_v8, bag_next_v9, hash_v8b_v8c, phash, fold_v8,
- fold_v9, fixtable_v8, fixtable_v9, match_v8, match_v9,
- select_v8, select_v9, update_counter, badarg,
- cache_sets_v8, cache_sets_v9, cache_bags_v8,
- cache_bags_v9, cache_duplicate_bags_v8,
- cache_duplicate_bags_v9, otp_4208, otp_4989,
- many_clients, otp_4906, otp_5402, simultaneous_open,
- insert_new, repair_continuation, otp_5487, otp_6206,
- otp_6359, otp_4738, otp_7146, otp_8070, otp_8856, otp_8898,
- otp_8899, otp_8903, otp_8923, otp_9282, otp_9607]
- end.
+ [
+ basic_v8, basic_v9, open_v8, open_v9, sets_v8, sets_v9,
+ bags_v8, bags_v9, duplicate_bags_v8, duplicate_bags_v9,
+ newly_started, open_file_v8, open_file_v9,
+ init_table_v8, init_table_v9, repair_v8, repair_v9,
+ access_v8, access_v9, oldbugs_v8, oldbugs_v9,
+ unsafe_assumptions, truncated_segment_array_v8,
+ truncated_segment_array_v9, dirty_mark, dirty_mark2,
+ bag_next_v8, bag_next_v9, hash_v8b_v8c, phash, fold_v8,
+ fold_v9, fixtable_v8, fixtable_v9, match_v8, match_v9,
+ select_v8, select_v9, update_counter, badarg,
+ cache_sets_v8, cache_sets_v9, cache_bags_v8,
+ cache_bags_v9, cache_duplicate_bags_v8,
+ cache_duplicate_bags_v9, otp_4208, otp_4989,
+ many_clients, otp_4906, otp_5402, simultaneous_open,
+ insert_new, repair_continuation, otp_5487, otp_6206,
+ otp_6359, otp_4738, otp_7146, otp_8070, otp_8856, otp_8898,
+ otp_8899, otp_8903, otp_8923, otp_9282, otp_9607
+ ].
groups() ->
[].
@@ -132,10 +130,6 @@ init_per_group(_GroupName, Config) ->
end_per_group(_GroupName, Config) ->
Config.
-not_run(suite) -> [];
-not_run(Conf) when is_list(Conf) ->
- {comment, "Not runnable VxWorks/NFS"}.
-
newly_started(doc) ->
["OTP-3621"];
newly_started(suite) ->
@@ -1949,7 +1943,7 @@ match(Config, Version) ->
%% match, badarg
MSpec = [{'_',[],['$_']}],
?line check_badarg(catch dets:match(no_table, '_'),
- dets, safe_fixtable, [no_table,true]),
+ dets, match, [no_table,'_']),
?line check_badarg(catch dets:match(T, '_', not_a_number),
dets, match, [T,'_',not_a_number]),
?line {EC1, _} = dets:select(T, MSpec, 1),
@@ -1958,7 +1952,7 @@ match(Config, Version) ->
%% match_object, badarg
?line check_badarg(catch dets:match_object(no_table, '_'),
- dets, safe_fixtable, [no_table,true]),
+ dets, match_object, [no_table,'_']),
?line check_badarg(catch dets:match_object(T, '_', not_a_number),
dets, match_object, [T,'_',not_a_number]),
?line {EC2, _} = dets:select(T, MSpec, 1),
@@ -2127,7 +2121,7 @@ select(Config, Version) ->
%% badarg
MSpec = [{'_',[],['$_']}],
?line check_badarg(catch dets:select(no_table, MSpec),
- dets, safe_fixtable, [no_table,true]),
+ dets, select, [no_table,MSpec]),
?line check_badarg(catch dets:select(T, <<17>>),
dets, select, [T,<<17>>]),
?line check_badarg(catch dets:select(T, []),
@@ -2330,7 +2324,7 @@ badarg(Config) when is_list(Config) ->
%% match_delete
?line check_badarg(catch dets:match_delete(no_table, '_'),
- dets, safe_fixtable, [no_table,true]),
+ dets, match_delete, [no_table,'_']),
%% delete_all_objects
?line check_badarg(catch dets:delete_all_objects(no_table),
@@ -2339,17 +2333,19 @@ badarg(Config) when is_list(Config) ->
%% select_delete
MSpec = [{'_',[],['$_']}],
?line check_badarg(catch dets:select_delete(no_table, MSpec),
- dets, safe_fixtable, [no_table,true]),
+ dets, select_delete, [no_table,MSpec]),
?line check_badarg(catch dets:select_delete(T, <<17>>),
dets, select_delete, [T, <<17>>]),
%% traverse, fold
- ?line check_badarg(catch dets:traverse(no_table, fun(_) -> continue end),
- dets, safe_fixtable, [no_table,true]),
- ?line check_badarg(catch dets:foldl(fun(_, A) -> A end, [], no_table),
- dets, safe_fixtable, [no_table,true]),
- ?line check_badarg(catch dets:foldr(fun(_, A) -> A end, [], no_table),
- dets, safe_fixtable, [no_table,true]),
+ TF = fun(_) -> continue end,
+ ?line check_badarg(catch dets:traverse(no_table, TF),
+ dets, traverse, [no_table,TF]),
+ FF = fun(_, A) -> A end,
+ ?line check_badarg(catch dets:foldl(FF, [], no_table),
+ dets, foldl, [FF,[],no_table]),
+ ?line check_badarg(catch dets:foldr(FF, [], no_table),
+ dets, foldl, [FF,[],no_table]),
%% close
?line ok = dets:close(T),
diff --git a/lib/stdlib/test/dict_SUITE.erl b/lib/stdlib/test/dict_SUITE.erl
index c46fc47b34..df9c769c67 100644
--- a/lib/stdlib/test/dict_SUITE.erl
+++ b/lib/stdlib/test/dict_SUITE.erl
@@ -53,7 +53,7 @@ end_per_group(_GroupName, Config) ->
init_per_testcase(_Case, Config) ->
- ?line Dog = ?t:timetrap(?t:minutes(5)),
+ Dog = ?t:timetrap(?t:minutes(5)),
[{watchdog,Dog}|Config].
end_per_testcase(_Case, Config) ->
@@ -65,22 +65,22 @@ create(Config) when is_list(Config) ->
test_all(fun create_1/1).
create_1(M) ->
- ?line D0 = M:empty(),
- ?line [] = M:to_list(D0),
- ?line 0 = M:size(D0),
+ D0 = M(empty, []),
+ [] = M(to_list, D0),
+ 0 = M(size, D0),
D0.
store(Config) when is_list(Config) ->
test_all([{0,132},{253,258},{510,514}], fun store_1/2).
store_1(List, M) ->
- ?line D0 = M:from_list(List),
+ D0 = M(from_list, List),
%% Make sure that we get the same result by inserting
%% elements one at the time.
- ?line D1 = foldl(fun({K,V}, Dict) -> M:enter(K, V, Dict) end,
- M:empty(), List),
- ?line true = M:equal(D0, D1),
+ D1 = foldl(fun({K,V}, Dict) -> M(enter, {K,V,Dict}) end,
+ M(empty, []), List),
+ true = M(equal, {D0,D1}),
D0.
%%%
@@ -98,7 +98,7 @@ dict_mods() ->
[Orddict,Dict,Gb].
test_all(Tester) ->
- ?line Pids = [spawn_tester(M, Tester) || M <- dict_mods()],
+ Pids = [spawn_tester(M, Tester) || M <- dict_mods()],
collect_all(Pids, []).
spawn_tester(M, Tester) ->
@@ -106,7 +106,7 @@ spawn_tester(M, Tester) ->
spawn_link(fun() ->
random:seed(1, 2, 42),
S = Tester(M),
- Res = {M:size(S),lists:sort(M:to_list(S))},
+ Res = {M(size, S),lists:sort(M(to_list, S))},
Parent ! {result,self(),Res}
end).
diff --git a/lib/stdlib/test/dict_test_lib.erl b/lib/stdlib/test/dict_test_lib.erl
index 92a75dad89..7167014310 100644
--- a/lib/stdlib/test/dict_test_lib.erl
+++ b/lib/stdlib/test/dict_test_lib.erl
@@ -17,67 +17,48 @@
%% %CopyrightEnd%
%%
--module(dict_test_lib, [Mod,Equal]).
+-module(dict_test_lib).
--export([module/0,equal/2,empty/0,size/1,to_list/1,from_list/1,
- enter/3,delete/2,lookup/2]).
+-export([new/2]).
-module() ->
- Mod.
-
-equal(X, Y) ->
- Equal(X, Y).
+new(Mod, Eq) ->
+ fun (enter, {K,V,D}) -> enter(Mod, K, V, D);
+ (empty, []) -> empty(Mod);
+ (equal, {D1,D2}) -> Eq(D1, D2);
+ (from_list, L) -> from_list(Mod, L);
+ (module, []) -> Mod;
+ (size, D) -> Mod:size(D);
+ (to_list, D) -> to_list(Mod, D)
+ end.
-empty() ->
+empty(Mod) ->
case erlang:function_exported(Mod, new, 0) of
false -> Mod:empty();
true -> Mod:new()
end.
-size(S) ->
- Mod:size(S).
-
-to_list(S) ->
- Mod:to_list(S).
+to_list(Mod, D) ->
+ Mod:to_list(D).
-from_list(S) ->
+from_list(Mod, L) ->
case erlang:function_exported(Mod, from_orddict, 1) of
false ->
- Mod:from_list(S);
+ Mod:from_list(L);
true ->
%% The gb_trees module has no from_list/1 function.
%%
%% The keys in S are not unique. To make sure
%% that we pick the same key/value pairs as
%% dict/orddict, first convert the list to an orddict.
- Orddict = orddict:from_list(S),
+ Orddict = orddict:from_list(L),
Mod:from_orddict(Orddict)
end.
%% Store new value into dictionary or update previous value in dictionary.
-enter(Key, Val, Dict) ->
+enter(Mod, Key, Val, Dict) ->
case erlang:function_exported(Mod, store, 3) of
false ->
Mod:enter(Key, Val, Dict);
true ->
Mod:store(Key, Val, Dict)
end.
-
-%% Delete an EXISTING key.
-delete(Key, Dict) ->
- case erlang:function_exported(Mod, delete, 2) of
- true -> Mod:delete(Key, Dict);
- false -> Mod:erase(Key, Dict)
- end.
-
-%% -> none | {value,Value}
-lookup(Key, Dict) ->
- case erlang:function_exported(Mod, lookup, 2) of
- false ->
- case Mod:find(Key, Dict) of
- error -> none;
- {ok,Value} -> {value,Value}
- end;
- true ->
- Mod:lookup(Key, Dict)
- end.
diff --git a/lib/stdlib/test/digraph_SUITE.erl b/lib/stdlib/test/digraph_SUITE.erl
index 1d1326d60e..ed01b32a59 100644
--- a/lib/stdlib/test/digraph_SUITE.erl
+++ b/lib/stdlib/test/digraph_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -400,7 +400,7 @@ sane1(G) ->
lists:foreach(
fun(V) ->
InEs = digraph:in_edges(G, V),
- %% Nu har man *alla* inkanter f�r V
+ %% *All* in-edoges of V
lists:foreach(
fun(E) ->
case digraph:edge(G, E) of
diff --git a/lib/stdlib/test/epp_SUITE.erl b/lib/stdlib/test/epp_SUITE.erl
index f79414db49..606bbbcbb2 100644
--- a/lib/stdlib/test/epp_SUITE.erl
+++ b/lib/stdlib/test/epp_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -25,7 +25,7 @@
variable_1/1, otp_4870/1, otp_4871/1, otp_5362/1,
pmod/1, not_circular/1, skip_header/1, otp_6277/1, otp_7702/1,
otp_8130/1, overload_mac/1, otp_8388/1, otp_8470/1, otp_8503/1,
- otp_8562/1, otp_8665/1, otp_8911/1]).
+ otp_8562/1, otp_8665/1, otp_8911/1, otp_10302/1]).
-export([epp_parse_erl_form/2]).
@@ -67,7 +67,7 @@ all() ->
{group, variable}, otp_4870, otp_4871, otp_5362, pmod,
not_circular, skip_header, otp_6277, otp_7702, otp_8130,
overload_mac, otp_8388, otp_8470, otp_8503, otp_8562,
- otp_8665, otp_8911].
+ otp_8665, otp_8911, otp_10302].
groups() ->
[{upcase_mac, [], [upcase_mac_1, upcase_mac_2]},
@@ -582,12 +582,13 @@ otp_8130(suite) ->
otp_8130(Config) when is_list(Config) ->
true = os:putenv("epp_inc1", "stdlib"),
Ts = [{otp_8130_1,
- %% The scanner handles UNICODE in a special way. Hopefully
- %% temporarily.
<<"-define(M(A), ??A). "
"t() -> "
- " \"{ 34 , [ $1 , 2730 ] , \\\"34\\\" , X . a , 2730 }\" = "
- " ?M({34,\"1\\x{aaa}\",\"34\",X.a,$\\x{aaa}}), ok. ">>,
+ " L = \"{ 34 , \\\"1\\\\x{AAA}\\\" , \\\"34\\\" , X . a , $\\\\x{AAA} }\", "
+ " R = ?M({34,\"1\\x{aaa}\",\"34\",X.a,$\\x{aaa}}),"
+ " Lt = erl_scan:string(L, 1, [unicode]),"
+ " Rt = erl_scan:string(R, 1, [unicode]),"
+ " Lt = Rt, ok. ">>,
ok},
{otp_8130_2,
@@ -1236,6 +1237,13 @@ otp_8911(doc) ->
otp_8911(suite) ->
[];
otp_8911(Config) when is_list(Config) ->
+ case test_server:is_cover() of
+ true ->
+ {skip, "Testing cover, so can not run when cover is already running"};
+ false ->
+ do_otp_8911(Config)
+ end.
+do_otp_8911(Config) ->
?line {ok, CWD} = file:get_cwd(),
?line ok = file:set_cwd(?config(priv_dir, Config)),
@@ -1277,6 +1285,75 @@ otp_8665(Config) when is_list(Config) ->
?line [] = compile(Config, Cs),
ok.
+otp_10302(doc) ->
+ "OTP-10302. Unicode characters scanner/parser.";
+otp_10302(suite) ->
+ [];
+otp_10302(Config) when is_list(Config) ->
+ %% Two messages (one too many). Keeps otp_4871 happy.
+ Cs = [{otp_8562,
+ <<"%% coding: utf-8\n \n \x{E4}">>,
+ {errors,[{3,epp,cannot_parse},
+ {3,file_io_server,invalid_unicode}],[]}}
+ ],
+ [] = compile(Config, Cs),
+ Dir = ?config(priv_dir, Config),
+ File = filename:join(Dir, "otp_10302.erl"),
+ utf8 = encoding("coding: utf-8", File),
+ utf8 = encoding("coding: UTF-8", File),
+ latin1 = encoding("coding: Latin-1", File),
+ latin1 = encoding("coding: latin-1", File),
+ none = encoding_com("coding: utf-8", File),
+ none = encoding_com("\n\n%% coding: utf-8", File),
+ none = encoding_nocom("\n\n coding: utf-8", File),
+ utf8 = encoding_com("\n%% coding: utf-8", File),
+ utf8 = encoding_nocom("\n coding: utf-8", File),
+ none = encoding("coding: \nutf-8", File),
+ latin1 = encoding("Encoding : latin-1", File),
+ utf8 = encoding("ccoding: UTF-8", File),
+ utf8 = encoding("coding= utf-8", File),
+ utf8 = encoding_com(" %% coding= utf-8", File),
+ utf8 = encoding("coding = utf-8", File),
+ none = encoding("coding: utf-16 coding: utf-8", File), %first is bad
+ none = encoding("Coding: utf-8", File), %capital c
+ utf8 = encoding("-*- coding: utf-8 -*-", File),
+ utf8 = encoding("-*-coding= utf-8-*-", File),
+ utf8 = encoding("codingcoding= utf-8", File),
+ ok = prefix("coding: utf-8", File, utf8),
+
+ "coding: latin-1" = epp:encoding_to_string(latin1),
+ "coding: utf-8" = epp:encoding_to_string(utf8),
+ true = lists:member(epp:default_encoding(), [latin1, utf8]),
+
+ ok.
+
+prefix(S, File, Enc) ->
+ prefix(0, S, File, Enc).
+
+prefix(100, _S, _File, _) ->
+ ok;
+prefix(N, S, File, Enc) ->
+ Enc = encoding(lists:duplicate(N, $\s) ++ S, File),
+ prefix(N+1, S, File, Enc).
+
+encoding(Enc, File) ->
+ E = encoding_com("%% " ++ Enc, File),
+ none = encoding_com(Enc, File),
+ E = encoding_nocom(Enc, File).
+
+encoding_com(Enc, File) ->
+ ok = file:write_file(File, Enc),
+ {ok, Fd} = file:open(File, [read]),
+ E = epp:set_encoding(Fd),
+ ok = file:close(Fd),
+ E = epp:read_encoding(File).
+
+encoding_nocom(Enc, File) ->
+ ok = file:write_file(File, Enc),
+ {ok, Fd} = file:open(File, [read]),
+ ok = file:close(Fd),
+ epp:read_encoding(File, [{in_comment_only, false}]).
+
check(Config, Tests) ->
eval_tests(Config, fun check_test/2, Tests).
diff --git a/lib/stdlib/test/erl_eval_SUITE.erl b/lib/stdlib/test/erl_eval_SUITE.erl
index b0c7d562d5..04d49770cb 100644
--- a/lib/stdlib/test/erl_eval_SUITE.erl
+++ b/lib/stdlib/test/erl_eval_SUITE.erl
@@ -216,13 +216,13 @@ guard_4(doc) ->
guard_4(suite) ->
[];
guard_4(Config) when is_list(Config) ->
- ?line check(fun() -> if {erlang,'+'}(3,a) -> true ; true -> false end end,
- "if {erlang,'+'}(3,a) -> true ; true -> false end.",
- false),
- ?line check(fun() -> if {erlang,is_integer}(3) -> true ; true -> false end
- end,
- "if {erlang,is_integer}(3) -> true ; true -> false end.",
- true),
+ check(fun() -> if erlang:'+'(3,a) -> true ; true -> false end end,
+ "if erlang:'+'(3,a) -> true ; true -> false end.",
+ false),
+ check(fun() -> if erlang:is_integer(3) -> true ; true -> false end
+ end,
+ "if erlang:is_integer(3) -> true ; true -> false end.",
+ true),
?line check(fun() -> [X || X <- [1,2,3], erlang:is_integer(X)] end,
"[X || X <- [1,2,3], erlang:is_integer(X)].",
[1,2,3]),
@@ -230,11 +230,11 @@ guard_4(Config) when is_list(Config) ->
end,
"if is_atom(is_integer(a)) -> true ; true -> false end.",
true),
- ?line check(fun() -> if {erlang,is_atom}({erlang,is_integer}(a)) -> true;
- true -> false end end,
- "if {erlang,is_atom}({erlang,is_integer}(a)) -> true; "
- "true -> false end.",
- true),
+ check(fun() -> if erlang:is_atom(erlang:is_integer(a)) -> true;
+ true -> false end end,
+ "if erlang:is_atom(erlang:is_integer(a)) -> true; "
+ "true -> false end.",
+ true),
?line check(fun() -> if is_atom(3+a) -> true ; true -> false end end,
"if is_atom(3+a) -> true ; true -> false end.",
false),
@@ -1077,11 +1077,6 @@ do_funs(LFH, EFH) ->
concat(["begin F1 = fun(F,N) -> apply(", M,
",count_down,[F, N]) end, F1(F1,1000) end."]),
0, ['F1'], LFH, EFH),
- ?line check(fun() -> F1 = fun(F,N) -> {?MODULE,count_down}(F,N)
- end, F1(F1, 1000) end,
- concat(["begin F1 = fun(F,N) -> {", M,
- ",count_down}(F, N) end, F1(F1,1000) end."]),
- 0, ['F1'], LFH, EFH),
?line check(fun() -> F = fun(F,N) when N > 0 -> apply(F,[F,N-1]);
(_F,0) -> ok end,
F(F, 1000)
@@ -1113,11 +1108,11 @@ do_funs(LFH, EFH) ->
true = {2,3} == F(2) end,
"begin F = fun(X) -> A = 1+X, {X,A} end,
true = {2,3} == F(2) end.", true, ['F'], LFH, EFH),
- ?line check(fun() -> F = fun(X) -> {erlang,'+'}(X,2) end,
- true = 3 == F(1) end,
- "begin F = fun(X) -> {erlang,'+'}(X,2) end,"
- " true = 3 == F(1) end.", true, ['F'],
- LFH, EFH),
+ check(fun() -> F = fun(X) -> erlang:'+'(X,2) end,
+ true = 3 == F(1) end,
+ "begin F = fun(X) -> erlang:'+'(X,2) end,"
+ " true = 3 == F(1) end.", true, ['F'],
+ LFH, EFH),
?line check(fun() -> F = fun(X) -> byte_size(X) end,
?MODULE:do_apply(F,<<"hej">>) end,
concat(["begin F = fun(X) -> size(X) end,",
@@ -1165,24 +1160,6 @@ do_funs(LFH, EFH) ->
concat(["begin F = fun(F, N) -> [", M,
":count_down(F,N) || X <-[1]] end, F(F,2) end."]),
[[[0]]], ['F'], LFH, EFH),
-
- %% Tests for a bug found by the Dialyzer - used to crash.
- case test_server:is_native(erl_eval) of
- true ->
- %% Parameterized modules are not supported by HiPE.
- ok;
- false ->
- check(fun() -> Pmod = erl_eval_helper:new(42), Pmod:add(5) end,
- "begin Pmod = erl_eval_helper:new(42), Pmod:add(5) end.",
- 47,
- ['Pmod'], LFH, EFH),
- check(fun() -> Pmod = erl_eval_helper:new(42),
- B = Pmod:add(7), B end,
- "begin Pmod = erl_eval_helper:new(42), "
- "B = Pmod:add(7), B end.",
- 49,
- ['B','Pmod'], LFH, EFH)
- end,
ok.
count_down(F, N) when N > 0 ->
diff --git a/lib/stdlib/test/erl_expand_records_SUITE.erl b/lib/stdlib/test/erl_expand_records_SUITE.erl
index 01cdb92d7b..94b4397a9c 100644
--- a/lib/stdlib/test/erl_expand_records_SUITE.erl
+++ b/lib/stdlib/test/erl_expand_records_SUITE.erl
@@ -35,7 +35,7 @@
init_per_group/2,end_per_group/2,
init_per_testcase/2, end_per_testcase/2]).
--export([abstract_module/1, attributes/1, expr/1, guard/1,
+-export([attributes/1, expr/1, guard/1,
init/1, pattern/1, strict/1, update/1,
otp_5915/1, otp_7931/1, otp_5990/1,
otp_7078/1, otp_7101/1]).
@@ -55,7 +55,7 @@ end_per_testcase(_Case, _Config) ->
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [abstract_module, attributes, expr, guard, init,
+ [attributes, expr, guard, init,
pattern, strict, update, {group, tickets}].
groups() ->
@@ -75,40 +75,12 @@ end_per_group(_GroupName, Config) ->
Config.
-abstract_module(doc) ->
- "Compile an abstract module.";
-abstract_module(suite) -> [];
-abstract_module(Config) when is_list(Config) ->
- %% erl_expand_records does not handle abstract modules. But anyway...
- File = filename("param.erl", Config),
- Beam = filename("param.beam", Config),
- Test = <<"-module(param, [A, B]).
-
- -export([args/1]).
-
- args(C) ->
- X = local(C),
- Z = new(A, B),
- {X, Z}.
-
- local(C) ->
- module_info(C).
- ">>,
-
- ?line ok = file:write_file(File, Test),
- ?line {ok, param} = compile:file(File, [{outdir,?privdir}]),
-
- ?line ok = file:delete(File),
- ?line ok = file:delete(Beam),
- ok.
-
attributes(doc) ->
"Import module and functions.";
attributes(suite) -> [];
attributes(Config) when is_list(Config) ->
Ts = [
- <<"-import(erl_expand_records_SUITE).
- -import(lists, [append/2, reverse/1]).
+ <<"-import(lists, [append/2, reverse/1]).
-record(r, {a,b}).
@@ -157,13 +129,13 @@ expr(Config) when is_list(Config) ->
One = 1 = fun f/1(1),
2 = fun(X) -> X end(One + One),
3 = fun exprec_test:f/1(3),
- 4 = {exprec_test,f}(4),
- 5 = ''.f(5),
+ 4 = exprec_test:f(4),
+ 5 = f(5),
L = receive
{a,message,L0} ->
L0
end,
- case catch a.b.c:foo(bar) of
+ case catch a:foo(bar) of
{'EXIT', _} -> ok
end,
_ = receive %Suppress warning.
@@ -197,7 +169,7 @@ guard(suite) -> [];
guard(Config) when is_list(Config) ->
File = filename("guard.erl", Config),
Beam = filename("guard.beam", Config),
- Test = <<"-module(guard, [A, B]).
+ Test = <<"-module(guard).
-export([t/1]).
@@ -263,8 +235,7 @@ pattern(doc) ->
pattern(suite) -> [];
pattern(Config) when is_list(Config) ->
Ts = [
- <<"-import(erl_expand_records_SUITE).
- -import(lists, [append/2, reverse/1]).
+ <<"-import(lists, [append/2, reverse/1]).
-record(r, {a,b}).
@@ -292,10 +263,10 @@ pattern(Config) when is_list(Config) ->
21 = t(#r{a = #r{}}),
22 = t(2),
23 = t(#r{a = #r{}, b = b}),
- 24 = t(a.b.c),
+ 24 = t(abc),
ok.
- t(a.b.c) ->
+ t(abc) ->
24;
t($a) ->
2;
diff --git a/lib/stdlib/test/erl_lint_SUITE.erl b/lib/stdlib/test/erl_lint_SUITE.erl
index dcd622b984..564f27a512 100644
--- a/lib/stdlib/test/erl_lint_SUITE.erl
+++ b/lib/stdlib/test/erl_lint_SUITE.erl
@@ -50,7 +50,8 @@
unsafe_vars_try/1,
guard/1, otp_4886/1, otp_4988/1, otp_5091/1, otp_5276/1, otp_5338/1,
otp_5362/1, otp_5371/1, otp_7227/1, otp_5494/1, otp_5644/1, otp_5878/1,
- otp_5917/1, otp_6585/1, otp_6885/1, export_all/1,
+ otp_5917/1, otp_6585/1, otp_6885/1, otp_10436/1,
+ export_all/1,
bif_clash/1,
behaviour_basic/1, behaviour_multiple/1,
otp_7550/1,
@@ -80,7 +81,7 @@ all() ->
unsafe_vars, unsafe_vars2, unsafe_vars_try, guard,
otp_4886, otp_4988, otp_5091, otp_5276, otp_5338,
otp_5362, otp_5371, otp_7227, otp_5494, otp_5644,
- otp_5878, otp_5917, otp_6585, otp_6885, export_all,
+ otp_5878, otp_5917, otp_6585, otp_6885, otp_10436, export_all,
bif_clash, behaviour_basic, behaviour_multiple,
otp_7550, otp_8051, format_warn, {group, on_load},
too_many_arguments].
@@ -1307,41 +1308,30 @@ guard(Config) when is_list(Config) ->
foo;
t3(A) when erlang:is_record(A, {apa}) ->
foo;
- t3(A) when {erlang,is_record}(A, {apa}) ->
- foo;
t3(A) when is_record(A, {apa}, 1) ->
foo;
t3(A) when erlang:is_record(A, {apa}, 1) ->
foo;
- t3(A) when {erlang,is_record}(A, {apa}, 1) ->
- foo;
t3(A) when is_record(A, apa, []) ->
foo;
t3(A) when erlang:is_record(A, apa, []) ->
foo;
- t3(A) when {erlang,is_record}(A, apa, []) ->
- foo;
t3(A) when record(A, apa) ->
foo;
t3(A) when is_record(A, apa) ->
foo;
t3(A) when erlang:is_record(A, apa) ->
- foo;
- t3(A) when {erlang,is_record}(A, apa) ->
foo.
">>,
[warn_unused_vars, nowarn_obsolete_guard],
{errors,[{2,erl_lint,illegal_guard_expr},
- {4,erl_lint,illegal_guard_expr},
- {6,erl_lint,illegal_guard_expr},
- {8,erl_lint,illegal_guard_expr},
+ {4,erl_lint,illegal_guard_expr},
+ {6,erl_lint,illegal_guard_expr},
+ {8,erl_lint,illegal_guard_expr},
{10,erl_lint,illegal_guard_expr},
{12,erl_lint,illegal_guard_expr},
- {14,erl_lint,illegal_guard_expr},
- {16,erl_lint,illegal_guard_expr},
- {18,erl_lint,illegal_guard_expr},
- {20,erl_lint,illegal_guard_expr}],
- []}},
+ {14,erl_lint,illegal_guard_expr}],
+ []}},
{guard6,
<<"-record(apa,{a=a,b=foo:bar()}).
apa() ->
@@ -1742,7 +1732,7 @@ otp_5362(Config) when is_list(Config) ->
{otp_5362_2,
<<"-export([inline/0]).
- -import(lists.foo, [a/1,b/1]). % b/1 is not used
+ -import(lists, [a/1,b/1]). % b/1 is not used
-compile([{inline,{inl,7}}]). % undefined
-compile([{inline,[{inl,17}]}]). % undefined
@@ -1774,7 +1764,7 @@ otp_5362(Config) when is_list(Config) ->
{6,erl_lint,{bad_inline,{inl,17}}},
{11,erl_lint,{undefined_function,{fipp,0}}},
{22,erl_lint,{bad_nowarn_unused_function,{and_not_used,2}}}],
- [{3,erl_lint,{unused_import,{{b,1},'lists.foo'}}},
+ [{3,erl_lint,{unused_import,{{b,1},lists}}},
{9,erl_lint,{unused_function,{foop,0}}},
{19,erl_lint,{unused_function,{not_used,0}}},
{23,erl_lint,{unused_function,{and_not_used,1}}}]}},
@@ -2197,27 +2187,9 @@ otp_5878(Config) when is_list(Config) ->
?line [] = run(Config, Ts),
Abstr = <<"-module(lint_test, [A, B]).
-
- -export([args/1]).
-
- -record(r, {a = A, b = THIS}). % A and THIS are unbound
-
- %% param:args(compile,param:new(1,2)).
-
- args(C) ->
- X = local(C),
- Z = new(A, B),
- F = fun(THIS) -> {x, A} end, % THIS unused and shadowed
- {X, Z, THIS, F, #r{}}.
-
- local(C) ->
- module_info(C).
">>,
- ?line {error,[{5,erl_lint,{unbound_var,'A'}},
- {5,erl_lint,{unbound_var,'THIS'}}],
- [{12,erl_lint,{unused_var,'THIS'}},
- {12,erl_lint,{shadowed_var,'THIS','fun'}}]}
- = run_test2(Config, Abstr, [warn_unused_record]),
+ {errors,[{1,erl_lint,pmod_unsupported}],[]} =
+ run_test2(Config, Abstr, [warn_unused_record]),
QLC1 = <<"-module(lint_test).
-include_lib(\"stdlib/include/qlc.hrl\").
@@ -2397,6 +2369,28 @@ otp_6885(Config) when is_list(Config) ->
[]} = run_test2(Config, Ts, []),
ok.
+otp_10436(doc) ->
+ "OTP-6885. Warnings for opaque types.";
+otp_10436(suite) -> [];
+otp_10436(Config) when is_list(Config) ->
+ Ts = <<"-module(otp_10436).
+ -export_type([t1/0]).
+ -opaque t1() :: {i, integer()}.
+ -opaque t2() :: {a, atom()}.
+ ">>,
+ {warnings,[{4,erl_lint,{not_exported_opaque,{t2,0}}},
+ {4,erl_lint,{unused_type,{t2,0}}}]} =
+ run_test2(Config, Ts, []),
+ Ts2 = <<"-module(otp_10436_2).
+ -export_type([t1/0, t2/0]).
+ -opaque t1() :: term().
+ -opaque t2() :: any().
+ ">>,
+ {warnings,[{3,erl_lint,{underspecified_opaque,{t1,0}}},
+ {4,erl_lint,{underspecified_opaque,{t2,0}}}]} =
+ run_test2(Config, Ts2, []),
+ ok.
+
export_all(doc) ->
"OTP-7392. Warning for export_all.";
export_all(Config) when is_list(Config) ->
@@ -2845,10 +2839,10 @@ otp_8051(doc) ->
otp_8051(Config) when is_list(Config) ->
Ts = [{otp_8051,
<<"-opaque foo() :: bar().
+ -export_type([foo/0]).
">>,
[],
- {error,[{1,erl_lint,{undefined_type,{bar,0}}}],
- [{1,erl_lint,{unused_type,{foo,0}}}]}}],
+ {errors,[{1,erl_lint,{undefined_type,{bar,0}}}],[]}}],
?line [] = run(Config, Ts),
ok.
diff --git a/lib/stdlib/test/erl_pp_SUITE.erl b/lib/stdlib/test/erl_pp_SUITE.erl
index 64853ca078..37be61d665 100644
--- a/lib/stdlib/test/erl_pp_SUITE.erl
+++ b/lib/stdlib/test/erl_pp_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -40,7 +40,7 @@
init_per_testcase/2, end_per_testcase/2]).
-export([ func/1, call/1, recs/1, try_catch/1, if_then/1,
- receive_after/1, bits/1, head_tail/1, package/1,
+ receive_after/1, bits/1, head_tail/1,
cond1/1, block/1, case1/1, ops/1, messages/1,
old_mnemosyne_syntax/1,
import_export/1, misc_attrs/1,
@@ -48,7 +48,8 @@
neg_indent/1,
otp_6321/1, otp_6911/1, otp_6914/1, otp_8150/1, otp_8238/1,
- otp_8473/1, otp_8522/1, otp_8567/1, otp_8664/1, otp_9147/1]).
+ otp_8473/1, otp_8522/1, otp_8567/1, otp_8664/1, otp_9147/1,
+ otp_10302/1]).
%% Internal export.
-export([ehook/6]).
@@ -74,12 +75,12 @@ all() ->
groups() ->
[{expr, [],
[func, call, recs, try_catch, if_then, receive_after,
- bits, head_tail, package, cond1, block, case1, ops,
+ bits, head_tail, cond1, block, case1, ops,
messages, old_mnemosyne_syntax]},
{attributes, [], [misc_attrs, import_export]},
{tickets, [],
[otp_6321, otp_6911, otp_6914, otp_8150, otp_8238,
- otp_8473, otp_8522, otp_8567, otp_8664, otp_9147]}].
+ otp_8473, otp_8522, otp_8567, otp_8664, otp_9147, otp_10302]}].
init_per_suite(Config) ->
Config.
@@ -438,9 +439,6 @@ bits(Config) when is_list(Config) ->
?line ok = pp_expr(<<"<<{a,b}/binary>>">>),
?line ok = pp_expr(<<"<<{foo:bar(),b}/binary>>">>),
?line ok = pp_expr(<<"<<(foo:bar()):(foo:bar())/binary>>">>),
- ?line ok = pp_expr(<<"<<(foo.bar)/binary>>">>),
- ?line ok = pp_expr(<<"<<(foo.bar):all/binary>>">>),
- ?line ok = pp_expr(<<"<<(foo.bar):(foo.bar)/binary>>">>),
ok.
head_tail(suite) ->
@@ -462,17 +460,6 @@ head_tail(Config) when is_list(Config) ->
?line compile(Config, Ts),
ok.
-package(suite) ->
- [];
-package(Config) when is_list(Config) ->
- Ts = [{package_1,
- <<"t() -> a.b:foo().">>},
- {package_2,
- <<"t() -> .lists:sort([]).">>}
- ],
- ?line compile(Config, Ts),
- ok.
-
cond1(suite) ->
[];
cond1(Config) when is_list(Config) ->
@@ -550,29 +537,9 @@ messages(Config) when is_list(Config) ->
?line true = "\n" =:= lists:flatten(erl_pp:form({eof,0})),
ok.
-old_mnemosyne_syntax(suite) ->
- [];
old_mnemosyne_syntax(Config) when is_list(Config) ->
- %% Since we have kept the 'query' syntax and ':-' token,
+ %% Since we have kept the ':-' token,
%% better test that we can pretty print it.
- Q = {'query',6,
- {lc,6,
- {var,6,'X'},
- [{generate,6,
- {var,6,'X'},
- {call,6,{atom,6,table},[{atom,6,tab}]}},
- {match,7,
- {record_field,7,{var,7,'X'},{atom,7,foo}},
- {atom,7,bar}}]}},
- ?line "query\n"
- " [ \n" % extra space...
- " X ||\n"
- " X <- table(tab),\n"
- " X.foo = bar\n"
- " ]\n"
- "end" =
- lists:flatten(erl_pp:expr(Q)),
-
R = {rule,12,sales,2,
[{clause,12,
[{var,12,'E'},{atom,12,employee}],
@@ -614,13 +581,11 @@ misc_attrs(suite) ->
[];
misc_attrs(Config) when is_list(Config) ->
?line ok = pp_forms(<<"-module(m). ">>),
- ?line ok = pp_forms(<<"-module(m.p, [A,B]). ">>),
?line ok = pp_forms(<<"-module(m, [Aafjlksfjdlsjflsdfjlsdjflkdsfjlk,"
"Blsjfdlslfjsdf]). ">>),
?line ok = pp_forms(<<"-export([]). ">>),
?line ok = pp_forms(<<"-export([foo/2, bar/0]). ">>),
?line ok = pp_forms(<<"-export([bar/0]). ">>),
- ?line ok = pp_forms(<<"-import(.lists). ">>),
?line ok = pp_forms(<<"-import(lists, []). ">>),
?line ok = pp_forms(<<"-import(lists, [map/2]). ">>),
?line ok = pp_forms(<<"-import(lists, [map/2, foreach/2]). ">>),
@@ -634,8 +599,12 @@ misc_attrs(Config) when is_list(Config) ->
hook(suite) ->
[];
hook(Config) when is_list(Config) ->
+ F = fun(H) -> H end,
+ do_hook(F).
+
+do_hook(HookFun) ->
Lc = parse_expr(binary_to_list(<<"[X || X <- [1,2,3]].">>)),
- H = fun hook/4,
+ H = HookFun(fun hook/4),
Expr = {call,0,{atom,0,fff},[{foo,Lc},{foo,Lc},{foo,Lc}]},
EChars = lists:flatten(erl_pp:expr(Expr, 0, H)),
Call = {call,0,{atom,0,foo},[Lc]},
@@ -692,7 +661,7 @@ hook(Config) when is_list(Config) ->
GChars2 = erl_pp:guard(G2),
?line true = GChars =:= lists:flatten(GChars2),
- EH = {?MODULE, ehook, [foo,bar]},
+ EH = HookFun({?MODULE, ehook, [foo,bar]}),
XEChars = erl_pp:expr(Expr, -1, EH),
?line true = remove_indentation(EChars) =:= lists:flatten(XEChars),
XEChars2 = erl_pp:expr(Expr, EH),
@@ -1068,6 +1037,43 @@ otp_9147(Config) when is_list(Config) ->
string:tokens(binary_to_list(Bin), "\n")),
ok.
+otp_10302(doc) ->
+ "OTP-10302. Unicode characters scanner/parser.";
+otp_10302(suite) -> [];
+otp_10302(Config) when is_list(Config) ->
+ Ts = [{uni_1,
+ <<"t() -> <<(<<\"abc\\x{aaa}\">>):3/binary>>.">>}
+ ],
+ compile(Config, Ts),
+ ok = pp_expr(<<"$\\x{aaa}">>),
+ ok = pp_expr(<<"\"1\\x{aaa}\"">>),
+ ok = pp_expr(<<"<<<<\"hej\">>/binary>>">>),
+ ok = pp_expr(<<"<< <<\"1\\x{aaa}\">>/binary>>">>),
+
+ U = [{encoding,unicode}],
+
+ do_hook(fun(H) -> [{hook,H}] end),
+ do_hook(fun(H) -> [{hook,H}]++U end),
+
+ ok = pp_expr(<<"$\\x{aaa}">>, [{hook,fun hook/4}]),
+
+ Opts = [{hook, fun unicode_hook/4},{encoding,unicode}],
+ Lc = parse_expr("[X || X <- [\"\x{400}\",\"\xFF\"]]."),
+ Expr = {call,0,{atom,0,fff},[{foo,{foo,Lc}},{foo,{foo,Lc}}]},
+ EChars = lists:flatten(erl_pp:expr(Expr, 0, Opts)),
+ Call = {call,0,{atom,0,foo},[{call,0,{atom,0,foo},[Lc]}]},
+ Expr2 = {call,0,{atom,0,fff},[Call,Call]},
+ EChars2 = erl_pp:exprs([Expr2], U),
+ EChars = lists:flatten(EChars2),
+ [$\x{400},$\x{400}] = [C || C <- EChars, C > 255],
+
+ ok = pp_forms(<<"function() -> {\"\x{400}\",$\x{400}}. "/utf8>>, U),
+ ok = pp_forms("function() -> {\"\x{400}\",$\x{400}}. ", []),
+ ok.
+
+unicode_hook({foo,E}, I, P, H) ->
+ erl_pp:expr({call,0,{atom,0,foo},[E]}, I, P, H).
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
compile(Config, Tests) ->
@@ -1137,9 +1143,11 @@ flat_expr(Expr) ->
pp_forms(Bin) ->
pp_forms(Bin, none).
-pp_forms(Bin, Hook) ->
- PP1 = (catch parse_and_pp_forms(binary_to_list(Bin), Hook)),
- PP2 = (catch parse_and_pp_forms(PP1, Hook)),
+pp_forms(Bin, Options) when is_binary(Bin) ->
+ pp_forms(to_list(Bin, Options), Options);
+pp_forms(List, Options) when is_list(List) ->
+ PP1 = (catch parse_and_pp_forms(List, Options)),
+ PP2 = (catch parse_and_pp_forms(PP1, Options)),
case PP1 =:= PP2 of % same line numbers
true ->
test_max_line(PP1);
@@ -1147,8 +1155,8 @@ pp_forms(Bin, Hook) ->
not_ok
end.
-parse_and_pp_forms(String, Hook) ->
- lists:append(lists:map(fun(AF) -> erl_pp:form(AF, Hook)
+parse_and_pp_forms(String, Options) ->
+ lists:append(lists:map(fun(AF) -> erl_pp:form(AF, Options)
end, parse_forms(String))).
parse_forms(Chars) ->
@@ -1158,7 +1166,7 @@ parse_forms(Chars) ->
parse_forms2([], _Cont, _Line, Forms) ->
lists:reverse(Forms);
parse_forms2(String, Cont0, Line, Forms) ->
- case erl_scan:tokens(Cont0, String, Line) of
+ case erl_scan:tokens(Cont0, String, Line, [unicode]) of
{done, {ok, Tokens, EndLine}, Chars} ->
{ok, Form} = erl_parse:parse_form(Tokens),
parse_forms2(Chars, [], EndLine, [Form | Forms]);
@@ -1174,10 +1182,12 @@ pp_expr(Bin) ->
pp_expr(Bin, none).
%% Final dot is added.
-pp_expr(Bin, Hook) ->
- PP1 = (catch parse_and_pp_expr(binary_to_list(Bin), 0, Hook)),
- PPneg = (catch parse_and_pp_expr(binary_to_list(Bin), -1, Hook)),
- PP2 = (catch parse_and_pp_expr(PPneg, 0, Hook)),
+pp_expr(Bin, Options) when is_binary(Bin) ->
+ pp_expr(to_list(Bin, Options), Options);
+pp_expr(List, Options) when is_list(List) ->
+ PP1 = (catch parse_and_pp_expr(List, 0, Options)),
+ PPneg = (catch parse_and_pp_expr(List, -1, Options)),
+ PP2 = (catch parse_and_pp_expr(PPneg, 0, Options)),
if
PP1 =:= PP2 -> % same line numbers
case
@@ -1192,15 +1202,24 @@ pp_expr(Bin, Hook) ->
not_ok
end.
-parse_and_pp_expr(String, Indent, Hook) ->
+parse_and_pp_expr(String, Indent, Options) ->
StringDot = lists:flatten(String) ++ ".",
- erl_pp:expr(parse_expr(StringDot), Indent, Hook).
+ erl_pp:expr(parse_expr(StringDot), Indent, Options).
parse_expr(Chars) ->
- {ok, Tokens, _} = erl_scan:string(Chars),
+ {ok, Tokens, _} = erl_scan:string(Chars, 1, [unicode]),
{ok, [Expr]} = erl_parse:parse_exprs(Tokens),
Expr.
+to_list(Bin, Options) when is_list(Options) ->
+ case proplists:get_value(encoding, Options) of
+ unicode -> unicode:characters_to_list(Bin);
+ encoding -> binary_to_list(Bin);
+ undefined -> binary_to_list(Bin)
+ end;
+to_list(Bin, _Hook) ->
+ binary_to_list(Bin).
+
test_new_line(String) ->
case string:chr(String, $\n) of
0 -> ok;
diff --git a/lib/stdlib/test/erl_scan_SUITE.erl b/lib/stdlib/test/erl_scan_SUITE.erl
index 4298b2c701..9a6b2f8f34 100644
--- a/lib/stdlib/test/erl_scan_SUITE.erl
+++ b/lib/stdlib/test/erl_scan_SUITE.erl
@@ -1,7 +1,8 @@
+%% -*- coding: utf-8 -*-
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -20,7 +21,7 @@
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2]).
--export([ error_1/1, error_2/1, iso88591/1, otp_7810/1]).
+-export([ error_1/1, error_2/1, iso88591/1, otp_7810/1, otp_10302/1]).
-import(lists, [nth/2,flatten/1]).
-import(io_lib, [print/1]).
@@ -59,7 +60,7 @@ end_per_testcase(_Case, Config) ->
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [{group, error}, iso88591, otp_7810].
+ [{group, error}, iso88591, otp_7810, otp_10302].
groups() ->
[{error, [], [error_1, error_2]}].
@@ -131,10 +132,10 @@ iso88591(Config) when is_list(Config) ->
?line ok =
case catch begin
%% Some atom and variable names
- V1s = [$�,$�,$�,$�],
- V2s = [$N,$�,$r],
- A1s = [$h,$�,$r],
- A2s = [$�,$r,$e],
+ V1s = [$Á,$á,$é,$ë],
+ V2s = [$N,$ä,$r],
+ A1s = [$h,$ä,$r],
+ A2s = [$ö,$r,$e],
%% Test parsing atom and variable characters.
{ok,Ts1,_} = erl_scan:string(V1s ++ " " ++ V2s ++
"\327" ++
@@ -196,7 +197,7 @@ otp_7810(Config) when is_list(Config) ->
reserved_words() ->
L = ['after', 'begin', 'case', 'try', 'cond', 'catch',
'andalso', 'orelse', 'end', 'fun', 'if', 'let', 'of',
- 'query', 'receive', 'when', 'bnot', 'not', 'div',
+ 'receive', 'when', 'bnot', 'not', 'div',
'rem', 'band', 'and', 'bor', 'bxor', 'bsl', 'bsr',
'or', 'xor'],
[begin
@@ -214,8 +215,8 @@ atoms() ->
?line test_string("'a b'", [{atom,1,'a b'}]),
?line test_string("a", [{atom,1,a}]),
?line test_string("a@2", [{atom,1,a@2}]),
- ?line test_string([39,65,200,39], [{atom,1,'A�'}]),
- ?line test_string("�rlig �sten", [{atom,1,�rlig},{atom,1,�sten}]),
+ ?line test_string([39,65,200,39], [{atom,1,'AÈ'}]),
+ ?line test_string("ärlig östen", [{atom,1,ärlig},{atom,1,östen}]),
?line {ok,[{atom,_,'$a'}],{1,6}} =
erl_scan:string("'$\\a'", {1,1}),
?line test("'$\\a'"),
@@ -289,7 +290,7 @@ errors() ->
?line {error,{1,erl_scan,{string,$","str"}},1} = %"
erl_scan:string("\"str"), %"
?line {error,{1,erl_scan,char},1} = erl_scan:string("$"),
- ?line test_string([34,65,200,34], [{string,1,"A�"}]),
+ ?line test_string([34,65,200,34], [{string,1,"AÈ"}]),
?line test_string("\\", [{'\\',1}]),
?line {'EXIT',_} =
(catch {foo, erl_scan:string('$\\a', {1,1})}), % type error
@@ -354,7 +355,7 @@ dots() ->
{".\n", {ok,[{dot,1}],2}},
{".%", {ok,[{dot,1}],1}},
{".\210",{ok,[{dot,1}],1}},
- {".% �h",{ok,[{dot,1}],1}},
+ {".% öh",{ok,[{dot,1}],1}},
{".%\n", {ok,[{dot,1}],2}},
{".$", {error,{1,erl_scan,char},1}},
{".$\\", {error,{1,erl_scan,char},1}},
@@ -369,7 +370,7 @@ dots() ->
?line [{column,1},{length,1},{line,1},{text,"."}] =
erl_scan:token_info(T2, [column, length, line, text]),
?line {ok,[{dot,_}=T3],{1,6}} =
- erl_scan:string(".% �h", {1,1}, text),
+ erl_scan:string(".% öh", {1,1}, text),
?line [{column,1},{length,1},{line,1},{text,"."}] =
erl_scan:token_info(T3, [column, length, line, text]),
?line {error,{{1,2},erl_scan,char},{1,3}} =
@@ -472,11 +473,11 @@ chars() ->
variables() ->
- ?line test_string(" \237_Aou�eiy��", [{var,1,'_Aou�eiy��'}]),
+ ?line test_string(" \237_Aouåeiyäö", [{var,1,'_Aouåeiyäö'}]),
?line test_string("A_b_c@", [{var,1,'A_b_c@'}]),
?line test_string("V@2", [{var,1,'V@2'}]),
- ?line test_string("ABD�", [{var,1,'ABD�'}]),
- ?line test_string("�rlig �sten", [{var,1,'�rlig'},{var,1,'�sten'}]),
+ ?line test_string("ABDÀ", [{var,1,'ABDÀ'}]),
+ ?line test_string("Ärlig Östen", [{var,1,'Ärlig'},{var,1,'Östen'}]),
ok.
eof() ->
@@ -823,7 +824,7 @@ unicode() ->
?line {ok,[{char,1,1}],1} = erl_scan:string([$$,$\\,$^,1089]),
?line {error,{1,erl_scan,Error},1} = erl_scan:string("\"qa\x{aaa}"),
- ?line "unterminated string starting with \"qa\\x{AAA}\"" =
+ ?line "unterminated string starting with \"qa"++[2730]++"\"" =
erl_scan:format_error(Error),
?line {error,{{1,1},erl_scan,_},{1,11}} =
erl_scan:string("\"qa\\x{aaa}",{1,1}),
@@ -887,9 +888,10 @@ unicode() ->
{char,_,$d},{']',_}],{1,8}} = erl_scan:string(Str1, {1,1}),
?line test(Str1),
Comment = "%% "++[1089],
- ?line {ok,[{comment,1,[$%,$%,$\s,1089]}],1} =
+ %% Returned a comment In R15B03:
+ {error,{1,erl_scan,{illegal,character}},1} =
erl_scan:string(Comment, 1, return),
- ?line {ok,[{comment,_,[$%,$%,$\s,1089]}],{1,5}} =
+ {error,{{1,1},erl_scan,{illegal,character}},{1,5}} =
erl_scan:string(Comment, {1,1}, return),
ok.
@@ -958,6 +960,182 @@ more_chars() ->
erl_scan:string("$\\xg", {1,1}),
ok.
+otp_10302(doc) ->
+ "OTP-10302. Unicode characters scanner/parser.";
+otp_10302(suite) ->
+ [];
+otp_10302(Config) when is_list(Config) ->
+ %% From unicode():
+ {error,{1,erl_scan,{illegal,atom}},1} =
+ erl_scan:string("'a"++[1089]++"b'", 1, unicode),
+ {error,{{1,1},erl_scan,{illegal,atom}},{1,12}} =
+ erl_scan:string("'qa\\x{aaa}'",{1,1},unicode),
+
+ {ok,[{char,1,1089}],1} = erl_scan:string([$$,1089], 1, unicode),
+ {ok,[{char,1,1089}],1} = erl_scan:string([$$,$\\,1089],1,unicode),
+
+ Qs = "$\\x{aaa}",
+ {ok,[{char,1,2730}],1} = erl_scan:string(Qs,1,unicode),
+ {ok,[Q2],{1,9}} = erl_scan:string(Qs,{1,1},[unicode,text]),
+ [{category,char},{column,1},{length,8},
+ {line,1},{symbol,16#aaa},{text,Qs}] =
+ erl_scan:token_info(Q2),
+
+ Tags = [category, column, length, line, symbol, text],
+
+ U1 = "\"\\x{aaa}\"",
+ {ok,[T1],{1,10}} = erl_scan:string(U1, {1,1}, [unicode,text]),
+ [{category,string},{column,1},{length,9},{line,1},
+ {symbol,[16#aaa]},{text,U1}] = erl_scan:token_info(T1, Tags),
+
+ U2 = "\"\\x41\\x{fff}\\x42\"",
+ {ok,[{string,1,[65,4095,66]}],1} = erl_scan:string(U2, 1, unicode),
+
+ U3 = "\"a\n\\x{fff}\n\"",
+ {ok,[{string,1,[97,10,4095,10]}],3} = erl_scan:string(U3, 1,unicode),
+
+ U4 = "\"\\^\n\\x{aaa}\\^\n\"",
+ {ok,[{string,1,[10,2730,10]}],3} = erl_scan:string(U4, 1,[unicode]),
+
+ Str1 = "\"ab" ++ [1089] ++ "cd\"",
+ {ok,[{string,1,[97,98,1089,99,100]}],1} =
+ erl_scan:string(Str1,1,unicode),
+ {ok,[{string,{1,1},[97,98,1089,99,100]}],{1,8}} =
+ erl_scan:string(Str1, {1,1},unicode),
+
+ OK1 = 16#D800-1,
+ OK2 = 16#DFFF+1,
+ OK3 = 16#FFFE-1,
+ OK4 = 16#FFFF+1,
+ OKL = [OK1,OK2,OK3,OK4],
+
+ Illegal1 = 16#D800,
+ Illegal2 = 16#DFFF,
+ Illegal3 = 16#FFFE,
+ Illegal4 = 16#FFFF,
+ IllegalL = [Illegal1,Illegal2,Illegal3,Illegal4],
+
+ [{ok,[{comment,1,[$%,$%,$\s,OK]}],1} =
+ erl_scan:string("%% "++[OK], 1, [unicode,return]) ||
+ OK <- OKL],
+ {ok,[{comment,_,[$%,$%,$\s,OK1]}],{1,5}} =
+ erl_scan:string("%% "++[OK1], {1,1}, [unicode,return]),
+ [{error,{1,erl_scan,{illegal,character}},1} =
+ erl_scan:string("%% "++[Illegal], 1, [unicode,return]) ||
+ Illegal <- IllegalL],
+ {error,{{1,1},erl_scan,{illegal,character}},{1,5}} =
+ erl_scan:string("%% "++[Illegal1], {1,1}, [unicode,return]),
+
+ [{ok,[],1} = erl_scan:string("%% "++[OK], 1, [unicode]) ||
+ OK <- OKL],
+ {ok,[],{1,5}} = erl_scan:string("%% "++[OK1], {1,1}, [unicode]),
+ [{error,{1,erl_scan,{illegal,character}},1} =
+ erl_scan:string("%% "++[Illegal], 1, [unicode]) ||
+ Illegal <- IllegalL],
+ {error,{{1,1},erl_scan,{illegal,character}},{1,5}} =
+ erl_scan:string("%% "++[Illegal1], {1,1}, [unicode]),
+
+ [{ok,[{string,{1,1},[OK]}],{1,4}} =
+ erl_scan:string("\""++[OK]++"\"",{1,1},unicode) ||
+ OK <- OKL],
+ [{error,{{1,2},erl_scan,{illegal,character}},{1,3}} =
+ erl_scan:string("\""++[OK]++"\"",{1,1},unicode) ||
+ OK <- IllegalL],
+
+ [{error,{{1,1},erl_scan,{illegal,character}},{1,2}} =
+ erl_scan:string([Illegal],{1,1},unicode) ||
+ Illegal <- IllegalL],
+
+ {ok,[{char,{1,1},OK1}],{1,3}} =
+ erl_scan:string([$$,OK1],{1,1},unicode),
+ {error,{{1,1},erl_scan,{illegal,character}},{1,2}} =
+ erl_scan:string([$$,Illegal1],{1,1},unicode),
+
+ {ok,[{char,{1,1},OK1}],{1,4}} =
+ erl_scan:string([$$,$\\,OK1],{1,1},unicode),
+ {error,{{1,1},erl_scan,{illegal,character}},{1,4}} =
+ erl_scan:string([$$,$\\,Illegal1],{1,1},unicode),
+
+ {ok,[{string,{1,1},[55295]}],{1,5}} =
+ erl_scan:string("\"\\"++[OK1]++"\"",{1,1},unicode),
+ {error,{{1,2},erl_scan,{illegal,character}},{1,4}} =
+ erl_scan:string("\"\\"++[Illegal1]++"\"",{1,1},unicode),
+
+ {ok,[{char,{1,1},OK1}],{1,10}} =
+ erl_scan:string("$\\x{D7FF}",{1,1},unicode),
+ {error,{{1,1},erl_scan,{illegal,character}},{1,10}} =
+ erl_scan:string("$\\x{D800}",{1,1},unicode),
+
+ %% Not erl_scan, but erl_parse.
+ {integer,0,1} = erl_parse:abstract(1),
+ Float = 3.14, {float,0,Float} = erl_parse:abstract(Float),
+ {nil,0} = erl_parse:abstract([]),
+ {bin,0,
+ [{bin_element,0,{integer,0,1},default,default},
+ {bin_element,0,{integer,0,2},default,default}]} =
+ erl_parse:abstract(<<1,2>>),
+ {cons,0,{tuple,0,[{atom,0,a}]},{atom,0,b}} =
+ erl_parse:abstract([{a} | b]),
+ {string,0,"str"} = erl_parse:abstract("str"),
+ {cons,0,
+ {integer,0,$a},
+ {cons,0,{integer,0,1024},{string,0,"c"}}} =
+ erl_parse:abstract("a"++[1024]++"c"),
+
+ Line = 17,
+ {integer,Line,1} = erl_parse:abstract(1, Line),
+ Float = 3.14, {float,Line,Float} = erl_parse:abstract(Float, Line),
+ {nil,Line} = erl_parse:abstract([], Line),
+ {bin,Line,
+ [{bin_element,Line,{integer,Line,1},default,default},
+ {bin_element,Line,{integer,Line,2},default,default}]} =
+ erl_parse:abstract(<<1,2>>, Line),
+ {cons,Line,{tuple,Line,[{atom,Line,a}]},{atom,Line,b}} =
+ erl_parse:abstract([{a} | b], Line),
+ {string,Line,"str"} = erl_parse:abstract("str", Line),
+ {cons,Line,
+ {integer,Line,$a},
+ {cons,Line,{integer,Line,1024},{string,Line,"c"}}} =
+ erl_parse:abstract("a"++[1024]++"c", Line),
+
+ Opts1 = [{line,17}],
+ {integer,Line,1} = erl_parse:abstract(1, Opts1),
+ Float = 3.14, {float,Line,Float} = erl_parse:abstract(Float, Opts1),
+ {nil,Line} = erl_parse:abstract([], Opts1),
+ {bin,Line,
+ [{bin_element,Line,{integer,Line,1},default,default},
+ {bin_element,Line,{integer,Line,2},default,default}]} =
+ erl_parse:abstract(<<1,2>>, Opts1),
+ {cons,Line,{tuple,Line,[{atom,Line,a}]},{atom,Line,b}} =
+ erl_parse:abstract([{a} | b], Opts1),
+ {string,Line,"str"} = erl_parse:abstract("str", Opts1),
+ {cons,Line,
+ {integer,Line,$a},
+ {cons,Line,{integer,Line,1024},{string,Line,"c"}}} =
+ erl_parse:abstract("a"++[1024]++"c", Opts1),
+
+ [begin
+ {integer,Line,1} = erl_parse:abstract(1, Opts2),
+ Float = 3.14, {float,Line,Float} = erl_parse:abstract(Float, Opts2),
+ {nil,Line} = erl_parse:abstract([], Opts2),
+ {bin,Line,
+ [{bin_element,Line,{integer,Line,1},default,default},
+ {bin_element,Line,{integer,Line,2},default,default}]} =
+ erl_parse:abstract(<<1,2>>, Opts2),
+ {cons,Line,{tuple,Line,[{atom,Line,a}]},{atom,Line,b}} =
+ erl_parse:abstract([{a} | b], Opts2),
+ {string,Line,"str"} = erl_parse:abstract("str", Opts2),
+ {string,Line,[97,1024,99]} =
+ erl_parse:abstract("a"++[1024]++"c", Opts2)
+ end || Opts2 <- [[{encoding,unicode},{line,Line}],
+ [{encoding,utf8},{line,Line}]]],
+
+ {cons,0,
+ {integer,0,97},
+ {cons,0,{integer,0,1024},{string,0,"c"}}} =
+ erl_parse:abstract("a"++[1024]++"c", [{encoding,latin1}]),
+ ok.
+
test_string(String, Expected) ->
{ok, Expected, _End} = erl_scan:string(String),
test(String).
diff --git a/lib/stdlib/test/escript_SUITE.erl b/lib/stdlib/test/escript_SUITE.erl
index 7ed1ee742a..3749d594f2 100644
--- a/lib/stdlib/test/escript_SUITE.erl
+++ b/lib/stdlib/test/escript_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -26,14 +26,17 @@
errors/1,
strange_name/1,
emulator_flags/1,
+ emulator_flags_no_shebang/1,
module_script/1,
beam_script/1,
archive_script/1,
+ archive_script_file_access/1,
epp/1,
create_and_extract/1,
foldl/1,
overflow/1,
- verify_sections/3
+ verify_sections/3,
+ unicode/1
]).
-include_lib("test_server/include/test_server.hrl").
@@ -43,8 +46,10 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[basic, errors, strange_name, emulator_flags,
+ emulator_flags_no_shebang,
module_script, beam_script, archive_script, epp,
- create_and_extract, foldl, overflow].
+ create_and_extract, foldl, overflow,
+ archive_script_file_access, unicode].
groups() ->
[].
@@ -62,7 +67,7 @@ end_per_group(_GroupName, Config) ->
Config.
init_per_testcase(_Case, Config) ->
- ?line Dog = ?t:timetrap(?t:minutes(2)),
+ ?line Dog = ?t:timetrap(?t:minutes(5)),
[{watchdog,Dog}|Config].
end_per_testcase(_Case, Config) ->
@@ -147,6 +152,21 @@ emulator_flags(Config) when is_list(Config) ->
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+emulator_flags_no_shebang(Config) when is_list(Config) ->
+ Data = ?config(data_dir, Config),
+ Dir = filename:absname(Data), %Get rid of trailing slash.
+ %% Need run_with_opts, to always use "escript" explicitly
+ ?line run_with_opts(Dir, "", "emulator_flags_no_shebang -arg1 arg2 arg3",
+ [<<"main:[\"-arg1\",\"arg2\",\"arg3\"]\n"
+ "nostick:[{nostick,[]}]\n"
+ "mnesia:[{mnesia,[\"dir\",\"a/directory\"]},{mnesia,[\"debug\",\"verbose\"]}]\n"
+ "ERL_FLAGS=false\n"
+ "unknown:[]\n"
+ "ExitCode:0">>]),
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Pick the source code from the emulator_flags script
%% Generate a new escript with a module header
@@ -356,7 +376,7 @@ beam_script(Config) when is_list(Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Create an archive file containing two entire applications plus two
%% alternate main modules. Generate a new escript containing the archive
-%% (with .app and .beam files and ) and the escript header.
+%% (with .app and .beam files and) and the escript header.
archive_script(Config) when is_list(Config) ->
%% Copy the orig files to priv_dir
@@ -464,6 +484,147 @@ archive_script(Config) when is_list(Config) ->
ok.
+%% Test the correction of OTP-10071
+%% The errors identified are
+%%
+%% a) If primary archive was named "xxx", then a file in the same
+%% directory named "xxxyyy" would be interpreted as a file named yyy
+%% inside the archive.
+%%
+%% b) erl_prim_loader did not correctly create and normalize absolute
+%% paths for primary archive and files inside it, so unless given
+%% with exact same path files inside the archive would not be
+%% found. E.g. if escript was started as ./xxx then "xxx/file"
+%% would not be found since erl_prim_loader would try to match
+%% /full/path/to/xxx with /full/path/to/./xxx. Same problem with
+%% ../. Also, the use of symlinks in the path to the archive would
+%% cause problems.
+%%
+%% c) Depending on how the primary archive was built,
+%% erl_prim_loader:list_dir/1 would sometimes return an empty string
+%% inside the file list. This was a virtual element representing the
+%% top directory of the archive. This shall not occur.
+%%
+archive_script_file_access(Config) when is_list(Config) ->
+ %% Copy the orig files to priv_dir
+ DataDir = ?config(data_dir, Config),
+ PrivDir = ?config(priv_dir, Config),
+
+ MainMod = "archive_script_file_access",
+ MainSrc = MainMod ++ ".erl",
+ MainBeam = MainMod ++ ".beam",
+
+ Archive = filename:join([PrivDir, "archive_script_file_access.zip"]),
+ ?line {ok, _} = zip:create(Archive, ["archive_script_file_access"],
+ [{compress, []}, {cwd, DataDir}]),
+ ?line {ok, _} = zip:extract(Archive, [{cwd, PrivDir}]),
+ TopDir = filename:join([PrivDir, "archive_script_file_access"]),
+
+ %% Compile the code
+ ?line ok = compile_files([MainSrc], TopDir, TopDir),
+
+ %% First, create a file structure which will be included in the archive:
+ %%
+ %% dir1/
+ %% dir1/subdir1/
+ %% dir1/subdir1/file1
+ %%
+ {ok, OldDir} = file:get_cwd(),
+ ok = file:set_cwd(TopDir),
+ DummyDir = "dir1",
+ DummySubDir = filename:join(DummyDir, "subdir1"),
+ RelDummyFile = filename:join(DummySubDir, "file1"),
+ DummyFile = filename:join(TopDir,RelDummyFile),
+ ok = filelib:ensure_dir(DummyFile),
+ ok = file:write_file(DummyFile, ["foo\nbar\nbaz"]),
+
+ %% 1. Create zip archive by adding the dummy file and the beam
+ %% file as binaries to zip.
+ %%
+ %% This used to provoke the following issues when the script was run as
+ %% "./<script_name>":
+ %% a. erl_prim_loader:read_file_info/1 returning 'error'
+ %% b. erl_prim_loader:list_dir/1 returning {ok, ["dir1", [], "file1"]}
+ %% leading to an infinite loop in reltool_target:spec_dir/1
+ Files1 =
+ lists:map(fun(Filename) ->
+ {ok, Bin} = file:read_file(Filename),
+ {Filename,Bin}
+ end,
+ [RelDummyFile,MainBeam]),
+ {ok, {"mem", Bin1}} = zip:create("mem", Files1, [memory]),
+
+ %% Create the escript
+ ScriptName1 = "archive_script_file_access1",
+ Script1 = filename:join([PrivDir, ScriptName1]),
+ Flags = "-escript main " ++ MainMod,
+ ok = escript:create(Script1,[shebang,{emu_args,Flags},{archive,Bin1}]),
+ ok = file:change_mode(Script1,8#00744),
+
+ %% If supported, create a symlink to the script. This is used to
+ %% test error b) described above this test case.
+ SymlinkName1 = "symlink_to_"++ScriptName1,
+ Symlink1 = filename:join([PrivDir, SymlinkName1]),
+ file:make_symlink(ScriptName1,Symlink1), % will fail if not supported
+
+ %% Also add a dummy file in the same directory with the same name
+ %% as the script except is also has an extension. This used to
+ %% test error a) described above this test case.
+ ok = file:write_file(Script1 ++ ".extension",
+ <<"same name as script, but with extension">>),
+
+ %% Change to script's directory and run it as "./<script_name>"
+ ok = file:set_cwd(PrivDir),
+ run(PrivDir, "./" ++ ScriptName1 ++ " " ++ ScriptName1,
+ [<<"ExitCode:0">>]),
+ ok = file:set_cwd(TopDir),
+
+
+ %% 2. Create zip archive by letting zip read the files from the file system
+ %%
+ %% The difference compared to the archive_script_file_access1 is
+ %% that this will have a file element for each directory in the
+ %% archive - while archive_script_file_access1 will only have a
+ %% file element per regular file.
+ Files2 = [DummyDir,MainBeam],
+ {ok, {"mem", Bin2}} = zip:create("mem", Files2, [memory]),
+
+ %% Create the escript
+ ScriptName2 = "archive_script_file_access2",
+ Script2 = filename:join([PrivDir, ScriptName2]),
+ ok = escript:create(Script2,[shebang,{emu_args,Flags},{archive,Bin2}]),
+ ok = file:change_mode(Script2,8#00744),
+
+ %% Also add a dummy file in the same directory with the same name
+ %% as the script except is also has an extension. This used to
+ %% test error a) described above this test case.
+ ok = file:write_file(Script2 ++ ".extension",
+ <<"same name as script, but with extension">>),
+
+ %% If supported, create a symlink to the script. This is used to
+ %% test error b) described above this test case.
+ SymlinkName2 = "symlink_to_"++ScriptName2,
+ Symlink2 = filename:join([PrivDir, SymlinkName2]),
+ file:make_symlink(ScriptName2,Symlink2), % will fail if not supported
+
+ %% Change to script's directory and run it as "./<script_name>"
+ ok = file:set_cwd(PrivDir),
+ run(PrivDir, "./" ++ ScriptName2 ++ " " ++ ScriptName2,
+ [<<"ExitCode:0">>]),
+
+ %% 3. If symlinks are supported, run one of the scripts via a symlink.
+ %%
+ %% This is in order to test error b) described above this test case.
+ case file:read_link(Symlink2) of
+ {ok,_} ->
+ run(PrivDir, "./" ++ SymlinkName2 ++ " " ++ ScriptName2,
+ [<<"ExitCode:0">>]);
+ _ -> % not supported
+ ok
+ end,
+ ok = file:set_cwd(OldDir).
+
+
compile_app(TopDir, AppName) ->
AppDir = filename:join([TopDir, AppName]),
SrcDir = filename:join([AppDir, "src"]),
@@ -475,7 +636,7 @@ compile_files([File | Files], SrcDir, OutDir) ->
case filename:extension(File) of
".erl" ->
AbsFile = filename:join([SrcDir, File]),
- case compile:file(AbsFile, [{outdir, OutDir}]) of
+ case compile:file(AbsFile, [{outdir, OutDir},report_errors]) of
{ok, _Mod} ->
compile_files(Files, SrcDir, OutDir);
Error ->
@@ -667,6 +828,8 @@ normalize_sections(Sections) ->
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+
foldl(Config) when is_list(Config) ->
{NewFile, _FileInfo,
_EmuArg, _Source,
@@ -744,6 +907,20 @@ emulate_escript_foldl(Fun, Acc, File) ->
{error, Reason}
end.
+unicode(Config) when is_list(Config) ->
+ Data = ?config(data_dir, Config),
+ Dir = filename:absname(Data), %Get rid of trailing slash.
+ run(Dir, "unicode1",
+ [<<"escript: exception error: an error occurred when evaluating"
+ " an arithmetic expression\n in operator '/'/2\n "
+ "called as <<170>> / <<170>>\nExitCode:127">>]),
+ run(Dir, "unicode2",
+ [<<"escript: exception error: an error occurred when evaluating"
+ " an arithmetic expression\n in operator '/'/2\n "
+ "called as <<\"\xaa\">> / <<\"\xaa\">>\nExitCode:127">>]),
+ run(Dir, "unicode3", [<<"ExitCode:0">>]),
+ ok.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
overflow(Config) when is_list(Config) ->
diff --git a/lib/stdlib/test/escript_SUITE_data/archive_script/archive_script_main2.erl b/lib/stdlib/test/escript_SUITE_data/archive_script/archive_script_main2.erl
index de56579998..1cddef0e3d 100644
--- a/lib/stdlib/test/escript_SUITE_data/archive_script/archive_script_main2.erl
+++ b/lib/stdlib/test/escript_SUITE_data/archive_script/archive_script_main2.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -21,6 +21,8 @@
-export([main/1]).
+-include_lib("kernel/include/file.hrl").
+
-define(DUMMY, archive_script_dummy).
-define(DICT, archive_script_dict).
@@ -32,7 +34,7 @@ main(MainArgs) ->
io:format("dummy:~p\n",[[E || E <- ErlArgs, element(1, E) =:= ?DUMMY]]),
%% Start the applications
- {error, {not_started, ?DICT}} = application:start(archive_script_dummy),
+ {error, {not_started, ?DICT}} = application:start(?DUMMY),
ok = application:start(?DICT),
ok = application:start(?DUMMY),
@@ -57,4 +59,17 @@ main(MainArgs) ->
ok = ?DICT:erase(Tab, Key),
error = ?DICT:find(Tab, Key),
ok = ?DICT:erase(Tab),
+
+ %% Check mtime related caching bug with escript/primary archive files
+ Escript = escript:script_name(),
+ {ok, FileInfo} = file:read_file_info(Escript),
+ %% Modify mtime of archive file and try to reload module
+ FileInfo2 = FileInfo#file_info{mtime=calendar:now_to_local_time(now())},
+ ok = file:write_file_info(Escript, FileInfo2),
+ Module = ?DICT,
+ {file, _} = code:is_loaded(Module),
+ true = code:delete(Module),
+ false = code:is_loaded(Module),
+ {module, Module} = code:ensure_loaded(Module),
+
ok.
diff --git a/lib/stdlib/test/escript_SUITE_data/archive_script_file_access/archive_script_file_access.erl b/lib/stdlib/test/escript_SUITE_data/archive_script_file_access/archive_script_file_access.erl
new file mode 100644
index 0000000000..b03c8ba70d
--- /dev/null
+++ b/lib/stdlib/test/escript_SUITE_data/archive_script_file_access/archive_script_file_access.erl
@@ -0,0 +1,105 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, 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(archive_script_file_access).
+-behaviour(escript).
+
+-export([main/1]).
+
+-include_lib("kernel/include/file.hrl").
+
+main([RelArchiveFile]) ->
+
+ AbsArchiveFile = filename:absname(RelArchiveFile),
+ DotSlashArchiveFile = "./" ++ RelArchiveFile,
+
+ Beam = atom_to_list(?MODULE) ++ ".beam",
+ AbsBeam = filename:join(AbsArchiveFile,Beam),
+ RelBeam = filename:join(RelArchiveFile,Beam),
+ DotSlashBeam = filename:join(DotSlashArchiveFile,Beam),
+ Dir = "dir1",
+ AbsDir = filename:join(AbsArchiveFile,Dir),
+ RelDir = filename:join(RelArchiveFile,Dir),
+ DotSlashDir = filename:join(DotSlashArchiveFile,Dir),
+ SubDir = "subdir1",
+ AbsSubDir = filename:join(AbsDir,SubDir),
+ RelSubDir = filename:join(RelDir,SubDir),
+ DotSlashSubDir = filename:join(DotSlashDir,SubDir),
+
+ {ok,List1} = erl_prim_loader:list_dir(AbsArchiveFile),
+ {ok,List1} = erl_prim_loader:list_dir(RelArchiveFile),
+ {ok,List1} = erl_prim_loader:list_dir(DotSlashArchiveFile),
+ {ok,List1} = erl_prim_loader:list_dir(AbsArchiveFile ++ "/"),
+ {ok,List1} = erl_prim_loader:list_dir(AbsArchiveFile ++ "/."),
+ {ok,List1} = erl_prim_loader:list_dir(filename:join([AbsDir,".."])),
+ {ok,List1} = erl_prim_loader:list_dir(filename:join([RelDir,".."])),
+ {ok,List1} = erl_prim_loader:list_dir(filename:join([DotSlashDir,".."])),
+ {ok,List1} = erl_prim_loader:list_dir(filename:join([AbsSubDir,"..",".."])),
+ {ok,List1} = erl_prim_loader:list_dir(filename:join([RelSubDir,"..",".."])),
+ {ok,List1} = erl_prim_loader:list_dir(filename:join([DotSlashSubDir,"..",".."])),
+ false = lists:member([],List1),
+
+ %% If symlinks are supported on this platform...
+ RelSymlinkArchiveFile = "symlink_to_" ++ RelArchiveFile,
+ case file:read_link(RelSymlinkArchiveFile) of
+ {ok,_} ->
+ DotSlashSymlinkArchiveFile = "./" ++ RelSymlinkArchiveFile,
+ AbsSymlinkArchiveFile=filename:join(filename:dirname(AbsArchiveFile),
+ RelSymlinkArchiveFile),
+ {ok,List1} = erl_prim_loader:list_dir(AbsSymlinkArchiveFile),
+ {ok,List1} = erl_prim_loader:list_dir(RelSymlinkArchiveFile),
+ {ok,List1} = erl_prim_loader:list_dir(DotSlashSymlinkArchiveFile);
+ _ -> % not supported
+ ok
+ end,
+
+
+ {ok,List2} = erl_prim_loader:list_dir(AbsDir),
+ {ok,List2} = erl_prim_loader:list_dir(RelDir),
+ {ok,List2} = erl_prim_loader:list_dir(DotSlashDir),
+ false = lists:member([],List2),
+
+ error = erl_prim_loader:list_dir(AbsBeam),
+ error = erl_prim_loader:list_dir(RelBeam),
+ error = erl_prim_loader:list_dir(DotSlashBeam),
+
+ error = erl_prim_loader:get_file(AbsArchiveFile),
+ error = erl_prim_loader:get_file(RelArchiveFile),
+ error = erl_prim_loader:get_file(DotSlashArchiveFile),
+ error = erl_prim_loader:get_file(AbsArchiveFile ++ "/"),
+ error = erl_prim_loader:get_file(AbsArchiveFile ++ "/."),
+ {ok,Bin,AbsBeam} = erl_prim_loader:get_file(AbsBeam),
+ {ok,Bin,RelBeam} = erl_prim_loader:get_file(RelBeam),
+ {ok,Bin,DotSlashBeam} = erl_prim_loader:get_file(DotSlashBeam),
+
+ {ok,#file_info{type=directory}=DFI} =
+ erl_prim_loader:read_file_info(AbsArchiveFile),
+ {ok,DFI} = erl_prim_loader:read_file_info(RelArchiveFile),
+ {ok,DFI} = erl_prim_loader:read_file_info(DotSlashArchiveFile),
+ {ok,DFI} = erl_prim_loader:read_file_info(AbsArchiveFile ++ "/"),
+ {ok,DFI} = erl_prim_loader:read_file_info(AbsArchiveFile ++ "/."),
+ {ok,#file_info{type=regular}=RFI} = erl_prim_loader:read_file_info(AbsBeam),
+ {ok,RFI} = erl_prim_loader:read_file_info(RelBeam),
+ {ok,RFI} = erl_prim_loader:read_file_info(DotSlashBeam),
+
+ F = AbsArchiveFile ++ ".extension",
+ error = erl_prim_loader:list_dir(F),
+ {ok,_,_} = erl_prim_loader:get_file(F),
+ {ok,#file_info{type=regular}} = erl_prim_loader:read_file_info(F),
+
+ ok.
diff --git a/lib/stdlib/test/escript_SUITE_data/emulator_flags_no_shebang b/lib/stdlib/test/escript_SUITE_data/emulator_flags_no_shebang
new file mode 100644
index 0000000000..47d843ebe1
--- /dev/null
+++ b/lib/stdlib/test/escript_SUITE_data/emulator_flags_no_shebang
@@ -0,0 +1,10 @@
+%% -*- erlang -*-
+%%! -nostick -mnesia dir a/directory -mnesia debug verbose
+
+main(MainArgs) ->
+ io:format("main:~p\n",[MainArgs]),
+ ErlArgs = init:get_arguments(),
+ io:format("nostick:~p\n",[[E || E <- ErlArgs, element(1, E) =:= nostick]]),
+ io:format("mnesia:~p\n", [[E || E <- ErlArgs, element(1, E) =:= mnesia]]),
+ io:format("ERL_FLAGS=~p\n", [os:getenv("ERL_FLAGS")]),
+ io:format("unknown:~p\n",[[E || E <- ErlArgs, element(1, E) =:= unknown]]).
diff --git a/lib/stdlib/test/escript_SUITE_data/unicode1 b/lib/stdlib/test/escript_SUITE_data/unicode1
new file mode 100755
index 0000000000..a77574625e
--- /dev/null
+++ b/lib/stdlib/test/escript_SUITE_data/unicode1
@@ -0,0 +1,14 @@
+#!/usr/bin/env escript
+%% -*- erlang -*-
+
+-export([main/1]).
+
+main(_) ->
+ ok = io:setopts([{encoding,unicode}]),
+ _D = erlang:system_flag(backtrace_depth, 0),
+ A = <<"\x{aa}">>,
+ S = lists:flatten(io_lib:format("~p/~p.", [A, A])),
+ {ok, Ts, _} = erl_scan:string(S, 1, [unicode]),
+ {ok, Es} = erl_parse:parse_exprs(Ts),
+ B = erl_eval:new_bindings(),
+ erl_eval:exprs(Es, B).
diff --git a/lib/stdlib/test/escript_SUITE_data/unicode2 b/lib/stdlib/test/escript_SUITE_data/unicode2
new file mode 100755
index 0000000000..495188f6f0
--- /dev/null
+++ b/lib/stdlib/test/escript_SUITE_data/unicode2
@@ -0,0 +1,14 @@
+#!/usr/bin/env escript
+%% -*- erlang -*-
+
+-export([main/1]).
+
+main(_) ->
+ ok = io:setopts([{encoding,latin1}]),
+ _D = erlang:system_flag(backtrace_depth, 0),
+ A = <<"\x{aa}">>,
+ S = lists:flatten(io_lib:format("~p/~p.", [A, A])),
+ {ok, Ts, _} = erl_scan:string(S, 1, [unicode]),
+ {ok, Es} = erl_parse:parse_exprs(Ts),
+ B = erl_eval:new_bindings(),
+ erl_eval:exprs(Es, B).
diff --git a/lib/stdlib/test/escript_SUITE_data/unicode3 b/lib/stdlib/test/escript_SUITE_data/unicode3
new file mode 100755
index 0000000000..944487dcae
--- /dev/null
+++ b/lib/stdlib/test/escript_SUITE_data/unicode3
@@ -0,0 +1,13 @@
+#!/usr/bin/env escript
+%% -*- erlang; coding: utf-8 -*-
+
+-export([main/1]).
+
+main(_) ->
+ ok = io:setopts([{encoding,unicode}]),
+ Bin1 = <<"örn_Ѐ שלום-שלום+של 日本語">>,
+
+ L = [246,114,110,95,1024,32,1513,1500,1493,1501,45,1513,1500,1493,
+ 1501,43,1513,1500,32,26085,26412,35486],
+ L = unicode:characters_to_list(Bin1, utf8),
+ ok.
diff --git a/lib/stdlib/test/ets_SUITE.erl b/lib/stdlib/test/ets_SUITE.erl
index 297c4ec1c9..dc17e5d33c 100644
--- a/lib/stdlib/test/ets_SUITE.erl
+++ b/lib/stdlib/test/ets_SUITE.erl
@@ -74,6 +74,7 @@
-export([bad_table/1, types/1]).
-export([otp_9932/1]).
-export([otp_9423/1]).
+-export([otp_10182/1]).
-export([init_per_testcase/2, end_per_testcase/2]).
%% Convenience for manual testing
@@ -146,6 +147,7 @@ all() ->
exit_many_large_table_owner, exit_many_tables_owner,
exit_many_many_tables_owner, write_concurrency, heir,
give_away, setopts, bad_table, types,
+ otp_10182,
otp_9932,
otp_9423].
@@ -1024,6 +1026,8 @@ t_test_ms(Config) when is_list(Config) ->
[{{'$1','$2'},[{'<','$1','$2'}],['$$']}]),
?line {ok,false} = ets:test_ms({a,b},
[{{'$1','$2'},[{'>','$1','$2'}],['$$']}]),
+ Tpl = {a,gb_sets:new()},
+ ?line {ok,Tpl} = ets:test_ms(Tpl, [{{'_','_'}, [], ['$_']}]), % OTP-10190
?line {error,[{error,String}]} = ets:test_ms({a,b},
[{{'$1','$2'},
[{'flurp','$1','$2'}],
@@ -2166,20 +2170,29 @@ heir_do(Opts) ->
?line undefined = ets:info(foo),
%% When heir dies and pid reused before founder dies
- NextPidIx = erts_debug:get_internal_state(next_pid),
- {Founder4,MrefF4} = my_spawn_monitor(fun()->heir_founder(Master,"The dying heir",Opts)end),
- {Heir4,MrefH4} = my_spawn_monitor(fun()->heir_heir(Founder4)end),
- Founder4 ! {go, Heir4},
- ?line {'DOWN', MrefH4, process, Heir4, normal} = receive_any(),
- erts_debug:set_internal_state(next_pid, NextPidIx),
- {Heir4,MrefH4_B} = spawn_monitor_with_pid(Heir4,
- fun()-> ?line die_please = receive_any() end),
- Founder4 ! die_please,
- ?line {'DOWN', MrefF4, process, Founder4, normal} = receive_any(),
- Heir4 ! die_please,
- ?line {'DOWN', MrefH4_B, process, Heir4, normal} = receive_any(),
- ?line undefined = ets:info(foo),
-
+ repeat_while(fun() ->
+ NextPidIx = erts_debug:get_internal_state(next_pid),
+ {Founder4,MrefF4} = my_spawn_monitor(fun()->heir_founder(Master,"The dying heir",Opts)end),
+ {Heir4,MrefH4} = my_spawn_monitor(fun()->heir_heir(Founder4)end),
+ Founder4 ! {go, Heir4},
+ ?line {'DOWN', MrefH4, process, Heir4, normal} = receive_any(),
+ erts_debug:set_internal_state(next_pid, NextPidIx),
+ DoppelGanger = spawn_monitor_with_pid(Heir4,
+ fun()-> ?line die_please = receive_any() end),
+ Founder4 ! die_please,
+ ?line {'DOWN', MrefF4, process, Founder4, normal} = receive_any(),
+ case DoppelGanger of
+ {Heir4,MrefH4_B} ->
+ Heir4 ! die_please,
+ ?line {'DOWN', MrefH4_B, process, Heir4, normal} = receive_any(),
+ ?line undefined = ets:info(foo),
+ false;
+ failed ->
+ io:format("Failed to spawn process with pid ~p\n", [Heir4]),
+ true % try again
+ end
+ end),
+
?line verify_etsmem(EtsMem).
heir_founder(Master, HeirData, Opts) ->
@@ -4204,21 +4217,13 @@ heavy_lookup_element(Config) when is_list(Config) ->
repeat_for_opts(heavy_lookup_element_do).
heavy_lookup_element_do(Opts) ->
- ?line EtsMem = etsmem(),
- ?line Tab = ets_new(foobar_table, [set, protected, {keypos, 2} | Opts]),
- ?line ok = fill_tab2(Tab, 0, 7000),
- case os:type() of
- vxworks ->
- ?line ?t:do_times(5, ?MODULE, do_lookup_element,
- [Tab, 6999, 1]);
- % lookup ALL elements 5 times.
- _ ->
- ?line ?t:do_times(50, ?MODULE, do_lookup_element,
- [Tab, 6999, 1])
- % lookup ALL elements 50 times.
- end,
- ?line true = ets:delete(Tab),
- ?line verify_etsmem(EtsMem).
+ EtsMem = etsmem(),
+ Tab = ets_new(foobar_table, [set, protected, {keypos, 2} | Opts]),
+ ok = fill_tab2(Tab, 0, 7000),
+ % lookup ALL elements 50 times
+ ?t:do_times(50, ?MODULE, do_lookup_element, [Tab, 6999, 1]),
+ true = ets:delete(Tab),
+ verify_etsmem(EtsMem).
do_lookup_element(_Tab, 0, _) -> ok;
do_lookup_element(Tab, N, M) ->
@@ -5470,6 +5475,20 @@ otp_9423(Config) when is_list(Config) ->
Skipped -> Skipped
end.
+
+%% Corrupted binary in compressed table
+otp_10182(Config) when is_list(Config) ->
+ Bin = <<"aHR0cDovL2hvb3RzdWl0ZS5jb20vYy9wcm8tYWRyb2xsLWFi">>,
+ Key = {test, Bin},
+ Value = base64:decode(Bin),
+ In = {Key,Value},
+ Db = ets:new(undefined, [set, protected, {read_concurrency, true}, compressed]),
+ ets:insert(Db, In),
+ [Out] = ets:lookup(Db, Key),
+ io:format("In : ~p\nOut: ~p\n", [In,Out]),
+ ets:delete(Db),
+ In = Out.
+
@@ -5777,25 +5796,20 @@ receive_any_spinning(Loops, N, Tries) when N>0 ->
spawn_monitor_with_pid(Pid, Fun) when is_pid(Pid) ->
- spawn_monitor_with_pid(Pid, Fun, 1, 10).
+ spawn_monitor_with_pid(Pid, Fun, 10).
-spawn_monitor_with_pid(Pid, Fun, N, M) when N > M*10 ->
- spawn_monitor_with_pid(Pid, Fun, N, M*10);
-spawn_monitor_with_pid(Pid, Fun, N, M) ->
- ?line false = is_process_alive(Pid),
+spawn_monitor_with_pid(_, _, 0) ->
+ failed;
+spawn_monitor_with_pid(Pid, Fun, N) ->
case my_spawn(fun()-> case self() of
Pid -> Fun();
_ -> die
end
end) of
- Pid ->
+ Pid ->
{Pid, erlang:monitor(process, Pid)};
Other ->
- case N rem M of
- 0 -> io:format("Failed ~p times to get pid ~p (current = ~p)\n",[N,Pid,Other]);
- _ -> ok
- end,
- spawn_monitor_with_pid(Pid,Fun,N+1,M)
+ spawn_monitor_with_pid(Pid,Fun,N-1)
end.
diff --git a/lib/stdlib/test/filelib_SUITE.erl b/lib/stdlib/test/filelib_SUITE.erl
index 1de639a166..1fd7518519 100644
--- a/lib/stdlib/test/filelib_SUITE.erl
+++ b/lib/stdlib/test/filelib_SUITE.erl
@@ -176,9 +176,64 @@ do_wildcard_5(Dir, Wcf) ->
%% Cleanup
?line del(Files),
- ?line foreach(fun(D) -> ok = file:del_dir(filename:join(Dir, D)) end, Dirs).
+ ?line foreach(fun(D) -> ok = file:del_dir(filename:join(Dir, D)) end, Dirs),
+ do_wildcard_6(Dir, Wcf).
+
+do_wildcard_6(Dir, Wcf) ->
+ ok = file:make_dir(filename:join(Dir, "xbin")),
+ All = ["xbin/a.x","xbin/b.x","xbin/c.x"],
+ Files = mkfiles(All, Dir),
+ All = Wcf("xbin/*.x"),
+ All = Wcf("xbin/*"),
+ ["xbin"] = Wcf("*"),
+ All = Wcf("*/*"),
+ del(Files),
+ ok = file:del_dir(filename:join(Dir, "xbin")),
+ do_wildcard_7(Dir, Wcf).
+
+do_wildcard_7(Dir, Wcf) ->
+ Dirs = ["blurf","xa","yyy"],
+ SubDirs = ["blurf/nisse"],
+ foreach(fun(D) ->
+ ok = file:make_dir(filename:join(Dir, D))
+ end, Dirs ++ SubDirs),
+ All = ["blurf/nisse/baz","xa/arne","xa/kalle","yyy/arne"],
+ Files = mkfiles(lists:reverse(All), Dir),
+ %% Test.
+ Listing = Wcf("**"),
+ ["blurf","blurf/nisse","blurf/nisse/baz",
+ "xa","xa/arne","xa/kalle","yyy","yyy/arne"] = Listing,
+ Listing = Wcf("**/*"),
+ ["xa/arne","yyy/arne"] = Wcf("**/arne"),
+ ["blurf/nisse"] = Wcf("**/nisse"),
+ [] = Wcf("mountain/**"),
+
+ %% Cleanup
+ del(Files),
+ foreach(fun(D) ->
+ ok = file:del_dir(filename:join(Dir, D))
+ end, SubDirs ++ Dirs),
+ do_wildcard_8(Dir, Wcf).
+
+do_wildcard_8(Dir, Wcf) ->
+ Dirs0 = ["blurf"],
+ Dirs1 = ["blurf/nisse"],
+ Dirs2 = ["blurf/nisse/a", "blurf/nisse/b"],
+ foreach(fun(D) ->
+ ok = file:make_dir(filename:join(Dir, D))
+ end, Dirs0 ++ Dirs1 ++ Dirs2),
+ All = ["blurf/nisse/a/1.txt", "blurf/nisse/b/2.txt", "blurf/nisse/b/3.txt"],
+ Files = mkfiles(lists:reverse(All), Dir),
+ %% Test.
+ All = Wcf("**/blurf/**/*.txt"),
+
+ %% Cleanup
+ del(Files),
+ foreach(fun(D) ->
+ ok = file:del_dir(filename:join(Dir, D))
+ end, Dirs2 ++ Dirs1 ++ Dirs0).
fold_files(Config) when is_list(Config) ->
?line Dir = filename:join(?config(priv_dir, Config), "fold_files"),
diff --git a/lib/stdlib/test/filename_SUITE.erl b/lib/stdlib/test/filename_SUITE.erl
index 4cfa589660..232df6a13f 100644
--- a/lib/stdlib/test/filename_SUITE.erl
+++ b/lib/stdlib/test/filename_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -25,7 +25,7 @@
-export([pathtype/1,rootname/1,split/1,find_src/1]).
-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]).
+ dirname_bin/1, extension_bin/1, join_bin/1, t_nativename_bin/1]).
-export([pathtype_bin/1,rootname_bin/1,split_bin/1]).
-include_lib("test_server/include/test_server.hrl").
@@ -38,7 +38,7 @@ all() ->
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].
+ join_bin, pathtype_bin, rootname_bin, split_bin, t_nativename_bin].
groups() ->
[].
@@ -112,19 +112,6 @@ absname(Config) when is_list(Config) ->
?line "/erlang/src" = filename:absname(["/erl",'a','ng',"/",'s',"rc"]),
?line "/erlang/src" = filename:absname("/erlang///src"),
?line "/file_sorter.erl" = filename:absname([file_sorter|'.erl']),
- ok;
- vxworks ->
- Test_dir = ?config(priv_dir, Config),
- Test1 = Test_dir ++ "/foo",
- Test2 = Test_dir ++ "/ebin",
- ?line ok = file:set_cwd(Test_dir),
- ?line Test1 = filename:absname(foo),
- ?line Test1= filename:absname("foo"),
- ?line Test2 = filename:absname("foo/../ebin"),
- ?line "/erlang" = filename:absname("/erlang"),
- ?line "/erlang/src" = filename:absname("/erlang/src"),
- ?line "/erlang/src" = filename:absname(["/erlan",'g/s',"rc"]),
- ?line "/erlang/src" = filename:absname("/erlang///src"),
ok
end.
@@ -179,15 +166,6 @@ absname_2(Config) when is_list(Config) ->
?line "/erlang" = filename:absname("/erlang", "/"),
?line "/erlang/src" = filename:absname("/erlang/src", "/"),
?line "/erlang/src" = filename:absname("/erlang///src", "/"),
- ok;
- vxworks ->
- ?line "/usr/foo" = filename:absname(foo, "/usr"),
- ?line "/usr/foo" = filename:absname("foo", "/usr"),
- ?line "/usr/ebin" = filename:absname("../ebin", "/usr"),
- ?line "/usr/ebin" = filename:absname("../ebin", "/usr/src"),
- ?line "/erlang" = filename:absname("/erlang", "/usr"),
- ?line "/erlang/src" = filename:absname("/erlang/src", "/usr"),
- ?line "/erlang/src" = filename:absname("/erlang///src", "/usr"),
ok
end.
@@ -213,11 +191,7 @@ basename_1(Config) when is_list(Config) ->
?line "foo" = filename:basename("A:foo");
{unix, _} ->
?line "strange\\but\\true" =
- filename:basename("strange\\but\\true");
- vxworks ->
- ?line "foo" = filename:basename(["usr\\foo\\"]),
- ?line "foo" = filename:basename("elrond:usr\\foo\\"),
- ?line "foo" = filename:basename("disk:/foo")
+ filename:basename("strange\\but\\true")
end,
?line test_server:timetrap_cancel(Dog),
ok.
@@ -249,15 +223,7 @@ basename_2(Config) when is_list(Config) ->
?line "strange\\but\\true" =
filename:basename("strange\\but\\true.erl", ".erl"),
?line "strange\\but\\true" =
- filename:basename("strange\\but\\true", ".erl");
- vxworks ->
- ?line "foo" = filename:basename("net:foo", ".erl"),
- ?line "foo.erl" = filename:basename("net:\\usr\\foo.erl",
- ".hrl"),
- ?line "foo.erl" =
- filename:basename("/disk0:\\usr.hrl\\foo.erl",
- ".hrl"),
- ?line "foo" = filename:basename("/home\\usr\\foo", ".hrl")
+ filename:basename("strange\\but\\true", ".erl")
end,
?line test_server:timetrap_cancel(Dog),
ok.
@@ -267,37 +233,25 @@ basename_2(Config) when is_list(Config) ->
dirname(Config) when is_list(Config) ->
case os:type() of
{win32,_} ->
- ?line "A:/usr" = filename:dirname("A:/usr/foo.erl"),
- ?line "A:usr" = filename:dirname("A:usr/foo.erl"),
- ?line "/usr" = filename:dirname("\\usr\\foo.erl"),
- ?line "/" = filename:dirname("\\usr"),
- ?line "A:" = filename:dirname("A:");
- vxworks ->
- ?line "net:/usr" = filename:dirname("net:/usr/foo.erl"),
- ?line "/disk0:/usr" = filename:dirname("/disk0:/usr/foo.erl"),
- ?line "/usr" = filename:dirname("\\usr\\foo.erl"),
- ?line "/usr" = filename:dirname("\\usr"),
- ?line "net:" = filename:dirname("net:");
+ "A:/usr" = filename:dirname("A:/usr/foo.erl"),
+ "A:usr" = filename:dirname("A:usr/foo.erl"),
+ "/usr" = filename:dirname("\\usr\\foo.erl"),
+ "/" = filename:dirname("\\usr"),
+ "A:" = filename:dirname("A:");
_ -> true
end,
- ?line "usr" = filename:dirname("usr///foo.erl"),
- ?line "." = filename:dirname("foo.erl"),
- ?line "." = filename:dirname("."),
- ?line "usr" = filename:dirname('usr/foo.erl'),
- ?line "usr" = filename:dirname(['usr','/foo.erl']),
- ?line "usr" = filename:dirname(['us','r/foo.erl']),
- ?line "usr" = filename:dirname(['usr/','/foo.erl']),
- ?line "usr" = filename:dirname(['usr/','foo.erl']),
- ?line "usr" = filename:dirname(['usr/'|'foo.erl']),
- ?line "usr" = filename:dirname(['usr/f','oo.erl']),
- case os:type() of
- vxworks ->
- ?line "/" = filename:dirname("/"),
- ?line "/usr" = filename:dirname("/usr");
- _ ->
- ?line "/" = filename:dirname("/"),
- ?line "/" = filename:dirname("/usr")
- end,
+ "usr" = filename:dirname("usr///foo.erl"),
+ "." = filename:dirname("foo.erl"),
+ "." = filename:dirname("."),
+ "usr" = filename:dirname('usr/foo.erl'),
+ "usr" = filename:dirname(['usr','/foo.erl']),
+ "usr" = filename:dirname(['us','r/foo.erl']),
+ "usr" = filename:dirname(['usr/','/foo.erl']),
+ "usr" = filename:dirname(['usr/','foo.erl']),
+ "usr" = filename:dirname(['usr/'|'foo.erl']),
+ "usr" = filename:dirname(['usr/f','oo.erl']),
+ "/" = filename:dirname("/"),
+ "/" = filename:dirname("/usr"),
ok.
@@ -319,12 +273,6 @@ extension(Config) when is_list(Config) ->
filename:extension("A:/usr.bar/foo.nisse.erl"),
?line "" = filename:extension("A:/usr.bar/foo"),
ok;
- vxworks ->
- ?line "" = filename:extension("/disk0:\\usr\\foo"),
- ?line ".erl" =
- filename:extension("net:/usr.bar/foo.nisse.erl"),
- ?line "" = filename:extension("net:/usr.bar/foo"),
- ok;
_ -> ok
end.
@@ -369,25 +317,6 @@ join(Config) when is_list(Config) ->
filename:join(["A:","C:usr","foo.erl"]),
?line "d:/foo" = filename:join([$D, $:, $/, []], "foo"),
ok;
- vxworks ->
- ?line "Net:" = filename:join(["Net:/"]),
- ?line "net:" = filename:join(["net:\\"]),
- ?line "net:/abc" = filename:join(["net:/", "abc"]),
- ?line "net:/abc" = filename:join(["net:", "abc"]),
- ?line "a/b/c/d/e/f/g" =
- filename:join(["a//b\\c//\\/\\d/\\e/f\\g"]),
- ?line "net:/usr/foo.erl" =
- filename:join(["net:","usr","foo.erl"]),
- ?line "/usr/foo.erl" =
- filename:join(["net:","/usr","foo.erl"]),
- ?line "/target:usr" = filename:join("net:","/target:usr"),
- ?line "kernel:/usr" = filename:join("net:", "kernel:/usr"),
- ?line "foo:/usr/foo.erl" =
- filename:join(["A:","foo:/usr","foo.erl"]),
- ?line "/disk0:usr/foo.erl" =
- filename:join(["kalle:","/disk0:usr","foo.erl"]),
- ?line "D:/foo" = filename:join([$D, $:, $/, []], "foo"),
- ok;
{unix, _} ->
ok
end.
@@ -406,10 +335,6 @@ pathtype(Config) when is_list(Config) ->
{unix, _} ->
?line absolute = filename:pathtype("/"),
?line absolute = filename:pathtype("/usr/local/bin"),
- ok;
- vxworks ->
- ?line absolute = filename:pathtype("/usr/local/bin"),
- ?line absolute = filename:pathtype("net:usr/local/bin"),
ok
end.
@@ -424,12 +349,7 @@ rootname(Config) when is_list(Config) ->
ok.
split(Config) when is_list(Config) ->
- case os:type() of
- vxworks ->
- ?line ["/usr","local","bin"] = filename:split("/usr/local/bin");
- _ ->
- ?line ["/","usr","local","bin"] = filename:split("/usr/local/bin")
- end,
+ ?line ["/","usr","local","bin"] = filename:split("/usr/local/bin"),
?line ["foo","bar"]= filename:split("foo/bar"),
?line ["foo", "bar", "hello"]= filename:split("foo////bar//hello"),
?line ["foo", "bar", "hello"]= filename:split(["foo//",'//bar//h',"ello"]),
@@ -447,18 +367,6 @@ split(Config) when is_list(Config) ->
?line ["a:","msdev","include"] =
filename:split("a:msdev\\include"),
ok;
- vxworks ->
- ?line ["net:","msdev","include"] =
- filename:split("net:/msdev/include"),
- ?line ["Target:","msdev","include"] =
- filename:split("Target:/msdev/include"),
- ?line ["msdev","include"] =
- filename:split("msdev\\include"),
- ?line ["/disk0:","msdev","include"] =
- filename:split("/disk0:\\msdev\\include"),
- ?line ["a:","msdev","include"] =
- filename:split("a:msdev\\include"),
- ok;
_ ->
ok
end.
@@ -657,56 +565,38 @@ basename_bin_2(Config) when is_list(Config) ->
dirname_bin(Config) when is_list(Config) ->
case os:type() of
{win32,_} ->
- ?line <<"A:/usr">> = filename:dirname(<<"A:/usr/foo.erl">>),
- ?line <<"A:usr">> = filename:dirname(<<"A:usr/foo.erl">>),
- ?line <<"/usr">> = filename:dirname(<<"\\usr\\foo.erl">>),
- ?line <<"/">> = filename:dirname(<<"\\usr">>),
- ?line <<"A:">> = filename:dirname(<<"A:">>);
- vxworks ->
- ?line <<"net:/usr">> = filename:dirname(<<"net:/usr/foo.erl">>),
- ?line <<"/disk0:/usr">> = filename:dirname(<<"/disk0:/usr/foo.erl">>),
- ?line <<"/usr">> = filename:dirname(<<"\\usr\\foo.erl">>),
- ?line <<"/usr">> = filename:dirname(<<"\\usr">>),
- ?line <<"net:">> = filename:dirname(<<"net:">>);
+ <<"A:/usr">> = filename:dirname(<<"A:/usr/foo.erl">>),
+ <<"A:usr">> = filename:dirname(<<"A:usr/foo.erl">>),
+ <<"/usr">> = filename:dirname(<<"\\usr\\foo.erl">>),
+ <<"/">> = filename:dirname(<<"\\usr">>),
+ <<"A:">> = filename:dirname(<<"A:">>);
_ -> true
end,
- ?line <<"usr">> = filename:dirname(<<"usr///foo.erl">>),
- ?line <<".">> = filename:dirname(<<"foo.erl">>),
- ?line <<".">> = filename:dirname(<<".">>),
- case os:type() of
- vxworks ->
- ?line <<"/">> = filename:dirname(<<"/">>),
- ?line <<"/usr">> = filename:dirname(<<"/usr">>);
- _ ->
- ?line <<"/">> = filename:dirname(<<"/">>),
- ?line <<"/">> = filename:dirname(<<"/usr">>)
- end,
+ <<"usr">> = filename:dirname(<<"usr///foo.erl">>),
+ <<".">> = filename:dirname(<<"foo.erl">>),
+ <<".">> = filename:dirname(<<".">>),
+ <<"/">> = filename:dirname(<<"/">>),
+ <<"/">> = filename:dirname(<<"/usr">>),
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
extension_bin(Config) when is_list(Config) ->
- ?line <<".erl">> = filename:extension(<<"A:/usr/foo.erl">>),
- ?line <<".erl">> = filename:extension(<<"A:/usr/foo.nisse.erl">>),
- ?line <<".erl">> = filename:extension(<<"A:/usr.bar/foo.nisse.erl">>),
- ?line <<"">> = filename:extension(<<"A:/usr.bar/foo">>),
- ?line <<"">> = filename:extension(<<"A:/usr/foo">>),
- ?line case os:type() of
- {win32, _} ->
- ?line <<"">> = filename:extension(<<"A:\\usr\\foo">>),
- ?line <<".erl">> =
- filename:extension(<<"A:/usr.bar/foo.nisse.erl">>),
- ?line <<"">> = filename:extension(<<"A:/usr.bar/foo">>),
- ok;
- vxworks ->
- ?line <<"">> = filename:extension(<<"/disk0:\\usr\\foo">>),
- ?line <<".erl">> =
- filename:extension(<<"net:/usr.bar/foo.nisse.erl">>),
- ?line <<"">> = filename:extension(<<"net:/usr.bar/foo">>),
- ok;
- _ -> ok
- end.
+ <<".erl">> = filename:extension(<<"A:/usr/foo.erl">>),
+ <<".erl">> = filename:extension(<<"A:/usr/foo.nisse.erl">>),
+ <<".erl">> = filename:extension(<<"A:/usr.bar/foo.nisse.erl">>),
+ <<"">> = filename:extension(<<"A:/usr.bar/foo">>),
+ <<"">> = filename:extension(<<"A:/usr/foo">>),
+ case os:type() of
+ {win32, _} ->
+ ?line <<"">> = filename:extension(<<"A:\\usr\\foo">>),
+ ?line <<".erl">> =
+ filename:extension(<<"A:/usr.bar/foo.nisse.erl">>),
+ ?line <<"">> = filename:extension(<<"A:/usr.bar/foo">>),
+ ok;
+ _ -> ok
+ end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -754,53 +644,58 @@ join_bin(Config) when is_list(Config) ->
end.
pathtype_bin(Config) when is_list(Config) ->
- ?line relative = filename:pathtype(<<"..">>),
- ?line relative = filename:pathtype(<<"foo">>),
- ?line relative = filename:pathtype(<<"foo/bar">>),
- ?line relative = filename:pathtype('foo/bar'),
+ relative = filename:pathtype(<<"..">>),
+ relative = filename:pathtype(<<"foo">>),
+ relative = filename:pathtype(<<"foo/bar">>),
+ relative = filename:pathtype('foo/bar'),
case os:type() of
{win32, _} ->
- ?line volumerelative = filename:pathtype(<<"/usr/local/bin">>),
- ?line volumerelative = filename:pathtype(<<"A:usr/local/bin">>),
+ volumerelative = filename:pathtype(<<"/usr/local/bin">>),
+ volumerelative = filename:pathtype(<<"A:usr/local/bin">>),
ok;
{unix, _} ->
- ?line absolute = filename:pathtype(<<"/">>),
- ?line absolute = filename:pathtype(<<"/usr/local/bin">>),
+ absolute = filename:pathtype(<<"/">>),
+ absolute = filename:pathtype(<<"/usr/local/bin">>),
ok
end.
rootname_bin(Config) when is_list(Config) ->
- ?line <<"/jam.src/kalle">> = filename:rootname(<<"/jam.src/kalle">>),
- ?line <<"/jam.src/foo">> = filename:rootname(<<"/jam.src/foo.erl">>),
- ?line <<"/jam.src/foo">> = filename:rootname(<<"/jam.src/foo.erl">>, <<".erl">>),
- ?line <<"/jam.src/foo.jam">> = filename:rootname(<<"/jam.src/foo.jam">>, <<".erl">>),
- ?line <<"/jam.src/foo.jam">> = filename:rootname(["/jam.sr",'c/foo.j',"am"],<<".erl">>),
- ?line <<"/jam.src/foo.jam">> = filename:rootname(["/jam.sr",'c/foo.j'|am],<<".erl">>),
+ <<"/jam.src/kalle">> = filename:rootname(<<"/jam.src/kalle">>),
+ <<"/jam.src/foo">> = filename:rootname(<<"/jam.src/foo.erl">>),
+ <<"/jam.src/foo">> = filename:rootname(<<"/jam.src/foo.erl">>, <<".erl">>),
+ <<"/jam.src/foo.jam">> = filename:rootname(<<"/jam.src/foo.jam">>, <<".erl">>),
+ <<"/jam.src/foo.jam">> = filename:rootname(["/jam.sr",'c/foo.j',"am"],<<".erl">>),
+ <<"/jam.src/foo.jam">> = filename:rootname(["/jam.sr",'c/foo.j'|am],<<".erl">>),
ok.
split_bin(Config) when is_list(Config) ->
- case os:type() of
- vxworks ->
- ?line [<<"/usr">>,<<"local">>,<<"bin">>] = filename:split(<<"/usr/local/bin">>);
- _ ->
- ?line [<<"/">>,<<"usr">>,<<"local">>,<<"bin">>] = filename:split(<<"/usr/local/bin">>)
- end,
- ?line [<<"foo">>,<<"bar">>]= filename:split(<<"foo/bar">>),
- ?line [<<"foo">>, <<"bar">>, <<"hello">>]= filename:split(<<"foo////bar//hello">>),
+ [<<"/">>,<<"usr">>,<<"local">>,<<"bin">>] = filename:split(<<"/usr/local/bin">>),
+ [<<"foo">>,<<"bar">>]= filename:split(<<"foo/bar">>),
+ [<<"foo">>, <<"bar">>, <<"hello">>]= filename:split(<<"foo////bar//hello">>),
case os:type() of
{win32,_} ->
- ?line [<<"a:/">>,<<"msdev">>,<<"include">>] =
+ [<<"a:/">>,<<"msdev">>,<<"include">>] =
filename:split(<<"a:/msdev/include">>),
- ?line [<<"a:/">>,<<"msdev">>,<<"include">>] =
+ [<<"a:/">>,<<"msdev">>,<<"include">>] =
filename:split(<<"A:/msdev/include">>),
- ?line [<<"msdev">>,<<"include">>] =
+ [<<"msdev">>,<<"include">>] =
filename:split(<<"msdev\\include">>),
- ?line [<<"a:/">>,<<"msdev">>,<<"include">>] =
+ [<<"a:/">>,<<"msdev">>,<<"include">>] =
filename:split(<<"a:\\msdev\\include">>),
- ?line [<<"a:">>,<<"msdev">>,<<"include">>] =
+ [<<"a:">>,<<"msdev">>,<<"include">>] =
filename:split(<<"a:msdev\\include">>),
ok;
_ ->
ok
end.
+t_nativename_bin(Config) when is_list(Config) ->
+ ?line <<"abcedf">> = filename:nativename(<<"abcedf">>),
+ case os:type() of
+ {win32, _} ->
+ ?line <<"a:\\temp\\arne.exe">> =
+ filename:nativename(<<"A:/temp//arne.exe/">>);
+ _ ->
+ ?line <<"/usr/tmp/arne">> =
+ filename:nativename(<<"/usr/tmp//arne/">>)
+ end.
diff --git a/lib/stdlib/test/gen_fsm_SUITE.erl b/lib/stdlib/test/gen_fsm_SUITE.erl
index bdb4ea65b5..22f66a6c14 100644
--- a/lib/stdlib/test/gen_fsm_SUITE.erl
+++ b/lib/stdlib/test/gen_fsm_SUITE.erl
@@ -281,21 +281,12 @@ start12(Config) when is_list(Config) ->
%% Check that time outs in calls work
abnormal1(suite) -> [];
abnormal1(Config) when is_list(Config) ->
- ?line {ok, _Pid} =
- gen_fsm:start({local, my_fsm}, gen_fsm_SUITE, [], []),
+ {ok, _Pid} = gen_fsm:start({local, my_fsm}, gen_fsm_SUITE, [], []),
%% timeout call.
- case os:type() of
- vxworks ->
- %% timeout call for VxWorks must be in 16ms increments.
- ?line delayed = gen_fsm:sync_send_event(my_fsm, {delayed_answer,1}, 17),
- ?line {'EXIT',{timeout,_}} =
- (catch gen_fsm:sync_send_event(my_fsm, {delayed_answer,17}, 1));
- _ ->
- ?line delayed = gen_fsm:sync_send_event(my_fsm, {delayed_answer,1}, 100),
- ?line {'EXIT',{timeout,_}} =
- (catch gen_fsm:sync_send_event(my_fsm, {delayed_answer,10}, 1))
- end,
+ delayed = gen_fsm:sync_send_event(my_fsm, {delayed_answer,1}, 100),
+ {'EXIT',{timeout,_}} =
+ (catch gen_fsm:sync_send_event(my_fsm, {delayed_answer,10}, 1)),
test_server:messages_get(),
ok.
diff --git a/lib/stdlib/test/gen_server_SUITE.erl b/lib/stdlib/test/gen_server_SUITE.erl
index cdf15ba017..dffeadb423 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-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -37,7 +37,8 @@
% spawn export
-export([spec_init_local/2, spec_init_global/2, spec_init_via/2,
- spec_init_default_timeout/2, spec_init_anonymous/1,
+ spec_init_default_timeout/2, spec_init_global_default_timeout/2,
+ spec_init_anonymous/1,
spec_init_anonymous_default_timeout/1,
spec_init_not_proc_lib/1, cast_fast_messup/0]).
@@ -230,14 +231,6 @@ start(Config) when is_list(Config) ->
end,
test_server:messages_get(),
- %% Must wait for all error messages before going to next test.
- %% (otherwise it interferes too much with real time characteristics).
- case os:type() of
- vxworks ->
- receive after 5000 -> ok end;
- _ ->
- ok
- end,
process_flag(trap_exit, OldFl),
ok.
@@ -749,7 +742,7 @@ spec_init(suite) ->
spec_init(Config) when is_list(Config) ->
OldFlag = process_flag(trap_exit, true),
-
+
?line {ok, Pid0} = start_link(spec_init_local, [{ok, my_server}, []]),
?line ok = gen_server:call(Pid0, started_p),
?line ok = gen_server:call(Pid0, stop),
@@ -819,6 +812,14 @@ spec_init(Config) when is_list(Config) ->
test_server:fail(gen_server_did_not_die)
end,
+ %% Before the OTP-10130 fix this failed because a timeout message
+ %% was generated as the spawned process crashed because a {global, Name}
+ %% was matched as a timeout value instead of matching on scope.
+ {ok, _PidHurra} =
+ start_link(spec_init_global_default_timeout, [{ok, hurra}, []]),
+ timer:sleep(1000),
+ ok = gen_server:call(_PidHurra, started_p),
+
?line Pid5 =
erlang:spawn_link(?MODULE, spec_init_not_proc_lib, [[]]),
receive
@@ -1045,8 +1046,9 @@ call_with_huge_message_queue(Config) when is_list(Config) ->
io:format("Time for empty message queue: ~p", [Time]),
io:format("Time for huge message queue: ~p", [NewTime]),
+ IsCover = test_server:is_cover(),
case (NewTime+1) / (Time+1) of
- Q when Q < 10 ->
+ Q when Q < 10; IsCover ->
ok;
Q ->
io:format("Q = ~p", [Q]),
@@ -1125,6 +1127,15 @@ spec_init_default_timeout({ok, Name}, Options) ->
%% Supervised init can occur here ...
gen_server:enter_loop(?MODULE, Options, {}, {local, Name}).
+%% OTP-10130, A bug was introduced where global scope was not matched when
+%% enter_loop/4 was called (no timeout).
+spec_init_global_default_timeout({ok, Name}, Options) ->
+ process_flag(trap_exit, true),
+ global:register_name(Name, self()),
+ proc_lib:init_ack({ok, self()}),
+ %% Supervised init can occur here ...
+ gen_server:enter_loop(?MODULE, Options, {}, {global, Name}).
+
spec_init_anonymous(Options) ->
process_flag(trap_exit, true),
proc_lib:init_ack({ok, self()}),
diff --git a/lib/stdlib/test/id_transform_SUITE.erl b/lib/stdlib/test/id_transform_SUITE.erl
index e1972a100e..ee97ffe7b3 100644
--- a/lib/stdlib/test/id_transform_SUITE.erl
+++ b/lib/stdlib/test/id_transform_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -26,7 +26,7 @@
id_transform/1]).
-export([check/2,check2/1,g/0,f/1,t/1,t1/1,t2/1,t3/1,t4/1,
- t5/1,t6/1,apa/1,new_fun/0]).
+ t5/1,apa/1,new_fun/0]).
% Serves as test...
-hej(hopp).
@@ -61,7 +61,7 @@ id_transform(Config) when is_list(Config) ->
?line {module,erl_id_trans}=code:load_binary(erl_id_trans,File,Bin),
?line case test_server:purify_is_running() of
false ->
- Dog = ?t:timetrap(?t:hours(1)),
+ Dog = ct:timetrap(?t:hours(1)),
?line Res = run_in_test_suite(),
?t:timetrap_cancel(Dog),
Res;
@@ -388,8 +388,6 @@ t3(A) when is_tuple(A) or is_tuple(A) ->
is_tuple;
t3(A) when record(A, apa) ->
foo;
-t3(A) when {erlang,is_record}(A, apa) ->
- foo;
t3(A) when erlang:is_record(A, apa) ->
foo;
t3(A) when is_record(A, apa) ->
@@ -397,13 +395,10 @@ t3(A) when is_record(A, apa) ->
t3(A) when record({apa}, apa) ->
{A,foo}.
-t4(_) when {erlang,is_record}({apa}, apa) ->
- foo.
-
-t5(A) when erlang:is_record({apa}, apa) ->
+t4(A) when erlang:is_record({apa}, apa) ->
{A,foo}.
-t6(A) when is_record({apa}, apa) ->
+t5(A) when is_record({apa}, apa) ->
{A,foo}.
-record(apa2,{a=a,b=foo:bar()}).
diff --git a/lib/stdlib/test/io_SUITE.erl b/lib/stdlib/test/io_SUITE.erl
index bb02a879c2..521d7255ea 100644
--- a/lib/stdlib/test/io_SUITE.erl
+++ b/lib/stdlib/test/io_SUITE.erl
@@ -1,7 +1,8 @@
+%% -*- coding: utf-8 -*-
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -27,7 +28,8 @@
otp_6282/1, otp_6354/1, otp_6495/1, otp_6517/1, otp_6502/1,
manpage/1, otp_6708/1, otp_7084/1, otp_7421/1,
io_lib_collect_line_3_wb/1, cr_whitespace_in_string/1,
- io_fread_newlines/1, otp_8989/1, io_lib_fread_literal/1]).
+ io_fread_newlines/1, otp_8989/1, io_lib_fread_literal/1,
+ io_lib_print_binary_depth_one/1, otp_10302/1]).
%-define(debug, true).
@@ -62,7 +64,8 @@ all() ->
otp_6282, otp_6354, otp_6495, otp_6517, otp_6502,
manpage, otp_6708, otp_7084, otp_7421,
io_lib_collect_line_3_wb, cr_whitespace_in_string,
- io_fread_newlines, otp_8989, io_lib_fread_literal].
+ io_fread_newlines, otp_8989, io_lib_fread_literal,
+ io_lib_print_binary_depth_one, otp_10302].
groups() ->
[].
@@ -892,7 +895,7 @@ otp_6354(Config) when is_list(Config) ->
?line "\"\\b\\t\\n\\v\\f\\r\\e\250\"" =
p([8,9,10,11,12,13,27,168], 1, 40, -1),
% ?line "\"\\b\\t\\n\"\n \"\\v\\f\\r\"\n \"\\e\250\"" =
- ?line "\"\\b\\t\\n\\v\\f\\r\\e�\"" =
+ ?line "\"\\b\\t\\n\\v\\f\\r\\e¨\"" =
p([8,9,10,11,12,13,27,168], 1, 10, -1),
?line "\"\\b\\t\\n\\v\\f\\r\\e\250\"" =
p([8,9,10,11,12,13,27,168], 1, 40, 100),
@@ -2021,3 +2024,55 @@ io_lib_fread_literal(Suite) when is_list(Suite) ->
?line {done,{error,{fread,input}},_} = io_lib:fread(C2, eof, " d"),
?line {done,{ok,[]},[]} = io_lib:fread(C2, "d\n", " d"),
ok.
+
+io_lib_print_binary_depth_one(doc) ->
+ "Test binaries printed with a depth of one behave correctly";
+io_lib_print_binary_depth_one(Suite) when is_list(Suite) ->
+ ?line "<<>>" = fmt("~W", [<<>>, 1]),
+ ?line "<<>>" = fmt("~P", [<<>>, 1]),
+ ?line "<<...>>" = fmt("~W", [<<1>>, 1]),
+ ?line "<<...>>" = fmt("~P", [<<1>>, 1]),
+ ?line "<<...>>" = fmt("~W", [<<1:7>>, 1]),
+ ?line "<<...>>" = fmt("~P", [<<1:7>>, 1]),
+ ok.
+
+otp_10302(doc) ->
+ "OTP-10302. Unicode";
+otp_10302(Suite) when is_list(Suite) ->
+ "\"\x{400}\"" = pretty("\x{400}", -1),
+ "<<\"\x{400}\"/utf8>>" = pretty(<<"\x{400}"/utf8>>, -1),
+
+ "<<\"\x{400}foo\"/utf8>>" = pretty(<<"\x{400}foo"/utf8>>, 2),
+ "<<\"äppl\"/utf8>>" = pretty(<<"äppl"/utf8>>, 2),
+ "<<\"äppl\"/utf8...>>" = pretty(<<"äpple"/utf8>>, 2),
+ "<<\"apel\">>" = pretty(<<"apel">>, 2),
+ "<<\"apel\"...>>" = pretty(<<"apelsin">>, 2),
+ "<<228,112,112,108>>" = fmt("~tp", [<<"äppl">>]),
+ "<<228,...>>" = fmt("~tP", [<<"äppl">>, 2]),
+
+ Chars = lists:seq(0, 512), % just a few...
+ [] = [C || C <- Chars, S <- io_lib:write_unicode_char_as_latin1(C),
+ not is_latin1(S)],
+ L1 = [S || C <- Chars, S <- io_lib:write_unicode_char(C),
+ not is_latin1(S)],
+ L1 = lists:seq(256, 512),
+
+ [] = [C || C <- Chars, S <- io_lib:write_unicode_string_as_latin1([C]),
+ not is_latin1(S)],
+ L2 = [S || C <- Chars, S <- io_lib:write_unicode_string([C]),
+ not is_latin1(S)],
+ L2 = lists:seq(256, 512),
+
+ ok.
+
+pretty(Term, Depth) when is_integer(Depth) ->
+ Opts = [{column, 1}, {line_length, 20},
+ {depth, Depth}, {max_chars, 60},
+ {encoding, unicode}],
+ pretty(Term, Opts);
+pretty(Term, Opts) when is_list(Opts) ->
+ R = io_lib_pretty:print(Term, Opts),
+ lists:flatten(io_lib:format("~ts", [R])).
+
+is_latin1(S) ->
+ S >= 0 andalso S =< 255.
diff --git a/lib/stdlib/test/io_proto_SUITE.erl b/lib/stdlib/test/io_proto_SUITE.erl
index 661d57c85b..299daf0e42 100644
--- a/lib/stdlib/test/io_proto_SUITE.erl
+++ b/lib/stdlib/test/io_proto_SUITE.erl
@@ -96,7 +96,8 @@ groups() ->
[].
init_per_suite(Config) ->
- Config.
+ DefShell = get_default_shell(),
+ [{default_shell,DefShell}|Config].
end_per_suite(_Config) ->
ok.
@@ -124,20 +125,25 @@ unicode_prompt(doc) ->
["Test that an Unicode prompt does not crash the shell"];
unicode_prompt(Config) when is_list(Config) ->
?line PA = filename:dirname(code:which(?MODULE)),
- ?line rtnode([{putline,""},
- {putline, "2."},
- {getline, "2"},
- {putline, "shell:prompt_func({io_proto_SUITE,uprompt})."},
- {getline, "default"},
- {putline, "io:get_line('')."},
- {putline, "hej"},
- {getline, "\"hej\\n\""},
- {putline, "io:setopts([{binary,true}])."},
- {getline, "ok"},
- {putline, "io:get_line('')."},
- {putline, "hej"},
- {getline, "<<\"hej\\n\">>"}
- ],[],[],"-pa \""++ PA++"\""),
+ case proplists:get_value(default_shell,Config) of
+ old ->
+ ok;
+ new ->
+ ?line rtnode([{putline,""},
+ {putline, "2."},
+ {getline, "2"},
+ {putline, "shell:prompt_func({io_proto_SUITE,uprompt})."},
+ {getline, "default"},
+ {putline, "io:get_line('')."},
+ {putline, "hej"},
+ {getline, "\"hej\\n\""},
+ {putline, "io:setopts([{binary,true}])."},
+ {getline, "ok"},
+ {putline, "io:get_line('')."},
+ {putline, "hej"},
+ {getline, "<<\"hej\\n\">>"}
+ ],[],[],"-pa \""++ PA++"\"")
+ end,
%% And one with oldshell
?line rtnode([{putline,""},
{putline, "2."},
@@ -234,21 +240,26 @@ setopts_getopts(Config) when is_list(Config) ->
lists:sort(io:getopts(RFile)),
?line eof = io:get_line(RFile,''),
?line file:close(RFile),
- %% So, lets test another node with new interactive shell
- ?line rtnode([{putline,""},
- {putline, "2."},
- {getline, "2"},
- {putline, "lists:keyfind(binary,1,io:getopts())."},
- {getline, "{binary,false}"},
- {putline, "io:get_line('')."},
- {putline, "hej"},
- {getline, "\"hej\\n\""},
- {putline, "io:setopts([{binary,true}])."},
- {getline, "ok"},
- {putline, "io:get_line('')."},
- {putline, "hej"},
- {getline, "<<\"hej\\n\">>"}
- ],[]),
+ case proplists:get_value(default_shell,Config) of
+ old ->
+ ok;
+ new ->
+ %% So, lets test another node with new interactive shell
+ ?line rtnode([{putline,""},
+ {putline, "2."},
+ {getline, "2"},
+ {putline, "lists:keyfind(binary,1,io:getopts())."},
+ {getline, "{binary,false}"},
+ {putline, "io:get_line('')."},
+ {putline, "hej"},
+ {getline, "\"hej\\n\""},
+ {putline, "io:setopts([{binary,true}])."},
+ {getline, "ok"},
+ {putline, "io:get_line('')."},
+ {putline, "hej"},
+ {getline, "<<\"hej\\n\">>"}
+ ],[])
+ end,
%% And one with oldshell
?line rtnode([{putline,""},
{putline, "2."},
@@ -433,21 +444,27 @@ unicode_options(Config) when is_list(Config) ->
end,
?line [ ok = CannotWriteFile(F,FailDir) || F <- AllNoBom ],
- %% OK, time for the group_leaders...
- ?line rtnode([{putline,""},
- {putline, "2."},
- {getline, "2"},
- {putline, "lists:keyfind(encoding,1,io:getopts())."},
- {getline, "{encoding,latin1}"},
- {putline, "io:format(\"~ts~n\",[[1024]])."},
- {getline, "\\x{400}"},
- {putline, "io:setopts([unicode])."},
- {getline, "ok"},
- {putline, "io:format(\"~ts~n\",[[1024]])."},
- {getline,
- binary_to_list(unicode:characters_to_binary(
- [1024],unicode,utf8))}
- ],[],"LC_CTYPE=\""++get_lc_ctype()++"\"; export LC_CTYPE; "),
+ case proplists:get_value(default_shell,Config) of
+ old ->
+ ok;
+ new ->
+ %% OK, time for the group_leaders...
+ ?line rtnode([{putline,""},
+ {putline, "2."},
+ {getline, "2"},
+ {putline, "lists:keyfind(encoding,1,io:getopts())."},
+ {getline, "{encoding,latin1}"},
+ {putline, "io:format(\"~ts~n\",[[1024]])."},
+ {getline, "\\x{400}"},
+ {putline, "io:setopts([unicode])."},
+ {getline, "ok"},
+ {putline, "io:format(\"~ts~n\",[[1024]])."},
+ {getline,
+ binary_to_list(unicode:characters_to_binary(
+ [1024],unicode,utf8))}
+ ],[],"LC_CTYPE=\""++get_lc_ctype()++"\"; "
+ "export LC_CTYPE; ")
+ end,
?line rtnode([{putline,""},
{putline, "2."},
{getline_re, ".*2."},
@@ -680,23 +697,28 @@ binary_options(Config) when is_list(Config) ->
?line file:close(F3),
%% OK, time for the group_leaders...
%% io:format(standard_error,"Hmmm:~w~n",["<<\""++binary_to_list(<<"\345\344\366"/utf8>>)++"\\n\">>"]),
- ?line rtnode([{putline,""},
- {putline, "2."},
- {getline, "2"},
- {putline, "lists:keyfind(binary,1,io:getopts())."},
- {getline, "{binary,false}"},
- {putline, "io:get_line('')."},
- {putline, "hej"},
- {getline, "\"hej\\n\""},
- {putline, "io:setopts([{binary,true},unicode])."},
- {getline, "ok"},
- {putline, "io:get_line('')."},
- {putline, "hej"},
- {getline, "<<\"hej\\n\">>"},
- {putline, "io:get_line('')."},
- {putline, binary_to_list(<<"\345\344\366"/utf8>>)},
- {getline, "<<\""++binary_to_list(unicode:characters_to_binary(<<"\345\344\366"/utf8>>,latin1,utf8))++"\\n\">>"}
- ],[]),
+ case proplists:get_value(default_shell,Config) of
+ old ->
+ ok;
+ new ->
+ ?line rtnode([{putline,""},
+ {putline, "2."},
+ {getline, "2"},
+ {putline, "lists:keyfind(binary,1,io:getopts())."},
+ {getline, "{binary,false}"},
+ {putline, "io:get_line('')."},
+ {putline, "hej"},
+ {getline, "\"hej\\n\""},
+ {putline, "io:setopts([{binary,true},unicode])."},
+ {getline, "ok"},
+ {putline, "io:get_line('')."},
+ {putline, "hej"},
+ {getline, "<<\"hej\\n\">>"},
+ {putline, "io:get_line('')."},
+ {putline, binary_to_list(<<"\345\344\366"/utf8>>)},
+ {getline, "<<\""++binary_to_list(unicode:characters_to_binary(<<"\345\344\366"/utf8>>,latin1,utf8))++"\\n\">>"}
+ ],[])
+ end,
%% And one with oldshell
?line rtnode([{putline,""},
{putline, "2."},
@@ -714,7 +736,7 @@ binary_options(Config) when is_list(Config) ->
{getline_re, ".*<<\"hej\\\\n\">>"},
{putline, "io:get_line('')."},
{putline, binary_to_list(<<"\345\344\366"/utf8>>)},
- {getline_re, ".*<<\""++binary_to_list(unicode:characters_to_binary(<<"\345\344\366"/utf8>>,latin1,utf8))++"\\\\n\">>"}
+ {getline_re, ".*<<\""++binary_to_list(<<"\345\344\366"/utf8>>)++"\\\\n\"/utf8>>"}
],[],[],"-oldshell"),
ok.
@@ -1146,9 +1168,11 @@ read_modes_gl(suite) ->
read_modes_gl(doc) ->
["Test various modes when reading from the group leade from another machine"];
read_modes_gl(Config) when is_list(Config) ->
- case get_progs() of
- {error,Reason} ->
+ case {get_progs(),proplists:get_value(default_shell,Config)} of
+ {{error,Reason},_} ->
{skipped,Reason};
+ {_,old} ->
+ {skipper,"No new shell"};
_ ->
read_modes_gl_1(Config,answering_machine1)
end.
@@ -1754,6 +1778,17 @@ get_data_within(Port, Timeout, Acc) ->
timeout
end.
+get_default_shell() ->
+ try
+ rtnode([{putline,""},
+ {putline, "whereis(user_drv)."},
+ {getline, "undefined"}],[]),
+ old
+ catch E:R ->
+ ?dbg({E,R}),
+ new
+ end.
+
%%
%% Test I/O-server
%%
diff --git a/lib/stdlib/test/proc_lib_SUITE.erl b/lib/stdlib/test/proc_lib_SUITE.erl
index c95089117c..8dca69bac4 100644
--- a/lib/stdlib/test/proc_lib_SUITE.erl
+++ b/lib/stdlib/test/proc_lib_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -28,7 +28,7 @@
crash/1, sync_start_nolink/1, sync_start_link/1,
spawn_opt/1, sp1/0, sp2/0, sp3/1, sp4/2, sp5/1,
hibernate/1]).
--export([ otp_6345/1]).
+-export([ otp_6345/1, init_dont_hang/1]).
-export([hib_loop/1, awaken/1]).
@@ -36,7 +36,7 @@
handle_event/2, handle_call/2, handle_info/2,
terminate/2]).
--export([otp_6345_init/1]).
+-export([otp_6345_init/1, init_dont_hang_init/1]).
-ifdef(STANDALONE).
@@ -52,7 +52,7 @@ all() ->
{group, tickets}].
groups() ->
- [{tickets, [], [otp_6345]},
+ [{tickets, [], [otp_6345, init_dont_hang]},
{sync_start, [], [sync_start_nolink, sync_start_link]}].
init_per_suite(Config) ->
@@ -343,6 +343,29 @@ otp_6345_loop() ->
otp_6345_loop()
end.
+%% OTP-9803
+init_dont_hang(suite) ->
+ [];
+init_dont_hang(doc) ->
+ ["Check that proc_lib:start don't hang if spawned process crashes before proc_lib:init_ack/2"];
+init_dont_hang(Config) when is_list(Config) ->
+ %% Start should behave as start_link
+ process_flag(trap_exit, true),
+ StartLinkRes = proc_lib:start_link(?MODULE, init_dont_hang_init, [self()]),
+ try
+ StartLinkRes = proc_lib:start(?MODULE, init_dont_hang_init, [self()], 1000),
+ StartLinkRes = proc_lib:start(?MODULE, init_dont_hang_init, [self()], 1000, []),
+ ok
+ catch _:Error ->
+ io:format("Error ~p /= ~p ~n",[erlang:get_stacktrace(), StartLinkRes]),
+ exit(Error)
+ end.
+
+init_dont_hang_init(Parent) ->
+ 1 = 2.
+
+
+
%%-----------------------------------------------------------------
%% The error_logger handler used.
%%-----------------------------------------------------------------
diff --git a/lib/stdlib/test/qlc_SUITE.erl b/lib/stdlib/test/qlc_SUITE.erl
index 1e74ad7727..cac8309bd9 100644
--- a/lib/stdlib/test/qlc_SUITE.erl
+++ b/lib/stdlib/test/qlc_SUITE.erl
@@ -2969,13 +2969,6 @@ lookup1(Config) when is_list(Config) ->
[3] = lookup_keys(Q)
end, [{1,a},{3,3}])">>,
- <<"A = 3,
- etsc(fun(E) ->
- Q = qlc:q([X || X <- ets:table(E), A =:= {erlang,element}(1, X)]),
- [{3,3}] = qlc:e(Q),
- [3] = lookup_keys(Q)
- end, [{1,a},{3,3}])">>,
-
<<"etsc(fun(E) ->
A = 3,
Q = qlc:q([X || X <- ets:table(E),
@@ -3439,12 +3432,6 @@ lookup2(Config) when is_list(Config) ->
[r] = qlc:e(Q),
[r] = lookup_keys(Q)
end, [{keypos,1}], [#r{}])">>,
- <<"etsc(fun(E) ->
- Q = qlc:q([element(1, X) || X <- ets:table(E),
- {erlang,is_record}(X, r, 2)]),
- [r] = qlc:e(Q),
- [r] = lookup_keys(Q)
- end, [{keypos,1}], [#r{}])">>,
{cres,
<<"etsc(fun(E) ->
Q = qlc:q([element(1, X) || X <- ets:table(E),
@@ -3464,12 +3451,6 @@ lookup2(Config) when is_list(Config) ->
is_record(X, r)]),
[r] = qlc:e(Q),
[r] = lookup_keys(Q)
- end, [{keypos,1}], [#r{}])">>,
- <<"etsc(fun(E) ->
- Q = qlc:q([element(1, X) || X <- ets:table(E),
- {erlang,is_record}(X, r)]),
- [r] = qlc:e(Q),
- [r] = lookup_keys(Q)
end, [{keypos,1}], [#r{}])">>
],
@@ -6081,21 +6062,6 @@ otp_6673(Config) when is_list(Config) ->
],
?line run(Config, Ts_RT),
- %% Ulf Wiger provided a patch that makes QLC work with packages:
- Dir = filename:join(?privdir, "p"),
- ?line ok = filelib:ensure_dir(filename:join(Dir, ".")),
- File = filename:join(Dir, "p.erl"),
- ?line ok = file:write_file(File,
- <<"-module(p.p).\n"
- "-export([q/0]).\n"
- "-include_lib(\"stdlib/include/qlc.hrl\").\n"
- "q() ->\n"
- " .qlc:q([X || X <- [1,2]]).">>),
- ?line {ok, 'p.p'} = compile:file(File, [{outdir,Dir}]),
- ?line code:purge('p.p'),
- ?line {module, 'p.p'} = code:load_abs(filename:rootname(File), 'p.p'),
- ?line [1,2] = qlc:e(p.p:q()),
-
ok.
otp_6964(doc) ->
diff --git a/lib/stdlib/test/re_SUITE.erl b/lib/stdlib/test/re_SUITE.erl
index a542745e67..500f5fadb9 100644
--- a/lib/stdlib/test/re_SUITE.erl
+++ b/lib/stdlib/test/re_SUITE.erl
@@ -1,3 +1,4 @@
+%% -*- coding: utf-8 -*-
%%
%% %CopyrightBegin%
%%
@@ -291,10 +292,10 @@ global_capture(Config) when is_list(Config) ->
?line match = re:run("ABCabcdABCabcdA","a(?<FOO>bcd)",[global,{capture,none,index}]),
?line match = re:run("ABCabcdABCabcdA","a(?<FOO>bcd)",[global,{capture,none,binary}]),
?line match = re:run("ABCabcdABCabcdA","a(?<FOO>bcd)",[global,{capture,none,list}]),
- ?line {match,[[<<195,133,98,99,100>>,<<"bcd">>],[<<"abcd">>,<<"bcd">>]]} = re:run("ABC�bcdABCabcdA",".(?<FOO>bcd)",[global,{capture,all,binary},unicode]),
- ?line {match,[["�bcd","bcd"],["abcd","bcd"]]} = re:run(<<"ABC",8#303,8#205,"bcdABCabcdA">>,".(?<FOO>bcd)",[global,{capture,all,list},unicode]),
- ?line {match,[["�bcd","bcd"],["abcd","bcd"]]} = re:run("ABC�bcdABCabcdA",".(?<FOO>bcd)",[global,{capture,all,list},unicode]),
- ?line {match,[[{3,5},{5,3}],[{11,4},{12,3}]]} = re:run("ABC�bcdABCabcdA",".(?<FOO>bcd)",[global,{capture,all,index},unicode]),
+ ?line {match,[[<<195,133,98,99,100>>,<<"bcd">>],[<<"abcd">>,<<"bcd">>]]} = re:run("ABCÅbcdABCabcdA",".(?<FOO>bcd)",[global,{capture,all,binary},unicode]),
+ ?line {match,[["Åbcd","bcd"],["abcd","bcd"]]} = re:run(<<"ABC",8#303,8#205,"bcdABCabcdA">>,".(?<FOO>bcd)",[global,{capture,all,list},unicode]),
+ ?line {match,[["Åbcd","bcd"],["abcd","bcd"]]} = re:run("ABCÅbcdABCabcdA",".(?<FOO>bcd)",[global,{capture,all,list},unicode]),
+ ?line {match,[[{3,5},{5,3}],[{11,4},{12,3}]]} = re:run("ABCÅbcdABCabcdA",".(?<FOO>bcd)",[global,{capture,all,index},unicode]),
?t:timetrap_cancel(Dog),
ok.
@@ -314,20 +315,26 @@ replace_return(Config) when is_list(Config) ->
Dog = ?t:timetrap(?t:minutes(3)),
?line {'EXIT',{badarg,_}} = (catch re:replace("na","(a","")),
?line <<"nasse">> = re:replace(<<"nisse">>,"i","a",[{return,binary}]),
- ?line <<"ABC�XABCXA">> = re:replace("ABC\305abcdABCabcdA","a(?<FOO>bcd)","X",[global,{return,binary}]),
+ ?line <<"ABCÅXABCXA">> = re:replace("ABC\305abcdABCabcdA","a(?<FOO>bcd)","X",[global,{return,binary}]),
- ?line [<<"ABC�">>,
+ ?line [<<"ABCÅ">>,
<<"X">>,
<<"ABC">>,
<<"X">> |
<<"A">> ] =
- re:replace("ABC�abcdABCabcdA","a(?<FOO>bcd)","X",[global,{return,iodata}]),
- ?line "ABC�XABCXA" = re:replace("ABC�abcdABCabcdA","a(?<FOO>bcd)","X",[global,{return,list},unicode]),
- ?line <<65,66,67,195,133,88,65,66,67,88,65>> = re:replace("ABC�abcdABCabcdA","a(?<FOO>bcd)","X",[global,{return,binary},unicode]),
- ?line <<65,66,67,195,133,88,65,66,67,97,98,99,100,65>> = re:replace("ABC�abcdABCabcdA","a(?<FOO>bcd)","X",[{return,binary},unicode]),
+ re:replace("ABCÅabcdABCabcdA","a(?<FOO>bcd)","X",[global,{return,iodata}]),
+ ?line "ABCÅXABCXA" = re:replace("ABCÅabcdABCabcdA","a(?<FOO>bcd)","X",[global,{return,list},unicode]),
+ ?line <<65,66,67,195,133,88,65,66,67,88,65>> = re:replace("ABCÅabcdABCabcdA","a(?<FOO>bcd)","X",[global,{return,binary},unicode]),
+ ?line <<65,66,67,195,133,88,65,66,67,97,98,99,100,65>> = re:replace("ABCÅabcdABCabcdA","a(?<FOO>bcd)","X",[{return,binary},unicode]),
?line <<"iXk">> = re:replace("abcdefghijk","(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)","\\9X",[{return,binary}]),
?line <<"jXk">> = re:replace("abcdefghijk","(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)","\\10X",[{return,binary}]),
?line <<"Xk">> = re:replace("abcdefghijk","(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)","\\11X",[{return,binary}]),
+ ?line <<"9X1">> = re:replace("12345678901","(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)","\\g9X",[{return,binary}]),
+ ?line <<"0X1">> = re:replace("12345678901","(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)","\\g10X",[{return,binary}]),
+ ?line <<"X1">> = re:replace("12345678901","(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)","\\g11X",[{return,binary}]),
+ ?line <<"971">> = re:replace("12345678901","(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)","\\g{9}7",[{return,binary}]),
+ ?line <<"071">> = re:replace("12345678901","(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)","\\g{10}7",[{return,binary}]),
+ ?line <<"71">> = re:replace("12345678901","(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)","\\g{11}7",[{return,binary}]),
?line "a\x{400}bcX" = re:replace("a\x{400}bcd","d","X",[global,{return,list},unicode]),
?line <<"a",208,128,"bcX">> = re:replace("a\x{400}bcd","d","X",[global,{return,binary},unicode]),
?line "a\x{400}bcd" = re:replace("a\x{400}bcd","Z","X",[global,{return,list},unicode]),
diff --git a/lib/stdlib/test/re_testoutput1_replacement_test.erl b/lib/stdlib/test/re_testoutput1_replacement_test.erl
index 69cb140e0d..8f8d8762ad 100644
--- a/lib/stdlib/test/re_testoutput1_replacement_test.erl
+++ b/lib/stdlib/test/re_testoutput1_replacement_test.erl
@@ -1,7 +1,8 @@
+%% -*- coding: utf-8 -*-
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -274,10 +275,10 @@ run() ->
?line <<"dthing">> = iolist_to_binary(re:replace("dthing","^[^]cde]","y\\1I&MoqRPG&GQa\\1l",[global])),
?line <<"ething">> = iolist_to_binary(re:replace("ething","^[^]cde]","AsxwUn\\1GqkWNdgRJk",[])),
?line <<"ething">> = iolist_to_binary(re:replace("ething","^[^]cde]","AsxwUn\\1GqkWNdgRJk",[global])),
-?line <<"RornKmOnaFr�tWgtW">> = iolist_to_binary(re:replace("�","^\\�","R\\1o\\1r\\1nKmOnaFr&tWgtW",[])),
-?line <<"RornKmOnaFr�tWgtW">> = iolist_to_binary(re:replace("�","^\\�","R\\1o\\1r\\1nKmOnaFr&tWgtW",[global])),
-?line <<"ufbmbfOYuK�wf�E�dx">> = iolist_to_binary(re:replace("�","^�","ufbmbfOYuK&wf&E&\\1dx",[])),
-?line <<"ufbmbfOYuK�wf�E�dx">> = iolist_to_binary(re:replace("�","^�","ufbmbfOYuK&wf&E&\\1dx",[global])),
+?line <<"RornKmOnaFrtWgtW">> = iolist_to_binary(re:replace("","^\\","R\\1o\\1r\\1nKmOnaFr&tWgtW",[])),
+?line <<"RornKmOnaFrtWgtW">> = iolist_to_binary(re:replace("","^\\","R\\1o\\1r\\1nKmOnaFr&tWgtW",[global])),
+?line <<"ufbmbfOYuKÿwfÿEÿdx">> = iolist_to_binary(re:replace("ÿ","^ÿ","ufbmbfOYuK&wf&E&\\1dx",[])),
+?line <<"ufbmbfOYuKÿwfÿEÿdx">> = iolist_to_binary(re:replace("ÿ","^ÿ","ufbmbfOYuK&wf&E&\\1dx",[global])),
?line <<"oAdJme0jw">> = iolist_to_binary(re:replace("0","^[0-9]+$","oAdJme\\1&jw",[])),
?line <<"oAdJme0jw">> = iolist_to_binary(re:replace("0","^[0-9]+$","oAdJme\\1&jw",[global])),
?line <<"1aoKN">> = iolist_to_binary(re:replace("1","^[0-9]+$","&aoKN",[])),
@@ -14972,10 +14973,10 @@ def">> = iolist_to_binary(re:replace("abc
def","abc$","M",[global])),
?line <<"abcWCabcSYXGPjRugTabcVGabcSX">> = iolist_to_binary(re:replace("abcS","(abc)\\123","\\1WC&YXGPjRugT\\1VG&X",[])),
?line <<"abcWCabcSYXGPjRugTabcVGabcSX">> = iolist_to_binary(re:replace("abcS","(abc)\\123","\\1WC&YXGPjRugT\\1VG&X",[global])),
-?line <<"fabc�Uabc�UmiqabceCsabcabc�">> = iolist_to_binary(re:replace("abc�","(abc)\\223","f&U&Umiq\\1eCs\\1&",[])),
-?line <<"fabc�Uabc�UmiqabceCsabcabc�">> = iolist_to_binary(re:replace("abc�","(abc)\\223","f&U&Umiq\\1eCs\\1&",[global])),
-?line <<"JRFabcxnbabc�Vkabc�fWigQMuaY">> = iolist_to_binary(re:replace("abc�","(abc)\\323","JRF\\1xnb&Vk&fWigQMuaY",[])),
-?line <<"JRFabcxnbabc�Vkabc�fWigQMuaY">> = iolist_to_binary(re:replace("abc�","(abc)\\323","JRF\\1xnb&Vk&fWigQMuaY",[global])),
+?line <<"fabc“Uabc“UmiqabceCsabcabc“">> = iolist_to_binary(re:replace("abc“","(abc)\\223","f&U&Umiq\\1eCs\\1&",[])),
+?line <<"fabc“Uabc“UmiqabceCsabcabc“">> = iolist_to_binary(re:replace("abc“","(abc)\\223","f&U&Umiq\\1eCs\\1&",[global])),
+?line <<"JRFabcxnbabcÓVkabcÓfWigQMuaY">> = iolist_to_binary(re:replace("abcÓ","(abc)\\323","JRF\\1xnb&Vk&fWigQMuaY",[])),
+?line <<"JRFabcxnbabcÓVkabcÓfWigQMuaY">> = iolist_to_binary(re:replace("abcÓ","(abc)\\323","JRF\\1xnb&Vk&fWigQMuaY",[global])),
?line <<"vgabc@QQ">> = iolist_to_binary(re:replace("abc@","(abc)\\100","vg&QQ",[])),
?line <<"vgabc@QQ">> = iolist_to_binary(re:replace("abc@","(abc)\\100","vg&QQ",[global])),
?line <<"abc@OkvNytabc@abcabc@a">> = iolist_to_binary(re:replace("abc@","(abc)\\100","&OkvNyt&\\1&a",[])),
@@ -18343,24 +18344,24 @@ xb","(?!^)x","\\1tysI\\1v\\1BVwx\\1FOWG\\1&C",[multiline,
?line <<"cY">> = iolist_to_binary(re:replace("M","\\M","cY\\1",[global])),
?line <<"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(re:replace("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","(a+)*b","yWOTIFhIX\\1H",[])),
?line <<"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(re:replace("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","(a+)*b","yWOTIFhIX\\1H",[global])),
-?line <<"NREGularsRREGularWEYrVRr">> = iolist_to_binary(re:replace("REGular","(?i)reg(?:ul(?:[a�]|ae)r|ex)","N&\\1sR&WEYrVRr",[])),
-?line <<"NREGularsRREGularWEYrVRr">> = iolist_to_binary(re:replace("REGular","(?i)reg(?:ul(?:[a�]|ae)r|ex)","N&\\1sR&WEYrVRr",[global])),
-?line <<"G">> = iolist_to_binary(re:replace("regulaer","(?i)reg(?:ul(?:[a�]|ae)r|ex)","G",[])),
-?line <<"G">> = iolist_to_binary(re:replace("regulaer","(?i)reg(?:ul(?:[a�]|ae)r|ex)","G",[global])),
-?line <<"PSsXtwlmy">> = iolist_to_binary(re:replace("Regex","(?i)reg(?:ul(?:[a�]|ae)r|ex)","PSsXtwlmy",[])),
-?line <<"PSsXtwlmy">> = iolist_to_binary(re:replace("Regex","(?i)reg(?:ul(?:[a�]|ae)r|ex)","PSsXtwlmy",[global])),
-?line <<"regul�rmiYTi">> = iolist_to_binary(re:replace("regul�r","(?i)reg(?:ul(?:[a�]|ae)r|ex)","&miYTi\\1\\1",[])),
-?line <<"regul�rmiYTi">> = iolist_to_binary(re:replace("regul�r","(?i)reg(?:ul(?:[a�]|ae)r|ex)","&miYTi\\1\\1",[global])),
-?line <<"W�����rxh�����yUoaLOIegmSA">> = iolist_to_binary(re:replace("�����","����[�-��-�]+","W&rxh&yUoaL\\1OIegmS\\1A",[])),
-?line <<"W�����rxh�����yUoaLOIegmSA">> = iolist_to_binary(re:replace("�����","����[�-��-�]+","W&rxh&yUoaL\\1OIegmS\\1A",[global])),
-?line <<"F�����gnWPyHeh�����tXTQ">> = iolist_to_binary(re:replace("�����","����[�-��-�]+","F&gnWPyHe\\1h&tXTQ",[])),
-?line <<"F�����gnWPyHeh�����tXTQ">> = iolist_to_binary(re:replace("�����","����[�-��-�]+","F&gnWPyHe\\1h&tXTQ",[global])),
-?line <<"sHerHnAhAdx">> = iolist_to_binary(re:replace("�����","����[�-��-�]+","sHer\\1HnA\\1h\\1Adx",[])),
-?line <<"sHerHnAhAdx">> = iolist_to_binary(re:replace("�����","����[�-��-�]+","sHer\\1HnA\\1h\\1Adx",[global])),
-?line <<"trobAQoU�����n">> = iolist_to_binary(re:replace("�����","����[�-��-�]+","tr\\1obAQoU&n",[])),
-?line <<"trobAQoU�����n">> = iolist_to_binary(re:replace("�����","����[�-��-�]+","tr\\1obAQoU&n",[global])),
-?line <<"�XAZSd">> = iolist_to_binary(re:replace("�XAZXB","(?<=Z)X.","Sd",[])),
-?line <<"�XAZSd">> = iolist_to_binary(re:replace("�XAZXB","(?<=Z)X.","Sd",[global])),
+?line <<"NREGularsRREGularWEYrVRr">> = iolist_to_binary(re:replace("REGular","(?i)reg(?:ul(?:[aä]|ae)r|ex)","N&\\1sR&WEYrVRr",[])),
+?line <<"NREGularsRREGularWEYrVRr">> = iolist_to_binary(re:replace("REGular","(?i)reg(?:ul(?:[aä]|ae)r|ex)","N&\\1sR&WEYrVRr",[global])),
+?line <<"G">> = iolist_to_binary(re:replace("regulaer","(?i)reg(?:ul(?:[aä]|ae)r|ex)","G",[])),
+?line <<"G">> = iolist_to_binary(re:replace("regulaer","(?i)reg(?:ul(?:[aä]|ae)r|ex)","G",[global])),
+?line <<"PSsXtwlmy">> = iolist_to_binary(re:replace("Regex","(?i)reg(?:ul(?:[aä]|ae)r|ex)","PSsXtwlmy",[])),
+?line <<"PSsXtwlmy">> = iolist_to_binary(re:replace("Regex","(?i)reg(?:ul(?:[aä]|ae)r|ex)","PSsXtwlmy",[global])),
+?line <<"regulärmiYTi">> = iolist_to_binary(re:replace("regulär","(?i)reg(?:ul(?:[aä]|ae)r|ex)","&miYTi\\1\\1",[])),
+?line <<"regulärmiYTi">> = iolist_to_binary(re:replace("regulär","(?i)reg(?:ul(?:[aä]|ae)r|ex)","&miYTi\\1\\1",[global])),
+?line <<"WÅæåäàrxhÅæåäàyUoaLOIegmSA">> = iolist_to_binary(re:replace("Åæåäà","Åæåä[à-ÿÀ-ß]+","W&rxh&yUoaL\\1OIegmS\\1A",[])),
+?line <<"WÅæåäàrxhÅæåäàyUoaLOIegmSA">> = iolist_to_binary(re:replace("Åæåäà","Åæåä[à-ÿÀ-ß]+","W&rxh&yUoaL\\1OIegmS\\1A",[global])),
+?line <<"FÅæåäÿgnWPyHehÅæåäÿtXTQ">> = iolist_to_binary(re:replace("Åæåäÿ","Åæåä[à-ÿÀ-ß]+","F&gnWPyHe\\1h&tXTQ",[])),
+?line <<"FÅæåäÿgnWPyHehÅæåäÿtXTQ">> = iolist_to_binary(re:replace("Åæåäÿ","Åæåä[à-ÿÀ-ß]+","F&gnWPyHe\\1h&tXTQ",[global])),
+?line <<"sHerHnAhAdx">> = iolist_to_binary(re:replace("ÅæåäÀ","Åæåä[à-ÿÀ-ß]+","sHer\\1HnA\\1h\\1Adx",[])),
+?line <<"sHerHnAhAdx">> = iolist_to_binary(re:replace("ÅæåäÀ","Åæåä[à-ÿÀ-ß]+","sHer\\1HnA\\1h\\1Adx",[global])),
+?line <<"trobAQoUÅæåäßn">> = iolist_to_binary(re:replace("Åæåäß","Åæåä[à-ÿÀ-ß]+","tr\\1obAQoU&n",[])),
+?line <<"trobAQoUÅæåäßn">> = iolist_to_binary(re:replace("Åæåäß","Åæåä[à-ÿÀ-ß]+","tr\\1obAQoU&n",[global])),
+?line <<"„XAZSd">> = iolist_to_binary(re:replace("„XAZXB","(?<=Z)X.","Sd",[])),
+?line <<"„XAZSd">> = iolist_to_binary(re:replace("„XAZXB","(?<=Z)X.","Sd",[global])),
?line <<"A">> = iolist_to_binary(re:replace("ab cd defg","ab cd (?x) de fg","\\1A\\1",[])),
?line <<"A">> = iolist_to_binary(re:replace("ab cd defg","ab cd (?x) de fg","\\1A\\1",[global])),
?line <<"fab cddefgLdtKCtPab cddefgxvVUHDah">> = iolist_to_binary(re:replace("ab cddefg","ab cd(?x) de fg","f&LdtKC\\1\\1tP&xvVUHDah",[])),
diff --git a/lib/stdlib/test/re_testoutput1_split_test.erl b/lib/stdlib/test/re_testoutput1_split_test.erl
index e86a04b008..4fc85b95c0 100644
--- a/lib/stdlib/test/re_testoutput1_split_test.erl
+++ b/lib/stdlib/test/re_testoutput1_split_test.erl
@@ -1,7 +1,8 @@
+%% -*- coding: utf-8 -*-
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -524,14 +525,14 @@ run() ->
?line <<"ething">> = iolist_to_binary(join(re:split("ething","^[^]cde]",[{parts,
2}]))),
?line <<"ething">> = iolist_to_binary(join(re:split("ething","^[^]cde]",[]))),
-?line <<"">> = iolist_to_binary(join(re:split("�","^\\�",[trim]))),
-?line <<":">> = iolist_to_binary(join(re:split("�","^\\�",[{parts,
+?line <<"">> = iolist_to_binary(join(re:split("","^\\",[trim]))),
+?line <<":">> = iolist_to_binary(join(re:split("","^\\",[{parts,
2}]))),
-?line <<":">> = iolist_to_binary(join(re:split("�","^\\�",[]))),
-?line <<"">> = iolist_to_binary(join(re:split("�","^�",[trim]))),
-?line <<":">> = iolist_to_binary(join(re:split("�","^�",[{parts,
+?line <<":">> = iolist_to_binary(join(re:split("","^\\",[]))),
+?line <<"">> = iolist_to_binary(join(re:split("ÿ","^ÿ",[trim]))),
+?line <<":">> = iolist_to_binary(join(re:split("ÿ","^ÿ",[{parts,
2}]))),
-?line <<":">> = iolist_to_binary(join(re:split("�","^�",[]))),
+?line <<":">> = iolist_to_binary(join(re:split("ÿ","^ÿ",[]))),
?line <<"">> = iolist_to_binary(join(re:split("0","^[0-9]+$",[trim]))),
?line <<":">> = iolist_to_binary(join(re:split("0","^[0-9]+$",[{parts,
2}]))),
@@ -22879,14 +22880,14 @@ def","abc$",[]))),
?line <<":abc:">> = iolist_to_binary(join(re:split("abcS","(abc)\\123",[{parts,
2}]))),
?line <<":abc:">> = iolist_to_binary(join(re:split("abcS","(abc)\\123",[]))),
-?line <<":abc">> = iolist_to_binary(join(re:split("abc�","(abc)\\223",[trim]))),
-?line <<":abc:">> = iolist_to_binary(join(re:split("abc�","(abc)\\223",[{parts,
+?line <<":abc">> = iolist_to_binary(join(re:split("abc“","(abc)\\223",[trim]))),
+?line <<":abc:">> = iolist_to_binary(join(re:split("abc“","(abc)\\223",[{parts,
2}]))),
-?line <<":abc:">> = iolist_to_binary(join(re:split("abc�","(abc)\\223",[]))),
-?line <<":abc">> = iolist_to_binary(join(re:split("abc�","(abc)\\323",[trim]))),
-?line <<":abc:">> = iolist_to_binary(join(re:split("abc�","(abc)\\323",[{parts,
+?line <<":abc:">> = iolist_to_binary(join(re:split("abc“","(abc)\\223",[]))),
+?line <<":abc">> = iolist_to_binary(join(re:split("abcÓ","(abc)\\323",[trim]))),
+?line <<":abc:">> = iolist_to_binary(join(re:split("abcÓ","(abc)\\323",[{parts,
2}]))),
-?line <<":abc:">> = iolist_to_binary(join(re:split("abc�","(abc)\\323",[]))),
+?line <<":abc:">> = iolist_to_binary(join(re:split("abcÓ","(abc)\\323",[]))),
?line <<":abc">> = iolist_to_binary(join(re:split("abc@","(abc)\\100",[trim]))),
?line <<":abc:">> = iolist_to_binary(join(re:split("abc@","(abc)\\100",[{parts,
2}]))),
@@ -28929,42 +28930,42 @@ xb","(?!^)x",[multiline]))),
?line <<"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","(a+)*b",[{parts,
2}]))),
?line <<"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","(a+)*b",[]))),
-?line <<"">> = iolist_to_binary(join(re:split("REGular","(?i)reg(?:ul(?:[a�]|ae)r|ex)",[trim]))),
-?line <<":">> = iolist_to_binary(join(re:split("REGular","(?i)reg(?:ul(?:[a�]|ae)r|ex)",[{parts,
+?line <<"">> = iolist_to_binary(join(re:split("REGular","(?i)reg(?:ul(?:[aä]|ae)r|ex)",[trim]))),
+?line <<":">> = iolist_to_binary(join(re:split("REGular","(?i)reg(?:ul(?:[aä]|ae)r|ex)",[{parts,
2}]))),
-?line <<":">> = iolist_to_binary(join(re:split("REGular","(?i)reg(?:ul(?:[a�]|ae)r|ex)",[]))),
-?line <<"">> = iolist_to_binary(join(re:split("regulaer","(?i)reg(?:ul(?:[a�]|ae)r|ex)",[trim]))),
-?line <<":">> = iolist_to_binary(join(re:split("regulaer","(?i)reg(?:ul(?:[a�]|ae)r|ex)",[{parts,
+?line <<":">> = iolist_to_binary(join(re:split("REGular","(?i)reg(?:ul(?:[aä]|ae)r|ex)",[]))),
+?line <<"">> = iolist_to_binary(join(re:split("regulaer","(?i)reg(?:ul(?:[aä]|ae)r|ex)",[trim]))),
+?line <<":">> = iolist_to_binary(join(re:split("regulaer","(?i)reg(?:ul(?:[aä]|ae)r|ex)",[{parts,
2}]))),
-?line <<":">> = iolist_to_binary(join(re:split("regulaer","(?i)reg(?:ul(?:[a�]|ae)r|ex)",[]))),
-?line <<"">> = iolist_to_binary(join(re:split("Regex","(?i)reg(?:ul(?:[a�]|ae)r|ex)",[trim]))),
-?line <<":">> = iolist_to_binary(join(re:split("Regex","(?i)reg(?:ul(?:[a�]|ae)r|ex)",[{parts,
+?line <<":">> = iolist_to_binary(join(re:split("regulaer","(?i)reg(?:ul(?:[aä]|ae)r|ex)",[]))),
+?line <<"">> = iolist_to_binary(join(re:split("Regex","(?i)reg(?:ul(?:[aä]|ae)r|ex)",[trim]))),
+?line <<":">> = iolist_to_binary(join(re:split("Regex","(?i)reg(?:ul(?:[aä]|ae)r|ex)",[{parts,
2}]))),
-?line <<":">> = iolist_to_binary(join(re:split("Regex","(?i)reg(?:ul(?:[a�]|ae)r|ex)",[]))),
-?line <<"">> = iolist_to_binary(join(re:split("regul�r","(?i)reg(?:ul(?:[a�]|ae)r|ex)",[trim]))),
-?line <<":">> = iolist_to_binary(join(re:split("regul�r","(?i)reg(?:ul(?:[a�]|ae)r|ex)",[{parts,
+?line <<":">> = iolist_to_binary(join(re:split("Regex","(?i)reg(?:ul(?:[aä]|ae)r|ex)",[]))),
+?line <<"">> = iolist_to_binary(join(re:split("regulär","(?i)reg(?:ul(?:[aä]|ae)r|ex)",[trim]))),
+?line <<":">> = iolist_to_binary(join(re:split("regulär","(?i)reg(?:ul(?:[aä]|ae)r|ex)",[{parts,
2}]))),
-?line <<":">> = iolist_to_binary(join(re:split("regul�r","(?i)reg(?:ul(?:[a�]|ae)r|ex)",[]))),
-?line <<"">> = iolist_to_binary(join(re:split("�����","����[�-��-�]+",[trim]))),
-?line <<":">> = iolist_to_binary(join(re:split("�����","����[�-��-�]+",[{parts,
+?line <<":">> = iolist_to_binary(join(re:split("regulär","(?i)reg(?:ul(?:[aä]|ae)r|ex)",[]))),
+?line <<"">> = iolist_to_binary(join(re:split("Åæåäà","Åæåä[à-ÿÀ-ß]+",[trim]))),
+?line <<":">> = iolist_to_binary(join(re:split("Åæåäà","Åæåä[à-ÿÀ-ß]+",[{parts,
2}]))),
-?line <<":">> = iolist_to_binary(join(re:split("�����","����[�-��-�]+",[]))),
-?line <<"">> = iolist_to_binary(join(re:split("�����","����[�-��-�]+",[trim]))),
-?line <<":">> = iolist_to_binary(join(re:split("�����","����[�-��-�]+",[{parts,
+?line <<":">> = iolist_to_binary(join(re:split("Åæåäà","Åæåä[à-ÿÀ-ß]+",[]))),
+?line <<"">> = iolist_to_binary(join(re:split("Åæåäÿ","Åæåä[à-ÿÀ-ß]+",[trim]))),
+?line <<":">> = iolist_to_binary(join(re:split("Åæåäÿ","Åæåä[à-ÿÀ-ß]+",[{parts,
2}]))),
-?line <<":">> = iolist_to_binary(join(re:split("�����","����[�-��-�]+",[]))),
-?line <<"">> = iolist_to_binary(join(re:split("�����","����[�-��-�]+",[trim]))),
-?line <<":">> = iolist_to_binary(join(re:split("�����","����[�-��-�]+",[{parts,
+?line <<":">> = iolist_to_binary(join(re:split("Åæåäÿ","Åæåä[à-ÿÀ-ß]+",[]))),
+?line <<"">> = iolist_to_binary(join(re:split("ÅæåäÀ","Åæåä[à-ÿÀ-ß]+",[trim]))),
+?line <<":">> = iolist_to_binary(join(re:split("ÅæåäÀ","Åæåä[à-ÿÀ-ß]+",[{parts,
2}]))),
-?line <<":">> = iolist_to_binary(join(re:split("�����","����[�-��-�]+",[]))),
-?line <<"">> = iolist_to_binary(join(re:split("�����","����[�-��-�]+",[trim]))),
-?line <<":">> = iolist_to_binary(join(re:split("�����","����[�-��-�]+",[{parts,
+?line <<":">> = iolist_to_binary(join(re:split("ÅæåäÀ","Åæåä[à-ÿÀ-ß]+",[]))),
+?line <<"">> = iolist_to_binary(join(re:split("Åæåäß","Åæåä[à-ÿÀ-ß]+",[trim]))),
+?line <<":">> = iolist_to_binary(join(re:split("Åæåäß","Åæåä[à-ÿÀ-ß]+",[{parts,
2}]))),
-?line <<":">> = iolist_to_binary(join(re:split("�����","����[�-��-�]+",[]))),
-?line <<"�XAZ">> = iolist_to_binary(join(re:split("�XAZXB","(?<=Z)X.",[trim]))),
-?line <<"�XAZ:">> = iolist_to_binary(join(re:split("�XAZXB","(?<=Z)X.",[{parts,
+?line <<":">> = iolist_to_binary(join(re:split("Åæåäß","Åæåä[à-ÿÀ-ß]+",[]))),
+?line <<"„XAZ">> = iolist_to_binary(join(re:split("„XAZXB","(?<=Z)X.",[trim]))),
+?line <<"„XAZ:">> = iolist_to_binary(join(re:split("„XAZXB","(?<=Z)X.",[{parts,
2}]))),
-?line <<"�XAZ:">> = iolist_to_binary(join(re:split("�XAZXB","(?<=Z)X.",[]))),
+?line <<"„XAZ:">> = iolist_to_binary(join(re:split("„XAZXB","(?<=Z)X.",[]))),
?line <<"">> = iolist_to_binary(join(re:split("ab cd defg","ab cd (?x) de fg",[trim]))),
?line <<":">> = iolist_to_binary(join(re:split("ab cd defg","ab cd (?x) de fg",[{parts,
2}]))),
diff --git a/lib/stdlib/test/sets_SUITE.erl b/lib/stdlib/test/sets_SUITE.erl
index f284276bd7..e2bcdd18ce 100644
--- a/lib/stdlib/test/sets_SUITE.erl
+++ b/lib/stdlib/test/sets_SUITE.erl
@@ -35,7 +35,7 @@
-import(lists, [foldl/3,reverse/1]).
init_per_testcase(_Case, Config) ->
- ?line Dog = ?t:timetrap(?t:minutes(5)),
+ Dog = ?t:timetrap(?t:minutes(5)),
[{watchdog,Dog}|Config].
end_per_testcase(_Case, Config) ->
@@ -70,65 +70,65 @@ create(Config) when is_list(Config) ->
test_all(fun create_1/1).
create_1(M) ->
- ?line S0 = M:empty(),
- ?line [] = M:to_list(S0),
- ?line 0 = M:size(S0),
- ?line true = M:is_empty(S0),
+ S0 = M(empty, []),
+ [] = M(to_list, S0),
+ 0 = M(size, S0),
+ true = M(is_empty, S0),
E = make_ref(),
- ?line One = M:singleton(E),
- ?line 1 = M:size(One),
- ?line false = M:is_empty(One),
- [E] = M:to_list(One),
+ One = M(singleton, E),
+ 1 = M(size, One),
+ false = M(is_empty, One),
+ [E] = M(to_list, One),
S0.
add_element(Config) when is_list(Config) ->
test_all([{0,132},{253,258},{510,514}], fun add_element_1/2).
add_element_1(List, M) ->
- ?line S = M:from_list(List),
- ?line SortedSet = lists:usort(List),
- ?line SortedSet = lists:sort(M:to_list(S)),
+ S = M(from_list, List),
+ SortedSet = lists:usort(List),
+ SortedSet = lists:sort(M(to_list, S)),
%% Make sure that we get the same result by inserting
%% elements one at the time.
- ?line S2 = foldl(fun(El, Set) -> M:add_element(El, Set) end,
- M:empty(), List),
- ?line true = M:equal(S, S2),
+ S2 = foldl(fun(El, Set) -> M(add_element, {El,Set}) end,
+ M(empty, []), List),
+ true = M(equal, {S,S2}),
%% Insert elements, randomly delete inserted elements,
%% and re-inserted all deleted elements at the end.
- ?line S3 = add_element_del(List, M, M:empty(), [], []),
- ?line true = M:equal(S2, S3),
- ?line true = M:equal(S, S3),
+ S3 = add_element_del(List, M, M(empty, []), [], []),
+ true = M(equal, {S2,S3}),
+ true = M(equal, {S,S3}),
S.
add_element_del([H|T], M, S, Del, []) ->
- add_element_del(T, M, M:add_element(H, S), Del, [H]);
+ add_element_del(T, M, M(add_element, {H,S}), Del, [H]);
add_element_del([H|T], M, S0, Del, Inserted) ->
- S1 = M:add_element(H, S0),
+ S1 = M(add_element, {H,S0}),
case random:uniform(3) of
1 ->
OldEl = lists:nth(random:uniform(length(Inserted)), Inserted),
- S = M:del_element(OldEl, S1),
+ S = M(del_element, {OldEl,S1}),
add_element_del(T, M, S, [OldEl|Del], [H|Inserted]);
_ ->
add_element_del(T, M, S1, Del, [H|Inserted])
end;
add_element_del([], M, S, Del, _) ->
- M:union(S, M:from_list(Del)).
+ M(union, {S,M(from_list, Del)}).
del_element(Config) when is_list(Config) ->
test_all([{0,132},{253,258},{510,514},{1022,1026}], fun del_element_1/2).
del_element_1(List, M) ->
- ?line S0 = M:from_list(List),
- ?line Empty = foldl(fun(El, Set) -> M:del_element(El, Set) end, S0, List),
- ?line Empty = M:empty(),
- ?line M:is_empty(Empty),
- ?line S1 = foldl(fun(El, Set) ->
- M:add_element(El, Set)
- end, S0, reverse(List)),
- ?line true = M:equal(S0, S1),
+ S0 = M(from_list, List),
+ Empty = foldl(fun(El, Set) -> M(del_element, {El,Set}) end, S0, List),
+ Empty = M(empty, []),
+ true = M(is_empty, Empty),
+ S1 = foldl(fun(El, Set) ->
+ M(add_element, {El,Set})
+ end, S0, reverse(List)),
+ true = M(equal, {S0,S1}),
S1.
subtract(Config) when is_list(Config) ->
@@ -138,23 +138,23 @@ subtract(Config) when is_list(Config) ->
test_all([{2,69},{126,130},{253,258},511,512,{1023,1030}], fun subtract_1/2).
subtract_empty(M) ->
- ?line Empty = M:empty(),
- ?line true = M:is_empty(M:subtract(Empty, Empty)),
- M:subtract(Empty, Empty).
+ Empty = M(empty, []),
+ true = M(is_empty, M(subtract, {Empty,Empty})),
+ M(subtract, {Empty,Empty}).
subtract_1(List, M) ->
- ?line S0 = M:from_list(List),
- ?line Empty = M:empty(),
+ S0 = M(from_list, List),
+ Empty = M(empty, []),
%% Trivial cases.
- ?line true = M:is_empty(M:subtract(Empty, S0)),
- ?line true = M:equal(S0, M:subtract(S0, Empty)),
+ true = M(is_empty, M(subtract, {Empty,S0})),
+ true = M(equal, {S0,M(subtract, {S0,Empty})}),
%% Not so trivial.
- ?line subtract_check(List, mutate_some(remove_some(List, 0.4)), M),
- ?line subtract_check(List, rnd_list(length(List) div 2 + 5), M),
- ?line subtract_check(List, rnd_list(length(List) div 7 + 9), M),
- ?line subtract_check(List, mutate_some(List), M).
+ subtract_check(List, mutate_some(remove_some(List, 0.4)), M),
+ subtract_check(List, rnd_list(length(List) div 2 + 5), M),
+ subtract_check(List, rnd_list(length(List) div 7 + 9), M),
+ subtract_check(List, mutate_some(List), M).
subtract_check(A, B, M) ->
one_subtract_check(B, A, M),
@@ -163,12 +163,12 @@ subtract_check(A, B, M) ->
one_subtract_check(A, B, M) ->
ASorted = lists:usort(A),
BSorted = lists:usort(B),
- ASet = M:from_list(A),
- BSet = M:from_list(B),
- DiffSet = M:subtract(ASet, BSet),
+ ASet = M(from_list, A),
+ BSet = M(from_list, B),
+ DiffSet = M(subtract, {ASet,BSet}),
Diff = ASorted -- BSorted,
- true = M:equal(DiffSet, M:from_list(Diff)),
- Diff = lists:sort(M:to_list(DiffSet)),
+ true = M(equal, {DiffSet,M(from_list, Diff)}),
+ Diff = lists:sort(M(to_list, DiffSet)),
DiffSet.
intersection(Config) when is_list(Config) ->
@@ -176,60 +176,60 @@ intersection(Config) when is_list(Config) ->
test_all([{1,65},{126,130},{253,259},{499,513},{1023,1025}], fun intersection_1/2).
intersection_1(List, M) ->
- ?line S0 = M:from_list(List),
+ S0 = M(from_list, List),
%% Intersection with self.
- ?line true = M:equal(S0, M:intersection(S0, S0)),
- ?line true = M:equal(S0, M:intersection([S0,S0])),
- ?line true = M:equal(S0, M:intersection([S0,S0,S0])),
- ?line true = M:equal(S0, M:intersection([S0])),
+ true = M(equal, {S0,M(intersection, {S0,S0})}),
+ true = M(equal, {S0,M(intersection, [S0,S0])}),
+ true = M(equal, {S0,M(intersection, [S0,S0,S0])}),
+ true = M(equal, {S0,M(intersection, [S0])}),
%% Intersection with empty.
- ?line Empty = M:empty(),
- ?line true = M:equal(Empty, M:intersection(S0, Empty)),
- ?line true = M:equal(Empty, M:intersection([S0,Empty,S0,Empty])),
+ Empty = M(empty, []),
+ true = M(equal, {Empty,M(intersection, {S0,Empty})}),
+ true = M(equal, {Empty,M(intersection, [S0,Empty,S0,Empty])}),
%% The intersection of no sets is undefined.
- ?line {'EXIT',_} = (catch M:intersection([])),
+ {'EXIT',_} = (catch M(intersection, [])),
%% Disjoint sets.
- ?line Disjoint = [{El} || El <- List],
- ?line DisjointSet = M:from_list(Disjoint),
- ?line M:is_empty(M:intersection(S0, DisjointSet)),
+ Disjoint = [{El} || El <- List],
+ DisjointSet = M(from_list, Disjoint),
+ true = M(is_empty, M(intersection, {S0,DisjointSet})),
%% Disjoint, different sizes.
- ?line M:is_empty(M:intersection(S0, M:from_list(remove_some(Disjoint, 0.3)))),
- ?line M:is_empty(M:intersection(S0, M:from_list(remove_some(Disjoint, 0.7)))),
- ?line M:is_empty(M:intersection(S0, M:from_list(remove_some(Disjoint, 0.9)))),
- ?line M:is_empty(M:intersection(M:from_list(remove_some(List, 0.3)), DisjointSet)),
- ?line M:is_empty(M:intersection(M:from_list(remove_some(List, 0.5)), DisjointSet)),
- ?line M:is_empty(M:intersection(M:from_list(remove_some(List, 0.9)), DisjointSet)),
+ [begin
+ SomeRemoved = M(from_list, remove_some(Disjoint, HowMuch)),
+ true = M(is_empty, M(intersection, {S0,SomeRemoved})),
+ MoreRemoved = M(from_list, remove_some(List, HowMuch)),
+ true = M(is_empty, M(intersection, {MoreRemoved,DisjointSet}))
+ end || HowMuch <- [0.3,0.5,0.7,0.9]],
%% Partial overlap (one or more elements in result set).
%% The sets have almost the same size. (Almost because a duplicated
%% element in the original list could be mutated and not mutated
%% at the same time.)
- ?line PartialOverlap = mutate_some(List, []),
- ?line IntersectionSet = check_intersection(List, PartialOverlap, M),
- ?line false = M:is_empty(IntersectionSet),
+ PartialOverlap = mutate_some(List, []),
+ IntersectionSet = check_intersection(List, PartialOverlap, M),
+ false = M(is_empty, IntersectionSet),
%% Partial overlap, different set sizes. (Intersection possibly empty.)
- ?line check_intersection(List, remove_some(PartialOverlap, 0.1), M),
- ?line check_intersection(List, remove_some(PartialOverlap, 0.3), M),
- ?line check_intersection(List, remove_some(PartialOverlap, 0.5), M),
- ?line check_intersection(List, remove_some(PartialOverlap, 0.7), M),
- ?line check_intersection(List, remove_some(PartialOverlap, 0.9), M),
+ check_intersection(List, remove_some(PartialOverlap, 0.1), M),
+ check_intersection(List, remove_some(PartialOverlap, 0.3), M),
+ check_intersection(List, remove_some(PartialOverlap, 0.5), M),
+ check_intersection(List, remove_some(PartialOverlap, 0.7), M),
+ check_intersection(List, remove_some(PartialOverlap, 0.9), M),
IntersectionSet.
check_intersection(Orig, Mutated, M) ->
- OrigSet = M:from_list(Orig),
- MutatedSet = M:from_list(Mutated),
+ OrigSet = M(from_list, Orig),
+ MutatedSet = M(from_list, Mutated),
Intersection = [El || El <- Mutated, not is_tuple(El)],
SortedIntersection = lists:usort(Intersection),
- IntersectionSet = M:intersection(OrigSet, MutatedSet),
- true = M:equal(IntersectionSet, M:from_list(SortedIntersection)),
- SortedIntersection = lists:sort(M:to_list(IntersectionSet)),
+ IntersectionSet = M(intersection, {OrigSet,MutatedSet}),
+ true = M(equal, {IntersectionSet,M(from_list, SortedIntersection)}),
+ SortedIntersection = lists:sort(M(to_list, IntersectionSet)),
IntersectionSet.
@@ -239,63 +239,63 @@ union(Config) when is_list(Config) ->
test_all([{1,71},{125,129},{254,259},{510,513},{1023,1025}], fun union_1/2).
union_1(List, M) ->
- ?line S = M:from_list(List),
+ S = M(from_list, List),
%% Union with self and empty.
- ?line Empty = M:empty(),
- ?line true = M:equal(S, M:union(S, S)),
- ?line true = M:equal(S, M:union([S,S])),
- ?line true = M:equal(S, M:union([S,S,Empty])),
- ?line true = M:equal(S, M:union([S,Empty,S])),
- ?line true = M:equal(S, M:union(S, Empty)),
- ?line true = M:equal(S, M:union([S])),
- ?line true = M:is_empty(M:union([])),
+ Empty = M(empty, []),
+ true = M(equal, {S,M(union, {S,S})}),
+ true = M(equal, {S,M(union, [S,S])}),
+ true = M(equal, {S,M(union, [S,S,Empty])}),
+ true = M(equal, {S,M(union, [S,Empty,S])}),
+ true = M(equal, {S,M(union, {S,Empty})}),
+ true = M(equal, {S,M(union, [S])}),
+ true = M(is_empty, M(union, [])),
%% Partial overlap.
- ?line check_union(List, remove_some(mutate_some(List), 0.9), M),
- ?line check_union(List, remove_some(mutate_some(List), 0.7), M),
- ?line check_union(List, remove_some(mutate_some(List), 0.5), M),
- ?line check_union(List, remove_some(mutate_some(List), 0.3), M),
- ?line check_union(List, remove_some(mutate_some(List), 0.1), M),
-
- ?line check_union(List, mutate_some(remove_some(List, 0.9)), M),
- ?line check_union(List, mutate_some(remove_some(List, 0.7)), M),
- ?line check_union(List, mutate_some(remove_some(List, 0.5)), M),
- ?line check_union(List, mutate_some(remove_some(List, 0.3)), M),
- ?line check_union(List, mutate_some(remove_some(List, 0.1)), M).
+ check_union(List, remove_some(mutate_some(List), 0.9), M),
+ check_union(List, remove_some(mutate_some(List), 0.7), M),
+ check_union(List, remove_some(mutate_some(List), 0.5), M),
+ check_union(List, remove_some(mutate_some(List), 0.3), M),
+ check_union(List, remove_some(mutate_some(List), 0.1), M),
+
+ check_union(List, mutate_some(remove_some(List, 0.9)), M),
+ check_union(List, mutate_some(remove_some(List, 0.7)), M),
+ check_union(List, mutate_some(remove_some(List, 0.5)), M),
+ check_union(List, mutate_some(remove_some(List, 0.3)), M),
+ check_union(List, mutate_some(remove_some(List, 0.1)), M).
check_union(Orig, Other, M) ->
- OrigSet = M:from_list(Orig),
- OtherSet = M:from_list(Other),
+ OrigSet = M(from_list, Orig),
+ OtherSet = M(from_list, Other),
Union = Orig++Other,
SortedUnion = lists:usort(Union),
- UnionSet = M:union(OrigSet, OtherSet),
- SortedUnion = lists:sort(M:to_list(UnionSet)),
- M:equal(UnionSet, M:from_list(Union)),
+ UnionSet = M(union, {OrigSet,OtherSet}),
+ SortedUnion = lists:sort(M(to_list, UnionSet)),
+ M(equal, {UnionSet,M(from_list, Union)}),
UnionSet.
is_subset(Config) when is_list(Config) ->
test_all([{1,132},{253,270},{299,311}], fun is_subset_1/2).
is_subset_1(List, M) ->
- ?line S = M:from_list(List),
- ?line Empty = M:empty(),
+ S = M(from_list, List),
+ Empty = M(empty, []),
%% Subset of empty and self.
- ?line true = M:is_subset(Empty, Empty),
- ?line true = M:is_subset(Empty, S),
- ?line false = M:is_subset(S, Empty),
- ?line true = M:is_subset(S, S),
+ true = M(is_subset, {Empty,Empty}),
+ true = M(is_subset, {Empty,S}),
+ false = M(is_subset, {S,Empty}),
+ true = M(is_subset, {S,S}),
%% Other cases.
- Res = [?line false = M:is_subset(M:singleton(make_ref()), S),
- ?line true = M:is_subset(M:singleton(hd(List)), S),
- ?line true = check_subset(remove_some(List, 0.1), List, M),
- ?line true = check_subset(remove_some(List, 0.5), List, M),
- ?line true = check_subset(remove_some(List, 0.9), List, M),
- ?line check_subset(mutate_some(List), List, M),
- ?line check_subset(rnd_list(length(List) div 2 + 5), List, M),
- ?line subtract_check(List, rnd_list(length(List) div 7 + 9), M)
+ Res = [false = M(is_subset, {M(singleton, make_ref()),S}),
+ true = M(is_subset, {M(singleton, hd(List)),S}),
+ true = check_subset(remove_some(List, 0.1), List, M),
+ true = check_subset(remove_some(List, 0.5), List, M),
+ true = check_subset(remove_some(List, 0.9), List, M),
+ check_subset(mutate_some(List), List, M),
+ check_subset(rnd_list(length(List) div 2 + 5), List, M),
+ subtract_check(List, rnd_list(length(List) div 7 + 9), M)
],
res_to_set(Res, M, 0, []).
@@ -304,12 +304,12 @@ check_subset(X, Y, M) ->
check_one_subset(X, Y, M).
check_one_subset(X, Y, M) ->
- XSet = M:from_list(X),
- YSet = M:from_list(Y),
+ XSet = M(from_list, X),
+ YSet = M(from_list, Y),
SortedX = lists:usort(X),
SortedY = lists:usort(Y),
IsSubSet = length(SortedY--SortedX) =:= length(SortedY) - length(SortedX),
- IsSubSet = M:is_subset(XSet, YSet),
+ IsSubSet = M(is_subset, {XSet,YSet}),
IsSubSet.
%% Encode all test results as a set to return.
@@ -317,54 +317,54 @@ res_to_set([true|T], M, I, Acc) ->
res_to_set(T, M, I+1, [I|Acc]);
res_to_set([_|T], M, I, Acc) ->
res_to_set(T, M, I+1, Acc);
-res_to_set([], M, _, Acc) -> M:from_list(Acc).
+res_to_set([], M, _, Acc) -> M(from_list, Acc).
is_set(Config) when is_list(Config) ->
%% is_set/1 is tested in the other test cases when its argument
%% is a set. Here test some arguments that makes it return false.
- ?line false = gb_sets:is_set([a,b]),
- ?line false = gb_sets:is_set({a,very,bad,tuple}),
+ false = gb_sets:is_set([a,b]),
+ false = gb_sets:is_set({a,very,bad,tuple}),
- ?line false = sets:is_set([a,b]),
- ?line false = sets:is_set({a,very,bad,tuple}),
+ false = sets:is_set([a,b]),
+ false = sets:is_set({a,very,bad,tuple}),
- ?line false = ordsets:is_set([b,a]),
- ?line false = ordsets:is_set({bad,tuple}),
+ false = ordsets:is_set([b,a]),
+ false = ordsets:is_set({bad,tuple}),
%% Now test values that are known to be bad for all set representations.
test_all(fun is_set_1/1).
is_set_1(M) ->
- ?line false = M:is_set(self()),
- ?line false = M:is_set(blurf),
- ?line false = M:is_set(make_ref()),
- ?line false = M:is_set(<<1,2,3>>),
- ?line false = M:is_set(42),
- ?line false = M:is_set(math:pi()),
- ?line false = M:is_set({}),
- M:empty().
+ false = M(is_set, self()),
+ false = M(is_set, blurf),
+ false = M(is_set, make_ref()),
+ false = M(is_set, <<1,2,3>>),
+ false = M(is_set, 42),
+ false = M(is_set, math:pi()),
+ false = M(is_set, {}),
+ M(empty, []).
fold(Config) when is_list(Config) ->
test_all([{0,71},{125,129},{254,259},{510,513},{1023,1025},{9999,10001}],
fun fold_1/2).
fold_1(List, M) ->
- ?line S = M:from_list(List),
- ?line L = M:fold(fun(E, A) -> [E|A] end, [], S),
- ?line true = lists:sort(L) =:= lists:usort(List),
- M:empty().
+ S = M(from_list, List),
+ L = M(fold, {fun(E, A) -> [E|A] end,[],S}),
+ true = lists:sort(L) =:= lists:usort(List),
+ M(empty, []).
filter(Config) when is_list(Config) ->
test_all([{0,69},{126,130},{254,259},{510,513},{1023,1025},{7999,8000}],
fun filter_1/2).
filter_1(List, M) ->
- ?line S = M:from_list(List),
+ S = M(from_list, List),
IsNumber = fun(X) -> is_number(X) end,
- ?line M:equal(M:from_list(lists:filter(IsNumber, List)),
- M:filter(IsNumber, S)),
- ?line M:filter(fun(X) -> is_atom(X) end, S).
+ M(equal, {M(from_list, lists:filter(IsNumber, List)),
+ M(filter, {IsNumber,S})}),
+ M(filter, {fun(X) -> is_atom(X) end,S}).
%%%
%%% Test specifics for gb_sets.
@@ -375,26 +375,26 @@ take_smallest(Config) when is_list(Config) ->
fun take_smallest_1/2).
take_smallest_1(List, M) ->
- case M:module() of
+ case M(module, []) of
gb_sets -> take_smallest_2(List, M);
_ -> ok
end,
- M:empty().
+ M(empty, []).
take_smallest_2(List0, M) ->
- ?line List = lists:usort(List0),
- ?line S = M:from_list(List0),
+ List = lists:usort(List0),
+ S = M(from_list, List0),
take_smallest_3(S, List, M).
take_smallest_3(S0, List0, M) ->
- case M:is_empty(S0) of
+ case M(is_empty, S0) of
true -> ok;
false ->
- ?line Smallest = hd(List0),
- ?line Smallest = gb_sets:smallest(S0),
- ?line {Smallest,S} = gb_sets:take_smallest(S0),
- ?line List = tl(List0),
- ?line true = gb_sets:to_list(S) =:= List,
+ Smallest = hd(List0),
+ Smallest = gb_sets:smallest(S0),
+ {Smallest,S} = gb_sets:take_smallest(S0),
+ List = tl(List0),
+ true = gb_sets:to_list(S) =:= List,
take_smallest_3(S, List, M)
end.
@@ -403,26 +403,26 @@ take_largest(Config) when is_list(Config) ->
fun take_largest_1/2).
take_largest_1(List, M) ->
- case M:module() of
+ case M(module, []) of
gb_sets -> take_largest_2(List, M);
_ -> ok
end,
- M:empty().
+ M(empty, []).
take_largest_2(List0, M) ->
- ?line List = reverse(lists:usort(List0)),
- ?line S = M:from_list(List0),
+ List = reverse(lists:usort(List0)),
+ S = M(from_list, List0),
take_largest_3(S, List, M).
take_largest_3(S0, List0, M) ->
- case M:is_empty(S0) of
+ case M(is_empty, S0) of
true -> ok;
false ->
- ?line Largest = hd(List0),
- ?line Largest = gb_sets:largest(S0),
- ?line {Largest,S} = gb_sets:take_largest(S0),
- ?line List = tl(List0),
- ?line true = gb_sets:to_list(S) =:= reverse(List),
+ Largest = hd(List0),
+ Largest = gb_sets:largest(S0),
+ {Largest,S} = gb_sets:take_largest(S0),
+ List = tl(List0),
+ true = gb_sets:to_list(S) =:= reverse(List),
take_largest_3(S, List, M)
end.
@@ -441,23 +441,23 @@ sets_mods() ->
[Ordsets,Sets,Gb].
test_all(Tester) ->
- ?line Res = [begin
- random:seed(1, 2, 42),
- S = Tester(M),
- {M:size(S),lists:sort(M:to_list(S))}
- end || M <- sets_mods()],
- ?line all_same(Res).
+ Res = [begin
+ random:seed(1, 2, 42),
+ S = Tester(M),
+ {M(size, S),lists:sort(M(to_list, S))}
+ end || M <- sets_mods()],
+ all_same(Res).
test_all([{Low,High}|T], Tester) ->
test_all(lists:seq(Low, High)++T, Tester);
test_all([Sz|T], Tester) when is_integer(Sz) ->
List = rnd_list(Sz),
- ?line Res = [begin
+ Res = [begin
random:seed(19, 2, Sz),
S = Tester(List, M),
- {M:size(S),lists:sort(M:to_list(S))}
+ {M(size, S),lists:sort(M(to_list, S))}
end || M <- sets_mods()],
- ?line all_same(Res),
+ all_same(Res),
test_all(T, Tester);
test_all([], _) -> ok.
diff --git a/lib/stdlib/test/sets_test_lib.erl b/lib/stdlib/test/sets_test_lib.erl
index bdfb0d59d2..fd4ec2bac3 100644
--- a/lib/stdlib/test/sets_test_lib.erl
+++ b/lib/stdlib/test/sets_test_lib.erl
@@ -17,91 +17,89 @@
%% %CopyrightEnd%
%%
--module(sets_test_lib, [Mod,Equal]).
-
--export([module/0,equal/2,empty/0,from_list/1,to_list/1,singleton/1,
- add_element/2,del_element/2,size/1,is_empty/1,is_set/1,
- intersection/1,intersection/2,subtract/2,
- union/1,union/2,is_subset/2,fold/3,filter/2]).
-
-module() ->
- Mod.
-
-equal(X, Y) ->
- Equal(X, Y).
-
-empty() ->
- Mod:new().
-
-from_list(L) ->
- Mod:from_list(L).
-
-to_list(S) ->
- Mod:to_list(S).
+-module(sets_test_lib).
+
+-export([new/2]).
+
+new(Mod, Eq) ->
+ fun (add_element, {El,S}) -> add_element(Mod, El, S);
+ (del_element, {El,S}) -> del_element(Mod, El, S);
+ (empty, []) -> Mod:new();
+ (equal, {S1,S2}) -> Eq(S1, S2);
+ (filter, {F,S}) -> filter(Mod, F, S);
+ (fold, {F,A,S}) -> fold(Mod, F, A, S);
+ (from_list, L) -> Mod:from_list(L);
+ (intersection, {S1,S2}) -> intersection(Mod, Eq, S1, S2);
+ (intersection, Ss) -> intersection(Mod, Eq, Ss);
+ (is_empty, S) -> is_empty(Mod, S);
+ (is_set, S) -> Mod:is_set(S);
+ (is_subset, {S,Set}) -> is_subset(Mod, Eq, S, Set);
+ (module, []) -> Mod;
+ (singleton, E) -> singleton(Mod, E);
+ (size, S) -> Mod:size(S);
+ (subtract, {S1,S2}) -> subtract(Mod, S1, S2);
+ (to_list, S) -> Mod:to_list(S);
+ (union, {S1,S2}) -> union(Mod, Eq, S1, S2);
+ (union, Ss) -> union(Mod, Eq, Ss)
+ end.
-singleton(E) ->
+singleton(Mod, E) ->
case erlang:function_exported(Mod, singleton, 1) of
true -> Mod:singleton(E);
- false -> from_list([E])
+ false -> Mod:from_list([E])
end.
-add_element(El, S0) ->
+add_element(Mod, El, S0) ->
S = Mod:add_element(El, S0),
true = Mod:is_element(El, S),
- false = is_empty(S),
+ false = is_empty(Mod, S),
true = Mod:is_set(S),
S.
-del_element(El, S0) ->
+del_element(Mod, El, S0) ->
S = Mod:del_element(El, S0),
false = Mod:is_element(El, S),
true = Mod:is_set(S),
S.
-size(S) ->
- Mod:size(S).
-
-is_empty(S) ->
+is_empty(Mod, S) ->
true = Mod:is_set(S),
case erlang:function_exported(Mod, is_empty, 1) of
true -> Mod:is_empty(S);
false -> Mod:size(S) == 0
end.
-is_set(S) ->
- Mod:is_set(S).
-
-intersection(S1, S2) ->
+intersection(Mod, Equal, S1, S2) ->
S = Mod:intersection(S1, S2),
true = Equal(S, Mod:intersection(S2, S1)),
- Disjoint = is_empty(S),
+ Disjoint = is_empty(Mod, S),
Disjoint = Mod:is_disjoint(S1, S2),
Disjoint = Mod:is_disjoint(S2, S1),
S.
-intersection(Ss) ->
+intersection(Mod, Equal, Ss) ->
S = Mod:intersection(Ss),
true = Equal(S, Mod:intersection(lists:reverse(Ss))),
S.
-subtract(S1, S2) ->
+subtract(Mod, S1, S2) ->
S = Mod:subtract(S1, S2),
true = Mod:is_set(S),
true = Mod:size(S) =< Mod:size(S1),
S.
-union(S1, S2) ->
+union(Mod, Equal, S1, S2) ->
S = Mod:union(S1, S2),
true = Equal(S, Mod:union(S2, S1)),
true = Mod:is_set(S),
S.
-union(Ss) ->
+union(Mod, Equal, Ss) ->
S = Mod:union(Ss),
true = Equal(S, Mod:union(lists:reverse(Ss))),
S.
-is_subset(S, Set) ->
+is_subset(Mod, Equal, S, Set) ->
case Mod:is_subset(S, Set) of
false -> false;
true ->
@@ -115,10 +113,10 @@ is_subset(S, Set) ->
true
end.
-fold(F, A, S) ->
+fold(Mod, F, A, S) ->
true = Mod:is_set(S),
Mod:fold(F, A, S).
-filter(F, S) ->
+filter(Mod, F, S) ->
true = Mod:is_set(S),
Mod:filter(F, S).
diff --git a/lib/stdlib/test/shell_SUITE.erl b/lib/stdlib/test/shell_SUITE.erl
index 4b83e42ee0..f22df96697 100644
--- a/lib/stdlib/test/shell_SUITE.erl
+++ b/lib/stdlib/test/shell_SUITE.erl
@@ -28,8 +28,8 @@
refman_bit_syntax/1,
progex_bit_syntax/1, progex_records/1,
progex_lc/1, progex_funs/1,
- otp_5990/1, otp_6166/1, otp_6554/1, otp_6785/1,
- otp_7184/1, otp_7232/1, otp_8393/1]).
+ otp_5990/1, otp_6166/1, otp_6554/1,
+ otp_7184/1, otp_7232/1, otp_8393/1, otp_10302/1]).
-export([ start_restricted_from_shell/1,
start_restricted_on_command_line/1,restricted_local/1]).
@@ -92,8 +92,8 @@ groups() ->
[progex_bit_syntax, progex_records, progex_lc,
progex_funs]},
{tickets, [],
- [otp_5990, otp_6166, otp_6554, otp_6785, otp_7184,
- otp_7232, otp_8393]}].
+ [otp_5990, otp_6166, otp_6554, otp_7184,
+ otp_7232, otp_8393, otp_10302]}].
init_per_suite(Config) ->
Config.
@@ -108,7 +108,7 @@ end_per_group(_GroupName, Config) ->
Config.
--record(state, {bin, reply, leader}).
+-record(state, {bin, reply, leader, unic = latin1}).
start_restricted_from_shell(doc) ->
@@ -374,15 +374,18 @@ records(Config) when is_list(Config) ->
MS = ?MODULE_STRING,
RR1 = "rr(" ++ MS ++ "). #state{}.",
?line "[state]\n"
- "#state{bin = undefined,reply = undefined,leader = undefined}.\n" =
+ "#state{bin = undefined,reply = undefined,leader = undefined,\n"
+ " unic = latin1}.\n" =
t(RR1),
RR2 = "rr(" ++ MS ++ ",[state]). #state{}.",
?line "[state]\n"
- "#state{bin = undefined,reply = undefined,leader = undefined}.\n" =
+ "#state{bin = undefined,reply = undefined,leader = undefined,\n"
+ " unic = latin1}.\n" =
t(RR2),
RR3 = "rr(" ++ MS ++ ",'_'). #state{}.",
?line "[state]\n"
- "#state{bin = undefined,reply = undefined,leader = undefined}.\n" =
+ "#state{bin = undefined,reply = undefined,leader = undefined,\n"
+ " unic = latin1}.\n" =
t(RR3),
RR4 = "rr(" ++ MS ++ ", '_', {d,test1}).",
?line [[state]] = scan(RR4),
@@ -817,9 +820,6 @@ otp_5916(Config) when is_list(Config) ->
true = if is_record(#r1{},r1,3) -> true; true -> false end,
false = if is_record(#r2{},r1,3) -> true; true -> false end,
- true = if {erlang,is_record}(#r1{},r1,3) -> true; true -> false end,
- false = if {erlang,is_record}(#r2{},r1,3) -> true; true -> false end,
-
ok.">>,
[ok] = scan(C),
ok.
@@ -2282,12 +2282,6 @@ otp_5990(doc) ->
otp_5990(suite) -> [];
otp_5990(Config) when is_list(Config) ->
?line [true] =
- scan(<<"rd(foo,{bar}), {erlang,is_record}(#foo{}, foo).">>),
- ?line [3] =
- scan(<<"rd(foo,{bar}), A = #foo{}, "
- "{if {erlang,is_record}(A, foo) -> erlang; "
- "true -> not_a_module end, length}([1,2,3]).">>),
- ?line [true] =
scan(<<"rd('OrdSet', {orddata = {},ordtype = type}), "
"S = #'OrdSet'{ordtype = {}}, "
"if tuple(S#'OrdSet'.ordtype) -> true; true -> false end.">>),
@@ -2549,19 +2543,6 @@ otp_6554(Config) when is_list(Config) ->
ok.
-otp_6785(doc) ->
- "OTP-6785. Parameterized modules.";
-otp_6785(suite) -> [];
-otp_6785(Config) when is_list(Config) ->
- MFile = filename:join(?config(priv_dir, Config), "parameterized.erl"),
- Contents = <<"-module(parameterized, [A]). "
- "-export([test/0]). "
- "test() -> A. ">>,
- ?line ok = compile_file(Config, MFile, Contents, []),
- ?line (parameterized:new(adsf)):test(),
- file:delete(MFile),
- ok.
-
otp_7184(doc) ->
"OTP-7184. Propagate exit signals from dying evaluator process.";
otp_7184(suite) -> [];
@@ -2757,6 +2738,143 @@ prompt_err(B) ->
S = string:strip(S2, both, $"),
string:strip(S, right, $.).
+otp_10302(doc) ->
+ "OTP-10302. Unicode.";
+otp_10302(suite) -> [];
+otp_10302(Config) when is_list(Config) ->
+ Test1 =
+ <<"begin
+ io:setopts([{encoding,utf8}]),
+ [1024] = \"\\x{400}\",
+ rd(rec, {a = \"\\x{400}\"}),
+ ok = rl(rec)
+ end.">>,
+ "-record(rec,{a = \"\x{400}\"}).\nok.\n" = t(Test1),
+
+ Test3 =
+ <<"io:setopts([{encoding,utf8}]).
+ rd(rec, {a = \"\\x{400}\"}).
+ ok = rp(#rec{}).">>,
+ "ok.\nrec\n#rec{a = \"\x{400}\"}.\nok.\n" = t(Test3),
+
+ Test4 =
+ <<"io:setopts([{encoding,utf8}]).
+ A = [1024] = \"\\x{400}\".
+ b().
+ h().">>,
+
+ "ok.\n\"\x{400}\"\nA = \"\x{400}\".\nok.\n"
+ "1: io:setopts([{encoding,utf8}])\n-> ok.\n"
+ "2: A = [1024] = \"\x{400}\"\n-> \"\x{400}\"\n"
+ "3: b()\n-> ok.\nok.\n" = t(Test4),
+
+ Test5 =
+ <<"begin
+ io:setopts([{encoding,utf8}]),
+ results(0),
+ A = [1024] = \"\\x{400}\",
+ b(),
+ h()
+ end.">>,
+ "A = \"\x{400}\".\nok.\n" = t(Test5),
+
+ %% One $" is "lost":
+ true =
+ "\x{400}\": command not found" =:=
+ prompt_err({<<"io:setopts([{encoding,utf8}]). v(\"\x{400}\")."/utf8>>,
+ unicode}),
+
+ "ok.\ndefault\n* Bad prompt function: \"\x{400}\".\n" =
+ t({<<"io:setopts([{encoding,utf8}]). "
+ "shell:prompt_func(\"\x{400}\")."/utf8>>,
+ unicode}),
+ _ = shell:prompt_func(default),
+
+ %% Test lib:format_exception() (cf. OTP-6554)
+ Test6 =
+ <<"begin
+ A = <<\"\\xaa\">>,
+ S = lists:flatten(io_lib:format(\"~p/~p.\", [A, A])),
+ {ok, Ts, _} = erl_scan:string(S, 1, [unicode]),
+ {ok, Es} = erl_parse:parse_exprs(Ts),
+ B = erl_eval:new_bindings(),
+ erl_eval:exprs(Es, B)
+ end.">>,
+
+ "** exception error: an error occurred when evaluating"
+ " an arithmetic expression\n in operator '/'/2\n"
+ " called as <<\"\xaa\">> / <<\"\xaa\">>.\n" = t(Test6),
+ Test7 =
+ <<"io:setopts([{encoding,utf8}]).
+ A = <<\"\\xaa\">>,
+ S = lists:flatten(io_lib:format(\"~p/~p.\", [A, A])),
+ {ok, Ts, _} = erl_scan:string(S, 1, [unicode]),
+ {ok, Es} = erl_parse:parse_exprs(Ts),
+ B = erl_eval:new_bindings(),
+ erl_eval:exprs(Es, B).">>,
+
+ "ok.\n** exception error: an error occurred when evaluating"
+ " an arithmetic expression\n in operator '/'/2\n"
+ " called as <<170>> / <<170>>.\n" = t(Test7),
+ Test8 =
+ <<"begin
+ A = [1089],
+ S = lists:flatten(io_lib:format(\"~tp/~tp.\", [A, A])),
+ {ok, Ts, _} = erl_scan:string(S, 1, [unicode]),
+ {ok, Es} = erl_parse:parse_exprs(Ts),
+ B = erl_eval:new_bindings(),
+ erl_eval:exprs(Es, B)
+ end.">>,
+ "** exception error: an error occurred when evaluating"
+ " an arithmetic expression\n in operator '/'/2\n"
+ " called as [1089] / [1089].\n" = t(Test8),
+ Test9 =
+ <<"io:setopts([{encoding,utf8}]).
+ A = [1089],
+ S = lists:flatten(io_lib:format(\"~tp/~tp.\", [A, A])),
+ {ok, Ts, _} = erl_scan:string(S, 1, [unicode]),
+ {ok, Es} = erl_parse:parse_exprs(Ts),
+ B = erl_eval:new_bindings(),
+ erl_eval:exprs(Es, B).">>,
+
+ "ok.\n** exception error: an error occurred when evaluating"
+ " an arithmetic expression\n in operator '/'/2\n"
+ " called as \"\x{441}\" / \"\x{441}\".\n" = t(Test9),
+ Test10 =
+ <<"A = {\"1\\xaa\",
+ $\\xaa,
+ << <<\"hi\">>/binary >>,
+ <<\"1\xaa\">>},
+ fun(a) -> true end(A).">>,
+ "** exception error: no function clause matching \n"
+ " erl_eval:'-inside-an-interpreted-fun-'"
+ "({\"1\xc2\xaa\",170,<<\"hi\">>,\n "
+ " <<\"1\xc2\xaa\">>}) .\n" = t(Test10),
+ Test11 =
+ <<"io:setopts([{encoding,utf8}]).
+ A = {\"1\\xaa\",
+ $\\xaa,
+ << <<\"hi\">>/binary >>,
+ <<\"1\xaa\">>},
+ fun(a) -> true end(A).">>,
+
+ "ok.\n** exception error: no function clause matching \n"
+ " erl_eval:'-inside-an-interpreted-fun-'"
+ "({\"1\xaa\",170,<<\"hi\">>,\n "
+ " <<\"1\xaa\"/utf8>>}) .\n" = t(Test11),
+ Test12 = <<"fun(a, b) -> false end(65, [1089]).">>,
+ "** exception error: no function clause matching \n"
+ " erl_eval:'-inside-an-interpreted-fun-'(65,[1089])"
+ " .\n" = t(Test12),
+ Test13 =
+ <<"io:setopts([{encoding,utf8}]).
+ fun(a, b) -> false end(65, [1089]).">>,
+ "ok.\n** exception error: no function clause matching \n"
+ " erl_eval:'-inside-an-interpreted-fun-'(65,\"\x{441}\")"
+ " .\n" = t(Test13),
+
+ ok.
+
scan(B) ->
F = fun(Ts) ->
case erl_parse:parse_term(Ts) of
@@ -2770,7 +2888,7 @@ scan(B) ->
scan(t(B), F).
scan(S0, F) ->
- case erl_scan:tokens([], S0, 1) of
+ case erl_scan:tokens([], S0, 1, [unicode]) of
{done,{ok,Ts,_},S} ->
[F(Ts) | scan(S, F)];
_Else ->
@@ -2778,29 +2896,36 @@ scan(S0, F) ->
end.
t({Node,Bin}) when is_atom(Node),is_binary(Bin) ->
- t0(Bin, fun() -> start_new_shell(Node) end);
+ t0({Bin,latin1}, fun() -> start_new_shell(Node) end);
t(Bin) when is_binary(Bin) ->
- t0(Bin, fun() -> start_new_shell() end);
+ t0({Bin,latin1}, fun() -> start_new_shell() end);
+t({Bin,Enc}) when is_binary(Bin), is_atom(Enc) ->
+ t0({Bin,Enc}, fun() -> start_new_shell() end);
t(L) ->
t(list_to_binary(L)).
-t0(Bin, F) ->
+t0({Bin,Enc}, F) ->
%% Spawn a process so that io_request messages do not interfer.
P = self(),
- C = spawn(fun() -> t1(P, Bin, F) end),
+ C = spawn(fun() -> t1(P, {Bin, Enc}, F) end),
receive {C, R} -> R end.
-t1(Parent, Bin, F) ->
- %% io:format("*** Testing ~s~n", [binary_to_list(Bin)]),
- S = #state{bin = Bin, reply = [], leader = group_leader()},
+t1(Parent, {Bin,Enc}, F) ->
+ io:format("*** Testing ~s~n", [binary_to_list(Bin)]),
+ S = #state{bin = Bin, unic = Enc, reply = [], leader = group_leader()},
group_leader(self(), self()),
_Shell = F(),
try
server_loop(S)
catch exit:R -> Parent ! {self(), R};
- throw:{?MODULE,LoopReply} ->
+ throw:{?MODULE,LoopReply,latin1} ->
L0 = binary_to_list(list_to_binary(LoopReply)),
[$\n | L1] = lists:dropwhile(fun(X) -> X =/= $\n end, L0),
+ Parent ! {self(), dotify(L1)};
+ throw:{?MODULE,LoopReply,_Uni} ->
+ Tmp = unicode:characters_to_binary(LoopReply),
+ L0 = unicode:characters_to_list(Tmp),
+ [$\n | L1] = lists:dropwhile(fun(X) -> X =/= $\n end, L0),
Parent ! {self(), dotify(L1)}
after group_leader(S#state.leader, self())
end.
@@ -2844,7 +2969,7 @@ do_io_request(Req, From, S, ReplyAs) ->
case io_requests([Req], [], S) of
{_Status,{eof,_},S1} ->
io_reply(From, ReplyAs, {error,terminated}),
- throw({?MODULE,S1#state.reply});
+ throw({?MODULE,S1#state.reply,S1#state.unic});
{_Status,Reply,S1} ->
io_reply(From, ReplyAs, Reply),
S1
@@ -2867,13 +2992,34 @@ io_requests([], [Rs|Cont], S) ->
io_requests([], [], S) ->
{ok,ok,S}.
+io_request({setopts, Opts}, S) ->
+ #state{unic = OldEnc, bin = Bin} = S,
+ NewEnc = case proplists:get_value(encoding, Opts) of
+ undefined -> OldEnc;
+ utf8 -> unicode;
+ New -> New
+ end,
+ NewBin = case {OldEnc, NewEnc} of
+ {E, E} -> Bin;
+ {latin1, _} ->
+ unicode:characters_to_binary(Bin, latin1, unicode);
+ {_, latin1} ->
+ unicode:characters_to_binary(Bin, unicode, latin1);
+ {_, _} -> Bin
+ end,
+ {ok, ok, S#state{unic = NewEnc, bin = NewBin}};
+io_request(getopts, S) ->
+ {ok,[{encoding,S#state.unic}],S};
io_request({get_geometry,columns}, S) ->
{ok,80,S};
io_request({get_geometry,rows}, S) ->
{ok,24,S};
io_request({put_chars,Chars}, S) ->
{ok,ok,S#state{reply = [S#state.reply | Chars]}};
-io_request({put_chars,_,Chars}, S) ->
+io_request({put_chars,latin1,Chars}, S) ->
+ {ok,ok,S#state{reply = [S#state.reply | Chars]}};
+io_request({put_chars,unicode,Chars0}, S) ->
+ Chars = unicode:characters_to_list(Chars0),
{ok,ok,S#state{reply = [S#state.reply | Chars]}};
io_request({put_chars,Mod,Func,Args}, S) ->
case catch apply(Mod, Func, Args) of
@@ -2899,9 +3045,12 @@ get_until_loop(M, F, As, S, {more,Cont}, Enc) ->
0 ->
get_until_loop(M, F, As, S,
catch apply(M, F, [Cont,eof|As]), Enc);
+ _ when S#state.unic =:= latin1 ->
+ get_until_loop(M, F, As, S#state{bin = <<>>},
+ catch apply(M, F, [Cont,binary_to_list(Bin)|As]), Enc);
_ ->
get_until_loop(M, F, As, S#state{bin = <<>>},
- catch apply(M, F, [Cont,binary_to_list(Bin)|As]), Enc)
+ catch apply(M, F, [Cont,unicode:characters_to_list(Bin)|As]), Enc)
end;
get_until_loop(_M, _F, _As, S, {done,Res,Buf}, Enc) ->
{ok,Res,S#state{bin = buf2bin(Buf, Enc)}};
@@ -2912,6 +3061,8 @@ buf2bin(eof,_) ->
<<>>;
buf2bin(Buf,latin1) ->
list_to_binary(Buf);
+buf2bin(Buf,utf8) ->
+ unicode:characters_to_binary(Buf,unicode,unicode);
buf2bin(Buf,unicode) ->
unicode:characters_to_binary(Buf,unicode,unicode).
diff --git a/lib/stdlib/test/stdlib.cover b/lib/stdlib/test/stdlib.cover
index 61f4f064b9..e71be880cb 100644
--- a/lib/stdlib/test/stdlib.cover
+++ b/lib/stdlib/test/stdlib.cover
@@ -1,17 +1,2 @@
%% -*- erlang -*-
{incl_app,stdlib,details}.
-
-{excl_mods,stdlib,
- [erl_parse,
- erl_eval,
- ets,
- filename,
- gen_event,
- gen_server,
- gen,
- lists,
- io,
- io_lib,
- io_lib_format,
- io_lib_pretty,
- proc_lib]}.
diff --git a/lib/stdlib/test/stdlib.spec.vxworks b/lib/stdlib/test/stdlib.spec.vxworks
deleted file mode 100644
index ddc804b831..0000000000
--- a/lib/stdlib/test/stdlib.spec.vxworks
+++ /dev/null
@@ -1,8 +0,0 @@
-{topcase, {dir, "../stdlib_test"}}.
-{skip,{dets_SUITE,"Not runnable VxWorks/NFS"}}.
-{skip,{slave_SUITE,"VxWorks: slave nodes are not supported"}}.
-{skip,{tar_SUITE,errors,"VxWorks filesystem too primitive"}}.
-{skip,{tar_SUITE,create_long_names,"VxWorks names too short"}}.
-{skip,{epp_SUITE,"Test not adopted to VxWorks"}}.
-{skip,{select_SUITE,"Test too memory consuming for VxWorks"}}.
-{skip,{beam_lib_SUITE,error,"All sections not present in stripped beam files"}}.
diff --git a/lib/stdlib/test/string_SUITE.erl b/lib/stdlib/test/string_SUITE.erl
index 6969c095a0..96e653985f 100644
--- a/lib/stdlib/test/string_SUITE.erl
+++ b/lib/stdlib/test/string_SUITE.erl
@@ -1,7 +1,8 @@
+%% -*- coding: utf-8 -*-
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -473,8 +474,8 @@ to_upper_to_lower(suite) ->
to_upper_to_lower(doc) ->
[];
to_upper_to_lower(Config) when is_list(Config) ->
- ?line "1234ABCDEF���=" = string:to_upper("1234abcdef���="),
- ?line "����������abc()" = string:to_lower("����������abc()"),
+ ?line "1234ABCDEFÅÄÖ=" = string:to_upper("1234abcdefåäö="),
+ ?line "éèíúùòóåäöabc()" = string:to_lower("ÉÈÍÚÙÒÓÅÄÖabc()"),
?line All = lists:seq(0, 255),
?line UC = string:to_upper(All),
diff --git a/lib/stdlib/test/supervisor_SUITE.erl b/lib/stdlib/test/supervisor_SUITE.erl
index 767ae3d62c..569c66959e 100644
--- a/lib/stdlib/test/supervisor_SUITE.erl
+++ b/lib/stdlib/test/supervisor_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -46,6 +46,7 @@
temporary_normal/1,
permanent_shutdown/1, transient_shutdown/1,
temporary_shutdown/1,
+ faulty_application_shutdown/1,
permanent_abnormal/1, transient_abnormal/1,
temporary_abnormal/1, temporary_bystander/1]).
@@ -98,7 +99,8 @@ groups() ->
{normal_termination, [],
[permanent_normal, transient_normal, temporary_normal]},
{shutdown_termination, [],
- [permanent_shutdown, transient_shutdown, temporary_shutdown]},
+ [permanent_shutdown, transient_shutdown, temporary_shutdown,
+ faulty_application_shutdown]},
{abnormal_termination, [],
[permanent_abnormal, transient_abnormal,
temporary_abnormal]},
@@ -659,6 +661,39 @@ temporary_shutdown(Config) when is_list(Config) ->
[0,0,0,0] = get_child_counts(sup_test).
%%-------------------------------------------------------------------------
+%% Faulty application should shutdown and pass on errors
+faulty_application_shutdown(Config) when is_list(Config) ->
+
+ %% Set some paths
+ AppDir = filename:join(?config(data_dir, Config), "app_faulty"),
+ EbinDir = filename:join(AppDir, "ebin"),
+
+ %% Start faulty app
+ code:add_patha(EbinDir),
+
+ %% {error,
+ %% {{shutdown,
+ %% {failed_to_start_child,
+ %% app_faulty,
+ %% {undef,
+ %% [{an_undefined_module_with,an_undefined_function,[argument1,argument2],
+ %% []},
+ %% {app_faulty_server,init,1,
+ %% [{file,"app_faulty/src/app_faulty_server.erl"},{line,16}]},
+ %% {gen_server,init_it,6,
+ %% [{file,"gen_server.erl"},{line,304}]},
+ %% {proc_lib,init_p_do_apply,3,
+ %% [{file,"proc_lib.erl"},{line,227}]}]}}},
+ %% {app_faulty,start,[normal,[]]}}}
+
+ {error, Error} = application:start(app_faulty),
+ {{shutdown, {failed_to_start_child,app_faulty,{undef, CallStack}}},
+ {app_faulty,start,_}} = Error,
+ [{an_undefined_module_with,an_undefined_function,_,_}|_] = CallStack,
+ ok = application:unload(app_faulty),
+ ok.
+
+%%-------------------------------------------------------------------------
%% A permanent child should always be restarted.
permanent_abnormal(Config) when is_list(Config) ->
{ok, SupPid} = start_link({ok, {{one_for_one, 2, 3600}, []}}),
diff --git a/lib/stdlib/test/supervisor_SUITE_data/Makefile.src b/lib/stdlib/test/supervisor_SUITE_data/Makefile.src
new file mode 100644
index 0000000000..dbc5729f47
--- /dev/null
+++ b/lib/stdlib/test/supervisor_SUITE_data/Makefile.src
@@ -0,0 +1,15 @@
+EFLAGS=+debug_info
+
+APP_FAULTY= \
+ app_faulty/ebin/app_faulty_sup.@EMULATOR@ \
+ app_faulty/ebin/app_faulty_server.@EMULATOR@ \
+ app_faulty/ebin/app_faulty.@EMULATOR@ \
+
+all: $(APP_FAULTY)
+
+app_faulty/ebin/app_faulty_server.@EMULATOR@: app_faulty/src/app_faulty_server.erl
+ erlc $(EFLAGS) -oapp_faulty/ebin app_faulty/src/app_faulty_server.erl
+app_faulty/ebin/app_faulty_sup.@EMULATOR@: app_faulty/src/app_faulty_sup.erl
+ erlc $(EFLAGS) -oapp_faulty/ebin app_faulty/src/app_faulty_sup.erl
+app_faulty/ebin/app_faulty.@EMULATOR@: app_faulty/src/app_faulty.erl
+ erlc $(EFLAGS) -oapp_faulty/ebin app_faulty/src/app_faulty.erl
diff --git a/lib/stdlib/test/supervisor_SUITE_data/app_faulty/ebin/app_faulty.app b/lib/stdlib/test/supervisor_SUITE_data/app_faulty/ebin/app_faulty.app
new file mode 100644
index 0000000000..d4ab07e485
--- /dev/null
+++ b/lib/stdlib/test/supervisor_SUITE_data/app_faulty/ebin/app_faulty.app
@@ -0,0 +1,10 @@
+{application, app_faulty,
+ [{description, "very simple example faulty application"},
+ {id, "app_faulty"},
+ {vsn, "1.0"},
+ {modules, [app_faulty, app_faulty_sup, app_faulty_server]},
+ {registered, [app_faulty]},
+ {applications, [kernel, stdlib]},
+ {env, [{var,val1}]},
+ {mod, {app_faulty, []}}
+ ]}.
diff --git a/lib/stdlib/test/supervisor_SUITE_data/app_faulty/src/app_faulty.erl b/lib/stdlib/test/supervisor_SUITE_data/app_faulty/src/app_faulty.erl
new file mode 100644
index 0000000000..c65b411cd6
--- /dev/null
+++ b/lib/stdlib/test/supervisor_SUITE_data/app_faulty/src/app_faulty.erl
@@ -0,0 +1,17 @@
+-module(app_faulty).
+
+-behaviour(application).
+
+%% Application callbacks
+-export([start/2, stop/1]).
+
+start(_Type, _StartArgs) ->
+ case app_faulty_sup:start_link() of
+ {ok, Pid} ->
+ {ok, Pid};
+ Error ->
+ Error
+ end.
+
+stop(_State) ->
+ ok.
diff --git a/lib/stdlib/test/supervisor_SUITE_data/app_faulty/src/app_faulty_server.erl b/lib/stdlib/test/supervisor_SUITE_data/app_faulty/src/app_faulty_server.erl
new file mode 100644
index 0000000000..6628f92210
--- /dev/null
+++ b/lib/stdlib/test/supervisor_SUITE_data/app_faulty/src/app_faulty_server.erl
@@ -0,0 +1,32 @@
+-module(app_faulty_server).
+
+-behaviour(gen_server).
+
+%% API
+-export([start_link/0]).
+
+%% gen_server callbacks
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+ terminate/2, code_change/3]).
+
+start_link() ->
+ gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
+
+init([]) ->
+ an_undefined_module_with:an_undefined_function(argument1, argument2),
+ {ok, []}.
+
+handle_call(_Request, _From, State) ->
+ {reply, ok, State}.
+
+handle_cast(_Msg, State) ->
+ {noreply, State}.
+
+handle_info(_Info, State) ->
+ {noreply, State}.
+
+terminate(_Reason, _State) ->
+ ok.
+
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
diff --git a/lib/stdlib/test/supervisor_SUITE_data/app_faulty/src/app_faulty_sup.erl b/lib/stdlib/test/supervisor_SUITE_data/app_faulty/src/app_faulty_sup.erl
new file mode 100644
index 0000000000..8115a88809
--- /dev/null
+++ b/lib/stdlib/test/supervisor_SUITE_data/app_faulty/src/app_faulty_sup.erl
@@ -0,0 +1,17 @@
+-module(app_faulty_sup).
+
+-behaviour(supervisor).
+
+%% API
+-export([start_link/0]).
+
+%% Supervisor callbacks
+-export([init/1]).
+
+start_link() ->
+ supervisor:start_link(?MODULE, []).
+
+init([]) ->
+ AChild = {app_faulty,{app_faulty_server,start_link,[]},
+ permanent,2000,worker,[app_faulty_server]},
+ {ok,{{one_for_all,0,1}, [AChild]}}.
diff --git a/lib/stdlib/test/sys_SUITE.erl b/lib/stdlib/test/sys_SUITE.erl
index fe039e8bcc..b2e1d12b2a 100644
--- a/lib/stdlib/test/sys_SUITE.erl
+++ b/lib/stdlib/test/sys_SUITE.erl
@@ -56,70 +56,60 @@ end_per_group(_GroupName, Config) ->
log(suite) -> [];
log(Config) when is_list(Config) ->
- ?line {ok,_Server} = start(),
- ?line ok = sys:log(?server,true),
- ?line {ok,-44} = public_call(44),
- ?line ok = sys:log(?server,false),
- ?line ok = sys:log(?server,print),
- ?line stop(),
+ {ok,_Server} = start(),
+ ok = sys:log(?server,true),
+ {ok,-44} = public_call(44),
+ ok = sys:log(?server,false),
+ ok = sys:log(?server,print),
+ stop(),
ok.
log_to_file(suite) -> [];
log_to_file(Config) when is_list(Config) ->
TempName = test_server:temp_name(?config(priv_dir,Config) ++ "sys."),
- ?line {ok,_Server} = start(),
- ?line ok = sys:log_to_file(?server,TempName),
- ?line {ok,-44} = public_call(44),
- ?line ok = sys:log_to_file(?server,false),
- ?line {ok,Fd} = file:open(TempName,[read]),
- ?line Msg1 = io:get_line(Fd,''),
- ?line Msg2 = io:get_line(Fd,''),
- ?line file:close(Fd),
- ?line lists:prefix("*DBG* sys_SUITE_server got call {req,44} from ",Msg1),
- ?line lists:prefix("*DBG* sys_SUITE_server sent {ok,-44} to ",Msg2),
- ?line stop(),
+ {ok,_Server} = start(),
+ ok = sys:log_to_file(?server,TempName),
+ {ok,-44} = public_call(44),
+ ok = sys:log_to_file(?server,false),
+ {ok,Fd} = file:open(TempName,[read]),
+ Msg1 = io:get_line(Fd,''),
+ Msg2 = io:get_line(Fd,''),
+ file:close(Fd),
+ lists:prefix("*DBG* sys_SUITE_server got call {req,44} from ",Msg1),
+ lists:prefix("*DBG* sys_SUITE_server sent {ok,-44} to ",Msg2),
+ stop(),
ok.
stats(suite) -> [];
stats(Config) when is_list(Config) ->
- ?line Self = self(),
- ?line {ok,_Server} = start(),
- ?line ok = sys:statistics(?server,true),
- ?line {ok,-44} = public_call(44),
- ?line {ok,Stats} = sys:statistics(?server,get),
- ?line lists:member({messages_in,1},Stats),
- ?line lists:member({messages_out,1},Stats),
- ?line ok = sys:statistics(?server,false),
- ?line {status,_Pid,{module,_Mod},[_PDict,running,Self,_,_]} =
+ Self = self(),
+ {ok,_Server} = start(),
+ ok = sys:statistics(?server,true),
+ {ok,-44} = public_call(44),
+ {ok,Stats} = sys:statistics(?server,get),
+ lists:member({messages_in,1},Stats),
+ lists:member({messages_out,1},Stats),
+ ok = sys:statistics(?server,false),
+ {status,_Pid,{module,_Mod},[_PDict,running,Self,_,_]} =
sys:get_status(?server),
- ?line {ok,no_statistics} = sys:statistics(?server,get),
- ?line stop(),
+ {ok,no_statistics} = sys:statistics(?server,get),
+ stop(),
ok.
trace(suite) -> [];
trace(Config) when is_list(Config) ->
- ?line {ok,_Server} = start(),
- case os:type() of
- vxworks ->
- ?line test_server:sleep(20000);
- _ ->
- ?line test_server:sleep(2000)
- end,
- ?line test_server:capture_start(),
- ?line sys:trace(?server,true),
- ?line {ok,-44} = public_call(44),
+ {ok,_Server} = start(),
+ test_server:sleep(2000),
+ test_server:capture_start(),
+ sys:trace(?server,true),
+ {ok,-44} = public_call(44),
%% ho, hum, allow for the io to reach us..
- case os:type() of
- vxworks ->
- ?line test_server:sleep(10000);
- _ ->
- ?line test_server:sleep(1000)
- end,
- ?line test_server:capture_stop(),
- ?line [Msg1,Msg2] = test_server:capture_get(),
- ?line lists:prefix("*DBG* sys_SUITE_server got call {req,44} from ",Msg1),
- ?line lists:prefix("*DBG* sys_SUITE_server sent {ok,-44} to ",Msg2),
- ?line stop(),
+ test_server:sleep(1000),
+ test_server:capture_stop(),
+ [Msg1,Msg2] = test_server:capture_get(),
+ lists:prefix("*DBG* sys_SUITE_server got call {req,44} from ",Msg1),
+ lists:prefix("*DBG* sys_SUITE_server sent {ok,-44} to ",Msg2),
+ stop(),
ok.
suspend(suite) -> [];
diff --git a/lib/stdlib/test/timer_SUITE.erl b/lib/stdlib/test/timer_SUITE.erl
index f84c72b0f8..1110891ab8 100644
--- a/lib/stdlib/test/timer_SUITE.erl
+++ b/lib/stdlib/test/timer_SUITE.erl
@@ -32,7 +32,6 @@
%% functions I guess. But I don't have time for that now.
%%
%% Expect it to run for at least 5-10 minutes!
-%% Except for VxWorks of course, where a couple of hours is more apropriate...
%% The main test case in this module is "do_big_test", which
@@ -77,12 +76,7 @@ end_per_group(_GroupName, Config) ->
do_big_test(TConfig) when is_list(TConfig) ->
Dog = ?t:timetrap(?t:minutes(20)),
Save = process_flag(trap_exit, true),
- Result = case os:type() of
- vxworks ->
- big_test(10);
- _ ->
- big_test(200)
- end,
+ Result = big_test(200),
process_flag(trap_exit, Save),
?t:timetrap_cancel(Dog),
report_result(Result).
diff --git a/lib/syntax_tools/doc/overview.edoc b/lib/syntax_tools/doc/overview.edoc
index 23eadce8fe..df02ad0b3a 100644
--- a/lib/syntax_tools/doc/overview.edoc
+++ b/lib/syntax_tools/doc/overview.edoc
@@ -1,5 +1,9 @@
+ -*- html -*-
-@author Richard Carlsson <[email protected]>
+ Syntax Tools overview page
+
+
+@author Richard Carlsson <[email protected]>
@copyright 1997-2004 Richard Carlsson
@version {@version}
@title Erlang Syntax Tools
diff --git a/lib/syntax_tools/doc/src/Makefile b/lib/syntax_tools/doc/src/Makefile
index d1c6989bd9..2502bf877a 100644
--- a/lib/syntax_tools/doc/src/Makefile
+++ b/lib/syntax_tools/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2006-2010. All Rights Reserved.
+# Copyright Ericsson AB 2006-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/syntax_tools/doc/src/notes.xml b/lib/syntax_tools/doc/src/notes.xml
index 434a3e721e..fdfa414ad2 100644
--- a/lib/syntax_tools/doc/src/notes.xml
+++ b/lib/syntax_tools/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2007</year><year>2011</year>
+ <year>2007</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -31,6 +31,24 @@
<p>This document describes the changes made to the Syntax_Tools
application.</p>
+<section><title>Syntax_Tools 1.6.9</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Syntax Tools 1.6.9</p>
+ <p>
+ Minor bugfixes, spec annotations and documentation
+ cleanup. Thanks to Richard Carlsson</p>
+ <p>
+ Own Id: OTP-10208</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Syntax_Tools 1.6.8</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/syntax_tools/src/Makefile b/lib/syntax_tools/src/Makefile
index dca5e78be9..2aa6591c77 100644
--- a/lib/syntax_tools/src/Makefile
+++ b/lib/syntax_tools/src/Makefile
@@ -62,17 +62,17 @@ distclean: clean
realclean: clean
$(EBIN)/%.$(EMULATOR):%.erl
- erlc -W $(ERL_COMPILE_FLAGS) -o$(EBIN) $<
+ $(erlc_verbose)erlc -W $(ERL_COMPILE_FLAGS) -o$(EBIN) $<
# ----------------------------------------------------
# Special Build Targets
# ----------------------------------------------------
$(APP_TARGET): $(APP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
# ----------------------------------------------------
# Release Target
diff --git a/lib/syntax_tools/src/epp_dodger.erl b/lib/syntax_tools/src/epp_dodger.erl
index 9f6f7d815e..70395848a1 100644
--- a/lib/syntax_tools/src/epp_dodger.erl
+++ b/lib/syntax_tools/src/epp_dodger.erl
@@ -14,10 +14,8 @@
%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
%% USA
%%
-%% $Id$
-%%
%% @copyright 2001-2006 Richard Carlsson
-%% @author Richard Carlsson <[email protected]>
+%% @author Richard Carlsson <[email protected]>
%% @end
%% =====================================================================
@@ -188,6 +186,7 @@ quick_parse_file(File, Options) ->
parse_file(File, Parser, Options) ->
case file:open(File, [read]) of
{ok, Dev} ->
+ _ = epp:set_encoding(Dev),
try Parser(Dev, 1, Options)
after ok = file:close(Dev)
end;
@@ -402,7 +401,7 @@ quick_parse_form(Dev, L0, Options) ->
parse_form(Dev, L0, Parser, Options) ->
NoFail = proplists:get_bool(no_fail, Options),
Opt = #opt{clever = proplists:get_bool(clever, Options)},
- case io:scan_erl_form(Dev, "", L0) of
+ case io:scan_erl_form(Dev, "", L0, [unicode]) of
{ok, Ts, L1} ->
case catch {ok, Parser(Ts, Opt)} of
{'EXIT', Term} ->
@@ -421,6 +420,7 @@ parse_form(Dev, L0, Parser, Options) ->
{ok, F, L1}
end;
{error, _IoErr, _L1} = Err -> Err;
+ {error, _Reason} -> {eof, L0}; % This is probably encoding problem
{eof, _L1} = Eof -> Eof
end.
diff --git a/lib/syntax_tools/src/erl_comment_scan.erl b/lib/syntax_tools/src/erl_comment_scan.erl
index 108ab3bffd..a70e7ba413 100644
--- a/lib/syntax_tools/src/erl_comment_scan.erl
+++ b/lib/syntax_tools/src/erl_comment_scan.erl
@@ -16,7 +16,7 @@
%%
%% =====================================================================
%% @copyright 1997-2006 Richard Carlsson
-%% @author Richard Carlsson <[email protected]>
+%% @author Richard Carlsson <[email protected]>
%% @end
%% =====================================================================
@@ -72,7 +72,17 @@ file(Name) ->
{ok, V} ->
case V of
{ok, B} ->
- string(binary_to_list(B));
+ Enc = case epp:read_encoding(Name) of
+ none -> epp:default_encoding();
+ Enc0 -> Enc0
+ end,
+ case catch unicode:characters_to_list(B, Enc) of
+ String when is_list(String) ->
+ string(String);
+ R ->
+ error_read_file(Name1),
+ exit(R)
+ end;
{error, E} ->
error_read_file(Name1),
exit({read, E})
diff --git a/lib/syntax_tools/src/erl_prettypr.erl b/lib/syntax_tools/src/erl_prettypr.erl
index 7caf0b3db6..1ffcf31134 100644
--- a/lib/syntax_tools/src/erl_prettypr.erl
+++ b/lib/syntax_tools/src/erl_prettypr.erl
@@ -14,10 +14,8 @@
%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
%% USA
%%
-%% $Id$
-%%
%% @copyright 1997-2006 Richard Carlsson
-%% @author Richard Carlsson <[email protected]>
+%% @author Richard Carlsson <[email protected]>
%% @end
%% =====================================================================
@@ -62,7 +60,9 @@
hook = ?NOHOOK :: hook(),
paper = ?PAPER :: integer(),
ribbon = ?RIBBON :: integer(),
- user = ?NOUSER :: term()}).
+ user = ?NOUSER :: term(),
+ encoding = epp:default_encoding() :: epp:source_encoding()}).
+
-type context() :: #ctxt{}.
%% =====================================================================
@@ -233,6 +233,8 @@ format(Node) ->
%% <dt>{user, term()}</dt>
%% <dd>User-specific data for use in hook functions. The default
%% value is `undefined'.</dd>
+%% <dt>{encoding, epp:source_encoding()}</dt>
+%% <dd>Specifies the encoding of the generated file.</dd>
%% </dl>
%%
%% A hook function (cf. the {@link hook()} type) is passed the current
@@ -344,7 +346,9 @@ layout(Node, Options) ->
#ctxt{hook = proplists:get_value(hook, Options, ?NOHOOK),
paper = proplists:get_value(paper, Options, ?PAPER),
ribbon = proplists:get_value(ribbon, Options, ?RIBBON),
- user = proplists:get_value(user, Options)}).
+ user = proplists:get_value(user, Options),
+ encoding = proplists:get_value(encoding, Options,
+ epp:default_encoding())}).
lay(Node, Ctxt) ->
case erl_syntax:get_ann(Node) of
@@ -447,10 +451,10 @@ lay_2(Node, Ctxt) ->
text(tidy_float(erl_syntax:float_literal(Node)));
char ->
- text(erl_syntax:char_literal(Node));
+ text(erl_syntax:char_literal(Node, Ctxt#ctxt.encoding));
string ->
- lay_string(erl_syntax:string_literal(Node), Ctxt);
+ lay_string(erl_syntax:string_literal(Node, Ctxt#ctxt.encoding), Ctxt);
nil ->
text("[]");
@@ -641,10 +645,6 @@ lay_2(Node, Ctxt) ->
set_prec(Ctxt, PrecR)),
beside(D1, beside(text(":"), D2));
- qualified_name ->
- Ss = erl_syntax:qualified_name_segments(Node),
- lay_qualified_name(Ss, Ctxt);
-
%%
%% The rest is in alphabetical order
%%
@@ -813,13 +813,6 @@ lay_2(Node, Ctxt) ->
reset_prec(Ctxt)),
lay_parentheses(D, Ctxt);
- query_expr ->
- Ctxt1 = reset_prec(Ctxt),
- D = lay(erl_syntax:query_expr_body(Node), Ctxt1),
- sep([text("query"),
- nest(Ctxt1#ctxt.sub_indent, D),
- text("end")]);
-
receive_expr ->
Ctxt1 = reset_prec(Ctxt),
D1 = lay_clauses(erl_syntax:receive_expr_clauses(Node),
@@ -968,26 +961,6 @@ maybe_parentheses(D, Prec, Ctxt) ->
D
end.
-lay_qualified_name([S | Ss1] = Ss, Ctxt) ->
- case erl_syntax:type(S) of
- atom ->
- case erl_syntax:atom_value(S) of
- '' ->
- beside(text("."),
- lay_qualified_name_1(Ss1, Ctxt));
- _ ->
- lay_qualified_name_1(Ss, Ctxt)
- end;
- _ ->
- lay_qualified_name_1(Ss, Ctxt)
- end.
-
-lay_qualified_name_1([S], Ctxt) ->
- lay(S, Ctxt);
-lay_qualified_name_1([S | Ss], Ctxt) ->
- beside(lay(S, Ctxt), beside(text("."),
- lay_qualified_name_1(Ss, Ctxt))).
-
lay_string(S, Ctxt) ->
%% S includes leading/trailing double-quote characters. The segment
%% width is 2/3 of the ribbon width - this seems to work well.
diff --git a/lib/syntax_tools/src/erl_recomment.erl b/lib/syntax_tools/src/erl_recomment.erl
index fc7c515700..7b2f9f7adb 100644
--- a/lib/syntax_tools/src/erl_recomment.erl
+++ b/lib/syntax_tools/src/erl_recomment.erl
@@ -14,10 +14,8 @@
%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
%% USA
%%
-%% $Id$
-%%
%% @copyright 1997-2006 Richard Carlsson
-%% @author Richard Carlsson <[email protected]>
+%% @author Richard Carlsson <[email protected]>
%% @end
%% =====================================================================
diff --git a/lib/syntax_tools/src/erl_syntax.erl b/lib/syntax_tools/src/erl_syntax.erl
index 32fd3722d6..41a1708925 100644
--- a/lib/syntax_tools/src/erl_syntax.erl
+++ b/lib/syntax_tools/src/erl_syntax.erl
@@ -14,10 +14,8 @@
%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
%% USA
%%
-%% $Id$
-%%
%% @copyright 1997-2006 Richard Carlsson
-%% @author Richard Carlsson <[email protected]>
+%% @author Richard Carlsson <[email protected]>
%% @end
%% =====================================================================
@@ -26,23 +24,20 @@
%% This module defines an abstract data type for representing Erlang
%% source code as syntax trees, in a way that is backwards compatible
%% with the data structures created by the Erlang standard library
-%% parser module <code>erl_parse</code> (often referred to as "parse
+%% parser module `erl_parse' (often referred to as "parse
%% trees", which is a bit of a misnomer). This means that all
-%% <code>erl_parse</code> trees are valid abstract syntax trees, but the
+%% `erl_parse' trees are valid abstract syntax trees, but the
%% reverse is not true: abstract syntax trees can in general not be used
-%% as input to functions expecting an <code>erl_parse</code> tree.
+%% as input to functions expecting an `erl_parse' tree.
%% However, as long as an abstract syntax tree represents a correct
-%% Erlang program, the function <a
-%% href="#revert-1"><code>revert/1</code></a> should be able to
-%% transform it to the corresponding <code>erl_parse</code>
+%% Erlang program, the function {@link revert/1} should be able to
+%% transform it to the corresponding `erl_parse'
%% representation.
%%
-%% A recommended starting point for the first-time user is the
-%% documentation of the <a
-%% href="#type-syntaxTree"><code>syntaxTree()</code></a> data type, and
-%% the function <a href="#type-1"><code>type/1</code></a>.
+%% A recommended starting point for the first-time user is the documentation
+%% of the {@link syntaxTree()} data type, and the function {@link type/1}.
%%
-%% <h3><b>NOTES:</b></h3>
+%% == NOTES: ==
%%
%% This module deals with the composition and decomposition of
%% <em>syntactic</em> entities (as opposed to semantic ones); its
@@ -52,36 +47,31 @@
%% in general, the user is assumed to pass type-correct arguments - if
%% this is not done, the effects are not defined.
%%
-%% With the exception of the <code>erl_parse</code> data structures,
+%% With the exception of the {@link erl_parse()} data structures,
%% 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
+%% as `none', by a list constructor `[X | Y]', or
+%% by the empty list `[]'. This can be relied on when writing
%% functions that operate on syntax trees.
-%% @type syntaxTree(). An abstract syntax tree. The
-%% <code>erl_parse</code> "parse tree" representation is a subset of the
-%% <code>syntaxTree()</code> representation.
+%% @type syntaxTree(). An abstract syntax tree. The {@link erl_parse()}
+%% "parse tree" representation is a proper subset of the `syntaxTree()'
+%% representation.
%%
%% Every abstract syntax tree node has a <em>type</em>, given by the
-%% function <a href="#type-1"><code>type/1</code></a>. Each node also
-%% has associated <em>attributes</em>; see <a
-%% href="#get_attrs-1"><code>get_attrs/1</code></a> for details. The
-%% functions <a href="#make_tree-2"><code>make_tree/2</code></a> and <a
-%% href="#subtrees-1"><code>subtrees/1</code></a> are generic
+%% function {@link type/1}. Each node also has associated
+%% <em>attributes</em>; see {@link get_attrs/1} for details. The functions
+%% {@link make_tree/2} and {@link subtrees/1} are generic
%% constructor/decomposition functions for abstract syntax trees. The
-%% functions <a href="#abstract-1"><code>abstract/1</code></a> and <a
-%% href="#concrete-1"><code>concrete/1</code></a> convert between
+%% functions {@link abstract/1} and {@link concrete/1} convert between
%% constant Erlang terms and their syntactic representations. The set of
-%% syntax tree nodes is extensible through the <a
-%% href="#tree-2"><code>tree/2</code></a> function.
+%% syntax tree nodes is extensible through the {@link tree/2} function.
%%
-%% A syntax tree can be transformed to the <code>erl_parse</code>
-%% representation with the <a href="#revert-1"><code>revert/1</code></a>
-%% function.
+%% A syntax tree can be transformed to the {@link erl_parse()}
+%% representation with the {@link revert/1} function.
-module(erl_syntax).
@@ -171,6 +161,7 @@
is_char/2,
char_value/1,
char_literal/1,
+ char_literal/2,
clause/2,
clause/3,
clause_body/1,
@@ -244,10 +235,6 @@
prefix_expr/2,
prefix_expr_argument/1,
prefix_expr_operator/1,
- qualified_name/1,
- qualified_name_segments/1,
- query_expr/1,
- query_expr_body/1,
receive_expr/1,
receive_expr/3,
receive_expr_action/1,
@@ -281,6 +268,7 @@
is_string/2,
string_value/1,
string_literal/1,
+ string_literal/2,
text/1,
text_string/1,
try_expr/2,
@@ -309,7 +297,7 @@
data/1,
is_tree/1]).
--export_type([forms/0, syntaxTree/0, syntaxTreeAttributes/0]).
+-export_type([forms/0, syntaxTree/0, syntaxTreeAttributes/0, padding/0]).
%% =====================================================================
%% IMPLEMENTATION NOTES:
@@ -390,11 +378,15 @@
-record(wrapper, {type :: atom(),
attr = #attr{} :: #attr{},
- tree :: term()}).
+ tree :: erl_parse()}).
%% =====================================================================
--type syntaxTree() :: #tree{} | #wrapper{} | tuple(). % XXX: refine
+-type syntaxTree() :: #tree{} | #wrapper{} | erl_parse().
+
+-type erl_parse() :: erl_parse:abstract_form() | erl_parse:abstract_expr().
+%% The representation built by the Erlang standard library parser
+%% `erl_parse'. This is a subset of the {@link syntaxTree()} type.
%% =====================================================================
%%
@@ -404,12 +396,11 @@
%% =====================================================================
-%% @spec type(Node::syntaxTree()) -> atom()
-%%
-%% @doc Returns the type tag of <code>Node</code>. If <code>Node</code>
+%% @doc Returns the type tag of `Node'. If `Node'
%% does not represent a syntax tree, evaluation fails with reason
-%% <code>badarg</code>. Node types currently defined by this module are:
-%% <p><center><table border="1">
+%% `badarg'. Node types currently defined by this module are:
+%%
+%% <center><table border="1">
%% <tr>
%% <td>application</td>
%% <td>arity_qualifier</td>
@@ -456,8 +447,6 @@
%% <td>parentheses</td>
%% <td>prefix_expr</td>
%% </tr><tr>
-%% <td>qualified_name</td>
-%% <td>query_expr</td>
%% <td>receive_expr</td>
%% <td>record_access</td>
%% </tr><tr>
@@ -476,12 +465,13 @@
%% <td>variable</td>
%% <td>warning_marker</td>
%% </tr>
-%% </table></center></p>
-%% <p>The user may (for special purposes) create additional nodes
-%% with other type tags, using the <code>tree/2</code> function.</p>
+%% </table></center>
%%
-%% <p>Note: The primary constructor functions for a node type should
-%% always have the same name as the node type itself.</p>
+%% The user may (for special purposes) create additional nodes
+%% with other type tags, using the {@link tree/2} function.
+%%
+%% Note: The primary constructor functions for a node type should
+%% always have the same name as the node type itself.
%%
%% @see tree/2
%% @see application/3
@@ -520,8 +510,6 @@
%% @see operator/1
%% @see parentheses/1
%% @see prefix_expr/2
-%% @see qualified_name/1
-%% @see query_expr/1
%% @see receive_expr/3
%% @see record_access/3
%% @see record_expr/2
@@ -586,15 +574,10 @@ type(Node) ->
{match, _, _, _} -> match_expr;
{op, _, _, _, _} -> infix_expr;
{op, _, _, _} -> prefix_expr;
- {'query', _, _} -> query_expr;
{record, _, _, _, _} -> record_expr;
{record, _, _, _} -> record_expr;
{record_field, _, _, _, _} -> record_access;
- {record_field, _, _, _} ->
- case is_qualified_name(Node) of
- true -> qualified_name;
- false -> record_access
- end;
+ {record_field, _, _, _} -> record_access;
{record_index, _, _, _} -> record_index_expr;
{remote, _, _, _} -> module_qualifier;
{rule, _, _, _, _} -> rule;
@@ -606,39 +589,38 @@ type(Node) ->
%% =====================================================================
-%% @spec is_leaf(Node::syntaxTree()) -> boolean()
-%%
-%% @doc Returns <code>true</code> if <code>Node</code> is a leaf node,
-%% otherwise <code>false</code>. The currently recognised leaf node
+%% @doc Returns `true' if `Node' is a leaf node,
+%% otherwise `false'. The currently recognised leaf node
%% types are:
-%% <p><center><table border="1">
+%%
+%% <center><table border="1">
%% <tr>
-%% <td><code>atom</code></td>
-%% <td><code>char</code></td>
-%% <td><code>comment</code></td>
-%% <td><code>eof_marker</code></td>
-%% <td><code>error_marker</code></td>
+%% <td>`atom'</td>
+%% <td>`char'</td>
+%% <td>`comment'</td>
+%% <td>`eof_marker'</td>
+%% <td>`error_marker'</td>
%% </tr><tr>
-%% <td><code>float</code></td>
-%% <td><code>integer</code></td>
-%% <td><code>nil</code></td>
-%% <td><code>operator</code></td>
-%% <td><code>string</code></td>
+%% <td>`float'</td>
+%% <td>`integer'</td>
+%% <td>`nil'</td>
+%% <td>`operator'</td>
+%% <td>`string'</td>
%% </tr><tr>
-%% <td><code>text</code></td>
-%% <td><code>underscore</code></td>
-%% <td><code>variable</code></td>
-%% <td><code>warning_marker</code></td>
+%% <td>`text'</td>
+%% <td>`underscore'</td>
+%% <td>`variable'</td>
+%% <td>`warning_marker'</td>
%% </tr>
-%% </table></center></p>
-%% <p>A node of type <code>tuple</code> is a leaf node if and only if
-%% its arity is zero.</p>
+%% </table></center>
%%
-%% <p>Note: not all literals are leaf nodes, and vice versa. E.g.,
+%% A node of type `tuple' is a leaf node if and only if its arity is zero.
+%%
+%% Note: not all literals are leaf nodes, and vice versa. E.g.,
%% tuples with nonzero arity and nonempty lists may be literals, but are
%% not leaf nodes. Variables, on the other hand, are leaf nodes but not
-%% literals.</p>
-%%
+%% literals.
+%%
%% @see type/1
%% @see is_literal/1
@@ -666,29 +648,29 @@ is_leaf(Node) ->
%% =====================================================================
-%% @spec is_form(Node::syntaxTree()) -> boolean()
-%%
-%% @doc Returns <code>true</code> if <code>Node</code> is a syntax tree
+%% @doc Returns `true' if `Node' is a syntax tree
%% representing a so-called "source code form", otherwise
-%% <code>false</code>. Forms are the Erlang source code units which,
+%% `false'. Forms are the Erlang source code units which,
%% placed in sequence, constitute an Erlang program. Current form types
%% are:
-%% <p><center><table border="1">
+%%
+%% <center><table border="1">
%% <tr>
-%% <td><code>attribute</code></td>
-%% <td><code>comment</code></td>
-%% <td><code>error_marker</code></td>
-%% <td><code>eof_marker</code></td>
-%% <td><code>form_list</code></td>
+%% <td>`attribute'</td>
+%% <td>`comment'</td>
+%% <td>`error_marker'</td>
+%% <td>`eof_marker'</td>
+%% <td>`form_list'</td>
%% </tr><tr>
-%% <td><code>function</code></td>
-%% <td><code>rule</code></td>
-%% <td><code>warning_marker</code></td>
-%% <td><code>text</code></td>
+%% <td>`function'</td>
+%% <td>`rule'</td>
+%% <td>`warning_marker'</td>
+%% <td>`text'</td>
%% </tr>
-%% </table></center></p>
+%% </table></center>
+%%
%% @see type/1
-%% @see attribute/2
+%% @see attribute/2
%% @see comment/2
%% @see eof_marker/0
%% @see error_marker/1
@@ -715,10 +697,8 @@ is_form(Node) ->
%% =====================================================================
-%% @spec get_pos(Node::syntaxTree()) -> term()
-%%
%% @doc Returns the position information associated with
-%% <code>Node</code>. This is usually a nonnegative integer (indicating
+%% `Node'. This is usually a nonnegative integer (indicating
%% the source code line number), but may be any term. By default, all
%% new tree nodes have their associated position information set to the
%% integer zero.
@@ -750,10 +730,7 @@ get_pos(Node) ->
%% =====================================================================
-%% @spec set_pos(Node::syntaxTree(), Pos::term()) -> syntaxTree()
-%%
-%% @doc Sets the position information of <code>Node</code> to
-%% <code>Pos</code>.
+%% @doc Sets the position information of `Node' to `Pos'.
%%
%% @see get_pos/1
%% @see copy_pos/2
@@ -774,14 +751,10 @@ set_pos(Node, Pos) ->
%% =====================================================================
-%% @spec copy_pos(Source::syntaxTree(), Target::syntaxTree()) ->
-%% syntaxTree()
+%% @doc Copies the position information from `Source' to `Target'.
%%
-%% @doc Copies the position information from <code>Source</code> to
-%% <code>Target</code>.
-%%
-%% <p>This is equivalent to <code>set_pos(Target,
-%% get_pos(Source))</code>, but potentially more efficient.</p>
+%% This is equivalent to `set_pos(Target,
+%% get_pos(Source))', but potentially more efficient.
%%
%% @see get_pos/1
%% @see set_pos/2
@@ -811,24 +784,20 @@ set_com(Node, Com) ->
%% =====================================================================
-%% @spec get_precomments(syntaxTree()) -> [syntaxTree()]
-%%
%% @doc Returns the associated pre-comments of a node. This is a
%% possibly empty list of abstract comments, in top-down textual order.
%% When the code is formatted, pre-comments are typically displayed
%% directly above the node. For example:
-%% <pre>
-%% % Pre-comment of function
-%% foo(X) -> {bar, X}.</pre>
+%% ```% Pre-comment of function
+%% foo(X) -> {bar, X}.'''
%%
-%% <p>If possible, the comment should be moved before any preceding
+%% If possible, the comment should be moved before any preceding
%% separator characters on the same line. E.g.:
-%% <pre>
-%% foo([X | Xs]) ->
-%% % Pre-comment of 'bar(X)' node
-%% [bar(X) | foo(Xs)];
-%% ...</pre>
-%% (where the comment is moved before the "<code>[</code>").</p>
+%% ```foo([X | Xs]) ->
+%% % Pre-comment of 'bar(X)' node
+%% [bar(X) | foo(Xs)];
+%% ...'''
+%% (where the comment is moved before the "`['").
%%
%% @see comment/2
%% @see set_precomments/2
@@ -846,11 +815,8 @@ get_precomments_1(#attr{com = #com{pre = Cs}}) -> Cs.
%% =====================================================================
-%% @spec set_precomments(Node::syntaxTree(),
-%% Comments::[syntaxTree()]) -> syntaxTree()
-%%
-%% @doc Sets the pre-comments of <code>Node</code> to
-%% <code>Comments</code>. <code>Comments</code> should be a possibly
+%% @doc Sets the pre-comments of `Node' to
+%% `Comments'. `Comments' should be a possibly
%% empty list of abstract comments, in top-down textual order.
%%
%% @see comment/2
@@ -880,15 +846,11 @@ set_precomments_1(#attr{com = Com} = Attr, Cs) ->
%% =====================================================================
-%% @spec add_precomments(Comments::[syntaxTree()],
-%% Node::syntaxTree()) -> syntaxTree()
+%% @doc Appends `Comments' to the pre-comments of `Node'.
%%
-%% @doc Appends <code>Comments</code> to the pre-comments of
-%% <code>Node</code>.
-%%
-%% <p>Note: This is equivalent to <code>set_precomments(Node,
-%% get_precomments(Node) ++ Comments)</code>, but potentially more
-%% efficient.</p>
+%% Note: This is equivalent to `set_precomments(Node,
+%% get_precomments(Node) ++ Comments)', but potentially more
+%% efficient.
%%
%% @see comment/2
%% @see get_precomments/1
@@ -915,24 +877,20 @@ add_precomments_1(Cs, #attr{com = Com} = Attr) ->
%% =====================================================================
-%% @spec get_postcomments(syntaxTree()) -> [syntaxTree()]
-%%
%% @doc Returns the associated post-comments of a node. This is a
%% possibly empty list of abstract comments, in top-down textual order.
%% When the code is formatted, post-comments are typically displayed to
%% the right of and/or below the node. For example:
-%% <pre>
-%% {foo, X, Y} % Post-comment of tuple</pre>
+%% ```{foo, X, Y} % Post-comment of tuple'''
%%
-%% <p>If possible, the comment should be moved past any following
+%% If possible, the comment should be moved past any following
%% separator characters on the same line, rather than placing the
%% separators on the following line. E.g.:
-%% <pre>
-%% foo([X | Xs], Y) ->
-%% foo(Xs, bar(X)); % Post-comment of 'bar(X)' node
-%% ...</pre>
-%% (where the comment is moved past the rightmost "<code>)</code>" and
-%% the "<code>;</code>").</p>
+%% ```foo([X | Xs], Y) ->
+%% foo(Xs, bar(X)); % Post-comment of 'bar(X)' node
+%% ...'''
+%% (where the comment is moved past the rightmost "`)'" and
+%% the "`;'").
%%
%% @see comment/2
%% @see set_postcomments/2
@@ -950,11 +908,8 @@ get_postcomments_1(#attr{com = #com{post = Cs}}) -> Cs.
%% =====================================================================
-%% @spec set_postcomments(Node::syntaxTree(),
-%% Comments::[syntaxTree()]) -> syntaxTree()
-%%
-%% @doc Sets the post-comments of <code>Node</code> to
-%% <code>Comments</code>. <code>Comments</code> should be a possibly
+%% @doc Sets the post-comments of `Node' to
+%% `Comments'. `Comments' should be a possibly
%% empty list of abstract comments, in top-down textual order
%%
%% @see comment/2
@@ -984,15 +939,11 @@ set_postcomments_1(#attr{com = Com} = Attr, Cs) ->
%% =====================================================================
-%% @spec add_postcomments(Comments::[syntaxTree()],
-%% Node::syntaxTree()) -> syntaxTree()
+%% @doc Appends `Comments' to the post-comments of `Node'.
%%
-%% @doc Appends <code>Comments</code> to the post-comments of
-%% <code>Node</code>.
-%%
-%% <p>Note: This is equivalent to <code>set_postcomments(Node,
-%% get_postcomments(Node) ++ Comments)</code>, but potentially more
-%% efficient.</p>
+%% Note: This is equivalent to `set_postcomments(Node,
+%% get_postcomments(Node) ++ Comments)', but potentially more
+%% efficient.
%%
%% @see comment/2
%% @see get_postcomments/1
@@ -1019,14 +970,12 @@ add_postcomments_1(Cs, #attr{com = Com} = Attr) ->
%% =====================================================================
-%% @spec has_comments(Node::syntaxTree()) -> boolean()
-%%
-%% @doc Yields <code>false</code> if the node has no associated
-%% comments, and <code>true</code> otherwise.
+%% @doc Yields `false' if the node has no associated
+%% comments, and `true' otherwise.
%%
-%% <p>Note: This is equivalent to <code>(get_precomments(Node) == [])
-%% and (get_postcomments(Node) == [])</code>, but potentially more
-%% efficient.</p>
+%% Note: This is equivalent to `(get_precomments(Node) == [])
+%% and (get_postcomments(Node) == [])', but potentially more
+%% efficient.
%%
%% @see get_precomments/1
%% @see get_postcomments/1
@@ -1050,13 +999,11 @@ has_comments(_) -> false.
%% =====================================================================
-%% @spec remove_comments(Node::syntaxTree()) -> syntaxTree()
+%% @doc Clears the associated comments of `Node'.
%%
-%% @doc Clears the associated comments of <code>Node</code>.
-%%
-%% <p>Note: This is equivalent to
-%% <code>set_precomments(set_postcomments(Node, []), [])</code>, but
-%% potentially more efficient.</p>
+%% Note: This is equivalent to
+%% `set_precomments(set_postcomments(Node, []), [])', but
+%% potentially more efficient.
%%
%% @see set_precomments/2
%% @see set_postcomments/2
@@ -1075,16 +1022,12 @@ remove_comments(Node) ->
%% =====================================================================
-%% @spec copy_comments(Source::syntaxTree(), Target::syntaxTree()) ->
-%% syntaxTree()
-%%
-%% @doc Copies the pre- and postcomments from <code>Source</code> to
-%% <code>Target</code>.
+%% @doc Copies the pre- and postcomments from `Source' to `Target'.
%%
-%% <p>Note: This is equivalent to
-%% <code>set_postcomments(set_precomments(Target,
-%% get_precomments(Source)), get_postcomments(Source))</code>, but
-%% potentially more efficient.</p>
+%% Note: This is equivalent to
+%% `set_postcomments(set_precomments(Target,
+%% get_precomments(Source)), get_postcomments(Source))', but
+%% potentially more efficient.
%%
%% @see comment/2
%% @see get_precomments/1
@@ -1099,16 +1042,13 @@ copy_comments(Source, Target) ->
%% =====================================================================
-%% @spec join_comments(Source::syntaxTree(), Target::syntaxTree()) ->
-%% syntaxTree()
-%%
-%% @doc Appends the comments of <code>Source</code> to the current
-%% comments of <code>Target</code>.
+%% @doc Appends the comments of `Source' to the current
+%% comments of `Target'.
%%
-%% <p>Note: This is equivalent to
-%% <code>add_postcomments(get_postcomments(Source),
-%% add_precomments(get_precomments(Source), Target))</code>, but
-%% potentially more efficient.</p>
+%% Note: This is equivalent to
+%% `add_postcomments(get_postcomments(Source),
+%% add_precomments(get_precomments(Source), Target))', but
+%% potentially more efficient.
%%
%% @see comment/2
%% @see get_precomments/1
@@ -1125,8 +1065,6 @@ join_comments(Source, Target) ->
%% =====================================================================
-%% @spec get_ann(syntaxTree()) -> [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.
@@ -1142,11 +1080,7 @@ get_ann(_) -> [].
%% =====================================================================
-%% @spec set_ann(Node::syntaxTree(), Annotations::[term()]) ->
-%% syntaxTree()
-%%
-%% @doc Sets the list of user annotations of <code>Node</code> to
-%% <code>Annotations</code>.
+%% @doc Sets the list of user annotations of `Node' to `Annotations'.
%%
%% @see get_ann/1
%% @see add_ann/2
@@ -1168,13 +1102,11 @@ set_ann(Node, As) ->
%% =====================================================================
-%% @spec add_ann(Annotation::term(), Node::syntaxTree()) -> syntaxTree()
+%% @doc Appends the term `Annotation' to the list of user
+%% annotations of `Node'.
%%
-%% @doc Appends the term <code>Annotation</code> to the list of user
-%% annotations of <code>Node</code>.
-%%
-%% <p>Note: this is equivalent to <code>set_ann(Node, [Annotation |
-%% get_ann(Node)])</code>, but potentially more efficient.</p>
+%% Note: this is equivalent to `set_ann(Node, [Annotation |
+%% get_ann(Node)])', but potentially more efficient.
%%
%% @see get_ann/1
%% @see set_ann/2
@@ -1195,14 +1127,10 @@ add_ann(A, Node) ->
%% =====================================================================
-%% @spec copy_ann(Source::syntaxTree(), Target::syntaxTree()) ->
-%% syntaxTree()
-%%
-%% @doc Copies the list of user annotations from <code>Source</code> to
-%% <code>Target</code>.
+%% @doc Copies the list of user annotations from `Source' to `Target'.
%%
-%% <p>Note: this is equivalent to <code>set_ann(Target,
-%% get_ann(Source))</code>, but potentially more efficient.</p>
+%% Note: this is equivalent to `set_ann(Target,
+%% get_ann(Source))', but potentially more efficient.
%%
%% @see get_ann/1
%% @see set_ann/2
@@ -1214,23 +1142,20 @@ copy_ann(Source, Target) ->
%% =====================================================================
-%% @spec get_attrs(syntaxTree()) -> syntaxTreeAttributes()
-%%
%% @doc Returns a representation of the attributes associated with a
%% syntax tree node. The attributes are all the extra information that
%% can be attached to a node. Currently, this includes position
%% information, source code comments, and user annotations. The result
%% of this function cannot be inspected directly; only attached to
-%% another node (cf. <code>set_attrs/2</code>).
+%% another node (see {@link set_attrs/2}).
%%
-%% <p>For accessing individual attributes, see <code>get_pos/1</code>,
-%% <code>get_ann/1</code>, <code>get_precomments/1</code> and
-%% <code>get_postcomments/1</code>.</p>
+%% For accessing individual attributes, see {@link get_pos/1},
+%% {@link get_ann/1}, {@link get_precomments/1} and
+%% {@link get_postcomments/1}.
%%
%% @type syntaxTreeAttributes(). This is an abstract representation of
-%% syntax tree node attributes; see the function <a
-%% href="#get_attrs-1"><code>get_attrs/1</code></a>.
-%%
+%% syntax tree node attributes; see the function {@link get_attrs/1}.
+%%
%% @see set_attrs/2
%% @see get_pos/1
%% @see get_ann/1
@@ -1247,11 +1172,7 @@ get_attrs(Node) -> #attr{pos = get_pos(Node),
%% =====================================================================
-%% @spec set_attrs(Node::syntaxTree(),
-%% Attributes::syntaxTreeAttributes()) -> syntaxTree()
-%%
-%% @doc Sets the attributes of <code>Node</code> to
-%% <code>Attributes</code>.
+%% @doc Sets the attributes of `Node' to `Attributes'.
%%
%% @see get_attrs/1
%% @see copy_attrs/2
@@ -1270,14 +1191,10 @@ set_attrs(Node, Attr) ->
%% =====================================================================
-%% @spec copy_attrs(Source::syntaxTree(), Target::syntaxTree()) ->
-%% syntaxTree()
+%% @doc Copies the attributes from `Source' to `Target'.
%%
-%% @doc Copies the attributes from <code>Source</code> to
-%% <code>Target</code>.
-%%
-%% <p>Note: this is equivalent to <code>set_attrs(Target,
-%% get_attrs(Source))</code>, but potentially more efficient.</p>
+%% Note: this is equivalent to `set_attrs(Target,
+%% get_attrs(Source))', but potentially more efficient.
%%
%% @see get_attrs/1
%% @see set_attrs/2
@@ -1289,7 +1206,6 @@ copy_attrs(S, T) ->
%% =====================================================================
-%% @spec comment(Strings) -> syntaxTree()
%% @equiv comment(none, Strings)
-spec comment([string()]) -> syntaxTree().
@@ -1299,22 +1215,19 @@ comment(Strings) ->
%% =====================================================================
-%% @spec comment(Padding, Strings::[string()]) -> syntaxTree()
-%% Padding = none | integer()
-%%
%% @doc Creates an abstract comment with the given padding and text. If
-%% <code>Strings</code> is a (possibly empty) list
+%% `Strings' is a (possibly empty) list
%% <code>["<em>Txt1</em>", ..., "<em>TxtN</em>"]</code>, the result
%% represents the source code text
%% <pre>
-%% %<em>Txt1</em>
-%% ...
-%% %<em>TxtN</em></pre>
-%% <code>Padding</code> states the number of empty character positions
+%% %<em>Txt1</em>
+%% ...
+%% %<em>TxtN</em></pre>
+%% `Padding' states the number of empty character positions
%% to the left of the comment separating it horizontally from
-%% source code on the same line (if any). If <code>Padding</code> is
-%% <code>none</code>, a default positive number is used. If
-%% <code>Padding</code> is an integer less than 1, there should be no
+%% source code on the same line (if any). If `Padding' is
+%% `none', a default positive number is used. If
+%% `Padding' is an integer less than 1, there should be no
%% separating space. Comments are in themselves regarded as source
%% program forms.
%%
@@ -1338,8 +1251,6 @@ comment(Pad, Strings) ->
%% =====================================================================
-%% @spec comment_text(syntaxTree()) -> [string()]
-%%
%% @doc Returns the lines of text of the abstract comment.
%%
%% @see comment/2
@@ -1351,11 +1262,8 @@ comment_text(Node) ->
%% =====================================================================
-%% @spec comment_padding(syntaxTree()) -> none | integer()
-%%
%% @doc Returns the amount of padding before the comment, or
-%% <code>none</code>. The latter means that a default padding may be
-%% used.
+%% `none'. The latter means that a default padding may be used.
%%
%% @see comment/2
@@ -1366,23 +1274,21 @@ comment_padding(Node) ->
%% =====================================================================
-%% @spec form_list(Forms::[syntaxTree()]) -> syntaxTree()
-%%
%% @doc Creates an abstract sequence of "source code forms". If
-%% <code>Forms</code> is <code>[F1, ..., Fn]</code>, where each
-%% <code>Fi</code> is a form (cf. <code>is_form/1</code>, the result
+%% `Forms' is `[F1, ..., Fn]', where each
+%% `Fi' is a form (see {@link is_form/1}, the result
%% represents
%% <pre>
-%% <em>F1</em>
-%% ...
-%% <em>Fn</em></pre>
-%% where the <code>Fi</code> are separated by one or more line breaks. A
-%% node of type <code>form_list</code> is itself regarded as a source
-%% code form; cf. <code>flatten_form_list/1</code>.
-%%
-%% <p>Note: this is simply a way of grouping source code forms as a
+%% <em>F1</em>
+%% ...
+%% <em>Fn</em></pre>
+%% where the `Fi' are separated by one or more line breaks. A
+%% node of type `form_list' is itself regarded as a source
+%% code form; see {@link flatten_form_list/1}.
+%%
+%% Note: this is simply a way of grouping source code forms as a
%% single syntax tree, usually in order to form an Erlang module
-%% definition.</p>
+%% definition.
%%
%% @see form_list_elements/1
%% @see is_form/1
@@ -1401,9 +1307,7 @@ form_list(Forms) ->
%% =====================================================================
-%% @spec form_list_elements(syntaxTree()) -> [syntaxTree()]
-%%
-%% @doc Returns the list of subnodes of a <code>form_list</code> node.
+%% @doc Returns the list of subnodes of a `form_list' node.
%%
%% @see form_list/1
@@ -1414,10 +1318,8 @@ form_list_elements(Node) ->
%% =====================================================================
-%% @spec flatten_form_list(Node::syntaxTree()) -> syntaxTree()
-%%
-%% @doc Flattens sublists of a <code>form_list</code> node. Returns
-%% <code>Node</code> with all subtrees of type <code>form_list</code>
+%% @doc Flattens sublists of a `form_list' node. Returns
+%% `Node' with all subtrees of type `form_list'
%% recursively expanded, yielding a single "flat" abstract form
%% sequence.
%%
@@ -1443,10 +1345,8 @@ flatten_form_list_1([], As) ->
%% =====================================================================
-%% @spec text(String::string()) -> syntaxTree()
-%%
%% @doc Creates an abstract piece of source code text. The result
-%% represents exactly the sequence of characters in <code>String</code>.
+%% represents exactly the sequence of characters in `String'.
%% This is useful in cases when one wants full control of the resulting
%% output, e.g., for the appearance of floating-point numbers or macro
%% definitions.
@@ -1463,10 +1363,7 @@ text(String) ->
%% =====================================================================
-%% @spec text_string(syntaxTree()) -> string()
-%%
-%% @doc Returns the character sequence represented by a
-%% <code>text</code> node.
+%% @doc Returns the character sequence represented by a `text' node.
%%
%% @see text/1
@@ -1477,18 +1374,15 @@ text_string(Node) ->
%% =====================================================================
-%% @spec variable(Name) -> syntaxTree()
-%% Name = atom() | string()
-%%
%% @doc Creates an abstract variable with the given name.
-%% <code>Name</code> may be any atom or string that represents a
+%% `Name' may be any atom or string that represents a
%% lexically valid variable name, but <em>not</em> a single underscore
-%% character; cf. <code>underscore/0</code>.
+%% character; see {@link underscore/0}.
%%
-%% <p>Note: no checking is done whether the character sequence
+%% Note: no checking is done whether the character sequence
%% represents a proper variable name, i.e., whether or not its first
%% character is an uppercase Erlang character, or whether it does not
-%% contain control characters, whitespace, etc.</p>
+%% contain control characters, whitespace, etc.
%%
%% @see variable_name/1
%% @see variable_literal/1
@@ -1517,9 +1411,7 @@ revert_variable(Node) ->
%% =====================================================================
-%% @spec variable_name(syntaxTree()) -> atom()
-%%
-%% @doc Returns the name of a <code>variable</code> node as an atom.
+%% @doc Returns the name of a `variable' node as an atom.
%%
%% @see variable/1
@@ -1535,9 +1427,7 @@ variable_name(Node) ->
%% =====================================================================
-%% @spec variable_literal(syntaxTree()) -> string()
-%%
-%% @doc Returns the name of a <code>variable</code> node as a string.
+%% @doc Returns the name of a `variable' node as a string.
%%
%% @see variable/1
@@ -1553,9 +1443,7 @@ variable_literal(Node) ->
%% =====================================================================
-%% @spec underscore() -> syntaxTree()
-%%
-%% @doc Creates an abstract universal pattern ("<code>_</code>"). The
+%% @doc Creates an abstract universal pattern ("`_'"). The
%% lexical representation is a single underscore character. Note that
%% this is <em>not</em> a variable, lexically speaking.
%%
@@ -1579,10 +1467,8 @@ revert_underscore(Node) ->
%% =====================================================================
-%% @spec integer(Value::integer()) -> syntaxTree()
-%%
%% @doc Creates an abstract integer literal. The lexical representation
-%% is the canonical decimal numeral of <code>Value</code>.
+%% is the canonical decimal numeral of `Value'.
%%
%% @see integer_value/1
%% @see integer_literal/1
@@ -1608,11 +1494,8 @@ revert_integer(Node) ->
%% =====================================================================
-%% @spec is_integer(Node::syntaxTree(), Value::integer()) -> boolean()
-%%
-%% @doc Returns <code>true</code> if <code>Node</code> has type
-%% <code>integer</code> and represents <code>Value</code>, otherwise
-%% <code>false</code>.
+%% @doc Returns `true' if `Node' has type
+%% `integer' and represents `Value', otherwise `false'.
%%
%% @see integer/1
@@ -1630,9 +1513,7 @@ is_integer(Node, Value) ->
%% =====================================================================
-%% @spec integer_value(syntaxTree()) -> integer()
-%%
-%% @doc Returns the value represented by an <code>integer</code> node.
+%% @doc Returns the value represented by an `integer' node.
%%
%% @see integer/1
@@ -1648,10 +1529,7 @@ integer_value(Node) ->
%% =====================================================================
-%% @spec integer_literal(syntaxTree()) -> string()
-%%
-%% @doc Returns the numeral string represented by an
-%% <code>integer</code> node.
+%% @doc Returns the numeral string represented by an `integer' node.
%%
%% @see integer/1
@@ -1662,11 +1540,8 @@ integer_literal(Node) ->
%% =====================================================================
-%% @spec float(Value::float()) -> syntaxTree()
-%%
%% @doc Creates an abstract floating-point literal. The lexical
-%% representation is the decimal floating-point numeral of
-%% <code>Value</code>.
+%% representation is the decimal floating-point numeral of `Value'.
%%
%% @see float_value/1
%% @see float_literal/1
@@ -1701,9 +1576,7 @@ revert_float(Node) ->
%% =====================================================================
-%% @spec float_value(syntaxTree()) -> float()
-%%
-%% @doc Returns the value represented by a <code>float</code> node. Note
+%% @doc Returns the value represented by a `float' node. Note
%% that floating-point values should usually not be compared for
%% equality.
%%
@@ -1721,10 +1594,7 @@ float_value(Node) ->
%% =====================================================================
-%% @spec float_literal(syntaxTree()) -> string()
-%%
-%% @doc Returns the numeral string represented by a <code>float</code>
-%% node.
+%% @doc Returns the numeral string represented by a `float' node.
%%
%% @see float/1
@@ -1735,20 +1605,19 @@ float_literal(Node) ->
%% =====================================================================
-%% @spec char(Value::char()) -> syntaxTree()
-%%
%% @doc Creates an abstract character literal. The result represents
-%% "<code>$<em>Name</em></code>", where <code>Name</code> corresponds to
-%% <code>Value</code>.
+%% "<code>$<em>Name</em></code>", where `Name' corresponds to
+%% `Value'.
%%
-%% <p>Note: the literal corresponding to a particular character value is
-%% not uniquely defined. E.g., the character "<code>a</code>" can be
-%% written both as "<code>$a</code>" and "<code>$\141</code>", and a Tab
-%% character can be written as "<code>$\11</code>", "<code>$\011</code>"
-%% or "<code>$\t</code>".</p>
+%% Note: the literal corresponding to a particular character value is
+%% not uniquely defined. E.g., the character "`a'" can be
+%% written both as "`$a'" and "`$\141'", and a Tab
+%% character can be written as "`$\11'", "`$\011'"
+%% or "`$\t'".
%%
%% @see char_value/1
%% @see char_literal/1
+%% @see char_literal/2
%% @see is_char/2
%% type(Node) = char
@@ -1771,11 +1640,8 @@ revert_char(Node) ->
%% =====================================================================
-%% @spec is_char(Node::syntaxTree(), Value::char()) -> boolean()
-%%
-%% @doc Returns <code>true</code> if <code>Node</code> has type
-%% <code>char</code> and represents <code>Value</code>, otherwise
-%% <code>false</code>.
+%% @doc Returns `true' if `Node' has type
+%% `char' and represents `Value', otherwise `false'.
%%
%% @see char/1
@@ -1793,9 +1659,7 @@ is_char(Node, Value) ->
%% =====================================================================
-%% @spec char_value(syntaxTree()) -> char()
-%%
-%% @doc Returns the value represented by a <code>char</code> node.
+%% @doc Returns the value represented by a `char' node.
%%
%% @see char/1
@@ -1811,33 +1675,51 @@ char_value(Node) ->
%% =====================================================================
-%% @spec char_literal(syntaxTree()) -> string()
-%%
-%% @doc Returns the literal string represented by a <code>char</code>
-%% node. This includes the leading "<code>$</code>" character.
+%% @doc Returns the literal string represented by a `char'
+%% node. This includes the leading "`$'" character.
+%% Characters beyond 255 will be escaped.
%%
%% @see char/1
-spec char_literal(syntaxTree()) -> nonempty_string().
char_literal(Node) ->
- io_lib:write_char(char_value(Node)).
+ char_literal(Node, latin1).
%% =====================================================================
-%% @spec string(Value::string()) -> syntaxTree()
+%% @doc Returns the literal string represented by a `char'
+%% node. This includes the leading "`$'" character.
+%% Depending on the encoding a character beyond 255 will be escaped
+%% ('latin1') or copied as is ('utf8').
%%
+%% @see char/1
+
+-type encoding() :: 'utf8' | 'unicode' | 'latin1'.
+
+-spec char_literal(syntaxTree(), encoding()) -> nonempty_string().
+
+char_literal(Node, unicode) ->
+ io_lib:write_unicode_char(char_value(Node));
+char_literal(Node, utf8) ->
+ io_lib:write_unicode_char(char_value(Node));
+char_literal(Node, latin1) ->
+ io_lib:write_unicode_char_as_latin1(char_value(Node)).
+
+
+%% =====================================================================
%% @doc Creates an abstract string literal. The result represents
%% <code>"<em>Text</em>"</code> (including the surrounding
-%% double-quotes), where <code>Text</code> corresponds to the sequence
-%% of characters in <code>Value</code>, but not representing a
-%% <em>specific</em> string literal. E.g., the result of
-%% <code>string("x\ny")</code> represents any and all of
-%% <code>"x\ny"</code>, <code>"x\12y"</code>, <code>"x\012y"</code> and
-%% <code>"x\^Jy"</code>; cf. <code>char/1</code>.
+%% double-quotes), where `Text' corresponds to the sequence
+%% of characters in `Value', but not representing a
+%% <em>specific</em> string literal.
+%%
+%% For example, the result of `string("x\ny")' represents any and all of
+%% `"x\ny"', `"x\12y"', `"x\012y"' and `"x\^Jy"'; see {@link char/1}.
%%
%% @see string_value/1
%% @see string_literal/1
+%% @see string_literal/2
%% @see is_string/2
%% @see char/1
@@ -1861,11 +1743,8 @@ revert_string(Node) ->
%% =====================================================================
-%% @spec is_string(Node::syntaxTree(), Value::string()) -> boolean()
-%%
-%% @doc Returns <code>true</code> if <code>Node</code> has type
-%% <code>string</code> and represents <code>Value</code>, otherwise
-%% <code>false</code>.
+%% @doc Returns `true' if `Node' has type
+%% `string' and represents `Value', otherwise `false'.
%%
%% @see string/1
@@ -1883,9 +1762,7 @@ is_string(Node, Value) ->
%% =====================================================================
-%% @spec string_value(syntaxTree()) -> string()
-%%
-%% @doc Returns the value represented by a <code>string</code> node.
+%% @doc Returns the value represented by a `string' node.
%%
%% @see string/1
@@ -1901,25 +1778,39 @@ string_value(Node) ->
%% =====================================================================
-%% @spec string_literal(syntaxTree()) -> string()
-%%
-%% @doc Returns the literal string represented by a <code>string</code>
+%% @doc Returns the literal string represented by a `string'
%% node. This includes surrounding double-quote characters.
+%% Characters beyond 255 will be escaped.
%%
%% @see string/1
-spec string_literal(syntaxTree()) -> nonempty_string().
string_literal(Node) ->
- io_lib:write_string(string_value(Node)).
+ string_literal(Node, latin1).
%% =====================================================================
-%% @spec atom(Name) -> syntaxTree()
-%% Name = atom() | string()
+%% @doc Returns the literal string represented by a `string'
+%% node. This includes surrounding double-quote characters.
+%% Depending on the encoding characters beyond 255 will be escaped
+%% ('latin1') or copied as is ('utf8').
%%
+%% @see string/1
+
+-spec string_literal(syntaxTree(), encoding()) -> nonempty_string().
+
+string_literal(Node, utf8) ->
+ io_lib:write_unicode_string(string_value(Node));
+string_literal(Node, unicode) ->
+ io_lib:write_unicode_string(string_value(Node));
+string_literal(Node, latin1) ->
+ io_lib:write_unicode_string_as_latin1(string_value(Node)).
+
+
+%% =====================================================================
%% @doc Creates an abstract atom literal. The print name of the atom is
-%% the character sequence represented by <code>Name</code>.
+%% the character sequence represented by `Name'.
%%
%% @see atom_value/1
%% @see atom_name/1
@@ -1948,11 +1839,8 @@ revert_atom(Node) ->
%% =====================================================================
-%% @spec is_atom(Node::syntaxTree(), Value::atom()) -> boolean()
-%%
-%% @doc Returns <code>true</code> if <code>Node</code> has type
-%% <code>atom</code> and represents <code>Value</code>, otherwise
-%% <code>false</code>.
+%% @doc Returns `true' if `Node' has type
+%% `atom' and represents `Value', otherwise `false'.
%%
%% @see atom/1
@@ -1970,9 +1858,7 @@ is_atom(Node, Value) ->
%% =====================================================================
-%% @spec atom_value(syntaxTree()) -> atom()
-%%
-%% @doc Returns the value represented by an <code>atom</code> node.
+%% @doc Returns the value represented by an `atom' node.
%%
%% @see atom/1
@@ -1988,9 +1874,7 @@ atom_value(Node) ->
%% =====================================================================
-%% @spec atom_name(syntaxTree()) -> string()
-%%
-%% @doc Returns the printname of an <code>atom</code> node.
+%% @doc Returns the printname of an `atom' node.
%%
%% @see atom/1
@@ -2001,15 +1885,12 @@ atom_name(Node) ->
%% =====================================================================
-%% @spec atom_literal(syntaxTree()) -> string()
-%%
-%% @doc Returns the literal string represented by an <code>atom</code>
+%% @doc Returns the literal string represented by an `atom'
%% node. This includes surrounding single-quote characters if necessary.
%%
-%% <p>Note that e.g. the result of <code>atom("x\ny")</code> represents
-%% any and all of <code>'x\ny'</code>, <code>'x\12y'</code>,
-%% <code>'x\012y'</code> and <code>'x\^Jy\'</code>; cf.
-%% <code>string/1</code>.</p>
+%% Note that e.g. the result of `atom("x\ny")' represents
+%% any and all of `'x\ny'', `'x\12y'',
+%% `'x\012y'' and `'x\^Jy\''; see {@link string/1}.
%%
%% @see atom/1
%% @see string/1
@@ -2021,14 +1902,12 @@ atom_literal(Node) ->
%% =====================================================================
-%% @spec tuple(Elements::[syntaxTree()]) -> syntaxTree()
-%%
-%% @doc Creates an abstract tuple. If <code>Elements</code> is
-%% <code>[X1, ..., Xn]</code>, the result represents
+%% @doc Creates an abstract tuple. If `Elements' is
+%% `[X1, ..., Xn]', the result represents
%% "<code>{<em>X1</em>, ..., <em>Xn</em>}</code>".
%%
-%% <p>Note: The Erlang language has distinct 1-tuples, i.e.,
-%% <code>{X}</code> is always distinct from <code>X</code> itself.</p>
+%% Note: The Erlang language has distinct 1-tuples, i.e.,
+%% `{X}' is always distinct from `X' itself.
%%
%% @see tuple_elements/1
%% @see tuple_size/1
@@ -2055,10 +1934,7 @@ revert_tuple(Node) ->
%% =====================================================================
-%% @spec tuple_elements(syntaxTree()) -> [syntaxTree()]
-%%
-%% @doc Returns the list of element subtrees of a <code>tuple</code>
-%% node.
+%% @doc Returns the list of element subtrees of a `tuple' node.
%%
%% @see tuple/1
@@ -2074,13 +1950,11 @@ tuple_elements(Node) ->
%% =====================================================================
-%% @spec tuple_size(syntaxTree()) -> integer()
-%%
-%% @doc Returns the number of elements of a <code>tuple</code> node.
+%% @doc Returns the number of elements of a `tuple' node.
%%
-%% <p>Note: this is equivalent to
-%% <code>length(tuple_elements(Node))</code>, but potentially more
-%% efficient.</p>
+%% Note: this is equivalent to
+%% `length(tuple_elements(Node))', but potentially more
+%% efficient.
%%
%% @see tuple/1
%% @see tuple_elements/1
@@ -2092,7 +1966,6 @@ tuple_size(Node) ->
%% =====================================================================
-%% @spec list(List) -> syntaxTree()
%% @equiv list(List, none)
-spec list([syntaxTree()]) -> syntaxTree().
@@ -2102,35 +1975,31 @@ list(List) ->
%% =====================================================================
-%% @spec list(List, Tail) -> syntaxTree()
-%% List = [syntaxTree()]
-%% Tail = none | syntaxTree()
-%%
%% @doc Constructs an abstract list skeleton. The result has type
-%% <code>list</code> or <code>nil</code>. If <code>List</code> is a
-%% nonempty list <code>[E1, ..., En]</code>, the result has type
-%% <code>list</code> and represents either "<code>[<em>E1</em>, ...,
-%% <em>En</em>]</code>", if <code>Tail</code> is <code>none</code>, or
+%% `list' or `nil'. If `List' is a
+%% nonempty list `[E1, ..., En]', the result has type
+%% `list' and represents either "<code>[<em>E1</em>, ...,
+%% <em>En</em>]</code>", if `Tail' is `none', or
%% otherwise "<code>[<em>E1</em>, ..., <em>En</em> |
-%% <em>Tail</em>]</code>". If <code>List</code> is the empty list,
-%% <code>Tail</code> <em>must</em> be <code>none</code>, and in that
-%% case the result has type <code>nil</code> and represents
-%% "<code>[]</code>" (cf. <code>nil/0</code>).
+%% <em>Tail</em>]</code>". If `List' is the empty list,
+%% `Tail' <em>must</em> be `none', and in that
+%% case the result has type `nil' and represents
+%% "`[]'" (see {@link nil/0}).
%%
-%% <p>The difference between lists as semantic objects (built up of
+%% The difference between lists as semantic objects (built up of
%% individual "cons" and "nil" terms) and the various syntactic forms
%% for denoting lists may be bewildering at first. This module provides
%% functions both for exact control of the syntactic representation as
%% well as for the simple composition and deconstruction in terms of
-%% cons and head/tail operations.</p>
+%% cons and head/tail operations.
%%
-%% <p>Note: in <code>list(Elements, none)</code>, the "nil" list
-%% terminator is implicit and has no associated information (cf.
-%% <code>get_attrs/1</code>), while in the seemingly equivalent
-%% <code>list(Elements, Tail)</code> when <code>Tail</code> has type
-%% <code>nil</code>, the list terminator subtree <code>Tail</code> may
+%% Note: in `list(Elements, none)', the "nil" list
+%% terminator is implicit and has no associated information (see
+%% {@link get_attrs/1}), while in the seemingly equivalent
+%% `list(Elements, Tail)' when `Tail' has type
+%% `nil', the list terminator subtree `Tail' may
%% have attached attributes such as position, comments, and annotations,
-%% which will be preserved in the result.</p>
+%% which will be preserved in the result.
%%
%% @see nil/0
%% @see list/1
@@ -2187,10 +2056,8 @@ revert_list(Node) ->
S, P).
%% =====================================================================
-%% @spec nil() -> syntaxTree()
-%%
%% @doc Creates an abstract empty list. The result represents
-%% "<code>[]</code>". The empty list is traditionally called "nil".
+%% "`[]'". The empty list is traditionally called "nil".
%%
%% @see list/2
%% @see is_list_skeleton/1
@@ -2213,13 +2080,11 @@ revert_nil(Node) ->
%% =====================================================================
-%% @spec list_prefix(Node::syntaxTree()) -> [syntaxTree()]
-%%
-%% @doc Returns the prefix element subtrees of a <code>list</code> node.
-%% If <code>Node</code> represents "<code>[<em>E1</em>, ...,
+%% @doc Returns the prefix element subtrees of a `list' node.
+%% If `Node' represents "<code>[<em>E1</em>, ...,
%% <em>En</em>]</code>" or "<code>[<em>E1</em>, ..., <em>En</em> |
-%% <em>Tail</em>]</code>", the returned value is <code>[E1, ...,
-%% En]</code>.
+%% <em>Tail</em>]</code>", the returned value is `[E1, ...,
+%% En]'.
%%
%% @see list/2
@@ -2227,28 +2092,31 @@ revert_nil(Node) ->
list_prefix(Node) ->
case unwrap(Node) of
- {cons, _, Head, _} ->
- [Head];
+ {cons, _, Head, Tail} ->
+ [Head | cons_prefix(Tail)];
Node1 ->
(data(Node1))#list.prefix
end.
+%% collects sequences of conses; cf. cons_suffix/1 below
+cons_prefix({cons, _, Head, Tail}) ->
+ [Head | cons_prefix(Tail)];
+cons_prefix(_) ->
+ [].
+
%% =====================================================================
-%% @spec list_suffix(Node::syntaxTree()) -> none | syntaxTree()
-%%
-%% @doc Returns the suffix subtree of a <code>list</code> node, if one
-%% exists. If <code>Node</code> represents "<code>[<em>E1</em>, ...,
+%% @doc Returns the suffix subtree of a `list' node, if one
+%% exists. If `Node' represents "<code>[<em>E1</em>, ...,
%% <em>En</em> | <em>Tail</em>]</code>", the returned value is
-%% <code>Tail</code>, otherwise, i.e., if <code>Node</code> represents
-%% "<code>[<em>E1</em>, ..., <em>En</em>]</code>", <code>none</code> is
+%% `Tail', otherwise, i.e., if `Node' represents
+%% "<code>[<em>E1</em>, ..., <em>En</em>]</code>", `none' is
%% returned.
%%
-%% <p>Note that even if this function returns some <code>Tail</code>
-%% that is not <code>none</code>, the type of <code>Tail</code> can be
-%% <code>nil</code>, if the tail has been given explicitly, and the list
-%% skeleton has not been compacted (cf.
-%% <code>compact_list/1</code>).</p>
+%% Note that even if this function returns some `Tail'
+%% that is not `none', the type of `Tail' can be
+%% `nil', if the tail has been given explicitly, and the list
+%% skeleton has not been compacted (see {@link compact_list/1}).
%%
%% @see list/2
%% @see nil/0
@@ -2259,34 +2127,36 @@ list_prefix(Node) ->
list_suffix(Node) ->
case unwrap(Node) of
{cons, _, _, Tail} ->
- %% If there could be comments/annotations on the tail node,
- %% we should not return `none' even if it has type `nil'.
- case Tail of
+ case cons_suffix(Tail) of
{nil, _} ->
- none; % no interesting information is lost
- _ ->
- Tail
+ none;
+ Tail1 ->
+ Tail1
end;
Node1 ->
(data(Node1))#list.suffix
end.
+%% skips sequences of conses; cf. cons_prefix/1 above
+cons_suffix({cons, _, _, Tail}) ->
+ cons_suffix(Tail);
+cons_suffix(Tail) ->
+ Tail.
+
%% =====================================================================
-%% @spec cons(Head::syntaxTree(), Tail::syntaxTree()) -> syntaxTree()
-%%
%% @doc "Optimising" list skeleton cons operation. Creates an abstract
-%% list skeleton whose first element is <code>Head</code> and whose tail
-%% corresponds to <code>Tail</code>. This is similar to
-%% <code>list([Head], Tail)</code>, except that <code>Tail</code> may
-%% not be <code>none</code>, and that the result does not necessarily
+%% list skeleton whose first element is `Head' and whose tail
+%% corresponds to `Tail'. This is similar to
+%% `list([Head], Tail)', except that `Tail' may
+%% not be `none', and that the result does not necessarily
%% represent exactly "<code>[<em>Head</em> | <em>Tail</em>]</code>", but
-%% may depend on the <code>Tail</code> subtree. E.g., if
-%% <code>Tail</code> represents <code>[X, Y]</code>, the result may
+%% may depend on the `Tail' subtree. E.g., if
+%% `Tail' represents `[X, Y]', the result may
%% represent "<code>[<em>Head</em>, X, Y]</code>", rather than
%% "<code>[<em>Head</em> | [X, Y]]</code>". Annotations on
-%% <code>Tail</code> itself may be lost if <code>Tail</code> represents
-%% a list skeleton, but comments on <code>Tail</code> are propagated to
+%% `Tail' itself may be lost if `Tail' represents
+%% a list skeleton, but comments on `Tail' are propagated to
%% the result.
%%
%% @see list/2
@@ -2308,10 +2178,8 @@ cons(Head, Tail) ->
%% =====================================================================
-%% @spec list_head(Node::syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the head element subtree of a <code>list</code> node. If
-%% <code>Node</code> represents "<code>[<em>Head</em> ...]</code>", the
+%% @doc Returns the head element subtree of a `list' node. If
+%% `Node' represents "<code>[<em>Head</em> ...]</code>", the
%% result will represent "<code><em>Head</em></code>".
%%
%% @see list/2
@@ -2325,15 +2193,13 @@ list_head(Node) ->
%% =====================================================================
-%% @spec list_tail(Node::syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the tail of a <code>list</code> node. If
-%% <code>Node</code> represents a single-element list
+%% @doc Returns the tail of a `list' node. If
+%% `Node' represents a single-element list
%% "<code>[<em>E</em>]</code>", then the result has type
-%% <code>nil</code>, representing "<code>[]</code>". If
-%% <code>Node</code> represents "<code>[<em>E1</em>, <em>E2</em>
+%% `nil', representing "`[]'". If
+%% `Node' represents "<code>[<em>E1</em>, <em>E2</em>
%% ...]</code>", the result will represent "<code>[<em>E2</em>
-%% ...]</code>", and if <code>Node</code> represents
+%% ...]</code>", and if `Node' represents
%% "<code>[<em>Head</em> | <em>Tail</em>]</code>", the result will
%% represent "<code><em>Tail</em></code>".
%%
@@ -2358,10 +2224,8 @@ list_tail(Node) ->
%% =====================================================================
-%% @spec is_list_skeleton(syntaxTree()) -> boolean()
-%%
-%% @doc Returns <code>true</code> if <code>Node</code> has type
-%% <code>list</code> or <code>nil</code>, otherwise <code>false</code>.
+%% @doc Returns `true' if `Node' has type
+%% `list' or `nil', otherwise `false'.
%%
%% @see list/2
%% @see nil/0
@@ -2377,24 +2241,22 @@ is_list_skeleton(Node) ->
%% =====================================================================
-%% @spec is_proper_list(Node::syntaxTree()) -> boolean()
-%%
-%% @doc Returns <code>true</code> if <code>Node</code> represents a
-%% proper list, and <code>false</code> otherwise. A proper list is a
-%% list skeleton either on the form "<code>[]</code>" or
+%% @doc Returns `true' if `Node' represents a
+%% proper list, and `false' otherwise. A proper list is a
+%% list skeleton either on the form "`[]'" or
%% "<code>[<em>E1</em>, ..., <em>En</em>]</code>", or "<code>[... |
-%% <em>Tail</em>]</code>" where recursively <code>Tail</code> also
+%% <em>Tail</em>]</code>" where recursively `Tail' also
%% represents a proper list.
%%
-%% <p>Note: Since <code>Node</code> is a syntax tree, the actual
+%% Note: Since `Node' 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>
+%% or completely unknown. Thus, if `Node' represents e.g.
+%% "`[... | Ns]'" (where `Ns' is a variable), then
+%% the function will return `false', because it is not known
+%% whether `Ns' will be bound to a list at run-time. If
+%% `Node' instead represents e.g. "`[1, 2, 3]'" or
+%% "`[A | []]'", then the function will return
+%% `true'.
%%
%% @see list/2
@@ -2417,14 +2279,11 @@ is_proper_list(Node) ->
%% =====================================================================
-%% @spec list_elements(Node::syntaxTree()) -> [syntaxTree()]
-%%
%% @doc Returns the list of element subtrees of a list skeleton.
-%% <code>Node</code> must represent a proper list. E.g., if
-%% <code>Node</code> represents "<code>[<em>X1</em>, <em>X2</em> |
+%% `Node' must represent a proper list. E.g., if
+%% `Node' 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>.
+%% `list_elements(Node)' yields the list `[X1, X2, X3, X4]'.
%%
%% @see list/2
%% @see is_proper_list/1
@@ -2450,17 +2309,15 @@ list_elements(Node, As) ->
%% =====================================================================
-%% @spec list_length(Node::syntaxTree()) -> integer()
-%%
%% @doc Returns the number of element subtrees of a list skeleton.
-%% <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
+%% `Node' must represent a proper list. E.g., if
+%% `Node' represents "`[X1 | [X2, X3 | [X4, X5,
+%% X6]]]'", then `list_length(Node)' returns the
%% integer 6.
%%
-%% <p>Note: this is equivalent to
-%% <code>length(list_elements(Node))</code>, but potentially more
-%% efficient.</p>
+%% Note: this is equivalent to
+%% `length(list_elements(Node))', but potentially more
+%% efficient.
%%
%% @see list/2
%% @see is_proper_list/1
@@ -2487,18 +2344,16 @@ list_length(Node, A) ->
%% =====================================================================
-%% @spec normalize_list(Node::syntaxTree()) -> syntaxTree()
-%%
%% @doc Expands an abstract list skeleton to its most explicit form. If
-%% <code>Node</code> represents "<code>[<em>E1</em>, ..., <em>En</em> |
+%% `Node' represents "<code>[<em>E1</em>, ..., <em>En</em> |
%% <em>Tail</em>]</code>", the result represents "<code>[<em>E1</em> |
%% ... [<em>En</em> | <em>Tail1</em>] ... ]</code>", where
-%% <code>Tail1</code> is the result of
-%% <code>normalize_list(Tail)</code>. If <code>Node</code> represents
+%% `Tail1' is the result of
+%% `normalize_list(Tail)'. If `Node' represents
%% "<code>[<em>E1</em>, ..., <em>En</em>]</code>", the result simply
%% represents "<code>[<em>E1</em> | ... [<em>En</em> | []] ...
-%% ]</code>". If <code>Node</code> does not represent a list skeleton,
-%% <code>Node</code> itself is returned.
+%% ]</code>". If `Node' does not represent a list skeleton,
+%% `Node' itself is returned.
%%
%% @see list/2
%% @see compact_list/1
@@ -2528,16 +2383,14 @@ normalize_list_1(Es, Tail) ->
%% =====================================================================
-%% @spec compact_list(Node::syntaxTree()) -> syntaxTree()
-%%
%% @doc Yields the most compact form for an abstract list skeleton. The
%% result either represents "<code>[<em>E1</em>, ..., <em>En</em> |
-%% <em>Tail</em>]</code>", where <code>Tail</code> is not a list
+%% <em>Tail</em>]</code>", where `Tail' is not a list
%% skeleton, or otherwise simply "<code>[<em>E1</em>, ...,
-%% <em>En</em>]</code>". Annotations on subtrees of <code>Node</code>
+%% <em>En</em>]</code>". Annotations on subtrees of `Node'
%% that represent list skeletons may be lost, but comments will be
-%% propagated to the result. Returns <code>Node</code> itself if
-%% <code>Node</code> does not represent a list skeleton.
+%% propagated to the result. Returns `Node' itself if
+%% `Node' does not represent a list skeleton.
%%
%% @see list/2
%% @see normalize_list/1
@@ -2575,10 +2428,8 @@ compact_list(Node) ->
%% =====================================================================
-%% @spec binary(Fields::[syntaxTree()]) -> syntaxTree()
-%%
%% @doc Creates an abstract binary-object template. If
-%% <code>Fields</code> is <code>[F1, ..., Fn]</code>, the result
+%% `Fields' is `[F1, ..., Fn]', the result
%% represents "<code>&lt;&lt;<em>F1</em>, ...,
%% <em>Fn</em>&gt;&gt;</code>".
%%
@@ -2611,10 +2462,7 @@ revert_binary(Node) ->
%% =====================================================================
-%% @spec binary_fields(syntaxTree()) -> [syntaxTree()]
-%%
-%% @doc Returns the list of field subtrees of a <code>binary</code>
-%% node.
+%% @doc Returns the list of field subtrees of a `binary' node.
%%
%% @see binary/1
%% @see binary_field/2
@@ -2631,7 +2479,6 @@ binary_fields(Node) ->
%% =====================================================================
-%% @spec binary_field(Body) -> syntaxTree()
%% @equiv binary_field(Body, [])
-spec binary_field(syntaxTree()) -> syntaxTree().
@@ -2641,15 +2488,11 @@ binary_field(Body) ->
%% =====================================================================
-%% @spec binary_field(Body::syntaxTree(), Size,
-%% Types::[syntaxTree()]) -> syntaxTree()
-%% Size = none | syntaxTree()
-%%
%% @doc Creates an abstract binary template field.
-%% If <code>Size</code> is <code>none</code>, this is equivalent to
-%% "<code>binary_field(Body, Types)</code>", otherwise it is
-%% equivalent to "<code>binary_field(size_qualifier(Body, Size),
-%% Types)</code>".
+%% If `Size' is `none', this is equivalent to
+%% "`binary_field(Body, Types)'", otherwise it is
+%% equivalent to "`binary_field(size_qualifier(Body, Size),
+%% Types)'".
%%
%% (This is a utility function.)
%%
@@ -2667,13 +2510,10 @@ binary_field(Body, Size, Types) ->
%% =====================================================================
-%% @spec binary_field(Body::syntaxTree(), Types::[syntaxTree()]) ->
-%% syntaxTree()
-%%
%% @doc Creates an abstract binary template field. If
-%% <code>Types</code> is the empty list, the result simply represents
-%% "<code><em>Body</em></code>", otherwise, if <code>Types</code> is
-%% <code>[T1, ..., Tn]</code>, the result represents
+%% `Types' is the empty list, the result simply represents
+%% "<code><em>Body</em></code>", otherwise, if `Types' is
+%% `[T1, ..., Tn]', the result represents
%% "<code><em>Body</em>/<em>T1</em>-...-<em>Tn</em></code>".
%%
%% @see binary/1
@@ -2727,9 +2567,7 @@ revert_binary_field(Node) ->
%% =====================================================================
-%% @spec binary_field_body(syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the body subtree of a <code>binary_field</code>.
+%% @doc Returns the body subtree of a `binary_field'.
%%
%% @see binary_field/2
@@ -2749,12 +2587,10 @@ binary_field_body(Node) ->
%% =====================================================================
-%% @spec binary_field_types(Node::syntaxTree()) -> [syntaxTree()]
-%%
%% @doc Returns the list of type-specifier subtrees of a
-%% <code>binary_field</code> node. If <code>Node</code> represents
+%% `binary_field' node. If `Node' represents
%% "<code>.../<em>T1</em>, ..., <em>Tn</em></code>", the result is
-%% <code>[T1, ..., Tn]</code>, otherwise the result is the empty list.
+%% `[T1, ..., Tn]', otherwise the result is the empty list.
%%
%% @see binary_field/2
@@ -2774,14 +2610,12 @@ binary_field_types(Node) ->
%% =====================================================================
-%% @spec binary_field_size(Node::syntaxTree()) -> none | syntaxTree()
-%%
%% @doc Returns the size specifier subtree of a
-%% <code>binary_field</code> node, if any. If <code>Node</code>
+%% `binary_field' node, if any. If `Node'
%% represents "<code><em>Body</em>:<em>Size</em></code>" or
%% "<code><em>Body</em>:<em>Size</em>/<em>T1</em>, ...,
-%% <em>Tn</em></code>", the result is <code>Size</code>, otherwise
-%% <code>none</code> is returned.
+%% <em>Tn</em></code>", the result is `Size', otherwise
+%% `none' is returned.
%%
%% (This is a utility function.)
%%
@@ -2810,9 +2644,6 @@ binary_field_size(Node) ->
%% =====================================================================
-%% @spec size_qualifier(Body::syntaxTree(), Size::syntaxTree()) ->
-%% syntaxTree()
-%%
%% @doc Creates an abstract size qualifier. The result represents
%% "<code><em>Body</em>:<em>Size</em></code>".
%%
@@ -2834,10 +2665,7 @@ size_qualifier(Body, Size) ->
%% =====================================================================
-%% @spec size_qualifier_body(syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the body subtree of a <code>size_qualifier</code>
-%% node.
+%% @doc Returns the body subtree of a `size_qualifier' node.
%%
%% @see size_qualifier/2
@@ -2848,10 +2676,8 @@ size_qualifier_body(Node) ->
%% =====================================================================
-%% @spec size_qualifier_argument(syntaxTree()) -> syntaxTree()
-%%
%% @doc Returns the argument subtree (the size) of a
-%% <code>size_qualifier</code> node.
+%% `size_qualifier' node.
%%
%% @see size_qualifier/2
@@ -2862,16 +2688,14 @@ size_qualifier_argument(Node) ->
%% =====================================================================
-%% @spec error_marker(Error::term()) -> syntaxTree()
-%%
%% @doc Creates an abstract error marker. The result represents an
%% occurrence of an error in the source code, with an associated Erlang
-%% I/O ErrorInfo structure given by <code>Error</code> (see module
+%% I/O ErrorInfo structure given by `Error' (see module
%% {@link //stdlib/io} for details). Error markers are regarded as source
%% code forms, but have no defined lexical form.
%%
-%% <p>Note: this is supported only for backwards compatibility with
-%% existing parsers and tools.</p>
+%% Note: this is supported only for backwards compatibility with
+%% existing parsers and tools.
%%
%% @see error_marker_info/1
%% @see warning_marker/1
@@ -2902,10 +2726,7 @@ revert_error_marker(Node) ->
%% =====================================================================
-%% @spec error_marker_info(syntaxTree()) -> term()
-%%
-%% @doc Returns the ErrorInfo structure of an <code>error_marker</code>
-%% node.
+%% @doc Returns the ErrorInfo structure of an `error_marker' node.
%%
%% @see error_marker/1
@@ -2921,16 +2742,14 @@ error_marker_info(Node) ->
%% =====================================================================
-%% @spec warning_marker(Error::term()) -> syntaxTree()
-%%
%% @doc Creates an abstract warning marker. The result represents an
%% occurrence of a possible problem in the source code, with an
-%% associated Erlang I/O ErrorInfo structure given by <code>Error</code>
+%% associated Erlang I/O ErrorInfo structure given by `Error'
%% (see module {@link //stdlib/io} for details). Warning markers are
%% regarded as source code forms, but have no defined lexical form.
%%
-%% <p>Note: this is supported only for backwards compatibility with
-%% existing parsers and tools.</p>
+%% Note: this is supported only for backwards compatibility with
+%% existing parsers and tools.
%%
%% @see warning_marker_info/1
%% @see error_marker/1
@@ -2961,10 +2780,7 @@ revert_warning_marker(Node) ->
%% =====================================================================
-%% @spec warning_marker_info(syntaxTree()) -> term()
-%%
-%% @doc Returns the ErrorInfo structure of a <code>warning_marker</code>
-%% node.
+%% @doc Returns the ErrorInfo structure of a `warning_marker' node.
%%
%% @see warning_marker/1
@@ -2980,16 +2796,14 @@ warning_marker_info(Node) ->
%% =====================================================================
-%% @spec eof_marker() -> syntaxTree()
-%%
%% @doc Creates an abstract end-of-file marker. This represents the
%% end of input when reading a sequence of source code forms. An
%% end-of-file marker is itself regarded as a source code form
%% (namely, the last in any sequence in which it occurs). It has no
%% defined lexical form.
%%
-%% <p>Note: this is retained only for backwards compatibility with
-%% existing parsers and tools.</p>
+%% Note: this is retained only for backwards compatibility with
+%% existing parsers and tools.
%%
%% @see error_marker/1
%% @see warning_marker/1
@@ -3013,7 +2827,6 @@ revert_eof_marker(Node) ->
%% =====================================================================
-%% @spec attribute(Name) -> syntaxTree()
%% @equiv attribute(Name, none)
-spec attribute(syntaxTree()) -> syntaxTree().
@@ -3023,23 +2836,20 @@ attribute(Name) ->
%% =====================================================================
-%% @spec attribute(Name::syntaxTree(), Arguments) -> syntaxTree()
-%% Arguments = none | [syntaxTree()]
-%%
%% @doc Creates an abstract program attribute. If
-%% <code>Arguments</code> is <code>[A1, ..., An]</code>, the result
+%% `Arguments' is `[A1, ..., An]', the result
%% represents "<code>-<em>Name</em>(<em>A1</em>, ...,
-%% <em>An</em>).</code>". Otherwise, if <code>Arguments</code> is
-%% <code>none</code>, the result represents
+%% <em>An</em>).</code>". Otherwise, if `Arguments' is
+%% `none', the result represents
%% "<code>-<em>Name</em>.</code>". The latter form makes it possible
%% to represent preprocessor directives such as
-%% "<code>-endif.</code>". Attributes are source code forms.
+%% "`-endif.'". Attributes are source code forms.
%%
-%% <p>Note: The preprocessor macro definition directive
+%% Note: The preprocessor macro definition directive
%% "<code>-define(<em>Name</em>, <em>Body</em>).</code>" has relatively
-%% few requirements on the syntactical form of <code>Body</code> (viewed
-%% as a sequence of tokens). The <code>text</code> node type can be used
-%% for a <code>Body</code> that is not a normal Erlang construct.</p>
+%% few requirements on the syntactical form of `Body' (viewed
+%% as a sequence of tokens). The `text' node type can be used
+%% for a `Body' that is not a normal Erlang construct.
%%
%% @see attribute/1
%% @see attribute_name/1
@@ -3224,18 +3034,13 @@ revert_module_name(A) ->
case type(A) of
atom ->
{ok, concrete(A)};
- qualified_name ->
- Ss = qualified_name_segments(A),
- {ok, [concrete(S) || S <- Ss]};
_ ->
error
end.
%% =====================================================================
-%% @spec attribute_name(syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the name subtree of an <code>attribute</code> node.
+%% @doc Returns the name subtree of an `attribute' node.
%%
%% @see attribute/1
@@ -3251,15 +3056,12 @@ attribute_name(Node) ->
%% =====================================================================
-%% @spec attribute_arguments(Node::syntaxTree()) ->
-%% none | [syntaxTree()]
-%%
%% @doc Returns the list of argument subtrees of an
-%% <code>attribute</code> node, if any. If <code>Node</code>
+%% `attribute' node, if any. If `Node'
%% represents "<code>-<em>Name</em>.</code>", the result is
-%% <code>none</code>. Otherwise, if <code>Node</code> represents
+%% `none'. Otherwise, if `Node' represents
%% "<code>-<em>Name</em>(<em>E1</em>, ..., <em>En</em>).</code>",
-%% <code>[E1, ..., E1]</code> is returned.
+%% `[E1, ..., E1]' is returned.
%%
%% @see attribute/1
@@ -3277,11 +3079,7 @@ attribute_arguments(Node) ->
M0 ->
{M0, none}
end,
- M2 = if is_list(M1) ->
- qualified_name([atom(A) || A <- M1]);
- true ->
- atom(M1)
- end,
+ M2 = atom(M1),
M = set_pos(M2, Pos),
if Vs == none -> [M];
true -> [M, set_pos(list(Vs), Pos)]
@@ -3291,20 +3089,11 @@ attribute_arguments(Node) ->
list(unfold_function_names(Data, Pos)),
Pos)];
import ->
- case Data of
- {Module, Imports} ->
- [if is_list(Module) ->
- qualified_name([atom(A)
- || A <- Module]);
- true ->
- set_pos(atom(Module), Pos)
- end,
- set_pos(
- list(unfold_function_names(Imports, Pos)),
- Pos)];
- _ ->
- [qualified_name([atom(A) || A <- Data])]
- end;
+ {Module, Imports} = Data,
+ [set_pos(atom(Module), Pos),
+ set_pos(
+ list(unfold_function_names(Imports, Pos)),
+ Pos)];
file ->
{File, Line} = Data,
[set_pos(string(File), Pos),
@@ -3326,9 +3115,6 @@ attribute_arguments(Node) ->
%% =====================================================================
-%% @spec arity_qualifier(Body::syntaxTree(), Arity::syntaxTree()) ->
-%% syntaxTree()
-%%
%% @doc Creates an abstract arity qualifier. The result represents
%% "<code><em>Body</em>/<em>Arity</em></code>".
%%
@@ -3350,10 +3136,7 @@ arity_qualifier(Body, Arity) ->
%% =====================================================================
-%% @spec arity_qualifier_body(syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the body subtree of an <code>arity_qualifier</code>
-%% node.
+%% @doc Returns the body subtree of an `arity_qualifier' node.
%%
%% @see arity_qualifier/2
@@ -3364,10 +3147,8 @@ arity_qualifier_body(Node) ->
%% =====================================================================
-%% @spec arity_qualifier_argument(syntaxTree()) -> syntaxTree()
-%%
%% @doc Returns the argument (the arity) subtree of an
-%% <code>arity_qualifier</code> node.
+%% `arity_qualifier' node.
%%
%% @see arity_qualifier/2
@@ -3378,9 +3159,6 @@ arity_qualifier_argument(Node) ->
%% =====================================================================
-%% @spec module_qualifier(Module::syntaxTree(), Body::syntaxTree()) ->
-%% syntaxTree()
-%%
%% @doc Creates an abstract module qualifier. The result represents
%% "<code><em>Module</em>:<em>Body</em></code>".
%%
@@ -3414,10 +3192,8 @@ revert_module_qualifier(Node) ->
%% =====================================================================
-%% @spec module_qualifier_argument(syntaxTree()) -> syntaxTree()
-%%
%% @doc Returns the argument (the module) subtree of a
-%% <code>module_qualifier</code> node.
+%% `module_qualifier' node.
%%
%% @see module_qualifier/2
@@ -3433,10 +3209,7 @@ module_qualifier_argument(Node) ->
%% =====================================================================
-%% @spec module_qualifier_body(syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the body subtree of a <code>module_qualifier</code>
-%% node.
+%% @doc Returns the body subtree of a `module_qualifier' node.
%%
%% @see module_qualifier/2
@@ -3452,64 +3225,10 @@ module_qualifier_body(Node) ->
%% =====================================================================
-%% @spec qualified_name(Segments::[syntaxTree()]) -> syntaxTree()
-%%
-%% @doc Creates an abstract qualified name. The result represents
-%% "<code><em>S1</em>.<em>S2</em>. ... .<em>Sn</em></code>", if
-%% <code>Segments</code> is <code>[S1, S2, ..., Sn]</code>.
-%%
-%% @see qualified_name_segments/1
-
-%% type(Node) = qualified_name
-%% data(Node) = [syntaxTree()]
-%%
-%% `erl_parse' representation:
-%%
-%% {record_field, Pos, Node, Node}
-%%
-%% Node = {atom, Pos, Value} | {record_field, Pos, Node, Node}
-%%
-%% Note that if not all leaf subnodes are (abstract) atoms, then Node
-%% represents a Mnemosyne query record field access ('record_access');
-%% see type/1 for details.
-
--spec qualified_name([syntaxTree()]) -> syntaxTree().
-
-qualified_name(Segments) ->
- tree(qualified_name, Segments).
-
-revert_qualified_name(Node) ->
- Pos = get_pos(Node),
- fold_qualified_name(qualified_name_segments(Node), Pos).
-
-
-%% =====================================================================
-%% @spec qualified_name_segments(syntaxTree()) -> [syntaxTree()]
-%%
-%% @doc Returns the list of name segments of a
-%% <code>qualified_name</code> node.
-%%
-%% @see qualified_name/1
-
--spec qualified_name_segments(syntaxTree()) -> [syntaxTree()].
-
-qualified_name_segments(Node) ->
- case unwrap(Node) of
- {record_field, _, _, _} = Node1 ->
- unfold_qualified_name(Node1);
- Node1 ->
- data(Node1)
- end.
-
-
-%% =====================================================================
-%% @spec function(Name::syntaxTree(), Clauses::[syntaxTree()]) ->
-%% syntaxTree()
-%%
-%% @doc Creates an abstract function definition. If <code>Clauses</code>
-%% is <code>[C1, ..., Cn]</code>, the result represents
+%% @doc Creates an abstract function definition. If `Clauses'
+%% is `[C1, ..., Cn]', the result represents
%% "<code><em>Name</em> <em>C1</em>; ...; <em>Name</em>
-%% <em>Cn</em>.</code>". More exactly, if each <code>Ci</code>
+%% <em>Cn</em>.</code>". More exactly, if each `Ci'
%% represents "<code>(<em>Pi1</em>, ..., <em>Pim</em>) <em>Gi</em> ->
%% <em>Bi</em></code>", then the result represents
%% "<code><em>Name</em>(<em>P11</em>, ..., <em>P1m</em>) <em>G1</em> ->
@@ -3523,13 +3242,12 @@ qualified_name_segments(Node) ->
%% @see is_form/1
%% @see rule/2
--record(function, {name, clauses}).
-%% XXX: This one is problematic because there is a tuple with the same
-%% tag and size that comes from 'erl_parse'
-%% -record(function, {name :: syntaxTree(), clauses :: [syntaxTree()]}).
+%% Don't use the name 'function' for this record, to avoid confusion with
+%% the tuples on the form {function,Name,Arity} used by erl_parse.
+-record(func, {name :: syntaxTree(), clauses :: [syntaxTree()]}).
%% type(Node) = function
-%% data(Node) = #function{name :: Name, clauses :: Clauses}
+%% data(Node) = #func{name :: Name, clauses :: Clauses}
%%
%% Name = syntaxTree()
%% Clauses = [syntaxTree()]
@@ -3556,7 +3274,7 @@ qualified_name_segments(Node) ->
-spec function(syntaxTree(), [syntaxTree()]) -> syntaxTree().
function(Name, Clauses) ->
- tree(function, #function{name = Name, clauses = Clauses}).
+ tree(function, #func{name = Name, clauses = Clauses}).
revert_function(Node) ->
Name = function_name(Node),
@@ -3572,9 +3290,7 @@ revert_function(Node) ->
%% =====================================================================
-%% @spec function_name(syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the name subtree of a <code>function</code> node.
+%% @doc Returns the name subtree of a `function' node.
%%
%% @see function/2
@@ -3585,15 +3301,12 @@ function_name(Node) ->
{function, Pos, Name, _, _} ->
set_pos(atom(Name), Pos);
Node1 ->
- (data(Node1))#function.name
+ (data(Node1))#func.name
end.
%% =====================================================================
-%% @spec function_clauses(syntaxTree()) -> [syntaxTree()]
-%%
-%% @doc Returns the list of clause subtrees of a <code>function</code>
-%% node.
+%% @doc Returns the list of clause subtrees of a `function' node.
%%
%% @see function/2
@@ -3604,21 +3317,19 @@ function_clauses(Node) ->
{function, _, _, _, Clauses} ->
Clauses;
Node1 ->
- (data(Node1))#function.clauses
+ (data(Node1))#func.clauses
end.
%% =====================================================================
-%% @spec function_arity(Node::syntaxTree()) -> integer()
-%%
-%% @doc Returns the arity of a <code>function</code> node. The result
+%% @doc Returns the arity of a `function' node. The result
%% is the number of parameter patterns in the first clause of the
%% function; subsequent clauses are ignored.
%%
-%% <p>An exception is thrown if <code>function_clauses(Node)</code>
+%% An exception is thrown if `function_clauses(Node)'
%% returns an empty list, or if the first element of that list is not
-%% a syntax tree <code>C</code> of type <code>clause</code> such that
-%% <code>clause_patterns(C)</code> is a nonempty list.</p>
+%% a syntax tree `C' of type `clause' such that
+%% `clause_patterns(C)' is a nonempty list.
%%
%% @see function/2
%% @see function_clauses/1
@@ -3634,7 +3345,6 @@ function_arity(Node) ->
%% =====================================================================
-%% @spec clause(Guard, Body) -> syntaxTree()
%% @equiv clause([], Guard, Body)
-type guard() :: 'none' | syntaxTree() | [syntaxTree()] | [[syntaxTree()]].
@@ -3646,34 +3356,28 @@ clause(Guard, Body) ->
%% =====================================================================
-%% @spec clause(Patterns::[syntaxTree()], Guard,
-%% Body::[syntaxTree()]) -> syntaxTree()
-%% Guard = none | syntaxTree()
-%% | [syntaxTree()] | [[syntaxTree()]]
-%%
-%% @doc Creates an abstract clause. If <code>Patterns</code> is
-%% <code>[P1, ..., Pn]</code> and <code>Body</code> is <code>[B1, ...,
-%% Bm]</code>, then if <code>Guard</code> is <code>none</code>, the
+%% @doc Creates an abstract clause. If `Patterns' is
+%% `[P1, ..., Pn]' and `Body' is `[B1, ...,
+%% Bm]', then if `Guard' is `none', the
%% result represents "<code>(<em>P1</em>, ..., <em>Pn</em>) ->
%% <em>B1</em>, ..., <em>Bm</em></code>", otherwise, unless
-%% <code>Guard</code> is a list, the result represents
+%% `Guard' is a list, the result represents
%% "<code>(<em>P1</em>, ..., <em>Pn</em>) when <em>Guard</em> ->
%% <em>B1</em>, ..., <em>Bm</em></code>".
%%
-%% <p>For simplicity, the <code>Guard</code> argument may also be any
+%% For simplicity, the `Guard' argument may also be any
%% of the following:
%% <ul>
-%% <li>An empty list <code>[]</code>. This is equivalent to passing
-%% <code>none</code>.</li>
-%% <li>A nonempty list <code>[E1, ..., Ej]</code> of syntax trees.
-%% This is equivalent to passing <code>conjunction([E1, ...,
-%% Ej])</code>.</li>
-%% <li>A nonempty list of lists of syntax trees <code>[[E1_1, ...,
-%% E1_k1], ..., [Ej_1, ..., Ej_kj]]</code>, which is equivalent
-%% to passing <code>disjunction([conjunction([E1_1, ...,
-%% E1_k1]), ..., conjunction([Ej_1, ..., Ej_kj])])</code>.</li>
+%% <li>An empty list `[]'. This is equivalent to passing
+%% `none'.</li>
+%% <li>A nonempty list `[E1, ..., Ej]' of syntax trees.
+%% This is equivalent to passing `conjunction([E1, ...,
+%% Ej])'.</li>
+%% <li>A nonempty list of lists of syntax trees `[[E1_1, ...,
+%% E1_k1], ..., [Ej_1, ..., Ej_kj]]', which is equivalent
+%% to passing `disjunction([conjunction([E1_1, ...,
+%% E1_k1]), ..., conjunction([Ej_1, ..., Ej_kj])])'.</li>
%% </ul>
-%% </p>
%%
%% @see clause/2
%% @see clause_patterns/1
@@ -3789,10 +3493,7 @@ unfold_try_clause({clause, Pos, [{tuple, _, [C, V, _]}],
%% =====================================================================
-%% @spec clause_patterns(syntaxTree()) -> [syntaxTree()]
-%%
-%% @doc Returns the list of pattern subtrees of a <code>clause</code>
-%% node.
+%% @doc Returns the list of pattern subtrees of a `clause' node.
%%
%% @see clause/3
@@ -3808,13 +3509,11 @@ clause_patterns(Node) ->
%% =====================================================================
-%% @spec clause_guard(Node::syntaxTree()) -> none | syntaxTree()
-%%
-%% @doc Returns the guard subtree of a <code>clause</code> node, if
-%% any. If <code>Node</code> represents "<code>(<em>P1</em>, ...,
+%% @doc Returns the guard subtree of a `clause' node, if
+%% any. If `Node' represents "<code>(<em>P1</em>, ...,
%% <em>Pn</em>) when <em>Guard</em> -> <em>B1</em>, ...,
-%% <em>Bm</em></code>", <code>Guard</code> is returned. Otherwise, the
-%% result is <code>none</code>.
+%% <em>Bm</em></code>", `Guard' is returned. Otherwise, the
+%% result is `none'.
%%
%% @see clause/3
@@ -3836,10 +3535,7 @@ clause_guard(Node) ->
%% =====================================================================
-%% @spec clause_body(syntaxTree()) -> [syntaxTree()]
-%%
-%% @doc Return the list of body subtrees of a <code>clause</code>
-%% node.
+%% @doc Return the list of body subtrees of a `clause' node.
%%
%% @see clause/3
@@ -3855,10 +3551,8 @@ clause_body(Node) ->
%% =====================================================================
-%% @spec disjunction(List::[syntaxTree()]) -> syntaxTree()
-%%
-%% @doc Creates an abstract disjunction. If <code>List</code> is
-%% <code>[E1, ..., En]</code>, the result represents
+%% @doc Creates an abstract disjunction. If `List' is
+%% `[E1, ..., En]', the result represents
%% "<code><em>E1</em>; ...; <em>En</em></code>".
%%
%% @see disjunction_body/1
@@ -3874,10 +3568,8 @@ disjunction(Tests) ->
%% =====================================================================
-%% @spec disjunction_body(syntaxTree()) -> [syntaxTree()]
-%%
%% @doc Returns the list of body subtrees of a
-%% <code>disjunction</code> node.
+%% `disjunction' node.
%%
%% @see disjunction/1
@@ -3888,10 +3580,8 @@ disjunction_body(Node) ->
%% =====================================================================
-%% @spec conjunction(List::[syntaxTree()]) -> syntaxTree()
-%%
-%% @doc Creates an abstract conjunction. If <code>List</code> is
-%% <code>[E1, ..., En]</code>, the result represents
+%% @doc Creates an abstract conjunction. If `List' is
+%% `[E1, ..., En]', the result represents
%% "<code><em>E1</em>, ..., <em>En</em></code>".
%%
%% @see conjunction_body/1
@@ -3907,10 +3597,8 @@ conjunction(Tests) ->
%% =====================================================================
-%% @spec conjunction_body(syntaxTree()) -> [syntaxTree()]
-%%
%% @doc Returns the list of body subtrees of a
-%% <code>conjunction</code> node.
+%% `conjunction' node.
%%
%% @see conjunction/1
@@ -3921,8 +3609,6 @@ conjunction_body(Node) ->
%% =====================================================================
-%% @spec catch_expr(Expr::syntaxTree()) -> syntaxTree()
-%%
%% @doc Creates an abstract catch-expression. The result represents
%% "<code>catch <em>Expr</em></code>".
%%
@@ -3949,9 +3635,7 @@ revert_catch_expr(Node) ->
%% =====================================================================
-%% @spec catch_expr_body(syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the body subtree of a <code>catch_expr</code> node.
+%% @doc Returns the body subtree of a `catch_expr' node.
%%
%% @see catch_expr/1
@@ -3967,9 +3651,6 @@ catch_expr_body(Node) ->
%% =====================================================================
-%% @spec match_expr(Pattern::syntaxTree(), Body::syntaxTree()) ->
-%% syntaxTree()
-%%
%% @doc Creates an abstract match-expression. The result represents
%% "<code><em>Pattern</em> = <em>Body</em></code>".
%%
@@ -4002,9 +3683,7 @@ revert_match_expr(Node) ->
%% =====================================================================
-%% @spec match_expr_pattern(syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the pattern subtree of a <code>match_expr</code> node.
+%% @doc Returns the pattern subtree of a `match_expr' node.
%%
%% @see match_expr/2
@@ -4020,9 +3699,7 @@ match_expr_pattern(Node) ->
%% =====================================================================
-%% @spec match_expr_body(syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the body subtree of a <code>match_expr</code> node.
+%% @doc Returns the body subtree of a `match_expr' node.
%%
%% @see match_expr/2
@@ -4038,15 +3715,12 @@ match_expr_body(Node) ->
%% =====================================================================
-%% @spec operator(Name) -> syntaxTree()
-%% Name = atom() | string()
-%%
%% @doc Creates an abstract operator. The name of the operator is the
-%% character sequence represented by <code>Name</code>. This is
+%% character sequence represented by `Name'. This is
%% analogous to the print name of an atom, but an operator is never
%% written within single-quotes; e.g., the result of
-%% <code>operator('++')</code> represents "<code>++</code>" rather
-%% than "<code>'++'</code>".
+%% `operator('++')' represents "`++'" rather
+%% than "`'++''".
%%
%% @see operator_name/1
%% @see operator_literal/1
@@ -4064,9 +3738,7 @@ operator(Name) ->
%% =====================================================================
-%% @spec operator_name(syntaxTree()) -> atom()
-%%
-%% @doc Returns the name of an <code>operator</code> node. Note that
+%% @doc Returns the name of an `operator' node. Note that
%% the name is returned as an atom.
%%
%% @see operator/1
@@ -4078,11 +3750,8 @@ operator_name(Node) ->
%% =====================================================================
-%% @spec operator_literal(syntaxTree()) -> string()
-%%
%% @doc Returns the literal string represented by an
-%% <code>operator</code> node. This is simply the operator name as a
-%% string.
+%% `operator' node. This is simply the operator name as a string.
%%
%% @see operator/1
@@ -4093,9 +3762,6 @@ operator_literal(Node) ->
%% =====================================================================
-%% @spec infix_expr(Left::syntaxTree(), Operator::syntaxTree(),
-%% Right::syntaxTree()) -> syntaxTree()
-%%
%% @doc Creates an abstract infix operator expression. The result
%% represents "<code><em>Left</em> <em>Operator</em>
%% <em>Right</em></code>".
@@ -4144,10 +3810,8 @@ revert_infix_expr(Node) ->
%% =====================================================================
-%% @spec infix_expr_left(syntaxTree()) -> syntaxTree()
-%%
%% @doc Returns the left argument subtree of an
-%% <code>infix_expr</code> node.
+%% `infix_expr' node.
%%
%% @see infix_expr/3
@@ -4163,10 +3827,7 @@ infix_expr_left(Node) ->
%% =====================================================================
-%% @spec infix_expr_operator(syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the operator subtree of an <code>infix_expr</code>
-%% node.
+%% @doc Returns the operator subtree of an `infix_expr' node.
%%
%% @see infix_expr/3
@@ -4182,10 +3843,8 @@ infix_expr_operator(Node) ->
%% =====================================================================
-%% @spec infix_expr_right(syntaxTree()) -> syntaxTree()
-%%
%% @doc Returns the right argument subtree of an
-%% <code>infix_expr</code> node.
+%% `infix_expr' node.
%%
%% @see infix_expr/3
@@ -4201,9 +3860,6 @@ infix_expr_right(Node) ->
%% =====================================================================
-%% @spec prefix_expr(Operator::syntaxTree(), Argument::syntaxTree()) ->
-%% syntaxTree()
-%%
%% @doc Creates an abstract prefix operator expression. The result
%% represents "<code><em>Operator</em> <em>Argument</em></code>".
%%
@@ -4247,10 +3903,7 @@ revert_prefix_expr(Node) ->
%% =====================================================================
-%% @spec prefix_expr_operator(syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the operator subtree of a <code>prefix_expr</code>
-%% node.
+%% @doc Returns the operator subtree of a `prefix_expr' node.
%%
%% @see prefix_expr/2
@@ -4266,10 +3919,7 @@ prefix_expr_operator(Node) ->
%% =====================================================================
-%% @spec prefix_expr_argument(syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the argument subtree of a <code>prefix_expr</code>
-%% node.
+%% @doc Returns the argument subtree of a `prefix_expr' node.
%%
%% @see prefix_expr/2
@@ -4285,7 +3935,6 @@ prefix_expr_argument(Node) ->
%% =====================================================================
-%% @spec record_field(Name) -> syntaxTree()
%% @equiv record_field(Name, none)
-spec record_field(syntaxTree()) -> syntaxTree().
@@ -4295,11 +3944,8 @@ record_field(Name) ->
%% =====================================================================
-%% @spec record_field(Name::syntaxTree(), Value) -> syntaxTree()
-%% Value = none | syntaxTree()
-%%
%% @doc Creates an abstract record field specification. If
-%% <code>Value</code> is <code>none</code>, the result represents
+%% `Value' is `none', the result represents
%% simply "<code><em>Name</em></code>", otherwise it represents
%% "<code><em>Name</em> = <em>Value</em></code>".
%%
@@ -4321,9 +3967,7 @@ record_field(Name, Value) ->
%% =====================================================================
-%% @spec record_field_name(syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the name subtree of a <code>record_field</code> node.
+%% @doc Returns the name subtree of a `record_field' node.
%%
%% @see record_field/2
@@ -4334,13 +3978,11 @@ record_field_name(Node) ->
%% =====================================================================
-%% @spec record_field_value(syntaxTree()) -> none | syntaxTree()
-%%
-%% @doc Returns the value subtree of a <code>record_field</code> node,
-%% if any. If <code>Node</code> represents
-%% "<code><em>Name</em></code>", <code>none</code> is
-%% returned. Otherwise, if <code>Node</code> represents
-%% "<code><em>Name</em> = <em>Value</em></code>", <code>Value</code>
+%% @doc Returns the value subtree of a `record_field' node,
+%% if any. If `Node' represents
+%% "<code><em>Name</em></code>", `none' is
+%% returned. Otherwise, if `Node' represents
+%% "<code><em>Name</em> = <em>Value</em></code>", `Value'
%% is returned.
%%
%% @see record_field/2
@@ -4352,15 +3994,12 @@ record_field_value(Node) ->
%% =====================================================================
-%% @spec record_index_expr(Type::syntaxTree(), Field::syntaxTree()) ->
-%% syntaxTree()
-%%
%% @doc Creates an abstract record field index expression. The result
%% represents "<code>#<em>Type</em>.<em>Field</em></code>".
%%
-%% <p>(Note: the function name <code>record_index/2</code> is reserved
+%% (Note: the function name `record_index/2' is reserved
%% by the Erlang compiler, which is why that name could not be used
-%% for this constructor.)</p>
+%% for this constructor.)
%%
%% @see record_index_expr_type/1
%% @see record_index_expr_field/1
@@ -4399,10 +4038,7 @@ revert_record_index_expr(Node) ->
%% =====================================================================
-%% @spec record_index_expr_type(syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the type subtree of a <code>record_index_expr</code>
-%% node.
+%% @doc Returns the type subtree of a `record_index_expr' node.
%%
%% @see record_index_expr/2
@@ -4418,10 +4054,7 @@ record_index_expr_type(Node) ->
%% =====================================================================
-%% @spec record_index_expr_field(syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the field subtree of a <code>record_index_expr</code>
-%% node.
+%% @doc Returns the field subtree of a `record_index_expr' node.
%%
%% @see record_index_expr/2
@@ -4437,7 +4070,6 @@ record_index_expr_field(Node) ->
%% =====================================================================
-%% @spec record_access(Argument, Field) -> syntaxTree()
%% @equiv record_access(Argument, none, Field)
-spec record_access(syntaxTree(), syntaxTree()) -> syntaxTree().
@@ -4447,24 +4079,19 @@ record_access(Argument, Field) ->
%% =====================================================================
-%% @spec record_access(Argument::syntaxTree(), Type,
-%% Field::syntaxTree()) -> syntaxTree()
-%% Type = none | syntaxTree()
-%%
%% @doc Creates an abstract record field access expression. If
-%% <code>Type</code> is not <code>none</code>, the result represents
+%% `Type' is not `none', the result represents
%% "<code><em>Argument</em>#<em>Type</em>.<em>Field</em></code>".
%%
-%% <p>If <code>Type</code> is <code>none</code>, the result represents
+%% If `Type' is `none', the result represents
%% "<code><em>Argument</em>.<em>Field</em></code>". This is a special
-%% form only allowed within Mnemosyne queries.</p>
+%% form only allowed within Mnemosyne queries.
%%
%% @see record_access/2
%% @see record_access_argument/1
%% @see record_access_type/1
%% @see record_access_field/1
%% @see record_expr/3
-%% @see query_expr/1
-record(record_access, {argument :: syntaxTree(),
type :: 'none' | syntaxTree(),
@@ -4512,10 +4139,7 @@ revert_record_access(Node) ->
%% =====================================================================
-%% @spec record_access_argument(syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the argument subtree of a <code>record_access</code>
-%% node.
+%% @doc Returns the argument subtree of a `record_access' node.
%%
%% @see record_access/3
@@ -4533,14 +4157,12 @@ record_access_argument(Node) ->
%% =====================================================================
-%% @spec record_access_type(syntaxTree()) -> none | syntaxTree()
-%%
-%% @doc Returns the type subtree of a <code>record_access</code> node,
-%% if any. If <code>Node</code> represents
-%% "<code><em>Argument</em>.<em>Field</em></code>", <code>none</code>
-%% is returned, otherwise if <code>Node</code> represents
+%% @doc Returns the type subtree of a `record_access' node,
+%% if any. If `Node' represents
+%% "<code><em>Argument</em>.<em>Field</em></code>", `none'
+%% is returned, otherwise if `Node' represents
%% "<code><em>Argument</em>#<em>Type</em>.<em>Field</em></code>",
-%% <code>Type</code> is returned.
+%% `Type' is returned.
%%
%% @see record_access/3
@@ -4558,10 +4180,7 @@ record_access_type(Node) ->
%% =====================================================================
-%% @spec record_access_field(syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the field subtree of a <code>record_access</code>
-%% node.
+%% @doc Returns the field subtree of a `record_access' node.
%%
%% @see record_access/3
@@ -4579,7 +4198,6 @@ record_access_field(Node) ->
%% =====================================================================
-%% @spec record_expr(Type, Fields) -> syntaxTree()
%% @equiv record_expr(none, Type, Fields)
-spec record_expr(syntaxTree(), [syntaxTree()]) -> syntaxTree().
@@ -4589,13 +4207,9 @@ record_expr(Type, Fields) ->
%% =====================================================================
-%% @spec record_expr(Argument, Type::syntaxTree(),
-%% Fields::[syntaxTree()]) -> syntaxTree()
-%% Argument = none | syntaxTree()
-%%
-%% @doc Creates an abstract record expression. If <code>Fields</code> is
-%% <code>[F1, ..., Fn]</code>, then if <code>Argument</code> is
-%% <code>none</code>, the result represents
+%% @doc Creates an abstract record expression. If `Fields' is
+%% `[F1, ..., Fn]', then if `Argument' is
+%% `none', the result represents
%% "<code>#<em>Type</em>{<em>F1</em>, ..., <em>Fn</em>}</code>",
%% otherwise it represents
%% "<code><em>Argument</em>#<em>Type</em>{<em>F1</em>, ...,
@@ -4661,14 +4275,12 @@ revert_record_expr(Node) ->
%% =====================================================================
-%% @spec record_expr_argument(syntaxTree()) -> none | syntaxTree()
-%%
-%% @doc Returns the argument subtree of a <code>record_expr</code> node,
-%% if any. If <code>Node</code> represents
-%% "<code>#<em>Type</em>{...}</code>", <code>none</code> is returned.
-%% Otherwise, if <code>Node</code> represents
+%% @doc Returns the argument subtree of a `record_expr' node,
+%% if any. If `Node' represents
+%% "<code>#<em>Type</em>{...}</code>", `none' is returned.
+%% Otherwise, if `Node' represents
%% "<code><em>Argument</em>#<em>Type</em>{...}</code>",
-%% <code>Argument</code> is returned.
+%% `Argument' is returned.
%%
%% @see record_expr/3
@@ -4686,9 +4298,7 @@ record_expr_argument(Node) ->
%% =====================================================================
-%% @spec record_expr_type(syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the type subtree of a <code>record_expr</code> node.
+%% @doc Returns the type subtree of a `record_expr' node.
%%
%% @see record_expr/3
@@ -4706,10 +4316,8 @@ record_expr_type(Node) ->
%% =====================================================================
-%% @spec record_expr_fields(syntaxTree()) -> [syntaxTree()]
-%%
%% @doc Returns the list of field subtrees of a
-%% <code>record_expr</code> node.
+%% `record_expr' node.
%%
%% @see record_expr/3
@@ -4727,15 +4335,11 @@ record_expr_fields(Node) ->
%% =====================================================================
-%% @spec application(Module, Function::syntaxTree(),
-%% Arguments::[syntaxTree()]) -> syntaxTree()
-%% Module = none | syntaxTree()
-%%
%% @doc Creates an abstract function application expression. If
-%% <code>Module</code> is <code>none</code>, this is call is equivalent
-%% to <code>application(Function, Arguments)</code>, otherwise it is
-%% equivalent to <code>application(module_qualifier(Module, Function),
-%% Arguments)</code>.
+%% `Module' is `none', this is call is equivalent
+%% to `application(Function, Arguments)', otherwise it is
+%% equivalent to `application(module_qualifier(Module, Function),
+%% Arguments)'.
%%
%% (This is a utility function.)
%%
@@ -4752,11 +4356,8 @@ application(Module, Name, Arguments) ->
%% =====================================================================
-%% @spec application(Operator::syntaxTree(),
-%% Arguments::[syntaxTree()]) -> syntaxTree()
-%%
%% @doc Creates an abstract function application expression. If
-%% <code>Arguments</code> is <code>[A1, ..., An]</code>, the result
+%% `Arguments' is `[A1, ..., An]', the result
%% represents "<code><em>Operator</em>(<em>A1</em>, ...,
%% <em>An</em>)</code>".
%%
@@ -4794,14 +4395,11 @@ revert_application(Node) ->
%% =====================================================================
-%% @spec application_operator(syntaxTree()) -> syntaxTree()
+%% @doc Returns the operator subtree of an `application' node.
%%
-%% @doc Returns the operator subtree of an <code>application</code>
-%% node.
-%%
-%% <p>Note: if <code>Node</code> represents
+%% Note: if `Node' represents
%% "<code><em>M</em>:<em>F</em>(...)</code>", then the result is the
-%% subtree representing "<code><em>M</em>:<em>F</em></code>".</p>
+%% subtree representing "<code><em>M</em>:<em>F</em></code>".
%%
%% @see application/2
%% @see module_qualifier/2
@@ -4818,10 +4416,8 @@ application_operator(Node) ->
%% =====================================================================
-%% @spec application_arguments(syntaxTree()) -> [syntaxTree()]
-%%
%% @doc Returns the list of argument subtrees of an
-%% <code>application</code> node.
+%% `application' node.
%%
%% @see application/2
@@ -4837,11 +4433,8 @@ application_arguments(Node) ->
%% =====================================================================
-%% @spec list_comp(Template::syntaxTree(), Body::[syntaxTree()]) ->
-%% syntaxTree()
-%%
-%% @doc Creates an abstract list comprehension. If <code>Body</code> is
-%% <code>[E1, ..., En]</code>, the result represents
+%% @doc Creates an abstract list comprehension. If `Body' is
+%% `[E1, ..., En]', the result represents
%% "<code>[<em>Template</em> || <em>E1</em>, ..., <em>En</em>]</code>".
%%
%% @see list_comp_template/1
@@ -4876,9 +4469,7 @@ revert_list_comp(Node) ->
%% =====================================================================
-%% @spec list_comp_template(syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the template subtree of a <code>list_comp</code> node.
+%% @doc Returns the template subtree of a `list_comp' node.
%%
%% @see list_comp/2
@@ -4894,10 +4485,7 @@ list_comp_template(Node) ->
%% =====================================================================
-%% @spec list_comp_body(syntaxTree()) -> [syntaxTree()]
-%%
-%% @doc Returns the list of body subtrees of a <code>list_comp</code>
-%% node.
+%% @doc Returns the list of body subtrees of a `list_comp' node.
%%
%% @see list_comp/2
@@ -4912,11 +4500,8 @@ list_comp_body(Node) ->
end.
%% =====================================================================
-%% @spec binary_comp(Template::syntaxTree(), Body::[syntaxTree()]) ->
-%% syntaxTree()
-%%
-%% @doc Creates an abstract binary comprehension. If <code>Body</code> is
-%% <code>[E1, ..., En]</code>, the result represents
+%% @doc Creates an abstract binary comprehension. If `Body' is
+%% `[E1, ..., En]', the result represents
%% "<code>&lt;&lt;<em>Template</em> || <em>E1</em>, ..., <em>En</em>&gt;&gt;</code>".
%%
%% @see binary_comp_template/1
@@ -4951,9 +4536,7 @@ revert_binary_comp(Node) ->
%% =====================================================================
-%% @spec binary_comp_template(syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the template subtree of a <code>binary_comp</code> node.
+%% @doc Returns the template subtree of a `binary_comp' node.
%%
%% @see binary_comp/2
@@ -4969,10 +4552,7 @@ binary_comp_template(Node) ->
%% =====================================================================
-%% @spec binary_comp_body(syntaxTree()) -> [syntaxTree()]
-%%
-%% @doc Returns the list of body subtrees of a <code>binary_comp</code>
-%% node.
+%% @doc Returns the list of body subtrees of a `binary_comp' node.
%%
%% @see binary_comp/2
@@ -4988,61 +4568,10 @@ binary_comp_body(Node) ->
%% =====================================================================
-%% @spec query_expr(Body::syntaxTree()) -> syntaxTree()
-%%
-%% @doc Creates an abstract Mnemosyne query expression. The result
-%% represents "<code>query <em>Body</em> end</code>".
-%%
-%% @see query_expr_body/1
-%% @see record_access/2
-%% @see rule/2
-
-%% type(Node) = query_expr
-%% data(Node) = syntaxTree()
-%%
-%% `erl_parse' representation:
-%%
-%% {'query', Pos, Body}
-%%
-%% Body = erl_parse()
-
--spec query_expr(syntaxTree()) -> syntaxTree().
-
-query_expr(Body) ->
- tree(query_expr, Body).
-
-revert_query_expr(Node) ->
- Pos = get_pos(Node),
- Body = list_comp_body(Node),
- {'query', Pos, Body}.
-
-
-%% =====================================================================
-%% @spec query_expr_body(syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the body subtree of a <code>query_expr</code> node.
-%%
-%% @see query_expr/1
-
--spec query_expr_body(syntaxTree()) -> syntaxTree().
-
-query_expr_body(Node) ->
- case unwrap(Node) of
- {'query', _, Body} ->
- Body;
- Node1 ->
- data(Node1)
- end.
-
-
-%% =====================================================================
-%% @spec rule(Name::syntaxTree(), Clauses::[syntaxTree()]) ->
-%% syntaxTree()
-%%
-%% @doc Creates an abstract Mnemosyne rule. If <code>Clauses</code> is
-%% <code>[C1, ..., Cn]</code>, the results represents
+%% @doc Creates an abstract Mnemosyne rule. If `Clauses' is
+%% `[C1, ..., Cn]', the results represents
%% "<code><em>Name</em> <em>C1</em>; ...; <em>Name</em>
-%% <em>Cn</em>.</code>". More exactly, if each <code>Ci</code>
+%% <em>Cn</em>.</code>". More exactly, if each `Ci'
%% represents "<code>(<em>Pi1</em>, ..., <em>Pim</em>) <em>Gi</em> ->
%% <em>Bi</em></code>", then the result represents
%% "<code><em>Name</em>(<em>P11</em>, ..., <em>P1m</em>) <em>G1</em> :-
@@ -5097,9 +4626,7 @@ revert_rule(Node) ->
%% =====================================================================
-%% @spec rule_name(syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the name subtree of a <code>rule</code> node.
+%% @doc Returns the name subtree of a `rule' node.
%%
%% @see rule/2
@@ -5114,9 +4641,7 @@ rule_name(Node) ->
end.
%% =====================================================================
-%% @spec rule_clauses(syntaxTree()) -> [syntaxTree()]
-%%
-%% @doc Returns the list of clause subtrees of a <code>rule</code> node.
+%% @doc Returns the list of clause subtrees of a `rule' node.
%%
%% @see rule/2
@@ -5131,16 +4656,14 @@ rule_clauses(Node) ->
end.
%% =====================================================================
-%% @spec rule_arity(Node::syntaxTree()) -> integer()
-%%
-%% @doc Returns the arity of a <code>rule</code> node. The result is the
+%% @doc Returns the arity of a `rule' node. The result is the
%% number of parameter patterns in the first clause of the rule;
%% subsequent clauses are ignored.
%%
-%% <p>An exception is thrown if <code>rule_clauses(Node)</code> returns
+%% An exception is thrown if `rule_clauses(Node)' returns
%% an empty list, or if the first element of that list is not a syntax
-%% tree <code>C</code> of type <code>clause</code> such that
-%% <code>clause_patterns(C)</code> is a nonempty list.</p>
+%% tree `C' of type `clause' such that
+%% `clause_patterns(C)' is a nonempty list.
%%
%% @see rule/2
%% @see rule_clauses/1
@@ -5156,9 +4679,6 @@ rule_arity(Node) ->
%% =====================================================================
-%% @spec generator(Pattern::syntaxTree(), Body::syntaxTree()) ->
-%% syntaxTree()
-%%
%% @doc Creates an abstract generator. The result represents
%% "<code><em>Pattern</em> &lt;- <em>Body</em></code>".
%%
@@ -5193,9 +4713,7 @@ revert_generator(Node) ->
%% =====================================================================
-%% @spec generator_pattern(syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the pattern subtree of a <code>generator</code> node.
+%% @doc Returns the pattern subtree of a `generator' node.
%%
%% @see generator/2
@@ -5211,9 +4729,7 @@ generator_pattern(Node) ->
%% =====================================================================
-%% @spec generator_body(syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the body subtree of a <code>generator</code> node.
+%% @doc Returns the body subtree of a `generator' node.
%%
%% @see generator/2
@@ -5229,9 +4745,6 @@ generator_body(Node) ->
%% =====================================================================
-%% @spec binary_generator(Pattern::syntaxTree(), Body::syntaxTree()) ->
-%% syntaxTree()
-%%
%% @doc Creates an abstract binary_generator. The result represents
%% "<code><em>Pattern</em> &lt;- <em>Body</em></code>".
%%
@@ -5266,9 +4779,7 @@ revert_binary_generator(Node) ->
%% =====================================================================
-%% @spec binary_generator_pattern(syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the pattern subtree of a <code>generator</code> node.
+%% @doc Returns the pattern subtree of a `generator' node.
%%
%% @see binary_generator/2
@@ -5284,9 +4795,7 @@ binary_generator_pattern(Node) ->
%% =====================================================================
-%% @spec binary_generator_body(syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the body subtree of a <code>generator</code> node.
+%% @doc Returns the body subtree of a `generator' node.
%%
%% @see binary_generator/2
@@ -5302,10 +4811,8 @@ binary_generator_body(Node) ->
%% =====================================================================
-%% @spec block_expr(Body::[syntaxTree()]) -> syntaxTree()
-%%
-%% @doc Creates an abstract block expression. If <code>Body</code> is
-%% <code>[B1, ..., Bn]</code>, the result represents "<code>begin
+%% @doc Creates an abstract block expression. If `Body' is
+%% `[B1, ..., Bn]', the result represents "<code>begin
%% <em>B1</em>, ..., <em>Bn</em> end</code>".
%%
%% @see block_expr_body/1
@@ -5321,7 +4828,7 @@ binary_generator_body(Node) ->
%%
%% Body = [erl_parse()] \ []
--spec block_expr(Body::[syntaxTree()]) -> syntaxTree().
+-spec block_expr([syntaxTree()]) -> syntaxTree().
block_expr(Body) ->
tree(block_expr, Body).
@@ -5333,10 +4840,7 @@ revert_block_expr(Node) ->
%% =====================================================================
-%% @spec block_expr_body(syntaxTree()) -> [syntaxTree()]
-%%
-%% @doc Returns the list of body subtrees of a <code>block_expr</code>
-%% node.
+%% @doc Returns the list of body subtrees of a `block_expr' node.
%%
%% @see block_expr/1
@@ -5352,12 +4856,10 @@ block_expr_body(Node) ->
%% =====================================================================
-%% @spec if_expr(Clauses::[syntaxTree()]) -> syntaxTree()
-%%
-%% @doc Creates an abstract if-expression. If <code>Clauses</code> is
-%% <code>[C1, ..., Cn]</code>, the result represents "<code>if
+%% @doc Creates an abstract if-expression. If `Clauses' is
+%% `[C1, ..., Cn]', the result represents "<code>if
%% <em>C1</em>; ...; <em>Cn</em> end</code>". More exactly, if each
-%% <code>Ci</code> represents "<code>() <em>Gi</em> ->
+%% `Ci' represents "<code>() <em>Gi</em> ->
%% <em>Bi</em></code>", then the result represents "<code>if
%% <em>G1</em> -> <em>B1</em>; ...; <em>Gn</em> -> <em>Bn</em>
%% end</code>".
@@ -5392,10 +4894,7 @@ revert_if_expr(Node) ->
%% =====================================================================
-%% @spec if_expr_clauses(syntaxTree()) -> [syntaxTree()]
-%%
-%% @doc Returns the list of clause subtrees of an <code>if_expr</code>
-%% node.
+%% @doc Returns the list of clause subtrees of an `if_expr' node.
%%
%% @see if_expr/1
@@ -5411,13 +4910,10 @@ if_expr_clauses(Node) ->
%% =====================================================================
-%% @spec case_expr(Argument::syntaxTree(), Clauses::[syntaxTree()]) ->
-%% syntaxTree()
-%%
-%% @doc Creates an abstract case-expression. If <code>Clauses</code> is
-%% <code>[C1, ..., Cn]</code>, the result represents "<code>case
+%% @doc Creates an abstract case-expression. If `Clauses' is
+%% `[C1, ..., Cn]', the result represents "<code>case
%% <em>Argument</em> of <em>C1</em>; ...; <em>Cn</em> end</code>". More
-%% exactly, if each <code>Ci</code> represents "<code>(<em>Pi</em>)
+%% exactly, if each `Ci' represents "<code>(<em>Pi</em>)
%% <em>Gi</em> -> <em>Bi</em></code>", then the result represents
%% "<code>case <em>Argument</em> of <em>P1</em> <em>G1</em> ->
%% <em>B1</em>; ...; <em>Pn</em> <em>Gn</em> -> <em>Bn</em> end</code>".
@@ -5461,9 +4957,7 @@ revert_case_expr(Node) ->
%% =====================================================================
-%% @spec case_expr_argument(syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the argument subtree of a <code>case_expr</code> node.
+%% @doc Returns the argument subtree of a `case_expr' node.
%%
%% @see case_expr/2
@@ -5479,10 +4973,7 @@ case_expr_argument(Node) ->
%% =====================================================================
-%% @spec case_expr_clauses(syntaxTree()) -> [syntaxTree()]
-%%
-%% @doc Returns the list of clause subtrees of a <code>case_expr</code>
-%% node.
+%% @doc Returns the list of clause subtrees of a `case_expr' node.
%%
%% @see case_expr/2
@@ -5498,12 +4989,10 @@ case_expr_clauses(Node) ->
%% =====================================================================
-%% @spec cond_expr(Clauses::[syntaxTree()]) -> syntaxTree()
-%%
-%% @doc Creates an abstract cond-expression. If <code>Clauses</code> is
-%% <code>[C1, ..., Cn]</code>, the result represents "<code>cond
+%% @doc Creates an abstract cond-expression. If `Clauses' is
+%% `[C1, ..., Cn]', the result represents "<code>cond
%% <em>C1</em>; ...; <em>Cn</em> end</code>". More exactly, if each
-%% <code>Ci</code> represents "<code>() <em>Ei</em> ->
+%% `Ci' represents "<code>() <em>Ei</em> ->
%% <em>Bi</em></code>", then the result represents "<code>cond
%% <em>E1</em> -> <em>B1</em>; ...; <em>En</em> -> <em>Bn</em>
%% end</code>".
@@ -5538,10 +5027,7 @@ revert_cond_expr(Node) ->
%% =====================================================================
-%% @spec cond_expr_clauses(syntaxTree()) -> [syntaxTree()]
-%%
-%% @doc Returns the list of clause subtrees of a <code>cond_expr</code>
-%% node.
+%% @doc Returns the list of clause subtrees of a `cond_expr' node.
%%
%% @see cond_expr/1
@@ -5557,7 +5043,6 @@ cond_expr_clauses(Node) ->
%% =====================================================================
-%% @spec receive_expr(Clauses) -> syntaxTree()
%% @equiv receive_expr(Clauses, none, [])
-spec receive_expr([syntaxTree()]) -> syntaxTree().
@@ -5567,25 +5052,21 @@ receive_expr(Clauses) ->
%% =====================================================================
-%% @spec receive_expr(Clauses::[syntaxTree()], Timeout,
-%% Action::[syntaxTree()]) -> syntaxTree()
-%% Timeout = none | syntaxTree()
-%%
-%% @doc Creates an abstract receive-expression. If <code>Timeout</code>
-%% is <code>none</code>, the result represents "<code>receive
-%% <em>C1</em>; ...; <em>Cn</em> end</code>" (the <code>Action</code>
-%% argument is ignored). Otherwise, if <code>Clauses</code> is
-%% <code>[C1, ..., Cn]</code> and <code>Action</code> is <code>[A1, ...,
-%% Am]</code>, the result represents "<code>receive <em>C1</em>; ...;
+%% @doc Creates an abstract receive-expression. If `Timeout'
+%% is `none', the result represents "<code>receive
+%% <em>C1</em>; ...; <em>Cn</em> end</code>" (the `Action'
+%% argument is ignored). Otherwise, if `Clauses' is
+%% `[C1, ..., Cn]' and `Action' is `[A1, ...,
+%% Am]', the result represents "<code>receive <em>C1</em>; ...;
%% <em>Cn</em> after <em>Timeout</em> -> <em>A1</em>, ..., <em>Am</em>
-%% end</code>". More exactly, if each <code>Ci</code> represents
+%% end</code>". More exactly, if each `Ci' represents
%% "<code>(<em>Pi</em>) <em>Gi</em> -> <em>Bi</em></code>", then the
%% result represents "<code>receive <em>P1</em> <em>G1</em> ->
%% <em>B1</em>; ...; <em>Pn</em> <em>Gn</em> -> <em>Bn</em> ...
%% end</code>".
%%
-%% <p>Note that in Erlang, a receive-expression must have at least one
-%% clause if no timeout part is specified.</p>
+%% Note that in Erlang, a receive-expression must have at least one
+%% clause if no timeout part is specified.
%%
%% @see receive_expr_clauses/1
%% @see receive_expr_timeout/1
@@ -5649,11 +5130,8 @@ revert_receive_expr(Node) ->
%% =====================================================================
-%% @spec receive_expr_clauses(syntaxTree()) -> [syntaxTree()]
-%% type(Node) = receive_expr
-%%
%% @doc Returns the list of clause subtrees of a
-%% <code>receive_expr</code> node.
+%% `receive_expr' node.
%%
%% @see receive_expr/3
@@ -5671,15 +5149,12 @@ receive_expr_clauses(Node) ->
%% =====================================================================
-%% @spec receive_expr_timeout(Node::syntaxTree()) -> Timeout
-%% Timeout = none | syntaxTree()
-%%
-%% @doc Returns the timeout subtree of a <code>receive_expr</code> node,
-%% if any. If <code>Node</code> represents "<code>receive <em>C1</em>;
-%% ...; <em>Cn</em> end</code>", <code>none</code> is returned.
-%% Otherwise, if <code>Node</code> represents "<code>receive
+%% @doc Returns the timeout subtree of a `receive_expr' node,
+%% if any. If `Node' represents "<code>receive <em>C1</em>;
+%% ...; <em>Cn</em> end</code>", `none' is returned.
+%% Otherwise, if `Node' represents "<code>receive
%% <em>C1</em>; ...; <em>Cn</em> after <em>Timeout</em> -> ... end</code>",
-%% <code>Timeout</code> is returned.
+%% `Timeout' is returned.
%%
%% @see receive_expr/3
@@ -5697,10 +5172,8 @@ receive_expr_timeout(Node) ->
%% =====================================================================
-%% @spec receive_expr_action(Node::syntaxTree()) -> [syntaxTree()]
-%%
%% @doc Returns the list of action body subtrees of a
-%% <code>receive_expr</code> node. If <code>Node</code> represents
+%% `receive_expr' node. If `Node' represents
%% "<code>receive <em>C1</em>; ...; <em>Cn</em> end</code>", this is the
%% empty list.
%%
@@ -5720,8 +5193,6 @@ receive_expr_action(Node) ->
%% =====================================================================
-%% @spec try_expr(Body::syntaxTree(), Handlers::[syntaxTree()]) ->
-%% syntaxTree()
%% @equiv try_expr(Body, [], Handlers)
-spec try_expr([syntaxTree()], [syntaxTree()]) -> syntaxTree().
@@ -5731,8 +5202,6 @@ try_expr(Body, Handlers) ->
%% =====================================================================
-%% @spec try_expr(Body::syntaxTree(), Clauses::[syntaxTree()],
-%% Handlers::[syntaxTree()]) -> syntaxTree()
%% @equiv try_expr(Body, Clauses, Handlers, [])
-spec try_expr([syntaxTree()], [syntaxTree()], [syntaxTree()]) -> syntaxTree().
@@ -5742,8 +5211,6 @@ try_expr(Body, Clauses, Handlers) ->
%% =====================================================================
-%% @spec try_after_expr(Body::syntaxTree(), After::[syntaxTree()]) ->
-%% syntaxTree()
%% @equiv try_expr(Body, [], [], After)
-spec try_after_expr([syntaxTree()], [syntaxTree()]) -> syntaxTree().
@@ -5753,30 +5220,26 @@ try_after_expr(Body, After) ->
%% =====================================================================
-%% @spec try_expr(Body::[syntaxTree()], Clauses::[syntaxTree()],
-%% Handlers::[syntaxTree()], After::[syntaxTree()]) ->
-%% syntaxTree()
-%%
-%% @doc Creates an abstract try-expression. If <code>Body</code> is
-%% <code>[B1, ..., Bn]</code>, <code>Clauses</code> is <code>[C1, ...,
-%% Cj]</code>, <code>Handlers</code> is <code>[H1, ..., Hk]</code>, and
-%% <code>After</code> is <code>[A1, ..., Am]</code>, the result
+%% @doc Creates an abstract try-expression. If `Body' is
+%% `[B1, ..., Bn]', `Clauses' is `[C1, ...,
+%% Cj]', `Handlers' is `[H1, ..., Hk]', and
+%% `After' is `[A1, ..., Am]', the result
%% represents "<code>try <em>B1</em>, ..., <em>Bn</em> of <em>C1</em>;
%% ...; <em>Cj</em> catch <em>H1</em>; ...; <em>Hk</em> after
%% <em>A1</em>, ..., <em>Am</em> end</code>". More exactly, if each
-%% <code>Ci</code> represents "<code>(<em>CPi</em>) <em>CGi</em> ->
-%% <em>CBi</em></code>", and each <code>Hi</code> represents
+%% `Ci' represents "<code>(<em>CPi</em>) <em>CGi</em> ->
+%% <em>CBi</em></code>", and each `Hi' represents
%% "<code>(<em>HPi</em>) <em>HGi</em> -> <em>HBi</em></code>", then the
%% result represents "<code>try <em>B1</em>, ..., <em>Bn</em> of
%% <em>CP1</em> <em>CG1</em> -> <em>CB1</em>; ...; <em>CPj</em>
%% <em>CGj</em> -> <em>CBj</em> catch <em>HP1</em> <em>HG1</em> ->
%% <em>HB1</em>; ...; <em>HPk</em> <em>HGk</em> -> <em>HBk</em> after
-%% <em>A1</em>, ..., <em>Am</em> end</code>"; cf.
-%% <code>case_expr/2</code>. If <code>Clauses</code> is the empty list,
-%% the <code>of ...</code> section is left out. If <code>After</code> is
-%% the empty list, the <code>after ...</code> section is left out. If
-%% <code>Handlers</code> is the empty list, and <code>After</code> is
-%% nonempty, the <code>catch ...</code> section is left out.
+%% <em>A1</em>, ..., <em>Am</em> end</code>"; see
+%% {@link case_expr/2}. If `Clauses' is the empty list,
+%% the `of ...' section is left out. If `After' is
+%% the empty list, the `after ...' section is left out. If
+%% `Handlers' is the empty list, and `After' is
+%% nonempty, the `catch ...' section is left out.
%%
%% @see try_expr_body/1
%% @see try_expr_clauses/1
@@ -5834,10 +5297,7 @@ revert_try_expr(Node) ->
%% =====================================================================
-%% @spec try_expr_body(syntaxTree()) -> [syntaxTree()]
-%%
-%% @doc Returns the list of body subtrees of a <code>try_expr</code>
-%% node.
+%% @doc Returns the list of body subtrees of a `try_expr' node.
%%
%% @see try_expr/4
@@ -5853,10 +5313,8 @@ try_expr_body(Node) ->
%% =====================================================================
-%% @spec try_expr_clauses(Node::syntaxTree()) -> [syntaxTree()]
-%%
%% @doc Returns the list of case-clause subtrees of a
-%% <code>try_expr</code> node. If <code>Node</code> represents
+%% `try_expr' node. If `Node' represents
%% "<code>try <em>Body</em> catch <em>H1</em>; ...; <em>Hn</em>
%% end</code>", the result is the empty list.
%%
@@ -5874,10 +5332,8 @@ try_expr_clauses(Node) ->
%% =====================================================================
-%% @spec try_expr_handlers(syntaxTree()) -> [syntaxTree()]
-%%
%% @doc Returns the list of handler-clause subtrees of a
-%% <code>try_expr</code> node.
+%% `try_expr' node.
%%
%% @see try_expr/4
@@ -5893,10 +5349,7 @@ try_expr_handlers(Node) ->
%% =====================================================================
-%% @spec try_expr_after(syntaxTree()) -> [syntaxTree()]
-%%
-%% @doc Returns the list of "after" subtrees of a <code>try_expr</code>
-%% node.
+%% @doc Returns the list of "after" subtrees of a `try_expr' node.
%%
%% @see try_expr/4
@@ -5912,9 +5365,6 @@ try_expr_after(Node) ->
%% =====================================================================
-%% @spec class_qualifier(Class::syntaxTree(), Body::syntaxTree()) ->
-%% syntaxTree()
-%%
%% @doc Creates an abstract class qualifier. The result represents
%% "<code><em>Class</em>:<em>Body</em></code>".
%%
@@ -5937,10 +5387,8 @@ class_qualifier(Class, Body) ->
%% =====================================================================
-%% @spec class_qualifier_argument(syntaxTree()) -> syntaxTree()
-%%
%% @doc Returns the argument (the class) subtree of a
-%% <code>class_qualifier</code> node.
+%% `class_qualifier' node.
%%
%% @see class_qualifier/2
@@ -5951,9 +5399,7 @@ class_qualifier_argument(Node) ->
%% =====================================================================
-%% @spec class_qualifier_body(syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the body subtree of a <code>class_qualifier</code> node.
+%% @doc Returns the body subtree of a `class_qualifier' node.
%%
%% @see class_qualifier/2
@@ -5964,13 +5410,10 @@ class_qualifier_body(Node) ->
%% =====================================================================
-%% @spec implicit_fun(Name::syntaxTree(), Arity::syntaxTree()) ->
-%% syntaxTree()
-%%
%% @doc Creates an abstract "implicit fun" expression. If
-%% <code>Arity</code> is <code>none</code>, this is equivalent to
-%% <code>implicit_fun(Name)</code>, otherwise it is equivalent to
-%% <code>implicit_fun(arity_qualifier(Name, Arity))</code>.
+%% `Arity' is `none', this is equivalent to
+%% `implicit_fun(Name)', otherwise it is equivalent to
+%% `implicit_fun(arity_qualifier(Name, Arity))'.
%%
%% (This is a utility function.)
%%
@@ -5986,14 +5429,11 @@ implicit_fun(Name, Arity) ->
%% =====================================================================
-%% @spec implicit_fun(Module::syntaxTree(), Name::syntaxTree(),
-%% Arity::syntaxTree()) -> syntaxTree()
-%%
%% @doc Creates an abstract module-qualified "implicit fun" expression.
-%% If <code>Module</code> is <code>none</code>, this is equivalent to
-%% <code>implicit_fun(Name, Arity)</code>, otherwise it is equivalent to
-%% <code>implicit_fun(module_qualifier(Module, arity_qualifier(Name,
-%% Arity))</code>.
+%% If `Module' is `none', this is equivalent to
+%% `implicit_fun(Name, Arity)', otherwise it is equivalent to
+%% `implicit_fun(module_qualifier(Module, arity_qualifier(Name,
+%% Arity))'.
%%
%% (This is a utility function.)
%%
@@ -6010,10 +5450,8 @@ implicit_fun(Module, Name, Arity) ->
%% =====================================================================
-%% @spec implicit_fun(Name::syntaxTree()) -> syntaxTree()
-%%
%% @doc Creates an abstract "implicit fun" expression. The result
-%% represents "<code>fun <em>Name</em></code>". <code>Name</code> should
+%% represents "<code>fun <em>Name</em></code>". `Name' should
%% represent either <code><em>F</em>/<em>A</em></code> or
%% <code><em>M</em>:<em>F</em>/<em>A</em></code>
%%
@@ -6072,15 +5510,13 @@ revert_implicit_fun(Node) ->
%% =====================================================================
-%% @spec implicit_fun_name(Node::syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the name subtree of an <code>implicit_fun</code> node.
+%% @doc Returns the name subtree of an `implicit_fun' node.
%%
-%% <p>Note: if <code>Node</code> represents "<code>fun
+%% Note: if `Node' represents "<code>fun
%% <em>N</em>/<em>A</em></code>" or "<code>fun
%% <em>M</em>:<em>N</em>/<em>A</em></code>", then the result is the
%% subtree representing "<code><em>N</em>/<em>A</em></code>" or
-%% "<code><em>M</em>:<em>N</em>/<em>A</em></code>", respectively.</p>
+%% "<code><em>M</em>:<em>N</em>/<em>A</em></code>", respectively.
%%
%% @see implicit_fun/1
%% @see arity_qualifier/2
@@ -6110,12 +5546,10 @@ implicit_fun_name(Node) ->
%% =====================================================================
-%% @spec fun_expr(Clauses::[syntaxTree()]) -> syntaxTree()
-%%
-%% @doc Creates an abstract fun-expression. If <code>Clauses</code> is
-%% <code>[C1, ..., Cn]</code>, the result represents "<code>fun
+%% @doc Creates an abstract fun-expression. If `Clauses' is
+%% `[C1, ..., Cn]', the result represents "<code>fun
%% <em>C1</em>; ...; <em>Cn</em> end</code>". More exactly, if each
-%% <code>Ci</code> represents "<code>(<em>Pi1</em>, ..., <em>Pim</em>)
+%% `Ci' represents "<code>(<em>Pi1</em>, ..., <em>Pim</em>)
%% <em>Gi</em> -> <em>Bi</em></code>", then the result represents
%% "<code>fun (<em>P11</em>, ..., <em>P1m</em>) <em>G1</em> ->
%% <em>B1</em>; ...; (<em>Pn1</em>, ..., <em>Pnm</em>) <em>Gn</em> ->
@@ -6152,10 +5586,7 @@ revert_fun_expr(Node) ->
%% =====================================================================
-%% @spec fun_expr_clauses(syntaxTree()) -> [syntaxTree()]
-%%
-%% @doc Returns the list of clause subtrees of a <code>fun_expr</code>
-%% node.
+%% @doc Returns the list of clause subtrees of a `fun_expr' node.
%%
%% @see fun_expr/1
@@ -6171,16 +5602,14 @@ fun_expr_clauses(Node) ->
%% =====================================================================
-%% @spec fun_expr_arity(syntaxTree()) -> integer()
-%%
-%% @doc Returns the arity of a <code>fun_expr</code> node. The result is
+%% @doc Returns the arity of a `fun_expr' node. The result is
%% the number of parameter patterns in the first clause of the
%% fun-expression; subsequent clauses are ignored.
%%
-%% <p>An exception is thrown if <code>fun_expr_clauses(Node)</code>
+%% An exception is thrown if `fun_expr_clauses(Node)'
%% returns an empty list, or if the first element of that list is not a
-%% syntax tree <code>C</code> of type <code>clause</code> such that
-%% <code>clause_patterns(C)</code> is a nonempty list.</p>
+%% syntax tree `C' of type `clause' such that
+%% `clause_patterns(C)' is a nonempty list.
%%
%% @see fun_expr/1
%% @see fun_expr_clauses/1
@@ -6194,8 +5623,6 @@ fun_expr_arity(Node) ->
%% =====================================================================
-%% @spec parentheses(Body::syntaxTree()) -> syntaxTree()
-%%
%% @doc Creates an abstract parenthesised expression. The result
%% represents "<code>(<em>Body</em>)</code>", independently of the
%% context.
@@ -6215,9 +5642,7 @@ revert_parentheses(Node) ->
%% =====================================================================
-%% @spec parentheses_body(syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the body subtree of a <code>parentheses</code> node.
+%% @doc Returns the body subtree of a `parentheses' node.
%%
%% @see parentheses/1
@@ -6228,7 +5653,6 @@ parentheses_body(Node) ->
%% =====================================================================
-%% @spec macro(Name) -> syntaxTree()
%% @equiv macro(Name, none)
-spec macro(syntaxTree()) -> syntaxTree().
@@ -6238,25 +5662,22 @@ macro(Name) ->
%% =====================================================================
-%% @spec macro(Name::syntaxTree(), Arguments) -> syntaxTree()
-%% Arguments = none | [syntaxTree()]
-%%
-%% @doc Creates an abstract macro application. If <code>Arguments</code>
-%% is <code>none</code>, the result represents
-%% "<code>?<em>Name</em></code>", otherwise, if <code>Arguments</code>
-%% is <code>[A1, ..., An]</code>, the result represents
+%% @doc Creates an abstract macro application. If `Arguments'
+%% is `none', the result represents
+%% "<code>?<em>Name</em></code>", otherwise, if `Arguments'
+%% is `[A1, ..., An]', the result represents
%% "<code>?<em>Name</em>(<em>A1</em>, ..., <em>An</em>)</code>".
%%
-%% <p>Notes: if <code>Arguments</code> is the empty list, the result
+%% Notes: if `Arguments' is the empty list, the result
%% will thus represent "<code>?<em>Name</em>()</code>", including a pair
-%% of matching parentheses.</p>
+%% of matching parentheses.
%%
-%% <p>The only syntactical limitation imposed by the preprocessor on the
+%% The only syntactical limitation imposed by the preprocessor on the
%% arguments to a macro application (viewed as sequences of tokens) is
%% that they must be balanced with respect to parentheses, brackets,
-%% <code>begin ... end</code>, <code>case ... end</code>, etc. The
-%% <code>text</code> node type can be used to represent arguments which
-%% are not regular Erlang constructs.</p>
+%% `begin ... end', `case ... end', etc. The
+%% `text' node type can be used to represent arguments which
+%% are not regular Erlang constructs.
%%
%% @see macro_name/1
%% @see macro_arguments/1
@@ -6278,9 +5699,7 @@ macro(Name, Arguments) ->
%% =====================================================================
-%% @spec macro_name(syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the name subtree of a <code>macro</code> node.
+%% @doc Returns the name subtree of a `macro' node.
%%
%% @see macro/2
@@ -6291,14 +5710,12 @@ macro_name(Node) ->
%% =====================================================================
-%% @spec macro_arguments(Node::syntaxTree()) -> none | [syntaxTree()]
-%%
-%% @doc Returns the list of argument subtrees of a <code>macro</code>
-%% node, if any. If <code>Node</code> represents
-%% "<code>?<em>Name</em></code>", <code>none</code> is returned.
-%% Otherwise, if <code>Node</code> represents
+%% @doc Returns the list of argument subtrees of a `macro'
+%% node, if any. If `Node' represents
+%% "<code>?<em>Name</em></code>", `none' is returned.
+%% Otherwise, if `Node' represents
%% "<code>?<em>Name</em>(<em>A1</em>, ..., <em>An</em>)</code>",
-%% <code>[A1, ..., An]</code> is returned.
+%% `[A1, ..., An]' is returned.
%%
%% @see macro/2
@@ -6309,15 +5726,13 @@ macro_arguments(Node) ->
%% =====================================================================
-%% @spec abstract(Term::term()) -> syntaxTree()
-%%
%% @doc Returns the syntax tree corresponding to an Erlang term.
-%% <code>Term</code> must be a literal term, i.e., one that can be
+%% `Term' 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. The function recognises printable strings, in order to get a
%% compact and readable representation. Evaluation fails with reason
-%% <code>badarg</code> if <code>Term</code> is not a literal term.
+%% `badarg' if `Term' is not a literal term.
%%
%% @see concrete/1
%% @see is_literal/1
@@ -6367,19 +5782,17 @@ abstract_tail(H, T) ->
%% =====================================================================
-%% @spec concrete(Node::syntaxTree()) -> term()
-%%
%% @doc Returns the Erlang term represented by a syntax tree. Evaluation
-%% fails with reason <code>badarg</code> if <code>Node</code> does not
+%% fails with reason `badarg' if `Node' does not
%% represent a literal term.
%%
-%% <p>Note: Currently, the set of syntax trees which have a concrete
+%% Note: Currently, the set of syntax trees which have a concrete
%% representation is larger than the set of trees which can be built
-%% using the function <code>abstract/1</code>. An abstract character
-%% will be concretised as an integer, while <code>abstract/1</code> does
+%% using the function {@link abstract/1}. An abstract character
+%% will be concretised as an integer, while {@link abstract/1} does
%% not at present yield an abstract character for any input. (Use the
-%% <code>char/1</code> function to explicitly create an abstract
-%% character.)</p>
+%% {@link char/1} function to explicitly create an abstract
+%% character.)
%%
%% @see abstract/1
%% @see is_literal/1
@@ -6422,7 +5835,7 @@ concrete(Node) ->
{value, concrete(F), []}
end, [], true),
B;
- _ ->
+ _ ->
erlang:error({badarg, Node})
end.
@@ -6433,12 +5846,10 @@ concrete_list([]) ->
%% =====================================================================
-%% @spec is_literal(Node::syntaxTree()) -> 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.
+%% @doc Returns `true' if `Node' represents a
+%% literal term, otherwise `false'. This function returns
+%% `true' if and only if the value of
+%% `concrete(Node)' is defined.
%%
%% @see abstract/1
%% @see concrete/1
@@ -6469,21 +5880,19 @@ is_literal(T) ->
%% =====================================================================
-%% @spec revert(Tree::syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns an <code>erl_parse</code>-compatible representation of a
-%% syntax tree, if possible. If <code>Tree</code> represents a
+%% @doc Returns an `erl_parse'-compatible representation of a
+%% syntax tree, if possible. If `Tree' represents a
%% well-formed Erlang program or expression, the conversion should work
-%% without problems. Typically, <code>is_tree/1</code> yields
-%% <code>true</code> if conversion failed (i.e., the result is still an
-%% abstract syntax tree), and <code>false</code> otherwise.
+%% without problems. Typically, {@link is_tree/1} yields
+%% `true' if conversion failed (i.e., the result is still an
+%% abstract syntax tree), and `false' otherwise.
%%
-%% <p>The <code>is_tree/1</code> test is not completely foolproof. For a
-%% few special node types (e.g. <code>arity_qualifier</code>), if such a
+%% The {@link is_tree/1} test is not completely foolproof. For a
+%% few special node types (e.g. `arity_qualifier'), if such a
%% node occurs in a context where it is not expected, it will be left
%% unchanged as a non-reverted subtree of the result. This can only
-%% happen if <code>Tree</code> does not actually represent legal Erlang
-%% code.</p>
+%% happen if `Tree' does not actually represent legal Erlang
+%% code.
%%
%% @see revert_forms/1
%% @see //stdlib/erl_parse
@@ -6582,10 +5991,6 @@ revert_root(Node) ->
revert_parentheses(Node);
prefix_expr ->
revert_prefix_expr(Node);
- qualified_name ->
- revert_qualified_name(Node);
- query_expr ->
- revert_query_expr(Node);
receive_expr ->
revert_receive_expr(Node);
record_access ->
@@ -6615,16 +6020,12 @@ revert_root(Node) ->
%% =====================================================================
-%% @spec revert_forms(Forms) -> [erl_parse()]
-%%
-%% Forms = syntaxTree() | [syntaxTree()]
-%%
%% @doc Reverts a sequence of Erlang source code forms. The sequence can
-%% be given either as a <code>form_list</code> syntax tree (possibly
+%% be given either as a `form_list' syntax tree (possibly
%% nested), or as a list of "program form" syntax trees. If successful,
-%% the corresponding flat list of <code>erl_parse</code>-compatible
-%% syntax trees is returned (cf. <code>revert/1</code>). If some program
-%% form could not be reverted, <code>{error, Form}</code> is thrown.
+%% the corresponding flat list of `erl_parse'-compatible
+%% syntax trees is returned (see {@link revert/1}). If some program
+%% form could not be reverted, `{error, Form}' is thrown.
%% Standalone comments in the form sequence are discarded.
%%
%% @see revert/1
@@ -6633,10 +6034,10 @@ revert_root(Node) ->
-type forms() :: syntaxTree() | [syntaxTree()].
-%% -spec revert_forms(forms()) -> [erl_parse()].
+-spec revert_forms(forms()) -> [erl_parse()].
-revert_forms(L) when is_list(L) ->
- revert_forms(form_list(L));
+revert_forms(Forms) when is_list(Forms) ->
+ revert_forms(form_list(Forms));
revert_forms(T) ->
case type(T) of
form_list ->
@@ -6673,60 +6074,54 @@ revert_forms_1([]) ->
%% =====================================================================
-%% @spec subtrees(Node::syntaxTree()) -> [[syntaxTree()]]
-%%
%% @doc Returns the grouped list of all subtrees of a syntax tree. If
-%% <code>Node</code> is a leaf node (cf. <code>is_leaf/1</code>), this
+%% `Node' is a leaf node (see {@link is_leaf/1}), this
%% is the empty list, otherwise the result is always a nonempty list,
-%% containing the lists of subtrees of <code>Node</code>, in
+%% containing the lists of subtrees of `Node', 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
+%% Depending on the type of `Node', 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>
+%% without notice.
%%
-%% <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
+%% The function {@link subtrees/1} and the constructor functions
+%% {@link make_tree/2} and {@link update_tree/2} 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>
+%% can be left to a default case.
%%
-%% <p>For example:
-%% <pre>
-%% postorder(F, Tree) ->
+%% For example:
+%% ```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
+%% end).'''
+%% maps the function `F' on `Tree' and all its
%% subtrees, doing a post-order traversal of the syntax tree. (Note the
-%% use of <code>update_tree/2</code> to preserve node attributes.) For a
+%% use of {@link update_tree/2} to preserve node attributes.) For a
%% simple function like:
-%% <pre>
-%% f(Node) ->
+%% ```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
+%% end.'''
+%% the call `postorder(fun f/1, Tree)' will yield a new
+%% representation of `Tree' in which all atom names have been
%% extended with the prefix "a_", but nothing else (including comments,
-%% annotations and line numbers) has been changed.</p>
+%% annotations and line numbers) has been changed.
%%
%% @see make_tree/2
%% @see type/1
@@ -6836,10 +6231,6 @@ subtrees(T) ->
prefix_expr ->
[[prefix_expr_operator(T)],
[prefix_expr_argument(T)]];
- qualified_name ->
- [qualified_name_segments(T)];
- query_expr ->
- [[query_expr_body(T)]];
receive_expr ->
case receive_expr_timeout(T) of
none ->
@@ -6896,12 +6287,9 @@ subtrees(T) ->
%% =====================================================================
-%% @spec update_tree(Node::syntaxTree(), Groups::[[syntaxTree()]]) ->
-%% syntaxTree()
-%%
%% @doc Creates a syntax tree with the same type and attributes as the
-%% given tree. This is equivalent to <code>copy_attrs(Node,
-%% make_tree(type(Node), Groups))</code>.
+%% given tree. This is equivalent to `copy_attrs(Node,
+%% make_tree(type(Node), Groups))'.
%%
%% @see make_tree/2
%% @see copy_attrs/2
@@ -6914,23 +6302,20 @@ update_tree(Node, Groups) ->
%% =====================================================================
-%% @spec make_tree(Type::atom(), Groups::[[syntaxTree()]]) ->
-%% syntaxTree()
-%%
%% @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
+%% `Type' must be a node type name (see {@link type/1})
+%% that does not denote a leaf node type (see {@link is_leaf/1}).
+%% `Groups' 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>.
+%% text, grouped by category as done by {@link subtrees/1}.
%%
-%% <p>The result of <code>copy_attrs(Node, make_tree(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
+%% The result of `copy_attrs(Node, make_tree(type(Node),
+%% subtrees(Node)))' (see {@link update_tree/2}) represents
+%% the same source code text as the original `Node', assuming
+%% that `subtrees(Node)' yields a nonempty list. However, it
%% does not necessarily have the same data representation as
-%% <code>Node</code>.</p>
+%% `Node'.
%%
%% @see update_tree/2
%% @see subtrees/1
@@ -6974,8 +6359,6 @@ make_tree(match_expr, [[P], [E]]) -> match_expr(P, E);
make_tree(module_qualifier, [[M], [N]]) -> module_qualifier(M, N);
make_tree(parentheses, [[E]]) -> parentheses(E);
make_tree(prefix_expr, [[F], [A]]) -> prefix_expr(F, A);
-make_tree(qualified_name, [S]) -> qualified_name(S);
-make_tree(query_expr, [[B]]) -> query_expr(B);
make_tree(receive_expr, [C]) -> receive_expr(C);
make_tree(receive_expr, [C, [E], A]) -> receive_expr(C, E, A);
make_tree(record_access, [[E], [F]]) ->
@@ -6995,42 +6378,40 @@ make_tree(tuple, [E]) -> tuple(E).
%% =====================================================================
-%% @spec meta(Tree::syntaxTree()) -> syntaxTree()
-%%
%% @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
+%% same source code text as `Tree' (although the actual data
%% representation may be different). The expression represented by
-%% <code>MetaTree</code> is <em>implementation independent</em> with
+%% `MetaTree' is <em>implementation independent</em> with
%% regard to the data structures used by the abstract syntax tree
-%% implementation. Comments attached to nodes of <code>Tree</code> will
+%% implementation. Comments attached to nodes of `Tree' will
%% be preserved, but other attributes are lost.
%%
-%% <p>Any node in <code>Tree</code> whose node type is
-%% <code>variable</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>
+%% Any node in `Tree' whose node type is
+%% `variable' (see {@link type/1}), and whose list of
+%% annotations (see {@link get_ann/1}) contains the atom
+%% `meta_var', will remain unchanged in the resulting tree,
+%% except that exactly one occurrence of `meta_var' is
+%% removed from its annotation list.
%%
-%% <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
+%% The main use of the function `meta/1' is to transform a
+%% data structure `Tree', 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
+%% printed</em>. E.g., suppose `Tree' represents a variable
+%% named "V". Then (assuming a function `print/1' for
+%% printing syntax trees), evaluating `print(abstract(Tree))'
+%% - simply using {@link abstract/1} to map the actual data
%% structure onto a syntax tree representation - would output a string
-%% that might look something like "<code>{tree, variable, ..., "V",
-%% ...}</code>", which is obviously dependent on the implementation of
+%% that might look something like "`{tree, variable, ..., "V",
+%% ...}'", 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
+%% Using `print(meta(Tree))' instead would output a
%% <em>representation independent</em> syntax tree generating
%% expression; in the above case, something like
-%% "<code>erl_syntax:variable("V")</code>".</p>
+%% "`erl_syntax:variable("V")'".
%%
%% @see abstract/1
%% @see type/1
@@ -7161,60 +6542,56 @@ meta_call(F, As) ->
%% =====================================================================
-%% @spec tree(Type) -> syntaxTree()
%% @equiv tree(Type, [])
--spec tree(atom()) -> syntaxTree().
+-spec tree(atom()) -> #tree{}.
tree(Type) ->
tree(Type, []).
%% =====================================================================
-%% @spec tree(Type::atom(), Data::term()) -> syntaxTree()
-%%
%% @doc <em>For special purposes only</em>. Creates an abstract syntax
-%% tree node with type tag <code>Type</code> and associated data
-%% <code>Data</code>.
+%% tree node with type tag `Type' and associated data
+%% `Data'.
%%
-%% <p>This function and the related <code>is_tree/1</code> and
-%% <code>data/1</code> provide a uniform way to extend the set of
-%% <code>erl_parse</code> node types. The associated data is any term,
-%% whose format may depend on the type tag.</p>
+%% This function and the related {@link is_tree/1} and
+%% {@link data/1} provide a uniform way to extend the set of
+%% `erl_parse' node types. The associated data is any term,
+%% whose format may depend on the type tag.
%%
-%% <h4>Notes:</h4>
+%% === Notes: ===
%% <ul>
%% <li>Any nodes created outside of this module must have type tags
%% distinct from those currently defined by this module; see
-%% <code>type/1</code> for a complete list.</li>
+%% {@link type/1} for a complete list.</li>
%% <li>The type tag of a syntax tree node may also be used
-%% as a primary tag by the <code>erl_parse</code> representation;
+%% as a primary tag by the `erl_parse' representation;
%% in that case, the selector functions for that node type
%% <em>must</em> handle both the abstract syntax tree and the
-%% <code>erl_parse</code> form. The function <code>type(T)</code>
+%% `erl_parse' form. The function `type(T)'
%% should return the correct type tag regardless of the
-%% representation of <code>T</code>, so that the user sees no
-%% difference between <code>erl_syntax</code> and
-%% <code>erl_parse</code> nodes.</li>
+%% representation of `T', so that the user sees no
+%% difference between `erl_syntax' and
+%% `erl_parse' nodes.</li>
%% </ul>
+%%
%% @see is_tree/1
%% @see data/1
%% @see type/1
--spec tree(atom(), term()) -> syntaxTree().
+-spec tree(atom(), term()) -> #tree{}.
tree(Type, Data) ->
#tree{type = Type, data = Data}.
%% =====================================================================
-%% @spec is_tree(Tree::syntaxTree()) -> boolean()
-%%
-%% @doc <em>For special purposes only</em>. Returns <code>true</code> if
-%% <code>Tree</code> is an abstract syntax tree and <code>false</code>
+%% @doc <em>For special purposes only</em>. Returns `true' if
+%% `Tree' is an abstract syntax tree and `false'
%% otherwise.
%%
-%% <p><em>Note</em>: this function yields <code>false</code> for all
-%% "old-style" <code>erl_parse</code>-compatible "parse trees".</p>
+%% <em>Note</em>: this function yields `false' for all
+%% "old-style" `erl_parse'-compatible "parse trees".
%%
%% @see tree/2
@@ -7227,12 +6604,10 @@ is_tree(_) ->
%% =====================================================================
-%% @spec data(Tree::syntaxTree()) -> term()
-%%
%% @doc <em>For special purposes only</em>. Returns the associated data
%% of a syntax tree node. Evaluation fails with reason
-%% <code>badarg</code> if <code>is_tree(Node)</code> does not yield
-%% <code>true</code>.
+%% `badarg' if `is_tree(Node)' does not yield
+%% `true'.
%%
%% @see tree/2
@@ -7248,26 +6623,19 @@ data(T) -> erlang:error({badarg, T}).
%% =====================================================================
-%% @spec wrap(Node::erl_parse()) -> syntaxTree()
-%%
-%% @type erl_parse() = erl_parse:parse_tree(). The "parse tree"
-%% representation built by the Erlang standard library parser
-%% <code>erl_parse</code>. This is a subset of the
-%% <a href="#type-syntaxTree"><code>syntaxTree</code></a> type.
-%%
-%% @doc Creates a wrapper structure around an <code>erl_parse</code>
+%% @doc Creates a wrapper structure around an `erl_parse'
%% "parse tree".
%%
-%% <p>This function and the related <code>unwrap/1</code> and
-%% <code>is_wrapper/1</code> provide a uniform way to attach arbitrary
-%% information to an <code>erl_parse</code> tree. Some information about
+%% This function and the related {@link unwrap/1} and
+%% {@link is_wrapper/1} provide a uniform way to attach arbitrary
+%% information to an `erl_parse' tree. Some information about
%% the encapsuled tree may be cached in the wrapper, such as the node
%% type. All functions on syntax trees must behave so that the user sees
-%% no difference between wrapped and non-wrapped <code>erl_parse</code>
+%% no difference between wrapped and non-wrapped `erl_parse'
%% trees. <em>Attaching a wrapper onto another wrapper structure is an
-%% error</em>.</p>
+%% error</em>.
-%%-spec wrap(erl_parse:parse_tree()) -> syntaxTree().
+-spec wrap(erl_parse()) -> #wrapper{}.
wrap(Node) ->
%% We assume that Node is an old-school `erl_parse' tree.
@@ -7276,24 +6644,20 @@ wrap(Node) ->
%% =====================================================================
-%% @spec unwrap(Node::syntaxTree()) -> syntaxTree()
-%%
-%% @doc Removes any wrapper structure, if present. If <code>Node</code>
+%% @doc Removes any wrapper structure, if present. If `Node'
%% is a wrapper structure, this function returns the wrapped
-%% <code>erl_parse</code> tree; otherwise it returns <code>Node</code>
+%% `erl_parse' tree; otherwise it returns `Node'
%% itself.
--spec unwrap(syntaxTree()) -> syntaxTree().
+-spec unwrap(syntaxTree()) -> #tree{} | erl_parse().
unwrap(#wrapper{tree = Node}) -> Node;
unwrap(Node) -> Node. % This could also be a new-form node.
%% =====================================================================
-%% @spec is_wrapper(Term::term()) -> boolean()
-%%
-%% @doc Returns <code>true</code> if the argument is a wrapper
-%% structure, otherwise <code>false</code>.
+%% @doc Returns `true' if the argument is a wrapper
+%% structure, otherwise `false'.
-ifndef(NO_UNUSED).
-spec is_wrapper(term()) -> boolean().
@@ -7337,32 +6701,6 @@ fold_variable_names(Vs) ->
unfold_variable_names(Vs, Pos) ->
[set_pos(variable(V), Pos) || V <- Vs].
-%% Support functions for qualified names ("foo.bar.baz",
-%% "erl.lang.lists", etc.). The representation overlaps with the weird
-%% "Mnesia query record access" operators. The '.' operator is left
-%% associative, so folding should nest on the left.
-
-is_qualified_name({record_field, _, L, R}) ->
- is_qualified_name(L) andalso is_qualified_name(R);
-is_qualified_name({atom, _, _}) -> true;
-is_qualified_name(_) -> false.
-
-unfold_qualified_name(Node) ->
- lists:reverse(unfold_qualified_name(Node, [])).
-
-unfold_qualified_name({record_field, _, L, R}, Ss) ->
- unfold_qualified_name(R, unfold_qualified_name(L, Ss));
-unfold_qualified_name(S, Ss) -> [S | Ss].
-
-fold_qualified_name([S | Ss], Pos) ->
- fold_qualified_name(Ss, Pos, {atom, Pos, atom_value(S)}).
-
-fold_qualified_name([S | Ss], Pos, Ack) ->
- fold_qualified_name(Ss, Pos, {record_field, Pos, Ack,
- {atom, Pos, atom_value(S)}});
-fold_qualified_name([], _Pos, Ack) ->
- Ack.
-
%% Support functions for transforming lists of record field definitions.
%%
%% There is no unique representation for field definitions in the
diff --git a/lib/syntax_tools/src/erl_syntax_lib.erl b/lib/syntax_tools/src/erl_syntax_lib.erl
index 97dfbfd7cd..2c94ac776d 100644
--- a/lib/syntax_tools/src/erl_syntax_lib.erl
+++ b/lib/syntax_tools/src/erl_syntax_lib.erl
@@ -14,10 +14,8 @@
%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
%% USA
%%
-%% $Id$
-%%
%% @copyright 1997-2006 Richard Carlsson
-%% @author Richard Carlsson <[email protected]>
+%% @author Richard Carlsson <[email protected]>
%% @end
%% =====================================================================
@@ -2225,11 +2223,6 @@ module_name_to_atom(M) ->
case erl_syntax:type(M) of
atom ->
erl_syntax:atom_value(M);
- qualified_name ->
- list_to_atom(packages:concat(
- [erl_syntax:atom_value(A)
- || A <- erl_syntax:qualified_name_segments(M)])
- );
_ ->
throw(syntax_error)
end.
diff --git a/lib/syntax_tools/src/erl_tidy.erl b/lib/syntax_tools/src/erl_tidy.erl
index 09efc9c392..e9a88caff3 100644
--- a/lib/syntax_tools/src/erl_tidy.erl
+++ b/lib/syntax_tools/src/erl_tidy.erl
@@ -14,10 +14,8 @@
%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
%% USA
%%
-%% $Id$
-%%
%% @copyright 1999-2006 Richard Carlsson
-%% @author Richard Carlsson <[email protected]>
+%% @author Richard Carlsson <[email protected]>
%% @end
%% =====================================================================
@@ -377,6 +375,8 @@ write_module(Tree, Name, Opts) ->
end,
filename(filename:join(Dir, Name1))
end,
+ Encoding = [{encoding,Enc} || Enc <- [epp:read_encoding(Name)],
+ Enc =/= none],
case proplists:get_bool(backups, Opts) of
true ->
backup_file(File, Opts);
@@ -384,9 +384,9 @@ write_module(Tree, Name, Opts) ->
ok
end,
Printer = proplists:get_value(printer, Opts),
- FD = open_output_file(File),
+ FD = open_output_file(File, Encoding),
verbose("writing to file `~s'.", [File], Opts),
- V = (catch {ok, output(FD, Printer, Tree, Opts)}),
+ V = (catch {ok, output(FD, Printer, Tree, Opts++Encoding)}),
ok = file:close(FD),
case V of
{ok, _} ->
@@ -434,8 +434,9 @@ file_type(Name, Links) ->
throw(R)
end.
-open_output_file(FName) ->
- case catch file:open(FName, [write]) of
+open_output_file(FName, Options) ->
+io:format("Options ~p~n", [Options]),
+ case catch file:open(FName, [write]++Options) of
{ok, FD} ->
FD;
{error, R} ->
diff --git a/lib/syntax_tools/src/igor.erl b/lib/syntax_tools/src/igor.erl
index aa933eb54b..8abc3f41cb 100644
--- a/lib/syntax_tools/src/igor.erl
+++ b/lib/syntax_tools/src/igor.erl
@@ -14,10 +14,8 @@
%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
%% USA
%%
-%% $Id$
-%%
%% @copyright 1998-2006 Richard Carlsson
-%% @author Richard Carlsson <[email protected]>
+%% @author Richard Carlsson <[email protected]>
%% @end
%% =====================================================================
@@ -343,10 +341,12 @@ merge(Name, Files) ->
merge(Name, Files, Opts) ->
Opts1 = Opts ++ ?DEFAULT_MERGE_OPTS,
- {Tree, Stubs} = merge_files(Name, Files, Opts1),
+ {Sources, Enc} = merge_files1(Files, Opts1),
+ {Tree, Stubs} = merge_sources(Name, Sources, Opts1),
Dir = proplists:get_value(dir, Opts1, ""),
Filename = proplists:get_value(outfile, Opts1, Name),
- File = write_module(Tree, Filename, Dir, Opts1),
+ Encoding = [{encoding, Enc} || Enc =/= none],
+ File = write_module(Tree, Filename, Dir, Encoding ++ Opts1),
[File | maybe_create_stubs(Stubs, Opts1)].
@@ -461,16 +461,21 @@ merge_files(Name, Files, Options) ->
-spec merge_files(atom(), erl_syntax:forms(), [file:filename()], [option()]) ->
{erl_syntax:syntaxTree(), [stubDescriptor()]}.
-merge_files(_, _Trees, [], _) ->
+merge_files(Name, Trees, Files, Opts) ->
+ {Sources, _Encoding} = merge_files1(Files, Opts),
+ merge_sources(Name, Trees ++ Sources, Opts).
+
+merge_files1([], _) ->
report_error("no files to merge."),
exit(badarg);
-merge_files(Name, Trees, Files, Opts) ->
+merge_files1(Files, Opts) ->
Opts1 = Opts ++ [{includes, ?DEFAULT_INCLUDES},
{macros, ?DEFAULT_MACROS},
{preprocess, false},
comments],
- Sources = [read_module(F, Opts1) || F <- Files],
- merge_sources(Name, Trees ++ Sources, Opts1).
+ SourceEncodings = [read_module(F, Opts1) || F <- Files],
+ {Sources, [Encoding | _]} = lists:unzip(SourceEncodings),
+ {Sources, Encoding}.
%% =====================================================================
@@ -2514,7 +2519,11 @@ rename(Files, Renamings, Opts) ->
lists:flatmap(fun (F) -> rename_file(F, Dict, Opts1) end, Files).
rename_file(File, Dict, Opts) ->
- S = read_module(File, Opts),
+ {S, Enc} = read_module(File, Opts),
+ %% Try to avoid *two* coding: comments:
+ Encoding = [{encoding, Enc} ||
+ Enc =/= none,
+ not proplists:get_bool(comments, Opts)],
M = get_module_info(S),
Name = M#module.name,
Name1 = case dict:find(Name, Dict) of
@@ -2528,10 +2537,10 @@ rename_file(File, Dict, Opts) ->
Opts1 = [no_headers,
{export, [Name]},
{static, [Name]},
- {redirect, dict:to_list(Dict1)}] ++ Opts,
+ {redirect, dict:to_list(Dict1)}] ++ Encoding ++ Opts,
{Tree, Stubs} = merge_sources(Name1, [S], Opts1),
Dir = filename:dirname(filename(File)),
- File1 = write_module(Tree, Name1, Dir, Opts),
+ File1 = write_module(Tree, Name1, Dir, Opts++Encoding),
%% We create the stub file in the same directory as the source file
%% and the target file.
@@ -2650,7 +2659,7 @@ error_text(D, Name) ->
{L, M, E} when is_integer(L), is_atom(M) ->
case catch M:format_error(E) of
S when is_list(S) ->
- io_lib:fwrite("`~w', line ~w: ~s.",
+ io_lib:fwrite("`~w', line ~w: ~ts.",
[Name, L, S]);
_ ->
error_text_1(D, Name)
@@ -2708,7 +2717,17 @@ open_output_file(FName) ->
exit(R)
end.
-%% read_module(Name, Options) -> syntaxTree()
+output_encoding(FD, Opts) ->
+ case proplists:get_value(encoding, Opts) of
+ undefined ->
+ ok = io:setopts(FD, [{encoding, epp:default_encoding()}]);
+ Encoding ->
+ ok = io:setopts(FD, [{encoding, Encoding}]),
+ EncS = epp:encoding_to_string(Encoding),
+ ok = io:fwrite(FD, <<"%% ~s\n">>, [EncS])
+ end.
+
+%% read_module(Name, Options) -> {syntaxTree(), epp:source_encoding()}
%%
%% This also tries to locate the real source file, if "Name" does not
%% point directly to a particular file.
@@ -2731,20 +2750,21 @@ read_module(Name, Options) ->
read_module_1(Name, Options) ->
verbose("reading module `~s'.", [filename(Name)], Options),
- Forms = read_module_2(Name, Options),
+ {Forms, Enc} = read_module_2(Name, Options),
case proplists:get_bool(comments, Options) of
false ->
- Forms;
+ {Forms, Enc};
true ->
Comments = erl_comment_scan:file(Name),
- erl_recomment:recomment_forms(Forms, Comments)
+ {erl_recomment:recomment_forms(Forms, Comments), Enc}
end.
read_module_2(Name, Options) ->
case read_module_3(Name, Options) of
{ok, Forms} ->
check_forms(Forms, Name),
- Forms;
+ Enc = epp:read_encoding(Name),
+ {Forms, Enc};
{error, _} = Error ->
error_read_file(Name),
exit(Error)
@@ -2774,7 +2794,7 @@ check_forms([F | Fs], File) ->
_ ->
"unknown error"
end,
- report_error("in file `~s' at line ~w:\n ~s",
+ report_error("in file `~s' at line ~w:\n ~ts",
[filename(File), erl_syntax:get_pos(F), S]),
exit(error);
_ ->
@@ -2849,6 +2869,7 @@ write_module(Tree, Name, Dir, Opts) ->
end,
Printer = proplists:get_value(printer, Opts),
FD = open_output_file(File),
+ ok = output_encoding(FD, Opts),
verbose("writing to file `~s'.", [File], Opts),
V = (catch {ok, output(FD, Printer, Tree, Opts)}),
ok = file:close(FD),
diff --git a/lib/syntax_tools/src/prettypr.erl b/lib/syntax_tools/src/prettypr.erl
index c13fa30998..1b5ba6b05a 100644
--- a/lib/syntax_tools/src/prettypr.erl
+++ b/lib/syntax_tools/src/prettypr.erl
@@ -14,10 +14,8 @@
%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
%% USA
%%
-%% $Id$
-%%
%% @copyright 2000-2006 Richard Carlsson
-%% @author Richard Carlsson <[email protected]>
+%% @author Richard Carlsson <[email protected]>
%% @end
%% =====================================================================
diff --git a/lib/syntax_tools/vsn.mk b/lib/syntax_tools/vsn.mk
index 2b9a08e192..8f774c5d75 100644
--- a/lib/syntax_tools/vsn.mk
+++ b/lib/syntax_tools/vsn.mk
@@ -1 +1 @@
-SYNTAX_TOOLS_VSN = 1.6.8
+SYNTAX_TOOLS_VSN = 1.6.9
diff --git a/lib/test_server/doc/src/Makefile b/lib/test_server/doc/src/Makefile
index 3ce549f0e1..8c7fa99886 100644
--- a/lib/test_server/doc/src/Makefile
+++ b/lib/test_server/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2002-2011. All Rights Reserved.
+# Copyright Ericsson AB 2002-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/test_server/doc/src/notes.xml b/lib/test_server/doc/src/notes.xml
index 3701066e56..6a9add044a 100644
--- a/lib/test_server/doc/src/notes.xml
+++ b/lib/test_server/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2004</year><year>2011</year>
+ <year>2004</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -32,6 +32,119 @@
<file>notes.xml</file>
</header>
+<section><title>Test_Server 3.5.3</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ test_server_h will now recognize info_reports written by
+ ct connection handlers (according to the description in
+ cth_conn_log) and ignore them as they will be completely
+ handled by by ct_conn_log_h.</p>
+ <p>
+ Earlier test_server_h would print a tag (testcase name)
+ before forwarding the report to error_logger_tty_h. This
+ would cause lots of tags in the log with no info report
+ following (since error_logger_tty_h did not handle them).</p>
+ <p>
+ Own Id: OTP-10571</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Known Bugs and Problems</title>
+ <list>
+ <item>
+ <p>
+ Restore Config data if lost when test case fails.</p>
+ <p>
+ Own Id: OTP-10070 Aux Id: kunagi-175 [86] </p>
+ </item>
+ <item>
+ <p>
+ IO server error in test_server.</p>
+ <p>
+ Own Id: OTP-10125 Aux Id: OTP-10101, kunagi-177 [88] </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Test_Server 3.5.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The documentation has been updated with the latest
+ changes for the test_server_ctrl:report/2 function.</p>
+ <p>
+ Own Id: OTP-10086 Aux Id: seq12066 </p>
+ </item>
+ <item>
+ <p>
+ The ct:get_status/0 function failed to report status if a
+ parallel test case group was running at the time of the
+ call. This has been fixed and the return value for the
+ function has been updated. Please see the ct reference
+ manual for details.</p>
+ <p>
+ Own Id: OTP-10172</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ It is now possible to sort the HTML tables by clicking on
+ the header elements. In order to reset a sorted table,
+ the browser window should simply be refreshed. This
+ feature requires that the browser supports javascript,
+ and has javascript execution enabled. If the 'ct_run
+ -basic_html' flag is used, no javascript code is included
+ in the generated HTML code.</p>
+ <p>
+ Own Id: OTP-9896 Aux Id: seq12034, OTP-9835 </p>
+ </item>
+ <item>
+ <p>
+ Verbosity levels for log printouts has been added. This
+ makes it possible to specify preferred verbosity for
+ different categories of log printouts, as well as general
+ printouts (such as standard IO), to allow control over
+ which strings get printed and which get ignored. New
+ versions of the Common Test logging functions, ct:log,
+ ct:pal and ct:print, have been introduced, with a new
+ Importance argument added. The Importance value is
+ compared to the verbosity level at runtime. More
+ information can be found in the chapter about Logging in
+ the Common Test User's Guide.</p>
+ <p>
+ Own Id: OTP-10067 Aux Id: seq12050 </p>
+ </item>
+ <item>
+ <p>
+ The Erlang/OTP test runner ts has been extended to allow
+ cross compilation of test suites. To cross compile the
+ test suites first follow the normal cross compilation
+ procedures and release the tests on the build host. Then
+ install ts using an xcomp specification file and compile
+ test suites using ts:compile_testcases/0. For more
+ details see $ERL_TOP/xcomp/README.md.</p>
+ <p>
+ Own Id: OTP-10074</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Test_Server 3.5.1</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 5bfa42c36f..841cbfbe91 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>2011</year>
+ <year>2012</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -529,6 +529,18 @@ Only valid for peer nodes. Note that slave nodes always
analogy with <c>os:getenv/1</c>), which removes the
environment variable. Only valid for peer nodes. Not
available on VxWorks.</item>
+ <tag><c>{start_cover, false}</c></tag>
+ <item>By default the test server will start cover on all nodes
+ when the test is run with code coverage analysis. To make
+ sure cover is not started on a new node, set this option to
+ <c>false</c>. This can be necessary if the connection to
+ the node at some point will be broken but the node is
+ expected to stay alive. The reason is that a remote cover
+ node can not continue to run without its main node. Another
+ solution would be to explicitly stop cover on the node
+ before breaking the connection, but in some situations (if
+ old code resides in one or more processes) this is not
+ possible.</item>
</taglist>
</desc>
</func>
diff --git a/lib/test_server/doc/src/test_server_ctrl.xml b/lib/test_server/doc/src/test_server_ctrl.xml
index 9028a67ecb..af96f1fe7e 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>2011</year>
+ <year>2012</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -427,11 +427,21 @@ Optional, if not given the test server controller node
<p>A <c>CoverFile</c> can have the following entries:</p>
<code type="none">
{exclude, all | ExcludeModuleList}.
-{include, IncludeModuleList}. </code>
+{include, IncludeModuleList}.
+{cross, CrossCoverInfo}.</code>
<p>Note that each line must end with a full
stop. <c>ExcludeModuleList</c> and <c>IncludeModuleList</c>
are lists of atoms, where each atom is a module name.
</p>
+
+ <p><c>CrossCoverInfo</c> is used when collecting cover data
+ over multiple tests. Modules listed here are compiled, but
+ they will not be analysed when the test is finished. See
+ <seealso
+ marker="#cross_cover_analyse-2">cross_cover_analyse/2</seealso>
+ for more information about the cross cover mechanism and the
+ format of <c>CrossCoverInfo</c>.
+ </p>
<p>If both an <c>Application</c> and a <c>CoverFile</c> is
given, all modules in the application are cover compiled,
except for the modules listed in <c>ExcludeModuleList</c>. The
@@ -467,30 +477,71 @@ Optional, if not given the test server controller node
</desc>
</func>
<func>
- <name>cross_cover_analyse(Level) -> ok</name>
- <fsummary>Analyse cover data collected from all tests</fsummary>
+ <name>cross_cover_analyse(Level, Tests) -> ok</name>
+ <fsummary>Analyse cover data collected from multiple tests</fsummary>
<type>
<v>Level = details | overview</v>
+ <v>Tests = [{Tag,LogDir}]</v>
+ <v>Tag = atom()</v>
+ <d>Test identifier.</d>
+ <v>LogDir = string()</v>
+ <d>Log directory for the test identified by <c>Tag</c>. This
+ can either be the <c>run.&lt;timestamp&gt;</c> directory or
+ the parent directory of this (in which case the latest
+ <c>run.&lt;timestamp&gt;</c> directory is chosen.</d>
</type>
<desc>
- <p>Analyse cover data collected from all tests. The modules
- analysed are the ones listed in the cross cover file
- <c>cross.cover</c> in the current directory of the test
- server.</p>
- <p>The modules listed in the <c>cross.cover</c> file are
- modules that are heavily used by other applications than the
- one they belong to. This function should be run after all
- tests are completed, and the result will be stored in a file
- called cross_cover.html in the run.&lt;timestamp&gt;
- directory of the application the modules belong to.
- </p>
- <p>The <c>cross.cover</c> file contains elements like this:</p>
- <pre>
-{App,Modules}. </pre>
- <p>where <c>App</c> can be an application name or the atom
- <c>all</c>. The application (or all applications) will cover
- compile the listed <c>Modules</c>.
- </p>
+ <p>Analyse cover data collected from multiple tests. The modules
+ analysed are the ones listed in <c>cross</c> statements in
+ the cover files. These are modules that are heavily used by
+ other tests than the one where they belong or are explicitly
+ tested. They should then be listed as cross modules in the
+ cover file for the test where they are used but do not
+ belong. Se example below.</p>
+ <p>This function should be run after all tests are completed,
+ and the result will be stored in a file called
+ <c>cross_cover.html</c> in the <c>run.&lt;timestamp&gt;</c>
+ directory of the test the modules belong to.</p>
+ <p>Note that the function can be executed on any node, and it
+ does not require <c>test_server_ctrl</c> to be started first.</p>
+ <p>The <c>cross</c> statement in the cover file must be like this:</p>
+ <code type="none">
+{cross,[{Tag,Modules}]}.</code>
+ <p>where <c>Tag</c> is the same as <c>Tag</c> in the
+ <c>Tests</c> parameter to this function and <c>Modules</c> is a
+ list of module names (atoms).</p>
+ <p><em>Example:</em></p>
+ <p>If the module <c>m1</c> belongs to system <c>s1</c> but is
+ heavily used also in the tests for another system <c>s2</c>,
+ then the cover files for the two systems' tests could be like
+ this:</p>
+<code type="none">
+s1.cover:
+ {include,[m1]}.
+
+s2.cover:
+ {include,[....]}. % modules belonging to system s2
+ {cross,[{s1,[m1]}]}.</code>
+ <p>When the tests for both <c>s1</c> and <c>s2</c> are completed, run</p>
+<code type="none">
+test_server_ctrl:cross_cover_analyse(Level,[{s1,S1LogDir},{s2,S2LogDir}])
+</code>
+
+ <p>and the accumulated cover data for <c>m1</c> will be written to
+ <c>S1LogDir/[run.&lt;timestamp&gt;/]cross_cover.html</c>.</p>
+ <p>Note that the <c>m1</c> module will also be presented in the
+ normal coverage log for <c>s1</c> (due to the include statement in
+ <c>s1.cover</c>), but that only includes the coverage achieved by the
+ <c>s1</c> test itself.</p>
+ <p>The Tag in the <c>cross</c> statement in the cover file has
+ no other purpose than mapping the list of modules
+ (<c>[m1]</c> in the example above) to the correct log
+ directory where it should be included in the
+ <c>cross_cover.html</c> file (<c>S1LogDir</c> in the example
+ above). I.e. the value of <c>Tag</c> has no meaning, it
+ could be <c>foo</c> as well as <c>s1</c> above, as long as
+ the same <c>Tag</c> is used in the cover file and in the
+ call to this function.</p>
</desc>
</func>
<func>
@@ -769,11 +820,13 @@ Optional, if not given the test server controller node
constantly updated. The following can be reported:
</p>
<p><c>What = tests_start, Data = {Name,NumCases}</c><br></br>
+ <c>What = loginfo, Data = [{topdir,TestRootDir},{rundir,CurrLogDir}]</c><br></br>
<c>What = tests_done, Data = {Ok,Failed,{UserSkipped,AutoSkipped}}</c><br></br>
- <c>What = tc_start, Data = {Mod,Func}</c><br></br>
+ <c>What = tc_start, Data = {{Mod,Func},TCLogFile}</c><br></br>
<c>What = tc_done, Data = {Mod,Func,Result}</c><br></br>
<c>What = tc_user_skip, Data = {Mod,Func,Comment}</c><br></br>
- <c>What = tc_auto_skip, Data = {Mod,Func,Comment}</c></p>
+ <c>What = tc_auto_skip, Data = {Mod,Func,Comment}</c><br></br>
+ <c>What = framework_error, Data = {{FWMod,FWFunc},Error}</c></p>
</desc>
</func>
<func>
diff --git a/lib/test_server/doc/src/ts.xml b/lib/test_server/doc/src/ts.xml
index f9b48d8372..82ba3a5017 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>2011</year>
+ <year>2012</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -85,8 +85,7 @@
<p><c>ts:install/1</c> or <c>ts:install/2</c> is used if the
target platform is different from the controller host, i.e. if
you run on "remote target" or if special options are required
- for your system. VxWorks is currently supported
- as remote target platform.
+ for your system.
</p>
<p>See the reference manual for detailed information about
<c>ts:install/0/1/2</c>.
@@ -249,9 +248,8 @@
<p>Installs and configures the Test Server Framework for
running test suites. If a remote host is to be used, the
<c>TargetSystem</c> argument must be given so that "cross
- installation" can be done. This should be used for testing on
- VxWorks. Installation is required for any of the
- functions in <c>ts</c> to work.
+ installation" can be done. Installation is required for
+ any of the functions in <c>ts</c> to work.
</p>
<p>Opts may be one or more of
</p>
@@ -452,7 +450,7 @@ This option is mandatory for remote targets
<desc>
<p>Analyse cover data collected from all tests.
</p>
- <p>See test_server_ctrl:cross_cover_analyse/1
+ <p>See test_server_ctrl:cross_cover_analyse/2
</p>
</desc>
</func>
@@ -500,29 +498,6 @@ This option is mandatory for remote targets
</desc>
</func>
<func>
- <name>index() -> ok | {error, Reason}</name>
- <fsummary>Updates local index page</fsummary>
- <type>
- <v>Reason = term()</v>
- </type>
- <desc>
- <p>This function updates the local index page. This can be
- useful if a previous test run was not completed and the index
- is incomplete.</p>
- </desc>
- </func>
- <func>
- <name>clean() -> ok</name>
- <name>clean(all) -> ok</name>
- <fsummary>Cleans up the log directories created when running tests. </fsummary>
- <desc>
- <p>This function cleans up log directories created when
- running test cases. <c>clean/0</c> cleans up all but the last
- run of each application. <c>clean/1</c> cleans up all test
- runs found.</p>
- </desc>
- </func>
- <func>
<name>estone() -> ok | {error, Reason}</name>
<name>estone(Opts) -> ok</name>
<fsummary>Runs the EStone test</fsummary>
diff --git a/lib/test_server/src/Makefile b/lib/test_server/src/Makefile
index a75855eaab..43a03f4e1d 100644
--- a/lib/test_server/src/Makefile
+++ b/lib/test_server/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2011. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -40,24 +40,24 @@ RELSYSDIR = $(RELEASE_PATH)/lib/test_server-$(VSN)
# ----------------------------------------------------
MODULES= test_server_ctrl \
+ test_server_gl \
+ test_server_io \
test_server_node \
test_server \
test_server_sup \
test_server_h \
- erl2html2 \
- vxworks_client
+ erl2html2
TS_MODULES= \
ts \
ts_run \
- ts_reports \
ts_lib \
ts_make \
ts_erl_config \
ts_autoconf_win32 \
- ts_autoconf_vxworks \
ts_install \
- ts_install_cth
+ ts_install_cth \
+ ts_benchmark
TARGET_MODULES= $(MODULES:%=$(EBIN)/%)
TS_TARGET_MODULES= $(TS_MODULES:%=$(EBIN)/%)
@@ -69,7 +69,6 @@ INTERNAL_HRL_FILES = test_server_internal.hrl
TS_HRL_FILES= ts.hrl
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
@@ -113,10 +112,10 @@ configure: configure.in
# Special Build Targets
# ----------------------------------------------------
$(APP_TARGET): $(APP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
# ----------------------------------------------------
# Release Target
@@ -137,7 +136,7 @@ release_tests_spec: opt
$(INSTALL_DATA) $(ERL_FILES) $(TS_ERL_FILES) \
$(HRL_FILES) $(INTERNAL_HRL_FILES) $(TS_HRL_FILES) \
$(TS_TARGET_FILES) \
- $(AUTOCONF_FILES) $(C_FILES) $(COVER_FILES) $(CONFIG) \
+ $(AUTOCONF_FILES) $(C_FILES) $(CONFIG) \
"$(RELEASE_PATH)/test_server"
$(INSTALL_SCRIPT) $(PROGRAMS) "$(RELEASE_PATH)/test_server"
diff --git a/lib/test_server/src/configure.in b/lib/test_server/src/configure.in
index 77bc993ccd..785bab395c 100644
--- a/lib/test_server/src/configure.in
+++ b/lib/test_server/src/configure.in
@@ -2,7 +2,7 @@ dnl Process this file with autoconf to produce a configure script for Erlang.
dnl
dnl %CopyrightBegin%
dnl
-dnl Copyright Ericsson AB 1997-2011. All Rights Reserved.
+dnl Copyright Ericsson AB 1997-2012. All Rights Reserved.
dnl
dnl The contents of this file are subject to the Erlang Public License,
dnl Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/test_server/src/erl2html2.erl b/lib/test_server/src/erl2html2.erl
index 6891e87e48..1729257809 100644
--- a/lib/test_server/src/erl2html2.erl
+++ b/lib/test_server/src/erl2html2.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,19 +18,9 @@
%%
%%%------------------------------------------------------------------
-%%% Purpose:Convert Erlang files to html. (Pretty faaast... :-)
+%%% Purpose:Convert Erlang files to html.
%%%------------------------------------------------------------------
-%--------------------------------------------------------------------
-% Some stats (Sparc5@110Mhz):
-% 4109 lines (erl_parse.erl): 3.00 secs
-% 1847 lines (application_controller.erl): 0.57 secs
-% 3160 lines (test_server.erl): 1.00 secs
-% 1199 lines (ts_estone.erl): 0.35 secs
-%
-% Avg: ~4.5e-4s/line, or ~0.45s/1000 lines, or ~2200 lines/sec.
-%--------------------------------------------------------------------
-
-module(erl2html2).
-export([convert/2, convert/3]).
@@ -44,142 +34,172 @@ convert(File, Dest) ->
%%
%% FIXME: The colours should *really* be set with
%% stylesheets...
+ Encoding = encoding(File),
Header = ["<!DOCTYPE HTML PUBLIC "
"\"-//W3C//DTD HTML 3.2 Final//EN\">\n"
"<!-- autogenerated by '"++atom_to_list(?MODULE)++"'. -->\n"
"<html>\n"
- "<head><title>", File, "</title></head>\n\n"
+ "<head>\n"
+ "<meta http-equiv=\"Content-Type\" content=\"text/html;"
+ "charset=",
+ Encoding,"\"/>\n"
+ "<title>", File, "</title>\n"
+ "</head>\n\n"
"<body bgcolor=\"white\" text=\"black\""
" link=\"blue\" vlink=\"purple\" alink=\"red\">\n"],
convert(File, Dest, Header).
-
+
+
convert(File, Dest, Header) ->
- case file:read_file(File) of
- {ok, Bin} ->
- Code=binary_to_list(Bin),
- statistics(runtime),
- {Html1, Lines} = root(Code, [], 1),
- Html = [Header,
- "<pre>\n", Html1, "</pre>\n",
- footer(Lines),"</body>\n</html>\n"],
- file:write_file(Dest, Html);
- {error, Reason} ->
- {error, Reason}
+ %% statistics(runtime),
+ case parse_file(File) of
+ {ok,Functions} ->
+ %% {_, Time1} = statistics(runtime),
+ %% io:format("Parsed file in ~.2f Seconds.~n",[Time1/1000]),
+ case file:open(File,[raw,{read_ahead,10000}]) of
+ {ok,SFd} ->
+ case file:open(Dest,[write,raw]) of
+ {ok,DFd} ->
+ file:write(DFd,[Header,"<pre>\n"]),
+ _Lines = build_html(SFd,DFd,Functions),
+ file:write(DFd,["</pre>\n",footer(),
+ "</body>\n</html>\n"]),
+ %% {_, Time2} = statistics(runtime),
+ %% io:format("Converted ~p lines in ~.2f Seconds.~n",
+ %% [_Lines, Time2/1000]),
+ file:close(SFd),
+ file:close(DFd),
+ ok;
+ Error ->
+ Error
+ end;
+ Error ->
+ Error
+ end;
+ Error ->
+ Error
end.
-root([], Res, Line) ->
- {Res, Line};
-root([Char0|Code], Res, Line0) ->
- Char = [Char0],
- case Char of
- "-" ->
- {Match, Line1, NewCode0, AttName} =
- read_to_char(Line0+1, Code, [], [$(, $.]),
- {_, Line2, NewCode, Stuff} = read_to_char(Line1, NewCode0, [], $\n),
- NewRes = [Res,linenum(Line0),"-<b>",AttName,
- "</b>",Match, Stuff, "\n"],
- root(NewCode, NewRes, Line2);
- "%" ->
- {_, Line, NewCode, Stuff} = read_to_char(Line0+1, Code, [], $\n),
- NewRes = [Res,linenum(Line0),"<i>%",Stuff,"</i>\n"],
- root(NewCode, NewRes, Line);
- "\n" ->
- root(Code, [Res,linenum(Line0), "\n"], Line0+1);
- " " ->
- {_, Line, NewCode, Stuff} = read_to_char(Line0+1, Code, [], $\n),
- root(NewCode, [Res,linenum(Line0)," ",Stuff, "\n"],
- Line);
- "\t" ->
- {_, Line, NewCode, Stuff} = read_to_char(Line0+1, Code, [], $\n),
- root(NewCode, [Res,linenum(Line0),"\t",Stuff, "\n"],
- Line);
- [Chr|_] when Chr>96, Chr<123 ->
- %% Assumed to be function/clause start.
- %% FIXME: This will trivially generate non-unique anchors
- %% (one for each clause) --- which is illegal HTML.
- {_, Line1, NewCode0, FName0} = read_to_char(Line0+1, Code, [], $(),
- {_, Line2, NewCode, Stuff} =
- read_to_char(Line1,NewCode0, [], $\n),
- FuncName = [[Chr],FName0],
- NewRes=[Res,"<a name=",FuncName,">",
- linenum(Line0),"<b>",FuncName,"</b></a>",
- "(",Stuff, "\n"],
- root(NewCode, NewRes, Line2);
- Chr ->
- {_, Line, NewCode, Stuff} = read_to_char(Line0+1, Code, [], $\n),
- root(NewCode, [Res,linenum(Line0),Chr,Stuff, "\n"],
- Line)
+%%%-----------------------------------------------------------------
+%%% Parse the input file to get the line numbers for all function
+%%% definitions. This will be used when creating link targets for each
+%%% function in build_html/5.
+%%%
+%%% All function clauses are also marked in order to allow
+%%% possibly_enhance/2 to write these in bold.
+parse_file(File) ->
+ case epp:open(File, [], []) of
+ {ok,Epp} ->
+ Forms = parse_file(Epp,File,false),
+ epp:close(Epp),
+ {ok,Forms};
+ {error,E} ->
+ {error,E}
end.
-read_to_char(Line0, [], Res, _Chr) ->
- {nomatch, Line0, [], Res};
-read_to_char(Line0, [Char|Code], Res, Chr) ->
- case Char of
- Chr -> {Char, Line0, Code, Res};
- _ when is_list(Chr) ->
- case lists:member(Char,Chr) of
- true ->
- {Char, Line0, Code, Res};
- false ->
- {Line,NewCode,NewRes} = maybe_convert(Line0,Code,Res,Char),
- read_to_char(Line, NewCode, NewRes, Chr)
+
+parse_file(Epp,File,InCorrectFile) ->
+ case epp:parse_erl_form(Epp) of
+ {ok,Form} ->
+ case Form of
+ {attribute,_,file,{File,_}} ->
+ parse_file(Epp,File,true);
+ {attribute,_,file,{_OtherFile,_}} ->
+ parse_file(Epp,File,false);
+ {function,L,F,A,[_|C]} when InCorrectFile ->
+ Clauses = [{clause,CL} || {clause,CL,_,_,_} <- C],
+ [{atom_to_list(F),A,L} | Clauses] ++
+ parse_file(Epp,File,true);
+ _ ->
+ parse_file(Epp,File,InCorrectFile)
end;
- _ ->
- {Line,NewCode,NewRes} = maybe_convert(Line0,Code,Res,Char),
- read_to_char(Line,NewCode, NewRes, Chr)
+ {error,_E} ->
+ parse_file(Epp,File,InCorrectFile);
+ {eof,_Location} ->
+ []
end.
-maybe_convert(Line0,Code,Res,Chr) ->
- case Chr of
- %% Quoted stuff should not have the highlighting like normal code
- %% FIXME: unbalanced quotes (e.g. in comments) will cause trouble with
- %% highlighting and line numbering in the rest of the module.
- $" ->
- {_, Line1, NewCode, Stuff0} = read_to_char(Line0, Code, [], $"),
- {Line2,Stuff} = add_linenumbers(Line1,lists:flatten(Stuff0),[]),
- {Line2,NewCode,[Res,$",Stuff,$"]};
- %% These chars have meaning in HTML, and *must* *not* be
- %% written as themselves.
- $& ->
- {Line0, Code, [Res,"&amp;"]};
- $< ->
- {Line0, Code, [Res,"&lt;"]};
- $> ->
- {Line0, Code, [Res,"&gt;"]};
- %% Everything else is simply copied.
- OtherChr ->
- {Line0, Code, [Res,OtherChr]}
- end.
+%%%-----------------------------------------------------------------
+%%% Add a link target for each line and one for each function definition.
+build_html(SFd,DFd,Functions) ->
+ build_html(SFd,DFd,file:read_line(SFd),1,Functions,false).
-add_linenumbers(Line,[Chr|Chrs],Res) ->
- case Chr of
- $\n -> add_linenumbers(Line+1,Chrs,[Res,$\n,linenum(Line)]);
- _ -> add_linenumbers(Line,Chrs,[Res,Chr])
- end;
-add_linenumbers(Line,[],Res) ->
- {Line,Res}.
-
-%% Make nicely indented line numbers.
-linenum(Line) ->
- Num = integer_to_list(Line),
- A = case Line rem 10 of
- 0 -> "<a name=\"" ++ Num ++"\"></a>";
- _ -> []
- end,
+build_html(SFd,DFd,{ok,Str},L,[{F,A,L}|Functions],_IsFuncDef) ->
+ FALink = http_uri:encode(F++"-"++integer_to_list(A)),
+ file:write(DFd,["<a name=\"",FALink,"\"/>"]),
+ build_html(SFd,DFd,{ok,Str},L,Functions,true);
+build_html(SFd,DFd,{ok,Str},L,[{clause,L}|Functions],_IsFuncDef) ->
+ build_html(SFd,DFd,{ok,Str},L,Functions,true);
+build_html(SFd,DFd,{ok,Str},L,Functions,IsFuncDef) ->
+ LStr = line_number(L),
+ Str1 = line(Str,IsFuncDef),
+ file:write(DFd,[LStr,Str1]),
+ build_html(SFd,DFd,file:read_line(SFd),L+1,Functions,false);
+build_html(_SFd,_DFd,eof,L,_Functions,_IsFuncDef) ->
+ L.
+
+line_number(L) ->
+ LStr = integer_to_list(L),
Pred =
- case length(Num) of
+ case length(LStr) of
Length when Length < 5 ->
lists:duplicate(5-Length,$\s);
_ ->
[]
end,
- [A,Pred,integer_to_list(Line),":"].
+ ["<a name=\"",LStr,"\"/>",Pred,LStr,": "].
+
+line(Str,IsFuncDef) ->
+ Str1 = htmlize(Str),
+ possibly_enhance(Str1,IsFuncDef).
-footer(_Lines) ->
+%%%-----------------------------------------------------------------
+%%% Substitute special characters that should not appear in HTML
+htmlize([$<|Str]) ->
+ [$&,$l,$t,$;|htmlize(Str)];
+htmlize([$>|Str]) ->
+ [$&,$g,$t,$;|htmlize(Str)];
+htmlize([$&|Str]) ->
+ [$&,$a,$m,$p,$;|htmlize(Str)];
+htmlize([$"|Str]) ->
+ [$&,$q,$u,$o,$t,$;|htmlize(Str)];
+htmlize([Ch|Str]) ->
+ [Ch|htmlize(Str)];
+htmlize([]) ->
+ [].
+
+%%%-----------------------------------------------------------------
+%%% Write comments in italic and function definitions in bold.
+possibly_enhance(Str,true) ->
+ case lists:splitwith(fun($() -> false; (_) -> true end, Str) of
+ {_,[]} -> Str;
+ {F,A} -> ["<b>",F,"</b>",A]
+ end;
+possibly_enhance([$%|_]=Str,_) ->
+ ["<i>",Str--"\n","</i>","\n"];
+possibly_enhance([$-|_]=Str,_) ->
+ possibly_enhance(Str,true);
+possibly_enhance(Str,false) ->
+ Str.
+
+%%%-----------------------------------------------------------------
+%%% End of the file
+footer() ->
"".
-%% {_, Time} = statistics(runtime),
-%% io:format("Converted ~p lines in ~.2f Seconds.~n",
-%% [Lines, Time/1000]),
-%% S = "<i>The transformation of this file (~p lines) took ~.2f seconds</i>",
-%% F = lists:flatten(io_lib:format(S, [Lines, Time/1000])),
-%% ["<hr size=1>",F,"<br>\n"].
+
+%%%-----------------------------------------------------------------
+%%% Read encoding from source file
+encoding(File) ->
+ Encoding =
+ case epp:read_encoding(File) of
+ none ->
+ epp:default_encoding();
+ E ->
+ E
+ end,
+ html_encoding(Encoding).
+
+html_encoding(latin1) ->
+ "iso-8859-1";
+html_encoding(utf8) ->
+ "utf-8".
diff --git a/lib/test_server/src/test_server.app.src b/lib/test_server/src/test_server.app.src
index faf7db835e..26330f9695 100644
--- a/lib/test_server/src/test_server.app.src
+++ b/lib/test_server/src/test_server.app.src
@@ -24,6 +24,7 @@
test_server_ctrl,
test_server,
test_server_h,
+ test_server_io,
test_server_node,
test_server_sup
]},
diff --git a/lib/test_server/src/test_server.erl b/lib/test_server/src/test_server.erl
index 6e94e4861a..37cd8fac99 100644
--- a/lib/test_server/src/test_server.erl
+++ b/lib/test_server/src/test_server.erl
@@ -20,21 +20,19 @@
-define(DEFAULT_TIMETRAP_SECS, 60).
-%%% START %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
--export([start/1,start/2]).
-
%%% TEST_SERVER_CTRL INTERFACE %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-export([run_test_case_apply/1,init_target_info/0,init_purify/0]).
--export([cover_compile/1,cover_analyse/2]).
+-export([cover_compile/1,cover_analyse/3]).
%%% TEST_SERVER_SUP INTERFACE %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
--export([get_loc/1]).
+-export([get_loc/1,set_tc_state/1]).
%%% TEST SUITE INTERFACE %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-export([lookup_config/2]).
-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([permit_io/2]).
-export([hours/1,minutes/1,seconds/1,sleep/1,adjusted_sleep/1,timecall/3]).
-export([timetrap_scale_factor/0,timetrap/1,get_timetrap_info/0,
timetrap_cancel/1,timetrap_cancel/0]).
@@ -49,7 +47,7 @@
-export([run_on_shielded_node/2]).
-export([is_cover/0,is_debug/0,is_commercial/0]).
--export([break/1,continue/0]).
+-export([break/1,break/2,break/3,continue/0,continue/1]).
%%% DEBUGGER INTERFACE %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-export([purify_new_leaks/0, purify_format/2, purify_new_fds_inuse/0,
@@ -59,49 +57,9 @@
-export([]).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
--record(state,{controller,jobs=[]}).
-
-include("test_server_internal.hrl").
-include_lib("kernel/include/file.hrl").
--define(pl2a(M), test_server_sup:package_atom(M)).
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%%
-%% **** START *** CODE FOR REMOTE TARGET ONLY ***
-%%
-%% test_server
-%% This process is started only if the test is to be run on a remote target
-%% The process is then started on target
-%% A socket connection is established with the test_server_ctrl process
-%% on host, and information about target is sent to host.
-start([ControllerHost]) when is_atom(ControllerHost) ->
- start(atom_to_list(ControllerHost));
-start(ControllerHost) when is_list(ControllerHost) ->
- start(ControllerHost,?MAIN_PORT).
-start(ControllerHost,ControllerPort) ->
- S = self(),
- Pid = spawn(fun() -> init(ControllerHost,ControllerPort,S) end),
- receive {Pid,started} -> {ok,Pid};
- {Pid,Error} -> Error
- end.
-
-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},
- {packet,2}]) of
- {ok,MainSock} ->
- Starter ! {self(),started},
- request(MainSock,{target_info,init_target_info()}),
- loop(#state{controller={Host,MainSock}});
- Error ->
- Starter ! {self(),{error,
- {could_not_contact_controller,Error}}}
- end.
-
init_target_info() ->
[$.|Emu] = code:objfile_extension(),
{_, OTPRel} = init:script_id(),
@@ -117,171 +75,10 @@ init_target_info() ->
username=test_server_sup:get_username(),
cookie=atom_to_list(erlang:get_cookie())}.
-
-loop(#state{controller={_,MainSock}} = State) ->
- receive
- {tcp, MainSock, <<1,Request/binary>>} ->
- State1 = decode_main(binary_to_term(Request),State),
- loop(State1);
- {tcp_closed, MainSock} ->
- gen_tcp:close(MainSock),
- halt();
- {'EXIT',Pid,Reason} ->
- case lists:keysearch(Pid,1,State#state.jobs) of
- {value,{Pid,Name}} ->
- case Reason of
- normal -> ignore;
- _other -> request(MainSock,{job_proc_killed,Name,Reason})
- end,
- NewJobs = lists:keydelete(Pid,1,State#state.jobs),
- loop(State#state{jobs = NewJobs});
- false ->
- loop(State)
- end
- end.
-
-%% Decode request on main socket
-decode_main({job,Port,Name},#state{controller={Host,_},jobs=Jobs}=State) ->
- S = self(),
- NewJob = spawn_link(fun() -> job(Host,Port,S) end),
- receive {NewJob,started} -> State#state{jobs=[{NewJob,Name}|Jobs]};
- {NewJob,_Error} -> State
- end.
-
init_purify() ->
purify_new_leaks().
-%% Temporary job process on target
-%% This process will live while all test cases in the job are executed.
-%% A socket connection is established with the job process on host.
-job(Host,Port,Starter) ->
- process_flag(trap_exit,true),
- init_purify(),
- case gen_tcp:connect(Host,Port, [binary,
- {reuseaddr,true},
- {packet,4},
- {active,false}]) of
- {ok,JobSock} ->
- Starter ! {self(),started},
- job(JobSock);
- Error ->
- Starter ! {self(),{error,
- {could_not_contact_controller,Error}}}
- end.
-
-job(JobSock) ->
- JobDir = get_jobdir(),
- ok = file:make_dir(JobDir),
- ok = file:make_dir(filename:join(JobDir,?priv_dir)),
- put(test_server_job_sock,JobSock),
- put(test_server_job_dir,JobDir),
- {ok,Cwd} = file:get_cwd(),
- job_loop(JobSock),
- ok = file:set_cwd(Cwd),
- send_privdir(JobDir,JobSock), % also recursively removes jobdir
- ok.
-
-
-get_jobdir() ->
- Now = now(),
- {{Y,M,D},{H,Mi,S}} = calendar:now_to_local_time(Now),
- Basename = io_lib:format("~w-~2.2.0w-~2.2.0w_~2.2.0w.~2.2.0w.~2.2.0w_~w",
- [Y,M,D,H,Mi,S,element(3,Now)]),
- %% if target has a file master, don't use prim_file to look up cwd
- case lists:keymember(master,1,init:get_arguments()) of
- true ->
- {ok,Cwd} = file:get_cwd(),
- Cwd ++ "/" ++ Basename;
- false ->
- filename:absname(Basename)
- end.
-
-send_privdir(JobDir,JobSock) ->
- LocalPrivDir = filename:join(JobDir,?priv_dir),
- case file:list_dir(LocalPrivDir) of
- {ok,List} when List/=[] ->
- Tarfile0 = ?priv_dir ++ ".tar.gz",
- Tarfile = filename:join(JobDir,Tarfile0),
- {ok,Tar} = erl_tar:open(Tarfile,[write,compressed,cooked]),
- ok = erl_tar:add(Tar,LocalPrivDir,?priv_dir,[]),
- ok = erl_tar:close(Tar),
- {ok,TarBin} = file:read_file(Tarfile),
- file:delete(Tarfile),
- ok = del_dir(JobDir),
- request(JobSock,{{privdir,Tarfile0},TarBin});
- _ ->
- ok = del_dir(JobDir),
- request(JobSock,{privdir,empty_priv_dir})
- end.
-
-del_dir(Dir) ->
- case file:read_file_info(Dir) of
- {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);
- {ok,#file_info{}} ->
- ok = file:delete(Dir);
- _r ->
- %% This might be a symlink - let's try to delete it!
- catch file:delete(Dir),
- ok
- end.
-
-%%
-%% Receive and decode request on job socket
-%%
-job_loop(JobSock) ->
- Request = recv(JobSock),
- case decode_job(Request) of
- 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
- code:add_patha(filename:dirname(Which)),
- % End of Patriks uglyness...
- {module,Mod} = code:load_binary(Mod,Which,Beam),
- ok;
-decode_job({{datadir,Tarfile0},Archive}) ->
- JobDir = get(test_server_job_dir),
- Tarfile = filename:join(JobDir,Tarfile0),
- ok = file:write_file(Tarfile,Archive),
- % Cooked is temporary removed/broken
- % ok = erl_tar:extract(Tarfile,[compressed,{cwd,JobDir},cooked]),
- ok = erl_tar:extract(Tarfile,[compressed,{cwd,JobDir}]),
- ok = file:delete(Tarfile),
- ok;
-decode_job({test_case,Case}) ->
- Result = run_test_case_apply(Case),
- JobSock = get(test_server_job_sock),
- request(JobSock,{test_case_result,Result}),
- case test_server_sup:tar_crash_dumps() of
- {error,no_crash_dumps} -> request(JobSock,{crash_dumps,no_crash_dumps});
- {ok,TarFile} ->
- {ok,TarBin} = file:read_file(TarFile),
- file:delete(TarFile),
- request(JobSock,{{crash_dumps,filename:basename(TarFile)},TarBin})
- end,
- ok;
-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) ->
- {stop,stopped}.
-
-%%
-%% **** STOP *** CODE FOR REMOTE TARGET ONLY ***
-%%
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-
-
-
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% cover_compile({App,Include,Exclude,Cross}) ->
%% {ok,AnalyseModules} | {error,Reason}
@@ -298,7 +95,8 @@ decode_job(job_done) ->
%% is found, else {error,application_not_found}.
cover_compile({none,_Exclude,Include,Cross}) ->
- CompileMods = Include++Cross,
+ CrossMods = lists:flatmap(fun({_,M}) -> M end,Cross),
+ CompileMods = Include++CrossMods,
case length(CompileMods) of
0 ->
io:fwrite("WARNING: No modules to cover compile!\n\n",[]),
@@ -312,7 +110,8 @@ cover_compile({none,_Exclude,Include,Cross}) ->
{ok,Include}
end;
cover_compile({App,all,Include,Cross}) ->
- CompileMods = Include++Cross,
+ CrossMods = lists:flatmap(fun({_,M}) -> M end,Cross),
+ CompileMods = Include++CrossMods,
case length(CompileMods) of
0 ->
io:fwrite("WARNING: No modules to cover compile!\n\n",[]),
@@ -330,9 +129,10 @@ cover_compile({App,all,Include,Cross}) ->
{ok,Include}
end;
cover_compile({App,Exclude,Include,Cross}) ->
+ CrossMods = lists:flatmap(fun({_,M}) -> M end,Cross),
case code:lib_dir(App) of
{error,bad_name} ->
- case Include++Cross of
+ case Include++CrossMods of
[] ->
io:format("\nWARNING: Can't find lib_dir for \'~w\'\n"
"Not cover compiling!\n\n",[App]),
@@ -353,7 +153,7 @@ cover_compile({App,Exclude,Include,Cross}) ->
WC = filename:join(EbinDir,"*.beam"),
AllMods = module_names(filelib:wildcard(WC)),
AnalyseMods = (AllMods ++ Include) -- Exclude,
- CompileMods = AnalyseMods ++ Cross,
+ CompileMods = AnalyseMods ++ CrossMods,
case length(CompileMods) of
0 ->
io:fwrite("WARNING: No modules to cover compile!\n\n",[]),
@@ -376,9 +176,7 @@ module_names(Beams) ->
do_cover_compile(Modules) ->
do_cover_compile1(lists:usort(Modules)). % remove duplicates
-do_cover_compile1([Dont|Rest]) when Dont=:=cover;
- Dont=:=test_server;
- Dont=:=test_server_ctrl ->
+do_cover_compile1([Dont|Rest]) when Dont=:=cover ->
do_cover_compile1(Rest);
do_cover_compile1([M|Rest]) ->
case {code:is_sticky(M),code:is_loaded(M)} of
@@ -415,7 +213,7 @@ do_cover_compile1([]) ->
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% cover_analyse(Analyse,Modules) -> [{M,{Cov,NotCov,Details}}]
+%% cover_analyse(Analyse,Modules,Stop) -> [{M,{Cov,NotCov,Details}}]
%%
%% Analyse = {details,Dir} | details | {overview,void()} | overview
%% Modules = [atom()], the modules to analyse
@@ -431,8 +229,19 @@ do_cover_compile1([]) ->
%%
%% Also, if a Dir exists, cover data will be exported to a file called
%% all.coverdata in that directory.
-cover_analyse(Analyse,Modules) ->
- io:fwrite("Cover analysing...\n",[]),
+%%
+%% Finally, if Stop==true, then cover will be stopped after the
+%% analysis is completed. Stopping cover causes the original (non
+%% cover compiled) modules to be loaded back in. If a process at this
+%% point is still running old code of any of the cover compiled
+%% modules, meaning that is has not done any fully qualified function
+%% call after the cover compilation, the process will now be
+%% killed. To avoid this scenario, it is possible to set Stop=false,
+%% which means that the modules will stay cover compiled. Note that
+%% this is only recommended if the erlang node is being terminated
+%% after the test is completed.
+cover_analyse(Analyse,Modules,Stop) ->
+ print(stdout, "Cover analysing...\n", []),
DetailsFun =
case Analyse of
{details,Dir} ->
@@ -482,9 +291,15 @@ cover_analyse(Analyse,Modules) ->
{M,Err}
end
end, Modules),
- Sticky = unstick_all_sticky(node()),
- cover:stop(),
- stick_all_sticky(node(),Sticky),
+
+ case Stop of
+ true ->
+ Sticky = unstick_all_sticky(node()),
+ cover:stop(),
+ stick_all_sticky(node(),Sticky);
+ false ->
+ ok
+ end,
R.
pmap(Fun,List) ->
@@ -501,7 +316,27 @@ pmap(Fun,List) ->
end
end, Pids).
+
+do_cover_for_node(Node,CoverFunc) ->
+ do_cover_for_node(Node,CoverFunc,true).
+do_cover_for_node(Node,CoverFunc,StickUnstick) ->
+ %% In case a slave node is starting another slave node! I.e. this
+ %% function is executed on a slave node - then the cover function
+ %% must be executed on the master node. This is for instance the
+ %% case in test_server's own tests.
+ MainCoverNode = cover:get_main_node(),
+ Sticky =
+ if StickUnstick -> unstick_all_sticky(MainCoverNode,Node);
+ true -> ok
+ end,
+ rpc:call(MainCoverNode,cover,CoverFunc,[Node]),
+ if StickUnstick -> stick_all_sticky(Node,Sticky);
+ true -> ok
+ end.
+
unstick_all_sticky(Node) ->
+ unstick_all_sticky(node(),Node).
+unstick_all_sticky(MainCoverNode,Node) ->
lists:filter(
fun(M) ->
case code:is_sticky(M) of
@@ -512,7 +347,7 @@ unstick_all_sticky(Node) ->
false
end
end,
- cover:modules()).
+ rpc:call(MainCoverNode,cover,modules,[])).
stick_all_sticky(Node,Sticky) ->
lists:foreach(
@@ -537,7 +372,6 @@ stick_all_sticky(Node,Sticky) ->
%% 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.
-%% This might be io requests (which are redirected to the log files).
%%
%% Returns a tuple with the time spent (in seconds) in the test case,
%% the return value from the test case or an {'EXIT',Reason} if the case
@@ -559,7 +393,8 @@ stick_all_sticky(Node,Sticky) ->
%% compensate timetraps for runtime delays introduced by e.g. tools like
%% cover.
-run_test_case_apply({CaseNum,Mod,Func,Args,Name,RunInit,TimetrapData}) ->
+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 ->
@@ -570,38 +405,30 @@ run_test_case_apply({CaseNum,Mod,Func,Args,Name,RunInit,TimetrapData}) ->
end,
test_server_h:testcase({Mod,Func,1}),
ProcBef = erlang:system_info(process_count),
- Result = run_test_case_apply(Mod, Func, Args, Name, RunInit, TimetrapData),
+ 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}.
+-type tc_status() :: 'starting' | 'running' | 'init_per_testcase' |
+ 'end_per_testcase' | {'framework',atom(),atom()} |
+ 'tc'.
+-record(st,
+ {
+ ref :: reference(),
+ pid :: pid(),
+ mf :: {atom(),atom()},
+ status :: tc_status() | 'undefined',
+ ret_val :: term(),
+ comment :: list(char()),
+ timeout :: non_neg_integer() | 'infinity',
+ config :: list() | 'undefined',
+ end_conf_pid :: pid() | 'undefined'
+ }).
+
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, TimetrapData);
- JobDir ->
- %% i'm a remote target
- case Args of
- [Config] when is_list(Config) ->
- {value,{data_dir,HostDataDir}} =
- lists:keysearch(data_dir, 1, Config),
- DataBase = filename:basename(HostDataDir),
- TargetDataDir = filename:join(JobDir, DataBase),
- Config1 = lists:keyreplace(data_dir, 1, Config,
- {data_dir,TargetDataDir}),
- TargetPrivDir = filename:join(JobDir, ?priv_dir),
- Config2 = lists:keyreplace(priv_dir, 1, Config1,
- {priv_dir,TargetPrivDir}),
- do_run_test_case_apply(Mod, Func, [Config2], Name, RunInit,
- TimetrapData);
- _other ->
- do_run_test_case_apply(Mod, Func, Args, Name, RunInit,
- TimetrapData)
- end
- end.
-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) ->
@@ -616,9 +443,6 @@ do_run_test_case_apply(Mod, Func, Args, Name, RunInit, TimetrapData) ->
TCCallback = get(test_server_testcase_callback),
LogOpts = get(test_server_logopts),
Ref = make_ref(),
- OldGLeader = group_leader(),
- %% Set ourself to group leader for the spawned process
- group_leader(self(),self()),
Pid =
spawn_link(
fun() ->
@@ -626,9 +450,10 @@ do_run_test_case_apply(Mod, Func, Args, Name, RunInit, TimetrapData) ->
RunInit, TimetrapData,
LogOpts, TCCallback)
end),
- group_leader(OldGLeader, self()),
put(test_server_detected_fail, []),
- run_test_case_msgloop(Ref, Pid, false, false, "", undefined, starting).
+ St = #st{ref=Ref,pid=Pid,mf={Mod,Func},status=starting,ret_val=[],
+ comment="",timeout=infinity,config=hd(Args)},
+ run_test_case_msgloop(St).
%% Ugly bug (pre R5A):
%% If this process (group leader of the test case) terminates before
@@ -639,32 +464,23 @@ do_run_test_case_apply(Mod, Func, Args, Name, RunInit, TimetrapData) ->
%% 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, CurrConf, Status) ->
- %% NOTE: Keep job_proxy_msgloop/0 up to date when changes
- %% are made in this function!
- {Timeout,ReturnValue} =
- case Terminate of
- {true, ReturnVal} ->
- %% stop any timetrap timers for the test case
- %% that have been started by this process
- timetrap_cancel_all(Pid, false),
- {20, ReturnVal};
- false ->
- {infinity, should_never_appear}
- end,
+run_test_case_msgloop(#st{ref=Ref,pid=Pid,end_conf_pid=EndConfPid0}=St0) ->
receive
- {test_case_initialized,Pid} ->
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,
- Comment,CurrConf,running);
- Abort = {abort_current_testcase,_,_} when Status == starting ->
+ {set_tc_state=Tag,From,{Status,Config0}} ->
+ Config = case Config0 of
+ unknown -> St0#st.config;
+ _ -> Config0
+ end,
+ St = St0#st{status=Status,config=Config},
+ From ! {self(),Tag,ok},
+ run_test_case_msgloop(St);
+ {abort_current_testcase,_,_}=Abort when St0#st.status =:= starting ->
%% we're in init phase, must must postpone this operation
%% until test case execution is in progress (or FW:init_tc
%% gets killed)
self() ! Abort,
erlang:yield(),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,
- Comment,CurrConf,Status);
+ run_test_case_msgloop(St0);
{abort_current_testcase,Reason,From} ->
Line = case is_process_alive(Pid) of
true -> get_loc(Pid);
@@ -674,130 +490,49 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate,
exit(Pid,{testcase_aborted,Reason,Line}),
erlang:yield(),
From ! {self(),abort_current_testcase,ok},
- NewComment =
- receive
- {'DOWN', Mon, process, Pid, _} ->
- Comment
- 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) ||
+ St = receive
+ {'DOWN', Mon, process, Pid, _} ->
+ St0
+ 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) ||
S <- string:tokens(Error,
[$\n])]),
- if length(Error1) > 63 ->
- string:substr(Error1,1,60) ++ "...";
- true ->
- Error1
- end
- end,
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,
- NewComment,CurrConf,Status);
- {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,CurrConf,Status);
- {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,CurrConf,Status);
- {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,CurrConf,Status);
- {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,CurrConf,Status);
- {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,CurrConf,Status);
- {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,CurrConf,Status);
- {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,CurrConf,Status);
- {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,CurrConf,Status);
- {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,CurrConf,Status);
- 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,CurrConf,Status);
- {structured_io,ClientPid,Msg} ->
- output(Msg, ClientPid),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,
- Comment,CurrConf,Status);
- {capture,NewCapture} ->
- run_test_case_msgloop(Ref,Pid,NewCapture,Terminate,
- Comment,CurrConf,Status);
+ Comment = if length(Error1) > 63 ->
+ string:substr(Error1,1,60) ++ "...";
+ true ->
+ Error1
+ end,
+ St0#st{comment=Comment}
+ end,
+ run_test_case_msgloop(St);
{sync_apply,From,MFA} ->
sync_local_or_remote_apply(false,From,MFA),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,
- Comment,CurrConf,Status);
+ run_test_case_msgloop(St0);
{sync_apply_proxy,Proxy,From,MFA} ->
sync_local_or_remote_apply(Proxy,From,MFA),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,
- Comment,CurrConf,Status);
- {printout,Detail,Format,Args} ->
- print(Detail,Format,Args),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,
- Comment,CurrConf,Status);
- {comment,NewComment} ->
- NewComment1 = test_server_ctrl:to_string(NewComment),
- NewComment2 = test_server_sup:framework_call(format_comment,
- [NewComment1],
- NewComment1),
- Terminate1 =
- case Terminate of
- {true,{Time,Value,Loc,Opts,_OldComment}} ->
- {true,{Time,Value,mod_loc(Loc),Opts,NewComment2}};
- Other ->
- Other
- end,
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate1,
- NewComment2,CurrConf,Status);
+ run_test_case_msgloop(St0);
+ {comment,NewComment0} ->
+ NewComment1 = test_server_ctrl:to_string(NewComment0),
+ NewComment = test_server_sup:framework_call(format_comment,
+ [NewComment1],
+ NewComment1),
+ run_test_case_msgloop(St0#st{comment=NewComment});
{read_comment,From} ->
- From ! {self(),read_comment,Comment},
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,
- Comment,CurrConf,Status);
- {set_curr_conf,From,NewCurrConf} ->
- From ! {self(),set_curr_conf,ok},
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,
- Comment,NewCurrConf,Status);
- {make_priv_dir,From} when CurrConf == undefined ->
- From ! {self(),make_priv_dir,{error,no_priv_dir_in_config}};
+ From ! {self(),read_comment,St0#st.comment},
+ run_test_case_msgloop(St0);
{make_priv_dir,From} ->
+ Config = case St0#st.config of
+ undefined -> [];
+ Config0 -> Config0
+ end,
Result =
- case proplists:get_value(priv_dir, element(2, CurrConf)) of
+ case proplists:get_value(priv_dir, Config) of
undefined ->
{error,no_priv_dir_in_config};
PrivDir ->
@@ -811,207 +546,63 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate,
end
end,
From ! {self(),make_priv_dir,Result},
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,
- Comment,CurrConf,Status);
+ run_test_case_msgloop(St0);
{'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,undefined,Status);
+ RetVal = {Time/1000000,Value,Loc,Opts},
+ St = setup_termination(RetVal, St0#st{config=undefined}),
+ run_test_case_msgloop(St);
{'EXIT',Pid,Reason} ->
- case Reason of
- {timetrap_timeout,TVal,Loc} ->
- %% convert Loc to form that can be formatted
- case mod_loc(Loc) of
- {FwMod,FwFunc,framework} ->
- %% timout during framework call
- spawn_fw_call(FwMod,FwFunc,CurrConf,Pid,
- {framework_error,{timetrap,TVal}},
- unknown,self()),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,
- Terminate,Comment,
- undefined,Status);
- 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};
- _ ->
- {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,CurrConf,Pid,
- {timetrap_timeout,TVal},
- Loc1,self()),
- undefined
- end,
- run_test_case_msgloop(Ref,Pid,CaptureStdout,
- Terminate,Comment,
- NewCurrConf,Status)
- end;
- {timetrap_timeout,TVal,Loc,InitOrEnd} ->
- case mod_loc(Loc) of
- {FwMod,FwFunc,framework} ->
- %% timout during framework call
- spawn_fw_call(FwMod,FwFunc,CurrConf,Pid,
- {framework_error,{timetrap,TVal}},
- unknown,self());
- Loc1 ->
- {Mod,_Func} = get_mf(Loc1),
- spawn_fw_call(Mod,InitOrEnd,CurrConf,Pid,
- {timetrap_timeout,TVal},
- Loc1,self())
- end,
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,
- Comment,CurrConf,Status);
- {testcase_aborted,ErrorMsg={user_timetrap_error,_},AbortLoc} ->
- %% user timetrap function caused exit
- %% during start of test case
- {Mod,Func} = get_mf(mod_loc(AbortLoc)),
- spawn_fw_call(Mod,Func,CurrConf,Pid,
- ErrorMsg,unknown,self()),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,
- Terminate,Comment,
- undefined,Status);
- {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,CurrConf,Pid,
- {framework_error,ErrorMsg},
- unknown,self()),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,
- Terminate,Comment,
- undefined,Status);
- 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};
- _ ->
- {Mod,Func} = get_mf(Loc1),
- spawn_fw_call(Mod,Func,CurrConf,Pid,
- ErrorMsg,Loc1,self()),
- undefined
- end,
- run_test_case_msgloop(Ref,Pid,CaptureStdout,
- Terminate,Comment,
- NewCurrConf,Status)
- end;
- killed ->
- %% result of an exit(TestCase,kill) call, which is the
- %% only way to abort a testcase process that traps exits
- %% (see abort_current_testcase)
- {Mod,Func} = case CurrConf of
- {MF,_} -> MF;
- _ -> {undefined,undefined}
- end,
- spawn_fw_call(Mod,Func,CurrConf,Pid,
- testcase_aborted_or_killed,
- unknown,self()),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,
- Comment,CurrConf,Status);
- {fw_error,{FwMod,FwFunc,FwError}} ->
- spawn_fw_call(FwMod,FwFunc,CurrConf,Pid,
- {framework_error,FwError},
- unknown,self()),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,
- Comment,CurrConf,Status);
- _Other ->
- %% the testcase has terminated because of Reason (e.g. an exit
- %% because a linked process failed)
- {Mod,Func} = case CurrConf of
- {MF,_} -> MF;
- _ -> {undefined,undefined}
- end,
- spawn_fw_call(Mod,Func,CurrConf,Pid,
- Reason,unknown,self()),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,
- Comment,CurrConf,Status)
- end;
- {EndConfPid,{call_end_conf,Data,_Result}} ->
+ St = handle_tc_exit(Reason, St0),
+ run_test_case_msgloop(St);
+ {EndConfPid0,{call_end_conf,Data,_Result}} ->
+ #st{mf={Mod,Func},config=CurrConf} = St0,
case CurrConf of
- {EndConfPid,{Mod,Func},_Conf} ->
+ _ when is_list(CurrConf) ->
{_Mod,_Func,TCPid,TCExitReason,Loc} = Data,
spawn_fw_call(Mod,Func,CurrConf,TCPid,
TCExitReason,Loc,self()),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,
- Comment,undefined,Status);
+ St = St0#st{config=undefined,end_conf_pid=undefined},
+ run_test_case_msgloop(St);
_ ->
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,
- Comment,CurrConf,Status)
+ run_test_case_msgloop(St0)
end;
{_FwCallPid,fw_notify_done,{T,Value,Loc,Opts,AddToComment}} ->
%% the framework has been notified, we're finished
- RetVal =
- case AddToComment of
- undefined ->
- {T,Value,Loc,Opts,Comment};
- _ ->
- Comment1 =
- if Comment == "" ->
- AddToComment;
- true ->
- Comment ++
- test_server_ctrl:xhtml("<br>",
- "<br />") ++
- AddToComment
- end,
- {T,Value,Loc,Opts,Comment1}
- end,
- run_test_case_msgloop(Ref,Pid,CaptureStdout,{true,RetVal},
- Comment,undefined,Status);
+ RetVal = {T,Value,Loc,Opts},
+ Comment0 = St0#st.comment,
+ Comment = case AddToComment of
+ undefined ->
+ Comment0;
+ _ ->
+ if Comment0 =:= "" ->
+ AddToComment;
+ true ->
+ Comment0 ++
+ test_server_ctrl:xhtml("<br>",
+ "<br />") ++
+ AddToComment
+ end
+ end,
+ St = setup_termination(RetVal, St0#st{comment=Comment,
+ config=undefined}),
+ run_test_case_msgloop(St);
{'EXIT',_FwCallPid,{fw_notify_done,Func,Error}} ->
%% a framework function failed
CB = os:getenv("TEST_SERVER_FRAMEWORK"),
Loc = case CB of
FW when FW =:= false; FW =:= "undefined" ->
- {test_server,Func};
+ [{test_server,Func}];
_ ->
- {list_to_atom(CB),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,undefined,Status);
+ RetVal = {died,{framework_error,Loc,Error},Loc},
+ St = setup_termination(RetVal, St0#st{comment="Framework error",
+ config=undefined}),
+ run_test_case_msgloop(St);
{failed,File,Line} ->
put(test_server_detected_fail,
[{File, Line}| get(test_server_detected_fail)]),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,
- Comment,CurrConf,Status);
+ run_test_case_msgloop(St0);
{user_timetrap,Pid,_TrapTime,StartTime,E={user_timetrap_error,_},_} ->
case update_user_timetraps(Pid, StartTime) of
@@ -1020,8 +611,7 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate,
ignore ->
ok
end,
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,
- Comment,CurrConf,Status);
+ run_test_case_msgloop(St0);
{user_timetrap,Pid,TrapTime,StartTime,ElapsedTime,Scale} ->
%% a user timetrap is triggered, ignore it if new
%% timetrap has been started since
@@ -1036,68 +626,123 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate,
ignore ->
ok
end,
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,
- Comment,CurrConf,Status);
+ run_test_case_msgloop(St0);
{timetrap_cancel_one,Handle,_From} ->
timetrap_cancel_one(Handle, false),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,
- Comment,CurrConf,Status);
+ run_test_case_msgloop(St0);
{timetrap_cancel_all,TCPid,_From} ->
timetrap_cancel_all(TCPid, false),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,
- Comment,CurrConf,Status);
- {get_timetrap_info,TCPid,From} ->
+ run_test_case_msgloop(St0);
+ {get_timetrap_info,From,TCPid} ->
Info = get_timetrap_info(TCPid, false),
From ! {self(),get_timetrap_info,Info},
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,
- Comment,CurrConf,Status);
+ run_test_case_msgloop(St0);
_Other when not is_tuple(_Other) ->
%% ignore anything not generated by test server
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,
- Comment,CurrConf,Status);
+ run_test_case_msgloop(St0);
_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,CurrConf,Status)
- after Timeout ->
- ReturnValue
+ run_test_case_msgloop(St0)
+ after St0#st.timeout ->
+ #st{ret_val=RetVal,comment=Comment} = St0,
+ erlang:append_element(RetVal, Comment)
end.
-run_test_case_msgloop_io(ReplyAs,CaptureStdout,Msg,From,Func) ->
- case Msg of
- {'EXIT',_} ->
- From ! {io_reply,ReplyAs,{error,Func}};
- _ ->
- From ! {io_reply,ReplyAs,ok}
- end,
- if CaptureStdout /= false ->
- CaptureStdout ! {captured,Msg};
- true ->
- ok
- end,
- output({minor,Msg},From).
+setup_termination(RetVal, #st{pid=Pid}=St) ->
+ timetrap_cancel_all(Pid, false),
+ St#st{ret_val=RetVal,timeout=20}.
+
+set_tc_state(State) ->
+ set_tc_state(State,unknown).
+set_tc_state(State, Config) ->
+ tc_supervisor_req(set_tc_state, {State,Config}).
+
+handle_tc_exit(killed, St) ->
+ %% probably the result of an exit(TestCase,kill) call, which is the
+ %% only way to abort a testcase process that traps exits
+ %% (see abort_current_testcase).
+ #st{config=Config,mf={Mod,Func},pid=Pid} = St,
+ Msg = testcase_aborted_or_killed,
+ spawn_fw_call(Mod, Func, Config, Pid, Msg, unknown, self()),
+ St;
+handle_tc_exit({testcase_aborted,{user_timetrap_error,_}=Msg,_}, St) ->
+ #st{config=Config,mf={Mod,Func},pid=Pid} = St,
+ spawn_fw_call(Mod, Func, Config, Pid, Msg, unknown, self()),
+ St;
+handle_tc_exit(Reason, #st{status={framework,FwMod,FwFunc},
+ config=Config,pid=Pid}=St) ->
+ R = case Reason of
+ {timetrap_timeout,TVal,_} ->
+ {timetrap,TVal};
+ {testcase_aborted=E,AbortReason,_} ->
+ {E,AbortReason};
+ {fw_error,{FwMod,FwFunc,FwError}} ->
+ FwError;
+ Other ->
+ Other
+ end,
+ Error = {framework_error,R},
+ spawn_fw_call(FwMod, FwFunc, Config, Pid, Error, unknown, self()),
+ St;
+handle_tc_exit(Reason, #st{status=tc,config=Config0,mf={Mod,Func},pid=Pid}=St)
+ when is_list(Config0) ->
+ {R,Loc1,F} = case Reason of
+ {timetrap_timeout=E,TVal,Loc0} ->
+ {{E,TVal},Loc0,E};
+ {testcase_aborted=E,AbortReason,Loc0} ->
+ Msg = {E,AbortReason},
+ {Msg,Loc0,Msg};
+ Other ->
+ {Other,unknown,Other}
+ end,
+ Timeout = end_conf_timeout(Reason, St),
+ Config = [{tc_status,{failed,F}}|Config0],
+ EndConfPid = call_end_conf(Mod, Func, Pid, R, Loc1, Config, Timeout),
+ St#st{end_conf_pid=EndConfPid};
+handle_tc_exit(Reason, #st{config=Config,mf={Mod,Func0},pid=Pid,
+ status=Status}=St) ->
+ {R,Loc1} = case Reason of
+ {timetrap_timeout=E,TVal,Loc0} ->
+ {{E,TVal},Loc0};
+ {testcase_aborted=E,AbortReason,Loc0} ->
+ {{E,AbortReason},Loc0};
+ Other ->
+ {Other,unknown}
+ end,
+ Func = case Status of
+ init_per_testcase=F -> {F,Func0};
+ end_per_testcase=F -> {F,Func0};
+ _ -> Func0
+ end,
+ spawn_fw_call(Mod, Func, Config, Pid, R, Loc1, self()),
+ St.
-output(Msg,Sender) ->
- local_or_remote_apply({test_server_ctrl,output,[Msg,Sender]}).
+end_conf_timeout({timetrap_timeout,Timeout,_}, _) ->
+ Timeout;
+end_conf_timeout(_, #st{config=Config}) when is_list(Config) ->
+ proplists:get_value(default_timeout, Config, ?DEFAULT_TIMETRAP_SECS*1000);
+end_conf_timeout(_, _) ->
+ ?DEFAULT_TIMETRAP_SECS*1000.
call_end_conf(Mod,Func,TCPid,TCExitReason,Loc,Conf,TVal) ->
- %% Starter is also the group leader process
Starter = self(),
Data = {Mod,Func,TCPid,TCExitReason,Loc},
EndConfProc =
fun() ->
- group_leader(Starter, self()),
+ process_flag(trap_exit,true), % to catch timetraps
Supervisor = self(),
EndConfApply =
fun() ->
+ timetrap(TVal),
case catch apply(Mod,end_per_testcase,[Func,Conf]) of
{'EXIT',Why} ->
timer:sleep(1),
group_leader() ! {printout,12,
- "WARNING! ~p:end_per_testcase(~p, ~p)"
+ "WARNING! "
+ "~p:end_per_testcase(~p, ~p)"
" crashed!\n\tReason: ~p\n",
[Mod,Func,Conf,Why]};
_ ->
@@ -1110,29 +755,26 @@ call_end_conf(Mod,Func,TCPid,TCExitReason,Loc,Conf,TVal) ->
{Pid,end_conf} ->
Starter ! {self(),{call_end_conf,Data,ok}};
{'EXIT',Pid,Reason} ->
- Starter ! {self(),{call_end_conf,Data,{error,Reason}}}
- after TVal ->
- exit(Pid, kill),
group_leader() ! {printout,12,
"WARNING! ~p:end_per_testcase(~p, ~p)"
- " failed!\n\tReason: timetrap timeout"
- " after ~w ms!\n", [Mod,Func,Conf,TVal]},
- Starter ! {self(),{call_end_conf,Data,{error,timeout}}}
+ " failed!\n\tReason: ~p\n",
+ [Mod,Func,Conf,Reason]},
+ Starter ! {self(),{call_end_conf,Data,{error,Reason}}};
+ {'EXIT',_OtherPid,Reason} ->
+ %% Probably the parent - not much to do about that
+ exit(Reason)
end
end,
spawn_link(EndConfProc).
-spawn_fw_call(Mod,{init_per_testcase,Func},_,Pid,{timetrap_timeout,TVal}=Why,
+spawn_fw_call(Mod,{init_per_testcase,Func},CurrConf,Pid,{timetrap_timeout,TVal}=Why,
Loc,SendTo) ->
FwCall =
fun() ->
- %% set group leader so that printouts/comments
- %% from the framework get printed in the logs
- group_leader(SendTo, self()),
Skip = {skip,{failed,{Mod,init_per_testcase,Why}}},
%% if init_per_testcase fails, the test case
%% should be skipped
- case catch do_end_tc_call(Mod,Func, Loc, {Pid,Skip,[[]]}, Why) of
+ case catch do_end_tc_call(Mod,Func, {Pid,Skip,[CurrConf]}, Why) of
{'EXIT',FwEndTCErr} ->
exit({fw_notify_done,end_tc,FwEndTCErr});
_ ->
@@ -1146,22 +788,10 @@ spawn_fw_call(Mod,{init_per_testcase,Func},_,Pid,{timetrap_timeout,TVal}=Why,
spawn_fw_call(Mod,{end_per_testcase,Func},EndConf,Pid,
{timetrap_timeout,TVal}=Why,_Loc,SendTo) ->
- %%! This is a temporary fix that keeps Test Server alive during
- %%! execution of a parallel test case group, when sometimes
- %%! this clause gets called with EndConf == undefined. See OTP-9594
- %%! for more info.
- EndConf1 = if EndConf == undefined ->
- [{tc_status,{failed,{Mod,end_per_testcase,Why}}}];
- true ->
- EndConf
- end,
FwCall =
fun() ->
- %% set group leader so that printouts/comments
- %% from the framework get printed in the logs
- group_leader(SendTo, self()),
{RetVal,Report} =
- case proplists:get_value(tc_status, EndConf1) of
+ case proplists:get_value(tc_status, EndConf) of
undefined ->
E = {failed,{Mod,end_per_testcase,Why}},
{E,E};
@@ -1175,9 +805,9 @@ spawn_fw_call(Mod,{end_per_testcase,Func},EndConf,Pid,
"WARNING! ~p:end_per_testcase(~p, ~p)"
" failed!\n\tReason: timetrap timeout"
" after ~w ms!\n", [Mod,Func,EndConf,TVal]},
- FailLoc = proplists:get_value(tc_fail_loc, EndConf1),
- case catch do_end_tc_call(Mod,Func, FailLoc,
- {Pid,Report,[EndConf1]}, Why) of
+ FailLoc = proplists:get_value(tc_fail_loc, EndConf),
+ case catch do_end_tc_call(Mod,Func,
+ {Pid,Report,[EndConf]}, Why) of
{'EXIT',FwEndTCErr} ->
exit({fw_notify_done,end_tc,FwEndTCErr});
_ ->
@@ -1195,9 +825,6 @@ spawn_fw_call(Mod,{end_per_testcase,Func},EndConf,Pid,
spawn_fw_call(FwMod,FwFunc,_,_Pid,{framework_error,FwError},_,SendTo) ->
FwCall =
fun() ->
- %% set group leader so that printouts/comments
- %% from the framework get printed in the logs
- group_leader(SendTo, self()),
test_server_sup:framework_call(report, [framework_error,
{{FwMod,FwFunc},
FwError}]),
@@ -1213,12 +840,9 @@ spawn_fw_call(FwMod,FwFunc,_,_Pid,{framework_error,FwError},_,SendTo) ->
end,
spawn_link(FwCall);
-spawn_fw_call(Mod,Func,_,Pid,Error,Loc,SendTo) ->
+spawn_fw_call(Mod,Func,CurrConf,Pid,Error,Loc,SendTo) ->
FwCall =
fun() ->
- %% set group leader so that printouts/comments
- %% from the framework get printed in the logs
- group_leader(SendTo, self()),
case catch fw_error_notify(Mod,Func,[],
Error,Loc) of
{'EXIT',FwErrorNotifyErr} ->
@@ -1227,8 +851,8 @@ spawn_fw_call(Mod,Func,_,Pid,Error,Loc,SendTo) ->
_ ->
ok
end,
- Conf = [{tc_status,{failed,timetrap_timeout}}],
- case catch do_end_tc_call(Mod,Func, Loc,
+ Conf = [{tc_status,{failed,timetrap_timeout}}|CurrConf],
+ case catch do_end_tc_call(Mod,Func,
{Pid,Error,[Conf]},Error) of
{'EXIT',FwEndTCErr} ->
exit({fw_notify_done,end_tc,FwEndTCErr});
@@ -1293,83 +917,73 @@ run_test_case_eval(Mod, Func, Args0, Name, Ref, RunInit,
TimetrapData, LogOpts, TCCallback) ->
put(test_server_multiply_timetraps, TimetrapData),
put(test_server_logopts, LogOpts),
- FWInitResult = test_server_sup:framework_call(init_tc,[?pl2a(Mod),Func,Args0],
+ Where = [{Mod,Func}],
+ put(test_server_loc, Where),
+ FWInitResult = test_server_sup:framework_call(init_tc,[Mod,Func,Args0],
{ok,Args0}),
- group_leader() ! {test_case_initialized,self()},
+ set_tc_state(running),
{{Time,Value},Loc,Opts} =
case FWInitResult of
{ok,Args} ->
run_test_case_eval1(Mod, Func, Args, Name, RunInit, TCCallback);
Error = {error,_Reason} ->
- Where = {Mod,Func},
- NewResult = do_end_tc_call(Mod,Func, Where, {Error,Args0},
+ NewResult = do_end_tc_call(Mod,Func, {Error,Args0},
{skip,{failed,Error}}),
{{0,NewResult},Where,[]};
{fail,Reason} ->
Conf = [{tc_status,{failed,Reason}} | hd(Args0)],
- Where = {Mod,Func},
fw_error_notify(Mod, Func, Conf, Reason),
- NewResult = do_end_tc_call(Mod,Func, Where, {{error,Reason},[Conf]},
+ NewResult = do_end_tc_call(Mod,Func, {{error,Reason},[Conf]},
{fail,Reason}),
{{0,NewResult},Where,[]};
Skip = {skip,_Reason} ->
- Where = {Mod,Func},
- NewResult = do_end_tc_call(Mod,Func, Where, {Skip,Args0}, Skip),
+ NewResult = do_end_tc_call(Mod,Func, {Skip,Args0}, Skip),
{{0,NewResult},Where,[]};
{auto_skip,Reason} ->
- Where = {Mod,Func},
- NewResult = do_end_tc_call(Mod,Func, Where, {{skip,Reason},Args0},
+ NewResult = do_end_tc_call(Mod,Func, {{skip,Reason},Args0},
{skip,Reason}),
{{0,NewResult},Where,[]}
end,
exit({Ref,Time,Value,Loc,Opts}).
run_test_case_eval1(Mod, Func, Args, Name, RunInit, TCCallback) ->
- %% save current state in controller loop
- sync_send(group_leader(),set_curr_conf,{{Mod,Func},hd(Args)},
- 5000, fun() -> exit(no_answer_from_group_leader) end),
case RunInit of
run_init ->
- put(test_server_init_or_end_conf,{init_per_testcase,Func}),
- put(test_server_loc, {Mod,{init_per_testcase,Func}}),
+ set_tc_state(init_per_testcase, hd(Args)),
ensure_timetrap(Args),
case init_per_testcase(Mod, Func, Args) of
Skip = {skip,Reason} ->
Line = get_loc(),
- Conf = [{tc_status,{skipped,Reason}}],
- NewRes = do_end_tc_call(Mod,Func, Line, {Skip,[Conf]}, Skip),
+ Conf = [{tc_status,{skipped,Reason}}|hd(Args)],
+ 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}],
- NewRes = do_end_tc_call(Mod,Func, Line, {{skip,Reason},[Conf]},
+ Conf = [{tc_status,{skipped,Reason}},{save_config,SaveCfg}|hd(Args)],
+ NewRes = do_end_tc_call(Mod,Func, {{skip,Reason},[Conf]},
{skip,Reason}),
{{0,NewRes},Line,[]};
FailTC = {fail,Reason} -> % user fails the testcase
EndConf = [{tc_status,{failed,Reason}} | hd(Args)],
fw_error_notify(Mod, Func, EndConf, Reason),
- NewRes = do_end_tc_call(Mod,Func, {Mod,Func},
+ NewRes = do_end_tc_call(Mod,Func,
{{error,Reason},[EndConf]},
FailTC),
- {{0,NewRes},{Mod,Func},[]};
+ {{0,NewRes},[{Mod,Func}],[]};
{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
- sync_send(group_leader(),set_curr_conf,{{Mod,Func},NewConf1},
- 5000, fun() -> exit(no_answer_from_group_leader) end),
- put(test_server_loc, {Mod,Func}),
+ set_tc_state(tc, NewConf1),
%% execute the test case
{{T,Return},Loc} = {ts_tc(Mod, Func, [NewConf1]),get_loc()},
{EndConf,TSReturn,FWReturn} =
case Return of
{E,TCError} when E=='EXIT' ; E==failed ->
- ModLoc = mod_loc(Loc),
fw_error_notify(Mod, Func, NewConf1,
- TCError, ModLoc),
+ TCError, Loc),
{[{tc_status,{failed,TCError}},
- {tc_fail_loc,ModLoc}|NewConf1],
+ {tc_fail_loc,Loc}|NewConf1],
Return,{error,TCError}};
SaveCfg={save_config,_} ->
{[{tc_status,ok},SaveCfg|NewConf1],Return,ok};
@@ -1386,8 +1000,6 @@ run_test_case_eval1(Mod, Func, Args, Name, RunInit, TCCallback) ->
%% call user callback function if defined
EndConf1 = user_callback(TCCallback, Mod, Func, 'end', EndConf),
%% update current state in controller loop
- sync_send(group_leader(),set_curr_conf,EndConf1, 5000,
- fun() -> exit(no_answer_from_group_leader) end),
{FWReturn1,TSReturn1,EndConf2} =
case end_per_testcase(Mod, Func, EndConf1) of
SaveCfg1={save_config,_} ->
@@ -1407,24 +1019,21 @@ run_test_case_eval1(Mod, Func, Args, Name, RunInit, TCCallback) ->
{FWReturn,TSReturn,EndConf1}
end,
%% clear current state in controller loop
- sync_send(group_leader(),set_curr_conf,undefined,
- 5000, fun() -> exit(no_answer_from_group_leader) end),
- put(test_server_init_or_end_conf,undefined),
- case do_end_tc_call(Mod,Func, Loc,
+ 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},[]};
+ {{T,NewReturn},[{Mod,Func}],[]};
NewReturn ->
{{T,NewReturn},Loc,[]}
end
end;
skip_init ->
+ set_tc_state(running, hd(Args)),
%% call user callback function if defined
Args1 = user_callback(TCCallback, Mod, Func, init, Args),
ensure_timetrap(Args1),
%% ts_tc does a catch
- put(test_server_loc, {Mod,Func}),
%% if this is a named conf group, the test case (init or end conf)
%% should be called with the name as the first argument
Args2 = if Name == undefined -> Args1;
@@ -1435,47 +1044,16 @@ run_test_case_eval1(Mod, Func, Args, Name, RunInit, TCCallback) ->
%% call user callback function if defined
Return1 = user_callback(TCCallback, Mod, Func, 'end', Return),
{Return2,Opts} = process_return_val([Return1], Mod, Func,
- Args1, {Mod,Func}, Return1),
+ Args1, [{Mod,Func}], Return1),
{{T,Return2},Loc,Opts}
end.
-do_end_tc_call(M,F, Loc, Res, Return) ->
- IsSuite = case lists:reverse(atom_to_list(M)) of
- [$E,$T,$I,$U,$S,$_|_] -> true;
- _ -> false
- end,
+do_end_tc_call(Mod, Func, Res, Return) ->
FwMod = os:getenv("TEST_SERVER_FRAMEWORK"),
- {Mod,Func} =
- if FwMod == M ; FwMod == "undefined"; FwMod == false ->
- {M,F};
- (not IsSuite) and is_list(Loc) and (length(Loc)>1) ->
- %% If failure in other module (M) than suite, try locate
- %% suite name in Loc list and call end_tc with Suite:TestCase
- %% instead of M:F.
- GetSuite = fun(S,TC) ->
- case lists:reverse(atom_to_list(S)) of
- [$E,$T,$I,$U,$S,$_|_] -> [{S,TC}];
- _ -> []
- end
- end,
- case lists:flatmap(fun({S,TC,_}) -> GetSuite(S,TC);
- ({{S,TC},_}) -> GetSuite(S,TC);
- ({S,TC}) -> GetSuite(S,TC);
- (_) -> []
- end, Loc) of
- [] ->
- {M,F};
- [FoundSuite|_] ->
- FoundSuite
- end;
- true ->
- {M,F}
- end,
-
Ref = make_ref(),
if FwMod == "ct_framework" ; FwMod == "undefined"; FwMod == false ->
case test_server_sup:framework_call(
- end_tc, [?pl2a(Mod),Func,Res, Return], ok) of
+ end_tc, [Mod,Func,Res, Return], ok) of
{fail,FWReason} ->
{failed,FWReason};
ok ->
@@ -1490,7 +1068,7 @@ do_end_tc_call(M,F, Loc, Res, Return) ->
end;
true ->
case test_server_sup:framework_call(FwMod, end_tc,
- [?pl2a(Mod),Func,Res], Ref) of
+ [Mod,Func,Res], Ref) of
{fail,FWReason} ->
{failed,FWReason};
_Else ->
@@ -1513,7 +1091,7 @@ process_return_val([Return], M,F,A, Loc, Final) when is_list(Return) ->
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
- case do_end_tc_call(M, F, Loc, {ok,A}, Return) of
+ case do_end_tc_call(M, F, {ok,A}, Return) of
{failed, FWReason} = Failed ->
fw_error_notify(M,F,A, FWReason),
{Failed, []};
@@ -1529,9 +1107,9 @@ process_return_val(Return, M,F,A, Loc, Final) ->
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)),
- case do_end_tc_call(M,F, Loc, {{error,TCError},
- [[{tc_status,{failed,TCError}}|Args]]},
+ fw_error_notify(M,F,A, TCError, Loc),
+ case do_end_tc_call(M,F, {{error,TCError},
+ [[{tc_status,{failed,TCError}}|Args]]},
Failed) of
{failed,FWReason} ->
{{failed,FWReason},SaveOpts};
@@ -1549,8 +1127,8 @@ process_return_val1([RetVal={Tag,_}|Opts], M,F,A, Loc, _, SaveOpts) when Tag==sk
process_return_val1(Opts, M,F,A, Loc, RetVal, SaveOpts);
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) ->
- case do_end_tc_call(M,F, Loc, {Final,A}, Final) of
+process_return_val1([], M,F,A, _Loc, Final, SaveOpts) ->
+ case do_end_tc_call(M,F, {Final,A}, Final) of
{failed,FWReason} ->
{{failed,FWReason},SaveOpts};
NewReturn ->
@@ -1616,7 +1194,7 @@ do_init_per_testcase(Mod, Args) ->
throw:Other ->
set_loc(erlang:get_stacktrace()),
Line = get_loc(),
- FormattedLoc = test_server_sup:format_loc(mod_loc(Line)),
+ FormattedLoc = test_server_sup:format_loc(Line),
group_leader() ! {printout,12,
"ERROR! init_per_testcase thrown!\n"
"\tLocation: ~s\n\tReason: ~p\n",
@@ -1627,7 +1205,7 @@ do_init_per_testcase(Mod, Args) ->
Reason = {Reason0,Stk},
set_loc(Stk),
Line = get_loc(),
- FormattedLoc = test_server_sup:format_loc(mod_loc(Line)),
+ FormattedLoc = test_server_sup:format_loc(Line),
group_leader() ! {printout,12,
"ERROR! init_per_testcase crashed!\n"
"\tLocation: ~s\n\tReason: ~p\n",
@@ -1650,8 +1228,7 @@ end_per_testcase(Mod, Func, Conf) ->
end.
do_end_per_testcase(Mod,EndFunc,Func,Conf) ->
- put(test_server_init_or_end_conf,{EndFunc,Func}),
- put(test_server_loc, {Mod,{EndFunc,Func}}),
+ set_tc_state(end_per_testcase, Conf),
try Mod:EndFunc(Func, Conf) of
{save_config,_}=SaveCfg ->
SaveCfg;
@@ -1675,8 +1252,7 @@ do_end_per_testcase(Mod,EndFunc,Func,Conf) ->
"Reason: ~p\n"
"Line: ~s\n",
[EndFunc, Other,
- test_server_sup:format_loc(
- mod_loc(get_loc()))]},
+ test_server_sup:format_loc(get_loc())]},
{failed,{Mod,end_per_testcase,Other}};
Class:Reason ->
Stk = erlang:get_stacktrace(),
@@ -1698,8 +1274,7 @@ do_end_per_testcase(Mod,EndFunc,Func,Conf) ->
"Reason: ~p\n"
"Line: ~s\n",
[EndFunc, Reason,
- test_server_sup:format_loc(
- mod_loc(get_loc()))]},
+ test_server_sup:format_loc(get_loc())]},
{failed,{Mod,end_per_testcase,Why}}
end.
@@ -1712,73 +1287,26 @@ get_loc(Pid) ->
lists:foreach(fun({Key,Val}) -> put(Key, Val) end, Dict),
Stk = [rewrite_loc_item(Loc) || Loc <- Stk0],
case get(test_server_loc) of
- undefined ->
- put(test_server_loc, Stk);
- {Suite,Case} ->
+ [{Suite,Case}] ->
%% location info unknown, check if {Suite,Case,Line}
%% is available in stacktrace. and if so, use stacktrace
- %% instead of currect test_server_loc
+ %% instead of current test_server_loc
case [match || {S,C,_L} <- Stk, S == Suite, C == Case] of
[match|_] -> put(test_server_loc, Stk);
_ -> ok
end;
_ ->
- ok
+ put(test_server_loc, Stk)
end,
get_loc().
-%% find the latest known Suite:Testcase
-get_mf(MFs) ->
- get_mf(MFs, {undefined,undefined}).
-
-get_mf([MF|MFs], _Found) when is_tuple(MF) ->
- ModFunc = {Mod,_} = case MF of
- {M,F,_} -> {M,F};
- MF -> MF
- end,
- case is_suite(Mod) of
- true -> ModFunc;
- false -> get_mf(MFs, ModFunc)
- end;
-get_mf(_, Found) ->
- Found.
-
-is_suite(Mod) ->
- case lists:reverse(atom_to_list(Mod)) of
- "ETIUS" ++ _ -> true;
- _ -> false
- end.
-
-mod_loc(Loc) ->
- %% handle diff line num versions
- case Loc of
- [{{_M,_F},_L}|_] ->
- [begin if L /= 0 -> {?pl2a(M),F,L};
- true -> {?pl2a(M),F} end end || {{M,F},L} <- Loc];
- [{_M,_F}|_] ->
- [{?pl2a(M),F} || {M,F} <- Loc];
- {{M,F},0} ->
- [{?pl2a(M),F}];
- {{M,F},L} ->
- [{?pl2a(M),F,L}];
- {M,ForL} ->
- [{?pl2a(M),ForL}];
- {M,F,0} ->
- [{M,F}];
- [{M,F,0}|Stack] ->
- [{M,F}|Stack];
- _ ->
- Loc
- end.
-
-
fw_error_notify(Mod, Func, Args, Error) ->
test_server_sup:framework_call(error_notification,
- [?pl2a(Mod),Func,[Args],
+ [Mod,Func,[Args],
{Error,unknown}]).
fw_error_notify(Mod, Func, Args, Error, Loc) ->
test_server_sup:framework_call(error_notification,
- [?pl2a(Mod),Func,[Args],
+ [Mod,Func,[Args],
{Error,Loc}]).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -1793,10 +1321,10 @@ fw_error_notify(Mod, Func, Args, Error, Loc) ->
%% 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]}).
+ test_server_ctrl:print(Detail, Format, Args).
print(Detail,Format,Args,Printer) ->
- local_or_remote_apply({test_server_ctrl,print,[Detail,Format,Args,Printer]}).
+ test_server_ctrl:print(Detail, Format, Args, Printer).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% print_timsteamp(Detail,Leader) -> ok
@@ -1806,7 +1334,7 @@ print(Detail,Format,Args,Printer) ->
%% log files.
print_timestamp(Detail,Leader) ->
- local_or_remote_apply({test_server_ctrl,print_timestamp,[Detail,Leader]}).
+ test_server_ctrl:print_timestamp(Detail, Leader).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -1854,7 +1382,12 @@ ts_tc(M, F, A) ->
{Elapsed, Result}.
set_loc(Stk) ->
- Loc = [rewrite_loc_item(I) || {_,_,_,_}=I <- Stk],
+ Loc = case [rewrite_loc_item(I) || {_,_,_,_}=I <- Stk] of
+ [{M,F,0}|Stack] ->
+ [{M,F}|Stack];
+ Other ->
+ Other
+ end,
put(test_server_loc, Loc).
rewrite_loc_item({M,F,_,Loc}) ->
@@ -1868,16 +1401,6 @@ rewrite_loc_item({M,F,_,Loc}) ->
%% Note: Some of these functions have been moved to test_server_sup %%
%% in an attempt to keep this modules small (yeah, right!) %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-unicode_to_latin1(Chars) when is_list(Chars); is_binary(Chars) ->
- lists:flatten(
- [ case X of
- High when High > 255 ->
- io_lib:format("\\{~.8B}",[X]);
- Low ->
- Low
- end || X <- unicode:characters_to_list(Chars,unicode) ]);
-unicode_to_latin1(Garbage) ->
- Garbage.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% format(Format) -> IoLibReturn
@@ -1959,6 +1482,13 @@ messages_get() ->
test_server_sup:messages_get([]).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% permit_io(GroupLeader, FromPid) -> ok
+%%
+%% Make sure proceeding IO from FromPid won't get rejected
+permit_io(GroupLeader, FromPid) ->
+ GroupLeader ! {permit_io,FromPid}.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% sleep(Time) -> ok
%% Time = integer() | float() | infinity
%%
@@ -2061,31 +1591,40 @@ fail() ->
%% Break a test case so part of the test can be done manually.
%% Use continue/0 to continue.
break(Comment) ->
- case erase(test_server_timetraps) of
- undefined -> ok;
- List -> lists:foreach(fun({Ref,_,_}) ->
- timetrap_cancel(Ref)
- end, List)
- end,
+ break(?MODULE, Comment).
+
+break(CBM, Comment) ->
+ break(CBM, '', Comment).
+
+break(CBM, TestCase, Comment) ->
+ timetrap_cancel(),
+ {TCName,CntArg,PName} =
+ if TestCase == '' ->
+ {"", "", test_server_break_process};
+ true ->
+ Str = atom_to_list(TestCase),
+ {[32 | Str], Str,
+ list_to_atom("test_server_break_process_" ++ Str)}
+ end,
io:format(user,
"\n\n\n--- SEMIAUTOMATIC TESTING ---"
- "\nThe test case executes on process ~w"
+ "\nThe test case~s executes on process ~w"
"\n\n\n~s"
"\n\n\n-----------------------------\n\n"
- "Continue with --> test_server:continue().\n",
- [self(),Comment]),
- case whereis(test_server_break_process) of
+ "Continue with --> ~w:continue(~s).\n",
+ [TCName,self(),Comment,CBM,CntArg]),
+ case whereis(PName) of
undefined ->
- spawn_break_process(self());
+ spawn_break_process(self(), PName);
OldBreakProcess ->
OldBreakProcess ! cancel,
- spawn_break_process(self())
+ spawn_break_process(self(), PName)
end,
receive continue -> ok end.
-spawn_break_process(Pid) ->
+spawn_break_process(Pid, PName) ->
spawn(fun() ->
- register(test_server_break_process,self()),
+ register(PName, self()),
receive
continue -> continue(Pid);
cancel -> ok
@@ -2094,13 +1633,19 @@ spawn_break_process(Pid) ->
continue() ->
case whereis(test_server_break_process) of
- undefined ->
- ok;
- BreakProcess ->
- BreakProcess ! continue
+ undefined -> ok;
+ BreakProcess -> BreakProcess ! continue
end.
-continue(Pid) ->
+continue(TestCase) when is_atom(TestCase) ->
+ PName = list_to_atom("test_server_break_process_" ++
+ atom_to_list(TestCase)),
+ case whereis(PName) of
+ undefined -> ok;
+ BreakProcess -> BreakProcess ! continue
+ end;
+
+continue(Pid) when is_pid(Pid) ->
Pid ! continue.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -2108,28 +1653,19 @@ continue(Pid) ->
%%
%% Returns the amount to scale timetraps with.
+%% {X, fun() -> check() end} <- multiply scale with X if Fun() is true
timetrap_scale_factor() ->
- F0 = case test_server:purify_is_running() of
- true -> 5;
- false -> 1
- end,
- F1 = case {is_debug(), has_lock_checking()} of
- {true,_} -> 6 * F0;
- {false,true} -> 2 * F0;
- {false,false} -> F0
- end,
- F2 = case has_superfluous_schedulers() of
- true -> 3*F1;
- false -> F1
- end,
- F = case test_server_sup:get_os_family() of
- vxworks -> 5 * F2;
- _ -> F2
- end,
- case test_server:is_cover() of
- true -> 10 * F;
- false -> F
- end.
+ timetrap_scale_factor([
+ { 2, fun() -> has_lock_checking() end},
+ { 3, fun() -> has_superfluous_schedulers() end},
+ { 5, fun() -> purify_is_running() end},
+ { 6, fun() -> is_debug() end},
+ {10, fun() -> is_cover() end}
+ ]).
+
+timetrap_scale_factor(Scales) ->
+ %% The fun in {S, Fun} a filter input to the list comprehension
+ lists:foldl(fun(S,O) -> O*S end, 1, [ S || {S,F} <- Scales, F()]).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -2457,11 +1993,7 @@ get_timetrap_info(TCPid, SendToServer) ->
[I|_] ->
I;
[] when SendToServer == true ->
- MsgLooper = group_leader(),
- MsgLooper ! {get_timetrap_info,TCPid,self()},
- receive
- {MsgLooper,get_timetrap_info,I} -> I
- end;
+ tc_supervisor_req({get_timetrap_info,TCPid});
[] ->
undefined
end
@@ -2480,17 +2012,29 @@ hours(N) -> trunc(N * 1000 * 60 * 60).
minutes(N) -> trunc(N * 1000 * 60).
seconds(N) -> trunc(N * 1000).
-
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% sync_send(Pid,Tag,Msg,Timeout,DoAfter) -> Result
+%% tc_supervisor_req(Tag) -> Result
+%% tc_supervisor_req(Tag, Msg) -> Result
%%
-sync_send(Pid,Tag,Msg,Timeout,DoAfter) ->
+
+tc_supervisor_req(Tag) ->
+ Pid = test_server_gl:get_tc_supervisor(group_leader()),
+ Pid ! {Tag,self()},
+ receive
+ {Pid,Tag,Result} ->
+ Result
+ after 5000 ->
+ error(no_answer_from_tc_supervisor)
+ end.
+
+tc_supervisor_req(Tag, Msg) ->
+ Pid = test_server_gl:get_tc_supervisor(group_leader()),
Pid ! {Tag,self(),Msg},
receive
{Pid,Tag,Result} ->
Result
- after Timeout ->
- DoAfter()
+ after 5000 ->
+ error(no_answer_from_tc_supervisor)
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -2650,9 +2194,9 @@ 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
+ Cover = case is_cover(Node) of
true ->
- not is_shielded(Name) andalso same_version(Node);
+ proplists:get_value(start_cover,Options,true);
false ->
false
end,
@@ -2660,9 +2204,7 @@ start_node(Name, Type, Options) ->
net_adm:ping(Node),
case Cover of
true ->
- Sticky = unstick_all_sticky(Node),
- cover:start(Node),
- stick_all_sticky(Node,Sticky);
+ do_cover_for_node(Node,start);
_ ->
ok
end,
@@ -2690,7 +2232,20 @@ wait_for_node(Slave) ->
group_leader() ! {sync_apply,
self(),
{test_server_ctrl,wait_for_node,[Slave]}},
- receive {sync_result,R} -> R end.
+ Result = receive {sync_result,R} -> R end,
+ case Result of
+ ok ->
+ net_adm:ping(Slave),
+ case is_cover(Slave) of
+ true ->
+ do_cover_for_node(Slave,start);
+ _ ->
+ ok
+ end;
+ _ ->
+ ok
+ end,
+ Result.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -2699,14 +2254,9 @@ wait_for_node(Slave) ->
%% Kills a (remote) node.
%% Also inform test_server_ctrl so it can clean up!
stop_node(Slave) ->
- Nocover = is_shielded(Slave) orelse not same_version(Slave),
- case is_cover() of
- true when not Nocover ->
- Sticky = unstick_all_sticky(Slave),
- cover:stop(Slave),
- stick_all_sticky(Slave,Sticky);
- _ ->
- ok
+ Cover = is_cover(Slave),
+ if Cover -> do_cover_for_node(Slave,flush,false);
+ true -> ok
end,
group_leader() ! {sync_apply,self(),{test_server_ctrl,stop_node,[Slave]}},
Result = receive {sync_result,R} -> R end,
@@ -2718,10 +2268,15 @@ stop_node(Slave) ->
{nodedown, Slave} ->
format(minor, "Stopped slave node: ~p", [Slave]),
format(major, "=node_stop ~p", [Slave]),
+ if Cover -> do_cover_for_node(Slave,stop,false);
+ true -> ok
+ end,
true
after 30000 ->
format("=== WARNING: Node ~p does not seem to terminate.",
[Slave]),
+ erlang:monitor_node(Slave, false),
+ receive {nodedown, Slave} -> ok after 0 -> ok end,
false
end;
{error, _Reason} ->
@@ -2733,9 +2288,27 @@ stop_node(Slave) ->
[Slave]),
case net_adm:ping(Slave)of
pong ->
+ erlang:monitor_node(Slave, true),
slave:stop(Slave),
- true;
+ receive
+ {nodedown, Slave} ->
+ format(minor, "Stopped slave node: ~p", [Slave]),
+ format(major, "=node_stop ~p", [Slave]),
+ if Cover -> do_cover_for_node(Slave,stop,false);
+ true -> ok
+ end,
+ true
+ after 30000 ->
+ format("=== WARNING: Node ~p does not seem to terminate.",
+ [Slave]),
+ erlang:monitor_node(Slave, false),
+ receive {nodedown, Slave} -> ok after 0 -> ok end,
+ false
+ end;
pang ->
+ if Cover -> do_cover_for_node(Slave,stop,false);
+ true -> ok
+ end,
false
end
end.
@@ -2822,6 +2395,14 @@ same_version(Name) ->
OtherVersion = rpc:call(Name, erlang, system_info, [version]),
ThisVersion =:= OtherVersion.
+is_cover(Name) ->
+ case is_cover() of
+ true ->
+ not is_shielded(Name) andalso same_version(Name);
+ false ->
+ false
+ end.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% temp_name(Stem) -> string()
%% Stem = string()
@@ -2885,13 +2466,7 @@ comment(String) ->
%% Read the current comment string stored in
%% state during test case execution.
read_comment() ->
- MsgLooper = group_leader(),
- MsgLooper ! {read_comment,self()},
- receive
- {MsgLooper,read_comment,Comment} -> Comment
- after
- 5000 -> ""
- end.
+ tc_supervisor_req(read_comment).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% make_priv_dir() -> ok
@@ -2899,13 +2474,7 @@ read_comment() ->
%% Order test server to create the private directory
%% for the current test case.
make_priv_dir() ->
- MsgLooper = group_leader(),
- group_leader() ! {make_priv_dir,self()},
- receive
- {MsgLooper,make_priv_dir,Result} -> Result
- after
- 5000 -> error
- end.
+ tc_supervisor_req(make_priv_dir).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% os_type() -> OsType
@@ -2913,7 +2482,7 @@ make_priv_dir() ->
%% Returns the OsType of the target node. OsType is
%% the same as returned from os:type()
os_type() ->
- test_server_ctrl:get_target_os_type().
+ os:type().
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -3032,47 +2601,9 @@ purify_format(Format, Args) ->
%%
%% Generic send functions for communication with host
%%
-sync_local_or_remote_apply(Proxy,From,{M,F,A} = MFA) ->
- case get(test_server_job_sock) of
- undefined ->
- %% i'm a local target
- Result = apply(M,F,A),
- if is_pid(Proxy) -> Proxy ! {sync_result_proxy,From,Result};
- true -> From ! {sync_result,Result}
- end;
- JobSock ->
- %% i'm a remote target
- request(JobSock,{sync_apply,MFA}),
- {sync_result,Result} = recv(JobSock),
- if is_pid(Proxy) -> Proxy ! {sync_result_proxy,From,Result};
- true -> From ! {sync_result,Result}
- end
- end.
-local_or_remote_apply({M,F,A} = MFA) ->
- case get(test_server_job_sock) of
- undefined ->
- %% i'm a local target
- apply(M,F,A),
- ok;
- JobSock ->
- %% i'm a remote target
- request(JobSock,{apply,MFA}),
- ok
- end.
-
-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} ->
- gen_tcp:close(Sock),
- exit(connection_lost);
- {ok,<<1,Request/binary>>} ->
- binary_to_term(Request);
- {ok,<<0,B/binary>>} ->
- B
+sync_local_or_remote_apply(Proxy, From, {M,F,A}) ->
+ %% i'm a local target
+ Result = apply(M, F, A),
+ if is_pid(Proxy) -> Proxy ! {sync_result_proxy,From,Result};
+ true -> From ! {sync_result,Result}
end.
diff --git a/lib/test_server/src/test_server_ctrl.erl b/lib/test_server/src/test_server_ctrl.erl
index 5ed296d215..c5c57426b4 100644
--- a/lib/test_server/src/test_server_ctrl.erl
+++ b/lib/test_server/src/test_server_ctrl.erl
@@ -34,118 +34,6 @@
%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% 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)
-%% executed on the target machine. Host and target communicates over
-%% 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)
-%% |
-%% |
-%% -----
-%% | | JobProc
-%% ----- (test_server_ctrl.erl and test_server.erl)
-%% |
-%% |
-%% -----
-%% | | 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'
-%% 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.
-%% 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)
-%% | |
-%% | |
-%% ----- JobSock -----
-%% JobProcH | |- - - - - - -| | JobProcT
-%% (test_server_ctrl.erl) ----- ----- (test_server.erl)
-%% |
-%% |
-%% -----
-%% | | 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)
-%% <- {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
-%% (used for cover compiling and analysing)
-%% <-> {sync_result,Result} Return value from sync_apply
-%% <- {test_case_result,Result} When a test case is finished
-%% <- {crash_dumps,Tarfile} When a test case is finished
-%% -> job_done When a job is finished
-%% <- {privdir,Privdir} When a job is finished
-%%
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-
-
%%% SUPERVISOR INTERFACE %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-export([start/0, start/1, start_link/1, stop/0]).
@@ -162,22 +50,20 @@
-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([reject_io_reqs/1, get_levels/0, set_levels/3]).
-export([multiply_timetraps/1, scale_timetraps/1, get_timetrap_parameters/0]).
-export([create_priv_dir/1]).
--export([cover/2, cover/3, cover/7,
- cross_cover_analyse/1, cross_cover_analyse/2, trc/1, stop_trace/0]).
+-export([cover/2, cover/3, cover/8, 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/4, print_timestamp/2]).
+-export([print/2, print/3, print/4, print_timestamp/2]).
-export([start_node/3, stop_node/1, wait_for_node/1, is_release_available/1]).
-export([format/1, format/2, format/3, to_string/1]).
-export([get_target_info/0]).
-export([get_hosts/0]).
--export([get_target_os_type/0]).
-export([node_started/1]).
%%% DEBUGGER INTERFACE %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -201,16 +87,18 @@
-define(data_dir_suffix, "_data/").
-define(suitelog_name, "suite.log").
-define(coverlog_name, "cover.html").
+-define(raw_coverlog_name, "cover.log").
-define(cross_coverlog_name, "cross_cover.html").
+-define(raw_cross_coverlog_name, "cross_cover.log").
+-define(cross_cover_info, "cross_cover.info").
-define(cover_total, "total_cover.log").
+-define(unexpected_io_log, "unexpected_io.log").
-define(last_file, "last_name").
-define(last_link, "last_link").
-define(last_test, "last_test").
-define(html_ext, ".html").
--define(cross_cover_file, "cross.cover").
-define(now, erlang:now()).
--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;
@@ -218,8 +106,9 @@
-define(auto_skip_color, "#FFA64D").
-define(user_skip_color, "#FF8000").
+-define(sortable_table_name, "SortableTable").
--record(state,{jobs=[],levels={1,19,10},
+-record(state,{jobs=[], levels={1,19,10}, reject_io_reqs=false,
multiply_timetraps=1, scale_timetraps=true,
create_priv_dir=auto_per_run, finish=false,
target_info, trc=false, cover=false, wait_for_node=[],
@@ -429,14 +318,6 @@ run_test(CommandLine) ->
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
- #target_info{os_family=vxworks} ->
- receive after 30000 -> ready_to_wait end;
- _ ->
- wait_now
- end,
wait_finish().
%% Converted CoverFile to a string unless it is 'none'
@@ -469,8 +350,7 @@ wait_finish() ->
ok.
abort_current_testcase(Reason) ->
- controller_call({abort_current_testcase,Reason}),
- ok.
+ controller_call({abort_current_testcase,Reason}).
abort() ->
OldTrap = process_flag(trap_exit, true),
@@ -498,6 +378,9 @@ get_levels() ->
set_levels(Show, Major, Minor) ->
controller_call({set_levels,Show,Major,Minor}).
+reject_io_reqs(Bool) ->
+ controller_call({reject_io_reqs,Bool}).
+
multiply_timetraps(N) ->
controller_call({multiply_timetraps,N}).
@@ -524,9 +407,11 @@ cover(App, Analyse) when is_atom(App) ->
cover(CoverFile, Analyse) ->
cover(none, CoverFile, Analyse).
cover(App, CoverFile, Analyse) ->
- controller_call({cover,{App,CoverFile},Analyse}).
-cover(App, CoverFile, Exclude, Include, Cross, Export, Analyse) ->
- controller_call({cover,{App,{CoverFile,Exclude,Include,Cross,Export}},Analyse}).
+ controller_call({cover,{App,CoverFile},Analyse,true}).
+cover(App, CoverFile, Exclude, Include, Cross, Export, Analyse, Stop) ->
+ controller_call({cover,
+ {App,{CoverFile,Exclude,Include,Cross,Export}},
+ Analyse,Stop}).
testcase_callback(ModFunc) ->
controller_call({testcase_callback,ModFunc}).
@@ -540,20 +425,6 @@ kill_slavenodes() ->
get_hosts() ->
get(test_server_hosts).
-get_target_os_type() ->
- case whereis(?MODULE) of
- 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
- #target_info{os_type=OsType} = controller_call(get_target_info),
- OsType
- end.
-
%%--------------------------------------------------------------------
add_job(Name, TopCase) ->
@@ -609,7 +480,7 @@ controller_call(Arg, Timeout) ->
%% Mode 'lazy' ignores (and resets to []) any jobs in the state file
%%
-init([Param]) ->
+init([_]) ->
case os:getenv("TEST_SERVER_CALL_TRACE") of
false ->
ok;
@@ -635,104 +506,14 @@ init([Param]) ->
test_server_sup:cleanup_crash_dumps(),
State = #state{jobs=[],finish=false},
put(test_server_free_targets,[]),
- case contact_main_target(Param) of
- {ok,TI} ->
- ets:new(slave_tab, [named_table,set,public,{keypos,2}]),
- set_hosts([TI#target_info.host]),
- {ok,State#state{target_info=TI}};
- {error,Reason} ->
- {stop,Reason}
- end.
-
-
-%% If the test is to be run at a remote target, this function sets up
-%% a socket communication with the target.
-contact_main_target(local) ->
- %% When used by a general framework, global registration of
- %% test_server should not be required.
- case get_fw_mod(undefined) of
- undefined ->
- %% Local target! The global test_server process implemented 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
- undefined ->
- global:register_name(test_server, self());
- 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;
- _ ->
- ok
- end,
- TI = test_server:init_target_info(),
+ TI0 = test_server:init_target_info(),
TargetHost = test_server_sup:hoststr(),
- {ok,TI#target_info{where=local,
- host=TargetHost,
- naming=naming(),
- master=TargetHost}};
-
-contact_main_target(ParameterFile) ->
- case read_parameters(ParameterFile) of
- {ok,Par} ->
- case test_server_node:start_remote_main_target(Par) of
- {ok,TI} ->
- {ok,TI};
- {error,Error} ->
- {error,{could_not_start_main_target,Error}}
- end;
- {error,Error} ->
- {error,{could_not_read_parameterfile,Error}}
- end.
-
-read_parameters(File) ->
- case file:consult(File) of
- {ok,Data} ->
- read_parameters(lists:flatten(Data), #par{naming=naming()});
- Error ->
- Error
- end.
-read_parameters([{type,Type}|Data], Par) -> % mandatory
- read_parameters(Data, Par#par{type=Type});
-read_parameters([{target,Target}|Data], Par) -> % mandatory
- read_parameters(Data, Par#par{target=cast_to_list(Target)});
-read_parameters([{slavetargets,SlaveTargets}|Data], Par) ->
- read_parameters(Data, Par#par{slave_targets=SlaveTargets});
-read_parameters([{longnames,Bool}|Data], Par) ->
- Naming = if Bool->"-name"; true->"-sname" end,
- read_parameters(Data, Par#par{naming=Naming});
-read_parameters([{master,{Node,Cookie}}|Data], Par) ->
- read_parameters(Data, Par#par{master=cast_to_list(Node),
- cookie=cast_to_list(Cookie)});
-read_parameters([Other|_Data], _Par) ->
- {error,{illegal_parameter,Other}};
-read_parameters([], Par) when Par#par.type==undefined ->
- {error, {missing_mandatory_parameter,type}};
-read_parameters([], Par) when Par#par.target==undefined ->
- {error, {missing_mandatory_parameter,target}};
-read_parameters([], Par0) ->
- Par =
- case {Par0#par.type, Par0#par.master} of
- {ose, undefined} ->
- %% Use this node as master and bootserver for target
- %% and slave nodes
- Par0#par{master = atom_to_list(node()),
- cookie = atom_to_list(erlang:get_cookie())};
- {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}
- end,
- {ok,Par}.
+ TI = TI0#target_info{host=TargetHost,
+ naming=naming(),
+ master=TargetHost},
+ ets:new(slave_tab, [named_table,set,public,{keypos,2}]),
+ set_hosts([TI#target_info.host]),
+ {ok,State#state{target_info=TI}}.
naming() ->
case lists:member($., test_server_sup:hoststr()) of
@@ -799,7 +580,7 @@ handle_call({add_job,Dir,Name,TopCase,Skip}, _From, State) ->
ExtraTools =
case State#state.cover of
false -> [];
- {App,Analyse} -> [{cover,App,Analyse}]
+ {App,Analyse,Stop} -> [{cover,App,Analyse,Stop}]
end,
ExtraTools1 =
case State#state.random_seed of
@@ -815,6 +596,7 @@ handle_call({add_job,Dir,Name,TopCase,Skip}, _From, State) ->
[SpecName,{State#state.multiply_timetraps,
State#state.scale_timetraps}],
LogDir, Name, State#state.levels,
+ State#state.reject_io_reqs,
State#state.create_priv_dir,
State#state.testcase_callback, ExtraTools1),
NewJobs = [{Name,Pid}|State#state.jobs],
@@ -825,6 +607,7 @@ handle_call({add_job,Dir,Name,TopCase,Skip}, _From, State) ->
[SpecList,{State#state.multiply_timetraps,
State#state.scale_timetraps}],
LogDir, Name, State#state.levels,
+ State#state.reject_io_reqs,
State#state.create_priv_dir,
State#state.testcase_callback, ExtraTools1),
NewJobs = [{Name,Pid}|State#state.jobs],
@@ -843,6 +626,7 @@ handle_call({add_job,Dir,Name,TopCase,Skip}, _From, State) ->
{State#state.multiply_timetraps,
State#state.scale_timetraps}],
LogDir, Name, State#state.levels,
+ State#state.reject_io_reqs,
State#state.create_priv_dir,
State#state.testcase_callback, ExtraTools1),
NewJobs = [{Name,Pid}|State#state.jobs],
@@ -975,6 +759,15 @@ handle_call({set_levels,Show,Major,Minor}, _From, State) ->
{reply,ok,State#state{levels={Show,Major,Minor}}};
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% handle_call({reject_io_reqs,Bool}, _, State) -> ok
+%% Bool = bool()
+%%
+%% May be used to switch off stdout printouts to the minor log file
+
+handle_call({reject_io_reqs,Bool}, _From, State) ->
+ {reply,ok,State#state{reject_io_reqs=Bool}};
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% handle_call({multiply_timetraps,N}, _, State) -> ok
%% N = integer() | infinity
%%
@@ -1043,13 +836,13 @@ handle_call(stop_trace, _From, State) ->
{reply,R,State#state{trc=false}};
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% handle_call({cover,App,Analyse}, _, State) -> ok | {error,Reason}
+%% handle_call({cover,App,Analyse,Stop}, _, State) -> ok | {error,Reason}
%%
%% All modules inn application App are cover compiled
%% Analyse indicates on which level the coverage should be analysed
-handle_call({cover,App,Analyse}, _From, State) ->
- {reply,ok,State#state{cover={App,Analyse}}};
+handle_call({cover,App,Analyse,Stop}, _From, State) ->
+ {reply,ok,State#state{cover={App,Analyse,Stop}}};
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% handle_call({create_priv_dir,Value}, _, State) -> ok | {error,Reason}
@@ -1201,25 +994,17 @@ handle_cast({node_started,Node}, State) ->
%% Pid = pid()
%% Reason = term()
%%
-%% Handles exit messages from linked processes. Only test suites and
-%% 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
-%% will do the cleanup
+%% Handles exit messages from linked processes. Only test suites 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 will do the cleanup
handle_info({'EXIT',Pid,Reason}, State) ->
case lists:keysearch(Pid,2,State#state.jobs) of
false ->
- TI = State#state.target_info,
- case TI#target_info.target_client of
- Pid ->
- %% The target client died - lost contact with target
- {stop,{lost_contact_with_target,Reason},State};
- _other ->
- %% not our problem
- {noreply,State}
- end;
+ %% not our problem
+ {noreply,State};
{value,{Name,_}} ->
NewJobs = lists:keydelete(Pid, 2, State#state.jobs),
case Reason of
@@ -1294,14 +1079,8 @@ handle_info({tcp_closed,Sock}, State=#state{trc=Sock}) ->
%%! Maybe print something???
{noreply,State#state{trc=false}};
handle_info({tcp_closed,Sock}, State) ->
- case test_server_node:nodedown(Sock,State#state.target_info) of
- target_died ->
- %% terminate/2 will do the cleanup
- {stop,target_died,State};
- _ ->
- {noreply,State}
- end;
-
+ test_server_node:nodedown(Sock, State#state.target_info),
+ {noreply,State};
handle_info(_, State) ->
%% dummy; accept all, do nothing.
{noreply, State}.
@@ -1340,14 +1119,15 @@ kill_all_jobs([]) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% spawn_tester(Mod, Func, Args, Dir, Name, Levels, CreatePrivDir,
-%% TestCaseCallback, ExtraTools) -> Pid
+%% spawn_tester(Mod, Func, Args, Dir, Name, Levels, RejectIoReqs,
+%% CreatePrivDir, TestCaseCallback, ExtraTools) -> Pid
%% Mod = atom()
%% Func = atom()
%% Args = [term(),...]
%% Dir = string()
%% Name = string()
%% Levels = {integer(),integer(),integer()}
+%% RejectIoReqs = bool()
%% CreatePrivDir = auto_per_run | manual_per_tc | auto_per_tc
%% TestCaseCallback = {CBMod,CBFunc} | undefined
%% ExtraTools = [ExtraTool,...]
@@ -1359,24 +1139,23 @@ kill_all_jobs([]) ->
%% 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,
+spawn_tester(Mod, Func, Args, Dir, Name, Levels, RejectIoReqs,
CreatePrivDir, TCCallback, ExtraTools) ->
- spawn_link(
- fun() -> init_tester(Mod, Func, Args, Dir, Name, Levels,
+ spawn_link(fun() ->
+ init_tester(Mod, Func, Args, Dir, Name, Levels, RejectIoReqs,
CreatePrivDir, TCCallback, ExtraTools)
end).
-init_tester(Mod, Func, Args, Dir, Name, {SumLev,MajLev,MinLev},
- CreatePrivDir, TCCallback, ExtraTools) ->
+init_tester(Mod, Func, Args, Dir, Name, {_,_,MinLev}=Levels,
+ RejectIoReqs, CreatePrivDir, TCCallback, ExtraTools) ->
process_flag(trap_exit, true),
+ test_server_io:start_link(),
put(test_server_name, Name),
put(test_server_dir, Dir),
put(test_server_total_time, 0),
put(test_server_ok, 0),
put(test_server_failed, 0),
put(test_server_skipped, {0,0}),
- put(test_server_summary_level, SumLev),
- put(test_server_major_level, MajLev),
put(test_server_minor_level, MinLev),
put(test_server_create_priv_dir, CreatePrivDir),
put(test_server_random_seed, proplists:get_value(random_seed, ExtraTools)),
@@ -1393,23 +1172,30 @@ init_tester(Mod, Func, Args, Dir, Name, {SumLev,MajLev,MinLev},
put(test_server_framework_name, list_to_atom(FWName))
end
end,
+
%% before first print, read and set logging options
LogOpts = test_server_sup:framework_call(get_logopts, [], []),
put(test_server_logopts, LogOpts),
- put(test_server_log_nl, not lists:member(no_nl, LogOpts)),
+
StartedExtraTools = start_extra_tools(ExtraTools),
+
+ test_server_io:set_job_name(Name),
+ test_server_io:set_gl_props([{levels,Levels},
+ {auto_nl,not lists:member(no_nl, LogOpts)},
+ {reject_io_reqs,RejectIoReqs}]),
+ group_leader(test_server_io:get_gl(true), self()),
{TimeMy,Result} = ts_tc(Mod, Func, Args),
- put(test_server_common_io_handler, undefined),
- stop_extra_tools(StartedExtraTools),
+ set_io_buffering(undefined),
+ test_server_io:set_job_name(undefined),
+ catch stop_extra_tools(StartedExtraTools),
case Result of
{'EXIT',test_suites_done} ->
- print(25, "DONE, normal exit", []);
+ ok;
{'EXIT',_Pid,Reason} ->
print(1, "EXIT, reason ~p", [Reason]);
{'EXIT',Reason} ->
- print(1, "EXIT, reason ~p", [Reason]);
- _Other ->
- print(25, "DONE", [])
+ report_severe_error(Reason),
+ print(1, "EXIT, reason ~p", [Reason])
end,
Time = TimeMy/1000000,
SuccessStr =
@@ -1424,9 +1210,15 @@ init_tester(Mod, Func, Args, Dir, Name, {SumLev,MajLev,MinLev},
end,
OkN = get(test_server_ok),
FailedN = get(test_server_failed),
- print(html,"<tr><td></td><td><b>TOTAL</b></td><td></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]).
+ print(html,"\n</tbody>\n<tfoot>\n"
+ "<tr><td></td><td><b>TOTAL</b></td><td></td><td></td><td></td>"
+ "<td>~.3fs</td><td><b>~s</b></td><td>~p Ok, ~p Failed~s of ~p</td></tr>\n"
+ "</tfoot>\n",
+ [Time,SuccessStr,OkN,FailedN,SkipStr,OkN+FailedN+SkippedN]),
+ test_server_io:stop().
+
+report_severe_error(Reason) ->
+ test_server_sup:framework_call(report, [severe_error,Reason]).
%% timer:tc/3
ts_tc(M, F, A) ->
@@ -1444,11 +1236,11 @@ elapsed_time(Before, After) ->
start_extra_tools(ExtraTools) ->
start_extra_tools(ExtraTools, []).
-start_extra_tools([{cover,App,Analyse} | ExtraTools], Started) ->
+start_extra_tools([{cover,App,Analyse,Stop} | ExtraTools], Started) ->
case cover_compile(App) of
{ok,AnalyseMods} ->
start_extra_tools(ExtraTools,
- [{cover,App,Analyse,AnalyseMods}|Started]);
+ [{cover,App,Analyse,AnalyseMods,Stop}|Started]);
{error,_} ->
start_extra_tools(ExtraTools, Started)
end;
@@ -1467,8 +1259,8 @@ stop_extra_tools(ExtraTools) ->
end,
stop_extra_tools(ExtraTools, TestDir).
-stop_extra_tools([{cover,App,Analyse,AnalyseMods}|ExtraTools], TestDir) ->
- cover_analyse(App, Analyse, AnalyseMods, TestDir),
+stop_extra_tools([{cover,App,Analyse,AnalyseMods,Stop}|ExtraTools], TestDir) ->
+ cover_analyse(App, Analyse, AnalyseMods, Stop, TestDir),
stop_extra_tools(ExtraTools, TestDir);
%%stop_extra_tools([_ | ExtraTools], TestDir) ->
%% stop_extra_tools(ExtraTools, TestDir);
@@ -1486,7 +1278,7 @@ stop_extra_tools([], _) ->
%% Reads the named test suite specification file, and executes it.
%%
%% This function is meant to be called by a process created by
-%% spawn_tester/7, which sets up some necessary dictionary values.
+%% spawn_tester/10, which sets up some necessary dictionary values.
do_spec(SpecName, TimetrapSpec) when is_list(SpecName) ->
case file:consult(SpecName) of
@@ -1535,7 +1327,7 @@ do_spec(SpecName, TimetrapSpec) when is_list(SpecName) ->
%% should not be used. Use a configuration test case instead.
%%
%% This function is meant to be called by a process created by
-%% spawn_tester/7, which sets up some necessary dictionary values.
+%% spawn_tester/10, which sets up some necessary dictionary values.
do_spec_list(TermList0, TimetrapSpec) ->
Nodes = [],
@@ -1702,7 +1494,7 @@ add_mod(Mod, Mods) ->
%% configuration information into the log files.
%%
%% This function is meant to be called by a process created by
-%% spawn_tester/7, which sets up some necessary dictionary values.
+%% spawn_tester/10, which sets up some necessary dictionary values.
do_test_cases(TopCases, SkipCases,
Config, MultiplyTimetrap) when is_integer(MultiplyTimetrap);
MultiplyTimetrap == infinity ->
@@ -1741,7 +1533,8 @@ do_test_cases(TopCases, SkipCases,
test_server_sup:framework_call(report, [tests_start,{Test,N}]),
{Header,Footer} =
case test_server_sup:framework_call(get_html_wrapper,
- [TestDescr,true,TestDir], "") of
+ [TestDescr,true,TestDir,
+ {[],[2,3,4,7,8],[1,6]}], "") of
Empty when (Empty == "") ; (element(2,Empty) == "") ->
put(basic_html, true),
{["<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n",
@@ -1799,18 +1592,20 @@ do_test_cases(TopCases, SkipCases,
print(html,
"<p><ul>\n"
"<li><a href=\"~s\">Full textual log</a></li>\n"
- "<li><a href=\"~s\">Coverage log</a></li>\n</ul></p>\n",
- [?suitelog_name,?coverlog_name]),
+ "<li><a href=\"~s\">Coverage log</a></li>\n"
+ "<li><a href=\"~s\">Unexpected I/O log</a></li>\n</ul></p>\n",
+ [?suitelog_name,?coverlog_name,?unexpected_io_log]),
print(html,
"<p>~s</p>\n" ++
- xhtml("<table bgcolor=\"white\" border=\"3\" cellpadding=\"5\">",
- "<table>") ++
- "<tr><th>Num</th><th>Module</th><th>Group</th>" ++
- "<th>Case</th><th>Log</th><th>Time</th><th>Result</th>" ++
- "<th>Comment</th></tr>\n",
- [print_if_known(N, {"<i>Executing <b>~p</b> test cases...</i>\n",[N]},
+ xhtml("<table bgcolor=\"white\" border=\"3\" cellpadding=\"5\">",
+ ["<table id=\"",?sortable_table_name,"\">\n",
+ "<thead>\n"]) ++
+ "<tr><th>Num</th><th>Module</th><th>Group</th>" ++
+ "<th>Case</th><th>Log</th><th>Time</th><th>Result</th>" ++
+ "<th>Comment</th></tr>\n</thead>\n<tbody>\n",
+ [print_if_known(N, {"<i>Executing <b>~p</b> test cases...</i>" ++
+ xhtml("\n<br>\n", "\n<br />\n"),[N]},
{"",[]})]),
- print(html, xhtml("<br>", "<br />")),
print(major, "=cases ~p", [get(test_server_cases)]),
print(major, "=user ~s", [TI#target_info.username]),
@@ -1859,7 +1654,7 @@ start_log_file() ->
{error, eexist} ->
ok;
MkDirError ->
- exit({cant_create_log_dir,{MkDirError,Dir}})
+ log_file_error(MkDirError, Dir)
end,
TestDir = timestamp_filename_get(filename:join(Dir, "run.")),
TestDir1 =
@@ -1874,20 +1669,26 @@ start_log_file() ->
ok ->
TestDirX;
MkDirError2 ->
- exit({cant_create_log_dir,{MkDirError2,TestDirX}})
+ log_file_error(MkDirError2, TestDirX)
end;
MkDirError2 ->
- exit({cant_create_log_dir,{MkDirError2,TestDir}})
+ log_file_error(MkDirError2, TestDir)
end,
ok = file:write_file(filename:join(Dir, ?last_file), TestDir1 ++ "\n"),
ok = file:write_file(?last_file, TestDir1 ++ "\n"),
put(test_server_log_dir_base,TestDir1),
MajorName = filename:join(TestDir1, ?suitelog_name),
HtmlName = MajorName ++ ?html_ext,
+ UnexpectedName = filename:join(TestDir1, ?unexpected_io_log),
{ok,Major} = file:open(MajorName, [write]),
{ok,Html} = file:open(HtmlName, [write]),
+ {ok,Unexpected} = file:open(UnexpectedName, [write]),
+ test_server_io:set_fd(major, Major),
+ test_server_io:set_fd(html, Html),
+ test_server_io:set_fd(unexpected_io, Unexpected),
put(test_server_major_fd,Major),
put(test_server_html_fd,Html),
+ put(test_server_unexpected_io, Unexpected),
make_html_link(filename:absname(?last_test ++ ?html_ext),
HtmlName, filename:basename(Dir)),
@@ -1898,12 +1699,15 @@ start_log_file() ->
PrivDir = filename:join(TestDir1, ?priv_dir),
ok = file:make_dir(PrivDir),
put(test_server_priv_dir,PrivDir++"/"),
- print_timestamp(13,"Suite started at "),
+ print_timestamp(major, "Suite started at "),
LogInfo = [{topdir,Dir},{rundir,lists:flatten(TestDir1)}],
test_server_sup:framework_call(report, [loginfo,LogInfo]),
{ok,TestDir1}.
+log_file_error(Error, Dir) ->
+ exit({cannot_create_log_dir,{Error,lists:flatten(Dir)}}).
+
make_html_link(LinkName, Target, Explanation) ->
%% if possible use a relative reference to Target.
TargetL = filename:split(Target),
@@ -1937,13 +1741,14 @@ make_html_link(LinkName, Target, Explanation) ->
%% Some header info will also be inserted into the log file.
start_minor_log_file(Mod, Func) ->
+ MFA = {Mod,Func,1},
LogDir = get(test_server_log_dir_base),
Name0 = lists:flatten(io_lib:format("~s.~s~s", [Mod,Func,?html_ext])),
Name = downcase(Name0),
AbsName = filename:join(LogDir, Name),
case file:read_file_info(AbsName) of
{error,_} -> %% normal case, unique name
- start_minor_log_file1(Mod, Func, LogDir, AbsName);
+ start_minor_log_file1(Mod, Func, LogDir, AbsName, MFA);
{ok,_} -> %% special case, duplicate names
{_,S,Us} = now(),
Name1_0 =
@@ -1952,19 +1757,21 @@ start_minor_log_file(Mod, Func) ->
?html_ext])),
Name1 = downcase(Name1_0),
AbsName1 = filename:join(LogDir, Name1),
- start_minor_log_file1(Mod, Func, LogDir, AbsName1)
+ start_minor_log_file1(Mod, Func, LogDir, AbsName1, MFA)
end.
-start_minor_log_file1(Mod, Func, LogDir, AbsName) ->
+start_minor_log_file1(Mod, Func, LogDir, AbsName, MFA) ->
{ok,Fd} = file:open(AbsName, [write]),
Lev = get(test_server_minor_level)+1000, %% far down in the minor levels
put(test_server_minor_fd, Fd),
-
+ test_server_gl:set_minor_fd(group_leader(), Fd, MFA),
+
TestDescr = io_lib:format("Test ~p:~p result", [Mod,Func]),
{Header,Footer} =
case test_server_sup:framework_call(get_html_wrapper,
[TestDescr,false,
- filename:dirname(AbsName)], "") of
+ filename:dirname(AbsName),
+ undefined], "") of
Empty when (Empty == "") ; (element(2,Empty) == "") ->
put(basic_html, true),
{["<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n",
@@ -1991,7 +1798,7 @@ start_minor_log_file1(Mod, Func, LogDir, AbsName) ->
lists:member(no_src, get(test_server_logopts))} of
{true,false} ->
print(Lev, "<a href=\"~s#~s\">source code for ~p:~p/1</a>\n",
- [SrcListing,Func,Mod,Func]);
+ [SrcListing,atom_to_list(Func)++"-1",Mod,Func]);
_ -> ok
end,
@@ -2006,6 +1813,7 @@ start_minor_log_file1(Mod, Func, LogDir, AbsName) ->
AbsName.
stop_minor_log_file() ->
+ test_server_gl:unset_minor_fd(group_leader()),
Fd = get(test_server_minor_fd),
Footer = get(test_server_minor_footer),
io:fwrite(Fd, "</pre>\n" ++ Footer, []),
@@ -2094,7 +1902,7 @@ html_possibly_convert(Src, SrcInfo, Dest) ->
Header =
case test_server_sup:framework_call(get_html_wrapper,
["Module "++Src,false,
- OutDir], "") of
+ OutDir,undefined], "") of
Empty when (Empty == "") ; (element(2,Empty) == "") ->
["<!DOCTYPE HTML PUBLIC",
"\"-//W3C//DTD HTML 3.2 Final//EN\">\n",
@@ -2281,9 +2089,7 @@ do_add_end_per_suite_and_skip(LastMod, LastRef, Mod, FwMod) ->
%% Runs the specified tests, then displays/logs the summary.
run_test_cases(TestSpec, Config, TimetrapData) ->
-
- maybe_open_job_sock(),
-
+ test_server:init_purify(),
case lists:member(no_src, get(test_server_logopts)) of
true ->
ok;
@@ -2293,8 +2099,6 @@ run_test_cases(TestSpec, Config, TimetrapData) ->
run_test_cases_loop(TestSpec, [Config], TimetrapData, [], []),
- maybe_get_privdir(),
-
{AllSkippedN,UserSkipN,AutoSkipN,SkipStr} =
case get(test_server_skipped) of
{0,0} -> {0,0,0,""};
@@ -2313,41 +2117,6 @@ run_test_cases(TestSpec, Config, TimetrapData) ->
print(major, "=auto_skipped ~p", [AutoSkipN]),
exit(test_suites_done).
-%% If the test is run at a remote target, this function sets up a socket
-%% communication with the target for handling this particular job.
-maybe_open_job_sock() ->
- TI = get_target_info(),
- case TI#target_info.where of
- local ->
- %% local target
- test_server:init_purify();
- MainSock ->
- %% remote target
- {ok,LSock} = gen_tcp:listen(0, [binary,
- {reuseaddr,true},
- {packet,4},
- {active,false}]),
- {ok,Port} = inet:port(LSock),
- request(MainSock, {job,Port,get(test_server_name)}),
- case gen_tcp:accept(LSock, ?ACCEPT_TIMEOUT) of
- {ok,Sock} -> put(test_server_ctrl_job_sock, Sock);
- {error,Reason} -> exit({no_contact,Reason})
- end
- end.
-
-%% If the test is run at a remote target, this function waits for a
-%% tar packet containing the privdir created by the test case.
-maybe_get_privdir() ->
- case get(test_server_ctrl_job_sock) of
- undefined ->
- %% local target
- ok;
- Sock ->
- %% remote target
- request(Sock, job_done),
- gen_tcp:close(Sock)
- end.
-
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% run_test_cases_loop(TestCases, Config, TimetrapData, Mode, Status) -> ok
@@ -2426,27 +2195,38 @@ maybe_get_privdir() ->
%% reason, the Mode argument specifies if a parallel group is currently
%% being executed.
%%
-%% A parallel test case process will always set the dictionary value
-%% 'test_server_common_io_handler' to the pid of the main (starting)
-%% process. With this value set, the print/3 function will send print
-%% messages to the main process instead of writing the data to file
-%% (only true for printouts to common log files).
+%% The low-level mechanism for buffering IO for the common log files
+%% is handled by the test_server_io module. Buffering is turned on by
+%% test_server_io:start_transaction/0 and off by calling
+%% test_server_io:end_transaction/0. The buffered data for the transaction
+%% can printed by calling test_server_io:print_buffered/1.
+%%
+%% This module is responsible for turning on IO buffering and to later
+%% test_server_io:print_buffered/1 to print the data. To help with this,
+%% two variables in the process dictionary are used:
+%% 'test_server_common_io_handler' and 'test_server_queued_io'. The values
+%% are set to as follwing:
+%%
+%% Value Meaning
+%% ----- -------
+%% undefined No parallel test cases running
+%% {tc,Pid} Running test cases in a top-level parallel group
+%% {Ref,Pid} Running sequential test case inside a parallel group
+%%
+%% FIXME: The Pid is no longer used.
%%
%% 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
-%% executed by the main process - to all end up as messages in the
-%% inbox of the main process.
+%% value gets set also on 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
%% 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
-%% handle_test_case_io_and_status/0 for details.
+%% list of test cases is traversed in order and test_server_io:print_buffered/1
+%% can be called for each test case. 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.
@@ -2499,7 +2279,7 @@ 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}]),
+ {Mod,Func,Comment}]),
run_test_cases_loop(Cases, Config, TimetrapData, ParentMode,
delete_status(Ref, Status));
_ ->
@@ -2508,7 +2288,7 @@ run_test_cases_loop([{auto_skip_case,{Type,Ref,Case,Comment},SkipMode}|Cases],
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}]),
+ {Mod,Func,Comment}]),
case CurrIOHandler of
{Ref,_} ->
%% current_io_handler was set by start conf of this
@@ -2525,7 +2305,7 @@ run_test_cases_loop([{auto_skip_case,{Type,Ref,Case,Comment},SkipMode}|Cases],
%% this is a skipped end conf for a non-parallel group that's 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}]),
+ test_server_sup:framework_call(report, [tc_auto_skip,{Mod,Func,Comment}]),
%% 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
@@ -2556,7 +2336,7 @@ run_test_cases_loop([{auto_skip_case,{Type,Ref,Case,Comment},SkipMode}|Cases],
%% this is a skipped end conf for a non-parallel group nested under
%% a parallel group (io buffering is active)
{Mod,Func} = skip_case(auto, Ref, 0, Case, Comment, true, SkipMode),
- test_server_sup:framework_call(report, [tc_auto_skip,{?pl2a(Mod),Func,Comment}]),
+ test_server_sup:framework_call(report, [tc_auto_skip,{Mod,Func,Comment}]),
case CurrIOHandler of
{Ref,_} ->
%% current_io_handler was set by start conf of this
@@ -2572,7 +2352,7 @@ run_test_cases_loop([{auto_skip_case,{Type,Ref,Case,Comment},SkipMode}|Cases],
%% 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}]),
+ test_server_sup:framework_call(report, [tc_auto_skip,{Mod,Func,Comment}]),
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
@@ -2583,22 +2363,21 @@ run_test_cases_loop([{auto_skip_case,{Type,Ref,Case,Comment},SkipMode}|Cases],
ok
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}]),
+ test_server_sup:framework_call(report, [tc_auto_skip,{Mod,Func,Comment}]),
run_test_cases_loop(Cases, Config, TimetrapData, [conf(Ref,[])|Mode], Status)
end;
run_test_cases_loop([{auto_skip_case,{Case,Comment},SkipMode}|Cases],
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}]),
+ {Mod,Func} = skip_case(auto, undefined, get(test_server_case_num)+1,
+ Case, Comment, is_io_buffered(), SkipMode),
+ test_server_sup:framework_call(report, [tc_auto_skip,{Mod,Func,Comment}]),
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, TimetrapData, Mode, Status) ->
- {Mod,Func} = skip_case(user, Ref, 0, Case, Comment,
- (undefined /= get(test_server_common_io_handler))),
+ {Mod,Func} = skip_case(user, Ref, 0, Case, Comment, is_io_buffered()),
{Cases,Config1} =
case curr_ref(Mode) of
Ref ->
@@ -2608,15 +2387,15 @@ run_test_cases_loop([{skip_case,{conf,Ref,Case,Comment}}|Cases0],
%% skipped start conf
{skip_cases_upto(Ref, Cases0, Comment, conf, Mode),Config}
end,
- test_server_sup:framework_call(report, [tc_user_skip,{?pl2a(Mod),Func,Comment}]),
+ test_server_sup:framework_call(report, [tc_user_skip,{Mod,Func,Comment}]),
run_test_cases_loop(Cases, Config1, TimetrapData, Mode,
update_status(skipped, Mod, Func, Status));
run_test_cases_loop([{skip_case,{Case,Comment}}|Cases],
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}]),
+ {Mod,Func} = skip_case(user, undefined, get(test_server_case_num)+1,
+ Case, Comment, is_io_buffered()),
+ test_server_sup:framework_call(report, [tc_user_skip,{Mod,Func,Comment}]),
run_test_cases_loop(Cases, Config, TimetrapData, Mode,
update_status(skipped, Mod, Func, Status));
@@ -2852,7 +2631,7 @@ run_test_cases_loop([{conf,Ref,Props,{Mod,Func}}|_Cases]=Cs0,
end,
CurrMode = curr_mode(Ref, Mode0, Mode),
- ConfCaseResult = run_test_case(Ref, 0, Mod, Func, [ActualCfg], skip_init, target,
+ ConfCaseResult = run_test_case(Ref, 0, Mod, Func, [ActualCfg], skip_init,
TimetrapData, CurrMode),
case ConfCaseResult of
@@ -2886,6 +2665,7 @@ run_test_cases_loop([{conf,Ref,Props,{Mod,Func}}|_Cases]=Cs0,
exit(framework_error);
{_,Fail,_} when element(1,Fail) == 'EXIT';
element(1,Fail) == timetrap_timeout;
+ element(1,Fail) == user_timetrap_error;
element(1,Fail) == failed ->
{Cases2,Config1,Status3} =
if StartConf ->
@@ -2905,14 +2685,6 @@ run_test_cases_loop([{conf,Ref,Props,{Mod,Func}}|_Cases]=Cs0,
set_io_buffering(IOHandler),
stop_minor_log_file(),
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, TimetrapData, Mode,
- delete_status(Ref, Status2));
{_,{Skip,Reason},_} when StartConf and ((Skip==skip) or (Skip==skipped)) ->
ReportAbortRepeat(skipped),
print(minor, "~n*** ~p skipped.~n"
@@ -2983,7 +2755,7 @@ run_test_cases_loop([{conf,Ref,Props,{Mod,Func}}|_Cases]=Cs0,
end;
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
+ case run_test_case(Ref, 0, Mod, Func, Args, skip_init, TimetrapData) of
{_,Why={'EXIT',_},_} ->
print(minor, "~n*** ~p failed.~n"
" Skipping all cases.", [Func]),
@@ -3014,23 +2786,21 @@ run_test_cases_loop([{Mod,Case}|Cases], Config, TimetrapData, 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
- case check_prop(parallel, Mode) of
+
+ case check_prop(parallel, Mode) =:= false andalso is_io_buffered() of
+ true ->
+ %% sequential test case nested in a parallel group;
+ %% io is buffered, so we must queue this test case
+ queue_test_case_io(undefined, self(), Num+1, Mod, Func);
false ->
- case get(test_server_common_io_handler) of
- 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, TimetrapData, Mode) of
+ run_init, 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]),
@@ -3077,8 +2847,8 @@ run_test_cases_loop([{Mod,Func,Args}|Cases], Config, TimetrapData, Mode, Status)
%% 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
- %% later, so we have to save info about the case
+ %% io from Pid will be buffered by the test_server_io process 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, TimetrapData, Mode, Status)
end;
@@ -3185,11 +2955,17 @@ get_data_dir(Mod, Suite) ->
non_existing ->
print(12, "The module ~p is not loaded", [Mod]),
[];
+ cover_compiled ->
+ MainCoverNode = cover:get_main_node(),
+ {file,File} = rpc:call(MainCoverNode,cover,is_compiled,[UseMod]),
+ do_get_data_dir(UseMod,File);
FullPath ->
- filename:dirname(FullPath) ++ "/" ++ cast_to_list(UseMod) ++
- ?data_dir_suffix
+ do_get_data_dir(UseMod,FullPath)
end.
+do_get_data_dir(Mod,File) ->
+ filename:dirname(File) ++ "/" ++ cast_to_list(Mod) ++ ?data_dir_suffix.
+
print_conf_time(0) ->
ok;
print_conf_time(ConfTime) ->
@@ -3333,7 +3109,9 @@ skip_case(Type, Ref, CaseNum, Case, Comment, SendSync, Mode) ->
if SendSync ->
queue_test_case_io(Ref, self(), CaseNum, Mod, Func),
self() ! {started,Ref,self(),CaseNum,Mod,Func},
+ test_server_io:start_transaction(),
skip_case1(Type, CaseNum, Mod, Func, Comment, Mode),
+ test_server_io:end_transaction(),
self() ! {finished,Ref,self(),CaseNum,Mod,Func,skipped,{0,skipped,[]}};
not SendSync ->
skip_case1(Type, CaseNum, Mod, Func, Comment, Mode)
@@ -3474,13 +3252,20 @@ modify_cases_upto1(Ref, CopyOp, [C|T], Orig, Alt) ->
%%
%% Save info about current process (always the main process) buffering
%% io printout messages from parallel test case processes (*and* possibly
-%% also the main process). If the value is the default 'undefined',
-%% io is not buffered but printed directly to file (see print/3).
+%% also the main process).
set_io_buffering(IOHandler) ->
put(test_server_common_io_handler, IOHandler).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% is_io_buffered() -> true|false
+%%
+%% Test whether is being buffered.
+
+is_io_buffered() ->
+ get(test_server_common_io_handler) =/= undefined.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% queue_test_case_io(Pid, Num, Mod, Func) -> ok
%%
%% Save info about test case that gets its io buffered. This can
@@ -3527,7 +3312,7 @@ 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
- %% to handle buffered io messages later
+ %% to test_server_io:print_buffered/1 later
self() ! Msg,
MF = {Mod,Func},
{Ok1,Skip1,Fail1} =
@@ -3558,16 +3343,18 @@ rm_cases_upto(Ref, [_|Ps]) ->
%%
%% Each parallel test case process prints to its own minor log file during
%% 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
-%% 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
+%% written to sequentially. This is handled by calling
+%% test_server_io:start_transaction/0 to tell the test_server_io process
+%% to buffer all print requests.
+%%
+%% An io session is always started with a
+%% {started,Ref,Pid,Num,Mod,Func} message (and
+%% test_server_io:start_transaction/0 will be called) and terminated
+%% with {finished,Ref,Pid,Num,Mod,Func,Result,RetVal} (and
+%% test_server_io:end_transaction/0 will be called). 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
%% terminating) is also received and handled here.
%%
%% During execution of a parallel group, any cases (conf or normal)
@@ -3576,13 +3363,13 @@ rm_cases_upto(Ref, [_|Ps]) ->
%% correct sequence. This function handles also the print messages
%% generated by nested group cases that have been executed sequentially
%% by the main process (note that these cases do not generate 'EXIT'
-%% messages, only 'start', 'print' and 'finished' messages).
+%% messages, only 'start' and 'finished' messages).
%%
%% 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
-%% do not get consumated by test_server:run_test_case_msgloop/5
+%% do not get consumed by test_server:run_test_case_msgloop/5
%% during the test case execution (e.g. in the catch clause of
%% the receive)!
@@ -3609,7 +3396,7 @@ handle_test_case_io_and_status() ->
%% 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) ->
- %% retreive the start message for the current io session (= testcase)
+ %% retrieve the start message for the current io session (= testcase)
receive
{started,_,CurrPid,CaseNum,Mod,Func} ->
{Ok1,Skip1,Fail1} =
@@ -3649,11 +3436,18 @@ handle_io_and_exit_loop(_, [], Ok,Skip,Fail) ->
handle_io_and_exits(Main, CurrPid, CaseNum, Mod, Func, Cases) ->
receive
+ {abort_current_testcase=Tag,_Reason,From} ->
+ %% If a parallel group is executing, there is no unique
+ %% current test case, so we must generate an error.
+ From ! {self(),Tag,{error,parallel_group}},
+ handle_io_and_exits(Main, CurrPid, CaseNum, Mod, Func, Cases);
%% end of io session from test case executed by main process
{finished,_,Main,CaseNum,Mod,Func,Result,_RetVal} ->
+ test_server_io:print_buffered(CurrPid),
{Result,{Mod,Func}};
%% end of io session from test case executed by parallel process
{finished,_,CurrPid,CaseNum,Mod,Func,Result,RetVal} ->
+ test_server_io:print_buffered(CurrPid),
case Result of
ok ->
put(test_server_ok, get(test_server_ok)+1);
@@ -3666,13 +3460,9 @@ handle_io_and_exits(Main, CurrPid, CaseNum, Mod, Func, Cases) ->
end,
{Result,{Mod,Func}};
- %% print to common log file
- {print,CurrPid,Detail,Msg} ->
- output({Detail,Msg}, internal),
- handle_io_and_exits(Main, CurrPid, CaseNum, Mod, Func, Cases);
-
%% unexpected termination of test case process
{'EXIT',TCPid,Reason} when Reason /= normal ->
+ test_server_io:print_buffered(CurrPid),
{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]),
@@ -3704,59 +3494,52 @@ handle_io_and_exits(Main, CurrPid, CaseNum, Mod, Func, Cases) ->
%% 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, TimetrapData) ->
+run_test_case(Ref, Num, Mod, Func, Args, RunInit, TimetrapData) ->
file:set_cwd(filename:dirname(get(test_server_dir))),
- run_test_case1(Ref, Num, Mod, Func, Args, RunInit, Where,
- TimetrapData, [], [], self()).
+ run_test_case1(Ref, Num, Mod, Func, Args, RunInit,
+ TimetrapData, [], self()).
-run_test_case(Ref, Num, Mod, Func, Args, skip_init, Where, TimetrapData, Mode) ->
+run_test_case(Ref, Num, Mod, Func, Args, skip_init, TimetrapData, Mode) ->
%% a conf case is always executed by the main process
- run_test_case1(Ref, Num, Mod, Func, Args, skip_init, Where,
- TimetrapData, [], Mode, self());
+ run_test_case1(Ref, Num, Mod, Func, Args, skip_init,
+ TimetrapData, Mode, self());
-run_test_case(Ref, Num, Mod, Func, Args, RunInit, Where, TimetrapData, Mode) ->
+run_test_case(Ref, Num, Mod, Func, Args, RunInit, TimetrapData, Mode) ->
file:set_cwd(filename:dirname(get(test_server_dir))),
+ Main = self(),
case check_prop(parallel, Mode) of
false ->
%% this is a sequential test case
- run_test_case1(Ref, Num, Mod, Func, Args, RunInit, Where,
- TimetrapData, [], Mode, self());
+ run_test_case1(Ref, Num, Mod, Func, Args, RunInit,
+ TimetrapData, Mode, Main);
_Ref ->
%% this a parallel test case, spawn the new process
- Main = self(),
- {dictionary,State} = process_info(self(), dictionary),
- spawn_link(fun() ->
- run_test_case1(Ref, Num, Mod, Func, Args, RunInit, Where,
- TimetrapData, State, Mode, Main)
- end)
+ Dictionary = get(),
+ {dictionary,Dictionary} = process_info(self(), dictionary),
+ spawn_link(
+ fun() ->
+ process_flag(trap_exit, true),
+ [put(Key, Val) || {Key,Val} <- Dictionary],
+ set_io_buffering({tc,Main}),
+ run_test_case1(Ref, Num, Mod, Func, Args, RunInit,
+ TimetrapData, Mode, Main)
+ end)
end.
-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),
+run_test_case1(Ref, Num, Mod, Func, Args, RunInit,
+ TimetrapData, Mode, Main) ->
+ group_leader(test_server_io:get_gl(Main == self()), self()),
+
%% 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;
- _ -> Main ! {started,Ref,self(),Num,Mod,Func}
+ case is_io_buffered() of
+ false -> ok;
+ true ->
+ test_server_io:start_transaction(),
+ Main ! {started,Ref,self(),Num,Mod,Func}
end,
TSDir = get(test_server_dir),
- case Where of
- target ->
- maybe_send_beam_and_datadir(Mod);
- host ->
- ok
- end,
+
print(major, "=case ~p:~p", [Mod, Func]),
MinorName = start_minor_log_file(Mod, Func),
print(minor, "<a name=\"top\"></a>", [], internal_raw),
@@ -3790,7 +3573,7 @@ run_test_case1(Ref, Num, Mod, Func, Args, RunInit, Where,
end,
test_server_sup:framework_call(report,
- [tc_start,{{?pl2a(Mod),Func},MinorName}]),
+ [tc_start,{{Mod,Func},MinorName}]),
print_props((RunInit==skip_init), get_props(Mode)),
GroupName = case get_name(Mode) of
@@ -3808,11 +3591,12 @@ run_test_case1(Ref, Num, Mod, Func, Args, RunInit, Where,
[num2str(Num),fw_name(Mod),GroupName,MinorBase,Func,
MinorBase,MinorBase]),
- do_if_parallel(Main, ok, fun erlang:yield/0),
+ do_unless_parallel(Main, fun erlang:yield/0),
+
%% run the test case
{Result,DetectedFail,ProcsBefore,ProcsAfter} =
run_test_case_apply(Num, Mod, Func, [UpdatedArgs], get_name(Mode),
- RunInit, Where, TimetrapData),
+ RunInit, TimetrapData),
{Time,RetVal,Loc,Opts,Comment} =
case Result of
Normal={_Time,_RetVal,_Loc,_Opts,_Comment} -> Normal;
@@ -3824,7 +3608,7 @@ run_test_case1(Ref, Num, Mod, Func, Args, RunInit, Where,
print_timestamp(minor, "Ended at "),
print(major, "=ended ~s", [lists:flatten(timestamp_get(""))]),
- do_if_parallel(Main, ok, fun() -> file:set_cwd(filename:dirname(TSDir)) end),
+ do_unless_parallel(Main, fun() -> file:set_cwd(filename:dirname(TSDir)) end),
%% call the appropriate progress function clause to print the results to log
Status =
@@ -3929,14 +3713,17 @@ run_test_case1(Ref, Num, Mod, Func, Args, RunInit, Where,
true ->
ok
end,
- check_new_crash_dumps(Where),
+ test_server_sup:check_new_crash_dumps(),
%% 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;
- _ -> Main ! {finished,Ref,self(),Num,Mod,Func,
- ?mod_result(Status),{Time,RetVal,Opts}}
+ case is_io_buffered() of
+ false ->
+ ok;
+ true ->
+ test_server_io:end_transaction(),
+ Main ! {finished,Ref,self(),Num,Mod,Func,
+ ?mod_result(Status),{Time,RetVal,Opts}}
end,
{Time,RetVal,Opts}.
@@ -3944,126 +3731,16 @@ run_test_case1(Ref, Num, Mod, Func, Args, RunInit, Where,
%%--------------------------------------------------------------------
%% various help functions
-%% Call If() if we're on parallel process, or
-%% call Else() if we're on main process
-do_if_parallel(Pid, If, Else) ->
+%% Call Action if we are running on the main process (not parallel).
+do_unless_parallel(Main, Action) when is_function(Action, 0) ->
case self() of
- Pid ->
- if is_function(Else) -> Else();
- true -> Else
- end;
- _ ->
- if is_function(If) -> If();
- true -> If
- end
+ Main -> Action();
+ _ -> ok
end.
num2str(0) -> "";
num2str(N) -> integer_to_list(N).
-%% If remote target, this function sends the test suite (if not already sent)
-%% and the content of datadir til target.
-maybe_send_beam_and_datadir(Mod) ->
- case get(test_server_ctrl_job_sock) of
- undefined ->
- %% local target
- ok;
- JobSock ->
- %% remote target
- case get(test_server_downloaded_suites) of
- undefined ->
- send_beam_and_datadir(Mod, JobSock),
- put(test_server_downloaded_suites, [Mod]);
- Suites ->
- case lists:member(Mod, Suites) of
- false ->
- send_beam_and_datadir(Mod, JobSock),
- put(test_server_downloaded_suites, [Mod|Suites]);
- true ->
- ok
- end
- end
- end.
-
-send_beam_and_datadir(Mod, JobSock) ->
- case code:which(Mod) of
- non_existing ->
- io:format("** WARNING: Suite ~w could not be found on host\n",
- [Mod]);
- BeamFile ->
- send_beam(JobSock, Mod, BeamFile)
- end,
- DataDir = get_data_dir(Mod),
- case file:read_file_info(DataDir) of
- {ok,_I} ->
- {ok,All} = file:list_dir(DataDir),
- AddTarFiles =
- case controller_call(get_target_info) of
- #target_info{os_family=ose} ->
- ObjExt = code:objfile_extension(),
- Wc = filename:join(DataDir, "*" ++ ObjExt),
- 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 compilation can be done on target anyway.
- %% Compiled C code must exist on target.
- %% Beam files are already sent as binaries.
- %% Erlang source are sent in case the test case
- %% is to compile it.
- Filter = fun("Makefile") -> false;
- ("Makefile.src") -> false;
- (Y) ->
- case filename:extension(Y) of
- ".c" -> false;
- ObjExt -> false;
- _ -> true
- end
- end,
- lists:filter(Filter, All);
- _ ->
- All
- end,
- Tarfile = "data_dir.tar.gz",
- {ok,Tar} = erl_tar:open(Tarfile, [write,compressed]),
- ShortDataDir = filename:basename(DataDir),
- AddTarFun =
- fun(File) ->
- Long = filename:join(DataDir, File),
- Short = filename:join(ShortDataDir, File),
- ok = erl_tar:add(Tar, Long, Short, [])
- end,
- lists:foreach(AddTarFun, AddTarFiles),
- ok = erl_tar:close(Tar),
- {ok,TarBin} = file:read_file(Tarfile),
- file:delete(Tarfile),
- request(JobSock, {{datadir,Tarfile}, TarBin});
- {error,_R} ->
- ok
- end.
-
-send_beam(JobSock, BeamFile) ->
- Mod=filename:rootname(filename:basename(BeamFile), code:objfile_extension()),
- 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 ->
- case get(test_server_ctrl_job_sock) of
- undefined ->
- ok;
- Socket ->
- read_job_sock_loop(Socket)
- end;
- _ ->
- ok
- end,
- test_server_sup:check_new_crash_dumps().
-
-
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% progress(Result, CaseNum, Mod, Func, Location, Reason, Time,
%% Comment, TimeFormat) -> Result
@@ -4081,7 +3758,7 @@ progress(skip, CaseNum, Mod, Func, Loc, Reason, Time,
print(major, "=result skipped", []),
print(1, "*** SKIPPED *** ~s",
[get_info_str(Func, CaseNum, get(test_server_cases))]),
- test_server_sup:framework_call(report, [tc_done,{?pl2a(Mod),Func,
+ test_server_sup:framework_call(report, [tc_done,{Mod,Func,
{skipped,Reason1}}]),
ReasonStr = reason_to_string(Reason1),
ReasonStr1 = lists:flatten([string:strip(S,left) ||
@@ -4112,7 +3789,7 @@ progress(failed, CaseNum, Mod, Func, Loc, timetrap_timeout, T,
print(1, "*** FAILED *** ~s",
[get_info_str(Func, CaseNum, get(test_server_cases))]),
test_server_sup:framework_call(report,
- [tc_done,{?pl2a(Mod),Func,
+ [tc_done,{Mod,Func,
{failed,timetrap_timeout}}]),
FormatLastLoc = test_server_sup:format_loc(get_last_loc(Loc)),
ErrorReason = io_lib:format("{timetrap_timeout,~s}", [FormatLastLoc]),
@@ -4138,7 +3815,7 @@ progress(failed, CaseNum, Mod, Func, Loc, {testcase_aborted,Reason}, _T,
print(1, "*** FAILED *** ~s",
[get_info_str(Func, CaseNum, get(test_server_cases))]),
test_server_sup:framework_call(report,
- [tc_done,{?pl2a(Mod),Func,
+ [tc_done,{Mod,Func,
{failed,testcase_aborted}}]),
FormatLastLoc = test_server_sup:format_loc(get_last_loc(Loc)),
ErrorReason = io_lib:format("{testcase_aborted,~s}", [FormatLastLoc]),
@@ -4163,7 +3840,7 @@ progress(failed, CaseNum, Mod, Func, unknown, Reason, Time,
print(major, "=result failed: ~p, ~p", [Reason,unknown]),
print(1, "*** FAILED *** ~s",
[get_info_str(Func, CaseNum, get(test_server_cases))]),
- test_server_sup:framework_call(report, [tc_done,{?pl2a(Mod),Func,
+ test_server_sup:framework_call(report, [tc_done,{Mod,Func,
{failed,Reason}}]),
TimeStr = io_lib:format(if is_float(Time) -> "~.3fs";
true -> "~w"
@@ -4199,7 +3876,7 @@ progress(failed, CaseNum, Mod, Func, Loc, Reason, Time,
print(major, "=result failed: ~p, ~p", [Reason,Loc]),
print(1, "*** FAILED *** ~s",
[get_info_str(Func, CaseNum, get(test_server_cases))]),
- test_server_sup:framework_call(report, [tc_done,{?pl2a(Mod),Func,
+ test_server_sup:framework_call(report, [tc_done,{Mod,Func,
{failed,Reason}}]),
TimeStr = io_lib:format(if is_float(Time) -> "~.3fs";
true -> "~w"
@@ -4224,7 +3901,7 @@ progress(failed, CaseNum, Mod, Func, Loc, Reason, 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}]),
+ test_server_sup:framework_call(report, [tc_done,{Mod,Func,ok}]),
Comment =
case RetVal of
{comment,RetComment} ->
@@ -4431,11 +4108,10 @@ do_format_exception(Reason={Error,Stack}) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% run_test_case_apply(CaseNum, Mod, Func, Args, Name, RunInit,
-%% Where, TimetrapData) ->
+%% TimetrapData) ->
%% {{Time,RetVal,Loc,Opts,Comment},DetectedFail,ProcessesBefore,ProcessesAfter} |
%% {{died,Reason,unknown,Comment},DetectedFail,ProcessesBefore,ProcessesAfter}
%% Name = atom()
-%% Where = target | host
%% Time = float() (seconds)
%% RetVal = term()
%% Loc = term()
@@ -4450,21 +4126,10 @@ do_format_exception(Reason={Error,Stack}) ->
%% 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, TimetrapData) ->
+run_test_case_apply(CaseNum, Mod, Func, Args, Name, RunInit,
+ TimetrapData) ->
test_server:run_test_case_apply({CaseNum,Mod,Func,Args,Name,RunInit,
- 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,
- TimetrapData});
- JobSock ->
- %% remote target
- request(JobSock, {test_case,{CaseNum,Mod,Func,Args,Name,RunInit,
- TimetrapData}}),
- read_job_sock_loop(JobSock)
- end.
+ TimetrapData}).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% print(Detail, Format, Args) -> ok
@@ -4474,16 +4139,6 @@ run_test_case_apply(CaseNum, Mod, Func, Args, Name, RunInit, target, TimetrapDat
%%
%% Just like io:format, except that depending on the Detail value, the output
%% 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).
-%%
-%% Buffered io is handled by the handle_test_case_io_and_status/0 function.
print(Detail, Format) ->
print(Detail, Format, []).
@@ -4496,19 +4151,7 @@ print(Detail, Format, Args, Printer) ->
print_or_buffer(Detail, Msg, Printer).
print_or_buffer(Detail, Msg, Printer) ->
- case get(test_server_minor_level) of
- _ when Detail == minor ->
- output({Detail,Msg}, Printer);
- MinLevel when is_number(Detail), Detail >= MinLevel ->
- output({Detail,Msg}, Printer);
- _ -> % Detail < Minor | major | html
- case get(test_server_common_io_handler) of
- undefined ->
- output({Detail,Msg}, Printer);
- {_,MainPid} ->
- MainPid ! {print,self(),Detail,Msg}
- end
- end.
+ test_server_gl:print(group_leader(), Detail, Msg, Printer).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% print_timestamp(Detail, Leader) -> ok
@@ -4572,112 +4215,6 @@ format(Detail, Format, Args) ->
print_or_buffer(Detail, Str, self()).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% output({Level,Message}, Sender) -> ok
-%% Level = integer() | minor | major | html
-%% Message = string() | [integer()]
-%% Sender = string() | internal
-%%
-%% Outputs the message on the channels indicated by Level. If Level is an
-%% atom, only the corresponding channel receives the output. When Level is
-%% an integer console, major and/or minor log file will receive output
-%% depending on the user set thresholds (see get_levels/0, set_levels/3)
-%%
-%% When printing on the console, the message is prefixed with the test
-%% suite's name. In case a name is not set (yet), Sender is used.
-%%
-%% When not outputting to the console, and the Sender is 'internal',
-%% the message is prefixed with "=== ", so that it will be apparent that
-%% the message comes from the test server and not the test suite itself.
-
-output({Level,Msg}, Sender) when is_integer(Level) ->
- SumLev = get(test_server_summary_level),
- if Level =< SumLev ->
- output_to_fd(stdout, Msg, Sender);
- true ->
- ok
- end,
- MajLev = get(test_server_major_level),
- if Level =< MajLev ->
- output_to_fd(get(test_server_major_fd), Msg, Sender);
- true ->
- ok
- end,
- MinLev = get(test_server_minor_level),
- if Level >= MinLev ->
- output_to_fd(get(test_server_minor_fd), Msg, Sender);
- true ->
- ok
- end;
-output({minor,Bytes}, Sender) when is_list(Bytes) ->
- output_to_fd(get(test_server_minor_fd), Bytes, Sender);
-output({major,Bytes}, Sender) when is_list(Bytes) ->
- output_to_fd(get(test_server_major_fd), Bytes, Sender);
-output({minor,Bytes}, Sender) when is_binary(Bytes) ->
- output_to_fd(get(test_server_minor_fd),binary_to_list(Bytes), Sender);
-output({major,Bytes}, Sender) when is_binary(Bytes) ->
- output_to_fd(get(test_server_major_fd),binary_to_list(Bytes), Sender);
-output({html,Msg}, _Sender) ->
- case get(test_server_html_fd) of
- undefined ->
- ok;
- Fd ->
- io:put_chars(Fd,Msg),
- case file:position(Fd, {cur, 0}) of
- {ok, Pos} ->
- %% We are writing to a seekable file. Finalise so
- %% we get complete valid (and viewable) HTML code.
- %% Then rewind to overwrite the finalising code.
- io:put_chars(Fd, "\n</table>\n"),
- case get(test_server_html_footer) of
- undefined ->
- io:put_chars(Fd, "</body>\n</html>\n");
- Footer ->
- io:put_chars(Fd, Footer)
- end,
- file:position(Fd, Pos);
- {error, epipe} ->
- %% The file is not seekable. We cannot erase what
- %% we've already written --- so the reader will
- %% have to wait until we're done.
- ok
- end
- end;
-output({minor,Data}, Sender) ->
- output_to_fd(get(test_server_minor_fd),
- lists:flatten(io_lib:format(
- "Unexpected output: ~p~n", [Data])),Sender);
-output({major,Data}, Sender) ->
- output_to_fd(get(test_server_major_fd),
- lists:flatten(io_lib:format(
- "Unexpected output: ~p~n", [Data])),Sender).
-
-output_to_fd(stdout, Msg, Sender) ->
- Name =
- case get(test_server_name) of
- undefined -> Sender;
- Other -> Other
- end,
- io:format("Testing ~s: ~s\n", [Name, lists:flatten(Msg)]);
-output_to_fd(undefined, _Msg, _Sender) ->
- ok;
-output_to_fd(Fd, [$=|Msg], internal) ->
- io:put_chars(Fd, [$=]),
- io:put_chars(Fd, Msg),
- io:put_chars(Fd, "\n");
-
-output_to_fd(Fd, Msg, internal) ->
- io:put_chars(Fd, [$=,$=,$=,$ ]),
- io:put_chars(Fd, Msg),
- io:put_chars(Fd, "\n");
-
-output_to_fd(Fd, Msg, _Sender) ->
- io:put_chars(Fd, Msg),
- case get(test_server_log_nl) of
- false -> ok;
- _ -> io:put_chars(Fd, "\n")
- end.
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% xhtml(BasicHtml, XHtml) -> BasicHtml | XHtml
%%
xhtml(HTML, XHTML) ->
@@ -5011,7 +4548,7 @@ collect_case_invoke(Mod, Case, MFA, St) ->
end;
_ ->
Suite = test_server_sup:framework_call(get_suite,
- [?pl2a(Mod),Case],
+ [Mod,Case],
[]),
collect_subcases(Mod, Case, MFA, St, Suite)
end.
@@ -5189,7 +4726,7 @@ get_target_info() ->
%% Called by test_server. See test_server:start_node/3 for details
start_node(Name, Type, Options) ->
- T = 10 * ?ACCEPT_TIMEOUT, % give some extra time
+ T = 10 * ?ACCEPT_TIMEOUT * test_server:timetrap_scale_factor(),
format(minor, "Attempt to start ~w node ~p with options ~p",
[Type, Name, Options]),
case controller_call({start_node,Name,Type,Options}, T) of
@@ -5234,7 +4771,8 @@ start_node(Name, Type, Options) ->
%% when the new node has contacted test_server_ctrl again
wait_for_node(Slave) ->
- case catch controller_call({wait_for_node,Slave},10000) of
+ T = 10000 * test_server:timetrap_scale_factor(),
+ case catch controller_call({wait_for_node,Slave},T) of
{'EXIT',{timeout,_}} -> {error,timeout};
ok -> ok
end.
@@ -5258,60 +4796,6 @@ stop_node(Slave) ->
controller_call({stop_node,Slave}).
-%%--------------------------------------------------------------------
-%% Functions handling target communication over socket
-
-%% Generic send function for communication with target
-request(Sock,Request) ->
- gen_tcp:send(Sock,<<1,(term_to_binary(Request))/binary>>).
-
-%% Receive and decode request on job specific socket
-%% Used when test is running on a remote target
-read_job_sock_loop(Sock) ->
- case gen_tcp:recv(Sock,0) of
- {error,Reason} ->
- gen_tcp:close(Sock),
- exit({controller,connection_lost,Reason});
- {ok,<<1,Request/binary>>} ->
- case decode(binary_to_term(Request)) of
- ok ->
- read_job_sock_loop(Sock);
- {stop,Result} ->
- Result
- end
- end.
-
-decode({apply,{M,F,A}}) ->
- apply(M,F,A),
- ok;
-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};
-decode({privdir,empty_priv_dir}) ->
- {stop,ok};
-decode({{privdir,PrivDirTar},TarBin}) ->
- Root = get(test_server_log_dir_base),
- unpack_tar(Root,PrivDirTar,TarBin),
- {stop,ok};
-decode({crash_dumps,no_crash_dumps}) ->
- {stop,ok};
-decode({{crash_dumps,CrashDumpTar},TarBin}) ->
- Dir = test_server_sup:crash_dump_dir(),
- unpack_tar(Dir,CrashDumpTar,TarBin),
- {stop,ok}.
-
-unpack_tar(Dir,TarFileName0,TarBin) ->
- TarFileName = filename:join(Dir,TarFileName0),
- ok = file:write_file(TarFileName,TarBin),
- ok = erl_tar:extract(TarFileName,[compressed,{cwd,Dir}]),
- ok = file:delete(TarFileName).
-
-
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% DEBUGGER INTERFACE %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -5416,33 +4900,52 @@ pinfo(P) ->
%% - 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'
-%% or under the tested application.
-%%
-%% The modules listed in the cross.cover file are modules that are
-%% hevily used by other applications than the one they belong
-%% to. After all tests are completed, these modules can be analysed
-%% with coverage data from all tests - see cross_cover_analyse/1. The
-%% result is stored in a file called cross_cover.html in the
-%% run.<timestamp> directory of the application the modules belong
-%% to.
-%%
-%% For example, the lists module is listed in cross.cover to be
-%% included in all tests. lists belongs to the stdlib
-%% application. cross_cover_analyse/1 will create a file named
-%% cross_cover.html under the newest stdlib.logs/run.xxx directory,
-%% where the coverage result for the lists module from all tests is
-%% presented.
-%%
-%% The lists module is also presented in the normal coverage log
-%% for stdlib, but that only includes the coverage achieved by
-%% the stdlib tests themselves.
-%%
-%% The Cross cover file cross.cover contains elements like this:
-%% {App,Modules}.
-%% where App can be an application name or the atom all. The
-%% application (or all applications) shall cover compile the listed
-%% Modules.
+%% {cross,[{Tag,List}]} part of the App.cover file
+%%
+%% The modules listed in the 'cross' part of the cover file are
+%% modules that are heavily used by other tests than the one where
+%% they are explicitly tested. They should then be listed as 'cross'
+%% in the cover file for the test where they are used but do not
+%% belong.
+%%
+%% After all tests are completed, the these modules can be analysed
+%% with coverage data from all tests where they are compiled - see
+%% cross_cover_analyse/2. The result is stored in a file called
+%% cross_cover.html in the run.<timestamp> directory of the
+%% test the modules belong to.
+%%
+%% Example:
+%% If the module m1 belongs to system s1 but is heavily used also in
+%% the tests for another system s2, then the cover files for the two
+%% systems could be like this:
+%%
+%% s1.cover:
+%% {include,[m1]}.
+%%
+%% s2.cover:
+%% {include,[....]}. % modules belonging to system s2
+%% {cross,[{s1,[m1]}]}.
+%%
+%% When the tests for both s1 and s2 are completed, run
+%% cross_cover_analyse(Level,[{s1,S1LogDir},{s2,S2LogDir}]), and
+%% the accumulated cover data for m1 will be written to
+%% S1LogDir/[run.<timestamp>/]cross_cover.html
+%%
+%% S1LogDir and S2LogDir are either the run.<timestamp> directories
+%% for the two tests, or the parent directory of these, in which case
+%% the latest run.<timestamp> directory will be chosen.
+%%
+%% Note that the m1 module will also be presented in the normal
+%% coverage log for s1 (due to the include statement in s1.cover), but
+%% that only includes the coverage achieved by the s1 test itself.
+%%
+%% The Tag in the 'cross' statement in the cover file has no other
+%% purpose than mapping the list of modules ([m1] in the example
+%% above) to the correct log directory where it should be included in
+%% the cross_cover.html file (S1LogDir in the example above).
+%% I.e. the value of the Tag has no meaning, it could be foo as well
+%% as s1 above, as long as the same Tag is used in the cover file and
+%% in the call to cross_cover_analyse/2.
%% Cover compilation
@@ -5451,62 +4954,68 @@ cover_compile({App,{_File,Exclude,Include,Cross,_Export}}) ->
cover_compile1({App,Exclude,Include,Cross});
cover_compile({App,CoverFile}) ->
- Cross = get_cross_modules(App),
- {Exclude,Include} = read_cover_file(CoverFile),
+ {Exclude,Include,Cross} = read_cover_file(CoverFile),
cover_compile1({App,Exclude,Include,Cross}).
cover_compile1(What) ->
- case get(test_server_ctrl_job_sock) of
- undefined ->
- %% local target
- test_server:cover_compile(What);
- JobSock ->
- %% remote target
- request(JobSock, {sync_apply,{test_server,cover_compile,[What]}}),
- read_job_sock_loop(JobSock)
- end.
-
+ test_server:cover_compile(What).
%% Read the coverfile for an application and return a list of modules
%% that are members of the application but shall not be compiled
%% (Exclude), and a list of modules that are not members of the
%% application but shall be compiled (Include).
read_cover_file(none) ->
- {[],[]};
+ {[],[],[]};
read_cover_file(CoverFile) ->
case file:consult(CoverFile) of
{ok,List} ->
- case check_cover_file(List, [], []) of
- {ok,Exclude,Include} -> {Exclude,Include};
+ case check_cover_file(List, [], [], []) of
+ {ok,Exclude,Include,Cross} -> {Exclude,Include,Cross};
error ->
io:fwrite("Faulty format of CoverFile ~p\n", [CoverFile]),
- {[],[]}
+ {[],[],[]}
end;
{error,Reason} ->
io:fwrite("Can't read CoverFile ~p\nReason: ~p\n",
[CoverFile,Reason]),
- {[],[]}
+ {[],[],[]}
end.
-check_cover_file([{exclude,all}|Rest], _, Include) ->
- check_cover_file(Rest, all, Include);
-check_cover_file([{exclude,Exclude}|Rest], _, Include) ->
+check_cover_file([{exclude,all}|Rest], _, Include, Cross) ->
+ check_cover_file(Rest, all, Include, Cross);
+check_cover_file([{exclude,Exclude}|Rest], _, Include, Cross) ->
case lists:all(fun(M) -> is_atom(M) end, Exclude) of
true ->
- check_cover_file(Rest, Exclude, Include);
+ check_cover_file(Rest, Exclude, Include, Cross);
false ->
error
end;
-check_cover_file([{include,Include}|Rest], Exclude, _) ->
+check_cover_file([{include,Include}|Rest], Exclude, _, Cross) ->
case lists:all(fun(M) -> is_atom(M) end, Include) of
true ->
- check_cover_file(Rest, Exclude, Include);
+ check_cover_file(Rest, Exclude, Include, Cross);
false ->
error
end;
-check_cover_file([], Exclude, Include) ->
- {ok,Exclude,Include}.
+check_cover_file([{cross,Cross}|Rest], Exclude, Include, _) ->
+ case check_cross(Cross) of
+ true ->
+ check_cover_file(Rest, Exclude, Include, Cross);
+ false ->
+ error
+ end;
+check_cover_file([], Exclude, Include, Cross) ->
+ {ok,Exclude,Include,Cross}.
+check_cross([{Tag,Modules}|Rest]) ->
+ case lists:all(fun(M) -> is_atom(M) end, [Tag|Modules]) of
+ true ->
+ check_cross(Rest);
+ false ->
+ false
+ end;
+check_cross([]) ->
+ true.
%% Cover analysis, per application
@@ -5517,7 +5026,7 @@ check_cover_file([], Exclude, Include) ->
%%
%% This per application analysis writes the file cover.html in the
%% application's run.<timestamp> directory.
-cover_analyse({App,CoverInfo}, Analyse, AnalyseMods, TestDir) ->
+cover_analyse({App,CoverInfo}, Analyse, AnalyseMods, Stop, TestDir) ->
write_default_cross_coverlog(TestDir),
{ok,CoverLog} = file:open(filename:join(TestDir, ?coverlog_name), [write]),
@@ -5527,16 +5036,17 @@ cover_analyse({App,CoverInfo}, Analyse, AnalyseMods, TestDir) ->
"<p><a href=\"~s\">Coverdata collected over all tests</a></p>",
[?cross_coverlog_name]),
- {CoverFile,_Included,Excluded} =
+ {CoverFile,_Included,Excluded,Cross} =
case CoverInfo of
- {File,Excl,Incl,_Cross,Export} ->
+ {File,Excl,Incl,Cr,Export} ->
cover:export(Export),
- {File,Incl,Excl};
+ {File,Incl,Excl,Cr};
File ->
- {Excl,Incl} = read_cover_file(File),
- {File,Incl,Excl}
+ {Excl,Incl,Cr} = read_cover_file(File),
+ {File,Incl,Excl,Cr}
end,
io:fwrite(CoverLog, "<p>CoverFile: <code>~p</code>\n", [CoverFile]),
+ write_cross_cover_info(TestDir,Cross),
case length(cover:imported_modules()) of
Imps when Imps > 0 ->
@@ -5548,7 +5058,9 @@ cover_analyse({App,CoverInfo}, Analyse, AnalyseMods, TestDir) ->
io:fwrite(CoverLog, "<p>Excluded module(s): <code>~p</code>\n", [Excluded]),
- Coverage = cover_analyse(Analyse, AnalyseMods),
+ Coverage = cover_analyse(Analyse, AnalyseMods, Stop),
+ file:write_file(filename:join(TestDir,?raw_coverlog_name),
+ term_to_binary(Coverage)),
case lists:filter(fun({_M,{_,_,_}}) -> false;
(_) -> true
@@ -5565,32 +5077,27 @@ cover_analyse({App,CoverInfo}, Analyse, AnalyseMods, TestDir) ->
file:write_file(filename:join(TestDir, ?cover_total),
term_to_binary(TotPercent)).
-cover_analyse(Analyse, AnalyseMods) ->
+cover_analyse(Analyse, AnalyseMods, Stop) ->
TestDir = get(test_server_log_dir_base),
- case get(test_server_ctrl_job_sock) of
- undefined ->
- %% local target
- test_server:cover_analyse({Analyse,TestDir}, AnalyseMods);
- JobSock ->
- %% remote target
- request(JobSock, {sync_apply,{test_server,
- cover_analyse,
- [Analyse,AnalyseMods]}}),
- read_job_sock_loop(JobSock)
- end.
+ test_server:cover_analyse({Analyse,TestDir}, AnalyseMods, Stop).
-%% Cover analysis, cross application
+%% Cover analysis - accumulated over multiple tests
%% This can be executed on any node after all tests are finished.
-%% The node's current directory must be the same as when the tests
-%% were run.
-cross_cover_analyse(Analyse) ->
- cross_cover_analyse(Analyse, undefined).
-
-cross_cover_analyse(Analyse, CrossModules) ->
- CoverdataFiles = get_coverdata_files(),
+%% Analyse = overview | details
+%% TagDirs = [{Tag,Dir}]
+%% Tag = atom(), identifier
+%% Dir = string(), the log directory for Tag, it can be a
+%% run.<timestamp> directory or the parent directory of
+%% such (in which case the latest run.<timestamp> directory
+%% is used)
+cross_cover_analyse(Analyse, TagDirs0) ->
+ TagDirs = get_latest_run_dirs(TagDirs0),
+ TagMods = get_all_cross_info(TagDirs,[]),
+ TagDirMods = add_cross_modules(TagMods,TagDirs),
+ CoverdataFiles = get_coverdata_files(TagDirMods),
lists:foreach(fun(CDF) -> cover:import(CDF) end, CoverdataFiles),
- io:fwrite("Cover analysing... ", []),
+ io:fwrite("Cover analysing...\n", []),
DetailsFun =
case Analyse of
details ->
@@ -5598,100 +5105,111 @@ cross_cover_analyse(Analyse, CrossModules) ->
OutFile = filename:join(Dir,
atom_to_list(M) ++
".CROSS_COVER.html"),
- cover:analyse_to_file(M, OutFile, [html]),
- {file,OutFile}
+ case cover:analyse_to_file(M, OutFile, [html]) of
+ {ok,_} ->
+ {file,OutFile};
+ Error ->
+ Error
+ end
end;
_ ->
fun(_,_) -> undefined end
end,
- SortedModules =
- case CrossModules of
- undefined ->
- sort_modules([Mod || Mod <- get_all_cross_modules(),
- lists:member(Mod, cover:imported_modules())], []);
- _ ->
- sort_modules(CrossModules, [])
- end,
- Coverage = analyse_apps(SortedModules, DetailsFun, []),
+ Coverage = analyse_tests(TagDirMods, DetailsFun, []),
cover:stop(),
- write_cross_cover_logs(Coverage).
+ write_cross_cover_logs(Coverage,TagDirMods).
-%% For each application from which there are modules listed in the
-%% 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 ->
- ok;
- Dir ->
+write_cross_cover_info(_Dir,[]) ->
+ ok;
+write_cross_cover_info(Dir,Cross) ->
+ {ok,Fd} = file:open(filename:join(Dir,?cross_cover_info),[write]),
+ lists:foreach(fun(C) -> io:format(Fd,"~p.~n",[C]) end, Cross),
+ file:close(Fd).
+
+%% For each test from which there are cross cover analysed
+%% modules, write a cross cover log (cross_cover.html).
+write_cross_cover_logs([{Tag,Coverage}|T],TagDirMods) ->
+ case lists:keyfind(Tag,1,TagDirMods) of
+ {_,Dir,Mods} when Mods=/=[] ->
+ file:write_file(filename:join(Dir,?raw_cross_coverlog_name),
+ term_to_binary(Coverage)),
CoverLogName = filename:join(Dir,?cross_coverlog_name),
{ok,CoverLog} = file:open(CoverLogName, [write]),
write_coverlog_header(CoverLog),
io:fwrite(CoverLog,
"<h1>Coverage results for \'~w\' from all tests</h1>\n",
- [App]),
+ [Tag]),
write_cover_result_table(CoverLog, Coverage),
- io:fwrite("Written file ~p\n", [CoverLogName])
+ io:fwrite("Written file ~p\n", [CoverLogName]);
+ _ ->
+ ok
end,
- write_cross_cover_logs(T);
-write_cross_cover_logs([]) ->
+ write_cross_cover_logs(T,TagDirMods);
+write_cross_cover_logs([],_) ->
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) ||
- Dir <- filelib:wildcard([$*|?logdir_ext]),
- filelib:is_dir(Dir)],
- [File || File <- PossibleFiles, filelib:is_file(File)].
-
-last_coverdata_file(Dir) ->
- LastDir = last_test(filelib:wildcard(filename:join(Dir,"run.[1-2]*")),false),
- filename:join(LastDir,"all.coverdata").
-
-
-%% Find the latest run.<timestamp> directory for the given application.
-last_test_for_app(App) ->
- AppLogDir = atom_to_list(App)++?logdir_ext,
- last_test(filelib:wildcard(filename:join(AppLogDir,"run.[1-2]*")),false).
-
-last_test([Run|Rest], false) ->
- last_test(Rest, Run);
-last_test([Run|Rest], Latest) when Run > Latest ->
- last_test(Rest, Run);
-last_test([_|Rest], Latest) ->
- last_test(Rest, Latest);
-last_test([], Latest) ->
+%% Get the latest run.<timestamp> directories
+get_latest_run_dirs([{Tag,Dir}|Rest]) ->
+ [{Tag,get_latest_run_dir(Dir)} | get_latest_run_dirs(Rest)];
+get_latest_run_dirs([]) ->
+ [].
+
+get_latest_run_dir(Dir) ->
+ case filelib:wildcard(filename:join(Dir,"run.[1-2]*")) of
+ [] ->
+ Dir;
+ [H|T] ->
+ get_latest_dir(T,H)
+ end.
+
+get_latest_dir([H|T],Latest) when H>Latest ->
+ get_latest_dir(T,H);
+get_latest_dir([_|T],Latest) ->
+ get_latest_dir(T,Latest);
+get_latest_dir([],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 =
- case lists:keysearch(App, 1, Acc) of
- {value,{App,LastTest,List}} ->
- lists:keyreplace(App, 1, Acc, {App,LastTest,[M|List]});
+get_all_cross_info([{_Tag,Dir}|Rest],Acc) ->
+ case file:consult(filename:join(Dir,?cross_cover_info)) of
+ {ok,TagMods} ->
+ get_all_cross_info(Rest,TagMods++Acc);
+ _ ->
+ get_all_cross_info(Rest,Acc)
+ end;
+get_all_cross_info([],Acc) ->
+ Acc.
+
+%% Associate the cross cover modules with their log directories
+add_cross_modules(TagMods,TagDirs)->
+ do_add_cross_modules(TagMods,[{Tag,Dir,[]} || {Tag,Dir} <- TagDirs]).
+do_add_cross_modules([{Tag,Mods1}|TagMods],TagDirMods)->
+ NewTagDirMods =
+ case lists:keytake(Tag,1,TagDirMods) of
+ {value,{Tag,Dir,Mods},Rest} ->
+ [{Tag,Dir,lists:umerge(lists:sort(Mods1),Mods)}|Rest];
false ->
- [{App,last_test_for_app(App),[M]}|Acc]
+ TagDirMods
end,
- sort_modules(Modules, Acc1);
-sort_modules([], Acc) ->
- Acc.
+ do_add_cross_modules(TagMods,NewTagDirMods);
+do_add_cross_modules([],TagDirMods) ->
+ %% Just to get the modules in the same order as in the normal cover log
+ [{Tag,Dir,lists:reverse(Mods)} || {Tag,Dir,Mods} <- TagDirMods].
-get_app(Module) ->
- Beam = code:which(Module),
- AppDir = filename:basename(filename:dirname(filename:dirname(Beam))),
- [AppStr|_] = string:tokens(AppDir,"-"),
- list_to_atom(AppStr).
+%% Find all exported coverdata files.
+get_coverdata_files(TagDirMods) ->
+ lists:flatmap(
+ fun({_,LatestDir,_}) ->
+ filelib:wildcard(filename:join(LatestDir,"all.coverdata"))
+ end,
+ TagDirMods).
-%% For each application, analyse all modules
+%% For each test, analyse all modules
%% Used for cross cover analysis.
-analyse_apps([{App,LastTest,Modules}|T], DetailsFun, Acc) ->
+analyse_tests([{Tag,LastTest,Modules}|T], DetailsFun, Acc) ->
Cov = analyse_modules(LastTest, Modules, DetailsFun, []),
- analyse_apps(T, DetailsFun, [{App,Cov}|Acc]);
-analyse_apps([], _DetailsFun, Acc) ->
+ analyse_tests(T, DetailsFun, [{Tag,Cov}|Acc]);
+analyse_tests([], _DetailsFun, Acc) ->
Acc.
%% Analyse each module
@@ -5704,27 +5222,6 @@ analyse_modules(_Dir, [], _DetailsFun, Acc) ->
Acc.
-%% Read the cross cover file (cross.cover)
-get_all_cross_modules() ->
- get_cross_modules(all).
-get_cross_modules(App) ->
- case file:consult(?cross_cover_file) of
- {ok,List} ->
- get_cross_modules(App, List, []);
- _X ->
- []
- end.
-
-get_cross_modules(App, [{_To,Modules}|T], Acc) when App==all->
- get_cross_modules(App, T, Acc ++ Modules);
-get_cross_modules(App, [{To,Modules}|T], Acc) when To==App; To==all->
- get_cross_modules(App, T, Acc ++ Modules);
-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
@@ -5808,11 +5305,11 @@ write_default_cross_coverlog(TestDir) ->
{ok,CrossCoverLog} =
file:open(filename:join(TestDir,?cross_coverlog_name), [write]),
write_coverlog_header(CrossCoverLog),
- io:fwrite(CrossCoverLog,
- ["No cross cover modules exist for this application,",
- xhtml("<br>","<br />"),
- "or cross cover analysis is not completed.\n"
- "</body></html>\n"], []),
+ io:put_chars(CrossCoverLog,
+ ["No cross cover modules exist for this application,",
+ xhtml("<br>","<br />"),
+ "or cross cover analysis is not completed.\n"
+ "</body></html>\n"]),
file:close(CrossCoverLog).
write_cover_result_table(CoverLog,Coverage) ->
diff --git a/lib/test_server/src/test_server_gl.erl b/lib/test_server/src/test_server_gl.erl
new file mode 100644
index 0000000000..d32c7c07dc
--- /dev/null
+++ b/lib/test_server/src/test_server_gl.erl
@@ -0,0 +1,293 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, 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 module implements group leader processes for test cases.
+%% Each group leader process handles output to the minor log file for
+%% a test case, and calls test_server_io to handle output to the common
+%% log files. The group leader processes are created and destroyed
+%% through the test_server_io module/process.
+
+-module(test_server_gl).
+-export([start_link/0,stop/1,set_minor_fd/3,unset_minor_fd/1,
+ get_tc_supervisor/1,print/4,set_props/2]).
+
+-export([init/1,handle_call/3,handle_cast/2,handle_info/2,terminate/2]).
+
+-record(st, {tc_supervisor :: 'none'|pid(), %Test case supervisor
+ tc :: mfa(), %Current test case MFA
+ minor :: 'none'|pid(), %Minor fd
+ minor_monitor, %Monitor ref for minor fd
+ capture :: 'none'|pid(), %Capture output
+ reject_io :: boolean(), %Reject I/O requests...
+ permit_io, %... and exceptions
+ auto_nl=true :: boolean(), %Automatically add NL
+ levels %{Stdout,Major,Minor}
+ }).
+
+%% start_link()
+%% Start a new group leader process. Only to be called by
+%% the test_server_io process.
+
+start_link() ->
+ case gen_server:start_link(?MODULE, [], []) of
+ {ok,Pid} ->
+ {ok,Pid};
+ Other ->
+ Other
+ end.
+
+
+%% stop(Pid)
+%% Stop a group leader process. Only to be called by
+%% the test_server_io process.
+
+stop(GL) ->
+ gen_server:cast(GL, stop).
+
+
+%% set_minor_fd(GL, Fd, MFA)
+%% GL = Pid for the group leader process
+%% Fd = file descriptor for the minor log file
+%% MFA = {M,F,A} for the test case owning the minor log file
+%%
+%% Register the file descriptor for the minor log file. Subsequent
+%% IO directed to the minor log file will be written to this file.
+%% Also register the currently executing process at the testcase
+%% supervisor corresponding to this group leader process.
+
+set_minor_fd(GL, Fd, MFA) ->
+ req(GL, {set_minor_fd,Fd,MFA,self()}).
+
+
+%% unset_minor_fd(GL, Fd, MFA)
+%% GL = Pid for the group leader process
+%%
+%% Unregister the file descriptor for minor log file (typically
+%% because the test case has ended the minor log file is about
+%% to be closed). Subsequent IO (for example, by a process spawned
+%% by the testcase process) will go to the unexpected_io log file.
+
+unset_minor_fd(GL) ->
+ req(GL, unset_minor_fd).
+
+
+%% get_tc_supervisor(GL)
+%% GL = Pid for the group leader process
+%%
+%% Return the Pid for the process that supervises the test case
+%% that has this group leader.
+
+get_tc_supervisor(GL) ->
+ req(GL, get_tc_supervisor).
+
+
+%% print(GL, Detail, Format, Args) -> ok
+%% GL = Pid for the group leader process
+%% Detail = integer() | minor | major | html | stdout
+%% Msg = iodata()
+%% Printer = internal | pid()
+%%
+%% Print a message to one of the log files. If Detail is an integer,
+%% it will be compared to the levels (set by set_props/2) to
+%% determine which log file(s) that are to receive the output. If
+%% Detail is an atom, the value of the atom will directly determine
+%% which log file to use. IO to the minor log file will be handled
+%% directly by this group leader process (printing to the file set by
+%% set_minor_fd/3), and all other IO will be handled by calling
+%% test_server_io:print/3.
+
+print(GL, Detail, Msg, Printer) ->
+ req(GL, {print,Detail,Msg,Printer}).
+
+
+%% set_props(GL, [PropertyTuple])
+%% GL = Pid for the group leader process
+%% PropertyTuple = {levels,{Show,Major,Minor}} |
+%% {auto_nl,boolean()} |
+%% {reject_io_reqs,boolean()}
+%%
+%% Set properties for this group leader process.
+
+set_props(GL, PropList) ->
+ req(GL, {set_props,PropList}).
+
+%%% Internal functions.
+
+init([]) ->
+ {ok,#st{tc_supervisor=none,
+ minor=none,
+ minor_monitor=none,
+ capture=none,
+ reject_io=false,
+ permit_io=gb_sets:empty(),
+ auto_nl=true,
+ levels={1,19,10}
+ }}.
+
+req(GL, Req) ->
+ gen_server:call(GL, Req, infinity).
+
+handle_call(get_tc_supervisor, _From, #st{tc_supervisor=Pid}=St) ->
+ {reply,Pid,St};
+handle_call({set_minor_fd,Fd,MFA,Supervisor}, _From, St) ->
+ Ref = erlang:monitor(process, Fd),
+ {reply,ok,St#st{tc=MFA,minor=Fd,minor_monitor=Ref,
+ tc_supervisor=Supervisor}};
+handle_call(unset_minor_fd, _From, St) ->
+ {reply,ok,St#st{minor=none,tc_supervisor=none}};
+handle_call({set_props,PropList}, _From, St) ->
+ {reply,ok,do_set_props(PropList, St)};
+handle_call({print,Detail,Msg,Printer}, {From,_}, St) ->
+ output(Detail, Msg, Printer, From, St),
+ {reply,ok,St}.
+
+handle_cast(stop, St) ->
+ {stop,normal,St}.
+
+handle_info({'DOWN',Ref,process,_,_}, #st{minor_monitor=Ref}=St) ->
+ {noreply,St#st{minor=none,minor_monitor=none}};
+handle_info({permit_io,Pid}, #st{permit_io=P}=St) ->
+ {noreply,St#st{permit_io=gb_sets:add(Pid, P)}};
+handle_info({capture,Cap0}, St) ->
+ Cap = case Cap0 of
+ false -> none;
+ Pid when is_pid(Cap0) -> Pid
+ end,
+ {noreply,St#st{capture=Cap}};
+handle_info({io_request,From,ReplyAs,Req}=IoReq, St) ->
+ try io_req(Req, From, St) of
+ passthrough ->
+ group_leader() ! IoReq;
+ Data ->
+ case is_io_permitted(From, St) of
+ false ->
+ ok;
+ true ->
+ case St of
+ #st{capture=none} ->
+ ok;
+ #st{capture=CapturePid} ->
+ CapturePid ! {captured,Data}
+ end,
+ output(minor, Data, From, From, St)
+ end,
+ From ! {io_reply,ReplyAs,ok}
+ catch
+ _:_ ->
+ {io_reply,ReplyAs,{error,arguments}}
+ end,
+ {noreply,St};
+handle_info({structured_io,ClientPid,{Detail,Str}}, St) ->
+ output(Detail, Str, ClientPid, ClientPid, St),
+ {noreply,St};
+handle_info({printout,Detail,Format,Args}, St) ->
+ Str = io_lib:format(Format, Args),
+ output(Detail, Str, internal, none, St),
+ {noreply,St};
+handle_info(Msg, #st{tc_supervisor=Pid}=St) when is_pid(Pid) ->
+ %% The process overseeing the testcase process also used to be
+ %% the group leader; thus, it is widely expected that it can be
+ %% reached by sending a message to the group leader. Therefore
+ %% we'll need to forward any non-recognized messaged to the test
+ %% case supervisor.
+ Pid ! Msg,
+ {noreply,St};
+handle_info(_Msg, #st{}=St) ->
+ %% There is no known supervisor process. Ignore this message.
+ {noreply,St}.
+
+terminate(_, _) ->
+ ok.
+
+do_set_props([{levels,Levels}|Ps], St) ->
+ do_set_props(Ps, St#st{levels=Levels});
+do_set_props([{auto_nl,AutoNL}|Ps], St) ->
+ do_set_props(Ps, St#st{auto_nl=AutoNL});
+do_set_props([{reject_io_reqs,Bool}|Ps], St) ->
+ do_set_props(Ps, St#st{reject_io=Bool});
+do_set_props([], St) -> St.
+
+io_req({put_chars,Enc,Bytes}, _, _) when Enc =:= latin1; Enc =:= unicode ->
+ to_latin1(Enc, Bytes);
+io_req({put_chars,Encoding,Mod,Func,[Format,Args]}, _, _) ->
+ Str = Mod:Func(Format, Args),
+ to_latin1(Encoding, Str);
+io_req(_, _, _) -> passthrough.
+
+to_latin1(unicode, Str) ->
+ [if C > 255 ->
+ io_lib:format("\\{~.8B}", [C]);
+ true ->
+ C
+ end || C <- unicode:characters_to_list(Str, unicode)];
+to_latin1(latin1, Str) -> Str.
+
+output(Level, Str, Sender, From, St) when is_integer(Level) ->
+ case selected_by_level(Level, stdout, St) of
+ true -> output(stdout, Str, Sender, From, St);
+ false -> ok
+ end,
+ case selected_by_level(Level, major, St) of
+ true -> output(major, Str, Sender, From, St);
+ false -> ok
+ end,
+ case selected_by_level(Level, minor, St) of
+ true -> output(minor, Str, Sender, From, St);
+ false -> ok
+ end;
+output(stdout, Str, _Sender, From, St) ->
+ output_to_file(stdout, Str, From, St);
+output(html, Str, _Sender, From, St) ->
+ output_to_file(html, Str, From, St);
+output(Level, Str, Sender, From, St) when is_atom(Level) ->
+ output_to_file(Level, dress_output(Str, Sender, St), From, St).
+
+output_to_file(minor, Data0, From, #st{tc={M,F,A},minor=none}) ->
+ Data = [io_lib:format("=== ~p:~p/~p\n", [M,F,A]),Data0],
+ test_server_io:print(From, unexpected_io, Data),
+ ok;
+output_to_file(minor, Data, From, #st{minor=Fd}) ->
+ try
+ io:put_chars(Fd, Data)
+ catch
+ _:_ ->
+ test_server_io:print(From, unexpected_io, Data)
+ end;
+output_to_file(Detail, Data, From, _) ->
+ test_server_io:print(From, Detail, Data).
+
+is_io_permitted(From, #st{reject_io=true,permit_io=P}) ->
+ gb_sets:is_member(From, P);
+is_io_permitted(_, #st{reject_io=false}) -> true.
+
+selected_by_level(Level, stdout, #st{levels={Stdout,_,_}}) ->
+ Level =< Stdout;
+selected_by_level(Level, major, #st{levels={_,Major,_}}) ->
+ Level =< Major;
+selected_by_level(Level, minor, #st{levels={_,_,Minor}}) ->
+ Level >= Minor.
+
+dress_output([$=|_]=Str, internal, _) ->
+ [Str,$\n];
+dress_output(Str, internal, _) ->
+ ["=== ",Str,$\n];
+dress_output(Str, _, #st{auto_nl=AutoNL}) ->
+ case AutoNL of
+ true -> [Str,$\n];
+ false -> Str
+ end.
diff --git a/lib/test_server/src/test_server_h.erl b/lib/test_server/src/test_server_h.erl
index fdeee59326..78daba855d 100644
--- a/lib/test_server/src/test_server_h.erl
+++ b/lib/test_server/src/test_server_h.erl
@@ -131,6 +131,11 @@ report_receiver(warning_msg, _) -> kernel;
report_receiver(warning_report, _) -> kernel;
report_receiver(info, _) -> kernel;
report_receiver(info_msg, _) -> kernel;
+report_receiver(info_report,Tuple)
+ when is_tuple(Tuple) andalso
+ (element(1,Tuple)==ct_connection orelse
+ element(1,Tuple)==conn_log) ->
+ none;
report_receiver(info_report, _) -> kernel;
report_receiver(_, _) -> none.
diff --git a/lib/test_server/src/test_server_internal.hrl b/lib/test_server/src/test_server_internal.hrl
index c9c52854e3..d204c35293 100644
--- a/lib/test_server/src/test_server_internal.hrl
+++ b/lib/test_server/src/test_server_internal.hrl
@@ -24,8 +24,7 @@
%% Target information generated by test_server:init_target_info/0 and
%% test_server_ctrl:contact_main_target/2
%% Once initiated, this information will never change!!
--record(target_info, {where, % local | Socket
- os_family, % atom(); win32 | unix | vxworks | ose
+-record(target_info, {os_family, % atom(); win32 | unix
os_type, % result of os:type()
host, % string(); the name of the target machine
version, % string()
@@ -43,7 +42,6 @@
% itself is master for slave nodes
%% The following are only used for remote targets
- target_client, % reference to a client talking to target
slave_targets=[]}).% list() of atom(); all available
% targets for starting slavenodes
diff --git a/lib/test_server/src/test_server_io.erl b/lib/test_server/src/test_server_io.erl
new file mode 100644
index 0000000000..777b377201
--- /dev/null
+++ b/lib/test_server/src/test_server_io.erl
@@ -0,0 +1,319 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, 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 module implements a process with the registered name 'test_server_io',
+%% which has two main responsibilities:
+%%
+%% * Manage group leader processes (see the test_server_gl module)
+%% for test cases. A group_leader process is obtained by calling
+%% get_gl/1. Group leader processes will be kept alive as along as
+%% the 'test_server_io' process is alive.
+%%
+%% * Handle output to the common log files (stdout, major, html,
+%% unexpected_io).
+%%
+
+-module(test_server_io).
+-export([start_link/0,stop/0,get_gl/1,set_fd/2,
+ start_transaction/0,end_transaction/0,print_buffered/1,print/3,
+ set_footer/1,set_job_name/1,set_gl_props/1]).
+
+-export([init/1,handle_call/3,handle_info/2,terminate/2]).
+
+-record(st, {fds, %Singleton fds (gb_tree)
+ shared_gl :: pid(), %Shared group leader
+ gls, %Group leaders (gb_set)
+ io_buffering=false, %I/O buffering
+ buffered, %Buffered I/O requests
+ html_footer, %HTML footer
+ job_name, %Name of current job.
+ gl_props, %Properties for GL.
+ stopping
+ }).
+
+start_link() ->
+ case gen_server:start_link({local,?MODULE}, ?MODULE, [], []) of
+ {ok,Pid} ->
+ {ok,Pid};
+ Other ->
+ Other
+ end.
+
+stop() ->
+ OldGL = group_leader(),
+ group_leader(self(), self()),
+ req(stop),
+ group_leader(OldGL, self()),
+ ok.
+
+%% get_gl(Shared) -> Pid
+%% Shared = boolean()
+%% Pid = pid()
+%%
+%% Return a group leader (a process using the test_server_gl module).
+%% If Shared is true, the shared group leader is returned (suitable for
+%% running sequential test cases), otherwise a new group leader process
+%% is spawned. Group leader processes will live until the
+%% 'test_server_io' process is stopped.
+
+get_gl(Shared) when is_boolean(Shared) ->
+ req({get_gl,Shared}).
+
+%% set_fd(Tag, Fd) -> ok.
+%% Tag = major | html | unexpected_io
+%% Fd = a file descriptor (as returned by file:open/2)
+%%
+%% Associate a file descriptor with the given Tag. This
+%% Tag can later be used in when calling to print/3.
+
+set_fd(Tag, Fd) ->
+ req({set_fd,Tag,Fd}).
+
+%% start_transaction()
+%%
+%% Subsequent calls to print/3 from the process executing start_transaction/0
+%% will cause the messages to be buffered instead of printed directly.
+
+start_transaction() ->
+ req({start_transaction,self()}).
+
+%% end_transaction()
+%%
+%% End the transaction started by start_transaction/0. Subsequent calls to
+%% print/3 will cause the message to be printed directly.
+
+end_transaction() ->
+ req({end_transaction,self()}).
+
+%% print(From, Tag, Msg)
+%% From = pid()
+%% Tag = stdout, or any tag that has been registered using set_fd/2
+%% Msg = string or iolist
+%%
+%% Either print Msg to the file identified by Tag, or buffer the message
+%% start_transaction/0 has been called from the process From.
+%%
+%% NOTE: The tags have various special meanings. For example, 'html'
+%% is assumed to be a HTML file.
+
+print(From, Tag, Msg) ->
+ req({print,From,Tag,Msg}).
+
+%% print_buffered(Pid)
+%% Pid = pid()
+%%
+%% Print all messages buffered in the *first* transaction buffered for Pid.
+%% (If start_transaction/0 and end_transaction/0 has been called N times,
+%% print_buffered/1 must be called N times to print all transactions.)
+
+print_buffered(Pid) ->
+ req({print_buffered,Pid}).
+
+%% set_footer(IoData)
+%%
+%% Set a footer for the file associated with the 'html' tag.
+%% It will be used by print/3 to print a footer for the HTML file.
+
+set_footer(Footer) ->
+ req({set_footer,Footer}).
+
+%% set_job_name(Name)
+%% Set a name for the currently running job. The name will be used
+%% when printing to 'stdout'.
+%%
+set_job_name(Name) ->
+ req({set_job_name,Name}).
+
+%% set_gl_props(PropList)
+%% Set properties for group leader processes. When a group_leader process
+%% is created, test_server_gl:set_props(PropList) will be called.
+
+set_gl_props(PropList) ->
+ req({set_gl_props,PropList}).
+
+
+%%% Internal functions.
+
+init([]) ->
+ process_flag(trap_exit, true),
+ Empty = gb_trees:empty(),
+ {ok,Shared} = test_server_gl:start_link(),
+ {ok,#st{fds=Empty,shared_gl=Shared,gls=gb_sets:empty(),
+ io_buffering=gb_sets:empty(),
+ buffered=Empty,
+ html_footer="</body>\n</html>\n",
+ job_name="<name not set>",
+ gl_props=[]}}.
+
+req(Req) ->
+ gen_server:call(?MODULE, Req, infinity).
+
+handle_call({get_gl,false}, _From, #st{gls=Gls,gl_props=Props}=St) ->
+ {ok,Pid} = test_server_gl:start_link(),
+ test_server_gl:set_props(Pid, Props),
+ {reply,Pid,St#st{gls=gb_sets:insert(Pid, Gls)}};
+handle_call({get_gl,true}, _From, #st{shared_gl=Shared}=St) ->
+ {reply,Shared,St};
+handle_call({set_fd,Tag,Fd}, _From, #st{fds=Fds0}=St) ->
+ Fds = gb_trees:enter(Tag, Fd, Fds0),
+ {reply,ok,St#st{fds=Fds}};
+handle_call({start_transaction,Pid}, _From, #st{io_buffering=Buffer0,
+ buffered=Buf0}=St) ->
+ Buf = case gb_trees:is_defined(Pid, Buf0) of
+ false -> gb_trees:insert(Pid, queue:new(), Buf0);
+ true -> Buf0
+ end,
+ Buffer = gb_sets:add(Pid, Buffer0),
+ {reply,ok,St#st{io_buffering=Buffer,buffered=Buf}};
+handle_call({print,From,Tag,Str}, _From, St0) ->
+ St = output(From, Tag, Str, St0),
+ {reply,ok,St};
+handle_call({end_transaction,Pid}, _From, #st{io_buffering=Buffer0,
+ buffered=Buffered0}=St0) ->
+ Q0 = gb_trees:get(Pid, Buffered0),
+ Q = queue:in(eot, Q0),
+ Buffered = gb_trees:update(Pid, Q, Buffered0),
+ Buffer = gb_sets:delete_any(Pid, Buffer0),
+ St = St0#st{io_buffering=Buffer,buffered=Buffered},
+ {reply,ok,St};
+handle_call({print_buffered,Pid}, _From, #st{buffered=Buffered0}=St0) ->
+ Q0 = gb_trees:get(Pid, Buffered0),
+ Q = do_print_buffered(Q0, St0),
+ Buffered = gb_trees:update(Pid, Q, Buffered0),
+ St = St0#st{buffered=Buffered},
+ {reply,ok,St};
+handle_call({set_footer,Footer}, _From, St) ->
+ {reply,ok,St#st{html_footer=Footer}};
+handle_call({set_job_name,Name}, _From, St) ->
+ {reply,ok,St#st{job_name=Name}};
+handle_call({set_gl_props,Props}, _From, #st{shared_gl=Shared}=St) ->
+ test_server_gl:set_props(Shared, Props),
+ {reply,ok,St#st{gl_props=Props}};
+handle_call(stop, From, #st{shared_gl=SGL,gls=Gls0}=St0) ->
+ St = St0#st{gls=gb_sets:insert(SGL, Gls0),stopping=From},
+ gc(St),
+ %% Give the users of the surviving group leaders some
+ %% time to finish.
+ erlang:send_after(2000, self(), stop_group_leaders),
+ {noreply,St}.
+
+handle_info({'EXIT',Pid,normal}, #st{gls=Gls0,stopping=From}=St) ->
+ Gls = gb_sets:delete_any(Pid, Gls0),
+ case gb_sets:is_empty(Gls) andalso stopping =/= undefined of
+ true ->
+ %% No more group leaders left.
+ gen_server:reply(From, ok),
+ {stop,normal,St#st{gls=Gls,stopping=undefined}};
+ false ->
+ %% Wait for more group leaders to finish.
+ {noreply,St#st{gls=Gls}}
+ end;
+handle_info({'EXIT',_Pid,Reason}, _St) ->
+ exit(Reason);
+handle_info(stop_group_leaders, #st{gls=Gls}=St) ->
+ %% Stop the remaining group leaders.
+ [test_server_gl:stop(GL) || GL <- gb_sets:to_list(Gls)],
+ erlang:send_after(2000, self(), kill_group_leaders),
+ {noreply,St};
+handle_info(kill_group_leaders, #st{gls=Gls,stopping=From}=St) ->
+ [exit(GL, kill) || GL <- gb_sets:to_list(Gls)],
+ gen_server:reply(From, ok),
+ {stop,normal,St};
+handle_info(Other, St) ->
+ io:format("Ignoring: ~p\n", [Other]),
+ {noreply,St}.
+
+terminate(_, _) ->
+ ok.
+
+output(From, Tag, Str, #st{io_buffering=Buffered,buffered=Buf0}=St) ->
+ case gb_sets:is_member(From, Buffered) of
+ false ->
+ do_output(Tag, Str, St),
+ St;
+ true ->
+ Q0 = gb_trees:get(From, Buf0),
+ Q = queue:in({Tag,Str}, Q0),
+ Buf = gb_trees:update(From, Q, Buf0),
+ St#st{buffered=Buf}
+ end.
+
+do_output(stdout, Str, #st{job_name=undefined}) ->
+ io:put_chars(Str);
+do_output(stdout, Str0, #st{job_name=Name}) ->
+ Str = io_lib:format("Testing ~s: ~s\n", [Name,Str0]),
+ io:put_chars(Str);
+do_output(Tag, Str, #st{fds=Fds}=St) ->
+ case gb_trees:lookup(Tag, Fds) of
+ none ->
+ S = io_lib:format("\n*** ERROR: ~p, line ~p: No known '~p' log file\n",
+ [?MODULE,?LINE,Tag]),
+ do_output(stdout, [S,Str], St);
+ {value,Fd} ->
+ try
+ io:put_chars(Fd, Str),
+ case Tag of
+ html -> finalise_table(Fd, St);
+ _ -> ok
+ end
+ catch _:Error ->
+ S = io_lib:format("\n*** ERROR: ~p, line ~p: Error writing to "
+ "log file '~p': ~p\n",
+ [?MODULE,?LINE,Tag,Error]),
+ do_output(stdout, [S,Str], St)
+ end
+ end.
+
+finalise_table(Fd, #st{html_footer=Footer}) ->
+ case file:position(Fd, {cur,0}) of
+ {ok,Pos} ->
+ %% We are writing to a seekable file. Finalise so
+ %% we get complete valid (and viewable) HTML code.
+ %% Then rewind to overwrite the finalising code.
+ io:put_chars(Fd, ["\n</table>\n",Footer]),
+ file:position(Fd, Pos);
+ {error,epipe} ->
+ %% The file is not seekable. We cannot erase what
+ %% we've already written --- so the reader will
+ %% have to wait until we're done.
+ ok
+ end.
+
+do_print_buffered(Q0, St) ->
+ Item = queue:get(Q0),
+ Q = queue:drop(Q0),
+ case Item of
+ eot ->
+ Q;
+ {Tag,Str} ->
+ do_output(Tag, Str, St),
+ do_print_buffered(Q, St)
+ end.
+
+gc(#st{gls=Gls0}) ->
+ InUse0 = [begin
+ case process_info(P, group_leader) of
+ {group_leader,GL} -> GL;
+ undefined -> undefined
+ end
+ end || P <- processes()],
+ InUse = ordsets:from_list(InUse0),
+ Gls = gb_sets:to_list(Gls0),
+ NotUsed = ordsets:subtract(Gls, InUse),
+ [test_server_gl:stop(Pid) || Pid <- NotUsed],
+ ok.
diff --git a/lib/test_server/src/test_server_node.erl b/lib/test_server/src/test_server_node.erl
index 6358efa764..b307d93c7d 100644
--- a/lib/test_server/src/test_server_node.erl
+++ b/lib/test_server/src/test_server_node.erl
@@ -26,7 +26,7 @@
%% Test Controller interface
-export([is_release_available/1]).
--export([start_remote_main_target/1,stop/1]).
+-export([stop/1]).
-export([start_tracer_node/2,trace_nodes/2,stop_tracer_node/1]).
-export([start_node/5, stop_node/2]).
-export([kill_nodes/1, nodedown/2]).
@@ -35,7 +35,6 @@
-include("test_server_internal.hrl").
-record(slave_info, {name,socket,client}).
--define(VXWORKS_ACCEPT_TIMEOUT,?ACCEPT_TIMEOUT).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% %%%
@@ -58,87 +57,8 @@ is_release_available(Rel) ->
false
end.
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%%% Start main target node on remote host
-%%% The target node must not know the controller node via erlang distribution.
-start_remote_main_target(Parameters) ->
- #par{type=TargetType,
- target=TargetHost,
- naming=Naming,
- master=MasterNode,
- cookie=MasterCookie,
- slave_targets=SlaveTargets} = Parameters,
-
- lists:foreach(fun(T) -> maybe_reboot_target({TargetType,T}) end,
- [list_to_atom(TargetHost)|SlaveTargets]),
-
- % Must give the targets a chance to reboot...
- case TargetType of
- vxworks ->
- receive after 15000 -> ok end;
- _ ->
- ok
- end,
-
- Cmd0 = get_main_target_start_command(TargetType,TargetHost,Naming,
- MasterNode,MasterCookie),
- Cmd =
- case os:getenv("TEST_SERVER_FRAMEWORK") of
- FW when FW =:= false; FW =:= "undefined" -> Cmd0;
- FW -> Cmd0 ++ " -env TEST_SERVER_FRAMEWORK " ++ FW
- end,
-
- {ok,LSock} = gen_tcp:listen(?MAIN_PORT,[binary,{reuseaddr,true},{packet,2}]),
- case start_target(TargetType,TargetHost,Cmd) of
- {ok,TargetClient,AcceptTimeout} ->
- case gen_tcp:accept(LSock,AcceptTimeout) of
- {ok,Sock} ->
- gen_tcp:close(LSock),
- receive
- {tcp,Sock,Bin} when is_binary(Bin) ->
- case unpack(Bin) of
- error ->
- gen_tcp:close(Sock),
- close_target_client(TargetClient),
- {error,bad_message};
- {ok,{target_info,TI}} ->
- put(test_server_free_targets,SlaveTargets),
- {ok, TI#target_info{where=Sock,
- host=TargetHost,
- naming=Naming,
- master=MasterNode,
- target_client=TargetClient,
- slave_targets=SlaveTargets}}
- end;
- {tcp_closed,Sock} ->
- gen_tcp:close(Sock),
- close_target_client(TargetClient),
- {error,could_not_contact_target}
- after AcceptTimeout ->
- gen_tcp:close(Sock),
- close_target_client(TargetClient),
- {error,timeout}
- end;
- Error ->
- %%! maybe something like kill_target(...)???
- gen_tcp:close(LSock),
- close_target_client(TargetClient),
- {error,{could_not_contact_target,Error}}
- end;
- Error ->
- gen_tcp:close(LSock),
- {error,{could_not_start_target,Error}}
- end.
-
stop(TI) ->
- kill_nodes(TI),
- case TI#target_info.where of
- local -> % there is no remote target to stop
- ok;
- Sock -> % stop remote target
- gen_tcp:close(Sock),
- close_target_client(TI#target_info.target_client)
- end.
+ kill_nodes(TI).
nodedown(Sock, TI) ->
Match = #slave_info{name='$1',socket=Sock,client='$2',_='_'},
@@ -155,14 +75,8 @@ nodedown(Sock, TI) ->
false -> ok
end,
slave_died;
- [] ->
- case TI#target_info.where of
- Sock ->
- %% test_server_ctrl will do the cleanup
- target_died;
- _ ->
- ignore
- end
+ [] ->
+ ok
end.
@@ -176,10 +90,7 @@ start_tracer_node(TraceFile,TI) ->
Match = #slave_info{name='$1',_='_'},
SlaveNodes = lists:map(fun([N]) -> [" ",N] end,
ets:match(slave_tab,Match)),
- TargetNode = case TI#target_info.where of
- local -> node();
- _ -> "test_server@" ++ TI#target_info.host
- end,
+ TargetNode = node(),
Cookie = TI#target_info.cookie,
{ok,LSock} = gen_tcp:listen(0,[binary,{reuseaddr,true},{packet,2}]),
{ok,TracePort} = inet:port(LSock),
@@ -433,10 +344,12 @@ start_node_peer(SlaveName, OptList, From, TI) ->
%% Bad environment can cause open port to fail. If this happens,
%% we ignore it and let the testcase handle the situation...
catch open_port({spawn, Cmd}, [stream|Opts]),
+
+ Tmo = 60000 * test_server:timetrap_scale_factor(),
case start_node_get_option_value(wait, OptList, true) of
true ->
- Ret = wait_for_node_started(LSock,60000,undefined,Cleanup,TI,self()),
+ Ret = wait_for_node_started(LSock,Tmo,undefined,Cleanup,TI,self()),
case {Ret,FailOnError} of
{{{ok, Node}, Warning},_} ->
gen_server:reply(From,{{ok,Node},HostStr,Cmd,[],Warning});
@@ -452,7 +365,7 @@ start_node_peer(SlaveName, OptList, From, TI) ->
Self = self(),
spawn_link(
fun() ->
- wait_for_node_started(LSock,60000,undefined,
+ wait_for_node_started(LSock,Tmo,undefined,
Cleanup,TI,Self),
receive after infinity -> ok end
end),
@@ -462,9 +375,6 @@ start_node_peer(SlaveName, OptList, From, TI) ->
%%
%% Slave nodes are started on a remote host if
%% - the option remote is given when calling test_server:start_node/3
-%% or
-%% - the target type is vxworks, since only one erlang node
-%% can be started on each vxworks host.
%%
start_node_slave(SlaveName, OptList, From, TI) ->
SuppliedArgs = start_node_get_option_value(args, OptList, []),
@@ -481,129 +391,29 @@ start_node_slave(SlaveName, OptList, From, TI) ->
Ret =
case start_which_node(OptList) of
{error,Reason} -> {{error,Reason},undefined,undefined};
- Host0 -> do_start_node_slave(Host0,SlaveName,Args,Prog,Cleanup,TI)
+ Host0 -> do_start_node_slave(Host0,SlaveName,Args,Prog,Cleanup)
end,
gen_server:reply(From,Ret).
-do_start_node_slave(Host0, SlaveName, Args, Prog, Cleanup, TI) ->
- case TI#target_info.where of
- local ->
- Host =
- case Host0 of
- local -> test_server_sup:hoststr();
- _ -> cast_to_list(Host0)
- end,
- Cmd = Prog ++ " " ++ Args,
- %% Can use slave.erl here because I'm both controller and target
- %% so I will ping the new node anyway
- case slave:start(Host, SlaveName, Args, no_link, Prog) of
- {ok,Nodename} ->
- case Cleanup of
- true -> ets:insert(slave_tab,#slave_info{name=Nodename});
- false -> ok
- end,
- {{ok,Nodename}, Host, Cmd, [], []};
- Ret ->
- {Ret, Host, Cmd}
- end;
-
- _Sock ->
- %% Cannot use slave.erl here because I'm only controller, and will
- %% not ping the new node. Only target shall contact the new node!!
- no_contact_start_slave(Host0,SlaveName,Args,Prog,Cleanup,TI)
- end.
-
-
-
-no_contact_start_slave(Host, Name, Args0, Prog, Cleanup,TI) ->
- Args1 = case string:str(Args0,"-setcookie") of
- 0 -> "-setcookie " ++ TI#target_info.cookie ++ " " ++ Args0;
- _ -> Args0
+do_start_node_slave(Host0, SlaveName, Args, Prog, Cleanup) ->
+ Host =
+ case Host0 of
+ local -> test_server_sup:hoststr();
+ _ -> cast_to_list(Host0)
+ end,
+ Cmd = Prog ++ " " ++ Args,
+ %% Can use slave.erl here because I'm both controller and target
+ %% so I will ping the new node anyway
+ case slave:start(Host, SlaveName, Args, no_link, Prog) of
+ {ok,Nodename} ->
+ case Cleanup of
+ true -> ets:insert(slave_tab,#slave_info{name=Nodename});
+ false -> ok
end,
- Args = TI#target_info.naming ++ " " ++ cast_to_list(Name) ++ " " ++ Args1,
- case Host of
- local ->
- case get(test_server_free_targets) of
- [] ->
- io:format("Starting slave ~p on HOST~n", [Name]),
- TargetType = test_server_sup:get_os_family(),
- Cmd0 = get_slave_node_start_command(TargetType,
- Prog,
- TI#target_info.master),
- Cmd = Cmd0 ++ " " ++ Args,
- do_no_contact_start_slave(TargetType,
- test_server_sup:hoststr(),
- Cmd, Cleanup,TI, false);
- [H|T] ->
- TargetType = TI#target_info.os_family,
- Cmd0 = get_slave_node_start_command(TargetType,
- Prog,
- TI#target_info.master),
- Cmd = Cmd0 ++ " " ++ Args,
- case do_no_contact_start_slave(TargetType,H,Cmd,Cleanup,
- TI,true) of
- {error,remove} ->
- io:format("Cannot start node on ~p, "
- "removing from slave "
- "target list.", [H]),
- put(test_server_free_targets,T),
- no_contact_start_slave(Host,Name,Args,Prog,
- Cleanup,TI);
- {error,keep} ->
- %% H is added to the END OF THE LIST
- %% in order to avoid the same target to
- %% be selected each time
- put(test_server_free_targets,T++[H]),
- no_contact_start_slave(Host,Name,Args,Prog,
- Cleanup,TI);
- R ->
- put(test_server_free_targets,T),
- R
- end
- end;
- _ ->
- TargetType = TI#target_info.os_family,
- Cmd0 = get_slave_node_start_command(TargetType,
- Prog,
- TI#target_info.master),
- Cmd = Cmd0 ++ " " ++ Args,
- do_no_contact_start_slave(TargetType, Host, Cmd, Cleanup, TI, false)
- end.
-
-do_no_contact_start_slave(TargetType,Host0,Cmd0,Cleanup,TI,Retry) ->
- %% Must use TargetType instead of TI#target_info.os_familiy here
- %% because if there were no free_targets we will be starting the
- %% slave node on host which might have a different os_familiy
- Host = cast_to_list(Host0),
- {ok,LSock} = gen_tcp:listen(0,[binary,
- {reuseaddr,true},
- {packet,2}]),
- {ok,WaitPort} = inet:port(LSock),
- Cmd = lists:concat([Cmd0, " -s ", ?MODULE, " node_started ",
- test_server_sup:hoststr(), " ", WaitPort]),
-
- case start_target(TargetType,Host,Cmd) of
- {ok,Client,AcceptTimeout} ->
- case wait_for_node_started(LSock,AcceptTimeout,
- Client,Cleanup,TI,self()) of
- {error,_}=WaitError ->
- if Retry ->
- case maybe_reboot_target(Client) of
- {error,_} -> {error,remove};
- ok -> {error,keep}
- end;
- true ->
- {WaitError,Host,Cmd}
- end;
- {Ok,Warning} ->
- {Ok,Host,Cmd,[],Warning}
- end;
- StartError ->
- gen_tcp:close(LSock),
- if Retry -> {error,remove};
- true -> {{error,{could_not_start_target,StartError}},Host,Cmd}
- end
+ {{ok,Nodename}, Host, Cmd, [], []};
+ Ret ->
+ {Ret, Host, Cmd}
end.
@@ -787,71 +597,10 @@ kill_node(SI,TI) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Platform specific code
-start_target(vxworks,TargetHost,Cmd) ->
- case vxworks_client:open(TargetHost) of
- {ok,P} ->
- case vxworks_client:send_data(P,Cmd,"start_erl called") of
- {ok,_} ->
- {ok,{vxworks,P},?VXWORKS_ACCEPT_TIMEOUT};
- Error ->
- Error
- end;
- Error ->
- Error
- end;
-
-start_target(unix,TargetHost,Cmd0) ->
- Cmd =
- case test_server_sup:hoststr() of
- TargetHost -> Cmd0;
- _ -> lists:concat(["rsh ",TargetHost, " ", Cmd0])
- end,
- open_port({spawn, Cmd}, [stream]),
- {ok,undefined,?ACCEPT_TIMEOUT}.
-
-maybe_reboot_target({vxworks,P}) when is_pid(P) ->
- %% Reboot the vxworks card.
- %% Client is also closed after this, even if reboot fails
- vxworks_client:send_data_wait_for_close(P,"q");
-maybe_reboot_target({vxworks,T}) when is_atom(T) ->
- %% Reboot the vxworks card.
- %% Client is also closed after this, even if reboot fails
- vxworks_client:reboot(T);
-maybe_reboot_target(_) ->
- {error, cannot_reboot_target}.
-
-close_target_client({vxworks,P}) ->
- vxworks_client:close(P);
close_target_client(undefined) ->
ok.
-
-%%
-%% Command for starting main target
-%%
-get_main_target_start_command(vxworks,_TargetHost,Naming,
- _MasterNode,_MasterCookie) ->
- "e" ++ Naming ++ " test_server -boot start_sasl"
- " -sasl errlog_type error"
- " -s test_server start " ++ test_server_sup:hoststr();
-get_main_target_start_command(unix,_TargetHost,Naming,
- _MasterNode,_MasterCookie) ->
- Prog = pick_erl_program(default),
- Prog ++ " " ++ Naming ++ " test_server" ++
- " -boot start_sasl -sasl errlog_type error"
- " -s test_server start " ++ test_server_sup:hoststr().
-
-%%
-%% Command for starting slave nodes
-%%
-get_slave_node_start_command(vxworks, _Prog, _MasterNode) ->
- "e";
- %"e-noinput -master " ++ MasterNode;
-get_slave_node_start_command(unix, Prog, MasterNode) ->
- cast_to_list(Prog) ++ " -detached -master " ++ MasterNode.
-
-
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% cast_to_list(X) -> string()
%%% X = list() | atom() | void()
diff --git a/lib/test_server/src/test_server_sup.erl b/lib/test_server/src/test_server_sup.erl
index 9d111ff769..a6d426887e 100644
--- a/lib/test_server/src/test_server_sup.erl
+++ b/lib/test_server/src/test_server_sup.erl
@@ -28,7 +28,7 @@
get_username/0, get_os_family/0,
hostatom/0, hostatom/1, hoststr/0, hoststr/1,
framework_call/2,framework_call/3,framework_call/4,
- format_loc/1, package_str/1, package_atom/1,
+ format_loc/1,
call_trace/1]).
-include("test_server_internal.hrl").
-define(crash_dump_tar,"crash_dumps.tar.gz").
@@ -64,13 +64,7 @@ timetrap(Timeout0, ReportTVal, Scale, Pid) ->
true -> ReportTVal end,
MFLs = test_server:get_loc(Pid),
Mon = erlang:monitor(process, Pid),
- Trap =
- case get(test_server_init_or_end_conf) of
- undefined ->
- {timetrap_timeout,TimeToReport,MFLs};
- InitOrEnd ->
- {timetrap_timeout,TimeToReport,MFLs,InitOrEnd}
- end,
+ Trap = {timetrap_timeout,TimeToReport,MFLs},
exit(Pid, Trap),
receive
{'DOWN', Mon, process, Pid, _} ->
@@ -473,10 +467,8 @@ getenv_any([]) -> "".
%%
%% Returns the OS family
get_os_family() ->
- case os:type() of
- {OsFamily,_OsName} -> OsFamily;
- OsFamily -> OsFamily
- end.
+ {OsFamily,_OsName} = os:type(),
+ OsFamily.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -520,8 +512,18 @@ 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,
+ SetTcState = case Func of
+ end_tc -> true;
+ init_tc -> true;
+ _ -> false
+ end,
+ case SetTcState of
+ true ->
+ test_server:set_tc_state({framework,Mod,Func});
+ false ->
+ ok
+ end,
try apply(Mod,Func,Args) of
Result ->
Result
@@ -551,19 +553,7 @@ format_loc([{Mod,Func,Line}|Rest]) ->
format_loc([{Mod,LineOrFunc}]) ->
format_loc({Mod,LineOrFunc});
format_loc({Mod,Func}) when is_atom(Func) ->
- io_lib:format("{~s,~w}",[package_str(Mod),Func]);
-format_loc({Mod,Line}) when is_integer(Line) ->
- %% ?line macro is used
- ModStr = package_str(Mod),
- case {lists:member(no_src, get(test_server_logopts)),
- lists:reverse(ModStr)} of
- {false,[$E,$T,$I,$U,$S,$_|_]} ->
- io_lib:format("{~s,<a href=\"~s~s#~w\">~w</a>}",
- [ModStr,downcase(ModStr),?src_listing_ext,
- round_to_10(Line),Line]);
- _ ->
- io_lib:format("{~s,~w}",[ModStr,Line])
- end;
+ io_lib:format("{~w,~w}",[Mod,Func]);
format_loc(Loc) ->
io_lib:format("~p",[Loc]).
@@ -572,22 +562,17 @@ format_loc1([{Mod,Func,Line}]) ->
format_loc1([{Mod,Func,Line}|Rest]) ->
[" ",format_loc1({Mod,Func,Line}),",\n"|format_loc1(Rest)];
format_loc1({Mod,Func,Line}) ->
- ModStr = package_str(Mod),
+ ModStr = atom_to_list(Mod),
case {lists:member(no_src, get(test_server_logopts)),
lists:reverse(ModStr)} of
{false,[$E,$T,$I,$U,$S,$_|_]} ->
- io_lib:format("{~s,~w,<a href=\"~s~s#~w\">~w</a>}",
- [ModStr,Func,downcase(ModStr),?src_listing_ext,
- round_to_10(Line),Line]);
+ io_lib:format("{~w,~w,<a href=\"~s~s#~w\">~w</a>}",
+ [Mod,Func,downcase(ModStr),?src_listing_ext,
+ Line,Line]);
_ ->
- io_lib:format("{~s,~w,~w}",[ModStr,Func,Line])
+ io_lib:format("{~w,~w,~w}",[Mod,Func,Line])
end.
-round_to_10(N) when (N rem 10) == 0 ->
- N;
-round_to_10(N) ->
- trunc(N/10)*10.
-
downcase(S) -> downcase(S, []).
downcase([Uc|Rest], Result) when $A =< Uc, Uc =< $Z ->
downcase(Rest, [Uc-$A+$a|Result]);
@@ -596,22 +581,6 @@ downcase([C|Rest], Result) ->
downcase([], Result) ->
lists:reverse(Result).
-package_str(Mod) when is_atom(Mod) ->
- atom_to_list(Mod);
-package_str(Mod) when is_list(Mod), is_atom(hd(Mod)) ->
- %% convert [s1,s2] -> "s1.s2"
- [_|M] = lists:flatten(["."++atom_to_list(S) || S <- Mod]),
- M;
-package_str(Mod) when is_list(Mod) ->
- Mod.
-
-package_atom(Mod) when is_atom(Mod) ->
- Mod;
-package_atom(Mod) when is_list(Mod), is_atom(hd(Mod)) ->
- list_to_atom(package_str(Mod));
-package_atom(Mod) when is_list(Mod) ->
- list_to_atom(Mod).
-
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% call_trace(TraceSpecFile) -> ok
%%
diff --git a/lib/test_server/src/ts.erl b/lib/test_server/src/ts.erl
index 4899f38d2b..cfd7161dbd 100644
--- a/lib/test_server/src/ts.erl
+++ b/lib/test_server/src/ts.erl
@@ -25,9 +25,9 @@
-module(ts).
-export([run/0, run/1, run/2, run/3, run/4,
- clean/0, clean/1,
tests/0, tests/1,
- install/0, install/1, index/0,
+ install/0, install/1,
+ bench/0, bench/1, bench/2, benchmarks/0,
estone/0, estone/1,
cross_cover_analyse/1,
compile_testcases/0, compile_testcases/1,
@@ -40,20 +40,14 @@
%%% the modules:
%%%
%%% +-- ts_install --+------ ts_autoconf_win32
-%%% | |
-%%% | +------ ts_autoconf_vxworks
%%% |
%%% ts ---+ +------ ts_erl_config
%%% | | ts_lib
-%%% | +------ ts_make
-%%% | |
-%%% +-- ts_run -----+
-%%% | ts_filelib
-%%% +------ ts_make_erl
-%%% |
-%%% +------ ts_reports (indirectly)
-%%%
-%%%
+%%% +-- ts_run -----+------ ts_make
+%%% | | ts_filelib
+%%% | +------ ts_make_erl
+%%% |
+%%% +-- ts_benchmark
%%%
%%% The modules ts_lib and ts_filelib contains utilities used by
%%% the other modules.
@@ -63,8 +57,7 @@
%%% ts Frontend to the test server framework. Contains all
%%% interface functions.
%%% ts_install Installs the test suite. On Unix, `autoconf' is
-%%% is used; on Windows, ts_autoconf_win32 is used,
-%%% on VxWorks, ts_autoconf_vxworks is used.
+%%% is used; on Windows, ts_autoconf_win32 is used.
%%% The result is written to the file `variables'.
%%% ts_run Supervises running of the tests.
%%% ts_autconf_win32 An `autoconf' for Windows.
@@ -77,10 +70,9 @@
%%% and other platforms.
%%% ts_make_erl A corrected version of the standar Erlang module
%%% make (used for rebuilding test suites).
-%%% ts_reports Generates index pages in HTML, providing a summary
-%%% of the tests run.
%%% ts_lib Miscellanous utility functions, each used by several
%%% other modules.
+%%% ts_benchmark Supervises otp benchmarks and collects results.
%%%----------------------------------------------------------------------
-include_lib("kernel/include/file.hrl").
@@ -128,7 +120,7 @@ help(installed) ->
" ts:run(Spec, Mod) - Run a single test suite.\n",
" ts:run(Spec, Mod, Case)\n",
" - Run a single test case.\n",
- " All above run functions can have the additional Options argument\n",
+ " All above run functions can have an additional Options argument\n",
" which is a list of options.\n",
"\n",
"Run options supported:\n",
@@ -158,27 +150,31 @@ help(installed) ->
" {ctp | ctpl, Mod, Func}\n",
" {ctp | ctpl, Mod, Func, Arity}\n",
"\n",
- "Support functions\n",
+ "Support functions:\n",
" ts:tests() - Shows all available families of tests.\n",
" ts:tests(Spec) - Shows all available test modules in Spec,\n",
" i.e. ../Spec_test/*_SUITE.erl\n",
- " ts:index() - Updates local index page.\n",
- " ts:clean() - Cleans up all but the last tests run.\n",
- " ts:clean(all) - Cleans up all test runs found.\n",
" ts:estone() - Run estone_SUITE in kernel application with\n"
" no run options\n",
" ts:estone(Opts) - Run estone_SUITE in kernel application with\n"
" the given run options\n",
" ts:cross_cover_analyse(Level)\n"
" - Used after ts:run with option cover or \n"
- " cover_details. Analyses modules specified in\n"
- " cross.cover.\n"
+ " cover_details. Analyses modules specified with\n"
+ " a 'cross' statement in the cover spec file.\n"
" Level can be 'overview' or 'details'.\n",
" ts:compile_testcases()~n"
" ts:compile_testcases(Apps)~n"
" - Compile all testcases for usage in a cross ~n"
" compile environment."
" \n"
+ "Benchmark functions:\n"
+ " ts:benchmarks() - Get all available families of benchmarks\n"
+ " ts:bench() - Runs all benchmarks\n"
+ " ts:bench(Spec) - Runs all benchmarks in the given spec file.\n"
+ " The spec file is actually ../*_test/Spec_bench.spec\n\n"
+ " ts:bench can take the same Options argument as ts:run.\n"
+ "\n"
"Installation (already done):\n"
],
show_help([H,?install_help]).
@@ -193,33 +189,6 @@ install() ->
install(Options) when is_list(Options) ->
ts_install:install(install_local,Options).
-%% Updates the local index page.
-
-index() ->
- check_and_run(fun(_Vars) -> ts_reports:make_index(), ok end).
-
-%%
-%% clean(all)
-%% Deletes all logfiles.
-%%
-clean(all) ->
- delete_files(filelib:wildcard("*" ++ ?logdir_ext)).
-
-%% clean/0
-%%
-%% Cleans up run logfiles, all but the last run.
-clean() ->
- clean1(filelib:wildcard("*" ++ ?logdir_ext)).
-
-clean1([Dir|Dirs]) ->
- List0 = filelib:wildcard(filename:join(Dir, "run.*")),
- case lists:reverse(lists:sort(List0)) of
- [] -> ok;
- [_Last|Rest] -> delete_files(Rest)
- end,
- clean1(Dirs);
-clean1([]) -> ok.
-
%% run/0
%% Runs all specs found by ts:tests(), if any, or returns
%% {error, no_tests_available}. (batch)
@@ -290,14 +259,43 @@ 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),
- Spec = case code:lib_dir(Testspec) of
- {error, bad_name} when Testspec /= emulator,
- Testspec /= system,
- Testspec /= epmd ->
- create_skip_spec(Testspec, tests(Testspec));
- _ ->
- File++".spec"
- end,
+ WhatToDo =
+ case Testspec of
+ %% Known to exist but fails generic tests below
+ emulator -> test;
+ system -> test;
+ erl_interface -> test;
+ epmd -> test;
+ _ ->
+ case code:lib_dir(Testspec) of
+ {error,bad_name} ->
+ %% Application does not exist
+ skip;
+ Path ->
+ case file:read_file_info(filename:join(Path,"ebin")) of
+ {ok,#file_info{type=directory}} ->
+ %% Erlang application is built
+ test;
+ _ ->
+ case filelib:wildcard(
+ filename:join([Path,"priv","*.jar"])) of
+ [] ->
+ %% The application is not built
+ skip;
+ [_|_] ->
+ %% Java application is built
+ test
+ end
+ end
+ end
+ end,
+ Spec =
+ case WhatToDo of
+ skip ->
+ create_skip_spec(Testspec, tests(Testspec));
+ test ->
+ File++".spec"
+ end,
run_test(File, [{spec,[Spec]}], Options);
%% Runs one module in a spec (interactive)
run(Testspec, Mod) when is_atom(Testspec), is_atom(Mod) ->
@@ -491,6 +489,25 @@ tests(Spec) ->
{ok, Cwd} = file:get_cwd(),
ts_lib:suites(Cwd, atom_to_list(Spec)).
+%% Benchmark related functions
+
+bench() ->
+ bench([]).
+
+bench(Opts) when is_list(Opts) ->
+ bench(benchmarks(),Opts);
+bench(Spec) ->
+ bench([Spec],[]).
+
+bench(Spec, Opts) when is_atom(Spec) ->
+ bench([Spec],Opts);
+bench(Specs, Opts) ->
+ check_and_run(fun(Vars) -> ts_benchmark:run(Specs, Opts, Vars) end).
+
+benchmarks() ->
+ ts_benchmark:benchmarks().
+
+
%%
%% estone/0, estone/1
@@ -510,8 +527,35 @@ estone(Opts) when is_list(Opts) -> run(emulator,estone_SUITE,Opts).
cross_cover_analyse([Level]) ->
cross_cover_analyse(Level);
cross_cover_analyse(Level) ->
- test_server_ctrl:cross_cover_analyse(Level).
-
+ Apps = get_last_app_tests(),
+ test_server_ctrl:cross_cover_analyse(Level,Apps).
+
+get_last_app_tests() ->
+ AllTests = filelib:wildcard(filename:join(["*","*_test.logs"])),
+ {ok,RE} = re:compile("^[^/]*/[^\.]*\.(.*)_test\.logs$"),
+ get_last_app_tests(AllTests,RE,[]).
+
+get_last_app_tests([Dir|Dirs],RE,Acc) ->
+ NewAcc =
+ case re:run(Dir,RE,[{capture,all,list}]) of
+ {match,[Dir,AppStr]} ->
+ App = list_to_atom(AppStr),
+ case lists:keytake(App,1,Acc) of
+ {value,{App,LastDir},Rest} ->
+ if Dir > LastDir ->
+ [{App,Dir}|Rest];
+ true ->
+ Acc
+ end;
+ false ->
+ [{App,Dir} | Acc]
+ end;
+ _ ->
+ Acc
+ end,
+ get_last_app_tests(Dirs,RE,NewAcc);
+get_last_app_tests([],_,Acc) ->
+ Acc.
%%% Implementation.
@@ -552,32 +596,6 @@ run_test(File, Args, Options) ->
run_test(File, Args, Options, Vars) ->
ts_run:run(File, Args, Options, Vars).
-
-delete_files([]) -> ok;
-delete_files([Item|Rest]) ->
- case file:delete(Item) of
- ok ->
- delete_files(Rest);
- {error,eperm} ->
- file:change_mode(Item, 8#777),
- delete_files(filelib:wildcard(filename:join(Item, "*"))),
- file:del_dir(Item),
- ok;
- {error,eacces} ->
- %% We'll see about that!
- file:change_mode(Item, 8#777),
- case file:delete(Item) of
- ok -> ok;
- {error,_} ->
- erlang:yield(),
- file:change_mode(Item, 8#777),
- file:delete(Item),
- ok
- end;
- {error,_} -> ok
- end,
- delete_files(Rest).
-
%% This module provides some convenient shortcuts to running
%% the test server from within a started Erlang shell.
diff --git a/lib/test_server/src/ts.hrl b/lib/test_server/src/ts.hrl
index db804d23a1..5e829f3575 100644
--- a/lib/test_server/src/ts.hrl
+++ b/lib/test_server/src/ts.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/test_server/src/ts_autoconf_vxworks.erl b/lib/test_server/src/ts_autoconf_vxworks.erl
deleted file mode 100644
index f4535cd89a..0000000000
--- a/lib/test_server/src/ts_autoconf_vxworks.erl
+++ /dev/null
@@ -1,191 +0,0 @@
-%%
-%% %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%
-%%
-
-%%% Purpose : Autoconf for cross environments.
-
--module(ts_autoconf_vxworks).
--export([configure/1]).
-%%% Supported cross platforms:
--define(PLATFORMS, ["vxworks_cpu32", "vxworks_ppc860", "vxworks_ppc603",
- "vxworks_sparc", "vxworks_ppc750", "vxworks_simso"]).
--include("ts.hrl").
-
-%% takes an argument {Target_arch, Target_host} (e.g. {vxworks_ppc860, thorin}).
-configure({Target_arch, Target_host}) ->
- case variables({Target_arch, Target_host}) of
- {ok, Vars} ->
- ts_lib:subst_file("conf_vars.in", "conf_vars", Vars);
- Error ->
- Error
- end.
-
-variables(Cross_spec) ->
- run_tests(Cross_spec, tests(), []).
-
-run_tests(Cross_spec, [{Prompt, Tester}|Rest], Vars) ->
- io:format("checking ~s... ", [Prompt]),
- case catch Tester(Cross_spec, Vars) of
- {'EXIT', Reason} ->
- io:format("FAILED~nExit status: ~p~n", [Reason]),
- {error, auto_conf_failed};
- {Result, NewVars} ->
- io:format("~s~n", [lists:concat([Result])]),
- run_tests(Cross_spec, Rest, NewVars)
- end;
-run_tests(_Cross_spec, [], Vars) ->
- {ok, Vars}.
-
-
-%%% The tests.
-
-tests() ->
- [{"supported target architecture", fun target_architecture/2},
- {"cross target host to run tests on", fun target_host/2},
- {"CPU type", fun cpu/2},
- {"for cross-compiling gcc", fun find_gcc/2},
- {"for cross-linker", fun find_ld/2},
- {"for object extension", fun find_obj/2},
- {"for shared libraries extension", fun find_dll/2},
- {"for executables extension", fun find_exe/2},
- {"for make", fun find_make/2}].
-
-target_architecture({Architecture, _Target_host}, Vars) ->
- case lists:member(Architecture, ?PLATFORMS) of
- true ->
- {Architecture, [{host_os, os_type(Architecture)}, {host, Architecture}|Vars]};
- false ->
- {"unsupported_platform", Vars}
- end.
-
-target_host({_Architecture, Target_host}, Vars) ->
- {Target_host, [{target_host, Target_host} | Vars]}.
-
-cpu({Arch, _Target_host}, Vars) ->
- Cpu = processor(Arch),
- {Cpu, [{host_cpu, Cpu}|Vars]}.
-
-find_gcc({Arch, _Target_host}, Vars) ->
- Gcc = "cc" ++ gnu_suffix(Arch),
- case os:find_executable(Gcc) of
- false ->
- {no, Vars};
- Path when is_list(Path) ->
- Cflags = cflags(Arch),
- {Path, [{'CC', Gcc},
- {'CFLAGS', Cflags},
- {'EI_CFLAGS', Cflags},
- {'ERTS_CFLAGS', Cflags},
- {'DEFS', ""},
- {'ERTS_LIBS', ""},
- {'LIBS', ""},
- {'SHLIB_CFLAGS', Cflags},
- {test_c_compiler, "{gnuc, undefined}"} | Vars]}
- end.
-
-find_ld({Arch, _Target_host}, Vars) ->
- Linker = "ld" ++ gnu_suffix(Arch),
- case os:find_executable(Linker) of
- false ->
- {no, Vars};
- Path when is_list(Path) ->
- {Path, [{'LD', Linker},
- {'CROSSLDFLAGS', ldflags(Arch)},
- {'SHLIB_EXTRACT_ALL', ""},
- {'SHLIB_LD', Linker},
- {'SHLIB_LDFLAGS', ""},
- {'SHLIB_LDLIBS', ""} | Vars]}
- end.
-
-find_obj({Arch, _Target_host}, Vars) ->
- Obj = obj_ext(Arch),
- {Obj, [{obj, Obj}|Vars]}.
-
-find_dll({Arch, _Target_host}, Vars) ->
- Dll = dll_ext(Arch),
- {Dll, [{'SHLIB_SUFFIX', Dll}|Vars]}.
-
-find_exe({Arch, _Target_host}, Vars) ->
- Exe = exe_ext(Arch),
- {Exe, [{exe, Exe}|Vars]}.
-
-find_make(_, Vars) ->
- {"make", [{make_command, "make"} | Vars]}.
-
-%%% some utility functions
-gnu_suffix(Arch) ->
- {_, _, _, _, Suffix, _Cpu, _Cflags, _} = cross_data(Arch),
- Suffix.
-
-processor(Arch) ->
- {_, _, _, _, _Suffix, Cpu, _Cflags, _} = cross_data(Arch),
- Cpu.
-
-cflags(Arch) ->
- {_, _, _, _, _Suffix, _Cpu, Cflags, _} = cross_data(Arch),
- Cflags.
-
-ldflags(Arch) ->
- {_, _, _, _, _Suffix, _Cpu, _Cflags, Ldflags} = cross_data(Arch),
- Ldflags.
-
-os_type(Arch) ->
- {Os_type, _, _, _, _, _, _, _} = cross_data(Arch),
- Os_type.
-
-obj_ext(Arch) ->
- {_, _, Obj, _, _, _, _, _} = cross_data(Arch),
- Obj.
-
-dll_ext(Arch) ->
- {_, _, _, Dll, _, _, _, _} = cross_data(Arch),
- Dll.
-
-exe_ext(Arch) ->
- {_, Exe, _, _, _, _, _, _} = cross_data(Arch),
- Exe.
-
-cross_data(Arch) ->
- case Arch of
- "vxworks_cpu32" ->
- {"VxWorks", "", ".o", ".eld", "68k", "cpu32",
- "-DCPU=CPU32 -DVXWORKS -I$(WIND_BASE)/target/h -mnobitfield -fno-builtin -nostdinc -fvolatile -msoft-float",
- "-r -d"};
- "vxworks_ppc860" ->
- {"VxWorks", "", ".o", ".eld", "ppc", "ppc860",
- "-DCPU=PPC860 -DVXWORKS -I$(WIND_BASE)/target/h -mcpu=860 -fno-builtin -fno-for-scope -msoft-float -D_GNU_TOOL -nostdinc",
- "-r -d"};
- "vxworks_ppc603" ->
- {"VxWorks", "", ".o", ".eld", "ppc", "ppc603",
- "-DCPU=PPC603 -DVXWORKS -I$(WIND_BASE)/target/h -fno-builtin -fno-for-scope -D_GNU_TOOL -nostdinc",
- "-r -d"};
- "vxworks_sparc" ->
- %%% The Sparc Architecture is included for private use (i.e. not Tornado 1.0.1 compatible).
- {"VxWorks", "", ".o", ".eld", "sparc", "sparc",
- "-DCPU=SPARC -DVXWORKS -I/home/gandalf/bsproj/BS.2/UOS/vw/5.2/h -fno-builtin -nostdinc",
- "-r -d"};
- "vxworks_ppc750" ->
- {"VxWorks", "", ".o", ".eld", "ppc", "ppc604",
- "-DCPU=PPC604 -DVXWORKS -DTOOL_FAMILY=gnu -DTOOL=gnu -I$(WIND_BASE)/target/h -fno-builtin -fno-for-scope -D_GNU_TOOL",
- "-r -d"};
- "vxworks_simso" ->
- {"VxWorks", "", ".o", ".eld", "simso", "simso",
- "-DCPU=SIMSPARCSOLARIS -DVXWORKS -DTOOL_FAMILY=gnu -DTOOL=gnu -I$(WIND_BASE)/target/h -I$(WIND_GCC_INCLUDE) -fno-builtin -fno-for-scope -D_GNU_TOOL",
- "-r -d"}
-
- end.
diff --git a/lib/test_server/src/ts_autoconf_win32.erl b/lib/test_server/src/ts_autoconf_win32.erl
index 258040b39e..4a5c5c7603 100644
--- a/lib/test_server/src/ts_autoconf_win32.erl
+++ b/lib/test_server/src/ts_autoconf_win32.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/test_server/src/ts_benchmark.erl b/lib/test_server/src/ts_benchmark.erl
new file mode 100644
index 0000000000..516d22fd2d
--- /dev/null
+++ b/lib/test_server/src/ts_benchmark.erl
@@ -0,0 +1,91 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012-2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, 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_benchmark).
+
+-include_lib("common_test/include/ct_event.hrl").
+-include_lib("kernel/include/file.hrl").
+-include("ts.hrl").
+
+-export([benchmarks/0,
+ run/3]).
+
+%% gen_event callbacks
+-export([init/1, handle_event/2]).
+
+benchmarks() ->
+ {ok, Cwd} = file:get_cwd(),
+ Benches = filelib:wildcard(
+ filename:join([Cwd,"..","*_test","*_bench.spec"])),
+ [begin
+ Base = filename:basename(N),
+ list_to_atom(string:substr(Base,1,string:rstr(Base,"_")-1))
+ end || N <- Benches].
+
+run(Specs, Opts, Vars) ->
+ {ok, Cwd} = file:get_cwd(),
+ {{YY,MM,DD},{HH,Mi,SS}} = calendar:local_time(),
+ BName = lists:concat([YY,"_",MM,"_",DD,"T",HH,"_",Mi,"_",SS]),
+ BDir = filename:join([Cwd,BName]),
+ file:make_dir(BDir),
+ [ts_run:run(atom_to_list(Spec),
+ [{spec, [atom_to_list(Spec)++"_bench.spec"]}],
+ [{event_handler, {ts_benchmark, [Spec,BDir]}}|Opts],Vars)
+ || Spec <- Specs],
+ file:delete(filename:join(Cwd,"latest_benchmark")),
+ {ok,D} = file:open(filename:join(Cwd,"latest_benchmark"),[write]),
+ io:format(D,BDir,[]),
+ file:close(D).
+
+
+%%%===================================================================
+%%% gen_event callbacks
+%%%===================================================================
+
+-record(state, { spec, suite, tc, stats_dir}).
+
+init([Spec,Dir]) ->
+ {ok, #state{ spec = Spec, stats_dir = Dir }}.
+
+handle_event(#event{name = tc_start, data = {Suite,Tc}}, State) ->
+ {ok,State#state{ suite = Suite, tc = Tc}};
+handle_event(#event{name = benchmark_data, data = Data}, State) ->
+ Spec = proplists:get_value(application, Data, State#state.spec),
+ Suite = proplists:get_value(suite, Data, State#state.suite),
+ Tc = proplists:get_value(name, Data, State#state.tc),
+ Value = proplists:get_value(value, Data),
+ {ok, D} = file:open(filename:join(
+ [State#state.stats_dir,
+ lists:concat([e(Spec),"-",e(Suite),"-",
+ e(Tc),".ebench"])]),
+ [append]),
+ io:format(D, "~p~n",[Value]),
+ file:close(D),
+ {ok, State};
+handle_event(_Event, State) ->
+ {ok, State}.
+
+
+e(Atom) when is_atom(Atom) ->
+ Atom;
+e(Str) when is_list(Str) ->
+ lists:map(fun($/) ->
+ $\\;
+ (C) ->
+ C
+ end,Str).
diff --git a/lib/test_server/src/ts_erl_config.erl b/lib/test_server/src/ts_erl_config.erl
index 45d88016a4..73abe86e11 100644
--- a/lib/test_server/src/ts_erl_config.erl
+++ b/lib/test_server/src/ts_erl_config.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -160,7 +160,6 @@ system_include(Root, Vars) ->
SysDir =
case ts_lib:var(os, Vars) of
"Windows" ++ _T -> "sys/win32";
- "VxWorks" -> "sys.vxworks";
_ -> "sys/unix"
end,
" -I" ++ quote(filename:nativename(filename:join([Root, "erts", "emulator", SysDir]))).
@@ -176,9 +175,6 @@ erl_interface(Vars,OsType) ->
{installed, _Root} ->
{filename:join(Dir, "lib"),
filename:join(Dir, "src")};
- {srctree, _Root, _Target} when OsType =:= vxworks ->
- {filename:join(Dir, "lib"),
- filename:join([Dir, "src"])};
{srctree, _Root, Target} ->
{filename:join([Dir, "obj", Target]),
filename:join([Dir, "src", Target])}
@@ -218,7 +214,7 @@ erl_interface(Vars,OsType) ->
{unix,_} ->
"-lpthread";
_ ->
- "" % VxWorks
+ ""
end,
[{erl_interface_libpath, quote(filename:nativename(LibPath))},
{erl_interface_sock_libs, sock_libraries(OsType)},
@@ -318,16 +314,12 @@ get_var(Key, Vars) ->
sock_libraries({win32, _}) ->
"ws2_32.lib";
sock_libraries({unix, _}) ->
- ""; % Included in general libraries if needed.
-sock_libraries(vxworks) ->
- "".
+ "". % Included in general libraries if needed.
link_library(LibName,{win32, _}) ->
LibName ++ ".lib";
link_library(LibName,{unix, _}) ->
"lib" ++ LibName ++ ".a";
-link_library(LibName,vxworks) ->
- "lib" ++ LibName ++ ".a";
link_library(_LibName,_Other) ->
exit({link_library, not_supported}).
diff --git a/lib/test_server/src/ts_install.erl b/lib/test_server/src/ts_install.erl
index 99ccfbc9bc..ba8952f10f 100644
--- a/lib/test_server/src/ts_install.erl
+++ b/lib/test_server/src/ts_install.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -55,8 +55,7 @@ build_install(TargetSystem, Options) ->
end.
os_type({unix,_}=OsType) -> OsType;
-os_type({win32,_}=OsType) -> OsType;
-os_type(_Other) -> vxworks.
+os_type({win32,_}=OsType) -> OsType.
target_install(CrossVars) ->
io:format("Cross installation detected, skipping configure and data_dir make~n"),
@@ -76,7 +75,6 @@ target_install(CrossVars) ->
%% Autoconf for various platforms.
%% unix uses the configure script
%% win32 uses ts_autoconf_win32
-%% VxWorks uses ts_autoconf_vxworks.
autoconf(TargetSystem, XComp) ->
case autoconf1(TargetSystem, XComp) of
@@ -90,8 +88,6 @@ autoconf1({win32, _},[{cross,"no"}]) ->
ts_autoconf_win32:configure();
autoconf1({unix, _},XCompFile) ->
unix_autoconf(XCompFile);
-autoconf1(Other,[{cross,"no"}]) ->
- ts_autoconf_vxworks:configure(Other);
autoconf1(_,_) ->
io:format("cross compilation not supported for that this platform~n"),
throw(cross_installation_failed).
diff --git a/lib/test_server/src/ts_install_cth.erl b/lib/test_server/src/ts_install_cth.erl
index 67f2df0cae..9b6e10e7e2 100644
--- a/lib/test_server/src/ts_install_cth.erl
+++ b/lib/test_server/src/ts_install_cth.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/test_server/src/ts_lib.erl b/lib/test_server/src/ts_lib.erl
index d521d2beda..c0200ab67c 100644
--- a/lib/test_server/src/ts_lib.erl
+++ b/lib/test_server/src/ts_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -25,9 +25,8 @@
-compile({no_auto_import,[error/1]}).
-export([error/1, var/2, erlang_type/0,
erlang_type/1,
- initial_capital/1, interesting_logs/1,
- specs/1, suites/2, last_test/1,
- force_write_file/2, force_delete/1,
+ initial_capital/1,
+ specs/1, suites/2,
subst_file/3, subst/2, print_data/1,
make_non_erlang/2,
maybe_atom_to_list/1, progress/4
@@ -91,25 +90,18 @@ initial_capital([C|Rest]) when $a =< C, C =< $z ->
initial_capital(String) ->
String.
-%% Returns a list of the "interesting logs" in a directory,
-%% i.e. those that correspond to spec files.
-
-interesting_logs(Dir) ->
- Logs = filelib:wildcard(filename:join(Dir, [$*|?logdir_ext])),
- Interesting =
- case specs(Dir) of
- [] ->
- Logs;
- Specs0 ->
- Specs = ordsets:from_list(Specs0),
- [L || L <- Logs, ordsets:is_element(filename_to_atom(L), Specs)]
- end,
- sort_tests(Interesting).
-
specs(Dir) ->
Specs = filelib:wildcard(filename:join([filename:dirname(Dir),
- "*_test", "*.{dyn,}spec"])),
- sort_tests([filename_to_atom(Name) || Name <- Specs]).
+ "*_test", "*.{dyn,}spec"])),
+ % Filter away all spec which end with _bench.spec
+ NoBench = fun(SpecName) ->
+ case lists:reverse(SpecName) of
+ "ceps.hcneb_"++_ -> false;
+ _ -> true
+ end
+ end,
+
+ sort_tests([filename_to_atom(Name) || Name <- Specs, NoBench(Name)]).
suites(Dir, Spec) ->
Glob=filename:join([filename:dirname(Dir), Spec++"_test",
@@ -151,48 +143,11 @@ suite_order(inets) -> 28;
suite_order(asn1) -> 30;
suite_order(os_mon) -> 32;
suite_order(snmp) -> 38;
-suite_order(mnemosyne) -> 40;
suite_order(mnesia_session) -> 42;
suite_order(mnesia) -> 44;
suite_order(system) -> 999; %% IMPORTANT: system SHOULD always be last!
suite_order(_) -> 200.
-last_test(Dir) ->
- last_test(filelib:wildcard(filename:join(Dir, "run.[1-2]*")), false).
-
-last_test([Run|Rest], false) ->
- last_test(Rest, Run);
-last_test([Run|Rest], Latest) when Run > Latest ->
- last_test(Rest, Run);
-last_test([_|Rest], Latest) ->
- last_test(Rest, Latest);
-last_test([], Latest) ->
- Latest.
-
-%% Do the utmost to ensure that the file is written, by deleting or
-%% renaming an old file with the same name.
-
-force_write_file(Name, Contents) ->
- force_delete(Name),
- file:write_file(Name, Contents).
-
-force_delete(Name) ->
- case file:delete(Name) of
- {error, eacces} ->
- force_rename(Name, Name ++ ".old.", 0);
- Other ->
- Other
- end.
-
-force_rename(From, To, Number) ->
- Dest = [To|integer_to_list(Number)],
- case file:read_file_info(Dest) of
- {ok, _} ->
- force_rename(From, To, Number+1);
- {error, _} ->
- file:rename(From, Dest)
- end.
-
%% Substitute all occurrences of @var@ in the In file, using
%% the list of variables in Vars, producing the output file Out.
%% Returns: ok | {error, Reason}
diff --git a/lib/test_server/src/ts_make.erl b/lib/test_server/src/ts_make.erl
index 1d8ef230c7..f3266f5836 100644
--- a/lib/test_server/src/ts_make.erl
+++ b/lib/test_server/src/ts_make.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/test_server/src/ts_reports.erl b/lib/test_server/src/ts_reports.erl
deleted file mode 100644
index f981a77ae4..0000000000
--- a/lib/test_server/src/ts_reports.erl
+++ /dev/null
@@ -1,545 +0,0 @@
-%%
-%% %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%
-%%
-
-%%% Purpose : Produces reports in HTML from the outcome of test suite runs.
-
--module(ts_reports).
-
--export([make_index/0, make_master_index/2, make_progress_index/2]).
--export([count_cases/1, year/0, current_time/0]).
-
--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]).
-
-
-%% Make master index page which points out index pages for all platforms.
-
-make_master_index(Dir, Vars) ->
- IndexName = filename:join(Dir, "index.html"),
- {ok, Index0} = make_master_index1(directories(Dir), master_header(Vars)),
- Index = [Index0|master_footer()],
- io:put_chars("Updating " ++ IndexName ++ "... "),
- ok = ts_lib:force_write_file(IndexName, Index),
- io:put_chars("done\n").
-
-make_master_index1([Dir|Rest], Result) ->
- NewResult =
- case catch read_variables(Dir) of
- {'EXIT',{{bad_installation,Reason},_}} ->
- io:put_chars("Failed to read " ++ filename:join(Dir,?variables)++
- ": " ++ Reason ++ " - Ignoring this directory\n"),
- Result;
- Vars ->
- Platform = ts_lib:var(platform_label, Vars),
- case make_index(Dir, Vars, false) of
- {ok, Summary} ->
- make_master_index(Platform, Dir, Summary, Result);
- {error, _} ->
- Result
- end
- end,
- make_master_index1(Rest, NewResult);
-make_master_index1([], Result) ->
- {ok, Result}.
-
-make_progress_index(Dir, Vars) ->
- IndexName = filename:join(Dir, "index.html"),
- io:put_chars("Updating " ++ IndexName ++ "... "),
- Index0=progress_header(Vars),
- ts_lib:force_delete(IndexName),
- Dirs=find_progress_runs(Dir),
- Index1=[Index0|make_progress_links(Dirs, [])],
- IndexF=[Index1|progress_footer()],
- ok = ts_lib:force_write_file(IndexName, IndexF),
- io:put_chars("done\n").
-
-find_progress_runs(Dir) ->
- case file:list_dir(Dir) of
- {ok, Dirs0} ->
- Dirs1= [filename:join(Dir,X) || X <- Dirs0,
- filelib:is_dir(filename:join(Dir,X))],
- lists:sort(Dirs1);
- _ ->
- []
- end.
-
-name_from_vars(Dir, Platform) ->
- VarFile=filename:join([Dir, Platform, "variables"]),
- case file:consult(VarFile) of
- {ok, Vars} ->
- ts_lib:var(platform_id, Vars);
- _Other ->
- Platform
- end.
-
-make_progress_links([], Acc) ->
- Acc;
-make_progress_links([RDir|Rest], Acc) ->
- Dir=filename:basename(RDir),
- Platforms=[filename:basename(X) ||
- X <- find_progress_runs(RDir)],
- PlatformLinks=["<A HREF=\""++filename:join([Dir,X,"index.html"])
- ++"\">"++name_from_vars(RDir, X)++"</A><BR>" ||
- X <- Platforms],
- LinkName=Dir++"/index.html",
- Link =
- [
- "<TR valign=top>\n",
- "<TD><A HREF=\"", LinkName, "\">", Dir, "</A></TD>", "\n",
- "<TD>", PlatformLinks, "</TD>", "\n"
- ],
- make_progress_links(Rest, [Link|Acc]).
-
-read_variables(Dir) ->
- case file:consult(filename:join(Dir, ?variables)) of
- {ok, Vars} -> Vars;
- {error, Reason} ->
- erlang:error({bad_installation,file:format_error(Reason)}, [Dir])
- end.
-
-make_master_index(Platform, Dirname, {Succ, Fail, UserSkip,AutoSkip}, Result) ->
- Link = filename:join(filename:basename(Dirname), "index.html"),
- FailStr =
- if Fail > 0 ->
- ["<FONT color=\"red\">",
- integer_to_list(Fail),"</FONT>"];
- true ->
- integer_to_list(Fail)
- end,
- AutoSkipStr =
- if AutoSkip > 0 ->
- ["<FONT color=\"brown\">",
- integer_to_list(AutoSkip),"</FONT>"];
- true -> integer_to_list(AutoSkip)
- end,
- [Result,
- "<TR valign=top>\n",
- "<TD><A HREF=\"", Link, "\">", Platform, "</A></TD>", "\n",
- make_row(integer_to_list(Succ), false),
- make_row(FailStr, false),
- make_row(integer_to_list(UserSkip), false),
- make_row(AutoSkipStr, false),
- "</TR>\n"].
-
-%% Make index page which points out individual test suites for a single platform.
-
-make_index() ->
- {ok, Pwd} = file:get_cwd(),
- Vars = read_variables(Pwd),
- make_index(Pwd, Vars, true).
-
-make_index(Dir, Vars, IncludeLast) ->
- IndexName = filename:absname("index.html", Dir),
- io:put_chars("Updating " ++ IndexName ++ "... "),
- case catch make_index1(Dir, IndexName, Vars, IncludeLast) of
- {'EXIT', Reason} ->
- io:put_chars("CRASHED!\n"),
- io:format("~p~n", [Reason]),
- {error, Reason};
- {error, Reason} ->
- io:put_chars("FAILED\n"),
- io:format("~p~n", [Reason]),
- {error, Reason};
- {ok, Summary} ->
- io:put_chars("done\n"),
- {ok, Summary};
- Err ->
- io:format("Unknown internal error. Please report.\n(Err: ~p, ID: 1)",
- [Err]),
- {error, Err}
- end.
-
-make_index1(Dir, IndexName, Vars, IncludeLast) ->
- Logs0 = ts_lib:interesting_logs(Dir),
- Logs =
- case IncludeLast of
- true -> add_last_name(Logs0);
- false -> Logs0
- end,
- {ok, {Index0, Summary}} = make_index(Logs, header(Vars), 0, 0, 0, 0, 0),
- Index = [Index0|footer()],
- case ts_lib:force_write_file(IndexName, Index) of
- ok ->
- {ok, Summary};
- {error, Reason} ->
- error({index_write_error, Reason})
- end.
-
-make_index([Name|Rest], Result, TotSucc, TotFail, UserSkip, AutoSkip, TotNotBuilt) ->
- case ts_lib:last_test(Name) of
- false ->
- %% Silently skip.
- make_index(Rest, Result, TotSucc, TotFail, UserSkip, AutoSkip, TotNotBuilt);
- Last ->
- case count_cases(Last) of
- {Succ, Fail, USkip, ASkip} ->
- Cov =
- case file:read_file(filename:join(Last,?cover_total)) of
- {ok,Bin} ->
- TotCoverage = binary_to_term(Bin),
- io_lib:format("~w %",[TotCoverage]);
- _error ->
- ""
- end,
- Link = filename:join(basename(Name), basename(Last)),
- JustTheName = rootname(basename(Name)),
- NotBuilt = not_built(JustTheName),
- NewResult = [Result, make_index1(JustTheName,
- Link, Succ, Fail, USkip, ASkip,
- NotBuilt, Cov, false)],
- make_index(Rest, NewResult, TotSucc+Succ, TotFail+Fail,
- UserSkip+USkip, AutoSkip+ASkip, TotNotBuilt+NotBuilt);
- error ->
- make_index(Rest, Result, TotSucc, TotFail, UserSkip, AutoSkip,
- TotNotBuilt)
- end
- end;
-make_index([], Result, TotSucc, TotFail, UserSkip, AutoSkip, TotNotBuilt) ->
- {ok, {[Result|make_index1("Total", no_link,
- TotSucc, TotFail, UserSkip, AutoSkip,
- TotNotBuilt, "", true)],
- {TotSucc, TotFail, UserSkip, AutoSkip}}}.
-
-make_index1(SuiteName, Link, Success, Fail, UserSkip, AutoSkip, NotBuilt, Coverage, Bold) ->
- Name = test_suite_name(SuiteName),
- FailStr =
- if Fail > 0 ->
- ["<FONT color=\"red\">",
- integer_to_list(Fail),"</FONT>"];
- true ->
- integer_to_list(Fail)
- end,
- AutoSkipStr =
- if AutoSkip > 0 ->
- ["<FONT color=\"brown\">",
- integer_to_list(AutoSkip),"</FONT>"];
- true -> integer_to_list(AutoSkip)
- end,
- ["<TR valign=top>\n",
- "<TD>",
- case Link of
- no_link ->
- ["<B>", Name|"</B>"];
- _Other ->
- CrashDumpName = SuiteName ++ "_erl_crash.dump",
- CrashDumpLink =
- case filelib:is_file(CrashDumpName) of
- true ->
- ["&nbsp;<A HREF=\"", CrashDumpName,
- "\">(CrashDump)</A>"];
- false ->
- ""
- end,
- LogFile = filename:join(Link, ?suitelog_name ++ ".html"),
- ["<A HREF=\"", LogFile, "\">", Name, "</A>\n", CrashDumpLink,
- "</TD>\n"]
- end,
- make_row(integer_to_list(Success), Bold),
- make_row(FailStr, Bold),
- make_row(integer_to_list(UserSkip), Bold),
- make_row(AutoSkipStr, Bold),
- make_row(integer_to_list(NotBuilt), Bold),
- make_row(Coverage, Bold),
- "</TR>\n"].
-
-make_row(Row, true) ->
- ["<TD ALIGN=right><B>", Row|"</B></TD>"];
-make_row(Row, false) ->
- ["<TD ALIGN=right>", Row|"</TD>"].
-
-not_built(BaseName) ->
- Dir = filename:join("..", BaseName++"_test"),
- Erl = length(filelib:wildcard(filename:join(Dir,"*_SUITE.erl"))),
- Beam = length(filelib:wildcard(filename:join(Dir,"*_SUITE.beam"))),
- Erl-Beam.
-
-
-%% Add the log file directory for the very last test run (according to
-%% last_name).
-
-add_last_name(Logs) ->
- case file:read_file("last_name") of
- {ok, Bin} ->
- Name = filename:dirname(lib:nonl(binary_to_list(Bin))),
- case lists:member(Name, Logs) of
- true -> Logs;
- false -> [Name|Logs]
- end;
- _ ->
- Logs
- end.
-
-term_to_text(Term) ->
- lists:flatten(io_lib:format("~p.\n", [Term])).
-
-test_suite_name(Name) ->
- ts_lib:initial_capital(Name) ++ " suite".
-
-directories(Dir) ->
- {ok, Files} = file:list_dir(Dir),
- [filename:join(Dir, X) || X <- Files,
- filelib:is_dir(filename:join(Dir, X))].
-
-
-%%% Headers and footers.
-
-header(Vars) ->
- Platform = ts_lib:var(platform_id, Vars),
- ["<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n"
- "<!-- autogenerated by '"++atom_to_list(?MODULE)++"'. -->\n"
- "<HTML>\n",
- "<HEAD>\n",
- "<TITLE>Test Results for ", Platform, "</TITLE>\n",
- "</HEAD>\n",
-
- body_tag(),
-
- "<!-- ---- DOCUMENT TITLE ---- -->\n",
-
- "<CENTER>\n",
- "<H1>Test Results for ", Platform, "</H1>\n",
- "</CENTER>\n",
-
- "<!-- ---- CONTENT ---- -->\n",
- "<CENTER>\n",
-
- "<TABLE border=3 cellpadding=5>\n",
- "<th><B>Family</B></th>\n",
- "<th>Successful</th>\n",
- "<th>Failed</th>\n",
- "<th>User Skipped</th>\n"
- "<th>Auto Skipped</th>\n"
- "<th>Missing Suites</th>\n"
- "<th>Coverage</th>\n"
- "\n"].
-
-footer() ->
- ["</TABLE>\n"
- "</CENTER>\n"
- "<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"
- "</HTML>\n"].
-
-progress_header(Vars) ->
- Release = ts_lib:var(erl_release, Vars),
- ["<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n"
- "<!-- autogenerated by '"++atom_to_list(?MODULE)++"'. -->\n"
- "<HTML>\n",
- "<HEAD>\n",
- "<TITLE>", Release, " Progress Test Results</TITLE>\n",
- "</HEAD>\n",
-
- body_tag(),
-
- "<!-- ---- DOCUMENT TITLE ---- -->\n",
-
- "<CENTER>\n",
- "<H1>", Release, " Progress Test Results</H1>\n",
- "<TABLE border=3 cellpadding=5>\n",
- "<th><b>Test Run</b></th><th>Platforms</th>\n"].
-
-progress_footer() ->
- ["</TABLE>\n",
- "</CENTER>\n",
- "<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",
- "</HTML>\n"].
-
-master_header(Vars) ->
- Release = ts_lib:var(erl_release, Vars),
- Vsn = erlang:system_info(version),
- ["<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n"
- "<!-- autogenerated by '"++atom_to_list(?MODULE)++"'. -->\n"
- "<HTML>\n",
- "<HEAD>\n",
- "<TITLE>", Release, " Test Results (", Vsn, ")</TITLE>\n",
- "</HEAD>\n",
-
- body_tag(),
-
- "<!-- ---- DOCUMENT TITLE ---- -->\n",
-
- "<CENTER>\n",
- "<H1>", Release, " Test Results (", Vsn, ")</H1>\n",
- "</CENTER>\n",
-
- "<!-- ---- CONTENT ---- -->\n",
-
- "<CENTER>\n",
-
- "<TABLE border=3 cellpadding=5>\n",
- "<th><b>Platform</b></th>\n",
- "<th>Successful</th>\n",
- "<th>Failed</th>\n",
- "<th>User Skipped</th>\n"
- "<th>Auto Skipped</th>\n"
- "\n"].
-
-master_footer() ->
- ["</TABLE>\n",
- "</CENTER>\n",
- "<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",
- "</HTML>\n"].
-
-body_tag() ->
- "<body bgcolor=\"#FFFFFF\" text=\"#000000\" link=\"#0000FF\""
- "vlink=\"#800080\" alink=\"#FF0000\">".
-
-year() ->
- {Y, _, _} = date(),
- integer_to_list(Y).
-
-current_time() ->
- {{Y, Mon, D}, {H, Min, S}} = calendar:local_time(),
- Weekday = weekday(calendar:day_of_the_week(Y, Mon, D)),
- lists:flatten(io_lib:format("~s ~s ~p ~2.2.0w:~2.2.0w:~2.2.0w ~w",
- [Weekday, month(Mon), D, H, Min, S, Y])).
-
-weekday(1) -> "Mon";
-weekday(2) -> "Tue";
-weekday(3) -> "Wed";
-weekday(4) -> "Thu";
-weekday(5) -> "Fri";
-weekday(6) -> "Sat";
-weekday(7) -> "Sun".
-
-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".
-
-%% Count test cases in the given directory (a directory of the type
-%% run.1997-08-04_09.58.52).
-
-count_cases(Dir) ->
- SumFile = filename:join(Dir, ?run_summary),
- case read_summary(SumFile, [summary]) of
- {ok, [{Succ,Fail,Skip}]} ->
- {Succ,Fail,Skip,0};
- {ok, [Summary]} ->
- Summary;
- {error, _} ->
- LogFile = filename:join(Dir, ?suitelog_name),
- case file:read_file(LogFile) of
- {ok, Bin} ->
- Summary = count_cases1(binary_to_list(Bin), {0, 0, 0, 0}),
- write_summary(SumFile, Summary),
- Summary;
- {error, _Reason} ->
- io:format("\nFailed to read ~p (skipped)\n", [LogFile]),
- error
- end
- end.
-
-write_summary(Name, Summary) ->
- File = [term_to_text({summary, Summary})],
- ts_lib:force_write_file(Name, File).
-
-% XXX: This function doesn't do what the writer expect. It can't handle
-% the case if there are several different keys and I had to add a special
-% case for the empty file. The caller also expect just one tuple as
-% a result so this function is written way to general for no reason.
-% But it works sort of. /kgb
-
-read_summary(Name, Keys) ->
- case file:consult(Name) of
- {ok, []} ->
- {error, "Empty summary file"};
- {ok, Terms} ->
- {ok, lists:map(fun(Key) -> {value, {_, Value}} =
- lists:keysearch(Key, 1, Terms),
- Value end,
- Keys)};
- {error, Reason} ->
- {error, Reason}
- end.
-
-count_cases1("=failed" ++ Rest, {Success, _Fail, UserSkip,AutoSkip}) ->
- {NextLine, Count} = get_number(Rest),
- count_cases1(NextLine, {Success, Count, UserSkip,AutoSkip});
-count_cases1("=successful" ++ Rest, {_Success, Fail, UserSkip,AutoSkip}) ->
- {NextLine, Count} = get_number(Rest),
- count_cases1(NextLine, {Count, Fail, UserSkip,AutoSkip});
-count_cases1("=skipped" ++ Rest, {Success, Fail, _UserSkip,AutoSkip}) ->
- {NextLine, Count} = get_number(Rest),
- count_cases1(NextLine, {Success, Fail, Count,AutoSkip});
-count_cases1("=user_skipped" ++ Rest, {Success, Fail, _UserSkip,AutoSkip}) ->
- {NextLine, Count} = get_number(Rest),
- count_cases1(NextLine, {Success, Fail, Count,AutoSkip});
-count_cases1("=auto_skipped" ++ Rest, {Success, Fail, UserSkip,_AutoSkip}) ->
- {NextLine, Count} = get_number(Rest),
- count_cases1(NextLine, {Success, Fail, UserSkip,Count});
-count_cases1([], Counters) ->
- Counters;
-count_cases1(Other, Counters) ->
- count_cases1(skip_to_nl(Other), Counters).
-
-get_number([$\s|Rest]) ->
- get_number(Rest);
-get_number([Digit|Rest]) when $0 =< Digit, Digit =< $9 ->
- get_number(Rest, Digit-$0).
-
-get_number([Digit|Rest], Acc) when $0 =< Digit, Digit =< $9 ->
- get_number(Rest, Acc*10+Digit-$0);
-get_number([$\n|Rest], Acc) ->
- {Rest, Acc};
-get_number([_|Rest], Acc) ->
- get_number(Rest, Acc).
-
-skip_to_nl([$\n|Rest]) ->
- Rest;
-skip_to_nl([_|Rest]) ->
- skip_to_nl(Rest);
-skip_to_nl([]) ->
- [].
diff --git a/lib/test_server/src/ts_run.erl b/lib/test_server/src/ts_run.erl
index a61028e4bc..741dd483f5 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-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -21,7 +21,7 @@
-module(ts_run).
--export([run/4]).
+-export([run/4,ct_run_test/2]).
-define(DEFAULT_MAKE_TIMETRAP_MINUTES, 60).
-define(DEFAULT_UNMAKE_TIMETRAP_MINUTES, 15).
@@ -87,6 +87,24 @@ execute([Hook|Rest], Vars0, Spec0, St0) ->
execute([], Vars, Spec, St) ->
{ok, Vars, Spec, St}.
+%% Wrapper to run tests using ct:run_test/1 and handle any errors.
+
+ct_run_test(Dir, CommonTestArgs) ->
+ try
+ ok = file:set_cwd(Dir),
+ case ct:run_test(CommonTestArgs) of
+ {_,_,_} ->
+ ok;
+ {error,Error} ->
+ io:format("ERROR: ~P\n", [Error,20]);
+ Other ->
+ io:format("~P\n", [Other,20])
+ end
+ catch
+ _:Crash ->
+ io:format("CRASH: ~P\n", [Crash,20])
+ end.
+
%%
%% Deletes File from Files when File is on the form .../<SUITE>_data/<file>
%% when all of <SUITE> has been skipped in Spec, i.e. there
@@ -157,7 +175,6 @@ get_config_files() ->
[TSConfig | case os:type() of
{unix,_} -> ["ts.unix.config"];
{win32,_} -> ["ts.win32.config"];
- vxworks -> ["ts.vxworks.config"];
_ -> []
end].
@@ -231,8 +248,7 @@ make_command(Vars, Spec, State) ->
" -boot start_sasl -sasl errlog_type error",
" -pz \"",Cwd,"\"",
" -ct_test_vars ",TestVars,
- " -eval \"file:set_cwd(\\\"",TestDir,"\\\")\" "
- " -eval \"ct:run_test(",
+ " -eval \"ts_run:ct_run_test(\\\"",TestDir,"\\\", ",
backslashify(lists:flatten(State#state.test_server_args)),")\""
" ",
ExtraArgs],
@@ -329,14 +345,13 @@ start_xterm(Command) ->
path_separator() ->
case os:type() of
{win32, _} -> ";";
- {unix, _} -> ":";
- vxworks -> ":"
+ {unix, _} -> ":"
end.
-make_common_test_args(Args0, Options, _Vars) ->
+make_common_test_args(Args0, Options0, _Vars) ->
Trace =
- case lists:keysearch(trace,1,Options) of
+ case lists:keysearch(trace,1,Options0) of
{value,{trace,TI}} when is_tuple(TI); is_tuple(hd(TI)) ->
ok = file:write_file(?tracefile,io_lib:format("~p.~n",[TI])),
[{ct_trace,?tracefile}];
@@ -348,40 +363,35 @@ make_common_test_args(Args0, Options, _Vars) ->
[]
end,
Cover =
- case lists:keysearch(cover,1,Options) of
+ case lists:keysearch(cover,1,Options0) of
{value,{cover, App, none, _Analyse}} ->
io:format("No cover file found for ~p~n",[App]),
[];
{value,{cover,_App,File,_Analyse}} ->
- [{cover,to_list(File)}];
+ [{cover,to_list(File)},{cover_stop,false}];
false ->
[]
end,
- Logdir = case lists:keysearch(logdir, 1, Options) of
+ Logdir = case lists:keysearch(logdir, 1, Options0) of
{value,{logdir, _}} ->
[];
false ->
[{logdir,"../test_server"}]
end,
- TimeTrap = case test_server:timetrap_scale_factor() of
- 1 ->
- [];
- Scale ->
- [{multiply_timetraps, Scale},
- {scale_timetraps, true}]
- 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,
+ TimeTrap = [{scale_timetraps, true}],
+
+ {ConfigPath,
+ Options} = case {os:getenv("TEST_CONFIG_PATH"),
+ lists:keysearch(config, 1, Options0)} of
+ {_,{value, {config, Path}}} ->
+ {Path,lists:keydelete(config, 1, Options0)};
+ {false,false} ->
+ {"../test_server",Options0};
+ {Path,_} ->
+ {Path,Options0}
+ end,
ConfigFiles = [{config,[filename:join(ConfigPath,File)
|| File <- get_config_files()]}],
io_lib:format("~100000p",[Args0++Trace++Cover++Logdir++
diff --git a/lib/test_server/src/ts_selftest.erl b/lib/test_server/src/ts_selftest.erl
deleted file mode 100644
index 655aa4bab3..0000000000
--- a/lib/test_server/src/ts_selftest.erl
+++ /dev/null
@@ -1,120 +0,0 @@
-%%
-%% %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%
-%%
--module(ts_selftest).
--export([selftest/0]).
-
-selftest() ->
- case node() of
- nonode@nohost ->
- io:format("Sorry, you have to start this node distributed.~n"),
- exit({error, node_not_distributed});
- _ ->
- ok
- end,
- case catch ts:tests(test_server) of
- {'EXIT', _} ->
- io:format("Test Server self test not availiable.");
- Other ->
- selftest1()
- end.
-
-selftest1() ->
- % Batch starts
- io:format("Selftest #1: Whole spec, batch mode:~n"),
- io:format("------------------------------------~n"),
- ts:run(test_server, [batch]),
- ok=check_result(1, "test_server.logs", 2),
-
- io:format("Selftest #2: One module, batch mode:~n"),
- io:format("------------------------------------~n"),
- ts:run(test_server, test_server_SUITE, [batch]),
- ok=check_result(2, "test_server_SUITE.logs", 2),
-
- io:format("Selftest #3: One testcase, batch mode:~n"),
- io:format("--------------------------------------~n"),
- ts:run(test_server, test_server_SUITE, msgs, [batch]),
- ok=check_result(3, "test_server_SUITE.logs", 0),
-
- % Interactive starts
- io:format("Selftest #4: Whole spec, interactive mode:~n"),
- io:format("------------------------------------------~n"),
- ts:run(test_server),
- kill_test_server(),
- ok=check_result(4, "test_server.logs", 2),
-
- io:format("Selftest #5: One module, interactive mode:~n"),
- io:format("------------------------------------------~n"),
- ts:run(test_server, test_server_SUITE),
- kill_test_server(),
- ok=check_result(5, "test_server_SUITE.logs", 2),
-
- io:format("Selftest #6: One testcase, interactive mode:~n"),
- io:format("--------------------------------------------~n"),
- ts:run(test_server, test_server_SUITE, msgs),
- kill_test_server(),
- ok=check_result(6, "test_server_SUITE.logs", 0),
-
- ok.
-
-check_result(Test, TDir, ExpSkip) ->
- Dir=ts_lib:last_test(TDir),
- {Total, Failed, Skipped}=ts_reports:count_cases(Dir),
- io:format("Selftest #~p:",[Test]),
- case {Total, Failed, Skipped} of
- {_, 0, ExpSkip} -> % 2 test cases should be skipped.
- io:format("All ok.~n~n"),
- ok;
- {_, _, _} ->
- io:format("Not completely successful.~n~n"),
- error
- end.
-
-
-%% Wait for test server to get started.
-kill_test_server() ->
- Node=list_to_atom("test_server@"++atom_to_list(hostname())),
- net_adm:ping(Node),
- case whereis(test_server_ctrl) of
- undefined ->
- kill_test_server();
- Pid ->
- kill_test_server(0, Pid)
- end.
-
-%% Wait for test server to finish.
-kill_test_server(30, Pid) ->
- exit(self(), test_server_is_dead);
-kill_test_server(Num, Pid) ->
- case whereis(test_server_ctrl) of
- undefined ->
- slave:stop(node(Pid));
- Pid ->
- receive
- after
- 1000 ->
- kill_test_server(Num+1, Pid)
- end
- end.
-
-
-hostname() ->
- list_to_atom(from($@, atom_to_list(node()))).
-from(H, [H | T]) -> T;
-from(H, [_ | T]) -> from(H, T);
-from(H, []) -> [].
diff --git a/lib/test_server/src/vxworks_client.erl b/lib/test_server/src/vxworks_client.erl
deleted file mode 100644
index ca65eca02a..0000000000
--- a/lib/test_server/src/vxworks_client.erl
+++ /dev/null
@@ -1,243 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2002-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(vxworks_client).
-
--export([open/1, close/1, send_data/2, send_data/3, send_data_wait_for_close/2, reboot/1]).
--export([init/2]).
-
--include("ts.hrl").
-
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%%% This is a client talking to a test server daemon on a VxWorks card.
-%%%
-%%% User interface:
-%%%
-%%% open/1
-%%% Start a client and establish the connection with the test server daemon
-%%%
-%%% send_data/2
-%%% Send data/command to the test server daemon, don't wait for any return
-%%%
-%%% send_data/3
-%%% Send data/command to the test server daemon and wait for the given
-%%% return value.
-%%%
-%%% send_data_wait_for_close/2
-%%% Send data/command to the test server daemon and wait for the daemon to
-%%% close the connection.
-%%%
-%%% close/1
-%%% Close the client.
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-
-%%
-%% User interface
-%%
-
-reboot(Target) ->
- {ok, {_,_,_,_,_,[Addr|_]}} = inet:gethostbyname(Target),
- Fun = fun({ok, Socket}) ->
- gen_tcp:send(Socket, "q\n"),
- receive
- {tcp_closed, Socket} ->
- gen_tcp:close(Socket),
- {ok, socket_closed}
- after 5000 ->
- exit({timeout, tryagain})
- end
- end,
- io:format("Stopping (rebooting) ~p ",[Target]),
- case fun_target(Addr, Fun) of
- {ok, socket_closed} ->
- ok;
- _Else ->
- io:format("No contact with ts daemon - exiting ...~n"),
- exit({stop, no_ts_daemon_contact})
- end.
-
-
-%% open(Target) -> {ok,Client} | {error, Reason}
-open(Target) ->
- {ok, {_,_,_,_,_,[Addr|_]}} = inet:gethostbyname(Target),
- Fun = fun({ok, Socket}) ->
- P = spawn(?MODULE,init,[Target,Socket]),
- inet_tcp:controlling_process(Socket,P),
- {ok,P}
- end,
- case fun_target(Addr,Fun) of
- {ok, Pid} ->
- {ok, Pid};
- {error,Reason} ->
- {error, Reason}
- end.
-
-%% send_data(Client,Data) -> ok
-send_data(Pid,Data) ->
- Pid ! {send_data,Data++"\n"},
- ok.
-
-%% send_data(Client,Data,ExpectedReturn) -> {ok,ExpectedReturn} | {error,Reason}
-send_data(Pid,Data,Return) ->
- Pid ! {send_data,Data++"\n",Return,self()},
- receive {Pid,Result} -> Result end.
-
-%% send_data_wait_for_close(Client,Data) -> ok | {error,Reason}
-send_data_wait_for_close(Pid,Data) ->
- send_data(Pid,Data,tcp_closed).
-
-%% close(Client) -> ok
-close(Pid) ->
- Pid ! close,
- ok.
-
-
-%%
-%% Internal
-%%
-
-init(Target,Socket) ->
- process_flag(trap_exit,true),
- loop(Target,Socket).
-
-loop(Target,Socket) ->
- receive
- {send_data,Data} ->
- %% io:format("vx client sending: ~p~n", [Data]),
- gen_tcp:send(Socket, Data),
- loop(Socket,Target);
- {send_data,Data,tcp_closed,From} ->
- %% io:format("vx client sending: ~p~n", [Data]),
- gen_tcp:send(Socket, Data),
- receive
- {tcp_closed, Socket} ->
- From ! {self(),ok}
- after 5000 ->
- From ! {self(),{error,timeout}}
- end,
- closed(Socket,normal);
- {send_data,Data,Return,From} ->
- %% io:format("vx client sending: ~p~n", [Data]),
- gen_tcp:send(Socket, Data),
- case receive_line(Socket,[],Return,200) of
- {tcp_closed, Socket} ->
- From ! {self(),{error,{socket_closed,Target}}},
- closed(Socket,{socket_closed,Target});
- {tcp,Socket,_Rest} ->
- From ! {self(),{ok,Data}},
- got_data(Target,Socket,Data);
- error ->
- From ! {self(),{error,{catatonic,Target}}}
- end;
- close ->
- closed(Socket,normal);
- {tcp_closed, Socket} ->
- closed(Socket,{socket_closed,Target});
- {tcp,Socket,Data} ->
- got_data(Target,Socket,Data)
- end.
-
-
-
-closed(Socket,Reason) ->
- gen_tcp:close(Socket),
- exit(Reason).
-
-got_data(Target,Socket,Data) ->
- if is_atom(Target) ->
- io:format("~w: ~s",[Target,uncr(Data)]);
- true ->
- io:format("~s: ~s",[Target,uncr(Data)])
- end,
- loop(Target,Socket).
-
-uncr([]) ->
- [];
-uncr([$\r | T]) ->
- uncr(T);
-uncr([H | T]) ->
- [H | uncr(T)].
-
-strip_line(Line) ->
- RPos = string:rchr(Line, $\n),
- string:substr(Line,RPos+1).
-
-maybe_done_receive(Socket,Ack,Match,C) ->
- case string:str(Ack,Match) of
- 0 ->
- receive_line(Socket,strip_line(Ack),Match,C);
- _ ->
- {tcp,Socket,strip_line(Ack)}
- end.
-
-
-receive_line(_Socket,_Ack,_Match,0) ->
- error;
-receive_line(Socket,Ack,Match,Counter) ->
- receive
- {tcp_closed, Socket} ->
- {tcp_closed, Socket};
- {tcp,Socket,Data} ->
- NewAck = Ack ++ Data,
- case {string:str(NewAck,"\r") > 0,
- string:str(NewAck,"\n") > 0} of
- {true,_} ->
- maybe_done_receive(Socket,NewAck,Match,Counter-1);
- {_,true} ->
- maybe_done_receive(Socket,NewAck,Match,Counter-1);
- _ ->
- receive_line(Socket,NewAck,Match,Counter)
- end
- after 20000 ->
- error
- end.
-
-
-%% Misc functions
-fun_target(Addr, Fun) ->
- io:format("["),
- fun_target(Addr, Fun, 60). %Vx-cards need plenty of time.
-
-fun_target(_Addr, _Fun, 0) ->
- io:format(" no contact with ts daemon]~n"),
- {error,failed_to_connect};
-fun_target(Addr, Fun, Tries_left) ->
- receive after 1 -> ok end,
- case do_connect(Addr, Fun) of
- {ok, Value} ->
- io:format(" ok]~n"),
- {ok, Value};
- _Error -> % typical {error, econnrefused}
- io:format("."),
- receive after 10000 -> ok end,
- fun_target(Addr, Fun, Tries_left-1)
- end.
-
-do_connect(Addr, Fun) ->
- case gen_tcp:connect(Addr, ?TS_PORT, [{reuseaddr, true}], 60000) of
- {ok, Socket} ->
- Fun({ok, Socket});
- Error ->
- Error
- end.
-
-
-
diff --git a/lib/test_server/test/Makefile b/lib/test_server/test/Makefile
index b2ac95afaa..afccc28662 100644
--- a/lib/test_server/test/Makefile
+++ b/lib/test_server/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -26,8 +26,8 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk
MODULES= \
test_server_SUITE \
- test_server_line_SUITE \
- test_server_test_lib
+ test_server_test_lib \
+ erl2html2_SUITE
ERL_FILES= $(MODULES:%=%.erl)
@@ -65,7 +65,6 @@ make_emakefile:
>> $(EMAKEFILE)
tests debug opt: make_emakefile
- cd ../src && $(MAKE) ../ebin/test_server_line.beam
erl $(ERL_MAKE_FLAGS) -make
clean:
diff --git a/lib/test_server/test/erl2html2_SUITE.erl b/lib/test_server/test/erl2html2_SUITE.erl
new file mode 100644
index 0000000000..96175413a1
--- /dev/null
+++ b/lib/test_server/test/erl2html2_SUITE.erl
@@ -0,0 +1,254 @@
+%%%-------------------------------------------------------------------
+%%% @author Siri Hansen <[email protected]>
+%%% @copyright (C) 2012, Siri Hansen
+%%% @doc
+%%%
+%%% @end
+%%% Created : 15 Nov 2012 by Siri Hansen <[email protected]>
+%%%-------------------------------------------------------------------
+-module(erl2html2_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+
+-define(HEADER,
+ ["<!DOCTYPE HTML PUBLIC",
+ "\"-//W3C//DTD HTML 3.2 Final//EN\">\n",
+ "<!-- autogenerated by 'erl2html2' -->\n",
+ "<html>\n",
+ "<head><title>Module ", Src, "</title>\n",
+ "<meta http-equiv=\"cache-control\" ",
+ "content=\"no-cache\">\n",
+ "</head>\n",
+ "<body bgcolor=\"white\" text=\"black\" ",
+ "link=\"blue\" vlink=\"purple\" alink=\"red\">\n"]).
+
+%%--------------------------------------------------------------------
+%% @spec suite() -> Info
+%% Info = [tuple()]
+%% @end
+%%--------------------------------------------------------------------
+suite() ->
+ [{timetrap,{seconds,30}},
+ {ct_hooks,[ts_install_cth,test_server_test_lib]}].
+
+%%--------------------------------------------------------------------
+%% @spec init_per_suite(Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%% @end
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% @spec end_per_suite(Config0) -> void() | {save_config,Config1}
+%% Config0 = Config1 = [tuple()]
+%% @end
+%%--------------------------------------------------------------------
+end_per_suite(_Config) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% @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(_GroupName, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% @spec end_per_group(GroupName, Config0) ->
+%% void() | {save_config,Config1}
+%% GroupName = atom()
+%% Config0 = Config1 = [tuple()]
+%% @end
+%%--------------------------------------------------------------------
+end_per_group(_GroupName, _Config) ->
+ 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() ->
+ [].
+
+%%--------------------------------------------------------------------
+%% @spec all() -> GroupsAndTestCases | {skip,Reason}
+%% GroupsAndTestCases = [{group,GroupName} | TestCase]
+%% GroupName = atom()
+%% TestCase = atom()
+%% Reason = term()
+%% @end
+%%--------------------------------------------------------------------
+all() ->
+ [m1].
+
+%%--------------------------------------------------------------------
+%% @spec TestCase() -> Info
+%% Info = [tuple()]
+%% @end
+%%--------------------------------------------------------------------
+m1() ->
+ [].
+
+%%--------------------------------------------------------------------
+%% @spec TestCase(Config0) ->
+%% ok | exit() | {skip,Reason} | {comment,Comment} |
+%% {save_config,Config1} | {skip_and_save,Reason,Config1}
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%% Comment = term()
+%% @end
+%%--------------------------------------------------------------------
+m1(Config) ->
+ {Src,Dst} = convert_module("m1",Config),
+ {true,L} = check_line_numbers(Src,Dst),
+ ok = check_link_targets(Src,Dst,L,[{baz,0}]),
+ ok.
+
+convert_module(Mod,Config) ->
+ DataDir = ?config(data_dir,Config),
+ PrivDir = ?config(priv_dir,Config),
+ Src = filename:join(DataDir,Mod++".erl"),
+ Dst = filename:join(PrivDir,Mod++".erl.html"),
+ io:format("<a href=\"~s\">~s</a>\n",[Src,filename:basename(Src)]),
+ ok = erl2html2:convert(Src, Dst, "<html><body>"),
+ io:format("<a href=\"~s\">~s</a>\n",[Dst,filename:basename(Dst)]),
+ {Src,Dst}.
+
+%% Check that there are the same number of lines in each file, and
+%% that all line numbers are displayed in the dst file.
+check_line_numbers(Src,Dst) ->
+ {ok,SFd} = file:open(Src,[read]),
+ {ok,DFd} = file:open(Dst,[read]),
+ {ok,SN} = count_src_lines(SFd,0),
+ ok = file:close(SFd),
+ {ok,DN} = read_dst_line_numbers(DFd),
+ ok = file:close(DFd),
+ {SN == DN,SN}.
+
+count_src_lines(Fd,N) ->
+ case io:get_line(Fd,"") of
+ eof ->
+ {ok,N};
+ {error,Reason} ->
+ {error,Reason,N};
+ _Line ->
+ count_src_lines(Fd,N+1)
+ end.
+
+read_dst_line_numbers(Fd) ->
+ "<html><body><pre>\n" = io:get_line(Fd,""),
+ read_dst_line_numbers(Fd,0).
+read_dst_line_numbers(Fd,Last) when is_integer(Last) ->
+ case io:get_line(Fd,"") of
+ eof ->
+ {ok,Last};
+ {error,Reason} ->
+ {error,Reason,Last};
+ "</pre>"++_ ->
+ {ok,Last};
+ "</body>"++_ ->
+ {ok,Last};
+ Line ->
+ %% erlang:display(Line),
+ Num = check_line_number(Last,Line,Line),
+ read_dst_line_numbers(Fd,Num)
+ end.
+
+check_line_number(Last,Line,OrigLine) ->
+ case Line of
+ "<a name="++_ ->
+ [$>|Rest] = lists:dropwhile(fun($>) -> false; (_) -> true end,Line),
+ check_line_number(Last,Rest,OrigLine);
+ _ ->
+ [N |_] = string:tokens(Line,":"),
+% erlang:display(N),
+ Num =
+ try list_to_integer(string:strip(N))
+ catch _:_ -> ct:fail({no_line_number_after,Last,OrigLine})
+ end,
+ if Num == Last+1 ->
+ Num;
+ true ->
+ ct:fail({unexpected_integer,Num,Last})
+ end
+ end.
+
+
+%% Check that there is one link target for each line and one for each
+%% function.
+%% The test module has -compile(export_all), so all functions are
+%% found by listing the exported ones.
+check_link_targets(Src,Dst,L,RmFncs) ->
+ Mod = list_to_atom(filename:basename(filename:rootname(Src))),
+ Exports = Mod:module_info(exports)--[{module_info,0},{module_info,1}|RmFncs],
+ {ok,{[],L},_} = xmerl_sax_parser:file(Dst,
+ [{event_fun,fun sax_event/3},
+ {event_state,{Exports,0}}]),
+ ok.
+
+sax_event(Event,_Loc,State) ->
+ sax_event(Event,State).
+
+sax_event({startElement,_Uri,"a",_QN,Attrs},{Exports,PrevLine}) ->
+ {_,_,"name",Name} = lists:keyfind("name",3,Attrs),
+ case catch list_to_integer(Name) of
+ Line when is_integer(Line) ->
+ case PrevLine + 1 of
+ Line ->
+% erlang:display({found_line,Line}),
+ {Exports,Line};
+ Other ->
+ ct:fail({unexpected_line_number_target,Other})
+ end;
+ {'EXIT',_} ->
+ {match,[FStr,AStr]} =
+ re:run(Name,"^(.*)-([0-9]+)$",[{capture,all_but_first,list}]),
+ F = list_to_atom(http_uri:decode(FStr)),
+ A = list_to_integer(AStr),
+% erlang:display({found_fnc,F,A}),
+ A = proplists:get_value(F,Exports),
+ {lists:delete({F,A},Exports),PrevLine}
+ end;
+sax_event(_,State) ->
+ State.
diff --git a/lib/test_server/test/erl2html2_SUITE_data/Makefile.src b/lib/test_server/test/erl2html2_SUITE_data/Makefile.src
new file mode 100644
index 0000000000..942ac0584b
--- /dev/null
+++ b/lib/test_server/test/erl2html2_SUITE_data/Makefile.src
@@ -0,0 +1,2 @@
+all:
+ erlc -Iinclude m1.erl \ No newline at end of file
diff --git a/lib/test_server/test/erl2html2_SUITE_data/header1.hrl b/lib/test_server/test/erl2html2_SUITE_data/header1.hrl
new file mode 100644
index 0000000000..53d1b79ac5
--- /dev/null
+++ b/lib/test_server/test/erl2html2_SUITE_data/header1.hrl
@@ -0,0 +1,4 @@
+baz() ->
+ ok.
+
+-define(MACRO_DEFINING_A_FUNCTION,quux() -> ok).
diff --git a/lib/test_server/test/erl2html2_SUITE_data/include/header2.hrl b/lib/test_server/test/erl2html2_SUITE_data/include/header2.hrl
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/test_server/test/erl2html2_SUITE_data/include/header2.hrl
diff --git a/lib/test_server/test/erl2html2_SUITE_data/m1.erl b/lib/test_server/test/erl2html2_SUITE_data/m1.erl
new file mode 100644
index 0000000000..156f1d0a51
--- /dev/null
+++ b/lib/test_server/test/erl2html2_SUITE_data/m1.erl
@@ -0,0 +1,46 @@
+%% Comment with <html> code &amp; </html>
+%% and also some "quotes" and 'single quotes'
+
+-module(m1).
+
+-compile(export_all).
+
+-include("header1.hrl").
+-include("header2.hrl").
+
+-define(MACRO1,value).
+
+%%% Comment
+foo(x) ->
+ %% Comment
+ ok_x;
+foo(y) ->
+ %% Second clause
+ ok_y.
+
+'quoted_foo'() ->
+ ok.
+
+'quoted_foo_with_"_and_/'() ->
+ ok.
+
+'quoted_foo_with_(_and_)'() ->
+ ok.
+
+'quoted_foo_with_<_and_>'() ->
+ ok.
+
+bar() ->
+ do_something(),
+ok. % indentation error, OTP-9710
+
+%% Function inside macro definition
+?MACRO_DEFINING_A_FUNCTION.
+
+%% Two function one one line
+quuux() -> ok. quuuux() -> ok.
+
+%% do_something/0 does something
+do_something() ->
+ ?MACRO1.
+%% comments after last line
diff --git a/lib/test_server/test/test_server.cover b/lib/test_server/test/test_server.cover
index 5c59bab494..052415377d 100644
--- a/lib/test_server/test/test_server.cover
+++ b/lib/test_server/test/test_server.cover
@@ -1,22 +1 @@
{incl_app,test_server,details}.
-
-{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.
-{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_SUITE.erl b/lib/test_server/test/test_server_SUITE.erl
index a8532b08ab..fb82a87fd0 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 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -79,7 +79,8 @@ groups() ->
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_server_skip_SUITE, test_server_shuffle01_SUITE,
+ test_server_break_SUITE, test_server_cover_SUITE].
%%--------------------------------------------------------------------
@@ -92,45 +93,113 @@ 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).
+ run_test_server_tests("test_server_SUITE",
+ [{test_server_SUITE,skip_case7,"SKIPPED!"}],
+ 38, 1, 30, 19, 9, 1, 11, 2, 25, Config).
test_server_parallel01_SUITE(Config) ->
- run_test_server_tests("test_server_parallel01_SUITE", 37, 0, 19,
- 19, 0, 0, 0, 0, 37, 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).
+ 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).
+ 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).
+ 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("test_server_conf02_SUITE", [],
+ 26, 0, 12, 12, 0, 0, 0, 0, 26, Config).
+test_server_break_SUITE(Config) ->
+ run_test_server_tests("test_server_break_SUITE", [],
+ 8, 2, 6, 4, 0, 0, 0, 2, 6, Config).
-run_test_server_tests(SuiteName, NCases, NFail, NExpected, NSucc,
+test_server_cover_SUITE(Config) ->
+ case test_server:is_cover() of
+ true ->
+ {skip, "Cover already running"};
+ false ->
+ PrivDir = ?config(priv_dir,Config),
+
+ %% Test suite has two test cases
+ %% tc1 calls cover_helper:foo/0
+ %% tc2 calls cover_helper:bar/0
+ %% Each function in cover_helper is one line.
+ %%
+ %% First test run skips tc2, so only cover_helper:foo/0 is executed.
+ %% Cover file specifies to include cover_helper in this test run.
+ CoverFile1 = filename:join(PrivDir,"t1.cover"),
+ CoverSpec1 = {include,[cover_helper]},
+ file:write_file(CoverFile1,io_lib:format("~p.~n",[CoverSpec1])),
+ run_test_server_tests("test_server_cover_SUITE",
+ [{test_server_cover_SUITE,tc2,"SKIPPED!"}],
+ 4, 0, 2, 1, 1, 0, 1, 0, 3,
+ CoverFile1, Config),
+
+ %% Next test run skips tc1, so only cover_helper:bar/0 is executed.
+ %% Cover file specifies cross compilation of cover_helper
+ CoverFile2 = filename:join(PrivDir,"t2.cover"),
+ CoverSpec2 = {cross,[{t1,[cover_helper]}]},
+ file:write_file(CoverFile2,io_lib:format("~p.~n",[CoverSpec2])),
+ run_test_server_tests("test_server_cover_SUITE",
+ [{test_server_cover_SUITE,tc1,"SKIPPED!"}],
+ 4, 0, 2, 1, 1, 0, 1, 0, 3, CoverFile2, Config),
+
+ %% Cross cover analyse
+ WorkDir = ?config(work_dir,Config),
+ WC = filename:join([WorkDir,"test_server_cover_SUITE.logs","run.*"]),
+ [D2,D1|_] = lists:reverse(lists:sort(filelib:wildcard(WC))),
+ TagDirs = [{t1,D1},{t2,D2}],
+ test_server_ctrl:cross_cover_analyse(details,TagDirs),
+
+ %% Check that cover log shows only what is really included
+ %% in the test and cross cover log show the accumulated
+ %% result.
+ {ok,Cover1} = file:read_file(filename:join(D1,"cover.log")),
+ [{cover_helper,{1,1,_}}] = binary_to_term(Cover1),
+ {ok,Cover2} = file:read_file(filename:join(D2,"cover.log")),
+ [] = binary_to_term(Cover2),
+ {ok,Cross} = file:read_file(filename:join(D1,"cross_cover.log")),
+ [{cover_helper,{2,0,_}}] = binary_to_term(Cross),
+ ok
+ end.
+
+
+run_test_server_tests(SuiteName, Skip, NCases, NFail, NExpected, NSucc,
NUsrSkip, NAutoSkip,
NActualSkip, NActualFail, NActualSucc, Config) ->
+ run_test_server_tests(SuiteName, Skip, NCases, NFail, NExpected, NSucc,
+ NUsrSkip, NAutoSkip,
+ NActualSkip, NActualFail, NActualSucc, false, Config).
- ct:log("See test case log files under:~n~p~n",
- [filename:join([proplists:get_value(priv_dir, Config),
- SuiteName++".logs"])]),
+run_test_server_tests(SuiteName, Skip, NCases, NFail, NExpected, NSucc,
+ NUsrSkip, NAutoSkip,
+ NActualSkip, NActualFail, NActualSucc, Cover, Config) ->
+
+ WorkDir = proplists:get_value(work_dir, Config),
+ ct:log("<a href=\"file://~s\">Test case log files</a>\n",
+ [filename:join(WorkDir, SuiteName++".logs")]),
Node = proplists:get_value(node, Config),
{ok,_Pid} = rpc:call(Node,test_server_ctrl, start, []),
+ case Cover of
+ false ->
+ ok;
+ _ ->
+ rpc:call(Node,test_server_ctrl,cover,[Cover,details])
+ end,
rpc:call(Node,
test_server_ctrl,add_dir_with_skip,
[SuiteName,
[proplists:get_value(data_dir,Config)],SuiteName,
- [{test_server_SUITE,skip_case7,"SKIPPED!"}]]),
+ Skip]),
until(fun() ->
rpc:call(Node,test_server_ctrl,jobs,[]) =:= []
@@ -138,17 +207,18 @@ run_test_server_tests(SuiteName, NCases, NFail, NExpected, NSucc,
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"])))),
+ {ok,Data} = test_server_test_lib:parse_suite(
+ lists:last(
+ lists:sort(
+ filelib:wildcard(
+ filename:join([WorkDir,SuiteName++".logs",
+ "run*","suite.log"]))))),
+ check([{"Number of cases",NCases,Data#suite.n_cases},
+ {"Number failed",NFail,Data#suite.n_cases_failed},
+ {"Number expected",NExpected,Data#suite.n_cases_expected},
+ {"Number successful",NSucc,Data#suite.n_cases_succ},
+ {"Number user skipped",NUsrSkip,Data#suite.n_cases_user_skip},
+ {"Number auto skipped",NAutoSkip,Data#suite.n_cases_auto_skip}], ok),
{NActualSkip,NActualFail,NActualSucc} =
lists:foldl(fun(#tc{ result = skip },{S,F,Su}) ->
{S+1,F,Su};
@@ -156,9 +226,18 @@ run_test_server_tests(SuiteName, NCases, NFail, NExpected, NSucc,
{S,F,Su+1};
(#tc{ result = failed },{S,F,Su}) ->
{S,F+1,Su}
- end,{0,0,0},Cases),
+ end,{0,0,0},Data#suite.cases),
Data.
+check([{Str,Same,Same}|T], Status) ->
+ io:format("~s: ~p\n", [Str,Same]),
+ check(T, Status);
+check([{Str,Expected,Actual}|T], _) ->
+ io:format("~s: expected ~p, actual ~p\n", [Str,Expected,Actual]),
+ check(T, error);
+check([], ok) -> ok;
+check([], error) -> ?t:fail().
+
until(Fun) ->
case Fun() of
true ->
diff --git a/lib/test_server/test/test_server_SUITE_data/Makefile.src b/lib/test_server/test/test_server_SUITE_data/Makefile.src
index 332b855df6..c770627f04 100644
--- a/lib/test_server/test/test_server_SUITE_data/Makefile.src
+++ b/lib/test_server/test/test_server_SUITE_data/Makefile.src
@@ -4,4 +4,7 @@ all:
erlc test_server_conf01_SUITE.erl
erlc test_server_shuffle01_SUITE.erl
erlc test_server_conf02_SUITE.erl
- erlc test_server_skip_SUITE.erl \ No newline at end of file
+ erlc test_server_skip_SUITE.erl
+ erlc test_server_break_SUITE.erl
+ erlc test_server_cover_SUITE.erl
+ erlc +debug_info test_server_cover_SUITE_data/cover_helper.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
index dfcdff0c3e..fc2adcd651 100644
--- 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
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -34,7 +34,7 @@
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,
+ skip_case8/1, skip_case9/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,
@@ -47,7 +47,7 @@ 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,
+ commercial,
{conf, conf_init, [check_new_conf], conf_cleanup},
check_old_conf,
{conf, conf_init_fail,[conf_member_skip],conf_cleanup_skip},
@@ -386,50 +386,6 @@ skip_case9(Config) when is_list(Config) ->
%% 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].
@@ -477,7 +433,7 @@ start_stop_node(Config) when is_list(Config) ->
?t:comment("WARNING: Node started with {wait,false}"
" is up faster than expected...");
false ->
- wait_for_node(Node4,0),
+ test_server:wait_for_node(Node4),
true = lists:member(Node4,nodes())
end,
@@ -494,16 +450,6 @@ start_stop_node(Config) when is_list(Config) ->
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) ->
diff --git a/lib/test_server/test/test_server_SUITE_data/test_server_break_SUITE.erl b/lib/test_server/test/test_server_SUITE_data/test_server_break_SUITE.erl
new file mode 100644
index 0000000000..d9f009679a
--- /dev/null
+++ b/lib/test_server/test/test_server_SUITE_data/test_server_break_SUITE.erl
@@ -0,0 +1,148 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, 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_break_SUITE).
+
+-export([all/1, init_per_suite/1, end_per_suite/1]).
+-export([init_per_testcase/2, end_per_testcase/2]).
+-export([break_in_init_tc/1,
+ break_in_tc/1,
+ break_in_end_tc/1,
+ break_in_end_tc_after_fail/1,
+ break_in_end_tc_after_abort/1,
+ check_all_breaks/1]).
+
+-include_lib("test_server/include/test_server.hrl").
+
+all(suite) ->
+ [break_in_init_tc,
+ break_in_tc,
+ break_in_end_tc,
+ break_in_end_tc_after_fail,
+ break_in_end_tc_after_abort,
+ check_all_breaks]. %must be the last test - checks result of previous tests
+
+init_per_suite(Config) ->
+ spawn(fun break_and_continue_sup/0),
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_testcase(Case,Config) when Case==break_in_init_tc ->
+ Config1 = init_timetrap(500,Config),
+ break_and_check(Case),
+ Config1;
+init_per_testcase(Case,Config) when Case==check_all_breaks ->
+ init_timetrap({seconds,20},Config);
+init_per_testcase(_Case,Config) ->
+ init_timetrap(500,Config).
+
+init_timetrap(T,Config) ->
+ Dog = ?t:timetrap(T),
+ [{watchdog, Dog}|Config].
+
+end_per_testcase(Case,Config) when Case==break_in_end_tc;
+ Case==break_in_end_tc_after_fail;
+ Case==break_in_end_tc_after_abort ->
+ break_and_check(Case),
+ cancel_timetrap(Config);
+end_per_testcase(_Case,Config) ->
+ cancel_timetrap(Config).
+
+cancel_timetrap(Config) ->
+ Dog=?config(watchdog, Config),
+ ?t:timetrap_cancel(Dog),
+ ok.
+
+
+%%%-----------------------------------------------------------------
+%%% Test cases
+
+break_in_init_tc(Config) when is_list(Config) ->
+ ok.
+
+break_in_tc(Config) when is_list(Config) ->
+ break_and_check(break_in_tc),
+ ok.
+
+break_in_end_tc(Config) when is_list(Config) ->
+ ok.
+
+break_in_end_tc_after_fail(Config) when is_list(Config) ->
+ ?t:fail(test_case_should_fail).
+
+break_in_end_tc_after_abort(Config) when is_list(Config) ->
+ ?t:adjusted_sleep(2000). % will cause a timetrap timeout
+
+%% This test case checks that all breaks in previous test cases was
+%% also continued, and that the break lasted as long as expected.
+%% The reason for this is that some of the breaks above are in
+%% end_per_testcase, and failures there will only produce a warning,
+%% not an error - so this is to catch the error for real.
+check_all_breaks(Config) when is_list(Config) ->
+ break_and_continue_sup ! {done,self()},
+ receive {Breaks,Continued} ->
+ check_all_breaks(Breaks,Continued)
+ end.
+%%%-----------------------------------------------------------------
+%%% Internal functions
+
+
+check_all_breaks([{From,Case,T,Start}|Breaks],[{From,End}|Continued]) ->
+ Diff = timer:now_diff(End,Start),
+ DiffSec = round(Diff/1000000),
+ TSec = round(T/1000000),
+ if DiffSec==TSec ->
+ ?t:format("Break in ~p successfully continued after ~p second(s)~n",
+ [Case,DiffSec]),
+ check_all_breaks(Breaks,Continued);
+ true ->
+ ?t:format("Faulty duration of break in ~p: continued after ~p second(s)~n",
+ [Case,DiffSec]),
+ ?t:fail({faulty_diff,Case,DiffSec,TSec})
+ end;
+check_all_breaks([],[]) ->
+ ok;
+check_all_breaks(Breaks,Continued) ->
+ %% This is probably a case of a missing continue - i.e. a break
+ %% has been started, but it was never continued.
+ ?t:fail({no_match_in_breaks_and_continued,Breaks,Continued}).
+
+break_and_check(Case) ->
+ break_and_continue_sup ! {break,Case,1000,self()},
+ ?t:break(atom_to_list(Case)),
+ break_and_continue_sup ! {continued,self()},
+ ok.
+
+break_and_continue_sup() ->
+ register(break_and_continue_sup,self()),
+ break_and_continue_loop([],[]).
+
+break_and_continue_loop(Breaks,Continued) ->
+ receive
+ {break,Case,T,From} ->
+ Start = now(),
+ {RealT,_} = timer:tc(?t,adjusted_sleep,[T]),
+ ?t:continue(),
+ break_and_continue_loop([{From,Case,RealT,Start}|Breaks],Continued);
+ {continued,From} ->
+ break_and_continue_loop(Breaks,[{From,now()}|Continued]);
+ {done,From} ->
+ From ! {lists:reverse(Breaks),lists:reverse(Continued)}
+ end.
diff --git a/lib/test_server/test/test_server_SUITE_data/test_server_cover_SUITE.erl b/lib/test_server/test/test_server_SUITE_data/test_server_cover_SUITE.erl
new file mode 100644
index 0000000000..b1ae70a302
--- /dev/null
+++ b/lib/test_server/test/test_server_SUITE_data/test_server_cover_SUITE.erl
@@ -0,0 +1,58 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, 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_cover_SUITE).
+
+-export([all/1, init_per_suite/1, end_per_suite/1]).
+-export([init_per_testcase/2, end_per_testcase/2]).
+-export([tc1/1, tc2/1]).
+
+-include_lib("test_server/include/test_server.hrl").
+
+all(suite) ->
+ [tc1,tc2].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_testcase(_Case,Config) ->
+ Dog = ?t:timetrap({minutes,10}),
+ [{watchdog, Dog}|Config].
+
+end_per_testcase(_Case,Config) ->
+ Dog=?config(watchdog, Config),
+ ?t:timetrap_cancel(Dog),
+ ok.
+
+
+%%%-----------------------------------------------------------------
+%%% Test cases
+tc1(Config) when is_list(Config) ->
+ cover_helper:foo(),
+ ok.
+
+tc2(Config) when is_list(Config) ->
+ cover_helper:bar(),
+ ok.
+
+%%%-----------------------------------------------------------------
+%%% Internal functions
+
diff --git a/lib/test_server/test/test_server_SUITE_data/test_server_cover_SUITE_data/cover_helper.erl b/lib/test_server/test/test_server_SUITE_data/test_server_cover_SUITE_data/cover_helper.erl
new file mode 100644
index 0000000000..6c74eb4e8a
--- /dev/null
+++ b/lib/test_server/test/test_server_SUITE_data/test_server_cover_SUITE_data/cover_helper.erl
@@ -0,0 +1,4 @@
+-module(cover_helper).
+-compile(export_all).
+foo() -> ok.
+bar() -> ok.
diff --git a/lib/test_server/test/test_server_line_SUITE.erl b/lib/test_server/test/test_server_line_SUITE.erl
deleted file mode 100644
index 0aba54f6b5..0000000000
--- a/lib/test_server/test/test_server_line_SUITE.erl
+++ /dev/null
@@ -1,131 +0,0 @@
-%%
-%% %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%
-%%
-
-%%%------------------------------------------------------------------
-%%% Test Server self test.
-%%%------------------------------------------------------------------
--module(test_server_line_SUITE).
--include_lib("test_server/include/test_server.hrl").
-
--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]).
-
-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].
-
-end_per_testcase(_Case, Config) ->
- ?line test_server_line:clear(),
- Dog=?config(watchdog, Config),
- ?t:timetrap_cancel(Dog),
- ok.
-
-parse_transform(suite) -> [];
-parse_transform(doc) -> [];
-parse_transform(Config) when is_list(Config) ->
- ?line DataDir = ?config(data_dir,Config),
- code:add_pathz(DataDir),
-
- ?line ok = parse_transform_test:excluded(),
- ?line [] = test_server_line:get_lines(),
-
- ?line test_server_line:clear(),
- ?line ok = parse_transform_test:func(),
-
- ?line [{parse_transform_test,func4,58},
- {parse_transform_test,func,49},
- {parse_transform_test,func3,56},
- {parse_transform_test,func,39},
- {parse_transform_test,func2,54},
- {parse_transform_test,func,36},
- {parse_transform_test,func1,52},
- {parse_transform_test,func,35}] = test_server_line:get_lines(),
-
- code:del_path(DataDir),
- ok.
-
-lines(suite) -> [];
-lines(doc) -> ["Test parse transform for collection line numbers"];
-lines(Config) when is_list(Config) ->
- ?line L0 = [{mod,func,1},{mod,func,2},{mod,func,3},
- {m,f,4},{m,f,5},{m,f,6},
- {mo,fu,7},{mo,fu,8},{mo,fu,9}],
- ?line LL = string:copies(L0, 1000),
- ?line T1 = erlang:now(),
- ?line lists:foreach(fun ({M,F,L}) ->
- test_server_line:'$test_server_line'(M, F, L)
- end, LL),
- ?line T2 = erlang:now(),
- ?line Long = test_server_line:get_lines(),
- ?line test_server_line:clear(),
-
- ?line T3 = erlang:now(),
- ?line lists:foreach(fun ({M,F,L}) ->
- test_server_line:'$test_server_lineQ'(M, F, L)
- end, LL),
- ?line T4 = erlang:now(),
- ?line LongQ = test_server_line:get_lines(),
-
- ?line io:format("'$test_server_line': ~f~n'$test_server_lineQ': ~f~n",
- [timer:now_diff(T2, T1)/1000, timer:now_diff(T4, T3)/1000]),
- ?line io:format("'$test_server_line' result long:~p~n", [Long]),
- ?line io:format("'$test_server_lineQ' result long:~p~n", [LongQ]),
-
- if Long =:= LongQ ->
- ?line ok;
- true ->
- ?line ?t:fail("The two methods did not produce same result for"
- " long lists of lines")
- end,
-
- ?line test_server_line:clear(),
- ?line lists:foreach(fun ({M,F,L}) ->
- test_server_line:'$test_server_line'(M, F, L)
- end, L0),
- ?line Short = test_server_line:get_lines(),
- ?line test_server_line:clear(),
- ?line lists:foreach(fun ({M,F,L}) ->
- test_server_line:'$test_server_lineQ'(M, F, L)
- end, L0),
- ?line ShortQ = test_server_line:get_lines(),
-
- ?line io:format("'$test_server_line' result short:~p~n", [Short]),
- ?line io:format("'$test_server_lineQ' result short:~p~n", [ShortQ]),
-
- if Short =:= ShortQ ->
- ?line ok;
- true ->
- ?line ?t:fail("The two methods did not produce same result for"
- " shot lists of lines\n")
- end.
diff --git a/lib/test_server/test/test_server_line_SUITE_data/Makefile.src b/lib/test_server/test/test_server_line_SUITE_data/Makefile.src
deleted file mode 100644
index a077648934..0000000000
--- a/lib/test_server/test/test_server_line_SUITE_data/Makefile.src
+++ /dev/null
@@ -1,6 +0,0 @@
-EFLAGS=+debug_info -pa ../../test_server -I../../test_server
-
-all: parse_transform_test.@EMULATOR@
-
-parse_transform_test.@EMULATOR@: parse_transform_test.erl
- erlc $(EFLAGS) parse_transform_test.erl
diff --git a/lib/test_server/test/test_server_test_lib.erl b/lib/test_server/test/test_server_test_lib.erl
index 5ca24f3df7..d466aa0110 100644
--- a/lib/test_server/test/test_server_test_lib.erl
+++ b/lib/test_server/test/test_server_test_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -54,9 +54,13 @@ start_slave(Config,_Level) ->
ok
end,
DataDir = proplists:get_value(data_dir, Config),
- PrivDir = proplists:get_value(priv_dir, Config),
+ %% We would normally use priv_dir for temporary data,
+ %% but the pathnames gets too long on Windows.
+ %% Until the run-time system can support long pathnames,
+ %% use the data dir.
+ WorkDir = DataDir,
- %% PrivDir as well as directory of Test Server suites
+ %% WorkDir 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)),
@@ -64,7 +68,7 @@ start_slave(Config,_Level) ->
undefined -> [];
Ds -> Ds
end,
- PathDirs = [PrivDir,TSDir | AddPathDirs],
+ PathDirs = [WorkDir,TSDir | AddPathDirs],
[true = rpc:call(Node, code, add_patha, [D]) || D <- PathDirs],
io:format("Dirs added to code path (on ~w):~n",
[Node]),
@@ -73,15 +77,27 @@ start_slave(Config,_Level) ->
true = rpc:call(Node, os, putenv,
["TEST_SERVER_FRAMEWORK", "undefined"]),
- ok = rpc:call(Node, file, set_cwd, [PrivDir]),
- [{node,Node} | Config]
+ ok = rpc:call(Node, file, set_cwd, [WorkDir]),
+ [{node,Node}, {work_dir,WorkDir} | Config]
end.
post_end_per_testcase(_TC, Config, Return, State) ->
Node = proplists:get_value(node, Config),
- cover:stop(Node),
+ Cover = test_server:is_cover(),
+ if Cover-> cover:flush(Node);
+ true -> ok
+ end,
+ erlang:monitor_node(Node, true),
slave:stop(Node),
-
+ receive
+ {nodedown, Node} ->
+ if Cover -> cover:stop(Node);
+ true -> ok
+ end
+ after 5000 ->
+ erlang:monitor_node(Node, false),
+ receive {nodedown, Node} -> ok after 0 -> ok end %flush
+ end,
{Return, State}.
%% Parse an .suite log file
diff --git a/lib/test_server/vsn.mk b/lib/test_server/vsn.mk
index a1f4559083..b956ebb2b3 100644
--- a/lib/test_server/vsn.mk
+++ b/lib/test_server/vsn.mk
@@ -1 +1 @@
-TEST_SERVER_VSN = 3.5.1
+TEST_SERVER_VSN = 3.5.3
diff --git a/lib/toolbar/doc/src/Makefile b/lib/toolbar/doc/src/Makefile
index af907859d7..2239421556 100644
--- a/lib/toolbar/doc/src/Makefile
+++ b/lib/toolbar/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2009. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/toolbar/doc/src/notes.xml b/lib/toolbar/doc/src/notes.xml
index b901e6c8ff..f3ed41bd82 100644
--- a/lib/toolbar/doc/src/notes.xml
+++ b/lib/toolbar/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2004</year><year>2011</year>
+ <year>2004</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
diff --git a/lib/toolbar/src/Makefile b/lib/toolbar/src/Makefile
index 91ce305e26..a24846976b 100644
--- a/lib/toolbar/src/Makefile
+++ b/lib/toolbar/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2009. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -71,10 +71,10 @@ clean:
rm -f errs core *~
$(APP_TARGET): $(APP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
docs:
diff --git a/lib/tools/c_src/Makefile.in b/lib/tools/c_src/Makefile.in
index 0382d3228d..aea5686ae9 100644
--- a/lib/tools/c_src/Makefile.in
+++ b/lib/tools/c_src/Makefile.in
@@ -17,6 +17,7 @@
# %CopyrightEnd%
#
+include $(ERL_TOP)/make/output.mk
include $(ERL_TOP)/make/target.mk
include $(ERL_TOP)/erts/include/internal/$(TARGET)/ethread.mk
@@ -150,7 +151,7 @@ _create_dirs := $(shell mkdir -p $(CREATE_DIRS))
all: $(PROGS) $(DRIVERS)
$(ERTS_LIB):
- cd $(ERL_TOP)/erts/lib_src && $(MAKE) $(TYPE)
+ $(make_verbose)cd $(ERL_TOP)/erts/lib_src && $(MAKE) $(TYPE)
docs:
@@ -167,7 +168,7 @@ clean:
#
$(EMEM_OBJ_DIR)/%.o: %.c
- $(CC) $(EMEM_CFLAGS) -o $@ -c $<
+ $(V_CC) $(EMEM_CFLAGS) -o $@ -c $<
#
# Driver targets
@@ -178,7 +179,7 @@ $(EMEM_OBJ_DIR)/%.o: %.c
#
$(BIN_DIR)/emem$(TYPEMARKER)@EXEEXT@: $(EMEM_OBJS) $(ERTS_LIB)
- $(PRE_LD) $(LD) $(EMEM_LDFLAGS) -o $@ $(EMEM_OBJS) $(EMEM_LIBS)
+ $(ld_verbose)$(PRE_LD) $(LD) $(EMEM_LDFLAGS) -o $@ $(EMEM_OBJS) $(EMEM_LIBS)
#
# Release targets
@@ -221,7 +222,8 @@ SED_DEPEND=sed '$(SED_REPL_OBJ_DIR);$(SED_REPL_TT_DIR);$(SED_REPL_TARGET);$(SED_
DEPEND_MK=depend.mk
dep depend:
- @echo "Generating dependency file $(DEPEND_MK)..."
+ [ $(v_p) == 0 ] && echo " GEN "$(DEPEND_MK)
+ $(V_colon)@echo "Generating dependency file $(DEPEND_MK)..."
@echo "# Generated dependency rules." > $(DEPEND_MK);
@echo "# Do *not* edit this file; instead, run 'make depend'." \
>> $(DEPEND_MK);
diff --git a/lib/tools/doc/src/Makefile b/lib/tools/doc/src/Makefile
index a76ce98ad6..e3a2d64041 100644
--- a/lib/tools/doc/src/Makefile
+++ b/lib/tools/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2010. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/tools/doc/src/cover.xml b/lib/tools/doc/src/cover.xml
index 683acc025d..a2444ec947 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>2011</year>
+ <year>2012</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -104,6 +104,13 @@
remove nodes. The same Cover compiled code will be loaded on each
node, and analysis will collect and sum up coverage data results
from all nodes.</p>
+ <p>To only collect data from remote nodes without stopping
+ <c>cover</c> on those nodes, use <c>cover:flush/1</c></p>
+ <p>If the connection to a remote node goes down, the main node
+ will mark it as lost. If the node comes back it will be added
+ again. If the remote node was alive during the disconnected
+ periode, cover data from before and during this periode will be
+ included in the analysis.</p>
</description>
<funcs>
<func>
@@ -477,6 +484,17 @@
remote nodes is fetched and stored on the main node.</p>
</desc>
</func>
+ <func>
+ <name>flush(Nodes) -> ok | {error,not_main_node}</name>
+ <fsummary>Collect cover data from remote nodes.</fsummary>
+ <type>
+ <v>Nodes = [atom()]</v>
+ </type>
+ <desc>
+ <p>Fetch data from the Cover database on the remote nodes and
+ stored on the main node.</p>
+ </desc>
+ </func>
</funcs>
<section>
diff --git a/lib/tools/doc/src/eprof.xml b/lib/tools/doc/src/eprof.xml
index 1c5e38109b..82eb8dd284 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>2011</year>
+ <year>1996</year><year>2012</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 f4da132102..5d7bd464ce 100644
--- a/lib/tools/doc/src/notes.xml
+++ b/lib/tools/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2004</year><year>2011</year>
+ <year>2004</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -30,6 +30,69 @@
</header>
<p>This document describes the changes made to the Tools application.</p>
+<section><title>Tools 2.6.8</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The last tuple fun call has been removed from fprof.</p>
+ <p>
+ Own Id: OTP-10091 Aux Id: seq12067 </p>
+ </item>
+ <item>
+ <p>
+ Fix indentation of record fields in Emacs (Thanks to
+ Tomas Abrahamsson)</p>
+ <p>
+ Own Id: OTP-10120</p>
+ </item>
+ <item>
+ <p>
+ Documentation fixes (Thanks to Ricardo Catalinas Jim�nez
+ )</p>
+ <p>
+ Own Id: OTP-10121</p>
+ </item>
+ <item>
+ <p>
+ Remove Erlang-specific compilation error regexp in
+ erlang.el</p>
+ <p>
+ Own Id: OTP-10168</p>
+ </item>
+ <item>
+ <p>
+ Fix highlighting of atoms ending with a dollar sign</p>
+ <p>
+ Like this: 'atom$'. In that example, the last single
+ quote should be recognised as ending the atom. This needs
+ a font-lock workaround similar to the one for strings.
+ Thanks to Magnus Henoch</p>
+ <p>
+ Own Id: OTP-10178</p>
+ </item>
+ <item>
+ <p> Xref now accepts filenames with character codes
+ greater than 126. (Thanks to Emile Joubert for reporting
+ the issue.) </p>
+ <p>
+ Own Id: OTP-10192</p>
+ </item>
+ <item>
+ <p>
+ Add test_indentation target to lib/tools/emacs/Makefile</p>
+ <p>
+ Automatically indent test.erl.orig, save to test.erl, and
+ compare to test.erl.intended. Thanks to Magnus Henoch.</p>
+ <p>
+ Own Id: OTP-10226</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Tools 2.6.7</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/tools/doc/src/xref.xml b/lib/tools/doc/src/xref.xml
index 17de66bb22..9706ae6746 100644
--- a/lib/tools/doc/src/xref.xml
+++ b/lib/tools/doc/src/xref.xml
@@ -1453,7 +1453,7 @@ Evaluates a predefined analysis.
Note however that the code path will be traversed once for
each used <seealso marker="#library_module">library module</seealso> while setting up module data.
On the other hand, if there are only a few modules that are
- used by not analyzed, using <c>code_path</c> may be faster
+ used but not analyzed, using <c>code_path</c> may be faster
than setting the library path to <c>code:get_path()</c>.
</p>
<p>If the library path is set to <c>code_path</c>, the set of
diff --git a/lib/tools/doc/src/xref_chapter.xml b/lib/tools/doc/src/xref_chapter.xml
index 39c5545af9..566776eab0 100644
--- a/lib/tools/doc/src/xref_chapter.xml
+++ b/lib/tools/doc/src/xref_chapter.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2000</year><year>2009</year>
+ <year>2000</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -301,7 +301,7 @@
and <c>|||</c>) are the only operators that accept both
representations. This means that in order to analyze indirect
calls using restriction, the <c>closure</c> operator (which creates the
- <c>digraph</c> representation of graphs) has to been
+ <c>digraph</c> representation of graphs) has to be
applied explicitly.
</p>
<p>As an example of analyzing indirect calls, the following Erlang
diff --git a/lib/tools/emacs/Makefile b/lib/tools/emacs/Makefile
index e5ed56b412..69946be24a 100644
--- a/lib/tools/emacs/Makefile
+++ b/lib/tools/emacs/Makefile
@@ -86,3 +86,19 @@ release_docs_spec: docs
$(INSTALL_DATA) $(MAN_FILES) "$(RELEASE_PATH)/man/man3"
endif
endif
+
+EMACS ?= emacs
+
+test_indentation:
+ @rm -f test.erl
+ @rm -f test_indent.el
+ @echo '(load "erlang-start")' >> test_indent.el
+ @echo '(find-file "test.erl.orig")' >> test_indent.el
+ @echo "(require 'cl) ; required with Emacs < 23 for ignore-errors" >> test_indent.el
+ @echo '(erlang-mode)' >> test_indent.el
+ @echo '(toggle-debug-on-error)' >> test_indent.el
+ @echo '(erlang-indent-current-buffer)' >> test_indent.el
+ @echo '(write-file "test.erl")' >> test_indent.el
+ $(EMACS) --batch -Q -L . -l test_indent.el
+ diff -u test.erl.indented test.erl
+ @echo "No differences between expected and actual indentation"
diff --git a/lib/tools/emacs/erlang-pkg.el b/lib/tools/emacs/erlang-pkg.el
new file mode 100644
index 0000000000..decc696e21
--- /dev/null
+++ b/lib/tools/emacs/erlang-pkg.el
@@ -0,0 +1,3 @@
+(define-package "erlang" "2.7.0"
+ "Erlang major mode"
+ '((flymake-mode "0.4.6")))
diff --git a/lib/tools/emacs/erlang.el b/lib/tools/emacs/erlang.el
index 2f6c7f554e..2967acf310 100644
--- a/lib/tools/emacs/erlang.el
+++ b/lib/tools/emacs/erlang.el
@@ -1,7 +1,13 @@
-;; erlang.el --- Major modes for editing and running Erlang
+;;; erlang.el --- Major modes for editing and running Erlang
+
+;; Copyright (C) 2004 Free Software Foundation, Inc.
+;; Author: Anders Lindgren
+;; Keywords: erlang, languages, processes
+;; Date: 2011-12-11
+
;; %CopyrightBegin%
;;
-;; Copyright Ericsson AB 1996-2011. All Rights Reserved.
+;; Copyright Ericsson AB 1996-2012. All Rights Reserved.
;;
;; The contents of this file are subject to the Erlang Public License,
;; Version 1.1, (the "License"); you may not use this file except in
@@ -15,10 +21,7 @@
;; under the License.
;;
;; %CopyrightEnd%
-;;
-;; Copyright (C) 2004 Free Software Foundation, Inc.
-;; Author: Anders Lindgren
-;; Keywords: erlang, languages, processes
+;;
;; Lars Thors�n's modifications of 2000-06-07 included.
;; The original version of this package was written by Robert Virding.
@@ -483,10 +486,6 @@ function.")
"*Non-nil means TAB in Erlang mode should always re-indent the current line,
regardless of where in the line point is when the TAB command is used.")
-(defvar erlang-error-regexp-alist
- '(("^\\([^:( \t\n]+\\)[:(][ \t]*\\([0-9]+\\)[:) \t]" . (1 2)))
- "*Patterns for matching Erlang errors.")
-
(defvar erlang-man-inhibit (eq system-type 'windows-nt)
"Inhibit the creation of the Erlang Manual Pages menu.
@@ -998,6 +997,22 @@ behaviour.")
1 'font-lock-function-name-face t))
"Font lock keyword highlighting a function header.")
+(defface erlang-font-lock-exported-function-name-face
+ '((default (:inherit font-lock-function-name-face)))
+ "Face used for highlighting exported functions.")
+
+(defvar erlang-font-lock-exported-function-name-face
+ 'erlang-font-lock-exported-function-name-face)
+
+(defvar erlang-inhibit-exported-function-name-face nil
+ "Inhibit separate face for exported functions")
+
+(defvar erlang-font-lock-keywords-exported-function-header
+ (list
+ (list #'erlang-match-next-exported-function
+ 1 'erlang-font-lock-exported-function-name-face t))
+ "Font lock keyword highlighting an exported function header.")
+
(defvar erlang-font-lock-keywords-int-bifs
(list
(list (concat erlang-int-bif-regexp "\\s-*(")
@@ -1133,7 +1148,8 @@ There exists three levels of Font Lock keywords for Erlang:
`erlang-font-lock-keywords-1' - Function headers and reserved keywords.
`erlang-font-lock-keywords-2' - Bifs, guards and `single quotes'.
`erlang-font-lock-keywords-3' - Variables, macros and records.
- `erlang-font-lock-keywords-4' - Function names, Funs, LCs (not Atoms)
+ `erlang-font-lock-keywords-4' - Exported functions, Function names,
+ Funs, LCs (not Atoms).
To use a specific level, please set the variable
`font-lock-maximum-decoration' to the appropriate level. Note that the
@@ -1175,6 +1191,7 @@ Example:
(defvar erlang-font-lock-keywords-4
(append erlang-font-lock-keywords-3
+ erlang-font-lock-keywords-exported-function-header
erlang-font-lock-keywords-int-function-calls
erlang-font-lock-keywords-ext-function-calls
erlang-font-lock-keywords-fun-n
@@ -1327,7 +1344,6 @@ Other commands:
(erlang-menu-init)
(erlang-mode-variables)
(erlang-check-module-name-init)
- (erlang-add-compilation-alist erlang-error-regexp-alist)
(erlang-man-init)
(erlang-tags-init)
(erlang-font-lock-init)
@@ -1443,31 +1459,6 @@ Other commands:
(set (make-local-variable 'outline-level) (lambda () 1))
(set (make-local-variable 'add-log-current-defun-function)
'erlang-current-defun))
-
-
-;; Compilation.
-;;
-;; The following code is compatible with the standard package `compilation',
-;; making it possible to go to errors using `erlang-next-error' (or just
-;; `next-error' in Emacs 21).
-;;
-;; The normal `compile' command works of course. For best result, please
-;; execute `make' with the `-w' flag.
-;;
-;; Please see the variables named `compiling-..' above.
-
-(defun erlang-add-compilation-alist (alist)
- (require 'compile)
- (cond ((boundp 'compilation-error-regexp-alist) ; Emacs 19
- (while alist
- (or (assoc (car (car alist)) compilation-error-regexp-alist)
- (setq compilation-error-regexp-alist
- (cons (car alist) compilation-error-regexp-alist)))
- (setq alist (cdr alist))))
- ((boundp 'compilation-error-regexp)
- ;; Emacs 18, Only one regexp is allowed.
- (funcall (symbol-function 'set)
- 'compilation-error-regexp (car (car alist))))))
(defun erlang-font-lock-init ()
"Initialize Font Lock for Erlang mode."
@@ -1516,7 +1507,7 @@ Other commands:
;; 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
+ ;; And a "string" consists of 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
@@ -1525,6 +1516,8 @@ Other commands:
;; know whether matching started inside a string: limiting
;; search to a single line keeps things sane.
. (("\\(?:^\\|[^$]\\)\"\\(?:[^\"\n]\\|\\\\\"\\)*\\(\\$\\)\"" 1 "w")
+ ;; Likewise for atoms
+ ("\\(?:^\\|[^$]\\)'\\(?:[^'\n]\\|\\\\'\\)*\\(\\$\\)'" 1 "w")
;; And the dollar sign in $\" escapes two characters, not
;; just one.
("\\(\\$\\)\\\\\\\"" 1 "'"))))))
@@ -3687,6 +3680,38 @@ In the future the list may contain more elements."
str)
(store-match-data md))))
+(defun erlang-match-next-exported-function (max)
+ "Returns non-nil if there is an exported function in the current
+buffer between point and MAX."
+ (block nil
+ (while (and (not erlang-inhibit-exported-function-name-face)
+ (erlang-match-next-function max))
+ (when (erlang-last-match-exported-p)
+ (return (match-data))))))
+
+(defun erlang-match-next-function (max)
+ "Searches forward in current buffer for the next erlang function,
+bounded by position MAX."
+ (re-search-forward erlang-defun-prompt-regexp max 'move-point))
+
+(defun erlang-last-match-exported-p ()
+ "Returns non-nil if match-data describes the name and arity of an
+exported function."
+ (save-excursion
+ (save-match-data
+ (goto-char (match-beginning 1))
+ (erlang-function-exported-p
+ (erlang-remove-quotes (erlang-get-function-name))
+ (erlang-get-function-arity)))))
+
+(defun erlang-function-exported-p (name arity)
+ "Returns non-nil if function of name and arity is exported in current buffer."
+ (save-excursion
+ (let* ((old-match-data (match-data))
+ (exports (erlang-get-export)))
+ (store-match-data old-match-data)
+ (member (cons name arity) exports))))
+
;;; Check module name
@@ -4894,7 +4919,6 @@ The following special commands are available:
(set (make-local-variable 'compilation-old-error-list) nil))
;; Needed when compiling directly from the Erlang shell.
(setq compilation-last-buffer (current-buffer))
- (erlang-add-compilation-alist erlang-error-regexp-alist)
(setq comint-prompt-regexp "^[^>=]*> *")
(setq comint-eol-on-send t)
(setq comint-input-ignoredups t)
diff --git a/lib/tools/emacs/test.erl.indented b/lib/tools/emacs/test.erl.indented
index e0593c6522..6c9343f6cb 100644
--- a/lib/tools/emacs/test.erl.indented
+++ b/lib/tools/emacs/test.erl.indented
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -215,6 +215,11 @@ highlighting(X) % Function definitions should be highlighted
"char $in string", atom,
+ 'atom$', atom, 'atom$', atom,
+ 'atom\$', atom,
+
+ 'char $in atom', atom,
+
$[, ${, $\\, atom,
?MACRO_1,
?MACRO_2(foo),
diff --git a/lib/tools/emacs/test.erl.orig b/lib/tools/emacs/test.erl.orig
index 69356aca9e..0f8c4a9175 100644
--- a/lib/tools/emacs/test.erl.orig
+++ b/lib/tools/emacs/test.erl.orig
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -215,6 +215,11 @@ highlighting(X) % Function definitions should be highlighted
"char $in string", atom,
+ 'atom$', atom, 'atom$', atom,
+ 'atom\$', atom,
+
+ 'char $in atom', atom,
+
$[, ${, $\\, atom,
?MACRO_1,
?MACRO_2(foo),
diff --git a/lib/tools/emacs/vsn.mk b/lib/tools/emacs/vsn.mk
index f33ea8b519..a495da3453 100644
--- a/lib/tools/emacs/vsn.mk
+++ b/lib/tools/emacs/vsn.mk
@@ -1,3 +1,2 @@
-EMACS_VSN = 2.4.13
-
+EMACS_VSN = 2.7.0
diff --git a/lib/tools/src/Makefile b/lib/tools/src/Makefile
index 4c0cc8cde7..bdd0cdce25 100644
--- a/lib/tools/src/Makefile
+++ b/lib/tools/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2010. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -94,10 +94,10 @@ docs:
# ----------------------------------------------------
$(APP_TARGET): $(APP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
# ----------------------------------------------------
# Release Target
diff --git a/lib/tools/src/cover.erl b/lib/tools/src/cover.erl
index e21bd1b88c..680c1781ca 100644
--- a/lib/tools/src/cover.erl
+++ b/lib/tools/src/cover.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -26,12 +26,17 @@
%% ARCHITECTURE
%% The coverage tool consists of one process on each node involved in
%% coverage analysis. The process is registered as 'cover_server'
-%% (?SERVER). All cover_servers in the distributed system are linked
-%% together. The cover_server on the 'main' node is in charge, and it
-%% traps exits so it can detect nodedown or process crashes on the
-%% remote nodes. This process is implemented by the functions
-%% init_main/1 and main_process_loop/1. The cover_server on the remote
-%% nodes are implemented by the functions init_remote/2 and
+%% (?SERVER). The cover_server on the 'main' node is in charge, and
+%% it monitors the cover_servers on all remote nodes. When it gets a
+%% 'DOWN' message for another cover_server, it marks the node as
+%% 'lost'. If a nodeup is received for a lost node the main node
+%% ensures that the cover compiled modules are loaded again. If the
+%% remote node was alive during the disconnected periode, cover data
+%% for this periode will also be included in the analysis.
+%%
+%% The cover_server process on the main node is implemented by the
+%% functions init_main/1 and main_process_loop/1. The cover_server on
+%% the remote nodes are implemented by the functions init_remote/2 and
%% remote_process_loop/1.
%%
%% TABLES
@@ -81,15 +86,17 @@
export/1, export/2, import/1,
modules/0, imported/0, imported_modules/0, which_nodes/0, is_compiled/1,
reset/1, reset/0,
+ flush/1,
stop/0, stop/1]).
--export([remote_start/1]).
+-export([remote_start/1,get_main_node/0]).
%-export([bump/5]).
-export([transform/4]). % for test purposes
-record(main_state, {compiled=[], % [{Module,File}]
imported=[], % [{Module,File,ImportFile}]
stopper, % undefined | pid()
- nodes=[]}). % [Node]
+ nodes=[], % [Node]
+ lost_nodes=[]}). % [Node]
-record(remote_state, {compiled=[], % [{Module,File}]
main_node}). % atom()
@@ -497,6 +504,19 @@ stop(Node) when is_atom(Node) ->
stop(Nodes) ->
call({stop,remove_myself(Nodes,[])}).
+%% flush(Nodes) -> ok | {error,not_main_node}
+%% Nodes = [Node] | Node
+%% Node = atom()
+%% Error = {not_cover_compiled,Module}
+flush(Node) when is_atom(Node) ->
+ flush([Node]);
+flush(Nodes) ->
+ call({flush,remove_myself(Nodes,[])}).
+
+%% Used by test_server only. Not documented.
+get_main_node() ->
+ call(get_main_node).
+
%% bump(Module, Function, Arity, Clause, Line)
%% Module = Function = atom()
%% Arity = Clause = Line = integer()
@@ -541,7 +561,10 @@ remote_call(Node,Request) ->
Return =
receive
{'DOWN', Ref, _Type, _Object, _Info} ->
- {error,node_dead};
+ case Request of
+ {remote,stop} -> ok;
+ _ -> {error,node_dead}
+ end;
{?SERVER,Reply} ->
Reply
end,
@@ -569,40 +592,14 @@ init_main(Starter) ->
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),
+ net_kernel:monitor_nodes(true),
Starter ! {?SERVER,started},
main_process_loop(#main_state{}).
main_process_loop(State) ->
receive
{From, {start_nodes,Nodes}} ->
- ThisNode = node(),
- StartedNodes =
- lists:foldl(
- fun(Node,Acc) ->
- case rpc:call(Node,cover,remote_start,[ThisNode]) of
- {ok,RPid} ->
- link(RPid),
- [Node|Acc];
- Error ->
- io:format("Could not start cover on ~w: ~p\n",
- [Node,Error]),
- Acc
- end
- end,
- [],
- Nodes),
-
- %% In case some of the compiled modules have been unloaded they
- %% should not be loaded on the new node.
- {_LoadedModules,Compiled} =
- get_compiled_still_loaded(State#main_state.nodes,
- State#main_state.compiled),
- remote_load_compiled(StartedNodes,Compiled),
-
- State1 =
- State#main_state{nodes = State#main_state.nodes ++ StartedNodes,
- compiled = Compiled},
+ {StartedNodes,State1} = do_start_nodes(Nodes, State),
reply(From, {ok,StartedNodes}),
main_process_loop(State1);
@@ -707,8 +704,15 @@ main_process_loop(State) ->
{From, {stop,Nodes}} ->
remote_collect('_',Nodes,true),
reply(From, ok),
- State1 = State#main_state{nodes=State#main_state.nodes--Nodes},
- main_process_loop(State1);
+ Nodes1 = State#main_state.nodes--Nodes,
+ LostNodes1 = State#main_state.lost_nodes--Nodes,
+ main_process_loop(State#main_state{nodes=Nodes1,
+ lost_nodes=LostNodes1});
+
+ {From, {flush,Nodes}} ->
+ remote_collect('_',Nodes,false),
+ reply(From, ok),
+ main_process_loop(State);
{From, stop} ->
lists:foreach(
@@ -788,14 +792,37 @@ main_process_loop(State) ->
end,
main_process_loop(S);
- {'EXIT',Pid,_Reason} ->
- %% Exit is trapped on the main node only, so this will only happen
- %% there. I assume that I'm only linked to cover_servers on remote
- %% nodes, so this must be one of them crashing.
- %% Remove node from list!
- State1 = State#main_state{nodes=State#main_state.nodes--[node(Pid)]},
+ {'DOWN', _MRef, process, {?SERVER,Node}, _Info} ->
+ %% A remote cover_server is down, mark as lost
+ {Nodes,Lost} =
+ case lists:member(Node,State#main_state.nodes) of
+ true ->
+ N = State#main_state.nodes--[Node],
+ L = [Node|State#main_state.lost_nodes],
+ {N,L};
+ false -> % node stopped
+ {State#main_state.nodes,State#main_state.lost_nodes}
+ end,
+ main_process_loop(State#main_state{nodes=Nodes,lost_nodes=Lost});
+
+ {nodeup,Node} ->
+ State1 =
+ case lists:member(Node,State#main_state.lost_nodes) of
+ true ->
+ sync_compiled(Node,State);
+ false ->
+ State
+ end,
main_process_loop(State1);
+
+ {nodedown,_} ->
+ %% Will be taken care of when 'DOWN' message arrives
+ main_process_loop(State);
+ {From, get_main_node} ->
+ reply(From, node()),
+ main_process_loop(State);
+
get_status ->
io:format("~p~n",[State]),
main_process_loop(State)
@@ -850,7 +877,16 @@ remote_process_loop(State) ->
{remote,stop} ->
reload_originals(State#remote_state.compiled),
unregister(?SERVER),
- remote_reply(State#remote_state.main_node, ok);
+ ok; % not replying since 'DOWN' message will be received anyway
+
+ {remote,get_compiled} ->
+ remote_reply(State#remote_state.main_node,
+ State#remote_state.compiled),
+ remote_process_loop(State);
+
+ {From, get_main_node} ->
+ remote_reply(From, State#remote_state.main_node),
+ remote_process_loop(State);
get_status ->
io:format("~p~n",[State]),
@@ -961,6 +997,36 @@ unload([]) ->
%%%--Handling of remote nodes--------------------------------------------
+do_start_nodes(Nodes, State) ->
+ ThisNode = node(),
+ StartedNodes =
+ lists:foldl(
+ fun(Node,Acc) ->
+ case rpc:call(Node,cover,remote_start,[ThisNode]) of
+ {ok,_RPid} ->
+ erlang:monitor(process,{?SERVER,Node}),
+ [Node|Acc];
+ Error ->
+ io:format("Could not start cover on ~w: ~p\n",
+ [Node,Error]),
+ Acc
+ end
+ end,
+ [],
+ Nodes),
+
+ %% In case some of the compiled modules have been unloaded they
+ %% should not be loaded on the new node.
+ {_LoadedModules,Compiled} =
+ get_compiled_still_loaded(State#main_state.nodes,
+ State#main_state.compiled),
+ remote_load_compiled(StartedNodes,Compiled),
+
+ State1 =
+ State#main_state{nodes = State#main_state.nodes ++ StartedNodes,
+ compiled = Compiled},
+ {StartedNodes, State1}.
+
%% start the cover_server on a remote node
remote_start(MainNode) ->
case whereis(?SERVER) of
@@ -984,6 +1050,30 @@ remote_start(MainNode) ->
{error,{already_started,Pid}}
end.
+%% If a lost node comes back, ensure that main and remote node has the
+%% same cover compiled modules. Note that no action is taken if the
+%% same {Mod,File} eksists on both, i.e. code change is not handled!
+sync_compiled(Node,State) ->
+ #main_state{compiled=Compiled0,nodes=Nodes,lost_nodes=Lost}=State,
+ State1 =
+ case remote_call(Node,{remote,get_compiled}) of
+ {error,node_dead} ->
+ {_,S} = do_start_nodes([Node],State),
+ S;
+ {error,_} ->
+ State;
+ RemoteCompiled ->
+ {_,Compiled} = get_compiled_still_loaded(Nodes,Compiled0),
+ Unload = [UM || {UM,_}=U <- RemoteCompiled,
+ false == lists:member(U,Compiled)],
+ remote_unload([Node],Unload),
+ Load = [L || L <- Compiled,
+ false == lists:member(L,RemoteCompiled)],
+ remote_load_compiled([Node],Load),
+ State#main_state{compiled=Compiled, nodes=[Node|Nodes]}
+ end,
+ State1#main_state{lost_nodes=Lost--[Node]}.
+
%% 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.
@@ -1094,7 +1184,6 @@ remove_myself([Node|Nodes],Acc) ->
remove_myself(Nodes,[Node|Acc]);
remove_myself([],Acc) ->
Acc.
-
%%%--Handling of modules state data--------------------------------------
@@ -1430,12 +1519,6 @@ aux_var(Vars, N) ->
%% This way we will be able to exclude functions defined in include files.
munge({function,0,module_info,_Arity,_Clauses},_Vars,_MainFile,_Switch) ->
ignore; % module_info will be added again when the forms are recompiled
-munge(Form={function,_,'MNEMOSYNE QUERY',_,_},Vars,_MainFile,Switch) ->
- {Form,Vars,Switch}; % No bumps in Mnemosyne code.
-munge(Form={function,_,'MNEMOSYNE RULE',_,_},Vars,_MainFile,Switch) ->
- {Form,Vars,Switch};
-munge(Form={function,_,'MNEMOSYNE RECFUNDEF',_,_},Vars,_MainFile,Switch) ->
- {Form,Vars,Switch};
munge({function,Line,Function,Arity,Clauses},Vars,_MainFile,on) ->
Vars2 = Vars#vars{function=Function,
arity=Arity,
@@ -2007,30 +2090,40 @@ do_analyse_to_file(Module, OutFile, ErlFile, HTML) ->
case file:open(OutFile, [write]) of
{ok, OutFd} ->
if HTML ->
- io:format(OutFd,
- "<html>\n"
- "<head><title>~s</title></head>"
- "<body bgcolor=white text=black>\n"
- "<pre>\n",
- [OutFile]);
+ Encoding = encoding(ErlFile),
+ Header =
+ ["<!DOCTYPE HTML PUBLIC "
+ "\"-//W3C//DTD HTML 3.2 Final//EN\">\n"
+ "<html>\n"
+ "<head>\n"
+ "<meta http-equiv=\"Content-Type\""
+ " content=\"text/html; charset=",
+ Encoding,"\"/>\n"
+ "<title>",OutFile,"</title>\n"
+ "</head>"
+ "<body style='background-color: white;"
+ " color: black'>\n"
+ "<pre>\n"],
+ file:write(OutFd,Header);
true -> ok
end,
%% Write some initial information to the output file
{{Y,Mo,D},{H,Mi,S}} = calendar:local_time(),
- io:format(OutFd, "File generated from ~s by COVER "
- "~p-~s-~s at ~s:~s:~s~n",
- [ErlFile,
- Y,
- string:right(integer_to_list(Mo), 2, $0),
- string:right(integer_to_list(D), 2, $0),
- string:right(integer_to_list(H), 2, $0),
- string:right(integer_to_list(Mi), 2, $0),
- string:right(integer_to_list(S), 2, $0)]),
- io:format(OutFd, "~n"
- "**************************************"
- "**************************************"
- "~n~n", []),
+ Timestamp =
+ io_lib:format("~p-~s-~s at ~s:~s:~s",
+ [Y,
+ string:right(integer_to_list(Mo), 2, $0),
+ string:right(integer_to_list(D), 2, $0),
+ string:right(integer_to_list(H), 2, $0),
+ string:right(integer_to_list(Mi), 2, $0),
+ string:right(integer_to_list(S), 2, $0)]),
+ file:write(OutFd,
+ ["File generated from ",ErlFile," by COVER ",
+ Timestamp,"\n\n"
+ "**************************************"
+ "**************************************"
+ "\n\n"]),
print_lines(Module, InFd, OutFd, 1, HTML),
@@ -2254,7 +2347,13 @@ do_reset2([]) ->
do_clear(Module) ->
ets:match_delete(?COVER_CLAUSE_TABLE, {Module,'_'}),
ets:match_delete(?COVER_TABLE, {#bump{module=Module},'_'}),
- ets:match_delete(?COLLECTION_TABLE, {#bump{module=Module},'_'}).
+ case lists:member(?COLLECTION_TABLE, ets:all()) of
+ true ->
+ %% We're on the main node
+ ets:match_delete(?COLLECTION_TABLE, {#bump{module=Module},'_'});
+ false ->
+ ok
+ end.
not_loaded(Module, unloaded, State) ->
do_clear(Module),
@@ -2307,7 +2406,7 @@ pmap(Fun, [E | Rest], Pids, Limit, Cnt, Acc) when Cnt < Limit ->
pmap(Fun, Rest, Pids ++ [Pid], Limit, Cnt + 1, Acc);
pmap(Fun, List, [Pid | Pids], Limit, Cnt, Acc) ->
receive
- {'DOWN', _Ref, process, _, _} ->
+ {'DOWN', _Ref, process, X, _} when is_pid(X) ->
pmap(Fun, List, [Pid | Pids], Limit, Cnt - 1, Acc);
{res, Pid, Res} ->
pmap(Fun, List, Pids, Limit, Cnt, [Res | Acc])
@@ -2316,6 +2415,23 @@ pmap(_Fun, [], [], _Limit, 0, Acc) ->
lists:reverse(Acc);
pmap(Fun, [], [], Limit, Cnt, Acc) ->
receive
- {'DOWN', _Ref, process, _, _} ->
+ {'DOWN', _Ref, process, X, _} when is_pid(X) ->
pmap(Fun, [], [], Limit, Cnt - 1, Acc)
end.
+
+%%%-----------------------------------------------------------------
+%%% Read encoding from source file
+encoding(File) ->
+ Encoding =
+ case epp:read_encoding(File) of
+ none ->
+ epp:default_encoding();
+ E ->
+ E
+ end,
+ html_encoding(Encoding).
+
+html_encoding(latin1) ->
+ "iso-8859-1";
+html_encoding(utf8) ->
+ "utf-8".
diff --git a/lib/tools/src/lcnt.erl b/lib/tools/src/lcnt.erl
index 989a661b75..70d62307c8 100644
--- a/lib/tools/src/lcnt.erl
+++ b/lib/tools/src/lcnt.erl
@@ -1,7 +1,8 @@
+%% -*- coding: utf-8 -*-
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/tools/src/tools.app.src b/lib/tools/src/tools.app.src
index cd9b622f15..553c5eb96b 100644
--- a/lib/tools/src/tools.app.src
+++ b/lib/tools/src/tools.app.src
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -24,6 +24,7 @@
eprof,
fprof,
instrument,
+ lcnt,
make,
xref,
xref_base,
diff --git a/lib/tools/src/xref_reader.erl b/lib/tools/src/xref_reader.erl
index 92f0c45c7b..2fcc2c503c 100644
--- a/lib/tools/src/xref_reader.erl
+++ b/lib/tools/src/xref_reader.erl
@@ -80,12 +80,6 @@ form({attribute, Line, xref, Calls}, S) -> % experimental
attr(Calls, Line, M, Fun, L, X, B, S);
form({attribute, _Line, _Attr, _Val}, S) ->
S;
-form({function, 0, 'MNEMOSYNE RULE', 1, _Clauses}, S) ->
- S;
-form({function, 0, 'MNEMOSYNE QUERY', 2, _Clauses}, S) ->
- S;
-form({function, 0, 'MNEMOSYNE RECFUNDEF', 1, _Clauses}, S) ->
- S;
form({function, 0, module_info, 0, _Clauses}, S) ->
S;
form({function, 0, module_info, 1, _Clauses}, S) ->
@@ -331,9 +325,6 @@ handle_call(Locality, Module, Name, Arity, Line, S) ->
handle_call(Locality, To, Line, S, false)
end.
-handle_call(_Locality, {_, 'MNEMOSYNE RULE',1}, _Line, S, _) -> S;
-handle_call(_Locality, {_, 'MNEMOSYNE QUERY', 2}, _Line, S, _) -> S;
-handle_call(_Locality, {_, 'MNEMOSYNE RECFUNDEF',1}, _Line, S, _) -> S;
handle_call(Locality, To0, Line, S, IsUnres) ->
From = S#xrefr.function,
To = adjust_arity(S, To0),
diff --git a/lib/tools/src/xref_utils.erl b/lib/tools/src/xref_utils.erl
index 9d4a175d88..680563e9df 100644
--- a/lib/tools/src/xref_utils.erl
+++ b/lib/tools/src/xref_utils.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -141,7 +141,7 @@ is_string([], _) ->
is_string(Term, C) ->
is_string1(Term, C).
-is_string1([H | T], C) when H > C, H < 127 ->
+is_string1([H | T], C) when H > C ->
is_string1(T, C);
is_string1([], _) ->
true;
diff --git a/lib/tools/test/Makefile b/lib/tools/test/Makefile
index b9a42041a2..86c81217b6 100644
--- a/lib/tools/test/Makefile
+++ b/lib/tools/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/tools/test/cover_SUITE.erl b/lib/tools/test/cover_SUITE.erl
index 0cd53a05db..57260a3869 100644
--- a/lib/tools/test/cover_SUITE.erl
+++ b/lib/tools/test/cover_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -23,7 +23,9 @@
init_per_group/2,end_per_group/2]).
-export([start/1, compile/1, analyse/1, misc/1, stop/1,
- distribution/1, export_import/1,
+ distribution/1, reconnect/1, die_and_reconnect/1,
+ dont_reconnect_after_stop/1, stop_node_after_disconnect/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]).
@@ -45,7 +47,9 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
case whereis(cover_server) of
undefined ->
- [start, compile, analyse, misc, stop, distribution,
+ [start, compile, analyse, misc, stop,
+ distribution, reconnect, die_and_reconnect,
+ dont_reconnect_after_stop, stop_node_after_disconnect,
export_import, otp_5031, eif, otp_5305, otp_5418,
otp_6115, otp_7095, otp_8188, otp_8270, otp_8273,
otp_8340];
@@ -85,8 +89,11 @@ init_per_testcase(TC, Config) when TC =:= misc;
init_per_testcase(_TestCase, Config) ->
Config.
-end_per_testcase(_TestCase, _Config) ->
- %cover:stop(),
+end_per_testcase(TestCase, _Config) ->
+ case lists:member(TestCase,[start,compile,analyse,misc]) of
+ true -> ok;
+ false -> cover:stop()
+ end,
ok.
start(suite) -> [];
@@ -326,14 +333,16 @@ distribution(Config) when is_list(Config) ->
?line {ok,N1} = ?t:start_node(cover_SUITE_distribution1,slave,[]),
?line {ok,N2} = ?t:start_node(cover_SUITE_distribution2,slave,[]),
?line {ok,N3} = ?t:start_node(cover_SUITE_distribution3,slave,[]),
+ ?line {ok,N4} = ?t:start_node(cover_SUITE_distribution4,slave,[]),
%% Check that an already compiled module is loaded on new nodes
?line {ok,f} = cover:compile(f),
- ?line {ok,[_,_,_]} = cover:start(nodes()),
+ ?line {ok,[_,_,_,_]} = cover:start(nodes()),
?line cover_compiled = code:which(f),
?line cover_compiled = rpc:call(N1,code,which,[f]),
?line cover_compiled = rpc:call(N2,code,which,[f]),
?line cover_compiled = rpc:call(N3,code,which,[f]),
+ ?line cover_compiled = rpc:call(N4,code,which,[f]),
%% Check that a node cannot be started twice
?line {ok,[]} = cover:start(N2),
@@ -351,6 +360,7 @@ distribution(Config) when is_list(Config) ->
?line cover_compiled = rpc:call(N1,code,which,[v]),
?line cover_compiled = rpc:call(N2,code,which,[v]),
?line cover_compiled = rpc:call(N3,code,which,[v]),
+ ?line cover_compiled = rpc:call(N4,code,which,[v]),
%% this is lost when the node is killed
?line rpc:call(N3,f,f2,[]),
@@ -385,6 +395,18 @@ distribution(Config) when is_list(Config) ->
%% reset on the remote node(s))
?line check_f_calls(1,1),
+ %% Another checn that data is not fetched twice, i.e. when flushed
+ %% then analyse should not add the same data again.
+ ?line rpc:call(N4,f,f2,[]),
+ ?line ok = cover:flush(N4),
+ ?line check_f_calls(1,2),
+
+ %% Check that flush collects data so calls are not lost if node is killed
+ ?line rpc:call(N4,f,f2,[]),
+ ?line ok = cover:flush(N4),
+ ?line rpc:call(N4,erlang,halt,[]),
+ ?line check_f_calls(1,3),
+
%% Check that stop() unloads on all nodes
?line ok = cover:stop(),
?line timer:sleep(100), %% Give nodes time to unload on slow machines.
@@ -393,20 +415,205 @@ distribution(Config) when is_list(Config) ->
?line true = is_unloaded(LocalBeam),
?line true = is_unloaded(N2Beam),
- %% Check that cover_server on remote node dies if main node dies
+ %% Check that cover_server on remote node does not die if main node dies
?line {ok,[N1]} = cover:start(N1),
- ?line true = is_pid(rpc:call(N1,erlang,whereis,[cover_server])),
+ ?line true = is_pid(N1Server = rpc:call(N1,erlang,whereis,[cover_server])),
?line exit(whereis(cover_server),kill),
- ?line timer:sleep(10),
- ?line undefined = rpc:call(N1,erlang,whereis,[cover_server]),
-
+ ?line timer:sleep(100),
+ ?line N1Server = rpc:call(N1,erlang,whereis,[cover_server]),
+
%% Cleanup
?line Files = lsfiles(),
?line remove(files(Files, ".beam")),
?line ?t:stop_node(N1),
?line ?t:stop_node(N2).
-
+%% Test that a lost node is reconnected
+reconnect(Config) ->
+ DataDir = ?config(data_dir, Config),
+ ok = file:set_cwd(DataDir),
+
+ {ok,a} = compile:file(a),
+ {ok,b} = compile:file(b),
+ {ok,f} = compile:file(f),
+
+ {ok,N1} = ?t:start_node(cover_SUITE_reconnect,peer,
+ [{args," -pa " ++ DataDir},{start_cover,false}]),
+ {ok,a} = cover:compile(a),
+ {ok,f} = cover:compile(f),
+ {ok,[N1]} = cover:start(nodes()),
+
+ %% Some calls to check later
+ rpc:call(N1,f,f1,[]),
+ cover:flush(N1),
+ rpc:call(N1,f,f1,[]),
+
+ %% This will cause a call to f:f2() when nodes()==[] on N1
+ rpc:cast(N1,f,call_f2_when_isolated,[]),
+
+ %% Disconnect and check that node is removed from main cover node
+ net_kernel:disconnect(N1),
+ timer:sleep(500), % allow some to detect disconnect and for f:f2() call
+ [] = cover:which_nodes(),
+
+ %% Do some add one module (b) and remove one module (a)
+ code:purge(a),
+ {module,a} = code:load_file(a),
+ {ok,b} = cover:compile(b),
+ cover_compiled = code:which(b),
+
+ [] = cover:which_nodes(),
+ check_f_calls(1,0), % only the first call - before the flush
+
+ %% Reconnect the node and check that b and f are cover compiled but not a
+ net_kernel:connect_node(N1),
+ timer:sleep(100),
+ [N1] = cover:which_nodes(), % we are reconnected
+ cover_compiled = rpc:call(N1,code,which,[b]),
+ cover_compiled = rpc:call(N1,code,which,[f]),
+ ABeam = rpc:call(N1,code,which,[a]),
+ false = (cover_compiled==ABeam),
+
+ %% Ensure that we have:
+ %% * one f1 call from before the flush,
+ %% * one f1 call from after the flush but before disconnect
+ %% * one f2 call when disconnected
+ check_f_calls(2,1),
+
+ cover:stop(),
+ ?t:stop_node(N1),
+ ok.
+
+%% Test that a lost node is reconnected - also if it has been dead
+die_and_reconnect(Config) ->
+ DataDir = ?config(data_dir, Config),
+ ok = file:set_cwd(DataDir),
+
+ {ok,f} = compile:file(f),
+
+ NodeName = cover_SUITE_die_and_reconnect,
+ {ok,N1} = ?t:start_node(NodeName,peer,
+ [{args," -pa " ++ DataDir},{start_cover,false}]),
+ %% {ok,a} = cover:compile(a),
+ {ok,f} = cover:compile(f),
+ {ok,[N1]} = cover:start(nodes()),
+
+ %% Some calls to check later
+ rpc:call(N1,f,f1,[]),
+ cover:flush(N1),
+ rpc:call(N1,f,f1,[]),
+
+ %% Kill the node
+ rpc:call(N1,erlang,halt,[]),
+ [] = cover:which_nodes(),
+
+ check_f_calls(1,0), % only the first call - before the flush
+
+ %% Restart the node and check that cover reconnects
+ {ok,N1} = ?t:start_node(NodeName,peer,
+ [{args," -pa " ++ DataDir},{start_cover,false}]),
+ timer:sleep(100),
+ [N1] = cover:which_nodes(), % we are reconnected
+ cover_compiled = rpc:call(N1,code,which,[f]),
+
+ %% One more call...
+ rpc:call(N1,f,f1,[]),
+
+ %% Ensure that no more calls are counted
+ check_f_calls(2,0),
+
+ cover:stop(),
+ ?t:stop_node(N1),
+ ok.
+
+%% Test that a stopped node is not marked as lost, i.e. that it is not
+%% reconnected if it is restarted (OTP-10638)
+dont_reconnect_after_stop(Config) ->
+ DataDir = ?config(data_dir, Config),
+ ok = file:set_cwd(DataDir),
+
+ {ok,f} = compile:file(f),
+
+ NodeName = cover_SUITE_dont_reconnect_after_stop,
+ {ok,N1} = ?t:start_node(NodeName,peer,
+ [{args," -pa " ++ DataDir},{start_cover,false}]),
+ {ok,f} = cover:compile(f),
+ {ok,[N1]} = cover:start(nodes()),
+
+ %% A call to check later
+ rpc:call(N1,f,f1,[]),
+
+ %% Stop cover on the node, then terminate the node
+ cover:stop(N1),
+ rpc:call(N1,erlang,halt,[]),
+ [] = cover:which_nodes(),
+
+ check_f_calls(1,0),
+
+ %% Restart the node and check that cover does not reconnect
+ {ok,N1} = ?t:start_node(NodeName,peer,
+ [{args," -pa " ++ DataDir},{start_cover,false}]),
+ timer:sleep(300),
+ [] = cover:which_nodes(),
+ Beam = rpc:call(N1,code,which,[f]),
+ false = (Beam==cover_compiled),
+
+ %% One more call...
+ rpc:call(N1,f,f1,[]),
+ cover:flush(N1),
+
+ %% Ensure that the last call is not counted
+ check_f_calls(1,0),
+
+ cover:stop(),
+ ?t:stop_node(N1),
+ ok.
+
+%% Test that a node which is stopped while it is marked as lost is not
+%% reconnected if it is restarted (OTP-10638)
+stop_node_after_disconnect(Config) ->
+ DataDir = ?config(data_dir, Config),
+ ok = file:set_cwd(DataDir),
+
+ {ok,f} = compile:file(f),
+
+ NodeName = cover_SUITE_stop_node_after_disconnect,
+ {ok,N1} = ?t:start_node(NodeName,peer,
+ [{args," -pa " ++ DataDir},{start_cover,false}]),
+ {ok,f} = cover:compile(f),
+ {ok,[N1]} = cover:start(nodes()),
+
+ %% A call to check later
+ rpc:call(N1,f,f1,[]),
+
+ %% Flush the node, then terminate the node to make it marked as lost
+ cover:flush(N1),
+ rpc:call(N1,erlang,halt,[]),
+
+ check_f_calls(1,0),
+
+ %% Stop cover on node
+ cover:stop(N1),
+
+ %% Restart the node and check that cover does not reconnect
+ {ok,N1} = ?t:start_node(NodeName,peer,
+ [{args," -pa " ++ DataDir},{start_cover,false}]),
+ timer:sleep(300),
+ [] = cover:which_nodes(),
+ Beam = rpc:call(N1,code,which,[f]),
+ false = (Beam==cover_compiled),
+
+ %% One more call...
+ rpc:call(N1,f,f1,[]),
+ cover:flush(N1),
+
+ %% Ensure that the last call is not counted
+ check_f_calls(1,0),
+
+ cover:stop(),
+ ?t:stop_node(N1),
+ ok.
+
export_import(suite) -> [];
export_import(Config) when is_list(Config) ->
?line DataDir = ?config(data_dir, Config),
@@ -795,7 +1002,7 @@ otp_8270(Config) when is_list(Config) ->
ok.
otp_8273(doc) ->
- ["OTP-8270. Bug."];
+ ["OTP-8273. Bug."];
otp_8273(suite) -> [];
otp_8273(Config) when is_list(Config) ->
Test = <<"-module(t).
@@ -1238,4 +1445,4 @@ is_unloaded(What) ->
end.
check_f_calls(F1,F2) ->
- {ok,[{{f,f1,0},F1},{{f,f2,0},F2}]} = cover:analyse(f,calls,function).
+ {ok,[{{f,f1,0},F1},{{f,f2,0},F2}|_]} = cover:analyse(f,calls,function).
diff --git a/lib/tools/test/cover_SUITE_data/f.erl b/lib/tools/test/cover_SUITE_data/f.erl
index 1ef8bbdb49..ce2963014a 100644
--- a/lib/tools/test/cover_SUITE_data/f.erl
+++ b/lib/tools/test/cover_SUITE_data/f.erl
@@ -1,5 +1,5 @@
-module(f).
--export([f1/0,f2/0]).
+-export([f1/0,f2/0,call_f2_when_isolated/0]).
f1() ->
f1_line1,
@@ -8,3 +8,12 @@ f1() ->
f2() ->
f2_line1,
f2_line2.
+
+call_f2_when_isolated() ->
+ case nodes() of
+ [] ->
+ f2();
+ _ ->
+ timer:sleep(100),
+ call_f2_when_isolated()
+ end.
diff --git a/lib/tools/test/xref_SUITE.erl b/lib/tools/test/xref_SUITE.erl
index 78e49044a5..dc06678b8e 100644
--- a/lib/tools/test/xref_SUITE.erl
+++ b/lib/tools/test/xref_SUITE.erl
@@ -1,3 +1,4 @@
+%% -*- coding: utf-8 -*-
%%
%% %CopyrightBegin%
%%
@@ -46,14 +47,14 @@
-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, fun_mfa_r14/1,
+ fun_mfa/1, fun_mfa_r14/1,
fun_mfa_vars/1, qlc/1]).
-export([
analyze/1, basic/1, md/1, q/1, variables/1, unused_locals/1]).
-export([
- format_error/1, otp_7423/1, otp_7831/1]).
+ format_error/1, otp_7423/1, otp_7831/1, otp_10192/1]).
-import(lists, [append/2, flatten/1, keysearch/3, member/2, sort/1, usort/1]).
@@ -82,11 +83,11 @@ groups() ->
modules]},
{files, [],
[add, default, info, lib, read, read2, remove, replace,
- update, deprecated, trycatch, abstract_modules, fun_mfa,
+ update, deprecated, trycatch, fun_mfa,
fun_mfa_r14, fun_mfa_vars, qlc]},
{analyses, [],
[analyze, basic, md, q, variables, unused_locals]},
- {misc, [], [format_error, otp_7423, otp_7831]}].
+ {misc, [], [format_error, otp_7423, otp_7831, otp_10192]}].
init_per_suite(Config) ->
init(Config).
@@ -1668,64 +1669,6 @@ trycatch(Conf) when is_list(Conf) ->
ok.
-abstract_modules(suite) -> [];
-abstract_modules(doc) -> ["OTP-5520: Abstract (parameterized) modules."];
-abstract_modules(Conf) when is_list(Conf) ->
- Dir = ?copydir,
- File = fname(Dir, "absmod.erl"),
- MFile = fname(Dir, "absmod"),
- Beam = fname(Dir, "absmod.beam"),
- Test = <<"-module(param, [A, B]).
-
- -export([args/1]).
-
- args(C) ->
- X = local(C),
- Y = THIS:new(), % undef
- Z = new(A, B),
- {X, Y, Z}.
-
- local(C) ->
- module_info(C).
- ">>,
-
- ?line ok = file:write_file(File, Test),
-
- %% The compiler will no longer allow us to have a mismatch between
- %% the module name and the output file, so we must use a trick.
- ?line {ok, param, BeamCode} = compile:file(File, [binary,debug_info]),
- ?line ok = file:write_file(Beam, BeamCode),
-
- ?line {ok, _} = xref:start(s),
- ?line {ok, param} = xref:add_module(s, MFile, {warnings,false}),
- A = param,
- ?line {ok, [{{{A,args,1},{'$M_EXPR',new,0}},[7]},
- {{{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]}]} =
- xref:q(s, "(Lin) E"),
- ?line {ok,[{param,args,1},
- {param,instance,2},
- {param,local,1},
- {param,module_info,1},
- {param,new,2}]} = xref:q(s, "F"),
-
- ?line ok = check_state(s),
- ?line xref:stop(s),
-
- ?line {ok, _} = xref:start(s, {xref_mode, modules}),
- ?line {ok, param} = xref:add_module(s, MFile),
- ?line {ok,[{param,args,1},
- {param,instance,2},
- {param,new,2}]} = xref:q(s, "X"),
- ?line ok = check_state(s),
- ?line xref:stop(s),
-
- ?line ok = file:delete(File),
- ?line ok = file:delete(Beam),
- ok.
-
fun_mfa(suite) -> [];
fun_mfa(doc) -> ["OTP-5653: fun M:F/A."];
fun_mfa(Conf) when is_list(Conf) ->
@@ -2515,6 +2458,18 @@ otp_7831(Conf) when is_list(Conf) ->
?line xref:stop(Pid2),
ok.
+otp_10192(suite) -> [];
+otp_10192(doc) ->
+ ["OTP-10192. Allow filenames with character codes greater than 126."];
+otp_10192(Conf) when is_list(Conf) ->
+ PrivDir = ?privdir,
+ {ok, _Pid} = xref:start(s),
+ Dir = filename:join(PrivDir, "ä"),
+ ok = file:make_dir(Dir),
+ {ok, []} = xref:add_directory(s, Dir),
+ xref:stop(s),
+ ok.
+
%%%
%%% Utilities
%%%
diff --git a/lib/tv/doc/src/Makefile b/lib/tv/doc/src/Makefile
index e36dd84b73..413eb8f634 100644
--- a/lib/tv/doc/src/Makefile
+++ b/lib/tv/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/tv/doc/src/notes.xml b/lib/tv/doc/src/notes.xml
index 9217c018e9..fa72dd23a9 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>2011</year>
+ <year>2004</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
diff --git a/lib/tv/priv/Makefile b/lib/tv/priv/Makefile
index 73ea8f4ee9..679a76fe88 100644
--- a/lib/tv/priv/Makefile
+++ b/lib/tv/priv/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2009. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/tv/src/Makefile b/lib/tv/src/Makefile
index f078e5dee1..3d680c1eaf 100644
--- a/lib/tv/src/Makefile
+++ b/lib/tv/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2009. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -109,10 +109,10 @@ clean:
rm -f errs core *~
$(APP_TARGET): $(APP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
docs:
diff --git a/lib/tv/src/tv_main.hrl b/lib/tv/src/tv_main.hrl
index 28329ca83c..06b405ac1f 100644
--- a/lib/tv/src/tv_main.hrl
+++ b/lib/tv/src/tv_main.hrl
@@ -131,7 +131,6 @@
ir_WstringDef,
lmcounter,
locks,
- mnemosyne_tmp,
pg2_table,
queue,
snmp_agent_table,
diff --git a/lib/typer/src/Makefile b/lib/typer/src/Makefile
index c3fae0ad42..13af466755 100644
--- a/lib/typer/src/Makefile
+++ b/lib/typer/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2006-2011. All Rights Reserved.
+# Copyright Ericsson AB 2006-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -82,13 +82,13 @@ clean:
# ----------------------------------------------------
$(EBIN)/typer.$(EMULATOR): typer.erl ../vsn.mk Makefile
- erlc -W $(ERL_COMPILE_FLAGS) -DVSN="\"v$(VSN)\"" -o$(EBIN) typer.erl
+ $(erlc_verbose)erlc -W $(ERL_COMPILE_FLAGS) -DVSN="\"v$(VSN)\"" -o$(EBIN) typer.erl
$(APP_TARGET): $(APP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
# ---------------------------------------------------------------------
# dependencies
diff --git a/lib/typer/src/typer.erl b/lib/typer/src/typer.erl
index 1e40b8926e..0ace1d5fb8 100644
--- a/lib/typer/src/typer.erl
+++ b/lib/typer/src/typer.erl
@@ -2,7 +2,7 @@
%%-----------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -1007,7 +1007,7 @@ msg(Msg) ->
port_command(P, Msg),
true = port_close(P),
ok;
- _ -> % win32, vxworks
+ _ -> % win32
io:format("~s", [Msg])
end.
diff --git a/lib/typer/vsn.mk b/lib/typer/vsn.mk
index 85a7c01424..18e328afc1 100644
--- a/lib/typer/vsn.mk
+++ b/lib/typer/vsn.mk
@@ -1 +1 @@
-TYPER_VSN = 0.9.3
+TYPER_VSN = 0.9.4
diff --git a/lib/webtool/src/Makefile b/lib/webtool/src/Makefile
index 45e8b2a6f0..f28c777240 100644
--- a/lib/webtool/src/Makefile
+++ b/lib/webtool/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2001-2009. All Rights Reserved.
+# Copyright Ericsson AB 2001-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -76,10 +76,10 @@ docs:
# ----------------------------------------------------
$(APP_TARGET): $(APP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
# ----------------------------------------------------
# Release Target
diff --git a/lib/wx/.gitignore b/lib/wx/.gitignore
index 0fa427bfe5..960c76a779 100644
--- a/lib/wx/.gitignore
+++ b/lib/wx/.gitignore
@@ -1,4 +1,5 @@
test_log_*
wx_test_case_info
-api_gen/gl_man?
-doc/html/* \ No newline at end of file
+doc/html/*
+%% Don't delete links to man src when git clean -dfX
+%% api_gen/gl_man?
diff --git a/lib/wx/aclocal.m4 b/lib/wx/aclocal.m4
index 339a15a2bb..918e30a886 100644
--- a/lib/wx/aclocal.m4
+++ b/lib/wx/aclocal.m4
@@ -1,7 +1,7 @@
dnl
dnl %CopyrightBegin%
dnl
-dnl Copyright Ericsson AB 1998-2011. All Rights Reserved.
+dnl Copyright Ericsson AB 1998-2012. All Rights Reserved.
dnl
dnl The contents of this file are subject to the Erlang Public License,
dnl Version 1.1, (the "License"); you may not use this file except in
@@ -59,6 +59,7 @@ AC_ARG_VAR(erl_xcomp_isysroot, [Absolute cross system root include path (only us
dnl Cross compilation variables
AC_ARG_VAR(erl_xcomp_bigendian, [big endian system: yes|no (only used when cross compiling)])
+AC_ARG_VAR(erl_xcomp_double_middle_endian, [double-middle-endian system: yes|no (only used when cross compiling)])
AC_ARG_VAR(erl_xcomp_linux_clock_gettime_correction, [clock_gettime() can be used for time correction: yes|no (only used when cross compiling)])
AC_ARG_VAR(erl_xcomp_linux_nptl, [have Native POSIX Thread Library: yes|no (only used when cross compiling)])
AC_ARG_VAR(erl_xcomp_linux_usable_sigusrx, [SIGUSR1 and SIGUSR2 can be used: yes|no (only used when cross compiling)])
@@ -606,6 +607,103 @@ ifelse([$5], , , [$5
fi
])
+dnl ----------------------------------------------------------------------
+dnl
+dnl AC_DOUBLE_MIDDLE_ENDIAN
+dnl
+dnl Checks whether doubles are represented in "middle-endian" format.
+dnl Sets ac_cv_double_middle_endian={no,yes,unknown} accordingly,
+dnl as well as DOUBLE_MIDDLE_ENDIAN.
+dnl
+dnl
+
+AC_DEFUN([AC_C_DOUBLE_MIDDLE_ENDIAN],
+[AC_CACHE_CHECK(whether double word ordering is middle-endian, ac_cv_c_double_middle_endian,
+[# It does not; compile a test program.
+AC_RUN_IFELSE(
+[AC_LANG_SOURCE([[#include <stdlib.h>
+
+int
+main(void)
+{
+ int i = 0;
+ int zero = 0;
+ int bigendian;
+ int zero_index = 0;
+
+ union
+ {
+ long int l;
+ char c[sizeof (long int)];
+ } u;
+
+ /* we'll use the one with 32-bit words */
+ union
+ {
+ double d;
+ unsigned int c[2];
+ } vint;
+
+ union
+ {
+ double d;
+ unsigned long c[2];
+ } vlong;
+
+ union
+ {
+ double d;
+ unsigned short c[2];
+ } vshort;
+
+
+ /* Are we little or big endian? From Harbison&Steele. */
+ u.l = 1;
+ bigendian = (u.c[sizeof (long int) - 1] == 1);
+
+ zero_index = bigendian ? 1 : 0;
+
+ vint.d = 1.0;
+ vlong.d = 1.0;
+ vshort.d = 1.0;
+
+ if (sizeof(unsigned int) == 4)
+ {
+ if (vint.c[zero_index] != 0)
+ zero = 1;
+ }
+ else if (sizeof(unsigned long) == 4)
+ {
+ if (vlong.c[zero_index] != 0)
+ zero = 1;
+ }
+ else if (sizeof(unsigned short) == 4)
+ {
+ if (vshort.c[zero_index] != 0)
+ zero = 1;
+ }
+
+ exit (zero);
+}
+]])],
+ [ac_cv_c_double_middle_endian=no],
+ [ac_cv_c_double_middle_endian=yes],
+ [ac_cv_c_double_middle=unknown])])
+case $ac_cv_c_double_middle_endian in
+ yes)
+ m4_default([$1],
+ [AC_DEFINE([DOUBLE_MIDDLE_ENDIAN], 1,
+ [Define to 1 if your processor stores the words in a double in
+ middle-endian format (like some ARMs).])]) ;;
+ no)
+ $2 ;;
+ *)
+ m4_default([$3],
+ [AC_MSG_WARN([unknown double endianness
+presetting ac_cv_c_double_middle_endian=no (or yes) will help])]) ;;
+esac
+])# AC_C_DOUBLE_MIDDLE_ENDIAN
+
dnl ----------------------------------------------------------------------
dnl
@@ -642,11 +740,16 @@ dnl Try to find POSIX threads
dnl The usual pthread lib...
AC_CHECK_LIB(pthread, pthread_create, THR_LIBS="-lpthread")
-dnl FreeBSD has pthreads in special c library, c_r...
+dnl Very old versions of FreeBSD have pthreads in special c library, c_r...
if test "x$THR_LIBS" = "x"; then
AC_CHECK_LIB(c_r, pthread_create, THR_LIBS="-lc_r")
fi
+dnl QNX has pthreads in standard C library
+ if test "x$THR_LIBS" = "x"; then
+ AC_CHECK_FUNC(pthread_create, THR_LIBS="none_needed")
+ fi
+
dnl On ofs1 the '-pthread' switch should be used
if test "x$THR_LIBS" = "x"; then
AC_MSG_CHECKING([if the '-pthread' switch can be used])
@@ -667,6 +770,9 @@ dnl On ofs1 the '-pthread' switch should be used
if test "x$THR_LIBS" != "x"; then
THR_DEFS="$THR_DEFS -D_THREAD_SAFE -D_REENTRANT -DPOSIX_THREADS"
THR_LIB_NAME=pthread
+ if test "x$THR_LIBS" = "xnone_needed"; then
+ THR_LIBS=
+ fi
case $host_os in
solaris*)
THR_DEFS="$THR_DEFS -D_POSIX_PTHREAD_SEMANTICS" ;;
@@ -1337,6 +1443,14 @@ if test "$ac_cv_c_bigendian" = "yes"; then
AC_DEFINE(ETHR_BIGENDIAN, 1, [Define if bigendian])
fi
+case X$erl_xcomp_double_middle_endian in
+ X) ;;
+ Xyes|Xno|Xunknown) ac_cv_c_double_middle_endian=$erl_xcomp_double_middle_endian;;
+ *) AC_MSG_ERROR([Bad erl_xcomp_double_middle_endian value: $erl_xcomp_double_middle_endian]);;
+esac
+
+AC_C_DOUBLE_MIDDLE_ENDIAN
+
AC_ARG_ENABLE(native-ethr-impls,
AS_HELP_STRING([--disable-native-ethr-impls],
[disable native ethread implementations]),
@@ -1735,6 +1849,31 @@ case $erl_gethrvtime in
esac
])dnl
+dnl ----------------------------------------------------------------------
+dnl
+dnl LM_TRY_ENABLE_CFLAG
+dnl
+dnl
+dnl Tries a CFLAG and sees if it can be enabled without compiler errors
+dnl $1: textual cflag to add
+dnl $2: variable to store the modified CFLAG in
+dnl Usage example LM_TRY_ENABLE_CFLAG([-Werror=return-type], [CFLAGS])
+dnl
+dnl
+AC_DEFUN([LM_TRY_ENABLE_CFLAG], [
+ AC_MSG_CHECKING([if we can add $1 to $2 (via CFLAGS)])
+ saved_CFLAGS=$CFLAGS;
+ CFLAGS="$1 $$2";
+ AC_TRY_COMPILE([],[return 0;],can_enable_flag=true,can_enable_flag=false)
+ CFLAGS=$saved_CFLAGS;
+ if test "X$can_enable_flag" = "Xtrue"; then
+ AC_MSG_RESULT([yes])
+ AS_VAR_SET($2, "$1 $$2")
+ else
+ AC_MSG_RESULT([no])
+ fi
+])
+
dnl ERL_TRY_LINK_JAVA(CLASSES, FUNCTION-BODY
dnl [ACTION_IF_FOUND [, ACTION-IF-NOT-FOUND]])
dnl Freely inspired by AC_TRY_LINK. (Maybe better to create a
diff --git a/lib/wx/api_gen/gen_util.erl b/lib/wx/api_gen/gen_util.erl
index 4638d4c7ea..2ba1c6e16f 100644
--- a/lib/wx/api_gen/gen_util.erl
+++ b/lib/wx/api_gen/gen_util.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -49,8 +49,10 @@ get_taylor_made(Str, Name) ->
[dotall, {capture, all_but_first, list}]).
open_write(File) ->
+ open_write(File, []).
+open_write(File, Opts) ->
%% io:format("Generating ~s~n",[File]),
- {ok, Fd} = file:open(File++".temp", [write]),
+ {ok, Fd} = file:open(File++".temp", [write|Opts]),
put(current_file, {Fd,File}).
@@ -187,6 +189,8 @@ replace_and_remove([$| | R], Acc) ->
replace_and_remove(R, ["|"|Acc]);
replace_and_remove([$* | R], Acc) ->
replace_and_remove(R, ["*"|Acc]);
+replace_and_remove([$+ | R], Acc) ->
+ replace_and_remove(R, ["+"|Acc]);
replace_and_remove([$& | R], Acc) ->
replace_and_remove(R, [$&|Acc]);
replace_and_remove([$<,$< | R], Acc) ->
@@ -221,7 +225,7 @@ erl_copyright() ->
w("%%~n",[]),
w("%% %CopyrightBegin%~n",[]),
w("%%~n",[]),
- w("%% Copyright Ericsson AB ~p-2012. All Rights Reserved.~n",
+ w("%% Copyright Ericsson AB ~p-2013. All Rights Reserved.~n",
[StartYear]),
w("%%~n",[]),
w("%% The contents of this file are subject to the Erlang Public License,~n",[]),
@@ -241,7 +245,7 @@ c_copyright() ->
w("/*~n",[]),
w(" * %CopyrightBegin%~n",[]),
w(" *~n",[]),
- w(" * Copyright Ericsson AB 2008-2012. All Rights Reserved.~n",[]),
+ w(" * Copyright Ericsson AB 2008-2013. All Rights Reserved.~n",[]),
w(" *~n",[]),
w(" * The contents of this file are subject to the Erlang Public License,~n",[]),
w(" * Version 1.1, (the \"License\"); you may not use this file except in~n",[]),
diff --git a/lib/wx/api_gen/gl_gen.erl b/lib/wx/api_gen/gl_gen.erl
index 331ba32ba4..ea967c958f 100644
--- a/lib/wx/api_gen/gl_gen.erl
+++ b/lib/wx/api_gen/gl_gen.erl
@@ -185,11 +185,11 @@ parse_define([#xmlElement{name=name,content=[#xmlText{value="WINGDIAPI"++_}]}|_]
throw(skip);
parse_define([#xmlElement{name=name,content=[#xmlText{value=Name}]}|R], Def, Os) ->
parse_define(R, Def#def{name=Name}, Os);
-parse_define([#xmlElement{name=initializer,content=[#xmlText{value=V}]}|_],Def,_Os) ->
- Val0 = string:strip(V),
- try
+parse_define([#xmlElement{name=initializer,content=Contents}|_R],Def,_Os) ->
+ Val0 = extract_def2(Contents),
+ try
case Val0 of
- "0x" ++ Val1 ->
+ "0x" ++ Val1 ->
_ = http_util:hexlist_to_integer(Val1),
Def#def{val=Val1, type=hex};
_ ->
@@ -207,6 +207,23 @@ parse_define([_|R], D, Opts) ->
parse_define([], D, _Opts) ->
D.
+extract_def2([#xmlText{value=Val}|R]) ->
+ strip_comment(string:strip(Val)) ++ extract_def2(R);
+extract_def2([#xmlElement{content=Cs}|R]) ->
+ extract_def2(Cs) ++ extract_def2(R);
+extract_def2([]) -> [].
+
+strip_comment("/*" ++ Rest) ->
+ strip_comment_until_end(Rest);
+strip_comment("//" ++ _) -> [];
+strip_comment([H|R]) -> [H | strip_comment(R)];
+strip_comment([]) -> [].
+
+strip_comment_until_end("*/" ++ Rest) ->
+ strip_comment(Rest);
+strip_comment_until_end([_|R]) ->
+ strip_comment_until_end(R).
+
parse_func(Xml, Opts) ->
{Func,_} = foldl(fun(X,Acc) -> parse_func(X,Acc,Opts) end, {#func{},1}, Xml),
put(current_func, Func#func.name),
diff --git a/lib/wx/api_gen/gl_gen_erl.erl b/lib/wx/api_gen/gl_gen_erl.erl
index 25f89e4ad4..446521098e 100644
--- a/lib/wx/api_gen/gl_gen_erl.erl
+++ b/lib/wx/api_gen/gl_gen_erl.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -31,7 +31,7 @@
-import(lists, [foldl/3,foldr/3,reverse/1, keysearch/3, map/2, filter/2, max/1]).
-import(gen_util, [lowercase/1, lowercase_all/1, uppercase/1, uppercase_all/1,
- open_write/1, close/0, erl_copyright/0, w/2,
+ open_write/1, open_write/2, close/0, erl_copyright/0, w/2,
args/3, args/4, strip_name/2]).
gl_defines(Defs) ->
@@ -90,7 +90,8 @@ types() ->
].
gl_api(Fs) ->
- open_write("../src/gen/gl.erl"),
+ open_write("../src/gen/gl.erl", [{encoding,utf8}]),
+ w("%% -*- coding: utf-8 -*-~n~n", []),
erl_copyright(),
w("~n%% OPENGL API~n~n", []),
w("%% This file is generated DO NOT EDIT~n~n", []),
@@ -148,7 +149,8 @@ gl_api(Fs) ->
ok.
glu_api(Fs) ->
- open_write("../src/gen/glu.erl"),
+ open_write("../src/gen/glu.erl", [{encoding,utf8}]),
+ w("%% -*- coding: utf-8 -*-~n~n", []),
erl_copyright(),
w("~n%% OPENGL UTILITY API~n~n", []),
w("%% This file is generated DO NOT EDIT~n~n", []),
@@ -330,7 +332,7 @@ format_doc([{constant, Const}|Rest], Count) ->
w("`?~s'", [Const]),
format_doc(Rest, Count-length(Const)-8);
format_doc([{emphasis, Const}|Rest], Count) ->
- w("`~s'", [Const]),
+ w("`~ts'", [Const]),
format_doc(Rest, Count-length(Const)-7);
format_doc([{function, Func}|Rest], Count) ->
case Func of
@@ -377,7 +379,7 @@ format_doc([{fenced, Open, Close, Eq}|Rest], Count) ->
format_doc(Rest, Count);
format_doc([{code, Code}|Rest], Count) ->
- w("``~s''", [Code]),
+ w("``~ts''", [Code]),
format_doc(Rest, Count-length(Code)-7);
format_doc([para|Rest], _Count) ->
@@ -387,10 +389,10 @@ format_doc([break|Rest], _Count) ->
w("<br />~n%% ", []),
format_doc(Rest, ?LINE_LEN);
format_doc([{purpose, Purpose}, para | Doc], _Count) ->
- w("%% @doc ~s~n%%~n%% ", [uppercase(Purpose)]),
+ w("%% @doc ~ts~n%%~n%% ", [uppercase(Purpose)]),
format_doc(Doc, ?LINE_LEN);
format_doc([{purpose, Purpose} | Doc], _Count) ->
- w("%% @doc ~s~n%%~n%% ", [Purpose]),
+ w("%% @doc ~ts~n%%~n%% ", [Purpose]),
format_doc(Doc, ?LINE_LEN);
format_doc([listentry|Rest], _Count) ->
w("~n%%~n%% ", []),
@@ -398,11 +400,11 @@ format_doc([listentry|Rest], _Count) ->
format_doc([Str|Rest], Count) ->
case length(Str) of
Len when Len < Count ->
- w("~s", [Str]),
+ w("~ts", [Str]),
format_doc(Rest, Count-Len);
_ ->
{Str1, Str2} = split(Str, Count, []),
- w("~s~n%% ", [Str1]),
+ w("~ts~n%% ", [Str1]),
format_doc([Str2|Rest], ?LINE_LEN)
end;
format_doc([], _) -> ok.
diff --git a/lib/wx/api_gen/gl_scan_doc.erl b/lib/wx/api_gen/gl_scan_doc.erl
index fc7b7cf275..80b4826a30 100644
--- a/lib/wx/api_gen/gl_scan_doc.erl
+++ b/lib/wx/api_gen/gl_scan_doc.erl
@@ -2,7 +2,7 @@
%%--------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2012. All Rights Reserved.
+%% Copyright Ericsson AB 2012-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -56,8 +56,10 @@ gen_output({characters, String0}, #state{gen_output=true, type=Type, str=Str} =
mi -> case hd(Str) of
"/" -> String;
"*" -> String;
+ [215] -> String;
"+" -> String;
"-" -> String;
+ "=" -> String;
{fenced,_,_} -> String;
_ ->
[$ |String]
@@ -268,7 +270,9 @@ fix_str([$<|Str]) ->
fix_str([$>|Str]) ->
[$&,$g,$t,$;|fix_str(Str)];
fix_str("&times;"++Str) ->
- [$*|fix_str(Str)];
+ [215|fix_str(Str)];
+%% fix_str([215|Str]) ->
+%% [$*|fix_str(Str)];
fix_str("&Prime;"++Str) ->
[$"|fix_str(Str)];
fix_str("&CenterDot;"++Str) ->
@@ -277,10 +281,12 @@ fix_str("&af;"++Str) ->
fix_str(Str);
fix_str("&it;"++Str) ->
[$ |fix_str(Str)];
+fix_str("&nbsp;"++Str) ->
+ [$ |fix_str(Str)];
fix_str([$&|Str]) ->
[$&,$a,$m,$p,$; |fix_str(Str)];
-fix_str([C|Str]) when C > 255 ->
- fix_str(Str);
+%% fix_str([C|Str]) when C > 255 ->
+%% fix_str(Str);
fix_str([C|Str]) ->
[C|fix_str(Str)];
fix_str([]) -> [].
diff --git a/lib/wx/api_gen/wx_extra/wxEvtHandler.erl b/lib/wx/api_gen/wx_extra/wxEvtHandler.erl
index 23a34225ca..c5802af679 100644
--- a/lib/wx/api_gen/wx_extra/wxEvtHandler.erl
+++ b/lib/wx/api_gen/wx_extra/wxEvtHandler.erl
@@ -61,7 +61,7 @@ connect(This, EventType) ->
%% {userData, term()} An erlang term that will be sent with the event. Default: [].
-spec connect(This::wxEvtHandler(), EventType::wxEventType(), [Option]) -> ok when
Option :: {id, integer()} | {lastId, integer()} | {skip, boolean()} |
- {callback, function()} | {userData, term()}.
+ callback | {callback, function()} | {userData, term()}.
connect(This=#wx_ref{type=ThisT}, EventType, Options) ->
EvH = parse_opts(Options, #evh{et=EventType}),
?CLASS(ThisT,wxEvtHandler),
diff --git a/lib/wx/api_gen/wx_extra/wxPrintout.erl b/lib/wx/api_gen/wx_extra/wxPrintout.erl
index be8f2e2fa5..1dfd86ec62 100644
--- a/lib/wx/api_gen/wx_extra/wxPrintout.erl
+++ b/lib/wx/api_gen/wx_extra/wxPrintout.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -40,7 +40,8 @@ new(Title, OnPrintPage) ->
%% <pre>OnBeginDocument(This,StartPage,EndPage) -> boolean() </pre>
%% <pre>OnEndDocument(This) -> term() </pre>
%% <pre>HasPage(This,Page)} -> boolean() </pre>
-%% <pre>GetPageInfo(This) -> {MinPage:.integer(), MaxPage::integer(), PageFrom::integer(), PageTo::integer()} </pre>
+%% <pre>GetPageInfo(This) -> {MinPage::integer(), MaxPage::integer(),
+%% PageFrom::integer(), PageTo::integer()} </pre>
%% The <b>This</b> argument is the wxPrintout object reference to this object
%% <br /> NOTE: The callbacks may not call other processes.
new(Title, OnPrintPage, Opts) when is_list(Title), is_function(OnPrintPage), is_list(Opts) ->
diff --git a/lib/wx/api_gen/wx_gen.erl b/lib/wx/api_gen/wx_gen.erl
index 7f85151d03..2eb9d9d33d 100644
--- a/lib/wx/api_gen/wx_gen.erl
+++ b/lib/wx/api_gen/wx_gen.erl
@@ -103,7 +103,12 @@ mangle_info({class,CN,P,O,FL}) ->
Event = get_value(event,O, false),
Acc = get_value(acc, O, []),
{Fs,Fopts} = foldr(fun(FWO={F,FO},{Fl,Fopt}) when is_list(FO) ->
- {[F|Fl],[FWO|Fopt]};
+ Opt = case F of
+ {Name, ArgLen} when is_integer(ArgLen) ->
+ {Name, FO};
+ _ -> FWO
+ end,
+ {[F|Fl],[Opt|Fopt]};
(F,{Fl,Fopt}) ->
{[F|Fl], Fopt}
end, {[],[]}, FL),
@@ -421,22 +426,30 @@ select_member(Several, #class{name=Class,file=Orig}, Defs0, Opts) ->
parse_member(Data,MType,Virtual,Opts = #hs{fopt=Fopts}) ->
Parse = fun(Con,A) -> parse_member2(Con,Opts,A) end,
- Method = #method{name=MName,params=PS0} =
+ Method = #method{name=MName,params=PS0} =
foldl(Parse, #method{method_type=MType, virtual=Virtual}, Data),
%% Skip motif name's if it's last and optional
PS2 = case PS0 of %% Backward order..
- [#param{name="name",def=Def,type=#type{name="wxString"}}|PS1]
- when Def =/= none ->
+ [#param{name="name",def=Def,type=#type{name="wxString"}}|PS1]
+ when Def =/= none ->
PS1;
_ ->
PS0
end,
Sz = length(PS2),
- PS = map(fun(P=#param{name=PName}) ->
+ PS = map(fun(P=#param{name=PName}) ->
patch_param(MName,{Sz,PName},P,Fopts)
end, PS2),
- Alias = find_erl_alias_name(MName,PS,Fopts),
- Method#method{params=PS, alias=Alias}.
+ Alias = find_erl_alias_name(MName,PS,Fopts),
+ FOpts = case gb_trees:lookup(MName, Fopts) of
+ {value, FuncO} when is_list(FuncO) ->
+ case lists:keyfind({func,Sz}, 1, FuncO) of
+ false -> FuncO;
+ {_, FuncNO} -> FuncNO
+ end;
+ _ -> []
+ end,
+ Method#method{params=PS, alias=Alias, opts=FOpts}.
find_erl_alias_name(MName,Ps,Fopts) ->
case gb_trees:lookup(MName, Fopts) of
@@ -527,7 +540,7 @@ add_param2(P=#param{name=Name},#hs{fopt=FOpt},M0=#method{name=MName,params=Ps})
M0#method{params=[Patched|Ps]}
end.
-patch_param(Method, Name, P, Opt) ->
+patch_param(Method, Name, P, Opt) ->
case gb_trees:lookup(Method,Opt) of
none -> P;
{value,NoArg} when is_integer(NoArg) -> P;
@@ -560,11 +573,14 @@ handle_param_opt(both, P) -> P#param{in=both};
handle_param_opt({def,Def},P) -> P#param{def=Def};
handle_param_opt({type,Type}, P=#param{type=T}) -> P#param{type=T#type{name=Type}};
handle_param_opt({single,Opt}, P=#param{type=T}) -> P#param{type=T#type{single=Opt}};
+handle_param_opt({base,Enum={enum,Type}}, P=#param{type=T}) -> P#param{type=T#type{base=Enum, name=Type}};
handle_param_opt({base,Opt}, P=#param{type=T}) -> P#param{type=T#type{base=Opt}};
handle_param_opt({c_only,Opt},P) -> P#param{where=c, alt=Opt};
-handle_param_opt({ref, pointer}, P=#param{type=T}) ->
+handle_param_opt({ref, pointer}, P=#param{type=T}) ->
P#param{type=T#type{by_val=false,ref={pointer, 1}}};
-handle_param_opt({mod,Mods}, P=#param{type=T=#type{mod=Mods0}}) ->
+handle_param_opt({by_val, true}, P=#param{type=T}) ->
+ P#param{type=T#type{by_val=true}};
+handle_param_opt({mod,Mods}, P=#param{type=T=#type{mod=Mods0}}) ->
P#param{type=T#type{mod=Mods++Mods0}}.
get_opt(Opt, Method, Sz, Opts) ->
@@ -654,6 +670,12 @@ extract_type_info(#xmlElement{name=ref,attributes=As,content=[#xmlText{value=V}]
{value, #xmlAttribute{value = Kind}} = keysearch(kindref,#xmlAttribute.name,As),
{reverse(foldl(fun extract_type_info2/2, [], string:tokens(V, " "))) ++ Acc,
{Kind,Refid}};
+extract_type_info(#xmlElement{name=ref,attributes=As,content=[#xmlText{value=V}]},
+ {Acc,_}) ->
+ {value, #xmlAttribute{value = Refid}} = keysearch(refid,#xmlAttribute.name,As),
+ {value, #xmlAttribute{value = Kind}} = keysearch(kindref,#xmlAttribute.name,As),
+ {reverse(foldl(fun extract_type_info2/2, [], string:tokens(V, " "))) ++ Acc,
+ {Kind,Refid}};
extract_type_info(What,Acc) ->
?error({parse_error,What,Acc}).
@@ -704,11 +726,9 @@ parse_type2([N="wxTextPos"|R],Info,Opts,T) -> %%long
parse_type2(R,Info,Opts,T#type{name=N,base=int});
parse_type2([N="wxPrintQuality"|R],Info,Opts,T) ->
parse_type2(R,Info,Opts,T#type{name=N,base=int});
-parse_type2([N="wxPaperSize"|R],Info,Opts,T) ->
- parse_type2(R,Info,Opts,T#type{name=N,base=int});
parse_type2(["wxDataFormat"|_R],_Info,_Opts,T) ->
%% Hack Hack
- T#type{name="wxDataFormatId",base=int};
+ T#type{name="wxDataFormatId",base={enum,"wxDataFormatId"}};
parse_type2([N="wxArrayInt"|R],Info,Opts,T) ->
parse_type2(R,Info,Opts,T#type{name=N,base=int,single=array});
parse_type2([N="wxArrayDouble"|R],Info,Opts,T) ->
@@ -1251,7 +1271,7 @@ parse_enums([File|Files], Parsed) ->
case gb_sets:is_member(File,Parsed) of
false ->
FileName = filename:join(["wx_xml",File ++ "_8h.xml"]),
-%% io:format("Parse Enums in ~s ~n", [FileName]),
+ %%io:format("Parse Enums in ~s ~n", [FileName]),
case xmerl_scan:file(FileName, [{space, normalize}]) of
{error, enoent} ->
parse_enums(Files, gb_sets:add(File,Parsed));
@@ -1318,41 +1338,37 @@ extract_enum2([], N, _Id, Acc) ->
extract_enum3([#xmlElement{name=name,content=[#xmlText{value=Name}]}|R], Id, Acc) ->
case lists:keymember(Name, 1, Acc) of
- true -> %% Doxygen double includes some defs.
+ true -> %% Doxygen double includes some defs.
{Acc,Id};
false ->
case Id of
- This = {Str,Num} ->
+ This = {Str,Num} ->
extract_enum3(R, {Str, Num+1}, [{Name,This}|Acc]);
Val ->
extract_enum3(R, Val+1, [{Name,Val}|Acc])
end
end;
-extract_enum3([#xmlElement{name=initializer,
- content=Cs=[#xmlText{}|_]}|_],_Id,[{Name,_}|Acc]) ->
-
- String = lists:append([string:strip(C#xmlText.value) || C <- Cs]),
-
+extract_enum3([#xmlElement{name=initializer,content=Cs}|_],_Id,[{Name,_}|Acc]) ->
+ String = extract_def2(Cs),
Val0 = gen_util:tokens(String,"<& "),
-
- try
+ try
case Val0 of
- ["0x" ++ Val1] ->
+ ["0x" ++ Val1] ->
Val = http_util:hexlist_to_integer(Val1),
{[{Name, Val}|Acc], Val+1};
- [Single] ->
- Val = list_to_integer(Single),
- {[{Name, Val}|Acc], Val+1};
["1", "<<", Shift] ->
Val = 1 bsl list_to_integer(Shift),
{[{Name, Val}|Acc], Val+1};
- [_Str, "+", _What] ->
- Val = lists:append(Val0),
- {[{Name, {Val, 0}}|Acc], {Val,1}};
- _What ->
- %% io:format("~p Name ~p ~p~n",[?LINE, Name, Val0]),
- throw(below)
+ [Str, "+", What] ->
+ Val = list_to_integer(What),
+ {[{Name, {Str, Val}}|Acc], {Str,Val+1}};
+ [Single] ->
+ Val = list_to_integer(Single),
+ {[{Name, Val}|Acc], Val+1};
+ _ ->
+ %% io:format("~p Name ~p ~p ~p~n",[?LINE, Name, Val0, String]),
+ throw(below)
end
catch _:_ ->
{[{Name,{String,0}}|Acc], {String,1}}
@@ -1372,7 +1388,7 @@ extract_defs(Defs, File) ->
end.
extract_defs2(#xmlElement{name=memberdef,content=C},{Acc,Skip}) ->
- try
+ try
Res = {Name,_} = extract_def(C,undefined,Skip),
case gb_sets:is_member(Name,Skip) orelse lists:keymember(Name, 1, Acc) of
true -> {Acc,Skip};
@@ -1380,30 +1396,31 @@ extract_defs2(#xmlElement{name=memberdef,content=C},{Acc,Skip}) ->
end
catch throw:SkipName -> {Acc, gb_sets:add(SkipName,Skip)}
end.
-
+
extract_def([#xmlElement{name=name,content=[#xmlText{value=Name}]}|R], _N, Skip) ->
case Name of
"wxUSE" ++ _ ->
throw(Name);
"wx" ++ _ ->
extract_def(R, Name, Skip);
- _ ->
+ _ ->
throw(Name)
end;
extract_def([#xmlElement{name=param}|_],Name,_) ->
throw(Name);
-extract_def([#xmlElement{name=initializer,content=[#xmlText{value=Val0}]}|_],N,Skip) ->
+extract_def([#xmlElement{name=initializer,content=Cs}|_R],N,Skip) ->
+ Val0 = extract_def2(Cs),
case Val0 of
"0x" ++ Val1 -> {N, http_util:hexlist_to_integer(Val1)};
_ ->
try
Val = list_to_integer(Val0),
{N, Val}
- catch _:_ ->
+ catch _:_ ->
case def_is_ok(Val0, Skip) of
false ->
throw(N);
- NVal when is_integer(NVal) ->
+ NVal when is_integer(NVal) ->
{N, NVal};
NVal ->
{N, {NVal,0}}
@@ -1414,7 +1431,24 @@ extract_def([_|R],N,Skip) ->
extract_def(R,N,Skip);
extract_def(_,N,_) ->
throw(N).
-
+
+extract_def2([#xmlText{value=Val}|R]) ->
+ strip_comment(string:strip(Val)) ++ extract_def2(R);
+extract_def2([#xmlElement{content=Cs}|R]) ->
+ extract_def2(Cs) ++ extract_def2(R);
+extract_def2([]) -> [].
+
+strip_comment("/*" ++ Rest) ->
+ strip_comment_until_end(Rest);
+strip_comment("//" ++ _) -> [];
+strip_comment([H|R]) -> [H | strip_comment(R)];
+strip_comment([]) -> [].
+
+strip_comment_until_end("*/" ++ Rest) ->
+ strip_comment(Rest);
+strip_comment_until_end([_|R]) ->
+ strip_comment_until_end(R).
+
def_is_ok(Name, Skip) ->
Toks = gen_util:tokens(Name,"()| \\:"),
R = def_is_ok(Toks, Skip, []),
diff --git a/lib/wx/api_gen/wx_gen.hrl b/lib/wx/api_gen/wx_gen.hrl
index 426e3adfae..89577b9707 100644
--- a/lib/wx/api_gen/wx_gen.hrl
+++ b/lib/wx/api_gen/wx_gen.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -44,7 +44,8 @@
doc, % Extra documentation
virtual, % Is virtual?
pre_hook = [], % Pre hook before call in c-code
- post_hook = [] % Post hook after call in c-code
+ post_hook = [], % Post hook after call in c-code
+ opts = [] % Options
}
).
diff --git a/lib/wx/api_gen/wx_gen_cpp.erl b/lib/wx/api_gen/wx_gen_cpp.erl
index 2209e4a53b..293c97507e 100644
--- a/lib/wx/api_gen/wx_gen_cpp.erl
+++ b/lib/wx/api_gen/wx_gen_cpp.erl
@@ -118,12 +118,18 @@ gen_constructors(#class{name=Class, methods=Ms0}) ->
gen_constructor(_Class, #method{where=merged_c}) -> ok;
gen_constructor(_Class, #method{where=erl_no_opt}) -> ok;
-gen_constructor(Class, _M=#method{params=Ps}) ->
+gen_constructor(Class, _M=#method{params=Ps, opts=FOpts}) ->
Gen1 = fun(#param{name=N, type=T}) -> gen_type(T,1) ++ N end,
Gen2 = fun(#param{name=N, type=T}) -> gen_type(T,2) ++ N end,
CallA = fun(#param{name=N}) -> N end,
HaveMergedType = fun(#param{type={merged,_,_,_,_,_,_}}) -> true; (_) -> false end,
?WTC("gen_constructor"),
+ Endif = case lists:keysearch(deprecated, 1, FOpts) of
+ {value, {deprecated, IfDef}} ->
+ w("#if ~s~n", [IfDef]),
+ true;
+ _ -> false
+ end,
case lists:any(HaveMergedType, Ps) of
false ->
w(" E~s(~s) : ~s(~s) {};~n",
@@ -133,7 +139,9 @@ gen_constructor(Class, _M=#method{params=Ps}) ->
[Class,args(Gen1,",",Ps),Class,args(CallA,",",Ps)]),
w(" E~s(~s) : ~s(~s) {};~n",
[Class,args(Gen2,",",Ps),Class,args(CallA,",",Ps)])
- end.
+ end,
+ Endif andalso w("#endif~n", []),
+ ok.
gen_type(#type{name=Type, ref={pointer,1}, mod=Mod},_) ->
mods(Mod) ++ to_string(Type) ++ " * ";
@@ -162,6 +170,14 @@ gen_funcs(Defs) ->
w("#include \"wxe_macros.h\"~n"),
w("#include \"wxe_derived_dest.h\"~n~n"),
+ w("#if !wxCHECK_VERSION(2,9,0)~n", []),
+ [w("#define ~p int~n", [Enum]) ||
+ Enum <- [wxPenJoin, wxPenCap, wxImageResizeQuality, %%wxBitmapType,
+ wxPolygonFillMode, wxMappingMode, wxRasterOperationMode,
+ wxFloodFillStyle
+ ]],
+ w("#endif~n",[]),
+
w("void WxeApp::wxe_dispatch(wxeCommand& Ecmd)~n{~n"),
w(" char * bp = Ecmd.buffer;~n"),
w(" wxeMemEnv *memenv = getMemEnv(Ecmd.port);~n"),
@@ -292,10 +308,16 @@ gen_method(CName, M=#method{name=N,params=[Ps],method_type=destructor,id=MethodI
ignore
end,
M;
-gen_method(CName, M=#method{name=N,params=Ps0,type=T,method_type=MT,id=MethodId}) ->
+gen_method(CName, M=#method{name=N,params=Ps0,type=T,method_type=MT,id=MethodId, opts=FOpts}) ->
put(current_func, N),
put(bin_count,-1),
?WTC("gen_method"),
+ Endif = case lists:keysearch(deprecated, 1, FOpts) of
+ {value, {deprecated, IfDef}} ->
+ w("#if ~s~n", [IfDef]),
+ true;
+ _ -> false
+ end,
w("case ~s: { // ~s::~s~n", [wx_gen_erl:get_unique_name(MethodId),CName,N]),
Ps1 = declare_variables(void, Ps0),
{Ps2,Align} = decode_arguments(Ps1),
@@ -314,6 +336,7 @@ gen_method(CName, M=#method{name=N,params=Ps0,type=T,method_type=MT,id=MethodId
free_args(),
build_return_vals(T,Ps3),
w(" break;~n}~n", []),
+ Endif andalso w("#endif~n", []),
erase(current_func),
M.
@@ -746,7 +769,7 @@ return_res1(#type{name=Type,ref={pointer,_}, base={term,_}}) ->
{Type ++ " * Result = (" ++ Type ++ "*)", ""};
return_res1(#type{name=Type,ref={pointer,_}}) ->
{Type ++ " * Result = (" ++ Type ++ "*)", ""};
-return_res1(#type{name=Type,single=true,ref=reference}) ->
+return_res1(#type{name=Type,single=true,by_val=false,ref=reference}) ->
{Type ++ " * Result = &", ""};
return_res1(#type{name=Type,single=true,by_val=true})
when is_atom(Type) ->
@@ -758,7 +781,7 @@ return_res1(#type{name=Type,base={class,_},single=list,ref=reference}) ->
return_res1(#type{name=Type,base={comp,_,_},single=array,by_val=true}) ->
{Type ++ " Result = ", ""};
return_res1(#type{name=Type,single=true,by_val=true, base={class, _}}) ->
- %% Memory leak !!!!!! XXXX BUGBUG FIXME or doument!!
+ %% Temporary memory leak !!!!!!
case Type of
"wxImage" -> ok;
"wxFont" -> ok;
@@ -769,7 +792,6 @@ return_res1(#type{name=Type,single=true,by_val=true, base={class, _}}) ->
io:format("~s::~s Building return value of temp ~s~n",
[get(current_class),get(current_func),Type])
end,
- %% #class{id=Id} = get({class,Type}),
{Type ++ " * Result = new " ++ Type ++ "(", "); newPtr((void *) Result,"
++ "3, memenv);"};
return_res1(#type{base={enum,_Type},single=true,by_val=true}) ->
@@ -794,15 +816,14 @@ call_arg(#param{where=c, alt={length,Alt}}) when is_list(Alt) ->
call_arg(#param{where=c, alt={size,Id}}) when is_integer(Id) ->
%% It's a binary
"Ecmd.bin["++ integer_to_list(Id) ++ "]->size";
-call_arg(#param{name=N,def=Def,type=#type{name=Type,by_val=true,single=true,base=Base}})
+call_arg(#param{name=N,def=Def,type=#type{by_val=true,single=true,base=Base}})
when Base =:= int; Base =:= long; Base =:= float; Base =:= double; Base =:= bool ->
case Def of
- none -> "(" ++ to_string(Type) ++ ") *" ++ N;
+ none -> "*" ++ N;
_ -> N
end;
-
-call_arg(#param{name=N,type=#type{base={enum,Type}, by_val=true,single=true}}) ->
- "(" ++ enum_type(Type) ++") " ++ N;
+call_arg(#param{name=N,type=#type{base={enum,_Type}, by_val=true,single=true}}) ->
+ N;
call_arg(#param{name=N,type=#type{base={class,_},by_val=true,single=true}}) -> "*" ++ N;
call_arg(#param{name=N,type=#type{base={class,_},ref=reference,single=true}}) -> "*" ++ N;
call_arg(#param{name=N,type=#type{base=eventType}}) ->
@@ -823,8 +844,8 @@ call_arg(#param{name=N,type={merged,_,#type{base={class,_},single=true,
ref=Ref},_,_,_,_}})
when ByVal =:= true; Ref =:= reference ->
"*" ++ N;
-call_arg(#param{def=Def, type=void}) when Def =/= none -> Def;
-call_arg(#param{def=Def, type=voidp}) when Def =/= none -> Def;
+call_arg(#param{def=Def, type=void}) when Def =/= none -> Def;
+call_arg(#param{def=Def, type=voidp}) when Def =/= none -> "(void **) " ++ Def;
call_arg(#param{name=N,type=#type{base={ref,_},by_val=true,single=true}}) -> N;
call_arg(#param{name=N,type={merged,_,_,_,_,_,_}}) -> N.
@@ -932,10 +953,18 @@ build_ret(Name,_,#type{base={enum,_Type},single=true}) ->
w(" rt.addInt(~s);~n",[Name]);
build_ret(Name,_,#type{base={comp,_,{record, _}},single=true}) ->
w(" rt.add(~s);~n", [Name]);
+build_ret(Name,{ret,_},#type{base={comp,_,_},single=true, by_val=true}) ->
+ w(" rt.add(~s);~n",[Name]);
build_ret(Name,{ret,_},#type{base={comp,_,_},single=true, ref=reference}) ->
w(" rt.add((*~s));~n",[Name]);
build_ret(Name,_,#type{base={comp,_,_},single=true}) ->
w(" rt.add(~s);~n",[Name]);
+build_ret(Name = "ev->m_scanCode",_,#type{base=bool,single=true,by_val=true}) ->
+ %% Hardcoded workaround for 2.9 and later
+ w("#if !wxCHECK_VERSION(2,9,0)~n", []),
+ w(" rt.addBool(~s);~n",[Name]),
+ w("#else~n rt.addBool(false);~n",[]),
+ w("#endif~n",[]);
build_ret(Name,_,#type{base=bool,single=true,by_val=true}) ->
w(" rt.addBool(~s);~n",[Name]);
build_ret(Name,{arg, both},#type{base=int,single=true,mod=M}) ->
diff --git a/lib/wx/api_gen/wx_gen_erl.erl b/lib/wx/api_gen/wx_gen_erl.erl
index a8f23575f3..a999a869e6 100644
--- a/lib/wx/api_gen/wx_gen_erl.erl
+++ b/lib/wx/api_gen/wx_gen_erl.erl
@@ -70,8 +70,7 @@ gen_class1(C=#class{name=Name,parent="static",methods=Ms,options=_Opts}) ->
Exp = fun(M) -> gen_export(C,M) end,
ExportList = lists:usort(lists:append(lists:map(Exp,reverse(Ms)))),
- w("-export([~s]).~n~n", [args(fun(EF) -> EF end, ",", ExportList, 60)]),
-
+ w("-export([~s]).~n~n", [args(fun({EF,_}) -> EF end, ",", ExportList, 60)]),
Gen = fun(M) -> gen_method(Name,M) end,
NewMs = lists:map(Gen,reverse(Ms)),
@@ -134,7 +133,7 @@ gen_class1(C=#class{name=Name,parent=Parent,methods=Ms,options=Opts}) ->
w("-include(\"wxe.hrl\").~n",[]),
Exp = fun(M) -> gen_export(C,M) end,
ExportList = lists:usort(lists:append(lists:map(Exp,reverse(Ms)))),
- w("-export([~s]).~n~n", [args(fun(EF) -> EF end, ",", ExportList, 60)]),
+ w("-export([~s]).~n~n", [args(fun({EF,_}) -> EF end, ",", ExportList, 60)]),
w("%% inherited exports~n",[]),
Done0 = ["Destroy", "New", "Create", "destroy", "new", "create"],
Done = gb_sets:from_list(Done0 ++ [M|| #method{name=M} <- lists:append(Ms)]),
@@ -143,6 +142,10 @@ gen_class1(C=#class{name=Name,parent=Parent,methods=Ms,options=Opts}) ->
lists:usort(["parent_class/1"|InExported]),
60)]),
w("-export_type([~s/0]).~n", [Name]),
+ case lists:filter(fun({_F,Depr}) -> Depr end, ExportList) of
+ [] -> ok;
+ Depr -> w("-deprecated([~s]).~n~n", [args(fun({EF,_}) -> EF end, ",", Depr, 60)])
+ end,
w("%% @hidden~n", []),
parents_check(Parents),
w("-type ~s() :: wx:wx_object().~n", [Name]),
@@ -218,33 +221,40 @@ gen_export(#class{name=Class,abstract=Abs},Ms0) ->
case Res of
[] -> [];
[M=#method{where=taylormade}|_] ->
- [taylormade_export(Class, M)];
+ [deprecated(M, taylormade_export(Class, M))];
Ms ->
- GetF = fun(#method{method_type=constructor,where=W,params=Ps}) ->
+ GetF = fun(M=#method{method_type=constructor,where=W,params=Ps}) ->
{Args,Opts} = split_optional(Ps),
OptLen = case Opts of
[] -> 0;
_ when W =:= erl_no_opt -> 0;
_ -> 1
end,
- "new/" ++ integer_to_list(length(Args)+OptLen);
- (#method{method_type=destructor}) ->
+ deprecated(M, "new" ++ "/" ++ integer_to_list(length(Args)+OptLen));
+ (M=#method{method_type=destructor}) ->
case Abs of
true -> [];
- _ -> "destroy/1"
+ _ -> deprecated(M, "destroy/1")
end;
- (#method{name=N,alias=A,where=W, params=Ps}) ->
+ (M=#method{name=N,alias=A,where=W, params=Ps}) ->
{Args,Opts} = split_optional(Ps),
OptLen = case Opts of
[] -> 0;
_ when W =:= erl_no_opt -> 0;
_ -> 1
end,
- erl_func_name(N,A) ++ "/" ++ integer_to_list(length(Args) + OptLen)
+ deprecated(M, erl_func_name(N,A) ++ "/" ++ integer_to_list(length(Args) + OptLen))
end,
lists:map(GetF, Ms)
end.
+deprecated(#method{opts=FOpts}, FA) ->
+ case lists:keysearch(deprecated, 1, FOpts) of
+ {value, {deprecated, _}} ->
+ {FA,true};
+ _ ->
+ {FA,false}
+ end.
gen_method(Class,Ms0) ->
RemoveC = fun(#method{where=merged_c}) -> false;(_Other) -> true end,
@@ -832,15 +842,20 @@ doc_enum(_,Ps) ->
[doc_enum_type(Type,Name) || #param{name=Name, type=#type{base={enum,Type}}} <- Ps].
doc_enum_type(Type, Name) ->
- {Enum0, #enum{vals=Vals}} = wx_gen:get_enum(Type),
- Enum = case Enum0 of {_, E} -> E; E -> E end,
- Consts = get(consts),
- Format = fun({N,_What}) ->
- #const{name=N} = gb_trees:get(N, Consts),
- "?" ++ enum_name(N)
- end,
- Vs = args(Format, " | ", Vals),
- {uppercase(Enum),Name, Vs}.
+ try
+ {Enum0, #enum{vals=Vals}} = wx_gen:get_enum(Type),
+ Enum = case Enum0 of {_, E} -> E; E -> E end,
+ Consts = get(consts),
+ Format = fun({N,_What}) ->
+ #const{name=N} = gb_trees:get(N, Consts),
+ "?" ++ enum_name(N)
+ end,
+ Vs = args(Format, " | ", Vals),
+ {uppercase(Enum),Name, Vs}
+ catch _:_ ->
+ io:format("Warning missing enum type ~p~n", [Type]),
+ {uppercase(Type),Name,"integer"}
+ end.
doc_enum_desc([]) -> ok;
doc_enum_desc([{_Enum,Name,Vs}|R]) ->
@@ -1014,11 +1029,23 @@ align(64, 1, Str) -> {"0:32," ++ Str,0};
align(Sz, W, Str) -> align(Sz, W rem 2, Str).
enum_name(Name) ->
+ case enum_split(Name) of
+ {undefined, _} -> Name;
+ {_C, ErlName} -> ErlName
+ end.
+
+enum_split(Name) ->
case string:tokens(Name, ":") of
- [Name] -> Name;
- [C,N] -> C ++ "_" ++ N
+ [Name] -> {undefined, Name};
+ [C,N] -> {C, enum_name(C,N)}
end.
+enum_name(undefined, Name) -> Name;
+enum_name(Enum, Name) -> Enum ++ "_" ++ Name.
+
+enum_name_c(undefined, Name) -> Name;
+enum_name_c(Enum, Name) -> Enum ++ "::" ++ Name.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
gen_enums_ints() ->
@@ -1058,6 +1085,14 @@ build_enum_ints(#enum{from=From, vals=Vals},Done) ->
w("% From class ~s::~s~n",[Class, Name])
end,
+ Consts = get(consts),
+ Ignore = fun(Name) ->
+ case gb_trees:lookup(Name, Consts) of
+ {value, Const} -> Const;
+ none -> true
+ end
+ end,
+
Format = fun(#const{name="wxEVT_" ++ _}) ->
ignore; %% Ignore event macros they are not valid in our event model
(#const{name=Name,val=Value,is_const=true}) when is_number(Value) ->
@@ -1065,44 +1100,57 @@ build_enum_ints(#enum{from=From, vals=Vals},Done) ->
(#const{name=Name,val=Value,is_const=false}) when is_number(Value) ->
w("-define(~s, wxe_util:get_const(~s)).~n", [enum_name(Name),enum_name(Name)]);
(#const{name=Name,val={Str,0}}) ->
+ {EnumClass, EnumName} = enum_split(Name),
case string:tokens(Str, " |()") of
[Token] ->
- w("-define(~s, ~s).~n", [enum_name(Name),const_value(Token)]);
+ w("-define(~s, ~s).~n", [EnumName,const_value(Token, EnumClass, Ignore)]);
Tokens ->
- Def = args(fun(T) -> const_value(T) end, " bor ", Tokens),
- w("-define(~s, (~s)).~n", [enum_name(Name),Def])
+ Def = args(fun(T) -> const_value(T, EnumClass, Ignore) end, " bor ", Tokens),
+ w("-define(~s, (~s)).~n", [EnumName, Def])
end;
(#const{name=Name,val={Str,N}}) ->
+ {EnumClass, EnumName} = enum_split(Name),
case string:tokens(Str, " |()") of
[Token] ->
- w("-define(~s, (?~s+~p)).~n", [enum_name(Name),Token,N])
+ w("-define(~s, (~s+~p)).~n", [EnumName,const_value(Token, EnumClass, Ignore),N])
end
end,
- Consts = get(consts),
+
Write = fun({Name,_What}, Skip) ->
- case gb_sets:is_member(Name,Skip) of
- true ->
- Skip;
- false ->
- case gb_trees:lookup(Name, Consts) of
- {value, Const} ->
- Format(Const),
- gb_sets:add(Name,Skip);
- none -> Skip
- end
+ case gb_sets:is_member(Name,Skip) orelse Ignore(Name) of
+ true -> Skip;
+ Const ->
+ try Format(Const)
+ catch {unknown_value, _Error} ->
+ %% io:format("Const ~s uses unknown define ~p ignoring~n", [Name, _Error]),
+ ok
+ end,
+ gb_sets:add(Name,Skip)
end
end,
lists:foldl(Write, Done, Vals).
-const_value(V) when is_integer(V) -> integer_to_list(V);
-const_value(V = "16#" ++ IntList) ->
+const_value(V,_,_) when is_integer(V) -> integer_to_list(V);
+const_value(V = "16#" ++ IntList,_,_) ->
_ = http_util:hexlist_to_integer(IntList), %% ASSERT
V;
-const_value(V0) ->
+const_value(V0, EnumClass, Ignore) ->
try
_ = list_to_integer(V0),
V0
- catch _:_ -> [$?|V0]
+ catch _:_ ->
+ EEnum = enum_name(EnumClass, V0),
+ CEnum = enum_name_c(EnumClass, V0),
+ case Ignore(CEnum) of
+ true when CEnum == V0 ->
+ throw({unknown_value, EEnum});
+ true ->
+ case Ignore(V0) of
+ true -> throw({unknown_value, EEnum});
+ _ -> [$?|V0]
+ end;
+ _ -> [$?|EEnum]
+ end
end.
gen_event_recs() ->
diff --git a/lib/wx/api_gen/wxapi.conf b/lib/wx/api_gen/wxapi.conf
index 1f6225ce60..94142ff6ba 100644
--- a/lib/wx/api_gen/wxapi.conf
+++ b/lib/wx/api_gen/wxapi.conf
@@ -148,7 +148,8 @@
{class, wxTopLevelWindowGTK, wxWindow,
[{alias, [{wxTopLevelWindowGTK, wxTopLevelWindow}]}],
- ['GetIcon','GetIcons','GetTitle','IsActive','Iconize',
+ [{'GetIcon', [{return, {by_val, true}}]},
+ 'GetIcons','GetTitle','IsActive','Iconize',
'IsFullScreen','IsIconized','IsMaximized','Maximize',
'RequestUserAttention','SetIcon','SetIcons',
'CenterOnScreen', 'CentreOnScreen',
@@ -279,8 +280,11 @@
{class, wxGridCellEditor, root, [],
['Create',
'IsCreated', 'SetSize', 'Show',
- 'PaintBackground', 'BeginEdit', 'EndEdit', 'Reset', 'StartingKey',
- 'StartingClick', 'HandleReturn'
+ {'PaintBackground', [{deprecated, "!wxCHECK_VERSION(2,9,0)"}]},
+ 'BeginEdit',
+ {'EndEdit', [{deprecated, "!wxCHECK_VERSION(2,9,0)"}]},
+ 'Reset', 'StartingKey',
+ 'StartingClick', 'HandleReturn'
%'Destroy','Clone','~wxGridCellEditor',
]}.
@@ -319,17 +323,25 @@
{class, wxDC, object,
[{skip, [{'DrawEllipse',5},{'DrawRectangle',5},
{'DrawRoundedRectangle',6},{'SetClippingRegion',5}]}],
- [{'Blit',7},'CalcBoundingBox','Clear','ComputeScaleAndOrigin',{'CrossHair',1},
+ [{{'Blit',7},[{"rop", [{base, {enum, "wxRasterOperationMode"}}]}]},
+ 'CalcBoundingBox','Clear',
+ {'ComputeScaleAndOrigin',[{deprecated, "!wxCHECK_VERSION(2,9,0)"}]},
+ {'CrossHair',1},
'DestroyClippingRegion','DeviceToLogicalX','DeviceToLogicalXRel',
'DeviceToLogicalY','DeviceToLogicalYRel',{'DrawArc',3},{'DrawBitmap',3},
{'DrawCheckMark',1},{'DrawCircle',2},'DrawEllipse',{'DrawEllipticArc',4},
{'DrawIcon',2},{'DrawLabel',4},{'DrawLine',2},
{'DrawLines', [{"n",{c_only,{length,"points"}}}]},
- {'DrawPolygon', [{"n",{c_only,{length,"points"}}}]}, %'DrawPolyPolygon',
+ {'DrawPolygon', [{"n",{c_only,{length,"points"}}},
+ {"fillStyle", [{base, {enum, "wxPolygonFillMode"}}]}
+ ]},
+ %%'DrawPolyPolygon',
{'DrawPoint',1},'DrawRectangle',
{'DrawRotatedText',3}, 'DrawRoundedRectangle',%'DrawSpline',
{'DrawText',2},
- 'EndDoc','EndPage',{'FloodFill',3},'GetBackground','GetBackgroundMode',
+ 'EndDoc','EndPage',
+ {{'FloodFill',3},[{"style", [{base, {enum, "wxFloodFillStyle"}}]}]},
+ 'GetBackground','GetBackgroundMode',
'GetBrush','GetCharHeight','GetCharWidth',{'GetClippingBox',[{"rect", skip_member}]},
'GetFont','GetLayoutDirection','GetLogicalFunction','GetMapMode','GetMultiLineTextExtent',
{'GetPartialTextExtents', [{"widths", out}]},
@@ -340,21 +352,33 @@
'LogicalToDeviceX','LogicalToDeviceXRel','LogicalToDeviceY','LogicalToDeviceYRel',
'MaxX','MaxY','MinX','MinY','IsOk','ResetBoundingBox','SetAxisOrientation',
'SetBackground','SetBackgroundMode','SetBrush','SetClippingRegion','SetDeviceOrigin',
- 'SetFont','SetLayoutDirection','SetLogicalFunction','SetMapMode', 'SetPalette',
+ 'SetFont','SetLayoutDirection',
+ {'SetLogicalFunction', [{"function", [{base, {enum, "wxRasterOperationMode"}}]}]},
+ {'SetMapMode', [{"mode", [{base, {enum, "wxMappingMode"}}]}]},
+ 'SetPalette',
'SetPen','SetTextBackground','SetTextForeground','SetUserScale','StartDoc','StartPage']}.
{class,wxMirrorDC, wxDC, [], ['wxMirrorDC', '~wxMirrorDC']}.
{class,wxScreenDC, wxDC, [], ['wxScreenDC', '~wxScreenDC']}.
-{class,wxPostScriptDC,wxDC,[],['wxPostScriptDC','~wxPostScriptDC','SetResolution','GetResolution']}.
-{class,wxWindowDC, wxDC, [], ['wxWindowDC', '~wxWindowDC']}.
-{class,wxClientDC,wxWindowDC,[],['wxClientDC', '~wxClientDC']}.
-{class,wxPaintDC, wxWindowDC, [], ['wxPaintDC', '~wxPaintDC']}.
+{class,wxPostScriptDC,wxDC,[],
+ ['wxPostScriptDC','~wxPostScriptDC',
+ {'SetResolution', [{deprecated, "!wxCHECK_VERSION(2,9,0)"}]},
+ {'GetResolution', [{deprecated, "!wxCHECK_VERSION(2,9,0)"}]}]}.
+{class,wxWindowDC, wxDC, [],
+ [{'wxWindowDC', [{{func, 0}, [{deprecated, "!wxCHECK_VERSION(2,9,0)"}]}]},
+ '~wxWindowDC']}.
+{class,wxClientDC,wxWindowDC,[],
+ [{'wxClientDC', [{{func, 0}, [{deprecated, "!wxCHECK_VERSION(2,9,0)"}]}]},
+ '~wxClientDC']}.
+{class,wxPaintDC, wxWindowDC, [],
+ [{'wxPaintDC', [{{func, 0}, [{deprecated, "!wxCHECK_VERSION(2,9,0)"}]}]},
+ '~wxPaintDC']}.
%%{class,wxPrinterDC, wxDC, [], ['wxPrinterDC','GetPaperRect']}. Not in GTK
{class,wxMemoryDC, wxDC, [], ['wxMemoryDC', '~wxMemoryDC','SelectObject','SelectObjectAsSource']}.
{class,wxBufferedDC,wxMemoryDC,[],['wxBufferedDC','~wxBufferedDC','Init']}.
{class,wxBufferedPaintDC,wxBufferedDC,[],['wxBufferedPaintDC', '~wxBufferedPaintDC']}.
%% Only a typedef!
-%%{class,wxAutoBufferedPaintDC,wxBufferedPaintDC,[],['wxAutoBufferedPaintDC']}.
+%%{class,wxAutoBufferedPaintDC,wxBufferedPaintDC,[],['wxAutoBufferedPaintDC']}.
{class, wxGraphicsObject, object, [{ifdef, wxUSE_GRAPHICS_CONTEXT}],
['~wxGraphicsObject', 'GetRenderer','IsNull']}.
@@ -362,13 +386,17 @@
[{ifdef, wxUSE_GRAPHICS_CONTEXT}, {skip, [{'StrokeLines',4}]}],
['~wxGraphicsContext',
'Create', %%CreateFromNative CreateFromNativeWindow
- 'CreatePen','CreateBrush','CreateRadialGradientBrush',
- 'CreateLinearGradientBrush','CreateFont','CreateMatrix',
+ 'CreatePen','CreateBrush',
+ {'CreateRadialGradientBrush', [{deprecated, "!wxCHECK_VERSION(2,9,0)"}]},
+ {'CreateLinearGradientBrush', [{deprecated, "!wxCHECK_VERSION(2,9,0)"}]},
+ 'CreateFont','CreateMatrix',
'CreatePath','Clip','ResetClip',
'DrawBitmap','DrawEllipse','DrawIcon',
- {'DrawLines', [{"n",{c_only,{length,"points"}}}, {"points", {single,array}}]},
- 'DrawPath',
- 'DrawRectangle','DrawRoundedRectangle','DrawText','FillPath',
+ {'DrawLines', [{"n",{c_only,{length,"points"}}}, {"points", {single,array}},
+ {"fillStyle", [{base, {enum, "wxPolygonFillMode"}}]}]},
+ {'DrawPath',[{"fillStyle", [{base, {enum, "wxPolygonFillMode"}}]}]},
+ 'DrawRectangle','DrawRoundedRectangle','DrawText',
+ {'FillPath',[{"fillStyle", [{base, {enum, "wxPolygonFillMode"}}]}]},
'StrokePath', %% 'GetNativeContext',
{'GetPartialTextExtents', [{"widths", out}]},
'GetTextExtent','Rotate','Scale','Translate',
@@ -383,15 +411,18 @@
{class, wxGraphicsPath, wxGraphicsObject, [{ifdef, wxUSE_GRAPHICS_CONTEXT}],
['MoveToPoint','AddArc','AddArcToPoint','AddCircle','AddCurveToPoint',
'AddEllipse','AddLineToPoint','AddPath','AddQuadCurveToPoint',
- 'AddRectangle','AddRoundedRectangle','CloseSubpath','Contains',
- 'GetBox','GetCurrentPoint','Transform'
+ 'AddRectangle','AddRoundedRectangle','CloseSubpath',
+ {'Contains', [{"fillStyle", [{base, {enum, "wxPolygonFillMode"}}]}]},
+ 'GetBox','GetCurrentPoint','Transform'
%'GetNativePath','UnGetNativePath'
]}.
{class, wxGraphicsRenderer, object, [{ifdef, wxUSE_GRAPHICS_CONTEXT}],
['GetDefaultRenderer','CreateContext',
%%'CreateContextFromNativeContext', 'CreateContextFromNativeWindow',
'CreatePen','CreateBrush',
- 'CreateLinearGradientBrush','CreateRadialGradientBrush','CreateFont',
+ {'CreateLinearGradientBrush', [{deprecated, "!wxCHECK_VERSION(2,9,0)"}]},
+ {'CreateRadialGradientBrush', [{deprecated, "!wxCHECK_VERSION(2,9,0)"}]},
+ 'CreateFont',
'CreateMatrix','CreatePath']}.
{class, wxGraphicsPen, wxGraphicsObject,[{ifdef, wxUSE_GRAPHICS_CONTEXT}], []}.
@@ -501,10 +532,12 @@
'CopyFromBitmap','~wxIcon']}.
{class, wxIconBundle, root, [],
- ['wxIconBundle','~wxIconBundle','AddIcon','GetIcon']}.
+ ['wxIconBundle','~wxIconBundle','AddIcon',
+ {'GetIcon', [{return, {by_val, true}}]}]}.
{class, wxCursor, wxBitmap,[],
- [{'wxCursor',[{"bits",[in,{base,binary},{single,true}]},
+ [{'wxCursor',[{{func, 5}, [{deprecated, "!wxCHECK_VERSION(2,9,0)"}]},
+ {"bits",[in,{base,binary},{single,true}]},
{"maskBits",nowhere},{"fg",nowhere},{"bg",nowhere}]},
'~wxCursor','Ok']}.
@@ -549,16 +582,22 @@
{'GetAlpha',[{{0,return},{base,{binary,"(This->GetWidth()*This->GetHeight())"}}}]},
'GetBlue',
{'GetData', [{return,{base,{binary,"(This->GetWidth()*This->GetHeight()*3)"}}}]},
- 'GetGreen', 'GetImageCount', %'GetHandlers',
+ 'GetGreen',
+ {'GetImageCount', [{"type", [{base, {enum, "wxBitmapType"}}]}]},
+ %%'GetHandlers',
'GetHeight','GetMaskBlue','GetMaskGreen',
'GetMaskRed','GetOrFindMaskColour','GetPalette',
'GetRed','GetSubImage', 'GetWidth',%%':HSVValue', 'HSVtoRGB',
'HasAlpha','HasMask','GetOption','GetOptionInt','HasOption',
'InitAlpha','InitStandardHandlers',%'InsertHandler',
'IsTransparent', 'LoadFile','Ok',%%RGBValue 'RGBtoHSV',
- 'RemoveHandler','Mirror','Replace','Rescale','Resize',
+ 'RemoveHandler','Mirror','Replace',
+ {'Rescale', [{"quality", [{base, {enum, "wxImageResizeQuality"}}]}]},
+ 'Resize',
'Rotate', 'RotateHue',
- 'Rotate90','SaveFile','Scale','Size',
+ 'Rotate90','SaveFile',
+ {'Scale', [{"quality", [{base, {enum, "wxImageResizeQuality"}}]}]},
+ 'Size',
{'SetAlpha', [{{2,"alpha"},[in,{base,binary}, {def, none}]},
{{2,pre_hook},
[{c, "if(!static_data) {"
@@ -576,15 +615,20 @@
'SetRGB']}.
{class, wxBrush, object, [],
- ['wxBrush','~wxBrush','GetColour','GetStipple','GetStyle',
+ ['wxBrush','~wxBrush',
+ {'GetColour', [{return, {by_val, true}}]},
+ 'GetStipple','GetStyle',
'IsHatch','IsOk','SetColour','SetStipple','SetStyle']}.
{class, wxPen, object, [],
- ['wxPen','~wxPen','GetCap','GetColour',
+ ['wxPen','~wxPen','GetCap',
+ {'GetColour', [{return, {by_val, true}}]},
%%'GetDashes', %'GetStipple',
- 'GetJoin', 'GetStyle','GetWidth','IsOk','SetCap','SetColour',
+ 'GetJoin', 'GetStyle','GetWidth','IsOk',
+ {'SetCap', [{"capStyle", [{base, {enum, "wxPenCap"}}]}]},
+ 'SetColour',
%%'SetDashes', %'SetStipple',
- 'SetJoin', 'SetStyle','SetWidth']}.
+ {'SetJoin', [{"joinStyle", [{base, {enum, "wxPenJoin"}}]}]}, 'SetStyle','SetWidth']}.
{enum, wxRegionContain, "wx"}.
@@ -683,8 +727,11 @@
['wxButton','~wxButton','Create',%'GetLabel',
'GetDefaultSize', 'SetDefault','SetLabel']}.
{class, wxBitmapButton, wxButton, [],
- ['wxBitmapButton','~wxBitmapButton','Create','GetBitmapDisabled',
- 'GetBitmapFocus','GetBitmapLabel','GetBitmapSelected',
+ ['wxBitmapButton','~wxBitmapButton','Create',
+ {'GetBitmapDisabled', [{return, {by_val, true}}]},
+ {'GetBitmapFocus', [{return, {by_val, true}}]},
+ {'GetBitmapLabel', [{return, {by_val, true}}]},
+ {'GetBitmapSelected', [{return, {by_val, true}}]},
'SetBitmapDisabled','SetBitmapFocus','SetBitmapLabel','SetBitmapSelected']}.
{class, wxToggleButton, wxControl, [],
['wxToggleButton','~wxToggleButton','Create','GetValue','SetValue']}.
@@ -693,8 +740,10 @@
{class, wxDateTime, root, [ignore], []}. %% Only for ifdefs and enums
{class, wxCalendarCtrl, wxControl, [],
- ['wxCalendarCtrl','Create','~wxCalendarCtrl','SetDate','GetDate',
- 'EnableYearChange','EnableMonthChange','EnableHolidayDisplay',
+ ['wxCalendarCtrl','Create','~wxCalendarCtrl','SetDate',
+ {'GetDate', [{return, {by_val, true}}]},
+ {'EnableYearChange', [{deprecated, "!wxCHECK_VERSION(2,9,0)"}]}, %% Temp bug in wx I assume
+ 'EnableMonthChange','EnableHolidayDisplay',
'SetHeaderColours','GetHeaderColourFg','GetHeaderColourBg',
'SetHighlightColours','GetHighlightColourFg','GetHighlightColourBg',
'SetHolidayColours','GetHolidayColourFg','GetHolidayColourBg',
@@ -816,8 +865,9 @@
{enum, wxTextAttrAlignment, "wxTEXT_ALIGNMENT_"}.
{class, wxTextAttr, root, [],
- ['wxTextAttr','GetAlignment','GetBackgroundColour','GetFont','GetLeftIndent',
- 'GetLeftSubIndent','GetRightIndent','GetTabs','GetTextColour',
+ ['wxTextAttr','GetAlignment','GetBackgroundColour',
+ {'GetFont', [{return, {by_val, true}}]},
+ 'GetLeftIndent','GetLeftSubIndent','GetRightIndent','GetTabs','GetTextColour',
'HasBackgroundColour','HasFont','HasTextColour','GetFlags','IsDefault',
'SetAlignment','SetBackgroundColour','SetFlags','SetFont','SetLeftIndent',
'SetRightIndent','SetTabs','SetTextColour']}.
@@ -1233,7 +1283,8 @@
{class, wxMDIParentFrame, wxFrame, [],
[
- 'wxMDIParentFrame', '~wxMDIParentFrame', 'ActivateNext', 'ActivatePrevious',
+ 'wxMDIParentFrame',
+ '~wxMDIParentFrame', 'ActivateNext', 'ActivatePrevious',
'ArrangeIcons', 'Cascade', 'Create',
%%'GetClientSize', 'GetToolBar', 'SetToolBar', defined in parent
'GetActiveChild', 'GetClientWindow',
@@ -1244,8 +1295,9 @@
{class, wxMDIChildFrame, wxFrame, [],
['wxMDIChildFrame','~wxMDIChildFrame','Activate','Create','Maximize','Restore']}.
-{class, wxMDIClientWindow, wxWindow, [],
- ['wxMDIClientWindow','~wxMDIClientWindow','CreateClient']}.
+{class, wxMDIClientWindow, wxWindow, [],
+ [{'wxMDIClientWindow', [{{func, 2}, [{deprecated, "!wxCHECK_VERSION(2,9,0)"}]}]},
+ '~wxMDIClientWindow','CreateClient']}.
{class, wxLayoutAlgorithm, object, [],
['wxLayoutAlgorithm', '~wxLayoutAlgorithm',
@@ -1310,12 +1362,7 @@
[wxEVT_LEFT_DOWN,wxEVT_LEFT_UP,wxEVT_MIDDLE_DOWN,wxEVT_MIDDLE_UP,
wxEVT_RIGHT_DOWN,wxEVT_RIGHT_UP,wxEVT_MOTION,wxEVT_ENTER_WINDOW,
wxEVT_LEAVE_WINDOW,wxEVT_LEFT_DCLICK,wxEVT_MIDDLE_DCLICK,
- wxEVT_RIGHT_DCLICK,wxEVT_MOUSEWHEEL,
- wxEVT_NC_LEFT_DOWN,wxEVT_NC_LEFT_UP,
- wxEVT_NC_MIDDLE_DOWN,wxEVT_NC_MIDDLE_UP,wxEVT_NC_RIGHT_DOWN,
- wxEVT_NC_RIGHT_UP,wxEVT_NC_MOTION,wxEVT_NC_ENTER_WINDOW,
- wxEVT_NC_LEAVE_WINDOW,wxEVT_NC_LEFT_DCLICK,wxEVT_NC_MIDDLE_DCLICK,
- wxEVT_NC_RIGHT_DCLICK]}],
+ wxEVT_RIGHT_DCLICK,wxEVT_MOUSEWHEEL]}],
['AltDown','Button','ButtonDClick','ButtonDown','ButtonUp','CmdDown','ControlDown',
'Dragging', 'Entering', 'GetButton', 'GetPosition', 'GetLogicalPosition',
'GetLinesPerAction', 'GetWheelRotation', 'GetWheelDelta', 'GetX', 'GetY',
@@ -1345,8 +1392,8 @@
['GetSize']}.
{class, wxMoveEvent, wxEvent, [{event,[wxEVT_MOVE]}],
['GetPosition']}.
-{class, wxPaintEvent, wxEvent, [{event,[wxEVT_PAINT,wxEVT_PAINT_ICON]}],[]}.
-{class, wxNcPaintEvent, wxEvent, [{event,[wxEVT_NC_PAINT]}],[]}.
+{class, wxPaintEvent, wxEvent, [{event,[wxEVT_PAINT]}],[]}.
+%%{class, wxNcPaintEvent, wxEvent, [{event,[wxEVT_NC_PAINT]}],[]}.
{class, wxEraseEvent, wxEvent,
[{acc, [{m_dc, "GetDC()"}]},
{event, [wxEVT_ERASE_BACKGROUND]}],
@@ -1415,7 +1462,8 @@
['GetPosition','SetPosition']}.
{enum, wxIdleMode, "wxIDLE_"}.
{class, wxIdleEvent, wxEvent, [{event,[wxEVT_IDLE]}],
- ['CanSend','GetMode','RequestMore','MoreRequested','SetMode']}.
+ [{'CanSend', [{deprecated, "!wxCHECK_VERSION(2,9,0)"}]},
+ 'GetMode','RequestMore','MoreRequested','SetMode']}.
{class, wxGridEvent, wxNotifyEvent,
[{acc, [{m_row, "GetRow()"}, {m_col, "GetCol()"}, {m_x, "GetPosition().x"},{m_y, "GetPosition().y"},
{m_selecting, "Selecting()"},{m_control,"ControlDown()"},
@@ -1634,7 +1682,7 @@
'StyleSetFont', 'StyleSetFontAttr', 'StyleSetCharacterSet', 'StyleSetFontEncoding',
'CmdKeyExecute',
'SetMargins', {'GetSelection', [{"startPos", [out]}, {"endPos",[out]}]},
- 'PointFromPosition', 'ScrollToLine', 'ScrollToColumn', 'SendMsg',
+ 'PointFromPosition', 'ScrollToLine', 'ScrollToColumn', %% 'SendMsg',
'SetVScrollBar', 'SetHScrollBar', 'GetLastKeydownProcessed', 'SetLastKeydownProcessed',
'SaveFile', 'LoadFile', 'DoDragOver', 'DoDropText', 'GetUseAntiAliasing',
{'AddTextRaw', [{"text", [in, {base, binary}]}]},
diff --git a/lib/wx/c_src/Makefile.in b/lib/wx/c_src/Makefile.in
index 1497ac4d16..1d17076d23 100644
--- a/lib/wx/c_src/Makefile.in
+++ b/lib/wx/c_src/Makefile.in
@@ -71,8 +71,7 @@ WX_OBJECTS = $(GENERAL_O) $(GENERATED_O) $(RC_FILE)
OBJECTS = $(WX_OBJECTS) $(GL_OBJECTS)
-TARGET_APIS = wxe_driver erl_gl
-TARGET_DIR = ../priv
+TARGET_DIR = ../priv/$(SYS_TYPE)
# -O2 -funroll-loops -ffast-math -fomit-frame-pointer
@@ -121,7 +120,7 @@ debug:
clean:
rm -f $(OBJECTS)
- rm -f ../priv/$(TARGET_DIR)/$(TARGET_API)$(SO_EXT)
+ rm -f $(TARGET_DIR)/*$(SO_EXT)
rm -f *~ erl_crash.dump
complete_clean:
diff --git a/lib/wx/c_src/gen/wxe_derived_dest.h b/lib/wx/c_src/gen/wxe_derived_dest.h
index 6b7a5378cb..5fdce27d4b 100644
--- a/lib/wx/c_src/gen/wxe_derived_dest.h
+++ b/lib/wx/c_src/gen/wxe_derived_dest.h
@@ -94,19 +94,25 @@ class EwxPostScriptDC : public wxPostScriptDC {
class EwxWindowDC : public wxWindowDC {
public: ~EwxWindowDC() {((WxeApp *)wxTheApp)->clearPtr(this);};
EwxWindowDC(wxWindow * win) : wxWindowDC(win) {};
+#if !wxCHECK_VERSION(2,9,0)
EwxWindowDC() : wxWindowDC() {};
+#endif
};
class EwxClientDC : public wxClientDC {
public: ~EwxClientDC() {((WxeApp *)wxTheApp)->clearPtr(this);};
EwxClientDC(wxWindow * win) : wxClientDC(win) {};
+#if !wxCHECK_VERSION(2,9,0)
EwxClientDC() : wxClientDC() {};
+#endif
};
class EwxPaintDC : public wxPaintDC {
public: ~EwxPaintDC() {((WxeApp *)wxTheApp)->clearPtr(this);};
EwxPaintDC(wxWindow * win) : wxPaintDC(win) {};
+#if !wxCHECK_VERSION(2,9,0)
EwxPaintDC() : wxPaintDC() {};
+#endif
};
class EwxMemoryDC : public wxMemoryDC {
@@ -182,7 +188,9 @@ class EwxIcon : public wxIcon {
class EwxCursor : public wxCursor {
public: ~EwxCursor() {((WxeApp *)wxTheApp)->clearPtr(this);};
+#if !wxCHECK_VERSION(2,9,0)
EwxCursor(const char * bits,int width,int height,int hotSpotX,int hotSpotY) : wxCursor(bits,width,height,hotSpotX,hotSpotY) {};
+#endif
EwxCursor(int cursorId) : wxCursor(cursorId) {};
EwxCursor(const wxImage& image) : wxCursor(image) {};
EwxCursor() : wxCursor() {};
@@ -286,7 +294,7 @@ class EwxStdDialogButtonSizer : public wxStdDialogButtonSizer {
class EwxFont : public wxFont {
public: ~EwxFont() {((WxeApp *)wxTheApp)->clearPtr(this);};
- EwxFont(int size,int family,int style,int weight,bool underlined,const wxString& face,wxFontEncoding encoding) : wxFont(size,family,style,weight,underlined,face,encoding) {};
+ EwxFont(int size,wxFontFamily family,wxFontStyle style,int weight,bool underlined,const wxString& face,wxFontEncoding encoding) : wxFont(size,family,style,weight,underlined,face,encoding) {};
EwxFont(const wxString& fontname) : wxFont(fontname) {};
EwxFont() : wxFont() {};
};
@@ -712,7 +720,9 @@ class EwxMDIChildFrame : public wxMDIChildFrame {
class EwxMDIClientWindow : public wxMDIClientWindow {
public: ~EwxMDIClientWindow() {((WxeApp *)wxTheApp)->clearPtr(this);};
+#if !wxCHECK_VERSION(2,9,0)
EwxMDIClientWindow(wxMDIParentFrame * parent,long style) : wxMDIClientWindow(parent,style) {};
+#endif
EwxMDIClientWindow() : wxMDIClientWindow() {};
};
diff --git a/lib/wx/c_src/gen/wxe_events.cpp b/lib/wx/c_src/gen/wxe_events.cpp
index 0afb02150a..7a3233a410 100644
--- a/lib/wx/c_src/gen/wxe_events.cpp
+++ b/lib/wx/c_src/gen/wxe_events.cpp
@@ -107,18 +107,6 @@ void initEventTable()
{wxEVT_MIDDLE_DCLICK, 167, "middle_dclick"},
{wxEVT_RIGHT_DCLICK, 167, "right_dclick"},
{wxEVT_MOUSEWHEEL, 167, "mousewheel"},
- {wxEVT_NC_LEFT_DOWN, 167, "nc_left_down"},
- {wxEVT_NC_LEFT_UP, 167, "nc_left_up"},
- {wxEVT_NC_MIDDLE_DOWN, 167, "nc_middle_down"},
- {wxEVT_NC_MIDDLE_UP, 167, "nc_middle_up"},
- {wxEVT_NC_RIGHT_DOWN, 167, "nc_right_down"},
- {wxEVT_NC_RIGHT_UP, 167, "nc_right_up"},
- {wxEVT_NC_MOTION, 167, "nc_motion"},
- {wxEVT_NC_ENTER_WINDOW, 167, "nc_enter_window"},
- {wxEVT_NC_LEAVE_WINDOW, 167, "nc_leave_window"},
- {wxEVT_NC_LEFT_DCLICK, 167, "nc_left_dclick"},
- {wxEVT_NC_MIDDLE_DCLICK, 167, "nc_middle_dclick"},
- {wxEVT_NC_RIGHT_DCLICK, 167, "nc_right_dclick"},
{wxEVT_SET_CURSOR, 168, "set_cursor"},
{wxEVT_CHAR, 169, "char"},
{wxEVT_CHAR_HOOK, 169, "char_hook"},
@@ -127,187 +115,185 @@ void initEventTable()
{wxEVT_SIZE, 170, "size"},
{wxEVT_MOVE, 171, "move"},
{wxEVT_PAINT, 172, "paint"},
- {wxEVT_PAINT_ICON, 172, "paint_icon"},
- {wxEVT_NC_PAINT, 173, "nc_paint"},
- {wxEVT_ERASE_BACKGROUND, 174, "erase_background"},
- {wxEVT_SET_FOCUS, 175, "set_focus"},
- {wxEVT_KILL_FOCUS, 175, "kill_focus"},
- {wxEVT_CHILD_FOCUS, 176, "child_focus"},
- {wxEVT_MENU_OPEN, 177, "menu_open"},
- {wxEVT_MENU_CLOSE, 177, "menu_close"},
- {wxEVT_MENU_HIGHLIGHT, 177, "menu_highlight"},
- {wxEVT_CLOSE_WINDOW, 178, "close_window"},
- {wxEVT_END_SESSION, 178, "end_session"},
- {wxEVT_QUERY_END_SESSION, 178, "query_end_session"},
- {wxEVT_SHOW, 179, "show"},
- {wxEVT_ICONIZE, 180, "iconize"},
- {wxEVT_MAXIMIZE, 181, "maximize"},
- {wxEVT_JOY_BUTTON_DOWN, 182, "joy_button_down"},
- {wxEVT_JOY_BUTTON_UP, 182, "joy_button_up"},
- {wxEVT_JOY_MOVE, 182, "joy_move"},
- {wxEVT_JOY_ZMOVE, 182, "joy_zmove"},
- {wxEVT_UPDATE_UI, 183, "update_ui"},
- {wxEVT_SYS_COLOUR_CHANGED, 184, "sys_colour_changed"},
- {wxEVT_MOUSE_CAPTURE_CHANGED, 185, "mouse_capture_changed"},
- {wxEVT_DISPLAY_CHANGED, 186, "display_changed"},
- {wxEVT_PALETTE_CHANGED, 187, "palette_changed"},
- {wxEVT_QUERY_NEW_PALETTE, 188, "query_new_palette"},
- {wxEVT_NAVIGATION_KEY, 189, "navigation_key"},
- {wxEVT_CREATE, 190, "create"},
- {wxEVT_DESTROY, 191, "destroy"},
- {wxEVT_HELP, 192, "help"},
- {wxEVT_DETAILED_HELP, 192, "detailed_help"},
- {wxEVT_CONTEXT_MENU, 193, "context_menu"},
- {wxEVT_IDLE, 194, "idle"},
- {wxEVT_GRID_CELL_LEFT_CLICK, 195, "grid_cell_left_click"},
- {wxEVT_GRID_CELL_RIGHT_CLICK, 195, "grid_cell_right_click"},
- {wxEVT_GRID_CELL_LEFT_DCLICK, 195, "grid_cell_left_dclick"},
- {wxEVT_GRID_CELL_RIGHT_DCLICK, 195, "grid_cell_right_dclick"},
- {wxEVT_GRID_LABEL_LEFT_CLICK, 195, "grid_label_left_click"},
- {wxEVT_GRID_LABEL_RIGHT_CLICK, 195, "grid_label_right_click"},
- {wxEVT_GRID_LABEL_LEFT_DCLICK, 195, "grid_label_left_dclick"},
- {wxEVT_GRID_LABEL_RIGHT_DCLICK, 195, "grid_label_right_dclick"},
- {wxEVT_GRID_ROW_SIZE, 195, "grid_row_size"},
- {wxEVT_GRID_COL_SIZE, 195, "grid_col_size"},
- {wxEVT_GRID_RANGE_SELECT, 195, "grid_range_select"},
- {wxEVT_GRID_CELL_CHANGE, 195, "grid_cell_change"},
- {wxEVT_GRID_SELECT_CELL, 195, "grid_select_cell"},
- {wxEVT_GRID_EDITOR_SHOWN, 195, "grid_editor_shown"},
- {wxEVT_GRID_EDITOR_HIDDEN, 195, "grid_editor_hidden"},
- {wxEVT_GRID_EDITOR_CREATED, 195, "grid_editor_created"},
- {wxEVT_GRID_CELL_BEGIN_DRAG, 195, "grid_cell_begin_drag"},
- {wxEVT_SASH_DRAGGED, 197, "sash_dragged"},
- {wxEVT_COMMAND_LIST_BEGIN_DRAG, 198, "command_list_begin_drag"},
- {wxEVT_COMMAND_LIST_BEGIN_RDRAG, 198, "command_list_begin_rdrag"},
- {wxEVT_COMMAND_LIST_BEGIN_LABEL_EDIT, 198, "command_list_begin_label_edit"},
- {wxEVT_COMMAND_LIST_END_LABEL_EDIT, 198, "command_list_end_label_edit"},
- {wxEVT_COMMAND_LIST_DELETE_ITEM, 198, "command_list_delete_item"},
- {wxEVT_COMMAND_LIST_DELETE_ALL_ITEMS, 198, "command_list_delete_all_items"},
- {wxEVT_COMMAND_LIST_KEY_DOWN, 198, "command_list_key_down"},
- {wxEVT_COMMAND_LIST_INSERT_ITEM, 198, "command_list_insert_item"},
- {wxEVT_COMMAND_LIST_COL_CLICK, 198, "command_list_col_click"},
- {wxEVT_COMMAND_LIST_COL_RIGHT_CLICK, 198, "command_list_col_right_click"},
- {wxEVT_COMMAND_LIST_COL_BEGIN_DRAG, 198, "command_list_col_begin_drag"},
- {wxEVT_COMMAND_LIST_COL_DRAGGING, 198, "command_list_col_dragging"},
- {wxEVT_COMMAND_LIST_COL_END_DRAG, 198, "command_list_col_end_drag"},
- {wxEVT_COMMAND_LIST_ITEM_SELECTED, 198, "command_list_item_selected"},
- {wxEVT_COMMAND_LIST_ITEM_DESELECTED, 198, "command_list_item_deselected"},
- {wxEVT_COMMAND_LIST_ITEM_RIGHT_CLICK, 198, "command_list_item_right_click"},
- {wxEVT_COMMAND_LIST_ITEM_MIDDLE_CLICK, 198, "command_list_item_middle_click"},
- {wxEVT_COMMAND_LIST_ITEM_ACTIVATED, 198, "command_list_item_activated"},
- {wxEVT_COMMAND_LIST_ITEM_FOCUSED, 198, "command_list_item_focused"},
- {wxEVT_COMMAND_LIST_CACHE_HINT, 198, "command_list_cache_hint"},
- {wxEVT_DATE_CHANGED, 199, "date_changed"},
- {wxEVT_CALENDAR_SEL_CHANGED, 200, "calendar_sel_changed"},
- {wxEVT_CALENDAR_DAY_CHANGED, 200, "calendar_day_changed"},
- {wxEVT_CALENDAR_MONTH_CHANGED, 200, "calendar_month_changed"},
- {wxEVT_CALENDAR_YEAR_CHANGED, 200, "calendar_year_changed"},
- {wxEVT_CALENDAR_DOUBLECLICKED, 200, "calendar_doubleclicked"},
- {wxEVT_CALENDAR_WEEKDAY_CLICKED, 200, "calendar_weekday_clicked"},
- {wxEVT_COMMAND_FILEPICKER_CHANGED, 201, "command_filepicker_changed"},
- {wxEVT_COMMAND_DIRPICKER_CHANGED, 201, "command_dirpicker_changed"},
- {wxEVT_COMMAND_COLOURPICKER_CHANGED, 202, "command_colourpicker_changed"},
- {wxEVT_COMMAND_FONTPICKER_CHANGED, 203, "command_fontpicker_changed"},
- {wxEVT_STC_CHANGE, 204, "stc_change"},
- {wxEVT_STC_STYLENEEDED, 204, "stc_styleneeded"},
- {wxEVT_STC_CHARADDED, 204, "stc_charadded"},
- {wxEVT_STC_SAVEPOINTREACHED, 204, "stc_savepointreached"},
- {wxEVT_STC_SAVEPOINTLEFT, 204, "stc_savepointleft"},
- {wxEVT_STC_ROMODIFYATTEMPT, 204, "stc_romodifyattempt"},
- {wxEVT_STC_KEY, 204, "stc_key"},
- {wxEVT_STC_DOUBLECLICK, 204, "stc_doubleclick"},
- {wxEVT_STC_UPDATEUI, 204, "stc_updateui"},
- {wxEVT_STC_MODIFIED, 204, "stc_modified"},
- {wxEVT_STC_MACRORECORD, 204, "stc_macrorecord"},
- {wxEVT_STC_MARGINCLICK, 204, "stc_marginclick"},
- {wxEVT_STC_NEEDSHOWN, 204, "stc_needshown"},
- {wxEVT_STC_PAINTED, 204, "stc_painted"},
- {wxEVT_STC_USERLISTSELECTION, 204, "stc_userlistselection"},
- {wxEVT_STC_URIDROPPED, 204, "stc_uridropped"},
- {wxEVT_STC_DWELLSTART, 204, "stc_dwellstart"},
- {wxEVT_STC_DWELLEND, 204, "stc_dwellend"},
- {wxEVT_STC_START_DRAG, 204, "stc_start_drag"},
- {wxEVT_STC_DRAG_OVER, 204, "stc_drag_over"},
- {wxEVT_STC_DO_DROP, 204, "stc_do_drop"},
- {wxEVT_STC_ZOOM, 204, "stc_zoom"},
- {wxEVT_STC_HOTSPOT_CLICK, 204, "stc_hotspot_click"},
- {wxEVT_STC_HOTSPOT_DCLICK, 204, "stc_hotspot_dclick"},
- {wxEVT_STC_CALLTIP_CLICK, 204, "stc_calltip_click"},
- {wxEVT_STC_AUTOCOMP_SELECTION, 204, "stc_autocomp_selection"},
- {wxEVT_COMMAND_TREE_BEGIN_DRAG, 209, "command_tree_begin_drag"},
- {wxEVT_COMMAND_TREE_BEGIN_RDRAG, 209, "command_tree_begin_rdrag"},
- {wxEVT_COMMAND_TREE_BEGIN_LABEL_EDIT, 209, "command_tree_begin_label_edit"},
- {wxEVT_COMMAND_TREE_END_LABEL_EDIT, 209, "command_tree_end_label_edit"},
- {wxEVT_COMMAND_TREE_DELETE_ITEM, 209, "command_tree_delete_item"},
- {wxEVT_COMMAND_TREE_GET_INFO, 209, "command_tree_get_info"},
- {wxEVT_COMMAND_TREE_SET_INFO, 209, "command_tree_set_info"},
- {wxEVT_COMMAND_TREE_ITEM_EXPANDED, 209, "command_tree_item_expanded"},
- {wxEVT_COMMAND_TREE_ITEM_EXPANDING, 209, "command_tree_item_expanding"},
- {wxEVT_COMMAND_TREE_ITEM_COLLAPSED, 209, "command_tree_item_collapsed"},
- {wxEVT_COMMAND_TREE_ITEM_COLLAPSING, 209, "command_tree_item_collapsing"},
- {wxEVT_COMMAND_TREE_SEL_CHANGED, 209, "command_tree_sel_changed"},
- {wxEVT_COMMAND_TREE_SEL_CHANGING, 209, "command_tree_sel_changing"},
- {wxEVT_COMMAND_TREE_KEY_DOWN, 209, "command_tree_key_down"},
- {wxEVT_COMMAND_TREE_ITEM_ACTIVATED, 209, "command_tree_item_activated"},
- {wxEVT_COMMAND_TREE_ITEM_RIGHT_CLICK, 209, "command_tree_item_right_click"},
- {wxEVT_COMMAND_TREE_ITEM_MIDDLE_CLICK, 209, "command_tree_item_middle_click"},
- {wxEVT_COMMAND_TREE_END_DRAG, 209, "command_tree_end_drag"},
- {wxEVT_COMMAND_TREE_STATE_IMAGE_CLICK, 209, "command_tree_state_image_click"},
- {wxEVT_COMMAND_TREE_ITEM_GETTOOLTIP, 209, "command_tree_item_gettooltip"},
- {wxEVT_COMMAND_TREE_ITEM_MENU, 209, "command_tree_item_menu"},
- {wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED, 210, "command_notebook_page_changed"},
- {wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING, 210, "command_notebook_page_changing"},
- {wxEVT_COMMAND_SPINCTRL_UPDATED, 216, "command_spinctrl_updated"},
+ {wxEVT_ERASE_BACKGROUND, 173, "erase_background"},
+ {wxEVT_SET_FOCUS, 174, "set_focus"},
+ {wxEVT_KILL_FOCUS, 174, "kill_focus"},
+ {wxEVT_CHILD_FOCUS, 175, "child_focus"},
+ {wxEVT_MENU_OPEN, 176, "menu_open"},
+ {wxEVT_MENU_CLOSE, 176, "menu_close"},
+ {wxEVT_MENU_HIGHLIGHT, 176, "menu_highlight"},
+ {wxEVT_CLOSE_WINDOW, 177, "close_window"},
+ {wxEVT_END_SESSION, 177, "end_session"},
+ {wxEVT_QUERY_END_SESSION, 177, "query_end_session"},
+ {wxEVT_SHOW, 178, "show"},
+ {wxEVT_ICONIZE, 179, "iconize"},
+ {wxEVT_MAXIMIZE, 180, "maximize"},
+ {wxEVT_JOY_BUTTON_DOWN, 181, "joy_button_down"},
+ {wxEVT_JOY_BUTTON_UP, 181, "joy_button_up"},
+ {wxEVT_JOY_MOVE, 181, "joy_move"},
+ {wxEVT_JOY_ZMOVE, 181, "joy_zmove"},
+ {wxEVT_UPDATE_UI, 182, "update_ui"},
+ {wxEVT_SYS_COLOUR_CHANGED, 183, "sys_colour_changed"},
+ {wxEVT_MOUSE_CAPTURE_CHANGED, 184, "mouse_capture_changed"},
+ {wxEVT_DISPLAY_CHANGED, 185, "display_changed"},
+ {wxEVT_PALETTE_CHANGED, 186, "palette_changed"},
+ {wxEVT_QUERY_NEW_PALETTE, 187, "query_new_palette"},
+ {wxEVT_NAVIGATION_KEY, 188, "navigation_key"},
+ {wxEVT_CREATE, 189, "create"},
+ {wxEVT_DESTROY, 190, "destroy"},
+ {wxEVT_HELP, 191, "help"},
+ {wxEVT_DETAILED_HELP, 191, "detailed_help"},
+ {wxEVT_CONTEXT_MENU, 192, "context_menu"},
+ {wxEVT_IDLE, 193, "idle"},
+ {wxEVT_GRID_CELL_LEFT_CLICK, 194, "grid_cell_left_click"},
+ {wxEVT_GRID_CELL_RIGHT_CLICK, 194, "grid_cell_right_click"},
+ {wxEVT_GRID_CELL_LEFT_DCLICK, 194, "grid_cell_left_dclick"},
+ {wxEVT_GRID_CELL_RIGHT_DCLICK, 194, "grid_cell_right_dclick"},
+ {wxEVT_GRID_LABEL_LEFT_CLICK, 194, "grid_label_left_click"},
+ {wxEVT_GRID_LABEL_RIGHT_CLICK, 194, "grid_label_right_click"},
+ {wxEVT_GRID_LABEL_LEFT_DCLICK, 194, "grid_label_left_dclick"},
+ {wxEVT_GRID_LABEL_RIGHT_DCLICK, 194, "grid_label_right_dclick"},
+ {wxEVT_GRID_ROW_SIZE, 194, "grid_row_size"},
+ {wxEVT_GRID_COL_SIZE, 194, "grid_col_size"},
+ {wxEVT_GRID_RANGE_SELECT, 194, "grid_range_select"},
+ {wxEVT_GRID_CELL_CHANGE, 194, "grid_cell_change"},
+ {wxEVT_GRID_SELECT_CELL, 194, "grid_select_cell"},
+ {wxEVT_GRID_EDITOR_SHOWN, 194, "grid_editor_shown"},
+ {wxEVT_GRID_EDITOR_HIDDEN, 194, "grid_editor_hidden"},
+ {wxEVT_GRID_EDITOR_CREATED, 194, "grid_editor_created"},
+ {wxEVT_GRID_CELL_BEGIN_DRAG, 194, "grid_cell_begin_drag"},
+ {wxEVT_SASH_DRAGGED, 196, "sash_dragged"},
+ {wxEVT_COMMAND_LIST_BEGIN_DRAG, 197, "command_list_begin_drag"},
+ {wxEVT_COMMAND_LIST_BEGIN_RDRAG, 197, "command_list_begin_rdrag"},
+ {wxEVT_COMMAND_LIST_BEGIN_LABEL_EDIT, 197, "command_list_begin_label_edit"},
+ {wxEVT_COMMAND_LIST_END_LABEL_EDIT, 197, "command_list_end_label_edit"},
+ {wxEVT_COMMAND_LIST_DELETE_ITEM, 197, "command_list_delete_item"},
+ {wxEVT_COMMAND_LIST_DELETE_ALL_ITEMS, 197, "command_list_delete_all_items"},
+ {wxEVT_COMMAND_LIST_KEY_DOWN, 197, "command_list_key_down"},
+ {wxEVT_COMMAND_LIST_INSERT_ITEM, 197, "command_list_insert_item"},
+ {wxEVT_COMMAND_LIST_COL_CLICK, 197, "command_list_col_click"},
+ {wxEVT_COMMAND_LIST_COL_RIGHT_CLICK, 197, "command_list_col_right_click"},
+ {wxEVT_COMMAND_LIST_COL_BEGIN_DRAG, 197, "command_list_col_begin_drag"},
+ {wxEVT_COMMAND_LIST_COL_DRAGGING, 197, "command_list_col_dragging"},
+ {wxEVT_COMMAND_LIST_COL_END_DRAG, 197, "command_list_col_end_drag"},
+ {wxEVT_COMMAND_LIST_ITEM_SELECTED, 197, "command_list_item_selected"},
+ {wxEVT_COMMAND_LIST_ITEM_DESELECTED, 197, "command_list_item_deselected"},
+ {wxEVT_COMMAND_LIST_ITEM_RIGHT_CLICK, 197, "command_list_item_right_click"},
+ {wxEVT_COMMAND_LIST_ITEM_MIDDLE_CLICK, 197, "command_list_item_middle_click"},
+ {wxEVT_COMMAND_LIST_ITEM_ACTIVATED, 197, "command_list_item_activated"},
+ {wxEVT_COMMAND_LIST_ITEM_FOCUSED, 197, "command_list_item_focused"},
+ {wxEVT_COMMAND_LIST_CACHE_HINT, 197, "command_list_cache_hint"},
+ {wxEVT_DATE_CHANGED, 198, "date_changed"},
+ {wxEVT_CALENDAR_SEL_CHANGED, 199, "calendar_sel_changed"},
+ {wxEVT_CALENDAR_DAY_CHANGED, 199, "calendar_day_changed"},
+ {wxEVT_CALENDAR_MONTH_CHANGED, 199, "calendar_month_changed"},
+ {wxEVT_CALENDAR_YEAR_CHANGED, 199, "calendar_year_changed"},
+ {wxEVT_CALENDAR_DOUBLECLICKED, 199, "calendar_doubleclicked"},
+ {wxEVT_CALENDAR_WEEKDAY_CLICKED, 199, "calendar_weekday_clicked"},
+ {wxEVT_COMMAND_FILEPICKER_CHANGED, 200, "command_filepicker_changed"},
+ {wxEVT_COMMAND_DIRPICKER_CHANGED, 200, "command_dirpicker_changed"},
+ {wxEVT_COMMAND_COLOURPICKER_CHANGED, 201, "command_colourpicker_changed"},
+ {wxEVT_COMMAND_FONTPICKER_CHANGED, 202, "command_fontpicker_changed"},
+ {wxEVT_STC_CHANGE, 203, "stc_change"},
+ {wxEVT_STC_STYLENEEDED, 203, "stc_styleneeded"},
+ {wxEVT_STC_CHARADDED, 203, "stc_charadded"},
+ {wxEVT_STC_SAVEPOINTREACHED, 203, "stc_savepointreached"},
+ {wxEVT_STC_SAVEPOINTLEFT, 203, "stc_savepointleft"},
+ {wxEVT_STC_ROMODIFYATTEMPT, 203, "stc_romodifyattempt"},
+ {wxEVT_STC_KEY, 203, "stc_key"},
+ {wxEVT_STC_DOUBLECLICK, 203, "stc_doubleclick"},
+ {wxEVT_STC_UPDATEUI, 203, "stc_updateui"},
+ {wxEVT_STC_MODIFIED, 203, "stc_modified"},
+ {wxEVT_STC_MACRORECORD, 203, "stc_macrorecord"},
+ {wxEVT_STC_MARGINCLICK, 203, "stc_marginclick"},
+ {wxEVT_STC_NEEDSHOWN, 203, "stc_needshown"},
+ {wxEVT_STC_PAINTED, 203, "stc_painted"},
+ {wxEVT_STC_USERLISTSELECTION, 203, "stc_userlistselection"},
+ {wxEVT_STC_URIDROPPED, 203, "stc_uridropped"},
+ {wxEVT_STC_DWELLSTART, 203, "stc_dwellstart"},
+ {wxEVT_STC_DWELLEND, 203, "stc_dwellend"},
+ {wxEVT_STC_START_DRAG, 203, "stc_start_drag"},
+ {wxEVT_STC_DRAG_OVER, 203, "stc_drag_over"},
+ {wxEVT_STC_DO_DROP, 203, "stc_do_drop"},
+ {wxEVT_STC_ZOOM, 203, "stc_zoom"},
+ {wxEVT_STC_HOTSPOT_CLICK, 203, "stc_hotspot_click"},
+ {wxEVT_STC_HOTSPOT_DCLICK, 203, "stc_hotspot_dclick"},
+ {wxEVT_STC_CALLTIP_CLICK, 203, "stc_calltip_click"},
+ {wxEVT_STC_AUTOCOMP_SELECTION, 203, "stc_autocomp_selection"},
+ {wxEVT_COMMAND_TREE_BEGIN_DRAG, 208, "command_tree_begin_drag"},
+ {wxEVT_COMMAND_TREE_BEGIN_RDRAG, 208, "command_tree_begin_rdrag"},
+ {wxEVT_COMMAND_TREE_BEGIN_LABEL_EDIT, 208, "command_tree_begin_label_edit"},
+ {wxEVT_COMMAND_TREE_END_LABEL_EDIT, 208, "command_tree_end_label_edit"},
+ {wxEVT_COMMAND_TREE_DELETE_ITEM, 208, "command_tree_delete_item"},
+ {wxEVT_COMMAND_TREE_GET_INFO, 208, "command_tree_get_info"},
+ {wxEVT_COMMAND_TREE_SET_INFO, 208, "command_tree_set_info"},
+ {wxEVT_COMMAND_TREE_ITEM_EXPANDED, 208, "command_tree_item_expanded"},
+ {wxEVT_COMMAND_TREE_ITEM_EXPANDING, 208, "command_tree_item_expanding"},
+ {wxEVT_COMMAND_TREE_ITEM_COLLAPSED, 208, "command_tree_item_collapsed"},
+ {wxEVT_COMMAND_TREE_ITEM_COLLAPSING, 208, "command_tree_item_collapsing"},
+ {wxEVT_COMMAND_TREE_SEL_CHANGED, 208, "command_tree_sel_changed"},
+ {wxEVT_COMMAND_TREE_SEL_CHANGING, 208, "command_tree_sel_changing"},
+ {wxEVT_COMMAND_TREE_KEY_DOWN, 208, "command_tree_key_down"},
+ {wxEVT_COMMAND_TREE_ITEM_ACTIVATED, 208, "command_tree_item_activated"},
+ {wxEVT_COMMAND_TREE_ITEM_RIGHT_CLICK, 208, "command_tree_item_right_click"},
+ {wxEVT_COMMAND_TREE_ITEM_MIDDLE_CLICK, 208, "command_tree_item_middle_click"},
+ {wxEVT_COMMAND_TREE_END_DRAG, 208, "command_tree_end_drag"},
+ {wxEVT_COMMAND_TREE_STATE_IMAGE_CLICK, 208, "command_tree_state_image_click"},
+ {wxEVT_COMMAND_TREE_ITEM_GETTOOLTIP, 208, "command_tree_item_gettooltip"},
+ {wxEVT_COMMAND_TREE_ITEM_MENU, 208, "command_tree_item_menu"},
+ {wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED, 209, "command_notebook_page_changed"},
+ {wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING, 209, "command_notebook_page_changing"},
+ {wxEVT_COMMAND_SPINCTRL_UPDATED, 215, "command_spinctrl_updated"},
{wxEVT_SCROLL_LINEUP + wxEVT_USER_FIRST, 165, "spin_up"},
{wxEVT_SCROLL_LINEDOWN + wxEVT_USER_FIRST, 165, "spin_down"},
{wxEVT_SCROLL_THUMBTRACK + wxEVT_USER_FIRST, 165, "spin"},
- {wxEVT_COMMAND_SPLITTER_SASH_POS_CHANGED, 218, "command_splitter_sash_pos_changed"},
- {wxEVT_COMMAND_SPLITTER_SASH_POS_CHANGING, 218, "command_splitter_sash_pos_changing"},
- {wxEVT_COMMAND_SPLITTER_DOUBLECLICKED, 218, "command_splitter_doubleclicked"},
- {wxEVT_COMMAND_SPLITTER_UNSPLIT, 218, "command_splitter_unsplit"},
- {wxEVT_COMMAND_HTML_LINK_CLICKED, 220, "command_html_link_clicked"},
- {wxEVT_COMMAND_AUINOTEBOOK_PAGE_CLOSE, 223, "command_auinotebook_page_close"},
- {wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGED, 223, "command_auinotebook_page_changed"},
- {wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGING, 223, "command_auinotebook_page_changing"},
- {wxEVT_COMMAND_AUINOTEBOOK_BUTTON, 223, "command_auinotebook_button"},
- {wxEVT_COMMAND_AUINOTEBOOK_BEGIN_DRAG, 223, "command_auinotebook_begin_drag"},
- {wxEVT_COMMAND_AUINOTEBOOK_END_DRAG, 223, "command_auinotebook_end_drag"},
- {wxEVT_COMMAND_AUINOTEBOOK_DRAG_MOTION, 223, "command_auinotebook_drag_motion"},
- {wxEVT_COMMAND_AUINOTEBOOK_ALLOW_DND, 223, "command_auinotebook_allow_dnd"},
+ {wxEVT_COMMAND_SPLITTER_SASH_POS_CHANGED, 217, "command_splitter_sash_pos_changed"},
+ {wxEVT_COMMAND_SPLITTER_SASH_POS_CHANGING, 217, "command_splitter_sash_pos_changing"},
+ {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, 222, "command_auinotebook_page_close"},
+ {wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGED, 222, "command_auinotebook_page_changed"},
+ {wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGING, 222, "command_auinotebook_page_changing"},
+ {wxEVT_COMMAND_AUINOTEBOOK_BUTTON, 222, "command_auinotebook_button"},
+ {wxEVT_COMMAND_AUINOTEBOOK_BEGIN_DRAG, 222, "command_auinotebook_begin_drag"},
+ {wxEVT_COMMAND_AUINOTEBOOK_END_DRAG, 222, "command_auinotebook_end_drag"},
+ {wxEVT_COMMAND_AUINOTEBOOK_DRAG_MOTION, 222, "command_auinotebook_drag_motion"},
+ {wxEVT_COMMAND_AUINOTEBOOK_ALLOW_DND, 222, "command_auinotebook_allow_dnd"},
#if wxCHECK_VERSION(2,8,5)
- {wxEVT_COMMAND_AUINOTEBOOK_TAB_MIDDLE_DOWN, 223, "command_auinotebook_tab_middle_down"},
+ {wxEVT_COMMAND_AUINOTEBOOK_TAB_MIDDLE_DOWN, 222, "command_auinotebook_tab_middle_down"},
#endif
#if wxCHECK_VERSION(2,8,5)
- {wxEVT_COMMAND_AUINOTEBOOK_TAB_MIDDLE_UP, 223, "command_auinotebook_tab_middle_up"},
+ {wxEVT_COMMAND_AUINOTEBOOK_TAB_MIDDLE_UP, 222, "command_auinotebook_tab_middle_up"},
#endif
#if wxCHECK_VERSION(2,8,5)
- {wxEVT_COMMAND_AUINOTEBOOK_TAB_RIGHT_DOWN, 223, "command_auinotebook_tab_right_down"},
+ {wxEVT_COMMAND_AUINOTEBOOK_TAB_RIGHT_DOWN, 222, "command_auinotebook_tab_right_down"},
#endif
#if wxCHECK_VERSION(2,8,5)
- {wxEVT_COMMAND_AUINOTEBOOK_TAB_RIGHT_UP, 223, "command_auinotebook_tab_right_up"},
+ {wxEVT_COMMAND_AUINOTEBOOK_TAB_RIGHT_UP, 222, "command_auinotebook_tab_right_up"},
#endif
#if wxCHECK_VERSION(2,8,5)
- {wxEVT_COMMAND_AUINOTEBOOK_PAGE_CLOSED, 223, "command_auinotebook_page_closed"},
+ {wxEVT_COMMAND_AUINOTEBOOK_PAGE_CLOSED, 222, "command_auinotebook_page_closed"},
#endif
#if wxCHECK_VERSION(2,8,5)
- {wxEVT_COMMAND_AUINOTEBOOK_DRAG_DONE, 223, "command_auinotebook_drag_done"},
+ {wxEVT_COMMAND_AUINOTEBOOK_DRAG_DONE, 222, "command_auinotebook_drag_done"},
#endif
#if wxCHECK_VERSION(2,8,5)
- {wxEVT_COMMAND_AUINOTEBOOK_BG_DCLICK, 223, "command_auinotebook_bg_dclick"},
+ {wxEVT_COMMAND_AUINOTEBOOK_BG_DCLICK, 222, "command_auinotebook_bg_dclick"},
#endif
- {wxEVT_AUI_PANE_BUTTON, 224, "aui_pane_button"},
- {wxEVT_AUI_PANE_CLOSE, 224, "aui_pane_close"},
- {wxEVT_AUI_PANE_MAXIMIZE, 224, "aui_pane_maximize"},
- {wxEVT_AUI_PANE_RESTORE, 224, "aui_pane_restore"},
- {wxEVT_AUI_RENDER, 224, "aui_render"},
- {wxEVT_AUI_FIND_MANAGER, 224, "aui_find_manager"},
- {wxEVT_TASKBAR_MOVE, 227, "taskbar_move"},
- {wxEVT_TASKBAR_LEFT_DOWN, 227, "taskbar_left_down"},
- {wxEVT_TASKBAR_LEFT_UP, 227, "taskbar_left_up"},
- {wxEVT_TASKBAR_RIGHT_DOWN, 227, "taskbar_right_down"},
- {wxEVT_TASKBAR_RIGHT_UP, 227, "taskbar_right_up"},
- {wxEVT_TASKBAR_LEFT_DCLICK, 227, "taskbar_left_dclick"},
- {wxEVT_TASKBAR_RIGHT_DCLICK, 227, "taskbar_right_dclick"},
+ {wxEVT_AUI_PANE_BUTTON, 223, "aui_pane_button"},
+ {wxEVT_AUI_PANE_CLOSE, 223, "aui_pane_close"},
+ {wxEVT_AUI_PANE_MAXIMIZE, 223, "aui_pane_maximize"},
+ {wxEVT_AUI_PANE_RESTORE, 223, "aui_pane_restore"},
+ {wxEVT_AUI_RENDER, 223, "aui_render"},
+ {wxEVT_AUI_FIND_MANAGER, 223, "aui_find_manager"},
+ {wxEVT_TASKBAR_MOVE, 226, "taskbar_move"},
+ {wxEVT_TASKBAR_LEFT_DOWN, 226, "taskbar_left_down"},
+ {wxEVT_TASKBAR_LEFT_UP, 226, "taskbar_left_up"},
+ {wxEVT_TASKBAR_RIGHT_DOWN, 226, "taskbar_right_down"},
+ {wxEVT_TASKBAR_RIGHT_UP, 226, "taskbar_right_up"},
+ {wxEVT_TASKBAR_LEFT_DCLICK, 226, "taskbar_left_dclick"},
+ {wxEVT_TASKBAR_RIGHT_DCLICK, 226, "taskbar_right_dclick"},
{-1, 0, }
};
for(int i=0; event_types[i].ev_type != -1; i++) {
@@ -437,7 +423,11 @@ case 169: {// wxKeyEvent
rt.addBool(ev->m_shiftDown);
rt.addBool(ev->m_altDown);
rt.addBool(ev->m_metaDown);
+#if !wxCHECK_VERSION(2,9,0)
rt.addBool(ev->m_scanCode);
+#else
+ rt.addBool(false);
+#endif
rt.addInt(ev->m_uniChar);
rt.addUint(ev->m_rawCode);
rt.addUint(ev->m_rawFlags);
@@ -468,14 +458,7 @@ case 172: {// wxPaintEvent
rt.addTupleCount(2);
break;
}
-case 173: {// wxNcPaintEvent
- evClass = (char*)"wxNcPaintEvent";
- rt.addAtom((char*)"wxNcPaint");
- rt.addAtom(Etype->eName);
- rt.addTupleCount(2);
- break;
-}
-case 174: {// wxEraseEvent
+case 173: {// wxEraseEvent
wxEraseEvent * ev = (wxEraseEvent *) event;
wxDC * GetDC = ev->GetDC();
evClass = (char*)"wxEraseEvent";
@@ -485,105 +468,105 @@ case 174: {// wxEraseEvent
rt.addTupleCount(3);
break;
}
-case 175: {// wxFocusEvent
+case 174: {// wxFocusEvent
evClass = (char*)"wxFocusEvent";
rt.addAtom((char*)"wxFocus");
rt.addAtom(Etype->eName);
rt.addTupleCount(2);
break;
}
-case 176: {// wxChildFocusEvent
+case 175: {// wxChildFocusEvent
evClass = (char*)"wxChildFocusEvent";
rt.addAtom((char*)"wxChildFocus");
rt.addAtom(Etype->eName);
rt.addTupleCount(2);
break;
}
-case 177: {// wxMenuEvent
+case 176: {// wxMenuEvent
evClass = (char*)"wxMenuEvent";
rt.addAtom((char*)"wxMenu");
rt.addAtom(Etype->eName);
rt.addTupleCount(2);
break;
}
-case 178: {// wxCloseEvent
+case 177: {// wxCloseEvent
evClass = (char*)"wxCloseEvent";
rt.addAtom((char*)"wxClose");
rt.addAtom(Etype->eName);
rt.addTupleCount(2);
break;
}
-case 179: {// wxShowEvent
+case 178: {// wxShowEvent
evClass = (char*)"wxShowEvent";
rt.addAtom((char*)"wxShow");
rt.addAtom(Etype->eName);
rt.addTupleCount(2);
break;
}
-case 180: {// wxIconizeEvent
+case 179: {// wxIconizeEvent
evClass = (char*)"wxIconizeEvent";
rt.addAtom((char*)"wxIconize");
rt.addAtom(Etype->eName);
rt.addTupleCount(2);
break;
}
-case 181: {// wxMaximizeEvent
+case 180: {// wxMaximizeEvent
evClass = (char*)"wxMaximizeEvent";
rt.addAtom((char*)"wxMaximize");
rt.addAtom(Etype->eName);
rt.addTupleCount(2);
break;
}
-case 182: {// wxJoystickEvent
+case 181: {// wxJoystickEvent
evClass = (char*)"wxJoystickEvent";
rt.addAtom((char*)"wxJoystick");
rt.addAtom(Etype->eName);
rt.addTupleCount(2);
break;
}
-case 183: {// wxUpdateUIEvent
+case 182: {// wxUpdateUIEvent
evClass = (char*)"wxUpdateUIEvent";
rt.addAtom((char*)"wxUpdateUI");
rt.addAtom(Etype->eName);
rt.addTupleCount(2);
break;
}
-case 184: {// wxSysColourChangedEvent
+case 183: {// wxSysColourChangedEvent
evClass = (char*)"wxSysColourChangedEvent";
rt.addAtom((char*)"wxSysColourChanged");
rt.addAtom(Etype->eName);
rt.addTupleCount(2);
break;
}
-case 185: {// wxMouseCaptureChangedEvent
+case 184: {// wxMouseCaptureChangedEvent
evClass = (char*)"wxMouseCaptureChangedEvent";
rt.addAtom((char*)"wxMouseCaptureChanged");
rt.addAtom(Etype->eName);
rt.addTupleCount(2);
break;
}
-case 186: {// wxDisplayChangedEvent
+case 185: {// wxDisplayChangedEvent
evClass = (char*)"wxDisplayChangedEvent";
rt.addAtom((char*)"wxDisplayChanged");
rt.addAtom(Etype->eName);
rt.addTupleCount(2);
break;
}
-case 187: {// wxPaletteChangedEvent
+case 186: {// wxPaletteChangedEvent
evClass = (char*)"wxPaletteChangedEvent";
rt.addAtom((char*)"wxPaletteChanged");
rt.addAtom(Etype->eName);
rt.addTupleCount(2);
break;
}
-case 188: {// wxQueryNewPaletteEvent
+case 187: {// wxQueryNewPaletteEvent
evClass = (char*)"wxQueryNewPaletteEvent";
rt.addAtom((char*)"wxQueryNewPalette");
rt.addAtom(Etype->eName);
rt.addTupleCount(2);
break;
}
-case 189: {// wxNavigationKeyEvent
+case 188: {// wxNavigationKeyEvent
wxNavigationKeyEvent * ev = (wxNavigationKeyEvent *) event;
evClass = (char*)"wxNavigationKeyEvent";
rt.addAtom((char*)"wxNavigationKey");
@@ -593,42 +576,42 @@ case 189: {// wxNavigationKeyEvent
rt.addTupleCount(4);
break;
}
-case 190: {// wxWindowCreateEvent
+case 189: {// wxWindowCreateEvent
evClass = (char*)"wxWindowCreateEvent";
rt.addAtom((char*)"wxWindowCreate");
rt.addAtom(Etype->eName);
rt.addTupleCount(2);
break;
}
-case 191: {// wxWindowDestroyEvent
+case 190: {// wxWindowDestroyEvent
evClass = (char*)"wxWindowDestroyEvent";
rt.addAtom((char*)"wxWindowDestroy");
rt.addAtom(Etype->eName);
rt.addTupleCount(2);
break;
}
-case 192: {// wxHelpEvent
+case 191: {// wxHelpEvent
evClass = (char*)"wxHelpEvent";
rt.addAtom((char*)"wxHelp");
rt.addAtom(Etype->eName);
rt.addTupleCount(2);
break;
}
-case 193: {// wxContextMenuEvent
+case 192: {// wxContextMenuEvent
evClass = (char*)"wxContextMenuEvent";
rt.addAtom((char*)"wxContextMenu");
rt.addAtom(Etype->eName);
rt.addTupleCount(2);
break;
}
-case 194: {// wxIdleEvent
+case 193: {// wxIdleEvent
evClass = (char*)"wxIdleEvent";
rt.addAtom((char*)"wxIdle");
rt.addAtom(Etype->eName);
rt.addTupleCount(2);
break;
}
-case 195: {// wxGridEvent
+case 194: {// wxGridEvent
wxGridEvent * ev = (wxGridEvent *) event;
evClass = (char*)"wxGridEvent";
rt.addAtom((char*)"wxGrid");
@@ -645,7 +628,7 @@ case 195: {// wxGridEvent
rt.addTupleCount(11);
break;
}
-case 197: {// wxSashEvent
+case 196: {// wxSashEvent
wxSashEvent * ev = (wxSashEvent *) event;
evClass = (char*)"wxSashEvent";
rt.addAtom((char*)"wxSash");
@@ -656,7 +639,7 @@ case 197: {// wxSashEvent
rt.addTupleCount(5);
break;
}
-case 198: {// wxListEvent
+case 197: {// wxListEvent
wxListEvent * ev = (wxListEvent *) event;
evClass = (char*)"wxListEvent";
rt.addAtom((char*)"wxList");
@@ -669,7 +652,7 @@ case 198: {// wxListEvent
rt.addTupleCount(7);
break;
}
-case 199: {// wxDateEvent
+case 198: {// wxDateEvent
wxDateEvent * ev = (wxDateEvent *) event;
evClass = (char*)"wxDateEvent";
rt.addAtom((char*)"wxDate");
@@ -678,14 +661,14 @@ case 199: {// wxDateEvent
rt.addTupleCount(3);
break;
}
-case 200: {// wxCalendarEvent
+case 199: {// wxCalendarEvent
evClass = (char*)"wxCalendarEvent";
rt.addAtom((char*)"wxCalendar");
rt.addAtom(Etype->eName);
rt.addTupleCount(2);
break;
}
-case 201: {// wxFileDirPickerEvent
+case 200: {// wxFileDirPickerEvent
wxFileDirPickerEvent * ev = (wxFileDirPickerEvent *) event;
evClass = (char*)"wxFileDirPickerEvent";
rt.addAtom((char*)"wxFileDirPicker");
@@ -694,7 +677,7 @@ case 201: {// wxFileDirPickerEvent
rt.addTupleCount(3);
break;
}
-case 202: {// wxColourPickerEvent
+case 201: {// wxColourPickerEvent
wxColourPickerEvent * ev = (wxColourPickerEvent *) event;
evClass = (char*)"wxColourPickerEvent";
rt.addAtom((char*)"wxColourPicker");
@@ -703,7 +686,7 @@ case 202: {// wxColourPickerEvent
rt.addTupleCount(3);
break;
}
-case 203: {// wxFontPickerEvent
+case 202: {// wxFontPickerEvent
wxFontPickerEvent * ev = (wxFontPickerEvent *) event;
wxFont * GetFont = new wxFont(ev->GetFont());
app->newPtr((void *) GetFont,3, memenv);
@@ -714,7 +697,7 @@ case 203: {// wxFontPickerEvent
rt.addTupleCount(3);
break;
}
-case 204: {// wxStyledTextEvent
+case 203: {// wxStyledTextEvent
wxStyledTextEvent * ev = (wxStyledTextEvent *) event;
evClass = (char*)"wxStyledTextEvent";
rt.addAtom((char*)"wxStyledText");
@@ -742,7 +725,7 @@ case 204: {// wxStyledTextEvent
rt.addTupleCount(22);
break;
}
-case 209: {// wxTreeEvent
+case 208: {// wxTreeEvent
wxTreeEvent * ev = (wxTreeEvent *) event;
evClass = (char*)"wxTreeEvent";
rt.addAtom((char*)"wxTree");
@@ -753,14 +736,14 @@ case 209: {// wxTreeEvent
rt.addTupleCount(5);
break;
}
-case 210: {// wxNotebookEvent
+case 209: {// wxNotebookEvent
evClass = (char*)"wxNotebookEvent";
rt.addAtom((char*)"wxNotebook");
rt.addAtom(Etype->eName);
rt.addTupleCount(2);
break;
}
-case 216: {// wxSpinEvent
+case 215: {// wxSpinEvent
wxSpinEvent * ev = (wxSpinEvent *) event;
evClass = (char*)"wxSpinEvent";
rt.addAtom((char*)"wxSpin");
@@ -769,14 +752,14 @@ case 216: {// wxSpinEvent
rt.addTupleCount(3);
break;
}
-case 218: {// wxSplitterEvent
+case 217: {// wxSplitterEvent
evClass = (char*)"wxSplitterEvent";
rt.addAtom((char*)"wxSplitter");
rt.addAtom(Etype->eName);
rt.addTupleCount(2);
break;
}
-case 220: {// wxHtmlLinkEvent
+case 219: {// wxHtmlLinkEvent
wxHtmlLinkEvent * ev = (wxHtmlLinkEvent *) event;
evClass = (char*)"wxHtmlLinkEvent";
rt.addAtom((char*)"wxHtmlLink");
@@ -785,7 +768,7 @@ case 220: {// wxHtmlLinkEvent
rt.addTupleCount(3);
break;
}
-case 223: {// wxAuiNotebookEvent
+case 222: {// wxAuiNotebookEvent
wxAuiNotebookEvent * ev = (wxAuiNotebookEvent *) event;
wxAuiNotebook * GetDragSource = ev->GetDragSource();
evClass = (char*)"wxAuiNotebookEvent";
@@ -797,7 +780,7 @@ case 223: {// wxAuiNotebookEvent
rt.addTupleCount(5);
break;
}
-case 224: {// wxAuiManagerEvent
+case 223: {// wxAuiManagerEvent
wxAuiManagerEvent * ev = (wxAuiManagerEvent *) event;
wxAuiManager * GetManager = ev->GetManager();
wxAuiPaneInfo * GetPane = ev->GetPane();
@@ -814,7 +797,7 @@ case 224: {// wxAuiManagerEvent
rt.addTupleCount(8);
break;
}
-case 227: {// wxTaskBarIconEvent
+case 226: {// wxTaskBarIconEvent
evClass = (char*)"wxTaskBarIconEvent";
rt.addAtom((char*)"wxTaskBarIcon");
rt.addAtom(Etype->eName);
diff --git a/lib/wx/c_src/gen/wxe_funcs.cpp b/lib/wx/c_src/gen/wxe_funcs.cpp
index 15012011ed..5fbe8a2a9e 100644
--- a/lib/wx/c_src/gen/wxe_funcs.cpp
+++ b/lib/wx/c_src/gen/wxe_funcs.cpp
@@ -27,6 +27,15 @@
#include "wxe_macros.h"
#include "wxe_derived_dest.h"
+#if !wxCHECK_VERSION(2,9,0)
+#define wxPenJoin int
+#define wxPenCap int
+#define wxImageResizeQuality int
+#define wxPolygonFillMode int
+#define wxMappingMode int
+#define wxRasterOperationMode int
+#define wxFloodFillStyle int
+#endif
void WxeApp::wxe_dispatch(wxeCommand& Ecmd)
{
char * bp = Ecmd.buffer;
@@ -149,7 +158,7 @@ case wxWindow_new_3: { // wxWindow::wxWindow
style = (long)*(int *) bp; bp += 4;
} break;
}};
- wxWindow * Result = new EwxWindow(parent,(wxWindowID) *id,pos,size,style);
+ wxWindow * Result = new EwxWindow(parent,*id,pos,size,style);
newPtr((void *) Result, 0, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxWindow");
break;
@@ -326,7 +335,7 @@ case wxWindow_FindWindow_1_0: { // wxWindow::FindWindow
wxWindow *This = (wxWindow *) getPtr(bp,memenv); bp += 4;
int * winid = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxWindow * Result = (wxWindow*)This->FindWindow((long) *winid);
+ wxWindow * Result = (wxWindow*)This->FindWindow(*winid);
rt.addRef(getRef((void *)Result,memenv), "wxWindow");
break;
}
@@ -349,7 +358,7 @@ case wxWindow_FindWindowById: { // wxWindow::FindWindowById
parent = (wxWindow *) getPtr(bp,memenv); bp += 4;
} break;
}};
- wxWindow * Result = (wxWindow*)wxWindow::FindWindowById((long) *winid,parent);
+ wxWindow * Result = (wxWindow*)wxWindow::FindWindowById(*winid,parent);
rt.addRef(getRef((void *)Result,memenv), "wxWindow");
break;
}
@@ -615,7 +624,7 @@ case wxWindow_GetScrollPos: { // wxWindow::GetScrollPos
wxWindow *This = (wxWindow *) getPtr(bp,memenv); bp += 4;
int * orient = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->GetScrollPos((int) *orient);
+ int Result = This->GetScrollPos(*orient);
rt.addInt(Result);
break;
}
@@ -623,7 +632,7 @@ case wxWindow_GetScrollRange: { // wxWindow::GetScrollRange
wxWindow *This = (wxWindow *) getPtr(bp,memenv); bp += 4;
int * orient = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->GetScrollRange((int) *orient);
+ int Result = This->GetScrollRange(*orient);
rt.addInt(Result);
break;
}
@@ -631,7 +640,7 @@ case wxWindow_GetScrollThumb: { // wxWindow::GetScrollThumb
wxWindow *This = (wxWindow *) getPtr(bp,memenv); bp += 4;
int * orient = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->GetScrollThumb((int) *orient);
+ int Result = This->GetScrollThumb(*orient);
rt.addInt(Result);
break;
}
@@ -719,7 +728,7 @@ case wxWindow_HasScrollbar: { // wxWindow::HasScrollbar
wxWindow *This = (wxWindow *) getPtr(bp,memenv); bp += 4;
int * orient = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->HasScrollbar((int) *orient);
+ bool Result = This->HasScrollbar(*orient);
rt.addBool(Result);
break;
}
@@ -767,7 +776,7 @@ case wxWindow_IsExposed_2: { // wxWindow::IsExposed
int * x = (int *) bp; bp += 4;
int * y = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->IsExposed((int) *x,(int) *y);
+ bool Result = This->IsExposed(*x,*y);
rt.addBool(Result);
break;
}
@@ -778,7 +787,7 @@ case wxWindow_IsExposed_4: { // wxWindow::IsExposed
int * w = (int *) bp; bp += 4;
int * h = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->IsExposed((int) *x,(int) *y,(int) *w,(int) *h);
+ bool Result = This->IsExposed(*x,*y,*w,*h);
rt.addBool(Result);
break;
}
@@ -877,7 +886,7 @@ case wxWindow_Move_3: { // wxWindow::Move
} break;
}};
if(!This) throw wxe_badarg(0);
- This->Move((int) *x,(int) *y,flags);
+ This->Move(*x,*y,flags);
break;
}
case wxWindow_Move_2: { // wxWindow::Move
@@ -975,7 +984,7 @@ case wxWindow_PopupMenu_3: { // wxWindow::PopupMenu
int * x = (int *) bp; bp += 4;
int * y = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->PopupMenu(menu,(int) *x,(int) *y);
+ bool Result = This->PopupMenu(menu,*x,*y);
rt.addBool(Result);
break;
}
@@ -1071,7 +1080,7 @@ case wxWindow_ScrollLines: { // wxWindow::ScrollLines
wxWindow *This = (wxWindow *) getPtr(bp,memenv); bp += 4;
int * lines = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->ScrollLines((int) *lines);
+ bool Result = This->ScrollLines(*lines);
rt.addBool(Result);
break;
}
@@ -1079,7 +1088,7 @@ case wxWindow_ScrollPages: { // wxWindow::ScrollPages
wxWindow *This = (wxWindow *) getPtr(bp,memenv); bp += 4;
int * pages = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->ScrollPages((int) *pages);
+ bool Result = This->ScrollPages(*pages);
rt.addBool(Result);
break;
}
@@ -1100,7 +1109,7 @@ case wxWindow_ScrollWindow: { // wxWindow::ScrollWindow
} break;
}};
if(!This) throw wxe_badarg(0);
- This->ScrollWindow((int) *dx,(int) *dy,rect);
+ This->ScrollWindow(*dx,*dy,rect);
break;
}
case wxWindow_SetAcceleratorTable: { // wxWindow::SetAcceleratorTable
@@ -1114,7 +1123,7 @@ case wxWindow_SetAutoLayout: { // wxWindow::SetAutoLayout
wxWindow *This = (wxWindow *) getPtr(bp,memenv); bp += 4;
bool * autoLayout = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetAutoLayout((bool) *autoLayout);
+ This->SetAutoLayout(*autoLayout);
break;
}
case wxWindow_SetBackgroundColour: { // wxWindow::SetBackgroundColour
@@ -1133,7 +1142,7 @@ case wxWindow_SetBackgroundStyle: { // wxWindow::SetBackgroundStyle
wxWindow *This = (wxWindow *) getPtr(bp,memenv); bp += 4;
wxBackgroundStyle style = *(wxBackgroundStyle *) bp; bp += 4;;
if(!This) throw wxe_badarg(0);
- bool Result = This->SetBackgroundStyle((wxBackgroundStyle) style);
+ bool Result = This->SetBackgroundStyle(style);
rt.addBool(Result);
break;
}
@@ -1149,7 +1158,7 @@ case wxWindow_SetClientSize_2: { // wxWindow::SetClientSize
int * width = (int *) bp; bp += 4;
int * height = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetClientSize((int) *width,(int) *height);
+ This->SetClientSize(*width,*height);
break;
}
case wxWindow_SetClientSize_1_0: { // wxWindow::SetClientSize
@@ -1245,7 +1254,7 @@ case wxWindow_SetExtraStyle: { // wxWindow::SetExtraStyle
wxWindow *This = (wxWindow *) getPtr(bp,memenv); bp += 4;
int * exStyle = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetExtraStyle((long) *exStyle);
+ This->SetExtraStyle(*exStyle);
break;
}
case wxWindow_SetFocus: { // wxWindow::SetFocus
@@ -1293,7 +1302,7 @@ case wxWindow_SetId: { // wxWindow::SetId
wxWindow *This = (wxWindow *) getPtr(bp,memenv); bp += 4;
int * winid = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetId((wxWindowID) *winid);
+ This->SetId(*winid);
break;
}
case wxWindow_SetLabel: { // wxWindow::SetLabel
@@ -1335,7 +1344,7 @@ case wxWindow_SetScrollbar: { // wxWindow::SetScrollbar
} break;
}};
if(!This) throw wxe_badarg(0);
- This->SetScrollbar((int) *orient,(int) *pos,(int) *thumbVisible,(int) *range,refresh);
+ This->SetScrollbar(*orient,*pos,*thumbVisible,*range,refresh);
break;
}
case wxWindow_SetScrollPos: { // wxWindow::SetScrollPos
@@ -1350,7 +1359,7 @@ case wxWindow_SetScrollPos: { // wxWindow::SetScrollPos
} break;
}};
if(!This) throw wxe_badarg(0);
- This->SetScrollPos((int) *orient,(int) *pos,refresh);
+ This->SetScrollPos(*orient,*pos,refresh);
break;
}
case wxWindow_SetSize_5: { // wxWindow::SetSize
@@ -1367,7 +1376,7 @@ case wxWindow_SetSize_5: { // wxWindow::SetSize
} break;
}};
if(!This) throw wxe_badarg(0);
- This->SetSize((int) *x,(int) *y,(int) *width,(int) *height,sizeFlags);
+ This->SetSize(*x,*y,*width,*height,sizeFlags);
break;
}
case wxWindow_SetSize_2_0: { // wxWindow::SetSize
@@ -1375,7 +1384,7 @@ case wxWindow_SetSize_2_0: { // wxWindow::SetSize
int * width = (int *) bp; bp += 4;
int * height = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetSize((int) *width,(int) *height);
+ This->SetSize(*width,*height);
break;
}
case wxWindow_SetSize_1: { // wxWindow::SetSize
@@ -1429,7 +1438,7 @@ case wxWindow_SetSizeHints_3: { // wxWindow::SetSizeHints
} break;
}};
if(!This) throw wxe_badarg(0);
- This->SetSizeHints((int) *minW,(int) *minH,maxW,maxH,incW,incH);
+ This->SetSizeHints(*minW,*minH,maxW,maxH,incW,incH);
break;
}
case wxWindow_SetSizeHints_2: { // wxWindow::SetSizeHints
@@ -1488,7 +1497,7 @@ case wxWindow_SetThemeEnabled: { // wxWindow::SetThemeEnabled
wxWindow *This = (wxWindow *) getPtr(bp,memenv); bp += 4;
bool * enableTheme = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetThemeEnabled((bool) *enableTheme);
+ This->SetThemeEnabled(*enableTheme);
break;
}
case wxWindow_SetToolTip_1_0: { // wxWindow::SetToolTip
@@ -1521,7 +1530,7 @@ case wxWindow_SetVirtualSize_2: { // wxWindow::SetVirtualSize
int * x = (int *) bp; bp += 4;
int * y = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetVirtualSize((int) *x,(int) *y);
+ This->SetVirtualSize(*x,*y);
break;
}
case wxWindow_SetVirtualSizeHints_3: { // wxWindow::SetVirtualSizeHints
@@ -1540,7 +1549,7 @@ case wxWindow_SetVirtualSizeHints_3: { // wxWindow::SetVirtualSizeHints
} break;
}};
if(!This) throw wxe_badarg(0);
- This->SetVirtualSizeHints((int) *minW,(int) *minH,maxW,maxH);
+ This->SetVirtualSizeHints(*minW,*minH,maxW,maxH);
break;
}
case wxWindow_SetVirtualSizeHints_2: { // wxWindow::SetVirtualSizeHints
@@ -1566,21 +1575,21 @@ case wxWindow_SetWindowStyle: { // wxWindow::SetWindowStyle
wxWindow *This = (wxWindow *) getPtr(bp,memenv); bp += 4;
int * style = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetWindowStyle((long) *style);
+ This->SetWindowStyle(*style);
break;
}
case wxWindow_SetWindowStyleFlag: { // wxWindow::SetWindowStyleFlag
wxWindow *This = (wxWindow *) getPtr(bp,memenv); bp += 4;
int * style = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetWindowStyleFlag((long) *style);
+ This->SetWindowStyleFlag(*style);
break;
}
case wxWindow_SetWindowVariant: { // wxWindow::SetWindowVariant
wxWindow *This = (wxWindow *) getPtr(bp,memenv); bp += 4;
wxWindowVariant variant = *(wxWindowVariant *) bp; bp += 4;;
if(!This) throw wxe_badarg(0);
- This->SetWindowVariant((wxWindowVariant) variant);
+ This->SetWindowVariant(variant);
break;
}
case wxWindow_ShouldInheritColours: { // wxWindow::ShouldInheritColours
@@ -1655,13 +1664,13 @@ case wxWindow_WarpPointer: { // wxWindow::WarpPointer
int * x = (int *) bp; bp += 4;
int * y = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->WarpPointer((int) *x,(int) *y);
+ This->WarpPointer(*x,*y);
break;
}
case wxTopLevelWindow_GetIcon: { // wxTopLevelWindow::GetIcon
wxTopLevelWindow *This = (wxTopLevelWindow *) getPtr(bp,memenv); bp += 4;
if(!This) throw wxe_badarg(0);
- const wxIcon * Result = &This->GetIcon();
+ const wxIcon * Result = new wxIcon(This->GetIcon()); newPtr((void *) Result,3, memenv);;
rt.addRef(getRef((void *)Result,memenv), "wxIcon");
break;
}
@@ -1813,7 +1822,7 @@ case wxTopLevelWindow_ShowFullScreen: { // wxTopLevelWindow::ShowFullScreen
} break;
}};
if(!This) throw wxe_badarg(0);
- bool Result = This->ShowFullScreen((bool) *show,style);
+ bool Result = This->ShowFullScreen(*show,style);
rt.addBool(Result);
break;
}
@@ -1843,7 +1852,7 @@ case wxFrame_new_4: { // wxFrame::wxFrame
style = (long)*(int *) bp; bp += 4;
} break;
}};
- wxFrame * Result = new EwxFrame(parent,(wxWindowID) *id,title,pos,size,style);
+ wxFrame * Result = new EwxFrame(parent,*id,title,pos,size,style);
newPtr((void *) Result, 0, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxFrame");
break;
@@ -1882,7 +1891,7 @@ case wxFrame_Create: { // wxFrame::Create
} break;
}};
if(!This) throw wxe_badarg(0);
- bool Result = This->Create(parent,(wxWindowID) *id,title,pos,size,style);
+ bool Result = This->Create(parent,*id,title,pos,size,style);
rt.addBool(Result);
break;
}
@@ -1965,7 +1974,7 @@ case wxFrame_ProcessCommand: { // wxFrame::ProcessCommand
wxFrame *This = (wxFrame *) getPtr(bp,memenv); bp += 4;
int * winid = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->ProcessCommand((int) *winid);
+ bool Result = This->ProcessCommand(*winid);
rt.addBool(Result);
break;
}
@@ -1993,7 +2002,7 @@ case wxFrame_SetStatusBarPane: { // wxFrame::SetStatusBarPane
wxFrame *This = (wxFrame *) getPtr(bp,memenv); bp += 4;
int * n = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetStatusBarPane((int) *n);
+ This->SetStatusBarPane(*n);
break;
}
case wxFrame_SetStatusText: { // wxFrame::SetStatusText
@@ -2058,7 +2067,7 @@ case wxMiniFrame_new_4: { // wxMiniFrame::wxMiniFrame
style = (long)*(int *) bp; bp += 4;
} break;
}};
- wxMiniFrame * Result = new EwxMiniFrame(parent,(wxWindowID) *id,title,pos,size,style);
+ wxMiniFrame * Result = new EwxMiniFrame(parent,*id,title,pos,size,style);
newPtr((void *) Result, 0, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxMiniFrame");
break;
@@ -2091,7 +2100,7 @@ case wxMiniFrame_Create: { // wxMiniFrame::Create
} break;
}};
if(!This) throw wxe_badarg(0);
- bool Result = This->Create(parent,(wxWindowID) *id,title,pos,size,style);
+ bool Result = This->Create(parent,*id,title,pos,size,style);
rt.addBool(Result);
break;
}
@@ -2128,7 +2137,7 @@ case wxSplashScreen_new_6: { // wxSplashScreen::wxSplashScreen
style = (long)*(int *) bp; bp += 4;
} break;
}};
- wxSplashScreen * Result = new EwxSplashScreen(*bitmap,(long) *splashStyle,(int) *milliseconds,parent,(wxWindowID) *id,pos,size,style);
+ wxSplashScreen * Result = new EwxSplashScreen(*bitmap,*splashStyle,*milliseconds,parent,*id,pos,size,style);
newPtr((void *) Result, 0, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxSplashScreen");
break;
@@ -2166,7 +2175,7 @@ case wxPanel_new_6: { // wxPanel::wxPanel
style = (long)*(int *) bp; bp += 4;
} break;
}};
- wxPanel * Result = new EwxPanel(parent,(int) *x,(int) *y,(int) *width,(int) *height,style);
+ wxPanel * Result = new EwxPanel(parent,*x,*y,*width,*height,style);
newPtr((void *) Result, 0, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxPanel");
break;
@@ -2254,7 +2263,7 @@ case wxScrolledWindow_CalcScrolledPosition_4: { // wxScrolledWindow::CalcScrolle
int * x = (int *) bp; bp += 4;
int * y = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->CalcScrolledPosition((int) *x,(int) *y,&xx,&yy);
+ This->CalcScrolledPosition(*x,*y,&xx,&yy);
rt.addInt(xx);
rt.addInt(yy);
rt.addTupleCount(2);
@@ -2277,7 +2286,7 @@ case wxScrolledWindow_CalcUnscrolledPosition_4: { // wxScrolledWindow::CalcUnscr
int * x = (int *) bp; bp += 4;
int * y = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->CalcUnscrolledPosition((int) *x,(int) *y,&xx,&yy);
+ This->CalcUnscrolledPosition(*x,*y,&xx,&yy);
rt.addInt(xx);
rt.addInt(yy);
rt.addTupleCount(2);
@@ -2298,7 +2307,7 @@ case wxScrolledWindow_EnableScrolling: { // wxScrolledWindow::EnableScrolling
bool * x_scrolling = (bool *) bp; bp += 4;
bool * y_scrolling = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->EnableScrolling((bool) *x_scrolling,(bool) *y_scrolling);
+ This->EnableScrolling(*x_scrolling,*y_scrolling);
break;
}
case wxScrolledWindow_GetScrollPixelsPerUnit: { // wxScrolledWindow::GetScrollPixelsPerUnit
@@ -2342,7 +2351,7 @@ case wxScrolledWindow_Scroll: { // wxScrolledWindow::Scroll
int * x = (int *) bp; bp += 4;
int * y = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->Scroll((int) *x,(int) *y);
+ This->Scroll(*x,*y);
break;
}
case wxScrolledWindow_SetScrollbars: { // wxScrolledWindow::SetScrollbars
@@ -2367,7 +2376,7 @@ case wxScrolledWindow_SetScrollbars: { // wxScrolledWindow::SetScrollbars
} break;
}};
if(!This) throw wxe_badarg(0);
- This->SetScrollbars((int) *pixelsPerUnitX,(int) *pixelsPerUnitY,(int) *noUnitsX,(int) *noUnitsY,xPos,yPos,noRefresh);
+ This->SetScrollbars(*pixelsPerUnitX,*pixelsPerUnitY,*noUnitsX,*noUnitsY,xPos,yPos,noRefresh);
break;
}
case wxScrolledWindow_SetScrollRate: { // wxScrolledWindow::SetScrollRate
@@ -2375,7 +2384,7 @@ case wxScrolledWindow_SetScrollRate: { // wxScrolledWindow::SetScrollRate
int * xstep = (int *) bp; bp += 4;
int * ystep = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetScrollRate((int) *xstep,(int) *ystep);
+ This->SetScrollRate(*xstep,*ystep);
break;
}
case wxScrolledWindow_SetTargetWindow: { // wxScrolledWindow::SetTargetWindow
@@ -2427,7 +2436,7 @@ case wxSashWindow_GetSashVisible: { // wxSashWindow::GetSashVisible
wxSashWindow *This = (wxSashWindow *) getPtr(bp,memenv); bp += 4;
wxSashEdgePosition edge = *(wxSashEdgePosition *) bp; bp += 4;;
if(!This) throw wxe_badarg(0);
- bool Result = This->GetSashVisible((wxSashEdgePosition) edge);
+ bool Result = This->GetSashVisible(edge);
rt.addBool(Result);
break;
}
@@ -2463,28 +2472,28 @@ case wxSashWindow_SetMaximumSizeX: { // wxSashWindow::SetMaximumSizeX
wxSashWindow *This = (wxSashWindow *) getPtr(bp,memenv); bp += 4;
int * max = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetMaximumSizeX((int) *max);
+ This->SetMaximumSizeX(*max);
break;
}
case wxSashWindow_SetMaximumSizeY: { // wxSashWindow::SetMaximumSizeY
wxSashWindow *This = (wxSashWindow *) getPtr(bp,memenv); bp += 4;
int * max = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetMaximumSizeY((int) *max);
+ This->SetMaximumSizeY(*max);
break;
}
case wxSashWindow_SetMinimumSizeX: { // wxSashWindow::SetMinimumSizeX
wxSashWindow *This = (wxSashWindow *) getPtr(bp,memenv); bp += 4;
int * min = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetMinimumSizeX((int) *min);
+ This->SetMinimumSizeX(*min);
break;
}
case wxSashWindow_SetMinimumSizeY: { // wxSashWindow::SetMinimumSizeY
wxSashWindow *This = (wxSashWindow *) getPtr(bp,memenv); bp += 4;
int * min = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetMinimumSizeY((int) *min);
+ This->SetMinimumSizeY(*min);
break;
}
case wxSashWindow_SetSashVisible: { // wxSashWindow::SetSashVisible
@@ -2492,7 +2501,7 @@ case wxSashWindow_SetSashVisible: { // wxSashWindow::SetSashVisible
wxSashEdgePosition edge = *(wxSashEdgePosition *) bp; bp += 4;;
bool * sash = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetSashVisible((wxSashEdgePosition) edge,(bool) *sash);
+ This->SetSashVisible(edge,*sash);
break;
}
case wxSashLayoutWindow_new_0: { // wxSashLayoutWindow::wxSashLayoutWindow
@@ -2583,7 +2592,7 @@ case wxSashLayoutWindow_SetAlignment: { // wxSashLayoutWindow::SetAlignment
wxSashLayoutWindow *This = (wxSashLayoutWindow *) getPtr(bp,memenv); bp += 4;
wxLayoutAlignment align = *(wxLayoutAlignment *) bp; bp += 4;;
if(!This) throw wxe_badarg(0);
- This->SetAlignment((wxLayoutAlignment) align);
+ This->SetAlignment(align);
break;
}
case wxSashLayoutWindow_SetDefaultSize: { // wxSashLayoutWindow::SetDefaultSize
@@ -2599,7 +2608,7 @@ case wxSashLayoutWindow_SetOrientation: { // wxSashLayoutWindow::SetOrientation
wxSashLayoutWindow *This = (wxSashLayoutWindow *) getPtr(bp,memenv); bp += 4;
wxLayoutOrientation orient = *(wxLayoutOrientation *) bp; bp += 4;;
if(!This) throw wxe_badarg(0);
- This->SetOrientation((wxLayoutOrientation) orient);
+ This->SetOrientation(orient);
break;
}
case wxGrid_new_0: { // wxGrid::wxGrid
@@ -2631,7 +2640,7 @@ case wxGrid_new_3: { // wxGrid::wxGrid
style = (long)*(int *) bp; bp += 4;
} break;
}};
- wxGrid * Result = new EwxGrid(parent,(wxWindowID) *id,pos,size,style);
+ wxGrid * Result = new EwxGrid(parent,*id,pos,size,style);
newPtr((void *) Result, 0, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxGrid");
break;
@@ -2655,7 +2664,7 @@ case wxGrid_new_4: { // wxGrid::wxGrid
style = (long)*(int *) bp; bp += 4;
} break;
}};
- wxGrid * Result = new EwxGrid(parent,(int) *x,(int) *y,w,h,style);
+ wxGrid * Result = new EwxGrid(parent,*x,*y,w,h,style);
newPtr((void *) Result, 0, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxGrid");
break;
@@ -2712,7 +2721,7 @@ case wxGrid_AutoSizeColumn: { // wxGrid::AutoSizeColumn
} break;
}};
if(!This) throw wxe_badarg(0);
- This->AutoSizeColumn((int) *col,setAsMin);
+ This->AutoSizeColumn(*col,setAsMin);
break;
}
case wxGrid_AutoSizeColumns: { // wxGrid::AutoSizeColumns
@@ -2738,7 +2747,7 @@ case wxGrid_AutoSizeRow: { // wxGrid::AutoSizeRow
} break;
}};
if(!This) throw wxe_badarg(0);
- This->AutoSizeRow((int) *row,setAsMin);
+ This->AutoSizeRow(*row,setAsMin);
break;
}
case wxGrid_AutoSizeRows: { // wxGrid::AutoSizeRows
@@ -2806,7 +2815,7 @@ case wxGrid_CellToRect_2: { // wxGrid::CellToRect
int * row = (int *) bp; bp += 4;
int * col = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxRect Result = This->CellToRect((int) *row,(int) *col);
+ wxRect Result = This->CellToRect(*row,*col);
rt.add(Result);
break;
}
@@ -2844,7 +2853,7 @@ selmode = *(wxGrid::wxGridSelectionModes *) bp; bp += 4;;
} break;
}};
if(!This) throw wxe_badarg(0);
- bool Result = This->CreateGrid((int) *numRows,(int) *numCols,(wxGrid::wxGridSelectionModes) selmode);
+ bool Result = This->CreateGrid(*numRows,*numCols,selmode);
rt.addBool(Result);
break;
}
@@ -2972,7 +2981,7 @@ case wxGrid_EnableEditing: { // wxGrid::EnableEditing
wxGrid *This = (wxGrid *) getPtr(bp,memenv); bp += 4;
bool * edit = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->EnableEditing((bool) *edit);
+ This->EnableEditing(*edit);
break;
}
case wxGrid_EnableGridLines: { // wxGrid::EnableGridLines
@@ -3020,7 +3029,7 @@ case wxGrid_GetCellAlignment: { // wxGrid::GetCellAlignment
int * row = (int *) bp; bp += 4;
int * col = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->GetCellAlignment((int) *row,(int) *col,&horiz,&vert);
+ This->GetCellAlignment(*row,*col,&horiz,&vert);
rt.addInt(horiz);
rt.addInt(vert);
rt.addTupleCount(2);
@@ -3031,7 +3040,7 @@ case wxGrid_GetCellBackgroundColour: { // wxGrid::GetCellBackgroundColour
int * row = (int *) bp; bp += 4;
int * col = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxColour Result = This->GetCellBackgroundColour((int) *row,(int) *col);
+ wxColour Result = This->GetCellBackgroundColour(*row,*col);
rt.add(Result);
break;
}
@@ -3040,7 +3049,7 @@ case wxGrid_GetCellEditor: { // wxGrid::GetCellEditor
int * row = (int *) bp; bp += 4;
int * col = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxGridCellEditor * Result = (wxGridCellEditor*)This->GetCellEditor((int) *row,(int) *col);
+ wxGridCellEditor * Result = (wxGridCellEditor*)This->GetCellEditor(*row,*col);
rt.addRef(getRef((void *)Result,memenv), "wxGridCellEditor");
break;
}
@@ -3049,7 +3058,7 @@ case wxGrid_GetCellFont: { // wxGrid::GetCellFont
int * row = (int *) bp; bp += 4;
int * col = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxFont * Result = new wxFont(This->GetCellFont((int) *row,(int) *col)); newPtr((void *) Result,3, memenv);;
+ wxFont * Result = new wxFont(This->GetCellFont(*row,*col)); newPtr((void *) Result,3, memenv);;
rt.addRef(getRef((void *)Result,memenv), "wxFont");
break;
}
@@ -3058,7 +3067,7 @@ case wxGrid_GetCellRenderer: { // wxGrid::GetCellRenderer
int * row = (int *) bp; bp += 4;
int * col = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxGridCellRenderer * Result = (wxGridCellRenderer*)This->GetCellRenderer((int) *row,(int) *col);
+ wxGridCellRenderer * Result = (wxGridCellRenderer*)This->GetCellRenderer(*row,*col);
rt.addRef(getRef((void *)Result,memenv), "wxGridCellRenderer");
break;
}
@@ -3067,7 +3076,7 @@ case wxGrid_GetCellTextColour: { // wxGrid::GetCellTextColour
int * row = (int *) bp; bp += 4;
int * col = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxColour Result = This->GetCellTextColour((int) *row,(int) *col);
+ wxColour Result = This->GetCellTextColour(*row,*col);
rt.add(Result);
break;
}
@@ -3076,7 +3085,7 @@ case wxGrid_GetCellValue_2: { // wxGrid::GetCellValue
int * row = (int *) bp; bp += 4;
int * col = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxString Result = This->GetCellValue((int) *row,(int) *col);
+ wxString Result = This->GetCellValue(*row,*col);
rt.add(Result);
break;
}
@@ -3112,7 +3121,7 @@ case wxGrid_GetColLabelValue: { // wxGrid::GetColLabelValue
wxGrid *This = (wxGrid *) getPtr(bp,memenv); bp += 4;
int * col = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxString Result = This->GetColLabelValue((int) *col);
+ wxString Result = This->GetColLabelValue(*col);
rt.add(Result);
break;
}
@@ -3181,7 +3190,7 @@ case wxGrid_GetDefaultEditorForCell_2: { // wxGrid::GetDefaultEditorForCell
int * row = (int *) bp; bp += 4;
int * col = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxGridCellEditor * Result = (wxGridCellEditor*)This->GetDefaultEditorForCell((int) *row,(int) *col);
+ wxGridCellEditor * Result = (wxGridCellEditor*)This->GetDefaultEditorForCell(*row,*col);
rt.addRef(getRef((void *)Result,memenv), "wxGridCellEditor");
break;
}
@@ -3217,7 +3226,7 @@ case wxGrid_GetDefaultRendererForCell: { // wxGrid::GetDefaultRendererForCell
int * row = (int *) bp; bp += 4;
int * col = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxGridCellRenderer * Result = (wxGridCellRenderer*)This->GetDefaultRendererForCell((int) *row,(int) *col);
+ wxGridCellRenderer * Result = (wxGridCellRenderer*)This->GetDefaultRendererForCell(*row,*col);
rt.addRef(getRef((void *)Result,memenv), "wxGridCellRenderer");
break;
}
@@ -3313,7 +3322,7 @@ case wxGrid_GetOrCreateCellAttr: { // wxGrid::GetOrCreateCellAttr
int * row = (int *) bp; bp += 4;
int * col = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxGridCellAttr * Result = (wxGridCellAttr*)This->GetOrCreateCellAttr((int) *row,(int) *col);
+ wxGridCellAttr * Result = (wxGridCellAttr*)This->GetOrCreateCellAttr(*row,*col);
rt.addRef(getRef((void *)Result,memenv), "wxGridCellAttr");
break;
}
@@ -3346,7 +3355,7 @@ case wxGrid_GetRowLabelValue: { // wxGrid::GetRowLabelValue
wxGrid *This = (wxGrid *) getPtr(bp,memenv); bp += 4;
int * row = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxString Result = This->GetRowLabelValue((int) *row);
+ wxString Result = This->GetRowLabelValue(*row);
rt.add(Result);
break;
}
@@ -3354,7 +3363,7 @@ case wxGrid_GetRowSize: { // wxGrid::GetRowSize
wxGrid *This = (wxGrid *) getPtr(bp,memenv); bp += 4;
int * row = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->GetRowSize((int) *row);
+ int Result = This->GetRowSize(*row);
rt.addInt(Result);
break;
}
@@ -3541,7 +3550,7 @@ case wxGrid_IsInSelection_2: { // wxGrid::IsInSelection
int * row = (int *) bp; bp += 4;
int * col = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->IsInSelection((int) *row,(int) *col);
+ bool Result = This->IsInSelection(*row,*col);
rt.addBool(Result);
break;
}
@@ -3560,7 +3569,7 @@ case wxGrid_IsReadOnly: { // wxGrid::IsReadOnly
int * row = (int *) bp; bp += 4;
int * col = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->IsReadOnly((int) *row,(int) *col);
+ bool Result = This->IsReadOnly(*row,*col);
rt.addBool(Result);
break;
}
@@ -3583,7 +3592,7 @@ case wxGrid_IsVisible_3: { // wxGrid::IsVisible
} break;
}};
if(!This) throw wxe_badarg(0);
- bool Result = This->IsVisible((int) *row,(int) *col,wholeCellVisible);
+ bool Result = This->IsVisible(*row,*col,wholeCellVisible);
rt.addBool(Result);
break;
}
@@ -3609,7 +3618,7 @@ case wxGrid_MakeCellVisible_2: { // wxGrid::MakeCellVisible
int * row = (int *) bp; bp += 4;
int * col = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->MakeCellVisible((int) *row,(int) *col);
+ This->MakeCellVisible(*row,*col);
break;
}
case wxGrid_MakeCellVisible_1: { // wxGrid::MakeCellVisible
@@ -3625,7 +3634,7 @@ case wxGrid_MoveCursorDown: { // wxGrid::MoveCursorDown
wxGrid *This = (wxGrid *) getPtr(bp,memenv); bp += 4;
bool * expandSelection = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->MoveCursorDown((bool) *expandSelection);
+ bool Result = This->MoveCursorDown(*expandSelection);
rt.addBool(Result);
break;
}
@@ -3633,7 +3642,7 @@ case wxGrid_MoveCursorLeft: { // wxGrid::MoveCursorLeft
wxGrid *This = (wxGrid *) getPtr(bp,memenv); bp += 4;
bool * expandSelection = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->MoveCursorLeft((bool) *expandSelection);
+ bool Result = This->MoveCursorLeft(*expandSelection);
rt.addBool(Result);
break;
}
@@ -3641,7 +3650,7 @@ case wxGrid_MoveCursorRight: { // wxGrid::MoveCursorRight
wxGrid *This = (wxGrid *) getPtr(bp,memenv); bp += 4;
bool * expandSelection = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->MoveCursorRight((bool) *expandSelection);
+ bool Result = This->MoveCursorRight(*expandSelection);
rt.addBool(Result);
break;
}
@@ -3649,7 +3658,7 @@ case wxGrid_MoveCursorUp: { // wxGrid::MoveCursorUp
wxGrid *This = (wxGrid *) getPtr(bp,memenv); bp += 4;
bool * expandSelection = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->MoveCursorUp((bool) *expandSelection);
+ bool Result = This->MoveCursorUp(*expandSelection);
rt.addBool(Result);
break;
}
@@ -3657,7 +3666,7 @@ case wxGrid_MoveCursorDownBlock: { // wxGrid::MoveCursorDownBlock
wxGrid *This = (wxGrid *) getPtr(bp,memenv); bp += 4;
bool * expandSelection = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->MoveCursorDownBlock((bool) *expandSelection);
+ bool Result = This->MoveCursorDownBlock(*expandSelection);
rt.addBool(Result);
break;
}
@@ -3665,7 +3674,7 @@ case wxGrid_MoveCursorLeftBlock: { // wxGrid::MoveCursorLeftBlock
wxGrid *This = (wxGrid *) getPtr(bp,memenv); bp += 4;
bool * expandSelection = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->MoveCursorLeftBlock((bool) *expandSelection);
+ bool Result = This->MoveCursorLeftBlock(*expandSelection);
rt.addBool(Result);
break;
}
@@ -3673,7 +3682,7 @@ case wxGrid_MoveCursorRightBlock: { // wxGrid::MoveCursorRightBlock
wxGrid *This = (wxGrid *) getPtr(bp,memenv); bp += 4;
bool * expandSelection = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->MoveCursorRightBlock((bool) *expandSelection);
+ bool Result = This->MoveCursorRightBlock(*expandSelection);
rt.addBool(Result);
break;
}
@@ -3681,7 +3690,7 @@ case wxGrid_MoveCursorUpBlock: { // wxGrid::MoveCursorUpBlock
wxGrid *This = (wxGrid *) getPtr(bp,memenv); bp += 4;
bool * expandSelection = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->MoveCursorUpBlock((bool) *expandSelection);
+ bool Result = This->MoveCursorUpBlock(*expandSelection);
rt.addBool(Result);
break;
}
@@ -3736,7 +3745,7 @@ case wxGrid_SelectBlock_5: { // wxGrid::SelectBlock
} break;
}};
if(!This) throw wxe_badarg(0);
- This->SelectBlock((int) *topRow,(int) *leftCol,(int) *bottomRow,(int) *rightCol,addToSelected);
+ This->SelectBlock(*topRow,*leftCol,*bottomRow,*rightCol,addToSelected);
break;
}
case wxGrid_SelectBlock_3: { // wxGrid::SelectBlock
@@ -3768,7 +3777,7 @@ case wxGrid_SelectCol: { // wxGrid::SelectCol
} break;
}};
if(!This) throw wxe_badarg(0);
- This->SelectCol((int) *col,addToSelected);
+ This->SelectCol(*col,addToSelected);
break;
}
case wxGrid_SelectRow: { // wxGrid::SelectRow
@@ -3781,7 +3790,7 @@ case wxGrid_SelectRow: { // wxGrid::SelectRow
} break;
}};
if(!This) throw wxe_badarg(0);
- This->SelectRow((int) *row,addToSelected);
+ This->SelectRow(*row,addToSelected);
break;
}
case wxGrid_SetCellAlignment_4: { // wxGrid::SetCellAlignment
@@ -3791,7 +3800,7 @@ case wxGrid_SetCellAlignment_4: { // wxGrid::SetCellAlignment
int * horiz = (int *) bp; bp += 4;
int * vert = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetCellAlignment((int) *row,(int) *col,(int) *horiz,(int) *vert);
+ This->SetCellAlignment(*row,*col,*horiz,*vert);
break;
}
case wxGrid_SetCellAlignment_3: { // wxGrid::SetCellAlignment
@@ -3800,14 +3809,14 @@ case wxGrid_SetCellAlignment_3: { // wxGrid::SetCellAlignment
int * row = (int *) bp; bp += 4;
int * col = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetCellAlignment((int) *align,(int) *row,(int) *col);
+ This->SetCellAlignment(*align,*row,*col);
break;
}
case wxGrid_SetCellAlignment_1: { // wxGrid::SetCellAlignment
wxGrid *This = (wxGrid *) getPtr(bp,memenv); bp += 4;
int * align = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetCellAlignment((int) *align);
+ This->SetCellAlignment(*align);
break;
}
case wxGrid_SetCellBackgroundColour_3_0: { // wxGrid::SetCellBackgroundColour
@@ -3820,7 +3829,7 @@ case wxGrid_SetCellBackgroundColour_3_0: { // wxGrid::SetCellBackgroundColour
int * valA = (int *) bp; bp += 4;
wxColour val = wxColour(*valR,*valG,*valB,*valA);
if(!This) throw wxe_badarg(0);
- This->SetCellBackgroundColour((int) *row,(int) *col,val);
+ This->SetCellBackgroundColour(*row,*col,val);
break;
}
case wxGrid_SetCellBackgroundColour_1: { // wxGrid::SetCellBackgroundColour
@@ -3844,7 +3853,7 @@ case wxGrid_SetCellBackgroundColour_3_1: { // wxGrid::SetCellBackgroundColour
int * row = (int *) bp; bp += 4;
int * col = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetCellBackgroundColour(colour,(int) *row,(int) *col);
+ This->SetCellBackgroundColour(colour,*row,*col);
break;
}
case wxGrid_SetCellEditor: { // wxGrid::SetCellEditor
@@ -3853,7 +3862,7 @@ case wxGrid_SetCellEditor: { // wxGrid::SetCellEditor
int * col = (int *) bp; bp += 4;
wxGridCellEditor *editor = (wxGridCellEditor *) getPtr(bp,memenv); bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetCellEditor((int) *row,(int) *col,editor);
+ This->SetCellEditor(*row,*col,editor);
break;
}
case wxGrid_SetCellFont: { // wxGrid::SetCellFont
@@ -3862,7 +3871,7 @@ case wxGrid_SetCellFont: { // wxGrid::SetCellFont
int * col = (int *) bp; bp += 4;
wxFont *val = (wxFont *) getPtr(bp,memenv); bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetCellFont((int) *row,(int) *col,*val);
+ This->SetCellFont(*row,*col,*val);
break;
}
case wxGrid_SetCellRenderer: { // wxGrid::SetCellRenderer
@@ -3871,7 +3880,7 @@ case wxGrid_SetCellRenderer: { // wxGrid::SetCellRenderer
int * col = (int *) bp; bp += 4;
wxGridCellRenderer *renderer = (wxGridCellRenderer *) getPtr(bp,memenv); bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetCellRenderer((int) *row,(int) *col,renderer);
+ This->SetCellRenderer(*row,*col,renderer);
break;
}
case wxGrid_SetCellTextColour_3_0: { // wxGrid::SetCellTextColour
@@ -3884,7 +3893,7 @@ case wxGrid_SetCellTextColour_3_0: { // wxGrid::SetCellTextColour
int * valA = (int *) bp; bp += 4;
wxColour val = wxColour(*valR,*valG,*valB,*valA);
if(!This) throw wxe_badarg(0);
- This->SetCellTextColour((int) *row,(int) *col,val);
+ This->SetCellTextColour(*row,*col,val);
break;
}
case wxGrid_SetCellTextColour_3_1: { // wxGrid::SetCellTextColour
@@ -3897,7 +3906,7 @@ case wxGrid_SetCellTextColour_3_1: { // wxGrid::SetCellTextColour
int * row = (int *) bp; bp += 4;
int * col = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetCellTextColour(val,(int) *row,(int) *col);
+ This->SetCellTextColour(val,*row,*col);
break;
}
case wxGrid_SetCellTextColour_1: { // wxGrid::SetCellTextColour
@@ -3919,7 +3928,7 @@ case wxGrid_SetCellValue_3_0: { // wxGrid::SetCellValue
wxString s = wxString(bp, wxConvUTF8);
bp += *sLen+((8-((0+ *sLen) & 7)) & 7);
if(!This) throw wxe_badarg(0);
- This->SetCellValue((int) *row,(int) *col,s);
+ This->SetCellValue(*row,*col,s);
break;
}
case wxGrid_SetCellValue_2: { // wxGrid::SetCellValue
@@ -3942,7 +3951,7 @@ case wxGrid_SetCellValue_3_1: { // wxGrid::SetCellValue
int * row = (int *) bp; bp += 4;
int * col = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetCellValue(val,(int) *row,(int) *col);
+ This->SetCellValue(val,*row,*col);
break;
}
case wxGrid_SetColAttr: { // wxGrid::SetColAttr
@@ -3950,21 +3959,21 @@ case wxGrid_SetColAttr: { // wxGrid::SetColAttr
int * col = (int *) bp; bp += 4;
wxGridCellAttr *attr = (wxGridCellAttr *) getPtr(bp,memenv); bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetColAttr((int) *col,attr);
+ This->SetColAttr(*col,attr);
break;
}
case wxGrid_SetColFormatBool: { // wxGrid::SetColFormatBool
wxGrid *This = (wxGrid *) getPtr(bp,memenv); bp += 4;
int * col = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetColFormatBool((int) *col);
+ This->SetColFormatBool(*col);
break;
}
case wxGrid_SetColFormatNumber: { // wxGrid::SetColFormatNumber
wxGrid *This = (wxGrid *) getPtr(bp,memenv); bp += 4;
int * col = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetColFormatNumber((int) *col);
+ This->SetColFormatNumber(*col);
break;
}
case wxGrid_SetColFormatFloat: { // wxGrid::SetColFormatFloat
@@ -3981,7 +3990,7 @@ case wxGrid_SetColFormatFloat: { // wxGrid::SetColFormatFloat
} break;
}};
if(!This) throw wxe_badarg(0);
- This->SetColFormatFloat((int) *col,width,precision);
+ This->SetColFormatFloat(*col,width,precision);
break;
}
case wxGrid_SetColFormatCustom: { // wxGrid::SetColFormatCustom
@@ -3991,7 +4000,7 @@ case wxGrid_SetColFormatCustom: { // wxGrid::SetColFormatCustom
wxString typeName = wxString(bp, wxConvUTF8);
bp += *typeNameLen+((8-((4+ *typeNameLen) & 7)) & 7);
if(!This) throw wxe_badarg(0);
- This->SetColFormatCustom((int) *col,typeName);
+ This->SetColFormatCustom(*col,typeName);
break;
}
case wxGrid_SetColLabelAlignment: { // wxGrid::SetColLabelAlignment
@@ -3999,14 +4008,14 @@ case wxGrid_SetColLabelAlignment: { // wxGrid::SetColLabelAlignment
int * horiz = (int *) bp; bp += 4;
int * vert = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetColLabelAlignment((int) *horiz,(int) *vert);
+ This->SetColLabelAlignment(*horiz,*vert);
break;
}
case wxGrid_SetColLabelSize: { // wxGrid::SetColLabelSize
wxGrid *This = (wxGrid *) getPtr(bp,memenv); bp += 4;
int * height = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetColLabelSize((int) *height);
+ This->SetColLabelSize(*height);
break;
}
case wxGrid_SetColLabelValue: { // wxGrid::SetColLabelValue
@@ -4016,7 +4025,7 @@ case wxGrid_SetColLabelValue: { // wxGrid::SetColLabelValue
wxString val = wxString(bp, wxConvUTF8);
bp += *valLen+((8-((4+ *valLen) & 7)) & 7);
if(!This) throw wxe_badarg(0);
- This->SetColLabelValue((int) *col,val);
+ This->SetColLabelValue(*col,val);
break;
}
case wxGrid_SetColMinimalWidth: { // wxGrid::SetColMinimalWidth
@@ -4024,14 +4033,14 @@ case wxGrid_SetColMinimalWidth: { // wxGrid::SetColMinimalWidth
int * col = (int *) bp; bp += 4;
int * width = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetColMinimalWidth((int) *col,(int) *width);
+ This->SetColMinimalWidth(*col,*width);
break;
}
case wxGrid_SetColMinimalAcceptableWidth: { // wxGrid::SetColMinimalAcceptableWidth
wxGrid *This = (wxGrid *) getPtr(bp,memenv); bp += 4;
int * width = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetColMinimalAcceptableWidth((int) *width);
+ This->SetColMinimalAcceptableWidth(*width);
break;
}
case wxGrid_SetColSize: { // wxGrid::SetColSize
@@ -4039,7 +4048,7 @@ case wxGrid_SetColSize: { // wxGrid::SetColSize
int * col = (int *) bp; bp += 4;
int * width = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetColSize((int) *col,(int) *width);
+ This->SetColSize(*col,*width);
break;
}
case wxGrid_SetDefaultCellAlignment: { // wxGrid::SetDefaultCellAlignment
@@ -4047,7 +4056,7 @@ case wxGrid_SetDefaultCellAlignment: { // wxGrid::SetDefaultCellAlignment
int * horiz = (int *) bp; bp += 4;
int * vert = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetDefaultCellAlignment((int) *horiz,(int) *vert);
+ This->SetDefaultCellAlignment(*horiz,*vert);
break;
}
case wxGrid_SetDefaultCellBackgroundColour: { // wxGrid::SetDefaultCellBackgroundColour
@@ -4103,7 +4112,7 @@ case wxGrid_SetDefaultColSize: { // wxGrid::SetDefaultColSize
} break;
}};
if(!This) throw wxe_badarg(0);
- This->SetDefaultColSize((int) *width,resizeExistingCols);
+ This->SetDefaultColSize(*width,resizeExistingCols);
break;
}
case wxGrid_SetDefaultRowSize: { // wxGrid::SetDefaultRowSize
@@ -4116,7 +4125,7 @@ case wxGrid_SetDefaultRowSize: { // wxGrid::SetDefaultRowSize
} break;
}};
if(!This) throw wxe_badarg(0);
- This->SetDefaultRowSize((int) *height,resizeExistingRows);
+ This->SetDefaultRowSize(*height,resizeExistingRows);
break;
}
case wxGrid_SetGridCursor: { // wxGrid::SetGridCursor
@@ -4124,7 +4133,7 @@ case wxGrid_SetGridCursor: { // wxGrid::SetGridCursor
int * row = (int *) bp; bp += 4;
int * col = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetGridCursor((int) *row,(int) *col);
+ This->SetGridCursor(*row,*col);
break;
}
case wxGrid_SetGridLineColour: { // wxGrid::SetGridLineColour
@@ -4172,7 +4181,7 @@ case wxGrid_SetMargins: { // wxGrid::SetMargins
int * extraWidth = (int *) bp; bp += 4;
int * extraHeight = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetMargins((int) *extraWidth,(int) *extraHeight);
+ This->SetMargins(*extraWidth,*extraHeight);
break;
}
case wxGrid_SetReadOnly: { // wxGrid::SetReadOnly
@@ -4187,7 +4196,7 @@ case wxGrid_SetReadOnly: { // wxGrid::SetReadOnly
} break;
}};
if(!This) throw wxe_badarg(0);
- This->SetReadOnly((int) *row,(int) *col,isReadOnly);
+ This->SetReadOnly(*row,*col,isReadOnly);
break;
}
case wxGrid_SetRowAttr: { // wxGrid::SetRowAttr
@@ -4195,7 +4204,7 @@ case wxGrid_SetRowAttr: { // wxGrid::SetRowAttr
int * row = (int *) bp; bp += 4;
wxGridCellAttr *attr = (wxGridCellAttr *) getPtr(bp,memenv); bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetRowAttr((int) *row,attr);
+ This->SetRowAttr(*row,attr);
break;
}
case wxGrid_SetRowLabelAlignment: { // wxGrid::SetRowLabelAlignment
@@ -4203,14 +4212,14 @@ case wxGrid_SetRowLabelAlignment: { // wxGrid::SetRowLabelAlignment
int * horiz = (int *) bp; bp += 4;
int * vert = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetRowLabelAlignment((int) *horiz,(int) *vert);
+ This->SetRowLabelAlignment(*horiz,*vert);
break;
}
case wxGrid_SetRowLabelSize: { // wxGrid::SetRowLabelSize
wxGrid *This = (wxGrid *) getPtr(bp,memenv); bp += 4;
int * width = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetRowLabelSize((int) *width);
+ This->SetRowLabelSize(*width);
break;
}
case wxGrid_SetRowLabelValue: { // wxGrid::SetRowLabelValue
@@ -4220,7 +4229,7 @@ case wxGrid_SetRowLabelValue: { // wxGrid::SetRowLabelValue
wxString val = wxString(bp, wxConvUTF8);
bp += *valLen+((8-((4+ *valLen) & 7)) & 7);
if(!This) throw wxe_badarg(0);
- This->SetRowLabelValue((int) *row,val);
+ This->SetRowLabelValue(*row,val);
break;
}
case wxGrid_SetRowMinimalHeight: { // wxGrid::SetRowMinimalHeight
@@ -4228,14 +4237,14 @@ case wxGrid_SetRowMinimalHeight: { // wxGrid::SetRowMinimalHeight
int * row = (int *) bp; bp += 4;
int * width = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetRowMinimalHeight((int) *row,(int) *width);
+ This->SetRowMinimalHeight(*row,*width);
break;
}
case wxGrid_SetRowMinimalAcceptableHeight: { // wxGrid::SetRowMinimalAcceptableHeight
wxGrid *This = (wxGrid *) getPtr(bp,memenv); bp += 4;
int * width = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetRowMinimalAcceptableHeight((int) *width);
+ This->SetRowMinimalAcceptableHeight(*width);
break;
}
case wxGrid_SetRowSize: { // wxGrid::SetRowSize
@@ -4243,21 +4252,21 @@ case wxGrid_SetRowSize: { // wxGrid::SetRowSize
int * row = (int *) bp; bp += 4;
int * height = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetRowSize((int) *row,(int) *height);
+ This->SetRowSize(*row,*height);
break;
}
case wxGrid_SetScrollLineX: { // wxGrid::SetScrollLineX
wxGrid *This = (wxGrid *) getPtr(bp,memenv); bp += 4;
int * x = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetScrollLineX((int) *x);
+ This->SetScrollLineX(*x);
break;
}
case wxGrid_SetScrollLineY: { // wxGrid::SetScrollLineY
wxGrid *This = (wxGrid *) getPtr(bp,memenv); bp += 4;
int * y = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetScrollLineY((int) *y);
+ This->SetScrollLineY(*y);
break;
}
case wxGrid_SetSelectionBackground: { // wxGrid::SetSelectionBackground
@@ -4286,7 +4295,7 @@ case wxGrid_SetSelectionMode: { // wxGrid::SetSelectionMode
wxGrid *This = (wxGrid *) getPtr(bp,memenv); bp += 4;
wxGrid::wxGridSelectionModes selmode = *(wxGrid::wxGridSelectionModes *) bp; bp += 4;;
if(!This) throw wxe_badarg(0);
- This->SetSelectionMode((wxGrid::wxGridSelectionModes) selmode);
+ This->SetSelectionMode(selmode);
break;
}
case wxGrid_ShowCellEditControl: { // wxGrid::ShowCellEditControl
@@ -4305,7 +4314,7 @@ case wxGrid_XToCol: { // wxGrid::XToCol
} break;
}};
if(!This) throw wxe_badarg(0);
- int Result = This->XToCol((int) *x,clipToMinMax);
+ int Result = This->XToCol(*x,clipToMinMax);
rt.addInt(Result);
break;
}
@@ -4313,7 +4322,7 @@ case wxGrid_XToEdgeOfCol: { // wxGrid::XToEdgeOfCol
wxGrid *This = (wxGrid *) getPtr(bp,memenv); bp += 4;
int * x = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->XToEdgeOfCol((int) *x);
+ int Result = This->XToEdgeOfCol(*x);
rt.addInt(Result);
break;
}
@@ -4321,7 +4330,7 @@ case wxGrid_YToEdgeOfRow: { // wxGrid::YToEdgeOfRow
wxGrid *This = (wxGrid *) getPtr(bp,memenv); bp += 4;
int * y = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->YToEdgeOfRow((int) *y);
+ int Result = This->YToEdgeOfRow(*y);
rt.addInt(Result);
break;
}
@@ -4329,7 +4338,7 @@ case wxGrid_YToRow: { // wxGrid::YToRow
wxGrid *This = (wxGrid *) getPtr(bp,memenv); bp += 4;
int * y = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->YToRow((int) *y);
+ int Result = This->YToRow(*y);
rt.addInt(Result);
break;
}
@@ -4347,7 +4356,7 @@ case wxGridCellRenderer_Draw: { // wxGridCellRenderer::Draw
int * col = (int *) bp; bp += 4;
bool * isSelected = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->Draw(*grid,*attr,*dc,rect,(int) *row,(int) *col,(bool) *isSelected);
+ This->Draw(*grid,*attr,*dc,rect,*row,*col,*isSelected);
break;
}
case wxGridCellRenderer_GetBestSize: { // wxGridCellRenderer::GetBestSize
@@ -4358,7 +4367,7 @@ case wxGridCellRenderer_GetBestSize: { // wxGridCellRenderer::GetBestSize
int * row = (int *) bp; bp += 4;
int * col = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxSize Result = This->GetBestSize(*grid,*attr,*dc,(int) *row,(int) *col);
+ wxSize Result = This->GetBestSize(*grid,*attr,*dc,*row,*col);
rt.add(Result);
break;
}
@@ -4368,7 +4377,7 @@ case wxGridCellEditor_Create: { // wxGridCellEditor::Create
int * id = (int *) bp; bp += 4;
wxEvtHandler *evtHandler = (wxEvtHandler *) getPtr(bp,memenv); bp += 4;
if(!This) throw wxe_badarg(0);
- This->Create(parent,(wxWindowID) *id,evtHandler);
+ This->Create(parent,*id,evtHandler);
break;
}
case wxGridCellEditor_IsCreated: { // wxGridCellEditor::IsCreated
@@ -4399,9 +4408,10 @@ attr = (wxGridCellAttr *) getPtr(bp,memenv); bp += 4;
} break;
}};
if(!This) throw wxe_badarg(0);
- This->Show((bool) *show,attr);
+ This->Show(*show,attr);
break;
}
+#if !wxCHECK_VERSION(2,9,0)
case wxGridCellEditor_PaintBackground: { // wxGridCellEditor::PaintBackground
wxGridCellEditor *This = (wxGridCellEditor *) getPtr(bp,memenv); bp += 4;
int * rectCellX = (int *) bp; bp += 4;
@@ -4414,25 +4424,28 @@ case wxGridCellEditor_PaintBackground: { // wxGridCellEditor::PaintBackground
This->PaintBackground(rectCell,attr);
break;
}
+#endif
case wxGridCellEditor_BeginEdit: { // wxGridCellEditor::BeginEdit
wxGridCellEditor *This = (wxGridCellEditor *) getPtr(bp,memenv); bp += 4;
int * row = (int *) bp; bp += 4;
int * col = (int *) bp; bp += 4;
wxGrid *grid = (wxGrid *) getPtr(bp,memenv); bp += 4;
if(!This) throw wxe_badarg(0);
- This->BeginEdit((int) *row,(int) *col,grid);
+ This->BeginEdit(*row,*col,grid);
break;
}
+#if !wxCHECK_VERSION(2,9,0)
case wxGridCellEditor_EndEdit: { // wxGridCellEditor::EndEdit
wxGridCellEditor *This = (wxGridCellEditor *) getPtr(bp,memenv); bp += 4;
int * row = (int *) bp; bp += 4;
int * col = (int *) bp; bp += 4;
wxGrid *grid = (wxGrid *) getPtr(bp,memenv); bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->EndEdit((int) *row,(int) *col,grid);
+ bool Result = This->EndEdit(*row,*col,grid);
rt.addBool(Result);
break;
}
+#endif
case wxGridCellEditor_Reset: { // wxGridCellEditor::Reset
wxGridCellEditor *This = (wxGridCellEditor *) getPtr(bp,memenv); bp += 4;
if(!This) throw wxe_badarg(0);
@@ -4552,14 +4565,14 @@ case wxGridCellFloatRenderer_SetPrecision: { // wxGridCellFloatRenderer::SetPrec
wxGridCellFloatRenderer *This = (wxGridCellFloatRenderer *) getPtr(bp,memenv); bp += 4;
int * precision = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetPrecision((int) *precision);
+ This->SetPrecision(*precision);
break;
}
case wxGridCellFloatRenderer_SetWidth: { // wxGridCellFloatRenderer::SetWidth
wxGridCellFloatRenderer *This = (wxGridCellFloatRenderer *) getPtr(bp,memenv); bp += 4;
int * width = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetWidth((int) *width);
+ This->SetWidth(*width);
break;
}
case wxGridCellFloatRenderer_destroy: { // wxGridCellFloatRenderer::destroy
@@ -4753,7 +4766,7 @@ case wxGridCellAttr_SetAlignment: { // wxGridCellAttr::SetAlignment
int * hAlign = (int *) bp; bp += 4;
int * vAlign = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetAlignment((int) *hAlign,(int) *vAlign);
+ This->SetAlignment(*hAlign,*vAlign);
break;
}
case wxGridCellAttr_SetReadOnly: { // wxGridCellAttr::SetReadOnly
@@ -4863,7 +4876,7 @@ case wxGridCellAttr_GetRenderer: { // wxGridCellAttr::GetRenderer
int * row = (int *) bp; bp += 4;
int * col = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxGridCellRenderer * Result = (wxGridCellRenderer*)This->GetRenderer(grid,(int) *row,(int) *col);
+ wxGridCellRenderer * Result = (wxGridCellRenderer*)This->GetRenderer(grid,*row,*col);
rt.addRef(getRef((void *)Result,memenv), "wxGridCellRenderer");
break;
}
@@ -4873,7 +4886,7 @@ case wxGridCellAttr_GetEditor: { // wxGridCellAttr::GetEditor
int * row = (int *) bp; bp += 4;
int * col = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxGridCellEditor * Result = (wxGridCellEditor*)This->GetEditor(grid,(int) *row,(int) *col);
+ wxGridCellEditor * Result = (wxGridCellEditor*)This->GetEditor(grid,*row,*col);
rt.addRef(getRef((void *)Result,memenv), "wxGridCellEditor");
break;
}
@@ -4892,7 +4905,7 @@ case wxGridCellAttr_SetDefAttr: { // wxGridCellAttr::SetDefAttr
break;
}
case wxDC_Blit: { // wxDC::Blit
- int rop=wxCOPY;
+ wxRasterOperationMode rop=wxCOPY;
bool useMask=false;
wxPoint srcPtMask= wxDefaultPosition;
wxDC *This = (wxDC *) getPtr(bp,memenv); bp += 4;
@@ -4908,7 +4921,7 @@ case wxDC_Blit: { // wxDC::Blit
wxPoint srcPt = wxPoint(*srcPtX,*srcPtY);
while( * (int*) bp) { switch (* (int*) bp) {
case 1: {bp += 4;
- rop = (int)*(int *) bp; bp += 4;
+rop = *(wxRasterOperationMode *) bp; bp += 4;;
} break;
case 2: {bp += 4;
useMask = *(bool *) bp; bp += 4;
@@ -4930,7 +4943,7 @@ case wxDC_CalcBoundingBox: { // wxDC::CalcBoundingBox
int * x = (int *) bp; bp += 4;
int * y = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->CalcBoundingBox((wxCoord) *x,(wxCoord) *y);
+ This->CalcBoundingBox(*x,*y);
break;
}
case wxDC_Clear: { // wxDC::Clear
@@ -4939,12 +4952,14 @@ case wxDC_Clear: { // wxDC::Clear
This->Clear();
break;
}
+#if !wxCHECK_VERSION(2,9,0)
case wxDC_ComputeScaleAndOrigin: { // wxDC::ComputeScaleAndOrigin
wxDC *This = (wxDC *) getPtr(bp,memenv); bp += 4;
if(!This) throw wxe_badarg(0);
This->ComputeScaleAndOrigin();
break;
}
+#endif
case wxDC_CrossHair: { // wxDC::CrossHair
wxDC *This = (wxDC *) getPtr(bp,memenv); bp += 4;
int * ptX = (int *) bp; bp += 4;
@@ -4964,7 +4979,7 @@ case wxDC_DeviceToLogicalX: { // wxDC::DeviceToLogicalX
wxDC *This = (wxDC *) getPtr(bp,memenv); bp += 4;
int * x = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxCoord Result = This->DeviceToLogicalX((wxCoord) *x);
+ wxCoord Result = This->DeviceToLogicalX(*x);
rt.addInt(Result);
break;
}
@@ -4972,7 +4987,7 @@ case wxDC_DeviceToLogicalXRel: { // wxDC::DeviceToLogicalXRel
wxDC *This = (wxDC *) getPtr(bp,memenv); bp += 4;
int * x = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxCoord Result = This->DeviceToLogicalXRel((wxCoord) *x);
+ wxCoord Result = This->DeviceToLogicalXRel(*x);
rt.addInt(Result);
break;
}
@@ -4980,7 +4995,7 @@ case wxDC_DeviceToLogicalY: { // wxDC::DeviceToLogicalY
wxDC *This = (wxDC *) getPtr(bp,memenv); bp += 4;
int * y = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxCoord Result = This->DeviceToLogicalY((wxCoord) *y);
+ wxCoord Result = This->DeviceToLogicalY(*y);
rt.addInt(Result);
break;
}
@@ -4988,7 +5003,7 @@ case wxDC_DeviceToLogicalYRel: { // wxDC::DeviceToLogicalYRel
wxDC *This = (wxDC *) getPtr(bp,memenv); bp += 4;
int * y = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxCoord Result = This->DeviceToLogicalYRel((wxCoord) *y);
+ wxCoord Result = This->DeviceToLogicalYRel(*y);
rt.addInt(Result);
break;
}
@@ -5041,7 +5056,7 @@ case wxDC_DrawCircle: { // wxDC::DrawCircle
wxPoint pt = wxPoint(*ptX,*ptY);
int * radius = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->DrawCircle(pt,(wxCoord) *radius);
+ This->DrawCircle(pt,*radius);
break;
}
case wxDC_DrawEllipse_2: { // wxDC::DrawEllipse
@@ -5079,7 +5094,7 @@ case wxDC_DrawEllipticArc: { // wxDC::DrawEllipticArc
double * sa = (double *) bp; bp += 8;
double * ea = (double *) bp; bp += 8;
if(!This) throw wxe_badarg(0);
- This->DrawEllipticArc(pt,sz,(double) *sa,(double) *ea);
+ This->DrawEllipticArc(pt,sz,*sa,*ea);
break;
}
case wxDC_DrawIcon: { // wxDC::DrawIcon
@@ -5155,7 +5170,7 @@ case wxDC_DrawLines: { // wxDC::DrawLines
case wxDC_DrawPolygon: { // wxDC::DrawPolygon
wxCoord xoffset=0;
wxCoord yoffset=0;
- int fillStyle=wxODDEVEN_RULE;
+ wxPolygonFillMode fillStyle=wxODDEVEN_RULE;
wxDC *This = (wxDC *) getPtr(bp,memenv); bp += 4;
int * pointsLen = (int *) bp; bp += 4;
wxPoint *points;
@@ -5172,7 +5187,7 @@ case wxDC_DrawPolygon: { // wxDC::DrawPolygon
yoffset = (wxCoord)*(int *) bp; bp += 4;
} break;
case 3: {bp += 4;
- fillStyle = (int)*(int *) bp; bp += 4;
+fillStyle = *(wxPolygonFillMode *) bp; bp += 4;;
} break;
}};
if(!This) throw wxe_badarg(0);
@@ -5222,7 +5237,7 @@ case wxDC_DrawRotatedText: { // wxDC::DrawRotatedText
wxPoint pt = wxPoint(*ptX,*ptY);
double * angle = (double *) bp; bp += 8;
if(!This) throw wxe_badarg(0);
- This->DrawRotatedText(text,pt,(double) *angle);
+ This->DrawRotatedText(text,pt,*angle);
break;
}
case wxDC_DrawRoundedRectangle_3: { // wxDC::DrawRoundedRectangle
@@ -5236,7 +5251,7 @@ case wxDC_DrawRoundedRectangle_3: { // wxDC::DrawRoundedRectangle
bp += 4; /* Align */
double * radius = (double *) bp; bp += 8;
if(!This) throw wxe_badarg(0);
- This->DrawRoundedRectangle(pt,sz,(double) *radius);
+ This->DrawRoundedRectangle(pt,sz,*radius);
break;
}
case wxDC_DrawRoundedRectangle_2: { // wxDC::DrawRoundedRectangle
@@ -5249,7 +5264,7 @@ case wxDC_DrawRoundedRectangle_2: { // wxDC::DrawRoundedRectangle
bp += 4; /* Align */
double * radius = (double *) bp; bp += 8;
if(!This) throw wxe_badarg(0);
- This->DrawRoundedRectangle(r,(double) *radius);
+ This->DrawRoundedRectangle(r,*radius);
break;
}
case wxDC_DrawText: { // wxDC::DrawText
@@ -5277,7 +5292,7 @@ case wxDC_EndPage: { // wxDC::EndPage
break;
}
case wxDC_FloodFill: { // wxDC::FloodFill
- int style=wxFLOOD_SURFACE;
+ wxFloodFillStyle style=wxFLOOD_SURFACE;
wxDC *This = (wxDC *) getPtr(bp,memenv); bp += 4;
int * ptX = (int *) bp; bp += 4;
int * ptY = (int *) bp; bp += 4;
@@ -5290,7 +5305,7 @@ case wxDC_FloodFill: { // wxDC::FloodFill
bp += 4; /* Align */
while( * (int*) bp) { switch (* (int*) bp) {
case 1: {bp += 4;
- style = (int)*(int *) bp; bp += 4;
+style = *(wxFloodFillStyle *) bp; bp += 4;;
} break;
}};
if(!This) throw wxe_badarg(0);
@@ -5591,14 +5606,14 @@ nDirection = *(wxDirection *) bp; bp += 4;;
} break;
}};
if(!This) throw wxe_badarg(0);
- This->GradientFillLinear(rect,initialColour,destColour,(wxDirection) nDirection);
+ This->GradientFillLinear(rect,initialColour,destColour,nDirection);
break;
}
case wxDC_LogicalToDeviceX: { // wxDC::LogicalToDeviceX
wxDC *This = (wxDC *) getPtr(bp,memenv); bp += 4;
int * x = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxCoord Result = This->LogicalToDeviceX((wxCoord) *x);
+ wxCoord Result = This->LogicalToDeviceX(*x);
rt.addInt(Result);
break;
}
@@ -5606,7 +5621,7 @@ case wxDC_LogicalToDeviceXRel: { // wxDC::LogicalToDeviceXRel
wxDC *This = (wxDC *) getPtr(bp,memenv); bp += 4;
int * x = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxCoord Result = This->LogicalToDeviceXRel((wxCoord) *x);
+ wxCoord Result = This->LogicalToDeviceXRel(*x);
rt.addInt(Result);
break;
}
@@ -5614,7 +5629,7 @@ case wxDC_LogicalToDeviceY: { // wxDC::LogicalToDeviceY
wxDC *This = (wxDC *) getPtr(bp,memenv); bp += 4;
int * y = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxCoord Result = This->LogicalToDeviceY((wxCoord) *y);
+ wxCoord Result = This->LogicalToDeviceY(*y);
rt.addInt(Result);
break;
}
@@ -5622,7 +5637,7 @@ case wxDC_LogicalToDeviceYRel: { // wxDC::LogicalToDeviceYRel
wxDC *This = (wxDC *) getPtr(bp,memenv); bp += 4;
int * y = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxCoord Result = This->LogicalToDeviceYRel((wxCoord) *y);
+ wxCoord Result = This->LogicalToDeviceYRel(*y);
rt.addInt(Result);
break;
}
@@ -5672,7 +5687,7 @@ case wxDC_SetAxisOrientation: { // wxDC::SetAxisOrientation
bool * xLeftRight = (bool *) bp; bp += 4;
bool * yBottomUp = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetAxisOrientation((bool) *xLeftRight,(bool) *yBottomUp);
+ This->SetAxisOrientation(*xLeftRight,*yBottomUp);
break;
}
case wxDC_SetBackground: { // wxDC::SetBackground
@@ -5686,7 +5701,7 @@ case wxDC_SetBackgroundMode: { // wxDC::SetBackgroundMode
wxDC *This = (wxDC *) getPtr(bp,memenv); bp += 4;
int * mode = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetBackgroundMode((int) *mode);
+ This->SetBackgroundMode(*mode);
break;
}
case wxDC_SetBrush: { // wxDC::SetBrush
@@ -5731,7 +5746,7 @@ case wxDC_SetDeviceOrigin: { // wxDC::SetDeviceOrigin
int * x = (int *) bp; bp += 4;
int * y = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetDeviceOrigin((wxCoord) *x,(wxCoord) *y);
+ This->SetDeviceOrigin(*x,*y);
break;
}
case wxDC_SetFont: { // wxDC::SetFont
@@ -5745,21 +5760,21 @@ case wxDC_SetLayoutDirection: { // wxDC::SetLayoutDirection
wxDC *This = (wxDC *) getPtr(bp,memenv); bp += 4;
wxLayoutDirection dir = *(wxLayoutDirection *) bp; bp += 4;;
if(!This) throw wxe_badarg(0);
- This->SetLayoutDirection((wxLayoutDirection) dir);
+ This->SetLayoutDirection(dir);
break;
}
case wxDC_SetLogicalFunction: { // wxDC::SetLogicalFunction
wxDC *This = (wxDC *) getPtr(bp,memenv); bp += 4;
- int * function = (int *) bp; bp += 4;
+ wxRasterOperationMode function = *(wxRasterOperationMode *) bp; bp += 4;;
if(!This) throw wxe_badarg(0);
- This->SetLogicalFunction((int) *function);
+ This->SetLogicalFunction(function);
break;
}
case wxDC_SetMapMode: { // wxDC::SetMapMode
wxDC *This = (wxDC *) getPtr(bp,memenv); bp += 4;
- int * mode = (int *) bp; bp += 4;
+ wxMappingMode mode = *(wxMappingMode *) bp; bp += 4;;
if(!This) throw wxe_badarg(0);
- This->SetMapMode((int) *mode);
+ This->SetMapMode(mode);
break;
}
case wxDC_SetPalette: { // wxDC::SetPalette
@@ -5804,7 +5819,7 @@ case wxDC_SetUserScale: { // wxDC::SetUserScale
double * x = (double *) bp; bp += 8;
double * y = (double *) bp; bp += 8;
if(!This) throw wxe_badarg(0);
- This->SetUserScale((double) *x,(double) *y);
+ This->SetUserScale(*x,*y);
break;
}
case wxDC_StartDoc: { // wxDC::StartDoc
@@ -5826,7 +5841,7 @@ case wxDC_StartPage: { // wxDC::StartPage
case wxMirrorDC_new: { // wxMirrorDC::wxMirrorDC
wxDC *dc = (wxDC *) getPtr(bp,memenv); bp += 4;
bool * mirror = (bool *) bp; bp += 4;
- wxMirrorDC * Result = new EwxMirrorDC(*dc,(bool) *mirror);
+ wxMirrorDC * Result = new EwxMirrorDC(*dc,*mirror);
newPtr((void *) Result, 1, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxMirrorDC");
break;
@@ -5850,22 +5865,28 @@ case wxPostScriptDC_new_1: { // wxPostScriptDC::wxPostScriptDC
rt.addRef(getRef((void *)Result,memenv), "wxPostScriptDC");
break;
}
+#if !wxCHECK_VERSION(2,9,0)
case wxPostScriptDC_SetResolution: { // wxPostScriptDC::SetResolution
int * ppi = (int *) bp; bp += 4;
- wxPostScriptDC::SetResolution((int) *ppi);
+ wxPostScriptDC::SetResolution(*ppi);
break;
}
+#endif
+#if !wxCHECK_VERSION(2,9,0)
case wxPostScriptDC_GetResolution: { // wxPostScriptDC::GetResolution
int Result = wxPostScriptDC::GetResolution();
rt.addInt(Result);
break;
}
+#endif
+#if !wxCHECK_VERSION(2,9,0)
case wxWindowDC_new_0: { // wxWindowDC::wxWindowDC
wxWindowDC * Result = new EwxWindowDC();
newPtr((void *) Result, 1, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxWindowDC");
break;
}
+#endif
case wxWindowDC_new_1: { // wxWindowDC::wxWindowDC
wxWindow *win = (wxWindow *) getPtr(bp,memenv); bp += 4;
wxWindowDC * Result = new EwxWindowDC(win);
@@ -5873,12 +5894,14 @@ case wxWindowDC_new_1: { // wxWindowDC::wxWindowDC
rt.addRef(getRef((void *)Result,memenv), "wxWindowDC");
break;
}
+#if !wxCHECK_VERSION(2,9,0)
case wxClientDC_new_0: { // wxClientDC::wxClientDC
wxClientDC * Result = new EwxClientDC();
newPtr((void *) Result, 1, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxClientDC");
break;
}
+#endif
case wxClientDC_new_1: { // wxClientDC::wxClientDC
wxWindow *win = (wxWindow *) getPtr(bp,memenv); bp += 4;
wxClientDC * Result = new EwxClientDC(win);
@@ -5886,12 +5909,14 @@ case wxClientDC_new_1: { // wxClientDC::wxClientDC
rt.addRef(getRef((void *)Result,memenv), "wxClientDC");
break;
}
+#if !wxCHECK_VERSION(2,9,0)
case wxPaintDC_new_0: { // wxPaintDC::wxPaintDC
wxPaintDC * Result = new EwxPaintDC();
newPtr((void *) Result, 1, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxPaintDC");
break;
}
+#endif
case wxPaintDC_new_1: { // wxPaintDC::wxPaintDC
wxWindow *win = (wxWindow *) getPtr(bp,memenv); bp += 4;
wxPaintDC * Result = new EwxPaintDC(win);
@@ -6085,6 +6110,7 @@ case wxGraphicsContext_CreateBrush: { // wxGraphicsContext::CreateBrush
rt.addRef(getRef((void *)Result,memenv), "wxGraphicsBrush");
break;
}
+#if !wxCHECK_VERSION(2,9,0)
case wxGraphicsContext_CreateRadialGradientBrush: { // wxGraphicsContext::CreateRadialGradientBrush
wxGraphicsContext *This = (wxGraphicsContext *) getPtr(bp,memenv); bp += 4;
bp += 4; /* Align */
@@ -6104,10 +6130,12 @@ case wxGraphicsContext_CreateRadialGradientBrush: { // wxGraphicsContext::Create
int * cColorA = (int *) bp; bp += 4;
wxColour cColor = wxColour(*cColorR,*cColorG,*cColorB,*cColorA);
if(!This) throw wxe_badarg(0);
- wxGraphicsBrush * Result = new wxGraphicsBrush(This->CreateRadialGradientBrush((wxDouble) *xo,(wxDouble) *yo,(wxDouble) *xc,(wxDouble) *yc,(wxDouble) *radius,oColor,cColor)); newPtr((void *) Result,3, memenv);;
+ wxGraphicsBrush * Result = new wxGraphicsBrush(This->CreateRadialGradientBrush(*xo,*yo,*xc,*yc,*radius,oColor,cColor)); newPtr((void *) Result,3, memenv);;
rt.addRef(getRef((void *)Result,memenv), "wxGraphicsBrush");
break;
}
+#endif
+#if !wxCHECK_VERSION(2,9,0)
case wxGraphicsContext_CreateLinearGradientBrush: { // wxGraphicsContext::CreateLinearGradientBrush
wxGraphicsContext *This = (wxGraphicsContext *) getPtr(bp,memenv); bp += 4;
bp += 4; /* Align */
@@ -6126,10 +6154,11 @@ case wxGraphicsContext_CreateLinearGradientBrush: { // wxGraphicsContext::Create
int * c2A = (int *) bp; bp += 4;
wxColour c2 = wxColour(*c2R,*c2G,*c2B,*c2A);
if(!This) throw wxe_badarg(0);
- wxGraphicsBrush * Result = new wxGraphicsBrush(This->CreateLinearGradientBrush((wxDouble) *x1,(wxDouble) *y1,(wxDouble) *x2,(wxDouble) *y2,c1,c2)); newPtr((void *) Result,3, memenv);;
+ wxGraphicsBrush * Result = new wxGraphicsBrush(This->CreateLinearGradientBrush(*x1,*y1,*x2,*y2,c1,c2)); newPtr((void *) Result,3, memenv);;
rt.addRef(getRef((void *)Result,memenv), "wxGraphicsBrush");
break;
}
+#endif
case wxGraphicsContext_CreateFont: { // wxGraphicsContext::CreateFont
wxColour col= *wxBLACK;
wxGraphicsContext *This = (wxGraphicsContext *) getPtr(bp,memenv); bp += 4;
@@ -6211,7 +6240,7 @@ case wxGraphicsContext_Clip_4: { // wxGraphicsContext::Clip
wxDouble * w = (wxDouble *) bp; bp += 8;
wxDouble * h = (wxDouble *) bp; bp += 8;
if(!This) throw wxe_badarg(0);
- This->Clip((wxDouble) *x,(wxDouble) *y,(wxDouble) *w,(wxDouble) *h);
+ This->Clip(*x,*y,*w,*h);
break;
}
case wxGraphicsContext_ResetClip: { // wxGraphicsContext::ResetClip
@@ -6228,7 +6257,7 @@ case wxGraphicsContext_DrawBitmap: { // wxGraphicsContext::DrawBitmap
wxDouble * w = (wxDouble *) bp; bp += 8;
wxDouble * h = (wxDouble *) bp; bp += 8;
if(!This) throw wxe_badarg(0);
- This->DrawBitmap(*bmp,(wxDouble) *x,(wxDouble) *y,(wxDouble) *w,(wxDouble) *h);
+ This->DrawBitmap(*bmp,*x,*y,*w,*h);
break;
}
case wxGraphicsContext_DrawEllipse: { // wxGraphicsContext::DrawEllipse
@@ -6239,7 +6268,7 @@ case wxGraphicsContext_DrawEllipse: { // wxGraphicsContext::DrawEllipse
wxDouble * w = (wxDouble *) bp; bp += 8;
wxDouble * h = (wxDouble *) bp; bp += 8;
if(!This) throw wxe_badarg(0);
- This->DrawEllipse((wxDouble) *x,(wxDouble) *y,(wxDouble) *w,(wxDouble) *h);
+ This->DrawEllipse(*x,*y,*w,*h);
break;
}
case wxGraphicsContext_DrawIcon: { // wxGraphicsContext::DrawIcon
@@ -6250,11 +6279,11 @@ case wxGraphicsContext_DrawIcon: { // wxGraphicsContext::DrawIcon
wxDouble * w = (wxDouble *) bp; bp += 8;
wxDouble * h = (wxDouble *) bp; bp += 8;
if(!This) throw wxe_badarg(0);
- This->DrawIcon(*icon,(wxDouble) *x,(wxDouble) *y,(wxDouble) *w,(wxDouble) *h);
+ This->DrawIcon(*icon,*x,*y,*w,*h);
break;
}
case wxGraphicsContext_DrawLines: { // wxGraphicsContext::DrawLines
- int fillStyle=wxODDEVEN_RULE;
+ wxPolygonFillMode fillStyle=wxODDEVEN_RULE;
wxGraphicsContext *This = (wxGraphicsContext *) getPtr(bp,memenv); bp += 4;
int * pointsLen = (int *) bp; bp += 4;
wxPoint2DDouble *points;
@@ -6265,7 +6294,7 @@ case wxGraphicsContext_DrawLines: { // wxGraphicsContext::DrawLines
points[i] = wxPoint2DDouble(x,y);}
while( * (int*) bp) { switch (* (int*) bp) {
case 1: {bp += 4;
- fillStyle = (int)*(int *) bp; bp += 4;
+fillStyle = *(wxPolygonFillMode *) bp; bp += 4;;
} break;
}};
if(!This) throw wxe_badarg(0);
@@ -6274,12 +6303,12 @@ case wxGraphicsContext_DrawLines: { // wxGraphicsContext::DrawLines
break;
}
case wxGraphicsContext_DrawPath: { // wxGraphicsContext::DrawPath
- int fillStyle=wxODDEVEN_RULE;
+ wxPolygonFillMode fillStyle=wxODDEVEN_RULE;
wxGraphicsContext *This = (wxGraphicsContext *) getPtr(bp,memenv); bp += 4;
wxGraphicsPath *path = (wxGraphicsPath *) getPtr(bp,memenv); bp += 4;
while( * (int*) bp) { switch (* (int*) bp) {
case 1: {bp += 4;
- fillStyle = (int)*(int *) bp; bp += 4;
+fillStyle = *(wxPolygonFillMode *) bp; bp += 4;;
} break;
}};
if(!This) throw wxe_badarg(0);
@@ -6294,7 +6323,7 @@ case wxGraphicsContext_DrawRectangle: { // wxGraphicsContext::DrawRectangle
wxDouble * w = (wxDouble *) bp; bp += 8;
wxDouble * h = (wxDouble *) bp; bp += 8;
if(!This) throw wxe_badarg(0);
- This->DrawRectangle((wxDouble) *x,(wxDouble) *y,(wxDouble) *w,(wxDouble) *h);
+ This->DrawRectangle(*x,*y,*w,*h);
break;
}
case wxGraphicsContext_DrawRoundedRectangle: { // wxGraphicsContext::DrawRoundedRectangle
@@ -6306,7 +6335,7 @@ case wxGraphicsContext_DrawRoundedRectangle: { // wxGraphicsContext::DrawRounded
wxDouble * h = (wxDouble *) bp; bp += 8;
wxDouble * radius = (wxDouble *) bp; bp += 8;
if(!This) throw wxe_badarg(0);
- This->DrawRoundedRectangle((wxDouble) *x,(wxDouble) *y,(wxDouble) *w,(wxDouble) *h,(wxDouble) *radius);
+ This->DrawRoundedRectangle(*x,*y,*w,*h,*radius);
break;
}
case wxGraphicsContext_DrawText_3: { // wxGraphicsContext::DrawText
@@ -6317,7 +6346,7 @@ case wxGraphicsContext_DrawText_3: { // wxGraphicsContext::DrawText
wxDouble * x = (wxDouble *) bp; bp += 8;
wxDouble * y = (wxDouble *) bp; bp += 8;
if(!This) throw wxe_badarg(0);
- This->DrawText(str,(wxDouble) *x,(wxDouble) *y);
+ This->DrawText(str,*x,*y);
break;
}
case wxGraphicsContext_DrawText_4_0: { // wxGraphicsContext::DrawText
@@ -6329,7 +6358,7 @@ case wxGraphicsContext_DrawText_4_0: { // wxGraphicsContext::DrawText
wxDouble * y = (wxDouble *) bp; bp += 8;
wxDouble * angle = (wxDouble *) bp; bp += 8;
if(!This) throw wxe_badarg(0);
- This->DrawText(str,(wxDouble) *x,(wxDouble) *y,(wxDouble) *angle);
+ This->DrawText(str,*x,*y,*angle);
break;
}
case wxGraphicsContext_DrawText_4_1: { // wxGraphicsContext::DrawText
@@ -6341,7 +6370,7 @@ case wxGraphicsContext_DrawText_4_1: { // wxGraphicsContext::DrawText
wxDouble * y = (wxDouble *) bp; bp += 8;
wxGraphicsBrush *backgroundBrush = (wxGraphicsBrush *) getPtr(bp,memenv); bp += 4;
if(!This) throw wxe_badarg(0);
- This->DrawText(str,(wxDouble) *x,(wxDouble) *y,*backgroundBrush);
+ This->DrawText(str,*x,*y,*backgroundBrush);
break;
}
case wxGraphicsContext_DrawText_5: { // wxGraphicsContext::DrawText
@@ -6354,16 +6383,16 @@ case wxGraphicsContext_DrawText_5: { // wxGraphicsContext::DrawText
wxDouble * angle = (wxDouble *) bp; bp += 8;
wxGraphicsBrush *backgroundBrush = (wxGraphicsBrush *) getPtr(bp,memenv); bp += 4;
if(!This) throw wxe_badarg(0);
- This->DrawText(str,(wxDouble) *x,(wxDouble) *y,(wxDouble) *angle,*backgroundBrush);
+ This->DrawText(str,*x,*y,*angle,*backgroundBrush);
break;
}
case wxGraphicsContext_FillPath: { // wxGraphicsContext::FillPath
- int fillStyle=wxODDEVEN_RULE;
+ wxPolygonFillMode fillStyle=wxODDEVEN_RULE;
wxGraphicsContext *This = (wxGraphicsContext *) getPtr(bp,memenv); bp += 4;
wxGraphicsPath *path = (wxGraphicsPath *) getPtr(bp,memenv); bp += 4;
while( * (int*) bp) { switch (* (int*) bp) {
case 1: {bp += 4;
- fillStyle = (int)*(int *) bp; bp += 4;
+fillStyle = *(wxPolygonFillMode *) bp; bp += 4;;
} break;
}};
if(!This) throw wxe_badarg(0);
@@ -6411,7 +6440,7 @@ case wxGraphicsContext_Rotate: { // wxGraphicsContext::Rotate
bp += 4; /* Align */
wxDouble * angle = (wxDouble *) bp; bp += 8;
if(!This) throw wxe_badarg(0);
- This->Rotate((wxDouble) *angle);
+ This->Rotate(*angle);
break;
}
case wxGraphicsContext_Scale: { // wxGraphicsContext::Scale
@@ -6420,7 +6449,7 @@ case wxGraphicsContext_Scale: { // wxGraphicsContext::Scale
wxDouble * xScale = (wxDouble *) bp; bp += 8;
wxDouble * yScale = (wxDouble *) bp; bp += 8;
if(!This) throw wxe_badarg(0);
- This->Scale((wxDouble) *xScale,(wxDouble) *yScale);
+ This->Scale(*xScale,*yScale);
break;
}
case wxGraphicsContext_Translate: { // wxGraphicsContext::Translate
@@ -6429,7 +6458,7 @@ case wxGraphicsContext_Translate: { // wxGraphicsContext::Translate
wxDouble * dx = (wxDouble *) bp; bp += 8;
wxDouble * dy = (wxDouble *) bp; bp += 8;
if(!This) throw wxe_badarg(0);
- This->Translate((wxDouble) *dx,(wxDouble) *dy);
+ This->Translate(*dx,*dy);
break;
}
case wxGraphicsContext_GetTransform: { // wxGraphicsContext::GetTransform
@@ -6508,7 +6537,7 @@ case wxGraphicsContext_StrokeLine: { // wxGraphicsContext::StrokeLine
wxDouble * x2 = (wxDouble *) bp; bp += 8;
wxDouble * y2 = (wxDouble *) bp; bp += 8;
if(!This) throw wxe_badarg(0);
- This->StrokeLine((wxDouble) *x1,(wxDouble) *y1,(wxDouble) *x2,(wxDouble) *y2);
+ This->StrokeLine(*x1,*y1,*x2,*y2);
break;
}
case wxGraphicsContext_StrokeLines: { // wxGraphicsContext::StrokeLines
@@ -6579,7 +6608,7 @@ case wxGraphicsMatrix_Rotate: { // wxGraphicsMatrix::Rotate
bp += 4; /* Align */
wxDouble * angle = (wxDouble *) bp; bp += 8;
if(!This) throw wxe_badarg(0);
- This->Rotate((wxDouble) *angle);
+ This->Rotate(*angle);
break;
}
case wxGraphicsMatrix_Scale: { // wxGraphicsMatrix::Scale
@@ -6588,7 +6617,7 @@ case wxGraphicsMatrix_Scale: { // wxGraphicsMatrix::Scale
wxDouble * xScale = (wxDouble *) bp; bp += 8;
wxDouble * yScale = (wxDouble *) bp; bp += 8;
if(!This) throw wxe_badarg(0);
- This->Scale((wxDouble) *xScale,(wxDouble) *yScale);
+ This->Scale(*xScale,*yScale);
break;
}
case wxGraphicsMatrix_Translate: { // wxGraphicsMatrix::Translate
@@ -6597,7 +6626,7 @@ case wxGraphicsMatrix_Translate: { // wxGraphicsMatrix::Translate
wxDouble * dx = (wxDouble *) bp; bp += 8;
wxDouble * dy = (wxDouble *) bp; bp += 8;
if(!This) throw wxe_badarg(0);
- This->Translate((wxDouble) *dx,(wxDouble) *dy);
+ This->Translate(*dx,*dy);
break;
}
case wxGraphicsMatrix_Set: { // wxGraphicsMatrix::Set
@@ -6669,7 +6698,7 @@ case wxGraphicsPath_MoveToPoint_2: { // wxGraphicsPath::MoveToPoint
wxDouble * x = (wxDouble *) bp; bp += 8;
wxDouble * y = (wxDouble *) bp; bp += 8;
if(!This) throw wxe_badarg(0);
- This->MoveToPoint((wxDouble) *x,(wxDouble) *y);
+ This->MoveToPoint(*x,*y);
break;
}
case wxGraphicsPath_MoveToPoint_1: { // wxGraphicsPath::MoveToPoint
@@ -6692,7 +6721,7 @@ case wxGraphicsPath_AddArc_6: { // wxGraphicsPath::AddArc
wxDouble * endAngle = (wxDouble *) bp; bp += 8;
bool * clockwise = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->AddArc((wxDouble) *x,(wxDouble) *y,(wxDouble) *r,(wxDouble) *startAngle,(wxDouble) *endAngle,(bool) *clockwise);
+ This->AddArc(*x,*y,*r,*startAngle,*endAngle,*clockwise);
break;
}
case wxGraphicsPath_AddArc_5: { // wxGraphicsPath::AddArc
@@ -6706,7 +6735,7 @@ case wxGraphicsPath_AddArc_5: { // wxGraphicsPath::AddArc
wxDouble * endAngle = (wxDouble *) bp; bp += 8;
bool * clockwise = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->AddArc(c,(wxDouble) *r,(wxDouble) *startAngle,(wxDouble) *endAngle,(bool) *clockwise);
+ This->AddArc(c,*r,*startAngle,*endAngle,*clockwise);
break;
}
case wxGraphicsPath_AddArcToPoint: { // wxGraphicsPath::AddArcToPoint
@@ -6718,7 +6747,7 @@ case wxGraphicsPath_AddArcToPoint: { // wxGraphicsPath::AddArcToPoint
wxDouble * y2 = (wxDouble *) bp; bp += 8;
wxDouble * r = (wxDouble *) bp; bp += 8;
if(!This) throw wxe_badarg(0);
- This->AddArcToPoint((wxDouble) *x1,(wxDouble) *y1,(wxDouble) *x2,(wxDouble) *y2,(wxDouble) *r);
+ This->AddArcToPoint(*x1,*y1,*x2,*y2,*r);
break;
}
case wxGraphicsPath_AddCircle: { // wxGraphicsPath::AddCircle
@@ -6728,7 +6757,7 @@ case wxGraphicsPath_AddCircle: { // wxGraphicsPath::AddCircle
wxDouble * y = (wxDouble *) bp; bp += 8;
wxDouble * r = (wxDouble *) bp; bp += 8;
if(!This) throw wxe_badarg(0);
- This->AddCircle((wxDouble) *x,(wxDouble) *y,(wxDouble) *r);
+ This->AddCircle(*x,*y,*r);
break;
}
case wxGraphicsPath_AddCurveToPoint_6: { // wxGraphicsPath::AddCurveToPoint
@@ -6741,7 +6770,7 @@ case wxGraphicsPath_AddCurveToPoint_6: { // wxGraphicsPath::AddCurveToPoint
wxDouble * x = (wxDouble *) bp; bp += 8;
wxDouble * y = (wxDouble *) bp; bp += 8;
if(!This) throw wxe_badarg(0);
- This->AddCurveToPoint((wxDouble) *cx1,(wxDouble) *cy1,(wxDouble) *cx2,(wxDouble) *cy2,(wxDouble) *x,(wxDouble) *y);
+ This->AddCurveToPoint(*cx1,*cy1,*cx2,*cy2,*x,*y);
break;
}
case wxGraphicsPath_AddCurveToPoint_3: { // wxGraphicsPath::AddCurveToPoint
@@ -6768,7 +6797,7 @@ case wxGraphicsPath_AddEllipse: { // wxGraphicsPath::AddEllipse
wxDouble * w = (wxDouble *) bp; bp += 8;
wxDouble * h = (wxDouble *) bp; bp += 8;
if(!This) throw wxe_badarg(0);
- This->AddEllipse((wxDouble) *x,(wxDouble) *y,(wxDouble) *w,(wxDouble) *h);
+ This->AddEllipse(*x,*y,*w,*h);
break;
}
case wxGraphicsPath_AddLineToPoint_2: { // wxGraphicsPath::AddLineToPoint
@@ -6777,7 +6806,7 @@ case wxGraphicsPath_AddLineToPoint_2: { // wxGraphicsPath::AddLineToPoint
wxDouble * x = (wxDouble *) bp; bp += 8;
wxDouble * y = (wxDouble *) bp; bp += 8;
if(!This) throw wxe_badarg(0);
- This->AddLineToPoint((wxDouble) *x,(wxDouble) *y);
+ This->AddLineToPoint(*x,*y);
break;
}
case wxGraphicsPath_AddLineToPoint_1: { // wxGraphicsPath::AddLineToPoint
@@ -6805,7 +6834,7 @@ case wxGraphicsPath_AddQuadCurveToPoint: { // wxGraphicsPath::AddQuadCurveToPoin
wxDouble * x = (wxDouble *) bp; bp += 8;
wxDouble * y = (wxDouble *) bp; bp += 8;
if(!This) throw wxe_badarg(0);
- This->AddQuadCurveToPoint((wxDouble) *cx,(wxDouble) *cy,(wxDouble) *x,(wxDouble) *y);
+ This->AddQuadCurveToPoint(*cx,*cy,*x,*y);
break;
}
case wxGraphicsPath_AddRectangle: { // wxGraphicsPath::AddRectangle
@@ -6816,7 +6845,7 @@ case wxGraphicsPath_AddRectangle: { // wxGraphicsPath::AddRectangle
wxDouble * w = (wxDouble *) bp; bp += 8;
wxDouble * h = (wxDouble *) bp; bp += 8;
if(!This) throw wxe_badarg(0);
- This->AddRectangle((wxDouble) *x,(wxDouble) *y,(wxDouble) *w,(wxDouble) *h);
+ This->AddRectangle(*x,*y,*w,*h);
break;
}
case wxGraphicsPath_AddRoundedRectangle: { // wxGraphicsPath::AddRoundedRectangle
@@ -6828,7 +6857,7 @@ case wxGraphicsPath_AddRoundedRectangle: { // wxGraphicsPath::AddRoundedRectangl
wxDouble * h = (wxDouble *) bp; bp += 8;
wxDouble * radius = (wxDouble *) bp; bp += 8;
if(!This) throw wxe_badarg(0);
- This->AddRoundedRectangle((wxDouble) *x,(wxDouble) *y,(wxDouble) *w,(wxDouble) *h,(wxDouble) *radius);
+ This->AddRoundedRectangle(*x,*y,*w,*h,*radius);
break;
}
case wxGraphicsPath_CloseSubpath: { // wxGraphicsPath::CloseSubpath
@@ -6838,23 +6867,23 @@ case wxGraphicsPath_CloseSubpath: { // wxGraphicsPath::CloseSubpath
break;
}
case wxGraphicsPath_Contains_3: { // wxGraphicsPath::Contains
- int fillStyle=wxODDEVEN_RULE;
+ wxPolygonFillMode fillStyle=wxODDEVEN_RULE;
wxGraphicsPath *This = (wxGraphicsPath *) getPtr(bp,memenv); bp += 4;
bp += 4; /* Align */
wxDouble * x = (wxDouble *) bp; bp += 8;
wxDouble * y = (wxDouble *) bp; bp += 8;
while( * (int*) bp) { switch (* (int*) bp) {
case 1: {bp += 4;
- fillStyle = (int)*(int *) bp; bp += 4;
+fillStyle = *(wxPolygonFillMode *) bp; bp += 4;;
} break;
}};
if(!This) throw wxe_badarg(0);
- bool Result = This->Contains((wxDouble) *x,(wxDouble) *y,fillStyle);
+ bool Result = This->Contains(*x,*y,fillStyle);
rt.addBool(Result);
break;
}
case wxGraphicsPath_Contains_2: { // wxGraphicsPath::Contains
- int fillStyle=wxODDEVEN_RULE;
+ wxPolygonFillMode fillStyle=wxODDEVEN_RULE;
wxGraphicsPath *This = (wxGraphicsPath *) getPtr(bp,memenv); bp += 4;
bp += 4; /* Align */
wxDouble * cX = (wxDouble *) bp; bp += 8;
@@ -6862,7 +6891,7 @@ case wxGraphicsPath_Contains_2: { // wxGraphicsPath::Contains
wxPoint2DDouble c = wxPoint2DDouble(*cX,*cY);
while( * (int*) bp) { switch (* (int*) bp) {
case 1: {bp += 4;
- fillStyle = (int)*(int *) bp; bp += 4;
+fillStyle = *(wxPolygonFillMode *) bp; bp += 4;;
} break;
}};
if(!This) throw wxe_badarg(0);
@@ -6930,6 +6959,7 @@ case wxGraphicsRenderer_CreateBrush: { // wxGraphicsRenderer::CreateBrush
rt.addRef(getRef((void *)Result,memenv), "wxGraphicsBrush");
break;
}
+#if !wxCHECK_VERSION(2,9,0)
case wxGraphicsRenderer_CreateLinearGradientBrush: { // wxGraphicsRenderer::CreateLinearGradientBrush
wxGraphicsRenderer *This = (wxGraphicsRenderer *) getPtr(bp,memenv); bp += 4;
bp += 4; /* Align */
@@ -6948,10 +6978,12 @@ case wxGraphicsRenderer_CreateLinearGradientBrush: { // wxGraphicsRenderer::Crea
int * c2A = (int *) bp; bp += 4;
wxColour c2 = wxColour(*c2R,*c2G,*c2B,*c2A);
if(!This) throw wxe_badarg(0);
- wxGraphicsBrush * Result = new wxGraphicsBrush(This->CreateLinearGradientBrush((wxDouble) *x1,(wxDouble) *y1,(wxDouble) *x2,(wxDouble) *y2,c1,c2)); newPtr((void *) Result,3, memenv);;
+ wxGraphicsBrush * Result = new wxGraphicsBrush(This->CreateLinearGradientBrush(*x1,*y1,*x2,*y2,c1,c2)); newPtr((void *) Result,3, memenv);;
rt.addRef(getRef((void *)Result,memenv), "wxGraphicsBrush");
break;
}
+#endif
+#if !wxCHECK_VERSION(2,9,0)
case wxGraphicsRenderer_CreateRadialGradientBrush: { // wxGraphicsRenderer::CreateRadialGradientBrush
wxGraphicsRenderer *This = (wxGraphicsRenderer *) getPtr(bp,memenv); bp += 4;
bp += 4; /* Align */
@@ -6971,10 +7003,11 @@ case wxGraphicsRenderer_CreateRadialGradientBrush: { // wxGraphicsRenderer::Crea
int * cColorA = (int *) bp; bp += 4;
wxColour cColor = wxColour(*cColorR,*cColorG,*cColorB,*cColorA);
if(!This) throw wxe_badarg(0);
- wxGraphicsBrush * Result = new wxGraphicsBrush(This->CreateRadialGradientBrush((wxDouble) *xo,(wxDouble) *yo,(wxDouble) *xc,(wxDouble) *yc,(wxDouble) *radius,oColor,cColor)); newPtr((void *) Result,3, memenv);;
+ wxGraphicsBrush * Result = new wxGraphicsBrush(This->CreateRadialGradientBrush(*xo,*yo,*xc,*yc,*radius,oColor,cColor)); newPtr((void *) Result,3, memenv);;
rt.addRef(getRef((void *)Result,memenv), "wxGraphicsBrush");
break;
}
+#endif
case wxGraphicsRenderer_CreateFont: { // wxGraphicsRenderer::CreateFont
wxColour col= *wxBLACK;
wxGraphicsRenderer *This = (wxGraphicsRenderer *) getPtr(bp,memenv); bp += 4;
@@ -7050,7 +7083,7 @@ case wxGraphicsRenderer_CreatePath: { // wxGraphicsRenderer::CreatePath
#endif // wxUSE_GRAPHICS_CONTEXT
case wxMenuBar_new_1: { // wxMenuBar::wxMenuBar
int * style = (int *) bp; bp += 4;
- wxMenuBar * Result = new EwxMenuBar((long) *style);
+ wxMenuBar * Result = new EwxMenuBar(*style);
newPtr((void *) Result, 0, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxMenuBar");
break;
@@ -7077,7 +7110,7 @@ case wxMenuBar_Check: { // wxMenuBar::Check
int * itemid = (int *) bp; bp += 4;
bool * check = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->Check((int) *itemid,(bool) *check);
+ This->Check(*itemid,*check);
break;
}
case wxMenuBar_Enable_2: { // wxMenuBar::Enable
@@ -7085,7 +7118,7 @@ case wxMenuBar_Enable_2: { // wxMenuBar::Enable
int * itemid = (int *) bp; bp += 4;
bool * enable = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->Enable((int) *itemid,(bool) *enable);
+ This->Enable(*itemid,*enable);
break;
}
case wxMenuBar_Enable_1: { // wxMenuBar::Enable
@@ -7107,7 +7140,7 @@ case wxMenuBar_EnableTop: { // wxMenuBar::EnableTop
int * pos = (int *) bp; bp += 4;
bool * flag = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->EnableTop((size_t) *pos,(bool) *flag);
+ This->EnableTop(*pos,*flag);
break;
}
case wxMenuBar_FindMenu: { // wxMenuBar::FindMenu
@@ -7138,7 +7171,7 @@ case wxMenuBar_FindItem: { // wxMenuBar::FindItem
wxMenuBar *This = (wxMenuBar *) getPtr(bp,memenv); bp += 4;
int * id = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxMenuItem * Result = (wxMenuItem*)This->FindItem((int) *id,menu);
+ wxMenuItem * Result = (wxMenuItem*)This->FindItem(*id,menu);
rt.addRef(getRef((void *)Result,memenv), "wxMenuItem");
break;
}
@@ -7146,7 +7179,7 @@ case wxMenuBar_GetHelpString: { // wxMenuBar::GetHelpString
wxMenuBar *This = (wxMenuBar *) getPtr(bp,memenv); bp += 4;
int * itemid = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxString Result = This->GetHelpString((int) *itemid);
+ wxString Result = This->GetHelpString(*itemid);
rt.add(Result);
break;
}
@@ -7154,7 +7187,7 @@ case wxMenuBar_GetLabel_1: { // wxMenuBar::GetLabel
wxMenuBar *This = (wxMenuBar *) getPtr(bp,memenv); bp += 4;
int * itemid = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxString Result = This->GetLabel((int) *itemid);
+ wxString Result = This->GetLabel(*itemid);
rt.add(Result);
break;
}
@@ -7169,7 +7202,7 @@ case wxMenuBar_GetLabelTop: { // wxMenuBar::GetLabelTop
wxMenuBar *This = (wxMenuBar *) getPtr(bp,memenv); bp += 4;
int * pos = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxString Result = This->GetLabelTop((size_t) *pos);
+ wxString Result = This->GetLabelTop(*pos);
rt.add(Result);
break;
}
@@ -7177,7 +7210,7 @@ case wxMenuBar_GetMenu: { // wxMenuBar::GetMenu
wxMenuBar *This = (wxMenuBar *) getPtr(bp,memenv); bp += 4;
int * pos = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxMenu * Result = (wxMenu*)This->GetMenu((size_t) *pos);
+ wxMenu * Result = (wxMenu*)This->GetMenu(*pos);
rt.addRef(getRef((void *)Result,memenv), "wxMenu");
break;
}
@@ -7196,7 +7229,7 @@ case wxMenuBar_Insert: { // wxMenuBar::Insert
wxString title = wxString(bp, wxConvUTF8);
bp += *titleLen+((8-((0+ *titleLen) & 7)) & 7);
if(!This) throw wxe_badarg(0);
- bool Result = This->Insert((size_t) *pos,menu,title);
+ bool Result = This->Insert(*pos,menu,title);
rt.addBool(Result);
break;
}
@@ -7204,7 +7237,7 @@ case wxMenuBar_IsChecked: { // wxMenuBar::IsChecked
wxMenuBar *This = (wxMenuBar *) getPtr(bp,memenv); bp += 4;
int * itemid = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->IsChecked((int) *itemid);
+ bool Result = This->IsChecked(*itemid);
rt.addBool(Result);
break;
}
@@ -7212,7 +7245,7 @@ case wxMenuBar_IsEnabled_1: { // wxMenuBar::IsEnabled
wxMenuBar *This = (wxMenuBar *) getPtr(bp,memenv); bp += 4;
int * itemid = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->IsEnabled((int) *itemid);
+ bool Result = This->IsEnabled(*itemid);
rt.addBool(Result);
break;
}
@@ -7227,7 +7260,7 @@ case wxMenuBar_Remove: { // wxMenuBar::Remove
wxMenuBar *This = (wxMenuBar *) getPtr(bp,memenv); bp += 4;
int * pos = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxMenu * Result = (wxMenu*)This->Remove((size_t) *pos);
+ wxMenu * Result = (wxMenu*)This->Remove(*pos);
rt.addRef(getRef((void *)Result,memenv), "wxMenu");
break;
}
@@ -7239,7 +7272,7 @@ case wxMenuBar_Replace: { // wxMenuBar::Replace
wxString title = wxString(bp, wxConvUTF8);
bp += *titleLen+((8-((0+ *titleLen) & 7)) & 7);
if(!This) throw wxe_badarg(0);
- wxMenu * Result = (wxMenu*)This->Replace((size_t) *pos,menu,title);
+ wxMenu * Result = (wxMenu*)This->Replace(*pos,menu,title);
rt.addRef(getRef((void *)Result,memenv), "wxMenu");
break;
}
@@ -7250,7 +7283,7 @@ case wxMenuBar_SetHelpString: { // wxMenuBar::SetHelpString
wxString helpString = wxString(bp, wxConvUTF8);
bp += *helpStringLen+((8-((4+ *helpStringLen) & 7)) & 7);
if(!This) throw wxe_badarg(0);
- This->SetHelpString((int) *itemid,helpString);
+ This->SetHelpString(*itemid,helpString);
break;
}
case wxMenuBar_SetLabel_2: { // wxMenuBar::SetLabel
@@ -7260,7 +7293,7 @@ case wxMenuBar_SetLabel_2: { // wxMenuBar::SetLabel
wxString label = wxString(bp, wxConvUTF8);
bp += *labelLen+((8-((4+ *labelLen) & 7)) & 7);
if(!This) throw wxe_badarg(0);
- This->SetLabel((int) *itemid,label);
+ This->SetLabel(*itemid,label);
break;
}
case wxMenuBar_SetLabel_1: { // wxMenuBar::SetLabel
@@ -7279,7 +7312,7 @@ case wxMenuBar_SetLabelTop: { // wxMenuBar::SetLabelTop
wxString label = wxString(bp, wxConvUTF8);
bp += *labelLen+((8-((4+ *labelLen) & 7)) & 7);
if(!This) throw wxe_badarg(0);
- This->SetLabelTop((size_t) *pos,label);
+ This->SetLabelTop(*pos,label);
break;
}
case wxControl_GetLabel: { // wxControl::GetLabel
@@ -7345,7 +7378,7 @@ case wxControlWithItems_Delete: { // wxControlWithItems::Delete
wxControlWithItems *This = (wxControlWithItems *) getPtr(bp,memenv); bp += 4;
unsigned int * n = (unsigned int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->Delete((int) *n);
+ This->Delete(*n);
break;
}
case wxControlWithItems_FindString: { // wxControlWithItems::FindString
@@ -7368,7 +7401,7 @@ case wxControlWithItems_getClientData: { // wxControlWithItems::GetClientObject
wxControlWithItems *This = (wxControlWithItems *) getPtr(bp,memenv); bp += 4;
unsigned int * n = (unsigned int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxeErlTerm * Result = (wxeErlTerm*)This->GetClientObject((int) *n);
+ wxeErlTerm * Result = (wxeErlTerm*)This->GetClientObject(*n);
rt.addExt2Term(Result);
break;
}
@@ -7377,7 +7410,7 @@ case wxControlWithItems_setClientData: { // wxControlWithItems::SetClientObject
unsigned int * n = (unsigned int *) bp; bp += 4;
wxeErlTerm * clientData = new wxeErlTerm(Ecmd.bin[0]);
if(!This) throw wxe_badarg(0);
- This->SetClientObject((int) *n,clientData);
+ This->SetClientObject(*n,clientData);
break;
}
case wxControlWithItems_GetCount: { // wxControlWithItems::GetCount
@@ -7398,7 +7431,7 @@ case wxControlWithItems_GetString: { // wxControlWithItems::GetString
wxControlWithItems *This = (wxControlWithItems *) getPtr(bp,memenv); bp += 4;
unsigned int * n = (unsigned int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxString Result = This->GetString((int) *n);
+ wxString Result = This->GetString(*n);
rt.add(Result);
break;
}
@@ -7416,7 +7449,7 @@ case wxControlWithItems_Insert_2: { // wxControlWithItems::Insert
bp += *itemLen+((8-((0+ *itemLen) & 7)) & 7);
unsigned int * pos = (unsigned int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->Insert(item,(int) *pos);
+ int Result = This->Insert(item,*pos);
rt.addInt(Result);
break;
}
@@ -7428,7 +7461,7 @@ case wxControlWithItems_Insert_3: { // wxControlWithItems::Insert
unsigned int * pos = (unsigned int *) bp; bp += 4;
wxeErlTerm * clientData = new wxeErlTerm(Ecmd.bin[0]);
if(!This) throw wxe_badarg(0);
- int Result = This->Insert(item,(int) *pos,clientData);
+ int Result = This->Insert(item,*pos,clientData);
rt.addInt(Result);
break;
}
@@ -7443,14 +7476,14 @@ case wxControlWithItems_Select: { // wxControlWithItems::Select
wxControlWithItems *This = (wxControlWithItems *) getPtr(bp,memenv); bp += 4;
int * n = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->Select((int) *n);
+ This->Select(*n);
break;
}
case wxControlWithItems_SetSelection: { // wxControlWithItems::SetSelection
wxControlWithItems *This = (wxControlWithItems *) getPtr(bp,memenv); bp += 4;
int * n = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetSelection((int) *n);
+ This->SetSelection(*n);
break;
}
case wxControlWithItems_SetString: { // wxControlWithItems::SetString
@@ -7460,7 +7493,7 @@ case wxControlWithItems_SetString: { // wxControlWithItems::SetString
wxString s = wxString(bp, wxConvUTF8);
bp += *sLen+((8-((4+ *sLen) & 7)) & 7);
if(!This) throw wxe_badarg(0);
- This->SetString((int) *n,s);
+ This->SetString(*n,s);
break;
}
case wxControlWithItems_SetStringSelection: { // wxControlWithItems::SetStringSelection
@@ -7519,7 +7552,7 @@ kind = *(wxItemKind *) bp; bp += 4;;
} break;
}};
if(!This) throw wxe_badarg(0);
- wxMenuItem * Result = (wxMenuItem*)This->Append((int) *itemid,text,help,(wxItemKind) kind);
+ wxMenuItem * Result = (wxMenuItem*)This->Append(*itemid,text,help,kind);
rt.addRef(getRef((void *)Result,memenv), "wxMenuItem");
break;
}
@@ -7542,7 +7575,7 @@ case wxMenu_Append_4_0: { // wxMenu::Append
bp += *helpLen+((8-((4+ *helpLen) & 7)) & 7);
bool * isCheckable = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->Append((int) *itemid,text,help,(bool) *isCheckable);
+ This->Append(*itemid,text,help,*isCheckable);
break;
}
case wxMenu_Append_4_1: { // wxMenu::Append
@@ -7562,7 +7595,7 @@ case wxMenu_Append_4_1: { // wxMenu::Append
} break;
}};
if(!This) throw wxe_badarg(0);
- wxMenuItem * Result = (wxMenuItem*)This->Append((int) *itemid,text,submenu,help);
+ wxMenuItem * Result = (wxMenuItem*)This->Append(*itemid,text,submenu,help);
rt.addRef(getRef((void *)Result,memenv), "wxMenuItem");
break;
}
@@ -7581,7 +7614,7 @@ case wxMenu_AppendCheckItem: { // wxMenu::AppendCheckItem
} break;
}};
if(!This) throw wxe_badarg(0);
- wxMenuItem * Result = (wxMenuItem*)This->AppendCheckItem((int) *itemid,text,help);
+ wxMenuItem * Result = (wxMenuItem*)This->AppendCheckItem(*itemid,text,help);
rt.addRef(getRef((void *)Result,memenv), "wxMenuItem");
break;
}
@@ -7600,7 +7633,7 @@ case wxMenu_AppendRadioItem: { // wxMenu::AppendRadioItem
} break;
}};
if(!This) throw wxe_badarg(0);
- wxMenuItem * Result = (wxMenuItem*)This->AppendRadioItem((int) *itemid,text,help);
+ wxMenuItem * Result = (wxMenuItem*)This->AppendRadioItem(*itemid,text,help);
rt.addRef(getRef((void *)Result,memenv), "wxMenuItem");
break;
}
@@ -7622,14 +7655,14 @@ case wxMenu_Check: { // wxMenu::Check
int * itemid = (int *) bp; bp += 4;
bool * check = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->Check((int) *itemid,(bool) *check);
+ This->Check(*itemid,*check);
break;
}
case wxMenu_Delete_1_0: { // wxMenu::Delete
wxMenu *This = (wxMenu *) getPtr(bp,memenv); bp += 4;
int * itemid = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->Delete((int) *itemid);
+ bool Result = This->Delete(*itemid);
rt.addBool(Result);
break;
}
@@ -7645,7 +7678,7 @@ case wxMenu_Destroy_1_0: { // wxMenu::Destroy
wxMenu *This = (wxMenu *) getPtr(bp,memenv); bp += 4;
int * itemid = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->Destroy((int) *itemid);
+ bool Result = This->Destroy(*itemid);
rt.addBool(Result);
break;
}
@@ -7662,7 +7695,7 @@ case wxMenu_Enable: { // wxMenu::Enable
int * itemid = (int *) bp; bp += 4;
bool * enable = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->Enable((int) *itemid,(bool) *enable);
+ This->Enable(*itemid,*enable);
break;
}
case wxMenu_FindItem_1: { // wxMenu::FindItem
@@ -7680,7 +7713,7 @@ case wxMenu_FindItem_2: { // wxMenu::FindItem
wxMenu *This = (wxMenu *) getPtr(bp,memenv); bp += 4;
int * itemid = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxMenuItem * Result = (wxMenuItem*)This->FindItem((int) *itemid,menu);
+ wxMenuItem * Result = (wxMenuItem*)This->FindItem(*itemid,menu);
rt.addRef(getRef((void *)Result,memenv), "wxMenuItem");
break;
}
@@ -7688,7 +7721,7 @@ case wxMenu_FindItemByPosition: { // wxMenu::FindItemByPosition
wxMenu *This = (wxMenu *) getPtr(bp,memenv); bp += 4;
int * position = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxMenuItem * Result = (wxMenuItem*)This->FindItemByPosition((size_t) *position);
+ wxMenuItem * Result = (wxMenuItem*)This->FindItemByPosition(*position);
rt.addRef(getRef((void *)Result,memenv), "wxMenuItem");
break;
}
@@ -7696,7 +7729,7 @@ case wxMenu_GetHelpString: { // wxMenu::GetHelpString
wxMenu *This = (wxMenu *) getPtr(bp,memenv); bp += 4;
int * itemid = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxString Result = This->GetHelpString((int) *itemid);
+ wxString Result = This->GetHelpString(*itemid);
rt.add(Result);
break;
}
@@ -7704,7 +7737,7 @@ case wxMenu_GetLabel: { // wxMenu::GetLabel
wxMenu *This = (wxMenu *) getPtr(bp,memenv); bp += 4;
int * itemid = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxString Result = This->GetLabel((int) *itemid);
+ wxString Result = This->GetLabel(*itemid);
rt.add(Result);
break;
}
@@ -7738,7 +7771,7 @@ case wxMenu_Insert_2: { // wxMenu::Insert
int * pos = (int *) bp; bp += 4;
wxMenuItem *item = (wxMenuItem *) getPtr(bp,memenv); bp += 4;
if(!This) throw wxe_badarg(0);
- wxMenuItem * Result = (wxMenuItem*)This->Insert((size_t) *pos,item);
+ wxMenuItem * Result = (wxMenuItem*)This->Insert(*pos,item);
rt.addRef(getRef((void *)Result,memenv), "wxMenuItem");
break;
}
@@ -7766,7 +7799,7 @@ kind = *(wxItemKind *) bp; bp += 4;;
} break;
}};
if(!This) throw wxe_badarg(0);
- wxMenuItem * Result = (wxMenuItem*)This->Insert((size_t) *pos,(int) *itemid,text,help,(wxItemKind) kind);
+ wxMenuItem * Result = (wxMenuItem*)This->Insert(*pos,*itemid,text,help,kind);
rt.addRef(getRef((void *)Result,memenv), "wxMenuItem");
break;
}
@@ -7788,7 +7821,7 @@ case wxMenu_Insert_5_1: { // wxMenu::Insert
} break;
}};
if(!This) throw wxe_badarg(0);
- wxMenuItem * Result = (wxMenuItem*)This->Insert((size_t) *pos,(int) *itemid,text,submenu,help);
+ wxMenuItem * Result = (wxMenuItem*)This->Insert(*pos,*itemid,text,submenu,help);
rt.addRef(getRef((void *)Result,memenv), "wxMenuItem");
break;
}
@@ -7804,7 +7837,7 @@ case wxMenu_Insert_5_0: { // wxMenu::Insert
bp += *helpLen+((8-((4+ *helpLen) & 7)) & 7);
bool * isCheckable = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->Insert((size_t) *pos,(int) *itemid,text,help,(bool) *isCheckable);
+ This->Insert(*pos,*itemid,text,help,*isCheckable);
break;
}
case wxMenu_InsertCheckItem: { // wxMenu::InsertCheckItem
@@ -7823,7 +7856,7 @@ case wxMenu_InsertCheckItem: { // wxMenu::InsertCheckItem
} break;
}};
if(!This) throw wxe_badarg(0);
- wxMenuItem * Result = (wxMenuItem*)This->InsertCheckItem((size_t) *pos,(int) *itemid,text,help);
+ wxMenuItem * Result = (wxMenuItem*)This->InsertCheckItem(*pos,*itemid,text,help);
rt.addRef(getRef((void *)Result,memenv), "wxMenuItem");
break;
}
@@ -7843,7 +7876,7 @@ case wxMenu_InsertRadioItem: { // wxMenu::InsertRadioItem
} break;
}};
if(!This) throw wxe_badarg(0);
- wxMenuItem * Result = (wxMenuItem*)This->InsertRadioItem((size_t) *pos,(int) *itemid,text,help);
+ wxMenuItem * Result = (wxMenuItem*)This->InsertRadioItem(*pos,*itemid,text,help);
rt.addRef(getRef((void *)Result,memenv), "wxMenuItem");
break;
}
@@ -7851,7 +7884,7 @@ case wxMenu_InsertSeparator: { // wxMenu::InsertSeparator
wxMenu *This = (wxMenu *) getPtr(bp,memenv); bp += 4;
int * pos = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxMenuItem * Result = (wxMenuItem*)This->InsertSeparator((size_t) *pos);
+ wxMenuItem * Result = (wxMenuItem*)This->InsertSeparator(*pos);
rt.addRef(getRef((void *)Result,memenv), "wxMenuItem");
break;
}
@@ -7859,7 +7892,7 @@ case wxMenu_IsChecked: { // wxMenu::IsChecked
wxMenu *This = (wxMenu *) getPtr(bp,memenv); bp += 4;
int * itemid = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->IsChecked((int) *itemid);
+ bool Result = This->IsChecked(*itemid);
rt.addBool(Result);
break;
}
@@ -7867,7 +7900,7 @@ case wxMenu_IsEnabled: { // wxMenu::IsEnabled
wxMenu *This = (wxMenu *) getPtr(bp,memenv); bp += 4;
int * itemid = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->IsEnabled((int) *itemid);
+ bool Result = This->IsEnabled(*itemid);
rt.addBool(Result);
break;
}
@@ -7901,7 +7934,7 @@ kind = *(wxItemKind *) bp; bp += 4;;
} break;
}};
if(!This) throw wxe_badarg(0);
- wxMenuItem * Result = (wxMenuItem*)This->Prepend((int) *itemid,text,help,(wxItemKind) kind);
+ wxMenuItem * Result = (wxMenuItem*)This->Prepend(*itemid,text,help,kind);
rt.addRef(getRef((void *)Result,memenv), "wxMenuItem");
break;
}
@@ -7922,7 +7955,7 @@ case wxMenu_Prepend_4_1: { // wxMenu::Prepend
} break;
}};
if(!This) throw wxe_badarg(0);
- wxMenuItem * Result = (wxMenuItem*)This->Prepend((int) *itemid,text,submenu,help);
+ wxMenuItem * Result = (wxMenuItem*)This->Prepend(*itemid,text,submenu,help);
rt.addRef(getRef((void *)Result,memenv), "wxMenuItem");
break;
}
@@ -7937,7 +7970,7 @@ case wxMenu_Prepend_4_0: { // wxMenu::Prepend
bp += *helpLen+((8-((4+ *helpLen) & 7)) & 7);
bool * isCheckable = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->Prepend((int) *itemid,text,help,(bool) *isCheckable);
+ This->Prepend(*itemid,text,help,*isCheckable);
break;
}
case wxMenu_PrependCheckItem: { // wxMenu::PrependCheckItem
@@ -7955,7 +7988,7 @@ case wxMenu_PrependCheckItem: { // wxMenu::PrependCheckItem
} break;
}};
if(!This) throw wxe_badarg(0);
- wxMenuItem * Result = (wxMenuItem*)This->PrependCheckItem((int) *itemid,text,help);
+ wxMenuItem * Result = (wxMenuItem*)This->PrependCheckItem(*itemid,text,help);
rt.addRef(getRef((void *)Result,memenv), "wxMenuItem");
break;
}
@@ -7974,7 +8007,7 @@ case wxMenu_PrependRadioItem: { // wxMenu::PrependRadioItem
} break;
}};
if(!This) throw wxe_badarg(0);
- wxMenuItem * Result = (wxMenuItem*)This->PrependRadioItem((int) *itemid,text,help);
+ wxMenuItem * Result = (wxMenuItem*)This->PrependRadioItem(*itemid,text,help);
rt.addRef(getRef((void *)Result,memenv), "wxMenuItem");
break;
}
@@ -7989,7 +8022,7 @@ case wxMenu_Remove_1_0: { // wxMenu::Remove
wxMenu *This = (wxMenu *) getPtr(bp,memenv); bp += 4;
int * itemid = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxMenuItem * Result = (wxMenuItem*)This->Remove((int) *itemid);
+ wxMenuItem * Result = (wxMenuItem*)This->Remove(*itemid);
rt.addRef(getRef((void *)Result,memenv), "wxMenuItem");
break;
}
@@ -8008,7 +8041,7 @@ case wxMenu_SetHelpString: { // wxMenu::SetHelpString
wxString helpString = wxString(bp, wxConvUTF8);
bp += *helpStringLen+((8-((4+ *helpStringLen) & 7)) & 7);
if(!This) throw wxe_badarg(0);
- This->SetHelpString((int) *itemid,helpString);
+ This->SetHelpString(*itemid,helpString);
break;
}
case wxMenu_SetLabel: { // wxMenu::SetLabel
@@ -8018,7 +8051,7 @@ case wxMenu_SetLabel: { // wxMenu::SetLabel
wxString label = wxString(bp, wxConvUTF8);
bp += *labelLen+((8-((4+ *labelLen) & 7)) & 7);
if(!This) throw wxe_badarg(0);
- This->SetLabel((int) *itemid,label);
+ This->SetLabel(*itemid,label);
break;
}
case wxMenu_SetTitle: { // wxMenu::SetTitle
@@ -8061,7 +8094,7 @@ kind = *(wxItemKind *) bp; bp += 4;;
subMenu = (wxMenu *) getPtr(bp,memenv); bp += 4;
} break;
}};
- wxMenuItem * Result = new EwxMenuItem(parentMenu,id,text,help,(wxItemKind) kind,subMenu);
+ wxMenuItem * Result = new EwxMenuItem(parentMenu,id,text,help,kind,subMenu);
newPtr((void *) Result, 1, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxMenuItem");
break;
@@ -8276,7 +8309,7 @@ data = (wxObject *) getPtr(bp,memenv); bp += 4;
} break;
}};
if(!This) throw wxe_badarg(0);
- wxToolBarToolBase * Result = (wxToolBarToolBase*)This->AddTool((int) *toolid,label,*bitmap,*bmpDisabled,(wxItemKind) kind,shortHelp,longHelp,data);
+ wxToolBarToolBase * Result = (wxToolBarToolBase*)This->AddTool(*toolid,label,*bitmap,*bmpDisabled,kind,shortHelp,longHelp,data);
rt.addRef(getRef((void *)Result,memenv), "wx");
break;
}
@@ -8301,7 +8334,7 @@ kind = *(wxItemKind *) bp; bp += 4;;
} break;
}};
if(!This) throw wxe_badarg(0);
- wxToolBarToolBase * Result = (wxToolBarToolBase*)This->AddTool((int) *toolid,label,*bitmap,shortHelp,(wxItemKind) kind);
+ wxToolBarToolBase * Result = (wxToolBarToolBase*)This->AddTool(*toolid,label,*bitmap,shortHelp,kind);
rt.addRef(getRef((void *)Result,memenv), "wx");
break;
}
@@ -8341,7 +8374,7 @@ clientData = (wxObject *) getPtr(bp,memenv); bp += 4;
} break;
}};
if(!This) throw wxe_badarg(0);
- wxToolBarToolBase * Result = (wxToolBarToolBase*)This->AddTool((int) *toolid,*bitmap,*bmpDisabled,toggle,clientData,shortHelpString,longHelpString);
+ wxToolBarToolBase * Result = (wxToolBarToolBase*)This->AddTool(*toolid,*bitmap,*bmpDisabled,toggle,clientData,shortHelpString,longHelpString);
rt.addRef(getRef((void *)Result,memenv), "wx");
break;
}
@@ -8365,7 +8398,7 @@ case wxToolBar_AddTool_3: { // wxToolBar::AddTool
} break;
}};
if(!This) throw wxe_badarg(0);
- wxToolBarToolBase * Result = (wxToolBarToolBase*)This->AddTool((int) *toolid,*bitmap,shortHelpString,longHelpString);
+ wxToolBarToolBase * Result = (wxToolBarToolBase*)This->AddTool(*toolid,*bitmap,shortHelpString,longHelpString);
rt.addRef(getRef((void *)Result,memenv), "wx");
break;
}
@@ -8399,7 +8432,7 @@ clientData = (wxObject *) getPtr(bp,memenv); bp += 4;
} break;
}};
if(!This) throw wxe_badarg(0);
- wxToolBarToolBase * Result = (wxToolBarToolBase*)This->AddTool((int) *toolid,*bitmap,*bmpDisabled,(bool) *toggle,(wxCoord) *xPos,yPos,clientData,shortHelp,longHelp);
+ wxToolBarToolBase * Result = (wxToolBarToolBase*)This->AddTool(*toolid,*bitmap,*bmpDisabled,*toggle,*xPos,yPos,clientData,shortHelp,longHelp);
rt.addRef(getRef((void *)Result,memenv), "wx");
break;
}
@@ -8434,7 +8467,7 @@ data = (wxObject *) getPtr(bp,memenv); bp += 4;
} break;
}};
if(!This) throw wxe_badarg(0);
- wxToolBarToolBase * Result = (wxToolBarToolBase*)This->AddCheckTool((int) *toolid,label,*bitmap,*bmpDisabled,shortHelp,longHelp,data);
+ wxToolBarToolBase * Result = (wxToolBarToolBase*)This->AddCheckTool(*toolid,label,*bitmap,*bmpDisabled,shortHelp,longHelp,data);
rt.addRef(getRef((void *)Result,memenv), "wx");
break;
}
@@ -8469,7 +8502,7 @@ data = (wxObject *) getPtr(bp,memenv); bp += 4;
} break;
}};
if(!This) throw wxe_badarg(0);
- wxToolBarToolBase * Result = (wxToolBarToolBase*)This->AddRadioTool((int) *toolid,label,*bitmap,*bmpDisabled,shortHelp,longHelp,data);
+ wxToolBarToolBase * Result = (wxToolBarToolBase*)This->AddRadioTool(*toolid,label,*bitmap,*bmpDisabled,shortHelp,longHelp,data);
rt.addRef(getRef((void *)Result,memenv), "wx");
break;
}
@@ -8477,7 +8510,7 @@ case wxToolBar_DeleteTool: { // wxToolBar::DeleteTool
wxToolBar *This = (wxToolBar *) getPtr(bp,memenv); bp += 4;
int * toolid = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->DeleteTool((int) *toolid);
+ bool Result = This->DeleteTool(*toolid);
rt.addBool(Result);
break;
}
@@ -8485,7 +8518,7 @@ case wxToolBar_DeleteToolByPos: { // wxToolBar::DeleteToolByPos
wxToolBar *This = (wxToolBar *) getPtr(bp,memenv); bp += 4;
int * pos = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->DeleteToolByPos((size_t) *pos);
+ bool Result = This->DeleteToolByPos(*pos);
rt.addBool(Result);
break;
}
@@ -8494,14 +8527,14 @@ case wxToolBar_EnableTool: { // wxToolBar::EnableTool
int * toolid = (int *) bp; bp += 4;
bool * enable = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->EnableTool((int) *toolid,(bool) *enable);
+ This->EnableTool(*toolid,*enable);
break;
}
case wxToolBar_FindById: { // wxToolBar::FindById
wxToolBar *This = (wxToolBar *) getPtr(bp,memenv); bp += 4;
int * toolid = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxToolBarToolBase * Result = (wxToolBarToolBase*)This->FindById((int) *toolid);
+ wxToolBarToolBase * Result = (wxToolBarToolBase*)This->FindById(*toolid);
rt.addRef(getRef((void *)Result,memenv), "wx");
break;
}
@@ -8509,7 +8542,7 @@ case wxToolBar_FindControl: { // wxToolBar::FindControl
wxToolBar *This = (wxToolBar *) getPtr(bp,memenv); bp += 4;
int * toolid = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxControl * Result = (wxControl*)This->FindControl((int) *toolid);
+ wxControl * Result = (wxControl*)This->FindControl(*toolid);
rt.addRef(getRef((void *)Result,memenv), "wxControl");
break;
}
@@ -8518,7 +8551,7 @@ case wxToolBar_FindToolForPosition: { // wxToolBar::FindToolForPosition
int * x = (int *) bp; bp += 4;
int * y = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxToolBarToolBase * Result = (wxToolBarToolBase*)This->FindToolForPosition((wxCoord) *x,(wxCoord) *y);
+ wxToolBarToolBase * Result = (wxToolBarToolBase*)This->FindToolForPosition(*x,*y);
rt.addRef(getRef((void *)Result,memenv), "wx");
break;
}
@@ -8547,7 +8580,7 @@ case wxToolBar_GetToolEnabled: { // wxToolBar::GetToolEnabled
wxToolBar *This = (wxToolBar *) getPtr(bp,memenv); bp += 4;
int * toolid = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->GetToolEnabled((int) *toolid);
+ bool Result = This->GetToolEnabled(*toolid);
rt.addBool(Result);
break;
}
@@ -8555,7 +8588,7 @@ case wxToolBar_GetToolLongHelp: { // wxToolBar::GetToolLongHelp
wxToolBar *This = (wxToolBar *) getPtr(bp,memenv); bp += 4;
int * toolid = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxString Result = This->GetToolLongHelp((int) *toolid);
+ wxString Result = This->GetToolLongHelp(*toolid);
rt.add(Result);
break;
}
@@ -8570,7 +8603,7 @@ case wxToolBar_GetToolPos: { // wxToolBar::GetToolPos
wxToolBar *This = (wxToolBar *) getPtr(bp,memenv); bp += 4;
int * id = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->GetToolPos((int) *id);
+ int Result = This->GetToolPos(*id);
rt.addInt(Result);
break;
}
@@ -8585,7 +8618,7 @@ case wxToolBar_GetToolShortHelp: { // wxToolBar::GetToolShortHelp
wxToolBar *This = (wxToolBar *) getPtr(bp,memenv); bp += 4;
int * toolid = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxString Result = This->GetToolShortHelp((int) *toolid);
+ wxString Result = This->GetToolShortHelp(*toolid);
rt.add(Result);
break;
}
@@ -8593,7 +8626,7 @@ case wxToolBar_GetToolState: { // wxToolBar::GetToolState
wxToolBar *This = (wxToolBar *) getPtr(bp,memenv); bp += 4;
int * toolid = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->GetToolState((int) *toolid);
+ bool Result = This->GetToolState(*toolid);
rt.addBool(Result);
break;
}
@@ -8602,7 +8635,7 @@ case wxToolBar_InsertControl: { // wxToolBar::InsertControl
int * pos = (int *) bp; bp += 4;
wxControl *control = (wxControl *) getPtr(bp,memenv); bp += 4;
if(!This) throw wxe_badarg(0);
- wxToolBarToolBase * Result = (wxToolBarToolBase*)This->InsertControl((size_t) *pos,control);
+ wxToolBarToolBase * Result = (wxToolBarToolBase*)This->InsertControl(*pos,control);
rt.addRef(getRef((void *)Result,memenv), "wx");
break;
}
@@ -8610,7 +8643,7 @@ case wxToolBar_InsertSeparator: { // wxToolBar::InsertSeparator
wxToolBar *This = (wxToolBar *) getPtr(bp,memenv); bp += 4;
int * pos = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxToolBarToolBase * Result = (wxToolBarToolBase*)This->InsertSeparator((size_t) *pos);
+ wxToolBarToolBase * Result = (wxToolBarToolBase*)This->InsertSeparator(*pos);
rt.addRef(getRef((void *)Result,memenv), "wx");
break;
}
@@ -8650,7 +8683,7 @@ clientData = (wxObject *) getPtr(bp,memenv); bp += 4;
} break;
}};
if(!This) throw wxe_badarg(0);
- wxToolBarToolBase * Result = (wxToolBarToolBase*)This->InsertTool((size_t) *pos,(int) *toolid,label,*bitmap,*bmpDisabled,(wxItemKind) kind,shortHelp,longHelp,clientData);
+ wxToolBarToolBase * Result = (wxToolBarToolBase*)This->InsertTool(*pos,*toolid,label,*bitmap,*bmpDisabled,kind,shortHelp,longHelp,clientData);
rt.addRef(getRef((void *)Result,memenv), "wx");
break;
}
@@ -8659,7 +8692,7 @@ case wxToolBar_InsertTool_2: { // wxToolBar::InsertTool
int * pos = (int *) bp; bp += 4;
wxToolBarToolBase *tool = (wxToolBarToolBase *) getPtr(bp,memenv); bp += 4;
if(!This) throw wxe_badarg(0);
- wxToolBarToolBase * Result = (wxToolBarToolBase*)This->InsertTool((size_t) *pos,tool);
+ wxToolBarToolBase * Result = (wxToolBarToolBase*)This->InsertTool(*pos,tool);
rt.addRef(getRef((void *)Result,memenv), "wx");
break;
}
@@ -8695,7 +8728,7 @@ clientData = (wxObject *) getPtr(bp,memenv); bp += 4;
} break;
}};
if(!This) throw wxe_badarg(0);
- wxToolBarToolBase * Result = (wxToolBarToolBase*)This->InsertTool((size_t) *pos,(int) *toolid,*bitmap,*bmpDisabled,toggle,clientData,shortHelp,longHelp);
+ wxToolBarToolBase * Result = (wxToolBarToolBase*)This->InsertTool(*pos,*toolid,*bitmap,*bmpDisabled,toggle,clientData,shortHelp,longHelp);
rt.addRef(getRef((void *)Result,memenv), "wx");
break;
}
@@ -8710,7 +8743,7 @@ case wxToolBar_RemoveTool: { // wxToolBar::RemoveTool
wxToolBar *This = (wxToolBar *) getPtr(bp,memenv); bp += 4;
int * toolid = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxToolBarToolBase * Result = (wxToolBarToolBase*)This->RemoveTool((int) *toolid);
+ wxToolBarToolBase * Result = (wxToolBarToolBase*)This->RemoveTool(*toolid);
rt.addRef(getRef((void *)Result,memenv), "wx");
break;
}
@@ -8719,7 +8752,7 @@ case wxToolBar_SetMargins: { // wxToolBar::SetMargins
int * x = (int *) bp; bp += 4;
int * y = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetMargins((int) *x,(int) *y);
+ This->SetMargins(*x,*y);
break;
}
case wxToolBar_SetToolBitmapSize: { // wxToolBar::SetToolBitmapSize
@@ -8738,14 +8771,14 @@ case wxToolBar_SetToolLongHelp: { // wxToolBar::SetToolLongHelp
wxString helpString = wxString(bp, wxConvUTF8);
bp += *helpStringLen+((8-((4+ *helpStringLen) & 7)) & 7);
if(!This) throw wxe_badarg(0);
- This->SetToolLongHelp((int) *toolid,helpString);
+ This->SetToolLongHelp(*toolid,helpString);
break;
}
case wxToolBar_SetToolPacking: { // wxToolBar::SetToolPacking
wxToolBar *This = (wxToolBar *) getPtr(bp,memenv); bp += 4;
int * packing = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetToolPacking((int) *packing);
+ This->SetToolPacking(*packing);
break;
}
case wxToolBar_SetToolShortHelp: { // wxToolBar::SetToolShortHelp
@@ -8755,14 +8788,14 @@ case wxToolBar_SetToolShortHelp: { // wxToolBar::SetToolShortHelp
wxString helpString = wxString(bp, wxConvUTF8);
bp += *helpStringLen+((8-((4+ *helpStringLen) & 7)) & 7);
if(!This) throw wxe_badarg(0);
- This->SetToolShortHelp((int) *id,helpString);
+ This->SetToolShortHelp(*id,helpString);
break;
}
case wxToolBar_SetToolSeparation: { // wxToolBar::SetToolSeparation
wxToolBar *This = (wxToolBar *) getPtr(bp,memenv); bp += 4;
int * separation = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetToolSeparation((int) *separation);
+ This->SetToolSeparation(*separation);
break;
}
case wxToolBar_ToggleTool: { // wxToolBar::ToggleTool
@@ -8770,7 +8803,7 @@ case wxToolBar_ToggleTool: { // wxToolBar::ToggleTool
int * toolid = (int *) bp; bp += 4;
bool * toggle = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->ToggleTool((int) *toolid,(bool) *toggle);
+ This->ToggleTool(*toolid,*toggle);
break;
}
case wxStatusBar_new_0: { // wxStatusBar::wxStatusBar
@@ -8824,7 +8857,7 @@ case wxStatusBar_GetFieldRect: { // wxStatusBar::GetFieldRect
int * rectH = (int *) bp; bp += 4;
wxRect rect = wxRect(*rectX,*rectY,*rectW,*rectH);
if(!This) throw wxe_badarg(0);
- bool Result = This->GetFieldRect((int) *i,rect);
+ bool Result = This->GetFieldRect(*i,rect);
rt.addBool(Result);
break;
}
@@ -8889,14 +8922,14 @@ case wxStatusBar_SetFieldsCount: { // wxStatusBar::SetFieldsCount
} break;
}};
if(!This) throw wxe_badarg(0);
- This->SetFieldsCount((int) *number,widths);
+ This->SetFieldsCount(*number,widths);
break;
}
case wxStatusBar_SetMinHeight: { // wxStatusBar::SetMinHeight
wxStatusBar *This = (wxStatusBar *) getPtr(bp,memenv); bp += 4;
int * height = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetMinHeight((int) *height);
+ This->SetMinHeight(*height);
break;
}
case wxStatusBar_SetStatusText: { // wxStatusBar::SetStatusText
@@ -8945,7 +8978,7 @@ case wxBitmap_new_3: { // wxBitmap::wxBitmap
depth = (int)*(int *) bp; bp += 4;
} break;
}};
- wxBitmap * Result = new EwxBitmap((int) *width,(int) *height,depth);
+ wxBitmap * Result = new EwxBitmap(*width,*height,depth);
newPtr((void *) Result, 1, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxBitmap");
break;
@@ -8960,7 +8993,7 @@ case wxBitmap_new_4: { // wxBitmap::wxBitmap
depth = (int)*(int *) bp; bp += 4;
} break;
}};
- wxBitmap * Result = new EwxBitmap(bits,(int) *width,(int) *height,depth);
+ wxBitmap * Result = new EwxBitmap(bits,*width,*height,depth);
newPtr((void *) Result, 1, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxBitmap");
break;
@@ -8975,7 +9008,7 @@ case wxBitmap_new_2_0: { // wxBitmap::wxBitmap
type = *(wxBitmapType *) bp; bp += 4;;
} break;
}};
- wxBitmap * Result = new EwxBitmap(filename,(wxBitmapType) type);
+ wxBitmap * Result = new EwxBitmap(filename,type);
newPtr((void *) Result, 1, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxBitmap");
break;
@@ -9021,7 +9054,7 @@ case wxBitmap_Create: { // wxBitmap::Create
} break;
}};
if(!This) throw wxe_badarg(0);
- bool Result = This->Create((int) *width,(int) *height,depth);
+ bool Result = This->Create(*width,*height,depth);
rt.addBool(Result);
break;
}
@@ -9084,7 +9117,7 @@ type = *(wxBitmapType *) bp; bp += 4;;
} break;
}};
if(!This) throw wxe_badarg(0);
- bool Result = This->LoadFile(name,(wxBitmapType) type);
+ bool Result = This->LoadFile(name,type);
rt.addBool(Result);
break;
}
@@ -9109,7 +9142,7 @@ palette = (wxPalette *) getPtr(bp,memenv); bp += 4;
} break;
}};
if(!This) throw wxe_badarg(0);
- bool Result = This->SaveFile(name,(wxBitmapType) type,palette);
+ bool Result = This->SaveFile(name,type,palette);
rt.addBool(Result);
break;
}
@@ -9117,14 +9150,14 @@ case wxBitmap_SetDepth: { // wxBitmap::SetDepth
wxBitmap *This = (wxBitmap *) getPtr(bp,memenv); bp += 4;
int * depth = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetDepth((int) *depth);
+ This->SetDepth(*depth);
break;
}
case wxBitmap_SetHeight: { // wxBitmap::SetHeight
wxBitmap *This = (wxBitmap *) getPtr(bp,memenv); bp += 4;
int * height = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetHeight((int) *height);
+ This->SetHeight(*height);
break;
}
case wxBitmap_SetMask: { // wxBitmap::SetMask
@@ -9145,7 +9178,7 @@ case wxBitmap_SetWidth: { // wxBitmap::SetWidth
wxBitmap *This = (wxBitmap *) getPtr(bp,memenv); bp += 4;
int * width = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetWidth((int) *width);
+ This->SetWidth(*width);
break;
}
case wxIcon_new_0: { // wxIcon::wxIcon
@@ -9172,7 +9205,7 @@ type = *(wxBitmapType *) bp; bp += 4;;
desiredHeight = (int)*(int *) bp; bp += 4;
} break;
}};
- wxIcon * Result = new EwxIcon(filename,(wxBitmapType) type,desiredWidth,desiredHeight);
+ wxIcon * Result = new EwxIcon(filename,type,desiredWidth,desiredHeight);
newPtr((void *) Result, 1, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxIcon");
break;
@@ -9202,7 +9235,7 @@ case wxIconBundle_new_2: { // wxIconBundle::wxIconBundle
wxString file = wxString(bp, wxConvUTF8);
bp += *fileLen+((8-((4+ *fileLen) & 7)) & 7);
int * type = (int *) bp; bp += 4;
- wxIconBundle * Result = new wxIconBundle(file,(long) *type);
+ wxIconBundle * Result = new wxIconBundle(file,*type);
newPtr((void *) Result, 61, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxIconBundle");
break;
@@ -9234,7 +9267,7 @@ case wxIconBundle_AddIcon_2: { // wxIconBundle::AddIcon
bp += *fileLen+((8-((0+ *fileLen) & 7)) & 7);
int * type = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->AddIcon(file,(long) *type);
+ This->AddIcon(file,*type);
break;
}
case wxIconBundle_AddIcon_1: { // wxIconBundle::AddIcon
@@ -9250,7 +9283,7 @@ case wxIconBundle_GetIcon_1_1: { // wxIconBundle::GetIcon
int * sizeH = (int *) bp; bp += 4;
wxSize size = wxSize(*sizeW,*sizeH);
if(!This) throw wxe_badarg(0);
- const wxIcon * Result = &This->GetIcon(size);
+ const wxIcon * Result = new wxIcon(This->GetIcon(size)); newPtr((void *) Result,3, memenv);;
rt.addRef(getRef((void *)Result,memenv), "wxIcon");
break;
}
@@ -9264,7 +9297,7 @@ case wxIconBundle_GetIcon_1_0: { // wxIconBundle::GetIcon
} break;
}};
if(!This) throw wxe_badarg(0);
- const wxIcon * Result = &This->GetIcon(size);
+ const wxIcon * Result = new wxIcon(This->GetIcon(size)); newPtr((void *) Result,3, memenv);;
rt.addRef(getRef((void *)Result,memenv), "wxIcon");
break;
}
@@ -9276,7 +9309,7 @@ case wxCursor_new_0: { // wxCursor::wxCursor
}
case wxCursor_new_1_0: { // wxCursor::wxCursor
int * cursorId = (int *) bp; bp += 4;
- wxCursor * Result = new EwxCursor((int) *cursorId);
+ wxCursor * Result = new EwxCursor(*cursorId);
newPtr((void *) Result, 1, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxCursor");
break;
@@ -9288,6 +9321,7 @@ case wxCursor_new_1_1: { // wxCursor::wxCursor
rt.addRef(getRef((void *)Result,memenv), "wxCursor");
break;
}
+#if !wxCHECK_VERSION(2,9,0)
case wxCursor_new_4: { // wxCursor::wxCursor
int hotSpotX=-1;
int hotSpotY=-1;
@@ -9302,11 +9336,12 @@ case wxCursor_new_4: { // wxCursor::wxCursor
hotSpotY = (int)*(int *) bp; bp += 4;
} break;
}};
- wxCursor * Result = new EwxCursor(bits,(int) *width,(int) *height,hotSpotX,hotSpotY);
+ wxCursor * Result = new EwxCursor(bits,*width,*height,hotSpotX,hotSpotY);
newPtr((void *) Result, 1, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxCursor");
break;
}
+#endif
case wxCursor_Ok: { // wxCursor::Ok
wxCursor *This = (wxCursor *) getPtr(bp,memenv); bp += 4;
if(!This) throw wxe_badarg(0);
@@ -9335,7 +9370,7 @@ case wxMask_new_2_1: { // wxMask::wxMask
case wxMask_new_2_0: { // wxMask::wxMask
wxBitmap *bitmap = (wxBitmap *) getPtr(bp,memenv); bp += 4;
int * paletteIndex = (int *) bp; bp += 4;
- wxMask * Result = new EwxMask(*bitmap,(int) *paletteIndex);
+ wxMask * Result = new EwxMask(*bitmap,*paletteIndex);
newPtr((void *) Result, 1, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxMask");
break;
@@ -9365,7 +9400,7 @@ case wxMask_Create_2_0: { // wxMask::Create
wxBitmap *bitmap = (wxBitmap *) getPtr(bp,memenv); bp += 4;
int * paletteIndex = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->Create(*bitmap,(int) *paletteIndex);
+ bool Result = This->Create(*bitmap,*paletteIndex);
rt.addBool(Result);
break;
}
@@ -9392,7 +9427,7 @@ case wxImage_new_3_0: { // wxImage::wxImage
clear = *(bool *) bp; bp += 4;
} break;
}};
- wxImage * Result = new EwxImage((int) *width,(int) *height,clear);
+ wxImage * Result = new EwxImage(*width,*height,clear);
newPtr((void *) Result, 1, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxImage");
break;
@@ -9408,7 +9443,7 @@ case wxImage_new_4: { // wxImage::wxImage
} break;
}};
if(!static_data) {data = (unsigned char *) malloc(Ecmd.bin[0]->size);memcpy(data,Ecmd.bin[0]->base,Ecmd.bin[0]->size);};
- wxImage * Result = new EwxImage((int) *width,(int) *height,data,static_data);
+ wxImage * Result = new EwxImage(*width,*height,data,static_data);
newPtr((void *) Result, 1, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxImage");
break;
@@ -9425,7 +9460,7 @@ case wxImage_new_5: { // wxImage::wxImage
} break;
}};
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 * Result = new EwxImage((int) *width,(int) *height,data,alpha,static_data);
+ wxImage * Result = new EwxImage(*width,*height,data,alpha,static_data);
newPtr((void *) Result, 1, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxImage");
break;
@@ -9471,7 +9506,7 @@ case wxImage_Blur: { // wxImage::Blur
wxImage *This = (wxImage *) getPtr(bp,memenv); bp += 4;
int * radius = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxImage * Result = new wxImage(This->Blur((int) *radius)); newPtr((void *) Result,3, memenv);;
+ wxImage * Result = new wxImage(This->Blur(*radius)); newPtr((void *) Result,3, memenv);;
rt.addRef(getRef((void *)Result,memenv), "wxImage");
break;
}
@@ -9479,7 +9514,7 @@ case wxImage_BlurHorizontal: { // wxImage::BlurHorizontal
wxImage *This = (wxImage *) getPtr(bp,memenv); bp += 4;
int * radius = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxImage * Result = new wxImage(This->BlurHorizontal((int) *radius)); newPtr((void *) Result,3, memenv);;
+ wxImage * Result = new wxImage(This->BlurHorizontal(*radius)); newPtr((void *) Result,3, memenv);;
rt.addRef(getRef((void *)Result,memenv), "wxImage");
break;
}
@@ -9487,7 +9522,7 @@ case wxImage_BlurVertical: { // wxImage::BlurVertical
wxImage *This = (wxImage *) getPtr(bp,memenv); bp += 4;
int * radius = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxImage * Result = new wxImage(This->BlurVertical((int) *radius)); newPtr((void *) Result,3, memenv);;
+ wxImage * Result = new wxImage(This->BlurVertical(*radius)); newPtr((void *) Result,3, memenv);;
rt.addRef(getRef((void *)Result,memenv), "wxImage");
break;
}
@@ -9536,7 +9571,7 @@ case wxImage_ConvertToMono: { // wxImage::ConvertToMono
unsigned int * g = (unsigned int *) bp; bp += 4;
unsigned int * b = (unsigned int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxImage * Result = new wxImage(This->ConvertToMono((char) *r,(char) *g,(char) *b)); newPtr((void *) Result,3, memenv);;
+ wxImage * Result = new wxImage(This->ConvertToMono(*r,*g,*b)); newPtr((void *) Result,3, memenv);;
rt.addRef(getRef((void *)Result,memenv), "wxImage");
break;
}
@@ -9559,7 +9594,7 @@ case wxImage_Create_3: { // wxImage::Create
} break;
}};
if(!This) throw wxe_badarg(0);
- bool Result = This->Create((int) *width,(int) *height,clear);
+ bool Result = This->Create(*width,*height,clear);
rt.addBool(Result);
break;
}
@@ -9577,7 +9612,7 @@ case wxImage_Create_4: { // wxImage::Create
}};
if(!static_data) {data = (unsigned char *) malloc(Ecmd.bin[0]->size);memcpy(data,Ecmd.bin[0]->base,Ecmd.bin[0]->size);};
if(!This) throw wxe_badarg(0);
- bool Result = This->Create((int) *width,(int) *height,data,static_data);
+ bool Result = This->Create(*width,*height,data,static_data);
rt.addBool(Result);
break;
}
@@ -9596,7 +9631,7 @@ case wxImage_Create_5: { // wxImage::Create
}};
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);};
if(!This) throw wxe_badarg(0);
- bool Result = This->Create((int) *width,(int) *height,data,alpha,static_data);
+ bool Result = This->Create(*width,*height,data,alpha,static_data);
rt.addBool(Result);
break;
}
@@ -9645,7 +9680,7 @@ case wxImage_GetAlpha_2: { // wxImage::GetAlpha
int * x = (int *) bp; bp += 4;
int * y = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- char Result = This->GetAlpha((int) *x,(int) *y);
+ char Result = This->GetAlpha(*x,*y);
rt.addUint(Result);
break;
}
@@ -9663,7 +9698,7 @@ case wxImage_GetBlue: { // wxImage::GetBlue
int * x = (int *) bp; bp += 4;
int * y = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- char Result = This->GetBlue((int) *x,(int) *y);
+ char Result = This->GetBlue(*x,*y);
rt.addUint(Result);
break;
}
@@ -9681,18 +9716,18 @@ case wxImage_GetGreen: { // wxImage::GetGreen
int * x = (int *) bp; bp += 4;
int * y = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- char Result = This->GetGreen((int) *x,(int) *y);
+ char Result = This->GetGreen(*x,*y);
rt.addUint(Result);
break;
}
case wxImage_GetImageCount: { // wxImage::GetImageCount
- long type=wxBITMAP_TYPE_ANY;
+ wxBitmapType type=wxBITMAP_TYPE_ANY;
int * nameLen = (int *) bp; bp += 4;
wxString name = wxString(bp, wxConvUTF8);
bp += *nameLen+((8-((4+ *nameLen) & 7)) & 7);
while( * (int*) bp) { switch (* (int*) bp) {
case 1: {bp += 4;
- type = (long)*(int *) bp; bp += 4;
+type = *(wxBitmapType *) bp; bp += 4;;
} break;
}};
int Result = wxImage::GetImageCount(name,type);
@@ -9753,7 +9788,7 @@ case wxImage_GetRed: { // wxImage::GetRed
int * x = (int *) bp; bp += 4;
int * y = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- char Result = This->GetRed((int) *x,(int) *y);
+ char Result = This->GetRed(*x,*y);
rt.addUint(Result);
break;
}
@@ -9842,7 +9877,7 @@ case wxImage_IsTransparent: { // wxImage::IsTransparent
} break;
}};
if(!This) throw wxe_badarg(0);
- bool Result = This->IsTransparent((int) *x,(int) *y,threshold);
+ bool Result = This->IsTransparent(*x,*y,threshold);
rt.addBool(Result);
break;
}
@@ -9923,22 +9958,22 @@ case wxImage_Replace: { // wxImage::Replace
unsigned int * g2 = (unsigned int *) bp; bp += 4;
unsigned int * b2 = (unsigned int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->Replace((char) *r1,(char) *g1,(char) *b1,(char) *r2,(char) *g2,(char) *b2);
+ This->Replace(*r1,*g1,*b1,*r2,*g2,*b2);
break;
}
case wxImage_Rescale: { // wxImage::Rescale
- int quality=wxIMAGE_QUALITY_NORMAL;
+ wxImageResizeQuality quality=wxIMAGE_QUALITY_NORMAL;
wxImage *This = (wxImage *) getPtr(bp,memenv); bp += 4;
int * width = (int *) bp; bp += 4;
int * height = (int *) bp; bp += 4;
bp += 4; /* Align */
while( * (int*) bp) { switch (* (int*) bp) {
case 1: {bp += 4;
- quality = (int)*(int *) bp; bp += 4;
+quality = *(wxImageResizeQuality *) bp; bp += 4;;
} break;
}};
if(!This) throw wxe_badarg(0);
- wxImage * Result = &This->Rescale((int) *width,(int) *height,quality);
+ wxImage * Result = &This->Rescale(*width,*height,quality);
rt.addRef(getRef((void *)Result,memenv), "wxImage");
break;
}
@@ -9991,7 +10026,7 @@ case wxImage_Rotate: { // wxImage::Rotate
} break;
}};
if(!This) throw wxe_badarg(0);
- wxImage * Result = new wxImage(This->Rotate((double) *angle,centre_of_rotation,interpolating,offset_after_rotation)); newPtr((void *) Result,3, memenv);;
+ wxImage * Result = new wxImage(This->Rotate(*angle,centre_of_rotation,interpolating,offset_after_rotation)); newPtr((void *) Result,3, memenv);;
rt.addRef(getRef((void *)Result,memenv), "wxImage");
break;
}
@@ -10000,7 +10035,7 @@ case wxImage_RotateHue: { // wxImage::RotateHue
bp += 4; /* Align */
double * angle = (double *) bp; bp += 8;
if(!This) throw wxe_badarg(0);
- This->RotateHue((double) *angle);
+ This->RotateHue(*angle);
break;
}
case wxImage_Rotate90: { // wxImage::Rotate90
@@ -10034,7 +10069,7 @@ case wxImage_SaveFile_2_0: { // wxImage::SaveFile
bp += *nameLen+((8-((0+ *nameLen) & 7)) & 7);
int * type = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->SaveFile(name,(int) *type);
+ bool Result = This->SaveFile(name,*type);
rt.addBool(Result);
break;
}
@@ -10052,18 +10087,18 @@ case wxImage_SaveFile_2_1: { // wxImage::SaveFile
break;
}
case wxImage_Scale: { // wxImage::Scale
- int quality=wxIMAGE_QUALITY_NORMAL;
+ wxImageResizeQuality quality=wxIMAGE_QUALITY_NORMAL;
wxImage *This = (wxImage *) getPtr(bp,memenv); bp += 4;
int * width = (int *) bp; bp += 4;
int * height = (int *) bp; bp += 4;
bp += 4; /* Align */
while( * (int*) bp) { switch (* (int*) bp) {
case 1: {bp += 4;
- quality = (int)*(int *) bp; bp += 4;
+quality = *(wxImageResizeQuality *) bp; bp += 4;;
} break;
}};
if(!This) throw wxe_badarg(0);
- wxImage * Result = new wxImage(This->Scale((int) *width,(int) *height,quality)); newPtr((void *) Result,3, memenv);;
+ wxImage * Result = new wxImage(This->Scale(*width,*height,quality)); newPtr((void *) Result,3, memenv);;
rt.addRef(getRef((void *)Result,memenv), "wxImage");
break;
}
@@ -10101,7 +10136,7 @@ case wxImage_SetAlpha_3: { // wxImage::SetAlpha
int * y = (int *) bp; bp += 4;
unsigned int * alpha = (unsigned int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetAlpha((int) *x,(int) *y,(char) *alpha);
+ This->SetAlpha(*x,*y,*alpha);
break;
}
case wxImage_SetAlpha_2: { // wxImage::SetAlpha
@@ -10148,7 +10183,7 @@ case wxImage_SetData_4: { // wxImage::SetData
}};
if(!static_data) {data = (unsigned char *) malloc(Ecmd.bin[0]->size);memcpy(data,Ecmd.bin[0]->base,Ecmd.bin[0]->size);};
if(!This) throw wxe_badarg(0);
- This->SetData(data,(int) *new_width,(int) *new_height,static_data);
+ This->SetData(data,*new_width,*new_height,static_data);
break;
}
case wxImage_SetMask: { // wxImage::SetMask
@@ -10170,7 +10205,7 @@ case wxImage_SetMaskColour: { // wxImage::SetMaskColour
unsigned int * g = (unsigned int *) bp; bp += 4;
unsigned int * b = (unsigned int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetMaskColour((char) *r,(char) *g,(char) *b);
+ This->SetMaskColour(*r,*g,*b);
break;
}
case wxImage_SetMaskFromImage: { // wxImage::SetMaskFromImage
@@ -10180,7 +10215,7 @@ case wxImage_SetMaskFromImage: { // wxImage::SetMaskFromImage
unsigned int * mg = (unsigned int *) bp; bp += 4;
unsigned int * mb = (unsigned int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->SetMaskFromImage(*mask,(char) *mr,(char) *mg,(char) *mb);
+ bool Result = This->SetMaskFromImage(*mask,*mr,*mg,*mb);
rt.addBool(Result);
break;
}
@@ -10203,7 +10238,7 @@ case wxImage_SetOption_2_0: { // wxImage::SetOption
bp += *nameLen+((8-((0+ *nameLen) & 7)) & 7);
int * value = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetOption(name,(int) *value);
+ This->SetOption(name,*value);
break;
}
case wxImage_SetPalette: { // wxImage::SetPalette
@@ -10221,7 +10256,7 @@ case wxImage_SetRGB_5: { // wxImage::SetRGB
unsigned int * g = (unsigned int *) bp; bp += 4;
unsigned int * b = (unsigned int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetRGB((int) *x,(int) *y,(char) *r,(char) *g,(char) *b);
+ This->SetRGB(*x,*y,*r,*g,*b);
break;
}
case wxImage_SetRGB_4: { // wxImage::SetRGB
@@ -10235,7 +10270,7 @@ case wxImage_SetRGB_4: { // wxImage::SetRGB
unsigned int * g = (unsigned int *) bp; bp += 4;
unsigned int * b = (unsigned int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetRGB(rect,(char) *r,(char) *g,(char) *b);
+ This->SetRGB(rect,*r,*g,*b);
break;
}
case wxBrush_new_0: { // wxBrush::wxBrush
@@ -10271,8 +10306,8 @@ case wxBrush_new_1: { // wxBrush::wxBrush
case wxBrush_GetColour: { // wxBrush::GetColour
wxBrush *This = (wxBrush *) getPtr(bp,memenv); bp += 4;
if(!This) throw wxe_badarg(0);
- wxColour * Result = &This->GetColour();
- rt.add((*Result));
+ wxColour Result = This->GetColour();
+ rt.add(Result);
break;
}
case wxBrush_GetStipple: { // wxBrush::GetStipple
@@ -10320,7 +10355,7 @@ case wxBrush_SetColour_3: { // wxBrush::SetColour
unsigned int * g = (unsigned int *) bp; bp += 4;
unsigned int * b = (unsigned int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetColour((char) *r,(char) *g,(char) *b);
+ This->SetColour(*r,*g,*b);
break;
}
case wxBrush_SetStipple: { // wxBrush::SetStipple
@@ -10334,7 +10369,7 @@ case wxBrush_SetStyle: { // wxBrush::SetStyle
wxBrush *This = (wxBrush *) getPtr(bp,memenv); bp += 4;
int * style = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetStyle((int) *style);
+ This->SetStyle(*style);
break;
}
case wxPen_new_0: { // wxPen::wxPen
@@ -10374,8 +10409,8 @@ case wxPen_GetCap: { // wxPen::GetCap
case wxPen_GetColour: { // wxPen::GetColour
wxPen *This = (wxPen *) getPtr(bp,memenv); bp += 4;
if(!This) throw wxe_badarg(0);
- wxColour * Result = &This->GetColour();
- rt.add((*Result));
+ wxColour Result = This->GetColour();
+ rt.add(Result);
break;
}
case wxPen_GetJoin: { // wxPen::GetJoin
@@ -10408,9 +10443,9 @@ case wxPen_IsOk: { // wxPen::IsOk
}
case wxPen_SetCap: { // wxPen::SetCap
wxPen *This = (wxPen *) getPtr(bp,memenv); bp += 4;
- int * capStyle = (int *) bp; bp += 4;
+ wxPenCap capStyle = *(wxPenCap *) bp; bp += 4;;
if(!This) throw wxe_badarg(0);
- This->SetCap((int) *capStyle);
+ This->SetCap(capStyle);
break;
}
case wxPen_SetColour_1: { // wxPen::SetColour
@@ -10430,28 +10465,28 @@ case wxPen_SetColour_3: { // wxPen::SetColour
unsigned int * green = (unsigned int *) bp; bp += 4;
unsigned int * blue = (unsigned int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetColour((char) *red,(char) *green,(char) *blue);
+ This->SetColour(*red,*green,*blue);
break;
}
case wxPen_SetJoin: { // wxPen::SetJoin
wxPen *This = (wxPen *) getPtr(bp,memenv); bp += 4;
- int * joinStyle = (int *) bp; bp += 4;
+ wxPenJoin joinStyle = *(wxPenJoin *) bp; bp += 4;;
if(!This) throw wxe_badarg(0);
- This->SetJoin((int) *joinStyle);
+ This->SetJoin(joinStyle);
break;
}
case wxPen_SetStyle: { // wxPen::SetStyle
wxPen *This = (wxPen *) getPtr(bp,memenv); bp += 4;
int * style = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetStyle((int) *style);
+ This->SetStyle(*style);
break;
}
case wxPen_SetWidth: { // wxPen::SetWidth
wxPen *This = (wxPen *) getPtr(bp,memenv); bp += 4;
int * width = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetWidth((int) *width);
+ This->SetWidth(*width);
break;
}
case wxRegion_new_0: { // wxRegion::wxRegion
@@ -10465,7 +10500,7 @@ case wxRegion_new_4: { // wxRegion::wxRegion
int * y = (int *) bp; bp += 4;
int * w = (int *) bp; bp += 4;
int * h = (int *) bp; bp += 4;
- wxRegion * Result = new EwxRegion((wxCoord) *x,(wxCoord) *y,(wxCoord) *w,(wxCoord) *h);
+ wxRegion * Result = new EwxRegion(*x,*y,*w,*h);
newPtr((void *) Result, 1, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxRegion");
break;
@@ -10511,7 +10546,7 @@ case wxRegion_Contains_2: { // wxRegion::Contains
int * x = (int *) bp; bp += 4;
int * y = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->Contains((wxCoord) *x,(wxCoord) *y);
+ int Result = This->Contains(*x,*y);
rt.addInt(Result);
break;
}
@@ -10532,7 +10567,7 @@ case wxRegion_Contains_4: { // wxRegion::Contains
int * w = (int *) bp; bp += 4;
int * h = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->Contains((wxCoord) *x,(wxCoord) *y,(wxCoord) *w,(wxCoord) *h);
+ int Result = This->Contains(*x,*y,*w,*h);
rt.addInt(Result);
break;
}
@@ -10569,7 +10604,7 @@ case wxRegion_Intersect_4: { // wxRegion::Intersect
int * w = (int *) bp; bp += 4;
int * h = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->Intersect((wxCoord) *x,(wxCoord) *y,(wxCoord) *w,(wxCoord) *h);
+ bool Result = This->Intersect(*x,*y,*w,*h);
rt.addBool(Result);
break;
}
@@ -10607,7 +10642,7 @@ case wxRegion_Subtract_4: { // wxRegion::Subtract
int * w = (int *) bp; bp += 4;
int * h = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->Subtract((wxCoord) *x,(wxCoord) *y,(wxCoord) *w,(wxCoord) *h);
+ bool Result = This->Subtract(*x,*y,*w,*h);
rt.addBool(Result);
break;
}
@@ -10636,7 +10671,7 @@ case wxRegion_Offset_2: { // wxRegion::Offset
int * x = (int *) bp; bp += 4;
int * y = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->Offset((wxCoord) *x,(wxCoord) *y);
+ bool Result = This->Offset(*x,*y);
rt.addBool(Result);
break;
}
@@ -10657,7 +10692,7 @@ case wxRegion_Union_4: { // wxRegion::Union
int * w = (int *) bp; bp += 4;
int * h = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->Union((wxCoord) *x,(wxCoord) *y,(wxCoord) *w,(wxCoord) *h);
+ bool Result = This->Union(*x,*y,*w,*h);
rt.addBool(Result);
break;
}
@@ -10715,7 +10750,7 @@ case wxRegion_Xor_4: { // wxRegion::Xor
int * w = (int *) bp; bp += 4;
int * h = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->Xor((wxCoord) *x,(wxCoord) *y,(wxCoord) *w,(wxCoord) *h);
+ bool Result = This->Xor(*x,*y,*w,*h);
rt.addBool(Result);
break;
}
@@ -10751,7 +10786,7 @@ case wxAcceleratorTable_new_2: { // wxAcceleratorTable::wxAcceleratorTable
wxAcceleratorEntry *entries;
entries = (wxAcceleratorEntry *) driver_alloc(sizeof(wxAcceleratorEntry) * *entriesLen); for(int i=0; i < *entriesLen; i++) { entries[i] = * (wxAcceleratorEntry *) getPtr(bp,memenv); bp += 4;}
bp += ((0+ *entriesLen)%2 )*4;
- wxAcceleratorTable * Result = new EwxAcceleratorTable((int) *n,entries);
+ wxAcceleratorTable * Result = new EwxAcceleratorTable(*n,entries);
newPtr((void *) Result, 1, memenv);
driver_free(entries);
rt.addRef(getRef((void *)Result,memenv), "wxAcceleratorTable");
@@ -10828,7 +10863,7 @@ item = (wxMenuItem *) getPtr(bp,memenv); bp += 4;
} break;
}};
if(!This) throw wxe_badarg(0);
- This->Set((int) *flags,(int) *keyCode,(int) *cmd,item);
+ This->Set(*flags,*keyCode,*cmd,item);
break;
}
case wxAcceleratorEntry_destroy: { // wxAcceleratorEntry::destroy
@@ -10841,7 +10876,7 @@ case wxCaret_new_3: { // wxCaret::wxCaret
wxWindow *window = (wxWindow *) getPtr(bp,memenv); bp += 4;
int * width = (int *) bp; bp += 4;
int * height = (int *) bp; bp += 4;
- wxCaret * Result = new EwxCaret(window,(int) *width,(int) *height);
+ wxCaret * Result = new EwxCaret(window,*width,*height);
newPtr((void *) Result, 70, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxCaret");
break;
@@ -10868,7 +10903,7 @@ case wxCaret_Create_3: { // wxCaret::Create
int * width = (int *) bp; bp += 4;
int * height = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->Create(window,(int) *width,(int) *height);
+ bool Result = This->Create(window,*width,*height);
rt.addBool(Result);
break;
}
@@ -10934,7 +10969,7 @@ case wxCaret_Move_2: { // wxCaret::Move
int * x = (int *) bp; bp += 4;
int * y = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->Move((int) *x,(int) *y);
+ This->Move(*x,*y);
break;
}
case wxCaret_Move_1: { // wxCaret::Move
@@ -10948,7 +10983,7 @@ case wxCaret_Move_1: { // wxCaret::Move
}
case wxCaret_SetBlinkTime: { // wxCaret::SetBlinkTime
int * milliseconds = (int *) bp; bp += 4;
- wxCaret::SetBlinkTime((int) *milliseconds);
+ wxCaret::SetBlinkTime(*milliseconds);
break;
}
case wxCaret_SetSize_2: { // wxCaret::SetSize
@@ -10956,7 +10991,7 @@ case wxCaret_SetSize_2: { // wxCaret::SetSize
int * width = (int *) bp; bp += 4;
int * height = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetSize((int) *width,(int) *height);
+ This->SetSize(*width,*height);
break;
}
case wxCaret_SetSize_1: { // wxCaret::SetSize
@@ -11057,7 +11092,7 @@ userData = (wxObject *) getPtr(bp,memenv); bp += 4;
} break;
}};
if(!This) throw wxe_badarg(0);
- wxSizerItem * Result = (wxSizerItem*)This->Add((int) *width,(int) *height,proportion,flag,border,userData);
+ wxSizerItem * Result = (wxSizerItem*)This->Add(*width,*height,proportion,flag,border,userData);
rt.addRef(getRef((void *)Result,memenv), "wxSizerItem");
break;
}
@@ -11083,7 +11118,7 @@ case wxSizer_AddSpacer: { // wxSizer::AddSpacer
wxSizer *This = (wxSizer *) getPtr(bp,memenv); bp += 4;
int * size = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxSizerItem * Result = (wxSizerItem*)This->AddSpacer((int) *size);
+ wxSizerItem * Result = (wxSizerItem*)This->AddSpacer(*size);
rt.addRef(getRef((void *)Result,memenv), "wxSizerItem");
break;
}
@@ -11141,7 +11176,7 @@ case wxSizer_Detach_1_0: { // wxSizer::Detach
wxSizer *This = (wxSizer *) getPtr(bp,memenv); bp += 4;
int * index = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->Detach((int) *index);
+ bool Result = This->Detach(*index);
rt.addBool(Result);
break;
}
@@ -11203,7 +11238,7 @@ case wxSizer_GetItem_1: { // wxSizer::GetItem
wxSizer *This = (wxSizer *) getPtr(bp,memenv); bp += 4;
int * index = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxSizerItem * Result = (wxSizerItem*)This->GetItem((size_t) *index);
+ wxSizerItem * Result = (wxSizerItem*)This->GetItem(*index);
rt.addRef(getRef((void *)Result,memenv), "wxSizerItem");
break;
}
@@ -11260,7 +11295,7 @@ case wxSizer_Hide_1: { // wxSizer::Hide
wxSizer *This = (wxSizer *) getPtr(bp,memenv); bp += 4;
int * index = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->Hide((size_t) *index);
+ bool Result = This->Hide(*index);
rt.addBool(Result);
break;
}
@@ -11288,7 +11323,7 @@ userData = (wxObject *) getPtr(bp,memenv); bp += 4;
} break;
}};
if(!This) throw wxe_badarg(0);
- wxSizerItem * Result = (wxSizerItem*)This->Insert((size_t) *index,window,proportion,flag,border,userData);
+ wxSizerItem * Result = (wxSizerItem*)This->Insert(*index,window,proportion,flag,border,userData);
rt.addRef(getRef((void *)Result,memenv), "wxSizerItem");
break;
}
@@ -11316,7 +11351,7 @@ userData = (wxObject *) getPtr(bp,memenv); bp += 4;
} break;
}};
if(!This) throw wxe_badarg(0);
- wxSizerItem * Result = (wxSizerItem*)This->Insert((size_t) *index,sizer,proportion,flag,border,userData);
+ wxSizerItem * Result = (wxSizerItem*)This->Insert(*index,sizer,proportion,flag,border,userData);
rt.addRef(getRef((void *)Result,memenv), "wxSizerItem");
break;
}
@@ -11344,7 +11379,7 @@ userData = (wxObject *) getPtr(bp,memenv); bp += 4;
} break;
}};
if(!This) throw wxe_badarg(0);
- wxSizerItem * Result = (wxSizerItem*)This->Insert((size_t) *index,(int) *width,(int) *height,proportion,flag,border,userData);
+ wxSizerItem * Result = (wxSizerItem*)This->Insert(*index,*width,*height,proportion,flag,border,userData);
rt.addRef(getRef((void *)Result,memenv), "wxSizerItem");
break;
}
@@ -11354,7 +11389,7 @@ case wxSizer_Insert_3_3: { // wxSizer::Insert
wxWindow * window = (wxWindow *) getPtr(bp,memenv); bp += 4;
wxSizerFlags *flags = (wxSizerFlags *) getPtr(bp,memenv); bp += 4;
if(!This) throw wxe_badarg(0);
- wxSizerItem * Result = (wxSizerItem*)This->Insert((size_t) *index,window,*flags);
+ wxSizerItem * Result = (wxSizerItem*)This->Insert(*index,window,*flags);
rt.addRef(getRef((void *)Result,memenv), "wxSizerItem");
break;
}
@@ -11364,7 +11399,7 @@ case wxSizer_Insert_3_2: { // wxSizer::Insert
wxSizer *sizer = (wxSizer *) getPtr(bp,memenv); bp += 4;
wxSizerFlags *flags = (wxSizerFlags *) getPtr(bp,memenv); bp += 4;
if(!This) throw wxe_badarg(0);
- wxSizerItem * Result = (wxSizerItem*)This->Insert((size_t) *index,sizer,*flags);
+ wxSizerItem * Result = (wxSizerItem*)This->Insert(*index,sizer,*flags);
rt.addRef(getRef((void *)Result,memenv), "wxSizerItem");
break;
}
@@ -11373,7 +11408,7 @@ case wxSizer_Insert_2: { // wxSizer::Insert
int * index = (int *) bp; bp += 4;
wxSizerItem *item = (wxSizerItem *) getPtr(bp,memenv); bp += 4;
if(!This) throw wxe_badarg(0);
- wxSizerItem * Result = (wxSizerItem*)This->Insert((size_t) *index,item);
+ wxSizerItem * Result = (wxSizerItem*)This->Insert(*index,item);
rt.addRef(getRef((void *)Result,memenv), "wxSizerItem");
break;
}
@@ -11382,7 +11417,7 @@ case wxSizer_InsertSpacer: { // wxSizer::InsertSpacer
int * index = (int *) bp; bp += 4;
int * size = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxSizerItem * Result = (wxSizerItem*)This->InsertSpacer((size_t) *index,(int) *size);
+ wxSizerItem * Result = (wxSizerItem*)This->InsertSpacer(*index,*size);
rt.addRef(getRef((void *)Result,memenv), "wxSizerItem");
break;
}
@@ -11396,7 +11431,7 @@ case wxSizer_InsertStretchSpacer: { // wxSizer::InsertStretchSpacer
} break;
}};
if(!This) throw wxe_badarg(0);
- wxSizerItem * Result = (wxSizerItem*)This->InsertStretchSpacer((size_t) *index,prop);
+ wxSizerItem * Result = (wxSizerItem*)This->InsertStretchSpacer(*index,prop);
rt.addRef(getRef((void *)Result,memenv), "wxSizerItem");
break;
}
@@ -11420,7 +11455,7 @@ case wxSizer_IsShown_1_0: { // wxSizer::IsShown
wxSizer *This = (wxSizer *) getPtr(bp,memenv); bp += 4;
int * index = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->IsShown((size_t) *index);
+ bool Result = This->IsShown(*index);
rt.addBool(Result);
break;
}
@@ -11506,7 +11541,7 @@ userData = (wxObject *) getPtr(bp,memenv); bp += 4;
} break;
}};
if(!This) throw wxe_badarg(0);
- wxSizerItem * Result = (wxSizerItem*)This->Prepend((int) *width,(int) *height,proportion,flag,border,userData);
+ wxSizerItem * Result = (wxSizerItem*)This->Prepend(*width,*height,proportion,flag,border,userData);
rt.addRef(getRef((void *)Result,memenv), "wxSizerItem");
break;
}
@@ -11540,7 +11575,7 @@ case wxSizer_PrependSpacer: { // wxSizer::PrependSpacer
wxSizer *This = (wxSizer *) getPtr(bp,memenv); bp += 4;
int * size = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxSizerItem * Result = (wxSizerItem*)This->PrependSpacer((int) *size);
+ wxSizerItem * Result = (wxSizerItem*)This->PrependSpacer(*size);
rt.addRef(getRef((void *)Result,memenv), "wxSizerItem");
break;
}
@@ -11576,7 +11611,7 @@ case wxSizer_Remove_1_0: { // wxSizer::Remove
wxSizer *This = (wxSizer *) getPtr(bp,memenv); bp += 4;
int * index = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->Remove((int) *index);
+ bool Result = This->Remove(*index);
rt.addBool(Result);
break;
}
@@ -11617,7 +11652,7 @@ case wxSizer_Replace_2: { // wxSizer::Replace
int * index = (int *) bp; bp += 4;
wxSizerItem *newitem = (wxSizerItem *) getPtr(bp,memenv); bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->Replace((size_t) *index,newitem);
+ bool Result = This->Replace(*index,newitem);
rt.addBool(Result);
break;
}
@@ -11628,7 +11663,7 @@ case wxSizer_SetDimension: { // wxSizer::SetDimension
int * width = (int *) bp; bp += 4;
int * height = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetDimension((int) *x,(int) *y,(int) *width,(int) *height);
+ This->SetDimension(*x,*y,*width,*height);
break;
}
case wxSizer_SetMinSize_2: { // wxSizer::SetMinSize
@@ -11636,7 +11671,7 @@ case wxSizer_SetMinSize_2: { // wxSizer::SetMinSize
int * width = (int *) bp; bp += 4;
int * height = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetMinSize((int) *width,(int) *height);
+ This->SetMinSize(*width,*height);
break;
}
case wxSizer_SetMinSize_1: { // wxSizer::SetMinSize
@@ -11654,7 +11689,7 @@ case wxSizer_SetItemMinSize_3_2: { // wxSizer::SetItemMinSize
int * width = (int *) bp; bp += 4;
int * height = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->SetItemMinSize(window,(int) *width,(int) *height);
+ bool Result = This->SetItemMinSize(window,*width,*height);
rt.addBool(Result);
break;
}
@@ -11675,7 +11710,7 @@ case wxSizer_SetItemMinSize_3_1: { // wxSizer::SetItemMinSize
int * width = (int *) bp; bp += 4;
int * height = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->SetItemMinSize(sizer,(int) *width,(int) *height);
+ bool Result = This->SetItemMinSize(sizer,*width,*height);
rt.addBool(Result);
break;
}
@@ -11696,7 +11731,7 @@ case wxSizer_SetItemMinSize_3_0: { // wxSizer::SetItemMinSize
int * width = (int *) bp; bp += 4;
int * height = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->SetItemMinSize((size_t) *index,(int) *width,(int) *height);
+ bool Result = This->SetItemMinSize(*index,*width,*height);
rt.addBool(Result);
break;
}
@@ -11707,7 +11742,7 @@ case wxSizer_SetItemMinSize_2_0: { // wxSizer::SetItemMinSize
int * sizeH = (int *) bp; bp += 4;
wxSize size = wxSize(*sizeW,*sizeH);
if(!This) throw wxe_badarg(0);
- bool Result = This->SetItemMinSize((size_t) *index,size);
+ bool Result = This->SetItemMinSize(*index,size);
rt.addBool(Result);
break;
}
@@ -11771,7 +11806,7 @@ case wxSizer_Show_2_0: { // wxSizer::Show
} break;
}};
if(!This) throw wxe_badarg(0);
- bool Result = This->Show((size_t) *index,show);
+ bool Result = This->Show(*index,show);
rt.addBool(Result);
break;
}
@@ -11779,7 +11814,7 @@ case wxSizer_Show_1: { // wxSizer::Show
wxSizer *This = (wxSizer *) getPtr(bp,memenv); bp += 4;
bool * show = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->Show((bool) *show);
+ This->Show(*show);
break;
}
case wxSizerFlags_new: { // wxSizerFlags::wxSizerFlags
@@ -11798,7 +11833,7 @@ case wxSizerFlags_Align: { // wxSizerFlags::Align
wxSizerFlags *This = (wxSizerFlags *) getPtr(bp,memenv); bp += 4;
int * alignment = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxSizerFlags * Result = &This->Align((int) *alignment);
+ wxSizerFlags * Result = &This->Align(*alignment);
rt.addRef(getRef((void *)Result,memenv), "wxSizerFlags");
break;
}
@@ -11807,7 +11842,7 @@ case wxSizerFlags_Border_2: { // wxSizerFlags::Border
int * direction = (int *) bp; bp += 4;
int * borderInPixels = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxSizerFlags * Result = &This->Border((int) *direction,(int) *borderInPixels);
+ wxSizerFlags * Result = &This->Border(*direction,*borderInPixels);
rt.addRef(getRef((void *)Result,memenv), "wxSizerFlags");
break;
}
@@ -11857,7 +11892,7 @@ case wxSizerFlags_Proportion: { // wxSizerFlags::Proportion
wxSizerFlags *This = (wxSizerFlags *) getPtr(bp,memenv); bp += 4;
int * proportion = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxSizerFlags * Result = &This->Proportion((int) *proportion);
+ wxSizerFlags * Result = &This->Proportion(*proportion);
rt.addRef(getRef((void *)Result,memenv), "wxSizerFlags");
break;
}
@@ -11880,7 +11915,7 @@ case wxSizerItem_new_5_1: { // wxSizerItem::wxSizerItem
int * flag = (int *) bp; bp += 4;
int * border = (int *) bp; bp += 4;
wxObject *userData = (wxObject *) getPtr(bp,memenv); bp += 4;
- wxSizerItem * Result = new EwxSizerItem(window,(int) *proportion,(int) *flag,(int) *border,userData);
+ wxSizerItem * Result = new EwxSizerItem(window,*proportion,*flag,*border,userData);
newPtr((void *) Result, 1, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxSizerItem");
break;
@@ -11899,7 +11934,7 @@ case wxSizerItem_new_5_0: { // wxSizerItem::wxSizerItem
int * flag = (int *) bp; bp += 4;
int * border = (int *) bp; bp += 4;
wxObject *userData = (wxObject *) getPtr(bp,memenv); bp += 4;
- wxSizerItem * Result = new EwxSizerItem(sizer,(int) *proportion,(int) *flag,(int) *border,userData);
+ wxSizerItem * Result = new EwxSizerItem(sizer,*proportion,*flag,*border,userData);
newPtr((void *) Result, 1, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxSizerItem");
break;
@@ -11919,7 +11954,7 @@ case wxSizerItem_new_6: { // wxSizerItem::wxSizerItem
int * flag = (int *) bp; bp += 4;
int * border = (int *) bp; bp += 4;
wxObject *userData = (wxObject *) getPtr(bp,memenv); bp += 4;
- wxSizerItem * Result = new EwxSizerItem((int) *width,(int) *height,(int) *proportion,(int) *flag,(int) *border,userData);
+ wxSizerItem * Result = new EwxSizerItem(*width,*height,*proportion,*flag,*border,userData);
newPtr((void *) Result, 1, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxSizerItem");
break;
@@ -11928,7 +11963,7 @@ case wxSizerItem_new_3: { // wxSizerItem::wxSizerItem
int * width = (int *) bp; bp += 4;
int * height = (int *) bp; bp += 4;
wxSizerFlags *flags = (wxSizerFlags *) getPtr(bp,memenv); bp += 4;
- wxSizerItem * Result = new EwxSizerItem((int) *width,(int) *height,*flags);
+ wxSizerItem * Result = new EwxSizerItem(*width,*height,*flags);
newPtr((void *) Result, 1, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxSizerItem");
break;
@@ -12074,7 +12109,7 @@ case wxSizerItem_SetBorder: { // wxSizerItem::SetBorder
wxSizerItem *This = (wxSizerItem *) getPtr(bp,memenv); bp += 4;
int * border = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetBorder((int) *border);
+ This->SetBorder(*border);
break;
}
case wxSizerItem_SetDimension: { // wxSizerItem::SetDimension
@@ -12093,7 +12128,7 @@ case wxSizerItem_SetFlag: { // wxSizerItem::SetFlag
wxSizerItem *This = (wxSizerItem *) getPtr(bp,memenv); bp += 4;
int * flag = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetFlag((int) *flag);
+ This->SetFlag(*flag);
break;
}
case wxSizerItem_SetInitSize: { // wxSizerItem::SetInitSize
@@ -12101,7 +12136,7 @@ case wxSizerItem_SetInitSize: { // wxSizerItem::SetInitSize
int * x = (int *) bp; bp += 4;
int * y = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetInitSize((int) *x,(int) *y);
+ This->SetInitSize(*x,*y);
break;
}
case wxSizerItem_SetMinSize_1: { // wxSizerItem::SetMinSize
@@ -12118,14 +12153,14 @@ case wxSizerItem_SetMinSize_2: { // wxSizerItem::SetMinSize
int * x = (int *) bp; bp += 4;
int * y = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetMinSize((int) *x,(int) *y);
+ This->SetMinSize(*x,*y);
break;
}
case wxSizerItem_SetProportion: { // wxSizerItem::SetProportion
wxSizerItem *This = (wxSizerItem *) getPtr(bp,memenv); bp += 4;
int * proportion = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetProportion((int) *proportion);
+ This->SetProportion(*proportion);
break;
}
case wxSizerItem_SetRatio_2: { // wxSizerItem::SetRatio
@@ -12133,7 +12168,7 @@ case wxSizerItem_SetRatio_2: { // wxSizerItem::SetRatio
int * width = (int *) bp; bp += 4;
int * height = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetRatio((int) *width,(int) *height);
+ This->SetRatio(*width,*height);
break;
}
case wxSizerItem_SetRatio_1_1: { // wxSizerItem::SetRatio
@@ -12149,7 +12184,7 @@ case wxSizerItem_SetRatio_1_0: { // wxSizerItem::SetRatio
wxSizerItem *This = (wxSizerItem *) getPtr(bp,memenv); bp += 4;
float * ratio = (float *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetRatio((float) *ratio);
+ This->SetRatio(*ratio);
break;
}
case wxSizerItem_SetSizer: { // wxSizerItem::SetSizer
@@ -12173,7 +12208,7 @@ case wxSizerItem_SetSpacer_2: { // wxSizerItem::SetSpacer
int * width = (int *) bp; bp += 4;
int * height = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetSpacer((int) *width,(int) *height);
+ This->SetSpacer(*width,*height);
break;
}
case wxSizerItem_SetWindow: { // wxSizerItem::SetWindow
@@ -12187,12 +12222,12 @@ case wxSizerItem_Show: { // wxSizerItem::Show
wxSizerItem *This = (wxSizerItem *) getPtr(bp,memenv); bp += 4;
bool * show = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->Show((bool) *show);
+ This->Show(*show);
break;
}
case wxBoxSizer_new: { // wxBoxSizer::wxBoxSizer
int * orient = (int *) bp; bp += 4;
- wxBoxSizer * Result = new EwxBoxSizer((int) *orient);
+ wxBoxSizer * Result = new EwxBoxSizer(*orient);
newPtr((void *) Result, 1, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxBoxSizer");
break;
@@ -12207,7 +12242,7 @@ case wxBoxSizer_GetOrientation: { // wxBoxSizer::GetOrientation
case wxStaticBoxSizer_new_2: { // wxStaticBoxSizer::wxStaticBoxSizer
wxStaticBox *box = (wxStaticBox *) getPtr(bp,memenv); bp += 4;
int * orient = (int *) bp; bp += 4;
- wxStaticBoxSizer * Result = new EwxStaticBoxSizer(box,(int) *orient);
+ wxStaticBoxSizer * Result = new EwxStaticBoxSizer(box,*orient);
newPtr((void *) Result, 1, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxStaticBoxSizer");
break;
@@ -12223,7 +12258,7 @@ case wxStaticBoxSizer_new_3: { // wxStaticBoxSizer::wxStaticBoxSizer
bp += *labelLen+((8-((0+ *labelLen) & 7)) & 7);
} break;
}};
- wxStaticBoxSizer * Result = new EwxStaticBoxSizer((int) *orient,win,label);
+ wxStaticBoxSizer * Result = new EwxStaticBoxSizer(*orient,win,label);
newPtr((void *) Result, 1, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxStaticBoxSizer");
break;
@@ -12240,7 +12275,7 @@ case wxGridSizer_new_4: { // wxGridSizer::wxGridSizer
int * cols = (int *) bp; bp += 4;
int * vgap = (int *) bp; bp += 4;
int * hgap = (int *) bp; bp += 4;
- wxGridSizer * Result = new EwxGridSizer((int) *rows,(int) *cols,(int) *vgap,(int) *hgap);
+ wxGridSizer * Result = new EwxGridSizer(*rows,*cols,*vgap,*hgap);
newPtr((void *) Result, 1, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxGridSizer");
break;
@@ -12258,7 +12293,7 @@ case wxGridSizer_new_2: { // wxGridSizer::wxGridSizer
hgap = (int)*(int *) bp; bp += 4;
} break;
}};
- wxGridSizer * Result = new EwxGridSizer((int) *cols,vgap,hgap);
+ wxGridSizer * Result = new EwxGridSizer(*cols,vgap,hgap);
newPtr((void *) Result, 1, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxGridSizer");
break;
@@ -12295,28 +12330,28 @@ case wxGridSizer_SetCols: { // wxGridSizer::SetCols
wxGridSizer *This = (wxGridSizer *) getPtr(bp,memenv); bp += 4;
int * cols = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetCols((int) *cols);
+ This->SetCols(*cols);
break;
}
case wxGridSizer_SetHGap: { // wxGridSizer::SetHGap
wxGridSizer *This = (wxGridSizer *) getPtr(bp,memenv); bp += 4;
int * gap = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetHGap((int) *gap);
+ This->SetHGap(*gap);
break;
}
case wxGridSizer_SetRows: { // wxGridSizer::SetRows
wxGridSizer *This = (wxGridSizer *) getPtr(bp,memenv); bp += 4;
int * rows = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetRows((int) *rows);
+ This->SetRows(*rows);
break;
}
case wxGridSizer_SetVGap: { // wxGridSizer::SetVGap
wxGridSizer *This = (wxGridSizer *) getPtr(bp,memenv); bp += 4;
int * gap = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetVGap((int) *gap);
+ This->SetVGap(*gap);
break;
}
case wxFlexGridSizer_new_4: { // wxFlexGridSizer::wxFlexGridSizer
@@ -12324,7 +12359,7 @@ case wxFlexGridSizer_new_4: { // wxFlexGridSizer::wxFlexGridSizer
int * cols = (int *) bp; bp += 4;
int * vgap = (int *) bp; bp += 4;
int * hgap = (int *) bp; bp += 4;
- wxFlexGridSizer * Result = new EwxFlexGridSizer((int) *rows,(int) *cols,(int) *vgap,(int) *hgap);
+ wxFlexGridSizer * Result = new EwxFlexGridSizer(*rows,*cols,*vgap,*hgap);
newPtr((void *) Result, 1, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxFlexGridSizer");
break;
@@ -12342,7 +12377,7 @@ case wxFlexGridSizer_new_2: { // wxFlexGridSizer::wxFlexGridSizer
hgap = (int)*(int *) bp; bp += 4;
} break;
}};
- wxFlexGridSizer * Result = new EwxFlexGridSizer((int) *cols,vgap,hgap);
+ wxFlexGridSizer * Result = new EwxFlexGridSizer(*cols,vgap,hgap);
newPtr((void *) Result, 1, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxFlexGridSizer");
break;
@@ -12357,7 +12392,7 @@ case wxFlexGridSizer_AddGrowableCol: { // wxFlexGridSizer::AddGrowableCol
} break;
}};
if(!This) throw wxe_badarg(0);
- This->AddGrowableCol((size_t) *idx,proportion);
+ This->AddGrowableCol(*idx,proportion);
break;
}
case wxFlexGridSizer_AddGrowableRow: { // wxFlexGridSizer::AddGrowableRow
@@ -12370,7 +12405,7 @@ case wxFlexGridSizer_AddGrowableRow: { // wxFlexGridSizer::AddGrowableRow
} break;
}};
if(!This) throw wxe_badarg(0);
- This->AddGrowableRow((size_t) *idx,proportion);
+ This->AddGrowableRow(*idx,proportion);
break;
}
case wxFlexGridSizer_GetFlexibleDirection: { // wxFlexGridSizer::GetFlexibleDirection
@@ -12391,28 +12426,28 @@ case wxFlexGridSizer_RemoveGrowableCol: { // wxFlexGridSizer::RemoveGrowableCol
wxFlexGridSizer *This = (wxFlexGridSizer *) getPtr(bp,memenv); bp += 4;
int * idx = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->RemoveGrowableCol((size_t) *idx);
+ This->RemoveGrowableCol(*idx);
break;
}
case wxFlexGridSizer_RemoveGrowableRow: { // wxFlexGridSizer::RemoveGrowableRow
wxFlexGridSizer *This = (wxFlexGridSizer *) getPtr(bp,memenv); bp += 4;
int * idx = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->RemoveGrowableRow((size_t) *idx);
+ This->RemoveGrowableRow(*idx);
break;
}
case wxFlexGridSizer_SetFlexibleDirection: { // wxFlexGridSizer::SetFlexibleDirection
wxFlexGridSizer *This = (wxFlexGridSizer *) getPtr(bp,memenv); bp += 4;
int * direction = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetFlexibleDirection((int) *direction);
+ This->SetFlexibleDirection(*direction);
break;
}
case wxFlexGridSizer_SetNonFlexibleGrowMode: { // wxFlexGridSizer::SetNonFlexibleGrowMode
wxFlexGridSizer *This = (wxFlexGridSizer *) getPtr(bp,memenv); bp += 4;
wxFlexSizerGrowMode mode = *(wxFlexSizerGrowMode *) bp; bp += 4;;
if(!This) throw wxe_badarg(0);
- This->SetNonFlexibleGrowMode((wxFlexSizerGrowMode) mode);
+ This->SetNonFlexibleGrowMode(mode);
break;
}
case wxGridBagSizer_new: { // wxGridBagSizer::wxGridBagSizer
@@ -12525,7 +12560,7 @@ userData = (wxObject *) getPtr(bp,memenv); bp += 4;
} break;
}};
if(!This) throw wxe_badarg(0);
- wxSizerItem * Result = (wxSizerItem*)This->Add((int) *width,(int) *height,pos,span,flag,border,userData);
+ wxSizerItem * Result = (wxSizerItem*)This->Add(*width,*height,pos,span,flag,border,userData);
rt.addRef(getRef((void *)Result,memenv), "wxSizerItem");
break;
}
@@ -12613,7 +12648,7 @@ userData = (wxObject *) getPtr(bp,memenv); bp += 4;
} break;
}};
if(!This) throw wxe_badarg(0);
- wxSizerItem * Result = (wxSizerItem*)This->Add((int) *width,(int) *height,proportion,flag,border,userData);
+ wxSizerItem * Result = (wxSizerItem*)This->Add(*width,*height,proportion,flag,border,userData);
rt.addRef(getRef((void *)Result,memenv), "wxSizerItem");
break;
}
@@ -12715,7 +12750,7 @@ case wxGridBagSizer_GetCellSize: { // wxGridBagSizer::GetCellSize
int * row = (int *) bp; bp += 4;
int * col = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxSize Result = This->GetCellSize((int) *row,(int) *col);
+ wxSize Result = This->GetCellSize(*row,*col);
rt.add(Result);
break;
}
@@ -12746,7 +12781,7 @@ case wxGridBagSizer_GetItemPosition_1_0: { // wxGridBagSizer::GetItemPosition
wxGridBagSizer *This = (wxGridBagSizer *) getPtr(bp,memenv); bp += 4;
int * index = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxGBPosition Result = This->GetItemPosition((size_t) *index);
+ wxGBPosition Result = This->GetItemPosition(*index);
rt.add(Result);
break;
}
@@ -12770,7 +12805,7 @@ case wxGridBagSizer_GetItemSpan_1_0: { // wxGridBagSizer::GetItemSpan
wxGridBagSizer *This = (wxGridBagSizer *) getPtr(bp,memenv); bp += 4;
int * index = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxGBSpan Result = This->GetItemSpan((size_t) *index);
+ wxGBSpan Result = This->GetItemSpan(*index);
rt.add(Result);
break;
}
@@ -12812,7 +12847,7 @@ case wxGridBagSizer_SetItemPosition_2_0: { // wxGridBagSizer::SetItemPosition
int * posC = (int *) bp; bp += 4;
wxGBPosition pos = wxGBPosition(*posR,*posC);
if(!This) throw wxe_badarg(0);
- bool Result = This->SetItemPosition((size_t) *index,pos);
+ bool Result = This->SetItemPosition(*index,pos);
rt.addBool(Result);
break;
}
@@ -12845,7 +12880,7 @@ case wxGridBagSizer_SetItemSpan_2_0: { // wxGridBagSizer::SetItemSpan
int * spanCS = (int *) bp; bp += 4;
wxGBSpan span = wxGBSpan(*spanRS,*spanCS);
if(!This) throw wxe_badarg(0);
- bool Result = This->SetItemSpan((size_t) *index,span);
+ bool Result = This->SetItemSpan(*index,span);
rt.addBool(Result);
break;
}
@@ -12925,7 +12960,7 @@ case wxFont_new_5: { // wxFont::wxFont
encoding = *(wxFontEncoding *) bp; bp += 4;;
} break;
}};
- wxFont * Result = new EwxFont((int) *size,(wxFontFamily) family,(wxFontStyle) style,(int) *weight,underlined,face,(wxFontEncoding) encoding);
+ wxFont * Result = new EwxFont(*size,family,style,*weight,underlined,face,encoding);
newPtr((void *) Result, 1, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxFont");
break;
@@ -13007,7 +13042,7 @@ case wxFont_Ok: { // wxFont::Ok
}
case wxFont_SetDefaultEncoding: { // wxFont::SetDefaultEncoding
wxFontEncoding encoding = *(wxFontEncoding *) bp; bp += 4;;
- wxFont::SetDefaultEncoding((wxFontEncoding) encoding);
+ wxFont::SetDefaultEncoding(encoding);
break;
}
case wxFont_SetFaceName: { // wxFont::SetFaceName
@@ -13024,45 +13059,45 @@ case wxFont_SetFamily: { // wxFont::SetFamily
wxFont *This = (wxFont *) getPtr(bp,memenv); bp += 4;
wxFontFamily family = *(wxFontFamily *) bp; bp += 4;;
if(!This) throw wxe_badarg(0);
- This->SetFamily((wxFontFamily) family);
+ This->SetFamily(family);
break;
}
case wxFont_SetPointSize: { // wxFont::SetPointSize
wxFont *This = (wxFont *) getPtr(bp,memenv); bp += 4;
int * pointSize = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetPointSize((int) *pointSize);
+ This->SetPointSize(*pointSize);
break;
}
case wxFont_SetStyle: { // wxFont::SetStyle
wxFont *This = (wxFont *) getPtr(bp,memenv); bp += 4;
wxFontStyle style = *(wxFontStyle *) bp; bp += 4;;
if(!This) throw wxe_badarg(0);
- This->SetStyle((wxFontStyle) style);
+ This->SetStyle(style);
break;
}
case wxFont_SetUnderlined: { // wxFont::SetUnderlined
wxFont *This = (wxFont *) getPtr(bp,memenv); bp += 4;
bool * underlined = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetUnderlined((bool) *underlined);
+ This->SetUnderlined(*underlined);
break;
}
case wxFont_SetWeight: { // wxFont::SetWeight
wxFont *This = (wxFont *) getPtr(bp,memenv); bp += 4;
int * weight = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetWeight((int) *weight);
+ This->SetWeight(*weight);
break;
}
case wxToolTip_Enable: { // wxToolTip::Enable
bool * flag = (bool *) bp; bp += 4;
- wxToolTip::Enable((bool) *flag);
+ wxToolTip::Enable(*flag);
break;
}
case wxToolTip_SetDelay: { // wxToolTip::SetDelay
int * msecs = (int *) bp; bp += 4;
- wxToolTip::SetDelay((long) *msecs);
+ wxToolTip::SetDelay(*msecs);
break;
}
case wxToolTip_new: { // wxToolTip::wxToolTip
@@ -13130,7 +13165,7 @@ case wxButton_new_3: { // wxButton::wxButton
validator = (wxValidator *) getPtr(bp,memenv); bp += 4;
} break;
}};
- wxButton * Result = new EwxButton(parent,(wxWindowID) *id,label,pos,size,style,*validator);
+ wxButton * Result = new EwxButton(parent,*id,label,pos,size,style,*validator);
newPtr((void *) Result, 0, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxButton");
break;
@@ -13177,7 +13212,7 @@ validator = (wxValidator *) getPtr(bp,memenv); bp += 4;
} break;
}};
if(!This) throw wxe_badarg(0);
- bool Result = This->Create(parent,(wxWindowID) *id,label,pos,size,style,*validator);
+ bool Result = This->Create(parent,*id,label,pos,size,style,*validator);
rt.addBool(Result);
break;
}
@@ -13230,7 +13265,7 @@ case wxBitmapButton_new_4: { // wxBitmapButton::wxBitmapButton
validator = (wxValidator *) getPtr(bp,memenv); bp += 4;
} break;
}};
- wxBitmapButton * Result = new EwxBitmapButton(parent,(wxWindowID) *id,*bitmap,pos,size,style,*validator);
+ wxBitmapButton * Result = new EwxBitmapButton(parent,*id,*bitmap,pos,size,style,*validator);
newPtr((void *) Result, 0, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxBitmapButton");
break;
@@ -13271,35 +13306,35 @@ validator = (wxValidator *) getPtr(bp,memenv); bp += 4;
} break;
}};
if(!This) throw wxe_badarg(0);
- bool Result = This->Create(parent,(wxWindowID) *id,*bitmap,pos,size,style,*validator);
+ bool Result = This->Create(parent,*id,*bitmap,pos,size,style,*validator);
rt.addBool(Result);
break;
}
case wxBitmapButton_GetBitmapDisabled: { // wxBitmapButton::GetBitmapDisabled
wxBitmapButton *This = (wxBitmapButton *) getPtr(bp,memenv); bp += 4;
if(!This) throw wxe_badarg(0);
- const wxBitmap * Result = &This->GetBitmapDisabled();
+ const wxBitmap * Result = new wxBitmap(This->GetBitmapDisabled()); newPtr((void *) Result,3, memenv);;
rt.addRef(getRef((void *)Result,memenv), "wxBitmap");
break;
}
case wxBitmapButton_GetBitmapFocus: { // wxBitmapButton::GetBitmapFocus
wxBitmapButton *This = (wxBitmapButton *) getPtr(bp,memenv); bp += 4;
if(!This) throw wxe_badarg(0);
- const wxBitmap * Result = &This->GetBitmapFocus();
+ const wxBitmap * Result = new wxBitmap(This->GetBitmapFocus()); newPtr((void *) Result,3, memenv);;
rt.addRef(getRef((void *)Result,memenv), "wxBitmap");
break;
}
case wxBitmapButton_GetBitmapLabel: { // wxBitmapButton::GetBitmapLabel
wxBitmapButton *This = (wxBitmapButton *) getPtr(bp,memenv); bp += 4;
if(!This) throw wxe_badarg(0);
- const wxBitmap * Result = &This->GetBitmapLabel();
+ const wxBitmap * Result = new wxBitmap(This->GetBitmapLabel()); newPtr((void *) Result,3, memenv);;
rt.addRef(getRef((void *)Result,memenv), "wxBitmap");
break;
}
case wxBitmapButton_GetBitmapSelected: { // wxBitmapButton::GetBitmapSelected
wxBitmapButton *This = (wxBitmapButton *) getPtr(bp,memenv); bp += 4;
if(!This) throw wxe_badarg(0);
- const wxBitmap * Result = &This->GetBitmapSelected();
+ const wxBitmap * Result = new wxBitmap(This->GetBitmapSelected()); newPtr((void *) Result,3, memenv);;
rt.addRef(getRef((void *)Result,memenv), "wxBitmap");
break;
}
@@ -13367,7 +13402,7 @@ case wxToggleButton_new_4: { // wxToggleButton::wxToggleButton
validator = (wxValidator *) getPtr(bp,memenv); bp += 4;
} break;
}};
- wxToggleButton * Result = new EwxToggleButton(parent,(wxWindowID) *id,label,pos,size,style,*validator);
+ wxToggleButton * Result = new EwxToggleButton(parent,*id,label,pos,size,style,*validator);
newPtr((void *) Result, 0, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxToggleButton");
break;
@@ -13404,7 +13439,7 @@ validator = (wxValidator *) getPtr(bp,memenv); bp += 4;
} break;
}};
if(!This) throw wxe_badarg(0);
- bool Result = This->Create(parent,(wxWindowID) *id,label,pos,size,style,*validator);
+ bool Result = This->Create(parent,*id,label,pos,size,style,*validator);
rt.addBool(Result);
break;
}
@@ -13419,7 +13454,7 @@ case wxToggleButton_SetValue: { // wxToggleButton::SetValue
wxToggleButton *This = (wxToggleButton *) getPtr(bp,memenv); bp += 4;
bool * state = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetValue((bool) *state);
+ This->SetValue(*state);
break;
}
case wxCalendarCtrl_new_0: { // wxCalendarCtrl::wxCalendarCtrl
@@ -13462,7 +13497,7 @@ case wxCalendarCtrl_new_3: { // wxCalendarCtrl::wxCalendarCtrl
style = (long)*(int *) bp; bp += 4;
} break;
}};
- wxCalendarCtrl * Result = new EwxCalendarCtrl(parent,(wxWindowID) *id,date,pos,size,style);
+ wxCalendarCtrl * Result = new EwxCalendarCtrl(parent,*id,date,pos,size,style);
newPtr((void *) Result, 0, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxCalendarCtrl");
break;
@@ -13504,7 +13539,7 @@ case wxCalendarCtrl_Create: { // wxCalendarCtrl::Create
} break;
}};
if(!This) throw wxe_badarg(0);
- bool Result = This->Create(parent,(wxWindowID) *id,date,pos,size,style);
+ bool Result = This->Create(parent,*id,date,pos,size,style);
rt.addBool(Result);
break;
}
@@ -13525,10 +13560,11 @@ case wxCalendarCtrl_SetDate: { // wxCalendarCtrl::SetDate
case wxCalendarCtrl_GetDate: { // wxCalendarCtrl::GetDate
wxCalendarCtrl *This = (wxCalendarCtrl *) getPtr(bp,memenv); bp += 4;
if(!This) throw wxe_badarg(0);
- const wxDateTime * Result = &This->GetDate();
- rt.add((*Result));
+ const wxDateTime Result = This->GetDate();
+ rt.add(Result);
break;
}
+#if !wxCHECK_VERSION(2,9,0)
case wxCalendarCtrl_EnableYearChange: { // wxCalendarCtrl::EnableYearChange
bool enable=true;
wxCalendarCtrl *This = (wxCalendarCtrl *) getPtr(bp,memenv); bp += 4;
@@ -13542,6 +13578,7 @@ case wxCalendarCtrl_EnableYearChange: { // wxCalendarCtrl::EnableYearChange
This->EnableYearChange(enable);
break;
}
+#endif
case wxCalendarCtrl_EnableMonthChange: { // wxCalendarCtrl::EnableMonthChange
bool enable=true;
wxCalendarCtrl *This = (wxCalendarCtrl *) getPtr(bp,memenv); bp += 4;
@@ -13662,7 +13699,7 @@ case wxCalendarCtrl_GetAttr: { // wxCalendarCtrl::GetAttr
wxCalendarCtrl *This = (wxCalendarCtrl *) getPtr(bp,memenv); bp += 4;
int * day = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxCalendarDateAttr * Result = (wxCalendarDateAttr*)This->GetAttr((size_t) *day);
+ wxCalendarDateAttr * Result = (wxCalendarDateAttr*)This->GetAttr(*day);
rt.addRef(getRef((void *)Result,memenv), "wxCalendarDateAttr");
break;
}
@@ -13671,21 +13708,21 @@ case wxCalendarCtrl_SetAttr: { // wxCalendarCtrl::SetAttr
int * day = (int *) bp; bp += 4;
wxCalendarDateAttr *attr = (wxCalendarDateAttr *) getPtr(bp,memenv); bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetAttr((size_t) *day,attr);
+ This->SetAttr(*day,attr);
break;
}
case wxCalendarCtrl_SetHoliday: { // wxCalendarCtrl::SetHoliday
wxCalendarCtrl *This = (wxCalendarCtrl *) getPtr(bp,memenv); bp += 4;
int * day = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetHoliday((size_t) *day);
+ This->SetHoliday(*day);
break;
}
case wxCalendarCtrl_ResetAttr: { // wxCalendarCtrl::ResetAttr
wxCalendarCtrl *This = (wxCalendarCtrl *) getPtr(bp,memenv); bp += 4;
int * day = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->ResetAttr((size_t) *day);
+ This->ResetAttr(*day);
break;
}
case wxCalendarCtrl_HitTest: { // wxCalendarCtrl::HitTest
@@ -13743,7 +13780,7 @@ font = (wxFont *) getPtr(bp,memenv); bp += 4;
border = *(wxCalendarDateBorder *) bp; bp += 4;;
} break;
}};
- wxCalendarDateAttr * Result = new wxCalendarDateAttr(colText,colBack,colBorder,*font,(wxCalendarDateBorder) border);
+ wxCalendarDateAttr * Result = new wxCalendarDateAttr(colText,colBack,colBorder,*font,border);
newPtr((void *) Result, 88, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxCalendarDateAttr");
break;
@@ -13762,7 +13799,7 @@ case wxCalendarDateAttr_new_2_0: { // wxCalendarDateAttr::wxCalendarDateAttr
bp += 4; /* Align */
} break;
}};
- wxCalendarDateAttr * Result = new wxCalendarDateAttr((wxCalendarDateBorder) border,colBorder);
+ wxCalendarDateAttr * Result = new wxCalendarDateAttr(border,colBorder);
newPtr((void *) Result, 88, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxCalendarDateAttr");
break;
@@ -13811,14 +13848,14 @@ case wxCalendarDateAttr_SetBorder: { // wxCalendarDateAttr::SetBorder
wxCalendarDateAttr *This = (wxCalendarDateAttr *) getPtr(bp,memenv); bp += 4;
wxCalendarDateBorder border = *(wxCalendarDateBorder *) bp; bp += 4;;
if(!This) throw wxe_badarg(0);
- This->SetBorder((wxCalendarDateBorder) border);
+ This->SetBorder(border);
break;
}
case wxCalendarDateAttr_SetHoliday: { // wxCalendarDateAttr::SetHoliday
wxCalendarDateAttr *This = (wxCalendarDateAttr *) getPtr(bp,memenv); bp += 4;
bool * holiday = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetHoliday((bool) *holiday);
+ This->SetHoliday(*holiday);
break;
}
case wxCalendarDateAttr_HasTextColour: { // wxCalendarDateAttr::HasTextColour
@@ -13934,7 +13971,7 @@ case wxCheckBox_new_4: { // wxCheckBox::wxCheckBox
validator = (wxValidator *) getPtr(bp,memenv); bp += 4;
} break;
}};
- wxCheckBox * Result = new EwxCheckBox(parent,(wxWindowID) *id,label,pos,size,style,*validator);
+ wxCheckBox * Result = new EwxCheckBox(parent,*id,label,pos,size,style,*validator);
newPtr((void *) Result, 0, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxCheckBox");
break;
@@ -13977,7 +14014,7 @@ validator = (wxValidator *) getPtr(bp,memenv); bp += 4;
} break;
}};
if(!This) throw wxe_badarg(0);
- bool Result = This->Create(parent,(wxWindowID) *id,label,pos,size,style,*validator);
+ bool Result = This->Create(parent,*id,label,pos,size,style,*validator);
rt.addBool(Result);
break;
}
@@ -14020,14 +14057,14 @@ case wxCheckBox_SetValue: { // wxCheckBox::SetValue
wxCheckBox *This = (wxCheckBox *) getPtr(bp,memenv); bp += 4;
bool * state = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetValue((bool) *state);
+ This->SetValue(*state);
break;
}
case wxCheckBox_Set3StateValue: { // wxCheckBox::Set3StateValue
wxCheckBox *This = (wxCheckBox *) getPtr(bp,memenv); bp += 4;
wxCheckBoxState state = *(wxCheckBoxState *) bp; bp += 4;;
if(!This) throw wxe_badarg(0);
- This->Set3StateValue((wxCheckBoxState) state);
+ This->Set3StateValue(state);
break;
}
case wxCheckListBox_new_0: { // wxCheckListBox::wxCheckListBox
@@ -14075,7 +14112,7 @@ case wxCheckListBox_new_3: { // wxCheckListBox::wxCheckListBox
validator = (wxValidator *) getPtr(bp,memenv); bp += 4;
} break;
}};
- wxCheckListBox * Result = new EwxCheckListBox(parent,(wxWindowID) *id,pos,size,choices,style,*validator);
+ wxCheckListBox * Result = new EwxCheckListBox(parent,*id,pos,size,choices,style,*validator);
newPtr((void *) Result, 0, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxCheckListBox");
break;
@@ -14090,14 +14127,14 @@ case wxCheckListBox_Check: { // wxCheckListBox::Check
} break;
}};
if(!This) throw wxe_badarg(0);
- This->Check((int) *index,check);
+ This->Check(*index,check);
break;
}
case wxCheckListBox_IsChecked: { // wxCheckListBox::IsChecked
wxCheckListBox *This = (wxCheckListBox *) getPtr(bp,memenv); bp += 4;
unsigned int * index = (unsigned int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->IsChecked((int) *index);
+ bool Result = This->IsChecked(*index);
rt.addBool(Result);
break;
}
@@ -14140,7 +14177,7 @@ case wxChoice_new_3: { // wxChoice::wxChoice
validator = (wxValidator *) getPtr(bp,memenv); bp += 4;
} break;
}};
- wxChoice * Result = new EwxChoice(parent,(wxWindowID) *id,pos,size,choices,style,*validator);
+ wxChoice * Result = new EwxChoice(parent,*id,pos,size,choices,style,*validator);
newPtr((void *) Result, 0, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxChoice");
break;
@@ -14182,7 +14219,7 @@ validator = (wxValidator *) getPtr(bp,memenv); bp += 4;
} break;
}};
if(!This) throw wxe_badarg(0);
- bool Result = This->Create(parent,(wxWindowID) *id,pos,size,choices,style,*validator);
+ bool Result = This->Create(parent,*id,pos,size,choices,style,*validator);
rt.addBool(Result);
break;
}
@@ -14190,7 +14227,7 @@ case wxChoice_Delete: { // wxChoice::Delete
wxChoice *This = (wxChoice *) getPtr(bp,memenv); bp += 4;
unsigned int * n = (unsigned int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->Delete((int) *n);
+ This->Delete(*n);
break;
}
case wxChoice_GetColumns: { // wxChoice::GetColumns
@@ -14264,7 +14301,7 @@ case wxComboBox_new_3: { // wxComboBox::wxComboBox
validator = (wxValidator *) getPtr(bp,memenv); bp += 4;
} break;
}};
- wxComboBox * Result = new EwxComboBox(parent,(wxWindowID) *id,value,pos,size,choices,style,*validator);
+ wxComboBox * Result = new EwxComboBox(parent,*id,value,pos,size,choices,style,*validator);
newPtr((void *) Result, 0, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxComboBox");
break;
@@ -14303,7 +14340,7 @@ validator = (wxValidator *) getPtr(bp,memenv); bp += 4;
} break;
}};
if(!This) throw wxe_badarg(0);
- bool Result = This->Create(parent,(wxWindowID) *id,value,pos,size,choices,style,*validator);
+ bool Result = This->Create(parent,*id,value,pos,size,choices,style,*validator);
rt.addBool(Result);
break;
}
@@ -14395,7 +14432,7 @@ case wxComboBox_Replace: { // wxComboBox::Replace
wxString value = wxString(bp, wxConvUTF8);
bp += *valueLen+((8-((0+ *valueLen) & 7)) & 7);
if(!This) throw wxe_badarg(0);
- This->Replace((long) *from,(long) *to,value);
+ This->Replace(*from,*to,value);
break;
}
case wxComboBox_Remove: { // wxComboBox::Remove
@@ -14403,14 +14440,14 @@ case wxComboBox_Remove: { // wxComboBox::Remove
int * from = (int *) bp; bp += 4;
int * to = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->Remove((long) *from,(long) *to);
+ This->Remove(*from,*to);
break;
}
case wxComboBox_SetInsertionPoint: { // wxComboBox::SetInsertionPoint
wxComboBox *This = (wxComboBox *) getPtr(bp,memenv); bp += 4;
int * pos = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetInsertionPoint((long) *pos);
+ This->SetInsertionPoint(*pos);
break;
}
case wxComboBox_SetInsertionPointEnd: { // wxComboBox::SetInsertionPointEnd
@@ -14423,7 +14460,7 @@ case wxComboBox_SetSelection_1: { // wxComboBox::SetSelection
wxComboBox *This = (wxComboBox *) getPtr(bp,memenv); bp += 4;
int * n = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetSelection((int) *n);
+ This->SetSelection(*n);
break;
}
case wxComboBox_SetSelection_2: { // wxComboBox::SetSelection
@@ -14431,7 +14468,7 @@ case wxComboBox_SetSelection_2: { // wxComboBox::SetSelection
int * from = (int *) bp; bp += 4;
int * to = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetSelection((long) *from,(long) *to);
+ This->SetSelection(*from,*to);
break;
}
case wxComboBox_SetValue: { // wxComboBox::SetValue
@@ -14484,7 +14521,7 @@ case wxGauge_new_4: { // wxGauge::wxGauge
validator = (wxValidator *) getPtr(bp,memenv); bp += 4;
} break;
}};
- wxGauge * Result = new EwxGauge(parent,(wxWindowID) *id,(int) *range,pos,size,style,*validator);
+ wxGauge * Result = new EwxGauge(parent,*id,*range,pos,size,style,*validator);
newPtr((void *) Result, 0, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxGauge");
break;
@@ -14519,7 +14556,7 @@ validator = (wxValidator *) getPtr(bp,memenv); bp += 4;
} break;
}};
if(!This) throw wxe_badarg(0);
- bool Result = This->Create(parent,(wxWindowID) *id,(int) *range,pos,size,style,*validator);
+ bool Result = This->Create(parent,*id,*range,pos,size,style,*validator);
rt.addBool(Result);
break;
}
@@ -14562,28 +14599,28 @@ case wxGauge_SetBezelFace: { // wxGauge::SetBezelFace
wxGauge *This = (wxGauge *) getPtr(bp,memenv); bp += 4;
int * w = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetBezelFace((int) *w);
+ This->SetBezelFace(*w);
break;
}
case wxGauge_SetRange: { // wxGauge::SetRange
wxGauge *This = (wxGauge *) getPtr(bp,memenv); bp += 4;
int * r = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetRange((int) *r);
+ This->SetRange(*r);
break;
}
case wxGauge_SetShadowWidth: { // wxGauge::SetShadowWidth
wxGauge *This = (wxGauge *) getPtr(bp,memenv); bp += 4;
int * w = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetShadowWidth((int) *w);
+ This->SetShadowWidth(*w);
break;
}
case wxGauge_SetValue: { // wxGauge::SetValue
wxGauge *This = (wxGauge *) getPtr(bp,memenv); bp += 4;
int * pos = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetValue((int) *pos);
+ This->SetValue(*pos);
break;
}
case wxGauge_Pulse: { // wxGauge::Pulse
@@ -14793,7 +14830,7 @@ case wxGenericDirCtrl_SetFilterIndex: { // wxGenericDirCtrl::SetFilterIndex
wxGenericDirCtrl *This = (wxGenericDirCtrl *) getPtr(bp,memenv); bp += 4;
int * n = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetFilterIndex((int) *n);
+ This->SetFilterIndex(*n);
break;
}
case wxGenericDirCtrl_SetPath: { // wxGenericDirCtrl::SetPath
@@ -14831,7 +14868,7 @@ case wxStaticBox_new_4: { // wxStaticBox::wxStaticBox
style = (long)*(int *) bp; bp += 4;
} break;
}};
- wxStaticBox * Result = new EwxStaticBox(parent,(wxWindowID) *id,label,pos,size,style);
+ wxStaticBox * Result = new EwxStaticBox(parent,*id,label,pos,size,style);
newPtr((void *) Result, 0, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxStaticBox");
break;
@@ -14870,7 +14907,7 @@ case wxStaticBox_Create: { // wxStaticBox::Create
} break;
}};
if(!This) throw wxe_badarg(0);
- bool Result = This->Create(parent,(wxWindowID) *id,label,pos,size,style);
+ bool Result = This->Create(parent,*id,label,pos,size,style);
rt.addBool(Result);
break;
}
@@ -14995,7 +15032,7 @@ case wxListBox_new_3: { // wxListBox::wxListBox
validator = (wxValidator *) getPtr(bp,memenv); bp += 4;
} break;
}};
- wxListBox * Result = new EwxListBox(parent,(wxWindowID) *id,pos,size,choices,style,*validator);
+ wxListBox * Result = new EwxListBox(parent,*id,pos,size,choices,style,*validator);
newPtr((void *) Result, 0, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxListBox");
break;
@@ -15037,7 +15074,7 @@ validator = (wxValidator *) getPtr(bp,memenv); bp += 4;
} break;
}};
if(!This) throw wxe_badarg(0);
- bool Result = This->Create(parent,(wxWindowID) *id,pos,size,choices,style,*validator);
+ bool Result = This->Create(parent,*id,pos,size,choices,style,*validator);
rt.addBool(Result);
break;
}
@@ -15045,7 +15082,7 @@ case wxListBox_Deselect: { // wxListBox::Deselect
wxListBox *This = (wxListBox *) getPtr(bp,memenv); bp += 4;
int * n = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->Deselect((int) *n);
+ This->Deselect(*n);
break;
}
case wxListBox_GetSelections: { // wxListBox::GetSelections
@@ -15072,14 +15109,14 @@ case wxListBox_InsertItems: { // wxListBox::InsertItems
bp += (8-((0+ itemsASz) & 7 )) & 7;
unsigned int * pos = (unsigned int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->InsertItems(items,(int) *pos);
+ This->InsertItems(items,*pos);
break;
}
case wxListBox_IsSelected: { // wxListBox::IsSelected
wxListBox *This = (wxListBox *) getPtr(bp,memenv); bp += 4;
int * n = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->IsSelected((int) *n);
+ bool Result = This->IsSelected(*n);
rt.addBool(Result);
break;
}
@@ -15096,7 +15133,7 @@ case wxListBox_Set: { // wxListBox::Set
}
bp += (8-((0+ itemsASz) & 7 )) & 7;
if(!This) throw wxe_badarg(0);
- This->Set(items,NULL);
+ This->Set(items,(void **) NULL);
break;
}
case wxListBox_HitTest: { // wxListBox::HitTest
@@ -15113,7 +15150,7 @@ case wxListBox_SetFirstItem_1_0: { // wxListBox::SetFirstItem
wxListBox *This = (wxListBox *) getPtr(bp,memenv); bp += 4;
int * n = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetFirstItem((int) *n);
+ This->SetFirstItem(*n);
break;
}
case wxListBox_SetFirstItem_1_1: { // wxListBox::SetFirstItem
@@ -15203,7 +15240,7 @@ case wxListCtrl_AssignImageList: { // wxListCtrl::AssignImageList
wxImageList *imageList = (wxImageList *) getPtr(bp,memenv); bp += 4;
int * which = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->AssignImageList(imageList,(int) *which);
+ This->AssignImageList(imageList,*which);
break;
}
case wxListCtrl_ClearAll: { // wxListCtrl::ClearAll
@@ -15277,7 +15314,7 @@ case wxListCtrl_DeleteColumn: { // wxListCtrl::DeleteColumn
wxListCtrl *This = (wxListCtrl *) getPtr(bp,memenv); bp += 4;
int * col = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->DeleteColumn((int) *col);
+ bool Result = This->DeleteColumn(*col);
rt.addBool(Result);
break;
}
@@ -15285,7 +15322,7 @@ case wxListCtrl_DeleteItem: { // wxListCtrl::DeleteItem
wxListCtrl *This = (wxListCtrl *) getPtr(bp,memenv); bp += 4;
int * item = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->DeleteItem((long) *item);
+ bool Result = This->DeleteItem(*item);
rt.addBool(Result);
break;
}
@@ -15293,7 +15330,7 @@ case wxListCtrl_EditLabel: { // wxListCtrl::EditLabel
wxListCtrl *This = (wxListCtrl *) getPtr(bp,memenv); bp += 4;
int * item = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxTextCtrl * Result = (wxTextCtrl*)This->EditLabel((long) *item);
+ wxTextCtrl * Result = (wxTextCtrl*)This->EditLabel(*item);
rt.addRef(getRef((void *)Result,memenv), "wxTextCtrl");
break;
}
@@ -15301,7 +15338,7 @@ case wxListCtrl_EnsureVisible: { // wxListCtrl::EnsureVisible
wxListCtrl *This = (wxListCtrl *) getPtr(bp,memenv); bp += 4;
int * item = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->EnsureVisible((long) *item);
+ bool Result = This->EnsureVisible(*item);
rt.addBool(Result);
break;
}
@@ -15318,7 +15355,7 @@ case wxListCtrl_FindItem_3_0: { // wxListCtrl::FindItem
} break;
}};
if(!This) throw wxe_badarg(0);
- long Result = This->FindItem((long) *start,str,partial);
+ long Result = This->FindItem(*start,str,partial);
rt.addInt(Result);
break;
}
@@ -15330,7 +15367,7 @@ case wxListCtrl_FindItem_3_1: { // wxListCtrl::FindItem
wxPoint pt = wxPoint(*ptX,*ptY);
int * direction = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- long Result = This->FindItem((long) *start,pt,(int) *direction);
+ long Result = This->FindItem(*start,pt,*direction);
rt.addInt(Result);
break;
}
@@ -15339,7 +15376,7 @@ case wxListCtrl_GetColumn: { // wxListCtrl::GetColumn
int * col = (int *) bp; bp += 4;
wxListItem *item = (wxListItem *) getPtr(bp,memenv); bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->GetColumn((int) *col,*item);
+ bool Result = This->GetColumn(*col,*item);
rt.addBool(Result);
break;
}
@@ -15354,7 +15391,7 @@ case wxListCtrl_GetColumnWidth: { // wxListCtrl::GetColumnWidth
wxListCtrl *This = (wxListCtrl *) getPtr(bp,memenv); bp += 4;
int * col = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->GetColumnWidth((int) *col);
+ int Result = This->GetColumnWidth(*col);
rt.addInt(Result);
break;
}
@@ -15376,7 +15413,7 @@ case wxListCtrl_GetImageList: { // wxListCtrl::GetImageList
wxListCtrl *This = (wxListCtrl *) getPtr(bp,memenv); bp += 4;
int * which = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxImageList * Result = (wxImageList*)This->GetImageList((int) *which);
+ wxImageList * Result = (wxImageList*)This->GetImageList(*which);
rt.addRef(getRef((void *)Result,memenv), "wxImageList");
break;
}
@@ -15392,7 +15429,7 @@ case wxListCtrl_GetItemBackgroundColour: { // wxListCtrl::GetItemBackgroundColou
wxListCtrl *This = (wxListCtrl *) getPtr(bp,memenv); bp += 4;
int * item = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxColour Result = This->GetItemBackgroundColour((long) *item);
+ wxColour Result = This->GetItemBackgroundColour(*item);
rt.add(Result);
break;
}
@@ -15407,7 +15444,7 @@ case wxListCtrl_GetItemData: { // wxListCtrl::GetItemData
wxListCtrl *This = (wxListCtrl *) getPtr(bp,memenv); bp += 4;
int * item = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxUIntPtr Result = This->GetItemData((long) *item);
+ wxUIntPtr Result = This->GetItemData(*item);
rt.addInt(Result);
break;
}
@@ -15415,7 +15452,7 @@ case wxListCtrl_GetItemFont: { // wxListCtrl::GetItemFont
wxListCtrl *This = (wxListCtrl *) getPtr(bp,memenv); bp += 4;
int * item = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxFont * Result = new wxFont(This->GetItemFont((long) *item)); newPtr((void *) Result,3, memenv);;
+ wxFont * Result = new wxFont(This->GetItemFont(*item)); newPtr((void *) Result,3, memenv);;
rt.addRef(getRef((void *)Result,memenv), "wxFont");
break;
}
@@ -15424,7 +15461,7 @@ case wxListCtrl_GetItemPosition: { // wxListCtrl::GetItemPosition
wxListCtrl *This = (wxListCtrl *) getPtr(bp,memenv); bp += 4;
int * item = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->GetItemPosition((long) *item,pos);
+ bool Result = This->GetItemPosition(*item,pos);
rt.addBool(Result);
rt.add(pos);
rt.addTupleCount(2);
@@ -15441,7 +15478,7 @@ case wxListCtrl_GetItemRect: { // wxListCtrl::GetItemRect
} break;
}};
if(!This) throw wxe_badarg(0);
- bool Result = This->GetItemRect((long) *item,rect,code);
+ bool Result = This->GetItemRect(*item,rect,code);
rt.addBool(Result);
rt.add(rect);
rt.addTupleCount(2);
@@ -15459,7 +15496,7 @@ case wxListCtrl_GetItemState: { // wxListCtrl::GetItemState
int * item = (int *) bp; bp += 4;
int * stateMask = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->GetItemState((long) *item,(long) *stateMask);
+ int Result = This->GetItemState(*item,*stateMask);
rt.addInt(Result);
break;
}
@@ -15467,7 +15504,7 @@ case wxListCtrl_GetItemText: { // wxListCtrl::GetItemText
wxListCtrl *This = (wxListCtrl *) getPtr(bp,memenv); bp += 4;
int * item = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxString Result = This->GetItemText((long) *item);
+ wxString Result = This->GetItemText(*item);
rt.add(Result);
break;
}
@@ -15475,7 +15512,7 @@ case wxListCtrl_GetItemTextColour: { // wxListCtrl::GetItemTextColour
wxListCtrl *This = (wxListCtrl *) getPtr(bp,memenv); bp += 4;
int * item = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxColour Result = This->GetItemTextColour((long) *item);
+ wxColour Result = This->GetItemTextColour(*item);
rt.add(Result);
break;
}
@@ -15493,7 +15530,7 @@ case wxListCtrl_GetNextItem: { // wxListCtrl::GetNextItem
} break;
}};
if(!This) throw wxe_badarg(0);
- long Result = This->GetNextItem((long) *item,geometry,state);
+ long Result = This->GetNextItem(*item,geometry,state);
rt.addInt(Result);
break;
}
@@ -15541,7 +15578,7 @@ case wxListCtrl_InsertColumn_2: { // wxListCtrl::InsertColumn
int * col = (int *) bp; bp += 4;
wxListItem *info = (wxListItem *) getPtr(bp,memenv); bp += 4;
if(!This) throw wxe_badarg(0);
- long Result = This->InsertColumn((long) *col,*info);
+ long Result = This->InsertColumn(*col,*info);
rt.addInt(Result);
break;
}
@@ -15562,7 +15599,7 @@ case wxListCtrl_InsertColumn_3: { // wxListCtrl::InsertColumn
} break;
}};
if(!This) throw wxe_badarg(0);
- long Result = This->InsertColumn((long) *col,heading,format,width);
+ long Result = This->InsertColumn(*col,heading,format,width);
rt.addInt(Result);
break;
}
@@ -15581,7 +15618,7 @@ case wxListCtrl_InsertItem_2_1: { // wxListCtrl::InsertItem
wxString label = wxString(bp, wxConvUTF8);
bp += *labelLen+((8-((4+ *labelLen) & 7)) & 7);
if(!This) throw wxe_badarg(0);
- long Result = This->InsertItem((long) *index,label);
+ long Result = This->InsertItem(*index,label);
rt.addInt(Result);
break;
}
@@ -15590,7 +15627,7 @@ case wxListCtrl_InsertItem_2_0: { // wxListCtrl::InsertItem
int * index = (int *) bp; bp += 4;
int * imageIndex = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- long Result = This->InsertItem((long) *index,(int) *imageIndex);
+ long Result = This->InsertItem(*index,*imageIndex);
rt.addInt(Result);
break;
}
@@ -15602,7 +15639,7 @@ case wxListCtrl_InsertItem_3: { // wxListCtrl::InsertItem
bp += *labelLen+((8-((4+ *labelLen) & 7)) & 7);
int * imageIndex = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- long Result = This->InsertItem((long) *index,label,(int) *imageIndex);
+ long Result = This->InsertItem(*index,label,*imageIndex);
rt.addInt(Result);
break;
}
@@ -15610,7 +15647,7 @@ case wxListCtrl_RefreshItem: { // wxListCtrl::RefreshItem
wxListCtrl *This = (wxListCtrl *) getPtr(bp,memenv); bp += 4;
int * item = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->RefreshItem((long) *item);
+ This->RefreshItem(*item);
break;
}
case wxListCtrl_RefreshItems: { // wxListCtrl::RefreshItems
@@ -15618,7 +15655,7 @@ case wxListCtrl_RefreshItems: { // wxListCtrl::RefreshItems
int * itemFrom = (int *) bp; bp += 4;
int * itemTo = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->RefreshItems((long) *itemFrom,(long) *itemTo);
+ This->RefreshItems(*itemFrom,*itemTo);
break;
}
case wxListCtrl_ScrollList: { // wxListCtrl::ScrollList
@@ -15626,7 +15663,7 @@ case wxListCtrl_ScrollList: { // wxListCtrl::ScrollList
int * dx = (int *) bp; bp += 4;
int * dy = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->ScrollList((int) *dx,(int) *dy);
+ bool Result = This->ScrollList(*dx,*dy);
rt.addBool(Result);
break;
}
@@ -15647,7 +15684,7 @@ case wxListCtrl_SetColumn: { // wxListCtrl::SetColumn
int * col = (int *) bp; bp += 4;
wxListItem *item = (wxListItem *) getPtr(bp,memenv); bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->SetColumn((int) *col,*item);
+ bool Result = This->SetColumn(*col,*item);
rt.addBool(Result);
break;
}
@@ -15656,7 +15693,7 @@ case wxListCtrl_SetColumnWidth: { // wxListCtrl::SetColumnWidth
int * col = (int *) bp; bp += 4;
int * width = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->SetColumnWidth((int) *col,(int) *width);
+ bool Result = This->SetColumnWidth(*col,*width);
rt.addBool(Result);
break;
}
@@ -15665,7 +15702,7 @@ case wxListCtrl_SetImageList: { // wxListCtrl::SetImageList
wxImageList *imageList = (wxImageList *) getPtr(bp,memenv); bp += 4;
int * which = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetImageList(imageList,(int) *which);
+ This->SetImageList(imageList,*which);
break;
}
case wxListCtrl_SetItem_1: { // wxListCtrl::SetItem
@@ -15690,7 +15727,7 @@ case wxListCtrl_SetItem_4: { // wxListCtrl::SetItem
} break;
}};
if(!This) throw wxe_badarg(0);
- long Result = This->SetItem((long) *index,(int) *col,label,imageId);
+ long Result = This->SetItem(*index,*col,label,imageId);
rt.addInt(Result);
break;
}
@@ -15703,14 +15740,14 @@ case wxListCtrl_SetItemBackgroundColour: { // wxListCtrl::SetItemBackgroundColou
int * colA = (int *) bp; bp += 4;
wxColour col = wxColour(*colR,*colG,*colB,*colA);
if(!This) throw wxe_badarg(0);
- This->SetItemBackgroundColour((long) *item,col);
+ This->SetItemBackgroundColour(*item,col);
break;
}
case wxListCtrl_SetItemCount: { // wxListCtrl::SetItemCount
wxListCtrl *This = (wxListCtrl *) getPtr(bp,memenv); bp += 4;
int * count = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetItemCount((long) *count);
+ This->SetItemCount(*count);
break;
}
case wxListCtrl_SetItemData: { // wxListCtrl::SetItemData
@@ -15718,7 +15755,7 @@ case wxListCtrl_SetItemData: { // wxListCtrl::SetItemData
int * item = (int *) bp; bp += 4;
int * data = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->SetItemData((long) *item,(long) *data);
+ bool Result = This->SetItemData(*item,*data);
rt.addBool(Result);
break;
}
@@ -15727,7 +15764,7 @@ case wxListCtrl_SetItemFont: { // wxListCtrl::SetItemFont
int * item = (int *) bp; bp += 4;
wxFont *f = (wxFont *) getPtr(bp,memenv); bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetItemFont((long) *item,*f);
+ This->SetItemFont(*item,*f);
break;
}
case wxListCtrl_SetItemImage: { // wxListCtrl::SetItemImage
@@ -15742,7 +15779,7 @@ case wxListCtrl_SetItemImage: { // wxListCtrl::SetItemImage
} break;
}};
if(!This) throw wxe_badarg(0);
- bool Result = This->SetItemImage((long) *item,(int) *image,selImage);
+ bool Result = This->SetItemImage(*item,*image,selImage);
rt.addBool(Result);
break;
}
@@ -15752,7 +15789,7 @@ case wxListCtrl_SetItemColumnImage: { // wxListCtrl::SetItemColumnImage
int * column = (int *) bp; bp += 4;
int * image = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->SetItemColumnImage((long) *item,(long) *column,(int) *image);
+ bool Result = This->SetItemColumnImage(*item,*column,*image);
rt.addBool(Result);
break;
}
@@ -15763,7 +15800,7 @@ case wxListCtrl_SetItemPosition: { // wxListCtrl::SetItemPosition
int * posY = (int *) bp; bp += 4;
wxPoint pos = wxPoint(*posX,*posY);
if(!This) throw wxe_badarg(0);
- bool Result = This->SetItemPosition((long) *item,pos);
+ bool Result = This->SetItemPosition(*item,pos);
rt.addBool(Result);
break;
}
@@ -15773,7 +15810,7 @@ case wxListCtrl_SetItemState: { // wxListCtrl::SetItemState
int * state = (int *) bp; bp += 4;
int * stateMask = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->SetItemState((long) *item,(long) *state,(long) *stateMask);
+ bool Result = This->SetItemState(*item,*state,*stateMask);
rt.addBool(Result);
break;
}
@@ -15784,7 +15821,7 @@ case wxListCtrl_SetItemText: { // wxListCtrl::SetItemText
wxString str = wxString(bp, wxConvUTF8);
bp += *strLen+((8-((4+ *strLen) & 7)) & 7);
if(!This) throw wxe_badarg(0);
- This->SetItemText((long) *item,str);
+ This->SetItemText(*item,str);
break;
}
case wxListCtrl_SetItemTextColour: { // wxListCtrl::SetItemTextColour
@@ -15796,7 +15833,7 @@ case wxListCtrl_SetItemTextColour: { // wxListCtrl::SetItemTextColour
int * colA = (int *) bp; bp += 4;
wxColour col = wxColour(*colR,*colG,*colB,*colA);
if(!This) throw wxe_badarg(0);
- This->SetItemTextColour((long) *item,col);
+ This->SetItemTextColour(*item,col);
break;
}
case wxListCtrl_SetSingleStyle: { // wxListCtrl::SetSingleStyle
@@ -15809,7 +15846,7 @@ case wxListCtrl_SetSingleStyle: { // wxListCtrl::SetSingleStyle
} break;
}};
if(!This) throw wxe_badarg(0);
- This->SetSingleStyle((long) *style,add);
+ This->SetSingleStyle(*style,add);
break;
}
case wxListCtrl_SetTextColour: { // wxListCtrl::SetTextColour
@@ -15827,7 +15864,7 @@ case wxListCtrl_SetWindowStyleFlag: { // wxListCtrl::SetWindowStyleFlag
wxListCtrl *This = (wxListCtrl *) getPtr(bp,memenv); bp += 4;
int * style = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetWindowStyleFlag((long) *style);
+ This->SetWindowStyleFlag(*style);
break;
}
@@ -15857,14 +15894,14 @@ case wxListView_ClearColumnImage: { // wxListView::ClearColumnImage
wxListView *This = (wxListView *) getPtr(bp,memenv); bp += 4;
int * col = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->ClearColumnImage((int) *col);
+ This->ClearColumnImage(*col);
break;
}
case wxListView_Focus: { // wxListView::Focus
wxListView *This = (wxListView *) getPtr(bp,memenv); bp += 4;
int * index = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->Focus((long) *index);
+ This->Focus(*index);
break;
}
case wxListView_GetFirstSelected: { // wxListView::GetFirstSelected
@@ -15885,7 +15922,7 @@ case wxListView_GetNextSelected: { // wxListView::GetNextSelected
wxListView *This = (wxListView *) getPtr(bp,memenv); bp += 4;
int * item = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- long Result = This->GetNextSelected((long) *item);
+ long Result = This->GetNextSelected(*item);
rt.addInt(Result);
break;
}
@@ -15893,7 +15930,7 @@ case wxListView_IsSelected: { // wxListView::IsSelected
wxListView *This = (wxListView *) getPtr(bp,memenv); bp += 4;
int * index = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->IsSelected((long) *index);
+ bool Result = This->IsSelected(*index);
rt.addBool(Result);
break;
}
@@ -15907,7 +15944,7 @@ case wxListView_Select: { // wxListView::Select
} break;
}};
if(!This) throw wxe_badarg(0);
- This->Select((long) *n,on);
+ This->Select(*n,on);
break;
}
case wxListView_SetColumnImage: { // wxListView::SetColumnImage
@@ -15915,7 +15952,7 @@ case wxListView_SetColumnImage: { // wxListView::SetColumnImage
int * col = (int *) bp; bp += 4;
int * image = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetColumnImage((int) *col,(int) *image);
+ This->SetColumnImage(*col,*image);
break;
}
case wxListItem_new_0: { // wxListItem::wxListItem
@@ -16018,7 +16055,7 @@ case wxListItem_SetAlign: { // wxListItem::SetAlign
wxListItem *This = (wxListItem *) getPtr(bp,memenv); bp += 4;
wxListColumnFormat align = *(wxListColumnFormat *) bp; bp += 4;;
if(!This) throw wxe_badarg(0);
- This->SetAlign((wxListColumnFormat) align);
+ This->SetAlign(align);
break;
}
case wxListItem_SetBackgroundColour: { // wxListItem::SetBackgroundColour
@@ -16036,7 +16073,7 @@ case wxListItem_SetColumn: { // wxListItem::SetColumn
wxListItem *This = (wxListItem *) getPtr(bp,memenv); bp += 4;
int * col = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetColumn((int) *col);
+ This->SetColumn(*col);
break;
}
case wxListItem_SetFont: { // wxListItem::SetFont
@@ -16050,35 +16087,35 @@ case wxListItem_SetId: { // wxListItem::SetId
wxListItem *This = (wxListItem *) getPtr(bp,memenv); bp += 4;
int * id = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetId((long) *id);
+ This->SetId(*id);
break;
}
case wxListItem_SetImage: { // wxListItem::SetImage
wxListItem *This = (wxListItem *) getPtr(bp,memenv); bp += 4;
int * image = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetImage((int) *image);
+ This->SetImage(*image);
break;
}
case wxListItem_SetMask: { // wxListItem::SetMask
wxListItem *This = (wxListItem *) getPtr(bp,memenv); bp += 4;
int * mask = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetMask((long) *mask);
+ This->SetMask(*mask);
break;
}
case wxListItem_SetState: { // wxListItem::SetState
wxListItem *This = (wxListItem *) getPtr(bp,memenv); bp += 4;
int * state = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetState((long) *state);
+ This->SetState(*state);
break;
}
case wxListItem_SetStateMask: { // wxListItem::SetStateMask
wxListItem *This = (wxListItem *) getPtr(bp,memenv); bp += 4;
int * stateMask = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetStateMask((long) *stateMask);
+ This->SetStateMask(*stateMask);
break;
}
case wxListItem_SetText: { // wxListItem::SetText
@@ -16105,7 +16142,7 @@ case wxListItem_SetWidth: { // wxListItem::SetWidth
wxListItem *This = (wxListItem *) getPtr(bp,memenv); bp += 4;
int * width = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetWidth((int) *width);
+ This->SetWidth(*width);
break;
}
case wxListItemAttr_new_0: { // wxListItemAttr::wxListItemAttr
@@ -16227,7 +16264,7 @@ case wxImageList_new_3: { // wxImageList::wxImageList
initialCount = (int)*(int *) bp; bp += 4;
} break;
}};
- wxImageList * Result = new EwxImageList((int) *width,(int) *height,mask,initialCount);
+ wxImageList * Result = new EwxImageList(*width,*height,mask,initialCount);
newPtr((void *) Result, 1, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxImageList");
break;
@@ -16278,7 +16315,7 @@ case wxImageList_Create: { // wxImageList::Create
} break;
}};
if(!This) throw wxe_badarg(0);
- bool Result = This->Create((int) *width,(int) *height,mask,initialCount);
+ bool Result = This->Create(*width,*height,mask,initialCount);
rt.addBool(Result);
break;
}
@@ -16300,7 +16337,7 @@ case wxImageList_Draw: { // wxImageList::Draw
} break;
}};
if(!This) throw wxe_badarg(0);
- bool Result = This->Draw((int) *index,*dc,(int) *x,(int) *y,flags,solidBackground);
+ bool Result = This->Draw(*index,*dc,*x,*y,flags,solidBackground);
rt.addBool(Result);
break;
}
@@ -16308,7 +16345,7 @@ case wxImageList_GetBitmap: { // wxImageList::GetBitmap
wxImageList *This = (wxImageList *) getPtr(bp,memenv); bp += 4;
int * index = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxBitmap * Result = new wxBitmap(This->GetBitmap((int) *index)); newPtr((void *) Result,3, memenv);;
+ wxBitmap * Result = new wxBitmap(This->GetBitmap(*index)); newPtr((void *) Result,3, memenv);;
rt.addRef(getRef((void *)Result,memenv), "wxBitmap");
break;
}
@@ -16316,7 +16353,7 @@ case wxImageList_GetIcon: { // wxImageList::GetIcon
wxImageList *This = (wxImageList *) getPtr(bp,memenv); bp += 4;
int * index = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxIcon * Result = new wxIcon(This->GetIcon((int) *index)); newPtr((void *) Result,3, memenv);;
+ wxIcon * Result = new wxIcon(This->GetIcon(*index)); newPtr((void *) Result,3, memenv);;
rt.addRef(getRef((void *)Result,memenv), "wxIcon");
break;
}
@@ -16333,7 +16370,7 @@ case wxImageList_GetSize: { // wxImageList::GetSize
wxImageList *This = (wxImageList *) getPtr(bp,memenv); bp += 4;
int * index = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->GetSize((int) *index,width,height);
+ bool Result = This->GetSize(*index,width,height);
rt.addBool(Result);
rt.addInt(width);
rt.addInt(height);
@@ -16344,7 +16381,7 @@ case wxImageList_Remove: { // wxImageList::Remove
wxImageList *This = (wxImageList *) getPtr(bp,memenv); bp += 4;
int * index = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->Remove((int) *index);
+ bool Result = This->Remove(*index);
rt.addBool(Result);
break;
}
@@ -16360,7 +16397,7 @@ case wxImageList_Replace_2: { // wxImageList::Replace
int * index = (int *) bp; bp += 4;
wxBitmap *bitmap = (wxBitmap *) getPtr(bp,memenv); bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->Replace((int) *index,*bitmap);
+ bool Result = This->Replace(*index,*bitmap);
rt.addBool(Result);
break;
}
@@ -16370,7 +16407,7 @@ case wxImageList_Replace_3: { // wxImageList::Replace
wxBitmap *bitmap = (wxBitmap *) getPtr(bp,memenv); bp += 4;
wxBitmap *mask = (wxBitmap *) getPtr(bp,memenv); bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->Replace((int) *index,*bitmap,*mask);
+ bool Result = This->Replace(*index,*bitmap,*mask);
rt.addBool(Result);
break;
}
@@ -16405,7 +16442,7 @@ font = (wxFont *) getPtr(bp,memenv); bp += 4;
alignment = *(wxTextAttrAlignment *) bp; bp += 4;;
} break;
}};
- wxTextAttr * Result = new wxTextAttr(colText,colBack,*font,(wxTextAttrAlignment) alignment);
+ wxTextAttr * Result = new wxTextAttr(colText,colBack,*font,alignment);
newPtr((void *) Result, 103, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxTextAttr");
break;
@@ -16427,7 +16464,7 @@ case wxTextAttr_GetBackgroundColour: { // wxTextAttr::GetBackgroundColour
case wxTextAttr_GetFont: { // wxTextAttr::GetFont
wxTextAttr *This = (wxTextAttr *) getPtr(bp,memenv); bp += 4;
if(!This) throw wxe_badarg(0);
- const wxFont * Result = &This->GetFont();
+ const wxFont * Result = new wxFont(This->GetFont()); newPtr((void *) Result,3, memenv);;
rt.addRef(getRef((void *)Result,memenv), "wxFont");
break;
}
@@ -16505,7 +16542,7 @@ case wxTextAttr_SetAlignment: { // wxTextAttr::SetAlignment
wxTextAttr *This = (wxTextAttr *) getPtr(bp,memenv); bp += 4;
wxTextAttrAlignment alignment = *(wxTextAttrAlignment *) bp; bp += 4;;
if(!This) throw wxe_badarg(0);
- This->SetAlignment((wxTextAttrAlignment) alignment);
+ This->SetAlignment(alignment);
break;
}
case wxTextAttr_SetBackgroundColour: { // wxTextAttr::SetBackgroundColour
@@ -16523,7 +16560,7 @@ case wxTextAttr_SetFlags: { // wxTextAttr::SetFlags
wxTextAttr *This = (wxTextAttr *) getPtr(bp,memenv); bp += 4;
int * flags = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetFlags((long) *flags);
+ This->SetFlags(*flags);
break;
}
case wxTextAttr_SetFont: { // wxTextAttr::SetFont
@@ -16549,14 +16586,14 @@ case wxTextAttr_SetLeftIndent: { // wxTextAttr::SetLeftIndent
} break;
}};
if(!This) throw wxe_badarg(0);
- This->SetLeftIndent((int) *indent,subIndent);
+ This->SetLeftIndent(*indent,subIndent);
break;
}
case wxTextAttr_SetRightIndent: { // wxTextAttr::SetRightIndent
wxTextAttr *This = (wxTextAttr *) getPtr(bp,memenv); bp += 4;
int * indent = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetRightIndent((int) *indent);
+ This->SetRightIndent(*indent);
break;
}
case wxTextAttr_SetTabs: { // wxTextAttr::SetTabs
@@ -16619,7 +16656,7 @@ case wxTextCtrl_new_3: { // wxTextCtrl::wxTextCtrl
validator = (wxValidator *) getPtr(bp,memenv); bp += 4;
} break;
}};
- wxTextCtrl * Result = new EwxTextCtrl(parent,(wxWindowID) *id,value,pos,size,style,*validator);
+ wxTextCtrl * Result = new EwxTextCtrl(parent,*id,value,pos,size,style,*validator);
newPtr((void *) Result, 0, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxTextCtrl");
break;
@@ -16722,7 +16759,7 @@ validator = (wxValidator *) getPtr(bp,memenv); bp += 4;
} break;
}};
if(!This) throw wxe_badarg(0);
- bool Result = This->Create(parent,(wxWindowID) *id,value,pos,size,style,*validator);
+ bool Result = This->Create(parent,*id,value,pos,size,style,*validator);
rt.addBool(Result);
break;
}
@@ -16771,7 +16808,7 @@ case wxTextCtrl_GetLineLength: { // wxTextCtrl::GetLineLength
wxTextCtrl *This = (wxTextCtrl *) getPtr(bp,memenv); bp += 4;
int * lineNo = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->GetLineLength((long) *lineNo);
+ int Result = This->GetLineLength(*lineNo);
rt.addInt(Result);
break;
}
@@ -16779,7 +16816,7 @@ case wxTextCtrl_GetLineText: { // wxTextCtrl::GetLineText
wxTextCtrl *This = (wxTextCtrl *) getPtr(bp,memenv); bp += 4;
int * lineNo = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxString Result = This->GetLineText((long) *lineNo);
+ wxString Result = This->GetLineText(*lineNo);
rt.add(Result);
break;
}
@@ -16795,7 +16832,7 @@ case wxTextCtrl_GetRange: { // wxTextCtrl::GetRange
int * from = (int *) bp; bp += 4;
int * to = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxString Result = This->GetRange((long) *from,(long) *to);
+ wxString Result = This->GetRange(*from,*to);
rt.add(Result);
break;
}
@@ -16822,7 +16859,7 @@ case wxTextCtrl_GetStyle: { // wxTextCtrl::GetStyle
int * position = (int *) bp; bp += 4;
wxTextAttr *style = (wxTextAttr *) getPtr(bp,memenv); bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->GetStyle((long) *position,*style);
+ bool Result = This->GetStyle(*position,*style);
rt.addBool(Result);
break;
}
@@ -16895,7 +16932,7 @@ case wxTextCtrl_PositionToXY: { // wxTextCtrl::PositionToXY
wxTextCtrl *This = (wxTextCtrl *) getPtr(bp,memenv); bp += 4;
int * pos = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->PositionToXY((long) *pos,&x,&y);
+ bool Result = This->PositionToXY(*pos,&x,&y);
rt.addBool(Result);
rt.addInt(x);
rt.addInt(y);
@@ -16913,7 +16950,7 @@ case wxTextCtrl_Remove: { // wxTextCtrl::Remove
int * from = (int *) bp; bp += 4;
int * to = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->Remove((long) *from,(long) *to);
+ This->Remove(*from,*to);
break;
}
case wxTextCtrl_Replace: { // wxTextCtrl::Replace
@@ -16924,7 +16961,7 @@ case wxTextCtrl_Replace: { // wxTextCtrl::Replace
wxString value = wxString(bp, wxConvUTF8);
bp += *valueLen+((8-((0+ *valueLen) & 7)) & 7);
if(!This) throw wxe_badarg(0);
- This->Replace((long) *from,(long) *to,value);
+ This->Replace(*from,*to,value);
break;
}
case wxTextCtrl_SaveFile: { // wxTextCtrl::SaveFile
@@ -16959,14 +16996,14 @@ case wxTextCtrl_SetEditable: { // wxTextCtrl::SetEditable
wxTextCtrl *This = (wxTextCtrl *) getPtr(bp,memenv); bp += 4;
bool * editable = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetEditable((bool) *editable);
+ This->SetEditable(*editable);
break;
}
case wxTextCtrl_SetInsertionPoint: { // wxTextCtrl::SetInsertionPoint
wxTextCtrl *This = (wxTextCtrl *) getPtr(bp,memenv); bp += 4;
int * pos = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetInsertionPoint((long) *pos);
+ This->SetInsertionPoint(*pos);
break;
}
case wxTextCtrl_SetInsertionPointEnd: { // wxTextCtrl::SetInsertionPointEnd
@@ -16979,7 +17016,7 @@ case wxTextCtrl_SetMaxLength: { // wxTextCtrl::SetMaxLength
wxTextCtrl *This = (wxTextCtrl *) getPtr(bp,memenv); bp += 4;
unsigned int * len = (unsigned int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetMaxLength((long) *len);
+ This->SetMaxLength(*len);
break;
}
case wxTextCtrl_SetSelection: { // wxTextCtrl::SetSelection
@@ -16987,7 +17024,7 @@ case wxTextCtrl_SetSelection: { // wxTextCtrl::SetSelection
int * from = (int *) bp; bp += 4;
int * to = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetSelection((long) *from,(long) *to);
+ This->SetSelection(*from,*to);
break;
}
case wxTextCtrl_SetStyle: { // wxTextCtrl::SetStyle
@@ -16996,7 +17033,7 @@ case wxTextCtrl_SetStyle: { // wxTextCtrl::SetStyle
int * end = (int *) bp; bp += 4;
wxTextAttr *style = (wxTextAttr *) getPtr(bp,memenv); bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->SetStyle((long) *start,(long) *end,*style);
+ bool Result = This->SetStyle(*start,*end,*style);
rt.addBool(Result);
break;
}
@@ -17013,7 +17050,7 @@ case wxTextCtrl_ShowPosition: { // wxTextCtrl::ShowPosition
wxTextCtrl *This = (wxTextCtrl *) getPtr(bp,memenv); bp += 4;
int * pos = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->ShowPosition((long) *pos);
+ This->ShowPosition(*pos);
break;
}
case wxTextCtrl_Undo: { // wxTextCtrl::Undo
@@ -17036,7 +17073,7 @@ case wxTextCtrl_XYToPosition: { // wxTextCtrl::XYToPosition
int * x = (int *) bp; bp += 4;
int * y = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- long Result = This->XYToPosition((long) *x,(long) *y);
+ long Result = This->XYToPosition(*x,*y);
rt.addInt(Result);
break;
}
@@ -17069,7 +17106,7 @@ case wxNotebook_new_3: { // wxNotebook::wxNotebook
style = (long)*(int *) bp; bp += 4;
} break;
}};
- wxNotebook * Result = new EwxNotebook(parent,(wxWindowID) *winid,pos,size,style);
+ wxNotebook * Result = new EwxNotebook(parent,*winid,pos,size,style);
newPtr((void *) Result, 0, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxNotebook");
break;
@@ -17141,7 +17178,7 @@ case wxNotebook_Create: { // wxNotebook::Create
} break;
}};
if(!This) throw wxe_badarg(0);
- bool Result = This->Create(parent,(wxWindowID) *id,pos,size,style);
+ bool Result = This->Create(parent,*id,pos,size,style);
rt.addBool(Result);
break;
}
@@ -17156,7 +17193,7 @@ case wxNotebook_DeletePage: { // wxNotebook::DeletePage
wxNotebook *This = (wxNotebook *) getPtr(bp,memenv); bp += 4;
int * nPage = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->DeletePage((size_t) *nPage);
+ bool Result = This->DeletePage(*nPage);
rt.addBool(Result);
break;
}
@@ -17164,7 +17201,7 @@ case wxNotebook_RemovePage: { // wxNotebook::RemovePage
wxNotebook *This = (wxNotebook *) getPtr(bp,memenv); bp += 4;
int * nPage = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->RemovePage((size_t) *nPage);
+ bool Result = This->RemovePage(*nPage);
rt.addBool(Result);
break;
}
@@ -17186,7 +17223,7 @@ case wxNotebook_GetPage: { // wxNotebook::GetPage
wxNotebook *This = (wxNotebook *) getPtr(bp,memenv); bp += 4;
int * n = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxWindow * Result = (wxWindow*)This->GetPage((size_t) *n);
+ wxWindow * Result = (wxWindow*)This->GetPage(*n);
rt.addRef(getRef((void *)Result,memenv), "wxWindow");
break;
}
@@ -17201,7 +17238,7 @@ case wxNotebook_GetPageImage: { // wxNotebook::GetPageImage
wxNotebook *This = (wxNotebook *) getPtr(bp,memenv); bp += 4;
int * nPage = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->GetPageImage((size_t) *nPage);
+ int Result = This->GetPageImage(*nPage);
rt.addInt(Result);
break;
}
@@ -17209,7 +17246,7 @@ case wxNotebook_GetPageText: { // wxNotebook::GetPageText
wxNotebook *This = (wxNotebook *) getPtr(bp,memenv); bp += 4;
int * nPage = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxString Result = This->GetPageText((size_t) *nPage);
+ wxString Result = This->GetPageText(*nPage);
rt.add(Result);
break;
}
@@ -17265,7 +17302,7 @@ case wxNotebook_InsertPage: { // wxNotebook::InsertPage
} break;
}};
if(!This) throw wxe_badarg(0);
- bool Result = This->InsertPage((size_t) *position,win,strText,bSelect,imageId);
+ bool Result = This->InsertPage(*position,win,strText,bSelect,imageId);
rt.addBool(Result);
break;
}
@@ -17299,7 +17336,7 @@ case wxNotebook_SetPageImage: { // wxNotebook::SetPageImage
int * nPage = (int *) bp; bp += 4;
int * nImage = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->SetPageImage((size_t) *nPage,(int) *nImage);
+ bool Result = This->SetPageImage(*nPage,*nImage);
rt.addBool(Result);
break;
}
@@ -17310,7 +17347,7 @@ case wxNotebook_SetPageText: { // wxNotebook::SetPageText
wxString strText = wxString(bp, wxConvUTF8);
bp += *strTextLen+((8-((4+ *strTextLen) & 7)) & 7);
if(!This) throw wxe_badarg(0);
- bool Result = This->SetPageText((size_t) *nPage,strText);
+ bool Result = This->SetPageText(*nPage,strText);
rt.addBool(Result);
break;
}
@@ -17318,7 +17355,7 @@ case wxNotebook_SetSelection: { // wxNotebook::SetSelection
wxNotebook *This = (wxNotebook *) getPtr(bp,memenv); bp += 4;
int * nPage = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->SetSelection((size_t) *nPage);
+ int Result = This->SetSelection(*nPage);
rt.addInt(Result);
break;
}
@@ -17326,7 +17363,7 @@ case wxNotebook_ChangeSelection: { // wxNotebook::ChangeSelection
wxNotebook *This = (wxNotebook *) getPtr(bp,memenv); bp += 4;
int * nPage = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->ChangeSelection((size_t) *nPage);
+ int Result = This->ChangeSelection(*nPage);
rt.addInt(Result);
break;
}
@@ -17359,7 +17396,7 @@ case wxChoicebook_new_3: { // wxChoicebook::wxChoicebook
style = (long)*(int *) bp; bp += 4;
} break;
}};
- wxChoicebook * Result = new EwxChoicebook(parent,(wxWindowID) *id,pos,size,style);
+ wxChoicebook * Result = new EwxChoicebook(parent,*id,pos,size,style);
newPtr((void *) Result, 0, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxChoicebook");
break;
@@ -17431,7 +17468,7 @@ case wxChoicebook_Create: { // wxChoicebook::Create
} break;
}};
if(!This) throw wxe_badarg(0);
- bool Result = This->Create(parent,(wxWindowID) *id,pos,size,style);
+ bool Result = This->Create(parent,*id,pos,size,style);
rt.addBool(Result);
break;
}
@@ -17446,7 +17483,7 @@ case wxChoicebook_DeletePage: { // wxChoicebook::DeletePage
wxChoicebook *This = (wxChoicebook *) getPtr(bp,memenv); bp += 4;
int * n = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->DeletePage((size_t) *n);
+ bool Result = This->DeletePage(*n);
rt.addBool(Result);
break;
}
@@ -17454,7 +17491,7 @@ case wxChoicebook_RemovePage: { // wxChoicebook::RemovePage
wxChoicebook *This = (wxChoicebook *) getPtr(bp,memenv); bp += 4;
int * n = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->RemovePage((size_t) *n);
+ bool Result = This->RemovePage(*n);
rt.addBool(Result);
break;
}
@@ -17476,7 +17513,7 @@ case wxChoicebook_GetPage: { // wxChoicebook::GetPage
wxChoicebook *This = (wxChoicebook *) getPtr(bp,memenv); bp += 4;
int * n = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxWindow * Result = (wxWindow*)This->GetPage((size_t) *n);
+ wxWindow * Result = (wxWindow*)This->GetPage(*n);
rt.addRef(getRef((void *)Result,memenv), "wxWindow");
break;
}
@@ -17491,7 +17528,7 @@ case wxChoicebook_GetPageImage: { // wxChoicebook::GetPageImage
wxChoicebook *This = (wxChoicebook *) getPtr(bp,memenv); bp += 4;
int * n = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->GetPageImage((size_t) *n);
+ int Result = This->GetPageImage(*n);
rt.addInt(Result);
break;
}
@@ -17499,7 +17536,7 @@ case wxChoicebook_GetPageText: { // wxChoicebook::GetPageText
wxChoicebook *This = (wxChoicebook *) getPtr(bp,memenv); bp += 4;
int * n = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxString Result = This->GetPageText((size_t) *n);
+ wxString Result = This->GetPageText(*n);
rt.add(Result);
break;
}
@@ -17541,7 +17578,7 @@ case wxChoicebook_InsertPage: { // wxChoicebook::InsertPage
} break;
}};
if(!This) throw wxe_badarg(0);
- bool Result = This->InsertPage((size_t) *n,page,text,bSelect,imageId);
+ bool Result = This->InsertPage(*n,page,text,bSelect,imageId);
rt.addBool(Result);
break;
}
@@ -17566,7 +17603,7 @@ case wxChoicebook_SetPageImage: { // wxChoicebook::SetPageImage
int * n = (int *) bp; bp += 4;
int * imageId = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->SetPageImage((size_t) *n,(int) *imageId);
+ bool Result = This->SetPageImage(*n,*imageId);
rt.addBool(Result);
break;
}
@@ -17577,7 +17614,7 @@ case wxChoicebook_SetPageText: { // wxChoicebook::SetPageText
wxString strText = wxString(bp, wxConvUTF8);
bp += *strTextLen+((8-((4+ *strTextLen) & 7)) & 7);
if(!This) throw wxe_badarg(0);
- bool Result = This->SetPageText((size_t) *n,strText);
+ bool Result = This->SetPageText(*n,strText);
rt.addBool(Result);
break;
}
@@ -17585,7 +17622,7 @@ case wxChoicebook_SetSelection: { // wxChoicebook::SetSelection
wxChoicebook *This = (wxChoicebook *) getPtr(bp,memenv); bp += 4;
int * n = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->SetSelection((size_t) *n);
+ int Result = This->SetSelection(*n);
rt.addInt(Result);
break;
}
@@ -17593,7 +17630,7 @@ case wxChoicebook_ChangeSelection: { // wxChoicebook::ChangeSelection
wxChoicebook *This = (wxChoicebook *) getPtr(bp,memenv); bp += 4;
int * n = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->ChangeSelection((size_t) *n);
+ int Result = This->ChangeSelection(*n);
rt.addInt(Result);
break;
}
@@ -17626,7 +17663,7 @@ case wxToolbook_new_3: { // wxToolbook::wxToolbook
style = (long)*(int *) bp; bp += 4;
} break;
}};
- wxToolbook * Result = new EwxToolbook(parent,(wxWindowID) *id,pos,size,style);
+ wxToolbook * Result = new EwxToolbook(parent,*id,pos,size,style);
newPtr((void *) Result, 0, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxToolbook");
break;
@@ -17698,7 +17735,7 @@ case wxToolbook_Create: { // wxToolbook::Create
} break;
}};
if(!This) throw wxe_badarg(0);
- bool Result = This->Create(parent,(wxWindowID) *id,pos,size,style);
+ bool Result = This->Create(parent,*id,pos,size,style);
rt.addBool(Result);
break;
}
@@ -17713,7 +17750,7 @@ case wxToolbook_DeletePage: { // wxToolbook::DeletePage
wxToolbook *This = (wxToolbook *) getPtr(bp,memenv); bp += 4;
int * n = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->DeletePage((size_t) *n);
+ bool Result = This->DeletePage(*n);
rt.addBool(Result);
break;
}
@@ -17721,7 +17758,7 @@ case wxToolbook_RemovePage: { // wxToolbook::RemovePage
wxToolbook *This = (wxToolbook *) getPtr(bp,memenv); bp += 4;
int * n = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->RemovePage((size_t) *n);
+ bool Result = This->RemovePage(*n);
rt.addBool(Result);
break;
}
@@ -17743,7 +17780,7 @@ case wxToolbook_GetPage: { // wxToolbook::GetPage
wxToolbook *This = (wxToolbook *) getPtr(bp,memenv); bp += 4;
int * n = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxWindow * Result = (wxWindow*)This->GetPage((size_t) *n);
+ wxWindow * Result = (wxWindow*)This->GetPage(*n);
rt.addRef(getRef((void *)Result,memenv), "wxWindow");
break;
}
@@ -17758,7 +17795,7 @@ case wxToolbook_GetPageImage: { // wxToolbook::GetPageImage
wxToolbook *This = (wxToolbook *) getPtr(bp,memenv); bp += 4;
int * n = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->GetPageImage((size_t) *n);
+ int Result = This->GetPageImage(*n);
rt.addInt(Result);
break;
}
@@ -17766,7 +17803,7 @@ case wxToolbook_GetPageText: { // wxToolbook::GetPageText
wxToolbook *This = (wxToolbook *) getPtr(bp,memenv); bp += 4;
int * n = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxString Result = This->GetPageText((size_t) *n);
+ wxString Result = This->GetPageText(*n);
rt.add(Result);
break;
}
@@ -17808,7 +17845,7 @@ case wxToolbook_InsertPage: { // wxToolbook::InsertPage
} break;
}};
if(!This) throw wxe_badarg(0);
- bool Result = This->InsertPage((size_t) *n,page,text,bSelect,imageId);
+ bool Result = This->InsertPage(*n,page,text,bSelect,imageId);
rt.addBool(Result);
break;
}
@@ -17833,7 +17870,7 @@ case wxToolbook_SetPageImage: { // wxToolbook::SetPageImage
int * n = (int *) bp; bp += 4;
int * imageId = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->SetPageImage((size_t) *n,(int) *imageId);
+ bool Result = This->SetPageImage(*n,*imageId);
rt.addBool(Result);
break;
}
@@ -17844,7 +17881,7 @@ case wxToolbook_SetPageText: { // wxToolbook::SetPageText
wxString strText = wxString(bp, wxConvUTF8);
bp += *strTextLen+((8-((4+ *strTextLen) & 7)) & 7);
if(!This) throw wxe_badarg(0);
- bool Result = This->SetPageText((size_t) *n,strText);
+ bool Result = This->SetPageText(*n,strText);
rt.addBool(Result);
break;
}
@@ -17852,7 +17889,7 @@ case wxToolbook_SetSelection: { // wxToolbook::SetSelection
wxToolbook *This = (wxToolbook *) getPtr(bp,memenv); bp += 4;
int * n = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->SetSelection((size_t) *n);
+ int Result = This->SetSelection(*n);
rt.addInt(Result);
break;
}
@@ -17860,7 +17897,7 @@ case wxToolbook_ChangeSelection: { // wxToolbook::ChangeSelection
wxToolbook *This = (wxToolbook *) getPtr(bp,memenv); bp += 4;
int * n = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->ChangeSelection((size_t) *n);
+ int Result = This->ChangeSelection(*n);
rt.addInt(Result);
break;
}
@@ -17893,7 +17930,7 @@ case wxListbook_new_3: { // wxListbook::wxListbook
style = (long)*(int *) bp; bp += 4;
} break;
}};
- wxListbook * Result = new EwxListbook(parent,(wxWindowID) *id,pos,size,style);
+ wxListbook * Result = new EwxListbook(parent,*id,pos,size,style);
newPtr((void *) Result, 0, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxListbook");
break;
@@ -17965,7 +18002,7 @@ case wxListbook_Create: { // wxListbook::Create
} break;
}};
if(!This) throw wxe_badarg(0);
- bool Result = This->Create(parent,(wxWindowID) *id,pos,size,style);
+ bool Result = This->Create(parent,*id,pos,size,style);
rt.addBool(Result);
break;
}
@@ -17980,7 +18017,7 @@ case wxListbook_DeletePage: { // wxListbook::DeletePage
wxListbook *This = (wxListbook *) getPtr(bp,memenv); bp += 4;
int * n = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->DeletePage((size_t) *n);
+ bool Result = This->DeletePage(*n);
rt.addBool(Result);
break;
}
@@ -17988,7 +18025,7 @@ case wxListbook_RemovePage: { // wxListbook::RemovePage
wxListbook *This = (wxListbook *) getPtr(bp,memenv); bp += 4;
int * n = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->RemovePage((size_t) *n);
+ bool Result = This->RemovePage(*n);
rt.addBool(Result);
break;
}
@@ -18010,7 +18047,7 @@ case wxListbook_GetPage: { // wxListbook::GetPage
wxListbook *This = (wxListbook *) getPtr(bp,memenv); bp += 4;
int * n = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxWindow * Result = (wxWindow*)This->GetPage((size_t) *n);
+ wxWindow * Result = (wxWindow*)This->GetPage(*n);
rt.addRef(getRef((void *)Result,memenv), "wxWindow");
break;
}
@@ -18025,7 +18062,7 @@ case wxListbook_GetPageImage: { // wxListbook::GetPageImage
wxListbook *This = (wxListbook *) getPtr(bp,memenv); bp += 4;
int * n = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->GetPageImage((size_t) *n);
+ int Result = This->GetPageImage(*n);
rt.addInt(Result);
break;
}
@@ -18033,7 +18070,7 @@ case wxListbook_GetPageText: { // wxListbook::GetPageText
wxListbook *This = (wxListbook *) getPtr(bp,memenv); bp += 4;
int * n = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxString Result = This->GetPageText((size_t) *n);
+ wxString Result = This->GetPageText(*n);
rt.add(Result);
break;
}
@@ -18075,7 +18112,7 @@ case wxListbook_InsertPage: { // wxListbook::InsertPage
} break;
}};
if(!This) throw wxe_badarg(0);
- bool Result = This->InsertPage((size_t) *n,page,text,bSelect,imageId);
+ bool Result = This->InsertPage(*n,page,text,bSelect,imageId);
rt.addBool(Result);
break;
}
@@ -18100,7 +18137,7 @@ case wxListbook_SetPageImage: { // wxListbook::SetPageImage
int * n = (int *) bp; bp += 4;
int * imageId = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->SetPageImage((size_t) *n,(int) *imageId);
+ bool Result = This->SetPageImage(*n,*imageId);
rt.addBool(Result);
break;
}
@@ -18111,7 +18148,7 @@ case wxListbook_SetPageText: { // wxListbook::SetPageText
wxString strText = wxString(bp, wxConvUTF8);
bp += *strTextLen+((8-((4+ *strTextLen) & 7)) & 7);
if(!This) throw wxe_badarg(0);
- bool Result = This->SetPageText((size_t) *n,strText);
+ bool Result = This->SetPageText(*n,strText);
rt.addBool(Result);
break;
}
@@ -18119,7 +18156,7 @@ case wxListbook_SetSelection: { // wxListbook::SetSelection
wxListbook *This = (wxListbook *) getPtr(bp,memenv); bp += 4;
int * n = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->SetSelection((size_t) *n);
+ int Result = This->SetSelection(*n);
rt.addInt(Result);
break;
}
@@ -18127,7 +18164,7 @@ case wxListbook_ChangeSelection: { // wxListbook::ChangeSelection
wxListbook *This = (wxListbook *) getPtr(bp,memenv); bp += 4;
int * n = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->ChangeSelection((size_t) *n);
+ int Result = This->ChangeSelection(*n);
rt.addInt(Result);
break;
}
@@ -18160,7 +18197,7 @@ case wxTreebook_new_3: { // wxTreebook::wxTreebook
style = (long)*(int *) bp; bp += 4;
} break;
}};
- wxTreebook * Result = new EwxTreebook(parent,(wxWindowID) *id,pos,size,style);
+ wxTreebook * Result = new EwxTreebook(parent,*id,pos,size,style);
newPtr((void *) Result, 0, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxTreebook");
break;
@@ -18232,7 +18269,7 @@ case wxTreebook_Create: { // wxTreebook::Create
} break;
}};
if(!This) throw wxe_badarg(0);
- bool Result = This->Create(parent,(wxWindowID) *id,pos,size,style);
+ bool Result = This->Create(parent,*id,pos,size,style);
rt.addBool(Result);
break;
}
@@ -18247,7 +18284,7 @@ case wxTreebook_DeletePage: { // wxTreebook::DeletePage
wxTreebook *This = (wxTreebook *) getPtr(bp,memenv); bp += 4;
int * pos = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->DeletePage((size_t) *pos);
+ bool Result = This->DeletePage(*pos);
rt.addBool(Result);
break;
}
@@ -18255,7 +18292,7 @@ case wxTreebook_RemovePage: { // wxTreebook::RemovePage
wxTreebook *This = (wxTreebook *) getPtr(bp,memenv); bp += 4;
int * n = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->RemovePage((size_t) *n);
+ bool Result = This->RemovePage(*n);
rt.addBool(Result);
break;
}
@@ -18277,7 +18314,7 @@ case wxTreebook_GetPage: { // wxTreebook::GetPage
wxTreebook *This = (wxTreebook *) getPtr(bp,memenv); bp += 4;
int * n = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxWindow * Result = (wxWindow*)This->GetPage((size_t) *n);
+ wxWindow * Result = (wxWindow*)This->GetPage(*n);
rt.addRef(getRef((void *)Result,memenv), "wxWindow");
break;
}
@@ -18292,7 +18329,7 @@ case wxTreebook_GetPageImage: { // wxTreebook::GetPageImage
wxTreebook *This = (wxTreebook *) getPtr(bp,memenv); bp += 4;
int * n = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->GetPageImage((size_t) *n);
+ int Result = This->GetPageImage(*n);
rt.addInt(Result);
break;
}
@@ -18300,7 +18337,7 @@ case wxTreebook_GetPageText: { // wxTreebook::GetPageText
wxTreebook *This = (wxTreebook *) getPtr(bp,memenv); bp += 4;
int * n = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxString Result = This->GetPageText((size_t) *n);
+ wxString Result = This->GetPageText(*n);
rt.add(Result);
break;
}
@@ -18321,7 +18358,7 @@ case wxTreebook_ExpandNode: { // wxTreebook::ExpandNode
} break;
}};
if(!This) throw wxe_badarg(0);
- bool Result = This->ExpandNode((size_t) *pos,expand);
+ bool Result = This->ExpandNode(*pos,expand);
rt.addBool(Result);
break;
}
@@ -18329,7 +18366,7 @@ case wxTreebook_IsNodeExpanded: { // wxTreebook::IsNodeExpanded
wxTreebook *This = (wxTreebook *) getPtr(bp,memenv); bp += 4;
int * pos = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->IsNodeExpanded((size_t) *pos);
+ bool Result = This->IsNodeExpanded(*pos);
rt.addBool(Result);
break;
}
@@ -18364,7 +18401,7 @@ case wxTreebook_InsertPage: { // wxTreebook::InsertPage
} break;
}};
if(!This) throw wxe_badarg(0);
- bool Result = This->InsertPage((size_t) *pos,page,text,bSelect,imageId);
+ bool Result = This->InsertPage(*pos,page,text,bSelect,imageId);
rt.addBool(Result);
break;
}
@@ -18386,7 +18423,7 @@ case wxTreebook_InsertSubPage: { // wxTreebook::InsertSubPage
} break;
}};
if(!This) throw wxe_badarg(0);
- bool Result = This->InsertSubPage((size_t) *pos,page,text,bSelect,imageId);
+ bool Result = This->InsertSubPage(*pos,page,text,bSelect,imageId);
rt.addBool(Result);
break;
}
@@ -18411,7 +18448,7 @@ case wxTreebook_SetPageImage: { // wxTreebook::SetPageImage
int * n = (int *) bp; bp += 4;
int * imageId = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->SetPageImage((size_t) *n,(int) *imageId);
+ bool Result = This->SetPageImage(*n,*imageId);
rt.addBool(Result);
break;
}
@@ -18422,7 +18459,7 @@ case wxTreebook_SetPageText: { // wxTreebook::SetPageText
wxString strText = wxString(bp, wxConvUTF8);
bp += *strTextLen+((8-((4+ *strTextLen) & 7)) & 7);
if(!This) throw wxe_badarg(0);
- bool Result = This->SetPageText((size_t) *n,strText);
+ bool Result = This->SetPageText(*n,strText);
rt.addBool(Result);
break;
}
@@ -18430,7 +18467,7 @@ case wxTreebook_SetSelection: { // wxTreebook::SetSelection
wxTreebook *This = (wxTreebook *) getPtr(bp,memenv); bp += 4;
int * n = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->SetSelection((size_t) *n);
+ int Result = This->SetSelection(*n);
rt.addInt(Result);
break;
}
@@ -18438,7 +18475,7 @@ case wxTreebook_ChangeSelection: { // wxTreebook::ChangeSelection
wxTreebook *This = (wxTreebook *) getPtr(bp,memenv); bp += 4;
int * n = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->ChangeSelection((size_t) *n);
+ int Result = This->ChangeSelection(*n);
rt.addInt(Result);
break;
}
@@ -18790,7 +18827,7 @@ which = *(wxTreeItemIcon *) bp; bp += 4;;
} break;
}};
if(!This) throw wxe_badarg(0);
- int Result = This->GetItemImage(item,(wxTreeItemIcon) which);
+ int Result = This->GetItemImage(item,which);
rt.addInt(Result);
break;
}
@@ -18933,7 +18970,7 @@ case wxTreeCtrl_InsertItem: { // wxTreeCtrl::InsertItem
} break;
}};
if(!This) throw wxe_badarg(0);
- wxTreeItemId Result = This->InsertItem(parent,(size_t) *pos,text,image,selImage,data);
+ wxTreeItemId Result = This->InsertItem(parent,*pos,text,image,selImage,data);
rt.add((wxUIntPtr *) Result.m_pItem);
break;
}
@@ -19043,7 +19080,7 @@ case wxTreeCtrl_SetIndent: { // wxTreeCtrl::SetIndent
wxTreeCtrl *This = (wxTreeCtrl *) getPtr(bp,memenv); bp += 4;
unsigned int * indent = (unsigned int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetIndent((int) *indent);
+ This->SetIndent(*indent);
break;
}
case wxTreeCtrl_SetImageList: { // wxTreeCtrl::SetImageList
@@ -19132,7 +19169,7 @@ case wxTreeCtrl_SetItemImage_2: { // wxTreeCtrl::SetItemImage
wxTreeItemId item = wxTreeItemId((void *) *(wxUint64 *) bp); bp += 8;
int * image = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetItemImage(item,(int) *image);
+ This->SetItemImage(item,*image);
break;
}
case wxTreeCtrl_SetItemImage_3: { // wxTreeCtrl::SetItemImage
@@ -19148,7 +19185,7 @@ which = *(wxTreeItemIcon *) bp; bp += 4;;
} break;
}};
if(!This) throw wxe_badarg(0);
- This->SetItemImage(item,(int) *image,(wxTreeItemIcon) which);
+ This->SetItemImage(item,*image,which);
break;
}
case wxTreeCtrl_SetItemText: { // wxTreeCtrl::SetItemText
@@ -19186,7 +19223,7 @@ case wxTreeCtrl_SetWindowStyle: { // wxTreeCtrl::SetWindowStyle
wxTreeCtrl *This = (wxTreeCtrl *) getPtr(bp,memenv); bp += 4;
const int * styles = (const int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetWindowStyle((long) *styles);
+ This->SetWindowStyle(*styles);
break;
}
case wxTreeCtrl_SortChildren: { // wxTreeCtrl::SortChildren
@@ -19266,7 +19303,7 @@ case wxScrollBar_new_3: { // wxScrollBar::wxScrollBar
validator = (wxValidator *) getPtr(bp,memenv); bp += 4;
} break;
}};
- wxScrollBar * Result = new EwxScrollBar(parent,(wxWindowID) *id,pos,size,style,*validator);
+ wxScrollBar * Result = new EwxScrollBar(parent,*id,pos,size,style,*validator);
newPtr((void *) Result, 0, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxScrollBar");
break;
@@ -19301,7 +19338,7 @@ validator = (wxValidator *) getPtr(bp,memenv); bp += 4;
} break;
}};
if(!This) throw wxe_badarg(0);
- bool Result = This->Create(parent,(wxWindowID) *id,pos,size,style,*validator);
+ bool Result = This->Create(parent,*id,pos,size,style,*validator);
rt.addBool(Result);
break;
}
@@ -19337,7 +19374,7 @@ case wxScrollBar_SetThumbPosition: { // wxScrollBar::SetThumbPosition
wxScrollBar *This = (wxScrollBar *) getPtr(bp,memenv); bp += 4;
int * viewStart = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetThumbPosition((int) *viewStart);
+ This->SetThumbPosition(*viewStart);
break;
}
case wxScrollBar_SetScrollbar: { // wxScrollBar::SetScrollbar
@@ -19354,7 +19391,7 @@ case wxScrollBar_SetScrollbar: { // wxScrollBar::SetScrollbar
} break;
}};
if(!This) throw wxe_badarg(0);
- This->SetScrollbar((int) *position,(int) *thumbSize,(int) *range,(int) *pageSize,refresh);
+ This->SetScrollbar(*position,*thumbSize,*range,*pageSize,refresh);
break;
}
case wxSpinButton_new_2: { // wxSpinButton::wxSpinButton
@@ -19453,14 +19490,14 @@ case wxSpinButton_SetRange: { // wxSpinButton::SetRange
int * minVal = (int *) bp; bp += 4;
int * maxVal = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetRange((int) *minVal,(int) *maxVal);
+ This->SetRange(*minVal,*maxVal);
break;
}
case wxSpinButton_SetValue: { // wxSpinButton::SetValue
wxSpinButton *This = (wxSpinButton *) getPtr(bp,memenv); bp += 4;
int * value = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetValue((int) *value);
+ This->SetValue(*value);
break;
}
case wxSpinCtrl_new_0: { // wxSpinCtrl::wxSpinCtrl
@@ -19582,7 +19619,7 @@ case wxSpinCtrl_SetValue_1_0: { // wxSpinCtrl::SetValue
wxSpinCtrl *This = (wxSpinCtrl *) getPtr(bp,memenv); bp += 4;
int * value = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetValue((int) *value);
+ This->SetValue(*value);
break;
}
case wxSpinCtrl_GetValue: { // wxSpinCtrl::GetValue
@@ -19597,7 +19634,7 @@ case wxSpinCtrl_SetRange: { // wxSpinCtrl::SetRange
int * minVal = (int *) bp; bp += 4;
int * maxVal = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetRange((int) *minVal,(int) *maxVal);
+ This->SetRange(*minVal,*maxVal);
break;
}
case wxSpinCtrl_SetSelection: { // wxSpinCtrl::SetSelection
@@ -19605,7 +19642,7 @@ case wxSpinCtrl_SetSelection: { // wxSpinCtrl::SetSelection
int * from = (int *) bp; bp += 4;
int * to = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetSelection((long) *from,(long) *to);
+ This->SetSelection(*from,*to);
break;
}
case wxSpinCtrl_GetMin: { // wxSpinCtrl::GetMin
@@ -19654,7 +19691,7 @@ case wxStaticText_new_4: { // wxStaticText::wxStaticText
style = (long)*(int *) bp; bp += 4;
} break;
}};
- wxStaticText * Result = new EwxStaticText(parent,(wxWindowID) *id,label,pos,size,style);
+ wxStaticText * Result = new EwxStaticText(parent,*id,label,pos,size,style);
newPtr((void *) Result, 0, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxStaticText");
break;
@@ -19687,7 +19724,7 @@ case wxStaticText_Create: { // wxStaticText::Create
} break;
}};
if(!This) throw wxe_badarg(0);
- bool Result = This->Create(parent,(wxWindowID) *id,label,pos,size,style);
+ bool Result = This->Create(parent,*id,label,pos,size,style);
rt.addBool(Result);
break;
}
@@ -19711,7 +19748,7 @@ case wxStaticText_Wrap: { // wxStaticText::Wrap
wxStaticText *This = (wxStaticText *) getPtr(bp,memenv); bp += 4;
int * width = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->Wrap((int) *width);
+ This->Wrap(*width);
break;
}
case wxStaticBitmap_new_0: { // wxStaticBitmap::wxStaticBitmap
@@ -19745,7 +19782,7 @@ case wxStaticBitmap_new_4: { // wxStaticBitmap::wxStaticBitmap
style = (long)*(int *) bp; bp += 4;
} break;
}};
- wxStaticBitmap * Result = new EwxStaticBitmap(parent,(wxWindowID) *id,*label,pos,size,style);
+ wxStaticBitmap * Result = new EwxStaticBitmap(parent,*id,*label,pos,size,style);
newPtr((void *) Result, 0, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxStaticBitmap");
break;
@@ -19776,7 +19813,7 @@ case wxStaticBitmap_Create: { // wxStaticBitmap::Create
} break;
}};
if(!This) throw wxe_badarg(0);
- bool Result = This->Create(parent,(wxWindowID) *id,*label,pos,size,style);
+ bool Result = This->Create(parent,*id,*label,pos,size,style);
rt.addBool(Result);
break;
}
@@ -19830,7 +19867,7 @@ case wxRadioBox_new: { // wxRadioBox::wxRadioBox
val = (wxValidator *) getPtr(bp,memenv); bp += 4;
} break;
}};
- wxRadioBox * Result = new EwxRadioBox(parent,(wxWindowID) *id,title,pos,size,choices,majorDim,style,*val);
+ wxRadioBox * Result = new EwxRadioBox(parent,*id,title,pos,size,choices,majorDim,style,*val);
newPtr((void *) Result, 0, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxRadioBox");
break;
@@ -19873,7 +19910,7 @@ val = (wxValidator *) getPtr(bp,memenv); bp += 4;
} break;
}};
if(!This) throw wxe_badarg(0);
- bool Result = This->Create(parent,(wxWindowID) *id,title,pos,size,choices,majorDim,style,*val);
+ bool Result = This->Create(parent,*id,title,pos,size,choices,majorDim,style,*val);
rt.addBool(Result);
break;
}
@@ -19887,7 +19924,7 @@ case wxRadioBox_Enable_2: { // wxRadioBox::Enable
} break;
}};
if(!This) throw wxe_badarg(0);
- bool Result = This->Enable((int) *n,enable);
+ bool Result = This->Enable(*n,enable);
rt.addBool(Result);
break;
}
@@ -19916,7 +19953,7 @@ case wxRadioBox_GetString: { // wxRadioBox::GetString
wxRadioBox *This = (wxRadioBox *) getPtr(bp,memenv); bp += 4;
unsigned int * n = (unsigned int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxString Result = This->GetString((int) *n);
+ wxString Result = This->GetString(*n);
rt.add(Result);
break;
}
@@ -19924,7 +19961,7 @@ case wxRadioBox_SetSelection: { // wxRadioBox::SetSelection
wxRadioBox *This = (wxRadioBox *) getPtr(bp,memenv); bp += 4;
int * n = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetSelection((int) *n);
+ This->SetSelection(*n);
break;
}
case wxRadioBox_Show_2: { // wxRadioBox::Show
@@ -19937,7 +19974,7 @@ case wxRadioBox_Show_2: { // wxRadioBox::Show
} break;
}};
if(!This) throw wxe_badarg(0);
- bool Result = This->Show((int) *n,show);
+ bool Result = This->Show(*n,show);
rt.addBool(Result);
break;
}
@@ -19966,7 +20003,7 @@ case wxRadioBox_GetItemHelpText: { // wxRadioBox::GetItemHelpText
wxRadioBox *This = (wxRadioBox *) getPtr(bp,memenv); bp += 4;
unsigned int * n = (unsigned int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxString Result = This->GetItemHelpText((int) *n);
+ wxString Result = This->GetItemHelpText(*n);
rt.add(Result);
break;
}
@@ -19974,7 +20011,7 @@ case wxRadioBox_GetItemToolTip: { // wxRadioBox::GetItemToolTip
wxRadioBox *This = (wxRadioBox *) getPtr(bp,memenv); bp += 4;
unsigned int * item = (unsigned int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxToolTip * Result = (wxToolTip*)This->GetItemToolTip((int) *item);
+ wxToolTip * Result = (wxToolTip*)This->GetItemToolTip(*item);
rt.addRef(getRef((void *)Result,memenv), "wxToolTip");
break;
}
@@ -19999,7 +20036,7 @@ case wxRadioBox_IsItemEnabled: { // wxRadioBox::IsItemEnabled
wxRadioBox *This = (wxRadioBox *) getPtr(bp,memenv); bp += 4;
unsigned int * n = (unsigned int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->IsItemEnabled((int) *n);
+ bool Result = This->IsItemEnabled(*n);
rt.addBool(Result);
break;
}
@@ -20007,7 +20044,7 @@ case wxRadioBox_IsItemShown: { // wxRadioBox::IsItemShown
wxRadioBox *This = (wxRadioBox *) getPtr(bp,memenv); bp += 4;
unsigned int * n = (unsigned int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->IsItemShown((int) *n);
+ bool Result = This->IsItemShown(*n);
rt.addBool(Result);
break;
}
@@ -20018,7 +20055,7 @@ case wxRadioBox_SetItemHelpText: { // wxRadioBox::SetItemHelpText
wxString helpText = wxString(bp, wxConvUTF8);
bp += *helpTextLen+((8-((4+ *helpTextLen) & 7)) & 7);
if(!This) throw wxe_badarg(0);
- This->SetItemHelpText((int) *n,helpText);
+ This->SetItemHelpText(*n,helpText);
break;
}
case wxRadioBox_SetItemToolTip: { // wxRadioBox::SetItemToolTip
@@ -20028,7 +20065,7 @@ case wxRadioBox_SetItemToolTip: { // wxRadioBox::SetItemToolTip
wxString text = wxString(bp, wxConvUTF8);
bp += *textLen+((8-((4+ *textLen) & 7)) & 7);
if(!This) throw wxe_badarg(0);
- This->SetItemToolTip((int) *item,text);
+ This->SetItemToolTip(*item,text);
break;
}
case wxRadioButton_new_0: { // wxRadioButton::wxRadioButton
@@ -20067,7 +20104,7 @@ case wxRadioButton_new_4: { // wxRadioButton::wxRadioButton
validator = (wxValidator *) getPtr(bp,memenv); bp += 4;
} break;
}};
- wxRadioButton * Result = new EwxRadioButton(parent,(wxWindowID) *id,label,pos,size,style,*validator);
+ wxRadioButton * Result = new EwxRadioButton(parent,*id,label,pos,size,style,*validator);
newPtr((void *) Result, 0, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxRadioButton");
break;
@@ -20104,7 +20141,7 @@ validator = (wxValidator *) getPtr(bp,memenv); bp += 4;
} break;
}};
if(!This) throw wxe_badarg(0);
- bool Result = This->Create(parent,(wxWindowID) *id,label,pos,size,style,*validator);
+ bool Result = This->Create(parent,*id,label,pos,size,style,*validator);
rt.addBool(Result);
break;
}
@@ -20119,7 +20156,7 @@ case wxRadioButton_SetValue: { // wxRadioButton::SetValue
wxRadioButton *This = (wxRadioButton *) getPtr(bp,memenv); bp += 4;
bool * val = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetValue((bool) *val);
+ This->SetValue(*val);
break;
}
case wxSlider_new_6: { // wxSlider::wxSlider
@@ -20153,7 +20190,7 @@ case wxSlider_new_6: { // wxSlider::wxSlider
validator = (wxValidator *) getPtr(bp,memenv); bp += 4;
} break;
}};
- wxSlider * Result = new EwxSlider(parent,(wxWindowID) *id,(int) *value,(int) *minValue,(int) *maxValue,pos,size,style,*validator);
+ wxSlider * Result = new EwxSlider(parent,*id,*value,*minValue,*maxValue,pos,size,style,*validator);
newPtr((void *) Result, 0, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxSlider");
break;
@@ -20196,7 +20233,7 @@ validator = (wxValidator *) getPtr(bp,memenv); bp += 4;
} break;
}};
if(!This) throw wxe_badarg(0);
- bool Result = This->Create(parent,(wxWindowID) *id,(int) *value,(int) *minValue,(int) *maxValue,pos,size,style,*validator);
+ bool Result = This->Create(parent,*id,*value,*minValue,*maxValue,pos,size,style,*validator);
rt.addBool(Result);
break;
}
@@ -20246,14 +20283,14 @@ case wxSlider_SetLineSize: { // wxSlider::SetLineSize
wxSlider *This = (wxSlider *) getPtr(bp,memenv); bp += 4;
int * lineSize = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetLineSize((int) *lineSize);
+ This->SetLineSize(*lineSize);
break;
}
case wxSlider_SetPageSize: { // wxSlider::SetPageSize
wxSlider *This = (wxSlider *) getPtr(bp,memenv); bp += 4;
int * pageSize = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetPageSize((int) *pageSize);
+ This->SetPageSize(*pageSize);
break;
}
case wxSlider_SetRange: { // wxSlider::SetRange
@@ -20261,21 +20298,21 @@ case wxSlider_SetRange: { // wxSlider::SetRange
int * minValue = (int *) bp; bp += 4;
int * maxValue = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetRange((int) *minValue,(int) *maxValue);
+ This->SetRange(*minValue,*maxValue);
break;
}
case wxSlider_SetThumbLength: { // wxSlider::SetThumbLength
wxSlider *This = (wxSlider *) getPtr(bp,memenv); bp += 4;
int * lenPixels = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetThumbLength((int) *lenPixels);
+ This->SetThumbLength(*lenPixels);
break;
}
case wxSlider_SetValue: { // wxSlider::SetValue
wxSlider *This = (wxSlider *) getPtr(bp,memenv); bp += 4;
int * value = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetValue((int) *value);
+ This->SetValue(*value);
break;
}
case wxDialog_new_4: { // wxDialog::wxDialog
@@ -20304,7 +20341,7 @@ case wxDialog_new_4: { // wxDialog::wxDialog
style = (long)*(int *) bp; bp += 4;
} break;
}};
- wxDialog * Result = new EwxDialog(parent,(wxWindowID) *id,title,pos,size,style);
+ wxDialog * Result = new EwxDialog(parent,*id,title,pos,size,style);
newPtr((void *) Result, 2, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxDialog");
break;
@@ -20343,7 +20380,7 @@ case wxDialog_Create: { // wxDialog::Create
} break;
}};
if(!This) throw wxe_badarg(0);
- bool Result = This->Create(parent,(wxWindowID) *id,title,pos,size,style);
+ bool Result = This->Create(parent,*id,title,pos,size,style);
rt.addBool(Result);
break;
}
@@ -20351,7 +20388,7 @@ case wxDialog_CreateButtonSizer: { // wxDialog::CreateButtonSizer
wxDialog *This = (wxDialog *) getPtr(bp,memenv); bp += 4;
int * flags = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxSizer * Result = (wxSizer*)This->CreateButtonSizer((long) *flags);
+ wxSizer * Result = (wxSizer*)This->CreateButtonSizer(*flags);
rt.addRef(getRef((void *)Result,memenv), "wxSizer");
break;
}
@@ -20359,7 +20396,7 @@ case wxDialog_CreateStdDialogButtonSizer: { // wxDialog::CreateStdDialogButtonSi
wxDialog *This = (wxDialog *) getPtr(bp,memenv); bp += 4;
int * flags = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxStdDialogButtonSizer * Result = (wxStdDialogButtonSizer*)This->CreateStdDialogButtonSizer((long) *flags);
+ wxStdDialogButtonSizer * Result = (wxStdDialogButtonSizer*)This->CreateStdDialogButtonSizer(*flags);
rt.addRef(getRef((void *)Result,memenv), "wxStdDialogButtonSizer");
break;
}
@@ -20367,7 +20404,7 @@ case wxDialog_EndModal: { // wxDialog::EndModal
wxDialog *This = (wxDialog *) getPtr(bp,memenv); bp += 4;
int * retCode = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->EndModal((int) *retCode);
+ This->EndModal(*retCode);
break;
}
case wxDialog_GetAffirmativeId: { // wxDialog::GetAffirmativeId
@@ -20395,14 +20432,14 @@ case wxDialog_SetAffirmativeId: { // wxDialog::SetAffirmativeId
wxDialog *This = (wxDialog *) getPtr(bp,memenv); bp += 4;
int * affirmativeId = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetAffirmativeId((int) *affirmativeId);
+ This->SetAffirmativeId(*affirmativeId);
break;
}
case wxDialog_SetReturnCode: { // wxDialog::SetReturnCode
wxDialog *This = (wxDialog *) getPtr(bp,memenv); bp += 4;
int * returnCode = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetReturnCode((int) *returnCode);
+ This->SetReturnCode(*returnCode);
break;
}
case wxDialog_Show: { // wxDialog::Show
@@ -20498,7 +20535,7 @@ case wxColourData_GetCustomColour: { // wxColourData::GetCustomColour
wxColourData *This = (wxColourData *) getPtr(bp,memenv); bp += 4;
int * i = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxColour Result = This->GetCustomColour((int) *i);
+ wxColour Result = This->GetCustomColour(*i);
rt.add(Result);
break;
}
@@ -20506,7 +20543,7 @@ case wxColourData_SetChooseFull: { // wxColourData::SetChooseFull
wxColourData *This = (wxColourData *) getPtr(bp,memenv); bp += 4;
bool * flag = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetChooseFull((bool) *flag);
+ This->SetChooseFull(*flag);
break;
}
case wxColourData_SetColour: { // wxColourData::SetColour
@@ -20529,7 +20566,7 @@ case wxColourData_SetCustomColour: { // wxColourData::SetCustomColour
int * colourA = (int *) bp; bp += 4;
wxColour colour = wxColour(*colourR,*colourG,*colourB,*colourA);
if(!This) throw wxe_badarg(0);
- This->SetCustomColour((int) *i,colour);
+ This->SetCustomColour(*i,colour);
break;
}
case wxPalette_new_0: { // wxPalette::wxPalette
@@ -20570,7 +20607,7 @@ case wxPalette_GetPixel: { // wxPalette::GetPixel
unsigned int * green = (unsigned int *) bp; bp += 4;
unsigned int * blue = (unsigned int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->GetPixel((char) *red,(char) *green,(char) *blue);
+ int Result = This->GetPixel(*red,*green,*blue);
rt.addInt(Result);
break;
}
@@ -20581,7 +20618,7 @@ case wxPalette_GetRGB: { // wxPalette::GetRGB
wxPalette *This = (wxPalette *) getPtr(bp,memenv); bp += 4;
int * pixel = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->GetRGB((int) *pixel,&red,&green,&blue);
+ bool Result = This->GetRGB(*pixel,&red,&green,&blue);
rt.addBool(Result);
rt.addUint(red);
rt.addUint(green);
@@ -20800,7 +20837,7 @@ case wxFileDialog_SetFilterIndex: { // wxFileDialog::SetFilterIndex
wxFileDialog *This = (wxFileDialog *) getPtr(bp,memenv); bp += 4;
int * filterIndex = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetFilterIndex((int) *filterIndex);
+ This->SetFilterIndex(*filterIndex);
break;
}
case wxFileDialog_SetMessage: { // wxFileDialog::SetMessage
@@ -20834,7 +20871,7 @@ case wxPickerBase_SetInternalMargin: { // wxPickerBase::SetInternalMargin
wxPickerBase *This = (wxPickerBase *) getPtr(bp,memenv); bp += 4;
int * newmargin = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetInternalMargin((int) *newmargin);
+ This->SetInternalMargin(*newmargin);
break;
}
case wxPickerBase_GetInternalMargin: { // wxPickerBase::GetInternalMargin
@@ -20848,14 +20885,14 @@ case wxPickerBase_SetTextCtrlProportion: { // wxPickerBase::SetTextCtrlProportio
wxPickerBase *This = (wxPickerBase *) getPtr(bp,memenv); bp += 4;
int * prop = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetTextCtrlProportion((int) *prop);
+ This->SetTextCtrlProportion(*prop);
break;
}
case wxPickerBase_SetPickerCtrlProportion: { // wxPickerBase::SetPickerCtrlProportion
wxPickerBase *This = (wxPickerBase *) getPtr(bp,memenv); bp += 4;
int * prop = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetPickerCtrlProportion((int) *prop);
+ This->SetPickerCtrlProportion(*prop);
break;
}
case wxPickerBase_GetTextCtrlProportion: { // wxPickerBase::GetTextCtrlProportion
@@ -20977,7 +21014,7 @@ case wxFilePickerCtrl_new_3: { // wxFilePickerCtrl::wxFilePickerCtrl
validator = (wxValidator *) getPtr(bp,memenv); bp += 4;
} break;
}};
- wxFilePickerCtrl * Result = new EwxFilePickerCtrl(parent,(wxWindowID) *id,path,message,wildcard,pos,size,style,*validator);
+ wxFilePickerCtrl * Result = new EwxFilePickerCtrl(parent,*id,path,message,wildcard,pos,size,style,*validator);
newPtr((void *) Result, 0, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxFilePickerCtrl");
break;
@@ -21030,7 +21067,7 @@ validator = (wxValidator *) getPtr(bp,memenv); bp += 4;
} break;
}};
if(!This) throw wxe_badarg(0);
- bool Result = This->Create(parent,(wxWindowID) *id,path,message,wildcard,pos,size,style,*validator);
+ bool Result = This->Create(parent,*id,path,message,wildcard,pos,size,style,*validator);
rt.addBool(Result);
break;
}
@@ -21095,7 +21132,7 @@ case wxDirPickerCtrl_new_3: { // wxDirPickerCtrl::wxDirPickerCtrl
validator = (wxValidator *) getPtr(bp,memenv); bp += 4;
} break;
}};
- wxDirPickerCtrl * Result = new EwxDirPickerCtrl(parent,(wxWindowID) *id,path,message,pos,size,style,*validator);
+ wxDirPickerCtrl * Result = new EwxDirPickerCtrl(parent,*id,path,message,pos,size,style,*validator);
newPtr((void *) Result, 0, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxDirPickerCtrl");
break;
@@ -21142,7 +21179,7 @@ validator = (wxValidator *) getPtr(bp,memenv); bp += 4;
} break;
}};
if(!This) throw wxe_badarg(0);
- bool Result = This->Create(parent,(wxWindowID) *id,path,message,pos,size,style,*validator);
+ bool Result = This->Create(parent,*id,path,message,pos,size,style,*validator);
rt.addBool(Result);
break;
}
@@ -21204,7 +21241,7 @@ case wxColourPickerCtrl_new_3: { // wxColourPickerCtrl::wxColourPickerCtrl
validator = (wxValidator *) getPtr(bp,memenv); bp += 4;
} break;
}};
- wxColourPickerCtrl * Result = new EwxColourPickerCtrl(parent,(wxWindowID) *id,col,pos,size,style,*validator);
+ wxColourPickerCtrl * Result = new EwxColourPickerCtrl(parent,*id,col,pos,size,style,*validator);
newPtr((void *) Result, 0, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxColourPickerCtrl");
break;
@@ -21248,7 +21285,7 @@ validator = (wxValidator *) getPtr(bp,memenv); bp += 4;
} break;
}};
if(!This) throw wxe_badarg(0);
- bool Result = This->Create(parent,(wxWindowID) *id,col,pos,size,style,*validator);
+ bool Result = This->Create(parent,*id,col,pos,size,style,*validator);
rt.addBool(Result);
break;
}
@@ -21324,7 +21361,7 @@ case wxDatePickerCtrl_new_3: { // wxDatePickerCtrl::wxDatePickerCtrl
validator = (wxValidator *) getPtr(bp,memenv); bp += 4;
} break;
}};
- wxDatePickerCtrl * Result = new EwxDatePickerCtrl(parent,(wxWindowID) *id,date,pos,size,style,*validator);
+ wxDatePickerCtrl * Result = new EwxDatePickerCtrl(parent,*id,date,pos,size,style,*validator);
newPtr((void *) Result, 0, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxDatePickerCtrl");
break;
@@ -21427,7 +21464,7 @@ initial = (wxFont *) getPtr(bp,memenv); bp += 4;
validator = (wxValidator *) getPtr(bp,memenv); bp += 4;
} break;
}};
- wxFontPickerCtrl * Result = new EwxFontPickerCtrl(parent,(wxWindowID) *id,*initial,pos,size,style,*validator);
+ wxFontPickerCtrl * Result = new EwxFontPickerCtrl(parent,*id,*initial,pos,size,style,*validator);
newPtr((void *) Result, 0, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxFontPickerCtrl");
break;
@@ -21466,7 +21503,7 @@ validator = (wxValidator *) getPtr(bp,memenv); bp += 4;
} break;
}};
if(!This) throw wxe_badarg(0);
- bool Result = This->Create(parent,(wxWindowID) *id,*initial,pos,size,style,*validator);
+ bool Result = This->Create(parent,*id,*initial,pos,size,style,*validator);
rt.addBool(Result);
break;
}
@@ -21495,7 +21532,7 @@ case wxFontPickerCtrl_SetMaxPointSize: { // wxFontPickerCtrl::SetMaxPointSize
wxFontPickerCtrl *This = (wxFontPickerCtrl *) getPtr(bp,memenv); bp += 4;
unsigned int * max = (unsigned int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetMaxPointSize((int) *max);
+ This->SetMaxPointSize(*max);
break;
}
case wxFindReplaceDialog_new_0: { // wxFindReplaceDialog::wxFindReplaceDialog
@@ -21554,7 +21591,7 @@ case wxFindReplaceData_new_0: { // wxFindReplaceData::wxFindReplaceData
}
case wxFindReplaceData_new_1: { // wxFindReplaceData::wxFindReplaceData
unsigned int * flags = (unsigned int *) bp; bp += 4;
- wxFindReplaceData * Result = new EwxFindReplaceData((int) *flags);
+ wxFindReplaceData * Result = new EwxFindReplaceData(*flags);
newPtr((void *) Result, 1, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxFindReplaceData");
break;
@@ -21584,7 +21621,7 @@ case wxFindReplaceData_SetFlags: { // wxFindReplaceData::SetFlags
wxFindReplaceData *This = (wxFindReplaceData *) getPtr(bp,memenv); bp += 4;
unsigned int * flags = (unsigned int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetFlags((int) *flags);
+ This->SetFlags(*flags);
break;
}
case wxFindReplaceData_SetFindString: { // wxFindReplaceData::SetFindString
@@ -21725,7 +21762,7 @@ case wxSingleChoiceDialog_SetSelection: { // wxSingleChoiceDialog::SetSelection
wxSingleChoiceDialog *This = (wxSingleChoiceDialog *) getPtr(bp,memenv); bp += 4;
int * sel = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetSelection((int) *sel);
+ This->SetSelection(*sel);
break;
}
case wxTextEntryDialog_new: { // wxTextEntryDialog::wxTextEntryDialog
@@ -21831,7 +21868,7 @@ case wxFontData_EnableEffects: { // wxFontData::EnableEffects
wxFontData *This = (wxFontData *) getPtr(bp,memenv); bp += 4;
bool * flag = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->EnableEffects((bool) *flag);
+ This->EnableEffects(*flag);
break;
}
case wxFontData_GetAllowSymbols: { // wxFontData::GetAllowSymbols
@@ -21880,7 +21917,7 @@ case wxFontData_SetAllowSymbols: { // wxFontData::SetAllowSymbols
wxFontData *This = (wxFontData *) getPtr(bp,memenv); bp += 4;
bool * flag = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetAllowSymbols((bool) *flag);
+ This->SetAllowSymbols(*flag);
break;
}
case wxFontData_SetChosenFont: { // wxFontData::SetChosenFont
@@ -21913,14 +21950,14 @@ case wxFontData_SetRange: { // wxFontData::SetRange
int * minRange = (int *) bp; bp += 4;
int * maxRange = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetRange((int) *minRange,(int) *maxRange);
+ This->SetRange(*minRange,*maxRange);
break;
}
case wxFontData_SetShowHelp: { // wxFontData::SetShowHelp
wxFontData *This = (wxFontData *) getPtr(bp,memenv); bp += 4;
bool * flag = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetShowHelp((bool) *flag);
+ This->SetShowHelp(*flag);
break;
}
case wxFontDialog_new_0: { // wxFontDialog::wxFontDialog
@@ -21997,7 +22034,7 @@ case wxProgressDialog_Update_2: { // wxProgressDialog::Update
} break;
}};
if(!This) throw wxe_badarg(0);
- bool Result = This->Update((int) *value,newmsg);
+ bool Result = This->Update(*value,newmsg);
rt.addBool(Result);
break;
}
@@ -22088,35 +22125,35 @@ case wxPageSetupDialogData_EnableHelp: { // wxPageSetupDialogData::EnableHelp
wxPageSetupDialogData *This = (wxPageSetupDialogData *) getPtr(bp,memenv); bp += 4;
bool * flag = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->EnableHelp((bool) *flag);
+ This->EnableHelp(*flag);
break;
}
case wxPageSetupDialogData_EnableMargins: { // wxPageSetupDialogData::EnableMargins
wxPageSetupDialogData *This = (wxPageSetupDialogData *) getPtr(bp,memenv); bp += 4;
bool * flag = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->EnableMargins((bool) *flag);
+ This->EnableMargins(*flag);
break;
}
case wxPageSetupDialogData_EnableOrientation: { // wxPageSetupDialogData::EnableOrientation
wxPageSetupDialogData *This = (wxPageSetupDialogData *) getPtr(bp,memenv); bp += 4;
bool * flag = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->EnableOrientation((bool) *flag);
+ This->EnableOrientation(*flag);
break;
}
case wxPageSetupDialogData_EnablePaper: { // wxPageSetupDialogData::EnablePaper
wxPageSetupDialogData *This = (wxPageSetupDialogData *) getPtr(bp,memenv); bp += 4;
bool * flag = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->EnablePaper((bool) *flag);
+ This->EnablePaper(*flag);
break;
}
case wxPageSetupDialogData_EnablePrinter: { // wxPageSetupDialogData::EnablePrinter
wxPageSetupDialogData *This = (wxPageSetupDialogData *) getPtr(bp,memenv); bp += 4;
bool * flag = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->EnablePrinter((bool) *flag);
+ This->EnablePrinter(*flag);
break;
}
case wxPageSetupDialogData_GetDefaultMinMargins: { // wxPageSetupDialogData::GetDefaultMinMargins
@@ -22199,7 +22236,7 @@ case wxPageSetupDialogData_GetMinMarginBottomRight: { // wxPageSetupDialogData::
case wxPageSetupDialogData_GetPaperId: { // wxPageSetupDialogData::GetPaperId
wxPageSetupDialogData *This = (wxPageSetupDialogData *) getPtr(bp,memenv); bp += 4;
if(!This) throw wxe_badarg(0);
- wxPaperSize Result = This->GetPaperId();
+ int Result = This->GetPaperId();
rt.addInt(Result);
break;
}
@@ -22228,14 +22265,14 @@ case wxPageSetupDialogData_SetDefaultInfo: { // wxPageSetupDialogData::SetDefaul
wxPageSetupDialogData *This = (wxPageSetupDialogData *) getPtr(bp,memenv); bp += 4;
bool * flag = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetDefaultInfo((bool) *flag);
+ This->SetDefaultInfo(*flag);
break;
}
case wxPageSetupDialogData_SetDefaultMinMargins: { // wxPageSetupDialogData::SetDefaultMinMargins
wxPageSetupDialogData *This = (wxPageSetupDialogData *) getPtr(bp,memenv); bp += 4;
bool * flag = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetDefaultMinMargins((bool) *flag);
+ This->SetDefaultMinMargins(*flag);
break;
}
case wxPageSetupDialogData_SetMarginTopLeft: { // wxPageSetupDialogData::SetMarginTopLeft
@@ -22276,9 +22313,9 @@ case wxPageSetupDialogData_SetMinMarginBottomRight: { // wxPageSetupDialogData::
}
case wxPageSetupDialogData_SetPaperId: { // wxPageSetupDialogData::SetPaperId
wxPageSetupDialogData *This = (wxPageSetupDialogData *) getPtr(bp,memenv); bp += 4;
- int * id = (int *) bp; bp += 4;
+ wxPaperSize id = *(wxPaperSize *) bp; bp += 4;;
if(!This) throw wxe_badarg(0);
- This->SetPaperId((wxPaperSize) *id);
+ This->SetPaperId(id);
break;
}
case wxPageSetupDialogData_SetPaperSize_1_1: { // wxPageSetupDialogData::SetPaperSize
@@ -22292,9 +22329,9 @@ case wxPageSetupDialogData_SetPaperSize_1_1: { // wxPageSetupDialogData::SetPape
}
case wxPageSetupDialogData_SetPaperSize_1_0: { // wxPageSetupDialogData::SetPaperSize
wxPageSetupDialogData *This = (wxPageSetupDialogData *) getPtr(bp,memenv); bp += 4;
- int * id = (int *) bp; bp += 4;
+ wxPaperSize id = *(wxPaperSize *) bp; bp += 4;;
if(!This) throw wxe_badarg(0);
- This->SetPaperSize((wxPaperSize) *id);
+ This->SetPaperSize(id);
break;
}
case wxPageSetupDialogData_SetPrintData: { // wxPageSetupDialogData::SetPrintData
@@ -22364,28 +22401,28 @@ case wxPrintDialogData_EnableHelp: { // wxPrintDialogData::EnableHelp
wxPrintDialogData *This = (wxPrintDialogData *) getPtr(bp,memenv); bp += 4;
bool * flag = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->EnableHelp((bool) *flag);
+ This->EnableHelp(*flag);
break;
}
case wxPrintDialogData_EnablePageNumbers: { // wxPrintDialogData::EnablePageNumbers
wxPrintDialogData *This = (wxPrintDialogData *) getPtr(bp,memenv); bp += 4;
bool * flag = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->EnablePageNumbers((bool) *flag);
+ This->EnablePageNumbers(*flag);
break;
}
case wxPrintDialogData_EnablePrintToFile: { // wxPrintDialogData::EnablePrintToFile
wxPrintDialogData *This = (wxPrintDialogData *) getPtr(bp,memenv); bp += 4;
bool * flag = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->EnablePrintToFile((bool) *flag);
+ This->EnablePrintToFile(*flag);
break;
}
case wxPrintDialogData_EnableSelection: { // wxPrintDialogData::EnableSelection
wxPrintDialogData *This = (wxPrintDialogData *) getPtr(bp,memenv); bp += 4;
bool * flag = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->EnableSelection((bool) *flag);
+ This->EnableSelection(*flag);
break;
}
case wxPrintDialogData_GetAllPages: { // wxPrintDialogData::GetAllPages
@@ -22469,35 +22506,35 @@ case wxPrintDialogData_SetCollate: { // wxPrintDialogData::SetCollate
wxPrintDialogData *This = (wxPrintDialogData *) getPtr(bp,memenv); bp += 4;
bool * flag = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetCollate((bool) *flag);
+ This->SetCollate(*flag);
break;
}
case wxPrintDialogData_SetFromPage: { // wxPrintDialogData::SetFromPage
wxPrintDialogData *This = (wxPrintDialogData *) getPtr(bp,memenv); bp += 4;
int * v = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetFromPage((int) *v);
+ This->SetFromPage(*v);
break;
}
case wxPrintDialogData_SetMaxPage: { // wxPrintDialogData::SetMaxPage
wxPrintDialogData *This = (wxPrintDialogData *) getPtr(bp,memenv); bp += 4;
int * v = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetMaxPage((int) *v);
+ This->SetMaxPage(*v);
break;
}
case wxPrintDialogData_SetMinPage: { // wxPrintDialogData::SetMinPage
wxPrintDialogData *This = (wxPrintDialogData *) getPtr(bp,memenv); bp += 4;
int * v = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetMinPage((int) *v);
+ This->SetMinPage(*v);
break;
}
case wxPrintDialogData_SetNoCopies: { // wxPrintDialogData::SetNoCopies
wxPrintDialogData *This = (wxPrintDialogData *) getPtr(bp,memenv); bp += 4;
int * v = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetNoCopies((int) *v);
+ This->SetNoCopies(*v);
break;
}
case wxPrintDialogData_SetPrintData: { // wxPrintDialogData::SetPrintData
@@ -22511,21 +22548,21 @@ case wxPrintDialogData_SetPrintToFile: { // wxPrintDialogData::SetPrintToFile
wxPrintDialogData *This = (wxPrintDialogData *) getPtr(bp,memenv); bp += 4;
bool * flag = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetPrintToFile((bool) *flag);
+ This->SetPrintToFile(*flag);
break;
}
case wxPrintDialogData_SetSelection: { // wxPrintDialogData::SetSelection
wxPrintDialogData *This = (wxPrintDialogData *) getPtr(bp,memenv); bp += 4;
bool * flag = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetSelection((bool) *flag);
+ This->SetSelection(*flag);
break;
}
case wxPrintDialogData_SetToPage: { // wxPrintDialogData::SetToPage
wxPrintDialogData *This = (wxPrintDialogData *) getPtr(bp,memenv); bp += 4;
int * v = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetToPage((int) *v);
+ This->SetToPage(*v);
break;
}
case wxPrintData_new_0: { // wxPrintData::wxPrintData
@@ -22586,7 +22623,7 @@ case wxPrintData_GetOrientation: { // wxPrintData::GetOrientation
case wxPrintData_GetPaperId: { // wxPrintData::GetPaperId
wxPrintData *This = (wxPrintData *) getPtr(bp,memenv); bp += 4;
if(!This) throw wxe_badarg(0);
- wxPaperSize Result = This->GetPaperId();
+ int Result = This->GetPaperId();
rt.addInt(Result);
break;
}
@@ -22615,49 +22652,49 @@ case wxPrintData_SetBin: { // wxPrintData::SetBin
wxPrintData *This = (wxPrintData *) getPtr(bp,memenv); bp += 4;
wxPrintBin bin = *(wxPrintBin *) bp; bp += 4;;
if(!This) throw wxe_badarg(0);
- This->SetBin((wxPrintBin) bin);
+ This->SetBin(bin);
break;
}
case wxPrintData_SetCollate: { // wxPrintData::SetCollate
wxPrintData *This = (wxPrintData *) getPtr(bp,memenv); bp += 4;
bool * flag = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetCollate((bool) *flag);
+ This->SetCollate(*flag);
break;
}
case wxPrintData_SetColour: { // wxPrintData::SetColour
wxPrintData *This = (wxPrintData *) getPtr(bp,memenv); bp += 4;
bool * colour = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetColour((bool) *colour);
+ This->SetColour(*colour);
break;
}
case wxPrintData_SetDuplex: { // wxPrintData::SetDuplex
wxPrintData *This = (wxPrintData *) getPtr(bp,memenv); bp += 4;
wxDuplexMode duplex = *(wxDuplexMode *) bp; bp += 4;;
if(!This) throw wxe_badarg(0);
- This->SetDuplex((wxDuplexMode) duplex);
+ This->SetDuplex(duplex);
break;
}
case wxPrintData_SetNoCopies: { // wxPrintData::SetNoCopies
wxPrintData *This = (wxPrintData *) getPtr(bp,memenv); bp += 4;
int * v = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetNoCopies((int) *v);
+ This->SetNoCopies(*v);
break;
}
case wxPrintData_SetOrientation: { // wxPrintData::SetOrientation
wxPrintData *This = (wxPrintData *) getPtr(bp,memenv); bp += 4;
int * orient = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetOrientation((int) *orient);
+ This->SetOrientation(*orient);
break;
}
case wxPrintData_SetPaperId: { // wxPrintData::SetPaperId
wxPrintData *This = (wxPrintData *) getPtr(bp,memenv); bp += 4;
- int * sizeId = (int *) bp; bp += 4;
+ wxPaperSize sizeId = *(wxPaperSize *) bp; bp += 4;;
if(!This) throw wxe_badarg(0);
- This->SetPaperId((wxPaperSize) *sizeId);
+ This->SetPaperId(sizeId);
break;
}
case wxPrintData_SetPrinterName: { // wxPrintData::SetPrinterName
@@ -22673,7 +22710,7 @@ case wxPrintData_SetQuality: { // wxPrintData::SetQuality
wxPrintData *This = (wxPrintData *) getPtr(bp,memenv); bp += 4;
int * quality = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetQuality((wxPrintQuality) *quality);
+ This->SetQuality(*quality);
break;
}
case wxPrintPreview_new_2: { // wxPrintPreview::wxPrintPreview
@@ -22772,7 +22809,7 @@ case wxPrintPreview_Print: { // wxPrintPreview::Print
wxPrintPreview *This = (wxPrintPreview *) getPtr(bp,memenv); bp += 4;
bool * interactive = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->Print((bool) *interactive);
+ bool Result = This->Print(*interactive);
rt.addBool(Result);
break;
}
@@ -22780,7 +22817,7 @@ case wxPrintPreview_RenderPage: { // wxPrintPreview::RenderPage
wxPrintPreview *This = (wxPrintPreview *) getPtr(bp,memenv); bp += 4;
int * pageNum = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->RenderPage((int) *pageNum);
+ bool Result = This->RenderPage(*pageNum);
rt.addBool(Result);
break;
}
@@ -22795,7 +22832,7 @@ case wxPrintPreview_SetCurrentPage: { // wxPrintPreview::SetCurrentPage
wxPrintPreview *This = (wxPrintPreview *) getPtr(bp,memenv); bp += 4;
int * pageNum = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->SetCurrentPage((int) *pageNum);
+ bool Result = This->SetCurrentPage(*pageNum);
rt.addBool(Result);
break;
}
@@ -22817,7 +22854,7 @@ case wxPrintPreview_SetZoom: { // wxPrintPreview::SetZoom
wxPrintPreview *This = (wxPrintPreview *) getPtr(bp,memenv); bp += 4;
int * percent = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetZoom((int) *percent);
+ This->SetZoom(*percent);
break;
}
case wxPreviewFrame_new: { // wxPreviewFrame::wxPreviewFrame
@@ -22904,7 +22941,7 @@ case wxPreviewControlBar_new: { // wxPreviewControlBar::wxPreviewControlBar
style = (long)*(int *) bp; bp += 4;
} break;
}};
- wxPreviewControlBar * Result = new EwxPreviewControlBar(preview,(long) *buttons,parent,pos,size,style);
+ wxPreviewControlBar * Result = new EwxPreviewControlBar(preview,*buttons,parent,pos,size,style);
newPtr((void *) Result, 0, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxPreviewControlBar");
break;
@@ -22933,7 +22970,7 @@ case wxPreviewControlBar_SetZoomControl: { // wxPreviewControlBar::SetZoomContro
wxPreviewControlBar *This = (wxPreviewControlBar *) getPtr(bp,memenv); bp += 4;
int * zoom = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetZoomControl((int) *zoom);
+ This->SetZoomControl(*zoom);
break;
}
case wxPrinter_new: { // wxPrinter::wxPrinter
@@ -23089,7 +23126,7 @@ case wxXmlResource_CompareVersion: { // wxXmlResource::CompareVersion
int * release = (int *) bp; bp += 4;
int * revision = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->CompareVersion((int) *major,(int) *minor,(int) *release,(int) *revision);
+ int Result = This->CompareVersion(*major,*minor,*release,*revision);
rt.addInt(Result);
break;
}
@@ -23283,7 +23320,7 @@ case wxXmlResource_SetFlags: { // wxXmlResource::SetFlags
wxXmlResource *This = (wxXmlResource *) getPtr(bp,memenv); bp += 4;
int * flags = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetFlags((int) *flags);
+ This->SetFlags(*flags);
break;
}
case wxXmlResource_Unload: { // wxXmlResource::Unload
@@ -23811,14 +23848,14 @@ case wxAuiManager_SetDockSizeConstraint: { // wxAuiManager::SetDockSizeConstrain
double * width_pct = (double *) bp; bp += 8;
double * height_pct = (double *) bp; bp += 8;
if(!This) throw wxe_badarg(0);
- This->SetDockSizeConstraint((double) *width_pct,(double) *height_pct);
+ This->SetDockSizeConstraint(*width_pct,*height_pct);
break;
}
case wxAuiManager_SetFlags: { // wxAuiManager::SetFlags
wxAuiManager *This = (wxAuiManager *) getPtr(bp,memenv); bp += 4;
unsigned int * flags = (unsigned int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetFlags((int) *flags);
+ This->SetFlags(*flags);
break;
}
case wxAuiManager_SetManagedWindow: { // wxAuiManager::SetManagedWindow
@@ -23887,7 +23924,7 @@ case wxAuiPaneInfo_BestSize_2: { // wxAuiPaneInfo::BestSize
int * x = (int *) bp; bp += 4;
int * y = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxAuiPaneInfo * Result = &This->BestSize((int) *x,(int) *y);
+ wxAuiPaneInfo * Result = &This->BestSize(*x,*y);
rt.addRef(getRef((void *)Result,memenv), "wxAuiPaneInfo");
break;
}
@@ -23989,7 +24026,7 @@ case wxAuiPaneInfo_Direction: { // wxAuiPaneInfo::Direction
wxAuiPaneInfo *This = (wxAuiPaneInfo *) getPtr(bp,memenv); bp += 4;
int * direction = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxAuiPaneInfo * Result = &This->Direction((int) *direction);
+ wxAuiPaneInfo * Result = &This->Direction(*direction);
rt.addRef(getRef((void *)Result,memenv), "wxAuiPaneInfo");
break;
}
@@ -24057,7 +24094,7 @@ case wxAuiPaneInfo_FloatingPosition_2: { // wxAuiPaneInfo::FloatingPosition
int * x = (int *) bp; bp += 4;
int * y = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxAuiPaneInfo * Result = &This->FloatingPosition((int) *x,(int) *y);
+ wxAuiPaneInfo * Result = &This->FloatingPosition(*x,*y);
rt.addRef(getRef((void *)Result,memenv), "wxAuiPaneInfo");
break;
}
@@ -24076,7 +24113,7 @@ case wxAuiPaneInfo_FloatingSize_2: { // wxAuiPaneInfo::FloatingSize
int * x = (int *) bp; bp += 4;
int * y = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxAuiPaneInfo * Result = &This->FloatingSize((int) *x,(int) *y);
+ wxAuiPaneInfo * Result = &This->FloatingSize(*x,*y);
rt.addRef(getRef((void *)Result,memenv), "wxAuiPaneInfo");
break;
}
@@ -24133,7 +24170,7 @@ case wxAuiPaneInfo_HasFlag: { // wxAuiPaneInfo::HasFlag
wxAuiPaneInfo *This = (wxAuiPaneInfo *) getPtr(bp,memenv); bp += 4;
unsigned int * flag = (unsigned int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->HasFlag((int) *flag);
+ bool Result = This->HasFlag(*flag);
rt.addBool(Result);
break;
}
@@ -24274,7 +24311,7 @@ case wxAuiPaneInfo_Layer: { // wxAuiPaneInfo::Layer
wxAuiPaneInfo *This = (wxAuiPaneInfo *) getPtr(bp,memenv); bp += 4;
int * layer = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxAuiPaneInfo * Result = &This->Layer((int) *layer);
+ wxAuiPaneInfo * Result = &This->Layer(*layer);
rt.addRef(getRef((void *)Result,memenv), "wxAuiPaneInfo");
break;
}
@@ -24314,7 +24351,7 @@ case wxAuiPaneInfo_MaxSize_2: { // wxAuiPaneInfo::MaxSize
int * x = (int *) bp; bp += 4;
int * y = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxAuiPaneInfo * Result = &This->MaxSize((int) *x,(int) *y);
+ wxAuiPaneInfo * Result = &This->MaxSize(*x,*y);
rt.addRef(getRef((void *)Result,memenv), "wxAuiPaneInfo");
break;
}
@@ -24347,7 +24384,7 @@ case wxAuiPaneInfo_MinSize_2: { // wxAuiPaneInfo::MinSize
int * x = (int *) bp; bp += 4;
int * y = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxAuiPaneInfo * Result = &This->MinSize((int) *x,(int) *y);
+ wxAuiPaneInfo * Result = &This->MinSize(*x,*y);
rt.addRef(getRef((void *)Result,memenv), "wxAuiPaneInfo");
break;
}
@@ -24421,7 +24458,7 @@ case wxAuiPaneInfo_Position: { // wxAuiPaneInfo::Position
wxAuiPaneInfo *This = (wxAuiPaneInfo *) getPtr(bp,memenv); bp += 4;
int * pos = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxAuiPaneInfo * Result = &This->Position((int) *pos);
+ wxAuiPaneInfo * Result = &This->Position(*pos);
rt.addRef(getRef((void *)Result,memenv), "wxAuiPaneInfo");
break;
}
@@ -24464,7 +24501,7 @@ case wxAuiPaneInfo_Row: { // wxAuiPaneInfo::Row
wxAuiPaneInfo *This = (wxAuiPaneInfo *) getPtr(bp,memenv); bp += 4;
int * row = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxAuiPaneInfo * Result = &This->Row((int) *row);
+ wxAuiPaneInfo * Result = &This->Row(*row);
rt.addRef(getRef((void *)Result,memenv), "wxAuiPaneInfo");
break;
}
@@ -24480,7 +24517,7 @@ case wxAuiPaneInfo_SetFlag: { // wxAuiPaneInfo::SetFlag
unsigned int * flag = (unsigned int *) bp; bp += 4;
bool * option_state = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxAuiPaneInfo * Result = &This->SetFlag((int) *flag,(bool) *option_state);
+ wxAuiPaneInfo * Result = &This->SetFlag(*flag,*option_state);
rt.addRef(getRef((void *)Result,memenv), "wxAuiPaneInfo");
break;
}
@@ -24631,7 +24668,7 @@ case wxAuiNotebook_DeletePage: { // wxAuiNotebook::DeletePage
wxAuiNotebook *This = (wxAuiNotebook *) getPtr(bp,memenv); bp += 4;
int * page = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->DeletePage((size_t) *page);
+ bool Result = This->DeletePage(*page);
rt.addBool(Result);
break;
}
@@ -24646,7 +24683,7 @@ case wxAuiNotebook_GetPage: { // wxAuiNotebook::GetPage
wxAuiNotebook *This = (wxAuiNotebook *) getPtr(bp,memenv); bp += 4;
int * page_idx = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxWindow * Result = (wxWindow*)This->GetPage((size_t) *page_idx);
+ wxWindow * Result = (wxWindow*)This->GetPage(*page_idx);
rt.addRef(getRef((void *)Result,memenv), "wxWindow");
break;
}
@@ -24654,7 +24691,7 @@ case wxAuiNotebook_GetPageBitmap: { // wxAuiNotebook::GetPageBitmap
wxAuiNotebook *This = (wxAuiNotebook *) getPtr(bp,memenv); bp += 4;
int * page_idx = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxBitmap * Result = new wxBitmap(This->GetPageBitmap((size_t) *page_idx)); newPtr((void *) Result,3, memenv);;
+ wxBitmap * Result = new wxBitmap(This->GetPageBitmap(*page_idx)); newPtr((void *) Result,3, memenv);;
rt.addRef(getRef((void *)Result,memenv), "wxBitmap");
break;
}
@@ -24677,7 +24714,7 @@ case wxAuiNotebook_GetPageText: { // wxAuiNotebook::GetPageText
wxAuiNotebook *This = (wxAuiNotebook *) getPtr(bp,memenv); bp += 4;
int * page_idx = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxString Result = This->GetPageText((size_t) *page_idx);
+ wxString Result = This->GetPageText(*page_idx);
rt.add(Result);
break;
}
@@ -24706,7 +24743,7 @@ bitmap = (wxBitmap *) getPtr(bp,memenv); bp += 4;
} break;
}};
if(!This) throw wxe_badarg(0);
- bool Result = This->InsertPage((size_t) *page_idx,page,caption,select,*bitmap);
+ bool Result = This->InsertPage(*page_idx,page,caption,select,*bitmap);
rt.addBool(Result);
break;
}
@@ -24714,7 +24751,7 @@ case wxAuiNotebook_RemovePage: { // wxAuiNotebook::RemovePage
wxAuiNotebook *This = (wxAuiNotebook *) getPtr(bp,memenv); bp += 4;
int * page = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->RemovePage((size_t) *page);
+ bool Result = This->RemovePage(*page);
rt.addBool(Result);
break;
}
@@ -24738,7 +24775,7 @@ case wxAuiNotebook_SetPageBitmap: { // wxAuiNotebook::SetPageBitmap
int * page = (int *) bp; bp += 4;
wxBitmap *bitmap = (wxBitmap *) getPtr(bp,memenv); bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->SetPageBitmap((size_t) *page,*bitmap);
+ bool Result = This->SetPageBitmap(*page,*bitmap);
rt.addBool(Result);
break;
}
@@ -24749,7 +24786,7 @@ case wxAuiNotebook_SetPageText: { // wxAuiNotebook::SetPageText
wxString text = wxString(bp, wxConvUTF8);
bp += *textLen+((8-((4+ *textLen) & 7)) & 7);
if(!This) throw wxe_badarg(0);
- bool Result = This->SetPageText((size_t) *page,text);
+ bool Result = This->SetPageText(*page,text);
rt.addBool(Result);
break;
}
@@ -24757,7 +24794,7 @@ case wxAuiNotebook_SetSelection: { // wxAuiNotebook::SetSelection
wxAuiNotebook *This = (wxAuiNotebook *) getPtr(bp,memenv); bp += 4;
int * new_page = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- size_t Result = This->SetSelection((size_t) *new_page);
+ size_t Result = This->SetSelection(*new_page);
rt.addInt(Result);
break;
}
@@ -24765,7 +24802,7 @@ case wxAuiNotebook_SetTabCtrlHeight: { // wxAuiNotebook::SetTabCtrlHeight
wxAuiNotebook *This = (wxAuiNotebook *) getPtr(bp,memenv); bp += 4;
int * height = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetTabCtrlHeight((int) *height);
+ This->SetTabCtrlHeight(*height);
break;
}
case wxAuiNotebook_SetUniformBitmapSize: { // wxAuiNotebook::SetUniformBitmapSize
@@ -24814,7 +24851,7 @@ case wxMDIParentFrame_new_4: { // wxMDIParentFrame::wxMDIParentFrame
style = (long)*(int *) bp; bp += 4;
} break;
}};
- wxMDIParentFrame * Result = new EwxMDIParentFrame(parent,(wxWindowID) *id,title,pos,size,style);
+ wxMDIParentFrame * Result = new EwxMDIParentFrame(parent,*id,title,pos,size,style);
newPtr((void *) Result, 0, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxMDIParentFrame");
break;
@@ -24871,7 +24908,7 @@ case wxMDIParentFrame_Create: { // wxMDIParentFrame::Create
} break;
}};
if(!This) throw wxe_badarg(0);
- bool Result = This->Create(parent,(wxWindowID) *id,title,pos,size,style);
+ bool Result = This->Create(parent,*id,title,pos,size,style);
rt.addBool(Result);
break;
}
@@ -24899,7 +24936,7 @@ orient = *(wxOrientation *) bp; bp += 4;;
} break;
}};
if(!This) throw wxe_badarg(0);
- This->Tile((wxOrientation) orient);
+ This->Tile(orient);
break;
}
case wxMDIChildFrame_new_0: { // wxMDIChildFrame::wxMDIChildFrame
@@ -24934,7 +24971,7 @@ case wxMDIChildFrame_new_4: { // wxMDIChildFrame::wxMDIChildFrame
style = (long)*(int *) bp; bp += 4;
} break;
}};
- wxMDIChildFrame * Result = new EwxMDIChildFrame(parent,(wxWindowID) *id,title,pos,size,style);
+ wxMDIChildFrame * Result = new EwxMDIChildFrame(parent,*id,title,pos,size,style);
newPtr((void *) Result, 0, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxMDIChildFrame");
break;
@@ -24973,7 +25010,7 @@ case wxMDIChildFrame_Create: { // wxMDIChildFrame::Create
} break;
}};
if(!This) throw wxe_badarg(0);
- bool Result = This->Create(parent,(wxWindowID) *id,title,pos,size,style);
+ bool Result = This->Create(parent,*id,title,pos,size,style);
rt.addBool(Result);
break;
}
@@ -25002,6 +25039,7 @@ case wxMDIClientWindow_new_0: { // wxMDIClientWindow::wxMDIClientWindow
rt.addRef(getRef((void *)Result,memenv), "wxMDIClientWindow");
break;
}
+#if !wxCHECK_VERSION(2,9,0)
case wxMDIClientWindow_new_2: { // wxMDIClientWindow::wxMDIClientWindow
long style=0;
wxMDIParentFrame *parent = (wxMDIParentFrame *) getPtr(bp,memenv); bp += 4;
@@ -25016,6 +25054,7 @@ case wxMDIClientWindow_new_2: { // wxMDIClientWindow::wxMDIClientWindow
rt.addRef(getRef((void *)Result,memenv), "wxMDIClientWindow");
break;
}
+#endif
case wxMDIClientWindow_CreateClient: { // wxMDIClientWindow::CreateClient
long style=wxVSCROLL|wxHSCROLL;
wxMDIClientWindow *This = (wxMDIClientWindow *) getPtr(bp,memenv); bp += 4;
@@ -25115,7 +25154,7 @@ case wxEvent_ResumePropagation: { // wxEvent::ResumePropagation
wxEvent *This = (wxEvent *) getPtr(bp,memenv); bp += 4;
int * propagationLevel = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->ResumePropagation((int) *propagationLevel);
+ This->ResumePropagation(*propagationLevel);
break;
}
case wxEvent_ShouldPropagate: { // wxEvent::ShouldPropagate
@@ -25198,7 +25237,7 @@ case wxCommandEvent_SetInt: { // wxCommandEvent::SetInt
wxCommandEvent *This = (wxCommandEvent *) getPtr(bp,memenv); bp += 4;
int * i = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetInt((int) *i);
+ This->SetInt(*i);
break;
}
case wxCommandEvent_SetString: { // wxCommandEvent::SetString
@@ -25249,7 +25288,7 @@ case wxMouseEvent_Button: { // wxMouseEvent::Button
wxMouseEvent *This = (wxMouseEvent *) getPtr(bp,memenv); bp += 4;
int * but = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->Button((int) *but);
+ bool Result = This->Button(*but);
rt.addBool(Result);
break;
}
@@ -25713,14 +25752,14 @@ case wxCloseEvent_SetCanVeto: { // wxCloseEvent::SetCanVeto
wxCloseEvent *This = (wxCloseEvent *) getPtr(bp,memenv); bp += 4;
bool * canVeto = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetCanVeto((bool) *canVeto);
+ This->SetCanVeto(*canVeto);
break;
}
case wxCloseEvent_SetLoggingOff: { // wxCloseEvent::SetLoggingOff
wxCloseEvent *This = (wxCloseEvent *) getPtr(bp,memenv); bp += 4;
bool * logOff = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetLoggingOff((bool) *logOff);
+ This->SetLoggingOff(*logOff);
break;
}
case wxCloseEvent_Veto: { // wxCloseEvent::Veto
@@ -25740,7 +25779,7 @@ case wxShowEvent_SetShow: { // wxShowEvent::SetShow
wxShowEvent *This = (wxShowEvent *) getPtr(bp,memenv); bp += 4;
bool * show = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetShow((bool) *show);
+ This->SetShow(*show);
break;
}
case wxShowEvent_GetShow: { // wxShowEvent::GetShow
@@ -25865,21 +25904,21 @@ case wxUpdateUIEvent_Check: { // wxUpdateUIEvent::Check
wxUpdateUIEvent *This = (wxUpdateUIEvent *) getPtr(bp,memenv); bp += 4;
bool * check = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->Check((bool) *check);
+ This->Check(*check);
break;
}
case wxUpdateUIEvent_Enable: { // wxUpdateUIEvent::Enable
wxUpdateUIEvent *This = (wxUpdateUIEvent *) getPtr(bp,memenv); bp += 4;
bool * enable = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->Enable((bool) *enable);
+ This->Enable(*enable);
break;
}
case wxUpdateUIEvent_Show: { // wxUpdateUIEvent::Show
wxUpdateUIEvent *This = (wxUpdateUIEvent *) getPtr(bp,memenv); bp += 4;
bool * show = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->Show((bool) *show);
+ This->Show(*show);
break;
}
case wxUpdateUIEvent_GetChecked: { // wxUpdateUIEvent::GetChecked
@@ -25954,7 +25993,7 @@ case wxUpdateUIEvent_ResetUpdateTime: { // wxUpdateUIEvent::ResetUpdateTime
}
case wxUpdateUIEvent_SetMode: { // wxUpdateUIEvent::SetMode
wxUpdateUIMode mode = *(wxUpdateUIMode *) bp; bp += 4;;
- wxUpdateUIEvent::SetMode((wxUpdateUIMode) mode);
+ wxUpdateUIEvent::SetMode(mode);
break;
}
case wxUpdateUIEvent_SetText: { // wxUpdateUIEvent::SetText
@@ -25968,7 +26007,7 @@ case wxUpdateUIEvent_SetText: { // wxUpdateUIEvent::SetText
}
case wxUpdateUIEvent_SetUpdateInterval: { // wxUpdateUIEvent::SetUpdateInterval
int * updateInterval = (int *) bp; bp += 4;
- wxUpdateUIEvent::SetUpdateInterval((long) *updateInterval);
+ wxUpdateUIEvent::SetUpdateInterval(*updateInterval);
break;
}
case wxMouseCaptureChangedEvent_GetCapturedWindow: { // wxMouseCaptureChangedEvent::GetCapturedWindow
@@ -25996,7 +26035,7 @@ case wxQueryNewPaletteEvent_SetPaletteRealized: { // wxQueryNewPaletteEvent::Set
wxQueryNewPaletteEvent *This = (wxQueryNewPaletteEvent *) getPtr(bp,memenv); bp += 4;
bool * realized = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetPaletteRealized((bool) *realized);
+ This->SetPaletteRealized(*realized);
break;
}
case wxQueryNewPaletteEvent_GetPaletteRealized: { // wxQueryNewPaletteEvent::GetPaletteRealized
@@ -26017,7 +26056,7 @@ case wxNavigationKeyEvent_SetDirection: { // wxNavigationKeyEvent::SetDirection
wxNavigationKeyEvent *This = (wxNavigationKeyEvent *) getPtr(bp,memenv); bp += 4;
bool * bForward = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetDirection((bool) *bForward);
+ This->SetDirection(*bForward);
break;
}
case wxNavigationKeyEvent_IsWindowChange: { // wxNavigationKeyEvent::IsWindowChange
@@ -26031,7 +26070,7 @@ case wxNavigationKeyEvent_SetWindowChange: { // wxNavigationKeyEvent::SetWindowC
wxNavigationKeyEvent *This = (wxNavigationKeyEvent *) getPtr(bp,memenv); bp += 4;
bool * bIs = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetWindowChange((bool) *bIs);
+ This->SetWindowChange(*bIs);
break;
}
case wxNavigationKeyEvent_IsFromTab: { // wxNavigationKeyEvent::IsFromTab
@@ -26045,7 +26084,7 @@ case wxNavigationKeyEvent_SetFromTab: { // wxNavigationKeyEvent::SetFromTab
wxNavigationKeyEvent *This = (wxNavigationKeyEvent *) getPtr(bp,memenv); bp += 4;
bool * bIs = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetFromTab((bool) *bIs);
+ This->SetFromTab(*bIs);
break;
}
case wxNavigationKeyEvent_GetCurrentFocus: { // wxNavigationKeyEvent::GetCurrentFocus
@@ -26080,7 +26119,7 @@ case wxHelpEvent_SetOrigin: { // wxHelpEvent::SetOrigin
wxHelpEvent *This = (wxHelpEvent *) getPtr(bp,memenv); bp += 4;
wxHelpEvent::Origin origin = *(wxHelpEvent::Origin *) bp; bp += 4;;
if(!This) throw wxe_badarg(0);
- This->SetOrigin((wxHelpEvent::Origin) origin);
+ This->SetOrigin(origin);
break;
}
case wxHelpEvent_SetPosition: { // wxHelpEvent::SetPosition
@@ -26108,12 +26147,14 @@ case wxContextMenuEvent_SetPosition: { // wxContextMenuEvent::SetPosition
This->SetPosition(pos);
break;
}
+#if !wxCHECK_VERSION(2,9,0)
case wxIdleEvent_CanSend: { // wxIdleEvent::CanSend
wxWindow *win = (wxWindow *) getPtr(bp,memenv); bp += 4;
bool Result = wxIdleEvent::CanSend(win);
rt.addBool(Result);
break;
}
+#endif
case wxIdleEvent_GetMode: { // wxIdleEvent::GetMode
int Result = wxIdleEvent::GetMode();
rt.addInt(Result);
@@ -26141,7 +26182,7 @@ case wxIdleEvent_MoreRequested: { // wxIdleEvent::MoreRequested
}
case wxIdleEvent_SetMode: { // wxIdleEvent::SetMode
wxIdleMode mode = *(wxIdleMode *) bp; bp += 4;;
- wxIdleEvent::SetMode((wxIdleMode) mode);
+ wxIdleEvent::SetMode(mode);
break;
}
case wxGridEvent_AltDown: { // wxGridEvent::AltDown
@@ -26529,7 +26570,7 @@ case wxStyledTextEvent_GetAlt: { // wxStyledTextEvent::GetAlt
}
case utils_wxGetKeyState: { // utils::wxGetKeyState
wxKeyCode key = *(wxKeyCode *) bp; bp += 4;;
- bool Result = ::wxGetKeyState((wxKeyCode) key);
+ bool Result = ::wxGetKeyState(key);
rt.addBool(Result);
break;
}
@@ -26549,7 +26590,7 @@ case utils_wxGetMouseState: { // utils::wxGetMouseState
}
case utils_wxSetDetectableAutoRepeat: { // utils::wxSetDetectableAutoRepeat
bool * flag = (bool *) bp; bp += 4;
- bool Result = ::wxSetDetectableAutoRepeat((bool) *flag);
+ bool Result = ::wxSetDetectableAutoRepeat(*flag);
rt.addBool(Result);
break;
}
@@ -26606,7 +26647,7 @@ case utils_wxIsBusy: { // utils::wxIsBusy
}
case utils_wxShutdown: { // utils::wxShutdown
wxShutdownFlags wFlags = *(wxShutdownFlags *) bp; bp += 4;;
- bool Result = ::wxShutdown((wxShutdownFlags) wFlags);
+ bool Result = ::wxShutdown(wFlags);
rt.addBool(Result);
break;
}
@@ -26659,7 +26700,7 @@ case utils_wxNewId: { // utils::wxNewId
}
case utils_wxRegisterId: { // utils::wxRegisterId
int * id = (int *) bp; bp += 4;
- ::wxRegisterId((long) *id);
+ ::wxRegisterId(*id);
break;
}
case utils_wxGetCurrentId: { // utils::wxGetCurrentId
@@ -26862,7 +26903,7 @@ case wxPrintout_SetLogicalOrigin: { // wxPrintout::SetLogicalOrigin
int * x = (int *) bp; bp += 4;
int * y = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetLogicalOrigin((wxCoord) *x,(wxCoord) *y);
+ This->SetLogicalOrigin(*x,*y);
break;
}
case wxPrintout_OffsetLogicalOrigin: { // wxPrintout::OffsetLogicalOrigin
@@ -26870,7 +26911,7 @@ case wxPrintout_OffsetLogicalOrigin: { // wxPrintout::OffsetLogicalOrigin
int * xoff = (int *) bp; bp += 4;
int * yoff = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->OffsetLogicalOrigin((wxCoord) *xoff,(wxCoord) *yoff);
+ This->OffsetLogicalOrigin(*xoff,*yoff);
break;
}
case wxStyledTextCtrl_new_2: { // wxStyledTextCtrl::wxStyledTextCtrl
@@ -26966,7 +27007,7 @@ case wxStyledTextCtrl_InsertText: { // wxStyledTextCtrl::InsertText
wxString text = wxString(bp, wxConvUTF8);
bp += *textLen+((8-((4+ *textLen) & 7)) & 7);
if(!This) throw wxe_badarg(0);
- This->InsertText((int) *pos,text);
+ This->InsertText(*pos,text);
break;
}
case wxStyledTextCtrl_ClearAll: { // wxStyledTextCtrl::ClearAll
@@ -26992,7 +27033,7 @@ case wxStyledTextCtrl_GetCharAt: { // wxStyledTextCtrl::GetCharAt
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * pos = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->GetCharAt((int) *pos);
+ int Result = This->GetCharAt(*pos);
rt.addInt(Result);
break;
}
@@ -27014,7 +27055,7 @@ case wxStyledTextCtrl_GetStyleAt: { // wxStyledTextCtrl::GetStyleAt
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * pos = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->GetStyleAt((int) *pos);
+ int Result = This->GetStyleAt(*pos);
rt.addInt(Result);
break;
}
@@ -27028,7 +27069,7 @@ case wxStyledTextCtrl_SetUndoCollection: { // wxStyledTextCtrl::SetUndoCollectio
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
bool * collectUndo = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetUndoCollection((bool) *collectUndo);
+ This->SetUndoCollection(*collectUndo);
break;
}
case wxStyledTextCtrl_SelectAll: { // wxStyledTextCtrl::SelectAll
@@ -27048,7 +27089,7 @@ case wxStyledTextCtrl_GetStyledText: { // wxStyledTextCtrl::GetStyledText
int * startPos = (int *) bp; bp += 4;
int * endPos = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxMemoryBuffer * Result = new wxMemoryBuffer(This->GetStyledText((int) *startPos,(int) *endPos)); newPtr((void *) Result,3, memenv);;
+ wxMemoryBuffer * Result = new wxMemoryBuffer(This->GetStyledText(*startPos,*endPos)); newPtr((void *) Result,3, memenv);;
rt.addRef(getRef((void *)Result,memenv), "wxMemoryBuffer");
break;
}
@@ -27063,7 +27104,7 @@ case wxStyledTextCtrl_MarkerLineFromHandle: { // wxStyledTextCtrl::MarkerLineFro
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * handle = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->MarkerLineFromHandle((int) *handle);
+ int Result = This->MarkerLineFromHandle(*handle);
rt.addInt(Result);
break;
}
@@ -27071,7 +27112,7 @@ case wxStyledTextCtrl_MarkerDeleteHandle: { // wxStyledTextCtrl::MarkerDeleteHan
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * handle = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->MarkerDeleteHandle((int) *handle);
+ This->MarkerDeleteHandle(*handle);
break;
}
case wxStyledTextCtrl_GetUndoCollection: { // wxStyledTextCtrl::GetUndoCollection
@@ -27092,7 +27133,7 @@ case wxStyledTextCtrl_SetViewWhiteSpace: { // wxStyledTextCtrl::SetViewWhiteSpac
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * viewWS = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetViewWhiteSpace((int) *viewWS);
+ This->SetViewWhiteSpace(*viewWS);
break;
}
case wxStyledTextCtrl_PositionFromPoint: { // wxStyledTextCtrl::PositionFromPoint
@@ -27110,7 +27151,7 @@ case wxStyledTextCtrl_PositionFromPointClose: { // wxStyledTextCtrl::PositionFro
int * x = (int *) bp; bp += 4;
int * y = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->PositionFromPointClose((int) *x,(int) *y);
+ int Result = This->PositionFromPointClose(*x,*y);
rt.addInt(Result);
break;
}
@@ -27118,21 +27159,21 @@ case wxStyledTextCtrl_GotoLine: { // wxStyledTextCtrl::GotoLine
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * line = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->GotoLine((int) *line);
+ This->GotoLine(*line);
break;
}
case wxStyledTextCtrl_GotoPos: { // wxStyledTextCtrl::GotoPos
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * pos = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->GotoPos((int) *pos);
+ This->GotoPos(*pos);
break;
}
case wxStyledTextCtrl_SetAnchor: { // wxStyledTextCtrl::SetAnchor
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * posAnchor = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetAnchor((int) *posAnchor);
+ This->SetAnchor(*posAnchor);
break;
}
case wxStyledTextCtrl_GetCurLine: { // wxStyledTextCtrl::GetCurLine
@@ -27156,7 +27197,7 @@ case wxStyledTextCtrl_ConvertEOLs: { // wxStyledTextCtrl::ConvertEOLs
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * eolMode = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->ConvertEOLs((int) *eolMode);
+ This->ConvertEOLs(*eolMode);
break;
}
case wxStyledTextCtrl_GetEOLMode: { // wxStyledTextCtrl::GetEOLMode
@@ -27170,7 +27211,7 @@ case wxStyledTextCtrl_SetEOLMode: { // wxStyledTextCtrl::SetEOLMode
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * eolMode = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetEOLMode((int) *eolMode);
+ This->SetEOLMode(*eolMode);
break;
}
case wxStyledTextCtrl_StartStyling: { // wxStyledTextCtrl::StartStyling
@@ -27178,7 +27219,7 @@ case wxStyledTextCtrl_StartStyling: { // wxStyledTextCtrl::StartStyling
int * pos = (int *) bp; bp += 4;
int * mask = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->StartStyling((int) *pos,(int) *mask);
+ This->StartStyling(*pos,*mask);
break;
}
case wxStyledTextCtrl_SetStyling: { // wxStyledTextCtrl::SetStyling
@@ -27186,7 +27227,7 @@ case wxStyledTextCtrl_SetStyling: { // wxStyledTextCtrl::SetStyling
int * length = (int *) bp; bp += 4;
int * style = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetStyling((int) *length,(int) *style);
+ This->SetStyling(*length,*style);
break;
}
case wxStyledTextCtrl_GetBufferedDraw: { // wxStyledTextCtrl::GetBufferedDraw
@@ -27200,14 +27241,14 @@ case wxStyledTextCtrl_SetBufferedDraw: { // wxStyledTextCtrl::SetBufferedDraw
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
bool * buffered = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetBufferedDraw((bool) *buffered);
+ This->SetBufferedDraw(*buffered);
break;
}
case wxStyledTextCtrl_SetTabWidth: { // wxStyledTextCtrl::SetTabWidth
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * tabWidth = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetTabWidth((int) *tabWidth);
+ This->SetTabWidth(*tabWidth);
break;
}
case wxStyledTextCtrl_GetTabWidth: { // wxStyledTextCtrl::GetTabWidth
@@ -27221,7 +27262,7 @@ case wxStyledTextCtrl_SetCodePage: { // wxStyledTextCtrl::SetCodePage
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * codePage = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetCodePage((int) *codePage);
+ This->SetCodePage(*codePage);
break;
}
case wxStyledTextCtrl_MarkerDefine: { // wxStyledTextCtrl::MarkerDefine
@@ -27250,7 +27291,7 @@ case wxStyledTextCtrl_MarkerDefine: { // wxStyledTextCtrl::MarkerDefine
} break;
}};
if(!This) throw wxe_badarg(0);
- This->MarkerDefine((int) *markerNumber,(int) *markerSymbol,foreground,background);
+ This->MarkerDefine(*markerNumber,*markerSymbol,foreground,background);
break;
}
case wxStyledTextCtrl_MarkerSetForeground: { // wxStyledTextCtrl::MarkerSetForeground
@@ -27262,7 +27303,7 @@ case wxStyledTextCtrl_MarkerSetForeground: { // wxStyledTextCtrl::MarkerSetForeg
int * foreA = (int *) bp; bp += 4;
wxColour fore = wxColour(*foreR,*foreG,*foreB,*foreA);
if(!This) throw wxe_badarg(0);
- This->MarkerSetForeground((int) *markerNumber,fore);
+ This->MarkerSetForeground(*markerNumber,fore);
break;
}
case wxStyledTextCtrl_MarkerSetBackground: { // wxStyledTextCtrl::MarkerSetBackground
@@ -27274,7 +27315,7 @@ case wxStyledTextCtrl_MarkerSetBackground: { // wxStyledTextCtrl::MarkerSetBackg
int * backA = (int *) bp; bp += 4;
wxColour back = wxColour(*backR,*backG,*backB,*backA);
if(!This) throw wxe_badarg(0);
- This->MarkerSetBackground((int) *markerNumber,back);
+ This->MarkerSetBackground(*markerNumber,back);
break;
}
case wxStyledTextCtrl_MarkerAdd: { // wxStyledTextCtrl::MarkerAdd
@@ -27282,7 +27323,7 @@ case wxStyledTextCtrl_MarkerAdd: { // wxStyledTextCtrl::MarkerAdd
int * line = (int *) bp; bp += 4;
int * markerNumber = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->MarkerAdd((int) *line,(int) *markerNumber);
+ int Result = This->MarkerAdd(*line,*markerNumber);
rt.addInt(Result);
break;
}
@@ -27291,21 +27332,21 @@ case wxStyledTextCtrl_MarkerDelete: { // wxStyledTextCtrl::MarkerDelete
int * line = (int *) bp; bp += 4;
int * markerNumber = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->MarkerDelete((int) *line,(int) *markerNumber);
+ This->MarkerDelete(*line,*markerNumber);
break;
}
case wxStyledTextCtrl_MarkerDeleteAll: { // wxStyledTextCtrl::MarkerDeleteAll
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * markerNumber = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->MarkerDeleteAll((int) *markerNumber);
+ This->MarkerDeleteAll(*markerNumber);
break;
}
case wxStyledTextCtrl_MarkerGet: { // wxStyledTextCtrl::MarkerGet
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * line = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->MarkerGet((int) *line);
+ int Result = This->MarkerGet(*line);
rt.addInt(Result);
break;
}
@@ -27314,7 +27355,7 @@ case wxStyledTextCtrl_MarkerNext: { // wxStyledTextCtrl::MarkerNext
int * lineStart = (int *) bp; bp += 4;
int * markerMask = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->MarkerNext((int) *lineStart,(int) *markerMask);
+ int Result = This->MarkerNext(*lineStart,*markerMask);
rt.addInt(Result);
break;
}
@@ -27323,7 +27364,7 @@ case wxStyledTextCtrl_MarkerPrevious: { // wxStyledTextCtrl::MarkerPrevious
int * lineStart = (int *) bp; bp += 4;
int * markerMask = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->MarkerPrevious((int) *lineStart,(int) *markerMask);
+ int Result = This->MarkerPrevious(*lineStart,*markerMask);
rt.addInt(Result);
break;
}
@@ -27332,7 +27373,7 @@ case wxStyledTextCtrl_MarkerDefineBitmap: { // wxStyledTextCtrl::MarkerDefineBit
int * markerNumber = (int *) bp; bp += 4;
wxBitmap *bmp = (wxBitmap *) getPtr(bp,memenv); bp += 4;
if(!This) throw wxe_badarg(0);
- This->MarkerDefineBitmap((int) *markerNumber,*bmp);
+ This->MarkerDefineBitmap(*markerNumber,*bmp);
break;
}
case wxStyledTextCtrl_MarkerAddSet: { // wxStyledTextCtrl::MarkerAddSet
@@ -27340,7 +27381,7 @@ case wxStyledTextCtrl_MarkerAddSet: { // wxStyledTextCtrl::MarkerAddSet
int * line = (int *) bp; bp += 4;
int * set = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->MarkerAddSet((int) *line,(int) *set);
+ This->MarkerAddSet(*line,*set);
break;
}
case wxStyledTextCtrl_MarkerSetAlpha: { // wxStyledTextCtrl::MarkerSetAlpha
@@ -27348,7 +27389,7 @@ case wxStyledTextCtrl_MarkerSetAlpha: { // wxStyledTextCtrl::MarkerSetAlpha
int * markerNumber = (int *) bp; bp += 4;
int * alpha = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->MarkerSetAlpha((int) *markerNumber,(int) *alpha);
+ This->MarkerSetAlpha(*markerNumber,*alpha);
break;
}
case wxStyledTextCtrl_SetMarginType: { // wxStyledTextCtrl::SetMarginType
@@ -27356,14 +27397,14 @@ case wxStyledTextCtrl_SetMarginType: { // wxStyledTextCtrl::SetMarginType
int * margin = (int *) bp; bp += 4;
int * marginType = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetMarginType((int) *margin,(int) *marginType);
+ This->SetMarginType(*margin,*marginType);
break;
}
case wxStyledTextCtrl_GetMarginType: { // wxStyledTextCtrl::GetMarginType
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * margin = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->GetMarginType((int) *margin);
+ int Result = This->GetMarginType(*margin);
rt.addInt(Result);
break;
}
@@ -27372,14 +27413,14 @@ case wxStyledTextCtrl_SetMarginWidth: { // wxStyledTextCtrl::SetMarginWidth
int * margin = (int *) bp; bp += 4;
int * pixelWidth = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetMarginWidth((int) *margin,(int) *pixelWidth);
+ This->SetMarginWidth(*margin,*pixelWidth);
break;
}
case wxStyledTextCtrl_GetMarginWidth: { // wxStyledTextCtrl::GetMarginWidth
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * margin = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->GetMarginWidth((int) *margin);
+ int Result = This->GetMarginWidth(*margin);
rt.addInt(Result);
break;
}
@@ -27388,14 +27429,14 @@ case wxStyledTextCtrl_SetMarginMask: { // wxStyledTextCtrl::SetMarginMask
int * margin = (int *) bp; bp += 4;
int * mask = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetMarginMask((int) *margin,(int) *mask);
+ This->SetMarginMask(*margin,*mask);
break;
}
case wxStyledTextCtrl_GetMarginMask: { // wxStyledTextCtrl::GetMarginMask
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * margin = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->GetMarginMask((int) *margin);
+ int Result = This->GetMarginMask(*margin);
rt.addInt(Result);
break;
}
@@ -27404,14 +27445,14 @@ case wxStyledTextCtrl_SetMarginSensitive: { // wxStyledTextCtrl::SetMarginSensit
int * margin = (int *) bp; bp += 4;
bool * sensitive = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetMarginSensitive((int) *margin,(bool) *sensitive);
+ This->SetMarginSensitive(*margin,*sensitive);
break;
}
case wxStyledTextCtrl_GetMarginSensitive: { // wxStyledTextCtrl::GetMarginSensitive
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * margin = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->GetMarginSensitive((int) *margin);
+ bool Result = This->GetMarginSensitive(*margin);
rt.addBool(Result);
break;
}
@@ -27430,7 +27471,7 @@ case wxStyledTextCtrl_StyleSetForeground: { // wxStyledTextCtrl::StyleSetForegro
int * foreA = (int *) bp; bp += 4;
wxColour fore = wxColour(*foreR,*foreG,*foreB,*foreA);
if(!This) throw wxe_badarg(0);
- This->StyleSetForeground((int) *style,fore);
+ This->StyleSetForeground(*style,fore);
break;
}
case wxStyledTextCtrl_StyleSetBackground: { // wxStyledTextCtrl::StyleSetBackground
@@ -27442,7 +27483,7 @@ case wxStyledTextCtrl_StyleSetBackground: { // wxStyledTextCtrl::StyleSetBackgro
int * backA = (int *) bp; bp += 4;
wxColour back = wxColour(*backR,*backG,*backB,*backA);
if(!This) throw wxe_badarg(0);
- This->StyleSetBackground((int) *style,back);
+ This->StyleSetBackground(*style,back);
break;
}
case wxStyledTextCtrl_StyleSetBold: { // wxStyledTextCtrl::StyleSetBold
@@ -27450,7 +27491,7 @@ case wxStyledTextCtrl_StyleSetBold: { // wxStyledTextCtrl::StyleSetBold
int * style = (int *) bp; bp += 4;
bool * bold = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->StyleSetBold((int) *style,(bool) *bold);
+ This->StyleSetBold(*style,*bold);
break;
}
case wxStyledTextCtrl_StyleSetItalic: { // wxStyledTextCtrl::StyleSetItalic
@@ -27458,7 +27499,7 @@ case wxStyledTextCtrl_StyleSetItalic: { // wxStyledTextCtrl::StyleSetItalic
int * style = (int *) bp; bp += 4;
bool * italic = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->StyleSetItalic((int) *style,(bool) *italic);
+ This->StyleSetItalic(*style,*italic);
break;
}
case wxStyledTextCtrl_StyleSetSize: { // wxStyledTextCtrl::StyleSetSize
@@ -27466,7 +27507,7 @@ case wxStyledTextCtrl_StyleSetSize: { // wxStyledTextCtrl::StyleSetSize
int * style = (int *) bp; bp += 4;
int * sizePoints = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->StyleSetSize((int) *style,(int) *sizePoints);
+ This->StyleSetSize(*style,*sizePoints);
break;
}
case wxStyledTextCtrl_StyleSetFaceName: { // wxStyledTextCtrl::StyleSetFaceName
@@ -27476,7 +27517,7 @@ case wxStyledTextCtrl_StyleSetFaceName: { // wxStyledTextCtrl::StyleSetFaceName
wxString fontName = wxString(bp, wxConvUTF8);
bp += *fontNameLen+((8-((4+ *fontNameLen) & 7)) & 7);
if(!This) throw wxe_badarg(0);
- This->StyleSetFaceName((int) *style,fontName);
+ This->StyleSetFaceName(*style,fontName);
break;
}
case wxStyledTextCtrl_StyleSetEOLFilled: { // wxStyledTextCtrl::StyleSetEOLFilled
@@ -27484,7 +27525,7 @@ case wxStyledTextCtrl_StyleSetEOLFilled: { // wxStyledTextCtrl::StyleSetEOLFille
int * style = (int *) bp; bp += 4;
bool * filled = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->StyleSetEOLFilled((int) *style,(bool) *filled);
+ This->StyleSetEOLFilled(*style,*filled);
break;
}
case wxStyledTextCtrl_StyleResetDefault: { // wxStyledTextCtrl::StyleResetDefault
@@ -27498,7 +27539,7 @@ case wxStyledTextCtrl_StyleSetUnderline: { // wxStyledTextCtrl::StyleSetUnderlin
int * style = (int *) bp; bp += 4;
bool * underline = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->StyleSetUnderline((int) *style,(bool) *underline);
+ This->StyleSetUnderline(*style,*underline);
break;
}
case wxStyledTextCtrl_StyleSetCase: { // wxStyledTextCtrl::StyleSetCase
@@ -27506,7 +27547,7 @@ case wxStyledTextCtrl_StyleSetCase: { // wxStyledTextCtrl::StyleSetCase
int * style = (int *) bp; bp += 4;
int * caseForce = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->StyleSetCase((int) *style,(int) *caseForce);
+ This->StyleSetCase(*style,*caseForce);
break;
}
case wxStyledTextCtrl_StyleSetHotSpot: { // wxStyledTextCtrl::StyleSetHotSpot
@@ -27514,7 +27555,7 @@ case wxStyledTextCtrl_StyleSetHotSpot: { // wxStyledTextCtrl::StyleSetHotSpot
int * style = (int *) bp; bp += 4;
bool * hotspot = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->StyleSetHotSpot((int) *style,(bool) *hotspot);
+ This->StyleSetHotSpot(*style,*hotspot);
break;
}
case wxStyledTextCtrl_SetSelForeground: { // wxStyledTextCtrl::SetSelForeground
@@ -27526,7 +27567,7 @@ case wxStyledTextCtrl_SetSelForeground: { // wxStyledTextCtrl::SetSelForeground
int * foreA = (int *) bp; bp += 4;
wxColour fore = wxColour(*foreR,*foreG,*foreB,*foreA);
if(!This) throw wxe_badarg(0);
- This->SetSelForeground((bool) *useSetting,fore);
+ This->SetSelForeground(*useSetting,fore);
break;
}
case wxStyledTextCtrl_SetSelBackground: { // wxStyledTextCtrl::SetSelBackground
@@ -27538,7 +27579,7 @@ case wxStyledTextCtrl_SetSelBackground: { // wxStyledTextCtrl::SetSelBackground
int * backA = (int *) bp; bp += 4;
wxColour back = wxColour(*backR,*backG,*backB,*backA);
if(!This) throw wxe_badarg(0);
- This->SetSelBackground((bool) *useSetting,back);
+ This->SetSelBackground(*useSetting,back);
break;
}
case wxStyledTextCtrl_GetSelAlpha: { // wxStyledTextCtrl::GetSelAlpha
@@ -27552,7 +27593,7 @@ case wxStyledTextCtrl_SetSelAlpha: { // wxStyledTextCtrl::SetSelAlpha
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * alpha = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetSelAlpha((int) *alpha);
+ This->SetSelAlpha(*alpha);
break;
}
case wxStyledTextCtrl_SetCaretForeground: { // wxStyledTextCtrl::SetCaretForeground
@@ -27572,7 +27613,7 @@ case wxStyledTextCtrl_CmdKeyAssign: { // wxStyledTextCtrl::CmdKeyAssign
int * modifiers = (int *) bp; bp += 4;
int * cmd = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->CmdKeyAssign((int) *key,(int) *modifiers,(int) *cmd);
+ This->CmdKeyAssign(*key,*modifiers,*cmd);
break;
}
case wxStyledTextCtrl_CmdKeyClear: { // wxStyledTextCtrl::CmdKeyClear
@@ -27580,7 +27621,7 @@ case wxStyledTextCtrl_CmdKeyClear: { // wxStyledTextCtrl::CmdKeyClear
int * key = (int *) bp; bp += 4;
int * modifiers = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->CmdKeyClear((int) *key,(int) *modifiers);
+ This->CmdKeyClear(*key,*modifiers);
break;
}
case wxStyledTextCtrl_CmdKeyClearAll: { // wxStyledTextCtrl::CmdKeyClearAll
@@ -27594,7 +27635,7 @@ case wxStyledTextCtrl_SetStyleBytes: { // wxStyledTextCtrl::SetStyleBytes
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * length = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetStyleBytes((int) *length,&styleBytes);
+ This->SetStyleBytes(*length,&styleBytes);
rt.addInt(styleBytes);
break;
}
@@ -27603,7 +27644,7 @@ case wxStyledTextCtrl_StyleSetVisible: { // wxStyledTextCtrl::StyleSetVisible
int * style = (int *) bp; bp += 4;
bool * visible = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->StyleSetVisible((int) *style,(bool) *visible);
+ This->StyleSetVisible(*style,*visible);
break;
}
case wxStyledTextCtrl_GetCaretPeriod: { // wxStyledTextCtrl::GetCaretPeriod
@@ -27617,7 +27658,7 @@ case wxStyledTextCtrl_SetCaretPeriod: { // wxStyledTextCtrl::SetCaretPeriod
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * periodMilliseconds = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetCaretPeriod((int) *periodMilliseconds);
+ This->SetCaretPeriod(*periodMilliseconds);
break;
}
case wxStyledTextCtrl_SetWordChars: { // wxStyledTextCtrl::SetWordChars
@@ -27646,14 +27687,14 @@ case wxStyledTextCtrl_IndicatorSetStyle: { // wxStyledTextCtrl::IndicatorSetStyl
int * indic = (int *) bp; bp += 4;
int * style = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->IndicatorSetStyle((int) *indic,(int) *style);
+ This->IndicatorSetStyle(*indic,*style);
break;
}
case wxStyledTextCtrl_IndicatorGetStyle: { // wxStyledTextCtrl::IndicatorGetStyle
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * indic = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->IndicatorGetStyle((int) *indic);
+ int Result = This->IndicatorGetStyle(*indic);
rt.addInt(Result);
break;
}
@@ -27666,14 +27707,14 @@ case wxStyledTextCtrl_IndicatorSetForeground: { // wxStyledTextCtrl::IndicatorSe
int * foreA = (int *) bp; bp += 4;
wxColour fore = wxColour(*foreR,*foreG,*foreB,*foreA);
if(!This) throw wxe_badarg(0);
- This->IndicatorSetForeground((int) *indic,fore);
+ This->IndicatorSetForeground(*indic,fore);
break;
}
case wxStyledTextCtrl_IndicatorGetForeground: { // wxStyledTextCtrl::IndicatorGetForeground
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * indic = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxColour Result = This->IndicatorGetForeground((int) *indic);
+ wxColour Result = This->IndicatorGetForeground(*indic);
rt.add(Result);
break;
}
@@ -27686,7 +27727,7 @@ case wxStyledTextCtrl_SetWhitespaceForeground: { // wxStyledTextCtrl::SetWhitesp
int * foreA = (int *) bp; bp += 4;
wxColour fore = wxColour(*foreR,*foreG,*foreB,*foreA);
if(!This) throw wxe_badarg(0);
- This->SetWhitespaceForeground((bool) *useSetting,fore);
+ This->SetWhitespaceForeground(*useSetting,fore);
break;
}
case wxStyledTextCtrl_SetWhitespaceBackground: { // wxStyledTextCtrl::SetWhitespaceBackground
@@ -27698,7 +27739,7 @@ case wxStyledTextCtrl_SetWhitespaceBackground: { // wxStyledTextCtrl::SetWhitesp
int * backA = (int *) bp; bp += 4;
wxColour back = wxColour(*backR,*backG,*backB,*backA);
if(!This) throw wxe_badarg(0);
- This->SetWhitespaceBackground((bool) *useSetting,back);
+ This->SetWhitespaceBackground(*useSetting,back);
break;
}
case wxStyledTextCtrl_GetStyleBits: { // wxStyledTextCtrl::GetStyleBits
@@ -27713,14 +27754,14 @@ case wxStyledTextCtrl_SetLineState: { // wxStyledTextCtrl::SetLineState
int * line = (int *) bp; bp += 4;
int * state = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetLineState((int) *line,(int) *state);
+ This->SetLineState(*line,*state);
break;
}
case wxStyledTextCtrl_GetLineState: { // wxStyledTextCtrl::GetLineState
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * line = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->GetLineState((int) *line);
+ int Result = This->GetLineState(*line);
rt.addInt(Result);
break;
}
@@ -27742,7 +27783,7 @@ case wxStyledTextCtrl_SetCaretLineVisible: { // wxStyledTextCtrl::SetCaretLineVi
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
bool * show = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetCaretLineVisible((bool) *show);
+ This->SetCaretLineVisible(*show);
break;
}
case wxStyledTextCtrl_GetCaretLineBackground: { // wxStyledTextCtrl::GetCaretLineBackground
@@ -27770,7 +27811,7 @@ case wxStyledTextCtrl_AutoCompShow: { // wxStyledTextCtrl::AutoCompShow
wxString itemList = wxString(bp, wxConvUTF8);
bp += *itemListLen+((8-((4+ *itemListLen) & 7)) & 7);
if(!This) throw wxe_badarg(0);
- This->AutoCompShow((int) *lenEntered,itemList);
+ This->AutoCompShow(*lenEntered,itemList);
break;
}
case wxStyledTextCtrl_AutoCompCancel: { // wxStyledTextCtrl::AutoCompCancel
@@ -27812,7 +27853,7 @@ case wxStyledTextCtrl_AutoCompSetSeparator: { // wxStyledTextCtrl::AutoCompSetSe
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * separatorCharacter = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->AutoCompSetSeparator((int) *separatorCharacter);
+ This->AutoCompSetSeparator(*separatorCharacter);
break;
}
case wxStyledTextCtrl_AutoCompGetSeparator: { // wxStyledTextCtrl::AutoCompGetSeparator
@@ -27835,7 +27876,7 @@ case wxStyledTextCtrl_AutoCompSetCancelAtStart: { // wxStyledTextCtrl::AutoCompS
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
bool * cancel = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->AutoCompSetCancelAtStart((bool) *cancel);
+ This->AutoCompSetCancelAtStart(*cancel);
break;
}
case wxStyledTextCtrl_AutoCompGetCancelAtStart: { // wxStyledTextCtrl::AutoCompGetCancelAtStart
@@ -27858,7 +27899,7 @@ case wxStyledTextCtrl_AutoCompSetChooseSingle: { // wxStyledTextCtrl::AutoCompSe
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
bool * chooseSingle = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->AutoCompSetChooseSingle((bool) *chooseSingle);
+ This->AutoCompSetChooseSingle(*chooseSingle);
break;
}
case wxStyledTextCtrl_AutoCompGetChooseSingle: { // wxStyledTextCtrl::AutoCompGetChooseSingle
@@ -27872,7 +27913,7 @@ case wxStyledTextCtrl_AutoCompSetIgnoreCase: { // wxStyledTextCtrl::AutoCompSetI
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
bool * ignoreCase = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->AutoCompSetIgnoreCase((bool) *ignoreCase);
+ This->AutoCompSetIgnoreCase(*ignoreCase);
break;
}
case wxStyledTextCtrl_AutoCompGetIgnoreCase: { // wxStyledTextCtrl::AutoCompGetIgnoreCase
@@ -27889,14 +27930,14 @@ case wxStyledTextCtrl_UserListShow: { // wxStyledTextCtrl::UserListShow
wxString itemList = wxString(bp, wxConvUTF8);
bp += *itemListLen+((8-((4+ *itemListLen) & 7)) & 7);
if(!This) throw wxe_badarg(0);
- This->UserListShow((int) *listType,itemList);
+ This->UserListShow(*listType,itemList);
break;
}
case wxStyledTextCtrl_AutoCompSetAutoHide: { // wxStyledTextCtrl::AutoCompSetAutoHide
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
bool * autoHide = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->AutoCompSetAutoHide((bool) *autoHide);
+ This->AutoCompSetAutoHide(*autoHide);
break;
}
case wxStyledTextCtrl_AutoCompGetAutoHide: { // wxStyledTextCtrl::AutoCompGetAutoHide
@@ -27910,7 +27951,7 @@ case wxStyledTextCtrl_AutoCompSetDropRestOfWord: { // wxStyledTextCtrl::AutoComp
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
bool * dropRestOfWord = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->AutoCompSetDropRestOfWord((bool) *dropRestOfWord);
+ This->AutoCompSetDropRestOfWord(*dropRestOfWord);
break;
}
case wxStyledTextCtrl_AutoCompGetDropRestOfWord: { // wxStyledTextCtrl::AutoCompGetDropRestOfWord
@@ -27925,7 +27966,7 @@ case wxStyledTextCtrl_RegisterImage: { // wxStyledTextCtrl::RegisterImage
int * type = (int *) bp; bp += 4;
wxBitmap *bmp = (wxBitmap *) getPtr(bp,memenv); bp += 4;
if(!This) throw wxe_badarg(0);
- This->RegisterImage((int) *type,*bmp);
+ This->RegisterImage(*type,*bmp);
break;
}
case wxStyledTextCtrl_ClearRegisteredImages: { // wxStyledTextCtrl::ClearRegisteredImages
@@ -27945,14 +27986,14 @@ case wxStyledTextCtrl_AutoCompSetTypeSeparator: { // wxStyledTextCtrl::AutoCompS
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * separatorCharacter = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->AutoCompSetTypeSeparator((int) *separatorCharacter);
+ This->AutoCompSetTypeSeparator(*separatorCharacter);
break;
}
case wxStyledTextCtrl_AutoCompSetMaxWidth: { // wxStyledTextCtrl::AutoCompSetMaxWidth
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * characterCount = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->AutoCompSetMaxWidth((int) *characterCount);
+ This->AutoCompSetMaxWidth(*characterCount);
break;
}
case wxStyledTextCtrl_AutoCompGetMaxWidth: { // wxStyledTextCtrl::AutoCompGetMaxWidth
@@ -27966,7 +28007,7 @@ case wxStyledTextCtrl_AutoCompSetMaxHeight: { // wxStyledTextCtrl::AutoCompSetMa
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * rowCount = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->AutoCompSetMaxHeight((int) *rowCount);
+ This->AutoCompSetMaxHeight(*rowCount);
break;
}
case wxStyledTextCtrl_AutoCompGetMaxHeight: { // wxStyledTextCtrl::AutoCompGetMaxHeight
@@ -27980,7 +28021,7 @@ case wxStyledTextCtrl_SetIndent: { // wxStyledTextCtrl::SetIndent
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * indentSize = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetIndent((int) *indentSize);
+ This->SetIndent(*indentSize);
break;
}
case wxStyledTextCtrl_GetIndent: { // wxStyledTextCtrl::GetIndent
@@ -27994,7 +28035,7 @@ case wxStyledTextCtrl_SetUseTabs: { // wxStyledTextCtrl::SetUseTabs
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
bool * useTabs = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetUseTabs((bool) *useTabs);
+ This->SetUseTabs(*useTabs);
break;
}
case wxStyledTextCtrl_GetUseTabs: { // wxStyledTextCtrl::GetUseTabs
@@ -28009,14 +28050,14 @@ case wxStyledTextCtrl_SetLineIndentation: { // wxStyledTextCtrl::SetLineIndentat
int * line = (int *) bp; bp += 4;
int * indentSize = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetLineIndentation((int) *line,(int) *indentSize);
+ This->SetLineIndentation(*line,*indentSize);
break;
}
case wxStyledTextCtrl_GetLineIndentation: { // wxStyledTextCtrl::GetLineIndentation
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * line = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->GetLineIndentation((int) *line);
+ int Result = This->GetLineIndentation(*line);
rt.addInt(Result);
break;
}
@@ -28024,7 +28065,7 @@ case wxStyledTextCtrl_GetLineIndentPosition: { // wxStyledTextCtrl::GetLineInden
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * line = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->GetLineIndentPosition((int) *line);
+ int Result = This->GetLineIndentPosition(*line);
rt.addInt(Result);
break;
}
@@ -28032,7 +28073,7 @@ case wxStyledTextCtrl_GetColumn: { // wxStyledTextCtrl::GetColumn
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * pos = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->GetColumn((int) *pos);
+ int Result = This->GetColumn(*pos);
rt.addInt(Result);
break;
}
@@ -28040,7 +28081,7 @@ case wxStyledTextCtrl_SetUseHorizontalScrollBar: { // wxStyledTextCtrl::SetUseHo
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
bool * show = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetUseHorizontalScrollBar((bool) *show);
+ This->SetUseHorizontalScrollBar(*show);
break;
}
case wxStyledTextCtrl_GetUseHorizontalScrollBar: { // wxStyledTextCtrl::GetUseHorizontalScrollBar
@@ -28054,7 +28095,7 @@ case wxStyledTextCtrl_SetIndentationGuides: { // wxStyledTextCtrl::SetIndentatio
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
bool * show = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetIndentationGuides((bool) *show);
+ This->SetIndentationGuides(*show);
break;
}
case wxStyledTextCtrl_GetIndentationGuides: { // wxStyledTextCtrl::GetIndentationGuides
@@ -28068,7 +28109,7 @@ case wxStyledTextCtrl_SetHighlightGuide: { // wxStyledTextCtrl::SetHighlightGuid
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * column = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetHighlightGuide((int) *column);
+ This->SetHighlightGuide(*column);
break;
}
case wxStyledTextCtrl_GetHighlightGuide: { // wxStyledTextCtrl::GetHighlightGuide
@@ -28082,7 +28123,7 @@ case wxStyledTextCtrl_GetLineEndPosition: { // wxStyledTextCtrl::GetLineEndPosit
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * line = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->GetLineEndPosition((int) *line);
+ int Result = This->GetLineEndPosition(*line);
rt.addInt(Result);
break;
}
@@ -28111,14 +28152,14 @@ case wxStyledTextCtrl_SetCurrentPos: { // wxStyledTextCtrl::SetCurrentPos
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * pos = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetCurrentPos((int) *pos);
+ This->SetCurrentPos(*pos);
break;
}
case wxStyledTextCtrl_SetSelectionStart: { // wxStyledTextCtrl::SetSelectionStart
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * pos = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetSelectionStart((int) *pos);
+ This->SetSelectionStart(*pos);
break;
}
case wxStyledTextCtrl_GetSelectionStart: { // wxStyledTextCtrl::GetSelectionStart
@@ -28132,7 +28173,7 @@ case wxStyledTextCtrl_SetSelectionEnd: { // wxStyledTextCtrl::SetSelectionEnd
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * pos = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetSelectionEnd((int) *pos);
+ This->SetSelectionEnd(*pos);
break;
}
case wxStyledTextCtrl_GetSelectionEnd: { // wxStyledTextCtrl::GetSelectionEnd
@@ -28146,7 +28187,7 @@ case wxStyledTextCtrl_SetPrintMagnification: { // wxStyledTextCtrl::SetPrintMagn
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * magnification = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetPrintMagnification((int) *magnification);
+ This->SetPrintMagnification(*magnification);
break;
}
case wxStyledTextCtrl_GetPrintMagnification: { // wxStyledTextCtrl::GetPrintMagnification
@@ -28160,7 +28201,7 @@ case wxStyledTextCtrl_SetPrintColourMode: { // wxStyledTextCtrl::SetPrintColourM
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * mode = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetPrintColourMode((int) *mode);
+ This->SetPrintColourMode(*mode);
break;
}
case wxStyledTextCtrl_GetPrintColourMode: { // wxStyledTextCtrl::GetPrintColourMode
@@ -28184,7 +28225,7 @@ case wxStyledTextCtrl_FindText: { // wxStyledTextCtrl::FindText
} break;
}};
if(!This) throw wxe_badarg(0);
- int Result = This->FindText((int) *minPos,(int) *maxPos,text,flags);
+ int Result = This->FindText(*minPos,*maxPos,text,flags);
rt.addInt(Result);
break;
}
@@ -28206,7 +28247,7 @@ case wxStyledTextCtrl_FormatRange: { // wxStyledTextCtrl::FormatRange
int * pageRectH = (int *) bp; bp += 4;
wxRect pageRect = wxRect(*pageRectX,*pageRectY,*pageRectW,*pageRectH);
if(!This) throw wxe_badarg(0);
- int Result = This->FormatRange((bool) *doDraw,(int) *startPos,(int) *endPos,draw,target,renderRect,pageRect);
+ int Result = This->FormatRange(*doDraw,*startPos,*endPos,draw,target,renderRect,pageRect);
rt.addInt(Result);
break;
}
@@ -28221,7 +28262,7 @@ case wxStyledTextCtrl_GetLine: { // wxStyledTextCtrl::GetLine
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * line = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxString Result = This->GetLine((int) *line);
+ wxString Result = This->GetLine(*line);
rt.add(Result);
break;
}
@@ -28236,7 +28277,7 @@ case wxStyledTextCtrl_SetMarginLeft: { // wxStyledTextCtrl::SetMarginLeft
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * pixelWidth = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetMarginLeft((int) *pixelWidth);
+ This->SetMarginLeft(*pixelWidth);
break;
}
case wxStyledTextCtrl_GetMarginLeft: { // wxStyledTextCtrl::GetMarginLeft
@@ -28250,7 +28291,7 @@ case wxStyledTextCtrl_SetMarginRight: { // wxStyledTextCtrl::SetMarginRight
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * pixelWidth = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetMarginRight((int) *pixelWidth);
+ This->SetMarginRight(*pixelWidth);
break;
}
case wxStyledTextCtrl_GetMarginRight: { // wxStyledTextCtrl::GetMarginRight
@@ -28272,7 +28313,7 @@ case wxStyledTextCtrl_SetSelection: { // wxStyledTextCtrl::SetSelection
int * start = (int *) bp; bp += 4;
int * end = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetSelection((int) *start,(int) *end);
+ This->SetSelection(*start,*end);
break;
}
case wxStyledTextCtrl_GetSelectedText: { // wxStyledTextCtrl::GetSelectedText
@@ -28287,7 +28328,7 @@ case wxStyledTextCtrl_GetTextRange: { // wxStyledTextCtrl::GetTextRange
int * startPos = (int *) bp; bp += 4;
int * endPos = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxString Result = This->GetTextRange((int) *startPos,(int) *endPos);
+ wxString Result = This->GetTextRange(*startPos,*endPos);
rt.add(Result);
break;
}
@@ -28295,14 +28336,14 @@ case wxStyledTextCtrl_HideSelection: { // wxStyledTextCtrl::HideSelection
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
bool * normal = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->HideSelection((bool) *normal);
+ This->HideSelection(*normal);
break;
}
case wxStyledTextCtrl_LineFromPosition: { // wxStyledTextCtrl::LineFromPosition
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * pos = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->LineFromPosition((int) *pos);
+ int Result = This->LineFromPosition(*pos);
rt.addInt(Result);
break;
}
@@ -28310,7 +28351,7 @@ case wxStyledTextCtrl_PositionFromLine: { // wxStyledTextCtrl::PositionFromLine
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * line = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->PositionFromLine((int) *line);
+ int Result = This->PositionFromLine(*line);
rt.addInt(Result);
break;
}
@@ -28319,7 +28360,7 @@ case wxStyledTextCtrl_LineScroll: { // wxStyledTextCtrl::LineScroll
int * columns = (int *) bp; bp += 4;
int * lines = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->LineScroll((int) *columns,(int) *lines);
+ This->LineScroll(*columns,*lines);
break;
}
case wxStyledTextCtrl_EnsureCaretVisible: { // wxStyledTextCtrl::EnsureCaretVisible
@@ -28341,7 +28382,7 @@ case wxStyledTextCtrl_SetReadOnly: { // wxStyledTextCtrl::SetReadOnly
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
bool * readOnly = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetReadOnly((bool) *readOnly);
+ This->SetReadOnly(*readOnly);
break;
}
case wxStyledTextCtrl_CanPaste: { // wxStyledTextCtrl::CanPaste
@@ -28428,7 +28469,7 @@ case wxStyledTextCtrl_SetCaretWidth: { // wxStyledTextCtrl::SetCaretWidth
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * pixelWidth = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetCaretWidth((int) *pixelWidth);
+ This->SetCaretWidth(*pixelWidth);
break;
}
case wxStyledTextCtrl_GetCaretWidth: { // wxStyledTextCtrl::GetCaretWidth
@@ -28442,7 +28483,7 @@ case wxStyledTextCtrl_SetTargetStart: { // wxStyledTextCtrl::SetTargetStart
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * pos = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetTargetStart((int) *pos);
+ This->SetTargetStart(*pos);
break;
}
case wxStyledTextCtrl_GetTargetStart: { // wxStyledTextCtrl::GetTargetStart
@@ -28456,7 +28497,7 @@ case wxStyledTextCtrl_SetTargetEnd: { // wxStyledTextCtrl::SetTargetEnd
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * pos = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetTargetEnd((int) *pos);
+ This->SetTargetEnd(*pos);
break;
}
case wxStyledTextCtrl_GetTargetEnd: { // wxStyledTextCtrl::GetTargetEnd
@@ -28490,7 +28531,7 @@ case wxStyledTextCtrl_SetSearchFlags: { // wxStyledTextCtrl::SetSearchFlags
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * flags = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetSearchFlags((int) *flags);
+ This->SetSearchFlags(*flags);
break;
}
case wxStyledTextCtrl_GetSearchFlags: { // wxStyledTextCtrl::GetSearchFlags
@@ -28507,7 +28548,7 @@ case wxStyledTextCtrl_CallTipShow: { // wxStyledTextCtrl::CallTipShow
wxString definition = wxString(bp, wxConvUTF8);
bp += *definitionLen+((8-((4+ *definitionLen) & 7)) & 7);
if(!This) throw wxe_badarg(0);
- This->CallTipShow((int) *pos,definition);
+ This->CallTipShow(*pos,definition);
break;
}
case wxStyledTextCtrl_CallTipCancel: { // wxStyledTextCtrl::CallTipCancel
@@ -28535,7 +28576,7 @@ case wxStyledTextCtrl_CallTipSetHighlight: { // wxStyledTextCtrl::CallTipSetHigh
int * start = (int *) bp; bp += 4;
int * end = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->CallTipSetHighlight((int) *start,(int) *end);
+ This->CallTipSetHighlight(*start,*end);
break;
}
case wxStyledTextCtrl_CallTipSetBackground: { // wxStyledTextCtrl::CallTipSetBackground
@@ -28575,14 +28616,14 @@ case wxStyledTextCtrl_CallTipUseStyle: { // wxStyledTextCtrl::CallTipUseStyle
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * tabSize = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->CallTipUseStyle((int) *tabSize);
+ This->CallTipUseStyle(*tabSize);
break;
}
case wxStyledTextCtrl_VisibleFromDocLine: { // wxStyledTextCtrl::VisibleFromDocLine
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * line = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->VisibleFromDocLine((int) *line);
+ int Result = This->VisibleFromDocLine(*line);
rt.addInt(Result);
break;
}
@@ -28590,7 +28631,7 @@ case wxStyledTextCtrl_DocLineFromVisible: { // wxStyledTextCtrl::DocLineFromVisi
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * lineDisplay = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->DocLineFromVisible((int) *lineDisplay);
+ int Result = This->DocLineFromVisible(*lineDisplay);
rt.addInt(Result);
break;
}
@@ -28598,7 +28639,7 @@ case wxStyledTextCtrl_WrapCount: { // wxStyledTextCtrl::WrapCount
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * line = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->WrapCount((int) *line);
+ int Result = This->WrapCount(*line);
rt.addInt(Result);
break;
}
@@ -28607,14 +28648,14 @@ case wxStyledTextCtrl_SetFoldLevel: { // wxStyledTextCtrl::SetFoldLevel
int * line = (int *) bp; bp += 4;
int * level = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetFoldLevel((int) *line,(int) *level);
+ This->SetFoldLevel(*line,*level);
break;
}
case wxStyledTextCtrl_GetFoldLevel: { // wxStyledTextCtrl::GetFoldLevel
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * line = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->GetFoldLevel((int) *line);
+ int Result = This->GetFoldLevel(*line);
rt.addInt(Result);
break;
}
@@ -28623,7 +28664,7 @@ case wxStyledTextCtrl_GetLastChild: { // wxStyledTextCtrl::GetLastChild
int * line = (int *) bp; bp += 4;
int * level = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->GetLastChild((int) *line,(int) *level);
+ int Result = This->GetLastChild(*line,*level);
rt.addInt(Result);
break;
}
@@ -28631,7 +28672,7 @@ case wxStyledTextCtrl_GetFoldParent: { // wxStyledTextCtrl::GetFoldParent
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * line = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->GetFoldParent((int) *line);
+ int Result = This->GetFoldParent(*line);
rt.addInt(Result);
break;
}
@@ -28640,7 +28681,7 @@ case wxStyledTextCtrl_ShowLines: { // wxStyledTextCtrl::ShowLines
int * lineStart = (int *) bp; bp += 4;
int * lineEnd = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->ShowLines((int) *lineStart,(int) *lineEnd);
+ This->ShowLines(*lineStart,*lineEnd);
break;
}
case wxStyledTextCtrl_HideLines: { // wxStyledTextCtrl::HideLines
@@ -28648,14 +28689,14 @@ case wxStyledTextCtrl_HideLines: { // wxStyledTextCtrl::HideLines
int * lineStart = (int *) bp; bp += 4;
int * lineEnd = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->HideLines((int) *lineStart,(int) *lineEnd);
+ This->HideLines(*lineStart,*lineEnd);
break;
}
case wxStyledTextCtrl_GetLineVisible: { // wxStyledTextCtrl::GetLineVisible
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * line = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->GetLineVisible((int) *line);
+ bool Result = This->GetLineVisible(*line);
rt.addBool(Result);
break;
}
@@ -28664,14 +28705,14 @@ case wxStyledTextCtrl_SetFoldExpanded: { // wxStyledTextCtrl::SetFoldExpanded
int * line = (int *) bp; bp += 4;
bool * expanded = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetFoldExpanded((int) *line,(bool) *expanded);
+ This->SetFoldExpanded(*line,*expanded);
break;
}
case wxStyledTextCtrl_GetFoldExpanded: { // wxStyledTextCtrl::GetFoldExpanded
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * line = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- bool Result = This->GetFoldExpanded((int) *line);
+ bool Result = This->GetFoldExpanded(*line);
rt.addBool(Result);
break;
}
@@ -28679,35 +28720,35 @@ case wxStyledTextCtrl_ToggleFold: { // wxStyledTextCtrl::ToggleFold
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * line = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->ToggleFold((int) *line);
+ This->ToggleFold(*line);
break;
}
case wxStyledTextCtrl_EnsureVisible: { // wxStyledTextCtrl::EnsureVisible
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * line = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->EnsureVisible((int) *line);
+ This->EnsureVisible(*line);
break;
}
case wxStyledTextCtrl_SetFoldFlags: { // wxStyledTextCtrl::SetFoldFlags
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * flags = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetFoldFlags((int) *flags);
+ This->SetFoldFlags(*flags);
break;
}
case wxStyledTextCtrl_EnsureVisibleEnforcePolicy: { // wxStyledTextCtrl::EnsureVisibleEnforcePolicy
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * line = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->EnsureVisibleEnforcePolicy((int) *line);
+ This->EnsureVisibleEnforcePolicy(*line);
break;
}
case wxStyledTextCtrl_SetTabIndents: { // wxStyledTextCtrl::SetTabIndents
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
bool * tabIndents = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetTabIndents((bool) *tabIndents);
+ This->SetTabIndents(*tabIndents);
break;
}
case wxStyledTextCtrl_GetTabIndents: { // wxStyledTextCtrl::GetTabIndents
@@ -28721,7 +28762,7 @@ case wxStyledTextCtrl_SetBackSpaceUnIndents: { // wxStyledTextCtrl::SetBackSpace
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
bool * bsUnIndents = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetBackSpaceUnIndents((bool) *bsUnIndents);
+ This->SetBackSpaceUnIndents(*bsUnIndents);
break;
}
case wxStyledTextCtrl_GetBackSpaceUnIndents: { // wxStyledTextCtrl::GetBackSpaceUnIndents
@@ -28735,7 +28776,7 @@ case wxStyledTextCtrl_SetMouseDwellTime: { // wxStyledTextCtrl::SetMouseDwellTim
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * periodMilliseconds = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetMouseDwellTime((int) *periodMilliseconds);
+ This->SetMouseDwellTime(*periodMilliseconds);
break;
}
case wxStyledTextCtrl_GetMouseDwellTime: { // wxStyledTextCtrl::GetMouseDwellTime
@@ -28750,7 +28791,7 @@ case wxStyledTextCtrl_WordStartPosition: { // wxStyledTextCtrl::WordStartPositio
int * pos = (int *) bp; bp += 4;
bool * onlyWordCharacters = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->WordStartPosition((int) *pos,(bool) *onlyWordCharacters);
+ int Result = This->WordStartPosition(*pos,*onlyWordCharacters);
rt.addInt(Result);
break;
}
@@ -28759,7 +28800,7 @@ case wxStyledTextCtrl_WordEndPosition: { // wxStyledTextCtrl::WordEndPosition
int * pos = (int *) bp; bp += 4;
bool * onlyWordCharacters = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->WordEndPosition((int) *pos,(bool) *onlyWordCharacters);
+ int Result = This->WordEndPosition(*pos,*onlyWordCharacters);
rt.addInt(Result);
break;
}
@@ -28767,7 +28808,7 @@ case wxStyledTextCtrl_SetWrapMode: { // wxStyledTextCtrl::SetWrapMode
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * mode = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetWrapMode((int) *mode);
+ This->SetWrapMode(*mode);
break;
}
case wxStyledTextCtrl_GetWrapMode: { // wxStyledTextCtrl::GetWrapMode
@@ -28781,7 +28822,7 @@ case wxStyledTextCtrl_SetWrapVisualFlags: { // wxStyledTextCtrl::SetWrapVisualFl
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * wrapVisualFlags = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetWrapVisualFlags((int) *wrapVisualFlags);
+ This->SetWrapVisualFlags(*wrapVisualFlags);
break;
}
case wxStyledTextCtrl_GetWrapVisualFlags: { // wxStyledTextCtrl::GetWrapVisualFlags
@@ -28795,7 +28836,7 @@ case wxStyledTextCtrl_SetWrapVisualFlagsLocation: { // wxStyledTextCtrl::SetWrap
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * wrapVisualFlagsLocation = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetWrapVisualFlagsLocation((int) *wrapVisualFlagsLocation);
+ This->SetWrapVisualFlagsLocation(*wrapVisualFlagsLocation);
break;
}
case wxStyledTextCtrl_GetWrapVisualFlagsLocation: { // wxStyledTextCtrl::GetWrapVisualFlagsLocation
@@ -28809,7 +28850,7 @@ case wxStyledTextCtrl_SetWrapStartIndent: { // wxStyledTextCtrl::SetWrapStartInd
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * indent = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetWrapStartIndent((int) *indent);
+ This->SetWrapStartIndent(*indent);
break;
}
case wxStyledTextCtrl_GetWrapStartIndent: { // wxStyledTextCtrl::GetWrapStartIndent
@@ -28823,7 +28864,7 @@ case wxStyledTextCtrl_SetLayoutCache: { // wxStyledTextCtrl::SetLayoutCache
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * mode = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetLayoutCache((int) *mode);
+ This->SetLayoutCache(*mode);
break;
}
case wxStyledTextCtrl_GetLayoutCache: { // wxStyledTextCtrl::GetLayoutCache
@@ -28837,7 +28878,7 @@ case wxStyledTextCtrl_SetScrollWidth: { // wxStyledTextCtrl::SetScrollWidth
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * pixelWidth = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetScrollWidth((int) *pixelWidth);
+ This->SetScrollWidth(*pixelWidth);
break;
}
case wxStyledTextCtrl_GetScrollWidth: { // wxStyledTextCtrl::GetScrollWidth
@@ -28854,7 +28895,7 @@ case wxStyledTextCtrl_TextWidth: { // wxStyledTextCtrl::TextWidth
wxString text = wxString(bp, wxConvUTF8);
bp += *textLen+((8-((4+ *textLen) & 7)) & 7);
if(!This) throw wxe_badarg(0);
- int Result = This->TextWidth((int) *style,text);
+ int Result = This->TextWidth(*style,text);
rt.addInt(Result);
break;
}
@@ -28869,7 +28910,7 @@ case wxStyledTextCtrl_TextHeight: { // wxStyledTextCtrl::TextHeight
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * line = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->TextHeight((int) *line);
+ int Result = This->TextHeight(*line);
rt.addInt(Result);
break;
}
@@ -28877,7 +28918,7 @@ case wxStyledTextCtrl_SetUseVerticalScrollBar: { // wxStyledTextCtrl::SetUseVert
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
bool * show = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetUseVerticalScrollBar((bool) *show);
+ This->SetUseVerticalScrollBar(*show);
break;
}
case wxStyledTextCtrl_GetUseVerticalScrollBar: { // wxStyledTextCtrl::GetUseVerticalScrollBar
@@ -28907,7 +28948,7 @@ case wxStyledTextCtrl_SetTwoPhaseDraw: { // wxStyledTextCtrl::SetTwoPhaseDraw
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
bool * twoPhase = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetTwoPhaseDraw((bool) *twoPhase);
+ This->SetTwoPhaseDraw(*twoPhase);
break;
}
case wxStyledTextCtrl_TargetFromSelection: { // wxStyledTextCtrl::TargetFromSelection
@@ -28926,7 +28967,7 @@ case wxStyledTextCtrl_LinesSplit: { // wxStyledTextCtrl::LinesSplit
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * pixelWidth = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->LinesSplit((int) *pixelWidth);
+ This->LinesSplit(*pixelWidth);
break;
}
case wxStyledTextCtrl_SetFoldMarginColour: { // wxStyledTextCtrl::SetFoldMarginColour
@@ -28938,7 +28979,7 @@ case wxStyledTextCtrl_SetFoldMarginColour: { // wxStyledTextCtrl::SetFoldMarginC
int * backA = (int *) bp; bp += 4;
wxColour back = wxColour(*backR,*backG,*backB,*backA);
if(!This) throw wxe_badarg(0);
- This->SetFoldMarginColour((bool) *useSetting,back);
+ This->SetFoldMarginColour(*useSetting,back);
break;
}
case wxStyledTextCtrl_SetFoldMarginHiColour: { // wxStyledTextCtrl::SetFoldMarginHiColour
@@ -28950,7 +28991,7 @@ case wxStyledTextCtrl_SetFoldMarginHiColour: { // wxStyledTextCtrl::SetFoldMargi
int * foreA = (int *) bp; bp += 4;
wxColour fore = wxColour(*foreR,*foreG,*foreB,*foreA);
if(!This) throw wxe_badarg(0);
- This->SetFoldMarginHiColour((bool) *useSetting,fore);
+ This->SetFoldMarginHiColour(*useSetting,fore);
break;
}
case wxStyledTextCtrl_LineDown: { // wxStyledTextCtrl::LineDown
@@ -29299,7 +29340,7 @@ case wxStyledTextCtrl_LineLength: { // wxStyledTextCtrl::LineLength
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * line = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->LineLength((int) *line);
+ int Result = This->LineLength(*line);
rt.addInt(Result);
break;
}
@@ -29308,21 +29349,21 @@ case wxStyledTextCtrl_BraceHighlight: { // wxStyledTextCtrl::BraceHighlight
int * pos1 = (int *) bp; bp += 4;
int * pos2 = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->BraceHighlight((int) *pos1,(int) *pos2);
+ This->BraceHighlight(*pos1,*pos2);
break;
}
case wxStyledTextCtrl_BraceBadLight: { // wxStyledTextCtrl::BraceBadLight
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * pos = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->BraceBadLight((int) *pos);
+ This->BraceBadLight(*pos);
break;
}
case wxStyledTextCtrl_BraceMatch: { // wxStyledTextCtrl::BraceMatch
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * pos = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->BraceMatch((int) *pos);
+ int Result = This->BraceMatch(*pos);
rt.addInt(Result);
break;
}
@@ -29337,14 +29378,14 @@ case wxStyledTextCtrl_SetViewEOL: { // wxStyledTextCtrl::SetViewEOL
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
bool * visible = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetViewEOL((bool) *visible);
+ This->SetViewEOL(*visible);
break;
}
case wxStyledTextCtrl_SetModEventMask: { // wxStyledTextCtrl::SetModEventMask
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * mask = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetModEventMask((int) *mask);
+ This->SetModEventMask(*mask);
break;
}
case wxStyledTextCtrl_GetEdgeColumn: { // wxStyledTextCtrl::GetEdgeColumn
@@ -29358,14 +29399,14 @@ case wxStyledTextCtrl_SetEdgeColumn: { // wxStyledTextCtrl::SetEdgeColumn
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * column = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetEdgeColumn((int) *column);
+ This->SetEdgeColumn(*column);
break;
}
case wxStyledTextCtrl_SetEdgeMode: { // wxStyledTextCtrl::SetEdgeMode
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * mode = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetEdgeMode((int) *mode);
+ This->SetEdgeMode(*mode);
break;
}
case wxStyledTextCtrl_GetEdgeMode: { // wxStyledTextCtrl::GetEdgeMode
@@ -29406,7 +29447,7 @@ case wxStyledTextCtrl_SearchNext: { // wxStyledTextCtrl::SearchNext
wxString text = wxString(bp, wxConvUTF8);
bp += *textLen+((8-((4+ *textLen) & 7)) & 7);
if(!This) throw wxe_badarg(0);
- int Result = This->SearchNext((int) *flags,text);
+ int Result = This->SearchNext(*flags,text);
rt.addInt(Result);
break;
}
@@ -29417,7 +29458,7 @@ case wxStyledTextCtrl_SearchPrev: { // wxStyledTextCtrl::SearchPrev
wxString text = wxString(bp, wxConvUTF8);
bp += *textLen+((8-((4+ *textLen) & 7)) & 7);
if(!This) throw wxe_badarg(0);
- int Result = This->SearchPrev((int) *flags,text);
+ int Result = This->SearchPrev(*flags,text);
rt.addInt(Result);
break;
}
@@ -29432,7 +29473,7 @@ case wxStyledTextCtrl_UsePopUp: { // wxStyledTextCtrl::UsePopUp
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
bool * allowPopUp = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->UsePopUp((bool) *allowPopUp);
+ This->UsePopUp(*allowPopUp);
break;
}
case wxStyledTextCtrl_SelectionIsRectangle: { // wxStyledTextCtrl::SelectionIsRectangle
@@ -29446,7 +29487,7 @@ case wxStyledTextCtrl_SetZoom: { // wxStyledTextCtrl::SetZoom
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * zoom = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetZoom((int) *zoom);
+ This->SetZoom(*zoom);
break;
}
case wxStyledTextCtrl_GetZoom: { // wxStyledTextCtrl::GetZoom
@@ -29467,7 +29508,7 @@ case wxStyledTextCtrl_SetSTCFocus: { // wxStyledTextCtrl::SetSTCFocus
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
bool * focus = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetSTCFocus((bool) *focus);
+ This->SetSTCFocus(*focus);
break;
}
case wxStyledTextCtrl_GetSTCFocus: { // wxStyledTextCtrl::GetSTCFocus
@@ -29481,7 +29522,7 @@ case wxStyledTextCtrl_SetStatus: { // wxStyledTextCtrl::SetStatus
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * statusCode = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetStatus((int) *statusCode);
+ This->SetStatus(*statusCode);
break;
}
case wxStyledTextCtrl_GetStatus: { // wxStyledTextCtrl::GetStatus
@@ -29495,7 +29536,7 @@ case wxStyledTextCtrl_SetMouseDownCaptures: { // wxStyledTextCtrl::SetMouseDownC
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
bool * captures = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetMouseDownCaptures((bool) *captures);
+ This->SetMouseDownCaptures(*captures);
break;
}
case wxStyledTextCtrl_GetMouseDownCaptures: { // wxStyledTextCtrl::GetMouseDownCaptures
@@ -29509,7 +29550,7 @@ case wxStyledTextCtrl_SetSTCCursor: { // wxStyledTextCtrl::SetSTCCursor
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * cursorType = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetSTCCursor((int) *cursorType);
+ This->SetSTCCursor(*cursorType);
break;
}
case wxStyledTextCtrl_GetSTCCursor: { // wxStyledTextCtrl::GetSTCCursor
@@ -29523,7 +29564,7 @@ case wxStyledTextCtrl_SetControlCharSymbol: { // wxStyledTextCtrl::SetControlCha
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * symbol = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetControlCharSymbol((int) *symbol);
+ This->SetControlCharSymbol(*symbol);
break;
}
case wxStyledTextCtrl_GetControlCharSymbol: { // wxStyledTextCtrl::GetControlCharSymbol
@@ -29562,7 +29603,7 @@ case wxStyledTextCtrl_SetVisiblePolicy: { // wxStyledTextCtrl::SetVisiblePolicy
int * visiblePolicy = (int *) bp; bp += 4;
int * visibleSlop = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetVisiblePolicy((int) *visiblePolicy,(int) *visibleSlop);
+ This->SetVisiblePolicy(*visiblePolicy,*visibleSlop);
break;
}
case wxStyledTextCtrl_DelLineLeft: { // wxStyledTextCtrl::DelLineLeft
@@ -29595,7 +29636,7 @@ case wxStyledTextCtrl_SetXCaretPolicy: { // wxStyledTextCtrl::SetXCaretPolicy
int * caretPolicy = (int *) bp; bp += 4;
int * caretSlop = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetXCaretPolicy((int) *caretPolicy,(int) *caretSlop);
+ This->SetXCaretPolicy(*caretPolicy,*caretSlop);
break;
}
case wxStyledTextCtrl_SetYCaretPolicy: { // wxStyledTextCtrl::SetYCaretPolicy
@@ -29603,7 +29644,7 @@ case wxStyledTextCtrl_SetYCaretPolicy: { // wxStyledTextCtrl::SetYCaretPolicy
int * caretPolicy = (int *) bp; bp += 4;
int * caretSlop = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetYCaretPolicy((int) *caretPolicy,(int) *caretSlop);
+ This->SetYCaretPolicy(*caretPolicy,*caretSlop);
break;
}
case wxStyledTextCtrl_GetPrintWrapMode: { // wxStyledTextCtrl::GetPrintWrapMode
@@ -29622,7 +29663,7 @@ case wxStyledTextCtrl_SetHotspotActiveForeground: { // wxStyledTextCtrl::SetHots
int * foreA = (int *) bp; bp += 4;
wxColour fore = wxColour(*foreR,*foreG,*foreB,*foreA);
if(!This) throw wxe_badarg(0);
- This->SetHotspotActiveForeground((bool) *useSetting,fore);
+ This->SetHotspotActiveForeground(*useSetting,fore);
break;
}
case wxStyledTextCtrl_SetHotspotActiveBackground: { // wxStyledTextCtrl::SetHotspotActiveBackground
@@ -29634,21 +29675,21 @@ case wxStyledTextCtrl_SetHotspotActiveBackground: { // wxStyledTextCtrl::SetHots
int * backA = (int *) bp; bp += 4;
wxColour back = wxColour(*backR,*backG,*backB,*backA);
if(!This) throw wxe_badarg(0);
- This->SetHotspotActiveBackground((bool) *useSetting,back);
+ This->SetHotspotActiveBackground(*useSetting,back);
break;
}
case wxStyledTextCtrl_SetHotspotActiveUnderline: { // wxStyledTextCtrl::SetHotspotActiveUnderline
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
bool * underline = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetHotspotActiveUnderline((bool) *underline);
+ This->SetHotspotActiveUnderline(*underline);
break;
}
case wxStyledTextCtrl_SetHotspotSingleLine: { // wxStyledTextCtrl::SetHotspotSingleLine
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
bool * singleLine = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetHotspotSingleLine((bool) *singleLine);
+ This->SetHotspotSingleLine(*singleLine);
break;
}
case wxStyledTextCtrl_ParaDownExtend: { // wxStyledTextCtrl::ParaDownExtend
@@ -29673,7 +29714,7 @@ case wxStyledTextCtrl_PositionBefore: { // wxStyledTextCtrl::PositionBefore
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * pos = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->PositionBefore((int) *pos);
+ int Result = This->PositionBefore(*pos);
rt.addInt(Result);
break;
}
@@ -29681,7 +29722,7 @@ case wxStyledTextCtrl_PositionAfter: { // wxStyledTextCtrl::PositionAfter
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * pos = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->PositionAfter((int) *pos);
+ int Result = This->PositionAfter(*pos);
rt.addInt(Result);
break;
}
@@ -29690,7 +29731,7 @@ case wxStyledTextCtrl_CopyRange: { // wxStyledTextCtrl::CopyRange
int * start = (int *) bp; bp += 4;
int * end = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->CopyRange((int) *start,(int) *end);
+ This->CopyRange(*start,*end);
break;
}
case wxStyledTextCtrl_CopyText: { // wxStyledTextCtrl::CopyText
@@ -29700,14 +29741,14 @@ case wxStyledTextCtrl_CopyText: { // wxStyledTextCtrl::CopyText
wxString text = wxString(bp, wxConvUTF8);
bp += *textLen+((8-((4+ *textLen) & 7)) & 7);
if(!This) throw wxe_badarg(0);
- This->CopyText((int) *length,text);
+ This->CopyText(*length,text);
break;
}
case wxStyledTextCtrl_SetSelectionMode: { // wxStyledTextCtrl::SetSelectionMode
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * mode = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetSelectionMode((int) *mode);
+ This->SetSelectionMode(*mode);
break;
}
case wxStyledTextCtrl_GetSelectionMode: { // wxStyledTextCtrl::GetSelectionMode
@@ -29845,7 +29886,7 @@ case wxStyledTextCtrl_Allocate: { // wxStyledTextCtrl::Allocate
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * bytes = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->Allocate((int) *bytes);
+ This->Allocate(*bytes);
break;
}
case wxStyledTextCtrl_FindColumn: { // wxStyledTextCtrl::FindColumn
@@ -29853,7 +29894,7 @@ case wxStyledTextCtrl_FindColumn: { // wxStyledTextCtrl::FindColumn
int * line = (int *) bp; bp += 4;
int * column = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- int Result = This->FindColumn((int) *line,(int) *column);
+ int Result = This->FindColumn(*line,*column);
rt.addInt(Result);
break;
}
@@ -29868,7 +29909,7 @@ case wxStyledTextCtrl_SetCaretSticky: { // wxStyledTextCtrl::SetCaretSticky
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
bool * useCaretStickyBehaviour = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetCaretSticky((bool) *useCaretStickyBehaviour);
+ This->SetCaretSticky(*useCaretStickyBehaviour);
break;
}
case wxStyledTextCtrl_ToggleCaretSticky: { // wxStyledTextCtrl::ToggleCaretSticky
@@ -29881,7 +29922,7 @@ case wxStyledTextCtrl_SetPasteConvertEndings: { // wxStyledTextCtrl::SetPasteCon
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
bool * convert = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetPasteConvertEndings((bool) *convert);
+ This->SetPasteConvertEndings(*convert);
break;
}
case wxStyledTextCtrl_GetPasteConvertEndings: { // wxStyledTextCtrl::GetPasteConvertEndings
@@ -29901,7 +29942,7 @@ case wxStyledTextCtrl_SetCaretLineBackAlpha: { // wxStyledTextCtrl::SetCaretLine
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * alpha = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetCaretLineBackAlpha((int) *alpha);
+ This->SetCaretLineBackAlpha(*alpha);
break;
}
case wxStyledTextCtrl_GetCaretLineBackAlpha: { // wxStyledTextCtrl::GetCaretLineBackAlpha
@@ -29927,7 +29968,7 @@ case wxStyledTextCtrl_SetLexer: { // wxStyledTextCtrl::SetLexer
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * lexer = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetLexer((int) *lexer);
+ This->SetLexer(*lexer);
break;
}
case wxStyledTextCtrl_GetLexer: { // wxStyledTextCtrl::GetLexer
@@ -29942,7 +29983,7 @@ case wxStyledTextCtrl_Colourise: { // wxStyledTextCtrl::Colourise
int * start = (int *) bp; bp += 4;
int * end = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->Colourise((int) *start,(int) *end);
+ This->Colourise(*start,*end);
break;
}
case wxStyledTextCtrl_SetProperty: { // wxStyledTextCtrl::SetProperty
@@ -29964,7 +30005,7 @@ case wxStyledTextCtrl_SetKeyWords: { // wxStyledTextCtrl::SetKeyWords
wxString keyWords = wxString(bp, wxConvUTF8);
bp += *keyWordsLen+((8-((4+ *keyWordsLen) & 7)) & 7);
if(!This) throw wxe_badarg(0);
- This->SetKeyWords((int) *keywordSet,keyWords);
+ This->SetKeyWords(*keywordSet,keyWords);
break;
}
case wxStyledTextCtrl_SetLexerLanguage: { // wxStyledTextCtrl::SetLexerLanguage
@@ -30007,7 +30048,7 @@ case wxStyledTextCtrl_StyleSetSpec: { // wxStyledTextCtrl::StyleSetSpec
wxString spec = wxString(bp, wxConvUTF8);
bp += *specLen+((8-((4+ *specLen) & 7)) & 7);
if(!This) throw wxe_badarg(0);
- This->StyleSetSpec((int) *styleNum,spec);
+ This->StyleSetSpec(*styleNum,spec);
break;
}
case wxStyledTextCtrl_StyleSetFont: { // wxStyledTextCtrl::StyleSetFont
@@ -30015,7 +30056,7 @@ case wxStyledTextCtrl_StyleSetFont: { // wxStyledTextCtrl::StyleSetFont
int * styleNum = (int *) bp; bp += 4;
wxFont *font = (wxFont *) getPtr(bp,memenv); bp += 4;
if(!This) throw wxe_badarg(0);
- This->StyleSetFont((int) *styleNum,*font);
+ This->StyleSetFont(*styleNum,*font);
break;
}
case wxStyledTextCtrl_StyleSetFontAttr: { // wxStyledTextCtrl::StyleSetFontAttr
@@ -30036,7 +30077,7 @@ encoding = *(wxFontEncoding *) bp; bp += 4;;
} break;
}};
if(!This) throw wxe_badarg(0);
- This->StyleSetFontAttr((int) *styleNum,(int) *size,faceName,(bool) *bold,(bool) *italic,(bool) *underline,(wxFontEncoding) encoding);
+ This->StyleSetFontAttr(*styleNum,*size,faceName,*bold,*italic,*underline,encoding);
break;
}
case wxStyledTextCtrl_StyleSetCharacterSet: { // wxStyledTextCtrl::StyleSetCharacterSet
@@ -30044,7 +30085,7 @@ case wxStyledTextCtrl_StyleSetCharacterSet: { // wxStyledTextCtrl::StyleSetChara
int * style = (int *) bp; bp += 4;
int * characterSet = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->StyleSetCharacterSet((int) *style,(int) *characterSet);
+ This->StyleSetCharacterSet(*style,*characterSet);
break;
}
case wxStyledTextCtrl_StyleSetFontEncoding: { // wxStyledTextCtrl::StyleSetFontEncoding
@@ -30052,14 +30093,14 @@ case wxStyledTextCtrl_StyleSetFontEncoding: { // wxStyledTextCtrl::StyleSetFontE
int * style = (int *) bp; bp += 4;
wxFontEncoding encoding = *(wxFontEncoding *) bp; bp += 4;;
if(!This) throw wxe_badarg(0);
- This->StyleSetFontEncoding((int) *style,(wxFontEncoding) encoding);
+ This->StyleSetFontEncoding(*style,encoding);
break;
}
case wxStyledTextCtrl_CmdKeyExecute: { // wxStyledTextCtrl::CmdKeyExecute
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * cmd = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->CmdKeyExecute((int) *cmd);
+ This->CmdKeyExecute(*cmd);
break;
}
case wxStyledTextCtrl_SetMargins: { // wxStyledTextCtrl::SetMargins
@@ -30067,7 +30108,7 @@ case wxStyledTextCtrl_SetMargins: { // wxStyledTextCtrl::SetMargins
int * left = (int *) bp; bp += 4;
int * right = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetMargins((int) *left,(int) *right);
+ This->SetMargins(*left,*right);
break;
}
case wxStyledTextCtrl_GetSelection: { // wxStyledTextCtrl::GetSelection
@@ -30085,7 +30126,7 @@ case wxStyledTextCtrl_PointFromPosition: { // wxStyledTextCtrl::PointFromPositio
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * pos = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- wxPoint Result = This->PointFromPosition((int) *pos);
+ wxPoint Result = This->PointFromPosition(*pos);
rt.add(Result);
break;
}
@@ -30093,32 +30134,14 @@ case wxStyledTextCtrl_ScrollToLine: { // wxStyledTextCtrl::ScrollToLine
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * line = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->ScrollToLine((int) *line);
+ This->ScrollToLine(*line);
break;
}
case wxStyledTextCtrl_ScrollToColumn: { // wxStyledTextCtrl::ScrollToColumn
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * column = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->ScrollToColumn((int) *column);
- break;
-}
-case wxStyledTextCtrl_SendMsg: { // wxStyledTextCtrl::SendMsg
- long wp=0;
- long lp=0;
- wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
- int * msg = (int *) bp; bp += 4;
- while( * (int*) bp) { switch (* (int*) bp) {
- case 1: {bp += 4;
- wp = (long)*(int *) bp; bp += 4;
- } break;
- case 2: {bp += 4;
- lp = (long)*(int *) bp; bp += 4;
- } break;
- }};
- if(!This) throw wxe_badarg(0);
- long Result = This->SendMsg((int) *msg,wp,lp);
- rt.addInt(Result);
+ This->ScrollToColumn(*column);
break;
}
case wxStyledTextCtrl_SetVScrollBar: { // wxStyledTextCtrl::SetVScrollBar
@@ -30146,7 +30169,7 @@ case wxStyledTextCtrl_SetLastKeydownProcessed: { // wxStyledTextCtrl::SetLastKey
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
bool * val = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetLastKeydownProcessed((bool) *val);
+ This->SetLastKeydownProcessed(*val);
break;
}
case wxStyledTextCtrl_SaveFile: { // wxStyledTextCtrl::SaveFile
@@ -30175,7 +30198,7 @@ case wxStyledTextCtrl_DoDragOver: { // wxStyledTextCtrl::DoDragOver
int * y = (int *) bp; bp += 4;
wxDragResult def = *(wxDragResult *) bp; bp += 4;;
if(!This) throw wxe_badarg(0);
- int Result = This->DoDragOver((wxCoord) *x,(wxCoord) *y,(wxDragResult) def);
+ int Result = This->DoDragOver(*x,*y,def);
rt.addInt(Result);
break;
}
@@ -30187,7 +30210,7 @@ case wxStyledTextCtrl_DoDropText: { // wxStyledTextCtrl::DoDropText
wxString data = wxString(bp, wxConvUTF8);
bp += *dataLen+((8-((0+ *dataLen) & 7)) & 7);
if(!This) throw wxe_badarg(0);
- bool Result = This->DoDropText((long) *x,(long) *y,data);
+ bool Result = This->DoDropText(*x,*y,data);
rt.addBool(Result);
break;
}
@@ -30210,7 +30233,7 @@ case wxStyledTextCtrl_InsertTextRaw: { // wxStyledTextCtrl::InsertTextRaw
int * pos = (int *) bp; bp += 4;
const char * text = (const char*) Ecmd.bin[0]->base;
if(!This) throw wxe_badarg(0);
- This->InsertTextRaw((int) *pos,text);
+ This->InsertTextRaw(*pos,text);
break;
}
case wxStyledTextCtrl_GetCurLineRaw: { // wxStyledTextCtrl::GetCurLineRaw
@@ -30229,7 +30252,7 @@ case wxStyledTextCtrl_GetLineRaw: { // wxStyledTextCtrl::GetLineRaw
wxStyledTextCtrl *This = (wxStyledTextCtrl *) getPtr(bp,memenv); bp += 4;
int * line = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- char * Result = This->GetLineRaw((int) *line).data();
+ char * Result = This->GetLineRaw(*line).data();
if(Result) {
rt.addBinary(Result, strlen(Result));
} else {rt.addAtom("null");};
@@ -30249,7 +30272,7 @@ case wxStyledTextCtrl_GetTextRangeRaw: { // wxStyledTextCtrl::GetTextRangeRaw
int * startPos = (int *) bp; bp += 4;
int * endPos = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- char * Result = This->GetTextRangeRaw((int) *startPos,(int) *endPos).data();
+ char * Result = This->GetTextRangeRaw(*startPos,*endPos).data();
if(Result) {
rt.addBinary(Result, strlen(Result));
} else {rt.addAtom("null");};
@@ -30400,19 +30423,19 @@ case wxNotebookEvent_SetOldSelection: { // wxNotebookEvent::SetOldSelection
wxNotebookEvent *This = (wxNotebookEvent *) getPtr(bp,memenv); bp += 4;
int * nOldSel = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetOldSelection((int) *nOldSel);
+ This->SetOldSelection(*nOldSel);
break;
}
case wxNotebookEvent_SetSelection: { // wxNotebookEvent::SetSelection
wxNotebookEvent *This = (wxNotebookEvent *) getPtr(bp,memenv); bp += 4;
int * nSel = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetSelection((int) *nSel);
+ This->SetSelection(*nSel);
break;
}
case wxFileDataObject_new: { // wxFileDataObject::wxFileDataObject
wxFileDataObject * Result = new wxFileDataObject();
- newPtr((void *) Result, 212, memenv);
+ newPtr((void *) Result, 211, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxFileDataObject");
break;
}
@@ -30448,7 +30471,7 @@ case wxTextDataObject_new: { // wxTextDataObject::wxTextDataObject
} break;
}};
wxTextDataObject * Result = new wxTextDataObject(text);
- newPtr((void *) Result, 213, memenv);
+ newPtr((void *) Result, 212, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxTextDataObject");
break;
}
@@ -30484,7 +30507,7 @@ case wxTextDataObject_destroy: { // wxTextDataObject::destroy
case wxBitmapDataObject_new_1_1: { // wxBitmapDataObject::wxBitmapDataObject
wxBitmap *bitmap = (wxBitmap *) getPtr(bp,memenv); bp += 4;
wxBitmapDataObject * Result = new wxBitmapDataObject(*bitmap);
- newPtr((void *) Result, 214, memenv);
+ newPtr((void *) Result, 213, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxBitmapDataObject");
break;
}
@@ -30496,7 +30519,7 @@ bitmap = (wxBitmap *) getPtr(bp,memenv); bp += 4;
} break;
}};
wxBitmapDataObject * Result = new wxBitmapDataObject(*bitmap);
- newPtr((void *) Result, 214, memenv);
+ newPtr((void *) Result, 213, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxBitmapDataObject");
break;
}
@@ -30598,9 +30621,9 @@ case wxClipboard_UsePrimarySelection: { // wxClipboard::UsePrimarySelection
}
case wxClipboard_IsSupported: { // wxClipboard::IsSupported
wxClipboard *This = (wxClipboard *) getPtr(bp,memenv); bp += 4;
- const int * format = (const int *) bp; bp += 4;
+ wxDataFormatId format = *(wxDataFormatId *) bp; bp += 4;;
if(!This) throw wxe_badarg(0);
- bool Result = This->IsSupported((wxDataFormatId) *format);
+ bool Result = This->IsSupported(format);
rt.addBool(Result);
break;
}
@@ -30620,7 +30643,7 @@ case wxSpinEvent_SetPosition: { // wxSpinEvent::SetPosition
wxSpinEvent *This = (wxSpinEvent *) getPtr(bp,memenv); bp += 4;
int * pos = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetPosition((int) *pos);
+ This->SetPosition(*pos);
break;
}
case wxSplitterWindow_new_0: { // wxSplitterWindow::wxSplitterWindow
@@ -30763,7 +30786,7 @@ case wxSplitterWindow_SetSashGravity: { // wxSplitterWindow::SetSashGravity
bp += 4; /* Align */
double * gravity = (double *) bp; bp += 8;
if(!This) throw wxe_badarg(0);
- This->SetSashGravity((double) *gravity);
+ This->SetSashGravity(*gravity);
break;
}
case wxSplitterWindow_SetSashPosition: { // wxSplitterWindow::SetSashPosition
@@ -30776,28 +30799,28 @@ case wxSplitterWindow_SetSashPosition: { // wxSplitterWindow::SetSashPosition
} break;
}};
if(!This) throw wxe_badarg(0);
- This->SetSashPosition((int) *position,redraw);
+ This->SetSashPosition(*position,redraw);
break;
}
case wxSplitterWindow_SetSashSize: { // wxSplitterWindow::SetSashSize
wxSplitterWindow *This = (wxSplitterWindow *) getPtr(bp,memenv); bp += 4;
int * width = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetSashSize((int) *width);
+ This->SetSashSize(*width);
break;
}
case wxSplitterWindow_SetMinimumPaneSize: { // wxSplitterWindow::SetMinimumPaneSize
wxSplitterWindow *This = (wxSplitterWindow *) getPtr(bp,memenv); bp += 4;
int * min = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetMinimumPaneSize((int) *min);
+ This->SetMinimumPaneSize(*min);
break;
}
case wxSplitterWindow_SetSplitMode: { // wxSplitterWindow::SetSplitMode
wxSplitterWindow *This = (wxSplitterWindow *) getPtr(bp,memenv); bp += 4;
int * mode = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetSplitMode((int) *mode);
+ This->SetSplitMode(*mode);
break;
}
case wxSplitterWindow_SplitHorizontally: { // wxSplitterWindow::SplitHorizontally
@@ -30884,7 +30907,7 @@ case wxSplitterEvent_SetSashPosition: { // wxSplitterEvent::SetSashPosition
wxSplitterEvent *This = (wxSplitterEvent *) getPtr(bp,memenv); bp += 4;
int * pos = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetSashPosition((int) *pos);
+ This->SetSashPosition(*pos);
break;
}
case wxHtmlWindow_new_0: { // wxHtmlWindow::wxHtmlWindow
@@ -31053,7 +31076,7 @@ case wxHtmlWindow_SetBorders: { // wxHtmlWindow::SetBorders
wxHtmlWindow *This = (wxHtmlWindow *) getPtr(bp,memenv); bp += 4;
int * b = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetBorders((int) *b);
+ This->SetBorders(*b);
break;
}
case wxHtmlWindow_SetFonts: { // wxHtmlWindow::SetFonts
@@ -31098,7 +31121,7 @@ case wxHtmlWindow_SetRelatedStatusBar: { // wxHtmlWindow::SetRelatedStatusBar
wxHtmlWindow *This = (wxHtmlWindow *) getPtr(bp,memenv); bp += 4;
int * bar = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetRelatedStatusBar((int) *bar);
+ This->SetRelatedStatusBar(*bar);
break;
}
case wxHtmlWindow_ToText: { // wxHtmlWindow::ToText
@@ -31117,13 +31140,13 @@ case wxHtmlLinkEvent_GetLinkInfo: { // wxHtmlLinkEvent::GetLinkInfo
}
case wxSystemSettings_GetColour: { // wxSystemSettings::GetColour
wxSystemColour index = *(wxSystemColour *) bp; bp += 4;;
- wxColour Result = wxSystemSettings::GetColour((wxSystemColour) index);
+ wxColour Result = wxSystemSettings::GetColour(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);;
+ wxFont * Result = new wxFont(wxSystemSettings::GetFont(index)); newPtr((void *) Result,3, memenv);;
rt.addRef(getRef((void *)Result,memenv), "wxFont");
break;
}
@@ -31136,7 +31159,7 @@ case wxSystemSettings_GetMetric: { // wxSystemSettings::GetMetric
win = (wxWindow *) getPtr(bp,memenv); bp += 4;
} break;
}};
- int Result = wxSystemSettings::GetMetric((wxSystemMetric) index,win);
+ int Result = wxSystemSettings::GetMetric(index,win);
rt.addInt(Result);
break;
}
@@ -31192,14 +31215,14 @@ case wxSystemOptions_SetOption_2_0: { // wxSystemOptions::SetOption
wxString name = wxString(bp, wxConvUTF8);
bp += *nameLen+((8-((4+ *nameLen) & 7)) & 7);
int * value = (int *) bp; bp += 4;
- wxSystemOptions::SetOption(name,(int) *value);
+ wxSystemOptions::SetOption(name,*value);
break;
}
case wxAuiNotebookEvent_SetSelection: { // wxAuiNotebookEvent::SetSelection
wxAuiNotebookEvent *This = (wxAuiNotebookEvent *) getPtr(bp,memenv); bp += 4;
int * s = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetSelection((int) *s);
+ This->SetSelection(*s);
break;
}
case wxAuiNotebookEvent_GetSelection: { // wxAuiNotebookEvent::GetSelection
@@ -31213,7 +31236,7 @@ case wxAuiNotebookEvent_SetOldSelection: { // wxAuiNotebookEvent::SetOldSelectio
wxAuiNotebookEvent *This = (wxAuiNotebookEvent *) getPtr(bp,memenv); bp += 4;
int * s = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetOldSelection((int) *s);
+ This->SetOldSelection(*s);
break;
}
case wxAuiNotebookEvent_GetOldSelection: { // wxAuiNotebookEvent::GetOldSelection
@@ -31269,7 +31292,7 @@ case wxAuiManagerEvent_SetButton: { // wxAuiManagerEvent::SetButton
wxAuiManagerEvent *This = (wxAuiManagerEvent *) getPtr(bp,memenv); bp += 4;
int * b = (int *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetButton((int) *b);
+ This->SetButton(*b);
break;
}
case wxAuiManagerEvent_GetButton: { // wxAuiManagerEvent::GetButton
@@ -31317,7 +31340,7 @@ case wxAuiManagerEvent_SetCanVeto: { // wxAuiManagerEvent::SetCanVeto
wxAuiManagerEvent *This = (wxAuiManagerEvent *) getPtr(bp,memenv); bp += 4;
bool * can_veto = (bool *) bp; bp += 4;
if(!This) throw wxe_badarg(0);
- This->SetCanVeto((bool) *can_veto);
+ This->SetCanVeto(*can_veto);
break;
}
case wxAuiManagerEvent_CanVeto: { // wxAuiManagerEvent::CanVeto
@@ -31329,7 +31352,7 @@ case wxAuiManagerEvent_CanVeto: { // wxAuiManagerEvent::CanVeto
}
case wxLogNull_new: { // wxLogNull::wxLogNull
wxLogNull * Result = new wxLogNull();
- newPtr((void *) Result, 225, memenv);
+ newPtr((void *) Result, 224, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxLogNull");
break;
}
@@ -31416,10 +31439,10 @@ void WxeApp::delete_object(void *ptr, wxeRefData *refd) {
case 101: delete (wxListItemAttr *) ptr; break;
case 103: delete (wxTextAttr *) ptr; break;
case 155: delete (wxAuiPaneInfo *) ptr; break;
- case 212: /* delete (wxFileDataObject *) ptr;These objects must be deleted by owner object */ break;
- case 213: /* delete (wxTextDataObject *) ptr;These objects must be deleted by owner object */ break;
- case 214: /* delete (wxBitmapDataObject *) ptr;These objects must be deleted by owner object */ break;
- case 225: delete (wxLogNull *) ptr; break;
+ 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 224: delete (wxLogNull *) ptr; break;
default: delete (wxObject *) ptr;
}}
diff --git a/lib/wx/c_src/gen/wxe_macros.h b/lib/wx/c_src/gen/wxe_macros.h
index b24becae06..4b87c2340e 100644
--- a/lib/wx/c_src/gen/wxe_macros.h
+++ b/lib/wx/c_src/gen/wxe_macros.h
@@ -3201,155 +3201,154 @@
#define wxStyledTextCtrl_PointFromPosition 3370
#define wxStyledTextCtrl_ScrollToLine 3371
#define wxStyledTextCtrl_ScrollToColumn 3372
-#define wxStyledTextCtrl_SendMsg 3373
-#define wxStyledTextCtrl_SetVScrollBar 3374
-#define wxStyledTextCtrl_SetHScrollBar 3375
-#define wxStyledTextCtrl_GetLastKeydownProcessed 3376
-#define wxStyledTextCtrl_SetLastKeydownProcessed 3377
-#define wxStyledTextCtrl_SaveFile 3378
-#define wxStyledTextCtrl_LoadFile 3379
-#define wxStyledTextCtrl_DoDragOver 3380
-#define wxStyledTextCtrl_DoDropText 3381
-#define wxStyledTextCtrl_GetUseAntiAliasing 3382
-#define wxStyledTextCtrl_AddTextRaw 3383
-#define wxStyledTextCtrl_InsertTextRaw 3384
-#define wxStyledTextCtrl_GetCurLineRaw 3385
-#define wxStyledTextCtrl_GetLineRaw 3386
-#define wxStyledTextCtrl_GetSelectedTextRaw 3387
-#define wxStyledTextCtrl_GetTextRangeRaw 3388
-#define wxStyledTextCtrl_SetTextRaw 3389
-#define wxStyledTextCtrl_GetTextRaw 3390
-#define wxStyledTextCtrl_AppendTextRaw 3391
-#define wxArtProvider_GetBitmap 3392
-#define wxArtProvider_GetIcon 3393
-#define wxTreeEvent_GetKeyCode 3394
-#define wxTreeEvent_GetItem 3395
-#define wxTreeEvent_GetKeyEvent 3396
-#define wxTreeEvent_GetLabel 3397
-#define wxTreeEvent_GetOldItem 3398
-#define wxTreeEvent_GetPoint 3399
-#define wxTreeEvent_IsEditCancelled 3400
-#define wxTreeEvent_SetToolTip 3401
-#define wxNotebookEvent_GetOldSelection 3402
-#define wxNotebookEvent_GetSelection 3403
-#define wxNotebookEvent_SetOldSelection 3404
-#define wxNotebookEvent_SetSelection 3405
-#define wxFileDataObject_new 3406
-#define wxFileDataObject_AddFile 3407
-#define wxFileDataObject_GetFilenames 3408
-#define wxFileDataObject_destroy 3409
-#define wxTextDataObject_new 3410
-#define wxTextDataObject_GetTextLength 3411
-#define wxTextDataObject_GetText 3412
-#define wxTextDataObject_SetText 3413
-#define wxTextDataObject_destroy 3414
-#define wxBitmapDataObject_new_1_1 3415
-#define wxBitmapDataObject_new_1_0 3416
-#define wxBitmapDataObject_GetBitmap 3417
-#define wxBitmapDataObject_SetBitmap 3418
-#define wxBitmapDataObject_destroy 3419
-#define wxClipboard_new 3421
-#define wxClipboard_destruct 3422
-#define wxClipboard_AddData 3423
-#define wxClipboard_Clear 3424
-#define wxClipboard_Close 3425
-#define wxClipboard_Flush 3426
-#define wxClipboard_GetData 3427
-#define wxClipboard_IsOpened 3428
-#define wxClipboard_Open 3429
-#define wxClipboard_SetData 3430
-#define wxClipboard_UsePrimarySelection 3432
-#define wxClipboard_IsSupported 3433
-#define wxClipboard_Get 3434
-#define wxSpinEvent_GetPosition 3435
-#define wxSpinEvent_SetPosition 3436
-#define wxSplitterWindow_new_0 3437
-#define wxSplitterWindow_new_2 3438
-#define wxSplitterWindow_destruct 3439
-#define wxSplitterWindow_Create 3440
-#define wxSplitterWindow_GetMinimumPaneSize 3441
-#define wxSplitterWindow_GetSashGravity 3442
-#define wxSplitterWindow_GetSashPosition 3443
-#define wxSplitterWindow_GetSplitMode 3444
-#define wxSplitterWindow_GetWindow1 3445
-#define wxSplitterWindow_GetWindow2 3446
-#define wxSplitterWindow_Initialize 3447
-#define wxSplitterWindow_IsSplit 3448
-#define wxSplitterWindow_ReplaceWindow 3449
-#define wxSplitterWindow_SetSashGravity 3450
-#define wxSplitterWindow_SetSashPosition 3451
-#define wxSplitterWindow_SetSashSize 3452
-#define wxSplitterWindow_SetMinimumPaneSize 3453
-#define wxSplitterWindow_SetSplitMode 3454
-#define wxSplitterWindow_SplitHorizontally 3455
-#define wxSplitterWindow_SplitVertically 3456
-#define wxSplitterWindow_Unsplit 3457
-#define wxSplitterWindow_UpdateSize 3458
-#define wxSplitterEvent_GetSashPosition 3459
-#define wxSplitterEvent_GetX 3460
-#define wxSplitterEvent_GetY 3461
-#define wxSplitterEvent_GetWindowBeingRemoved 3462
-#define wxSplitterEvent_SetSashPosition 3463
-#define wxHtmlWindow_new_0 3464
-#define wxHtmlWindow_new_2 3465
-#define wxHtmlWindow_AppendToPage 3466
-#define wxHtmlWindow_GetOpenedAnchor 3467
-#define wxHtmlWindow_GetOpenedPage 3468
-#define wxHtmlWindow_GetOpenedPageTitle 3469
-#define wxHtmlWindow_GetRelatedFrame 3470
-#define wxHtmlWindow_HistoryBack 3471
-#define wxHtmlWindow_HistoryCanBack 3472
-#define wxHtmlWindow_HistoryCanForward 3473
-#define wxHtmlWindow_HistoryClear 3474
-#define wxHtmlWindow_HistoryForward 3475
-#define wxHtmlWindow_LoadFile 3476
-#define wxHtmlWindow_LoadPage 3477
-#define wxHtmlWindow_SelectAll 3478
-#define wxHtmlWindow_SelectionToText 3479
-#define wxHtmlWindow_SelectLine 3480
-#define wxHtmlWindow_SelectWord 3481
-#define wxHtmlWindow_SetBorders 3482
-#define wxHtmlWindow_SetFonts 3483
-#define wxHtmlWindow_SetPage 3484
-#define wxHtmlWindow_SetRelatedFrame 3485
-#define wxHtmlWindow_SetRelatedStatusBar 3486
-#define wxHtmlWindow_ToText 3487
-#define wxHtmlWindow_destroy 3488
-#define wxHtmlLinkEvent_GetLinkInfo 3489
-#define wxSystemSettings_GetColour 3490
-#define wxSystemSettings_GetFont 3491
-#define wxSystemSettings_GetMetric 3492
-#define wxSystemSettings_GetScreenType 3493
-#define wxSystemOptions_GetOption 3494
-#define wxSystemOptions_GetOptionInt 3495
-#define wxSystemOptions_HasOption 3496
-#define wxSystemOptions_IsFalse 3497
-#define wxSystemOptions_SetOption_2_1 3498
-#define wxSystemOptions_SetOption_2_0 3499
-#define wxAuiNotebookEvent_SetSelection 3500
-#define wxAuiNotebookEvent_GetSelection 3501
-#define wxAuiNotebookEvent_SetOldSelection 3502
-#define wxAuiNotebookEvent_GetOldSelection 3503
-#define wxAuiNotebookEvent_SetDragSource 3504
-#define wxAuiNotebookEvent_GetDragSource 3505
-#define wxAuiManagerEvent_SetManager 3506
-#define wxAuiManagerEvent_GetManager 3507
-#define wxAuiManagerEvent_SetPane 3508
-#define wxAuiManagerEvent_GetPane 3509
-#define wxAuiManagerEvent_SetButton 3510
-#define wxAuiManagerEvent_GetButton 3511
-#define wxAuiManagerEvent_SetDC 3512
-#define wxAuiManagerEvent_GetDC 3513
-#define wxAuiManagerEvent_Veto 3514
-#define wxAuiManagerEvent_GetVeto 3515
-#define wxAuiManagerEvent_SetCanVeto 3516
-#define wxAuiManagerEvent_CanVeto 3517
-#define wxLogNull_new 3518
-#define wxLogNull_destroy 3519
-#define wxTaskBarIcon_new 3520
-#define wxTaskBarIcon_destruct 3521
-#define wxTaskBarIcon_PopupMenu 3522
-#define wxTaskBarIcon_RemoveIcon 3523
-#define wxTaskBarIcon_SetIcon 3524
+#define wxStyledTextCtrl_SetVScrollBar 3373
+#define wxStyledTextCtrl_SetHScrollBar 3374
+#define wxStyledTextCtrl_GetLastKeydownProcessed 3375
+#define wxStyledTextCtrl_SetLastKeydownProcessed 3376
+#define wxStyledTextCtrl_SaveFile 3377
+#define wxStyledTextCtrl_LoadFile 3378
+#define wxStyledTextCtrl_DoDragOver 3379
+#define wxStyledTextCtrl_DoDropText 3380
+#define wxStyledTextCtrl_GetUseAntiAliasing 3381
+#define wxStyledTextCtrl_AddTextRaw 3382
+#define wxStyledTextCtrl_InsertTextRaw 3383
+#define wxStyledTextCtrl_GetCurLineRaw 3384
+#define wxStyledTextCtrl_GetLineRaw 3385
+#define wxStyledTextCtrl_GetSelectedTextRaw 3386
+#define wxStyledTextCtrl_GetTextRangeRaw 3387
+#define wxStyledTextCtrl_SetTextRaw 3388
+#define wxStyledTextCtrl_GetTextRaw 3389
+#define wxStyledTextCtrl_AppendTextRaw 3390
+#define wxArtProvider_GetBitmap 3391
+#define wxArtProvider_GetIcon 3392
+#define wxTreeEvent_GetKeyCode 3393
+#define wxTreeEvent_GetItem 3394
+#define wxTreeEvent_GetKeyEvent 3395
+#define wxTreeEvent_GetLabel 3396
+#define wxTreeEvent_GetOldItem 3397
+#define wxTreeEvent_GetPoint 3398
+#define wxTreeEvent_IsEditCancelled 3399
+#define wxTreeEvent_SetToolTip 3400
+#define wxNotebookEvent_GetOldSelection 3401
+#define wxNotebookEvent_GetSelection 3402
+#define wxNotebookEvent_SetOldSelection 3403
+#define wxNotebookEvent_SetSelection 3404
+#define wxFileDataObject_new 3405
+#define wxFileDataObject_AddFile 3406
+#define wxFileDataObject_GetFilenames 3407
+#define wxFileDataObject_destroy 3408
+#define wxTextDataObject_new 3409
+#define wxTextDataObject_GetTextLength 3410
+#define wxTextDataObject_GetText 3411
+#define wxTextDataObject_SetText 3412
+#define wxTextDataObject_destroy 3413
+#define wxBitmapDataObject_new_1_1 3414
+#define wxBitmapDataObject_new_1_0 3415
+#define wxBitmapDataObject_GetBitmap 3416
+#define wxBitmapDataObject_SetBitmap 3417
+#define wxBitmapDataObject_destroy 3418
+#define wxClipboard_new 3420
+#define wxClipboard_destruct 3421
+#define wxClipboard_AddData 3422
+#define wxClipboard_Clear 3423
+#define wxClipboard_Close 3424
+#define wxClipboard_Flush 3425
+#define wxClipboard_GetData 3426
+#define wxClipboard_IsOpened 3427
+#define wxClipboard_Open 3428
+#define wxClipboard_SetData 3429
+#define wxClipboard_UsePrimarySelection 3431
+#define wxClipboard_IsSupported 3432
+#define wxClipboard_Get 3433
+#define wxSpinEvent_GetPosition 3434
+#define wxSpinEvent_SetPosition 3435
+#define wxSplitterWindow_new_0 3436
+#define wxSplitterWindow_new_2 3437
+#define wxSplitterWindow_destruct 3438
+#define wxSplitterWindow_Create 3439
+#define wxSplitterWindow_GetMinimumPaneSize 3440
+#define wxSplitterWindow_GetSashGravity 3441
+#define wxSplitterWindow_GetSashPosition 3442
+#define wxSplitterWindow_GetSplitMode 3443
+#define wxSplitterWindow_GetWindow1 3444
+#define wxSplitterWindow_GetWindow2 3445
+#define wxSplitterWindow_Initialize 3446
+#define wxSplitterWindow_IsSplit 3447
+#define wxSplitterWindow_ReplaceWindow 3448
+#define wxSplitterWindow_SetSashGravity 3449
+#define wxSplitterWindow_SetSashPosition 3450
+#define wxSplitterWindow_SetSashSize 3451
+#define wxSplitterWindow_SetMinimumPaneSize 3452
+#define wxSplitterWindow_SetSplitMode 3453
+#define wxSplitterWindow_SplitHorizontally 3454
+#define wxSplitterWindow_SplitVertically 3455
+#define wxSplitterWindow_Unsplit 3456
+#define wxSplitterWindow_UpdateSize 3457
+#define wxSplitterEvent_GetSashPosition 3458
+#define wxSplitterEvent_GetX 3459
+#define wxSplitterEvent_GetY 3460
+#define wxSplitterEvent_GetWindowBeingRemoved 3461
+#define wxSplitterEvent_SetSashPosition 3462
+#define wxHtmlWindow_new_0 3463
+#define wxHtmlWindow_new_2 3464
+#define wxHtmlWindow_AppendToPage 3465
+#define wxHtmlWindow_GetOpenedAnchor 3466
+#define wxHtmlWindow_GetOpenedPage 3467
+#define wxHtmlWindow_GetOpenedPageTitle 3468
+#define wxHtmlWindow_GetRelatedFrame 3469
+#define wxHtmlWindow_HistoryBack 3470
+#define wxHtmlWindow_HistoryCanBack 3471
+#define wxHtmlWindow_HistoryCanForward 3472
+#define wxHtmlWindow_HistoryClear 3473
+#define wxHtmlWindow_HistoryForward 3474
+#define wxHtmlWindow_LoadFile 3475
+#define wxHtmlWindow_LoadPage 3476
+#define wxHtmlWindow_SelectAll 3477
+#define wxHtmlWindow_SelectionToText 3478
+#define wxHtmlWindow_SelectLine 3479
+#define wxHtmlWindow_SelectWord 3480
+#define wxHtmlWindow_SetBorders 3481
+#define wxHtmlWindow_SetFonts 3482
+#define wxHtmlWindow_SetPage 3483
+#define wxHtmlWindow_SetRelatedFrame 3484
+#define wxHtmlWindow_SetRelatedStatusBar 3485
+#define wxHtmlWindow_ToText 3486
+#define wxHtmlWindow_destroy 3487
+#define wxHtmlLinkEvent_GetLinkInfo 3488
+#define wxSystemSettings_GetColour 3489
+#define wxSystemSettings_GetFont 3490
+#define wxSystemSettings_GetMetric 3491
+#define wxSystemSettings_GetScreenType 3492
+#define wxSystemOptions_GetOption 3493
+#define wxSystemOptions_GetOptionInt 3494
+#define wxSystemOptions_HasOption 3495
+#define wxSystemOptions_IsFalse 3496
+#define wxSystemOptions_SetOption_2_1 3497
+#define wxSystemOptions_SetOption_2_0 3498
+#define wxAuiNotebookEvent_SetSelection 3499
+#define wxAuiNotebookEvent_GetSelection 3500
+#define wxAuiNotebookEvent_SetOldSelection 3501
+#define wxAuiNotebookEvent_GetOldSelection 3502
+#define wxAuiNotebookEvent_SetDragSource 3503
+#define wxAuiNotebookEvent_GetDragSource 3504
+#define wxAuiManagerEvent_SetManager 3505
+#define wxAuiManagerEvent_GetManager 3506
+#define wxAuiManagerEvent_SetPane 3507
+#define wxAuiManagerEvent_GetPane 3508
+#define wxAuiManagerEvent_SetButton 3509
+#define wxAuiManagerEvent_GetButton 3510
+#define wxAuiManagerEvent_SetDC 3511
+#define wxAuiManagerEvent_GetDC 3512
+#define wxAuiManagerEvent_Veto 3513
+#define wxAuiManagerEvent_GetVeto 3514
+#define wxAuiManagerEvent_SetCanVeto 3515
+#define wxAuiManagerEvent_CanVeto 3516
+#define wxLogNull_new 3517
+#define wxLogNull_destroy 3518
+#define wxTaskBarIcon_new 3519
+#define wxTaskBarIcon_destruct 3520
+#define wxTaskBarIcon_PopupMenu 3521
+#define wxTaskBarIcon_RemoveIcon 3522
+#define wxTaskBarIcon_SetIcon 3523
diff --git a/lib/wx/c_src/wxe_impl.cpp b/lib/wx/c_src/wxe_impl.cpp
index 69fcd4e362..527fabc315 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-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2008-2012. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -30,6 +30,11 @@
// Ok ugly but needed for wxBufferedDC crash workaround
#define private public
#include <wx/dcbuffer.h>
+
+#if defined(__WXMSW__)
+ #include <wx/msw/private.h> // for wxSetInstance
+#endif
+
#undef private
#include "wxe_impl.h"
@@ -222,6 +227,11 @@ void *wxe_main_loop(void *vpdl)
// This should be done in emulator but it's not in yet.
#ifndef _WIN32
erts_thread_disable_fpe();
+#else
+ // Setup that wxWidgets should look for cursors and icons in
+ // this dll and not in werl.exe (which is the default)
+ HMODULE WXEHandle = GetModuleHandle(_T("wxe_driver"));
+ wxSetInstance((HINSTANCE) WXEHandle);
#endif
result = wxEntry(argc, argv);
@@ -248,20 +258,30 @@ wxFrame * dummy_window;
void create_dummy_window() {
dummy_window = new wxFrame(NULL,-1, wxT("wx driver"),
- wxDefaultPosition, wxSize(5,5),
+ wxPoint(0,0), wxSize(5,5),
wxFRAME_NO_TASKBAR);
+
+ wxMenuBar * menubar = new wxMenuBar();
+ dummy_window->SetMenuBar(menubar);
+ // wx-2.9 Don't delete the app menubar correctly
dummy_window->Connect(wxID_ANY, wxEVT_CLOSE_WINDOW,
(wxObjectEventFunction) (wxEventFunction) &WxeApp::dummy_close);
+ dummy_window->Connect(wxID_ANY, wxEVT_COMMAND_MENU_SELECTED,
+ (wxObjectEventFunction) (wxEventFunction) &WxeApp::dummy_close);
+ dummy_window->Show(true);
+ // dummy_window->Show(false);
}
// wxMac really wants a top level window which command-q quits if there are no
// windows open, and this will kill the thread, so restart the dummy_window each
// time a we receive a close.
void WxeApp::dummy_close(wxEvent& Ev) {
- // fprintf(stderr, "Tried to close dummy window\r\n"); fflush(stderr);
- create_dummy_window();
+ if(Ev.GetEventType() == wxEVT_CLOSE_WINDOW) {
+ create_dummy_window();
+ }
}
+
// Init wx-widgets thread
bool WxeApp::OnInit()
{
@@ -272,7 +292,7 @@ bool WxeApp::OnInit()
wxe_batch_cb_saved = new wxList;
cb_buff = NULL;
- wxIdleEvent::SetMode(wxIDLE_PROCESS_SPECIFIED);
+ // wxIdleEvent::SetMode(wxIDLE_PROCESS_SPECIFIED); Hmm printpreview doesn't work in 2.9 with this
this->Connect(wxID_ANY, wxEVT_IDLE,
(wxObjectEventFunction) (wxEventFunction) &WxeApp::idle);
@@ -290,7 +310,11 @@ bool WxeApp::OnInit()
/* Create a dummy window so wxWidgets don't automagicly quits the main loop
after the last window */
+#ifdef __DARWIN__
create_dummy_window();
+#else
+ SetExitOnFrameDelete(false);
+#endif
init_nonconsts(global_me, init_caller);
erl_drv_mutex_lock(wxe_status_m);
@@ -301,7 +325,9 @@ bool WxeApp::OnInit()
}
void WxeApp::shutdown(wxeMetaCommand& Ecmd) {
+#ifdef __DARWIN__
delete dummy_window;
+#endif
ExitMainLoop();
}
diff --git a/lib/wx/c_src/wxe_ps_init.c b/lib/wx/c_src/wxe_ps_init.c
index a85f751024..025ea90f8b 100644
--- a/lib/wx/c_src/wxe_ps_init.c
+++ b/lib/wx/c_src/wxe_ps_init.c
@@ -25,12 +25,17 @@
#include <Cocoa/Cocoa.h>
#include <objc/objc-runtime.h>
+extern OSErr CPSSetProcessName (ProcessSerialNumber *psn, char *processname);
+
void * wxe_ps_init()
{
ProcessSerialNumber psn;
NSAutoreleasePool *pool;
// Enable GUI
GetCurrentProcess(&psn);
+ char *app_title = getenv("WX_APP_TITLE");
+ // Undocumented function (but no documented way of doing this exists)
+ CPSSetProcessName(&psn, app_title?app_title:"Erlang");
TransformProcessType(&psn, kProcessTransformToForegroundApplication);
SetFrontProcess(&psn);
// Enable Cocoa calls from Carbon app
diff --git a/lib/wx/configure.in b/lib/wx/configure.in
index 8e5f696bc7..c45d2285af 100755
--- a/lib/wx/configure.in
+++ b/lib/wx/configure.in
@@ -2,7 +2,7 @@ dnl Process this file with autoconf to produce a configure script. -*-m4-*-
dnl %CopyrightBegin%
dnl
-dnl Copyright Ericsson AB 2008-2011. All Rights Reserved.
+dnl Copyright Ericsson AB 2008-2012. All Rights Reserved.
dnl
dnl The contents of this file are subject to the Erlang Public License,
dnl Version 1.1, (the "License"); you may not use this file except in
@@ -80,20 +80,20 @@ fi
## Otherwise skip building wxErlang
AC_CHECK_SIZEOF(void *)
-case $ac_cv_sizeof_void_p-$host_os in
- 8-darwin*)
- if test X"$WX_BUILDING_INSIDE_ERLSRC" != X"true" ; then
- AC_MSG_ERROR([Can not use 64bits wxWidgets on Darwin])
- else
- echo "Can not combine 64bits erlang with wxWidgets on MacOSX, wx will not be useable" > ./CONF_INFO
- WXERL_CAN_BUILD_DRIVER=false
- AC_MSG_WARN([Can not combine 64bits erlang with wxWidgets on MacOSX, wx will not be useable])
- fi
- WXERL_CAN_BUILD_DRIVER=false
- ;;
- *)
- ;;
-esac
+# case $ac_cv_sizeof_void_p-$host_os in
+# 8-darwin*)
+# if test X"$WX_BUILDING_INSIDE_ERLSRC" != X"true" ; then
+# AC_MSG_ERROR([Can not use 64bits wxWidgets on Darwin])
+# else
+# echo "Can not combine 64bits erlang with wxWidgets on MacOSX, wx will not be useable" > ./CONF_INFO
+# WXERL_CAN_BUILD_DRIVER=false
+# AC_MSG_WARN([Can not combine 64bits erlang with wxWidgets on MacOSX, wx will not be useable])
+# fi
+# WXERL_CAN_BUILD_DRIVER=false
+# ;;
+# *)
+# ;;
+# esac
PTHR_CFLAGS="-D_THREAD_SAFE -D_REENTRANT"
@@ -155,7 +155,7 @@ case $host_os in
OBJC_CFLAGS="-ObjC"
fi
fi
- CFLAGS=$saved_CFLAGS
+ CFLAGS="$saved_CFLAGS -Wno-deprecated-declarations"
CPPFLAGS="$CPPFLAGS -D_MACOSX $PTHR_CFLAGS"
;;
mingw32)
@@ -169,6 +169,7 @@ case $host_os in
CPPFLAGS="$CPPFLAGS -D_WIN32_WINNT=0x0500"
;;
*)
+ CFLAGS="$CFLAGS -Wno-deprecated-declarations"
CPPFLAGS="$CPPFLAGS -D_GNU_SOURCE $PTHR_CFLAGS"
;;
esac
@@ -252,6 +253,18 @@ else
AC_CHECK_HEADERS([gl/gl.h],[],[],[#include <windows.h>])
fi
+if test X"$host_os" != X"win32" ; then
+ AC_CHECK_HEADERS([GL/glu.h], [],
+ [AC_CHECK_HEADERS([OpenGL/glu.h])])
+ if test X"$ac_cv_header_GL_glu_h" != Xyes &&
+ test X"$ac_cv_header_OpenGL_glu_h" != Xyes
+ then
+ AC_MSG_WARN([No GLU headers found, wx will NOT be usable])
+ fi
+else
+ AC_CHECK_HEADERS([gl/glu.h],[],[],[#include <windows.h>])
+fi
+
AC_SUBST(GL_LIBS)
CXXFLAGS="$CFLAGS $CPPFLAGS"
@@ -308,7 +321,15 @@ if test "$cross_compiling" = "yes"; then
echo "Cross compilation of the wx driver is not supported yet, wx will NOT be usable" > ./CONF_INFO
WXERL_CAN_BUILD_DRIVER=false
elif test X"$MIXED_CYGWIN_VC" == X"no" -a X"$MIXED_MSYS_VC" == X"no"; then
- m4_include(wxwin.m4)
+ WX_VERSION=`wx-config --version`
+ case $WX_VERSION in
+ 2.8.*)
+ m4_include(wxwin-2.8.m4)
+ ;;
+ *)
+ m4_include(wxwin-2.9.m4)
+ ;;
+ esac
AM_OPTIONS_WXCONFIG
reqwx=2.8.4
@@ -395,12 +416,12 @@ else
else
CWXWIN_PROG=`cygpath -d "$PROGRAMFILES" | cygpath -f - 2>/dev/null`
fi
- CWXWIN3=$CWXWIN_PROG/wxWidgets-2.8
- CWXWIN4=$CWXWIN_PROG/wxMSW-2.8
- CWX_DOCUMENTED="/opt/local/pgm/wxMSW-2.8.* /opt/local/pgm/wxWidgets-2.8.*"
+ CWXWIN3=$CWXWIN_PROG/wxWidgets-2.*.*
+ CWXWIN4=$CWXWIN_PROG/wxMSW-2.*.*
+ CWX_DOCUMENTED="/opt/local/pgm/wxMSW-2.*.* /opt/local/pgm/wxWidgets-2.*.*"
case $ac_cv_sizeof_void_p in
8)
- CWX_DOCUMENTED="/opt/local64/pgm/wxMSW-2.8.* /opt/local64/pgm/wxWidgets-2.8.* $CWX_DOCUMENTED"
+ CWX_DOCUMENTED="/opt/local64/pgm/wxMSW-2.*.* /opt/local64/pgm/wxWidgets-2.*.* $CWX_DOCUMENTED"
;;
*)
true
@@ -442,6 +463,7 @@ else
fi
fi
done
+
if test -z "$WX_LIBS_STATIC"; then
AC_MSG_RESULT([failed])
if test X"$WX_BUILDING_INSIDE_ERLSRC" != X"true" ; then
@@ -621,6 +643,12 @@ fi dnl - if test "$WXERL_CAN_BUILD_DRIVER" != "false"
AC_SUBST(WXERL_CAN_BUILD_DRIVER)
+if test "x$GCC" = xyes; then
+ # Treat certain GCC warnings as errors
+ LM_TRY_ENABLE_CFLAG([-Werror=return-type], [CFLAGS])
+ LM_TRY_ENABLE_CFLAG([-Werror=return-type], [CXXFLAGS])
+fi
+
#############################################################################
dnl
diff --git a/lib/wx/doc/src/notes.xml b/lib/wx/doc/src/notes.xml
index 1ba7e62d7d..95036bd7f0 100644
--- a/lib/wx/doc/src/notes.xml
+++ b/lib/wx/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2009</year><year>2011</year>
+ <year>2009</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
diff --git a/lib/wx/examples/demo/demo.erl b/lib/wx/examples/demo/demo.erl
index 61e71af021..2f560096f5 100644
--- a/lib/wx/examples/demo/demo.erl
+++ b/lib/wx/examples/demo/demo.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -32,6 +32,21 @@
-record(state, {win, demo, example, selector, log, code}).
+%% For wx-2.9 usage
+-ifndef(wxSTC_ERLANG_COMMENT_FUNCTION).
+-define(wxSTC_ERLANG_COMMENT_FUNCTION, 14).
+-define(wxSTC_ERLANG_COMMENT_MODULE, 15).
+-define(wxSTC_ERLANG_COMMENT_DOC, 16).
+-define(wxSTC_ERLANG_COMMENT_DOC_MACRO, 17).
+-define(wxSTC_ERLANG_ATOM_QUOTED, 18).
+-define(wxSTC_ERLANG_MACRO_QUOTED, 19).
+-define(wxSTC_ERLANG_RECORD_QUOTED, 20).
+-define(wxSTC_ERLANG_NODE_NAME_QUOTED, 21).
+-define(wxSTC_ERLANG_BIFS, 22).
+-define(wxSTC_ERLANG_MODULES, 23).
+-define(wxSTC_ERLANG_MODULES_ATT, 24).
+-endif.
+
start() ->
start([]).
@@ -78,6 +93,7 @@ init(Options) ->
wxFrame:setMenuBar(Frame,MB),
wxFrame:connect(Frame, command_menu_selected),
+ wxFrame:connect(Frame, close_window),
_SB = wxFrame:createStatusBar(Frame,[]),
@@ -179,6 +195,8 @@ create_subwindow(Parent, BoxLabel, Funs) ->
%% Handled as in normal gen_server callbacks
handle_info({'EXIT',_, wx_deleted}, State) ->
{noreply,State};
+handle_info({'EXIT',_, shutdown}, State) ->
+ {noreply,State};
handle_info({'EXIT',_, normal}, State) ->
{noreply,State};
handle_info(Msg, State) ->
@@ -197,13 +215,13 @@ handle_cast(Msg, State) ->
handle_event(#wx{event=#wxCommand{type=command_listbox_selected, cmdString=Ex}},
State = #state{demo={_,DemoSz}, example=Example, code=Code}) ->
case Ex of
- [] ->
+ [] ->
{noreply, State};
_ ->
wxSizer:detach(DemoSz, Example),
- wxWindow:destroy(Example),
+ wx_object:call(Example, shutdown),
unload_code(Code),
- NewExample = load_example(Ex, State),
+ NewExample = load_example(Ex, State),
wxSizer:add(DemoSz, NewExample, [{proportion,1}, {flag, ?wxEXPAND}]),
wxSizer:layout(DemoSz),
{noreply, State#state{example=NewExample}}
@@ -247,9 +265,9 @@ handle_event(#wx{id = Id,
?wxICON_INFORMATION bor
?wxSTAY_ON_TOP},
{caption, "About"}])),
- {noreply, State};
+ {noreply, State};
?wxID_EXIT ->
- wx_object:get_pid(State#state.example) ! stop,
+ wx_object:call(State#state.example, shutdown),
{stop, normal, State};
_ ->
{noreply, State}
@@ -265,9 +283,9 @@ handle_event(Ev,State) ->
code_change(_, _, State) ->
{stop, not_yet_implemented, State}.
-terminate(_Reason, State) ->
- wx_object:get_pid(State#state.example) ! stop,
- timer:sleep(200), %% Give the example process some time to cleanup.
+terminate(_Reason, State = #state{win=Frame}) ->
+ catch wx_object:call(State#state.example, shutdown),
+ wxFrame:destroy(Frame),
wx:destroy().
%%%%%%%%%%%%%%%%% Internals %%%%%%%%%%
@@ -275,8 +293,6 @@ terminate(_Reason, State) ->
load_example(Ex, #state{demo={DemoPanel,DemoSz}, log=EvCtrl, code=Code}) ->
ModStr = "ex_" ++ Ex,
Mod = list_to_atom(ModStr),
-%% WxDir = code:lib_dir(wx),
-%% ModFile = filename:join([WxDir, "examples","demo", ModStr ++ ".erl"]),
ModFile = ModStr ++ ".erl",
load_code(Code, file:read_file(ModFile)),
find(Code),
@@ -312,7 +328,20 @@ code_area(Parent) ->
{?wxSTC_ERLANG_MACRO, {40,144,170}},
{?wxSTC_ERLANG_RECORD, {40,100,20}},
{?wxSTC_ERLANG_SEPARATOR,{0,0,0}},
- {?wxSTC_ERLANG_NODE_NAME,{0,0,0}}],
+ {?wxSTC_ERLANG_NODE_NAME,{0,0,0}},
+ %% Optional 2.9 stuff
+ {?wxSTC_ERLANG_COMMENT_FUNCTION, {160,53,35}},
+ {?wxSTC_ERLANG_COMMENT_MODULE, {160,53,35}},
+ {?wxSTC_ERLANG_COMMENT_DOC, {160,53,35}},
+ {?wxSTC_ERLANG_COMMENT_DOC_MACRO, {160,53,35}},
+ {?wxSTC_ERLANG_ATOM_QUOTED, {0,0,0}},
+ {?wxSTC_ERLANG_MACRO_QUOTED, {40,144,170}},
+ {?wxSTC_ERLANG_RECORD_QUOTED, {40,100,20}},
+ {?wxSTC_ERLANG_NODE_NAME_QUOTED, {0,0,0}},
+ {?wxSTC_ERLANG_BIFS, {130,40,172}},
+ {?wxSTC_ERLANG_MODULES, {64,102,244}},
+ {?wxSTC_ERLANG_MODULES_ATT, {64,102,244}}
+ ],
SetStyle = fun({Style, Color}) ->
?stc:styleSetFont(Ed, Style, FixedFont),
?stc:styleSetForeground(Ed, Style, Color)
diff --git a/lib/wx/examples/demo/demo_html_tagger.erl b/lib/wx/examples/demo/demo_html_tagger.erl
index 243e5d659f..46bfe73676 100644
--- a/lib/wx/examples/demo/demo_html_tagger.erl
+++ b/lib/wx/examples/demo/demo_html_tagger.erl
@@ -485,7 +485,6 @@ is_keyword('not') -> true;
is_keyword('of' ) -> true;
is_keyword('or' ) -> true;
is_keyword('orelse' ) -> true;
-is_keyword('query' ) -> true;
is_keyword('receive' ) -> true;
is_keyword('rem' ) -> true;
is_keyword('spec') -> true;
diff --git a/lib/wx/examples/demo/ex_aui.erl b/lib/wx/examples/demo/ex_aui.erl
index 50f077638d..70372caad8 100644
--- a/lib/wx/examples/demo/ex_aui.erl
+++ b/lib/wx/examples/demo/ex_aui.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -32,7 +32,8 @@
-record(state,
{
parent,
- config
+ config,
+ aui
}).
start(Config) ->
@@ -47,12 +48,10 @@ init(Config) ->
do_init(Config) ->
Parent = proplists:get_value(parent, Config),
Panel = wxPanel:new(Parent, []),
-
%% Setup sizers
MainSizer = wxBoxSizer:new(?wxVERTICAL),
- Manager = wxAuiManager:new([{managed_wnd, Panel}
- ]),
+ Manager = wxAuiManager:new([{managed_wnd, Panel}]),
Pane = ?pi:new(),
?pi:closeButton(Pane),
@@ -79,8 +78,8 @@ do_init(Config) ->
wxAuiManager:connect(Manager, aui_pane_button, [{skip,true}]),
wxAuiManager:connect(Manager, aui_pane_maximize, [{skip,true}]),
wxAuiManager:update(Manager),
-
- {Panel, #state{parent=Panel, config=Config}}.
+ process_flag(trap_exit, true),
+ {Panel, #state{parent=Panel, config=Config, aui=Manager}}.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Callbacks handled as normal gen_server callbacks
@@ -88,6 +87,12 @@ handle_info(Msg, State) ->
demo:format(State#state.config, "Got Info ~p\n", [Msg]),
{noreply, State}.
+handle_call(shutdown, _From, State=#state{parent=Panel, aui=Manager}) ->
+ wxAuiManager:unInit(Manager),
+ wxAuiManager:destroy(Manager),
+ wxPanel:destroy(Panel),
+ {stop, normal, ok, State};
+
handle_call(Msg, _From, State) ->
demo:format(State#state.config, "Got Call ~p\n", [Msg]),
{reply,{error, nyi}, State}.
@@ -134,11 +139,10 @@ handle_event(Ev = #wx{}, State) ->
io:format("~p\n", [Ev]),
{noreply, State}.
-
code_change(_, _, State) ->
{stop, ignore, State}.
-terminate(_Reason, _State) ->
+terminate(_Reason, _) ->
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/wx/examples/demo/ex_button.erl b/lib/wx/examples/demo/ex_button.erl
index 0dd0363933..41bf34e572 100644
--- a/lib/wx/examples/demo/ex_button.erl
+++ b/lib/wx/examples/demo/ex_button.erl
@@ -25,7 +25,8 @@
-include_lib("wx/include/wx.hrl").
-behaviour(wx_object).
--export([start/1, init/1, terminate/2, code_change/3,
+-export([start/1, init/1,
+ terminate/2, code_change/3,
handle_info/2, handle_call/3, handle_cast/2, handle_event/2]).
-record(state,
@@ -120,6 +121,7 @@ do_init(Config) ->
wxWindow:connect(Panel, command_button_clicked),
wxWindow:setSizer(Panel, Sz),
wxSizer:layout(Sz),
+ wxWindow:refresh(Panel),
wxScrolledWindow:setScrollRate(Panel, 5, 5),
{Panel, #state{parent=Panel, config=Config}}.
@@ -149,6 +151,10 @@ handle_info(Msg, State) ->
demo:format(State#state.config, "Got Info ~p~n",[Msg]),
{noreply,State}.
+handle_call(shutdown, _From, State=#state{parent=Panel}) ->
+ wxPanel:destroy(Panel),
+ {stop, normal, ok, State};
+
handle_call(Msg, _From, State) ->
demo:format(State#state.config,"Got Call ~p~n",[Msg]),
{reply,ok,State}.
@@ -160,7 +166,7 @@ handle_cast(Msg, State) ->
code_change(_, _, State) ->
{stop, ignore, State}.
-terminate(_Reason, _State) ->
+terminate(_Reason, _) ->
ok.
%%%%% a copy from wxwidgets samples.
diff --git a/lib/wx/examples/demo/ex_canvas.erl b/lib/wx/examples/demo/ex_canvas.erl
index 1ec4760f40..1f1d260276 100644
--- a/lib/wx/examples/demo/ex_canvas.erl
+++ b/lib/wx/examples/demo/ex_canvas.erl
@@ -140,6 +140,9 @@ handle_info(Msg, State) ->
demo:format(State#state.config, "Got Info ~p\n", [Msg]),
{noreply, State}.
+handle_call(shutdown, _From, State=#state{parent=Panel}) ->
+ wxPanel:destroy(Panel),
+ {stop, normal, ok, State};
handle_call(Msg, _From, State) ->
demo:format(State#state.config, "Got Call ~p\n", [Msg]),
{reply,{error, nyi}, State}.
@@ -151,7 +154,7 @@ handle_cast(Msg, State) ->
code_change(_, _, State) ->
{stop, ignore, State}.
-terminate(_Reason, _State) ->
+terminate(_Reason, _) ->
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/wx/examples/demo/ex_canvas_paint.erl b/lib/wx/examples/demo/ex_canvas_paint.erl
index 9bc083766a..6873724655 100644
--- a/lib/wx/examples/demo/ex_canvas_paint.erl
+++ b/lib/wx/examples/demo/ex_canvas_paint.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -207,6 +207,10 @@ handle_info(Msg, State) ->
demo:format(State#state.config, "Got Info ~p\n", [Msg]),
{noreply, State}.
+handle_call(shutdown, _From, State=#state{parent=Panel}) ->
+ wxPanel:destroy(Panel),
+ {stop, normal, ok, State};
+
handle_call(Msg, _From, State) ->
demo:format(State#state.config, "Got Call ~p\n", [Msg]),
{reply,{error, nyi}, State}.
@@ -218,7 +222,7 @@ handle_cast(Msg, State) ->
code_change(_, _, State) ->
{stop, ignore, State}.
-terminate(_Reason, _State) ->
+terminate(_Reason, _) ->
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -239,14 +243,17 @@ draw(Canvas, Bitmap, Fun) ->
wxMemoryDC:destroy(MemoryDC).
redraw(DC, Bitmap) ->
- MemoryDC = wxMemoryDC:new(Bitmap),
+ try
+ MemoryDC = wxMemoryDC:new(Bitmap),
- wxDC:blit(DC, {0,0},
- {wxBitmap:getWidth(Bitmap), wxBitmap:getHeight(Bitmap)},
- MemoryDC, {0,0}),
-
- wxMemoryDC:destroy(MemoryDC).
+ wxDC:blit(DC, {0,0},
+ {wxBitmap:getWidth(Bitmap), wxBitmap:getHeight(Bitmap)},
+ MemoryDC, {0,0}),
+ wxMemoryDC:destroy(MemoryDC)
+ catch error:{{badarg,_},_} -> %% Bitmap have been deleted
+ ok
+ end.
-getPageInfo(_This) ->
+getPageInfo(_This) ->
{1,1,1,1}.
diff --git a/lib/wx/examples/demo/ex_choices.erl b/lib/wx/examples/demo/ex_choices.erl
index 2e456ae249..b4418293c1 100644
--- a/lib/wx/examples/demo/ex_choices.erl
+++ b/lib/wx/examples/demo/ex_choices.erl
@@ -143,6 +143,10 @@ handle_info(Msg, State) ->
demo:format(State#state.config, "Got Info ~p\n",[Msg]),
{noreply, State}.
+handle_call(shutdown, _From, State=#state{parent=Panel}) ->
+ wxPanel:destroy(Panel),
+ {stop, normal, ok, State};
+
handle_call(Msg, _From, State) ->
demo:format(State#state.config,"Got Call ~p\n",[Msg]),
{reply, {error,nyi}, State}.
@@ -154,7 +158,7 @@ handle_cast(Msg, State) ->
code_change(_, _, State) ->
{stop, ignore, State}.
-terminate(_Reason, _State) ->
+terminate(_Reason, _) ->
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/wx/examples/demo/ex_cursor.erl b/lib/wx/examples/demo/ex_cursor.erl
index c1a558541b..2f1eaaf7c7 100644
--- a/lib/wx/examples/demo/ex_cursor.erl
+++ b/lib/wx/examples/demo/ex_cursor.erl
@@ -131,6 +131,10 @@ handle_info(Msg, State) ->
demo:format(State#state.config, "Got Info ~p\n", [Msg]),
{noreply, State}.
+handle_call(shutdown, _From, State=#state{parent=Panel}) ->
+ wxPanel:destroy(Panel),
+ {stop, normal, ok, State};
+
handle_call(Msg, _From, State) ->
demo:format(State#state.config, "Got Call ~p\n", [Msg]),
{reply,{error, nyi}, State}.
diff --git a/lib/wx/examples/demo/ex_dialogs.erl b/lib/wx/examples/demo/ex_dialogs.erl
index b39344f8b1..5c47b51271 100644
--- a/lib/wx/examples/demo/ex_dialogs.erl
+++ b/lib/wx/examples/demo/ex_dialogs.erl
@@ -149,6 +149,10 @@ handle_info(Msg, State) ->
demo:format(State#state.config, "Got Info ~p\n", [Msg]),
{noreply, State}.
+handle_call(shutdown, _From, State=#state{parent=Panel}) ->
+ wxPanel:destroy(Panel),
+ {stop, normal, ok, State};
+
handle_call(Msg, _From, State) ->
demo:format(State#state.config, "Got Call ~p\n", [Msg]),
{reply,{error, nyi}, State}.
diff --git a/lib/wx/examples/demo/ex_frame_utils.erl b/lib/wx/examples/demo/ex_frame_utils.erl
index a90642b355..4a59bb3a68 100644
--- a/lib/wx/examples/demo/ex_frame_utils.erl
+++ b/lib/wx/examples/demo/ex_frame_utils.erl
@@ -98,6 +98,10 @@ handle_info(Msg, State) ->
demo:format(State#state.config, "Got Info ~p\n", [Msg]),
{noreply, State}.
+handle_call(shutdown, _From, State=#state{parent=Panel}) ->
+ wxPanel:destroy(Panel),
+ {stop, normal, ok, State};
+
handle_call(Msg, _From, State) ->
demo:format(State#state.config, "Got Call ~p\n", [Msg]),
{reply,{error, nyi}, State}.
diff --git a/lib/wx/examples/demo/ex_gauge.erl b/lib/wx/examples/demo/ex_gauge.erl
index ffc667ff05..c53dfb807b 100644
--- a/lib/wx/examples/demo/ex_gauge.erl
+++ b/lib/wx/examples/demo/ex_gauge.erl
@@ -114,6 +114,10 @@ handle_info(pulse, State=#state{undeterminate_gauge = Gauge=#gauge{obj = Obj}})
Timer = erlang:send_after(300, self(), pulse),
{noreply, State#state{undeterminate_gauge = Gauge#gauge{timer = Timer}}}.
+handle_call(shutdown, _From, State=#state{parent=Panel}) ->
+ wxPanel:destroy(Panel),
+ {stop, normal, ok, State};
+
handle_call(Msg, _From, State) ->
demo:format(State#state.config,"Got Call ~p\n",[Msg]),
{reply,ok, State}.
diff --git a/lib/wx/examples/demo/ex_gl.erl b/lib/wx/examples/demo/ex_gl.erl
index 72dad2cf9d..6bb2d12dff 100644
--- a/lib/wx/examples/demo/ex_gl.erl
+++ b/lib/wx/examples/demo/ex_gl.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -20,7 +20,8 @@
-behaviour(wx_object).
--export([init/1, code_change/3, handle_info/2, handle_event/2,
+-export([init/1, code_change/3, handle_info/2,
+ handle_sync_event/3, handle_event/2,
handle_call/3, handle_cast/2, terminate/2,
start/1]).
@@ -34,6 +35,7 @@
config,
gl,
canvas,
+ image,
timer,
time
}).
@@ -58,7 +60,7 @@ do_init(Config) ->
%% Setup sizer
Sizer = wxStaticBoxSizer:new(?wxHORIZONTAL, Panel, [{label, "wxGLCanvas"}]),
- Opts = [{size, {300,300}}, {style, ?wxSUNKEN_BORDER}],
+ Opts = [{style, ?wxFULL_REPAINT_ON_RESIZE}],
GLAttrib = [{attribList, [?WX_GL_RGBA,
?WX_GL_DOUBLEBUFFER,
?WX_GL_MIN_RED,8,
@@ -67,34 +69,48 @@ do_init(Config) ->
?WX_GL_DEPTH_SIZE,24,0]}],
Canvas = wxGLCanvas:new(Panel,Opts ++ GLAttrib),
wxGLCanvas:connect(Canvas, size),
+ wxGLCanvas:connect(Canvas, paint, [callback]),
- wxGLCanvas:setCurrent(Canvas),
Image = wxImage:scale(wxImage:new("image.jpg"), 128,128),
- GL = setup_gl(Canvas,Image),
- Timer = timer:send_interval(20, self(), update),
%% Add to sizers
wxSizer:add(Sizer, Canvas, [{flag, ?wxEXPAND},{proportion, 1}]),
wxWindow:setSizer(Panel,Sizer),
wxSizer:layout(Sizer),
+ Timer = timer:send_interval(20, self(), update),
{Panel, #state{parent = Panel, config = Config,
- canvas = Canvas,
- gl = GL, timer = Timer}}.
+ canvas = Canvas, image=Image,
+ timer = Timer}}.
%% Event handling
-handle_event(#wx{event = #wxSize{size = {W,H}}}, State) ->
- case W =:= 0 orelse H =:= 0 of
- true -> skip;
- _ ->
+handle_sync_event(_PaintEvent, _, #state{canvas=Canvas}) ->
+ %% Sync events are called from a temporary process,
+ %% we need to setup the gl canvas on cocoa for some reason
+ %% We do not really have to do anything, the timer event will refresh the painting
+ wxGLCanvas:setCurrent(Canvas),
+ DC= wxPaintDC:new(Canvas),
+ wxPaintDC:destroy(DC),
+ ok.
+
+handle_event(#wx{event = #wxSize{size = {W,H}}}, State = #state{gl=GL}) ->
+ if
+ GL =:= undefined ->
+ #state{canvas=Canvas, image=Image} = State,
+ wxGLCanvas:setCurrent(Canvas),
+ {noreply, State#state{gl=setup_gl(Canvas,Image)}};
+ W =:= 0, H =:= 0 -> {noreply, State};
+ true ->
gl:viewport(0,0,W,H),
gl:matrixMode(?GL_PROJECTION),
gl:loadIdentity(),
gl:ortho( -2.0, 2.0, -2.0*H/W, 2.0*H/W, -20.0, 20.0),
gl:matrixMode(?GL_MODELVIEW),
- gl:loadIdentity()
- end,
- {noreply, State}.
+ gl:loadIdentity(),
+ {noreply, State}
+ end.
+handle_info(update, State=#state{gl=undefined}) ->
+ {noreply, State};
handle_info(update, State) ->
S1 = update_rotation(State),
GL = S1#state.gl,
@@ -113,7 +129,13 @@ handle_info(stop, State) ->
timer:cancel(State#state.timer),
catch wxGLCanvas:destroy(State#state.canvas),
{stop, normal, State}.
-
+
+handle_call(shutdown, _From, State=#state{parent=Panel}) ->
+ catch wxGLCanvas:destroy(State#state.canvas),
+ timer:cancel(State#state.timer),
+ wxPanel:destroy(Panel),
+ {stop, normal, ok, State};
+
handle_call(Msg, _From, State) ->
io:format("Got Call ~p~n",[Msg]),
{reply,ok,State}.
@@ -125,11 +147,8 @@ handle_cast(Msg, State) ->
code_change(_, _, State) ->
{stop, not_yet_implemented, State}.
-terminate(_Reason, State) ->
- catch wxGLCanvas:destroy(State#state.canvas),
- timer:cancel(State#state.timer),
- timer:sleep(300).
-
+terminate(_Reason, _State) ->
+ ok.
-define(VS, {{-0.5, -0.5, -0.5}, %1
diff --git a/lib/wx/examples/demo/ex_graphicsContext.erl b/lib/wx/examples/demo/ex_graphicsContext.erl
index c356500d99..59bfe7ff64 100644
--- a/lib/wx/examples/demo/ex_graphicsContext.erl
+++ b/lib/wx/examples/demo/ex_graphicsContext.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -25,8 +25,8 @@
%% wx_object callbacks
-export([init/1, terminate/2, code_change/3,
- handle_info/2, handle_call/3,
-handle_cast/2, handle_event/2, handle_sync_event/3]).
+ handle_info/2, handle_call/3,handle_cast/2,
+ handle_event/2, handle_sync_event/3]).
-include_lib("wx/include/wx.hrl").
@@ -57,11 +57,9 @@ do_init(Config) ->
[{label, "wxGrapicsContext"}]),
Win = wxPanel:new(Panel, []),
- Pen = wxPen:new(),
- Brush = wxBrush:new(?wxBLACK),
- Font = wxFont:new(),
- wxFont:setWeight(Font, ?wxBOLD),
-
+ Pen = ?wxBLACK_PEN,
+ Brush = wxBrush:new({30, 175, 23, 127}),
+ Font = ?wxITALIC_FONT,
wxPanel:connect(Win, paint, [callback]),
%% Add to sizers
@@ -94,6 +92,10 @@ handle_info(Msg, State) ->
demo:format(State#state.config, "Got Info ~p\n", [Msg]),
{noreply, State}.
+handle_call(shutdown, _From, State=#state{parent=Panel}) ->
+ wxPanel:destroy(Panel),
+ {stop, normal, ok, State};
+
handle_call(Msg, _From, State) ->
demo:format(State#state.config, "Got Call ~p\n", [Msg]),
{reply,{error, nyi}, State}.
@@ -112,36 +114,26 @@ terminate(_Reason, _State) ->
%% Local functions
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-draw(Win, Pen0, _Brush0, Font0) ->
+draw(Win, Pen, Brush, Font) ->
try
Canvas = wxGraphicsContext:create(Win),
- Pen = wxGraphicsContext:createPen(Canvas, Pen0),
wxGraphicsContext:setPen(Canvas, Pen),
- Brush = wxGraphicsContext:createLinearGradientBrush(Canvas, 0.0,0.0, 30.0,30.0,
- {200,50,50,50},
- {200,50,50,200}),
wxGraphicsContext:setBrush(Canvas, Brush),
- Font = wxGraphicsContext:createFont(Canvas, Font0),
- wxGraphicsContext:setFont(Canvas, Font),
+ wxGraphicsContext:setFont(Canvas, Font, {0, 0, 50}),
wxGraphicsContext:drawRoundedRectangle(Canvas, 35.0,35.0, 100.0, 50.0, 10.0),
- wxGraphicsContext:drawText(Canvas, "Welcome", 60.0, 55.0),
+ wxGraphicsContext:drawText(Canvas, "This text should be antialised", 60.0, 55.0),
Path = wxGraphicsContext:createPath(Canvas),
wxGraphicsPath:addCircle(Path, 0.0, 0.0, 40.0),
wxGraphicsPath:closeSubpath(Path),
- wxGraphicsContext:translate(Canvas, 100.0, 100.0),
-
- Brush2 = wxGraphicsContext:createLinearGradientBrush(Canvas, 0.0,0.0, 30.0,30.0,
- {50,200,50,50},
- {50,50,200,50}),
- wxGraphicsContext:setBrush(Canvas, Brush2),
+ wxGraphicsContext:translate(Canvas, 100.0, 250.0),
- F = fun(_) ->
+ F = fun(N) ->
wxGraphicsContext:scale(Canvas, 1.1, 1.1),
- wxGraphicsContext:translate(Canvas, 3.0,3.0),
+ wxGraphicsContext:translate(Canvas, 15.0,-1.0*N),
wxGraphicsContext:drawPath(Canvas, Path)
end,
- wx:foreach(F, lists:seq(1,5)),
+ wx:foreach(F, lists:seq(1,10)),
ok
catch _:{not_supported, _} ->
Err = "wxGraphicsContext not available in this build of wxwidgets",
diff --git a/lib/wx/examples/demo/ex_grid.erl b/lib/wx/examples/demo/ex_grid.erl
index d1a9952ab2..e284836d5b 100644
--- a/lib/wx/examples/demo/ex_grid.erl
+++ b/lib/wx/examples/demo/ex_grid.erl
@@ -78,6 +78,10 @@ handle_event(#wx{event = #wxGrid{type = grid_cell_change,
handle_info(_Msg, State) ->
{noreply, State}.
+handle_call(shutdown, _From, State=#state{parent=Panel}) ->
+ wxPanel:destroy(Panel),
+ {stop, normal, ok, State};
+
handle_call(_Msg, _From, State) ->
{reply,{error, nyi}, State}.
diff --git a/lib/wx/examples/demo/ex_htmlWindow.erl b/lib/wx/examples/demo/ex_htmlWindow.erl
index 564c790e48..af3d4c71f5 100644
--- a/lib/wx/examples/demo/ex_htmlWindow.erl
+++ b/lib/wx/examples/demo/ex_htmlWindow.erl
@@ -77,6 +77,10 @@ handle_info(Msg, State) ->
demo:format(State#state.config, "Got Info ~p\n", [Msg]),
{noreply, State}.
+handle_call(shutdown, _From, State=#state{parent=Panel}) ->
+ wxPanel:destroy(Panel),
+ {stop, normal, ok, State};
+
handle_call(Msg, _From, State) ->
demo:format(State#state.config, "Got Call ~p\n", [Msg]),
{reply,{error, nyi}, State}.
diff --git a/lib/wx/examples/demo/ex_listCtrl.erl b/lib/wx/examples/demo/ex_listCtrl.erl
index 13096dfa52..2c62ac9d5f 100644
--- a/lib/wx/examples/demo/ex_listCtrl.erl
+++ b/lib/wx/examples/demo/ex_listCtrl.erl
@@ -58,8 +58,8 @@ do_init(Config) ->
IL = wxImageList:new(16,16),
wxImageList:add(IL, wxArtProvider:getBitmap("wxART_COPY", [{size, {16,16}}])),
wxImageList:add(IL, wxArtProvider:getBitmap("wxART_MISSING_IMAGE", [{size, {16,16}}])),
- wxImageList:add(IL, wxArtProvider:getBitmap("wxART_TICK_MARK", [{size, {16,16}}])),
- wxImageList:add(IL, wxArtProvider:getBitmap("wxART_CROSS_MARK", [{size, {16,16}}])),
+ wxImageList:add(IL, wxArtProvider:getBitmap("wxART_QUESTION", [{size, {16,16}}])),
+ wxImageList:add(IL, wxArtProvider:getBitmap("wxART_WARNING", [{size, {16,16}}])),
wxListCtrl:assignImageList(ListCtrl2, IL, ?wxIMAGE_LIST_SMALL),
Fun =
fun(Item) ->
@@ -143,6 +143,10 @@ handle_info(Msg, State) ->
demo:format(State#state.config, "Got Info ~p\n",[Msg]),
{noreply,State}.
+handle_call(shutdown, _From, State=#state{parent=Panel}) ->
+ wxPanel:destroy(Panel),
+ {stop, normal, ok, State};
+
handle_call(Msg, _From, State) ->
demo:format(State#state.config,"Got Call ~p\n",[Msg]),
{reply,ok,State}.
diff --git a/lib/wx/examples/demo/ex_notebook.erl b/lib/wx/examples/demo/ex_notebook.erl
index fc38fdae08..5edcc65082 100644
--- a/lib/wx/examples/demo/ex_notebook.erl
+++ b/lib/wx/examples/demo/ex_notebook.erl
@@ -129,6 +129,10 @@ handle_info(Msg, State) ->
demo:format(State#state.config, "Got Info ~p\n",[Msg]),
{noreply,State}.
+handle_call(shutdown, _From, State=#state{parent=Panel}) ->
+ wxPanel:destroy(Panel),
+ {stop, normal, ok, State};
+
handle_call(Msg, _From, State) ->
demo:format(State#state.config,"Got Call ~p\n",[Msg]),
{reply,ok,State}.
diff --git a/lib/wx/examples/demo/ex_pickers.erl b/lib/wx/examples/demo/ex_pickers.erl
index 8013a5ba32..54f8c7a8e5 100644
--- a/lib/wx/examples/demo/ex_pickers.erl
+++ b/lib/wx/examples/demo/ex_pickers.erl
@@ -120,6 +120,10 @@ handle_info(Msg, State) ->
demo:format(State#state.config, "Got Info ~p\n", [Msg]),
{noreply, State}.
+handle_call(shutdown, _From, State=#state{parent=Panel}) ->
+ wxPanel:destroy(Panel),
+ {stop, normal, ok, State};
+
handle_call(Msg, _From, State) ->
demo:format(State#state.config, "Got Call ~p\n", [Msg]),
{reply,{error, nyi}, State}.
diff --git a/lib/wx/examples/demo/ex_popupMenu.erl b/lib/wx/examples/demo/ex_popupMenu.erl
index d6778c5dc5..f48b00963d 100644
--- a/lib/wx/examples/demo/ex_popupMenu.erl
+++ b/lib/wx/examples/demo/ex_popupMenu.erl
@@ -86,6 +86,10 @@ handle_info(Msg, State) ->
demo:format(State#state.config, "Got Info ~p\n", [Msg]),
{noreply, State}.
+handle_call(shutdown, _From, State=#state{parent=Panel}) ->
+ wxPanel:destroy(Panel),
+ {stop, normal, ok, State};
+
handle_call(Msg, _From, State) ->
demo:format(State#state.config, "Got Call ~p\n", [Msg]),
{reply,{error, nyi}, State}.
diff --git a/lib/wx/examples/demo/ex_radioBox.erl b/lib/wx/examples/demo/ex_radioBox.erl
index ab7685f41f..17a11d1054 100644
--- a/lib/wx/examples/demo/ex_radioBox.erl
+++ b/lib/wx/examples/demo/ex_radioBox.erl
@@ -103,6 +103,10 @@ handle_info(Msg, State) ->
demo:format(State#state.config, "Got Info ~p\n",[Msg]),
{noreply, State}.
+handle_call(shutdown, _From, State=#state{parent=Panel}) ->
+ wxPanel:destroy(Panel),
+ {stop, normal, ok, State};
+
handle_call(Msg, _From, State) ->
demo:format(State#state.config,"Got Call ~p\n",[Msg]),
{reply, {error, nyi}, State}.
diff --git a/lib/wx/examples/demo/ex_sashWindow.erl b/lib/wx/examples/demo/ex_sashWindow.erl
index d8a8958f28..9eb3b9b27e 100644
--- a/lib/wx/examples/demo/ex_sashWindow.erl
+++ b/lib/wx/examples/demo/ex_sashWindow.erl
@@ -112,6 +112,10 @@ handle_info(Msg, State) ->
demo:format(State#state.config, "Got Info ~p\n", [Msg]),
{noreply, State}.
+handle_call(shutdown, _From, State=#state{parent=Panel}) ->
+ wxPanel:destroy(Panel),
+ {stop, normal, ok, State};
+
handle_call(Msg, _From, State) ->
demo:format(State#state.config, "Got Call ~p\n", [Msg]),
{reply,{error, nyi}, State}.
diff --git a/lib/wx/examples/demo/ex_sizers.erl b/lib/wx/examples/demo/ex_sizers.erl
index 7b9e8eb37f..ecd539cd62 100644
--- a/lib/wx/examples/demo/ex_sizers.erl
+++ b/lib/wx/examples/demo/ex_sizers.erl
@@ -97,6 +97,10 @@ handle_info(Msg, State) ->
demo:format(State#state.config, "Got Info ~p\n", [Msg]),
{noreply, State}.
+handle_call(shutdown, _From, State=#state{parent=Panel}) ->
+ wxPanel:destroy(Panel),
+ {stop, normal, ok, State};
+
handle_call(Msg, _From, State) ->
demo:format(State#state.config, "Got Call ~p\n", [Msg]),
{reply,{error, nyi}, State}.
diff --git a/lib/wx/examples/demo/ex_slider.erl b/lib/wx/examples/demo/ex_slider.erl
index 612543ff26..4979e8b4f4 100644
--- a/lib/wx/examples/demo/ex_slider.erl
+++ b/lib/wx/examples/demo/ex_slider.erl
@@ -97,6 +97,10 @@ handle_info(Msg, State) ->
demo:format(State#state.config, "Got Info ~p\n",[Msg]),
{noreply, State}.
+handle_call(shutdown, _From, State=#state{parent=Panel}) ->
+ wxPanel:destroy(Panel),
+ {stop, normal, ok, State};
+
handle_call(Msg, _From, State) ->
demo:format(State#state.config,"Got Call ~p\n",[Msg]),
{reply, {error, nyi},State}.
diff --git a/lib/wx/examples/demo/ex_splitterWindow.erl b/lib/wx/examples/demo/ex_splitterWindow.erl
index 4f25b73293..ac2fbe0113 100644
--- a/lib/wx/examples/demo/ex_splitterWindow.erl
+++ b/lib/wx/examples/demo/ex_splitterWindow.erl
@@ -86,6 +86,10 @@ handle_info(Msg, State) ->
demo:format(State#state.config, "Got Info ~p\n", [Msg]),
{noreply, State}.
+handle_call(shutdown, _From, State=#state{parent=Panel}) ->
+ wxPanel:destroy(Panel),
+ {stop, normal, ok, State};
+
handle_call(Msg, _From, State) ->
demo:format(State#state.config, "Got Call ~p\n", [Msg]),
{reply,{error, nyi}, State}.
diff --git a/lib/wx/examples/demo/ex_static.erl b/lib/wx/examples/demo/ex_static.erl
index 013bd5ac35..8cf477b55a 100644
--- a/lib/wx/examples/demo/ex_static.erl
+++ b/lib/wx/examples/demo/ex_static.erl
@@ -101,6 +101,10 @@ handle_info(Msg, State) ->
demo:format(State#state.config, "Got Info ~p\n", [Msg]),
{noreply, State}.
+handle_call(shutdown, _From, State=#state{parent=Panel}) ->
+ wxPanel:destroy(Panel),
+ {stop, normal, ok, State};
+
handle_call(Msg, _From, State) ->
demo:format(State#state.config, "Got Call ~p\n", [Msg]),
{reply,{error, nyi}, State}.
diff --git a/lib/wx/examples/demo/ex_textCtrl.erl b/lib/wx/examples/demo/ex_textCtrl.erl
index d82884f30b..57088ad878 100644
--- a/lib/wx/examples/demo/ex_textCtrl.erl
+++ b/lib/wx/examples/demo/ex_textCtrl.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -51,27 +51,23 @@ do_init(Config) ->
Sizer3 = wxStaticBoxSizer:new(?wxVERTICAL, Panel,
[{label, "wxTextCtrl multiline"}]),
- TextCtrl = wxTextCtrl:new(Panel, 1, [{value, "This is a single line wxTextCtrl"},
+ TextCtrl = wxTextCtrl:new(Panel, 1, [{value, "This is a single line wxTextCtrl"},
{style, ?wxDEFAULT}]),
TextCtrl2 = wxTextCtrl:new(Panel, 2, [{value, "password"},
- {style, ?wxDEFAULT bor
- ?wxTE_PASSWORD}]),
- TextCtrl3 = wxTextCtrl:new(Panel, 3, [{value, "This is a\n"
- "multiline\n"
- "wxTextCtrl"},
- {style, ?wxDEFAULT bor
- ?wxTE_MULTILINE}]),
+ {style, ?wxDEFAULT bor ?wxTE_PASSWORD}]),
+ TextCtrl3 = wxTextCtrl:new(Panel, 3, [{value, "This is a\nmultiline\nwxTextCtrl"},
+ {style, ?wxDEFAULT bor ?wxTE_MULTILINE}]),
%% Add to sizers
- wxSizer:add(Sizer, TextCtrl, [{flag, ?wxEXPAND}]),
+ wxSizer:add(Sizer, TextCtrl, [{flag, ?wxEXPAND}]),
wxSizer:add(Sizer2, TextCtrl2, []),
- wxSizer:add(Sizer3, TextCtrl3, [{flag, ?wxEXPAND}]),
+ wxSizer:add(Sizer3, TextCtrl3, [{flag, ?wxEXPAND}, {proportion, 1}]),
wxSizer:add(MainSizer, Sizer, [{flag, ?wxEXPAND}]),
wxSizer:addSpacer(MainSizer, 10),
wxSizer:add(MainSizer, Sizer2, [{flag, ?wxEXPAND}]),
wxSizer:addSpacer(MainSizer, 10),
- wxSizer:add(MainSizer, Sizer3, [{flag, ?wxEXPAND}]),
+ wxSizer:add(MainSizer, Sizer3, [{flag, ?wxEXPAND}, {proportion, 1}]),
wxPanel:setSizer(Panel, MainSizer),
{Panel, #state{parent=Panel, config=Config}}.
@@ -88,6 +84,10 @@ handle_info(Msg, State) ->
demo:format(State#state.config, "Got Info ~p\n",[Msg]),
{noreply, State}.
+handle_call(shutdown, _From, State=#state{parent=Panel}) ->
+ wxPanel:destroy(Panel),
+ {stop, normal, ok, State};
+
handle_call(Msg, _From, State) ->
demo:format(State#state.config,"Got Call ~p\n",[Msg]),
{reply, {error,nyi}, State}.
diff --git a/lib/wx/examples/demo/ex_treeCtrl.erl b/lib/wx/examples/demo/ex_treeCtrl.erl
index 611904500a..7698ab1f00 100644
--- a/lib/wx/examples/demo/ex_treeCtrl.erl
+++ b/lib/wx/examples/demo/ex_treeCtrl.erl
@@ -105,6 +105,10 @@ handle_info(Msg, State) ->
demo:format(State#state.config, "Got Info ~p\n", [Msg]),
{noreply, State}.
+handle_call(shutdown, _From, State=#state{parent=Panel}) ->
+ wxPanel:destroy(Panel),
+ {stop, normal, ok, State};
+
handle_call(Msg, _From, State) ->
demo:format(State#state.config, "Got Call ~p\n", [Msg]),
{reply,{error, nyi}, State}.
diff --git a/lib/wx/examples/simple/menu.erl b/lib/wx/examples/simple/menu.erl
index 0025a0b027..9e6b28b46b 100644
--- a/lib/wx/examples/simple/menu.erl
+++ b/lib/wx/examples/simple/menu.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -105,7 +105,7 @@ create_frame(Wx) ->
Frame = wxFrame:new(Wx, -1, "wxErlang menu sample", [{size, {600,400}}]),
Path = filename:dirname(code:which(?MODULE)),
- wxFrame:setIcon(Frame, wxIcon:new(filename:join(Path,"sample.xpm"))),
+ wxFrame:setIcon(Frame, wxIcon:new(filename:join(Path,"sample.xpm"), [{type, ?wxBITMAP_TYPE_XPM}])),
wxFrame:createStatusBar(Frame,[]),
wxFrame:connect(Frame, close_window),
diff --git a/lib/wx/examples/simple/minimal.erl b/lib/wx/examples/simple/minimal.erl
index bdff66e217..4782745dfc 100644
--- a/lib/wx/examples/simple/minimal.erl
+++ b/lib/wx/examples/simple/minimal.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -41,8 +41,8 @@ start() ->
create_window(Wx) ->
Frame = wxFrame:new(Wx, -1, "Minimal wxErlang App", [{size, {600,400}}]),
- Path = filename:dirname(code:which(?MODULE)),
- wxFrame:setIcon(Frame, wxIcon:new(filename:join(Path,"sample.xpm"))),
+ Path = filename:dirname(code:which(?MODULE)),
+ wxFrame:setIcon(Frame, wxIcon:new(filename:join(Path,"sample.xpm"), [{type, ?wxBITMAP_TYPE_XPM}])),
wxFrame:createStatusBar(Frame,[]),
wxFrame:connect(Frame, close_window),
diff --git a/lib/wx/examples/sudoku/sudoku_board.erl b/lib/wx/examples/sudoku/sudoku_board.erl
index 4b26ff97da..ed9c62b7c8 100644
--- a/lib/wx/examples/sudoku/sudoku_board.erl
+++ b/lib/wx/examples/sudoku/sudoku_board.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -27,7 +27,7 @@
-export([new/1, setup_board/2, clear_board/1, left/1,
get_board_data/1,set_board_data/2,
set_butt/3, butt_correct/3,
- draw/3,
+ get_state/1, redraw/3,
%% Callbacks
init/1, handle_sync_event/3,
handle_event/2, handle_info/2, handle_call/3, handle_cast/2,
@@ -69,9 +69,8 @@ get_board_data(Board) ->
set_board_data(Board, List) ->
wx_object:call(Board, {set_board_data, List}).
-
-draw(Board, DC, Size) ->
- wx_object:call(Board, {draw, DC, Size}).
+get_state(Board) ->
+ wx_object:call(Board, get_state).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -85,26 +84,29 @@ init([ParentObj, ParentPid]) ->
wxWindow:connect(Win, erase_background, []),
wxWindow:connect(Win, key_up, [{skip, true}]),
wxWindow:connect(Win, left_down, [{skip, true}]),
- wxWindow:connect(Win, enter_window, [{skip, true}]),
+ wxWindow:connect(Win, enter_window, [{skip, true}]),
%% Init pens and fonts
Pen = wxPen:new({0,0,0}, [{width, 3}]),
Fs0 = [{Sz,wxFont:new(Sz, ?wxSWISS, ?wxNORMAL, ?wxNORMAL,[])} ||
Sz <- [8,9,10,11,12,13,14,16,18,20,22,24,26,28,30,34,38,42,44,46]],
- TestDC = wxClientDC:new(Win),
+ TestDC = wxMemoryDC:new(),
+ Bitmap = wxBitmap:new(256,256),
+ wxMemoryDC:selectObject(TestDC, Bitmap),
+ true = wxDC:isOk(TestDC),
CW = fun({Sz,Font},Acc) ->
case wxFont:ok(Font) of
- true ->
+ true ->
wxDC:setFont(TestDC, Font),
- CH = wxDC:getCharHeight(TestDC),
+ CH = wxDC:getCharHeight(TestDC),
[{CH,Sz,Font} | Acc];
false ->
Acc
end
end,
Fs = lists:foldl(CW, [], Fs0),
- wxClientDC:destroy(TestDC),
- {Win, #state{win=Win, board=[], pen=Pen, fonts=Fs, parent=ParentPid}}.
+ wxMemoryDC:destroy(TestDC),
+ {Win, #state{win=Win, board=[], pen=Pen, fonts=Fs,parent=ParentPid}}.
handle_sync_event(#wx{event=#wxPaint{}}, _Obj, State = #state{win=Win}) ->
%% io:format("EPaint~n",[]),
@@ -119,22 +121,17 @@ handle_sync_event(#wx{event=#wxPaint{}}, _Obj, State = #state{win=Win}) ->
handle_event(#wx{event=#wxMouse{type=enter_window}}, State = #state{win=Win}) ->
wxWindow:setFocus(Win), %% Get keyboard focus
{noreply,State};
-handle_event(#wx{event=#wxKey{keyCode=KeyC, x=X,y=Y}},
+handle_event(#wx{event=#wxKey{keyCode=KeyC}},
S = #state{parent=Pid, win=Win}) ->
Val = if KeyC > 47, KeyC < 58 -> KeyC - $0;
KeyC > 325, KeyC < 336 -> KeyC - 326; %% NUM LOCK
true -> 0
end,
- case get_butt(X,Y,S) of
- error -> %% Mac don't get correct coordinates.
- Global = wx_misc:getMousePosition(),
- {CX,CY} = wxWindow:screenToClient(Win, Global),
- case get_butt(CX,CY,S) of
- error -> ignore;
- Id -> Pid ! {set_val,Id,Val}
- end;
- Id ->
- Pid ! {set_val,Id,Val}
+ Global = wx_misc:getMousePosition(),
+ {CX,CY} = wxWindow:screenToClient(Win, Global),
+ case get_butt(CX,CY,S) of
+ error -> ignore;
+ Id -> Pid ! {set_val,Id,Val}
end,
{noreply, S};
handle_event(#wx{event=#wxMouse{type=left_down,x=X,y=Y}},
@@ -205,9 +202,8 @@ handle_call({set_board_data, B},_From, S0) ->
handle_call(left,_From, S = #state{board=B}) ->
Res = 81 - length([ok || #sq{correct=C} <- B, C /= false]),
{reply, Res, S};
-handle_call({draw, DC, Size},_From, S) ->
- redraw(DC,Size,S),
- {reply, ok, S}.
+handle_call(get_state, _From, S) ->
+ {reply, {ok,S}, S}.
handle_cast(Msg, State) ->
io:format("Got cast ~p~n",[Msg]),
diff --git a/lib/wx/examples/sudoku/sudoku_gui.erl b/lib/wx/examples/sudoku/sudoku_gui.erl
index 3d0c95ffa7..5f3f1a2621 100644
--- a/lib/wx/examples/sudoku/sudoku_gui.erl
+++ b/lib/wx/examples/sudoku/sudoku_gui.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -307,19 +307,19 @@ dialog(?PRINT_PAGE_SETUP, S = #gs{frame=Frame, print_psdd=PsDD0, print_d=PD0}) -
wxPageSetupDialogData:destroy(PsDD0),
wxPrintData:destroy(PD0),
S#gs{print_psdd=PsDD, print_d=PD};
-dialog(?PRINT_PRE, S = #gs{frame=Frame, print_d=PD}) ->
+dialog(?PRINT_PRE, S = #gs{frame=Frame, print_d=PD, board=Board}) ->
+ {ok, BoardS} = sudoku_board:get_state(Board),
PDD = wxPrintDialogData:new(PD),
- Printout1 = wxPrintout:new("Print", fun(This,Page) -> printout(This,Page,S) end,
+ Printout1 = wxPrintout:new("Print 1", fun(This,Page) -> printout(This,Page,BoardS, S) end,
[{getPageInfo, fun getPageInfo/1}]),
- Printout2 = wxPrintout:new("Print", fun(This,Page) -> printout(This,Page,S) end,
+ Printout2 = wxPrintout:new("Print 2", fun(This,Page) -> printout(This,Page,BoardS, S) end,
[{getPageInfo, fun getPageInfo/1}]),
- Preview = wxPrintPreview:new(Printout1, [{printoutForPrinting,Printout2},{data,PDD}]),
+ Preview = wxPrintPreview:new(Printout1, [{printoutForPrinting,Printout2},{data,PDD}]),
case wxPrintPreview:isOk(Preview) of
true ->
PF = wxPreviewFrame:new(Preview, Frame, [{title, "Print Preview"}]),
wxPreviewFrame:centre(PF, [{dir, ?wxBOTH}]),
wxPreviewFrame:initialize(PF),
- wxPreviewFrame:centre(PF),
wxPreviewFrame:show(PF);
false ->
io:format("Could not create preview window.\n"
@@ -327,10 +327,11 @@ dialog(?PRINT_PRE, S = #gs{frame=Frame, print_d=PD}) ->
wxPrintPreview:destroy(Preview)
end,
S;
-dialog(?PRINT, S = #gs{frame=Frame, print_d=PD}) ->
+dialog(?PRINT, S = #gs{frame=Frame, print_d=PD, board=Board}) ->
+ {ok, BoardS} = sudoku_board:get_state(Board),
PDD = wxPrintDialogData:new(PD),
Printer = wxPrinter:new([{data,PDD}]),
- Printout = wxPrintout:new("Print", fun(This,Page) -> printout(This,Page,S) end,
+ Printout = wxPrintout:new("Print", fun(This,Page) -> printout(This,Page,BoardS,S) end,
[{getPageInfo, fun getPageInfo/1}]),
case wxPrinter:print(Printer, Frame, Printout, [{prompt,true}]) of
@@ -374,16 +375,14 @@ init_printer(S) ->
getPageInfo(_This) ->
{1,1,1,1}.
-printout(This, _Page, #gs{board=Board, print_psdd=PsDD}) ->
+printout(This, _Page, Board, #gs{print_psdd=PsDD}) ->
MX = MY = 500,
wxPrintout:fitThisSizeToPageMargins(This, {MX,MY}, PsDD),
-
+
_DBG = {_X,_Y,W,H} = wxPrintout:getLogicalPageMarginsRect(This, PsDD),
wxPrintout:offsetLogicalOrigin(This,(W-MX) div 2, (H-MY) div 2),
-%% io:format("~p ->{~p,~p} ~n", [_DBG, (W-MX) div 2, (H-MY) div 2]),
-
DC = wxPrintout:getDC(This),
- sudoku_board:draw(Board, DC, {500,500}),
+ sudoku_board:redraw(DC, {500,500}, Board),
true.
set_val(Id, Val, Board, G) ->
diff --git a/lib/wx/include/wx.hrl b/lib/wx/include/wx.hrl
index 62979908a6..a48c756dea 100644
--- a/lib/wx/include/wx.hrl
+++ b/lib/wx/include/wx.hrl
@@ -131,7 +131,7 @@
-type wxScrollWin() :: #wxScrollWin{}. %% Callback event: {@link wxScrollWinEvent}
-record(wxPaint, {type :: wxPaintEventType()}). %% Callback event: {@link wxPaintEvent}
--type wxPaintEventType() :: paint | paint_icon.
+-type wxPaintEventType() :: paint.
-type wxPaint() :: #wxPaint{}. %% Callback event: {@link wxPaintEvent}
-record(wxChildFocus, {type :: wxChildFocusEventType()}). %% Callback event: {@link wxChildFocusEvent}
@@ -252,7 +252,7 @@
wheelRotation :: integer(),
wheelDelta :: integer(),
linesPerAction :: integer()}).
--type wxMouseEventType() :: left_down | left_up | middle_down | middle_up | right_down | right_up | motion | enter_window | leave_window | left_dclick | middle_dclick | right_dclick | mousewheel | nc_left_down | nc_left_up | nc_middle_down | nc_middle_up | nc_right_down | nc_right_up | nc_motion | nc_enter_window | nc_leave_window | nc_left_dclick | nc_middle_dclick | nc_right_dclick.
+-type wxMouseEventType() :: left_down | left_up | middle_down | middle_up | right_down | right_up | motion | enter_window | leave_window | left_dclick | middle_dclick | right_dclick | mousewheel.
-type wxMouse() :: #wxMouse{}. %% Callback event: {@link wxMouseEvent}
-record(wxWindowCreate, {type :: wxWindowCreateEventType()}). %% Callback event: {@link wxWindowCreateEvent}
@@ -292,10 +292,6 @@
-type wxIdleEventType() :: idle.
-type wxIdle() :: #wxIdle{}. %% Callback event: {@link wxIdleEvent}
--record(wxNcPaint, {type :: wxNcPaintEventType()}). %% Callback event: {@link wxNcPaintEvent}
--type wxNcPaintEventType() :: nc_paint.
--type wxNcPaint() :: #wxNcPaint{}. %% Callback event: {@link wxNcPaintEvent}
-
-record(wxColourPicker,{type :: wxColourPickerEventType(), %% Callback event: {@link wxColourPickerEvent}
colour :: wx:wx_colour()}).
-type wxColourPickerEventType() :: command_colourpicker_changed.
@@ -312,8 +308,8 @@
-type wxTreeEventType() :: command_tree_begin_drag | command_tree_begin_rdrag | command_tree_begin_label_edit | command_tree_end_label_edit | command_tree_delete_item | command_tree_get_info | command_tree_set_info | command_tree_item_expanded | command_tree_item_expanding | command_tree_item_collapsed | command_tree_item_collapsing | command_tree_sel_changed | command_tree_sel_changing | command_tree_key_down | command_tree_item_activated | command_tree_item_right_click | command_tree_item_middle_click | command_tree_end_drag | command_tree_state_image_click | command_tree_item_gettooltip | command_tree_item_menu.
-type wxTree() :: #wxTree{}. %% Callback event: {@link wxTreeEvent}
--type event() :: wxAuiManager() | wxAuiNotebook() | wxCalendar() | wxChildFocus() | wxClose() | wxColourPicker() | wxCommand() | wxContextMenu() | wxDate() | wxDisplayChanged() | wxErase() | wxFileDirPicker() | wxFocus() | wxFontPicker() | wxGrid() | wxHelp() | wxHtmlLink() | wxIconize() | wxIdle() | wxJoystick() | wxKey() | wxList() | wxMaximize() | wxMenu() | wxMouse() | wxMouseCaptureChanged() | wxMove() | wxNavigationKey() | wxNcPaint() | wxNotebook() | wxPaint() | wxPaletteChanged() | wxQueryNewPalette() | wxSash() | wxScroll() | wxScrollWin() | wxSetCursor() | wxShow() | wxSize() | wxSpin() | wxSplitter() | wxStyledText() | wxSysColourChanged() | wxTaskBarIcon() | wxTree() | wxUpdateUI() | wxWindowCreate() | wxWindowDestroy().
--type wxEventType() :: wxAuiManagerEventType() | wxAuiNotebookEventType() | wxCalendarEventType() | wxChildFocusEventType() | wxCloseEventType() | wxColourPickerEventType() | wxCommandEventType() | wxContextMenuEventType() | wxDateEventType() | wxDisplayChangedEventType() | wxEraseEventType() | wxFileDirPickerEventType() | wxFocusEventType() | wxFontPickerEventType() | wxGridEventType() | wxHelpEventType() | wxHtmlLinkEventType() | wxIconizeEventType() | wxIdleEventType() | wxJoystickEventType() | wxKeyEventType() | wxListEventType() | wxMaximizeEventType() | wxMenuEventType() | wxMouseCaptureChangedEventType() | wxMouseEventType() | wxMoveEventType() | wxNavigationKeyEventType() | wxNcPaintEventType() | wxNotebookEventType() | wxPaintEventType() | wxPaletteChangedEventType() | wxQueryNewPaletteEventType() | wxSashEventType() | wxScrollEventType() | wxScrollWinEventType() | wxSetCursorEventType() | wxShowEventType() | wxSizeEventType() | wxSpinEventType() | wxSplitterEventType() | wxStyledTextEventType() | wxSysColourChangedEventType() | wxTaskBarIconEventType() | wxTreeEventType() | wxUpdateUIEventType() | wxWindowCreateEventType() | wxWindowDestroyEventType().
+-type event() :: wxAuiManager() | wxAuiNotebook() | wxCalendar() | wxChildFocus() | wxClose() | wxColourPicker() | wxCommand() | wxContextMenu() | wxDate() | wxDisplayChanged() | wxErase() | wxFileDirPicker() | wxFocus() | wxFontPicker() | wxGrid() | wxHelp() | wxHtmlLink() | wxIconize() | wxIdle() | wxJoystick() | wxKey() | wxList() | wxMaximize() | wxMenu() | wxMouse() | wxMouseCaptureChanged() | wxMove() | wxNavigationKey() | wxNotebook() | wxPaint() | wxPaletteChanged() | wxQueryNewPalette() | wxSash() | wxScroll() | wxScrollWin() | wxSetCursor() | wxShow() | wxSize() | wxSpin() | wxSplitter() | wxStyledText() | wxSysColourChanged() | wxTaskBarIcon() | wxTree() | wxUpdateUI() | wxWindowCreate() | wxWindowDestroy().
+-type wxEventType() :: wxAuiManagerEventType() | wxAuiNotebookEventType() | wxCalendarEventType() | wxChildFocusEventType() | wxCloseEventType() | wxColourPickerEventType() | wxCommandEventType() | wxContextMenuEventType() | wxDateEventType() | wxDisplayChangedEventType() | wxEraseEventType() | wxFileDirPickerEventType() | wxFocusEventType() | wxFontPickerEventType() | wxGridEventType() | wxHelpEventType() | wxHtmlLinkEventType() | wxIconizeEventType() | wxIdleEventType() | wxJoystickEventType() | wxKeyEventType() | wxListEventType() | wxMaximizeEventType() | wxMenuEventType() | wxMouseCaptureChangedEventType() | wxMouseEventType() | wxMoveEventType() | wxNavigationKeyEventType() | wxNotebookEventType() | wxPaintEventType() | wxPaletteChangedEventType() | wxQueryNewPaletteEventType() | wxSashEventType() | wxScrollEventType() | wxScrollWinEventType() | wxSetCursorEventType() | wxShowEventType() | wxSizeEventType() | wxSpinEventType() | wxSplitterEventType() | wxStyledTextEventType() | wxSysColourChangedEventType() | wxTaskBarIconEventType() | wxTreeEventType() | wxUpdateUIEventType() | wxWindowCreateEventType() | wxWindowDestroyEventType().
%% Hardcoded Records
-record(wxMouseState, {x :: integer(), y :: integer(),
@@ -420,78 +416,78 @@
-define(wxDateTime_Country_Unknown, 0).
-define(wxDateTime_Country_Default, 1).
-define(wxDateTime_Country_WesternEurope_Start, 2).
--define(wxDateTime_Country_EEC, ?Country_WesternEurope_Start).
--define(wxDateTime_France, (?Country_WesternEurope_Start+1)).
--define(wxDateTime_Germany, (?Country_WesternEurope_Start+2)).
--define(wxDateTime_UK, (?Country_WesternEurope_Start+3)).
--define(wxDateTime_Country_WesternEurope_End, ?UK).
--define(wxDateTime_Russia, (?UK+1)).
--define(wxDateTime_USA, (?UK+2)).
+-define(wxDateTime_Country_EEC, ?wxDateTime_Country_WesternEurope_Start).
+-define(wxDateTime_France, (?wxDateTime_Country_WesternEurope_Start+1)).
+-define(wxDateTime_Germany, (?wxDateTime_Country_WesternEurope_Start+2)).
+-define(wxDateTime_UK, (?wxDateTime_Country_WesternEurope_Start+3)).
+-define(wxDateTime_Country_WesternEurope_End, ?wxDateTime_UK).
+-define(wxDateTime_Russia, (?wxDateTime_UK+1)).
+-define(wxDateTime_USA, (?wxDateTime_UK+2)).
% From class wxDateTime::GregorianAdoption
-define(wxDateTime_Gr_Unknown, 0).
-define(wxDateTime_Gr_Standard, 1).
-define(wxDateTime_Gr_Alaska, 2).
-define(wxDateTime_Gr_Albania, 3).
--define(wxDateTime_Gr_Austria, ?Gr_Unknown).
--define(wxDateTime_Gr_Austria_Brixen, (?Gr_Unknown+1)).
--define(wxDateTime_Gr_Austria_Salzburg, ?Gr_Austria_Brixen).
--define(wxDateTime_Gr_Austria_Tyrol, ?Gr_Austria_Brixen).
--define(wxDateTime_Gr_Austria_Carinthia, (?Gr_Austria_Brixen+1)).
--define(wxDateTime_Gr_Austria_Styria, ?Gr_Austria_Carinthia).
--define(wxDateTime_Gr_Belgium, (?Gr_Austria_Carinthia+1)).
--define(wxDateTime_Gr_Bulgaria, ?Gr_Unknown).
--define(wxDateTime_Gr_Bulgaria_1, (?Gr_Unknown+1)).
--define(wxDateTime_Gr_Bulgaria_2, (?Gr_Unknown+2)).
--define(wxDateTime_Gr_Bulgaria_3, (?Gr_Unknown+3)).
--define(wxDateTime_Gr_Canada, ?Gr_Unknown).
--define(wxDateTime_Gr_China, ?Gr_Unknown).
--define(wxDateTime_Gr_China_1, (?Gr_Unknown+1)).
--define(wxDateTime_Gr_China_2, (?Gr_Unknown+2)).
--define(wxDateTime_Gr_Czechoslovakia, (?Gr_Unknown+3)).
--define(wxDateTime_Gr_Denmark, (?Gr_Unknown+4)).
--define(wxDateTime_Gr_Egypt, (?Gr_Unknown+5)).
--define(wxDateTime_Gr_Estonia, (?Gr_Unknown+6)).
--define(wxDateTime_Gr_Finland, (?Gr_Unknown+7)).
--define(wxDateTime_Gr_France, (?Gr_Unknown+8)).
--define(wxDateTime_Gr_France_Alsace, (?Gr_Unknown+9)).
--define(wxDateTime_Gr_France_Lorraine, (?Gr_Unknown+10)).
--define(wxDateTime_Gr_France_Strasbourg, (?Gr_Unknown+11)).
--define(wxDateTime_Gr_Germany, ?Gr_Unknown).
--define(wxDateTime_Gr_Germany_Catholic, (?Gr_Unknown+1)).
--define(wxDateTime_Gr_Germany_Prussia, (?Gr_Unknown+2)).
--define(wxDateTime_Gr_Germany_Protestant, (?Gr_Unknown+3)).
--define(wxDateTime_Gr_GreatBritain, (?Gr_Unknown+4)).
--define(wxDateTime_Gr_Greece, (?Gr_Unknown+5)).
--define(wxDateTime_Gr_Hungary, (?Gr_Unknown+6)).
--define(wxDateTime_Gr_Ireland, ?Gr_GreatBritain).
--define(wxDateTime_Gr_Italy, ?Gr_Standard).
--define(wxDateTime_Gr_Japan, ?Gr_Unknown).
--define(wxDateTime_Gr_Japan_1, (?Gr_Unknown+1)).
--define(wxDateTime_Gr_Japan_2, (?Gr_Unknown+2)).
--define(wxDateTime_Gr_Japan_3, (?Gr_Unknown+3)).
--define(wxDateTime_Gr_Latvia, (?Gr_Unknown+4)).
--define(wxDateTime_Gr_Lithuania, (?Gr_Unknown+5)).
--define(wxDateTime_Gr_Luxemburg, (?Gr_Unknown+6)).
--define(wxDateTime_Gr_Netherlands, ?Gr_Belgium).
--define(wxDateTime_Gr_Netherlands_Groningen, (?Gr_Belgium+1)).
--define(wxDateTime_Gr_Netherlands_Gelderland, (?Gr_Belgium+2)).
--define(wxDateTime_Gr_Netherlands_Utrecht, (?Gr_Belgium+3)).
--define(wxDateTime_Gr_Netherlands_Friesland, (?Gr_Belgium+4)).
--define(wxDateTime_Gr_Norway, ?Gr_Denmark).
--define(wxDateTime_Gr_Poland, ?Gr_Standard).
--define(wxDateTime_Gr_Portugal, ?Gr_Standard).
--define(wxDateTime_Gr_Romania, (?Gr_Standard+1)).
--define(wxDateTime_Gr_Russia, (?Gr_Standard+2)).
--define(wxDateTime_Gr_Scotland, ?Gr_GreatBritain).
--define(wxDateTime_Gr_Spain, ?Gr_Standard).
--define(wxDateTime_Gr_Sweden, ?Gr_Finland).
--define(wxDateTime_Gr_Switzerland, ?Gr_Unknown).
--define(wxDateTime_Gr_Switzerland_Catholic, (?Gr_Unknown+1)).
--define(wxDateTime_Gr_Switzerland_Protestant, (?Gr_Unknown+2)).
--define(wxDateTime_Gr_Turkey, (?Gr_Unknown+3)).
--define(wxDateTime_Gr_USA, ?Gr_GreatBritain).
--define(wxDateTime_Gr_Wales, ?Gr_GreatBritain).
--define(wxDateTime_Gr_Yugoslavia, (?Gr_GreatBritain+1)).
+-define(wxDateTime_Gr_Austria, ?wxDateTime_Gr_Unknown).
+-define(wxDateTime_Gr_Austria_Brixen, (?wxDateTime_Gr_Unknown+1)).
+-define(wxDateTime_Gr_Austria_Salzburg, ?wxDateTime_Gr_Austria_Brixen).
+-define(wxDateTime_Gr_Austria_Tyrol, ?wxDateTime_Gr_Austria_Brixen).
+-define(wxDateTime_Gr_Austria_Carinthia, (?wxDateTime_Gr_Austria_Brixen+1)).
+-define(wxDateTime_Gr_Austria_Styria, ?wxDateTime_Gr_Austria_Carinthia).
+-define(wxDateTime_Gr_Belgium, (?wxDateTime_Gr_Austria_Carinthia+1)).
+-define(wxDateTime_Gr_Bulgaria, ?wxDateTime_Gr_Unknown).
+-define(wxDateTime_Gr_Bulgaria_1, (?wxDateTime_Gr_Unknown+1)).
+-define(wxDateTime_Gr_Bulgaria_2, (?wxDateTime_Gr_Unknown+2)).
+-define(wxDateTime_Gr_Bulgaria_3, (?wxDateTime_Gr_Unknown+3)).
+-define(wxDateTime_Gr_Canada, ?wxDateTime_Gr_Unknown).
+-define(wxDateTime_Gr_China, ?wxDateTime_Gr_Unknown).
+-define(wxDateTime_Gr_China_1, (?wxDateTime_Gr_Unknown+1)).
+-define(wxDateTime_Gr_China_2, (?wxDateTime_Gr_Unknown+2)).
+-define(wxDateTime_Gr_Czechoslovakia, (?wxDateTime_Gr_Unknown+3)).
+-define(wxDateTime_Gr_Denmark, (?wxDateTime_Gr_Unknown+4)).
+-define(wxDateTime_Gr_Egypt, (?wxDateTime_Gr_Unknown+5)).
+-define(wxDateTime_Gr_Estonia, (?wxDateTime_Gr_Unknown+6)).
+-define(wxDateTime_Gr_Finland, (?wxDateTime_Gr_Unknown+7)).
+-define(wxDateTime_Gr_France, (?wxDateTime_Gr_Unknown+8)).
+-define(wxDateTime_Gr_France_Alsace, (?wxDateTime_Gr_Unknown+9)).
+-define(wxDateTime_Gr_France_Lorraine, (?wxDateTime_Gr_Unknown+10)).
+-define(wxDateTime_Gr_France_Strasbourg, (?wxDateTime_Gr_Unknown+11)).
+-define(wxDateTime_Gr_Germany, ?wxDateTime_Gr_Unknown).
+-define(wxDateTime_Gr_Germany_Catholic, (?wxDateTime_Gr_Unknown+1)).
+-define(wxDateTime_Gr_Germany_Prussia, (?wxDateTime_Gr_Unknown+2)).
+-define(wxDateTime_Gr_Germany_Protestant, (?wxDateTime_Gr_Unknown+3)).
+-define(wxDateTime_Gr_GreatBritain, (?wxDateTime_Gr_Unknown+4)).
+-define(wxDateTime_Gr_Greece, (?wxDateTime_Gr_Unknown+5)).
+-define(wxDateTime_Gr_Hungary, (?wxDateTime_Gr_Unknown+6)).
+-define(wxDateTime_Gr_Ireland, ?wxDateTime_Gr_GreatBritain).
+-define(wxDateTime_Gr_Italy, ?wxDateTime_Gr_Standard).
+-define(wxDateTime_Gr_Japan, ?wxDateTime_Gr_Unknown).
+-define(wxDateTime_Gr_Japan_1, (?wxDateTime_Gr_Unknown+1)).
+-define(wxDateTime_Gr_Japan_2, (?wxDateTime_Gr_Unknown+2)).
+-define(wxDateTime_Gr_Japan_3, (?wxDateTime_Gr_Unknown+3)).
+-define(wxDateTime_Gr_Latvia, (?wxDateTime_Gr_Unknown+4)).
+-define(wxDateTime_Gr_Lithuania, (?wxDateTime_Gr_Unknown+5)).
+-define(wxDateTime_Gr_Luxemburg, (?wxDateTime_Gr_Unknown+6)).
+-define(wxDateTime_Gr_Netherlands, ?wxDateTime_Gr_Belgium).
+-define(wxDateTime_Gr_Netherlands_Groningen, (?wxDateTime_Gr_Belgium+1)).
+-define(wxDateTime_Gr_Netherlands_Gelderland, (?wxDateTime_Gr_Belgium+2)).
+-define(wxDateTime_Gr_Netherlands_Utrecht, (?wxDateTime_Gr_Belgium+3)).
+-define(wxDateTime_Gr_Netherlands_Friesland, (?wxDateTime_Gr_Belgium+4)).
+-define(wxDateTime_Gr_Norway, ?wxDateTime_Gr_Denmark).
+-define(wxDateTime_Gr_Poland, ?wxDateTime_Gr_Standard).
+-define(wxDateTime_Gr_Portugal, ?wxDateTime_Gr_Standard).
+-define(wxDateTime_Gr_Romania, (?wxDateTime_Gr_Standard+1)).
+-define(wxDateTime_Gr_Russia, (?wxDateTime_Gr_Standard+2)).
+-define(wxDateTime_Gr_Scotland, ?wxDateTime_Gr_GreatBritain).
+-define(wxDateTime_Gr_Spain, ?wxDateTime_Gr_Standard).
+-define(wxDateTime_Gr_Sweden, ?wxDateTime_Gr_Finland).
+-define(wxDateTime_Gr_Switzerland, ?wxDateTime_Gr_Unknown).
+-define(wxDateTime_Gr_Switzerland_Catholic, (?wxDateTime_Gr_Unknown+1)).
+-define(wxDateTime_Gr_Switzerland_Protestant, (?wxDateTime_Gr_Unknown+2)).
+-define(wxDateTime_Gr_Turkey, (?wxDateTime_Gr_Unknown+3)).
+-define(wxDateTime_Gr_USA, ?wxDateTime_Gr_GreatBritain).
+-define(wxDateTime_Gr_Wales, ?wxDateTime_Gr_GreatBritain).
+-define(wxDateTime_Gr_Yugoslavia, (?wxDateTime_Gr_GreatBritain+1)).
% From class wxDateTime::Month
-define(wxDateTime_Jan, 0).
-define(wxDateTime_Feb, 1).
@@ -537,34 +533,34 @@
-define(wxDateTime_GMT11, 24).
-define(wxDateTime_GMT12, 25).
-define(wxDateTime_GMT13, 26).
--define(wxDateTime_WET, ?GMT0).
--define(wxDateTime_WEST, ?GMT1).
--define(wxDateTime_CET, ?GMT1).
--define(wxDateTime_CEST, ?GMT2).
--define(wxDateTime_EET, ?GMT2).
--define(wxDateTime_EEST, ?GMT3).
--define(wxDateTime_MSK, ?GMT3).
--define(wxDateTime_MSD, ?GMT4).
--define(wxDateTime_AST, ?GMT_4).
--define(wxDateTime_ADT, ?GMT_3).
--define(wxDateTime_EST, ?GMT_5).
--define(wxDateTime_EDT, ?GMT_4).
--define(wxDateTime_CST, ?GMT_6).
--define(wxDateTime_CDT, ?GMT_5).
--define(wxDateTime_MST, ?GMT_7).
--define(wxDateTime_MDT, ?GMT_6).
--define(wxDateTime_PST, ?GMT_8).
--define(wxDateTime_PDT, ?GMT_7).
--define(wxDateTime_HST, ?GMT_10).
--define(wxDateTime_AKST, ?GMT_9).
--define(wxDateTime_AKDT, ?GMT_8).
--define(wxDateTime_A_WST, ?GMT8).
--define(wxDateTime_A_CST, ?GMT13+1).
--define(wxDateTime_A_EST, ?GMT10).
--define(wxDateTime_A_ESST, ?GMT11).
--define(wxDateTime_NZST, ?GMT12).
--define(wxDateTime_NZDT, ?GMT13).
--define(wxDateTime_UTC, ?GMT0).
+-define(wxDateTime_WET, ?wxDateTime_GMT0).
+-define(wxDateTime_WEST, ?wxDateTime_GMT1).
+-define(wxDateTime_CET, ?wxDateTime_GMT1).
+-define(wxDateTime_CEST, ?wxDateTime_GMT2).
+-define(wxDateTime_EET, ?wxDateTime_GMT2).
+-define(wxDateTime_EEST, ?wxDateTime_GMT3).
+-define(wxDateTime_MSK, ?wxDateTime_GMT3).
+-define(wxDateTime_MSD, ?wxDateTime_GMT4).
+-define(wxDateTime_AST, ?wxDateTime_GMT_4).
+-define(wxDateTime_ADT, ?wxDateTime_GMT_3).
+-define(wxDateTime_EST, ?wxDateTime_GMT_5).
+-define(wxDateTime_EDT, ?wxDateTime_GMT_4).
+-define(wxDateTime_CST, ?wxDateTime_GMT_6).
+-define(wxDateTime_CDT, ?wxDateTime_GMT_5).
+-define(wxDateTime_MST, ?wxDateTime_GMT_7).
+-define(wxDateTime_MDT, ?wxDateTime_GMT_6).
+-define(wxDateTime_PST, ?wxDateTime_GMT_8).
+-define(wxDateTime_PDT, ?wxDateTime_GMT_7).
+-define(wxDateTime_HST, ?wxDateTime_GMT_10).
+-define(wxDateTime_AKST, ?wxDateTime_GMT_9).
+-define(wxDateTime_AKDT, ?wxDateTime_GMT_8).
+-define(wxDateTime_A_WST, ?wxDateTime_GMT8).
+-define(wxDateTime_A_CST, (?wxDateTime_GMT13+1)).
+-define(wxDateTime_A_EST, ?wxDateTime_GMT10).
+-define(wxDateTime_A_ESST, ?wxDateTime_GMT11).
+-define(wxDateTime_NZST, ?wxDateTime_GMT12).
+-define(wxDateTime_NZDT, ?wxDateTime_GMT13).
+-define(wxDateTime_UTC, ?wxDateTime_GMT0).
% From class wxDateTime::WeekDay
-define(wxDateTime_Sun, 0).
-define(wxDateTime_Mon, 1).
@@ -579,7 +575,6 @@
-define(wxDateTime_Monday_First, 1).
-define(wxDateTime_Sunday_First, 2).
% From class wxDateTime::Year
--define(wxDateTime_Inv_Year, ?SHRT_MIN).
% From class wxDialog
-define(wxDialog_ButtonSizerFlags, (?wxOK bor ?wxCANCEL bor ?wxYES bor ?wxNO bor ?wxHELP bor ?wxNO_DEFAULT)).
% From class wxGrid
@@ -760,6 +755,7 @@
-define(wxCLRP_DEFAULT_STYLE, 0).
-define(wxCLRP_USE_TEXTCTRL, ?wxPB_USE_TEXTCTRL).
-define(wxCLRP_SHOW_LABEL, 8).
+% From "cmndata.h"
% From "cmndata.h": wxPrintBin
-define(wxPRINTBIN_DEFAULT, 0).
-define(wxPRINTBIN_ONLYONE, 1).
@@ -782,6 +778,8 @@
-define(wxC2S_NAME, 1).
% From "confbase.h"
-define(wxCONFIG_CASE_SENSITIVE, 0).
+% From "cpp.h"
+-define(wxEMPTY_PARAMETER_VALUE, ()).
% From "datectrl.h"
-define(wxDP_DEFAULT, 0).
-define(wxDP_SPIN, 1).
@@ -789,7 +787,6 @@
-define(wxDP_SHOWCENTURY, 4).
-define(wxDP_ALLOWNONE, 8).
% From "datetime.h"
--define(wxInvalidDateTime, ?wxDefaultDateTime).
% From "dcbuffer.h"
-define(wxBUFFER_CLIENT_AREA, 2).
-define(wxBUFFER_VIRTUAL_AREA, 1).
@@ -1560,13 +1557,13 @@
% From "event.h"
% From "event.h": Propagation_state
-define(wxEVENT_PROPAGATE_NONE, 0).
--define(wxEVENT_PROPAGATE_MAX, ?INT_MAX).
% From "event.h": wxIdleMode
-define(wxIDLE_PROCESS_ALL, 0).
-define(wxIDLE_PROCESS_SPECIFIED, 1).
% From "event.h": wxUpdateUIMode
-define(wxUPDATE_UI_PROCESS_ALL, 0).
-define(wxUPDATE_UI_PROCESS_SPECIFIED, 1).
+% From "fdrepdlg.h"
% From "fdrepdlg.h": wxFindReplaceDialogStyles
-define(wxFR_REPLACEDIALOG, 1).
-define(wxFR_NOUPDOWN, 2).
@@ -1810,7 +1807,6 @@
-define(wxGA_VERTICAL, ?wxVERTICAL).
-define(wxGA_HORIZONTAL, ?wxHORIZONTAL).
% From "gdicmn.h"
--define(wxGetDisplayDepth, ?wxDisplayDepth).
% From "gdicmn.h": wxBitmapType
-define(wxBITMAP_TYPE_INVALID, 0).
-define(wxBITMAP_TYPE_BMP, 1).
@@ -2091,7 +2087,6 @@
-define(wxLB_TOP, ?wxBK_TOP).
-define(wxLB_DEFAULT, ?wxBK_DEFAULT).
% From "log.h"
--define(wxTRACE_OleCalls, ?wxEmptyString).
-define(wxTraceRefCount, 8).
-define(wxTraceResAlloc, 4).
-define(wxTraceMessages, 2).
@@ -2195,7 +2190,8 @@
-define(wxSYS_COLOUR_MENUHILIGHT, (?wxSYS_COLOUR_BTNHIGHLIGHT+9)).
-define(wxSYS_COLOUR_MENUBAR, (?wxSYS_COLOUR_BTNHIGHLIGHT+10)).
-define(wxSYS_COLOUR_LISTBOXTEXT, (?wxSYS_COLOUR_BTNHIGHLIGHT+11)).
--define(wxSYS_COLOUR_MAX, (?wxSYS_COLOUR_BTNHIGHLIGHT+12)).
+-define(wxSYS_COLOUR_LISTBOXHIGHLIGHTTEXT, (?wxSYS_COLOUR_BTNHIGHLIGHT+12)).
+-define(wxSYS_COLOUR_MAX, (?wxSYS_COLOUR_BTNHIGHLIGHT+13)).
% From "settings.h": wxSystemFeature
-define(wxSYS_CAN_DRAW_FRAME_DECORATIONS, 1).
-define(wxSYS_CAN_ICONIZE_FRAME, 2).
@@ -3797,6 +3793,7 @@
-define(wxRELEASE_NUMBER, wxe_util:get_const(wxRELEASE_NUMBER)).
-define(wxMINOR_VERSION, wxe_util:get_const(wxMINOR_VERSION)).
-define(wxMAJOR_VERSION, wxe_util:get_const(wxMAJOR_VERSION)).
+% From "window.h"
% From "window.h": wxWindowVariant
-define(wxWINDOW_VARIANT_NORMAL, 0).
-define(wxWINDOW_VARIANT_SMALL, 1).
diff --git a/lib/wx/src/Makefile b/lib/wx/src/Makefile
index 777fb7d998..26574ed86f 100644
--- a/lib/wx/src/Makefile
+++ b/lib/wx/src/Makefile
@@ -101,19 +101,19 @@ archive: opt
# ----------------------------------------------------
$(APP_TARGET): $(APP_SRC) ../vsn.mk Makefile
- sed -e 's;%GEN_MODS%;$(GEN_MODS);' $< > [email protected]
- sed -e 's;%VSN%;$(VSN);' [email protected] > $@
+ $(gen_verbose)sed -e 's;%GEN_MODS%;$(GEN_MODS);' $< > [email protected]
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' [email protected] > $@
+ $(V_at)rm [email protected]
$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk Makefile
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
# Rules
$(EBIN)/%.beam: $(ESRC)/%.erl $(HEADER_FILES)
- $(ERLC) -W -bbeam $(ERL_FLAGS) $(ERL_COMPILE_FLAGS) -o$(EBIN) $<
+ $(V_ERLC) -W -bbeam $(ERL_FLAGS) $(ERL_COMPILE_FLAGS) -o$(EBIN) $<
$(EBIN)/%.beam: $(EGEN)/%.erl $(HEADER_FILES)
- $(ERLC) -W -bbeam $(ERL_FLAGS) $(ERL_COMPILE_FLAGS) -o$(EBIN) $<
+ $(V_ERLC) -W -bbeam $(ERL_FLAGS) $(ERL_COMPILE_FLAGS) -o$(EBIN) $<
# ----------------------------------------------------
# Release Target
diff --git a/lib/wx/src/gen/gl.erl b/lib/wx/src/gen/gl.erl
index ff381683ee..8a8158c35e 100644
--- a/lib/wx/src/gen/gl.erl
+++ b/lib/wx/src/gen/gl.erl
@@ -1,7 +1,9 @@
+%% -*- coding: utf-8 -*-
+
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -460,7 +462,7 @@ alphaFunc(Func,Ref) ->
%% as (R s0 G s0 B s0 A s0), (R s1 G s1 B s1 A s1) and (R d G d B d A d), respectively. The color specified by {@link gl:blendColor/4} is referred to
%% as (R c G c B c A c). They are understood to have integer values between 0 and (k R k G k B k A), where
%%
-%% k c= 2(m c)-1
+%% k c=2(m c)-1
%%
%% and (m R m G m B m A) is the number of red, green, blue, and alpha bitplanes.
%%
@@ -489,12 +491,12 @@ alphaFunc(Func,Ref) ->
%%
%% In the table,
%%
-%% i= min(A s k A-A d) k/A
+%% i=min(A s k A-A d) k/A
%%
%% To determine the blended RGBA values of a pixel, the system uses the following equations:
%%
%%
-%% R d= min(k R R s s R+R d d R) G d= min(k G G s s G+G d d G) B d= min(k B B s s B+B d d B) A d= min(k A A s s A+A d d A)
+%% R d=min(k R R s s R+R d d R) G d=min(k G G s s G+G d d G) B d=min(k B B s s B+B d d B) A d=min(k A A s s A+A d d A)
%%
%% Despite the apparent precision of the above equations, blending arithmetic is not exactly
%% specified, because blending operates with imprecise integer color values. However, a blend
@@ -503,7 +505,7 @@ alphaFunc(Func,Ref) ->
%% , `Dfactor' is `?GL_ONE_MINUS_SRC_ALPHA', and A s is equal to k A, the equations
%% reduce to simple replacement:
%%
-%% R d= R s G d= G s B d= B s A d= A s
+%% R d=R s G d=G s B d=B s A d=A s
%%
%%
%%
@@ -643,7 +645,7 @@ lineWidth(Width) ->
%% is 0, otherwise these fragments are sent to the frame buffer. Bit zero of `Pattern'
%% is the least significant bit.
%%
-%% Antialiased lines are treated as a sequence of 1*width rectangles for purposes of stippling.
+%% Antialiased lines are treated as a sequence of 1×width rectangles for purposes of stippling.
%% Whether rectangle s is rasterized or not depends on the fragment rule described for
%% aliased lines, counting rectangles rather than groups of fragments.
%%
@@ -690,7 +692,7 @@ polygonMode(Face,Mode) ->
%% When `?GL_POLYGON_OFFSET_FILL', `?GL_POLYGON_OFFSET_LINE', or `?GL_POLYGON_OFFSET_POINT'
%% is enabled, each fragment's `depth' value will be offset after it is interpolated
%% from the `depth' values of the appropriate vertices. The value of the offset is
-%% factor*DZ+r*units, where DZ is a measurement of the change in depth relative to the
+%% factor×DZ+r×units, where DZ is a measurement of the change in depth relative to the
%% screen area of the polygon, and r is the smallest value that is guaranteed to produce
%% a resolvable offset for a given implementation. The offset is added before the depth test
%% is performed and before the value is written into the depth buffer.
@@ -709,10 +711,10 @@ polygonOffset(Factor,Units) ->
%% fragments produced by rasterization, creating a pattern. Stippling is independent of polygon
%% antialiasing.
%%
-%% `Pattern' is a pointer to a 32*32 stipple pattern that is stored in memory just
+%% `Pattern' is a pointer to a 32×32 stipple pattern that is stored in memory just
%% like the pixel data supplied to a {@link gl:drawPixels/5} call with height and `width'
%% both equal to 32, a pixel format of `?GL_COLOR_INDEX', and data type of `?GL_BITMAP'
-%% . That is, the stipple pattern is represented as a 32*32 array of 1-bit color indices
+%% . That is, the stipple pattern is represented as a 32×32 array of 1-bit color indices
%% packed in unsigned bytes. {@link gl:pixelStoref/2} parameters like `?GL_UNPACK_SWAP_BYTES'
%% and `?GL_UNPACK_LSB_FIRST' affect the assembling of the bits into a stipple pattern.
%% Pixel transfer operations (shift, offset, pixel map) are not applied to the stipple image,
@@ -737,10 +739,10 @@ polygonStipple(Mask) ->
%% @doc Return the polygon stipple pattern
%%
-%% ``gl:getPolygonStipple'' returns to `Pattern' a 32*32 polygon stipple pattern.
+%% ``gl:getPolygonStipple'' returns to `Pattern' a 32×32 polygon stipple pattern.
%% The pattern is packed into memory as if {@link gl:readPixels/7} with both `height'
%% and `width' of 32, `type' of `?GL_BITMAP', and `format' of `?GL_COLOR_INDEX'
-%% were called, and the stipple pattern were stored in an internal 32*32 color index buffer.
+%% were called, and the stipple pattern were stored in an internal 32×32 color index buffer.
%% Unlike {@link gl:readPixels/7} , however, pixel transfer operations (shift, offset, pixel
%% map) are not applied to the returned stipple image.
%%
@@ -2635,7 +2637,7 @@ loadIdentity() ->
%% and `M' points to an array of 16 single- or double-precision floating-point values
%% m={m[0] m[1] ... m[15]}, then the modelview transformation M(v) does the following:
%%
-%% M(v)=(m[0] m[4] m[8] m[12] m[1] m[5] m[9] m[13] m[2] m[6] m[10] m[14] m[3] m[7] m[11] m[15])*(v[0] v[1] v[2] v[3])
+%% M(v)=(m[0] m[4] m[8] m[12] m[1] m[5] m[9] m[13] m[2] m[6] m[10] m[14] m[3] m[7] m[11] m[15])×(v[0] v[1] v[2] v[3])
%%
%% Projection and texture transformations are similarly defined.
%%
@@ -2687,7 +2689,7 @@ multMatrixf({M1,M2,M3,M4,M5,M6,M7,M8,M9,M10,M11,M12}) ->
%% (x 2(1-c)+c x y(1-c)-z s x z(1-c)+y s 0 y x(1-c)+z s y 2(1-c)+c y z(1-c)-x s 0 x z(1-c)-y s y z(1-c)+x s z 2(1-c)+c 0 0 0 0
%% 1)
%%
-%% Where c= cos(angle), s= sin(angle), and ||(x y z)||= 1 (if not, the GL will normalize this vector).
+%% Where c=cos(angle), s=sin(angle), and ||(x y z)||=1 (if not, the GL will normalize this vector).
%%
%% If the matrix mode is either `?GL_MODELVIEW' or `?GL_PROJECTION', all objects
%% drawn after ``gl:rotate'' is called are rotated. Use {@link gl:pushMatrix/0} and {@link gl:pushMatrix/0}
@@ -3814,7 +3816,7 @@ rasterPos4sv({X,Y,Z,W}) -> rasterPos4s(X,Y,Z,W).
%% ``gl:rect'' supports efficient specification of rectangles as two corner points. Each
%% rectangle command takes four arguments, organized either as two consecutive pairs of (x y)
%% coordinates or as two pointers to arrays, each containing an (x y) pair. The resulting rectangle
-%% is defined in the z= 0 plane.
+%% is defined in the z=0 plane.
%%
%% ``gl:rect''( `X1' , `Y1' , `X2' , `Y2' ) is exactly equivalent to the
%% following sequence: glBegin(`?GL_POLYGON'); glVertex2( `X1' , `Y1' ); glVertex2(
@@ -4684,9 +4686,9 @@ pixelZoom(Xfactor,Yfactor) ->
%% is the number of pixels in a row (`?GL_PACK_ROW_LENGTH' if it is greater than 0,
%% the width argument to the pixel routine otherwise), a is the value of `?GL_PACK_ALIGNMENT'
%% , and s is the size, in bytes, of a single component (if a&lt; s, then it is as if a=
-%% s). In the case of 1-bit values, the location of the next row is obtained by skipping
+%% s). In the case of 1-bit values, the location of the next row is obtained by skipping
%%
-%% k= 8 a |(n l)/(8 a)|
+%% k=8 a |(n l)/(8 a)|
%%
%% components or indices.
%%
@@ -4708,7 +4710,7 @@ pixelZoom(Xfactor,Yfactor) ->
%% a pixel image (`?GL_PACK_IMAGE_HEIGHT' if it is greater than 0, the height argument
%% to the {@link gl:texImage3D/10} routine otherwise), a is the value of `?GL_PACK_ALIGNMENT'
%% , and s is the size, in bytes, of a single component (if a&lt; s, then it is as if
-%% a= s).
+%% a=s).
%%
%% The word `component' in this description refers to the nonindex values red, green,
%% blue, alpha, and depth. Storage format `?GL_RGB', for example, has three components
@@ -4758,9 +4760,9 @@ pixelZoom(Xfactor,Yfactor) ->
%% is the number of pixels in a row (`?GL_UNPACK_ROW_LENGTH' if it is greater than 0,
%% the width argument to the pixel routine otherwise), a is the value of `?GL_UNPACK_ALIGNMENT'
%% , and s is the size, in bytes, of a single component (if a&lt; s, then it is as if a=
-%% s). In the case of 1-bit values, the location of the next row is obtained by skipping
+%% s). In the case of 1-bit values, the location of the next row is obtained by skipping
%%
-%% k= 8 a |(n l)/(8 a)|
+%% k=8 a |(n l)/(8 a)|
%%
%% components or indices.
%%
@@ -4781,8 +4783,8 @@ pixelZoom(Xfactor,Yfactor) ->
%% the width argument to {@link gl:texImage3D/10} otherwise), h is the number of rows in
%% an image (`?GL_UNPACK_IMAGE_HEIGHT' if it is greater than 0, the height argument
%% to {@link gl:texImage3D/10} otherwise), a is the value of `?GL_UNPACK_ALIGNMENT',
-%% and s is the size, in bytes, of a single component (if a&lt; s, then it is as if a=
-%% s).
+%% and s is the size, in bytes, of a single component (if a&lt; s, then it is as if a=s).
+%%
%%
%% The word `component' in this description refers to the nonindex values red, green,
%% blue, alpha, and depth. Storage format `?GL_RGB', for example, has three components
@@ -5327,7 +5329,7 @@ readPixels(X,Y,Width,Height,Format,Type,Pixels) ->
%% or `?GL_STENCIL_INDEX'. Each unsigned byte is treated as eight 1-bit pixels, with
%% bit ordering determined by `?GL_UNPACK_LSB_FIRST' (see {@link gl:pixelStoref/2} ).
%%
-%% width*height pixels are read from memory, starting at location `Data' . By default,
+%% width×height pixels are read from memory, starting at location `Data' . By default,
%% these pixels are taken from adjacent memory locations, except that after all `Width'
%% pixels are read, the read pointer is advanced to the next four-byte boundary. The four-byte
%% row alignment is specified by {@link gl:pixelStoref/2} with argument `?GL_UNPACK_ALIGNMENT'
@@ -5340,7 +5342,7 @@ readPixels(X,Y,Width,Height,Format,Type,Pixels) ->
%% (see {@link gl:bindBuffer/2} ) while a block of pixels is specified, `Data' is treated
%% as a byte offset into the buffer object's data store.
%%
-%% The width*height pixels that are read from memory are each operated on in the same
+%% The width×height pixels that are read from memory are each operated on in the same
%% way, based on the values of several parameters specified by {@link gl:pixelTransferf/2}
%% and {@link gl:pixelMapfv/3} . The details of these operations, as well as the target buffer
%% into which the pixels are drawn, are specific to the format of the pixels, as specified
@@ -5366,10 +5368,10 @@ readPixels(X,Y,Width,Height,Format,Type,Pixels) ->
%%
%% The GL then converts the resulting indices or RGBA colors to fragments by attaching the
%% current raster position `z' coordinate and texture coordinates to each pixel, then
-%% assigning x and y window coordinates to the nth fragment such that x n= x r+n%
-%% width
+%% assigning x and y window coordinates to the nth fragment such that x n=x r+n% width
+%%
%%
-%% y n= y r+|n/width|
+%% y n=y r+|n/width|
%%
%% where (x r y r) is the current raster position. These pixel fragments are then treated just like
%% the fragments generated by rasterizing points, lines, or polygons. Texture mapping, fog,
@@ -5391,9 +5393,9 @@ readPixels(X,Y,Width,Height,Format,Type,Pixels) ->
%% the number of bits in the stencil buffer. The resulting stencil indices are then written
%% to the stencil buffer such that the nth index is written to location
%%
-%% x n= x r+n% width
+%% x n=x r+n% width
%%
-%% y n= y r+|n/width|
+%% y n=y r+|n/width|
%%
%% where (x r y r) is the current raster position. Only the pixel ownership test, the scissor test,
%% and the stencil writemask affect these write operations.
@@ -5411,9 +5413,9 @@ readPixels(X,Y,Width,Height,Format,Type,Pixels) ->
%% raster position color or color index and texture coordinates to each pixel, then assigning
%% x and y window coordinates to the nth fragment such that
%%
-%% x n= x r+n% width
+%% x n=x r+n% width
%%
-%% y n= y r+|n/width|
+%% y n=y r+|n/width|
%%
%% where (x r y r) is the current raster position. These pixel fragments are then treated just like
%% the fragments generated by rasterizing points, lines, or polygons. Texture mapping, fog,
@@ -5442,9 +5444,9 @@ readPixels(X,Y,Width,Height,Format,Type,Pixels) ->
%% raster position `z' coordinate and texture coordinates to each pixel, then assigning
%% x and y window coordinates to the nth fragment such that
%%
-%% x n= x r+n% width
+%% x n=x r+n% width
%%
-%% y n= y r+|n/width|
+%% y n=y r+|n/width|
%%
%% where (x r y r) is the current raster position. These pixel fragments are then treated just like
%% the fragments generated by rasterizing points, lines, or polygons. Texture mapping, fog,
@@ -5810,7 +5812,7 @@ clearStencil(S) ->
%%
%% If the texture generation function is `?GL_OBJECT_LINEAR', the function
%%
-%% g= p 1*x o+p 2*y o+p 3*z o+p 4*w o
+%% g=p 1×x o+p 2×y o+p 3×z o+p 4×w o
%%
%% is used, where g is the value computed for the coordinate named in `Coord' , p 1,
%% p 2, p 3, and p 4 are the four values supplied in `Params' , and x o, y o, z o,
@@ -5823,7 +5825,7 @@ clearStencil(S) ->
%%
%% If the texture generation function is `?GL_EYE_LINEAR', the function
%%
-%% g=(p 1)"*x e+(p 2)"*y e+(p 3)"*z e+(p 4)"*w e
+%% g=(p 1)"×x e+(p 2)"×y e+(p 3)"×z e+(p 4)"×w e
%%
%% is used, where
%%
@@ -5847,14 +5849,14 @@ clearStencil(S) ->
%%
%% f=(f x f y f z) T be the reflection vector such that
%%
-%% f= u-2 n" (n") T u
+%% f=u-2 n" (n") T u
%%
-%% Finally, let m= 2 ((f x) 2+(f y) 2+(f z+1) 2). Then the values assigned to the s and t texture coordinates
+%% Finally, let m=2 ((f x) 2+(f y) 2+(f z+1) 2). Then the values assigned to the s and t texture coordinates
%% are
%%
-%% s= f x/m+1/2
+%% s=f x/m+1/2
%%
-%% t= f y/m+1/2
+%% t=f y/m+1/2
%%
%% To enable or disable a texture-coordinate generation function, call {@link gl:enable/1}
%% or {@link gl:enable/1} with one of the symbolic texture-coordinate names (`?GL_TEXTURE_GEN_S'
@@ -6002,7 +6004,7 @@ texEnvi(Target,Pname,Param) ->
%% `?GL_BLEND' Function </td><td>`?GL_ADD' Function </td></tr></tbody><tbody><tr><td>
%% `?GL_ALPHA'</td><td> C v=</td><td> C p</td><td> C p</td><td> undefined </td><td> C p</td>
%% <td> C p</td></tr><tr><td></td><td> A v=</td><td> A s</td><td> A p A s</td><td></td><td>
-%% A v= A p A s</td><td> A p A s</td></tr><tr><td>`?GL_LUMINANCE'</td><td> C v=</td><td>
+%% A v=A p A s</td><td> A p A s</td></tr><tr><td>`?GL_LUMINANCE'</td><td> C v=</td><td>
%% C s</td><td> C p C s</td><td> undefined </td><td> C p (1-C s)+C c C s</td><td> C p+C s</td></tr>
%% <tr><td> (or 1) </td><td> A v=</td><td> A p</td><td> A p</td><td></td><td> A p</td><td> A
%% p</td></tr><tr><td>`?GL_LUMINANCE_ALPHA'</td><td> C v=</td><td> C s</td><td> C p C
@@ -6034,11 +6036,11 @@ texEnvi(Target,Pname,Param) ->
%%
%% <table><tbody><tr><td>`?GL_COMBINE_RGB'</td><td>` Texture Function '</td></tr></tbody>
%% <tbody><tr><td>`?GL_REPLACE'</td><td> Arg0</td></tr><tr><td>`?GL_MODULATE'</td><td>
-%% Arg0*Arg1</td></tr><tr><td>`?GL_ADD'</td><td> Arg0+Arg1</td></tr><tr><td>`?GL_ADD_SIGNED'
-%% </td><td> Arg0+Arg1-0.5</td></tr><tr><td>`?GL_INTERPOLATE'</td><td> Arg0*Arg2+Arg1*(1-
+%% Arg0×Arg1</td></tr><tr><td>`?GL_ADD'</td><td> Arg0+Arg1</td></tr><tr><td>`?GL_ADD_SIGNED'
+%% </td><td> Arg0+Arg1-0.5</td></tr><tr><td>`?GL_INTERPOLATE'</td><td> Arg0×Arg2+Arg1×(1-
%% Arg2)</td>
%% </tr><tr><td>`?GL_SUBTRACT'</td><td> Arg0-Arg1</td></tr><tr><td>`?GL_DOT3_RGB'
-%% or `?GL_DOT3_RGBA'</td><td> 4*((((Arg0 r)-0.5)*((Arg1 r)-0.5))+(((Arg0 g)-0.5)*((Arg1 g)-0.5))+(((Arg0 b)-0.5)*((Arg1 b)-0.5)))</td></tr></tbody></table>
+%% or `?GL_DOT3_RGBA'</td><td> 4×((((Arg0 r)-0.5)×((Arg1 r)-0.5))+(((Arg0 g)-0.5)×((Arg1 g)-0.5))+(((Arg0 b)-0.5)×((Arg1 b)-0.5)))</td></tr></tbody></table>
%%
%% The scalar results for `?GL_DOT3_RGB' and `?GL_DOT3_RGBA' are placed into each
%% of the 3 (RGB) or 4 (RGBA) components on output.
@@ -6049,8 +6051,8 @@ texEnvi(Target,Pname,Param) ->
%%
%% <table><tbody><tr><td>`?GL_COMBINE_ALPHA'</td><td>` Texture Function '</td></tr>
%% </tbody><tbody><tr><td>`?GL_REPLACE'</td><td> Arg0</td></tr><tr><td>`?GL_MODULATE'
-%% </td><td> Arg0*Arg1</td></tr><tr><td>`?GL_ADD'</td><td> Arg0+Arg1</td></tr><tr><td>`?GL_ADD_SIGNED'
-%% </td><td> Arg0+Arg1-0.5</td></tr><tr><td>`?GL_INTERPOLATE'</td><td> Arg0*Arg2+Arg1*(1-
+%% </td><td> Arg0×Arg1</td></tr><tr><td>`?GL_ADD'</td><td> Arg0+Arg1</td></tr><tr><td>`?GL_ADD_SIGNED'
+%% </td><td> Arg0+Arg1-0.5</td></tr><tr><td>`?GL_INTERPOLATE'</td><td> Arg0×Arg2+Arg1×(1-
%% Arg2)</td>
%% </tr><tr><td>`?GL_SUBTRACT'</td><td> Arg0-Arg1</td></tr></tbody></table>
%%
@@ -6245,19 +6247,18 @@ getTexEnviv(Target,Pname) ->
%% If the values for `?GL_TEXTURE_BORDER_COLOR' are specified with ``gl:texParameterIiv''
%% or ``gl:texParameterIuiv'', the values are stored unmodified with an internal data
%% type of integer. If specified with ``gl:texParameteriv'', they are converted to floating
-%% point with the following equation: f= 2 c+1 2 b-/1. If specified with ``gl:texParameterfv''
+%% point with the following equation: f=2 c+1 2 b-/1. If specified with ``gl:texParameterfv''
%% , they are stored unmodified as floating-point values.
%%
%% `?GL_TEXTURE_COMPARE_FUNC': Specifies the comparison operator used when `?GL_TEXTURE_COMPARE_MODE'
%% is set to `?GL_COMPARE_REF_TO_TEXTURE'. Permissible values are: <table><tbody><tr><td>
%% ` Texture Comparison Function '</td><td>` Computed result '</td></tr></tbody><tbody>
-%% <tr><td>`?GL_LEQUAL'</td><td> result={1.0 0.0 &amp;nbsp;&amp;nbsp; r&lt;=(D t) r&gt;(D t))</td></tr><tr><td>`?GL_GEQUAL'</td><td>
-%% result={1.0 0.0 &amp;nbsp;&amp;nbsp; r&gt;=(D t) r&lt;(D t))</td></tr><tr><td>`?GL_LESS'</td><td> result={1.0 0.0 &amp;nbsp;&amp;nbsp; r&lt;
-%% (D t) r&gt;=(D t))</td></tr><tr><td>`?GL_GREATER'
-%% </td><td> result={1.0 0.0 &amp;nbsp;&amp;nbsp; r&gt;(D t) r&lt;=(D t))</td></tr><tr><td>`?GL_EQUAL'</td><td> result={1.0 0.0 &amp;nbsp;&amp;nbsp;
-%% r=(D t) r&amp;ne;(D t))</td></tr><tr><td>`?GL_NOTEQUAL'
-%% </td><td> result={1.0 0.0 &amp;nbsp;&amp;nbsp; r&amp;ne;(D t) r=(D t))</td></tr><tr><td>`?GL_ALWAYS'</td><td> result= 1.0</td></tr><tr><td>
-%% `?GL_NEVER'</td><td> result= 0.0</td></tr></tbody></table> where r is the current
+%% <tr><td>`?GL_LEQUAL'</td><td> result={1.0 0.0 r&lt;=(D t) r&gt;(D t))</td></tr><tr><td>`?GL_GEQUAL'</td><td>
+%% result={1.0 0.0 r&gt;=(D t) r&lt;(D t))</td></tr><tr><td>`?GL_LESS'</td><td> result={1.0 0.0 r&lt;(D t) r&gt;=(D t))</td></tr><tr><td>`?GL_GREATER'
+%% </td><td> result={1.0 0.0 r&gt;(D t) r&lt;=(D t))</td></tr><tr><td>`?GL_EQUAL'</td><td> result={1.0 0.0 r=(D t) r&amp;ne;
+%% (D t))</td></tr><tr><td>`?GL_NOTEQUAL'
+%% </td><td> result={1.0 0.0 r&amp;ne;(D t) r=(D t))</td></tr><tr><td>`?GL_ALWAYS'</td><td> result=1.0</td></tr><tr><td>
+%% `?GL_NEVER'</td><td> result=0.0</td></tr></tbody></table> where r is the current
%% interpolated texture coordinate, and D t is the depth texture value sampled from the
%% currently bound depth texture. result is assigned to the the red channel.
%%
@@ -6286,14 +6287,14 @@ getTexEnviv(Target,Pname) ->
%% The other four use mipmaps.
%%
%% A mipmap is an ordered set of arrays representing the same image at progressively lower
-%% resolutions. If the texture has dimensions 2 n*2 m, there are max(n m)+1 mipmaps. The first
-%% mipmap is the original texture, with dimensions 2 n*2 m. Each subsequent mipmap has
-%% dimensions 2(k-1)*2(l-1), where 2 k*2 l are the dimensions of the previous mipmap, until either
-%% k= 0 or l= 0. At that point, subsequent mipmaps have dimension 1*2(l-1) or 2(k-1)*1 until
-%% the final mipmap, which has dimension 1*1. To define the mipmaps, call {@link gl:texImage1D/8}
+%% resolutions. If the texture has dimensions 2 n×2 m, there are max(n m)+1 mipmaps. The first
+%% mipmap is the original texture, with dimensions 2 n×2 m. Each subsequent mipmap has
+%% dimensions 2(k-1)×2(l-1), where 2 k×2 l are the dimensions of the previous mipmap, until either
+%% k=0 or l=0. At that point, subsequent mipmaps have dimension 1×2(l-1) or 2(k-1)×1 until
+%% the final mipmap, which has dimension 1×1. To define the mipmaps, call {@link gl:texImage1D/8}
%% , {@link gl:texImage2D/9} , {@link gl:texImage3D/10} , {@link gl:copyTexImage1D/7} , or {@link gl:copyTexImage2D/8}
%% with the `level' argument indicating the order of the mipmaps. Level 0 is the original
-%% texture; level max(n m) is the final 1*1 mipmap.
+%% texture; level max(n m) is the final 1×1 mipmap.
%%
%% `Params' supplies a function for minifying the texture as one of the following:
%%
@@ -7255,7 +7256,7 @@ map2f(Target,U1,U2,Ustride,Uorder,V1,V2,Vstride,Vorder,Points) ->
%% `Query' can assume the following values:
%%
%% `?GL_COEFF': `V' returns the control points for the evaluator function. One-dimensional
-%% evaluators return order control points, and two-dimensional evaluators return uorder*vorder
+%% evaluators return order control points, and two-dimensional evaluators return uorder×vorder
%% control points. Each control point consists of one, two, three, or four integer, single-precision
%% floating-point, or double-precision floating-point values, depending on the type of the
%% evaluator. The GL returns two-dimensional control points in row-major order, incrementing
@@ -7330,9 +7331,9 @@ getMapiv(Target,Query,V) ->
%% `?GL_AUTO_NORMAL', ``gl:evalCoord2'' generates surface normals analytically, regardless
%% of the contents or enabling of the `?GL_MAP2_NORMAL' map. Let
%%
-%% m=((&amp;PartialD; p)/(&amp;PartialD; u))*((&amp;PartialD; p)/(&amp;PartialD; v))
+%% m=((&amp;PartialD; p)/(&amp;PartialD; u))×((&amp;PartialD; p)/(&amp;PartialD; v))
%%
-%% Then the generated normal n is n= m/(||m||)
+%% Then the generated normal n is n=m/(||m||)
%%
%% If automatic normal generation is disabled, the corresponding normal map `?GL_MAP2_NORMAL'
%% , if enabled, is used to produce a normal. If neither automatic normal generation nor
@@ -7393,17 +7394,17 @@ evalCoord2fv({U,V}) -> evalCoord2f(U,V).
%% 0 maps exactly to `U1' , and integer grid coordinate `Un' maps exactly to `U2'
%% . All other integer grid coordinates i are mapped so that
%%
-%% u= i(u2-u1)/un+u1
+%% u=i(u2-u1)/un+u1
%%
%% ``gl:mapGrid2'' specifies two such linear mappings. One maps integer grid coordinate
-%% i= 0 exactly to `U1' , and integer grid coordinate i= un exactly to `U2' . The
-%% other maps integer grid coordinate j= 0 exactly to `V1' , and integer grid coordinate
-%% j= vn exactly to `V2' . Other integer grid coordinates i and j are mapped such
+%% i=0 exactly to `U1' , and integer grid coordinate i=un exactly to `U2' . The
+%% other maps integer grid coordinate j=0 exactly to `V1' , and integer grid coordinate
+%% j=vn exactly to `V2' . Other integer grid coordinates i and j are mapped such
%% that
%%
-%% u= i(u2-u1)/un+u1
+%% u=i(u2-u1)/un+u1
%%
-%% v= j(v2-v1)/vn+v1
+%% v=j(v2-v1)/vn+v1
%%
%% The mappings specified by ``gl:mapGrid'' are used identically by {@link gl:evalMesh1/3}
%% and {@link gl:evalPoint1/1} .
@@ -7440,7 +7441,7 @@ mapGrid2f(Un,U1,U2,Vn,V1,V2) ->
%% 1 ); where &amp;Delta; u=(u 2-u 1)/n
%%
%% and n, u 1, and u 2 are the arguments to the most recent {@link gl:mapGrid1d/3} command.
-%% The one absolute numeric requirement is that if i= n, then the value computed from i.&amp;Delta;
+%% The one absolute numeric requirement is that if i=n, then the value computed from i.&amp;Delta;
%% u+u 1 is exactly u 2.
%%
%% In the two-dimensional case, ``gl:evalPoint2'', let
@@ -7452,8 +7453,8 @@ mapGrid2f(Un,U1,U2,Vn,V1,V2) ->
%% where n, u 1, u 2, m, v 1, and v 2 are the arguments to the most recent {@link gl:mapGrid1d/3}
%% command. Then the ``gl:evalPoint2'' command is equivalent to calling glEvalCoord2( i.
%% &amp;Delta; u+u 1, j.&amp;Delta; v+v 1 ); The only absolute numeric requirements are
-%% that if i= n, then the value computed from i.&amp;Delta; u+u 1 is exactly u 2, and
-%% if j= m, then the value computed from j.&amp;Delta; v+v 1 is exactly v 2.
+%% that if i=n, then the value computed from i.&amp;Delta; u+u 1 is exactly u 2, and
+%% if j=m, then the value computed from j.&amp;Delta; v+v 1 is exactly v 2.
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glEvalPoint.xml">external</a> documentation.
-spec evalPoint1(I) -> ok when I :: integer().
@@ -7486,8 +7487,8 @@ evalPoint2(I,J) ->
%% `type' is `?GL_POINTS' if `Mode' is `?GL_POINT', or `?GL_LINES'
%% if `Mode' is `?GL_LINE'.
%%
-%% The one absolute numeric requirement is that if i= n, then the value computed from i.
-%% &amp;Delta; u+u 1 is exactly u 2.
+%% The one absolute numeric requirement is that if i=n, then the value computed from i.&amp;Delta;
+%% u+u 1 is exactly u 2.
%%
%% In the two-dimensional case, ``gl:evalMesh2'', let .cp &amp;Delta; u=(u 2-u 1)/n
%%
@@ -7516,8 +7517,8 @@ evalPoint2(I,J) ->
%% ; i &lt;= `I2' ; i += 1 ) glEvalCoord2( i.&amp;Delta; u+u 1, j.&amp;Delta; v+v 1
%% ); glEnd();
%%
-%% In all three cases, the only absolute numeric requirements are that if i= n, then the
-%% value computed from i.&amp;Delta; u+u 1 is exactly u 2, and if j= m, then the value
+%% In all three cases, the only absolute numeric requirements are that if i=n, then the
+%% value computed from i.&amp;Delta; u+u 1 is exactly u 2, and if j=m, then the value
%% computed from j.&amp;Delta; v+v 1 is exactly v 2.
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glEvalMesh.xml">external</a> documentation.
@@ -7578,21 +7579,21 @@ evalMesh2(Mode,I1,I2,J1,J2) ->
%% (in the case that `?GL_FOG_COORD_SRC' is `?GL_FOG_COORD'). The equation for `?GL_LINEAR'
%% fog is f=(end-c)/(end-start)
%%
-%% The equation for `?GL_EXP' fog is f= e(-(density. c))
+%% The equation for `?GL_EXP' fog is f=e(-(density. c))
%%
-%% The equation for `?GL_EXP2' fog is f= e(-(density. c)) 2
+%% The equation for `?GL_EXP2' fog is f=e(-(density. c)) 2
%%
%% Regardless of the fog mode, f is clamped to the range [0 1] after it is computed. Then,
%% if the GL is in RGBA color mode, the fragment's red, green, and blue colors, represented
%% by C r, are replaced by
%%
-%% (C r)"= f*C r+(1-f)*C f
+%% (C r)"=f×C r+(1-f)×C f
%%
%% Fog does not affect a fragment's alpha component.
%%
%% In color index mode, the fragment's color index i r is replaced by
%%
-%% (i r)"= i r+(1-f)*i f
+%% (i r)"=i r+(1-f)×i f
%%
%%
%%
@@ -7664,44 +7665,45 @@ fogiv(Pname,Params) ->
%% is fed back as some number of floating-point values, as determined by `Type' . Colors
%% are fed back as four values in RGBA mode and one value in color index mode.
%%
-%% feedbackList feedbackItem feedbackList | feedbackItem
+%% feedbackList ← feedbackItem feedbackList | feedbackItem
%%
-%% feedbackItem point | lineSegment | polygon | bitmap | pixelRectangle | passThru
+%% feedbackItem ← point | lineSegment | polygon | bitmap | pixelRectangle | passThru
%%
-%% point `?GL_POINT_TOKEN' vertex
+%% point ←`?GL_POINT_TOKEN' vertex
%%
-%% lineSegment `?GL_LINE_TOKEN' vertex vertex | `?GL_LINE_RESET_TOKEN' vertex
+%% lineSegment ←`?GL_LINE_TOKEN' vertex vertex | `?GL_LINE_RESET_TOKEN' vertex
%% vertex
%%
-%% polygon `?GL_POLYGON_TOKEN' n polySpec
+%% polygon ←`?GL_POLYGON_TOKEN' n polySpec
%%
-%% polySpec polySpec vertex | vertex vertex vertex
+%% polySpec ← polySpec vertex | vertex vertex vertex
%%
-%% bitmap `?GL_BITMAP_TOKEN' vertex
+%% bitmap ←`?GL_BITMAP_TOKEN' vertex
%%
-%% pixelRectangle `?GL_DRAW_PIXEL_TOKEN' vertex | `?GL_COPY_PIXEL_TOKEN' vertex
+%% pixelRectangle ←`?GL_DRAW_PIXEL_TOKEN' vertex | `?GL_COPY_PIXEL_TOKEN' vertex
+%%
%%
-%% passThru `?GL_PASS_THROUGH_TOKEN' value
+%% passThru ←`?GL_PASS_THROUGH_TOKEN' value
%%
-%% vertex 2d | 3d | 3dColor | 3dColorTexture | 4dColorTexture
+%% vertex ← 2d | 3d | 3dColor | 3dColorTexture | 4dColorTexture
%%
-%% 2d value value
+%% 2d ← value value
%%
-%% 3d value value value
+%% 3d ← value value value
%%
-%% 3dColor value value value color
+%% 3dColor ← value value value color
%%
-%% 3dColorTexture value value value color tex
+%% 3dColorTexture ← value value value color tex
%%
-%% 4dColorTexture value value value value color tex
+%% 4dColorTexture ← value value value value color tex
%%
-%% color rgba | index
+%% color ← rgba | index
%%
-%% rgba value value value value
+%% rgba ← value value value value
%%
-%% index value
+%% index ← value
%%
-%% tex value value value value
+%% tex ← value value value value
%%
%% `value' is a floating-point number, and `n' is a floating-point integer giving
%% the number of vertices in the polygon. `?GL_POINT_TOKEN', `?GL_LINE_TOKEN', `?GL_LINE_RESET_TOKEN'
@@ -7886,13 +7888,13 @@ blendColor(Red,Green,Blue,Alpha) ->
%% blend factors are denoted (s R s G s B s A) and (d R d G d B d A), respectively. For these equations all color components
%% are understood to have values in the range [0 1]. <table><tbody><tr><td>` Mode '</td><td>
%% ` RGB Components '</td><td>` Alpha Component '</td></tr></tbody><tbody><tr><td>`?GL_FUNC_ADD'
-%% </td><td> Rr= R s s R+R d d R Gr= G s s G+G d d G Br= B s s B+B d d B</td><td> Ar=
-%% A s s A+A d d A</td></tr><tr><td>`?GL_FUNC_SUBTRACT'</td><td> Rr= R s s R-R d d
-%% R Gr= G s s G-G d d G Br= B s s B-B d d B</td><td> Ar= A s s A-A d d A</td></tr><tr>
-%% <td>`?GL_FUNC_REVERSE_SUBTRACT'</td><td> Rr= R d d R-R s s R Gr= G d d G-G s s G
-%% Br= B d d B-B s s B</td><td> Ar= A d d A-A s s A</td></tr><tr><td>`?GL_MIN'</td><td>
-%% Rr= min(R s R d) Gr= min(G s G d) Br= min(B s B d)</td><td> Ar= min(A s A d)</td></tr><tr><td>`?GL_MAX'</td><td> Rr=
-%% max(R s R d) Gr= max(G s G d) Br= max(B s B d)</td><td> Ar= max(A s A d)</td></tr></tbody></table>
+%% </td><td> Rr=R s s R+R d d R Gr=G s s G+G d d G Br=B s s B+B d d B</td><td> Ar=A s
+%% s A+A d d A</td></tr><tr><td>`?GL_FUNC_SUBTRACT'</td><td> Rr=R s s R-R d d R Gr=G
+%% s s G-G d d G Br=B s s B-B d d B</td><td> Ar=A s s A-A d d A</td></tr><tr><td>`?GL_FUNC_REVERSE_SUBTRACT'
+%% </td><td> Rr=R d d R-R s s R Gr=G d d G-G s s G Br=B d d B-B s s B</td><td> Ar=A d
+%% d A-A s s A</td></tr><tr><td>`?GL_MIN'</td><td> Rr=min(R s R d) Gr=min(G s G d) Br=min(B s B d)</td><td> Ar=min
+%% (A s A d)</td></tr><tr><td>`?GL_MAX'</td><td> Rr=max(R s R d) Gr=max(G s G d) Br=max(B s B d)</td><td> Ar=max(A s A d)</td></tr></tbody>
+%% </table>
%%
%% The results of these equations are clamped to the range [0 1].
%%
@@ -9062,7 +9064,7 @@ sampleCoverage(Value,Invert) ->
%%
%% `ImageSize' must be equal to:
%%
-%% b s*|width b/w|*|height b/h|*|depth b/d|
+%% b s×|width b/w|×|height b/h|×|depth b/d|
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glCompressedTexImage3D.xml">external</a> documentation.
-spec compressedTexImage3D(Target, Level, Internalformat, Width, Height, Depth, Border, ImageSize, Data) -> ok when Target :: enum(),Level :: integer(),Internalformat :: enum(),Width :: integer(),Height :: integer(),Depth :: integer(),Border :: integer(),ImageSize :: integer(),Data :: offset()|mem().
@@ -9124,7 +9126,7 @@ compressedTexImage3D(Target,Level,Internalformat,Width,Height,Depth,Border,Image
%%
%% `ImageSize' must be equal to:
%%
-%% b s*|width b/w|*|height b/h|
+%% b s×|width b/w|×|height b/h|
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glCompressedTexImage2D.xml">external</a> documentation.
-spec compressedTexImage2D(Target, Level, Internalformat, Width, Height, Border, ImageSize, Data) -> ok when Target :: enum(),Level :: integer(),Internalformat :: enum(),Width :: integer(),Height :: integer(),Border :: integer(),ImageSize :: integer(),Data :: offset()|mem().
@@ -9181,7 +9183,7 @@ compressedTexImage2D(Target,Level,Internalformat,Width,Height,Border,ImageSize,D
%%
%% `ImageSize' must be equal to:
%%
-%% b s*|width b/w|
+%% b s×|width b/w|
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glCompressedTexImage1D.xml">external</a> documentation.
-spec compressedTexImage1D(Target, Level, Internalformat, Width, Border, ImageSize, Data) -> ok when Target :: enum(),Level :: integer(),Internalformat :: enum(),Width :: integer(),Border :: integer(),ImageSize :: integer(),Data :: offset()|mem().
@@ -9502,7 +9504,7 @@ multiTexCoord4sv(Target,{S,T,R,Q}) -> multiTexCoord4s(Target,S,T,R,Q).
%% and `M' points to an array of 16 single- or double-precision floating-point values
%% m={m[0] m[1] ... m[15]}, then the modelview transformation M(v) does the following:
%%
-%% M(v)=(m[0] m[1] m[2] m[3] m[4] m[5] m[6] m[7] m[8] m[9] m[10] m[11] m[12] m[13] m[14] m[15])*(v[0] v[1] v[2] v[3])
+%% M(v)=(m[0] m[1] m[2] m[3] m[4] m[5] m[6] m[7] m[8] m[9] m[10] m[11] m[12] m[13] m[14] m[15])×(v[0] v[1] v[2] v[3])
%%
%% Projection and texture transformations are similarly defined.
%%
@@ -9569,7 +9571,7 @@ multTransposeMatrixd({M1,M2,M3,M4,M5,M6,M7,M8,M9,M10,M11,M12}) ->
%% is referred to as (R c G c B c A c). They are understood to have integer values between 0 and (k R k G k B
%% k A), where
%%
-%% k c= 2(m c)-1
+%% k c=2(m c)-1
%%
%% and (m R m G m B m A) is the number of red, green, blue, and alpha bitplanes.
%%
@@ -9601,12 +9603,12 @@ multTransposeMatrixd({M1,M2,M3,M4,M5,M6,M7,M8,M9,M10,M11,M12}) ->
%%
%% In the table,
%%
-%% i= min(A s 1-(A d))
+%% i=min(A s 1-(A d))
%%
%% To determine the blended RGBA values of a pixel, the system uses the following equations:
%%
%%
-%% R d= min(k R R s s R+R d d R) G d= min(k G G s s G+G d d G) B d= min(k B B s s B+B d d B) A d= min(k A A s s A+A d d A)
+%% R d=min(k R R s s R+R d d R) G d=min(k G G s s G+G d d G) B d=min(k B B s s B+B d d B) A d=min(k A A s s A+A d d A)
%%
%% Despite the apparent precision of the above equations, blending arithmetic is not exactly
%% specified, because blending operates with imprecise integer color values. However, a blend
@@ -9615,7 +9617,7 @@ multTransposeMatrixd({M1,M2,M3,M4,M5,M6,M7,M8,M9,M10,M11,M12}) ->
%% , `DstRGB' is `?GL_ONE_MINUS_SRC_ALPHA', and A s is equal to k A, the equations
%% reduce to simple replacement:
%%
-%% R d= R s G d= G s B d= B s A d= A s
+%% R d=R s G d=G s B d=B s A d=A s
%%
%%
%%
@@ -9899,7 +9901,7 @@ secondaryColorPointer(Size,Type,Stride,Pointer) ->
%% current modelview and projection matrices, nor by the viewport-to-window transform. The
%% z coordinate of the current raster position is updated in the following manner:
%%
-%% z={n f(n+z*(f-n)) if z&lt;= 0 if z&gt;= 1(otherwise))
+%% z={n f(n+z×(f-n)) if z&lt;= 0 if z&gt;= 1(otherwise))
%%
%% where n is `?GL_DEPTH_RANGE''s near value, and f is `?GL_DEPTH_RANGE''s
%% far value. See {@link gl:depthRange/2} .
@@ -10397,13 +10399,13 @@ getBufferParameteriv(Target,Pname) ->
%% blend factors are denoted (s R s G s B s A) and (d R d G d B d A), respectively. For these equations all color components
%% are understood to have values in the range [0 1]. <table><tbody><tr><td>` Mode '</td><td>
%% ` RGB Components '</td><td>` Alpha Component '</td></tr></tbody><tbody><tr><td>`?GL_FUNC_ADD'
-%% </td><td> Rr= R s s R+R d d R Gr= G s s G+G d d G Br= B s s B+B d d B</td><td> Ar=
-%% A s s A+A d d A</td></tr><tr><td>`?GL_FUNC_SUBTRACT'</td><td> Rr= R s s R-R d d
-%% R Gr= G s s G-G d d G Br= B s s B-B d d B</td><td> Ar= A s s A-A d d A</td></tr><tr>
-%% <td>`?GL_FUNC_REVERSE_SUBTRACT'</td><td> Rr= R d d R-R s s R Gr= G d d G-G s s G
-%% Br= B d d B-B s s B</td><td> Ar= A d d A-A s s A</td></tr><tr><td>`?GL_MIN'</td><td>
-%% Rr= min(R s R d) Gr= min(G s G d) Br= min(B s B d)</td><td> Ar= min(A s A d)</td></tr><tr><td>`?GL_MAX'</td><td> Rr=
-%% max(R s R d) Gr= max(G s G d) Br= max(B s B d)</td><td> Ar= max(A s A d)</td></tr></tbody></table>
+%% </td><td> Rr=R s s R+R d d R Gr=G s s G+G d d G Br=B s s B+B d d B</td><td> Ar=A s
+%% s A+A d d A</td></tr><tr><td>`?GL_FUNC_SUBTRACT'</td><td> Rr=R s s R-R d d R Gr=G
+%% s s G-G d d G Br=B s s B-B d d B</td><td> Ar=A s s A-A d d A</td></tr><tr><td>`?GL_FUNC_REVERSE_SUBTRACT'
+%% </td><td> Rr=R d d R-R s s R Gr=G d d G-G s s G Br=B d d B-B s s B</td><td> Ar=A d
+%% d A-A s s A</td></tr><tr><td>`?GL_MIN'</td><td> Rr=min(R s R d) Gr=min(G s G d) Br=min(B s B d)</td><td> Ar=min
+%% (A s A d)</td></tr><tr><td>`?GL_MAX'</td><td> Rr=max(R s R d) Gr=max(G s G d) Br=max(B s B d)</td><td> Ar=max(A s A d)</td></tr></tbody>
+%% </table>
%%
%% The results of these equations are clamped to the range [0 1].
%%
@@ -11626,11 +11628,11 @@ useProgram(Program) ->
%%
%% The commands ``gl:uniformMatrix{2|3|4|2x3|3x2|2x4|4x2|3x4|4x3}fv'' are used to modify
%% a matrix or an array of matrices. The numbers in the command name are interpreted as the
-%% dimensionality of the matrix. The number `2' indicates a 2 � 2 matrix (i.e., 4 values),
-%% the number `3' indicates a 3 � 3 matrix (i.e., 9 values), and the number `4'
-%% indicates a 4 � 4 matrix (i.e., 16 values). Non-square matrix dimensionality is explicit,
+%% dimensionality of the matrix. The number `2' indicates a 2 × 2 matrix (i.e., 4 values),
+%% the number `3' indicates a 3 × 3 matrix (i.e., 9 values), and the number `4'
+%% indicates a 4 × 4 matrix (i.e., 16 values). Non-square matrix dimensionality is explicit,
%% with the first number representing the number of columns and the second number representing
-%% the number of rows. For example, `2x4' indicates a 2 � 4 matrix with 2 columns and
+%% the number of rows. For example, `2x4' indicates a 2 × 4 matrix with 2 columns and
%% 4 rows (i.e., 8 values). If `Transpose' is `?GL_FALSE', each matrix is assumed
%% to be supplied in column major order. If `Transpose' is `?GL_TRUE', each matrix
%% is assumed to be supplied in row major order. The `Count' argument indicates the
@@ -12753,7 +12755,7 @@ drawElementsInstanced(Mode,Count,Type,Indices,Primcount) ->
%%
%% When a buffer object is attached to a buffer texture, the buffer object's data store
%% is taken as the texture's texel array. The number of texels in the buffer texture's texel
-%% array is given by buffer_size components� sizeof( base_type/)
+%% array is given by buffer_size components×sizeof( base_type/)
%%
%% where `buffer_size' is the size of the buffer object, in basic machine units and
%% components and base type are the element count and base data type for elements, as specified
@@ -14576,14 +14578,14 @@ bindSampler(Unit,Sampler) ->
%% to compute the texture value. The other four use mipmaps.
%%
%% A mipmap is an ordered set of arrays representing the same image at progressively lower
-%% resolutions. If the texture has dimensions 2 n*2 m, there are max(n m)+1 mipmaps. The first
-%% mipmap is the original texture, with dimensions 2 n*2 m. Each subsequent mipmap has
-%% dimensions 2(k-1)*2(l-1), where 2 k*2 l are the dimensions of the previous mipmap, until either
-%% k= 0 or l= 0. At that point, subsequent mipmaps have dimension 1*2(l-1) or 2(k-1)*1 until
-%% the final mipmap, which has dimension 1*1. To define the mipmaps, call {@link gl:texImage1D/8}
+%% resolutions. If the texture has dimensions 2 n×2 m, there are max(n m)+1 mipmaps. The first
+%% mipmap is the original texture, with dimensions 2 n×2 m. Each subsequent mipmap has
+%% dimensions 2(k-1)×2(l-1), where 2 k×2 l are the dimensions of the previous mipmap, until either
+%% k=0 or l=0. At that point, subsequent mipmaps have dimension 1×2(l-1) or 2(k-1)×1 until
+%% the final mipmap, which has dimension 1×1. To define the mipmaps, call {@link gl:texImage1D/8}
%% , {@link gl:texImage2D/9} , {@link gl:texImage3D/10} , {@link gl:copyTexImage1D/7} , or {@link gl:copyTexImage2D/8}
%% with the `level' argument indicating the order of the mipmaps. Level 0 is the original
-%% texture; level max(n m) is the final 1*1 mipmap.
+%% texture; level max(n m) is the final 1×1 mipmap.
%%
%% `Params' supplies a function for minifying the texture as one of the following:
%%
@@ -14695,13 +14697,12 @@ bindSampler(Unit,Sampler) ->
%% `?GL_TEXTURE_COMPARE_FUNC': Specifies the comparison operator used when `?GL_TEXTURE_COMPARE_MODE'
%% is set to `?GL_COMPARE_REF_TO_TEXTURE'. Permissible values are: <table><tbody><tr><td>
%% ` Texture Comparison Function '</td><td>` Computed result '</td></tr></tbody><tbody>
-%% <tr><td>`?GL_LEQUAL'</td><td> result={1.0 0.0 &amp;nbsp;&amp;nbsp; r&lt;=(D t) r&gt;(D t))</td></tr><tr><td>`?GL_GEQUAL'</td><td>
-%% result={1.0 0.0 &amp;nbsp;&amp;nbsp; r&gt;=(D t) r&lt;(D t))</td></tr><tr><td>`?GL_LESS'</td><td> result={1.0 0.0 &amp;nbsp;&amp;nbsp; r&lt;
-%% (D t) r&gt;=(D t))</td></tr><tr><td>`?GL_GREATER'
-%% </td><td> result={1.0 0.0 &amp;nbsp;&amp;nbsp; r&gt;(D t) r&lt;=(D t))</td></tr><tr><td>`?GL_EQUAL'</td><td> result={1.0 0.0 &amp;nbsp;&amp;nbsp;
-%% r=(D t) r&amp;ne;(D t))</td></tr><tr><td>`?GL_NOTEQUAL'
-%% </td><td> result={1.0 0.0 &amp;nbsp;&amp;nbsp; r&amp;ne;(D t) r=(D t))</td></tr><tr><td>`?GL_ALWAYS'</td><td> result= 1.0</td></tr><tr><td>
-%% `?GL_NEVER'</td><td> result= 0.0</td></tr></tbody></table> where r is the current
+%% <tr><td>`?GL_LEQUAL'</td><td> result={1.0 0.0 r&lt;=(D t) r&gt;(D t))</td></tr><tr><td>`?GL_GEQUAL'</td><td>
+%% result={1.0 0.0 r&gt;=(D t) r&lt;(D t))</td></tr><tr><td>`?GL_LESS'</td><td> result={1.0 0.0 r&lt;(D t) r&gt;=(D t))</td></tr><tr><td>`?GL_GREATER'
+%% </td><td> result={1.0 0.0 r&gt;(D t) r&lt;=(D t))</td></tr><tr><td>`?GL_EQUAL'</td><td> result={1.0 0.0 r=(D t) r&amp;ne;
+%% (D t))</td></tr><tr><td>`?GL_NOTEQUAL'
+%% </td><td> result={1.0 0.0 r&amp;ne;(D t) r=(D t))</td></tr><tr><td>`?GL_ALWAYS'</td><td> result=1.0</td></tr><tr><td>
+%% `?GL_NEVER'</td><td> result=0.0</td></tr></tbody></table> where r is the current
%% interpolated texture coordinate, and D t is the texture value sampled from the currently
%% bound texture. result is assigned to R t.
%%
@@ -15774,11 +15775,11 @@ getProgramPipelineiv(Pipeline,Pname) ->
%%
%% The commands ``gl:programUniformMatrix{2|3|4|2x3|3x2|2x4|4x2|3x4|4x3}fv'' are used
%% to modify a matrix or an array of matrices. The numbers in the command name are interpreted
-%% as the dimensionality of the matrix. The number `2' indicates a 2 � 2 matrix (i.e.,
-%% 4 values), the number `3' indicates a 3 � 3 matrix (i.e., 9 values), and the number `4'
-%% indicates a 4 � 4 matrix (i.e., 16 values). Non-square matrix dimensionality is explicit,
+%% as the dimensionality of the matrix. The number `2' indicates a 2 × 2 matrix (i.e.,
+%% 4 values), the number `3' indicates a 3 × 3 matrix (i.e., 9 values), and the number `4'
+%% indicates a 4 × 4 matrix (i.e., 16 values). Non-square matrix dimensionality is explicit,
%% with the first number representing the number of columns and the second number representing
-%% the number of rows. For example, `2x4' indicates a 2 � 4 matrix with 2 columns and
+%% the number of rows. For example, `2x4' indicates a 2 × 4 matrix with 2 columns and
%% 4 rows (i.e., 8 values). If `Transpose' is `?GL_FALSE', each matrix is assumed
%% to be supplied in column major order. If `Transpose' is `?GL_TRUE', each matrix
%% is assumed to be supplied in row major order. The `Count' argument indicates the
diff --git a/lib/wx/src/gen/glu.erl b/lib/wx/src/gen/glu.erl
index 2c82c9792f..dc64c3c3a7 100644
--- a/lib/wx/src/gen/glu.erl
+++ b/lib/wx/src/gen/glu.erl
@@ -1,7 +1,9 @@
+%% -*- coding: utf-8 -*-
+
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -91,19 +93,19 @@ tesselate({Nx,Ny,Nz}, Vs) ->
%% ).
%%
%% A series of mipmap levels from `Base' to `Max' is built by decimating `Data'
-%% in half until size 1*1 is reached. At each level, each texel in the halved mipmap
+%% in half until size 1×1 is reached. At each level, each texel in the halved mipmap
%% level is an average of the corresponding two texels in the larger mipmap level. {@link gl:texImage1D/8}
%% is called to load these mipmap levels from `Base' to `Max' . If `Max' is
%% larger than the highest mipmap level for the texture of the specified size, then a GLU
%% error code is returned (see {@link glu:errorString/1} ) and nothing is loaded.
%%
%% For example, if `Level' is 2 and `Width' is 16, the following levels are possible:
-%% 16*1, 8*1, 4*1, 2*1, 1*1. These correspond to levels 2 through 6 respectively.
-%% If `Base' is 3 and `Max' is 5, then only mipmap levels 8*1, 4*1 and 2*1
+%% 16×1, 8×1, 4×1, 2×1, 1×1. These correspond to levels 2 through 6 respectively.
+%% If `Base' is 3 and `Max' is 5, then only mipmap levels 8×1, 4×1 and 2×1
%% are loaded. However, if `Max' is 7, then an error is returned and nothing is loaded
%% since `Max' is larger than the highest mipmap level which is, in this case, 6.
%%
-%% The highest mipmap level can be derived from the formula log 2(width*2 level).
+%% The highest mipmap level can be derived from the formula log 2(width×2 level).
%%
%% See the {@link gl:texImage1D/8} reference page for a description of the acceptable values
%% for `Type' parameter. See the {@link gl:drawPixels/5} reference page for a description
@@ -134,13 +136,13 @@ build1DMipmapLevels(Target,InternalFormat,Width,Format,Type,Level,Base,Max,Data)
%% can fit the requested texture. If not, `Width' is continually halved until it fits.
%%
%% Next, a series of mipmap levels is built by decimating a copy of `Data' in half
-%% until size 1*1 is reached. At each level, each texel in the halved mipmap level is an
+%% until size 1×1 is reached. At each level, each texel in the halved mipmap level is an
%% average of the corresponding two texels in the larger mipmap level.
%%
%% {@link gl:texImage1D/8} is called to load each of these mipmap levels. Level 0 is a copy
%% of `Data' . The highest level is (log 2)(width). For example, if `Width' is 64 and the implementation
-%% can store a texture of this size, the following mipmap levels are built: 64*1, 32*1,
-%% 16*1, 8*1, 4*1, 2*1, and 1*1. These correspond to levels 0 through 6, respectively.
+%% can store a texture of this size, the following mipmap levels are built: 64×1, 32×1,
+%% 16×1, 8×1, 4×1, 2×1, and 1×1. These correspond to levels 0 through 6, respectively.
%%
%%
%% See the {@link gl:texImage1D/8} reference page for a description of the acceptable values
@@ -163,22 +165,22 @@ build1DMipmaps(Target,InternalFormat,Width,Format,Type,Data) ->
%% ).
%%
%% A series of mipmap levels from `Base' to `Max' is built by decimating `Data'
-%% in half along both dimensions until size 1*1 is reached. At each level, each texel
+%% in half along both dimensions until size 1×1 is reached. At each level, each texel
%% in the halved mipmap level is an average of the corresponding four texels in the larger
%% mipmap level. (In the case of rectangular images, the decimation will ultimately reach
-%% an N*1 or 1*N configuration. Here, two texels are averaged instead.) {@link gl:texImage2D/9}
+%% an N×1 or 1×N configuration. Here, two texels are averaged instead.) {@link gl:texImage2D/9}
%% is called to load these mipmap levels from `Base' to `Max' . If `Max' is
%% larger than the highest mipmap level for the texture of the specified size, then a GLU
%% error code is returned (see {@link glu:errorString/1} ) and nothing is loaded.
%%
%% For example, if `Level' is 2 and `Width' is 16 and `Height' is 8, the
-%% following levels are possible: 16*8, 8*4, 4*2, 2*1, 1*1. These correspond to
+%% following levels are possible: 16×8, 8×4, 4×2, 2×1, 1×1. These correspond to
%% levels 2 through 6 respectively. If `Base' is 3 and `Max' is 5, then only mipmap
-%% levels 8*4, 4*2, and 2*1 are loaded. However, if `Max' is 7, then an error is
+%% levels 8×4, 4×2, and 2×1 are loaded. However, if `Max' is 7, then an error is
%% returned and nothing is loaded since `Max' is larger than the highest mipmap level
%% which is, in this case, 6.
%%
-%% The highest mipmap level can be derived from the formula log 2(max(width height)*2 level).
+%% The highest mipmap level can be derived from the formula log 2(max(width height)×2 level).
%%
%% See the {@link gl:texImage1D/8} reference page for a description of the acceptable values
%% for `Format' parameter. See the {@link gl:drawPixels/5} reference page for a description
@@ -214,15 +216,15 @@ build2DMipmapLevels(Target,InternalFormat,Width,Height,Format,Type,Level,Base,Ma
%% .)
%%
%% Next, a series of mipmap levels is built by decimating a copy of `Data' in half
-%% along both dimensions until size 1*1 is reached. At each level, each texel in the halved
+%% along both dimensions until size 1×1 is reached. At each level, each texel in the halved
%% mipmap level is an average of the corresponding four texels in the larger mipmap level.
-%% (In the case of rectangular images, the decimation will ultimately reach an N*1 or 1*N
+%% (In the case of rectangular images, the decimation will ultimately reach an N×1 or 1×N
%% configuration. Here, two texels are averaged instead.)
%%
%% {@link gl:texImage2D/9} is called to load each of these mipmap levels. Level 0 is a copy
%% of `Data' . The highest level is (log 2)(max(width height)). For example, if `Width' is 64 and `Height'
%% is 16 and the implementation can store a texture of this size, the following mipmap levels
-%% are built: 64*16, 32*8, 16*4, 8*2, 4*1, 2*1, and 1*1 These correspond to
+%% are built: 64×16, 32×8, 16×4, 8×2, 4×1, 2×1, and 1×1 These correspond to
%% levels 0 through 6, respectively.
%%
%% See the {@link gl:texImage1D/8} reference page for a description of the acceptable values
@@ -245,7 +247,7 @@ build2DMipmaps(Target,InternalFormat,Width,Height,Format,Type,Data) ->
%% ).
%%
%% A series of mipmap levels from `Base' to `Max' is built by decimating `Data'
-%% in half along both dimensions until size 1*1*1 is reached. At each level, each texel
+%% in half along both dimensions until size 1×1×1 is reached. At each level, each texel
%% in the halved mipmap level is an average of the corresponding eight texels in the larger
%% mipmap level. (If exactly one of the dimensions is 1, four texels are averaged. If exactly
%% two of the dimensions are 1, two texels are averaged.) {@link gl:texImage3D/10} is called
@@ -254,13 +256,13 @@ build2DMipmaps(Target,InternalFormat,Width,Height,Format,Type,Data) ->
%% is returned (see {@link glu:errorString/1} ) and nothing is loaded.
%%
%% For example, if `Level' is 2 and `Width' is 16, `Height' is 8 and `Depth'
-%% is 4, the following levels are possible: 16*8*4, 8*4*2, 4*2*1, 2*1*1, 1*1*1.
+%% is 4, the following levels are possible: 16×8×4, 8×4×2, 4×2×1, 2×1×1, 1×1×1.
%% These correspond to levels 2 through 6 respectively. If `Base' is 3 and `Max'
-%% is 5, then only mipmap levels 8*4*2, 4*2*1, and 2*1*1 are loaded. However, if `Max'
+%% is 5, then only mipmap levels 8×4×2, 4×2×1, and 2×1×1 are loaded. However, if `Max'
%% is 7, then an error is returned and nothing is loaded, since `Max' is larger than
%% the highest mipmap level which is, in this case, 6.
%%
-%% The highest mipmap level can be derived from the formula log 2(max(width height depth)*2 level).
+%% The highest mipmap level can be derived from the formula log 2(max(width height depth)×2 level).
%%
%% See the {@link gl:texImage1D/8} reference page for a description of the acceptable values
%% for `Format' parameter. See the {@link gl:drawPixels/5} reference page for a description
@@ -295,7 +297,7 @@ build3DMipmapLevels(Target,InternalFormat,Width,Height,Depth,Format,Type,Level,B
%% it fits.
%%
%% Next, a series of mipmap levels is built by decimating a copy of `Data' in half
-%% along all three dimensions until size 1*1*1 is reached. At each level, each texel in
+%% along all three dimensions until size 1×1×1 is reached. At each level, each texel in
%% the halved mipmap level is an average of the corresponding eight texels in the larger
%% mipmap level. (If exactly one of the dimensions is 1, four texels are averaged. If exactly
%% two of the dimensions are 1, two texels are averaged.)
@@ -303,8 +305,8 @@ build3DMipmapLevels(Target,InternalFormat,Width,Height,Depth,Format,Type,Level,B
%% {@link gl:texImage3D/10} is called to load each of these mipmap levels. Level 0 is a copy
%% of `Data' . The highest level is (log 2)(max(width height depth)). For example, if `Width' is 64, `Height'
%% is 16, and `Depth' is 32, and the implementation can store a texture of this size,
-%% the following mipmap levels are built: 64*16*32, 32*8*16, 16*4*8, 8*2*4, 4*1*2,
-%% 2*1*1, and 1*1*1. These correspond to levels 0 through 6, respectively.
+%% the following mipmap levels are built: 64×16×32, 32×8×16, 16×4×8, 8×2×4, 4×1×2,
+%% 2×1×1, and 1×1×1. These correspond to levels 0 through 6, respectively.
%%
%% See the {@link gl:texImage1D/8} reference page for a description of the acceptable values
%% for `Format' parameter. See the {@link gl:drawPixels/5} reference page for a description
@@ -334,7 +336,7 @@ checkExtension(ExtName,ExtString) ->
%% @doc Draw a cylinder
%%
%% ``glu:cylinder'' draws a cylinder oriented along the `z' axis. The base of the
-%% cylinder is placed at `z' = 0 and the top at z= height. Like a sphere, a cylinder
+%% cylinder is placed at `z' = 0 and the top at z=height. Like a sphere, a cylinder
%% is subdivided around the `z' axis into slices and along the `z' axis into stacks.
%%
%%
@@ -380,7 +382,7 @@ deleteQuadric(Quad) ->
%% the -`z' axis.
%%
%% If texturing has been turned on (with {@link glu:quadricTexture/2} ), texture coordinates
-%% are generated linearly such that where r= outer, the value at (`r', 0, 0) is (1,
+%% are generated linearly such that where r=outer, the value at (`r', 0, 0) is (1,
%% 0.5), at (0, `r', 0) it is (0.5, 1), at (-`r', 0, 0) it is (0, 0.5), and at
%% (0, -`r', 0) it is (0.5, 0).
%%
@@ -451,11 +453,11 @@ getString(Name) ->
%%
%% Let `UP' be the vector (upX upY upZ).
%%
-%% Then normalize as follows: f= F/(||F||)
+%% Then normalize as follows: f=F/(||F||)
%%
-%% UP"= UP/(||UP||)
+%% UP"=UP/(||UP||)
%%
-%% Finally, let s= f*UP", and u= s*f.
+%% Finally, let s=f×UP", and u=s×f.
%%
%% M is then constructed as follows: M=(s[0] s[1] s[2] 0 u[0] u[1] u[2] 0-f[0]-f[1]-f[2] 0 0 0 0 1)
%%
@@ -481,7 +483,7 @@ newQuadric() ->
%% @doc Define a 2D orthographic projection matrix
%%
%% ``glu:ortho2D'' sets up a two-dimensional orthographic viewing region. This is equivalent
-%% to calling {@link gl:ortho/6} with near= -1 and far= 1.
+%% to calling {@link gl:ortho/6} with near=-1 and far=1.
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/gluOrtho2D.xml">external</a> documentation.
-spec ortho2D(Left, Right, Bottom, Top) -> ok when Left :: float(),Right :: float(),Bottom :: float(),Top :: float().
@@ -490,7 +492,7 @@ ortho2D(Left,Right,Bottom,Top) ->
%% @doc Draw an arc of a disk
%%
-%% ``glu:partialDisk'' renders a partial disk on the z= 0 plane. A partial disk is similar
+%% ``glu:partialDisk'' renders a partial disk on the z=0 plane. A partial disk is similar
%% to a full disk, except that only the subset of the disk from `Start' through `Start'
%% + `Sweep' is included (where 0 degrees is along the +f2yf axis, 90 degrees along
%% the +`x' axis, 180 degrees along the -`y' axis, and 270 degrees along the -`x'
@@ -508,7 +510,7 @@ ortho2D(Left,Right,Bottom,Top) ->
%% Otherwise, they point along the -`z' axis.
%%
%% If texturing is turned on (with {@link glu:quadricTexture/2} ), texture coordinates are
-%% generated linearly such that where r= outer, the value at (`r', 0, 0) is (1.0,
+%% generated linearly such that where r=outer, the value at (`r', 0, 0) is (1.0,
%% 0.5), at (0, `r', 0) it is (0.5, 1.0), at (-`r', 0, 0) it is (0.0, 0.5), and
%% at (0, -`r', 0) it is (0.5, 0.0).
%%
@@ -521,7 +523,7 @@ partialDisk(Quad,Inner,Outer,Slices,Loops,Start,Sweep) ->
%%
%% ``glu:perspective'' specifies a viewing frustum into the world coordinate system. In
%% general, the aspect ratio in ``glu:perspective'' should match the aspect ratio of the
-%% associated viewport. For example, aspect= 2.0 means the viewer's angle of view is twice
+%% associated viewport. For example, aspect=2.0 means the viewer's angle of view is twice
%% as wide in `x' as it is in `y'. If the viewport is twice as wide as it is tall,
%% it displays the image without distortion.
%%
@@ -532,9 +534,9 @@ partialDisk(Quad,Inner,Outer,Slices,Loops,Start,Sweep) ->
%%
%% Given `f' defined as follows:
%%
-%% f= cotangent(fovy/2) The generated matrix is
+%% f=cotangent(fovy/2) The generated matrix is
%%
-%% (f/aspect 0 0 0 0 f 0 0 0 0(zFar+zNear)/(zNear-zFar)(2*zFar*zNear)/(zNear-zFar) 0 0 -1 0)
+%% (f/aspect 0 0 0 0 f 0 0 0 0(zFar+zNear)/(zNear-zFar)(2×zFar×zNear)/(zNear-zFar) 0 0 -1 0)
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/gluPerspective.xml">external</a> documentation.
-spec perspective(Fovy, Aspect, ZNear, ZFar) -> ok when Fovy :: float(),Aspect :: float(),ZNear :: float(),ZFar :: float().
@@ -577,16 +579,16 @@ pickMatrix(X,Y,DelX,DelY,{V1,V2,V3,V4}) ->
%% To compute the coordinates, let v=(objX objY objZ 1.0) represented as a matrix with 4 rows and 1 column.
%% Then ``glu:project'' computes v" as follows:
%%
-%% v"= P*M*v
+%% v"=P×M×v
%%
%% where P is the current projection matrix `Proj' and M is the current modelview
-%% matrix `Model' (both represented as 4*4 matrices in column-major order).
+%% matrix `Model' (both represented as 4×4 matrices in column-major order).
%%
%% The window coordinates are then computed as follows:
%%
-%% winX= view(0)+view(2)*(v"(0)+1)/2
+%% winX=view(0)+view(2)×(v"(0)+1)/2
%%
-%% winY= view(1)+view(3)*(v"(1)+1)/2
+%% winY=view(1)+view(3)×(v"(1)+1)/2
%%
%% winZ=(v"(2)+1)/2
%%
@@ -703,7 +705,7 @@ scaleImage(Format,WIn,HIn,TypeIn,DataIn,WOut,HOut,TypeOut,DataOut) ->
%% point toward the center of the sphere.
%%
%% If texturing is turned on (with {@link glu:quadricTexture/2} ), then texture coordinates
-%% are generated so that `t' ranges from 0.0 at z=-radius to 1.0 at z= radius (`t'
+%% are generated so that `t' ranges from 0.0 at z=-radius to 1.0 at z=radius (`t'
%% increases linearly along longitudinal lines), and `s' ranges from 0.0 at the +`y'
%% axis, to 0.25 at the +`x' axis, to 0.5 at the -`y' axis, to 0.75 at the -`x'
%% axis, and back to 1.0 at the +`y' axis.
@@ -723,7 +725,7 @@ sphere(Quad,Radius,Slices,Stacks) ->
%% To compute the coordinates (objX objY objZ), ``glu:unProject'' multiplies the normalized device coordinates
%% by the inverse of `Model' * `Proj' as follows:
%%
-%% (objX objY objZ W)= INV(P M) ((2(winX-view[0]))/(view[2])-1(2(winY-view[1]))/(view[3])-1 2(winZ)-1 1) INV denotes matrix inversion. W is an unused variable, included for consistent
+%% (objX objY objZ W)=INV(P M) ((2(winX-view[0]))/(view[2])-1(2(winY-view[1]))/(view[3])-1 2(winZ)-1 1) INV denotes matrix inversion. W is an unused variable, included for consistent
%% matrix notation.
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/gluUnProject.xml">external</a> documentation.
diff --git a/lib/wx/src/gen/wxCalendarCtrl.erl b/lib/wx/src/gen/wxCalendarCtrl.erl
index 01d171800b..2a476c5e92 100644
--- a/lib/wx/src/gen/wxCalendarCtrl.erl
+++ b/lib/wx/src/gen/wxCalendarCtrl.erl
@@ -75,6 +75,8 @@
update/1,updateWindowUI/1,updateWindowUI/2,validate/1,warpPointer/3]).
-export_type([wxCalendarCtrl/0]).
+-deprecated([enableYearChange/1,enableYearChange/2]).
+
%% @hidden
parent_class(wxControl) -> true;
parent_class(wxWindow) -> true;
diff --git a/lib/wx/src/gen/wxClientDC.erl b/lib/wx/src/gen/wxClientDC.erl
index 7dfebe4068..cb75fdc58d 100644
--- a/lib/wx/src/gen/wxClientDC.erl
+++ b/lib/wx/src/gen/wxClientDC.erl
@@ -54,6 +54,8 @@
startPage/1]).
-export_type([wxClientDC/0]).
+-deprecated([new/0]).
+
%% @hidden
parent_class(wxWindowDC) -> true;
parent_class(wxDC) -> true;
diff --git a/lib/wx/src/gen/wxClipboard.erl b/lib/wx/src/gen/wxClipboard.erl
index 8f1d59f603..c7336fcc47 100644
--- a/lib/wx/src/gen/wxClipboard.erl
+++ b/lib/wx/src/gen/wxClipboard.erl
@@ -130,8 +130,9 @@ usePrimarySelection(#wx_ref{type=ThisT,ref=ThisRef}, Options)
<<ThisRef:32/?UI, 0:32,BinOpt/binary>>).
%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxclipboard.html#wxclipboardissupported">external documentation</a>.
+%%<br /> Format = ?wxDF_INVALID | ?wxDF_TEXT | ?wxDF_BITMAP | ?wxDF_METAFILE | ?wxDF_SYLK | ?wxDF_DIF | ?wxDF_TIFF | ?wxDF_OEMTEXT | ?wxDF_DIB | ?wxDF_PALETTE | ?wxDF_PENDATA | ?wxDF_RIFF | ?wxDF_WAVE | ?wxDF_UNICODETEXT | ?wxDF_ENHMETAFILE | ?wxDF_FILENAME | ?wxDF_LOCALE | ?wxDF_PRIVATE | ?wxDF_HTML | ?wxDF_MAX
-spec isSupported(This, Format) -> boolean() when
- This::wxClipboard(), Format::integer().
+ This::wxClipboard(), Format::wx:wx_enum().
isSupported(#wx_ref{type=ThisT,ref=ThisRef},Format)
when is_integer(Format) ->
?CLASS(ThisT,wxClipboard),
diff --git a/lib/wx/src/gen/wxCursor.erl b/lib/wx/src/gen/wxCursor.erl
index b9e3a8e3f7..423e444f2f 100644
--- a/lib/wx/src/gen/wxCursor.erl
+++ b/lib/wx/src/gen/wxCursor.erl
@@ -35,6 +35,8 @@
saveFile/4,setDepth/2,setHeight/2,setMask/2,setPalette/2,setWidth/2]).
-export_type([wxCursor/0]).
+-deprecated([new/3,new/4]).
+
%% @hidden
parent_class(wxBitmap) -> true;
parent_class(_Class) -> erlang:error({badtype, ?MODULE}).
diff --git a/lib/wx/src/gen/wxDC.erl b/lib/wx/src/gen/wxDC.erl
index 9f57978849..42d5d7b1df 100644
--- a/lib/wx/src/gen/wxDC.erl
+++ b/lib/wx/src/gen/wxDC.erl
@@ -50,6 +50,8 @@
-export([parent_class/1]).
-export_type([wxDC/0]).
+-deprecated([computeScaleAndOrigin/1]).
+
%% @hidden
parent_class(_Class) -> erlang:error({badtype, ?MODULE}).
@@ -63,9 +65,10 @@ blit(This,DestPt={DestPtX,DestPtY},Sz={SzW,SzH},Source,SrcPt={SrcPtX,SrcPtY})
blit(This,DestPt,Sz,Source,SrcPt, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxdc.html#wxdcblit">external documentation</a>.
+%%<br /> Rop = integer
-spec blit(This, DestPt, Sz, Source, SrcPt, [Option]) -> boolean() when
This::wxDC(), DestPt::{X::integer(), Y::integer()}, Sz::{W::integer(), H::integer()}, Source::wxDC(), SrcPt::{X::integer(), Y::integer()},
- Option :: {rop, integer()}
+ Option :: {rop, wx:wx_enum()}
| {useMask, boolean()}
| {srcPtMask, {X::integer(), Y::integer()}}.
blit(#wx_ref{type=ThisT,ref=ThisRef},{DestPtX,DestPtY},{SzW,SzH},#wx_ref{type=SourceT,ref=SourceRef},{SrcPtX,SrcPtY}, Options)
@@ -310,11 +313,12 @@ drawPolygon(This,Points)
drawPolygon(This,Points, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxdc.html#wxdcdrawpolygon">external documentation</a>.
+%%<br /> FillStyle = integer
-spec drawPolygon(This, Points, [Option]) -> ok when
This::wxDC(), Points::[{X::integer(), Y::integer()}],
Option :: {xoffset, integer()}
| {yoffset, integer()}
- | {fillStyle, integer()}.
+ | {fillStyle, wx:wx_enum()}.
drawPolygon(#wx_ref{type=ThisT,ref=ThisRef},Points, Options)
when is_list(Points),is_list(Options) ->
?CLASS(ThisT,wxDC),
@@ -417,9 +421,10 @@ floodFill(This,Pt={PtX,PtY},Col)
floodFill(This,Pt,Col, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxdc.html#wxdcfloodfill">external documentation</a>.
+%%<br /> Style = integer
-spec floodFill(This, Pt, Col, [Option]) -> boolean() when
This::wxDC(), Pt::{X::integer(), Y::integer()}, Col::wx:wx_colour(),
- Option :: {style, integer()}.
+ Option :: {style, wx:wx_enum()}.
floodFill(#wx_ref{type=ThisT,ref=ThisRef},{PtX,PtY},Col, Options)
when is_integer(PtX),is_integer(PtY),tuple_size(Col) =:= 3; tuple_size(Col) =:= 4,is_list(Options) ->
?CLASS(ThisT,wxDC),
@@ -855,8 +860,9 @@ setLayoutDirection(#wx_ref{type=ThisT,ref=ThisRef},Dir)
<<ThisRef:32/?UI,Dir:32/?UI>>).
%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxdc.html#wxdcsetlogicalfunction">external documentation</a>.
+%%<br /> Function = integer
-spec setLogicalFunction(This, Function) -> ok when
- This::wxDC(), Function::integer().
+ This::wxDC(), Function::wx:wx_enum().
setLogicalFunction(#wx_ref{type=ThisT,ref=ThisRef},Function)
when is_integer(Function) ->
?CLASS(ThisT,wxDC),
@@ -864,8 +870,9 @@ setLogicalFunction(#wx_ref{type=ThisT,ref=ThisRef},Function)
<<ThisRef:32/?UI,Function:32/?UI>>).
%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxdc.html#wxdcsetmapmode">external documentation</a>.
+%%<br /> Mode = integer
-spec setMapMode(This, Mode) -> ok when
- This::wxDC(), Mode::integer().
+ This::wxDC(), Mode::wx:wx_enum().
setMapMode(#wx_ref{type=ThisT,ref=ThisRef},Mode)
when is_integer(Mode) ->
?CLASS(ThisT,wxDC),
diff --git a/lib/wx/src/gen/wxEvtHandler.erl b/lib/wx/src/gen/wxEvtHandler.erl
index cf4a72da5a..22c203392c 100644
--- a/lib/wx/src/gen/wxEvtHandler.erl
+++ b/lib/wx/src/gen/wxEvtHandler.erl
@@ -80,7 +80,7 @@ connect(This, EventType) ->
%% {userData, term()} An erlang term that will be sent with the event. Default: [].
-spec connect(This::wxEvtHandler(), EventType::wxEventType(), [Option]) -> ok when
Option :: {id, integer()} | {lastId, integer()} | {skip, boolean()} |
- {callback, function()} | {userData, term()}.
+ callback | {callback, function()} | {userData, term()}.
connect(This=#wx_ref{type=ThisT}, EventType, Options) ->
EvH = parse_opts(Options, #evh{et=EventType}),
?CLASS(ThisT,wxEvtHandler),
diff --git a/lib/wx/src/gen/wxGraphicsContext.erl b/lib/wx/src/gen/wxGraphicsContext.erl
index 1dfa0dd405..575e48d7af 100644
--- a/lib/wx/src/gen/wxGraphicsContext.erl
+++ b/lib/wx/src/gen/wxGraphicsContext.erl
@@ -40,6 +40,8 @@
-export([getRenderer/1,isNull/1,parent_class/1]).
-export_type([wxGraphicsContext/0]).
+-deprecated([createLinearGradientBrush/7,createRadialGradientBrush/8]).
+
%% @hidden
parent_class(wxGraphicsObject) -> true;
parent_class(_Class) -> erlang:error({badtype, ?MODULE}).
@@ -225,9 +227,10 @@ drawLines(This,Points)
drawLines(This,Points, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxgraphicscontext.html#wxgraphicscontextdrawlines">external documentation</a>.
+%%<br /> FillStyle = integer
-spec drawLines(This, Points, [Option]) -> ok when
This::wxGraphicsContext(), Points::[{X::float(), Y::float()}],
- Option :: {fillStyle, integer()}.
+ Option :: {fillStyle, wx:wx_enum()}.
drawLines(#wx_ref{type=ThisT,ref=ThisRef},Points, Options)
when is_list(Points),is_list(Options) ->
?CLASS(ThisT,wxGraphicsContext),
@@ -247,9 +250,10 @@ drawPath(This,Path)
drawPath(This,Path, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxgraphicscontext.html#wxgraphicscontextdrawpath">external documentation</a>.
+%%<br /> FillStyle = integer
-spec drawPath(This, Path, [Option]) -> ok when
This::wxGraphicsContext(), Path::wxGraphicsPath:wxGraphicsPath(),
- Option :: {fillStyle, integer()}.
+ Option :: {fillStyle, wx:wx_enum()}.
drawPath(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=PathT,ref=PathRef}, Options)
when is_list(Options) ->
?CLASS(ThisT,wxGraphicsContext),
@@ -331,9 +335,10 @@ fillPath(This,Path)
fillPath(This,Path, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxgraphicscontext.html#wxgraphicscontextfillpath">external documentation</a>.
+%%<br /> FillStyle = integer
-spec fillPath(This, Path, [Option]) -> ok when
This::wxGraphicsContext(), Path::wxGraphicsPath:wxGraphicsPath(),
- Option :: {fillStyle, integer()}.
+ Option :: {fillStyle, wx:wx_enum()}.
fillPath(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=PathT,ref=PathRef}, Options)
when is_list(Options) ->
?CLASS(ThisT,wxGraphicsContext),
diff --git a/lib/wx/src/gen/wxGraphicsPath.erl b/lib/wx/src/gen/wxGraphicsPath.erl
index 56b853899a..246ea489ec 100644
--- a/lib/wx/src/gen/wxGraphicsPath.erl
+++ b/lib/wx/src/gen/wxGraphicsPath.erl
@@ -197,13 +197,14 @@ contains(This,C={CX,CY})
%% <br /> Also:<br />
%% contains(This, C, [Option]) -> boolean() when<br />
%% This::wxGraphicsPath(), C::{X::float(), Y::float()},<br />
-%% Option :: {fillStyle, integer()}.<br />
+%% Option :: {fillStyle, wx:wx_enum()}.<br />
%%
+%%<br /> FillStyle = integer
-spec contains(This, X, Y) -> boolean() when
This::wxGraphicsPath(), X::number(), Y::number();
(This, C, [Option]) -> boolean() when
This::wxGraphicsPath(), C::{X::float(), Y::float()},
- Option :: {fillStyle, integer()}.
+ Option :: {fillStyle, wx:wx_enum()}.
contains(This,X,Y)
when is_record(This, wx_ref),is_number(X),is_number(Y) ->
@@ -218,9 +219,10 @@ contains(#wx_ref{type=ThisT,ref=ThisRef},{CX,CY}, Options)
<<ThisRef:32/?UI,0:32,CX:64/?F,CY:64/?F, BinOpt/binary>>).
%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxgraphicspath.html#wxgraphicspathcontains">external documentation</a>.
+%%<br /> FillStyle = integer
-spec contains(This, X, Y, [Option]) -> boolean() when
This::wxGraphicsPath(), X::number(), Y::number(),
- Option :: {fillStyle, integer()}.
+ Option :: {fillStyle, wx:wx_enum()}.
contains(#wx_ref{type=ThisT,ref=ThisRef},X,Y, Options)
when is_number(X),is_number(Y),is_list(Options) ->
?CLASS(ThisT,wxGraphicsPath),
diff --git a/lib/wx/src/gen/wxGraphicsRenderer.erl b/lib/wx/src/gen/wxGraphicsRenderer.erl
index 21082bde23..2b64f86182 100644
--- a/lib/wx/src/gen/wxGraphicsRenderer.erl
+++ b/lib/wx/src/gen/wxGraphicsRenderer.erl
@@ -32,6 +32,8 @@
-export([parent_class/1]).
-export_type([wxGraphicsRenderer/0]).
+-deprecated([createLinearGradientBrush/7,createRadialGradientBrush/8]).
+
%% @hidden
parent_class(_Class) -> erlang:error({badtype, ?MODULE}).
diff --git a/lib/wx/src/gen/wxGridCellEditor.erl b/lib/wx/src/gen/wxGridCellEditor.erl
index e84cdeb49a..4f86e307b5 100644
--- a/lib/wx/src/gen/wxGridCellEditor.erl
+++ b/lib/wx/src/gen/wxGridCellEditor.erl
@@ -31,6 +31,8 @@
-export([parent_class/1]).
-export_type([wxGridCellEditor/0]).
+-deprecated([endEdit/4,paintBackground/3]).
+
%% @hidden
parent_class(_Class) -> erlang:error({badtype, ?MODULE}).
diff --git a/lib/wx/src/gen/wxIdleEvent.erl b/lib/wx/src/gen/wxIdleEvent.erl
index 4749026446..a19fdcc48e 100644
--- a/lib/wx/src/gen/wxIdleEvent.erl
+++ b/lib/wx/src/gen/wxIdleEvent.erl
@@ -38,6 +38,8 @@
resumePropagation/2,shouldPropagate/1,skip/1,skip/2,stopPropagation/1]).
-export_type([wxIdleEvent/0]).
+-deprecated([canSend/1]).
+
%% @hidden
parent_class(wxEvent) -> true;
parent_class(_Class) -> erlang:error({badtype, ?MODULE}).
diff --git a/lib/wx/src/gen/wxImage.erl b/lib/wx/src/gen/wxImage.erl
index c21b5d4789..0edaee2979 100644
--- a/lib/wx/src/gen/wxImage.erl
+++ b/lib/wx/src/gen/wxImage.erl
@@ -424,9 +424,10 @@ getImageCount(Name)
getImageCount(Name, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wximage.html#wximagegetimagecount">external documentation</a>.
+%%<br /> Type = ?wxBITMAP_TYPE_INVALID | ?wxBITMAP_TYPE_BMP | ?wxBITMAP_TYPE_BMP_RESOURCE | ?wxBITMAP_TYPE_RESOURCE | ?wxBITMAP_TYPE_ICO | ?wxBITMAP_TYPE_ICO_RESOURCE | ?wxBITMAP_TYPE_CUR | ?wxBITMAP_TYPE_CUR_RESOURCE | ?wxBITMAP_TYPE_XBM | ?wxBITMAP_TYPE_XBM_DATA | ?wxBITMAP_TYPE_XPM | ?wxBITMAP_TYPE_XPM_DATA | ?wxBITMAP_TYPE_TIF | ?wxBITMAP_TYPE_TIF_RESOURCE | ?wxBITMAP_TYPE_GIF | ?wxBITMAP_TYPE_GIF_RESOURCE | ?wxBITMAP_TYPE_PNG | ?wxBITMAP_TYPE_PNG_RESOURCE | ?wxBITMAP_TYPE_JPEG | ?wxBITMAP_TYPE_JPEG_RESOURCE | ?wxBITMAP_TYPE_PNM | ?wxBITMAP_TYPE_PNM_RESOURCE | ?wxBITMAP_TYPE_PCX | ?wxBITMAP_TYPE_PCX_RESOURCE | ?wxBITMAP_TYPE_PICT | ?wxBITMAP_TYPE_PICT_RESOURCE | ?wxBITMAP_TYPE_ICON | ?wxBITMAP_TYPE_ICON_RESOURCE | ?wxBITMAP_TYPE_ANI | ?wxBITMAP_TYPE_IFF | ?wxBITMAP_TYPE_TGA | ?wxBITMAP_TYPE_MACCURSOR | ?wxBITMAP_TYPE_MACCURSOR_RESOURCE | ?wxBITMAP_TYPE_ANY
-spec getImageCount(Name, [Option]) -> integer() when
Name::unicode:chardata(),
- Option :: {type, integer()}.
+ Option :: {type, wx:wx_enum()}.
getImageCount(Name, Options)
when is_list(Name),is_list(Options) ->
Name_UC = unicode:characters_to_binary([Name,0]),
@@ -687,9 +688,10 @@ rescale(This,Width,Height)
rescale(This,Width,Height, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wximage.html#wximagerescale">external documentation</a>.
+%%<br /> Quality = integer
-spec rescale(This, Width, Height, [Option]) -> wxImage() when
This::wxImage(), Width::integer(), Height::integer(),
- Option :: {quality, integer()}.
+ Option :: {quality, wx:wx_enum()}.
rescale(#wx_ref{type=ThisT,ref=ThisRef},Width,Height, Options)
when is_integer(Width),is_integer(Height),is_list(Options) ->
?CLASS(ThisT,wxImage),
@@ -819,9 +821,10 @@ scale(This,Width,Height)
scale(This,Width,Height, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wximage.html#wximagescale">external documentation</a>.
+%%<br /> Quality = integer
-spec scale(This, Width, Height, [Option]) -> wxImage() when
This::wxImage(), Width::integer(), Height::integer(),
- Option :: {quality, integer()}.
+ Option :: {quality, wx:wx_enum()}.
scale(#wx_ref{type=ThisT,ref=ThisRef},Width,Height, Options)
when is_integer(Width),is_integer(Height),is_list(Options) ->
?CLASS(ThisT,wxImage),
diff --git a/lib/wx/src/gen/wxMDIClientWindow.erl b/lib/wx/src/gen/wxMDIClientWindow.erl
index 344dcdbbaf..bfdba336f8 100644
--- a/lib/wx/src/gen/wxMDIClientWindow.erl
+++ b/lib/wx/src/gen/wxMDIClientWindow.erl
@@ -69,6 +69,8 @@
update/1,updateWindowUI/1,updateWindowUI/2,validate/1,warpPointer/3]).
-export_type([wxMDIClientWindow/0]).
+-deprecated([new/1,new/2]).
+
%% @hidden
parent_class(wxWindow) -> true;
parent_class(wxEvtHandler) -> true;
diff --git a/lib/wx/src/gen/wxMouseEvent.erl b/lib/wx/src/gen/wxMouseEvent.erl
index 97ea9e908f..29a4f13ba8 100644
--- a/lib/wx/src/gen/wxMouseEvent.erl
+++ b/lib/wx/src/gen/wxMouseEvent.erl
@@ -19,7 +19,7 @@
%% @doc See external documentation: <a href="http://www.wxwidgets.org/manuals/stable/wx_wxmouseevent.html">wxMouseEvent</a>.
%% <dl><dt>Use {@link wxEvtHandler:connect/3.} with EventType:</dt>
-%% <dd><em>left_down</em>, <em>left_up</em>, <em>middle_down</em>, <em>middle_up</em>, <em>right_down</em>, <em>right_up</em>, <em>motion</em>, <em>enter_window</em>, <em>leave_window</em>, <em>left_dclick</em>, <em>middle_dclick</em>, <em>right_dclick</em>, <em>mousewheel</em>, <em>nc_left_down</em>, <em>nc_left_up</em>, <em>nc_middle_down</em>, <em>nc_middle_up</em>, <em>nc_right_down</em>, <em>nc_right_up</em>, <em>nc_motion</em>, <em>nc_enter_window</em>, <em>nc_leave_window</em>, <em>nc_left_dclick</em>, <em>nc_middle_dclick</em>, <em>nc_right_dclick</em></dd></dl>
+%% <dd><em>left_down</em>, <em>left_up</em>, <em>middle_down</em>, <em>middle_up</em>, <em>right_down</em>, <em>right_up</em>, <em>motion</em>, <em>enter_window</em>, <em>leave_window</em>, <em>left_dclick</em>, <em>middle_dclick</em>, <em>right_dclick</em>, <em>mousewheel</em></dd></dl>
%% See also the message variant {@link wxEvtHandler:wxMouse(). #wxMouse{}} event record type.
%%
%% <p>This class is derived (and can use functions) from:
diff --git a/lib/wx/src/gen/wxPageSetupDialogData.erl b/lib/wx/src/gen/wxPageSetupDialogData.erl
index 7c453a9872..4850e62925 100644
--- a/lib/wx/src/gen/wxPageSetupDialogData.erl
+++ b/lib/wx/src/gen/wxPageSetupDialogData.erl
@@ -194,7 +194,8 @@ getMinMarginBottomRight(#wx_ref{type=ThisT,ref=ThisRef}) ->
<<ThisRef:32/?UI>>).
%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxpagesetupdialogdata.html#wxpagesetupdialogdatagetpaperid">external documentation</a>.
--spec getPaperId(This) -> integer() when
+%%<br /> Res = ?wxPAPER_NONE | ?wxPAPER_LETTER | ?wxPAPER_LEGAL | ?wxPAPER_A4 | ?wxPAPER_CSHEET | ?wxPAPER_DSHEET | ?wxPAPER_ESHEET | ?wxPAPER_LETTERSMALL | ?wxPAPER_TABLOID | ?wxPAPER_LEDGER | ?wxPAPER_STATEMENT | ?wxPAPER_EXECUTIVE | ?wxPAPER_A3 | ?wxPAPER_A4SMALL | ?wxPAPER_A5 | ?wxPAPER_B4 | ?wxPAPER_B5 | ?wxPAPER_FOLIO | ?wxPAPER_QUARTO | ?wxPAPER_10X14 | ?wxPAPER_11X17 | ?wxPAPER_NOTE | ?wxPAPER_ENV_9 | ?wxPAPER_ENV_10 | ?wxPAPER_ENV_11 | ?wxPAPER_ENV_12 | ?wxPAPER_ENV_14 | ?wxPAPER_ENV_DL | ?wxPAPER_ENV_C5 | ?wxPAPER_ENV_C3 | ?wxPAPER_ENV_C4 | ?wxPAPER_ENV_C6 | ?wxPAPER_ENV_C65 | ?wxPAPER_ENV_B4 | ?wxPAPER_ENV_B5 | ?wxPAPER_ENV_B6 | ?wxPAPER_ENV_ITALY | ?wxPAPER_ENV_MONARCH | ?wxPAPER_ENV_PERSONAL | ?wxPAPER_FANFOLD_US | ?wxPAPER_FANFOLD_STD_GERMAN | ?wxPAPER_FANFOLD_LGL_GERMAN | ?wxPAPER_ISO_B4 | ?wxPAPER_JAPANESE_POSTCARD | ?wxPAPER_9X11 | ?wxPAPER_10X11 | ?wxPAPER_15X11 | ?wxPAPER_ENV_INVITE | ?wxPAPER_LETTER_EXTRA | ?wxPAPER_LEGAL_EXTRA | ?wxPAPER_TABLOID_EXTRA | ?wxPAPER_A4_EXTRA | ?wxPAPER_LETTER_TRANSVERSE | ?wxPAPER_A4_TRANSVERSE | ?wxPAPER_LETTER_EXTRA_TRANSVERSE | ?wxPAPER_A_PLUS | ?wxPAPER_B_PLUS | ?wxPAPER_LETTER_PLUS | ?wxPAPER_A4_PLUS | ?wxPAPER_A5_TRANSVERSE | ?wxPAPER_B5_TRANSVERSE | ?wxPAPER_A3_EXTRA | ?wxPAPER_A5_EXTRA | ?wxPAPER_B5_EXTRA | ?wxPAPER_A2 | ?wxPAPER_A3_TRANSVERSE | ?wxPAPER_A3_EXTRA_TRANSVERSE | ?wxPAPER_DBL_JAPANESE_POSTCARD | ?wxPAPER_A6 | ?wxPAPER_JENV_KAKU2 | ?wxPAPER_JENV_KAKU3 | ?wxPAPER_JENV_CHOU3 | ?wxPAPER_JENV_CHOU4 | ?wxPAPER_LETTER_ROTATED | ?wxPAPER_A3_ROTATED | ?wxPAPER_A4_ROTATED | ?wxPAPER_A5_ROTATED | ?wxPAPER_B4_JIS_ROTATED | ?wxPAPER_B5_JIS_ROTATED | ?wxPAPER_JAPANESE_POSTCARD_ROTATED | ?wxPAPER_DBL_JAPANESE_POSTCARD_ROTATED | ?wxPAPER_A6_ROTATED | ?wxPAPER_JENV_KAKU2_ROTATED | ?wxPAPER_JENV_KAKU3_ROTATED | ?wxPAPER_JENV_CHOU3_ROTATED | ?wxPAPER_JENV_CHOU4_ROTATED | ?wxPAPER_B6_JIS | ?wxPAPER_B6_JIS_ROTATED | ?wxPAPER_12X11 | ?wxPAPER_JENV_YOU4 | ?wxPAPER_JENV_YOU4_ROTATED | ?wxPAPER_P16K | ?wxPAPER_P32K | ?wxPAPER_P32KBIG | ?wxPAPER_PENV_1 | ?wxPAPER_PENV_2 | ?wxPAPER_PENV_3 | ?wxPAPER_PENV_4 | ?wxPAPER_PENV_5 | ?wxPAPER_PENV_6 | ?wxPAPER_PENV_7 | ?wxPAPER_PENV_8 | ?wxPAPER_PENV_9 | ?wxPAPER_PENV_10 | ?wxPAPER_P16K_ROTATED | ?wxPAPER_P32K_ROTATED | ?wxPAPER_P32KBIG_ROTATED | ?wxPAPER_PENV_1_ROTATED | ?wxPAPER_PENV_2_ROTATED | ?wxPAPER_PENV_3_ROTATED | ?wxPAPER_PENV_4_ROTATED | ?wxPAPER_PENV_5_ROTATED | ?wxPAPER_PENV_6_ROTATED | ?wxPAPER_PENV_7_ROTATED | ?wxPAPER_PENV_8_ROTATED | ?wxPAPER_PENV_9_ROTATED | ?wxPAPER_PENV_10_ROTATED
+-spec getPaperId(This) -> wx:wx_enum() when
This::wxPageSetupDialogData().
getPaperId(#wx_ref{type=ThisT,ref=ThisRef}) ->
?CLASS(ThisT,wxPageSetupDialogData),
@@ -280,8 +281,9 @@ setMinMarginBottomRight(#wx_ref{type=ThisT,ref=ThisRef},{PtX,PtY})
<<ThisRef:32/?UI,PtX:32/?UI,PtY:32/?UI>>).
%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxpagesetupdialogdata.html#wxpagesetupdialogdatasetpaperid">external documentation</a>.
+%%<br /> Id = ?wxPAPER_NONE | ?wxPAPER_LETTER | ?wxPAPER_LEGAL | ?wxPAPER_A4 | ?wxPAPER_CSHEET | ?wxPAPER_DSHEET | ?wxPAPER_ESHEET | ?wxPAPER_LETTERSMALL | ?wxPAPER_TABLOID | ?wxPAPER_LEDGER | ?wxPAPER_STATEMENT | ?wxPAPER_EXECUTIVE | ?wxPAPER_A3 | ?wxPAPER_A4SMALL | ?wxPAPER_A5 | ?wxPAPER_B4 | ?wxPAPER_B5 | ?wxPAPER_FOLIO | ?wxPAPER_QUARTO | ?wxPAPER_10X14 | ?wxPAPER_11X17 | ?wxPAPER_NOTE | ?wxPAPER_ENV_9 | ?wxPAPER_ENV_10 | ?wxPAPER_ENV_11 | ?wxPAPER_ENV_12 | ?wxPAPER_ENV_14 | ?wxPAPER_ENV_DL | ?wxPAPER_ENV_C5 | ?wxPAPER_ENV_C3 | ?wxPAPER_ENV_C4 | ?wxPAPER_ENV_C6 | ?wxPAPER_ENV_C65 | ?wxPAPER_ENV_B4 | ?wxPAPER_ENV_B5 | ?wxPAPER_ENV_B6 | ?wxPAPER_ENV_ITALY | ?wxPAPER_ENV_MONARCH | ?wxPAPER_ENV_PERSONAL | ?wxPAPER_FANFOLD_US | ?wxPAPER_FANFOLD_STD_GERMAN | ?wxPAPER_FANFOLD_LGL_GERMAN | ?wxPAPER_ISO_B4 | ?wxPAPER_JAPANESE_POSTCARD | ?wxPAPER_9X11 | ?wxPAPER_10X11 | ?wxPAPER_15X11 | ?wxPAPER_ENV_INVITE | ?wxPAPER_LETTER_EXTRA | ?wxPAPER_LEGAL_EXTRA | ?wxPAPER_TABLOID_EXTRA | ?wxPAPER_A4_EXTRA | ?wxPAPER_LETTER_TRANSVERSE | ?wxPAPER_A4_TRANSVERSE | ?wxPAPER_LETTER_EXTRA_TRANSVERSE | ?wxPAPER_A_PLUS | ?wxPAPER_B_PLUS | ?wxPAPER_LETTER_PLUS | ?wxPAPER_A4_PLUS | ?wxPAPER_A5_TRANSVERSE | ?wxPAPER_B5_TRANSVERSE | ?wxPAPER_A3_EXTRA | ?wxPAPER_A5_EXTRA | ?wxPAPER_B5_EXTRA | ?wxPAPER_A2 | ?wxPAPER_A3_TRANSVERSE | ?wxPAPER_A3_EXTRA_TRANSVERSE | ?wxPAPER_DBL_JAPANESE_POSTCARD | ?wxPAPER_A6 | ?wxPAPER_JENV_KAKU2 | ?wxPAPER_JENV_KAKU3 | ?wxPAPER_JENV_CHOU3 | ?wxPAPER_JENV_CHOU4 | ?wxPAPER_LETTER_ROTATED | ?wxPAPER_A3_ROTATED | ?wxPAPER_A4_ROTATED | ?wxPAPER_A5_ROTATED | ?wxPAPER_B4_JIS_ROTATED | ?wxPAPER_B5_JIS_ROTATED | ?wxPAPER_JAPANESE_POSTCARD_ROTATED | ?wxPAPER_DBL_JAPANESE_POSTCARD_ROTATED | ?wxPAPER_A6_ROTATED | ?wxPAPER_JENV_KAKU2_ROTATED | ?wxPAPER_JENV_KAKU3_ROTATED | ?wxPAPER_JENV_CHOU3_ROTATED | ?wxPAPER_JENV_CHOU4_ROTATED | ?wxPAPER_B6_JIS | ?wxPAPER_B6_JIS_ROTATED | ?wxPAPER_12X11 | ?wxPAPER_JENV_YOU4 | ?wxPAPER_JENV_YOU4_ROTATED | ?wxPAPER_P16K | ?wxPAPER_P32K | ?wxPAPER_P32KBIG | ?wxPAPER_PENV_1 | ?wxPAPER_PENV_2 | ?wxPAPER_PENV_3 | ?wxPAPER_PENV_4 | ?wxPAPER_PENV_5 | ?wxPAPER_PENV_6 | ?wxPAPER_PENV_7 | ?wxPAPER_PENV_8 | ?wxPAPER_PENV_9 | ?wxPAPER_PENV_10 | ?wxPAPER_P16K_ROTATED | ?wxPAPER_P32K_ROTATED | ?wxPAPER_P32KBIG_ROTATED | ?wxPAPER_PENV_1_ROTATED | ?wxPAPER_PENV_2_ROTATED | ?wxPAPER_PENV_3_ROTATED | ?wxPAPER_PENV_4_ROTATED | ?wxPAPER_PENV_5_ROTATED | ?wxPAPER_PENV_6_ROTATED | ?wxPAPER_PENV_7_ROTATED | ?wxPAPER_PENV_8_ROTATED | ?wxPAPER_PENV_9_ROTATED | ?wxPAPER_PENV_10_ROTATED
-spec setPaperId(This, Id) -> ok when
- This::wxPageSetupDialogData(), Id::integer().
+ This::wxPageSetupDialogData(), Id::wx:wx_enum().
setPaperId(#wx_ref{type=ThisT,ref=ThisRef},Id)
when is_integer(Id) ->
?CLASS(ThisT,wxPageSetupDialogData),
@@ -293,8 +295,9 @@ setPaperId(#wx_ref{type=ThisT,ref=ThisRef},Id)
%% setPaperSize(This, Sz) -> ok when<br />
%% This::wxPageSetupDialogData(), Sz::{W::integer(), H::integer()}.<br />
%%
+%%<br /> Id = ?wxPAPER_NONE | ?wxPAPER_LETTER | ?wxPAPER_LEGAL | ?wxPAPER_A4 | ?wxPAPER_CSHEET | ?wxPAPER_DSHEET | ?wxPAPER_ESHEET | ?wxPAPER_LETTERSMALL | ?wxPAPER_TABLOID | ?wxPAPER_LEDGER | ?wxPAPER_STATEMENT | ?wxPAPER_EXECUTIVE | ?wxPAPER_A3 | ?wxPAPER_A4SMALL | ?wxPAPER_A5 | ?wxPAPER_B4 | ?wxPAPER_B5 | ?wxPAPER_FOLIO | ?wxPAPER_QUARTO | ?wxPAPER_10X14 | ?wxPAPER_11X17 | ?wxPAPER_NOTE | ?wxPAPER_ENV_9 | ?wxPAPER_ENV_10 | ?wxPAPER_ENV_11 | ?wxPAPER_ENV_12 | ?wxPAPER_ENV_14 | ?wxPAPER_ENV_DL | ?wxPAPER_ENV_C5 | ?wxPAPER_ENV_C3 | ?wxPAPER_ENV_C4 | ?wxPAPER_ENV_C6 | ?wxPAPER_ENV_C65 | ?wxPAPER_ENV_B4 | ?wxPAPER_ENV_B5 | ?wxPAPER_ENV_B6 | ?wxPAPER_ENV_ITALY | ?wxPAPER_ENV_MONARCH | ?wxPAPER_ENV_PERSONAL | ?wxPAPER_FANFOLD_US | ?wxPAPER_FANFOLD_STD_GERMAN | ?wxPAPER_FANFOLD_LGL_GERMAN | ?wxPAPER_ISO_B4 | ?wxPAPER_JAPANESE_POSTCARD | ?wxPAPER_9X11 | ?wxPAPER_10X11 | ?wxPAPER_15X11 | ?wxPAPER_ENV_INVITE | ?wxPAPER_LETTER_EXTRA | ?wxPAPER_LEGAL_EXTRA | ?wxPAPER_TABLOID_EXTRA | ?wxPAPER_A4_EXTRA | ?wxPAPER_LETTER_TRANSVERSE | ?wxPAPER_A4_TRANSVERSE | ?wxPAPER_LETTER_EXTRA_TRANSVERSE | ?wxPAPER_A_PLUS | ?wxPAPER_B_PLUS | ?wxPAPER_LETTER_PLUS | ?wxPAPER_A4_PLUS | ?wxPAPER_A5_TRANSVERSE | ?wxPAPER_B5_TRANSVERSE | ?wxPAPER_A3_EXTRA | ?wxPAPER_A5_EXTRA | ?wxPAPER_B5_EXTRA | ?wxPAPER_A2 | ?wxPAPER_A3_TRANSVERSE | ?wxPAPER_A3_EXTRA_TRANSVERSE | ?wxPAPER_DBL_JAPANESE_POSTCARD | ?wxPAPER_A6 | ?wxPAPER_JENV_KAKU2 | ?wxPAPER_JENV_KAKU3 | ?wxPAPER_JENV_CHOU3 | ?wxPAPER_JENV_CHOU4 | ?wxPAPER_LETTER_ROTATED | ?wxPAPER_A3_ROTATED | ?wxPAPER_A4_ROTATED | ?wxPAPER_A5_ROTATED | ?wxPAPER_B4_JIS_ROTATED | ?wxPAPER_B5_JIS_ROTATED | ?wxPAPER_JAPANESE_POSTCARD_ROTATED | ?wxPAPER_DBL_JAPANESE_POSTCARD_ROTATED | ?wxPAPER_A6_ROTATED | ?wxPAPER_JENV_KAKU2_ROTATED | ?wxPAPER_JENV_KAKU3_ROTATED | ?wxPAPER_JENV_CHOU3_ROTATED | ?wxPAPER_JENV_CHOU4_ROTATED | ?wxPAPER_B6_JIS | ?wxPAPER_B6_JIS_ROTATED | ?wxPAPER_12X11 | ?wxPAPER_JENV_YOU4 | ?wxPAPER_JENV_YOU4_ROTATED | ?wxPAPER_P16K | ?wxPAPER_P32K | ?wxPAPER_P32KBIG | ?wxPAPER_PENV_1 | ?wxPAPER_PENV_2 | ?wxPAPER_PENV_3 | ?wxPAPER_PENV_4 | ?wxPAPER_PENV_5 | ?wxPAPER_PENV_6 | ?wxPAPER_PENV_7 | ?wxPAPER_PENV_8 | ?wxPAPER_PENV_9 | ?wxPAPER_PENV_10 | ?wxPAPER_P16K_ROTATED | ?wxPAPER_P32K_ROTATED | ?wxPAPER_P32KBIG_ROTATED | ?wxPAPER_PENV_1_ROTATED | ?wxPAPER_PENV_2_ROTATED | ?wxPAPER_PENV_3_ROTATED | ?wxPAPER_PENV_4_ROTATED | ?wxPAPER_PENV_5_ROTATED | ?wxPAPER_PENV_6_ROTATED | ?wxPAPER_PENV_7_ROTATED | ?wxPAPER_PENV_8_ROTATED | ?wxPAPER_PENV_9_ROTATED | ?wxPAPER_PENV_10_ROTATED
-spec setPaperSize(This, Id) -> ok when
- This::wxPageSetupDialogData(), Id::integer();
+ This::wxPageSetupDialogData(), Id::wx:wx_enum();
(This, Sz) -> ok when
This::wxPageSetupDialogData(), Sz::{W::integer(), H::integer()}.
setPaperSize(#wx_ref{type=ThisT,ref=ThisRef},Id)
diff --git a/lib/wx/src/gen/wxPaintDC.erl b/lib/wx/src/gen/wxPaintDC.erl
index 9e81bc31af..6648f278bb 100644
--- a/lib/wx/src/gen/wxPaintDC.erl
+++ b/lib/wx/src/gen/wxPaintDC.erl
@@ -54,6 +54,8 @@
startPage/1]).
-export_type([wxPaintDC/0]).
+-deprecated([new/0]).
+
%% @hidden
parent_class(wxWindowDC) -> true;
parent_class(wxDC) -> true;
diff --git a/lib/wx/src/gen/wxPaintEvent.erl b/lib/wx/src/gen/wxPaintEvent.erl
index a13db607a4..80ac7d78ce 100644
--- a/lib/wx/src/gen/wxPaintEvent.erl
+++ b/lib/wx/src/gen/wxPaintEvent.erl
@@ -19,7 +19,7 @@
%% @doc See external documentation: <a href="http://www.wxwidgets.org/manuals/stable/wx_wxpaintevent.html">wxPaintEvent</a>.
%% <dl><dt>Use {@link wxEvtHandler:connect/3.} with EventType:</dt>
-%% <dd><em>paint</em>, <em>paint_icon</em></dd></dl>
+%% <dd><em>paint</em></dd></dl>
%% See also the message variant {@link wxEvtHandler:wxPaint(). #wxPaint{}} event record type.
%%
%% <p>This class is derived (and can use functions) from:
diff --git a/lib/wx/src/gen/wxPen.erl b/lib/wx/src/gen/wxPen.erl
index 8b8aafddba..681a7edebc 100644
--- a/lib/wx/src/gen/wxPen.erl
+++ b/lib/wx/src/gen/wxPen.erl
@@ -113,8 +113,9 @@ isOk(#wx_ref{type=ThisT,ref=ThisRef}) ->
<<ThisRef:32/?UI>>).
%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxpen.html#wxpensetcap">external documentation</a>.
+%%<br /> CapStyle = integer
-spec setCap(This, CapStyle) -> ok when
- This::wxPen(), CapStyle::integer().
+ This::wxPen(), CapStyle::wx:wx_enum().
setCap(#wx_ref{type=ThisT,ref=ThisRef},CapStyle)
when is_integer(CapStyle) ->
?CLASS(ThisT,wxPen),
@@ -140,8 +141,9 @@ setColour(#wx_ref{type=ThisT,ref=ThisRef},Red,Green,Blue)
<<ThisRef:32/?UI,Red:32/?UI,Green:32/?UI,Blue:32/?UI>>).
%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxpen.html#wxpensetjoin">external documentation</a>.
+%%<br /> JoinStyle = integer
-spec setJoin(This, JoinStyle) -> ok when
- This::wxPen(), JoinStyle::integer().
+ This::wxPen(), JoinStyle::wx:wx_enum().
setJoin(#wx_ref{type=ThisT,ref=ThisRef},JoinStyle)
when is_integer(JoinStyle) ->
?CLASS(ThisT,wxPen),
diff --git a/lib/wx/src/gen/wxPostScriptDC.erl b/lib/wx/src/gen/wxPostScriptDC.erl
index 2eb25b6a8e..5329d4562e 100644
--- a/lib/wx/src/gen/wxPostScriptDC.erl
+++ b/lib/wx/src/gen/wxPostScriptDC.erl
@@ -53,6 +53,8 @@
startPage/1]).
-export_type([wxPostScriptDC/0]).
+-deprecated([getResolution/0,setResolution/1]).
+
%% @hidden
parent_class(wxDC) -> true;
parent_class(_Class) -> erlang:error({badtype, ?MODULE}).
diff --git a/lib/wx/src/gen/wxPrintData.erl b/lib/wx/src/gen/wxPrintData.erl
index 2cb8dd3c5f..79ba26afab 100644
--- a/lib/wx/src/gen/wxPrintData.erl
+++ b/lib/wx/src/gen/wxPrintData.erl
@@ -102,7 +102,8 @@ getOrientation(#wx_ref{type=ThisT,ref=ThisRef}) ->
<<ThisRef:32/?UI>>).
%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxprintdata.html#wxprintdatagetpaperid">external documentation</a>.
--spec getPaperId(This) -> integer() when
+%%<br /> Res = ?wxPAPER_NONE | ?wxPAPER_LETTER | ?wxPAPER_LEGAL | ?wxPAPER_A4 | ?wxPAPER_CSHEET | ?wxPAPER_DSHEET | ?wxPAPER_ESHEET | ?wxPAPER_LETTERSMALL | ?wxPAPER_TABLOID | ?wxPAPER_LEDGER | ?wxPAPER_STATEMENT | ?wxPAPER_EXECUTIVE | ?wxPAPER_A3 | ?wxPAPER_A4SMALL | ?wxPAPER_A5 | ?wxPAPER_B4 | ?wxPAPER_B5 | ?wxPAPER_FOLIO | ?wxPAPER_QUARTO | ?wxPAPER_10X14 | ?wxPAPER_11X17 | ?wxPAPER_NOTE | ?wxPAPER_ENV_9 | ?wxPAPER_ENV_10 | ?wxPAPER_ENV_11 | ?wxPAPER_ENV_12 | ?wxPAPER_ENV_14 | ?wxPAPER_ENV_DL | ?wxPAPER_ENV_C5 | ?wxPAPER_ENV_C3 | ?wxPAPER_ENV_C4 | ?wxPAPER_ENV_C6 | ?wxPAPER_ENV_C65 | ?wxPAPER_ENV_B4 | ?wxPAPER_ENV_B5 | ?wxPAPER_ENV_B6 | ?wxPAPER_ENV_ITALY | ?wxPAPER_ENV_MONARCH | ?wxPAPER_ENV_PERSONAL | ?wxPAPER_FANFOLD_US | ?wxPAPER_FANFOLD_STD_GERMAN | ?wxPAPER_FANFOLD_LGL_GERMAN | ?wxPAPER_ISO_B4 | ?wxPAPER_JAPANESE_POSTCARD | ?wxPAPER_9X11 | ?wxPAPER_10X11 | ?wxPAPER_15X11 | ?wxPAPER_ENV_INVITE | ?wxPAPER_LETTER_EXTRA | ?wxPAPER_LEGAL_EXTRA | ?wxPAPER_TABLOID_EXTRA | ?wxPAPER_A4_EXTRA | ?wxPAPER_LETTER_TRANSVERSE | ?wxPAPER_A4_TRANSVERSE | ?wxPAPER_LETTER_EXTRA_TRANSVERSE | ?wxPAPER_A_PLUS | ?wxPAPER_B_PLUS | ?wxPAPER_LETTER_PLUS | ?wxPAPER_A4_PLUS | ?wxPAPER_A5_TRANSVERSE | ?wxPAPER_B5_TRANSVERSE | ?wxPAPER_A3_EXTRA | ?wxPAPER_A5_EXTRA | ?wxPAPER_B5_EXTRA | ?wxPAPER_A2 | ?wxPAPER_A3_TRANSVERSE | ?wxPAPER_A3_EXTRA_TRANSVERSE | ?wxPAPER_DBL_JAPANESE_POSTCARD | ?wxPAPER_A6 | ?wxPAPER_JENV_KAKU2 | ?wxPAPER_JENV_KAKU3 | ?wxPAPER_JENV_CHOU3 | ?wxPAPER_JENV_CHOU4 | ?wxPAPER_LETTER_ROTATED | ?wxPAPER_A3_ROTATED | ?wxPAPER_A4_ROTATED | ?wxPAPER_A5_ROTATED | ?wxPAPER_B4_JIS_ROTATED | ?wxPAPER_B5_JIS_ROTATED | ?wxPAPER_JAPANESE_POSTCARD_ROTATED | ?wxPAPER_DBL_JAPANESE_POSTCARD_ROTATED | ?wxPAPER_A6_ROTATED | ?wxPAPER_JENV_KAKU2_ROTATED | ?wxPAPER_JENV_KAKU3_ROTATED | ?wxPAPER_JENV_CHOU3_ROTATED | ?wxPAPER_JENV_CHOU4_ROTATED | ?wxPAPER_B6_JIS | ?wxPAPER_B6_JIS_ROTATED | ?wxPAPER_12X11 | ?wxPAPER_JENV_YOU4 | ?wxPAPER_JENV_YOU4_ROTATED | ?wxPAPER_P16K | ?wxPAPER_P32K | ?wxPAPER_P32KBIG | ?wxPAPER_PENV_1 | ?wxPAPER_PENV_2 | ?wxPAPER_PENV_3 | ?wxPAPER_PENV_4 | ?wxPAPER_PENV_5 | ?wxPAPER_PENV_6 | ?wxPAPER_PENV_7 | ?wxPAPER_PENV_8 | ?wxPAPER_PENV_9 | ?wxPAPER_PENV_10 | ?wxPAPER_P16K_ROTATED | ?wxPAPER_P32K_ROTATED | ?wxPAPER_P32KBIG_ROTATED | ?wxPAPER_PENV_1_ROTATED | ?wxPAPER_PENV_2_ROTATED | ?wxPAPER_PENV_3_ROTATED | ?wxPAPER_PENV_4_ROTATED | ?wxPAPER_PENV_5_ROTATED | ?wxPAPER_PENV_6_ROTATED | ?wxPAPER_PENV_7_ROTATED | ?wxPAPER_PENV_8_ROTATED | ?wxPAPER_PENV_9_ROTATED | ?wxPAPER_PENV_10_ROTATED
+-spec getPaperId(This) -> wx:wx_enum() when
This::wxPrintData().
getPaperId(#wx_ref{type=ThisT,ref=ThisRef}) ->
?CLASS(ThisT,wxPrintData),
@@ -190,8 +191,9 @@ setOrientation(#wx_ref{type=ThisT,ref=ThisRef},Orient)
<<ThisRef:32/?UI,Orient:32/?UI>>).
%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxprintdata.html#wxprintdatasetpaperid">external documentation</a>.
+%%<br /> SizeId = ?wxPAPER_NONE | ?wxPAPER_LETTER | ?wxPAPER_LEGAL | ?wxPAPER_A4 | ?wxPAPER_CSHEET | ?wxPAPER_DSHEET | ?wxPAPER_ESHEET | ?wxPAPER_LETTERSMALL | ?wxPAPER_TABLOID | ?wxPAPER_LEDGER | ?wxPAPER_STATEMENT | ?wxPAPER_EXECUTIVE | ?wxPAPER_A3 | ?wxPAPER_A4SMALL | ?wxPAPER_A5 | ?wxPAPER_B4 | ?wxPAPER_B5 | ?wxPAPER_FOLIO | ?wxPAPER_QUARTO | ?wxPAPER_10X14 | ?wxPAPER_11X17 | ?wxPAPER_NOTE | ?wxPAPER_ENV_9 | ?wxPAPER_ENV_10 | ?wxPAPER_ENV_11 | ?wxPAPER_ENV_12 | ?wxPAPER_ENV_14 | ?wxPAPER_ENV_DL | ?wxPAPER_ENV_C5 | ?wxPAPER_ENV_C3 | ?wxPAPER_ENV_C4 | ?wxPAPER_ENV_C6 | ?wxPAPER_ENV_C65 | ?wxPAPER_ENV_B4 | ?wxPAPER_ENV_B5 | ?wxPAPER_ENV_B6 | ?wxPAPER_ENV_ITALY | ?wxPAPER_ENV_MONARCH | ?wxPAPER_ENV_PERSONAL | ?wxPAPER_FANFOLD_US | ?wxPAPER_FANFOLD_STD_GERMAN | ?wxPAPER_FANFOLD_LGL_GERMAN | ?wxPAPER_ISO_B4 | ?wxPAPER_JAPANESE_POSTCARD | ?wxPAPER_9X11 | ?wxPAPER_10X11 | ?wxPAPER_15X11 | ?wxPAPER_ENV_INVITE | ?wxPAPER_LETTER_EXTRA | ?wxPAPER_LEGAL_EXTRA | ?wxPAPER_TABLOID_EXTRA | ?wxPAPER_A4_EXTRA | ?wxPAPER_LETTER_TRANSVERSE | ?wxPAPER_A4_TRANSVERSE | ?wxPAPER_LETTER_EXTRA_TRANSVERSE | ?wxPAPER_A_PLUS | ?wxPAPER_B_PLUS | ?wxPAPER_LETTER_PLUS | ?wxPAPER_A4_PLUS | ?wxPAPER_A5_TRANSVERSE | ?wxPAPER_B5_TRANSVERSE | ?wxPAPER_A3_EXTRA | ?wxPAPER_A5_EXTRA | ?wxPAPER_B5_EXTRA | ?wxPAPER_A2 | ?wxPAPER_A3_TRANSVERSE | ?wxPAPER_A3_EXTRA_TRANSVERSE | ?wxPAPER_DBL_JAPANESE_POSTCARD | ?wxPAPER_A6 | ?wxPAPER_JENV_KAKU2 | ?wxPAPER_JENV_KAKU3 | ?wxPAPER_JENV_CHOU3 | ?wxPAPER_JENV_CHOU4 | ?wxPAPER_LETTER_ROTATED | ?wxPAPER_A3_ROTATED | ?wxPAPER_A4_ROTATED | ?wxPAPER_A5_ROTATED | ?wxPAPER_B4_JIS_ROTATED | ?wxPAPER_B5_JIS_ROTATED | ?wxPAPER_JAPANESE_POSTCARD_ROTATED | ?wxPAPER_DBL_JAPANESE_POSTCARD_ROTATED | ?wxPAPER_A6_ROTATED | ?wxPAPER_JENV_KAKU2_ROTATED | ?wxPAPER_JENV_KAKU3_ROTATED | ?wxPAPER_JENV_CHOU3_ROTATED | ?wxPAPER_JENV_CHOU4_ROTATED | ?wxPAPER_B6_JIS | ?wxPAPER_B6_JIS_ROTATED | ?wxPAPER_12X11 | ?wxPAPER_JENV_YOU4 | ?wxPAPER_JENV_YOU4_ROTATED | ?wxPAPER_P16K | ?wxPAPER_P32K | ?wxPAPER_P32KBIG | ?wxPAPER_PENV_1 | ?wxPAPER_PENV_2 | ?wxPAPER_PENV_3 | ?wxPAPER_PENV_4 | ?wxPAPER_PENV_5 | ?wxPAPER_PENV_6 | ?wxPAPER_PENV_7 | ?wxPAPER_PENV_8 | ?wxPAPER_PENV_9 | ?wxPAPER_PENV_10 | ?wxPAPER_P16K_ROTATED | ?wxPAPER_P32K_ROTATED | ?wxPAPER_P32KBIG_ROTATED | ?wxPAPER_PENV_1_ROTATED | ?wxPAPER_PENV_2_ROTATED | ?wxPAPER_PENV_3_ROTATED | ?wxPAPER_PENV_4_ROTATED | ?wxPAPER_PENV_5_ROTATED | ?wxPAPER_PENV_6_ROTATED | ?wxPAPER_PENV_7_ROTATED | ?wxPAPER_PENV_8_ROTATED | ?wxPAPER_PENV_9_ROTATED | ?wxPAPER_PENV_10_ROTATED
-spec setPaperId(This, SizeId) -> ok when
- This::wxPrintData(), SizeId::integer().
+ This::wxPrintData(), SizeId::wx:wx_enum().
setPaperId(#wx_ref{type=ThisT,ref=ThisRef},SizeId)
when is_integer(SizeId) ->
?CLASS(ThisT,wxPrintData),
diff --git a/lib/wx/src/gen/wxPrintout.erl b/lib/wx/src/gen/wxPrintout.erl
index ab96a09c09..c75edd2b5a 100644
--- a/lib/wx/src/gen/wxPrintout.erl
+++ b/lib/wx/src/gen/wxPrintout.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -61,7 +61,8 @@ new(Title, OnPrintPage) ->
%% <pre>OnBeginDocument(This,StartPage,EndPage) -> boolean() </pre>
%% <pre>OnEndDocument(This) -> term() </pre>
%% <pre>HasPage(This,Page)} -> boolean() </pre>
-%% <pre>GetPageInfo(This) -> {MinPage:.integer(), MaxPage::integer(), PageFrom::integer(), PageTo::integer()} </pre>
+%% <pre>GetPageInfo(This) -> {MinPage::integer(), MaxPage::integer(),
+%% PageFrom::integer(), PageTo::integer()} </pre>
%% The <b>This</b> argument is the wxPrintout object reference to this object
%% <br /> NOTE: The callbacks may not call other processes.
new(Title, OnPrintPage, Opts) when is_list(Title), is_function(OnPrintPage), is_list(Opts) ->
diff --git a/lib/wx/src/gen/wxStyledTextCtrl.erl b/lib/wx/src/gen/wxStyledTextCtrl.erl
index f6dc2176b7..55ac410407 100644
--- a/lib/wx/src/gen/wxStyledTextCtrl.erl
+++ b/lib/wx/src/gen/wxStyledTextCtrl.erl
@@ -95,18 +95,18 @@
redo/1,registerImage/3,replaceSelection/2,replaceTarget/2,saveFile/2,
scrollToColumn/2,scrollToLine/2,searchAnchor/1,searchInTarget/2,searchNext/3,
searchPrev/3,selectAll/1,selectionDuplicate/1,selectionIsRectangle/1,
- sendMsg/2,sendMsg/3,setAnchor/2,setBackSpaceUnIndents/2,setBufferedDraw/2,
- setCaretForeground/2,setCaretLineBackAlpha/2,setCaretLineBackground/2,
- setCaretLineVisible/2,setCaretPeriod/2,setCaretSticky/2,setCaretWidth/2,
- setCharsDefault/1,setCodePage/2,setControlCharSymbol/2,setCurrentPos/2,
- setEOLMode/2,setEdgeColour/2,setEdgeColumn/2,setEdgeMode/2,setFoldExpanded/3,
- setFoldFlags/2,setFoldLevel/3,setFoldMarginColour/3,setFoldMarginHiColour/3,
- setHScrollBar/2,setHighlightGuide/2,setHotspotActiveBackground/3,
- setHotspotActiveForeground/3,setHotspotActiveUnderline/2,setHotspotSingleLine/2,
- setIndent/2,setIndentationGuides/2,setKeyWords/3,setLastKeydownProcessed/2,
- setLayoutCache/2,setLexer/2,setLexerLanguage/2,setLineIndentation/3,
- setLineState/3,setMarginLeft/2,setMarginMask/3,setMarginRight/2,setMarginSensitive/3,
- setMarginType/3,setMarginWidth/3,setMargins/3,setModEventMask/2,setMouseDownCaptures/2,
+ setAnchor/2,setBackSpaceUnIndents/2,setBufferedDraw/2,setCaretForeground/2,
+ setCaretLineBackAlpha/2,setCaretLineBackground/2,setCaretLineVisible/2,
+ setCaretPeriod/2,setCaretSticky/2,setCaretWidth/2,setCharsDefault/1,
+ setCodePage/2,setControlCharSymbol/2,setCurrentPos/2,setEOLMode/2,
+ setEdgeColour/2,setEdgeColumn/2,setEdgeMode/2,setFoldExpanded/3,setFoldFlags/2,
+ setFoldLevel/3,setFoldMarginColour/3,setFoldMarginHiColour/3,setHScrollBar/2,
+ setHighlightGuide/2,setHotspotActiveBackground/3,setHotspotActiveForeground/3,
+ setHotspotActiveUnderline/2,setHotspotSingleLine/2,setIndent/2,setIndentationGuides/2,
+ setKeyWords/3,setLastKeydownProcessed/2,setLayoutCache/2,setLexer/2,
+ setLexerLanguage/2,setLineIndentation/3,setLineState/3,setMarginLeft/2,
+ setMarginMask/3,setMarginRight/2,setMarginSensitive/3,setMarginType/3,
+ setMarginWidth/3,setMargins/3,setModEventMask/2,setMouseDownCaptures/2,
setMouseDwellTime/2,setPasteConvertEndings/2,setPrintColourMode/2,
setPrintMagnification/2,setProperty/3,setReadOnly/2,setSTCCursor/2,
setSTCFocus/2,setSavePoint/1,setScrollWidth/2,setSearchFlags/2,setSelAlpha/2,
@@ -3897,29 +3897,6 @@ scrollToColumn(#wx_ref{type=ThisT,ref=ThisRef},Column)
wxe_util:cast(?wxStyledTextCtrl_ScrollToColumn,
<<ThisRef:32/?UI,Column:32/?UI>>).
-%% @equiv sendMsg(This,Msg, [])
--spec sendMsg(This, Msg) -> integer() when
- This::wxStyledTextCtrl(), Msg::integer().
-
-sendMsg(This,Msg)
- when is_record(This, wx_ref),is_integer(Msg) ->
- sendMsg(This,Msg, []).
-
-%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxstyledtextctrl.html#wxstyledtextctrlsendmsg">external documentation</a>.
--spec sendMsg(This, Msg, [Option]) -> integer() when
- This::wxStyledTextCtrl(), Msg::integer(),
- Option :: {wp, integer()}
- | {lp, integer()}.
-sendMsg(#wx_ref{type=ThisT,ref=ThisRef},Msg, Options)
- when is_integer(Msg),is_list(Options) ->
- ?CLASS(ThisT,wxStyledTextCtrl),
- MOpts = fun({wp, Wp}, Acc) -> [<<1:32/?UI,Wp:32/?UI>>|Acc];
- ({lp, Lp}, Acc) -> [<<2:32/?UI,Lp:32/?UI>>|Acc];
- (BadOpt, _) -> erlang:error({badoption, BadOpt}) end,
- BinOpt = list_to_binary(lists:foldl(MOpts, [<<0:32>>], Options)),
- wxe_util:call(?wxStyledTextCtrl_SendMsg,
- <<ThisRef:32/?UI,Msg:32/?UI, BinOpt/binary>>).
-
%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxstyledtextctrl.html#wxstyledtextctrlsetvscrollbar">external documentation</a>.
-spec setVScrollBar(This, Bar) -> ok when
This::wxStyledTextCtrl(), Bar::wxScrollBar:wxScrollBar().
diff --git a/lib/wx/src/gen/wxSystemSettings.erl b/lib/wx/src/gen/wxSystemSettings.erl
index a3bae5c85f..630162afd2 100644
--- a/lib/wx/src/gen/wxSystemSettings.erl
+++ b/lib/wx/src/gen/wxSystemSettings.erl
@@ -35,7 +35,7 @@ parent_class(_Class) -> erlang:error({badtype, ?MODULE}).
-type wxSystemSettings() :: wx:wx_object().
%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxsystemsettings.html#wxsystemsettingsgetcolour">external documentation</a>.
-%%<br /> Index = ?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
+%%<br /> Index = ?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_LISTBOXHIGHLIGHTTEXT | ?wxSYS_COLOUR_MAX
-spec getColour(Index) -> wx:wx_colour4() when
Index::wx:wx_enum().
getColour(Index)
diff --git a/lib/wx/src/gen/wxWindowDC.erl b/lib/wx/src/gen/wxWindowDC.erl
index babb3c0e90..4515f0e6b9 100644
--- a/lib/wx/src/gen/wxWindowDC.erl
+++ b/lib/wx/src/gen/wxWindowDC.erl
@@ -53,6 +53,8 @@
startPage/1]).
-export_type([wxWindowDC/0]).
+-deprecated([new/0]).
+
%% @hidden
parent_class(wxDC) -> true;
parent_class(_Class) -> erlang:error({badtype, ?MODULE}).
diff --git a/lib/wx/src/gen/wxe_debug.hrl b/lib/wx/src/gen/wxe_debug.hrl
index 297d99324a..6f4fa3fe34 100644
--- a/lib/wx/src/gen/wxe_debug.hrl
+++ b/lib/wx/src/gen/wxe_debug.hrl
@@ -3154,156 +3154,155 @@ wxdebug_table() ->
{3370, {wxStyledTextCtrl, pointFromPosition, 1}},
{3371, {wxStyledTextCtrl, scrollToLine, 1}},
{3372, {wxStyledTextCtrl, scrollToColumn, 1}},
- {3373, {wxStyledTextCtrl, sendMsg, 2}},
- {3374, {wxStyledTextCtrl, setVScrollBar, 1}},
- {3375, {wxStyledTextCtrl, setHScrollBar, 1}},
- {3376, {wxStyledTextCtrl, getLastKeydownProcessed, 0}},
- {3377, {wxStyledTextCtrl, setLastKeydownProcessed, 1}},
- {3378, {wxStyledTextCtrl, saveFile, 1}},
- {3379, {wxStyledTextCtrl, loadFile, 1}},
- {3380, {wxStyledTextCtrl, doDragOver, 3}},
- {3381, {wxStyledTextCtrl, doDropText, 3}},
- {3382, {wxStyledTextCtrl, getUseAntiAliasing, 0}},
- {3383, {wxStyledTextCtrl, addTextRaw, 1}},
- {3384, {wxStyledTextCtrl, insertTextRaw, 2}},
- {3385, {wxStyledTextCtrl, getCurLineRaw, 1}},
- {3386, {wxStyledTextCtrl, getLineRaw, 1}},
- {3387, {wxStyledTextCtrl, getSelectedTextRaw, 0}},
- {3388, {wxStyledTextCtrl, getTextRangeRaw, 2}},
- {3389, {wxStyledTextCtrl, setTextRaw, 1}},
- {3390, {wxStyledTextCtrl, getTextRaw, 0}},
- {3391, {wxStyledTextCtrl, appendTextRaw, 1}},
- {3392, {wxArtProvider, getBitmap, 2}},
- {3393, {wxArtProvider, getIcon, 2}},
- {3394, {wxTreeEvent, getKeyCode, 0}},
- {3395, {wxTreeEvent, getItem, 0}},
- {3396, {wxTreeEvent, getKeyEvent, 0}},
- {3397, {wxTreeEvent, getLabel, 0}},
- {3398, {wxTreeEvent, getOldItem, 0}},
- {3399, {wxTreeEvent, getPoint, 0}},
- {3400, {wxTreeEvent, isEditCancelled, 0}},
- {3401, {wxTreeEvent, setToolTip, 1}},
- {3402, {wxNotebookEvent, getOldSelection, 0}},
- {3403, {wxNotebookEvent, getSelection, 0}},
- {3404, {wxNotebookEvent, setOldSelection, 1}},
- {3405, {wxNotebookEvent, setSelection, 1}},
- {3406, {wxFileDataObject, new, 0}},
- {3407, {wxFileDataObject, addFile, 1}},
- {3408, {wxFileDataObject, getFilenames, 0}},
- {3409, {wxFileDataObject, 'Destroy', undefined}},
- {3410, {wxTextDataObject, new, 1}},
- {3411, {wxTextDataObject, getTextLength, 0}},
- {3412, {wxTextDataObject, getText, 0}},
- {3413, {wxTextDataObject, setText, 1}},
- {3414, {wxTextDataObject, 'Destroy', undefined}},
- {3415, {wxBitmapDataObject, new_1_1, 1}},
- {3416, {wxBitmapDataObject, new_1_0, 1}},
- {3417, {wxBitmapDataObject, getBitmap, 0}},
- {3418, {wxBitmapDataObject, setBitmap, 1}},
- {3419, {wxBitmapDataObject, 'Destroy', undefined}},
- {3421, {wxClipboard, new, 0}},
- {3422, {wxClipboard, destruct, 0}},
- {3423, {wxClipboard, addData, 1}},
- {3424, {wxClipboard, clear, 0}},
- {3425, {wxClipboard, close, 0}},
- {3426, {wxClipboard, flush, 0}},
- {3427, {wxClipboard, getData, 1}},
- {3428, {wxClipboard, isOpened, 0}},
- {3429, {wxClipboard, open, 0}},
- {3430, {wxClipboard, setData, 1}},
- {3432, {wxClipboard, usePrimarySelection, 1}},
- {3433, {wxClipboard, isSupported, 1}},
- {3434, {wxClipboard, get, 0}},
- {3435, {wxSpinEvent, getPosition, 0}},
- {3436, {wxSpinEvent, setPosition, 1}},
- {3437, {wxSplitterWindow, new_0, 0}},
- {3438, {wxSplitterWindow, new_2, 2}},
- {3439, {wxSplitterWindow, destruct, 0}},
- {3440, {wxSplitterWindow, create, 2}},
- {3441, {wxSplitterWindow, getMinimumPaneSize, 0}},
- {3442, {wxSplitterWindow, getSashGravity, 0}},
- {3443, {wxSplitterWindow, getSashPosition, 0}},
- {3444, {wxSplitterWindow, getSplitMode, 0}},
- {3445, {wxSplitterWindow, getWindow1, 0}},
- {3446, {wxSplitterWindow, getWindow2, 0}},
- {3447, {wxSplitterWindow, initialize, 1}},
- {3448, {wxSplitterWindow, isSplit, 0}},
- {3449, {wxSplitterWindow, replaceWindow, 2}},
- {3450, {wxSplitterWindow, setSashGravity, 1}},
- {3451, {wxSplitterWindow, setSashPosition, 2}},
- {3452, {wxSplitterWindow, setSashSize, 1}},
- {3453, {wxSplitterWindow, setMinimumPaneSize, 1}},
- {3454, {wxSplitterWindow, setSplitMode, 1}},
- {3455, {wxSplitterWindow, splitHorizontally, 3}},
- {3456, {wxSplitterWindow, splitVertically, 3}},
- {3457, {wxSplitterWindow, unsplit, 1}},
- {3458, {wxSplitterWindow, updateSize, 0}},
- {3459, {wxSplitterEvent, getSashPosition, 0}},
- {3460, {wxSplitterEvent, getX, 0}},
- {3461, {wxSplitterEvent, getY, 0}},
- {3462, {wxSplitterEvent, getWindowBeingRemoved, 0}},
- {3463, {wxSplitterEvent, setSashPosition, 1}},
- {3464, {wxHtmlWindow, new_0, 0}},
- {3465, {wxHtmlWindow, new_2, 2}},
- {3466, {wxHtmlWindow, appendToPage, 1}},
- {3467, {wxHtmlWindow, getOpenedAnchor, 0}},
- {3468, {wxHtmlWindow, getOpenedPage, 0}},
- {3469, {wxHtmlWindow, getOpenedPageTitle, 0}},
- {3470, {wxHtmlWindow, getRelatedFrame, 0}},
- {3471, {wxHtmlWindow, historyBack, 0}},
- {3472, {wxHtmlWindow, historyCanBack, 0}},
- {3473, {wxHtmlWindow, historyCanForward, 0}},
- {3474, {wxHtmlWindow, historyClear, 0}},
- {3475, {wxHtmlWindow, historyForward, 0}},
- {3476, {wxHtmlWindow, loadFile, 1}},
- {3477, {wxHtmlWindow, loadPage, 1}},
- {3478, {wxHtmlWindow, selectAll, 0}},
- {3479, {wxHtmlWindow, selectionToText, 0}},
- {3480, {wxHtmlWindow, selectLine, 1}},
- {3481, {wxHtmlWindow, selectWord, 1}},
- {3482, {wxHtmlWindow, setBorders, 1}},
- {3483, {wxHtmlWindow, setFonts, 3}},
- {3484, {wxHtmlWindow, setPage, 1}},
- {3485, {wxHtmlWindow, setRelatedFrame, 2}},
- {3486, {wxHtmlWindow, setRelatedStatusBar, 1}},
- {3487, {wxHtmlWindow, toText, 0}},
- {3488, {wxHtmlWindow, 'Destroy', undefined}},
- {3489, {wxHtmlLinkEvent, getLinkInfo, 0}},
- {3490, {wxSystemSettings, getColour, 1}},
- {3491, {wxSystemSettings, getFont, 1}},
- {3492, {wxSystemSettings, getMetric, 2}},
- {3493, {wxSystemSettings, getScreenType, 0}},
- {3494, {wxSystemOptions, getOption, 1}},
- {3495, {wxSystemOptions, getOptionInt, 1}},
- {3496, {wxSystemOptions, hasOption, 1}},
- {3497, {wxSystemOptions, isFalse, 1}},
- {3498, {wxSystemOptions, setOption_2_1, 2}},
- {3499, {wxSystemOptions, setOption_2_0, 2}},
- {3500, {wxAuiNotebookEvent, setSelection, 1}},
- {3501, {wxAuiNotebookEvent, getSelection, 0}},
- {3502, {wxAuiNotebookEvent, setOldSelection, 1}},
- {3503, {wxAuiNotebookEvent, getOldSelection, 0}},
- {3504, {wxAuiNotebookEvent, setDragSource, 1}},
- {3505, {wxAuiNotebookEvent, getDragSource, 0}},
- {3506, {wxAuiManagerEvent, setManager, 1}},
- {3507, {wxAuiManagerEvent, getManager, 0}},
- {3508, {wxAuiManagerEvent, setPane, 1}},
- {3509, {wxAuiManagerEvent, getPane, 0}},
- {3510, {wxAuiManagerEvent, setButton, 1}},
- {3511, {wxAuiManagerEvent, getButton, 0}},
- {3512, {wxAuiManagerEvent, setDC, 1}},
- {3513, {wxAuiManagerEvent, getDC, 0}},
- {3514, {wxAuiManagerEvent, veto, 1}},
- {3515, {wxAuiManagerEvent, getVeto, 0}},
- {3516, {wxAuiManagerEvent, setCanVeto, 1}},
- {3517, {wxAuiManagerEvent, canVeto, 0}},
- {3518, {wxLogNull, new, 0}},
- {3519, {wxLogNull, 'Destroy', undefined}},
- {3520, {wxTaskBarIcon, new, 0}},
- {3521, {wxTaskBarIcon, destruct, 0}},
- {3522, {wxTaskBarIcon, popupMenu, 1}},
- {3523, {wxTaskBarIcon, removeIcon, 0}},
- {3524, {wxTaskBarIcon, setIcon, 2}},
+ {3373, {wxStyledTextCtrl, setVScrollBar, 1}},
+ {3374, {wxStyledTextCtrl, setHScrollBar, 1}},
+ {3375, {wxStyledTextCtrl, getLastKeydownProcessed, 0}},
+ {3376, {wxStyledTextCtrl, setLastKeydownProcessed, 1}},
+ {3377, {wxStyledTextCtrl, saveFile, 1}},
+ {3378, {wxStyledTextCtrl, loadFile, 1}},
+ {3379, {wxStyledTextCtrl, doDragOver, 3}},
+ {3380, {wxStyledTextCtrl, doDropText, 3}},
+ {3381, {wxStyledTextCtrl, getUseAntiAliasing, 0}},
+ {3382, {wxStyledTextCtrl, addTextRaw, 1}},
+ {3383, {wxStyledTextCtrl, insertTextRaw, 2}},
+ {3384, {wxStyledTextCtrl, getCurLineRaw, 1}},
+ {3385, {wxStyledTextCtrl, getLineRaw, 1}},
+ {3386, {wxStyledTextCtrl, getSelectedTextRaw, 0}},
+ {3387, {wxStyledTextCtrl, getTextRangeRaw, 2}},
+ {3388, {wxStyledTextCtrl, setTextRaw, 1}},
+ {3389, {wxStyledTextCtrl, getTextRaw, 0}},
+ {3390, {wxStyledTextCtrl, appendTextRaw, 1}},
+ {3391, {wxArtProvider, getBitmap, 2}},
+ {3392, {wxArtProvider, getIcon, 2}},
+ {3393, {wxTreeEvent, getKeyCode, 0}},
+ {3394, {wxTreeEvent, getItem, 0}},
+ {3395, {wxTreeEvent, getKeyEvent, 0}},
+ {3396, {wxTreeEvent, getLabel, 0}},
+ {3397, {wxTreeEvent, getOldItem, 0}},
+ {3398, {wxTreeEvent, getPoint, 0}},
+ {3399, {wxTreeEvent, isEditCancelled, 0}},
+ {3400, {wxTreeEvent, setToolTip, 1}},
+ {3401, {wxNotebookEvent, getOldSelection, 0}},
+ {3402, {wxNotebookEvent, getSelection, 0}},
+ {3403, {wxNotebookEvent, setOldSelection, 1}},
+ {3404, {wxNotebookEvent, setSelection, 1}},
+ {3405, {wxFileDataObject, new, 0}},
+ {3406, {wxFileDataObject, addFile, 1}},
+ {3407, {wxFileDataObject, getFilenames, 0}},
+ {3408, {wxFileDataObject, 'Destroy', undefined}},
+ {3409, {wxTextDataObject, new, 1}},
+ {3410, {wxTextDataObject, getTextLength, 0}},
+ {3411, {wxTextDataObject, getText, 0}},
+ {3412, {wxTextDataObject, setText, 1}},
+ {3413, {wxTextDataObject, 'Destroy', undefined}},
+ {3414, {wxBitmapDataObject, new_1_1, 1}},
+ {3415, {wxBitmapDataObject, new_1_0, 1}},
+ {3416, {wxBitmapDataObject, getBitmap, 0}},
+ {3417, {wxBitmapDataObject, setBitmap, 1}},
+ {3418, {wxBitmapDataObject, 'Destroy', undefined}},
+ {3420, {wxClipboard, new, 0}},
+ {3421, {wxClipboard, destruct, 0}},
+ {3422, {wxClipboard, addData, 1}},
+ {3423, {wxClipboard, clear, 0}},
+ {3424, {wxClipboard, close, 0}},
+ {3425, {wxClipboard, flush, 0}},
+ {3426, {wxClipboard, getData, 1}},
+ {3427, {wxClipboard, isOpened, 0}},
+ {3428, {wxClipboard, open, 0}},
+ {3429, {wxClipboard, setData, 1}},
+ {3431, {wxClipboard, usePrimarySelection, 1}},
+ {3432, {wxClipboard, isSupported, 1}},
+ {3433, {wxClipboard, get, 0}},
+ {3434, {wxSpinEvent, getPosition, 0}},
+ {3435, {wxSpinEvent, setPosition, 1}},
+ {3436, {wxSplitterWindow, new_0, 0}},
+ {3437, {wxSplitterWindow, new_2, 2}},
+ {3438, {wxSplitterWindow, destruct, 0}},
+ {3439, {wxSplitterWindow, create, 2}},
+ {3440, {wxSplitterWindow, getMinimumPaneSize, 0}},
+ {3441, {wxSplitterWindow, getSashGravity, 0}},
+ {3442, {wxSplitterWindow, getSashPosition, 0}},
+ {3443, {wxSplitterWindow, getSplitMode, 0}},
+ {3444, {wxSplitterWindow, getWindow1, 0}},
+ {3445, {wxSplitterWindow, getWindow2, 0}},
+ {3446, {wxSplitterWindow, initialize, 1}},
+ {3447, {wxSplitterWindow, isSplit, 0}},
+ {3448, {wxSplitterWindow, replaceWindow, 2}},
+ {3449, {wxSplitterWindow, setSashGravity, 1}},
+ {3450, {wxSplitterWindow, setSashPosition, 2}},
+ {3451, {wxSplitterWindow, setSashSize, 1}},
+ {3452, {wxSplitterWindow, setMinimumPaneSize, 1}},
+ {3453, {wxSplitterWindow, setSplitMode, 1}},
+ {3454, {wxSplitterWindow, splitHorizontally, 3}},
+ {3455, {wxSplitterWindow, splitVertically, 3}},
+ {3456, {wxSplitterWindow, unsplit, 1}},
+ {3457, {wxSplitterWindow, updateSize, 0}},
+ {3458, {wxSplitterEvent, getSashPosition, 0}},
+ {3459, {wxSplitterEvent, getX, 0}},
+ {3460, {wxSplitterEvent, getY, 0}},
+ {3461, {wxSplitterEvent, getWindowBeingRemoved, 0}},
+ {3462, {wxSplitterEvent, setSashPosition, 1}},
+ {3463, {wxHtmlWindow, new_0, 0}},
+ {3464, {wxHtmlWindow, new_2, 2}},
+ {3465, {wxHtmlWindow, appendToPage, 1}},
+ {3466, {wxHtmlWindow, getOpenedAnchor, 0}},
+ {3467, {wxHtmlWindow, getOpenedPage, 0}},
+ {3468, {wxHtmlWindow, getOpenedPageTitle, 0}},
+ {3469, {wxHtmlWindow, getRelatedFrame, 0}},
+ {3470, {wxHtmlWindow, historyBack, 0}},
+ {3471, {wxHtmlWindow, historyCanBack, 0}},
+ {3472, {wxHtmlWindow, historyCanForward, 0}},
+ {3473, {wxHtmlWindow, historyClear, 0}},
+ {3474, {wxHtmlWindow, historyForward, 0}},
+ {3475, {wxHtmlWindow, loadFile, 1}},
+ {3476, {wxHtmlWindow, loadPage, 1}},
+ {3477, {wxHtmlWindow, selectAll, 0}},
+ {3478, {wxHtmlWindow, selectionToText, 0}},
+ {3479, {wxHtmlWindow, selectLine, 1}},
+ {3480, {wxHtmlWindow, selectWord, 1}},
+ {3481, {wxHtmlWindow, setBorders, 1}},
+ {3482, {wxHtmlWindow, setFonts, 3}},
+ {3483, {wxHtmlWindow, setPage, 1}},
+ {3484, {wxHtmlWindow, setRelatedFrame, 2}},
+ {3485, {wxHtmlWindow, setRelatedStatusBar, 1}},
+ {3486, {wxHtmlWindow, toText, 0}},
+ {3487, {wxHtmlWindow, 'Destroy', undefined}},
+ {3488, {wxHtmlLinkEvent, getLinkInfo, 0}},
+ {3489, {wxSystemSettings, getColour, 1}},
+ {3490, {wxSystemSettings, getFont, 1}},
+ {3491, {wxSystemSettings, getMetric, 2}},
+ {3492, {wxSystemSettings, getScreenType, 0}},
+ {3493, {wxSystemOptions, getOption, 1}},
+ {3494, {wxSystemOptions, getOptionInt, 1}},
+ {3495, {wxSystemOptions, hasOption, 1}},
+ {3496, {wxSystemOptions, isFalse, 1}},
+ {3497, {wxSystemOptions, setOption_2_1, 2}},
+ {3498, {wxSystemOptions, setOption_2_0, 2}},
+ {3499, {wxAuiNotebookEvent, setSelection, 1}},
+ {3500, {wxAuiNotebookEvent, getSelection, 0}},
+ {3501, {wxAuiNotebookEvent, setOldSelection, 1}},
+ {3502, {wxAuiNotebookEvent, getOldSelection, 0}},
+ {3503, {wxAuiNotebookEvent, setDragSource, 1}},
+ {3504, {wxAuiNotebookEvent, getDragSource, 0}},
+ {3505, {wxAuiManagerEvent, setManager, 1}},
+ {3506, {wxAuiManagerEvent, getManager, 0}},
+ {3507, {wxAuiManagerEvent, setPane, 1}},
+ {3508, {wxAuiManagerEvent, getPane, 0}},
+ {3509, {wxAuiManagerEvent, setButton, 1}},
+ {3510, {wxAuiManagerEvent, getButton, 0}},
+ {3511, {wxAuiManagerEvent, setDC, 1}},
+ {3512, {wxAuiManagerEvent, getDC, 0}},
+ {3513, {wxAuiManagerEvent, veto, 1}},
+ {3514, {wxAuiManagerEvent, getVeto, 0}},
+ {3515, {wxAuiManagerEvent, setCanVeto, 1}},
+ {3516, {wxAuiManagerEvent, canVeto, 0}},
+ {3517, {wxLogNull, new, 0}},
+ {3518, {wxLogNull, 'Destroy', undefined}},
+ {3519, {wxTaskBarIcon, new, 0}},
+ {3520, {wxTaskBarIcon, destruct, 0}},
+ {3521, {wxTaskBarIcon, popupMenu, 1}},
+ {3522, {wxTaskBarIcon, removeIcon, 0}},
+ {3523, {wxTaskBarIcon, setIcon, 2}},
{-1, {mod, func, -1}}
].
diff --git a/lib/wx/src/gen/wxe_funcs.hrl b/lib/wx/src/gen/wxe_funcs.hrl
index fe35cb1374..e6aced6e09 100644
--- a/lib/wx/src/gen/wxe_funcs.hrl
+++ b/lib/wx/src/gen/wxe_funcs.hrl
@@ -3151,153 +3151,152 @@
-define(wxStyledTextCtrl_PointFromPosition, 3370).
-define(wxStyledTextCtrl_ScrollToLine, 3371).
-define(wxStyledTextCtrl_ScrollToColumn, 3372).
--define(wxStyledTextCtrl_SendMsg, 3373).
--define(wxStyledTextCtrl_SetVScrollBar, 3374).
--define(wxStyledTextCtrl_SetHScrollBar, 3375).
--define(wxStyledTextCtrl_GetLastKeydownProcessed, 3376).
--define(wxStyledTextCtrl_SetLastKeydownProcessed, 3377).
--define(wxStyledTextCtrl_SaveFile, 3378).
--define(wxStyledTextCtrl_LoadFile, 3379).
--define(wxStyledTextCtrl_DoDragOver, 3380).
--define(wxStyledTextCtrl_DoDropText, 3381).
--define(wxStyledTextCtrl_GetUseAntiAliasing, 3382).
--define(wxStyledTextCtrl_AddTextRaw, 3383).
--define(wxStyledTextCtrl_InsertTextRaw, 3384).
--define(wxStyledTextCtrl_GetCurLineRaw, 3385).
--define(wxStyledTextCtrl_GetLineRaw, 3386).
--define(wxStyledTextCtrl_GetSelectedTextRaw, 3387).
--define(wxStyledTextCtrl_GetTextRangeRaw, 3388).
--define(wxStyledTextCtrl_SetTextRaw, 3389).
--define(wxStyledTextCtrl_GetTextRaw, 3390).
--define(wxStyledTextCtrl_AppendTextRaw, 3391).
--define(wxArtProvider_GetBitmap, 3392).
--define(wxArtProvider_GetIcon, 3393).
--define(wxTreeEvent_GetKeyCode, 3394).
--define(wxTreeEvent_GetItem, 3395).
--define(wxTreeEvent_GetKeyEvent, 3396).
--define(wxTreeEvent_GetLabel, 3397).
--define(wxTreeEvent_GetOldItem, 3398).
--define(wxTreeEvent_GetPoint, 3399).
--define(wxTreeEvent_IsEditCancelled, 3400).
--define(wxTreeEvent_SetToolTip, 3401).
--define(wxNotebookEvent_GetOldSelection, 3402).
--define(wxNotebookEvent_GetSelection, 3403).
--define(wxNotebookEvent_SetOldSelection, 3404).
--define(wxNotebookEvent_SetSelection, 3405).
--define(wxFileDataObject_new, 3406).
--define(wxFileDataObject_AddFile, 3407).
--define(wxFileDataObject_GetFilenames, 3408).
--define(wxFileDataObject_destroy, 3409).
--define(wxTextDataObject_new, 3410).
--define(wxTextDataObject_GetTextLength, 3411).
--define(wxTextDataObject_GetText, 3412).
--define(wxTextDataObject_SetText, 3413).
--define(wxTextDataObject_destroy, 3414).
--define(wxBitmapDataObject_new_1_1, 3415).
--define(wxBitmapDataObject_new_1_0, 3416).
--define(wxBitmapDataObject_GetBitmap, 3417).
--define(wxBitmapDataObject_SetBitmap, 3418).
--define(wxBitmapDataObject_destroy, 3419).
--define(wxClipboard_new, 3421).
--define(wxClipboard_destruct, 3422).
--define(wxClipboard_AddData, 3423).
--define(wxClipboard_Clear, 3424).
--define(wxClipboard_Close, 3425).
--define(wxClipboard_Flush, 3426).
--define(wxClipboard_GetData, 3427).
--define(wxClipboard_IsOpened, 3428).
--define(wxClipboard_Open, 3429).
--define(wxClipboard_SetData, 3430).
--define(wxClipboard_UsePrimarySelection, 3432).
--define(wxClipboard_IsSupported, 3433).
--define(wxClipboard_Get, 3434).
--define(wxSpinEvent_GetPosition, 3435).
--define(wxSpinEvent_SetPosition, 3436).
--define(wxSplitterWindow_new_0, 3437).
--define(wxSplitterWindow_new_2, 3438).
--define(wxSplitterWindow_destruct, 3439).
--define(wxSplitterWindow_Create, 3440).
--define(wxSplitterWindow_GetMinimumPaneSize, 3441).
--define(wxSplitterWindow_GetSashGravity, 3442).
--define(wxSplitterWindow_GetSashPosition, 3443).
--define(wxSplitterWindow_GetSplitMode, 3444).
--define(wxSplitterWindow_GetWindow1, 3445).
--define(wxSplitterWindow_GetWindow2, 3446).
--define(wxSplitterWindow_Initialize, 3447).
--define(wxSplitterWindow_IsSplit, 3448).
--define(wxSplitterWindow_ReplaceWindow, 3449).
--define(wxSplitterWindow_SetSashGravity, 3450).
--define(wxSplitterWindow_SetSashPosition, 3451).
--define(wxSplitterWindow_SetSashSize, 3452).
--define(wxSplitterWindow_SetMinimumPaneSize, 3453).
--define(wxSplitterWindow_SetSplitMode, 3454).
--define(wxSplitterWindow_SplitHorizontally, 3455).
--define(wxSplitterWindow_SplitVertically, 3456).
--define(wxSplitterWindow_Unsplit, 3457).
--define(wxSplitterWindow_UpdateSize, 3458).
--define(wxSplitterEvent_GetSashPosition, 3459).
--define(wxSplitterEvent_GetX, 3460).
--define(wxSplitterEvent_GetY, 3461).
--define(wxSplitterEvent_GetWindowBeingRemoved, 3462).
--define(wxSplitterEvent_SetSashPosition, 3463).
--define(wxHtmlWindow_new_0, 3464).
--define(wxHtmlWindow_new_2, 3465).
--define(wxHtmlWindow_AppendToPage, 3466).
--define(wxHtmlWindow_GetOpenedAnchor, 3467).
--define(wxHtmlWindow_GetOpenedPage, 3468).
--define(wxHtmlWindow_GetOpenedPageTitle, 3469).
--define(wxHtmlWindow_GetRelatedFrame, 3470).
--define(wxHtmlWindow_HistoryBack, 3471).
--define(wxHtmlWindow_HistoryCanBack, 3472).
--define(wxHtmlWindow_HistoryCanForward, 3473).
--define(wxHtmlWindow_HistoryClear, 3474).
--define(wxHtmlWindow_HistoryForward, 3475).
--define(wxHtmlWindow_LoadFile, 3476).
--define(wxHtmlWindow_LoadPage, 3477).
--define(wxHtmlWindow_SelectAll, 3478).
--define(wxHtmlWindow_SelectionToText, 3479).
--define(wxHtmlWindow_SelectLine, 3480).
--define(wxHtmlWindow_SelectWord, 3481).
--define(wxHtmlWindow_SetBorders, 3482).
--define(wxHtmlWindow_SetFonts, 3483).
--define(wxHtmlWindow_SetPage, 3484).
--define(wxHtmlWindow_SetRelatedFrame, 3485).
--define(wxHtmlWindow_SetRelatedStatusBar, 3486).
--define(wxHtmlWindow_ToText, 3487).
--define(wxHtmlWindow_destroy, 3488).
--define(wxHtmlLinkEvent_GetLinkInfo, 3489).
--define(wxSystemSettings_GetColour, 3490).
--define(wxSystemSettings_GetFont, 3491).
--define(wxSystemSettings_GetMetric, 3492).
--define(wxSystemSettings_GetScreenType, 3493).
--define(wxSystemOptions_GetOption, 3494).
--define(wxSystemOptions_GetOptionInt, 3495).
--define(wxSystemOptions_HasOption, 3496).
--define(wxSystemOptions_IsFalse, 3497).
--define(wxSystemOptions_SetOption_2_1, 3498).
--define(wxSystemOptions_SetOption_2_0, 3499).
--define(wxAuiNotebookEvent_SetSelection, 3500).
--define(wxAuiNotebookEvent_GetSelection, 3501).
--define(wxAuiNotebookEvent_SetOldSelection, 3502).
--define(wxAuiNotebookEvent_GetOldSelection, 3503).
--define(wxAuiNotebookEvent_SetDragSource, 3504).
--define(wxAuiNotebookEvent_GetDragSource, 3505).
--define(wxAuiManagerEvent_SetManager, 3506).
--define(wxAuiManagerEvent_GetManager, 3507).
--define(wxAuiManagerEvent_SetPane, 3508).
--define(wxAuiManagerEvent_GetPane, 3509).
--define(wxAuiManagerEvent_SetButton, 3510).
--define(wxAuiManagerEvent_GetButton, 3511).
--define(wxAuiManagerEvent_SetDC, 3512).
--define(wxAuiManagerEvent_GetDC, 3513).
--define(wxAuiManagerEvent_Veto, 3514).
--define(wxAuiManagerEvent_GetVeto, 3515).
--define(wxAuiManagerEvent_SetCanVeto, 3516).
--define(wxAuiManagerEvent_CanVeto, 3517).
--define(wxLogNull_new, 3518).
--define(wxLogNull_destroy, 3519).
--define(wxTaskBarIcon_new, 3520).
--define(wxTaskBarIcon_destruct, 3521).
--define(wxTaskBarIcon_PopupMenu, 3522).
--define(wxTaskBarIcon_RemoveIcon, 3523).
--define(wxTaskBarIcon_SetIcon, 3524).
+-define(wxStyledTextCtrl_SetVScrollBar, 3373).
+-define(wxStyledTextCtrl_SetHScrollBar, 3374).
+-define(wxStyledTextCtrl_GetLastKeydownProcessed, 3375).
+-define(wxStyledTextCtrl_SetLastKeydownProcessed, 3376).
+-define(wxStyledTextCtrl_SaveFile, 3377).
+-define(wxStyledTextCtrl_LoadFile, 3378).
+-define(wxStyledTextCtrl_DoDragOver, 3379).
+-define(wxStyledTextCtrl_DoDropText, 3380).
+-define(wxStyledTextCtrl_GetUseAntiAliasing, 3381).
+-define(wxStyledTextCtrl_AddTextRaw, 3382).
+-define(wxStyledTextCtrl_InsertTextRaw, 3383).
+-define(wxStyledTextCtrl_GetCurLineRaw, 3384).
+-define(wxStyledTextCtrl_GetLineRaw, 3385).
+-define(wxStyledTextCtrl_GetSelectedTextRaw, 3386).
+-define(wxStyledTextCtrl_GetTextRangeRaw, 3387).
+-define(wxStyledTextCtrl_SetTextRaw, 3388).
+-define(wxStyledTextCtrl_GetTextRaw, 3389).
+-define(wxStyledTextCtrl_AppendTextRaw, 3390).
+-define(wxArtProvider_GetBitmap, 3391).
+-define(wxArtProvider_GetIcon, 3392).
+-define(wxTreeEvent_GetKeyCode, 3393).
+-define(wxTreeEvent_GetItem, 3394).
+-define(wxTreeEvent_GetKeyEvent, 3395).
+-define(wxTreeEvent_GetLabel, 3396).
+-define(wxTreeEvent_GetOldItem, 3397).
+-define(wxTreeEvent_GetPoint, 3398).
+-define(wxTreeEvent_IsEditCancelled, 3399).
+-define(wxTreeEvent_SetToolTip, 3400).
+-define(wxNotebookEvent_GetOldSelection, 3401).
+-define(wxNotebookEvent_GetSelection, 3402).
+-define(wxNotebookEvent_SetOldSelection, 3403).
+-define(wxNotebookEvent_SetSelection, 3404).
+-define(wxFileDataObject_new, 3405).
+-define(wxFileDataObject_AddFile, 3406).
+-define(wxFileDataObject_GetFilenames, 3407).
+-define(wxFileDataObject_destroy, 3408).
+-define(wxTextDataObject_new, 3409).
+-define(wxTextDataObject_GetTextLength, 3410).
+-define(wxTextDataObject_GetText, 3411).
+-define(wxTextDataObject_SetText, 3412).
+-define(wxTextDataObject_destroy, 3413).
+-define(wxBitmapDataObject_new_1_1, 3414).
+-define(wxBitmapDataObject_new_1_0, 3415).
+-define(wxBitmapDataObject_GetBitmap, 3416).
+-define(wxBitmapDataObject_SetBitmap, 3417).
+-define(wxBitmapDataObject_destroy, 3418).
+-define(wxClipboard_new, 3420).
+-define(wxClipboard_destruct, 3421).
+-define(wxClipboard_AddData, 3422).
+-define(wxClipboard_Clear, 3423).
+-define(wxClipboard_Close, 3424).
+-define(wxClipboard_Flush, 3425).
+-define(wxClipboard_GetData, 3426).
+-define(wxClipboard_IsOpened, 3427).
+-define(wxClipboard_Open, 3428).
+-define(wxClipboard_SetData, 3429).
+-define(wxClipboard_UsePrimarySelection, 3431).
+-define(wxClipboard_IsSupported, 3432).
+-define(wxClipboard_Get, 3433).
+-define(wxSpinEvent_GetPosition, 3434).
+-define(wxSpinEvent_SetPosition, 3435).
+-define(wxSplitterWindow_new_0, 3436).
+-define(wxSplitterWindow_new_2, 3437).
+-define(wxSplitterWindow_destruct, 3438).
+-define(wxSplitterWindow_Create, 3439).
+-define(wxSplitterWindow_GetMinimumPaneSize, 3440).
+-define(wxSplitterWindow_GetSashGravity, 3441).
+-define(wxSplitterWindow_GetSashPosition, 3442).
+-define(wxSplitterWindow_GetSplitMode, 3443).
+-define(wxSplitterWindow_GetWindow1, 3444).
+-define(wxSplitterWindow_GetWindow2, 3445).
+-define(wxSplitterWindow_Initialize, 3446).
+-define(wxSplitterWindow_IsSplit, 3447).
+-define(wxSplitterWindow_ReplaceWindow, 3448).
+-define(wxSplitterWindow_SetSashGravity, 3449).
+-define(wxSplitterWindow_SetSashPosition, 3450).
+-define(wxSplitterWindow_SetSashSize, 3451).
+-define(wxSplitterWindow_SetMinimumPaneSize, 3452).
+-define(wxSplitterWindow_SetSplitMode, 3453).
+-define(wxSplitterWindow_SplitHorizontally, 3454).
+-define(wxSplitterWindow_SplitVertically, 3455).
+-define(wxSplitterWindow_Unsplit, 3456).
+-define(wxSplitterWindow_UpdateSize, 3457).
+-define(wxSplitterEvent_GetSashPosition, 3458).
+-define(wxSplitterEvent_GetX, 3459).
+-define(wxSplitterEvent_GetY, 3460).
+-define(wxSplitterEvent_GetWindowBeingRemoved, 3461).
+-define(wxSplitterEvent_SetSashPosition, 3462).
+-define(wxHtmlWindow_new_0, 3463).
+-define(wxHtmlWindow_new_2, 3464).
+-define(wxHtmlWindow_AppendToPage, 3465).
+-define(wxHtmlWindow_GetOpenedAnchor, 3466).
+-define(wxHtmlWindow_GetOpenedPage, 3467).
+-define(wxHtmlWindow_GetOpenedPageTitle, 3468).
+-define(wxHtmlWindow_GetRelatedFrame, 3469).
+-define(wxHtmlWindow_HistoryBack, 3470).
+-define(wxHtmlWindow_HistoryCanBack, 3471).
+-define(wxHtmlWindow_HistoryCanForward, 3472).
+-define(wxHtmlWindow_HistoryClear, 3473).
+-define(wxHtmlWindow_HistoryForward, 3474).
+-define(wxHtmlWindow_LoadFile, 3475).
+-define(wxHtmlWindow_LoadPage, 3476).
+-define(wxHtmlWindow_SelectAll, 3477).
+-define(wxHtmlWindow_SelectionToText, 3478).
+-define(wxHtmlWindow_SelectLine, 3479).
+-define(wxHtmlWindow_SelectWord, 3480).
+-define(wxHtmlWindow_SetBorders, 3481).
+-define(wxHtmlWindow_SetFonts, 3482).
+-define(wxHtmlWindow_SetPage, 3483).
+-define(wxHtmlWindow_SetRelatedFrame, 3484).
+-define(wxHtmlWindow_SetRelatedStatusBar, 3485).
+-define(wxHtmlWindow_ToText, 3486).
+-define(wxHtmlWindow_destroy, 3487).
+-define(wxHtmlLinkEvent_GetLinkInfo, 3488).
+-define(wxSystemSettings_GetColour, 3489).
+-define(wxSystemSettings_GetFont, 3490).
+-define(wxSystemSettings_GetMetric, 3491).
+-define(wxSystemSettings_GetScreenType, 3492).
+-define(wxSystemOptions_GetOption, 3493).
+-define(wxSystemOptions_GetOptionInt, 3494).
+-define(wxSystemOptions_HasOption, 3495).
+-define(wxSystemOptions_IsFalse, 3496).
+-define(wxSystemOptions_SetOption_2_1, 3497).
+-define(wxSystemOptions_SetOption_2_0, 3498).
+-define(wxAuiNotebookEvent_SetSelection, 3499).
+-define(wxAuiNotebookEvent_GetSelection, 3500).
+-define(wxAuiNotebookEvent_SetOldSelection, 3501).
+-define(wxAuiNotebookEvent_GetOldSelection, 3502).
+-define(wxAuiNotebookEvent_SetDragSource, 3503).
+-define(wxAuiNotebookEvent_GetDragSource, 3504).
+-define(wxAuiManagerEvent_SetManager, 3505).
+-define(wxAuiManagerEvent_GetManager, 3506).
+-define(wxAuiManagerEvent_SetPane, 3507).
+-define(wxAuiManagerEvent_GetPane, 3508).
+-define(wxAuiManagerEvent_SetButton, 3509).
+-define(wxAuiManagerEvent_GetButton, 3510).
+-define(wxAuiManagerEvent_SetDC, 3511).
+-define(wxAuiManagerEvent_GetDC, 3512).
+-define(wxAuiManagerEvent_Veto, 3513).
+-define(wxAuiManagerEvent_GetVeto, 3514).
+-define(wxAuiManagerEvent_SetCanVeto, 3515).
+-define(wxAuiManagerEvent_CanVeto, 3516).
+-define(wxLogNull_new, 3517).
+-define(wxLogNull_destroy, 3518).
+-define(wxTaskBarIcon_new, 3519).
+-define(wxTaskBarIcon_destruct, 3520).
+-define(wxTaskBarIcon_PopupMenu, 3521).
+-define(wxTaskBarIcon_RemoveIcon, 3522).
+-define(wxTaskBarIcon_SetIcon, 3523).
diff --git a/lib/wx/src/wx.erl b/lib/wx/src/wx.erl
index 7d62305048..2a4b18d101 100644
--- a/lib/wx/src/wx.erl
+++ b/lib/wx/src/wx.erl
@@ -102,12 +102,18 @@ new() ->
%% @doc Starts a wx server.
%% Option may be {debug, Level}, see debug/1.
--spec new([Option]) -> wx_object() when Option :: {debug, list() | atom()}.
+%% Or {silent_start, Bool}, which causes error messages at startup to
+%% be suppressed. The latter can be used as a silent test of whether
+%% wx is properly installed or not.
+-spec new([Option]) -> wx_object() when Option :: {debug, list() | atom()} |
+ {silent_start, boolean()}.
new(Options) when is_list(Options) ->
- #wx_env{port=Port} = wxe_server:start(),
- put(opengl_port, Port),
Debug = proplists:get_value(debug, Options, 0),
- debug(Debug),
+ SilentStart = proplists:get_value(silent_start, Options, false),
+ Level = calc_level(Debug),
+ #wx_env{port=Port} = wxe_server:start(SilentStart andalso Level =:= 0),
+ put(opengl_port, Port),
+ set_debug(Level),
null().
%% @doc Stops a wx server.
@@ -282,13 +288,16 @@ release_memory(Bin) when is_binary(Bin) ->
-spec debug(Level | [Level]) -> ok
when Level :: none | verbose | trace | driver | integer().
-debug(none) -> debug(0);
-debug(verbose) -> debug(1);
-debug(trace) -> debug(2);
-debug(driver) -> debug(16);
-debug([]) -> debug(0);
+debug(Debug) ->
+ Level = calc_level(Debug),
+ set_debug(Level).
-debug(List) when is_list(List) ->
+calc_level(none) -> calc_level(0);
+calc_level(verbose) -> calc_level(1);
+calc_level(trace) -> calc_level(2);
+calc_level(driver) -> calc_level(16);
+calc_level([]) -> calc_level(0);
+calc_level(List) when is_list(List) ->
{Drv,Erl} =
lists:foldl(fun(verbose, {Drv,_Erl}) ->
{Drv,1};
@@ -297,8 +306,11 @@ debug(List) when is_list(List) ->
(driver, {_Drv,Erl}) ->
{16, Erl}
end, {0,0}, List),
- debug(Drv + Erl);
-debug(Level) when is_integer(Level) ->
+ Drv + Erl;
+calc_level(Level) when is_integer(Level) ->
+ Level.
+
+set_debug(Level) when is_integer(Level) ->
case get(?WXE_IDENTIFIER) of
undefined -> erlang:error({wxe,unknown_port});
#wx_env{debug=Old} when Old =:= Level -> ok;
diff --git a/lib/wx/src/wxe_master.erl b/lib/wx/src/wxe_master.erl
index ac6e4a56e6..b98a7c793e 100644
--- a/lib/wx/src/wxe_master.erl
+++ b/lib/wx/src/wxe_master.erl
@@ -28,7 +28,7 @@
-behaviour(gen_server).
%% API
--export([start/0, init_port/0, init_opengl/0]).
+-export([start/1, init_port/1, init_opengl/0]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
@@ -47,20 +47,20 @@
%% API
%%====================================================================
%%--------------------------------------------------------------------
-%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
+%% Function: start(SilentStart) -> {ok,Pid} | ignore | {error,Error}
%% Description: Starts the server
%%--------------------------------------------------------------------
-start() ->
- gen_server:start({local, ?MODULE}, ?MODULE, [], []).
+start(SilentStart) ->
+ gen_server:start({local, ?MODULE}, ?MODULE, [SilentStart], []).
%%--------------------------------------------------------------------
-%% Function: init_port() -> {UserPort,CallBackPort} | error(Error)
+%% Function: init_port(SilentStart) -> {UserPort,CallBackPort} | error(Error)
%% Description: Creates the port
%%--------------------------------------------------------------------
-init_port() ->
+init_port(SilentStart) ->
case whereis(?MODULE) of
undefined ->
- case start() of
+ case start(SilentStart) of
{ok,Pid} -> Pid;
{error,{already_started,Pid}} -> Pid;
{error, {Reason,Stack}} ->
@@ -93,14 +93,17 @@ init_opengl() ->
%% {stop, Reason}
%% Description: Initiates the server
%%--------------------------------------------------------------------
-init([]) ->
+init([SilentStart]) ->
DriverName = ?DRIVER,
- PrivDir = wxe_util:priv_dir(?DRIVER),
+ PrivDir = wxe_util:priv_dir(?DRIVER, SilentStart),
erlang:group_leader(whereis(init), self()),
case catch erlang:system_info(smp_support) of
true -> ok;
_ ->
- error_logger:format("WX ERROR: SMP emulator required (start with erl -smp)", []),
+ wxe_util:opt_error_log(SilentStart,
+ "WX ERROR: SMP emulator required"
+ " (start with erl -smp)",
+ []),
erlang:error(not_smp)
end,
@@ -114,7 +117,9 @@ init([]) ->
case erl_ddll:load_driver(PrivDir,DriverName) of
ok -> ok;
{error, What} ->
- error_logger:format("WX Failed loading ~p@~p ~n", [DriverName,PrivDir]),
+ wxe_util:opt_error_log(SilentStart,
+ "WX Failed loading ~p@~p ~n",
+ [DriverName,PrivDir]),
Str = erl_ddll:format_error(What),
erlang:error({load_driver,Str})
end,
@@ -210,4 +215,3 @@ debug_ping(Port) ->
_R = (catch erlang:port_call(Port, 0, [])),
%% io:format("Erlang ping ~p ~n", [_R]),
debug_ping(Port).
-
diff --git a/lib/wx/src/wxe_server.erl b/lib/wx/src/wxe_server.erl
index 6e982c97f6..689fe16a70 100644
--- a/lib/wx/src/wxe_server.erl
+++ b/lib/wx/src/wxe_server.erl
@@ -29,7 +29,7 @@
-behaviour(gen_server).
%% API
--export([start/0, stop/0, register_me/1, set_debug/2, invoke_callback/1]).
+-export([start/1, stop/0, register_me/1, set_debug/2, invoke_callback/1]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
@@ -49,13 +49,13 @@
%% API
%%====================================================================
%%--------------------------------------------------------------------
-%% Function: start() -> #wx_env{}
+%% Function: start(SilentStart) -> #wx_env{}
%% Description: Starts the server
%%--------------------------------------------------------------------
-start() ->
+start(SilentStart) ->
case get(?WXE_IDENTIFIER) of
undefined ->
- case gen_server:start(?MODULE, [], []) of
+ case gen_server:start(?MODULE, [SilentStart], []) of
{ok, Pid} ->
{ok, Port} = gen_server:call(Pid, get_port, infinity),
wx:set_env(Env = #wx_env{port=Port,sv=Pid}),
@@ -69,7 +69,7 @@ start() ->
Env;
false -> %% Ok we got an old wx env, someone forgot
erase(?WXE_IDENTIFIER), %% to call wx:destroy()
- start()
+ start(SilentStart)
end
end.
@@ -88,8 +88,8 @@ set_debug(Pid, Level) ->
%% gen_server callbacks
%%====================================================================
-init([]) ->
- {Port,CBPort} = wxe_master:init_port(),
+init([SilentStart]) ->
+ {Port,CBPort} = wxe_master:init_port(SilentStart),
put(?WXE_IDENTIFIER, #wx_env{port=Port,sv=self()}),
{ok,#state{port=Port, cb_port=CBPort,
users=gb_trees:empty(), cb=gb_trees:empty(), cb_cnt=1}}.
diff --git a/lib/wx/src/wxe_util.erl b/lib/wx/src/wxe_util.erl
index 02bca62486..a31c3e30b8 100644
--- a/lib/wx/src/wxe_util.erl
+++ b/lib/wx/src/wxe_util.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -32,7 +32,7 @@
get_const/1,colour_bin/1,datetime_bin/1,
to_bool/1,from_bool/1]).
--export([wxgl_dl/0, priv_dir/1]).
+-export([wxgl_dl/0, priv_dir/2, opt_error_log/3]).
-include("wxe.hrl").
@@ -205,7 +205,7 @@ check_previous() ->
wxgl_dl() ->
DynLib0 = "erl_gl",
- PrivDir = priv_dir(DynLib0),
+ PrivDir = priv_dir(DynLib0, false),
DynLib = case os:type() of
{win32,_} ->
DynLib0 ++ ".dll";
@@ -214,7 +214,7 @@ wxgl_dl() ->
end,
filename:join(PrivDir, DynLib).
-priv_dir(Driver0) ->
+priv_dir(Driver0, Silent) ->
{file, Path} = code:is_loaded(?MODULE),
Priv = case filelib:is_regular(Path) of
true ->
@@ -229,14 +229,20 @@ priv_dir(Driver0) ->
_ ->
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"})
+ SrcPriv = filename:join(Priv, erlang:system_info(system_architecture)),
+ case file:read_file_info(filename:join(SrcPriv, Driver)) of
+ {ok, _} ->
+ SrcPriv;
+ {error, _} ->
+ opt_error_log(Silent,
+ "ERROR: Could not find \'~s\' in: ~s~n",
+ [Driver, Priv]),
+ erlang:error({load_driver, "No driver found"})
+ end
end.
strip(Src, Src) ->
@@ -244,3 +250,7 @@ strip(Src, Src) ->
strip([H|R], Src) ->
[H| strip(R, Src)].
+opt_error_log(false, Format, Args) ->
+ error_logger:format(Format, Args);
+opt_error_log(true, _Format, _Args) ->
+ ok.
diff --git a/lib/wx/test/wx_basic_SUITE.erl b/lib/wx/test/wx_basic_SUITE.erl
index 46c72bb453..cf17818a9d 100644
--- a/lib/wx/test/wx_basic_SUITE.erl
+++ b/lib/wx/test/wx_basic_SUITE.erl
@@ -1,7 +1,8 @@
+%% -*- coding: utf-8 -*-
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -47,7 +48,7 @@ end_per_testcase(Func,Config) ->
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [create_window, several_apps, wx_api, wx_misc,
+ [silent_start, create_window, several_apps, wx_api, wx_misc,
data_types, wx_object].
groups() ->
@@ -62,6 +63,25 @@ end_per_group(_GroupName, Config) ->
%% The test cases
+%% test silent start of wx
+silent_start(TestInfo) when is_atom(TestInfo) -> wx_test_lib:tc_info(TestInfo);
+silent_start(_Config) ->
+ ?mr(wx_ref, wx:new([])),
+ wx:destroy(),
+
+ ?mr(wx_ref, wx:new([{silent_start, true}])),
+ wx:destroy(),
+
+ ?mr(wx_ref, wx:new([{silent_start, true}, {debug, verbose}])),
+ wx:destroy(),
+
+ ?mr(wx_ref, wx:new([{silent_start, false}])),
+ wx:destroy(),
+
+ ?mr('EXIT', catch wx:new([{silent_start, foo}])),
+
+ ok.
+
%% create and test creating a window
create_window(TestInfo) when is_atom(TestInfo) -> wx_test_lib:tc_info(TestInfo);
create_window(Config) ->
@@ -109,7 +129,7 @@ wx_api(Config) ->
?m(true, wx:is_null(Wx)),
Null = ?mr(wx_ref, wx:null()),
?m(true, wx:is_null(Null)),
- Frame = ?mt(wxFrame, wxFrame:new(Wx, 1, "WX API: " ++ unicode:characters_to_list("������"))),
+ Frame = ?mt(wxFrame, wxFrame:new(Wx, 1, "WX API: " ++ unicode:characters_to_list("åäöÅÄÖ"))),
?m(false, wx:is_null(Frame)),
?m(wxFrame, wx:getObjectType(Frame)),
Env = ?mr(wx_env, wx:get_env()),
@@ -288,12 +308,12 @@ data_types(_Config) ->
?m({_,_}, wxWindow:getSize(Frame)),
%% DateTime
- DateTime = calendar:now_to_datetime(erlang:now()),
+ DateTime = {Date, _Time} = calendar:now_to_datetime(erlang:now()),
io:format("DateTime ~p ~n",[DateTime]),
Cal = ?mt(wxCalendarCtrl, wxCalendarCtrl:new(Frame, ?wxID_ANY, [{date,DateTime}])),
- ?m(DateTime, wxCalendarCtrl:getDate(Cal)),
+ ?m({Date,_}, wxCalendarCtrl:getDate(Cal)),
?m(true, is_boolean(wxCalendarCtrl:setDate(Cal,DateTime))),
- ?m(DateTime, wxCalendarCtrl:getDate(Cal)),
+ ?m({Date,_}, wxCalendarCtrl:getDate(Cal)),
wxClientDC:destroy(CDC),
%%wx_test_lib:wx_destroy(Frame,Config).
diff --git a/lib/wx/test/wx_class_SUITE.erl b/lib/wx/test/wx_class_SUITE.erl
index 6ed7243c5b..22bfa53e0a 100644
--- a/lib/wx/test/wx_class_SUITE.erl
+++ b/lib/wx/test/wx_class_SUITE.erl
@@ -82,13 +82,14 @@ calendarCtrl(Config) ->
true ->
?log("DateAttr is null~n",[]);
false ->
- ?log("DateAttr is useable~n",[])
+ ?log("DateAttr is useable~n",[]),
+ DateAttr = ?mt(wxCalendarDateAttr, wxCalendarDateAttr:new()),
+ wxCalendarDateAttr:setBackgroundColour(DateAttr, {0,243,0}),
+ wxCalendarCtrl:setAttr(Cal, Day, DateAttr),
+ DateAttr1 = ?mt(wxCalendarDateAttr, wxCalendarCtrl:getAttr(Cal,Day)),
+ io:format("DateAttr ~p~n",[DateAttr1]),
+ ?m({0,243,0,255}, wxCalendarDateAttr:getBackgroundColour(DateAttr1))
end,
- DateAttr = ?mt(wxCalendarDateAttr, wxCalendarDateAttr:new()),
- wxCalendarDateAttr:setBackgroundColour(DateAttr, {0,243,0}),
- wxCalendarCtrl:setAttr(Cal, Day, DateAttr),
- DateAttr1 = ?mt(wxCalendarDateAttr, wxCalendarCtrl:getAttr(Cal,Day)),
- ?m({0,243,0,255}, wxCalendarDateAttr:getBackgroundColour(DateAttr1)),
?m({YMD, _},wxCalendarCtrl:getDate(Cal)),
@@ -476,7 +477,9 @@ taskBarIcon(Config) ->
Wx = wx:new(),
Frame = wxFrame:new(Wx, ?wxID_ANY, "Frame"),
TBI = wxTaskBarIcon:new(),
- Icon = wxIcon:new(filename:join(code:priv_dir(debugger), "erlang_bug.png")),
+ Image = wxImage:new(filename:join(code:priv_dir(debugger), "erlang_bug.png")),
+ io:format("Image ~p~n",[wxImage:ok(Image)]),
+ Icon = wxIcon:new(filename:join(code:priv_dir(debugger), "erlang_bug.png"), [{type, ?wxBITMAP_TYPE_PNG}]),
wxTaskBarIcon:setIcon(TBI, Icon, [{tooltip, "Testing wxTaskBarIcon"}]),
wxWindow:show(Frame),
wxTaskBarIcon:connect(TBI, taskbar_left_down, [{callback, fun(Ev,_) -> io:format("Left clicked: ~p~n",[Ev]) end}]),
diff --git a/lib/wx/test/wx_event_SUITE.erl b/lib/wx/test/wx_event_SUITE.erl
index 8f364049b4..53a2ee7d7b 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-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -301,35 +301,70 @@ connect_in_callback(TestInfo)
when is_atom(TestInfo) -> wx_test_lib:tc_info(TestInfo);
connect_in_callback(Config) ->
Wx = ?mr(wx_ref, wx:new()),
+ %% wx:debug([driver,trace]),
+ %% io:format("gdb -p ~s~n",[os:getpid()]),
Frame = wxFrame:new(Wx, ?wxID_ANY, "Connect in callback"),
Panel = wxPanel:new(Frame, []),
-
+
+ Tester = self(),
+
+ %% Connect in callbacks works different in 2.9
+ %% such that new events are not fired until the previous
+ %% callback have returned.
+
+ %% That means that a callback can not wait for other events
+ %% in receive since they will not come.
+ %% It also means that you can not attach a new callback directly from
+ %% the callback since that callback will be removed when the temporary
+ %% process that executes the outer callback (may) die(s) before the callback
+ %% is invoked
+
+ %% Thus connect in callbacks needs to done in a another process, and
+ %% not in the fun directly
+ Env = wx:get_env(),
+ TestWindow =
+ fun() ->
+ wx:set_env(Env),
+ Me = self(),
+ F1 = wxFrame:new(Frame, ?wxID_ANY, "Frame size event"),
+ wxFrame:connect(F1,size,[{callback,
+ fun(_,_) ->
+ io:format("CB2 got size~n",[]),
+ Me ! {continue, F1}
+ end}]),
+ wxWindow:show(F1),
+ receive
+ {continue, F1} -> Tester ! {continue, F1}
+ end
+ end,
wxFrame:connect(Frame,size,
- [{callback,
- fun(#wx{event=#wxSize{}},_SizeEv) ->
- io:format("Frame got size~n",[]),
- F1 = wxFrame:new(Frame, ?wxID_ANY, "Frame size event"),
- CBPid = self(),
- wxFrame:connect(F1,size,[{callback,
- fun(_,_) ->
- io:format("CB2 got size~n",[]),
- CBPid ! continue
- end}]),
- wxWindow:show(F1),
- receive continue -> wxFrame:destroy(F1) end
+ [{callback,
+ fun(#wx{event=#wxSize{}},_SizeEv) ->
+ io:format("Frame got size~n",[]),
+ spawn(TestWindow)
end}]),
wxPanel:connect(Panel,size,
- [{callback,
- fun(#wx{event=#wxSize{}},_SizeEv) ->
+ [{callback,
+ fun(#wx{event=#wxSize{}},_SizeEv) ->
io:format("Panel got size~n",[]),
- F1 = wxFrame:new(Frame, ?wxID_ANY, "Panel size event"),
- wxFrame:connect(F1,size),
- wxWindow:show(F1),
- receive #wx{event=#wxSize{}} -> wxFrame:destroy(F1) end
- end}]),
+ spawn(fun() ->
+ wx:set_env(Env),
+ F1 = wxFrame:new(Frame, ?wxID_ANY,
+ "Panel size event"),
+ wxFrame:connect(F1,size),
+ wxWindow:show(F1),
+ receive
+ #wx{event=#wxSize{}} ->
+ io:format("All Fine ~n",[]),
+ wxFrame:destroy(F1)
+ end
+ end)
+ end}]),
wxFrame:show(Frame),
+
+ ok = receive {continue, F1} -> wxFrame:destroy(F1)
+ after 5000 -> timeout end,
wx_test_lib:flush(),
-
wx_test_lib:wx_destroy(Frame, Config).
%% Test that event callback which triggers another callback works
diff --git a/lib/wx/test/wx_opengl_SUITE.erl b/lib/wx/test/wx_opengl_SUITE.erl
index e8fdf603d6..f351bc93ed 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-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -92,10 +92,22 @@ canvas(TestInfo) when is_atom(TestInfo) -> wx_test_lib:tc_info(TestInfo);
canvas(Config) ->
WX = ?mr(wx_ref, wx:new()),
Frame = wxFrame:new(WX,1,"Hello 3D-World",[]),
- Attrs = [{attribList, [?WX_GL_RGBA,?WX_GL_DOUBLEBUFFER,0]}],
- Canvas = ?mt(wxGLCanvas, wxGLCanvas:new(Frame, Attrs)),
+ Attrs = [{attribList, [?WX_GL_RGBA,
+ ?WX_GL_DOUBLEBUFFER,
+ ?WX_GL_MIN_RED,8,
+ ?WX_GL_MIN_GREEN,8,
+ ?WX_GL_MIN_BLUE,8,
+ ?WX_GL_DEPTH_SIZE,24,0]}],
+ Canvas = ?mt(wxGLCanvas, wxGLCanvas:new(Frame, [{style,?wxFULL_REPAINT_ON_RESIZE}|
+ Attrs])),
+ wxFrame:connect(Frame, show),
?m(true, wxWindow:show(Frame)),
+
+ receive #wx{event=#wxShow{}} -> ok
+ after 1000 -> exit(show_timeout)
+ end,
+
?m(false, wx:is_null(wxGLCanvas:getContext(Canvas))),
?m({'EXIT', {{error, no_gl_context,_},_}}, gl:getString(?GL_VENDOR)),
@@ -111,7 +123,7 @@ canvas(Config) ->
%%gl:frustum( -2.0, 2.0, -2.0, 2.0, 5.0, 25.0 ),
gl:ortho( -2.0, 2.0, -2.0*H/W, 2.0*H/W, -20.0, 20.0),
gl:matrixMode(?GL_MODELVIEW),
- gl:loadIdentity(),
+ gl:loadIdentity(),
gl:enable(?GL_DEPTH_TEST),
gl:depthFunc(?GL_LESS),
{R,G,B,_} = wxWindow:getBackgroundColour(Frame),
@@ -122,7 +134,7 @@ canvas(Config) ->
?m([], flush()),
Env = wx:get_env(),
Tester = self(),
- spawn_link(fun() ->
+ spawn_link(fun() ->
wx:set_env(Env),
?m(ok, wxGLCanvas:setCurrent(Canvas)),
?m(ok, drawBox(1, Data)),
@@ -131,11 +143,20 @@ canvas(Config) ->
%% This may fail when window is deleted
catch draw_loop(2,Data,Canvas)
end),
+ %% Needed on mac with wx-2.9
+ wxGLCanvas:connect(Canvas, paint,
+ [{callback, fun(_,_) ->
+ wxGLCanvas:setCurrent(Canvas),
+ DC= wxPaintDC:new(Canvas),
+ wxPaintDC:destroy(DC)
+ end}]),
+
+
?m_receive(works),
?m([], flush()),
io:format("Undef func ~p ~n", [catch gl:uniform1d(2, 0.75)]),
timer:sleep(500),
- ?m([], flush()),
+ flush(),
wx_test_lib:wx_destroy(Frame, Config).
flush() ->
@@ -150,6 +171,8 @@ flush(Collected) ->
draw_loop(Deg,Data,Canvas) ->
timer:sleep(15),
+ {NW,NH} = wxGLCanvas:getClientSize(Canvas),
+ gl:viewport(0,0,NW,NH),
drawBox(Deg,Data),
?m(ok, wxGLCanvas:swapBuffers(Canvas)),
draw_loop(Deg+1, Data,Canvas).
@@ -181,7 +204,12 @@ glu_tesselation(Config) ->
Frame = wxFrame:new(WX,1,"Hello 3D-World",[]),
Attrs = [{attribList, [?WX_GL_RGBA,?WX_GL_DOUBLEBUFFER,0]}],
Canvas = ?mt(wxGLCanvas, wxGLCanvas:new(Frame, Attrs)),
+ wxFrame:connect(Frame, show),
?m(true, wxWindow:show(Frame)),
+
+ receive #wx{event=#wxShow{}} -> ok
+ after 1000 -> exit(show_timeout)
+ end,
?m(ok, wxGLCanvas:setCurrent(Canvas)),
{RL1,RB1} = ?m({_,_}, glu:tesselate({0,0,1}, [{-1,0,0},{1,0,0},{0,1,0}])),
diff --git a/lib/wx/test/wx_xtra_SUITE.erl b/lib/wx/test/wx_xtra_SUITE.erl
index 02a0672594..a2d4c26319 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-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -111,6 +111,7 @@ app_dies(_Config) ->
oops(Die,?LINE),
wxFrame:show(Frame),
oops(Die,?LINE),
+ timer:sleep(100), %% Let the window be shown before DC can be created
DC0 = wxClientDC:new(Win),
oops(Die,?LINE),
DC = wxBufferedDC:new(DC0),
@@ -134,7 +135,7 @@ app_dies2(Test, N) ->
end.
oops(Die, Line) when (Die =:= last) orelse (Die =< Line) ->
- timer:sleep(500),
+ timer:sleep(300),
?log(" Exits at line ~p~n",[Line]),
exit({oops, Die});
oops(_,_) -> ok.
@@ -191,8 +192,12 @@ menu_item_debug(Config) ->
wxFrame:connect(Frame, close_window),
wxPanel:new(Frame),
- create_menus(Frame),
+ MenuBar = create_menus(Frame),
wxWindow:show(Frame),
+ N = wxMenuBar:getMenuCount(MenuBar),
+ io:format("No of menus ~p~n",[N]),
+ [io:format("Menu ~p ~p~n",[Id, wxMenuBar:getLabelTop(MenuBar, Id)])
+ || Id <- lists:seq(0, N-1)],
wx_test_lib:wx_destroy(Frame,Config).
@@ -224,14 +229,15 @@ create_menus(Frame) ->
|| Id <- lists:seq(600, 620)],
?m(ok,wxFrame:connect(Frame, command_menu_selected)),
- ?m(true, wxMenuBar:append(MenuBar, File, "&File")),
- ?m(true, wxMenuBar:append(MenuBar, Help, "&Help")),
- ?m(true, wxMenuBar:append(MenuBar, T1, "T1")),
- ?m(true, wxMenuBar:append(MenuBar, T2, "T2")),
- ?m(true, wxMenuBar:append(MenuBar, T3, "T3")),
- ?m(true, wxMenuBar:append(MenuBar, T4, "T4")),
- ?m(true, wxMenuBar:append(MenuBar, T5, "T5")),
- ?m(true, wxMenuBar:append(MenuBar, T6, "T6")),
-
- ?m(ok, wxFrame:setMenuBar(Frame,MenuBar)).
+ ?m(true, wxMenuBar:insert(MenuBar, 0,File, "&File")),
+ ?m(true, wxMenuBar:insert(MenuBar, 1,Help, "&Help")),
+ ?m(true, wxMenuBar:insert(MenuBar, 2,T1, "T1")),
+ ?m(true, wxMenuBar:insert(MenuBar, 3,T2, "T2")),
+ ?m(true, wxMenuBar:insert(MenuBar, 4,T3, "T3")),
+ ?m(true, wxMenuBar:insert(MenuBar, 5,T4, "T4")),
+ ?m(true, wxMenuBar:insert(MenuBar, 6,T5, "T5")),
+ ?m(true, wxMenuBar:insert(MenuBar, 7,T6, "T6")),
+
+ ?m(ok, wxFrame:setMenuBar(Frame,MenuBar)),
+ MenuBar.
diff --git a/lib/wx/wxwin.m4 b/lib/wx/wxwin-2.8.m4
index 52c55e2e6e..52c55e2e6e 100644
--- a/lib/wx/wxwin.m4
+++ b/lib/wx/wxwin-2.8.m4
diff --git a/lib/wx/wxwin-2.9.m4 b/lib/wx/wxwin-2.9.m4
new file mode 100644
index 0000000000..1c50dcc272
--- /dev/null
+++ b/lib/wx/wxwin-2.9.m4
@@ -0,0 +1,1060 @@
+dnl ---------------------------------------------------------------------------
+dnl Author: wxWidgets development team,
+dnl Francesco Montorsi,
+dnl Bob McCown (Mac-testing)
+dnl Creation date: 24/11/2001
+dnl RCS-ID: $Id$
+dnl ---------------------------------------------------------------------------
+
+dnl ===========================================================================
+dnl Table of Contents of this macro file:
+dnl -------------------------------------
+dnl
+dnl SECTION A: wxWidgets main macros
+dnl - WX_CONFIG_OPTIONS
+dnl - WX_CONFIG_CHECK
+dnl - WXRC_CHECK
+dnl - WX_STANDARD_OPTIONS
+dnl - WX_CONVERT_STANDARD_OPTIONS_TO_WXCONFIG_FLAGS
+dnl - WX_DETECT_STANDARD_OPTION_VALUES
+dnl
+dnl SECTION B: wxWidgets-related utilities
+dnl - WX_LIKE_LIBNAME
+dnl - WX_ARG_ENABLE_YESNOAUTO
+dnl - WX_ARG_WITH_YESNOAUTO
+dnl
+dnl SECTION C: messages to the user
+dnl - WX_STANDARD_OPTIONS_SUMMARY_MSG
+dnl - WX_STANDARD_OPTIONS_SUMMARY_MSG_BEGIN
+dnl - WX_STANDARD_OPTIONS_SUMMARY_MSG_END
+dnl - WX_BOOLOPT_SUMMARY
+dnl
+dnl The special "WX_DEBUG_CONFIGURE" variable can be set to 1 to enable extra
+dnl debug output on stdout from these macros.
+dnl ===========================================================================
+
+
+dnl ---------------------------------------------------------------------------
+dnl Macros for wxWidgets detection. Typically used in configure.in as:
+dnl
+dnl AC_ARG_ENABLE(...)
+dnl AC_ARG_WITH(...)
+dnl ...
+dnl WX_CONFIG_OPTIONS
+dnl ...
+dnl ...
+dnl WX_CONFIG_CHECK([2.6.0], [wxWin=1])
+dnl if test "$wxWin" != 1; then
+dnl AC_MSG_ERROR([
+dnl wxWidgets must be installed on your system
+dnl but wx-config script couldn't be found.
+dnl
+dnl Please check that wx-config is in path, the directory
+dnl where wxWidgets libraries are installed (returned by
+dnl 'wx-config --libs' command) is in LD_LIBRARY_PATH or
+dnl equivalent variable and wxWidgets version is 2.3.4 or above.
+dnl ])
+dnl fi
+dnl CPPFLAGS="$CPPFLAGS $WX_CPPFLAGS"
+dnl CXXFLAGS="$CXXFLAGS $WX_CXXFLAGS_ONLY"
+dnl CFLAGS="$CFLAGS $WX_CFLAGS_ONLY"
+dnl
+dnl LIBS="$LIBS $WX_LIBS"
+dnl
+dnl If you want to support standard --enable-debug/unicode/shared options, you
+dnl may do the following:
+dnl
+dnl ...
+dnl AC_CANONICAL_SYSTEM
+dnl
+dnl # define configure options
+dnl WX_CONFIG_OPTIONS
+dnl WX_STANDARD_OPTIONS([debug,unicode,shared,toolkit,wxshared])
+dnl
+dnl # basic configure checks
+dnl ...
+dnl
+dnl # we want to always have DEBUG==WX_DEBUG and UNICODE==WX_UNICODE
+dnl WX_DEBUG=$DEBUG
+dnl WX_UNICODE=$UNICODE
+dnl
+dnl WX_CONVERT_STANDARD_OPTIONS_TO_WXCONFIG_FLAGS
+dnl WX_CONFIG_CHECK([2.8.0], [wxWin=1],,[html,core,net,base],[$WXCONFIG_FLAGS])
+dnl WX_DETECT_STANDARD_OPTION_VALUES
+dnl
+dnl # write the output files
+dnl AC_CONFIG_FILES([Makefile ...])
+dnl AC_OUTPUT
+dnl
+dnl # optional: just to show a message to the user
+dnl WX_STANDARD_OPTIONS_SUMMARY_MSG
+dnl
+dnl ---------------------------------------------------------------------------
+
+
+dnl ---------------------------------------------------------------------------
+dnl WX_CONFIG_OPTIONS
+dnl
+dnl adds support for --wx-prefix, --wx-exec-prefix, --with-wxdir and
+dnl --wx-config command line options
+dnl ---------------------------------------------------------------------------
+
+AC_DEFUN([WX_CONFIG_OPTIONS],
+[
+ AC_ARG_WITH(wxdir,
+ [ --with-wxdir=PATH Use uninstalled version of wxWidgets in PATH],
+ [ wx_config_name="$withval/wx-config"
+ wx_config_args="--inplace"])
+ AC_ARG_WITH(wx-config,
+ [ --with-wx-config=CONFIG wx-config script to use (optional)],
+ wx_config_name="$withval" )
+ AC_ARG_WITH(wx-prefix,
+ [ --with-wx-prefix=PREFIX Prefix where wxWidgets is installed (optional)],
+ wx_config_prefix="$withval", wx_config_prefix="")
+ AC_ARG_WITH(wx-exec-prefix,
+ [ --with-wx-exec-prefix=PREFIX
+ Exec prefix where wxWidgets is installed (optional)],
+ wx_config_exec_prefix="$withval", wx_config_exec_prefix="")
+])
+
+dnl Helper macro for checking if wx version is at least $1.$2.$3, set's
+dnl wx_ver_ok=yes if it is:
+AC_DEFUN([_WX_PRIVATE_CHECK_VERSION],
+[
+ wx_ver_ok=""
+ if test "x$WX_VERSION" != x ; then
+ if test $wx_config_major_version -gt $1; then
+ wx_ver_ok=yes
+ else
+ if test $wx_config_major_version -eq $1; then
+ if test $wx_config_minor_version -gt $2; then
+ wx_ver_ok=yes
+ else
+ if test $wx_config_minor_version -eq $2; then
+ if test $wx_config_micro_version -ge $3; then
+ wx_ver_ok=yes
+ fi
+ fi
+ fi
+ fi
+ fi
+ fi
+])
+
+dnl ---------------------------------------------------------------------------
+dnl WX_CONFIG_CHECK(VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND
+dnl [, WX-LIBS [, ADDITIONAL-WX-CONFIG-FLAGS]]]])
+dnl
+dnl Test for wxWidgets, and define WX_C*FLAGS, WX_LIBS and WX_LIBS_STATIC
+dnl (the latter is for static linking against wxWidgets). Set WX_CONFIG_NAME
+dnl environment variable to override the default name of the wx-config script
+dnl to use. Set WX_CONFIG_PATH to specify the full path to wx-config - in this
+dnl case the macro won't even waste time on tests for its existence.
+dnl
+dnl Optional WX-LIBS argument contains comma- or space-separated list of
+dnl wxWidgets libraries to link against. If it is not specified then WX_LIBS
+dnl and WX_LIBS_STATIC will contain flags to link with all of the core
+dnl wxWidgets libraries.
+dnl
+dnl Optional ADDITIONAL-WX-CONFIG-FLAGS argument is appended to wx-config
+dnl invocation command in present. It can be used to fine-tune lookup of
+dnl best wxWidgets build available.
+dnl
+dnl Example use:
+dnl WX_CONFIG_CHECK([2.6.0], [wxWin=1], [wxWin=0], [html,core,net]
+dnl [--unicode --debug])
+dnl ---------------------------------------------------------------------------
+
+dnl
+dnl Get the cflags and libraries from the wx-config script
+dnl
+AC_DEFUN([WX_CONFIG_CHECK],
+[
+ dnl do we have wx-config name: it can be wx-config or wxd-config or ...
+ if test x${WX_CONFIG_NAME+set} != xset ; then
+ WX_CONFIG_NAME=wx-config
+ fi
+
+ if test "x$wx_config_name" != x ; then
+ WX_CONFIG_NAME="$wx_config_name"
+ fi
+
+ dnl deal with optional prefixes
+ if test x$wx_config_exec_prefix != x ; then
+ wx_config_args="$wx_config_args --exec-prefix=$wx_config_exec_prefix"
+ WX_LOOKUP_PATH="$wx_config_exec_prefix/bin"
+ fi
+ if test x$wx_config_prefix != x ; then
+ wx_config_args="$wx_config_args --prefix=$wx_config_prefix"
+ WX_LOOKUP_PATH="$WX_LOOKUP_PATH:$wx_config_prefix/bin"
+ fi
+ if test "$cross_compiling" = "yes"; then
+ wx_config_args="$wx_config_args --host=$host_alias"
+ fi
+
+ dnl don't search the PATH if WX_CONFIG_NAME is absolute filename
+ if test -x "$WX_CONFIG_NAME" ; then
+ AC_MSG_CHECKING(for wx-config)
+ WX_CONFIG_PATH="$WX_CONFIG_NAME"
+ AC_MSG_RESULT($WX_CONFIG_PATH)
+ else
+ AC_PATH_PROG(WX_CONFIG_PATH, $WX_CONFIG_NAME, no, "$WX_LOOKUP_PATH:$PATH")
+ fi
+
+ if test "$WX_CONFIG_PATH" != "no" ; then
+ WX_VERSION=""
+
+ min_wx_version=ifelse([$1], ,2.2.1,$1)
+ if test -z "$5" ; then
+ AC_MSG_CHECKING([for wxWidgets version >= $min_wx_version])
+ else
+ AC_MSG_CHECKING([for wxWidgets version >= $min_wx_version ($5)])
+ fi
+
+ dnl don't add the libraries ($4) to this variable as this would result in
+ dnl an error when it's used with --version below
+ WX_CONFIG_WITH_ARGS="$WX_CONFIG_PATH $wx_config_args $5"
+
+ WX_VERSION=`$WX_CONFIG_WITH_ARGS --version 2>/dev/null`
+ wx_config_major_version=`echo $WX_VERSION | \
+ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'`
+ wx_config_minor_version=`echo $WX_VERSION | \
+ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'`
+ wx_config_micro_version=`echo $WX_VERSION | \
+ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'`
+
+ wx_requested_major_version=`echo $min_wx_version | \
+ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'`
+ wx_requested_minor_version=`echo $min_wx_version | \
+ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'`
+ wx_requested_micro_version=`echo $min_wx_version | \
+ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'`
+
+ _WX_PRIVATE_CHECK_VERSION([$wx_requested_major_version],
+ [$wx_requested_minor_version],
+ [$wx_requested_micro_version])
+
+ if test -n "$wx_ver_ok"; then
+ AC_MSG_RESULT(yes (version $WX_VERSION))
+ WX_LIBS=`$WX_CONFIG_WITH_ARGS --libs $4`
+
+ dnl is this even still appropriate? --static is a real option now
+ dnl and WX_CONFIG_WITH_ARGS is likely to contain it if that is
+ dnl what the user actually wants, making this redundant at best.
+ dnl For now keep it in case anyone actually used it in the past.
+ AC_MSG_CHECKING([for wxWidgets static library])
+ WX_LIBS_STATIC=`$WX_CONFIG_WITH_ARGS --static --libs $4 2>/dev/null`
+ if test "x$WX_LIBS_STATIC" = "x"; then
+ AC_MSG_RESULT(no)
+ else
+ AC_MSG_RESULT(yes)
+ fi
+
+ dnl starting with version 2.2.6 wx-config has --cppflags argument
+ wx_has_cppflags=""
+ if test $wx_config_major_version -gt 2; then
+ wx_has_cppflags=yes
+ else
+ if test $wx_config_major_version -eq 2; then
+ if test $wx_config_minor_version -gt 2; then
+ wx_has_cppflags=yes
+ else
+ if test $wx_config_minor_version -eq 2; then
+ if test $wx_config_micro_version -ge 6; then
+ wx_has_cppflags=yes
+ fi
+ fi
+ fi
+ fi
+ fi
+
+ dnl starting with version 2.7.0 wx-config has --rescomp option
+ wx_has_rescomp=""
+ if test $wx_config_major_version -gt 2; then
+ wx_has_rescomp=yes
+ else
+ if test $wx_config_major_version -eq 2; then
+ if test $wx_config_minor_version -ge 7; then
+ wx_has_rescomp=yes
+ fi
+ fi
+ fi
+ if test "x$wx_has_rescomp" = x ; then
+ dnl cannot give any useful info for resource compiler
+ WX_RESCOMP=
+ else
+ WX_RESCOMP=`$WX_CONFIG_WITH_ARGS --rescomp`
+ fi
+
+ if test "x$wx_has_cppflags" = x ; then
+ dnl no choice but to define all flags like CFLAGS
+ WX_CFLAGS=`$WX_CONFIG_WITH_ARGS --cflags $4`
+ WX_CPPFLAGS=$WX_CFLAGS
+ WX_CXXFLAGS=$WX_CFLAGS
+
+ WX_CFLAGS_ONLY=$WX_CFLAGS
+ WX_CXXFLAGS_ONLY=$WX_CFLAGS
+ else
+ dnl we have CPPFLAGS included in CFLAGS included in CXXFLAGS
+ WX_CPPFLAGS=`$WX_CONFIG_WITH_ARGS --cppflags $4`
+ WX_CXXFLAGS=`$WX_CONFIG_WITH_ARGS --cxxflags $4`
+ WX_CFLAGS=`$WX_CONFIG_WITH_ARGS --cflags $4`
+
+ WX_CFLAGS_ONLY=`echo $WX_CFLAGS | sed "s@^$WX_CPPFLAGS *@@"`
+ WX_CXXFLAGS_ONLY=`echo $WX_CXXFLAGS | sed "s@^$WX_CFLAGS *@@"`
+ fi
+
+ ifelse([$2], , :, [$2])
+
+ else
+
+ if test "x$WX_VERSION" = x; then
+ dnl no wx-config at all
+ AC_MSG_RESULT(no)
+ else
+ AC_MSG_RESULT(no (version $WX_VERSION is not new enough))
+ fi
+
+ WX_CFLAGS=""
+ WX_CPPFLAGS=""
+ WX_CXXFLAGS=""
+ WX_LIBS=""
+ WX_LIBS_STATIC=""
+ WX_RESCOMP=""
+
+ if test ! -z "$5"; then
+
+ wx_error_message="
+ The configuration you asked for $PACKAGE_NAME requires a wxWidgets
+ build with the following settings:
+ $5
+ but such build is not available.
+
+ To see the wxWidgets builds available on this system, please use
+ 'wx-config --list' command. To use the default build, returned by
+ 'wx-config --selected-config', use the options with their 'auto'
+ default values."
+
+ fi
+
+ wx_error_message="
+ The requested wxWidgets build couldn't be found.
+ $wx_error_message
+
+ If you still get this error, then check that 'wx-config' is
+ in path, the directory where wxWidgets libraries are installed
+ (returned by 'wx-config --libs' command) is in LD_LIBRARY_PATH
+ or equivalent variable and wxWidgets version is $1 or above."
+
+ ifelse([$3], , AC_MSG_ERROR([$wx_error_message]), [$3])
+
+ fi
+ else
+
+ WX_CFLAGS=""
+ WX_CPPFLAGS=""
+ WX_CXXFLAGS=""
+ WX_LIBS=""
+ WX_LIBS_STATIC=""
+ WX_RESCOMP=""
+
+ ifelse([$3], , :, [$3])
+
+ fi
+
+ AC_SUBST(WX_CPPFLAGS)
+ AC_SUBST(WX_CFLAGS)
+ AC_SUBST(WX_CXXFLAGS)
+ AC_SUBST(WX_CFLAGS_ONLY)
+ AC_SUBST(WX_CXXFLAGS_ONLY)
+ AC_SUBST(WX_LIBS)
+ AC_SUBST(WX_LIBS_STATIC)
+ AC_SUBST(WX_VERSION)
+ AC_SUBST(WX_RESCOMP)
+
+ dnl need to export also WX_VERSION_MINOR and WX_VERSION_MAJOR symbols
+ dnl to support wxpresets bakefiles (we export also WX_VERSION_MICRO for completeness):
+ WX_VERSION_MAJOR="$wx_config_major_version"
+ WX_VERSION_MINOR="$wx_config_minor_version"
+ WX_VERSION_MICRO="$wx_config_micro_version"
+ AC_SUBST(WX_VERSION_MAJOR)
+ AC_SUBST(WX_VERSION_MINOR)
+ AC_SUBST(WX_VERSION_MICRO)
+])
+
+dnl ---------------------------------------------------------------------------
+dnl Get information on the wxrc program for making C++, Python and xrs
+dnl resource files.
+dnl
+dnl AC_ARG_ENABLE(...)
+dnl AC_ARG_WITH(...)
+dnl ...
+dnl WX_CONFIG_OPTIONS
+dnl ...
+dnl WX_CONFIG_CHECK(2.6.0, wxWin=1)
+dnl if test "$wxWin" != 1; then
+dnl AC_MSG_ERROR([
+dnl wxWidgets must be installed on your system
+dnl but wx-config script couldn't be found.
+dnl
+dnl Please check that wx-config is in path, the directory
+dnl where wxWidgets libraries are installed (returned by
+dnl 'wx-config --libs' command) is in LD_LIBRARY_PATH or
+dnl equivalent variable and wxWidgets version is 2.6.0 or above.
+dnl ])
+dnl fi
+dnl
+dnl WXRC_CHECK([HAVE_WXRC=1], [HAVE_WXRC=0])
+dnl if test "x$HAVE_WXRC" != x1; then
+dnl AC_MSG_ERROR([
+dnl The wxrc program was not installed or not found.
+dnl
+dnl Please check the wxWidgets installation.
+dnl ])
+dnl fi
+dnl
+dnl CPPFLAGS="$CPPFLAGS $WX_CPPFLAGS"
+dnl CXXFLAGS="$CXXFLAGS $WX_CXXFLAGS_ONLY"
+dnl CFLAGS="$CFLAGS $WX_CFLAGS_ONLY"
+dnl
+dnl LDFLAGS="$LDFLAGS $WX_LIBS"
+dnl ---------------------------------------------------------------------------
+
+dnl ---------------------------------------------------------------------------
+dnl WXRC_CHECK([ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]])
+dnl
+dnl Test for wxWidgets' wxrc program for creating either C++, Python or XRS
+dnl resources. The variable WXRC will be set and substituted in the configure
+dnl script and Makefiles.
+dnl
+dnl Example use:
+dnl WXRC_CHECK([wxrc=1], [wxrc=0])
+dnl ---------------------------------------------------------------------------
+
+dnl
+dnl wxrc program from the wx-config script
+dnl
+AC_DEFUN([WXRC_CHECK],
+[
+ AC_ARG_VAR([WXRC], [Path to wxWidget's wxrc resource compiler])
+
+ if test "x$WX_CONFIG_NAME" = x; then
+ AC_MSG_ERROR([The wxrc tests must run after wxWidgets test.])
+ else
+
+ AC_MSG_CHECKING([for wxrc])
+
+ if test "x$WXRC" = x ; then
+ dnl wx-config --utility is a new addition to wxWidgets:
+ _WX_PRIVATE_CHECK_VERSION(2,5,3)
+ if test -n "$wx_ver_ok"; then
+ WXRC=`$WX_CONFIG_WITH_ARGS --utility=wxrc`
+ fi
+ fi
+
+ if test "x$WXRC" = x ; then
+ AC_MSG_RESULT([not found])
+ ifelse([$2], , :, [$2])
+ else
+ AC_MSG_RESULT([$WXRC])
+ ifelse([$1], , :, [$1])
+ fi
+
+ AC_SUBST(WXRC)
+ fi
+])
+
+dnl ---------------------------------------------------------------------------
+dnl WX_LIKE_LIBNAME([output-var] [prefix], [name])
+dnl
+dnl Sets the "output-var" variable to the name of a library named with same
+dnl wxWidgets rule.
+dnl E.g. for output-var=='lib', name=='test', prefix='mine', sets
+dnl the $lib variable to:
+dnl 'mine_gtk2ud_test-2.8'
+dnl if WX_PORT=gtk2, WX_UNICODE=1, WX_DEBUG=1 and WX_RELEASE=28
+dnl ---------------------------------------------------------------------------
+AC_DEFUN([WX_LIKE_LIBNAME],
+ [
+ wx_temp="$2""_""$WX_PORT"
+
+ dnl add the [u][d] string
+ if test "$WX_UNICODE" = "1"; then
+ wx_temp="$wx_temp""u"
+ fi
+ if test "$WX_DEBUG" = "1"; then
+ wx_temp="$wx_temp""d"
+ fi
+
+ dnl complete the name of the lib
+ wx_temp="$wx_temp""_""$3""-$WX_VERSION_MAJOR.$WX_VERSION_MINOR"
+
+ dnl save it in the user's variable
+ $1=$wx_temp
+ ])
+
+dnl ---------------------------------------------------------------------------
+dnl WX_ARG_ENABLE_YESNOAUTO/WX_ARG_WITH_YESNOAUTO
+dnl
+dnl Two little custom macros which define the ENABLE/WITH configure arguments.
+dnl Macro arguments:
+dnl $1 = the name of the --enable / --with feature
+dnl $2 = the name of the variable associated
+dnl $3 = the description of that feature
+dnl $4 = the default value for that feature
+dnl $5 = additional action to do in case option is given with "yes" value
+dnl ---------------------------------------------------------------------------
+AC_DEFUN([WX_ARG_ENABLE_YESNOAUTO],
+ [AC_ARG_ENABLE($1,
+ AC_HELP_STRING([--enable-$1], [$3 (default is $4)]),
+ [], [enableval="$4"])
+
+ dnl Show a message to the user about this option
+ AC_MSG_CHECKING([for the --enable-$1 option])
+ if test "$enableval" = "yes" ; then
+ AC_MSG_RESULT([yes])
+ $2=1
+ $5
+ elif test "$enableval" = "no" ; then
+ AC_MSG_RESULT([no])
+ $2=0
+ elif test "$enableval" = "auto" ; then
+ AC_MSG_RESULT([will be automatically detected])
+ $2="auto"
+ else
+ AC_MSG_ERROR([
+ Unrecognized option value (allowed values: yes, no, auto)
+ ])
+ fi
+ ])
+
+AC_DEFUN([WX_ARG_WITH_YESNOAUTO],
+ [AC_ARG_WITH($1,
+ AC_HELP_STRING([--with-$1], [$3 (default is $4)]),
+ [], [withval="$4"])
+
+ dnl Show a message to the user about this option
+ AC_MSG_CHECKING([for the --with-$1 option])
+ if test "$withval" = "yes" ; then
+ AC_MSG_RESULT([yes])
+ $2=1
+ $5
+ dnl NB: by default we don't allow --with-$1=no option
+ dnl since it does not make much sense !
+ elif test "$6" = "1" -a "$withval" = "no" ; then
+ AC_MSG_RESULT([no])
+ $2=0
+ elif test "$withval" = "auto" ; then
+ AC_MSG_RESULT([will be automatically detected])
+ $2="auto"
+ else
+ AC_MSG_ERROR([
+ Unrecognized option value (allowed values: yes, auto)
+ ])
+ fi
+ ])
+
+
+dnl ---------------------------------------------------------------------------
+dnl WX_STANDARD_OPTIONS([options-to-add])
+dnl
+dnl Adds to the configure script one or more of the following options:
+dnl --enable-[debug|unicode|shared|wxshared|wxdebug]
+dnl --with-[gtk|msw|motif|x11|mac|dfb]
+dnl --with-wxversion
+dnl Then checks for their presence and eventually set the DEBUG, UNICODE, SHARED,
+dnl PORT, WX_SHARED, WX_DEBUG, variables to one of the "yes", "no", "auto" values.
+dnl
+dnl Note that e.g. UNICODE != WX_UNICODE; the first is the value of the
+dnl --enable-unicode option (in boolean format) while the second indicates
+dnl if wxWidgets was built in Unicode mode (and still is in boolean format).
+dnl ---------------------------------------------------------------------------
+AC_DEFUN([WX_STANDARD_OPTIONS],
+ [
+
+ dnl the following lines will expand to WX_ARG_ENABLE_YESNOAUTO calls if and only if
+ dnl the $1 argument contains respectively the debug,unicode or shared options.
+
+ dnl be careful here not to set debug flag if only "wxdebug" was specified
+ ifelse(regexp([$1], [\bdebug]), [-1],,
+ [WX_ARG_ENABLE_YESNOAUTO([debug], [DEBUG], [Build in debug mode], [auto])])
+
+ ifelse(index([$1], [unicode]), [-1],,
+ [WX_ARG_ENABLE_YESNOAUTO([unicode], [UNICODE], [Build in Unicode mode], [auto])])
+
+ ifelse(regexp([$1], [\bshared]), [-1],,
+ [WX_ARG_ENABLE_YESNOAUTO([shared], [SHARED], [Build as shared library], [auto])])
+
+ dnl WX_ARG_WITH_YESNOAUTO cannot be used for --with-toolkit since it's an option
+ dnl which must be able to accept the auto|gtk1|gtk2|msw|... values
+ ifelse(index([$1], [toolkit]), [-1],,
+ [
+ AC_ARG_WITH([toolkit],
+ AC_HELP_STRING([--with-toolkit],
+ [Build against a specific wxWidgets toolkit (default is auto)]),
+ [], [withval="auto"])
+
+ dnl Show a message to the user about this option
+ AC_MSG_CHECKING([for the --with-toolkit option])
+ if test "$withval" = "auto" ; then
+ AC_MSG_RESULT([will be automatically detected])
+ TOOLKIT="auto"
+ else
+ TOOLKIT="$withval"
+
+ dnl PORT must be one of the allowed values
+ if test "$TOOLKIT" != "gtk1" -a "$TOOLKIT" != "gtk2" -a \
+ "$TOOLKIT" != "msw" -a "$TOOLKIT" != "motif" -a \
+ "$TOOLKIT" != "osx_carbon" -a "$TOOLKIT" != "osx_cocoa" -a \
+ "$TOOLKIT" != "dfb" -a "$TOOLKIT" != "x11"; then
+ AC_MSG_ERROR([
+ Unrecognized option value (allowed values: auto, gtk1, gtk2, msw, motif, osx_carbon, osx_cocoa, dfb, x11)
+ ])
+ fi
+
+ AC_MSG_RESULT([$TOOLKIT])
+ fi
+ ])
+
+ dnl ****** IMPORTANT *******
+ dnl Unlike for the UNICODE setting, you can build your program in
+ dnl shared mode against a static build of wxWidgets. Thus we have the
+ dnl following option which allows these mixtures. E.g.
+ dnl
+ dnl ./configure --disable-shared --with-wxshared
+ dnl
+ dnl will build your library in static mode against the first available
+ dnl shared build of wxWidgets.
+ dnl
+ dnl Note that's not possible to do the viceversa:
+ dnl
+ dnl ./configure --enable-shared --without-wxshared
+ dnl
+ dnl Doing so you would try to build your library in shared mode against a static
+ dnl build of wxWidgets. This is not possible (you would mix PIC and non PIC code) !
+ dnl A check for this combination of options is in WX_DETECT_STANDARD_OPTION_VALUES
+ dnl (where we know what 'auto' should be expanded to).
+ dnl
+ dnl If you try to build something in ANSI mode against a UNICODE build
+ dnl of wxWidgets or in RELEASE mode against a DEBUG build of wxWidgets,
+ dnl then at best you'll get ton of linking errors !
+ dnl ************************
+
+ ifelse(index([$1], [wxshared]), [-1],,
+ [
+ WX_ARG_WITH_YESNOAUTO(
+ [wxshared], [WX_SHARED],
+ [Force building against a shared build of wxWidgets, even if --disable-shared is given],
+ [auto], [], [1])
+ ])
+
+ dnl Just like for SHARED and WX_SHARED it may happen that some adventurous
+ dnl peoples will want to mix a wxWidgets release build with a debug build of
+ dnl his app/lib. So, we have both DEBUG and WX_DEBUG variables.
+ ifelse(index([$1], [wxdebug]), [-1],,
+ [
+ WX_ARG_WITH_YESNOAUTO(
+ [wxdebug], [WX_DEBUG],
+ [Force building against a debug build of wxWidgets, even if --disable-debug is given],
+ [auto], [], [1])
+ ])
+
+ dnl WX_ARG_WITH_YESNOAUTO cannot be used for --with-wxversion since it's an option
+ dnl which accepts the "auto|2.6|2.7|2.8|2.9|3.0" etc etc values
+ ifelse(index([$1], [wxversion]), [-1],,
+ [
+ AC_ARG_WITH([wxversion],
+ AC_HELP_STRING([--with-wxversion],
+ [Build against a specific version of wxWidgets (default is auto)]),
+ [], [withval="auto"])
+
+ dnl Show a message to the user about this option
+ AC_MSG_CHECKING([for the --with-wxversion option])
+ if test "$withval" = "auto" ; then
+ AC_MSG_RESULT([will be automatically detected])
+ WX_RELEASE="auto"
+ else
+
+ wx_requested_major_version=`echo $withval | \
+ sed 's/\([[0-9]]*\).\([[0-9]]*\).*/\1/'`
+ wx_requested_minor_version=`echo $withval | \
+ sed 's/\([[0-9]]*\).\([[0-9]]*\).*/\2/'`
+
+ dnl both vars above must be exactly 1 digit
+ if test "${#wx_requested_major_version}" != "1" -o \
+ "${#wx_requested_minor_version}" != "1" ; then
+ AC_MSG_ERROR([
+ Unrecognized option value (allowed values: auto, 2.6, 2.7, 2.8, 2.9, 3.0)
+ ])
+ fi
+
+ WX_RELEASE="$wx_requested_major_version"".""$wx_requested_minor_version"
+ AC_MSG_RESULT([$WX_RELEASE])
+ fi
+ ])
+
+ if test "$WX_DEBUG_CONFIGURE" = "1"; then
+ echo "[[dbg]] DEBUG: $DEBUG, WX_DEBUG: $WX_DEBUG"
+ echo "[[dbg]] UNICODE: $UNICODE, WX_UNICODE: $WX_UNICODE"
+ echo "[[dbg]] SHARED: $SHARED, WX_SHARED: $WX_SHARED"
+ echo "[[dbg]] TOOLKIT: $TOOLKIT, WX_TOOLKIT: $WX_TOOLKIT"
+ echo "[[dbg]] VERSION: $VERSION, WX_RELEASE: $WX_RELEASE"
+ fi
+ ])
+
+
+dnl ---------------------------------------------------------------------------
+dnl WX_CONVERT_STANDARD_OPTIONS_TO_WXCONFIG_FLAGS
+dnl
+dnl Sets the WXCONFIG_FLAGS string using the SHARED,DEBUG,UNICODE variable values
+dnl which are different from "auto".
+dnl Thus this macro needs to be called only once all options have been set.
+dnl ---------------------------------------------------------------------------
+AC_DEFUN([WX_CONVERT_STANDARD_OPTIONS_TO_WXCONFIG_FLAGS],
+ [
+ if test "$WX_SHARED" = "1" ; then
+ WXCONFIG_FLAGS="--static=no "
+ elif test "$WX_SHARED" = "0" ; then
+ WXCONFIG_FLAGS="--static=yes "
+ fi
+
+ if test "$WX_DEBUG" = "1" ; then
+ WXCONFIG_FLAGS="$WXCONFIG_FLAGS""--debug=yes "
+ elif test "$WX_DEBUG" = "0" ; then
+ WXCONFIG_FLAGS="$WXCONFIG_FLAGS""--debug=no "
+ fi
+
+ dnl The user should have set WX_UNICODE=UNICODE
+ if test "$WX_UNICODE" = "1" ; then
+ WXCONFIG_FLAGS="$WXCONFIG_FLAGS""--unicode=yes "
+ elif test "$WX_UNICODE" = "0" ; then
+ WXCONFIG_FLAGS="$WXCONFIG_FLAGS""--unicode=no "
+ fi
+
+ if test "$TOOLKIT" != "auto" ; then
+ WXCONFIG_FLAGS="$WXCONFIG_FLAGS""--toolkit=$TOOLKIT "
+ fi
+
+ if test "$WX_RELEASE" != "auto" ; then
+ WXCONFIG_FLAGS="$WXCONFIG_FLAGS""--version=$WX_RELEASE "
+ fi
+
+ dnl strip out the last space of the string
+ WXCONFIG_FLAGS=${WXCONFIG_FLAGS% }
+
+ if test "$WX_DEBUG_CONFIGURE" = "1"; then
+ echo "[[dbg]] WXCONFIG_FLAGS: $WXCONFIG_FLAGS"
+ fi
+ ])
+
+
+dnl ---------------------------------------------------------------------------
+dnl _WX_SELECTEDCONFIG_CHECKFOR([RESULTVAR], [STRING], [MSG]
+dnl [, ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]])
+dnl
+dnl Outputs the given MSG. Then searches the given STRING in the wxWidgets
+dnl additional CPP flags and put the result of the search in WX_$RESULTVAR
+dnl also adding the "yes" or "no" message result to MSG.
+dnl ---------------------------------------------------------------------------
+AC_DEFUN([_WX_SELECTEDCONFIG_CHECKFOR],
+ [
+ if test "$$1" = "auto" ; then
+
+ dnl The user does not have particular preferences for this option;
+ dnl so we will detect the wxWidgets relative build setting and use it
+ AC_MSG_CHECKING([$3])
+
+ dnl set WX_$1 variable to 1 if the $WX_SELECTEDCONFIG contains the $2
+ dnl string or to 0 otherwise.
+ dnl NOTE: 'expr match STRING REGEXP' cannot be used since on Mac it
+ dnl doesn't work; we use 'expr STRING : REGEXP' instead
+ WX_$1=$(expr "$WX_SELECTEDCONFIG" : ".*$2.*")
+
+ if test "$WX_$1" != "0"; then
+ WX_$1=1
+ AC_MSG_RESULT([yes])
+ ifelse([$4], , :, [$4])
+ else
+ WX_$1=0
+ AC_MSG_RESULT([no])
+ ifelse([$5], , :, [$5])
+ fi
+ else
+
+ dnl Use the setting given by the user
+ WX_$1=$$1
+ fi
+ ])
+
+dnl ---------------------------------------------------------------------------
+dnl WX_DETECT_STANDARD_OPTION_VALUES
+dnl
+dnl Detects the values of the following variables:
+dnl 1) WX_RELEASE
+dnl 2) WX_UNICODE
+dnl 3) WX_DEBUG
+dnl 4) WX_SHARED (and also WX_STATIC)
+dnl 5) WX_PORT
+dnl from the previously selected wxWidgets build; this macro in fact must be
+dnl called *after* calling the WX_CONFIG_CHECK macro.
+dnl
+dnl Note that the WX_VERSION_MAJOR, WX_VERSION_MINOR symbols are already set
+dnl by WX_CONFIG_CHECK macro
+dnl ---------------------------------------------------------------------------
+AC_DEFUN([WX_DETECT_STANDARD_OPTION_VALUES],
+ [
+ dnl IMPORTANT: WX_VERSION contains all three major.minor.micro digits,
+ dnl while WX_RELEASE only the major.minor ones.
+ WX_RELEASE="$WX_VERSION_MAJOR""$WX_VERSION_MINOR"
+ if test $WX_RELEASE -lt 26 ; then
+
+ AC_MSG_ERROR([
+ Cannot detect the wxWidgets configuration for the selected wxWidgets build
+ since its version is $WX_VERSION < 2.6.0; please install a newer
+ version of wxWidgets.
+ ])
+ fi
+
+ dnl The wx-config we are using understands the "--selected_config"
+ dnl option which returns an easy-parseable string !
+ WX_SELECTEDCONFIG=$($WX_CONFIG_WITH_ARGS --selected_config)
+
+ if test "$WX_DEBUG_CONFIGURE" = "1"; then
+ echo "[[dbg]] Using wx-config --selected-config"
+ echo "[[dbg]] WX_SELECTEDCONFIG: $WX_SELECTEDCONFIG"
+ fi
+
+
+ dnl we could test directly for WX_SHARED with a line like:
+ dnl _WX_SELECTEDCONFIG_CHECKFOR([SHARED], [shared],
+ dnl [if wxWidgets was built in SHARED mode])
+ dnl but wx-config --selected-config DOES NOT outputs the 'shared'
+ dnl word when wx was built in shared mode; it rather outputs the
+ dnl 'static' word when built in static mode.
+ if test $WX_SHARED = "1"; then
+ STATIC=0
+ elif test $WX_SHARED = "0"; then
+ STATIC=1
+ elif test $WX_SHARED = "auto"; then
+ STATIC="auto"
+ fi
+
+ dnl Now set the WX_UNICODE, WX_DEBUG, WX_STATIC variables
+ _WX_SELECTEDCONFIG_CHECKFOR([UNICODE], [unicode],
+ [if wxWidgets was built with UNICODE enabled])
+ _WX_SELECTEDCONFIG_CHECKFOR([DEBUG], [debug],
+ [if wxWidgets was built in DEBUG mode])
+ _WX_SELECTEDCONFIG_CHECKFOR([STATIC], [static],
+ [if wxWidgets was built in STATIC mode])
+
+ dnl init WX_SHARED from WX_STATIC
+ if test "$WX_STATIC" != "0"; then
+ WX_SHARED=0
+ else
+ WX_SHARED=1
+ fi
+
+ AC_SUBST(WX_UNICODE)
+ AC_SUBST(WX_DEBUG)
+ AC_SUBST(WX_SHARED)
+
+ dnl detect the WX_PORT to use
+ if test "$TOOLKIT" = "auto" ; then
+
+ dnl The user does not have particular preferences for this option;
+ dnl so we will detect the wxWidgets relative build setting and use it
+ AC_MSG_CHECKING([which wxWidgets toolkit was selected])
+
+ WX_GTKPORT1=$(expr "$WX_SELECTEDCONFIG" : ".*gtk1.*")
+ WX_GTKPORT2=$(expr "$WX_SELECTEDCONFIG" : ".*gtk2.*")
+ WX_MSWPORT=$(expr "$WX_SELECTEDCONFIG" : ".*msw.*")
+ WX_MOTIFPORT=$(expr "$WX_SELECTEDCONFIG" : ".*motif.*")
+ WX_OSXCOCOAPORT=$(expr "$WX_SELECTEDCONFIG" : ".*osx_cocoa.*")
+ WX_OSXCARBONPORT=$(expr "$WX_SELECTEDCONFIG" : ".*osx_carbon.*")
+ WX_X11PORT=$(expr "$WX_SELECTEDCONFIG" : ".*x11.*")
+ WX_DFBPORT=$(expr "$WX_SELECTEDCONFIG" : ".*dfb.*")
+
+ WX_PORT="unknown"
+ if test "$WX_GTKPORT1" != "0"; then WX_PORT="gtk1"; fi
+ if test "$WX_GTKPORT2" != "0"; then WX_PORT="gtk2"; fi
+ if test "$WX_MSWPORT" != "0"; then WX_PORT="msw"; fi
+ if test "$WX_MOTIFPORT" != "0"; then WX_PORT="motif"; fi
+ if test "$WX_OSXCOCOAPORT" != "0"; then WX_PORT="osx_cocoa"; fi
+ if test "$WX_OSXCARBONPORT" != "0"; then WX_PORT="osx_carbon"; fi
+ if test "$WX_X11PORT" != "0"; then WX_PORT="x11"; fi
+ if test "$WX_DFBPORT" != "0"; then WX_PORT="dfb"; fi
+
+ dnl NOTE: backward-compatible check for wx2.8; in wx2.9 the mac
+ dnl ports are called 'osx_cocoa' and 'osx_carbon' (see above)
+ WX_MACPORT=$(expr "$WX_SELECTEDCONFIG" : ".*mac.*")
+ if test "$WX_MACPORT" != "0"; then WX_PORT="mac"; fi
+
+ dnl check at least one of the WX_*PORT has been set !
+
+ if test "$WX_PORT" = "unknown" ; then
+ AC_MSG_ERROR([
+ Cannot detect the currently installed wxWidgets port !
+ Please check your 'wx-config --cxxflags'...
+ ])
+ fi
+
+ AC_MSG_RESULT([$WX_PORT])
+ else
+
+ dnl Use the setting given by the user
+ if test -z "$TOOLKIT" ; then
+ WX_PORT=$TOOLKIT
+ else
+ dnl try with PORT
+ WX_PORT=$PORT
+ fi
+ fi
+
+ AC_SUBST(WX_PORT)
+
+ if test "$WX_DEBUG_CONFIGURE" = "1"; then
+ echo "[[dbg]] Values of all WX_* options after final detection:"
+ echo "[[dbg]] WX_DEBUG: $WX_DEBUG"
+ echo "[[dbg]] WX_UNICODE: $WX_UNICODE"
+ echo "[[dbg]] WX_SHARED: $WX_SHARED"
+ echo "[[dbg]] WX_RELEASE: $WX_RELEASE"
+ echo "[[dbg]] WX_PORT: $WX_PORT"
+ fi
+
+ dnl Avoid problem described in the WX_STANDARD_OPTIONS which happens when
+ dnl the user gives the options:
+ dnl ./configure --enable-shared --without-wxshared
+ dnl or just do
+ dnl ./configure --enable-shared
+ dnl but there is only a static build of wxWidgets available.
+ if test "$WX_SHARED" = "0" -a "$SHARED" = "1"; then
+ AC_MSG_ERROR([
+ Cannot build shared library against a static build of wxWidgets !
+ This error happens because the wxWidgets build which was selected
+ has been detected as static while you asked to build $PACKAGE_NAME
+ as shared library and this is not possible.
+ Use the '--disable-shared' option to build $PACKAGE_NAME
+ as static library or '--with-wxshared' to use wxWidgets as shared library.
+ ])
+ fi
+
+ dnl now we can finally update the DEBUG,UNICODE,SHARED options
+ dnl to their final values if they were set to 'auto'
+ if test "$DEBUG" = "auto"; then
+ DEBUG=$WX_DEBUG
+ fi
+ if test "$UNICODE" = "auto"; then
+ UNICODE=$WX_UNICODE
+ fi
+ if test "$SHARED" = "auto"; then
+ SHARED=$WX_SHARED
+ fi
+ if test "$TOOLKIT" = "auto"; then
+ TOOLKIT=$WX_PORT
+ fi
+
+ dnl in case the user needs a BUILD=debug/release var...
+ if test "$DEBUG" = "1"; then
+ BUILD="debug"
+ elif test "$DEBUG" = "0" -o "$DEBUG" = ""; then
+ BUILD="release"
+ fi
+
+ dnl respect the DEBUG variable adding the optimize/debug flags
+ dnl NOTE: the CXXFLAGS are merged together with the CPPFLAGS so we
+ dnl don't need to set them, too
+ if test "$DEBUG" = "1"; then
+ CXXFLAGS="$CXXFLAGS -g -O0"
+ CFLAGS="$CFLAGS -g -O0"
+ else
+ CXXFLAGS="$CXXFLAGS -O2"
+ CFLAGS="$CFLAGS -O2"
+ fi
+ ])
+
+dnl ---------------------------------------------------------------------------
+dnl WX_BOOLOPT_SUMMARY([name of the boolean variable to show summary for],
+dnl [what to print when var is 1],
+dnl [what to print when var is 0])
+dnl
+dnl Prints $2 when variable $1 == 1 and prints $3 when variable $1 == 0.
+dnl This macro mainly exists just to make configure.ac scripts more readable.
+dnl
+dnl NOTE: you need to use the [" my message"] syntax for 2nd and 3rd arguments
+dnl if you want that m4 avoid to throw away the spaces prefixed to the
+dnl argument value.
+dnl ---------------------------------------------------------------------------
+AC_DEFUN([WX_BOOLOPT_SUMMARY],
+ [
+ if test "x$$1" = "x1" ; then
+ echo $2
+ elif test "x$$1" = "x0" ; then
+ echo $3
+ else
+ echo "$1 is $$1"
+ fi
+ ])
+
+dnl ---------------------------------------------------------------------------
+dnl WX_STANDARD_OPTIONS_SUMMARY_MSG
+dnl
+dnl Shows a summary message to the user about the WX_* variable contents.
+dnl This macro is used typically at the end of the configure script.
+dnl ---------------------------------------------------------------------------
+AC_DEFUN([WX_STANDARD_OPTIONS_SUMMARY_MSG],
+ [
+ echo
+ echo " The wxWidgets build which will be used by $PACKAGE_NAME $PACKAGE_VERSION"
+ echo " has the following settings:"
+ WX_BOOLOPT_SUMMARY([WX_DEBUG], [" - DEBUG build"], [" - RELEASE build"])
+ WX_BOOLOPT_SUMMARY([WX_UNICODE], [" - UNICODE mode"], [" - ANSI mode"])
+ WX_BOOLOPT_SUMMARY([WX_SHARED], [" - SHARED mode"], [" - STATIC mode"])
+ echo " - VERSION: $WX_VERSION"
+ echo " - PORT: $WX_PORT"
+ ])
+
+
+dnl ---------------------------------------------------------------------------
+dnl WX_STANDARD_OPTIONS_SUMMARY_MSG_BEGIN, WX_STANDARD_OPTIONS_SUMMARY_MSG_END
+dnl
+dnl Like WX_STANDARD_OPTIONS_SUMMARY_MSG macro but these two macros also gives info
+dnl about the configuration of the package which used the wxpresets.
+dnl
+dnl Typical usage:
+dnl WX_STANDARD_OPTIONS_SUMMARY_MSG_BEGIN
+dnl echo " - Package setting 1: $SETTING1"
+dnl echo " - Package setting 2: $SETTING1"
+dnl ...
+dnl WX_STANDARD_OPTIONS_SUMMARY_MSG_END
+dnl
+dnl ---------------------------------------------------------------------------
+AC_DEFUN([WX_STANDARD_OPTIONS_SUMMARY_MSG_BEGIN],
+ [
+ echo
+ echo " ----------------------------------------------------------------"
+ echo " Configuration for $PACKAGE_NAME $PACKAGE_VERSION successfully completed."
+ echo " Summary of main configuration settings for $PACKAGE_NAME:"
+ WX_BOOLOPT_SUMMARY([DEBUG], [" - DEBUG build"], [" - RELEASE build"])
+ WX_BOOLOPT_SUMMARY([UNICODE], [" - UNICODE mode"], [" - ANSI mode"])
+ WX_BOOLOPT_SUMMARY([SHARED], [" - SHARED mode"], [" - STATIC mode"])
+ ])
+
+AC_DEFUN([WX_STANDARD_OPTIONS_SUMMARY_MSG_END],
+ [
+ WX_STANDARD_OPTIONS_SUMMARY_MSG
+ echo
+ echo " Now, just run make."
+ echo " ----------------------------------------------------------------"
+ echo
+ ])
+
+
+dnl ---------------------------------------------------------------------------
+dnl Deprecated macro wrappers
+dnl ---------------------------------------------------------------------------
+
+AC_DEFUN([AM_OPTIONS_WXCONFIG], [WX_CONFIG_OPTIONS])
+AC_DEFUN([AM_PATH_WXCONFIG], [
+ WX_CONFIG_CHECK([$1],[$2],[$3],[$4],[$5])
+])
+AC_DEFUN([AM_PATH_WXRC], [WXRC_CHECK([$1],[$2])])
diff --git a/lib/xmerl/doc/src/Makefile b/lib/xmerl/doc/src/Makefile
index 4de06249df..6f95e30a28 100644
--- a/lib/xmerl/doc/src/Makefile
+++ b/lib/xmerl/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2004-2010. All Rights Reserved.
+# Copyright Ericsson AB 2004-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/xmerl/doc/src/motorcycles_dtd.txt b/lib/xmerl/doc/src/motorcycles_dtd.txt
index bab0d563f0..62ad4ac5fe 100644
--- a/lib/xmerl/doc/src/motorcycles_dtd.txt
+++ b/lib/xmerl/doc/src/motorcycles_dtd.txt
@@ -15,4 +15,5 @@
<!ELEMENT date (#PCDATA)>
<!ATTLIST bike year NMTOKEN #REQUIRED
color NMTOKENS #REQUIRED
- condition (useless | bad | serviceable | moderate | good | excellent | new | outstanding) "excellent" >
+ condition (useless | bad | serviceable | moderate | good |
+ excellent | new | outstanding) "excellent" >
diff --git a/lib/xmerl/doc/src/notes.xml b/lib/xmerl/doc/src/notes.xml
index bd12e688d0..056097ab2b 100644
--- a/lib/xmerl/doc/src/notes.xml
+++ b/lib/xmerl/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2004</year><year>2011</year>
+ <year>2004</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
diff --git a/lib/xmerl/src/Makefile b/lib/xmerl/src/Makefile
index ce1aa11fba..d5ce3fe6ff 100644
--- a/lib/xmerl/src/Makefile
+++ b/lib/xmerl/src/Makefile
@@ -166,34 +166,34 @@ EDOC_PATHS = \
-pa $(EDOC_APP)/ebin -pa $(XMERL_APP)/ebin -pa $(SYNTAX_TOOLS_APP)/ebin
$(APP_TARGET): $(APP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
xmerl_xpath_parse.erl: xmerl_xpath_parse.yrl
- $(ERLC) -o $(ESRC) $<
+ $(yecc_verbose)$(ERLC) -o $(ESRC) $<
xmerl_b64Bin.erl: xmerl_b64Bin.yrl
- $(ERLC) -o $(ESRC) $<
+ $(yecc_verbose)$(ERLC) -o $(ESRC) $<
xmerl_sax_parser_list.erl: xmerl_sax_parser_list.erlsrc xmerl_sax_parser_base.erlsrc
- cat xmerl_sax_parser_list.erlsrc xmerl_sax_parser_base.erlsrc >$@
+ $(gen_verbose)cat xmerl_sax_parser_list.erlsrc xmerl_sax_parser_base.erlsrc >$@
xmerl_sax_parser_latin1.erl: xmerl_sax_parser_latin1.erlsrc xmerl_sax_parser_base.erlsrc
- cat xmerl_sax_parser_latin1.erlsrc xmerl_sax_parser_base.erlsrc >$@
+ $(gen_verbose)cat xmerl_sax_parser_latin1.erlsrc xmerl_sax_parser_base.erlsrc >$@
xmerl_sax_parser_utf8.erl: xmerl_sax_parser_utf8.erlsrc xmerl_sax_parser_base.erlsrc
- cat xmerl_sax_parser_utf8.erlsrc xmerl_sax_parser_base.erlsrc >$@
+ $(gen_verbose)cat xmerl_sax_parser_utf8.erlsrc xmerl_sax_parser_base.erlsrc >$@
xmerl_sax_parser_utf16be.erl: xmerl_sax_parser_utf16be.erlsrc xmerl_sax_parser_base.erlsrc
- cat xmerl_sax_parser_utf16be.erlsrc xmerl_sax_parser_base.erlsrc >$@
+ $(gen_verbose)cat xmerl_sax_parser_utf16be.erlsrc xmerl_sax_parser_base.erlsrc >$@
xmerl_sax_parser_utf16le.erl: xmerl_sax_parser_utf16le.erlsrc xmerl_sax_parser_base.erlsrc
- cat xmerl_sax_parser_utf16le.erlsrc xmerl_sax_parser_base.erlsrc >$@
+ $(gen_verbose)cat xmerl_sax_parser_utf16le.erlsrc xmerl_sax_parser_base.erlsrc >$@
$(EBIN)/%.beam: %.erl
- $(ERLC) $(ERL_COMPILE_FLAGS) -o $(EBIN) $<
+ $(V_ERLC) $(ERL_COMPILE_FLAGS) -o $(EBIN) $<
$(DOCDIR)/%.html: %.erl
$(ERL) -noshell $(EDOC_PATHS) \
diff --git a/lib/xmerl/src/xmerl_sax_parser_base.erlsrc b/lib/xmerl/src/xmerl_sax_parser_base.erlsrc
index d38045f2a5..7b64d7c302 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-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/xmerl/src/xmerl_scan.erl b/lib/xmerl/src/xmerl_scan.erl
index 05431a5fd2..883153628c 100644
--- a/lib/xmerl/src/xmerl_scan.erl
+++ b/lib/xmerl/src/xmerl_scan.erl
@@ -1,7 +1,8 @@
+%% -*- coding: utf-8 -*-
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -461,7 +462,7 @@ hook(X, State) ->
event(_X, S) ->
S.
-%% The acc/3 function can return either {Acc�, S'} or {Acc', Pos', S'},
+%% The acc/3 function can return either {Acc´, S'} or {Acc', Pos', S'},
%% where Pos' can be derived from X#xmlElement.pos, X#xmlText.pos, or
%% X#xmlAttribute.pos (whichever is the current object type.)
%% The acc/3 function is not allowed to redefine the type of object
diff --git a/lib/xmerl/src/xmerl_xsd.erl b/lib/xmerl/src/xmerl_xsd.erl
index 78d84d23a4..ffe227c671 100644
--- a/lib/xmerl/src/xmerl_xsd.erl
+++ b/lib/xmerl/src/xmerl_xsd.erl
@@ -1,3 +1,4 @@
+%% -*- coding: utf-8 -*-
%%
%% %CopyrightBegin%
%%
@@ -849,7 +850,7 @@ element_content({import,S=#xsd_state{schema_name=ThisSchema,
SchemaLocation = get_attribute_value(schemaLocation,I,absent),
%% If SchemaLocation is absent, the identification of that schema
%% is leaved to the instance, application or user, via the
- %% mechanisms described ��4.3 in XML Schema Part 1.
+ %% mechanisms described §4.3 in XML Schema Part 1.
S2 = process_external_schema_once(SchemaLocation,Namespace,S),
{{import,[]},S2#xsd_state{schema_name=ThisSchema,
diff --git a/lib/xmerl/test/Makefile b/lib/xmerl/test/Makefile
index 1fdbb7ca0d..ac3370dbda 100644
--- a/lib/xmerl/test/Makefile
+++ b/lib/xmerl/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2004-2011. All Rights Reserved.
+# Copyright Ericsson AB 2004-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/xmerl/test/xmerl_SUITE.erl b/lib/xmerl/test/xmerl_SUITE.erl
index 55b6d1844c..e21355f877 100644
--- a/lib/xmerl/test/xmerl_SUITE.erl
+++ b/lib/xmerl/test/xmerl_SUITE.erl
@@ -1,7 +1,8 @@
+%% -*- coding: utf-8 -*-
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -531,8 +532,8 @@ ticket_7430(Config) ->
{xmlElement,a,a,[],
{xmlNamespace,[],[]},
[],1,[],
- [{xmlText,[{a,1}],1,[],"�",text},
- {xmlText,[{a,1}],2,[],"\n�",text}],
+ [{xmlText,[{a,1}],1,[],"é",text},
+ {xmlText,[{a,1}],2,[],"\né",text}],
[],_,undeclared} ->
ok;
_ ->
diff --git a/make/emd2exml.in b/make/emd2exml.in
index 16c38379d9..b677ef8ed4 100644
--- a/make/emd2exml.in
+++ b/make/emd2exml.in
@@ -266,8 +266,11 @@ parse(#state{code = true, type = blank, line = CB, code_blank = CBs} = S) ->
parse(#state{code = true,
code_blank = CB,
list_lvl = Lvl,
- type = {text, TxtLvl},
- line = Line} = S) when TxtLvl > Lvl ->
+ type = {Type, TxtLvl},
+ line = Line} = S) when TxtLvl > Lvl,
+ (Type == text orelse
+ Type == uolist orelse
+ Type == olist) ->
Data = code(strip_lvls(Lvl+1, Line)),
parse(get_line(put_chars(S#state{code_blank = []},
[strip_code_blank(Lvl+1, CB), Data])));
@@ -994,7 +997,7 @@ resolve_link([_|Cs], start, "", "", "") ->
resolve_link(Cs, start, "", "", "");
resolve_link("]:" ++ Rest, key, Yek, "", "") ->
resolve_link(Rest, url, Yek, "", "");
-resolve_link([C|Cs], url, Yek, Lru, "") when C == $"; C == $' ->
+resolve_link([C|Cs], url, Yek, Lru, "") when C == $"; C == $' -> %"
resolve_link(Cs, {title, C}, Yek, Lru, "");
resolve_link([$(|Cs], url, Yek, Lru, "") ->
resolve_link(Cs, {title, $)}, Yek, Lru, "");
@@ -1010,9 +1013,21 @@ resolve_link([_|Cs], drop, Yek, Lru, Eltit) ->
resolve_link(Cs, drop, Yek, Lru, Eltit);
resolve_link([], _, Yek, Lru, Eltit) ->
{ws_strip(lists:reverse(Yek)),
- ws_strip(lists:reverse(Lru)),
+ ws_strip(md_strip_n_reverse(Lru)),
ws_strip(lists:reverse(Eltit))}.
+%% Remove .md at end of references.
+md_strip_n_reverse(Lru) ->
+ md_strip_n_reverse(Lru,[]).
+md_strip_n_reverse("\ndm."++Lru,Acc) ->
+ md_strip_n_reverse(Lru,Acc);
+md_strip_n_reverse("#dm."++Lru,Acc) ->
+ md_strip_n_reverse(Lru,[$#|Acc]);
+md_strip_n_reverse([C|T],Acc) ->
+ md_strip_n_reverse(T,[C|Acc]);
+md_strip_n_reverse([], Acc) ->
+ Acc.
+
%%
%% Misc
%%
diff --git a/make/fakefop b/make/fakefop
index bbe81ef3b1..b64428bbd1 100755
--- a/make/fakefop
+++ b/make/fakefop
@@ -1,10 +1,9 @@
#!/bin/sh
#
-# Copyright Tuncer Ayaz 2010. All Rights Reserved.
-#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2010. All Rights Reserved.
+# Copyright Tuncer Ayaz 2010-2013. All Rights Reserved.
+# Copyright Ericsson AB 2010-2013. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -22,92 +21,85 @@
# Author: Tuncer Ayaz
#
-if [ $# -lt 4 ]
+if [ $# -lt 6 ]
then
- echo "Usage: fakefop -fo IGNORED -pdf OUTFILE"
+ echo "Usage: fakefop -c IGNORED -fo IGNORED -pdf OUTFILE"
exit 1
fi
-OUTFILE=$4
-NAME=`basename $4 .pdf`
+OUTFILE=$6
+
+echo -n -e '%PDF-1.4\n%\0342\0343\0317\0323\n\n' > $OUTFILE
-echo Write $OUTFILE
-cat > $OUTFILE <<EndOfFile
-%PDF-1.4
+cat >> $OUTFILE <<EndOfFile
1 0 obj
- << /Type /Catalog
- /Outlines 2 0 R
- /Pages 3 0 R
- >>
+<<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
endobj
2 0 obj
- << /Type /Outlines
- /Count 0
- >>
+<<
+ /Type /Pages
+ /Kids [ 3 0 R ]
+ /Count 1
+>>
endobj
3 0 obj
- << /Type /Pages
- /Kids [4 0 R]
- /Count 1
+<<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [ 0 0 612 492 ]
+ /Contents 5 0 R
+ /Resources <<
+ /Font <<
+ /F1 4 0 R
+ >>
>>
+>>
endobj
4 0 obj
- << /Type /Page
- /Parent 3 0 R
- /MediaBox [0 0 612 492]
- /Contents 5 0 R
- /Resources << /ProcSet 6 0 R
- /Font << /F1 7 0 R >>
- >>
- >>
+<<
+ /Type /Font
+ /Subtype /Type1
+ /Name /F1
+ /BaseFont /Helvetica
+ /Encoding /StandardEncoding
+>>
endobj
5 0 obj
- << /Length 73 >>
+<<
+ /Length 74
+>>
stream
- BT
- /F1 24 Tf
- 10 400 Td
- ($NAME) Tj
- ET
- BT
- /F1 24 Tf
- 10 350 Td
- (\(placeholder PDF generated without FOP\)) Tj
- ET
+BT
+/F1 24 Tf
+10 400 Td
+(\(placeholder PDF generated with fakefop\)) Tj
+ET
endstream
endobj
-6 0 obj
- [/PDF /Text]
-endobj
-7 0 obj
- << /Type /Font
- /Subtype /Type1
- /Name /F1
- /BaseFont /Helvetica
- /Encoding /MacRomanEncoding
- >>
-endobj
xref
-0 8
-0000000000 65535 f
-0000000009 00000 n
-0000000074 00000 n
-0000000120 00000 n
-0000000179 00000 n
-0000000364 00000 n
-0000000466 00000 n
-0000000496 00000 n
+0 6
+0000000000 65536 f
+0000000016 00000 n
+0000000070 00000 n
+0000000136 00000 n
+0000000291 00000 n
+0000000410 00000 n
trailer
- << /Size 8
- /Root 1 0 R
- >>
+<<
+ /Size 6
+ /Root 1 0 R
+>>
+
startxref
-625
+536
%%EOF
EndOfFile
diff --git a/make/otp.mk.in b/make/otp.mk.in
index 756cc85443..fca9cf3cff 100644
--- a/make/otp.mk.in
+++ b/make/otp.mk.in
@@ -25,6 +25,11 @@
.3 .1 .fig .dvi .tex .class .java .pdf .fo .psframe .pscrop .el .elc
# ----------------------------------------------------
+# Output
+# ----------------------------------------------------
+include $(ERL_TOP)/make/output.mk
+
+# ----------------------------------------------------
# Cross Compiling
# ----------------------------------------------------
CROSS_COMPILING = @CROSS_COMPILING@
@@ -77,19 +82,13 @@ OTP_RELEASE = @OTP_RELEASE@
# Erlang language section
# ----------------------------------------------------
EMULATOR = beam
-ifeq ($(findstring vxworks,$(TARGET)),vxworks)
-# VxWorks object files should be compressed.
-# Other object files should have debug_info.
- ERL_COMPILE_FLAGS += +compressed
+ifeq ($(findstring ose_ppc750,$(TARGET)),ose_ppc750)
+ERL_COMPILE_FLAGS += +compressed
else
- ifeq ($(findstring ose_ppc750,$(TARGET)),ose_ppc750)
- ERL_COMPILE_FLAGS += +compressed
+ ifdef BOOTSTRAP
+ ERL_COMPILE_FLAGS += +slim
else
- ifdef BOOTSTRAP
- ERL_COMPILE_FLAGS += +slim
- else
- ERL_COMPILE_FLAGS += +debug_info
- endif
+ ERL_COMPILE_FLAGS += +debug_info
endif
endif
ERLC_WFLAGS = -W
@@ -110,19 +109,19 @@ ESRC = .
endif
$(EBIN)/%.beam: $(EGEN)/%.erl
- $(ERLC) $(ERL_COMPILE_FLAGS) -o$(EBIN) $<
+ $(V_ERLC) $(ERL_COMPILE_FLAGS) -o$(EBIN) $<
$(EBIN)/%.beam: $(ESRC)/%.erl
- $(ERLC) $(ERL_COMPILE_FLAGS) -o$(EBIN) $<
+ $(V_ERLC) $(ERL_COMPILE_FLAGS) -o$(EBIN) $<
ifeq ($(NATIVE_LIBS_ENABLED),yes)
# Special rule for the HIPE bootstrap w/ native libs
../boot_ebin/%.beam: $(ESRC)/%.erl
- $(ERLC) $(ERL_COMPILE_FLAGS) -o../boot_ebin $<
+ $(V_ERLC) $(ERL_COMPILE_FLAGS) -o../boot_ebin $<
endif
.erl.beam:
- $(ERLC) $(ERL_COMPILE_FLAGS) -o$(dir $@) $<
+ $(V_ERLC) $(ERL_COMPILE_FLAGS) -o$(dir $@) $<
#
# When .erl files are automatically created GNU make removes them if
@@ -137,10 +136,10 @@ endif
## $(ERLC) $(IDL_FLAGS) $<
$(EGEN)/%.erl: $(ESRC)/%.yrl
- $(ERLC) $(YRL_FLAGS) -o$(EGEN) $<
+ $(yecc_verbose)$(ERLC) $(YRL_FLAGS) -o$(EGEN) $<
$(EGEN)/%.erl: $(ESRC)/%.xrl
- $(ERLC) $(XRL_FLAGS) -o$(EGEN) $<
+ $(leex_verbose)$(ERLC) $(XRL_FLAGS) -o$(EGEN) $<
# ----------------------------------------------------
# SNMP language section
@@ -155,16 +154,16 @@ endif
$(SNMP_BIN_TARGET_DIR)/%.bin: %.mib
- $(ERLC) -pa $(SNMP_TOOLKIT)/ebin -I $(SNMP_TOOLKIT)/priv/mibs $(SNMP_FLAGS) -o $(SNMP_BIN_TARGET_DIR) $<
+ $(snmp_verbose)$(ERLC) -pa $(SNMP_TOOLKIT)/ebin -I $(SNMP_TOOLKIT)/priv/mibs $(SNMP_FLAGS) -o $(SNMP_BIN_TARGET_DIR) $<
$(SNMP_HRL_TARGET_DIR)/%.hrl: $(SNMP_BIN_TARGET_DIR)/%.bin
- $(ERLC) -pa $(SNMP_TOOLKIT)/ebin -o $(SNMP_HRL_TARGET_DIR) $<
+ $(snmp_verbose)$(ERLC) -pa $(SNMP_TOOLKIT)/ebin -o $(SNMP_HRL_TARGET_DIR) $<
.mib.bin:
- $(ERLC) -pa $(SNMP_TOOLKIT)/ebin -I $(SNMP_TOOLKIT)/priv/mibs $(SNMP_FLAGS) $<
+ $(snmp_verbose)$(ERLC) -pa $(SNMP_TOOLKIT)/ebin -I $(SNMP_TOOLKIT)/priv/mibs $(SNMP_FLAGS) $<
.bin.hrl:
- $(ERLC) -pa $(SNMP_TOOLKIT)/ebin $<
+ $(snmp_verbose)$(ERLC) -pa $(SNMP_TOOLKIT)/ebin $<
# ----------------------------------------------------
# Java language section
@@ -176,11 +175,11 @@ JAVA_DEST_ROOT = ../priv/
endif
.java.class:
- CLASSPATH=$(CLASSPATH) $(JAVA) $(JAVA_OPTIONS) $<
+ $(javac_verbose)CLASSPATH=$(CLASSPATH) $(JAVA) $(JAVA_OPTIONS) $<
$(JAVA_DEST_ROOT)$(JAVA_CLASS_SUBDIR)%.class: %.java
- CLASSPATH=$(CLASSPATH) $(JAVA) $(JAVA_OPTIONS) -d $(JAVA_DEST_ROOT) $<
+ $(javac_verbose)CLASSPATH=$(CLASSPATH) $(JAVA) $(JAVA_OPTIONS) -d $(JAVA_DEST_ROOT) $<
# ----------------------------------------------------
# Emacs byte code compiling
@@ -189,7 +188,7 @@ EMACS_COMPILER=emacs-20
EMACS_COMPILE_OPTIONS=-q --no-site-file -batch -f batch-byte-compile
.el.elc:
- $(EMACS_COMPILER) $(EMACS_COMPILE_OPTIONS) $<
+ $(emacs_verbose)$(EMACS_COMPILER) $(EMACS_COMPILE_OPTIONS) $<
# ----------------------------------------------------
# Documentation section
@@ -215,6 +214,22 @@ TEXDIR = .
SPECDIR = $(DOCDIR)/specs
+ifeq ($(CSS_FILE),)
+CSS_FILE = otp_doc.css
+endif
+ifeq ($(WINPREFIX),)
+WINPREFIX = Erlang
+endif
+ifeq ($(HTMLLOGO),)
+HTMLLOGO_FILE = erlang-logo.png
+endif
+ifeq ($(PDFLOGO),)
+PDFLOGO_FILE = $(DOCGEN)/priv/images/erlang-logo.gif
+endif
+ifeq ($(PDFCOLOR),)
+PDFCOLOR = \#960003
+endif
+
# HTML & GIF files that always are generated and must be delivered
SGML_COLL_FILES = $(SGML_APPLICATION_FILES) $(SGML_PART_FILES)
XML_COLL_FILES = $(XML_APPLICATION_FILES) $(XML_PART_FILES)
@@ -240,8 +255,10 @@ DEFAULT_GIF_FILES = $(HTMLDIR)/min_head.gif
#
XSLTPROC = @XSLTPROC@
FOP = @FOP@
+XMLLINT = @XMLLINT@
DOCGEN=$(ERL_TOP)/lib/erl_docgen
+FOP_CONFIG = $(DOCGEN)/priv/fop.xconf
ifneq (,$(findstring $(origin SPECS_ESRC),$(DUBIOUS_ORIGINS)))
SPECS_ESRC = ../../src
@@ -256,12 +273,10 @@ $(MAN1DIR)/%.1: %.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/dtd -path $(DOCGEN)/priv/dtd_man_entities $(DOCGEN)/priv/xsl/db_man.xsl $<
-
$(MAN2DIR)/%.2: %.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/dtd -path $(DOCGEN)/priv/dtd_man_entities $(DOCGEN)/priv/xsl/db_man.xsl $<
-
ifneq ($(wildcard $(SPECDIR)),)
$(MAN3DIR)/%.3: %.xml $(SPECDIR)/specs_%.xml
date=`date +"%B %e %Y"`; \
@@ -300,5 +315,5 @@ $(MAN9DIR)/%.9: %.xml
escript $(DOCGEN)/priv/bin/codeline_preprocessing.escript $< $@
.fo.pdf:
- $(FOP) -fo $< -pdf $@
+ $(FOP) -c $(FOP_CONFIG) -fo $< -pdf $@
diff --git a/make/otp_ded.mk.in b/make/otp_ded.mk.in
index e719312473..e2232acbb1 100644
--- a/make/otp_ded.mk.in
+++ b/make/otp_ded.mk.in
@@ -37,7 +37,7 @@ DED__NOWARN_CFLAGS = @DED_EMU_THR_DEFS@ @DED_CFLAGS@
DED_THR_DEFS = @DED_THR_DEFS@
DED_EMU_THR_DEFS = @DED_EMU_THR_DEFS@
DED_WARN_FLAGS = @WFLAGS@
-DED_CFLAGS = @WFLAGS@ @DED_EMU_THR_DEFS@ @DED_CFLAGS@
+DED_CFLAGS = @WERRORFLAGS@ @WFLAGS@ @DED_EMU_THR_DEFS@ @DED_CFLAGS@
DED_LIBS = @LIBS@
DED_EXT = @DED_EXT@
ERLANG_OSTYPE = @ERLANG_OSTYPE@
diff --git a/make/otp_release_targets.mk b/make/otp_release_targets.mk
index fa87a4306a..65a2e62979 100644
--- a/make/otp_release_targets.mk
+++ b/make/otp_release_targets.mk
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -42,17 +42,34 @@ $(HTMLDIR)/index.html: $(XML_FILES) $(SPECS_FILES)
--stringparam gendate "$$date" \
--stringparam appname "$(APPLICATION)" \
--stringparam appver "$(VSN)" \
+ --stringparam stylesheet "$(CSS_FILE)" \
+ --stringparam winprefix "$(WINPREFIX)" \
+ --stringparam logo "$(HTMLLOGO_FILE)" \
+ --stringparam pdfname "$(PDFNAME)" \
-path $(DOCGEN)/priv/dtd \
-path $(DOCGEN)/priv/dtd_html_entities \
$(DOCGEN)/priv/xsl/db_html.xsl book.xml
+
endif
$(HTMLDIR)/users_guide.html: $(XML_FILES)
date=`date +"%B %e %Y"`; \
- $(XSLTPROC) --noout --stringparam outdir $(HTMLDIR) --stringparam docgen "$(DOCGEN)" --stringparam topdocdir "$(TOPDOCDIR)" \
+ $(XSLTPROC) --noout \
+ --stringparam outdir $(HTMLDIR) \
+ --stringparam docgen "$(DOCGEN)" \
+ --stringparam topdocdir "$(TOPDOCDIR)" \
--stringparam pdfdir "$(PDFDIR)" \
- --stringparam gendate "$$date" --stringparam appname "$(APPLICATION)" --stringparam appver "$(VSN)" --xinclude \
- -path $(DOCGEN)/priv/dtd -path $(DOCGEN)/priv/dtd_html_entities $(DOCGEN)/priv/xsl/db_html.xsl book.xml
+ --stringparam gendate "$$date" \
+ --stringparam appname "$(APPLICATION)" \
+ --stringparam appver "$(VSN)" \
+ --stringparam stylesheet "$(CSS_FILE)" \
+ --stringparam winprefix "$(WINPREFIX)" \
+ --stringparam logo "$(HTMLLOGO_FILE)" \
+ --stringparam pdfname "$(PDFNAME)" \
+ --xinclude \
+ -path $(DOCGEN)/priv/dtd \
+ -path $(DOCGEN)/priv/dtd_html_entities \
+ $(DOCGEN)/priv/xsl/db_html.xsl book.xml
%.fo: $(XML_FILES) $(SPECS_FILES)
date=`date +"%B %e %Y"`; \
@@ -61,6 +78,8 @@ $(HTMLDIR)/users_guide.html: $(XML_FILES)
--stringparam gendate "$$date" \
--stringparam appname "$(APPLICATION)" \
--stringparam appver "$(VSN)" \
+ --stringparam logo "$(PDFLOGO_FILE)" \
+ --stringparam pdfcolor "$(PDFCOLOR)" \
--xinclude $(TOP_SPECS_PARAM) \
-path $(DOCGEN)/priv/dtd \
-path $(DOCGEN)/priv/dtd_html_entities \
@@ -77,11 +96,19 @@ ifneq ($(XML_FILES),)
$(HTMLDIR)/$(APPLICATION).eix: $(XML_FILES) $(SPECS_FILES)
date=`date +"%B %e %Y"`; \
$(XSLTPROC) --stringparam docgen "$(DOCGEN)" \
- --stringparam gendate "$$date" --stringparam appname "$(APPLICATION)" --stringparam appver "$(VSN)" --xinclude $(TOP_SPECS_PARAM) \
- -path $(DOCGEN)/priv/dtd -path $(DOCGEN)/priv/dtd_html_entities $(DOCGEN)/priv/xsl/db_eix.xsl book.xml > $@
+ --stringparam gendate "$$date" \
+ --stringparam appname "$(APPLICATION)" \
+ --stringparam appver "$(VSN)" \
+ -xinclude $(TOP_SPECS_PARAM) \
+ -path $(DOCGEN)/priv/dtd \
+ -path $(DOCGEN)/priv/dtd_html_entities \
+ $(DOCGEN)/priv/xsl/db_eix.xsl book.xml > $@
docs: $(HTMLDIR)/$(APPLICATION).eix
+xmllint: $(XML_FILES)
+ $(XMLLINT) --noout --valid --nodefdtd --loaddtd --path $(DOCGEN)/priv/dtd:$(DOCGEN)/priv/dtd_html_entities $(XML_FILES)
+
# ----------------------------------------------------
# Local documentation target for testing
# ----------------------------------------------------
diff --git a/make/output.mk.in b/make/output.mk.in
new file mode 100644
index 0000000000..2f1a1d3a79
--- /dev/null
+++ b/make/output.mk.in
@@ -0,0 +1,112 @@
+#-*-makefile-*- ; force emacs to enter makefile-mode
+# ----------------------------------------------------
+# Make include file for otp
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 1997-2012. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, 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: Anthony Ramine
+# ----------------------------------------------------
+
+ifeq ($(V),)
+V = @DEFAULT_VERBOSITY@
+endif
+
+ifeq ($(V),0)
+v_p = 0
+else
+v_p = 1
+endif
+
+V_at_0 = @
+V_at = $(V_at_$(V))
+
+V_colon_0 = @: ""
+V_colon = $(V_colon_$(V))
+
+ar_verbose_0 = @echo " AR "$@;
+ar_verbose = $(ar_verbose_$(V))
+V_AR = $(ar_verbose)$(AR)
+
+asn_verbose_0 = @echo " ASN "$@;
+asn_verbose = $(asn_verbose_$(V))
+
+cc_verbose_0 = @echo " CC "$@;
+cc_verbose = $(cc_verbose_$(V))
+V_CC = $(cc_verbose)$(CC)
+
+dia_verbose_0 = @echo " DIA "$@;
+dia_verbose = $(dia_verbose_$(V))
+
+dtrace_verbose_0 = @echo " DTRACE "$@;
+dtrace_verbose = $(dtrace_verbose_$(V))
+
+emacs_verbose_0 = @echo " EMACS "$@;
+emacs_verbose = $(emacs_verbose_$(V))
+
+emu_cc_verbose_0 = @echo " EMU_CC "$@;
+emu_cc_verbose = $(emu_cc_verbose_$(V))
+V_EMU_CC = $(emu_cc_verbose)$(EMU_CC)
+
+erlc_verbose_0 = @echo " ERLC "$@;
+erlc_verbose = $(erlc_verbose_$(V))
+V_ERLC = $(erlc_verbose)$(ERLC)
+
+gen_verbose_0 = @echo " GEN "$@;
+gen_verbose = $(gen_verbose_$(V))
+
+javac_verbose_0 = @echo " JAVAC "$@;
+javac_verbose = $(javac_verbose_$(V))
+V_JAVAC = $(javac_verbose)$(JAVAC)
+
+ld_verbose_0 = @echo " LD "$@;
+ld_verbose = $(ld_verbose_$(V))
+V_LD = $(ld_verbose)$(LD)
+
+leex_verbose_0 = @echo " LEEX "$@;
+leex_verbose = $(leex_verbose_$(V))
+
+lex_verbose_0 = @echo " LEX "$@;
+lex_verbose = $(lex_verbose_$(V))
+V_LEX = $(lex_verbose)$(LEX)
+
+m4_verbose_0 = @echo " M4 "$@;
+m4_verbose = $(m4_verbose_$(V))
+
+make_verbose_0 = @echo " MAKE "$@;
+make_verbose = $(make_verbose_$(V))
+
+mc_verbose_0 = @echo " MC "$@;
+mc_verbose = $(mc_verbose_$(V))
+V_MC = $(mc_verbose)$(MC)
+
+ranlib_verbose_0 = @echo " RANLIB "$@;
+ranlib_verbose = $(ranlib_verbose_$(V))
+V_RANLIB = $(ranlib_verbose)$(RANLIB)
+
+rc_verbose_0 = @echo " RC "$@;
+rc_verbose = $(rc_verbose_$(V))
+V_RC = $(rc_verbose)$(RC)
+
+snmp_verbose_0 = @echo " SNMP "$@;
+snmp_verbose = $(snmp_verbose_$(V))
+
+vsn_verbose_0 = @echo " VSN "$@;
+vsn_verbose = $(vsn_verbose_$(V))
+
+yecc_verbose_0 = @echo " YECC "$@;
+yecc_verbose = $(yecc_verbose_$(V))
diff --git a/make/run_make.mk b/make/run_make.mk
index b7a5a64847..1b4213107f 100644
--- a/make/run_make.mk
+++ b/make/run_make.mk
@@ -25,19 +25,20 @@
#
# ----------------------------------------------------
+include $(ERL_TOP)/make/output.mk
include $(ERL_TOP)/make/target.mk
.PHONY: valgrind
opt debug purify quantify purecov valgrind gcov gprof lcnt:
- $(MAKE) -f $(TARGET)/Makefile TYPE=$@
+ $(make_verbose)$(MAKE) -f $(TARGET)/Makefile TYPE=$@
plain smp frag smp_frag:
- $(MAKE) -f $(TARGET)/Makefile FLAVOR=$@
+ $(make_verbose)$(MAKE) -f $(TARGET)/Makefile FLAVOR=$@
clean generate depend docs release release_spec release_docs release_docs_spec \
tests release_tests release_tests_spec:
- $(MAKE) -f $(TARGET)/Makefile $@
+ $(make_verbose)$(MAKE) -f $(TARGET)/Makefile $@
diff --git a/otp_build b/otp_build
index 1fee139666..093fde8034 100755
--- a/otp_build
+++ b/otp_build
@@ -2,7 +2,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2002-2011. All Rights Reserved.
+# Copyright Ericsson AB 2002-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -295,7 +295,7 @@ chk_eq()
check_config_helpers ()
{
- aclocals="./aclocal.m4 ./lib/erl_interface/aclocal.m4 ./lib/odbc/aclocal.m4 ./lib/wx/aclocal.m4"
+ aclocals="./aclocal.m4 ./lib/erl_interface/aclocal.m4 ./lib/odbc/aclocal.m4 ./lib/wx/aclocal.m4 ./lib/megaco/aclocal.m4"
install_shs="./lib/common_test/priv/auxdir/install-sh ./lib/erl_interface/src/auxdir/install-sh ./lib/test_server/src/install-sh"
config_guesses="./lib/common_test/priv/auxdir/config.guess ./lib/erl_interface/src/auxdir/config.guess ./lib/test_server/src/config.guess"
config_subs="./lib/common_test/priv/auxdir/config.sub ./lib/erl_interface/src/auxdir/config.sub ./lib/test_server/src/config.sub"
diff --git a/system/README b/system/README
index b8ff18119d..b1e18ef55c 100644
--- a/system/README
+++ b/system/README
@@ -51,10 +51,9 @@ Release of Erlang 5.9/OTP R15B
-------------------------------
3.1 The platform VxWorks is discontinued in the sense that only the
- libraries (erl_interface and ic's libraries) are supported. Running
- the Erlang emulator on VxWorks might still possible but it's no longer
- maintained. The VxWorks release is still packaged as a full release,
- but no support will be available for anything but the communication libraries.
+ libraries (erl_interface and ic's libraries) are supported. The VxWorks
+ release is still packaged as a full release, but no support will be
+ available for anything but the communication libraries.
4 NOTES ABOUT THE LINUX VERSIONS
-----------------------------
diff --git a/system/doc/design_principles/Makefile b/system/doc/design_principles/Makefile
index a9d4665ee4..73e1e2c6f7 100644
--- a/system/doc/design_principles/Makefile
+++ b/system/doc/design_principles/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/system/doc/efficiency_guide/Makefile b/system/doc/efficiency_guide/Makefile
index 1fd7b36453..655531f42b 100644
--- a/system/doc/efficiency_guide/Makefile
+++ b/system/doc/efficiency_guide/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2001-2011. All Rights Reserved.
+# Copyright Ericsson AB 2001-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/system/doc/efficiency_guide/advanced.xml b/system/doc/efficiency_guide/advanced.xml
index 821175bb09..ac35a37bc4 100644
--- a/system/doc/efficiency_guide/advanced.xml
+++ b/system/doc/efficiency_guide/advanced.xml
@@ -123,12 +123,11 @@ On 64-bit architectures: 4 words for a reference from the current local node, an
<tag><em>Processes</em></tag>
<item>
<p>The maximum number of simultaneously alive Erlang processes is
- by default 32768. This limit can be raised up to at most 268435456
- processes at startup (see documentation of the system flag
- <seealso marker="erts:erl#max_processes">+P</seealso> in the
- <seealso marker="erts:erl">erl(1)</seealso> documentation).
- The maximum limit of 268435456 processes will at least on a 32-bit
- architecture be impossible to reach due to memory shortage.</p>
+ by default 32768. This limit can be configured at startup,
+ for more information see the
+ <seealso marker="erts:erl#max_processes"><c>+P</c></seealso>
+ command line flag of
+ <seealso marker="erts:erl"><c>erl(1)</c></seealso>.</p>
</item>
<tag><em>Distributed nodes</em></tag>
<item>
@@ -184,13 +183,12 @@ On 64-bit architectures: 4 words for a reference from the current local node, an
<tag><em>Open ports</em></tag>
<item>
<marker id="ports"></marker>
- <p>The maximum number of simultaneously open Erlang ports is
- by default 1024. This limit can be raised up to at most 268435456
- at startup (see environment variable
- <seealso marker="erts:erlang#ERL_MAX_PORTS">ERL_MAX_PORTS</seealso>
- in <seealso marker="erts:erlang">erlang(3)</seealso>)
- The maximum limit of 268435456 open ports will at least on a 32-bit
- architecture be impossible to reach due to memory shortage.</p>
+ <p>The maximum number of simultaneously oper Erlang ports is
+ often by default 16384. This limit can be configured at startup,
+ for more information see the
+ <seealso marker="erts:erl#max_ports"><c>+Q</c></seealso>
+ command line flag of
+ <seealso marker="erts:erl"><c>erl(1)</c></seealso>.</p>
</item>
<tag><em>Open files, and sockets</em></tag>
<item> <marker id="files_sockets"></marker>
diff --git a/system/doc/efficiency_guide/drivers.xml b/system/doc/efficiency_guide/drivers.xml
index fec68ca059..b10595ea4d 100644
--- a/system/doc/efficiency_guide/drivers.xml
+++ b/system/doc/efficiency_guide/drivers.xml
@@ -105,9 +105,9 @@ client_port() ->
<p>If you know that the binaries you return are always small,
you should use driver API calls that do not require a pre-allocated
binary, for instance
- <seealso marker="erts:erl_driver#int driver_output-3">driver_output()</seealso>
+ <seealso marker="erts:erl_driver#driver_output">driver_output()</seealso>
or
- <seealso marker="erts:erl_driver#int driver_output_term-3">driver_output_term()</seealso>
+ <seealso marker="erts:erl_driver#erl_drv_output_term">erl_drv_output_term()</seealso>
using the <c>ERL_DRV_BUF2BINARY</c> format,
to allow the run-time to construct a heap binary.</p>
@@ -120,7 +120,7 @@ client_port() ->
the driver to an Erlang process, the driver must first allocate the
binary and then send it to an Erlang process in some way.</p>
- <p>Use <seealso marker="erts:erl_driver#ErlDrvBinary* driver_alloc_binary-1">driver_alloc_binary()</seealso> to allocate a binary.</p>
+ <p>Use <seealso marker="erts:erl_driver#driver_alloc_binary">driver_alloc_binary()</seealso> to allocate a binary.</p>
<p>There are several ways to send a binary created with
<c>driver_alloc_binary()</c>.</p>
@@ -128,17 +128,17 @@ client_port() ->
<list type="bulleted">
<item><p>From the <c>control</c> callback, a binary can be returned provided
that
- <seealso marker="erts:erl_driver#void set_port_control_flags-2">set_port_control()</seealso>
+ <seealso marker="erts:erl_driver#set_port_control_flags">set_port_control_flags()</seealso>
has been called with the flag value <c>PORT_CONTROL_FLAG_BINARY</c>.</p>
</item>
<item><p>A single binary can be sent with
- <seealso marker="erts:erl_driver#int driver_output_binary-6">driver_output_binary()</seealso>.</p></item>
+ <seealso marker="erts:erl_driver#driver_output_binary">driver_output_binary()</seealso>.</p></item>
<item><p>Using
- <seealso marker="erts:erl_driver#int driver_output_term-3">driver_output_term()</seealso>
+ <seealso marker="erts:erl_driver#erl_drv_output_term">erl_drv_output_term()</seealso>
or
- <seealso marker="erts:erl_driver#int driver_send_term-4">driver_send_term()</seealso>,
+ <seealso marker="erts:erl_driver#erl_drv_send_term">erl_drv_send_term()</seealso>,
a binary can be included in an Erlang term.</p>
</item>
</list>
diff --git a/system/doc/embedded/Makefile b/system/doc/embedded/Makefile
index 825fb23cfe..2f78d7ddcd 100644
--- a/system/doc/embedded/Makefile
+++ b/system/doc/embedded/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/system/doc/embedded/part.xml b/system/doc/embedded/part.xml
index abedce46d6..d96a94a1a0 100644
--- a/system/doc/embedded/part.xml
+++ b/system/doc/embedded/part.xml
@@ -45,6 +45,5 @@
</description>
<xi:include href="embedded_solaris.xml"/>
<xi:include href="embedded_nt.xml"/>
- <xi:include href="vxworks.xml"/>
</part>
diff --git a/system/doc/embedded/vxworks.xml b/system/doc/embedded/vxworks.xml
deleted file mode 100644
index 52143a42e3..0000000000
--- a/system/doc/embedded/vxworks.xml
+++ /dev/null
@@ -1,193 +0,0 @@
-<?xml version="1.0" encoding="latin1" ?>
-<!DOCTYPE chapter SYSTEM "chapter.dtd">
-
-<chapter>
- <header>
- <copyright>
- <year>1997</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>VxWorks</title>
- <prepared>Patrik Winroth</prepared>
- <responsible></responsible>
- <docno></docno>
- <approved></approved>
- <checked></checked>
- <date>2000-10-17</date>
- <rev></rev>
- <file>vxworks.xml</file>
- </header>
- <p>This chapter describes the OS specific parts of OTP which relate
- to VxWorks.
- </p>
-
- <section>
- <title>Introduction</title>
- <p>The Erlang/OTP distribution for VxWorks is limited to what
- Switchboard requires (Switchboard is a general purpose
- switching hardware developed by Ericsson).
- </p>
- <p>Please consult the README file, included at root level in the
- installation, for latest information on the distribution.
- </p>
- </section>
-
- <section>
- <title>Memory Usage</title>
- <p>Memory required is 32 Mbyte.
- </p>
- </section>
-
- <section>
- <title>Disk Usage</title>
- <p>The disk space required is 22 Mbyte, the documentation included.
- </p>
- </section>
-
- <section>
- <title>Installation</title>
- <p>OTP/VxWorks is supplied in a distribution file named
- <c><![CDATA[<PREFIX>.tar.gz]]></c>; i.e. a tar archive that is
- compressed with gzip. <c><![CDATA[<PREFIX>]]></c> represents the
- name of the release,
- e.g. <c>otp_LXA12345_vxworks_cpu32_R42A</c>. Assuming you are
- installing to a Solaris file system, the installation is
- performed by following these steps: &lt;
- </p>
- <p></p>
- <list type="bulleted">
- <item>Change to the directory where you want to install
- OTP/VxWorks (<c><![CDATA[<ROOTDIR>]]></c>): <c><![CDATA[cd <ROOTDIR>]]></c></item>
- <item>Make a directory to put OTP/VxWorks in: <c>mkdir otp_vxworks_cpu32</c> (or whatever you want to call it)</item>
- <item>Change directory to the newly created one: <c>cd otp_vxworks_cpu32</c></item>
- <item>Copy the distribution file there from where it is located
- (<c><![CDATA[<RELDIR>]]></c>): <c><![CDATA[cp <RELDIR>/<PREFIX>.tar.gz .]]></c></item>
- <item>Unzip the distribution file: <c><![CDATA[gunzip <PREFIX>.tar.gz]]></c></item>
- <item>Untar <c><![CDATA[<PREFIX>.tar]]></c>: <c><![CDATA[tar xvf <PREFIX>.tar]]></c></item>
- <item>Create a bin directory: <c>mkdir bin</c></item>
- <item>Copy the VxWorks Erlang/OTP start-up script to the bin directory:
- <c>cp erts-Vsn/bin/erl bin/.</c></item>
- <item>Copy the example start scripts to the bin directory:
- <c>cp releases/R42A/*.boot bin/.</c></item>
- </list>
- <p>If you use VxWorks nfs mounting facility to mount the Solaris
- file system, this installation may be directly used. An other
- possibility is to copy the installation to a local VxWorks DOS
- file system, from where it is used.
- </p>
- </section>
-
- <section>
- <title>OS Specific Functionality/Information</title>
- <p>There are a couple of files that are unique to the VxWorks
- distribution of Erlang/OTP, these files are described here.
- </p>
- <list type="bulleted">
- <item>README - this files has some information on VxWorks
- specifics that you are advised to consult. This includes the
- latest information on what parts of OTP are included in the
- VxWorks distribution of Erlang/OTP. If you want us to
- include more parts, please contact us to discuss
- this.</item>
- <item>erts-Vsn/bin/resolv.conf - A resolver configuration EXAMPLE file.
- You have to edit this file.</item>
- <item>erts-Vsn/bin/erl - This is an EXAMPLE start script for VxWorks.
- You have to edit this file to suit your needs.</item>
- <item>erts-Vsn/bin/erl_io - One possible solution to the problem
- of competing Erlang and VxWorks shell. Contains the function
- 'start_erl' called by the erl script. Also contains the
- function 'to_erl' to be used when connecting to the Erlang
- shell from VxWorks' shell.</item>
- <item>erts-Vsn/bin/erl_exec - Rearranges command line arguments
- and starts Erlang.</item>
- <item>erts-Vsn/bin/vxcall - Allows spawning of standard VxWorks
- shell functions (which is just about any function in the
- system...) from open_port/2. E.g. open_port({spawn, 'vxcall
- func arg1 arg2'}, []) will cause the output that 'func arg1,
- arg2' would have given in the shell to be received from the
- port.</item>
- <item>erts-Vsn/bin/rdate - Set the time from a networked host,
- like the SunOS command. Nothing Erlang-specific, but nice
- if you want date/0 and time/0 to give meaningful values (you
- also need a TIMEZONE environment setting if GMT isn't
- acceptable). For example: <c>putenv "TIMEZONE=CET::-60:033002:102603"</c> sets central european
- time.</item>
- <item>erts-Vsn/src - Contains source for the above files, and
- additionally config.c, driver.h, preload.c and
- reclaim.h. Reclaim.h defines the interface to a simple
- mechanism for "resource reclamation" that is part of the
- Erlang runtime system - may be useful to "port program" writers (and
- possibly others). Take careful note of the caveats listed in
- the file!</item>
- </list>
- </section>
-
- <section>
- <title>Starting Erlang</title>
- <p>Start (and restart) of the system depends on what file system
- is used. To be able to start the system from a nfs mounted
- file system you can use VxWorks start script facility to run a
- start script similar to the example below. Note that the
- Erlang/OTP start-up script is run at the end of this script.
- </p>
- <code type="none"><![CDATA[
-# start.script v1.0 1997/09/08 patrik
-#
-# File name: start.script
-# Purpose: Starting the VxWorks/cpu32 erlang/OTP
-# Resides in: ~tornado/wind/target/config/ads360/
-
-#
-# Set shell prompt
-#
-shellPromptSet("sauron-> ")
-
-#
-# Set default gateway
-#
-hostAdd "router-20","150.236.20.251"
-routeAdd "0","router-20"
-
-#
-# Mount /home from gandalf
-#
-hostAdd "gandalf","150.236.20.16"
-usergroup=10
-nfsAuthUnixSet("gandalf", 452, 10, 1, &usergroup)
-nfsMount("gandalf", "/export/home", "/home")
-
-#
-# Load and run rdate.o to set correct date on the target
-#
-ld < /home/gandalf/tornado/wind/target/config/ads360/rdate.o
-rdate("gandalf")
-
-#
-# Setup timezone information (Central European time)
-#
-putenv "TIMEZONE=CET::-60:033002:102603"
-
-#
-# Run the Erlang/OTP start script
-#
-cd "/home/gandalf/tornado/wind/target/erlang_cpu32_R42A/bin"
-<erl
- ]]></code>
- </section>
-</chapter>
-
diff --git a/system/doc/embedded/xmlfiles.mk b/system/doc/embedded/xmlfiles.mk
index 2bdc34ae28..0001a55b9a 100644
--- a/system/doc/embedded/xmlfiles.mk
+++ b/system/doc/embedded/xmlfiles.mk
@@ -18,5 +18,4 @@
#
EMBEDDED_CHAPTER_FILES = \
embedded_solaris.xml \
- embedded_nt.xml \
- vxworks.xml
+ embedded_nt.xml
diff --git a/system/doc/getting_started/Makefile b/system/doc/getting_started/Makefile
index 972a51d3a5..ce53256b16 100644
--- a/system/doc/getting_started/Makefile
+++ b/system/doc/getting_started/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2011. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/system/doc/installation_guide/Makefile b/system/doc/installation_guide/Makefile
index 4636650570..6923f52d8a 100644
--- a/system/doc/installation_guide/Makefile
+++ b/system/doc/installation_guide/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2010. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -58,7 +58,8 @@ XML_FILES = \
GENERATED_XML_FILES = \
INSTALL.xml \
INSTALL-CROSS.xml \
- INSTALL-WIN32.xml
+ INSTALL-WIN32.xml \
+ MARKDOWN.xml
# ----------------------------------------------------
@@ -73,7 +74,8 @@ REDIRECT_HTML_DIR = $(HTMLDIR)/source
REDIRECT_HTML_FILES = \
$(REDIRECT_HTML_DIR)/INSTALL.html \
$(REDIRECT_HTML_DIR)/INSTALL-CROSS.html \
- $(REDIRECT_HTML_DIR)/INSTALL-WIN32.html
+ $(REDIRECT_HTML_DIR)/INSTALL-WIN32.html \
+ $(REDIRECT_HTML_DIR)/MARKDOWN.html
# ----------------------------------------------------
# FLAGS
@@ -85,7 +87,7 @@ DVIPS_FLAGS +=
# Targets
# ----------------------------------------------------
-%.xml: $(ERL_TOP)/%.md $(ERL_TOP)/make/emd2exml
+%.xml: $(ERL_TOP)/HOWTO/%.md $(ERL_TOP)/make/emd2exml
$(ERL_TOP)/make/emd2exml $< $@
$(REDIRECT_HTML_DIR)/%.html: Makefile
diff --git a/system/doc/installation_guide/part.xml b/system/doc/installation_guide/part.xml
index fceacdd8f6..648beb0afd 100644
--- a/system/doc/installation_guide/part.xml
+++ b/system/doc/installation_guide/part.xml
@@ -36,5 +36,5 @@
<xi:include href="INSTALL.xml"/>
<xi:include href="INSTALL-CROSS.xml"/>
<xi:include href="INSTALL-WIN32.xml"/>
-</part>
-
+ <xi:include href="MARKDOWN.xml"/>
+</part> \ No newline at end of file
diff --git a/system/doc/installation_guide/xmlfiles.mk b/system/doc/installation_guide/xmlfiles.mk
index dee67b3c70..8ea2b296aa 100644
--- a/system/doc/installation_guide/xmlfiles.mk
+++ b/system/doc/installation_guide/xmlfiles.mk
@@ -21,4 +21,5 @@ INST_GUIDE_CHAPTER_FILES = \
verification.xml \
INSTALL.xml \
INSTALL-CROSS.xml \
- INSTALL-WIN32.xml
+ INSTALL-WIN32.xml \
+ MARKDOWN.xml
diff --git a/system/doc/oam/Makefile b/system/doc/oam/Makefile
index f459f4349f..ca07d82f75 100644
--- a/system/doc/oam/Makefile
+++ b/system/doc/oam/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/system/doc/programming_examples/Makefile b/system/doc/programming_examples/Makefile
index fcc51e3b18..41c27ed0dd 100644
--- a/system/doc/programming_examples/Makefile
+++ b/system/doc/programming_examples/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2003-2011. All Rights Reserved.
+# Copyright Ericsson AB 2003-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/system/doc/reference_manual/Makefile b/system/doc/reference_manual/Makefile
index 87a477d14e..dbdfea2162 100644
--- a/system/doc/reference_manual/Makefile
+++ b/system/doc/reference_manual/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2003-2011. All Rights Reserved.
+# Copyright Ericsson AB 2003-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/system/doc/reference_manual/expressions.xml b/system/doc/reference_manual/expressions.xml
index d564b20ca6..357f89f731 100644
--- a/system/doc/reference_manual/expressions.xml
+++ b/system/doc/reference_manual/expressions.xml
@@ -236,10 +236,6 @@ Fun1 = fun(X) -> X+1 end
Fun1(3)
=> 4
-Fun2 = {lists,append}
-Fun2([1,2], [3,4])
-=> [1,2,3,4]
-
fun lists:append/2([1,2], [3,4])
=> [1,2,3,4]</code>
@@ -1000,13 +996,6 @@ fun (Arg1,...,ArgN) -> Name(Arg1,...,ArgN) end</pre>
<c>Module</c>. A fun defined in this way will not be dependent on
the code for module in which it is defined.
</p>
- <p>When applied to a number N of arguments, a tuple
- <c>{Module,FunctionName}</c> is interpreted as a fun, referring
- to the function <c>FunctionName</c> with arity N in the module
- <c>Module</c>. The function must be exported.
- <em>This usage is deprecated.</em> Use <c>fun Module:Name/Arity</c>
- instead.
- See <seealso marker="#calls">Function Calls</seealso> for an example.</p>
<p>More examples can be found in <em>Programming Examples</em>.</p>
</section>
diff --git a/system/doc/reference_manual/introduction.xml b/system/doc/reference_manual/introduction.xml
index 3dac5cfe13..7737c34469 100644
--- a/system/doc/reference_manual/introduction.xml
+++ b/system/doc/reference_manual/introduction.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2003</year><year>2009</year>
+ <year>2003</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -150,6 +150,17 @@
</row>
<tcaption>Character Classes.</tcaption>
</table>
+ <p>In Erlang/OTP R16 the syntax of Erlang tokens was extended to
+ handle Unicode. To begin with the support is limited to strings,
+ but Erlang/OTP R18 is expected to handle Unicode atoms as well.
+ More about the usage of Unicode in Erlang source files can be
+ found in <seealso
+ marker="stdlib:unicode_usage#unicode_in_erlang">STDLIB's User'S
+ Guide</seealso>. The default encoding for Erlang source files
+ is still Latin-1, but in Erlang/OTP R17 the default encoding
+ will be UTF-8. The details on how to state the encoding of an
+ Erlang source file can be found in <seealso
+ marker="stdlib:epp#encoding">epp(3)</seealso>.</p>
</section>
</chapter>
diff --git a/system/doc/reference_manual/ports.xml b/system/doc/reference_manual/ports.xml
index 4847dd67cd..c4e4ef1d35 100644
--- a/system/doc/reference_manual/ports.xml
+++ b/system/doc/reference_manual/ports.xml
@@ -87,8 +87,14 @@
of bytes, the option <c>binary</c> must be included.</p>
<p>The port owner <c>Pid</c> can communicate with the port
<c>Port</c> by sending and receiving messages. (In fact, any
- process can send the messages to the port, but the messages from
- the port always go to the port owner).</p>
+ process can send the messages to the port, but the port owner must
+ be identified in the message).</p>
+ <p>As of OTP-R16 messages sent to ports are delivered truly
+ asynchronously. The underlying implementation previously
+ delivered messages to ports synchronously. Message passing has
+ however always been documented as an asynchronous operation, so
+ this should not be an issue for an Erlang program communicating
+ with ports, unless false assumptions about ports has been made.</p>
<p>Below, <c>Data</c> must be an I/O list. An I/O list is a binary
or a (possibly deep) list of binaries or integers in the range
0..255.</p>
@@ -127,8 +133,7 @@
<tcaption>Messages Received From a Port.</tcaption>
</table>
<p>Instead of sending and receiving messages, there are also a
- number of BIFs that can be used. These can be called by any
- process, not only the port owner.</p>
+ number of BIFs that can be used.</p>
<table>
<row>
<cell align="left" valign="middle"><c>port_command(Port,Data)</c></cell>
diff --git a/system/doc/system_architecture_intro/Makefile b/system/doc/system_architecture_intro/Makefile
index 2de67473c1..0b5322de4f 100644
--- a/system/doc/system_architecture_intro/Makefile
+++ b/system/doc/system_architecture_intro/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/system/doc/system_principles/Makefile b/system/doc/system_principles/Makefile
index 4b7fc8f177..b1c0fc3ddd 100644
--- a/system/doc/system_principles/Makefile
+++ b/system/doc/system_principles/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2011. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/system/doc/top/Makefile b/system/doc/top/Makefile
index bbdb4d7055..673ba44c94 100644
--- a/system/doc/top/Makefile
+++ b/system/doc/top/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2011. All Rights Reserved.
+# Copyright Ericsson AB 1999-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -163,17 +163,34 @@ $(MAN_INDEX): $(MAN_INDEX_SCRIPT)
$(HTMLDIR)/highlights.html: highlights.xml
date=`date +"%B %e %Y"`; \
- $(XSLTPROC) --output $(@) --stringparam docgen "$(DOCGEN)" --stringparam topdocdir "$(TOPDOCDIR)" \
- --stringparam pdfdir "$(PDFREFDIR)" --stringparam gendate "$$date" --stringparam appname "$(APPLICATION)" \
- --stringparam appver "$(VSN)" -path $(DOCGEN)/priv/dtd -path $(DOCGEN)/priv/dtd_html_entities \
+ $(XSLTPROC) --output $(@) \
+ --stringparam docgen "$(DOCGEN)" \
+ --stringparam topdocdir "$(TOPDOCDIR)" \
+ --stringparam pdfdir "$(PDFREFDIR)" \
+ --stringparam gendate "$$date" \
+ --stringparam appname "$(APPLICATION)" \
+ --stringparam appver "$(VSN)" \
+ --stringparam stylesheet "$(CSS_FILE)" \
+ --stringparam winprefix "$(WINPREFIX)" \
+ --stringparam logo "$(HTMLLOGO_FILE)" \
+ -path $(DOCGEN)/priv/dtd \
+ -path $(DOCGEN)/priv/dtd_html_entities \
$(DOCGEN)/priv/xsl/db_html.xsl $<
$(HTMLDIR)/incompatible.html: incompatible.xml
date=`date +"%B %e %Y"`; \
- $(XSLTPROC) --output $(@) --stringparam docgen "$(DOCGEN)" --stringparam topdocdir "$(TOPDOCDIR)" \
- --stringparam pdfdir "$(PDFREFDIR)" --stringparam gendate "$$date" --stringparam appname "$(APPLICATION)" \
- --stringparam appver "$(VSN)" -path $(DOCGEN)/priv/dtd -path $(DOCGEN)/priv/dtd_html_entities \
+ $(XSLTPROC) --output $(@) --stringparam docgen "$(DOCGEN)" \
+ --stringparam topdocdir "$(TOPDOCDIR)" \
+ --stringparam pdfdir "$(PDFREFDIR)" \
+ --stringparam gendate "$$date" \
+ --stringparam appname "$(APPLICATION)" \
+ --stringparam appver "$(VSN)" \
+ --stringparam stylesheet "$(CSS_FILE)" \
+ --stringparam winprefix "$(WINPREFIX)" \
+ --stringparam logo "$(HTMLLOGO_FILE)" \
+ -path $(DOCGEN)/priv/dtd \
+ -path $(DOCGEN)/priv/dtd_html_entities \
$(DOCGEN)/priv/xsl/db_html.xsl $<
#--------------------------------------------------------------------------
diff --git a/system/doc/top/templates/index.html.src b/system/doc/top/templates/index.html.src
index 655f532a5d..de87600ebd 100644
--- a/system/doc/top/templates/index.html.src
+++ b/system/doc/top/templates/index.html.src
@@ -2,7 +2,7 @@
<!--
%CopyrightBegin%
-Copyright Ericsson AB 2009-2010. All Rights Reserved.
+Copyright Ericsson AB 2009-2012. All Rights Reserved.
The contents of this file are subject to the Erlang Public License,
Version 1.1, (the "License"); you may not use this file except in
@@ -94,7 +94,7 @@ Some hints that may get you started faster
<ul>
<li>
-The complete Erlang language is described in the
+The Erlang language is described in the
<a href="reference_manual/users_guide.html">Erlang Reference Manual</a>.
An Erlang tutorial can be found in
<a href="getting_started/users_guide.html">
@@ -109,6 +109,9 @@ In addition to the documentation here Erlang is described in several recent book
<li>
<a href="http://www.pragprog.com/titles/jaerlang">"Programming Erlang"</a> from Pragmatic.
</li>
+<li>
+<a href="http://www.manning.com/logan">"Erlang and OTP in Action"</a> from Manning.
+</li>
</ul>
<p>
These books are highly recommended as a start for learning Erlang.
@@ -136,23 +139,21 @@ tags support and more. See the <a href="#tools#/index.html">
Tools</a> application for details.
<p>
There is also an
-<a href="http://erlide.sourceforge.net">
+<a href="http://erlide.org/index.html">
Erlang plugin (ErlIDE) for Eclipse</a> if you prefer a more graphical
environment. ErlIDE is under active development with new features in almost every release.
<li>When developing with Erlang/OTP you usually test your programs
from the interactive shell (see <a href="getting_started/users_guide.html">
Getting Started With Erlang</a>) where you can call individual
functions. There is also a number of tools available, such as the graphical <a
-href="#debugger#/index.html" >Debugger</a>, the process
-manager <a href="#pman#/index.html" >Pman</a> and table
-viewer <a href="#tv#/index.html">TV</a>.
+href="#debugger#/index.html" >Debugger</a> and the <a href="#observer#/index.html" >Observer tool</a> for inspection of system information, ets and mnesia tables etc.
<p> Also note that there are some shell features like history list
(control-p and control-n), in line editing (Emacs key bindings) and
module and function name completion (tab) if the module is loaded.
<p>
<li>OpenSource users can ask questions
-and share experiences on the <a href="http://www.erlang.org/faq.html">
+and share experiences on the <a href="http://www.erlang.org/static/doc/mailinglist.html">
Erlang questions mailing list</a>. <p>
<li>Before asking a question you can browse the <a
diff --git a/system/doc/tutorial/Makefile b/system/doc/tutorial/Makefile
index e9eb800e46..682a8df4d5 100644
--- a/system/doc/tutorial/Makefile
+++ b/system/doc/tutorial/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2000-2011. All Rights Reserved.
+# Copyright Ericsson AB 2000-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/system/doc/tutorial/port_driver.c b/system/doc/tutorial/port_driver.c
index d428d08ff3..37de67310f 100644
--- a/system/doc/tutorial/port_driver.c
+++ b/system/doc/tutorial/port_driver.c
@@ -19,7 +19,8 @@ static void example_drv_stop(ErlDrvData handle)
driver_free((char*)handle);
}
-static void example_drv_output(ErlDrvData handle, char *buff, int bufflen)
+static void example_drv_output(ErlDrvData handle, char *buff,
+ ErlDrvSizeT bufflen)
{
example_data* d = (example_data*)handle;
char fn = buff[0], arg = buff[1], res;
@@ -32,7 +33,7 @@ static void example_drv_output(ErlDrvData handle, char *buff, int bufflen)
}
ErlDrvEntry example_driver_entry = {
- NULL, /* F_PTR init, N/A */
+ NULL, /* F_PTR init, called when driver is loaded */
example_drv_start, /* L_PTR start, called when port is opened */
example_drv_stop, /* F_PTR stop, called when port is closed */
example_drv_output, /* F_PTR output, called when erlang has sent */
@@ -40,9 +41,30 @@ ErlDrvEntry example_driver_entry = {
NULL, /* F_PTR ready_output, called when output descriptor ready */
"example_drv", /* char *driver_name, the argument to open_port */
NULL, /* F_PTR finish, called when unloaded */
+ NULL, /* void *handle, Reserved by VM */
NULL, /* F_PTR control, port_command callback */
NULL, /* F_PTR timeout, reserved */
- NULL /* F_PTR outputv, reserved */
+ NULL, /* F_PTR outputv, reserved */
+ NULL, /* F_PTR ready_async, only for async drivers */
+ NULL, /* F_PTR flush, called when port is about
+ to be closed, but there is data in driver
+ queue */
+ NULL, /* F_PTR call, much like control, sync call
+ to driver */
+ NULL, /* F_PTR event, called when an event selected
+ by driver_event() occurs. */
+ ERL_DRV_EXTENDED_MARKER, /* int extended marker, Should always be
+ set to indicate driver versioning */
+ ERL_DRV_EXTENDED_MAJOR_VERSION, /* int major_version, should always be
+ set to this value */
+ ERL_DRV_EXTENDED_MINOR_VERSION, /* int minor_version, should always be
+ set to this value */
+ 0, /* int driver_flags, see documentation */
+ NULL, /* void *handle2, reserved for VM use */
+ NULL, /* F_PTR process_exit, called when a
+ monitored process dies */
+ NULL /* F_PTR stop_select, called to close an
+ event object */
};
DRIVER_INIT(example_drv) /* must match name in driver_entry */
diff --git a/xcomp/README.md b/xcomp/README.md
index 5f4b36bdca..768efc9c7d 100644
--- a/xcomp/README.md
+++ b/xcomp/README.md
@@ -462,6 +462,10 @@ When a variable has been set, no warning will be issued.
`configure` will fail unless this variable is set. Since no default
value is used, `configure` will try to figure this out automatically.
+* `erl_xcomp_double_middle` - `yes|no`. Defaults to `no`.
+ If `yes`, the target system must have doubles in "middle-endian" format. If
+ `no`, it has "regular" endianness.
+
* `erl_xcomp_clock_gettime_cpu_time` - `yes|no`. Defaults to `no`. If `yes`,
the target system must have a working `clock_gettime()` implementation
that can be used for retrieving process CPU time.
@@ -523,7 +527,7 @@ Copyright and License
%CopyrightBegin%
-Copyright Ericsson AB 2009-2010. All Rights Reserved.
+Copyright Ericsson AB 2009-2012. All Rights Reserved.
The contents of this file are subject to the Erlang Public License,
Version 1.1, (the "License"); you may not use this file except in
diff --git a/xcomp/erl-xcomp-TileraMDE2.0-tilepro.conf b/xcomp/erl-xcomp-TileraMDE2.0-tilepro.conf
index f9fff0fa8e..84bf735ff2 100644
--- a/xcomp/erl-xcomp-TileraMDE2.0-tilepro.conf
+++ b/xcomp/erl-xcomp-TileraMDE2.0-tilepro.conf
@@ -2,7 +2,7 @@
##
## %CopyrightBegin%
##
-## Copyright Ericsson AB 2009-2010. All Rights Reserved.
+## Copyright Ericsson AB 2009-2012. All Rights Reserved.
##
## The contents of this file are subject to the Erlang Public License,
## Version 1.1, (the "License"); you may not use this file except in
@@ -187,6 +187,15 @@ erl_xcomp_sysroot="$TILERA_ROOT/tile"
# value is used, `configure' will try to figure this out automatically.
#erl_xcomp_bigendian=
+# * `erl_xcomp_double_middle_endian` - `yes|no`. No default. If `yes`, the
+# target system must have doubles in "middle-endian" format. If
+# `no`, it has "regular" endianness. This can often be automatically
+# detected, but not always. If not automatically detected,
+# `configure` will fail unless this variable is set. Since no
+# default value is used, `configure` will try to figure this out
+# automatically.
+#erl_xcomp_double_middle_endian
+
# * `erl_xcomp_clock_gettime_cpu_time' - `yes|no'. Defaults to `no'. If `yes',
# the target system must have a working `clock_gettime()' implementation
# that can be used for retrieving process CPU time.
diff --git a/xcomp/erl-xcomp-avr32-atmel-linux-gnu.conf b/xcomp/erl-xcomp-avr32-atmel-linux-gnu.conf
index be22d498d6..e86043f5c8 100644
--- a/xcomp/erl-xcomp-avr32-atmel-linux-gnu.conf
+++ b/xcomp/erl-xcomp-avr32-atmel-linux-gnu.conf
@@ -190,6 +190,15 @@ erl_xcomp_sysroot="$ATMEL_SYSROOT_AVR32"
# value is used, `configure' will try to figure this out automatically.
#erl_xcomp_bigendian=
+# * `erl_xcomp_double_middle_endian` - `yes|no`. No default. If `yes`, the
+# target system must have doubles in "middle-endian" format. If
+# `no`, it has "regular" endianness. This can often be automatically
+# detected, but not always. If not automatically detected,
+# `configure` will fail unless this variable is set. Since no
+# default value is used, `configure` will try to figure this out
+# automatically.
+#erl_xcomp_double_middle_endian
+
# * `erl_xcomp_clock_gettime_cpu_time' - `yes|no'. Defaults to `no'. If `yes',
# the target system must have a working `clock_gettime()' implementation
# that can be used for retrieving process CPU time.
diff --git a/xcomp/erl-xcomp-mips-linux.conf b/xcomp/erl-xcomp-mips-linux.conf
index 3ac057bb4c..e031bdcd26 100644
--- a/xcomp/erl-xcomp-mips-linux.conf
+++ b/xcomp/erl-xcomp-mips-linux.conf
@@ -2,7 +2,7 @@
##
## %CopyrightBegin%
##
-## Copyright Ericsson AB 2010. All Rights Reserved.
+## Copyright Ericsson AB 2010-2012. All Rights Reserved.
##
## The contents of this file are subject to the Erlang Public License,
## Version 1.1, (the "License"); you may not use this file except in
@@ -187,6 +187,15 @@ CFLAGS=-Os
# value is used, `configure' will try to figure this out automatically.
#erl_xcomp_bigendian=
+# * `erl_xcomp_double_middle_endian` - `yes|no`. No default. If `yes`, the
+# target system must have doubles in "middle-endian" format. If
+# `no`, it has "regular" endianness. This can often be automatically
+# detected, but not always. If not automatically detected,
+# `configure` will fail unless this variable is set. Since no
+# default value is used, `configure` will try to figure this out
+# automatically.
+#erl_xcomp_double_middle_endian
+
# * `erl_xcomp_clock_gettime_cpu_time' - `yes|no'. Defaults to `no'. If `yes',
# the target system must have a working `clock_gettime()' implementation
# that can be used for retrieving process CPU time.
diff --git a/xcomp/erl-xcomp-powerpc-dso-linux-gnu.conf b/xcomp/erl-xcomp-powerpc-dso-linux-gnu.conf
new file mode 100644
index 0000000000..c245b493a5
--- /dev/null
+++ b/xcomp/erl-xcomp-powerpc-dso-linux-gnu.conf
@@ -0,0 +1,258 @@
+## -*-shell-script-*-
+##
+## %CopyrightBegin%
+##
+## Copyright Ericsson AB 2009-2012. All Rights Reserved.
+##
+## The contents of this file are subject to the Erlang Public License,
+## Version 1.1, (the "License"); you may not use this file except in
+## compliance with the License. You should have received a copy of the
+## Erlang Public License along with this software. If not, it can be
+## retrieved online at http://www.erlang.org/.
+##
+## Software distributed under the License is distributed on an "AS IS"
+## basis, 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: erl-xcomp.conf.template
+## Author: Rickard Green
+##
+## -----------------------------------------------------------------------------
+## When cross compiling Erlang/OTP using `otp_build', copy this file and set
+## the variables needed below. Then pass the path to the copy of this file as
+## an argument to `otp_build' in the configure stage:
+## `otp_build configure --xcomp-conf=<FILE>'
+## -----------------------------------------------------------------------------
+
+## Note that you cannot define arbitrary variables in a cross compilation
+## configuration file. Only the ones listed below will be guaranteed to be
+## visible throughout the whole execution of all `configure' scripts. Other
+## variables needs to be defined as arguments to `configure' or exported in
+## the environment.
+
+## -- Variables for `otp_build' Only -------------------------------------------
+
+## Variables in this section are only used, when configuring Erlang/OTP for
+## cross compilation using `$ERL_TOP/otp_build configure'.
+
+## *NOTE*! These variables currently have *no* effect if you configure using
+## the `configure' script directly.
+
+# * `erl_xcomp_build' - The build system used. This value will be passed as
+# `--build=$erl_xcomp_build' argument to the `configure' script. It does
+# not have to be a full `CPU-VENDOR-OS' triplet, but can be. The full
+# `CPU-VENDOR-OS' triplet will be created by
+# `$ERL_TOP/erts/autoconf/config.sub $erl_xcomp_build'. If set to `guess',
+# the build system will be guessed using
+# `$ERL_TOP/erts/autoconf/config.guess'.
+erl_xcomp_build=guess
+
+# * `erl_xcomp_host' - Cross host/target system to build for. This value will
+# be passed as `--host=$erl_xcomp_host' argument to the `configure' script.
+# It does not have to be a full `CPU-VENDOR-OS' triplet, but can be. The
+# full `CPU-VENDOR-OS' triplet will be created by
+# `$ERL_TOP/erts/autoconf/config.sub $erl_xcomp_host'.
+erl_xcomp_host=powerpc-linux-gnu
+
+# * `erl_xcomp_configure_flags' - Extra configure flags to pass to the
+# `configure' script.
+erl_xcomp_configure_flags="--without-termcap"
+
+## -- Cross Compiler and Other Tools -------------------------------------------
+
+## If the cross compilation tools are prefixed by `<HOST>-' you probably do
+## not need to set these variables (where `<HOST>' is what has been passed as
+## `--host=<HOST>' argument to `configure').
+
+## All variables in this section can also be used when native compiling.
+
+# * `CC' - C compiler.
+CC=powerpc-linux-gnu-gcc
+
+# * `CFLAGS' - C compiler flags.
+#CFLAGS=
+
+# * `STATIC_CFLAGS' - Static C compiler flags.
+#STATIC_CFLAGS=
+
+# * `CFLAG_RUNTIME_LIBRARY_PATH' - This flag should set runtime library
+# search path for the shared libraries. Note that this actually is a
+# linker flag, but it needs to be passed via the compiler.
+#CFLAG_RUNTIME_LIBRARY_PATH=
+
+# * `CPP' - C pre-processor.
+#CPP=
+
+# * `CPPFLAGS' - C pre-processor flags.
+#CPPFLAGS=
+
+# * `CXX' - C++ compiler.
+CXX=powerpc-linux-gnu-g++
+
+# * `CXXFLAGS' - C++ compiler flags.
+#CXXFLAGS=
+
+# * `LD' - Linker.
+LD=powerpc-linux-gnu-ld
+
+# * `LDFLAGS' - Linker flags.
+#LDFLAGS=
+
+# * `LIBS' - Libraries.
+#LIBS=
+
+## -- *D*ynamic *E*rlang *D*river Linking --
+
+## *NOTE*! Either set all or none of the `DED_LD*' variables.
+
+# * `DED_LD' - Linker for Dynamically loaded Erlang Drivers.
+#DED_LD=
+
+# * `DED_LDFLAGS' - Linker flags to use with `DED_LD'.
+#DED_LDFLAGS=
+
+# * `DED_LD_FLAG_RUNTIME_LIBRARY_PATH' - This flag should set runtime library
+# search path for shared libraries when linking with `DED_LD'.
+#DED_LD_FLAG_RUNTIME_LIBRARY_PATH=
+
+## -- Large File Support --
+
+## *NOTE*! Either set all or none of the `LFS_*' variables.
+
+# * `LFS_CFLAGS' - Large file support C compiler flags.
+#LFS_CFLAGS=
+
+# * `LFS_LDFLAGS' - Large file support linker flags.
+#LFS_LDFLAGS=
+
+# * `LFS_LIBS' - Large file support libraries.
+#LFS_LIBS=
+
+## -- Other Tools --
+
+# * `RANLIB' - `ranlib' archive index tool.
+RANLIB=powerpc-linux-gnu-ranlib
+
+# * `AR' - `ar' archiving tool.
+AR=powerpc-linux-gnu-ar
+
+# * `GETCONF' - `getconf' system configuration inspection tool. `getconf' is
+# currently used for finding out large file support flags to use, and
+# on Linux systems for finding out if we have an NPTL thread library or
+# not.
+#GETCONF=
+
+## -- Cross System Root Locations ----------------------------------------------
+
+# * `erl_xcomp_sysroot' - The absolute path to the system root of the cross
+# compilation environment. Currently, the `crypto', `odbc', `ssh' and
+# `ssl' applications need the system root. These applications will be
+# skipped if the system root has not been set. The system root might be
+# needed for other things too. If this is the case and the system root
+# has not been set, `configure' will fail and request you to set it.
+erl_xcomp_sysroot="$DSO_SYSROOT_POWERPC"
+
+# * `erl_xcomp_isysroot' - The absolute path to the system root for includes
+# of the cross compilation environment. If not set, this value defaults
+# to `$erl_xcomp_sysroot', i.e., only set this value if the include system
+# root path is not the same as the system root path.
+#erl_xcomp_isysroot=
+
+## -- Optional Feature, and Bug Tests ------------------------------------------
+
+## These tests cannot (always) be done automatically when cross compiling. You
+## usually do not need to set these variables. Only set these if you really
+## know what you are doing.
+
+## Note that some of these values will override results of tests performed
+## by `configure', and some will not be used until `configure' is sure that
+## it cannot figure the result out.
+
+## The `configure' script will issue a warning when a default value is used.
+## When a variable has been set, no warning will be issued.
+
+# * `erl_xcomp_after_morecore_hook' - `yes|no'. Defaults to `no'. If `yes',
+# the target system must have a working `__after_morecore_hook' that can be
+# used for tracking used `malloc()' implementations core memory usage.
+# This is currently only used by unsupported features.
+#erl_xcomp_after_morecore_hook=
+
+# * `erl_xcomp_bigendian' - `yes|no'. No default. If `yes', the target system
+# must be big endian. If `no', little endian. This can often be
+# automatically detected, but not always. If not automatically detected,
+# `configure' will fail unless this variable is set. Since no default
+# value is used, `configure' will try to figure this out automatically.
+#erl_xcomp_bigendian=
+
+# * `erl_xcomp_clock_gettime_cpu_time' - `yes|no'. Defaults to `no'. If `yes',
+# the target system must have a working `clock_gettime()' implementation
+# that can be used for retrieving process CPU time.
+#erl_xcomp_clock_gettime_cpu_time=
+
+# * `erl_xcomp_getaddrinfo' - `yes|no'. Defaults to `no'. If `yes', the target
+# system must have a working `getaddrinfo()' implementation that can
+# handle both IPv4 and IPv6.
+erl_xcomp_getaddrinfo=yes
+
+# * `erl_xcomp_gethrvtime_procfs_ioctl' - `yes|no'. Defaults to `no'. If `yes',
+# the target system must have a working `gethrvtime()' implementation and
+# is used with procfs `ioctl()'.
+#erl_xcomp_gethrvtime_procfs_ioctl=
+
+# * `erl_xcomp_dlsym_brk_wrappers' - `yes|no'. Defaults to `no'. If `yes', the
+# target system must have a working `dlsym(RTLD_NEXT, <S>)' implementation
+# that can be used on `brk' and `sbrk' symbols used by the `malloc()'
+# implementation in use, and by this track the `malloc()' implementations
+# core memory usage. This is currently only used by unsupported features.
+#erl_xcomp_dlsym_brk_wrappers=
+
+# * `erl_xcomp_kqueue' - `yes|no'. Defaults to `no'. If `yes', the target
+# system must have a working `kqueue()' implementation that returns a file
+# descriptor which can be used by `poll()' and/or `select()'. If `no' and
+# the target system has not got `epoll()' or `/dev/poll', the kernel-poll
+# feature will be disabled.
+#erl_xcomp_kqueue=
+
+# * `erl_xcomp_linux_clock_gettime_correction' - `yes|no'. Defaults to `yes' on
+# Linux; otherwise, `no'. If `yes', `clock_gettime(CLOCK_MONOTONIC, _)' on
+# the target system must work. This variable is recommended to be set to
+# `no' on Linux systems with kernel versions less than 2.6.
+#erl_xcomp_linux_clock_gettime_correction=
+
+# * `erl_xcomp_linux_nptl' - `yes|no'. Defaults to `yes' on Linux; otherwise,
+# `no'. If `yes', the target system must have NPTL (Native POSIX Thread
+# Library). Older Linux systems have LinuxThreads instead of NPTL (Linux
+# kernel versions typically less than 2.6).
+#erl_xcomp_linux_nptl=
+
+# * `erl_xcomp_linux_usable_sigaltstack' - `yes|no'. Defaults to `yes' on Linux;
+# otherwise, `no'. If `yes', `sigaltstack()' must be usable on the target
+# system. `sigaltstack()' on Linux kernel versions less than 2.4 are
+# broken.
+#erl_xcomp_linux_usable_sigaltstack=
+
+# * `erl_xcomp_linux_usable_sigusrx' - `yes|no'. Defaults to `yes'. If `yes',
+# the `SIGUSR1' and `SIGUSR2' signals must be usable by the ERTS. Old
+# LinuxThreads thread libraries (Linux kernel versions typically less than
+# 2.2) used these signals and made them unusable by the ERTS.
+#erl_xcomp_linux_usable_sigusrx=
+
+# * `erl_xcomp_poll' - `yes|no'. Defaults to `no' on Darwin/MacOSX; otherwise,
+# `yes'. If `yes', the target system must have a working `poll()'
+# implementation that also can handle devices. If `no', `select()' will be
+# used instead of `poll()'.
+#erl_xcomp_poll=
+
+# * `erl_xcomp_putenv_copy' - `yes|no'. Defaults to `no'. If `yes', the target
+# system must have a `putenv()' implementation that stores a copy of the
+# key/value pair.
+#erl_xcomp_putenv_copy=
+
+# * `erl_xcomp_reliable_fpe' - `yes|no'. Defaults to `no'. If `yes', the target
+# system must have reliable floating point exceptions.
+#erl_xcomp_reliable_fpe=
+
+## -----------------------------------------------------------------------------
diff --git a/xcomp/erl-xcomp-vars.sh b/xcomp/erl-xcomp-vars.sh
index d9972eb3db..eccdff47e3 100644
--- a/xcomp/erl-xcomp-vars.sh
+++ b/xcomp/erl-xcomp-vars.sh
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2010. All Rights Reserved.
+# Copyright Ericsson AB 2010-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -26,4 +26,4 @@
# and precious variables in $ERL_TOP/erts/aclocal.m4.
#
-erl_xcomp_vars="erl_xcomp_sysroot erl_xcomp_isysroot erl_xcomp_bigendian erl_xcomp_linux_clock_gettime_correction erl_xcomp_linux_nptl erl_xcomp_linux_usable_sigusrx erl_xcomp_linux_usable_sigaltstack erl_xcomp_poll erl_xcomp_kqueue erl_xcomp_putenv_copy erl_xcomp_reliable_fpe erl_xcomp_getaddrinfo erl_xcomp_gethrvtime_procfs_ioctl erl_xcomp_clock_gettime_cpu_time erl_xcomp_after_morecore_hook erl_xcomp_dlsym_brk_wrappers"
+erl_xcomp_vars="erl_xcomp_sysroot erl_xcomp_isysroot erl_xcomp_bigendian erl_xcomp_double_middle_endian erl_xcomp_linux_clock_gettime_correction erl_xcomp_linux_nptl erl_xcomp_linux_usable_sigusrx erl_xcomp_linux_usable_sigaltstack erl_xcomp_poll erl_xcomp_kqueue erl_xcomp_putenv_copy erl_xcomp_reliable_fpe erl_xcomp_getaddrinfo erl_xcomp_gethrvtime_procfs_ioctl erl_xcomp_clock_gettime_cpu_time erl_xcomp_after_morecore_hook erl_xcomp_dlsym_brk_wrappers"
diff --git a/xcomp/erl-xcomp-vxworks_ppc32.conf b/xcomp/erl-xcomp-vxworks_ppc32.conf
index ed8305ea93..7424cee496 100644
--- a/xcomp/erl-xcomp-vxworks_ppc32.conf
+++ b/xcomp/erl-xcomp-vxworks_ppc32.conf
@@ -2,7 +2,7 @@
##
## %CopyrightBegin%
##
-## Copyright Ericsson AB 2009-2010. All Rights Reserved.
+## Copyright Ericsson AB 2009-2012. All Rights Reserved.
##
## The contents of this file are subject to the Erlang Public License,
## Version 1.1, (the "License"); you may not use this file except in
@@ -187,6 +187,15 @@ AR="$WIND_BASE/gnu/3.4.4-vxworks-6.3/$WIND_HOST_TYPE/bin/arppc"
# value is used, `configure' will try to figure this out automatically.
erl_xcomp_bigendian=yes
+# * `erl_xcomp_double_middle_endian` - `yes|no`. No default. If `yes`, the
+# target system must have doubles in "middle-endian" format. If
+# `no`, it has "regular" endianness. This can often be automatically
+# detected, but not always. If not automatically detected,
+# `configure` will fail unless this variable is set. Since no
+# default value is used, `configure` will try to figure this out
+# automatically.
+#erl_xcomp_double_middle_endian
+
# * `erl_xcomp_clock_gettime_cpu_time' - `yes|no'. Defaults to `no'. If `yes',
# the target system must have a working `clock_gettime()' implementation
# that can be used for retrieving process CPU time.
diff --git a/xcomp/erl-xcomp-x86_64-saf-linux-gnu.conf b/xcomp/erl-xcomp-x86_64-saf-linux-gnu.conf
index f645ddbd96..d5b9d8c469 100644
--- a/xcomp/erl-xcomp-x86_64-saf-linux-gnu.conf
+++ b/xcomp/erl-xcomp-x86_64-saf-linux-gnu.conf
@@ -2,7 +2,7 @@
##
## %CopyrightBegin%
##
-## Copyright Ericsson AB 2010. All Rights Reserved.
+## Copyright Ericsson AB 2010-2012. All Rights Reserved.
##
## The contents of this file are subject to the Erlang Public License,
## Version 1.1, (the "License"); you may not use this file except in
@@ -187,6 +187,15 @@ erl_xcomp_after_morecore_hook=yes
# value is used, `configure' will try to figure this out automatically.
#erl_xcomp_bigendian=
+# * `erl_xcomp_double_middle_endian` - `yes|no`. No default. If `yes`, the
+# target system must have doubles in "middle-endian" format. If
+# `no`, it has "regular" endianness. This can often be automatically
+# detected, but not always. If not automatically detected,
+# `configure` will fail unless this variable is set. Since no
+# default value is used, `configure` will try to figure this out
+# automatically.
+#erl_xcomp_double_middle_endian
+
# * `erl_xcomp_clock_gettime_cpu_time' - `yes|no'. Defaults to `no'. If `yes',
# the target system must have a working `clock_gettime()' implementation
# that can be used for retrieving process CPU time.
diff --git a/xcomp/erl-xcomp.conf.template b/xcomp/erl-xcomp.conf.template
index eb331ffe92..7d70332cef 100644
--- a/xcomp/erl-xcomp.conf.template
+++ b/xcomp/erl-xcomp.conf.template
@@ -2,7 +2,7 @@
##
## %CopyrightBegin%
##
-## Copyright Ericsson AB 2009-2010. All Rights Reserved.
+## Copyright Ericsson AB 2009-2012. All Rights Reserved.
##
## The contents of this file are subject to the Erlang Public License,
## Version 1.1, (the "License"); you may not use this file except in
@@ -187,6 +187,15 @@
# value is used, `configure' will try to figure this out automatically.
#erl_xcomp_bigendian=
+# * `erl_xcomp_double_middle` - `yes|no`. No default. If `yes`, the
+# target system must have doubles in "middle-endian" format. If
+# `no`, it has "regular" endianness. This can often be automatically
+# detected, but not always. If not automatically detected,
+# `configure` will fail unless this variable is set. Since no
+# default value is used, `configure` will try to figure this out
+# automatically.
+#erl_xcomp_double_middle_endian
+
# * `erl_xcomp_clock_gettime_cpu_time' - `yes|no'. Defaults to `no'. If `yes',
# the target system must have a working `clock_gettime()' implementation
# that can be used for retrieving process CPU time.